1 //===-- dsymutil.cpp - Debug info dumping utility for llvm ----------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This program is a utility that aims to be a dropin replacement for
13 //===----------------------------------------------------------------------===//
16 #include "MachOUtils.h"
18 #include "llvm/Object/MachO.h"
19 #include "llvm/Support/FileSystem.h"
20 #include "llvm/Support/FileUtilities.h"
21 #include "llvm/Support/ManagedStatic.h"
22 #include "llvm/Support/Options.h"
23 #include "llvm/Support/PrettyStackTrace.h"
24 #include "llvm/Support/Signals.h"
25 #include "llvm/Support/raw_ostream.h"
26 #include "llvm/Support/TargetSelect.h"
29 using namespace llvm::dsymutil;
32 using namespace llvm::cl;
34 OptionCategory DsymCategory("Specific Options");
35 static opt<bool> Help("h", desc("Alias for -help"), Hidden);
36 static opt<bool> Version("v", desc("Alias for -version"), Hidden);
38 static list<std::string> InputFiles(Positional, OneOrMore,
39 desc("<input files>"), cat(DsymCategory));
41 static opt<std::string>
43 desc("Specify the output file. default: <input file>.dwarf"),
44 value_desc("filename"), cat(DsymCategory));
46 static opt<std::string> OsoPrependPath(
48 desc("Specify a directory to prepend to the paths of object files."),
49 value_desc("path"), cat(DsymCategory));
51 static opt<bool> DumpStab(
53 desc("Dumps the symbol table found in executable or object file(s) and\n"
55 init(false), cat(DsymCategory));
56 static alias DumpStabA("s", desc("Alias for --symtab"), aliasopt(DumpStab));
58 static opt<bool> FlatOut("flat",
59 desc("Produce a flat dSYM file (not a bundle)."),
60 init(false), cat(DsymCategory));
61 static alias FlatOutA("f", desc("Alias for --flat"), aliasopt(FlatOut));
63 static opt<bool> Verbose("verbose", desc("Verbosity level"), init(false),
68 desc("Do the link in memory, but do not emit the result file."),
69 init(false), cat(DsymCategory));
71 static list<std::string> ArchFlags(
73 desc("Link DWARF debug information only for specified CPU architecture\n"
74 "types. This option can be specified multiple times, once for each\n"
75 "desired architecture. All cpu architectures will be linked by\n"
77 ZeroOrMore, cat(DsymCategory));
81 desc("Do not use ODR (One Definition Rule) for type uniquing."),
82 init(false), cat(DsymCategory));
84 static opt<bool> DumpDebugMap(
86 desc("Parse and dump the debug map to standard output. Not DWARF link "
88 init(false), cat(DsymCategory));
90 static opt<bool> InputIsYAMLDebugMap(
91 "y", desc("Treat the input file is a YAML debug map rather than a binary."),
92 init(false), cat(DsymCategory));
95 static bool createPlistFile(llvm::StringRef BundleRoot) {
99 // Create plist file to write to.
100 llvm::SmallString<128> InfoPlist(BundleRoot);
101 llvm::sys::path::append(InfoPlist, "Contents/Info.plist");
103 llvm::raw_fd_ostream PL(InfoPlist, EC, llvm::sys::fs::F_Text);
105 llvm::errs() << "error: cannot create plist file " << InfoPlist << ": "
106 << EC.message() << '\n';
110 // FIXME: Use CoreFoundation to get executable bundle info. Use
111 // dummy values for now.
112 std::string bundleVersionStr = "1", bundleShortVersionStr = "1.0",
115 llvm::StringRef BundleID = *llvm::sys::path::rbegin(BundleRoot);
116 if (llvm::sys::path::extension(BundleRoot) == ".dSYM")
117 bundleIDStr = llvm::sys::path::stem(BundleID);
119 bundleIDStr = BundleID;
121 // Print out information to the plist file.
122 PL << "<?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n"
123 << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
124 << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
125 << "<plist version=\"1.0\">\n"
127 << "\t\t<key>CFBundleDevelopmentRegion</key>\n"
128 << "\t\t<string>English</string>\n"
129 << "\t\t<key>CFBundleIdentifier</key>\n"
130 << "\t\t<string>com.apple.xcode.dsym." << bundleIDStr << "</string>\n"
131 << "\t\t<key>CFBundleInfoDictionaryVersion</key>\n"
132 << "\t\t<string>6.0</string>\n"
133 << "\t\t<key>CFBundlePackageType</key>\n"
134 << "\t\t<string>dSYM</string>\n"
135 << "\t\t<key>CFBundleSignature</key>\n"
136 << "\t\t<string>\?\?\?\?</string>\n"
137 << "\t\t<key>CFBundleShortVersionString</key>\n"
138 << "\t\t<string>" << bundleShortVersionStr << "</string>\n"
139 << "\t\t<key>CFBundleVersion</key>\n"
140 << "\t\t<string>" << bundleVersionStr << "</string>\n"
148 static bool createBundleDir(llvm::StringRef BundleBase) {
152 llvm::SmallString<128> Bundle(BundleBase);
153 llvm::sys::path::append(Bundle, "Contents", "Resources", "DWARF");
154 if (std::error_code EC = create_directories(Bundle.str(), true,
155 llvm::sys::fs::perms::all_all)) {
156 llvm::errs() << "error: cannot create directory " << Bundle << ": "
157 << EC.message() << "\n";
163 static std::error_code getUniqueFile(const llvm::Twine &Model, int &ResultFD,
164 llvm::SmallVectorImpl<char> &ResultPath) {
165 // If in NoOutput mode, use the createUniqueFile variant that
166 // doesn't open the file but still generates a somewhat unique
167 // name. In the real usage scenario, we'll want to ensure that the
168 // file is trully unique, and creating it is the only way to achieve
171 return llvm::sys::fs::createUniqueFile(Model, ResultPath);
172 return llvm::sys::fs::createUniqueFile(Model, ResultFD, ResultPath);
175 static std::string getOutputFileName(llvm::StringRef InputFile,
176 bool TempFile = false) {
178 llvm::StringRef Basename =
179 OutputFileOpt.empty() ? InputFile : llvm::StringRef(OutputFileOpt);
180 llvm::Twine OutputFile = Basename + ".tmp%%%%%%.dwarf";
182 llvm::SmallString<128> UniqueFile;
183 if (auto EC = getUniqueFile(OutputFile, FD, UniqueFile)) {
184 llvm::errs() << "error: failed to create temporary outfile '"
185 << OutputFile << "': " << EC.message() << '\n';
188 llvm::sys::RemoveFileOnSignal(UniqueFile);
190 // Close the file immediately. We know it is unique. It will be
191 // reopened and written to later.
192 llvm::raw_fd_ostream CloseImmediately(FD, true /* shouldClose */, true);
194 return UniqueFile.str();
198 // If a flat dSYM has been requested, things are pretty simple.
199 if (OutputFileOpt.empty()) {
200 if (InputFile == "-")
201 return "a.out.dwarf";
202 return (InputFile + ".dwarf").str();
205 return OutputFileOpt;
208 // We need to create/update a dSYM bundle.
209 // A bundle hierarchy looks like this:
210 // <bundle name>.dSYM/
216 std::string DwarfFile =
217 InputFile == "-" ? llvm::StringRef("a.out") : InputFile;
218 llvm::SmallString<128> BundleDir(OutputFileOpt);
219 if (BundleDir.empty())
220 BundleDir = DwarfFile + ".dSYM";
221 if (!createBundleDir(BundleDir) || !createPlistFile(BundleDir))
224 llvm::sys::path::append(BundleDir, "Contents", "Resources", "DWARF",
225 llvm::sys::path::filename(DwarfFile));
226 return BundleDir.str();
229 void llvm::dsymutil::exitDsymutil(int ExitStatus) {
230 // Cleanup temporary files.
231 llvm::sys::RunInterruptHandlers();
235 int main(int argc, char **argv) {
236 llvm::sys::PrintStackTraceOnErrorSignal();
237 llvm::PrettyStackTraceProgram StackPrinter(argc, argv);
238 llvm::llvm_shutdown_obj Shutdown;
241 HideUnrelatedOptions(DsymCategory);
242 llvm::cl::ParseCommandLineOptions(
244 "manipulate archived DWARF debug symbol files.\n\n"
245 "dsymutil links the DWARF debug information found in the object files\n"
246 "for the executable <input file> by using debug symbols information\n"
247 "contained in its symbol table.\n");
253 llvm::cl::PrintVersionMessage();
257 Options.Verbose = Verbose;
258 Options.NoOutput = NoOutput;
259 Options.NoODR = NoODR;
261 llvm::InitializeAllTargetInfos();
262 llvm::InitializeAllTargetMCs();
263 llvm::InitializeAllTargets();
264 llvm::InitializeAllAsmPrinters();
266 if (!FlatOut && OutputFileOpt == "-") {
267 llvm::errs() << "error: cannot emit to standard output without --flat\n";
271 if (InputFiles.size() > 1 && FlatOut && !OutputFileOpt.empty()) {
272 llvm::errs() << "error: cannot use -o with multiple inputs in flat mode\n";
276 for (const auto &Arch : ArchFlags)
277 if (Arch != "*" && Arch != "all" &&
278 !llvm::object::MachOObjectFile::isValidArch(Arch)) {
279 llvm::errs() << "error: Unsupported cpu architecture: '" << Arch << "'\n";
283 for (auto &InputFile : InputFiles) {
284 // Dump the symbol table for each input file and requested arch
286 if (!dumpStab(InputFile, ArchFlags, OsoPrependPath))
291 auto DebugMapPtrsOrErr = parseDebugMap(InputFile, ArchFlags, OsoPrependPath,
292 Verbose, InputIsYAMLDebugMap);
294 if (auto EC = DebugMapPtrsOrErr.getError()) {
295 llvm::errs() << "error: cannot parse the debug map for \"" << InputFile
296 << "\": " << EC.message() << '\n';
300 if (DebugMapPtrsOrErr->empty()) {
301 llvm::errs() << "error: no architecture to link\n";
305 // If there is more than one link to execute, we need to generate
307 bool NeedsTempFiles = !DumpDebugMap && (*DebugMapPtrsOrErr).size() != 1;
308 llvm::SmallVector<MachOUtils::ArchAndFilename, 4> TempFiles;
309 for (auto &Map : *DebugMapPtrsOrErr) {
310 if (Verbose || DumpDebugMap)
311 Map->print(llvm::outs());
316 if (Map->begin() == Map->end())
317 llvm::errs() << "warning: no debug symbols in executable (-arch "
318 << MachOUtils::getArchName(Map->getTriple().getArchName())
321 std::string OutputFile = getOutputFileName(InputFile, NeedsTempFiles);
322 if (OutputFile.empty() || !linkDwarf(OutputFile, *Map, Options))
326 TempFiles.emplace_back(Map->getTriple().getArchName().str(),
330 if (NeedsTempFiles &&
331 !MachOUtils::generateUniversalBinary(
332 TempFiles, getOutputFileName(InputFile), Options))