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 static opt<bool> 72 NoTimestamp("no-swiftmodule-timestamp", 73 desc("Don't check timestamp for swiftmodule files."), 74 init(false), cat(DsymCategory)); 75 static list<std::string> ArchFlags( 76 "arch", 77 desc("Link DWARF debug information only for specified CPU architecture\n" 78 "types. This option can be specified multiple times, once for each\n" 79 "desired architecture. All cpu architectures will be linked by\n" 80 "default."), 81 ZeroOrMore, cat(DsymCategory)); 82 83 static opt<bool> 84 NoODR("no-odr", 85 desc("Do not use ODR (One Definition Rule) for type uniquing."), 86 init(false), cat(DsymCategory)); 87 88 static opt<bool> DumpDebugMap( 89 "dump-debug-map", 90 desc("Parse and dump the debug map to standard output. Not DWARF link " 91 "will take place."), 92 init(false), cat(DsymCategory)); 93 94 static opt<bool> InputIsYAMLDebugMap( 95 "y", desc("Treat the input file is a YAML debug map rather than a binary."), 96 init(false), cat(DsymCategory)); 97 } 98 99 static bool createPlistFile(llvm::StringRef BundleRoot) { 100 if (NoOutput) 101 return true; 102 103 // Create plist file to write to. 104 llvm::SmallString<128> InfoPlist(BundleRoot); 105 llvm::sys::path::append(InfoPlist, "Contents/Info.plist"); 106 std::error_code EC; 107 llvm::raw_fd_ostream PL(InfoPlist, EC, llvm::sys::fs::F_Text); 108 if (EC) { 109 llvm::errs() << "error: cannot create plist file " << InfoPlist << ": " 110 << EC.message() << '\n'; 111 return false; 112 } 113 114 // FIXME: Use CoreFoundation to get executable bundle info. Use 115 // dummy values for now. 116 std::string bundleVersionStr = "1", bundleShortVersionStr = "1.0", 117 bundleIDStr; 118 119 llvm::StringRef BundleID = *llvm::sys::path::rbegin(BundleRoot); 120 if (llvm::sys::path::extension(BundleRoot) == ".dSYM") 121 bundleIDStr = llvm::sys::path::stem(BundleID); 122 else 123 bundleIDStr = BundleID; 124 125 // Print out information to the plist file. 126 PL << "<?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n" 127 << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" " 128 << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" 129 << "<plist version=\"1.0\">\n" 130 << "\t<dict>\n" 131 << "\t\t<key>CFBundleDevelopmentRegion</key>\n" 132 << "\t\t<string>English</string>\n" 133 << "\t\t<key>CFBundleIdentifier</key>\n" 134 << "\t\t<string>com.apple.xcode.dsym." << bundleIDStr << "</string>\n" 135 << "\t\t<key>CFBundleInfoDictionaryVersion</key>\n" 136 << "\t\t<string>6.0</string>\n" 137 << "\t\t<key>CFBundlePackageType</key>\n" 138 << "\t\t<string>dSYM</string>\n" 139 << "\t\t<key>CFBundleSignature</key>\n" 140 << "\t\t<string>\?\?\?\?</string>\n" 141 << "\t\t<key>CFBundleShortVersionString</key>\n" 142 << "\t\t<string>" << bundleShortVersionStr << "</string>\n" 143 << "\t\t<key>CFBundleVersion</key>\n" 144 << "\t\t<string>" << bundleVersionStr << "</string>\n" 145 << "\t</dict>\n" 146 << "</plist>\n"; 147 148 PL.close(); 149 return true; 150 } 151 152 static bool createBundleDir(llvm::StringRef BundleBase) { 153 if (NoOutput) 154 return true; 155 156 llvm::SmallString<128> Bundle(BundleBase); 157 llvm::sys::path::append(Bundle, "Contents", "Resources", "DWARF"); 158 if (std::error_code EC = create_directories(Bundle.str(), true, 159 llvm::sys::fs::perms::all_all)) { 160 llvm::errs() << "error: cannot create directory " << Bundle << ": " 161 << EC.message() << "\n"; 162 return false; 163 } 164 return true; 165 } 166 167 static std::error_code getUniqueFile(const llvm::Twine &Model, int &ResultFD, 168 llvm::SmallVectorImpl<char> &ResultPath) { 169 // If in NoOutput mode, use the createUniqueFile variant that 170 // doesn't open the file but still generates a somewhat unique 171 // name. In the real usage scenario, we'll want to ensure that the 172 // file is trully unique, and creating it is the only way to achieve 173 // that. 174 if (NoOutput) 175 return llvm::sys::fs::createUniqueFile(Model, ResultPath); 176 return llvm::sys::fs::createUniqueFile(Model, ResultFD, ResultPath); 177 } 178 179 static std::string getOutputFileName(llvm::StringRef InputFile, 180 bool TempFile = false) { 181 if (TempFile) { 182 llvm::SmallString<128> TmpFile; 183 llvm::sys::path::system_temp_directory(true, TmpFile); 184 llvm::StringRef Basename = 185 OutputFileOpt.empty() ? InputFile : llvm::StringRef(OutputFileOpt); 186 llvm::sys::path::append(TmpFile, llvm::sys::path::filename(Basename)); 187 188 int FD; 189 llvm::SmallString<128> UniqueFile; 190 if (auto EC = getUniqueFile(TmpFile + ".tmp%%%%%.dwarf", FD, UniqueFile)) { 191 llvm::errs() << "error: failed to create temporary outfile '" 192 << TmpFile << "': " << EC.message() << '\n'; 193 return ""; 194 } 195 llvm::sys::RemoveFileOnSignal(UniqueFile); 196 if (!NoOutput) { 197 // Close the file immediately. We know it is unique. It will be 198 // reopened and written to later. 199 llvm::raw_fd_ostream CloseImmediately(FD, true /* shouldClose */, true); 200 } 201 return UniqueFile.str(); 202 } 203 204 if (FlatOut) { 205 // If a flat dSYM has been requested, things are pretty simple. 206 if (OutputFileOpt.empty()) { 207 if (InputFile == "-") 208 return "a.out.dwarf"; 209 return (InputFile + ".dwarf").str(); 210 } 211 212 return OutputFileOpt; 213 } 214 215 // We need to create/update a dSYM bundle. 216 // A bundle hierarchy looks like this: 217 // <bundle name>.dSYM/ 218 // Contents/ 219 // Info.plist 220 // Resources/ 221 // DWARF/ 222 // <DWARF file(s)> 223 std::string DwarfFile = 224 InputFile == "-" ? llvm::StringRef("a.out") : InputFile; 225 llvm::SmallString<128> BundleDir(OutputFileOpt); 226 if (BundleDir.empty()) 227 BundleDir = DwarfFile + ".dSYM"; 228 if (!createBundleDir(BundleDir) || !createPlistFile(BundleDir)) 229 return ""; 230 231 llvm::sys::path::append(BundleDir, "Contents", "Resources", "DWARF", 232 llvm::sys::path::filename(DwarfFile)); 233 return BundleDir.str(); 234 } 235 236 void llvm::dsymutil::exitDsymutil(int ExitStatus) { 237 // Cleanup temporary files. 238 llvm::sys::RunInterruptHandlers(); 239 exit(ExitStatus); 240 } 241 242 int main(int argc, char **argv) { 243 llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); 244 llvm::PrettyStackTraceProgram StackPrinter(argc, argv); 245 llvm::llvm_shutdown_obj Shutdown; 246 LinkOptions Options; 247 void *MainAddr = (void *)(intptr_t)&exitDsymutil; 248 std::string SDKPath = llvm::sys::fs::getMainExecutable(argv[0], MainAddr); 249 SDKPath = llvm::sys::path::parent_path(SDKPath); 250 251 HideUnrelatedOptions(DsymCategory); 252 llvm::cl::ParseCommandLineOptions( 253 argc, argv, 254 "manipulate archived DWARF debug symbol files.\n\n" 255 "dsymutil links the DWARF debug information found in the object files\n" 256 "for the executable <input file> by using debug symbols information\n" 257 "contained in its symbol table.\n"); 258 259 if (Help) { 260 PrintHelpMessage(); 261 return 0; 262 } 263 264 if (Version) { 265 llvm::cl::PrintVersionMessage(); 266 return 0; 267 } 268 269 Options.Verbose = Verbose; 270 Options.NoOutput = NoOutput; 271 Options.NoODR = NoODR; 272 Options.NoTimestamp = NoTimestamp; 273 Options.PrependPath = OsoPrependPath; 274 275 llvm::InitializeAllTargetInfos(); 276 llvm::InitializeAllTargetMCs(); 277 llvm::InitializeAllTargets(); 278 llvm::InitializeAllAsmPrinters(); 279 280 if (!FlatOut && OutputFileOpt == "-") { 281 llvm::errs() << "error: cannot emit to standard output without --flat\n"; 282 return 1; 283 } 284 285 if (InputFiles.size() > 1 && FlatOut && !OutputFileOpt.empty()) { 286 llvm::errs() << "error: cannot use -o with multiple inputs in flat mode\n"; 287 return 1; 288 } 289 290 for (const auto &Arch : ArchFlags) 291 if (Arch != "*" && Arch != "all" && 292 !llvm::object::MachOObjectFile::isValidArch(Arch)) { 293 llvm::errs() << "error: Unsupported cpu architecture: '" << Arch << "'\n"; 294 exitDsymutil(1); 295 } 296 297 for (auto &InputFile : InputFiles) { 298 // Dump the symbol table for each input file and requested arch 299 if (DumpStab) { 300 if (!dumpStab(InputFile, ArchFlags, OsoPrependPath)) 301 exitDsymutil(1); 302 continue; 303 } 304 305 auto DebugMapPtrsOrErr = parseDebugMap(InputFile, ArchFlags, OsoPrependPath, 306 Verbose, InputIsYAMLDebugMap); 307 308 if (auto EC = DebugMapPtrsOrErr.getError()) { 309 llvm::errs() << "error: cannot parse the debug map for \"" << InputFile 310 << "\": " << EC.message() << '\n'; 311 exitDsymutil(1); 312 } 313 314 if (DebugMapPtrsOrErr->empty()) { 315 llvm::errs() << "error: no architecture to link\n"; 316 exitDsymutil(1); 317 } 318 319 // If there is more than one link to execute, we need to generate 320 // temporary files. 321 bool NeedsTempFiles = !DumpDebugMap && (*DebugMapPtrsOrErr).size() != 1; 322 llvm::SmallVector<MachOUtils::ArchAndFilename, 4> TempFiles; 323 for (auto &Map : *DebugMapPtrsOrErr) { 324 if (Verbose || DumpDebugMap) 325 Map->print(llvm::outs()); 326 327 if (DumpDebugMap) 328 continue; 329 330 if (Map->begin() == Map->end()) 331 llvm::errs() << "warning: no debug symbols in executable (-arch " 332 << MachOUtils::getArchName(Map->getTriple().getArchName()) 333 << ")\n"; 334 335 std::string OutputFile = getOutputFileName(InputFile, NeedsTempFiles); 336 if (OutputFile.empty() || !linkDwarf(OutputFile, *Map, Options)) 337 exitDsymutil(1); 338 339 if (NeedsTempFiles) 340 TempFiles.emplace_back(Map->getTriple().getArchName().str(), 341 OutputFile); 342 } 343 344 if (NeedsTempFiles && 345 !MachOUtils::generateUniversalBinary( 346 TempFiles, getOutputFileName(InputFile), Options, SDKPath)) 347 exitDsymutil(1); 348 } 349 350 exitDsymutil(0); 351 } 352