1 //===-- dsymutil.cpp - Debug info dumping utility for llvm ----------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This program is a utility that aims to be a dropin replacement for
13 //===----------------------------------------------------------------------===//
16 #include "MachOUtils.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"
30 using namespace llvm::dsymutil
;
33 using namespace llvm::cl
;
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
);
39 static list
<std::string
> InputFiles(Positional
, OneOrMore
,
40 desc("<input files>"), cat(DsymCategory
));
42 static opt
<std::string
>
44 desc("Specify the output file. default: <input file>.dwarf"),
45 value_desc("filename"), cat(DsymCategory
));
47 static opt
<std::string
> OsoPrependPath(
49 desc("Specify a directory to prepend to the paths of object files."),
50 value_desc("path"), cat(DsymCategory
));
52 static opt
<bool> DumpStab(
54 desc("Dumps the symbol table found in executable or object file(s) and\n"
56 init(false), cat(DsymCategory
));
57 static alias
DumpStabA("s", desc("Alias for --symtab"), aliasopt(DumpStab
));
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
));
64 static opt
<bool> Verbose("verbose", desc("Verbosity level"), init(false),
69 desc("Do the link in memory, but do not emit the result file."),
70 init(false), cat(DsymCategory
));
72 static list
<std::string
> ArchFlags(
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"
78 ZeroOrMore
, cat(DsymCategory
));
82 desc("Do not use ODR (One Definition Rule) for type uniquing."),
83 init(false), cat(DsymCategory
));
85 static opt
<bool> DumpDebugMap(
87 desc("Parse and dump the debug map to standard output. Not DWARF link "
89 init(false), cat(DsymCategory
));
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
));
96 static bool createPlistFile(llvm::StringRef BundleRoot
) {
100 // Create plist file to write to.
101 llvm::SmallString
<128> InfoPlist(BundleRoot
);
102 llvm::sys::path::append(InfoPlist
, "Contents/Info.plist");
104 llvm::raw_fd_ostream
PL(InfoPlist
, EC
, llvm::sys::fs::F_Text
);
106 llvm::errs() << "error: cannot create plist file " << InfoPlist
<< ": "
107 << EC
.message() << '\n';
111 // FIXME: Use CoreFoundation to get executable bundle info. Use
112 // dummy values for now.
113 std::string bundleVersionStr
= "1", bundleShortVersionStr
= "1.0",
116 llvm::StringRef BundleID
= *llvm::sys::path::rbegin(BundleRoot
);
117 if (llvm::sys::path::extension(BundleRoot
) == ".dSYM")
118 bundleIDStr
= llvm::sys::path::stem(BundleID
);
120 bundleIDStr
= BundleID
;
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"
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"
149 static bool createBundleDir(llvm::StringRef BundleBase
) {
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";
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
172 return llvm::sys::fs::createUniqueFile(Model
, ResultPath
);
173 return llvm::sys::fs::createUniqueFile(Model
, ResultFD
, ResultPath
);
176 static std::string
getOutputFileName(llvm::StringRef InputFile
,
177 bool TempFile
= false) {
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
));
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';
192 llvm::sys::RemoveFileOnSignal(UniqueFile
);
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);
198 return UniqueFile
.str();
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();
209 return OutputFileOpt
;
212 // We need to create/update a dSYM bundle.
213 // A bundle hierarchy looks like this:
214 // <bundle name>.dSYM/
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
))
228 llvm::sys::path::append(BundleDir
, "Contents", "Resources", "DWARF",
229 llvm::sys::path::filename(DwarfFile
));
230 return BundleDir
.str();
233 void llvm::dsymutil::exitDsymutil(int ExitStatus
) {
234 // Cleanup temporary files.
235 llvm::sys::RunInterruptHandlers();
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
;
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
);
248 HideUnrelatedOptions(DsymCategory
);
249 llvm::cl::ParseCommandLineOptions(
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");
262 llvm::cl::PrintVersionMessage();
266 Options
.Verbose
= Verbose
;
267 Options
.NoOutput
= NoOutput
;
268 Options
.NoODR
= NoODR
;
269 Options
.PrependPath
= OsoPrependPath
;
271 llvm::InitializeAllTargetInfos();
272 llvm::InitializeAllTargetMCs();
273 llvm::InitializeAllTargets();
274 llvm::InitializeAllAsmPrinters();
276 if (!FlatOut
&& OutputFileOpt
== "-") {
277 llvm::errs() << "error: cannot emit to standard output without --flat\n";
281 if (InputFiles
.size() > 1 && FlatOut
&& !OutputFileOpt
.empty()) {
282 llvm::errs() << "error: cannot use -o with multiple inputs in flat mode\n";
286 for (const auto &Arch
: ArchFlags
)
287 if (Arch
!= "*" && Arch
!= "all" &&
288 !llvm::object::MachOObjectFile::isValidArch(Arch
)) {
289 llvm::errs() << "error: Unsupported cpu architecture: '" << Arch
<< "'\n";
293 for (auto &InputFile
: InputFiles
) {
294 // Dump the symbol table for each input file and requested arch
296 if (!dumpStab(InputFile
, ArchFlags
, OsoPrependPath
))
301 auto DebugMapPtrsOrErr
= parseDebugMap(InputFile
, ArchFlags
, OsoPrependPath
,
302 Verbose
, InputIsYAMLDebugMap
);
304 if (auto EC
= DebugMapPtrsOrErr
.getError()) {
305 llvm::errs() << "error: cannot parse the debug map for \"" << InputFile
306 << "\": " << EC
.message() << '\n';
310 if (DebugMapPtrsOrErr
->empty()) {
311 llvm::errs() << "error: no architecture to link\n";
315 // If there is more than one link to execute, we need to generate
317 bool NeedsTempFiles
= !DumpDebugMap
&& (*DebugMapPtrsOrErr
).size() != 1;
318 llvm::SmallVector
<MachOUtils::ArchAndFilename
, 4> TempFiles
;
319 for (auto &Map
: *DebugMapPtrsOrErr
) {
320 if (Verbose
|| DumpDebugMap
)
321 Map
->print(llvm::outs());
326 if (Map
->begin() == Map
->end())
327 llvm::errs() << "warning: no debug symbols in executable (-arch "
328 << MachOUtils::getArchName(Map
->getTriple().getArchName())
331 std::string OutputFile
= getOutputFileName(InputFile
, NeedsTempFiles
);
332 if (OutputFile
.empty() || !linkDwarf(OutputFile
, *Map
, Options
))
336 TempFiles
.emplace_back(Map
->getTriple().getArchName().str(),
340 if (NeedsTempFiles
&&
341 !MachOUtils::generateUniversalBinary(
342 TempFiles
, getOutputFileName(InputFile
), Options
, SDKPath
))