dsymutil: Make -oso-prepend-path available to DwarfLinker.
[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   Options.PrependPath = OsoPrependPath;
261
262   llvm::InitializeAllTargetInfos();
263   llvm::InitializeAllTargetMCs();
264   llvm::InitializeAllTargets();
265   llvm::InitializeAllAsmPrinters();
266
267   if (!FlatOut && OutputFileOpt == "-") {
268     llvm::errs() << "error: cannot emit to standard output without --flat\n";
269     return 1;
270   }
271
272   if (InputFiles.size() > 1 && FlatOut && !OutputFileOpt.empty()) {
273     llvm::errs() << "error: cannot use -o with multiple inputs in flat mode\n";
274     return 1;
275   }
276
277   for (const auto &Arch : ArchFlags)
278     if (Arch != "*" && Arch != "all" &&
279         !llvm::object::MachOObjectFile::isValidArch(Arch)) {
280       llvm::errs() << "error: Unsupported cpu architecture: '" << Arch << "'\n";
281       exitDsymutil(1);
282     }
283
284   for (auto &InputFile : InputFiles) {
285     // Dump the symbol table for each input file and requested arch
286     if (DumpStab) {
287       if (!dumpStab(InputFile, ArchFlags, OsoPrependPath))
288         exitDsymutil(1);
289       continue;
290     }
291
292     auto DebugMapPtrsOrErr = parseDebugMap(InputFile, ArchFlags, OsoPrependPath,
293                                            Verbose, InputIsYAMLDebugMap);
294
295     if (auto EC = DebugMapPtrsOrErr.getError()) {
296       llvm::errs() << "error: cannot parse the debug map for \"" << InputFile
297                    << "\": " << EC.message() << '\n';
298       exitDsymutil(1);
299     }
300
301     if (DebugMapPtrsOrErr->empty()) {
302       llvm::errs() << "error: no architecture to link\n";
303       exitDsymutil(1);
304     }
305
306     // If there is more than one link to execute, we need to generate
307     // temporary files.
308     bool NeedsTempFiles = !DumpDebugMap && (*DebugMapPtrsOrErr).size() != 1;
309     llvm::SmallVector<MachOUtils::ArchAndFilename, 4> TempFiles;
310     for (auto &Map : *DebugMapPtrsOrErr) {
311       if (Verbose || DumpDebugMap)
312         Map->print(llvm::outs());
313
314       if (DumpDebugMap)
315         continue;
316
317       if (Map->begin() == Map->end())
318         llvm::errs() << "warning: no debug symbols in executable (-arch "
319                      << MachOUtils::getArchName(Map->getTriple().getArchName())
320                      << ")\n";
321
322       std::string OutputFile = getOutputFileName(InputFile, NeedsTempFiles);
323       if (OutputFile.empty() || !linkDwarf(OutputFile, *Map, Options))
324         exitDsymutil(1);
325
326       if (NeedsTempFiles)
327         TempFiles.emplace_back(Map->getTriple().getArchName().str(),
328                                OutputFile);
329     }
330
331     if (NeedsTempFiles &&
332         !MachOUtils::generateUniversalBinary(
333             TempFiles, getOutputFileName(InputFile), Options))
334       exitDsymutil(1);
335   }
336
337   exitDsymutil(0);
338 }