From c34daa26c67f4a879ff541775c099ddc19a1dec5 Mon Sep 17 00:00:00 2001
From: Chris Xiong <chirs241097@gmail.com>
Date: Mon, 2 Dec 2019 01:19:30 +0800
Subject: Add a stack trace printer for the beloved operating system.

---
 third_party/backtrace-mingw/CMakeLists.txt |  11 +
 third_party/backtrace-mingw/README.md      |  20 ++
 third_party/backtrace-mingw/backtrace.c    | 457 +++++++++++++++++++++++++++++
 third_party/backtrace-mingw/test.c         |  23 ++
 4 files changed, 511 insertions(+)
 create mode 100644 third_party/backtrace-mingw/CMakeLists.txt
 create mode 100644 third_party/backtrace-mingw/README.md
 create mode 100644 third_party/backtrace-mingw/backtrace.c
 create mode 100644 third_party/backtrace-mingw/test.c

(limited to 'third_party')

diff --git a/third_party/backtrace-mingw/CMakeLists.txt b/third_party/backtrace-mingw/CMakeLists.txt
new file mode 100644
index 0000000..394d420
--- /dev/null
+++ b/third_party/backtrace-mingw/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_library(backtrace SHARED backtrace.c)
+find_path(BFD_INCLUDE_DIR NAMES bfd.h)
+find_library(BFD_LIBRARIES NAMES bfd)
+find_library(IBERTY_LIBRARIES NAMES iberty)
+find_package(ZLIB REQUIRED)
+target_link_libraries(backtrace
+	${BFD_LIBRARIES}
+	${IBERTY_LIBRARIES}
+	${ZLIB_LIBRARIES}
+	imagehlp
+)
diff --git a/third_party/backtrace-mingw/README.md b/third_party/backtrace-mingw/README.md
new file mode 100644
index 0000000..77cb879
--- /dev/null
+++ b/third_party/backtrace-mingw/README.md
@@ -0,0 +1,20 @@
+The original project doesn't come with a readme.
+
+The code has been slightly tweaked to add support for amd64 and to obtain a
+more readable output format.
+
+Tested with mxe (gcc 5.5.0, binutils 2.28). Obviously you need a shared build.
+mxe disables shared versions of bfd and iberty, but actually they work just
+fine. Simply remove this line
+```
+$(PKG)_BUILD_SHARED =
+```
+from their makefiles and change the options for `./configure`
+(`--enable-static` to `--disable-static`, `--disable-shared` to
+`--enable-shared`).
+
+[Source project](https://github.com/cloudwu/backtrace-mingw)
+
+References
+
+[Printing a Stack Trace with MinGW](http://theorangeduck.com/page/printing-stack-trace-mingw)
diff --git a/third_party/backtrace-mingw/backtrace.c b/third_party/backtrace-mingw/backtrace.c
new file mode 100644
index 0000000..b9ddcf4
--- /dev/null
+++ b/third_party/backtrace-mingw/backtrace.c
@@ -0,0 +1,457 @@
+/*
+	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
+
+	how to use: Call LoadLibraryA("backtrace.dll"); at beginning of your program .
+
+*/
+
+#define PACKAGE "backtrace"
+#define PACKAGE_VERSION "0.1"
+
+#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)
+
+#define BFD_ERR_OK          (0)
+#define BFD_ERR_OPEN_FAIL   (1)
+#define BFD_ERR_BAD_FORMAT  (2)
+#define BFD_ERR_NO_SYMBOLS  (3)
+#define BFD_ERR_READ_SYMBOL (4)
+
+static const char *const bfd_errors[] = {
+	"",
+	"(Failed to open bfd)",
+	"(Bad format)",
+	"(No symbols)",
+	"(Failed to read symbols)",
+};
+
+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 const char*
+exception_name(DWORD code)
+{
+	switch(code)
+	{
+		case EXCEPTION_ACCESS_VIOLATION:
+			return "access violation";
+		case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
+			return "array index out of bound";
+		case EXCEPTION_BREAKPOINT:
+			return "breakpoint reached";
+		case EXCEPTION_DATATYPE_MISALIGNMENT:
+			return "misaligned data access";
+		case EXCEPTION_FLT_DENORMAL_OPERAND:
+			return "operand had denormal value";
+		case EXCEPTION_FLT_DIVIDE_BY_ZERO:
+			return "floating-point division by zero";
+		case EXCEPTION_FLT_INEXACT_RESULT:
+			return "no decimal fraction representation for value";
+		case EXCEPTION_FLT_INVALID_OPERATION:
+			return "invalid floating-point operation";
+		case EXCEPTION_FLT_OVERFLOW:
+			return "floating-point overflow";
+		case EXCEPTION_FLT_STACK_CHECK:
+			return "floating-point stack corruption";
+		case EXCEPTION_FLT_UNDERFLOW:
+			return "floating-point underflow";
+		case EXCEPTION_ILLEGAL_INSTRUCTION:
+			return "illegal instruction";
+		case EXCEPTION_IN_PAGE_ERROR:
+			return "inaccessible page";
+		case EXCEPTION_INT_DIVIDE_BY_ZERO:
+			return "integer division by zero";
+		case EXCEPTION_INT_OVERFLOW:
+			return "integer overflow";
+		case EXCEPTION_INVALID_DISPOSITION:
+			return "documentation says this should never happen";
+		case EXCEPTION_NONCONTINUABLE_EXCEPTION:
+			return "can't continue after a noncontinuable exception";
+		case EXCEPTION_PRIV_INSTRUCTION:
+			return "attempted to execute a privileged instruction";
+		case EXCEPTION_SINGLE_STEP:
+			return "one instruction has been executed";
+		case EXCEPTION_STACK_OVERFLOW:
+			return "stack overflow";
+	}
+	return "unknown exception";
+}
+
+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, int *err)
+{
+	bc->handle = NULL;
+	bc->symbol = NULL;
+
+	bfd *b = bfd_openr(procname, 0);
+	if (!b) {
+		if(err) { *err = BFD_ERR_OPEN_FAIL; }
+		return 1;
+	}
+
+	if(!bfd_check_format(b, bfd_object)) {
+		bfd_close(b);
+		if(err) { *err = BFD_ERR_BAD_FORMAT; }
+		return 1;
+	}
+
+	if(!(bfd_get_file_flags(b) & HAS_SYMS)) {
+		bfd_close(b);
+		if(err) { *err = BFD_ERR_NO_SYMBOLS; }
+		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);
+			if(err) { *err = BFD_ERR_READ_SYMBOL; }
+			return 1;
+		}
+	}
+
+	bc->handle = b;
+	bc->symbol = symbol_table;
+
+	if(err) { *err = BFD_ERR_OK; }
+	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 bfd_set *set , const char *procname, int *err)
+{
+	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, err)) {
+		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;
+	int err = BFD_ERR_OK;
+	DWORD machine_type = 0;
+
+	STACKFRAME frame;
+	memset(&frame,0,sizeof(frame));
+
+#if defined(_M_IX86) || defined(__i386__)
+	machine_type = IMAGE_FILE_MACHINE_I386;
+	frame.AddrPC.Offset = context->Eip;
+	frame.AddrStack.Offset = context->Esp;
+	frame.AddrFrame.Offset = context->Ebp;
+#elif defined(_M_IX64) || defined(__amd64__)
+	machine_type = IMAGE_FILE_MACHINE_AMD64;
+	frame.AddrPC.Offset = context->Rip;
+	frame.AddrStack.Offset = context->Rsp;
+	frame.AddrFrame.Offset = context->Rbp;
+#else
+#error "Unsupported platform"
+	//IA-64 anybody?
+#endif
+	frame.AddrPC.Mode = AddrModeFlat;
+	frame.AddrStack.Mode = AddrModeFlat;
+	frame.AddrFrame.Mode = AddrModeFlat;
+
+	HANDLE process = GetCurrentProcess();
+	HANDLE thread = GetCurrentThread();
+
+	char symbol_buffer[sizeof(IMAGEHLP_SYMBOL) + 255];
+	char module_name_raw[MAX_PATH];
+
+	while(StackWalk64(machine_type,
+		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(set, module_name, &err);
+		}
+
+		const char * source_file = NULL;
+		const char * func = NULL;
+		unsigned line = 0;
+
+		if (bc) {
+			find(bc,frame.AddrPC.Offset,&source_file,&func,&line);
+		}
+
+		if (source_file == NULL) {
+			DWORD dummy = 0;
+			if (SymGetSymFromAddr(process, frame.AddrPC.Offset, &dummy, symbol)) {
+				source_file = symbol->Name;
+			}
+			else {
+				source_file = "[unknown file]";
+			}
+		}
+		if (func == NULL) {
+			output_print(ob,"0x%08x from %s in %s %s \n",
+				frame.AddrPC.Offset,
+				module_name,
+				source_file,
+				bfd_errors[err]);
+		}
+		else {
+			output_print(ob,"0x%08x in %s at %s:%d from %s \n",
+				frame.AddrPC.Offset,
+				func,
+				source_file,
+				line,
+				module_name);
+		}
+	}
+}
+
+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();
+		PEXCEPTION_RECORD rec = info->ExceptionRecord;
+		output_print(&ob,"Unhandled exception occured at 0x%08x: %s.\n",
+			rec->ExceptionAddress,
+			exception_name(rec->ExceptionCode)
+		);
+		if (rec->ExceptionCode == EXCEPTION_ACCESS_VIOLATION || rec->ExceptionCode == EXCEPTION_IN_PAGE_ERROR)
+			if (rec->NumberParameters >= 2) {
+				const char *op =
+					rec->ExceptionInformation[0] == 0 ? "read" :
+					rec->ExceptionInformation[0] == 1 ? "written" : "executed";
+				output_print(&ob, "The data at memory address 0x%08x could not be %s.\n",
+					rec->ExceptionInformation[1], op);
+			}
+		struct bfd_set *set = calloc(1,sizeof(*set));
+		_backtrace(&ob , set , 128 , info->ContextRecord);
+		release_set(set);
+
+		SymCleanup(GetCurrentProcess());
+	}
+
+	fputs(g_output , stderr);
+
+	return EXCEPTION_CONTINUE_SEARCH;
+}
+
+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;
+	}
+}
+
+int
+__printf__(const char * format, ...) {
+	int value;
+	va_list arg;
+	va_start(arg, format);
+	value = vprintf ( format, arg );
+	va_end(arg);
+	return value;
+}
+
+__declspec(dllexport) BOOL WINAPI
+DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved)
+{
+	switch (dwReason) {
+	case DLL_PROCESS_ATTACH:
+		backtrace_register();
+		break;
+	case DLL_PROCESS_DETACH:
+		backtrace_unregister();
+		break;
+	}
+	return TRUE;
+}
+
diff --git a/third_party/backtrace-mingw/test.c b/third_party/backtrace-mingw/test.c
new file mode 100644
index 0000000..731129f
--- /dev/null
+++ b/third_party/backtrace-mingw/test.c
@@ -0,0 +1,23 @@
+#include <windows.h>
+
+static void
+foo()
+{
+	int *f=NULL;
+	*f = 0;
+}
+
+static void
+bar()
+{
+	foo();
+}
+
+int
+main()
+{
+	LoadLibraryA("backtrace.dll");
+	bar();
+
+	return 0;
+}
-- 
cgit v1.2.3