[lld] r342080 - [PDB] Emit old fpo data to the PDB file.

Zachary Turner via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 12 14:02:01 PDT 2018


Author: zturner
Date: Wed Sep 12 14:02:01 2018
New Revision: 342080

URL: http://llvm.org/viewvc/llvm-project?rev=342080&view=rev
Log:
[PDB] Emit old fpo data to the PDB file.

r342003 added support for emitting FPO data from the
DEBUG_S_FRAMEDATA subsection of the .debug$S section to the PDB
file.  However, that is not the end of the story.  FPO can end
up in two different destinations in a PDB, each corresponding to
a different FPO data source.

The case handled by r342003 involves copying data from the
DEBUG_S_FRAMEDATA subsection of the .debug$S section to the
"New FPO" stream in the PDB, which is then referred to by the
DBI stream.  The case handled by this patch involves copying
records from the .debug$F section of an object file to the "FPO"
stream (or perhaps more aptly, the "Old FPO" stream) in the PDB
file, which is also referred to by the DBI stream.

The formats are largely similar, and the difference is mostly
only visible in masm generated object files, such as some of the
low-level CRT object files like memcpy.  MASM doesn't appear to
support writing the DEBUG_S_FRAMEDATA subsection, and instead
just writes these records to the .debug$F section.

Although clang-cl does not emit a .debug$F section ever, lld still
needs to support it so we have good debugging for CRT functions.

Differential Revision: https://reviews.llvm.org/D51958

Added:
    lld/trunk/test/COFF/pdb-debug-f.s
Modified:
    lld/trunk/COFF/PDB.cpp

Modified: lld/trunk/COFF/PDB.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/PDB.cpp?rev=342080&r1=342079&r2=342080&view=diff
==============================================================================
--- lld/trunk/COFF/PDB.cpp (original)
+++ lld/trunk/COFF/PDB.cpp Wed Sep 12 14:02:01 2018
@@ -82,7 +82,11 @@ struct CVIndexMap {
   bool IsTypeServerMap = false;
 };
 
+class DebugSHandler;
+
 class PDBLinker {
+  friend DebugSHandler;
+
 public:
   PDBLinker(SymbolTable *Symtab)
       : Alloc(), Symtab(Symtab), Builder(Alloc), TypeTable(Alloc),
@@ -168,6 +172,50 @@ private:
   /// Cached to prevent repeated load attempts.
   std::map<GUID, std::string> MissingTypeServerPDBs;
 };
+
+class DebugSHandler {
+  PDBLinker &Linker;
+
+  /// The object file whose .debug$S sections we're processing.
+  ObjFile &File;
+
+  /// The result of merging type indices.
+  const CVIndexMap &IndexMap;
+
+  /// The DEBUG_S_STRINGTABLE subsection.  These strings are referred to by
+  /// index from other records in the .debug$S section.  All of these strings
+  /// need to be added to the global PDB string table, and all references to
+  /// these strings need to have their indices re-written to refer to the
+  /// global PDB string table.
+  DebugStringTableSubsectionRef CVStrTab;
+
+  /// The DEBUG_S_FILECHKSMS subsection.  As above, these are referred to
+  /// by other records in the .debug$S section and need to be merged into the
+  /// PDB.
+  DebugChecksumsSubsectionRef Checksums;
+
+  /// The DEBUG_S_FRAMEDATA subsection(s).  There can be more than one of
+  /// these and they need not appear in any specific order.  However, they
+  /// contain string table references which need to be re-written, so we
+  /// collect them all here and re-write them after all subsections have been
+  /// discovered and processed.
+  std::vector<DebugFrameDataSubsectionRef> NewFpoFrames;
+
+  /// Pointers to raw memory that we determine have string table references
+  /// that need to be re-written.  We first process all .debug$S subsections
+  /// to ensure that we can handle subsections written in any order, building
+  /// up this list as we go.  At the end, we use the string table (which must
+  /// have been discovered by now else it is an error) to re-write these
+  /// references.
+  std::vector<ulittle32_t *> StringTableReferences;
+
+public:
+  DebugSHandler(PDBLinker &Linker, ObjFile &File, const CVIndexMap &IndexMap)
+      : Linker(Linker), File(File), IndexMap(IndexMap) {}
+
+  void handleDebugS(lld::coff::SectionChunk &DebugS);
+  void finish();
+};
 }
 
 static SectionChunk *findByName(ArrayRef<SectionChunk *> Sections,
@@ -785,15 +833,14 @@ static void mergeSymbolRecords(BumpPtrAl
   cantFail(std::move(EC));
 }
 
-// Allocate memory for a .debug$S section and relocate it.
+// Allocate memory for a .debug$S / .debug$F section and relocate it.
 static ArrayRef<uint8_t> relocateDebugChunk(BumpPtrAllocator &Alloc,
-                                            SectionChunk *DebugChunk) {
-  uint8_t *Buffer = Alloc.Allocate<uint8_t>(DebugChunk->getSize());
-  assert(DebugChunk->OutputSectionOff == 0 &&
+                                            SectionChunk &DebugChunk) {
+  uint8_t *Buffer = Alloc.Allocate<uint8_t>(DebugChunk.getSize());
+  assert(DebugChunk.OutputSectionOff == 0 &&
          "debug sections should not be in output sections");
-  DebugChunk->writeTo(Buffer);
-  return consumeDebugMagic(makeArrayRef(Buffer, DebugChunk->getSize()),
-                           ".debug$S");
+  DebugChunk.writeTo(Buffer);
+  return makeArrayRef(Buffer, DebugChunk.getSize());
 }
 
 static pdb::SectionContrib createSectionContrib(const Chunk *C, uint32_t Modi) {
@@ -836,6 +883,114 @@ translateStringTableIndex(uint32_t ObjIn
   return PdbStrTable.insert(*ExpectedString);
 }
 
+void DebugSHandler::handleDebugS(lld::coff::SectionChunk &DebugS) {
+  DebugSubsectionArray Subsections;
+
+  ArrayRef<uint8_t> RelocatedDebugContents = consumeDebugMagic(
+      relocateDebugChunk(Linker.Alloc, DebugS), DebugS.getSectionName());
+
+  BinaryStreamReader Reader(RelocatedDebugContents, support::little);
+  ExitOnErr(Reader.readArray(Subsections, RelocatedDebugContents.size()));
+
+  for (const DebugSubsectionRecord &SS : Subsections) {
+    switch (SS.kind()) {
+    case DebugSubsectionKind::StringTable: {
+      assert(!CVStrTab.valid() &&
+             "Encountered multiple string table subsections!");
+      ExitOnErr(CVStrTab.initialize(SS.getRecordData()));
+      break;
+    }
+    case DebugSubsectionKind::FileChecksums:
+      assert(!Checksums.valid() &&
+             "Encountered multiple checksum subsections!");
+      ExitOnErr(Checksums.initialize(SS.getRecordData()));
+      break;
+    case DebugSubsectionKind::Lines:
+      // We can add the relocated line table directly to the PDB without
+      // modification because the file checksum offsets will stay the same.
+      File.ModuleDBI->addDebugSubsection(SS);
+      break;
+    case DebugSubsectionKind::FrameData: {
+      // We need to re-write string table indices here, so save off all
+      // frame data subsections until we've processed the entire list of
+      // subsections so that we can be sure we have the string table.
+      DebugFrameDataSubsectionRef FDS;
+      ExitOnErr(FDS.initialize(SS.getRecordData()));
+      NewFpoFrames.push_back(std::move(FDS));
+      break;
+    }
+    case DebugSubsectionKind::Symbols:
+      if (Config->DebugGHashes) {
+        mergeSymbolRecords(Linker.Alloc, &File, Linker.Builder.getGsiBuilder(),
+                           IndexMap, Linker.GlobalIDTable,
+                           StringTableReferences, SS.getRecordData());
+      } else {
+        mergeSymbolRecords(Linker.Alloc, &File, Linker.Builder.getGsiBuilder(),
+                           IndexMap, Linker.IDTable, StringTableReferences,
+                           SS.getRecordData());
+      }
+      break;
+    default:
+      // FIXME: Process the rest of the subsections.
+      break;
+    }
+  }
+}
+
+void DebugSHandler::finish() {
+  pdb::DbiStreamBuilder &DbiBuilder = Linker.Builder.getDbiBuilder();
+
+  // We should have seen all debug subsections across the entire object file now
+  // which means that if a StringTable subsection and Checksums subsection were
+  // present, now is the time to handle them.
+  if (!CVStrTab.valid()) {
+    if (Checksums.valid())
+      fatal(".debug$S sections with a checksums subsection must also contain a "
+            "string table subsection");
+
+    if (!StringTableReferences.empty())
+      warn("No StringTable subsection was encountered, but there are string "
+           "table references");
+    return;
+  }
+
+  // Rewrite string table indices in the Fpo Data and symbol records to refer to
+  // the global PDB string table instead of the object file string table.
+  for (DebugFrameDataSubsectionRef &FDS : NewFpoFrames) {
+    const uint32_t *Reloc = FDS.getRelocPtr();
+    for (codeview::FrameData FD : FDS) {
+      FD.RvaStart += *Reloc;
+      FD.FrameFunc =
+          translateStringTableIndex(FD.FrameFunc, CVStrTab, Linker.PDBStrTab);
+      DbiBuilder.addNewFpoData(FD);
+    }
+  }
+
+  for (ulittle32_t *Ref : StringTableReferences)
+    *Ref = translateStringTableIndex(*Ref, CVStrTab, Linker.PDBStrTab);
+
+  // Make a new file checksum table that refers to offsets in the PDB-wide
+  // string table. Generally the string table subsection appears after the
+  // checksum table, so we have to do this after looping over all the
+  // subsections.
+  auto NewChecksums = make_unique<DebugChecksumsSubsection>(Linker.PDBStrTab);
+  for (FileChecksumEntry &FC : Checksums) {
+    SmallString<128> FileName =
+        ExitOnErr(CVStrTab.getString(FC.FileNameOffset));
+    if (!sys::path::is_absolute(FileName) && !Config->PDBSourcePath.empty()) {
+      SmallString<128> AbsoluteFileName = Config->PDBSourcePath;
+      sys::path::append(AbsoluteFileName, FileName);
+      sys::path::native(AbsoluteFileName);
+      sys::path::remove_dots(AbsoluteFileName, /*remove_dot_dots=*/true);
+      FileName = std::move(AbsoluteFileName);
+    }
+    ExitOnErr(Linker.Builder.getDbiBuilder().addModuleSourceFile(
+        *File.ModuleDBI, FileName));
+    NewChecksums->addChecksum(FileName, FC.Kind, FC.Checksum);
+  }
+  File.ModuleDBI->addDebugSubsection(std::move(NewChecksums));
+}
+
 void PDBLinker::addObjFile(ObjFile *File) {
   // Add a module descriptor for every object file. We need to put an absolute
   // path to the object into the PDB. If this is a plain object, we make its
@@ -878,122 +1033,38 @@ void PDBLinker::addObjFile(ObjFile *File
     return;
   }
 
-  const CVIndexMap &IndexMap = *IndexMapResult;
-
   ScopedTimer T(SymbolMergingTimer);
 
-  // Now do all live .debug$S sections.
-  DebugStringTableSubsectionRef CVStrTab;
-  DebugChecksumsSubsectionRef Checksums;
-  std::vector<ulittle32_t *> StringTableReferences;
-  std::vector<DebugFrameDataSubsectionRef> FpoFrames;
+  DebugSHandler DSH(*this, *File, *IndexMapResult);
+  // Now do all live .debug$S and .debug$F sections.
   for (SectionChunk *DebugChunk : File->getDebugChunks()) {
-    if (!DebugChunk->Live || DebugChunk->getSectionName() != ".debug$S")
+    if (!DebugChunk->Live || DebugChunk->getSize() == 0)
       continue;
 
-    ArrayRef<uint8_t> RelocatedDebugContents =
-        relocateDebugChunk(Alloc, DebugChunk);
-    if (RelocatedDebugContents.empty())
+    if (DebugChunk->getSectionName() == ".debug$S") {
+      DSH.handleDebugS(*DebugChunk);
       continue;
-
-    DebugSubsectionArray Subsections;
-    BinaryStreamReader Reader(RelocatedDebugContents, support::little);
-    ExitOnErr(Reader.readArray(Subsections, RelocatedDebugContents.size()));
-
-    for (const DebugSubsectionRecord &SS : Subsections) {
-      switch (SS.kind()) {
-      case DebugSubsectionKind::StringTable: {
-        assert(!CVStrTab.valid() &&
-               "Encountered multiple string table subsections!");
-        ExitOnErr(CVStrTab.initialize(SS.getRecordData()));
-        break;
-      }
-      case DebugSubsectionKind::FileChecksums:
-        assert(!Checksums.valid() &&
-               "Encountered multiple checksum subsections!");
-        ExitOnErr(Checksums.initialize(SS.getRecordData()));
-        break;
-      case DebugSubsectionKind::Lines:
-        // We can add the relocated line table directly to the PDB without
-        // modification because the file checksum offsets will stay the same.
-        File->ModuleDBI->addDebugSubsection(SS);
-        break;
-      case DebugSubsectionKind::FrameData: {
-        // We need to re-write string table indices here, so save off all
-        // frame data subsections until we've processed the entire list of
-        // subsections so that we can be sure we have the string table.
-        DebugFrameDataSubsectionRef FDS;
-        ExitOnErr(FDS.initialize(SS.getRecordData()));
-        FpoFrames.push_back(std::move(FDS));
-        break;
-      }
-      case DebugSubsectionKind::Symbols:
-        if (Config->DebugGHashes) {
-          mergeSymbolRecords(Alloc, File, Builder.getGsiBuilder(), IndexMap,
-                             GlobalIDTable, StringTableReferences,
-                             SS.getRecordData());
-        } else {
-          mergeSymbolRecords(Alloc, File, Builder.getGsiBuilder(), IndexMap,
-                             IDTable, StringTableReferences,
-                             SS.getRecordData());
-        }
-        break;
-      default:
-        // FIXME: Process the rest of the subsections.
-        break;
-      }
     }
-  }
-
-  // We should have seen all debug subsections across the entire object file now
-  // which means that if a StringTable subsection and Checksums subsection were
-  // present, now is the time to handle them.
-  if (!CVStrTab.valid()) {
-    if (Checksums.valid())
-      fatal(".debug$S sections with a checksums subsection must also contain a "
-            "string table subsection");
 
-    if (!StringTableReferences.empty())
-      warn("No StringTable subsection was encountered, but there are string "
-           "table references");
-    return;
-  }
-
-  // Rewrite string table indices in the Fpo Data and symbol records to refer to
-  // the global PDB string table instead of the object file string table.
-  for (DebugFrameDataSubsectionRef &FDS : FpoFrames) {
-    const uint32_t *Reloc = FDS.getRelocPtr();
-    for (codeview::FrameData FD : FDS) {
-      FD.RvaStart += *Reloc;
-      FD.FrameFunc =
-          translateStringTableIndex(FD.FrameFunc, CVStrTab, PDBStrTab);
-      DbiBuilder.addFrameData(FD);
+    if (DebugChunk->getSectionName() == ".debug$F") {
+      ArrayRef<uint8_t> RelocatedDebugContents =
+          relocateDebugChunk(Alloc, *DebugChunk);
+
+      FixedStreamArray<object::FpoData> FpoRecords;
+      BinaryStreamReader Reader(RelocatedDebugContents, support::little);
+      uint32_t Count = RelocatedDebugContents.size() / sizeof(object::FpoData);
+      ExitOnErr(Reader.readArray(FpoRecords, Count));
+
+      // These are already relocated and don't refer to the string table, so we
+      // can just copy it.
+      for (const object::FpoData &FD : FpoRecords)
+        DbiBuilder.addOldFpoData(FD);
+      continue;
     }
   }
 
-  for (ulittle32_t *Ref : StringTableReferences)
-    *Ref = translateStringTableIndex(*Ref, CVStrTab, PDBStrTab);
-
-  // Make a new file checksum table that refers to offsets in the PDB-wide
-  // string table. Generally the string table subsection appears after the
-  // checksum table, so we have to do this after looping over all the
-  // subsections.
-  auto NewChecksums = make_unique<DebugChecksumsSubsection>(PDBStrTab);
-  for (FileChecksumEntry &FC : Checksums) {
-    SmallString<128> FileName = ExitOnErr(CVStrTab.getString(FC.FileNameOffset));
-    if (!sys::path::is_absolute(FileName) &&
-        !Config->PDBSourcePath.empty()) {
-      SmallString<128> AbsoluteFileName = Config->PDBSourcePath;
-      sys::path::append(AbsoluteFileName, FileName);
-      sys::path::native(AbsoluteFileName);
-      sys::path::remove_dots(AbsoluteFileName, /*remove_dot_dots=*/true);
-      FileName = std::move(AbsoluteFileName);
-    }
-    ExitOnErr(Builder.getDbiBuilder().addModuleSourceFile(*File->ModuleDBI,
-                                                          FileName));
-    NewChecksums->addChecksum(FileName, FC.Kind, FC.Checksum);
-  }
-  File->ModuleDBI->addDebugSubsection(std::move(NewChecksums));
+  // Do any post-processing now that all .debug$S sections have been processed.
+  DSH.finish();
 }
 
 static PublicSym32 createPublic(Defined *Def) {

Added: lld/trunk/test/COFF/pdb-debug-f.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/pdb-debug-f.s?rev=342080&view=auto
==============================================================================
--- lld/trunk/test/COFF/pdb-debug-f.s (added)
+++ lld/trunk/test/COFF/pdb-debug-f.s Wed Sep 12 14:02:01 2018
@@ -0,0 +1,27 @@
+# RUN: llvm-mc -triple=i386-pc-win32 -filetype=obj -o %t.obj %s
+# RUN: lld-link /subsystem:console /debug /nodefaultlib /entry:foo /out:%t.exe /pdb:%t.pdb %t.obj
+# RUN: llvm-pdbutil dump -fpo %t.pdb | FileCheck %s
+
+# CHECK:                         Old FPO Data
+# CHECK-NEXT: ============================================================
+# CHECK-NEXT:   RVA    | Code | Locals | Params | Prolog | Saved Regs | Use BP | Has SEH | Frame Type
+# CHECK-NEXT: 00001002 |    1 |      2 |      3 |      4 |          0 |  false |   false |       FPO
+
+.text
+_foo:
+ret
+
+.global _foo
+
+.section .debug$F,"dr"
+	.long _foo at IMGREL+2
+	.long 1 #  cbProc
+	.long 2 # cdwLocals;
+	.short 3 # cdwParams;
+	.short 4 # flags
+  # cbProlog : 8;
+  # cbRegs : 3;
+  # fHasSEH : 1;
+  # fUseBP : 1;
+  # reserved : 1;
+  # cbFrame : 2;
\ No newline at end of file




More information about the llvm-commits mailing list