From 48bfab80aa65a985cc47df6ca990b89b94e74a2a Mon Sep 17 00:00:00 2001 From: Tim Northover Date: Tue, 12 Aug 2014 11:52:59 +0000 Subject: [PATCH] llvm-objdump: print contents of MachO __unwind_info sections git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@215437 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../Inputs/unwind-info.macho-arm64 | Bin 0 -> 50024 bytes .../Inputs/unwind-info.macho-x86_64 | Bin 0 -> 9136 bytes .../llvm-objdump/macho-unwind-info-arm64.test | 28 ++ .../macho-unwind-info-x86_64.test | 29 ++ tools/llvm-objdump/MachODump.cpp | 260 +++++++++++++++++- 5 files changed, 306 insertions(+), 11 deletions(-) create mode 100755 test/tools/llvm-objdump/Inputs/unwind-info.macho-arm64 create mode 100755 test/tools/llvm-objdump/Inputs/unwind-info.macho-x86_64 create mode 100644 test/tools/llvm-objdump/macho-unwind-info-arm64.test create mode 100644 test/tools/llvm-objdump/macho-unwind-info-x86_64.test diff --git a/test/tools/llvm-objdump/Inputs/unwind-info.macho-arm64 b/test/tools/llvm-objdump/Inputs/unwind-info.macho-arm64 new file mode 100755 index 0000000000000000000000000000000000000000..5b9ce9cf96c911af99d569287c2918b75b6f93ee GIT binary patch literal 50024 zcmeI5U1(fI6vxl*HfGz@CXK=RowaC*HZ*BkN(u_MO+ym=XxgTw7Gd0v+hlRG8+JG8 zhGJci;!`0)LBW@zKD0imRBRA!YF~WQ7A?LgwfK-Kg`gm$8sq=mJGF( z&Y77r=l<^8*O{+w-}-B_?2r zBGvh3Y`zi#^;fQMFdjEVX!=+(oiNGt(abXSowoT>c7pmV<BK~wQpUyAIT`+W?l*E`+)a1s8W0{m0 z$!C}3ixmC(TI>Y%EA!PNzS{O_v+y50+xPr4-Fx~jbi*<`v6?N;>S8&P?? zT2uJX(1g)CXiL&N<)iu%?MnzMpZlPPFNxHnEq>wiu7lkVRnal3`uOO88&1KtC$yi%rJe7d2@*q^}$c%65JGFPmiBC4X`^d@nzCZssVhvFHHu7ZI z_ksexMV&W+eO@W8a5OHCRkb0yzVgvNeJ2V0qK}1|P*;5O<}F*dptxkr8{df$hE4q7PS6H}%#O_n$@V%w==oB5ImDe(CH~$XwI;JF3>d`XASS zc!l+GfA&AFe{bdbU2-KXeyyLPj>=k_BcX5m>*TNmo3E+eUST&>*fVPu#(Zt!8h49V%t{RcG`BuBzEvn zU@V#)lODKZ^4u!<>2?5zI86NEJ^%Ecb2yLz;nrfHs7Et+a{Gt1H84uYG}x$jbc1K> zI_q#4gr38olbWor=P{_i30e4Y!&&OF0w*N&x z?!BIlN%_7`^(2QUoU(ta%0E-(e^}*zUgdw|`{ng;hKa0!)!1*4 zxiJyx>FTmO#pI*0l)H6Fq(iVbw|SpQ4ev|AuwT%arC-{3o1l8L*d2c7ZEAZeH>eFQ TY)u>RHzk~9{*xs?310qhmN5cP literal 0 HcmV?d00001 diff --git a/test/tools/llvm-objdump/Inputs/unwind-info.macho-x86_64 b/test/tools/llvm-objdump/Inputs/unwind-info.macho-x86_64 new file mode 100755 index 0000000000000000000000000000000000000000..9e6ad6bb9262638415398253d4db238f3908c327 GIT binary patch literal 9136 zcmeHNUuauZ82@fNUANUW9S-aM$aS;QS!p|4q!q-pU1)}#c3U@PY#fv9wh5$3NYXAH z42ok9p$zlj+g^%32>MbHVG7QN3GJJI@WBU>Au1_U+>?w^tiRvMxk-Av4t-O&ADny6 z?>pc5ec%1=x#8S%^7Z}S|Jp8MHHyqNh)63+=@F4@VxuMUBuW@XsjfP-;B%=J-YvJwC8eCk7)nK{)cL=~gQ56!JA&FJI;{+zh&3GC>hdq5S&3Qszms_b+Qsri+qUof{ z7LGIyOW7%_r=-d zMPSN)6vpw#yAm$%?c4j#;A*;O?St4e&>?jqJ9p~xoQtW0bl`S4uHHnk%;QJ`e9MC|6-fh{`71v?@VIfIF+zS_%!t>Mj%DZ7UQ8MH0@+a%->npTgc?Fl^x8Y*wZt=wGd}#GE7|OtM%e5C# zhv%={Eia)l-&8zNUJ9*o-M19_g*N3+aarMt40oea?&G-sv(@fgl zS_J)M=&?zjeQ(_eYR-k!%Qwa{~pJH7e8`VAAZ?ppj+2)HVl9FUQs{K)CGLcfSh{f5_Q|)kYJY7}ySJnA=Hhqbf!98$srgDsZ z%E?XR>{P0&IeDIYGSbj11AP-S6bug1begCIsZV_>tNmHTbRtvX6g6Kn4 z-SXj4GHsgNBSW+sjSaVhIL5Wv1snI65Nc`Uz}?s?bkF&@&qK`pAP(gDVGA1W{~o#x z=H3zec_ztz?jaEmX&k_{g3oGPIsat;`HJ4XC*l79pl6pB{!^kJU-#ez5B}JLZ+Yo?|%b5`T>^! literal 0 HcmV?d00001 diff --git a/test/tools/llvm-objdump/macho-unwind-info-arm64.test b/test/tools/llvm-objdump/macho-unwind-info-arm64.test new file mode 100644 index 00000000000..712edef50bd --- /dev/null +++ b/test/tools/llvm-objdump/macho-unwind-info-arm64.test @@ -0,0 +1,28 @@ +# RUN: llvm-objdump -unwind-info %p/Inputs/unwind-info.macho-arm64 2>/dev/null | FileCheck %s + +# The 2nd level index here is "regular", including all offsets & encodings in +# full. + +# CHECK: Contents of __unwind_info section: +# CHECK: Version: 0x1 +# CHECK: Common encodings array section offset: 0x1c +# CHECK: Number of common encodings in array: 0x2 +# CHECK: Personality function array section offset: 0x24 +# CHECK: Number of personality functions in array: 0x1 +# CHECK: Index array section offset: 0x28 +# CHECK: Number of indices in array: 0x2 +# CHECK: Common encodings: (count = 2) +# CHECK: encoding[0]: 0x04000000 +# CHECK: encoding[1]: 0x54000000 +# CHECK: Personality functions: (count = 1) +# CHECK: personality[1]: 0x00008008 +# CHECK: Top level indices: (count = 2) +# CHECK: [0]: function offset=0x00007d64, 2nd level page offset=0x00000050, LSDA offset=0x00000040 +# CHECK: [1]: function offset=0x00007eb5, 2nd level page offset=0x00000000, LSDA offset=0x00000050 +# CHECK: LSDA descriptors: +# CHECK: [0]: function offset=0x00007d90, LSDA offset=0x00007f44 +# CHECK: [1]: function offset=0x00007e10, LSDA offset=0x00007f6c +# CHECK: Second level indices: +# CHECK: Second level index[0]: offset in section=0x00000050, base function offset=0x00007d64 +# CHECK: [0]: function offset=0x00007d90, encoding=0x78563412 +# CHECK: [1]: function offset=0x00007e10, encoding=0x21436587 diff --git a/test/tools/llvm-objdump/macho-unwind-info-x86_64.test b/test/tools/llvm-objdump/macho-unwind-info-x86_64.test new file mode 100644 index 00000000000..1333d9a4cdb --- /dev/null +++ b/test/tools/llvm-objdump/macho-unwind-info-x86_64.test @@ -0,0 +1,29 @@ +# RUN: llvm-objdump -unwind-info %p/Inputs/unwind-info.macho-x86_64 2>/dev/null | FileCheck %s + +# The 2nd level index in this file is in compressed form, referring to both +# common and packed encodings. + +# CHECK:Contents of __unwind_info section: +# CHECK: Version: 0x1 +# CHECK: Common encodings array section offset: 0x1c +# CHECK: Number of common encodings in array: 0x2 +# CHECK: Personality function array section offset: 0x24 +# CHECK: Number of personality functions in array: 0x1 +# CHECK: Index array section offset: 0x28 +# CHECK: Number of indices in array: 0x2 +# CHECK: Common encodings: (count = 2) +# CHECK: encoding[0]: 0x01000000 +# CHECK: encoding[1]: 0x51000000 +# CHECK: Personality functions: (count = 1) +# CHECK: personality[1]: 0x00001018 +# CHECK: Top level indices: (count = 2) +# CHECK: [0]: function offset=0x00000d70, 2nd level page offset=0x00000050, LSDA offset=0x00000040 +# CHECK: [1]: function offset=0x00000eab, 2nd level page offset=0x00000000, LSDA offset=0x00000050 +# CHECK: LSDA descriptors: +# CHECK: [0]: function offset=0x00000db0, LSDA offset=0x00000f0c +# CHECK: [1]: function offset=0x00000e20, LSDA offset=0x00000f34 +# CHECK: Second level indices: +# CHECK: Second level index[0]: offset in section=0x00000050, base function offset=0x00000d70 +# CHECK: [0]: function offset=0x00000d70, encoding[0]=0x01000000 +# CHECK: [1]: function offset=0x00000db0, encoding[1]=0x51000000 +# CHECK: [2]: function offset=0x00000e20, encoding[2]=0x01234567 diff --git a/tools/llvm-objdump/MachODump.cpp b/tools/llvm-objdump/MachODump.cpp index 15b47730208..bd4b6cb5719 100644 --- a/tools/llvm-objdump/MachODump.cpp +++ b/tools/llvm-objdump/MachODump.cpp @@ -471,7 +471,22 @@ static void DisassembleInputMachO2(StringRef Filename, } } + +//===----------------------------------------------------------------------===// +// __compact_unwind section dumping +//===----------------------------------------------------------------------===// + namespace { + +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; + } + struct CompactUnwindEntry { uint32_t OffsetInSection; @@ -494,16 +509,6 @@ struct CompactUnwindEntry { } 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); @@ -665,6 +670,239 @@ printMachOCompactUnwindSection(const MachOObjectFile *Obj, } } +//===----------------------------------------------------------------------===// +// __unwind_info section dumping +//===----------------------------------------------------------------------===// + +static void printRegularSecondLevelUnwindPage(const char *PageStart) { + const char *Pos = PageStart; + uint32_t Kind = readNext(Pos); + (void)Kind; + assert(Kind == 2 && "kind for a regular 2nd level index should be 2"); + + uint16_t EntriesStart = readNext(Pos); + uint16_t NumEntries = readNext(Pos); + + Pos = PageStart + EntriesStart; + for (unsigned i = 0; i < NumEntries; ++i) { + uint32_t FunctionOffset = readNext(Pos); + uint32_t Encoding = readNext(Pos); + + outs() << " [" << i << "]: " + << "function offset=" + << format("0x%08" PRIx32, FunctionOffset) << ", " + << "encoding=" + << format("0x%08" PRIx32, Encoding) + << '\n'; + } +} + +static void printCompressedSecondLevelUnwindPage( + const char *PageStart, uint32_t FunctionBase, + const SmallVectorImpl &CommonEncodings) { + const char *Pos = PageStart; + uint32_t Kind = readNext(Pos); + (void)Kind; + assert(Kind == 3 && "kind for a compressed 2nd level index should be 3"); + + uint16_t EntriesStart = readNext(Pos); + uint16_t NumEntries = readNext(Pos); + + uint16_t EncodingsStart = readNext(Pos); + readNext(Pos); + auto PageEncodings = (support::ulittle32_t *)(PageStart + EncodingsStart); + + Pos = PageStart + EntriesStart; + for (unsigned i = 0; i < NumEntries; ++i) { + uint32_t Entry = readNext(Pos); + uint32_t FunctionOffset = FunctionBase + (Entry & 0xffffff); + uint32_t EncodingIdx = Entry >> 24; + + uint32_t Encoding; + if (EncodingIdx < CommonEncodings.size()) + Encoding = CommonEncodings[EncodingIdx]; + else + Encoding = PageEncodings[EncodingIdx - CommonEncodings.size()]; + + outs() << " [" << i << "]: " + << "function offset=" + << format("0x%08" PRIx32, FunctionOffset) << ", " + << "encoding[" << EncodingIdx << "]=" + << format("0x%08" PRIx32, Encoding) + << '\n'; + } +} + +static void +printMachOUnwindInfoSection(const MachOObjectFile *Obj, + std::map &Symbols, + const SectionRef &UnwindInfo) { + + assert(Obj->isLittleEndian() && + "There should not be a big-endian .o with __unwind_info"); + + outs() << "Contents of __unwind_info section:\n"; + + StringRef Contents; + UnwindInfo.getContents(Contents); + const char *Pos = Contents.data(); + + //===---------------------------------- + // Section header + //===---------------------------------- + + uint32_t Version = readNext(Pos); + outs() << " Version: " + << format("0x%" PRIx32, Version) << '\n'; + assert(Version == 1 && "only understand version 1"); + + uint32_t CommonEncodingsStart = readNext(Pos); + outs() << " Common encodings array section offset: " + << format("0x%" PRIx32, CommonEncodingsStart) << '\n'; + uint32_t NumCommonEncodings = readNext(Pos); + outs() << " Number of common encodings in array: " + << format("0x%" PRIx32, NumCommonEncodings) << '\n'; + + uint32_t PersonalitiesStart = readNext(Pos); + outs() << " Personality function array section offset: " + << format("0x%" PRIx32, PersonalitiesStart) << '\n'; + uint32_t NumPersonalities = readNext(Pos); + outs() << " Number of personality functions in array: " + << format("0x%" PRIx32, NumPersonalities) << '\n'; + + uint32_t IndicesStart = readNext(Pos); + outs() << " Index array section offset: " + << format("0x%" PRIx32, IndicesStart) << '\n'; + uint32_t NumIndices = readNext(Pos); + outs() << " Number of indices in array: " + << format("0x%" PRIx32, NumIndices) << '\n'; + + //===---------------------------------- + // A shared list of common encodings + //===---------------------------------- + + // These occupy indices in the range [0, N] whenever an encoding is referenced + // from a compressed 2nd level index table. In practice the linker only + // creates ~128 of these, so that indices are available to embed encodings in + // the 2nd level index. + + SmallVector CommonEncodings; + outs() << " Common encodings: (count = " << NumCommonEncodings << ")\n"; + Pos = Contents.data() + CommonEncodingsStart; + for (unsigned i = 0; i < NumCommonEncodings; ++i) { + uint32_t Encoding = readNext(Pos); + CommonEncodings.push_back(Encoding); + + outs() << " encoding[" << i << "]: " << format("0x%08" PRIx32, Encoding) + << '\n'; + } + + + //===---------------------------------- + // Personality functions used in this executable + //===---------------------------------- + + // There should be only a handful of these (one per source language, + // roughly). Particularly since they only get 2 bits in the compact encoding. + + outs() << " Personality functions: (count = " << NumPersonalities << ")\n"; + Pos = Contents.data() + PersonalitiesStart; + for (unsigned i = 0; i < NumPersonalities; ++i) { + uint32_t PersonalityFn = readNext(Pos); + outs() << " personality[" << i + 1 + << "]: " << format("0x%08" PRIx32, PersonalityFn) << '\n'; + } + + //===---------------------------------- + // The level 1 index entries + //===---------------------------------- + + // These specify an approximate place to start searching for the more detailed + // information, sorted by PC. + + struct IndexEntry { + uint32_t FunctionOffset; + uint32_t SecondLevelPageStart; + uint32_t LSDAStart; + }; + + SmallVector IndexEntries; + + outs() << " Top level indices: (count = " << NumIndices << ")\n"; + Pos = Contents.data() + IndicesStart; + for (unsigned i = 0; i < NumIndices; ++i) { + IndexEntry Entry; + + Entry.FunctionOffset = readNext(Pos); + Entry.SecondLevelPageStart = readNext(Pos); + Entry.LSDAStart = readNext(Pos); + IndexEntries.push_back(Entry); + + outs() << " [" << i << "]: " + << "function offset=" + << format("0x%08" PRIx32, Entry.FunctionOffset) << ", " + << "2nd level page offset=" + << format("0x%08" PRIx32, Entry.SecondLevelPageStart) << ", " + << "LSDA offset=" + << format("0x%08" PRIx32, Entry.LSDAStart) << '\n'; + } + + + //===---------------------------------- + // Next come the LSDA tables + //===---------------------------------- + + // The LSDA layout is rather implicit: it's a contiguous array of entries from + // the first top-level index's LSDAOffset to the last (sentinel). + + outs() << " LSDA descriptors:\n"; + Pos = Contents.data() + IndexEntries[0].LSDAStart; + int NumLSDAs = (IndexEntries.back().LSDAStart - IndexEntries[0].LSDAStart) / + (2 * sizeof(uint32_t)); + for (int i = 0; i < NumLSDAs; ++i) { + uint32_t FunctionOffset = readNext(Pos); + uint32_t LSDAOffset = readNext(Pos); + outs() << " [" << i << "]: " + << "function offset=" + << format("0x%08" PRIx32, FunctionOffset) << ", " + << "LSDA offset=" + << format("0x%08" PRIx32, LSDAOffset) << '\n'; + } + + //===---------------------------------- + // Finally, the 2nd level indices + //===---------------------------------- + + // Generally these are 4K in size, and have 2 possible forms: + // + Regular stores up to 511 entries with disparate encodings + // + Compressed stores up to 1021 entries if few enough compact encoding + // values are used. + outs() << " Second level indices:\n"; + for (unsigned i = 0; i < IndexEntries.size() - 1; ++i) { + // The final sentinel top-level index has no associated 2nd level page + if (IndexEntries[i].SecondLevelPageStart == 0) + break; + + outs() << " Second level index[" << i << "]: " + << "offset in section=" + << format("0x%08" PRIx32, IndexEntries[i].SecondLevelPageStart) + << ", " + << "base function offset=" + << format("0x%08" PRIx32, IndexEntries[i].FunctionOffset) << '\n'; + + Pos = Contents.data() + IndexEntries[i].SecondLevelPageStart; + uint32_t Kind = *(support::ulittle32_t *)Pos; + if (Kind == 2) + printRegularSecondLevelUnwindPage(Pos); + else if (Kind == 3) + printCompressedSecondLevelUnwindPage(Pos, IndexEntries[i].FunctionOffset, + CommonEncodings); + else + llvm_unreachable("Do not know how to print this kind of 2nd level page"); + + } +} + void llvm::printMachOUnwindInfo(const MachOObjectFile *Obj) { std::map Symbols; for (const SymbolRef &SymRef : Obj->symbols()) { @@ -686,7 +924,7 @@ void llvm::printMachOUnwindInfo(const MachOObjectFile *Obj) { if (SectName == "__compact_unwind") printMachOCompactUnwindSection(Obj, Symbols, Section); else if (SectName == "__unwind_info") - outs() << "llvm-objdump: warning: unhandled __unwind_info section\n"; + printMachOUnwindInfoSection(Obj, Symbols, Section); else if (SectName == "__eh_frame") outs() << "llvm-objdump: warning: unhandled __eh_frame section\n"; -- 2.34.1