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> FlatOut("flat",
52 desc("Produce a flat dSYM file (not a bundle)."),
53 init(false), cat(DsymCategory));
54 static alias FlatOutA("f", desc("Alias for --flat"), aliasopt(FlatOut));
56 static opt<bool> Verbose("verbose", desc("Verbosity level"), init(false),
61 desc("Do the link in memory, but do not emit the result file."),
62 init(false), cat(DsymCategory));
64 static list<std::string> ArchFlags(
66 desc("Link DWARF debug information only for specified CPU architecture\n"
67 "types. This option can be specified multiple times, once for each\n"
68 "desired architecture. All cpu architectures will be linked by\n"
70 ZeroOrMore, cat(DsymCategory));
74 desc("Do not use ODR (One Definition Rule) for type uniquing."),
75 init(false), cat(DsymCategory));
77 static opt<bool> DumpDebugMap(
79 desc("Parse and dump the debug map to standard output. Not DWARF link "
81 init(false), cat(DsymCategory));
83 static opt<bool> InputIsYAMLDebugMap(
84 "y", desc("Treat the input file is a YAML debug map rather than a binary."),
85 init(false), cat(DsymCategory));
88 static bool createPlistFile(llvm::StringRef BundleRoot) {
92 // Create plist file to write to.
93 llvm::SmallString<128> InfoPlist(BundleRoot);
94 llvm::sys::path::append(InfoPlist, "Contents/Info.plist");
96 llvm::raw_fd_ostream PL(InfoPlist, EC, llvm::sys::fs::F_Text);
98 llvm::errs() << "error: cannot create plist file " << InfoPlist << ": "
99 << EC.message() << '\n';
103 // FIXME: Use CoreFoundation to get executable bundle info. Use
104 // dummy values for now.
105 std::string bundleVersionStr = "1", bundleShortVersionStr = "1.0",
108 llvm::StringRef BundleID = *llvm::sys::path::rbegin(BundleRoot);
109 if (llvm::sys::path::extension(BundleRoot) == ".dSYM")
110 bundleIDStr = llvm::sys::path::stem(BundleID);
112 bundleIDStr = BundleID;
114 // Print out information to the plist file.
115 PL << "<?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n"
116 << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
117 << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
118 << "<plist version=\"1.0\">\n"
120 << "\t\t<key>CFBundleDevelopmentRegion</key>\n"
121 << "\t\t<string>English</string>\n"
122 << "\t\t<key>CFBundleIdentifier</key>\n"
123 << "\t\t<string>com.apple.xcode.dsym." << bundleIDStr << "</string>\n"
124 << "\t\t<key>CFBundleInfoDictionaryVersion</key>\n"
125 << "\t\t<string>6.0</string>\n"
126 << "\t\t<key>CFBundlePackageType</key>\n"
127 << "\t\t<string>dSYM</string>\n"
128 << "\t\t<key>CFBundleSignature</key>\n"
129 << "\t\t<string>\?\?\?\?</string>\n"
130 << "\t\t<key>CFBundleShortVersionString</key>\n"
131 << "\t\t<string>" << bundleShortVersionStr << "</string>\n"
132 << "\t\t<key>CFBundleVersion</key>\n"
133 << "\t\t<string>" << bundleVersionStr << "</string>\n"
141 static bool createBundleDir(llvm::StringRef BundleBase) {
145 llvm::SmallString<128> Bundle(BundleBase);
146 llvm::sys::path::append(Bundle, "Contents", "Resources", "DWARF");
147 if (std::error_code EC = create_directories(Bundle.str(), true,
148 llvm::sys::fs::perms::all_all)) {
149 llvm::errs() << "error: cannot create directory " << Bundle << ": "
150 << EC.message() << "\n";
156 static std::error_code getUniqueFile(const llvm::Twine &Model, int &ResultFD,
157 llvm::SmallVectorImpl<char> &ResultPath) {
158 // If in NoOutput mode, use the createUniqueFile variant that
159 // doesn't open the file but still generates a somewhat unique
160 // name. In the real usage scenario, we'll want to ensure that the
161 // file is trully unique, and creating it is the only way to achieve
164 return llvm::sys::fs::createUniqueFile(Model, ResultPath);
165 return llvm::sys::fs::createUniqueFile(Model, ResultFD, ResultPath);
168 static std::string getOutputFileName(llvm::StringRef InputFile,
169 bool TempFile = false) {
171 llvm::StringRef Basename =
172 OutputFileOpt.empty() ? InputFile : llvm::StringRef(OutputFileOpt);
173 llvm::Twine OutputFile = Basename + ".tmp%%%%%%.dwarf";
175 llvm::SmallString<128> UniqueFile;
176 if (auto EC = getUniqueFile(OutputFile, FD, UniqueFile)) {
177 llvm::errs() << "error: failed to create temporary outfile '"
178 << OutputFile << "': " << EC.message() << '\n';
181 llvm::sys::RemoveFileOnSignal(UniqueFile);
183 // Close the file immediately. We know it is unique. It will be
184 // reopened and written to later.
185 llvm::raw_fd_ostream CloseImmediately(FD, true /* shouldClose */, true);
187 return UniqueFile.str();
191 // If a flat dSYM has been requested, things are pretty simple.
192 if (OutputFileOpt.empty()) {
193 if (InputFile == "-")
194 return "a.out.dwarf";
195 return (InputFile + ".dwarf").str();
198 return OutputFileOpt;
201 // We need to create/update a dSYM bundle.
202 // A bundle hierarchy looks like this:
203 // <bundle name>.dSYM/
209 std::string DwarfFile =
210 InputFile == "-" ? llvm::StringRef("a.out") : InputFile;
211 llvm::SmallString<128> BundleDir(OutputFileOpt);
212 if (BundleDir.empty())
213 BundleDir = DwarfFile + ".dSYM";
214 if (!createBundleDir(BundleDir) || !createPlistFile(BundleDir))
217 llvm::sys::path::append(BundleDir, "Contents", "Resources", "DWARF",
218 llvm::sys::path::filename(DwarfFile));
219 return BundleDir.str();
222 void llvm::dsymutil::exitDsymutil(int ExitStatus) {
223 // Cleanup temporary files.
224 llvm::sys::RunInterruptHandlers();
228 int main(int argc, char **argv) {
229 llvm::sys::PrintStackTraceOnErrorSignal();
230 llvm::PrettyStackTraceProgram StackPrinter(argc, argv);
231 llvm::llvm_shutdown_obj Shutdown;
234 HideUnrelatedOptions(DsymCategory);
235 llvm::cl::ParseCommandLineOptions(
237 "manipulate archived DWARF debug symbol files.\n\n"
238 "dsymutil links the DWARF debug information found in the object files\n"
239 "for the executable <input file> by using debug symbols information\n"
240 "contained in its symbol table.\n");
246 llvm::cl::PrintVersionMessage();
250 Options.Verbose = Verbose;
251 Options.NoOutput = NoOutput;
252 Options.NoODR = NoODR;
254 llvm::InitializeAllTargetInfos();
255 llvm::InitializeAllTargetMCs();
256 llvm::InitializeAllTargets();
257 llvm::InitializeAllAsmPrinters();
259 if (!FlatOut && OutputFileOpt == "-") {
260 llvm::errs() << "error: cannot emit to standard output without --flat\n";
264 if (InputFiles.size() > 1 && FlatOut && !OutputFileOpt.empty()) {
265 llvm::errs() << "error: cannot use -o with multiple inputs in flat mode\n";
269 for (const auto &Arch : ArchFlags)
270 if (Arch != "*" && Arch != "all" &&
271 !llvm::object::MachOObjectFile::isValidArch(Arch)) {
272 llvm::errs() << "error: Unsupported cpu architecture: '" << Arch << "'\n";
276 for (auto &InputFile : InputFiles) {
277 auto DebugMapPtrsOrErr = parseDebugMap(InputFile, ArchFlags, OsoPrependPath,
278 Verbose, InputIsYAMLDebugMap);
280 if (auto EC = DebugMapPtrsOrErr.getError()) {
281 llvm::errs() << "error: cannot parse the debug map for \"" << InputFile
282 << "\": " << EC.message() << '\n';
286 if (DebugMapPtrsOrErr->empty()) {
287 llvm::errs() << "error: no architecture to link\n";
291 // If there is more than one link to execute, we need to generate
293 bool NeedsTempFiles = !DumpDebugMap && (*DebugMapPtrsOrErr).size() != 1;
294 llvm::SmallVector<MachOUtils::ArchAndFilename, 4> TempFiles;
295 for (auto &Map : *DebugMapPtrsOrErr) {
296 if (Verbose || DumpDebugMap)
297 Map->print(llvm::outs());
302 std::string OutputFile = getOutputFileName(InputFile, NeedsTempFiles);
303 if (OutputFile.empty() || !linkDwarf(OutputFile, *Map, Options))
307 TempFiles.emplace_back(Map->getTriple().getArchName().str(),
311 if (NeedsTempFiles &&
312 !MachOUtils::generateUniversalBinary(
313 TempFiles, getOutputFileName(InputFile), Options))