From f28a36e527e1c1db67a5d098eae50c409ea1077c Mon Sep 17 00:00:00 2001 From: Tim Northover Date: Fri, 1 Aug 2014 13:07:19 +0000 Subject: [PATCH] llvm-objdump: implement printing for MachO __compact_unwind info. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@214509 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../Inputs/compact-unwind.macho-i386 | Bin 0 -> 2140 bytes .../Inputs/compact-unwind.macho-x86_64 | Bin 0 -> 2272 bytes .../macho-compact-unwind-i386.test | 27 +++ .../macho-compact-unwind-x86_64.test | 27 +++ tools/llvm-objdump/MachODump.cpp | 223 ++++++++++++++++++ tools/llvm-objdump/llvm-objdump.cpp | 6 +- tools/llvm-objdump/llvm-objdump.h | 3 + 7 files changed, 284 insertions(+), 2 deletions(-) create mode 100644 test/tools/llvm-objdump/Inputs/compact-unwind.macho-i386 create mode 100644 test/tools/llvm-objdump/Inputs/compact-unwind.macho-x86_64 create mode 100644 test/tools/llvm-objdump/macho-compact-unwind-i386.test create mode 100644 test/tools/llvm-objdump/macho-compact-unwind-x86_64.test diff --git a/test/tools/llvm-objdump/Inputs/compact-unwind.macho-i386 b/test/tools/llvm-objdump/Inputs/compact-unwind.macho-i386 new file mode 100644 index 0000000000000000000000000000000000000000..174d3830232568ad7046ddd1f107ca2d93f947b3 GIT binary patch literal 2140 zcmaJ?UuaWT7(cN$Gceo4Kd>Rfie%7@B25*k;J~iAxMIt)F5N_M$C%zGku4Tt%4}Iw17CR`VFp*;X{Z7t}x2fAl&i#G= zzwi9XIrpCZ^L)1uq6H(Qv1b>U_PV@8X3V|eLvEWz}nPQJGZ_iOid<3SuXq>(K#C$$UnagISRnA)Dj&w2? zoBOCBM+(mOkpQiTTj(Pg&pc9Ec{w_f8M7!+-rfh-=N9yF1Tx1Gau8n^A`eg`<+xoa zVK$S#+&`H5aHz2&6$I>o>3&mje?1u`W!4@W&tx4rRk~a#jYv6=tY@o#ze5hrWm)&J zj6S$NAhx53%$pdUNPGZz2%B~a{i9bE3sCcC8;oU7ItRF&)Q&vCwf8-S-M>GXS&Ms= z&(1T$q7f&!=B7Ki?me#6YSqEb>NN)){YkHlXzc4EI@yTs6|NC?({W=lsX~N1wdVbT zfYH$-(YI#lERcU@t;4$xt!sL#RIR>;k83`4hc~O{<~&k~6L+HD90yjRHR4{5Md7ku zHP>BpYtdYzlb3>em4|oH+%Fb&E!XSy!FOl9lPZI%_wVys z4b$S3Om8FFX?m*aimLhu#{a4M2h7d$jd)>If6E&K&ZCg3e`n@)pcT1%`n!-~)Jr4gUqC{Z}CE*MYR>wEZLCKFBz!bG*|)H~GJN2Qu#k zUv38zh2KCxe;58(!?{`QHwXDNWX=h6^9|rhFi;ykK>SyYV?dk{13V e@E3BrOPucO3%sI5E3reOGfaTYFb86Fa4L>NG(xnl2aS3(kMwEx z%Jjdwoh`{)+2_ z9W)BTd|lW58*#p-@J&k62FSSOUSu>MZ!nDWBg+g`Zp zwU)L`aIsiCnR@4Q6DoXZ;|Sf2Oalux#=>U3EL}0a|XyyYnP{{c$%5i|@03M%G`C*5|9(cl~p4bg8gM z?$6!WNe$;WOclTXPZgQoDq{P0Wc`l8M_5IL?(^^|ig&I*hBjuz&F$X*uGv%vhmx@I zUmU_n9K~Z6!$`usq@b)Xnq^VShXwC!z@%r2iWr{a*s;RL;ItLJ7`!XcD?ZYs2Y`~s} zr$u+pPdiA?89X;OTpNi>0OdpHs4`Mnll55#aJy^u8R9dL5EW@A+4PX0}O0D46 z1G%%xwJJlWKbo}xCfuZFO*(=LrEE9(Contents.data() + Offset); + else + read(Contents.data() + Offset); + } + +private: + template + static uint64_t readNext(const char *&Buf) { + using llvm::support::little; + using llvm::support::unaligned; + + uint64_t Val = support::endian::read(Buf); + Buf += sizeof(T); + return Val; + } + + template + void read(const char *Buf) { + FunctionAddr = readNext(Buf); + Length = readNext(Buf); + CompactEncoding = readNext(Buf); + PersonalityAddr = readNext(Buf); + LSDAAddr = readNext(Buf); + } +}; +} + +/// Given a relocation from __compact_unwind, consisting of the RelocationRef +/// and data being relocated, determine the best base Name and Addend to use for +/// display purposes. +/// +/// 1. An Extern relocation will directly reference a symbol (and the data is +/// then already an addend), so use that. +/// 2. Otherwise the data is an offset in the object file's layout; try to find +// a symbol before it in the same section, and use the offset from there. +/// 3. Finally, if all that fails, fall back to an offset from the start of the +/// referenced section. +static void findUnwindRelocNameAddend(const MachOObjectFile *Obj, + std::map &Symbols, + const RelocationRef &Reloc, + uint64_t Addr, + StringRef &Name, uint64_t &Addend) { + if (Reloc.getSymbol() != Obj->symbol_end()) { + Reloc.getSymbol()->getName(Name); + Addend = Addr; + return; + } + + auto RE = Obj->getRelocation(Reloc.getRawDataRefImpl()); + SectionRef RelocSection = Obj->getRelocationSection(RE); + + uint64_t SectionAddr; + RelocSection.getAddress(SectionAddr); + + auto Sym = Symbols.upper_bound(Addr); + if (Sym == Symbols.begin()) { + // The first symbol in the object is after this reference, the best we can + // do is section-relative notation. + RelocSection.getName(Name); + Addend = Addr - SectionAddr; + return; + } + + // Go back one so that SymbolAddress <= Addr. + --Sym; + + section_iterator SymSection = Obj->section_end(); + Sym->second.getSection(SymSection); + if (RelocSection == *SymSection) { + // There's a valid symbol in the same section before this reference. + Sym->second.getName(Name); + Addend = Addr - Sym->first; + return; + } + + // There is a symbol before this reference, but it's in a different + // section. Probably not helpful to mention it, so use the section name. + RelocSection.getName(Name); + Addend = Addr - SectionAddr; +} + +static void printUnwindRelocDest(const MachOObjectFile *Obj, + std::map &Symbols, + const RelocationRef &Reloc, + uint64_t Addr) { + StringRef Name; + uint64_t Addend; + + findUnwindRelocNameAddend(Obj, Symbols, Reloc, Addr, Name, Addend); + + outs() << Name; + if (Addend) + outs() << " + " << format("0x%x", Addend); +} + +static void +printMachOCompactUnwindSection(const MachOObjectFile *Obj, + std::map &Symbols, + const SectionRef &CompactUnwind) { + + assert(Obj->isLittleEndian() && + "There should not be a big-endian .o with __compact_unwind"); + + bool Is64 = Obj->is64Bit(); + uint32_t PointerSize = Is64 ? sizeof(uint64_t) : sizeof(uint32_t); + uint32_t EntrySize = 3 * PointerSize + 2 * sizeof(uint32_t); + + StringRef Contents; + CompactUnwind.getContents(Contents); + + SmallVector CompactUnwinds; + + // First populate the initial raw offsets, encodings and so on from the entry. + for (unsigned Offset = 0; Offset < Contents.size(); Offset += EntrySize) { + CompactUnwindEntry Entry(Contents.data(), Offset, Is64); + CompactUnwinds.push_back(Entry); + } + + // Next we need to look at the relocations to find out what objects are + // actually being referred to. + for (const RelocationRef &Reloc : CompactUnwind.relocations()) { + uint64_t RelocAddress; + Reloc.getOffset(RelocAddress); + + uint32_t EntryIdx = RelocAddress / EntrySize; + uint32_t OffsetInEntry = RelocAddress - EntryIdx * EntrySize; + CompactUnwindEntry &Entry = CompactUnwinds[EntryIdx]; + + if (OffsetInEntry == 0) + Entry.FunctionReloc = Reloc; + else if (OffsetInEntry == PointerSize + 2 * sizeof(uint32_t)) + Entry.PersonalityReloc = Reloc; + else if (OffsetInEntry == 2 * PointerSize + 2 * sizeof(uint32_t)) + Entry.LSDAReloc = Reloc; + else + llvm_unreachable("Unexpected relocation in __compact_unwind section"); + } + + // Finally, we're ready to print the data we've gathered. + outs() << "Contents of __compact_unwind section:\n"; + for (auto &Entry : CompactUnwinds) { + outs() << " Entry at offset " << format("0x%x", Entry.OffsetInSection) + << ":\n"; + + // 1. Start of the region this entry applies to. + outs() << " start: " + << format("0x%x", Entry.FunctionAddr) << ' '; + printUnwindRelocDest(Obj, Symbols, Entry.FunctionReloc, + Entry.FunctionAddr); + outs() << '\n'; + + // 2. Length of the region this entry applies to. + outs() << " length: " + << format("0x%x", Entry.Length) << '\n'; + // 3. The 32-bit compact encoding. + outs() << " compact encoding: " + << format("0x%08x", Entry.CompactEncoding) << '\n'; + + // 4. The personality function, if present. + if (Entry.PersonalityReloc.getObjectFile()) { + outs() << " personality function: " + << format("0x%x", Entry.PersonalityAddr) << ' '; + printUnwindRelocDest(Obj, Symbols, Entry.PersonalityReloc, + Entry.PersonalityAddr); + outs() << '\n'; + } + + // 5. This entry's language-specific data area. + if (Entry.LSDAReloc.getObjectFile()) { + outs() << " LSDA: " + << format("0x%x", Entry.LSDAAddr) << ' '; + printUnwindRelocDest(Obj, Symbols, Entry.LSDAReloc, Entry.LSDAAddr); + outs() << '\n'; + } + } +} + +void llvm::printMachOUnwindInfo(const MachOObjectFile *Obj) { + std::map Symbols; + for (const SymbolRef &SymRef : Obj->symbols()) { + // Discard any undefined or absolute symbols. They're not going to take part + // in the convenience lookup for unwind info and just take up resources. + section_iterator Section = Obj->section_end(); + SymRef.getSection(Section); + if (Section == Obj->section_end()) + continue; + + uint64_t Addr; + SymRef.getAddress(Addr); + Symbols.insert(std::make_pair(Addr, SymRef)); + } + + for (const SectionRef &Section : Obj->sections()) { + StringRef SectName; + Section.getName(SectName); + if (SectName == "__compact_unwind") + printMachOCompactUnwindSection(Obj, Symbols, Section); + else if (SectName == "__unwind_info") + outs() << "llvm-objdump: warning: unhandled __unwind_info section\n"; + else if (SectName == "__eh_frame") + outs() << "llvm-objdump: warning: unhandled __eh_frame section\n"; + + } +} diff --git a/tools/llvm-objdump/llvm-objdump.cpp b/tools/llvm-objdump/llvm-objdump.cpp index 97087a276c7..8041a88396a 100644 --- a/tools/llvm-objdump/llvm-objdump.cpp +++ b/tools/llvm-objdump/llvm-objdump.cpp @@ -813,10 +813,12 @@ static void PrintUnwindInfo(const ObjectFile *o) { if (const COFFObjectFile *coff = dyn_cast(o)) { printCOFFUnwindInfo(coff); - } else { + } else if (const MachOObjectFile *MachO = dyn_cast(o)) + printMachOUnwindInfo(MachO); + else { // TODO: Extract DWARF dump tool to objdump. errs() << "This operation is only currently supported " - "for COFF object files.\n"; + "for COFF and MachO object files.\n"; return; } } diff --git a/tools/llvm-objdump/llvm-objdump.h b/tools/llvm-objdump/llvm-objdump.h index 80f8f581a88..6d17f0f09fb 100644 --- a/tools/llvm-objdump/llvm-objdump.h +++ b/tools/llvm-objdump/llvm-objdump.h @@ -18,6 +18,7 @@ namespace llvm { namespace object { class COFFObjectFile; + class MachOObjectFile; class ObjectFile; class RelocationRef; } @@ -31,6 +32,8 @@ bool RelocAddressLess(object::RelocationRef a, object::RelocationRef b); void DumpBytes(StringRef bytes); void DisassembleInputMachO(StringRef Filename); void printCOFFUnwindInfo(const object::COFFObjectFile* o); +void printMachOUnwindInfo(const object::MachOObjectFile* o); + void printELFFileHeader(const object::ObjectFile *o); void printCOFFFileHeader(const object::ObjectFile *o);