Re: [edk2] [PATCH v4] BaseTools/GenFw: Add X64 GOTPCREL Support to GenFw

2018-07-10 Thread Gao, Liming
Reviewed-by: Liming Gao 

>-Original Message-
>From: edk2-devel [mailto:edk2-devel-boun...@lists.01.org] On Behalf Of
>Zenith432
>Sent: Monday, July 09, 2018 8:58 PM
>To: edk2-devel@lists.01.org
>Cc: Gao, Liming 
>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 
>Cc: Yonghong Zhu 
>Cc: Liming Gao 
>Contributed-under: TianoCore Contribution Agreement 1.1
>Signed-off-by: Zenith432 
>---
> 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.
>+// 

[edk2] [PATCH v4] BaseTools/GenFw: Add X64 GOTPCREL Support to GenFw

2018-07-09 Thread Zenith432


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 
Cc: Yonghong Zhu 
Cc: Liming Gao 
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Zenith432 
---
 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,