Index: llvm/include/llvm/DebugInfo/DWARF/DWARFFormValue.h =================================================================== --- llvm/include/llvm/DebugInfo/DWARF/DWARFFormValue.h +++ llvm/include/llvm/DebugInfo/DWARF/DWARFFormValue.h @@ -159,6 +159,19 @@ return None; } +/// Take an optional DWARFFormValue and try to extract a string value from it. +/// +/// \param V and optional DWARFFormValue to attempt to extract the value from. +/// \returns an optional value that contains a value if the form value +/// was valid and was a string. +inline StringRef toStringRef(const Optional &V, + StringRef Default = {}) { + if (V) + if (auto S = V->getAsCString()) + return *S; + return Default; +} + /// Take an optional DWARFFormValue and extract a string value from it. /// /// \param V and optional DWARFFormValue to attempt to extract the value from. Index: llvm/test/tools/dsymutil/Inputs/swift-interface.ll =================================================================== --- /dev/null +++ llvm/test/tools/dsymutil/Inputs/swift-interface.ll @@ -0,0 +1,34 @@ +; This is a manually stripped empty Swift program with one import. +source_filename = "/swift-interface.ll" +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.9.0" + +@__swift_reflection_version = linkonce_odr hidden constant i16 3 +@llvm.used = appending global [1 x i8*] [i8* bitcast (i16* @__swift_reflection_version to i8*)], section "llvm.metadata", align 8 + +define i32 @main(i32, i8**) !dbg !29 { +entry: + %2 = bitcast i8** %1 to i8* + ret i32 0, !dbg !35 +} + +!llvm.dbg.cu = !{!0} +!swift.module.flags = !{!14} +!llvm.module.flags = !{!20, !21, !24} + +!0 = distinct !DICompileUnit(language: DW_LANG_Swift, file: !1, isOptimized: false, runtimeVersion: 5, emissionKind: FullDebug, enums: !2, imports: !3) +!1 = !DIFile(filename: "ParseableInterfaceImports.swift", directory: "/") +!2 = !{} +!3 = !{!4} +!4 = !DIImportedEntity(tag: DW_TAG_imported_module, scope: !1, entity: !5, file: !1) +!5 = !DIModule(scope: null, name: "Foo", includePath: "/Foo/x86_64.swiftinterface") +!14 = !{!"standard-library", i1 false} +!20 = !{i32 2, !"Dwarf Version", i32 4} +!21 = !{i32 2, !"Debug Info Version", i32 3} +!24 = !{i32 1, !"Swift Version", i32 7} +!29 = distinct !DISubprogram(name: "main", linkageName: "main", scope: !5, file: !1, line: 1, type: !30, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!30 = !DISubroutineType(types: !31) +!31 = !{} +!35 = !DILocation(line: 0, scope: !36) +!36 = !DILexicalBlockFile(scope: !29, file: !37, discriminator: 0) +!37 = !DIFile(filename: "", directory: "") Index: llvm/test/tools/dsymutil/X86/swift-interface.test =================================================================== --- /dev/null +++ llvm/test/tools/dsymutil/X86/swift-interface.test @@ -0,0 +1,23 @@ +# RUN: rm -rf %t.dir +# RUN: mkdir -p %t.dir/obj +# RUN: mkdir -p %t.dir/Foo/x86_64 +# RUN: llc %p/../Inputs/swift-interface.ll -filetype=obj -o %t.dir/obj/1.o +# RUN: dsymutil -oso-prepend-path %t.dir -y %s \ +# RUN: -o %t.dir/swift-interface.dSYM 2>&1 \ +# RUN: | FileCheck %s --check-prefix=WARNINGS +# RUN: echo "// module Foo" >%t.dir/Foo/x86_64.swiftinterface +# RUN: dsymutil -oso-prepend-path %t.dir -y %s \ +# RUN: -o %t.dir/swift-interface.dSYM +# RUN: cat %t.dir/swift-interface.dSYM/Contents/Resources/Swift/Foo.swiftinterface \ +# RUN: | FileCheck %s --check-prefix=INTERFACE + +# WARNINGS: cannot copy parseable Swift interface +# INTERFACE: module Foo + +--- +triple: 'x86_64-apple-darwin' +objects: + - filename: obj/1.o + symbols: + - { sym: _main, objAddr: 0x0, binAddr: 0x10000, size: 0x10 } +... Index: llvm/tools/dsymutil/CompileUnit.h =================================================================== --- llvm/tools/dsymutil/CompileUnit.h +++ llvm/tools/dsymutil/CompileUnit.h @@ -114,6 +114,8 @@ bool hasODR() const { return HasODR; } bool isClangModule() const { return !ClangModuleName.empty(); } + uint16_t getLanguage(); + const std::string &getClangModuleName() const { return ClangModuleName; } DIEInfo &getInfo(unsigned Idx) { return Info[Idx]; } @@ -316,6 +318,9 @@ /// Did a DIE actually contain a valid reloc? bool HasInterestingContent; + /// The DW_AT_language of this unit. + uint16_t Language = 0; + /// If this is a Clang module, this holds the module's name. std::string ClangModuleName; }; Index: llvm/tools/dsymutil/CompileUnit.cpp =================================================================== --- llvm/tools/dsymutil/CompileUnit.cpp +++ llvm/tools/dsymutil/CompileUnit.cpp @@ -22,6 +22,14 @@ return false; } +uint16_t CompileUnit::getLanguage() { + if (!Language) { + DWARFDie CU = getOrigUnit().getUnitDIE(); + Language = dwarf::toUnsigned(CU.find(dwarf::DW_AT_language), 0); + } + return Language; +} + void CompileUnit::markEverythingAsKept() { unsigned Idx = 0; Index: llvm/tools/dsymutil/DwarfLinker.h =================================================================== --- llvm/tools/dsymutil/DwarfLinker.h +++ llvm/tools/dsymutil/DwarfLinker.h @@ -193,7 +193,7 @@ /// 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 DWARFDie &CUDie, const DWARFUnit &Unit, + bool registerModuleReference(DWARFDie CUDie, const DWARFUnit &Unit, DebugMap &ModuleMap, const DebugMapObject &DMO, RangesTy &Ranges, OffsetsStringPool &OffsetsStringPool, @@ -206,7 +206,7 @@ /// 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. - Error loadClangModule(StringRef Filename, StringRef ModulePath, + Error loadClangModule(DWARFDie CUDie, StringRef FilePath, StringRef ModuleName, uint64_t DwoId, DebugMap &ModuleMap, const DebugMapObject &DMO, RangesTy &Ranges, OffsetsStringPool &OffsetsStringPool, @@ -494,6 +494,12 @@ /// Mapping the PCM filename to the DwoId. StringMap ClangModules; + /// A list of all .swiftinterface files referenced by the debug + /// info, mapping Module name to path on disk. The entries need to + /// be uniqued and sorted and there are only few entries expected + /// per compile unit, which is why this is a std::map. + std::map ParseableSwiftInterfaces; + bool ModuleCacheHintDisplayed = false; bool ArchiveHintDisplayed = false; }; Index: llvm/tools/dsymutil/DwarfLinker.cpp =================================================================== --- llvm/tools/dsymutil/DwarfLinker.cpp +++ llvm/tools/dsymutil/DwarfLinker.cpp @@ -244,18 +244,55 @@ return Streamer->init(TheTriple); } +/// Resolve the relative path to a build artifact referenced by DWARF by +/// applying DW_AT_comp_dir. +static void resolveRelativeObjectPath(SmallVectorImpl &Buf, DWARFDie CU) { + sys::path::append(Buf, dwarf::toString(CU.find(dwarf::DW_AT_comp_dir), "")); +} + +/// Collect references to parseable Swift interfaces in imported +/// DW_TAG_module blocks. +static void analyzeImportedModule( + const DWARFDie &DIE, CompileUnit &CU, + std::map &ParseableSwiftInterfaces, + std::function ReportWarning) { + if (CU.getLanguage() != dwarf::DW_LANG_Swift) + return; + + StringRef Path = dwarf::toStringRef(DIE.find(dwarf::DW_AT_LLVM_include_path)); + if (!Path.endswith(".swiftinterface")) + return; + if (Optional Val = DIE.find(dwarf::DW_AT_name)) + if (Optional Name = Val->getAsCString()) { + auto &Entry = ParseableSwiftInterfaces[*Name]; + // The prepend path is applied later when copying. + DWARFDie CUDie = CU.getOrigUnit().getUnitDIE(); + SmallString<128> ResolvedPath; + if (sys::path::is_relative(Path)) + resolveRelativeObjectPath(ResolvedPath, CUDie); + sys::path::append(ResolvedPath, Path); + if (!Entry.empty() && Entry != ResolvedPath) + ReportWarning( + Twine("Conflicting parseable interfaces for Swift Module ") + + *Name + ": " + Entry + " and " + Path, + DIE); + Entry = ResolvedPath.str(); + } +} + /// Recursive helper to build the global DeclContext information and /// gather the child->parent relationships in the original compile unit. /// /// \return true when this DIE and all of its children are only /// forward declarations to types defined in external clang modules /// (i.e., forward declarations that are children of a DW_TAG_module). -static bool analyzeContextInfo(const DWARFDie &DIE, unsigned ParentIdx, - CompileUnit &CU, DeclContext *CurrentDeclContext, - UniquingStringPool &StringPool, - DeclContextTree &Contexts, - uint64_t ModulesEndOffset, - bool InImportedModule = false) { +static bool analyzeContextInfo( + const DWARFDie &DIE, unsigned ParentIdx, CompileUnit &CU, + DeclContext *CurrentDeclContext, UniquingStringPool &StringPool, + DeclContextTree &Contexts, uint64_t ModulesEndOffset, + std::map &ParseableSwiftInterfaces, + std::function ReportWarning, + bool InImportedModule = false) { unsigned MyIdx = CU.getOrigUnit().getDIEIndex(DIE); CompileUnit::DIEInfo &Info = CU.getInfo(MyIdx); @@ -275,6 +312,7 @@ dwarf::toString(DIE.find(dwarf::DW_AT_name), "") != CU.getClangModuleName()) { InImportedModule = true; + analyzeImportedModule(DIE, CU, ParseableSwiftInterfaces, ReportWarning); } Info.ParentIdx = ParentIdx; @@ -295,9 +333,10 @@ Info.Prune = InImportedModule; if (DIE.hasChildren()) for (auto Child : DIE.children()) - Info.Prune &= - analyzeContextInfo(Child, MyIdx, CU, CurrentDeclContext, StringPool, - Contexts, ModulesEndOffset, InImportedModule); + Info.Prune &= analyzeContextInfo(Child, MyIdx, CU, CurrentDeclContext, + StringPool, Contexts, ModulesEndOffset, + ParseableSwiftInterfaces, ReportWarning, + InImportedModule); // Prune this DIE if it is either a forward declaration inside a // DW_TAG_module or a DW_TAG_module that contains nothing but @@ -2107,7 +2146,7 @@ } bool DwarfLinker::registerModuleReference( - const DWARFDie &CUDie, const DWARFUnit &Unit, DebugMap &ModuleMap, + DWARFDie CUDie, const DWARFUnit &Unit, DebugMap &ModuleMap, const DebugMapObject &DMO, RangesTy &Ranges, OffsetsStringPool &StringPool, UniquingStringPool &UniquingStringPool, DeclContextTree &ODRContexts, uint64_t ModulesEndOffset, unsigned &UnitID, bool IsLittleEndian, @@ -2118,7 +2157,6 @@ return false; // Clang module DWARF skeleton CUs abuse this for the path to the module. - std::string PCMpath = dwarf::toString(CUDie.find(dwarf::DW_AT_comp_dir), ""); uint64_t DwoId = getDwoId(CUDie, Unit); std::string Name = dwarf::toString(CUDie.find(dwarf::DW_AT_name), ""); @@ -2153,7 +2191,8 @@ // 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, DwoId}); - if (Error E = loadClangModule(PCMfile, PCMpath, Name, DwoId, ModuleMap, DMO, + + if (Error E = loadClangModule(CUDie, PCMfile, Name, DwoId, ModuleMap, DMO, Ranges, StringPool, UniquingStringPool, ODRContexts, ModulesEndOffset, UnitID, IsLittleEndian, Indent + 2, Quiet)) { @@ -2186,17 +2225,16 @@ } Error DwarfLinker::loadClangModule( - StringRef Filename, StringRef ModulePath, StringRef ModuleName, - uint64_t DwoId, DebugMap &ModuleMap, const DebugMapObject &DMO, - RangesTy &Ranges, OffsetsStringPool &StringPool, - UniquingStringPool &UniquingStringPool, DeclContextTree &ODRContexts, - uint64_t ModulesEndOffset, unsigned &UnitID, bool IsLittleEndian, - unsigned Indent, bool Quiet) { - SmallString<80> Path(Options.PrependPath); + DWARFDie CUDie, StringRef Filename, StringRef ModuleName, uint64_t DwoId, + DebugMap &ModuleMap, const DebugMapObject &DMO, RangesTy &Ranges, + OffsetsStringPool &StringPool, UniquingStringPool &UniquingStringPool, + DeclContextTree &ODRContexts, uint64_t ModulesEndOffset, unsigned &UnitID, + bool IsLittleEndian, unsigned Indent, bool Quiet) { + /// Using a SmallString<0> because loadClangModule() is recursive. + SmallString<0> Path(Options.PrependPath); if (sys::path::is_relative(Filename)) - sys::path::append(Path, ModulePath, Filename); - else - sys::path::append(Path, Filename); + resolveRelativeObjectPath(Path, CUDie); + sys::path::append(Path, Filename); // Don't use the cached binary holder because we have no thread-safety // guarantee and the lifetime is limited. auto &Obj = ModuleMap.addDebugMapObject( @@ -2283,7 +2321,11 @@ ModuleName); Unit->setHasInterestingContent(); analyzeContextInfo(CUDie, 0, *Unit, &ODRContexts.getRoot(), - UniquingStringPool, ODRContexts, ModulesEndOffset); + UniquingStringPool, ODRContexts, ModulesEndOffset, + ParseableSwiftInterfaces, + [&](const Twine &Warning, const DWARFDie &DIE) { + reportWarning(Warning, DMO, &DIE); + }); // Keep everything. Unit->markEverythingAsKept(); } @@ -2450,6 +2492,43 @@ return true; } +static Error copySwiftInterfaces( + const std::map &ParseableSwiftInterfaces, + StringRef Architecture, const LinkOptions &Options) { + std::error_code EC; + SmallString<128> InputPath; + SmallString<128> Path; + sys::path::append(Path, *Options.ResourceDir, "Swift"); + if ((EC = sys::fs::create_directories(Path.str(), true, + sys::fs::perms::all_all))) + return make_error( + "cannot create directory: " + toString(errorCodeToError(EC)), EC); + unsigned BaseLength = Path.size(); + + for (auto &I : ParseableSwiftInterfaces) { + StringRef ModuleName = I.first; + StringRef InterfaceFile = I.second; + if (!Options.PrependPath.empty()) { + InputPath.clear(); + sys::path::append(InputPath, Options.PrependPath, InterfaceFile); + InterfaceFile = InputPath; + } + sys::path::append(Path, ModuleName); + Path.append(".swiftinterface"); + if (Options.Verbose) + outs() << "copy parseable Swift interface " << InterfaceFile << " -> " + << Path.str() << '\n'; + + // copy_file attempts an APFS clone first, so this should be cheap. + if ((EC = sys::fs::copy_file(InterfaceFile, Path.str()))) + warn(Twine("cannot copy parseable Swift interface ") + + InterfaceFile + ": " + + toString(errorCodeToError(EC))); + Path.resize(BaseLength); + } + return Error::success(); +} + bool DwarfLinker::link(const DebugMap &Map) { if (!createStreamer(Map.getTriple(), OutFile)) return false; @@ -2637,7 +2716,11 @@ continue; analyzeContextInfo(CurrentUnit->getOrigUnit().getUnitDIE(), 0, *CurrentUnit, &ODRContexts.getRoot(), - UniquingStringPool, ODRContexts, ModulesEndOffset); + UniquingStringPool, ODRContexts, ModulesEndOffset, + ParseableSwiftInterfaces, + [&](const Twine &Warning, const DWARFDie &DIE) { + reportWarning(Warning, LinkContext.DMO, &DIE); + }); } }; @@ -2750,7 +2833,17 @@ pool.wait(); } - return Options.NoOutput ? true : Streamer->finish(Map, Options.Translator); + if (Options.NoOutput) + return true; + + if (Options.ResourceDir && !ParseableSwiftInterfaces.empty()) { + StringRef ArchName = Triple::getArchTypeName(Map.getTriple().getArch()); + if (auto E = + copySwiftInterfaces(ParseableSwiftInterfaces, ArchName, Options)) + return error(toString(std::move(E))); + } + + return Streamer->finish(Map, Options.Translator); } // namespace dsymutil bool linkDwarf(raw_fd_ostream &OutFile, BinaryHolder &BinHolder, Index: llvm/tools/dsymutil/LinkUtils.h =================================================================== --- llvm/tools/dsymutil/LinkUtils.h +++ llvm/tools/dsymutil/LinkUtils.h @@ -62,6 +62,9 @@ /// -oso-prepend-path std::string PrependPath; + /// The Resources directory in the .dSYM bundle. + Optional ResourceDir; + /// Symbol map translator. SymbolMapTranslator Translator; Index: llvm/tools/dsymutil/dsymutil.cpp =================================================================== --- llvm/tools/dsymutil/dsymutil.cpp +++ llvm/tools/dsymutil/dsymutil.cpp @@ -278,23 +278,30 @@ return false; } -static Expected getOutputFileName(llvm::StringRef InputFile) { +namespace { +struct OutputLocation { + std::string DWARFFile; + Optional ResourceDir; +}; +} + +static Expected getOutputFileName(llvm::StringRef InputFile) { if (OutputFileOpt == "-") - return OutputFileOpt; + return OutputLocation{OutputFileOpt, {}}; // When updating, do in place replacement. if (OutputFileOpt.empty() && (Update || !SymbolMap.empty())) - return InputFile; + return OutputLocation{InputFile, {}}; // If a flat dSYM has been requested, things are pretty simple. if (FlatOut) { if (OutputFileOpt.empty()) { if (InputFile == "-") - return "a.out.dwarf"; - return (InputFile + ".dwarf").str(); + return OutputLocation{"a.out.dwarf", {}}; + return OutputLocation{(InputFile + ".dwarf").str(), {}}; } - return OutputFileOpt; + return OutputLocation{OutputFileOpt, {}}; } // We need to create/update a dSYM bundle. @@ -307,17 +314,18 @@ // std::string DwarfFile = InputFile == "-" ? llvm::StringRef("a.out") : InputFile; - llvm::SmallString<128> BundleDir(OutputFileOpt); - if (BundleDir.empty()) - BundleDir = DwarfFile + ".dSYM"; - if (auto E = createBundleDir(BundleDir)) + llvm::SmallString<128> Path(OutputFileOpt); + if (Path.empty()) + Path = DwarfFile + ".dSYM"; + if (auto E = createBundleDir(Path)) return std::move(E); - if (auto E = createPlistFile(DwarfFile, BundleDir)) + if (auto E = createPlistFile(DwarfFile, Path)) return std::move(E); - llvm::sys::path::append(BundleDir, "Contents", "Resources", "DWARF", - llvm::sys::path::filename(DwarfFile)); - return BundleDir.str(); + llvm::sys::path::append(Path, "Contents", "Resources"); + StringRef ResourceDir = Path; + llvm::sys::path::append(Path, "DWARF", llvm::sys::path::filename(DwarfFile)); + return OutputLocation{Path.str(), ResourceDir.str()}; } /// Parses the command line options into the LinkOptions struct and performs @@ -544,13 +552,15 @@ // types don't work with std::bind in the ThreadPool implementation. std::shared_ptr OS; - Expected OutputFileOrErr = getOutputFileName(InputFile); - if (!OutputFileOrErr) { - WithColor::error() << toString(OutputFileOrErr.takeError()); + Expected OutputLocationOrErr = + getOutputFileName(InputFile); + if (!OutputLocationOrErr) { + WithColor::error() << toString(OutputLocationOrErr.takeError()); return 1; } + OptionsOrErr->ResourceDir = OutputLocationOrErr->ResourceDir; - std::string OutputFile = *OutputFileOrErr; + std::string OutputFile = OutputLocationOrErr->DWARFFile; if (NeedsTempFiles) { TempFiles.emplace_back(Map->getTriple().getArchName().str()); @@ -597,12 +607,13 @@ return 1; if (NeedsTempFiles) { - Expected OutputFileOrErr = getOutputFileName(InputFile); - if (!OutputFileOrErr) { - WithColor::error() << toString(OutputFileOrErr.takeError()); + Expected OutputLocationOrErr = getOutputFileName(InputFile); + if (!OutputLocationOrErr) { + WithColor::error() << toString(OutputLocationOrErr.takeError()); return 1; } - if (!MachOUtils::generateUniversalBinary(TempFiles, *OutputFileOrErr, + if (!MachOUtils::generateUniversalBinary(TempFiles, + OutputLocationOrErr->DWARFFile, *OptionsOrErr, SDKPath)) return 1; }