Files
Vangers/lib/utils/backtrace.c
Zhuravlev Uriy aka stalkerg 6730f30327 First version of public source.
2016-03-12 15:23:57 +03:00

459 lines
9.7 KiB
C

/*
Copyright (c) 2010 ,
Cloud Wu . All rights reserved.
http://www.codingnow.com
Use, modification and distribution are subject to the "New BSD License"
as listed at <url: http://www.opensource.org/licenses/bsd-license.php >.
filename: backtrace.c
compiler: gcc 3.4.5 (mingw-win32)
build command: gcc -O2 -shared -Wall -o backtrace.dll backtrace.c -lbfd -liberty -limagehlp -lintl
how to use: Call LoadLibraryA("backtrace.dll"); at beginning of your program .
*/
/* config.h. Generated from config.h.in by configure. */
/* config.h.in. Generated from configure.in by autoheader. */
/* Define to 1 if you have the <dlfcn.h> header file. */
/* #undef HAVE_DLFCN_H */
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
#define LT_OBJDIR ".libs/"
/* Define to 1 if your C compiler doesn't accept -c and -o together. */
/* #undef NO_MINUS_C_MINUS_O */
/* Name of package */
#define PACKAGE "libogg"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "ogg-dev@xiph.org"
/* Define to the full name of this package. */
#define PACKAGE_NAME "libogg"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "libogg 1.3.1"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "libogg"
/* Define to the home page for this package. */
#define PACKAGE_URL ""
/* Define to the version of this package. */
#define PACKAGE_VERSION "1.3.1"
/* The size of `int', as computed by sizeof. */
#define SIZEOF_INT 4
/* The size of `int16_t', as computed by sizeof. */
#define SIZEOF_INT16_T 2
/* The size of `int32_t', as computed by sizeof. */
#define SIZEOF_INT32_T 4
/* The size of `int64_t', as computed by sizeof. */
#define SIZEOF_INT64_T 8
/* The size of `long', as computed by sizeof. */
#define SIZEOF_LONG 4
/* The size of `long long', as computed by sizeof. */
#define SIZEOF_LONG_LONG 8
/* The size of `short', as computed by sizeof. */
#define SIZEOF_SHORT 2
/* The size of `uint16_t', as computed by sizeof. */
#define SIZEOF_UINT16_T 2
/* The size of `uint32_t', as computed by sizeof. */
#define SIZEOF_UINT32_T 4
/* The size of `u_int16_t', as computed by sizeof. */
#define SIZEOF_U_INT16_T 0
/* The size of `u_int32_t', as computed by sizeof. */
#define SIZEOF_U_INT32_T 0
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Version number of package */
#define VERSION "1.3.1"
/* Define to empty if `const' does not conform to ANSI C. */
/* #undef const */
#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 (16*1024)
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;
};
struct output_buffer {
char * buf;
size_t sz;
size_t ptr;
};
static void
output_init(struct output_buffer *ob, char * buf, size_t sz)
{
ob->buf = buf;
ob->sz = sz;
ob->ptr = 0;
ob->buf[0] = '\0';
}
static void
output_print(struct output_buffer *ob, const char * format, ...)
{
if (ob->sz == ob->ptr)
return;
ob->buf[ob->ptr] = '\0';
va_list ap;
va_start(ap,format);
vsnprintf(ob->buf + ob->ptr , ob->sz - ob->ptr , format, ap);
va_end(ap);
ob->ptr = strlen(ob->buf + ob->ptr) + ob->ptr;
}
static 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));
}
static 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;
}
}
static int
init_bfd_ctx(struct bfd_ctx *bc, const char * procname, struct output_buffer *ob)
{
bc->handle = NULL;
bc->symbol = NULL;
bfd *b = bfd_openr(procname, 0);
if (!b) {
output_print(ob,"Failed to open bfd from (%s)\n" , procname);
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);
output_print(ob,"Failed to init bfd from (%s)\n", procname);
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);
output_print(ob,"Failed to read symbols from (%s)\n", procname);
return 1;
}
}
bc->handle = b;
bc->symbol = symbol_table;
return 0;
}
static void
close_bfd_ctx(struct bfd_ctx *bc)
{
if (bc) {
if (bc->symbol) {
free(bc->symbol);
}
if (bc->handle) {
bfd_close(bc->handle);
}
}
}
static struct bfd_ctx *
get_bc(struct output_buffer *ob , 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 , ob)) {
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;
}
static 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;
}
}
static void
_backtrace(struct output_buffer *ob, struct bfd_set *set, int depth , LPCONTEXT context)
{
char procname[MAX_PATH];
GetModuleFileNameA(NULL, procname, sizeof procname);
struct bfd_ctx *bc = NULL;
STACKFRAME frame;
memset(&frame,0,sizeof(frame));
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;
HANDLE process = GetCurrentProcess();
HANDLE thread = GetCurrentThread();
char symbol_buffer[sizeof(IMAGEHLP_SYMBOL) + 255];
char module_name_raw[MAX_PATH];
while(StackWalk(IMAGE_FILE_MACHINE_I386,
process,
thread,
&frame,
context,
0,
SymFunctionTableAccess,
SymGetModuleBase, 0)) {
--depth;
if (depth < 0)
break;
IMAGEHLP_SYMBOL *symbol = (IMAGEHLP_SYMBOL *)symbol_buffer;
symbol->SizeOfStruct = (sizeof *symbol) + 255;
symbol->MaxNameLength = 254;
DWORD module_base = SymGetModuleBase(process, frame.AddrPC.Offset);
const char * module_name = "[unknown module]";
if (module_base &&
GetModuleFileNameA((HINSTANCE)module_base, module_name_raw, MAX_PATH)) {
module_name = module_name_raw;
bc = get_bc(ob, set, module_name);
}
const char * file = NULL;
const char * func = NULL;
unsigned line = 0;
if (bc) {
find(bc,frame.AddrPC.Offset,&file,&func,&line);
}
if (file == NULL) {
DWORD dummy = 0;
if (SymGetSymFromAddr(process, frame.AddrPC.Offset, &dummy, symbol)) {
file = symbol->Name;
}
else {
file = "[unknown file]";
}
}
if (func == NULL) {
output_print(ob,"0x%x : %s : %s \n",
frame.AddrPC.Offset,
module_name,
file);
}
else {
output_print(ob,"0x%x : %s : %s (%d) : in function (%s) \n",
frame.AddrPC.Offset,
module_name,
file,
line,
func);
}
}
}
static char * g_output = NULL;
static LPTOP_LEVEL_EXCEPTION_FILTER g_prev = NULL;
static LONG WINAPI
exception_filter(LPEXCEPTION_POINTERS info)
{
struct output_buffer ob;
output_init(&ob, g_output, BUFFER_MAX);
if (!SymInitialize(GetCurrentProcess(), 0, TRUE)) {
output_print(&ob,"Failed to init symbol context\n");
}
else {
bfd_init();
struct bfd_set *set = calloc(1,sizeof(*set));
_backtrace(&ob , set , 128 , info->ContextRecord);
release_set(set);
SymCleanup(GetCurrentProcess());
}
fputs(g_output , stderr);
exit(1);
return 0;
}
static void
backtrace_register(void)
{
if (g_output == NULL) {
g_output = malloc(BUFFER_MAX);
g_prev = SetUnhandledExceptionFilter(exception_filter);
}
}
static void
backtrace_unregister(void)
{
if (g_output) {
free(g_output);
SetUnhandledExceptionFilter(g_prev);
g_prev = NULL;
g_output = NULL;
}
}
BOOL WINAPI
DllMain(HANDLE hinstDLL, DWORD dwReason, LPVOID lpvReserved)
{
switch (dwReason) {
case DLL_PROCESS_ATTACH:
backtrace_register();
break;
case DLL_PROCESS_DETACH:
backtrace_unregister();
break;
}
return TRUE;
}