blob: c404eea892dadffaf4a5e6489a757bd8be78c819 [file] [log] [blame]
Alexander Potapenkof41954b2012-11-12 11:33:29 +00001//===-- 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/MachO.h"
22#include "llvm/Object/ObjectFile.h"
23#include "llvm/Support/Casting.h"
24#include "llvm/Support/CommandLine.h"
25#include "llvm/Support/Debug.h"
26#include "llvm/Support/ManagedStatic.h"
27#include "llvm/Support/MemoryBuffer.h"
28#include "llvm/Support/Path.h"
29#include "llvm/Support/PrettyStackTrace.h"
30#include "llvm/Support/Signals.h"
31#include "llvm/Support/raw_ostream.h"
Alexander Potapenkof41954b2012-11-12 11:33:29 +000032#include <cstdio>
33#include <cstring>
34#include <map>
35#include <string>
36
37using namespace llvm;
38using namespace object;
39
40static cl::opt<bool>
41UseSymbolTable("use-symbol-table", cl::init(true),
42 cl::desc("Prefer names in symbol table to names "
43 "in debug info"));
44
45static cl::opt<bool>
46PrintFunctions("functions", cl::init(true),
47 cl::desc("Print function names as well as line "
48 "information for a given address"));
49
50static cl::opt<bool>
51PrintInlining("inlining", cl::init(true),
52 cl::desc("Print all inlined frames for a given address"));
53
54static cl::opt<bool>
55Demangle("demangle", cl::init(true),
56 cl::desc("Demangle function names"));
57
Dmitry Vyukovfc183ce2013-01-11 07:16:20 +000058static const std::string kSymbolizerBadString = "??";
59
Alexander Potapenkof41954b2012-11-12 11:33:29 +000060static StringRef ToolInvocationPath;
61
62static bool error(error_code ec) {
63 if (!ec) return false;
64 errs() << ToolInvocationPath << ": error reading file: "
65 << ec.message() << ".\n";
66 return true;
67}
68
69static uint32_t getDILineInfoSpecifierFlags() {
70 uint32_t Flags = llvm::DILineInfoSpecifier::FileLineInfo |
71 llvm::DILineInfoSpecifier::AbsoluteFilePath;
72 if (PrintFunctions)
73 Flags |= llvm::DILineInfoSpecifier::FunctionName;
74 return Flags;
75}
76
77static void patchFunctionNameInDILineInfo(const std::string &NewFunctionName,
78 DILineInfo &LineInfo) {
79 std::string FileName = LineInfo.getFileName();
80 LineInfo = DILineInfo(StringRef(FileName), StringRef(NewFunctionName),
81 LineInfo.getLine(), LineInfo.getColumn());
82}
83
84namespace {
85class ModuleInfo {
86 OwningPtr<ObjectFile> Module;
87 OwningPtr<DIContext> DebugInfoContext;
88 public:
89 ModuleInfo(ObjectFile *Obj, DIContext *DICtx)
90 : Module(Obj), DebugInfoContext(DICtx) {}
91
92 DILineInfo symbolizeCode(uint64_t ModuleOffset) const {
93 DILineInfo LineInfo;
94 if (DebugInfoContext) {
95 LineInfo = DebugInfoContext->getLineInfoForAddress(
96 ModuleOffset, getDILineInfoSpecifierFlags());
97 }
98 // Override function name from symbol table if necessary.
99 if (PrintFunctions && UseSymbolTable) {
100 std::string Function;
Dmitry Vyukovfc183ce2013-01-11 07:16:20 +0000101 uint64_t Start, Size;
102 if (getNameFromSymbolTable(SymbolRef::ST_Function,
103 ModuleOffset, Function, Start, Size)) {
Alexander Potapenkof41954b2012-11-12 11:33:29 +0000104 patchFunctionNameInDILineInfo(Function, LineInfo);
105 }
106 }
107 return LineInfo;
108 }
109
110 DIInliningInfo symbolizeInlinedCode(uint64_t ModuleOffset) const {
111 DIInliningInfo InlinedContext;
112 if (DebugInfoContext) {
113 InlinedContext = DebugInfoContext->getInliningInfoForAddress(
114 ModuleOffset, getDILineInfoSpecifierFlags());
115 }
116 // Make sure there is at least one frame in context.
117 if (InlinedContext.getNumberOfFrames() == 0) {
118 InlinedContext.addFrame(DILineInfo());
119 }
120 // Override the function name in lower frame with name from symbol table.
121 if (PrintFunctions && UseSymbolTable) {
122 DIInliningInfo PatchedInlinedContext;
123 for (uint32_t i = 0, n = InlinedContext.getNumberOfFrames();
124 i != n; i++) {
125 DILineInfo LineInfo = InlinedContext.getFrame(i);
126 if (i == n - 1) {
127 std::string Function;
Dmitry Vyukovfc183ce2013-01-11 07:16:20 +0000128 uint64_t Start, Size;
129 if (getNameFromSymbolTable(SymbolRef::ST_Function,
130 ModuleOffset, Function, Start, Size)) {
Alexander Potapenkof41954b2012-11-12 11:33:29 +0000131 patchFunctionNameInDILineInfo(Function, LineInfo);
132 }
133 }
134 PatchedInlinedContext.addFrame(LineInfo);
135 }
136 InlinedContext = PatchedInlinedContext;
137 }
138 return InlinedContext;
139 }
140
Dmitry Vyukovfc183ce2013-01-11 07:16:20 +0000141 bool symbolizeData(uint64_t ModuleOffset, std::string &Name,
142 uint64_t &Start, uint64_t &Size) const {
143 return getNameFromSymbolTable(SymbolRef::ST_Data,
144 ModuleOffset, Name, Start, Size);
145 }
146
Alexander Potapenkof41954b2012-11-12 11:33:29 +0000147 private:
Dmitry Vyukovfc183ce2013-01-11 07:16:20 +0000148 bool getNameFromSymbolTable(SymbolRef::Type Type,
149 uint64_t Address,
150 std::string &Name,
151 uint64_t &Addr,
152 uint64_t &Size) const {
Alexander Potapenkof41954b2012-11-12 11:33:29 +0000153 assert(Module);
154 error_code ec;
155 for (symbol_iterator si = Module->begin_symbols(),
156 se = Module->end_symbols();
157 si != se; si.increment(ec)) {
158 if (error(ec)) return false;
159 uint64_t SymbolAddress;
160 uint64_t SymbolSize;
161 SymbolRef::Type SymbolType;
162 if (error(si->getAddress(SymbolAddress)) ||
163 SymbolAddress == UnknownAddressOrSize) continue;
164 if (error(si->getSize(SymbolSize)) ||
165 SymbolSize == UnknownAddressOrSize) continue;
166 if (error(si->getType(SymbolType))) continue;
167 // FIXME: If a function has alias, there are two entries in symbol table
168 // with same address size. Make sure we choose the correct one.
169 if (SymbolAddress <= Address && Address < SymbolAddress + SymbolSize &&
Dmitry Vyukovfc183ce2013-01-11 07:16:20 +0000170 SymbolType == Type) {
171 StringRef SymbolName;
172 if (error(si->getName(SymbolName))) continue;
173 Name = SymbolName.str();
174 Addr = SymbolAddress;
175 Size = SymbolSize;
Alexander Potapenkof41954b2012-11-12 11:33:29 +0000176 return true;
177 }
178 }
179 return false;
180 }
181};
182
Dmitry Vyukovfc183ce2013-01-11 07:16:20 +0000183#if !defined(_MSC_VER)
184// Assume that __cxa_demangle is provided by libcxxabi (except for Windows).
185extern "C" char *__cxa_demangle(const char *mangled_name, char *output_buffer,
186 size_t *length, int *status);
187#endif
188
189void DemangleName(std::string &Name) {
190#if !defined(_MSC_VER)
191 if (!Demangle)
192 return;
193 int status = 0;
194 char *DemangledName = __cxa_demangle(Name.c_str(), 0, 0, &status);
195 if (status != 0)
196 return;
197 Name = DemangledName;
198 free(DemangledName);
199#endif
200}
201
Alexander Potapenkof41954b2012-11-12 11:33:29 +0000202typedef std::map<std::string, ModuleInfo*> ModuleMapTy;
203typedef ModuleMapTy::iterator ModuleMapIter;
204} // namespace
205
206static ModuleMapTy Modules;
207
Alexander Potapenkof41954b2012-11-12 11:33:29 +0000208// Returns true if the object endianness is known.
209static bool getObjectEndianness(const ObjectFile *Obj,
210 bool &IsLittleEndian) {
211 // FIXME: Implement this when libLLVMObject allows to do it easily.
212 IsLittleEndian = true;
213 return true;
214}
215
Alexander Potapenkof41954b2012-11-12 11:33:29 +0000216static ObjectFile *getObjectFile(const std::string &Path) {
217 OwningPtr<MemoryBuffer> Buff;
218 MemoryBuffer::getFile(Path, Buff);
219 return ObjectFile::createObjectFile(Buff.take());
220}
221
222static std::string getDarwinDWARFResourceForModule(const std::string &Path) {
223 StringRef Basename = sys::path::filename(Path);
224 const std::string &DSymDirectory = Path + ".dSYM";
225 SmallString<16> ResourceName = StringRef(DSymDirectory);
226 sys::path::append(ResourceName, "Contents", "Resources", "DWARF");
227 sys::path::append(ResourceName, Basename);
228 return ResourceName.str();
229}
230
231static ModuleInfo *getOrCreateModuleInfo(const std::string &ModuleName) {
232 ModuleMapIter I = Modules.find(ModuleName);
233 if (I != Modules.end())
234 return I->second;
235
236 ObjectFile *Obj = getObjectFile(ModuleName);
Eric Christopherd1726a42012-11-12 21:40:38 +0000237 ObjectFile *DbgObj = Obj;
Alexander Potapenkof41954b2012-11-12 11:33:29 +0000238 if (Obj == 0) {
239 // Module name doesn't point to a valid object file.
240 Modules.insert(make_pair(ModuleName, (ModuleInfo*)0));
241 return 0;
242 }
243
244 DIContext *Context = 0;
245 bool IsLittleEndian;
246 if (getObjectEndianness(Obj, IsLittleEndian)) {
Alexander Potapenkof41954b2012-11-12 11:33:29 +0000247 // On Darwin we may find DWARF in separate object file in
248 // resource directory.
249 if (isa<MachOObjectFile>(Obj)) {
250 const std::string &ResourceName = getDarwinDWARFResourceForModule(
251 ModuleName);
252 ObjectFile *ResourceObj = getObjectFile(ResourceName);
253 if (ResourceObj != 0)
Eric Christopherd1726a42012-11-12 21:40:38 +0000254 DbgObj = ResourceObj;
Alexander Potapenkof41954b2012-11-12 11:33:29 +0000255 }
Eric Christopherd1726a42012-11-12 21:40:38 +0000256 Context = DIContext::getDWARFContext(DbgObj);
Alexander Potapenkof41954b2012-11-12 11:33:29 +0000257 assert(Context);
258 }
259
260 ModuleInfo *Info = new ModuleInfo(Obj, Context);
261 Modules.insert(make_pair(ModuleName, Info));
262 return Info;
263}
264
Alexander Potapenkof41954b2012-11-12 11:33:29 +0000265static void printDILineInfo(DILineInfo LineInfo) {
266 // By default, DILineInfo contains "<invalid>" for function/filename it
267 // cannot fetch. We replace it to "??" to make our output closer to addr2line.
268 static const std::string kDILineInfoBadString = "<invalid>";
Alexander Potapenkof41954b2012-11-12 11:33:29 +0000269 if (PrintFunctions) {
270 std::string FunctionName = LineInfo.getFunctionName();
271 if (FunctionName == kDILineInfoBadString)
272 FunctionName = kSymbolizerBadString;
Dmitry Vyukovfc183ce2013-01-11 07:16:20 +0000273 DemangleName(FunctionName);
Alexander Potapenkof41954b2012-11-12 11:33:29 +0000274 outs() << FunctionName << "\n";
275 }
276 std::string Filename = LineInfo.getFileName();
277 if (Filename == kDILineInfoBadString)
278 Filename = kSymbolizerBadString;
279 outs() << Filename <<
280 ":" << LineInfo.getLine() <<
281 ":" << LineInfo.getColumn() <<
282 "\n";
283}
284
Dmitry Vyukovfc183ce2013-01-11 07:16:20 +0000285static void symbolizeCode(std::string ModuleName, std::string ModuleOffsetStr) {
Alexander Potapenkof41954b2012-11-12 11:33:29 +0000286 ModuleInfo *Info = getOrCreateModuleInfo(ModuleName);
287 uint64_t Offset = 0;
288 if (Info == 0 ||
289 StringRef(ModuleOffsetStr).getAsInteger(0, Offset)) {
290 printDILineInfo(DILineInfo());
291 } else if (PrintInlining) {
292 DIInliningInfo InlinedContext = Info->symbolizeInlinedCode(Offset);
293 uint32_t FramesNum = InlinedContext.getNumberOfFrames();
294 assert(FramesNum > 0);
295 for (uint32_t i = 0; i < FramesNum; i++) {
296 DILineInfo LineInfo = InlinedContext.getFrame(i);
297 printDILineInfo(LineInfo);
298 }
299 } else {
300 DILineInfo LineInfo = Info->symbolizeCode(Offset);
301 printDILineInfo(LineInfo);
302 }
303
304 outs() << "\n"; // Print extra empty line to mark the end of output.
305 outs().flush();
306}
307
Dmitry Vyukovfc183ce2013-01-11 07:16:20 +0000308static void symbolizeData(std::string ModuleName, std::string ModuleOffsetStr) {
309 std::string Name = kSymbolizerBadString;
310 uint64_t Start = 0;
311 uint64_t Size = 0;
312 uint64_t Offset = 0;
313 if (UseSymbolTable) {
314 ModuleInfo *Info = getOrCreateModuleInfo(ModuleName);
315 if (Info && !StringRef(ModuleOffsetStr).getAsInteger(0, Offset)) {
316 if (Info->symbolizeData(Offset, Name, Start, Size))
317 DemangleName(Name);
318 }
319 }
320 outs() << Name << "\n" << Start << " " << Size << "\n\n";
321 outs().flush();
322}
323
324static bool parseCommand(bool &IsData,
325 std::string &ModuleName,
326 std::string &ModuleOffsetStr) {
327 const char *kDataCmd = "DATA ";
328 const char *kCodeCmd = "CODE ";
329 const int kMaxInputStringLength = 1024;
330 const char kDelimiters[] = " \n";
Alexander Potapenkof41954b2012-11-12 11:33:29 +0000331 char InputString[kMaxInputStringLength];
332 if (!fgets(InputString, sizeof(InputString), stdin))
333 return false;
Dmitry Vyukovfc183ce2013-01-11 07:16:20 +0000334 IsData = false;
Alexander Potapenkof41954b2012-11-12 11:33:29 +0000335 ModuleName = "";
336 ModuleOffsetStr = "";
Dmitry Vyukovfc183ce2013-01-11 07:16:20 +0000337 char *pos = InputString;
338 if (strncmp(pos, kDataCmd, strlen(kDataCmd)) == 0) {
339 IsData = true;
340 pos += strlen(kDataCmd);
341 } else if (strncmp(pos, kCodeCmd, strlen(kCodeCmd)) == 0) {
342 IsData = false;
343 pos += strlen(kCodeCmd);
344 } else {
345 // If no cmd, assume it's CODE.
346 IsData = false;
347 }
Alexander Potapenkof41954b2012-11-12 11:33:29 +0000348 // FIXME: Handle case when filename is given in quotes.
Dmitry Vyukovfc183ce2013-01-11 07:16:20 +0000349 if (char *FilePath = strtok(pos, kDelimiters)) {
Alexander Potapenkof41954b2012-11-12 11:33:29 +0000350 ModuleName = FilePath;
351 if (char *OffsetStr = strtok((char*)0, kDelimiters))
352 ModuleOffsetStr = OffsetStr;
353 }
354 return true;
355}
356
357int main(int argc, char **argv) {
358 // Print stack trace if we signal out.
359 sys::PrintStackTraceOnErrorSignal();
360 PrettyStackTraceProgram X(argc, argv);
361 llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
362
363 cl::ParseCommandLineOptions(argc, argv, "llvm symbolizer for compiler-rt\n");
364 ToolInvocationPath = argv[0];
365
Dmitry Vyukovfc183ce2013-01-11 07:16:20 +0000366 bool IsData = false;
Alexander Potapenkof41954b2012-11-12 11:33:29 +0000367 std::string ModuleName;
368 std::string ModuleOffsetStr;
Dmitry Vyukovfc183ce2013-01-11 07:16:20 +0000369 while (parseCommand(IsData, ModuleName, ModuleOffsetStr)) {
370 if (IsData)
371 symbolizeData(ModuleName, ModuleOffsetStr);
372 else
373 symbolizeCode(ModuleName, ModuleOffsetStr);
Alexander Potapenkof41954b2012-11-12 11:33:29 +0000374 }
375 return 0;
376}