4304 lines
		
	
	
		
			119 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			4304 lines
		
	
	
		
			119 KiB
		
	
	
	
		
			C
		
	
	
| // 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
 |