1 //===-- llvm-symbolizer.cpp - Simple addr2line-like symbolizer ------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This utility works much like "addr2line". It is able of transforming 10 // tuples (module name, module offset) to code locations (function name, 11 // file, line number, column number). It is targeted for compiler-rt tools 12 // (especially AddressSanitizer and ThreadSanitizer) that can use it 13 // to symbolize stack traces in their error reports. 14 // 15 //===----------------------------------------------------------------------===// 16 17 #include "llvm/ADT/StringRef.h" 18 #include "llvm/DebugInfo/Symbolize/DIPrinter.h" 19 #include "llvm/DebugInfo/Symbolize/Symbolize.h" 20 #include "llvm/Support/COM.h" 21 #include "llvm/Support/CommandLine.h" 22 #include "llvm/Support/Debug.h" 23 #include "llvm/Support/FileSystem.h" 24 #include "llvm/Support/InitLLVM.h" 25 #include "llvm/Support/Path.h" 26 #include "llvm/Support/raw_ostream.h" 27 #include <algorithm> 28 #include <cstdio> 29 #include <cstring> 30 #include <string> 31 32 using namespace llvm; 33 using namespace symbolize; 34 35 static cl::opt<bool> 36 ClUseSymbolTable("use-symbol-table", cl::init(true), 37 cl::desc("Prefer names in symbol table to names " 38 "in debug info")); 39 40 static cl::opt<FunctionNameKind> ClPrintFunctions( 41 "functions", cl::init(FunctionNameKind::LinkageName), 42 cl::desc("Print function name for a given address"), cl::ValueOptional, 43 cl::values(clEnumValN(FunctionNameKind::None, "none", "omit function name"), 44 clEnumValN(FunctionNameKind::ShortName, "short", 45 "print short function name"), 46 clEnumValN(FunctionNameKind::LinkageName, "linkage", 47 "print function linkage name"), 48 // Sentinel value for unspecified value. 49 clEnumValN(FunctionNameKind::LinkageName, "", ""))); 50 static cl::alias ClPrintFunctionsShort("f", cl::desc("Alias for -functions"), 51 cl::NotHidden, cl::Grouping, 52 cl::aliasopt(ClPrintFunctions)); 53 54 static cl::opt<bool> 55 ClUseRelativeAddress("relative-address", cl::init(false), 56 cl::desc("Interpret addresses as relative addresses"), 57 cl::ReallyHidden); 58 59 static cl::opt<bool> ClUntagAddresses( 60 "untag-addresses", cl::init(true), 61 cl::desc("Remove memory tags from addresses before symbolization")); 62 63 static cl::opt<bool> 64 ClPrintInlining("inlining", cl::init(true), 65 cl::desc("Print all inlined frames for a given address")); 66 static cl::alias 67 ClPrintInliningAliasI("i", cl::desc("Alias for -inlining"), 68 cl::NotHidden, cl::aliasopt(ClPrintInlining), 69 cl::Grouping); 70 static cl::alias 71 ClPrintInliningAliasInlines("inlines", cl::desc("Alias for -inlining"), 72 cl::NotHidden, cl::aliasopt(ClPrintInlining)); 73 74 static cl::opt<bool> ClBasenames("basenames", cl::init(false), 75 cl::desc("Strip directory names from paths")); 76 static cl::alias ClBasenamesShort("s", cl::desc("Alias for -basenames"), 77 cl::NotHidden, cl::aliasopt(ClBasenames)); 78 79 static cl::opt<bool> 80 ClRelativenames("relativenames", cl::init(false), 81 cl::desc("Strip the compilation directory from paths")); 82 83 static cl::opt<bool> 84 ClDemangle("demangle", cl::init(true), cl::desc("Demangle function names")); 85 static cl::alias 86 ClDemangleShort("C", cl::desc("Alias for -demangle"), 87 cl::NotHidden, cl::aliasopt(ClDemangle), cl::Grouping); 88 static cl::opt<bool> 89 ClNoDemangle("no-demangle", cl::init(false), 90 cl::desc("Don't demangle function names")); 91 92 static cl::opt<std::string> ClDefaultArch("default-arch", cl::init(""), 93 cl::desc("Default architecture " 94 "(for multi-arch objects)")); 95 96 static cl::opt<std::string> 97 ClBinaryName("obj", cl::init(""), 98 cl::desc("Path to object file to be symbolized (if not provided, " 99 "object file should be specified for each input line)")); 100 static cl::alias 101 ClBinaryNameAliasExe("exe", cl::desc("Alias for -obj"), 102 cl::NotHidden, cl::aliasopt(ClBinaryName)); 103 static cl::alias ClBinaryNameAliasE("e", cl::desc("Alias for -obj"), 104 cl::NotHidden, cl::Grouping, cl::Prefix, 105 cl::aliasopt(ClBinaryName)); 106 107 static cl::opt<std::string> 108 ClDwpName("dwp", cl::init(""), 109 cl::desc("Path to DWP file to be use for any split CUs")); 110 111 static cl::list<std::string> 112 ClDsymHint("dsym-hint", cl::ZeroOrMore, 113 cl::desc("Path to .dSYM bundles to search for debug info for the " 114 "object files")); 115 116 static cl::opt<bool> 117 ClPrintAddress("print-address", cl::init(false), 118 cl::desc("Show address before line information")); 119 static cl::alias 120 ClPrintAddressAliasAddresses("addresses", cl::desc("Alias for -print-address"), 121 cl::NotHidden, cl::aliasopt(ClPrintAddress)); 122 static cl::alias 123 ClPrintAddressAliasA("a", cl::desc("Alias for -print-address"), 124 cl::NotHidden, cl::aliasopt(ClPrintAddress), cl::Grouping); 125 126 static cl::opt<bool> 127 ClPrettyPrint("pretty-print", cl::init(false), 128 cl::desc("Make the output more human friendly")); 129 static cl::alias ClPrettyPrintShort("p", cl::desc("Alias for -pretty-print"), 130 cl::NotHidden, 131 cl::aliasopt(ClPrettyPrint), cl::Grouping); 132 133 static cl::opt<int> ClPrintSourceContextLines( 134 "print-source-context-lines", cl::init(0), 135 cl::desc("Print N number of source file context")); 136 137 static cl::opt<bool> ClVerbose("verbose", cl::init(false), 138 cl::desc("Print verbose line info")); 139 140 static cl::opt<uint64_t> 141 ClAdjustVMA("adjust-vma", cl::init(0), cl::value_desc("offset"), 142 cl::desc("Add specified offset to object file addresses")); 143 144 static cl::list<std::string> ClInputAddresses(cl::Positional, 145 cl::desc("<input addresses>..."), 146 cl::ZeroOrMore); 147 148 static cl::opt<std::string> 149 ClFallbackDebugPath("fallback-debug-path", cl::init(""), 150 cl::desc("Fallback path for debug binaries.")); 151 152 static cl::list<std::string> 153 ClDebugFileDirectory("debug-file-directory", cl::ZeroOrMore, 154 cl::value_desc("dir"), 155 cl::desc("Path to directory where to look for debug " 156 "files.")); 157 158 static cl::opt<DIPrinter::OutputStyle> 159 ClOutputStyle("output-style", cl::init(DIPrinter::OutputStyle::LLVM), 160 cl::desc("Specify print style"), 161 cl::values(clEnumValN(DIPrinter::OutputStyle::LLVM, "LLVM", 162 "LLVM default style"), 163 clEnumValN(DIPrinter::OutputStyle::GNU, "GNU", 164 "GNU addr2line style"))); 165 166 static cl::opt<bool> 167 ClUseNativePDBReader("use-native-pdb-reader", cl::init(0), 168 cl::desc("Use native PDB functionality")); 169 170 static cl::extrahelp 171 HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); 172 173 template<typename T> 174 static bool error(Expected<T> &ResOrErr) { 175 if (ResOrErr) 176 return false; 177 logAllUnhandledErrors(ResOrErr.takeError(), errs(), 178 "LLVMSymbolizer: error reading file: "); 179 return true; 180 } 181 182 enum class Command { 183 Code, 184 Data, 185 Frame, 186 }; 187 188 static bool parseCommand(bool IsAddr2Line, StringRef InputString, Command &Cmd, 189 std::string &ModuleName, uint64_t &ModuleOffset) { 190 const char kDelimiters[] = " \n\r"; 191 ModuleName = ""; 192 if (InputString.consume_front("CODE ")) { 193 Cmd = Command::Code; 194 } else if (InputString.consume_front("DATA ")) { 195 Cmd = Command::Data; 196 } else if (InputString.consume_front("FRAME ")) { 197 Cmd = Command::Frame; 198 } else { 199 // If no cmd, assume it's CODE. 200 Cmd = Command::Code; 201 } 202 const char *Pos = InputString.data(); 203 // Skip delimiters and parse input filename (if needed). 204 if (ClBinaryName.empty()) { 205 Pos += strspn(Pos, kDelimiters); 206 if (*Pos == '"' || *Pos == '\'') { 207 char Quote = *Pos; 208 Pos++; 209 const char *End = strchr(Pos, Quote); 210 if (!End) 211 return false; 212 ModuleName = std::string(Pos, End - Pos); 213 Pos = End + 1; 214 } else { 215 int NameLength = strcspn(Pos, kDelimiters); 216 ModuleName = std::string(Pos, NameLength); 217 Pos += NameLength; 218 } 219 } else { 220 ModuleName = ClBinaryName; 221 } 222 // Skip delimiters and parse module offset. 223 Pos += strspn(Pos, kDelimiters); 224 int OffsetLength = strcspn(Pos, kDelimiters); 225 StringRef Offset(Pos, OffsetLength); 226 // GNU addr2line assumes the offset is hexadecimal and allows a redundant 227 // "0x" or "0X" prefix; do the same for compatibility. 228 if (IsAddr2Line) 229 Offset.consume_front("0x") || Offset.consume_front("0X"); 230 return !Offset.getAsInteger(IsAddr2Line ? 16 : 0, ModuleOffset); 231 } 232 233 static void symbolizeInput(bool IsAddr2Line, StringRef InputString, 234 LLVMSymbolizer &Symbolizer, DIPrinter &Printer) { 235 Command Cmd; 236 std::string ModuleName; 237 uint64_t Offset = 0; 238 if (!parseCommand(IsAddr2Line, StringRef(InputString), Cmd, ModuleName, 239 Offset)) { 240 outs() << InputString << "\n"; 241 return; 242 } 243 244 if (ClPrintAddress) { 245 outs() << "0x"; 246 outs().write_hex(Offset); 247 StringRef Delimiter = ClPrettyPrint ? ": " : "\n"; 248 outs() << Delimiter; 249 } 250 Offset -= ClAdjustVMA; 251 if (Cmd == Command::Data) { 252 auto ResOrErr = Symbolizer.symbolizeData( 253 ModuleName, {Offset, object::SectionedAddress::UndefSection}); 254 Printer << (error(ResOrErr) ? DIGlobal() : ResOrErr.get()); 255 } else if (Cmd == Command::Frame) { 256 auto ResOrErr = Symbolizer.symbolizeFrame( 257 ModuleName, {Offset, object::SectionedAddress::UndefSection}); 258 if (!error(ResOrErr)) { 259 for (DILocal Local : *ResOrErr) 260 Printer << Local; 261 if (ResOrErr->empty()) 262 outs() << "??\n"; 263 } 264 } else if (ClPrintInlining) { 265 auto ResOrErr = Symbolizer.symbolizeInlinedCode( 266 ModuleName, {Offset, object::SectionedAddress::UndefSection}); 267 Printer << (error(ResOrErr) ? DIInliningInfo() : ResOrErr.get()); 268 } else if (ClOutputStyle == DIPrinter::OutputStyle::GNU) { 269 // With ClPrintFunctions == FunctionNameKind::LinkageName (default) 270 // and ClUseSymbolTable == true (also default), Symbolizer.symbolizeCode() 271 // may override the name of an inlined function with the name of the topmost 272 // caller function in the inlining chain. This contradicts the existing 273 // behavior of addr2line. Symbolizer.symbolizeInlinedCode() overrides only 274 // the topmost function, which suits our needs better. 275 auto ResOrErr = Symbolizer.symbolizeInlinedCode( 276 ModuleName, {Offset, object::SectionedAddress::UndefSection}); 277 Printer << (error(ResOrErr) ? DILineInfo() : ResOrErr.get().getFrame(0)); 278 } else { 279 auto ResOrErr = Symbolizer.symbolizeCode( 280 ModuleName, {Offset, object::SectionedAddress::UndefSection}); 281 Printer << (error(ResOrErr) ? DILineInfo() : ResOrErr.get()); 282 } 283 if (ClOutputStyle == DIPrinter::OutputStyle::LLVM) 284 outs() << "\n"; 285 } 286 287 int main(int argc, char **argv) { 288 InitLLVM X(argc, argv); 289 290 bool IsAddr2Line = sys::path::stem(argv[0]).contains("addr2line"); 291 292 if (IsAddr2Line) { 293 ClDemangle.setInitialValue(false); 294 ClPrintFunctions.setInitialValue(FunctionNameKind::None); 295 ClPrintInlining.setInitialValue(false); 296 ClUntagAddresses.setInitialValue(false); 297 ClOutputStyle.setInitialValue(DIPrinter::OutputStyle::GNU); 298 } 299 300 llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded); 301 cl::ParseCommandLineOptions( 302 argc, argv, IsAddr2Line ? "llvm-addr2line\n" : "llvm-symbolizer\n", 303 /*Errs=*/nullptr, 304 IsAddr2Line ? "LLVM_ADDR2LINE_OPTS" : "LLVM_SYMBOLIZER_OPTS"); 305 306 // If both --demangle and --no-demangle are specified then pick the last one. 307 if (ClNoDemangle.getPosition() > ClDemangle.getPosition()) 308 ClDemangle = !ClNoDemangle; 309 310 LLVMSymbolizer::Options Opts; 311 Opts.PrintFunctions = ClPrintFunctions; 312 Opts.UseSymbolTable = ClUseSymbolTable; 313 Opts.Demangle = ClDemangle; 314 Opts.RelativeAddresses = ClUseRelativeAddress; 315 Opts.UntagAddresses = ClUntagAddresses; 316 Opts.DefaultArch = ClDefaultArch; 317 Opts.FallbackDebugPath = ClFallbackDebugPath; 318 Opts.DWPName = ClDwpName; 319 Opts.DebugFileDirectory = ClDebugFileDirectory; 320 Opts.UseNativePDBReader = ClUseNativePDBReader; 321 Opts.PathStyle = DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath; 322 // If both --basenames and --relativenames are specified then pick the last 323 // one. 324 if (ClBasenames.getPosition() > ClRelativenames.getPosition()) 325 Opts.PathStyle = DILineInfoSpecifier::FileLineInfoKind::BaseNameOnly; 326 else if (ClRelativenames) 327 Opts.PathStyle = DILineInfoSpecifier::FileLineInfoKind::RelativeFilePath; 328 329 for (const auto &hint : ClDsymHint) { 330 if (sys::path::extension(hint) == ".dSYM") { 331 Opts.DsymHints.push_back(hint); 332 } else { 333 errs() << "Warning: invalid dSYM hint: \"" << hint << 334 "\" (must have the '.dSYM' extension).\n"; 335 } 336 } 337 LLVMSymbolizer Symbolizer(Opts); 338 339 DIPrinter Printer(outs(), ClPrintFunctions != FunctionNameKind::None, 340 ClPrettyPrint, ClPrintSourceContextLines, ClVerbose, 341 ClOutputStyle); 342 343 if (ClInputAddresses.empty()) { 344 const int kMaxInputStringLength = 1024; 345 char InputString[kMaxInputStringLength]; 346 347 while (fgets(InputString, sizeof(InputString), stdin)) { 348 // Strip newline characters. 349 std::string StrippedInputString(InputString); 350 StrippedInputString.erase( 351 std::remove_if(StrippedInputString.begin(), StrippedInputString.end(), 352 [](char c) { return c == '\r' || c == '\n'; }), 353 StrippedInputString.end()); 354 symbolizeInput(IsAddr2Line, StrippedInputString, Symbolizer, Printer); 355 outs().flush(); 356 } 357 } else { 358 for (StringRef Address : ClInputAddresses) 359 symbolizeInput(IsAddr2Line, Address, Symbolizer, Printer); 360 } 361 362 return 0; 363 } 364