diff options
Diffstat (limited to 'libdw')
58 files changed, 4829 insertions, 696 deletions
diff --git a/libdw/ChangeLog b/libdw/ChangeLog index 6533eb50..da7ed9d0 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,727 @@ +2018-06-28 Mark Wielaard <[email protected]> + + * dwarf_next_cfi.c (dwarf_next_cfi): Check whether length is zero. + +2018-06-27 Mark Wielaard <[email protected]> + + * dwarf_begin_elf.c (check_section): Allow a single .debug_frame + section. + +2018-06-26 Mark Wielaard <[email protected]> + + * libdw.h (dwarf_getscn_info): Remove. + * libdw.map (ELFUTILS_0.122): Remove dwarf_getscn_info. + +2018-06-25 Mark Wielaard <[email protected]> + + * Makefile.am (libdw_a_SOURCES): Add dwarf_next_lines.c. + * libdw.h (dwarf_next_lines): New function declaration. + * libdw.map (ELFUTILS_0.173): New section. + * dwarf_next_lines.c: New files. + * dwarf_begin_elf.c (check_section): Don't error out when elf + decompression fails. + (valid_p): Allow just a single .debug_line section. + * dwarf_getsrclines.c (read_srclines): Keep files relative if comp_dir + is missing. + +2018-06-22 Mark Wielaard <[email protected]> + + * dwarf_nextcu.c (__libdw_next_unit): Set next_off to -1 when it would + wrap around. + +2018-06-18 Mark Wielaard <[email protected]> + + * dwarf_aggregate_size.c (array_size): New depth argument. Use + aggregate_size instead of dwarf_aggregate_size and pass depth. + (aggregate_size): New depth argument. Check depth isn't bigger + than MAX_DEPTH (256). Pass depth to recursive calls. + (dwarf_aggregate_size): ass zero as depth to aggregate_size. + +2018-06-18 Mark Wielaard <[email protected]> + + * dwarf_peel_type.c (dwarf_peel_type): Limit modifier chain to 64. + +2018-06-18 Mark Wielaard <[email protected]> + + * dwarf_aggregate_size.c (aggregate_size): Check die is not NULL. + +2018-06-17 Luiz Angelo Daros de Luca <[email protected]> + + * dwarf_getsrclines.c (read_srclines): Intialize filelist early. + +2018-06-15 Mark Wielaard <[email protected]> + + * dwarf_getlocation.c (check_constant_offset): Clarify DW_FORM_data16 + isn't really a constant. + (dwarf_getlocation): Don't handle DW_FORM_data16 as block. + (dwarf_getlocation_addr): Likewise. + (dwarf_getlocations): Likewise. + +2018-06-12 Mark Wielaard <[email protected]> + + * memory-access.h (read_3ubyte_unaligned_inc): New define. + +2018-06-12 Mark Wielaard <[email protected]> + + * libdw.h (__libdw_dieabbrev): Set die->abbrev to DWARF_END_ABBREV + on failure. + +2018-06-10 Mark Wielaard <[email protected]> + + * dwarf_attr_integrate.c (dwarf_attr_integrate): Stop after 16 DIE + ref chains. + * dwarf_hasattr_integrate.c (dwarf_hasattr_integrate): Likewise. + +2018-06-08 Mark Wielaard <[email protected]> + + * dwarf_getabbrev.c (dwarf_getabbrev): Check die and offset. + +2018-06-08 Mark Wielaard <[email protected]> + + * dwarf_get_units.c (dwarf_get_units): Handle existing error, no + dwarf. + +2018-06-08 Mark Wielaard <[email protected]> + + * dwarf_getlocation.c (store_implicit_value): Return error when + seeing bad DWARF or when tsearch runs out of memory. + (__libdw_intern_expression): Report error when store_implicit_value + reported an error. + +2018-06-08 Mark Wielaard <[email protected]> + + * dwarf_getsrclines.c (read_srclines): Sanity check ndirs and nfiles. + +2018-06-08 Mark Wielaard <[email protected]> + + * dwarf_getlocation_attr.c (addr_valp): Set error and return NULL + when there is no .debug_addr section. + (dwarf_getlocation_attr): If addr_valp returns NULL, then return -1. + +2018-06-07 Mark Wielaard <[email protected]> + + * libdw_findcu.c (__libdw_intern_next_unit): Report DWARF_E_VERSION, + not DWARF_E_INVALID_DWARF on unknown version. Set address_size and + offset_size to 8 when unknown. + +2018-06-06 Mark Wielaard <[email protected]> + + * libdwP.h (__libdw_dieabbrev): Check DIE addr falls in cu. + +2018-06-06 Mark Wielaard <[email protected]> + + * dwarf_getlocation_die.c (dwarf_getlocation_die): Check offset + falls inside cu data. + +2018-06-05 Mark Wielaard <[email protected]> + + * dwarf_getsrclines.c (read_srclines): Explicitly set diridx to -1 + in case dwarf_formudata fails. + +2018-06-05 Mark Wielaard <[email protected]> + + * dwarf_getaranges (dwarf_getaranges): Free new_arange if + __libdw_findcu fails. + +2018-06-05 Mark Wielaard <[email protected]> + + * dwarf_getsrclines.c (read_srclines): Define dirarray early and + check whether or not it is equal to dirstack on exit/out before + cleanup. + +2018-06-05 Mark Wielaard <[email protected]> + + * dwarf_getalt.c (find_debug_altlink): id_path array should be 2 + larger to contain MAX_BUILD_ID_BYTES. + +2018-05-31 Mark Wielaard <[email protected]> + + * libdw_find_split_unit.c (try_split_file): New function extracted + from... + (__libdw_find_split_unit): ... here. Try both the relative and + absolute paths to find a .dwo file. + +2018-05-30 Mark Wielaard <[email protected]> + + * libdw/dwarf_getsrclines.c (read_srclines): Change ndir and + ndirlist to size_t. Add check to see ndirlist doesn't overflow. + +2018-05-31 Mark Wielaard <[email protected]> + + * dwarf_dieoffset.c: Check die->cu != NULL. Return -1, not ~0ul + on failure. + +2018-05-29 Mark Wielaard <[email protected]> + + * dwarf_cuoffset.c (dwarf_cuoffset): Check die->cu is not NULL. + * dwarf_die_addr_die.c (dwarf_die_addr_die): Also search split + Dwarfs. + * libdwP.h (struct Dwarf): Add split_tree field. + (__libdw_find_split_dbg_addr): New internal function definition. + (__libdw_finddbg_cb): Likewise. + * libdw_find_split_unit.c (__libdw_find_split_unit): Insert split + Dwarf into skeleton dbg split_tree. + * libdw_findcu.c (__libdw_finddbg_cb): New function. + (__libdw_find_split_dbg_addr): Likewise. + * dwarf_end (dwarf_end): Destroy split_tree. + +2018-05-29 Mark Wielaard <[email protected]> + + * dwarf.h: Add GNU DebugFission list entry encodings + DW_LLE_GNU_end_of_list_entry, + DW_LLE_GNU_base_address_selection_entry, + DW_LLE_GNU_start_end_entry and DW_LLE_GNU_start_length_entry. + * dwarf_ranges.c (__libdw_read_begin_end_pair_inc): Handle + GNU DebugFission list entries. + +2018-05-28 Mark Wielaard <[email protected]> + + * libdw_find_split_unit.c (__libdw_find_split_unit): End split_dwarf + only after we tried every unit id in it. + +2018-04-07 Mark Wielaard <[email protected]> + + * libdwP.h (struct Dwarf_CU): Add locs_base. + (__libdw_cu_locs_base): New static inline function. + * libdw_findcu.c (__libdw_intern_next_unit): Initialize locs_base. + * dwarf_begin_elf.c (valid_p): Create fake_loclists_cu if necessary. + * dwarf_end.c (dwarf_end): Clean up fake_loclists_cu. + * dwarf_getlocation.c (initial_offset): Handle .debug_loclists. + (getlocations_addr): Likewise. + (dwarf_getlocation_addr): Likewise. + * dwarf_getlocation_attr.c (attr_form_cu): Use fake_loclists_cu for + DWARF5. + (initial_offset): Handle DW_FORM_loclistx. + * dwarf_ranges.c (__libdw_read_begin_end_pair_inc): Handle + .debug_loclists. + * libdwP.h (struct Dwarf): Add fake_loclists_cu. + +2018-04-12 Mark Wielaard <[email protected]> + + * dwarf.h: Add DWARF5 location list entry DW_LLE encodings. + * begin_elf.c (dwarf_scnnames): Add IDX_debug_loclists. + * dwarf_error.c (errmsgs): Remove DWARF_E_NO_LOCLIST. And replace + with DWARF_E_NO_DEBUG_LOC, DWARF_E_NO_DEBUG_LOCLISTS and + DWARF_E_NO_LOC_VALUE. + * dwarf_formudata.c (dwarf_formudata): Handle DW_AT_loclists_base + and DW_FORM_loclistx. + * dwarf_getlocation.c (attr_ok): Use DWARF_E_NO_LOC_VALUE. + (initial_offset): Use DWARF_E_NO_DEBUG_LOC. + * libdwP.h: Add IDX_debug_rnglists. Remove DWARF_E_NO_LOCLIST. + Add DWARF_E_NO_DEBUG_LOC, DWARF_E_NO_DEBUG_LOCLISTS and + DWARF_E_NO_LOC_VALUE. + +2018-05-25 Mark Wielaard <[email protected]> + + * libdw_find_split_unit.c (__libdw_find_split_unit): Extract linking + skeleton and split compile units code into... + * libdwP (__libdw_link_skel_split): ...this new function. + +2018-04-06 Mark Wielaard <[email protected]> + + * dwarf_formaddr.c (__libdw_addrx): New function, extracted from... + (dwarf_formaddr): here. Use __libdw_addrx. + * dwarf_getlocation.c (getlocations_addr): Pass cu to + __libdw_read_begin_end_pair_inc. + * dwarf_ranges.c (__libdw_read_begin_end_pair_inc): Take cu as + argument. Handle .debug_rnglists. + (initial_offset): Handle .debug_rnglists and DW_FORM_rnglistx. + (dwarf_ranges): Likewise. Check cu isn't NULL before use. Pass cu to + __libdw_read_begin_end_pair_inc. + * libdwP.h (__libdw_read_begin_end_pair_inc): Take cu as argument. + (__libdw_cu_ranges_base): Handle DW_AT_rnglists_base. + (__libdw_addrx): New function definition. + +2018-04-11 Mark Wielaard <[email protected]> + + * dwarf.h: Add DWARF5 range list entry DW_RLE encodings. + * begin_elf.c (dwarf_scnnames): Add IDX_debug_rnglists. + * dwarf_error.c (errmsgs): Add DWARF_E_NO_DEBUG_RNGLISTS. + * dwarf_formudata.c (dwarf_formudata): Handle DW_AT_rnglists_base + and DW_FORM_rnglistx. + * dwarf_getscopes.c (pc_match): Also check for + DWARF_E_NO_DEBUG_RNGLISTS. + * libdwP.h: Add IDX_debug_rnglists. + +2018-05-25 Mark Wielaard <[email protected]> + + * dwarf_getlocation_attr.c (__libdw_cu_addr_base): Cast offset to + uintptr_t before returning as pointer. + +2018-05-22 Mark Wielaard <[email protected]> + + * dwarf_getlocation.c (__libdw_cu_base_address): Treat errors of + getting lowpc or entrypc the same as missing base address (zero). + * dwarf_highpc (dwarf_highpc): Handle any address form. Always set + error when attribute could not be found. + +2018-05-21 Mark Wielaard <[email protected]> + + * dwarf_begin_elf.c (valid_p): Add a fake_addr_cu to the result. + * dwarf_end.c (cu_free): Disconnect the fake_addr_cu from the split + dwarf if shared with skeleton. + (dwarf_end): release fake_addr_cu. + * dwarf_formaddr.c (__libdw_cu_addr_base): Move to... + * libdwP.h (__libdw_cu_addr_base): ... here. + (struct Dwarf): Add fake_addr_cu field. + * dwarf_formudata.c (dwarf_formudata): Handle + DW_FORM_GNU_addr_index and DW_FORM_addrx[1234]. + * dwarf_getlocation_attr.c (addr_valp): New static function. + (dwarf_getlocation_attr): Create attribute for values of + DW_OP_GNU_const_index, DW_OP_constx and DW_OP_GNU_addr_index and + DW_OP_addrx. + * libdw_find_split_unit.c (__libdw_find_split_unit): Connect + IDX_debug_addr sectiondata and fake_addr_cu between split and + skeleton. + +2018-05-20 Mark Wielaard <[email protected]> + + * dwarf_cu_info.c: New file. + * Makefile.am (libdw_a_SOURCES): Add dwarf_cu_info.c. + * libdw.h (dwarf_cu_info): New function declaration. + * libdw.map (ELFUTILS_0.171): Add dwarf_cu_info. + +2018-05-24 Mark Wielaard <[email protected]> + + * dwarf_ranges.c (dwarf_ranges): Check for NULL cu. + * libdw_findcu.c (__libdw_intern_next_unit): Initialize ranges_base. + +2018-05-18 Mark Wielaard <[email protected]> + + * dwarf_formudata.c (__libdw_formptr): Handle the special case + of IDX_debug_ranges for DW_UT_split_compile with version < 5. + * dwarf_highpc.c (dwarf_highpc): Use dwarf_lowpc, check for + split compile cudie. + * dwarf_lowpc.c (dwarf_lowpc): Check for split compile cudie. + * dwarf_ranges.c (dwarf_ranges): Switch cu and sectiondata for + split compile units. + * libdwP.h (struct Dwarf_CU): Add ranges_base field. + (__libdw_cu_ranges_base): New static inline function. + +2018-05-18 Mark Wielaard <[email protected]> + + * libdw_findcu.c (__libdw_intern_next_unit): Init files to NULL. + * dwarf_getsrclines.c (dwarf_getsrclines): Handle split units by + taking the line table from the skeleton. + * dwarf_getsrcfiles.c (dwarf_getsrcfiles): Handle split units by + only taking the files from .debug_line offset zero (if it exists), + otherwise fall back to the skeleton. + +2018-05-17 Mark Wielaard <[email protected]> + + * dwarf_begin_elf.c (__libdw_debugdir): New function. + (valid_p): Call __libdw_debugdir. + * dwarf_end.c (dwarf_end.c): Free debugdir. + * dwarf_getalt.c (__libdw_filepath): Extract __libdw_debugdir logic. + take debugdir as argument instead of fd. + (find_debug_altlink): Call __libdw_filepath with debugdir. + * libdwP.h (struct Dwarf): Add debugdir field. + (__libdw_debugdir): New function prototype. + (__libdw_filepath): Adjust prototype to take a const char * instead of + an int. + * libdw_find_split_unit.c (__libdw_find_split_unit): Call + __libdw_filepath with debugdir. + +2018-05-17 Mark Wielaard <[email protected]> + + * dwarf_attr_integrate.c (dwarf_attr_integrate): Handle split_compile + unit DIE, search skeleton_compile unit DIE. + * dwarf_hasattr_integrate.c (dwarf_hasattr_integrate): Likewise. + * libdwP.h (is_cudie): Check cu is not NULL. + +2018-05-19 Mark Wielaard <[email protected]> + + * libdwP.h (__libdw_find_split_unit): Mark as internal_function. + +2018-05-15 Mark Wielaard <[email protected]> + + * Makefile.am (libdw_a_SOURCES): Add libdw_find_split_unit.c. + * dwarf_end.c (cu_free): Free split Dwarf. + * dwarf_get_units.c (dwarf_get_units): Handle DW_UT_skeleton by + calling __libdw_find_split_unit. + * libdwP.h (struct Dwarf_CU): Add split Dwarf_CU field. + (__libdw_find_split_unit): New function prototype. + (str_offsets_base_off): Use cu Dwarf if dbg is NULL. + (filepath): Rename to ... + (__libdw_filepath): This. Which is the actual function name in + dwarf_getalt.c. + (libdw_find_split_unit.c): New file. + * libdw_findcu.c (__libdw_intern_next_unit): Initialize split to -1. + +2018-05-15 Mark Wielaard <[email protected]> + + * libdwP.h (__libdw_first_die_from_cu_start): Adjust commented out + asserts. + * libdw_findcu.c (__libdw_intern_next_unit): For version 4 DWARF if + the cudie has a DW_AT_GNU_dwi_id set the unit_id8 and unit_type to + DW_UT_skeleton or DW_UT_split_compile based on whether the cudie has + child DIEs and a DW_AT_GNU_dwo_name attribute. + +2018-05-14 Mark Wielaard <[email protected]> + + * dwarf.h: Add GNU Debug Fission extensions. DW_AT_GNU_dwo_name, + DW_AT_GNU_dwo_id, DW_AT_GNU_ranges_base, DW_AT_GNU_addr_base, + DW_AT_GNU_pubnames, DW_AT_GNU_pubtypes. DW_FORM_GNU_addr_index, + DW_FORM_GNU_str_index. DW_OP_GNU_addr_index, DW_OP_GNU_const_index. + * dwarf_formaddr.c (dwarf_formaddr): Handle DW_FORM_GNU_addr_index + as DW_FORM_addrx. + (__libdw_cu_addr_base): Check for both DW_AT_GNU_addr_base and + DW_AT_addr_base. + * dwarf_formstring.c (dwarf_formstring): Handle DW_FORM_GNU_str_index + as DW_FORM_strx. + * dwarf_formudata.c (dwarf_formudata): Recognize DW_AT_GNU_addr_base + as addrptr. Recognize DW_AT_GNU_ranges_base as rangelistptr. + * dwarf_getlocation.c (__libdw_intern_expression): Handle + DW_OP_GNU_addr_index as DW_OP_addrx and DW_OP_GNU_const_index as + DW_OP_constx. + * libdw_form.c (__libdw_form_val_compute_len): Handle + DW_FORM_GNU_addr_index and DW_FORM_GNU_str_index taking an uleb128. + +2018-05-12 Mark Wielaard <[email protected]> + + * dwarf_begin_elf.c (check_section): Also recognize .dwo section + name variants. + +2018-05-11 Mark Wielaard <[email protected]> + + * dwarf_formudata.c (dwarf_formudata): Handle DW_AT_macros as macptr. + * dwarf_getmacros.c (get_table_for_offset): Add DW_MACRO_define_sup, + DW_MACRO_undef_sup, DW_MACRO_import_sup, DW_MACRO_define_strx and + DW_MACRO_undef_strx. Add str_offsets_base_off to fake CU. Deal with + DW_AT_macros. Use libdw_valid_user_form. + +2018-05-09 Mark Wielaard <[email protected]> + + * dwarf_formstring.c (__libdw_cu_str_off_base): Moved to... + * libdwP.h (__libdw_cu_str_off_base): ...here. Make static inline. + (str_offsets_base_off): New internal function that also parses + .debug_str_offsets header if necessary. + +2018-05-11 Mark Wielaard <[email protected]> + + * dwarf_siblingof.c (dwarf_siblingof): Don't reference cu till it is + known the Dwarf_Die is came from is valid. + * libdwP.h (__libdw_dieabbrev): Check cu is not NULL. + +2018-05-08 Mark Wielaard <[email protected]> + + * dwarf_formref.c (__libdw_formref): Explicitly don't handle + DW_FORM_ref_sup4 and DW_FORM_ref_sup8. + * dwarf_formref_die.c (dwarf_formref_die): Handle DW_FORM_ref_sup4 + and DW_FORM_ref_sup8. + * dwarf_formstring.c (dwarf_formstring): Handle DW_FORM_strp_sup + as DW_FORM_GNU_strp_alt. + +2018-05-05 Mark Wielaard <[email protected]> + + * dwarf.h: Add DWARF line content descriptions. + * libdwP.h (libdw_valid_user_form): New static function. + * dwarf_getsrclines.c (read_srclines): Check and parse version 5 + DWARF header, dir and file tables separately from older versions + where different. + +2018-04-24 Mark Wielaard <[email protected]> + + * dwarf_begin_elf.c (dwarf_scnnames): Add ".debug_line_str". + * dwarf_error.c (errmsgs): Add DWARF_E_NO_DEBUG_STR and + DWARF_E_NO_DEBUG_LINE_STR. + * dwarf_formstring.c (dwarf_formstring): Handle DW_FORM_line_strp. + Get data from either .debug_str or .debug_line_str. + * libdwP.h: Add IDX_debug_line_str, DWARF_E_NO_DEBUG_STR and + DWARF_E_NO_DEBUG_LINE_STR. + +2018-04-03 Mark Wielaard <[email protected]> + + * dwarf_formudata.c (__libdw_formptr): Take and return const + unsigned char pointers. + * dwarf_getlocation.c (attr_base_address): Rename to... + (__libdw_cu_base_address): this. Take Dwarf_CU, check and set + base_address. + (initial_offset_base): Renamed to... + (initial_offset): this. Only provide offset. + (getlocations_addr): Move data size check and + address base addition into __libdw_read_begin_end_pair_inc. Use + __libdw_cu_base_address and initial_offset. Drop Elf_Data NULL + check (already done by initial_offset, through __libdw_formptr). + (dwarf_getlocations): Use __libdw_cu_base_address and initial_offset. + Drop Elf_Data NULL check. + * dwarf_ranges.c (__libdw_read_begin_end_pair_inc): Change argument + type of readp to Add readend argument. Check data size. Include base + in begin and end result. + (initial_offset): New static function. + (dwarf_ranges): Don't check Elf_Data being NULL (already done by + initial_offset, through __libdw_formptr). Use __libdw_cu_base_address + and initial_offset. Remove base check and addition (already done by + __libdw_read_begin_end_pair_inc. + * libdwP.h (Dwarf_CU): Add base_address field. + (__libdw_read_begin_end_pair_inc): Change argument type of readp to + const. Add readend argument. + (__libdw_formptr): Take and return const unsigned char pointers. + * libdw_findcu.c (__libdw_intern_next_unit): Initialize Dwarf_CU + base_address. + +2018-04-04 Mark Wielaard <[email protected]> + + * libdw_findcu.c (__libdw_intern_next_unit): Initialize Dwarf_CU + addr_base and str_off_base. + +2018-03-23 Mark Wielaard <[email protected]> + + * dwarf_begin_elf.c (dwarf_scnnames): Add IDX_debug_str_offsets, + increase size. + * dwarf_error.c (errmsgs): Add DWARF_E_NO_STR_OFFSETS. + * dwarf_formstring.c (dwarf_formstring): Handle DW_FORM_strx[1234]. + (__libdw_cu_str_off_base): New function. + * dwarf_formudata.c (dwarf_formudata): Handle IDX_debug_str_offsets + as stroffsetsptr. + * libdwP.h: Add IDX_debug_str_offsets and DWARF_E_NO_STR_OFFSETS. + (struct Dwarf_CU): Add str_off_base field. + (__libdw_cu_str_off_base): New function declaration. + +2018-03-22 Mark Wielaard <[email protected]> + + * dwarf_begin_elf.c (dwarf_scnnames): Add IDX_debug_addr. + * dwarf_error.c (errmsgs): Add DWARF_E_NO_DEBUG_ADDR. + * dwarf_formaddr.c (dwarf_formaddr): Handle DW_FORM_addrx[1234]. + (__libdw_cu_addr_base): New function. + * dwarf_formudata.c (dwarf_formudata): Handle DW_AT_addr_base as + addrptr. + * libdwP.h: Add IDX_debug_addr and DWARF_E_NO_DEBUG_ADDR. + (struct Dwarf_CU): Add addr_base field. + (__libdw_cu_addr_base): New function definition. + * memory-access.h (file_byte_order): New static function. + (read_3ubyte_unaligned): New inline function. + +2018-03-29 Mark Wielaard <[email protected]> + + * libdw.h (dwarf_decl_file): Extend documentation. + (dwarf_linesrc): Likewise. + (dwarf_filesrc): Likewise. + +2018-03-06 Mark Wielaard <[email protected]> + + * dwarf.h: Add DW_OP_implicit_pointer, DW_OP_addrx, DW_OP_constx, + DW_OP_entry_value, DW_OP_const_type, DW_OP_regval_type, + DW_OP_deref_type, DW_OP_xderef_type, DW_OP_convert and + DW_OP_reinterpret. + * dwarf_getlocation.c (__libdw_intern_expression): Handle + DW_OP_convert, DW_OP_reinterpret, DW_OP_addrx, DW_OP_constx, + DW_OP_regval_type, DW_OP_entry_value, DW_OP_implicit_pointer, + DW_OP_deref_type, DW_OP_xderef_type and DW_OP_const_type. + * dwarf_getlocation_attr.c (dwarf_getlocation_attr): Handle + DW_OP_entry_value, DW_OP_const_type and DW_OP_implicit_pointer. + * dwarf_getlocation_die.c (dwarf_getlocation_die): Handle + DW_OP_implicit_pointer, DW_OP_convert, DW_OP_reinterpret, + DW_OP_const_type, DW_OP_regval_type, DW_OP_deref_type and + DW_OP_xderef_type. + * dwarf_getlocation_implicit_pointer.c + (dwarf_getlocation_implicit_pointer): Handle DW_OP_implicit_pointer. + +2018-03-01 Mark Wielaard <[email protected]> + + * dwarf.h: Add DW_AT_GNU_locviews and DW_AT_GNU_entry_view. + * dwarf_formudata.c (dwarf_formudata): Handle DW_AT_GNU_locviews + as a loclistptr. + +2018-02-09 Mark Wielaard <[email protected]> + + * dwarf_formblock.c (dwarf_formblock): Handle DW_FORM_data16 as a + 16 byte block. + +2018-02-09 Mark Wielaard <[email protected]> + + * dwarf_child.c (__libdw_find_attr): Handle DW_FORM_implicit_const. + * dwarf_formsdata.c (dwarf_formsdata): Likewise. + * dwarf_formudata.c (dwarf_formudata): Likewise. + * dwarf_getabbrev.c (__libdw_getabbrev): Likewise. + * dwarf_getattrs.c (dwarf_getattrs): Likewise. + * dwarf_hasattr.c (dwarf_hasattr): Likewise. + * dwarf_getabbrevattr.c (dwarf_getabbrevattr_data): New function + that will also return any data associated with the abbrev. Which + currently is only for DW_FORM_implicit_const. Based on... + (dwarf_getabbrevattr): ... this function. Which now just calls + dwarf_getabbrevattr_data. + * libdw.h (dwarf_getabbrevattr_data): Declare new function. + * libdw.map (ELFUTILS_0.170): Add dwarf_getabbrevattr_data. + * libdwP.h (dwarf_getabbrevattr_data): INTDECL. + * memory-access.h (__libdw_get_sleb128_unchecked): New inlined + function based on __libdw_get_uleb128_unchecked. + +2018-02-08 Mark Wielaard <[email protected]> + + * dwarf.h: Add DWARF5 DW_FORMs. + * libdwP.h (__libdw_form_val_compute_len): Handle fix length + DW_FORM_implicit_const, DW_FORM_addrx[1234], DW_FORM_strx[1234], + DW_FORM_ref_sup[48] and DW_FORM_data16. + * libdw_form.c (__libdw_form_val_compute_len): DW_FORM_strp_sup + and DW_FORM_line_strp are offset_size. DW_FORM_addrx, DW_FORM_strx, + DW_FORM_loclistx and DW_FORM_rnglistx are uleb128. + +2018-01-30 Mark Wielaard <[email protected]> + + * Makefile.am (libdw_a_SOURCES): Add dwarf_get_units.c. + * dwarf_get_units.c: New file. + * libdw.h (dwarf_get_units): New function declaration. + * libdw.map (ELFUTILS_0.170): Add dwarf_get_units. + +2018-01-29 Mark Wielaard <[email protected]> + + * dwarf.h (DW_UT_*): Add DWARF Unit Header Types. + * dwarf_cu_die.c (dwarf_cu_die): Rename arguments. type_signaturep + is now called unit_idp. type_offsetp is now called subdie_offsetp. + * dwarf_formref_die.c (dwarf_formref_die): Scan both .debug_info + and .debug_types sections for type units when type signature ref + not found. + * dwarf_getaranges.c (dwarf_getaranges): Use __libdw_findcu and + __libdw_first_die_off_from_cu instead of trying by hand. + * dwarf_getlocation_die.c (dwarf_getlocation_die): Use ISV4TU + instead of checking type_offset by hand. + * dwarf_getlocation_implicit_pointer.c + (dwarf_getlocation_implicit_pointer): Likewise. + * dwarf_nextcu.c (dwarf_next_unit): Call __libdw_next_unit. + (__libdw_next_unit): New function based on dwarf_next_unit with + DWARF5 header support. + * libdwP.h (struct Dwarf_CU): Renamed type_offset to subdie_offset + and type_sig8 to unit_id8. + (ISV4TU): New macro to determine whether a CU is a version 4 type + unit (which comes from the .debug_types section). + (DIE_OFFSET_FROM_CU_OFFSET): Replaced macro by real function... + (__libdw_first_die_from_cu_start): ... that also handles DWARF5 + unit headers. + (__libdw_first_die_off_from_cu): New function that calls the above + using the CU fields. + (CUDIE): Use __libdw_first_die_off_from_cu. + (SUBDIE): New macro that provides the DIE for a CU using the + subdie_offset. + (__libdw_next_unit): New internal function declaration. + * libdw_findcu.c (__libdw_intern_next_unit): Use __libdw_next_unit. + Accept DWARF version 5 headers. Setup unit_type. + (__libdw_findcu): Rename debug_types argument to v4_debug_types + argument (to indicate that we are looking in the .debug_types + section). Support finding the exact offset (unit header start). + +2018-01-25 Mark Wielaard <[email protected]> + + * Makefile.am (libdw_a_SOURCES): Add dwarf_die_addr_die.c. + * dwarf_die_addr_die.c: New file. + * libdw.h (dwarf_die_addr_die): New function declaration. + * libdw.map (ELFUTILS_0.171): New section with dwarf_die_addr_die. + * libdwP.h (__libdw_findcu_addr): New internal function declaration. + * libdw_findcu.c (__libdw_findcu_addr): New internal function. + +2018-02-09 Joshua Watt <[email protected]> + + * cfi.c (execute_cfi): Use FALLTHROUGH macro instead of comment. + * dwarf_frame_register.c (dwarf_frame_register): Likewise. + +2018-01-22 Mark Wielaard <[email protected]> + + * Makefile.am (AM_CPPFLAGS): Add -I libdwelf. + * dwarf_begin_elf.c (dwarf_begin_elf): Initialize Dwarf alt_fd to -1. + * dwarf_end.c (dwarf_end): Call dwarf_end and close on the alt_dwarf + and alt_fd if we allocated them. + * dwarf_fromref_die.c (dwarf_formref_die): Call dwarf_getalt. + * dwarf_formstring.c (dwarf_formstring): Likewise. + * dwarf_getalt.c (__libdw_filepath): New internal function. + (find_debug_altlink): New static function. + (dwarf_getalt): Check Dwarf alt_dwarf and call find_debug_altlink. + Cache result. + * dwarf_setalt.c (dwarf_setalt): Clean up Dwarf alt_dwarf and alt_fd + if we allocated. + * libdw.h (dwarf_getalt): Extend documentation. + (dwarf_setalt): Likewise. + * libdwP.h (struct Dwarf): Add alt_fd field. + (filepath): Declare new internal function. + +2018-01-14 Petr Machata <[email protected]> + + * dwarf_formsdata.c (dwarf_formsdata): + <DW_FORM_data1>: Cast to signed char. + <DW_FORM_data2,4,8>: Use read_*sbyte_unaligned instead of + read_*ubyte_unaligned. + +2017-12-26 Mark Wielaard <[email protected]> + + * libdwP.h (struct Dwarf_Abbrev): Pack struct. Remove attrcnt, + use bitfields for has_children and code. + * dwarf_getabbrev.c (__libdw_getabbrev): Don't count attrs. + * dwarf_getattrcnt.c (dwarf_getattrcnt): Count attrs. + +2017-12-26 Mark Wielaard <[email protected]> + + * memory-access.h (__libdw_get_uleb128_unchecked): New function. + (get_uleb128_unchecked): New define. + * dwarf_child.c (__libdw_find_attr): Use get_uleb128_unchecked to + read attr name and form. + * dwarf_getabbrevattr.c (dwarf_getabbrevattr): Likewise. + * dwarf_getattrs.c (dwarf_getattrs): Likewise. + * dwarf_hasattr.c (dwarf_hasattr): Likewise. + +2017-12-28 Mark Wielaard <[email protected]> + + * dwarf_offdie.c (__libdw_offdie): Check sectiondata exists. + +2017-05-09 Ulf Hermann <[email protected]> + Mark Wielaard <[email protected]> + + * libdwP.h (__libdw_in_section): Fix check for the upper border of + the range. + (__libdw_offset_in_section): Likewise. + +2017-12-20 Mark Wielaard <[email protected]> + + * libdwP.h (struct Dwarf_CU): Add sec_idx field. + (cu_sec_idx): Return cu->sec_idx. + * libdw_findcu.c (__libdw_intern_next_unit): Set cu sec_idx to + IDX_debug_info or IDX_debug_types. + * dwarf_begin_elf.c (valid_p): Set fake_loc_cu->sec_idx to + IDX_debug_loc. + * dwarf_getmacros.c (read_macros): Set fake_cu->sec_idx to + IDX_debug_macro or IDX_debug_macinfo. + +2017-12-12 Mark Wielaard <[email protected]> + + * dwarf_aggregate_size.c (dwarf_aggregate_size): Don't peel the + given DIE. Reserve memory for a new DIE first. + +2017-12-11 Dima Kogan <[email protected]> + + * dwarf_aggregate_size.c (array_size): Handle multi-dimensional + arrays properly. + +2017-11-03 Mark Wielaard <[email protected]> + + * dwarf_getlocation.c (__libdw_intern_expression): Handle + DW_OP_GNU_variable_value. + * dwarf_getlocation_attr.c (dwarf_getlocation_attr): Likewise. + * dwarf_getlocation_die.c (dwarf_getlocation_die): Likewise. + +2017-11-03 Mark Wielaard <[email protected]> + + * dwarf_getlocation.c (attr_ok): Always accept DW_FORM_exprloc. + Update list of acceptable attribute codes based on DWARF5. + +2017-11-03 Mark Wielaard <[email protected]> + + * dwarf.h: Add DW_OP_GNU_variable_value. + +2017-10-03 Mark Wielaard <[email protected]> + + * libdw.h: Define LIBDW_CIE_ID and use it in dwarf_cfi_cie_p. + +2017-08-18 Ulf Hermann <[email protected]> + + * memory-access.h: Use attribute_packed. + +2017-02-27 Ulf Hermann <[email protected]> + + * libdwP.h: Use attribute_hidden. + * libdw_alloc.c: Likewise. + +2017-02-27 Ulf Hermann <[email protected]> + + * Makefile.am: Use fpic_CFLAGS and dso_LDFLAGS. + 2017-07-26 Mark Wielaard <[email protected]> * dwarf.h: Add DW_MACRO_* and compat defines for DW_MACRO_GNU_*. diff --git a/libdw/Makefile.am b/libdw/Makefile.am index b3e781d8..166e37cf 100644 --- a/libdw/Makefile.am +++ b/libdw/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to create Makefile.in ## -## Copyright (C) 2002-2010, 2012, 2014, 2016 Red Hat, Inc. +## Copyright (C) 2002-2010, 2012, 2014, 2016, 2018 Red Hat, Inc. ## This file is part of elfutils. ## ## This file is free software; you can redistribute it and/or modify @@ -29,9 +29,9 @@ ## include $(top_srcdir)/config/eu.am if BUILD_STATIC -AM_CFLAGS += -fPIC +AM_CFLAGS += $(fpic_CFLAGS) endif -AM_CPPFLAGS += -I$(srcdir)/../libelf +AM_CPPFLAGS += -I$(srcdir)/../libelf -I$(srcdir)/../libdwelf VERSION = 1 lib_LIBRARIES = libdw.a @@ -90,7 +90,10 @@ libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \ dwarf_aggregate_size.c dwarf_getlocation_implicit_pointer.c \ dwarf_getlocation_die.c dwarf_getlocation_attr.c \ dwarf_getalt.c dwarf_setalt.c dwarf_cu_getdwarf.c \ - dwarf_cu_die.c dwarf_peel_type.c dwarf_default_lower_bound.c + dwarf_cu_die.c dwarf_peel_type.c dwarf_default_lower_bound.c \ + dwarf_die_addr_die.c dwarf_get_units.c \ + libdw_find_split_unit.c dwarf_cu_info.c \ + dwarf_next_lines.c # Minimal library with symbols needed by those libebl backends that we # ship statically. This is so that e.g. strip doesn't end up bringing @@ -105,7 +108,11 @@ libdw_static_pic_a_SOURCES = libdw_form.c dwarf_child.c dwarf_attr.c \ dwarf_aggregate_size.c dwarf_siblingof.c dwarf_formsdata.c \ dwarf_srclang.c dwarf_formflag.c dwarf_diecu.c \ dwarf_bytesize.c dwarf_bitsize.c dwarf_peel_type.c \ - dwarf_default_lower_bound.c + dwarf_default_lower_bound.c libdw_find_split_unit.c \ + dwarf_getalt.c dwarf_haschildren.c \ + dwarf_begin.c dwarf_begin_elf.c dwarf_end.c frame-cache.c \ + dwarf_get_units.c dwarf_formstring.c \ + ../libdwelf/dwelf_dwarf_gnu_debugaltlink.c if MAINTAINER_MODE BUILT_SOURCES = $(srcdir)/known-dwarf.h @@ -129,8 +136,8 @@ libdw_so_SOURCES = libdw.so$(EXEEXT): $(srcdir)/libdw.map $(libdw_so_LIBS) $(libdw_so_DEPS) # The rpath is necessary for libebl because its $ORIGIN use will # not fly in a setuid executable that links in libdw. - $(AM_V_CCLD)$(LINK) -shared -o $@ \ - -Wl,--soname,$@.$(VERSION),-z,defs,-z,relro \ + $(AM_V_CCLD)$(LINK) $(dso_LDFLAGS) -o $@ \ + -Wl,--soname,$@.$(VERSION) \ -Wl,--enable-new-dtags,-rpath,$(pkglibdir) \ -Wl,--version-script,$<,--no-undefined \ -Wl,--whole-archive $(libdw_so_LIBS) -Wl,--no-whole-archive \ diff --git a/libdw/cfi.c b/libdw/cfi.c index daa845f3..341e055b 100644 --- a/libdw/cfi.c +++ b/libdw/cfi.c @@ -138,7 +138,7 @@ execute_cfi (Dwarf_CFI *cache, case DW_CFA_advance_loc1: operand = *program++; - /* Fallthrough */ + FALLTHROUGH; case DW_CFA_advance_loc + 0 ... DW_CFA_advance_loc + CFI_PRIMARY_MAX: advance_loc: loc += operand * cie->code_alignment_factor; @@ -301,7 +301,7 @@ execute_cfi (Dwarf_CFI *cache, case DW_CFA_restore_extended: get_uleb128 (operand, program, end); - /* Fallthrough */ + FALLTHROUGH; case DW_CFA_restore + 0 ... DW_CFA_restore + CFI_PRIMARY_MAX: if (unlikely (abi_cfi) && likely (opcode == DW_CFA_restore)) diff --git a/libdw/dwarf.h b/libdw/dwarf.h index 902d2617..dc597335 100644 --- a/libdw/dwarf.h +++ b/libdw/dwarf.h @@ -1,5 +1,5 @@ /* This file defines standard DWARF types, structures, and macros. - Copyright (C) 2000-2011, 2014, 2016, 2017 Red Hat, Inc. + Copyright (C) 2000-2011, 2014, 2016, 2017, 2018 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -29,6 +29,20 @@ #ifndef _DWARF_H #define _DWARF_H 1 +/* DWARF Unit Header Types. */ +enum + { + 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 + }; + /* DWARF tags. */ enum { @@ -325,8 +339,17 @@ enum 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_locviews = 0x2137, + DW_AT_GNU_entry_view = 0x2138, DW_AT_GNU_macros = 0x2119, DW_AT_GNU_deleted = 0x211a, + /* GNU Debug Fission extensions. */ + 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_hi_user = 0x3fff }; @@ -368,7 +391,29 @@ enum DW_FORM_sec_offset = 0x17, DW_FORM_exprloc = 0x18, DW_FORM_flag_present = 0x19, + 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_ref_sig8 = 0x20, + 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, + + /* GNU Debug Fission extensions. */ + DW_FORM_GNU_addr_index = 0x1f01, + DW_FORM_GNU_str_index = 0x1f02, DW_FORM_GNU_ref_alt = 0x1f20, /* offset in alternate .debuginfo. */ DW_FORM_GNU_strp_alt = 0x1f21 /* offset in alternate .debug_str. */ @@ -533,6 +578,17 @@ enum DW_OP_implicit_value = 0x9e, /* DW_FORM_block follows opcode. */ DW_OP_stack_value = 0x9f, /* No operands, special like DW_OP_piece. */ + DW_OP_implicit_pointer = 0xa0, + DW_OP_addrx = 0xa1, + DW_OP_constx = 0xa2, + DW_OP_entry_value = 0xa3, + DW_OP_const_type = 0xa4, + DW_OP_regval_type = 0xa5, + DW_OP_deref_type = 0xa6, + DW_OP_xderef_type = 0xa7, + DW_OP_convert = 0xa8, + DW_OP_reinterpret = 0xa9, + /* GNU extensions. */ DW_OP_GNU_push_tls_address = 0xe0, DW_OP_GNU_uninit = 0xf0, @@ -546,6 +602,12 @@ enum DW_OP_GNU_reinterpret = 0xf9, DW_OP_GNU_parameter_ref = 0xfa, + /* GNU Debug Fission extensions. */ + DW_OP_GNU_addr_index = 0xfb, + DW_OP_GNU_const_index = 0xfc, + + DW_OP_GNU_variable_value = 0xfd, + DW_OP_lo_user = 0xe0, /* Implementation-defined range start. */ DW_OP_hi_user = 0xff /* Implementation-defined range end. */ }; @@ -737,6 +799,17 @@ enum DW_DEFAULTED_out_of_class = 2 }; +/* DWARF line content descriptions. */ +enum + { + 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 + }; /* DWARF standard opcode encodings. */ enum @@ -813,6 +886,45 @@ enum #define DW_MACRO_GNU_hi_user DW_MACRO_hi_user +/* Range list entry encoding. */ +enum + { + DW_RLE_end_of_list = 0x0, + DW_RLE_base_addressx = 0x1, + DW_RLE_startx_endx = 0x2, + DW_RLE_startx_length = 0x3, + DW_RLE_offset_pair = 0x4, + DW_RLE_base_address = 0x5, + DW_RLE_start_end = 0x6, + DW_RLE_start_length = 0x7 + }; + + +/* Location list entry encoding. */ +enum + { + DW_LLE_end_of_list = 0x0, + DW_LLE_base_addressx = 0x1, + DW_LLE_startx_endx = 0x2, + DW_LLE_startx_length = 0x3, + DW_LLE_offset_pair = 0x4, + DW_LLE_default_location = 0x5, + DW_LLE_base_address = 0x6, + DW_LLE_start_end = 0x7, + DW_LLE_start_length = 0x8 + }; + + +/* GNU DebugFission list entry encodings (.debug_loc.dwo). */ +enum + { + DW_LLE_GNU_end_of_list_entry = 0x0, + DW_LLE_GNU_base_address_selection_entry = 0x1, + DW_LLE_GNU_start_end_entry = 0x2, + DW_LLE_GNU_start_length_entry = 0x3 + }; + + /* DWARF call frame instruction encodings. */ enum { diff --git a/libdw/dwarf_aggregate_size.c b/libdw/dwarf_aggregate_size.c index 838468dd..75105e4d 100644 --- a/libdw/dwarf_aggregate_size.c +++ b/libdw/dwarf_aggregate_size.c @@ -46,13 +46,17 @@ get_type (Dwarf_Die *die, Dwarf_Attribute *attr_mem, Dwarf_Die *type_mem) return type; } +static int aggregate_size (Dwarf_Die *die, Dwarf_Word *size, + Dwarf_Die *type_mem, int depth); + static int array_size (Dwarf_Die *die, Dwarf_Word *size, - Dwarf_Attribute *attr_mem, Dwarf_Die *type_mem) + Dwarf_Attribute *attr_mem, int depth) { Dwarf_Word eltsize; - if (INTUSE(dwarf_aggregate_size) (get_type (die, attr_mem, type_mem), - &eltsize) != 0) + Dwarf_Die type_mem, aggregate_type_mem; + if (aggregate_size (get_type (die, attr_mem, &type_mem), &eltsize, + &aggregate_type_mem, depth) != 0) return -1; /* An array can have DW_TAG_subrange_type or DW_TAG_enumeration_type @@ -63,7 +67,7 @@ array_size (Dwarf_Die *die, Dwarf_Word *size, return -1; bool any = false; - Dwarf_Word total = 0; + Dwarf_Word count_total = 1; do { Dwarf_Word count; @@ -134,53 +138,63 @@ array_size (Dwarf_Die *die, Dwarf_Word *size, continue; } - /* This is a subrange_type or enumeration_type and we've set COUNT. - Now determine the stride for this array dimension. */ - Dwarf_Word stride = eltsize; - if (INTUSE(dwarf_attr_integrate) (&child, DW_AT_byte_stride, - attr_mem) != NULL) - { - if (INTUSE(dwarf_formudata) (attr_mem, &stride) != 0) - return -1; - } - else if (INTUSE(dwarf_attr_integrate) (&child, DW_AT_bit_stride, - attr_mem) != NULL) - { - if (INTUSE(dwarf_formudata) (attr_mem, &stride) != 0) - return -1; - if (stride % 8) /* XXX maybe compute in bits? */ - return -1; - stride /= 8; - } + count_total *= count; any = true; - total += stride * count; } while (INTUSE(dwarf_siblingof) (&child, &child) == 0); if (!any) return -1; - *size = total; + /* This is a subrange_type or enumeration_type and we've set COUNT. + Now determine the stride for this array. */ + Dwarf_Word stride = eltsize; + if (INTUSE(dwarf_attr_integrate) (die, DW_AT_byte_stride, + attr_mem) != NULL) + { + if (INTUSE(dwarf_formudata) (attr_mem, &stride) != 0) + return -1; + } + else if (INTUSE(dwarf_attr_integrate) (die, DW_AT_bit_stride, + attr_mem) != NULL) + { + if (INTUSE(dwarf_formudata) (attr_mem, &stride) != 0) + return -1; + if (stride % 8) /* XXX maybe compute in bits? */ + return -1; + stride /= 8; + } + + *size = count_total * stride; return 0; } static int -aggregate_size (Dwarf_Die *die, Dwarf_Word *size, Dwarf_Die *type_mem) +aggregate_size (Dwarf_Die *die, Dwarf_Word *size, + Dwarf_Die *type_mem, int depth) { Dwarf_Attribute attr_mem; +/* Arrays of arrays of subrange types of arrays... Don't recurse too deep. */ +#define MAX_DEPTH 256 + if (die == NULL || depth++ >= MAX_DEPTH) + return -1; + if (INTUSE(dwarf_attr_integrate) (die, DW_AT_byte_size, &attr_mem) != NULL) return INTUSE(dwarf_formudata) (&attr_mem, size); switch (INTUSE(dwarf_tag) (die)) { case DW_TAG_subrange_type: - return aggregate_size (get_type (die, &attr_mem, type_mem), - size, type_mem); /* Tail call. */ + { + Dwarf_Die aggregate_type_mem; + return aggregate_size (get_type (die, &attr_mem, type_mem), + size, &aggregate_type_mem, depth); + } case DW_TAG_array_type: - return array_size (die, size, &attr_mem, type_mem); + return array_size (die, size, &attr_mem, depth); /* Assume references and pointers have pointer size if not given an explicit DW_AT_byte_size. */ @@ -198,12 +212,12 @@ aggregate_size (Dwarf_Die *die, Dwarf_Word *size, Dwarf_Die *type_mem) int dwarf_aggregate_size (Dwarf_Die *die, Dwarf_Word *size) { - Dwarf_Die type_mem; + Dwarf_Die die_mem, type_mem; - if (INTUSE (dwarf_peel_type) (die, die) != 0) + if (INTUSE (dwarf_peel_type) (die, &die_mem) != 0) return -1; - return aggregate_size (die, size, &type_mem); + return aggregate_size (&die_mem, size, &type_mem, 0); } INTDEF (dwarf_aggregate_size) OLD_VERSION (dwarf_aggregate_size, ELFUTILS_0.144) diff --git a/libdw/dwarf_attr_integrate.c b/libdw/dwarf_attr_integrate.c index 812d74b9..fc068638 100644 --- a/libdw/dwarf_attr_integrate.c +++ b/libdw/dwarf_attr_integrate.c @@ -1,5 +1,5 @@ /* Return specific DWARF attribute of a DIE, integrating indirections. - Copyright (C) 2005 Red Hat, Inc. + Copyright (C) 2005, 2018 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -38,7 +38,7 @@ dwarf_attr_integrate (Dwarf_Die *die, unsigned int search_name, Dwarf_Attribute *result) { Dwarf_Die die_mem; - + int chain = 16; /* Largest DIE ref chain we will follow. */ do { Dwarf_Attribute *attr = INTUSE(dwarf_attr) (die, search_name, result); @@ -53,8 +53,21 @@ dwarf_attr_integrate (Dwarf_Die *die, unsigned int search_name, die = INTUSE(dwarf_formref_die) (attr, &die_mem); } - while (die != NULL); + while (die != NULL && chain-- != 0); + /* Not NULL if it didn't have abstract_origin and specification + attributes. If it is a split CU then see if the skeleton + has it. */ + if (die != NULL && is_cudie (die) + && die->cu->unit_type == DW_UT_split_compile) + { + Dwarf_CU *skel_cu = __libdw_find_split_unit (die->cu); + if (skel_cu != NULL) + { + Dwarf_Die skel_die = CUDIE (skel_cu); + return INTUSE(dwarf_attr) (&skel_die, search_name, result); + } + } return NULL; } INTDEF (dwarf_attr_integrate) diff --git a/libdw/dwarf_begin_elf.c b/libdw/dwarf_begin_elf.c index afa15cef..184a6dc9 100644 --- a/libdw/dwarf_begin_elf.c +++ b/libdw/dwarf_begin_elf.c @@ -1,7 +1,6 @@ /* Create descriptor from ELF descriptor for processing file. - Copyright (C) 2002-2011, 2014, 2015 Red Hat, Inc. + Copyright (C) 2002-2011, 2014, 2015, 2017, 2018 Red Hat, Inc. This file is part of elfutils. - Written by Ulrich Drepper <[email protected]>, 2002. This file is free software; you can redistribute it and/or modify it under the terms of either @@ -43,24 +42,30 @@ #include <fcntl.h> #include <endian.h> +#include "libelfP.h" #include "libdwP.h" -/* Section names. */ -static const char dwarf_scnnames[IDX_last][18] = +/* Section names. (Note .debug_str_offsets is the largest 19 chars.) */ +static const char dwarf_scnnames[IDX_last][19] = { [IDX_debug_info] = ".debug_info", [IDX_debug_types] = ".debug_types", [IDX_debug_abbrev] = ".debug_abbrev", + [IDX_debug_addr] = ".debug_addr", [IDX_debug_aranges] = ".debug_aranges", [IDX_debug_line] = ".debug_line", + [IDX_debug_line_str] = ".debug_line_str", [IDX_debug_frame] = ".debug_frame", [IDX_debug_loc] = ".debug_loc", + [IDX_debug_loclists] = ".debug_loclists", [IDX_debug_pubnames] = ".debug_pubnames", [IDX_debug_str] = ".debug_str", + [IDX_debug_str_offsets] = ".debug_str_offsets", [IDX_debug_macinfo] = ".debug_macinfo", [IDX_debug_macro] = ".debug_macro", [IDX_debug_ranges] = ".debug_ranges", + [IDX_debug_rnglists] = ".debug_rnglists", [IDX_gnu_debugaltlink] = ".gnu_debugaltlink" }; #define ndwarf_scnnames (sizeof (dwarf_scnnames) / sizeof (dwarf_scnnames[0])) @@ -113,14 +118,26 @@ check_section (Dwarf *result, GElf_Ehdr *ehdr, Elf_Scn *scn, bool inscngrp) size_t cnt; bool gnu_compressed = false; for (cnt = 0; cnt < ndwarf_scnnames; ++cnt) - if (strcmp (scnname, dwarf_scnnames[cnt]) == 0) - break; - else if (scnname[0] == '.' && scnname[1] == 'z' - && strcmp (&scnname[2], &dwarf_scnnames[cnt][1]) == 0) - { - gnu_compressed = true; - break; - } + { + size_t dbglen = strlen (dwarf_scnnames[cnt]); + size_t scnlen = strlen (scnname); + if (strncmp (scnname, dwarf_scnnames[cnt], dbglen) == 0 + && (dbglen == scnlen + || (scnlen == dbglen + 4 + && strstr (scnname, ".dwo") == scnname + dbglen))) + break; + else if (scnname[0] == '.' && scnname[1] == 'z' + && (strncmp (&scnname[2], &dwarf_scnnames[cnt][1], + dbglen - 1) == 0 + && (scnlen == dbglen + 1 + || (scnlen == dbglen + 5 + && strstr (scnname, + ".dwo") == scnname + dbglen + 1)))) + { + gnu_compressed = true; + break; + } + } if (cnt >= ndwarf_scnnames) /* Not a debug section; ignore it. */ @@ -139,17 +156,9 @@ check_section (Dwarf *result, GElf_Ehdr *ehdr, Elf_Scn *scn, bool inscngrp) { if (elf_compress (scn, 0, 0) < 0) { - /* If we failed to decompress the section and it's the - debug_info section, then fail with specific error rather - than the generic NO_DWARF. Without debug_info we can't do - anything (see also valid_p()). */ - if (cnt == IDX_debug_info) - { - Dwarf_Sig8_Hash_free (&result->sig8_hash); - __libdw_seterrno (DWARF_E_COMPRESSED_ERROR); - free (result); - return NULL; - } + /* It would be nice if we could fail with a specific error. + But we don't know if this was an essential section or not. + So just continue for now. See also valid_p(). */ return result; } } @@ -170,6 +179,26 @@ check_section (Dwarf *result, GElf_Ehdr *ehdr, Elf_Scn *scn, bool inscngrp) } +/* Helper function to set debugdir field. We want to cache the dir + where we found this Dwarf ELF file to locate alt and dwo files. */ +char * +__libdw_debugdir (int fd) +{ + /* strlen ("/proc/self/fd/") = 14 + strlen (<MAXINT>) = 10 + 1 = 25. */ + char devfdpath[25]; + sprintf (devfdpath, "/proc/self/fd/%u", fd); + char *fdpath = realpath (devfdpath, NULL); + char *fddir; + if (fdpath != NULL && fdpath[0] == '/' + && (fddir = strrchr (fdpath, '/')) != NULL) + { + *++fddir = '\0'; + return fdpath; + } + return NULL; +} + + /* Check whether all the necessary DWARF information is available. */ static Dwarf * valid_p (Dwarf *result) @@ -177,11 +206,11 @@ valid_p (Dwarf *result) /* We looked at all the sections. Now determine whether all the sections with debugging information we need are there. - XXX Which sections are absolutely necessary? Add tests if - necessary. For now we require only .debug_info. Hopefully this - is correct. */ + Require at least one section that can be read "standalone". */ if (likely (result != NULL) - && unlikely (result->sectiondata[IDX_debug_info] == NULL)) + && unlikely (result->sectiondata[IDX_debug_info] == NULL + && result->sectiondata[IDX_debug_line] == NULL + && result->sectiondata[IDX_debug_frame] == NULL)) { Dwarf_Sig8_Hash_free (&result->sig8_hash); __libdw_seterrno (DWARF_E_NO_DWARF); @@ -189,6 +218,9 @@ valid_p (Dwarf *result) result = NULL; } + /* For dwarf_location_attr () we need a "fake" CU to indicate + where the "fake" attribute data comes from. This is a block + inside the .debug_loc or .debug_loclists section. */ if (result != NULL && result->sectiondata[IDX_debug_loc] != NULL) { result->fake_loc_cu = (Dwarf_CU *) calloc (1, sizeof (Dwarf_CU)); @@ -201,6 +233,7 @@ valid_p (Dwarf *result) } else { + result->fake_loc_cu->sec_idx = IDX_debug_loc; result->fake_loc_cu->dbg = result; result->fake_loc_cu->startp = result->sectiondata[IDX_debug_loc]->d_buf; @@ -210,6 +243,60 @@ valid_p (Dwarf *result) } } + if (result != NULL && result->sectiondata[IDX_debug_loclists] != NULL) + { + result->fake_loclists_cu = (Dwarf_CU *) calloc (1, sizeof (Dwarf_CU)); + if (unlikely (result->fake_loclists_cu == NULL)) + { + Dwarf_Sig8_Hash_free (&result->sig8_hash); + __libdw_seterrno (DWARF_E_NOMEM); + free (result->fake_loc_cu); + free (result); + result = NULL; + } + else + { + result->fake_loclists_cu->sec_idx = IDX_debug_loclists; + result->fake_loclists_cu->dbg = result; + result->fake_loclists_cu->startp + = result->sectiondata[IDX_debug_loclists]->d_buf; + result->fake_loclists_cu->endp + = (result->sectiondata[IDX_debug_loclists]->d_buf + + result->sectiondata[IDX_debug_loclists]->d_size); + } + } + + /* For DW_OP_constx/GNU_const_index and DW_OP_addrx/GNU_addr_index + the dwarf_location_attr () will need a "fake" address CU to + indicate where the attribute data comes from. This is a just + inside the .debug_addr section, if it exists. */ + if (result != NULL && result->sectiondata[IDX_debug_addr] != NULL) + { + result->fake_addr_cu = (Dwarf_CU *) calloc (1, sizeof (Dwarf_CU)); + if (unlikely (result->fake_addr_cu == NULL)) + { + Dwarf_Sig8_Hash_free (&result->sig8_hash); + __libdw_seterrno (DWARF_E_NOMEM); + free (result->fake_loc_cu); + free (result->fake_loclists_cu); + free (result); + result = NULL; + } + else + { + result->fake_addr_cu->sec_idx = IDX_debug_addr; + result->fake_addr_cu->dbg = result; + result->fake_addr_cu->startp + = result->sectiondata[IDX_debug_addr]->d_buf; + result->fake_addr_cu->endp + = (result->sectiondata[IDX_debug_addr]->d_buf + + result->sectiondata[IDX_debug_addr]->d_size); + } + } + + if (result != NULL) + result->debugdir = __libdw_debugdir (result->elf->fildes); + return result; } @@ -325,6 +412,7 @@ dwarf_begin_elf (Elf *elf, Dwarf_Cmd cmd, Elf_Scn *scngrp) result->other_byte_order = true; result->elf = elf; + result->alt_fd = -1; /* Initialize the memory handling. */ result->mem_default_size = mem_default_size; diff --git a/libdw/dwarf_child.c b/libdw/dwarf_child.c index cc95fb3f..9446b880 100644 --- a/libdw/dwarf_child.c +++ b/libdw/dwarf_child.c @@ -1,5 +1,5 @@ /* Return child of current DIE. - Copyright (C) 2003-2011, 2014 Red Hat, Inc. + Copyright (C) 2003-2011, 2014, 2017 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <[email protected]>, 2003. @@ -43,36 +43,27 @@ internal_function __libdw_find_attr (Dwarf_Die *die, unsigned int search_name, unsigned int *codep, unsigned int *formp) { - Dwarf *dbg = die->cu->dbg; const unsigned char *readp; /* Find the abbreviation entry. */ Dwarf_Abbrev *abbrevp = __libdw_dieabbrev (die, &readp); if (unlikely (abbrevp == DWARF_END_ABBREV)) { - invalid_dwarf: __libdw_seterrno (DWARF_E_INVALID_DWARF); return NULL; } - /* Search the name attribute. */ - unsigned char *const endp - = ((unsigned char *) dbg->sectiondata[IDX_debug_abbrev]->d_buf - + dbg->sectiondata[IDX_debug_abbrev]->d_size); - + /* Search the name attribute. Attribute has been checked when + Dwarf_Abbrev was created, we can read unchecked. */ const unsigned char *attrp = abbrevp->attrp; while (1) { /* Get attribute name and form. */ - if (unlikely (attrp >= endp)) - goto invalid_dwarf; unsigned int attr_name; - get_uleb128 (attr_name, attrp, endp); + get_uleb128_unchecked (attr_name, attrp); - if (unlikely (attrp >= endp)) - goto invalid_dwarf; unsigned int attr_form; - get_uleb128 (attr_form, attrp, endp); + get_uleb128_unchecked (attr_form, attrp); /* We can stop if we found the attribute with value zero. */ if (attr_name == 0 && attr_form == 0) @@ -86,7 +77,12 @@ __libdw_find_attr (Dwarf_Die *die, unsigned int search_name, if (formp != NULL) *formp = attr_form; - return (unsigned char *) readp; + /* Normally the attribute data comes from the DIE/info, + except for implicit_form, where it comes from the abbrev. */ + if (attr_form == DW_FORM_implicit_const) + return (unsigned char *) attrp; + else + return (unsigned char *) readp; } /* Skip over the rest of this attribute (if there is any). */ @@ -101,6 +97,13 @@ __libdw_find_attr (Dwarf_Die *die, unsigned int search_name, // __libdw_form_val_len will have done a bounds check. readp += len; + + // If the value is in the abbrev data, skip it. + if (attr_form == DW_FORM_implicit_const) + { + int64_t attr_value __attribute__((__unused__)); + get_sleb128_unchecked (attr_value, attrp); + } } } diff --git a/libdw/dwarf_cu_die.c b/libdw/dwarf_cu_die.c index 194da58b..7594e7dc 100644 --- a/libdw/dwarf_cu_die.c +++ b/libdw/dwarf_cu_die.c @@ -37,8 +37,8 @@ Dwarf_Die * dwarf_cu_die (Dwarf_CU *cu, Dwarf_Die *result, Dwarf_Half *versionp, Dwarf_Off *abbrev_offsetp, uint8_t *address_sizep, - uint8_t *offset_sizep, uint64_t *type_signaturep, - Dwarf_Off *type_offsetp) + uint8_t *offset_sizep, uint64_t *unit_idp, + Dwarf_Off *subdie_offsetp) { if (cu == NULL) return NULL; @@ -53,10 +53,10 @@ dwarf_cu_die (Dwarf_CU *cu, Dwarf_Die *result, Dwarf_Half *versionp, *address_sizep = cu->address_size; if (offset_sizep != NULL) *offset_sizep = cu->offset_size; - if (type_signaturep != NULL) - *type_signaturep = cu->type_sig8; - if (type_offsetp != NULL) - *type_offsetp = cu->type_offset; + if (unit_idp != NULL) + *unit_idp = cu->unit_id8; + if (subdie_offsetp != NULL) + *subdie_offsetp = cu->subdie_offset; return result; } diff --git a/libdw/dwarf_cu_info.c b/libdw/dwarf_cu_info.c new file mode 100644 index 00000000..30aee6c7 --- /dev/null +++ b/libdw/dwarf_cu_info.c @@ -0,0 +1,103 @@ +/* Provides information and DIEs associated with the Dwarf_CU unit. + Copyright (C) 2018 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see <http://www.gnu.org/licenses/>. */ + + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <string.h> +#include "libdwP.h" + + +int +dwarf_cu_info (Dwarf_CU *cu, + Dwarf_Half *version, uint8_t *unit_type, + Dwarf_Die *cudie, Dwarf_Die *subdie, + uint64_t *unit_id, + uint8_t *address_size, uint8_t *offset_size) +{ + if (cu == NULL) + return -1; + + if (version != NULL) + *version = cu->version; + + if (unit_type != NULL) + *unit_type = cu->unit_type; + + if (cudie != NULL) + { + if (cu->version >= 2 && cu->version <= 5 + && cu->unit_type >= DW_UT_compile + && cu->unit_type <= DW_UT_split_type) + *cudie = CUDIE (cu); + else + { + invalid: + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return -1; + } + } + + if (subdie != NULL) + { + if (cu->version >= 2 && cu->version <= 5) + { + /* For types, return the actual type DIE. For skeletons, + find the associated split compile unit and return its + DIE. */ + if (cu->unit_type == DW_UT_type + || cu->unit_type == DW_UT_split_type) + *subdie = SUBDIE(cu); + else if (cu->unit_type == DW_UT_skeleton) + { + Dwarf_CU *split_cu = __libdw_find_split_unit (cu); + if (split_cu != NULL) + *subdie = CUDIE(split_cu); + else + memset (subdie, '\0', sizeof (Dwarf_Die)); + } + else + memset (subdie, '\0', sizeof (Dwarf_Die)); + } + else + goto invalid; + } + + if (unit_id != NULL) + *unit_id = cu->unit_id8; + + if (address_size != NULL) + *address_size = cu->address_size; + + if (offset_size != NULL) + *offset_size = cu->offset_size; + + return 0; +} diff --git a/libdw/dwarf_cuoffset.c b/libdw/dwarf_cuoffset.c index ba376486..f13b02fd 100644 --- a/libdw/dwarf_cuoffset.c +++ b/libdw/dwarf_cuoffset.c @@ -38,7 +38,7 @@ Dwarf_Off dwarf_cuoffset (Dwarf_Die *die) { - return (die == NULL + return ((die == NULL || die->cu == NULL) ? (Dwarf_Off) -1l : (Dwarf_Off) (die->addr - die->cu->startp)); } diff --git a/libdw/dwarf_die_addr_die.c b/libdw/dwarf_die_addr_die.c new file mode 100644 index 00000000..65729166 --- /dev/null +++ b/libdw/dwarf_die_addr_die.c @@ -0,0 +1,70 @@ +/* Return offset of DIE. + Copyright (C) 2018 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <string.h> + +#include <dwarf.h> +#include "libdwP.h" + + +Dwarf_Die * +dwarf_die_addr_die (Dwarf *dbg, void *addr, Dwarf_Die *result) +{ + if (dbg == NULL) + return NULL; + + Dwarf_CU *cu = __libdw_findcu_addr (dbg, addr); + + if (cu == NULL) + { + Dwarf *alt = INTUSE (dwarf_getalt) (dbg); + if (alt != NULL) + cu = __libdw_findcu_addr (alt, addr); + } + + if (cu == NULL) + { + Dwarf *split = __libdw_find_split_dbg_addr (dbg, addr); + if (split != NULL) + cu = __libdw_findcu_addr (split, addr); + } + + if (cu == NULL) + { + memset (result, '\0', sizeof (Dwarf_Die)); + return NULL; + } + + *result = (Dwarf_Die) { .addr = addr, .cu = cu }; + + return result; +} diff --git a/libdw/dwarf_dieoffset.c b/libdw/dwarf_dieoffset.c index 8028f6dd..3a8e2cb6 100644 --- a/libdw/dwarf_dieoffset.c +++ b/libdw/dwarf_dieoffset.c @@ -38,8 +38,8 @@ Dwarf_Off dwarf_dieoffset (Dwarf_Die *die) { - return (die == NULL - ? ~0ul + return ((die == NULL || die->cu == NULL) + ? (Dwarf_Off) -1 : (Dwarf_Off) (die->addr - die->cu->startp + die->cu->start)); } INTDEF(dwarf_dieoffset) diff --git a/libdw/dwarf_end.c b/libdw/dwarf_end.c index 6c6d985a..29795c10 100644 --- a/libdw/dwarf_end.c +++ b/libdw/dwarf_end.c @@ -1,5 +1,5 @@ /* Release debugging handling context. - Copyright (C) 2002-2011, 2014 Red Hat, Inc. + Copyright (C) 2002-2011, 2014, 2018 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <[email protected]>, 2002. @@ -35,6 +35,7 @@ #include <stdlib.h> #include <assert.h> #include <string.h> +#include <unistd.h> #include "libdwP.h" #include "cfi.h" @@ -54,6 +55,16 @@ cu_free (void *arg) Dwarf_Abbrev_Hash_free (&p->abbrev_hash); tdestroy (p->locs, noop_free); + + /* Free split dwarf one way (from skeleton to split). */ + if (p->unit_type == DW_UT_skeleton + && p->split != NULL && p->split != (void *)-1) + { + /* The fake_addr_cu might be shared, only release one. */ + if (p->dbg->fake_addr_cu == p->split->dbg->fake_addr_cu) + p->split->dbg->fake_addr_cu = NULL; + INTUSE(dwarf_end) (p->split->dbg); + } } @@ -80,6 +91,9 @@ dwarf_end (Dwarf *dwarf) /* Search tree for decoded .debug_lines units. */ tdestroy (dwarf->files_lines, noop_free); + /* And the split Dwarf. */ + tdestroy (dwarf->split_tree, noop_free); + struct libdw_memblock *memp = dwarf->mem_tail; /* The first block is allocated together with the Dwarf object. */ while (memp->prev != NULL) @@ -102,6 +116,26 @@ dwarf_end (Dwarf *dwarf) cu_free (dwarf->fake_loc_cu); free (dwarf->fake_loc_cu); } + if (dwarf->fake_loclists_cu != NULL) + { + cu_free (dwarf->fake_loclists_cu); + free (dwarf->fake_loclists_cu); + } + if (dwarf->fake_addr_cu != NULL) + { + cu_free (dwarf->fake_addr_cu); + free (dwarf->fake_addr_cu); + } + + /* Did we find and allocate the alt Dwarf ourselves? */ + if (dwarf->alt_fd != -1) + { + INTUSE(dwarf_end) (dwarf->alt_dwarf); + close (dwarf->alt_fd); + } + + /* The cached dir we found the Dwarf ELF file in. */ + free (dwarf->debugdir); /* Free the context descriptor. */ free (dwarf); diff --git a/libdw/dwarf_error.c b/libdw/dwarf_error.c index 939ec047..46ea16b3 100644 --- a/libdw/dwarf_error.c +++ b/libdw/dwarf_error.c @@ -1,7 +1,6 @@ /* Retrieve ELF descriptor used for DWARF access. - Copyright (C) 2002, 2003, 2004, 2005, 2009, 2014, 2015 Red Hat, Inc. + Copyright (C) 2002-2005, 2009, 2014, 2015, 2017, 2018 Red Hat, Inc. This file is part of elfutils. - Written by Ulrich Drepper <[email protected]>, 2002. This file is free software; you can redistribute it and/or modify it under the terms of either @@ -73,6 +72,9 @@ static const char *errmsgs[] = [DWARF_E_NO_ENTRY] = N_("no entries found"), [DWARF_E_INVALID_DWARF] = N_("invalid DWARF"), [DWARF_E_NO_STRING] = N_("no string data"), + [DWARF_E_NO_DEBUG_STR] = N_(".debug_str section missing"), + [DWARF_E_NO_DEBUG_LINE_STR] = N_(".debug_line_str section missing"), + [DWARF_E_NO_STR_OFFSETS] = N_(".debug_str_offsets section missing"), [DWARF_E_NO_ADDR] = N_("no address value"), [DWARF_E_NO_CONSTANT] = N_("no constant value"), [DWARF_E_NO_REFERENCE] = N_("no reference value"), @@ -83,7 +85,9 @@ static const char *errmsgs[] = [DWARF_E_VERSION] = N_("invalid DWARF version"), [DWARF_E_INVALID_DIR_IDX] = N_("invalid directory index"), [DWARF_E_ADDR_OUTOFRANGE] = N_("address out of range"), - [DWARF_E_NO_LOCLIST] = N_("no location list value"), + [DWARF_E_NO_DEBUG_LOC] = N_(".debug_loc section missing"), + [DWARF_E_NO_DEBUG_LOCLISTS] = N_(".debug_loclists section missing"), + [DWARF_E_NO_LOC_VALUE] = N_("not a location list value"), [DWARF_E_NO_BLOCK] = N_("no block data"), [DWARF_E_INVALID_LINE_IDX] = N_("invalid line index"), [DWARF_E_INVALID_ARANGE_IDX] = N_("invalid address range index"), @@ -91,11 +95,13 @@ static const char *errmsgs[] = [DWARF_E_NO_FLAG] = N_("no flag value"), [DWARF_E_INVALID_OFFSET] = N_("invalid offset"), [DWARF_E_NO_DEBUG_RANGES] = N_(".debug_ranges section missing"), + [DWARF_E_NO_DEBUG_RNGLISTS] = N_(".debug_rnglists section missing"), [DWARF_E_INVALID_CFI] = N_("invalid CFI section"), [DWARF_E_NO_ALT_DEBUGLINK] = N_("no alternative debug link found"), [DWARF_E_INVALID_OPCODE] = N_("invalid opcode"), [DWARF_E_NOT_CUDIE] = N_("not a CU (unit) DIE"), - [DWARF_E_UNKNOWN_LANGUAGE] = N_("unknown language code") + [DWARF_E_UNKNOWN_LANGUAGE] = N_("unknown language code"), + [DWARF_E_NO_DEBUG_ADDR] = N_(".debug_addr section missing"), }; #define nerrmsgs (sizeof (errmsgs) / sizeof (errmsgs[0])) diff --git a/libdw/dwarf_formaddr.c b/libdw/dwarf_formaddr.c index ddc48382..9cd3d200 100644 --- a/libdw/dwarf_formaddr.c +++ b/libdw/dwarf_formaddr.c @@ -1,7 +1,6 @@ /* Return address represented by attribute. - Copyright (C) 2003-2010 Red Hat, Inc. + Copyright (C) 2003-2010, 2018 Red Hat, Inc. This file is part of elfutils. - Written by Ulrich Drepper <[email protected]>, 2003. This file is free software; you can redistribute it and/or modify it under the terms of either @@ -36,20 +35,112 @@ int +__libdw_addrx (Dwarf_CU *cu, Dwarf_Word idx, Dwarf_Addr *addr) +{ + Dwarf_Off addr_off = __libdw_cu_addr_base (cu); + if (addr_off == (Dwarf_Off) -1) + return -1; + + Dwarf *dbg = cu->dbg; + if (dbg->sectiondata[IDX_debug_addr] == NULL) + { + __libdw_seterrno (DWARF_E_NO_DEBUG_ADDR); + return -1; + } + + /* The section should at least contain room for one address. */ + int address_size = cu->address_size; + if (cu->address_size > dbg->sectiondata[IDX_debug_addr]->d_size) + { + invalid_offset: + __libdw_seterrno (DWARF_E_INVALID_OFFSET); + return -1; + } + + if (addr_off > (dbg->sectiondata[IDX_debug_addr]->d_size + - address_size)) + goto invalid_offset; + + idx *= address_size; + if (idx > (dbg->sectiondata[IDX_debug_addr]->d_size + - address_size - addr_off)) + goto invalid_offset; + + const unsigned char *datap; + datap = dbg->sectiondata[IDX_debug_addr]->d_buf + addr_off + idx; + if (address_size == 4) + *addr = read_4ubyte_unaligned (dbg, datap); + else + *addr = read_8ubyte_unaligned (dbg, datap); + + return 0; +} + +int dwarf_formaddr (Dwarf_Attribute *attr, Dwarf_Addr *return_addr) { if (attr == NULL) return -1; - if (unlikely (attr->form != DW_FORM_addr)) + Dwarf_Word idx; + Dwarf_CU *cu = attr->cu; + Dwarf *dbg = cu->dbg; + const unsigned char *datap = attr->valp; + const unsigned char *endp = attr->cu->endp; + switch (attr->form) { - __libdw_seterrno (DWARF_E_NO_ADDR); - return -1; + /* There is one form that just encodes the whole address. */ + case DW_FORM_addr: + if (__libdw_read_address (dbg, cu_sec_idx (cu), datap, + cu->address_size, return_addr)) + return -1; + return 0; + + /* All others encode an index into the .debug_addr section where + the address can be found. */ + case DW_FORM_GNU_addr_index: + case DW_FORM_addrx: + if (datap >= endp) + { + invalid: + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return -1; + } + get_uleb128 (idx, datap, endp); + break; + + case DW_FORM_addrx1: + if (datap >= endp - 1) + goto invalid; + idx = *datap; + break; + + case DW_FORM_addrx2: + if (datap >= endp - 2) + goto invalid; + idx = read_2ubyte_unaligned (dbg, datap); + break; + + case DW_FORM_addrx3: + if (datap >= endp - 3) + goto invalid; + idx = read_3ubyte_unaligned (dbg, datap); + break; + + case DW_FORM_addrx4: + if (datap >= endp - 4) + goto invalid; + idx = read_4ubyte_unaligned (dbg, datap); + break; + + default: + __libdw_seterrno (DWARF_E_NO_ADDR); + return -1; } - if (__libdw_read_address (attr->cu->dbg, - cu_sec_idx (attr->cu), attr->valp, - attr->cu->address_size, return_addr)) + /* So we got an index. Lets see if it is valid and we can get the actual + address. */ + if (__libdw_addrx (cu, idx, return_addr) != 0) return -1; return 0; diff --git a/libdw/dwarf_formblock.c b/libdw/dwarf_formblock.c index 13f9e72a..924baf45 100644 --- a/libdw/dwarf_formblock.c +++ b/libdw/dwarf_formblock.c @@ -1,5 +1,5 @@ /* Return block represented by attribute. - Copyright (C) 2004-2010, 2014 Red Hat, Inc. + Copyright (C) 2004-2010, 2014, 2018 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <[email protected]>, 2004. @@ -75,6 +75,16 @@ dwarf_formblock (Dwarf_Attribute *attr, Dwarf_Block *return_block) return_block->data = (unsigned char *) datap; break; + case DW_FORM_data16: + /* The DWARFv5 spec calls this constant class, but we interpret + it as a block that the user will need to interpret when + converting to a value. */ + if (unlikely (endp - datap < 16)) + goto invalid; + return_block->length = 16; + return_block->data = (unsigned char *) datap; + break; + default: __libdw_seterrno (DWARF_E_NO_BLOCK); return -1; diff --git a/libdw/dwarf_formref.c b/libdw/dwarf_formref.c index 2240a258..2bae2a44 100644 --- a/libdw/dwarf_formref.c +++ b/libdw/dwarf_formref.c @@ -86,6 +86,8 @@ __libdw_formref (Dwarf_Attribute *attr, Dwarf_Off *return_offset) case DW_FORM_ref_addr: case DW_FORM_ref_sig8: case DW_FORM_GNU_ref_alt: + case DW_FORM_ref_sup4: + case DW_FORM_ref_sup8: /* These aren't handled by dwarf_formref, only by dwarf_formref_die. */ __libdw_seterrno (DWARF_E_INVALID_REFERENCE); return -1; diff --git a/libdw/dwarf_formref_die.c b/libdw/dwarf_formref_die.c index 7e2b11a2..f196331a 100644 --- a/libdw/dwarf_formref_die.c +++ b/libdw/dwarf_formref_die.c @@ -1,5 +1,5 @@ /* Look up the DIE in a reference-form attribute. - Copyright (C) 2005-2010 Red Hat, Inc. + Copyright (C) 2005-2010, 2018 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -44,16 +44,23 @@ dwarf_formref_die (Dwarf_Attribute *attr, Dwarf_Die *result) struct Dwarf_CU *cu = attr->cu; Dwarf_Off offset; - if (attr->form == DW_FORM_ref_addr || attr->form == DW_FORM_GNU_ref_alt) + if (attr->form == DW_FORM_ref_addr || attr->form == DW_FORM_GNU_ref_alt + || attr->form == DW_FORM_ref_sup4 || attr->form == DW_FORM_ref_sup8) { /* This has an absolute offset. */ - uint8_t ref_size = (cu->version == 2 && attr->form == DW_FORM_ref_addr - ? cu->address_size - : cu->offset_size); + uint8_t ref_size; + if (cu->version == 2 && attr->form == DW_FORM_ref_addr) + ref_size = cu->address_size; + else if (attr->form == DW_FORM_ref_sup4) + ref_size = 4; + else if (attr->form == DW_FORM_ref_sup8) + ref_size = 8; + else + ref_size = cu->offset_size; Dwarf *dbg_ret = (attr->form == DW_FORM_GNU_ref_alt - ? cu->dbg->alt_dwarf : cu->dbg); + ? INTUSE(dwarf_getalt) (cu->dbg) : cu->dbg); if (dbg_ret == NULL) { @@ -73,27 +80,38 @@ dwarf_formref_die (Dwarf_Attribute *attr, Dwarf_Die *result) if (attr->form == DW_FORM_ref_sig8) { /* This doesn't have an offset, but instead a value we - have to match in the .debug_types type unit headers. */ + have to match in the type unit headers. */ uint64_t sig = read_8ubyte_unaligned (cu->dbg, attr->valp); cu = Dwarf_Sig8_Hash_find (&cu->dbg->sig8_hash, sig, NULL); if (cu == NULL) - /* Not seen before. We have to scan through the type units. */ - do - { - cu = __libdw_intern_next_unit (attr->cu->dbg, true); - if (cu == NULL) - { - __libdw_seterrno (INTUSE(dwarf_errno) () - ?: DWARF_E_INVALID_REFERENCE); - return NULL; - } - } - while (cu->type_sig8 != sig); - - datap = cu->dbg->sectiondata[IDX_debug_types]->d_buf; - size = cu->dbg->sectiondata[IDX_debug_types]->d_size; - offset = cu->start + cu->type_offset; + { + /* Not seen before. We have to scan through the type units. + Since DWARFv5 these can (also) be found in .debug_info, + so scan that first. */ + bool scan_debug_types = false; + do + { + cu = __libdw_intern_next_unit (attr->cu->dbg, scan_debug_types); + if (cu == NULL) + { + if (scan_debug_types == false) + scan_debug_types = true; + else + { + __libdw_seterrno (INTUSE(dwarf_errno) () + ?: DWARF_E_INVALID_REFERENCE); + return NULL; + } + } + } + while (cu == NULL || cu->unit_id8 != sig); + } + + int secid = cu_sec_idx (cu); + datap = cu->dbg->sectiondata[secid]->d_buf; + size = cu->dbg->sectiondata[secid]->d_size; + offset = cu->start + cu->subdie_offset; } else { diff --git a/libdw/dwarf_formsdata.c b/libdw/dwarf_formsdata.c index e7deaee1..def32c9d 100644 --- a/libdw/dwarf_formsdata.c +++ b/libdw/dwarf_formsdata.c @@ -1,5 +1,5 @@ /* Return signed constant represented by attribute. - Copyright (C) 2003, 2005, 2014 Red Hat, Inc. + Copyright (C) 2003, 2005, 2014, 2017 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <[email protected]>, 2003. @@ -53,25 +53,25 @@ dwarf_formsdata (Dwarf_Attribute *attr, Dwarf_Sword *return_sval) __libdw_seterrno (DWARF_E_INVALID_DWARF); return -1; } - *return_sval = *attr->valp; + *return_sval = (signed char) *attr->valp; break; case DW_FORM_data2: if (datap + 2 > endp) goto invalid; - *return_sval = read_2ubyte_unaligned (attr->cu->dbg, attr->valp); + *return_sval = read_2sbyte_unaligned (attr->cu->dbg, attr->valp); break; case DW_FORM_data4: if (datap + 4 > endp) goto invalid; - *return_sval = read_4ubyte_unaligned (attr->cu->dbg, attr->valp); + *return_sval = read_4sbyte_unaligned (attr->cu->dbg, attr->valp); break; case DW_FORM_data8: if (datap + 8 > endp) goto invalid; - *return_sval = read_8ubyte_unaligned (attr->cu->dbg, attr->valp); + *return_sval = read_8sbyte_unaligned (attr->cu->dbg, attr->valp); break; case DW_FORM_sdata: @@ -86,6 +86,11 @@ dwarf_formsdata (Dwarf_Attribute *attr, Dwarf_Sword *return_sval) get_uleb128 (*return_sval, datap, endp); break; + case DW_FORM_implicit_const: + // The data comes from the abbrev, which has been bounds checked. + get_sleb128_unchecked (*return_sval, datap); + break; + default: __libdw_seterrno (DWARF_E_NO_CONSTANT); return -1; diff --git a/libdw/dwarf_formstring.c b/libdw/dwarf_formstring.c index 83abd53d..c3e892a8 100644 --- a/libdw/dwarf_formstring.c +++ b/libdw/dwarf_formstring.c @@ -1,7 +1,6 @@ /* Return string associated with given attribute. - Copyright (C) 2003-2010, 2013 Red Hat, Inc. + Copyright (C) 2003-2010, 2013, 2017, 2018 Red Hat, Inc. This file is part of elfutils. - Written by Ulrich Drepper <[email protected]>, 2003. This file is free software; you can redistribute it and/or modify it under the terms of either @@ -47,8 +46,11 @@ dwarf_formstring (Dwarf_Attribute *attrp) /* A simple inlined string. */ return (const char *) attrp->valp; - Dwarf *dbg = attrp->cu->dbg; - Dwarf *dbg_ret = attrp->form == DW_FORM_GNU_strp_alt ? dbg->alt_dwarf : dbg; + Dwarf_CU *cu = attrp->cu; + Dwarf *dbg = cu->dbg; + Dwarf *dbg_ret = ((attrp->form == DW_FORM_GNU_strp_alt + || attrp->form == DW_FORM_strp_sup) + ? INTUSE(dwarf_getalt) (dbg) : dbg); if (unlikely (dbg_ret == NULL)) { @@ -56,20 +58,123 @@ dwarf_formstring (Dwarf_Attribute *attrp) return NULL; } - - if (unlikely (attrp->form != DW_FORM_strp - && attrp->form != DW_FORM_GNU_strp_alt) - || dbg_ret->sectiondata[IDX_debug_str] == NULL) + Elf_Data *data = ((attrp->form == DW_FORM_line_strp) + ? dbg_ret->sectiondata[IDX_debug_line_str] + : dbg_ret->sectiondata[IDX_debug_str]); + if (data == NULL) { - __libdw_seterrno (DWARF_E_NO_STRING); + __libdw_seterrno ((attrp->form == DW_FORM_line_strp) + ? DWARF_E_NO_DEBUG_LINE_STR + : DWARF_E_NO_DEBUG_STR); return NULL; } uint64_t off; - if (__libdw_read_offset (dbg, dbg_ret, cu_sec_idx (attrp->cu), attrp->valp, - attrp->cu->offset_size, &off, IDX_debug_str, 1)) - return NULL; + if (attrp->form == DW_FORM_strp + || attrp->form == DW_FORM_GNU_strp_alt + || attrp->form == DW_FORM_strp_sup) + { + if (__libdw_read_offset (dbg, dbg_ret, cu_sec_idx (cu), + attrp->valp, cu->offset_size, &off, + IDX_debug_str, 1)) + return NULL; + } + else if (attrp->form == DW_FORM_line_strp) + { + if (__libdw_read_offset (dbg, dbg_ret, cu_sec_idx (cu), + attrp->valp, cu->offset_size, &off, + IDX_debug_line_str, 1)) + return NULL; + } + else + { + Dwarf_Word idx; + const unsigned char *datap = attrp->valp; + const unsigned char *endp = cu->endp; + switch (attrp->form) + { + case DW_FORM_strx: + case DW_FORM_GNU_str_index: + if (datap >= endp) + { + invalid: + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return NULL; + } + get_uleb128 (idx, datap, endp); + break; + + case DW_FORM_strx1: + if (datap >= endp - 1) + goto invalid; + idx = *datap; + break; + + case DW_FORM_strx2: + if (datap >= endp - 2) + goto invalid; + idx = read_2ubyte_unaligned (dbg, datap); + break; + + case DW_FORM_strx3: + if (datap >= endp - 3) + goto invalid; + idx = read_3ubyte_unaligned (dbg, datap); + break; + + case DW_FORM_strx4: + if (datap >= endp - 4) + goto invalid; + idx = read_4ubyte_unaligned (dbg, datap); + break; + + default: + __libdw_seterrno (DWARF_E_NO_STRING); + return NULL; + } + + /* So we got an index in the .debug_str_offsets. Lets see if it + is valid and we can get the actual .debug_str offset. */ + Dwarf_Off str_off = __libdw_cu_str_off_base (cu); + if (str_off == (Dwarf_Off) -1) + return NULL; + + if (dbg->sectiondata[IDX_debug_str_offsets] == NULL) + { + __libdw_seterrno (DWARF_E_NO_STR_OFFSETS); + return NULL; + } + + /* The section should at least contain room for one offset. */ + int offset_size = cu->offset_size; + if (cu->offset_size > dbg->sectiondata[IDX_debug_str_offsets]->d_size) + { + invalid_offset: + __libdw_seterrno (DWARF_E_INVALID_OFFSET); + return NULL; + } + + /* And the base offset should be at least inside the section. */ + if (str_off > (dbg->sectiondata[IDX_debug_str_offsets]->d_size + - offset_size)) + goto invalid_offset; + + size_t max_idx = (dbg->sectiondata[IDX_debug_str_offsets]->d_size + - offset_size - str_off) / offset_size; + if (idx > max_idx) + goto invalid_offset; + + datap = (dbg->sectiondata[IDX_debug_str_offsets]->d_buf + + str_off + (idx * offset_size)); + if (offset_size == 4) + off = read_4ubyte_unaligned (dbg, datap); + else + off = read_8ubyte_unaligned (dbg, datap); + + if (off > dbg->sectiondata[IDX_debug_str]->d_size) + goto invalid_offset; + } - return (const char *) dbg_ret->sectiondata[IDX_debug_str]->d_buf + off; + return (const char *) data->d_buf + off; } INTDEF(dwarf_formstring) diff --git a/libdw/dwarf_formudata.c b/libdw/dwarf_formudata.c index e41981a4..26f86f12 100644 --- a/libdw/dwarf_formudata.c +++ b/libdw/dwarf_formudata.c @@ -1,5 +1,5 @@ /* Return unsigned constant represented by attribute. - Copyright (C) 2003-2012, 2014 Red Hat, Inc. + Copyright (C) 2003-2012, 2014, 2017 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <[email protected]>, 2003. @@ -34,15 +34,26 @@ #include <dwarf.h> #include "libdwP.h" -internal_function unsigned char * +internal_function const unsigned char * __libdw_formptr (Dwarf_Attribute *attr, int sec_index, - int err_nodata, unsigned char **endpp, + int err_nodata, const unsigned char **endpp, Dwarf_Off *offsetp) { if (attr == NULL) return NULL; const Elf_Data *d = attr->cu->dbg->sectiondata[sec_index]; + Dwarf_CU *skel = NULL; /* See below, needed for GNU DebugFission. */ + if (unlikely (d == NULL + && sec_index == IDX_debug_ranges + && attr->cu->version < 5 + && attr->cu->unit_type == DW_UT_split_compile)) + { + skel = __libdw_find_split_unit (attr->cu); + if (skel != NULL) + d = skel->dbg->sectiondata[IDX_debug_ranges]; + } + if (unlikely (d == NULL)) { __libdw_seterrno (err_nodata); @@ -52,10 +63,41 @@ __libdw_formptr (Dwarf_Attribute *attr, int sec_index, Dwarf_Word offset; if (attr->form == DW_FORM_sec_offset) { - if (__libdw_read_offset (attr->cu->dbg, attr->cu->dbg, - cu_sec_idx (attr->cu), attr->valp, - attr->cu->offset_size, &offset, sec_index, 0)) - return NULL; + /* GNU DebugFission is slightly odd. It uses DW_FORM_sec_offset + in split units, but they are really (unrelocated) offsets + from the skeleton DW_AT_GNU_ranges_base (which is only used + for the split unit, not the skeleton ranges itself, see also + DW_AT_rnglists_base, which is used in DWARF5 for both, but + points to the offsets index). So it isn't really a formptr, + but an offset + base calculation. */ + if (unlikely (skel != NULL)) + { + Elf_Data *data = attr->cu->dbg->sectiondata[cu_sec_idx (attr->cu)]; + const unsigned char *datap = attr->valp; + size_t size = attr->cu->offset_size; + if (unlikely (data == NULL + || datap < (const unsigned char *) data->d_buf + || data->d_size < size + || ((size_t) (datap + - (const unsigned char *) data->d_buf) + > data->d_size - size))) + goto invalid; + + if (size == 4) + offset = read_4ubyte_unaligned (attr->cu->dbg, datap); + else + offset = read_8ubyte_unaligned (attr->cu->dbg, datap); + + offset += __libdw_cu_ranges_base (skel); + } + else + { + if (__libdw_read_offset (attr->cu->dbg, attr->cu->dbg, + cu_sec_idx (attr->cu), attr->valp, + attr->cu->offset_size, &offset, + sec_index, 0)) + return NULL; + } } else if (attr->cu->version > 3) goto invalid; @@ -141,11 +183,24 @@ dwarf_formudata (Dwarf_Attribute *attr, Dwarf_Word *return_uval) case DW_AT_string_length: case DW_AT_use_location: case DW_AT_vtable_elem_location: - /* loclistptr */ - if (__libdw_formptr (attr, IDX_debug_loc, - DWARF_E_NO_LOCLIST, NULL, - return_uval) == NULL) - return -1; + case DW_AT_GNU_locviews: + case DW_AT_loclists_base: + if (attr->cu->version < 5) + { + /* loclistptr */ + if (__libdw_formptr (attr, IDX_debug_loc, + DWARF_E_NO_DEBUG_LOC, NULL, + return_uval) == NULL) + return -1; + } + else + { + /* loclist, loclistsptr */ + if (__libdw_formptr (attr, IDX_debug_loclists, + DWARF_E_NO_DEBUG_LOCLISTS, NULL, + return_uval) == NULL) + return -1; + } break; case DW_AT_macro_info: @@ -157,6 +212,7 @@ dwarf_formudata (Dwarf_Attribute *attr, Dwarf_Word *return_uval) break; case DW_AT_GNU_macros: + case DW_AT_macros: /* macptr into .debug_macro */ if (__libdw_formptr (attr, IDX_debug_macro, DWARF_E_NO_ENTRY, NULL, @@ -166,11 +222,24 @@ dwarf_formudata (Dwarf_Attribute *attr, Dwarf_Word *return_uval) case DW_AT_ranges: case DW_AT_start_scope: - /* rangelistptr */ - if (__libdw_formptr (attr, IDX_debug_ranges, - DWARF_E_NO_DEBUG_RANGES, NULL, - return_uval) == NULL) - return -1; + case DW_AT_GNU_ranges_base: + case DW_AT_rnglists_base: + if (attr->cu->version < 5) + { + /* rangelistptr */ + if (__libdw_formptr (attr, IDX_debug_ranges, + DWARF_E_NO_DEBUG_RANGES, NULL, + return_uval) == NULL) + return -1; + } + else + { + /* rnglistsptr */ + if (__libdw_formptr (attr, IDX_debug_rnglists, + DWARF_E_NO_DEBUG_RNGLISTS, NULL, + return_uval) == NULL) + return -1; + } break; case DW_AT_stmt_list: @@ -181,6 +250,23 @@ dwarf_formudata (Dwarf_Attribute *attr, Dwarf_Word *return_uval) return -1; break; + case DW_AT_addr_base: + case DW_AT_GNU_addr_base: + /* addrptr */ + if (__libdw_formptr (attr, IDX_debug_addr, + DWARF_E_NO_DEBUG_ADDR, NULL, + return_uval) == NULL) + return -1; + break; + + case DW_AT_str_offsets_base: + /* stroffsetsptr */ + if (__libdw_formptr (attr, IDX_debug_str_offsets, + DWARF_E_NO_STR_OFFSETS, NULL, + return_uval) == NULL) + return -1; + break; + default: /* sec_offset can only be used by one of the above attrs. */ if (attr->form == DW_FORM_sec_offset) @@ -216,11 +302,51 @@ dwarf_formudata (Dwarf_Attribute *attr, Dwarf_Word *return_uval) break; case DW_FORM_udata: + case DW_FORM_rnglistx: + case DW_FORM_loclistx: if (datap + 1 > endp) goto invalid; get_uleb128 (*return_uval, datap, endp); break; + case DW_FORM_implicit_const: + // The data comes from the abbrev, which has been bounds checked. + get_sleb128_unchecked (*return_uval, datap); + break; + + /* These are indexes into the .debug_addr section, normally resolved + with dwarf_formaddr. Here treat as constants. */ + case DW_FORM_GNU_addr_index: + case DW_FORM_addrx: + if (datap >= endp) + goto invalid; + get_uleb128 (*return_uval, datap, endp); + break; + + case DW_FORM_addrx1: + if (datap >= endp - 1) + goto invalid; + *return_uval = *datap; + break; + + case DW_FORM_addrx2: + if (datap >= endp - 2) + goto invalid; + *return_uval = read_2ubyte_unaligned (attr->cu->dbg, datap); + break; + + case DW_FORM_addrx3: + if (datap >= endp - 3) + goto invalid; + *return_uval = read_3ubyte_unaligned (attr->cu->dbg, datap); + break; + + case DW_FORM_addrx4: + if (datap >= endp - 4) + goto invalid; + *return_uval = read_4ubyte_unaligned (attr->cu->dbg, datap); + break; + default: __libdw_seterrno (DWARF_E_NO_CONSTANT); return -1; diff --git a/libdw/dwarf_frame_register.c b/libdw/dwarf_frame_register.c index 37e8e917..d0159fb8 100644 --- a/libdw/dwarf_frame_register.c +++ b/libdw/dwarf_frame_register.c @@ -62,7 +62,7 @@ dwarf_frame_register (Dwarf_Frame *fs, int regno, Dwarf_Op *ops_mem, /* Use the default rule for registers not yet mentioned in CFI. */ if (fs->cache->default_same_value) goto same_value; - /*FALLTHROUGH*/ + FALLTHROUGH; case reg_undefined: /* The value is known to be unavailable. */ break; diff --git a/libdw/dwarf_get_units.c b/libdw/dwarf_get_units.c new file mode 100644 index 00000000..6215bf4b --- /dev/null +++ b/libdw/dwarf_get_units.c @@ -0,0 +1,131 @@ +/* Iterate through the CU units for a given Dwarf. + Copyright (C) 2016, 2017 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see <http://www.gnu.org/licenses/>. */ + + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <string.h> + +#include "libdwP.h" + +int +dwarf_get_units (Dwarf *dwarf, Dwarf_CU *cu, Dwarf_CU **next_cu, + Dwarf_Half *version, uint8_t *unit_type, + Dwarf_Die *cudie, Dwarf_Die *subdie) +{ + /* Handle existing error. */ + if (dwarf == NULL) + return -1; + + Dwarf_Off off; + bool v4type; + if (cu == NULL) + { + off = 0; + v4type = false; + } + else + { + off = cu->end; + v4type = cu->sec_idx != IDX_debug_info; + + /* Make sure we got a real (not fake) CU. */ + if (cu->sec_idx != IDX_debug_info && cu->sec_idx != IDX_debug_types) + { + __libdw_seterrno (DWARF_E_INVALID_OFFSET); + return -1; + } + + /* Do we have to switch to the other section, or are we at the end? */ + if (! v4type) + { + if (off >= cu->dbg->sectiondata[IDX_debug_info]->d_size) + { + if (cu->dbg->sectiondata[IDX_debug_types] == NULL) + return 1; + + off = 0; + v4type = true; + } + } + else + if (off >= cu->dbg->sectiondata[IDX_debug_types]->d_size) + return 1; + } + + *next_cu = __libdw_findcu (dwarf, off, v4type); + if (*next_cu == NULL) + return -1; + + Dwarf_CU *next = (*next_cu); + + if (version != NULL) + *version = next->version; + + if (unit_type != NULL) + *unit_type = next->unit_type; + + if (cudie != NULL) + { + if (next->version >= 2 && next->version <= 5 + && next->unit_type >= DW_UT_compile + && next->unit_type <= DW_UT_split_type) + *cudie = CUDIE (next); + else + memset (cudie, '\0', sizeof (Dwarf_Die)); + } + + if (subdie != NULL) + { + if (next->version >= 2 && next->version <= 5) + { + /* For types, return the actual type DIE. For skeletons, + find the associated split compile unit and return its + DIE. */ + if (next->unit_type == DW_UT_type + || next->unit_type == DW_UT_split_type) + *subdie = SUBDIE(next); + else if (next->unit_type == DW_UT_skeleton) + { + Dwarf_CU *split_cu = __libdw_find_split_unit (next); + if (split_cu != NULL) + *subdie = CUDIE(split_cu); + else + memset (subdie, '\0', sizeof (Dwarf_Die)); + } + else + memset (subdie, '\0', sizeof (Dwarf_Die)); + } + else + memset (subdie, '\0', sizeof (Dwarf_Die)); + } + + return 0; +} diff --git a/libdw/dwarf_getabbrev.c b/libdw/dwarf_getabbrev.c index ef51b847..988d12c2 100644 --- a/libdw/dwarf_getabbrev.c +++ b/libdw/dwarf_getabbrev.c @@ -1,5 +1,5 @@ /* Get abbreviation at given offset. - Copyright (C) 2003, 2004, 2005, 2006, 2014 Red Hat, Inc. + Copyright (C) 2003, 2004, 2005, 2006, 2014, 2017 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <[email protected]>, 2003. @@ -121,8 +121,7 @@ __libdw_getabbrev (Dwarf *dbg, struct Dwarf_CU *cu, Dwarf_Off offset, abb->attrp = (unsigned char *) abbrevp; abb->offset = offset; - /* Skip over all the attributes and count them while doing so. */ - abb->attrcnt = 0; + /* Skip over all the attributes and check rest of the abbrev is valid. */ unsigned int attrname; unsigned int attrform; do @@ -133,8 +132,15 @@ __libdw_getabbrev (Dwarf *dbg, struct Dwarf_CU *cu, Dwarf_Off offset, if (abbrevp >= end) goto invalid; get_uleb128 (attrform, abbrevp, end); + if (attrform == DW_FORM_implicit_const) + { + int64_t formval __attribute__((__unused__)); + if (abbrevp >= end) + goto invalid; + get_sleb128 (formval, abbrevp, end); + } } - while (attrname != 0 && attrform != 0 && ++abb->attrcnt); + while (attrname != 0 && attrform != 0); /* Return the length to the caller if she asked for it. */ if (lengthp != NULL) @@ -152,7 +158,21 @@ __libdw_getabbrev (Dwarf *dbg, struct Dwarf_CU *cu, Dwarf_Off offset, Dwarf_Abbrev * dwarf_getabbrev (Dwarf_Die *die, Dwarf_Off offset, size_t *lengthp) { - return __libdw_getabbrev (die->cu->dbg, die->cu, - die->cu->orig_abbrev_offset + offset, lengthp, - NULL); + if (die == NULL || die->cu == NULL) + return NULL; + + Dwarf_CU *cu = die->cu; + Dwarf *dbg = cu->dbg; + Dwarf_Off abbrev_offset = cu->orig_abbrev_offset; + Elf_Data *data = dbg->sectiondata[IDX_debug_abbrev]; + if (data == NULL) + return NULL; + + if (offset >= data->d_size - abbrev_offset) + { + __libdw_seterrno (DWARF_E_INVALID_OFFSET); + return NULL; + } + + return __libdw_getabbrev (dbg, cu, abbrev_offset + offset, lengthp, NULL); } diff --git a/libdw/dwarf_getabbrevattr.c b/libdw/dwarf_getabbrevattr.c index 3b4da99c..54ff604f 100644 --- a/libdw/dwarf_getabbrevattr.c +++ b/libdw/dwarf_getabbrevattr.c @@ -1,5 +1,5 @@ /* Get specific attribute of abbreviation. - Copyright (C) 2003, 2004, 2005, 2014 Red Hat, Inc. + Copyright (C) 2003, 2004, 2005, 2014, 2017 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <[email protected]>, 2003. @@ -37,8 +37,9 @@ int -dwarf_getabbrevattr (Dwarf_Abbrev *abbrev, size_t idx, unsigned int *namep, - unsigned int *formp, Dwarf_Off *offsetp) +dwarf_getabbrevattr_data (Dwarf_Abbrev *abbrev, size_t idx, + unsigned int *namep, unsigned int *formp, + Dwarf_Sword *datap, Dwarf_Off *offsetp) { if (abbrev == NULL) return -1; @@ -48,15 +49,21 @@ dwarf_getabbrevattr (Dwarf_Abbrev *abbrev, size_t idx, unsigned int *namep, const unsigned char *start_attrp; unsigned int name; unsigned int form; + Dwarf_Word data; do { start_attrp = attrp; - /* Attribute code and form are encoded as ULEB128 values.i - XXX We have no way to bounds check. */ - get_uleb128 (name, attrp, attrp + len_leb128 (name)); - get_uleb128 (form, attrp, attrp + len_leb128 (form)); + /* Attribute code and form are encoded as ULEB128 values. + Already checked when Dwarf_Abbrev was created, read unchecked. */ + get_uleb128_unchecked (name, attrp); + get_uleb128_unchecked (form, attrp); + + if (form == DW_FORM_implicit_const) + get_sleb128_unchecked (data, attrp); + else + data = 0; /* If both values are zero the index is out of range. */ if (name == 0 && form == 0) @@ -69,8 +76,19 @@ dwarf_getabbrevattr (Dwarf_Abbrev *abbrev, size_t idx, unsigned int *namep, *namep = name; if (formp != NULL) *formp = form; + if (datap != NULL) + *datap = data; if (offsetp != NULL) *offsetp = (start_attrp - abbrev->attrp) + abbrev->offset; return 0; } +INTDEF(dwarf_getabbrevattr_data) + +int +dwarf_getabbrevattr (Dwarf_Abbrev *abbrev, size_t idx, unsigned int *namep, + unsigned int *formp, Dwarf_Off *offsetp) +{ + return INTUSE(dwarf_getabbrevattr_data) (abbrev, idx, namep, formp, + NULL, offsetp); +} diff --git a/libdw/dwarf_getalt.c b/libdw/dwarf_getalt.c index cc434f03..0a12dfae 100644 --- a/libdw/dwarf_getalt.c +++ b/libdw/dwarf_getalt.c @@ -1,5 +1,5 @@ /* Retrieves the DWARF descriptor for debugaltlink data. - Copyright (C) 2014 Red Hat, Inc. + Copyright (C) 2014, 2018 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -31,12 +31,154 @@ #endif #include "libdwP.h" +#include "libelfP.h" +#include "libdwelfP.h" +#include "system.h" + +#include <inttypes.h> +#include <fcntl.h> +#include <limits.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> + + +char * +internal_function +__libdw_filepath (const char *debugdir, const char *dir, const char *file) +{ + if (file == NULL) + return NULL; + + if (file[0] == '/') + return strdup (file); + + if (dir != NULL && dir[0] == '/') + { + size_t dirlen = strlen (dir); + size_t filelen = strlen (file); + size_t len = dirlen + 1 + filelen + 1; + char *path = malloc (len); + if (path != NULL) + { + char *c = mempcpy (path, dir, dirlen); + if (dir[dirlen - 1] != '/') + *c++ = '/'; + mempcpy (c, file, filelen + 1); + } + return path; + } + + if (debugdir != NULL) + { + size_t debugdirlen = strlen (debugdir); + size_t dirlen = dir != NULL ? strlen (dir) : 0; + size_t filelen = strlen (file); + size_t len = debugdirlen + 1 + dirlen + 1 + filelen + 1; + char *path = malloc (len); + if (path != NULL) + { + char *c = mempcpy (path, debugdir, debugdirlen); + if (dirlen > 0) + { + c = mempcpy (c, dir, dirlen); + if (dir[dirlen - 1] != '/') + *c++ = '/'; + } + mempcpy (c, file, filelen + 1); + return path; + } + } + + return NULL; +} + +static void +find_debug_altlink (Dwarf *dbg) +{ + const char *altname; + const void *build_id; + ssize_t build_id_len = INTUSE(dwelf_dwarf_gnu_debugaltlink) (dbg, + &altname, + &build_id); + + /* Couldn't even get the debugaltlink. It probably doesn't exist. */ + if (build_id_len <= 0) + return; + + const uint8_t *id = (const uint8_t *) build_id; + size_t id_len = build_id_len; + int fd = -1; + + /* We only look in the standard path. And relative to the dbg file. */ +#define DEBUGINFO_PATH "/usr/lib/debug" + + /* We don't handle very short or really large build-ids. We need at + at least 3 and allow for up to 64 (normally ids are 20 long). */ +#define MIN_BUILD_ID_BYTES 3 +#define MAX_BUILD_ID_BYTES 64 + if (id_len >= MIN_BUILD_ID_BYTES && id_len <= MAX_BUILD_ID_BYTES) + { + /* Note sizeof a string literal includes the trailing zero. */ + char id_path[sizeof DEBUGINFO_PATH - 1 + sizeof "/.build-id/" - 1 + + 2 + 1 + (MAX_BUILD_ID_BYTES - 1) * 2 + sizeof ".debug"]; + sprintf (&id_path[0], "%s%s", DEBUGINFO_PATH, "/.build-id/"); + sprintf (&id_path[sizeof DEBUGINFO_PATH - 1 + sizeof "/.build-id/" - 1], + "%02" PRIx8 "/", (uint8_t) id[0]); + for (size_t i = 1; i < id_len; ++i) + sprintf (&id_path[sizeof DEBUGINFO_PATH - 1 + sizeof "/.build-id/" - 1 + + 3 + (i - 1) * 2], "%02" PRIx8, (uint8_t) id[i]); + strcpy (&id_path[sizeof DEBUGINFO_PATH - 1 + sizeof "/.build-id/" - 1 + + 3 + (id_len - 1) * 2], ".debug"); + + fd = TEMP_FAILURE_RETRY (open (id_path, O_RDONLY)); + } + + /* Fall back on (possible relative) alt file path. */ + if (fd < 0) + { + char *altpath = __libdw_filepath (dbg->debugdir, NULL, altname); + if (altpath != NULL) + { + fd = TEMP_FAILURE_RETRY (open (altpath, O_RDONLY)); + free (altpath); + } + } + + if (fd >= 0) + { + Dwarf *alt = dwarf_begin (fd, O_RDONLY); + if (alt != NULL) + { + dbg->alt_dwarf = alt; + dbg->alt_fd = fd; + } + else + close (fd); + } +} Dwarf * dwarf_getalt (Dwarf *main) { - if (main == NULL) + /* Only try once. */ + if (main == NULL || main->alt_dwarf == (void *) -1) return NULL; + + if (main->alt_dwarf != NULL) + return main->alt_dwarf; + + find_debug_altlink (main); + + /* If we found nothing, make sure we don't try again. */ + if (main->alt_dwarf == NULL) + { + main->alt_dwarf = (void *) -1; + return NULL; + } + return main->alt_dwarf; } INTDEF (dwarf_getalt) diff --git a/libdw/dwarf_getaranges.c b/libdw/dwarf_getaranges.c index 4252746e..bff9c860 100644 --- a/libdw/dwarf_getaranges.c +++ b/libdw/dwarf_getaranges.c @@ -1,5 +1,5 @@ /* Return list address ranges. - Copyright (C) 2000-2010 Red Hat, Inc. + Copyright (C) 2000-2010, 2016, 2017 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <[email protected]>, 2000. @@ -195,16 +195,15 @@ dwarf_getaranges (Dwarf *dbg, Dwarf_Aranges **aranges, size_t *naranges) new_arange->arange.length = range_length; /* We store the actual CU DIE offset, not the CU header offset. */ - const char *cu_header = (dbg->sectiondata[IDX_debug_info]->d_buf - + offset); - unsigned int offset_size; - if (read_4ubyte_unaligned_noncvt (cu_header) == DWARF3_LENGTH_64_BIT) - offset_size = 8; - else - offset_size = 4; - new_arange->arange.offset = DIE_OFFSET_FROM_CU_OFFSET (offset, - offset_size, - false); + Dwarf_CU *cu = __libdw_findcu (dbg, offset, false); + if (unlikely (cu == NULL)) + { + /* We haven't gotten a chance to link in the new_arange + into the arangelist, don't leak it. */ + free (new_arange); + goto fail; + } + new_arange->arange.offset = __libdw_first_die_off_from_cu (cu); new_arange->next = arangelist; arangelist = new_arange; diff --git a/libdw/dwarf_getattrcnt.c b/libdw/dwarf_getattrcnt.c index 2bfb4ac5..a05976d4 100644 --- a/libdw/dwarf_getattrcnt.c +++ b/libdw/dwarf_getattrcnt.c @@ -1,5 +1,5 @@ /* Get number of attributes of abbreviation. - Copyright (C) 2003, 2004 Red Hat, Inc. + Copyright (C) 2003, 2004, 2017 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <[email protected]>, 2003. @@ -40,7 +40,22 @@ dwarf_getattrcnt (Dwarf_Abbrev *abbrev, size_t *attrcntp) if (abbrev == NULL) return -1; - *attrcntp = abbrev->attrcnt; + const unsigned char *abbrevp = abbrev->attrp; + + /* Skip over all the attributes and count them while doing so. */ + int attrcnt = 0; + unsigned int attrname; + unsigned int attrform; + do + { + /* We can use unchecked since they were checked when the Dwrf_Abbrev + was created. */ + get_uleb128_unchecked (attrname, abbrevp); + get_uleb128_unchecked (attrform, abbrevp); + } + while (attrname != 0 && attrform != 0 && ++attrcnt); + + *attrcntp = attrcnt; return 0; } diff --git a/libdw/dwarf_getattrs.c b/libdw/dwarf_getattrs.c index 0da8b5ba..50faf988 100644 --- a/libdw/dwarf_getattrs.c +++ b/libdw/dwarf_getattrs.c @@ -1,5 +1,5 @@ /* Get attributes of the DIE. - Copyright (C) 2004, 2005, 2008, 2009, 2014 Red Hat, Inc. + Copyright (C) 2004, 2005, 2008, 2009, 2014, 2017 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <[email protected]>, 2004. @@ -51,7 +51,6 @@ dwarf_getattrs (Dwarf_Die *die, int (*callback) (Dwarf_Attribute *, void *), if (unlikely (abbrevp == DWARF_END_ABBREV)) { - invalid_dwarf: __libdw_seterrno (DWARF_E_INVALID_DWARF); return -1l; } @@ -61,24 +60,15 @@ dwarf_getattrs (Dwarf_Die *die, int (*callback) (Dwarf_Attribute *, void *), const unsigned char *const offset_attrp = abbrevp->attrp + offset; /* Go over the list of attributes. */ - Dwarf *dbg = die->cu->dbg; - const unsigned char *endp; - endp = ((const unsigned char *) dbg->sectiondata[IDX_debug_abbrev]->d_buf - + dbg->sectiondata[IDX_debug_abbrev]->d_size); while (1) { - /* Are we still in bounds? */ - if (unlikely (attrp >= endp)) - goto invalid_dwarf; - - /* Get attribute name and form. */ + /* Get attribute name and form. Dwarf_Abbrev was checked when + created, so we can read unchecked. */ Dwarf_Attribute attr; const unsigned char *remembered_attrp = attrp; - get_uleb128 (attr.code, attrp, endp); - if (unlikely (attrp >= endp)) - goto invalid_dwarf; - get_uleb128 (attr.form, attrp, endp); + get_uleb128_unchecked (attr.code, attrp); + get_uleb128_unchecked (attr.form, attrp); /* We can stop if we found the attribute with value zero. */ if (attr.code == 0 && attr.form == 0) @@ -93,7 +83,10 @@ dwarf_getattrs (Dwarf_Die *die, int (*callback) (Dwarf_Attribute *, void *), if (remembered_attrp >= offset_attrp) { /* Fill in the rest. */ - attr.valp = (unsigned char *) die_addr; + if (attr.form == DW_FORM_implicit_const) + attr.valp = (unsigned char *) attrp; + else + attr.valp = (unsigned char *) die_addr; attr.cu = die->cu; /* Now call the callback function. */ @@ -114,6 +107,12 @@ dwarf_getattrs (Dwarf_Die *die, int (*callback) (Dwarf_Attribute *, void *), // __libdw_form_val_len will have done a bounds check. die_addr += len; + + if (attr.form == DW_FORM_implicit_const) + { + int64_t attr_value __attribute__((__unused__)); + get_sleb128_unchecked (attr_value, attrp); + } } } /* NOTREACHED */ diff --git a/libdw/dwarf_getlocation.c b/libdw/dwarf_getlocation.c index a4a2761e..fc59a2ab 100644 --- a/libdw/dwarf_getlocation.c +++ b/libdw/dwarf_getlocation.c @@ -1,7 +1,6 @@ /* Return location expression list. - Copyright (C) 2000-2010, 2013-2015 Red Hat, Inc. + Copyright (C) 2000-2010, 2013-2015, 2017, 2018 Red Hat, Inc. This file is part of elfutils. - Written by Ulrich Drepper <[email protected]>, 2000. This file is free software; you can redistribute it and/or modify it under the terms of either @@ -45,10 +44,34 @@ attr_ok (Dwarf_Attribute *attr) if (attr == NULL) return false; - /* Must be one of the attributes listed below. */ + /* If it is an exprloc, it is obviously OK. */ + if (dwarf_whatform (attr) == DW_FORM_exprloc) + return true; + + /* Otherwise must be one of the attributes listed below. Older + DWARF versions might have encoded the exprloc as block, and we + cannot easily distinquish attributes in the loclist class because + the same forms are used for different classes. */ switch (attr->code) { case DW_AT_location: + case DW_AT_byte_size: + case DW_AT_bit_offset: + case DW_AT_bit_size: + case DW_AT_lower_bound: + case DW_AT_bit_stride: + case DW_AT_upper_bound: + case DW_AT_count: + case DW_AT_allocated: + case DW_AT_associated: + case DW_AT_data_location: + case DW_AT_byte_stride: + case DW_AT_rank: + case DW_AT_call_value: + case DW_AT_call_target: + case DW_AT_call_target_clobbered: + case DW_AT_call_data_location: + case DW_AT_call_data_value: case DW_AT_data_member_location: case DW_AT_vtable_elem_location: case DW_AT_string_length: @@ -64,7 +87,7 @@ attr_ok (Dwarf_Attribute *attr) break; default: - __libdw_seterrno (DWARF_E_NO_LOCLIST); + __libdw_seterrno (DWARF_E_NO_LOC_VALUE); return false; } @@ -97,19 +120,23 @@ loc_compare (const void *p1, const void *p2) } /* For each DW_OP_implicit_value, we store a special entry in the cache. - This points us directly to the block data for later fetching. */ -static void + This points us directly to the block data for later fetching. + Returns zero on success, -1 on bad DWARF or 1 if tsearch failed. */ +static int store_implicit_value (Dwarf *dbg, void **cache, Dwarf_Op *op) { struct loc_block_s *block = libdw_alloc (dbg, struct loc_block_s, sizeof (struct loc_block_s), 1); const unsigned char *data = (const unsigned char *) (uintptr_t) op->number2; - // Ignored, equal to op->number. And data length already checked. - (void) __libdw_get_uleb128 (&data, data + len_leb128 (Dwarf_Word)); + uint64_t len = __libdw_get_uleb128 (&data, data + len_leb128 (Dwarf_Word)); + if (unlikely (len != op->number)) + return -1; block->addr = op; block->data = (unsigned char *) data; block->length = op->number; - (void) tsearch (block, cache, loc_compare); + if (unlikely (tsearch (block, cache, loc_compare) == NULL)) + return 1; + return 0; } int @@ -147,6 +174,8 @@ check_constant_offset (Dwarf_Attribute *attr, default: return 1; + /* Note, we don't regard DW_FORM_data16 as a constant form, + even though technically it is according to the standard. */ case DW_FORM_data1: case DW_FORM_data2: case DW_FORM_data4: @@ -299,6 +328,7 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order, break; case DW_OP_call_ref: + case DW_OP_GNU_variable_value: /* DW_FORM_ref_addr, depends on offset size of CU. */ if (dbg == NULL || __libdw_read_offset_inc (dbg, sec_index, &data, ref_size, @@ -427,8 +457,14 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order, case DW_OP_plus_uconst: case DW_OP_regx: case DW_OP_piece: + case DW_OP_convert: case DW_OP_GNU_convert: + case DW_OP_reinterpret: case DW_OP_GNU_reinterpret: + case DW_OP_addrx: + case DW_OP_GNU_addr_index: + case DW_OP_constx: + case DW_OP_GNU_const_index: get_uleb128 (newloc->number, data, end_data); break; @@ -446,6 +482,7 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order, break; case DW_OP_bit_piece: + case DW_OP_regval_type: case DW_OP_GNU_regval_type: get_uleb128 (newloc->number, data, end_data); if (unlikely (data >= end_data)) @@ -454,6 +491,7 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order, break; case DW_OP_implicit_value: + case DW_OP_entry_value: case DW_OP_GNU_entry_value: /* This cannot be used in a CFI expression. */ if (unlikely (dbg == NULL)) @@ -467,6 +505,7 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order, data += newloc->number; /* Skip the block. */ break; + case DW_OP_implicit_pointer: case DW_OP_GNU_implicit_pointer: /* DW_FORM_ref_addr, depends on offset size of CU. */ if (dbg == NULL || __libdw_read_offset_inc (dbg, sec_index, &data, @@ -479,13 +518,16 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order, get_uleb128 (newloc->number2, data, end_data); /* Byte offset. */ break; + case DW_OP_deref_type: case DW_OP_GNU_deref_type: + case DW_OP_xderef_type: if (unlikely (data + 1 >= end_data)) goto invalid; newloc->number = *data++; get_uleb128 (newloc->number2, data, end_data); break; + case DW_OP_const_type: case DW_OP_GNU_const_type: { size_t size; @@ -553,7 +595,16 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order, result[n].offset = loclist->offset; if (result[n].atom == DW_OP_implicit_value) - store_implicit_value (dbg, cache, &result[n]); + { + int store = store_implicit_value (dbg, cache, &result[n]); + if (unlikely (store != 0)) + { + if (store < 0) + goto invalid; + else + goto nomem; + } + } struct loclist *loc = loclist; loclist = loclist->next; @@ -616,7 +667,13 @@ dwarf_getlocation (Dwarf_Attribute *attr, Dwarf_Op **llbuf, size_t *listlen) if (result != 1) return result; - /* If it has a block form, it's a single location expression. */ + /* If it has a block form, it's a single location expression. + Except for DW_FORM_data16, which is a 128bit constant. */ + if (attr->form == DW_FORM_data16) + { + __libdw_seterrno (DWARF_E_NO_BLOCK); + return -1; + } Dwarf_Block block; if (INTUSE(dwarf_formblock) (attr, &block) != 0) return -1; @@ -624,47 +681,113 @@ dwarf_getlocation (Dwarf_Attribute *attr, Dwarf_Op **llbuf, size_t *listlen) return getlocation (attr->cu, &block, llbuf, listlen, cu_sec_idx (attr->cu)); } -static int -attr_base_address (Dwarf_Attribute *attr, Dwarf_Addr *basep) +Dwarf_Addr +__libdw_cu_base_address (Dwarf_CU *cu) { - /* Fetch the CU's base address. */ - Dwarf_Die cudie = CUDIE (attr->cu); - - /* Find the base address of the compilation unit. It will - normally be specified by DW_AT_low_pc. In DWARF-3 draft 4, - the base address could be overridden by DW_AT_entry_pc. It's - been removed, but GCC emits DW_AT_entry_pc and not DW_AT_lowpc - for compilation units with discontinuous ranges. */ - Dwarf_Attribute attr_mem; - if (unlikely (INTUSE(dwarf_lowpc) (&cudie, basep) != 0) - && INTUSE(dwarf_formaddr) (INTUSE(dwarf_attr) (&cudie, - DW_AT_entry_pc, - &attr_mem), - basep) != 0) + if (cu->base_address == (Dwarf_Addr) -1) { - if (INTUSE(dwarf_errno) () != 0) - return -1; - - /* The compiler provided no base address when it should - have. Buggy GCC does this when it used absolute - addresses in the location list and no DW_AT_ranges. */ - *basep = 0; + Dwarf_Addr base; + + /* Fetch the CU's base address. */ + Dwarf_Die cudie = CUDIE (cu); + + /* Find the base address of the compilation unit. It will + normally be specified by DW_AT_low_pc. In DWARF-3 draft 4, + the base address could be overridden by DW_AT_entry_pc. It's + been removed, but GCC emits DW_AT_entry_pc and not DW_AT_lowpc + for compilation units with discontinuous ranges. */ + Dwarf_Attribute attr_mem; + if (INTUSE(dwarf_lowpc) (&cudie, &base) != 0 + && INTUSE(dwarf_formaddr) (INTUSE(dwarf_attr) (&cudie, + DW_AT_entry_pc, + &attr_mem), + &base) != 0) + { + /* The compiler provided no base address when it should + have. Buggy GCC does this when it used absolute + addresses in the location list and no DW_AT_ranges. */ + base = 0; + } + cu->base_address = base; } - return 0; + + return cu->base_address; } static int -initial_offset_base (Dwarf_Attribute *attr, ptrdiff_t *offset, - Dwarf_Addr *basep) +initial_offset (Dwarf_Attribute *attr, ptrdiff_t *offset) { - if (attr_base_address (attr, basep) != 0) - return -1; + size_t secidx = (attr->cu->version < 5 + ? IDX_debug_loc : IDX_debug_loclists); Dwarf_Word start_offset; - if (__libdw_formptr (attr, IDX_debug_loc, - DWARF_E_NO_LOCLIST, - NULL, &start_offset) == NULL) - return -1; + if (attr->form == DW_FORM_loclistx) + { + Dwarf_Word idx; + Dwarf_CU *cu = attr->cu; + const unsigned char *datap = attr->valp; + const unsigned char *endp = cu->endp; + if (datap >= endp) + { + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return -1; + } + get_uleb128 (idx, datap, endp); + + Elf_Data *data = cu->dbg->sectiondata[secidx]; + if (data == NULL && cu->unit_type == DW_UT_split_compile) + { + cu = __libdw_find_split_unit (cu); + if (cu != NULL) + data = cu->dbg->sectiondata[secidx]; + } + + if (data == NULL) + { + __libdw_seterrno (secidx == IDX_debug_loc + ? DWARF_E_NO_DEBUG_LOC + : DWARF_E_NO_DEBUG_LOCLISTS); + return -1; + } + + Dwarf_Off loc_base_off = __libdw_cu_locs_base (cu); + + /* The section should at least contain room for one offset. */ + size_t sec_size = cu->dbg->sectiondata[secidx]->d_size; + size_t offset_size = cu->offset_size; + if (offset_size > sec_size) + { + invalid_offset: + __libdw_seterrno (DWARF_E_INVALID_OFFSET); + return -1; + } + + /* And the base offset should be at least inside the section. */ + if (loc_base_off > (sec_size - offset_size)) + goto invalid_offset; + + size_t max_idx = (sec_size - offset_size - loc_base_off) / offset_size; + if (idx > max_idx) + goto invalid_offset; + + datap = (cu->dbg->sectiondata[secidx]->d_buf + + loc_base_off + (idx * offset_size)); + if (offset_size == 4) + start_offset = read_4ubyte_unaligned (cu->dbg, datap); + else + start_offset = read_8ubyte_unaligned (cu->dbg, datap); + + start_offset += loc_base_off; + } + else + { + if (__libdw_formptr (attr, secidx, + (secidx == IDX_debug_loc + ? DWARF_E_NO_DEBUG_LOC + : DWARF_E_NO_DEBUG_LOCLISTS), + NULL, &start_offset) == NULL) + return -1; + } *offset = start_offset; return 0; @@ -676,22 +799,19 @@ getlocations_addr (Dwarf_Attribute *attr, ptrdiff_t offset, Dwarf_Addr address, const Elf_Data *locs, Dwarf_Op **expr, size_t *exprlen) { - unsigned char *readp = locs->d_buf + offset; - unsigned char *readendp = locs->d_buf + locs->d_size; - - next: - if (readendp - readp < attr->cu->address_size * 2) - { - invalid: - __libdw_seterrno (DWARF_E_INVALID_DWARF); - return -1; - } + Dwarf_CU *cu = attr->cu; + Dwarf *dbg = cu->dbg; + size_t secidx = cu->version < 5 ? IDX_debug_loc : IDX_debug_loclists; + const unsigned char *readp = locs->d_buf + offset; + const unsigned char *readendp = locs->d_buf + locs->d_size; Dwarf_Addr begin; Dwarf_Addr end; - switch (__libdw_read_begin_end_pair_inc (attr->cu->dbg, IDX_debug_loc, - &readp, attr->cu->address_size, + next: + switch (__libdw_read_begin_end_pair_inc (cu, secidx, + &readp, readendp, + cu->address_size, &begin, &end, basep)) { case 0: /* got location range. */ @@ -704,25 +824,38 @@ getlocations_addr (Dwarf_Attribute *attr, ptrdiff_t offset, return -1; } - if (readendp - readp < 2) - goto invalid; - /* We have a location expression. */ Dwarf_Block block; - block.length = read_2ubyte_unaligned_inc (attr->cu->dbg, readp); - block.data = readp; + if (secidx == IDX_debug_loc) + { + if (readendp - readp < 2) + { + invalid: + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return -1; + } + block.length = read_2ubyte_unaligned_inc (dbg, readp); + } + else + { + if (readendp - readp < 1) + goto invalid; + get_uleb128 (block.length, readp, readendp); + } + block.data = (unsigned char *) readp; if (readendp - readp < (ptrdiff_t) block.length) goto invalid; readp += block.length; - *startp = *basep + begin; - *endp = *basep + end; + /* Note these addresses include any base (if necessary) already. */ + *startp = begin; + *endp = end; /* If address is minus one we want them all, otherwise only matching. */ if (address != (Dwarf_Word) -1 && (address < *startp || address >= *endp)) goto next; - if (getlocation (attr->cu, &block, expr, exprlen, IDX_debug_loc) != 0) + if (getlocation (cu, &block, expr, exprlen, secidx) != 0) return -1; return readp - (unsigned char *) locs->d_buf; @@ -738,9 +871,11 @@ dwarf_getlocation_addr (Dwarf_Attribute *attr, Dwarf_Addr address, if (llbufs == NULL) maxlocs = SIZE_MAX; - /* If it has a block form, it's a single location expression. */ + /* If it has a block form, it's a single location expression. + Except for DW_FORM_data16, which is a 128bit constant. */ Dwarf_Block block; - if (INTUSE(dwarf_formblock) (attr, &block) == 0) + if (attr->form != DW_FORM_data16 + && INTUSE(dwarf_formblock) (attr, &block) == 0) { if (maxlocs == 0) return 0; @@ -751,11 +886,14 @@ dwarf_getlocation_addr (Dwarf_Attribute *attr, Dwarf_Addr address, return listlens[0] == 0 ? 0 : 1; } - int error = INTUSE(dwarf_errno) (); - if (unlikely (error != DWARF_E_NO_BLOCK)) + if (attr->form != DW_FORM_data16) { - __libdw_seterrno (error); - return -1; + int error = INTUSE(dwarf_errno) (); + if (unlikely (error != DWARF_E_NO_BLOCK)) + { + __libdw_seterrno (error); + return -1; + } } int result = check_constant_offset (attr, &llbufs[0], &listlens[0]); @@ -769,19 +907,19 @@ dwarf_getlocation_addr (Dwarf_Attribute *attr, Dwarf_Addr address, size_t got = 0; /* This is a true loclistptr, fetch the initial base address and offset. */ - if (initial_offset_base (attr, &off, &base) != 0) + base = __libdw_cu_base_address (attr->cu); + if (base == (Dwarf_Addr) -1) return -1; - const Elf_Data *d = attr->cu->dbg->sectiondata[IDX_debug_loc]; - if (d == NULL) - { - __libdw_seterrno (DWARF_E_NO_LOCLIST); - return -1; - } + if (initial_offset (attr, &off) != 0) + return -1; + + size_t secidx = attr->cu->version < 5 ? IDX_debug_loc : IDX_debug_loclists; + const Elf_Data *d = attr->cu->dbg->sectiondata[secidx]; while (got < maxlocs && (off = getlocations_addr (attr, off, &base, &start, &end, - address, d, &expr, &expr_len)) > 0) + address, d, &expr, &expr_len)) > 0) { /* This one matches the address. */ if (llbufs != NULL) @@ -813,9 +951,11 @@ dwarf_getlocations (Dwarf_Attribute *attr, ptrdiff_t offset, Dwarf_Addr *basep, if (offset == 0) { - /* If it has a block form, it's a single location expression. */ + /* If it has a block form, it's a single location expression. + Except for DW_FORM_data16, which is a 128bit constant. */ Dwarf_Block block; - if (INTUSE(dwarf_formblock) (attr, &block) == 0) + if (attr->form != DW_FORM_data16 + && INTUSE(dwarf_formblock) (attr, &block) == 0) { if (getlocation (attr->cu, &block, expr, exprlen, cu_sec_idx (attr->cu)) != 0) @@ -827,11 +967,14 @@ dwarf_getlocations (Dwarf_Attribute *attr, ptrdiff_t offset, Dwarf_Addr *basep, return 1; } - int error = INTUSE(dwarf_errno) (); - if (unlikely (error != DWARF_E_NO_BLOCK)) + if (attr->form != DW_FORM_data16) { - __libdw_seterrno (error); - return -1; + int error = INTUSE(dwarf_errno) (); + if (unlikely (error != DWARF_E_NO_BLOCK)) + { + __libdw_seterrno (error); + return -1; + } } int result = check_constant_offset (attr, expr, exprlen); @@ -849,17 +992,17 @@ dwarf_getlocations (Dwarf_Attribute *attr, ptrdiff_t offset, Dwarf_Addr *basep, /* We must be looking at a true loclistptr, fetch the initial base address and offset. */ - if (initial_offset_base (attr, &offset, basep) != 0) + *basep = __libdw_cu_base_address (attr->cu); + if (*basep == (Dwarf_Addr) -1) return -1; - } - const Elf_Data *d = attr->cu->dbg->sectiondata[IDX_debug_loc]; - if (d == NULL) - { - __libdw_seterrno (DWARF_E_NO_LOCLIST); - return -1; + if (initial_offset (attr, &offset) != 0) + return -1; } + size_t secidx = attr->cu->version < 5 ? IDX_debug_loc : IDX_debug_loclists; + const Elf_Data *d = attr->cu->dbg->sectiondata[secidx]; + return getlocations_addr (attr, offset, basep, startp, endp, (Dwarf_Word) -1, d, expr, exprlen); } diff --git a/libdw/dwarf_getlocation_attr.c b/libdw/dwarf_getlocation_attr.c index 8b6a4afd..99bcc828 100644 --- a/libdw/dwarf_getlocation_attr.c +++ b/libdw/dwarf_getlocation_attr.c @@ -1,5 +1,5 @@ /* Return DWARF attribute associated with a location expression op. - Copyright (C) 2013, 2014 Red Hat, Inc. + Copyright (C) 2013, 2014, 2017, 2018 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -38,7 +38,7 @@ attr_form_cu (Dwarf_Attribute *attr) { /* If the attribute has block/expr form the data comes from the .debug_info from the same cu as the attr. Otherwise it comes from - the .debug_loc data section. */ + the .debug_loc or .debug_loclists data section. */ switch (attr->form) { case DW_FORM_block1: @@ -48,10 +48,26 @@ attr_form_cu (Dwarf_Attribute *attr) case DW_FORM_exprloc: return attr->cu; default: - return attr->cu->dbg->fake_loc_cu; + return (attr->cu->version < 5 + ? attr->cu->dbg->fake_loc_cu + : attr->cu->dbg->fake_loclists_cu); } } +static unsigned char * +addr_valp (Dwarf_CU *cu, Dwarf_Word index) +{ + Elf_Data *debug_addr = cu->dbg->sectiondata[IDX_debug_addr]; + if (debug_addr == NULL) + { + __libdw_seterrno (DWARF_E_NO_DEBUG_ADDR); + return NULL; + } + + Dwarf_Word offset = __libdw_cu_addr_base (cu) + (index * cu->address_size); + return (unsigned char *) debug_addr->d_buf + offset; +} + int dwarf_getlocation_attr (Dwarf_Attribute *attr, const Dwarf_Op *op, Dwarf_Attribute *result) { @@ -67,6 +83,7 @@ dwarf_getlocation_attr (Dwarf_Attribute *attr, const Dwarf_Op *op, Dwarf_Attribu result->cu = attr_form_cu (attr); break; + case DW_OP_entry_value: case DW_OP_GNU_entry_value: result->code = DW_AT_location; result->form = DW_FORM_exprloc; @@ -74,6 +91,7 @@ dwarf_getlocation_attr (Dwarf_Attribute *attr, const Dwarf_Op *op, Dwarf_Attribu result->cu = attr_form_cu (attr); break; + case DW_OP_const_type: case DW_OP_GNU_const_type: result->code = DW_AT_const_value; result->form = DW_FORM_block1; @@ -81,6 +99,29 @@ dwarf_getlocation_attr (Dwarf_Attribute *attr, const Dwarf_Op *op, Dwarf_Attribu result->cu = attr_form_cu (attr); break; + case DW_OP_GNU_const_index: + case DW_OP_constx: + result->code = DW_AT_const_value; + if (attr->cu->address_size == 4) + result->form = DW_FORM_data4; + else + result->form = DW_FORM_data8; + result->valp = addr_valp (attr->cu, op->number); + if (result->valp == NULL) + return -1; + result->cu = attr->cu->dbg->fake_addr_cu; + break; + + case DW_OP_GNU_addr_index: + case DW_OP_addrx: + result->code = DW_AT_low_pc; + result->form = DW_FORM_addr; + result->valp = addr_valp (attr->cu, op->number); + if (result->valp == NULL) + return -1; + result->cu = attr->cu->dbg->fake_addr_cu; + break; + case DW_OP_call2: case DW_OP_call4: case DW_OP_call_ref: @@ -96,7 +137,9 @@ dwarf_getlocation_attr (Dwarf_Attribute *attr, const Dwarf_Op *op, Dwarf_Attribu } break; + case DW_OP_implicit_pointer: case DW_OP_GNU_implicit_pointer: + case DW_OP_GNU_variable_value: { Dwarf_Die die; if (INTUSE(dwarf_getlocation_die) (attr, op, &die) != 0) diff --git a/libdw/dwarf_getlocation_die.c b/libdw/dwarf_getlocation_die.c index b4908d2b..673c61cf 100644 --- a/libdw/dwarf_getlocation_die.c +++ b/libdw/dwarf_getlocation_die.c @@ -1,5 +1,5 @@ /* Return DIE associated with a location expression op. - Copyright (C) 2013 Red Hat, Inc. + Copyright (C) 2013, 2017 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -43,32 +43,51 @@ dwarf_getlocation_die (Dwarf_Attribute *attr, const Dwarf_Op *op, Dwarf_Off dieoff; switch (op->atom) { + case DW_OP_implicit_pointer: case DW_OP_GNU_implicit_pointer: case DW_OP_call_ref: + case DW_OP_GNU_variable_value: dieoff = op->number; break; case DW_OP_GNU_parameter_ref: + case DW_OP_convert: case DW_OP_GNU_convert: + case DW_OP_reinterpret: case DW_OP_GNU_reinterpret: + case DW_OP_const_type: case DW_OP_GNU_const_type: case DW_OP_call2: case DW_OP_call4: + if (op->number > (attr->cu->end - attr->cu->start)) + { + invalid_offset: + __libdw_seterrno (DWARF_E_INVALID_OFFSET); + return -1; + } dieoff = attr->cu->start + op->number; break; + case DW_OP_regval_type: case DW_OP_GNU_regval_type: + case DW_OP_deref_type: case DW_OP_GNU_deref_type: + if (op->number2 > (attr->cu->end - attr->cu->start)) + goto invalid_offset; dieoff = attr->cu->start + op->number2; break; + case DW_OP_xderef_type: + dieoff = op->number2; + break; + default: __libdw_seterrno (DWARF_E_INVALID_ACCESS); return -1; } if (__libdw_offdie (attr->cu->dbg, dieoff, result, - attr->cu->type_offset != 0) == NULL) + ISV4TU(attr->cu)) == NULL) return -1; return 0; diff --git a/libdw/dwarf_getlocation_implicit_pointer.c b/libdw/dwarf_getlocation_implicit_pointer.c index 95053820..0c1cd00a 100644 --- a/libdw/dwarf_getlocation_implicit_pointer.c +++ b/libdw/dwarf_getlocation_implicit_pointer.c @@ -1,5 +1,5 @@ /* Return associated attribute for DW_OP_GNU_implicit_pointer. - Copyright (C) 2010 Red Hat, Inc. + Copyright (C) 2010, 2017 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -55,7 +55,8 @@ dwarf_getlocation_implicit_pointer (Dwarf_Attribute *attr, const Dwarf_Op *op, if (attr == NULL) return -1; - if (unlikely (op->atom != DW_OP_GNU_implicit_pointer)) + if (unlikely (op->atom != DW_OP_implicit_pointer + && op->atom != DW_OP_GNU_implicit_pointer)) { __libdw_seterrno (DWARF_E_INVALID_ACCESS); return -1; @@ -63,7 +64,7 @@ dwarf_getlocation_implicit_pointer (Dwarf_Attribute *attr, const Dwarf_Op *op, Dwarf_Die die; if (__libdw_offdie (attr->cu->dbg, op->number, &die, - attr->cu->type_offset != 0) == NULL) + ISV4TU(attr->cu)) == NULL) return -1; if (INTUSE(dwarf_attr) (&die, DW_AT_location, result) == NULL diff --git a/libdw/dwarf_getmacros.c b/libdw/dwarf_getmacros.c index db6582b6..fd929669 100644 --- a/libdw/dwarf_getmacros.c +++ b/libdw/dwarf_getmacros.c @@ -1,7 +1,6 @@ /* Get macro information. - Copyright (C) 2002-2009, 2014 Red Hat, Inc. + Copyright (C) 2002-2009, 2014, 2017, 2018 Red Hat, Inc. This file is part of elfutils. - Written by Ulrich Drepper <[email protected]>, 2002. This file is free software; you can redistribute it and/or modify it under the terms of either @@ -192,6 +191,8 @@ get_table_for_offset (Dwarf *dbg, Dwarf_Word macoff, MACRO_PROTO (p_udata_str, DW_FORM_udata, DW_FORM_string); MACRO_PROTO (p_udata_strp, DW_FORM_udata, DW_FORM_strp); + MACRO_PROTO (p_udata_strsup, DW_FORM_udata, DW_FORM_strp_sup); + MACRO_PROTO (p_udata_strx, DW_FORM_udata, DW_FORM_strx); MACRO_PROTO (p_udata_udata, DW_FORM_udata, DW_FORM_udata); MACRO_PROTO (p_secoffset, DW_FORM_sec_offset); MACRO_PROTO (p_none); @@ -205,10 +206,11 @@ get_table_for_offset (Dwarf *dbg, Dwarf_Word macoff, [DW_MACRO_start_file - 1] = p_udata_udata, [DW_MACRO_end_file - 1] = p_none, [DW_MACRO_import - 1] = p_secoffset, - /* When adding support for DWARF5 supplementary object files and - indirect string tables also add support for DW_MACRO_define_sup, - DW_MACRO_undef_sup, DW_MACRO_import_sup, DW_MACRO_define_strx - and DW_MACRO_undef_strx. */ + [DW_MACRO_define_sup - 1] = p_udata_strsup, + [DW_MACRO_undef_sup - 1] = p_udata_strsup, + [DW_MACRO_import_sup - 1] = p_secoffset, /* XXX - but in sup!. */ + [DW_MACRO_define_strx - 1] = p_udata_strx, + [DW_MACRO_undef_strx - 1] = p_udata_strx, }; if ((flags & 0x4) != 0) @@ -357,11 +359,18 @@ read_macros (Dwarf *dbg, int sec_index, /* A fake CU with bare minimum data to fool dwarf_formX into doing the right thing with the attributes that we put out. We pretend it is the same version as the actual table. - Version 4 for the old GNU extension, version 5 for DWARF5. */ + Version 4 for the old GNU extension, version 5 for DWARF5. + To handle DW_FORM_strx[1234] we set the .str_offsets_base + from the given CU. + XXX We will need to deal with DW_MACRO_import_sup and change + out the dbg somehow for the DW_FORM_sec_offset to make sense. */ Dwarf_CU fake_cu = { .dbg = dbg, + .sec_idx = sec_index, .version = table->version, .offset_size = table->is_64bit ? 8 : 4, + .str_off_base = str_offsets_base_off (dbg, (cudie != NULL + ? cudie->cu: NULL)), .startp = (void *) startp + offset, .endp = (void *) endp, }; @@ -384,14 +393,25 @@ read_macros (Dwarf *dbg, int sec_index, for (Dwarf_Word i = 0; i < proto->nforms; ++i) { - /* We pretend this is a DW_AT_GNU_macros attribute so that + /* We pretend this is a DW_AT[_GNU]_macros attribute so that DW_FORM_sec_offset forms get correctly interpreted as - offset into .debug_macro. */ - attributes[i].code = DW_AT_GNU_macros; + offset into .debug_macro. XXX Deal with DW_MACRO_import_sup + (swap .dbg) for DW_FORM_sec_offset? */ + attributes[i].code = (fake_cu.version == 4 ? DW_AT_GNU_macros + : DW_AT_macros); attributes[i].form = proto->forms[i]; attributes[i].valp = (void *) readp; attributes[i].cu = &fake_cu; + /* We don't want forms that aren't allowed because they could + read from the "abbrev" like DW_FORM_implicit_const. */ + if (! libdw_valid_user_form (attributes[i].form)) + { + __libdw_seterrno (DWARF_E_INVALID_DWARF); + free (attributesp); + return -1; + } + size_t len = __libdw_form_val_len (&fake_cu, proto->forms[i], readp); if (unlikely (len == (size_t) -1)) { @@ -561,7 +581,8 @@ dwarf_getmacros (Dwarf_Die *cudie, int (*callback) (Dwarf_Macro *, void *), { /* DW_AT_GNU_macros, DW_AT_macros */ Dwarf_Word macoff; - if (get_offset_from (cudie, DW_AT_GNU_macros, &macoff) != 0) + if (get_offset_from (cudie, DW_AT_GNU_macros, &macoff) != 0 + && get_offset_from (cudie, DW_AT_macros, &macoff) != 0) return -1; offset = gnu_macros_getmacros_off (cudie->cu->dbg, macoff, callback, arg, offset, accept_0xff, diff --git a/libdw/dwarf_getscopes.c b/libdw/dwarf_getscopes.c index df480d33..5662eecf 100644 --- a/libdw/dwarf_getscopes.c +++ b/libdw/dwarf_getscopes.c @@ -62,7 +62,9 @@ pc_match (unsigned int depth, struct Dwarf_Die_Chain *die, void *arg) if (result < 0) { int error = INTUSE(dwarf_errno) (); - if (error != DWARF_E_NOERROR && error != DWARF_E_NO_DEBUG_RANGES) + if (error != DWARF_E_NOERROR + && error != DWARF_E_NO_DEBUG_RANGES + && error != DWARF_E_NO_DEBUG_RNGLISTS) { __libdw_seterrno (error); return -1; diff --git a/libdw/dwarf_getsrcfiles.c b/libdw/dwarf_getsrcfiles.c index 5af6f68b..12fdabf2 100644 --- a/libdw/dwarf_getsrcfiles.c +++ b/libdw/dwarf_getsrcfiles.c @@ -1,7 +1,6 @@ /* Return source file information of CU. - Copyright (C) 2004, 2005, 2013, 2015 Red Hat, Inc. + Copyright (C) 2004, 2005, 2013, 2015, 2018 Red Hat, Inc. This file is part of elfutils. - Written by Ulrich Drepper <[email protected]>, 2004. This file is free software; you can redistribute it and/or modify it under the terms of either @@ -51,14 +50,47 @@ dwarf_getsrcfiles (Dwarf_Die *cudie, Dwarf_Files **files, size_t *nfiles) /* Get the information if it is not already known. */ struct Dwarf_CU *const cu = cudie->cu; - if (cu->lines == NULL) + if (cu->files == NULL) { - Dwarf_Lines *lines; - size_t nlines; - - /* Let the more generic function do the work. It'll create more - data but that will be needed in an real program anyway. */ - res = INTUSE(dwarf_getsrclines) (cudie, &lines, &nlines); + /* For split units there might be a simple file table (without lines). + If not, use the one from the skeleton. */ + if (cu->unit_type == DW_UT_split_compile + || cu->unit_type == DW_UT_split_type) + { + /* We tried, assume we fail... */ + cu->files = (void *) -1; + + /* See if there is a .debug_line section, for split CUs + the table is at offset zero. */ + if (cu->dbg->sectiondata[IDX_debug_line] != NULL) + { + /* We are only interested in the files, the lines will + always come from the skeleton. */ + res = __libdw_getsrclines (cu->dbg, 0, + __libdw_getcompdir (cudie), + cu->address_size, NULL, + &cu->files); + } + else + { + Dwarf_CU *skel = __libdw_find_split_unit (cu); + if (skel != NULL) + { + Dwarf_Die skeldie = CUDIE (skel); + res = INTUSE(dwarf_getsrcfiles) (&skeldie, files, nfiles); + cu->files = skel->files; + } + } + } + else + { + Dwarf_Lines *lines; + size_t nlines; + + /* Let the more generic function do the work. It'll create more + data but that will be needed in an real program anyway. */ + res = INTUSE(dwarf_getsrclines) (cudie, &lines, &nlines); + } } else if (cu->files != (void *) -1l) /* We already have the information. */ diff --git a/libdw/dwarf_getsrclines.c b/libdw/dwarf_getsrclines.c index d02c38db..1432b1db 100644 --- a/libdw/dwarf_getsrclines.c +++ b/libdw/dwarf_getsrclines.c @@ -1,7 +1,6 @@ /* Return line number information of CU. - Copyright (C) 2004-2010, 2013, 2014, 2015, 2016 Red Hat, Inc. + Copyright (C) 2004-2010, 2013, 2014, 2015, 2016, 2018 Red Hat, Inc. This file is part of elfutils. - Written by Ulrich Drepper <[email protected]>, 2004. This file is free software; you can redistribute it and/or modify it under the terms of either @@ -154,20 +153,9 @@ read_srclines (Dwarf *dbg, { int res = -1; + struct filelist *filelist = NULL; size_t nfilelist = 0; - unsigned int ndirlist = 0; - - struct filelist null_file = - { - .info = - { - .name = "???", - .mtime = 0, - .length = 0 - }, - .next = NULL - }; - struct filelist *filelist = &null_file; + size_t ndirlist = 0; /* If there are a large number of lines, files or dirs don't blow up the stack. Stack allocate some entries, only dynamically malloc @@ -177,16 +165,7 @@ read_srclines (Dwarf *dbg, #define MAX_STACK_FILES (MAX_STACK_ALLOC / 4) #define MAX_STACK_DIRS (MAX_STACK_ALLOC / 16) - struct dirlist - { - const char *dir; - size_t len; - }; - struct dirlist dirstack[MAX_STACK_DIRS]; - struct dirlist *dirarray = dirstack; - - /* We are about to process the statement program. Initialize the - state machine registers (see 6.2.2 in the v2.1 specification). */ + /* Initial statement program state (except for stmt_list, see below). */ struct line_state state = { .linelist = NULL, @@ -204,6 +183,17 @@ read_srclines (Dwarf *dbg, .discriminator = 0 }; + /* The dirs normally go on the stack, but if there are too many + we alloc them all. Set up stack storage early, so we can check on + error if we need to free them or not. */ + struct dirlist + { + const char *dir; + size_t len; + }; + struct dirlist dirstack[MAX_STACK_DIRS]; + struct dirlist *dirarray = dirstack; + if (unlikely (linep + 4 > lineendp)) { invalid_data: @@ -222,25 +212,45 @@ read_srclines (Dwarf *dbg, } /* Check whether we have enough room in the section. */ - if (unlikely (unit_length > (size_t) (lineendp - linep) - || unit_length < 2 + length + 5 * 1)) + if (unlikely (unit_length > (size_t) (lineendp - linep))) goto invalid_data; lineendp = linep + unit_length; /* The next element of the header is the version identifier. */ + if ((size_t) (lineendp - linep) < 2) + goto invalid_data; uint_fast16_t version = read_2ubyte_unaligned_inc (dbg, linep); - if (unlikely (version < 2) || unlikely (version > 4)) + if (unlikely (version < 2) || unlikely (version > 5)) { __libdw_seterrno (DWARF_E_VERSION); goto out; } + /* DWARF5 explicitly lists address and segment_selector sizes. */ + if (version >= 5) + { + if ((size_t) (lineendp - linep) < 2) + goto invalid_data; + size_t line_address_size = *linep++; + size_t segment_selector_size = *linep++; + if (line_address_size != address_size || segment_selector_size != 0) + goto invalid_data; + } + /* Next comes the header length. */ Dwarf_Word header_length; if (length == 4) - header_length = read_4ubyte_unaligned_inc (dbg, linep); + { + if ((size_t) (lineendp - linep) < 4) + goto invalid_data; + header_length = read_4ubyte_unaligned_inc (dbg, linep); + } else - header_length = read_8ubyte_unaligned_inc (dbg, linep); + { + if ((size_t) (lineendp - linep) < 8) + goto invalid_data; + header_length = read_8ubyte_unaligned_inc (dbg, linep); + } const unsigned char *header_start = linep; /* Next the minimum instruction length. */ @@ -250,13 +260,17 @@ read_srclines (Dwarf *dbg, uint_fast8_t max_ops_per_instr = 1; if (version >= 4) { - if (unlikely (lineendp - linep < 5)) + if (unlikely ((size_t) (lineendp - linep) < 1)) goto invalid_data; max_ops_per_instr = *linep++; if (unlikely (max_ops_per_instr == 0)) goto invalid_data; } + /* 4 more bytes, is_stmt, line_base, line_range and opcode_base. */ + if ((size_t) (lineendp - linep) < 4) + goto invalid_data; + /* Then the flag determining the default value of the is_stmt register. */ uint_fast8_t default_is_stmt = *linep++; @@ -277,31 +291,85 @@ read_srclines (Dwarf *dbg, goto invalid_data; linep += opcode_base - 1; - /* First comes the list of directories. Add the compilation - directory first since the index zero is used for it. */ - struct dirlist comp_dir_elem = - { - .dir = comp_dir, - .len = comp_dir ? strlen (comp_dir) : 0, - }; - ndirlist = 1; + /* To read DWARF5 dir and file lists we need to know the forms. For + now we skip everything, except the DW_LNCT_path and + DW_LNCT_directory_index. */ + uint16_t forms[256]; + unsigned char nforms = 0; + unsigned char form_path = -1; /* Which forms is DW_LNCT_path. */ + unsigned char form_idx = -1; /* And which is DW_LNCT_directory_index. */ + + /* To read/skip form data. */ + Dwarf_CU fake_cu = { + .dbg = dbg, + .sec_idx = IDX_debug_line, + .version = 5, + .offset_size = length, + .address_size = address_size, + .startp = (void *) linep, + .endp = (void *) lineendp, + }; /* First count the entries. */ - const unsigned char *dirp = linep; - unsigned int ndirs = 0; - while (*dirp != 0) + size_t ndirs = 0; + if (version < 5) + { + const unsigned char *dirp = linep; + while (*dirp != 0) + { + uint8_t *endp = memchr (dirp, '\0', lineendp - dirp); + if (endp == NULL) + goto invalid_data; + ++ndirs; + dirp = endp + 1; + } + ndirs = ndirs + 1; /* There is always the "unknown" dir. */ + } + else { - uint8_t *endp = memchr (dirp, '\0', lineendp - dirp); - if (endp == NULL) + if ((size_t) (lineendp - linep) < 1) + goto invalid_data; + nforms = *linep++; + for (int i = 0; i < nforms; i++) + { + uint16_t desc, form; + if ((size_t) (lineendp - linep) < 1) + goto invalid_data; + get_uleb128 (desc, linep, lineendp); + if ((size_t) (lineendp - linep) < 1) + goto invalid_data; + get_uleb128 (form, linep, lineendp); + + if (! libdw_valid_user_form (form)) + goto invalid_data; + + forms[i] = form; + if (desc == DW_LNCT_path) + form_path = i; + } + + if (nforms > 0 && form_path == (unsigned char) -1) + goto invalid_data; + + if ((size_t) (lineendp - linep) < 1) + goto invalid_data; + get_uleb128 (ndirs, linep, lineendp); + + if (nforms == 0 && ndirs != 0) + goto invalid_data; + + /* Assume there is at least 1 byte needed per form to describe + the directory. Filters out insanely large ndirs. */ + if (nforms != 0 && ndirs > (size_t) (lineendp - linep) / nforms) goto invalid_data; - ++ndirs; - dirp = endp + 1; } - ndirlist += ndirs; /* Arrange the list in array form. */ + ndirlist = ndirs; if (ndirlist >= MAX_STACK_DIRS) { + if (ndirlist > SIZE_MAX / sizeof (*dirarray)) + goto no_mem; dirarray = (struct dirlist *) malloc (ndirlist * sizeof (*dirarray)); if (unlikely (dirarray == NULL)) { @@ -310,20 +378,82 @@ read_srclines (Dwarf *dbg, goto out; } } - dirarray[0] = comp_dir_elem; - for (unsigned int n = 1; n < ndirlist; n++) + + /* Entry zero is implicit for older versions, but explicit for 5+. */ + struct dirlist comp_dir_elem; + if (version < 5) { - dirarray[n].dir = (char *) linep; - uint8_t *endp = memchr (linep, '\0', lineendp - linep); - assert (endp != NULL); - dirarray[n].len = endp - linep; - linep = endp + 1; + /* First comes the list of directories. Add the compilation + directory first since the index zero is used for it. */ + comp_dir_elem.dir = comp_dir; + comp_dir_elem.len = comp_dir ? strlen (comp_dir) : 0, + dirarray[0] = comp_dir_elem; + for (unsigned int n = 1; n < ndirlist; n++) + { + dirarray[n].dir = (char *) linep; + uint8_t *endp = memchr (linep, '\0', lineendp - linep); + assert (endp != NULL); + dirarray[n].len = endp - linep; + linep = endp + 1; + } + /* Skip the final NUL byte. */ + ++linep; + } + else + { + Dwarf_Attribute attr; + attr.code = DW_AT_name; + attr.cu = &fake_cu; + for (unsigned int n = 0; n < ndirlist; n++) + { + const char *dir = NULL; + for (unsigned char m = 0; m < nforms; m++) + { + if (m == form_path) + { + attr.form = forms[m]; + attr.valp = (void *) linep; + dir = dwarf_formstring (&attr); + } + + size_t len = __libdw_form_val_len (&fake_cu, forms[m], linep); + if ((size_t) (lineendp - linep) < len) + goto invalid_data; + + linep += len; + } + + if (dir == NULL) + goto invalid_data; + + dirarray[n].dir = dir; + dirarray[n].len = strlen (dir); + } } - /* Skip the final NUL byte. */ - ++linep; + + /* File index zero doesn't exist for DWARF < 5. Files are indexed + starting from 1. But for DWARF5 they are indexed starting from + zero, but the default index is still 1. In both cases the + "first" file is special and refers to the main compile unit file, + equal to the DW_AT_name of the DW_TAG_compile_unit. */ + struct filelist null_file = + { + .info = + { + .name = "???", + .mtime = 0, + .length = 0 + }, + .next = NULL + }; + filelist = &null_file; + nfilelist = 1; /* Allocate memory for a new file. For the first MAX_STACK_FILES - entries just return a slot in the preallocated stack array. */ + entries just return a slot in the preallocated stack array. + This is slightly complicated because in DWARF < 5 new files could + be defined with DW_LNE_define_file after the normal file list was + read. */ struct filelist flstack[MAX_STACK_FILES]; #define NEW_FILE() ({ \ struct filelist *fl = (nfilelist < MAX_STACK_FILES \ @@ -337,69 +467,181 @@ read_srclines (Dwarf *dbg, fl; }) /* Now read the files. */ - nfilelist = 1; - - if (unlikely (linep >= lineendp)) - goto invalid_data; - while (*linep != 0) + if (version < 5) { - struct filelist *new_file = NEW_FILE (); - - /* First comes the file name. */ - char *fname = (char *) linep; - uint8_t *endp = memchr (fname, '\0', lineendp - linep); - if (endp == NULL) - goto invalid_data; - size_t fnamelen = endp - (uint8_t *) fname; - linep = endp + 1; - - /* Then the index. */ - Dwarf_Word diridx; if (unlikely (linep >= lineendp)) goto invalid_data; - get_uleb128 (diridx, linep, lineendp); - if (unlikely (diridx >= ndirlist)) + while (*linep != 0) { - __libdw_seterrno (DWARF_E_INVALID_DIR_IDX); - goto out; - } + struct filelist *new_file = NEW_FILE (); - if (*fname == '/') - /* It's an absolute path. */ - new_file->info.name = fname; - else - { - new_file->info.name = libdw_alloc (dbg, char, 1, - dirarray[diridx].len + 1 - + fnamelen + 1); - char *cp = new_file->info.name; + /* First comes the file name. */ + char *fname = (char *) linep; + uint8_t *endp = memchr (fname, '\0', lineendp - linep); + if (endp == NULL) + goto invalid_data; + size_t fnamelen = endp - (uint8_t *) fname; + linep = endp + 1; - if (dirarray[diridx].dir != NULL) + /* Then the index. */ + Dwarf_Word diridx; + if (unlikely (linep >= lineendp)) + goto invalid_data; + get_uleb128 (diridx, linep, lineendp); + if (unlikely (diridx >= ndirlist)) { - /* This value could be NULL in case the DW_AT_comp_dir - was not present. We cannot do much in this case. - The easiest thing is to convert the path in an - absolute path. */ - cp = stpcpy (cp, dirarray[diridx].dir); + __libdw_seterrno (DWARF_E_INVALID_DIR_IDX); + goto out; + } + + if (*fname == '/') + /* It's an absolute path. */ + new_file->info.name = fname; + else + { + new_file->info.name = libdw_alloc (dbg, char, 1, + dirarray[diridx].len + 1 + + fnamelen + 1); + char *cp = new_file->info.name; + + if (dirarray[diridx].dir != NULL) + { + /* This value could be NULL in case the DW_AT_comp_dir + was not present. We cannot do much in this case. + Just keep the file relative. */ + cp = stpcpy (cp, dirarray[diridx].dir); + *cp++ = '/'; + } + strcpy (cp, fname); + assert (strlen (new_file->info.name) + < dirarray[diridx].len + 1 + fnamelen + 1); } - *cp++ = '/'; - strcpy (cp, fname); - assert (strlen (new_file->info.name) - < dirarray[diridx].len + 1 + fnamelen + 1); + + /* Next comes the modification time. */ + if (unlikely (linep >= lineendp)) + goto invalid_data; + get_uleb128 (new_file->info.mtime, linep, lineendp); + + /* Finally the length of the file. */ + if (unlikely (linep >= lineendp)) + goto invalid_data; + get_uleb128 (new_file->info.length, linep, lineendp); } + /* Skip the final NUL byte. */ + ++linep; + } + else + { + if ((size_t) (lineendp - linep) < 1) + goto invalid_data; + nforms = *linep++; + form_path = form_idx = -1; + for (int i = 0; i < nforms; i++) + { + uint16_t desc, form; + if ((size_t) (lineendp - linep) < 1) + goto invalid_data; + get_uleb128 (desc, linep, lineendp); + if ((size_t) (lineendp - linep) < 1) + goto invalid_data; + get_uleb128 (form, linep, lineendp); - /* Next comes the modification time. */ - if (unlikely (linep >= lineendp)) + if (! libdw_valid_user_form (form)) + goto invalid_data; + + forms[i] = form; + if (desc == DW_LNCT_path) + form_path = i; + else if (desc == DW_LNCT_directory_index) + form_idx = i; + } + + if (nforms > 0 && (form_path == (unsigned char) -1 + || form_idx == (unsigned char) -1)) goto invalid_data; - get_uleb128 (new_file->info.mtime, linep, lineendp); - /* Finally the length of the file. */ - if (unlikely (linep >= lineendp)) + size_t nfiles; + get_uleb128 (nfiles, linep, lineendp); + + if (nforms == 0 && nfiles != 0) + goto invalid_data; + + /* Assume there is at least 1 byte needed per form to describe + the file. Filters out insanely large nfiles. */ + if (nforms != 0 && nfiles > (size_t) (lineendp - linep) / nforms) goto invalid_data; - get_uleb128 (new_file->info.length, linep, lineendp); + + Dwarf_Attribute attr; + attr.cu = &fake_cu; + for (unsigned int n = 0; n < nfiles; n++) + { + const char *fname = NULL; + Dwarf_Word diridx = (Dwarf_Word) -1; + for (unsigned char m = 0; m < nforms; m++) + { + if (m == form_path) + { + attr.code = DW_AT_name; + attr.form = forms[m]; + attr.valp = (void *) linep; + fname = dwarf_formstring (&attr); + } + else if (m == form_idx) + { + attr.code = DW_AT_decl_file; /* Close enough. */ + attr.form = forms[m]; + attr.valp = (void *) linep; + if (dwarf_formudata (&attr, &diridx) != 0) + diridx = (Dwarf_Word) -1; + } + + size_t len = __libdw_form_val_len (&fake_cu, forms[m], linep); + if ((size_t) (lineendp - linep) < len) + goto invalid_data; + + linep += len; + } + + if (fname == NULL || diridx == (Dwarf_Word) -1) + goto invalid_data; + + size_t fnamelen = strlen (fname); + + if (unlikely (diridx >= ndirlist)) + { + __libdw_seterrno (DWARF_E_INVALID_DIR_IDX); + goto out; + } + + /* Yes, weird. Looks like an off-by-one in the spec. */ + struct filelist *new_file = n == 0 ? &null_file : NEW_FILE (); + + /* We follow the same rules as above for DWARF < 5, even + though the standard doesn't explicitly mention absolute + paths and ignoring the dir index. */ + if (*fname == '/') + /* It's an absolute path. */ + new_file->info.name = (char *) fname; + else + { + new_file->info.name = libdw_alloc (dbg, char, 1, + dirarray[diridx].len + 1 + + fnamelen + 1); + char *cp = new_file->info.name; + + /* In the DWARF >= 5 case, dir can never be NULL. */ + cp = stpcpy (cp, dirarray[diridx].dir); + *cp++ = '/'; + strcpy (cp, fname); + assert (strlen (new_file->info.name) + < dirarray[diridx].len + 1 + fnamelen + 1); + } + + /* For now we just ignore the modification time and file length. */ + new_file->info.mtime = 0; + new_file->info.length = 0; + } } - /* Skip the final NUL byte. */ - ++linep; /* Consistency check. */ if (unlikely (linep != header_start + header_length)) @@ -408,6 +650,9 @@ read_srclines (Dwarf *dbg, goto out; } + /* We are about to process the statement program. Most state machine + registers have already been initialize above. Just add the is_stmt + default. See 6.2.2 in the v2.1 specification. */ state.is_stmt = default_is_stmt; /* Apply the "operation advance" from a special opcode or @@ -557,11 +802,12 @@ read_srclines (Dwarf *dbg, if (dirarray[diridx].dir != NULL) /* This value could be NULL in case the DW_AT_comp_dir was not present. We - cannot do much in this case. The easiest - thing is to convert the path in an - absolute path. */ - cp = stpcpy (cp, dirarray[diridx].dir); - *cp++ = '/'; + cannot do much in this case. Just + keep the file relative. */ + { + cp = stpcpy (cp, dirarray[diridx].dir); + *cp++ = '/'; + } strcpy (cp, fname); } @@ -820,7 +1066,7 @@ read_srclines (Dwarf *dbg, free (state.linelist); state.linelist = ll; } - if (ndirlist >= MAX_STACK_DIRS) + if (dirarray != dirstack) free (dirarray); for (size_t i = MAX_STACK_FILES; i < nfilelist; i++) { @@ -918,6 +1164,31 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines) struct Dwarf_CU *const cu = cudie->cu; if (cu->lines == NULL) { + /* For split units always pick the lines from the skeleton. */ + if (cu->unit_type == DW_UT_split_compile + || cu->unit_type == DW_UT_split_type) + { + /* We tries, assume we fail... */ + cu->lines = (void *) -1l; + + Dwarf_CU *skel = __libdw_find_split_unit (cu); + if (skel != NULL) + { + Dwarf_Die skeldie = CUDIE (skel); + int res = INTUSE(dwarf_getsrclines) (&skeldie, lines, nlines); + if (res == 0) + { + cu->lines = skel->lines; + *lines = cu->lines; + *nlines = cu->lines->nlines; + } + return res; + } + + __libdw_seterrno (DWARF_E_NO_DEBUG_LINE); + return -1; + } + /* Failsafe mode: no data found. */ cu->lines = (void *) -1l; cu->files = (void *) -1l; diff --git a/libdw/dwarf_hasattr.c b/libdw/dwarf_hasattr.c index 2bb8dc82..90053b13 100644 --- a/libdw/dwarf_hasattr.c +++ b/libdw/dwarf_hasattr.c @@ -1,5 +1,5 @@ /* Check whether given DIE has specific attribute. - Copyright (C) 2003, 2005, 2014 Red Hat, Inc. + Copyright (C) 2003, 2005, 2014, 2017 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <[email protected]>, 2003. @@ -45,32 +45,20 @@ dwarf_hasattr (Dwarf_Die *die, unsigned int search_name) Dwarf_Abbrev *abbrevp = __libdw_dieabbrev (die, NULL); if (unlikely (abbrevp == DWARF_END_ABBREV)) { - invalid_dwarf: __libdw_seterrno (DWARF_E_INVALID_DWARF); return 0; } - Dwarf *dbg = die->cu->dbg; - - /* Search the name attribute. */ - unsigned char *const endp - = ((unsigned char *) dbg->sectiondata[IDX_debug_abbrev]->d_buf - + dbg->sectiondata[IDX_debug_abbrev]->d_size); - + /* Search the name attribute. Dwarf_Abbrev was checked when created, + so we can read unchecked here. */ const unsigned char *attrp = abbrevp->attrp; while (1) { - /* Are we still in bounds? This test needs to be refined. */ - if (unlikely (attrp >= endp)) - goto invalid_dwarf; - /* Get attribute name and form. */ unsigned int attr_name; - get_uleb128 (attr_name, attrp, endp); + get_uleb128_unchecked (attr_name, attrp); unsigned int attr_form; - if (unlikely (attrp >= endp)) - goto invalid_dwarf; - get_uleb128 (attr_form, attrp, endp); + get_uleb128_unchecked (attr_form, attrp); /* We can stop if we found the attribute with value zero. */ if (attr_name == 0 || attr_form == 0) @@ -78,6 +66,12 @@ dwarf_hasattr (Dwarf_Die *die, unsigned int search_name) if (attr_name == search_name) return 1; + + if (attr_form == DW_FORM_implicit_const) + { + int64_t attr_value __attribute__ ((unused)); + get_sleb128_unchecked (attr_value, attrp); + } } } INTDEF (dwarf_hasattr) diff --git a/libdw/dwarf_hasattr_integrate.c b/libdw/dwarf_hasattr_integrate.c index 2d5348cf..1d946280 100644 --- a/libdw/dwarf_hasattr_integrate.c +++ b/libdw/dwarf_hasattr_integrate.c @@ -1,5 +1,5 @@ /* Check whether DIE has specific attribute, integrating DW_AT_abstract_origin. - Copyright (C) 2005 Red Hat, Inc. + Copyright (C) 2005, 2018 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -37,7 +37,7 @@ int dwarf_hasattr_integrate (Dwarf_Die *die, unsigned int search_name) { Dwarf_Die die_mem; - + int chain = 16; /* Largest DIE ref chain we will follow. */ do { if (INTUSE(dwarf_hasattr) (die, search_name)) @@ -53,7 +53,21 @@ dwarf_hasattr_integrate (Dwarf_Die *die, unsigned int search_name) die = INTUSE(dwarf_formref_die) (attr, &die_mem); } - while (die != NULL); + while (die != NULL && chain-- != 0); + + /* Not NULL if it didn't have abstract_origin and specification + attributes. If it is a split CU then see if the skeleton + has it. */ + if (die != NULL && is_cudie (die) + && die->cu->unit_type == DW_UT_split_compile) + { + Dwarf_CU *skel_cu = __libdw_find_split_unit (die->cu); + if (skel_cu != NULL) + { + Dwarf_Die skel_die = CUDIE (skel_cu); + return INTUSE(dwarf_hasattr) (&skel_die, search_name); + } + } return 0; } diff --git a/libdw/dwarf_highpc.c b/libdw/dwarf_highpc.c index 20702545..5b2f0fd6 100644 --- a/libdw/dwarf_highpc.c +++ b/libdw/dwarf_highpc.c @@ -1,7 +1,6 @@ /* Return high PC attribute of DIE. - Copyright (C) 2003, 2005, 2012 Red Hat, Inc. + Copyright (C) 2003, 2005, 2012, 2018 Red Hat, Inc. This file is part of elfutils. - Written by Ulrich Drepper <[email protected]>, 2003. This file is free software; you can redistribute it and/or modify it under the terms of either @@ -39,19 +38,22 @@ int dwarf_highpc (Dwarf_Die *die, Dwarf_Addr *return_addr) { Dwarf_Attribute attr_high_mem; - Dwarf_Attribute *attr_high = INTUSE(dwarf_attr) (die, DW_AT_high_pc, - &attr_high_mem); + Dwarf_Attribute *attr_high; + /* Split compile DIEs inherit high_pc from their skeleton DIE. */ + if (is_cudie (die) && die->cu->unit_type == DW_UT_split_compile) + attr_high = INTUSE(dwarf_attr_integrate) (die, DW_AT_high_pc, + &attr_high_mem); + else + attr_high = INTUSE(dwarf_attr) (die, DW_AT_high_pc, &attr_high_mem); + if (attr_high == NULL) - return -1; + goto no_addr; - if (attr_high->form == DW_FORM_addr) - return INTUSE(dwarf_formaddr) (attr_high, return_addr); + if (INTUSE(dwarf_formaddr) (attr_high, return_addr) == 0) + return 0; /* DWARF 4 allows high_pc to be a constant offset from low_pc. */ - Dwarf_Attribute attr_low_mem; - if (INTUSE(dwarf_formaddr) (INTUSE(dwarf_attr) (die, DW_AT_low_pc, - &attr_low_mem), - return_addr) == 0) + if (INTUSE(dwarf_lowpc) (die, return_addr) == 0) { Dwarf_Word uval; if (INTUSE(dwarf_formudata) (attr_high, &uval) == 0) @@ -59,8 +61,10 @@ dwarf_highpc (Dwarf_Die *die, Dwarf_Addr *return_addr) *return_addr += uval; return 0; } - __libdw_seterrno (DWARF_E_NO_ADDR); } + +no_addr: + __libdw_seterrno (DWARF_E_NO_ADDR); return -1; } INTDEF(dwarf_highpc) diff --git a/libdw/dwarf_lowpc.c b/libdw/dwarf_lowpc.c index b3be2b0e..4d743a72 100644 --- a/libdw/dwarf_lowpc.c +++ b/libdw/dwarf_lowpc.c @@ -1,7 +1,6 @@ /* Return low PC attribute of DIE. - Copyright (C) 2003, 2005 Red Hat, Inc. + Copyright (C) 2003, 2005, 2018 Red Hat, Inc. This file is part of elfutils. - Written by Ulrich Drepper <[email protected]>, 2003. This file is free software; you can redistribute it and/or modify it under the terms of either @@ -38,10 +37,12 @@ int dwarf_lowpc (Dwarf_Die *die, Dwarf_Addr *return_addr) { - Dwarf_Attribute attr_mem; - - return INTUSE(dwarf_formaddr) (INTUSE(dwarf_attr) (die, DW_AT_low_pc, - &attr_mem), - return_addr); + Dwarf_Attribute attr_mem, *attr; + /* Split compile DIEs inherit low_pc from their skeleton DIE. */ + if (is_cudie (die) && die->cu->unit_type == DW_UT_split_compile) + attr = INTUSE(dwarf_attr_integrate) (die, DW_AT_low_pc, &attr_mem); + else + attr = INTUSE(dwarf_attr) (die, DW_AT_low_pc, &attr_mem); + return INTUSE(dwarf_formaddr) (attr, return_addr); } INTDEF(dwarf_lowpc) diff --git a/libdw/dwarf_next_cfi.c b/libdw/dwarf_next_cfi.c index 53fc3697..fa28d99b 100644 --- a/libdw/dwarf_next_cfi.c +++ b/libdw/dwarf_next_cfi.c @@ -54,6 +54,7 @@ dwarf_next_cfi (const unsigned char e_ident[], we don't know yet whether this is a 64-bit object or not. */ || unlikely (off + 4 >= data->d_size)) { + done: *next_off = (Dwarf_Off) -1l; return 1; } @@ -79,6 +80,13 @@ dwarf_next_cfi (const unsigned char e_ident[], } length = read_8ubyte_unaligned_inc (&dw, bytes); } + + /* Not explicitly in the DWARF spec, but mentioned in the LSB exception + frames (.eh_frame) spec. If Length contains the value 0, then this + CIE shall be considered a terminator and processing shall end. */ + if (length == 0) + goto done; + if (unlikely ((uint64_t) (limit - bytes) < length) || unlikely (length < offset_size + 1)) goto invalid; diff --git a/libdw/dwarf_next_lines.c b/libdw/dwarf_next_lines.c new file mode 100644 index 00000000..9b76b47e --- /dev/null +++ b/libdw/dwarf_next_lines.c @@ -0,0 +1,197 @@ +/* Iterate through the debug line table. + Copyright (C) 2018 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <libdwP.h> + + +int +dwarf_next_lines (Dwarf *dbg, Dwarf_Off off, + Dwarf_Off *next_off, Dwarf_CU **cu, + Dwarf_Files **srcfiles, size_t *nfiles, + Dwarf_Lines **srclines, size_t *nlines) +{ + /* Ignore existing errors. */ + if (dbg == NULL) + return -1; + + Elf_Data *lines = dbg->sectiondata[IDX_debug_line]; + if (lines == NULL) + { + __libdw_seterrno (DWARF_E_NO_DEBUG_LINE); + return -1; + } + + if (off == (Dwarf_Off) -1 + || lines->d_size < 4 + || off >= lines->d_size) + { + *next_off = (Dwarf_Off) -1; + return 1; + } + + /* Read enough of the header to know where the next table is and + whether we need to lookup the CU (version < 5). */ + const unsigned char *linep = lines->d_buf + off; + const unsigned char *lineendp = lines->d_buf + lines->d_size; + + if ((size_t) (lineendp - linep) < 4) + { + invalid_data: + __libdw_seterrno (DWARF_E_INVALID_DEBUG_LINE); + return -1; + } + + *next_off = off + 4; + Dwarf_Word unit_length = read_4ubyte_unaligned_inc (dbg, linep); + if (unit_length == DWARF3_LENGTH_64_BIT) + { + if ((size_t) (lineendp - linep) < 8) + goto invalid_data; + unit_length = read_8ubyte_unaligned_inc (dbg, linep); + *next_off += 8; + } + + if (unit_length > (size_t) (lineendp - linep)) + goto invalid_data; + + *next_off += unit_length; + lineendp = linep + unit_length; + + if ((size_t) (lineendp - linep) < 2) + goto invalid_data; + uint_fast16_t version = read_2ubyte_unaligned_inc (dbg, linep); + + Dwarf_Die cudie; + if (version < 5) + { + /* We need to find the matching CU to get the comp_dir. Use the + given CU as hint where to start searching. Normally it will + be the next CU that has a statement list. */ + Dwarf_CU *given_cu = *cu; + Dwarf_CU *next_cu = given_cu; + bool found = false; + while (dwarf_get_units (dbg, next_cu, &next_cu, NULL, NULL, + &cudie, NULL) == 0) + { + if (dwarf_hasattr (&cudie, DW_AT_stmt_list)) + { + Dwarf_Attribute attr; + Dwarf_Word stmt_off; + if (dwarf_formudata (dwarf_attr (&cudie, DW_AT_stmt_list, &attr), + &stmt_off) == 0 + && stmt_off == off) + { + found = true; + break; + } + } + else if (off == 0 + && (next_cu->unit_type == DW_UT_split_compile + || next_cu->unit_type == DW_UT_split_type)) + { + /* For split units (in .dwo files) there is only one table + at offset zero (containing just the files, no lines). */ + found = true; + break; + } + } + + if (!found && given_cu != NULL) + { + /* The CUs might be in a different order from the line + tables. Need to do a linear search (but stop at the given + CU, since we already searched those. */ + next_cu = NULL; + while (dwarf_get_units (dbg, next_cu, &next_cu, NULL, NULL, + &cudie, NULL) == 0 + && next_cu != given_cu) + { + Dwarf_Attribute attr; + Dwarf_Word stmt_off; + if (dwarf_formudata (dwarf_attr (&cudie, DW_AT_stmt_list, &attr), + &stmt_off) == 0 + && stmt_off == off) + { + found = true; + break; + } + } + } + + if (found) + *cu = next_cu; + else + *cu = NULL; + } + else + *cu = NULL; + + const char *comp_dir; + unsigned address_size; + if (*cu != NULL) + { + comp_dir = __libdw_getcompdir (&cudie); + address_size = (*cu)->address_size; + } + else + { + comp_dir = NULL; + + size_t esize; + char *ident = elf_getident (dbg->elf, &esize); + if (ident == NULL || esize < EI_NIDENT) + goto invalid_data; + address_size = ident[EI_CLASS] == ELFCLASS32 ? 4 : 8; + } + + if (__libdw_getsrclines (dbg, off, comp_dir, address_size, + srclines, srcfiles) != 0) + return -1; + + if (nlines != NULL) + { + if (srclines != NULL && *srclines != NULL) + *nlines = (*srclines)->nlines; + else + *nlines = 0; + } + + if (nfiles != NULL) + { + if (srcfiles != NULL && *srcfiles != NULL) + *nfiles = (*srcfiles)->nfiles; + else + *nfiles = 0; + } + + return 0; +} diff --git a/libdw/dwarf_nextcu.c b/libdw/dwarf_nextcu.c index fa9b0af3..be113270 100644 --- a/libdw/dwarf_nextcu.c +++ b/libdw/dwarf_nextcu.c @@ -1,5 +1,5 @@ /* Advance to next CU header. - Copyright (C) 2002-2010 Red Hat, Inc. + Copyright (C) 2002-2010, 2016, 2017 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <[email protected]>, 2002. @@ -39,11 +39,31 @@ int dwarf_next_unit (Dwarf *dwarf, Dwarf_Off off, Dwarf_Off *next_off, size_t *header_sizep, Dwarf_Half *versionp, Dwarf_Off *abbrev_offsetp, uint8_t *address_sizep, - uint8_t *offset_sizep, uint64_t *type_signaturep, - Dwarf_Off *type_offsetp) + uint8_t *offset_sizep, uint64_t *v4_type_signaturep, + Dwarf_Off *v4_type_offsetp) { - const bool debug_types = type_signaturep != NULL; - const size_t sec_idx = debug_types ? IDX_debug_types : IDX_debug_info; + const bool v4_debug_types = v4_type_signaturep != NULL; + return __libdw_next_unit (dwarf, v4_debug_types, off, next_off, + header_sizep, versionp, NULL, + abbrev_offsetp, address_sizep, offset_sizep, + v4_type_signaturep, v4_type_offsetp); +} +INTDEF(dwarf_next_unit) + +int +internal_function +__libdw_next_unit (Dwarf *dwarf, bool v4_debug_types, Dwarf_Off off, + Dwarf_Off *next_off, size_t *header_sizep, + Dwarf_Half *versionp, uint8_t *unit_typep, + Dwarf_Off *abbrev_offsetp, uint8_t *address_sizep, + uint8_t *offset_sizep, uint64_t *unit_id8p, + Dwarf_Off *subdie_offsetp) +{ + /* Note that debug_type units come from .debug_types in DWARF < 5 and + from .debug_info in DWARF >= 5. If the user requested the + v4_type_signature we return from .debug_types always. If no signature + is requested we return units (any type) from .debug_info. */ + const size_t sec_idx = v4_debug_types ? IDX_debug_types : IDX_debug_info; /* Maybe there has been an error before. */ if (dwarf == NULL) @@ -61,12 +81,14 @@ dwarf_next_unit (Dwarf *dwarf, Dwarf_Off off, Dwarf_Off *next_off, return 1; } - /* This points into the .debug_info section to the beginning of the - CU entry. */ + /* This points into the .debug_info or .debug_types section to the + beginning of the CU entry. */ const unsigned char *data = dwarf->sectiondata[sec_idx]->d_buf; const unsigned char *bytes = data + off; - /* The format of the CU header is described in dwarf2p1 7.5.1: + /* The format of the CU header is described in dwarf2p1 7.5.1 and + changed in DWARFv5 (to include unit type, switch location of some + fields and add some optional fields). 1. A 4-byte or 12-byte unsigned integer representing the length of the .debug_info contribution for that compilation unit, not @@ -74,23 +96,58 @@ dwarf_next_unit (Dwarf *dwarf, Dwarf_Off off, Dwarf_Off *next_off, this is a 4-byte unsigned integer (which must be less than 0xfffffff0); in the 64-bit DWARF format, this consists of the 4-byte value 0xffffffff followed by an 8-byte unsigned integer - that gives the actual length (see Section 7.2.2). + that gives the actual length (see Section 7.2.2). This field + indicates whether this unit is 32-bit of 64-bit DWARF, which + affects all other offset fields in this header. 2. A 2-byte unsigned integer representing the version of the DWARF information for that compilation unit. For DWARF Version - 2.1, the value in this field is 2. + 2.1, the value in this field is 2 (3 for v3, 4 for v4, 5 for v5). + This fields determines the order of the next fields and whether + there are any optional fields in this header. - 3. A 4-byte or 8-byte unsigned offset into the .debug_abbrev + 3. For DWARF 2, 3 and 4 (including v4 type units): + A 4-byte or 8-byte unsigned offset into the .debug_abbrev section. This offset associates the compilation unit with a particular set of debugging information entry abbreviations. In the 32-bit DWARF format, this is a 4-byte unsigned length; in the 64-bit DWARF format, this is an 8-byte unsigned length (see Section 7.4). - 4. A 1-byte unsigned integer representing the size in bytes of + For DWARF 5: + A 1-byte unsigned integer representing the unit (header) type. + This field determines what the optional fields in the header + represent. If this is an unknown unit type then we cannot + assume anything about the rest of the unit (header). + + 4. For all DWARF versions (including v4 type units): + A 1-byte unsigned integer representing the size in bytes of an address on the target architecture. If the system uses segmented addressing, this value represents the size of the - offset portion of an address. */ + offset portion of an address. This is the last field in the header + for DWARF versions 2, 3 and 4 (except for v4 type units). + + 5. For DWARF 5 only (this is field 3 for DWARF 2, 3, 4 and v4 types): + A 4-byte or 8-byte unsigned offset into the .debug_abbrev + section. This offset associates the compilation unit with a + particular set of debugging information entry abbreviations. In + the 32-bit DWARF format, this is a 4-byte unsigned length; in + the 64-bit DWARF format, this is an 8-byte unsigned length. + + 6. For v4 type units (this is really field 5 for v4 types) and + DWARF 5 optional (skeleton, split_compile, type and + split_type): An 8 byte (opaque) integer constant value. For + v4 and v5 type units this is the type signature. For skeleton + and split compile units this is the compilation ID. + + 7. For v4 type units (this is really field 6 for v4 types) and + DWARF 5 optional (type and split_type) and v4 type units: + A 4-byte or 8-byte unsigned offset. In the 32-bit DWARF format, + this is a 4-byte unsigned length; in the 64-bit DWARF format, + this is an 8-byte unsigned length. This is the type DIE offset + (which is not necessarily the first DIE in the unit). + */ + uint64_t length = read_4ubyte_unaligned_inc (dwarf, bytes); size_t offset_size = 4; /* Lengths of 0xfffffff0 - 0xffffffff are escape codes. Oxffffffff is @@ -106,14 +163,6 @@ dwarf_next_unit (Dwarf *dwarf, Dwarf_Off off, Dwarf_Off *next_off, return -1; } - /* Now we know how large the header is. */ - if (unlikely (DIE_OFFSET_FROM_CU_OFFSET (off, offset_size, debug_types) - >= dwarf->sectiondata[sec_idx]->d_size)) - { - *next_off = -1; - return 1; - } - if (length == DWARF3_LENGTH_64_BIT) /* This is a 64-bit DWARF format. */ length = read_8ubyte_unaligned_inc (dwarf, bytes); @@ -121,41 +170,99 @@ dwarf_next_unit (Dwarf *dwarf, Dwarf_Off off, Dwarf_Off *next_off, /* Read the version stamp. Always a 16-bit value. */ uint_fast16_t version = read_2ubyte_unaligned_inc (dwarf, bytes); + /* We keep unit_type at zero for older DWARF since we cannot + easily guess whether it is a compile or partial unit. */ + uint8_t unit_type = 0; + if (version >= 5) + unit_type = *bytes++; + + /* All these are optional. */ + Dwarf_Off subdie_off = 0; + uint64_t sig_id = 0; + Dwarf_Off abbrev_offset = 0; + uint8_t address_size = 0; + + if (version < 2 || version > 5 + || (version == 5 && ! (unit_type == DW_UT_compile + || unit_type == DW_UT_partial + || unit_type == DW_UT_skeleton + || unit_type == DW_UT_split_compile + || unit_type == DW_UT_type + || unit_type == DW_UT_split_type))) + { + /* We cannot really know more about the header. Just report + the length of the unit, version and unit type. */ + goto done; + } + + /* We have to guess the unit_type. But we don't have a real CUDIE. */ + if (version < 5) + unit_type = v4_debug_types ? DW_UT_type : DW_UT_compile; + + /* Now we know how large the header is (should be). */ + if (unlikely (__libdw_first_die_from_cu_start (off, offset_size, version, + unit_type) + >= dwarf->sectiondata[sec_idx]->d_size)) + { + *next_off = -1; + return 1; + } + + /* The address size. Always an 8-bit value. + Comes after abbrev_offset for version < 5, otherwise unit type + and address size (if a known unit type) comes before abbrev_offset. */ + if (version >= 5) + address_size = *bytes++; + /* Get offset in .debug_abbrev. Note that the size of the entry depends on whether this is a 32-bit or 64-bit DWARF definition. */ - uint64_t abbrev_offset; if (__libdw_read_offset_inc (dwarf, sec_idx, &bytes, offset_size, &abbrev_offset, IDX_debug_abbrev, 0)) return -1; - /* The address size. Always an 8-bit value. */ - uint8_t address_size = *bytes++; + if (version < 5) + address_size = *bytes++; - if (debug_types) + /* Extra fields, signature/id and type offset/padding. */ + if (v4_debug_types + || (version >= 5 + && (unit_type == DW_UT_skeleton || unit_type == DW_UT_split_compile + || unit_type == DW_UT_type || unit_type == DW_UT_split_type))) { - uint64_t type_sig8 = read_8ubyte_unaligned_inc (dwarf, bytes); - - Dwarf_Off type_offset; - if (__libdw_read_offset_inc (dwarf, sec_idx, &bytes, offset_size, - &type_offset, sec_idx, 0)) - return -1; + sig_id = read_8ubyte_unaligned_inc (dwarf, bytes); + + if ((v4_debug_types + || unit_type == DW_UT_type || unit_type == DW_UT_split_type)) + { + if (__libdw_read_offset_inc (dwarf, sec_idx, &bytes, offset_size, + &subdie_off, sec_idx, 0)) + return -1; + + /* Validate that the TYPE_OFFSET points past the header. */ + if (unlikely (subdie_off < (size_t) (bytes - (data + off)))) + goto invalid; + } + } - /* Validate that the TYPE_OFFSET points past the header. */ - if (unlikely (type_offset < (size_t) (bytes - (data + off)))) - goto invalid; + done: + if (unit_id8p != NULL) + *unit_id8p = sig_id; - *type_signaturep = type_sig8; - if (type_offsetp != NULL) - *type_offsetp = type_offset; - } + if (subdie_offsetp != NULL) + *subdie_offsetp = subdie_off; - /* Store the header length. */ + /* Store the header length. This is really how much we have read + from the header. If we didn't recognize the unit type the + header might actually be bigger. */ if (header_sizep != NULL) *header_sizep = bytes - (data + off); if (versionp != NULL) *versionp = version; + if (unit_typep != NULL) + *unit_typep = unit_type; + if (abbrev_offsetp != NULL) *abbrev_offsetp = abbrev_offset; @@ -166,13 +273,18 @@ dwarf_next_unit (Dwarf *dwarf, Dwarf_Off off, Dwarf_Off *next_off, if (offset_sizep != NULL) *offset_sizep = offset_size; - /* See definition of DIE_OFFSET_FROM_CU_OFFSET macro - for an explanation of the trick in this expression. */ + /* The length of the unit doesn't include the length field itself. + The length field is either, with offset == 4: 2 * 4 - 4 == 4, + or with offset == 8: 2 * 8 - 4 == 12. */ *next_off = off + 2 * offset_size - 4 + length; + /* This means that the length field is bogus, but return the CU anyway. + We just won't return anything after this. */ + if (*next_off <= off) + *next_off = (Dwarf_Off) -1; + return 0; } -INTDEF(dwarf_next_unit) int dwarf_nextcu (Dwarf *dwarf, Dwarf_Off off, Dwarf_Off *next_off, diff --git a/libdw/dwarf_offdie.c b/libdw/dwarf_offdie.c index 15f55c22..883720de 100644 --- a/libdw/dwarf_offdie.c +++ b/libdw/dwarf_offdie.c @@ -1,5 +1,5 @@ /* Return DIE at given offset. - Copyright (C) 2002-2010 Red Hat, Inc. + Copyright (C) 2002-2010, 2017 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <[email protected]>, 2002. @@ -45,7 +45,7 @@ __libdw_offdie (Dwarf *dbg, Dwarf_Off offset, Dwarf_Die *result, Elf_Data *const data = dbg->sectiondata[debug_types ? IDX_debug_types : IDX_debug_info]; - if (offset >= data->d_size) + if (data == NULL || offset >= data->d_size) { __libdw_seterrno (DWARF_E_INVALID_DWARF); return NULL; diff --git a/libdw/dwarf_peel_type.c b/libdw/dwarf_peel_type.c index 6bbfd424..59fc6f15 100644 --- a/libdw/dwarf_peel_type.c +++ b/libdw/dwarf_peel_type.c @@ -46,14 +46,19 @@ dwarf_peel_type (Dwarf_Die *die, Dwarf_Die *result) *result = *die; tag = INTUSE (dwarf_tag) (result); - while (tag == DW_TAG_typedef - || tag == DW_TAG_const_type - || tag == DW_TAG_volatile_type - || tag == DW_TAG_restrict_type - || tag == DW_TAG_atomic_type - || tag == DW_TAG_immutable_type - || tag == DW_TAG_packed_type - || tag == DW_TAG_shared_type) + +/* Stack 8 of all these modifiers, after that it gets silly. */ +#define MAX_DEPTH (8 * 8) + int max_depth = MAX_DEPTH; + while ((tag == DW_TAG_typedef + || tag == DW_TAG_const_type + || tag == DW_TAG_volatile_type + || tag == DW_TAG_restrict_type + || tag == DW_TAG_atomic_type + || tag == DW_TAG_immutable_type + || tag == DW_TAG_packed_type + || tag == DW_TAG_shared_type) + && max_depth-- > 0) { Dwarf_Attribute attr_mem; Dwarf_Attribute *attr = INTUSE (dwarf_attr_integrate) (result, DW_AT_type, @@ -67,7 +72,7 @@ dwarf_peel_type (Dwarf_Die *die, Dwarf_Die *result) tag = INTUSE (dwarf_tag) (result); } - if (tag == DW_TAG_invalid) + if (tag == DW_TAG_invalid || max_depth <= 0) return -1; return 0; diff --git a/libdw/dwarf_ranges.c b/libdw/dwarf_ranges.c index 4b6853d3..f67d8a5a 100644 --- a/libdw/dwarf_ranges.c +++ b/libdw/dwarf_ranges.c @@ -1,5 +1,5 @@ /* Enumerate the PC ranges covered by a DIE. - Copyright (C) 2005, 2007, 2009 Red Hat, Inc. + Copyright (C) 2005, 2007, 2009, 2018 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -36,48 +36,427 @@ /* Read up begin/end pair and increment read pointer. - If it's normal range record, set up `*beginp' and `*endp' and return 0. + - If it's a default location, set `*beginp' (0), `*endp' (-1) and return 0. - If it's base address selection record, set up `*basep' and return 1. - If it's end of rangelist, don't set anything and return 2 - If an error occurs, don't set anything and return -1. */ internal_function int -__libdw_read_begin_end_pair_inc (Dwarf *dbg, int sec_index, - unsigned char **addrp, int width, +__libdw_read_begin_end_pair_inc (Dwarf_CU *cu, int sec_index, + const unsigned char **addrp, + const unsigned char *addrend, + int width, Dwarf_Addr *beginp, Dwarf_Addr *endp, Dwarf_Addr *basep) { - Dwarf_Addr escape = (width == 8 ? (Elf64_Addr) -1 - : (Elf64_Addr) (Elf32_Addr) -1); - Dwarf_Addr begin; - Dwarf_Addr end; + Dwarf *dbg = cu->dbg; + if (sec_index == IDX_debug_loc + && cu->version < 5 + && cu->unit_type == DW_UT_split_compile) + { + /* GNU DebugFission. */ + const unsigned char *addr = *addrp; + if (addrend - addr < 1) + goto invalid; + + const char code = *addr++; + uint64_t begin = 0, end = 0, base = *basep, addr_idx; + switch (code) + { + case DW_LLE_GNU_end_of_list_entry: + *addrp = addr; + return 2; + + case DW_LLE_GNU_base_address_selection_entry: + if (addrend - addr < 1) + goto invalid; + get_uleb128 (addr_idx, addr, addrend); + if (__libdw_addrx (cu, addr_idx, &base) != 0) + return -1; + *basep = base; + *addrp = addr; + return 1; + + case DW_LLE_GNU_start_end_entry: + if (addrend - addr < 1) + goto invalid; + get_uleb128 (addr_idx, addr, addrend); + if (__libdw_addrx (cu, addr_idx, &begin) != 0) + return -1; + if (addrend - addr < 1) + goto invalid; + get_uleb128 (addr_idx, addr, addrend); + if (__libdw_addrx (cu, addr_idx, &end) != 0) + return -1; + + *beginp = begin; + *endp = end; + *addrp = addr; + return 0; + + case DW_LLE_GNU_start_length_entry: + if (addrend - addr < 1) + goto invalid; + get_uleb128 (addr_idx, addr, addrend); + if (__libdw_addrx (cu, addr_idx, &begin) != 0) + return -1; + if (addrend - addr < 4) + goto invalid; + end = read_4ubyte_unaligned_inc (dbg, addr); + + *beginp = begin; + *endp = begin + end; + *addrp = addr; + return 0; + + default: + goto invalid; + } + } + else if (sec_index == IDX_debug_ranges || sec_index == IDX_debug_loc) + { + Dwarf_Addr escape = (width == 8 ? (Elf64_Addr) -1 + : (Elf64_Addr) (Elf32_Addr) -1); + Dwarf_Addr begin; + Dwarf_Addr end; + + const unsigned char *addr = *addrp; + if (addrend - addr < width * 2) + { + invalid: + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return -1; + } + + bool begin_relocated = READ_AND_RELOCATE (__libdw_relocate_address, + begin); + bool end_relocated = READ_AND_RELOCATE (__libdw_relocate_address, + end); + *addrp = addr; + + /* Unrelocated escape for begin means base address selection. */ + if (begin == escape && !begin_relocated) + { + if (unlikely (end == escape)) + goto invalid; + + *basep = end; + return 1; + } + + /* Unrelocated pair of zeroes means end of range list. */ + if (begin == 0 && end == 0 && !begin_relocated && !end_relocated) + return 2; + + /* Don't check for begin_relocated == end_relocated. Serve the data + to the client even though it may be buggy. */ + *beginp = begin + *basep; + *endp = end + *basep; - unsigned char *addr = *addrp; - bool begin_relocated = READ_AND_RELOCATE (__libdw_relocate_address, begin); - bool end_relocated = READ_AND_RELOCATE (__libdw_relocate_address, end); - *addrp = addr; + return 0; + } + else if (sec_index == IDX_debug_rnglists) + { + const unsigned char *addr = *addrp; + if (addrend - addr < 1) + goto invalid; + + const char code = *addr++; + uint64_t begin = 0, end = 0, base = *basep, addr_idx; + switch (code) + { + case DW_RLE_end_of_list: + *addrp = addr; + return 2; + + case DW_RLE_base_addressx: + if (addrend - addr < 1) + goto invalid; + get_uleb128 (addr_idx, addr, addrend); + if (__libdw_addrx (cu, addr_idx, &base) != 0) + return -1; + + *basep = base; + *addrp = addr; + return 1; + + case DW_RLE_startx_endx: + if (addrend - addr < 1) + goto invalid; + get_uleb128 (addr_idx, addr, addrend); + if (__libdw_addrx (cu, addr_idx, &begin) != 0) + return -1; + if (addrend - addr < 1) + goto invalid; + get_uleb128 (addr_idx, addr, addrend); + if (__libdw_addrx (cu, addr_idx, &end) != 0) + return -1; + + *beginp = begin; + *endp = end; + *addrp = addr; + return 0; + + case DW_RLE_startx_length: + if (addrend - addr < 1) + goto invalid; + get_uleb128 (addr_idx, addr, addrend); + if (__libdw_addrx (cu, addr_idx, &begin) != 0) + return -1; + if (addrend - addr < 1) + goto invalid; + get_uleb128 (end, addr, addrend); + + *beginp = begin; + *endp = begin + end; + *addrp = addr; + return 0; + + case DW_RLE_offset_pair: + if (addrend - addr < 1) + goto invalid; + get_uleb128 (begin, addr, addrend); + if (addrend - addr < 1) + goto invalid; + get_uleb128 (end, addr, addrend); + + *beginp = begin + base; + *endp = end + base; + *addrp = addr; + return 0; + + case DW_RLE_base_address: + if (addrend - addr < width) + goto invalid; + __libdw_read_address_inc (dbg, sec_index, &addr, width, &base); + + *basep = base; + *addrp = addr; + return 1; + + case DW_RLE_start_end: + if (addrend - addr < 2 * width) + goto invalid; + __libdw_read_address_inc (dbg, sec_index, &addr, width, &begin); + __libdw_read_address_inc (dbg, sec_index, &addr, width, &end); + + *beginp = begin; + *endp = end; + *addrp = addr; + return 0; + + case DW_RLE_start_length: + if (addrend - addr < width) + goto invalid; + __libdw_read_address_inc (dbg, sec_index, &addr, width, &begin); + if (addrend - addr < 1) + goto invalid; + get_uleb128 (end, addr, addrend); + + *beginp = begin; + *endp = begin + end; + *addrp = addr; + return 0; + + default: + goto invalid; + } + } + else if (sec_index == IDX_debug_loclists) + { + const unsigned char *addr = *addrp; + if (addrend - addr < 1) + goto invalid; + + const char code = *addr++; + uint64_t begin = 0, end = 0, base = *basep, addr_idx; + switch (code) + { + case DW_LLE_end_of_list: + *addrp = addr; + return 2; + + case DW_LLE_base_addressx: + if (addrend - addr < 1) + goto invalid; + get_uleb128 (addr_idx, addr, addrend); + if (__libdw_addrx (cu, addr_idx, &base) != 0) + return -1; + + *basep = base; + *addrp = addr; + return 1; + + case DW_LLE_startx_endx: + if (addrend - addr < 1) + goto invalid; + get_uleb128 (addr_idx, addr, addrend); + if (__libdw_addrx (cu, addr_idx, &begin) != 0) + return -1; + if (addrend - addr < 1) + goto invalid; + get_uleb128 (addr_idx, addr, addrend); + if (__libdw_addrx (cu, addr_idx, &end) != 0) + return -1; + + *beginp = begin; + *endp = end; + *addrp = addr; + return 0; + + case DW_LLE_startx_length: + if (addrend - addr < 1) + goto invalid; + get_uleb128 (addr_idx, addr, addrend); + if (__libdw_addrx (cu, addr_idx, &begin) != 0) + return -1; + if (addrend - addr < 1) + goto invalid; + get_uleb128 (end, addr, addrend); + + *beginp = begin; + *endp = begin + end; + *addrp = addr; + return 0; + + case DW_LLE_offset_pair: + if (addrend - addr < 1) + goto invalid; + get_uleb128 (begin, addr, addrend); + if (addrend - addr < 1) + goto invalid; + get_uleb128 (end, addr, addrend); + + *beginp = begin + base; + *endp = end + base; + *addrp = addr; + return 0; + + case DW_LLE_default_location: + *beginp = 0; + *endp = (Dwarf_Addr) -1; + *addrp = addr; + return 0; + + case DW_LLE_base_address: + if (addrend - addr < width) + goto invalid; + __libdw_read_address_inc (dbg, sec_index, &addr, width, &base); + + *basep = base; + *addrp = addr; + return 1; + + case DW_LLE_start_end: + if (addrend - addr < 2 * width) + goto invalid; + __libdw_read_address_inc (dbg, sec_index, &addr, width, &begin); + __libdw_read_address_inc (dbg, sec_index, &addr, width, &end); + + *beginp = begin; + *endp = end; + *addrp = addr; + return 0; + + case DW_LLE_start_length: + if (addrend - addr < width) + goto invalid; + __libdw_read_address_inc (dbg, sec_index, &addr, width, &begin); + if (addrend - addr < 1) + goto invalid; + get_uleb128 (end, addr, addrend); + + *beginp = begin; + *endp = begin + end; + *addrp = addr; + return 0; + + default: + goto invalid; + } + } + else + { + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return -1; + } +} + +static int +initial_offset (Dwarf_Attribute *attr, ptrdiff_t *offset) +{ + size_t secidx = (attr->cu->version < 5 + ? IDX_debug_ranges : IDX_debug_rnglists); - /* Unrelocated escape for begin means base address selection. */ - if (begin == escape && !begin_relocated) + Dwarf_Word start_offset; + if (attr->form == DW_FORM_rnglistx) { - if (unlikely (end == escape)) + Dwarf_Word idx; + Dwarf_CU *cu = attr->cu; + const unsigned char *datap = attr->valp; + const unsigned char *endp = cu->endp; + if (datap >= endp) { __libdw_seterrno (DWARF_E_INVALID_DWARF); return -1; } + get_uleb128 (idx, datap, endp); - if (basep != NULL) - *basep = end; - return 1; - } + Elf_Data *data = cu->dbg->sectiondata[secidx]; + if (data == NULL && cu->unit_type == DW_UT_split_compile) + { + cu = __libdw_find_split_unit (cu); + if (cu != NULL) + data = cu->dbg->sectiondata[secidx]; + } + + if (data == NULL) + { + __libdw_seterrno (secidx == IDX_debug_ranges + ? DWARF_E_NO_DEBUG_RANGES + : DWARF_E_NO_DEBUG_RNGLISTS); + return -1; + } - /* Unrelocated pair of zeroes means end of range list. */ - if (begin == 0 && end == 0 && !begin_relocated && !end_relocated) - return 2; + Dwarf_Off range_base_off = __libdw_cu_ranges_base (cu); - /* Don't check for begin_relocated == end_relocated. Serve the data - to the client even though it may be buggy. */ - *beginp = begin; - *endp = end; + /* The section should at least contain room for one offset. */ + size_t sec_size = cu->dbg->sectiondata[secidx]->d_size; + size_t offset_size = cu->offset_size; + if (offset_size > sec_size) + { + invalid_offset: + __libdw_seterrno (DWARF_E_INVALID_OFFSET); + return -1; + } + + /* And the base offset should be at least inside the section. */ + if (range_base_off > (sec_size - offset_size)) + goto invalid_offset; + + size_t max_idx = (sec_size - offset_size - range_base_off) / offset_size; + if (idx > max_idx) + goto invalid_offset; + + datap = (cu->dbg->sectiondata[secidx]->d_buf + + range_base_off + (idx * offset_size)); + if (offset_size == 4) + start_offset = read_4ubyte_unaligned (cu->dbg, datap); + else + start_offset = read_8ubyte_unaligned (cu->dbg, datap); + + start_offset += range_base_off; + } + else + { + if (__libdw_formptr (attr, secidx, + (secidx == IDX_debug_ranges + ? DWARF_E_NO_DEBUG_RANGES + : DWARF_E_NO_DEBUG_RNGLISTS), + NULL, &start_offset) == NULL) + return -1; + } + *offset = start_offset; return 0; } @@ -101,68 +480,64 @@ dwarf_ranges (Dwarf_Die *die, ptrdiff_t offset, Dwarf_Addr *basep, return 0; /* We have to look for a noncontiguous range. */ - - const Elf_Data *d = die->cu->dbg->sectiondata[IDX_debug_ranges]; - if (d == NULL && offset != 0) + Dwarf_CU *cu = die->cu; + if (cu == NULL) { - __libdw_seterrno (DWARF_E_NO_DEBUG_RANGES); + __libdw_seterrno (DWARF_E_INVALID_DWARF); return -1; } - unsigned char *readp; - unsigned char *readendp; + size_t secidx = (cu->version < 5 ? IDX_debug_ranges : IDX_debug_rnglists); + const Elf_Data *d = cu->dbg->sectiondata[secidx]; + if (d == NULL && cu->unit_type == DW_UT_split_compile) + { + Dwarf_CU *skel = __libdw_find_split_unit (cu); + if (skel != NULL) + { + cu = skel; + d = cu->dbg->sectiondata[secidx]; + } + } + + const unsigned char *readp; + const unsigned char *readendp; if (offset == 0) { Dwarf_Attribute attr_mem; Dwarf_Attribute *attr = INTUSE(dwarf_attr) (die, DW_AT_ranges, &attr_mem); + if (attr == NULL + && is_cudie (die) + && die->cu->unit_type == DW_UT_split_compile) + attr = INTUSE(dwarf_attr_integrate) (die, DW_AT_ranges, &attr_mem); if (attr == NULL) /* No PC attributes in this DIE at all, so an empty range list. */ return 0; - Dwarf_Word start_offset; - if ((readp = __libdw_formptr (attr, IDX_debug_ranges, - DWARF_E_NO_DEBUG_RANGES, - &readendp, &start_offset)) == NULL) + *basep = __libdw_cu_base_address (attr->cu); + if (*basep == (Dwarf_Addr) -1) return -1; - offset = start_offset; - assert ((Dwarf_Word) offset == start_offset); - - /* Fetch the CU's base address. */ - Dwarf_Die cudie = CUDIE (attr->cu); - - /* Find the base address of the compilation unit. It will - normally be specified by DW_AT_low_pc. In DWARF-3 draft 4, - the base address could be overridden by DW_AT_entry_pc. It's - been removed, but GCC emits DW_AT_entry_pc and not DW_AT_lowpc - for compilation units with discontinuous ranges. */ - if (unlikely (INTUSE(dwarf_lowpc) (&cudie, basep) != 0) - && INTUSE(dwarf_formaddr) (INTUSE(dwarf_attr) (&cudie, - DW_AT_entry_pc, - &attr_mem), - basep) != 0) - *basep = (Dwarf_Addr) -1; + if (initial_offset (attr, &offset) != 0) + return -1; } else { - if (__libdw_offset_in_section (die->cu->dbg, - IDX_debug_ranges, offset, 1)) - return -1l; - - readp = d->d_buf + offset; - readendp = d->d_buf + d->d_size; + if (__libdw_offset_in_section (cu->dbg, + secidx, offset, 1)) + return -1; } - next: - if (readendp - readp < die->cu->address_size * 2) - goto invalid; + readp = d->d_buf + offset; + readendp = d->d_buf + d->d_size; Dwarf_Addr begin; Dwarf_Addr end; - switch (__libdw_read_begin_end_pair_inc (die->cu->dbg, IDX_debug_ranges, - &readp, die->cu->address_size, + next: + switch (__libdw_read_begin_end_pair_inc (cu, secidx, + &readp, readendp, + cu->address_size, &begin, &end, basep)) { case 0: @@ -172,22 +547,11 @@ dwarf_ranges (Dwarf_Die *die, ptrdiff_t offset, Dwarf_Addr *basep, case 2: return 0; default: - return -1l; - } - - /* We have an address range entry. Check that we have a base. */ - if (*basep == (Dwarf_Addr) -1) - { - if (INTUSE(dwarf_errno) () == 0) - { - invalid: - __libdw_seterrno (DWARF_E_INVALID_DWARF); - } return -1; } - *startp = *basep + begin; - *endp = *basep + end; + *startp = begin; + *endp = end; return readp - (unsigned char *) d->d_buf; } INTDEF (dwarf_ranges) diff --git a/libdw/dwarf_setalt.c b/libdw/dwarf_setalt.c index 9bd566ff..9051b8e0 100644 --- a/libdw/dwarf_setalt.c +++ b/libdw/dwarf_setalt.c @@ -1,5 +1,5 @@ /* Provides the data referenced by the .gnu_debugaltlink section. - Copyright (C) 2014 Red Hat, Inc. + Copyright (C) 2014, 2018 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -32,9 +32,18 @@ #include "libdwP.h" +#include <unistd.h> + void dwarf_setalt (Dwarf *main, Dwarf *alt) { + if (main->alt_fd != -1) + { + INTUSE(dwarf_end) (main->alt_dwarf); + close (main->alt_fd); + main->alt_fd = -1; + } + main->alt_dwarf = alt; } INTDEF (dwarf_setalt) diff --git a/libdw/dwarf_siblingof.c b/libdw/dwarf_siblingof.c index df39c1cb..613d2090 100644 --- a/libdw/dwarf_siblingof.c +++ b/libdw/dwarf_siblingof.c @@ -58,8 +58,6 @@ dwarf_siblingof (Dwarf_Die *die, Dwarf_Die *result) sibattr.cu = this_die.cu; /* That's the address we start looking. */ unsigned char *addr = this_die.addr; - /* End of the buffer. */ - unsigned char *endp = sibattr.cu->endp; /* Search for the beginning of the next die on this level. We must not return the dies for children of the given die. */ @@ -96,6 +94,8 @@ dwarf_siblingof (Dwarf_Die *die, Dwarf_Die *result) /* This abbreviation has children. */ ++level; + /* End of the buffer. */ + unsigned char *endp = sibattr.cu->endp; while (1) { @@ -125,6 +125,7 @@ dwarf_siblingof (Dwarf_Die *die, Dwarf_Die *result) while (level > 0); /* Maybe we reached the end of the CU. */ + unsigned char *endp = sibattr.cu->endp; if (addr >= endp) return 1; diff --git a/libdw/libdw.h b/libdw/libdw.h index 63a38ff9..e20961be 100644 --- a/libdw/libdw.h +++ b/libdw/libdw.h @@ -1,5 +1,5 @@ /* Interfaces for libdw. - Copyright (C) 2002-2010, 2013, 2014, 2016 Red Hat, Inc. + Copyright (C) 2002-2010, 2013, 2014, 2016, 2018 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -211,7 +211,9 @@ typedef union Dwarf_FDE fde; } Dwarf_CFI_Entry; -#define dwarf_cfi_cie_p(entry) ((entry)->cie.CIE_id == DW_CIE_ID_64) +/* Same as DW_CIE_ID_64 from dwarf.h to keep libdw.h independent. */ +#define LIBDW_CIE_ID 0xffffffffffffffffULL +#define dwarf_cfi_cie_p(entry) ((entry)->cie.CIE_id == LIBDW_CIE_ID) /* Opaque type representing a frame state described by CFI. */ typedef struct Dwarf_Frame_s Dwarf_Frame; @@ -248,7 +250,9 @@ extern Elf *dwarf_getelf (Dwarf *dwarf); extern Dwarf *dwarf_cu_getdwarf (Dwarf_CU *cu); /* Retrieves the DWARF descriptor for debugaltlink data. Returns NULL - if no alternate debug data has been supplied. */ + if no alternate debug data has been supplied yet. libdw will try + to set the alt file on first use of an alt FORM if not yet explicitly + provided by dwarf_setalt. */ extern Dwarf *dwarf_getalt (Dwarf *main); /* Provides the data referenced by the .gnu_debugaltlink section. The @@ -256,16 +260,15 @@ extern Dwarf *dwarf_getalt (Dwarf *main); same build ID). It is the responsibility of the caller to ensure that the data referenced by ALT stays valid while it is used by MAIN, until dwarf_setalt is called on MAIN with a different - descriptor, or dwarf_end. */ + descriptor, or dwarf_end. Must be called before inspecting DIEs + that might have alt FORMs. Otherwise libdw will try to set the + alt file itself on first use. */ extern void dwarf_setalt (Dwarf *main, Dwarf *alt); /* Release debugging handling context. */ extern int dwarf_end (Dwarf *dwarf); -/* Get the data block for the .debug_info section. */ -extern Elf_Data *dwarf_getscn_info (Dwarf *dwarf); - /* Read the header for the DWARF CU. */ extern int dwarf_nextcu (Dwarf *dwarf, Dwarf_Off off, Dwarf_Off *next_off, size_t *header_sizep, Dwarf_Off *abbrev_offsetp, @@ -283,6 +286,33 @@ extern int dwarf_next_unit (Dwarf *dwarf, Dwarf_Off off, Dwarf_Off *next_off, __nonnull_attribute__ (3); +/* Gets the next Dwarf_CU (unit), version, unit type and if available + the CU DIE and sub (type) DIE of the unit. Returns 0 on success, + -1 on error or 1 if there are no more units. To start iterating + provide NULL for CU. If version < 5 the unit type is set from the + CU DIE if available (DW_UT_compile for DW_TAG_compile_unit, + DW_UT_type for DW_TAG_type_unit or DW_UT_partial for + DW_TAG_partial_unit), otherwise it is set to zero. If unavailable + (the version or unit type is unknown) the CU DIE is cleared. + Likewise if the sub DIE isn't isn't available (the unit type is not + DW_UT_type or DW_UT_split_type) the sub DIE tag is cleared. */ +extern int dwarf_get_units (Dwarf *dwarf, Dwarf_CU *cu, Dwarf_CU **next_cu, + Dwarf_Half *version, uint8_t *unit_type, + Dwarf_Die *cudie, Dwarf_Die *subdie) + __nonnull_attribute__ (3); + +/* Provides information and DIEs associated with the given Dwarf_CU + unit. Returns -1 on error, zero on success. Arguments not needed + may be NULL. If they are NULL and aren't known yet, they won't be + looked up. If the subdie doesn't exist for this unit_type it will + be cleared. If there is no unit_id for this unit type it will be + set to zero. */ +extern int dwarf_cu_info (Dwarf_CU *cu, + Dwarf_Half *version, uint8_t *unit_type, + Dwarf_Die *cudie, Dwarf_Die *subdie, + uint64_t *unit_id, + uint8_t *address_size, uint8_t *offset_size); + /* Decode one DWARF CFI entry (CIE or FDE) from the raw section data. The E_IDENT from the originating ELF file indicates the address size and byte order used in the CFI section contained in DATA; @@ -344,6 +374,18 @@ extern Dwarf_Die *dwarf_diecu (Dwarf_Die *die, Dwarf_Die *result, uint8_t *address_sizep, uint8_t *offset_sizep) __nonnull_attribute__ (2); +/* Given a Dwarf_Die addr returns a (reconstructed) Dwarf_Die, or NULL + if the given addr didn't come from a valid Dwarf_Die. In particular + it will make sure that the correct Dwarf_CU pointer is set for the + Dwarf_Die, the Dwarf_Abbrev pointer will not be set up yet (it will + only be once the Dwarf_Die is used to read attributes, children or + siblings). This functions can be used to keep a reference to a + Dwarf_Die which you want to refer to later. The addr, and the result + of this function, is only valid while the associated Dwarf is valid. */ +extern Dwarf_Die *dwarf_die_addr_die (Dwarf *dbg, void *addr, + Dwarf_Die *result) + __nonnull_attribute__ (3); + /* Return the CU DIE and the header info associated with a Dwarf_Die or Dwarf_Attribute. A Dwarf_Die or a Dwarf_Attribute is associated with a particular Dwarf_CU handle. This function returns the CU or @@ -561,6 +603,12 @@ extern int dwarf_getabbrevattr (Dwarf_Abbrev *abbrev, size_t idx, unsigned int *namep, unsigned int *formp, Dwarf_Off *offset); +/* Get specific attribute of abbreviation and any data encoded with it. + Specifically for DW_FORM_implicit_const data will be set to the + constant value associated. */ +extern int dwarf_getabbrevattr_data (Dwarf_Abbrev *abbrev, size_t idx, + unsigned int *namep, unsigned int *formp, + Dwarf_Sword *datap, Dwarf_Off *offset); /* Get string from-debug_str section. */ extern const char *dwarf_getstring (Dwarf *dbg, Dwarf_Off offset, @@ -640,11 +688,15 @@ extern int dwarf_linediscriminator (Dwarf_Line *line, unsigned int *discp) __nonnull_attribute__ (2); -/* Find line information for address. */ +/* Find line information for address. The returned string is NULL when + an error occured, or the file path. The file path is either absolute + or relative to the compilation directory. See dwarf_decl_file. */ extern const char *dwarf_linesrc (Dwarf_Line *line, Dwarf_Word *mtime, Dwarf_Word *length); -/* Return file information. */ +/* Return file information. The returned string is NULL when + an error occured, or the file path. The file path is either absolute + or relative to the compilation directory. See dwarf_decl_file. */ extern const char *dwarf_filesrc (Dwarf_Files *file, size_t idx, Dwarf_Word *mtime, Dwarf_Word *length); @@ -661,6 +713,24 @@ extern int dwarf_getsrcdirs (Dwarf_Files *files, const char *const **result, size_t *ndirs) __nonnull_attribute__ (2, 3); +/* Iterates through the debug line units. Returns 0 on success, -1 on + error or 1 if there are no more units. To start iterating use zero + for OFF and set *CU to NULL. On success NEXT_OFF will be set to + the next offset to use. The *CU will be set if this line table + needed a specific CU and needs to be given when calling + dwarf_next_lines again (to help dwarf_next_lines quickly find the + next CU). *CU might be set to NULL when it couldn't be found (the + compilation directory entry will be the empty string in that case) + or for DWARF 5 or later tables, which are self contained. SRCFILES + and SRCLINES may be NULL if the caller is not interested in the + actual line or file table. On success and when not NULL, NFILES + and NLINES will be set to the number of files in the file table and + number of lines in the line table. */ +extern int dwarf_next_lines (Dwarf *dwarf, Dwarf_Off off, + Dwarf_Off *next_off, Dwarf_CU **cu, + Dwarf_Files **srcfiles, size_t *nfiles, + Dwarf_Lines **srclines, size_t *nlines) + __nonnull_attribute__ (3,4); /* Return location expression, decoded as a list of operations. */ extern int dwarf_getlocation (Dwarf_Attribute *attr, Dwarf_Op **expr, @@ -816,7 +886,14 @@ extern ptrdiff_t dwarf_getfuncs (Dwarf_Die *cudie, void *arg, ptrdiff_t offset); -/* Return file name containing definition of the given declaration. */ +/* Return file name containing definition of the given declaration. + Of the DECL has an (indirect, see dwarf_attr_integrate) decl_file + attribute. The returned file path is either absolute, or relative + to the compilation directory. Given the decl DIE, the compilation + directory can be retrieved through: + dwarf_formstring (dwarf_attr (dwarf_diecu (decl, &cudie, NULL, NULL), + DW_AT_comp_dir, &attr)); + Returns NULL if no decl_file could be found or an error occured. */ extern const char *dwarf_decl_file (Dwarf_Die *decl); /* Get line number of beginning of given declaration. */ diff --git a/libdw/libdw.map b/libdw/libdw.map index 14307056..3fef2ede 100644 --- a/libdw/libdw.map +++ b/libdw/libdw.map @@ -49,7 +49,6 @@ ELFUTILS_0.122 { dwarf_getlocation_addr; dwarf_getmacros; dwarf_getpubnames; - dwarf_getscn_info; dwarf_getscopes; dwarf_getscopes_die; dwarf_getscopevar; @@ -344,3 +343,16 @@ ELFUTILS_0.170 { dwarf_default_lower_bound; dwarf_line_file; } ELFUTILS_0.167; + +ELFUTILS_0.171 { + global: + dwarf_die_addr_die; + dwarf_get_units; + dwarf_getabbrevattr_data; + dwarf_cu_info; +} ELFUTILS_0.170; + +ELFUTILS_0.173 { + global: + dwarf_next_lines; +} ELFUTILS_0.171; diff --git a/libdw/libdwP.h b/libdw/libdwP.h index 6ad322c1..eebb7d12 100644 --- a/libdw/libdwP.h +++ b/libdw/libdwP.h @@ -1,7 +1,6 @@ -/* Internal definitions for libdwarf. - Copyright (C) 2002-2011, 2013-2016 Red Hat, Inc. +/* Internal definitions for libdw. + Copyright (C) 2002-2011, 2013-2018 Red Hat, Inc. This file is part of elfutils. - Written by Ulrich Drepper <[email protected]>, 2002. This file is free software; you can redistribute it and/or modify it under the terms of either @@ -74,14 +73,19 @@ enum IDX_debug_types, IDX_debug_abbrev, IDX_debug_aranges, + IDX_debug_addr, IDX_debug_line, + IDX_debug_line_str, IDX_debug_frame, IDX_debug_loc, + IDX_debug_loclists, IDX_debug_pubnames, IDX_debug_str, + IDX_debug_str_offsets, IDX_debug_macinfo, IDX_debug_macro, IDX_debug_ranges, + IDX_debug_rnglists, IDX_gnu_debugaltlink, IDX_last }; @@ -108,6 +112,9 @@ enum DWARF_E_NO_ENTRY, DWARF_E_INVALID_DWARF, DWARF_E_NO_STRING, + DWARF_E_NO_DEBUG_STR, + DWARF_E_NO_DEBUG_LINE_STR, + DWARF_E_NO_STR_OFFSETS, DWARF_E_NO_ADDR, DWARF_E_NO_CONSTANT, DWARF_E_NO_REFERENCE, @@ -118,7 +125,9 @@ enum DWARF_E_VERSION, DWARF_E_INVALID_DIR_IDX, DWARF_E_ADDR_OUTOFRANGE, - DWARF_E_NO_LOCLIST, + DWARF_E_NO_DEBUG_LOC, + DWARF_E_NO_DEBUG_LOCLISTS, + DWARF_E_NO_LOC_VALUE, DWARF_E_NO_BLOCK, DWARF_E_INVALID_LINE_IDX, DWARF_E_INVALID_ARANGE_IDX, @@ -126,11 +135,13 @@ enum DWARF_E_NO_FLAG, DWARF_E_INVALID_OFFSET, DWARF_E_NO_DEBUG_RANGES, + DWARF_E_NO_DEBUG_RNGLISTS, DWARF_E_INVALID_CFI, DWARF_E_NO_ALT_DEBUGLINK, DWARF_E_INVALID_OPCODE, DWARF_E_NOT_CUDIE, DWARF_E_UNKNOWN_LANGUAGE, + DWARF_E_NO_DEBUG_ADDR, }; @@ -142,6 +153,10 @@ struct Dwarf /* The underlying ELF file. */ Elf *elf; + /* The (absolute) path to the ELF dir, if known. To help locating + alt and dwo files. */ + char *debugdir; + /* dwz alternate DWARF file. */ Dwarf *alt_dwarf; @@ -154,6 +169,10 @@ struct Dwarf /* If true, we allocated the ELF descriptor ourselves. */ bool free_elf; + /* If >= 0, we allocated the alt_dwarf ourselves and must end it and + close this file descriptor. */ + int alt_fd; + /* Information for traversing the .debug_pubnames section. This is an array and separately allocated with malloc. */ struct pubnames_s @@ -174,6 +193,9 @@ struct Dwarf Dwarf_Off next_tu_offset; Dwarf_Sig8_Hash sig8_hash; + /* Search tree for split Dwarf associated with CUs in this debug. */ + void *split_tree; + /* Search tree for .debug_macro operator tables. */ void *macro_ops; @@ -187,8 +209,14 @@ struct Dwarf struct Dwarf_CFI_s *cfi; /* Fake loc CU. Used when synthesizing attributes for Dwarf_Ops that - came from a location list entry in dwarf_getlocation_attr. */ + came from a location list entry in dwarf_getlocation_attr. + Depending on version this is the .debug_loc or .debug_loclists + section (could be both if mixing CUs with different DWARF versions). */ struct Dwarf_CU *fake_loc_cu; + struct Dwarf_CU *fake_loclists_cu; + + /* Similar for addrx/constx, which will come from .debug_addr section. */ + struct Dwarf_CU *fake_addr_cu; /* Internal memory handling. This is basically a simplified reimplementation of obstacks. Unfortunately the standard obstack @@ -212,13 +240,12 @@ struct Dwarf /* Abbreviation representation. */ struct Dwarf_Abbrev { - Dwarf_Off offset; - unsigned char *attrp; - unsigned int attrcnt; - unsigned int code; - unsigned int tag; - bool has_children; -}; + Dwarf_Off offset; /* Offset to start of abbrev into .debug_abbrev. */ + unsigned char *attrp; /* Pointer to start of attribute name/form pairs. */ + bool has_children : 1; /* Whether or not the DIE has children. */ + unsigned int code : 31; /* The (unique) abbrev code. */ + unsigned int tag; /* The tag of the DIE. */ +} attribute_packed; #include "dwarf_abbrev_hash.h" @@ -293,9 +320,23 @@ struct Dwarf_CU uint8_t offset_size; uint16_t version; - /* Zero if this is a normal CU. Nonzero if it is a type unit. */ - size_t type_offset; - uint64_t type_sig8; + size_t sec_idx; /* Normally .debug_info, could be .debug_type or "fake". */ + + /* The unit type if version >= 5. Otherwise 0 for normal CUs (from + .debug_info) or 1 for v4 type units (from .debug_types). */ + uint8_t unit_type; + + /* Zero if the unit type doesn't support a die/type offset and/or id/sig. + Nonzero if it is a v4 type unit or for DWARFv5 units depending on + unit_type. */ + size_t subdie_offset; + uint64_t unit_id8; + + /* If this is a skeleton unit this points to the split compile unit. + Or the other way around if this is a split compile unit. Set to -1 + if not yet searched. Always use __libdw_find_split_unit to access + this field. */ + struct Dwarf_CU *split; /* Hash table for the abbreviations. */ Dwarf_Abbrev_Hash abbrev_hash; @@ -313,13 +354,57 @@ struct Dwarf_CU /* Known location lists. */ void *locs; + /* Base address for use with ranges and locs. + Don't access directly, call __libdw_cu_base_address. */ + Dwarf_Addr base_address; + + /* The offset into the .debug_addr section where index zero begins. + Don't access directly, call __libdw_cu_addr_base. */ + Dwarf_Off addr_base; + + /* The offset into the .debug_str_offsets section where index zero begins. + Don't access directly, call __libdw_cu_str_off_base. */ + Dwarf_Off str_off_base; + + /* The offset into the .debug_ranges section to use for GNU + DebugFission split units. Don't access directly, call + __libdw_cu_ranges_base. */ + Dwarf_Off ranges_base; + + /* The start of the offset table in .debug_loclists. + Don't access directly, call __libdw_cu_locs_base. */ + Dwarf_Off locs_base; + /* Memory boundaries of this CU. */ void *startp; void *endp; }; -/* Compute the offset of a CU's first DIE from its offset. This - is either: +#define ISV4TU(cu) ((cu)->version == 4 && (cu)->sec_idx == IDX_debug_types) + +/* Compute the offset of a CU's first DIE from the CU offset. + CU must be a valid/known version/unit_type. */ +static inline Dwarf_Off +__libdw_first_die_from_cu_start (Dwarf_Off cu_start, + uint8_t offset_size, + uint16_t version, + uint8_t unit_type) +{ +/* + assert (offset_size == 4 || offset_size == 8); + assert (version >= 2 && version <= 5); + assert (unit_type == DW_UT_compile + || unit_type == DW_UT_partial + || unit_type == DW_UT_skeleton + || unit_type == DW_UT_split_compile + || unit_type == DW_UT_type + || unit_type == DW_UT_split_type); +*/ + + Dwarf_Off off = cu_start; + if (version < 5) + { + /* LEN VER OFFSET ADDR 4-bytes + 2-bytes + 4-bytes + 1-byte for 32-bit dwarf 12-bytes + 2-bytes + 8-bytes + 1-byte for 64-bit dwarf @@ -328,22 +413,61 @@ struct Dwarf_CU 12-bytes + 2-bytes + 8-bytes + 1-byte + 8-bytes + 8-bytes for 64-bit Note the trick in the computation. If the offset_size is 4 - the '- 4' term changes the '3 *' into a '2 *'. If the - offset_size is 8 it accounts for the 4-byte escape value + the '- 4' term changes the '3 *' (or '4 *') into a '2 *' (or '3 *). + If the offset_size is 8 it accounts for the 4-byte escape value used at the start of the length. */ -#define DIE_OFFSET_FROM_CU_OFFSET(cu_offset, offset_size, type_unit) \ - ((type_unit) ? ((cu_offset) + 4 * (offset_size) - 4 + 3 + 8) \ - : ((cu_offset) + 3 * (offset_size) - 4 + 3)) + if (unit_type != DW_UT_type) + off += 3 * offset_size - 4 + 3; + else + off += 4 * offset_size - 4 + 3 + 8; + } + else + { + /* + LEN VER TYPE ADDR OFFSET SIGNATURE TYPE-OFFSET + 4-bytes + 2-bytes + 1-byte + 1-byte + 4-bytes + 8-bytes + 4-bytes 32-bit + 12-bytes + 2-bytes + 1-byte + 1-byte + 8-bytes + 8-bytes + 8-bytes 64-bit + Both signature and type offset are optional. + + Note same 4/8 offset size trick as above. + We explicitly ignore unknow unit types (see asserts above). */ + off += 3 * offset_size - 4 + 4; + if (unit_type == DW_UT_skeleton || unit_type == DW_UT_split_compile + || unit_type == DW_UT_type || unit_type == DW_UT_split_type) + { + off += 8; + if (unit_type == DW_UT_type || unit_type == DW_UT_split_type) + off += offset_size; + } + } + + return off; +} + +static inline Dwarf_Off +__libdw_first_die_off_from_cu (struct Dwarf_CU *cu) +{ + return __libdw_first_die_from_cu_start (cu->start, + cu->offset_size, + cu->version, + cu->unit_type); +} #define CUDIE(fromcu) \ ((Dwarf_Die) \ { \ .cu = (fromcu), \ - .addr = ((char *) fromcu->dbg->sectiondata[cu_sec_idx (fromcu)]->d_buf \ - + DIE_OFFSET_FROM_CU_OFFSET ((fromcu)->start, \ - (fromcu)->offset_size, \ - (fromcu)->type_offset != 0)) \ - }) \ + .addr = ((char *) (fromcu)->dbg->sectiondata[cu_sec_idx (fromcu)]->d_buf \ + + __libdw_first_die_off_from_cu (fromcu)) \ + }) + +#define SUBDIE(fromcu) \ + ((Dwarf_Die) \ + { \ + .cu = (fromcu), \ + .addr = ((char *) (fromcu)->dbg->sectiondata[cu_sec_idx (fromcu)]->d_buf \ + + (fromcu)->start + (fromcu)->subdie_offset) \ + }) /* Prototype of a single .debug_macro operator. */ @@ -399,6 +523,44 @@ libdw_macro_nforms (Dwarf_Macro *macro) return macro->table->table[macro->table->opcodes[macro->opcode - 1]].nforms; } +/* Returns true for any allowed FORM in the opcode_operands_table as + mentioned in the DWARF5 spec (6.3.1 Macro Information Header). + Or those mentioned in DWARF5 spec (6.2.4.2 Vendor-defined Content + Descriptions) for the directory/file table (plus DW_FORM_strp_sup). */ +static inline bool +libdw_valid_user_form (int form) +{ + switch (form) + { + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + case DW_FORM_data1: + case DW_FORM_data2: + case DW_FORM_data4: + case DW_FORM_data8: + case DW_FORM_data16: + case DW_FORM_flag: + case DW_FORM_line_strp: + case DW_FORM_sdata: + case DW_FORM_sec_offset: + case DW_FORM_string: + case DW_FORM_strp: + case DW_FORM_strp_sup: + case DW_FORM_strx: + case DW_FORM_strx1: + case DW_FORM_strx2: + case DW_FORM_strx3: + case DW_FORM_strx4: + case DW_FORM_udata: + return true; + default: + return false; + } +} + + /* We have to include the file at this point because the inline functions access internals of the Dwarf structure. */ #include "memory-access.h" @@ -434,7 +596,19 @@ extern void *__libdw_allocate (Dwarf *dbg, size_t minsize, size_t align) __attribute__ ((__malloc__)) __nonnull_attribute__ (1); /* Default OOM handler. */ -extern void __libdw_oom (void) __attribute ((noreturn, visibility ("hidden"))); +extern void __libdw_oom (void) __attribute ((noreturn)) attribute_hidden; + +/* Read next unit (or v4 debug type) and return next offset. Doesn't + create an actual Dwarf_CU just provides necessary header fields. */ +extern int +internal_function +__libdw_next_unit (Dwarf *dbg, bool v4_debug_types, Dwarf_Off off, + Dwarf_Off *next_off, size_t *header_sizep, + Dwarf_Half *versionp, uint8_t *unit_typep, + Dwarf_Off *abbrev_offsetp, uint8_t *address_sizep, + uint8_t *offset_sizep, uint64_t *unit_id8p, + Dwarf_Off *subdie_offsetp) + __nonnull_attribute__ (4) internal_function; /* Allocate the internal data for a unit not seen before. */ extern struct Dwarf_CU *__libdw_intern_next_unit (Dwarf *dbg, bool debug_types) @@ -444,6 +618,18 @@ extern struct Dwarf_CU *__libdw_intern_next_unit (Dwarf *dbg, bool debug_types) extern struct Dwarf_CU *__libdw_findcu (Dwarf *dbg, Dwarf_Off offset, bool tu) __nonnull_attribute__ (1) internal_function; +/* Find CU for given DIE address. */ +extern struct Dwarf_CU *__libdw_findcu_addr (Dwarf *dbg, void *addr) + __nonnull_attribute__ (1) internal_function; + +/* Find split Dwarf for given DIE address. */ +extern struct Dwarf *__libdw_find_split_dbg_addr (Dwarf *dbg, void *addr) + __nonnull_attribute__ (1) internal_function; + +/* Find the split (or skeleton) unit. */ +extern struct Dwarf_CU *__libdw_find_split_unit (Dwarf_CU *cu) + internal_function; + /* Get abbreviation with given code. */ extern Dwarf_Abbrev *__libdw_findabbrev (struct Dwarf_CU *cu, unsigned int code) @@ -467,6 +653,9 @@ __libdw_dieabbrev (Dwarf_Die *die, const unsigned char **readp) /* Get the abbreviation code. */ unsigned int code; const unsigned char *addr = die->addr; + if (unlikely (die->cu == NULL + || addr >= (const unsigned char *) die->cu->endp)) + return die->abbrev = DWARF_END_ABBREV; get_uleb128 (code, addr, die->cu->endp); if (readp != NULL) *readp = addr; @@ -484,7 +673,7 @@ extern size_t __libdw_form_val_compute_len (struct Dwarf_CU *cu, const unsigned char *valp) __nonnull_attribute__ (1, 3) internal_function; -/* Find the length of a form attribute. */ +/* Find the length of a form attribute in DIE/info data. */ static inline size_t __nonnull_attribute__ (1, 3) __libdw_form_val_len (struct Dwarf_CU *cu, unsigned int form, @@ -495,10 +684,24 @@ __libdw_form_val_len (struct Dwarf_CU *cu, unsigned int form, static const uint8_t form_lengths[] = { [DW_FORM_flag_present] = 0x80, - [DW_FORM_data1] = 1, [DW_FORM_ref1] = 1, [DW_FORM_flag] = 1, + [DW_FORM_implicit_const] = 0x80, /* Value is in abbrev, not in info. */ + + [DW_FORM_flag] = 1, + [DW_FORM_data1] = 1, [DW_FORM_ref1] = 1, + [DW_FORM_addrx1] = 1, [DW_FORM_strx1] = 1, + [DW_FORM_data2] = 2, [DW_FORM_ref2] = 2, - [DW_FORM_data4] = 4, [DW_FORM_ref4] = 4, - [DW_FORM_data8] = 8, [DW_FORM_ref8] = 8, [DW_FORM_ref_sig8] = 8, + [DW_FORM_addrx2] = 2, [DW_FORM_strx2] = 2, + + [DW_FORM_addrx3] = 3, [DW_FORM_strx3] = 3, + + [DW_FORM_data4] = 4, [DW_FORM_ref4] = 4, [DW_FORM_ref_sup4] = 4, + [DW_FORM_addrx4] = 4, [DW_FORM_strx4] = 4, + + [DW_FORM_ref_sig8] = 8, + [DW_FORM_data8] = 8, [DW_FORM_ref8] = 8, [DW_FORM_ref_sup8] = 8, + + [DW_FORM_data16] = 16, }; /* Return immediately for forms with fixed lengths. */ @@ -626,7 +829,8 @@ __libdw_offset_in_section (Dwarf *dbg, int sec_index, if (data == NULL) return -1; if (unlikely (offset > data->d_size) - || unlikely (data->d_size - offset < size)) + || unlikely (data->d_size < size) + || unlikely (offset > data->d_size - size)) { __libdw_seterrno (DWARF_E_INVALID_OFFSET); return -1; @@ -643,7 +847,8 @@ __libdw_in_section (Dwarf *dbg, int sec_index, if (data == NULL) return false; if (unlikely (addr < data->d_buf) - || unlikely (data->d_size - (addr - data->d_buf) < size)) + || unlikely (data->d_size < size) + || unlikely ((size_t)(addr - data->d_buf) > data->d_size - size)) { __libdw_seterrno (DWARF_E_INVALID_OFFSET); return false; @@ -714,13 +919,13 @@ __libdw_read_offset (Dwarf *dbg, Dwarf *dbg_ret, static inline size_t cu_sec_idx (struct Dwarf_CU *cu) { - return cu->type_offset == 0 ? IDX_debug_info : IDX_debug_types; + return cu->sec_idx; } static inline bool is_cudie (Dwarf_Die *cudie) { - return CUDIE (cudie->cu).addr == cudie->addr; + return cudie->cu != NULL && CUDIE (cudie->cu).addr == cudie->addr; } /* Read up begin/end pair and increment read pointer. @@ -728,15 +933,18 @@ is_cudie (Dwarf_Die *cudie) - If it's base address selection record, set up *BASEP and return 1. - If it's end of rangelist, don't set anything and return 2 - If an error occurs, don't set anything and return <0. */ -int __libdw_read_begin_end_pair_inc (Dwarf *dbg, int sec_index, - unsigned char **addr, int width, +int __libdw_read_begin_end_pair_inc (Dwarf_CU *cu, int sec_index, + const unsigned char **readp, + const unsigned char *readend, + int width, Dwarf_Addr *beginp, Dwarf_Addr *endp, Dwarf_Addr *basep) internal_function; -unsigned char * __libdw_formptr (Dwarf_Attribute *attr, int sec_index, - int err_nodata, unsigned char **endpp, - Dwarf_Off *offsetp) +const unsigned char * __libdw_formptr (Dwarf_Attribute *attr, int sec_index, + int err_nodata, + const unsigned char **endpp, + Dwarf_Off *offsetp) internal_function; /* Fills in the given attribute to point at an empty location expression. */ @@ -757,6 +965,363 @@ int __libdw_getsrclines (Dwarf *dbg, Dwarf_Off debug_line_offset, /* Load and return value of DW_AT_comp_dir from CUDIE. */ const char *__libdw_getcompdir (Dwarf_Die *cudie); +/* Get the base address for the CU, fetches it when not yet set. + This is used as initial base address for ranges and loclists. */ +Dwarf_Addr __libdw_cu_base_address (Dwarf_CU *cu); + +/* Get the address base for the CU, fetches it when not yet set. */ +static inline Dwarf_Off +__libdw_cu_addr_base (Dwarf_CU *cu) +{ + if (cu->addr_base == (Dwarf_Off) -1) + { + Dwarf_Die cu_die = CUDIE(cu); + Dwarf_Attribute attr; + Dwarf_Off offset = 0; + if (dwarf_attr (&cu_die, DW_AT_GNU_addr_base, &attr) != NULL + || dwarf_attr (&cu_die, DW_AT_addr_base, &attr) != NULL) + { + Dwarf_Word off; + if (dwarf_formudata (&attr, &off) == 0) + offset = off; + } + cu->addr_base = offset; + } + + return cu->addr_base; +} + +/* Gets the .debug_str_offsets base offset to use. static inline to + be shared between libdw and eu-readelf. */ +static inline Dwarf_Off +str_offsets_base_off (Dwarf *dbg, Dwarf_CU *cu) +{ + /* If we don't have a CU, then find and use the first one in the + debug file (when we support .dwp files, we must actually find the + one matching our "caller" - aka macro or line). If we (now) have + a cu and str_offsets_base attribute, just use that. Otherwise + use the first offset. But we might have to parse the header + first, but only if this is version 5. Assume if all else fails, + this is version 4, without header. */ + + if (cu == NULL && dbg != NULL) + { + Dwarf_CU *first_cu; + if (dwarf_get_units (dbg, NULL, &first_cu, + NULL, NULL, NULL, NULL) == 0) + cu = first_cu; + } + + if (cu != NULL) + { + if (cu->str_off_base == (Dwarf_Off) -1) + { + Dwarf_Die cu_die = CUDIE(cu); + Dwarf_Attribute attr; + if (dwarf_attr (&cu_die, DW_AT_str_offsets_base, &attr) != NULL) + { + Dwarf_Word off; + if (dwarf_formudata (&attr, &off) == 0) + { + cu->str_off_base = off; + return cu->str_off_base; + } + } + /* For older DWARF simply assume zero (no header). */ + if (cu->version < 5) + { + cu->str_off_base = 0; + return cu->str_off_base; + } + + if (dbg == NULL) + dbg = cu->dbg; + } + else + return cu->str_off_base; + } + + /* No str_offsets_base attribute, we have to assume "zero". + But there could be a header first. */ + Dwarf_Off off = 0; + if (dbg == NULL) + goto no_header; + + Elf_Data *data = dbg->sectiondata[IDX_debug_str_offsets]; + if (data == NULL) + goto no_header; + + const unsigned char *start; + const unsigned char *readp; + const unsigned char *readendp; + start = readp = (const unsigned char *) data->d_buf; + readendp = (const unsigned char *) data->d_buf + data->d_size; + + uint64_t unit_length; + uint16_t version; + + unit_length = read_4ubyte_unaligned_inc (dbg, readp); + if (unlikely (unit_length == 0xffffffff)) + { + if (unlikely (readendp - readp < 8)) + goto no_header; + unit_length = read_8ubyte_unaligned_inc (dbg, readp); + /* In theory the offset size could be different + between CU and str_offsets unit. But we just + ignore that here. */ + } + + /* We need at least 2-bytes (version) + 2-bytes (padding) = + 4 bytes to complete the header. And this unit cannot go + beyond the section data. */ + if (readendp - readp < 4 + || unit_length < 4 + || (uint64_t) (readendp - readp) < unit_length) + goto no_header; + + version = read_2ubyte_unaligned_inc (dbg, readp); + if (version != 5) + goto no_header; + /* padding */ + read_2ubyte_unaligned_inc (dbg, readp); + + off = (Dwarf_Off) (readp - start); + + no_header: + if (cu != NULL) + cu->str_off_base = off; + + return off; +} + + +/* Get the string offsets base for the CU, fetches it when not yet set. */ +static inline Dwarf_Off __libdw_cu_str_off_base (Dwarf_CU *cu) +{ + return str_offsets_base_off (NULL, cu); +} + + +/* Either a direct offset into .debug_ranges for version < 5, or the + start of the offset table in .debug_rnglists for version > 5. */ +static inline Dwarf_Off +__libdw_cu_ranges_base (Dwarf_CU *cu) +{ + if (cu->ranges_base == (Dwarf_Off) -1) + { + Dwarf_Off offset = 0; + Dwarf_Die cu_die = CUDIE(cu); + Dwarf_Attribute attr; + if (cu->version < 5) + { + if (dwarf_attr (&cu_die, DW_AT_GNU_ranges_base, &attr) != NULL) + { + Dwarf_Word off; + if (dwarf_formudata (&attr, &off) == 0) + offset = off; + } + } + else + { + if (dwarf_attr (&cu_die, DW_AT_rnglists_base, &attr) != NULL) + { + Dwarf_Word off; + if (dwarf_formudata (&attr, &off) == 0) + offset = off; + } + + /* There wasn't an rnglists_base, if the Dwarf does have a + .debug_rnglists section, then it might be we need the + base after the first header. */ + Elf_Data *data = cu->dbg->sectiondata[IDX_debug_rnglists]; + if (offset == 0 && data != NULL) + { + Dwarf *dbg = cu->dbg; + const unsigned char *readp = data->d_buf; + const unsigned char *const dataend + = (unsigned char *) data->d_buf + data->d_size; + + uint64_t unit_length = read_4ubyte_unaligned_inc (dbg, readp); + unsigned int offset_size = 4; + if (unlikely (unit_length == 0xffffffff)) + { + if (unlikely (readp > dataend - 8)) + goto no_header; + + unit_length = read_8ubyte_unaligned_inc (dbg, readp); + offset_size = 8; + } + + if (readp > dataend - 8 + || unit_length < 8 + || unit_length > (uint64_t) (dataend - readp)) + goto no_header; + + uint16_t version = read_2ubyte_unaligned_inc (dbg, readp); + if (version != 5) + goto no_header; + + uint8_t address_size = *readp++; + if (address_size != 4 && address_size != 8) + goto no_header; + + uint8_t segment_size = *readp++; + if (segment_size != 0) + goto no_header; + + uint32_t offset_entry_count; + offset_entry_count = read_4ubyte_unaligned_inc (dbg, readp); + + const unsigned char *offset_array_start = readp; + if (offset_entry_count <= 0) + goto no_header; + + uint64_t needed = offset_entry_count * offset_size; + if (unit_length - 8 < needed) + goto no_header; + + offset = (Dwarf_Off) (offset_array_start + - (unsigned char *) data->d_buf); + } + } + no_header: + cu->ranges_base = offset; + } + + return cu->ranges_base; +} + + +/* The start of the offset table in .debug_loclists for DWARF5. */ +static inline Dwarf_Off +__libdw_cu_locs_base (Dwarf_CU *cu) +{ + if (cu->locs_base == (Dwarf_Off) -1) + { + Dwarf_Off offset = 0; + Dwarf_Die cu_die = CUDIE(cu); + Dwarf_Attribute attr; + if (dwarf_attr (&cu_die, DW_AT_loclists_base, &attr) != NULL) + { + Dwarf_Word off; + if (dwarf_formudata (&attr, &off) == 0) + offset = off; + } + + /* There wasn't an loclists_base, if the Dwarf does have a + .debug_loclists section, then it might be we need the + base after the first header. */ + Elf_Data *data = cu->dbg->sectiondata[IDX_debug_loclists]; + if (offset == 0 && data != NULL) + { + Dwarf *dbg = cu->dbg; + const unsigned char *readp = data->d_buf; + const unsigned char *const dataend + = (unsigned char *) data->d_buf + data->d_size; + + uint64_t unit_length = read_4ubyte_unaligned_inc (dbg, readp); + unsigned int offset_size = 4; + if (unlikely (unit_length == 0xffffffff)) + { + if (unlikely (readp > dataend - 8)) + goto no_header; + + unit_length = read_8ubyte_unaligned_inc (dbg, readp); + offset_size = 8; + } + + if (readp > dataend - 8 + || unit_length < 8 + || unit_length > (uint64_t) (dataend - readp)) + goto no_header; + + uint16_t version = read_2ubyte_unaligned_inc (dbg, readp); + if (version != 5) + goto no_header; + + uint8_t address_size = *readp++; + if (address_size != 4 && address_size != 8) + goto no_header; + + uint8_t segment_size = *readp++; + if (segment_size != 0) + goto no_header; + + uint32_t offset_entry_count; + offset_entry_count = read_4ubyte_unaligned_inc (dbg, readp); + + const unsigned char *offset_array_start = readp; + if (offset_entry_count <= 0) + goto no_header; + + uint64_t needed = offset_entry_count * offset_size; + if (unit_length - 8 < needed) + goto no_header; + + offset = (Dwarf_Off) (offset_array_start + - (unsigned char *) data->d_buf); + } + + no_header: + cu->locs_base = offset; + } + + return cu->locs_base; +} + +/* Helper function for tsearch/tfind split_tree Dwarf. */ +int __libdw_finddbg_cb (const void *arg1, const void *arg2); + +/* Link skeleton and split compile units. */ +static inline void +__libdw_link_skel_split (Dwarf_CU *skel, Dwarf_CU *split) +{ + skel->split = split; + split->split = skel; + + /* Get .debug_addr and addr_base greedy. + We also need it for the fake addr cu. + There is only one per split debug. */ + Dwarf *dbg = skel->dbg; + Dwarf *sdbg = split->dbg; + if (sdbg->sectiondata[IDX_debug_addr] == NULL + && dbg->sectiondata[IDX_debug_addr] != NULL) + { + sdbg->sectiondata[IDX_debug_addr] + = dbg->sectiondata[IDX_debug_addr]; + split->addr_base = __libdw_cu_addr_base (skel); + sdbg->fake_addr_cu = dbg->fake_addr_cu; + } +} + + +/* Given an address index for a CU return the address. + Returns -1 and sets libdw_errno if an error occurs. */ +int __libdw_addrx (Dwarf_CU *cu, Dwarf_Word idx, Dwarf_Addr *addr); + + +/* Helper function to set debugdir field in Dwarf, used from dwarf_begin_elf + and libdwfl process_file. */ +char * __libdw_debugdir (int fd); + + +/* Given the directory of a debug file, an absolute or relative dir + to look in, and file returns a full path. + + If the file is absolute (starts with a /) a copy of file is returned. + the file isn't absolute, but dir is absolute, then a path that is + the concatenation of dir and file is returned. If neither file, + nor dir is absolute, the path will be constructed using dir (if not + NULL) and file relative to the debugdir (if valid). + + The debugdir and the dir may be NULL (in which case they aren't used). + If file is NULL, or no full path can be constructed NULL is returned. + + The caller is responsible for freeing the result if not NULL. */ +char * __libdw_filepath (const char *debugdir, const char *dir, + const char *file) + internal_function; + /* Aliases to avoid PLTs. */ INTDECL (dwarf_aggregate_size) @@ -777,6 +1342,7 @@ INTDECL (dwarf_formref_die) INTDECL (dwarf_formsdata) INTDECL (dwarf_formstring) INTDECL (dwarf_formudata) +INTDECL (dwarf_getabbrevattr_data) INTDECL (dwarf_getalt) INTDECL (dwarf_getarange_addr) INTDECL (dwarf_getarangeinfo) diff --git a/libdw/libdw_alloc.c b/libdw/libdw_alloc.c index 28a8cf6e..d6af23a2 100644 --- a/libdw/libdw_alloc.c +++ b/libdw/libdw_alloc.c @@ -70,7 +70,7 @@ dwarf_new_oom_handler (Dwarf *dbg, Dwarf_OOM handler) void -__attribute ((noreturn, visibility ("hidden"))) +__attribute ((noreturn)) attribute_hidden __libdw_oom (void) { while (1) diff --git a/libdw/libdw_find_split_unit.c b/libdw/libdw_find_split_unit.c new file mode 100644 index 00000000..da039e50 --- /dev/null +++ b/libdw/libdw_find_split_unit.c @@ -0,0 +1,147 @@ +/* Find the split (or skeleton) unit for a given unit. + Copyright (C) 2018 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "libdwP.h" +#include "libelfP.h" + +#include <limits.h> +#include <search.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +void +try_split_file (Dwarf_CU *cu, const char *dwo_path) +{ + int split_fd = open (dwo_path, O_RDONLY); + if (split_fd != -1) + { + Dwarf *split_dwarf = dwarf_begin (split_fd, DWARF_C_READ); + if (split_dwarf != NULL) + { + Dwarf_CU *split = NULL; + while (dwarf_get_units (split_dwarf, split, &split, + NULL, NULL, NULL, NULL) == 0) + { + if (split->unit_type == DW_UT_split_compile + && cu->unit_id8 == split->unit_id8) + { + if (tsearch (split->dbg, &cu->dbg->split_tree, + __libdw_finddbg_cb) == NULL) + { + /* Something went wrong. Don't link. */ + __libdw_seterrno (DWARF_E_NOMEM); + break; + } + + /* Link skeleton and split compile units. */ + __libdw_link_skel_split (cu, split); + + /* We have everything we need from this ELF + file. And we are going to close the fd to + not run out of file descriptors. */ + elf_cntl (split_dwarf->elf, ELF_C_FDDONE); + break; + } + } + if (cu->split == (Dwarf_CU *) -1) + dwarf_end (split_dwarf); + } + /* Always close, because we don't want to run out of file + descriptors. See also the elf_fcntl ELF_C_FDDONE call + above. */ + close (split_fd); + } +} + +Dwarf_CU * +internal_function +__libdw_find_split_unit (Dwarf_CU *cu) +{ + /* Only try once. */ + if (cu->split != (Dwarf_CU *) -1) + return cu->split; + + /* We need a skeleton unit with a comp_dir and [GNU_]dwo_name attributes. + The split unit will be the first in the dwo file and should have the + same id as the skeleton. */ + if (cu->unit_type == DW_UT_skeleton) + { + Dwarf_Die cudie = CUDIE (cu); + Dwarf_Attribute dwo_name; + /* It is fine if dwo_dir doesn't exists, but then dwo_name needs + to be an absolute path. */ + if (dwarf_attr (&cudie, DW_AT_dwo_name, &dwo_name) != NULL + || dwarf_attr (&cudie, DW_AT_GNU_dwo_name, &dwo_name) != NULL) + { + /* First try the dwo file name in the same directory + as we found the skeleton file. */ + const char *dwo_file = dwarf_formstring (&dwo_name); + const char *debugdir = cu->dbg->debugdir; + char *dwo_path = __libdw_filepath (debugdir, NULL, dwo_file); + if (dwo_path != NULL) + { + try_split_file (cu, dwo_path); + free (dwo_path); + } + + if (cu->split == (Dwarf_CU *) -1) + { + /* Try compdir plus dwo_name. */ + Dwarf_Attribute compdir; + dwarf_attr (&cudie, DW_AT_comp_dir, &compdir); + const char *dwo_dir = dwarf_formstring (&compdir); + if (dwo_dir != NULL) + { + dwo_path = __libdw_filepath (debugdir, dwo_dir, dwo_file); + if (dwo_path != NULL) + { + try_split_file (cu, dwo_path); + free (dwo_path); + } + } + } + /* XXX If still not found we could try stripping dirs from the + comp_dir and adding them from the comp_dir, assuming + someone moved a whole build tree around. */ + } + } + + /* If we found nothing, make sure we don't try again. */ + if (cu->split == (Dwarf_CU *) -1) + cu->split = NULL; + + return cu->split; +} diff --git a/libdw/libdw_findcu.c b/libdw/libdw_findcu.c index 082307b0..ed744231 100644 --- a/libdw/libdw_findcu.c +++ b/libdw/libdw_findcu.c @@ -1,5 +1,5 @@ /* Find CU for given offset. - Copyright (C) 2003-2010, 2014 Red Hat, Inc. + Copyright (C) 2003-2010, 2014, 2016, 2017, 2018 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <[email protected]>, 2003. @@ -61,6 +61,40 @@ findcu_cb (const void *arg1, const void *arg2) return 0; } +int +__libdw_finddbg_cb (const void *arg1, const void *arg2) +{ + Dwarf *dbg1 = (Dwarf *) arg1; + Dwarf *dbg2 = (Dwarf *) arg2; + + Elf_Data *dbg1_data = dbg1->sectiondata[IDX_debug_info]; + unsigned char *dbg1_start = dbg1_data->d_buf; + size_t dbg1_size = dbg1_data->d_size; + + Elf_Data *dbg2_data = dbg2->sectiondata[IDX_debug_info]; + unsigned char *dbg2_start = dbg2_data->d_buf; + size_t dbg2_size = dbg2_data->d_size; + + /* Find out which of the two arguments is the search value. It has + a size of 0. */ + if (dbg1_size == 0) + { + if (dbg1_start < dbg2_start) + return -1; + if (dbg1_start >= dbg2_start + dbg2_size) + return 1; + } + else + { + if (dbg2_start < dbg1_start) + return 1; + if (dbg2_start >= dbg1_start + dbg1_size) + return -1; + } + + return 0; +} + struct Dwarf_CU * internal_function __libdw_intern_next_unit (Dwarf *dbg, bool debug_types) @@ -71,30 +105,41 @@ __libdw_intern_next_unit (Dwarf *dbg, bool debug_types) Dwarf_Off oldoff = *offsetp; uint16_t version; + uint8_t unit_type; uint8_t address_size; uint8_t offset_size; Dwarf_Off abbrev_offset; - uint64_t type_sig8 = 0; - Dwarf_Off type_offset = 0; - - if (INTUSE(dwarf_next_unit) (dbg, oldoff, offsetp, NULL, - &version, &abbrev_offset, - &address_size, &offset_size, - debug_types ? &type_sig8 : NULL, - debug_types ? &type_offset : NULL) != 0) + uint64_t unit_id8; + Dwarf_Off subdie_offset; + + if (__libdw_next_unit (dbg, debug_types, oldoff, offsetp, NULL, + &version, &unit_type, &abbrev_offset, + &address_size, &offset_size, + &unit_id8, &subdie_offset) != 0) /* No more entries. */ return NULL; - /* We only know how to handle the DWARF version 2 through 4 formats. */ - if (unlikely (version < 2) || unlikely (version > 4)) + /* We only know how to handle the DWARF version 2 through 5 formats. + For v4 debug types we only handle version 4. */ + if (unlikely (version < 2) || unlikely (version > 5) + || (debug_types && unlikely (version != 4))) { - __libdw_seterrno (DWARF_E_INVALID_DWARF); + __libdw_seterrno (DWARF_E_VERSION); return NULL; } + /* We only handle 32 or 64 bit (4 or 8 byte) addresses and offsets. + Just assume we are dealing with 64bit in case the size is "unknown". + Too much code assumes if it isn't 4 then it is 8 (or the other way + around). */ + if (unlikely (address_size != 4 && address_size != 8)) + address_size = 8; + if (unlikely (offset_size != 4 && offset_size != 8)) + offset_size = 8; + /* Invalid or truncated debug section data? */ - Elf_Data *data = dbg->sectiondata[debug_types - ? IDX_debug_types : IDX_debug_info]; + size_t sec_idx = debug_types ? IDX_debug_types : IDX_debug_info; + Elf_Data *data = dbg->sectiondata[sec_idx]; if (unlikely (*offsetp > data->d_size)) *offsetp = data->d_size; @@ -102,24 +147,71 @@ __libdw_intern_next_unit (Dwarf *dbg, bool debug_types) struct Dwarf_CU *newp = libdw_typed_alloc (dbg, struct Dwarf_CU); newp->dbg = dbg; + newp->sec_idx = sec_idx; newp->start = oldoff; newp->end = *offsetp; newp->address_size = address_size; newp->offset_size = offset_size; newp->version = version; - newp->type_sig8 = type_sig8; - newp->type_offset = type_offset; + newp->unit_id8 = unit_id8; + newp->subdie_offset = subdie_offset; Dwarf_Abbrev_Hash_init (&newp->abbrev_hash, 41); newp->orig_abbrev_offset = newp->last_abbrev_offset = abbrev_offset; + newp->files = NULL; newp->lines = NULL; newp->locs = NULL; - - if (debug_types) - Dwarf_Sig8_Hash_insert (&dbg->sig8_hash, type_sig8, newp); + newp->split = (Dwarf_CU *) -1; + newp->base_address = (Dwarf_Addr) -1; + newp->addr_base = (Dwarf_Off) -1; + newp->str_off_base = (Dwarf_Off) -1; + newp->ranges_base = (Dwarf_Off) -1; + newp->locs_base = (Dwarf_Off) -1; newp->startp = data->d_buf + newp->start; newp->endp = data->d_buf + newp->end; + /* v4 debug type units have version == 4 and unit_type == DW_UT_type. */ + if (debug_types) + newp->unit_type = DW_UT_type; + else if (version < 5) + { + /* This is a reasonable guess (and needed to get the CUDIE). */ + newp->unit_type = DW_UT_compile; + + /* But set it correctly from the actual CUDIE tag. */ + Dwarf_Die cudie = CUDIE (newp); + int tag = INTUSE(dwarf_tag) (&cudie); + if (tag == DW_TAG_compile_unit) + { + Dwarf_Attribute dwo_id; + if (INTUSE(dwarf_attr) (&cudie, DW_AT_GNU_dwo_id, &dwo_id) != NULL) + { + Dwarf_Word id8; + if (INTUSE(dwarf_formudata) (&dwo_id, &id8) == 0) + { + if (INTUSE(dwarf_haschildren) (&cudie) == 0 + && INTUSE(dwarf_hasattr) (&cudie, + DW_AT_GNU_dwo_name) == 1) + newp->unit_type = DW_UT_skeleton; + else + newp->unit_type = DW_UT_split_compile; + + newp->unit_id8 = id8; + } + } + } + else if (tag == DW_TAG_partial_unit) + newp->unit_type = DW_UT_partial; + else if (tag == DW_TAG_type_unit) + newp->unit_type = DW_UT_type; + } + else + newp->unit_type = unit_type; + + /* Store a reference to any type unit ids in the hash for quick lookup. */ + if (unit_type == DW_UT_type || unit_type == DW_UT_split_type) + Dwarf_Sig8_Hash_insert (&dbg->sig8_hash, unit_id8, newp); + /* Add the new entry to the search tree. */ if (tsearch (newp, tree, findcu_cb) == NULL) { @@ -134,11 +226,11 @@ __libdw_intern_next_unit (Dwarf *dbg, bool debug_types) struct Dwarf_CU * internal_function -__libdw_findcu (Dwarf *dbg, Dwarf_Off start, bool debug_types) +__libdw_findcu (Dwarf *dbg, Dwarf_Off start, bool v4_debug_types) { - void **tree = debug_types ? &dbg->tu_tree : &dbg->cu_tree; + void **tree = v4_debug_types ? &dbg->tu_tree : &dbg->cu_tree; Dwarf_Off *next_offset - = debug_types ? &dbg->next_tu_offset : &dbg->next_cu_offset; + = v4_debug_types ? &dbg->next_tu_offset : &dbg->next_cu_offset; /* Maybe we already know that CU. */ struct Dwarf_CU fake = { .start = start, .end = 0 }; @@ -155,14 +247,61 @@ __libdw_findcu (Dwarf *dbg, Dwarf_Off start, bool debug_types) /* No. Then read more CUs. */ while (1) { - struct Dwarf_CU *newp = __libdw_intern_next_unit (dbg, debug_types); + struct Dwarf_CU *newp = __libdw_intern_next_unit (dbg, v4_debug_types); if (newp == NULL) return NULL; /* Is this the one we are looking for? */ - if (start < *next_offset) - // XXX Match exact offset. + if (start < *next_offset || start == newp->start) return newp; } /* NOTREACHED */ } + +struct Dwarf_CU * +internal_function +__libdw_findcu_addr (Dwarf *dbg, void *addr) +{ + void **tree; + Dwarf_Off start; + if (addr >= dbg->sectiondata[IDX_debug_info]->d_buf + && addr < (dbg->sectiondata[IDX_debug_info]->d_buf + + dbg->sectiondata[IDX_debug_info]->d_size)) + { + tree = &dbg->cu_tree; + start = addr - dbg->sectiondata[IDX_debug_info]->d_buf; + } + else if (dbg->sectiondata[IDX_debug_types] != NULL + && addr >= dbg->sectiondata[IDX_debug_types]->d_buf + && addr < (dbg->sectiondata[IDX_debug_types]->d_buf + + dbg->sectiondata[IDX_debug_types]->d_size)) + { + tree = &dbg->tu_tree; + start = addr - dbg->sectiondata[IDX_debug_types]->d_buf; + } + else + return NULL; + + struct Dwarf_CU fake = { .start = start, .end = 0 }; + struct Dwarf_CU **found = tfind (&fake, tree, findcu_cb); + + if (found != NULL) + return *found; + + return NULL; +} + +Dwarf * +internal_function +__libdw_find_split_dbg_addr (Dwarf *dbg, void *addr) +{ + /* XXX Assumes split DWARF only has CUs in main IDX_debug_info. */ + Elf_Data fake_data = { .d_buf = addr, .d_size = 0 }; + Dwarf fake = { .sectiondata[IDX_debug_info] = &fake_data }; + Dwarf **found = tfind (&fake, &dbg->split_tree, __libdw_finddbg_cb); + + if (found != NULL) + return *found; + + return NULL; +} diff --git a/libdw/libdw_form.c b/libdw/libdw_form.c index 72e2390c..584c8467 100644 --- a/libdw/libdw_form.c +++ b/libdw/libdw_form.c @@ -60,6 +60,8 @@ __libdw_form_val_compute_len (struct Dwarf_CU *cu, unsigned int form, break; case DW_FORM_strp: + case DW_FORM_strp_sup: + case DW_FORM_line_strp: case DW_FORM_sec_offset: case DW_FORM_GNU_ref_alt: case DW_FORM_GNU_strp_alt: @@ -103,6 +105,12 @@ __libdw_form_val_compute_len (struct Dwarf_CU *cu, unsigned int form, case DW_FORM_sdata: case DW_FORM_udata: case DW_FORM_ref_udata: + case DW_FORM_addrx: + case DW_FORM_loclistx: + case DW_FORM_rnglistx: + case DW_FORM_strx: + case DW_FORM_GNU_addr_index: + case DW_FORM_GNU_str_index: get_uleb128 (u128, valp, endp); result = valp - startp; break; diff --git a/libdw/memory-access.h b/libdw/memory-access.h index a749b5a9..a39ad6d2 100644 --- a/libdw/memory-access.h +++ b/libdw/memory-access.h @@ -1,7 +1,6 @@ /* Unaligned memory access functionality. - Copyright (C) 2000-2014 Red Hat, Inc. + Copyright (C) 2000-2014, 2018 Red Hat, Inc. This file is part of elfutils. - Written by Ulrich Drepper <[email protected]>, 2001. This file is free software; you can redistribute it and/or modify it under the terms of either @@ -31,6 +30,7 @@ #define _MEMORY_ACCESS_H 1 #include <byteswap.h> +#include <endian.h> #include <limits.h> #include <stdint.h> @@ -87,8 +87,26 @@ __libdw_get_uleb128 (const unsigned char **addrp, const unsigned char *end) return UINT64_MAX; } +static inline uint64_t +__libdw_get_uleb128_unchecked (const unsigned char **addrp) +{ + uint64_t acc = 0; + + /* Unroll the first step to help the compiler optimize + for the common single-byte case. */ + get_uleb128_step (acc, *addrp, 0); + + const size_t max = len_leb128 (uint64_t); + for (size_t i = 1; i < max; ++i) + get_uleb128_step (acc, *addrp, i); + /* Other implementations set VALUE to UINT_MAX in this + case. So we better do this as well. */ + return UINT64_MAX; +} + /* Note, addr needs to me smaller than end. */ #define get_uleb128(var, addr, end) ((var) = __libdw_get_uleb128 (&(addr), end)) +#define get_uleb128_unchecked(var, addr) ((var) = __libdw_get_uleb128_unchecked (&(addr))) /* The signed case is similar, but we sign-extend the result. */ @@ -121,7 +139,26 @@ __libdw_get_sleb128 (const unsigned char **addrp, const unsigned char *end) return INT64_MAX; } +static inline int64_t +__libdw_get_sleb128_unchecked (const unsigned char **addrp) +{ + int64_t acc = 0; + + /* Unroll the first step to help the compiler optimize + for the common single-byte case. */ + get_sleb128_step (acc, *addrp, 0); + + /* Subtract one step, so we don't shift into sign bit. */ + const size_t max = len_leb128 (int64_t) - 1; + for (size_t i = 1; i < max; ++i) + get_sleb128_step (acc, *addrp, i); + /* Other implementations set VALUE to INT_MAX in this + case. So we better do this as well. */ + return INT64_MAX; +} + #define get_sleb128(var, addr, end) ((var) = __libdw_get_sleb128 (&(addr), end)) +#define get_sleb128_unchecked(var, addr) ((var) = __libdw_get_sleb128_unchecked (&(addr))) /* We use simple memory access functions in case the hardware allows it. @@ -170,7 +207,7 @@ union unaligned int16_t s2; int32_t s4; int64_t s8; - } __attribute__ ((packed)); + } attribute_packed; # define read_2ubyte_unaligned(Dbg, Addr) \ read_2ubyte_unaligned_1 ((Dbg)->other_byte_order, (Addr)) @@ -278,6 +315,57 @@ read_8sbyte_unaligned_1 (bool other_byte_order, const void *p) Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 8); \ t_; }) +/* 3ubyte reads are only used for DW_FORM_addrx3 and DW_FORM_strx3. + And are probably very rare. They are not optimized. They are + handled as if reading a 4byte value with the first (for big endian) + or last (for little endian) byte zero. */ + +static inline int +file_byte_order (bool other_byte_order) +{ +#if __BYTE_ORDER == __LITTLE_ENDIAN + return other_byte_order ? __BIG_ENDIAN : __LITTLE_ENDIAN; +#else + return other_byte_order ? __LITTLE_ENDIAN : __BIG_ENDIAN; +#endif +} + +static inline uint32_t +read_3ubyte_unaligned (Dwarf *dbg, const unsigned char *p) +{ + union + { + uint32_t u4; + unsigned char c[4]; + } d; + bool other_byte_order = dbg->other_byte_order; + + if (file_byte_order (other_byte_order) == __BIG_ENDIAN) + { + d.c[0] = 0x00; + d.c[1] = p[0]; + d.c[2] = p[1]; + d.c[3] = p[2]; + } + else + { + d.c[0] = p[0]; + d.c[1] = p[1]; + d.c[2] = p[2]; + d.c[3] = 0x00; + } + + if (other_byte_order) + return bswap_32 (d.u4); + else + return d.u4; +} + + +#define read_3ubyte_unaligned_inc(Dbg, Addr) \ + ({ uint32_t t_ = read_2ubyte_unaligned (Dbg, Addr); \ + Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 3); \ + t_; }) #define read_addr_unaligned_inc(Nbytes, Dbg, Addr) \ (assert ((Nbytes) == 4 || (Nbytes) == 8), \ |
