builtin: add libbacktrace as option for generating stack traces (#14277)
							parent
							
								
									ca2ab70c8b
								
							
						
					
					
						commit
						26a1d332a4
					
				|  | @ -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) | ||||
| 			} | ||||
| 			print_backtrace_skipping_top_frames(1) | ||||
| 			$if use_libbacktrace ? { | ||||
| 				print_libbacktrace(1) | ||||
| 			} $else { | ||||
| 				print_backtrace_skipping_top_frames(1) | ||||
| 			} | ||||
| 			$if panics_break_into_debugger ? { | ||||
| 				break_if_debugger_attached() | ||||
| 			} | ||||
|  | @ -101,7 +105,11 @@ pub fn panic(s string) { | |||
| 				} | ||||
| 				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 ? { | ||||
| 				break_if_debugger_attached() | ||||
| 			} | ||||
|  | @ -557,8 +565,23 @@ pub fn print_backtrace() { | |||
| 			$if tinyc { | ||||
| 				C.tcc_backtrace(c'Backtrace') | ||||
| 			} $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 { | ||||
| 		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() { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue