1 //===-- llvm-symbolizer.cpp - Simple addr2line-like symbolizer ------------===// 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 utility works much like "addr2line". It is able of transforming 11 // tuples (module name, module offset) to code locations (function name, 12 // file, line number, column number). It is targeted for compiler-rt tools 13 // (especially AddressSanitizer and ThreadSanitizer) that can use it 14 // to symbolize stack traces in their error reports. 15 // 16 //===----------------------------------------------------------------------===// 17 18 #include "llvm/ADT/OwningPtr.h" 19 #include "llvm/ADT/StringRef.h" 20 #include "llvm/DebugInfo/DIContext.h" 21 #include "llvm/Object/ObjectFile.h" 22 #include "llvm/Support/CommandLine.h" 23 #include "llvm/Support/Debug.h" 24 #include "llvm/Support/ManagedStatic.h" 25 #include "llvm/Support/MemoryBuffer.h" 26 #include "llvm/Support/PrettyStackTrace.h" 27 #include "llvm/Support/Signals.h" 28 #include "llvm/Support/raw_ostream.h" 29 30 #include <cstdio> 31 #include <cstring> 32 #include <map> 33 #include <string> 34 35 using namespace llvm; 36 using namespace object; 37 using std::string; 38 39 static cl::opt<bool> 40 UseSymbolTable("use-symbol-table", cl::init(true), 41 cl::desc("Prefer names in symbol table to names " 42 "in debug info")); 43 44 static cl::opt<bool> 45 PrintFunctions("functions", cl::init(true), 46 cl::desc("Print function names as well as line " 47 "information for a given address")); 48 49 static cl::opt<bool> 50 PrintInlining("inlining", cl::init(true), 51 cl::desc("Print all inlined frames for a given address")); 52 53 static cl::opt<bool> 54 Demangle("demangle", cl::init(true), 55 cl::desc("Demangle function names")); 56 57 static StringRef ToolInvocationPath; 58 59 static bool error(error_code ec) { 60 if (!ec) return false; 61 errs() << ToolInvocationPath << ": error reading file: " 62 << ec.message() << ".\n"; 63 return true; 64 } 65 66 static uint32_t getDILineInfoSpecifierFlags() { 67 uint32_t Flags = llvm::DILineInfoSpecifier::FileLineInfo | 68 llvm::DILineInfoSpecifier::AbsoluteFilePath; 69 if (PrintFunctions) 70 Flags |= llvm::DILineInfoSpecifier::FunctionName; 71 return Flags; 72 } 73 74 static void patchFunctionNameInDILineInfo(const string &NewFunctionName, 75 DILineInfo &LineInfo) { 76 string FileName = LineInfo.getFileName(); 77 LineInfo = DILineInfo(StringRef(FileName), StringRef(NewFunctionName), 78 LineInfo.getLine(), LineInfo.getColumn()); 79 } 80 81 namespace { 82 class ModuleInfo { 83 OwningPtr<ObjectFile> Module; 84 OwningPtr<DIContext> DebugInfoContext; 85 public: 86 ModuleInfo(ObjectFile *Obj, DIContext *DICtx) 87 : Module(Obj), DebugInfoContext(DICtx) {} 88 DILineInfo symbolizeCode(uint64_t ModuleOffset) const { 89 DILineInfo LineInfo; 90 if (DebugInfoContext) { 91 LineInfo = DebugInfoContext->getLineInfoForAddress( 92 ModuleOffset, getDILineInfoSpecifierFlags()); 93 } 94 // Override function name from symbol table if necessary. 95 if (PrintFunctions && UseSymbolTable) { 96 string Function; 97 if (getFunctionNameFromSymbolTable(ModuleOffset, Function)) { 98 patchFunctionNameInDILineInfo(Function, LineInfo); 99 } 100 } 101 return LineInfo; 102 } 103 DIInliningInfo symbolizeInlinedCode(uint64_t ModuleOffset) const { 104 DIInliningInfo InlinedContext; 105 if (DebugInfoContext) { 106 InlinedContext = DebugInfoContext->getInliningInfoForAddress( 107 ModuleOffset, getDILineInfoSpecifierFlags()); 108 } 109 // Make sure there is at least one frame in context. 110 if (InlinedContext.getNumberOfFrames() == 0) { 111 InlinedContext.addFrame(DILineInfo()); 112 } 113 // Override the function name in lower frame with name from symbol table. 114 if (PrintFunctions && UseSymbolTable) { 115 DIInliningInfo PatchedInlinedContext; 116 for (uint32_t i = 0, n = InlinedContext.getNumberOfFrames(); 117 i != n; i++) { 118 DILineInfo LineInfo = InlinedContext.getFrame(i); 119 if (i == n - 1) { 120 string Function; 121 if (getFunctionNameFromSymbolTable(ModuleOffset, Function)) { 122 patchFunctionNameInDILineInfo(Function, LineInfo); 123 } 124 } 125 PatchedInlinedContext.addFrame(LineInfo); 126 } 127 InlinedContext = PatchedInlinedContext; 128 } 129 return InlinedContext; 130 } 131 private: 132 bool getFunctionNameFromSymbolTable(size_t Address, 133 string &FunctionName) const { 134 assert(Module); 135 error_code ec; 136 for (symbol_iterator si = Module->begin_symbols(), 137 se = Module->end_symbols(); 138 si != se; si.increment(ec)) { 139 if (error(ec)) return false; 140 uint64_t SymbolAddress; 141 uint64_t SymbolSize; 142 if (error(si->getAddress(SymbolAddress))) continue; 143 if (error(si->getSize(SymbolSize))) continue; 144 // FIXME: If a function has alias, there are two entries in symbol table 145 // with same address size. Make sure we choose the correct one. 146 if (SymbolAddress <= Address && Address < SymbolAddress + SymbolSize) { 147 StringRef Name; 148 if (error(si->getName(Name))) continue; 149 FunctionName = Name.str(); 150 return true; 151 } 152 } 153 return false; 154 } 155 }; 156 157 typedef std::map<string, ModuleInfo*> ModuleMapTy; 158 typedef ModuleMapTy::iterator ModuleMapIter; 159 typedef ModuleMapTy::const_iterator ModuleMapConstIter; 160 } // namespace 161 162 static ModuleMapTy Modules; 163 164 static bool isFullNameOfDwarfSection(const StringRef &FullName, 165 const StringRef &ShortName) { 166 static const char kDwarfPrefix[] = "__DWARF,"; 167 StringRef Name = FullName; 168 // Skip "__DWARF," prefix. 169 if (Name.startswith(kDwarfPrefix)) 170 Name = Name.substr(strlen(kDwarfPrefix)); 171 // Skip . and _ prefixes. 172 Name = Name.substr(Name.find_first_not_of("._")); 173 return (Name == ShortName); 174 } 175 176 // Returns true if the object endianness is known. 177 static bool getObjectEndianness(const ObjectFile *Obj, 178 bool &IsLittleEndian) { 179 // FIXME: Implement this when libLLVMObject allows to do it easily. 180 IsLittleEndian = true; 181 return true; 182 } 183 184 static ModuleInfo *getOrCreateModuleInfo(const string &ModuleName) { 185 ModuleMapIter I = Modules.find(ModuleName); 186 if (I != Modules.end()) 187 return I->second; 188 189 OwningPtr<MemoryBuffer> Buff; 190 MemoryBuffer::getFile(ModuleName, Buff); 191 ObjectFile *Obj = ObjectFile::createObjectFile(Buff.take()); 192 if (Obj == 0) { 193 // Module name doesn't point to a valid object file. 194 Modules.insert(make_pair(ModuleName, (ModuleInfo*)0)); 195 return 0; 196 } 197 198 DIContext *Context = 0; 199 bool IsLittleEndian; 200 if (getObjectEndianness(Obj, IsLittleEndian)) { 201 StringRef DebugInfoSection; 202 StringRef DebugAbbrevSection; 203 StringRef DebugLineSection; 204 StringRef DebugArangesSection; 205 StringRef DebugStringSection; 206 StringRef DebugRangesSection; 207 error_code ec; 208 for (section_iterator i = Obj->begin_sections(), 209 e = Obj->end_sections(); 210 i != e; i.increment(ec)) { 211 if (error(ec)) break; 212 StringRef Name; 213 if (error(i->getName(Name))) continue; 214 StringRef Data; 215 if (error(i->getContents(Data))) continue; 216 if (isFullNameOfDwarfSection(Name, "debug_info")) 217 DebugInfoSection = Data; 218 else if (isFullNameOfDwarfSection(Name, "debug_abbrev")) 219 DebugAbbrevSection = Data; 220 else if (isFullNameOfDwarfSection(Name, "debug_line")) 221 DebugLineSection = Data; 222 // Don't use debug_aranges for now, as address ranges contained 223 // there may not cover all instructions in the module 224 // else if (isFullNameOfDwarfSection(Name, "debug_aranges")) 225 // DebugArangesSection = Data; 226 else if (isFullNameOfDwarfSection(Name, "debug_str")) 227 DebugStringSection = Data; 228 else if (isFullNameOfDwarfSection(Name, "debug_ranges")) 229 DebugRangesSection = Data; 230 } 231 232 Context = DIContext::getDWARFContext( 233 IsLittleEndian, DebugInfoSection, DebugAbbrevSection, 234 DebugArangesSection, DebugLineSection, DebugStringSection, 235 DebugRangesSection); 236 assert(Context); 237 } 238 239 ModuleInfo *Info = new ModuleInfo(Obj, Context); 240 Modules.insert(make_pair(ModuleName, Info)); 241 return Info; 242 } 243 244 // Assume that __cxa_demangle is provided by libcxxabi. 245 extern "C" char *__cxa_demangle(const char *mangled_name, char *output_buffer, 246 size_t *length, int *status); 247 248 static void printDILineInfo(DILineInfo LineInfo) { 249 // By default, DILineInfo contains "<invalid>" for function/filename it 250 // cannot fetch. We replace it to "??" to make our output closer to addr2line. 251 static const string kDILineInfoBadString = "<invalid>"; 252 static const string kSymbolizerBadString = "??"; 253 if (PrintFunctions) { 254 string FunctionName = LineInfo.getFunctionName(); 255 if (FunctionName == kDILineInfoBadString) 256 FunctionName = kSymbolizerBadString; 257 if (Demangle) { 258 int status = 0; 259 char *DemangledName = __cxa_demangle( 260 FunctionName.c_str(), 0, 0, &status); 261 if (status == 0) { 262 FunctionName = DemangledName; 263 free(DemangledName); 264 } 265 } 266 outs() << FunctionName << "\n"; 267 } 268 string Filename = LineInfo.getFileName(); 269 if (Filename == kDILineInfoBadString) 270 Filename = kSymbolizerBadString; 271 outs() << Filename << 272 ":" << LineInfo.getLine() << 273 ":" << LineInfo.getColumn() << 274 "\n"; 275 } 276 277 static void symbolize(string ModuleName, string ModuleOffsetStr) { 278 ModuleInfo *Info = getOrCreateModuleInfo(ModuleName); 279 uint64_t Offset = 0; 280 if (Info == 0 || 281 StringRef(ModuleOffsetStr).getAsInteger(0, Offset)) { 282 printDILineInfo(DILineInfo()); 283 } else if (PrintInlining) { 284 DIInliningInfo InlinedContext = Info->symbolizeInlinedCode(Offset); 285 uint32_t FramesNum = InlinedContext.getNumberOfFrames(); 286 assert(FramesNum > 0); 287 for (uint32_t i = 0; i < FramesNum; i++) { 288 DILineInfo LineInfo = InlinedContext.getFrame(i); 289 printDILineInfo(LineInfo); 290 } 291 } else { 292 DILineInfo LineInfo = Info->symbolizeCode(Offset); 293 printDILineInfo(LineInfo); 294 } 295 296 outs() << "\n"; // Print extra empty line to mark the end of output. 297 outs().flush(); 298 } 299 300 static bool parseModuleNameAndOffset(string &ModuleName, 301 string &ModuleOffsetStr) { 302 static const int kMaxInputStringLength = 1024; 303 static const char kDelimiters[] = " \n"; 304 char InputString[kMaxInputStringLength]; 305 if (!fgets(InputString, sizeof(InputString), stdin)) 306 return false; 307 ModuleName = ""; 308 ModuleOffsetStr = ""; 309 // FIXME: Handle case when filename is given in quotes. 310 if (char *FilePath = strtok(InputString, kDelimiters)) { 311 ModuleName = FilePath; 312 if (char *OffsetStr = strtok((char*)0, kDelimiters)) 313 ModuleOffsetStr = OffsetStr; 314 } 315 return true; 316 } 317 318 int main(int argc, char **argv) { 319 // Print stack trace if we signal out. 320 sys::PrintStackTraceOnErrorSignal(); 321 PrettyStackTraceProgram X(argc, argv); 322 llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. 323 324 cl::ParseCommandLineOptions(argc, argv, "llvm symbolizer for compiler-rt\n"); 325 ToolInvocationPath = argv[0]; 326 327 string ModuleName; 328 string ModuleOffsetStr; 329 while (parseModuleNameAndOffset(ModuleName, ModuleOffsetStr)) { 330 symbolize(ModuleName, ModuleOffsetStr); 331 } 332 return 0; 333 } 334