#include "DebugMap.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/Options.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/TargetSelect.h"
+#include <cstdint>
#include <string>
using namespace llvm::dsymutil;
desc("Specify a directory to prepend to the paths of object files."),
value_desc("path"), cat(DsymCategory));
+static opt<bool> DumpStab(
+ "symtab",
+ desc("Dumps the symbol table found in executable or object file(s) and\n"
+ "exits."),
+ init(false), cat(DsymCategory));
+static alias DumpStabA("s", desc("Alias for --symtab"), aliasopt(DumpStab));
+
+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));
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 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 std::string getOutputFileName(llvm::StringRef InputFile,
bool TempFile = false) {
if (TempFile) {
- std::string OutputFile = (InputFile + ".tmp%%%%%%.dwarf").str();
+ llvm::StringRef Basename =
+ OutputFileOpt.empty() ? InputFile : llvm::StringRef(OutputFileOpt);
+ llvm::Twine OutputFile = Basename + ".tmp%%%%%%.dwarf";
int FD;
llvm::SmallString<128> UniqueFile;
- if (auto EC = llvm::sys::fs::createUniqueFile(OutputFile, FD, 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);
- // 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);
+ 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 (OutputFileOpt.empty()) {
- if (InputFile == "-")
- return "a.out.dwarf";
- return (InputFile + ".dwarf").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();
+ }
+
+ return OutputFileOpt;
}
- return OutputFileOpt;
+
+ // 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) {
llvm::PrettyStackTraceProgram StackPrinter(argc, argv);
llvm::llvm_shutdown_obj Shutdown;
LinkOptions Options;
+ void *MainAddr = (void *)(intptr_t)&exitDsymutil;
+ std::string SDKPath = llvm::sys::fs::getMainExecutable(argv[0], MainAddr);
+ SDKPath = llvm::sys::path::parent_path(SDKPath);
HideUnrelatedOptions(DsymCategory);
llvm::cl::ParseCommandLineOptions(
Options.Verbose = Verbose;
Options.NoOutput = NoOutput;
Options.NoODR = NoODR;
+ Options.PrependPath = OsoPrependPath;
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllTargets();
llvm::InitializeAllAsmPrinters();
- if (InputFiles.size() > 1 && !OutputFileOpt.empty()) {
- llvm::errs() << "error: cannot use -o with multiple inputs\n";
+ if (!FlatOut && OutputFileOpt == "-") {
+ llvm::errs() << "error: cannot emit to standard output without --flat\n";
return 1;
}
+ if (InputFiles.size() > 1 && FlatOut && !OutputFileOpt.empty()) {
+ llvm::errs() << "error: cannot use -o with multiple inputs in flat mode\n";
+ return 1;
+ }
+
+ 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, OsoPrependPath, Verbose, InputIsYAMLDebugMap);
+ // Dump the symbol table for each input file and requested arch
+ if (DumpStab) {
+ if (!dumpStab(InputFile, ArchFlags, OsoPrependPath))
+ exitDsymutil(1);
+ continue;
+ }
+
+ auto DebugMapPtrsOrErr = parseDebugMap(InputFile, ArchFlags, OsoPrependPath,
+ Verbose, InputIsYAMLDebugMap);
if (auto EC = DebugMapPtrsOrErr.getError()) {
llvm::errs() << "error: cannot parse the debug map for \"" << InputFile
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;
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 &&
!MachOUtils::generateUniversalBinary(
- TempFiles, getOutputFileName(InputFile), Options))
+ TempFiles, getOutputFileName(InputFile), Options, SDKPath))
exitDsymutil(1);
}