aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRui Ueyama <ruiu@google.com>2016-06-23 04:33:42 +0000
committerRui Ueyama <ruiu@google.com>2016-06-23 04:33:42 +0000
commit1c48a2e94ee309c66ab6375c0c24bd1ce2e42016 (patch)
treebf460c24e107c523233d3ddfe0ed12c40277dc73
parentffecc7071c1e35b3c74abfd97d118ee61099f2be (diff)
Fix a bug that MIPS thunks can overwrite other section contents.
Peter Smith found while trying to support thunk creation for ARM that LLD sometimes creates broken thunks for MIPS. The cause of the bug is that we assign file offsets to input sections too early. We need to create all sections and then assign section offsets because appending thunks changes file offsets for all following sections. This patch separates the pass to assign file offsets from thunk creation pass. This effectively reverts r265673. Differential Revision: http://reviews.llvm.org/D21598 git-svn-id: https://llvm.org/svn/llvm-project/lld/trunk@273532 91177308-0d34-0410-b5e6-96231b3b80d8
-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: