X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=BaseTools%2FSource%2FC%2FGenFw%2FElf64Convert.c;h=d623dce1f9da020f110de1dd14d1182e9f90818a;hb=f55c76b301f15d287b58cd86dd59be978dc753a3;hp=54011d75f1ec6c9951071be5b5261a091200e058;hpb=3f0218003141ae38152f5a2520c969445afc721f;p=mirror_edk2.git
diff --git a/BaseTools/Source/C/GenFw/Elf64Convert.c b/BaseTools/Source/C/GenFw/Elf64Convert.c
index 54011d75f1..d623dce1f9 100644
--- a/BaseTools/Source/C/GenFw/Elf64Convert.c
+++ b/BaseTools/Source/C/GenFw/Elf64Convert.c
@@ -4,13 +4,7 @@ Elf64 convert solution
Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
Portions copyright (c) 2013-2014, ARM Ltd. All rights reserved.
-This program and the accompanying materials are licensed and made available
-under the terms and conditions of the BSD License which accompanies this
-distribution. The full text of the license may be found at
-http://opensource.org/licenses/bsd-license.php
-
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+SPDX-License-Identifier: BSD-2-Clause-Patent
**/
@@ -74,7 +68,7 @@ CleanUp64 (
);
//
-// Rename ELF32 strucutres to common names to help when porting to ELF64.
+// Rename ELF32 structures to common names to help when porting to ELF64.
//
typedef Elf64_Shdr Elf_Shdr;
typedef Elf64_Ehdr Elf_Ehdr;
@@ -94,6 +88,15 @@ STATIC Elf_Ehdr *mEhdr;
STATIC Elf_Shdr *mShdrBase;
STATIC Elf_Phdr *mPhdrBase;
+//
+// GOT information
+//
+STATIC Elf_Shdr *mGOTShdr = NULL;
+STATIC UINT32 mGOTShindex = 0;
+STATIC UINT32 *mGOTCoffEntries = NULL;
+STATIC UINT32 mGOTMaxCoffEntries = 0;
+STATIC UINT32 mGOTNumCoffEntries = 0;
+
//
// Coff information
//
@@ -322,6 +325,134 @@ GetSymName (
return StrtabContents + Sym->st_name;
}
+//
+// Find the ELF section hosting the GOT from an ELF Rva
+// of a single GOT entry. Normally, GOT is placed in
+// ELF .text section, so assume once we find in which
+// section the GOT is, all GOT entries are there, and
+// just verify this.
+//
+STATIC
+VOID
+FindElfGOTSectionFromGOTEntryElfRva (
+ Elf64_Addr GOTEntryElfRva
+ )
+{
+ UINT32 i;
+ if (mGOTShdr != NULL) {
+ if (GOTEntryElfRva >= mGOTShdr->sh_addr &&
+ GOTEntryElfRva < mGOTShdr->sh_addr + mGOTShdr->sh_size) {
+ return;
+ }
+ Error (NULL, 0, 3000, "Unsupported", "FindElfGOTSectionFromGOTEntryElfRva: GOT entries found in multiple sections.");
+ exit(EXIT_FAILURE);
+ }
+ for (i = 0; i < mEhdr->e_shnum; i++) {
+ Elf_Shdr *shdr = GetShdrByIndex(i);
+ if (GOTEntryElfRva >= shdr->sh_addr &&
+ GOTEntryElfRva < shdr->sh_addr + shdr->sh_size) {
+ mGOTShdr = shdr;
+ mGOTShindex = i;
+ return;
+ }
+ }
+ Error (NULL, 0, 3000, "Invalid", "FindElfGOTSectionFromGOTEntryElfRva: ElfRva 0x%016LX for GOT entry not found in any section.", GOTEntryElfRva);
+ exit(EXIT_FAILURE);
+}
+
+//
+// Stores locations of GOT entries in COFF image.
+// Returns TRUE if GOT entry is new.
+// Simple implementation as number of GOT
+// entries is expected to be low.
+//
+
+STATIC
+BOOLEAN
+AccumulateCoffGOTEntries (
+ UINT32 GOTCoffEntry
+ )
+{
+ UINT32 i;
+ if (mGOTCoffEntries != NULL) {
+ for (i = 0; i < mGOTNumCoffEntries; i++) {
+ if (mGOTCoffEntries[i] == GOTCoffEntry) {
+ return FALSE;
+ }
+ }
+ }
+ if (mGOTCoffEntries == NULL) {
+ mGOTCoffEntries = (UINT32*)malloc(5 * sizeof *mGOTCoffEntries);
+ if (mGOTCoffEntries == NULL) {
+ Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
+ }
+ assert (mGOTCoffEntries != NULL);
+ mGOTMaxCoffEntries = 5;
+ mGOTNumCoffEntries = 0;
+ } else if (mGOTNumCoffEntries == mGOTMaxCoffEntries) {
+ mGOTCoffEntries = (UINT32*)realloc(mGOTCoffEntries, 2 * mGOTMaxCoffEntries * sizeof *mGOTCoffEntries);
+ if (mGOTCoffEntries == NULL) {
+ Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
+ }
+ assert (mGOTCoffEntries != NULL);
+ mGOTMaxCoffEntries += mGOTMaxCoffEntries;
+ }
+ mGOTCoffEntries[mGOTNumCoffEntries++] = GOTCoffEntry;
+ return TRUE;
+}
+
+//
+// 32-bit Unsigned integer comparator for qsort.
+//
+STATIC
+int
+UINT32Comparator (
+ const void* lhs,
+ const void* rhs
+ )
+{
+ if (*(const UINT32*)lhs < *(const UINT32*)rhs) {
+ return -1;
+ }
+ return *(const UINT32*)lhs > *(const UINT32*)rhs;
+}
+
+//
+// Emit accumulated Coff GOT entry relocations into
+// Coff image. This function performs its job
+// once and then releases the entry list, so
+// it can safely be called multiple times.
+//
+STATIC
+VOID
+EmitGOTRelocations (
+ VOID
+ )
+{
+ UINT32 i;
+ if (mGOTCoffEntries == NULL) {
+ return;
+ }
+ //
+ // Emit Coff relocations with Rvas ordered.
+ //
+ qsort(
+ mGOTCoffEntries,
+ mGOTNumCoffEntries,
+ sizeof *mGOTCoffEntries,
+ UINT32Comparator);
+ for (i = 0; i < mGOTNumCoffEntries; i++) {
+ VerboseMsg ("EFI_IMAGE_REL_BASED_DIR64 Offset: 0x%08X", mGOTCoffEntries[i]);
+ CoffAddFixup(
+ mGOTCoffEntries[i],
+ EFI_IMAGE_REL_BASED_DIR64);
+ }
+ free(mGOTCoffEntries);
+ mGOTCoffEntries = NULL;
+ mGOTMaxCoffEntries = 0;
+ mGOTNumCoffEntries = 0;
+}
+
//
// Elf functions interface implementation
//
@@ -349,7 +480,6 @@ ScanSections64 (
mNtHdrOffset = mCoffOffset;
switch (mEhdr->e_machine) {
case EM_X86_64:
- case EM_IA_64:
case EM_AARCH64:
mCoffOffset += sizeof (EFI_IMAGE_NT_HEADERS64);
break;
@@ -444,7 +574,7 @@ ScanSections64 (
mCoffOffset = CoffAlign(mCoffOffset);
if (SectionCount > 1 && mOutImageType == FW_EFI_IMAGE) {
- Warning (NULL, 0, 0, NULL, "Mulitple sections in %s are merged into 1 text section. Source level debug might not work correctly.", mInImageName);
+ Warning (NULL, 0, 0, NULL, "Multiple sections in %s are merged into 1 text section. Source level debug might not work correctly.", mInImageName);
}
//
@@ -498,7 +628,7 @@ ScanSections64 (
}
if (SectionCount > 1 && mOutImageType == FW_EFI_IMAGE) {
- Warning (NULL, 0, 0, NULL, "Mulitple sections in %s are merged into 1 data section. Source level debug might not work correctly.", mInImageName);
+ Warning (NULL, 0, 0, NULL, "Multiple sections in %s are merged into 1 data section. Source level debug might not work correctly.", mInImageName);
}
//
@@ -556,10 +686,6 @@ ScanSections64 (
NtHdr->Pe32Plus.FileHeader.Machine = EFI_IMAGE_MACHINE_X64;
NtHdr->Pe32Plus.OptionalHeader.Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
break;
- case EM_IA_64:
- NtHdr->Pe32Plus.FileHeader.Machine = EFI_IMAGE_MACHINE_IPF;
- NtHdr->Pe32Plus.OptionalHeader.Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
- break;
case EM_AARCH64:
NtHdr->Pe32Plus.FileHeader.Machine = EFI_IMAGE_MACHINE_AARCH64;
NtHdr->Pe32Plus.OptionalHeader.Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
@@ -643,6 +769,7 @@ WriteSections64 (
Elf_Shdr *SecShdr;
UINT32 SecOffset;
BOOLEAN (*Filter)(Elf_Shdr *);
+ Elf64_Addr GOTEntryRva;
//
// Initialize filter pointer
@@ -670,6 +797,9 @@ WriteSections64 (
switch (Shdr->sh_type) {
case SHT_PROGBITS:
/* Copy. */
+ if (Shdr->sh_offset + Shdr->sh_size > mFileBufferSize) {
+ return FALSE;
+ }
memcpy(mCoffFile + mCoffSectionsOffset[Idx],
(UINT8*)mEhdr + Shdr->sh_offset,
(size_t) Shdr->sh_size);
@@ -681,9 +811,9 @@ WriteSections64 (
default:
//
- // Ignore for unkown section type.
+ // Ignore for unknown section type.
//
- VerboseMsg ("%s unknown section type %x. We directly copy this section into Coff file", mInImageName, (unsigned)Shdr->sh_type);
+ VerboseMsg ("%s unknown section type %x. We ignore this unknown section type.", mInImageName, (unsigned)Shdr->sh_type);
break;
}
}
@@ -707,7 +837,7 @@ WriteSections64 (
// section that applies to the entire binary, and which will have its section
// index set to #0 (which is a NULL section with the SHF_ALLOC bit cleared).
//
- // In the absence of GOT based relocations (which we currently don't support),
+ // In the absence of GOT based relocations,
// this RELA section will contain redundant R_xxx_RELATIVE relocations, one
// for every R_xxx_xx64 relocation appearing in the per-section RELA sections.
// (i.e., .rela.text and .rela.data)
@@ -797,24 +927,24 @@ WriteSections64 (
// Absolute relocation.
//
VerboseMsg ("R_X86_64_64");
- VerboseMsg ("Offset: 0x%08X, Addend: 0x%016LX",
- (UINT32)(SecOffset + (Rel->r_offset - SecShdr->sh_addr)),
+ VerboseMsg ("Offset: 0x%08X, Addend: 0x%016LX",
+ (UINT32)(SecOffset + (Rel->r_offset - SecShdr->sh_addr)),
*(UINT64 *)Targ);
*(UINT64 *)Targ = *(UINT64 *)Targ - SymShdr->sh_addr + mCoffSectionsOffset[Sym->st_shndx];
VerboseMsg ("Relocation: 0x%016LX", *(UINT64*)Targ);
break;
case R_X86_64_32:
VerboseMsg ("R_X86_64_32");
- VerboseMsg ("Offset: 0x%08X, Addend: 0x%08X",
- (UINT32)(SecOffset + (Rel->r_offset - SecShdr->sh_addr)),
+ VerboseMsg ("Offset: 0x%08X, Addend: 0x%08X",
+ (UINT32)(SecOffset + (Rel->r_offset - SecShdr->sh_addr)),
*(UINT32 *)Targ);
*(UINT32 *)Targ = (UINT32)((UINT64)(*(UINT32 *)Targ) - SymShdr->sh_addr + mCoffSectionsOffset[Sym->st_shndx]);
VerboseMsg ("Relocation: 0x%08X", *(UINT32*)Targ);
break;
case R_X86_64_32S:
VerboseMsg ("R_X86_64_32S");
- VerboseMsg ("Offset: 0x%08X, Addend: 0x%08X",
- (UINT32)(SecOffset + (Rel->r_offset - SecShdr->sh_addr)),
+ VerboseMsg ("Offset: 0x%08X, Addend: 0x%08X",
+ (UINT32)(SecOffset + (Rel->r_offset - SecShdr->sh_addr)),
*(UINT32 *)Targ);
*(INT32 *)Targ = (INT32)((INT64)(*(INT32 *)Targ) - SymShdr->sh_addr + mCoffSectionsOffset[Sym->st_shndx]);
VerboseMsg ("Relocation: 0x%08X", *(UINT32*)Targ);
@@ -835,22 +965,98 @@ WriteSections64 (
// Relative relocation: Symbol - Ip + Addend
//
VerboseMsg ("R_X86_64_PC32");
- VerboseMsg ("Offset: 0x%08X, Addend: 0x%08X",
- (UINT32)(SecOffset + (Rel->r_offset - SecShdr->sh_addr)),
+ VerboseMsg ("Offset: 0x%08X, Addend: 0x%08X",
+ (UINT32)(SecOffset + (Rel->r_offset - SecShdr->sh_addr)),
*(UINT32 *)Targ);
*(UINT32 *)Targ = (UINT32) (*(UINT32 *)Targ
+ (mCoffSectionsOffset[Sym->st_shndx] - SymShdr->sh_addr)
- (SecOffset - SecShdr->sh_addr));
VerboseMsg ("Relocation: 0x%08X", *(UINT32 *)Targ);
break;
+ case R_X86_64_GOTPCREL:
+ case R_X86_64_GOTPCRELX:
+ case R_X86_64_REX_GOTPCRELX:
+ VerboseMsg ("R_X86_64_GOTPCREL family");
+ VerboseMsg ("Offset: 0x%08X, Addend: 0x%08X",
+ (UINT32)(SecOffset + (Rel->r_offset - SecShdr->sh_addr)),
+ *(UINT32 *)Targ);
+ GOTEntryRva = Rel->r_offset - Rel->r_addend + *(INT32 *)Targ;
+ FindElfGOTSectionFromGOTEntryElfRva(GOTEntryRva);
+ *(UINT32 *)Targ = (UINT32) (*(UINT32 *)Targ
+ + (mCoffSectionsOffset[mGOTShindex] - mGOTShdr->sh_addr)
+ - (SecOffset - SecShdr->sh_addr));
+ VerboseMsg ("Relocation: 0x%08X", *(UINT32 *)Targ);
+ GOTEntryRva += (mCoffSectionsOffset[mGOTShindex] - mGOTShdr->sh_addr); // ELF Rva -> COFF Rva
+ if (AccumulateCoffGOTEntries((UINT32)GOTEntryRva)) {
+ //
+ // Relocate GOT entry if it's the first time we run into it
+ //
+ Targ = mCoffFile + GOTEntryRva;
+ //
+ // Limitation: The following three statements assume memory
+ // at *Targ is valid because the section containing the GOT
+ // has already been copied from the ELF image to the Coff image.
+ // This pre-condition presently holds because the GOT is placed
+ // in section .text, and the ELF text sections are all copied
+ // prior to reaching this point.
+ // If the pre-condition is violated in the future, this fixup
+ // either needs to be deferred after the GOT section is copied
+ // to the Coff image, or the fixup should be performed on the
+ // source Elf image instead of the destination Coff image.
+ //
+ VerboseMsg ("Offset: 0x%08X, Addend: 0x%016LX",
+ (UINT32)GOTEntryRva,
+ *(UINT64 *)Targ);
+ *(UINT64 *)Targ = *(UINT64 *)Targ - SymShdr->sh_addr + mCoffSectionsOffset[Sym->st_shndx];
+ VerboseMsg ("Relocation: 0x%016LX", *(UINT64*)Targ);
+ }
+ break;
default:
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) {
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:
+ //
+ // In order to handle Cortex-A53 erratum #843419, the LD linker may
+ // convert ADRP instructions into ADR instructions, but without
+ // updating the static relocation type, and so we may end up here
+ // while the instruction in question is actually ADR. So let's
+ // just disregard it: the section offset check we apply below to
+ // ADR instructions will trigger for its R_AARCH64_xxx_ABS_LO12_NC
+ // companion instruction as well, so it is safe to omit it here.
+ //
+ if ((*(UINT32 *)Targ & BIT31) == 0) {
+ break;
+ }
+
//
// AArch64 PG_H21 relocations are typically paired with ABS_LO12
// relocations, where a PC-relative reference with +/- 4 GB range is
@@ -869,7 +1075,6 @@ WriteSections64 (
// Attempt to convert the ADRP into an ADR instruction.
// This is only possible if the symbol is within +/- 1 MB.
//
- INT64 Offset;
// Decode the ADRP instruction
Offset = (INT32)((*(UINT32 *)Targ & 0xffffe0) << 8);
@@ -981,18 +1186,42 @@ WriteRelocations64 (
case R_X86_64_NONE:
case R_X86_64_PC32:
case R_X86_64_PLT32:
+ case R_X86_64_GOTPCREL:
+ case R_X86_64_GOTPCRELX:
+ case R_X86_64_REX_GOTPCRELX:
break;
case R_X86_64_64:
- VerboseMsg ("EFI_IMAGE_REL_BASED_DIR64 Offset: 0x%08X",
+ VerboseMsg ("EFI_IMAGE_REL_BASED_DIR64 Offset: 0x%08X",
mCoffSectionsOffset[RelShdr->sh_info] + (Rel->r_offset - SecShdr->sh_addr));
CoffAddFixup(
(UINT32) ((UINT64) mCoffSectionsOffset[RelShdr->sh_info]
+ (Rel->r_offset - SecShdr->sh_addr)),
EFI_IMAGE_REL_BASED_DIR64);
break;
- case R_X86_64_32S:
+ //
+ // R_X86_64_32 and R_X86_64_32S are ELF64 relocations emitted when using
+ // the SYSV X64 ABI small non-position-independent code model.
+ // R_X86_64_32 is used for unsigned 32-bit immediates with a 32-bit operand
+ // size. The value is either not extended, or zero-extended to 64 bits.
+ // R_X86_64_32S is used for either signed 32-bit non-rip-relative displacements
+ // or signed 32-bit immediates with a 64-bit operand size. The value is
+ // sign-extended to 64 bits.
+ // EFI_IMAGE_REL_BASED_HIGHLOW is a PE relocation that uses 32-bit arithmetic
+ // for rebasing an image.
+ // EFI PE binaries declare themselves EFI_IMAGE_FILE_LARGE_ADDRESS_AWARE and
+ // may load above 2GB. If an EFI PE binary with a converted R_X86_64_32S
+ // relocation is loaded above 2GB, the value will get sign-extended to the
+ // negative part of the 64-bit address space. The negative part of the 64-bit
+ // address space is unmapped, so accessing such an address page-faults.
+ // In order to support R_X86_64_32S, it is necessary to unset
+ // EFI_IMAGE_FILE_LARGE_ADDRESS_AWARE, and the EFI PE loader must implement
+ // this flag and abstain from loading such a PE binary above 2GB.
+ // Since this feature is not supported, support for R_X86_64_32S (and hence
+ // the small non-position-independent code model) is disabled.
+ //
+ // case R_X86_64_32S:
case R_X86_64_32:
- VerboseMsg ("EFI_IMAGE_REL_BASED_HIGHLOW Offset: 0x%08X",
+ VerboseMsg ("EFI_IMAGE_REL_BASED_HIGHLOW Offset: 0x%08X",
mCoffSectionsOffset[RelShdr->sh_info] + (Rel->r_offset - SecShdr->sh_addr));
CoffAddFixup(
(UINT32) ((UINT64) mCoffSectionsOffset[RelShdr->sh_info]
@@ -1020,6 +1249,8 @@ WriteRelocations64 (
case R_AARCH64_LDST32_ABS_LO12_NC:
case R_AARCH64_LDST64_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
// the relative offsets between sections have been preserved in
@@ -1049,10 +1280,32 @@ WriteRelocations64 (
Error (NULL, 0, 3000, "Not Supported", "This tool does not support relocations for ELF with e_machine %u (processor type).", (unsigned) mEhdr->e_machine);
}
}
+ if (mEhdr->e_machine == EM_X86_64 && RelShdr->sh_info == mGOTShindex) {
+ //
+ // Tack relocations for GOT entries after other relocations for
+ // the section the GOT is in, as it's usually found at the end
+ // of the section. This is done in order to maintain Rva order
+ // of Coff relocations.
+ //
+ EmitGOTRelocations();
+ }
}
}
}
+ if (mEhdr->e_machine == EM_X86_64) {
+ //
+ // This is a safety net just in case the GOT is in a section
+ // with no other relocations and the first invocation of
+ // EmitGOTRelocations() above was skipped. This invocation
+ // does not maintain Rva order of Coff relocations.
+ // At present, with a single text section, all references to
+ // the GOT and the GOT itself reside in section .text, so
+ // if there's a GOT at all, the first invocation above
+ // is executed.
+ //
+ EmitGOTRelocations();
+ }
//
// Pad by adding empty entries.
//