BaseTools/GenFw AARCH64: fix up GOT based relative relocations

We take great care to avoid GOT based relocations in EDK2 executables,
primarily because they are pointless - we don't care about things like
the CoW footprint or relocations that target read-only sections, and so
GOT entries only bloat the binary.

However, in some cases (e.g., when building the relocatable PrePi SEC
module in ArmVirtPkg with the CLANG38 toolchain), we may end up with
some GOT based relocations nonetheless, which break the build since
GenFw does not know how to deal with them.

The relocations emitted in this case are ADRP/LDR instruction pairs
that are annotated as GOT based, which means that it is the linker's
job to emit the GOT entry and tag it with an appropriate dynamic
relocation that ensures that the correct absolute value is stored into
the GOT entry when the executable is loaded. This dynamic relocation is
not visible to GenFw, and so populating the PE/COFF relocation section
for these entries is non-trivial.

Since each ADRP/LDR pair refers to a single symbol that is local to the
binary (given that shared libraries are not supported), we can actually
convert the ADRP/LDR pair into an ADRP/ADD pair that produces the symbol
address directly rather than loading it from memory. This leaves the
GOT entry in the binary, but since it is now unused, it is no longer
necessary to emit a PE/COFF relocation entry for it.

Acked-by: Liming Gao <liming.gao@intel.com>
Reviewed-by: Leif Lindholm <leif.lindholm@linaro.org>
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
This commit is contained in:
Ard Biesheuvel 2019-09-03 20:58:18 -07:00
parent adb59b633c
commit d2687f23c9
1 changed files with 27 additions and 1 deletions

View File

@ -1017,6 +1017,31 @@ WriteSections64 (
} else if (mEhdr->e_machine == EM_AARCH64) { } else if (mEhdr->e_machine == EM_AARCH64) {
switch (ELF_R_TYPE(Rel->r_info)) { switch (ELF_R_TYPE(Rel->r_info)) {
INT64 Offset;
case R_AARCH64_LD64_GOT_LO12_NC:
//
// Convert into an ADD instruction - see R_AARCH64_ADR_GOT_PAGE below.
//
*(UINT32 *)Targ &= 0x3ff;
*(UINT32 *)Targ |= 0x91000000 | ((Sym->st_value & 0xfff) << 10);
break;
case R_AARCH64_ADR_GOT_PAGE:
//
// This relocation points to the GOT entry that contains the absolute
// address of the symbol we are referring to. Since EDK2 only uses
// fully linked binaries, we can avoid the indirection, and simply
// refer to the symbol directly. This implies having to patch the
// subsequent LDR instruction (covered by a R_AARCH64_LD64_GOT_LO12_NC
// relocation) into an ADD instruction - this is handled above.
//
Offset = (Sym->st_value - (Rel->r_offset & ~0xfff)) >> 12;
*(UINT32 *)Targ &= 0x9000001f;
*(UINT32 *)Targ |= ((Offset & 0x1ffffc) << (5 - 2)) | ((Offset & 0x3) << 29);
/* fall through */
case R_AARCH64_ADR_PREL_PG_HI21: case R_AARCH64_ADR_PREL_PG_HI21:
// //
@ -1037,7 +1062,6 @@ WriteSections64 (
// Attempt to convert the ADRP into an ADR instruction. // Attempt to convert the ADRP into an ADR instruction.
// This is only possible if the symbol is within +/- 1 MB. // This is only possible if the symbol is within +/- 1 MB.
// //
INT64 Offset;
// Decode the ADRP instruction // Decode the ADRP instruction
Offset = (INT32)((*(UINT32 *)Targ & 0xffffe0) << 8); Offset = (INT32)((*(UINT32 *)Targ & 0xffffe0) << 8);
@ -1212,6 +1236,8 @@ WriteRelocations64 (
case R_AARCH64_LDST32_ABS_LO12_NC: case R_AARCH64_LDST32_ABS_LO12_NC:
case R_AARCH64_LDST64_ABS_LO12_NC: case R_AARCH64_LDST64_ABS_LO12_NC:
case R_AARCH64_LDST128_ABS_LO12_NC: case R_AARCH64_LDST128_ABS_LO12_NC:
case R_AARCH64_ADR_GOT_PAGE:
case R_AARCH64_LD64_GOT_LO12_NC:
// //
// No fixups are required for relative relocations, provided that // No fixups are required for relative relocations, provided that
// the relative offsets between sections have been preserved in // the relative offsets between sections have been preserved in