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