├── Makefile ├── backtrace.c └── test.c /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all clean 2 | 3 | all : backtrace.dll test.exe 4 | 5 | backtrace.dll : backtrace.c 6 | gcc -O2 -shared -Wall -o $@ $^ -lbfd -liberty -limagehlp -lz -lintl 7 | 8 | test.exe : test.c 9 | gcc -g -Wall -o $@ $^ 10 | 11 | clean : 12 | -del -f backtrace.dll test.exe 13 | -------------------------------------------------------------------------------- /backtrace.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2010 , 3 | Cloud Wu . All rights reserved. 4 | 5 | http://www.codingnow.com 6 | 7 | Use, modification and distribution are subject to the "New BSD License" 8 | as listed at . 9 | 10 | filename: backtrace.c 11 | 12 | compiler: gcc 3.4.5 (mingw-win32) 13 | 14 | build command: gcc -O2 -shared -Wall -o backtrace.dll backtrace.c -lbfd -liberty -limagehlp 15 | 16 | how to use: Call LoadLibraryA("backtrace.dll"); at beginning of your program . 17 | 18 | */ 19 | 20 | #define PACKAGE "your-program-name" 21 | #define PACKAGE_VERSION "0.1" 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #define BUFFER_MAX (16*1024) 35 | 36 | #define BFD_ERR_OK (0) 37 | #define BFD_ERR_OPEN_FAIL (1) 38 | #define BFD_ERR_BAD_FORMAT (2) 39 | #define BFD_ERR_NO_SYMBOLS (3) 40 | #define BFD_ERR_READ_SYMBOL (4) 41 | 42 | static const char *const bfd_errors[] = { 43 | "", 44 | "(Failed to open bfd)", 45 | "(Bad format)", 46 | "(No symbols)", 47 | "(Failed to read symbols)", 48 | }; 49 | 50 | struct bfd_ctx { 51 | bfd * handle; 52 | asymbol ** symbol; 53 | }; 54 | 55 | struct bfd_set { 56 | char * name; 57 | struct bfd_ctx * bc; 58 | struct bfd_set *next; 59 | }; 60 | 61 | struct find_info { 62 | asymbol **symbol; 63 | bfd_vma counter; 64 | const char *file; 65 | const char *func; 66 | unsigned line; 67 | }; 68 | 69 | struct output_buffer { 70 | char * buf; 71 | size_t sz; 72 | size_t ptr; 73 | }; 74 | 75 | static void 76 | output_init(struct output_buffer *ob, char * buf, size_t sz) 77 | { 78 | ob->buf = buf; 79 | ob->sz = sz; 80 | ob->ptr = 0; 81 | ob->buf[0] = '\0'; 82 | } 83 | 84 | static void 85 | output_print(struct output_buffer *ob, const char * format, ...) 86 | { 87 | if (ob->sz == ob->ptr) 88 | return; 89 | ob->buf[ob->ptr] = '\0'; 90 | va_list ap; 91 | va_start(ap,format); 92 | vsnprintf(ob->buf + ob->ptr , ob->sz - ob->ptr , format, ap); 93 | va_end(ap); 94 | 95 | ob->ptr = strlen(ob->buf + ob->ptr) + ob->ptr; 96 | } 97 | 98 | static void 99 | lookup_section(bfd *abfd, asection *sec, void *opaque_data) 100 | { 101 | struct find_info *data = opaque_data; 102 | 103 | if (data->func) 104 | return; 105 | 106 | if (!(bfd_get_section_flags(abfd, sec) & SEC_ALLOC)) 107 | return; 108 | 109 | bfd_vma vma = bfd_get_section_vma(abfd, sec); 110 | if (data->counter < vma || vma + bfd_get_section_size(sec) <= data->counter) 111 | return; 112 | 113 | bfd_find_nearest_line(abfd, sec, data->symbol, data->counter - vma, &(data->file), &(data->func), &(data->line)); 114 | } 115 | 116 | static void 117 | find(struct bfd_ctx * b, DWORD offset, const char **file, const char **func, unsigned *line) 118 | { 119 | struct find_info data; 120 | data.func = NULL; 121 | data.symbol = b->symbol; 122 | data.counter = offset; 123 | data.file = NULL; 124 | data.func = NULL; 125 | data.line = 0; 126 | 127 | bfd_map_over_sections(b->handle, &lookup_section, &data); 128 | if (file) { 129 | *file = data.file; 130 | } 131 | if (func) { 132 | *func = data.func; 133 | } 134 | if (line) { 135 | *line = data.line; 136 | } 137 | } 138 | 139 | static int 140 | init_bfd_ctx(struct bfd_ctx *bc, const char * procname, int *err) 141 | { 142 | bc->handle = NULL; 143 | bc->symbol = NULL; 144 | 145 | bfd *b = bfd_openr(procname, 0); 146 | if (!b) { 147 | if(err) { *err = BFD_ERR_OPEN_FAIL; } 148 | return 1; 149 | } 150 | 151 | if(!bfd_check_format(b, bfd_object)) { 152 | bfd_close(b); 153 | if(err) { *err = BFD_ERR_BAD_FORMAT; } 154 | return 1; 155 | } 156 | 157 | if(!(bfd_get_file_flags(b) & HAS_SYMS)) { 158 | bfd_close(b); 159 | if(err) { *err = BFD_ERR_NO_SYMBOLS; } 160 | return 1; 161 | } 162 | 163 | void *symbol_table; 164 | 165 | unsigned dummy = 0; 166 | if (bfd_read_minisymbols(b, FALSE, &symbol_table, &dummy) == 0) { 167 | if (bfd_read_minisymbols(b, TRUE, &symbol_table, &dummy) < 0) { 168 | free(symbol_table); 169 | bfd_close(b); 170 | if(err) { *err = BFD_ERR_READ_SYMBOL; } 171 | return 1; 172 | } 173 | } 174 | 175 | bc->handle = b; 176 | bc->symbol = symbol_table; 177 | 178 | if(err) { *err = BFD_ERR_OK; } 179 | return 0; 180 | } 181 | 182 | static void 183 | close_bfd_ctx(struct bfd_ctx *bc) 184 | { 185 | if (bc) { 186 | if (bc->symbol) { 187 | free(bc->symbol); 188 | } 189 | if (bc->handle) { 190 | bfd_close(bc->handle); 191 | } 192 | } 193 | } 194 | 195 | static struct bfd_ctx * 196 | get_bc(struct bfd_set *set , const char *procname, int *err) 197 | { 198 | while(set->name) { 199 | if (strcmp(set->name , procname) == 0) { 200 | return set->bc; 201 | } 202 | set = set->next; 203 | } 204 | struct bfd_ctx bc; 205 | if (init_bfd_ctx(&bc, procname, err)) { 206 | return NULL; 207 | } 208 | set->next = calloc(1, sizeof(*set)); 209 | set->bc = malloc(sizeof(struct bfd_ctx)); 210 | memcpy(set->bc, &bc, sizeof(bc)); 211 | set->name = strdup(procname); 212 | 213 | return set->bc; 214 | } 215 | 216 | static void 217 | release_set(struct bfd_set *set) 218 | { 219 | while(set) { 220 | struct bfd_set * temp = set->next; 221 | free(set->name); 222 | close_bfd_ctx(set->bc); 223 | free(set); 224 | set = temp; 225 | } 226 | } 227 | 228 | static void 229 | _backtrace(struct output_buffer *ob, struct bfd_set *set, int depth , LPCONTEXT context) 230 | { 231 | char procname[MAX_PATH]; 232 | GetModuleFileNameA(NULL, procname, sizeof procname); 233 | 234 | struct bfd_ctx *bc = NULL; 235 | int err = BFD_ERR_OK; 236 | 237 | STACKFRAME frame; 238 | memset(&frame,0,sizeof(frame)); 239 | 240 | frame.AddrPC.Offset = context->Eip; 241 | frame.AddrPC.Mode = AddrModeFlat; 242 | frame.AddrStack.Offset = context->Esp; 243 | frame.AddrStack.Mode = AddrModeFlat; 244 | frame.AddrFrame.Offset = context->Ebp; 245 | frame.AddrFrame.Mode = AddrModeFlat; 246 | 247 | HANDLE process = GetCurrentProcess(); 248 | HANDLE thread = GetCurrentThread(); 249 | 250 | char symbol_buffer[sizeof(IMAGEHLP_SYMBOL) + 255]; 251 | char module_name_raw[MAX_PATH]; 252 | 253 | while(StackWalk(IMAGE_FILE_MACHINE_I386, 254 | process, 255 | thread, 256 | &frame, 257 | context, 258 | 0, 259 | SymFunctionTableAccess, 260 | SymGetModuleBase, 0)) { 261 | 262 | --depth; 263 | if (depth < 0) 264 | break; 265 | 266 | IMAGEHLP_SYMBOL *symbol = (IMAGEHLP_SYMBOL *)symbol_buffer; 267 | symbol->SizeOfStruct = (sizeof *symbol) + 255; 268 | symbol->MaxNameLength = 254; 269 | 270 | DWORD module_base = SymGetModuleBase(process, frame.AddrPC.Offset); 271 | 272 | const char * module_name = "[unknown module]"; 273 | if (module_base && 274 | GetModuleFileNameA((HINSTANCE)module_base, module_name_raw, MAX_PATH)) { 275 | module_name = module_name_raw; 276 | bc = get_bc(set, module_name, &err); 277 | } 278 | 279 | const char * file = NULL; 280 | const char * func = NULL; 281 | unsigned line = 0; 282 | 283 | if (bc) { 284 | find(bc,frame.AddrPC.Offset,&file,&func,&line); 285 | } 286 | 287 | if (file == NULL) { 288 | DWORD dummy = 0; 289 | if (SymGetSymFromAddr(process, frame.AddrPC.Offset, &dummy, symbol)) { 290 | file = symbol->Name; 291 | } 292 | else { 293 | file = "[unknown file]"; 294 | } 295 | } 296 | if (func == NULL) { 297 | output_print(ob,"0x%08x : %s : %s %s \n", 298 | frame.AddrPC.Offset, 299 | module_name, 300 | file, 301 | bfd_errors[err]); 302 | } 303 | else { 304 | output_print(ob,"0x%08x : %s : %s (%d) : in function (%s) \n", 305 | frame.AddrPC.Offset, 306 | module_name, 307 | file, 308 | line, 309 | func); 310 | } 311 | } 312 | } 313 | 314 | static char * g_output = NULL; 315 | static LPTOP_LEVEL_EXCEPTION_FILTER g_prev = NULL; 316 | 317 | static LONG WINAPI 318 | exception_filter(LPEXCEPTION_POINTERS info) 319 | { 320 | struct output_buffer ob; 321 | output_init(&ob, g_output, BUFFER_MAX); 322 | 323 | if (!SymInitialize(GetCurrentProcess(), 0, TRUE)) { 324 | output_print(&ob,"Failed to init symbol context\n"); 325 | } 326 | else { 327 | bfd_init(); 328 | struct bfd_set *set = calloc(1,sizeof(*set)); 329 | _backtrace(&ob , set , 128 , info->ContextRecord); 330 | release_set(set); 331 | 332 | SymCleanup(GetCurrentProcess()); 333 | } 334 | 335 | fputs(g_output , stderr); 336 | 337 | return EXCEPTION_CONTINUE_SEARCH; 338 | } 339 | 340 | static void 341 | backtrace_register(void) 342 | { 343 | if (g_output == NULL) { 344 | g_output = malloc(BUFFER_MAX); 345 | g_prev = SetUnhandledExceptionFilter(exception_filter); 346 | } 347 | } 348 | 349 | static void 350 | backtrace_unregister(void) 351 | { 352 | if (g_output) { 353 | free(g_output); 354 | SetUnhandledExceptionFilter(g_prev); 355 | g_prev = NULL; 356 | g_output = NULL; 357 | } 358 | } 359 | 360 | int 361 | __printf__(const char * format, ...) { 362 | int value; 363 | va_list arg; 364 | va_start(arg, format); 365 | value = vprintf ( format, arg ); 366 | va_end(arg); 367 | return value; 368 | } 369 | 370 | BOOL WINAPI 371 | DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved) 372 | { 373 | switch (dwReason) { 374 | case DLL_PROCESS_ATTACH: 375 | backtrace_register(); 376 | break; 377 | case DLL_PROCESS_DETACH: 378 | backtrace_unregister(); 379 | break; 380 | } 381 | return TRUE; 382 | } 383 | 384 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static void 4 | foo() 5 | { 6 | int *f=NULL; 7 | *f = 0; 8 | } 9 | 10 | static void 11 | bar() 12 | { 13 | foo(); 14 | } 15 | 16 | int 17 | main() 18 | { 19 | LoadLibraryA("backtrace.dll"); 20 | bar(); 21 | 22 | return 0; 23 | } 24 | --------------------------------------------------------------------------------