e9ee57f3dee674e6dad863b2ed055a17f0f0e5f7
[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 <cstdint>
28 #include <string>
29
30 using namespace llvm::dsymutil;
31
32 namespace {
33 using namespace llvm::cl;
34
35 OptionCategory DsymCategory("Specific Options");
36 static opt<bool> Help("h", desc("Alias for -help"), Hidden);
37 static opt<bool> Version("v", desc("Alias for -version"), Hidden);
38
39 static list<std::string> InputFiles(Positional, OneOrMore,
40                                     desc("<input files>"), cat(DsymCategory));
41
42 static opt<std::string>
43     OutputFileOpt("o",
44                   desc("Specify the output file. default: <input file>.dwarf"),
45                   value_desc("filename"), cat(DsymCategory));
46
47 static opt<std::string> OsoPrependPath(
48     "oso-prepend-path",
49     desc("Specify a directory to prepend to the paths of object files."),
50     value_desc("path"), cat(DsymCategory));
51
52 static opt<bool> DumpStab(
53     "symtab",
54     desc("Dumps the symbol table found in executable or object file(s) and\n"
55          "exits."),
56     init(false), cat(DsymCategory));
57 static alias DumpStabA("s", desc("Alias for --symtab"), aliasopt(DumpStab));
58
59 static opt<bool> FlatOut("flat",
60                          desc("Produce a flat dSYM file (not a bundle)."),
61                          init(false), cat(DsymCategory));
62 static alias FlatOutA("f", desc("Alias for --flat"), aliasopt(FlatOut));
63
64 static opt<bool> Verbose("verbose", desc("Verbosity level"), init(false),
65                          cat(DsymCategory));
66
67 static opt<bool>
68     NoOutput("no-output",
69              desc("Do the link in memory, but do not emit the result file."),
70              init(false), cat(DsymCategory));
71
72 static list<std::string> ArchFlags(
73     "arch",
74     desc("Link DWARF debug information only for specified CPU architecture\n"
75          "types. This option can be specified multiple times, once for each\n"
76          "desired architecture.  All cpu architectures will be linked by\n"
77          "default."),
78     ZeroOrMore, cat(DsymCategory));
79
80 static opt<bool>
81     NoODR("no-odr",
82           desc("Do not use ODR (One Definition Rule) for type uniquing."),
83           init(false), cat(DsymCategory));
84
85 static opt<bool> DumpDebugMap(
86     "dump-debug-map",
87     desc("Parse and dump the debug map to standard output. Not DWARF link "
88          "will take place."),
89     init(false), cat(DsymCategory));
90
91 static opt<bool> InputIsYAMLDebugMap(
92     "y", desc("Treat the input file is a YAML debug map rather than a binary."),
93     init(false), cat(DsymCategory));
94 }
95
96 static bool createPlistFile(llvm::StringRef BundleRoot) {
97   if (NoOutput)
98     return true;
99
100   // Create plist file to write to.
101   llvm::SmallString<128> InfoPlist(BundleRoot);
102   llvm::sys::path::append(InfoPlist, "Contents/Info.plist");
103   std::error_code EC;
104   llvm::raw_fd_ostream PL(InfoPlist, EC, llvm::sys::fs::F_Text);
105   if (EC) {
106     llvm::errs() << "error: cannot create plist file " << InfoPlist << ": "
107                  << EC.message() << '\n';
108     return false;
109   }
110
111   // FIXME: Use CoreFoundation to get executable bundle info. Use
112   // dummy values for now.
113   std::string bundleVersionStr = "1", bundleShortVersionStr = "1.0",
114               bundleIDStr;
115
116   llvm::StringRef BundleID = *llvm::sys::path::rbegin(BundleRoot);
117   if (llvm::sys::path::extension(BundleRoot) == ".dSYM")
118     bundleIDStr = llvm::sys::path::stem(BundleID);
119   else
120     bundleIDStr = BundleID;
121
122   // Print out information to the plist file.
123   PL << "<?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n"
124      << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
125      << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
126      << "<plist version=\"1.0\">\n"
127      << "\t<dict>\n"
128      << "\t\t<key>CFBundleDevelopmentRegion</key>\n"
129      << "\t\t<string>English</string>\n"
130      << "\t\t<key>CFBundleIdentifier</key>\n"
131      << "\t\t<string>com.apple.xcode.dsym." << bundleIDStr << "</string>\n"
132      << "\t\t<key>CFBundleInfoDictionaryVersion</key>\n"
133      << "\t\t<string>6.0</string>\n"
134      << "\t\t<key>CFBundlePackageType</key>\n"
135      << "\t\t<string>dSYM</string>\n"
136      << "\t\t<key>CFBundleSignature</key>\n"
137      << "\t\t<string>\?\?\?\?</string>\n"
138      << "\t\t<key>CFBundleShortVersionString</key>\n"
139      << "\t\t<string>" << bundleShortVersionStr << "</string>\n"
140      << "\t\t<key>CFBundleVersion</key>\n"
141      << "\t\t<string>" << bundleVersionStr << "</string>\n"
142      << "\t</dict>\n"
143      << "</plist>\n";
144
145   PL.close();
146   return true;
147 }
148
149 static bool createBundleDir(llvm::StringRef BundleBase) {
150   if (NoOutput)
151     return true;
152
153   llvm::SmallString<128> Bundle(BundleBase);
154   llvm::sys::path::append(Bundle, "Contents", "Resources", "DWARF");
155   if (std::error_code EC = create_directories(Bundle.str(), true,
156                                               llvm::sys::fs::perms::all_all)) {
157     llvm::errs() << "error: cannot create directory " << Bundle << ": "
158                  << EC.message() << "\n";
159     return false;
160   }
161   return true;
162 }
163
164 static std::error_code getUniqueFile(const llvm::Twine &Model, int &ResultFD,
165                                      llvm::SmallVectorImpl<char> &ResultPath) {
166   // If in NoOutput mode, use the createUniqueFile variant that
167   // doesn't open the file but still generates a somewhat unique
168   // name. In the real usage scenario, we'll want to ensure that the
169   // file is trully unique, and creating it is the only way to achieve
170   // that.
171   if (NoOutput)
172     return llvm::sys::fs::createUniqueFile(Model, ResultPath);
173   return llvm::sys::fs::createUniqueFile(Model, ResultFD, ResultPath);
174 }
175
176 static std::string getOutputFileName(llvm::StringRef InputFile,
177                                      bool TempFile = false) {
178   if (TempFile) {
179     llvm::StringRef Basename =
180         OutputFileOpt.empty() ? InputFile : llvm::StringRef(OutputFileOpt);
181     llvm::Twine OutputFile = Basename + ".tmp%%%%%%.dwarf";
182     int FD;
183     llvm::SmallString<128> UniqueFile;
184     if (auto EC = getUniqueFile(OutputFile, FD, UniqueFile)) {
185       llvm::errs() << "error: failed to create temporary outfile '"
186                    << OutputFile << "': " << EC.message() << '\n';
187       return "";
188     }
189     llvm::sys::RemoveFileOnSignal(UniqueFile);
190     if (!NoOutput) {
191       // Close the file immediately. We know it is unique. It will be
192       // reopened and written to later.
193       llvm::raw_fd_ostream CloseImmediately(FD, true /* shouldClose */, true);
194     }
195     return UniqueFile.str();
196   }
197
198   if (FlatOut) {
199     // If a flat dSYM has been requested, things are pretty simple.
200     if (OutputFileOpt.empty()) {
201       if (InputFile == "-")
202         return "a.out.dwarf";
203       return (InputFile + ".dwarf").str();
204     }
205
206     return OutputFileOpt;
207   }
208
209   // We need to create/update a dSYM bundle.
210   // A bundle hierarchy looks like this:
211   //   <bundle name>.dSYM/
212   //       Contents/
213   //          Info.plist
214   //          Resources/
215   //             DWARF/
216   //                <DWARF file(s)>
217   std::string DwarfFile =
218       InputFile == "-" ? llvm::StringRef("a.out") : InputFile;
219   llvm::SmallString<128> BundleDir(OutputFileOpt);
220   if (BundleDir.empty())
221     BundleDir = DwarfFile + ".dSYM";
222   if (!createBundleDir(BundleDir) || !createPlistFile(BundleDir))
223     return "";
224
225   llvm::sys::path::append(BundleDir, "Contents", "Resources", "DWARF",
226                           llvm::sys::path::filename(DwarfFile));
227   return BundleDir.str();
228 }
229
230 void llvm::dsymutil::exitDsymutil(int ExitStatus) {
231   // Cleanup temporary files.
232   llvm::sys::RunInterruptHandlers();
233   exit(ExitStatus);
234 }
235
236 int main(int argc, char **argv) {
237   llvm::sys::PrintStackTraceOnErrorSignal();
238   llvm::PrettyStackTraceProgram StackPrinter(argc, argv);
239   llvm::llvm_shutdown_obj Shutdown;
240   LinkOptions Options;
241   void *MainAddr = (void *)(intptr_t)&exitDsymutil;
242   std::string SDKPath = llvm::sys::fs::getMainExecutable(argv[0], MainAddr);
243   SDKPath = llvm::sys::path::parent_path(SDKPath);
244
245   HideUnrelatedOptions(DsymCategory);
246   llvm::cl::ParseCommandLineOptions(
247       argc, argv,
248       "manipulate archived DWARF debug symbol files.\n\n"
249       "dsymutil links the DWARF debug information found in the object files\n"
250       "for the executable <input file> by using debug symbols information\n"
251       "contained in its symbol table.\n");
252
253   if (Help)
254     PrintHelpMessage();
255
256   if (Version) {
257     llvm::cl::PrintVersionMessage();
258     return 0;
259   }
260
261   Options.Verbose = Verbose;
262   Options.NoOutput = NoOutput;
263   Options.NoODR = NoODR;
264   Options.PrependPath = OsoPrependPath;
265
266   llvm::InitializeAllTargetInfos();
267   llvm::InitializeAllTargetMCs();
268   llvm::InitializeAllTargets();
269   llvm::InitializeAllAsmPrinters();
270
271   if (!FlatOut && OutputFileOpt == "-") {
272     llvm::errs() << "error: cannot emit to standard output without --flat\n";
273     return 1;
274   }
275
276   if (InputFiles.size() > 1 && FlatOut && !OutputFileOpt.empty()) {
277     llvm::errs() << "error: cannot use -o with multiple inputs in flat mode\n";
278     return 1;
279   }
280
281   for (const auto &Arch : ArchFlags)
282     if (Arch != "*" && Arch != "all" &&
283         !llvm::object::MachOObjectFile::isValidArch(Arch)) {
284       llvm::errs() << "error: Unsupported cpu architecture: '" << Arch << "'\n";
285       exitDsymutil(1);
286     }
287
288   for (auto &InputFile : InputFiles) {
289     // Dump the symbol table for each input file and requested arch
290     if (DumpStab) {
291       if (!dumpStab(InputFile, ArchFlags, OsoPrependPath))
292         exitDsymutil(1);
293       continue;
294     }
295
296     auto DebugMapPtrsOrErr = parseDebugMap(InputFile, ArchFlags, OsoPrependPath,
297                                            Verbose, InputIsYAMLDebugMap);
298
299     if (auto EC = DebugMapPtrsOrErr.getError()) {
300       llvm::errs() << "error: cannot parse the debug map for \"" << InputFile
301                    << "\": " << EC.message() << '\n';
302       exitDsymutil(1);
303     }
304
305     if (DebugMapPtrsOrErr->empty()) {
306       llvm::errs() << "error: no architecture to link\n";
307       exitDsymutil(1);
308     }
309
310     // If there is more than one link to execute, we need to generate
311     // temporary files.
312     bool NeedsTempFiles = !DumpDebugMap && (*DebugMapPtrsOrErr).size() != 1;
313     llvm::SmallVector<MachOUtils::ArchAndFilename, 4> TempFiles;
314     for (auto &Map : *DebugMapPtrsOrErr) {
315       if (Verbose || DumpDebugMap)
316         Map->print(llvm::outs());
317
318       if (DumpDebugMap)
319         continue;
320
321       if (Map->begin() == Map->end())
322         llvm::errs() << "warning: no debug symbols in executable (-arch "
323                      << MachOUtils::getArchName(Map->getTriple().getArchName())
324                      << ")\n";
325
326       std::string OutputFile = getOutputFileName(InputFile, NeedsTempFiles);
327       if (OutputFile.empty() || !linkDwarf(OutputFile, *Map, Options))
328         exitDsymutil(1);
329
330       if (NeedsTempFiles)
331         TempFiles.emplace_back(Map->getTriple().getArchName().str(),
332                                OutputFile);
333     }
334
335     if (NeedsTempFiles &&
336         !MachOUtils::generateUniversalBinary(
337             TempFiles, getOutputFileName(InputFile), Options, SDKPath))
338       exitDsymutil(1);
339   }
340
341   exitDsymutil(0);
342 }