From 76a7354506bf63f6bb7fe46f39d925c2de8ae056 Mon Sep 17 00:00:00 2001 From: Ned <7358345+nedpals@users.noreply.github.com> Date: Thu, 5 May 2022 02:17:05 +0800 Subject: [PATCH] builtin: add libbacktrace as option for generating stack traces (#14277) --- thirdparty/libbacktrace/amalgamation.txt | 4 + thirdparty/libbacktrace/backtrace.c | 111 + thirdparty/libbacktrace/backtrace.h | 189 + thirdparty/libbacktrace/base.c | 4303 +++++++++++++++++ thirdparty/libbacktrace/darwin.c | 1298 +++++ thirdparty/libbacktrace/linux.c | 3865 +++++++++++++++ thirdparty/libbacktrace/windows.c | 818 ++++ vlib/builtin/builtin.c.v | 29 +- vlib/builtin/builtin_d_use_libbacktrace.c.v | 86 + .../builtin/builtin_notd_use_libbacktrace.c.v | 4 + vlib/builtin/builtin_test.v | 14 + vlib/v/gen/c/cmain.v | 2 + 12 files changed, 10720 insertions(+), 3 deletions(-) create mode 100644 thirdparty/libbacktrace/amalgamation.txt create mode 100644 thirdparty/libbacktrace/backtrace.c create mode 100644 thirdparty/libbacktrace/backtrace.h create mode 100644 thirdparty/libbacktrace/base.c create mode 100644 thirdparty/libbacktrace/darwin.c create mode 100644 thirdparty/libbacktrace/linux.c create mode 100644 thirdparty/libbacktrace/windows.c create mode 100644 vlib/builtin/builtin_d_use_libbacktrace.c.v create mode 100644 vlib/builtin/builtin_notd_use_libbacktrace.c.v create mode 100644 vlib/builtin/builtin_test.v diff --git a/thirdparty/libbacktrace/amalgamation.txt b/thirdparty/libbacktrace/amalgamation.txt new file mode 100644 index 0000000000..514669240a --- /dev/null +++ b/thirdparty/libbacktrace/amalgamation.txt @@ -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. \ No newline at end of file diff --git a/thirdparty/libbacktrace/backtrace.c b/thirdparty/libbacktrace/backtrace.c new file mode 100644 index 0000000000..ce7388eef0 --- /dev/null +++ b/thirdparty/libbacktrace/backtrace.c @@ -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 +#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 diff --git a/thirdparty/libbacktrace/backtrace.h b/thirdparty/libbacktrace/backtrace.h new file mode 100644 index 0000000000..69cea4ca1e --- /dev/null +++ b/thirdparty/libbacktrace/backtrace.h @@ -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 +#include +#include + +#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 diff --git a/thirdparty/libbacktrace/base.c b/thirdparty/libbacktrace/base.c new file mode 100644 index 0000000000..434d0318c7 --- /dev/null +++ b/thirdparty/libbacktrace/base.c @@ -0,0 +1,4303 @@ +// NOTE: Portions of the code have been modified in order to fix compilation in TCC - Ned + +// backtrace.h: +#ifndef BACKTRACE_H +#define BACKTRACE_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct backtrace_state; + +typedef void (*backtrace_error_callback)(void *data, const char *msg, + int errnum); + +extern struct backtrace_state *backtrace_create_state( + const char *filename, int threaded, backtrace_error_callback error_callback, + void *data); + +typedef int (*backtrace_full_callback)(void *data, uintptr_t pc, + const char *filename, int lineno, + const char *function); + +extern int backtrace_full(struct backtrace_state *state, int skip, + backtrace_full_callback callback, + backtrace_error_callback error_callback, void *data); + +typedef int (*backtrace_simple_callback)(void *data, uintptr_t pc); + +extern int backtrace_simple(struct backtrace_state *state, int skip, + backtrace_simple_callback callback, + backtrace_error_callback error_callback, + void *data); + +extern void backtrace_print(struct backtrace_state *state, int skip, FILE *); + +extern int backtrace_pcinfo(struct backtrace_state *state, uintptr_t pc, + backtrace_full_callback callback, + backtrace_error_callback error_callback, + void *data); + +typedef void (*backtrace_syminfo_callback)(void *data, uintptr_t pc, + const char *symname, + uintptr_t symval, uintptr_t symsize); + +extern int backtrace_syminfo(struct backtrace_state *state, uintptr_t addr, + backtrace_syminfo_callback callback, + backtrace_error_callback error_callback, + void *data); + +#ifdef __cplusplus +} +#endif + +#endif + +// internal.h: +#ifndef BACKTRACE_INTERNAL_H +#define BACKTRACE_INTERNAL_H + +#ifndef GCC_VERSION +#define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__) +#endif + +#if (GCC_VERSION < 2007) +#define __attribute__(x) +#endif + +#ifndef ATTRIBUTE_UNUSED +#define ATTRIBUTE_UNUSED __attribute__((__unused__)) +#endif + +#ifndef ATTRIBUTE_MALLOC +#if (GCC_VERSION >= 2096) +#define ATTRIBUTE_MALLOC __attribute__((__malloc__)) +#else +#define ATTRIBUTE_MALLOC +#endif +#endif + +#ifndef ATTRIBUTE_FALLTHROUGH +#if (GCC_VERSION >= 7000) +#define ATTRIBUTE_FALLTHROUGH __attribute__((__fallthrough__)) +#else +#define ATTRIBUTE_FALLTHROUGH +#endif +#endif + +#ifndef HAVE_SYNC_FUNCTIONS + +#define __sync_bool_compare_and_swap(A, B, C) (abort(), 1) +#define __sync_lock_test_and_set(A, B) (abort(), 0) +#define __sync_lock_release(A) abort() + +#endif +#ifdef HAVE_ATOMIC_FUNCTIONS + +#define backtrace_atomic_load_pointer(p) __atomic_load_n((p), __ATOMIC_ACQUIRE) +#define backtrace_atomic_load_int(p) __atomic_load_n((p), __ATOMIC_ACQUIRE) +#define backtrace_atomic_store_pointer(p, v) \ + __atomic_store_n((p), (v), __ATOMIC_RELEASE) +#define backtrace_atomic_store_size_t(p, v) \ + __atomic_store_n((p), (v), __ATOMIC_RELEASE) +#define backtrace_atomic_store_int(p, v) \ + __atomic_store_n((p), (v), __ATOMIC_RELEASE) + +#else +#ifdef HAVE_SYNC_FUNCTIONS + +extern void *backtrace_atomic_load_pointer(void *); +extern int backtrace_atomic_load_int(int *); +extern void backtrace_atomic_store_pointer(void *, void *); +extern void backtrace_atomic_store_size_t(size_t *, size_t); +extern void backtrace_atomic_store_int(int *, int); + +#else + +#define backtrace_atomic_load_pointer(p) (abort(), (void *)NULL) +#define backtrace_atomic_load_int(p) (abort(), 0) +#define backtrace_atomic_store_pointer(p, v) abort() +#define backtrace_atomic_store_size_t(p, v) abort() +#define backtrace_atomic_store_int(p, v) abort() + +#endif +#endif + +typedef int (*fileline)(struct backtrace_state *state, uintptr_t pc, + backtrace_full_callback callback, + backtrace_error_callback error_callback, void *data); + +typedef void (*syminfo)(struct backtrace_state *state, uintptr_t pc, + backtrace_syminfo_callback callback, + backtrace_error_callback error_callback, void *data); + +struct backtrace_state { + const char *filename; + + int threaded; + + void *lock; + + fileline fileline_fn; + + void *fileline_data; + + syminfo syminfo_fn; + + void *syminfo_data; + + int fileline_initialization_failed; + + int lock_alloc; + + struct backtrace_freelist_struct *freelist; +}; + +extern int backtrace_open(const char *filename, + backtrace_error_callback error_callback, void *data, + int *does_not_exist); + +struct backtrace_view { + const void *data; + + void *base; + + size_t len; +}; + +extern 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); + +extern void backtrace_release_view(struct backtrace_state *state, + struct backtrace_view *view, + backtrace_error_callback error_callback, + void *data); + +extern int backtrace_close(int descriptor, + backtrace_error_callback error_callback, void *data); + +extern void backtrace_qsort(void *base, size_t count, size_t size, + int (*compar)(const void *, const void *)); + +extern void *backtrace_alloc(struct backtrace_state *state, size_t size, + backtrace_error_callback error_callback, + void *data) ATTRIBUTE_MALLOC; + +extern void backtrace_free(struct backtrace_state *state, void *mem, + size_t size, backtrace_error_callback error_callback, + void *data); + +struct backtrace_vector { + void *base; + + size_t size; + + size_t alc; +}; + +extern void *backtrace_vector_grow(struct backtrace_state *state, size_t size, + backtrace_error_callback error_callback, + void *data, struct backtrace_vector *vec); + +extern void *backtrace_vector_finish(struct backtrace_state *state, + struct backtrace_vector *vec, + backtrace_error_callback error_callback, + void *data); + +extern int backtrace_vector_release(struct backtrace_state *state, + struct backtrace_vector *vec, + backtrace_error_callback error_callback, + void *data); + +static inline void backtrace_vector_free( + struct backtrace_state *state, struct backtrace_vector *vec, + backtrace_error_callback error_callback, void *data) { + vec->alc += vec->size; + vec->size = 0; + backtrace_vector_release(state, vec, error_callback, data); +} + +extern int backtrace_initialize(struct backtrace_state *state, + const char *filename, int descriptor, + backtrace_error_callback error_callback, + void *data, fileline *fileline_fn); + +enum dwarf_section { + DEBUG_INFO, + DEBUG_LINE, + DEBUG_ABBREV, + DEBUG_RANGES, + DEBUG_STR, + DEBUG_ADDR, + DEBUG_STR_OFFSETS, + DEBUG_LINE_STR, + DEBUG_RNGLISTS, + + DEBUG_MAX +}; + +struct dwarf_sections { + const unsigned char *data[DEBUG_MAX]; + size_t size[DEBUG_MAX]; +}; + +struct dwarf_data; + +extern int backtrace_dwarf_add(struct backtrace_state *state, + uintptr_t base_address, + const struct dwarf_sections *dwarf_sections, + int is_bigendian, + struct dwarf_data *fileline_altlink, + backtrace_error_callback error_callback, + void *data, fileline *fileline_fn, + struct dwarf_data **fileline_entry); + +struct backtrace_call_full { + backtrace_full_callback full_callback; + backtrace_error_callback full_error_callback; + void *full_data; + int ret; +}; + +extern void backtrace_syminfo_to_full_callback(void *data, uintptr_t pc, + const char *symname, + uintptr_t symval, + uintptr_t symsize); + +extern void backtrace_syminfo_to_full_error_callback(void *, const char *, int); + +extern int backtrace_uncompress_zdebug(struct backtrace_state *, + const unsigned char *compressed, + size_t compressed_size, + backtrace_error_callback, void *data, + unsigned char **uncompressed, + size_t *uncompressed_size); + +extern int backtrace_uncompress_lzma(struct backtrace_state *, + const unsigned char *compressed, + size_t compressed_size, + backtrace_error_callback, void *data, + unsigned char **uncompressed, + size_t *uncompressed_size); + +#endif + +// filenames.h: +#ifndef GCC_VERSION +#define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__) +#endif + +#if (GCC_VERSION < 2007) +#define __attribute__(x) +#endif + +#ifndef ATTRIBUTE_UNUSED +#define ATTRIBUTE_UNUSED __attribute__((__unused__)) +#endif + +#if defined(__MSDOS__) || defined(_WIN32) || defined(__OS2__) || \ + defined(__CYGWIN__) +#define IS_DIR_SEPARATOR(c) ((c) == '/' || (c) == '\\') +#define HAS_DRIVE_SPEC(f) ((f)[0] != '\0' && (f)[1] == ':') +#define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR((f)[0]) || HAS_DRIVE_SPEC(f)) +#else +#define IS_DIR_SEPARATOR(c) ((c) == '/') +#define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR((f)[0])) +#endif + +// atomic.c: +#include + +// dwarf.c: +#include +#include +#include +#include + +enum dwarf_tag { + DW_TAG_entry_point = 0x3, + DW_TAG_compile_unit = 0x11, + DW_TAG_inlined_subroutine = 0x1d, + DW_TAG_subprogram = 0x2e, + DW_TAG_skeleton_unit = 0x4a, +}; + +enum dwarf_form { + DW_FORM_addr = 0x01, + DW_FORM_block2 = 0x03, + DW_FORM_block4 = 0x04, + DW_FORM_data2 = 0x05, + DW_FORM_data4 = 0x06, + DW_FORM_data8 = 0x07, + DW_FORM_string = 0x08, + DW_FORM_block = 0x09, + DW_FORM_block1 = 0x0a, + DW_FORM_data1 = 0x0b, + DW_FORM_flag = 0x0c, + DW_FORM_sdata = 0x0d, + DW_FORM_strp = 0x0e, + DW_FORM_udata = 0x0f, + DW_FORM_ref_addr = 0x10, + DW_FORM_ref1 = 0x11, + DW_FORM_ref2 = 0x12, + DW_FORM_ref4 = 0x13, + DW_FORM_ref8 = 0x14, + DW_FORM_ref_udata = 0x15, + DW_FORM_indirect = 0x16, + DW_FORM_sec_offset = 0x17, + DW_FORM_exprloc = 0x18, + DW_FORM_flag_present = 0x19, + DW_FORM_ref_sig8 = 0x20, + DW_FORM_strx = 0x1a, + DW_FORM_addrx = 0x1b, + DW_FORM_ref_sup4 = 0x1c, + DW_FORM_strp_sup = 0x1d, + DW_FORM_data16 = 0x1e, + DW_FORM_line_strp = 0x1f, + DW_FORM_implicit_const = 0x21, + DW_FORM_loclistx = 0x22, + DW_FORM_rnglistx = 0x23, + DW_FORM_ref_sup8 = 0x24, + DW_FORM_strx1 = 0x25, + DW_FORM_strx2 = 0x26, + DW_FORM_strx3 = 0x27, + DW_FORM_strx4 = 0x28, + DW_FORM_addrx1 = 0x29, + DW_FORM_addrx2 = 0x2a, + DW_FORM_addrx3 = 0x2b, + DW_FORM_addrx4 = 0x2c, + DW_FORM_GNU_addr_index = 0x1f01, + DW_FORM_GNU_str_index = 0x1f02, + DW_FORM_GNU_ref_alt = 0x1f20, + DW_FORM_GNU_strp_alt = 0x1f21 +}; + +enum dwarf_attribute { + DW_AT_sibling = 0x01, + DW_AT_location = 0x02, + DW_AT_name = 0x03, + DW_AT_ordering = 0x09, + DW_AT_subscr_data = 0x0a, + DW_AT_byte_size = 0x0b, + DW_AT_bit_offset = 0x0c, + DW_AT_bit_size = 0x0d, + DW_AT_element_list = 0x0f, + DW_AT_stmt_list = 0x10, + DW_AT_low_pc = 0x11, + DW_AT_high_pc = 0x12, + DW_AT_language = 0x13, + DW_AT_member = 0x14, + DW_AT_discr = 0x15, + DW_AT_discr_value = 0x16, + DW_AT_visibility = 0x17, + DW_AT_import = 0x18, + DW_AT_string_length = 0x19, + DW_AT_common_reference = 0x1a, + DW_AT_comp_dir = 0x1b, + DW_AT_const_value = 0x1c, + DW_AT_containing_type = 0x1d, + DW_AT_default_value = 0x1e, + DW_AT_inline = 0x20, + DW_AT_is_optional = 0x21, + DW_AT_lower_bound = 0x22, + DW_AT_producer = 0x25, + DW_AT_prototyped = 0x27, + DW_AT_return_addr = 0x2a, + DW_AT_start_scope = 0x2c, + DW_AT_bit_stride = 0x2e, + DW_AT_upper_bound = 0x2f, + DW_AT_abstract_origin = 0x31, + DW_AT_accessibility = 0x32, + DW_AT_address_class = 0x33, + DW_AT_artificial = 0x34, + DW_AT_base_types = 0x35, + DW_AT_calling_convention = 0x36, + DW_AT_count = 0x37, + DW_AT_data_member_location = 0x38, + DW_AT_decl_column = 0x39, + DW_AT_decl_file = 0x3a, + DW_AT_decl_line = 0x3b, + DW_AT_declaration = 0x3c, + DW_AT_discr_list = 0x3d, + DW_AT_encoding = 0x3e, + DW_AT_external = 0x3f, + DW_AT_frame_base = 0x40, + DW_AT_friend = 0x41, + DW_AT_identifier_case = 0x42, + DW_AT_macro_info = 0x43, + DW_AT_namelist_items = 0x44, + DW_AT_priority = 0x45, + DW_AT_segment = 0x46, + DW_AT_specification = 0x47, + DW_AT_static_link = 0x48, + DW_AT_type = 0x49, + DW_AT_use_location = 0x4a, + DW_AT_variable_parameter = 0x4b, + DW_AT_virtuality = 0x4c, + DW_AT_vtable_elem_location = 0x4d, + DW_AT_allocated = 0x4e, + DW_AT_associated = 0x4f, + DW_AT_data_location = 0x50, + DW_AT_byte_stride = 0x51, + DW_AT_entry_pc = 0x52, + DW_AT_use_UTF8 = 0x53, + DW_AT_extension = 0x54, + DW_AT_ranges = 0x55, + DW_AT_trampoline = 0x56, + DW_AT_call_column = 0x57, + DW_AT_call_file = 0x58, + DW_AT_call_line = 0x59, + DW_AT_description = 0x5a, + DW_AT_binary_scale = 0x5b, + DW_AT_decimal_scale = 0x5c, + DW_AT_small = 0x5d, + DW_AT_decimal_sign = 0x5e, + DW_AT_digit_count = 0x5f, + DW_AT_picture_string = 0x60, + DW_AT_mutable = 0x61, + DW_AT_threads_scaled = 0x62, + DW_AT_explicit = 0x63, + DW_AT_object_pointer = 0x64, + DW_AT_endianity = 0x65, + DW_AT_elemental = 0x66, + DW_AT_pure = 0x67, + DW_AT_recursive = 0x68, + DW_AT_signature = 0x69, + DW_AT_main_subprogram = 0x6a, + DW_AT_data_bit_offset = 0x6b, + DW_AT_const_expr = 0x6c, + DW_AT_enum_class = 0x6d, + DW_AT_linkage_name = 0x6e, + DW_AT_string_length_bit_size = 0x6f, + DW_AT_string_length_byte_size = 0x70, + DW_AT_rank = 0x71, + DW_AT_str_offsets_base = 0x72, + DW_AT_addr_base = 0x73, + DW_AT_rnglists_base = 0x74, + DW_AT_dwo_name = 0x76, + DW_AT_reference = 0x77, + DW_AT_rvalue_reference = 0x78, + DW_AT_macros = 0x79, + DW_AT_call_all_calls = 0x7a, + DW_AT_call_all_source_calls = 0x7b, + DW_AT_call_all_tail_calls = 0x7c, + DW_AT_call_return_pc = 0x7d, + DW_AT_call_value = 0x7e, + DW_AT_call_origin = 0x7f, + DW_AT_call_parameter = 0x80, + DW_AT_call_pc = 0x81, + DW_AT_call_tail_call = 0x82, + DW_AT_call_target = 0x83, + DW_AT_call_target_clobbered = 0x84, + DW_AT_call_data_location = 0x85, + DW_AT_call_data_value = 0x86, + DW_AT_noreturn = 0x87, + DW_AT_alignment = 0x88, + DW_AT_export_symbols = 0x89, + DW_AT_deleted = 0x8a, + DW_AT_defaulted = 0x8b, + DW_AT_loclists_base = 0x8c, + DW_AT_lo_user = 0x2000, + DW_AT_hi_user = 0x3fff, + DW_AT_MIPS_fde = 0x2001, + DW_AT_MIPS_loop_begin = 0x2002, + DW_AT_MIPS_tail_loop_begin = 0x2003, + DW_AT_MIPS_epilog_begin = 0x2004, + DW_AT_MIPS_loop_unroll_factor = 0x2005, + DW_AT_MIPS_software_pipeline_depth = 0x2006, + DW_AT_MIPS_linkage_name = 0x2007, + DW_AT_MIPS_stride = 0x2008, + DW_AT_MIPS_abstract_name = 0x2009, + DW_AT_MIPS_clone_origin = 0x200a, + DW_AT_MIPS_has_inlines = 0x200b, + DW_AT_HP_block_index = 0x2000, + DW_AT_HP_unmodifiable = 0x2001, + DW_AT_HP_prologue = 0x2005, + DW_AT_HP_epilogue = 0x2008, + DW_AT_HP_actuals_stmt_list = 0x2010, + DW_AT_HP_proc_per_section = 0x2011, + DW_AT_HP_raw_data_ptr = 0x2012, + DW_AT_HP_pass_by_reference = 0x2013, + DW_AT_HP_opt_level = 0x2014, + DW_AT_HP_prof_version_id = 0x2015, + DW_AT_HP_opt_flags = 0x2016, + DW_AT_HP_cold_region_low_pc = 0x2017, + DW_AT_HP_cold_region_high_pc = 0x2018, + DW_AT_HP_all_variables_modifiable = 0x2019, + DW_AT_HP_linkage_name = 0x201a, + DW_AT_HP_prof_flags = 0x201b, + DW_AT_HP_unit_name = 0x201f, + DW_AT_HP_unit_size = 0x2020, + DW_AT_HP_widened_byte_size = 0x2021, + DW_AT_HP_definition_points = 0x2022, + DW_AT_HP_default_location = 0x2023, + DW_AT_HP_is_result_param = 0x2029, + DW_AT_sf_names = 0x2101, + DW_AT_src_info = 0x2102, + DW_AT_mac_info = 0x2103, + DW_AT_src_coords = 0x2104, + DW_AT_body_begin = 0x2105, + DW_AT_body_end = 0x2106, + DW_AT_GNU_vector = 0x2107, + DW_AT_GNU_guarded_by = 0x2108, + DW_AT_GNU_pt_guarded_by = 0x2109, + DW_AT_GNU_guarded = 0x210a, + DW_AT_GNU_pt_guarded = 0x210b, + DW_AT_GNU_locks_excluded = 0x210c, + DW_AT_GNU_exclusive_locks_required = 0x210d, + DW_AT_GNU_shared_locks_required = 0x210e, + DW_AT_GNU_odr_signature = 0x210f, + DW_AT_GNU_template_name = 0x2110, + DW_AT_GNU_call_site_value = 0x2111, + DW_AT_GNU_call_site_data_value = 0x2112, + DW_AT_GNU_call_site_target = 0x2113, + DW_AT_GNU_call_site_target_clobbered = 0x2114, + DW_AT_GNU_tail_call = 0x2115, + DW_AT_GNU_all_tail_call_sites = 0x2116, + DW_AT_GNU_all_call_sites = 0x2117, + DW_AT_GNU_all_source_call_sites = 0x2118, + DW_AT_GNU_macros = 0x2119, + DW_AT_GNU_deleted = 0x211a, + DW_AT_GNU_dwo_name = 0x2130, + DW_AT_GNU_dwo_id = 0x2131, + DW_AT_GNU_ranges_base = 0x2132, + DW_AT_GNU_addr_base = 0x2133, + DW_AT_GNU_pubnames = 0x2134, + DW_AT_GNU_pubtypes = 0x2135, + DW_AT_GNU_discriminator = 0x2136, + DW_AT_GNU_locviews = 0x2137, + DW_AT_GNU_entry_view = 0x2138, + DW_AT_VMS_rtnbeg_pd_address = 0x2201, + DW_AT_use_GNAT_descriptive_type = 0x2301, + DW_AT_GNAT_descriptive_type = 0x2302, + DW_AT_GNU_numerator = 0x2303, + DW_AT_GNU_denominator = 0x2304, + DW_AT_GNU_bias = 0x2305, + DW_AT_upc_threads_scaled = 0x3210, + DW_AT_PGI_lbase = 0x3a00, + DW_AT_PGI_soffset = 0x3a01, + DW_AT_PGI_lstride = 0x3a02, + DW_AT_APPLE_optimized = 0x3fe1, + DW_AT_APPLE_flags = 0x3fe2, + DW_AT_APPLE_isa = 0x3fe3, + DW_AT_APPLE_block = 0x3fe4, + DW_AT_APPLE_major_runtime_vers = 0x3fe5, + DW_AT_APPLE_runtime_class = 0x3fe6, + DW_AT_APPLE_omit_frame_ptr = 0x3fe7, + DW_AT_APPLE_property_name = 0x3fe8, + DW_AT_APPLE_property_getter = 0x3fe9, + DW_AT_APPLE_property_setter = 0x3fea, + DW_AT_APPLE_property_attribute = 0x3feb, + DW_AT_APPLE_objc_complete_type = 0x3fec, + DW_AT_APPLE_property = 0x3fed +}; + +enum dwarf_line_number_op { + DW_LNS_extended_op = 0x0, + DW_LNS_copy = 0x1, + DW_LNS_advance_pc = 0x2, + DW_LNS_advance_line = 0x3, + DW_LNS_set_file = 0x4, + DW_LNS_set_column = 0x5, + DW_LNS_negate_stmt = 0x6, + DW_LNS_set_basic_block = 0x7, + DW_LNS_const_add_pc = 0x8, + DW_LNS_fixed_advance_pc = 0x9, + DW_LNS_set_prologue_end = 0xa, + DW_LNS_set_epilogue_begin = 0xb, + DW_LNS_set_isa = 0xc, +}; + +enum dwarf_extended_line_number_op { + DW_LNE_end_sequence = 0x1, + DW_LNE_set_address = 0x2, + DW_LNE_define_file = 0x3, + DW_LNE_set_discriminator = 0x4, +}; + +enum dwarf_line_number_content_type { + DW_LNCT_path = 0x1, + DW_LNCT_directory_index = 0x2, + DW_LNCT_timestamp = 0x3, + DW_LNCT_size = 0x4, + DW_LNCT_MD5 = 0x5, + DW_LNCT_lo_user = 0x2000, + DW_LNCT_hi_user = 0x3fff +}; + +enum dwarf_range_list_entry { + DW_RLE_end_of_list = 0x00, + DW_RLE_base_addressx = 0x01, + DW_RLE_startx_endx = 0x02, + DW_RLE_startx_length = 0x03, + DW_RLE_offset_pair = 0x04, + DW_RLE_base_address = 0x05, + DW_RLE_start_end = 0x06, + DW_RLE_start_length = 0x07 +}; + +enum dwarf_unit_type { + DW_UT_compile = 0x01, + DW_UT_type = 0x02, + DW_UT_partial = 0x03, + DW_UT_skeleton = 0x04, + DW_UT_split_compile = 0x05, + DW_UT_split_type = 0x06, + DW_UT_lo_user = 0x80, + DW_UT_hi_user = 0xff +}; + +#if !defined(HAVE_DECL_STRNLEN) || !HAVE_DECL_STRNLEN + +static size_t xstrnlen(const char *s, size_t maxlen) { + size_t i; + + for (i = 0; i < maxlen; ++i) + if (s[i] == '\0') break; + return i; +} + +#define strnlen xstrnlen + +#endif + +struct dwarf_buf { + const char *name; + + const unsigned char *start; + + const unsigned char *buf; + + size_t left; + + int is_bigendian; + + backtrace_error_callback error_callback; + + void *data; + + int reported_underflow; +}; + +struct attr { + enum dwarf_attribute name; + + enum dwarf_form form; + + int64_t val; +}; + +struct abbrev { + uint64_t code; + + enum dwarf_tag tag; + + int has_children; + + size_t num_attrs; + + struct attr *attrs; +}; + +struct abbrevs { + size_t num_abbrevs; + + struct abbrev *abbrevs; +}; + +enum attr_val_encoding { + + ATTR_VAL_NONE, + + ATTR_VAL_ADDRESS, + + ATTR_VAL_ADDRESS_INDEX, + + ATTR_VAL_UINT, + + ATTR_VAL_SINT, + + ATTR_VAL_STRING, + + ATTR_VAL_STRING_INDEX, + + ATTR_VAL_REF_UNIT, + + ATTR_VAL_REF_INFO, + + ATTR_VAL_REF_ALT_INFO, + + ATTR_VAL_REF_SECTION, + + ATTR_VAL_REF_TYPE, + + ATTR_VAL_RNGLISTS_INDEX, + + ATTR_VAL_BLOCK, + + ATTR_VAL_EXPR, +}; + +struct attr_val { + enum attr_val_encoding encoding; + union { + uint64_t uint; + + int64_t sint; + + const char *string; + + } u; +}; + +struct line_header { + int version; + + int addrsize; + + unsigned int min_insn_len; + + unsigned int max_ops_per_insn; + + int line_base; + + unsigned int line_range; + + unsigned int opcode_base; + + const unsigned char *opcode_lengths; + + size_t dirs_count; + + const char **dirs; + + size_t filenames_count; + + const char **filenames; +}; + +struct line_header_format { + int lnct; + enum dwarf_form form; +}; + +struct line { + uintptr_t pc; + + const char *filename; + + int lineno; + + int idx; +}; + +struct line_vector { + struct backtrace_vector vec; + + size_t count; +}; + +struct function { + const char *name; + + const char *caller_filename; + + int caller_lineno; + + struct function_addrs *function_addrs; + size_t function_addrs_count; +}; + +struct function_addrs { + uint64_t low; + uint64_t high; + + struct function *function; +}; + +struct function_vector { + struct backtrace_vector vec; + + size_t count; +}; + +struct unit { + const unsigned char *unit_data; + + size_t unit_data_len; + + size_t unit_data_offset; + + size_t low_offset; + + size_t high_offset; + + int version; + + int is_dwarf64; + + int addrsize; + + off_t lineoff; + + uint64_t str_offsets_base; + + uint64_t addr_base; + + uint64_t rnglists_base; + + const char *filename; + + const char *comp_dir; + + const char *abs_filename; + + struct abbrevs abbrevs; + + struct line *lines; + + size_t lines_count; + + struct function_addrs *function_addrs; + size_t function_addrs_count; +}; + +struct unit_addrs { + uint64_t low; + uint64_t high; + + struct unit *u; +}; + +struct unit_addrs_vector { + struct backtrace_vector vec; + + size_t count; +}; + +struct unit_vector { + struct backtrace_vector vec; + size_t count; +}; + +struct dwarf_data { + struct dwarf_data *next; + + struct dwarf_data *altlink; + + uintptr_t base_address; + + struct unit_addrs *addrs; + + size_t addrs_count; + + struct unit **units; + + size_t units_count; + + struct dwarf_sections dwarf_sections; + + int is_bigendian; + + struct function_vector fvec; +}; + +static void dwarf_buf_error(struct dwarf_buf *buf, const char *msg, + int errnum) { + char b[200]; + + snprintf(b, sizeof b, "%s in %s at %d", msg, buf->name, + (int)(buf->buf - buf->start)); + buf->error_callback(buf->data, b, errnum); +} + +static int require(struct dwarf_buf *buf, size_t count) { + if (buf->left >= count) return 1; + + if (!buf->reported_underflow) { + dwarf_buf_error(buf, "DWARF underflow", 0); + buf->reported_underflow = 1; + } + + return 0; +} + +static int advance(struct dwarf_buf *buf, size_t count) { + if (!require(buf, count)) return 0; + buf->buf += count; + buf->left -= count; + return 1; +} + +static const char *read_string(struct dwarf_buf *buf) { + const char *p = (const char *)buf->buf; + size_t len = strnlen(p, buf->left); + + size_t count = len + 1; + + if (!advance(buf, count)) return NULL; + + return p; +} + +static unsigned char read_byte(struct dwarf_buf *buf) { + const unsigned char *p = buf->buf; + + if (!advance(buf, 1)) return 0; + return p[0]; +} + +static signed char read_sbyte(struct dwarf_buf *buf) { + const unsigned char *p = buf->buf; + + if (!advance(buf, 1)) return 0; + return (*p ^ 0x80) - 0x80; +} + +static uint16_t read_uint16(struct dwarf_buf *buf) { + const unsigned char *p = buf->buf; + + if (!advance(buf, 2)) return 0; + if (buf->is_bigendian) + return ((uint16_t)p[0] << 8) | (uint16_t)p[1]; + else + return ((uint16_t)p[1] << 8) | (uint16_t)p[0]; +} + +static uint32_t read_uint24(struct dwarf_buf *buf) { + const unsigned char *p = buf->buf; + + if (!advance(buf, 3)) return 0; + if (buf->is_bigendian) + return (((uint32_t)p[0] << 16) | ((uint32_t)p[1] << 8) | (uint32_t)p[2]); + else + return (((uint32_t)p[2] << 16) | ((uint32_t)p[1] << 8) | (uint32_t)p[0]); +} + +static uint32_t read_uint32(struct dwarf_buf *buf) { + const unsigned char *p = buf->buf; + + if (!advance(buf, 4)) return 0; + if (buf->is_bigendian) + return (((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | + ((uint32_t)p[2] << 8) | (uint32_t)p[3]); + else + return (((uint32_t)p[3] << 24) | ((uint32_t)p[2] << 16) | + ((uint32_t)p[1] << 8) | (uint32_t)p[0]); +} + +static uint64_t read_uint64(struct dwarf_buf *buf) { + const unsigned char *p = buf->buf; + + if (!advance(buf, 8)) return 0; + if (buf->is_bigendian) + return (((uint64_t)p[0] << 56) | ((uint64_t)p[1] << 48) | + ((uint64_t)p[2] << 40) | ((uint64_t)p[3] << 32) | + ((uint64_t)p[4] << 24) | ((uint64_t)p[5] << 16) | + ((uint64_t)p[6] << 8) | (uint64_t)p[7]); + else + return (((uint64_t)p[7] << 56) | ((uint64_t)p[6] << 48) | + ((uint64_t)p[5] << 40) | ((uint64_t)p[4] << 32) | + ((uint64_t)p[3] << 24) | ((uint64_t)p[2] << 16) | + ((uint64_t)p[1] << 8) | (uint64_t)p[0]); +} + +static uint64_t read_offset(struct dwarf_buf *buf, int is_dwarf64) { + if (is_dwarf64) + return read_uint64(buf); + else + return read_uint32(buf); +} + +static uint64_t read_address(struct dwarf_buf *buf, int addrsize) { + switch (addrsize) { + case 1: + return read_byte(buf); + case 2: + return read_uint16(buf); + case 4: + return read_uint32(buf); + case 8: + return read_uint64(buf); + default: + dwarf_buf_error(buf, "unrecognized address size", 0); + return 0; + } +} + +static int is_highest_address(uint64_t address, int addrsize) { + switch (addrsize) { + case 1: + return address == (unsigned char)-1; + case 2: + return address == (uint16_t)-1; + case 4: + return address == (uint32_t)-1; + case 8: + return address == (uint64_t)-1; + default: + return 0; + } +} + +static uint64_t read_uleb128(struct dwarf_buf *buf) { + uint64_t ret; + unsigned int shift; + int overflow; + unsigned char b; + + ret = 0; + shift = 0; + overflow = 0; + do { + const unsigned char *p; + + p = buf->buf; + if (!advance(buf, 1)) return 0; + b = *p; + if (shift < 64) + ret |= ((uint64_t)(b & 0x7f)) << shift; + else if (!overflow) { + dwarf_buf_error(buf, "LEB128 overflows uint64_t", 0); + overflow = 1; + } + shift += 7; + } while ((b & 0x80) != 0); + + return ret; +} + +static int64_t read_sleb128(struct dwarf_buf *buf) { + uint64_t val; + unsigned int shift; + int overflow; + unsigned char b; + + val = 0; + shift = 0; + overflow = 0; + do { + const unsigned char *p; + + p = buf->buf; + if (!advance(buf, 1)) return 0; + b = *p; + if (shift < 64) + val |= ((uint64_t)(b & 0x7f)) << shift; + else if (!overflow) { + dwarf_buf_error(buf, "signed LEB128 overflows uint64_t", 0); + overflow = 1; + } + shift += 7; + } while ((b & 0x80) != 0); + + if ((b & 0x40) != 0 && shift < 64) val |= ((uint64_t)-1) << shift; + + return (int64_t)val; +} + +static size_t leb128_len(const unsigned char *p) { + size_t ret; + + ret = 1; + while ((*p & 0x80) != 0) { + ++p; + ++ret; + } + return ret; +} + +static uint64_t read_initial_length(struct dwarf_buf *buf, int *is_dwarf64) { + uint64_t len; + + len = read_uint32(buf); + if (len == 0xffffffff) { + len = read_uint64(buf); + *is_dwarf64 = 1; + } else + *is_dwarf64 = 0; + + return len; +} + +static void free_abbrevs(struct backtrace_state *state, struct abbrevs *abbrevs, + backtrace_error_callback error_callback, void *data) { + size_t i; + + for (i = 0; i < abbrevs->num_abbrevs; ++i) + backtrace_free(state, abbrevs->abbrevs[i].attrs, + abbrevs->abbrevs[i].num_attrs * sizeof(struct attr), + error_callback, data); + backtrace_free(state, abbrevs->abbrevs, + abbrevs->num_abbrevs * sizeof(struct abbrev), error_callback, + data); + abbrevs->num_abbrevs = 0; + abbrevs->abbrevs = NULL; +} + +static int read_attribute(enum dwarf_form form, uint64_t implicit_val, + struct dwarf_buf *buf, int is_dwarf64, int version, + int addrsize, + const struct dwarf_sections *dwarf_sections, + struct dwarf_data *altlink, struct attr_val *val) { + memset(val, 0, sizeof *val); + + switch (form) { + case DW_FORM_addr: + val->encoding = ATTR_VAL_ADDRESS; + val->u.uint = read_address(buf, addrsize); + return 1; + case DW_FORM_block2: + val->encoding = ATTR_VAL_BLOCK; + return advance(buf, read_uint16(buf)); + case DW_FORM_block4: + val->encoding = ATTR_VAL_BLOCK; + return advance(buf, read_uint32(buf)); + case DW_FORM_data2: + val->encoding = ATTR_VAL_UINT; + val->u.uint = read_uint16(buf); + return 1; + case DW_FORM_data4: + val->encoding = ATTR_VAL_UINT; + val->u.uint = read_uint32(buf); + return 1; + case DW_FORM_data8: + val->encoding = ATTR_VAL_UINT; + val->u.uint = read_uint64(buf); + return 1; + case DW_FORM_data16: + val->encoding = ATTR_VAL_BLOCK; + return advance(buf, 16); + case DW_FORM_string: + val->encoding = ATTR_VAL_STRING; + val->u.string = read_string(buf); + return val->u.string == NULL ? 0 : 1; + case DW_FORM_block: + val->encoding = ATTR_VAL_BLOCK; + return advance(buf, read_uleb128(buf)); + case DW_FORM_block1: + val->encoding = ATTR_VAL_BLOCK; + return advance(buf, read_byte(buf)); + case DW_FORM_data1: + val->encoding = ATTR_VAL_UINT; + val->u.uint = read_byte(buf); + return 1; + case DW_FORM_flag: + val->encoding = ATTR_VAL_UINT; + val->u.uint = read_byte(buf); + return 1; + case DW_FORM_sdata: + val->encoding = ATTR_VAL_SINT; + val->u.sint = read_sleb128(buf); + return 1; + case DW_FORM_strp: { + uint64_t offset; + + offset = read_offset(buf, is_dwarf64); + if (offset >= dwarf_sections->size[DEBUG_STR]) { + dwarf_buf_error(buf, "DW_FORM_strp out of range", 0); + return 0; + } + val->encoding = ATTR_VAL_STRING; + val->u.string = (const char *)dwarf_sections->data[DEBUG_STR] + offset; + return 1; + } + case DW_FORM_line_strp: { + uint64_t offset; + + offset = read_offset(buf, is_dwarf64); + if (offset >= dwarf_sections->size[DEBUG_LINE_STR]) { + dwarf_buf_error(buf, "DW_FORM_line_strp out of range", 0); + return 0; + } + val->encoding = ATTR_VAL_STRING; + val->u.string = + (const char *)dwarf_sections->data[DEBUG_LINE_STR] + offset; + return 1; + } + case DW_FORM_udata: + val->encoding = ATTR_VAL_UINT; + val->u.uint = read_uleb128(buf); + return 1; + case DW_FORM_ref_addr: + val->encoding = ATTR_VAL_REF_INFO; + if (version == 2) + val->u.uint = read_address(buf, addrsize); + else + val->u.uint = read_offset(buf, is_dwarf64); + return 1; + case DW_FORM_ref1: + val->encoding = ATTR_VAL_REF_UNIT; + val->u.uint = read_byte(buf); + return 1; + case DW_FORM_ref2: + val->encoding = ATTR_VAL_REF_UNIT; + val->u.uint = read_uint16(buf); + return 1; + case DW_FORM_ref4: + val->encoding = ATTR_VAL_REF_UNIT; + val->u.uint = read_uint32(buf); + return 1; + case DW_FORM_ref8: + val->encoding = ATTR_VAL_REF_UNIT; + val->u.uint = read_uint64(buf); + return 1; + case DW_FORM_ref_udata: + val->encoding = ATTR_VAL_REF_UNIT; + val->u.uint = read_uleb128(buf); + return 1; + case DW_FORM_indirect: { + uint64_t form; + + form = read_uleb128(buf); + if (form == DW_FORM_implicit_const) { + dwarf_buf_error(buf, "DW_FORM_indirect to DW_FORM_implicit_const", 0); + return 0; + } + return read_attribute((enum dwarf_form)form, 0, buf, is_dwarf64, version, + addrsize, dwarf_sections, altlink, val); + } + case DW_FORM_sec_offset: + val->encoding = ATTR_VAL_REF_SECTION; + val->u.uint = read_offset(buf, is_dwarf64); + return 1; + case DW_FORM_exprloc: + val->encoding = ATTR_VAL_EXPR; + return advance(buf, read_uleb128(buf)); + case DW_FORM_flag_present: + val->encoding = ATTR_VAL_UINT; + val->u.uint = 1; + return 1; + case DW_FORM_ref_sig8: + val->encoding = ATTR_VAL_REF_TYPE; + val->u.uint = read_uint64(buf); + return 1; + case DW_FORM_strx: + case DW_FORM_strx1: + case DW_FORM_strx2: + case DW_FORM_strx3: + case DW_FORM_strx4: { + uint64_t offset; + + switch (form) { + case DW_FORM_strx: + offset = read_uleb128(buf); + break; + case DW_FORM_strx1: + offset = read_byte(buf); + break; + case DW_FORM_strx2: + offset = read_uint16(buf); + break; + case DW_FORM_strx3: + offset = read_uint24(buf); + break; + case DW_FORM_strx4: + offset = read_uint32(buf); + break; + default: + + return 0; + } + val->encoding = ATTR_VAL_STRING_INDEX; + val->u.uint = offset; + return 1; + } + case DW_FORM_addrx: + case DW_FORM_addrx1: + case DW_FORM_addrx2: + case DW_FORM_addrx3: + case DW_FORM_addrx4: { + uint64_t offset; + + switch (form) { + case DW_FORM_addrx: + offset = read_uleb128(buf); + break; + case DW_FORM_addrx1: + offset = read_byte(buf); + break; + case DW_FORM_addrx2: + offset = read_uint16(buf); + break; + case DW_FORM_addrx3: + offset = read_uint24(buf); + break; + case DW_FORM_addrx4: + offset = read_uint32(buf); + break; + default: + + return 0; + } + val->encoding = ATTR_VAL_ADDRESS_INDEX; + val->u.uint = offset; + return 1; + } + case DW_FORM_ref_sup4: + val->encoding = ATTR_VAL_REF_SECTION; + val->u.uint = read_uint32(buf); + return 1; + case DW_FORM_ref_sup8: + val->encoding = ATTR_VAL_REF_SECTION; + val->u.uint = read_uint64(buf); + return 1; + case DW_FORM_implicit_const: + val->encoding = ATTR_VAL_UINT; + val->u.uint = implicit_val; + return 1; + case DW_FORM_loclistx: + + val->encoding = ATTR_VAL_REF_SECTION; + val->u.uint = read_uleb128(buf); + return 1; + case DW_FORM_rnglistx: + val->encoding = ATTR_VAL_RNGLISTS_INDEX; + val->u.uint = read_uleb128(buf); + return 1; + case DW_FORM_GNU_addr_index: + val->encoding = ATTR_VAL_REF_SECTION; + val->u.uint = read_uleb128(buf); + return 1; + case DW_FORM_GNU_str_index: + val->encoding = ATTR_VAL_REF_SECTION; + val->u.uint = read_uleb128(buf); + return 1; + case DW_FORM_GNU_ref_alt: + val->u.uint = read_offset(buf, is_dwarf64); + if (altlink == NULL) { + val->encoding = ATTR_VAL_NONE; + return 1; + } + val->encoding = ATTR_VAL_REF_ALT_INFO; + return 1; + case DW_FORM_strp_sup: + case DW_FORM_GNU_strp_alt: { + uint64_t offset; + + offset = read_offset(buf, is_dwarf64); + if (altlink == NULL) { + val->encoding = ATTR_VAL_NONE; + return 1; + } + if (offset >= altlink->dwarf_sections.size[DEBUG_STR]) { + dwarf_buf_error(buf, "DW_FORM_strp_sup out of range", 0); + return 0; + } + val->encoding = ATTR_VAL_STRING; + val->u.string = + (const char *)altlink->dwarf_sections.data[DEBUG_STR] + offset; + return 1; + } + default: + dwarf_buf_error(buf, "unrecognized DWARF form", -1); + return 0; + } +} + +static int resolve_string(const struct dwarf_sections *dwarf_sections, + int is_dwarf64, int is_bigendian, + uint64_t str_offsets_base, const struct attr_val *val, + backtrace_error_callback error_callback, void *data, + const char **string) { + switch (val->encoding) { + case ATTR_VAL_STRING: + *string = val->u.string; + return 1; + + case ATTR_VAL_STRING_INDEX: { + uint64_t offset; + struct dwarf_buf offset_buf; + + offset = val->u.uint * (is_dwarf64 ? 8 : 4) + str_offsets_base; + if (offset + (is_dwarf64 ? 8 : 4) > + dwarf_sections->size[DEBUG_STR_OFFSETS]) { + error_callback(data, "DW_FORM_strx value out of range", 0); + return 0; + } + + offset_buf.name = ".debug_str_offsets"; + offset_buf.start = dwarf_sections->data[DEBUG_STR_OFFSETS]; + offset_buf.buf = dwarf_sections->data[DEBUG_STR_OFFSETS] + offset; + offset_buf.left = dwarf_sections->size[DEBUG_STR_OFFSETS] - offset; + offset_buf.is_bigendian = is_bigendian; + offset_buf.error_callback = error_callback; + offset_buf.data = data; + offset_buf.reported_underflow = 0; + + offset = read_offset(&offset_buf, is_dwarf64); + if (offset >= dwarf_sections->size[DEBUG_STR]) { + dwarf_buf_error(&offset_buf, "DW_FORM_strx offset out of range", 0); + return 0; + } + *string = (const char *)dwarf_sections->data[DEBUG_STR] + offset; + return 1; + } + + default: + return 1; + } +} + +static int resolve_addr_index(const struct dwarf_sections *dwarf_sections, + uint64_t addr_base, int addrsize, + int is_bigendian, uint64_t addr_index, + backtrace_error_callback error_callback, + void *data, uint64_t *address) { + uint64_t offset; + struct dwarf_buf addr_buf; + + offset = addr_index * addrsize + addr_base; + if (offset + addrsize > dwarf_sections->size[DEBUG_ADDR]) { + error_callback(data, "DW_FORM_addrx value out of range", 0); + return 0; + } + + addr_buf.name = ".debug_addr"; + addr_buf.start = dwarf_sections->data[DEBUG_ADDR]; + addr_buf.buf = dwarf_sections->data[DEBUG_ADDR] + offset; + addr_buf.left = dwarf_sections->size[DEBUG_ADDR] - offset; + addr_buf.is_bigendian = is_bigendian; + addr_buf.error_callback = error_callback; + addr_buf.data = data; + addr_buf.reported_underflow = 0; + + *address = read_address(&addr_buf, addrsize); + return 1; +} + +static int units_search(const void *vkey, const void *ventry) { + const size_t *key = (const size_t *)vkey; + const struct unit *entry = *((const struct unit *const *)ventry); + size_t offset; + + offset = *key; + if (offset < entry->low_offset) + return -1; + else if (offset >= entry->high_offset) + return 1; + else + return 0; +} + +static struct unit *find_unit(struct unit **pu, size_t units_count, + size_t offset) { + struct unit **u; + u = bsearch(&offset, pu, units_count, sizeof(struct unit *), units_search); + return u == NULL ? NULL : *u; +} + +static int function_addrs_compare(const void *v1, const void *v2) { + const struct function_addrs *a1 = (const struct function_addrs *)v1; + const struct function_addrs *a2 = (const struct function_addrs *)v2; + + if (a1->low < a2->low) return -1; + if (a1->low > a2->low) return 1; + if (a1->high < a2->high) return 1; + if (a1->high > a2->high) return -1; + return strcmp(a1->function->name, a2->function->name); +} + +static int function_addrs_search(const void *vkey, const void *ventry) { + const uintptr_t *key = (const uintptr_t *)vkey; + const struct function_addrs *entry = (const struct function_addrs *)ventry; + uintptr_t pc; + + pc = *key; + if (pc < entry->low) + return -1; + else if (pc > (entry + 1)->low) + return 1; + else + return 0; +} + +static int add_unit_addr(struct backtrace_state *state, void *rdata, + uint64_t lowpc, uint64_t highpc, + backtrace_error_callback error_callback, void *data, + void *pvec) { + struct unit *u = (struct unit *)rdata; + struct unit_addrs_vector *vec = (struct unit_addrs_vector *)pvec; + struct unit_addrs *p; + + if (vec->count > 0) { + p = (struct unit_addrs *)vec->vec.base + (vec->count - 1); + if ((lowpc == p->high || lowpc == p->high + 1) && u == p->u) { + if (highpc > p->high) p->high = highpc; + return 1; + } + } + + p = ((struct unit_addrs *)backtrace_vector_grow( + state, sizeof(struct unit_addrs), error_callback, data, &vec->vec)); + if (p == NULL) return 0; + + p->low = lowpc; + p->high = highpc; + p->u = u; + + ++vec->count; + + return 1; +} + +static int unit_addrs_compare(const void *v1, const void *v2) { + const struct unit_addrs *a1 = (const struct unit_addrs *)v1; + const struct unit_addrs *a2 = (const struct unit_addrs *)v2; + + if (a1->low < a2->low) return -1; + if (a1->low > a2->low) return 1; + if (a1->high < a2->high) return 1; + if (a1->high > a2->high) return -1; + if (a1->u->lineoff < a2->u->lineoff) return -1; + if (a1->u->lineoff > a2->u->lineoff) return 1; + return 0; +} + +static int unit_addrs_search(const void *vkey, const void *ventry) { + const uintptr_t *key = (const uintptr_t *)vkey; + const struct unit_addrs *entry = (const struct unit_addrs *)ventry; + uintptr_t pc; + + pc = *key; + if (pc < entry->low) + return -1; + else if (pc > (entry + 1)->low) + return 1; + else + return 0; +} + +static int line_compare(const void *v1, const void *v2) { + const struct line *ln1 = (const struct line *)v1; + const struct line *ln2 = (const struct line *)v2; + + if (ln1->pc < ln2->pc) + return -1; + else if (ln1->pc > ln2->pc) + return 1; + else if (ln1->idx < ln2->idx) + return -1; + else if (ln1->idx > ln2->idx) + return 1; + else + return 0; +} + +static int line_search(const void *vkey, const void *ventry) { + const uintptr_t *key = (const uintptr_t *)vkey; + const struct line *entry = (const struct line *)ventry; + uintptr_t pc; + + pc = *key; + if (pc < entry->pc) + return -1; + else if (pc >= (entry + 1)->pc) + return 1; + else + return 0; +} + +static int abbrev_compare(const void *v1, const void *v2) { + const struct abbrev *a1 = (const struct abbrev *)v1; + const struct abbrev *a2 = (const struct abbrev *)v2; + + if (a1->code < a2->code) + return -1; + else if (a1->code > a2->code) + return 1; + else { + return 0; + } +} + +static int read_abbrevs(struct backtrace_state *state, uint64_t abbrev_offset, + const unsigned char *dwarf_abbrev, + size_t dwarf_abbrev_size, int is_bigendian, + backtrace_error_callback error_callback, void *data, + struct abbrevs *abbrevs) { + struct dwarf_buf abbrev_buf; + struct dwarf_buf count_buf; + size_t num_abbrevs; + + abbrevs->num_abbrevs = 0; + abbrevs->abbrevs = NULL; + + if (abbrev_offset >= dwarf_abbrev_size) { + error_callback(data, "abbrev offset out of range", 0); + return 0; + } + + abbrev_buf.name = ".debug_abbrev"; + abbrev_buf.start = dwarf_abbrev; + abbrev_buf.buf = dwarf_abbrev + abbrev_offset; + abbrev_buf.left = dwarf_abbrev_size - abbrev_offset; + abbrev_buf.is_bigendian = is_bigendian; + abbrev_buf.error_callback = error_callback; + abbrev_buf.data = data; + abbrev_buf.reported_underflow = 0; + + count_buf = abbrev_buf; + num_abbrevs = 0; + while (read_uleb128(&count_buf) != 0) { + if (count_buf.reported_underflow) return 0; + ++num_abbrevs; + + read_uleb128(&count_buf); + + read_byte(&count_buf); + + while (read_uleb128(&count_buf) != 0) { + uint64_t form; + + form = read_uleb128(&count_buf); + if ((enum dwarf_form)form == DW_FORM_implicit_const) + read_sleb128(&count_buf); + } + + read_uleb128(&count_buf); + } + + if (count_buf.reported_underflow) return 0; + + if (num_abbrevs == 0) return 1; + + abbrevs->abbrevs = ((struct abbrev *)backtrace_alloc( + state, num_abbrevs * sizeof(struct abbrev), error_callback, data)); + if (abbrevs->abbrevs == NULL) return 0; + abbrevs->num_abbrevs = num_abbrevs; + memset(abbrevs->abbrevs, 0, num_abbrevs * sizeof(struct abbrev)); + + num_abbrevs = 0; + while (1) { + uint64_t code; + struct abbrev a; + size_t num_attrs; + struct attr *attrs; + + if (abbrev_buf.reported_underflow) goto fail; + + code = read_uleb128(&abbrev_buf); + if (code == 0) break; + + a.code = code; + a.tag = (enum dwarf_tag)read_uleb128(&abbrev_buf); + a.has_children = read_byte(&abbrev_buf); + + count_buf = abbrev_buf; + num_attrs = 0; + while (read_uleb128(&count_buf) != 0) { + uint64_t form; + + ++num_attrs; + form = read_uleb128(&count_buf); + if ((enum dwarf_form)form == DW_FORM_implicit_const) + read_sleb128(&count_buf); + } + + if (num_attrs == 0) { + attrs = NULL; + read_uleb128(&abbrev_buf); + read_uleb128(&abbrev_buf); + } else { + attrs = ((struct attr *)backtrace_alloc(state, num_attrs * sizeof *attrs, + error_callback, data)); + if (attrs == NULL) goto fail; + num_attrs = 0; + while (1) { + uint64_t name; + uint64_t form; + + name = read_uleb128(&abbrev_buf); + form = read_uleb128(&abbrev_buf); + if (name == 0) break; + attrs[num_attrs].name = (enum dwarf_attribute)name; + attrs[num_attrs].form = (enum dwarf_form)form; + if ((enum dwarf_form)form == DW_FORM_implicit_const) + attrs[num_attrs].val = read_sleb128(&abbrev_buf); + else + attrs[num_attrs].val = 0; + ++num_attrs; + } + } + + a.num_attrs = num_attrs; + a.attrs = attrs; + + abbrevs->abbrevs[num_abbrevs] = a; + ++num_abbrevs; + } + + backtrace_qsort(abbrevs->abbrevs, abbrevs->num_abbrevs, sizeof(struct abbrev), + abbrev_compare); + + return 1; + +fail: + free_abbrevs(state, abbrevs, error_callback, data); + return 0; +} + +static const struct abbrev *lookup_abbrev( + struct abbrevs *abbrevs, uint64_t code, + backtrace_error_callback error_callback, void *data) { + struct abbrev key; + void *p; + + if (code - 1 < abbrevs->num_abbrevs && + abbrevs->abbrevs[code - 1].code == code) + return &abbrevs->abbrevs[code - 1]; + + memset(&key, 0, sizeof key); + key.code = code; + p = bsearch(&key, abbrevs->abbrevs, abbrevs->num_abbrevs, + sizeof(struct abbrev), abbrev_compare); + if (p == NULL) { + error_callback(data, "invalid abbreviation code", 0); + return NULL; + } + return (const struct abbrev *)p; +} + +struct pcrange { + uint64_t lowpc; + int have_lowpc; + int lowpc_is_addr_index; + uint64_t highpc; + int have_highpc; + int highpc_is_relative; + int highpc_is_addr_index; + uint64_t ranges; + int have_ranges; + int ranges_is_index; +}; + +static void update_pcrange(const struct attr *attr, const struct attr_val *val, + struct pcrange *pcrange) { + switch (attr->name) { + case DW_AT_low_pc: + if (val->encoding == ATTR_VAL_ADDRESS) { + pcrange->lowpc = val->u.uint; + pcrange->have_lowpc = 1; + } else if (val->encoding == ATTR_VAL_ADDRESS_INDEX) { + pcrange->lowpc = val->u.uint; + pcrange->have_lowpc = 1; + pcrange->lowpc_is_addr_index = 1; + } + break; + + case DW_AT_high_pc: + if (val->encoding == ATTR_VAL_ADDRESS) { + pcrange->highpc = val->u.uint; + pcrange->have_highpc = 1; + } else if (val->encoding == ATTR_VAL_UINT) { + pcrange->highpc = val->u.uint; + pcrange->have_highpc = 1; + pcrange->highpc_is_relative = 1; + } else if (val->encoding == ATTR_VAL_ADDRESS_INDEX) { + pcrange->highpc = val->u.uint; + pcrange->have_highpc = 1; + pcrange->highpc_is_addr_index = 1; + } + break; + + case DW_AT_ranges: + if (val->encoding == ATTR_VAL_UINT || + val->encoding == ATTR_VAL_REF_SECTION) { + pcrange->ranges = val->u.uint; + pcrange->have_ranges = 1; + } else if (val->encoding == ATTR_VAL_RNGLISTS_INDEX) { + pcrange->ranges = val->u.uint; + pcrange->have_ranges = 1; + pcrange->ranges_is_index = 1; + } + break; + + default: + break; + } +} + +static int add_low_high_range( + struct backtrace_state *state, const struct dwarf_sections *dwarf_sections, + uintptr_t base_address, int is_bigendian, struct unit *u, + const struct pcrange *pcrange, + int (*add_range)(struct backtrace_state *state, void *rdata, uint64_t lowpc, + uint64_t highpc, backtrace_error_callback error_callback, + void *data, void *vec), + void *rdata, backtrace_error_callback error_callback, void *data, + void *vec) { + uint64_t lowpc; + uint64_t highpc; + + lowpc = pcrange->lowpc; + if (pcrange->lowpc_is_addr_index) { + if (!resolve_addr_index(dwarf_sections, u->addr_base, u->addrsize, + is_bigendian, lowpc, error_callback, data, &lowpc)) + return 0; + } + + highpc = pcrange->highpc; + if (pcrange->highpc_is_addr_index) { + if (!resolve_addr_index(dwarf_sections, u->addr_base, u->addrsize, + is_bigendian, highpc, error_callback, data, + &highpc)) + return 0; + } + if (pcrange->highpc_is_relative) highpc += lowpc; + + lowpc += base_address; + highpc += base_address; + + return add_range(state, rdata, lowpc, highpc, error_callback, data, vec); +} + +static int add_ranges_from_ranges( + struct backtrace_state *state, const struct dwarf_sections *dwarf_sections, + uintptr_t base_address, int is_bigendian, struct unit *u, uint64_t base, + const struct pcrange *pcrange, + int (*add_range)(struct backtrace_state *state, void *rdata, uint64_t lowpc, + uint64_t highpc, backtrace_error_callback error_callback, + void *data, void *vec), + void *rdata, backtrace_error_callback error_callback, void *data, + void *vec) { + struct dwarf_buf ranges_buf; + + if (pcrange->ranges >= dwarf_sections->size[DEBUG_RANGES]) { + error_callback(data, "ranges offset out of range", 0); + return 0; + } + + ranges_buf.name = ".debug_ranges"; + ranges_buf.start = dwarf_sections->data[DEBUG_RANGES]; + ranges_buf.buf = dwarf_sections->data[DEBUG_RANGES] + pcrange->ranges; + ranges_buf.left = dwarf_sections->size[DEBUG_RANGES] - pcrange->ranges; + ranges_buf.is_bigendian = is_bigendian; + ranges_buf.error_callback = error_callback; + ranges_buf.data = data; + ranges_buf.reported_underflow = 0; + + while (1) { + uint64_t low; + uint64_t high; + + if (ranges_buf.reported_underflow) return 0; + + low = read_address(&ranges_buf, u->addrsize); + high = read_address(&ranges_buf, u->addrsize); + + if (low == 0 && high == 0) break; + + if (is_highest_address(low, u->addrsize)) + base = high; + else { + if (!add_range(state, rdata, low + base + base_address, + high + base + base_address, error_callback, data, vec)) + return 0; + } + } + + if (ranges_buf.reported_underflow) return 0; + + return 1; +} + +static int add_ranges_from_rnglists( + struct backtrace_state *state, const struct dwarf_sections *dwarf_sections, + uintptr_t base_address, int is_bigendian, struct unit *u, uint64_t base, + const struct pcrange *pcrange, + int (*add_range)(struct backtrace_state *state, void *rdata, uint64_t lowpc, + uint64_t highpc, backtrace_error_callback error_callback, + void *data, void *vec), + void *rdata, backtrace_error_callback error_callback, void *data, + void *vec) { + uint64_t offset; + struct dwarf_buf rnglists_buf; + + if (!pcrange->ranges_is_index) + offset = pcrange->ranges; + else + offset = u->rnglists_base + pcrange->ranges * (u->is_dwarf64 ? 8 : 4); + if (offset >= dwarf_sections->size[DEBUG_RNGLISTS]) { + error_callback(data, "rnglists offset out of range", 0); + return 0; + } + + rnglists_buf.name = ".debug_rnglists"; + rnglists_buf.start = dwarf_sections->data[DEBUG_RNGLISTS]; + rnglists_buf.buf = dwarf_sections->data[DEBUG_RNGLISTS] + offset; + rnglists_buf.left = dwarf_sections->size[DEBUG_RNGLISTS] - offset; + rnglists_buf.is_bigendian = is_bigendian; + rnglists_buf.error_callback = error_callback; + rnglists_buf.data = data; + rnglists_buf.reported_underflow = 0; + + if (pcrange->ranges_is_index) { + offset = read_offset(&rnglists_buf, u->is_dwarf64); + offset += u->rnglists_base; + if (offset >= dwarf_sections->size[DEBUG_RNGLISTS]) { + error_callback(data, "rnglists index offset out of range", 0); + return 0; + } + rnglists_buf.buf = dwarf_sections->data[DEBUG_RNGLISTS] + offset; + rnglists_buf.left = dwarf_sections->size[DEBUG_RNGLISTS] - offset; + } + + while (1) { + unsigned char rle; + + rle = read_byte(&rnglists_buf); + if (rle == DW_RLE_end_of_list) break; + switch (rle) { + case DW_RLE_base_addressx: { + uint64_t index; + + index = read_uleb128(&rnglists_buf); + if (!resolve_addr_index(dwarf_sections, u->addr_base, u->addrsize, + is_bigendian, index, error_callback, data, + &base)) + return 0; + } break; + + case DW_RLE_startx_endx: { + uint64_t index; + uint64_t low; + uint64_t high; + + index = read_uleb128(&rnglists_buf); + if (!resolve_addr_index(dwarf_sections, u->addr_base, u->addrsize, + is_bigendian, index, error_callback, data, + &low)) + return 0; + index = read_uleb128(&rnglists_buf); + if (!resolve_addr_index(dwarf_sections, u->addr_base, u->addrsize, + is_bigendian, index, error_callback, data, + &high)) + return 0; + if (!add_range(state, rdata, low + base_address, high + base_address, + error_callback, data, vec)) + return 0; + } break; + + case DW_RLE_startx_length: { + uint64_t index; + uint64_t low; + uint64_t length; + + index = read_uleb128(&rnglists_buf); + if (!resolve_addr_index(dwarf_sections, u->addr_base, u->addrsize, + is_bigendian, index, error_callback, data, + &low)) + return 0; + length = read_uleb128(&rnglists_buf); + low += base_address; + if (!add_range(state, rdata, low, low + length, error_callback, data, + vec)) + return 0; + } break; + + case DW_RLE_offset_pair: { + uint64_t low; + uint64_t high; + + low = read_uleb128(&rnglists_buf); + high = read_uleb128(&rnglists_buf); + if (!add_range(state, rdata, low + base + base_address, + high + base + base_address, error_callback, data, vec)) + return 0; + } break; + + case DW_RLE_base_address: + base = read_address(&rnglists_buf, u->addrsize); + break; + + case DW_RLE_start_end: { + uint64_t low; + uint64_t high; + + low = read_address(&rnglists_buf, u->addrsize); + high = read_address(&rnglists_buf, u->addrsize); + if (!add_range(state, rdata, low + base_address, high + base_address, + error_callback, data, vec)) + return 0; + } break; + + case DW_RLE_start_length: { + uint64_t low; + uint64_t length; + + low = read_address(&rnglists_buf, u->addrsize); + length = read_uleb128(&rnglists_buf); + low += base_address; + if (!add_range(state, rdata, low, low + length, error_callback, data, + vec)) + return 0; + } break; + + default: + dwarf_buf_error(&rnglists_buf, "unrecognized DW_RLE value", -1); + return 0; + } + } + + if (rnglists_buf.reported_underflow) return 0; + + return 1; +} + +static int add_ranges( + struct backtrace_state *state, const struct dwarf_sections *dwarf_sections, + uintptr_t base_address, int is_bigendian, struct unit *u, uint64_t base, + const struct pcrange *pcrange, + int (*add_range)(struct backtrace_state *state, void *rdata, uint64_t lowpc, + uint64_t highpc, backtrace_error_callback error_callback, + void *data, void *vec), + void *rdata, backtrace_error_callback error_callback, void *data, + void *vec) { + if (pcrange->have_lowpc && pcrange->have_highpc) + return add_low_high_range(state, dwarf_sections, base_address, is_bigendian, + u, pcrange, add_range, rdata, error_callback, + data, vec); + + if (!pcrange->have_ranges) { + return 1; + } + + if (u->version < 5) + return add_ranges_from_ranges(state, dwarf_sections, base_address, + is_bigendian, u, base, pcrange, add_range, + rdata, error_callback, data, vec); + else + return add_ranges_from_rnglists(state, dwarf_sections, base_address, + is_bigendian, u, base, pcrange, add_range, + rdata, error_callback, data, vec); +} + +static int find_address_ranges( + struct backtrace_state *state, uintptr_t base_address, + struct dwarf_buf *unit_buf, const struct dwarf_sections *dwarf_sections, + int is_bigendian, struct dwarf_data *altlink, + backtrace_error_callback error_callback, void *data, struct unit *u, + struct unit_addrs_vector *addrs, enum dwarf_tag *unit_tag) { + while (unit_buf->left > 0) { + uint64_t code; + const struct abbrev *abbrev; + struct pcrange pcrange; + struct attr_val name_val; + int have_name_val; + struct attr_val comp_dir_val; + int have_comp_dir_val; + size_t i; + + code = read_uleb128(unit_buf); + if (code == 0) return 1; + + abbrev = lookup_abbrev(&u->abbrevs, code, error_callback, data); + if (abbrev == NULL) return 0; + + if (unit_tag != NULL) *unit_tag = abbrev->tag; + + memset(&pcrange, 0, sizeof pcrange); + memset(&name_val, 0, sizeof name_val); + have_name_val = 0; + memset(&comp_dir_val, 0, sizeof comp_dir_val); + have_comp_dir_val = 0; + for (i = 0; i < abbrev->num_attrs; ++i) { + struct attr_val val; + + if (!read_attribute(abbrev->attrs[i].form, abbrev->attrs[i].val, unit_buf, + u->is_dwarf64, u->version, u->addrsize, + dwarf_sections, altlink, &val)) + return 0; + + switch (abbrev->attrs[i].name) { + case DW_AT_low_pc: + case DW_AT_high_pc: + case DW_AT_ranges: + update_pcrange(&abbrev->attrs[i], &val, &pcrange); + break; + + case DW_AT_stmt_list: + if ((abbrev->tag == DW_TAG_compile_unit || + abbrev->tag == DW_TAG_skeleton_unit) && + (val.encoding == ATTR_VAL_UINT || + val.encoding == ATTR_VAL_REF_SECTION)) + u->lineoff = val.u.uint; + break; + + case DW_AT_name: + if (abbrev->tag == DW_TAG_compile_unit || + abbrev->tag == DW_TAG_skeleton_unit) { + name_val = val; + have_name_val = 1; + } + break; + + case DW_AT_comp_dir: + if (abbrev->tag == DW_TAG_compile_unit || + abbrev->tag == DW_TAG_skeleton_unit) { + comp_dir_val = val; + have_comp_dir_val = 1; + } + break; + + case DW_AT_str_offsets_base: + if ((abbrev->tag == DW_TAG_compile_unit || + abbrev->tag == DW_TAG_skeleton_unit) && + val.encoding == ATTR_VAL_REF_SECTION) + u->str_offsets_base = val.u.uint; + break; + + case DW_AT_addr_base: + if ((abbrev->tag == DW_TAG_compile_unit || + abbrev->tag == DW_TAG_skeleton_unit) && + val.encoding == ATTR_VAL_REF_SECTION) + u->addr_base = val.u.uint; + break; + + case DW_AT_rnglists_base: + if ((abbrev->tag == DW_TAG_compile_unit || + abbrev->tag == DW_TAG_skeleton_unit) && + val.encoding == ATTR_VAL_REF_SECTION) + u->rnglists_base = val.u.uint; + break; + + default: + break; + } + } + + if (have_name_val) { + if (!resolve_string(dwarf_sections, u->is_dwarf64, is_bigendian, + u->str_offsets_base, &name_val, error_callback, data, + &u->filename)) + return 0; + } + if (have_comp_dir_val) { + if (!resolve_string(dwarf_sections, u->is_dwarf64, is_bigendian, + u->str_offsets_base, &comp_dir_val, error_callback, + data, &u->comp_dir)) + return 0; + } + + if (abbrev->tag == DW_TAG_compile_unit || + abbrev->tag == DW_TAG_subprogram || + abbrev->tag == DW_TAG_skeleton_unit) { + if (!add_ranges(state, dwarf_sections, base_address, is_bigendian, u, + pcrange.lowpc, &pcrange, add_unit_addr, (void *)u, + error_callback, data, (void *)addrs)) + return 0; + + if ((abbrev->tag == DW_TAG_compile_unit || + abbrev->tag == DW_TAG_skeleton_unit) && + (pcrange.have_ranges || (pcrange.have_lowpc && pcrange.have_highpc))) + return 1; + } + + if (abbrev->has_children) { + if (!find_address_ranges(state, base_address, unit_buf, dwarf_sections, + is_bigendian, altlink, error_callback, data, u, + addrs, NULL)) + return 0; + } + } + + return 1; +} + +static int build_address_map(struct backtrace_state *state, + uintptr_t base_address, + const struct dwarf_sections *dwarf_sections, + int is_bigendian, struct dwarf_data *altlink, + backtrace_error_callback error_callback, + void *data, struct unit_addrs_vector *addrs, + struct unit_vector *unit_vec) { + struct dwarf_buf info; + struct backtrace_vector units; + size_t units_count; + size_t i; + struct unit **pu; + size_t unit_offset = 0; + struct unit_addrs *pa; + + memset(&addrs->vec, 0, sizeof addrs->vec); + memset(&unit_vec->vec, 0, sizeof unit_vec->vec); + addrs->count = 0; + unit_vec->count = 0; + + info.name = ".debug_info"; + info.start = dwarf_sections->data[DEBUG_INFO]; + info.buf = info.start; + info.left = dwarf_sections->size[DEBUG_INFO]; + info.is_bigendian = is_bigendian; + info.error_callback = error_callback; + info.data = data; + info.reported_underflow = 0; + + memset(&units, 0, sizeof units); + units_count = 0; + + while (info.left > 0) { + const unsigned char *unit_data_start; + uint64_t len; + int is_dwarf64; + struct dwarf_buf unit_buf; + int version; + int unit_type; + uint64_t abbrev_offset; + int addrsize; + struct unit *u; + enum dwarf_tag unit_tag; + + if (info.reported_underflow) goto fail; + + unit_data_start = info.buf; + + len = read_initial_length(&info, &is_dwarf64); + unit_buf = info; + unit_buf.left = len; + + if (!advance(&info, len)) goto fail; + + version = read_uint16(&unit_buf); + if (version < 2 || version > 5) { + dwarf_buf_error(&unit_buf, "unrecognized DWARF version", -1); + goto fail; + } + + if (version < 5) + unit_type = 0; + else { + unit_type = read_byte(&unit_buf); + if (unit_type == DW_UT_type || unit_type == DW_UT_split_type) { + continue; + } + } + + pu = ((struct unit **)backtrace_vector_grow(state, sizeof(struct unit *), + error_callback, data, &units)); + if (pu == NULL) goto fail; + + u = ((struct unit *)backtrace_alloc(state, sizeof *u, error_callback, + data)); + if (u == NULL) goto fail; + + *pu = u; + ++units_count; + + if (version < 5) + addrsize = 0; + else + addrsize = read_byte(&unit_buf); + + memset(&u->abbrevs, 0, sizeof u->abbrevs); + abbrev_offset = read_offset(&unit_buf, is_dwarf64); + if (!read_abbrevs(state, abbrev_offset, dwarf_sections->data[DEBUG_ABBREV], + dwarf_sections->size[DEBUG_ABBREV], is_bigendian, + error_callback, data, &u->abbrevs)) + goto fail; + + if (version < 5) addrsize = read_byte(&unit_buf); + + switch (unit_type) { + case 0: + break; + case DW_UT_compile: + case DW_UT_partial: + break; + case DW_UT_skeleton: + case DW_UT_split_compile: + read_uint64(&unit_buf); + break; + default: + break; + } + + u->low_offset = unit_offset; + unit_offset += len + (is_dwarf64 ? 12 : 4); + u->high_offset = unit_offset; + u->unit_data = unit_buf.buf; + u->unit_data_len = unit_buf.left; + u->unit_data_offset = unit_buf.buf - unit_data_start; + u->version = version; + u->is_dwarf64 = is_dwarf64; + u->addrsize = addrsize; + u->filename = NULL; + u->comp_dir = NULL; + u->abs_filename = NULL; + u->lineoff = 0; + u->str_offsets_base = 0; + u->addr_base = 0; + u->rnglists_base = 0; + + u->lines = NULL; + u->lines_count = 0; + u->function_addrs = NULL; + u->function_addrs_count = 0; + + if (!find_address_ranges(state, base_address, &unit_buf, dwarf_sections, + is_bigendian, altlink, error_callback, data, u, + addrs, &unit_tag)) + goto fail; + + if (unit_buf.reported_underflow) goto fail; + } + if (info.reported_underflow) goto fail; + + pa = ((struct unit_addrs *)backtrace_vector_grow( + state, sizeof(struct unit_addrs), error_callback, data, &addrs->vec)); + if (pa == NULL) goto fail; + pa->low = 0; + --pa->low; + pa->high = pa->low; + pa->u = NULL; + + unit_vec->vec = units; + unit_vec->count = units_count; + return 1; + +fail: + if (units_count > 0) { + pu = (struct unit **)units.base; + for (i = 0; i < units_count; i++) { + free_abbrevs(state, &pu[i]->abbrevs, error_callback, data); + backtrace_free(state, pu[i], sizeof **pu, error_callback, data); + } + backtrace_vector_free(state, &units, error_callback, data); + } + if (addrs->count > 0) { + backtrace_vector_free(state, &addrs->vec, error_callback, data); + addrs->count = 0; + } + return 0; +} + +static int add_line(struct backtrace_state *state, struct dwarf_data *ddata, + uintptr_t pc, const char *filename, int lineno, + backtrace_error_callback error_callback, void *data, + struct line_vector *vec) { + struct line *ln; + + if (vec->count > 0) { + ln = (struct line *)vec->vec.base + (vec->count - 1); + if (pc == ln->pc && filename == ln->filename && lineno == ln->lineno) + return 1; + } + + ln = ((struct line *)backtrace_vector_grow(state, sizeof(struct line), + error_callback, data, &vec->vec)); + if (ln == NULL) return 0; + + ln->pc = pc + ddata->base_address; + + ln->filename = filename; + ln->lineno = lineno; + ln->idx = vec->count; + + ++vec->count; + + return 1; +} + +static void free_line_header(struct backtrace_state *state, + struct line_header *hdr, + backtrace_error_callback error_callback, + void *data) { + if (hdr->dirs_count != 0) + backtrace_free(state, hdr->dirs, hdr->dirs_count * sizeof(const char *), + error_callback, data); + backtrace_free(state, hdr->filenames, hdr->filenames_count * sizeof(char *), + error_callback, data); +} + +static int read_v2_paths(struct backtrace_state *state, struct unit *u, + struct dwarf_buf *hdr_buf, struct line_header *hdr) { + const unsigned char *p; + const unsigned char *pend; + size_t i; + + hdr->dirs_count = 0; + p = hdr_buf->buf; + pend = p + hdr_buf->left; + while (p < pend && *p != '\0') { + p += strnlen((const char *)p, pend - p) + 1; + ++hdr->dirs_count; + } + + ++hdr->dirs_count; + hdr->dirs = ((const char **)backtrace_alloc( + state, hdr->dirs_count * sizeof(const char *), hdr_buf->error_callback, + hdr_buf->data)); + if (hdr->dirs == NULL) return 0; + + hdr->dirs[0] = u->comp_dir; + i = 1; + while (*hdr_buf->buf != '\0') { + if (hdr_buf->reported_underflow) return 0; + + hdr->dirs[i] = read_string(hdr_buf); + if (hdr->dirs[i] == NULL) return 0; + ++i; + } + if (!advance(hdr_buf, 1)) return 0; + + hdr->filenames_count = 0; + p = hdr_buf->buf; + pend = p + hdr_buf->left; + while (p < pend && *p != '\0') { + p += strnlen((const char *)p, pend - p) + 1; + p += leb128_len(p); + p += leb128_len(p); + p += leb128_len(p); + ++hdr->filenames_count; + } + + ++hdr->filenames_count; + hdr->filenames = ((const char **)backtrace_alloc( + state, hdr->filenames_count * sizeof(char *), hdr_buf->error_callback, + hdr_buf->data)); + if (hdr->filenames == NULL) return 0; + hdr->filenames[0] = u->filename; + i = 1; + while (*hdr_buf->buf != '\0') { + const char *filename; + uint64_t dir_index; + + if (hdr_buf->reported_underflow) return 0; + + filename = read_string(hdr_buf); + if (filename == NULL) return 0; + dir_index = read_uleb128(hdr_buf); + if (IS_ABSOLUTE_PATH(filename) || + (dir_index < hdr->dirs_count && hdr->dirs[dir_index] == NULL)) + hdr->filenames[i] = filename; + else { + const char *dir; + size_t dir_len; + size_t filename_len; + char *s; + + if (dir_index < hdr->dirs_count) + dir = hdr->dirs[dir_index]; + else { + dwarf_buf_error(hdr_buf, + ("invalid directory index in " + "line number program header"), + 0); + return 0; + } + dir_len = strlen(dir); + filename_len = strlen(filename); + s = ((char *)backtrace_alloc(state, dir_len + filename_len + 2, + hdr_buf->error_callback, hdr_buf->data)); + if (s == NULL) return 0; + memcpy(s, dir, dir_len); + + s[dir_len] = '/'; + memcpy(s + dir_len + 1, filename, filename_len + 1); + hdr->filenames[i] = s; + } + + read_uleb128(hdr_buf); + read_uleb128(hdr_buf); + + ++i; + } + + return 1; +} + +static int read_lnct(struct backtrace_state *state, struct dwarf_data *ddata, + struct unit *u, struct dwarf_buf *hdr_buf, + const struct line_header *hdr, size_t formats_count, + const struct line_header_format *formats, + const char **string) { + size_t i; + const char *dir; + const char *path; + + dir = NULL; + path = NULL; + for (i = 0; i < formats_count; i++) { + struct attr_val val; + + if (!read_attribute(formats[i].form, 0, hdr_buf, u->is_dwarf64, u->version, + hdr->addrsize, &ddata->dwarf_sections, ddata->altlink, + &val)) + return 0; + switch (formats[i].lnct) { + case DW_LNCT_path: + if (!resolve_string(&ddata->dwarf_sections, u->is_dwarf64, + ddata->is_bigendian, u->str_offsets_base, &val, + hdr_buf->error_callback, hdr_buf->data, &path)) + return 0; + break; + case DW_LNCT_directory_index: + if (val.encoding == ATTR_VAL_UINT) { + if (val.u.uint >= hdr->dirs_count) { + dwarf_buf_error(hdr_buf, + ("invalid directory index in " + "line number program header"), + 0); + return 0; + } + dir = hdr->dirs[val.u.uint]; + } + break; + default: + + break; + } + } + + if (path == NULL) { + dwarf_buf_error(hdr_buf, "missing file name in line number program header", + 0); + return 0; + } + + if (dir == NULL) + *string = path; + else { + size_t dir_len; + size_t path_len; + char *s; + + dir_len = strlen(dir); + path_len = strlen(path); + s = (char *)backtrace_alloc(state, dir_len + path_len + 2, + hdr_buf->error_callback, hdr_buf->data); + if (s == NULL) return 0; + memcpy(s, dir, dir_len); + + s[dir_len] = '/'; + memcpy(s + dir_len + 1, path, path_len + 1); + *string = s; + } + + return 1; +} + +static int read_line_header_format_entries( + struct backtrace_state *state, struct dwarf_data *ddata, struct unit *u, + struct dwarf_buf *hdr_buf, struct line_header *hdr, size_t *pcount, + const char ***ppaths) { + size_t formats_count; + struct line_header_format *formats; + size_t paths_count; + const char **paths; + size_t i; + int ret; + + formats_count = read_byte(hdr_buf); + if (formats_count == 0) + formats = NULL; + else { + formats = ((struct line_header_format *)backtrace_alloc( + state, (formats_count * sizeof(struct line_header_format)), + hdr_buf->error_callback, hdr_buf->data)); + if (formats == NULL) return 0; + + for (i = 0; i < formats_count; i++) { + formats[i].lnct = (int)read_uleb128(hdr_buf); + formats[i].form = (enum dwarf_form)read_uleb128(hdr_buf); + } + } + + paths_count = read_uleb128(hdr_buf); + if (paths_count == 0) { + *pcount = 0; + *ppaths = NULL; + ret = 1; + goto exit; + } + + paths = + ((const char **)backtrace_alloc(state, paths_count * sizeof(const char *), + hdr_buf->error_callback, hdr_buf->data)); + if (paths == NULL) { + ret = 0; + goto exit; + } + for (i = 0; i < paths_count; i++) { + if (!read_lnct(state, ddata, u, hdr_buf, hdr, formats_count, formats, + &paths[i])) { + backtrace_free(state, paths, paths_count * sizeof(const char *), + hdr_buf->error_callback, hdr_buf->data); + ret = 0; + goto exit; + } + } + + *pcount = paths_count; + *ppaths = paths; + + ret = 1; + +exit: + if (formats != NULL) + backtrace_free(state, formats, + formats_count * sizeof(struct line_header_format), + hdr_buf->error_callback, hdr_buf->data); + + return ret; +} + +static int read_line_header(struct backtrace_state *state, + struct dwarf_data *ddata, struct unit *u, + int is_dwarf64, struct dwarf_buf *line_buf, + struct line_header *hdr) { + uint64_t hdrlen; + struct dwarf_buf hdr_buf; + + hdr->version = read_uint16(line_buf); + if (hdr->version < 2 || hdr->version > 5) { + dwarf_buf_error(line_buf, "unsupported line number version", -1); + return 0; + } + + if (hdr->version < 5) + hdr->addrsize = u->addrsize; + else { + hdr->addrsize = read_byte(line_buf); + + if (read_byte(line_buf) != 0) { + dwarf_buf_error(line_buf, "non-zero segment_selector_size not supported", + -1); + return 0; + } + } + + hdrlen = read_offset(line_buf, is_dwarf64); + + hdr_buf = *line_buf; + hdr_buf.left = hdrlen; + + if (!advance(line_buf, hdrlen)) return 0; + + hdr->min_insn_len = read_byte(&hdr_buf); + if (hdr->version < 4) + hdr->max_ops_per_insn = 1; + else + hdr->max_ops_per_insn = read_byte(&hdr_buf); + + read_byte(&hdr_buf); + + hdr->line_base = read_sbyte(&hdr_buf); + hdr->line_range = read_byte(&hdr_buf); + + hdr->opcode_base = read_byte(&hdr_buf); + hdr->opcode_lengths = hdr_buf.buf; + if (!advance(&hdr_buf, hdr->opcode_base - 1)) return 0; + + if (hdr->version < 5) { + if (!read_v2_paths(state, u, &hdr_buf, hdr)) return 0; + } else { + if (!read_line_header_format_entries(state, ddata, u, &hdr_buf, hdr, + &hdr->dirs_count, &hdr->dirs)) + return 0; + if (!read_line_header_format_entries(state, ddata, u, &hdr_buf, hdr, + &hdr->filenames_count, + &hdr->filenames)) + return 0; + } + + if (hdr_buf.reported_underflow) return 0; + + return 1; +} + +static int read_line_program(struct backtrace_state *state, + struct dwarf_data *ddata, + const struct line_header *hdr, + struct dwarf_buf *line_buf, + struct line_vector *vec) { + uint64_t address; + unsigned int op_index; + const char *reset_filename; + const char *filename; + int lineno; + + address = 0; + op_index = 0; + if (hdr->filenames_count > 1) + reset_filename = hdr->filenames[1]; + else + reset_filename = ""; + filename = reset_filename; + lineno = 1; + while (line_buf->left > 0) { + unsigned int op; + + op = read_byte(line_buf); + if (op >= hdr->opcode_base) { + unsigned int advance; + + op -= hdr->opcode_base; + advance = op / hdr->line_range; + address += + (hdr->min_insn_len * (op_index + advance) / hdr->max_ops_per_insn); + op_index = (op_index + advance) % hdr->max_ops_per_insn; + lineno += hdr->line_base + (int)(op % hdr->line_range); + add_line(state, ddata, address, filename, lineno, + line_buf->error_callback, line_buf->data, vec); + } else if (op == DW_LNS_extended_op) { + uint64_t len; + + len = read_uleb128(line_buf); + op = read_byte(line_buf); + switch (op) { + case DW_LNE_end_sequence: + + address = 0; + op_index = 0; + filename = reset_filename; + lineno = 1; + break; + case DW_LNE_set_address: + address = read_address(line_buf, hdr->addrsize); + break; + case DW_LNE_define_file: { + const char *f; + unsigned int dir_index; + + f = read_string(line_buf); + if (f == NULL) return 0; + dir_index = read_uleb128(line_buf); + + read_uleb128(line_buf); + read_uleb128(line_buf); + if (IS_ABSOLUTE_PATH(f)) + filename = f; + else { + const char *dir; + size_t dir_len; + size_t f_len; + char *p; + + if (dir_index < hdr->dirs_count) + dir = hdr->dirs[dir_index]; + else { + dwarf_buf_error(line_buf, + ("invalid directory index " + "in line number program"), + 0); + return 0; + } + dir_len = strlen(dir); + f_len = strlen(f); + p = ((char *)backtrace_alloc(state, dir_len + f_len + 2, + line_buf->error_callback, + line_buf->data)); + if (p == NULL) return 0; + memcpy(p, dir, dir_len); + + p[dir_len] = '/'; + memcpy(p + dir_len + 1, f, f_len + 1); + filename = p; + } + } break; + case DW_LNE_set_discriminator: + + read_uleb128(line_buf); + break; + default: + if (!advance(line_buf, len - 1)) return 0; + break; + } + } else { + switch (op) { + case DW_LNS_copy: + add_line(state, ddata, address, filename, lineno, + line_buf->error_callback, line_buf->data, vec); + break; + case DW_LNS_advance_pc: { + uint64_t advance; + + advance = read_uleb128(line_buf); + address += (hdr->min_insn_len * (op_index + advance) / + hdr->max_ops_per_insn); + op_index = (op_index + advance) % hdr->max_ops_per_insn; + } break; + case DW_LNS_advance_line: + lineno += (int)read_sleb128(line_buf); + break; + case DW_LNS_set_file: { + uint64_t fileno; + + fileno = read_uleb128(line_buf); + if (fileno >= hdr->filenames_count) { + dwarf_buf_error(line_buf, + ("invalid file number in " + "line number program"), + 0); + return 0; + } + filename = hdr->filenames[fileno]; + } break; + case DW_LNS_set_column: + read_uleb128(line_buf); + break; + case DW_LNS_negate_stmt: + break; + case DW_LNS_set_basic_block: + break; + case DW_LNS_const_add_pc: { + unsigned int advance; + + op = 255 - hdr->opcode_base; + advance = op / hdr->line_range; + address += (hdr->min_insn_len * (op_index + advance) / + hdr->max_ops_per_insn); + op_index = (op_index + advance) % hdr->max_ops_per_insn; + } break; + case DW_LNS_fixed_advance_pc: + address += read_uint16(line_buf); + op_index = 0; + break; + case DW_LNS_set_prologue_end: + break; + case DW_LNS_set_epilogue_begin: + break; + case DW_LNS_set_isa: + read_uleb128(line_buf); + break; + default: { + unsigned int i; + + for (i = hdr->opcode_lengths[op - 1]; i > 0; --i) + read_uleb128(line_buf); + } break; + } + } + } + + return 1; +} + +static int read_line_info(struct backtrace_state *state, + struct dwarf_data *ddata, + backtrace_error_callback error_callback, void *data, + struct unit *u, struct line_header *hdr, + struct line **lines, size_t *lines_count) { + struct line_vector vec; + struct dwarf_buf line_buf; + uint64_t len; + int is_dwarf64; + struct line *ln; + + memset(&vec.vec, 0, sizeof vec.vec); + vec.count = 0; + + memset(hdr, 0, sizeof *hdr); + + if (u->lineoff != (off_t)(size_t)u->lineoff || + (size_t)u->lineoff >= ddata->dwarf_sections.size[DEBUG_LINE]) { + error_callback(data, "unit line offset out of range", 0); + goto fail; + } + + line_buf.name = ".debug_line"; + line_buf.start = ddata->dwarf_sections.data[DEBUG_LINE]; + line_buf.buf = ddata->dwarf_sections.data[DEBUG_LINE] + u->lineoff; + line_buf.left = ddata->dwarf_sections.size[DEBUG_LINE] - u->lineoff; + line_buf.is_bigendian = ddata->is_bigendian; + line_buf.error_callback = error_callback; + line_buf.data = data; + line_buf.reported_underflow = 0; + + len = read_initial_length(&line_buf, &is_dwarf64); + line_buf.left = len; + + if (!read_line_header(state, ddata, u, is_dwarf64, &line_buf, hdr)) goto fail; + + if (!read_line_program(state, ddata, hdr, &line_buf, &vec)) goto fail; + + if (line_buf.reported_underflow) goto fail; + + if (vec.count == 0) { + goto fail; + } + + ln = ((struct line *)backtrace_vector_grow(state, sizeof(struct line), + error_callback, data, &vec.vec)); + if (ln == NULL) goto fail; + ln->pc = (uintptr_t)-1; + ln->filename = NULL; + ln->lineno = 0; + ln->idx = 0; + + if (!backtrace_vector_release(state, &vec.vec, error_callback, data)) + goto fail; + + ln = (struct line *)vec.vec.base; + backtrace_qsort(ln, vec.count, sizeof(struct line), line_compare); + + *lines = ln; + *lines_count = vec.count; + + return 1; + +fail: + backtrace_vector_free(state, &vec.vec, error_callback, data); + free_line_header(state, hdr, error_callback, data); + *lines = (struct line *)(uintptr_t)-1; + *lines_count = 0; + return 0; +} + +static const char *read_referenced_name(struct dwarf_data *, struct unit *, + uint64_t, backtrace_error_callback, + void *); + +static const char *read_referenced_name_from_attr( + struct dwarf_data *ddata, struct unit *u, struct attr *attr, + struct attr_val *val, backtrace_error_callback error_callback, void *data) { + switch (attr->name) { + case DW_AT_abstract_origin: + case DW_AT_specification: + break; + default: + return NULL; + } + + if (attr->form == DW_FORM_ref_sig8) return NULL; + + if (val->encoding == ATTR_VAL_REF_INFO) { + struct unit *unit = + find_unit(ddata->units, ddata->units_count, val->u.uint); + if (unit == NULL) return NULL; + + uint64_t offset = val->u.uint - unit->low_offset; + return read_referenced_name(ddata, unit, offset, error_callback, data); + } + + if (val->encoding == ATTR_VAL_UINT || val->encoding == ATTR_VAL_REF_UNIT) + return read_referenced_name(ddata, u, val->u.uint, error_callback, data); + + if (val->encoding == ATTR_VAL_REF_ALT_INFO) { + struct unit *alt_unit = find_unit(ddata->altlink->units, + ddata->altlink->units_count, val->u.uint); + if (alt_unit == NULL) return NULL; + + uint64_t offset = val->u.uint - alt_unit->low_offset; + return read_referenced_name(ddata->altlink, alt_unit, offset, + error_callback, data); + } + + return NULL; +} + +static const char *read_referenced_name(struct dwarf_data *ddata, + struct unit *u, uint64_t offset, + backtrace_error_callback error_callback, + void *data) { + struct dwarf_buf unit_buf; + uint64_t code; + const struct abbrev *abbrev; + const char *ret; + size_t i; + + if (offset < u->unit_data_offset || + offset - u->unit_data_offset >= u->unit_data_len) { + error_callback(data, "abstract origin or specification out of range", 0); + return NULL; + } + + offset -= u->unit_data_offset; + + unit_buf.name = ".debug_info"; + unit_buf.start = ddata->dwarf_sections.data[DEBUG_INFO]; + unit_buf.buf = u->unit_data + offset; + unit_buf.left = u->unit_data_len - offset; + unit_buf.is_bigendian = ddata->is_bigendian; + unit_buf.error_callback = error_callback; + unit_buf.data = data; + unit_buf.reported_underflow = 0; + + code = read_uleb128(&unit_buf); + if (code == 0) { + dwarf_buf_error(&unit_buf, "invalid abstract origin or specification", 0); + return NULL; + } + + abbrev = lookup_abbrev(&u->abbrevs, code, error_callback, data); + if (abbrev == NULL) return NULL; + + ret = NULL; + for (i = 0; i < abbrev->num_attrs; ++i) { + struct attr_val val; + + if (!read_attribute(abbrev->attrs[i].form, abbrev->attrs[i].val, &unit_buf, + u->is_dwarf64, u->version, u->addrsize, + &ddata->dwarf_sections, ddata->altlink, &val)) + return NULL; + + switch (abbrev->attrs[i].name) { + case DW_AT_name: + + if (ret != NULL) break; + if (!resolve_string(&ddata->dwarf_sections, u->is_dwarf64, + ddata->is_bigendian, u->str_offsets_base, &val, + error_callback, data, &ret)) + return NULL; + break; + + case DW_AT_linkage_name: + case DW_AT_MIPS_linkage_name: + + { + const char *s; + + s = NULL; + if (!resolve_string(&ddata->dwarf_sections, u->is_dwarf64, + ddata->is_bigendian, u->str_offsets_base, &val, + error_callback, data, &s)) + return NULL; + if (s != NULL) return s; + } break; + + case DW_AT_specification: + + { + const char *name; + + name = read_referenced_name_from_attr(ddata, u, &abbrev->attrs[i], &val, + error_callback, data); + if (name != NULL) ret = name; + } break; + + default: + break; + } + } + + return ret; +} + +static int add_function_range(struct backtrace_state *state, void *rdata, + uint64_t lowpc, uint64_t highpc, + backtrace_error_callback error_callback, + void *data, void *pvec) { + struct function *function = (struct function *)rdata; + struct function_vector *vec = (struct function_vector *)pvec; + struct function_addrs *p; + + if (vec->count > 0) { + p = (struct function_addrs *)vec->vec.base + (vec->count - 1); + if ((lowpc == p->high || lowpc == p->high + 1) && function == p->function) { + if (highpc > p->high) p->high = highpc; + return 1; + } + } + + p = ((struct function_addrs *)backtrace_vector_grow( + state, sizeof(struct function_addrs), error_callback, data, &vec->vec)); + if (p == NULL) return 0; + + p->low = lowpc; + p->high = highpc; + p->function = function; + + ++vec->count; + + return 1; +} + +static int read_function_entry(struct backtrace_state *state, + struct dwarf_data *ddata, struct unit *u, + uint64_t base, struct dwarf_buf *unit_buf, + const struct line_header *lhdr, + backtrace_error_callback error_callback, + void *data, struct function_vector *vec_function, + struct function_vector *vec_inlined) { + while (unit_buf->left > 0) { + uint64_t code; + const struct abbrev *abbrev; + int is_function; + struct function *function; + struct function_vector *vec; + size_t i; + struct pcrange pcrange; + int have_linkage_name; + + code = read_uleb128(unit_buf); + if (code == 0) return 1; + + abbrev = lookup_abbrev(&u->abbrevs, code, error_callback, data); + if (abbrev == NULL) return 0; + + is_function = (abbrev->tag == DW_TAG_subprogram || + abbrev->tag == DW_TAG_entry_point || + abbrev->tag == DW_TAG_inlined_subroutine); + + if (abbrev->tag == DW_TAG_inlined_subroutine) + vec = vec_inlined; + else + vec = vec_function; + + function = NULL; + if (is_function) { + function = ((struct function *)backtrace_alloc(state, sizeof *function, + error_callback, data)); + if (function == NULL) return 0; + memset(function, 0, sizeof *function); + } + + memset(&pcrange, 0, sizeof pcrange); + have_linkage_name = 0; + for (i = 0; i < abbrev->num_attrs; ++i) { + struct attr_val val; + + if (!read_attribute(abbrev->attrs[i].form, abbrev->attrs[i].val, unit_buf, + u->is_dwarf64, u->version, u->addrsize, + &ddata->dwarf_sections, ddata->altlink, &val)) + return 0; + + if ((abbrev->tag == DW_TAG_compile_unit || + abbrev->tag == DW_TAG_skeleton_unit) && + abbrev->attrs[i].name == DW_AT_low_pc) { + if (val.encoding == ATTR_VAL_ADDRESS) + base = val.u.uint; + else if (val.encoding == ATTR_VAL_ADDRESS_INDEX) { + if (!resolve_addr_index(&ddata->dwarf_sections, u->addr_base, + u->addrsize, ddata->is_bigendian, val.u.uint, + error_callback, data, &base)) + return 0; + } + } + + if (is_function) { + switch (abbrev->attrs[i].name) { + case DW_AT_call_file: + if (val.encoding == ATTR_VAL_UINT) { + if (val.u.uint >= lhdr->filenames_count) { + dwarf_buf_error(unit_buf, + ("invalid file number in " + "DW_AT_call_file attribute"), + 0); + return 0; + } + function->caller_filename = lhdr->filenames[val.u.uint]; + } + break; + + case DW_AT_call_line: + if (val.encoding == ATTR_VAL_UINT) + function->caller_lineno = val.u.uint; + break; + + case DW_AT_abstract_origin: + case DW_AT_specification: + + if (have_linkage_name) break; + { + const char *name; + + name = read_referenced_name_from_attr(ddata, u, &abbrev->attrs[i], + &val, error_callback, data); + if (name != NULL) function->name = name; + } + break; + + case DW_AT_name: + + if (function->name != NULL) break; + if (!resolve_string(&ddata->dwarf_sections, u->is_dwarf64, + ddata->is_bigendian, u->str_offsets_base, &val, + error_callback, data, &function->name)) + return 0; + break; + + case DW_AT_linkage_name: + case DW_AT_MIPS_linkage_name: + + { + const char *s; + + s = NULL; + if (!resolve_string(&ddata->dwarf_sections, u->is_dwarf64, + ddata->is_bigendian, u->str_offsets_base, &val, + error_callback, data, &s)) + return 0; + if (s != NULL) { + function->name = s; + have_linkage_name = 1; + } + } break; + + case DW_AT_low_pc: + case DW_AT_high_pc: + case DW_AT_ranges: + update_pcrange(&abbrev->attrs[i], &val, &pcrange); + break; + + default: + break; + } + } + } + + if (is_function && function->name == NULL) { + backtrace_free(state, function, sizeof *function, error_callback, data); + is_function = 0; + } + + if (is_function) { + if (pcrange.have_ranges || (pcrange.have_lowpc && pcrange.have_highpc)) { + if (!add_ranges(state, &ddata->dwarf_sections, ddata->base_address, + ddata->is_bigendian, u, base, &pcrange, + add_function_range, (void *)function, error_callback, + data, (void *)vec)) + return 0; + } else { + backtrace_free(state, function, sizeof *function, error_callback, data); + is_function = 0; + } + } + + if (abbrev->has_children) { + if (!is_function) { + if (!read_function_entry(state, ddata, u, base, unit_buf, lhdr, + error_callback, data, vec_function, + vec_inlined)) + return 0; + } else { + struct function_vector fvec; + + memset(&fvec, 0, sizeof fvec); + + if (!read_function_entry(state, ddata, u, base, unit_buf, lhdr, + error_callback, data, vec_function, &fvec)) + return 0; + + if (fvec.count > 0) { + struct function_addrs *p; + struct function_addrs *faddrs; + + p = ((struct function_addrs *)backtrace_vector_grow( + state, sizeof(struct function_addrs), error_callback, data, + &fvec.vec)); + if (p == NULL) return 0; + p->low = 0; + --p->low; + p->high = p->low; + p->function = NULL; + + if (!backtrace_vector_release(state, &fvec.vec, error_callback, data)) + return 0; + + faddrs = (struct function_addrs *)fvec.vec.base; + backtrace_qsort(faddrs, fvec.count, sizeof(struct function_addrs), + function_addrs_compare); + + function->function_addrs = faddrs; + function->function_addrs_count = fvec.count; + } + } + } + } + + return 1; +} + +static void read_function_info( + struct backtrace_state *state, struct dwarf_data *ddata, + const struct line_header *lhdr, backtrace_error_callback error_callback, + void *data, struct unit *u, struct function_vector *fvec, + struct function_addrs **ret_addrs, size_t *ret_addrs_count) { + struct function_vector lvec; + struct function_vector *pfvec; + struct dwarf_buf unit_buf; + struct function_addrs *p; + struct function_addrs *addrs; + size_t addrs_count; + + if (fvec != NULL) + pfvec = fvec; + else { + memset(&lvec, 0, sizeof lvec); + pfvec = &lvec; + } + + unit_buf.name = ".debug_info"; + unit_buf.start = ddata->dwarf_sections.data[DEBUG_INFO]; + unit_buf.buf = u->unit_data; + unit_buf.left = u->unit_data_len; + unit_buf.is_bigendian = ddata->is_bigendian; + unit_buf.error_callback = error_callback; + unit_buf.data = data; + unit_buf.reported_underflow = 0; + + while (unit_buf.left > 0) { + if (!read_function_entry(state, ddata, u, 0, &unit_buf, lhdr, + error_callback, data, pfvec, pfvec)) + return; + } + + if (pfvec->count == 0) return; + + p = ((struct function_addrs *)backtrace_vector_grow( + state, sizeof(struct function_addrs), error_callback, data, &pfvec->vec)); + if (p == NULL) return; + p->low = 0; + --p->low; + p->high = p->low; + p->function = NULL; + + addrs_count = pfvec->count; + + if (fvec == NULL) { + if (!backtrace_vector_release(state, &lvec.vec, error_callback, data)) + return; + addrs = (struct function_addrs *)pfvec->vec.base; + } else { + addrs = ((struct function_addrs *)backtrace_vector_finish( + state, &fvec->vec, error_callback, data)); + if (addrs == NULL) return; + fvec->count = 0; + } + + backtrace_qsort(addrs, addrs_count, sizeof(struct function_addrs), + function_addrs_compare); + + *ret_addrs = addrs; + *ret_addrs_count = addrs_count; +} + +static int report_inlined_functions(uintptr_t pc, struct function *function, + backtrace_full_callback callback, + void *data, const char **filename, + int *lineno) { + struct function_addrs *p; + struct function_addrs *match; + struct function *inlined; + int ret; + + if (function->function_addrs_count == 0) return 0; + + if (pc + 1 == 0) return 0; + + p = ((struct function_addrs *)bsearch( + &pc, function->function_addrs, function->function_addrs_count, + sizeof(struct function_addrs), function_addrs_search)); + if (p == NULL) return 0; + + while (pc == (p + 1)->low) ++p; + match = NULL; + while (1) { + if (pc < p->high) { + match = p; + break; + } + if (p == function->function_addrs) break; + if ((p - 1)->low < p->low) break; + --p; + } + if (match == NULL) return 0; + + inlined = match->function; + + ret = report_inlined_functions(pc, inlined, callback, data, filename, lineno); + if (ret != 0) return ret; + + ret = callback(data, pc, *filename, *lineno, inlined->name); + if (ret != 0) return ret; + + *filename = inlined->caller_filename; + *lineno = inlined->caller_lineno; + + return 0; +} + +static int dwarf_lookup_pc(struct backtrace_state *state, + struct dwarf_data *ddata, uintptr_t pc, + backtrace_full_callback callback, + backtrace_error_callback error_callback, void *data, + int *found) { + struct unit_addrs *entry; + int found_entry; + struct unit *u; + int new_data; + struct line *lines; + struct line *ln; + struct function_addrs *p; + struct function_addrs *fmatch; + struct function *function; + const char *filename; + int lineno; + int ret; + + *found = 1; + + entry = (ddata->addrs_count == 0 || pc + 1 == 0 + ? NULL + : bsearch(&pc, ddata->addrs, ddata->addrs_count, + sizeof(struct unit_addrs), unit_addrs_search)); + + if (entry == NULL) { + *found = 0; + return 0; + } + + while (pc == (entry + 1)->low) ++entry; + found_entry = 0; + while (1) { + if (pc < entry->high) { + found_entry = 1; + break; + } + if (entry == ddata->addrs) break; + if ((entry - 1)->low < entry->low) break; + --entry; + } + if (!found_entry) { + *found = 0; + return 0; + } + + u = entry->u; + lines = u->lines; + + while (entry > ddata->addrs && pc >= (entry - 1)->low && + pc < (entry - 1)->high) { + if (state->threaded) + lines = (struct line *)backtrace_atomic_load_pointer(&u->lines); + + if (lines != (struct line *)(uintptr_t)-1) break; + + --entry; + + u = entry->u; + lines = u->lines; + } + + if (state->threaded) lines = backtrace_atomic_load_pointer(&u->lines); + + new_data = 0; + if (lines == NULL) { + struct function_addrs *function_addrs; + size_t function_addrs_count; + struct line_header lhdr; + size_t count; + + function_addrs = NULL; + function_addrs_count = 0; + if (read_line_info(state, ddata, error_callback, data, entry->u, &lhdr, + &lines, &count)) { + struct function_vector *pfvec; + + if (state->threaded) + pfvec = NULL; + else + pfvec = &ddata->fvec; + read_function_info(state, ddata, &lhdr, error_callback, data, entry->u, + pfvec, &function_addrs, &function_addrs_count); + free_line_header(state, &lhdr, error_callback, data); + new_data = 1; + } + + if (!state->threaded) { + u->lines_count = count; + u->function_addrs = function_addrs; + u->function_addrs_count = function_addrs_count; + u->lines = lines; + } else { + backtrace_atomic_store_size_t(&u->lines_count, count); + backtrace_atomic_store_pointer(&u->function_addrs, function_addrs); + backtrace_atomic_store_size_t(&u->function_addrs_count, + function_addrs_count); + backtrace_atomic_store_pointer(&u->lines, lines); + } + } + + if (lines == (struct line *)(uintptr_t)-1) { + if (new_data) + return dwarf_lookup_pc(state, ddata, pc, callback, error_callback, data, + found); + return callback(data, pc, NULL, 0, NULL); + } + + ln = (struct line *)bsearch(&pc, lines, entry->u->lines_count, + sizeof(struct line), line_search); + if (ln == NULL) { + if (entry->u->abs_filename == NULL) { + const char *filename; + + filename = entry->u->filename; + if (filename != NULL && !IS_ABSOLUTE_PATH(filename) && + entry->u->comp_dir != NULL) { + size_t filename_len; + const char *dir; + size_t dir_len; + char *s; + + filename_len = strlen(filename); + dir = entry->u->comp_dir; + dir_len = strlen(dir); + s = (char *)backtrace_alloc(state, dir_len + filename_len + 2, + error_callback, data); + if (s == NULL) { + *found = 0; + return 0; + } + memcpy(s, dir, dir_len); + + s[dir_len] = '/'; + memcpy(s + dir_len + 1, filename, filename_len + 1); + filename = s; + } + entry->u->abs_filename = filename; + } + + return callback(data, pc, entry->u->abs_filename, 0, NULL); + } + + if (entry->u->function_addrs_count == 0) + return callback(data, pc, ln->filename, ln->lineno, NULL); + + p = ((struct function_addrs *)bsearch( + &pc, entry->u->function_addrs, entry->u->function_addrs_count, + sizeof(struct function_addrs), function_addrs_search)); + if (p == NULL) return callback(data, pc, ln->filename, ln->lineno, NULL); + + while (pc == (p + 1)->low) ++p; + fmatch = NULL; + while (1) { + if (pc < p->high) { + fmatch = p; + break; + } + if (p == entry->u->function_addrs) break; + if ((p - 1)->low < p->low) break; + --p; + } + if (fmatch == NULL) return callback(data, pc, ln->filename, ln->lineno, NULL); + + function = fmatch->function; + + filename = ln->filename; + lineno = ln->lineno; + + ret = report_inlined_functions(pc, function, callback, data, &filename, + &lineno); + if (ret != 0) return ret; + + return callback(data, pc, filename, lineno, function->name); +} + +static int dwarf_fileline(struct backtrace_state *state, uintptr_t pc, + backtrace_full_callback callback, + backtrace_error_callback error_callback, void *data) { + struct dwarf_data *ddata; + int found; + int ret; + + if (!state->threaded) { + for (ddata = (struct dwarf_data *)state->fileline_data; ddata != NULL; + ddata = ddata->next) { + ret = dwarf_lookup_pc(state, ddata, pc, callback, error_callback, data, + &found); + if (ret != 0 || found) return ret; + } + } else { + struct dwarf_data **pp; + + pp = (struct dwarf_data **)(void *)&state->fileline_data; + while (1) { + ddata = backtrace_atomic_load_pointer(pp); + if (ddata == NULL) break; + + ret = dwarf_lookup_pc(state, ddata, pc, callback, error_callback, data, + &found); + if (ret != 0 || found) return ret; + + pp = &ddata->next; + } + } + + return callback(data, pc, NULL, 0, NULL); +} + +static struct dwarf_data *build_dwarf_data( + struct backtrace_state *state, uintptr_t base_address, + const struct dwarf_sections *dwarf_sections, int is_bigendian, + struct dwarf_data *altlink, backtrace_error_callback error_callback, + void *data) { + struct unit_addrs_vector addrs_vec; + struct unit_addrs *addrs; + size_t addrs_count; + struct unit_vector units_vec; + struct unit **units; + size_t units_count; + struct dwarf_data *fdata; + + if (!build_address_map(state, base_address, dwarf_sections, is_bigendian, + altlink, error_callback, data, &addrs_vec, &units_vec)) + return NULL; + + if (!backtrace_vector_release(state, &addrs_vec.vec, error_callback, data)) + return NULL; + if (!backtrace_vector_release(state, &units_vec.vec, error_callback, data)) + return NULL; + addrs = (struct unit_addrs *)addrs_vec.vec.base; + units = (struct unit **)units_vec.vec.base; + addrs_count = addrs_vec.count; + units_count = units_vec.count; + backtrace_qsort(addrs, addrs_count, sizeof(struct unit_addrs), + unit_addrs_compare); + + fdata = ((struct dwarf_data *)backtrace_alloc( + state, sizeof(struct dwarf_data), error_callback, data)); + if (fdata == NULL) return NULL; + + fdata->next = NULL; + fdata->altlink = altlink; + fdata->base_address = base_address; + fdata->addrs = addrs; + fdata->addrs_count = addrs_count; + fdata->units = units; + fdata->units_count = units_count; + fdata->dwarf_sections = *dwarf_sections; + fdata->is_bigendian = is_bigendian; + memset(&fdata->fvec, 0, sizeof fdata->fvec); + + return fdata; +} + +int backtrace_dwarf_add(struct backtrace_state *state, uintptr_t base_address, + const struct dwarf_sections *dwarf_sections, + int is_bigendian, struct dwarf_data *fileline_altlink, + backtrace_error_callback error_callback, void *data, + fileline *fileline_fn, + struct dwarf_data **fileline_entry) { + struct dwarf_data *fdata; + + fdata = build_dwarf_data(state, base_address, dwarf_sections, is_bigendian, + fileline_altlink, error_callback, data); + if (fdata == NULL) return 0; + + if (fileline_entry != NULL) *fileline_entry = fdata; + + if (!state->threaded) { + struct dwarf_data **pp; + + for (pp = (struct dwarf_data **)(void *)&state->fileline_data; *pp != NULL; + pp = &(*pp)->next) + ; + *pp = fdata; + } else { + while (1) { + struct dwarf_data **pp; + + pp = (struct dwarf_data **)(void *)&state->fileline_data; + + while (1) { + struct dwarf_data *p; + + p = backtrace_atomic_load_pointer(pp); + + if (p == NULL) break; + + pp = &p->next; + } + + if (__sync_bool_compare_and_swap(pp, NULL, fdata)) break; + } + } + + *fileline_fn = dwarf_fileline; + + return 1; +} + +// fileline.c: +#include +#include +#include +#include +#include +#include + +#if defined(HAVE_KERN_PROC_ARGS) || defined(HAVE_KERN_PROC) +#include +#endif + +#ifdef HAVE_MACH_O_DYLD_H +#include +#endif + +#ifndef HAVE_GETEXECNAME +#define getexecname() NULL +#endif + +#if !defined(HAVE_KERN_PROC_ARGS) && !defined(HAVE_KERN_PROC) + +#define sysctl_exec_name1(state, error_callback, data) NULL +#define sysctl_exec_name2(state, error_callback, data) NULL + +#else +static char *sysctl_exec_name(struct backtrace_state *state, int mib0, int mib1, + int mib2, int mib3, + backtrace_error_callback error_callback, + void *data) { + int mib[4]; + size_t len; + char *name; + size_t rlen; + + mib[0] = mib0; + mib[1] = mib1; + mib[2] = mib2; + mib[3] = mib3; + + if (sysctl(mib, 4, NULL, &len, NULL, 0) < 0) return NULL; + name = (char *)backtrace_alloc(state, len, error_callback, data); + if (name == NULL) return NULL; + rlen = len; + if (sysctl(mib, 4, name, &rlen, NULL, 0) < 0) { + backtrace_free(state, name, len, error_callback, data); + return NULL; + } + return name; +} + +#ifdef HAVE_KERN_PROC_ARGS + +static char *sysctl_exec_name1(struct backtrace_state *state, + backtrace_error_callback error_callback, + void *data) { + return sysctl_exec_name(state, CTL_KERN, KERN_PROC_ARGS, -1, + KERN_PROC_PATHNAME, error_callback, data); +} + +#else + +#define sysctl_exec_name1(state, error_callback, data) NULL + +#endif + +#ifdef HAVE_KERN_PROC + +static char *sysctl_exec_name2(struct backtrace_state *state, + backtrace_error_callback error_callback, + void *data) { + return sysctl_exec_name(state, CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1, + error_callback, data); +} + +#else + +#define sysctl_exec_name2(state, error_callback, data) NULL + +#endif + +#endif +#ifdef HAVE_MACH_O_DYLD_H + +static char *macho_get_executable_path(struct backtrace_state *state, + backtrace_error_callback error_callback, + void *data) { + uint32_t len; + char *name; + + len = 0; + if (_NSGetExecutablePath(NULL, &len) == 0) return NULL; + name = (char *)backtrace_alloc(state, len, error_callback, data); + if (name == NULL) return NULL; + if (_NSGetExecutablePath(name, &len) != 0) { + backtrace_free(state, name, len, error_callback, data); + return NULL; + } + return name; +} + +#else +#define macho_get_executable_path(state, error_callback, data) NULL + +#endif + +static int fileline_initialize(struct backtrace_state *state, + backtrace_error_callback error_callback, + void *data) { + int failed; + fileline fileline_fn; + int pass; + int called_error_callback; + int descriptor; + const char *filename; + char buf[64]; + + if (!state->threaded) + failed = state->fileline_initialization_failed; + else + failed = backtrace_atomic_load_int(&state->fileline_initialization_failed); + + if (failed) { + error_callback(data, "failed to read executable information", -1); + return 0; + } + + if (!state->threaded) + fileline_fn = state->fileline_fn; + else + fileline_fn = backtrace_atomic_load_pointer(&state->fileline_fn); + if (fileline_fn != NULL) return 1; + + descriptor = -1; + called_error_callback = 0; + for (pass = 0; pass < 8; ++pass) { + int does_not_exist; + + switch (pass) { + case 0: + filename = state->filename; + break; + case 1: + filename = getexecname(); + break; + case 2: + filename = "/proc/self/exe"; + break; + case 3: + filename = "/proc/curproc/file"; + break; + case 4: + snprintf(buf, sizeof(buf), "/proc/%ld/object/a.out", (long)getpid()); + filename = buf; + break; + case 5: + filename = sysctl_exec_name1(state, error_callback, data); + break; + case 6: + filename = sysctl_exec_name2(state, error_callback, data); + break; + case 7: + filename = macho_get_executable_path(state, error_callback, data); + break; + default: + abort(); + } + + if (filename == NULL) continue; + + descriptor = + backtrace_open(filename, error_callback, data, &does_not_exist); + if (descriptor < 0 && !does_not_exist) { + called_error_callback = 1; + break; + } + if (descriptor >= 0) break; + } + + if (descriptor < 0) { + if (!called_error_callback) { + if (state->filename != NULL) + error_callback(data, state->filename, ENOENT); + else + error_callback(data, "libbacktrace could not find executable to open", + 0); + } + failed = 1; + } + + if (!failed) { + if (!backtrace_initialize(state, filename, descriptor, error_callback, data, + &fileline_fn)) + failed = 1; + } + + if (failed) { + if (!state->threaded) + state->fileline_initialization_failed = 1; + else + backtrace_atomic_store_int(&state->fileline_initialization_failed, 1); + return 0; + } + + if (!state->threaded) + state->fileline_fn = fileline_fn; + else { + backtrace_atomic_store_pointer(&state->fileline_fn, fileline_fn); + } + + return 1; +} + +int backtrace_pcinfo(struct backtrace_state *state, uintptr_t pc, + backtrace_full_callback callback, + backtrace_error_callback error_callback, void *data) { + if (!fileline_initialize(state, error_callback, data)) return 0; + + if (state->fileline_initialization_failed) return 0; + + return state->fileline_fn(state, pc, callback, error_callback, data); +} + +int backtrace_syminfo(struct backtrace_state *state, uintptr_t pc, + backtrace_syminfo_callback callback, + backtrace_error_callback error_callback, void *data) { + if (!fileline_initialize(state, error_callback, data)) return 0; + + if (state->fileline_initialization_failed) return 0; + + state->syminfo_fn(state, pc, callback, error_callback, data); + return 1; +} + +void backtrace_syminfo_to_full_callback(void *data, uintptr_t pc, + const char *symname, + uintptr_t symval ATTRIBUTE_UNUSED, + uintptr_t symsize ATTRIBUTE_UNUSED) { + struct backtrace_call_full *bdata = (struct backtrace_call_full *)data; + + bdata->ret = bdata->full_callback(bdata->full_data, pc, NULL, 0, symname); +} + +void backtrace_syminfo_to_full_error_callback(void *data, const char *msg, + int errnum) { + struct backtrace_call_full *bdata = (struct backtrace_call_full *)data; + + bdata->full_error_callback(bdata->full_data, msg, errnum); +} + +// posix.c: +#include +#include +#include +#include +#include + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif + +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif + +int backtrace_open(const char *filename, + backtrace_error_callback error_callback, void *data, + int *does_not_exist) { + int descriptor; + + if (does_not_exist != NULL) *does_not_exist = 0; + + descriptor = open(filename, (int)(O_RDONLY | O_BINARY | O_CLOEXEC)); + if (descriptor < 0) { + if (does_not_exist != NULL && (errno == ENOENT || errno == EACCES)) + *does_not_exist = 1; + else + error_callback(data, filename, errno); + return -1; + } + +#ifdef HAVE_FCNTL + + fcntl(descriptor, F_SETFD, FD_CLOEXEC); +#endif + + return descriptor; +} + +int backtrace_close(int descriptor, backtrace_error_callback error_callback, + void *data) { + if (close(descriptor) < 0) { + error_callback(data, "close", errno); + return 0; + } + return 1; +} + +// print.c: +#include +#include +#include + +struct print_data { + struct backtrace_state *state; + FILE *f; +}; + +static int print_callback(void *data, uintptr_t pc, const char *filename, + int lineno, const char *function) { + struct print_data *pdata = (struct print_data *)data; + + fprintf(pdata->f, "0x%lx %s\n\t%s:%d\n", (unsigned long)pc, + function == NULL ? "???" : function, + filename == NULL ? "???" : filename, lineno); + return 0; +} + +static void error_callback(void *data, const char *msg, int errnum) { + struct print_data *pdata = (struct print_data *)data; + + if (pdata->state->filename != NULL) + fprintf(stderr, "%s: ", pdata->state->filename); + fprintf(stderr, "libbacktrace: %s", msg); + if (errnum > 0) fprintf(stderr, ": %s", strerror(errnum)); + fputc('\n', stderr); +} + +void __attribute__((noinline)) +backtrace_print(struct backtrace_state *state, int skip, FILE *f) { + struct print_data data; + + data.state = state; + data.f = f; + backtrace_full(state, skip + 1, print_callback, error_callback, + (void *)&data); +} + +// sort.c: +#include +#include + +static void swap(char *a, char *b, size_t size) { + size_t i; + + for (i = 0; i < size; i++, a++, b++) { + char t; + + t = *a; + *a = *b; + *b = t; + } +} + +void backtrace_qsort(void *basearg, size_t count, size_t size, + int (*compar)(const void *, const void *)) { + char *base = (char *)basearg; + size_t i; + size_t mid; + +tail_recurse: + if (count < 2) return; + + swap(base, base + (count / 2) * size, size); + + mid = 0; + for (i = 1; i < count; i++) { + if ((*compar)(base, base + i * size) > 0) { + ++mid; + if (i != mid) swap(base + mid * size, base + i * size, size); + } + } + + if (mid > 0) swap(base, base + mid * size, size); + + if (2 * mid < count) { + backtrace_qsort(base, mid, size, compar); + base += (mid + 1) * size; + count -= mid + 1; + goto tail_recurse; + } else { + backtrace_qsort(base + (mid + 1) * size, count - (mid + 1), size, compar); + count = mid; + goto tail_recurse; + } +} + +// state.c: +#include +#include + +struct backtrace_state *backtrace_create_state( + const char *filename, int threaded, backtrace_error_callback error_callback, + void *data) { + struct backtrace_state init_state; + struct backtrace_state *state; + +#ifndef HAVE_SYNC_FUNCTIONS + if (threaded) { + error_callback(data, "backtrace library does not support threads", 0); + return NULL; + } +#endif + + memset(&init_state, 0, sizeof init_state); + init_state.filename = filename; + init_state.threaded = threaded; + + state = ((struct backtrace_state *)backtrace_alloc(&init_state, sizeof *state, + error_callback, data)); + if (state == NULL) return NULL; + *state = init_state; + + return state; +} + +// backtrace.c: +#include + +#ifdef BACKTRACE_SUPPORTED +#include + +struct backtrace_data { + int skip; + + struct backtrace_state *state; + + backtrace_full_callback callback; + + backtrace_error_callback error_callback; + + void *data; + + int ret; + + int can_alloc; +}; + +static _Unwind_Reason_Code unwind(struct _Unwind_Context *context, + void *vdata) { + struct backtrace_data *bdata = (struct backtrace_data *)vdata; + uintptr_t pc; + int ip_before_insn = 0; + +#ifdef HAVE_GETIPINFO + pc = _Unwind_GetIPInfo(context, &ip_before_insn); +#else + pc = _Unwind_GetIP(context); +#endif + + if (bdata->skip > 0) { + --bdata->skip; + return _URC_NO_REASON; + } + + if (!ip_before_insn) --pc; + + if (!bdata->can_alloc) + bdata->ret = bdata->callback(bdata->data, pc, NULL, 0, NULL); + else + bdata->ret = backtrace_pcinfo(bdata->state, pc, bdata->callback, + bdata->error_callback, bdata->data); + if (bdata->ret != 0) return _URC_END_OF_STACK; + + return _URC_NO_REASON; +} + +int __attribute__((noinline)) +backtrace_full(struct backtrace_state *state, int skip, + backtrace_full_callback callback, + backtrace_error_callback error_callback, void *data) { + struct backtrace_data bdata; + void *p; + + bdata.skip = skip + 1; + bdata.state = state; + bdata.callback = callback; + bdata.error_callback = error_callback; + bdata.data = data; + bdata.ret = 0; + + p = backtrace_alloc(state, 4096, NULL, NULL); + if (p == NULL) + bdata.can_alloc = 0; + else { + backtrace_free(state, p, 4096, NULL, NULL); + bdata.can_alloc = 1; + } + + _Unwind_Backtrace(unwind, &bdata); + return bdata.ret; +} +#else +// Copied from nounwind.c +int +backtrace_full (struct backtrace_state *state ATTRIBUTE_UNUSED, + int skip ATTRIBUTE_UNUSED, + backtrace_full_callback callback ATTRIBUTE_UNUSED, + backtrace_error_callback error_callback, void *data) +{ + error_callback (data, + "no stack trace because unwind library not available", + 0); + return 0; +} +#endif + +// simple.c: +#ifdef BACKTRACE_SUPPORTED +#include + +struct backtrace_simple_data { + int skip; + + struct backtrace_state *state; + + backtrace_simple_callback callback; + + backtrace_error_callback error_callback; + + void *data; + + int ret; +}; + +static _Unwind_Reason_Code simple_unwind(struct _Unwind_Context *context, + void *vdata) { + struct backtrace_simple_data *bdata = (struct backtrace_simple_data *)vdata; + uintptr_t pc; + int ip_before_insn = 0; + +#ifdef HAVE_GETIPINFO + pc = _Unwind_GetIPInfo(context, &ip_before_insn); +#else + pc = _Unwind_GetIP(context); +#endif + + if (bdata->skip > 0) { + --bdata->skip; + return _URC_NO_REASON; + } + + if (!ip_before_insn) --pc; + + bdata->ret = bdata->callback(bdata->data, pc); + + if (bdata->ret != 0) return _URC_END_OF_STACK; + + return _URC_NO_REASON; +} + +int __attribute__((noinline)) +backtrace_simple(struct backtrace_state *state, int skip, + backtrace_simple_callback callback, + backtrace_error_callback error_callback, void *data) { + struct backtrace_simple_data bdata; + + bdata.skip = skip + 1; + bdata.state = state; + bdata.callback = callback; + bdata.error_callback = error_callback; + bdata.data = data; + bdata.ret = 0; + _Unwind_Backtrace(simple_unwind, &bdata); + return bdata.ret; +} +#else +int +backtrace_simple (struct backtrace_state *state ATTRIBUTE_UNUSED, + int skip ATTRIBUTE_UNUSED, + backtrace_simple_callback callback ATTRIBUTE_UNUSED, + backtrace_error_callback error_callback, void *data) +{ + error_callback (data, + "no stack trace because unwind library not available", + 0); + return 0; +} +#endif diff --git a/thirdparty/libbacktrace/darwin.c b/thirdparty/libbacktrace/darwin.c new file mode 100644 index 0000000000..59d58f6603 --- /dev/null +++ b/thirdparty/libbacktrace/darwin.c @@ -0,0 +1,1298 @@ +// macho.c: +#include +#include +#include +#include + +#ifdef HAVE_MACH_O_DYLD_H +#include +#endif + +struct macho_header_32 { + uint32_t magic; + uint32_t cputype; + uint32_t cpusubtype; + uint32_t filetype; + uint32_t ncmds; + uint32_t sizeofcmds; + uint32_t flags; +}; + +struct macho_header_64 { + uint32_t magic; + uint32_t cputype; + uint32_t cpusubtype; + uint32_t filetype; + uint32_t ncmds; + uint32_t sizeofcmds; + uint32_t flags; + uint32_t reserved; +}; + +struct macho_header_fat { + uint32_t magic; + uint32_t nfat_arch; +}; + +#define MACH_O_MH_MAGIC_32 0xfeedface +#define MACH_O_MH_MAGIC_64 0xfeedfacf +#define MACH_O_MH_MAGIC_FAT 0xcafebabe +#define MACH_O_MH_CIGAM_FAT 0xbebafeca +#define MACH_O_MH_MAGIC_FAT_64 0xcafebabf +#define MACH_O_MH_CIGAM_FAT_64 0xbfbafeca + +#define MACH_O_MH_EXECUTE 0x02 +#define MACH_O_MH_DYLIB 0x06 +#define MACH_O_MH_DSYM 0x0a + +struct macho_fat_arch { + uint32_t cputype; + uint32_t cpusubtype; + uint32_t offset; + uint32_t size; + uint32_t align; +}; + +struct macho_fat_arch_64 { + uint32_t cputype; + uint32_t cpusubtype; + uint64_t offset; + uint64_t size; + uint32_t align; + uint32_t reserved; +}; + +#define MACH_O_CPU_ARCH_ABI64 0x01000000 + +#define MACH_O_CPU_TYPE_X86 7 +#define MACH_O_CPU_TYPE_ARM 12 +#define MACH_O_CPU_TYPE_PPC 18 + +#define MACH_O_CPU_TYPE_X86_64 (MACH_O_CPU_TYPE_X86 | MACH_O_CPU_ARCH_ABI64) +#define MACH_O_CPU_TYPE_ARM64 (MACH_O_CPU_TYPE_ARM | MACH_O_CPU_ARCH_ABI64) +#define MACH_O_CPU_TYPE_PPC64 (MACH_O_CPU_TYPE_PPC | MACH_O_CPU_ARCH_ABI64) + +struct macho_load_command { + uint32_t cmd; + uint32_t cmdsize; +}; + +#define MACH_O_LC_SEGMENT 0x01 +#define MACH_O_LC_SYMTAB 0x02 +#define MACH_O_LC_SEGMENT_64 0x19 +#define MACH_O_LC_UUID 0x1b + +#define MACH_O_NAMELEN (16) + +struct macho_segment_command { + uint32_t cmd; + uint32_t cmdsize; + char segname[MACH_O_NAMELEN]; + uint32_t vmaddr; + uint32_t vmsize; + uint32_t fileoff; + uint32_t filesize; + uint32_t maxprot; + uint32_t initprot; + uint32_t nsects; + uint32_t flags; +}; + +struct macho_segment_64_command { + uint32_t cmd; + uint32_t cmdsize; + char segname[MACH_O_NAMELEN]; + uint64_t vmaddr; + uint64_t vmsize; + uint64_t fileoff; + uint64_t filesize; + uint32_t maxprot; + uint32_t initprot; + uint32_t nsects; + uint32_t flags; +}; + +struct macho_symtab_command { + uint32_t cmd; + uint32_t cmdsize; + uint32_t symoff; + uint32_t nsyms; + uint32_t stroff; + uint32_t strsize; +}; + +#define MACH_O_UUID_LEN (16) + +struct macho_uuid_command { + uint32_t cmd; + uint32_t cmdsize; + unsigned char uuid[MACH_O_UUID_LEN]; +}; + +struct macho_section { + char sectname[MACH_O_NAMELEN]; + char segment[MACH_O_NAMELEN]; + uint32_t addr; + uint32_t size; + uint32_t offset; + uint32_t align; + uint32_t reloff; + uint32_t nreloc; + uint32_t flags; + uint32_t reserved1; + uint32_t reserved2; +}; + +struct macho_section_64 { + char sectname[MACH_O_NAMELEN]; + char segment[MACH_O_NAMELEN]; + uint64_t addr; + uint64_t size; + uint32_t offset; + uint32_t align; + uint32_t reloff; + uint32_t nreloc; + uint32_t flags; + uint32_t reserved1; + uint32_t reserved2; + uint32_t reserved3; +}; + +struct macho_nlist { + uint32_t n_strx; + uint8_t n_type; + uint8_t n_sect; + uint16_t n_desc; + uint32_t n_value; +}; + +struct macho_nlist_64 { + uint32_t n_strx; + uint8_t n_type; + uint8_t n_sect; + uint16_t n_desc; + uint64_t n_value; +}; + +#define MACH_O_N_EXT 0x01 +#define MACH_O_N_ABS 0x02 +#define MACH_O_N_SECT 0x0e +#define MACH_O_N_TYPE 0x0e +#define MACH_O_N_STAB 0xe0 + +struct macho_symbol { + const char *name; + uintptr_t address; +}; + +struct macho_syminfo_data { + struct macho_syminfo_data *next; + struct macho_symbol *symbols; + size_t count; +}; + +static const char *const dwarf_section_names[DEBUG_MAX] = { + "__debug_info", "__debug_line", + "__debug_abbrev", "__debug_ranges", + "__debug_str", "", + "__debug_str_offs", "", + "__debug_rnglists"}; + +static int macho_add(struct backtrace_state *, const char *, int, off_t, + const unsigned char *, uintptr_t, int, + backtrace_error_callback, void *, fileline *, int *); + +static int macho_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 Mach-O executable", -1); + return 0; +} + +static void macho_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 Mach-O executable", -1); +} + +static int macho_add_dwarf_section(struct backtrace_state *state, + int descriptor, const char *sectname, + uint32_t offset, uint64_t size, + backtrace_error_callback error_callback, + void *data, + struct dwarf_sections *dwarf_sections) { + int i; + + for (i = 0; i < (int)DEBUG_MAX; ++i) { + if (dwarf_section_names[i][0] != '\0' && + strncmp(sectname, dwarf_section_names[i], MACH_O_NAMELEN) == 0) { + struct backtrace_view section_view; + + if (!backtrace_get_view(state, descriptor, offset, size, error_callback, + data, §ion_view)) + return 0; + dwarf_sections->data[i] = (const unsigned char *)section_view.data; + dwarf_sections->size[i] = size; + break; + } + } + return 1; +} + +static int macho_add_dwarf_segment(struct backtrace_state *state, + int descriptor, off_t offset, + unsigned int cmd, const char *psecs, + size_t sizesecs, unsigned int nsects, + backtrace_error_callback error_callback, + void *data, + struct dwarf_sections *dwarf_sections) { + size_t sec_header_size; + size_t secoffset; + unsigned int i; + + switch (cmd) { + case MACH_O_LC_SEGMENT: + sec_header_size = sizeof(struct macho_section); + break; + case MACH_O_LC_SEGMENT_64: + sec_header_size = sizeof(struct macho_section_64); + break; + default: + abort(); + } + + secoffset = 0; + for (i = 0; i < nsects; ++i) { + if (secoffset + sec_header_size > sizesecs) { + error_callback(data, "section overflow withing segment", 0); + return 0; + } + + switch (cmd) { + case MACH_O_LC_SEGMENT: { + struct macho_section section; + + memcpy(§ion, psecs + secoffset, sizeof section); + macho_add_dwarf_section(state, descriptor, section.sectname, + offset + section.offset, section.size, + error_callback, data, dwarf_sections); + } break; + + case MACH_O_LC_SEGMENT_64: { + struct macho_section_64 section; + + memcpy(§ion, psecs + secoffset, sizeof section); + macho_add_dwarf_section(state, descriptor, section.sectname, + offset + section.offset, section.size, + error_callback, data, dwarf_sections); + } break; + + default: + abort(); + } + + secoffset += sec_header_size; + } + + return 1; +} + +static int macho_symbol_compare(const void *v1, const void *v2) { + const struct macho_symbol *m1 = (const struct macho_symbol *)v1; + const struct macho_symbol *m2 = (const struct macho_symbol *)v2; + + if (m1->address < m2->address) + return -1; + else if (m1->address > m2->address) + return 1; + else + return 0; +} + +static int macho_symbol_search(const void *vkey, const void *ventry) { + const uintptr_t *key = (const uintptr_t *)vkey; + const struct macho_symbol *entry = (const struct macho_symbol *)ventry; + uintptr_t addr; + + addr = *key; + if (addr < entry->address) + return -1; + else if (entry->name[0] == '\0' && entry->address == ~(uintptr_t)0) + return -1; + else if ((entry + 1)->name[0] == '\0' && + (entry + 1)->address == ~(uintptr_t)0) + return -1; + else if (addr >= (entry + 1)->address) + return 1; + else + return 0; +} + +static int macho_defined_symbol(uint8_t type) { + if ((type & MACH_O_N_STAB) != 0) return 0; + if ((type & MACH_O_N_EXT) != 0) return 0; + switch (type & MACH_O_N_TYPE) { + case MACH_O_N_ABS: + return 1; + case MACH_O_N_SECT: + return 1; + default: + return 0; + } +} + +static int macho_add_symtab(struct backtrace_state *state, int descriptor, + uintptr_t base_address, int is_64, off_t symoff, + unsigned int nsyms, off_t stroff, + unsigned int strsize, + backtrace_error_callback error_callback, + void *data) { + size_t symsize; + struct backtrace_view sym_view; + int sym_view_valid; + struct backtrace_view str_view; + int str_view_valid; + size_t ndefs; + size_t symtaboff; + unsigned int i; + size_t macho_symbol_size; + struct macho_symbol *macho_symbols; + unsigned int j; + struct macho_syminfo_data *sdata; + + sym_view_valid = 0; + str_view_valid = 0; + macho_symbol_size = 0; + macho_symbols = NULL; + + if (is_64) + symsize = sizeof(struct macho_nlist_64); + else + symsize = sizeof(struct macho_nlist); + + if (!backtrace_get_view(state, descriptor, symoff, nsyms * symsize, + error_callback, data, &sym_view)) + goto fail; + sym_view_valid = 1; + + if (!backtrace_get_view(state, descriptor, stroff, strsize, error_callback, + data, &str_view)) + return 0; + str_view_valid = 1; + + ndefs = 0; + symtaboff = 0; + for (i = 0; i < nsyms; ++i, symtaboff += symsize) { + if (is_64) { + struct macho_nlist_64 nlist; + + memcpy(&nlist, (const char *)sym_view.data + symtaboff, sizeof nlist); + if (macho_defined_symbol(nlist.n_type)) ++ndefs; + } else { + struct macho_nlist nlist; + + memcpy(&nlist, (const char *)sym_view.data + symtaboff, sizeof nlist); + if (macho_defined_symbol(nlist.n_type)) ++ndefs; + } + } + + macho_symbol_size = (ndefs + 1) * sizeof(struct macho_symbol); + macho_symbols = ((struct macho_symbol *)backtrace_alloc( + state, macho_symbol_size, error_callback, data)); + if (macho_symbols == NULL) goto fail; + + j = 0; + symtaboff = 0; + for (i = 0; i < nsyms; ++i, symtaboff += symsize) { + uint32_t strx; + uint64_t value; + const char *name; + + strx = 0; + value = 0; + if (is_64) { + struct macho_nlist_64 nlist; + + memcpy(&nlist, (const char *)sym_view.data + symtaboff, sizeof nlist); + if (!macho_defined_symbol(nlist.n_type)) continue; + + strx = nlist.n_strx; + value = nlist.n_value; + } else { + struct macho_nlist nlist; + + memcpy(&nlist, (const char *)sym_view.data + symtaboff, sizeof nlist); + if (!macho_defined_symbol(nlist.n_type)) continue; + + strx = nlist.n_strx; + value = nlist.n_value; + } + + if (strx >= strsize) { + error_callback(data, "symbol string index out of range", 0); + goto fail; + } + + name = (const char *)str_view.data + strx; + if (name[0] == '_') ++name; + macho_symbols[j].name = name; + macho_symbols[j].address = value + base_address; + ++j; + } + + sdata = ((struct macho_syminfo_data *)backtrace_alloc(state, sizeof *sdata, + error_callback, data)); + if (sdata == NULL) goto fail; + + backtrace_release_view(state, &sym_view, error_callback, data); + sym_view_valid = 0; + str_view_valid = 0; + + macho_symbols[j].name = ""; + macho_symbols[j].address = ~(uintptr_t)0; + + backtrace_qsort(macho_symbols, ndefs + 1, sizeof(struct macho_symbol), + macho_symbol_compare); + + sdata->next = NULL; + sdata->symbols = macho_symbols; + sdata->count = ndefs; + + if (!state->threaded) { + struct macho_syminfo_data **pp; + + for (pp = (struct macho_syminfo_data **)(void *)&state->syminfo_data; + *pp != NULL; pp = &(*pp)->next) + ; + *pp = sdata; + } else { + while (1) { + struct macho_syminfo_data **pp; + + pp = (struct macho_syminfo_data **)(void *)&state->syminfo_data; + + while (1) { + struct macho_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; + } + } + + return 1; + +fail: + if (macho_symbols != NULL) + backtrace_free(state, macho_symbols, macho_symbol_size, error_callback, + data); + if (sym_view_valid) + backtrace_release_view(state, &sym_view, error_callback, data); + if (str_view_valid) + backtrace_release_view(state, &str_view, error_callback, data); + return 0; +} + +static void macho_syminfo( + struct backtrace_state *state, uintptr_t addr, + backtrace_syminfo_callback callback, + backtrace_error_callback error_callback ATTRIBUTE_UNUSED, void *data) { + struct macho_syminfo_data *sdata; + struct macho_symbol *sym; + + sym = NULL; + if (!state->threaded) { + for (sdata = (struct macho_syminfo_data *)state->syminfo_data; + sdata != NULL; sdata = sdata->next) { + sym = ((struct macho_symbol *)bsearch(&addr, sdata->symbols, sdata->count, + sizeof(struct macho_symbol), + macho_symbol_search)); + if (sym != NULL) break; + } + } else { + struct macho_syminfo_data **pp; + + pp = (struct macho_syminfo_data **)(void *)&state->syminfo_data; + while (1) { + sdata = backtrace_atomic_load_pointer(pp); + if (sdata == NULL) break; + + sym = ((struct macho_symbol *)bsearch(&addr, sdata->symbols, sdata->count, + sizeof(struct macho_symbol), + macho_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 macho_add_fat(struct backtrace_state *state, const char *filename, + int descriptor, int swapped, off_t offset, + const unsigned char *match_uuid, + uintptr_t base_address, int skip_symtab, + uint32_t nfat_arch, int is_64, + backtrace_error_callback error_callback, void *data, + fileline *fileline_fn, int *found_sym) { + int arch_view_valid; + unsigned int cputype; + size_t arch_size; + struct backtrace_view arch_view; + unsigned int i; + + arch_view_valid = 0; + +#if defined(__x86_64__) + cputype = MACH_O_CPU_TYPE_X86_64; +#elif defined(__i386__) + cputype = MACH_O_CPU_TYPE_X86; +#elif defined(__aarch64__) + cputype = MACH_O_CPU_TYPE_ARM64; +#elif defined(__arm__) + cputype = MACH_O_CPU_TYPE_ARM; +#elif defined(__ppc__) + cputype = MACH_O_CPU_TYPE_PPC; +#elif defined(__ppc64__) + cputype = MACH_O_CPU_TYPE_PPC64; +#else + error_callback(data, "unknown Mach-O architecture", 0); + goto fail; +#endif + + if (is_64) + arch_size = sizeof(struct macho_fat_arch_64); + else + arch_size = sizeof(struct macho_fat_arch); + + if (!backtrace_get_view(state, descriptor, offset, nfat_arch * arch_size, + error_callback, data, &arch_view)) + goto fail; + + for (i = 0; i < nfat_arch; ++i) { + uint32_t fcputype; + uint64_t foffset; + + if (is_64) { + struct macho_fat_arch_64 fat_arch_64; + + memcpy(&fat_arch_64, (const char *)arch_view.data + i * arch_size, + arch_size); + fcputype = fat_arch_64.cputype; + foffset = fat_arch_64.offset; + if (swapped) { + fcputype = __builtin_bswap32(fcputype); + foffset = __builtin_bswap64(foffset); + } + } else { + struct macho_fat_arch fat_arch_32; + + memcpy(&fat_arch_32, (const char *)arch_view.data + i * arch_size, + arch_size); + fcputype = fat_arch_32.cputype; + foffset = (uint64_t)fat_arch_32.offset; + if (swapped) { + fcputype = __builtin_bswap32(fcputype); + foffset = (uint64_t)__builtin_bswap32((uint32_t)foffset); + } + } + + if (fcputype == cputype) { + backtrace_release_view(state, &arch_view, error_callback, data); + return macho_add(state, filename, descriptor, foffset, match_uuid, + base_address, skip_symtab, error_callback, data, + fileline_fn, found_sym); + } + } + + error_callback(data, "could not find executable in fat file", 0); + +fail: + if (arch_view_valid) + backtrace_release_view(state, &arch_view, error_callback, data); + if (descriptor != -1) backtrace_close(descriptor, error_callback, data); + return 0; +} + +static int macho_add_dsym(struct backtrace_state *state, const char *filename, + uintptr_t base_address, const unsigned char *uuid, + backtrace_error_callback error_callback, void *data, + fileline *fileline_fn) { + const char *p; + const char *dirname; + char *diralc; + size_t dirnamelen; + const char *basename; + size_t basenamelen; + const char *dsymsuffixdir; + size_t dsymsuffixdirlen; + size_t dsymlen; + char *dsym; + char *ps; + int d; + int does_not_exist; + int dummy_found_sym; + + diralc = NULL; + dirnamelen = 0; + dsym = NULL; + dsymlen = 0; + + p = strrchr(filename, '/'); + if (p == NULL) { + dirname = "."; + dirnamelen = 1; + basename = filename; + basenamelen = strlen(basename); + diralc = NULL; + } else { + dirnamelen = p - filename; + diralc = backtrace_alloc(state, dirnamelen + 1, error_callback, data); + if (diralc == NULL) goto fail; + memcpy(diralc, filename, dirnamelen); + diralc[dirnamelen] = '\0'; + dirname = diralc; + basename = p + 1; + basenamelen = strlen(basename); + } + + dsymsuffixdir = ".dSYM/Contents/Resources/DWARF/"; + dsymsuffixdirlen = strlen(dsymsuffixdir); + + dsymlen = (dirnamelen + 1 + basenamelen + dsymsuffixdirlen + basenamelen + 1); + dsym = backtrace_alloc(state, dsymlen, error_callback, data); + if (dsym == NULL) goto fail; + + ps = dsym; + memcpy(ps, dirname, dirnamelen); + ps += dirnamelen; + *ps++ = '/'; + memcpy(ps, basename, basenamelen); + ps += basenamelen; + memcpy(ps, dsymsuffixdir, dsymsuffixdirlen); + ps += dsymsuffixdirlen; + memcpy(ps, basename, basenamelen); + ps += basenamelen; + *ps = '\0'; + + if (diralc != NULL) { + backtrace_free(state, diralc, dirnamelen + 1, error_callback, data); + diralc = NULL; + } + + d = backtrace_open(dsym, error_callback, data, &does_not_exist); + if (d < 0) { + backtrace_free(state, dsym, dsymlen, error_callback, data); + return 1; + } + + if (!macho_add(state, dsym, d, 0, uuid, base_address, 1, error_callback, data, + fileline_fn, &dummy_found_sym)) + goto fail; + + backtrace_free(state, dsym, dsymlen, error_callback, data); + + return 1; + +fail: + if (dsym != NULL) backtrace_free(state, dsym, dsymlen, error_callback, data); + if (diralc != NULL) + backtrace_free(state, diralc, dirnamelen, error_callback, data); + return 0; +} + +static int macho_add(struct backtrace_state *state, const char *filename, + int descriptor, off_t offset, + const unsigned char *match_uuid, uintptr_t base_address, + int skip_symtab, backtrace_error_callback error_callback, + void *data, fileline *fileline_fn, int *found_sym) { + struct backtrace_view header_view; + struct macho_header_32 header; + off_t hdroffset; + int is_64; + struct backtrace_view cmds_view; + int cmds_view_valid; + struct dwarf_sections dwarf_sections; + int have_dwarf; + unsigned char uuid[MACH_O_UUID_LEN]; + int have_uuid; + size_t cmdoffset; + unsigned int i; + + *found_sym = 0; + + cmds_view_valid = 0; + + if (!backtrace_get_view(state, descriptor, offset, + sizeof(struct macho_header_32), error_callback, data, + &header_view)) + goto fail; + + memcpy(&header, header_view.data, sizeof header); + + backtrace_release_view(state, &header_view, error_callback, data); + + switch (header.magic) { + case MACH_O_MH_MAGIC_32: + is_64 = 0; + hdroffset = offset + sizeof(struct macho_header_32); + break; + case MACH_O_MH_MAGIC_64: + is_64 = 1; + hdroffset = offset + sizeof(struct macho_header_64); + break; + case MACH_O_MH_MAGIC_FAT: + case MACH_O_MH_MAGIC_FAT_64: { + struct macho_header_fat fat_header; + + hdroffset = offset + sizeof(struct macho_header_fat); + memcpy(&fat_header, &header, sizeof fat_header); + return macho_add_fat(state, filename, descriptor, 0, hdroffset, + match_uuid, base_address, skip_symtab, + fat_header.nfat_arch, + header.magic == MACH_O_MH_MAGIC_FAT_64, + error_callback, data, fileline_fn, found_sym); + } + case MACH_O_MH_CIGAM_FAT: + case MACH_O_MH_CIGAM_FAT_64: { + struct macho_header_fat fat_header; + uint32_t nfat_arch; + + hdroffset = offset + sizeof(struct macho_header_fat); + memcpy(&fat_header, &header, sizeof fat_header); + nfat_arch = __builtin_bswap32(fat_header.nfat_arch); + return macho_add_fat(state, filename, descriptor, 1, hdroffset, + match_uuid, base_address, skip_symtab, nfat_arch, + header.magic == MACH_O_MH_CIGAM_FAT_64, + error_callback, data, fileline_fn, found_sym); + } + default: + error_callback(data, "executable file is not in Mach-O format", 0); + goto fail; + } + + switch (header.filetype) { + case MACH_O_MH_EXECUTE: + case MACH_O_MH_DYLIB: + case MACH_O_MH_DSYM: + break; + default: + error_callback(data, "executable file is not an executable", 0); + goto fail; + } + + if (!backtrace_get_view(state, descriptor, hdroffset, header.sizeofcmds, + error_callback, data, &cmds_view)) + goto fail; + cmds_view_valid = 1; + + memset(&dwarf_sections, 0, sizeof dwarf_sections); + have_dwarf = 0; + memset(&uuid, 0, sizeof uuid); + have_uuid = 0; + + cmdoffset = 0; + for (i = 0; i < header.ncmds; ++i) { + const char *pcmd; + struct macho_load_command load_command; + + if (cmdoffset + sizeof load_command > header.sizeofcmds) break; + + pcmd = (const char *)cmds_view.data + cmdoffset; + memcpy(&load_command, pcmd, sizeof load_command); + + switch (load_command.cmd) { + case MACH_O_LC_SEGMENT: { + struct macho_segment_command segcmd; + + memcpy(&segcmd, pcmd, sizeof segcmd); + if (memcmp(segcmd.segname, "__DWARF\0\0\0\0\0\0\0\0\0", + MACH_O_NAMELEN) == 0) { + if (!macho_add_dwarf_segment( + state, descriptor, offset, load_command.cmd, + pcmd + sizeof segcmd, (load_command.cmdsize - sizeof segcmd), + segcmd.nsects, error_callback, data, &dwarf_sections)) + goto fail; + have_dwarf = 1; + } + } break; + + case MACH_O_LC_SEGMENT_64: { + struct macho_segment_64_command segcmd; + + memcpy(&segcmd, pcmd, sizeof segcmd); + if (memcmp(segcmd.segname, "__DWARF\0\0\0\0\0\0\0\0\0", + MACH_O_NAMELEN) == 0) { + if (!macho_add_dwarf_segment( + state, descriptor, offset, load_command.cmd, + pcmd + sizeof segcmd, (load_command.cmdsize - sizeof segcmd), + segcmd.nsects, error_callback, data, &dwarf_sections)) + goto fail; + have_dwarf = 1; + } + } break; + + case MACH_O_LC_SYMTAB: + if (!skip_symtab) { + struct macho_symtab_command symcmd; + + memcpy(&symcmd, pcmd, sizeof symcmd); + if (!macho_add_symtab(state, descriptor, base_address, is_64, + offset + symcmd.symoff, symcmd.nsyms, + offset + symcmd.stroff, symcmd.strsize, + error_callback, data)) + goto fail; + + *found_sym = 1; + } + break; + + case MACH_O_LC_UUID: { + struct macho_uuid_command uuidcmd; + + memcpy(&uuidcmd, pcmd, sizeof uuidcmd); + memcpy(&uuid[0], &uuidcmd.uuid[0], MACH_O_UUID_LEN); + have_uuid = 1; + } break; + + default: + break; + } + + cmdoffset += load_command.cmdsize; + } + + if (!backtrace_close(descriptor, error_callback, data)) goto fail; + descriptor = -1; + + backtrace_release_view(state, &cmds_view, error_callback, data); + cmds_view_valid = 0; + + if (match_uuid != NULL) { + if (!have_uuid || memcmp(match_uuid, &uuid[0], MACH_O_UUID_LEN) != 0) + return 1; + } + + if (have_dwarf) { + int is_big_endian; + + is_big_endian = 0; +#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + is_big_endian = 1; +#endif +#endif + + if (!backtrace_dwarf_add(state, base_address, &dwarf_sections, + is_big_endian, NULL, error_callback, data, + fileline_fn, NULL)) + goto fail; + } + + if (!have_dwarf && have_uuid) { + if (!macho_add_dsym(state, filename, base_address, &uuid[0], error_callback, + data, fileline_fn)) + goto fail; + } + + return 1; + +fail: + if (cmds_view_valid) + backtrace_release_view(state, &cmds_view, error_callback, data); + if (descriptor != -1) backtrace_close(descriptor, error_callback, data); + return 0; +} + +#ifdef HAVE_MACH_O_DYLD_H + +int backtrace_initialize(struct backtrace_state *state, const char *filename, + int descriptor, + backtrace_error_callback error_callback, void *data, + fileline *fileline_fn) { + uint32_t c; + uint32_t i; + int closed_descriptor; + int found_sym; + fileline macho_fileline_fn; + + closed_descriptor = 0; + found_sym = 0; + macho_fileline_fn = macho_nodebug; + + c = _dyld_image_count(); + for (i = 0; i < c; ++i) { + uintptr_t base_address; + const char *name; + int d; + fileline mff; + int mfs; + + name = _dyld_get_image_name(i); + if (name == NULL) continue; + + if (strcmp(name, filename) == 0 && !closed_descriptor) { + d = descriptor; + closed_descriptor = 1; + } else { + int does_not_exist; + + d = backtrace_open(name, error_callback, data, &does_not_exist); + if (d < 0) continue; + } + + base_address = _dyld_get_image_vmaddr_slide(i); + + mff = macho_nodebug; + if (!macho_add(state, name, d, 0, NULL, base_address, 0, error_callback, + data, &mff, &mfs)) + return 0; + + if (mff != macho_nodebug) macho_fileline_fn = mff; + if (mfs) found_sym = 1; + } + + if (!closed_descriptor) backtrace_close(descriptor, error_callback, data); + + if (!state->threaded) { + if (found_sym) + state->syminfo_fn = macho_syminfo; + else if (state->syminfo_fn == NULL) + state->syminfo_fn = macho_nosyms; + } else { + if (found_sym) + backtrace_atomic_store_pointer(&state->syminfo_fn, macho_syminfo); + else + (void)__sync_bool_compare_and_swap(&state->syminfo_fn, NULL, + macho_nosyms); + } + + if (!state->threaded) + *fileline_fn = state->fileline_fn; + else + *fileline_fn = backtrace_atomic_load_pointer(&state->fileline_fn); + + if (*fileline_fn == NULL || *fileline_fn == macho_nodebug) + *fileline_fn = macho_fileline_fn; + + return 1; +} + +#else + +int backtrace_initialize(struct backtrace_state *state, const char *filename, + int descriptor, + backtrace_error_callback error_callback, void *data, + fileline *fileline_fn) { + fileline macho_fileline_fn; + int found_sym; + + macho_fileline_fn = macho_nodebug; + if (!macho_add(state, filename, descriptor, 0, NULL, 0, 0, error_callback, + data, &macho_fileline_fn, &found_sym)) + return 0; + + if (!state->threaded) { + if (found_sym) + state->syminfo_fn = macho_syminfo; + else if (state->syminfo_fn == NULL) + state->syminfo_fn = macho_nosyms; + } else { + if (found_sym) + backtrace_atomic_store_pointer(&state->syminfo_fn, macho_syminfo); + else + (void)__sync_bool_compare_and_swap(&state->syminfo_fn, NULL, + macho_nosyms); + } + + if (!state->threaded) + *fileline_fn = state->fileline_fn; + else + *fileline_fn = backtrace_atomic_load_pointer(&state->fileline_fn); + + if (*fileline_fn == NULL || *fileline_fn == macho_nodebug) + *fileline_fn = macho_fileline_fn; + + return 1; +} + +#endif + +// mmapio.c: +#include +#include +#include +#include + +#ifndef HAVE_DECL_GETPAGESIZE +extern int getpagesize(void); +#endif + +#ifndef MAP_FAILED +#define MAP_FAILED ((void *)-1) +#endif + +int backtrace_get_view(struct backtrace_state *state ATTRIBUTE_UNUSED, + int descriptor, off_t offset, uint64_t size, + backtrace_error_callback error_callback, void *data, + struct backtrace_view *view) { + size_t pagesize; + unsigned int inpage; + off_t pageoff; + void *map; + + if ((uint64_t)(size_t)size != size) { + error_callback(data, "file size too large", 0); + return 0; + } + + pagesize = getpagesize(); + inpage = offset % pagesize; + pageoff = offset - inpage; + + size += inpage; + size = (size + (pagesize - 1)) & ~(pagesize - 1); + + map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, descriptor, pageoff); + if (map == MAP_FAILED) { + error_callback(data, "mmap", errno); + return 0; + } + + view->data = (char *)map + inpage; + view->base = map; + view->len = size; + + return 1; +} + +void backtrace_release_view(struct backtrace_state *state ATTRIBUTE_UNUSED, + struct backtrace_view *view, + backtrace_error_callback error_callback, + void *data) { + union { + const void *cv; + void *v; + } const_cast; + + const_cast.cv = view->base; + if (munmap(const_cast.v, view->len) < 0) + error_callback(data, "munmap", errno); +} + +// mmap.c: +#include +#include +#include +#include +#include +#include + +#ifndef HAVE_DECL_GETPAGESIZE +extern int getpagesize(void); +#endif + +#ifndef MAP_ANONYMOUS +#define MAP_ANONYMOUS MAP_ANON +#endif + +#ifndef MAP_FAILED +#define MAP_FAILED ((void *)-1) +#endif + +struct backtrace_freelist_struct { + struct backtrace_freelist_struct *next; + + size_t size; +}; + +static void backtrace_free_locked(struct backtrace_state *state, void *addr, + size_t size) { + if (size >= sizeof(struct backtrace_freelist_struct)) { + size_t c; + struct backtrace_freelist_struct **ppsmall; + struct backtrace_freelist_struct **pp; + struct backtrace_freelist_struct *p; + + c = 0; + ppsmall = NULL; + for (pp = &state->freelist; *pp != NULL; pp = &(*pp)->next) { + if (ppsmall == NULL || (*pp)->size < (*ppsmall)->size) ppsmall = pp; + ++c; + } + if (c >= 16) { + if (size <= (*ppsmall)->size) return; + *ppsmall = (*ppsmall)->next; + } + + p = (struct backtrace_freelist_struct *)addr; + p->next = state->freelist; + p->size = size; + state->freelist = p; + } +} + +void *backtrace_alloc(struct backtrace_state *state, size_t size, + backtrace_error_callback error_callback, void *data) { + void *ret; + int locked; + struct backtrace_freelist_struct **pp; + size_t pagesize; + size_t asksize; + void *page; + + ret = NULL; + + if (!state->threaded) + locked = 1; + else + locked = __sync_lock_test_and_set(&state->lock_alloc, 1) == 0; + + if (locked) { + for (pp = &state->freelist; *pp != NULL; pp = &(*pp)->next) { + if ((*pp)->size >= size) { + struct backtrace_freelist_struct *p; + + p = *pp; + *pp = p->next; + + size = (size + 7) & ~(size_t)7; + if (size < p->size) + backtrace_free_locked(state, (char *)p + size, p->size - size); + + ret = (void *)p; + + break; + } + } + + if (state->threaded) __sync_lock_release(&state->lock_alloc); + } + + if (ret == NULL) { + pagesize = getpagesize(); + asksize = (size + pagesize - 1) & ~(pagesize - 1); + page = mmap(NULL, asksize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (page == MAP_FAILED) { + if (error_callback) error_callback(data, "mmap", errno); + } else { + size = (size + 7) & ~(size_t)7; + if (size < asksize) + backtrace_free(state, (char *)page + size, asksize - size, + error_callback, data); + + ret = page; + } + } + + return ret; +} + +void backtrace_free(struct backtrace_state *state, void *addr, size_t size, + backtrace_error_callback error_callback ATTRIBUTE_UNUSED, + void *data ATTRIBUTE_UNUSED) { + int locked; + + if (size >= 16 * 4096) { + size_t pagesize; + + pagesize = getpagesize(); + if (((uintptr_t)addr & (pagesize - 1)) == 0 && + (size & (pagesize - 1)) == 0) { + if (munmap(addr, size) == 0) return; + } + } + + if (!state->threaded) + locked = 1; + else + locked = __sync_lock_test_and_set(&state->lock_alloc, 1) == 0; + + if (locked) { + backtrace_free_locked(state, addr, size); + + if (state->threaded) __sync_lock_release(&state->lock_alloc); + } +} + +void *backtrace_vector_grow(struct backtrace_state *state, size_t size, + backtrace_error_callback error_callback, void *data, + struct backtrace_vector *vec) { + void *ret; + + if (size > vec->alc) { + size_t pagesize; + size_t alc; + void *base; + + pagesize = getpagesize(); + alc = vec->size + size; + if (vec->size == 0) + alc = 16 * size; + else if (alc < pagesize) { + alc *= 2; + if (alc > pagesize) alc = pagesize; + } else { + alc *= 2; + alc = (alc + pagesize - 1) & ~(pagesize - 1); + } + base = backtrace_alloc(state, alc, error_callback, data); + if (base == NULL) return NULL; + if (vec->base != NULL) { + memcpy(base, vec->base, vec->size); + backtrace_free(state, vec->base, vec->size + vec->alc, error_callback, + data); + } + 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 ATTRIBUTE_UNUSED, + struct backtrace_vector *vec, + backtrace_error_callback error_callback + ATTRIBUTE_UNUSED, + void *data ATTRIBUTE_UNUSED) { + void *ret; + + ret = vec->base; + vec->base = (char *)vec->base + vec->size; + vec->size = 0; + return ret; +} + +int backtrace_vector_release(struct backtrace_state *state, + struct backtrace_vector *vec, + backtrace_error_callback error_callback, + void *data) { + size_t size; + size_t alc; + size_t aligned; + + size = vec->size; + alc = vec->alc; + aligned = (size + 7) & ~(size_t)7; + alc -= aligned - size; + + backtrace_free(state, (char *)vec->base + aligned, alc, error_callback, data); + vec->alc = 0; + if (vec->size == 0) vec->base = NULL; + return 1; +} diff --git a/thirdparty/libbacktrace/linux.c b/thirdparty/libbacktrace/linux.c new file mode 100644 index 0000000000..96bcca6c7a --- /dev/null +++ b/thirdparty/libbacktrace/linux.c @@ -0,0 +1,3865 @@ +// elf.c: +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_DL_ITERATE_PHDR +#include +#endif + +#ifndef S_ISLNK +#ifndef S_IFLNK +#define S_IFLNK 0120000 +#endif +#ifndef S_IFMT +#define S_IFMT 0170000 +#endif +#define S_ISLNK(m) (((m)&S_IFMT) == S_IFLNK) +#endif + +#ifndef __GNUC__ +#define __builtin_prefetch(p, r, l) +#define unlikely(x) (x) +#else +#define unlikely(x) __builtin_expect(!!(x), 0) +#endif + +#if !defined(HAVE_DECL_STRNLEN) || !HAVE_DECL_STRNLEN + +static size_t xstrnlen(const char *s, size_t maxlen) { + size_t i; + + for (i = 0; i < maxlen; ++i) + if (s[i] == '\0') break; + return i; +} + +#define strnlen xstrnlen + +#endif + +#ifndef HAVE_LSTAT + +static int xlstat(const char *path ATTRIBUTE_UNUSED, + struct stat *st ATTRIBUTE_UNUSED) { + return -1; +} + +#define lstat xlstat + +#endif + +#ifndef HAVE_READLINK + +static ssize_t xreadlink(const char *path ATTRIBUTE_UNUSED, + char *buf ATTRIBUTE_UNUSED, + size_t bufsz ATTRIBUTE_UNUSED) { + return -1; +} + +#define readlink xreadlink + +#endif + +#ifndef HAVE_DL_ITERATE_PHDR + +#define dl_phdr_info x_dl_phdr_info +#define dl_iterate_phdr x_dl_iterate_phdr + +struct dl_phdr_info { + uintptr_t dlpi_addr; + const char *dlpi_name; +}; + +static int dl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, + void *) ATTRIBUTE_UNUSED, + void *data ATTRIBUTE_UNUSED) { + return 0; +} + +#endif + +#if BACKTRACE_ELF_SIZE != 32 && BACKTRACE_ELF_SIZE != 64 +#error "Unknown BACKTRACE_ELF_SIZE" +#endif + +#undef EI_NIDENT +#undef EI_MAG0 +#undef EI_MAG1 +#undef EI_MAG2 +#undef EI_MAG3 +#undef EI_CLASS +#undef EI_DATA +#undef EI_VERSION +#undef ELF_MAG0 +#undef ELF_MAG1 +#undef ELF_MAG2 +#undef ELF_MAG3 +#undef ELFCLASS32 +#undef ELFCLASS64 +#undef ELFDATA2LSB +#undef ELFDATA2MSB +#undef EV_CURRENT +#undef ET_DYN +#undef EM_PPC64 +#undef EF_PPC64_ABI +#undef SHN_LORESERVE +#undef SHN_XINDEX +#undef SHN_UNDEF +#undef SHT_PROGBITS +#undef SHT_SYMTAB +#undef SHT_STRTAB +#undef SHT_DYNSYM +#undef SHF_COMPRESSED +#undef STT_OBJECT +#undef STT_FUNC +#undef NT_GNU_BUILD_ID +#undef ELFCOMPRESS_ZLIB + +typedef uint16_t b_elf_half; +typedef uint32_t b_elf_word; +typedef int32_t b_elf_sword; +#if BACKTRACE_ELF_SIZE == 32 + +typedef uint32_t b_elf_addr; +typedef uint32_t b_elf_off; +typedef uint32_t b_elf_wxword; +#else + +typedef uint64_t b_elf_addr; +typedef uint64_t b_elf_off; +typedef uint64_t b_elf_xword; +typedef int64_t b_elf_sxword; +typedef uint64_t b_elf_wxword; +#endif + +#define EI_NIDENT 16 + +typedef struct { + unsigned char e_ident[EI_NIDENT]; + b_elf_half e_type; + b_elf_half e_machine; + b_elf_word e_version; + b_elf_addr e_entry; + b_elf_off e_phoff; + b_elf_off e_shoff; + b_elf_word e_flags; + b_elf_half e_ehsize; + b_elf_half e_phentsize; + b_elf_half e_phnum; + b_elf_half e_shentsize; + b_elf_half e_shnum; + b_elf_half e_shstrndx; +} b_elf_ehdr; +#define EI_MAG0 0 +#define EI_MAG1 1 +#define EI_MAG2 2 +#define EI_MAG3 3 +#define EI_CLASS 4 +#define EI_DATA 5 +#define EI_VERSION 6 + +#define ELFMAG0 0x7f +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' + +#define ELFCLASS32 1 +#define ELFCLASS64 2 + +#define ELFDATA2LSB 1 +#define ELFDATA2MSB 2 + +#define EV_CURRENT 1 + +#define ET_DYN 3 + +#define EM_PPC64 21 +#define EF_PPC64_ABI 3 + +typedef struct { + b_elf_word sh_name; + b_elf_word sh_type; + b_elf_wxword sh_flags; + b_elf_addr sh_addr; + b_elf_off sh_offset; + b_elf_wxword sh_size; + b_elf_word sh_link; + b_elf_word sh_info; + b_elf_wxword sh_addralign; + b_elf_wxword sh_entsize; +} b_elf_shdr; +#define SHN_UNDEF 0x0000 +#define SHN_LORESERVE 0xFF00 +#define SHN_XINDEX 0xFFFF +#define SHT_PROGBITS 1 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_DYNSYM 11 + +#define SHF_COMPRESSED 0x800 + +#if BACKTRACE_ELF_SIZE == 32 + +typedef struct { + b_elf_word st_name; + b_elf_addr st_value; + b_elf_word st_size; + unsigned char st_info; + unsigned char st_other; + b_elf_half st_shndx; +} b_elf_sym; +#else +typedef struct { + b_elf_word st_name; + unsigned char st_info; + unsigned char st_other; + b_elf_half st_shndx; + b_elf_addr st_value; + b_elf_xword st_size; +} b_elf_sym; +#endif +#define STT_OBJECT 1 +#define STT_FUNC 2 + +typedef struct { + uint32_t namesz; + uint32_t descsz; + uint32_t type; + char name[1]; +} b_elf_note; + +#define NT_GNU_BUILD_ID 3 + +#if BACKTRACE_ELF_SIZE == 32 + +typedef struct { + b_elf_word ch_type; + b_elf_word ch_size; + b_elf_word ch_addralign; +} b_elf_chdr; +#else +typedef struct { + b_elf_word ch_type; + b_elf_word ch_reserved; + b_elf_xword ch_size; + b_elf_xword ch_addralign; +} b_elf_chdr; +#endif +#define ELFCOMPRESS_ZLIB 1 + +static const char *const dwarf_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; + + const unsigned char *data; + + int compressed; +}; + +struct elf_symbol { + const char *name; + + uintptr_t address; + + size_t size; +}; + +struct elf_syminfo_data { + struct elf_syminfo_data *next; + + struct elf_symbol *symbols; + + size_t count; +}; + +struct elf_view { + struct backtrace_view view; + int release; +}; + +struct elf_ppc64_opd_data { + b_elf_addr addr; + + const char *data; + + size_t size; + + struct elf_view view; +}; + +static int elf_get_view(struct backtrace_state *state, int descriptor, + const unsigned char *memory, size_t memory_size, + off_t offset, uint64_t size, + backtrace_error_callback error_callback, void *data, + struct elf_view *view) { + if (memory == NULL) { + view->release = 1; + return backtrace_get_view(state, descriptor, offset, size, error_callback, + data, &view->view); + } else { + if ((uint64_t)offset + size > (uint64_t)memory_size) { + error_callback(data, "out of range for in-memory file", 0); + return 0; + } + view->view.data = (const void *)(memory + offset); + view->view.base = NULL; + view->view.len = size; + view->release = 0; + return 1; + } +} + +static void elf_release_view(struct backtrace_state *state, + struct elf_view *view, + backtrace_error_callback error_callback, + void *data) { + if (view->release) + backtrace_release_view(state, &view->view, error_callback, data); +} + +static uint32_t elf_crc32(uint32_t crc, const unsigned char *buf, size_t len) { + static const uint32_t crc32_table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d}; + const unsigned char *end; + + crc = ~crc; + for (end = buf + len; buf < end; ++buf) + crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8); + return ~crc; +} + +static uint32_t elf_crc32_file(struct backtrace_state *state, int descriptor, + backtrace_error_callback error_callback, + void *data) { + struct stat st; + struct backtrace_view file_view; + uint32_t ret; + + if (fstat(descriptor, &st) < 0) { + error_callback(data, "fstat", errno); + return 0; + } + + if (!backtrace_get_view(state, descriptor, 0, st.st_size, error_callback, + data, &file_view)) + return 0; + + ret = elf_crc32(0, (const unsigned char *)file_view.data, st.st_size); + + backtrace_release_view(state, &file_view, error_callback, data); + + return ret; +} + +static void elf_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 ELF executable", -1); +} + +static int elf_nodebug(struct backtrace_state *state, uintptr_t pc, + backtrace_full_callback callback, + backtrace_error_callback error_callback, void *data) { + if (state->syminfo_fn != NULL && state->syminfo_fn != elf_nosyms) { + struct backtrace_call_full bdata; + + bdata.full_callback = callback; + bdata.full_error_callback = error_callback; + bdata.full_data = data; + bdata.ret = 0; + state->syminfo_fn(state, pc, backtrace_syminfo_to_full_callback, + backtrace_syminfo_to_full_error_callback, &bdata); + return bdata.ret; + } + + error_callback(data, "no debug info in ELF executable", -1); + return 0; +} + +static int elf_symbol_compare(const void *v1, const void *v2) { + const struct elf_symbol *e1 = (const struct elf_symbol *)v1; + const struct elf_symbol *e2 = (const struct elf_symbol *)v2; + + if (e1->address < e2->address) + return -1; + else if (e1->address > e2->address) + return 1; + else + return 0; +} + +static int elf_symbol_search(const void *vkey, const void *ventry) { + const uintptr_t *key = (const uintptr_t *)vkey; + const struct elf_symbol *entry = (const struct elf_symbol *)ventry; + uintptr_t addr; + + addr = *key; + if (addr < entry->address) + return -1; + else if (addr >= entry->address + entry->size) + return 1; + else + return 0; +} + +static int elf_initialize_syminfo( + struct backtrace_state *state, uintptr_t base_address, + const unsigned char *symtab_data, size_t symtab_size, + const unsigned char *strtab, size_t strtab_size, + backtrace_error_callback error_callback, void *data, + struct elf_syminfo_data *sdata, struct elf_ppc64_opd_data *opd) { + size_t sym_count; + const b_elf_sym *sym; + size_t elf_symbol_count; + size_t elf_symbol_size; + struct elf_symbol *elf_symbols; + size_t i; + unsigned int j; + + sym_count = symtab_size / sizeof(b_elf_sym); + + sym = (const b_elf_sym *)symtab_data; + elf_symbol_count = 0; + for (i = 0; i < sym_count; ++i, ++sym) { + int info; + + info = sym->st_info & 0xf; + if ((info == STT_FUNC || info == STT_OBJECT) && sym->st_shndx != SHN_UNDEF) + ++elf_symbol_count; + } + + elf_symbol_size = elf_symbol_count * sizeof(struct elf_symbol); + elf_symbols = ((struct elf_symbol *)backtrace_alloc(state, elf_symbol_size, + error_callback, data)); + if (elf_symbols == NULL) return 0; + + sym = (const b_elf_sym *)symtab_data; + j = 0; + for (i = 0; i < sym_count; ++i, ++sym) { + int info; + + info = sym->st_info & 0xf; + if (info != STT_FUNC && info != STT_OBJECT) continue; + if (sym->st_shndx == SHN_UNDEF) continue; + if (sym->st_name >= strtab_size) { + error_callback(data, "symbol string index out of range", 0); + backtrace_free(state, elf_symbols, elf_symbol_size, error_callback, data); + return 0; + } + elf_symbols[j].name = (const char *)strtab + sym->st_name; + + if (opd && sym->st_value >= opd->addr && + sym->st_value < opd->addr + opd->size) + elf_symbols[j].address = + *(const b_elf_addr *)(opd->data + (sym->st_value - opd->addr)); + else + elf_symbols[j].address = sym->st_value; + elf_symbols[j].address += base_address; + elf_symbols[j].size = sym->st_size; + ++j; + } + + backtrace_qsort(elf_symbols, elf_symbol_count, sizeof(struct elf_symbol), + elf_symbol_compare); + + sdata->next = NULL; + sdata->symbols = elf_symbols; + sdata->count = elf_symbol_count; + + return 1; +} + +static void elf_add_syminfo_data(struct backtrace_state *state, + struct elf_syminfo_data *edata) { + if (!state->threaded) { + struct elf_syminfo_data **pp; + + for (pp = (struct elf_syminfo_data **)(void *)&state->syminfo_data; + *pp != NULL; pp = &(*pp)->next) + ; + *pp = edata; + } else { + while (1) { + struct elf_syminfo_data **pp; + + pp = (struct elf_syminfo_data **)(void *)&state->syminfo_data; + + while (1) { + struct elf_syminfo_data *p; + + p = backtrace_atomic_load_pointer(pp); + + if (p == NULL) break; + + pp = &p->next; + } + + if (__sync_bool_compare_and_swap(pp, NULL, edata)) break; + } + } +} + +static void elf_syminfo( + struct backtrace_state *state, uintptr_t addr, + backtrace_syminfo_callback callback, + backtrace_error_callback error_callback ATTRIBUTE_UNUSED, void *data) { + struct elf_syminfo_data *edata; + struct elf_symbol *sym = NULL; + + if (!state->threaded) { + for (edata = (struct elf_syminfo_data *)state->syminfo_data; edata != NULL; + edata = edata->next) { + sym = ((struct elf_symbol *)bsearch(&addr, edata->symbols, edata->count, + sizeof(struct elf_symbol), + elf_symbol_search)); + if (sym != NULL) break; + } + } else { + struct elf_syminfo_data **pp; + + pp = (struct elf_syminfo_data **)(void *)&state->syminfo_data; + while (1) { + edata = backtrace_atomic_load_pointer(pp); + if (edata == NULL) break; + + sym = ((struct elf_symbol *)bsearch(&addr, edata->symbols, edata->count, + sizeof(struct elf_symbol), + elf_symbol_search)); + if (sym != NULL) break; + + pp = &edata->next; + } + } + + if (sym == NULL) + callback(data, addr, NULL, 0, 0); + else + callback(data, addr, sym->name, sym->address, sym->size); +} + +static int elf_is_symlink(const char *filename) { + struct stat st; + + if (lstat(filename, &st) < 0) return 0; + return S_ISLNK(st.st_mode); +} + +static char *elf_readlink(struct backtrace_state *state, const char *filename, + backtrace_error_callback error_callback, void *data, + size_t *plen) { + size_t len; + char *buf; + + len = 128; + while (1) { + ssize_t rl; + + buf = backtrace_alloc(state, len, error_callback, data); + if (buf == NULL) return NULL; + rl = readlink(filename, buf, len); + if (rl < 0) { + backtrace_free(state, buf, len, error_callback, data); + return NULL; + } + if ((size_t)rl < len - 1) { + buf[rl] = '\0'; + *plen = len; + return buf; + } + backtrace_free(state, buf, len, error_callback, data); + len *= 2; + } +} + +#define SYSTEM_BUILD_ID_DIR "/usr/lib/debug/.build-id/" + +static int elf_open_debugfile_by_buildid( + struct backtrace_state *state, const char *buildid_data, + size_t buildid_size, backtrace_error_callback error_callback, void *data) { + const char *const prefix = SYSTEM_BUILD_ID_DIR; + const size_t prefix_len = strlen(prefix); + const char *const suffix = ".debug"; + const size_t suffix_len = strlen(suffix); + size_t len; + char *bd_filename; + char *t; + size_t i; + int ret; + int does_not_exist; + + len = prefix_len + buildid_size * 2 + suffix_len + 2; + bd_filename = backtrace_alloc(state, len, error_callback, data); + if (bd_filename == NULL) return -1; + + t = bd_filename; + memcpy(t, prefix, prefix_len); + t += prefix_len; + for (i = 0; i < buildid_size; i++) { + unsigned char b; + unsigned char nib; + + b = (unsigned char)buildid_data[i]; + nib = (b & 0xf0) >> 4; + *t++ = nib < 10 ? '0' + nib : 'a' + nib - 10; + nib = b & 0x0f; + *t++ = nib < 10 ? '0' + nib : 'a' + nib - 10; + if (i == 0) *t++ = '/'; + } + memcpy(t, suffix, suffix_len); + t[suffix_len] = '\0'; + + ret = backtrace_open(bd_filename, error_callback, data, &does_not_exist); + + backtrace_free(state, bd_filename, len, error_callback, data); + + return ret; +} + +static int elf_try_debugfile(struct backtrace_state *state, const char *prefix, + size_t prefix_len, const char *prefix2, + size_t prefix2_len, const char *debuglink_name, + backtrace_error_callback error_callback, + void *data) { + size_t debuglink_len; + size_t try_len; + char *try; + int does_not_exist; + int ret; + + debuglink_len = strlen(debuglink_name); + try_len = prefix_len + prefix2_len + debuglink_len + 1; + try = backtrace_alloc(state, try_len, error_callback, data); + if (try == NULL) return -1; + + memcpy(try, prefix, prefix_len); + memcpy(try + prefix_len, prefix2, prefix2_len); + memcpy(try + prefix_len + prefix2_len, debuglink_name, debuglink_len); + try[prefix_len + prefix2_len + debuglink_len] = '\0'; + + ret = backtrace_open(try, error_callback, data, &does_not_exist); + + backtrace_free(state, try, try_len, error_callback, data); + + return ret; +} + +static int elf_find_debugfile_by_debuglink( + struct backtrace_state *state, const char *filename, + const char *debuglink_name, backtrace_error_callback error_callback, + void *data) { + int ret; + char *alc; + size_t alc_len; + const char *slash; + int ddescriptor; + const char *prefix; + size_t prefix_len; + + ret = -1; + alc = NULL; + alc_len = 0; + while (elf_is_symlink(filename)) { + char *new_buf; + size_t new_len; + + new_buf = elf_readlink(state, filename, error_callback, data, &new_len); + if (new_buf == NULL) break; + + if (new_buf[0] == '/') + filename = new_buf; + else { + slash = strrchr(filename, '/'); + if (slash == NULL) + filename = new_buf; + else { + size_t clen; + char *c; + + slash++; + clen = slash - filename + strlen(new_buf) + 1; + c = backtrace_alloc(state, clen, error_callback, data); + if (c == NULL) goto done; + + memcpy(c, filename, slash - filename); + memcpy(c + (slash - filename), new_buf, strlen(new_buf)); + c[slash - filename + strlen(new_buf)] = '\0'; + backtrace_free(state, new_buf, new_len, error_callback, data); + filename = c; + new_buf = c; + new_len = clen; + } + } + + if (alc != NULL) backtrace_free(state, alc, alc_len, error_callback, data); + alc = new_buf; + alc_len = new_len; + } + + slash = strrchr(filename, '/'); + if (slash == NULL) { + prefix = ""; + prefix_len = 0; + } else { + slash++; + prefix = filename; + prefix_len = slash - filename; + } + + ddescriptor = elf_try_debugfile(state, prefix, prefix_len, "", 0, + debuglink_name, error_callback, data); + if (ddescriptor >= 0) { + ret = ddescriptor; + goto done; + } + + ddescriptor = + elf_try_debugfile(state, prefix, prefix_len, ".debug/", strlen(".debug/"), + debuglink_name, error_callback, data); + if (ddescriptor >= 0) { + ret = ddescriptor; + goto done; + } + + ddescriptor = elf_try_debugfile(state, "/usr/lib/debug/", + strlen("/usr/lib/debug/"), prefix, prefix_len, + debuglink_name, error_callback, data); + if (ddescriptor >= 0) ret = ddescriptor; + +done: + if (alc != NULL && alc_len > 0) + backtrace_free(state, alc, alc_len, error_callback, data); + return ret; +} + +static int elf_open_debugfile_by_debuglink( + struct backtrace_state *state, const char *filename, + const char *debuglink_name, uint32_t debuglink_crc, + backtrace_error_callback error_callback, void *data) { + int ddescriptor; + + ddescriptor = elf_find_debugfile_by_debuglink(state, filename, debuglink_name, + error_callback, data); + if (ddescriptor < 0) return -1; + + if (debuglink_crc != 0) { + uint32_t got_crc; + + got_crc = elf_crc32_file(state, ddescriptor, error_callback, data); + if (got_crc != debuglink_crc) { + backtrace_close(ddescriptor, error_callback, data); + return -1; + } + } + + return ddescriptor; +} + +static void elf_uncompress_failed(void) {} + +static int elf_zlib_fetch(const unsigned char **ppin, + const unsigned char *pinend, uint64_t *pval, + unsigned int *pbits) { + unsigned int bits; + const unsigned char *pin; + uint64_t val; + uint32_t next; + + bits = *pbits; + if (bits >= 15) return 1; + pin = *ppin; + val = *pval; + + if (unlikely(pinend - pin < 4)) { + elf_uncompress_failed(); + return 0; + } + +#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \ + defined(__ORDER_BIG_ENDIAN__) && \ + (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ || \ + __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + + next = *(const uint32_t *)pin; + +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + next = __builtin_bswap32(next); +#endif +#else + next = pin[0] | (pin[1] << 8) | (pin[2] << 16) | (pin[3] << 24); +#endif + + val |= (uint64_t)next << bits; + bits += 32; + pin += 4; + + __builtin_prefetch(pin, 0, 0); + + *ppin = pin; + *pval = val; + *pbits = bits; + return 1; +} + +#define HUFFMAN_TABLE_SIZE (1024) + +#define HUFFMAN_VALUE_MASK 0x01ff +#define HUFFMAN_BITS_SHIFT 9 +#define HUFFMAN_BITS_MASK 0x7 +#define HUFFMAN_SECONDARY_SHIFT 12 + +#define ZDEBUG_TABLE_SIZE \ + (2 * HUFFMAN_TABLE_SIZE * sizeof(uint16_t) + (286 + 30) * sizeof(uint16_t) + \ + (286 + 30) * sizeof(unsigned char)) + +#define ZDEBUG_TABLE_CODELEN_OFFSET \ + (2 * HUFFMAN_TABLE_SIZE * sizeof(uint16_t) + (286 + 30) * sizeof(uint16_t)) + +#define ZDEBUG_TABLE_WORK_OFFSET (2 * HUFFMAN_TABLE_SIZE * sizeof(uint16_t)) + +#ifdef BACKTRACE_GENERATE_FIXED_HUFFMAN_TABLE + +static size_t final_next_secondary; + +#endif + +static int elf_zlib_inflate_table(unsigned char *codes, size_t codes_len, + uint16_t *zdebug_table, uint16_t *table) { + uint16_t count[16]; + uint16_t start[16]; + uint16_t prev[16]; + uint16_t firstcode[7]; + uint16_t *next; + size_t i; + size_t j; + unsigned int code; + size_t next_secondary; + + next = + (uint16_t *)(((unsigned char *)zdebug_table) + ZDEBUG_TABLE_WORK_OFFSET); + + memset(&count[0], 0, 16 * sizeof(uint16_t)); + for (i = 0; i < codes_len; ++i) { + if (unlikely(codes[i] >= 16)) { + elf_uncompress_failed(); + return 0; + } + + if (count[codes[i]] == 0) { + start[codes[i]] = i; + prev[codes[i]] = i; + } else { + next[prev[codes[i]]] = i; + prev[codes[i]] = i; + } + + ++count[codes[i]]; + } + + memset(table, 0, HUFFMAN_TABLE_SIZE * sizeof(uint16_t)); + + code = 0; + for (j = 1; j <= 8; ++j) { + unsigned int jcnt; + unsigned int val; + + jcnt = count[j]; + if (jcnt == 0) continue; + + if (unlikely(jcnt > (1U << j))) { + elf_uncompress_failed(); + return 0; + } + + val = start[j]; + for (i = 0; i < jcnt; ++i) { + uint16_t tval; + size_t ind; + unsigned int incr; + + if (unlikely((val & ~HUFFMAN_VALUE_MASK) != 0)) { + elf_uncompress_failed(); + return 0; + } + + tval = val | ((j - 1) << HUFFMAN_BITS_SHIFT); + + for (ind = code; ind < 0x100; ind += 1 << j) { + if (unlikely(table[ind] != 0)) { + elf_uncompress_failed(); + return 0; + } + table[ind] = tval; + } + + if (i + 1 < jcnt) val = next[val]; + + incr = 1U << (j - 1); + while ((code & incr) != 0) incr >>= 1; + if (incr == 0) + code = 0; + else { + code &= incr - 1; + code += incr; + } + } + } + + for (j = 9; j < 16; j++) { + unsigned int jcnt; + unsigned int k; + + jcnt = count[j]; + if (jcnt == 0) continue; + + firstcode[j - 9] = code; + + for (k = 0; k < j; ++k) { + if ((jcnt & (1U << k)) != 0) { + unsigned int m; + unsigned int bit; + + bit = 1U << (j - k - 1); + for (m = 0; m < j - k; ++m, bit >>= 1) { + if ((code & bit) == 0) { + code += bit; + break; + } + code &= ~bit; + } + jcnt &= ~(1U << k); + } + } + if (unlikely(jcnt != 0)) { + elf_uncompress_failed(); + return 0; + } + } + + next_secondary = 0; + for (j = 15; j >= 9; j--) { + unsigned int jcnt; + unsigned int val; + size_t primary; + size_t secondary; + size_t secondary_bits; + jcnt = count[j]; + if (jcnt == 0) continue; + + val = start[j]; + code = firstcode[j - 9]; + primary = 0x100; + secondary = 0; + secondary_bits = 0; + for (i = 0; i < jcnt; ++i) { + uint16_t tval; + size_t ind; + unsigned int incr; + + if ((code & 0xff) != primary) { + uint16_t tprimary; + + primary = code & 0xff; + + tprimary = table[primary]; + if (tprimary == 0) { + if (unlikely((next_secondary & HUFFMAN_VALUE_MASK) != + next_secondary)) { + elf_uncompress_failed(); + return 0; + } + + secondary = next_secondary; + secondary_bits = j - 8; + next_secondary += 1 << secondary_bits; + table[primary] = (secondary + ((j - 8) << HUFFMAN_BITS_SHIFT) + + (1U << HUFFMAN_SECONDARY_SHIFT)); + } else { + if (unlikely((tprimary & (1U << HUFFMAN_SECONDARY_SHIFT)) == 0)) { + elf_uncompress_failed(); + return 0; + } + secondary = tprimary & HUFFMAN_VALUE_MASK; + secondary_bits = + ((tprimary >> HUFFMAN_BITS_SHIFT) & HUFFMAN_BITS_MASK); + if (unlikely(secondary_bits < j - 8)) { + elf_uncompress_failed(); + return 0; + } + } + } + + tval = val | ((j - 8) << HUFFMAN_BITS_SHIFT); + + for (ind = code >> 8; ind < (1U << secondary_bits); + ind += 1U << (j - 8)) { + if (unlikely(table[secondary + 0x100 + ind] != 0)) { + elf_uncompress_failed(); + return 0; + } + table[secondary + 0x100 + ind] = tval; + } + + if (i + 1 < jcnt) val = next[val]; + + incr = 1U << (j - 1); + while ((code & incr) != 0) incr >>= 1; + if (incr == 0) + code = 0; + else { + code &= incr - 1; + code += incr; + } + } + } + +#ifdef BACKTRACE_GENERATE_FIXED_HUFFMAN_TABLE + final_next_secondary = next_secondary; +#endif + + return 1; +} + +static const uint16_t elf_zlib_default_table[0x170] = { + 0xd00, 0xe50, 0xe10, 0xf18, 0xd10, 0xe70, 0xe30, 0x1230, 0xd08, 0xe60, + 0xe20, 0x1210, 0xe00, 0xe80, 0xe40, 0x1250, 0xd04, 0xe58, 0xe18, 0x1200, + 0xd14, 0xe78, 0xe38, 0x1240, 0xd0c, 0xe68, 0xe28, 0x1220, 0xe08, 0xe88, + 0xe48, 0x1260, 0xd02, 0xe54, 0xe14, 0xf1c, 0xd12, 0xe74, 0xe34, 0x1238, + 0xd0a, 0xe64, 0xe24, 0x1218, 0xe04, 0xe84, 0xe44, 0x1258, 0xd06, 0xe5c, + 0xe1c, 0x1208, 0xd16, 0xe7c, 0xe3c, 0x1248, 0xd0e, 0xe6c, 0xe2c, 0x1228, + 0xe0c, 0xe8c, 0xe4c, 0x1268, 0xd01, 0xe52, 0xe12, 0xf1a, 0xd11, 0xe72, + 0xe32, 0x1234, 0xd09, 0xe62, 0xe22, 0x1214, 0xe02, 0xe82, 0xe42, 0x1254, + 0xd05, 0xe5a, 0xe1a, 0x1204, 0xd15, 0xe7a, 0xe3a, 0x1244, 0xd0d, 0xe6a, + 0xe2a, 0x1224, 0xe0a, 0xe8a, 0xe4a, 0x1264, 0xd03, 0xe56, 0xe16, 0xf1e, + 0xd13, 0xe76, 0xe36, 0x123c, 0xd0b, 0xe66, 0xe26, 0x121c, 0xe06, 0xe86, + 0xe46, 0x125c, 0xd07, 0xe5e, 0xe1e, 0x120c, 0xd17, 0xe7e, 0xe3e, 0x124c, + 0xd0f, 0xe6e, 0xe2e, 0x122c, 0xe0e, 0xe8e, 0xe4e, 0x126c, 0xd00, 0xe51, + 0xe11, 0xf19, 0xd10, 0xe71, 0xe31, 0x1232, 0xd08, 0xe61, 0xe21, 0x1212, + 0xe01, 0xe81, 0xe41, 0x1252, 0xd04, 0xe59, 0xe19, 0x1202, 0xd14, 0xe79, + 0xe39, 0x1242, 0xd0c, 0xe69, 0xe29, 0x1222, 0xe09, 0xe89, 0xe49, 0x1262, + 0xd02, 0xe55, 0xe15, 0xf1d, 0xd12, 0xe75, 0xe35, 0x123a, 0xd0a, 0xe65, + 0xe25, 0x121a, 0xe05, 0xe85, 0xe45, 0x125a, 0xd06, 0xe5d, 0xe1d, 0x120a, + 0xd16, 0xe7d, 0xe3d, 0x124a, 0xd0e, 0xe6d, 0xe2d, 0x122a, 0xe0d, 0xe8d, + 0xe4d, 0x126a, 0xd01, 0xe53, 0xe13, 0xf1b, 0xd11, 0xe73, 0xe33, 0x1236, + 0xd09, 0xe63, 0xe23, 0x1216, 0xe03, 0xe83, 0xe43, 0x1256, 0xd05, 0xe5b, + 0xe1b, 0x1206, 0xd15, 0xe7b, 0xe3b, 0x1246, 0xd0d, 0xe6b, 0xe2b, 0x1226, + 0xe0b, 0xe8b, 0xe4b, 0x1266, 0xd03, 0xe57, 0xe17, 0xf1f, 0xd13, 0xe77, + 0xe37, 0x123e, 0xd0b, 0xe67, 0xe27, 0x121e, 0xe07, 0xe87, 0xe47, 0x125e, + 0xd07, 0xe5f, 0xe1f, 0x120e, 0xd17, 0xe7f, 0xe3f, 0x124e, 0xd0f, 0xe6f, + 0xe2f, 0x122e, 0xe0f, 0xe8f, 0xe4f, 0x126e, 0x290, 0x291, 0x292, 0x293, + 0x294, 0x295, 0x296, 0x297, 0x298, 0x299, 0x29a, 0x29b, 0x29c, 0x29d, + 0x29e, 0x29f, 0x2a0, 0x2a1, 0x2a2, 0x2a3, 0x2a4, 0x2a5, 0x2a6, 0x2a7, + 0x2a8, 0x2a9, 0x2aa, 0x2ab, 0x2ac, 0x2ad, 0x2ae, 0x2af, 0x2b0, 0x2b1, + 0x2b2, 0x2b3, 0x2b4, 0x2b5, 0x2b6, 0x2b7, 0x2b8, 0x2b9, 0x2ba, 0x2bb, + 0x2bc, 0x2bd, 0x2be, 0x2bf, 0x2c0, 0x2c1, 0x2c2, 0x2c3, 0x2c4, 0x2c5, + 0x2c6, 0x2c7, 0x2c8, 0x2c9, 0x2ca, 0x2cb, 0x2cc, 0x2cd, 0x2ce, 0x2cf, + 0x2d0, 0x2d1, 0x2d2, 0x2d3, 0x2d4, 0x2d5, 0x2d6, 0x2d7, 0x2d8, 0x2d9, + 0x2da, 0x2db, 0x2dc, 0x2dd, 0x2de, 0x2df, 0x2e0, 0x2e1, 0x2e2, 0x2e3, + 0x2e4, 0x2e5, 0x2e6, 0x2e7, 0x2e8, 0x2e9, 0x2ea, 0x2eb, 0x2ec, 0x2ed, + 0x2ee, 0x2ef, 0x2f0, 0x2f1, 0x2f2, 0x2f3, 0x2f4, 0x2f5, 0x2f6, 0x2f7, + 0x2f8, 0x2f9, 0x2fa, 0x2fb, 0x2fc, 0x2fd, 0x2fe, 0x2ff, +}; + +static const uint16_t elf_zlib_default_dist_table[0x100] = { + 0x800, 0x810, 0x808, 0x818, 0x804, 0x814, 0x80c, 0x81c, 0x802, 0x812, 0x80a, + 0x81a, 0x806, 0x816, 0x80e, 0x81e, 0x801, 0x811, 0x809, 0x819, 0x805, 0x815, + 0x80d, 0x81d, 0x803, 0x813, 0x80b, 0x81b, 0x807, 0x817, 0x80f, 0x81f, 0x800, + 0x810, 0x808, 0x818, 0x804, 0x814, 0x80c, 0x81c, 0x802, 0x812, 0x80a, 0x81a, + 0x806, 0x816, 0x80e, 0x81e, 0x801, 0x811, 0x809, 0x819, 0x805, 0x815, 0x80d, + 0x81d, 0x803, 0x813, 0x80b, 0x81b, 0x807, 0x817, 0x80f, 0x81f, 0x800, 0x810, + 0x808, 0x818, 0x804, 0x814, 0x80c, 0x81c, 0x802, 0x812, 0x80a, 0x81a, 0x806, + 0x816, 0x80e, 0x81e, 0x801, 0x811, 0x809, 0x819, 0x805, 0x815, 0x80d, 0x81d, + 0x803, 0x813, 0x80b, 0x81b, 0x807, 0x817, 0x80f, 0x81f, 0x800, 0x810, 0x808, + 0x818, 0x804, 0x814, 0x80c, 0x81c, 0x802, 0x812, 0x80a, 0x81a, 0x806, 0x816, + 0x80e, 0x81e, 0x801, 0x811, 0x809, 0x819, 0x805, 0x815, 0x80d, 0x81d, 0x803, + 0x813, 0x80b, 0x81b, 0x807, 0x817, 0x80f, 0x81f, 0x800, 0x810, 0x808, 0x818, + 0x804, 0x814, 0x80c, 0x81c, 0x802, 0x812, 0x80a, 0x81a, 0x806, 0x816, 0x80e, + 0x81e, 0x801, 0x811, 0x809, 0x819, 0x805, 0x815, 0x80d, 0x81d, 0x803, 0x813, + 0x80b, 0x81b, 0x807, 0x817, 0x80f, 0x81f, 0x800, 0x810, 0x808, 0x818, 0x804, + 0x814, 0x80c, 0x81c, 0x802, 0x812, 0x80a, 0x81a, 0x806, 0x816, 0x80e, 0x81e, + 0x801, 0x811, 0x809, 0x819, 0x805, 0x815, 0x80d, 0x81d, 0x803, 0x813, 0x80b, + 0x81b, 0x807, 0x817, 0x80f, 0x81f, 0x800, 0x810, 0x808, 0x818, 0x804, 0x814, + 0x80c, 0x81c, 0x802, 0x812, 0x80a, 0x81a, 0x806, 0x816, 0x80e, 0x81e, 0x801, + 0x811, 0x809, 0x819, 0x805, 0x815, 0x80d, 0x81d, 0x803, 0x813, 0x80b, 0x81b, + 0x807, 0x817, 0x80f, 0x81f, 0x800, 0x810, 0x808, 0x818, 0x804, 0x814, 0x80c, + 0x81c, 0x802, 0x812, 0x80a, 0x81a, 0x806, 0x816, 0x80e, 0x81e, 0x801, 0x811, + 0x809, 0x819, 0x805, 0x815, 0x80d, 0x81d, 0x803, 0x813, 0x80b, 0x81b, 0x807, + 0x817, 0x80f, 0x81f, +}; + +static int elf_zlib_inflate(const unsigned char *pin, size_t sin, + uint16_t *zdebug_table, unsigned char *pout, + size_t sout) { + unsigned char *porigout; + const unsigned char *pinend; + unsigned char *poutend; + + porigout = pout; + pinend = pin + sin; + poutend = pout + sout; + while ((pinend - pin) > 4) { + uint64_t val; + unsigned int bits; + int last; + + if (unlikely((pin[0] & 0xf) != 8)) { + elf_uncompress_failed(); + return 0; + } + if (unlikely((pin[0] >> 4) > 7)) { + elf_uncompress_failed(); + return 0; + } + if (unlikely((pin[1] & 0x20) != 0)) { + elf_uncompress_failed(); + return 0; + } + val = (pin[0] << 8) | pin[1]; + if (unlikely(val % 31 != 0)) { + elf_uncompress_failed(); + return 0; + } + pin += 2; + + val = 0; + bits = 0; + while ((((uintptr_t)pin) & 3) != 0) { + val |= (uint64_t)*pin << bits; + bits += 8; + ++pin; + } + + last = 0; + + while (!last) { + unsigned int type; + const uint16_t *tlit; + const uint16_t *tdist; + + if (!elf_zlib_fetch(&pin, pinend, &val, &bits)) return 0; + + last = val & 1; + type = (val >> 1) & 3; + val >>= 3; + bits -= 3; + + if (unlikely(type == 3)) { + elf_uncompress_failed(); + return 0; + } + + if (type == 0) { + uint16_t len; + uint16_t lenc; + + while (bits >= 8) { + --pin; + bits -= 8; + } + + val = 0; + bits = 0; + if (unlikely((pinend - pin) < 4)) { + elf_uncompress_failed(); + return 0; + } + len = pin[0] | (pin[1] << 8); + lenc = pin[2] | (pin[3] << 8); + pin += 4; + lenc = ~lenc; + if (unlikely(len != lenc)) { + elf_uncompress_failed(); + return 0; + } + if (unlikely(len > (unsigned int)(pinend - pin) || + len > (unsigned int)(poutend - pout))) { + elf_uncompress_failed(); + return 0; + } + memcpy(pout, pin, len); + pout += len; + pin += len; + + while ((((uintptr_t)pin) & 3) != 0) { + val |= (uint64_t)*pin << bits; + bits += 8; + ++pin; + } + + continue; + } + + if (type == 1) { + tlit = elf_zlib_default_table; + tdist = elf_zlib_default_dist_table; + } else { + unsigned int nlit; + unsigned int ndist; + unsigned int nclen; + unsigned char codebits[19]; + unsigned char *plenbase; + unsigned char *plen; + unsigned char *plenend; + + if (!elf_zlib_fetch(&pin, pinend, &val, &bits)) return 0; + + nlit = (val & 0x1f) + 257; + val >>= 5; + ndist = (val & 0x1f) + 1; + val >>= 5; + nclen = (val & 0xf) + 4; + val >>= 4; + bits -= 14; + if (unlikely(nlit > 286 || ndist > 30)) { + elf_uncompress_failed(); + return 0; + } + + memset(&codebits[0], 0, 19); + + if (!elf_zlib_fetch(&pin, pinend, &val, &bits)) return 0; + + codebits[16] = val & 7; + codebits[17] = (val >> 3) & 7; + codebits[18] = (val >> 6) & 7; + codebits[0] = (val >> 9) & 7; + val >>= 12; + bits -= 12; + + if (nclen == 4) goto codebitsdone; + + codebits[8] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 5) goto codebitsdone; + + if (!elf_zlib_fetch(&pin, pinend, &val, &bits)) return 0; + + codebits[7] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 6) goto codebitsdone; + + codebits[9] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 7) goto codebitsdone; + + codebits[6] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 8) goto codebitsdone; + + codebits[10] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 9) goto codebitsdone; + + codebits[5] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 10) goto codebitsdone; + + if (!elf_zlib_fetch(&pin, pinend, &val, &bits)) return 0; + + codebits[11] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 11) goto codebitsdone; + + codebits[4] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 12) goto codebitsdone; + + codebits[12] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 13) goto codebitsdone; + + codebits[3] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 14) goto codebitsdone; + + codebits[13] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 15) goto codebitsdone; + + if (!elf_zlib_fetch(&pin, pinend, &val, &bits)) return 0; + + codebits[2] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 16) goto codebitsdone; + + codebits[14] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 17) goto codebitsdone; + + codebits[1] = val & 7; + val >>= 3; + bits -= 3; + + if (nclen == 18) goto codebitsdone; + + codebits[15] = val & 7; + val >>= 3; + bits -= 3; + + codebitsdone: + + if (!elf_zlib_inflate_table(codebits, 19, zdebug_table, zdebug_table)) + return 0; + + plenbase = + (((unsigned char *)zdebug_table) + ZDEBUG_TABLE_CODELEN_OFFSET); + plen = plenbase; + plenend = plen + nlit + ndist; + while (plen < plenend) { + uint16_t t; + unsigned int b; + uint16_t v; + + if (!elf_zlib_fetch(&pin, pinend, &val, &bits)) return 0; + + t = zdebug_table[val & 0xff]; + + if (unlikely((t & (1U << HUFFMAN_SECONDARY_SHIFT)) != 0)) { + elf_uncompress_failed(); + return 0; + } + + b = (t >> HUFFMAN_BITS_SHIFT) & HUFFMAN_BITS_MASK; + val >>= b + 1; + bits -= b + 1; + + v = t & HUFFMAN_VALUE_MASK; + if (v < 16) + *plen++ = v; + else if (v == 16) { + unsigned int c; + unsigned int prev; + + if (unlikely(plen == plenbase)) { + elf_uncompress_failed(); + return 0; + } + + c = 3 + (val & 0x3); + val >>= 2; + bits -= 2; + if (unlikely((unsigned int)(plenend - plen) < c)) { + elf_uncompress_failed(); + return 0; + } + + prev = plen[-1]; + switch (c) { + case 6: + *plen++ = prev; + ATTRIBUTE_FALLTHROUGH; + case 5: + *plen++ = prev; + ATTRIBUTE_FALLTHROUGH; + case 4: + *plen++ = prev; + } + *plen++ = prev; + *plen++ = prev; + *plen++ = prev; + } else if (v == 17) { + unsigned int c; + + c = 3 + (val & 0x7); + val >>= 3; + bits -= 3; + if (unlikely((unsigned int)(plenend - plen) < c)) { + elf_uncompress_failed(); + return 0; + } + + switch (c) { + case 10: + *plen++ = 0; + ATTRIBUTE_FALLTHROUGH; + case 9: + *plen++ = 0; + ATTRIBUTE_FALLTHROUGH; + case 8: + *plen++ = 0; + ATTRIBUTE_FALLTHROUGH; + case 7: + *plen++ = 0; + ATTRIBUTE_FALLTHROUGH; + case 6: + *plen++ = 0; + ATTRIBUTE_FALLTHROUGH; + case 5: + *plen++ = 0; + ATTRIBUTE_FALLTHROUGH; + case 4: + *plen++ = 0; + } + *plen++ = 0; + *plen++ = 0; + *plen++ = 0; + } else if (v == 18) { + unsigned int c; + + c = 11 + (val & 0x7f); + val >>= 7; + bits -= 7; + if (unlikely((unsigned int)(plenend - plen) < c)) { + elf_uncompress_failed(); + return 0; + } + + memset(plen, 0, c); + plen += c; + } else { + elf_uncompress_failed(); + return 0; + } + } + + plen = plenbase; + if (unlikely(plen[256] == 0)) { + elf_uncompress_failed(); + return 0; + } + + if (!elf_zlib_inflate_table(plen, nlit, zdebug_table, zdebug_table)) + return 0; + if (!elf_zlib_inflate_table(plen + nlit, ndist, zdebug_table, + zdebug_table + HUFFMAN_TABLE_SIZE)) + return 0; + tlit = zdebug_table; + tdist = zdebug_table + HUFFMAN_TABLE_SIZE; + } + + while (1) { + uint16_t t; + unsigned int b; + uint16_t v; + unsigned int lit; + + if (!elf_zlib_fetch(&pin, pinend, &val, &bits)) return 0; + + t = tlit[val & 0xff]; + b = (t >> HUFFMAN_BITS_SHIFT) & HUFFMAN_BITS_MASK; + v = t & HUFFMAN_VALUE_MASK; + + if ((t & (1U << HUFFMAN_SECONDARY_SHIFT)) == 0) { + lit = v; + val >>= b + 1; + bits -= b + 1; + } else { + t = tlit[v + 0x100 + ((val >> 8) & ((1U << b) - 1))]; + b = (t >> HUFFMAN_BITS_SHIFT) & HUFFMAN_BITS_MASK; + lit = t & HUFFMAN_VALUE_MASK; + val >>= b + 8; + bits -= b + 8; + } + + if (lit < 256) { + if (unlikely(pout == poutend)) { + elf_uncompress_failed(); + return 0; + } + + *pout++ = lit; + + __builtin_prefetch(pout, 1, 3); + } else if (lit == 256) { + break; + } else { + unsigned int dist; + unsigned int len; + + if (lit < 265) + len = lit - 257 + 3; + else if (lit == 285) + len = 258; + else if (unlikely(lit > 285)) { + elf_uncompress_failed(); + return 0; + } else { + unsigned int extra; + + if (!elf_zlib_fetch(&pin, pinend, &val, &bits)) return 0; + + lit -= 265; + extra = (lit >> 2) + 1; + len = (lit & 3) << extra; + len += 11; + len += ((1U << (extra - 1)) - 1) << 3; + len += val & ((1U << extra) - 1); + val >>= extra; + bits -= extra; + } + + if (!elf_zlib_fetch(&pin, pinend, &val, &bits)) return 0; + + t = tdist[val & 0xff]; + b = (t >> HUFFMAN_BITS_SHIFT) & HUFFMAN_BITS_MASK; + v = t & HUFFMAN_VALUE_MASK; + + if ((t & (1U << HUFFMAN_SECONDARY_SHIFT)) == 0) { + dist = v; + val >>= b + 1; + bits -= b + 1; + } else { + t = tdist[v + 0x100 + ((val >> 8) & ((1U << b) - 1))]; + b = (t >> HUFFMAN_BITS_SHIFT) & HUFFMAN_BITS_MASK; + dist = t & HUFFMAN_VALUE_MASK; + val >>= b + 8; + bits -= b + 8; + } + + if (dist == 0) { + if (unlikely(pout == porigout)) { + elf_uncompress_failed(); + return 0; + } + + if (unlikely((unsigned int)(poutend - pout) < len)) { + elf_uncompress_failed(); + return 0; + } + + memset(pout, pout[-1], len); + pout += len; + } else if (unlikely(dist > 29)) { + elf_uncompress_failed(); + return 0; + } else { + if (dist < 4) + dist = dist + 1; + else { + unsigned int extra; + + if (!elf_zlib_fetch(&pin, pinend, &val, &bits)) return 0; + + dist -= 4; + extra = (dist >> 1) + 1; + dist = (dist & 1) << extra; + dist += 5; + dist += ((1U << (extra - 1)) - 1) << 2; + dist += val & ((1U << extra) - 1); + val >>= extra; + bits -= extra; + } + + if (unlikely((unsigned int)(pout - porigout) < dist)) { + elf_uncompress_failed(); + return 0; + } + + if (unlikely((unsigned int)(poutend - pout) < len)) { + elf_uncompress_failed(); + return 0; + } + + if (dist >= len) { + memcpy(pout, pout - dist, len); + pout += len; + } else { + while (len > 0) { + unsigned int copy; + + copy = len < dist ? len : dist; + memcpy(pout, pout - dist, copy); + len -= copy; + pout += copy; + } + } + } + } + } + } + } + + if (unlikely(pout != poutend)) { + elf_uncompress_failed(); + return 0; + } + + return 1; +} + +static int elf_zlib_verify_checksum(const unsigned char *checkbytes, + const unsigned char *uncompressed, + size_t uncompressed_size) { + unsigned int i; + unsigned int cksum; + const unsigned char *p; + uint32_t s1; + uint32_t s2; + size_t hsz; + + cksum = 0; + for (i = 0; i < 4; i++) cksum = (cksum << 8) | checkbytes[i]; + + s1 = 1; + s2 = 0; + + p = uncompressed; + hsz = uncompressed_size; + while (hsz >= 5552) { + for (i = 0; i < 5552; i += 16) { + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + } + hsz -= 5552; + s1 %= 65521; + s2 %= 65521; + } + + while (hsz >= 16) { + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + s1 = s1 + *p++; + s2 = s2 + s1; + + hsz -= 16; + } + + for (i = 0; i < hsz; ++i) { + s1 = s1 + *p++; + s2 = s2 + s1; + } + + s1 %= 65521; + s2 %= 65521; + + if (unlikely((s2 << 16) + s1 != cksum)) { + elf_uncompress_failed(); + return 0; + } + + return 1; +} + +static int elf_zlib_inflate_and_verify(const unsigned char *pin, size_t sin, + uint16_t *zdebug_table, + unsigned char *pout, size_t sout) { + if (!elf_zlib_inflate(pin, sin, zdebug_table, pout, sout)) return 0; + if (!elf_zlib_verify_checksum(pin + sin - 4, pout, sout)) return 0; + return 1; +} + +static int elf_uncompress_zdebug(struct backtrace_state *state, + const unsigned char *compressed, + size_t compressed_size, uint16_t *zdebug_table, + backtrace_error_callback error_callback, + void *data, unsigned char **uncompressed, + size_t *uncompressed_size) { + size_t sz; + size_t i; + unsigned char *po; + + *uncompressed = NULL; + *uncompressed_size = 0; + + if (compressed_size < 12 || memcmp(compressed, "ZLIB", 4) != 0) return 1; + + sz = 0; + for (i = 0; i < 8; i++) sz = (sz << 8) | compressed[i + 4]; + + if (*uncompressed != NULL && *uncompressed_size >= sz) + po = *uncompressed; + else { + po = (unsigned char *)backtrace_alloc(state, sz, error_callback, data); + if (po == NULL) return 0; + } + + if (!elf_zlib_inflate_and_verify(compressed + 12, compressed_size - 12, + zdebug_table, po, sz)) + return 1; + + *uncompressed = po; + *uncompressed_size = sz; + + return 1; +} + +static int elf_uncompress_chdr(struct backtrace_state *state, + const unsigned char *compressed, + size_t compressed_size, uint16_t *zdebug_table, + backtrace_error_callback error_callback, + void *data, unsigned char **uncompressed, + size_t *uncompressed_size) { + const b_elf_chdr *chdr; + unsigned char *po; + + *uncompressed = NULL; + *uncompressed_size = 0; + + if (compressed_size < sizeof(b_elf_chdr)) return 1; + + chdr = (const b_elf_chdr *)compressed; + + if (chdr->ch_type != ELFCOMPRESS_ZLIB) { + return 1; + } + + if (*uncompressed != NULL && *uncompressed_size >= chdr->ch_size) + po = *uncompressed; + else { + po = (unsigned char *)backtrace_alloc(state, chdr->ch_size, error_callback, + data); + if (po == NULL) return 0; + } + + if (!elf_zlib_inflate_and_verify(compressed + sizeof(b_elf_chdr), + compressed_size - sizeof(b_elf_chdr), + zdebug_table, po, chdr->ch_size)) + return 1; + + *uncompressed = po; + *uncompressed_size = chdr->ch_size; + + return 1; +} + +int backtrace_uncompress_zdebug(struct backtrace_state *state, + const unsigned char *compressed, + size_t compressed_size, + backtrace_error_callback error_callback, + void *data, unsigned char **uncompressed, + size_t *uncompressed_size) { + uint16_t *zdebug_table; + int ret; + + zdebug_table = ((uint16_t *)backtrace_alloc(state, ZDEBUG_TABLE_SIZE, + error_callback, data)); + if (zdebug_table == NULL) return 0; + ret = elf_uncompress_zdebug(state, compressed, compressed_size, zdebug_table, + error_callback, data, uncompressed, + uncompressed_size); + backtrace_free(state, zdebug_table, ZDEBUG_TABLE_SIZE, error_callback, data); + return ret; +} + +#define LZMA_STATES (12) + +#define LZMA_POS_STATES (16) + +#define LZMA_DIST_STATES (4) + +#define LZMA_DIST_SLOTS (64) + +#define LZMA_DIST_MODEL_START (4) + +#define LZMA_DIST_MODEL_END (14) + +#define LZMA_FULL_DISTANCES (128) + +#define LZMA_ALIGN_SIZE (16) + +#define LZMA_LEN_LOW_SYMBOLS (8) +#define LZMA_LEN_MID_SYMBOLS (8) +#define LZMA_LEN_HIGH_SYMBOLS (256) + +#define LZMA_LITERAL_CODERS_MAX (16) +#define LZMA_LITERAL_CODER_SIZE (0x300) + +#define LZMA_PROB_IS_MATCH_LEN (LZMA_STATES * LZMA_POS_STATES) +#define LZMA_PROB_IS_REP_LEN LZMA_STATES +#define LZMA_PROB_IS_REP0_LEN LZMA_STATES +#define LZMA_PROB_IS_REP1_LEN LZMA_STATES +#define LZMA_PROB_IS_REP2_LEN LZMA_STATES +#define LZMA_PROB_IS_REP0_LONG_LEN (LZMA_STATES * LZMA_POS_STATES) +#define LZMA_PROB_DIST_SLOT_LEN (LZMA_DIST_STATES * LZMA_DIST_SLOTS) +#define LZMA_PROB_DIST_SPECIAL_LEN (LZMA_FULL_DISTANCES - LZMA_DIST_MODEL_END) +#define LZMA_PROB_DIST_ALIGN_LEN LZMA_ALIGN_SIZE +#define LZMA_PROB_MATCH_LEN_CHOICE_LEN 1 +#define LZMA_PROB_MATCH_LEN_CHOICE2_LEN 1 +#define LZMA_PROB_MATCH_LEN_LOW_LEN (LZMA_POS_STATES * LZMA_LEN_LOW_SYMBOLS) +#define LZMA_PROB_MATCH_LEN_MID_LEN (LZMA_POS_STATES * LZMA_LEN_MID_SYMBOLS) +#define LZMA_PROB_MATCH_LEN_HIGH_LEN LZMA_LEN_HIGH_SYMBOLS +#define LZMA_PROB_REP_LEN_CHOICE_LEN 1 +#define LZMA_PROB_REP_LEN_CHOICE2_LEN 1 +#define LZMA_PROB_REP_LEN_LOW_LEN (LZMA_POS_STATES * LZMA_LEN_LOW_SYMBOLS) +#define LZMA_PROB_REP_LEN_MID_LEN (LZMA_POS_STATES * LZMA_LEN_MID_SYMBOLS) +#define LZMA_PROB_REP_LEN_HIGH_LEN LZMA_LEN_HIGH_SYMBOLS +#define LZMA_PROB_LITERAL_LEN \ + (LZMA_LITERAL_CODERS_MAX * LZMA_LITERAL_CODER_SIZE) + +#define LZMA_PROB_IS_MATCH_OFFSET 0 +#define LZMA_PROB_IS_REP_OFFSET \ + (LZMA_PROB_IS_MATCH_OFFSET + LZMA_PROB_IS_MATCH_LEN) +#define LZMA_PROB_IS_REP0_OFFSET \ + (LZMA_PROB_IS_REP_OFFSET + LZMA_PROB_IS_REP_LEN) +#define LZMA_PROB_IS_REP1_OFFSET \ + (LZMA_PROB_IS_REP0_OFFSET + LZMA_PROB_IS_REP0_LEN) +#define LZMA_PROB_IS_REP2_OFFSET \ + (LZMA_PROB_IS_REP1_OFFSET + LZMA_PROB_IS_REP1_LEN) +#define LZMA_PROB_IS_REP0_LONG_OFFSET \ + (LZMA_PROB_IS_REP2_OFFSET + LZMA_PROB_IS_REP2_LEN) +#define LZMA_PROB_DIST_SLOT_OFFSET \ + (LZMA_PROB_IS_REP0_LONG_OFFSET + LZMA_PROB_IS_REP0_LONG_LEN) +#define LZMA_PROB_DIST_SPECIAL_OFFSET \ + (LZMA_PROB_DIST_SLOT_OFFSET + LZMA_PROB_DIST_SLOT_LEN) +#define LZMA_PROB_DIST_ALIGN_OFFSET \ + (LZMA_PROB_DIST_SPECIAL_OFFSET + LZMA_PROB_DIST_SPECIAL_LEN) +#define LZMA_PROB_MATCH_LEN_CHOICE_OFFSET \ + (LZMA_PROB_DIST_ALIGN_OFFSET + LZMA_PROB_DIST_ALIGN_LEN) +#define LZMA_PROB_MATCH_LEN_CHOICE2_OFFSET \ + (LZMA_PROB_MATCH_LEN_CHOICE_OFFSET + LZMA_PROB_MATCH_LEN_CHOICE_LEN) +#define LZMA_PROB_MATCH_LEN_LOW_OFFSET \ + (LZMA_PROB_MATCH_LEN_CHOICE2_OFFSET + LZMA_PROB_MATCH_LEN_CHOICE2_LEN) +#define LZMA_PROB_MATCH_LEN_MID_OFFSET \ + (LZMA_PROB_MATCH_LEN_LOW_OFFSET + LZMA_PROB_MATCH_LEN_LOW_LEN) +#define LZMA_PROB_MATCH_LEN_HIGH_OFFSET \ + (LZMA_PROB_MATCH_LEN_MID_OFFSET + LZMA_PROB_MATCH_LEN_MID_LEN) +#define LZMA_PROB_REP_LEN_CHOICE_OFFSET \ + (LZMA_PROB_MATCH_LEN_HIGH_OFFSET + LZMA_PROB_MATCH_LEN_HIGH_LEN) +#define LZMA_PROB_REP_LEN_CHOICE2_OFFSET \ + (LZMA_PROB_REP_LEN_CHOICE_OFFSET + LZMA_PROB_REP_LEN_CHOICE_LEN) +#define LZMA_PROB_REP_LEN_LOW_OFFSET \ + (LZMA_PROB_REP_LEN_CHOICE2_OFFSET + LZMA_PROB_REP_LEN_CHOICE2_LEN) +#define LZMA_PROB_REP_LEN_MID_OFFSET \ + (LZMA_PROB_REP_LEN_LOW_OFFSET + LZMA_PROB_REP_LEN_LOW_LEN) +#define LZMA_PROB_REP_LEN_HIGH_OFFSET \ + (LZMA_PROB_REP_LEN_MID_OFFSET + LZMA_PROB_REP_LEN_MID_LEN) +#define LZMA_PROB_LITERAL_OFFSET \ + (LZMA_PROB_REP_LEN_HIGH_OFFSET + LZMA_PROB_REP_LEN_HIGH_LEN) + +#define LZMA_PROB_TOTAL_COUNT (LZMA_PROB_LITERAL_OFFSET + LZMA_PROB_LITERAL_LEN) + +#if LZMA_PROB_TOTAL_COUNT != 1846 + (1 << 4) * 0x300 +#error Wrong number of LZMA probabilities +#endif + +#define LZMA_IS_MATCH(state, pos) \ + (LZMA_PROB_IS_MATCH_OFFSET + (state)*LZMA_POS_STATES + (pos)) +#define LZMA_IS_REP(state) (LZMA_PROB_IS_REP_OFFSET + (state)) +#define LZMA_IS_REP0(state) (LZMA_PROB_IS_REP0_OFFSET + (state)) +#define LZMA_IS_REP1(state) (LZMA_PROB_IS_REP1_OFFSET + (state)) +#define LZMA_IS_REP2(state) (LZMA_PROB_IS_REP2_OFFSET + (state)) +#define LZMA_IS_REP0_LONG(state, pos) \ + (LZMA_PROB_IS_REP0_LONG_OFFSET + (state)*LZMA_POS_STATES + (pos)) +#define LZMA_DIST_SLOT(dist, slot) \ + (LZMA_PROB_DIST_SLOT_OFFSET + (dist)*LZMA_DIST_SLOTS + (slot)) +#define LZMA_DIST_SPECIAL(dist) (LZMA_PROB_DIST_SPECIAL_OFFSET + (dist)) +#define LZMA_DIST_ALIGN(dist) (LZMA_PROB_DIST_ALIGN_OFFSET + (dist)) +#define LZMA_MATCH_LEN_CHOICE LZMA_PROB_MATCH_LEN_CHOICE_OFFSET +#define LZMA_MATCH_LEN_CHOICE2 LZMA_PROB_MATCH_LEN_CHOICE2_OFFSET +#define LZMA_MATCH_LEN_LOW(pos, sym) \ + (LZMA_PROB_MATCH_LEN_LOW_OFFSET + (pos)*LZMA_LEN_LOW_SYMBOLS + (sym)) +#define LZMA_MATCH_LEN_MID(pos, sym) \ + (LZMA_PROB_MATCH_LEN_MID_OFFSET + (pos)*LZMA_LEN_MID_SYMBOLS + (sym)) +#define LZMA_MATCH_LEN_HIGH(sym) (LZMA_PROB_MATCH_LEN_HIGH_OFFSET + (sym)) +#define LZMA_REP_LEN_CHOICE LZMA_PROB_REP_LEN_CHOICE_OFFSET +#define LZMA_REP_LEN_CHOICE2 LZMA_PROB_REP_LEN_CHOICE2_OFFSET +#define LZMA_REP_LEN_LOW(pos, sym) \ + (LZMA_PROB_REP_LEN_LOW_OFFSET + (pos)*LZMA_LEN_LOW_SYMBOLS + (sym)) +#define LZMA_REP_LEN_MID(pos, sym) \ + (LZMA_PROB_REP_LEN_MID_OFFSET + (pos)*LZMA_LEN_MID_SYMBOLS + (sym)) +#define LZMA_REP_LEN_HIGH(sym) (LZMA_PROB_REP_LEN_HIGH_OFFSET + (sym)) +#define LZMA_LITERAL(code, size) \ + (LZMA_PROB_LITERAL_OFFSET + (code)*LZMA_LITERAL_CODER_SIZE + (size)) + +static int elf_lzma_varint(const unsigned char *compressed, + size_t compressed_size, size_t *poffset, + uint64_t *val) { + size_t off; + int i; + uint64_t v; + unsigned char b; + + off = *poffset; + i = 0; + v = 0; + while (1) { + if (unlikely(off >= compressed_size)) { + elf_uncompress_failed(); + return 0; + } + b = compressed[off]; + v |= (b & 0x7f) << (i * 7); + ++off; + if ((b & 0x80) == 0) { + *poffset = off; + *val = v; + return 1; + } + ++i; + if (unlikely(i >= 9)) { + elf_uncompress_failed(); + return 0; + } + } +} + +static void elf_lzma_range_normalize(const unsigned char *compressed, + size_t compressed_size, size_t *poffset, + uint32_t *prange, uint32_t *pcode) { + if (*prange < (1U << 24)) { + if (unlikely(*poffset >= compressed_size)) { + elf_uncompress_failed(); + return; + } + *prange <<= 8; + *pcode <<= 8; + *pcode += compressed[*poffset]; + ++*poffset; + } +} + +static int elf_lzma_bit(const unsigned char *compressed, size_t compressed_size, + uint16_t *prob, size_t *poffset, uint32_t *prange, + uint32_t *pcode) { + uint32_t bound; + + elf_lzma_range_normalize(compressed, compressed_size, poffset, prange, pcode); + bound = (*prange >> 11) * (uint32_t)*prob; + if (*pcode < bound) { + *prange = bound; + *prob += ((1U << 11) - *prob) >> 5; + return 0; + } else { + *prange -= bound; + *pcode -= bound; + *prob -= *prob >> 5; + return 1; + } +} + +static uint32_t elf_lzma_integer(const unsigned char *compressed, + size_t compressed_size, uint16_t *probs, + uint32_t bits, size_t *poffset, + uint32_t *prange, uint32_t *pcode) { + uint32_t sym; + uint32_t i; + + sym = 1; + for (i = 0; i < bits; i++) { + int bit; + + bit = elf_lzma_bit(compressed, compressed_size, probs + sym, poffset, + prange, pcode); + sym <<= 1; + sym += bit; + } + return sym - (1 << bits); +} + +static uint32_t elf_lzma_reverse_integer(const unsigned char *compressed, + size_t compressed_size, + uint16_t *probs, uint32_t bits, + size_t *poffset, uint32_t *prange, + uint32_t *pcode) { + uint32_t sym; + uint32_t val; + uint32_t i; + + sym = 1; + val = 0; + for (i = 0; i < bits; i++) { + int bit; + + bit = elf_lzma_bit(compressed, compressed_size, probs + sym, poffset, + prange, pcode); + sym <<= 1; + sym += bit; + val += bit << i; + } + return val; +} + +static uint32_t elf_lzma_len(const unsigned char *compressed, + size_t compressed_size, uint16_t *probs, + int is_rep, unsigned int pos_state, + size_t *poffset, uint32_t *prange, + uint32_t *pcode) { + uint16_t *probs_choice; + uint16_t *probs_sym; + uint32_t bits; + uint32_t len; + + probs_choice = probs + (is_rep ? LZMA_REP_LEN_CHOICE : LZMA_MATCH_LEN_CHOICE); + if (elf_lzma_bit(compressed, compressed_size, probs_choice, poffset, prange, + pcode)) { + probs_choice = + probs + (is_rep ? LZMA_REP_LEN_CHOICE2 : LZMA_MATCH_LEN_CHOICE2); + if (elf_lzma_bit(compressed, compressed_size, probs_choice, poffset, prange, + pcode)) { + probs_sym = + probs + (is_rep ? LZMA_REP_LEN_HIGH(0) : LZMA_MATCH_LEN_HIGH(0)); + bits = 8; + len = 2 + 8 + 8; + } else { + probs_sym = probs + (is_rep ? LZMA_REP_LEN_MID(pos_state, 0) + : LZMA_MATCH_LEN_MID(pos_state, 0)); + bits = 3; + len = 2 + 8; + } + } else { + probs_sym = probs + (is_rep ? LZMA_REP_LEN_LOW(pos_state, 0) + : LZMA_MATCH_LEN_LOW(pos_state, 0)); + bits = 3; + len = 2; + } + + len += elf_lzma_integer(compressed, compressed_size, probs_sym, bits, poffset, + prange, pcode); + return len; +} + +static int elf_uncompress_lzma_block(const unsigned char *compressed, + size_t compressed_size, + unsigned char check, uint16_t *probs, + unsigned char *uncompressed, + size_t uncompressed_size, + size_t *poffset) { + size_t off; + size_t block_header_offset; + size_t block_header_size; + unsigned char block_flags; + uint64_t header_compressed_size; + uint64_t header_uncompressed_size; + unsigned char lzma2_properties; + uint32_t computed_crc; + uint32_t stream_crc; + size_t uncompressed_offset; + size_t dict_start_offset; + unsigned int lc; + unsigned int lp; + unsigned int pb; + uint32_t range; + uint32_t code; + uint32_t lstate; + uint32_t dist[4]; + + off = *poffset; + block_header_offset = off; + + if (unlikely(off >= compressed_size)) { + elf_uncompress_failed(); + return 0; + } + block_header_size = (compressed[off] + 1) * 4; + if (unlikely(off + block_header_size > compressed_size)) { + elf_uncompress_failed(); + return 0; + } + + block_flags = compressed[off + 1]; + if (unlikely((block_flags & 0x3c) != 0)) { + elf_uncompress_failed(); + return 0; + } + + off += 2; + + header_compressed_size = 0; + if ((block_flags & 0x40) != 0) { + *poffset = off; + if (!elf_lzma_varint(compressed, compressed_size, poffset, + &header_compressed_size)) + return 0; + off = *poffset; + } + + header_uncompressed_size = 0; + if ((block_flags & 0x80) != 0) { + *poffset = off; + if (!elf_lzma_varint(compressed, compressed_size, poffset, + &header_uncompressed_size)) + return 0; + off = *poffset; + } + + if (unlikely((block_flags & 0x3) != 0)) { + elf_uncompress_failed(); + return 0; + } + + if (unlikely(off + 2 >= block_header_offset + block_header_size)) { + elf_uncompress_failed(); + return 0; + } + + if (unlikely(compressed[off] != 0x21)) { + elf_uncompress_failed(); + return 0; + } + ++off; + + if (unlikely(compressed[off] != 1)) { + elf_uncompress_failed(); + return 0; + } + ++off; + + lzma2_properties = compressed[off]; + ++off; + + if (unlikely(lzma2_properties > 40)) { + elf_uncompress_failed(); + return 0; + } + + if (unlikely(off + 4 > compressed_size)) { + elf_uncompress_failed(); + return 0; + } + + off = (off + 3) & ~(size_t)3; + + if (unlikely(off + 4 > compressed_size)) { + elf_uncompress_failed(); + return 0; + } + + computed_crc = + elf_crc32(0, compressed + block_header_offset, block_header_size - 4); + stream_crc = (compressed[off] | (compressed[off + 1] << 8) | + (compressed[off + 2] << 16) | (compressed[off + 3] << 24)); + if (unlikely(computed_crc != stream_crc)) { + elf_uncompress_failed(); + return 0; + } + off += 4; + + uncompressed_offset = 0; + dict_start_offset = 0; + lc = 0; + lp = 0; + pb = 0; + lstate = 0; + while (off < compressed_size) { + unsigned char control; + + range = 0xffffffff; + code = 0; + + control = compressed[off]; + ++off; + if (unlikely(control == 0)) { + break; + } + + if (control == 1 || control >= 0xe0) { + dict_start_offset = uncompressed_offset; + } + + if (control < 0x80) { + size_t chunk_size; + + if (unlikely(control > 2)) { + elf_uncompress_failed(); + return 0; + } + + if (unlikely(off + 2 > compressed_size)) { + elf_uncompress_failed(); + return 0; + } + + chunk_size = compressed[off] << 8; + chunk_size += compressed[off + 1]; + ++chunk_size; + + off += 2; + + if (unlikely(off + chunk_size > compressed_size)) { + elf_uncompress_failed(); + return 0; + } + if (unlikely(uncompressed_offset + chunk_size > uncompressed_size)) { + elf_uncompress_failed(); + return 0; + } + + memcpy(uncompressed + uncompressed_offset, compressed + off, chunk_size); + uncompressed_offset += chunk_size; + off += chunk_size; + } else { + size_t uncompressed_chunk_start; + size_t uncompressed_chunk_size; + size_t compressed_chunk_size; + size_t limit; + + if (unlikely(off + 4 >= compressed_size)) { + elf_uncompress_failed(); + return 0; + } + + uncompressed_chunk_start = uncompressed_offset; + + uncompressed_chunk_size = (control & 0x1f) << 16; + uncompressed_chunk_size += compressed[off] << 8; + uncompressed_chunk_size += compressed[off + 1]; + ++uncompressed_chunk_size; + + compressed_chunk_size = compressed[off + 2] << 8; + compressed_chunk_size += compressed[off + 3]; + ++compressed_chunk_size; + + off += 4; + + if (control >= 0xc0) { + unsigned char props; + + if (unlikely(off >= compressed_size)) { + elf_uncompress_failed(); + return 0; + } + props = compressed[off]; + ++off; + if (unlikely(props > (4 * 5 + 4) * 9 + 8)) { + elf_uncompress_failed(); + return 0; + } + pb = 0; + while (props >= 9 * 5) { + props -= 9 * 5; + ++pb; + } + lp = 0; + while (props > 9) { + props -= 9; + ++lp; + } + lc = props; + if (unlikely(lc + lp > 4)) { + elf_uncompress_failed(); + return 0; + } + } + + if (control >= 0xa0) { + size_t i; + + lstate = 0; + memset(&dist, 0, sizeof dist); + for (i = 0; i < LZMA_PROB_TOTAL_COUNT; i++) probs[i] = 1 << 10; + range = 0xffffffff; + code = 0; + } + + if (unlikely(off + 5 > compressed_size)) { + elf_uncompress_failed(); + return 0; + } + + code = ((compressed[off + 1] << 24) + (compressed[off + 2] << 16) + + (compressed[off + 3] << 8) + compressed[off + 4]); + off += 5; + + limit = off + compressed_chunk_size; + *poffset = off; + while (*poffset < limit) { + unsigned int pos_state; + + if (unlikely(uncompressed_offset == + (uncompressed_chunk_start + uncompressed_chunk_size))) { + break; + } + + pos_state = + ((uncompressed_offset - dict_start_offset) & ((1 << pb) - 1)); + + if (elf_lzma_bit(compressed, compressed_size, + probs + LZMA_IS_MATCH(lstate, pos_state), poffset, + &range, &code)) { + uint32_t len; + + if (elf_lzma_bit(compressed, compressed_size, + probs + LZMA_IS_REP(lstate), poffset, &range, + &code)) { + int short_rep; + uint32_t next_dist; + + short_rep = 0; + if (elf_lzma_bit(compressed, compressed_size, + probs + LZMA_IS_REP0(lstate), poffset, &range, + &code)) { + if (elf_lzma_bit(compressed, compressed_size, + probs + LZMA_IS_REP1(lstate), poffset, &range, + &code)) { + if (elf_lzma_bit(compressed, compressed_size, + probs + LZMA_IS_REP2(lstate), poffset, &range, + &code)) { + next_dist = dist[3]; + dist[3] = dist[2]; + } else { + next_dist = dist[2]; + } + dist[2] = dist[1]; + } else { + next_dist = dist[1]; + } + + dist[1] = dist[0]; + dist[0] = next_dist; + } else { + if (!elf_lzma_bit(compressed, compressed_size, + (probs + LZMA_IS_REP0_LONG(lstate, pos_state)), + poffset, &range, &code)) + short_rep = 1; + } + + if (lstate < 7) + lstate = short_rep ? 9 : 8; + else + lstate = 11; + + if (short_rep) + len = 1; + else + len = elf_lzma_len(compressed, compressed_size, probs, 1, + pos_state, poffset, &range, &code); + } else { + uint32_t dist_state; + uint32_t dist_slot; + uint16_t *probs_dist; + + if (lstate < 7) + lstate = 7; + else + lstate = 10; + dist[3] = dist[2]; + dist[2] = dist[1]; + dist[1] = dist[0]; + len = elf_lzma_len(compressed, compressed_size, probs, 0, pos_state, + poffset, &range, &code); + + if (len < 4 + 2) + dist_state = len - 2; + else + dist_state = 3; + probs_dist = probs + LZMA_DIST_SLOT(dist_state, 0); + dist_slot = elf_lzma_integer(compressed, compressed_size, + probs_dist, 6, poffset, &range, &code); + if (dist_slot < LZMA_DIST_MODEL_START) + dist[0] = dist_slot; + else { + uint32_t limit; + + limit = (dist_slot >> 1) - 1; + dist[0] = 2 + (dist_slot & 1); + if (dist_slot < LZMA_DIST_MODEL_END) { + dist[0] <<= limit; + probs_dist = + (probs + LZMA_DIST_SPECIAL(dist[0] - dist_slot - 1)); + dist[0] += elf_lzma_reverse_integer(compressed, compressed_size, + probs_dist, limit, poffset, + &range, &code); + } else { + uint32_t dist0; + uint32_t i; + + dist0 = dist[0]; + for (i = 0; i < limit - 4; i++) { + uint32_t mask; + + elf_lzma_range_normalize(compressed, compressed_size, poffset, + &range, &code); + range >>= 1; + code -= range; + mask = -(code >> 31); + code += range & mask; + dist0 <<= 1; + dist0 += mask + 1; + } + dist0 <<= 4; + probs_dist = probs + LZMA_DIST_ALIGN(0); + dist0 += elf_lzma_reverse_integer(compressed, compressed_size, + probs_dist, 4, poffset, + &range, &code); + dist[0] = dist0; + } + } + } + + if (unlikely(uncompressed_offset - dict_start_offset < dist[0] + 1)) { + elf_uncompress_failed(); + return 0; + } + if (unlikely(uncompressed_offset + len > uncompressed_size)) { + elf_uncompress_failed(); + return 0; + } + + if (dist[0] == 0) { + memset(uncompressed + uncompressed_offset, + uncompressed[uncompressed_offset - 1], len); + uncompressed_offset += len; + } else if (dist[0] + 1 >= len) { + memcpy(uncompressed + uncompressed_offset, + uncompressed + uncompressed_offset - dist[0] - 1, len); + uncompressed_offset += len; + } else { + while (len > 0) { + uint32_t copy; + + copy = len < dist[0] + 1 ? len : dist[0] + 1; + memcpy(uncompressed + uncompressed_offset, + (uncompressed + uncompressed_offset - dist[0] - 1), copy); + len -= copy; + uncompressed_offset += copy; + } + } + } else { + unsigned char prev; + unsigned char low; + size_t high; + uint16_t *lit_probs; + unsigned int sym; + + if (uncompressed_offset > 0) + prev = uncompressed[uncompressed_offset - 1]; + else + prev = 0; + low = prev >> (8 - lc); + high = (((uncompressed_offset - dict_start_offset) & ((1 << lp) - 1)) + << lc); + lit_probs = probs + LZMA_LITERAL(low + high, 0); + if (lstate < 7) + sym = elf_lzma_integer(compressed, compressed_size, lit_probs, 8, + poffset, &range, &code); + else { + unsigned int match; + unsigned int bit; + unsigned int match_bit; + unsigned int idx; + + sym = 1; + if (uncompressed_offset >= dist[0] + 1) + match = uncompressed[uncompressed_offset - dist[0] - 1]; + else + match = 0; + match <<= 1; + bit = 0x100; + do { + match_bit = match & bit; + match <<= 1; + idx = bit + match_bit + sym; + sym <<= 1; + if (elf_lzma_bit(compressed, compressed_size, lit_probs + idx, + poffset, &range, &code)) { + ++sym; + bit &= match_bit; + } else { + bit &= ~match_bit; + } + } while (sym < 0x100); + } + + if (unlikely(uncompressed_offset >= uncompressed_size)) { + elf_uncompress_failed(); + return 0; + } + + uncompressed[uncompressed_offset] = (unsigned char)sym; + ++uncompressed_offset; + if (lstate <= 3) + lstate = 0; + else if (lstate <= 9) + lstate -= 3; + else + lstate -= 6; + } + } + + elf_lzma_range_normalize(compressed, compressed_size, poffset, &range, + &code); + + off = *poffset; + } + } + + off = (off + 3) & ~(size_t)3; + if (unlikely(off > compressed_size)) { + elf_uncompress_failed(); + return 0; + } + + switch (check) { + case 0: + + break; + + case 1: + + if (unlikely(off + 4 > compressed_size)) { + elf_uncompress_failed(); + return 0; + } + computed_crc = elf_crc32(0, uncompressed, uncompressed_offset); + stream_crc = (compressed[off] | (compressed[off + 1] << 8) | + (compressed[off + 2] << 16) | (compressed[off + 3] << 24)); + if (computed_crc != stream_crc) { + elf_uncompress_failed(); + return 0; + } + off += 4; + break; + + case 4: + + if (unlikely(off + 8 > compressed_size)) { + elf_uncompress_failed(); + return 0; + } + off += 8; + break; + + case 10: + + if (unlikely(off + 32 > compressed_size)) { + elf_uncompress_failed(); + return 0; + } + off += 32; + break; + + default: + elf_uncompress_failed(); + return 0; + } + + *poffset = off; + + return 1; +} + +static int elf_uncompress_lzma(struct backtrace_state *state, + const unsigned char *compressed, + size_t compressed_size, + backtrace_error_callback error_callback, + void *data, unsigned char **uncompressed, + size_t *uncompressed_size) { + size_t header_size; + size_t footer_size; + unsigned char check; + uint32_t computed_crc; + uint32_t stream_crc; + size_t offset; + size_t index_size; + size_t footer_offset; + size_t index_offset; + uint64_t index_compressed_size; + uint64_t index_uncompressed_size; + unsigned char *mem; + uint16_t *probs; + size_t compressed_block_size; + + header_size = 12; + footer_size = 12; + if (unlikely(compressed_size < header_size + footer_size)) { + elf_uncompress_failed(); + return 0; + } + + if (unlikely(memcmp(compressed, + "\375" + "7zXZ\0", + 6) != 0)) { + elf_uncompress_failed(); + return 0; + } + + if (unlikely(compressed[6] != 0)) { + elf_uncompress_failed(); + return 0; + } + check = compressed[7]; + if (unlikely((check & 0xf8) != 0)) { + elf_uncompress_failed(); + return 0; + } + + computed_crc = elf_crc32(0, compressed + 6, 2); + stream_crc = (compressed[8] | (compressed[9] << 8) | (compressed[10] << 16) | + (compressed[11] << 24)); + if (unlikely(computed_crc != stream_crc)) { + elf_uncompress_failed(); + return 0; + } + + offset = compressed_size; + if (unlikely(memcmp(compressed + offset - 2, "YZ", 2) != 0)) { + elf_uncompress_failed(); + return 0; + } + offset -= 2; + + if (unlikely(compressed[offset - 2] != 0 || + compressed[offset - 1] != check)) { + elf_uncompress_failed(); + return 0; + } + offset -= 2; + + index_size = + (compressed[offset - 4] | (compressed[offset - 3] << 8) | + (compressed[offset - 2] << 16) | (compressed[offset - 1] << 24)); + index_size = (index_size + 1) * 4; + offset -= 4; + + computed_crc = elf_crc32(0, compressed + offset, 6); + stream_crc = + (compressed[offset - 4] | (compressed[offset - 3] << 8) | + (compressed[offset - 2] << 16) | (compressed[offset - 1] << 24)); + if (unlikely(computed_crc != stream_crc)) { + elf_uncompress_failed(); + return 0; + } + offset -= 4; + + if (unlikely(offset < index_size + header_size)) { + elf_uncompress_failed(); + return 0; + } + + footer_offset = offset; + offset -= index_size; + index_offset = offset; + + if (unlikely(compressed[offset] != 0)) { + elf_uncompress_failed(); + return 0; + } + ++offset; + + if (unlikely(compressed[offset] == 0)) { + *uncompressed = NULL; + *uncompressed_size = 0; + return 1; + } + if (unlikely(compressed[offset] != 1)) { + elf_uncompress_failed(); + return 0; + } + ++offset; + + if (!elf_lzma_varint(compressed, compressed_size, &offset, + &index_compressed_size)) + return 0; + if (!elf_lzma_varint(compressed, compressed_size, &offset, + &index_uncompressed_size)) + return 0; + + offset = (offset + 3) & ~(size_t)3; + + computed_crc = elf_crc32(0, compressed + index_offset, offset - index_offset); + stream_crc = + (compressed[offset] | (compressed[offset + 1] << 8) | + (compressed[offset + 2] << 16) | (compressed[offset + 3] << 24)); + if (unlikely(computed_crc != stream_crc)) { + elf_uncompress_failed(); + return 0; + } + offset += 4; + + if (unlikely(offset != footer_offset)) { + elf_uncompress_failed(); + return 0; + } + + mem = (unsigned char *)backtrace_alloc(state, index_uncompressed_size, + error_callback, data); + if (unlikely(mem == NULL)) return 0; + *uncompressed = mem; + *uncompressed_size = index_uncompressed_size; + + probs = ((uint16_t *)backtrace_alloc( + state, LZMA_PROB_TOTAL_COUNT * sizeof(uint16_t), error_callback, data)); + if (unlikely(probs == NULL)) { + backtrace_free(state, mem, index_uncompressed_size, error_callback, data); + return 0; + } + + offset = 12; + if (!elf_uncompress_lzma_block(compressed, compressed_size, check, probs, mem, + index_uncompressed_size, &offset)) { + backtrace_free(state, mem, index_uncompressed_size, error_callback, data); + return 0; + } + + compressed_block_size = offset - 12; + if (unlikely(compressed_block_size != + ((index_compressed_size + 3) & ~(size_t)3))) { + elf_uncompress_failed(); + backtrace_free(state, mem, index_uncompressed_size, error_callback, data); + return 0; + } + + offset = (offset + 3) & ~(size_t)3; + if (unlikely(offset != index_offset)) { + elf_uncompress_failed(); + backtrace_free(state, mem, index_uncompressed_size, error_callback, data); + return 0; + } + + return 1; +} + +int backtrace_uncompress_lzma(struct backtrace_state *state, + const unsigned char *compressed, + size_t compressed_size, + backtrace_error_callback error_callback, + void *data, unsigned char **uncompressed, + size_t *uncompressed_size) { + return elf_uncompress_lzma(state, compressed, compressed_size, error_callback, + data, uncompressed, uncompressed_size); +} + +static int elf_add(struct backtrace_state *state, const char *filename, + int descriptor, const unsigned char *memory, + size_t memory_size, uintptr_t base_address, + backtrace_error_callback error_callback, void *data, + fileline *fileline_fn, int *found_sym, int *found_dwarf, + struct dwarf_data **fileline_entry, int exe, int debuginfo, + const char *with_buildid_data, uint32_t with_buildid_size) { + struct elf_view ehdr_view; + b_elf_ehdr ehdr; + off_t shoff; + unsigned int shnum; + unsigned int shstrndx; + struct elf_view shdrs_view; + int shdrs_view_valid; + const b_elf_shdr *shdrs; + const b_elf_shdr *shstrhdr; + size_t shstr_size; + off_t shstr_off; + struct elf_view names_view; + int names_view_valid; + const char *names; + unsigned int symtab_shndx; + unsigned int dynsym_shndx; + unsigned int i; + struct debug_section_info sections[DEBUG_MAX]; + struct debug_section_info zsections[DEBUG_MAX]; + struct elf_view symtab_view; + int symtab_view_valid; + struct elf_view strtab_view; + int strtab_view_valid; + struct elf_view buildid_view; + int buildid_view_valid; + const char *buildid_data; + uint32_t buildid_size; + struct elf_view debuglink_view; + int debuglink_view_valid; + const char *debuglink_name; + uint32_t debuglink_crc; + struct elf_view debugaltlink_view; + int debugaltlink_view_valid; + const char *debugaltlink_name; + const char *debugaltlink_buildid_data; + uint32_t debugaltlink_buildid_size; + struct elf_view gnu_debugdata_view; + int gnu_debugdata_view_valid; + size_t gnu_debugdata_size; + unsigned char *gnu_debugdata_uncompressed; + size_t gnu_debugdata_uncompressed_size; + off_t min_offset; + off_t max_offset; + off_t debug_size; + struct elf_view debug_view; + int debug_view_valid; + unsigned int using_debug_view; + uint16_t *zdebug_table; + struct elf_view split_debug_view[DEBUG_MAX]; + unsigned char split_debug_view_valid[DEBUG_MAX]; + struct elf_ppc64_opd_data opd_data, *opd; + struct dwarf_sections dwarf_sections; + + if (!debuginfo) { + *found_sym = 0; + *found_dwarf = 0; + } + + shdrs_view_valid = 0; + names_view_valid = 0; + symtab_view_valid = 0; + strtab_view_valid = 0; + buildid_view_valid = 0; + buildid_data = NULL; + buildid_size = 0; + debuglink_view_valid = 0; + debuglink_name = NULL; + debuglink_crc = 0; + debugaltlink_view_valid = 0; + debugaltlink_name = NULL; + debugaltlink_buildid_data = NULL; + debugaltlink_buildid_size = 0; + gnu_debugdata_view_valid = 0; + gnu_debugdata_size = 0; + debug_view_valid = 0; + memset(&split_debug_view_valid[0], 0, sizeof split_debug_view_valid); + opd = NULL; + + if (!elf_get_view(state, descriptor, memory, memory_size, 0, sizeof ehdr, + error_callback, data, &ehdr_view)) + goto fail; + + memcpy(&ehdr, ehdr_view.view.data, sizeof ehdr); + + elf_release_view(state, &ehdr_view, error_callback, data); + + if (ehdr.e_ident[EI_MAG0] != ELFMAG0 || ehdr.e_ident[EI_MAG1] != ELFMAG1 || + ehdr.e_ident[EI_MAG2] != ELFMAG2 || ehdr.e_ident[EI_MAG3] != ELFMAG3) { + error_callback(data, "executable file is not ELF", 0); + goto fail; + } + if (ehdr.e_ident[EI_VERSION] != EV_CURRENT) { + error_callback(data, "executable file is unrecognized ELF version", 0); + goto fail; + } + +#if BACKTRACE_ELF_SIZE == 32 +#define BACKTRACE_ELFCLASS ELFCLASS32 +#else +#define BACKTRACE_ELFCLASS ELFCLASS64 +#endif + + if (ehdr.e_ident[EI_CLASS] != BACKTRACE_ELFCLASS) { + error_callback(data, "executable file is unexpected ELF class", 0); + goto fail; + } + + if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB && + ehdr.e_ident[EI_DATA] != ELFDATA2MSB) { + error_callback(data, "executable file has unknown endianness", 0); + goto fail; + } + + if (exe && ehdr.e_type == ET_DYN) return -1; + + shoff = ehdr.e_shoff; + shnum = ehdr.e_shnum; + shstrndx = ehdr.e_shstrndx; + + if ((shnum == 0 || shstrndx == SHN_XINDEX) && shoff != 0) { + struct elf_view shdr_view; + const b_elf_shdr *shdr; + + if (!elf_get_view(state, descriptor, memory, memory_size, shoff, + sizeof shdr, error_callback, data, &shdr_view)) + goto fail; + + shdr = (const b_elf_shdr *)shdr_view.view.data; + + if (shnum == 0) shnum = shdr->sh_size; + + if (shstrndx == SHN_XINDEX) { + shstrndx = shdr->sh_link; + + if (shstrndx >= shnum && shstrndx >= SHN_LORESERVE + 0x100) + shstrndx -= 0x100; + } + + elf_release_view(state, &shdr_view, error_callback, data); + } + + if (shnum == 0 || shstrndx == 0) goto fail; + + if (!elf_get_view( + state, descriptor, memory, memory_size, shoff + sizeof(b_elf_shdr), + (shnum - 1) * sizeof(b_elf_shdr), error_callback, data, &shdrs_view)) + goto fail; + shdrs_view_valid = 1; + shdrs = (const b_elf_shdr *)shdrs_view.view.data; + + shstrhdr = &shdrs[shstrndx - 1]; + shstr_size = shstrhdr->sh_size; + shstr_off = shstrhdr->sh_offset; + + if (!elf_get_view(state, descriptor, memory, memory_size, shstr_off, + shstrhdr->sh_size, error_callback, data, &names_view)) + goto fail; + names_view_valid = 1; + names = (const char *)names_view.view.data; + + symtab_shndx = 0; + dynsym_shndx = 0; + + memset(sections, 0, sizeof sections); + memset(zsections, 0, sizeof zsections); + + for (i = 1; i < shnum; ++i) { + const b_elf_shdr *shdr; + unsigned int sh_name; + const char *name; + int j; + + shdr = &shdrs[i - 1]; + + if (shdr->sh_type == SHT_SYMTAB) + symtab_shndx = i; + else if (shdr->sh_type == SHT_DYNSYM) + dynsym_shndx = i; + + sh_name = shdr->sh_name; + if (sh_name >= shstr_size) { + error_callback(data, "ELF section name out of range", 0); + goto fail; + } + + name = names + sh_name; + + for (j = 0; j < (int)DEBUG_MAX; ++j) { + if (strcmp(name, dwarf_section_names[j]) == 0) { + sections[j].offset = shdr->sh_offset; + sections[j].size = shdr->sh_size; + sections[j].compressed = (shdr->sh_flags & SHF_COMPRESSED) != 0; + break; + } + } + + if (name[0] == '.' && name[1] == 'z') { + for (j = 0; j < (int)DEBUG_MAX; ++j) { + if (strcmp(name + 2, dwarf_section_names[j] + 1) == 0) { + zsections[j].offset = shdr->sh_offset; + zsections[j].size = shdr->sh_size; + break; + } + } + } + + if ((!debuginfo || with_buildid_data != NULL) && !buildid_view_valid && + strcmp(name, ".note.gnu.build-id") == 0) { + const b_elf_note *note; + + if (!elf_get_view(state, descriptor, memory, memory_size, shdr->sh_offset, + shdr->sh_size, error_callback, data, &buildid_view)) + goto fail; + + buildid_view_valid = 1; + note = (const b_elf_note *)buildid_view.view.data; + if (note->type == NT_GNU_BUILD_ID && note->namesz == 4 && + strncmp(note->name, "GNU", 4) == 0 && + shdr->sh_size <= 12 + ((note->namesz + 3) & ~3) + note->descsz) { + buildid_data = ¬e->name[0] + ((note->namesz + 3) & ~3); + buildid_size = note->descsz; + } + + if (with_buildid_size != 0) { + if (buildid_size != with_buildid_size) goto fail; + + if (memcmp(buildid_data, with_buildid_data, buildid_size) != 0) + goto fail; + } + } + + if (!debuginfo && !debuglink_view_valid && + strcmp(name, ".gnu_debuglink") == 0) { + const char *debuglink_data; + size_t crc_offset; + + if (!elf_get_view(state, descriptor, memory, memory_size, shdr->sh_offset, + shdr->sh_size, error_callback, data, &debuglink_view)) + goto fail; + + debuglink_view_valid = 1; + debuglink_data = (const char *)debuglink_view.view.data; + crc_offset = strnlen(debuglink_data, shdr->sh_size); + crc_offset = (crc_offset + 3) & ~3; + if (crc_offset + 4 <= shdr->sh_size) { + debuglink_name = debuglink_data; + debuglink_crc = *(const uint32_t *)(debuglink_data + crc_offset); + } + } + + if (!debugaltlink_view_valid && strcmp(name, ".gnu_debugaltlink") == 0) { + const char *debugaltlink_data; + size_t debugaltlink_name_len; + + if (!elf_get_view(state, descriptor, memory, memory_size, shdr->sh_offset, + shdr->sh_size, error_callback, data, + &debugaltlink_view)) + goto fail; + + debugaltlink_view_valid = 1; + debugaltlink_data = (const char *)debugaltlink_view.view.data; + debugaltlink_name = debugaltlink_data; + debugaltlink_name_len = strnlen(debugaltlink_data, shdr->sh_size); + if (debugaltlink_name_len < shdr->sh_size) { + debugaltlink_name_len += 1; + + debugaltlink_buildid_data = debugaltlink_data + debugaltlink_name_len; + debugaltlink_buildid_size = shdr->sh_size - debugaltlink_name_len; + } + } + + if (!gnu_debugdata_view_valid && strcmp(name, ".gnu_debugdata") == 0) { + if (!elf_get_view(state, descriptor, memory, memory_size, shdr->sh_offset, + shdr->sh_size, error_callback, data, + &gnu_debugdata_view)) + goto fail; + + gnu_debugdata_size = shdr->sh_size; + gnu_debugdata_view_valid = 1; + } + + if (ehdr.e_machine == EM_PPC64 && (ehdr.e_flags & EF_PPC64_ABI) < 2 && + shdr->sh_type == SHT_PROGBITS && strcmp(name, ".opd") == 0) { + if (!elf_get_view(state, descriptor, memory, memory_size, shdr->sh_offset, + shdr->sh_size, error_callback, data, &opd_data.view)) + goto fail; + + opd = &opd_data; + opd->addr = shdr->sh_addr; + opd->data = (const char *)opd_data.view.view.data; + opd->size = shdr->sh_size; + } + } + + if (symtab_shndx == 0) symtab_shndx = dynsym_shndx; + if (symtab_shndx != 0 && !debuginfo) { + const b_elf_shdr *symtab_shdr; + unsigned int strtab_shndx; + const b_elf_shdr *strtab_shdr; + struct elf_syminfo_data *sdata; + + symtab_shdr = &shdrs[symtab_shndx - 1]; + strtab_shndx = symtab_shdr->sh_link; + if (strtab_shndx >= shnum) { + error_callback(data, "ELF symbol table strtab link out of range", 0); + goto fail; + } + strtab_shdr = &shdrs[strtab_shndx - 1]; + + if (!elf_get_view(state, descriptor, memory, memory_size, + symtab_shdr->sh_offset, symtab_shdr->sh_size, + error_callback, data, &symtab_view)) + goto fail; + symtab_view_valid = 1; + + if (!elf_get_view(state, descriptor, memory, memory_size, + strtab_shdr->sh_offset, strtab_shdr->sh_size, + error_callback, data, &strtab_view)) + goto fail; + strtab_view_valid = 1; + + sdata = ((struct elf_syminfo_data *)backtrace_alloc(state, sizeof *sdata, + error_callback, data)); + if (sdata == NULL) goto fail; + + if (!elf_initialize_syminfo(state, base_address, symtab_view.view.data, + symtab_shdr->sh_size, strtab_view.view.data, + strtab_shdr->sh_size, error_callback, data, + sdata, opd)) { + backtrace_free(state, sdata, sizeof *sdata, error_callback, data); + goto fail; + } + + elf_release_view(state, &symtab_view, error_callback, data); + symtab_view_valid = 0; + strtab_view_valid = 0; + + *found_sym = 1; + + elf_add_syminfo_data(state, sdata); + } + + elf_release_view(state, &shdrs_view, error_callback, data); + shdrs_view_valid = 0; + elf_release_view(state, &names_view, error_callback, data); + names_view_valid = 0; + + if (buildid_data != NULL) { + int d; + + d = elf_open_debugfile_by_buildid(state, buildid_data, buildid_size, + error_callback, data); + if (d >= 0) { + int ret; + + elf_release_view(state, &buildid_view, error_callback, data); + if (debuglink_view_valid) + elf_release_view(state, &debuglink_view, error_callback, data); + if (debugaltlink_view_valid) + elf_release_view(state, &debugaltlink_view, error_callback, data); + ret = elf_add(state, "", d, NULL, 0, base_address, error_callback, data, + fileline_fn, found_sym, found_dwarf, NULL, 0, 1, NULL, 0); + if (ret < 0) + backtrace_close(d, error_callback, data); + else if (descriptor >= 0) + backtrace_close(descriptor, error_callback, data); + return ret; + } + } + + if (buildid_view_valid) { + elf_release_view(state, &buildid_view, error_callback, data); + buildid_view_valid = 0; + } + + if (opd) { + elf_release_view(state, &opd->view, error_callback, data); + opd = NULL; + } + + if (debuglink_name != NULL) { + int d; + + d = elf_open_debugfile_by_debuglink(state, filename, debuglink_name, + debuglink_crc, error_callback, data); + if (d >= 0) { + int ret; + + elf_release_view(state, &debuglink_view, error_callback, data); + if (debugaltlink_view_valid) + elf_release_view(state, &debugaltlink_view, error_callback, data); + ret = elf_add(state, "", d, NULL, 0, base_address, error_callback, data, + fileline_fn, found_sym, found_dwarf, NULL, 0, 1, NULL, 0); + if (ret < 0) + backtrace_close(d, error_callback, data); + else if (descriptor >= 0) + backtrace_close(descriptor, error_callback, data); + return ret; + } + } + + if (debuglink_view_valid) { + elf_release_view(state, &debuglink_view, error_callback, data); + debuglink_view_valid = 0; + } + + struct dwarf_data *fileline_altlink = NULL; + if (debugaltlink_name != NULL) { + int d; + + d = elf_open_debugfile_by_debuglink(state, filename, debugaltlink_name, 0, + error_callback, data); + if (d >= 0) { + int ret; + + ret = + elf_add(state, filename, d, NULL, 0, base_address, error_callback, + data, fileline_fn, found_sym, found_dwarf, &fileline_altlink, + 0, 1, debugaltlink_buildid_data, debugaltlink_buildid_size); + elf_release_view(state, &debugaltlink_view, error_callback, data); + debugaltlink_view_valid = 0; + if (ret < 0) { + backtrace_close(d, error_callback, data); + return ret; + } + } + } + + if (debugaltlink_view_valid) { + elf_release_view(state, &debugaltlink_view, error_callback, data); + debugaltlink_view_valid = 0; + } + + if (gnu_debugdata_view_valid) { + int ret; + + ret = elf_uncompress_lzma( + state, ((const unsigned char *)gnu_debugdata_view.view.data), + gnu_debugdata_size, error_callback, data, &gnu_debugdata_uncompressed, + &gnu_debugdata_uncompressed_size); + + elf_release_view(state, &gnu_debugdata_view, error_callback, data); + gnu_debugdata_view_valid = 0; + + if (ret) { + ret = elf_add(state, filename, -1, gnu_debugdata_uncompressed, + gnu_debugdata_uncompressed_size, base_address, + error_callback, data, fileline_fn, found_sym, found_dwarf, + NULL, 0, 0, NULL, 0); + if (ret >= 0 && descriptor >= 0) + backtrace_close(descriptor, error_callback, data); + return ret; + } + } + + min_offset = 0; + max_offset = 0; + debug_size = 0; + for (i = 0; i < (int)DEBUG_MAX; ++i) { + off_t end; + + if (sections[i].size != 0) { + 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; + debug_size += sections[i].size; + } + if (zsections[i].size != 0) { + if (min_offset == 0 || zsections[i].offset < min_offset) + min_offset = zsections[i].offset; + end = zsections[i].offset + zsections[i].size; + if (end > max_offset) max_offset = end; + debug_size += zsections[i].size; + } + } + if (min_offset == 0 || max_offset == 0) { + if (descriptor >= 0) { + if (!backtrace_close(descriptor, error_callback, data)) goto fail; + } + return 1; + } + + if (max_offset - min_offset < 0x20000000 || + max_offset - min_offset < debug_size + 0x10000) { + if (!elf_get_view(state, descriptor, memory, memory_size, min_offset, + max_offset - min_offset, error_callback, data, + &debug_view)) + goto fail; + debug_view_valid = 1; + } else { + memset(&split_debug_view[0], 0, sizeof split_debug_view); + for (i = 0; i < (int)DEBUG_MAX; ++i) { + struct debug_section_info *dsec; + + if (sections[i].size != 0) + dsec = §ions[i]; + else if (zsections[i].size != 0) + dsec = &zsections[i]; + else + continue; + + if (!elf_get_view(state, descriptor, memory, memory_size, dsec->offset, + dsec->size, error_callback, data, &split_debug_view[i])) + goto fail; + split_debug_view_valid[i] = 1; + + if (sections[i].size != 0) + sections[i].data = + ((const unsigned char *)split_debug_view[i].view.data); + else + zsections[i].data = + ((const unsigned char *)split_debug_view[i].view.data); + } + } + + if (descriptor >= 0) { + if (!backtrace_close(descriptor, error_callback, data)) goto fail; + descriptor = -1; + } + + using_debug_view = 0; + if (debug_view_valid) { + for (i = 0; i < (int)DEBUG_MAX; ++i) { + if (sections[i].size == 0) + sections[i].data = NULL; + else { + sections[i].data = ((const unsigned char *)debug_view.view.data + + (sections[i].offset - min_offset)); + ++using_debug_view; + } + + if (zsections[i].size == 0) + zsections[i].data = NULL; + else + zsections[i].data = ((const unsigned char *)debug_view.view.data + + (zsections[i].offset - min_offset)); + } + } + + zdebug_table = NULL; + for (i = 0; i < (int)DEBUG_MAX; ++i) { + if (sections[i].size == 0 && zsections[i].size > 0) { + unsigned char *uncompressed_data; + size_t uncompressed_size; + + if (zdebug_table == NULL) { + zdebug_table = ((uint16_t *)backtrace_alloc(state, ZDEBUG_TABLE_SIZE, + error_callback, data)); + if (zdebug_table == NULL) goto fail; + } + + uncompressed_data = NULL; + uncompressed_size = 0; + if (!elf_uncompress_zdebug(state, zsections[i].data, zsections[i].size, + zdebug_table, error_callback, data, + &uncompressed_data, &uncompressed_size)) + goto fail; + sections[i].data = uncompressed_data; + sections[i].size = uncompressed_size; + sections[i].compressed = 0; + + if (split_debug_view_valid[i]) { + elf_release_view(state, &split_debug_view[i], error_callback, data); + split_debug_view_valid[i] = 0; + } + } + } + + for (i = 0; i < (int)DEBUG_MAX; ++i) { + unsigned char *uncompressed_data; + size_t uncompressed_size; + + if (sections[i].size == 0 || !sections[i].compressed) continue; + + if (zdebug_table == NULL) { + zdebug_table = ((uint16_t *)backtrace_alloc(state, ZDEBUG_TABLE_SIZE, + error_callback, data)); + if (zdebug_table == NULL) goto fail; + } + + uncompressed_data = NULL; + uncompressed_size = 0; + if (!elf_uncompress_chdr(state, sections[i].data, sections[i].size, + zdebug_table, error_callback, data, + &uncompressed_data, &uncompressed_size)) + goto fail; + sections[i].data = uncompressed_data; + sections[i].size = uncompressed_size; + sections[i].compressed = 0; + + if (debug_view_valid) + --using_debug_view; + else if (split_debug_view_valid[i]) { + elf_release_view(state, &split_debug_view[i], error_callback, data); + split_debug_view_valid[i] = 0; + } + } + + if (zdebug_table != NULL) + backtrace_free(state, zdebug_table, ZDEBUG_TABLE_SIZE, error_callback, + data); + + if (debug_view_valid && using_debug_view == 0) { + elf_release_view(state, &debug_view, error_callback, data); + debug_view_valid = 0; + } + + for (i = 0; i < (int)DEBUG_MAX; ++i) { + dwarf_sections.data[i] = sections[i].data; + dwarf_sections.size[i] = sections[i].size; + } + + if (!backtrace_dwarf_add(state, base_address, &dwarf_sections, + ehdr.e_ident[EI_DATA] == ELFDATA2MSB, + fileline_altlink, error_callback, data, fileline_fn, + fileline_entry)) + goto fail; + + *found_dwarf = 1; + + return 1; + +fail: + if (shdrs_view_valid) + elf_release_view(state, &shdrs_view, error_callback, data); + if (names_view_valid) + elf_release_view(state, &names_view, error_callback, data); + if (symtab_view_valid) + elf_release_view(state, &symtab_view, error_callback, data); + if (strtab_view_valid) + elf_release_view(state, &strtab_view, error_callback, data); + if (debuglink_view_valid) + elf_release_view(state, &debuglink_view, error_callback, data); + if (debugaltlink_view_valid) + elf_release_view(state, &debugaltlink_view, error_callback, data); + if (gnu_debugdata_view_valid) + elf_release_view(state, &gnu_debugdata_view, error_callback, data); + if (buildid_view_valid) + elf_release_view(state, &buildid_view, error_callback, data); + if (debug_view_valid) + elf_release_view(state, &debug_view, error_callback, data); + for (i = 0; i < (int)DEBUG_MAX; ++i) { + if (split_debug_view_valid[i]) + elf_release_view(state, &split_debug_view[i], error_callback, data); + } + if (opd) elf_release_view(state, &opd->view, error_callback, data); + if (descriptor >= 0) backtrace_close(descriptor, error_callback, data); + return 0; +} + +struct phdr_data { + struct backtrace_state *state; + backtrace_error_callback error_callback; + void *data; + fileline *fileline_fn; + int *found_sym; + int *found_dwarf; + const char *exe_filename; + int exe_descriptor; +}; + +static int +#ifdef __i386__ + __attribute__((__force_align_arg_pointer__)) +#endif + phdr_callback(struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED, + void *pdata) { + struct phdr_data *pd = (struct phdr_data *)pdata; + const char *filename; + int descriptor; + int does_not_exist; + fileline elf_fileline_fn; + int found_dwarf; + + if (info->dlpi_name == NULL || info->dlpi_name[0] == '\0') { + if (pd->exe_descriptor == -1) return 0; + filename = pd->exe_filename; + descriptor = pd->exe_descriptor; + pd->exe_descriptor = -1; + } else { + if (pd->exe_descriptor != -1) { + backtrace_close(pd->exe_descriptor, pd->error_callback, pd->data); + pd->exe_descriptor = -1; + } + + filename = info->dlpi_name; + descriptor = backtrace_open(info->dlpi_name, pd->error_callback, pd->data, + &does_not_exist); + if (descriptor < 0) return 0; + } + + if (elf_add(pd->state, filename, descriptor, NULL, 0, info->dlpi_addr, + pd->error_callback, pd->data, &elf_fileline_fn, pd->found_sym, + &found_dwarf, NULL, 0, 0, NULL, 0)) { + if (found_dwarf) { + *pd->found_dwarf = 1; + *pd->fileline_fn = elf_fileline_fn; + } + } + + return 0; +} + +int backtrace_initialize(struct backtrace_state *state, const char *filename, + int descriptor, + backtrace_error_callback error_callback, void *data, + fileline *fileline_fn) { + int ret; + int found_sym; + int found_dwarf; + fileline elf_fileline_fn = elf_nodebug; + struct phdr_data pd; + + ret = + elf_add(state, filename, descriptor, NULL, 0, 0, error_callback, data, + &elf_fileline_fn, &found_sym, &found_dwarf, NULL, 1, 0, NULL, 0); + if (!ret) return 0; + + pd.state = state; + pd.error_callback = error_callback; + pd.data = data; + pd.fileline_fn = &elf_fileline_fn; + pd.found_sym = &found_sym; + pd.found_dwarf = &found_dwarf; + pd.exe_filename = filename; + pd.exe_descriptor = ret < 0 ? descriptor : -1; + + dl_iterate_phdr(phdr_callback, (void *)&pd); + + if (!state->threaded) { + if (found_sym) + state->syminfo_fn = elf_syminfo; + else if (state->syminfo_fn == NULL) + state->syminfo_fn = elf_nosyms; + } else { + if (found_sym) + backtrace_atomic_store_pointer(&state->syminfo_fn, elf_syminfo); + else + (void)__sync_bool_compare_and_swap(&state->syminfo_fn, NULL, elf_nosyms); + } + + if (!state->threaded) + *fileline_fn = state->fileline_fn; + else + *fileline_fn = backtrace_atomic_load_pointer(&state->fileline_fn); + + if (*fileline_fn == NULL || *fileline_fn == elf_nodebug) + *fileline_fn = elf_fileline_fn; + + return 1; +} + +// mmapio.c: +#include +#include +#include +#include + +#ifndef HAVE_DECL_GETPAGESIZE +extern int getpagesize(void); +#endif + +#ifndef MAP_FAILED +#define MAP_FAILED ((void *)-1) +#endif + +int backtrace_get_view(struct backtrace_state *state ATTRIBUTE_UNUSED, + int descriptor, off_t offset, uint64_t size, + backtrace_error_callback error_callback, void *data, + struct backtrace_view *view) { + size_t pagesize; + unsigned int inpage; + off_t pageoff; + void *map; + + if ((uint64_t)(size_t)size != size) { + error_callback(data, "file size too large", 0); + return 0; + } + + pagesize = getpagesize(); + inpage = offset % pagesize; + pageoff = offset - inpage; + + size += inpage; + size = (size + (pagesize - 1)) & ~(pagesize - 1); + + map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, descriptor, pageoff); + if (map == MAP_FAILED) { + error_callback(data, "mmap", errno); + return 0; + } + + view->data = (char *)map + inpage; + view->base = map; + view->len = size; + + return 1; +} + +void backtrace_release_view(struct backtrace_state *state ATTRIBUTE_UNUSED, + struct backtrace_view *view, + backtrace_error_callback error_callback, + void *data) { + union { + const void *cv; + void *v; + } const_cast; + + const_cast.cv = view->base; + if (munmap(const_cast.v, view->len) < 0) + error_callback(data, "munmap", errno); +} + +// mmap.c: +#include +#include +#include +#include +#include +#include + +#ifndef HAVE_DECL_GETPAGESIZE +extern int getpagesize(void); +#endif + +#ifndef MAP_ANONYMOUS +#define MAP_ANONYMOUS MAP_ANON +#endif + +#ifndef MAP_FAILED +#define MAP_FAILED ((void *)-1) +#endif + +struct backtrace_freelist_struct { + struct backtrace_freelist_struct *next; + + size_t size; +}; + +static void backtrace_free_locked(struct backtrace_state *state, void *addr, + size_t size) { + if (size >= sizeof(struct backtrace_freelist_struct)) { + size_t c; + struct backtrace_freelist_struct **ppsmall; + struct backtrace_freelist_struct **pp; + struct backtrace_freelist_struct *p; + + c = 0; + ppsmall = NULL; + for (pp = &state->freelist; *pp != NULL; pp = &(*pp)->next) { + if (ppsmall == NULL || (*pp)->size < (*ppsmall)->size) ppsmall = pp; + ++c; + } + if (c >= 16) { + if (size <= (*ppsmall)->size) return; + *ppsmall = (*ppsmall)->next; + } + + p = (struct backtrace_freelist_struct *)addr; + p->next = state->freelist; + p->size = size; + state->freelist = p; + } +} + +void *backtrace_alloc(struct backtrace_state *state, size_t size, + backtrace_error_callback error_callback, void *data) { + void *ret; + int locked; + struct backtrace_freelist_struct **pp; + size_t pagesize; + size_t asksize; + void *page; + + ret = NULL; + + if (!state->threaded) + locked = 1; + else + locked = __sync_lock_test_and_set(&state->lock_alloc, 1) == 0; + + if (locked) { + for (pp = &state->freelist; *pp != NULL; pp = &(*pp)->next) { + if ((*pp)->size >= size) { + struct backtrace_freelist_struct *p; + + p = *pp; + *pp = p->next; + + size = (size + 7) & ~(size_t)7; + if (size < p->size) + backtrace_free_locked(state, (char *)p + size, p->size - size); + + ret = (void *)p; + + break; + } + } + + if (state->threaded) __sync_lock_release(&state->lock_alloc); + } + + if (ret == NULL) { + pagesize = getpagesize(); + asksize = (size + pagesize - 1) & ~(pagesize - 1); + page = mmap(NULL, asksize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (page == MAP_FAILED) { + if (error_callback) error_callback(data, "mmap", errno); + } else { + size = (size + 7) & ~(size_t)7; + if (size < asksize) + backtrace_free(state, (char *)page + size, asksize - size, + error_callback, data); + + ret = page; + } + } + + return ret; +} + +void backtrace_free(struct backtrace_state *state, void *addr, size_t size, + backtrace_error_callback error_callback ATTRIBUTE_UNUSED, + void *data ATTRIBUTE_UNUSED) { + int locked; + + if (size >= 16 * 4096) { + size_t pagesize; + + pagesize = getpagesize(); + if (((uintptr_t)addr & (pagesize - 1)) == 0 && + (size & (pagesize - 1)) == 0) { + if (munmap(addr, size) == 0) return; + } + } + + if (!state->threaded) + locked = 1; + else + locked = __sync_lock_test_and_set(&state->lock_alloc, 1) == 0; + + if (locked) { + backtrace_free_locked(state, addr, size); + + if (state->threaded) __sync_lock_release(&state->lock_alloc); + } +} + +void *backtrace_vector_grow(struct backtrace_state *state, size_t size, + backtrace_error_callback error_callback, void *data, + struct backtrace_vector *vec) { + void *ret; + + if (size > vec->alc) { + size_t pagesize; + size_t alc; + void *base; + + pagesize = getpagesize(); + alc = vec->size + size; + if (vec->size == 0) + alc = 16 * size; + else if (alc < pagesize) { + alc *= 2; + if (alc > pagesize) alc = pagesize; + } else { + alc *= 2; + alc = (alc + pagesize - 1) & ~(pagesize - 1); + } + base = backtrace_alloc(state, alc, error_callback, data); + if (base == NULL) return NULL; + if (vec->base != NULL) { + memcpy(base, vec->base, vec->size); + backtrace_free(state, vec->base, vec->size + vec->alc, error_callback, + data); + } + 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 ATTRIBUTE_UNUSED, + struct backtrace_vector *vec, + backtrace_error_callback error_callback + ATTRIBUTE_UNUSED, + void *data ATTRIBUTE_UNUSED) { + void *ret; + + ret = vec->base; + vec->base = (char *)vec->base + vec->size; + vec->size = 0; + return ret; +} + +int backtrace_vector_release(struct backtrace_state *state, + struct backtrace_vector *vec, + backtrace_error_callback error_callback, + void *data) { + size_t size; + size_t alc; + size_t aligned; + + size = vec->size; + alc = vec->alc; + aligned = (size + 7) & ~(size_t)7; + alc -= aligned - size; + + backtrace_free(state, (char *)vec->base + aligned, alc, error_callback, data); + vec->alc = 0; + if (vec->size == 0) vec->base = NULL; + return 1; +} diff --git a/thirdparty/libbacktrace/windows.c b/thirdparty/libbacktrace/windows.c new file mode 100644 index 0000000000..415da53ebc --- /dev/null +++ b/thirdparty/libbacktrace/windows.c @@ -0,0 +1,818 @@ +// pecoff.c: +#include +#include +#include + +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 +#include +#include +#include + +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 +#include +#include + +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; +} diff --git a/vlib/builtin/builtin.c.v b/vlib/builtin/builtin.c.v index d295730919..5a4faca7a4 100644 --- a/vlib/builtin/builtin.c.v +++ b/vlib/builtin/builtin.c.v @@ -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) diff --git a/vlib/builtin/builtin_d_use_libbacktrace.c.v b/vlib/builtin/builtin_d_use_libbacktrace.c.v new file mode 100644 index 0000000000..577dcb1abc --- /dev/null +++ b/vlib/builtin/builtin_d_use_libbacktrace.c.v @@ -0,0 +1,86 @@ +[has_globals] +module builtin + +#flag -I@VEXEROOT/thirdparty/libbacktrace +#flag @VEXEROOT/thirdparty/libbacktrace/backtrace.o +#include + +// 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) +} diff --git a/vlib/builtin/builtin_notd_use_libbacktrace.c.v b/vlib/builtin/builtin_notd_use_libbacktrace.c.v new file mode 100644 index 0000000000..4a675e2753 --- /dev/null +++ b/vlib/builtin/builtin_notd_use_libbacktrace.c.v @@ -0,0 +1,4 @@ +module builtin + +fn print_libbacktrace(frames_to_skip int) { +} diff --git a/vlib/builtin/builtin_test.v b/vlib/builtin/builtin_test.v new file mode 100644 index 0000000000..65b7bc27b9 --- /dev/null +++ b/vlib/builtin/builtin_test.v @@ -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') +} diff --git a/vlib/v/gen/c/cmain.v b/vlib/v/gen/c/cmain.v index feb421597e..147797f9d6 100644 --- a/vlib/v/gen/c/cmain.v +++ b/vlib/v/gen/c/cmain.v @@ -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() {