Started adding backtrace functionality for Win32, but need to find a replacement for kill and sigsetjmp/siglongjmp since mingw doesn't support them ....
This commit is contained in:
9
Makefile
9
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:
|
||||
|
||||
284
backtrace.c
Normal file
284
backtrace.c
Normal file
@@ -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 <windows.h>
|
||||
#include <excpt.h>
|
||||
#include <imagehlp.h>
|
||||
#include <bfd.h>
|
||||
#include <psapi.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#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
|
||||
7
backtrace.h
Normal file
7
backtrace.h
Normal file
@@ -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__
|
||||
7
exclib.c
7
exclib.c
@@ -2,7 +2,6 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <setjmp.h>
|
||||
#include <execinfo.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
@@ -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;
|
||||
|
||||
7
exclib.h
7
exclib.h
@@ -2,7 +2,11 @@
|
||||
#define __EXCLIB_H__
|
||||
|
||||
#include <setjmp.h>
|
||||
#ifdef WIN32
|
||||
#include "backtrace.h"
|
||||
#else
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
@@ -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; \
|
||||
|
||||
Reference in New Issue
Block a user