• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
createPlistFile(llvm::StringRef BundleRoot)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 
createBundleDir(llvm::StringRef BundleBase)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 
getUniqueFile(const llvm::Twine & Model,int & ResultFD,llvm::SmallVectorImpl<char> & ResultPath)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 
getOutputFileName(llvm::StringRef InputFile,bool TempFile=false)176 static std::string getOutputFileName(llvm::StringRef InputFile,
177                                      bool TempFile = false) {
178   if (TempFile) {
179     llvm::SmallString<128> TmpFile;
180     llvm::sys::path::system_temp_directory(true, TmpFile);
181     llvm::StringRef Basename =
182         OutputFileOpt.empty() ? InputFile : llvm::StringRef(OutputFileOpt);
183     llvm::sys::path::append(TmpFile, llvm::sys::path::filename(Basename));
184 
185     int FD;
186     llvm::SmallString<128> UniqueFile;
187     if (auto EC = getUniqueFile(TmpFile + ".tmp%%%%%.dwarf", FD, UniqueFile)) {
188       llvm::errs() << "error: failed to create temporary outfile '"
189                    << TmpFile << "': " << EC.message() << '\n';
190       return "";
191     }
192     llvm::sys::RemoveFileOnSignal(UniqueFile);
193     if (!NoOutput) {
194       // Close the file immediately. We know it is unique. It will be
195       // reopened and written to later.
196       llvm::raw_fd_ostream CloseImmediately(FD, true /* shouldClose */, true);
197     }
198     return UniqueFile.str();
199   }
200 
201   if (FlatOut) {
202     // If a flat dSYM has been requested, things are pretty simple.
203     if (OutputFileOpt.empty()) {
204       if (InputFile == "-")
205         return "a.out.dwarf";
206       return (InputFile + ".dwarf").str();
207     }
208 
209     return OutputFileOpt;
210   }
211 
212   // We need to create/update a dSYM bundle.
213   // A bundle hierarchy looks like this:
214   //   <bundle name>.dSYM/
215   //       Contents/
216   //          Info.plist
217   //          Resources/
218   //             DWARF/
219   //                <DWARF file(s)>
220   std::string DwarfFile =
221       InputFile == "-" ? llvm::StringRef("a.out") : InputFile;
222   llvm::SmallString<128> BundleDir(OutputFileOpt);
223   if (BundleDir.empty())
224     BundleDir = DwarfFile + ".dSYM";
225   if (!createBundleDir(BundleDir) || !createPlistFile(BundleDir))
226     return "";
227 
228   llvm::sys::path::append(BundleDir, "Contents", "Resources", "DWARF",
229                           llvm::sys::path::filename(DwarfFile));
230   return BundleDir.str();
231 }
232 
exitDsymutil(int ExitStatus)233 void llvm::dsymutil::exitDsymutil(int ExitStatus) {
234   // Cleanup temporary files.
235   llvm::sys::RunInterruptHandlers();
236   exit(ExitStatus);
237 }
238 
main(int argc,char ** argv)239 int main(int argc, char **argv) {
240   llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
241   llvm::PrettyStackTraceProgram StackPrinter(argc, argv);
242   llvm::llvm_shutdown_obj Shutdown;
243   LinkOptions Options;
244   void *MainAddr = (void *)(intptr_t)&exitDsymutil;
245   std::string SDKPath = llvm::sys::fs::getMainExecutable(argv[0], MainAddr);
246   SDKPath = llvm::sys::path::parent_path(SDKPath);
247 
248   HideUnrelatedOptions(DsymCategory);
249   llvm::cl::ParseCommandLineOptions(
250       argc, argv,
251       "manipulate archived DWARF debug symbol files.\n\n"
252       "dsymutil links the DWARF debug information found in the object files\n"
253       "for the executable <input file> by using debug symbols information\n"
254       "contained in its symbol table.\n");
255 
256   if (Help)
257     PrintHelpMessage();
258 
259   if (Version) {
260     llvm::cl::PrintVersionMessage();
261     return 0;
262   }
263 
264   Options.Verbose = Verbose;
265   Options.NoOutput = NoOutput;
266   Options.NoODR = NoODR;
267   Options.PrependPath = OsoPrependPath;
268 
269   llvm::InitializeAllTargetInfos();
270   llvm::InitializeAllTargetMCs();
271   llvm::InitializeAllTargets();
272   llvm::InitializeAllAsmPrinters();
273 
274   if (!FlatOut && OutputFileOpt == "-") {
275     llvm::errs() << "error: cannot emit to standard output without --flat\n";
276     return 1;
277   }
278 
279   if (InputFiles.size() > 1 && FlatOut && !OutputFileOpt.empty()) {
280     llvm::errs() << "error: cannot use -o with multiple inputs in flat mode\n";
281     return 1;
282   }
283 
284   for (const auto &Arch : ArchFlags)
285     if (Arch != "*" && Arch != "all" &&
286         !llvm::object::MachOObjectFile::isValidArch(Arch)) {
287       llvm::errs() << "error: Unsupported cpu architecture: '" << Arch << "'\n";
288       exitDsymutil(1);
289     }
290 
291   for (auto &InputFile : InputFiles) {
292     // Dump the symbol table for each input file and requested arch
293     if (DumpStab) {
294       if (!dumpStab(InputFile, ArchFlags, OsoPrependPath))
295         exitDsymutil(1);
296       continue;
297     }
298 
299     auto DebugMapPtrsOrErr = parseDebugMap(InputFile, ArchFlags, OsoPrependPath,
300                                            Verbose, InputIsYAMLDebugMap);
301 
302     if (auto EC = DebugMapPtrsOrErr.getError()) {
303       llvm::errs() << "error: cannot parse the debug map for \"" << InputFile
304                    << "\": " << EC.message() << '\n';
305       exitDsymutil(1);
306     }
307 
308     if (DebugMapPtrsOrErr->empty()) {
309       llvm::errs() << "error: no architecture to link\n";
310       exitDsymutil(1);
311     }
312 
313     // If there is more than one link to execute, we need to generate
314     // temporary files.
315     bool NeedsTempFiles = !DumpDebugMap && (*DebugMapPtrsOrErr).size() != 1;
316     llvm::SmallVector<MachOUtils::ArchAndFilename, 4> TempFiles;
317     for (auto &Map : *DebugMapPtrsOrErr) {
318       if (Verbose || DumpDebugMap)
319         Map->print(llvm::outs());
320 
321       if (DumpDebugMap)
322         continue;
323 
324       if (Map->begin() == Map->end())
325         llvm::errs() << "warning: no debug symbols in executable (-arch "
326                      << MachOUtils::getArchName(Map->getTriple().getArchName())
327                      << ")\n";
328 
329       std::string OutputFile = getOutputFileName(InputFile, NeedsTempFiles);
330       if (OutputFile.empty() || !linkDwarf(OutputFile, *Map, Options))
331         exitDsymutil(1);
332 
333       if (NeedsTempFiles)
334         TempFiles.emplace_back(Map->getTriple().getArchName().str(),
335                                OutputFile);
336     }
337 
338     if (NeedsTempFiles &&
339         !MachOUtils::generateUniversalBinary(
340             TempFiles, getOutputFileName(InputFile), Options, SDKPath))
341       exitDsymutil(1);
342   }
343 
344   exitDsymutil(0);
345 }
346