diff options
-rw-r--r-- | ELF/InputSection.cpp | 23 | ||||
-rw-r--r-- | ELF/InputSection.h | 11 | ||||
-rw-r--r-- | ELF/OutputSections.cpp | 50 | ||||
-rw-r--r-- | ELF/OutputSections.h | 38 | ||||
-rw-r--r-- | ELF/Relocations.cpp | 31 | ||||
-rw-r--r-- | ELF/Relocations.h | 3 | ||||
-rw-r--r-- | ELF/Writer.cpp | 30 | ||||
-rw-r--r-- | test/ELF/mips-npic-call-pic.s | 65 |
8 files changed, 174 insertions, 77 deletions
diff --git a/ELF/InputSection.cpp b/ELF/InputSection.cpp index f02106a4c..5bca2aaa8 100644 --- a/ELF/InputSection.cpp +++ b/ELF/InputSection.cpp @@ -56,7 +56,7 @@ ArrayRef<uint8_t> InputSectionBase<ELFT>::getSectionData() const { } template <class ELFT> -typename ELFT::uint InputSectionBase<ELFT>::getOffset(uintX_t Offset) { +typename ELFT::uint InputSectionBase<ELFT>::getOffset(uintX_t Offset) const { switch (SectionKind) { case Regular: return cast<InputSection<ELFT>>(this)->OutSecOff + Offset; @@ -80,7 +80,7 @@ typename ELFT::uint InputSectionBase<ELFT>::getOffset(uintX_t Offset) { template <class ELFT> typename ELFT::uint -InputSectionBase<ELFT>::getOffset(const DefinedRegular<ELFT> &Sym) { +InputSectionBase<ELFT>::getOffset(const DefinedRegular<ELFT> &Sym) const { return getOffset(Sym.Value); } @@ -297,8 +297,8 @@ void InputSectionBase<ELFT>::relocate(uint8_t *Buf, uint8_t *BufEnd) { } const unsigned Bits = sizeof(uintX_t) * 8; - for (const Relocation &Rel : Relocations) { - uintX_t Offset = Rel.Offset; + for (const Relocation<ELFT> &Rel : Relocations) { + uintX_t Offset = Rel.InputSec->getOffset(Rel.Offset); uint8_t *BufLoc = Buf + Offset; uint32_t Type = Rel.Type; uintX_t A = Rel.Addend; @@ -422,13 +422,13 @@ void EhInputSection<ELFT>::split() { } template <class ELFT> -typename ELFT::uint EhInputSection<ELFT>::getOffset(uintX_t Offset) { +typename ELFT::uint EhInputSection<ELFT>::getOffset(uintX_t Offset) const { // The file crtbeginT.o has relocations pointing to the start of an empty // .eh_frame that is known to be the first in the link. It does that to // identify the start of the output .eh_frame. Handle this special case. if (this->getSectionHdr()->sh_size == 0) return Offset; - SectionPiece *Piece = this->getSectionPiece(Offset); + const SectionPiece *Piece = this->getSectionPiece(Offset); if (Piece->OutputOff == size_t(-1)) return -1; // Not in the output @@ -506,6 +506,13 @@ bool MergeInputSection<ELFT>::classof(const InputSectionBase<ELFT> *S) { // Do binary search to get a section piece at a given input offset. template <class ELFT> SectionPiece *SplitInputSection<ELFT>::getSectionPiece(uintX_t Offset) { + auto *This = static_cast<const SplitInputSection<ELFT> *>(this); + return const_cast<SectionPiece *>(This->getSectionPiece(Offset)); +} + +template <class ELFT> +const SectionPiece * +SplitInputSection<ELFT>::getSectionPiece(uintX_t Offset) const { ArrayRef<uint8_t> D = this->getSectionData(); StringRef Data((const char *)D.data(), D.size()); uintX_t Size = Data.size(); @@ -524,14 +531,14 @@ SectionPiece *SplitInputSection<ELFT>::getSectionPiece(uintX_t Offset) { // Because contents of a mergeable section is not contiguous in output, // it is not just an addition to a base output offset. template <class ELFT> -typename ELFT::uint MergeInputSection<ELFT>::getOffset(uintX_t Offset) { +typename ELFT::uint MergeInputSection<ELFT>::getOffset(uintX_t Offset) const { auto It = OffsetMap.find(Offset); if (It != OffsetMap.end()) return It->second; // If Offset is not at beginning of a section piece, it is not in the map. // In that case we need to search from the original section piece vector. - SectionPiece &Piece = *this->getSectionPiece(Offset); + const SectionPiece &Piece = *this->getSectionPiece(Offset); assert(Piece.Live); uintX_t Addend = Offset - Piece.InputOff; return Piece.OutputOff + Addend; diff --git a/ELF/InputSection.h b/ELF/InputSection.h index 51e191da0..38850a1f1 100644 --- a/ELF/InputSection.h +++ b/ELF/InputSection.h @@ -70,16 +70,16 @@ public: StringRef getSectionName() const; const Elf_Shdr *getSectionHdr() const { return Header; } ObjectFile<ELFT> *getFile() const { return File; } - uintX_t getOffset(const DefinedRegular<ELFT> &Sym); + uintX_t getOffset(const DefinedRegular<ELFT> &Sym) const; // Translate an offset in the input section to an offset in the output // section. - uintX_t getOffset(uintX_t Offset); + uintX_t getOffset(uintX_t Offset) const; ArrayRef<uint8_t> getSectionData() const; void relocate(uint8_t *Buf, uint8_t *BufEnd); - std::vector<Relocation> Relocations; + std::vector<Relocation<ELFT>> Relocations; }; template <class ELFT> InputSectionBase<ELFT> InputSectionBase<ELFT>::Discarded; @@ -125,6 +125,7 @@ public: // Returns the SectionPiece at a given input section offset. SectionPiece *getSectionPiece(uintX_t Offset); + const SectionPiece *getSectionPiece(uintX_t Offset) const; }; // This corresponds to a SHF_MERGE section of an input file. @@ -143,7 +144,7 @@ public: // Translate an offset in the input section to an offset // in the output section. - uintX_t getOffset(uintX_t Offset); + uintX_t getOffset(uintX_t Offset) const; void finalizePieces(); @@ -163,7 +164,7 @@ public: // Translate an offset in the input section to an offset in the output // section. - uintX_t getOffset(uintX_t Offset); + uintX_t getOffset(uintX_t Offset) const; // Relocation section that refer to this one. const Elf_Shdr *RelocSection = nullptr; diff --git a/ELF/OutputSections.cpp b/ELF/OutputSections.cpp index f0ebd95cc..efe4dc15d 100644 --- a/ELF/OutputSections.cpp +++ b/ELF/OutputSections.cpp @@ -351,13 +351,11 @@ template <class ELFT> void RelocationSection<ELFT>::writeTo(uint8_t *Buf) { for (const DynamicReloc<ELFT> &Rel : Relocs) { auto *P = reinterpret_cast<Elf_Rela *>(Buf); Buf += Config->Rela ? sizeof(Elf_Rela) : sizeof(Elf_Rel); - SymbolBody *Sym = Rel.Sym; if (Config->Rela) - P->r_addend = Rel.UseSymVA ? Sym->getVA<ELFT>(Rel.Addend) : Rel.Addend; - P->r_offset = Rel.OffsetInSec + Rel.OffsetSec->getVA(); - uint32_t SymIdx = (!Rel.UseSymVA && Sym) ? Sym->DynsymIndex : 0; - P->setSymbolAndType(SymIdx, Rel.Type, Config->Mips64EL); + P->r_addend = Rel.getAddend(); + P->r_offset = Rel.getOffset(); + P->setSymbolAndType(Rel.getSymIndex(), Rel.Type, Config->Mips64EL); } if (Sort) { @@ -836,11 +834,16 @@ static int getPriority(StringRef S) { return V; } -template <class ELFT> -void OutputSection<ELFT>::forEachInputSection( - std::function<void(InputSectionBase<ELFT> *)> F) { - for (InputSection<ELFT> *S : Sections) - F(S); +// This function is called after we sort input sections +// and scan relocations to setup sections' offsets. +template <class ELFT> void OutputSection<ELFT>::assignOffsets() { + uintX_t Off = this->Header.sh_size; + for (InputSection<ELFT> *S : Sections) { + Off = alignTo(Off, S->Alignment); + S->OutSecOff = Off; + Off += S->getSize(); + } + this->Header.sh_size = Off; } // Sorts input sections by section name suffixes, so that .foo.N comes @@ -947,13 +950,6 @@ template <class ELFT> EhOutputSection<ELFT>::EhOutputSection() : OutputSectionBase<ELFT>(".eh_frame", SHT_PROGBITS, SHF_ALLOC) {} -template <class ELFT> -void EhOutputSection<ELFT>::forEachInputSection( - std::function<void(InputSectionBase<ELFT> *)> F) { - for (EhInputSection<ELFT> *S : Sections) - F(S); -} - // Returns the first relocation that points to a region // between Begin and Begin+Size. template <class IntTy, class RelTy> @@ -1269,6 +1265,26 @@ template <class ELFT> void StringTableSection<ELFT>::writeTo(uint8_t *Buf) { } template <class ELFT> +typename ELFT::uint DynamicReloc<ELFT>::getOffset() const { + if (OutputSec) + return OutputSec->getVA() + OffsetInSec; + return InputSec->OutSec->getVA() + InputSec->getOffset(OffsetInSec); +} + +template <class ELFT> +typename ELFT::uint DynamicReloc<ELFT>::getAddend() const { + if (UseSymVA) + return Sym->getVA<ELFT>(Addend); + return Addend; +} + +template <class ELFT> uint32_t DynamicReloc<ELFT>::getSymIndex() const { + if (Sym && !UseSymVA) + return Sym->DynsymIndex; + return 0; +} + +template <class ELFT> SymbolTableSection<ELFT>::SymbolTableSection( StringTableSection<ELFT> &StrTabSec) : OutputSectionBase<ELFT>(StrTabSec.isDynamic() ? ".dynsym" : ".symtab", diff --git a/ELF/OutputSections.h b/ELF/OutputSections.h index 7fe0e9c02..09b7bbe79 100644 --- a/ELF/OutputSections.h +++ b/ELF/OutputSections.h @@ -96,8 +96,7 @@ public: virtual void finalize() {} virtual void finalizePieces() {} - virtual void - forEachInputSection(std::function<void(InputSectionBase<ELFT> *)> F) {} + virtual void assignOffsets() {} virtual void writeTo(uint8_t *Buf) {} virtual ~OutputSectionBase() = default; @@ -197,21 +196,35 @@ private: std::vector<std::pair<const SymbolBody *, unsigned>> Entries; }; -template <class ELFT> struct DynamicReloc { +template <class ELFT> class DynamicReloc { typedef typename ELFT::uint uintX_t; + +public: + DynamicReloc(uint32_t Type, const InputSectionBase<ELFT> *InputSec, + uintX_t OffsetInSec, bool UseSymVA, SymbolBody *Sym, + uintX_t Addend) + : Type(Type), Sym(Sym), InputSec(InputSec), OffsetInSec(OffsetInSec), + UseSymVA(UseSymVA), Addend(Addend) {} + + DynamicReloc(uint32_t Type, const OutputSectionBase<ELFT> *OutputSec, + uintX_t OffsetInSec, bool UseSymVA, SymbolBody *Sym, + uintX_t Addend) + : Type(Type), Sym(Sym), OutputSec(OutputSec), OffsetInSec(OffsetInSec), + UseSymVA(UseSymVA), Addend(Addend) {} + + uintX_t getOffset() const; + uintX_t getAddend() const; + uint32_t getSymIndex() const; + uint32_t Type; +private: SymbolBody *Sym; - const OutputSectionBase<ELFT> *OffsetSec; + const InputSectionBase<ELFT> *InputSec = nullptr; + const OutputSectionBase<ELFT> *OutputSec = nullptr; uintX_t OffsetInSec; bool UseSymVA; uintX_t Addend; - - DynamicReloc(uint32_t Type, const OutputSectionBase<ELFT> *OffsetSec, - uintX_t OffsetInSec, bool UseSymVA, SymbolBody *Sym, - uintX_t Addend) - : Type(Type), Sym(Sym), OffsetSec(OffsetSec), OffsetInSec(OffsetInSec), - UseSymVA(UseSymVA), Addend(Addend) {} }; template <class ELFT> @@ -343,8 +356,7 @@ public: void sortCtorsDtors(); void writeTo(uint8_t *Buf) override; void finalize() override; - void - forEachInputSection(std::function<void(InputSectionBase<ELFT> *)> F) override; + void assignOffsets() override; std::vector<InputSection<ELFT> *> Sections; }; @@ -385,8 +397,6 @@ public: void writeTo(uint8_t *Buf) override; void finalize() override; bool empty() const { return Sections.empty(); } - void - forEachInputSection(std::function<void(InputSectionBase<ELFT> *)> F) override; void addSection(InputSectionBase<ELFT> *S) override; diff --git a/ELF/Relocations.cpp b/ELF/Relocations.cpp index 87f764995..5cf0a61c0 100644 --- a/ELF/Relocations.cpp +++ b/ELF/Relocations.cpp @@ -103,7 +103,7 @@ static unsigned handleTlsRelocation(uint32_t Type, SymbolBody &Body, {Target->TlsDescRel, Out<ELFT>::Got, Off, false, &Body, 0}); } if (Expr != R_HINT) - C.Relocations.push_back({Expr, Type, Offset, Addend, &Body}); + C.Relocations.push_back({Expr, Type, &C, Offset, Addend, &Body}); return 1; } @@ -111,21 +111,21 @@ static unsigned handleTlsRelocation(uint32_t Type, SymbolBody &Body, // Local-Dynamic relocs can be relaxed to Local-Exec. if (!Config->Shared) { C.Relocations.push_back( - {R_RELAX_TLS_LD_TO_LE, Type, Offset, Addend, &Body}); + {R_RELAX_TLS_LD_TO_LE, Type, &C, Offset, Addend, &Body}); return 2; } if (Out<ELFT>::Got->addTlsIndex()) Out<ELFT>::RelaDyn->addReloc({Target->TlsModuleIndexRel, Out<ELFT>::Got, Out<ELFT>::Got->getTlsIndexOff(), false, nullptr, 0}); - C.Relocations.push_back({Expr, Type, Offset, Addend, &Body}); + C.Relocations.push_back({Expr, Type, &C, Offset, Addend, &Body}); return 1; } // Local-Dynamic relocs can be relaxed to Local-Exec. if (Target->isTlsLocalDynamicRel(Type) && !Config->Shared) { C.Relocations.push_back( - {R_RELAX_TLS_LD_TO_LE, Type, Offset, Addend, &Body}); + {R_RELAX_TLS_LD_TO_LE, Type, &C, Offset, Addend, &Body}); return 1; } @@ -144,7 +144,7 @@ static unsigned handleTlsRelocation(uint32_t Type, SymbolBody &Body, Off + (uintX_t)sizeof(uintX_t), false, &Body, 0}); } - C.Relocations.push_back({Expr, Type, Offset, Addend, &Body}); + C.Relocations.push_back({Expr, Type, &C, Offset, Addend, &Body}); return 1; } @@ -153,7 +153,7 @@ static unsigned handleTlsRelocation(uint32_t Type, SymbolBody &Body, if (isPreemptible(Body, Type)) { C.Relocations.push_back( {Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_GD_TO_IE), Type, - Offset, Addend, &Body}); + &C, Offset, Addend, &Body}); if (!Body.isInGot()) { Out<ELFT>::Got->addEntry(Body); Out<ELFT>::RelaDyn->addReloc({Target->TlsGotRel, Out<ELFT>::Got, @@ -163,7 +163,7 @@ static unsigned handleTlsRelocation(uint32_t Type, SymbolBody &Body, return Target->TlsGdRelaxSkip; } C.Relocations.push_back( - {Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_GD_TO_LE), Type, + {Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_GD_TO_LE), Type, &C, Offset, Addend, &Body}); return Target->TlsGdRelaxSkip; } @@ -173,7 +173,7 @@ static unsigned handleTlsRelocation(uint32_t Type, SymbolBody &Body, if (Target->isTlsInitialExecRel(Type) && !Config->Shared && !isPreemptible(Body, Type)) { C.Relocations.push_back( - {R_RELAX_TLS_IE_TO_LE, Type, Offset, Addend, &Body}); + {R_RELAX_TLS_IE_TO_LE, Type, &C, Offset, Addend, &Body}); return 1; } return 0; @@ -497,8 +497,9 @@ static void scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) { if (HasError) continue; - uintX_t Offset = C.getOffset(RI.r_offset); - if (Offset == (uintX_t)-1) + // Skip a relocation that points to a dead piece + // in a mergeable section. + if (C.getOffset(RI.r_offset) == (uintX_t)-1) continue; // This relocation does not require got entry, but it is relative to got and @@ -508,8 +509,8 @@ static void scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) { uintX_t Addend = computeAddend(File, Buf, E, RI, Expr, Body); - if (unsigned Processed = - handleTlsRelocation<ELFT>(Type, Body, C, Offset, Addend, Expr)) { + if (unsigned Processed = handleTlsRelocation<ELFT>( + Type, Body, C, RI.r_offset, Addend, Expr)) { I += (Processed - 1); continue; } @@ -529,17 +530,17 @@ static void scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) { // relocation. We can process some of it and and just ask the dynamic // linker to add the load address. if (!Constant) - AddDyn({Target->RelativeRel, C.OutSec, Offset, true, &Body, Addend}); + AddDyn({Target->RelativeRel, &C, RI.r_offset, true, &Body, Addend}); // If the produced value is a constant, we just remember to write it // when outputting this section. We also have to do it if the format // uses Elf_Rel, since in that case the written value is the addend. if (Constant || !RelTy::IsRela) - C.Relocations.push_back({Expr, Type, Offset, Addend, &Body}); + C.Relocations.push_back({Expr, Type, &C, RI.r_offset, Addend, &Body}); } else { // We don't know anything about the finaly symbol. Just ask the dynamic // linker to handle the relocation for us. - AddDyn({Target->getDynRel(Type), C.OutSec, Offset, false, &Body, Addend}); + AddDyn({Target->getDynRel(Type), &C, RI.r_offset, false, &Body, Addend}); // MIPS ABI turns using of GOT and dynamic relocations inside out. // While regular ABI uses dynamic relocations to fill up GOT entries // MIPS ABI requires dynamic linker to fills up GOT entries using diff --git a/ELF/Relocations.h b/ELF/Relocations.h index 56c14c115..dab60b449 100644 --- a/ELF/Relocations.h +++ b/ELF/Relocations.h @@ -60,9 +60,10 @@ enum RelExpr { R_TLSLD_PC }; -struct Relocation { +template <class ELFT> struct Relocation { RelExpr Expr; uint32_t Type; + InputSectionBase<ELFT> *InputSec; uint64_t Offset; uint64_t Addend; SymbolBody *Sym; diff --git a/ELF/Writer.cpp b/ELF/Writer.cpp index 6e20305b4..98752699b 100644 --- a/ELF/Writer.cpp +++ b/ELF/Writer.cpp @@ -793,24 +793,24 @@ template <class ELFT> void Writer<ELFT>::createSections() { // Scan relocations. This must be done after every symbol is declared so that // we can correctly decide if a dynamic relocation is needed. - for (OutputSectionBase<ELFT> *Sec : OutputSections) { - Sec->forEachInputSection([&](InputSectionBase<ELFT> *S) { - if (auto *IS = dyn_cast<InputSection<ELFT>>(S)) { - // Set OutSecOff so that scanRelocations can use it. - uintX_t Off = alignTo(Sec->getSize(), S->Alignment); - IS->OutSecOff = Off; - - scanRelocations(*IS); - - // Now that scan relocs possibly changed the size, update the offset. - Sec->setSize(Off + S->getSize()); - } else if (auto *EH = dyn_cast<EhInputSection<ELFT>>(S)) { - if (EH->RelocSection) - scanRelocations(*EH, *EH->RelocSection); + for (const std::unique_ptr<elf::ObjectFile<ELFT>> &F : + Symtab.getObjectFiles()) { + for (InputSectionBase<ELFT> *C : F->getSections()) { + if (isDiscarded(C)) + continue; + if (auto *S = dyn_cast<InputSection<ELFT>>(C)) { + scanRelocations(*S); + continue; } - }); + if (auto *S = dyn_cast<EhInputSection<ELFT>>(C)) + if (S->RelocSection) + scanRelocations(*S, *S->RelocSection); + } } + for (OutputSectionBase<ELFT> *Sec : OutputSections) + 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; diff --git a/test/ELF/mips-npic-call-pic.s b/test/ELF/mips-npic-call-pic.s index 755963cfc..dbf053570 100644 --- a/test/ELF/mips-npic-call-pic.s +++ b/test/ELF/mips-npic-call-pic.s @@ -1,3 +1,4 @@ +# REQUIRES: mips # Check LA25 stubs creation. This stub code is necessary when # non-PIC code calls PIC function. @@ -7,8 +8,6 @@ # RUN: ld.lld %t-npic.o %t-pic.o %p/Inputs/mips-sto-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 0e jal 131128 <foo1b+0x4> @@ -72,6 +71,68 @@ # CHECK-NEXT: 200a4: 08 00 80 20 j 131200 <fpic> # CHECK-NEXT: 200a8: 27 39 00 80 addiu $25, $25, 128 +# Make sure tha thunks are created properly no matter how +# objects are laid out. +# +# RUN: ld.lld %t-pic.o %t-npic.o %p/Inputs/mips-sto-pic.o -o %t.exe +# RUN: llvm-objdump -d %t.exe | FileCheck -check-prefix=REVERSE %s + +# REVERSE: foo1a: +# REVERSE-NEXT: 20000: 00 00 00 00 nop +# +# REVERSE: foo1b: +# REVERSE-NEXT: 20004: 00 00 00 00 nop +# REVERSE-NEXT: 20008: 3c 19 00 02 lui $25, 2 +# REVERSE-NEXT: 2000c: 08 00 80 00 j 131072 <foo1a> +# REVERSE-NEXT: 20010: 27 39 00 00 addiu $25, $25, 0 +# REVERSE-NEXT: 20014: 00 00 00 00 nop +# REVERSE-NEXT: 20018: 3c 19 00 02 lui $25, 2 +# REVERSE-NEXT: 2001c: 08 00 80 01 j 131076 <foo1b> +# REVERSE-NEXT: 20020: 27 39 00 04 addiu $25, $25, 4 +# REVERSE-NEXT: 20024: 00 00 00 00 nop +# REVERSE-NEXT: 20028: 00 00 00 00 nop +# REVERSE-NEXT: 2002c: 00 00 00 00 nop +# +# REVERSE: foo2: +# REVERSE-NEXT: 20030: 00 00 00 00 nop +# REVERSE-NEXT: 20034: 3c 19 00 02 lui $25, 2 +# REVERSE-NEXT: 20038: 08 00 80 0c j 131120 <foo2> +# REVERSE-NEXT: 2003c: 27 39 00 30 addiu $25, $25, 48 +# REVERSE-NEXT: 20040: 00 00 00 00 nop +# REVERSE-NEXT: 20044: 00 00 00 00 nop +# REVERSE-NEXT: 20048: 00 00 00 00 nop +# REVERSE-NEXT: 2004c: 00 00 00 00 nop +# +# REVERSE: __start: +# REVERSE-NEXT: 20050: 0c 00 80 02 jal 131080 <foo1b+0x4> +# REVERSE-NEXT: 20054: 00 00 00 00 nop +# REVERSE-NEXT: 20058: 0c 00 80 0d jal 131124 <foo2+0x4> +# REVERSE-NEXT: 2005c: 00 00 00 00 nop +# REVERSE-NEXT: 20060: 0c 00 80 06 jal 131096 <foo1b+0x14> +# REVERSE-NEXT: 20064: 00 00 00 00 nop +# REVERSE-NEXT: 20068: 0c 00 80 0d jal 131124 <foo2+0x4> +# REVERSE-NEXT: 2006c: 00 00 00 00 nop +# REVERSE-NEXT: 20070: 0c 00 80 28 jal 131232 <fnpic+0x10> +# REVERSE-NEXT: 20074: 00 00 00 00 nop +# REVERSE-NEXT: 20078: 0c 00 80 24 jal 131216 <fnpic> +# REVERSE-NEXT: 2007c: 00 00 00 00 nop +# +# REVERSE: fpic: +# REVERSE-NEXT: 20080: 00 00 00 00 nop +# REVERSE-NEXT: 20084: 00 00 00 00 nop +# REVERSE-NEXT: 20088: 00 00 00 00 nop +# REVERSE-NEXT: 2008c: 00 00 00 00 nop +# +# REVERSE: fnpic: +# REVERSE-NEXT: 20090: 00 00 00 00 nop +# REVERSE-NEXT: 20094: 00 00 00 00 nop +# REVERSE-NEXT: 20098: 00 00 00 00 nop +# REVERSE-NEXT: 2009c: 00 00 00 00 nop +# REVERSE-NEXT: 200a0: 3c 19 00 02 lui $25, 2 +# REVERSE-NEXT: 200a4: 08 00 80 20 j 131200 <fpic> +# REVERSE-NEXT: 200a8: 27 39 00 80 addiu $25, $25, 128 +# REVERSE-NEXT: 200ac: 00 00 00 00 nop + .text .globl __start __start: |