/// \brief .tdata section address plus fixed offset.
uint64_t getTPOffset() const { return *_tpOff; }
+ uint64_t getDTPOffset() const { return *_dtpOff; }
private:
typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym;
typedef llvm::object::Elf_Rel_Impl<ELFT, false> Elf_Rel;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Rel_Iter Elf_Rel_Iter;
- enum { TP_OFFSET = 0x7000 };
+ enum { TP_OFFSET = 0x7000, DTP_OFFSET = 0x8000 };
llvm::Optional<int64_t> _gp0;
llvm::Optional<uint64_t> _tpOff;
+ llvm::Optional<uint64_t> _dtpOff;
ErrorOr<ELFDefinedAtom<ELFT> *> handleDefinedSymbol(
StringRef symName, StringRef sectionName, const Elf_Sym *sym,
assert(raw.size() == sizeof(Elf_RegInfo) &&
"Invalid size of RegInfo section");
_gp0 = reinterpret_cast<const Elf_RegInfo *>(raw.data())->ri_gp_value;
- } else if (!_tpOff.hasValue() && section.sh_flags & llvm::ELF::SHF_TLS)
+ } else if (!_tpOff.hasValue() && section.sh_flags & llvm::ELF::SHF_TLS) {
_tpOff = section.sh_addr + TP_OFFSET;
+ _dtpOff = section.sh_addr + DTP_OFFSET;
+ }
}
return error_code();
}
case llvm::ELF::R_MIPS_HI16:
case llvm::ELF::R_MIPS_LO16:
case llvm::ELF::R_MIPS_GOT16:
+ case llvm::ELF::R_MIPS_TLS_GD:
+ case llvm::ELF::R_MIPS_TLS_LDM:
+ case llvm::ELF::R_MIPS_TLS_GOTTPREL:
+ case llvm::ELF::R_MIPS_TLS_DTPREL_HI16:
+ case llvm::ELF::R_MIPS_TLS_DTPREL_LO16:
case llvm::ELF::R_MIPS_TLS_TPREL_HI16:
case llvm::ELF::R_MIPS_TLS_TPREL_LO16:
return *(int16_t *)ap;
void MipsExecutableWriter<ELFT>::buildDynamicSymbolTable(const File &file) {
// MIPS ABI requires to add to dynsym even undefined symbols
// if they have a corresponding entries in a global part of GOT.
+ for (auto sec : this->_layout.sections())
+ if (auto section = dyn_cast<AtomSection<ELFT>>(sec))
+ for (const auto &atom : section->atoms()) {
+ if (_writeHelper.hasGlobalGOTEntry(atom->_atom))
+ this->_dynamicSymbolTable->addSymbol(atom->_atom, section->ordinal(),
+ atom->_virtualAddr, atom);
+ }
+
for (const UndefinedAtom *a : file.undefined())
// FIXME (simon): Consider to move this check to the
// MipsELFUndefinedAtom class method. That allows to
switch (r.kindValue()) {
case llvm::ELF::R_MIPS_COPY:
case llvm::ELF::R_MIPS_REL32:
+ case llvm::ELF::R_MIPS_TLS_DTPMOD32:
+ case llvm::ELF::R_MIPS_TLS_DTPREL32:
+ case llvm::ELF::R_MIPS_TLS_TPREL32:
return true;
default:
return false;
applyReloc(location, G, 0xffff);
}
-/// \brief R_MIPS_TLS_TPREL_HI16, LLD_R_MIPS_HI16
+/// \brief R_MIPS_TLS_DTPREL_HI16, R_MIPS_TLS_TPREL_HI16, LLD_R_MIPS_HI16
/// (S + A) >> 16
static void relocGeneralHi16(uint8_t *location, uint64_t S, int64_t A) {
int32_t result = S + A + 0x8000;
applyReloc(location, result >> 16, 0xffff);
}
-/// \brief R_MIPS_TLS_TPREL_LO16, LLD_R_MIPS_LO16
+/// \brief R_MIPS_TLS_DTPREL_LO16, R_MIPS_TLS_TPREL_LO16, LLD_R_MIPS_LO16
/// S + A
static void relocGeneralLo16(uint8_t *location, uint64_t S, int64_t A) {
int32_t result = S + A;
case R_MIPS_CALL16:
relocGOT(location, relocVAddress, targetVAddress, ref.addend(), gpAddr);
break;
+ case R_MIPS_TLS_GD:
+ relocGOT(location, relocVAddress, targetVAddress, ref.addend(), gpAddr);
+ break;
+ case R_MIPS_TLS_LDM:
+ case R_MIPS_TLS_GOTTPREL:
+ relocGOT(location, relocVAddress, targetVAddress, ref.addend(), gpAddr);
+ break;
+ case R_MIPS_TLS_DTPREL_HI16:
case R_MIPS_TLS_TPREL_HI16:
relocGeneralHi16(location, targetVAddress, ref.addend());
break;
+ case R_MIPS_TLS_DTPREL_LO16:
case R_MIPS_TLS_TPREL_LO16:
relocGeneralLo16(location, targetVAddress, ref.addend());
break;
case R_MIPS_REL32:
case R_MIPS_JUMP_SLOT:
case R_MIPS_COPY:
+ case R_MIPS_TLS_DTPMOD32:
+ case R_MIPS_TLS_DTPREL32:
+ case R_MIPS_TLS_TPREL32:
// Ignore runtime relocations.
break;
case R_MIPS_PC32:
0x00, 0x00, 0x00, 0x80
};
+// TLS GD Entry
+static const uint8_t mipsGotTlsGdAtomContent[] = {
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
+
// PLT0 entry
static const uint8_t mipsPlt0AtomContent[] = {
0x00, 0x00, 0x1c, 0x3c, // lui $28, %hi(&GOTPLT[0])
}
};
+/// \brief MIPS GOT TLS GD entry.
+class GOTTLSGdAtom : public MipsGOTAtom {
+public:
+ GOTTLSGdAtom(const File &f) : MipsGOTAtom(f) {}
+
+ ArrayRef<uint8_t> rawContent() const override {
+ return llvm::makeArrayRef(mipsGotTlsGdAtomContent);
+ }
+};
+
class PLT0Atom : public PLTAtom {
public:
PLT0Atom(const File &f) : PLTAtom(f, ".plt") {}
/// \brief Map Atoms to global GOT entries.
llvm::DenseMap<const Atom *, GOTAtom *> _gotGlobalMap;
+ /// \brief Map Atoms to TLS GOT entries.
+ llvm::DenseMap<const Atom *, GOTAtom *> _gotTLSMap;
+
+ /// \brief Map Atoms to TLS GD GOT entries.
+ llvm::DenseMap<const Atom *, GOTAtom *> _gotTLSGdMap;
+
+ /// \brief GOT entry for the R_MIPS_TLS_LDM relocation.
+ GOTTLSGdAtom *_gotLDMEntry;
+
/// \brief the list of local GOT atoms.
std::vector<GOTAtom *> _localGotVector;
/// \brief the list of global GOT atoms.
std::vector<GOTAtom *> _globalGotVector;
+ /// \brief the list of TLS GOT atoms.
+ std::vector<GOTAtom *> _tlsGotVector;
+
/// \brief Map Atoms to their PLT entries.
llvm::DenseMap<const Atom *, PLTAtom *> _pltMap;
void handle26(Reference &ref);
void handleGOT(Reference &ref);
void handleGPRel(const MipsELFDefinedAtom<ELFT> &atom, Reference &ref);
- void handleTLS(const MipsELFDefinedAtom<ELFT> &atom, Reference &ref);
const GOTAtom *getLocalGOTEntry(const Reference &ref);
const GOTAtom *getGlobalGOTEntry(const Atom *a);
+ const GOTAtom *getTLSGOTEntry(const Atom *a);
+ const GOTAtom *getTLSGdGOTEntry(const Atom *a);
+ const GOTAtom *getTLSLdmGOTEntry(const Atom *a);
PLTAtom *getPLTEntry(const Atom *a);
const LA25Atom *getLA25Entry(const Atom *a);
const ObjectAtom *getObjectEntry(const SharedLibraryAtom *a);
template <typename ELFT>
RelocationPass<ELFT>::RelocationPass(MipsLinkingContext &context)
- : _context(context), _file(context) {
+ : _context(context), _file(context), _gotLDMEntry(nullptr) {
_localGotVector.push_back(new (_file._alloc) GOT0Atom(_file));
_localGotVector.push_back(new (_file._alloc) GOTModulePointerAtom(_file));
}
mf->addAtom(*got);
}
+ for (auto &got : _tlsGotVector) {
+ got->setOrdinal(ordinal++);
+ mf->addAtom(*got);
+ }
+
for (auto &plt : _pltVector) {
DEBUG_WITH_TYPE("MipsGOT", llvm::dbgs() << "[ PLT ] Adding " << plt->name()
<< "\n");
case R_MIPS_GPREL32:
handleGPRel(atom, ref);
break;
+ case R_MIPS_TLS_DTPREL_HI16:
+ case R_MIPS_TLS_DTPREL_LO16:
+ ref.setAddend(ref.addend() - atom.file().getDTPOffset());
+ break;
case R_MIPS_TLS_TPREL_HI16:
case R_MIPS_TLS_TPREL_LO16:
- handleTLS(atom, ref);
+ ref.setAddend(ref.addend() - atom.file().getTPOffset());
+ break;
+ case R_MIPS_TLS_GD:
+ ref.setTarget(getTLSGdGOTEntry(ref.target()));
+ break;
+ case R_MIPS_TLS_LDM:
+ ref.setTarget(getTLSLdmGOTEntry(ref.target()));
+ break;
+ case R_MIPS_TLS_GOTTPREL:
+ ref.setTarget(getTLSGOTEntry(ref.target()));
break;
}
}
}
template <typename ELFT>
-void RelocationPass<ELFT>::handleTLS(const MipsELFDefinedAtom<ELFT> &atom,
- Reference &ref) {
- assert((ref.kindValue() == R_MIPS_TLS_TPREL_HI16 ||
- ref.kindValue() == R_MIPS_TLS_TPREL_LO16) &&
- "Unexpected kind of relocation");
- ref.setAddend(ref.addend() - atom.file().getTPOffset());
-}
-
-template <typename ELFT>
bool RelocationPass<ELFT>::isLocalCall(const Atom *a) const {
Atom::Scope scope;
if (auto *da = dyn_cast<DefinedAtom>(a))
return ga;
}
+template <typename ELFT>
+const GOTAtom *RelocationPass<ELFT>::getTLSGOTEntry(const Atom *a) {
+ auto got = _gotTLSMap.find(a);
+ if (got != _gotTLSMap.end())
+ return got->second;
+
+ auto ga = new (_file._alloc) GOT0Atom(_file);
+ _gotTLSMap[a] = ga;
+
+ _tlsGotVector.push_back(ga);
+ ga->addReferenceELF_Mips(R_MIPS_TLS_TPREL32, 0, a, 0);
+
+ return ga;
+}
+
+template <typename ELFT>
+const GOTAtom *RelocationPass<ELFT>::getTLSGdGOTEntry(const Atom *a) {
+ auto got = _gotTLSGdMap.find(a);
+ if (got != _gotTLSGdMap.end())
+ return got->second;
+
+ auto ga = new (_file._alloc) GOTTLSGdAtom(_file);
+ _gotTLSGdMap[a] = ga;
+
+ _tlsGotVector.push_back(ga);
+ ga->addReferenceELF_Mips(R_MIPS_TLS_DTPMOD32, 0, a, 0);
+ ga->addReferenceELF_Mips(R_MIPS_TLS_DTPREL32, 4, a, 0);
+
+ return ga;
+}
+
+template <typename ELFT>
+const GOTAtom *RelocationPass<ELFT>::getTLSLdmGOTEntry(const Atom *a) {
+ if (_gotLDMEntry)
+ return _gotLDMEntry;
+
+ _gotLDMEntry = new (_file._alloc) GOTTLSGdAtom(_file);
+ _tlsGotVector.push_back(_gotLDMEntry);
+ _gotLDMEntry->addReferenceELF_Mips(R_MIPS_TLS_DTPMOD32, 0, _gotLDMEntry, 0);
+
+ return _gotLDMEntry;
+}
+
template <typename ELFT> void RelocationPass<ELFT>::createPLTHeader() {
assert(_pltVector.empty() && _gotpltVector.empty());
MipsGOTSection(const MipsLinkingContext &context)
: AtomSection<ELFType>(context, ".got", DefinedAtom::typeGOT,
DefinedAtom::permRW_,
- MipsTargetLayout<ELFType>::ORDER_GOT) {
+ MipsTargetLayout<ELFType>::ORDER_GOT),
+ _hasNonLocal(false), _localCount(0) {
this->_flags |= SHF_MIPS_GPREL;
this->_align2 = 4;
}
/// \brief Number of local GOT entries.
- std::size_t getLocalCount() const {
- return this->_atoms.size() - getGlobalCount();
- }
+ std::size_t getLocalCount() const { return _localCount; }
/// \brief Number of global GOT entries.
std::size_t getGlobalCount() const { return _posMap.size(); }
/// \brief Does the atom have a global GOT entry?
- bool hasGlobalGOTEntry(const Atom *a) const { return _posMap.count(a); }
+ bool hasGlobalGOTEntry(const Atom *a) const {
+ return _posMap.count(a) || _tlsMap.count(a);
+ }
/// \brief Compare two atoms accordingly theirs positions in the GOT.
bool compare(const Atom *a, const Atom *b) const {
const lld::AtomLayout &appendAtom(const Atom *atom) override {
const DefinedAtom *da = dyn_cast<DefinedAtom>(atom);
- const Atom *ta = nullptr;
for (const auto &r : *da) {
if (r->kindNamespace() != lld::Reference::KindNamespace::ELF)
continue;
assert(r->kindArch() == Reference::KindArch::Mips);
- if (r->kindValue() == LLD_R_MIPS_GLOBAL_GOT) {
- ta = r->target();
- break;
+ switch (r->kindValue()) {
+ case LLD_R_MIPS_GLOBAL_GOT:
+ _hasNonLocal = true;
+ _posMap[r->target()] = _posMap.size();
+ return AtomSection<ELFType>::appendAtom(atom);
+ case R_MIPS_TLS_TPREL32:
+ case R_MIPS_TLS_DTPREL32:
+ _hasNonLocal = true;
+ _tlsMap[r->target()] = _tlsMap.size();
+ return AtomSection<ELFType>::appendAtom(atom);
+ case R_MIPS_TLS_DTPMOD32:
+ _hasNonLocal = true;
+ return AtomSection<ELFType>::appendAtom(atom);
}
}
- if (ta)
- _posMap[ta] = _posMap.size();
+ if (!_hasNonLocal)
+ ++_localCount;
return AtomSection<ELFType>::appendAtom(atom);
}
private:
+ /// \brief True if the GOT contains non-local entries.
+ bool _hasNonLocal;
+
+ /// \brief Number of local GOT entries.
+ std::size_t _localCount;
+
+ /// \brief Map TLS Atoms to their GOT entry index.
+ llvm::DenseMap<const Atom *, std::size_t> _tlsMap;
+
/// \brief Map Atoms to their GOT entry index.
llvm::DenseMap<const Atom *, std::size_t> _posMap;
};
LLD_KIND_STRING_ENTRY(R_MIPS_CALL16),
LLD_KIND_STRING_ENTRY(R_MIPS_GPREL32),
LLD_KIND_STRING_ENTRY(R_MIPS_JALR),
+ LLD_KIND_STRING_ENTRY(R_MIPS_TLS_DTPMOD32),
+ LLD_KIND_STRING_ENTRY(R_MIPS_TLS_DTPREL32),
+ LLD_KIND_STRING_ENTRY(R_MIPS_TLS_GD),
+ LLD_KIND_STRING_ENTRY(R_MIPS_TLS_LDM),
+ LLD_KIND_STRING_ENTRY(R_MIPS_TLS_GOTTPREL),
+ LLD_KIND_STRING_ENTRY(R_MIPS_TLS_TPREL32),
+ LLD_KIND_STRING_ENTRY(R_MIPS_TLS_DTPREL_HI16),
+ LLD_KIND_STRING_ENTRY(R_MIPS_TLS_DTPREL_LO16),
LLD_KIND_STRING_ENTRY(R_MIPS_TLS_TPREL_HI16),
LLD_KIND_STRING_ENTRY(R_MIPS_TLS_TPREL_LO16),
LLD_KIND_STRING_ENTRY(R_MIPS_COPY),
--- /dev/null
+# Check handling of R_MIPS_TLS_GOTTPREL and R_MIPS_TLS_GD relocations
+# and generation of corresponding dynamic relocations R_MIPS_TLS_TPREL32,
+# R_MIPS_TLS_DTPMOD32 and R_MIPS_TLS_DTPREL32 in case of shared library.
+
+# Create a shared library with thread symbol D1.
+# RUN: yaml2obj -format=elf -o %t-so.o %s
+# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o
+
+# Check dynamic relocations and GOT in the shared library.
+# RUN: llvm-readobj -r %t.so | FileCheck -check-prefix=REL %s
+# RUN: llvm-readobj -dynamic-table %t.so | FileCheck -check-prefix=DYN %s
+# RUN: llvm-readobj -dt %t.so | FileCheck -check-prefix=SYM %s
+# RUN: llvm-objdump -s %t.so | FileCheck -check-prefix=GOT %s
+
+# REL: Section (4) .rel.dyn {
+# REL-NEXT: 0x2008 R_MIPS_TLS_DTPMOD32 D1 0x0
+# REL-NEXT: 0x200C R_MIPS_TLS_DTPREL32 D1 0x0
+# REL-NEXT: }
+
+# DYN: 0x7000000A MIPS_LOCAL_GOTNO 2
+# DYN: 0x70000013 MIPS_GOTSYM 0x3
+
+# SYM: Name: T1@ (1)
+# SYM: Name: D1@ (4)
+
+# GOT: Contents of section .got:
+# GOT-NEXT: 2000 00000000 00000080 00000000 00000000 ................
+
+---
+FileHeader:
+ Class: ELFCLASS32
+ Data: ELFDATA2LSB
+ OSABI: ELFOSABI_GNU
+ Type: ET_REL
+ Machine: EM_MIPS
+ Flags: [ EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC,
+ EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2 ]
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ AddressAlign: 0x04
+ Size: 0x04
+ - Name: .rel.text
+ Type: SHT_REL
+ Link: .symtab
+ Info: .text
+ AddressAlign: 0x04
+ Relocations:
+ - Offset: 0x00
+ Symbol: D1
+ Type: R_MIPS_TLS_GD
+ - Name: .tdata
+ Type: SHT_PROGBITS
+ Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ]
+ AddressAlign: 0x04
+ Size: 0x04
+
+Symbols:
+ Global:
+ - Name: T1
+ Type: STT_FUNC
+ Section: .text
+ Size: 0x04
+ - Name: D1
+ Type: STT_TLS
+ Section: .tdata
+ Size: 0x04
+...
--- /dev/null
+# Check handling of R_MIPS_TLS_GOTTPREL and R_MIPS_TLS_GD relocations
+# and generation of corresponding dynamic relocations R_MIPS_TLS_TPREL32,
+# R_MIPS_TLS_DTPMOD32 and R_MIPS_TLS_DTPREL32 in case of executable linking.
+
+# Create a shared library with thread symbol D1.
+# RUN: yaml2obj -format=elf -docnum 1 -o %t-so.o %s
+# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o
+
+# Create executable file linked using two object files and the shared library.
+# The object files defines thread symbols D0 and D2.
+# RUN: yaml2obj -format=elf -docnum 2 -o %t-o1.o %s
+# RUN: yaml2obj -format=elf -docnum 3 -o %t-o2.o %s
+# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t-o1.o %t-o2.o %t.so
+
+# Check dynamic relocations and GOT in the executable file.
+# RUN: llvm-readobj -r %t.exe | FileCheck -check-prefix=REL %s
+# RUN: llvm-readobj -dynamic-table %t.exe | FileCheck -check-prefix=DYN %s
+# RUN: llvm-readobj -dt %t.exe | FileCheck -check-prefix=SYM %s
+# RUN: llvm-objdump -s %t.exe | FileCheck -check-prefix=GOT %s
+
+# REL: Section (5) .rel.dyn {
+# REL-NEXT: 0x402008 R_MIPS_TLS_TPREL32 D1 0x0
+# REL-NEXT: 0x40200C R_MIPS_TLS_TPREL32 D2 0x0
+# REL-NEXT: }
+
+# DYN: 0x7000000A MIPS_LOCAL_GOTNO 2
+# DYN: 0x70000013 MIPS_GOTSYM 0x3
+
+# SYM: Name: D2@ (1)
+# SYM: Name: D1@ (4)
+
+# GOT: Contents of section .got:
+# GOT-NEXT: 402000 00000000 00000080 00000000 00000000 ................
+
+# so.o
+---
+FileHeader:
+ Class: ELFCLASS32
+ Data: ELFDATA2LSB
+ OSABI: ELFOSABI_GNU
+ Type: ET_REL
+ Machine: EM_MIPS
+ Flags: [ EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC,
+ EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2 ]
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ AddressAlign: 0x04
+ Size: 0x04
+ - Name: .rel.text
+ Type: SHT_REL
+ Link: .symtab
+ Info: .text
+ AddressAlign: 0x04
+ Relocations:
+ - Offset: 0x00
+ Symbol: D1
+ Type: R_MIPS_TLS_GD
+ - Name: .tdata
+ Type: SHT_PROGBITS
+ Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ]
+ AddressAlign: 0x04
+ Size: 0x04
+
+Symbols:
+ Global:
+ - Name: T1
+ Type: STT_FUNC
+ Section: .text
+ Size: 0x04
+ - Name: D1
+ Type: STT_TLS
+ Section: .tdata
+ Size: 0x04
+
+# o1.o
+---
+FileHeader:
+ Class: ELFCLASS32
+ Data: ELFDATA2LSB
+ OSABI: ELFOSABI_GNU
+ Type: ET_REL
+ Machine: EM_MIPS
+ Flags: [ EF_MIPS_NOREORDER, EF_MIPS_CPIC,
+ EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2 ]
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ AddressAlign: 0x04
+ Size: 0x08
+ - Name: .rel.text
+ Type: SHT_REL
+ Link: .symtab
+ Info: .text
+ AddressAlign: 0x04
+ Relocations:
+ - Offset: 0x00
+ Symbol: D2
+ Type: R_MIPS_TLS_TPREL_HI16
+ - Offset: 0x04
+ Symbol: D2
+ Type: R_MIPS_TLS_TPREL_LO16
+ - Name: .tdata
+ Type: SHT_PROGBITS
+ Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ]
+ AddressAlign: 0x04
+ Size: 0x04
+
+Symbols:
+ Global:
+ - Name: T2
+ Type: STT_FUNC
+ Section: .text
+ Size: 0x08
+ - Name: D2
+ Type: STT_TLS
+ Section: .tdata
+ Size: 0x04
+
+# o2.o
+---
+FileHeader:
+ Class: ELFCLASS32
+ Data: ELFDATA2LSB
+ OSABI: ELFOSABI_GNU
+ Type: ET_REL
+ Machine: EM_MIPS
+ Flags: [ EF_MIPS_NOREORDER, EF_MIPS_CPIC,
+ EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2 ]
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ AddressAlign: 0x04
+ Size: 0x10
+ - Name: .rel.text
+ Type: SHT_REL
+ Link: .symtab
+ Info: .text
+ AddressAlign: 0x04
+ Relocations:
+ - Offset: 0x00
+ Symbol: D1
+ Type: R_MIPS_TLS_GOTTPREL
+ Addend: 0
+ - Offset: 0x04
+ Symbol: D0
+ Type: R_MIPS_TLS_TPREL_HI16
+ Addend: 0
+ - Offset: 0x08
+ Symbol: D0
+ Type: R_MIPS_TLS_TPREL_LO16
+ Addend: 0
+ - Offset: 0x0C
+ Symbol: D2
+ Type: R_MIPS_TLS_GOTTPREL
+ Addend: 0
+ - Name: .tdata
+ Type: SHT_PROGBITS
+ Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ]
+ AddressAlign: 0x04
+ Size: 0x04
+
+Symbols:
+ Global:
+ - Name: D0
+ Type: STT_TLS
+ Section: .tdata
+ Size: 0x04
+ - Name: T0
+ Type: STT_FUNC
+ Section: .text
+ Size: 0x10
+ - Name: D1
+ Type: STT_TLS
+ - Name: D2
+ Type: STT_TLS
+...
--- /dev/null
+# Check handling of R_MIPS_TLS_LDM relocation and generation of corresponding
+# dynamic relocation R_MIPS_TLS_DTPMOD32.
+
+# RUN: yaml2obj -format=elf -docnum 1 -o %t-so1.o %s
+# RUN: yaml2obj -format=elf -docnum 2 -o %t-so2.o %s
+# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so1.o %t-so2.o
+
+# RUN: llvm-readobj -r %t.so | FileCheck -check-prefix=REL %s
+# RUN: llvm-readobj -dynamic-table %t.so | FileCheck -check-prefix=DYN %s
+# RUN: llvm-readobj -dt %t.so | FileCheck -check-prefix=SYM %s
+# RUN: llvm-objdump -s %t.so | FileCheck -check-prefix=GOT %s
+
+# REL: Section (4) .rel.dyn {
+# REL-NEXT: 0x2008 R_MIPS_TLS_DTPMOD32 - 0x0
+# REL-NEXT: }
+
+# DYN: 0x7000000A MIPS_LOCAL_GOTNO 2
+# DYN: 0x70000013 MIPS_GOTSYM 0x4
+
+# SYM: Name: @ (0)
+# SYM: Name: T1@ (1)
+# SYM: Name: T2@ (4)
+# SYM: Name: T3@ (7)
+
+# GOT: Contents of section .got:
+# GOT-NEXT: 2000 00000000 00000080 00000000 00000000 ................
+# Two LDM entries --^--------^
+
+# so1.o
+---
+FileHeader:
+ Class: ELFCLASS32
+ Data: ELFDATA2LSB
+ OSABI: ELFOSABI_GNU
+ Type: ET_REL
+ Machine: EM_MIPS
+ Flags: [ EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC,
+ EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2 ]
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ AddressAlign: 0x04
+ Size: 0x08
+ - Name: .rel.text
+ Type: SHT_REL
+ Link: .symtab
+ AddressAlign: 0x04
+ Info: .text
+ Relocations:
+ - Offset: 0x00
+ Symbol: L01
+ Type: R_MIPS_TLS_LDM
+ - Offset: 0x04
+ Symbol: L01
+ Type: R_MIPS_TLS_LDM
+ - Name: .tdata
+ Type: SHT_PROGBITS
+ Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ]
+ AddressAlign: 0x04
+ Size: 0x04
+
+Symbols:
+ Local:
+ - Name: L01
+ Type: STT_TLS
+ Section: .tdata
+ Size: 0x04
+ Global:
+ - Name: T1
+ Type: STT_FUNC
+ Section: .text
+ Value: 0x00
+ Size: 0x04
+ - Name: T2
+ Type: STT_FUNC
+ Section: .text
+ Value: 0x04
+ Size: 0x04
+
+# so2.o
+---
+FileHeader:
+ Class: ELFCLASS32
+ Data: ELFDATA2LSB
+ OSABI: ELFOSABI_GNU
+ Type: ET_REL
+ Machine: EM_MIPS
+ Flags: [ EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC,
+ EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2 ]
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ AddressAlign: 0x04
+ Size: 0x04
+ - Name: .rel.text
+ Type: SHT_REL
+ Link: .symtab
+ AddressAlign: 0x04
+ Info: .text
+ Relocations:
+ - Offset: 0x00
+ Symbol: L02
+ Type: R_MIPS_TLS_LDM
+ - Name: .tdata
+ Type: SHT_PROGBITS
+ Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ]
+ AddressAlign: 0x04
+ Size: 0x04
+
+Symbols:
+ Local:
+ - Name: L02
+ Type: STT_TLS
+ Section: .tdata
+ Size: 0x04
+ Global:
+ - Name: T3
+ Type: STT_FUNC
+ Section: .text
+ Size: 0x04
+...