You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4280 lines
110 KiB
4280 lines
110 KiB
/* dwarf.c -- Get file/line information from DWARF for backtraces. |
|
Copyright (C) 2012-2020 Free Software Foundation, Inc. |
|
Written by Ian Lance Taylor, Google. |
|
|
|
Redistribution and use in source and binary forms, with or without |
|
modification, are permitted provided that the following conditions are |
|
met: |
|
|
|
(1) Redistributions of source code must retain the above copyright |
|
notice, this list of conditions and the following disclaimer. |
|
|
|
(2) Redistributions in binary form must reproduce the above copyright |
|
notice, this list of conditions and the following disclaimer in |
|
the documentation and/or other materials provided with the |
|
distribution. |
|
|
|
(3) The name of the author may not be used to |
|
endorse or promote products derived from this software without |
|
specific prior written permission. |
|
|
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
POSSIBILITY OF SUCH DAMAGE. */ |
|
|
|
#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" |
|
|
|
/* DWARF constants. */ |
|
|
|
enum dwarf_tag { |
|
DW_TAG_entry_point = 0x3, |
|
DW_TAG_compile_unit = 0x11, |
|
DW_TAG_inlined_subroutine = 0x1d, |
|
DW_TAG_subprogram = 0x2e, |
|
}; |
|
|
|
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 |
|
|
|
/* If strnlen is not declared, provide our own version. */ |
|
|
|
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 |
|
|
|
/* A buffer to read DWARF info. */ |
|
|
|
struct dwarf_buf |
|
{ |
|
/* Buffer name for error messages. */ |
|
const char *name; |
|
/* Start of the buffer. */ |
|
const unsigned char *start; |
|
/* Next byte to read. */ |
|
const unsigned char *buf; |
|
/* The number of bytes remaining. */ |
|
size_t left; |
|
/* Whether the data is big-endian. */ |
|
int is_bigendian; |
|
/* Error callback routine. */ |
|
backtrace_error_callback error_callback; |
|
/* Data for error_callback. */ |
|
void *data; |
|
/* Non-zero if we've reported an underflow error. */ |
|
int reported_underflow; |
|
}; |
|
|
|
/* A single attribute in a DWARF abbreviation. */ |
|
|
|
struct attr |
|
{ |
|
/* The attribute name. */ |
|
enum dwarf_attribute name; |
|
/* The attribute form. */ |
|
enum dwarf_form form; |
|
/* The attribute value, for DW_FORM_implicit_const. */ |
|
int64_t val; |
|
}; |
|
|
|
/* A single DWARF abbreviation. */ |
|
|
|
struct abbrev |
|
{ |
|
/* The abbrev code--the number used to refer to the abbrev. */ |
|
uint64_t code; |
|
/* The entry tag. */ |
|
enum dwarf_tag tag; |
|
/* Non-zero if this abbrev has child entries. */ |
|
int has_children; |
|
/* The number of attributes. */ |
|
size_t num_attrs; |
|
/* The attributes. */ |
|
struct attr *attrs; |
|
}; |
|
|
|
/* The DWARF abbreviations for a compilation unit. This structure |
|
only exists while reading the compilation unit. Most DWARF readers |
|
seem to a hash table to map abbrev ID's to abbrev entries. |
|
However, we primarily care about GCC, and GCC simply issues ID's in |
|
numerical order starting at 1. So we simply keep a sorted vector, |
|
and try to just look up the code. */ |
|
|
|
struct abbrevs |
|
{ |
|
/* The number of abbrevs in the vector. */ |
|
size_t num_abbrevs; |
|
/* The abbrevs, sorted by the code field. */ |
|
struct abbrev *abbrevs; |
|
}; |
|
|
|
/* The different kinds of attribute values. */ |
|
|
|
enum attr_val_encoding |
|
{ |
|
/* No attribute value. */ |
|
ATTR_VAL_NONE, |
|
/* An address. */ |
|
ATTR_VAL_ADDRESS, |
|
/* An index into the .debug_addr section, whose value is relative to |
|
* the DW_AT_addr_base attribute of the compilation unit. */ |
|
ATTR_VAL_ADDRESS_INDEX, |
|
/* A unsigned integer. */ |
|
ATTR_VAL_UINT, |
|
/* A sigd integer. */ |
|
ATTR_VAL_SINT, |
|
/* A string. */ |
|
ATTR_VAL_STRING, |
|
/* An index into the .debug_str_offsets section. */ |
|
ATTR_VAL_STRING_INDEX, |
|
/* An offset to other data in the containing unit. */ |
|
ATTR_VAL_REF_UNIT, |
|
/* An offset to other data within the .debug_info section. */ |
|
ATTR_VAL_REF_INFO, |
|
/* An offset to other data within the alt .debug_info section. */ |
|
ATTR_VAL_REF_ALT_INFO, |
|
/* An offset to data in some other section. */ |
|
ATTR_VAL_REF_SECTION, |
|
/* A type signature. */ |
|
ATTR_VAL_REF_TYPE, |
|
/* An index into the .debug_rnglists section. */ |
|
ATTR_VAL_RNGLISTS_INDEX, |
|
/* A block of data (not represented). */ |
|
ATTR_VAL_BLOCK, |
|
/* An expression (not represented). */ |
|
ATTR_VAL_EXPR, |
|
}; |
|
|
|
/* An attribute value. */ |
|
|
|
struct attr_val |
|
{ |
|
/* How the value is stored in the field u. */ |
|
enum attr_val_encoding encoding; |
|
union |
|
{ |
|
/* ATTR_VAL_ADDRESS*, ATTR_VAL_UINT, ATTR_VAL_REF*. */ |
|
uint64_t uint; |
|
/* ATTR_VAL_SINT. */ |
|
int64_t sint; |
|
/* ATTR_VAL_STRING. */ |
|
const char *string; |
|
/* ATTR_VAL_BLOCK not stored. */ |
|
} u; |
|
}; |
|
|
|
/* The line number program header. */ |
|
|
|
struct line_header |
|
{ |
|
/* The version of the line number information. */ |
|
int version; |
|
/* Address size. */ |
|
int addrsize; |
|
/* The minimum instruction length. */ |
|
unsigned int min_insn_len; |
|
/* The maximum number of ops per instruction. */ |
|
unsigned int max_ops_per_insn; |
|
/* The line base for special opcodes. */ |
|
int line_base; |
|
/* The line range for special opcodes. */ |
|
unsigned int line_range; |
|
/* The opcode base--the first special opcode. */ |
|
unsigned int opcode_base; |
|
/* Opcode lengths, indexed by opcode - 1. */ |
|
const unsigned char *opcode_lengths; |
|
/* The number of directory entries. */ |
|
size_t dirs_count; |
|
/* The directory entries. */ |
|
const char **dirs; |
|
/* The number of filenames. */ |
|
size_t filenames_count; |
|
/* The filenames. */ |
|
const char **filenames; |
|
}; |
|
|
|
/* A format description from a line header. */ |
|
|
|
struct line_header_format |
|
{ |
|
int lnct; /* LNCT code. */ |
|
enum dwarf_form form; /* Form of entry data. */ |
|
}; |
|
|
|
/* Map a single PC value to a file/line. We will keep a vector of |
|
these sorted by PC value. Each file/line will be correct from the |
|
PC up to the PC of the next entry if there is one. We allocate one |
|
extra entry at the end so that we can use bsearch. */ |
|
|
|
struct line |
|
{ |
|
/* PC. */ |
|
uintptr_t pc; |
|
/* File name. Many entries in the array are expected to point to |
|
the same file name. */ |
|
const char *filename; |
|
/* Line number. */ |
|
int lineno; |
|
/* Index of the object in the original array read from the DWARF |
|
section, before it has been sorted. The index makes it possible |
|
to use Quicksort and maintain stability. */ |
|
int idx; |
|
}; |
|
|
|
/* A growable vector of line number information. This is used while |
|
reading the line numbers. */ |
|
|
|
struct line_vector |
|
{ |
|
/* Memory. This is an array of struct line. */ |
|
struct backtrace_vector vec; |
|
/* Number of valid mappings. */ |
|
size_t count; |
|
}; |
|
|
|
/* A function described in the debug info. */ |
|
|
|
struct function |
|
{ |
|
/* The name of the function. */ |
|
const char *name; |
|
/* If this is an inlined function, the filename of the call |
|
site. */ |
|
const char *caller_filename; |
|
/* If this is an inlined function, the line number of the call |
|
site. */ |
|
int caller_lineno; |
|
/* Map PC ranges to inlined functions. */ |
|
struct function_addrs *function_addrs; |
|
size_t function_addrs_count; |
|
}; |
|
|
|
/* An address range for a function. This maps a PC value to a |
|
specific function. */ |
|
|
|
struct function_addrs |
|
{ |
|
/* Range is LOW <= PC < HIGH. */ |
|
uint64_t low; |
|
uint64_t high; |
|
/* Function for this address range. */ |
|
struct function *function; |
|
}; |
|
|
|
/* A growable vector of function address ranges. */ |
|
|
|
struct function_vector |
|
{ |
|
/* Memory. This is an array of struct function_addrs. */ |
|
struct backtrace_vector vec; |
|
/* Number of address ranges present. */ |
|
size_t count; |
|
}; |
|
|
|
/* A DWARF compilation unit. This only holds the information we need |
|
to map a PC to a file and line. */ |
|
|
|
struct unit |
|
{ |
|
/* The first entry for this compilation unit. */ |
|
const unsigned char *unit_data; |
|
/* The length of the data for this compilation unit. */ |
|
size_t unit_data_len; |
|
/* The offset of UNIT_DATA from the start of the information for |
|
this compilation unit. */ |
|
size_t unit_data_offset; |
|
/* Offset of the start of the compilation unit from the start of the |
|
.debug_info section. */ |
|
size_t low_offset; |
|
/* Offset of the end of the compilation unit from the start of the |
|
.debug_info section. */ |
|
size_t high_offset; |
|
/* DWARF version. */ |
|
int version; |
|
/* Whether unit is DWARF64. */ |
|
int is_dwarf64; |
|
/* Address size. */ |
|
int addrsize; |
|
/* Offset into line number information. */ |
|
off_t lineoff; |
|
/* Offset of compilation unit in .debug_str_offsets. */ |
|
uint64_t str_offsets_base; |
|
/* Offset of compilation unit in .debug_addr. */ |
|
uint64_t addr_base; |
|
/* Offset of compilation unit in .debug_rnglists. */ |
|
uint64_t rnglists_base; |
|
/* Primary source file. */ |
|
const char *filename; |
|
/* Compilation command working directory. */ |
|
const char *comp_dir; |
|
/* Absolute file name, only set if needed. */ |
|
const char *abs_filename; |
|
/* The abbreviations for this unit. */ |
|
struct abbrevs abbrevs; |
|
|
|
/* The fields above this point are read in during initialization and |
|
may be accessed freely. The fields below this point are read in |
|
as needed, and therefore require care, as different threads may |
|
try to initialize them simultaneously. */ |
|
|
|
/* PC to line number mapping. This is NULL if the values have not |
|
been read. This is (struct line *) -1 if there was an error |
|
reading the values. */ |
|
struct line *lines; |
|
/* Number of entries in lines. */ |
|
size_t lines_count; |
|
/* PC ranges to function. */ |
|
struct function_addrs *function_addrs; |
|
size_t function_addrs_count; |
|
}; |
|
|
|
/* An address range for a compilation unit. This maps a PC value to a |
|
specific compilation unit. Note that we invert the representation |
|
in DWARF: instead of listing the units and attaching a list of |
|
ranges, we list the ranges and have each one point to the unit. |
|
This lets us do a binary search to find the unit. */ |
|
|
|
struct unit_addrs |
|
{ |
|
/* Range is LOW <= PC < HIGH. */ |
|
uint64_t low; |
|
uint64_t high; |
|
/* Compilation unit for this address range. */ |
|
struct unit *u; |
|
}; |
|
|
|
/* A growable vector of compilation unit address ranges. */ |
|
|
|
struct unit_addrs_vector |
|
{ |
|
/* Memory. This is an array of struct unit_addrs. */ |
|
struct backtrace_vector vec; |
|
/* Number of address ranges present. */ |
|
size_t count; |
|
}; |
|
|
|
/* A growable vector of compilation unit pointer. */ |
|
|
|
struct unit_vector |
|
{ |
|
struct backtrace_vector vec; |
|
size_t count; |
|
}; |
|
|
|
/* The information we need to map a PC to a file and line. */ |
|
|
|
struct dwarf_data |
|
{ |
|
/* The data for the next file we know about. */ |
|
struct dwarf_data *next; |
|
/* The data for .gnu_debugaltlink. */ |
|
struct dwarf_data *altlink; |
|
/* The base address for this file. */ |
|
uintptr_t base_address; |
|
/* A sorted list of address ranges. */ |
|
struct unit_addrs *addrs; |
|
/* Number of address ranges in list. */ |
|
size_t addrs_count; |
|
/* A sorted list of units. */ |
|
struct unit **units; |
|
/* Number of units in the list. */ |
|
size_t units_count; |
|
/* The unparsed DWARF debug data. */ |
|
struct dwarf_sections dwarf_sections; |
|
/* Whether the data is big-endian or not. */ |
|
int is_bigendian; |
|
/* A vector used for function addresses. We keep this here so that |
|
we can grow the vector as we read more functions. */ |
|
struct function_vector fvec; |
|
}; |
|
|
|
/* Report an error for a DWARF buffer. */ |
|
|
|
static void |
|
dwarf_buf_error (struct dwarf_buf *buf, const char *msg) |
|
{ |
|
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, 0); |
|
} |
|
|
|
/* Require at least COUNT bytes in BUF. Return 1 if all is well, 0 on |
|
error. */ |
|
|
|
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"); |
|
buf->reported_underflow = 1; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
/* Advance COUNT bytes in BUF. Return 1 if all is well, 0 on |
|
error. */ |
|
|
|
static int |
|
advance (struct dwarf_buf *buf, size_t count) |
|
{ |
|
if (!require (buf, count)) |
|
return 0; |
|
buf->buf += count; |
|
buf->left -= count; |
|
return 1; |
|
} |
|
|
|
/* Read one zero-terminated string from BUF and advance past the string. */ |
|
|
|
static const char * |
|
read_string (struct dwarf_buf *buf) |
|
{ |
|
const char *p = (const char *)buf->buf; |
|
size_t len = strnlen (p, buf->left); |
|
|
|
/* - If len == left, we ran out of buffer before finding the zero terminator. |
|
Generate an error by advancing len + 1. |
|
- If len < left, advance by len + 1 to skip past the zero terminator. */ |
|
size_t count = len + 1; |
|
|
|
if (!advance (buf, count)) |
|
return NULL; |
|
|
|
return p; |
|
} |
|
|
|
/* Read one byte from BUF and advance 1 byte. */ |
|
|
|
static unsigned char |
|
read_byte (struct dwarf_buf *buf) |
|
{ |
|
const unsigned char *p = buf->buf; |
|
|
|
if (!advance (buf, 1)) |
|
return 0; |
|
return p[0]; |
|
} |
|
|
|
/* Read a signed char from BUF and advance 1 byte. */ |
|
|
|
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; |
|
} |
|
|
|
/* Read a uint16 from BUF and advance 2 bytes. */ |
|
|
|
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]; |
|
} |
|
|
|
/* Read a 24 bit value from BUF and advance 3 bytes. */ |
|
|
|
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]); |
|
} |
|
|
|
/* Read a uint32 from BUF and advance 4 bytes. */ |
|
|
|
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]); |
|
} |
|
|
|
/* Read a uint64 from BUF and advance 8 bytes. */ |
|
|
|
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]); |
|
} |
|
|
|
/* Read an offset from BUF and advance the appropriate number of |
|
bytes. */ |
|
|
|
static uint64_t |
|
read_offset (struct dwarf_buf *buf, int is_dwarf64) |
|
{ |
|
if (is_dwarf64) |
|
return read_uint64 (buf); |
|
else |
|
return read_uint32 (buf); |
|
} |
|
|
|
/* Read an address from BUF and advance the appropriate number of |
|
bytes. */ |
|
|
|
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"); |
|
return 0; |
|
} |
|
} |
|
|
|
/* Return whether a value is the highest possible address, given the |
|
address size. */ |
|
|
|
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; |
|
} |
|
} |
|
|
|
/* Read an unsigned LEB128 number. */ |
|
|
|
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"); |
|
overflow = 1; |
|
} |
|
shift += 7; |
|
} |
|
while ((b & 0x80) != 0); |
|
|
|
return ret; |
|
} |
|
|
|
/* Read a signed LEB128 number. */ |
|
|
|
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"); |
|
overflow = 1; |
|
} |
|
shift += 7; |
|
} |
|
while ((b & 0x80) != 0); |
|
|
|
if ((b & 0x40) != 0 && shift < 64) |
|
val |= ((uint64_t) -1) << shift; |
|
|
|
return (int64_t) val; |
|
} |
|
|
|
/* Return the length of an LEB128 number. */ |
|
|
|
static size_t |
|
leb128_len (const unsigned char *p) |
|
{ |
|
size_t ret; |
|
|
|
ret = 1; |
|
while ((*p & 0x80) != 0) |
|
{ |
|
++p; |
|
++ret; |
|
} |
|
return ret; |
|
} |
|
|
|
/* Read initial_length from BUF and advance the appropriate number of bytes. */ |
|
|
|
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; |
|
} |
|
|
|
/* Free an abbreviations structure. */ |
|
|
|
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; |
|
} |
|
|
|
/* Read an attribute value. Returns 1 on success, 0 on failure. If |
|
the value can be represented as a uint64_t, sets *VAL and sets |
|
*IS_VALID to 1. We don't try to store the value of other attribute |
|
forms, because we don't care about them. */ |
|
|
|
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) |
|
{ |
|
/* Avoid warnings about val.u.FIELD may be used uninitialized if |
|
this function is inlined. The warnings aren't valid but can |
|
occur because the different fields are set and used |
|
conditionally. */ |
|
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"); |
|
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"); |
|
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"); |
|
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: |
|
/* This case can't happen. */ |
|
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: |
|
/* This case can't happen. */ |
|
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: |
|
/* We don't distinguish this from DW_FORM_sec_offset. It |
|
* shouldn't matter since we don't care about loclists. */ |
|
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"); |
|
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"); |
|
return 0; |
|
} |
|
} |
|
|
|
/* If we can determine the value of a string attribute, set *STRING to |
|
point to the string. Return 1 on success, 0 on error. If we don't |
|
know the value, we consider that a success, and we don't change |
|
*STRING. An error is only reported for some sort of out of range |
|
offset. */ |
|
|
|
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"); |
|
return 0; |
|
} |
|
*string = (const char *) dwarf_sections->data[DEBUG_STR] + offset; |
|
return 1; |
|
} |
|
|
|
default: |
|
return 1; |
|
} |
|
} |
|
|
|
/* Set *ADDRESS to the real address for a ATTR_VAL_ADDRESS_INDEX. |
|
Return 1 on success, 0 on error. */ |
|
|
|
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; |
|
} |
|
|
|
/* Compare a unit offset against a unit for bsearch. */ |
|
|
|
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; |
|
} |
|
|
|
/* Find a unit in PU containing OFFSET. */ |
|
|
|
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; |
|
} |
|
|
|
/* Compare function_addrs for qsort. When ranges are nested, make the |
|
smallest one sort last. */ |
|
|
|
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); |
|
} |
|
|
|
/* Compare a PC against a function_addrs for bsearch. Note that if |
|
there are multiple ranges containing PC, which one will be returned |
|
is unpredictable. We compensate for that in dwarf_fileline. */ |
|
|
|
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->high) |
|
return 1; |
|
else |
|
return 0; |
|
} |
|
|
|
/* Add a new compilation unit address range to a vector. This is |
|
called via add_ranges. Returns 1 on success, 0 on failure. */ |
|
|
|
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; |
|
|
|
/* Try to merge with the last entry. */ |
|
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; |
|
} |
|
|
|
/* Compare unit_addrs for qsort. When ranges are nested, make the |
|
smallest one sort last. */ |
|
|
|
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; |
|
} |
|
|
|
/* Compare a PC against a unit_addrs for bsearch. Note that if there |
|
are multiple ranges containing PC, which one will be returned is |
|
unpredictable. We compensate for that in dwarf_fileline. */ |
|
|
|
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->high) |
|
return 1; |
|
else |
|
return 0; |
|
} |
|
|
|
/* Sort the line vector by PC. We want a stable sort here to maintain |
|
the order of lines for the same PC values. Since the sequence is |
|
being sorted in place, their addresses cannot be relied on to |
|
maintain stability. That is the purpose of the index member. */ |
|
|
|
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; |
|
} |
|
|
|
/* Find a PC in a line vector. We always allocate an extra entry at |
|
the end of the lines vector, so that this routine can safely look |
|
at the next entry. Note that when there are multiple mappings for |
|
the same PC value, this will return the last one. */ |
|
|
|
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; |
|
} |
|
|
|
/* Sort the abbrevs by the abbrev code. This function is passed to |
|
both qsort and bsearch. */ |
|
|
|
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 |
|
{ |
|
/* This really shouldn't happen. It means there are two |
|
different abbrevs with the same code, and that means we don't |
|
know which one lookup_abbrev should return. */ |
|
return 0; |
|
} |
|
} |
|
|
|
/* Read the abbreviation table for a compilation unit. Returns 1 on |
|
success, 0 on failure. */ |
|
|
|
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 the number of abbrevs in this list. */ |
|
|
|
count_buf = abbrev_buf; |
|
num_abbrevs = 0; |
|
while (read_uleb128 (&count_buf) != 0) |
|
{ |
|
if (count_buf.reported_underflow) |
|
return 0; |
|
++num_abbrevs; |
|
// Skip tag. |
|
read_uleb128 (&count_buf); |
|
// Skip has_children. |
|
read_byte (&count_buf); |
|
// Skip attributes. |
|
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); |
|
} |
|
// Skip form of last attribute. |
|
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; |
|
} |
|
|
|
/* Return the abbrev information for an abbrev code. */ |
|
|
|
static const struct abbrev * |
|
lookup_abbrev (struct abbrevs *abbrevs, uint64_t code, |
|
backtrace_error_callback error_callback, void *data) |
|
{ |
|
struct abbrev key; |
|
void *p; |
|
|
|
/* With GCC, where abbrevs are simply numbered in order, we should |
|
be able to just look up the entry. */ |
|
if (code - 1 < abbrevs->num_abbrevs |
|
&& abbrevs->abbrevs[code - 1].code == code) |
|
return &abbrevs->abbrevs[code - 1]; |
|
|
|
/* Otherwise we have to search. */ |
|
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; |
|
} |
|
|
|
/* This struct is used to gather address range information while |
|
reading attributes. We use this while building a mapping from |
|
address ranges to compilation units and then again while mapping |
|
from address ranges to function entries. Normally either |
|
lowpc/highpc is set or ranges is set. */ |
|
|
|
struct pcrange { |
|
uint64_t lowpc; /* The low PC value. */ |
|
int have_lowpc; /* Whether a low PC value was found. */ |
|
int lowpc_is_addr_index; /* Whether lowpc is in .debug_addr. */ |
|
uint64_t highpc; /* The high PC value. */ |
|
int have_highpc; /* Whether a high PC value was found. */ |
|
int highpc_is_relative; /* Whether highpc is relative to lowpc. */ |
|
int highpc_is_addr_index; /* Whether highpc is in .debug_addr. */ |
|
uint64_t ranges; /* Offset in ranges section. */ |
|
int have_ranges; /* Whether ranges is valid. */ |
|
int ranges_is_index; /* Whether ranges is DW_FORM_rnglistx. */ |
|
}; |
|
|
|
/* Update PCRANGE from an attribute value. */ |
|
|
|
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; |
|
} |
|
} |
|
|
|
/* Call ADD_RANGE for a low/high PC pair. Returns 1 on success, 0 on |
|
error. */ |
|
|
|
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; |
|
|
|
/* Add in the base address of the module when recording PC values, |
|
so that we can look up the PC directly. */ |
|
lowpc += base_address; |
|
highpc += base_address; |
|
|
|
return add_range (state, rdata, lowpc, highpc, error_callback, data, vec); |
|
} |
|
|
|
/* Call ADD_RANGE for each range read from .debug_ranges, as used in |
|
DWARF versions 2 through 4. */ |
|
|
|
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; |
|
} |
|
|
|
/* Call ADD_RANGE for each range read from .debug_rnglists, as used in |
|
DWARF version 5. */ |
|
|
|
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"); |
|
return 0; |
|
} |
|
} |
|
|
|
if (rnglists_buf.reported_underflow) |
|
return 0; |
|
|
|
return 1; |
|
} |
|
|
|
/* Call ADD_RANGE for each lowpc/highpc pair in PCRANGE. RDATA is |
|
passed to ADD_RANGE, and is either a struct unit * or a struct |
|
function *. VEC is the vector we are adding ranges to, and is |
|
either a struct unit_addrs_vector * or a struct function_vector *. |
|
Returns 1 on success, 0 on error. */ |
|
|
|
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) |
|
{ |
|
/* Did not find any address ranges to add. */ |
|
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); |
|
} |
|
|
|
/* Find the address range covered by a compilation unit, reading from |
|
UNIT_BUF and adding values to U. Returns 1 if all data could be |
|
read, 0 if there is some error. */ |
|
|
|
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 |
|
&& (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) |
|
{ |
|
name_val = val; |
|
have_name_val = 1; |
|
} |
|
break; |
|
|
|
case DW_AT_comp_dir: |
|
if (abbrev->tag == DW_TAG_compile_unit) |
|
{ |
|
comp_dir_val = val; |
|
have_comp_dir_val = 1; |
|
} |
|
break; |
|
|
|
case DW_AT_str_offsets_base: |
|
if (abbrev->tag == DW_TAG_compile_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 |
|
&& 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 |
|
&& val.encoding == ATTR_VAL_REF_SECTION) |
|
u->rnglists_base = val.u.uint; |
|
break; |
|
|
|
default: |
|
break; |
|
} |
|
} |
|
|
|
// Resolve strings after we're sure that we have seen |
|
// DW_AT_str_offsets_base. |
|
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) |
|
{ |
|
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 we found the PC range in the DW_TAG_compile_unit, we |
|
can stop now. */ |
|
if (abbrev->tag == DW_TAG_compile_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; |
|
} |
|
|
|
/* Build a mapping from address ranges to the compilation units where |
|
the line number information for that range can be found. Returns 1 |
|
on success, 0 on failure. */ |
|
|
|
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; |
|
|
|
memset (&addrs->vec, 0, sizeof addrs->vec); |
|
memset (&unit_vec->vec, 0, sizeof unit_vec->vec); |
|
addrs->count = 0; |
|
unit_vec->count = 0; |
|
|
|
/* Read through the .debug_info section. FIXME: Should we use the |
|
.debug_aranges section? gdb and addr2line don't use it, but I'm |
|
not sure why. */ |
|
|
|
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"); |
|
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) |
|
{ |
|
/* This unit doesn't have anything we need. */ |
|
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; /* Set below. */ |
|
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); /* dwo_id */ |
|
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; |
|
|
|
/* The actual line number mappings will be read as needed. */ |
|
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; |
|
|
|
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; |
|
} |
|
|
|
/* Add a new mapping to the vector of line mappings that we are |
|
building. Returns 1 on success, 0 on failure. */ |
|
|
|
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 we are adding the same mapping, ignore it. This can happen |
|
when using discriminators. */ |
|
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; |
|
|
|
/* Add in the base address here, so that we can look up the PC |
|
directly. */ |
|
ln->pc = pc + ddata->base_address; |
|
|
|
ln->filename = filename; |
|
ln->lineno = lineno; |
|
ln->idx = vec->count; |
|
|
|
++vec->count; |
|
|
|
return 1; |
|
} |
|
|
|
/* Free the line header information. */ |
|
|
|
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); |
|
} |
|
|
|
/* Read the directories and file names for a line header for version |
|
2, setting fields in HDR. Return 1 on success, 0 on failure. */ |
|
|
|
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; |
|
|
|
/* Count the number of directory entries. */ |
|
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 = NULL; |
|
if (hdr->dirs_count != 0) |
|
{ |
|
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; |
|
} |
|
|
|
i = 0; |
|
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; |
|
|
|
/* Count the number of file entries. */ |
|
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 = ((const char **) |
|
backtrace_alloc (state, |
|
hdr->filenames_count * sizeof (char *), |
|
hdr_buf->error_callback, |
|
hdr_buf->data)); |
|
if (hdr->filenames == NULL) |
|
return 0; |
|
i = 0; |
|
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 == 0 && u->comp_dir == NULL)) |
|
hdr->filenames[i] = filename; |
|
else |
|
{ |
|
const char *dir; |
|
size_t dir_len; |
|
size_t filename_len; |
|
char *s; |
|
|
|
if (dir_index == 0) |
|
dir = u->comp_dir; |
|
else if (dir_index - 1 < hdr->dirs_count) |
|
dir = hdr->dirs[dir_index - 1]; |
|
else |
|
{ |
|
dwarf_buf_error (hdr_buf, |
|
("invalid directory index in " |
|
"line number program header")); |
|
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); |
|
/* FIXME: If we are on a DOS-based file system, and the |
|
directory or the file name use backslashes, then we |
|
should use a backslash here. */ |
|
s[dir_len] = '/'; |
|
memcpy (s + dir_len + 1, filename, filename_len + 1); |
|
hdr->filenames[i] = s; |
|
} |
|
|
|
/* Ignore the modification time and size. */ |
|
read_uleb128 (hdr_buf); |
|
read_uleb128 (hdr_buf); |
|
|
|
++i; |
|
} |
|
|
|
return 1; |
|
} |
|
|
|
/* Read a single version 5 LNCT entry for a directory or file name in a |
|
line header. Sets *STRING to the resulting name, ignoring other |
|
data. Return 1 on success, 0 on failure. */ |
|
|
|
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")); |
|
return 0; |
|
} |
|
dir = hdr->dirs[val.u.uint]; |
|
} |
|
break; |
|
default: |
|
/* We don't care about timestamps or sizes or hashes. */ |
|
break; |
|
} |
|
} |
|
|
|
if (path == NULL) |
|
{ |
|
dwarf_buf_error (hdr_buf, |
|
"missing file name in line number program header"); |
|
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); |
|
/* FIXME: If we are on a DOS-based file system, and the |
|
directory or the path name use backslashes, then we should |
|
use a backslash here. */ |
|
s[dir_len] = '/'; |
|
memcpy (s + dir_len + 1, path, path_len + 1); |
|
*string = s; |
|
} |
|
|
|
return 1; |
|
} |
|
|
|
/* Read a set of DWARF 5 line header format entries, setting *PCOUNT |
|
and *PPATHS. Return 1 on success, 0 on failure. */ |
|
|
|
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); |
|
} |
|