public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "Gao, Liming" <liming.gao@intel.com>
To: Zenith432 <zenith432@users.sourceforge.net>,
	"edk2-devel@lists.01.org" <edk2-devel@lists.01.org>
Subject: Re: [PATCH v4] BaseTools/GenFw: Add X64 GOTPCREL Support to GenFw
Date: Tue, 10 Jul 2018 07:07:31 +0000	[thread overview]
Message-ID: <4A89E2EF3DFEDB4C8BFDE51014F606A14E2B93B2@SHSMSX104.ccr.corp.intel.com> (raw)
In-Reply-To: <1442830504.1295133.1531141095532@mail.yahoo.com>

Reviewed-by: Liming Gao <liming.gao@intel.com>

>-----Original Message-----
>From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of
>Zenith432
>Sent: Monday, July 09, 2018 8:58 PM
>To: edk2-devel@lists.01.org
>Cc: Gao, Liming <liming.gao@intel.com>
>Subject: [edk2] [PATCH v4] BaseTools/GenFw: Add X64 GOTPCREL Support to
>GenFw
>
>
>Adds support for the following X64 ELF relocations to GenFw
>  R_X86_64_GOTPCREL
>  R_X86_64_GOTPCRELX
>  R_X86_64_REX_GOTPCRELX
>
>Background:
>The GCC49 and GCC5 toolchains use the small pie model for X64.  In the
>small pie model, gcc emits a GOTPCREL relocation whenever C code takes
>the address of a global function.  The emission of GOTPCREL is mitigated
>by several factors
>1. In GCC49, all global symbols are declared hidden thereby eliminating
>the emission of GOTPCREL.
>2. In GCC5, LTO is used.  In LTO, the complier first creates intermediate
>representation (IR) files.  During the static link stage, the LTO compiler
>combines all IR files as a single compilation unit, using linker symbol
>assistance to generate code.  Any global symbols defined in the IR that
>are not referenced from outside the IR are converted to local symbols -
>thereby eliminating the emission of GOTPCREL for them.
>3. The linker (binutils ld) further transforms any GOTPCREL used with
>the movq opcode to a direct rip-relative relocation used with the leaq
>opcode.  This linker optimization can be disabled with the option
>-Wl,--no-relax.  Furthermore, gcc is able to emit GOTPCREL with other
>opcodes
>  - pushq opcode for passing arguments to functions.
>  - addq/subq opcodes for pointer arithmetic.
>These other opcode uses are not transformed by the linker.
>Ultimately, in GCC5 there are some emissions of GOTPCREL that survive
>all these mitigations - if C code takes the address of a global function
>defined in assembly code - and performs pointer arithmetic on the
>address - then the GOTPCREL remains in the final linker product.
>A GOTPCREL relocation today causes the build to stop since GenFw does
>not handle them.  It is possible to eliminate any remaining GOTPCREL
>emissions by manually declaring the global symbols causing them to have
>hidden visibility.  This patch is offered instead to allow GenFw to
>handle any residual GOTPCREL.
>
>Cc: Shi Steven <steven.shi@intel.com>
>Cc: Yonghong Zhu <yonghong.zhu@intel.com>
>Cc: Liming Gao <liming.gao@intel.com>
>Contributed-under: TianoCore Contribution Agreement 1.1
>Signed-off-by: Zenith432 <zenith432@users.sourceforge.net>
>---
> BaseTools/Source/C/GenFw/Elf64Convert.c | 203
>+++++++++++++++++++++++-
> BaseTools/Source/C/GenFw/elf_common.h   |  17 ++
> 2 files changed, 219 insertions(+), 1 deletion(-)
>
>diff --git a/BaseTools/Source/C/GenFw/Elf64Convert.c
>b/BaseTools/Source/C/GenFw/Elf64Convert.c
>index 4636cfee..90351125 100644
>--- a/BaseTools/Source/C/GenFw/Elf64Convert.c
>+++ b/BaseTools/Source/C/GenFw/Elf64Convert.c
>@@ -94,6 +94,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 +331,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
> //
>@@ -643,6 +780,7 @@ WriteSections64 (
>   Elf_Shdr    *SecShdr;
>   UINT32      SecOffset;
>   BOOLEAN     (*Filter)(Elf_Shdr *);
>+  Elf64_Addr  GOTEntryRva;
>
>   //
>   // Initialize filter pointer
>@@ -710,7 +848,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)
>@@ -846,6 +984,44 @@ WriteSections64 (
>               - (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));
>           }
>@@ -984,6 +1160,9 @@ 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",
>@@ -1052,10 +1231,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.
>   //
>diff --git a/BaseTools/Source/C/GenFw/elf_common.h
>b/BaseTools/Source/C/GenFw/elf_common.h
>index 242ad00a..03dec50c 100644
>--- a/BaseTools/Source/C/GenFw/elf_common.h
>+++ b/BaseTools/Source/C/GenFw/elf_common.h
>@@ -1052,6 +1052,23 @@ typedef struct {
> #define  R_X86_64_DTPOFF32  21  /* Offset in TLS block */
> #define  R_X86_64_GOTTPOFF  22  /* PC relative offset to IE GOT entry */
> #define  R_X86_64_TPOFF32  23  /* Offset in static TLS block */
>+#define  R_X86_64_PC64  24  /* PC relative 64 bit */
>+#define  R_X86_64_GOTOFF64  25  /* 64 bit offset to GOT */
>+#define  R_X86_64_GOTPC3  26  /* 32 bit signed pc relative offset to GOT */
>+#define  R_X86_64_GOT64  27  /* 64-bit GOT entry offset */
>+#define  R_X86_64_GOTPCREL64  28  /* 64-bit PC relative offset to GOT entry
>*/
>+#define  R_X86_64_GOTPC64  29  /* 64-bit PC relative offset to GOT */
>+#define  R_X86_64_GOTPLT64  30  /* like GOT64, says PLT entry needed */
>+#define  R_X86_64_PLTOFF64  31  /* 64-bit GOT relative offset to PLT entry
>*/
>+#define  R_X86_64_SIZE32  32  /* Size of symbol plus 32-bit addend */
>+#define  R_X86_64_SIZE64  33  /* Size of symbol plus 64-bit addend */
>+#define  R_X86_64_GOTPC32_TLSDESC  34  /* GOT offset for TLS descriptor.
>*/
>+#define  R_X86_64_TLSDESC_CALL  35  /* Marker for call through TLS
>descriptor. */
>+#define  R_X86_64_TLSDESC  36  /* TLS descriptor. */
>+#define  R_X86_64_IRELATIVE  37  /* Adjust indirectly by program base */
>+#define  R_X86_64_RELATIVE64  38  /* 64-bit adjust by program base */
>+#define  R_X86_64_GOTPCRELX  41  /* Load from 32 bit signed pc relative
>offset to GOT entry without REX prefix, relaxable. */
>+#define  R_X86_64_REX_GOTPCRELX  42  /* Load from 32 bit signed pc
>relative offset to GOT entry with REX prefix, relaxable. */
>
>
> #endif /* !_SYS_ELF_COMMON_H_ */
>--
>2.17.1
>
>_______________________________________________
>edk2-devel mailing list
>edk2-devel@lists.01.org
>https://lists.01.org/mailman/listinfo/edk2-devel


      reply	other threads:[~2018-07-10  7:07 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <1442830504.1295133.1531141095532.ref@mail.yahoo.com>
2018-07-09 12:58 ` [PATCH v4] BaseTools/GenFw: Add X64 GOTPCREL Support to GenFw Zenith432
2018-07-10  7:07   ` Gao, Liming [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4A89E2EF3DFEDB4C8BFDE51014F606A14E2B93B2@SHSMSX104.ccr.corp.intel.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox