[llvm] r244087 - [dsymutil] Implement support for handling mach-o universal binaries as main input/output.

Frederic Riss friss at apple.com
Wed Aug 5 11:27:44 PDT 2015


Author: friss
Date: Wed Aug  5 13:27:44 2015
New Revision: 244087

URL: http://llvm.org/viewvc/llvm-project?rev=244087&view=rev
Log:
[dsymutil] Implement support for handling mach-o universal binaries as main input/output.

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.

Added:
    llvm/trunk/test/tools/dsymutil/Inputs/fat-test.dylib   (with props)
    llvm/trunk/test/tools/dsymutil/fat-binary-output.test
    llvm/trunk/tools/dsymutil/MachOUtils.cpp
    llvm/trunk/tools/dsymutil/MachOUtils.h
Modified:
    llvm/trunk/tools/dsymutil/CMakeLists.txt
    llvm/trunk/tools/dsymutil/DebugMap.cpp
    llvm/trunk/tools/dsymutil/DebugMap.h
    llvm/trunk/tools/dsymutil/MachODebugMapParser.cpp
    llvm/trunk/tools/dsymutil/dsymutil.cpp
    llvm/trunk/tools/dsymutil/dsymutil.h

Added: llvm/trunk/test/tools/dsymutil/Inputs/fat-test.dylib
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/dsymutil/Inputs/fat-test.dylib?rev=244087&view=auto
==============================================================================
Binary files llvm/trunk/test/tools/dsymutil/Inputs/fat-test.dylib (added) and llvm/trunk/test/tools/dsymutil/Inputs/fat-test.dylib Wed Aug  5 13:27:44 2015 differ

Propchange: llvm/trunk/test/tools/dsymutil/Inputs/fat-test.dylib
------------------------------------------------------------------------------
    svn:executable = *

Added: llvm/trunk/test/tools/dsymutil/fat-binary-output.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/dsymutil/fat-binary-output.test?rev=244087&view=auto
==============================================================================
--- llvm/trunk/test/tools/dsymutil/fat-binary-output.test (added)
+++ llvm/trunk/test/tools/dsymutil/fat-binary-output.test Wed Aug  5 13:27:44 2015
@@ -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
+

Modified: llvm/trunk/tools/dsymutil/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/dsymutil/CMakeLists.txt?rev=244087&r1=244086&r2=244087&view=diff
==============================================================================
--- llvm/trunk/tools/dsymutil/CMakeLists.txt (original)
+++ llvm/trunk/tools/dsymutil/CMakeLists.txt Wed Aug  5 13:27:44 2015
@@ -14,5 +14,6 @@ add_llvm_tool(llvm-dsymutil
   DebugMap.cpp
   DwarfLinker.cpp
   MachODebugMapParser.cpp
+  MachOUtils.cpp
   )
 

Modified: llvm/trunk/tools/dsymutil/DebugMap.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/dsymutil/DebugMap.cpp?rev=244087&r1=244086&r2=244087&view=diff
==============================================================================
--- llvm/trunk/tools/dsymutil/DebugMap.cpp (original)
+++ llvm/trunk/tools/dsymutil/DebugMap.cpp Wed Aug  5 13:27:44 2015
@@ -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);
@@ -114,8 +114,9 @@ DebugMap::parseYAMLDebugMap(StringRef In
 
   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);
 }
 }
 

Modified: llvm/trunk/tools/dsymutil/DebugMap.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/dsymutil/DebugMap.h?rev=244087&r1=244086&r2=244087&view=diff
==============================================================================
--- llvm/trunk/tools/dsymutil/DebugMap.h (original)
+++ llvm/trunk/tools/dsymutil/DebugMap.h Wed Aug  5 13:27:44 2015
@@ -102,7 +102,7 @@ public:
 #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);
 };
 

Modified: llvm/trunk/tools/dsymutil/MachODebugMapParser.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/dsymutil/MachODebugMapParser.cpp?rev=244087&r1=244086&r2=244087&view=diff
==============================================================================
--- llvm/trunk/tools/dsymutil/MachODebugMapParser.cpp (original)
+++ llvm/trunk/tools/dsymutil/MachODebugMapParser.cpp Wed Aug  5 13:27:44 2015
@@ -27,10 +27,12 @@ public:
         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.
