e3e37a36262ed14a5a1650e9a3f40acb2549f3fd
[oota-llvm.git] / tools / dsymutil / dsymutil.cpp
1 //===-- dsymutil.cpp - Debug info dumping utility for llvm ----------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This program is a utility that aims to be a dropin replacement for
11 // Darwin's dsymutil.
12 //
13 //===----------------------------------------------------------------------===//
14
15 #include "DebugMap.h"
16 #include "MachOUtils.h"
17 #include "dsymutil.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"
27 #include <string>
28
29 using namespace llvm::dsymutil;
30
31 namespace {
32 using namespace llvm::cl;
33
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);
37
38 static list<std::string> InputFiles(Positional, OneOrMore,
39                                     desc("<input files>"), cat(DsymCategory));
40
41 static opt<std::string>
42     OutputFileOpt("o",
43                   desc("Specify the output file. default: <input file>.dwarf"),
44                   value_desc("filename"), cat(DsymCategory));
45
46 static opt<std::string> OsoPrependPath(
47     "oso-prepend-path",
48     desc("Specify a directory to prepend to the paths of object files."),
49     value_desc("path"), cat(DsymCategory));
50
51 static opt<bool> DumpStab(
52     "symtab",
53     desc("Dumps the symbol table found in executable or object file(s) and\n"
54          "exits."),
55     init(false), cat(DsymCategory));
56 static alias DumpStabA("s", desc("Alias for --symtab"), aliasopt(DumpStab));
57
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));
62
63 static opt<bool> Verbose("verbose", desc("Verbosity level"), init(false),
64                          cat(DsymCategory));
65
66 static opt<bool>
67     NoOutput("no-output",
68              desc("Do the link in memory, but do not emit the result file."),
69              init(false), cat(DsymCategory));
70
71 static list<std::string> ArchFlags(
72     "arch",
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"
76          "default."),
77     ZeroOrMore, cat(DsymCategory));
78
79 static opt<bool>
80     NoODR("no-odr",
81           desc("Do not use ODR (One Definition Rule) for type uniquing."),
82           init(false), cat(DsymCategory));
83
84 static opt<bool> DumpDebugMap(
85     "dump-debug-map",
86     desc("Parse and dump the debug map to standard output. Not DWARF link "
87          "will take place."),
88     init(false), cat(DsymCategory));
89
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));
93 }
94
95 static bool createPlistFile(llvm::StringRef BundleRoot) {
96   if (NoOutput)
97     return true;
98
99   // Create plist file to write to.
100   llvm::SmallString<128> InfoPlist(BundleRoot);
101   llvm::sys::path::append(InfoPlist, "Contents/Info.plist");
102   std::error_code EC;
103   llvm::raw_fd_ostream PL(InfoPlist, EC, llvm::sys::fs::F_Text);
104   if (EC) {
105     llvm::errs() << "error: cannot create plist file " << InfoPlist << ": "
106                  << EC.message() << '\n';
107     return false;
108   }
109
110   // FIXME: Use CoreFoundation to get executable bundle info. Use
111   // dummy values for now.
112   std::string bundleVersionStr = "1", bundleShortVersionStr = "1.0",
113               bundleIDStr;
114
115   llvm::StringRef BundleID = *llvm::sys::path::rbegin(BundleRoot);
116   if (llvm::sys::path::extension(BundleRoot) == ".dSYM")
117     bundleIDStr = llvm::sys::path::stem(BundleID);
118   else
119     bundleIDStr = BundleID;
120
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"
126      << "\t<dict>\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"
141      << "\t</dict>\n"
142      << "</plist>\n";
143
144   PL.close();
145   return true;
146 }
147
148 static bool createBundleDir(llvm::StringRef BundleBase) {
149   if (NoOutput)
150     return true;
151
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";
158     return false;
159   }
160   return true;
161 }
162
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
169   // that.
170   if (NoOutput)
171     return llvm::sys::fs::createUniqueFile(Model, ResultPath);
172   return llvm::sys::fs::createUniqueFile(Model, ResultFD, ResultPath);
173 }
174
175 static std::string getOutputFileName(llvm::StringRef InputFile,
176                                      bool TempFile = false) {
177   if (TempFile) {
178     llvm::StringRef Basename =
179         OutputFileOpt.empty() ? InputFile : llvm::StringRef(OutputFileOpt);
180     llvm::Twine OutputFile = Basename + ".tmp%%%%%%.dwarf";
181     int FD;
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';
186       return "";
187     }
188     llvm::sys::RemoveFileOnSignal(UniqueFile);
189     if (!NoOutput) {
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);
193     }
194     return UniqueFile.str();
195   }
196
197   if (FlatOut) {
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();
203     }
204
205     return OutputFileOpt;
206   }
207
208   // We need to create/update a dSYM bundle.
209   // A bundle hierarchy looks like this:
210   //   <bundle name>.dSYM/
211   //       Contents/
212   //          Info.plist
213   //          Resources/
214   //             DWARF/
215   //                <DWARF file(s)>
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))
222     return "";
223
224   llvm::sys::path::append(BundleDir, "Contents", "Resources", "DWARF",
225                           llvm::sys::path::filename(DwarfFile));
226   return BundleDir.str();
227 }
228
229 void llvm::dsymutil::exitDsymutil(int ExitStatus) {
230   // Cleanup temporary files.
231   llvm::sys::RunInterruptHandlers();
232   exit(ExitStatus);
233 }
234
235 int main(int argc, char **argv) {
236   llvm::sys::PrintStackTraceOnErrorSignal();
237   llvm::PrettyStackTraceProgram StackPrinter(argc, argv);
238   llvm::llvm_shutdown_obj Shutdown;
239   LinkOptions Options;
240
241   HideUnrelatedOptions(DsymCategory);
242   llvm::cl::ParseCommandLineOptions(
243       argc, argv,
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");
248
249   if (Help)
250     PrintHelpMessage();
251
252   if (Version) {
253     llvm::cl::PrintVersionMessage();
254     return 0;
255   }
256
257   Options.Verbose = Verbose;
258   Options.NoOutput = NoOutput;
259   Options.NoODR = NoODR;
260
261   llvm::InitializeAllTargetInfos();
262   llvm::InitializeAllTargetMCs();
263   llvm::InitializeAllTargets();
264   llvm::InitializeAllAsmPrinters();
265
266   if (!FlatOut && OutputFileOpt == "-") {
267     llvm::errs() << "error: cannot emit to standard output without --flat\n";
268     return 1;
269   }
270
271   if (InputFiles.size() > 1 && FlatOut && !OutputFileOpt.empty()) {
272     llvm::errs() << "error: cannot use -o with multiple inputs in flat mode\n";
273     return 1;
274   }
275
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";
280       exitDsymutil(1);
281     }
282
283   for (auto &InputFile : InputFiles) {
284     // Dump the symbol table for each input file and requested arch
285     if (DumpStab) {
286       if (!dumpStab(InputFile, ArchFlags, OsoPrependPath))
287         exitDsymutil(1);
288       continue;
289     }
290
291     auto DebugMapPtrsOrErr = parseDebugMap(InputFile, ArchFlags, OsoPrependPath,
292                                            Verbose, InputIsYAMLDebugMap);
293
294     if (auto EC = DebugMapPtrsOrErr.getError()) {
295       llvm::errs() << "error: cannot parse the debug map for \"" << InputFile
296                    << "\": " << EC.message() << '\n';
297       exitDsymutil(1);
298     }
299
300     if (DebugMapPtrsOrErr->empty()) {
301       llvm::errs() << "error: no architecture to link\n";
302       exitDsymutil(1);
303     }
304
305     // If there is more than one link to execute, we need to generate
306     // temporary files.
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());
312
313       if (DumpDebugMap)
314         continue;
315
316       if (Map->begin() == Map->end())
317         llvm::errs() << "warning: no debug symbols in executable (-arch "
318                      << MachOUtils::getArchName(Map->getTriple().getArchName())
319                      << ")\n";
320
321       std::string OutputFile = getOutputFileName(InputFile, NeedsTempFiles);
322       if (OutputFile.empty() || !linkDwarf(OutputFile, *Map, Options))
323         exitDsymutil(1);
324
325       if (NeedsTempFiles)
326         TempFiles.emplace_back(Map->getTriple().getArchName().str(),
327                                OutputFile);
328     }
329
330     if (NeedsTempFiles &&
331         !MachOUtils::generateUniversalBinary(
332             TempFiles, getOutputFileName(InputFile), Options))
333       exitDsymutil(1);
334   }
335
336   exitDsymutil(0);
337 }