When EDK2 is built for the small memory model with AArch64 LLVM, some page-relative relocations (R_AARCH64_ADR_PREL_PG_HI21 and its R_AARCH64_LDST(16|32|64|128)_ABS_LO12_NC companions) that were not supported before by EDK2 BaseTools are now needed.
This change adds support for these relocations in BaseTools. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Harry Liebel <harry.lie...@arm.com> Reviewed-by: Olivier Martin <olivier.mar...@arm.com> --- BaseTools/Source/C/Common/BasePeCoff.c | 14 +++ BaseTools/Source/C/Common/PeCoffLib.h | 13 +++ BaseTools/Source/C/Common/PeCoffLoaderEx.c | 58 +++++++++++ BaseTools/Source/C/GenFw/Elf64Convert.c | 159 +++++++++++++++++++++++------ BaseTools/Source/C/GenFw/elf_common.h | 4 + 5 files changed, 215 insertions(+), 33 deletions(-) diff --git a/BaseTools/Source/C/Common/BasePeCoff.c b/BaseTools/Source/C/Common/BasePeCoff.c index afb45df..e1401f2 100644 --- a/BaseTools/Source/C/Common/BasePeCoff.c +++ b/BaseTools/Source/C/Common/BasePeCoff.c @@ -715,6 +715,20 @@ Returns: RelocBaseEnd = (EFI_IMAGE_BASE_RELOCATION *) ((UINTN) RelocBase + (UINTN) RelocDir->Size - 1); } + // In order for page relative code relocations to work on AArch64 we need to + // ensure that any address adjustment to images are 4k page aligned. The page + // relative relocations are processed at build time as we do not have enough + // information at runtime to recalculate them. + // The PE/COFF Base relocation types do not have a matching type to describe + // page relative relocations so we do not know if they may be present in the + // images. We must assume they are present and ensure the image is properly + // aligned to keep these relocations valid. + if (ImageContext->Machine == EFI_IMAGE_MACHINE_AARCH64) { + if ((Adjust & 0xfff) != 0x0) { + return RETURN_LOAD_ERROR; + } + } + // // Run the relocation information and apply the fixups // diff --git a/BaseTools/Source/C/Common/PeCoffLib.h b/BaseTools/Source/C/Common/PeCoffLib.h index b56dd75..2f8feb7 100644 --- a/BaseTools/Source/C/Common/PeCoffLib.h +++ b/BaseTools/Source/C/Common/PeCoffLib.h @@ -205,6 +205,19 @@ ThumbMovwMovtImmediatePatch ( IN UINT32 Address ); +/** + Update AArch64 instruction immediate address data. + + @param Instruction Pointer to AArch64 instruction to update + @param Address New addres to patch into the instruction + +**/ +RETURN_STATUS +EFIAPI +Aarch64ImmediatePatch ( + IN OUT UINT32 *Instruction, + IN INT32 Val + ); #endif diff --git a/BaseTools/Source/C/Common/PeCoffLoaderEx.c b/BaseTools/Source/C/Common/PeCoffLoaderEx.c index b7b7227..3864391 100644 --- a/BaseTools/Source/C/Common/PeCoffLoaderEx.c +++ b/BaseTools/Source/C/Common/PeCoffLoaderEx.c @@ -466,6 +466,64 @@ PeCoffLoaderRelocateArmImage ( return RETURN_SUCCESS; } + +/** + Update AArch64 instruction immediate address data. + + @param Instruction Pointer to AArch64 instruction to update + @param Value New value to patch into the instruction. Signed value + to preserve sign extension where needed. Some + instructions can have positive and negative values. + +**/ +// The values below are used to match instructions based on the architecture encoding +// Don't support shifted imm12 for ADD instructions. Set as 0 in MASK and INST +#define AARCH64_ADD_MASK 0x1FC00000 +#define AARCH64_ADD_INST 0x11000000 +#define AARCH64_ADD_IMM 0x003FFC00 +#define IS_AARCH64_ADD(x) ((x & AARCH64_ADD_MASK) == AARCH64_ADD_INST) +#define AARCH64_ADRP_MASK 0x9f000000 +#define AARCH64_ADRP_INST 0x90000000 +#define AARCH64_ADRP_IMM 0x60FFFFE0 +#define IS_AARCH64_ADRP(x) ((x & AARCH64_ADRP_MASK) == AARCH64_ADRP_INST) +#define AARCH64_LDST_MASK 0x3b000000 +#define AARCH64_LDST_INST 0x39000000 +#define AARCH64_LDST_IMM 0x003FFC00 +#define IS_AARCH64_LDST(x) ((x & AARCH64_LDST_MASK) == AARCH64_LDST_INST) + +RETURN_STATUS +EFIAPI +Aarch64ImmediatePatch ( + IN OUT UINT32 *Instruction, + IN INT32 Value + ) +{ + UINT32 Patch; + RETURN_STATUS Status; + + Status = RETURN_UNSUPPORTED; + + // Disassemble and update the instruction + if (IS_AARCH64_ADD (*Instruction)) { + Patch = Value << 10; + *Instruction = (*Instruction & ~AARCH64_ADD_IMM) | Patch; + Status = RETURN_SUCCESS; + } else if (IS_AARCH64_ADRP (*Instruction)) { + Patch = ((Value & 0x3) << 29) | (((Value & 0x1FFFFC) >> 2) << 5); + *Instruction = (*Instruction & ~AARCH64_ADRP_IMM) | Patch; + Status = RETURN_SUCCESS; + } else if (IS_AARCH64_LDST (*Instruction)) { + // Relocation types LDST8,16,32,64,128 specify different bitsizes. + // The Calling function knows the relocation type and has already masked and + // scaled the address as needed. + Patch = Value << 10; + *Instruction = (*Instruction & ~AARCH64_LDST_IMM) | Patch; + Status = RETURN_SUCCESS; + } + + return Status; +} + RETURN_STATUS PeCoffLoaderRelocateAArch64Image ( IN UINT16 *Reloc, diff --git a/BaseTools/Source/C/GenFw/Elf64Convert.c b/BaseTools/Source/C/GenFw/Elf64Convert.c index d2becf1..355398f 100644 --- a/BaseTools/Source/C/GenFw/Elf64Convert.c +++ b/BaseTools/Source/C/GenFw/Elf64Convert.c @@ -709,56 +709,148 @@ WriteSections64 ( Error (NULL, 0, 3000, "Invalid", "%s unsupported ELF EM_X86_64 relocation 0x%x.", mInImageName, (unsigned) ELF_R_TYPE(Rel->r_info)); } } else if (mEhdr->e_machine == EM_AARCH64) { - - // AARCH64 GCC uses RELA relocation, so all relocations have to be fixed up. - // As opposed to ARM32 using REL. - + // For ease of use specify relocations in terms of P, S and A as + // described the AArch64 ELF specification + UINT64 P, S; + INT64 A; + RETURN_STATUS Status; + + // P - Address of the 'Place' being relocated + // S - Address of the Symbol + // A - The Addend for the relocation + P = (UINT64)(SecOffset + (Rel->r_offset - SecShdr->sh_addr)); + S = (UINT64)(Sym->st_value - SymShdr->sh_addr + mCoffSectionsOffset[Sym->st_shndx]); + A = (INT64)(Rel->r_addend); + + // Some versions of the AArch64 GCC linker does not update the ELF + // relocations properly when doing the final static link. For this + // reason we do not fully recalculate the relocations that it might + // use with the Large memory model. We rely on the assumption that the + // code sections affected by these relocations are not reordered going + // from ELF to PE/COFF and a simple offset adjustment is sufficient. + // In the case of the ARM LLVM based compiler the relocation + // information is correct and as such we can use the small memory + // model and recalculate the relocations based on 'Place', 'Symbol' + // and 'Addend' information. At some point in the future this should + // be be done for AArch64 GCC as well. switch (ELF_R_TYPE(Rel->r_info)) { + // Ideally we should recalculate all relocations in case the code + // sections moved around, but in some AArch64 GCC versions this is + // currently not working correctly. + + // Relative relocations case R_AARCH64_ADR_PREL_LO21: - if (Rel->r_addend != 0 ) { /* TODO */ - Error (NULL, 0, 3000, "Invalid", "AArch64: R_AARCH64_ADR_PREL_LO21 Need to fixup with addend!."); - } + // GCC with Large memory model uses this one. Leave as is for now. + // Reloc = (S+A-P) + VerboseMsg ("R_AARCH64_ADR_PREL_LO21: Targ = 0x%08lx, *Targ=0x%08x\n", (UINT64)Targ, *(UINT32 *)Targ); break; case R_AARCH64_CONDBR19: - if (Rel->r_addend != 0 ) { /* TODO */ - Error (NULL, 0, 3000, "Invalid", "AArch64: R_AARCH64_CONDBR19 Need to fixup with addend!."); - } + // GCC with Large memory model uses this one. Leave as is for now. + // Reloc = (S+A-P) + VerboseMsg ("R_AARCH64_CONDBR19: Targ = 0x%08lx, *Targ=0x%08x\n", (UINT64)Targ, *(UINT32 *)Targ); break; case R_AARCH64_LD_PREL_LO19: - if (Rel->r_addend != 0 ) { /* TODO */ - Error (NULL, 0, 3000, "Invalid", "AArch64: R_AARCH64_LD_PREL_LO19 Need to fixup with addend!."); - } + // GCC with Large memory model uses this one. Leave as is for now. + // Reloc = (S+A-P) + VerboseMsg ("R_AARCH64_LD_PREL_LO19: Targ = 0x%08lx, *Targ=0x%08x\n", (UINT64)Targ, *(UINT32 *)Targ); break; case R_AARCH64_CALL26: + // GCC with Large memory model uses this one. Leave as is for now. + // Reloc = (S+A-P) + VerboseMsg ("R_AARCH64_CALL26: Targ = 0x%08lx, *Targ=0x%08x\n", (UINT64)Targ, *(UINT32 *)Targ); + break; + case R_AARCH64_JUMP26: - if (Rel->r_addend != 0 ) { - // Some references to static functions sometime start at the base of .text + addend. - // It is safe to ignore these relocations because they patch a `BL` instructions that - // contains an offset from the instruction itself and there is only a single .text section. - // So we check if the symbol is a "section symbol" - if (ELF64_ST_TYPE (Sym->st_info) == STT_SECTION) { - break; - } - Error (NULL, 0, 3000, "Invalid", "AArch64: R_AARCH64_JUMP26 Need to fixup with addend!."); - } + // GCC with Large memory model uses this one. Leave as is for now. + // Reloc = (S+A-P) + VerboseMsg ("R_AARCH64_JUMP26: Targ = 0x%08lx, *Targ=0x%08x\n", (UINT64)Targ, *(UINT32 *)Targ); break; case R_AARCH64_ADR_PREL_PG_HI21: - // TODO : AArch64 'small' memory model. - Error (NULL, 0, 3000, "Invalid", "WriteSections64(): %s unsupported ELF EM_AARCH64 relocation R_AARCH64_ADR_PREL_PG_HI21.", mInImageName); + // We need to fully recalculate this relocation. This is used with the small memory model. + // Reloc = PAGE(S+A) - PAGE(P) ; Page = 4k + VerboseMsg ("R_AARCH64_ADR_PREL_PG_HI21: Targ = 0x%08lx, *Targ=0x%08x\n", (UINT64)Targ, *(UINT32 *)Targ); + INT32 Tmp = R_AARCH64_PAGE (S + A) - R_AARCH64_PAGE (P); + Status = Aarch64ImmediatePatch ((UINT32 *)Targ, Tmp / 4096); + if (Status != RETURN_SUCCESS) { + Error (NULL, 0, 3000, "Invalid", "WriteSections64(): %s Patching ELF EM_AARCH64 relocation failed.", mInImageName); + } break; case R_AARCH64_ADD_ABS_LO12_NC: - // TODO : AArch64 'small' memory model. - Error (NULL, 0, 3000, "Invalid", "WriteSections64(): %s unsupported ELF EM_AARCH64 relocation R_AARCH64_ADD_ABS_LO12_NC.", mInImageName); + // Used with R_AARCH64_ADR_PREL_PG_HI21. This is used with the small memory model. + // Reloc = (S+A) + VerboseMsg ("R_AARCH64_ADD_ABS_LO12_NC: Targ = 0x%08lx, *Targ=0x%08x\n", (UINT64)Targ, *(UINT32 *)Targ); + Status = Aarch64ImmediatePatch ((UINT32 *)Targ, (S + A) & R_AARCH64_PAGE_MASK); + if (Status != RETURN_SUCCESS) { + Error (NULL, 0, 3000, "Invalid", "WriteSections64(): %s Patching ELF EM_AARCH64 relocation failed.", mInImageName); + } + break; + + case R_AARCH64_LDST8_ABS_LO12_NC: + // Used with R_AARCH64_ADR_PREL_PG_HI21. This is used with the small memory model. + // Reloc = (S+A) + VerboseMsg ("R_AARCH64_LDST8_ABS_LO12_NC: Targ = 0x%08lx, *Targ=0x%08x\n", (UINT64)Targ, *(UINT32 *)Targ); + Status = Aarch64ImmediatePatch ((UINT32 *)Targ, (S + A) & R_AARCH64_PAGE_MASK); + if (Status != RETURN_SUCCESS) { + Error (NULL, 0, 3000, "Invalid", "WriteSections64(): %s Patching ELF EM_AARCH64 relocation failed.", mInImageName); + } + break; + + case R_AARCH64_LDST16_ABS_LO12_NC: + // Used with R_AARCH64_ADR_PREL_PG_HI21. This is used with the small memory model. + // Reloc = (S+A) + VerboseMsg ("R_AARCH64_LDST16_ABS_LO12_NC: Targ = 0x%08lx, *Targ=0x%08x\n", (UINT64)Targ, *(UINT32 *)Targ); + Status = Aarch64ImmediatePatch ((UINT32 *)Targ, ((S + A) & R_AARCH64_PAGE_MASK) >> 1 ); + if (Status != RETURN_SUCCESS) { + Error (NULL, 0, 3000, "Invalid", "WriteSections64(): %s Patching ELF EM_AARCH64 relocation failed.", mInImageName); + } + break; + + case R_AARCH64_LDST32_ABS_LO12_NC: + // Used with R_AARCH64_ADR_PREL_PG_HI21. This is used with the small memory model. + // Reloc = (S+A) + VerboseMsg ("R_AARCH64_LDST32_ABS_LO12_NC: Targ = 0x%08lx, *Targ=0x%08x\n", (UINT64)Targ, *(UINT32 *)Targ); + Status = Aarch64ImmediatePatch ((UINT32 *)Targ, ((S + A) & R_AARCH64_PAGE_MASK) >> 2 ); + if (Status != RETURN_SUCCESS) { + Error (NULL, 0, 3000, "Invalid", "WriteSections64(): %s Patching ELF EM_AARCH64 relocation failed.", mInImageName); + } + break; + + case R_AARCH64_LDST64_ABS_LO12_NC: + // Used with R_AARCH64_ADR_PREL_PG_HI21. This is used with the small memory model. + // Reloc = (S+A) + VerboseMsg ("R_AARCH64_LDST64_ABS_LO12_NC: Targ = 0x%08lx, *Targ=0x%08x\n", (UINT64)Targ, *(UINT32 *)Targ); + Status = Aarch64ImmediatePatch ((UINT32 *)Targ, ((S + A) & R_AARCH64_PAGE_MASK) >> 3 ); + if (Status != RETURN_SUCCESS) { + Error (NULL, 0, 3000, "Invalid", "WriteSections64(): %s Patching ELF EM_AARCH64 relocation failed.", mInImageName); + } + break; + + case R_AARCH64_LDST128_ABS_LO12_NC: + // Used with R_AARCH64_ADR_PREL_PG_HI21. This is used with the small memory model. + // Reloc = (S+A) + VerboseMsg ("R_AARCH64_LDST128_ABS_LO12_NC: Targ = 0x%08lx, *Targ=0x%08x\n", (UINT64)Targ, *(UINT32 *)Targ); + Status = Aarch64ImmediatePatch ((UINT32 *)Targ, ((S + A) & R_AARCH64_PAGE_MASK) >> 4 ); + if (Status != RETURN_SUCCESS) { + Error (NULL, 0, 3000, "Invalid", "WriteSections64(): %s Patching ELF EM_AARCH64 relocation failed.", mInImageName); + } break; // Absolute relocations. case R_AARCH64_ABS64: + // GCC with Large memory model uses this one. Don't recalculate the + // relocation, just do the section offset adjustment. This assumes + // the relevant sections are not getting reordered. + // Reloc = (S+A) + VerboseMsg ("R_AARCH64_ABS64: Targ = 0x%08lx, *Targ=0x%08lx\n", (UINT64)Targ, *(UINT64 *)Targ); + + // If S lives in bss (like global gST): + // *Targ = *Targ - elf-section-addr[bss] + coff-section-addr[bss] *(UINT64 *)Targ = *(UINT64 *)Targ - SymShdr->sh_addr + mCoffSectionsOffset[Sym->st_shndx]; break; @@ -838,17 +930,18 @@ WriteRelocations64 ( case R_AARCH64_JUMP26: break; + // These relocations are used together case R_AARCH64_ADR_PREL_PG_HI21: - // TODO : AArch64 'small' memory model. - Error (NULL, 0, 3000, "Invalid", "WriteRelocations64(): %s unsupported ELF EM_AARCH64 relocation R_AARCH64_ADR_PREL_PG_HI21.", mInImageName); - break; - case R_AARCH64_ADD_ABS_LO12_NC: - // TODO : AArch64 'small' memory model. - Error (NULL, 0, 3000, "Invalid", "WriteRelocations64(): %s unsupported ELF EM_AARCH64 relocation R_AARCH64_ADD_ABS_LO12_NC.", mInImageName); + case R_AARCH64_LDST8_ABS_LO12_NC: + case R_AARCH64_LDST16_ABS_LO12_NC: + case R_AARCH64_LDST32_ABS_LO12_NC: + case R_AARCH64_LDST64_ABS_LO12_NC: + case R_AARCH64_LDST128_ABS_LO12_NC: break; case R_AARCH64_ABS64: + // Write DIR64 to the PE/COFF reloc fixup list CoffAddFixup( (UINT32) ((UINT64) mCoffSectionsOffset[RelShdr->sh_info] + (Rel->r_offset - SecShdr->sh_addr)), diff --git a/BaseTools/Source/C/GenFw/elf_common.h b/BaseTools/Source/C/GenFw/elf_common.h index 766d0e4..d9057b6 100644 --- a/BaseTools/Source/C/GenFw/elf_common.h +++ b/BaseTools/Source/C/GenFw/elf_common.h @@ -673,6 +673,10 @@ typedef struct { #define R_AARCH64_TLS_DTPREL32 1031 /* DTPREL(S+A) */ #define R_AARCH64_TLS_DTPMOD32 1032 /* LDM(S) */ #define R_AARCH64_TLS_TPREL32 1033 /* DTPREL(S+A) */ +/* AArch64 page relative relocations use 4k pages. */ +#define R_AARCH64_PAGE_SIZE 0x1000 +#define R_AARCH64_PAGE_MASK 0xfffUL +#define R_AARCH64_PAGE(x) ((x) & ~R_AARCH64_PAGE_MASK) #define R_ALPHA_NONE 0 /* No reloc */ #define R_ALPHA_REFLONG 1 /* Direct 32 bit */ -- 2.1.1 ------------------------------------------------------------------------------ Don't Limit Your Business. Reach for the Cloud. GigeNET's Cloud Solutions provide you with the tools and support that you need to offload your IT needs and focus on growing your business. Configured For All Businesses. Start Your Cloud Today. https://www.gigenetcloud.com/ _______________________________________________ edk2-devel mailing list edk2-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/edk2-devel