Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -267,6 +267,8 @@ // If that's the case, we leave the field alone rather than filling it // with a possibly incorrect value. continue; + } else if (Target->needsThunk(Type, *this->getFile(), Body)) { + SymVA = Body.getThunkVA(); } else if (Config->EMachine == EM_MIPS) { if (Type == R_MIPS_HI16 && &Body == Config->MipsGpDisp) { SymVA = getMipsGpAddr() - AddrLoc + A; Index: ELF/OutputSections.h =================================================================== --- ELF/OutputSections.h +++ ELF/OutputSections.h @@ -34,6 +34,7 @@ template class OutputSection; template class ObjectFile; template class DefinedRegular; +template class ThunkChunk; template static inline typename llvm::object::ELFFile::uintX_t @@ -71,6 +72,9 @@ virtual void addSection(InputSectionBase *C) {} + virtual void addThunk(const InputSectionBase &C, SymbolBody &S) {} + virtual uintX_t getThunkOffset(const InputSectionBase &C) { return 0; } + unsigned SectionIndex; // Returns the size of the section in the output file. @@ -89,6 +93,7 @@ Header.sh_addralign = Align; } + virtual void assignOffsets() {} virtual void finalize() {} virtual void writeTo(uint8_t *Buf) {} virtual ~OutputSectionBase() = default; @@ -271,14 +276,18 @@ typedef typename llvm::object::ELFFile::uintX_t uintX_t; OutputSection(StringRef Name, uint32_t Type, uintX_t Flags); void addSection(InputSectionBase *C) override; + void addThunk(const InputSectionBase &C, SymbolBody &S) override; + uintX_t getThunkOffset(const InputSectionBase &C) override; void sortInitFini(); void sortCtorsDtors(); void writeTo(uint8_t *Buf) override; + void assignOffsets() override; void finalize() override; private: - void reassignOffsets(); std::vector *> Sections; + llvm::DenseMap *, ThunkChunk*> ThunkChunks; + llvm::BumpPtrAllocator Alloc; }; template Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -746,6 +746,31 @@ this->Header.sh_size += 8; } +template class lld::elf::ThunkChunk { + typedef typename llvm::object::ELFFile::uintX_t uintX_t; + +public: + uintX_t OutSecOff = 0; + + void addThunk(SymbolBody &S) { + S.ThunkIndex = Thunks.size(); + Thunks.push_back(&S); + } + + size_t getSize() const { return Target->ThunkSize * Thunks.size(); } + + void writeTo(uint8_t *Buf) { + Buf += OutSecOff; + for (SymbolBody *S : Thunks) { + Target->writeThunk(Buf, S->getVA()); + Buf += Target->ThunkSize; + } + } + +private: + std::vector Thunks; +}; + template OutputSection::OutputSection(StringRef Name, uint32_t Type, uintX_t Flags) : OutputSectionBase(Name, Type, Flags) { @@ -773,12 +798,23 @@ Sections.push_back(S); S->OutSec = this; this->updateAlign(S->Align); +} - uintX_t Off = this->Header.sh_size; - Off = alignTo(Off, S->Align); - S->OutSecOff = Off; - Off += S->getSize(); - this->Header.sh_size = Off; +template +void OutputSection::addThunk(const InputSectionBase &C, + SymbolBody &S) { + ThunkChunk *&TC = ThunkChunks[&C]; + if (!TC) + TC = new (Alloc) ThunkChunk(); + TC->addThunk(S); +} + +template +typename OutputSection::uintX_t +OutputSection::getThunkOffset(const InputSectionBase &S) { + ThunkChunk *TC = ThunkChunks.lookup(&S); + assert(TC); + return TC->OutSecOff; } // If an input string is in the form of "foo.N" where N is a number, @@ -795,11 +831,16 @@ } // This function is called after we sort input sections -// to update their offsets. -template void OutputSection::reassignOffsets() { +// to setup their offsets. +template void OutputSection::assignOffsets() { uintX_t Off = 0; for (InputSection *S : Sections) { Off = alignTo(Off, S->Align); + if (ThunkChunk * TC = ThunkChunks.lookup(S)) { + TC->OutSecOff = Off; + Off += TC->getSize(); + } + Off = alignTo(Off, S->Align); S->OutSecOff = Off; Off += S->getSize(); } @@ -824,7 +865,6 @@ Sections.clear(); for (Pair &P : V) Sections.push_back(P.second); - reassignOffsets(); } // Returns true if S matches /Filename.?\.o$/. @@ -885,7 +925,6 @@ // Read the comment above. template void OutputSection::sortCtorsDtors() { std::stable_sort(Sections.begin(), Sections.end(), compCtors); - reassignOffsets(); } // Returns true if a symbol can be replaced at load-time by a symbol @@ -930,13 +969,15 @@ ArrayRef Filler = Script->getFiller(this->Name); if (!Filler.empty()) fill(Buf, this->getSize(), Filler); - if (Config->Threads) { - parallel_for_each(Sections.begin(), Sections.end(), - [=](InputSection *C) { C->writeTo(Buf); }); - } else { - for (InputSection *C : Sections) - C->writeTo(Buf); - } + auto WriteSection = [=](InputSection *C) { + if (ThunkChunk *TC = ThunkChunks.lookup(C)) + TC->writeTo(Buf); + C->writeTo(Buf); + }; + if (Config->Threads) + parallel_for_each(Sections.begin(), Sections.end(), WriteSection); + else + std::for_each(Sections.begin(), Sections.end(), WriteSection); } template Index: ELF/Symbols.h =================================================================== --- ELF/Symbols.h +++ ELF/Symbols.h @@ -93,9 +93,11 @@ uint32_t GotIndex = -1; uint32_t GotPltIndex = -1; uint32_t PltIndex = -1; + uint32_t ThunkIndex = -1; bool hasGlobalDynIndex() { return GlobalDynIndex != uint32_t(-1); } bool isInGot() const { return GotIndex != -1U; } bool isInPlt() const { return PltIndex != -1U; } + bool hasThunk() const { return ThunkIndex != -1U; } template typename llvm::object::ELFFile::uintX_t @@ -108,6 +110,8 @@ typename llvm::object::ELFFile::uintX_t getPltVA() const; template typename llvm::object::ELFFile::uintX_t getSize() const; + template + typename llvm::object::ELFFile::uintX_t getThunkVA() const; // A SymbolBody has a backreference to a Symbol. Originally they are // doubly-linked. A backreference will never change. But the pointer @@ -254,6 +258,10 @@ return S->kind() == SymbolBody::DefinedSyntheticKind; } + // Special value designates that the symbol 'points' + // to the end of the section. + static const uintX_t End = uintX_t(-1); + uintX_t Value; const OutputSectionBase &Section; }; Index: ELF/Symbols.cpp =================================================================== --- ELF/Symbols.cpp +++ ELF/Symbols.cpp @@ -129,6 +129,13 @@ return 0; } +template +typename ELFFile::uintX_t SymbolBody::getThunkVA() const { + auto *S = cast>(this)->Section; + return S->OutSec->getVA() + S->OutSec->getThunkOffset(*S) + + ThunkIndex * Target->ThunkSize; +} + static uint8_t getMinVisibility(uint8_t VA, uint8_t VB) { if (VA == STV_DEFAULT) return VB; @@ -285,6 +292,11 @@ template uint64_t SymbolBody::template getSize() const; template uint64_t SymbolBody::template getSize() const; +template uint32_t SymbolBody::template getThunkVA() const; +template uint32_t SymbolBody::template getThunkVA() const; +template uint64_t SymbolBody::template getThunkVA() const; +template uint64_t SymbolBody::template getThunkVA() const; + template int SymbolBody::compare(SymbolBody *Other); template int SymbolBody::compare(SymbolBody *Other); template int SymbolBody::compare(SymbolBody *Other); Index: ELF/Target.h =================================================================== --- ELF/Target.h +++ ELF/Target.h @@ -17,6 +17,7 @@ namespace lld { namespace elf { +class InputFile; class SymbolBody; class TargetInfo { @@ -62,6 +63,11 @@ template PltNeed needsPlt(uint32_t Type, const SymbolBody &S) const; + virtual bool needsThunk(uint32_t Type, const InputFile &File, + const SymbolBody &S) const; + + virtual void writeThunk(uint8_t *Buf, uint64_t S) const {} + virtual void relocateOne(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type, uint64_t P, uint64_t SA, uint64_t ZA = 0, uint8_t *PairedLoc = nullptr) const = 0; @@ -95,6 +101,7 @@ unsigned PltZeroSize = 0; unsigned GotHeaderEntriesNum = 0; unsigned GotPltHeaderEntriesNum = 3; + uint32_t ThunkSize = 0; bool UseLazyBinding = false; private: Index: ELF/Target.cpp =================================================================== --- ELF/Target.cpp +++ ELF/Target.cpp @@ -17,6 +17,7 @@ #include "Target.h" #include "Error.h" +#include "InputFiles.h" #include "OutputSections.h" #include "Symbols.h" @@ -219,9 +220,12 @@ void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const override; void writeGotHeader(uint8_t *Buf) const override; + void writeThunk(uint8_t *Buf, uint64_t S) const override; bool needsCopyRelImpl(uint32_t Type) const override; bool needsGot(uint32_t Type, SymbolBody &S) const override; bool needsPltImpl(uint32_t Type) const override; + bool needsThunk(uint32_t Type, const InputFile &File, + const SymbolBody &S) const override; void relocateOne(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type, uint64_t P, uint64_t S, uint64_t ZA = 0, uint8_t *PairedLoc = nullptr) const override; @@ -349,6 +353,11 @@ return Plt_No; } +bool TargetInfo::needsThunk(uint32_t Type, const InputFile &File, + const SymbolBody &S) const { + return false; +} + bool TargetInfo::isTlsInitialExecRel(uint32_t Type) const { return false; } bool TargetInfo::pointsToLocalDynamicGotEntry(uint32_t Type) const { @@ -1565,6 +1574,7 @@ PageSize = 65536; PltEntrySize = 16; PltZeroSize = 32; + ThunkSize = 16; UseLazyBinding = true; CopyRel = R_MIPS_COPY; PltRel = R_MIPS_JUMP_SLOT; @@ -1678,6 +1688,23 @@ } template +void MipsTargetInfo::writeThunk(uint8_t *Buf, uint64_t S) const { + // Any MIPS PIC code function is invoked with its address in register $t9. + // So if we have a branch instruction from non-PIC code to the PIC one we + // cannot make the jump directly and need to create a stub to save the target + // function address. + // See page 3-38 ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf + const endianness E = ELFT::TargetEndianness; + write32(Buf, 0x3c190000); // lui $25, %hi(func) + write32(Buf + 4, 0x08000000); // j func + write32(Buf + 8, 0x27390000); // addiu $25, $25, %lo(func) + write32(Buf + 12, 0x00000000); // nop + writeMipsHi16(Buf, S); + write32(Buf + 4, 0x08000000 | (S >> 2)); + writeMipsLo16(Buf + 8, S); +} + +template bool MipsTargetInfo::needsCopyRelImpl(uint32_t Type) const { return !isRelRelative(Type); } @@ -1698,6 +1725,31 @@ } template +bool MipsTargetInfo::needsThunk(uint32_t Type, const InputFile &File, + const SymbolBody &S) const { + // Any MIPS PIC code function is invoked with its address in register $t9. + // So if we have a branch instruction from non-PIC code to the PIC one + // we cannot make the jump directly and need to create a small stubs + // to save the target function address. + // See page 3-38 ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf + if (Type != R_MIPS_26) + return false; + auto *F = dyn_cast>(&File); + if (!F) + return false; + // If current file has PIC code, LA25 stub is not required. + if (F->getObj().getHeader()->e_flags & EF_MIPS_PIC) + return false; + auto *D = dyn_cast>(&S); + if (!D || !D->Section) + return false; + // LA25 is required if target file has PIC code + // or target symbol is a PIC symbol. + return (D->Section->getFile()->getObj().getHeader()->e_flags & EF_MIPS_PIC) || + (D->Sym.st_other & STO_MIPS_MIPS16) == STO_MIPS_PIC; +} + +template void MipsTargetInfo::relocateOne(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type, uint64_t P, uint64_t S, uint64_t ZA, uint8_t *PairedLoc) const { Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -477,6 +477,20 @@ Out::RelaDyn->addReloc( {Target->RelativeRel, &C, RI.r_offset, true, &Body, Addend}); } + + // Some targets might require creation of thunk for relocations. + // For example, MIPS needs LA25 thunk to local call PIC code from non-PIC. + for (const RelType &RI : Rels) { + uint32_t Type = RI.getType(Config->Mips64EL); + uint32_t SymIndex = RI.getSymbol(Config->Mips64EL); + SymbolBody &Body = File.getSymbolBody(SymIndex).repl(); + + if (!Body.hasThunk() && Target->needsThunk(Type, File, Body)) { + const InputSectionBase *Section = + cast>(&Body)->Section; + Section->OutSec->addThunk(*Section, Body); + } + } } template void Writer::scanRelocs(InputSection &C) { @@ -1025,6 +1039,9 @@ } } + for (OutputSectionBase *Sec : getSections()) + Sec->assignOffsets(); + // Now that we have defined all possible symbols including linker- // synthesized ones. Visit all symbols to give the finishing touches. std::vector CommonSymbols; @@ -1040,6 +1057,11 @@ if (auto *SC = dyn_cast>(Body)) if (SC->needsCopy()) CopyRelSymbols.push_back(SC); + if (auto *SS = dyn_cast>(Body)) + // Setup values of synthetic symbols that need + // to point to end of a section. + if (SS->Value == DefinedSynthetic::End) + SS->Value = SS->Section.getSize(); if (!includeInSymtab(*Body)) continue; @@ -1150,7 +1172,7 @@ OutputSectionBase *OS) { if (OS) { Symtab.addSynthetic(Start, *OS, 0, STV_DEFAULT); - Symtab.addSynthetic(End, *OS, OS->getSize(), STV_DEFAULT); + Symtab.addSynthetic(End, *OS, DefinedSynthetic::End, STV_DEFAULT); } else { Symtab.addIgnored(Start); Symtab.addIgnored(End); @@ -1183,7 +1205,7 @@ Symtab.addSynthetic(Start, *Sec, 0, STV_DEFAULT); if (SymbolBody *B = Symtab.find(Stop)) if (B->isUndefined()) - Symtab.addSynthetic(Stop, *Sec, Sec->getSize(), STV_DEFAULT); + Symtab.addSynthetic(Stop, *Sec, DefinedSynthetic::End, STV_DEFAULT); } template static bool needsPtLoad(OutputSectionBase *Sec) { Index: test/ELF/Inputs/mips-pic.s =================================================================== --- /dev/null +++ test/ELF/Inputs/mips-pic.s @@ -0,0 +1,19 @@ + .option pic2 + + .section .text.1,"ax",@progbits + .align 4 + .globl foo1a + .type foo1a, @function +foo1a: + nop + .globl foo1b + .type foo1b, @function +foo1b: + nop + + .section .text.2,"ax",@progbits + .align 4 + .globl foo2 + .type foo2, @function +foo2: + nop Index: test/ELF/mips-npic-call-pic.s =================================================================== --- /dev/null +++ test/ELF/mips-npic-call-pic.s @@ -0,0 +1,59 @@ +# Check LA25 stubs creation. This stub code is necessary when +# non-PIC code calls PIC function. + +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \ +# RUN: %p/Inputs/mips-pic.s -o %t-pic.o +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t-npic.o +# RUN: ld.lld %t-npic.o %t-pic.o -o %t.exe +# RUN: llvm-objdump -d %t.exe | FileCheck %s + +# REQUIRES: mips + +# CHECK: Disassembly of section .text: +# CHECK-NEXT: __start: +# CHECK-NEXT: 20000: 0c 00 80 08 jal 131104 +# ^-- 0x20020 .pic.foo1a +# CHECK-NEXT: 20004: 00 00 00 00 nop +# CHECK-NEXT: 20008: 0c 00 80 14 jal 131152 +# ^-- 0x20050 .pic.foo2 +# CHECK-NEXT: 2000c: 00 00 00 00 nop +# CHECK-NEXT: 20010: 0c 00 80 0c jal 131120 +# ^-- 0x20030 .pic.foo1b +# CHECK-NEXT: 20014: 00 00 00 00 nop +# CHECK-NEXT: 20018: 0c 00 80 14 jal 131152 +# ^-- 0x20050 .pic.foo2 +# CHECK-NEXT: 2001c: 00 00 00 00 nop +# CHECK-NEXT: 20020: 3c 19 00 02 lui $25, 2 +# CHECK-NEXT: 20024: 08 00 80 10 j 131136 +# ^-- 0x20040 foo1a +# CHECK-NEXT: 20028: 27 39 00 40 addiu $25, $25, 64 +# CHECK-NEXT: 2002c: 00 00 00 00 nop +# CHECK-NEXT: 20030: 3c 19 00 02 lui $25, 2 +# CHECK-NEXT: 20034: 08 00 80 11 j 131140 +# ^-- 0x20044 foo1b +# CHECK-NEXT: 20038: 27 39 00 44 addiu $25, $25, 68 +# CHECK-NEXT: 2003c: 00 00 00 00 nop +# +# CHECK: foo1a: +# CHECK-NEXT: 20040: 00 00 00 00 nop +# +# CHECK: foo1b: +# CHECK-NEXT: 20044: 00 00 00 00 nop +# CHECK-NEXT: 20048: 00 00 00 00 nop +# CHECK-NEXT: 2004c: 00 00 00 00 nop +# CHECK-NEXT: 20050: 3c 19 00 02 lui $25, 2 +# CHECK-NEXT: 20054: 08 00 80 18 j 131168 +# ^-- 0x20060 foo2 +# CHECK-NEXT: 20058: 27 39 00 60 addiu $25, $25, 96 +# CHECK-NEXT: 2005c: 00 00 00 00 nop +# +# CHECK: foo2: +# CHECK-NEXT: 20060: 00 00 00 00 nop + + .text + .globl __start +__start: + jal foo1a + jal foo2 + jal foo1b + jal foo2