-  ErrorOr<std::unique_ptr<DebugMap>> parse();
+  ErrorOr<std::vector<std::unique_ptr<DebugMap>>> parse();
 
 private:
   std::string BinaryPath;
@@ -55,6 +57,9 @@ private:
   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);
@@ -110,19 +115,9 @@ void MachODebugMapParser::switchToNewDeb
   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();
@@ -138,6 +133,22 @@ ErrorOr<std::unique_ptr<DebugMap>> MachO
   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,
@@ -254,10 +265,9 @@ void MachODebugMapParser::loadMainBinary
 
 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();

Added: llvm/trunk/tools/dsymutil/MachOUtils.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/dsymutil/MachOUtils.cpp?rev=244087&view=auto
==============================================================================
--- llvm/trunk/tools/dsymutil/MachOUtils.cpp (added)
+++ llvm/trunk/tools/dsymutil/MachOUtils.cpp Wed Aug  5 13:27:44 2015
@@ -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);
+}
+}
+}
+}

Added: llvm/trunk/tools/dsymutil/MachOUtils.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/dsymutil/MachOUtils.h?rev=244087&view=auto
==============================================================================
--- llvm/trunk/tools/dsymutil/MachOUtils.h (added)
+++ llvm/trunk/tools/dsymutil/MachOUtils.h Wed Aug  5 13:27:44 2015
@@ -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

Modified: llvm/trunk/tools/dsymutil/dsymutil.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/dsymutil/dsymutil.cpp?rev=244087&r1=244086&r2=244087&view=diff
==============================================================================
--- llvm/trunk/tools/dsymutil/dsymutil.cpp (original)
+++ llvm/trunk/tools/dsymutil/dsymutil.cpp Wed Aug  5 13:27:44 2015
@@ -13,7 +13,9 @@
 //===----------------------------------------------------------------------===//
 
 #include "DebugMap.h"
+#include "MachOUtils.h"
 #include "dsymutil.h"
+#include "llvm/Support/FileUtilities.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));
 }
 
-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";
@@ -78,6 +97,8 @@ static std::string getOutputFileName(llv
 }
 
 void llvm::dsymutil::exitDsymutil(int ExitStatus) {
+  // Cleanup temporary files.
+  llvm::sys::RunInterruptHandlers();
   exit(ExitStatus);
 }
 
@@ -118,24 +139,39 @@ int main(int argc, char **argv) {
   }
 
   for (auto &InputFile : InputFiles) {
-    auto DebugMapPtrOrErr =
+    auto DebugMapPtrsOrErr =
         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);
     }
 
-    if (Verbose || DumpDebugMap)
-      (*DebugMapPtrOrErr)->print(llvm::outs());
-
-    if (DumpDebugMap)
-      continue;
+    // 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;
+
+      std::string OutputFile = getOutputFileName(InputFile, NeedsTempFiles);
+      if (OutputFile.empty() || !linkDwarf(OutputFile, *Map, Options))
+        exitDsymutil(1);
+
+      if (NeedsTempFiles)
+        TempFiles.emplace_back(Map->getTriple().getArchName().str(),
+                               OutputFile);
+    }
 
-    std::string OutputFile = getOutputFileName(InputFile);
-    if (!linkDwarf(OutputFile, **DebugMapPtrOrErr, Options))
-      exitDsymuti(1);
+    if (NeedsTempFiles &&
+        !MachOUtils::generateUniversalBinary(
+            TempFiles, getOutputFileName(InputFile), Options))
+      exitDsymutil(1);
   }
 
   exitDsymutil(0);

Modified: llvm/trunk/tools/dsymutil/dsymutil.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/dsymutil/dsymutil.h?rev=244087&r1=244086&r2=244087&view=diff
==============================================================================
--- llvm/trunk/tools/dsymutil/dsymutil.h (original)
+++ llvm/trunk/tools/dsymutil/dsymutil.h Wed Aug  5 13:27:44 2015
@@ -32,12 +32,12 @@ struct LinkOptions {
   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.
@@ -48,7 +48,6 @@ bool linkDwarf(StringRef OutputFilename,
 /// \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




More information about the llvm-commits mailing list