[AArch64][CollectLOH] Fix a regression that prevented us to detect chains of
[oota-llvm.git] / tools / dsymutil / dsymutil.cpp
index 00052bc30eafcc0a72dc3497cd1a38517c4d4b2c..9e112ba0727984d2953fd46fe7e21d7a8875d7fc 100644 (file)
 //===----------------------------------------------------------------------===//
 
 #include "DebugMap.h"
-#include "DwarfLinker.h"
-#include "MachODebugMapParser.h"
-
+#include "MachOUtils.h"
+#include "dsymutil.h"
+#include "llvm/Object/MachO.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FileUtilities.h"
 #include "llvm/Support/ManagedStatic.h"
-#include "llvm/Support/PrettyStackTrace.h"
 #include "llvm/Support/Options.h"
-#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/PrettyStackTrace.h"
 #include "llvm/Support/Signals.h"
-
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/TargetSelect.h"
 #include <string>
 
-static llvm::cl::opt<std::string> InputFile(llvm::cl::Positional,
-                                            llvm::cl::desc("<input file>"),
-                                            llvm::cl::init("-"));
+using namespace llvm::dsymutil;
+
+namespace {
+using namespace llvm::cl;
+
+OptionCategory DsymCategory("Specific Options");
+static opt<bool> Help("h", desc("Alias for -help"), Hidden);
+static opt<bool> Version("v", desc("Alias for -version"), Hidden);
+
+static list<std::string> InputFiles(Positional, OneOrMore,
+                                    desc("<input files>"), cat(DsymCategory));
+
+static opt<std::string>
+    OutputFileOpt("o",
+                  desc("Specify the output file. default: <input file>.dwarf"),
+                  value_desc("filename"), cat(DsymCategory));
+
+static opt<std::string> OsoPrependPath(
+    "oso-prepend-path",
+    desc("Specify a directory to prepend to the paths of object files."),
+    value_desc("path"), cat(DsymCategory));
+
+static opt<bool> FlatOut("flat",
+                         desc("Produce a flat dSYM file (not a bundle)."),
+                         init(false), cat(DsymCategory));
+static alias FlatOutA("f", desc("Alias for --flat"), aliasopt(FlatOut));
+
+static opt<bool> Verbose("verbose", desc("Verbosity level"), init(false),
+                         cat(DsymCategory));
+
+static opt<bool>
+    NoOutput("no-output",
+             desc("Do the link in memory, but do not emit the result file."),
+             init(false), cat(DsymCategory));
+
+static list<std::string> ArchFlags(
+    "arch",
+    desc("Link DWARF debug information only for specified CPU architecture\n"
+         "types. This option can be specified multiple times, once for each\n"
+         "desired architecture.  All cpu architectures will be linked by\n"
+         "default."),
+    ZeroOrMore, cat(DsymCategory));
+
+static opt<bool>
+    NoODR("no-odr",
+          desc("Do not use ODR (One Definition Rule) for type uniquing."),
+          init(false), cat(DsymCategory));
+
+static opt<bool> DumpDebugMap(
+    "dump-debug-map",
+    desc("Parse and dump the debug map to standard output. Not DWARF link "
+         "will take place."),
+    init(false), cat(DsymCategory));
+
+static opt<bool> InputIsYAMLDebugMap(
+    "y", desc("Treat the input file is a YAML debug map rather than a binary."),
+    init(false), cat(DsymCategory));
+}
+
+static bool createPlistFile(llvm::StringRef BundleRoot) {
+  if (NoOutput)
+    return true;
+
+  // Create plist file to write to.
+  llvm::SmallString<128> InfoPlist(BundleRoot);
+  llvm::sys::path::append(InfoPlist, "Contents/Info.plist");
+  std::error_code EC;
+  llvm::raw_fd_ostream PL(InfoPlist, EC, llvm::sys::fs::F_Text);
+  if (EC) {
+    llvm::errs() << "error: cannot create plist file " << InfoPlist << ": "
+                 << EC.message() << '\n';
+    return false;
+  }
+
+  // FIXME: Use CoreFoundation to get executable bundle info. Use
+  // dummy values for now.
+  std::string bundleVersionStr = "1", bundleShortVersionStr = "1.0",
+              bundleIDStr;
+
+  llvm::StringRef BundleID = *llvm::sys::path::rbegin(BundleRoot);
+  if (llvm::sys::path::extension(BundleRoot) == ".dSYM")
+    bundleIDStr = llvm::sys::path::stem(BundleID);
+  else
+    bundleIDStr = BundleID;
+
+  // Print out information to the plist file.
+  PL << "<?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n"
+     << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
+     << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
+     << "<plist version=\"1.0\">\n"
+     << "\t<dict>\n"
+     << "\t\t<key>CFBundleDevelopmentRegion</key>\n"
+     << "\t\t<string>English</string>\n"
+     << "\t\t<key>CFBundleIdentifier</key>\n"
+     << "\t\t<string>com.apple.xcode.dsym." << bundleIDStr << "</string>\n"
+     << "\t\t<key>CFBundleInfoDictionaryVersion</key>\n"
+     << "\t\t<string>6.0</string>\n"
+     << "\t\t<key>CFBundlePackageType</key>\n"
+     << "\t\t<string>dSYM</string>\n"
+     << "\t\t<key>CFBundleSignature</key>\n"
+     << "\t\t<string>\?\?\?\?</string>\n"
+     << "\t\t<key>CFBundleShortVersionString</key>\n"
+     << "\t\t<string>" << bundleShortVersionStr << "</string>\n"
+     << "\t\t<key>CFBundleVersion</key>\n"
+     << "\t\t<string>" << bundleVersionStr << "</string>\n"
+     << "\t</dict>\n"
+     << "</plist>\n";
+
+  PL.close();
+  return true;
+}
+
+static bool createBundleDir(llvm::StringRef BundleBase) {
+  if (NoOutput)
+    return true;
+
+  llvm::SmallString<128> Bundle(BundleBase);
+  llvm::sys::path::append(Bundle, "Contents", "Resources", "DWARF");
+  if (std::error_code EC = create_directories(Bundle.str(), true,
+                                              llvm::sys::fs::perms::all_all)) {
+    llvm::errs() << "error: cannot create directory " << Bundle << ": "
+                 << EC.message() << "\n";
+    return false;
+  }
+  return true;
+}
+
+static std::error_code getUniqueFile(const llvm::Twine &Model, int &ResultFD,
+                                     llvm::SmallVectorImpl<char> &ResultPath) {
+  // If in NoOutput mode, use the createUniqueFile variant that
+  // doesn't open the file but still generates a somewhat unique
+  // name. In the real usage scenario, we'll want to ensure that the
+  // file is trully unique, and creating it is the only way to achieve
+  // that.
+  if (NoOutput)
+    return llvm::sys::fs::createUniqueFile(Model, ResultPath);
+  return llvm::sys::fs::createUniqueFile(Model, ResultFD, ResultPath);
+}
 
