Release script: correctly symlink clang-tools-extra into the build (PR22765)
[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> 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));
55
56 static opt<bool> Verbose("verbose", desc("Verbosity level"), init(false),
57                          cat(DsymCategory));
58
59 static opt<bool>
60     NoOutput("no-output",
61              desc("Do the link in memory, but do not emit the result file."),
62              init(false), cat(DsymCategory));
63
64 static list<std::string> ArchFlags(
65     "arch",
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"
69          "default."),
70     ZeroOrMore, cat(DsymCategory));
71
72 static opt<bool>
73     NoODR("no-odr",
74           desc("Do not use ODR (One Definition Rule) for type uniquing."),
75           init(false), cat(DsymCategory));
76
77 static opt<bool> DumpDebugMap(
78     "dump-debug-map",
79     desc("Parse and dump the debug map to standard output. Not DWARF link "
80          "will take place."),
81     init(false), cat(DsymCategory));
82
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));
86 }
87
88 static bool createPlistFile(llvm::StringRef BundleRoot) {
89   if (NoOutput)
90     return true;
91
92   // Create plist file to write to.
93   llvm::SmallString<128> InfoPlist(BundleRoot);
94   llvm::sys::path::append(InfoPlist, "Contents/Info.plist");
95   std::error_code EC;
96   llvm::raw_fd_ostream PL(InfoPlist, EC, llvm::sys::fs::F_Text);
97   if (EC) {
98     llvm::errs() << "error: cannot create plist file " << InfoPlist << ": "
99                  << EC.message() << '\n';
100     return false;
101   }
102
103   // FIXME: Use CoreFoundation to get executable bundle info. Use
104   // dummy values for now.
105   std::string bundleVersionStr = "1", bundleShortVersionStr = "1.0",
106               bundleIDStr;
107
108   llvm::StringRef BundleID = *llvm::sys::path::rbegin(BundleRoot);
109   if (llvm::sys::path::extension(BundleRoot) == ".dSYM")
110     bundleIDStr = llvm::sys::path::stem(BundleID);
111   else
112     bundleIDStr = BundleID;
113
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"
119      << "\t<dict>\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"
134      << "\t</dict>\n"
135      << "</plist>\n";
136
137   PL.close();
138   return true;
139 }
140
141 static bool createBundleDir(llvm::StringRef BundleBase) {
142   if (NoOutput)
143     return true;
144
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";
151     return false;
152   }
153   return true;
154 }
155
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
162   // that.
163   if (NoOutput)
164     return llvm::sys::fs::createUniqueFile(Model, ResultPath);
165   return llvm::sys::fs::createUniqueFile(Model, ResultFD, ResultPath);
166 }
167
168 static std::string getOutputFileName(llvm::StringRef InputFile,
169                                      bool TempFile = false) {
170   if (TempFile) {
171     llvm::StringRef Basename =
172         OutputFileOpt.empty() ? InputFile : llvm::StringRef(OutputFileOpt);
173     llvm::Twine OutputFile = Basename + ".tmp%%%%%%.dwarf";
174     int FD;
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';
179       return "";
180     }
181     llvm::sys::RemoveFileOnSignal(UniqueFile);
182     if (!NoOutput) {
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);
186     }
187     return UniqueFile.str();
188   }
189
190   if (FlatOut) {
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();
196     }
197
198     return OutputFileOpt;
199   }
200
201   // We need to create/update a dSYM bundle.
202   // A bundle hierarchy looks like this:
203   //   <bundle name>.dSYM/
204   //       Contents/
205   //          Info.plist
206   //          Resources/
207   //             DWARF/
208   //                <DWARF file(s)>
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))
215     return "";
216
217   llvm::sys::path::append(BundleDir, "Contents", "Resources", "DWARF",
218                           llvm::sys::path::filename(DwarfFile));
219   return BundleDir.str();
220 }
221
222 void llvm::dsymutil::exitDsymutil(int ExitStatus) {
223   // Cleanup temporary files.
224   llvm::sys::RunInterruptHandlers();
225   exit(ExitStatus);
226 }
227
228 int main(int argc, char **argv) {
229   llvm::sys::PrintStackTraceOnErrorSignal();
230   llvm::PrettyStackTraceProgram StackPrinter(argc, argv);
231   llvm::llvm_shutdown_obj Shutdown;
232   LinkOptions Options;
233
234   HideUnrelatedOptions(DsymCategory);
235   llvm::cl::ParseCommandLineOptions(
236       argc, argv,
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");
241
242   if (Help)
243     PrintHelpMessage();
244
245   if (Version) {
246     llvm::cl::PrintVersionMessage();
247     return 0;
248   }
249
250   Options.Verbose = Verbose;
251   Options.NoOutput = NoOutput;
252   Options.NoODR = NoODR;
253
254   llvm::InitializeAllTargetInfos();
255   llvm::InitializeAllTargetMCs();
256   llvm::InitializeAllTargets();
257   llvm::InitializeAllAsmPrinters();
258
259   if (!FlatOut && OutputFileOpt == "-") {
260     llvm::errs() << "error: cannot emit to standard output without --flat\n";
261     return 1;
262   }
263
264   if (InputFiles.size() > 1 && FlatOut && !OutputFileOpt.empty()) {
265     llvm::errs() << "error: cannot use -o with multiple inputs in flat mode\n";
266     return 1;
267   }
268
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";
273       exitDsymutil(1);
274     }
275
276   for (auto &InputFile : InputFiles) {
277     auto DebugMapPtrsOrErr = parseDebugMap(InputFile, ArchFlags, OsoPrependPath,
278                                            Verbose, InputIsYAMLDebugMap);
279
280     if (auto EC = DebugMapPtrsOrErr.getError()) {
281       llvm::errs() << "error: cannot parse the debug map for \"" << InputFile
282                    << "\": " << EC.message() << '\n';
283       exitDsymutil(1);
284     }
285
286     if (DebugMapPtrsOrErr->empty()) {
287       llvm::errs() << "error: no architecture to link\n";
288       exitDsymutil(1);
289     }
290
291     // If there is more than one link to execute, we need to generate
292     // temporary files.
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());
298
299       if (DumpDebugMap)
300         continue;
301
302       std::string OutputFile = getOutputFileName(InputFile, NeedsTempFiles);
303       if (OutputFile.empty() || !linkDwarf(OutputFile, *Map, Options))
304         exitDsymutil(1);
305
306       if (NeedsTempFiles)
307         TempFiles.emplace_back(Map->getTriple().getArchName().str(),
308                                OutputFile);
309     }
310
311     if (NeedsTempFiles &&
312         !MachOUtils::generateUniversalBinary(
313             TempFiles, getOutputFileName(InputFile), Options))
314       exitDsymutil(1);
315   }
316
317   exitDsymutil(0);
318 }