#include "config.h"
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include "filenames.h"
#include "backtrace.h"
#include "internal.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;
}