[ELF][MIPS] Support GOT entries for non-preemptible symbols with different addends

There are two motivations for this patch. The first one is a preparation
for support MIPS TLS relocations. It might sound like a joke but for GOT
entries related to TLS relocations MIPS ABI uses almost regular approach
with creation of dynamic relocations for each GOT enty etc. But we need
to separate these 'regular' TLS related entries from MIPS specific local
and global parts of GOT. ABI declare simple solution - all TLS related
entries allocated at the end of GOT after local/global parts. The second
motivation it to support GOT relocations for non-preemptible symbols
with addends. If we have more than one GOT relocations against symbol S
with different addends we need to create GOT entries for each unique
Symbol/Addend pairs.

So we store all MIPS GOT entries in separate containers. For non-preemptible
symbols we have to maintain two data structures. The first one is MipsLocal
vector. Each entry corresponds to the GOT entry from the 'local' part
of the GOT contains the symbol's address plus addend. The second one
is MipsLocalMap. It is a map from Symbol/Addend pair to the GOT index.

Differential Revision: http://reviews.llvm.org/D21297

llvm-svn: 273127
This commit is contained in:
Simon Atanasyan 2016-06-19 21:39:37 +00:00
parent 3ffe2dd4d2
commit 4132511cdc
14 changed files with 292 additions and 153 deletions

View File

