PR21189: Teach llvm-readobj to dump bits of COFF symbol subsections required to debug...
authorTimur Iskhodzhanov <timurrrr@google.com>
Thu, 23 Oct 2014 22:25:31 +0000 (22:25 +0000)
committerTimur Iskhodzhanov <timurrrr@google.com>
Thu, 23 Oct 2014 22:25:31 +0000 (22:25 +0000)
Reviewed at http://reviews.llvm.org/D5755
Thanks to Andrey Guskov for his help investigating this!

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@220526 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/Support/COFF.h
test/tools/llvm-readobj/codeview-linetables.test
tools/llvm-readobj/COFFDumper.cpp

index 1bfb7f7443334a5fe03d6e9f186b2df7c4dfd855..86698e3c5a2716249e41ed9c0393bfeb733dc37d 100644 (file)
@@ -662,9 +662,14 @@ namespace COFF {
 
   enum CodeViewLineTableIdentifiers {
     DEBUG_SECTION_MAGIC           = 0x4,
+    DEBUG_SYMBOL_SUBSECTION       = 0xF1,
     DEBUG_LINE_TABLE_SUBSECTION   = 0xF2,
     DEBUG_STRING_TABLE_SUBSECTION = 0xF3,
-    DEBUG_INDEX_SUBSECTION        = 0xF4
+    DEBUG_INDEX_SUBSECTION        = 0xF4,
+
+    // Symbol subsections are split into records of different types.
+    DEBUG_SYMBOL_TYPE_PROC_START = 0x1147,
+    DEBUG_SYMBOL_TYPE_PROC_END   = 0x114F
   };
 
   inline bool isReservedSectionNumber(int32_t SectionNumber) {
index a1bfca02d89a9666e5b43f87ce8d3abbb99f0f44..7b9e743d84900bf4097b625c42c6d906c95d331d 100644 (file)
@@ -40,6 +40,12 @@ MFUN32:        ]
 MFUN32-NEXT:   Subsection [
 MFUN32-NEXT:     Type: 0xF1
 MFUN32-NEXT:     PayloadSize: 0x4B
+MFUN32:          ProcStart {
+MFUN32-NEXT:       FunctionName: x
+MFUN32-NEXT:       Section: _x
+MFUN32-NEXT:       CodeSize: 0xA
+MFUN32-NEXT:     }
+MFUN32-NEXT:     ProcEnd
 MFUN32:        ]
 MFUN32-NEXT:   Subsection [
 MFUN32-NEXT:     Type: 0xF2
@@ -53,6 +59,12 @@ MFUN32:        ]
 MFUN32-NEXT:   Subsection [
 MFUN32-NEXT:     Type: 0xF1
 MFUN32-NEXT:     PayloadSize: 0x4B
+MFUN32:          ProcStart {
+MFUN32-NEXT:       FunctionName: y
+MFUN32-NEXT:       Section: _y
+MFUN32-NEXT:       CodeSize: 0xA
+MFUN32-NEXT:     }
+MFUN32-NEXT:     ProcEnd
 MFUN32:        ]
 MFUN32-NEXT:   Subsection [
 MFUN32-NEXT:     Type: 0xF2
@@ -66,6 +78,12 @@ MFUN32:        ]
 MFUN32-NEXT:   Subsection [
 MFUN32-NEXT:     Type: 0xF1
 MFUN32-NEXT:     PayloadSize: 0x4B
+MFUN32:          ProcStart {
+MFUN32-NEXT:       FunctionName: f
+MFUN32-NEXT:       Section: _f
+MFUN32-NEXT:       CodeSize: 0x14
+MFUN32-NEXT:     }
+MFUN32-NEXT:     ProcEnd
 MFUN32:        ]
 MFUN32-NEXT:   Subsection [
 MFUN32-NEXT:     Type: 0xF2
@@ -127,6 +145,12 @@ MFUN64:        ]
 MFUN64-NEXT:   Subsection [
 MFUN64-NEXT:     Type: 0xF1
 MFUN64-NEXT:     PayloadSize: 0x4B
+MFUN64:          ProcStart {
+MFUN64-NEXT:       FunctionName: x
+MFUN64-NEXT:       Section: x
+MFUN64-NEXT:       CodeSize: 0xE
+MFUN64-NEXT:     }
+MFUN64-NEXT:     ProcEnd
 MFUN64:        ]
 MFUN64-NEXT:   Subsection [
 MFUN64-NEXT:     Type: 0xF2
@@ -136,6 +160,12 @@ MFUN64-NEXT:   ]
 MFUN64-NEXT:   Subsection [
 MFUN64-NEXT:     Type: 0xF1
 MFUN64-NEXT:     PayloadSize: 0x4B
+MFUN64:          ProcStart {
+MFUN64-NEXT:       FunctionName: y
+MFUN64-NEXT:       Section: y
+MFUN64-NEXT:       CodeSize: 0xE
+MFUN64-NEXT:     }
+MFUN64-NEXT:     ProcEnd
 MFUN64:        ]
 MFUN64-NEXT:   Subsection [
 MFUN64-NEXT:     Type: 0xF2
@@ -145,6 +175,12 @@ MFUN64-NEXT:   ]
 MFUN64-NEXT:   Subsection [
 MFUN64-NEXT:     Type: 0xF1
 MFUN64-NEXT:     PayloadSize: 0x4B
+MFUN64:          ProcStart {
+MFUN64-NEXT:       FunctionName: f
+MFUN64-NEXT:       Section: f
+MFUN64-NEXT:       CodeSize: 0x18
+MFUN64-NEXT:     }
+MFUN64-NEXT:     ProcEnd
 MFUN64:        ]
 MFUN64-NEXT:   Subsection [
 MFUN64-NEXT:     Type: 0xF2
@@ -234,6 +270,12 @@ MFILE32:        ]
 MFILE32-NEXT:   Subsection [
 MFILE32-NEXT:     Type: 0xF1
 MFILE32-NEXT:     PayloadSize: 0x4B
+MFILE32:          ProcStart {
+MFILE32-NEXT:       FunctionName: f
+MFILE32-NEXT:       Section: _f
+MFILE32-NEXT:       CodeSize: 0x14
+MFILE32-NEXT:     }
+MFILE32-NEXT:     ProcEnd
 MFILE32:        ]
 MFILE32-NEXT:   Subsection [
 MFILE32-NEXT:     Type: 0xF2
@@ -284,6 +326,12 @@ MFILE64:        ]
 MFILE64-NEXT:   Subsection [
 MFILE64-NEXT:     Type: 0xF1
 MFILE64-NEXT:     PayloadSize: 0x4B
+MFILE64:          ProcStart {
+MFILE64-NEXT:       FunctionName: f
+MFILE64-NEXT:       Section: f
+MFILE64-NEXT:       CodeSize: 0x18
+MFILE64-NEXT:     }
+MFILE64-NEXT:     ProcEnd
 MFILE64:        ]
 MFILE64-NEXT:   Subsection [
 MFILE64-NEXT:     Type: 0xF2
@@ -344,23 +392,33 @@ RUN:   | FileCheck %s -check-prefix MCOMDAT
 RUN: llvm-readobj -s -codeview-linetables %p/Inputs/comdat-function-linetables.obj.coff-2013-i386 \
 RUN:   | FileCheck %s -check-prefix MCOMDAT
 
+MCOMDAT:      ProcStart {
+MCOMDAT-NEXT:   FunctionName: f
+MCOMDAT-NEXT:   Section: ?f@@YAHXZ
+MCOMDAT-NEXT:   CodeSize: 0x7
+MCOMDAT-NEXT: }
 MCOMDAT:      FunctionLineTable [
-MCOMDAT-NEXT:        FunctionName: ?f@@YAHXZ
-MCOMDAT-NEXT:        CodeSize: 0x7
-MCOMDAT-NEXT:        FilenameSegment [
-MCOMDAT-NEXT:          Filename: c:\src\test.cc
-MCOMDAT-NEXT:          +0x0: 2
-MCOMDAT-NEXT:          +0x3: 3
-MCOMDAT-NEXT:          +0x5: 4
-MCOMDAT-NEXT:        ]
-MCOMDAT-NEXT:      ]
+MCOMDAT-NEXT:   FunctionName: ?f@@YAHXZ
+MCOMDAT-NEXT:   CodeSize: 0x7
+MCOMDAT-NEXT:   FilenameSegment [
+MCOMDAT-NEXT:     Filename: c:\src\test.cc
+MCOMDAT-NEXT:     +0x0: 2
+MCOMDAT-NEXT:     +0x3: 3
+MCOMDAT-NEXT:     +0x5: 4
+MCOMDAT-NEXT:   ]
+MCOMDAT-NEXT: ]
+MCOMDAT:      ProcStart {
+MCOMDAT-NEXT:   FunctionName: g
+MCOMDAT-NEXT:   Section: ?g@@YAHXZ
+MCOMDAT-NEXT:   CodeSize: 0x7
+MCOMDAT-NEXT: }
 MCOMDAT:      FunctionLineTable [
-MCOMDAT-NEXT:        FunctionName: ?g@@YAHXZ
-MCOMDAT-NEXT:        CodeSize: 0x7
-MCOMDAT-NEXT:        FilenameSegment [
-MCOMDAT-NEXT:          Filename: c:\src\test.cc
-MCOMDAT-NEXT:          +0x0: 7
-MCOMDAT-NEXT:          +0x3: 8
-MCOMDAT-NEXT:          +0x5: 9
-MCOMDAT-NEXT:        ]
-MCOMDAT-NEXT:      ]
+MCOMDAT-NEXT:   FunctionName: ?g@@YAHXZ
+MCOMDAT-NEXT:   CodeSize: 0x7
+MCOMDAT-NEXT:   FilenameSegment [
+MCOMDAT-NEXT:     Filename: c:\src\test.cc
+MCOMDAT-NEXT:     +0x0: 7
+MCOMDAT-NEXT:     +0x3: 8
+MCOMDAT-NEXT:     +0x5: 9
+MCOMDAT-NEXT:   ]
+MCOMDAT-NEXT: ]
index dc280a634dc832703296a49cbf3257665880f619..6242a790f5ad787e8c59272c11d0e40ea0bd0196 100644 (file)
@@ -69,6 +69,10 @@ private:
 
   void printCodeViewLineTables(const SectionRef &Section);
 
