1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00
v/thirdparty/libbacktrace/base.c

4304 lines
119 KiB
C
Raw Normal View History

// 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 <stddef.h>
#include <stdint.h>
#include <stdio.h>
#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 <sys/types.h>
// dwarf.c:
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
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 <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#if defined(HAVE_KERN_PROC_ARGS) || defined(HAVE_KERN_PROC)
#include <sys/sysctl.h>
#endif
#ifdef HAVE_MACH_O_DYLD_H
#include <mach-o/dyld.h>
#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 <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#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 <stdio.h>
#include <string.h>
#include <sys/types.h>
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 <stddef.h>
#include <sys/types.h>
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 <string.h>
#include <sys/types.h>
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 <sys/types.h>
#ifdef BACKTRACE_SUPPORTED
#include <unwind.h>
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 <unwind.h>
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