From bed04bf1df9d442f4a8e4d914d0dcd9155f2b931 Mon Sep 17 00:00:00 2001 From: Simon Atanasyan Date: Fri, 21 Oct 2016 07:22:30 +0000 Subject: [PATCH] [ELF][MIPS] Put local GOT entries accessed via a 16-bit index first Some MIPS relocations used to access GOT entries are able to manipulate 16-bit index. The other ones like R_MIPS_CALL_HI16/LO16 can handle 32-bit indexes. 16-bit relocations are generated by default. The 32-bit relocations are generated by -mxgot flag passed to compiler. Usually these relocation are not mixed in the same code but files like crt*.o contain 16-bit relocations so even if all "user's" code compiled with -mxgot flag a few 16-bit relocations might come to the linking phase. Now LLD does not differentiate local GOT entries accessed via a 16-bit and 32-bit indexes. That might lead to relocation's overflow if 16-bit entries are allocated to far from the beginning of the GOT. The patch introduces new "part" of MIPS GOT dedicated to the local GOT entries accessed by 32-bit relocations. That allows to put local GOT entries accessed via a 16-bit index first and escape relocation's overflow. Differential revision: https://reviews.llvm.org/D25833 llvm-svn: 284809 --- lld/ELF/InputSection.cpp | 1 + lld/ELF/OutputSections.cpp | 14 ++++++++++-- lld/ELF/OutputSections.h | 1 + lld/ELF/Relocations.cpp | 19 +++++++++-------- lld/ELF/Relocations.h | 1 + lld/ELF/Symbols.cpp | 8 +++---- lld/ELF/Symbols.h | 3 +++ lld/ELF/Target.cpp | 7 +++--- lld/test/ELF/mips-xgot-order.s | 48 ++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 84 insertions(+), 18 deletions(-) create mode 100644 lld/test/ELF/mips-xgot-order.s diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index 21bbfa9..09acd0f 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -325,6 +325,7 @@ static typename ELFT::uint getSymVA(uint32_t Type, typename ELFT::uint A, // of sum the symbol's value and the addend. return Out::Got->getMipsLocalPageOffset(Body.getVA(A)); case R_MIPS_GOT_OFF: + case R_MIPS_GOT_OFF32: // In case of MIPS if a GOT relocation has non-zero addend this addend // should be applied to the GOT entry content not to the GOT entry offset. // That is why we use separate expression type. diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp index e9546f4..66d44e2 100644 --- a/lld/ELF/OutputSections.cpp +++ b/lld/ELF/OutputSections.cpp @@ -202,8 +202,15 @@ void GotSection::addMipsEntry(SymbolBody &Sym, uintX_t Addend, // Ignore addends for preemptible symbols. They got single GOT entry anyway. AddEntry(Sym, 0, MipsGlobal); Sym.IsInGlobalMipsGot = true; - } else + } else if (Expr == R_MIPS_GOT_OFF32) { + AddEntry(Sym, Addend, MipsLocal32); + Sym.Is32BitMipsGot = true; + } else { + // Hold local GOT entries accessed via a 16-bit index separately. + // That allows to write them in the beginning of the GOT and keep + // their indexes as less as possible to escape relocation's overflow. AddEntry(Sym, Addend, MipsLocal); + } } template bool GotSection::addDynTlsEntry(SymbolBody &Sym) { @@ -250,6 +257,8 @@ GotSection::getMipsGotOffset(const SymbolBody &B, uintX_t Addend) const { GotBlockOff = getMipsTlsOffset(); else if (B.IsInGlobalMipsGot) GotBlockOff = getMipsLocalEntriesNum() * sizeof(uintX_t); + else if (B.Is32BitMipsGot) + GotBlockOff = (MipsPageEntries + MipsLocal.size()) * sizeof(uintX_t); else GotBlockOff = MipsPageEntries * sizeof(uintX_t); // Calculate index of the GOT entry in the block. @@ -288,7 +297,7 @@ const SymbolBody *GotSection::getMipsFirstGlobalEntry() const { template unsigned GotSection::getMipsLocalEntriesNum() const { - return MipsPageEntries + MipsLocal.size(); + return MipsPageEntries + MipsLocal.size() + MipsLocal32.size(); } template void GotSection::finalize() { @@ -347,6 +356,7 @@ template void GotSection::writeMipsGot(uint8_t *Buf) { writeUint(Entry, VA); }; std::for_each(std::begin(MipsLocal), std::end(MipsLocal), AddEntry); + std::for_each(std::begin(MipsLocal32), std::end(MipsLocal32), AddEntry); std::for_each(std::begin(MipsGlobal), std::end(MipsGlobal), AddEntry); // Initialize TLS-related GOT entries. If the entry has a corresponding // dynamic relocations, leave it initialized by zero. Write down adjusted diff --git a/lld/ELF/OutputSections.h b/lld/ELF/OutputSections.h index 14e9dfe..182c96f 100644 --- a/lld/ELF/OutputSections.h +++ b/lld/ELF/OutputSections.h @@ -217,6 +217,7 @@ private: typedef std::vector MipsGotEntries; llvm::DenseMap MipsGotMap; MipsGotEntries MipsLocal; + MipsGotEntries MipsLocal32; MipsGotEntries MipsGlobal; // Write MIPS-specific parts of the GOT. diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp index 2f7757a..62f81f7 100644 --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -62,10 +62,11 @@ namespace elf { static bool refersToGotEntry(RelExpr Expr) { return Expr == R_GOT || Expr == R_GOT_OFF || Expr == R_MIPS_GOT_LOCAL_PAGE || - Expr == R_MIPS_GOT_OFF || Expr == R_MIPS_TLSGD || - Expr == R_MIPS_TLSLD || Expr == R_GOT_PAGE_PC || Expr == R_GOT_PC || - Expr == R_GOT_FROM_END || Expr == R_TLSGD || Expr == R_TLSGD_PC || - Expr == R_TLSDESC || Expr == R_TLSDESC_PAGE; + Expr == R_MIPS_GOT_OFF || Expr == R_MIPS_GOT_OFF32 || + Expr == R_MIPS_TLSGD || Expr == R_MIPS_TLSLD || + Expr == R_GOT_PAGE_PC || Expr == R_GOT_PC || Expr == R_GOT_FROM_END || + Expr == R_TLSGD || Expr == R_TLSGD_PC || Expr == R_TLSDESC || + Expr == R_TLSDESC_PAGE; } static bool isPreemptible(const SymbolBody &Body, uint32_t Type) { @@ -303,11 +304,11 @@ static bool isStaticLinkTimeConstant(RelExpr E, uint32_t Type, const SymbolBody &Body) { // These expressions always compute a constant if (E == R_SIZE || E == R_GOT_FROM_END || E == R_GOT_OFF || - E == R_MIPS_GOT_LOCAL_PAGE || E == R_MIPS_GOT_OFF || E == R_MIPS_TLSGD || - E == R_GOT_PAGE_PC || E == R_GOT_PC || E == R_PLT_PC || - E == R_TLSGD_PC || E == R_TLSGD || E == R_PPC_PLT_OPD || - E == R_TLSDESC_CALL || E == R_TLSDESC_PAGE || E == R_HINT || - E == R_THUNK_PC || E == R_THUNK_PLT_PC) + E == R_MIPS_GOT_LOCAL_PAGE || E == R_MIPS_GOT_OFF || + E == R_MIPS_GOT_OFF32 || E == R_MIPS_TLSGD || E == R_GOT_PAGE_PC || + E == R_GOT_PC || E == R_PLT_PC || E == R_TLSGD_PC || E == R_TLSGD || + E == R_PPC_PLT_OPD || E == R_TLSDESC_CALL || E == R_TLSDESC_PAGE || + E == R_HINT || E == R_THUNK_PC || E == R_THUNK_PLT_PC) return true; // These never do, except if the entire file is position dependent or if diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h index f2f17aa..728034b 100644 --- a/lld/ELF/Relocations.h +++ b/lld/ELF/Relocations.h @@ -36,6 +36,7 @@ enum RelExpr { R_HINT, R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOT_OFF, + R_MIPS_GOT_OFF32, R_MIPS_TLSGD, R_MIPS_TLSLD, R_NEG_TLS, diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp index e813ca1..707143e 100644 --- a/lld/ELF/Symbols.cpp +++ b/lld/ELF/Symbols.cpp @@ -93,13 +93,13 @@ static typename ELFT::uint getSymVA(const SymbolBody &Body, SymbolBody::SymbolBody(Kind K, uint32_t NameOffset, uint8_t StOther, uint8_t Type) : SymbolKind(K), NeedsCopyOrPltAddr(false), IsLocal(true), - IsInGlobalMipsGot(false), Type(Type), StOther(StOther), - NameOffset(NameOffset) {} + IsInGlobalMipsGot(false), Is32BitMipsGot(false), Type(Type), + StOther(StOther), NameOffset(NameOffset) {} SymbolBody::SymbolBody(Kind K, StringRef Name, uint8_t StOther, uint8_t Type) : SymbolKind(K), NeedsCopyOrPltAddr(false), IsLocal(false), - IsInGlobalMipsGot(false), Type(Type), StOther(StOther), - Name({Name.data(), Name.size()}) {} + IsInGlobalMipsGot(false), Is32BitMipsGot(false), Type(Type), + StOther(StOther), Name({Name.data(), Name.size()}) {} StringRef SymbolBody::getName() const { assert(!isLocal()); diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h index 9f68450..2856ded 100644 --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -120,6 +120,9 @@ public: // True if this symbol has an entry in the global part of MIPS GOT. unsigned IsInGlobalMipsGot : 1; + // True if this symbol is referenced by 32-bit GOT relocations. + unsigned Is32BitMipsGot : 1; + // The following fields have the same meaning as the ELF symbol attributes. uint8_t Type; // symbol type uint8_t StOther; // st_other field value diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp index 1a23341..c3a3c29 100644 --- a/lld/ELF/Target.cpp +++ b/lld/ELF/Target.cpp @@ -1940,13 +1940,14 @@ RelExpr MipsTargetInfo::getRelExpr(uint32_t Type, return R_MIPS_GOT_LOCAL_PAGE; // fallthrough case R_MIPS_CALL16: + case R_MIPS_GOT_DISP: + case R_MIPS_TLS_GOTTPREL: + return R_MIPS_GOT_OFF; case R_MIPS_CALL_HI16: case R_MIPS_CALL_LO16: - case R_MIPS_GOT_DISP: case R_MIPS_GOT_HI16: case R_MIPS_GOT_LO16: - case R_MIPS_TLS_GOTTPREL: - return R_MIPS_GOT_OFF; + return R_MIPS_GOT_OFF32; case R_MIPS_GOT_PAGE: return R_MIPS_GOT_LOCAL_PAGE; case R_MIPS_TLS_GD: diff --git a/lld/test/ELF/mips-xgot-order.s b/lld/test/ELF/mips-xgot-order.s new file mode 100644 index 0000000..26d6ef0 --- /dev/null +++ b/lld/test/ELF/mips-xgot-order.s @@ -0,0 +1,48 @@ +# Check that GOT entries accessed via 16-bit indexing are allocated +# in the beginning of the GOT. + +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o +# RUN: ld.lld %t.o -o %t.exe +# RUN: llvm-objdump -d -s -t %t.exe | FileCheck %s + +# REQUIRES: mips + +# CHECK: Disassembly of section .text: +# CHECK-NEXT: __start: +# CHECK-NEXT: 20000: 3c 02 00 00 lui $2, 0 +# CHECK-NEXT: 20004: 8c 42 80 20 lw $2, -32736($2) +# CHECK-NEXT: 20008: 3c 02 00 00 lui $2, 0 +# CHECK-NEXT: 2000c: 8c 42 80 24 lw $2, -32732($2) +# +# CHECK: bar: +# CHECK-NEXT: 20010: 8c 42 80 1c lw $2, -32740($2) +# CHECK-NEXT: 20014: 8c 42 80 18 lw $2, -32744($2) +# CHECK-NEXT: 20018: 20 42 00 00 addi $2, $2, 0 + +# CHECK: Contents of section .got: +# CHECK-NEXT: 30000 00000000 80000000 00040000 00020010 +# ^ %hi(loc) +# ^ %got(bar) +# CHECK-NEXT: 30010 00020000 00040000 +# ^ %got_hi/lo(start) +# ^ %got_hi/lo(loc) + +# CHECK: 00040000 .data 00000000 loc +# CHECK: 00020000 .text 00000000 __start +# CHECK: 00020010 .text 00000000 bar + + .text + .global __start, bar +__start: + lui $2, %got_hi(__start) + lw $2, %got_lo(__start)($2) + lui $2, %got_hi(loc) + lw $2, %got_lo(loc)($2) +bar: + lw $2, %got(bar)($2) + lw $2, %got(loc)($2) + addi $2, $2, %lo(loc) + + .data +loc: + .word 0 -- 2.7.4