diff --git a/Sources/CBacktrace/atomic.c b/Sources/CBacktrace/atomic.c index 68a9f74..faaa9e1 100644 --- a/Sources/CBacktrace/atomic.c +++ b/Sources/CBacktrace/atomic.c @@ -1,6 +1,6 @@ #ifdef __linux__ /* atomic.c -- Support for atomic functions if not present. - Copyright (C) 2013-2018 Free Software Foundation, Inc. + Copyright (C) 2013-2021 Free Software Foundation, Inc. Written by Ian Lance Taylor, Google. Redistribution and use in source and binary forms, with or without diff --git a/Sources/CBacktrace/backtrace-supported.h b/Sources/CBacktrace/backtrace-supported.h index c4bd628..33644e3 100644 --- a/Sources/CBacktrace/backtrace-supported.h +++ b/Sources/CBacktrace/backtrace-supported.h @@ -1,6 +1,6 @@ #ifdef __linux__ /* backtrace-supported.h.in -- Whether stack backtrace is supported. - Copyright (C) 2012-2016 Free Software Foundation, Inc. + Copyright (C) 2012-2021 Free Software Foundation, Inc. Written by Ian Lance Taylor, Google. Redistribution and use in source and binary forms, with or without diff --git a/Sources/CBacktrace/backtrace.c b/Sources/CBacktrace/backtrace.c index 8e92f03..a991f4b 100644 --- a/Sources/CBacktrace/backtrace.c +++ b/Sources/CBacktrace/backtrace.c @@ -1,6 +1,6 @@ #ifdef __linux__ /* backtrace.c -- Entry point for stack backtrace library. - Copyright (C) 2012-2018 Free Software Foundation, Inc. + Copyright (C) 2012-2021 Free Software Foundation, Inc. Written by Ian Lance Taylor, Google. Redistribution and use in source and binary forms, with or without @@ -99,7 +99,7 @@ unwind (struct _Unwind_Context *context, void *vdata) /* Get a stack backtrace. */ -int +int __attribute__((noinline)) backtrace_full (struct backtrace_state *state, int skip, backtrace_full_callback callback, backtrace_error_callback error_callback, void *data) diff --git a/Sources/CBacktrace/config.h b/Sources/CBacktrace/config.h index c9d6306..0dec086 100644 --- a/Sources/CBacktrace/config.h +++ b/Sources/CBacktrace/config.h @@ -14,6 +14,10 @@ /* Define to 1 if you have the `clock_gettime' function. */ #define HAVE_CLOCK_GETTIME 1 +/* Define to 1 if you have the declaration of `getpagesize', and to 0 if you + don't. */ +#define HAVE_DECL_GETPAGESIZE 1 + /* Define to 1 if you have the declaration of `strnlen', and to 0 if you don't. */ #define HAVE_DECL_STRNLEN 1 @@ -36,8 +40,16 @@ /* Define to 1 if you have the header file. */ #define HAVE_INTTYPES_H 1 -/* Define to 1 if you have the `z' library (-lz). */ -/* #undef HAVE_LIBZ */ +/* Define to 1 if you have KERN_PROC and KERN_PROC_PATHNAME in . + */ +/* #undef HAVE_KERN_PROC */ + +/* Define to 1 if you have KERN_PROCARGS and KERN_PROC_PATHNAME in + . */ +/* #undef HAVE_KERN_PROC_ARGS */ + +/* Define if -llzma is available. */ +/* #undef HAVE_LIBLZMA */ /* Define to 1 if you have the header file. */ #define HAVE_LINK_H 1 @@ -48,6 +60,9 @@ /* Define to 1 if you have the `lstat' function. */ #define HAVE_LSTAT 1 +/* Define to 1 if you have the header file. */ +/* #undef HAVE_MACH_O_DYLD_H */ + /* Define to 1 if you have the header file. */ #define HAVE_MEMORY_H 1 @@ -85,7 +100,7 @@ #define HAVE_UNISTD_H 1 /* Define if -lz is available. */ -/* #undef HAVE_ZLIB */ +#define HAVE_ZLIB 1 /* Define to the sub-directory in which libtool stores uninstalled libraries. */ @@ -134,6 +149,11 @@ #endif +/* Enable large inode numbers on Mac OS X 10.5. */ +#ifndef _DARWIN_USE_64_BIT_INODE +# define _DARWIN_USE_64_BIT_INODE 1 +#endif + /* Number of bits in a file offset, on hosts where this is settable. */ /* #undef _FILE_OFFSET_BITS */ diff --git a/Sources/CBacktrace/dwarf.c b/Sources/CBacktrace/dwarf.c index 3493b85..2eae3ee 100644 --- a/Sources/CBacktrace/dwarf.c +++ b/Sources/CBacktrace/dwarf.c @@ -1,6 +1,6 @@ #ifdef __linux__ /* dwarf.c -- Get file/line information from DWARF for backtraces. - Copyright (C) 2012-2018 Free Software Foundation, Inc. + Copyright (C) 2012-2021 Free Software Foundation, Inc. Written by Ian Lance Taylor, Google. Redistribution and use in source and binary forms, with or without @@ -38,6 +38,8 @@ POSSIBILITY OF SUCH DAMAGE. */ #include #include +#include "filenames.h" + #include "include/backtrace.h" #include "internal.h" @@ -48,14 +50,15 @@ enum dwarf_tag { 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 = 0x1, - DW_FORM_block2 = 0x3, - DW_FORM_block4 = 0x4, - DW_FORM_data2 = 0x5, - DW_FORM_data4 = 0x6, + 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, @@ -76,25 +79,248 @@ enum dwarf_form { 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, + DW_FORM_GNU_strp_alt = 0x1f21 }; enum dwarf_attribute { - DW_AT_name = 0x3, + 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 { @@ -113,21 +339,44 @@ enum dwarf_line_number_op { DW_LNS_set_isa = 0xc, }; -enum dwarf_extedned_line_number_op { +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, }; -#if defined(__MSDOS__) || defined(_WIN32) || defined(__OS2__) || defined (__CYGWIN__) -# define IS_DIR_SEPARATOR(c) ((c) == '/' || (c) == '\\') -#define HAS_DRIVE_SPEC(f) ((f)[0] && (f)[1] == ':') -# define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR(f[0]) || HAS_DRIVE_SPEC(f)) -#else -# define IS_DIR_SEPARATOR(c) ((c) == '/') -# define IS_ABSOLUTE_PATH(f) IS_DIR_SEPARATOR(f[0]) -#endif +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 @@ -178,6 +427,8 @@ struct attr 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. */ @@ -215,22 +466,33 @@ struct abbrevs 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 .dwarf_info section. */ + /* 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). */ @@ -245,7 +507,7 @@ struct attr_val enum attr_val_encoding encoding; union { - /* ATTR_VAL_ADDRESS, ATTR_VAL_UINT, ATTR_VAL_REF*. */ + /* ATTR_VAL_ADDRESS*, ATTR_VAL_UINT, ATTR_VAL_REF*. */ uint64_t uint; /* ATTR_VAL_SINT. */ int64_t sint; @@ -261,6 +523,8 @@ 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. */ @@ -283,6 +547,14 @@ struct line_header 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 @@ -365,6 +637,12 @@ struct unit /* 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. */ @@ -373,6 +651,12 @@ struct unit 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. */ @@ -423,30 +707,34 @@ struct unit_addrs_vector 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; - /* The unparsed .debug_info section. */ - const unsigned char *dwarf_info; - size_t dwarf_info_size; - /* The unparsed .debug_line section. */ - const unsigned char *dwarf_line; - size_t dwarf_line_size; - /* The unparsed .debug_ranges section. */ - const unsigned char *dwarf_ranges; - size_t dwarf_ranges_size; - /* The unparsed .debug_str section. */ - const unsigned char *dwarf_str; - size_t dwarf_str_size; + /* 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 @@ -457,13 +745,13 @@ struct dwarf_data /* Report an error for a DWARF buffer. */ static void -dwarf_buf_error (struct dwarf_buf *buf, const char *msg) +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, 0); + buf->error_callback (buf->data, b, errnum); } /* Require at least COUNT bytes in BUF. Return 1 if all is well, 0 on @@ -477,7 +765,7 @@ require (struct dwarf_buf *buf, size_t count) if (!buf->reported_underflow) { - dwarf_buf_error (buf, "DWARF underflow"); + dwarf_buf_error (buf, "DWARF underflow", 0); buf->reported_underflow = 1; } @@ -497,6 +785,25 @@ advance (struct dwarf_buf *buf, size_t 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 @@ -536,6 +843,23 @@ read_uint16 (struct dwarf_buf *buf) 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 @@ -603,7 +927,7 @@ read_address (struct dwarf_buf *buf, int addrsize) case 8: return read_uint64 (buf); default: - dwarf_buf_error (buf, "unrecognized address size"); + dwarf_buf_error (buf, "unrecognized address size", 0); return 0; } } @@ -654,7 +978,7 @@ read_uleb128 (struct dwarf_buf *buf) ret |= ((uint64_t) (b & 0x7f)) << shift; else if (!overflow) { - dwarf_buf_error (buf, "LEB128 overflows uint64_t"); + dwarf_buf_error (buf, "LEB128 overflows uint64_t", 0); overflow = 1; } shift += 7; @@ -689,7 +1013,7 @@ read_sleb128 (struct dwarf_buf *buf) val |= ((uint64_t) (b & 0x7f)) << shift; else if (!overflow) { - dwarf_buf_error (buf, "signed LEB128 overflows uint64_t"); + dwarf_buf_error (buf, "signed LEB128 overflows uint64_t", 0); overflow = 1; } shift += 7; @@ -718,6 +1042,25 @@ leb128_len (const unsigned char *p) 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 @@ -743,10 +1086,10 @@ free_abbrevs (struct backtrace_state *state, struct abbrevs *abbrevs, forms, because we don't care about them. */ static int -read_attribute (enum dwarf_form form, struct dwarf_buf *buf, - int is_dwarf64, int version, int addrsize, - const unsigned char *dwarf_str, size_t dwarf_str_size, - struct attr_val *val) +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 @@ -778,10 +1121,13 @@ read_attribute (enum dwarf_form form, struct dwarf_buf *buf, 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 = (const char *) buf->buf; - return advance (buf, strnlen ((const char *) buf->buf, buf->left) + 1); + 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)); @@ -805,13 +1151,29 @@ read_attribute (enum dwarf_form form, struct dwarf_buf *buf, uint64_t offset; offset = read_offset (buf, is_dwarf64); - if (offset >= dwarf_str_size) + 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_strp out of range"); + 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_str + offset; + val->u.string = + (const char *) dwarf_sections->data[DEBUG_LINE_STR] + offset; return 1; } case DW_FORM_udata: @@ -850,8 +1212,15 @@ read_attribute (enum dwarf_form form, struct dwarf_buf *buf, uint64_t form; form = read_uleb128 (buf); - return read_attribute ((enum dwarf_form) form, buf, is_dwarf64, - version, addrsize, dwarf_str, dwarf_str_size, + 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: @@ -869,6 +1238,88 @@ read_attribute (enum dwarf_form form, struct dwarf_buf *buf, 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); @@ -878,17 +1329,157 @@ read_attribute (enum dwarf_form form, struct dwarf_buf *buf, val->u.uint = read_uleb128 (buf); return 1; case DW_FORM_GNU_ref_alt: - val->encoding = ATTR_VAL_REF_SECTION; 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_GNU_strp_alt: - val->encoding = ATTR_VAL_REF_SECTION; - val->u.uint = read_offset (buf, is_dwarf64); + 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; + } +} + +/* 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", + 0); + return 0; + } + *string = (const char *) dwarf_sections->data[DEBUG_STR] + offset; + return 1; + } + default: - dwarf_buf_error (buf, "unrecognized DWARF form"); + 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 @@ -911,9 +1502,11 @@ function_addrs_compare (const void *v1, const void *v2) 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. */ +/* Compare a PC against a function_addrs for bsearch. We always + allocate an entra entry at the end of the vector, so that this + routine can safely look at the next entry. 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) @@ -925,37 +1518,34 @@ function_addrs_search (const void *vkey, const void *ventry) pc = *key; if (pc < entry->low) return -1; - else if (pc >= entry->high) + else if (pc > (entry + 1)->low) return 1; else return 0; } -/* Add a new compilation unit address range to a vector. Returns 1 on - success, 0 on failure. */ +/* 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, uintptr_t base_address, - struct unit_addrs addrs, +add_unit_addr (struct backtrace_state *state, void *rdata, + uint64_t lowpc, uint64_t highpc, backtrace_error_callback error_callback, void *data, - struct unit_addrs_vector *vec) + void *pvec) { + struct unit *u = (struct unit *) rdata; + struct unit_addrs_vector *vec = (struct unit_addrs_vector *) pvec; struct unit_addrs *p; - /* Add in the base address of the module here, so that we can look - up the PC directly. */ - addrs.low += base_address; - addrs.high += base_address; - /* Try to merge with the last entry. */ if (vec->count > 0) { p = (struct unit_addrs *) vec->vec.base + (vec->count - 1); - if ((addrs.low == p->high || addrs.low == p->high + 1) - && addrs.u == p->u) + if ((lowpc == p->high || lowpc == p->high + 1) + && u == p->u) { - if (addrs.high > p->high) - p->high = addrs.high; + if (highpc > p->high) + p->high = highpc; return 1; } } @@ -966,24 +1556,13 @@ add_unit_addr (struct backtrace_state *state, uintptr_t base_address, if (p == NULL) return 0; - *p = addrs; - ++vec->count; - return 1; -} - -/* Free a unit address vector. */ + p->low = lowpc; + p->high = highpc; + p->u = u; -static void -free_unit_addrs_vector (struct backtrace_state *state, - struct unit_addrs_vector *vec, - backtrace_error_callback error_callback, void *data) -{ - struct unit_addrs *addrs; - size_t i; + ++vec->count; - addrs = (struct unit_addrs *) vec->vec.base; - for (i = 0; i < vec->count; ++i) - free_abbrevs (state, &addrs[i].u->abbrevs, error_callback, data); + return 1; } /* Compare unit_addrs for qsort. When ranges are nested, make the @@ -1010,9 +1589,11 @@ unit_addrs_compare (const void *v1, const void *v2) 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. */ +/* Compare a PC against a unit_addrs for bsearch. We always allocate + an entry entry at the end of the vector, so that this routine can + safely look at the next entry. 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) @@ -1024,7 +1605,7 @@ unit_addrs_search (const void *vkey, const void *ventry) pc = *key; if (pc < entry->low) return -1; - else if (pc >= entry->high) + else if (pc > (entry + 1)->low) return 1; else return 0; @@ -1142,7 +1723,13 @@ read_abbrevs (struct backtrace_state *state, uint64_t abbrev_offset, read_byte (&count_buf); // Skip attributes. while (read_uleb128 (&count_buf) != 0) - read_uleb128 (&count_buf); + { + 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); } @@ -1153,13 +1740,13 @@ read_abbrevs (struct backtrace_state *state, uint64_t abbrev_offset, if (num_abbrevs == 0) return 1; - abbrevs->num_abbrevs = num_abbrevs; 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; @@ -1185,8 +1772,12 @@ read_abbrevs (struct backtrace_state *state, uint64_t abbrev_offset, num_attrs = 0; while (read_uleb128 (&count_buf) != 0) { + uint64_t form; + ++num_attrs; - read_uleb128 (&count_buf); + form = read_uleb128 (&count_buf); + if ((enum dwarf_form) form == DW_FORM_implicit_const) + read_sleb128 (&count_buf); } if (num_attrs == 0) @@ -1214,6 +1805,10 @@ read_abbrevs (struct backtrace_state *state, uint64_t abbrev_offset, 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; } } @@ -1263,29 +1858,165 @@ lookup_abbrev (struct abbrevs *abbrevs, uint64_t code, return (const struct abbrev *) p; } -/* Add non-contiguous address ranges for a compilation unit. Returns - 1 on success, 0 on failure. */ +/* 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_unit_ranges (struct backtrace_state *state, uintptr_t base_address, - struct unit *u, uint64_t ranges, uint64_t base, - int is_bigendian, const unsigned char *dwarf_ranges, - size_t dwarf_ranges_size, - backtrace_error_callback error_callback, void *data, - struct unit_addrs_vector *addrs) +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 (ranges >= dwarf_ranges_size) + 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_ranges; - ranges_buf.buf = dwarf_ranges + ranges; - ranges_buf.left = dwarf_ranges_size - 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; @@ -1309,13 +2040,10 @@ add_unit_ranges (struct backtrace_state *state, uintptr_t base_address, base = high; else { - struct unit_addrs a; - - a.low = low + base; - a.high = high + base; - a.u = u; - if (!add_unit_addr (state, base_address, a, error_callback, data, - addrs)) + if (!add_range (state, rdata, + low + base + base_address, + high + base + base_address, + error_callback, data, vec)) return 0; } } @@ -1326,31 +2054,242 @@ add_unit_ranges (struct backtrace_state *state, uintptr_t base_address, return 1; } -/* 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. */ +/* Call ADD_RANGE for each range read from .debug_rnglists, as used in + DWARF version 5. */ static int -find_address_ranges (struct backtrace_state *state, uintptr_t base_address, - struct dwarf_buf *unit_buf, - const unsigned char *dwarf_str, size_t dwarf_str_size, - const unsigned char *dwarf_ranges, - size_t dwarf_ranges_size, - int is_bigendian, backtrace_error_callback error_callback, - void *data, struct unit *u, - struct unit_addrs_vector *addrs) +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) { - while (unit_buf->left > 0) + 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]) { - uint64_t code; + 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; +} + +/* 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; - uint64_t lowpc; - int have_lowpc; - uint64_t highpc; - int have_highpc; - int highpc_is_relative; - uint64_t ranges; - int have_ranges; + 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); @@ -1361,57 +2300,32 @@ find_address_ranges (struct backtrace_state *state, uintptr_t base_address, if (abbrev == NULL) return 0; - lowpc = 0; - have_lowpc = 0; - highpc = 0; - have_highpc = 0; - highpc_is_relative = 0; - ranges = 0; - have_ranges = 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, unit_buf, - u->is_dwarf64, u->version, u->addrsize, - dwarf_str, dwarf_str_size, &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: - if (val.encoding == ATTR_VAL_ADDRESS) - { - lowpc = val.u.uint; - have_lowpc = 1; - } - break; - - case DW_AT_high_pc: - if (val.encoding == ATTR_VAL_ADDRESS) - { - highpc = val.u.uint; - have_highpc = 1; - } - else if (val.encoding == ATTR_VAL_UINT) - { - highpc = val.u.uint; - have_highpc = 1; - highpc_is_relative = 1; - } - break; - - case DW_AT_ranges: - if (val.encoding == ATTR_VAL_UINT - || val.encoding == ATTR_VAL_REF_SECTION) - { - ranges = val.u.uint; - have_ranges = 1; - } + 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 + 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; @@ -1419,14 +2333,41 @@ find_address_ranges (struct backtrace_state *state, uintptr_t base_address, case DW_AT_name: if (abbrev->tag == DW_TAG_compile_unit - && val.encoding == ATTR_VAL_STRING) - u->filename = val.u.string; + || 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 - && val.encoding == ATTR_VAL_STRING) - u->comp_dir = val.u.string; + || 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: @@ -1434,46 +2375,47 @@ find_address_ranges (struct backtrace_state *state, uintptr_t base_address, } } - if (abbrev->tag == DW_TAG_compile_unit - || abbrev->tag == DW_TAG_subprogram) + // Resolve strings after we're sure that we have seen + // DW_AT_str_offsets_base. + if (have_name_val) { - if (have_ranges) - { - if (!add_unit_ranges (state, base_address, u, ranges, lowpc, - is_bigendian, dwarf_ranges, - dwarf_ranges_size, error_callback, - data, addrs)) - return 0; - } - else if (have_lowpc && have_highpc) - { - struct unit_addrs a; - - if (highpc_is_relative) - highpc += lowpc; - a.low = lowpc; - a.high = highpc; - a.u = u; + 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 (!add_unit_addr (state, base_address, a, error_callback, data, - addrs)) - 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 we found the PC range in the DW_TAG_compile_unit, we - can stop now. */ - if (abbrev->tag == DW_TAG_compile_unit - && (have_ranges || (have_lowpc && have_highpc))) + /* If we found the PC range in the DW_TAG_compile_unit or + DW_TAG_skeleton_unit, we can stop now. */ + 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_str, dwarf_str_size, - dwarf_ranges, dwarf_ranges_size, - is_bigendian, error_callback, data, - u, addrs)) + dwarf_sections, is_bigendian, altlink, + error_callback, data, u, addrs, NULL)) return 0; } } @@ -1487,33 +2429,41 @@ find_address_ranges (struct backtrace_state *state, uintptr_t base_address, static int build_address_map (struct backtrace_state *state, uintptr_t base_address, - const unsigned char *dwarf_info, size_t dwarf_info_size, - const unsigned char *dwarf_abbrev, size_t dwarf_abbrev_size, - const unsigned char *dwarf_ranges, size_t dwarf_ranges_size, - const unsigned char *dwarf_str, size_t dwarf_str_size, - int is_bigendian, backtrace_error_callback error_callback, - void *data, struct unit_addrs_vector *addrs) + 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 abbrevs abbrevs; + 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; /* 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_info; - info.buf = dwarf_info; - info.left = dwarf_info_size; + 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 (&abbrevs, 0, sizeof abbrevs); + memset (&units, 0, sizeof units); + units_count = 0; + while (info.left > 0) { const unsigned char *unit_data_start; @@ -1521,23 +2471,18 @@ build_address_map (struct backtrace_state *state, uintptr_t base_address, 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; - is_dwarf64 = 0; - len = read_uint32 (&info); - if (len == 0xffffffff) - { - len = read_uint64 (&info); - is_dwarf64 = 1; - } - + len = read_initial_length (&info, &is_dwarf64); unit_buf = info; unit_buf.left = len; @@ -1545,23 +2490,70 @@ build_address_map (struct backtrace_state *state, uintptr_t base_address, goto fail; version = read_uint16 (&unit_buf); - if (version < 2 || version > 4) + if (version < 2 || version > 5) { - dwarf_buf_error (&unit_buf, "unrecognized DWARF version"); + dwarf_buf_error (&unit_buf, "unrecognized DWARF version", -1); goto fail; } - abbrev_offset = read_offset (&unit_buf, is_dwarf64); - if (!read_abbrevs (state, abbrev_offset, dwarf_abbrev, dwarf_abbrev_size, - is_bigendian, error_callback, data, &abbrevs)) - 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; + } + } - addrsize = read_byte (&unit_buf); + 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; @@ -1572,8 +2564,9 @@ build_address_map (struct backtrace_state *state, uintptr_t base_address, u->comp_dir = NULL; u->abs_filename = NULL; u->lineoff = 0; - u->abbrevs = abbrevs; - memset (&abbrevs, 0, sizeof abbrevs); + u->str_offsets_base = 0; + u->addr_base = 0; + u->rnglists_base = 0; /* The actual line number mappings will be read as needed. */ u->lines = NULL; @@ -1581,32 +2574,48 @@ build_address_map (struct backtrace_state *state, uintptr_t base_address, u->function_addrs = NULL; u->function_addrs_count = 0; - if (!find_address_ranges (state, base_address, &unit_buf, - dwarf_str, dwarf_str_size, - dwarf_ranges, dwarf_ranges_size, - is_bigendian, error_callback, data, - u, addrs)) - { - free_abbrevs (state, &u->abbrevs, error_callback, data); - backtrace_free (state, u, sizeof *u, error_callback, data); - goto fail; - } + 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) - { - free_abbrevs (state, &u->abbrevs, error_callback, data); - backtrace_free (state, u, sizeof *u, error_callback, data); - goto fail; - } + goto fail; } if (info.reported_underflow) goto fail; + /* Add a trailing addrs entry, but don't include it in addrs->count. */ + 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: - free_abbrevs (state, &abbrevs, error_callback, data); - free_unit_addrs_vector (state, addrs, error_callback, data); + 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; } @@ -1663,91 +2672,58 @@ free_line_header (struct backtrace_state *state, struct line_header *hdr, error_callback, data); } -/* Read the line header. Return 1 on success, 0 on failure. */ +/* 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_line_header (struct backtrace_state *state, struct unit *u, - int is_dwarf64, struct dwarf_buf *line_buf, - struct line_header *hdr) +read_v2_paths (struct backtrace_state *state, struct unit *u, + struct dwarf_buf *hdr_buf, struct line_header *hdr) { - uint64_t hdrlen; - struct dwarf_buf hdr_buf; const unsigned char *p; const unsigned char *pend; size_t i; - hdr->version = read_uint16 (line_buf); - if (hdr->version < 2 || hdr->version > 4) - { - dwarf_buf_error (line_buf, "unsupported line number version"); - 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); - - /* We don't care about default_is_stmt. */ - 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; - /* Count the number of directory entries. */ hdr->dirs_count = 0; - p = hdr_buf.buf; - pend = p + hdr_buf.left; + 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 *), - line_buf->error_callback, line_buf->data)); - if (hdr->dirs == NULL) - return 0; - } + /* The index of the first entry in the list of directories is 1. Index 0 is + used for the current directory of the compilation. To simplify index + handling, we set entry 0 to the compilation unit directory. */ + ++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; - i = 0; - while (*hdr_buf.buf != '\0') + hdr->dirs[0] = u->comp_dir; + i = 1; + while (*hdr_buf->buf != '\0') { - if (hdr_buf.reported_underflow) + if (hdr_buf->reported_underflow) return 0; - hdr->dirs[i] = (const char *) hdr_buf.buf; - ++i; - if (!advance (&hdr_buf, - strnlen ((const char *) hdr_buf.buf, hdr_buf.left) + 1)) + hdr->dirs[i] = read_string (hdr_buf); + if (hdr->dirs[i] == NULL) return 0; + ++i; } - if (!advance (&hdr_buf, 1)) + 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; + p = hdr_buf->buf; + pend = p + hdr_buf->left; while (p < pend && *p != '\0') { p += strnlen ((const char *) p, pend - p) + 1; @@ -1757,29 +2733,33 @@ read_line_header (struct backtrace_state *state, struct unit *u, ++hdr->filenames_count; } + /* The index of the first entry in the list of file names is 1. Index 0 is + used for the DW_AT_name of the compilation unit. To simplify index + handling, we set entry 0 to the compilation unit file name. */ + ++hdr->filenames_count; hdr->filenames = ((const char **) backtrace_alloc (state, hdr->filenames_count * sizeof (char *), - line_buf->error_callback, - line_buf->data)); + hdr_buf->error_callback, + hdr_buf->data)); if (hdr->filenames == NULL) return 0; - i = 0; - while (*hdr_buf.buf != '\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) + if (hdr_buf->reported_underflow) return 0; - filename = (const char *) hdr_buf.buf; - if (!advance (&hdr_buf, - strnlen ((const char *) hdr_buf.buf, hdr_buf.left) + 1)) + filename = read_string (hdr_buf); + if (filename == NULL) return 0; - dir_index = read_uleb128 (&hdr_buf); + dir_index = read_uleb128 (hdr_buf); if (IS_ABSOLUTE_PATH (filename) - || (dir_index == 0 && u->comp_dir == NULL)) + || (dir_index < hdr->dirs_count && hdr->dirs[dir_index] == NULL)) hdr->filenames[i] = filename; else { @@ -1788,22 +2768,21 @@ read_line_header (struct backtrace_state *state, struct unit *u, 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]; + if (dir_index < hdr->dirs_count) + dir = hdr->dirs[dir_index]; else { - dwarf_buf_error (line_buf, + dwarf_buf_error (hdr_buf, ("invalid directory index in " - "line number program header")); + "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, - line_buf->error_callback, line_buf->data)); + 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); @@ -1816,12 +2795,261 @@ read_line_header (struct backtrace_state *state, struct unit *u, } /* Ignore the modification time and size. */ - read_uleb128 (&hdr_buf); - read_uleb128 (&hdr_buf); + 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"), + 0); + 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", + 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); + /* 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); + } + } + + 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; +} + +/* Read the line header. Return 1 on success, 0 on failure. */ + +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); + /* We could support a non-zero segment_selector_size but I doubt + we'll ever see it. */ + 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); + + /* We don't care about default_is_stmt. */ + 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; @@ -1833,8 +3061,8 @@ read_line_header (struct backtrace_state *state, struct unit *u, static int read_line_program (struct backtrace_state *state, struct dwarf_data *ddata, - struct unit *u, const struct line_header *hdr, - struct dwarf_buf *line_buf, struct line_vector *vec) + const struct line_header *hdr, struct dwarf_buf *line_buf, + struct line_vector *vec) { uint64_t address; unsigned int op_index; @@ -1844,8 +3072,8 @@ read_line_program (struct backtrace_state *state, struct dwarf_data *ddata, address = 0; op_index = 0; - if (hdr->filenames_count > 0) - reset_filename = hdr->filenames[0]; + if (hdr->filenames_count > 1) + reset_filename = hdr->filenames[1]; else reset_filename = ""; filename = reset_filename; @@ -1887,15 +3115,15 @@ read_line_program (struct backtrace_state *state, struct dwarf_data *ddata, lineno = 1; break; case DW_LNE_set_address: - address = read_address (line_buf, u->addrsize); + address = read_address (line_buf, hdr->addrsize); break; case DW_LNE_define_file: { const char *f; unsigned int dir_index; - f = (const char *) line_buf->buf; - if (!advance (line_buf, strnlen (f, line_buf->left) + 1)) + f = read_string (line_buf); + if (f == NULL) return 0; dir_index = read_uleb128 (line_buf); /* Ignore that time and length. */ @@ -1910,15 +3138,14 @@ read_line_program (struct backtrace_state *state, struct dwarf_data *ddata, size_t f_len; char *p; - if (dir_index == 0) - dir = u->comp_dir; - else if (dir_index - 1 < hdr->dirs_count) - dir = hdr->dirs[dir_index - 1]; + if (dir_index < hdr->dirs_count) + dir = hdr->dirs[dir_index]; else { dwarf_buf_error (line_buf, ("invalid directory index " - "in line number program")); + "in line number program"), + 0); return 0; } dir_len = strlen (dir); @@ -1976,19 +3203,15 @@ read_line_program (struct backtrace_state *state, struct dwarf_data *ddata, uint64_t fileno; fileno = read_uleb128 (line_buf); - if (fileno == 0) - filename = ""; - else + if (fileno >= hdr->filenames_count) { - if (fileno - 1 >= hdr->filenames_count) - { - dwarf_buf_error (line_buf, - ("invalid file number in " - "line number program")); - return 0; - } - filename = hdr->filenames[fileno - 1]; + 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: @@ -2056,34 +3279,28 @@ read_line_info (struct backtrace_state *state, struct dwarf_data *ddata, memset (hdr, 0, sizeof *hdr); if (u->lineoff != (off_t) (size_t) u->lineoff - || (size_t) u->lineoff >= ddata->dwarf_line_size) + || (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_line; - line_buf.buf = ddata->dwarf_line + u->lineoff; - line_buf.left = ddata->dwarf_line_size - u->lineoff; + 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; - is_dwarf64 = 0; - len = read_uint32 (&line_buf); - if (len == 0xffffffff) - { - len = read_uint64 (&line_buf); - is_dwarf64 = 1; - } + len = read_initial_length (&line_buf, &is_dwarf64); line_buf.left = len; - if (!read_line_header (state, u, is_dwarf64, &line_buf, hdr)) + if (!read_line_header (state, ddata, u, is_dwarf64, &line_buf, hdr)) goto fail; - if (!read_line_program (state, ddata, u, hdr, &line_buf, &vec)) + if (!read_line_program (state, ddata, hdr, &line_buf, &vec)) goto fail; if (line_buf.reported_underflow) @@ -2120,15 +3337,69 @@ read_line_info (struct backtrace_state *state, struct dwarf_data *ddata, return 1; fail: - vec.vec.alc += vec.vec.size; - vec.vec.size = 0; - backtrace_vector_release (state, &vec.vec, error_callback, data); + 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 *); + +/* Read the name of a function from a DIE referenced by ATTR with VAL. */ + +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; +} + /* Read the name of a function from a DIE referenced by a DW_AT_abstract_origin or DW_AT_specification tag. OFFSET is within the same compilation unit. */ @@ -2160,7 +3431,7 @@ read_referenced_name (struct dwarf_data *ddata, struct unit *u, offset -= u->unit_data_offset; unit_buf.name = ".debug_info"; - unit_buf.start = ddata->dwarf_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; @@ -2171,7 +3442,9 @@ read_referenced_name (struct dwarf_data *ddata, struct unit *u, code = read_uleb128 (&unit_buf); if (code == 0) { - dwarf_buf_error (&unit_buf, "invalid abstract origin or specification"); + dwarf_buf_error (&unit_buf, + "invalid abstract origin or specification", + 0); return NULL; } @@ -2184,45 +3457,52 @@ read_referenced_name (struct dwarf_data *ddata, struct unit *u, { struct attr_val val; - if (!read_attribute (abbrev->attrs[i].form, &unit_buf, - u->is_dwarf64, u->version, u->addrsize, - ddata->dwarf_str, ddata->dwarf_str_size, - &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: - /* We prefer the linkage name if get one. */ - if (val.encoding == ATTR_VAL_STRING) - ret = val.u.string; + /* Third name preference: don't override. A name we found in some + other way, will normally be more useful -- e.g., this name is + normally not mangled. */ + 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: - if (val.encoding == ATTR_VAL_STRING) - return val.u.string; + /* First name preference: override all. */ + { + 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: - if (abbrev->attrs[i].form == DW_FORM_ref_addr - || abbrev->attrs[i].form == DW_FORM_ref_sig8) - { - /* This refers to a specification defined in some other - compilation unit. We can handle this case if we - must, but it's harder. */ - break; - } - if (val.encoding == ATTR_VAL_UINT - || val.encoding == ATTR_VAL_REF_UNIT) - { - const char *name; + /* Second name preference: override DW_AT_name, don't override + DW_AT_linkage_name. */ + { + const char *name; - name = read_referenced_name (ddata, u, val.u.uint, - error_callback, data); - if (name != NULL) - ret = name; - } + name = read_referenced_name_from_attr (ddata, u, &abbrev->attrs[i], + &val, error_callback, data); + if (name != NULL) + ret = name; + } break; default: @@ -2233,25 +3513,22 @@ read_referenced_name (struct dwarf_data *ddata, struct unit *u, return ret; } -/* Add a single range to U that maps to function. Returns 1 on - success, 0 on error. */ +/* Add a range to a unit that maps to a function. This is called via + add_ranges. Returns 1 on success, 0 on error. */ static int -add_function_range (struct backtrace_state *state, struct dwarf_data *ddata, - struct function *function, uint64_t lowpc, uint64_t highpc, - backtrace_error_callback error_callback, - void *data, struct function_vector *vec) +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; - /* Add in the base address here, so that we can look up the PC - directly. */ - lowpc += ddata->base_address; - highpc += ddata->base_address; - if (vec->count > 0) { - p = (struct function_addrs *) vec->vec.base + vec->count - 1; + p = (struct function_addrs *) vec->vec.base + (vec->count - 1); if ((lowpc == p->high || lowpc == p->high + 1) && function == p->function) { @@ -2270,63 +3547,8 @@ add_function_range (struct backtrace_state *state, struct dwarf_data *ddata, p->low = lowpc; p->high = highpc; p->function = function; - ++vec->count; - return 1; -} -/* Add PC ranges to U that map to FUNCTION. Returns 1 on success, 0 - on error. */ - -static int -add_function_ranges (struct backtrace_state *state, struct dwarf_data *ddata, - struct unit *u, struct function *function, - uint64_t ranges, uint64_t base, - backtrace_error_callback error_callback, void *data, - struct function_vector *vec) -{ - struct dwarf_buf ranges_buf; - - if (ranges >= ddata->dwarf_ranges_size) - { - error_callback (data, "function ranges offset out of range", 0); - return 0; - } - - ranges_buf.name = ".debug_ranges"; - ranges_buf.start = ddata->dwarf_ranges; - ranges_buf.buf = ddata->dwarf_ranges + ranges; - ranges_buf.left = ddata->dwarf_ranges_size - ranges; - ranges_buf.is_bigendian = ddata->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_function_range (state, ddata, function, low + base, - high + base, error_callback, data, vec)) - return 0; - } - } - - if (ranges_buf.reported_underflow) - return 0; + ++vec->count; return 1; } @@ -2350,13 +3572,8 @@ read_function_entry (struct backtrace_state *state, struct dwarf_data *ddata, struct function *function; struct function_vector *vec; size_t i; - uint64_t lowpc; - int have_lowpc; - uint64_t highpc; - int have_highpc; - int highpc_is_relative; - uint64_t ranges; - int have_ranges; + struct pcrange pcrange; + int have_linkage_name; code = read_uleb128 (unit_buf); if (code == 0) @@ -2386,29 +3603,35 @@ read_function_entry (struct backtrace_state *state, struct dwarf_data *ddata, memset (function, 0, sizeof *function); } - lowpc = 0; - have_lowpc = 0; - highpc = 0; - have_highpc = 0; - highpc_is_relative = 0; - ranges = 0; - have_ranges = 0; + 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, unit_buf, - u->is_dwarf64, u->version, u->addrsize, - ddata->dwarf_str, ddata->dwarf_str_size, - &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; /* The compile unit sets the base address for any address ranges in the function entries. */ - if (abbrev->tag == DW_TAG_compile_unit - && abbrev->attrs[i].name == DW_AT_low_pc - && val.encoding == ATTR_VAL_ADDRESS) - base = val.u.uint; + 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) { @@ -2417,20 +3640,15 @@ read_function_entry (struct backtrace_state *state, struct dwarf_data *ddata, case DW_AT_call_file: if (val.encoding == ATTR_VAL_UINT) { - if (val.u.uint == 0) - function->caller_filename = ""; - else + if (val.u.uint >= lhdr->filenames_count) { - if (val.u.uint - 1 >= lhdr->filenames_count) - { - dwarf_buf_error (unit_buf, - ("invalid file number in " - "DW_AT_call_file attribute")); - return 0; - } - function->caller_filename = - lhdr->filenames[val.u.uint - 1]; + 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; @@ -2441,73 +3659,55 @@ read_function_entry (struct backtrace_state *state, struct dwarf_data *ddata, case DW_AT_abstract_origin: case DW_AT_specification: - if (abbrev->attrs[i].form == DW_FORM_ref_addr - || abbrev->attrs[i].form == DW_FORM_ref_sig8) - { - /* This refers to an abstract origin defined in - some other compilation unit. We can handle - this case if we must, but it's harder. */ - break; - } - if (val.encoding == ATTR_VAL_UINT - || val.encoding == ATTR_VAL_REF_UNIT) - { - const char *name; - - name = read_referenced_name (ddata, u, val.u.uint, - error_callback, data); - if (name != NULL) - function->name = name; - } + /* Second name preference: override DW_AT_name, don't override + DW_AT_linkage_name. */ + 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 (val.encoding == ATTR_VAL_STRING) - { - /* Don't override a name we found in some other - way, as it will normally be more - useful--e.g., this name is normally not - mangled. */ - if (function->name == NULL) - function->name = val.u.string; - } + /* Third name preference: don't override. */ + 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: - if (val.encoding == ATTR_VAL_STRING) - function->name = val.u.string; - break; - - case DW_AT_low_pc: - if (val.encoding == ATTR_VAL_ADDRESS) - { - lowpc = val.u.uint; - have_lowpc = 1; - } - break; + /* First name preference: override all. */ + { + const char *s; - case DW_AT_high_pc: - if (val.encoding == ATTR_VAL_ADDRESS) - { - highpc = val.u.uint; - have_highpc = 1; - } - else if (val.encoding == ATTR_VAL_UINT) - { - highpc = val.u.uint; - have_highpc = 1; - highpc_is_relative = 1; - } + 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_ranges: - if (val.encoding == ATTR_VAL_UINT - || val.encoding == ATTR_VAL_REF_SECTION) - { - ranges = val.u.uint; - have_ranges = 1; - } + case DW_AT_low_pc: case DW_AT_high_pc: case DW_AT_ranges: + update_pcrange (&abbrev->attrs[i], &val, &pcrange); break; default: @@ -2527,18 +3727,14 @@ read_function_entry (struct backtrace_state *state, struct dwarf_data *ddata, if (is_function) { - if (have_ranges) + if (pcrange.have_ranges + || (pcrange.have_lowpc && pcrange.have_highpc)) { - if (!add_function_ranges (state, ddata, u, function, ranges, - base, error_callback, data, vec)) - return 0; - } - else if (have_lowpc && have_highpc) - { - if (highpc_is_relative) - highpc += lowpc; - if (!add_function_range (state, ddata, function, lowpc, highpc, - error_callback, data, vec)) + 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 @@ -2574,8 +3770,23 @@ read_function_entry (struct backtrace_state *state, struct dwarf_data *ddata, if (fvec.count > 0) { + struct function_addrs *p; struct function_addrs *faddrs; + /* Allocate a trailing entry, but don't include it + in fvec.count. */ + 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; @@ -2609,6 +3820,7 @@ read_function_info (struct backtrace_state *state, struct dwarf_data *ddata, 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; @@ -2622,7 +3834,7 @@ read_function_info (struct backtrace_state *state, struct dwarf_data *ddata, } unit_buf.name = ".debug_info"; - unit_buf.start = ddata->dwarf_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; @@ -2640,6 +3852,18 @@ read_function_info (struct backtrace_state *state, struct dwarf_data *ddata, if (pfvec->count == 0) return; + /* Allocate a trailing entry, but don't include it in + pfvec->count. */ + 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) @@ -2676,30 +3900,54 @@ report_inlined_functions (uintptr_t pc, struct function *function, backtrace_full_callback callback, void *data, const char **filename, int *lineno) { - struct function_addrs *function_addrs; + struct function_addrs *p; + struct function_addrs *match; struct function *inlined; int ret; if (function->function_addrs_count == 0) return 0; - function_addrs = ((struct function_addrs *) - bsearch (&pc, function->function_addrs, - function->function_addrs_count, - sizeof (struct function_addrs), - function_addrs_search)); - if (function_addrs == NULL) + /* Our search isn't safe if pc == -1, as that is the sentinel + value. */ + if (pc + 1 == 0) return 0; - while (((size_t) (function_addrs - function->function_addrs) + 1 - < function->function_addrs_count) - && pc >= (function_addrs + 1)->low - && pc < (function_addrs + 1)->high) - ++function_addrs; + 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; + + /* Here pc >= p->low && pc < (p + 1)->low. The function_addrs are + sorted by low, so if pc > p->low we are at the end of a range of + function_addrs with the same low value. If pc == p->low walk + forward to the end of the range with that low value. Then walk + backward and use the first range that includes pc. */ + 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; /* We found an inlined call. */ - inlined = function_addrs->function; + inlined = match->function; /* Report any calls inlined into this one. */ ret = report_inlined_functions (pc, inlined, callback, data, @@ -2732,11 +3980,13 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata, int *found) { struct unit_addrs *entry; + int found_entry; struct unit *u; int new_data; struct line *lines; struct line *ln; - struct function_addrs *function_addrs; + struct function_addrs *p; + struct function_addrs *fmatch; struct function *function; const char *filename; int lineno; @@ -2744,9 +3994,13 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata, *found = 1; - /* Find an address range that includes PC. */ - entry = bsearch (&pc, ddata->addrs, ddata->addrs_count, - sizeof (struct unit_addrs), unit_addrs_search); + /* Find an address range that includes PC. Our search isn't safe if + PC == -1, as we use that as a sentinel value, so skip the search + in that case. */ + 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) { @@ -2754,14 +4008,32 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata, return 0; } - /* If there are multiple ranges that contain PC, use the last one, - in order to produce predictable results. If we assume that all - ranges are properly nested, then the last range will be the - smallest one. */ - while ((size_t) (entry - ddata->addrs) + 1 < ddata->addrs_count - && pc >= (entry + 1)->low - && pc < (entry + 1)->high) + /* Here pc >= entry->low && pc < (entry + 1)->low. The unit_addrs + are sorted by low, so if pc > p->low we are at the end of a range + of unit_addrs with the same low value. If pc == p->low walk + forward to the end of the range with that low value. Then walk + backward and use the first range that includes pc. */ + 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; + } /* We need the lines, lines_count, function_addrs, function_addrs_count fields of u. If they are not set, we need @@ -2797,6 +4069,7 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata, new_data = 0; if (lines == NULL) { + struct function_addrs *function_addrs; size_t function_addrs_count; struct line_header lhdr; size_t count; @@ -2913,24 +4186,39 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata, if (entry->u->function_addrs_count == 0) return callback (data, pc, ln->filename, ln->lineno, NULL); - function_addrs = ((struct function_addrs *) - bsearch (&pc, entry->u->function_addrs, - entry->u->function_addrs_count, - sizeof (struct function_addrs), - function_addrs_search)); - if (function_addrs == 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); - /* If there are multiple function ranges that contain PC, use the - last one, in order to produce predictable results. */ - - while (((size_t) (function_addrs - entry->u->function_addrs + 1) - < entry->u->function_addrs_count) - && pc >= (function_addrs + 1)->low - && pc < (function_addrs + 1)->high) - ++function_addrs; + /* Here pc >= p->low && pc < (p + 1)->low. The function_addrs are + sorted by low, so if pc > p->low we are at the end of a range of + function_addrs with the same low value. If pc == p->low walk + forward to the end of the range with that low value. Then walk + backward and use the first range that includes pc. */ + 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 = function_addrs->function; + function = fmatch->function; filename = ln->filename; lineno = ln->lineno; @@ -2999,37 +4287,36 @@ dwarf_fileline (struct backtrace_state *state, uintptr_t pc, static struct dwarf_data * build_dwarf_data (struct backtrace_state *state, uintptr_t base_address, - const unsigned char *dwarf_info, - size_t dwarf_info_size, - const unsigned char *dwarf_line, - size_t dwarf_line_size, - const unsigned char *dwarf_abbrev, - size_t dwarf_abbrev_size, - const unsigned char *dwarf_ranges, - size_t dwarf_ranges_size, - const unsigned char *dwarf_str, - size_t dwarf_str_size, + 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_info, dwarf_info_size, - dwarf_abbrev, dwarf_abbrev_size, dwarf_ranges, - dwarf_ranges_size, dwarf_str, dwarf_str_size, - is_bigendian, error_callback, data, &addrs_vec)) + 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); + /* No qsort for units required, already sorted. */ fdata = ((struct dwarf_data *) backtrace_alloc (state, sizeof (struct dwarf_data), @@ -3038,17 +4325,13 @@ build_dwarf_data (struct backtrace_state *state, return NULL; fdata->next = NULL; + fdata->altlink = altlink; fdata->base_address = base_address; fdata->addrs = addrs; fdata->addrs_count = addrs_count; - fdata->dwarf_info = dwarf_info; - fdata->dwarf_info_size = dwarf_info_size; - fdata->dwarf_line = dwarf_line; - fdata->dwarf_line_size = dwarf_line_size; - fdata->dwarf_ranges = dwarf_ranges; - fdata->dwarf_ranges_size = dwarf_ranges_size; - fdata->dwarf_str = dwarf_str; - fdata->dwarf_str_size = dwarf_str_size; + 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); @@ -3062,30 +4345,23 @@ build_dwarf_data (struct backtrace_state *state, int backtrace_dwarf_add (struct backtrace_state *state, uintptr_t base_address, - const unsigned char *dwarf_info, - size_t dwarf_info_size, - const unsigned char *dwarf_line, - size_t dwarf_line_size, - const unsigned char *dwarf_abbrev, - size_t dwarf_abbrev_size, - const unsigned char *dwarf_ranges, - size_t dwarf_ranges_size, - const unsigned char *dwarf_str, - size_t dwarf_str_size, + const struct dwarf_sections *dwarf_sections, int is_bigendian, + struct dwarf_data *fileline_altlink, backtrace_error_callback error_callback, - void *data, fileline *fileline_fn) + void *data, fileline *fileline_fn, + struct dwarf_data **fileline_entry) { struct dwarf_data *fdata; - fdata = build_dwarf_data (state, base_address, dwarf_info, dwarf_info_size, - dwarf_line, dwarf_line_size, dwarf_abbrev, - dwarf_abbrev_size, dwarf_ranges, dwarf_ranges_size, - dwarf_str, dwarf_str_size, is_bigendian, - error_callback, data); + 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; diff --git a/Sources/CBacktrace/elf.c b/Sources/CBacktrace/elf.c index 896e67b..cdafbef 100644 --- a/Sources/CBacktrace/elf.c +++ b/Sources/CBacktrace/elf.c @@ -1,6 +1,6 @@ #ifdef __linux__ /* elf.c -- Get debug data from an ELF file for backtraces. - Copyright (C) 2012-2018 Free Software Foundation, Inc. + Copyright (C) 2012-2021 Free Software Foundation, Inc. Written by Ian Lance Taylor, Google. Redistribution and use in source and binary forms, with or without @@ -338,41 +338,19 @@ typedef struct #define ELFCOMPRESS_ZLIB 1 -/* An index of ELF sections we care about. */ +/* Names of sections, indexed by enum dwarf_section in internal.h. */ -enum debug_section -{ - DEBUG_INFO, - DEBUG_LINE, - DEBUG_ABBREV, - DEBUG_RANGES, - DEBUG_STR, - - /* The old style compressed sections. This list must correspond to - the list of normal debug sections. */ - ZDEBUG_INFO, - ZDEBUG_LINE, - ZDEBUG_ABBREV, - ZDEBUG_RANGES, - ZDEBUG_STR, - - DEBUG_MAX -}; - -/* Names of sections, indexed by enum elf_section. */ - -static const char * const debug_section_names[DEBUG_MAX] = +static const char * const dwarf_section_names[DEBUG_MAX] = { ".debug_info", ".debug_line", ".debug_abbrev", ".debug_ranges", ".debug_str", - ".zdebug_info", - ".zdebug_line", - ".zdebug_abbrev", - ".zdebug_ranges", - ".zdebug_str" + ".debug_addr", + ".debug_str_offsets", + ".debug_line_str", + ".debug_rnglists" }; /* Information we gather for the sections we care about. */ @@ -413,6 +391,14 @@ struct elf_syminfo_data size_t count; }; +/* A view that works for either a file or memory. */ + +struct elf_view +{ + struct backtrace_view view; + int release; /* If non-zero, must call backtrace_release_view. */ +}; + /* Information about PowerPC64 ELFv1 .opd section. */ struct elf_ppc64_opd_data @@ -424,9 +410,48 @@ struct elf_ppc64_opd_data /* Size of the .opd section. */ size_t size; /* Corresponding section view. */ - struct backtrace_view view; + struct elf_view view; }; +/* Create a view of SIZE bytes from DESCRIPTOR/MEMORY at OFFSET. */ + +static int +elf_get_view (struct backtrace_state *state, int descriptor, + const unsigned char *memory, size_t memory_size, off_t offset, + uint64_t size, backtrace_error_callback error_callback, + void *data, struct elf_view *view) +{ + if (memory == NULL) + { + view->release = 1; + return backtrace_get_view (state, descriptor, offset, size, + error_callback, data, &view->view); + } + else + { + if ((uint64_t) offset + size > (uint64_t) memory_size) + { + error_callback (data, "out of range for in-memory file", 0); + return 0; + } + view->view.data = (const void *) (memory + offset); + view->view.base = NULL; + view->view.len = size; + view->release = 0; + return 1; + } +} + +/* Release a view read by elf_get_view. */ + +static void +elf_release_view (struct backtrace_state *state, struct elf_view *view, + backtrace_error_callback error_callback, void *data) +{ + if (view->release) + backtrace_release_view (state, &view->view, error_callback, data); +} + /* Compute the CRC-32 of BUF/LEN. This uses the CRC used for .gnu_debuglink files. */ @@ -523,18 +548,6 @@ elf_crc32_file (struct backtrace_state *state, int descriptor, return ret; } -/* A dummy callback function used when we can't find any debug info. */ - -static int -elf_nodebug (struct backtrace_state *state ATTRIBUTE_UNUSED, - uintptr_t pc ATTRIBUTE_UNUSED, - backtrace_full_callback callback ATTRIBUTE_UNUSED, - backtrace_error_callback error_callback, void *data) -{ - error_callback (data, "no debug info in ELF executable", -1); - return 0; -} - /* A dummy callback function used when we can't find a symbol table. */ @@ -547,6 +560,33 @@ elf_nosyms (struct backtrace_state *state ATTRIBUTE_UNUSED, error_callback (data, "no symbol table in ELF executable", -1); } +/* A callback function used when we can't find any debug info. */ + +static int +elf_nodebug (struct backtrace_state *state, uintptr_t pc, + backtrace_full_callback callback, + backtrace_error_callback error_callback, void *data) +{ + if (state->syminfo_fn != NULL && state->syminfo_fn != elf_nosyms) + { + struct backtrace_call_full bdata; + + /* Fetch symbol information so that we can least get the + function name. */ + + bdata.full_callback = callback; + bdata.full_error_callback = error_callback; + bdata.full_data = data; + bdata.ret = 0; + state->syminfo_fn (state, pc, backtrace_syminfo_to_full_callback, + backtrace_syminfo_to_full_error_callback, &bdata); + return bdata.ret; + } + + error_callback (data, "no debug info in ELF executable", -1); + return 0; +} + /* Compare struct elf_symbol for qsort. */ static int @@ -810,6 +850,8 @@ elf_readlink (struct backtrace_state *state, const char *filename, } } +#define SYSTEM_BUILD_ID_DIR "/usr/lib/debug/.build-id/" + /* Open a separate debug info file, using the build ID to find it. Returns an open file descriptor, or -1. @@ -822,7 +864,7 @@ elf_open_debugfile_by_buildid (struct backtrace_state *state, backtrace_error_callback error_callback, void *data) { - const char * const prefix = "/usr/lib/debug/.build-id/"; + const char * const prefix = SYSTEM_BUILD_ID_DIR; const size_t prefix_len = strlen (prefix); const char * const suffix = ".debug"; const size_t suffix_len = strlen (suffix); @@ -1056,7 +1098,7 @@ elf_open_debugfile_by_debuglink (struct backtrace_state *state, when this code is compiled with -g. */ static void -elf_zlib_failed(void) +elf_uncompress_failed(void) { } @@ -1083,7 +1125,7 @@ elf_zlib_fetch (const unsigned char **ppin, const unsigned char *pinend, if (unlikely (pinend - pin < 4)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -1213,7 +1255,7 @@ elf_zlib_inflate_table (unsigned char *codes, size_t codes_len, { if (unlikely (codes[i] >= 16)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -1250,7 +1292,7 @@ elf_zlib_inflate_table (unsigned char *codes, size_t codes_len, if (unlikely (jcnt > (1U << j))) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -1270,7 +1312,7 @@ elf_zlib_inflate_table (unsigned char *codes, size_t codes_len, if (unlikely ((val & ~HUFFMAN_VALUE_MASK) != 0)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -1286,7 +1328,7 @@ elf_zlib_inflate_table (unsigned char *codes, size_t codes_len, { if (unlikely (table[ind] != 0)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } table[ind] = tval; @@ -1374,7 +1416,7 @@ elf_zlib_inflate_table (unsigned char *codes, size_t codes_len, } if (unlikely (jcnt != 0)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } } @@ -1427,7 +1469,7 @@ elf_zlib_inflate_table (unsigned char *codes, size_t codes_len, if (unlikely ((next_secondary & HUFFMAN_VALUE_MASK) != next_secondary)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -1445,7 +1487,7 @@ elf_zlib_inflate_table (unsigned char *codes, size_t codes_len, if (unlikely ((tprimary & (1U << HUFFMAN_SECONDARY_SHIFT)) == 0)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } secondary = tprimary & HUFFMAN_VALUE_MASK; @@ -1453,7 +1495,7 @@ elf_zlib_inflate_table (unsigned char *codes, size_t codes_len, & HUFFMAN_BITS_MASK); if (unlikely (secondary_bits < j - 8)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } } @@ -1469,7 +1511,7 @@ elf_zlib_inflate_table (unsigned char *codes, size_t codes_len, { if (unlikely (table[secondary + 0x100 + ind] != 0)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } table[secondary + 0x100 + ind] = tval; @@ -1685,28 +1727,28 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, if (unlikely ((pin[0] & 0xf) != 8)) /* 8 is zlib encoding. */ { /* Unknown compression method. */ - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } if (unlikely ((pin[0] >> 4) > 7)) { /* Window size too large. Other than this check, we don't care about the window size. */ - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } if (unlikely ((pin[1] & 0x20) != 0)) { /* Stream expects a predefined dictionary, but we have no dictionary. */ - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } val = (pin[0] << 8) | pin[1]; if (unlikely (val % 31 != 0)) { /* Header check failure. */ - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } pin += 2; @@ -1743,7 +1785,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, if (unlikely (type == 3)) { /* Invalid block type. */ - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -1755,7 +1797,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, /* An uncompressed block. */ /* If we've read ahead more than a byte, back up. */ - while (bits > 8) + while (bits >= 8) { --pin; bits -= 8; @@ -1766,7 +1808,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, if (unlikely ((pinend - pin) < 4)) { /* Missing length. */ - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } len = pin[0] | (pin[1] << 8); @@ -1776,14 +1818,14 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, if (unlikely (len != lenc)) { /* Corrupt data. */ - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } if (unlikely (len > (unsigned int) (pinend - pin) || len > (unsigned int) (poutend - pout))) { /* Not enough space in buffers. */ - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } memcpy (pout, pin, len); @@ -1833,7 +1875,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, if (unlikely (nlit > 286 || ndist > 30)) { /* Values out of range. */ - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -1998,7 +2040,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, a secondary table is never necessary. */ if (unlikely ((t & (1U << HUFFMAN_SECONDARY_SHIFT)) != 0)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -2018,7 +2060,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, if (unlikely (plen == plenbase)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -2031,7 +2073,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, bits -= 2; if (unlikely ((unsigned int) (plenend - plen) < c)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -2040,10 +2082,10 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, { case 6: *plen++ = prev; - /* fallthrough */ + ATTRIBUTE_FALLTHROUGH; case 5: *plen++ = prev; - /* fallthrough */ + ATTRIBUTE_FALLTHROUGH; case 4: *plen++ = prev; } @@ -2066,7 +2108,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, bits -= 3; if (unlikely ((unsigned int) (plenend - plen) < c)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -2074,22 +2116,22 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, { case 10: *plen++ = 0; - /* fallthrough */ + ATTRIBUTE_FALLTHROUGH; case 9: *plen++ = 0; - /* fallthrough */ + ATTRIBUTE_FALLTHROUGH; case 8: *plen++ = 0; - /* fallthrough */ + ATTRIBUTE_FALLTHROUGH; case 7: *plen++ = 0; - /* fallthrough */ + ATTRIBUTE_FALLTHROUGH; case 6: *plen++ = 0; - /* fallthrough */ + ATTRIBUTE_FALLTHROUGH; case 5: *plen++ = 0; - /* fallthrough */ + ATTRIBUTE_FALLTHROUGH; case 4: *plen++ = 0; } @@ -2112,7 +2154,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, bits -= 7; if (unlikely ((unsigned int) (plenend - plen) < c)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -2121,7 +2163,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, } else { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } } @@ -2131,7 +2173,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, plen = plenbase; if (unlikely (plen[256] == 0)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -2183,7 +2225,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, { if (unlikely (pout == poutend)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -2212,7 +2254,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, len = 258; else if (unlikely (lit > 285)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } else @@ -2265,13 +2307,13 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, if (unlikely (pout == porigout)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } if (unlikely ((unsigned int) (poutend - pout) < len)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -2280,7 +2322,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, } else if (unlikely (dist > 29)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } else @@ -2311,13 +2353,13 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, if (unlikely ((unsigned int) (pout - porigout) < dist)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } if (unlikely ((unsigned int) (poutend - pout) < len)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -2347,7 +2389,7 @@ elf_zlib_inflate (const unsigned char *pin, size_t sin, uint16_t *zdebug_table, /* We should have filled the output buffer. */ if (unlikely (pout != poutend)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -2474,7 +2516,7 @@ elf_zlib_verify_checksum (const unsigned char *checkbytes, if (unlikely ((s2 << 16) + s1 != cksum)) { - elf_zlib_failed (); + elf_uncompress_failed (); return 0; } @@ -2629,219 +2671,1553 @@ backtrace_uncompress_zdebug (struct backtrace_state *state, return ret; } -/* Add the backtrace data for one ELF file. Returns 1 on success, - 0 on failure (in both cases descriptor is closed) or -1 if exe - is non-zero and the ELF file is ET_DYN, which tells the caller that - elf_add will need to be called on the descriptor again after - base_address is determined. */ +/* Number of LZMA states. */ +#define LZMA_STATES (12) + +/* Number of LZMA position states. The pb value of the property byte + is the number of bits to include in these states, and the maximum + value of pb is 4. */ +#define LZMA_POS_STATES (16) + +/* Number of LZMA distance states. These are used match distances + with a short match length: up to 4 bytes. */ +#define LZMA_DIST_STATES (4) + +/* Number of LZMA distance slots. LZMA uses six bits to encode larger + match lengths, so 1 << 6 possible probabilities. */ +#define LZMA_DIST_SLOTS (64) + +/* LZMA distances 0 to 3 are encoded directly, larger values use a + probability model. */ +#define LZMA_DIST_MODEL_START (4) + +/* The LZMA probability model ends at 14. */ +#define LZMA_DIST_MODEL_END (14) + +/* LZMA distance slots for distances less than 127. */ +#define LZMA_FULL_DISTANCES (128) + +/* LZMA uses four alignment bits. */ +#define LZMA_ALIGN_SIZE (16) + +/* LZMA match length is encoded with 4, 5, or 10 bits, some of which + are already known. */ +#define LZMA_LEN_LOW_SYMBOLS (8) +#define LZMA_LEN_MID_SYMBOLS (8) +#define LZMA_LEN_HIGH_SYMBOLS (256) + +/* LZMA literal encoding. */ +#define LZMA_LITERAL_CODERS_MAX (16) +#define LZMA_LITERAL_CODER_SIZE (0x300) + +/* LZMA is based on a large set of probabilities, each managed + independently. Each probability is an 11 bit number that we store + in a uint16_t. We use a single large array of probabilities. */ + +/* Lengths of entries in the LZMA probabilities array. The names used + here are copied from the Linux kernel implementation. */ + +#define LZMA_PROB_IS_MATCH_LEN (LZMA_STATES * LZMA_POS_STATES) +#define LZMA_PROB_IS_REP_LEN LZMA_STATES +#define LZMA_PROB_IS_REP0_LEN LZMA_STATES +#define LZMA_PROB_IS_REP1_LEN LZMA_STATES +#define LZMA_PROB_IS_REP2_LEN LZMA_STATES +#define LZMA_PROB_IS_REP0_LONG_LEN (LZMA_STATES * LZMA_POS_STATES) +#define LZMA_PROB_DIST_SLOT_LEN (LZMA_DIST_STATES * LZMA_DIST_SLOTS) +#define LZMA_PROB_DIST_SPECIAL_LEN (LZMA_FULL_DISTANCES - LZMA_DIST_MODEL_END) +#define LZMA_PROB_DIST_ALIGN_LEN LZMA_ALIGN_SIZE +#define LZMA_PROB_MATCH_LEN_CHOICE_LEN 1 +#define LZMA_PROB_MATCH_LEN_CHOICE2_LEN 1 +#define LZMA_PROB_MATCH_LEN_LOW_LEN (LZMA_POS_STATES * LZMA_LEN_LOW_SYMBOLS) +#define LZMA_PROB_MATCH_LEN_MID_LEN (LZMA_POS_STATES * LZMA_LEN_MID_SYMBOLS) +#define LZMA_PROB_MATCH_LEN_HIGH_LEN LZMA_LEN_HIGH_SYMBOLS +#define LZMA_PROB_REP_LEN_CHOICE_LEN 1 +#define LZMA_PROB_REP_LEN_CHOICE2_LEN 1 +#define LZMA_PROB_REP_LEN_LOW_LEN (LZMA_POS_STATES * LZMA_LEN_LOW_SYMBOLS) +#define LZMA_PROB_REP_LEN_MID_LEN (LZMA_POS_STATES * LZMA_LEN_MID_SYMBOLS) +#define LZMA_PROB_REP_LEN_HIGH_LEN LZMA_LEN_HIGH_SYMBOLS +#define LZMA_PROB_LITERAL_LEN \ + (LZMA_LITERAL_CODERS_MAX * LZMA_LITERAL_CODER_SIZE) + +/* Offsets into the LZMA probabilities array. This is mechanically + generated from the above lengths. */ + +#define LZMA_PROB_IS_MATCH_OFFSET 0 +#define LZMA_PROB_IS_REP_OFFSET \ + (LZMA_PROB_IS_MATCH_OFFSET + LZMA_PROB_IS_MATCH_LEN) +#define LZMA_PROB_IS_REP0_OFFSET \ + (LZMA_PROB_IS_REP_OFFSET + LZMA_PROB_IS_REP_LEN) +#define LZMA_PROB_IS_REP1_OFFSET \ + (LZMA_PROB_IS_REP0_OFFSET + LZMA_PROB_IS_REP0_LEN) +#define LZMA_PROB_IS_REP2_OFFSET \ + (LZMA_PROB_IS_REP1_OFFSET + LZMA_PROB_IS_REP1_LEN) +#define LZMA_PROB_IS_REP0_LONG_OFFSET \ + (LZMA_PROB_IS_REP2_OFFSET + LZMA_PROB_IS_REP2_LEN) +#define LZMA_PROB_DIST_SLOT_OFFSET \ + (LZMA_PROB_IS_REP0_LONG_OFFSET + LZMA_PROB_IS_REP0_LONG_LEN) +#define LZMA_PROB_DIST_SPECIAL_OFFSET \ + (LZMA_PROB_DIST_SLOT_OFFSET + LZMA_PROB_DIST_SLOT_LEN) +#define LZMA_PROB_DIST_ALIGN_OFFSET \ + (LZMA_PROB_DIST_SPECIAL_OFFSET + LZMA_PROB_DIST_SPECIAL_LEN) +#define LZMA_PROB_MATCH_LEN_CHOICE_OFFSET \ + (LZMA_PROB_DIST_ALIGN_OFFSET + LZMA_PROB_DIST_ALIGN_LEN) +#define LZMA_PROB_MATCH_LEN_CHOICE2_OFFSET \ + (LZMA_PROB_MATCH_LEN_CHOICE_OFFSET + LZMA_PROB_MATCH_LEN_CHOICE_LEN) +#define LZMA_PROB_MATCH_LEN_LOW_OFFSET \ + (LZMA_PROB_MATCH_LEN_CHOICE2_OFFSET + LZMA_PROB_MATCH_LEN_CHOICE2_LEN) +#define LZMA_PROB_MATCH_LEN_MID_OFFSET \ + (LZMA_PROB_MATCH_LEN_LOW_OFFSET + LZMA_PROB_MATCH_LEN_LOW_LEN) +#define LZMA_PROB_MATCH_LEN_HIGH_OFFSET \ + (LZMA_PROB_MATCH_LEN_MID_OFFSET + LZMA_PROB_MATCH_LEN_MID_LEN) +#define LZMA_PROB_REP_LEN_CHOICE_OFFSET \ + (LZMA_PROB_MATCH_LEN_HIGH_OFFSET + LZMA_PROB_MATCH_LEN_HIGH_LEN) +#define LZMA_PROB_REP_LEN_CHOICE2_OFFSET \ + (LZMA_PROB_REP_LEN_CHOICE_OFFSET + LZMA_PROB_REP_LEN_CHOICE_LEN) +#define LZMA_PROB_REP_LEN_LOW_OFFSET \ + (LZMA_PROB_REP_LEN_CHOICE2_OFFSET + LZMA_PROB_REP_LEN_CHOICE2_LEN) +#define LZMA_PROB_REP_LEN_MID_OFFSET \ + (LZMA_PROB_REP_LEN_LOW_OFFSET + LZMA_PROB_REP_LEN_LOW_LEN) +#define LZMA_PROB_REP_LEN_HIGH_OFFSET \ + (LZMA_PROB_REP_LEN_MID_OFFSET + LZMA_PROB_REP_LEN_MID_LEN) +#define LZMA_PROB_LITERAL_OFFSET \ + (LZMA_PROB_REP_LEN_HIGH_OFFSET + LZMA_PROB_REP_LEN_HIGH_LEN) + +#define LZMA_PROB_TOTAL_COUNT \ + (LZMA_PROB_LITERAL_OFFSET + LZMA_PROB_LITERAL_LEN) + +/* Check that the number of LZMA probabilities is the same as the + Linux kernel implementation. */ + +#if LZMA_PROB_TOTAL_COUNT != 1846 + (1 << 4) * 0x300 + #error Wrong number of LZMA probabilities +#endif + +/* Expressions for the offset in the LZMA probabilities array of a + specific probability. */ + +#define LZMA_IS_MATCH(state, pos) \ + (LZMA_PROB_IS_MATCH_OFFSET + (state) * LZMA_POS_STATES + (pos)) +#define LZMA_IS_REP(state) \ + (LZMA_PROB_IS_REP_OFFSET + (state)) +#define LZMA_IS_REP0(state) \ + (LZMA_PROB_IS_REP0_OFFSET + (state)) +#define LZMA_IS_REP1(state) \ + (LZMA_PROB_IS_REP1_OFFSET + (state)) +#define LZMA_IS_REP2(state) \ + (LZMA_PROB_IS_REP2_OFFSET + (state)) +#define LZMA_IS_REP0_LONG(state, pos) \ + (LZMA_PROB_IS_REP0_LONG_OFFSET + (state) * LZMA_POS_STATES + (pos)) +#define LZMA_DIST_SLOT(dist, slot) \ + (LZMA_PROB_DIST_SLOT_OFFSET + (dist) * LZMA_DIST_SLOTS + (slot)) +#define LZMA_DIST_SPECIAL(dist) \ + (LZMA_PROB_DIST_SPECIAL_OFFSET + (dist)) +#define LZMA_DIST_ALIGN(dist) \ + (LZMA_PROB_DIST_ALIGN_OFFSET + (dist)) +#define LZMA_MATCH_LEN_CHOICE \ + LZMA_PROB_MATCH_LEN_CHOICE_OFFSET +#define LZMA_MATCH_LEN_CHOICE2 \ + LZMA_PROB_MATCH_LEN_CHOICE2_OFFSET +#define LZMA_MATCH_LEN_LOW(pos, sym) \ + (LZMA_PROB_MATCH_LEN_LOW_OFFSET + (pos) * LZMA_LEN_LOW_SYMBOLS + (sym)) +#define LZMA_MATCH_LEN_MID(pos, sym) \ + (LZMA_PROB_MATCH_LEN_MID_OFFSET + (pos) * LZMA_LEN_MID_SYMBOLS + (sym)) +#define LZMA_MATCH_LEN_HIGH(sym) \ + (LZMA_PROB_MATCH_LEN_HIGH_OFFSET + (sym)) +#define LZMA_REP_LEN_CHOICE \ + LZMA_PROB_REP_LEN_CHOICE_OFFSET +#define LZMA_REP_LEN_CHOICE2 \ + LZMA_PROB_REP_LEN_CHOICE2_OFFSET +#define LZMA_REP_LEN_LOW(pos, sym) \ + (LZMA_PROB_REP_LEN_LOW_OFFSET + (pos) * LZMA_LEN_LOW_SYMBOLS + (sym)) +#define LZMA_REP_LEN_MID(pos, sym) \ + (LZMA_PROB_REP_LEN_MID_OFFSET + (pos) * LZMA_LEN_MID_SYMBOLS + (sym)) +#define LZMA_REP_LEN_HIGH(sym) \ + (LZMA_PROB_REP_LEN_HIGH_OFFSET + (sym)) +#define LZMA_LITERAL(code, size) \ + (LZMA_PROB_LITERAL_OFFSET + (code) * LZMA_LITERAL_CODER_SIZE + (size)) + +/* Read an LZMA varint from BUF, reading and updating *POFFSET, + setting *VAL. Returns 0 on error, 1 on success. */ static int -elf_add (struct backtrace_state *state, const char *filename, int descriptor, - uintptr_t base_address, backtrace_error_callback error_callback, - void *data, fileline *fileline_fn, int *found_sym, int *found_dwarf, - int exe, int debuginfo) +elf_lzma_varint (const unsigned char *compressed, size_t compressed_size, + size_t *poffset, uint64_t *val) { - struct backtrace_view ehdr_view; - b_elf_ehdr ehdr; - off_t shoff; - unsigned int shnum; - unsigned int shstrndx; - struct backtrace_view shdrs_view; - int shdrs_view_valid; - const b_elf_shdr *shdrs; - const b_elf_shdr *shstrhdr; - size_t shstr_size; - off_t shstr_off; - struct backtrace_view names_view; - int names_view_valid; - const char *names; - unsigned int symtab_shndx; - unsigned int dynsym_shndx; - unsigned int i; - struct debug_section_info sections[DEBUG_MAX]; - struct backtrace_view symtab_view; - int symtab_view_valid; - struct backtrace_view strtab_view; - int strtab_view_valid; - struct backtrace_view buildid_view; - int buildid_view_valid; - const char *buildid_data; - uint32_t buildid_size; - struct backtrace_view debuglink_view; - int debuglink_view_valid; - const char *debuglink_name; - uint32_t debuglink_crc; - off_t min_offset; - off_t max_offset; - struct backtrace_view debug_view; - int debug_view_valid; - unsigned int using_debug_view; - uint16_t *zdebug_table; - struct elf_ppc64_opd_data opd_data, *opd; - - if (!debuginfo) + size_t off; + int i; + uint64_t v; + unsigned char b; + + off = *poffset; + i = 0; + v = 0; + while (1) { - *found_sym = 0; - *found_dwarf = 0; + if (unlikely (off >= compressed_size)) + { + elf_uncompress_failed (); + return 0; + } + b = compressed[off]; + v |= (b & 0x7f) << (i * 7); + ++off; + if ((b & 0x80) == 0) + { + *poffset = off; + *val = v; + return 1; + } + ++i; + if (unlikely (i >= 9)) + { + elf_uncompress_failed (); + return 0; + } } +} - shdrs_view_valid = 0; - names_view_valid = 0; - symtab_view_valid = 0; - strtab_view_valid = 0; - buildid_view_valid = 0; - buildid_data = NULL; - buildid_size = 0; - debuglink_view_valid = 0; - debuglink_name = NULL; - debuglink_crc = 0; - debug_view_valid = 0; - opd = NULL; +/* Normalize the LZMA range decoder, pulling in an extra input byte if + needed. */ - if (!backtrace_get_view (state, descriptor, 0, sizeof ehdr, error_callback, - data, &ehdr_view)) - goto fail; +static void +elf_lzma_range_normalize (const unsigned char *compressed, + size_t compressed_size, size_t *poffset, + uint32_t *prange, uint32_t *pcode) +{ + if (*prange < (1U << 24)) + { + if (unlikely (*poffset >= compressed_size)) + { + /* We assume this will be caught elsewhere. */ + elf_uncompress_failed (); + return; + } + *prange <<= 8; + *pcode <<= 8; + *pcode += compressed[*poffset]; + ++*poffset; + } +} - memcpy (&ehdr, ehdr_view.data, sizeof ehdr); +/* Read and return a single bit from the LZMA stream, reading and + updating *PROB. Each bit comes from the range coder. */ - backtrace_release_view (state, &ehdr_view, error_callback, data); +static int +elf_lzma_bit (const unsigned char *compressed, size_t compressed_size, + uint16_t *prob, size_t *poffset, uint32_t *prange, + uint32_t *pcode) +{ + uint32_t bound; - if (ehdr.e_ident[EI_MAG0] != ELFMAG0 - || ehdr.e_ident[EI_MAG1] != ELFMAG1 - || ehdr.e_ident[EI_MAG2] != ELFMAG2 - || ehdr.e_ident[EI_MAG3] != ELFMAG3) + elf_lzma_range_normalize (compressed, compressed_size, poffset, + prange, pcode); + bound = (*prange >> 11) * (uint32_t) *prob; + if (*pcode < bound) { - error_callback (data, "executable file is not ELF", 0); - goto fail; + *prange = bound; + *prob += ((1U << 11) - *prob) >> 5; + return 0; } - if (ehdr.e_ident[EI_VERSION] != EV_CURRENT) + else { - error_callback (data, "executable file is unrecognized ELF version", 0); - goto fail; + *prange -= bound; + *pcode -= bound; + *prob -= *prob >> 5; + return 1; } +} -#if BACKTRACE_ELF_SIZE == 32 -#define BACKTRACE_ELFCLASS ELFCLASS32 -#else -#define BACKTRACE_ELFCLASS ELFCLASS64 -#endif +/* Read an integer of size BITS from the LZMA stream, most significant + bit first. The bits are predicted using PROBS. */ - if (ehdr.e_ident[EI_CLASS] != BACKTRACE_ELFCLASS) - { - error_callback (data, "executable file is unexpected ELF class", 0); - goto fail; - } +static uint32_t +elf_lzma_integer (const unsigned char *compressed, size_t compressed_size, + uint16_t *probs, uint32_t bits, size_t *poffset, + uint32_t *prange, uint32_t *pcode) +{ + uint32_t sym; + uint32_t i; - if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB - && ehdr.e_ident[EI_DATA] != ELFDATA2MSB) + sym = 1; + for (i = 0; i < bits; i++) { - error_callback (data, "executable file has unknown endianness", 0); - goto fail; + int bit; + + bit = elf_lzma_bit (compressed, compressed_size, probs + sym, poffset, + prange, pcode); + sym <<= 1; + sym += bit; } + return sym - (1 << bits); +} - /* If the executable is ET_DYN, it is either a PIE, or we are running - directly a shared library with .interp. We need to wait for - dl_iterate_phdr in that case to determine the actual base_address. */ - if (exe && ehdr.e_type == ET_DYN) - return -1; +/* Read an integer of size BITS from the LZMA stream, least + significant bit first. The bits are predicted using PROBS. */ - shoff = ehdr.e_shoff; - shnum = ehdr.e_shnum; - shstrndx = ehdr.e_shstrndx; +static uint32_t +elf_lzma_reverse_integer (const unsigned char *compressed, + size_t compressed_size, uint16_t *probs, + uint32_t bits, size_t *poffset, uint32_t *prange, + uint32_t *pcode) +{ + uint32_t sym; + uint32_t val; + uint32_t i; - if ((shnum == 0 || shstrndx == SHN_XINDEX) - && shoff != 0) + sym = 1; + val = 0; + for (i = 0; i < bits; i++) { - struct backtrace_view shdr_view; - const b_elf_shdr *shdr; + int bit; - if (!backtrace_get_view (state, descriptor, shoff, sizeof shdr, - error_callback, data, &shdr_view)) - goto fail; - - shdr = (const b_elf_shdr *) shdr_view.data; + bit = elf_lzma_bit (compressed, compressed_size, probs + sym, poffset, + prange, pcode); + sym <<= 1; + sym += bit; + val += bit << i; + } + return val; +} - if (shnum == 0) - shnum = shdr->sh_size; +/* Read a length from the LZMA stream. IS_REP picks either LZMA_MATCH + or LZMA_REP probabilities. */ - if (shstrndx == SHN_XINDEX) +static uint32_t +elf_lzma_len (const unsigned char *compressed, size_t compressed_size, + uint16_t *probs, int is_rep, unsigned int pos_state, + size_t *poffset, uint32_t *prange, uint32_t *pcode) +{ + uint16_t *probs_choice; + uint16_t *probs_sym; + uint32_t bits; + uint32_t len; + + probs_choice = probs + (is_rep + ? LZMA_REP_LEN_CHOICE + : LZMA_MATCH_LEN_CHOICE); + if (elf_lzma_bit (compressed, compressed_size, probs_choice, poffset, + prange, pcode)) + { + probs_choice = probs + (is_rep + ? LZMA_REP_LEN_CHOICE2 + : LZMA_MATCH_LEN_CHOICE2); + if (elf_lzma_bit (compressed, compressed_size, probs_choice, + poffset, prange, pcode)) { - shstrndx = shdr->sh_link; - - /* Versions of the GNU binutils between 2.12 and 2.18 did - not handle objects with more than SHN_LORESERVE sections - correctly. All large section indexes were offset by - 0x100. There is more information at - http://sourceware.org/bugzilla/show_bug.cgi?id-5900 . - Fortunately these object files are easy to detect, as the - GNU binutils always put the section header string table - near the end of the list of sections. Thus if the - section header string table index is larger than the - number of sections, then we know we have to subtract - 0x100 to get the real section index. */ - if (shstrndx >= shnum && shstrndx >= SHN_LORESERVE + 0x100) - shstrndx -= 0x100; + probs_sym = probs + (is_rep + ? LZMA_REP_LEN_HIGH (0) + : LZMA_MATCH_LEN_HIGH (0)); + bits = 8; + len = 2 + 8 + 8; } - - backtrace_release_view (state, &shdr_view, error_callback, data); + else + { + probs_sym = probs + (is_rep + ? LZMA_REP_LEN_MID (pos_state, 0) + : LZMA_MATCH_LEN_MID (pos_state, 0)); + bits = 3; + len = 2 + 8; + } + } + else + { + probs_sym = probs + (is_rep + ? LZMA_REP_LEN_LOW (pos_state, 0) + : LZMA_MATCH_LEN_LOW (pos_state, 0)); + bits = 3; + len = 2; } - /* To translate PC to file/line when using DWARF, we need to find - the .debug_info and .debug_line sections. */ + len += elf_lzma_integer (compressed, compressed_size, probs_sym, bits, + poffset, prange, pcode); + return len; +} - /* Read the section headers, skipping the first one. */ +/* Uncompress one LZMA block from a minidebug file. The compressed + data is at COMPRESSED + *POFFSET. Update *POFFSET. Store the data + into the memory at UNCOMPRESSED, size UNCOMPRESSED_SIZE. CHECK is + the stream flag from the xz header. Return 1 on successful + decompression. */ - if (!backtrace_get_view (state, descriptor, shoff + sizeof (b_elf_shdr), - (shnum - 1) * sizeof (b_elf_shdr), - error_callback, data, &shdrs_view)) - goto fail; - shdrs_view_valid = 1; - shdrs = (const b_elf_shdr *) shdrs_view.data; +static int +elf_uncompress_lzma_block (const unsigned char *compressed, + size_t compressed_size, unsigned char check, + uint16_t *probs, unsigned char *uncompressed, + size_t uncompressed_size, size_t *poffset) +{ + size_t off; + size_t block_header_offset; + size_t block_header_size; + unsigned char block_flags; + uint64_t header_compressed_size; + uint64_t header_uncompressed_size; + unsigned char lzma2_properties; + uint32_t computed_crc; + uint32_t stream_crc; + size_t uncompressed_offset; + size_t dict_start_offset; + unsigned int lc; + unsigned int lp; + unsigned int pb; + uint32_t range; + uint32_t code; + uint32_t lstate; + uint32_t dist[4]; + + off = *poffset; + block_header_offset = off; + + /* Block header size is a single byte. */ + if (unlikely (off >= compressed_size)) + { + elf_uncompress_failed (); + return 0; + } + block_header_size = (compressed[off] + 1) * 4; + if (unlikely (off + block_header_size > compressed_size)) + { + elf_uncompress_failed (); + return 0; + } - /* Read the section names. */ + /* Block flags. */ + block_flags = compressed[off + 1]; + if (unlikely ((block_flags & 0x3c) != 0)) + { + elf_uncompress_failed (); + return 0; + } - shstrhdr = &shdrs[shstrndx - 1]; - shstr_size = shstrhdr->sh_size; - shstr_off = shstrhdr->sh_offset; + off += 2; - if (!backtrace_get_view (state, descriptor, shstr_off, shstr_size, - error_callback, data, &names_view)) - goto fail; - names_view_valid = 1; - names = (const char *) names_view.data; + /* Optional compressed size. */ + header_compressed_size = 0; + if ((block_flags & 0x40) != 0) + { + *poffset = off; + if (!elf_lzma_varint (compressed, compressed_size, poffset, + &header_compressed_size)) + return 0; + off = *poffset; + } - symtab_shndx = 0; - dynsym_shndx = 0; + /* Optional uncompressed size. */ + header_uncompressed_size = 0; + if ((block_flags & 0x80) != 0) + { + *poffset = off; + if (!elf_lzma_varint (compressed, compressed_size, poffset, + &header_uncompressed_size)) + return 0; + off = *poffset; + } - memset (sections, 0, sizeof sections); + /* The recipe for creating a minidebug file is to run the xz program + with no arguments, so we expect exactly one filter: lzma2. */ - /* Look for the symbol table. */ - for (i = 1; i < shnum; ++i) + if (unlikely ((block_flags & 0x3) != 0)) { - const b_elf_shdr *shdr; - unsigned int sh_name; - const char *name; - int j; + elf_uncompress_failed (); + return 0; + } - shdr = &shdrs[i - 1]; + if (unlikely (off + 2 >= block_header_offset + block_header_size)) + { + elf_uncompress_failed (); + return 0; + } - if (shdr->sh_type == SHT_SYMTAB) - symtab_shndx = i; - else if (shdr->sh_type == SHT_DYNSYM) - dynsym_shndx = i; + /* The filter ID for LZMA2 is 0x21. */ + if (unlikely (compressed[off] != 0x21)) + { + elf_uncompress_failed (); + return 0; + } + ++off; - sh_name = shdr->sh_name; - if (sh_name >= shstr_size) - { - error_callback (data, "ELF section name out of range", 0); - goto fail; + /* The size of the filter properties for LZMA2 is 1. */ + if (unlikely (compressed[off] != 1)) + { + elf_uncompress_failed (); + return 0; + } + ++off; + + lzma2_properties = compressed[off]; + ++off; + + if (unlikely (lzma2_properties > 40)) + { + elf_uncompress_failed (); + return 0; + } + + /* The properties describe the dictionary size, but we don't care + what that is. */ + + /* Block header padding. */ + if (unlikely (off + 4 > compressed_size)) + { + elf_uncompress_failed (); + return 0; + } + + off = (off + 3) &~ (size_t) 3; + + if (unlikely (off + 4 > compressed_size)) + { + elf_uncompress_failed (); + return 0; + } + + /* Block header CRC. */ + computed_crc = elf_crc32 (0, compressed + block_header_offset, + block_header_size - 4); + stream_crc = (compressed[off] + | (compressed[off + 1] << 8) + | (compressed[off + 2] << 16) + | (compressed[off + 3] << 24)); + if (unlikely (computed_crc != stream_crc)) + { + elf_uncompress_failed (); + return 0; + } + off += 4; + + /* Read a sequence of LZMA2 packets. */ + + uncompressed_offset = 0; + dict_start_offset = 0; + lc = 0; + lp = 0; + pb = 0; + lstate = 0; + while (off < compressed_size) + { + unsigned char control; + + range = 0xffffffff; + code = 0; + + control = compressed[off]; + ++off; + if (unlikely (control == 0)) + { + /* End of packets. */ + break; + } + + if (control == 1 || control >= 0xe0) + { + /* Reset dictionary to empty. */ + dict_start_offset = uncompressed_offset; + } + + if (control < 0x80) + { + size_t chunk_size; + + /* The only valid values here are 1 or 2. A 1 means to + reset the dictionary (done above). Then we see an + uncompressed chunk. */ + + if (unlikely (control > 2)) + { + elf_uncompress_failed (); + return 0; + } + + /* An uncompressed chunk is a two byte size followed by + data. */ + + if (unlikely (off + 2 > compressed_size)) + { + elf_uncompress_failed (); + return 0; + } + + chunk_size = compressed[off] << 8; + chunk_size += compressed[off + 1]; + ++chunk_size; + + off += 2; + + if (unlikely (off + chunk_size > compressed_size)) + { + elf_uncompress_failed (); + return 0; + } + if (unlikely (uncompressed_offset + chunk_size > uncompressed_size)) + { + elf_uncompress_failed (); + return 0; + } + + memcpy (uncompressed + uncompressed_offset, compressed + off, + chunk_size); + uncompressed_offset += chunk_size; + off += chunk_size; + } + else + { + size_t uncompressed_chunk_start; + size_t uncompressed_chunk_size; + size_t compressed_chunk_size; + size_t limit; + + /* An LZMA chunk. This starts with an uncompressed size and + a compressed size. */ + + if (unlikely (off + 4 >= compressed_size)) + { + elf_uncompress_failed (); + return 0; + } + + uncompressed_chunk_start = uncompressed_offset; + + uncompressed_chunk_size = (control & 0x1f) << 16; + uncompressed_chunk_size += compressed[off] << 8; + uncompressed_chunk_size += compressed[off + 1]; + ++uncompressed_chunk_size; + + compressed_chunk_size = compressed[off + 2] << 8; + compressed_chunk_size += compressed[off + 3]; + ++compressed_chunk_size; + + off += 4; + + /* Bit 7 (0x80) is set. + Bits 6 and 5 (0x40 and 0x20) are as follows: + 0: don't reset anything + 1: reset state + 2: reset state, read properties + 3: reset state, read properties, reset dictionary (done above) */ + + if (control >= 0xc0) + { + unsigned char props; + + /* Bit 6 is set, read properties. */ + + if (unlikely (off >= compressed_size)) + { + elf_uncompress_failed (); + return 0; + } + props = compressed[off]; + ++off; + if (unlikely (props > (4 * 5 + 4) * 9 + 8)) + { + elf_uncompress_failed (); + return 0; + } + pb = 0; + while (props >= 9 * 5) + { + props -= 9 * 5; + ++pb; + } + lp = 0; + while (props > 9) + { + props -= 9; + ++lp; + } + lc = props; + if (unlikely (lc + lp > 4)) + { + elf_uncompress_failed (); + return 0; + } + } + + if (control >= 0xa0) + { + size_t i; + + /* Bit 5 or 6 is set, reset LZMA state. */ + + lstate = 0; + memset (&dist, 0, sizeof dist); + for (i = 0; i < LZMA_PROB_TOTAL_COUNT; i++) + probs[i] = 1 << 10; + range = 0xffffffff; + code = 0; + } + + /* Read the range code. */ + + if (unlikely (off + 5 > compressed_size)) + { + elf_uncompress_failed (); + return 0; + } + + /* The byte at compressed[off] is ignored for some + reason. */ + + code = ((compressed[off + 1] << 24) + + (compressed[off + 2] << 16) + + (compressed[off + 3] << 8) + + compressed[off + 4]); + off += 5; + + /* This is the main LZMA decode loop. */ + + limit = off + compressed_chunk_size; + *poffset = off; + while (*poffset < limit) + { + unsigned int pos_state; + + if (unlikely (uncompressed_offset + == (uncompressed_chunk_start + + uncompressed_chunk_size))) + { + /* We've decompressed all the expected bytes. */ + break; + } + + pos_state = ((uncompressed_offset - dict_start_offset) + & ((1 << pb) - 1)); + + if (elf_lzma_bit (compressed, compressed_size, + probs + LZMA_IS_MATCH (lstate, pos_state), + poffset, &range, &code)) + { + uint32_t len; + + if (elf_lzma_bit (compressed, compressed_size, + probs + LZMA_IS_REP (lstate), + poffset, &range, &code)) + { + int short_rep; + uint32_t next_dist; + + /* Repeated match. */ + + short_rep = 0; + if (elf_lzma_bit (compressed, compressed_size, + probs + LZMA_IS_REP0 (lstate), + poffset, &range, &code)) + { + if (elf_lzma_bit (compressed, compressed_size, + probs + LZMA_IS_REP1 (lstate), + poffset, &range, &code)) + { + if (elf_lzma_bit (compressed, compressed_size, + probs + LZMA_IS_REP2 (lstate), + poffset, &range, &code)) + { + next_dist = dist[3]; + dist[3] = dist[2]; + } + else + { + next_dist = dist[2]; + } + dist[2] = dist[1]; + } + else + { + next_dist = dist[1]; + } + + dist[1] = dist[0]; + dist[0] = next_dist; + } + else + { + if (!elf_lzma_bit (compressed, compressed_size, + (probs + + LZMA_IS_REP0_LONG (lstate, + pos_state)), + poffset, &range, &code)) + short_rep = 1; + } + + if (lstate < 7) + lstate = short_rep ? 9 : 8; + else + lstate = 11; + + if (short_rep) + len = 1; + else + len = elf_lzma_len (compressed, compressed_size, + probs, 1, pos_state, poffset, + &range, &code); + } + else + { + uint32_t dist_state; + uint32_t dist_slot; + uint16_t *probs_dist; + + /* Match. */ + + if (lstate < 7) + lstate = 7; + else + lstate = 10; + dist[3] = dist[2]; + dist[2] = dist[1]; + dist[1] = dist[0]; + len = elf_lzma_len (compressed, compressed_size, + probs, 0, pos_state, poffset, + &range, &code); + + if (len < 4 + 2) + dist_state = len - 2; + else + dist_state = 3; + probs_dist = probs + LZMA_DIST_SLOT (dist_state, 0); + dist_slot = elf_lzma_integer (compressed, + compressed_size, + probs_dist, 6, + poffset, &range, + &code); + if (dist_slot < LZMA_DIST_MODEL_START) + dist[0] = dist_slot; + else + { + uint32_t limit; + + limit = (dist_slot >> 1) - 1; + dist[0] = 2 + (dist_slot & 1); + if (dist_slot < LZMA_DIST_MODEL_END) + { + dist[0] <<= limit; + probs_dist = (probs + + LZMA_DIST_SPECIAL(dist[0] + - dist_slot + - 1)); + dist[0] += + elf_lzma_reverse_integer (compressed, + compressed_size, + probs_dist, + limit, poffset, + &range, &code); + } + else + { + uint32_t dist0; + uint32_t i; + + dist0 = dist[0]; + for (i = 0; i < limit - 4; i++) + { + uint32_t mask; + + elf_lzma_range_normalize (compressed, + compressed_size, + poffset, + &range, &code); + range >>= 1; + code -= range; + mask = -(code >> 31); + code += range & mask; + dist0 <<= 1; + dist0 += mask + 1; + } + dist0 <<= 4; + probs_dist = probs + LZMA_DIST_ALIGN (0); + dist0 += + elf_lzma_reverse_integer (compressed, + compressed_size, + probs_dist, 4, + poffset, + &range, &code); + dist[0] = dist0; + } + } + } + + if (unlikely (uncompressed_offset + - dict_start_offset < dist[0] + 1)) + { + elf_uncompress_failed (); + return 0; + } + if (unlikely (uncompressed_offset + len > uncompressed_size)) + { + elf_uncompress_failed (); + return 0; + } + + if (dist[0] == 0) + { + /* A common case, meaning repeat the last + character LEN times. */ + memset (uncompressed + uncompressed_offset, + uncompressed[uncompressed_offset - 1], + len); + uncompressed_offset += len; + } + else if (dist[0] + 1 >= len) + { + memcpy (uncompressed + uncompressed_offset, + uncompressed + uncompressed_offset - dist[0] - 1, + len); + uncompressed_offset += len; + } + else + { + while (len > 0) + { + uint32_t copy; + + copy = len < dist[0] + 1 ? len : dist[0] + 1; + memcpy (uncompressed + uncompressed_offset, + (uncompressed + uncompressed_offset + - dist[0] - 1), + copy); + len -= copy; + uncompressed_offset += copy; + } + } + } + else + { + unsigned char prev; + unsigned char low; + size_t high; + uint16_t *lit_probs; + unsigned int sym; + + /* Literal value. */ + + if (uncompressed_offset > 0) + prev = uncompressed[uncompressed_offset - 1]; + else + prev = 0; + low = prev >> (8 - lc); + high = (((uncompressed_offset - dict_start_offset) + & ((1 << lp) - 1)) + << lc); + lit_probs = probs + LZMA_LITERAL (low + high, 0); + if (lstate < 7) + sym = elf_lzma_integer (compressed, compressed_size, + lit_probs, 8, poffset, &range, + &code); + else + { + unsigned int match; + unsigned int bit; + unsigned int match_bit; + unsigned int idx; + + sym = 1; + if (uncompressed_offset >= dist[0] + 1) + match = uncompressed[uncompressed_offset - dist[0] - 1]; + else + match = 0; + match <<= 1; + bit = 0x100; + do + { + match_bit = match & bit; + match <<= 1; + idx = bit + match_bit + sym; + sym <<= 1; + if (elf_lzma_bit (compressed, compressed_size, + lit_probs + idx, poffset, + &range, &code)) + { + ++sym; + bit &= match_bit; + } + else + { + bit &= ~ match_bit; + } + } + while (sym < 0x100); + } + + if (unlikely (uncompressed_offset >= uncompressed_size)) + { + elf_uncompress_failed (); + return 0; + } + + uncompressed[uncompressed_offset] = (unsigned char) sym; + ++uncompressed_offset; + if (lstate <= 3) + lstate = 0; + else if (lstate <= 9) + lstate -= 3; + else + lstate -= 6; + } + } + + elf_lzma_range_normalize (compressed, compressed_size, poffset, + &range, &code); + + off = *poffset; + } + } + + /* We have reached the end of the block. Pad to four byte + boundary. */ + off = (off + 3) &~ (size_t) 3; + if (unlikely (off > compressed_size)) + { + elf_uncompress_failed (); + return 0; + } + + switch (check) + { + case 0: + /* No check. */ + break; + + case 1: + /* CRC32 */ + if (unlikely (off + 4 > compressed_size)) + { + elf_uncompress_failed (); + return 0; + } + computed_crc = elf_crc32 (0, uncompressed, uncompressed_offset); + stream_crc = (compressed[off] + | (compressed[off + 1] << 8) + | (compressed[off + 2] << 16) + | (compressed[off + 3] << 24)); + if (computed_crc != stream_crc) + { + elf_uncompress_failed (); + return 0; + } + off += 4; + break; + + case 4: + /* CRC64. We don't bother computing a CRC64 checksum. */ + if (unlikely (off + 8 > compressed_size)) + { + elf_uncompress_failed (); + return 0; + } + off += 8; + break; + + case 10: + /* SHA. We don't bother computing a SHA checksum. */ + if (unlikely (off + 32 > compressed_size)) + { + elf_uncompress_failed (); + return 0; + } + off += 32; + break; + + default: + elf_uncompress_failed (); + return 0; + } + + *poffset = off; + + return 1; +} + +/* Uncompress LZMA data found in a minidebug file. The minidebug + format is described at + https://sourceware.org/gdb/current/onlinedocs/gdb/MiniDebugInfo.html. + Returns 0 on error, 1 on successful decompression. For this + function we return 0 on failure to decompress, as the calling code + will carry on in that case. */ + +static int +elf_uncompress_lzma (struct backtrace_state *state, + const unsigned char *compressed, size_t compressed_size, + backtrace_error_callback error_callback, void *data, + unsigned char **uncompressed, size_t *uncompressed_size) +{ + size_t header_size; + size_t footer_size; + unsigned char check; + uint32_t computed_crc; + uint32_t stream_crc; + size_t offset; + size_t index_size; + size_t footer_offset; + size_t index_offset; + uint64_t index_compressed_size; + uint64_t index_uncompressed_size; + unsigned char *mem; + uint16_t *probs; + size_t compressed_block_size; + + /* The format starts with a stream header and ends with a stream + footer. */ + header_size = 12; + footer_size = 12; + if (unlikely (compressed_size < header_size + footer_size)) + { + elf_uncompress_failed (); + return 0; + } + + /* The stream header starts with a magic string. */ + if (unlikely (memcmp (compressed, "\375" "7zXZ\0", 6) != 0)) + { + elf_uncompress_failed (); + return 0; + } + + /* Next come stream flags. The first byte is zero, the second byte + is the check. */ + if (unlikely (compressed[6] != 0)) + { + elf_uncompress_failed (); + return 0; + } + check = compressed[7]; + if (unlikely ((check & 0xf8) != 0)) + { + elf_uncompress_failed (); + return 0; + } + + /* Next comes a CRC of the stream flags. */ + computed_crc = elf_crc32 (0, compressed + 6, 2); + stream_crc = (compressed[8] + | (compressed[9] << 8) + | (compressed[10] << 16) + | (compressed[11] << 24)); + if (unlikely (computed_crc != stream_crc)) + { + elf_uncompress_failed (); + return 0; + } + + /* Now that we've parsed the header, parse the footer, so that we + can get the uncompressed size. */ + + /* The footer ends with two magic bytes. */ + + offset = compressed_size; + if (unlikely (memcmp (compressed + offset - 2, "YZ", 2) != 0)) + { + elf_uncompress_failed (); + return 0; + } + offset -= 2; + + /* Before that are the stream flags, which should be the same as the + flags in the header. */ + if (unlikely (compressed[offset - 2] != 0 + || compressed[offset - 1] != check)) + { + elf_uncompress_failed (); + return 0; + } + offset -= 2; + + /* Before that is the size of the index field, which precedes the + footer. */ + index_size = (compressed[offset - 4] + | (compressed[offset - 3] << 8) + | (compressed[offset - 2] << 16) + | (compressed[offset - 1] << 24)); + index_size = (index_size + 1) * 4; + offset -= 4; + + /* Before that is a footer CRC. */ + computed_crc = elf_crc32 (0, compressed + offset, 6); + stream_crc = (compressed[offset - 4] + | (compressed[offset - 3] << 8) + | (compressed[offset - 2] << 16) + | (compressed[offset - 1] << 24)); + if (unlikely (computed_crc != stream_crc)) + { + elf_uncompress_failed (); + return 0; + } + offset -= 4; + + /* The index comes just before the footer. */ + if (unlikely (offset < index_size + header_size)) + { + elf_uncompress_failed (); + return 0; + } + + footer_offset = offset; + offset -= index_size; + index_offset = offset; + + /* The index starts with a zero byte. */ + if (unlikely (compressed[offset] != 0)) + { + elf_uncompress_failed (); + return 0; + } + ++offset; + + /* Next is the number of blocks. We expect zero blocks for an empty + stream, and otherwise a single block. */ + if (unlikely (compressed[offset] == 0)) + { + *uncompressed = NULL; + *uncompressed_size = 0; + return 1; + } + if (unlikely (compressed[offset] != 1)) + { + elf_uncompress_failed (); + return 0; + } + ++offset; + + /* Next is the compressed size and the uncompressed size. */ + if (!elf_lzma_varint (compressed, compressed_size, &offset, + &index_compressed_size)) + return 0; + if (!elf_lzma_varint (compressed, compressed_size, &offset, + &index_uncompressed_size)) + return 0; + + /* Pad to a four byte boundary. */ + offset = (offset + 3) &~ (size_t) 3; + + /* Next is a CRC of the index. */ + computed_crc = elf_crc32 (0, compressed + index_offset, + offset - index_offset); + stream_crc = (compressed[offset] + | (compressed[offset + 1] << 8) + | (compressed[offset + 2] << 16) + | (compressed[offset + 3] << 24)); + if (unlikely (computed_crc != stream_crc)) + { + elf_uncompress_failed (); + return 0; + } + offset += 4; + + /* We should now be back at the footer. */ + if (unlikely (offset != footer_offset)) + { + elf_uncompress_failed (); + return 0; + } + + /* Allocate space to hold the uncompressed data. If we succeed in + uncompressing the LZMA data, we never free this memory. */ + mem = (unsigned char *) backtrace_alloc (state, index_uncompressed_size, + error_callback, data); + if (unlikely (mem == NULL)) + return 0; + *uncompressed = mem; + *uncompressed_size = index_uncompressed_size; + + /* Allocate space for probabilities. */ + probs = ((uint16_t *) + backtrace_alloc (state, + LZMA_PROB_TOTAL_COUNT * sizeof (uint16_t), + error_callback, data)); + if (unlikely (probs == NULL)) + { + backtrace_free (state, mem, index_uncompressed_size, error_callback, + data); + return 0; + } + + /* Uncompress the block, which follows the header. */ + offset = 12; + if (!elf_uncompress_lzma_block (compressed, compressed_size, check, probs, + mem, index_uncompressed_size, &offset)) + { + backtrace_free (state, mem, index_uncompressed_size, error_callback, + data); + return 0; + } + + compressed_block_size = offset - 12; + if (unlikely (compressed_block_size + != ((index_compressed_size + 3) &~ (size_t) 3))) + { + elf_uncompress_failed (); + backtrace_free (state, mem, index_uncompressed_size, error_callback, + data); + return 0; + } + + offset = (offset + 3) &~ (size_t) 3; + if (unlikely (offset != index_offset)) + { + elf_uncompress_failed (); + backtrace_free (state, mem, index_uncompressed_size, error_callback, + data); + return 0; + } + + return 1; +} + +/* This function is a hook for testing the LZMA support. It is only + used by tests. */ + +int +backtrace_uncompress_lzma (struct backtrace_state *state, + const unsigned char *compressed, + size_t compressed_size, + backtrace_error_callback error_callback, + void *data, unsigned char **uncompressed, + size_t *uncompressed_size) +{ + return elf_uncompress_lzma (state, compressed, compressed_size, + error_callback, data, uncompressed, + uncompressed_size); +} + +/* Add the backtrace data for one ELF file. Returns 1 on success, + 0 on failure (in both cases descriptor is closed) or -1 if exe + is non-zero and the ELF file is ET_DYN, which tells the caller that + elf_add will need to be called on the descriptor again after + base_address is determined. */ + +static int +elf_add (struct backtrace_state *state, const char *filename, int descriptor, + const unsigned char *memory, size_t memory_size, + uintptr_t base_address, backtrace_error_callback error_callback, + void *data, fileline *fileline_fn, int *found_sym, int *found_dwarf, + struct dwarf_data **fileline_entry, int exe, int debuginfo, + const char *with_buildid_data, uint32_t with_buildid_size) +{ + struct elf_view ehdr_view; + b_elf_ehdr ehdr; + off_t shoff; + unsigned int shnum; + unsigned int shstrndx; + struct elf_view shdrs_view; + int shdrs_view_valid; + const b_elf_shdr *shdrs; + const b_elf_shdr *shstrhdr; + size_t shstr_size; + off_t shstr_off; + struct elf_view names_view; + int names_view_valid; + const char *names; + unsigned int symtab_shndx; + unsigned int dynsym_shndx; + unsigned int i; + struct debug_section_info sections[DEBUG_MAX]; + struct debug_section_info zsections[DEBUG_MAX]; + struct elf_view symtab_view; + int symtab_view_valid; + struct elf_view strtab_view; + int strtab_view_valid; + struct elf_view buildid_view; + int buildid_view_valid; + const char *buildid_data; + uint32_t buildid_size; + struct elf_view debuglink_view; + int debuglink_view_valid; + const char *debuglink_name; + uint32_t debuglink_crc; + struct elf_view debugaltlink_view; + int debugaltlink_view_valid; + const char *debugaltlink_name; + const char *debugaltlink_buildid_data; + uint32_t debugaltlink_buildid_size; + struct elf_view gnu_debugdata_view; + int gnu_debugdata_view_valid; + size_t gnu_debugdata_size; + unsigned char *gnu_debugdata_uncompressed; + size_t gnu_debugdata_uncompressed_size; + off_t min_offset; + off_t max_offset; + off_t debug_size; + struct elf_view debug_view; + int debug_view_valid; + unsigned int using_debug_view; + uint16_t *zdebug_table; + struct elf_view split_debug_view[DEBUG_MAX]; + unsigned char split_debug_view_valid[DEBUG_MAX]; + struct elf_ppc64_opd_data opd_data, *opd; + struct dwarf_sections dwarf_sections; + + if (!debuginfo) + { + *found_sym = 0; + *found_dwarf = 0; + } + + shdrs_view_valid = 0; + names_view_valid = 0; + symtab_view_valid = 0; + strtab_view_valid = 0; + buildid_view_valid = 0; + buildid_data = NULL; + buildid_size = 0; + debuglink_view_valid = 0; + debuglink_name = NULL; + debuglink_crc = 0; + debugaltlink_view_valid = 0; + debugaltlink_name = NULL; + debugaltlink_buildid_data = NULL; + debugaltlink_buildid_size = 0; + gnu_debugdata_view_valid = 0; + gnu_debugdata_size = 0; + debug_view_valid = 0; + memset (&split_debug_view_valid[0], 0, sizeof split_debug_view_valid); + opd = NULL; + + if (!elf_get_view (state, descriptor, memory, memory_size, 0, sizeof ehdr, + error_callback, data, &ehdr_view)) + goto fail; + + memcpy (&ehdr, ehdr_view.view.data, sizeof ehdr); + + elf_release_view (state, &ehdr_view, error_callback, data); + + if (ehdr.e_ident[EI_MAG0] != ELFMAG0 + || ehdr.e_ident[EI_MAG1] != ELFMAG1 + || ehdr.e_ident[EI_MAG2] != ELFMAG2 + || ehdr.e_ident[EI_MAG3] != ELFMAG3) + { + error_callback (data, "executable file is not ELF", 0); + goto fail; + } + if (ehdr.e_ident[EI_VERSION] != EV_CURRENT) + { + error_callback (data, "executable file is unrecognized ELF version", 0); + goto fail; + } + +#if BACKTRACE_ELF_SIZE == 32 +#define BACKTRACE_ELFCLASS ELFCLASS32 +#else +#define BACKTRACE_ELFCLASS ELFCLASS64 +#endif + + if (ehdr.e_ident[EI_CLASS] != BACKTRACE_ELFCLASS) + { + error_callback (data, "executable file is unexpected ELF class", 0); + goto fail; + } + + if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB + && ehdr.e_ident[EI_DATA] != ELFDATA2MSB) + { + error_callback (data, "executable file has unknown endianness", 0); + goto fail; + } + + /* If the executable is ET_DYN, it is either a PIE, or we are running + directly a shared library with .interp. We need to wait for + dl_iterate_phdr in that case to determine the actual base_address. */ + if (exe && ehdr.e_type == ET_DYN) + return -1; + + shoff = ehdr.e_shoff; + shnum = ehdr.e_shnum; + shstrndx = ehdr.e_shstrndx; + + if ((shnum == 0 || shstrndx == SHN_XINDEX) + && shoff != 0) + { + struct elf_view shdr_view; + const b_elf_shdr *shdr; + + if (!elf_get_view (state, descriptor, memory, memory_size, shoff, + sizeof shdr, error_callback, data, &shdr_view)) + goto fail; + + shdr = (const b_elf_shdr *) shdr_view.view.data; + + if (shnum == 0) + shnum = shdr->sh_size; + + if (shstrndx == SHN_XINDEX) + { + shstrndx = shdr->sh_link; + + /* Versions of the GNU binutils between 2.12 and 2.18 did + not handle objects with more than SHN_LORESERVE sections + correctly. All large section indexes were offset by + 0x100. There is more information at + http://sourceware.org/bugzilla/show_bug.cgi?id-5900 . + Fortunately these object files are easy to detect, as the + GNU binutils always put the section header string table + near the end of the list of sections. Thus if the + section header string table index is larger than the + number of sections, then we know we have to subtract + 0x100 to get the real section index. */ + if (shstrndx >= shnum && shstrndx >= SHN_LORESERVE + 0x100) + shstrndx -= 0x100; + } + + elf_release_view (state, &shdr_view, error_callback, data); + } + + if (shnum == 0 || shstrndx == 0) + goto fail; + + /* To translate PC to file/line when using DWARF, we need to find + the .debug_info and .debug_line sections. */ + + /* Read the section headers, skipping the first one. */ + + if (!elf_get_view (state, descriptor, memory, memory_size, + shoff + sizeof (b_elf_shdr), + (shnum - 1) * sizeof (b_elf_shdr), + error_callback, data, &shdrs_view)) + goto fail; + shdrs_view_valid = 1; + shdrs = (const b_elf_shdr *) shdrs_view.view.data; + + /* Read the section names. */ + + shstrhdr = &shdrs[shstrndx - 1]; + shstr_size = shstrhdr->sh_size; + shstr_off = shstrhdr->sh_offset; + + if (!elf_get_view (state, descriptor, memory, memory_size, shstr_off, + shstrhdr->sh_size, error_callback, data, &names_view)) + goto fail; + names_view_valid = 1; + names = (const char *) names_view.view.data; + + symtab_shndx = 0; + dynsym_shndx = 0; + + memset (sections, 0, sizeof sections); + memset (zsections, 0, sizeof zsections); + + /* Look for the symbol table. */ + for (i = 1; i < shnum; ++i) + { + const b_elf_shdr *shdr; + unsigned int sh_name; + const char *name; + int j; + + shdr = &shdrs[i - 1]; + + if (shdr->sh_type == SHT_SYMTAB) + symtab_shndx = i; + else if (shdr->sh_type == SHT_DYNSYM) + dynsym_shndx = i; + + sh_name = shdr->sh_name; + if (sh_name >= shstr_size) + { + error_callback (data, "ELF section name out of range", 0); + goto fail; } name = names + sh_name; for (j = 0; j < (int) DEBUG_MAX; ++j) { - if (strcmp (name, debug_section_names[j]) == 0) + if (strcmp (name, dwarf_section_names[j]) == 0) { sections[j].offset = shdr->sh_offset; sections[j].size = shdr->sh_size; @@ -2850,30 +4226,52 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, } } + if (name[0] == '.' && name[1] == 'z') + { + for (j = 0; j < (int) DEBUG_MAX; ++j) + { + if (strcmp (name + 2, dwarf_section_names[j] + 1) == 0) + { + zsections[j].offset = shdr->sh_offset; + zsections[j].size = shdr->sh_size; + break; + } + } + } + /* Read the build ID if present. This could check for any SHT_NOTE section with the right note name and type, but gdb looks for a specific section name. */ - if (!debuginfo + if ((!debuginfo || with_buildid_data != NULL) && !buildid_view_valid && strcmp (name, ".note.gnu.build-id") == 0) { const b_elf_note *note; - if (!backtrace_get_view (state, descriptor, shdr->sh_offset, - shdr->sh_size, error_callback, data, - &buildid_view)) + if (!elf_get_view (state, descriptor, memory, memory_size, + shdr->sh_offset, shdr->sh_size, error_callback, + data, &buildid_view)) goto fail; buildid_view_valid = 1; - note = (const b_elf_note *) buildid_view.data; + note = (const b_elf_note *) buildid_view.view.data; if (note->type == NT_GNU_BUILD_ID && note->namesz == 4 && strncmp (note->name, "GNU", 4) == 0 - && shdr->sh_size < 12 + ((note->namesz + 3) & ~ 3) + note->descsz) + && shdr->sh_size <= 12 + ((note->namesz + 3) & ~ 3) + note->descsz) { buildid_data = ¬e->name[0] + ((note->namesz + 3) & ~ 3); buildid_size = note->descsz; } + + if (with_buildid_size != 0) + { + if (buildid_size != with_buildid_size) + goto fail; + + if (memcmp (buildid_data, with_buildid_data, buildid_size) != 0) + goto fail; + } } /* Read the debuglink file if present. */ @@ -2884,13 +4282,13 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, const char *debuglink_data; size_t crc_offset; - if (!backtrace_get_view (state, descriptor, shdr->sh_offset, - shdr->sh_size, error_callback, data, - &debuglink_view)) + if (!elf_get_view (state, descriptor, memory, memory_size, + shdr->sh_offset, shdr->sh_size, error_callback, + data, &debuglink_view)) goto fail; debuglink_view_valid = 1; - debuglink_data = (const char *) debuglink_view.data; + debuglink_data = (const char *) debuglink_view.view.data; crc_offset = strnlen (debuglink_data, shdr->sh_size); crc_offset = (crc_offset + 3) & ~3; if (crc_offset + 4 <= shdr->sh_size) @@ -2900,20 +4298,58 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, } } + if (!debugaltlink_view_valid + && strcmp (name, ".gnu_debugaltlink") == 0) + { + const char *debugaltlink_data; + size_t debugaltlink_name_len; + + if (!elf_get_view (state, descriptor, memory, memory_size, + shdr->sh_offset, shdr->sh_size, error_callback, + data, &debugaltlink_view)) + goto fail; + + debugaltlink_view_valid = 1; + debugaltlink_data = (const char *) debugaltlink_view.view.data; + debugaltlink_name = debugaltlink_data; + debugaltlink_name_len = strnlen (debugaltlink_data, shdr->sh_size); + if (debugaltlink_name_len < shdr->sh_size) + { + /* Include terminating zero. */ + debugaltlink_name_len += 1; + + debugaltlink_buildid_data + = debugaltlink_data + debugaltlink_name_len; + debugaltlink_buildid_size = shdr->sh_size - debugaltlink_name_len; + } + } + + if (!gnu_debugdata_view_valid + && strcmp (name, ".gnu_debugdata") == 0) + { + if (!elf_get_view (state, descriptor, memory, memory_size, + shdr->sh_offset, shdr->sh_size, error_callback, + data, &gnu_debugdata_view)) + goto fail; + + gnu_debugdata_size = shdr->sh_size; + gnu_debugdata_view_valid = 1; + } + /* Read the .opd section on PowerPC64 ELFv1. */ if (ehdr.e_machine == EM_PPC64 && (ehdr.e_flags & EF_PPC64_ABI) < 2 && shdr->sh_type == SHT_PROGBITS && strcmp (name, ".opd") == 0) { - if (!backtrace_get_view (state, descriptor, shdr->sh_offset, - shdr->sh_size, error_callback, data, - &opd_data.view)) + if (!elf_get_view (state, descriptor, memory, memory_size, + shdr->sh_offset, shdr->sh_size, error_callback, + data, &opd_data.view)) goto fail; opd = &opd_data; opd->addr = shdr->sh_addr; - opd->data = (const char *) opd_data.view.data; + opd->data = (const char *) opd_data.view.view.data; opd->size = shdr->sh_size; } } @@ -2937,15 +4373,15 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, } strtab_shdr = &shdrs[strtab_shndx - 1]; - if (!backtrace_get_view (state, descriptor, symtab_shdr->sh_offset, - symtab_shdr->sh_size, error_callback, data, - &symtab_view)) + if (!elf_get_view (state, descriptor, memory, memory_size, + symtab_shdr->sh_offset, symtab_shdr->sh_size, + error_callback, data, &symtab_view)) goto fail; symtab_view_valid = 1; - if (!backtrace_get_view (state, descriptor, strtab_shdr->sh_offset, - strtab_shdr->sh_size, error_callback, data, - &strtab_view)) + if (!elf_get_view (state, descriptor, memory, memory_size, + strtab_shdr->sh_offset, strtab_shdr->sh_size, + error_callback, data, &strtab_view)) goto fail; strtab_view_valid = 1; @@ -2955,8 +4391,8 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, goto fail; if (!elf_initialize_syminfo (state, base_address, - symtab_view.data, symtab_shdr->sh_size, - strtab_view.data, strtab_shdr->sh_size, + symtab_view.view.data, symtab_shdr->sh_size, + strtab_view.view.data, strtab_shdr->sh_size, error_callback, data, sdata, opd)) { backtrace_free (state, sdata, sizeof *sdata, error_callback, data); @@ -2965,17 +4401,18 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, /* We no longer need the symbol table, but we hold on to the string table permanently. */ - backtrace_release_view (state, &symtab_view, error_callback, data); + elf_release_view (state, &symtab_view, error_callback, data); symtab_view_valid = 0; + strtab_view_valid = 0; *found_sym = 1; elf_add_syminfo_data (state, sdata); } - backtrace_release_view (state, &shdrs_view, error_callback, data); + elf_release_view (state, &shdrs_view, error_callback, data); shdrs_view_valid = 0; - backtrace_release_view (state, &names_view, error_callback, data); + elf_release_view (state, &names_view, error_callback, data); names_view_valid = 0; /* If the debug info is in a separate file, read that one instead. */ @@ -2990,15 +4427,17 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, { int ret; - backtrace_release_view (state, &buildid_view, error_callback, data); + elf_release_view (state, &buildid_view, error_callback, data); if (debuglink_view_valid) - backtrace_release_view (state, &debuglink_view, error_callback, - data); - ret = elf_add (state, NULL, d, base_address, error_callback, data, - fileline_fn, found_sym, found_dwarf, 0, 1); + elf_release_view (state, &debuglink_view, error_callback, data); + if (debugaltlink_view_valid) + elf_release_view (state, &debugaltlink_view, error_callback, data); + ret = elf_add (state, "", d, NULL, 0, base_address, error_callback, + data, fileline_fn, found_sym, found_dwarf, NULL, 0, + 1, NULL, 0); if (ret < 0) backtrace_close (d, error_callback, data); - else + else if (descriptor >= 0) backtrace_close (descriptor, error_callback, data); return ret; } @@ -3006,13 +4445,13 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, if (buildid_view_valid) { - backtrace_release_view (state, &buildid_view, error_callback, data); + elf_release_view (state, &buildid_view, error_callback, data); buildid_view_valid = 0; } if (opd) { - backtrace_release_view (state, &opd->view, error_callback, data); + elf_release_view (state, &opd->view, error_callback, data); opd = NULL; } @@ -3027,13 +4466,15 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, { int ret; - backtrace_release_view (state, &debuglink_view, error_callback, - data); - ret = elf_add (state, NULL, d, base_address, error_callback, data, - fileline_fn, found_sym, found_dwarf, 0, 1); + elf_release_view (state, &debuglink_view, error_callback, data); + if (debugaltlink_view_valid) + elf_release_view (state, &debugaltlink_view, error_callback, data); + ret = elf_add (state, "", d, NULL, 0, base_address, error_callback, + data, fileline_fn, found_sym, found_dwarf, NULL, 0, + 1, NULL, 0); if (ret < 0) backtrace_close (d, error_callback, data); - else + else if (descriptor >= 0) backtrace_close(descriptor, error_callback, data); return ret; } @@ -3041,68 +4482,184 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, if (debuglink_view_valid) { - backtrace_release_view (state, &debuglink_view, error_callback, data); + elf_release_view (state, &debuglink_view, error_callback, data); debuglink_view_valid = 0; } + struct dwarf_data *fileline_altlink = NULL; + if (debugaltlink_name != NULL) + { + int d; + + d = elf_open_debugfile_by_debuglink (state, filename, debugaltlink_name, + 0, error_callback, data); + if (d >= 0) + { + int ret; + + ret = elf_add (state, filename, d, NULL, 0, base_address, + error_callback, data, fileline_fn, found_sym, + found_dwarf, &fileline_altlink, 0, 1, + debugaltlink_buildid_data, debugaltlink_buildid_size); + elf_release_view (state, &debugaltlink_view, error_callback, data); + debugaltlink_view_valid = 0; + if (ret < 0) + { + backtrace_close (d, error_callback, data); + return ret; + } + } + } + + if (debugaltlink_view_valid) + { + elf_release_view (state, &debugaltlink_view, error_callback, data); + debugaltlink_view_valid = 0; + } + + if (gnu_debugdata_view_valid) + { + int ret; + + ret = elf_uncompress_lzma (state, + ((const unsigned char *) + gnu_debugdata_view.view.data), + gnu_debugdata_size, error_callback, data, + &gnu_debugdata_uncompressed, + &gnu_debugdata_uncompressed_size); + + elf_release_view (state, &gnu_debugdata_view, error_callback, data); + gnu_debugdata_view_valid = 0; + + if (ret) + { + ret = elf_add (state, filename, -1, gnu_debugdata_uncompressed, + gnu_debugdata_uncompressed_size, base_address, + error_callback, data, fileline_fn, found_sym, + found_dwarf, NULL, 0, 0, NULL, 0); + if (ret >= 0 && descriptor >= 0) + backtrace_close(descriptor, error_callback, data); + return ret; + } + } + /* Read all the debug sections in a single view, since they are - probably adjacent in the file. We never release this view. */ + probably adjacent in the file. If any of sections are + uncompressed, we never release this view. */ min_offset = 0; max_offset = 0; + debug_size = 0; for (i = 0; i < (int) DEBUG_MAX; ++i) { off_t end; - if (sections[i].size == 0) - continue; - if (min_offset == 0 || sections[i].offset < min_offset) - min_offset = sections[i].offset; - end = sections[i].offset + sections[i].size; - if (end > max_offset) - max_offset = end; + if (sections[i].size != 0) + { + if (min_offset == 0 || sections[i].offset < min_offset) + min_offset = sections[i].offset; + end = sections[i].offset + sections[i].size; + if (end > max_offset) + max_offset = end; + debug_size += sections[i].size; + } + if (zsections[i].size != 0) + { + if (min_offset == 0 || zsections[i].offset < min_offset) + min_offset = zsections[i].offset; + end = zsections[i].offset + zsections[i].size; + if (end > max_offset) + max_offset = end; + debug_size += zsections[i].size; + } } if (min_offset == 0 || max_offset == 0) { - if (!backtrace_close (descriptor, error_callback, data)) - goto fail; + if (descriptor >= 0) + { + if (!backtrace_close (descriptor, error_callback, data)) + goto fail; + } return 1; } - if (!backtrace_get_view (state, descriptor, min_offset, - max_offset - min_offset, - error_callback, data, &debug_view)) - goto fail; - debug_view_valid = 1; + /* If the total debug section size is large, assume that there are + gaps between the sections, and read them individually. */ + + if (max_offset - min_offset < 0x20000000 + || max_offset - min_offset < debug_size + 0x10000) + { + if (!elf_get_view (state, descriptor, memory, memory_size, min_offset, + max_offset - min_offset, error_callback, data, + &debug_view)) + goto fail; + debug_view_valid = 1; + } + else + { + memset (&split_debug_view[0], 0, sizeof split_debug_view); + for (i = 0; i < (int) DEBUG_MAX; ++i) + { + struct debug_section_info *dsec; + + if (sections[i].size != 0) + dsec = §ions[i]; + else if (zsections[i].size != 0) + dsec = &zsections[i]; + else + continue; + + if (!elf_get_view (state, descriptor, memory, memory_size, + dsec->offset, dsec->size, error_callback, data, + &split_debug_view[i])) + goto fail; + split_debug_view_valid[i] = 1; + + if (sections[i].size != 0) + sections[i].data = ((const unsigned char *) + split_debug_view[i].view.data); + else + zsections[i].data = ((const unsigned char *) + split_debug_view[i].view.data); + } + } /* We've read all we need from the executable. */ - if (!backtrace_close (descriptor, error_callback, data)) - goto fail; - descriptor = -1; + if (descriptor >= 0) + { + if (!backtrace_close (descriptor, error_callback, data)) + goto fail; + descriptor = -1; + } using_debug_view = 0; - for (i = 0; i < (int) DEBUG_MAX; ++i) + if (debug_view_valid) { - if (sections[i].size == 0) - sections[i].data = NULL; - else + for (i = 0; i < (int) DEBUG_MAX; ++i) { - sections[i].data = ((const unsigned char *) debug_view.data - + (sections[i].offset - min_offset)); - if (i < ZDEBUG_INFO) - ++using_debug_view; + if (sections[i].size == 0) + sections[i].data = NULL; + else + { + sections[i].data = ((const unsigned char *) debug_view.view.data + + (sections[i].offset - min_offset)); + ++using_debug_view; + } + + if (zsections[i].size == 0) + zsections[i].data = NULL; + else + zsections[i].data = ((const unsigned char *) debug_view.view.data + + (zsections[i].offset - min_offset)); } } /* Uncompress the old format (--compress-debug-sections=zlib-gnu). */ zdebug_table = NULL; - for (i = 0; i < ZDEBUG_INFO; ++i) + for (i = 0; i < (int) DEBUG_MAX; ++i) { - struct debug_section_info *pz; - - pz = §ions[i + ZDEBUG_INFO - DEBUG_INFO]; - if (sections[i].size == 0 && pz->size > 0) + if (sections[i].size == 0 && zsections[i].size > 0) { unsigned char *uncompressed_data; size_t uncompressed_size; @@ -3118,19 +4675,27 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, uncompressed_data = NULL; uncompressed_size = 0; - if (!elf_uncompress_zdebug (state, pz->data, pz->size, zdebug_table, + if (!elf_uncompress_zdebug (state, zsections[i].data, + zsections[i].size, zdebug_table, error_callback, data, &uncompressed_data, &uncompressed_size)) goto fail; sections[i].data = uncompressed_data; sections[i].size = uncompressed_size; sections[i].compressed = 0; + + if (split_debug_view_valid[i]) + { + elf_release_view (state, &split_debug_view[i], + error_callback, data); + split_debug_view_valid[i] = 0; + } } } /* Uncompress the official ELF format (--compress-debug-sections=zlib-gabi). */ - for (i = 0; i < ZDEBUG_INFO; ++i) + for (i = 0; i < (int) DEBUG_MAX; ++i) { unsigned char *uncompressed_data; size_t uncompressed_size; @@ -3157,7 +4722,13 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, sections[i].size = uncompressed_size; sections[i].compressed = 0; - --using_debug_view; + if (debug_view_valid) + --using_debug_view; + else if (split_debug_view_valid[i]) + { + elf_release_view (state, &split_debug_view[i], error_callback, data); + split_debug_view_valid[i] = 0; + } } if (zdebug_table != NULL) @@ -3166,23 +4737,21 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, if (debug_view_valid && using_debug_view == 0) { - backtrace_release_view (state, &debug_view, error_callback, data); + elf_release_view (state, &debug_view, error_callback, data); debug_view_valid = 0; } - if (!backtrace_dwarf_add (state, base_address, - sections[DEBUG_INFO].data, - sections[DEBUG_INFO].size, - sections[DEBUG_LINE].data, - sections[DEBUG_LINE].size, - sections[DEBUG_ABBREV].data, - sections[DEBUG_ABBREV].size, - sections[DEBUG_RANGES].data, - sections[DEBUG_RANGES].size, - sections[DEBUG_STR].data, - sections[DEBUG_STR].size, + for (i = 0; i < (int) DEBUG_MAX; ++i) + { + dwarf_sections.data[i] = sections[i].data; + dwarf_sections.size[i] = sections[i].size; + } + + if (!backtrace_dwarf_add (state, base_address, &dwarf_sections, ehdr.e_ident[EI_DATA] == ELFDATA2MSB, - error_callback, data, fileline_fn)) + fileline_altlink, + error_callback, data, fileline_fn, + fileline_entry)) goto fail; *found_dwarf = 1; @@ -3191,22 +4760,31 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor, fail: if (shdrs_view_valid) - backtrace_release_view (state, &shdrs_view, error_callback, data); + elf_release_view (state, &shdrs_view, error_callback, data); if (names_view_valid) - backtrace_release_view (state, &names_view, error_callback, data); + elf_release_view (state, &names_view, error_callback, data); if (symtab_view_valid) - backtrace_release_view (state, &symtab_view, error_callback, data); + elf_release_view (state, &symtab_view, error_callback, data); if (strtab_view_valid) - backtrace_release_view (state, &strtab_view, error_callback, data); + elf_release_view (state, &strtab_view, error_callback, data); if (debuglink_view_valid) - backtrace_release_view (state, &debuglink_view, error_callback, data); + elf_release_view (state, &debuglink_view, error_callback, data); + if (debugaltlink_view_valid) + elf_release_view (state, &debugaltlink_view, error_callback, data); + if (gnu_debugdata_view_valid) + elf_release_view (state, &gnu_debugdata_view, error_callback, data); if (buildid_view_valid) - backtrace_release_view (state, &buildid_view, error_callback, data); + elf_release_view (state, &buildid_view, error_callback, data); if (debug_view_valid) - backtrace_release_view (state, &debug_view, error_callback, data); + elf_release_view (state, &debug_view, error_callback, data); + for (i = 0; i < (int) DEBUG_MAX; ++i) + { + if (split_debug_view_valid[i]) + elf_release_view (state, &split_debug_view[i], error_callback, data); + } if (opd) - backtrace_release_view (state, &opd->view, error_callback, data); - if (descriptor != -1) + elf_release_view (state, &opd->view, error_callback, data); + if (descriptor >= 0) backtrace_close (descriptor, error_callback, data); return 0; } @@ -3268,9 +4846,9 @@ phdr_callback (struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED, return 0; } - if (elf_add (pd->state, filename, descriptor, info->dlpi_addr, + if (elf_add (pd->state, filename, descriptor, NULL, 0, info->dlpi_addr, pd->error_callback, pd->data, &elf_fileline_fn, pd->found_sym, - &found_dwarf, 0, 0)) + &found_dwarf, NULL, 0, 0, NULL, 0)) { if (found_dwarf) { @@ -3297,8 +4875,9 @@ backtrace_initialize (struct backtrace_state *state, const char *filename, fileline elf_fileline_fn = elf_nodebug; struct phdr_data pd; - ret = elf_add (state, filename, descriptor, 0, error_callback, data, - &elf_fileline_fn, &found_sym, &found_dwarf, 1, 0); + ret = elf_add (state, filename, descriptor, NULL, 0, 0, error_callback, data, + &elf_fileline_fn, &found_sym, &found_dwarf, NULL, 1, 0, NULL, + 0); if (!ret) return 0; diff --git a/Sources/CBacktrace/fileline.c b/Sources/CBacktrace/fileline.c index 7f9e6e5..be813e6 100644 --- a/Sources/CBacktrace/fileline.c +++ b/Sources/CBacktrace/fileline.c @@ -1,6 +1,6 @@ #ifdef __linux__ /* fileline.c -- Get file and line number information in a backtrace. - Copyright (C) 2012-2018 Free Software Foundation, Inc. + Copyright (C) 2012-2021 Free Software Foundation, Inc. Written by Ian Lance Taylor, Google. Redistribution and use in source and binary forms, with or without @@ -40,6 +40,14 @@ POSSIBILITY OF SUCH DAMAGE. */ #include #include +#if defined (HAVE_KERN_PROC_ARGS) || defined (HAVE_KERN_PROC) +#include +#endif + +#ifdef HAVE_MACH_O_DYLD_H +#include +#endif + #include "include/backtrace.h" #include "internal.h" @@ -47,6 +55,107 @@ POSSIBILITY OF SUCH DAMAGE. */ #define getexecname() NULL #endif +#if !defined (HAVE_KERN_PROC_ARGS) && !defined (HAVE_KERN_PROC) + +#define sysctl_exec_name1(state, error_callback, data) NULL +#define sysctl_exec_name2(state, error_callback, data) NULL + +#else /* defined (HAVE_KERN_PROC_ARGS) || |defined (HAVE_KERN_PROC) */ + +static char * +sysctl_exec_name (struct backtrace_state *state, + int mib0, int mib1, int mib2, int mib3, + backtrace_error_callback error_callback, void *data) +{ + int mib[4]; + size_t len; + char *name; + size_t rlen; + + mib[0] = mib0; + mib[1] = mib1; + mib[2] = mib2; + mib[3] = mib3; + + if (sysctl (mib, 4, NULL, &len, NULL, 0) < 0) + return NULL; + name = (char *) backtrace_alloc (state, len, error_callback, data); + if (name == NULL) + return NULL; + rlen = len; + if (sysctl (mib, 4, name, &rlen, NULL, 0) < 0) + { + backtrace_free (state, name, len, error_callback, data); + return NULL; + } + return name; +} + +#ifdef HAVE_KERN_PROC_ARGS + +static char * +sysctl_exec_name1 (struct backtrace_state *state, + backtrace_error_callback error_callback, void *data) +{ + /* This variant is used on NetBSD. */ + return sysctl_exec_name (state, CTL_KERN, KERN_PROC_ARGS, -1, + KERN_PROC_PATHNAME, error_callback, data); +} + +#else + +#define sysctl_exec_name1(state, error_callback, data) NULL + +#endif + +#ifdef HAVE_KERN_PROC + +static char * +sysctl_exec_name2 (struct backtrace_state *state, + backtrace_error_callback error_callback, void *data) +{ + /* This variant is used on FreeBSD. */ + return sysctl_exec_name (state, CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1, + error_callback, data); +} + +#else + +#define sysctl_exec_name2(state, error_callback, data) NULL + +#endif + +#endif /* defined (HAVE_KERN_PROC_ARGS) || |defined (HAVE_KERN_PROC) */ + +#ifdef HAVE_MACH_O_DYLD_H + +static char * +macho_get_executable_path (struct backtrace_state *state, + backtrace_error_callback error_callback, void *data) +{ + uint32_t len; + char *name; + + len = 0; + if (_NSGetExecutablePath (NULL, &len) == 0) + return NULL; + name = (char *) backtrace_alloc (state, len, error_callback, data); + if (name == NULL) + return NULL; + if (_NSGetExecutablePath (name, &len) != 0) + { + backtrace_free (state, name, len, error_callback, data); + return NULL; + } + return name; +} + +#else /* !defined (HAVE_MACH_O_DYLD_H) */ + +#define macho_get_executable_path(state, error_callback, data) NULL + +#endif /* !defined (HAVE_MACH_O_DYLD_H) */ + /* Initialize the fileline information from the executable. Returns 1 on success, 0 on failure. */ @@ -84,7 +193,7 @@ fileline_initialize (struct backtrace_state *state, descriptor = -1; called_error_callback = 0; - for (pass = 0; pass < 5; ++pass) + for (pass = 0; pass < 8; ++pass) { int does_not_exist; @@ -94,19 +203,28 @@ fileline_initialize (struct backtrace_state *state, filename = state->filename; break; case 1: - filename = "/proc/self/exe"; + filename = getexecname (); break; case 2: - filename = "/proc/curproc/file"; + filename = "/proc/self/exe"; break; case 3: - filename = getexecname (); + filename = "/proc/curproc/file"; break; case 4: snprintf (buf, sizeof (buf), "/proc/%ld/object/a.out", (long) getpid ()); filename = buf; break; + case 5: + filename = sysctl_exec_name1 (state, error_callback, data); + break; + case 6: + filename = sysctl_exec_name2 (state, error_callback, data); + break; + case 7: + filename = macho_get_executable_path (state, error_callback, data); + break; default: abort (); } @@ -200,4 +318,31 @@ backtrace_syminfo (struct backtrace_state *state, uintptr_t pc, state->syminfo_fn (state, pc, callback, error_callback, data); return 1; } + +/* A backtrace_syminfo_callback that can call into a + backtrace_full_callback, used when we have a symbol table but no + debug info. */ + +void +backtrace_syminfo_to_full_callback (void *data, uintptr_t pc, + const char *symname, + uintptr_t symval ATTRIBUTE_UNUSED, + uintptr_t symsize ATTRIBUTE_UNUSED) +{ + struct backtrace_call_full *bdata = (struct backtrace_call_full *) data; + + bdata->ret = bdata->full_callback (bdata->full_data, pc, NULL, 0, symname); +} + +/* An error callback that corresponds to + backtrace_syminfo_to_full_callback. */ + +void +backtrace_syminfo_to_full_error_callback (void *data, const char *msg, + int errnum) +{ + struct backtrace_call_full *bdata = (struct backtrace_call_full *) data; + + bdata->full_error_callback (bdata->full_data, msg, errnum); +} #endif diff --git a/Sources/CBacktrace/filenames.h b/Sources/CBacktrace/filenames.h index 8837343..0f7df4d 100644 --- a/Sources/CBacktrace/filenames.h +++ b/Sources/CBacktrace/filenames.h @@ -45,7 +45,10 @@ POSSIBILITY OF SUCH DAMAGE. */ #if defined(__MSDOS__) || defined(_WIN32) || defined(__OS2__) || defined (__CYGWIN__) # define IS_DIR_SEPARATOR(c) ((c) == '/' || (c) == '\\') +# define HAS_DRIVE_SPEC(f) ((f)[0] != '\0' && (f)[1] == ':') +# define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR((f)[0]) || HAS_DRIVE_SPEC(f)) #else # define IS_DIR_SEPARATOR(c) ((c) == '/') +# define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR((f)[0])) #endif #endif diff --git a/Sources/CBacktrace/include/backtrace.h b/Sources/CBacktrace/include/backtrace.h index b6fe505..a3bbc25 100644 --- a/Sources/CBacktrace/include/backtrace.h +++ b/Sources/CBacktrace/include/backtrace.h @@ -1,6 +1,6 @@ #ifdef __linux__ /* backtrace.h -- Public header file for stack backtrace library. - Copyright (C) 2012-2018 Free Software Foundation, Inc. + Copyright (C) 2012-2021 Free Software Foundation, Inc. Written by Ian Lance Taylor, Google. Redistribution and use in source and binary forms, with or without @@ -55,13 +55,14 @@ struct backtrace_state; invalid after this function returns. As a special case, the ERRNUM argument will be passed as -1 if no - debug info can be found for the executable, but the function - requires debug info (e.g., backtrace_full, backtrace_pcinfo). The - MSG in this case will be something along the lines of "no debug - info". Similarly, ERRNUM will be passed as -1 if there is no - symbol table, but the function requires a symbol table (e.g., - backtrace_syminfo). This may be used as a signal that some other - approach should be tried. */ + debug info can be found for the executable, or if the debug info + exists but has an unsupported version, but the function requires + debug info (e.g., backtrace_full, backtrace_pcinfo). The MSG in + this case will be something along the lines of "no debug info". + Similarly, ERRNUM will be passed as -1 if there is no symbol table, + but the function requires a symbol table (e.g., backtrace_syminfo). + This may be used as a signal that some other approach should be + tried. */ typedef void (*backtrace_error_callback) (void *data, const char *msg, int errnum); @@ -76,7 +77,13 @@ typedef void (*backtrace_error_callback) (void *data, const char *msg, use appropriate atomic operations. If THREADED is zero the state may only be accessed by one thread at a time. This returns a state pointer on success, NULL on error. If an error occurs, this will - call the ERROR_CALLBACK routine. */ + call the ERROR_CALLBACK routine. + + Calling this function allocates resources that cannot be freed. + There is no backtrace_free_state function. The state is used to + cache information that is expensive to recompute. Programs are + expected to call this function at most once and to save the return + value for all later calls to backtrace functions. */ extern struct backtrace_state *backtrace_create_state ( const char *filename, int threaded, diff --git a/Sources/CBacktrace/internal.h b/Sources/CBacktrace/internal.h index cbf2544..7e94ec4 100644 --- a/Sources/CBacktrace/internal.h +++ b/Sources/CBacktrace/internal.h @@ -1,6 +1,6 @@ #ifdef __linux__ /* internal.h -- Internal header file for stack backtrace library. - Copyright (C) 2012-2018 Free Software Foundation, Inc. + Copyright (C) 2012-2021 Free Software Foundation, Inc. Written by Ian Lance Taylor, Google. Redistribution and use in source and binary forms, with or without @@ -57,6 +57,14 @@ POSSIBILITY OF SUCH DAMAGE. */ # endif #endif +#ifndef ATTRIBUTE_FALLTHROUGH +# if (GCC_VERSION >= 7000) +# define ATTRIBUTE_FALLTHROUGH __attribute__ ((__fallthrough__)) +# else +# define ATTRIBUTE_FALLTHROUGH +# endif +#endif + #ifndef HAVE_SYNC_FUNCTIONS /* Define out the sync functions. These should never be called if @@ -180,7 +188,7 @@ struct backtrace_view /* Create a view of SIZE bytes from DESCRIPTOR at OFFSET. Store the result in *VIEW. Returns 1 on success, 0 on error. */ extern int backtrace_get_view (struct backtrace_state *state, int descriptor, - off_t offset, size_t size, + off_t offset, uint64_t size, backtrace_error_callback error_callback, void *data, struct backtrace_view *view); @@ -258,6 +266,18 @@ extern int backtrace_vector_release (struct backtrace_state *state, backtrace_error_callback error_callback, void *data); +/* Free the space managed by VEC. This will reset VEC. */ + +static inline void +backtrace_vector_free (struct backtrace_state *state, + struct backtrace_vector *vec, + backtrace_error_callback error_callback, void *data) +{ + vec->alc += vec->size; + vec->size = 0; + backtrace_vector_release (state, vec, error_callback, data); +} + /* Read initial debug data from a descriptor, and set the fileline_data, syminfo_fn, and syminfo_data fields of STATE. Return the fileln_fn field in *FILELN_FN--this is done this way so @@ -275,23 +295,70 @@ extern int backtrace_initialize (struct backtrace_state *state, void *data, fileline *fileline_fn); +/* An enum for the DWARF sections we care about. */ + +enum dwarf_section +{ + DEBUG_INFO, + DEBUG_LINE, + DEBUG_ABBREV, + DEBUG_RANGES, + DEBUG_STR, + DEBUG_ADDR, + DEBUG_STR_OFFSETS, + DEBUG_LINE_STR, + DEBUG_RNGLISTS, + + DEBUG_MAX +}; + +/* Data for the DWARF sections we care about. */ + +struct dwarf_sections +{ + const unsigned char *data[DEBUG_MAX]; + size_t size[DEBUG_MAX]; +}; + +/* DWARF data read from a file, used for .gnu_debugaltlink. */ + +struct dwarf_data; + /* Add file/line information for a DWARF module. */ extern int backtrace_dwarf_add (struct backtrace_state *state, uintptr_t base_address, - const unsigned char* dwarf_info, - size_t dwarf_info_size, - const unsigned char *dwarf_line, - size_t dwarf_line_size, - const unsigned char *dwarf_abbrev, - size_t dwarf_abbrev_size, - const unsigned char *dwarf_ranges, - size_t dwarf_range_size, - const unsigned char *dwarf_str, - size_t dwarf_str_size, + const struct dwarf_sections *dwarf_sections, int is_bigendian, + struct dwarf_data *fileline_altlink, backtrace_error_callback error_callback, - void *data, fileline *fileline_fn); + void *data, fileline *fileline_fn, + struct dwarf_data **fileline_entry); + +/* A data structure to pass to backtrace_syminfo_to_full. */ + +struct backtrace_call_full +{ + backtrace_full_callback full_callback; + backtrace_error_callback full_error_callback; + void *full_data; + int ret; +}; + +/* A backtrace_syminfo_callback that can call into a + backtrace_full_callback, used when we have a symbol table but no + debug info. */ + +extern void backtrace_syminfo_to_full_callback (void *data, uintptr_t pc, + const char *symname, + uintptr_t symval, + uintptr_t symsize); + +/* An error callback that corresponds to + backtrace_syminfo_to_full_callback. */ + +extern void backtrace_syminfo_to_full_error_callback (void *, const char *, + int); /* A test-only hook for elf_uncompress_zdebug. */ @@ -302,5 +369,14 @@ extern int backtrace_uncompress_zdebug (struct backtrace_state *, unsigned char **uncompressed, size_t *uncompressed_size); +/* A test-only hook for elf_uncompress_lzma. */ + +extern int backtrace_uncompress_lzma (struct backtrace_state *, + const unsigned char *compressed, + size_t compressed_size, + backtrace_error_callback, void *data, + unsigned char **uncompressed, + size_t *uncompressed_size); + #endif #endif diff --git a/Sources/CBacktrace/mmap.c b/Sources/CBacktrace/mmap.c index 320f0a1..99a1283 100644 --- a/Sources/CBacktrace/mmap.c +++ b/Sources/CBacktrace/mmap.c @@ -1,6 +1,6 @@ #ifdef __linux__ /* mmap.c -- Memory allocation with mmap. - Copyright (C) 2012-2018 Free Software Foundation, Inc. + Copyright (C) 2012-2021 Free Software Foundation, Inc. Written by Ian Lance Taylor, Google. Redistribution and use in source and binary forms, with or without @@ -43,6 +43,10 @@ POSSIBILITY OF SUCH DAMAGE. */ #include "include/backtrace.h" #include "internal.h" +#ifndef HAVE_DECL_GETPAGESIZE +extern int getpagesize (void); +#endif + /* Memory allocation on systems that provide anonymous mmap. This permits the backtrace functions to be invoked from a signal handler, assuming that mmap is async-signal safe. */ @@ -170,7 +174,7 @@ backtrace_alloc (struct backtrace_state *state, if (page == MAP_FAILED) { if (error_callback) - error_callback (data, "mmap for alloc", errno); + error_callback (data, "mmap", errno); } else { @@ -322,6 +326,8 @@ backtrace_vector_release (struct backtrace_state *state, backtrace_free (state, (char *) vec->base + aligned, alc, error_callback, data); vec->alc = 0; + if (vec->size == 0) + vec->base = NULL; return 1; } #endif diff --git a/Sources/CBacktrace/mmapio.c b/Sources/CBacktrace/mmapio.c index 2f84cd7..b2c83c3 100644 --- a/Sources/CBacktrace/mmapio.c +++ b/Sources/CBacktrace/mmapio.c @@ -1,6 +1,6 @@ #ifdef __linux__ /* mmapio.c -- File views using mmap. - Copyright (C) 2012-2018 Free Software Foundation, Inc. + Copyright (C) 2012-2021 Free Software Foundation, Inc. Written by Ian Lance Taylor, Google. Redistribution and use in source and binary forms, with or without @@ -41,6 +41,10 @@ POSSIBILITY OF SUCH DAMAGE. */ #include "include/backtrace.h" #include "internal.h" +#ifndef HAVE_DECL_GETPAGESIZE +extern int getpagesize (void); +#endif + #ifndef MAP_FAILED #define MAP_FAILED ((void *)-1) #endif @@ -52,7 +56,7 @@ POSSIBILITY OF SUCH DAMAGE. */ int backtrace_get_view (struct backtrace_state *state ATTRIBUTE_UNUSED, - int descriptor, off_t offset, size_t size, + int descriptor, off_t offset, uint64_t size, backtrace_error_callback error_callback, void *data, struct backtrace_view *view) { @@ -61,6 +65,12 @@ backtrace_get_view (struct backtrace_state *state ATTRIBUTE_UNUSED, off_t pageoff; void *map; + if ((uint64_t) (size_t) size != size) + { + error_callback (data, "file size too large", 0); + return 0; + } + pagesize = getpagesize (); inpage = offset % pagesize; pageoff = offset - inpage; @@ -71,7 +81,7 @@ backtrace_get_view (struct backtrace_state *state ATTRIBUTE_UNUSED, map = mmap (NULL, size, PROT_READ, MAP_PRIVATE, descriptor, pageoff); if (map == MAP_FAILED) { - error_callback (data, "mmap file i/o", errno); + error_callback (data, "mmap", errno); return 0; } diff --git a/Sources/CBacktrace/posix.c b/Sources/CBacktrace/posix.c index eeb3a6a..12c4c6f 100644 --- a/Sources/CBacktrace/posix.c +++ b/Sources/CBacktrace/posix.c @@ -1,6 +1,6 @@ #ifdef __linux__ /* posix.c -- POSIX file I/O routines for the backtrace library. - Copyright (C) 2012-2018 Free Software Foundation, Inc. + Copyright (C) 2012-2021 Free Software Foundation, Inc. Written by Ian Lance Taylor, Google. Redistribution and use in source and binary forms, with or without @@ -68,7 +68,11 @@ backtrace_open (const char *filename, backtrace_error_callback error_callback, descriptor = open (filename, (int) (O_RDONLY | O_BINARY | O_CLOEXEC)); if (descriptor < 0) { - if (does_not_exist != NULL && errno == ENOENT) + /* If DOES_NOT_EXIST is not NULL, then don't call ERROR_CALLBACK + if the file does not exist. We treat lacking permission to + open the file as the file not existing; this case arises when + running the libgo syscall package tests as root. */ + if (does_not_exist != NULL && (errno == ENOENT || errno == EACCES)) *does_not_exist = 1; else error_callback (data, filename, errno); diff --git a/Sources/CBacktrace/print.c b/Sources/CBacktrace/print.c index d8e2427..3d2c0a3 100644 --- a/Sources/CBacktrace/print.c +++ b/Sources/CBacktrace/print.c @@ -1,6 +1,6 @@ #ifdef __linux__ /* print.c -- Print the current backtrace. - Copyright (C) 2012-2018 Free Software Foundation, Inc. + Copyright (C) 2012-2021 Free Software Foundation, Inc. Written by Ian Lance Taylor, Google. Redistribution and use in source and binary forms, with or without @@ -81,7 +81,7 @@ error_callback (void *data, const char *msg, int errnum) /* Print a backtrace. */ -void +void __attribute__((noinline)) backtrace_print (struct backtrace_state *state, int skip, FILE *f) { struct print_data data; diff --git a/Sources/CBacktrace/simple.c b/Sources/CBacktrace/simple.c index ab7bca6..27af524 100644 --- a/Sources/CBacktrace/simple.c +++ b/Sources/CBacktrace/simple.c @@ -1,6 +1,6 @@ #ifdef __linux__ /* simple.c -- The backtrace_simple function. - Copyright (C) 2012-2018 Free Software Foundation, Inc. + Copyright (C) 2012-2021 Free Software Foundation, Inc. Written by Ian Lance Taylor, Google. Redistribution and use in source and binary forms, with or without @@ -56,7 +56,7 @@ struct backtrace_simple_data int ret; }; -/* Unwind library callback routine. This is passd to +/* Unwind library callback routine. This is passed to _Unwind_Backtrace. */ static _Unwind_Reason_Code @@ -91,7 +91,7 @@ simple_unwind (struct _Unwind_Context *context, void *vdata) /* Get a simple stack backtrace. */ -int +int __attribute__((noinline)) backtrace_simple (struct backtrace_state *state, int skip, backtrace_simple_callback callback, backtrace_error_callback error_callback, void *data) diff --git a/Sources/CBacktrace/sort.c b/Sources/CBacktrace/sort.c index c8f8258..21899fb 100644 --- a/Sources/CBacktrace/sort.c +++ b/Sources/CBacktrace/sort.c @@ -1,6 +1,6 @@ #ifdef __linux__ /* sort.c -- Sort without allocating memory - Copyright (C) 2012-2018 Free Software Foundation, Inc. + Copyright (C) 2012-2021 Free Software Foundation, Inc. Written by Ian Lance Taylor, Google. Redistribution and use in source and binary forms, with or without diff --git a/Sources/CBacktrace/state.c b/Sources/CBacktrace/state.c index 0bca99c..b4f40af 100644 --- a/Sources/CBacktrace/state.c +++ b/Sources/CBacktrace/state.c @@ -1,6 +1,6 @@ #ifdef __linux__ /* state.c -- Create the backtrace state. - Copyright (C) 2012-2018 Free Software Foundation, Inc. + Copyright (C) 2012-2021 Free Software Foundation, Inc. Written by Ian Lance Taylor, Google. Redistribution and use in source and binary forms, with or without diff --git a/scripts/vendor-libbacktrace.sh b/scripts/vendor-libbacktrace.sh index 7bfc1da..7f49542 100755 --- a/scripts/vendor-libbacktrace.sh +++ b/scripts/vendor-libbacktrace.sh @@ -63,12 +63,15 @@ done EXCLUDES=( '*test*' -'alloc.c' 'nounwind.c' 'pecoff.c' 'read.c' 'unknown.c' 'xcoff.c' +'macho.c' +'alloc.c' +'allocfail.c' +'instrumented_alloc.c' ) for exclude in "${EXCLUDES[@]}"