diff options
author | Daniel Dunbar <daniel@zuster.org> | 2009-08-26 13:58:10 +0000 |
---|---|---|
committer | Daniel Dunbar <daniel@zuster.org> | 2009-08-26 13:58:10 +0000 |
commit | 3f6a960f9c9ad27f2ac573020df414e8b8cdda04 (patch) | |
tree | 0207ce4dc49bf5e6867099a361ccb91e3dd56273 | |
parent | be9635569401b9a40984c02c6e171aa9da9ad0a2 (diff) | |
download | llvm-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.h | 68 | ||||
-rw-r--r-- | lib/MC/MCAssembler.cpp | 238 | ||||
-rw-r--r-- | test/MC/MachO/reloc.s | 227 |
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: ]) |