[dsymutil] Implement support for handling mach-o universal binaries as main input...
authorFrederic Riss <friss@apple.com>
Wed, 5 Aug 2015 18:27:44 +0000 (18:27 +0000)
committerFrederic Riss <friss@apple.com>
Wed, 5 Aug 2015 18:27:44 +0000 (18:27 +0000)
The DWARF linker isn't touched by this, the implementation links
individual files and merges them together into a fat binary by
calling out to the 'lipo' utility.

The main change is that the MachODebugMapParser can now return
multiple debug maps for a single binary.

The test just verifies that lipo would be invoked correctly, but
doesn't actually generate a binary. This mimics the way clang
tests its external iplatform tools integration.

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

test/tools/dsymutil/Inputs/fat-test.dylib [new file with mode: 0755]
test/tools/dsymutil/fat-binary-output.test [new file with mode: 0644]
tools/dsymutil/CMakeLists.txt
tools/dsymutil/DebugMap.cpp
tools/dsymutil/DebugMap.h
tools/dsymutil/MachODebugMapParser.cpp
tools/dsymutil/MachOUtils.cpp [new file with mode: 0644]
tools/dsymutil/MachOUtils.h [new file with mode: 0644]
tools/dsymutil/dsymutil.cpp
tools/dsymutil/dsymutil.h

diff --git a/test/tools/dsymutil/Inputs/fat-test.dylib b/test/tools/dsymutil/Inputs/fat-test.dylib
new file mode 100755 (executable)
index 0000000..4def340
Binary files /dev/null and b/test/tools/dsymutil/Inputs/fat-test.dylib differ
diff --git a/test/tools/dsymutil/fat-binary-output.test b/test/tools/dsymutil/fat-binary-output.test
new file mode 100644 (file)
index 0000000..ca631e4
--- /dev/null
@@ -0,0 +1,32 @@
+RUN: llvm-dsymutil -verbose -no-output %p/Inputs/fat-test.dylib -oso-prepend-path %p | FileCheck %s
+
+This test doesn't produce any filesytstem output, we just look at the verbose
+log output.
+
+For each arch in the binary, check that we emit the right triple with the right
+file and the right symbol inside it (each slice has a different symbol, so that
+means that the logic is looking at the right file slice too).
+
+After the link of each architecture, check that lipo is correctly invoked to
+generate the fat output binary.
+
+CHECK: triple:          'x86_64-apple-darwin'
+CHECK:   - filename:        [[INPUTS_PATH:.*]]fat-test.o
+CHECK:   DW_AT_name{{.*}} "x86_64_var"
+
+CHECK: triple:          'i386-apple-darwin'
+CHECK:   - filename:        [[INPUTS_PATH]]fat-test.o
+CHECK:   DW_AT_name{{.*}} "i386_var"
+
+CHECK: triple:          'x86_64h-apple-darwin'
+CHECK:   - filename:        [[INPUTS_PATH]]fat-test.o
+CHECK:   DW_AT_name{{.*}} "x86_64h_var"
+
+CHECK: Running lipo
+CHECK-NEXT: lipo -create
+CHECK-SAME: [[INPUTS_PATH]]fat-test.dylib.tmp{{......}}.dwarf
+CHECK-SAME: [[INPUTS_PATH]]fat-test.dylib.tmp{{......}}.dwarf
+CHECK-SAME: [[INPUTS_PATH]]fat-test.dylib.tmp{{......}}.dwarf
+CHECK-SAME: -segalign x86_64 20 -segalign i386 20 -segalign x86_64h 20
+CHECK-SAME: -output [[INPUTS_PATH]]fat-test.dylib.dwarf
+
index 88f9f1f083db15da03a50322b885aa65c3577721..0f45d20de22c058ee1e07fb0dedcd276a85db33b 100644 (file)
@@ -14,5 +14,6 @@ add_llvm_tool(llvm-dsymutil
   DebugMap.cpp
   DwarfLinker.cpp
   MachODebugMapParser.cpp
   DebugMap.cpp
   DwarfLinker.cpp
   MachODebugMapParser.cpp
+  MachOUtils.cpp
   )
 
   )
 
index e29c885b5ac0248afaa5780e62493a2822a65c7b..387adc7927698660fa4dddea844bd7c32fd22a0d 100644 (file)
@@ -97,7 +97,7 @@ struct YAMLContext {
 };
 }
 
 };
 }
 
-ErrorOr<std::unique_ptr<DebugMap>>
+ErrorOr<std::vector<std::unique_ptr<DebugMap>>>
 DebugMap::parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath,
                             bool Verbose) {
   auto ErrOrFile = MemoryBuffer::getFileOrSTDIN(InputFile);
 DebugMap::parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath,
                             bool Verbose) {
   auto ErrOrFile = MemoryBuffer::getFileOrSTDIN(InputFile);
@@ -114,8 +114,9 @@ DebugMap::parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath,
 
   if (auto EC = yin.error())
     return EC;
 
   if (auto EC = yin.error())
     return EC;
-
-  return std::move(Res);
+  std::vector<std::unique_ptr<DebugMap>> Result;
+  Result.push_back(std::move(Res));
+  return std::move(Result);
 }
 }
 
 }
 }
 
index 29347ed0f5051405511efe005e6748e804669f16..9bf20c6db31abface6d8ae36ba2f65edc32ab19a 100644 (file)
@@ -102,7 +102,7 @@ public:
 #endif
 
   /// Read a debug map for \a InputFile.
 #endif
 
   /// Read a debug map for \a InputFile.
-  static ErrorOr<std::unique_ptr<DebugMap>>
+  static ErrorOr<std::vector<std::unique_ptr<DebugMap>>>
   parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath, bool Verbose);
 };
 
   parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath, bool Verbose);
 };
 
index 33554f1212279ff564c954836cc46706119bbabc..aa034cb683c532f014f40b741a095e12eec9a81e 100644 (file)
@@ -27,10 +27,12 @@ public:
         MainBinaryHolder(Verbose), CurrentObjectHolder(Verbose),
         CurrentDebugMapObject(nullptr) {}
 
         MainBinaryHolder(Verbose), CurrentObjectHolder(Verbose),
         CurrentDebugMapObject(nullptr) {}
 
-  /// \brief Parses and returns the DebugMap of the input binary.
+  /// \brief Parses and returns the DebugMaps of the input binary.
+  /// The binary contains multiple maps in case it is a universal
+  /// binary.
   /// \returns an error in case the provided BinaryPath doesn't exist
   /// or isn't of a supported type.
   /// \returns an error in case the provided BinaryPath doesn't exist
   /// or isn't of a supported type.
-  ErrorOr<std::unique_ptr<DebugMap>> parse();
+  ErrorOr<std::vector<std::unique_ptr<DebugMap>>> parse();
 
 private:
   std::string BinaryPath;
 
 private:
   std::string BinaryPath;
@@ -55,6 +57,9 @@ private:
   const char *CurrentFunctionName;
   uint64_t CurrentFunctionAddress;
 
   const char *CurrentFunctionName;
   uint64_t CurrentFunctionAddress;
 
+  std::unique_ptr<DebugMap> parseOneBinary(const MachOObjectFile &MainBinary,
+                                           StringRef BinaryPath);
+
   void switchToNewDebugMapObject(StringRef Filename, sys::TimeValue Timestamp);
   void resetParserState();
   uint64_t getMainBinarySymbolAddress(StringRef Name);
   void switchToNewDebugMapObject(StringRef Filename, sys::TimeValue Timestamp);
   void resetParserState();
   uint64_t getMainBinarySymbolAddress(StringRef Name);
@@ -110,19 +115,9 @@ void MachODebugMapParser::switchToNewDebugMapObject(StringRef Filename,
   loadCurrentObjectFileSymbols(*ErrOrAchObj);
 }
 
   loadCurrentObjectFileSymbols(*ErrOrAchObj);
 }
 
