Index: test/tools/dsymutil/X86/modules.map =================================================================== --- /dev/null +++ test/tools/dsymutil/X86/modules.map @@ -0,0 +1,8 @@ +--- +triple: 'x86_64-apple-darwin' +objects: + - filename: /Inputs/modules.macho.x86_64 + timestamp: 1442276341 + symbols: + - { sym: _main, objAddr: 0x0000000000000000, binAddr: 0x0000000100000F90, size: 0x0000001E } +... Index: test/tools/dsymutil/X86/modules.test =================================================================== --- /dev/null +++ test/tools/dsymutil/X86/modules.test @@ -0,0 +1,43 @@ +RUN: rm -rf %T/modules +RUN: mkdir -p %T/modules +RUN: cat %p/modules.map > %T/modules/modules.map +RUN: llvm-dsymutil -y -f -oso-prepend-path=%p/.. %T/modules/modules.map -o %t.dSYM +RUN: llvm-dwarfdump --debug-dump=info %t.dSYM | FileCheck %s + + +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; +}; + +Foo.h +----- +CHECK: DW_TAG_compile_unit +CHECK: DW_TAG_module +CHECK-NEXT: DW_AT_name{{.*}}"Foo" +CHECK: DW_TAG_typedef +#include "Bar.h" +typedef struct Bar Bar; + +modules.c +--------- + +CHECK: DW_TAG_compile_unit +CHECK: DW_TAG_subprogram +CHECK: DW_AT_name {{.*}}"main" +// clang -cc1 -emit-obj -fmodules -fimplicit-module-maps -fmodule-format=obj -dwarf-ext-refs -fmodules-cache-path=. -fdisable-module-hash modules.c +// /usr/bin/ld -demangle -dynamic -arch x86_64 -macosx_version_min 10.10.0 -o modules modules.o -lSystem +#include "Foo.h" +int main(int argc, char **argv) { + Bar bar; + bar.value = 42; + return bar.value; +} + Index: tools/dsymutil/DwarfLinker.cpp =================================================================== --- tools/dsymutil/DwarfLinker.cpp +++ tools/dsymutil/DwarfLinker.cpp @@ -248,6 +248,14 @@ 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 /// called after the CU's DIEs have been cloned. /// \returns the next unit offset (which is also the current @@ -368,8 +376,46 @@ /// Is this unit subject to the ODR rule? bool HasODR; + /// Did a DIE actually contain a valid reloc? + bool HasInterestingContent; }; +static bool inFunctionScope(CompileUnit &U, unsigned Idx) { + if (Idx == 0) + return false; + if (U.getOrigUnit().getDIEAtIndex(Idx)->getTag() == dwarf::DW_TAG_subprogram) + return true; + return inFunctionScope(U, U.getInfo(Idx).ParentIdx); +} + +void CompileUnit::markEverythingAsKept() { + unsigned Idx = 0; + + for (auto &I : Info) { + I.Keep = true; + const auto *DIE = OrigUnit.getDIEAtIndex(Idx++); + + // Try to guess which DIEs must go to the accelerator tables. We + // do that just for variables, because functions will be handled + // depending on wether they carry a DW_AT_low_pc attribute or not. + if (DIE->getTag() != dwarf::DW_TAG_variable && + DIE->getTag() != dwarf::DW_TAG_constant) + continue; + DWARFFormValue Value; + if (!DIE->getAttributeValue(&OrigUnit, dwarf::DW_AT_location, Value)) { + if (DIE->getAttributeValue(&OrigUnit, dwarf::DW_AT_const_value, Value) && + !inFunctionScope(*this, I.ParentIdx)) + I.InDebugMap = true; + continue; + } + if (auto Block = Value.getAsBlock()) { + if (Block->size() >= OrigUnit.getAddressByteSize() + 1 && + (*Block)[0] == dwarf::DW_OP_addr) + I.InDebugMap = true; + } + } +} + uint64_t CompileUnit::computeNextUnitOffset() { NextUnitOffset = StartOffset + 11 /* Header size */; // The root DIE might be null, meaning that the Unit had nothing to @@ -1096,6 +1142,9 @@ /// \brief Link the contents of the DebugMap. bool link(const DebugMap &); + void reportWarning(const Twine &Warning, const DWARFUnit *Unit = nullptr, + const DWARFDebugInfoEntryMinimal *DIE = nullptr) const; + private: /// \brief Called at the start of a debug object link. void startDebugObject(DWARFContext &, DebugMapObject &); @@ -1174,6 +1223,18 @@ const DebugMapObject &DMO, CompileUnit &CU, unsigned Flags); + /// If this CU is really a skeleton clang module reference, + /// register it in ClangModules and return true. + 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 enum TravesalFlags { TF_Keep = 1 << 0, ///< Mark the traversed DIEs as kept. @@ -1372,16 +1433,6 @@ /// \defgroup Helpers Various helper methods. /// /// @{ - const DWARFDebugInfoEntryMinimal * - resolveDIEReference(const DWARFFormValue &RefValue, const DWARFUnit &Unit, - const DWARFDebugInfoEntryMinimal &DIE, - CompileUnit *&ReferencedCU); - - CompileUnit *getUnitForOffset(unsigned Offset); - - void reportWarning(const Twine &Warning, const DWARFUnit *Unit = nullptr, - const DWARFDebugInfoEntryMinimal *DIE = nullptr) const; - bool createStreamer(Triple TheTriple, StringRef OutputFilename); /// \brief Attempt to load a debug object from disk. @@ -1390,12 +1441,12 @@ const DebugMap &Map); /// @} -private: std::string OutputFilename; LinkOptions Options; BinaryHolder BinHolder; std::unique_ptr Streamer; uint64_t OutputDebugInfoSize; + unsigned UnitID; ///< A unique ID that identifies each compile unit. /// The units of the current debug map object. std::vector Units; @@ -1423,11 +1474,15 @@ /// Offset of the last CIE that has been emitted in the output /// debug_frame section. uint32_t LastCIEOffset; + + /// FIXME: We may need to use something more resilient than the PCM filename. + StringMap ClangModules; }; -/// \brief Similar to DWARFUnitSection::getUnitForOffset(), but -/// returning our CompileUnit object instead. -CompileUnit *DwarfLinker::getUnitForOffset(unsigned Offset) { +/// Similar to DWARFUnitSection::getUnitForOffset(), but returning our +/// CompileUnit object instead. +static CompileUnit *getUnitForOffset(MutableArrayRef Units, + unsigned Offset) { auto CU = std::upper_bound(Units.begin(), Units.end(), Offset, [](uint32_t LHS, const CompileUnit &RHS) { @@ -1436,21 +1491,22 @@ return CU != Units.end() ? &*CU : nullptr; } -/// \brief Resolve the DIE attribute reference that has been +/// Resolve the DIE attribute reference that has been /// extracted in \p RefValue. The resulting DIE migh be in another /// CompileUnit which is stored into \p ReferencedCU. /// \returns null if resolving fails for any reason. -const DWARFDebugInfoEntryMinimal *DwarfLinker::resolveDIEReference( +static const DWARFDebugInfoEntryMinimal *resolveDIEReference( + const DwarfLinker &Linker, MutableArrayRef Units, const DWARFFormValue &RefValue, const DWARFUnit &Unit, const DWARFDebugInfoEntryMinimal &DIE, CompileUnit *&RefCU) { assert(RefValue.isFormClass(DWARFFormValue::FC_Reference)); uint64_t RefOffset = *RefValue.getAsReference(&Unit); - if ((RefCU = getUnitForOffset(RefOffset))) + if ((RefCU = getUnitForOffset(Units, RefOffset))) if (const auto *RefDie = RefCU->getOrigUnit().getDIEForOffset(RefOffset)) return RefDie; - reportWarning("could not find referenced DIE", &Unit, &DIE); + Linker.reportWarning("could not find referenced DIE", &Unit, &DIE); return nullptr; } @@ -2102,7 +2158,8 @@ Val.extractValue(Data, &Offset, &Unit); CompileUnit *ReferencedCU; if (const auto *RefDIE = - resolveDIEReference(Val, Unit, Die, ReferencedCU)) { + resolveDIEReference(*this, MutableArrayRef(Units), + Val, Unit, Die, ReferencedCU)) { uint32_t RefIdx = ReferencedCU->getOrigUnit().getDIEIndex(RefDIE); CompileUnit::DIEInfo &Info = ReferencedCU->getInfo(RefIdx); // If the referenced DIE has a DeclContext that has already been @@ -2231,7 +2288,7 @@ DeclContext *Ctxt = nullptr; const DWARFDebugInfoEntryMinimal *RefDie = - Linker.resolveDIEReference(Val, U, InputDIE, RefUnit); + resolveDIEReference(Linker, CompileUnits, Val, U, InputDIE, RefUnit); // If the referenced DIE is not found, drop the attribute. if (!RefDie) @@ -2592,7 +2649,10 @@ // Extract and clone every attribute. DataExtractor Data = U.getDebugInfoExtractor(); - uint32_t NextOffset = U.getDIEAtIndex(Idx + 1)->getOffset(); + uint32_t NextOffset = + (Idx + 1 < U.getNumDIEs()) + ? U.getDIEAtIndex(Idx + 1)->getOffset() + : U.getNextUnitOffset(); AttributesInfo AttrInfo; // We could copy the data only if we need to aply a relocation to @@ -3055,6 +3115,38 @@ 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, 0}); + loadClangModule(PCMfile, PCMpath, ModuleMap, Indent + 2); + return true; +} + ErrorOr DwarfLinker::loadObject(BinaryHolder &BinaryHolder, DebugMapObject &Obj, const DebugMap &Map) { @@ -3070,6 +3162,59 @@ 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::MinTime()); + auto ErrOrObj = loadObject(ObjHolder, Obj, ModuleMap); + if (!ErrOrObj) { + ClangModules.erase(ClangModules.find(Filename)); + return; + } + + 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); + if (false && Options.Verbose) { + outs().indent(Indent); + outs() << "Input compilation unit:"; + CUDie->dump(outs(), CU.get(), 0, Indent); + } + // 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(*Unit), + Options) + .cloneAllCompileUnits(DwarfContext); +} + void DwarfLinker::DIECloner::cloneAllCompileUnits( DWARFContextInMemory &DwarfContext) { if (!Linker.Streamer) @@ -3117,7 +3262,9 @@ // Size of the DIEs (and headers) generated for the linked output. OutputDebugInfoSize = 0; // 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()) { CurrentDebugObject = Obj.get(); @@ -3147,9 +3294,11 @@ outs() << "Input compilation unit:"; CUDie->dump(outs(), CU.get(), 0); } - Units.emplace_back(*CU, UnitID++, !Options.NoODR); - gatherDIEParents(CUDie, 0, Units.back(), &ODRContexts.getRoot(), - StringPool, ODRContexts); + if (!registerModuleReference(*CUDie, *CU, ModuleMap)) { + Units.emplace_back(*CU, UnitID++, !Options.NoODR); + gatherDIEParents(CUDie, 0, Units.back(), &ODRContexts.getRoot(), + StringPool, ODRContexts); + } } // Then mark all the DIEs that need to be present in the linked Index: tools/dsymutil/dsymutil.h =================================================================== --- tools/dsymutil/dsymutil.h +++ tools/dsymutil/dsymutil.h @@ -28,7 +28,7 @@ bool Verbose; ///< Verbosity bool NoOutput; ///< Skip emitting output bool NoODR; ///< Do not unique types according to ODR - + std::string PrependPath; //< -oso-prepend-path LinkOptions() : Verbose(false), NoOutput(false) {} }; Index: tools/dsymutil/dsymutil.cpp =================================================================== --- tools/dsymutil/dsymutil.cpp +++ tools/dsymutil/dsymutil.cpp @@ -257,6 +257,7 @@ Options.Verbose = Verbose; Options.NoOutput = NoOutput; Options.NoODR = NoODR; + Options.PrependPath = OsoPrependPath; llvm::InitializeAllTargetInfos(); llvm::InitializeAllTargetMCs();