Olivier Martin
2015-07-08 14:57:22 UTC
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 <***@arm.com>
Reviewed-by: Olivier Martin <***@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 */
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 <***@arm.com>
Reviewed-by: Olivier Martin <***@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
2.1.1