1 // Copyright 2007 Edd Dawson.
2 // Distributed under the Boost Software License, Version 1.0.
3 // (See accompanying file LICENSE_1_0.txt or copy at
4 // http://www.boost.org/LICENSE_1_0.txt)
5 
6 #include <iomanip>
7 #include <sstream>
8 #include <ostream>
9 #include <iostream>
10 #include <cassert>
11 #include <cstring>
12 #include "stack.h"
13 
14 #if defined(_WIN32)
15     #include <windows.h>
16     #include <imagehlp.h>
17 
18     #if defined(__MINGW32__)
19         #include <bfd.h> // link against libbfd and libiberty
20         #include <psapi.h> // link against psapi
21         #include <cxxabi.h>
22     #endif
23 
24 #elif defined(__GNUC__)
25     #include <dlfcn.h>
26     #include <cxxabi.h>
27 #endif
28 
29 namespace
30 {
31 
32 #if defined(__GNUC__)
demangle(const char * name)33     std::string demangle(const char *name)
34     {
35         int status = 0;
36         char *d = 0;
37         std::string ret = name;
38         try { if ((d = abi::__cxa_demangle(name, 0, 0, &status))) ret = d; }
39         catch(...) {  }
40         free(d);
41         return ret;
42     }
43 #endif
44 
45 #if defined(_WIN32)
46 
47     class uncopyable
48     {
49         public:
uncopyable()50             uncopyable() { }
51 
52         private:
53             uncopyable(const uncopyable &); // remains undefined
54             uncopyable &operator= (const uncopyable &); // remains undefined
55     };
56 
57     #if defined(__MINGW32__)
58 
59     class bfd_context : uncopyable
60     {
61         private:
62             struct find_data
63             {
64                 std::string func;
65                 asymbol **symbol_table;
66                 bfd_vma counter;
67             };
68 
69         public:
bfd_context()70             bfd_context() :
71                 abfd_(0),
72                 sec_(0),
73                 symbol_table_(0)
74             {
75                 char procname[MAX_PATH];
76                 GetModuleFileNameEx(GetCurrentProcess(), NULL, procname, sizeof procname);
77 
78                 bfd_init();
79                 abfd_ = bfd_openr(procname, 0);
80                 if (!abfd_)
81                     throw dbg::stack_error("Failed to parse object data for the executable");
82 
83                 char **formats = 0;
84                 bool b1 = bfd_check_format(abfd_, bfd_object);
85                 bool b2 = bfd_check_format_matches(abfd_, bfd_object, &formats);
86                 bool b3 = bfd_get_file_flags(abfd_) & HAS_SYMS;
87 
88                 if (!(b1 && b2 && b3))
89                 {
90                     bfd_close(abfd_);
91                     free(formats);
92                     throw dbg::stack_error("Failed to parse object data for the executable");
93                 }
94                 free(formats);
95 
96                 // Load symbol table
97                 unsigned dummy = 0;
98                 if (bfd_read_minisymbols(abfd_, FALSE, (void **)&symbol_table_, &dummy) == 0 &&
99                     bfd_read_minisymbols(abfd_, TRUE, (void **)&symbol_table_, &dummy) < 0)
100                 {
101                     free(symbol_table_);
102                     bfd_close(abfd_);
103                     throw dbg::stack_error("Failed to parse object data for the executable");
104                 }
105             }
106 
~bfd_context()107             ~bfd_context()
108             {
109                 free(symbol_table_);
110                 bfd_close(abfd_);
111             }
112 
get_function_name(DWORD offset)113             std::string get_function_name(DWORD offset)
114             {
115                 find_data data;
116                 data.symbol_table = symbol_table_;
117                 data.counter = offset;
118 
119                 bfd_map_over_sections(abfd_, &find_function_name_in_section, &data);
120 
121                 return data.func;
122             }
123 
124         private:
find_function_name_in_section(bfd * abfd,asection * sec,void * opaque_data)125             static void find_function_name_in_section(bfd *abfd, asection *sec, void *opaque_data)
126             {
127                 assert(sec);
128                 assert(opaque_data);
129                 find_data &data = *static_cast<find_data *>(opaque_data);
130 
131                 if (!data.func.empty()) return; // already found it
132 
133                 if (!(bfd_get_section_flags(abfd, sec) & SEC_ALLOC)) return;
134 
135                 bfd_vma vma = bfd_get_section_vma(abfd, sec);
136                 if (data.counter < vma || vma + bfd_get_section_size(sec) <= data.counter) return;
137 
138                 const char *func = 0;
139                 const char *file = 0;
140                 unsigned line = 0;
141 
142                 if (bfd_find_nearest_line(abfd, sec, data.symbol_table, data.counter - vma, &file, &func, &line) && func)
143                     data.func = demangle(func);
144             }
145 
146         private:
147             bfd *abfd_;
148             asection *sec_;
149             asymbol **symbol_table_;
150     };
151 
152     #endif // __MINGW32__
153 
154     class auto_cast_function_ptr
155     {
156         public:
157             template<typename FuncPtr>
auto_cast_function_ptr(FuncPtr f)158             explicit auto_cast_function_ptr(FuncPtr f) : fptr_(reinterpret_cast<void (*)(void)>(f)) { }
159 
160             template<typename FuncPtr>
operator FuncPtr() const161             operator FuncPtr() const { return reinterpret_cast<FuncPtr>(fptr_); }
162 
163         private:
164             void (*fptr_)(void);
165     };
166 
167     class windows_dll : uncopyable
168     {
169         public:
windows_dll(const std::string & libname)170             explicit windows_dll(const std::string &libname) :
171                 name_(libname),
172                 lib_(LoadLibrary(name_.c_str()))
173             {
174                 if (!lib_) throw dbg::stack_error("Failed to load dll " + name_);
175             }
176 
~windows_dll()177             ~windows_dll() { FreeLibrary(lib_); }
178 
name() const179             const std::string &name() const { return name_; }
180 
function(const std::string & func_name) const181             auto_cast_function_ptr function(const std::string &func_name) const
182             {
183                 FARPROC proc = GetProcAddress(lib_, func_name.c_str());
184                 if (!proc) throw dbg::stack_error("failed to load function " + func_name + " from library " + name_);
185 
186                 return auto_cast_function_ptr(proc);
187             }
188 
189         private:
190             std::string name_;
191             HMODULE lib_;
192     };
193 
194     class symbol_context : uncopyable
195     {
196         public:
symbol_context()197             symbol_context() { SymInitialize(GetCurrentProcess(), 0, true); }
~symbol_context()198             ~symbol_context() { SymCleanup(GetCurrentProcess()); }
199     };
200 
201 
202     class mutex : uncopyable
203     {
204         public:
mutex()205             mutex() { InitializeCriticalSection(&cs_); }
~mutex()206             ~mutex() { DeleteCriticalSection(&cs_); }
lock()207             void lock() { EnterCriticalSection(&cs_); }
unlock()208             void unlock() { LeaveCriticalSection(&cs_); }
209 
210         private:
211             CRITICAL_SECTION cs_;
212     };
213 
214     class scoped_lock : uncopyable
215     {
216         public:
scoped_lock(mutex & m)217             scoped_lock(mutex &m) : m_(m) { m_.lock(); }
~scoped_lock()218             ~scoped_lock() { m_.unlock(); }
219         private:
220             mutex &m_;
221     };
222 
223     mutex fill_frames_mtx_;
224 
225 
fill_frames(std::list<dbg::stack_frame> & frames,dbg::stack::depth_type limit)226     void fill_frames(std::list<dbg::stack_frame> &frames, dbg::stack::depth_type limit)
227     {
228         scoped_lock lk(fill_frames_mtx_);
229 
230         #if defined(__MINGW32__)
231             static bfd_context bfdc;
232         #endif
233 
234         symbol_context sc;
235 
236         STACKFRAME frame; std::memset(&frame, 0, sizeof frame);
237         CONTEXT context;  std::memset(&context, 0, sizeof(CONTEXT));
238         context.ContextFlags = CONTEXT_FULL;
239 
240         windows_dll kernel32("kernel32.dll");
241 
242         void (WINAPI *RtlCaptureContext_) (CONTEXT*) = kernel32.function("RtlCaptureContext");
243 
244         RtlCaptureContext_(&context);
245 
246         frame.AddrPC.Offset = context.Eip;
247         frame.AddrPC.Mode = AddrModeFlat;
248         frame.AddrStack.Offset = context.Esp;
249         frame.AddrStack.Mode = AddrModeFlat;
250         frame.AddrFrame.Offset = context.Ebp;
251         frame.AddrFrame.Mode = AddrModeFlat;
252 
253         HANDLE process = GetCurrentProcess();
254         HANDLE thread = GetCurrentThread();
255 
256         dbg::stack::depth_type skip = 0;
257         bool has_limit = limit != 0;
258         char symbol_buffer[sizeof(IMAGEHLP_SYMBOL) + 255];
259         char module_name_raw[MAX_PATH];
260 
261         while(StackWalk(IMAGE_FILE_MACHINE_I386, process, thread,
262                         &frame, &context, 0, SymFunctionTableAccess, SymGetModuleBase, 0    ))
263         {
264             if (skip < 1)
265             {
266                 ++skip;
267                 continue;
268             }
269 
270             if (has_limit && limit-- == 0) break;
271 
272             IMAGEHLP_SYMBOL *symbol = reinterpret_cast<IMAGEHLP_SYMBOL *>(symbol_buffer);
273             symbol->SizeOfStruct = (sizeof *symbol) + 255;
274             symbol->MaxNameLength = 254;
275 
276             DWORD module_base = SymGetModuleBase(process, frame.AddrPC.Offset);
277             std::string module_name = "[unknown module]";
278             if(   module_base && GetModuleFileName(reinterpret_cast<HINSTANCE>(module_base), module_name_raw, MAX_PATH))
279                 module_name = module_name_raw;
280 
281             #if defined(__MINGW32__)
282                 std::string func = bfdc.get_function_name(frame.AddrPC.Offset);
283 
284                 if (func.empty())
285                 {
286                     DWORD displacement = 0; // dummy variable
287                     BOOL got_symbol = SymGetSymFromAddr(process, frame.AddrPC.Offset, &displacement, symbol);
288                     func = got_symbol ? symbol->Name : "[unknown function]";
289                 }
290             #else
291                 DWORD displacement = 0; // dummy variable
292                 BOOL got_symbol = SymGetSymFromAddr(process, frame.AddrPC.Offset, &displacement, symbol);
293                 std::string func = got_symbol ? symbol->Name : "[unknown function]";
294             #endif
295 
296             dbg::stack_frame f(reinterpret_cast<const void *>(frame.AddrPC.Offset), func + " in " + module_name);
297             frames.push_back(f);
298         }
299     }
300 #elif defined(__GNUC__)
301     #if defined(__i386__) || defined(__x86_64__)
302 
fill_frames(std::list<dbg::stack_frame> & frames,dbg::stack::depth_type limit)303     void fill_frames(std::list<dbg::stack_frame> &frames, dbg::stack::depth_type limit)
304     {
305         // Based on code found at:
306         // http://www.tlug.org.za/wiki/index.php/Obtaining_a_stack_trace_in_C_upon_SIGSEGV
307 
308         Dl_info info;
309         void **frame = static_cast<void **>(__builtin_frame_address(0));
310         void **bp = static_cast<void **>(*frame);
311         void *ip = frame[1];
312 
313         bool has_limit = limit != 0;
314         dbg::stack::depth_type skip = 0;
315 
316         while(bp && ip && dladdr(ip, &info))
317         {
318             if (skip < 1)
319                 ++skip;
320             else
321             {
322                 if (has_limit && limit-- == 0) break;
323                 frames.push_back(dbg::stack_frame(ip, demangle(info.dli_sname) + " in " + info.dli_fname));
324 
325                 if(info.dli_sname && !strcmp(info.dli_sname, "main")) break;
326             }
327 
328             ip = bp[1];
329             bp = static_cast<void**>(bp[0]);
330         }
331     }
332 
333     #elif defined(__ppc__)
334 
fill_frames(std::list<dbg::stack_frame> & frames,dbg::stack::depth_type limit)335     void fill_frames(std::list<dbg::stack_frame> &frames, dbg::stack::depth_type limit)
336     {
337         // Based on code found at:
338         // http://www.informit.com/articles/article.aspx?p=606582&seqNum=4&rl=1
339 
340         void *ip = __builtin_return_address(0);
341         void **frame = static_cast<void **>(__builtin_frame_address(1));
342         bool has_limit = limit != 0;
343         Dl_info info;
344 
345         do
346         {
347             if (has_limit && limit-- == 0) break;
348 
349             if (dladdr(ip, &info))
350                 frames.push_back(dbg::stack_frame(ip, demangle(info.dli_sname) + " in " + info.dli_fname));
351 
352             if (frame && (frame = static_cast<void**>(*frame))) ip = *(frame + 2);
353         }
354         while (frame && ip);
355     }
356 
357     #else
358         // GNU, but neither x86 or PPC
359         #error "Sorry but dbg::stack is not supported on this architecture"
360     #endif
361 #else
362     // Unsupported compiler
363     #error "Sorry but dbg::stack is not supported on this compiler"
364 #endif
365 }
366 
367 
368 
369 namespace dbg
370 {
stack_error(const std::string & what)371     stack_error::stack_error(const std::string &what) :
372         what_(what)
373     {
374     }
375 
~stack_error()376     stack_error::~stack_error() throw()
377     {
378     }
379 
what() const380     const char *stack_error::what() const throw()
381     {
382         return what_.c_str();
383     }
384 
385 
stack_frame(const void * instruction,const std::string & function)386     stack_frame::stack_frame(const void *instruction, const std::string &function) :
387         instruction_(instruction),
388         function_(function)
389     {
390     }
391 
instruction() const392     const void *stack_frame::instruction() const
393     {
394         return instruction_;
395     }
396 
function() const397     const std::string &stack_frame::function() const
398     {
399         return function_;
400     }
401 
operator <<(std::ostream & out,const stack_frame & frame)402     std::ostream &operator<< (std::ostream &out, const stack_frame &frame)
403     {
404         return out << frame.instruction() << ": " << frame.function();
405     }
406 
stack(depth_type limit)407     stack::stack(depth_type limit)
408     {
409         fill_frames(frames_, limit);
410     }
411 
begin() const412     stack::const_iterator stack::begin() const
413     {
414         return frames_.begin();
415     }
416 
end() const417     stack::const_iterator stack::end() const
418     {
419         return frames_.end();
420     }
421 
depth() const422     stack::depth_type stack::depth() const
423     {
424         return frames_.size();
425     }
426 
427 } // close namespace dbg
428 
429