[ELF] Implement infrastructure for thunk code creation
Some targets might require creation of thunks. For example, MIPS targets require stubs to call PIC code from non-PIC one. The patch implements infrastructure for thunk code creation and provides support for MIPS LA25 stubs. 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 stub to save the target function address. See page 3-38 ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf - In relocation scanning phase we ask target about thunk creation necessity by calling `TagetInfo::needsThunk` method. The `InputSection` class maintains list of Symbols requires thunk creation. - Reassigning offsets performed for each input sections after relocation scanning complete because position of each section might change due thunk creation. - The patch introduces new dedicated value for DefinedSynthetic symbols DefinedSynthetic::SectionEnd. Synthetic symbol with that value always points to the end of the corresponding output section. That allows to escape updating synthetic symbols if output sections sizes changes after relocation scanning due thunk creation. - In the `InputSection::writeTo` method we write thunks after corresponding input section. Each thunk is written by calling `TargetInfo::writeThunk` method. - The patch supports the only type of thunk code for each target. For now, it is enough. Differential Revision: http://reviews.llvm.org/D17934 llvm-svn: 265059
This commit is contained in:
parent
098c3fcb03
commit
13f6da1d2c
|
@ -38,6 +38,13 @@ InputSectionBase<ELFT>::InputSectionBase(elf::ObjectFile<ELFT> *File,
|
|||
Align = std::max<uintX_t>(Header->sh_addralign, 1);
|
||||
}
|
||||
|
||||
template <class ELFT> size_t InputSectionBase<ELFT>::getSize() const {
|
||||
if (auto *D = dyn_cast<InputSection<ELFT>>(this))
|
||||
if (D->getThunksSize() > 0)
|
||||
return D->getThunkOff() + D->getThunksSize();
|
||||
return Header->sh_size;
|
||||
}
|
||||
|
||||
template <class ELFT> StringRef InputSectionBase<ELFT>::getSectionName() const {
|
||||
return check(File->getObj().getSectionName(this->Header));
|
||||
}
|
||||
|
@ -105,6 +112,19 @@ InputSectionBase<ELFT> *InputSection<ELFT>::getRelocatedSection() {
|
|||
return Sections[this->Header->sh_info];
|
||||
}
|
||||
|
||||
template <class ELFT> void InputSection<ELFT>::addThunk(SymbolBody &Body) {
|
||||
Body.ThunkIndex = Thunks.size();
|
||||
Thunks.push_back(&Body);
|
||||
}
|
||||
|
||||
template <class ELFT> uint64_t InputSection<ELFT>::getThunkOff() const {
|
||||
return this->Header->sh_size;
|
||||
}
|
||||
|
||||
template <class ELFT> uint64_t InputSection<ELFT>::getThunksSize() const {
|
||||
return Thunks.size() * Target->ThunkSize;
|
||||
}
|
||||
|
||||
// This is used for -r. We can't use memcpy to copy relocations because we need
|
||||
// to update symbol table offset and section index for each relocation. So we
|
||||
// copy relocations one by one.
|
||||
|
@ -293,6 +313,9 @@ void InputSectionBase<ELFT>::relocate(uint8_t *Buf, uint8_t *BufEnd,
|
|||
// 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)) {
|
||||
// Get address of a thunk code related to the symbol.
|
||||
SymVA = Body.getThunkVA<ELFT>();
|
||||
} else if (Config->EMachine == EM_MIPS) {
|
||||
SymVA = adjustMipsSymVA<ELFT>(Type, *File, Body, AddrLoc, SymVA);
|
||||
} else if (!Target->needsCopyRel<ELFT>(Type, Body) &&
|
||||
|
@ -333,6 +356,19 @@ template <class ELFT> void InputSection<ELFT>::writeTo(uint8_t *Buf) {
|
|||
else
|
||||
this->relocate(Buf, BufEnd, EObj.rels(RelSec));
|
||||
}
|
||||
|
||||
// The section might have a data/code generated by the linker and need
|
||||
// to be written after the section. Usually these are thunks - small piece
|
||||
// of code used to jump between "incompatible" functions like PIC and non-PIC
|
||||
// or if the jump target too far and its address does not fit to the short
|
||||
// jump istruction.
|
||||
if (!Thunks.empty()) {
|
||||
Buf += OutSecOff + getThunkOff();
|
||||
for (const SymbolBody *S : Thunks) {
|
||||
Target->writeThunk(Buf, S->getVA<ELFT>());
|
||||
Buf += Target->ThunkSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
|
|
|
@ -56,7 +56,7 @@ public:
|
|||
InputSectionBase<ELFT> *Repl;
|
||||
|
||||
// Returns the size of this section (even if this is a common or BSS.)
|
||||
size_t getSize() const { return Header->sh_size; }
|
||||
size_t getSize() const;
|
||||
|
||||
static InputSectionBase<ELFT> *Discarded;
|
||||
|
||||
|
@ -167,6 +167,17 @@ public:
|
|||
|
||||
InputSectionBase<ELFT> *getRelocatedSection();
|
||||
|
||||
// Register thunk related to the symbol. When the section is written
|
||||
// to a mmap'ed file, target is requested to write an actual thunk code.
|
||||
// Now thunks is supported for MIPS target only.
|
||||
void addThunk(SymbolBody &Body);
|
||||
|
||||
// The offset of synthetic thunk code from beginning of this section.
|
||||
uint64_t getThunkOff() const;
|
||||
|
||||
// Size of chunk with thunks code.
|
||||
uint64_t getThunksSize() const;
|
||||
|
||||
private:
|
||||
template <class RelTy>
|
||||
void copyRelocations(uint8_t *Buf, llvm::iterator_range<const RelTy *> Rels);
|
||||
|
@ -176,6 +187,8 @@ private:
|
|||
|
||||
// Used by ICF.
|
||||
uint64_t GroupId = 0;
|
||||
|
||||
llvm::TinyPtrVector<const SymbolBody *> Thunks;
|
||||
};
|
||||
|
||||
// MIPS .reginfo section provides information on the registers used by the code
|
||||
|
|
|
@ -821,12 +821,6 @@ void OutputSection<ELFT>::addSection(InputSectionBase<ELFT> *C) {
|
|||
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;
|
||||
}
|
||||
|
||||
// If an input string is in the form of "foo.N" where N is a number,
|
||||
|
@ -843,8 +837,8 @@ static int getPriority(StringRef S) {
|
|||
}
|
||||
|
||||
// This function is called after we sort input sections
|
||||
// to update their offsets.
|
||||
template <class ELFT> void OutputSection<ELFT>::reassignOffsets() {
|
||||
// and scan relocations to setup sections' offsets.
|
||||
template <class ELFT> void OutputSection<ELFT>::assignOffsets() {
|
||||
uintX_t Off = 0;
|
||||
for (InputSection<ELFT> *S : Sections) {
|
||||
Off = alignTo(Off, S->Align);
|
||||
|
@ -872,7 +866,6 @@ template <class ELFT> void OutputSection<ELFT>::sortInitFini() {
|
|||
Sections.clear();
|
||||
for (Pair &P : V)
|
||||
Sections.push_back(P.second);
|
||||
reassignOffsets();
|
||||
}
|
||||
|
||||
// Returns true if S matches /Filename.?\.o$/.
|
||||
|
@ -933,7 +926,6 @@ static bool compCtors(const InputSection<ELFT> *A,
|
|||
// Read the comment above.
|
||||
template <class ELFT> void OutputSection<ELFT>::sortCtorsDtors() {
|
||||
std::stable_sort(Sections.begin(), Sections.end(), compCtors<ELFT>);
|
||||
reassignOffsets();
|
||||
}
|
||||
|
||||
static void fill(uint8_t *Buf, size_t Size, ArrayRef<uint8_t> A) {
|
||||
|
|
|
@ -87,6 +87,7 @@ public:
|
|||
// Typically the first section of each PT_LOAD segment has this flag.
|
||||
bool PageAlign = false;
|
||||
|
||||
virtual void assignOffsets() {}
|
||||
virtual void finalize() {}
|
||||
virtual void writeTo(uint8_t *Buf) {}
|
||||
virtual ~OutputSectionBase() = default;
|
||||
|
@ -271,10 +272,10 @@ public:
|
|||
void sortInitFini();
|
||||
void sortCtorsDtors();
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
void assignOffsets() override;
|
||||
void finalize() override;
|
||||
|
||||
private:
|
||||
void reassignOffsets();
|
||||
std::vector<InputSection<ELFT> *> Sections;
|
||||
};
|
||||
|
||||
|
|
|
@ -37,6 +37,8 @@ static typename ELFT::uint getSymVA(const SymbolBody &Body,
|
|||
switch (Body.kind()) {
|
||||
case SymbolBody::DefinedSyntheticKind: {
|
||||
auto &D = cast<DefinedSynthetic<ELFT>>(Body);
|
||||
if (D.Value == DefinedSynthetic<ELFT>::SectionEnd)
|
||||
return D.Section.getVA() + D.Section.getSize();
|
||||
return D.Section.getVA() + D.Value;
|
||||
}
|
||||
case SymbolBody::DefinedRegularKind: {
|
||||
|
@ -133,6 +135,13 @@ template <class ELFT> typename ELFT::uint SymbolBody::getPltVA() const {
|
|||
PltIndex * Target->PltEntrySize;
|
||||
}
|
||||
|
||||
template <class ELFT> typename ELFT::uint SymbolBody::getThunkVA() const {
|
||||
auto *D = cast<DefinedRegular<ELFT>>(this);
|
||||
auto *S = cast<InputSection<ELFT>>(D->Section);
|
||||
return S->OutSec->getVA() + S->OutSecOff + S->getThunkOff() +
|
||||
ThunkIndex * Target->ThunkSize;
|
||||
}
|
||||
|
||||
template <class ELFT> typename ELFT::uint SymbolBody::getSize() const {
|
||||
if (auto *B = dyn_cast<DefinedElf<ELFT>>(this))
|
||||
return B->Sym.st_size;
|
||||
|
@ -298,6 +307,11 @@ template uint32_t SymbolBody::template getSize<ELF32BE>() const;
|
|||
template uint64_t SymbolBody::template getSize<ELF64LE>() const;
|
||||
template uint64_t SymbolBody::template getSize<ELF64BE>() const;
|
||||
|
||||
template uint32_t SymbolBody::template getThunkVA<ELF32LE>() const;
|
||||
template uint32_t SymbolBody::template getThunkVA<ELF32BE>() const;
|
||||
template uint64_t SymbolBody::template getThunkVA<ELF64LE>() const;
|
||||
template uint64_t SymbolBody::template getThunkVA<ELF64BE>() const;
|
||||
|
||||
template int SymbolBody::compare<ELF32LE>(SymbolBody *Other);
|
||||
template int SymbolBody::compare<ELF32BE>(SymbolBody *Other);
|
||||
template int SymbolBody::compare<ELF64LE>(SymbolBody *Other);
|
||||
|
|
|
@ -85,9 +85,11 @@ public:
|
|||
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; }
|
||||
|
||||
void setUsedInRegularObj() { IsUsedInRegularObj = true; }
|
||||
|
||||
|
@ -97,6 +99,7 @@ public:
|
|||
template <class ELFT> typename ELFT::uint getGotVA() const;
|
||||
template <class ELFT> typename ELFT::uint getGotPltVA() const;
|
||||
template <class ELFT> typename ELFT::uint getPltVA() const;
|
||||
template <class ELFT> typename ELFT::uint getThunkVA() const;
|
||||
template <class ELFT> typename ELFT::uint getSize() const;
|
||||
|
||||
// A SymbolBody has a backreference to a Symbol. Originally they are
|
||||
|
@ -249,6 +252,10 @@ public:
|
|||
return S->kind() == SymbolBody::DefinedSyntheticKind;
|
||||
}
|
||||
|
||||
// Special value designates that the symbol 'points'
|
||||
// to the end of the section.
|
||||
static const uintX_t SectionEnd = uintX_t(-1);
|
||||
|
||||
uintX_t Value;
|
||||
const OutputSectionBase<ELFT> &Section;
|
||||
};
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include "Target.h"
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "OutputSections.h"
|
||||
#include "Symbols.h"
|
||||
|
||||
|
@ -198,9 +199,12 @@ public:
|
|||
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, const 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 SA) const override;
|
||||
bool isHintRel(uint32_t Type) const override;
|
||||
|
@ -331,6 +335,11 @@ TargetInfo::PltNeed TargetInfo::needsPlt(uint32_t Type,
|
|||
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 {
|
||||
|
@ -1581,6 +1590,7 @@ template <class ELFT> MipsTargetInfo<ELFT>::MipsTargetInfo() {
|
|||
PageSize = 65536;
|
||||
PltEntrySize = 16;
|
||||
PltZeroSize = 32;
|
||||
ThunkSize = 16;
|
||||
UseLazyBinding = true;
|
||||
CopyRel = R_MIPS_COPY;
|
||||
PltRel = R_MIPS_JUMP_SLOT;
|
||||
|
@ -1694,6 +1704,20 @@ void MipsTargetInfo<ELFT>::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
|
|||
writeMipsLo16<E>(Buf + 12, GotEntryAddr);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void MipsTargetInfo<ELFT>::writeThunk(uint8_t *Buf, uint64_t S) const {
|
||||
// Write MIPS LA25 thunk code to call PIC function from the non-PIC one.
|
||||
// See MipsTargetInfo::writeThunk for details.
|
||||
const endianness E = ELFT::TargetEndianness;
|
||||
write32<E>(Buf, 0x3c190000); // lui $25, %hi(func)
|
||||
write32<E>(Buf + 4, 0x08000000); // j func
|
||||
write32<E>(Buf + 8, 0x27390000); // addiu $25, $25, %lo(func)
|
||||
write32<E>(Buf + 12, 0x00000000); // nop
|
||||
writeMipsHi16<E>(Buf, S);
|
||||
write32<E>(Buf + 4, 0x08000000 | (S >> 2));
|
||||
writeMipsLo16<E>(Buf + 8, S);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
bool MipsTargetInfo<ELFT>::needsCopyRelImpl(uint32_t Type) const {
|
||||
return !isRelRelative(Type);
|
||||
|
@ -1714,6 +1738,31 @@ bool MipsTargetInfo<ELFT>::needsPltImpl(uint32_t Type) const {
|
|||
return Type == R_MIPS_26;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
bool MipsTargetInfo<ELFT>::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<ELFFileBase<ELFT>>(&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<DefinedRegular<ELFT>>(&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 <class ELFT>
|
||||
uint64_t MipsTargetInfo<ELFT>::getImplicitAddend(uint8_t *Buf,
|
||||
uint32_t Type) const {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
class InputFile;
|
||||
class SymbolBody;
|
||||
|
||||
class TargetInfo {
|
||||
|
@ -62,6 +63,11 @@ public:
|
|||
enum PltNeed { Plt_No, Plt_Explicit, Plt_Implicit };
|
||||
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) const = 0;
|
||||
virtual bool isGotRelative(uint32_t Type) const;
|
||||
|
@ -94,6 +100,7 @@ public:
|
|||
unsigned PltZeroSize = 0;
|
||||
unsigned GotHeaderEntriesNum = 0;
|
||||
unsigned GotPltHeaderEntriesNum = 3;
|
||||
uint32_t ThunkSize = 0;
|
||||
bool UseLazyBinding = false;
|
||||
|
||||
private:
|
||||
|
|
|
@ -85,6 +85,9 @@ private:
|
|||
bool isOutputDynamic() const {
|
||||
return !Symtab.getSharedFiles().empty() || Config->Pic;
|
||||
}
|
||||
template <class RelTy>
|
||||
void scanRelocsForThunks(const elf::ObjectFile<ELFT> &File,
|
||||
iterator_range<const RelTy *> Rels);
|
||||
|
||||
void ensureBss();
|
||||
void addCommonSymbols(std::vector<DefinedCommon *> &Syms);
|
||||
|
@ -298,6 +301,25 @@ static unsigned handleTlsRelocation(uint32_t Type, SymbolBody &Body,
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Some targets might require creation of thunks for relocations. Now we
|
||||
// support only MIPS which requires LA25 thunk to call PIC code from non-PIC
|
||||
// one. Scan relocations to find each one requires thunk.
|
||||
template <class ELFT>
|
||||
template <class RelTy>
|
||||
void Writer<ELFT>::scanRelocsForThunks(const elf::ObjectFile<ELFT> &File,
|
||||
iterator_range<const RelTy *> Rels) {
|
||||
for (const RelTy &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))
|
||||
continue;
|
||||
auto *D = cast<DefinedRegular<ELFT>>(&Body);
|
||||
auto *S = cast<InputSection<ELFT>>(D->Section);
|
||||
S->addThunk(Body);
|
||||
}
|
||||
}
|
||||
|
||||
// The reason we have to do this early scan is as follows
|
||||
// * To mmap the output file, we need to know the size
|
||||
// * For that, we need to know how many dynamic relocs we will have.
|
||||
|
@ -479,6 +501,10 @@ void Writer<ELFT>::scanRelocs(InputSectionBase<ELFT> &C,
|
|||
Out<ELFT>::RelaDyn->addReloc(
|
||||
{Target->RelativeRel, &C, RI.r_offset, true, &Body, Addend});
|
||||
}
|
||||
|
||||
// Scan relocations for necessary thunks.
|
||||
if (Config->EMachine == EM_MIPS)
|
||||
scanRelocsForThunks(File, Rels);
|
||||
}
|
||||
|
||||
template <class ELFT> void Writer<ELFT>::scanRelocs(InputSection<ELFT> &C) {
|
||||
|
@ -1042,6 +1068,9 @@ template <class ELFT> bool Writer<ELFT>::createSections() {
|
|||
}
|
||||
}
|
||||
|
||||
for (OutputSectionBase<ELFT> *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<DefinedCommon *> CommonSymbols;
|
||||
|
@ -1167,7 +1196,8 @@ template <class ELFT> void Writer<ELFT>::addStartEndSymbols() {
|
|||
OutputSectionBase<ELFT> *OS) {
|
||||
if (OS) {
|
||||
Symtab.addSynthetic(Start, *OS, 0, STV_DEFAULT);
|
||||
Symtab.addSynthetic(End, *OS, OS->getSize(), STV_DEFAULT);
|
||||
Symtab.addSynthetic(End, *OS, DefinedSynthetic<ELFT>::SectionEnd,
|
||||
STV_DEFAULT);
|
||||
} else {
|
||||
Symtab.addIgnored(Start);
|
||||
Symtab.addIgnored(End);
|
||||
|
@ -1200,7 +1230,8 @@ void Writer<ELFT>::addStartStopSymbols(OutputSectionBase<ELFT> *Sec) {
|
|||
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<ELFT>::SectionEnd,
|
||||
STV_DEFAULT);
|
||||
}
|
||||
|
||||
template <class ELFT> static bool needsPtLoad(OutputSectionBase<ELFT> *Sec) {
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1,58 @@
|
|||
# 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 0a jal 131112
|
||||
# ^-- 0x20030 .pic.foo1a
|
||||
# CHECK-NEXT: 20004: 00 00 00 00 nop
|
||||
# CHECK-NEXT: 20008: 0c 00 80 15 jal 131156
|
||||
# ^-- 0x20060 .pic.foo2
|
||||
# CHECK-NEXT: 2000c: 00 00 00 00 nop
|
||||
# CHECK-NEXT: 20010: 0c 00 80 0e jal 131128
|
||||
# ^-- 0x20040 .pic.foo1b
|
||||
# CHECK-NEXT: 20014: 00 00 00 00 nop
|
||||
# CHECK-NEXT: 20018: 0c 00 80 15 jal 131156
|
||||
# ^-- 0x20060 .pic.foo2
|
||||
# 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: 3c 19 00 02 lui $25, 2
|
||||
# CHECK-NEXT: 2002c: 08 00 80 08 j 131104 <foo1a>
|
||||
# CHECK-NEXT: 20030: 27 39 00 20 addiu $25, $25, 32
|
||||
# CHECK-NEXT: 20034: 00 00 00 00 nop
|
||||
# CHECK-NEXT: 20038: 3c 19 00 02 lui $25, 2
|
||||
# CHECK-NEXT: 2003c: 08 00 80 09 j 131108 <foo1b>
|
||||
# CHECK-NEXT: 20040: 27 39 00 24 addiu $25, $25, 36
|
||||
# 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: foo2:
|
||||
# CHECK-NEXT: 20050: 00 00 00 00 nop
|
||||
#
|
||||
# CHECK-NEXT: 20054: 3c 19 00 02 lui $25, 2
|
||||
# CHECK-NEXT: 20058: 08 00 80 14 j 131152 <foo2>
|
||||
# CHECK-NEXT: 2005c: 27 39 00 50 addiu $25, $25, 80
|
||||
# CHECK-NEXT: 20060: 00 00 00 00 nop
|
||||
|
||||
.text
|
||||
.globl __start
|
||||
__start:
|
||||
jal foo1a
|
||||
jal foo2
|
||||
jal foo1b
|
||||
jal foo2
|
Loading…
Reference in New Issue