-/// This main parsing routine tries to open the main binary and if
-/// successful iterates over the STAB entries. The real parsing is
-/// done in handleStabSymbolTableEntry.
-ErrorOr<std::unique_ptr<DebugMap>> MachODebugMapParser::parse() {
-  auto MainBinOrError =
-      MainBinaryHolder.GetFilesAs<MachOObjectFile>(BinaryPath);
-  if (auto Error = MainBinOrError.getError())
-    return Error;
-
-  if (MainBinOrError->size() != 1)
-    return make_error_code(object::object_error::invalid_file_type);
-
-  const MachOObjectFile &MainBinary = *MainBinOrError->front();
+std::unique_ptr<DebugMap>
+MachODebugMapParser::parseOneBinary(const MachOObjectFile &MainBinary,
+                                    StringRef BinaryPath) {
   loadMainBinarySymbols(MainBinary);
   Result = make_unique<DebugMap>(BinaryHolder::getTriple(MainBinary));
   MainBinaryStrings = MainBinary.getStringTableData();
   loadMainBinarySymbols(MainBinary);
   Result = make_unique<DebugMap>(BinaryHolder::getTriple(MainBinary));
   MainBinaryStrings = MainBinary.getStringTableData();
@@ -138,6 +133,22 @@ ErrorOr<std::unique_ptr<DebugMap>> MachODebugMapParser::parse() {
   return std::move(Result);
 }
 
   return std::move(Result);
 }
 
+/// This main parsing routine tries to open the main binary and if
+/// successful iterates over the STAB entries. The real parsing is
+/// done in handleStabSymbolTableEntry.
+ErrorOr<std::vector<std::unique_ptr<DebugMap>>> MachODebugMapParser::parse() {
+  auto MainBinOrError =
+      MainBinaryHolder.GetFilesAs<MachOObjectFile>(BinaryPath);
+  if (auto Error = MainBinOrError.getError())
+    return Error;
+
+  std::vector<std::unique_ptr<DebugMap>> Results;
+  for (const auto *Binary : *MainBinOrError)
+    Results.push_back(parseOneBinary(*Binary, BinaryPath));
+
+  return std::move(Results);
+}
+
 /// Interpret the STAB entries to fill the DebugMap.
 void MachODebugMapParser::handleStabSymbolTableEntry(uint32_t StringIndex,
                                                      uint8_t Type,
 /// Interpret the STAB entries to fill the DebugMap.
 void MachODebugMapParser::handleStabSymbolTableEntry(uint32_t StringIndex,
                                                      uint8_t Type,
@@ -254,10 +265,9 @@ void MachODebugMapParser::loadMainBinarySymbols(
 
 namespace llvm {
 namespace dsymutil {
 
 namespace llvm {
 namespace dsymutil {
-llvm::ErrorOr<std::unique_ptr<DebugMap>> parseDebugMap(StringRef InputFile,
-                                                       StringRef PrependPath,
-                                                       bool Verbose,
-                                                       bool InputIsYAML) {
+llvm::ErrorOr<std::vector<std::unique_ptr<DebugMap>>>
+parseDebugMap(StringRef InputFile, StringRef PrependPath, bool Verbose,
+              bool InputIsYAML) {
   if (!InputIsYAML) {
     MachODebugMapParser Parser(InputFile, PrependPath, Verbose);
     return Parser.parse();
   if (!InputIsYAML) {
     MachODebugMapParser Parser(InputFile, PrependPath, Verbose);
     return Parser.parse();
diff --git a/tools/dsymutil/MachOUtils.cpp b/tools/dsymutil/MachOUtils.cpp
new file mode 100644 (file)
index 0000000..15605fe
--- /dev/null
@@ -0,0 +1,86 @@
+//===-- MachOUtils.h - Mach-o specific helpers for dsymutil  --------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachOUtils.h"
+#include "dsymutil.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace llvm {
+namespace dsymutil {
+namespace MachOUtils {
+
+static bool runLipo(SmallVectorImpl<const char *> &Args) {
+  auto Path = sys::findProgramByName("lipo");
+
+  if (!Path) {
+    errs() << "error: lipo: " << Path.getError().message() << "\n";
+    return false;
+  }
+
+  std::string ErrMsg;
+  int result =
+      sys::ExecuteAndWait(*Path, Args.data(), nullptr, nullptr, 0, 0, &ErrMsg);
+  if (result) {
+    errs() << "error: lipo: " << ErrMsg << "\n";
+    return false;
+  }
+
+  return true;
+}
+
+bool generateUniversalBinary(SmallVectorImpl<ArchAndFilename> &ArchFiles,
+                             StringRef OutputFileName,
+                             const LinkOptions &Options) {
+  // No need to merge one file into a universal fat binary. First, try
+  // to move it (rename) to the final location. If that fails because
+  // of cross-device link issues then copy and delete.
+  if (ArchFiles.size() == 1) {
+    StringRef From(ArchFiles.front().Path);
+    if (sys::fs::rename(From, OutputFileName)) {
+      if (std::error_code EC = sys::fs::copy_file(From, OutputFileName)) {
+        errs() << "error: while copying " << From << " to " << OutputFileName
+               << ": " << EC.message() << "\n";
+        return false;
+      }
+      sys::fs::remove(From);
+    }
+    return true;
+  }
+
+  SmallVector<const char *, 8> Args;
+  Args.push_back("lipo");
+  Args.push_back("-create");
+
+  for (auto &Thin : ArchFiles)
+    Args.push_back(Thin.Path.c_str());
+
+  // Align segments to match dsymutil-classic alignment
+  for (auto &Thin : ArchFiles) {
+    Args.push_back("-segalign");
+    Args.push_back(Thin.Arch.c_str());
+    Args.push_back("20");
+  }
+
+  Args.push_back("-output");
+  Args.push_back(OutputFileName.data());
+  Args.push_back(nullptr);
+
+  if (Options.Verbose) {
+    outs() << "Running lipo\n";
+    for (auto Arg : Args)
+      outs() << ' ' << ((Arg == nullptr) ? "\n" : Arg);
+  }
+
+  return Options.NoOutput ? true : runLipo(Args);
+}
+}
+}
+}
diff --git a/tools/dsymutil/MachOUtils.h b/tools/dsymutil/MachOUtils.h
new file mode 100644 (file)
index 0000000..f1b2ad9
--- /dev/null
@@ -0,0 +1,30 @@
+//===-- MachOUtils.h - Mach-o specific helpers for dsymutil  --------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H
+#define LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H
+
+#include <string>
+#include "llvm/ADT/StringRef.h"
+
+namespace llvm {
+namespace dsymutil {
+struct LinkOptions;
+namespace MachOUtils {
+
+struct ArchAndFilename {
+  std::string Arch, Path;
+  ArchAndFilename(StringRef Arch, StringRef Path) : Arch(Arch), Path(Path) {}
+};
+
+bool generateUniversalBinary(SmallVectorImpl<ArchAndFilename> &ArchFiles,
+                             StringRef OutputFileName, const LinkOptions &);
+}
+}
+}
+#endif // LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H
index 07fdf314c442a1140b792b258a770ca9dcbb50ba..48eb9a14f0a9d1b7a0d161d96a2d88f4077cfbb5 100644 (file)
@@ -13,7 +13,9 @@
 //===----------------------------------------------------------------------===//
 
 #include "DebugMap.h"
 //===----------------------------------------------------------------------===//
 
 #include "DebugMap.h"
+#include "MachOUtils.h"
 #include "dsymutil.h"
 #include "dsymutil.h"
+#include "llvm/Support/FileUtilities.h"
 #include "llvm/Support/ManagedStatic.h"
 #include "llvm/Support/Options.h"
 #include "llvm/Support/PrettyStackTrace.h"
 #include "llvm/Support/ManagedStatic.h"
 #include "llvm/Support/Options.h"
 #include "llvm/Support/PrettyStackTrace.h"
@@ -68,7 +70,24 @@ static opt<bool> InputIsYAMLDebugMap(
     init(false), cat(DsymCategory));
 }
 
     init(false), cat(DsymCategory));
 }
 
-static std::string getOutputFileName(llvm::StringRef InputFile) {
+static std::string getOutputFileName(llvm::StringRef InputFile,
+                                     bool TempFile = false) {
+  if (TempFile) {
+    std::string OutputFile = (InputFile + ".tmp%%%%%%.dwarf").str();
+    int FD;
+    llvm::SmallString<128> UniqueFile;
+    if (auto EC = llvm::sys::fs::createUniqueFile(OutputFile, FD, UniqueFile)) {
+      llvm::errs() << "error: failed to create temporary outfile '"
+                   << OutputFile << "': " << EC.message() << '\n';
+      return "";
+    }
+    llvm::sys::RemoveFileOnSignal(UniqueFile);
+    // Close the file immediately. We know it is unique. It will be
+    // reopened and written to later.
+    llvm::raw_fd_ostream CloseImmediately(FD, true /* shouldClose */, true);
+    return UniqueFile.str();
+  }
+
   if (OutputFileOpt.empty()) {
     if (InputFile == "-")
       return "a.out.dwarf";
   if (OutputFileOpt.empty()) {
     if (InputFile == "-")
       return "a.out.dwarf";
@@ -78,6 +97,8 @@ static std::string getOutputFileName(llvm::StringRef InputFile) {
 }
 
 void llvm::dsymutil::exitDsymutil(int ExitStatus) {
 }
 
 void llvm::dsymutil::exitDsymutil(int ExitStatus) {
+  // Cleanup temporary files.
+  llvm::sys::RunInterruptHandlers();
   exit(ExitStatus);
 }
 
   exit(ExitStatus);
 }
 
@@ -118,24 +139,39 @@ int main(int argc, char **argv) {
   }
 
   for (auto &InputFile : InputFiles) {
   }
 
   for (auto &InputFile : InputFiles) {
-    auto DebugMapPtrOrErr =
+    auto DebugMapPtrsOrErr =
         parseDebugMap(InputFile, OsoPrependPath, Verbose, InputIsYAMLDebugMap);
 
         parseDebugMap(InputFile, OsoPrependPath, Verbose, InputIsYAMLDebugMap);
 
-    if (auto EC = DebugMapPtrOrErr.getError()) {
+    if (auto EC = DebugMapPtrsOrErr.getError()) {
       llvm::errs() << "error: cannot parse the debug map for \"" << InputFile
                    << "\": " << EC.message() << '\n';
       exitDsymutil(1);
     }
 
       llvm::errs() << "error: cannot parse the debug map for \"" << InputFile
                    << "\": " << EC.message() << '\n';
       exitDsymutil(1);
     }
 
-    if (Verbose || DumpDebugMap)
-      (*DebugMapPtrOrErr)->print(llvm::outs());
+    // If there is more than one link to execute, we need to generate
+    // temporary files.
+    bool NeedsTempFiles = !DumpDebugMap && (*DebugMapPtrsOrErr).size() != 1;
+    llvm::SmallVector<MachOUtils::ArchAndFilename, 4> TempFiles;
+    for (auto &Map : *DebugMapPtrsOrErr) {
+      if (Verbose || DumpDebugMap)
+        Map->print(llvm::outs());
+
+      if (DumpDebugMap)
+        continue;
 
 
-    if (DumpDebugMap)
-      continue;
+      std::string OutputFile = getOutputFileName(InputFile, NeedsTempFiles);
+      if (OutputFile.empty() || !linkDwarf(OutputFile, *Map, Options))
+        exitDsymutil(1);
 
 
-    std::string OutputFile = getOutputFileName(InputFile);
-    if (!linkDwarf(OutputFile, **DebugMapPtrOrErr, Options))
-      exitDsymuti(1);
+      if (NeedsTempFiles)
+        TempFiles.emplace_back(Map->getTriple().getArchName().str(),
+                               OutputFile);
+    }
+
+    if (NeedsTempFiles &&
+        !MachOUtils::generateUniversalBinary(
+            TempFiles, getOutputFileName(InputFile), Options))
+      exitDsymutil(1);
   }
 
   exitDsymutil(0);
   }
 
   exitDsymutil(0);
index 7c1810f4fad823b38423d3c1bd6a2554ba9a8c78..82f0deeb2e080f685e3b536225d508fbd8813946 100644 (file)
@@ -32,12 +32,12 @@ struct LinkOptions {
   LinkOptions() : Verbose(false), NoOutput(false) {}
 };
 
   LinkOptions() : Verbose(false), NoOutput(false) {}
 };
 
-/// \brief Extract the DebugMap from the given file.
-/// The file has to be a MachO object file.
-llvm::ErrorOr<std::unique_ptr<DebugMap>> parseDebugMap(StringRef InputFile,
-                                                       StringRef PrependPath,
-                                                       bool Verbose,
-                                                       bool InputIsYAML);
+/// \brief Extract the DebugMaps from the given file.
+/// The file has to be a MachO object file. Multiple debug maps can be
+/// returned when the file is universal (aka fat) binary.
+llvm::ErrorOr<std::vector<std::unique_ptr<DebugMap>>>
+parseDebugMap(StringRef InputFile, StringRef PrependPath, bool Verbose,
+              bool InputIsYAML);
 
 /// \brief Link the Dwarf debuginfo as directed by the passed DebugMap
 /// \p DM into a DwarfFile named \p OutputFilename.
 
 /// \brief Link the Dwarf debuginfo as directed by the passed DebugMap
 /// \p DM into a DwarfFile named \p OutputFilename.
@@ -48,7 +48,6 @@ bool linkDwarf(StringRef OutputFilename, const DebugMap &DM,
 /// \brief Exit the dsymutil process, cleaning up every temporary
 /// files that we created.
 LLVM_ATTRIBUTE_NORETURN void exitDsymutil(int ExitStatus);
 /// \brief Exit the dsymutil process, cleaning up every temporary
 /// files that we created.
 LLVM_ATTRIBUTE_NORETURN void exitDsymutil(int ExitStatus);
-
 }
 }
 #endif // LLVM_TOOLS_DSYMUTIL_DSYMUTIL_H
 }
 }
 #endif // LLVM_TOOLS_DSYMUTIL_DSYMUTIL_H