builtin: add libbacktrace as option for generating stack traces (#14277)
parent
4242e7610f
commit
76a7354506
|
@ -0,0 +1,4 @@
|
||||||
|
The libbacktrace source is distributed here as an amalgamation (https://sqlite.org/amalgamation.html).
|
||||||
|
This means that, rather than mirroring the entire libbacktrace repo here, most of the source code is
|
||||||
|
packaged into one C file while other platform-specific code are into their respective C files which
|
||||||
|
is much easier to handle.
|
|
@ -0,0 +1,111 @@
|
||||||
|
/* Copyright (C) 2012-2021 Free Software Foundation, Inc.
|
||||||
|
Written by Ian Lance Taylor, Google.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
(1) Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
(2) Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in
|
||||||
|
the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
|
||||||
|
(3) The name of the author may not be used to
|
||||||
|
endorse or promote products derived from this software without
|
||||||
|
specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGE. */
|
||||||
|
|
||||||
|
#define HAVE_ATOMIC_FUNCTIONS 1
|
||||||
|
#define HAVE_CLOCK_GETTIME 1
|
||||||
|
#define HAVE_DECL_GETPAGESIZE 0
|
||||||
|
#define HAVE_DECL_STRNLEN 1
|
||||||
|
#define HAVE_DL_ITERATE_PHDR 1
|
||||||
|
#define HAVE_GETIPINFO 1
|
||||||
|
#define HAVE_LSTAT 1
|
||||||
|
#define HAVE_READLINK 1
|
||||||
|
#define HAVE_SYNC_FUNCTIONS 1
|
||||||
|
|
||||||
|
#define HAVE_DLFCN_H 1
|
||||||
|
#define HAVE_INTTYPES_H 1
|
||||||
|
#define HAVE_LINK_H 1
|
||||||
|
#define HAVE_MEMORY_H 1
|
||||||
|
#define HAVE_STDINT_H 1
|
||||||
|
#define HAVE_STDLIB_H 1
|
||||||
|
#define HAVE_STRINGS_H 1
|
||||||
|
#define HAVE_STRING_H 1
|
||||||
|
#define HAVE_UNISTD_H 1
|
||||||
|
#define STDC_HEADERS 1
|
||||||
|
#include <stdint.h>
|
||||||
|
#if UINTPTR_MAX == 0xFFFFFFFF
|
||||||
|
#define BACKTRACE_ELF_SIZE 32
|
||||||
|
#define BACKTRACE_XCOFF_SIZE 32
|
||||||
|
#elif UINTPTR_MAX == 0xFFFFFFFFFFFFFFFFu
|
||||||
|
#define BACKTRACE_ELF_SIZE 64
|
||||||
|
#define BACKTRACE_XCOFF_SIZE 64
|
||||||
|
#endif
|
||||||
|
#ifdef __TINYC__
|
||||||
|
#undef HAVE_ATOMIC_FUNCTIONS
|
||||||
|
#undef HAVE_SYNC_FUNCTIONS
|
||||||
|
#endif
|
||||||
|
#ifndef _WIN32
|
||||||
|
#define HAVE_FCNTL 1
|
||||||
|
#endif
|
||||||
|
#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
|
||||||
|
#define HAVE_KERN_PROC 1
|
||||||
|
#define HAVE_KERN_PROC_ARGS 1
|
||||||
|
#endif
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#define HAVE_MACH_O_DYLD_H 1
|
||||||
|
#endif
|
||||||
|
#ifndef _ALL_SOURCE
|
||||||
|
#define _ALL_SOURCE 1
|
||||||
|
#endif
|
||||||
|
#ifndef _GNU_SOURCE
|
||||||
|
#define _GNU_SOURCE 1
|
||||||
|
#undef HAVE_DL_ITERATE_PHDR
|
||||||
|
#endif
|
||||||
|
#ifndef _POSIX_PTHREAD_SEMANTICS
|
||||||
|
#define _POSIX_PTHREAD_SEMANTICS 1
|
||||||
|
#endif
|
||||||
|
#ifndef _TANDEM_SOURCE
|
||||||
|
#define _TANDEM_SOURCE 1
|
||||||
|
#endif
|
||||||
|
#ifndef __EXTENSIONS__
|
||||||
|
#define __EXTENSIONS__ 1
|
||||||
|
#endif
|
||||||
|
#ifndef _DARWIN_USE_64_BIT_INODE
|
||||||
|
#define _DARWIN_USE_64_BIT_INODE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BACKTRACE_SUPPORTED 1
|
||||||
|
#define BACKTRACE_USES_MALLOC 1
|
||||||
|
#define BACKTRACE_SUPPORTS_THREADS 1
|
||||||
|
#define BACKTRACE_SUPPORTS_DATA 0
|
||||||
|
|
||||||
|
#if __TINYC__
|
||||||
|
#undef BACKTRACE_SUPPORTED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "base.c"
|
||||||
|
|
||||||
|
#if defined(__linux__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
|
||||||
|
#include "linux.c"
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#include "darwin.c"
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
#include "windows.c"
|
||||||
|
#endif
|
|
@ -0,0 +1,189 @@
|
||||||
|
/* backtrace.h -- Public header file for stack backtrace library.
|
||||||
|
Copyright (C) 2012-2021 Free Software Foundation, Inc.
|
||||||
|
Written by Ian Lance Taylor, Google.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
(1) Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
(2) Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in
|
||||||
|
the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
|
||||||
|
(3) The name of the author may not be used to
|
||||||
|
endorse or promote products derived from this software without
|
||||||
|
specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGE. */
|
||||||
|
|
||||||
|
#ifndef BACKTRACE_H
|
||||||
|
#define BACKTRACE_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* The backtrace state. This struct is intentionally not defined in
|
||||||
|
the public interface. */
|
||||||
|
|
||||||
|
struct backtrace_state;
|
||||||
|
|
||||||
|
/* The type of the error callback argument to backtrace functions.
|
||||||
|
This function, if not NULL, will be called for certain error cases.
|
||||||
|
The DATA argument is passed to the function that calls this one.
|
||||||
|
The MSG argument is an error message. The ERRNUM argument, if
|
||||||
|
greater than 0, holds an errno value. The MSG buffer may become
|
||||||
|
invalid after this function returns.
|
||||||
|
|
||||||
|
As a special case, the ERRNUM argument will be passed as -1 if no
|
||||||
|
debug info can be found for the executable, or if the debug info
|
||||||
|
exists but has an unsupported version, but the function requires
|
||||||
|
debug info (e.g., backtrace_full, backtrace_pcinfo). The MSG in
|
||||||
|
this case will be something along the lines of "no debug info".
|
||||||
|
Similarly, ERRNUM will be passed as -1 if there is no symbol table,
|
||||||
|
but the function requires a symbol table (e.g., backtrace_syminfo).
|
||||||
|
This may be used as a signal that some other approach should be
|
||||||
|
tried. */
|
||||||
|
|
||||||
|
typedef void (*backtrace_error_callback) (void *data, const char *msg,
|
||||||
|
int errnum);
|
||||||
|
|
||||||
|
/* Create state information for the backtrace routines. This must be
|
||||||
|
called before any of the other routines, and its return value must
|
||||||
|
be passed to all of the other routines. FILENAME is the path name
|
||||||
|
of the executable file; if it is NULL the library will try
|
||||||
|
system-specific path names. If not NULL, FILENAME must point to a
|
||||||
|
permanent buffer. If THREADED is non-zero the state may be
|
||||||
|
accessed by multiple threads simultaneously, and the library will
|
||||||
|
use appropriate atomic operations. If THREADED is zero the state
|
||||||
|
may only be accessed by one thread at a time. This returns a state
|
||||||
|
pointer on success, NULL on error. If an error occurs, this will
|
||||||
|
call the ERROR_CALLBACK routine.
|
||||||
|
|
||||||
|
Calling this function allocates resources that cannot be freed.
|
||||||
|
There is no backtrace_free_state function. The state is used to
|
||||||
|
cache information that is expensive to recompute. Programs are
|
||||||
|
expected to call this function at most once and to save the return
|
||||||
|
value for all later calls to backtrace functions. */
|
||||||
|
|
||||||
|
extern struct backtrace_state *backtrace_create_state (
|
||||||
|
const char *filename, int threaded,
|
||||||
|
backtrace_error_callback error_callback, void *data);
|
||||||
|
|
||||||
|
/* The type of the callback argument to the backtrace_full function.
|
||||||
|
DATA is the argument passed to backtrace_full. PC is the program
|
||||||
|
counter. FILENAME is the name of the file containing PC, or NULL
|
||||||
|
if not available. LINENO is the line number in FILENAME containing
|
||||||
|
PC, or 0 if not available. FUNCTION is the name of the function
|
||||||
|
containing PC, or NULL if not available. This should return 0 to
|
||||||
|
continuing tracing. The FILENAME and FUNCTION buffers may become
|
||||||
|
invalid after this function returns. */
|
||||||
|
|
||||||
|
typedef int (*backtrace_full_callback) (void *data, uintptr_t pc,
|
||||||
|
const char *filename, int lineno,
|
||||||
|
const char *function);
|
||||||
|
|
||||||
|
/* Get a full stack backtrace. SKIP is the number of frames to skip;
|
||||||
|
passing 0 will start the trace with the function calling
|
||||||
|
backtrace_full. DATA is passed to the callback routine. If any
|
||||||
|
call to CALLBACK returns a non-zero value, the stack backtrace
|
||||||
|
stops, and backtrace returns that value; this may be used to limit
|
||||||
|
the number of stack frames desired. If all calls to CALLBACK
|
||||||
|
return 0, backtrace returns 0. The backtrace_full function will
|
||||||
|
make at least one call to either CALLBACK or ERROR_CALLBACK. This
|
||||||
|
function requires debug info for the executable. */
|
||||||
|
|
||||||
|
extern int backtrace_full (struct backtrace_state *state, int skip,
|
||||||
|
backtrace_full_callback callback,
|
||||||
|
backtrace_error_callback error_callback,
|
||||||
|
void *data);
|
||||||
|
|
||||||
|
/* The type of the callback argument to the backtrace_simple function.
|
||||||
|
DATA is the argument passed to simple_backtrace. PC is the program
|
||||||
|
counter. This should return 0 to continue tracing. */
|
||||||
|
|
||||||
|
typedef int (*backtrace_simple_callback) (void *data, uintptr_t pc);
|
||||||
|
|
||||||
|
/* Get a simple backtrace. SKIP is the number of frames to skip, as
|
||||||
|
in backtrace. DATA is passed to the callback routine. If any call
|
||||||
|
to CALLBACK returns a non-zero value, the stack backtrace stops,
|
||||||
|
and backtrace_simple returns that value. Otherwise
|
||||||
|
backtrace_simple returns 0. The backtrace_simple function will
|
||||||
|
make at least one call to either CALLBACK or ERROR_CALLBACK. This
|
||||||
|
function does not require any debug info for the executable. */
|
||||||
|
|
||||||
|
extern int backtrace_simple (struct backtrace_state *state, int skip,
|
||||||
|
backtrace_simple_callback callback,
|
||||||
|
backtrace_error_callback error_callback,
|
||||||
|
void *data);
|
||||||
|
|
||||||
|
/* Print the current backtrace in a user readable format to a FILE.
|
||||||
|
SKIP is the number of frames to skip, as in backtrace_full. Any
|
||||||
|
error messages are printed to stderr. This function requires debug
|
||||||
|
info for the executable. */
|
||||||
|
|
||||||
|
extern void backtrace_print (struct backtrace_state *state, int skip, FILE *);
|
||||||
|
|
||||||
|
/* Given PC, a program counter in the current program, call the
|
||||||
|
callback function with filename, line number, and function name
|
||||||
|
information. This will normally call the callback function exactly
|
||||||
|
once. However, if the PC happens to describe an inlined call, and
|
||||||
|
the debugging information contains the necessary information, then
|
||||||
|
this may call the callback function multiple times. This will make
|
||||||
|
at least one call to either CALLBACK or ERROR_CALLBACK. This
|
||||||
|
returns the first non-zero value returned by CALLBACK, or 0. */
|
||||||
|
|
||||||
|
extern int backtrace_pcinfo (struct backtrace_state *state, uintptr_t pc,
|
||||||
|
backtrace_full_callback callback,
|
||||||
|
backtrace_error_callback error_callback,
|
||||||
|
void *data);
|
||||||
|
|
||||||
|
/* The type of the callback argument to backtrace_syminfo. DATA and
|
||||||
|
PC are the arguments passed to backtrace_syminfo. SYMNAME is the
|
||||||
|
name of the symbol for the corresponding code. SYMVAL is the
|
||||||
|
value and SYMSIZE is the size of the symbol. SYMNAME will be NULL
|
||||||
|
if no error occurred but the symbol could not be found. */
|
||||||
|
|
||||||
|
typedef void (*backtrace_syminfo_callback) (void *data, uintptr_t pc,
|
||||||
|
const char *symname,
|
||||||
|
uintptr_t symval,
|
||||||
|
uintptr_t symsize);
|
||||||
|
|
||||||
|
/* Given ADDR, an address or program counter in the current program,
|
||||||
|
call the callback information with the symbol name and value
|
||||||
|
describing the function or variable in which ADDR may be found.
|
||||||
|
This will call either CALLBACK or ERROR_CALLBACK exactly once.
|
||||||
|
This returns 1 on success, 0 on failure. This function requires
|
||||||
|
the symbol table but does not require the debug info. Note that if
|
||||||
|
the symbol table is present but ADDR could not be found in the
|
||||||
|
table, CALLBACK will be called with a NULL SYMNAME argument.
|
||||||
|
Returns 1 on success, 0 on error. */
|
||||||
|
|
||||||
|
extern int backtrace_syminfo (struct backtrace_state *state, uintptr_t addr,
|
||||||
|
backtrace_syminfo_callback callback,
|
||||||
|
backtrace_error_callback error_callback,
|
||||||
|
void *data);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* End extern "C". */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,818 @@
|
||||||
|
// pecoff.c:
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t machine;
|
||||||
|
uint16_t number_of_sections;
|
||||||
|
uint32_t time_date_stamp;
|
||||||
|
uint32_t pointer_to_symbol_table;
|
||||||
|
uint32_t number_of_symbols;
|
||||||
|
uint16_t size_of_optional_header;
|
||||||
|
uint16_t characteristics;
|
||||||
|
} b_coff_file_header;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t magic;
|
||||||
|
uint8_t major_linker_version;
|
||||||
|
uint8_t minor_linker_version;
|
||||||
|
uint32_t size_of_code;
|
||||||
|
uint32_t size_of_initialized_data;
|
||||||
|
uint32_t size_of_uninitialized_data;
|
||||||
|
uint32_t address_of_entry_point;
|
||||||
|
uint32_t base_of_code;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
uint32_t base_of_data;
|
||||||
|
uint32_t image_base;
|
||||||
|
} pe;
|
||||||
|
struct {
|
||||||
|
uint64_t image_base;
|
||||||
|
} pep;
|
||||||
|
} u;
|
||||||
|
} b_coff_optional_header;
|
||||||
|
|
||||||
|
#define PE_MAGIC 0x10b
|
||||||
|
#define PEP_MAGIC 0x20b
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char name[8];
|
||||||
|
uint32_t virtual_size;
|
||||||
|
uint32_t virtual_address;
|
||||||
|
uint32_t size_of_raw_data;
|
||||||
|
uint32_t pointer_to_raw_data;
|
||||||
|
uint32_t pointer_to_relocations;
|
||||||
|
uint32_t pointer_to_line_numbers;
|
||||||
|
uint16_t number_of_relocations;
|
||||||
|
uint16_t number_of_line_numbers;
|
||||||
|
uint32_t characteristics;
|
||||||
|
} b_coff_section_header;
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
char short_name[8];
|
||||||
|
struct {
|
||||||
|
unsigned char zeroes[4];
|
||||||
|
unsigned char off[4];
|
||||||
|
} long_name;
|
||||||
|
} b_coff_name;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
b_coff_name name;
|
||||||
|
unsigned char value[4];
|
||||||
|
unsigned char section_number[2];
|
||||||
|
unsigned char type[2];
|
||||||
|
unsigned char storage_class;
|
||||||
|
unsigned char number_of_aux_symbols;
|
||||||
|
} b_coff_external_symbol;
|
||||||
|
|
||||||
|
#define N_TBSHFT 4
|
||||||
|
#define IMAGE_SYM_DTYPE_FUNCTION 2
|
||||||
|
|
||||||
|
#define SYM_SZ 18
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *name;
|
||||||
|
uint32_t value;
|
||||||
|
int16_t sec;
|
||||||
|
uint16_t type;
|
||||||
|
uint16_t sc;
|
||||||
|
} b_coff_internal_symbol;
|
||||||
|
|
||||||
|
static const char *const debug_section_names[DEBUG_MAX] = {
|
||||||
|
".debug_info", ".debug_line", ".debug_abbrev",
|
||||||
|
".debug_ranges", ".debug_str", ".debug_addr",
|
||||||
|
".debug_str_offsets", ".debug_line_str", ".debug_rnglists"};
|
||||||
|
|
||||||
|
struct debug_section_info {
|
||||||
|
off_t offset;
|
||||||
|
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct coff_symbol {
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
uintptr_t address;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct coff_syminfo_data {
|
||||||
|
struct coff_syminfo_data *next;
|
||||||
|
|
||||||
|
struct coff_symbol *symbols;
|
||||||
|
|
||||||
|
size_t count;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int coff_nodebug(struct backtrace_state *state ATTRIBUTE_UNUSED,
|
||||||
|
uintptr_t pc ATTRIBUTE_UNUSED,
|
||||||
|
backtrace_full_callback callback ATTRIBUTE_UNUSED,
|
||||||
|
backtrace_error_callback error_callback, void *data) {
|
||||||
|
error_callback(data, "no debug info in PE/COFF executable", -1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void coff_nosyms(struct backtrace_state *state ATTRIBUTE_UNUSED,
|
||||||
|
uintptr_t addr ATTRIBUTE_UNUSED,
|
||||||
|
backtrace_syminfo_callback callback ATTRIBUTE_UNUSED,
|
||||||
|
backtrace_error_callback error_callback, void *data) {
|
||||||
|
error_callback(data, "no symbol table in PE/COFF executable", -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t coff_read4(const unsigned char *p) {
|
||||||
|
uint32_t res;
|
||||||
|
|
||||||
|
memcpy(&res, p, 4);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t coff_read2(const unsigned char *p) {
|
||||||
|
uint16_t res;
|
||||||
|
|
||||||
|
memcpy(&res, p, sizeof(res));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t coff_short_name_len(const char *name) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
if (name[i] == 0) return i;
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coff_short_name_eq(const char *name, const char *cname) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
if (name[i] != cname[i]) return 0;
|
||||||
|
if (name[i] == 0) return 1;
|
||||||
|
}
|
||||||
|
return name[8] == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coff_long_name_eq(const char *name, unsigned int off,
|
||||||
|
struct backtrace_view *str_view) {
|
||||||
|
if (off >= str_view->len) return 0;
|
||||||
|
return strcmp(name, (const char *)str_view->data + off) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coff_symbol_compare(const void *v1, const void *v2) {
|
||||||
|
const struct coff_symbol *e1 = (const struct coff_symbol *)v1;
|
||||||
|
const struct coff_symbol *e2 = (const struct coff_symbol *)v2;
|
||||||
|
|
||||||
|
if (e1->address < e2->address)
|
||||||
|
return -1;
|
||||||
|
else if (e1->address > e2->address)
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coff_expand_symbol(b_coff_internal_symbol *isym,
|
||||||
|
const b_coff_external_symbol *sym,
|
||||||
|
uint16_t sects_num, const unsigned char *strtab,
|
||||||
|
size_t strtab_size) {
|
||||||
|
isym->type = coff_read2(sym->type);
|
||||||
|
isym->sec = coff_read2(sym->section_number);
|
||||||
|
isym->sc = sym->storage_class;
|
||||||
|
|
||||||
|
if (isym->sec > 0 && (uint16_t)isym->sec > sects_num) return -1;
|
||||||
|
if (sym->name.short_name[0] != 0)
|
||||||
|
isym->name = sym->name.short_name;
|
||||||
|
else {
|
||||||
|
uint32_t off = coff_read4(sym->name.long_name.off);
|
||||||
|
|
||||||
|
if (off >= strtab_size) return -1;
|
||||||
|
isym->name = (const char *)strtab + off;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coff_is_function_symbol(const b_coff_internal_symbol *isym) {
|
||||||
|
return (isym->type >> N_TBSHFT) == IMAGE_SYM_DTYPE_FUNCTION && isym->sec > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coff_initialize_syminfo(
|
||||||
|
struct backtrace_state *state, uintptr_t base_address, int is_64,
|
||||||
|
const b_coff_section_header *sects, size_t sects_num,
|
||||||
|
const b_coff_external_symbol *syms, size_t syms_size,
|
||||||
|
const unsigned char *strtab, size_t strtab_size,
|
||||||
|
backtrace_error_callback error_callback, void *data,
|
||||||
|
struct coff_syminfo_data *sdata) {
|
||||||
|
size_t syms_count;
|
||||||
|
char *coff_symstr;
|
||||||
|
size_t coff_symstr_len;
|
||||||
|
size_t coff_symbol_count;
|
||||||
|
size_t coff_symbol_size;
|
||||||
|
struct coff_symbol *coff_symbols;
|
||||||
|
struct coff_symbol *coff_sym;
|
||||||
|
char *coff_str;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
syms_count = syms_size / SYM_SZ;
|
||||||
|
|
||||||
|
coff_symbol_count = 0;
|
||||||
|
coff_symstr_len = 0;
|
||||||
|
for (i = 0; i < syms_count; ++i) {
|
||||||
|
const b_coff_external_symbol *asym = &syms[i];
|
||||||
|
b_coff_internal_symbol isym;
|
||||||
|
|
||||||
|
if (coff_expand_symbol(&isym, asym, sects_num, strtab, strtab_size) < 0) {
|
||||||
|
error_callback(data, "invalid section or offset in coff symbol", 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (coff_is_function_symbol(&isym)) {
|
||||||
|
++coff_symbol_count;
|
||||||
|
if (asym->name.short_name[0] != 0)
|
||||||
|
coff_symstr_len += coff_short_name_len(asym->name.short_name) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += asym->number_of_aux_symbols;
|
||||||
|
}
|
||||||
|
|
||||||
|
coff_symbol_size = (coff_symbol_count + 1) * sizeof(struct coff_symbol);
|
||||||
|
coff_symbols = ((struct coff_symbol *)backtrace_alloc(state, coff_symbol_size,
|
||||||
|
error_callback, data));
|
||||||
|
if (coff_symbols == NULL) return 0;
|
||||||
|
|
||||||
|
if (coff_symstr_len > 0) {
|
||||||
|
coff_symstr =
|
||||||
|
((char *)backtrace_alloc(state, coff_symstr_len, error_callback, data));
|
||||||
|
if (coff_symstr == NULL) {
|
||||||
|
backtrace_free(state, coff_symbols, coff_symbol_size, error_callback,
|
||||||
|
data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
coff_symstr = NULL;
|
||||||
|
|
||||||
|
coff_sym = coff_symbols;
|
||||||
|
coff_str = coff_symstr;
|
||||||
|
for (i = 0; i < syms_count; ++i) {
|
||||||
|
const b_coff_external_symbol *asym = &syms[i];
|
||||||
|
b_coff_internal_symbol isym;
|
||||||
|
|
||||||
|
if (coff_expand_symbol(&isym, asym, sects_num, strtab, strtab_size)) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
if (coff_is_function_symbol(&isym)) {
|
||||||
|
const char *name;
|
||||||
|
int16_t secnum;
|
||||||
|
|
||||||
|
if (asym->name.short_name[0] != 0) {
|
||||||
|
size_t len = coff_short_name_len(isym.name);
|
||||||
|
name = coff_str;
|
||||||
|
memcpy(coff_str, isym.name, len);
|
||||||
|
coff_str[len] = 0;
|
||||||
|
coff_str += len + 1;
|
||||||
|
} else
|
||||||
|
name = isym.name;
|
||||||
|
|
||||||
|
if (!is_64) {
|
||||||
|
if (name[0] == '_') name++;
|
||||||
|
}
|
||||||
|
|
||||||
|
secnum = coff_read2(asym->section_number);
|
||||||
|
|
||||||
|
coff_sym->name = name;
|
||||||
|
coff_sym->address = (coff_read4(asym->value) +
|
||||||
|
sects[secnum - 1].virtual_address + base_address);
|
||||||
|
coff_sym++;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += asym->number_of_aux_symbols;
|
||||||
|
}
|
||||||
|
|
||||||
|
coff_sym->name = NULL;
|
||||||
|
coff_sym->address = -1;
|
||||||
|
|
||||||
|
backtrace_qsort(coff_symbols, coff_symbol_count, sizeof(struct coff_symbol),
|
||||||
|
coff_symbol_compare);
|
||||||
|
|
||||||
|
sdata->next = NULL;
|
||||||
|
sdata->symbols = coff_symbols;
|
||||||
|
sdata->count = coff_symbol_count;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void coff_add_syminfo_data(struct backtrace_state *state,
|
||||||
|
struct coff_syminfo_data *sdata) {
|
||||||
|
if (!state->threaded) {
|
||||||
|
struct coff_syminfo_data **pp;
|
||||||
|
|
||||||
|
for (pp = (struct coff_syminfo_data **)(void *)&state->syminfo_data;
|
||||||
|
*pp != NULL; pp = &(*pp)->next)
|
||||||
|
;
|
||||||
|
*pp = sdata;
|
||||||
|
} else {
|
||||||
|
while (1) {
|
||||||
|
struct coff_syminfo_data **pp;
|
||||||
|
|
||||||
|
pp = (struct coff_syminfo_data **)(void *)&state->syminfo_data;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
struct coff_syminfo_data *p;
|
||||||
|
|
||||||
|
p = backtrace_atomic_load_pointer(pp);
|
||||||
|
|
||||||
|
if (p == NULL) break;
|
||||||
|
|
||||||
|
pp = &p->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (__sync_bool_compare_and_swap(pp, NULL, sdata)) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coff_symbol_search(const void *vkey, const void *ventry) {
|
||||||
|
const uintptr_t *key = (const uintptr_t *)vkey;
|
||||||
|
const struct coff_symbol *entry = (const struct coff_symbol *)ventry;
|
||||||
|
uintptr_t addr;
|
||||||
|
|
||||||
|
addr = *key;
|
||||||
|
if (addr < entry->address)
|
||||||
|
return -1;
|
||||||
|
else if (addr >= entry[1].address)
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void coff_syminfo(
|
||||||
|
struct backtrace_state *state, uintptr_t addr,
|
||||||
|
backtrace_syminfo_callback callback,
|
||||||
|
backtrace_error_callback error_callback ATTRIBUTE_UNUSED, void *data) {
|
||||||
|
struct coff_syminfo_data *sdata;
|
||||||
|
struct coff_symbol *sym = NULL;
|
||||||
|
|
||||||
|
if (!state->threaded) {
|
||||||
|
for (sdata = (struct coff_syminfo_data *)state->syminfo_data; sdata != NULL;
|
||||||
|
sdata = sdata->next) {
|
||||||
|
sym = ((struct coff_symbol *)bsearch(&addr, sdata->symbols, sdata->count,
|
||||||
|
sizeof(struct coff_symbol),
|
||||||
|
coff_symbol_search));
|
||||||
|
if (sym != NULL) break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
struct coff_syminfo_data **pp;
|
||||||
|
|
||||||
|
pp = (struct coff_syminfo_data **)(void *)&state->syminfo_data;
|
||||||
|
while (1) {
|
||||||
|
sdata = backtrace_atomic_load_pointer(pp);
|
||||||
|
if (sdata == NULL) break;
|
||||||
|
|
||||||
|
sym = ((struct coff_symbol *)bsearch(&addr, sdata->symbols, sdata->count,
|
||||||
|
sizeof(struct coff_symbol),
|
||||||
|
coff_symbol_search));
|
||||||
|
if (sym != NULL) break;
|
||||||
|
|
||||||
|
pp = &sdata->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sym == NULL)
|
||||||
|
callback(data, addr, NULL, 0, 0);
|
||||||
|
else
|
||||||
|
callback(data, addr, sym->name, sym->address, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coff_add(struct backtrace_state *state, int descriptor,
|
||||||
|
backtrace_error_callback error_callback, void *data,
|
||||||
|
fileline *fileline_fn, int *found_sym, int *found_dwarf) {
|
||||||
|
struct backtrace_view fhdr_view;
|
||||||
|
off_t fhdr_off;
|
||||||
|
int magic_ok;
|
||||||
|
b_coff_file_header fhdr;
|
||||||
|
off_t opt_sects_off;
|
||||||
|
size_t opt_sects_size;
|
||||||
|
unsigned int sects_num;
|
||||||
|
struct backtrace_view sects_view;
|
||||||
|
int sects_view_valid;
|
||||||
|
const b_coff_optional_header *opt_hdr;
|
||||||
|
const b_coff_section_header *sects;
|
||||||
|
struct backtrace_view str_view;
|
||||||
|
int str_view_valid;
|
||||||
|
size_t str_size;
|
||||||
|
off_t str_off;
|
||||||
|
struct backtrace_view syms_view;
|
||||||
|
off_t syms_off;
|
||||||
|
size_t syms_size;
|
||||||
|
int syms_view_valid;
|
||||||
|
unsigned int syms_num;
|
||||||
|
unsigned int i;
|
||||||
|
struct debug_section_info sections[DEBUG_MAX];
|
||||||
|
off_t min_offset;
|
||||||
|
off_t max_offset;
|
||||||
|
struct backtrace_view debug_view;
|
||||||
|
int debug_view_valid;
|
||||||
|
int is_64;
|
||||||
|
uintptr_t image_base;
|
||||||
|
struct dwarf_sections dwarf_sections;
|
||||||
|
|
||||||
|
*found_sym = 0;
|
||||||
|
*found_dwarf = 0;
|
||||||
|
|
||||||
|
sects_view_valid = 0;
|
||||||
|
syms_view_valid = 0;
|
||||||
|
str_view_valid = 0;
|
||||||
|
debug_view_valid = 0;
|
||||||
|
|
||||||
|
if (!backtrace_get_view(state, descriptor, 0, 0x40, error_callback, data,
|
||||||
|
&fhdr_view))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
{
|
||||||
|
const unsigned char *vptr = fhdr_view.data;
|
||||||
|
|
||||||
|
if (vptr[0] == 'M' && vptr[1] == 'Z')
|
||||||
|
fhdr_off = coff_read4(vptr + 0x3c);
|
||||||
|
else
|
||||||
|
fhdr_off = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
backtrace_release_view(state, &fhdr_view, error_callback, data);
|
||||||
|
|
||||||
|
if (!backtrace_get_view(state, descriptor, fhdr_off,
|
||||||
|
sizeof(b_coff_file_header) + 4, error_callback, data,
|
||||||
|
&fhdr_view))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if (fhdr_off != 0) {
|
||||||
|
const char *magic = (const char *)fhdr_view.data;
|
||||||
|
magic_ok = memcmp(magic, "PE\0", 4) == 0;
|
||||||
|
fhdr_off += 4;
|
||||||
|
|
||||||
|
memcpy(&fhdr, fhdr_view.data + 4, sizeof fhdr);
|
||||||
|
} else {
|
||||||
|
memcpy(&fhdr, fhdr_view.data, sizeof fhdr);
|
||||||
|
|
||||||
|
magic_ok = 0;
|
||||||
|
}
|
||||||
|
backtrace_release_view(state, &fhdr_view, error_callback, data);
|
||||||
|
|
||||||
|
if (!magic_ok) {
|
||||||
|
error_callback(data, "executable file is not COFF", 0);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
sects_num = fhdr.number_of_sections;
|
||||||
|
syms_num = fhdr.number_of_symbols;
|
||||||
|
|
||||||
|
opt_sects_off = fhdr_off + sizeof(fhdr);
|
||||||
|
opt_sects_size = (fhdr.size_of_optional_header +
|
||||||
|
sects_num * sizeof(b_coff_section_header));
|
||||||
|
|
||||||
|
if (!backtrace_get_view(state, descriptor, opt_sects_off, opt_sects_size,
|
||||||
|
error_callback, data, §s_view))
|
||||||
|
goto fail;
|
||||||
|
sects_view_valid = 1;
|
||||||
|
opt_hdr = (const b_coff_optional_header *)sects_view.data;
|
||||||
|
sects = (const b_coff_section_header *)(sects_view.data +
|
||||||
|
fhdr.size_of_optional_header);
|
||||||
|
|
||||||
|
is_64 = 0;
|
||||||
|
if (fhdr.size_of_optional_header > sizeof(*opt_hdr)) {
|
||||||
|
if (opt_hdr->magic == PE_MAGIC)
|
||||||
|
image_base = opt_hdr->u.pe.image_base;
|
||||||
|
else if (opt_hdr->magic == PEP_MAGIC) {
|
||||||
|
image_base = opt_hdr->u.pep.image_base;
|
||||||
|
is_64 = 1;
|
||||||
|
} else {
|
||||||
|
error_callback(data, "bad magic in PE optional header", 0);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
image_base = 0;
|
||||||
|
|
||||||
|
if (fhdr.pointer_to_symbol_table == 0) {
|
||||||
|
str_off = 0;
|
||||||
|
str_size = 0;
|
||||||
|
syms_num = 0;
|
||||||
|
syms_size = 0;
|
||||||
|
} else {
|
||||||
|
syms_off = fhdr.pointer_to_symbol_table;
|
||||||
|
syms_size = syms_num * SYM_SZ;
|
||||||
|
|
||||||
|
if (!backtrace_get_view(state, descriptor, syms_off, syms_size + 4,
|
||||||
|
error_callback, data, &syms_view))
|
||||||
|
goto fail;
|
||||||
|
syms_view_valid = 1;
|
||||||
|
|
||||||
|
str_size = coff_read4(syms_view.data + syms_size);
|
||||||
|
|
||||||
|
str_off = syms_off + syms_size;
|
||||||
|
|
||||||
|
if (str_size > 4) {
|
||||||
|
if (!backtrace_get_view(state, descriptor, str_off, str_size,
|
||||||
|
error_callback, data, &str_view))
|
||||||
|
goto fail;
|
||||||
|
str_view_valid = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(sections, 0, sizeof sections);
|
||||||
|
|
||||||
|
for (i = 0; i < sects_num; ++i) {
|
||||||
|
const b_coff_section_header *s = sects + i;
|
||||||
|
unsigned int str_off;
|
||||||
|
int j;
|
||||||
|
|
||||||
|
if (s->name[0] == '/') {
|
||||||
|
str_off = atoi(s->name + 1);
|
||||||
|
} else
|
||||||
|
str_off = 0;
|
||||||
|
|
||||||
|
for (j = 0; j < (int)DEBUG_MAX; ++j) {
|
||||||
|
const char *dbg_name = debug_section_names[j];
|
||||||
|
int match;
|
||||||
|
|
||||||
|
if (str_off != 0)
|
||||||
|
match = coff_long_name_eq(dbg_name, str_off, &str_view);
|
||||||
|
else
|
||||||
|
match = coff_short_name_eq(dbg_name, s->name);
|
||||||
|
if (match) {
|
||||||
|
sections[j].offset = s->pointer_to_raw_data;
|
||||||
|
sections[j].size = s->virtual_size <= s->size_of_raw_data
|
||||||
|
? s->virtual_size
|
||||||
|
: s->size_of_raw_data;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (syms_num != 0) {
|
||||||
|
struct coff_syminfo_data *sdata;
|
||||||
|
|
||||||
|
sdata = ((struct coff_syminfo_data *)backtrace_alloc(state, sizeof *sdata,
|
||||||
|
error_callback, data));
|
||||||
|
if (sdata == NULL) goto fail;
|
||||||
|
|
||||||
|
if (!coff_initialize_syminfo(state, image_base, is_64, sects, sects_num,
|
||||||
|
syms_view.data, syms_size, str_view.data,
|
||||||
|
str_size, error_callback, data, sdata)) {
|
||||||
|
backtrace_free(state, sdata, sizeof *sdata, error_callback, data);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
*found_sym = 1;
|
||||||
|
|
||||||
|
coff_add_syminfo_data(state, sdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
backtrace_release_view(state, §s_view, error_callback, data);
|
||||||
|
sects_view_valid = 0;
|
||||||
|
if (syms_view_valid) {
|
||||||
|
backtrace_release_view(state, &syms_view, error_callback, data);
|
||||||
|
syms_view_valid = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
min_offset = 0;
|
||||||
|
max_offset = 0;
|
||||||
|
for (i = 0; i < (int)DEBUG_MAX; ++i) {
|
||||||
|
off_t end;
|
||||||
|
|
||||||
|
if (sections[i].size == 0) continue;
|
||||||
|
if (min_offset == 0 || sections[i].offset < min_offset)
|
||||||
|
min_offset = sections[i].offset;
|
||||||
|
end = sections[i].offset + sections[i].size;
|
||||||
|
if (end > max_offset) max_offset = end;
|
||||||
|
}
|
||||||
|
if (min_offset == 0 || max_offset == 0) {
|
||||||
|
if (!backtrace_close(descriptor, error_callback, data)) goto fail;
|
||||||
|
*fileline_fn = coff_nodebug;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!backtrace_get_view(state, descriptor, min_offset,
|
||||||
|
max_offset - min_offset, error_callback, data,
|
||||||
|
&debug_view))
|
||||||
|
goto fail;
|
||||||
|
debug_view_valid = 1;
|
||||||
|
|
||||||
|
if (!backtrace_close(descriptor, error_callback, data)) goto fail;
|
||||||
|
descriptor = -1;
|
||||||
|
|
||||||
|
for (i = 0; i < (int)DEBUG_MAX; ++i) {
|
||||||
|
size_t size = sections[i].size;
|
||||||
|
dwarf_sections.size[i] = size;
|
||||||
|
if (size == 0)
|
||||||
|
dwarf_sections.data[i] = NULL;
|
||||||
|
else
|
||||||
|
dwarf_sections.data[i] = ((const unsigned char *)debug_view.data +
|
||||||
|
(sections[i].offset - min_offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!backtrace_dwarf_add(state, 0, &dwarf_sections, 0, NULL, error_callback,
|
||||||
|
data, fileline_fn, NULL))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
*found_dwarf = 1;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (sects_view_valid)
|
||||||
|
backtrace_release_view(state, §s_view, error_callback, data);
|
||||||
|
if (str_view_valid)
|
||||||
|
backtrace_release_view(state, &str_view, error_callback, data);
|
||||||
|
if (syms_view_valid)
|
||||||
|
backtrace_release_view(state, &syms_view, error_callback, data);
|
||||||
|
if (debug_view_valid)
|
||||||
|
backtrace_release_view(state, &debug_view, error_callback, data);
|
||||||
|
if (descriptor != -1) backtrace_close(descriptor, error_callback, data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int backtrace_initialize(struct backtrace_state *state,
|
||||||
|
const char *filename ATTRIBUTE_UNUSED, int descriptor,
|
||||||
|
backtrace_error_callback error_callback, void *data,
|
||||||
|
fileline *fileline_fn) {
|
||||||
|
int ret;
|
||||||
|
int found_sym;
|
||||||
|
int found_dwarf;
|
||||||
|
fileline coff_fileline_fn;
|
||||||
|
|
||||||
|
ret = coff_add(state, descriptor, error_callback, data, &coff_fileline_fn,
|
||||||
|
&found_sym, &found_dwarf);
|
||||||
|
if (!ret) return 0;
|
||||||
|
|
||||||
|
if (!state->threaded) {
|
||||||
|
if (found_sym)
|
||||||
|
state->syminfo_fn = coff_syminfo;
|
||||||
|
else if (state->syminfo_fn == NULL)
|
||||||
|
state->syminfo_fn = coff_nosyms;
|
||||||
|
} else {
|
||||||
|
if (found_sym)
|
||||||
|
backtrace_atomic_store_pointer(&state->syminfo_fn, coff_syminfo);
|
||||||
|
else
|
||||||
|
(void)__sync_bool_compare_and_swap(&state->syminfo_fn, NULL, coff_nosyms);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!state->threaded) {
|
||||||
|
if (state->fileline_fn == NULL || state->fileline_fn == coff_nodebug)
|
||||||
|
*fileline_fn = coff_fileline_fn;
|
||||||
|
} else {
|
||||||
|
fileline current_fn;
|
||||||
|
|
||||||
|
current_fn = backtrace_atomic_load_pointer(&state->fileline_fn);
|
||||||
|
if (current_fn == NULL || current_fn == coff_nodebug)
|
||||||
|
*fileline_fn = coff_fileline_fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read.c:
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int backtrace_get_view(struct backtrace_state *state, int descriptor,
|
||||||
|
off_t offset, uint64_t size,
|
||||||
|
backtrace_error_callback error_callback, void *data,
|
||||||
|
struct backtrace_view *view) {
|
||||||
|
uint64_t got;
|
||||||
|
ssize_t r;
|
||||||
|
|
||||||
|
if ((uint64_t)(size_t)size != size) {
|
||||||
|
error_callback(data, "file size too large", 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lseek(descriptor, offset, SEEK_SET) < 0) {
|
||||||
|
error_callback(data, "lseek", errno);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
view->base = backtrace_alloc(state, size, error_callback, data);
|
||||||
|
if (view->base == NULL) return 0;
|
||||||
|
view->data = view->base;
|
||||||
|
view->len = size;
|
||||||
|
|
||||||
|
got = 0;
|
||||||
|
while (got < size) {
|
||||||
|
r = read(descriptor, view->base, size - got);
|
||||||
|
if (r < 0) {
|
||||||
|
error_callback(data, "read", errno);
|
||||||
|
free(view->base);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (r == 0) break;
|
||||||
|
got += (uint64_t)r;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (got < size) {
|
||||||
|
error_callback(data, "file too short", 0);
|
||||||
|
free(view->base);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void backtrace_release_view(struct backtrace_state *state,
|
||||||
|
struct backtrace_view *view,
|
||||||
|
backtrace_error_callback error_callback,
|
||||||
|
void *data) {
|
||||||
|
backtrace_free(state, view->base, view->len, error_callback, data);
|
||||||
|
view->data = NULL;
|
||||||
|
view->base = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// alloc.c:
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
void *backtrace_alloc(struct backtrace_state *state ATTRIBUTE_UNUSED,
|
||||||
|
size_t size, backtrace_error_callback error_callback,
|
||||||
|
void *data) {
|
||||||
|
void *ret;
|
||||||
|
|
||||||
|
ret = malloc(size);
|
||||||
|
if (ret == NULL) {
|
||||||
|
if (error_callback) error_callback(data, "malloc", errno);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void backtrace_free(struct backtrace_state *state ATTRIBUTE_UNUSED, void *p,
|
||||||
|
size_t size ATTRIBUTE_UNUSED,
|
||||||
|
backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
|
||||||
|
void *data ATTRIBUTE_UNUSED) {
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *backtrace_vector_grow(struct backtrace_state *state ATTRIBUTE_UNUSED,
|
||||||
|
size_t size,
|
||||||
|
backtrace_error_callback error_callback, void *data,
|
||||||
|
struct backtrace_vector *vec) {
|
||||||
|
void *ret;
|
||||||
|
|
||||||
|
if (size > vec->alc) {
|
||||||
|
size_t alc;
|
||||||
|
void *base;
|
||||||
|
|
||||||
|
if (vec->size == 0)
|
||||||
|
alc = 32 * size;
|
||||||
|
else if (vec->size >= 4096)
|
||||||
|
alc = vec->size + 4096;
|
||||||
|
else
|
||||||
|
alc = 2 * vec->size;
|
||||||
|
|
||||||
|
if (alc < vec->size + size) alc = vec->size + size;
|
||||||
|
|
||||||
|
base = realloc(vec->base, alc);
|
||||||
|
if (base == NULL) {
|
||||||
|
error_callback(data, "realloc", errno);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec->base = base;
|
||||||
|
vec->alc = alc - vec->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = (char *)vec->base + vec->size;
|
||||||
|
vec->size += size;
|
||||||
|
vec->alc -= size;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *backtrace_vector_finish(struct backtrace_state *state,
|
||||||
|
struct backtrace_vector *vec,
|
||||||
|
backtrace_error_callback error_callback,
|
||||||
|
void *data) {
|
||||||
|
void *ret;
|
||||||
|
|
||||||
|
if (!backtrace_vector_release(state, vec, error_callback, data)) return NULL;
|
||||||
|
ret = vec->base;
|
||||||
|
vec->base = NULL;
|
||||||
|
vec->size = 0;
|
||||||
|
vec->alc = 0;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int backtrace_vector_release(struct backtrace_state *state ATTRIBUTE_UNUSED,
|
||||||
|
struct backtrace_vector *vec,
|
||||||
|
backtrace_error_callback error_callback,
|
||||||
|
void *data) {
|
||||||
|
vec->alc = 0;
|
||||||
|
|
||||||
|
if (vec->size == 0) {
|
||||||
|
free(vec->base);
|
||||||
|
vec->base = NULL;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec->base = realloc(vec->base, vec->size);
|
||||||
|
if (vec->base == NULL) {
|
||||||
|
error_callback(data, "realloc", errno);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
|
@ -54,7 +54,11 @@ fn panic_debug(line_no int, file string, mod string, fn_name string, s string) {
|
||||||
}
|
}
|
||||||
C.exit(1)
|
C.exit(1)
|
||||||
}
|
}
|
||||||
print_backtrace_skipping_top_frames(1)
|
$if use_libbacktrace ? {
|
||||||
|
print_libbacktrace(1)
|
||||||
|
} $else {
|
||||||
|
print_backtrace_skipping_top_frames(1)
|
||||||
|
}
|
||||||
$if panics_break_into_debugger ? {
|
$if panics_break_into_debugger ? {
|
||||||
break_if_debugger_attached()
|
break_if_debugger_attached()
|
||||||
}
|
}
|
||||||
|
@ -101,7 +105,11 @@ pub fn panic(s string) {
|
||||||
}
|
}
|
||||||
C.exit(1)
|
C.exit(1)
|
||||||
}
|
}
|
||||||
print_backtrace_skipping_top_frames(1)
|
$if use_libbacktrace ? {
|
||||||
|
print_libbacktrace(1)
|
||||||
|
} $else {
|
||||||
|
print_backtrace_skipping_top_frames(1)
|
||||||
|
}
|
||||||
$if panics_break_into_debugger ? {
|
$if panics_break_into_debugger ? {
|
||||||
break_if_debugger_attached()
|
break_if_debugger_attached()
|
||||||
}
|
}
|
||||||
|
@ -557,8 +565,23 @@ pub fn print_backtrace() {
|
||||||
$if tinyc {
|
$if tinyc {
|
||||||
C.tcc_backtrace(c'Backtrace')
|
C.tcc_backtrace(c'Backtrace')
|
||||||
} $else {
|
} $else {
|
||||||
print_backtrace_skipping_top_frames(2)
|
// NOTE: TCC doesn't have the unwind library
|
||||||
|
$if use_libbacktrace ? {
|
||||||
|
print_libbacktrace(1)
|
||||||
|
} $else {
|
||||||
|
print_backtrace_skipping_top_frames(2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: g_main_argc and g_main_argv are filled in right after C's main start.
|
||||||
|
// They are used internally by V's builtin; for user code, it is much
|
||||||
|
// more convenient to just use `os.args` instead.
|
||||||
|
|
||||||
|
[markused]
|
||||||
|
__global g_main_argc = int(0)
|
||||||
|
|
||||||
|
[markused]
|
||||||
|
__global g_main_argv = voidptr(0)
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
[has_globals]
|
||||||
|
module builtin
|
||||||
|
|
||||||
|
#flag -I@VEXEROOT/thirdparty/libbacktrace
|
||||||
|
#flag @VEXEROOT/thirdparty/libbacktrace/backtrace.o
|
||||||
|
#include <backtrace.h>
|
||||||
|
|
||||||
|
// NOTE: Don't mark this as a [typedef] or it may cause compiler errors!
|
||||||
|
struct C.backtrace_state {
|
||||||
|
// filename &char
|
||||||
|
}
|
||||||
|
|
||||||
|
type BacktraceErrorCallback = fn (data voidptr, msg &char, errnum int) voidptr
|
||||||
|
|
||||||
|
type BacktraceFullCallback = fn (data voidptr, pc voidptr, filename &char, lineno int, func &char) &int
|
||||||
|
|
||||||
|
fn C.backtrace_create_state(filename &char, threaded int, error_callback BacktraceErrorCallback, data voidptr) &C.backtrace_state
|
||||||
|
fn C.backtrace_full(state &C.backtrace_state, skip int, cb BacktraceFullCallback, err_cb BacktraceErrorCallback, data voidptr) int
|
||||||
|
|
||||||
|
__global bt_state = init_bt_state()
|
||||||
|
|
||||||
|
fn init_bt_state() &C.backtrace_state {
|
||||||
|
$if !tinyc {
|
||||||
|
mut filename := &char(0)
|
||||||
|
$if windows {
|
||||||
|
filename = unsafe { string_from_wide(&&u16(g_main_argv)[0]).str }
|
||||||
|
} $else {
|
||||||
|
filename = unsafe { &&char(g_main_argv)[0] }
|
||||||
|
}
|
||||||
|
return C.backtrace_create_state(filename, 1, bt_error_handler, 0)
|
||||||
|
}
|
||||||
|
return &C.backtrace_state(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// for bt_error_callback
|
||||||
|
// struct BacktraceData {
|
||||||
|
// state &C.backtrace_state
|
||||||
|
// }
|
||||||
|
|
||||||
|
fn bt_print_callback(data voidptr, pc voidptr, filename_ptr &char, line int, fn_name_ptr &char) int {
|
||||||
|
filename := if isnil(filename_ptr) { '???' } else { unsafe { filename_ptr.vstring() } }
|
||||||
|
fn_name := if isnil(fn_name_ptr) {
|
||||||
|
'???'
|
||||||
|
} else {
|
||||||
|
(unsafe { fn_name_ptr.vstring() }).replace('__', '.')
|
||||||
|
}
|
||||||
|
// keep it for later
|
||||||
|
// pc_64 := u64(pc)
|
||||||
|
println('$filename:$line: by $fn_name')
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bt_error_callback(data voidptr, msg_ptr &char, errnum int) {
|
||||||
|
// if !isnil(data) && !isnil(data.state) && !isnil(data.state.filename) {
|
||||||
|
// filename := unsafe{ data.state.filename.vstring() }
|
||||||
|
// eprint('$filename: ')
|
||||||
|
// }
|
||||||
|
|
||||||
|
msg := unsafe { msg_ptr.vstring() }
|
||||||
|
eprint('libbacktrace: $msg')
|
||||||
|
if errnum > 0 {
|
||||||
|
eprint(': ${C.strerror(errnum)}')
|
||||||
|
}
|
||||||
|
|
||||||
|
eprintln('')
|
||||||
|
}
|
||||||
|
|
||||||
|
// for backtrace_create_state only
|
||||||
|
fn bt_error_handler(data voidptr, msg &char, errnum int) {
|
||||||
|
eprint('libbacktrace: ')
|
||||||
|
eprint(unsafe { msg.vstring() })
|
||||||
|
if errnum > 0 {
|
||||||
|
eprint(': ${C.strerror(errnum)}')
|
||||||
|
}
|
||||||
|
eprintln('')
|
||||||
|
exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
[noinline]
|
||||||
|
fn print_libbacktrace(frames_to_skip int) {
|
||||||
|
$if no_backtrace ? {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// data := &BacktraceData{bt_state}
|
||||||
|
C.backtrace_full(bt_state, frames_to_skip, bt_print_callback, bt_error_callback, 0)
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
module builtin
|
||||||
|
|
||||||
|
fn print_libbacktrace(frames_to_skip int) {
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
fn test_g_main_argc() {
|
||||||
|
assert g_main_argc > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_g_main_argv() {
|
||||||
|
assert g_main_argv != 0
|
||||||
|
mut first_arg := ''
|
||||||
|
$if windows {
|
||||||
|
first_arg = unsafe { string_from_wide(&&u16(g_main_argv)[0]) }
|
||||||
|
} $else {
|
||||||
|
first_arg = unsafe { cstring_to_vstring(&&char(g_main_argv)[0]) }
|
||||||
|
}
|
||||||
|
assert first_arg.contains('builtin_test')
|
||||||
|
}
|
|
@ -72,6 +72,8 @@ fn (mut g Gen) gen_c_main_function_header() {
|
||||||
} else {
|
} else {
|
||||||
g.writeln('int main(int ___argc, char** ___argv){')
|
g.writeln('int main(int ___argc, char** ___argv){')
|
||||||
}
|
}
|
||||||
|
g.writeln('\tg_main_argc = ___argc;')
|
||||||
|
g.writeln('\tg_main_argv = ___argv;')
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) gen_c_main_header() {
|
fn (mut g Gen) gen_c_main_header() {
|
||||||
|
|
Loading…
Reference in New Issue