dsymutil: Follow references to clang modules and recursively clone the

debug info.

This does not yet resolve external type references.

llvm-svn: 248331
This commit is contained in:
Adrian Prantl 2015-09-22 22:20:50 +00:00
parent 0a9466c11e
commit e5162dba49
6 changed files with 198 additions and 8 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,4 +1,4 @@
if not 'X86' in config.root.targets: if not 'X86' in config.root.targets:
config.unsupported = True config.unsupported = True
config.suffixes = ['.test', '.cpp', '.s'] config.suffixes = ['.test', '.cpp', '.m', '.s']

View File

@ -0,0 +1,62 @@
/* Compile with:
cat >modules.modulemap <<EOF
module Foo {
header "Foo.h"
export *
}
module Bar {
header "Bar.h"
export *
}
EOF
clang -D BAR_H -E -o Bar.h
clang -D FOO_H -E -o Foo.h
clang -cc1 -emit-obj -fmodules -fmodule-map-file=modules.modulemap \
-fmodule-format=obj -g -dwarf-ext-refs -fmodules-cache-path=. \
-fdisable-module-hash modules.m -o 1.o
*/
// RUN: llvm-dsymutil -f -oso-prepend-path=%p/../Inputs/modules \
// RUN: -y %p/dummy-debug-map.map -o - \
// RUN: | llvm-dwarfdump --debug-dump=info - | FileCheck %s
// ---------------------------------------------------------------------
#ifdef BAR_H
// ---------------------------------------------------------------------
// CHECK: DW_TAG_compile_unit
// CHECK: DW_TAG_module
// CHECK-NEXT: DW_AT_name {{.*}}"Bar"
// CHECK: DW_TAG_member
// CHECK: DW_AT_name {{.*}}"value"
struct Bar {
int value;
};
#else
// ---------------------------------------------------------------------
#ifdef FOO_H
// ---------------------------------------------------------------------
// CHECK: 55{{.*}}DW_TAG_compile_unit
// CHECK: DW_TAG_module
// CHECK-NEXT: DW_AT_name {{.*}}"Foo"
// CHECK: DW_TAG_typedef
@import Bar;
typedef struct Bar Bar;
struct S {};
// ---------------------------------------------------------------------
#else
// ---------------------------------------------------------------------
// CHECK: DW_TAG_compile_unit
// CHECK: DW_TAG_subprogram
// CHECK: DW_AT_name {{.*}}"main"
@import Foo;
int main(int argc, char **argv) {
Bar bar;
bar.value = 42;
return bar.value;
}
#endif
#endif

View File

@ -13,7 +13,7 @@
#include "MachOUtils.h" #include "MachOUtils.h"
#include "NonRelocatableStringpool.h" #include "NonRelocatableStringpool.h"
#include "llvm/ADT/IntervalMap.h" #include "llvm/ADT/IntervalMap.h"
#include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSet.h"
#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/STLExtras.h"
#include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/CodeGen/DIE.h" #include "llvm/CodeGen/DIE.h"
@ -248,6 +248,14 @@ public:
return LocationAttributes; return LocationAttributes;
} }
void setHasInterestingContent() { HasInterestingContent = true; }
bool hasInterestingContent() { return HasInterestingContent; }
/// Mark every DIE in this unit as kept. This function also
/// marks variables as InDebugMap so that they appear in the
/// reconstructed accelerator tables.
void markEverythingAsKept();
/// \brief Compute the end offset for this unit. Must be /// \brief Compute the end offset for this unit. Must be
/// called after the CU's DIEs have been cloned. /// called after the CU's DIEs have been cloned.
/// \returns the next unit offset (which is also the current /// \returns the next unit offset (which is also the current
@ -368,8 +376,15 @@ private:
/// Is this unit subject to the ODR rule? /// Is this unit subject to the ODR rule?
bool HasODR; bool HasODR;
/// Did a DIE actually contain a valid reloc?
bool HasInterestingContent;
}; };
void CompileUnit::markEverythingAsKept() {
for (auto &I : Info)
I.Keep = true;
}
uint64_t CompileUnit::computeNextUnitOffset() { uint64_t CompileUnit::computeNextUnitOffset() {
NextUnitOffset = StartOffset + 11 /* Header size */; NextUnitOffset = StartOffset + 11 /* Header size */;
// The root DIE might be null, meaning that the Unit had nothing to // The root DIE might be null, meaning that the Unit had nothing to
@ -1177,6 +1192,22 @@ private:
const DebugMapObject &DMO, CompileUnit &CU, const DebugMapObject &DMO, CompileUnit &CU,
unsigned Flags); unsigned Flags);
/// If this compile unit is really a skeleton CU that points to a
/// clang module, register it in ClangModules and return true.
///
/// A skeleton CU is a CU without children, a DW_AT_gnu_dwo_name
/// pointing to the module, and a DW_AT_gnu_dwo_id with the module
/// hash.
bool registerModuleReference(const DWARFDebugInfoEntryMinimal &CUDie,
const DWARFUnit &Unit, DebugMap &ModuleMap,
unsigned Indent = 0);
/// Recursively add the debug info in this clang module .pcm
/// file (and all the modules imported by it in a bottom-up fashion)
/// to Units.
void loadClangModule(StringRef Filename, StringRef ModulePath,
DebugMap &ModuleMap, unsigned Indent = 0);
/// \brief Flags passed to DwarfLinker::lookForDIEsToKeep /// \brief Flags passed to DwarfLinker::lookForDIEsToKeep
enum TravesalFlags { enum TravesalFlags {
TF_Keep = 1 << 0, ///< Mark the traversed DIEs as kept. TF_Keep = 1 << 0, ///< Mark the traversed DIEs as kept.
@ -1383,12 +1414,12 @@ private:
const DebugMap &Map); const DebugMap &Map);
/// @} /// @}
private:
std::string OutputFilename; std::string OutputFilename;
LinkOptions Options; LinkOptions Options;
BinaryHolder BinHolder; BinaryHolder BinHolder;
std::unique_ptr<DwarfStreamer> Streamer; std::unique_ptr<DwarfStreamer> Streamer;
uint64_t OutputDebugInfoSize; uint64_t OutputDebugInfoSize;
unsigned UnitID; ///< A unique ID that identifies each compile unit.
/// The units of the current debug map object. /// The units of the current debug map object.
std::vector<CompileUnit> Units; std::vector<CompileUnit> Units;
@ -1416,6 +1447,9 @@ private:
/// Offset of the last CIE that has been emitted in the output /// Offset of the last CIE that has been emitted in the output
/// debug_frame section. /// debug_frame section.
uint32_t LastCIEOffset; uint32_t LastCIEOffset;
/// FIXME: We may need to use something more resilient than the PCM filename.
StringSet<> ClangModules;
}; };
/// Similar to DWARFUnitSection::getUnitForOffset(), but returning our /// Similar to DWARFUnitSection::getUnitForOffset(), but returning our
@ -2588,7 +2622,13 @@ DIE *DwarfLinker::DIECloner::cloneDIE(
// Extract and clone every attribute. // Extract and clone every attribute.
DataExtractor Data = U.getDebugInfoExtractor(); DataExtractor Data = U.getDebugInfoExtractor();
uint32_t NextOffset = U.getDIEAtIndex(Idx + 1)->getOffset(); // Point to the next DIE (generally there is always at least a NULL
// entry after the current one). If this is a lone
// DW_TAG_compile_unit without any children, point to the next unit.
uint32_t NextOffset =
(Idx + 1 < U.getNumDIEs())
? U.getDIEAtIndex(Idx + 1)->getOffset()
: U.getNextUnitOffset();
AttributesInfo AttrInfo; AttributesInfo AttrInfo;
// We could copy the data only if we need to aply a relocation to // We could copy the data only if we need to aply a relocation to
@ -3051,6 +3091,38 @@ void DwarfLinker::DIECloner::copyAbbrev(
Linker.AssignAbbrev(Copy); Linker.AssignAbbrev(Copy);
} }
bool DwarfLinker::registerModuleReference(
const DWARFDebugInfoEntryMinimal &CUDie, const DWARFUnit &Unit,
DebugMap &ModuleMap, unsigned Indent) {
std::string PCMfile =
CUDie.getAttributeValueAsString(&Unit, dwarf::DW_AT_GNU_dwo_name, "");
if (PCMfile.empty())
return false;
// Clang module DWARF skeleton CUs abuse this for the path to the module.
std::string PCMpath =
CUDie.getAttributeValueAsString(&Unit, dwarf::DW_AT_comp_dir, "");
if (Options.Verbose) {
outs().indent(Indent);
outs() << "Found clang module reference " << PCMfile;
}
if (ClangModules.count(PCMfile)) {
if (Options.Verbose)
outs() << " [cached].\n";
return true;
}
if (Options.Verbose)
outs() << " ...\n";
// Cyclic dependencies are disallowed by Clang, but we still
// shouldn't run into an infinite loop, so mark it as processed now.
ClangModules.insert(PCMfile);
loadClangModule(PCMfile, PCMpath, ModuleMap, Indent + 2);
return true;
}
ErrorOr<const object::ObjectFile &> ErrorOr<const object::ObjectFile &>
DwarfLinker::loadObject(BinaryHolder &BinaryHolder, DebugMapObject &Obj, DwarfLinker::loadObject(BinaryHolder &BinaryHolder, DebugMapObject &Obj,
const DebugMap &Map) { const DebugMap &Map) {
@ -3066,6 +3138,58 @@ DwarfLinker::loadObject(BinaryHolder &BinaryHolder, DebugMapObject &Obj,
return ErrOrObj; return ErrOrObj;
} }
void DwarfLinker::loadClangModule(StringRef Filename, StringRef ModulePath,
DebugMap &ModuleMap, unsigned Indent) {
SmallString<80> Path(Options.PrependPath);
if (sys::path::is_relative(Filename))
sys::path::append(Path, ModulePath, Filename);
else
sys::path::append(Path, Filename);
BinaryHolder ObjHolder(Options.Verbose);
auto &Obj =
ModuleMap.addDebugMapObject(Path, sys::TimeValue::PosixZeroTime());
auto ErrOrObj = loadObject(ObjHolder, Obj, ModuleMap);
if (!ErrOrObj) {
ClangModules.erase(ClangModules.find(Filename));
return;
}
// FIXME: At this point dsymutil should verify the DW_AT_gnu_dwo_id
// against the module hash of the clang module.
CompileUnit *Unit = nullptr;
// Setup access to the debug info.
DWARFContextInMemory DwarfContext(*ErrOrObj);
RelocationManager RelocMgr(*this);
for (const auto &CU : DwarfContext.compile_units()) {
auto *CUDie = CU->getUnitDIE(false);
// Recursively get all modules imported by this one.
if (!registerModuleReference(*CUDie, *CU, ModuleMap, Indent)) {
// Add this module.
if (Unit) {
errs() << Filename << ": Clang modules are expected to have exactly"
<< " 1 compile unit.\n";
exitDsymutil(1);
}
Unit = new CompileUnit(*CU, UnitID++, !Options.NoODR);
Unit->setHasInterestingContent();
gatherDIEParents(CUDie, 0, *Unit, &ODRContexts.getRoot(), StringPool,
ODRContexts);
// Keep everything.
Unit->markEverythingAsKept();
}
}
if (Options.Verbose) {
outs().indent(Indent);
outs() << "cloning .debug_info from " << Filename << "\n";
}
DIECloner(*this, RelocMgr, DIEAlloc, MutableArrayRef<CompileUnit>(*Unit),
Options)
.cloneAllCompileUnits(DwarfContext);
}
void DwarfLinker::DIECloner::cloneAllCompileUnits( void DwarfLinker::DIECloner::cloneAllCompileUnits(
DWARFContextInMemory &DwarfContext) { DWARFContextInMemory &DwarfContext) {
if (!Linker.Streamer) if (!Linker.Streamer)
@ -3113,7 +3237,9 @@ bool DwarfLinker::link(const DebugMap &Map) {
// Size of the DIEs (and headers) generated for the linked output. // Size of the DIEs (and headers) generated for the linked output.
OutputDebugInfoSize = 0; OutputDebugInfoSize = 0;
// A unique ID that identifies each compile unit. // A unique ID that identifies each compile unit.
unsigned UnitID = 0; UnitID = 0;
DebugMap ModuleMap(Map.getTriple(), Map.getBinaryPath());
for (const auto &Obj : Map.objects()) { for (const auto &Obj : Map.objects()) {
CurrentDebugObject = Obj.get(); CurrentDebugObject = Obj.get();
@ -3143,9 +3269,11 @@ bool DwarfLinker::link(const DebugMap &Map) {
outs() << "Input compilation unit:"; outs() << "Input compilation unit:";
CUDie->dump(outs(), CU.get(), 0); CUDie->dump(outs(), CU.get(), 0);
} }
Units.emplace_back(*CU, UnitID++, !Options.NoODR); if (!registerModuleReference(*CUDie, *CU, ModuleMap)) {
gatherDIEParents(CUDie, 0, Units.back(), &ODRContexts.getRoot(), Units.emplace_back(*CU, UnitID++, !Options.NoODR);
StringPool, ODRContexts); gatherDIEParents(CUDie, 0, Units.back(), &ODRContexts.getRoot(),
StringPool, ODRContexts);
}
} }
// Then mark all the DIEs that need to be present in the linked // Then mark all the DIEs that need to be present in the linked