Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -295,6 +295,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; Index: ELF/OutputSections.h =================================================================== --- ELF/OutputSections.h +++ ELF/OutputSections.h @@ -77,6 +77,9 @@ virtual void addSection(InputSectionBase *C) {} + virtual void addThunk(SymbolBody *S) {} + virtual uintX_t getThunkOffset() { return 0; } + unsigned SectionIndex; // Returns the size of the section in the output file. @@ -289,6 +292,8 @@ 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(SymbolBody *S) override; + uintX_t getThunkOffset() override { return ThunkOffset; } void sortInitFini(); void sortCtorsDtors(); void writeTo(uint8_t *Buf) override; @@ -297,6 +302,8 @@ private: void reassignOffsets(); std::vector *> Sections; + std::vector Thunks; + uintX_t ThunkOffset = 0; }; template Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -756,6 +756,11 @@ } template void OutputSection::finalize() { + if (!Thunks.empty()) { + // Update the section's size. + ThunkOffset = alignTo(this->Header.sh_size, this->Header.sh_addralign); + this->Header.sh_size = ThunkOffset + Thunks.size() * Target->ThunkSize; + } uint32_t Type = this->Header.sh_type; if (Type != SHT_RELA && Type != SHT_REL) return; @@ -781,6 +786,11 @@ this->Header.sh_size = Off; } +template void OutputSection::addThunk(SymbolBody *S) { + S->ThunkIndex = Thunks.size(); + Thunks.push_back(S); +} + // If an input string is in the form of "foo.N" where N is a number, // return N. Otherwise, returns 65536, which is one greater than the // lowest priority. @@ -973,6 +983,11 @@ fill(Buf, this->getSize(), Filler); for (InputSection *C : Sections) C->writeTo(Buf); + uint8_t *TOff = Buf + ThunkOffset; + for (SymbolBody *T : Thunks) { + Target->writeThunk(TOff, T->getVA()); + TOff += Target->ThunkSize; + } } template Index: ELF/Symbols.h =================================================================== --- ELF/Symbols.h +++ ELF/Symbols.h @@ -95,9 +95,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 getVA() const; @@ -109,6 +111,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 @@ -255,6 +259,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 @@ -104,6 +104,12 @@ return 0; } +template +typename ELFFile::uintX_t SymbolBody::getThunkVA() const { + auto *S = cast>(this)->Section->OutSec; + return S->getVA() + S->getThunkOffset() + ThunkIndex * Target->ThunkSize; +} + static uint8_t getMinVisibility(uint8_t VA, uint8_t VB) { if (VA == STV_DEFAULT) return VB; @@ -281,6 +287,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 { @@ -63,6 +64,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; @@ -96,6 +102,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" @@ -222,9 +223,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; @@ -356,6 +360,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 { @@ -1590,6 +1599,7 @@ PageSize = 65536; PltEntrySize = 16; PltZeroSize = 32; + ThunkSize = 16; UseLazyBinding = true; CopyRel = R_MIPS_COPY; PltRel = R_MIPS_JUMP_SLOT; @@ -1703,6 +1713,18 @@ } template +void MipsTargetInfo::writeThunk(uint8_t *Buf, uint64_t S) const { + 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); } @@ -1723,6 +1745,35 @@ } 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 & llvm::ELF::EF_MIPS_PIC) + return false; + auto *ElfSym = dyn_cast>(&S); + if (!ElfSym || !ElfSym->Section) + return false; + // LA25 is required if target file has PIC code or ... + if (ElfSym->Section->getFile()->getObj().getHeader()->e_flags & + llvm::ELF::EF_MIPS_PIC) + return true; + // ... target symbol is a PIC symbol. + if ((ElfSym->Sym.st_other & llvm::ELF::STO_MIPS_MIPS16) == STO_MIPS_PIC) + return true; + return false; +} + +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 @@ -343,6 +343,11 @@ continue; } + // Some targets might require creation of thunk for relocations. + // For example MIPS needs LA25 thunk to local call PIC code from non-PIC. + if (Body && !Body->hasThunk() && Target->needsThunk(Type, File, *Body)) + cast>(Body)->Section->OutSec->addThunk(Body); + // If a symbol in a DSO is referenced directly instead of through GOT, // we need to create a copy relocation for the symbol. if (auto *B = dyn_cast_or_null>(Body)) { @@ -1053,6 +1058,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; @@ -1163,7 +1173,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); @@ -1196,7 +1206,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,55 @@ +# 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 10 jal 131136 +# CHECK-NEXT: 20004: 00 00 00 00 nop +# CHECK-NEXT: 20008: 0c 00 80 14 jal 131152 +# CHECK-NEXT: 2000c: 00 00 00 00 nop +# CHECK-NEXT: 20010: 0c 00 80 18 jal 131168 +# CHECK-NEXT: 20014: 00 00 00 00 nop +# CHECK-NEXT: 20018: 0c 00 80 14 jal 131152 +# CHECK-NEXT: 2001c: 00 00 00 00 nop +# +# CHECK: foo1a: +# CHECK-NEXT: 20020: 00 00 00 00 nop +# +# CHECK: foo1b: +# CHECK-NEXT: 20024: 00 00 00 00 nop +# CHECK-NEXT: 20028: 00 00 00 00 nop +# CHECK-NEXT: 2002c: 00 00 00 00 nop +# +# CHECK: foo2: +# CHECK-NEXT: 20030: 00 00 00 00 nop +# CHECK-NEXT: 20034: 00 00 00 00 nop +# CHECK-NEXT: 20038: 00 00 00 00 nop +# CHECK-NEXT: 2003c: 00 00 00 00 nop +# CHECK-NEXT: 20040: 3c 19 00 02 lui $25, 2 +# CHECK-NEXT: 20044: 08 00 80 08 j 131104 +# CHECK-NEXT: 20048: 27 39 00 20 addiu $25, $25, 32 +# CHECK-NEXT: 2004c: 00 00 00 00 nop +# CHECK-NEXT: 20050: 3c 19 00 02 lui $25, 2 +# CHECK-NEXT: 20054: 08 00 80 0c j 131120 +# CHECK-NEXT: 20058: 27 39 00 30 addiu $25, $25, 48 +# CHECK-NEXT: 2005c: 00 00 00 00 nop +# CHECK-NEXT: 20060: 3c 19 00 02 lui $25, 2 +# CHECK-NEXT: 20064: 08 00 80 09 j 131108 +# CHECK-NEXT: 20068: 27 39 00 24 addiu $25, $25, 36 +# CHECK-NEXT: 2006c: 00 00 00 00 nop + + .text + .globl __start +__start: + jal foo1a + jal foo2 + jal foo1b + jal foo2