aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ELF/InputSection.cpp23
-rw-r--r--ELF/InputSection.h11
-rw-r--r--ELF/OutputSections.cpp50
-rw-r--r--ELF/OutputSections.h38
-rw-r--r--ELF/Relocations.cpp31
-rw-r--r--ELF/Relocations.h3
-rw-r--r--ELF/Writer.cpp30
-rw-r--r--test/ELF/mips-npic-call-pic.s65
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: