summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Dunbar <daniel@zuster.org>2009-08-26 13:58:10 +0000
committerDaniel Dunbar <daniel@zuster.org>2009-08-26 13:58:10 +0000
commit3f6a960f9c9ad27f2ac573020df414e8b8cdda04 (patch)
tree0207ce4dc49bf5e6867099a361ccb91e3dd56273
parentbe9635569401b9a40984c02c6e171aa9da9ad0a2 (diff)
downloadllvm-3f6a960f9c9ad27f2ac573020df414e8b8cdda04.tar.gz
llvm-3f6a960f9c9ad27f2ac573020df414e8b8cdda04.tar.bz2
llvm-3f6a960f9c9ad27f2ac573020df414e8b8cdda04.tar.xz
llvm-mc/Mach-O: Add support for relocations.
- I haven't really tried to find the "right" way to store the fixups or apply them, yet. This works, but isn't particularly elegant or fast. - Still no evaluation support, so we don't actually ever not turn a fixup into a relocation entry. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@80089 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/llvm/MC/MCAssembler.h68
-rw-r--r--lib/MC/MCAssembler.cpp238
-rw-r--r--test/MC/MachO/reloc.s227
3 files changed, 520 insertions, 13 deletions
diff --git a/include/llvm/MC/MCAssembler.h b/include/llvm/MC/MCAssembler.h
index 74a4748658..dc3f5c0f58 100644
--- a/include/llvm/MC/MCAssembler.h
+++ b/include/llvm/MC/MCAssembler.h
@@ -246,11 +246,45 @@ class MCSectionData : public ilist_node<MCSectionData> {
void operator=(const MCSectionData&); // DO NOT IMPLEMENT
public:
+ /// Fixup - Represent a fixed size region of bytes inside some fragment which
+ /// needs to be rewritten. This region will either be rewritten by the
+ /// assembler or cause a relocation entry to be generated.
+ struct Fixup {
+ /// Fragment - The fragment containing the fixup.
+ MCFragment *Fragment;
+
+ /// Offset - The offset inside the fragment which needs to be rewritten.
+ uint64_t Offset;
+
+ /// Value - The expression to eventually write into the fragment.
+ //
+ // FIXME: We could probably get away with requiring the client to pass in an
+ // owned reference whose lifetime extends past that of the fixup.
+ MCValue Value;
+
+ /// Size - The fixup size.
+ unsigned Size;
+
+ /// FixedValue - The value to replace the fix up by.
+ //
+ // FIXME: This should not be here.
+ uint64_t FixedValue;
+
+ public:
+ Fixup(MCFragment &_Fragment, uint64_t _Offset, const MCValue &_Value,
+ unsigned _Size)
+ : Fragment(&_Fragment), Offset(_Offset), Value(_Value), Size(_Size),
+ FixedValue(0) {}
+ };
+
typedef iplist<MCFragment> FragmentListType;
typedef FragmentListType::const_iterator const_iterator;
typedef FragmentListType::iterator iterator;
+ typedef std::vector<Fixup>::const_iterator const_fixup_iterator;
+ typedef std::vector<Fixup>::iterator fixup_iterator;
+
private:
iplist<MCFragment> Fragments;
const MCSection &Section;
@@ -274,6 +308,12 @@ private:
/// initialized.
uint64_t FileSize;
+ /// LastFixupLookup - Cache for the last looked up fixup.
+ mutable unsigned LastFixupLookup;
+
+ /// Fixups - The list of fixups in this section.
+ std::vector<Fixup> Fixups;
+
/// @}
public:
@@ -303,11 +343,39 @@ public:
bool empty() const { return Fragments.empty(); }
/// @}
+ /// @name Fixup Access
+ /// @{
+
+ std::vector<Fixup> &getFixups() {
+ return Fixups;
+ }
+
+ fixup_iterator fixup_begin() {
+ return Fixups.begin();
+ }
+
+ fixup_iterator fixup_end() {
+ return Fixups.end();
+ }
+
+ size_t fixup_size() const { return Fixups.size(); }
+
+ /// @}
/// @name Assembler Backend Support
/// @{
//
// FIXME: This could all be kept private to the assembler implementation.
+ /// LookupFixup - Look up the fixup for the given \arg Fragment and \arg
+ /// Offset.
+ ///
+ /// If multiple fixups exist for the same fragment and offset it is undefined
+ /// which one is returned.
+ //
+ // FIXME: This isn't horribly slow in practice, but there are much nicer
+ // solutions to applying the fixups.
+ const Fixup *LookupFixup(const MCFragment *Fragment, uint64_t Offset) const;
+
uint64_t getAddress() const {
assert(Address != ~UINT64_C(0) && "Address not set!");
return Address;
diff --git a/lib/MC/MCAssembler.cpp b/lib/MC/MCAssembler.cpp
index a9bbfa17e1..1f62b06052 100644
--- a/lib/MC/MCAssembler.cpp
+++ b/lib/MC/MCAssembler.cpp
@@ -42,6 +42,7 @@ class MachObjectWriter {
static const unsigned SymtabLoadCommandSize = 24;
static const unsigned DysymtabLoadCommandSize = 80;
static const unsigned Nlist32Size = 12;
+ static const unsigned RelocationInfoSize = 8;
enum HeaderFileType {
HFT_Object = 0x1
@@ -77,6 +78,19 @@ class MachObjectWriter {
ISF_Absolute = 0x40000000
};
+ /// RelocationFlags - Special flags for addresses.
+ enum RelocationFlags {
+ RF_Scattered = 0x80000000
+ };
+
+ enum RelocationInfoType {
+ RIT_Vanilla = 0,
+ RIT_Pair = 1,
+ RIT_Difference = 2,
+ RIT_PreboundLazyPointer = 3,
+ RIT_LocalDifference = 4
+ };
+
/// MachSymbolData - Helper struct for containing some precomputed information
/// on symbols.
struct MachSymbolData {
@@ -205,7 +219,8 @@ public:
assert(OS.tell() - Start == SegmentLoadCommand32Size);
}
- void WriteSection32(const MCSectionData &SD, uint64_t FileOffset) {
+ void WriteSection32(const MCSectionData &SD, uint64_t FileOffset,
+ uint64_t RelocationsStart, unsigned NumRelocations) {
// struct section (68 bytes)
uint64_t Start = OS.tell();
@@ -222,8 +237,8 @@ public:
assert(isPowerOf2_32(SD.getAlignment()) && "Invalid alignment!");
Write32(Log2_32(SD.getAlignment()));
- Write32(0); // file offset of relocation entries
- Write32(0); // number of relocation entrions
+ Write32(NumRelocations ? RelocationsStart : 0);
+ Write32(NumRelocations);
Write32(Section.getTypeAndAttributes());
Write32(0); // reserved1
Write32(Section.getStubSize()); // reserved2
@@ -332,6 +347,126 @@ public:
Write32(Address);
}
+ struct MachRelocationEntry {
+ uint32_t Word0;
+ uint32_t Word1;
+ };
+ void ComputeScatteredRelocationInfo(MCAssembler &Asm,
+ MCSectionData::Fixup &Fixup,
+ DenseMap<const MCSymbol*,MCSymbolData*> &SymbolMap,
+ std::vector<MachRelocationEntry> &Relocs) {
+ uint32_t Address = Fixup.Fragment->getOffset() + Fixup.Offset;
+ unsigned IsPCRel = 0;
+ unsigned Type = RIT_Vanilla;
+
+ // See <reloc.h>.
+
+ const MCSymbol *A = Fixup.Value.getSymA();
+ MCSymbolData *SD = SymbolMap.lookup(A);
+ uint32_t Value = SD->getFragment()->getAddress() + SD->getOffset();
+ uint32_t Value2 = 0;
+
+ if (const MCSymbol *B = Fixup.Value.getSymB()) {
+ Type = RIT_LocalDifference;
+
+ MCSymbolData *SD = SymbolMap.lookup(B);
+ Value2 = SD->getFragment()->getAddress() + SD->getOffset();
+ }
+
+ unsigned Log2Size = Log2_32(Fixup.Size);
+ assert((1U << Log2Size) == Fixup.Size && "Invalid fixup size!");
+
+ // The value which goes in the fixup is current value of the expression.
+ Fixup.FixedValue = Value - Value2 + Fixup.Value.getConstant();
+
+ MachRelocationEntry MRE;
+ MRE.Word0 = ((Address << 0) |
+ (Type << 24) |
+ (Log2Size << 28) |
+ (IsPCRel << 30) |
+ RF_Scattered);
+ MRE.Word1 = Value;
+ Relocs.push_back(MRE);
+
+ if (Type == RIT_LocalDifference) {
+ Type = RIT_Pair;
+
+ MachRelocationEntry MRE;
+ MRE.Word0 = ((0 << 0) |
+ (Type << 24) |
+ (Log2Size << 28) |
+ (0 << 30) |
+ RF_Scattered);
+ MRE.Word1 = Value2;
+ Relocs.push_back(MRE);
+ }
+ }
+
+ void ComputeRelocationInfo(MCAssembler &Asm,
+ MCSectionData::Fixup &Fixup,
+ DenseMap<const MCSymbol*,MCSymbolData*> &SymbolMap,
+ std::vector<MachRelocationEntry> &Relocs) {
+ // If this is a local symbol plus an offset or a difference, then we need a
+ // scattered relocation entry.
+ if (Fixup.Value.getSymB()) // a - b
+ return ComputeScatteredRelocationInfo(Asm, Fixup, SymbolMap, Relocs);
+ if (Fixup.Value.getSymA() && Fixup.Value.getConstant())
+ if (!Fixup.Value.getSymA()->isUndefined())
+ return ComputeScatteredRelocationInfo(Asm, Fixup, SymbolMap, Relocs);
+
+ // See <reloc.h>.
+ uint32_t Address = Fixup.Fragment->getOffset() + Fixup.Offset;
+ uint32_t Value = 0;
+ unsigned Index = 0;
+ unsigned IsPCRel = 0;
+ unsigned IsExtern = 0;
+ unsigned Type = 0;
+
+ if (Fixup.Value.isAbsolute()) { // constant
+ // SymbolNum of 0 indicates the absolute section.
+ Type = RIT_Vanilla;
+ Value = 0;
+ llvm_unreachable("FIXME: Not yet implemented!");
+ } else {
+ const MCSymbol *Symbol = Fixup.Value.getSymA();
+ MCSymbolData *SD = SymbolMap.lookup(Symbol);
+
+ if (Symbol->isUndefined()) {
+ IsExtern = 1;
+ Index = SD->getIndex();
+ Value = 0;
+ } else {
+ // The index is the section ordinal.
+ //
+ // FIXME: O(N)
+ Index = 1;
+ for (MCAssembler::iterator it = Asm.begin(),
+ ie = Asm.end(); it != ie; ++it, ++Index)
+ if (&*it == SD->getFragment()->getParent())
+ break;
+ Value = SD->getFragment()->getAddress() + SD->getOffset();
+ }
+
+ Type = RIT_Vanilla;
+ }
+
+ // The value which goes in the fixup is current value of the expression.
+ Fixup.FixedValue = Value + Fixup.Value.getConstant();
+
+ unsigned Log2Size = Log2_32(Fixup.Size);
+ assert((1U << Log2Size) == Fixup.Size && "Invalid fixup size!");
+
+ // struct relocation_info (8 bytes)
+ MachRelocationEntry MRE;
+ MRE.Word0 = Address;
+ MRE.Word1 = ((Index << 0) |
+ (IsPCRel << 24) |
+ (Log2Size << 25) |
+ (IsExtern << 27) |
+ (Type << 28));
+ Relocs.push_back(MRE);
+ }
+
void BindIndirectSymbols(MCAssembler &Asm,
DenseMap<const MCSymbol*,MCSymbolData*> &SymbolMap) {
// This is the point where 'as' creates actual symbols for indirect symbols
@@ -543,9 +678,31 @@ public:
WriteSegmentLoadCommand32(NumSections, SectionDataStart, SectionDataSize);
// ... and then the section headers.
- for (MCAssembler::iterator it = Asm.begin(), ie = Asm.end(); it != ie; ++it)
- WriteSection32(*it, SectionDataStart + it->getAddress());
-
+ //
+ // We also compute the section relocations while we do this. Note that
+ // compute relocation info will also update the fixup to have the correct
+ // value; this will be overwrite the appropriate data in the fragment when
+ // it is written.
+ std::vector<MachRelocationEntry> RelocInfos;
+ uint64_t RelocTableEnd = SectionDataEnd;
+ for (MCAssembler::iterator it = Asm.begin(), ie = Asm.end(); it != ie;
+ ++it) {
+ MCSectionData &SD = *it;
+
+ // The assembler writes relocations in the reverse order they were seen.
+ //
+ // FIXME: It is probably more complicated than this.
+ unsigned NumRelocsStart = RelocInfos.size();
+ for (unsigned i = 0, e = SD.fixup_size(); i != e; ++i)
+ ComputeRelocationInfo(Asm, SD.getFixups()[e - i - 1], SymbolMap,
+ RelocInfos);
+
+ unsigned NumRelocs = RelocInfos.size() - NumRelocsStart;
+ uint64_t SectionStart = SectionDataStart + SD.getAddress();
+ WriteSection32(SD, SectionStart, RelocTableEnd, NumRelocs);
+ RelocTableEnd += NumRelocs * RelocationInfoSize;
+ }
+
// Write the symbol table load command, if used.
if (NumSymbols) {
unsigned FirstLocalSymbol = 0;
@@ -562,10 +719,10 @@ public:
// If used, the indirect symbols are written after the section data.
if (NumIndirectSymbols)
- IndirectSymbolOffset = SectionDataEnd;
+ IndirectSymbolOffset = RelocTableEnd;
// The symbol table is written after the indirect symbol data.
- uint64_t SymbolTableOffset = SectionDataEnd + IndirectSymbolSize;
+ uint64_t SymbolTableOffset = RelocTableEnd + IndirectSymbolSize;
// The string table is written after symbol table.
uint64_t StringTableOffset =
@@ -583,6 +740,12 @@ public:
for (MCAssembler::iterator it = Asm.begin(), ie = Asm.end(); it != ie; ++it)
WriteFileData(OS, *it, *this);
+ // Write the relocation entries.
+ for (unsigned i = 0, e = RelocInfos.size(); i != e; ++i) {
+ Write32(RelocInfos[i].Word0);
+ Write32(RelocInfos[i].Word1);
+ }
+
// Write the symbol table data, if used.
if (NumSymbols) {
// Write the indirect symbol entries.
@@ -657,12 +820,35 @@ MCSectionData::MCSectionData(const MCSection &_Section, MCAssembler *A)
Alignment(1),
Address(~UINT64_C(0)),
Size(~UINT64_C(0)),
- FileSize(~UINT64_C(0))
+ FileSize(~UINT64_C(0)),
+ LastFixupLookup(~0)
{
if (A)
A->getSectionList().push_back(this);
}
+const MCSectionData::Fixup *
+MCSectionData::LookupFixup(const MCFragment *Fragment, uint64_t Offset) const {
+ // Use a one level cache to turn the common case of accessing the fixups in
+ // order into O(1) instead of O(N).
+ unsigned i = LastFixupLookup, Count = Fixups.size(), End = Fixups.size();
+ if (i >= End)
+ i = 0;
+ while (Count--) {
+ const Fixup &F = Fixups[i];
+ if (F.Fragment == Fragment && F.Offset == Offset) {
+ LastFixupLookup = i;
+ return &F;
+ }
+
+ ++i;
+ if (i == End)
+ i = 0;
+ }
+
+ return 0;
+}
+
/* *** */
MCSymbolData::MCSymbolData() : Symbol(*(MCSymbol*)0) {}
@@ -705,10 +891,27 @@ void MCAssembler::LayoutSection(MCSectionData &SD, unsigned NextAlign) {
}
case MCFragment::FT_Data:
- case MCFragment::FT_Fill:
F.setFileSize(F.getMaxFileSize());
break;
+ case MCFragment::FT_Fill: {
+ MCFillFragment &FF = cast<MCFillFragment>(F);
+
+ F.setFileSize(F.getMaxFileSize());
+
+ // If the fill value is constant, thats it.
+ if (FF.getValue().isAbsolute())
+ break;
+
+ // Otherwise, add fixups for the values.
+ for (uint64_t i = 0, e = FF.getCount(); i != e; ++i) {
+ MCSectionData::Fixup Fix(F, i * FF.getValueSize(),
+ FF.getValue(),FF.getValueSize());
+ SD.getFixups().push_back(Fix);
+ }
+ break;
+ }
+
case MCFragment::FT_Org: {
MCOrgFragment &OF = cast<MCOrgFragment>(F);
@@ -778,11 +981,20 @@ static void WriteFileData(raw_ostream &OS, const MCFragment &F,
case MCFragment::FT_Fill: {
MCFillFragment &FF = cast<MCFillFragment>(F);
+ int64_t Value = 0;
if (!FF.getValue().isAbsolute())
- llvm_unreachable("FIXME: Not yet implemented!");
- int64_t Value = FF.getValue().getConstant();
-
+ Value = FF.getValue().getConstant();
for (uint64_t i = 0, e = FF.getCount(); i != e; ++i) {
+ if (!FF.getValue().isAbsolute()) {
+ // Find the fixup.
+ //
+ // FIXME: Find a better way to write in the fixes.
+ const MCSectionData::Fixup *Fixup =
+ F.getParent()->LookupFixup(&F, i * FF.getValueSize());
+ assert(Fixup && "Missing fixup for fill value!");
+ Value = Fixup->FixedValue;
+ }
+
switch (FF.getValueSize()) {
default:
assert(0 && "Invalid size!");
diff --git a/test/MC/MachO/reloc.s b/test/MC/MachO/reloc.s
new file mode 100644
index 0000000000..e86ed8c6de
--- /dev/null
+++ b/test/MC/MachO/reloc.s
@@ -0,0 +1,227 @@
+// RUN: llvm-mc -triple i386-apple-darwin9 %s -filetype=obj -o - | macho-dump --dump-section-data | FileCheck %s
+
+ .data
+ .long undef
+ .long (undef + 4)
+
+ .globl local_a_ext
+local_a_ext:
+ .long local_a_ext
+
+local_a:
+ .long 0
+local_a_elt:
+ .long 0
+local_b:
+ .long local_b - local_c + 245
+ .long 0
+local_c:
+ .long 0
+
+
+ .long local_a_elt + 1
+ .long local_a_elt + 10
+ .short local_a_elt + 20
+ .byte local_a_elt + 89
+
+ .const
+
+ .long
+bar:
+ .long local_a_elt - bar + 33
+
+// CHECK: ('cputype', 7)
+// CHECK: ('cpusubtype', 3)
+// CHECK: ('filetype', 1)
+// CHECK: ('num_load_commands', 1)
+// CHECK: ('load_commands_size', 364)
+// CHECK: ('flag', 0)
+// CHECK: ('load_commands', [
+// CHECK: # Load Command 0
+// CHECK: (('command', 1)
+// CHECK: ('size', 260)
+// CHECK: ('segment_name', '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
+// CHECK: ('vm_addr', 0)
+// CHECK: ('vm_size', 47)
+// CHECK: ('file_offset', 392)
+// CHECK: ('file_size', 47)
+// CHECK: ('maxprot', 7)
+// CHECK: ('initprot', 7)
+// CHECK: ('num_sections', 3)
+// CHECK: ('flags', 0)
+// CHECK: ('sections', [
+// CHECK: # Section 0
+// CHECK: (('section_name', '__text\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
+// CHECK: ('segment_name', '__TEXT\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
+// CHECK: ('address', 0)
+// CHECK: ('size', 0)
+// CHECK: ('offset', 392)
+// CHECK: ('alignment', 0)
+// CHECK: ('reloc_offset', 0)
+// CHECK: ('num_reloc', 0)
+// CHECK: ('flags', 0x80000000)
+// CHECK: ('reserved1', 0)
+// CHECK: ('reserved2', 0)
+// CHECK: ),
+// CHECK: ('_relocations', [
+// CHECK: ])
+// CHECK: ('_section_data', '')
+// CHECK: # Section 1
+// CHECK: (('section_name', '__data\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
+// CHECK: ('segment_name', '__DATA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
+// CHECK: ('address', 0)
+// CHECK: ('size', 43)
+// CHECK: ('offset', 392)
+// CHECK: ('alignment', 0)
+// CHECK: ('reloc_offset', 440)
+// CHECK: ('num_reloc', 9)
+// CHECK: ('flags', 0x0)
+// CHECK: ('reserved1', 0)
+// CHECK: ('reserved2', 0)
+// CHECK: ),
+// CHECK: ('_relocations', [
+// CHECK: # Relocation 0
+// CHECK: (('word-0', 0x8000002a),
+// CHECK: ('word-1', 0x10)),
+// CHECK: # Relocation 1
+// CHECK: (('word-0', 0x90000028),
+// CHECK: ('word-1', 0x10)),
+// CHECK: # Relocation 2
+// CHECK: (('word-0', 0xa0000024),
+// CHECK: ('word-1', 0x10)),
+// CHECK: # Relocation 3
+// CHECK: (('word-0', 0xa0000020),
+// CHECK: ('word-1', 0x10)),
+// CHECK: # Relocation 4
+// CHECK: (('word-0', 0xa4000014),
+// CHECK: ('word-1', 0x14)),
+// CHECK: # Relocation 5
+// CHECK: (('word-0', 0xa1000000),
+// CHECK: ('word-1', 0x1c)),
+// CHECK: # Relocation 6
+// CHECK: (('word-0', 0x8),
+// CHECK: ('word-1', 0x4000002)),
+// CHECK: # Relocation 7
+// CHECK: (('word-0', 0x4),
+// CHECK: ('word-1', 0xc000006)),
+// CHECK: # Relocation 8
+// CHECK: (('word-0', 0x0),
+// CHECK: ('word-1', 0xc000006)),
+// CHECK: ])
+// CHECK: ('_section_data', '\x00\x00\x00\x00\x04\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xed\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x1a\x00\x00\x00$\x00i')
+// CHECK: # Section 2
+// CHECK: (('section_name', '__const\x00\x00\x00\x00\x00\x00\x00\x00\x00')
+// CHECK: ('segment_name', '__TEXT\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
+// CHECK: ('address', 43)
+// CHECK: ('size', 4)
+// CHECK: ('offset', 435)
+// CHECK: ('alignment', 0)
+// CHECK: ('reloc_offset', 512)
+// CHECK: ('num_reloc', 2)
+// CHECK: ('flags', 0x0)
+// CHECK: ('reserved1', 0)
+// CHECK: ('reserved2', 0)
+// CHECK: ),
+// CHECK: ('_relocations', [
+// CHECK: # Relocation 0
+// CHECK: (('word-0', 0xa4000000),
+// CHECK: ('word-1', 0x10)),
+// CHECK: # Relocation 1
+// CHECK: (('word-0', 0xa1000000),
+// CHECK: ('word-1', 0x2b)),
+// CHECK: ])
+// CHECK: ('_section_data', '\x06\x00\x00\x00')
+// CHECK: ])
+// CHECK: ),
+// CHECK: # Load Command 1
+// CHECK: (('command', 2)
+// CHECK: ('size', 24)
+// CHECK: ('symoff', 528)
+// CHECK: ('nsyms', 7)
+// CHECK: ('stroff', 612)
+// CHECK: ('strsize', 60)
+// CHECK: ('_string_data', '\x00undef\x00local_a_ext\x00local_a\x00local_a_elt\x00local_b\x00local_c\x00bar\x00\x00')
+// CHECK: ('_symbols', [
+// CHECK: # Symbol 0
+// CHECK: (('n_strx', 19)
+// CHECK: ('n_type', 0xe)
+// CHECK: ('n_sect', 2)
+// CHECK: ('n_desc', 0)
+// CHECK: ('n_value', 12)
+// CHECK: ('_string', 'local_a')
+// CHECK: ),
+// CHECK: # Symbol 1
+// CHECK: (('n_strx', 27)
+// CHECK: ('n_type', 0xe)
+// CHECK: ('n_sect', 2)
+// CHECK: ('n_desc', 0)
+// CHECK: ('n_value', 16)
+// CHECK: ('_string', 'local_a_elt')
+// CHECK: ),
+// CHECK: # Symbol 2
+// CHECK: (('n_strx', 39)
+// CHECK: ('n_type', 0xe)
+// CHECK: ('n_sect', 2)
+// CHECK: ('n_desc', 0)
+// CHECK: ('n_value', 20)
+// CHECK: ('_string', 'local_b')
+// CHECK: ),
+// CHECK: # Symbol 3
+// CHECK: (('n_strx', 47)
+// CHECK: ('n_type', 0xe)
+// CHECK: ('n_sect', 2)
+// CHECK: ('n_desc', 0)
+// CHECK: ('n_value', 28)
+// CHECK: ('_string', 'local_c')
+// CHECK: ),
+// CHECK: # Symbol 4
+// CHECK: (('n_strx', 55)
+// CHECK: ('n_type', 0xe)
+// CHECK: ('n_sect', 3)
+// CHECK: ('n_desc', 0)
+// CHECK: ('n_value', 43)
+// CHECK: ('_string', 'bar')
+// CHECK: ),
+// CHECK: # Symbol 5
+// CHECK: (('n_strx', 7)
+// CHECK: ('n_type', 0xf)
+// CHECK: ('n_sect', 2)
+// CHECK: ('n_desc', 0)
+// CHECK: ('n_value', 8)
+// CHECK: ('_string', 'local_a_ext')
+// CHECK: ),
+// CHECK: # Symbol 6
+// CHECK: (('n_strx', 1)
+// CHECK: ('n_type', 0x1)
+// CHECK: ('n_sect', 0)
+// CHECK: ('n_desc', 0)
+// CHECK: ('n_value', 0)
+// CHECK: ('_string', 'undef')
+// CHECK: ),
+// CHECK: ])
+// CHECK: ),
+// CHECK: # Load Command 2
+// CHECK: (('command', 11)
+// CHECK: ('size', 80)
+// CHECK: ('ilocalsym', 0)
+// CHECK: ('nlocalsym', 5)
+// CHECK: ('iextdefsym', 5)
+// CHECK: ('nextdefsym', 1)
+// CHECK: ('iundefsym', 6)
+// CHECK: ('nundefsym', 1)
+// CHECK: ('tocoff', 0)
+// CHECK: ('ntoc', 0)
+// CHECK: ('modtaboff', 0)
+// CHECK: ('nmodtab', 0)
+// CHECK: ('extrefsymoff', 0)
+// CHECK: ('nextrefsyms', 0)
+// CHECK: ('indirectsymoff', 0)
+// CHECK: ('nindirectsyms', 0)
+// CHECK: ('extreloff', 0)
+// CHECK: ('nextrel', 0)
+// CHECK: ('locreloff', 0)
+// CHECK: ('nlocrel', 0)
+// CHECK: ('_indirect_symbols', [
+// CHECK: ])
+// CHECK: ),
+// CHECK: ])