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