+  void printCodeViewSymbolsSubsection(StringRef Subsection,
+                                      const SectionRef &Section,
+                                      uint32_t Offset);
+
   void cacheRelocations();
 
   std::error_code resolveSymbol(const coff_section *Section, uint64_t Offset,
@@ -444,6 +448,7 @@ void COFFDumper::printCodeViewLineTables(const SectionRef &Section) {
 
   ListScope D(W, "CodeViewLineTables");
   {
+    // FIXME: Add more offset correctness checks.
     DataExtractor DE(Data, true, 4);
     uint32_t Offset = 0,
              Magic = DE.getU32(&Offset);
@@ -473,6 +478,9 @@ void COFFDumper::printCodeViewLineTables(const SectionRef &Section) {
       W.printBinaryBlock("Contents", Contents);
 
       switch (SubSectionType) {
+      case COFF::DEBUG_SYMBOL_SUBSECTION:
+        printCodeViewSymbolsSubsection(Contents, Section, Offset);
+        break;
       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
@@ -592,6 +600,80 @@ void COFFDumper::printCodeViewLineTables(const SectionRef &Section) {
   }
 }
 
+void COFFDumper::printCodeViewSymbolsSubsection(StringRef Subsection,
+                                                const SectionRef &Section,
+                                                uint32_t OffsetInSection) {
+  if (Subsection.size() == 0) {
+    error(object_error::parse_failed);
+    return;
+  }
+  DataExtractor DE(Subsection, true, 4);
+  uint32_t Offset = 0;
+
+  // Function-level subsections have "procedure start" and "procedure end"
+  // commands that should come in pairs and surround relevant info.
+  bool InFunctionScope = false;
+  while (DE.isValidOffset(Offset)) {
+    // Read subsection segments one by one.
+    uint16_t Size = DE.getU16(&Offset);
+    // The section size includes the size of the type identifier.
+    if (Size < 2 || !DE.isValidOffsetForDataOfSize(Offset, Size)) {
+      error(object_error::parse_failed);
+      return;
+    }
+    Size -= 2;
+    uint16_t Type = DE.getU16(&Offset);
+    switch (Type) {
+    case COFF::DEBUG_SYMBOL_TYPE_PROC_START: {
+      DictScope S(W, "ProcStart");
+      if (InFunctionScope || Size < 36) {
+        error(object_error::parse_failed);
+        return;
+      }
+      InFunctionScope = true;
+
+      // We're currently interested in a limited subset of fields in this
+      // segment, just ignore the rest of the fields for now.
+      uint8_t Unused[12];
+      DE.getU8(&Offset, Unused, 12);
+      uint32_t CodeSize = DE.getU32(&Offset);
+      DE.getU8(&Offset, Unused, 12);
+      StringRef SectionName;
+      if (error(resolveSymbolName(Obj->getCOFFSection(Section),
+                                  OffsetInSection + Offset, SectionName)))
+        return;
+      Offset += 4;
+      DE.getU8(&Offset, Unused, 3);
+      StringRef FunctionName = DE.getCStr(&Offset);
+      if (!DE.isValidOffset(Offset)) {
+        error(object_error::parse_failed);
+        return;
+      }
+      W.printString("FunctionName", FunctionName);
+      W.printString("Section", SectionName);
+      W.printHex("CodeSize", CodeSize);
+
+      break;
+    }
+    case COFF::DEBUG_SYMBOL_TYPE_PROC_END: {
+      W.startLine() << "ProcEnd\n";
+      if (!InFunctionScope || Size > 0) {
+        error(object_error::parse_failed);
+        return;
+      }
+      InFunctionScope = false;
+      break;
+    }
+    default:
+      Offset += Size;
+      break;
+    }
+  }
+
+  if (InFunctionScope)
+    error(object_error::parse_failed);
+}
+
 void COFFDumper::printSections() {
   ListScope SectionsD(W, "Sections");
   int SectionNumber = 0;