diff --git a/Makefile b/Makefile index c3fc4b6..3e26f74 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,11 @@ CC=gcc LD=gcc -OBJECTS=exclib.o exception_test.o +EXECOBJ= +OBJECTS=exclib.o exception_test.o backtrace.o +LIBS= +ifeq "$(OS)" "win32" + LIBS=-lbfd -lintl -liberty -imagehlp +endif all: exception_test @@ -8,7 +13,7 @@ all: exception_test $(CC) -c -o $@ $(CFLAGS) -rdynamic -ggdb -gstabs $< exception_test: $(OBJECTS) - $(LD) -o exception_test $(OBJECTS) + $(LD) -o exception_test $(LIBS) $(OBJECTS) .PHONY: clean clean: diff --git a/backtrace.c b/backtrace.c new file mode 100644 index 0000000..d334e80 --- /dev/null +++ b/backtrace.c @@ -0,0 +1,284 @@ +/* + Based on mingw32 "backtrace" library provided by Cloud Wu, here: + http://code.google.com/p/backtrace-mingw/ + + Modified to provide the same interface that execinfo.h does on GNU/Unix hosts. +*/ + +#ifdef WIN32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUFFER_MAX 1024 +char **symbols; +char scratch[BUFFER_MAX]; + +struct bfd_ctx { + bfd * handle; + asymbol ** symbol; +}; + +struct bfd_set { + char * name; + struct bfd_ctx * bc; + struct bfd_set *next; +}; + +struct find_info { + asymbol **symbol; + bfd_vma counter; + const char *file; + const char *func; + unsigned line; +}; + +void lookup_section(bfd *abfd, asection *sec, void *opaque_data) +{ + struct find_info *data = opaque_data; + + if (data->func) + return; + + if (!(bfd_get_section_flags(abfd, sec) & SEC_ALLOC)) + return; + + bfd_vma vma = bfd_get_section_vma(abfd, sec); + if (data->counter < vma || vma + bfd_get_section_size(sec) <= data->counter) + return; + + bfd_find_nearest_line(abfd, sec, data->symbol, data->counter - vma, &(data->file), &(data->func), &(data->line)); +} + +void find(struct bfd_ctx * b, DWORD offset, const char **file, const char **func, unsigned *line) +{ + struct find_info data; + data.func = NULL; + data.symbol = b->symbol; + data.counter = offset; + data.file = NULL; + data.func = NULL; + data.line = 0; + + bfd_map_over_sections(b->handle, &lookup_section, &data); + if (file) { + *file = data.file; + } + if (func) { + *func = data.func; + } + if (line) { + *line = data.line; + } +} + +int init_bfd_ctx(struct bfd_ctx *bc, const char * procname) +{ + bc->handle = NULL; + bc->symbol = NULL; + + bfd *b = bfd_openr(procname, 0); + if (!b) { + return 1; + } + + int r1 = bfd_check_format(b, bfd_object); + int r2 = bfd_check_format_matches(b, bfd_object, NULL); + int r3 = bfd_get_file_flags(b) & HAS_SYMS; + + if (!(r1 && r2 && r3)) { + bfd_close(b); + return 1; + } + + void *symbol_table; + + unsigned dummy = 0; + if (bfd_read_minisymbols(b, FALSE, &symbol_table, &dummy) == 0) { + if (bfd_read_minisymbols(b, TRUE, &symbol_table, &dummy) < 0) { + free(symbol_table); + bfd_close(b); + return 1; + } + } + + bc->handle = b; + bc->symbol = symbol_table; + + return 0; +} + +void close_bfd_ctx(struct bfd_ctx *bc) +{ + if (bc) { + if (bc->symbol) { + free(bc->symbol); + } + if (bc->handle) { + bfd_close(bc->handle); + } + } +} + +struct bfd_ctx *get_bc(struct bfd_set *set , const char *procname) +{ + while(set->name) { + if (strcmp(set->name , procname) == 0) { + return set->bc; + } + set = set->next; + } + struct bfd_ctx bc; + if (init_bfd_ctx(&bc, procname)) { + return NULL; + } + set->next = calloc(1, sizeof(*set)); + set->bc = malloc(sizeof(struct bfd_ctx)); + memcpy(set->bc, &bc, sizeof(bc)); + set->name = strdup(procname); + + return set->bc; +} + +void release_set(struct bfd_set *set) +{ + while(set) { + struct bfd_set * temp = set->next; + free(set->name); + close_bfd_ctx(set->bc); + free(set); + set = temp; + } +} + +int backtrace(void **buffer, int size) +{ + if ( !buffer ) + return 1; + bfd_init(); + struct bfd_set *set = calloc(1,sizeof(*set)); + _backtrace(buffer, size, set, NULL); + release_set(set); + return 0; +} + +char **backtrace_symbols(void **buffer, int size) +{ + return symbols; +} + +int _backtrace(void **buffer, int depth, struct bfd_set *set) +{ + char procname[MAX_PATH]; + LPCONTEXT context; + struct bfd_ctx *bc = NULL; + int i = depth; + STACKFRAME frame; + HANDLE process; + HANDLE thread; + char symbol_buffer[sizeof(IMAGEHLP_SYMBOL) + 255]; + char module_name_raw[MAX_PATH]; + IMAGEHLP_SYMBOL *symbol; + DWORD module_base; + const char * module_name; + const char * file = NULL; + const char * func = NULL; + char tmpbuf[512]; + unsigned line = 0; + DWORD dummy; + char *spos = &scratch; + + if ( symbols ) + free(symbols); + symbols = (void **)malloc((sizeof(void *) * depth)); + if (!symbols) + return 1; + memset(symbols, 0, sizeof(void *) * depth); + + memset(spos, 0x00, BUFFER_MAX); + + memset(&frame,0,sizeof(frame)); + GetModuleFileNameA(NULL, procname, sizeof procname); + process = GetCurrentProcess(); + thread = GetCurrentThread(); + GetThreadContext(thread, context); + + frame.AddrPC.Offset = context->Eip; + frame.AddrPC.Mode = AddrModeFlat; + frame.AddrStack.Offset = context->Esp; + frame.AddrStack.Mode = AddrModeFlat; + frame.AddrFrame.Offset = context->Ebp; + frame.AddrFrame.Mode = AddrModeFlat; + + while(StackWalk(IMAGE_FILE_MACHINE_I386, + process, + thread, + &frame, + context, + 0, + SymFunctionTableAccess, + SymGetModuleBase, 0)) { + + i++; + if ( i >= depth ) + break; + + symbol = (IMAGEHLP_SYMBOL *)symbol_buffer; + symbol->SizeOfStruct = (sizeof *symbol) + 255; + symbol->MaxNameLength = 254; + + module_base = SymGetModuleBase(process, frame.AddrPC.Offset); + + module_name = "[unknown module]"; + if (module_base && + GetModuleFileNameA((HINSTANCE)module_base, module_name_raw, MAX_PATH)) { + module_name = module_name_raw; + bc = get_bc(set, module_name); + } + + file = NULL; + func = NULL; + line = 0; + + if (bc) { + find(bc,frame.AddrPC.Offset,&file,&func,&line); + } + + if (file == NULL) { + dummy = 0; + if (SymGetSymFromAddr(process, frame.AddrPC.Offset, &dummy, symbol)) { + file = symbol->Name; + } + else { + file = "[unknown file]"; + } + } + + if (func == NULL) { + buffer[i] = frame.AddrPC.Offset; + sprintf((char *)&tmpbuf, "%s(NULL+0x??)[0x%x]\0", + module_name, + frame.AddrPC.Offset); + } + else { + buffer[i] = frame.AddrPC.Offset; + sprintf((char *)&tmpbuf, "%s(%s+0x??)[0x%x]\0", + module_name, + frame.AddrPC.Offset, + func); + } + strcpy(spos, (char *)&tmpbuf); + symbols[i] = spos; + spos += strlen((char *)&tmpbuf); + } +} + +#endif // WIN32 diff --git a/backtrace.h b/backtrace.h new file mode 100644 index 0000000..ae8d465 --- /dev/null +++ b/backtrace.h @@ -0,0 +1,7 @@ +#ifndef __BACKTRACE_H__ +#define __BACKTRACE_H__ + +extern int backtrace(void **buffer, int size); +extern char **backtrace_symbols(void *const *buffer, int size); + +#endif // __BACKTRACE_H__ diff --git a/exclib.c b/exclib.c index ba9527f..1e103ba 100644 --- a/exclib.c +++ b/exclib.c @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include @@ -16,7 +15,7 @@ int __exc_signals[EXC_MAX_EXCEPTIONS]; struct exc_name_data __exclib_exc_names[EXC_PREDEFINED_EXCEPTIONS] = { {EXC_NULLPOINTER, "Null Pointer", SIGSEGV}, - {EXC_OUTOFBOUNDS, "Array Index Out of Bounds", SIGKILL} + {EXC_OUTOFBOUNDS, "Array Index Out of Bounds", SIGTERM} }; void exclib_print_exception_stack(char *file, char *func, int line) @@ -158,7 +157,7 @@ void exclib_init_strings() // this takes some time but the security is worth it, also the ease of use for ( i = 0; i < EXC_MAX_EXCEPTIONS; i++) { __exc_names[i] = 0; - __exc_signals[i] = SIGKILL; + __exc_signals[i] = SIGTERM; } // make sure all the exceptions we (the library) export are defined & named __exc_initstrings = 1; @@ -228,7 +227,7 @@ int exclib_clear_exc_frame() if ( !es ) { exclib_print_stacktrace("exclib_clear_exc_frame was called but there were no exception frames to clear!", __FILE__, (char *)__func__, __LINE__); - kill(getpid(), SIGKILL); + kill(getpid(), SIGTERM); } if ( es->prev ) es->prev->next = NULL; diff --git a/exclib.h b/exclib.h index 45d713b..4e78cfa 100644 --- a/exclib.h +++ b/exclib.h @@ -2,7 +2,11 @@ #define __EXCLIB_H__ #include +#ifdef WIN32 +#include "backtrace.h" +#else #include +#endif #include #include #include @@ -103,7 +107,8 @@ if ( exclib_new_exc_frame(&__exc_statuses[__exc_curidx++], __FILE__, (char *)__f exclib_print_stacktrace("Tried to TRY but couldn't create new exception frame", __FILE__, (char *)__func__, __LINE__); \ EXC_STATUS_LIST->value = sigsetjmp(EXC_STATUS_LIST->buf, 1); \ EXC_STATUS_LIST->tried = 1;\ -switch( EXC_STATUS_LIST->value ) { case 0: +switch( EXC_STATUS_LIST->value ) { \ + case 0: #define CATCH(x) \ break; \