OSDN Git Service

[dsymutil] Collect parseable Swift interfaces in the .dSYM bundle.
authorAdrian Prantl <aprantl@apple.com>
Mon, 22 Apr 2019 21:33:22 +0000 (21:33 +0000)
committerAdrian Prantl <aprantl@apple.com>
Mon, 22 Apr 2019 21:33:22 +0000 (21:33 +0000)
When a Swift module built with debug info imports a library without
debug info from a textual interface, the textual interface is
necessary to reconstruct types defined in the library's interface. By
recording the Swift interface files in DWARF dsymutil can collect them
and LLDB can find them.

This patch teaches dsymutil to look for DW_TAG_imported_modules and
records all references to parseable Swift ingterfrace files and copies
them to

  a.out.dSYM/Contents/Resources/<Arch>/<ModuleName>.swiftinterface

<rdar://problem/49751748>

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

include/llvm/DebugInfo/DWARF/DWARFFormValue.h
test/tools/dsymutil/Inputs/swift-interface.ll [new file with mode: 0644]
test/tools/dsymutil/X86/swift-interface.test [new file with mode: 0644]
tools/dsymutil/CompileUnit.cpp
tools/dsymutil/CompileUnit.h
tools/dsymutil/DwarfLinker.cpp
tools/dsymutil/DwarfLinker.h
tools/dsymutil/LinkUtils.h
tools/dsymutil/dsymutil.cpp

index 8107624..279a2e7 100644 (file)
@@ -159,6 +159,19 @@ inline Optional<const char *> toString(const Optional<DWARFFormValue> &V) {
   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<DWARFFormValue> &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.
diff --git a/test/tools/dsymutil/Inputs/swift-interface.ll b/test/tools/dsymutil/Inputs/swift-interface.ll
new file mode 100644 (file)
index 0000000..715800e
--- /dev/null
@@ -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: "<compiler-generated>", directory: "")
diff --git a/test/tools/dsymutil/X86/swift-interface.test b/test/tools/dsymutil/X86/swift-interface.test
new file mode 100644 (file)
index 0000000..4bfd2b8
--- /dev/null
@@ -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 670591a..036c61a 100644 (file)
@@ -22,6 +22,14 @@ static bool inFunctionScope(CompileUnit &U, unsigned Idx) {
   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 6e3c2c6..e0f5d3b 100644 (file)
@@ -114,6 +114,8 @@ public:
 
   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 @@ private:
   /// 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 fa408d7..e97d502 100644 (file)
@@ -243,18 +243,55 @@ bool DwarfLinker::createStreamer(const Triple &TheTriple,
   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<char> &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<std::string, std::string> &ParseableSwiftInterfaces,
+    std::function<void(const Twine &, const DWARFDie &)> 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<DWARFFormValue> Val = DIE.find(dwarf::DW_AT_name))
+    if (Optional<const char *> 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<std::string, std::string> &ParseableSwiftInterfaces,
+    std::function<void(const Twine &, const DWARFDie &)> ReportWarning,
+    bool InImportedModule = false) {
   unsigned MyIdx = CU.getOrigUnit().getDIEIndex(DIE);
   CompileUnit::DIEInfo &Info = CU.getInfo(MyIdx);
 
@@ -274,6 +311,7 @@ static bool analyzeContextInfo(const DWARFDie &DIE, unsigned ParentIdx,
       dwarf::toString(DIE.find(dwarf::DW_AT_name), "") !=
           CU.getClangModuleName()) {
     InImportedModule = true;
+    analyzeImportedModule(DIE, CU, ParseableSwiftInterfaces, ReportWarning);
   }
 
   Info.ParentIdx = ParentIdx;
@@ -294,9 +332,10 @@ static bool analyzeContextInfo(const DWARFDie &DIE, unsigned ParentIdx,
   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
@@ -2106,7 +2145,7 @@ static uint64_t getDwoId(const DWARFDie &CUDie, const DWARFUnit &Unit) {
 }
 
 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,
@@ -2117,7 +2156,6 @@ bool DwarfLinker::registerModuleReference(
     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), "");
@@ -2152,7 +2190,8 @@ bool DwarfLinker::registerModuleReference(
   // 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)) {
@@ -2185,17 +2224,16 @@ DwarfLinker::loadObject(const DebugMapObject &Obj, const DebugMap &Map) {
 }
 
 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(
@@ -2282,7 +2320,11 @@ Error DwarfLinker::loadClangModule(
                                             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();
     }
@@ -2449,6 +2491,43 @@ bool DwarfLinker::emitPaperTrailWarnings(const DebugMapObject &DMO,
   return true;
 }
 
+static Error copySwiftInterfaces(
+    const std::map<std::string, std::string> &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<StringError>(
+        "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;
@@ -2636,7 +2715,11 @@ bool DwarfLinker::link(const DebugMap &Map) {
         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);
+                         });
     }
   };
 
@@ -2749,7 +2832,17 @@ bool DwarfLinker::link(const DebugMap &Map) {
     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 fdc86cf..482c607 100644 (file)
@@ -193,7 +193,7 @@ private:
   /// 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 @@ private:
   /// 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 @@ private:
   /// Mapping the PCM filename to the DwoId.
   StringMap<uint64_t> 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<std::string, std::string> ParseableSwiftInterfaces;
+  
   bool ModuleCacheHintDisplayed = false;
   bool ArchiveHintDisplayed = false;
 };
index 2b3ccb2..0cd5600 100644 (file)
@@ -62,6 +62,9 @@ struct LinkOptions {
   /// -oso-prepend-path
   std::string PrependPath;
 
+  /// The Resources directory in the .dSYM bundle.
+  Optional<std::string> ResourceDir;
+
   /// Symbol map translator.
   SymbolMapTranslator Translator;
 
index 14f9a0e..1651fa8 100644 (file)
@@ -278,23 +278,30 @@ static bool verify(llvm::StringRef OutputFile, llvm::StringRef Arch) {
   return false;
 }
 
-static Expected<std::string> getOutputFileName(llvm::StringRef InputFile) {
+namespace {
+struct OutputLocation {
+  std::string DWARFFile;
+  Optional<std::string> ResourceDir;
+};
+}
+
+static Expected<OutputLocation> 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 @@ static Expected<std::string> getOutputFileName(llvm::StringRef InputFile) {
   //                <DWARF file(s)>
   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 @@ int main(int argc, char **argv) {
       // types don't work with std::bind in the ThreadPool implementation.
       std::shared_ptr<raw_fd_ostream> OS;
 
-      Expected<std::string> OutputFileOrErr = getOutputFileName(InputFile);
-      if (!OutputFileOrErr) {
-        WithColor::error() << toString(OutputFileOrErr.takeError());
+      Expected<OutputLocation> 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 @@ int main(int argc, char **argv) {
       return 1;
 
     if (NeedsTempFiles) {
-      Expected<std::string> OutputFileOrErr = getOutputFileName(InputFile);
-      if (!OutputFileOrErr) {
-        WithColor::error() << toString(OutputFileOrErr.takeError());
+      Expected<OutputLocation> 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;
     }