From e493a9976c6ff5aa7852c6c6f512b0ff60e3f5ce Mon Sep 17 00:00:00 2001 From: Timur Iskhodzhanov Date: Thu, 19 Dec 2013 11:37:14 +0000 Subject: [PATCH] Teach the llvm-readobj COFF dumper to dump debug line tables from object files Reviewed at http://llvm-reviews.chandlerc.com/D2425 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@197674 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/Support/COFF.h | 7 + .../Inputs/multifile-linetables.obj.coff-2012-i368 | Bin 0 -> 1631 bytes .../multifile-linetables.obj.coff-2012-x86_64 | Bin 0 -> 1799 bytes .../multifunction-linetables.obj.coff-2012-i368 | Bin 0 -> 2155 bytes .../multifunction-linetables.obj.coff-2012-x86_64 | Bin 0 -> 2475 bytes test/tools/llvm-readobj/codeview-linetables.test | 282 +++++++++++++++++++++ tools/llvm-readobj/COFFDumper.cpp | 165 ++++++++++++ tools/llvm-readobj/llvm-readobj.cpp | 4 + tools/llvm-readobj/llvm-readobj.h | 1 + 9 files changed, 459 insertions(+) create mode 100644 test/tools/llvm-readobj/Inputs/multifile-linetables.obj.coff-2012-i368 create mode 100644 test/tools/llvm-readobj/Inputs/multifile-linetables.obj.coff-2012-x86_64 create mode 100644 test/tools/llvm-readobj/Inputs/multifunction-linetables.obj.coff-2012-i368 create mode 100644 test/tools/llvm-readobj/Inputs/multifunction-linetables.obj.coff-2012-x86_64 create mode 100644 test/tools/llvm-readobj/codeview-linetables.test diff --git a/include/llvm/Support/COFF.h b/include/llvm/Support/COFF.h index 9cc3989df04..2de01000a74 100644 --- a/include/llvm/Support/COFF.h +++ b/include/llvm/Support/COFF.h @@ -611,6 +611,13 @@ namespace COFF { } }; + enum CodeViewLineTableIdentifiers { + DEBUG_SECTION_MAGIC = 0x4, + DEBUG_LINE_TABLE_SUBSECTION = 0xF2, + DEBUG_STRING_TABLE_SUBSECTION = 0xF3, + DEBUG_INDEX_SUBSECTION = 0xF4 + }; + } // End namespace COFF. } // End namespace llvm. diff --git a/test/tools/llvm-readobj/Inputs/multifile-linetables.obj.coff-2012-i368 b/test/tools/llvm-readobj/Inputs/multifile-linetables.obj.coff-2012-i368 new file mode 100644 index 0000000000000000000000000000000000000000..1672d3a542423e0be736afe86c9d3c9969792874 GIT binary patch literal 1631 zcmbtU&1(};5TDIQ(Ca;wrLK<#GFfjyxY`b_3hZhu4*9@&Hs3Iny7EX- zt1P3{8%j;qDc29AI0^QlvfbwrDpa~t$1KWM;dZAE$B$yjXOMWDyx z(+OG(-xa^f{X7g@34K1d_x`eY!SinMCBPx#Yvd!P1x=NCkpy#FvjnRJBE*YH7MIHn zHjYebFw(EHYwUgpK%4+eF)U>at>ete&@j(^@ATP>p{Xz2qC0Cq=WsHXKfm@&a7EK>u+hs=$|qKeO+mwD@};FOn61w zZGV8HBiaKK6}e>c$>ehmS{CA7$@Ds2j+XMt<6g(~|MhaTknP=RySi5~O}dw(Wqk7R ht~E@RdpT!2JxzVuvwSzI?ihyqVxw}5_<#_K{RKKaBsc&7 literal 0 HcmV?d00001 diff --git a/test/tools/llvm-readobj/Inputs/multifile-linetables.obj.coff-2012-x86_64 b/test/tools/llvm-readobj/Inputs/multifile-linetables.obj.coff-2012-x86_64 new file mode 100644 index 0000000000000000000000000000000000000000..30bfe79bc3087c3e02aaedf4c124c843afb6dd0b GIT binary patch literal 1799 zcmbtV&1(};5TEU)X`2>JRf?$SGX#u6))b{`^w1`4wV1Tprd4E7)7?#6X|grh*y<@D zil7Hi9=-SnD0tL^_<;vO^bhdl#X}L=dKBt!_M_c^qUgZ9nfH71=FOXVJFDLbQ|jW= z$;a(PTgfFwuNj(s-SDP#5K2h$XP%5iNzskjMmav^uiS;rA@K7y7YRWOQZ(b)6@0t% zSpVYL_?Tm78;iCNKyC*gM_f~2CK)W~%w=U!FWN;1a)wTf*E|~^9i-HtGT&;~gT))L zyB+Yyc0!1Q!y}_Z)44)!ER&95$mR>NR&XLWJU)~knTiR%)-w1kcq>W05Hy@t&FXx^ zR;<~pl%^BCbeZTQ@!G2;XiX0dHcKfd$gTxZ_Yj; zTE+Tqw$an7RW%fi6??&Q6*sLKmN_pROPF%KJqZ3T^lpdmds>8EtYa_NF}_Ebq%9DL zwKK}k$RYTMP+L@j>_N1fNa|yp=OtQM4$jM%N2uLdN3Chn)3a&yOwB6SiWM=6`qf2Z z@#w&Qb)(KRX1!6I6H|6WH!acMucXwOteP1cS7CUmSkVU#tJ<7mEE@F9^O?MGL?ktl z%ojv*EcTBljE!e=)59aN|8Ox>$mK_~>R)&|XI6Du4r-QX_p%JQjOONp=iU^a$Rx85XPb9do4A96-l+q{lR z=%k?xrXc z#B<|^e9-+ygN>cDC`+6LrccM-axy&fV|HvP*yh8qkR>;RM2`+!eBy42JHLXs6NF>q Nbgg)!ikG1S{t49CE$9FM literal 0 HcmV?d00001 diff --git a/test/tools/llvm-readobj/Inputs/multifunction-linetables.obj.coff-2012-i368 b/test/tools/llvm-readobj/Inputs/multifunction-linetables.obj.coff-2012-i368 new file mode 100644 index 0000000000000000000000000000000000000000..a0196ff2d73763d46ff0b7f8b6fb6fdf26fbc0f7 GIT binary patch literal 2155 zcmb_d&1(};5T9+5wkCe0B@_|CM+n#=SrZVgMWJolN;PRqo2tnA(e7^QYMO*(qiIiz zC&82dfr4Hk?p$M&d@}R%jeY;JiVo_(AotfXh_xAVZeN0{n)1%8z zXK!{9?IllCqi*W+v_C}j5y5v@mVMA+ijT^6MHj@& z?jX7!_9M@_?QFAUx53BYN5JbOlqCWcG;>V|add2aVq`vF%ukLECGpA?ipj0ubbf4V zq%c006#S=k=qxlrN)HT;4QY*Pv#y(Jb@2ub(WxGcOkKnuU#%&DEm(TJ+NhRn(LXyN zrfasfY^_?AC6TKx*Q~NxNBeKk6x5ff??}4`mNVk>I0yeQopE`{Cd7TXO*h-Pk18!X zNWDSm!_waApbnbup$){Dwr~Fbc|4yNd`EI{_#a56^S^nA1ER{x|Kg36cX@-e%X`;0 zyc2D6XLl$0=02Q{UT9>KcWI}*htba$|Np!_dCRiIB=6mlH#j-W3rEc1f8uC3Ut#w{ z6=+;}oPF5&d|G|;{^jEKiW#9tS$Z+5NiRwd z0_;(MeSpXF?xGY9J$o_gmtK@E2ACFLd`db=;Upnj-Em$eSHL(*9dU)N!{3xX#yM`$ z#(I0a@=IL$C0<1wrHI=fHvyfMer`ycsaKck3(H~xhr1#ATZ6|3w9QN4ywzwfl*OFg zG_0z~X4Q;#KBtXNPHB2sHCrae{Vc~yUpgVux=0s_B0ZV>%aJFia{2kO@#H@YF;dJI zCUV*iM!H~CjOy)%IA_@nZ4gtlDtftTm|EldLftgBB|*qzxDSg=yRZ%w%pG-sWZr%| zU~_rf!T!|kwpFRy=vmB(^hNrzvED0lEw6H&u|A1mU2o~xY(}jai;N$jP+x?)z^)s* z8@dM?gKn(P-&^DDPM2T5b>(-l+eo(Leabg&0*~+UZObPrK?8X6NlB23x7Ti1N6k2A$|fm~hntoIfuI9FSnNSIt?#Y2!vl1RyK?;||^PqZISrT_o{ literal 0 HcmV?d00001 diff --git a/test/tools/llvm-readobj/Inputs/multifunction-linetables.obj.coff-2012-x86_64 b/test/tools/llvm-readobj/Inputs/multifunction-linetables.obj.coff-2012-x86_64 new file mode 100644 index 0000000000000000000000000000000000000000..14f65ab2f6d70a162d24a5b733f00cc258bf64e3 GIT binary patch literal 2475 zcmb_eOK%%h6h4!2oYZ;XgvgYK#Fga=cj1YxR;7wqICc_ZYR6IHmI#eX9nUxnitWgc z){a-EYo4 z_n!M0G=`?8`MnG{}jt+%MGtBMIPHfZd(MO0bfcKti zBa={R3TGT%$>qHT-dMnG*;?}2X1DEww_qOxiGD$08ATDaenaaT<#t&@vO0@;3i10T zP06&jH_VHHa1Okj!##sG^MnxdOUo;ZSM#NOF0&9tl`WK_!(cJLw6<7S-i!*4{0L_{Mor+-qAkYu~Ex zh-{;_XYHCz@Y(kjlo#k_=^Lh$Y1BM6hpmPs#8sD`quFrWYY!P2g+{12L&rdqz8?0) z!kOY_VS(Le|0sPP1Iyh%O)P;yEWQ6L7G$W;v5Y&hAfCs@!s%!3lN|FPmg;F^N#i?L z*cS_LwTsdsn!woXAKO{3%M>)PA4;Fcd;E{%xgT%`$7t03eiYdfu6+drb&q~&h0K8ANkw86Yc7W z_CDGn8kOJUyx{!Uc6LEuZ#H(C<(gQ*5on2c_wuFl`rxQsvs#_cdey2Mjaw~o#cH?o%b227uk3aVQ*XUdZkom;gCMMDAc7>)8RVmmx#LzQXZ;@Q zuv2@avw!;RmQ`=I@ntD15?l1(_-NKyYvPIbDp&fi_cepUO~+#C_qKj zHc!DT7sz0DpU@!iY4^l$yEg`RGOog-YmLL=8$y*M;hunnDf5j1YYz2O(qbQstGKdU zOTc UCs); + void printCodeViewLineTables(section_iterator SecI); + void cacheRelocations(); error_code getSectionContents( @@ -648,6 +652,164 @@ void COFFDumper::printFileHeaders() { } } +void COFFDumper::printCodeViewLineTables(section_iterator SecI) { + StringRef Data; + if (error(SecI->getContents(Data))) return; + + SmallVector FunctionNames; + StringMap FunctionLineTables; + StringRef FileIndexToStringOffsetTable; + StringRef StringTable; + + ListScope D(W, "CodeViewLineTables"); + { + DataExtractor DE(Data, true, 4); + uint32_t Offset = 0, + Magic = DE.getU32(&Offset); + W.printHex("Magic", Magic); + if (Magic != COFF::DEBUG_SECTION_MAGIC) { + error(object_error::parse_failed); + return; + } + + bool Finished = false; + while (DE.isValidOffset(Offset) && !Finished) { + // The section consists of a number of subsection in the following format: + // |Type|PayloadSize|Payload...| + uint32_t SubSectionType = DE.getU32(&Offset), + PayloadSize = DE.getU32(&Offset); + ListScope S(W, "Subsection"); + W.printHex("Type", SubSectionType); + W.printHex("PayloadSize", PayloadSize); + if (PayloadSize > Data.size() - Offset) { + error(object_error::parse_failed); + return; + } + + // Print the raw contents to simplify debugging if anything goes wrong + // afterwards. + StringRef Contents = Data.substr(Offset, PayloadSize); + W.printBinaryBlock("Contents", Contents); + + switch (SubSectionType) { + case COFF::DEBUG_LINE_TABLE_SUBSECTION: { + // Holds a PC to file:line table. Some data to parse this subsection is + // stored in the other subsections, so just check sanity and store the + // pointers for deferred processing. + + if (PayloadSize < 12) { + // There should be at least three words to store two function + // relocations and size of the code. + error(object_error::parse_failed); + return; + } + + StringRef FunctionName; + if (error(resolveSymbolName(RelocMap[Obj->getCOFFSection(SecI)], Offset, + FunctionName))) + return; + W.printString("FunctionName", FunctionName); + if (FunctionLineTables.count(FunctionName) != 0) { + // Saw debug info for this function already? + error(object_error::parse_failed); + return; + } + + FunctionLineTables[FunctionName] = Contents; + FunctionNames.push_back(FunctionName); + break; + } + case COFF::DEBUG_STRING_TABLE_SUBSECTION: + if (PayloadSize == 0 || StringTable.data() != 0 || + Contents.back() != '\0') { + // Empty or duplicate or non-null-terminated subsection. + error(object_error::parse_failed); + return; + } + StringTable = Contents; + break; + case COFF::DEBUG_INDEX_SUBSECTION: + // Holds the translation table from file indices + // to offsets in the string table. + + if (PayloadSize == 0 || FileIndexToStringOffsetTable.data() != 0) { + // Empty or duplicate subsection. + error(object_error::parse_failed); + return; + } + FileIndexToStringOffsetTable = Contents; + break; + } + Offset += PayloadSize; + + // Align the reading pointer by 4. + Offset += (-Offset) % 4; + } + } + + // Dump the line tables now that we've read all the subsections and know all + // the required information. + for (unsigned I = 0, E = FunctionNames.size(); I != E; ++I) { + StringRef Name = FunctionNames[I]; + ListScope S(W, "FunctionLineTable"); + W.printString("FunctionName", Name); + + DataExtractor DE(FunctionLineTables[Name], true, 4); + uint32_t Offset = 8; // Skip relocations. + uint32_t FunctionSize = DE.getU32(&Offset); + W.printHex("CodeSize", FunctionSize); + while (DE.isValidOffset(Offset)) { + // For each range of lines with the same filename, we have a segment + // in the line table. The filename string is accessed using double + // indirection to the string table subsection using the index subsection. + uint32_t OffsetInIndex = DE.getU32(&Offset), + SegmentLength = DE.getU32(&Offset), + FullSegmentSize = DE.getU32(&Offset); + if (FullSegmentSize != 12 + 8 * SegmentLength) { + error(object_error::parse_failed); + return; + } + + uint32_t FilenameOffset; + { + DataExtractor SDE(FileIndexToStringOffsetTable, true, 4); + uint32_t OffsetInSDE = OffsetInIndex; + if (!SDE.isValidOffset(OffsetInSDE)) { + error(object_error::parse_failed); + return; + } + FilenameOffset = SDE.getU32(&OffsetInSDE); + } + + if (FilenameOffset == 0 || FilenameOffset + 1 >= StringTable.size() || + StringTable.data()[FilenameOffset - 1] != '\0') { + // Each string in an F3 subsection should be preceded by a null + // character. + error(object_error::parse_failed); + return; + } + + StringRef Filename(StringTable.data() + FilenameOffset); + ListScope S(W, "FilenameSegment"); + W.printString("Filename", Filename); + for (unsigned J = 0; J != SegmentLength && DE.isValidOffset(Offset); + ++J) { + // Then go the (PC, LineNumber) pairs. The line number is stored in the + // least significant 31 bits of the respective word in the table. + uint32_t PC = DE.getU32(&Offset), + LineNumber = DE.getU32(&Offset) & 0x7fffffff; + if (PC >= FunctionSize) { + error(object_error::parse_failed); + return; + } + char Buffer[32]; + format("+0x%X", PC).snprint(Buffer, 32); + W.printNumber(Buffer, LineNumber); + } + } + } +} + void COFFDumper::printSections() { error_code EC; @@ -707,6 +869,9 @@ void COFFDumper::printSections() { } } + if (Name == ".debug$S" && opts::CodeViewLineTables) + printCodeViewLineTables(SecI); + if (opts::SectionData) { StringRef Data; if (error(SecI->getContents(Data))) break; diff --git a/tools/llvm-readobj/llvm-readobj.cpp b/tools/llvm-readobj/llvm-readobj.cpp index f84a72f4614..c09be74dc7e 100644 --- a/tools/llvm-readobj/llvm-readobj.cpp +++ b/tools/llvm-readobj/llvm-readobj.cpp @@ -128,6 +128,10 @@ namespace opts { // -expand-relocs cl::opt ExpandRelocs("expand-relocs", cl::desc("Expand each shown relocation to multiple lines")); + + // -codeview-linetables + cl::opt CodeViewLineTables("codeview-linetables", + cl::desc("Display CodeView line table information")); } // namespace opts static int ReturnValue = EXIT_SUCCESS; diff --git a/tools/llvm-readobj/llvm-readobj.h b/tools/llvm-readobj/llvm-readobj.h index 3f756106c97..f0bba42e918 100644 --- a/tools/llvm-readobj/llvm-readobj.h +++ b/tools/llvm-readobj/llvm-readobj.h @@ -38,6 +38,7 @@ namespace opts { extern llvm::cl::opt DynamicSymbols; extern llvm::cl::opt UnwindInfo; extern llvm::cl::opt ExpandRelocs; + extern llvm::cl::opt CodeViewLineTables; } // namespace opts #define LLVM_READOBJ_ENUM_ENT(ns, enum) \ -- 2.11.0