-static llvm::cl::opt<std::string> OsoPrependPath("oso-prepend-path",
-                                                 llvm::cl::desc("<path>"));
+static std::string getOutputFileName(llvm::StringRef InputFile,
+                                     bool TempFile = false) {
+  if (TempFile) {
+    llvm::StringRef Basename =
+        OutputFileOpt.empty() ? InputFile : llvm::StringRef(OutputFileOpt);
+    llvm::Twine OutputFile = Basename + ".tmp%%%%%%.dwarf";
+    int FD;
+    llvm::SmallString<128> UniqueFile;
+    if (auto EC = getUniqueFile(OutputFile, FD, UniqueFile)) {
+      llvm::errs() << "error: failed to create temporary outfile '"
+                   << OutputFile << "': " << EC.message() << '\n';
+      return "";
+    }
+    llvm::sys::RemoveFileOnSignal(UniqueFile);
+    if (!NoOutput) {
+      // 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 (FlatOut) {
+    // If a flat dSYM has been requested, things are pretty simple.
+    if (OutputFileOpt.empty()) {
+      if (InputFile == "-")
+        return "a.out.dwarf";
+      return (InputFile + ".dwarf").str();
+    }
 
-static llvm::cl::opt<bool> Verbose("v", llvm::cl::desc("Verbosity level"),
-                                   llvm::cl::init(false));
+    return OutputFileOpt;
+  }
 
-static llvm::cl::opt<bool> ParseOnly("parse-only",
-                                     llvm::cl::desc("Only parse the debug map, do "
-                                                    "not actaully link the DWARF."),
-                                     llvm::cl::init(false));
+  // We need to create/update a dSYM bundle.
+  // A bundle hierarchy looks like this:
+  //   <bundle name>.dSYM/
+  //       Contents/
+  //          Info.plist
+  //          Resources/
+  //             DWARF/
+  //                <DWARF file(s)>
+  std::string DwarfFile =
+      InputFile == "-" ? llvm::StringRef("a.out") : InputFile;
+  llvm::SmallString<128> BundleDir(OutputFileOpt);
+  if (BundleDir.empty())
+    BundleDir = DwarfFile + ".dSYM";
+  if (!createBundleDir(BundleDir) || !createPlistFile(BundleDir))
+    return "";
+
+  llvm::sys::path::append(BundleDir, "Contents", "Resources", "DWARF",
+                          llvm::sys::path::filename(DwarfFile));
+  return BundleDir.str();
+}
+
+void llvm::dsymutil::exitDsymutil(int ExitStatus) {
+  // Cleanup temporary files.
+  llvm::sys::RunInterruptHandlers();
+  exit(ExitStatus);
+}
 
 int main(int argc, char **argv) {
   llvm::sys::PrintStackTraceOnErrorSignal();
   llvm::PrettyStackTraceProgram StackPrinter(argc, argv);
   llvm::llvm_shutdown_obj Shutdown;
+  LinkOptions Options;
+
+  HideUnrelatedOptions(DsymCategory);
+  llvm::cl::ParseCommandLineOptions(
+      argc, argv,
+      "manipulate archived DWARF debug symbol files.\n\n"
+      "dsymutil links the DWARF debug information found in the object files\n"
+      "for the executable <input file> by using debug symbols information\n"
+      "contained in its symbol table.\n");
 
-  llvm::cl::ParseCommandLineOptions(argc, argv, "llvm dsymutil\n");
+  if (Help)
+    PrintHelpMessage();
+
+  if (Version) {
+    llvm::cl::PrintVersionMessage();
+    return 0;
+  }
 
-  llvm::MachODebugMapParser Parser(InputFile);
-  Parser.setPreprendPath(OsoPrependPath);
-  llvm::ErrorOr<std::unique_ptr<llvm::DebugMap>> DebugMap = Parser.parse();
+  Options.Verbose = Verbose;
+  Options.NoOutput = NoOutput;
+  Options.NoODR = NoODR;
 
-  if (auto EC = DebugMap.getError()) {
-    llvm::errs() << "error: cannot parse the debug map for \"" << InputFile <<
-      "\": " << EC.message() << '\n';
+  llvm::InitializeAllTargetInfos();
+  llvm::InitializeAllTargetMCs();
+  llvm::InitializeAllTargets();
+  llvm::InitializeAllAsmPrinters();
+
+  if (!FlatOut && OutputFileOpt == "-") {
+    llvm::errs() << "error: cannot emit to standard output without --flat\n";
     return 1;
   }
 
-  if (Verbose)
-    (*DebugMap)->print(llvm::outs());
+  if (InputFiles.size() > 1 && FlatOut && !OutputFileOpt.empty()) {
+    llvm::errs() << "error: cannot use -o with multiple inputs in flat mode\n";
+    return 1;
+  }
 
-  if (ParseOnly)
-    return 0;
+  for (const auto &Arch : ArchFlags)
+    if (Arch != "*" && Arch != "all" &&
+        !llvm::object::MachOObjectFile::isValidArch(Arch)) {
+      llvm::errs() << "error: Unsupported cpu architecture: '" << Arch << "'\n";
+      exitDsymutil(1);
+    }
+
+  for (auto &InputFile : InputFiles) {
+    auto DebugMapPtrsOrErr = parseDebugMap(InputFile, ArchFlags, OsoPrependPath,
+                                           Verbose, InputIsYAMLDebugMap);
+
+    if (auto EC = DebugMapPtrsOrErr.getError()) {
+      llvm::errs() << "error: cannot parse the debug map for \"" << InputFile
+                   << "\": " << EC.message() << '\n';
+      exitDsymutil(1);
+    }
+
+    if (DebugMapPtrsOrErr->empty()) {
+      llvm::errs() << "error: no architecture to link\n";
+      exitDsymutil(1);
+    }
+
+    // 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 (Map->begin() == Map->end())
+        llvm::errs() << "warning: no debug symbols in executable (-arch "
+                     << MachOUtils::getArchName(Map->getTriple().getArchName())
+                     << ")\n";
+
+      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);
+    }
+
+    if (NeedsTempFiles &&
+        !MachOUtils::generateUniversalBinary(
+            TempFiles, getOutputFileName(InputFile), Options))
+      exitDsymutil(1);
+  }
 
-  llvm::DwarfLinker Linker(InputFile + ".dwarf");
-  return !Linker.link(*DebugMap.get());
+  exitDsymutil(0);
 }