@ -215,13 +215,11 @@ getSymVA(uint32_t Type, typename ELFT::uint A, typename ELFT::uint P,
// should be initialized by 'page address'. This address is high 16-bits // should be initialized by 'page address'. This address is high 16-bits
// of sum the symbol's value and the addend. // of sum the symbol's value and the addend.
return Out<ELFT>::Got->getMipsLocalPageOffset(Body.getVA<ELFT>(A)); return Out<ELFT>::Got->getMipsLocalPageOffset(Body.getVA<ELFT>(A));
case R_MIPS_GOT_LOCAL: case R_MIPS_GOT_OFF:
// For non-local symbols GOT entries should contain their full // In case of MIPS if a GOT relocation has non-zero addend this addend
// addresses. But if such symbol cannot be preempted, we do not // should be applied to the GOT entry content not to the GOT entry offset.
// have to put them into the "global" part of GOT and use dynamic // That is why we use separate expression type.
// linker to determine their actual addresses. That is why we return Out<ELFT>::Got->getMipsGotOffset(Body, A);
// create GOT entries for them in the "local" part of GOT.
return Out<ELFT>::Got->getMipsLocalEntryOffset(Body.getVA<ELFT>(A));
case R_PPC_OPD: { case R_PPC_OPD: {
uint64_t SymVA = Body.getVA<ELFT>(A); uint64_t SymVA = Body.getVA<ELFT>(A);
// If we have an undefined weak symbol, we might get here with a symbol // If we have an undefined weak symbol, we might get here with a symbol

View File

@ -92,58 +92,67 @@ GotSection<ELFT>::GotSection()
this->Header.sh_addralign = sizeof(uintX_t); this->Header.sh_addralign = sizeof(uintX_t);
} }
template <class ELFT> void GotSection<ELFT>::addEntry(SymbolBody &Sym) { template <class ELFT>
if (Config->EMachine == EM_MIPS) { void GotSection<ELFT>::addEntry(SymbolBody &Sym) {
// For "true" local symbols which can be referenced from the same module
// only compiler creates two instructions for address loading:
//
// lw $8, 0($gp) # R_MIPS_GOT16
// addi $8, $8, 0 # R_MIPS_LO16
//
// The first instruction loads high 16 bits of the symbol address while
// the second adds an offset. That allows to reduce number of required
// GOT entries because only one global offset table entry is necessary
// for every 64 KBytes of local data. So for local symbols we need to
// allocate number of GOT entries to hold all required "page" addresses.
//
// All global symbols (hidden and regular) considered by compiler uniformly.
// It always generates a single `lw` instruction and R_MIPS_GOT16 relocation
// to load address of the symbol. So for each such symbol we need to
// allocate dedicated GOT entry to store its address.
//
// If a symbol is preemptible we need help of dynamic linker to get its
// final address. The corresponding GOT entries are allocated in the
// "global" part of GOT. Entries for non preemptible global symbol allocated
// in the "local" part of GOT.
//
// See "Global Offset Table" in Chapter 5:
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
if (Sym.isLocal()) {
// At this point we do not know final symbol value so to reduce number
// of allocated GOT entries do the following trick. Save all output
// sections referenced by GOT relocations. Then later in the `finalize`
// method calculate number of "pages" required to cover all saved output
// section and allocate appropriate number of GOT entries.
auto *OutSec = cast<DefinedRegular<ELFT>>(&Sym)->Section->OutSec;
MipsOutSections.insert(OutSec);
return;
}
if (!Sym.isPreemptible()) {
// In case of non-local symbols require an entry in the local part
// of MIPS GOT, we set GotIndex to 1 just to accent that this symbol
// has the GOT entry and escape creation more redundant GOT entries.
// FIXME (simon): We can try to store such symbols in the `Entries`
// container. But in that case we have to sort out that container
// and update GotIndex assigned to symbols.
Sym.GotIndex = 1;
++MipsLocalEntries;
return;
}
}
Sym.GotIndex = Entries.size(); Sym.GotIndex = Entries.size();
Entries.push_back(&Sym); Entries.push_back(&Sym);
} }
template <class ELFT>
void GotSection<ELFT>::addMipsEntry(SymbolBody &Sym, uintX_t Addend,
RelExpr Expr) {
// For "true" local symbols which can be referenced from the same module
// only compiler creates two instructions for address loading:
//
// lw $8, 0($gp) # R_MIPS_GOT16
// addi $8, $8, 0 # R_MIPS_LO16
//
// The first instruction loads high 16 bits of the symbol address while
// the second adds an offset. That allows to reduce number of required
// GOT entries because only one global offset table entry is necessary
// for every 64 KBytes of local data. So for local symbols we need to
// allocate number of GOT entries to hold all required "page" addresses.
//
// All global symbols (hidden and regular) considered by compiler uniformly.
// It always generates a single `lw` instruction and R_MIPS_GOT16 relocation
// to load address of the symbol. So for each such symbol we need to
// allocate dedicated GOT entry to store its address.
//
// If a symbol is preemptible we need help of dynamic linker to get its
// final address. The corresponding GOT entries are allocated in the
// "global" part of GOT. Entries for non preemptible global symbol allocated
// in the "local" part of GOT.
//
// See "Global Offset Table" in Chapter 5:
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
if (Expr == R_MIPS_GOT_LOCAL_PAGE) {
// At this point we do not know final symbol value so to reduce number
// of allocated GOT entries do the following trick. Save all output
// sections referenced by GOT relocations. Then later in the `finalize`
// method calculate number of "pages" required to cover all saved output
// section and allocate appropriate number of GOT entries.
auto *OutSec = cast<DefinedRegular<ELFT>>(&Sym)->Section->OutSec;
MipsOutSections.insert(OutSec);
return;
}
auto AddEntry = [&](SymbolBody &S, uintX_t A, MipsGotEntries &Items) {
if (S.isInGot() && !A)
return;
size_t NewIndex = Items.size();
if (!MipsGotMap.insert({{&S, A}, NewIndex}).second)
return;
Items.emplace_back(&S, A);
if (!A)
S.GotIndex = NewIndex;
};
if (Sym.isPreemptible()) {
// Ignore addends for preemptible symbols. They got single GOT entry anyway.
AddEntry(Sym, 0, MipsGlobal);
Sym.IsInGlobalMipsGot = true;
} else
AddEntry(Sym, Addend, MipsLocal);
}
template <class ELFT> bool GotSection<ELFT>::addDynTlsEntry(SymbolBody &Sym) { template <class ELFT> bool GotSection<ELFT>::addDynTlsEntry(SymbolBody &Sym) {
if (Sym.GlobalDynIndex != -1U) if (Sym.GlobalDynIndex != -1U)
return false; return false;
@ -170,20 +179,31 @@ typename GotSection<ELFT>::uintX_t
GotSection<ELFT>::getMipsLocalPageOffset(uintX_t EntryValue) { GotSection<ELFT>::getMipsLocalPageOffset(uintX_t EntryValue) {
// Initialize the entry by the %hi(EntryValue) expression // Initialize the entry by the %hi(EntryValue) expression
// but without right-shifting. // but without right-shifting.
return getMipsLocalEntryOffset((EntryValue + 0x8000) & ~0xffff); EntryValue = (EntryValue + 0x8000) & ~0xffff;
}
template <class ELFT>
typename GotSection<ELFT>::uintX_t
GotSection<ELFT>::getMipsLocalEntryOffset(uintX_t EntryValue) {
// Take into account MIPS GOT header. // Take into account MIPS GOT header.
// See comment in the GotSection::writeTo. // See comment in the GotSection::writeTo.
size_t NewIndex = MipsLocalGotPos.size() + 2; size_t NewIndex = MipsLocalGotPos.size() + 2;
auto P = MipsLocalGotPos.insert(std::make_pair(EntryValue, NewIndex)); auto P = MipsLocalGotPos.insert(std::make_pair(EntryValue, NewIndex));
assert(!P.second || MipsLocalGotPos.size() <= MipsLocalEntries); assert(!P.second || MipsLocalGotPos.size() <= MipsPageEntries);
return (uintX_t)P.first->second * sizeof(uintX_t) - MipsGPOffset; return (uintX_t)P.first->second * sizeof(uintX_t) - MipsGPOffset;
} }
template <class ELFT>
typename GotSection<ELFT>::uintX_t
GotSection<ELFT>::getMipsGotOffset(const SymbolBody &B, uintX_t Addend) const {
uintX_t Off = MipsPageEntries;
if (B.IsInGlobalMipsGot)
Off += MipsLocal.size() + B.GotIndex;
else if (B.isInGot())
Off += B.GotIndex;
else {
auto It = MipsGotMap.find({&B, Addend});
assert(It != MipsGotMap.end());
Off += It->second;
}
return Off * sizeof(uintX_t) - MipsGPOffset;
}
template <class ELFT> template <class ELFT>
typename GotSection<ELFT>::uintX_t typename GotSection<ELFT>::uintX_t
GotSection<ELFT>::getGlobalDynAddr(const SymbolBody &B) const { GotSection<ELFT>::getGlobalDynAddr(const SymbolBody &B) const {
@ -198,12 +218,12 @@ GotSection<ELFT>::getGlobalDynOffset(const SymbolBody &B) const {
template <class ELFT> template <class ELFT>
const SymbolBody *GotSection<ELFT>::getMipsFirstGlobalEntry() const { const SymbolBody *GotSection<ELFT>::getMipsFirstGlobalEntry() const {
return Entries.empty() ? nullptr : Entries.front(); return MipsGlobal.empty() ? nullptr : MipsGlobal.front().first;
} }
template <class ELFT> template <class ELFT>
unsigned GotSection<ELFT>::getMipsLocalEntriesNum() const { unsigned GotSection<ELFT>::getMipsLocalEntriesNum() const {
return MipsLocalEntries; return MipsPageEntries + MipsLocal.size();
} }
template <class ELFT> void GotSection<ELFT>::finalize() { template <class ELFT> void GotSection<ELFT>::finalize() {
@ -211,55 +231,62 @@ template <class ELFT> void GotSection<ELFT>::finalize() {
if (Config->EMachine == EM_MIPS) { if (Config->EMachine == EM_MIPS) {
// Take into account MIPS GOT header. // Take into account MIPS GOT header.
// See comment in the GotSection::writeTo. // See comment in the GotSection::writeTo.
MipsLocalEntries += 2; MipsPageEntries += 2;
for (const OutputSectionBase<ELFT> *OutSec : MipsOutSections) { for (const OutputSectionBase<ELFT> *OutSec : MipsOutSections) {
// Calculate an upper bound of MIPS GOT entries required to store page // Calculate an upper bound of MIPS GOT entries required to store page
// addresses of local symbols. We assume the worst case - each 64kb // addresses of local symbols. We assume the worst case - each 64kb
// page of the output section has at least one GOT relocation against it. // page of the output section has at least one GOT relocation against it.
// Add 0x8000 to the section's size because the page address stored // Add 0x8000 to the section's size because the page address stored
// in the GOT entry is calculated as (value + 0x8000) & ~0xffff. // in the GOT entry is calculated as (value + 0x8000) & ~0xffff.
MipsLocalEntries += (OutSec->getSize() + 0x8000 + 0xfffe) / 0xffff; MipsPageEntries += (OutSec->getSize() + 0x8000 + 0xfffe) / 0xffff;
} }
EntriesNum += MipsLocalEntries; EntriesNum += MipsPageEntries + MipsLocal.size() + MipsGlobal.size();
} }
this->Header.sh_size = EntriesNum * sizeof(uintX_t); this->Header.sh_size = EntriesNum * sizeof(uintX_t);
} }
template <class ELFT> void GotSection<ELFT>::writeTo(uint8_t *Buf) { template <class ELFT> void GotSection<ELFT>::writeMipsGot(uint8_t *&Buf) {
if (Config->EMachine == EM_MIPS) { // Set the MSB of the second GOT slot. This is not required by any
// Set the MSB of the second GOT slot. This is not required by any // MIPS ABI documentation, though.
// MIPS ABI documentation, though. //
// // There is a comment in glibc saying that "The MSB of got[1] of a
// There is a comment in glibc saying that "The MSB of got[1] of a // gnu object is set to identify gnu objects," and in GNU gold it
// gnu object is set to identify gnu objects," and in GNU gold it // says "the second entry will be used by some runtime loaders".
// says "the second entry will be used by some runtime loaders". // But how this field is being used is unclear.
// But how this field is being used is unclear. //
// // We are not really willing to mimic other linkers behaviors
// We are not really willing to mimic other linkers behaviors // without understanding why they do that, but because all files
// without understanding why they do that, but because all files // generated by GNU tools have this special GOT value, and because
// generated by GNU tools have this special GOT value, and because // we've been doing this for years, it is probably a safe bet to
// we've been doing this for years, it is probably a safe bet to // keep doing this for now. We really need to revisit this to see
// keep doing this for now. We really need to revisit this to see // if we had to do this.
// if we had to do this. auto *P = reinterpret_cast<typename ELFT::Off *>(Buf);
auto *P = reinterpret_cast<typename ELFT::Off *>(Buf); P[1] = uintX_t(1) << (ELFT::Is64Bits ? 63 : 31);
P[1] = uintX_t(1) << (ELFT::Is64Bits ? 63 : 31); // Write 'page address' entries to the local part of the GOT.
for (std::pair<uintX_t, size_t> &L : MipsLocalGotPos) { for (std::pair<uintX_t, size_t> &L : MipsLocalGotPos) {
uint8_t *Entry = Buf + L.second * sizeof(uintX_t); uint8_t *Entry = Buf + L.second * sizeof(uintX_t);
write<uintX_t, ELFT::TargetEndianness, sizeof(uintX_t)>(Entry, L.first); write<uintX_t, ELFT::TargetEndianness, sizeof(uintX_t)>(Entry, L.first);
}
Buf += MipsLocalEntries * sizeof(uintX_t);
} }
Buf += MipsPageEntries * sizeof(uintX_t);
auto AddEntry = [&](const MipsGotEntry &SA) {
uint8_t *Entry = Buf;
Buf += sizeof(uintX_t);
uintX_t VA = SA.first->template getVA<ELFT>(SA.second);
write<uintX_t, ELFT::TargetEndianness, sizeof(uintX_t)>(Entry, VA);
};
std::for_each(std::begin(MipsLocal), std::end(MipsLocal), AddEntry);
std::for_each(std::begin(MipsGlobal), std::end(MipsGlobal), AddEntry);
}
template <class ELFT> void GotSection<ELFT>::writeTo(uint8_t *Buf) {
if (Config->EMachine == EM_MIPS)
writeMipsGot(Buf);
for (const SymbolBody *B : Entries) { for (const SymbolBody *B : Entries) {
uint8_t *Entry = Buf; uint8_t *Entry = Buf;
Buf += sizeof(uintX_t); Buf += sizeof(uintX_t);
if (!B) if (!B)
continue; continue;
// MIPS has special rules to fill up GOT entries. if (B->isPreemptible())
// See "Global Offset Table" in Chapter 5 in the following document
// for detailed description:
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
// As the first approach, we can just store addresses for all symbols.
if (Config->EMachine != EM_MIPS && B->isPreemptible())
continue; // The dynamic linker will take care of it. continue; // The dynamic linker will take care of it.
uintX_t VA = B->getVA<ELFT>(); uintX_t VA = B->getVA<ELFT>();
write<uintX_t, ELFT::TargetEndianness, sizeof(uintX_t)>(Entry, VA); write<uintX_t, ELFT::TargetEndianness, sizeof(uintX_t)>(Entry, VA);
@ -1249,10 +1276,8 @@ static bool sortMipsSymbols(const std::pair<SymbolBody *, unsigned> &L,
const std::pair<SymbolBody *, unsigned> &R) { const std::pair<SymbolBody *, unsigned> &R) {
// Sort entries related to non-local preemptible symbols by GOT indexes. // Sort entries related to non-local preemptible symbols by GOT indexes.
// All other entries go to the first part of GOT in arbitrary order. // All other entries go to the first part of GOT in arbitrary order.
bool LIsInLocalGot = !L.first->isInGot() || !L.first->isPreemptible(); if (!L.first->IsInGlobalMipsGot || !R.first->IsInGlobalMipsGot)
bool RIsInLocalGot = !R.first->isInGot() || !R.first->isPreemptible(); return !L.first->IsInGlobalMipsGot;
if (LIsInLocalGot || RIsInLocalGot)
return !RIsInLocalGot;
return L.first->GotIndex < R.first->GotIndex; return L.first->GotIndex < R.first->GotIndex;
} }

View File

@ -11,6 +11,7 @@
#define LLD_ELF_OUTPUT_SECTIONS_H #define LLD_ELF_OUTPUT_SECTIONS_H
#include "Config.h" #include "Config.h"
#include "Relocations.h"
#include "lld/Core/LLVM.h" #include "lld/Core/LLVM.h"
#include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallPtrSet.h"
@ -113,11 +114,12 @@ public:
void finalize() override; void finalize() override;
void writeTo(uint8_t *Buf) override; void writeTo(uint8_t *Buf) override;
void addEntry(SymbolBody &Sym); void addEntry(SymbolBody &Sym);
void addMipsEntry(SymbolBody &Sym, uintX_t Addend, RelExpr Expr);
bool addDynTlsEntry(SymbolBody &Sym); bool addDynTlsEntry(SymbolBody &Sym);
bool addTlsIndex(); bool addTlsIndex();
bool empty() const { return MipsLocalEntries == 0 && Entries.empty(); } bool empty() const { return MipsPageEntries == 0 && Entries.empty(); }
uintX_t getMipsLocalEntryOffset(uintX_t EntryValue);
uintX_t getMipsLocalPageOffset(uintX_t Addr); uintX_t getMipsLocalPageOffset(uintX_t Addr);
uintX_t getMipsGotOffset(const SymbolBody &B, uintX_t Addend) const;
uintX_t getGlobalDynAddr(const SymbolBody &B) const; uintX_t getGlobalDynAddr(const SymbolBody &B) const;
uintX_t getGlobalDynOffset(const SymbolBody &B) const; uintX_t getGlobalDynOffset(const SymbolBody &B) const;
uintX_t getNumEntries() const { return Entries.size(); } uintX_t getNumEntries() const { return Entries.size(); }
@ -142,10 +144,26 @@ public:
private: private:
std::vector<const SymbolBody *> Entries; std::vector<const SymbolBody *> Entries;
uint32_t TlsIndexOff = -1; uint32_t TlsIndexOff = -1;
uint32_t MipsLocalEntries = 0; uint32_t MipsPageEntries = 0;
// Output sections referenced by MIPS GOT relocations. // Output sections referenced by MIPS GOT relocations.
llvm::SmallPtrSet<const OutputSectionBase<ELFT> *, 10> MipsOutSections; llvm::SmallPtrSet<const OutputSectionBase<ELFT> *, 10> MipsOutSections;
llvm::DenseMap<uintX_t, size_t> MipsLocalGotPos; llvm::DenseMap<uintX_t, size_t> MipsLocalGotPos;
// MIPS ABI requires to create unique GOT entry for each Symbol/Addend
// pairs. The `MipsGotMap` maps (S,A) pair to the GOT index in the `MipsLocal`
// or `MipsGlobal` vectors. In general it does not have a sence to take in
// account addend for preemptible symbols because the corresponding
// GOT entries should have one-to-one mapping with dynamic symbols table.
// But we use the same container's types for both kind of GOT entries
// to handle them uniformly.
typedef std::pair<const SymbolBody*, uintX_t> MipsGotEntry;
typedef std::vector<MipsGotEntry> MipsGotEntries;
llvm::DenseMap<MipsGotEntry, size_t> MipsGotMap;
MipsGotEntries MipsLocal;
MipsGotEntries MipsGlobal;
// Write MIPS-specific parts of the GOT.
void writeMipsGot(uint8_t *&Buf);
}; };
template <class ELFT> template <class ELFT>

View File

@ -59,10 +59,10 @@ namespace lld {
namespace elf { namespace elf {
static bool refersToGotEntry(RelExpr Expr) { static bool refersToGotEntry(RelExpr Expr) {
return Expr == R_GOT || Expr == R_GOT_OFF || Expr == R_MIPS_GOT_LOCAL || return Expr == R_GOT || Expr == R_GOT_OFF || Expr == R_MIPS_GOT_LOCAL_PAGE ||
Expr == R_MIPS_GOT_LOCAL_PAGE || Expr == R_GOT_PAGE_PC || Expr == R_MIPS_GOT_OFF || Expr == R_GOT_PAGE_PC || Expr == R_GOT_PC ||
Expr == R_GOT_PC || Expr == R_GOT_FROM_END || Expr == R_TLSGD || Expr == R_GOT_FROM_END || Expr == R_TLSGD || Expr == R_TLSGD_PC ||
Expr == R_TLSGD_PC || Expr == R_TLSDESC || Expr == R_TLSDESC_PAGE; Expr == R_TLSDESC || Expr == R_TLSDESC_PAGE;
} }
static bool isPreemptible(const SymbolBody &Body, uint32_t Type) { static bool isPreemptible(const SymbolBody &Body, uint32_t Type) {
@ -271,9 +271,9 @@ static bool isStaticLinkTimeConstant(RelExpr E, uint32_t Type,
const SymbolBody &Body) { const SymbolBody &Body) {
// These expressions always compute a constant // These expressions always compute a constant
if (E == R_SIZE || E == R_GOT_FROM_END || E == R_GOT_OFF || if (E == R_SIZE || E == R_GOT_FROM_END || E == R_GOT_OFF ||
E == R_MIPS_GOT_LOCAL || E == R_MIPS_GOT_LOCAL_PAGE || E == R_MIPS_GOT_LOCAL_PAGE || E == R_MIPS_GOT_OFF || E == R_GOT_PAGE_PC ||
E == R_GOT_PAGE_PC || E == R_GOT_PC || E == R_PLT_PC || E == R_TLSGD_PC || E == R_GOT_PC || E == R_PLT_PC || E == R_TLSGD_PC || E == R_TLSGD ||
E == R_TLSGD || E == R_PPC_PLT_OPD || E == R_TLSDESC_PAGE || E == R_HINT) E == R_PPC_PLT_OPD || E == R_TLSDESC_PAGE || E == R_HINT)
return true; return true;
// These never do, except if the entire file is position dependent or if // These never do, except if the entire file is position dependent or if
@ -466,8 +466,6 @@ static typename ELFT::uint computeAddend(const elf::ObjectFile<ELFT> &File,
// For details see p. 4-19 at // For details see p. 4-19 at
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
Addend += 4; Addend += 4;
if (Expr == R_GOT_OFF)
Addend -= MipsGPOffset;
if (Expr == R_GOTREL) { if (Expr == R_GOTREL) {
Addend -= MipsGPOffset; Addend -= MipsGPOffset;
if (Body.isLocal()) if (Body.isLocal())
@ -574,8 +572,8 @@ static void scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) {
// to the GOT entry and reads the GOT entry when it needs to perform // to the GOT entry and reads the GOT entry when it needs to perform
// a dynamic relocation. // a dynamic relocation.
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf p.4-19 // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf p.4-19
if (Config->EMachine == EM_MIPS && !Body.isInGot()) if (Config->EMachine == EM_MIPS)
Out<ELFT>::Got->addEntry(Body); Out<ELFT>::Got->addMipsEntry(Body, Addend, Expr);
continue; continue;
} }
@ -605,18 +603,20 @@ static void scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) {
} }
if (refersToGotEntry(Expr)) { if (refersToGotEntry(Expr)) {
if (Body.isInGot()) if (Config->EMachine == EM_MIPS) {
continue;
Out<ELFT>::Got->addEntry(Body);
if (Config->EMachine == EM_MIPS)
// MIPS ABI has special rules to process GOT entries // MIPS ABI has special rules to process GOT entries
// and doesn't require relocation entries for them. // and doesn't require relocation entries for them.
// See "Global Offset Table" in Chapter 5 in the following document // See "Global Offset Table" in Chapter 5 in the following document
// for detailed description: // for detailed description:
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
Out<ELFT>::Got->addMipsEntry(Body, Addend, Expr);
continue;
}
if (Body.isInGot())
continue; continue;
Out<ELFT>::Got->addEntry(Body);
if (Preemptible || (Config->Pic && !isAbsolute<ELFT>(Body))) { if (Preemptible || (Config->Pic && !isAbsolute<ELFT>(Body))) {
uint32_t DynType; uint32_t DynType;
if (Body.isTls()) if (Body.isTls())

View File

@ -28,8 +28,8 @@ enum RelExpr {
R_GOT_PAGE_PC, R_GOT_PAGE_PC,
R_GOT_PC, R_GOT_PC,
R_HINT, R_HINT,
R_MIPS_GOT_LOCAL,
R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOT_LOCAL_PAGE,
R_MIPS_GOT_OFF,
R_NEG_TLS, R_NEG_TLS,
R_PAGE_PC, R_PAGE_PC,
R_PC, R_PC,

View File

@ -92,12 +92,14 @@ static typename ELFT::uint getSymVA(const SymbolBody &Body,
SymbolBody::SymbolBody(Kind K, uint32_t NameOffset, uint8_t StOther, SymbolBody::SymbolBody(Kind K, uint32_t NameOffset, uint8_t StOther,
uint8_t Type) uint8_t Type)
: SymbolKind(K), NeedsCopyOrPltAddr(false), IsLocal(true), Type(Type), : SymbolKind(K), NeedsCopyOrPltAddr(false), IsLocal(true),
StOther(StOther), NameOffset(NameOffset) {} IsInGlobalMipsGot(false), Type(Type), StOther(StOther),
NameOffset(NameOffset) {}
SymbolBody::SymbolBody(Kind K, StringRef Name, uint8_t StOther, uint8_t Type) SymbolBody::SymbolBody(Kind K, StringRef Name, uint8_t StOther, uint8_t Type)
: SymbolKind(K), NeedsCopyOrPltAddr(false), IsLocal(false), Type(Type), : SymbolKind(K), NeedsCopyOrPltAddr(false), IsLocal(false),
StOther(StOther), Name({Name.data(), Name.size()}) {} IsInGlobalMipsGot(false), Type(Type), StOther(StOther),
Name({Name.data(), Name.size()}) {}
// Returns true if a symbol can be replaced at load-time by a symbol // Returns true if a symbol can be replaced at load-time by a symbol
// with the same name defined in other ELF executable or DSO. // with the same name defined in other ELF executable or DSO.
@ -149,8 +151,7 @@ template <class ELFT> typename ELFT::uint SymbolBody::getGotVA() const {
} }
template <class ELFT> typename ELFT::uint SymbolBody::getGotOffset() const { template <class ELFT> typename ELFT::uint SymbolBody::getGotOffset() const {
return (Out<ELFT>::Got->getMipsLocalEntriesNum() + GotIndex) * return GotIndex * sizeof(typename ELFT::uint);
sizeof(typename ELFT::uint);
} }
template <class ELFT> typename ELFT::uint SymbolBody::getGotPltVA() const { template <class ELFT> typename ELFT::uint SymbolBody::getGotPltVA() const {

View File

@ -127,6 +127,9 @@ public:
// True if this is a local symbol. // True if this is a local symbol.
unsigned IsLocal : 1; unsigned IsLocal : 1;
// True if this symbol has an entry in the global part of MIPS GOT.
unsigned IsInGlobalMipsGot : 1;
// The following fields have the same meaning as the ELF symbol attributes. // The following fields have the same meaning as the ELF symbol attributes.
uint8_t Type; // symbol type uint8_t Type; // symbol type
uint8_t StOther; // st_other field value uint8_t StOther; // st_other field value

View File

@ -1745,9 +1745,7 @@ RelExpr MipsTargetInfo<ELFT>::getRelExpr(uint32_t Type,
// fallthrough // fallthrough
case R_MIPS_CALL16: case R_MIPS_CALL16:
case R_MIPS_GOT_DISP: case R_MIPS_GOT_DISP:
if (!S.isPreemptible()) return R_MIPS_GOT_OFF;
return R_MIPS_GOT_LOCAL;
return R_GOT_OFF;
case R_MIPS_GOT_PAGE: case R_MIPS_GOT_PAGE:
return R_MIPS_GOT_LOCAL_PAGE; return R_MIPS_GOT_LOCAL_PAGE;
} }

View File

@ -0,0 +1,89 @@
# Check R_MIPS_GOT_DISP relocations against various kind of symbols.
# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux \
# RUN: %p/Inputs/mips-pic.s -o %t.so.o
# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux %s -o %t.exe.o
# RUN: ld.lld %t.so.o -shared -o %t.so
# RUN: ld.lld %t.exe.o %t.so -o %t.exe
# RUN: llvm-objdump -d -t %t.exe | FileCheck %s
# RUN: llvm-readobj -r -mips-plt-got %t.exe | FileCheck -check-prefix=GOT %s
# REQUIRES: mips
# CHECK: __start:
# CHECK-NEXT: 20000: 24 42 80 40 addiu $2, $2, -32704
# CHECK-NEXT: 20004: 24 42 80 20 addiu $2, $2, -32736
# CHECK-NEXT: 20008: 24 42 80 28 addiu $2, $2, -32728
# CHECK-NEXT: 2000c: 24 42 80 30 addiu $2, $2, -32720
# CHECK-NEXT: 20010: 24 42 80 38 addiu $2, $2, -32712
# CHECK: 0000000000020014 .text 00000000 foo
# CHECK: 0000000000037ff0 .got 00000000 .hidden _gp
# CHECK: 0000000000020000 .text 00000000 __start
# CHECK: 0000000000000000 g F *UND* 00000000 foo1a
# GOT: Relocations [
# GOT-NEXT: ]
# GOT-NEXT: Primary GOT {
# GOT-NEXT: Canonical gp value: 0x37FF0
# GOT-NEXT: Reserved entries [
# GOT-NEXT: Entry {
# GOT-NEXT: Address: 0x30000
# GOT-NEXT: Access: -32752
# GOT-NEXT: Initial: 0x0
# GOT-NEXT: Purpose: Lazy resolver
# GOT-NEXT: }
# GOT-NEXT: Entry {
# GOT-NEXT: Address: 0x30008
# GOT-NEXT: Access: -32744
# GOT-NEXT: Initial: 0x8000000000000000
# GOT-NEXT: Purpose: Module pointer (GNU extension)
# GOT-NEXT: }
# GOT-NEXT: ]
# GOT-NEXT: Local entries [
# GOT-NEXT: Entry {
# GOT-NEXT: Address: 0x30010
# GOT-NEXT: Access: -32736
# GOT-NEXT: Initial: 0x20014
# GOT-NEXT: }
# GOT-NEXT: Entry {
# GOT-NEXT: Address: 0x30018
# GOT-NEXT: Access: -32728
# GOT-NEXT: Initial: 0x20004
# GOT-NEXT: }
# GOT-NEXT: Entry {
# GOT-NEXT: Address: 0x30020
# GOT-NEXT: Access: -32720
# GOT-NEXT: Initial: 0x20008
# GOT-NEXT: }
# GOT-NEXT: Entry {
# GOT-NEXT: Address: 0x30028
# GOT-NEXT: Access: -32712
# GOT-NEXT: Initial: 0x2000C
# GOT-NEXT: }
# GOT-NEXT: ]
# GOT-NEXT: Global entries [
# GOT-NEXT: Entry {
# GOT-NEXT: Address: 0x30030
# GOT-NEXT: Access: -32704
# GOT-NEXT: Initial: 0x0
# GOT-NEXT: Value: 0x0
# GOT-NEXT: Type: Function
# GOT-NEXT: Section: Undefined
# GOT-NEXT: Name: foo1a
# GOT-NEXT: }
# GOT-NEXT: ]
# GOT-NEXT: Number of TLS and multi-GOT entries: 0
# GOT-NEXT: }
.text
.global __start
__start:
addiu $v0,$v0,%got_disp(foo1a) # R_MIPS_GOT_DISP
addiu $v0,$v0,%got_disp(foo) # R_MIPS_GOT_DISP
addiu $v0,$v0,%got_disp(.text+4) # R_MIPS_GOT_DISP
addiu $v0,$v0,%got_disp(.text+8) # R_MIPS_GOT_DISP
addiu $v0,$v0,%got_disp(.text+12) # R_MIPS_GOT_DISP
foo:
nop

View File

@ -13,14 +13,15 @@
# CHECK: __start: # CHECK: __start:
# CHECK-NEXT: 20000: df 82 80 20 ld $2, -32736($gp) # CHECK-NEXT: 20000: df 82 80 20 ld $2, -32736($gp)
# CHECK-NEXT: 20004: 64 42 00 14 daddiu $2, $2, 20 # CHECK-NEXT: 20004: 64 42 00 18 daddiu $2, $2, 24
# CHECK-NEXT: 20008: 24 42 80 30 addiu $2, $2, -32720 # CHECK-NEXT: 20008: 24 42 80 38 addiu $2, $2, -32712
# CHECK-NEXT: 2000c: 24 42 80 28 addiu $2, $2, -32728 # CHECK-NEXT: 2000c: 24 42 80 28 addiu $2, $2, -32728
# CHECK-NEXT: 20010: 24 42 80 30 addiu $2, $2, -32720
# CHECK: 0000000000020014 .text 00000000 foo # CHECK: 0000000000020018 .text 00000000 foo
# CHECK: 0000000000037ff0 .got 00000000 .hidden _gp # CHECK: 0000000000037ff0 .got 00000000 .hidden _gp
# CHECK: 0000000000020000 .text 00000000 __start # CHECK: 0000000000020000 .text 00000000 __start
# CHECK: 0000000000020010 .text 00000000 bar # CHECK: 0000000000020014 .text 00000000 bar
# GOT: Relocations [ # GOT: Relocations [
# GOT-NEXT: ] # GOT-NEXT: ]
@ -49,13 +50,18 @@
# GOT-NEXT: Entry { # GOT-NEXT: Entry {
# GOT-NEXT: Address: 0x30018 # GOT-NEXT: Address: 0x30018
# GOT-NEXT: Access: -32728 # GOT-NEXT: Access: -32728
# GOT-NEXT: Initial: 0x20010 # GOT-NEXT: Initial: 0x20014
# GOT-NEXT: }
# GOT-NEXT: Entry {
# GOT-NEXT: Address: 0x30020
# GOT-NEXT: Access: -32720
# GOT-NEXT: Initial: 0x20018
# GOT-NEXT: } # GOT-NEXT: }
# GOT-NEXT: ] # GOT-NEXT: ]
# GOT-NEXT: Global entries [ # GOT-NEXT: Global entries [
# GOT-NEXT: Entry { # GOT-NEXT: Entry {
# GOT-NEXT: Address: 0x30020 # GOT-NEXT: Address: 0x30028
# GOT-NEXT: Access: -32720 # GOT-NEXT: Access: -32712
# GOT-NEXT: Initial: 0x0 # GOT-NEXT: Initial: 0x0
# GOT-NEXT: Value: 0x0 # GOT-NEXT: Value: 0x0
# GOT-NEXT: Type: Function # GOT-NEXT: Type: Function
@ -73,6 +79,7 @@ __start:
daddiu $v0,$v0,%got_ofst(foo) # R_MIPS_GOT_OFST daddiu $v0,$v0,%got_ofst(foo) # R_MIPS_GOT_OFST
addiu $v0,$v0,%got_disp(foo1a) # R_MIPS_GOT_DISP addiu $v0,$v0,%got_disp(foo1a) # R_MIPS_GOT_DISP
addiu $v0,$v0,%got_disp(bar) # R_MIPS_GOT_DISP addiu $v0,$v0,%got_disp(bar) # R_MIPS_GOT_DISP
addiu $v0,$v0,%got_disp(foo) # R_MIPS_GOT_DISP
bar: bar:
nop nop

View File

@ -71,8 +71,8 @@
# DSO: ] # DSO: ]
# DSO: DynamicSymbols [ # DSO: DynamicSymbols [
# DSO: Name: @ # DSO: Name: @
# DSO: Name: __start@
# DSO: Name: _foo@ # DSO: Name: _foo@
# DSO: Name: __start@
# DSO: ] # DSO: ]
# DSO: DynamicSection [ # DSO: DynamicSection [
# DSO-NEXT: Tag Type Name/Value # DSO-NEXT: Tag Type Name/Value

View File

@ -17,7 +17,7 @@
# CHECK-NEXT: 10014: 21 08 90 04 addi $8, $8, -28668 # CHECK-NEXT: 10014: 21 08 90 04 addi $8, $8, -28668
# CHECK-NEXT: 10018: 8f 88 80 20 lw $8, -32736($gp) # CHECK-NEXT: 10018: 8f 88 80 20 lw $8, -32736($gp)
# CHECK-NEXT: 1001c: 21 08 10 04 addi $8, $8, 4100 # CHECK-NEXT: 1001c: 21 08 10 04 addi $8, $8, 4100
# CHECK-NEXT: 10020: 8f 88 80 24 lw $8, -32732($gp) # CHECK-NEXT: 10020: 8f 88 80 28 lw $8, -32728($gp)
# CHECK-NEXT: 10024: 21 08 10 08 addi $8, $8, 4104 # CHECK-NEXT: 10024: 21 08 10 08 addi $8, $8, 4104
# CHECK-NEXT: 10028: 8f 88 80 2c lw $8, -32724($gp) # CHECK-NEXT: 10028: 8f 88 80 2c lw $8, -32724($gp)
# #
@ -67,14 +67,14 @@
# GOT-NEXT: Entry { # GOT-NEXT: Entry {
# GOT-NEXT: Address: 0x20014 # GOT-NEXT: Address: 0x20014
# GOT-NEXT: Access: -32732 # GOT-NEXT: Access: -32732
# GOT-NEXT: Initial: 0x51008 # GOT-NEXT: Initial: 0x0
# ^-- 'bar' address # ^-- redundant unused entry
# GOT-NEXT: } # GOT-NEXT: }
# GOT-NEXT: Entry { # GOT-NEXT: Entry {
# GOT-NEXT: Address: 0x20018 # GOT-NEXT: Address: 0x20018
# GOT-NEXT: Access: -32728 # GOT-NEXT: Access: -327
# GOT-NEXT: Initial: 0x0 # GOT-NEXT: Initial: 0x51008
# ^-- redundant unused entry # ^-- 'bar' address
# GOT-NEXT: } # GOT-NEXT: }
# GOT-NEXT: ] # GOT-NEXT: ]
# GOT-NEXT: Global entries [ # GOT-NEXT: Global entries [

View File

@ -12,8 +12,8 @@
# CHECK: Relocations [ # CHECK: Relocations [
# CHECK-NEXT: Section ({{.*}}) .rel.dyn { # CHECK-NEXT: Section ({{.*}}) .rel.dyn {
# CHECK-NEXT: 0x{{[0-9A-F]+}} R_MIPS_COPY data0 0x0
# CHECK-NEXT: 0x{{[0-9A-F]+}} R_MIPS_COPY data1 0x0 # CHECK-NEXT: 0x{{[0-9A-F]+}} R_MIPS_COPY data1 0x0
# CHECK-NEXT: 0x{{[0-9A-F]+}} R_MIPS_COPY data0 0x0
# CHECK-NEXT: } # CHECK-NEXT: }
# CHECK-NEXT: Section ({{.*}}) .rel.plt { # CHECK-NEXT: Section ({{.*}}) .rel.plt {
# CHECK-NEXT: 0x{{[0-9A-F]+}} R_MIPS_JUMP_SLOT foo0 0x0 # CHECK-NEXT: 0x{{[0-9A-F]+}} R_MIPS_JUMP_SLOT foo0 0x0

View File

@ -9,15 +9,6 @@
# REQUIRES: mips # REQUIRES: mips
# CHECK: Symbol {
# CHECK: Name: foo0@
# CHECK-NEXT: Value: 0x0
# CHECK-NEXT: Size: 0
# CHECK-NEXT: Binding: Global
# CHECK-NEXT: Type: Function
# CHECK-NEXT: Other: 0
# CHECK-NEXT: Section: Undefined
# CHECK-NEXT: }
# CHECK: Symbol { # CHECK: Symbol {
# CHECK: Name: foo1@ # CHECK: Name: foo1@
# CHECK-NEXT: Value: 0x20050 # CHECK-NEXT: Value: 0x20050
@ -29,6 +20,15 @@
# CHECK-NEXT: ] # CHECK-NEXT: ]
# CHECK-NEXT: Section: Undefined # CHECK-NEXT: Section: Undefined
# CHECK-NEXT: } # CHECK-NEXT: }
# CHECK: Symbol {
# CHECK: Name: foo0@
# CHECK-NEXT: Value: 0x0
# CHECK-NEXT: Size: 0
# CHECK-NEXT: Binding: Global
# CHECK-NEXT: Type: Function
# CHECK-NEXT: Other: 0
# CHECK-NEXT: Section: Undefined
# CHECK-NEXT: }
# CHECK: Primary GOT { # CHECK: Primary GOT {
# CHECK: Local entries [ # CHECK: Local entries [