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