/* Copyright (C) 2021-2024 Free Software Foundation, Inc. Contributed by Oracle. This file is part of GNU Binutils. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. This program 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 a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config.h" #include #include "util.h" #include "Dwarf.h" #include "DwarfLib.h" #include "Elf.h" #include "Function.h" #include "Module.h" #include "StringBuilder.h" #include "DbeArray.h" #include "DbeSession.h" #define NO_STMT_LIST ((uint64_t) -1) #define CASE_S(x) case x: s = (char *) #x; break static char * gelf_st_type2str (int type) { static char buf[128]; char *s; switch (type) { CASE_S (STT_NOTYPE); CASE_S (STT_OBJECT); CASE_S (STT_FUNC); CASE_S (STT_SECTION); CASE_S (STT_FILE); CASE_S (STT_COMMON); CASE_S (STT_TLS); // CASE_S(STT_NUM); CASE_S (STT_LOPROC); CASE_S (STT_HIPROC); default: s = NTXT ("???"); break; } snprintf (buf, sizeof (buf), NTXT ("%s(%d)"), s, type); buf[sizeof (buf) - 1] = 0; return buf; } static char * special_opcode2str (int opcode) { static char buf[128]; snprintf (buf, sizeof (buf), NTXT ("SpecialOpcode: %3d"), opcode); buf[sizeof (buf) - 1] = 0; return buf; } static char * extended_opcode2str (int opcode) { static char buf[128]; char *s; switch (opcode) { CASE_S (DW_LNE_end_sequence); CASE_S (DW_LNE_set_address); CASE_S (DW_LNE_define_file); default: snprintf (buf, sizeof (buf), NTXT ("??? (%d)"), opcode); buf[sizeof (buf) - 1] = 0; s = buf; break; } return s; } static char * standard_opcode2str (int opcode) { static char buf[128]; char *s; switch (opcode) { CASE_S (DW_LNS_copy); CASE_S (DW_LNS_advance_pc); CASE_S (DW_LNS_advance_line); CASE_S (DW_LNS_set_file); CASE_S (DW_LNS_set_column); CASE_S (DW_LNS_negate_stmt); CASE_S (DW_LNS_set_basic_block); CASE_S (DW_LNS_const_add_pc); CASE_S (DW_LNS_fixed_advance_pc); default: snprintf (buf, sizeof (buf), NTXT ("??? (%d)"), opcode); buf[sizeof (buf) - 1] = 0; s = buf; break; } return s; } template<> void Vector ::dump (const char *msg) { Dprintf (1, NTXT ("%s Vector [%lld]\n"), msg ? msg : NTXT (""), (long long) size ()); for (long i = 0, sz = size (); i < sz; i++) { DwrInlinedSubr *p = get (i); Dprintf (1, NTXT ("%ld: "), (long) i); p->dump (); } } template<> void Vector ::dump (const char *msg) { Dprintf (1, "%s Vector [%lld]:\n address [file line column]\n", msg ? msg : NTXT (""), (long long) size ()); for (long i = 0, sz = size (); i < sz; i++) { DwrLine *lnp = get (i); Dprintf (1, NTXT (" %2lld 0x%08llx [ %2lld, %lld, %lld ] \n"), (long long) i, (long long) lnp->address, (long long) lnp->file, (long long) lnp->line, (long long) lnp->column); } Dprintf (1, NTXT ("\n\n")); } template<> void Vector ::dump (const char *msg) { Dprintf (1, "\n%s Vector [%lld]: [dir_ind tstamp fsize]\n", msg ? msg : NTXT (""), (long long) size ()); for (long i = 0, sz = size (); i < sz; i++) { DwrFileName *fnp = get (i); Dprintf (1, " %2ld %3lld %8lld %8lld %s\n", i, (long long) fnp->dir_index, (long long) fnp->timestamp, (long long) fnp->file_size, STR (fnp->fname)); } Dprintf (1, "\n"); } static char * get_string (DwrSec *sec, uint64_t off) { if (sec) { sec->offset = off; return sec->GetString (); } return NULL; } ////////////////////////////////////////////////////////// // class ElfReloc ElfReloc::ElfReloc (Elf *_elf) { elf = _elf; reloc = NULL; cur_reloc_ind = 0; } ElfReloc::~ElfReloc () { if (reloc) { reloc->destroy (); delete reloc; } } void ElfReloc::dump_rela_debug_sec (int sec) { if (!DUMP_RELA_SEC) return; Elf_Internal_Shdr *shdr = elf->get_shdr (sec); if (shdr == NULL) return; Elf_Data *data = elf->elf_getdata (sec); if (data == NULL) return; uint64_t ScnSize = data->d_size; uint64_t EntSize = shdr->sh_entsize; if (ScnSize == 0 || EntSize == 0) return; Elf_Internal_Shdr *shdr_sym = elf->get_shdr (shdr->sh_link); if (shdr_sym == NULL) return; Elf_Data *data_sym = elf->elf_getdata (shdr->sh_link); Elf_Data *data_str = elf->elf_getdata (shdr_sym->sh_link); char *Strtab = data_str ? (char*) data_str->d_buf : NULL; Elf_Internal_Rela rela; int n, cnt = (int) (ScnSize / EntSize); char *sec_name = elf->get_sec_name (sec); if (sec_name == NULL) // It can not be, but let's check return; Dprintf (DUMP_RELA_SEC, "======= DwarfLib::dump_rela_debug_sec Section:%2d '%s'\n", sec, sec_name); Dprintf (DUMP_RELA_SEC, " N |addend| offset | r_info | stt_type |\n"); for (n = 0; n < cnt; n++) { if (strncmp (sec_name, NTXT (".rela."), 6) == 0) elf->elf_getrela (data, n, &rela); else { elf->elf_getrel (data, n, &rela); rela.r_addend = 0; } int ndx = (int) GELF_R_SYM (rela.r_info); Elf_Internal_Shdr *secHdr; Elf_Internal_Sym sym; elf->elf_getsym (data_sym, ndx, &sym); Dprintf (DUMP_RELA_SEC, NTXT ("%3d:%5d |%11lld |0x%016llx | %-15s|"), n, (int) rela.r_addend, (long long) rela.r_offset, (long long) rela.r_info, gelf_st_type2str ((int) GELF_ST_TYPE (sym.st_info))); switch (GELF_ST_TYPE (sym.st_info)) { case STT_FUNC: case STT_OBJECT: case STT_NOTYPE: secHdr = elf->get_shdr (sym.st_shndx); if (secHdr) Dprintf (DUMP_RELA_SEC, NTXT (" img_offset=0x%llx"), (long long) (sym.st_value + secHdr->sh_offset)); if (Strtab && sym.st_name) Dprintf (DUMP_RELA_SEC, NTXT (" %s"), Strtab + sym.st_name); break; case STT_SECTION: secHdr = elf->get_shdr (sym.st_shndx); if (secHdr) { Dprintf (DUMP_RELA_SEC, NTXT (" value=0x%016llx (%lld)"), (long long) (secHdr->sh_offset + rela.r_addend), (long long) (secHdr->sh_offset + rela.r_addend)); } break; default: break; } Dprintf (DUMP_RELA_SEC, NTXT ("\n")); } Dprintf (DUMP_RELA_SEC, NTXT ("\n")); } void ElfReloc::dump () { if (!DUMP_ELF_RELOC || (reloc == NULL) || (reloc->size () == 0)) return; Dprintf (DUMP_ELF_RELOC, NTXT ("======= ElfReloc::dump\n")); Dprintf (DUMP_ELF_RELOC, NTXT (" N | offset | value | STT_TYPE\n")); for (int i = 0; i < reloc->size (); i++) { Sreloc *srlc = reloc->fetch (i); Dprintf (DUMP_ELF_RELOC, NTXT ("%3d:%11lld |%11lld | %s\n"), i, (long long) srlc->offset, (long long) srlc->value, gelf_st_type2str (srlc->stt_type)); } Dprintf (DUMP_ELF_RELOC, NTXT ("\n")); } static int DwrRelocOffsetCmp (const void *a, const void *b) { ElfReloc::Sreloc *item1 = *((ElfReloc::Sreloc **) a); ElfReloc::Sreloc *item2 = *((ElfReloc::Sreloc **) b); return item1->offset < item2->offset ? -1 : item1->offset == item2->offset ? 0 : 1; } ElfReloc * ElfReloc::get_elf_reloc (Elf *elfp, char *sec_name, ElfReloc *rlc) { int et = elfp->elf_getehdr ()->e_type; if (et == ET_EXEC || et == ET_DYN) return rlc; int sec = elfp->elf_get_sec_num (sec_name); if (sec == 0) return rlc; Elf_Internal_Shdr *shdr = elfp->get_shdr (sec); if (shdr == NULL || shdr->sh_entsize == 0) return rlc; Elf_Data *data = elfp->elf_getdata (sec); if (data == NULL || data->d_size == 0) return rlc; int cnt = (int) (data->d_size / shdr->sh_entsize); Elf_Internal_Shdr *shdr_sym = elfp->get_shdr (shdr->sh_link); if (shdr_sym == NULL) return rlc; Elf_Data *data_sym = elfp->elf_getdata (shdr->sh_link); Vector *vp = NULL; for (int n = 0; n < cnt; n++) { Elf_Internal_Shdr *secHdr; Sreloc *srlc; Elf_Internal_Rela rela; if (strncmp (sec_name, NTXT (".rela."), 6) == 0) elfp->elf_getrela (data, n, &rela); else { elfp->elf_getrel (data, n, &rela); rela.r_addend = 0; } int ndx = (int) GELF_R_SYM (rela.r_info); Elf_Internal_Sym sym; elfp->elf_getsym (data_sym, ndx, &sym); srlc = new Sreloc; srlc->offset = rela.r_offset; srlc->value = 0; srlc->stt_type = (int) GELF_ST_TYPE (sym.st_info); switch (GELF_ST_TYPE (sym.st_info)) { case STT_FUNC: secHdr = elfp->get_shdr (sym.st_shndx); if (secHdr) srlc->value = secHdr->sh_offset + sym.st_value; break; case STT_OBJECT: case STT_NOTYPE: secHdr = elfp->get_shdr (shdr->sh_info); if (secHdr) { srlc->offset = rela.r_info; srlc->value = secHdr->sh_offset + rela.r_addend; } break; case STT_SECTION: secHdr = elfp->get_shdr (sym.st_shndx); if (secHdr) srlc->value = rela.r_addend; break; default: srlc->value = 0; break; } if (rlc == NULL) { rlc = new ElfReloc (elfp); vp = rlc->reloc; } if (vp == NULL) { vp = new Vector; rlc->reloc = vp; } vp->append (srlc); } if (vp) vp->sort (DwrRelocOffsetCmp); if (rlc) { rlc->dump_rela_debug_sec (sec); rlc->dump (); } return rlc; } long long ElfReloc::get_reloc_addr (long long offset) { Sreloc *srlc; int i = cur_reloc_ind - 1; if (i >= 0 && i < reloc->size ()) { srlc = reloc->fetch (i); if (srlc->offset > offset) // need to reset cur_reloc_ind = 0; } for (; cur_reloc_ind < reloc->size (); cur_reloc_ind++) { srlc = reloc->fetch (cur_reloc_ind); if (srlc->offset == offset) return srlc->value; if (srlc->offset > offset) return 0; } return 0; } DwrLocation * DwrCU::dwr_get_location (DwrSec *secp, DwrLocation *lp) { lp->offset = secp->offset; lp->lc_number = 0; lp->lc_number2 = 0; lp->op = secp->Get_8 (); switch (lp->op) { // registers case DW_OP_reg0: case DW_OP_reg1: case DW_OP_reg2: case DW_OP_reg3: case DW_OP_reg4: case DW_OP_reg5: case DW_OP_reg6: case DW_OP_reg7: case DW_OP_reg8: case DW_OP_reg9: case DW_OP_reg10: case DW_OP_reg11: case DW_OP_reg12: case DW_OP_reg13: case DW_OP_reg14: case DW_OP_reg15: case DW_OP_reg16: case DW_OP_reg17: case DW_OP_reg18: case DW_OP_reg19: case DW_OP_reg20: case DW_OP_reg21: case DW_OP_reg22: case DW_OP_reg23: case DW_OP_reg24: case DW_OP_reg25: case DW_OP_reg26: case DW_OP_reg27: case DW_OP_reg28: case DW_OP_reg29: case DW_OP_reg30: case DW_OP_reg31: break; case DW_OP_regx: lp->lc_number = secp->GetULEB128 (); break; case DW_OP_breg0: case DW_OP_breg1: case DW_OP_breg2: case DW_OP_breg3: case DW_OP_breg4: case DW_OP_breg5: case DW_OP_breg6: case DW_OP_breg7: case DW_OP_breg8: case DW_OP_breg9: case DW_OP_breg10: case DW_OP_breg11: case DW_OP_breg12: case DW_OP_breg13: case DW_OP_breg14: case DW_OP_breg15: case DW_OP_breg16: case DW_OP_breg17: case DW_OP_breg18: case DW_OP_breg19: case DW_OP_breg20: case DW_OP_breg21: case DW_OP_breg22: case DW_OP_breg23: case DW_OP_breg24: case DW_OP_breg25: case DW_OP_breg26: case DW_OP_breg27: case DW_OP_breg28: case DW_OP_breg29: case DW_OP_breg30: case DW_OP_breg31: lp->lc_number = secp->GetSLEB128 (); break; case DW_OP_fbreg: lp->lc_number = secp->GetSLEB128 (); break; case DW_OP_bregx: lp->lc_number = secp->GetULEB128 (); lp->lc_number2 = secp->GetSLEB128 (); break; case DW_OP_lit0: case DW_OP_lit1: case DW_OP_lit2: case DW_OP_lit3: case DW_OP_lit4: case DW_OP_lit5: case DW_OP_lit6: case DW_OP_lit7: case DW_OP_lit8: case DW_OP_lit9: case DW_OP_lit10: case DW_OP_lit11: case DW_OP_lit12: case DW_OP_lit13: case DW_OP_lit14: case DW_OP_lit15: case DW_OP_lit16: case DW_OP_lit17: case DW_OP_lit18: case DW_OP_lit19: case DW_OP_lit20: case DW_OP_lit21: case DW_OP_lit22: case DW_OP_lit23: case DW_OP_lit24: case DW_OP_lit25: case DW_OP_lit26: case DW_OP_lit27: case DW_OP_lit28: case DW_OP_lit29: case DW_OP_lit30: case DW_OP_lit31: lp->lc_number = lp->op - DW_OP_lit0; break; case DW_OP_addr: lp->lc_number = secp->GetADDR (); break; case DW_OP_const1u: lp->lc_number = secp->Get_8 (); break; case DW_OP_const1s: { signed char x; x = secp->Get_8 (); lp->lc_number = x; } break; case DW_OP_const2u: lp->lc_number = secp->Get_16 (); break; case DW_OP_const2s: { signed short x; x = secp->Get_16 (); lp->lc_number = x; } break; case DW_OP_const4u: lp->lc_number = secp->Get_32 (); break; case DW_OP_const4s: { signed int x; x = secp->Get_32 (); lp->lc_number = x; } break; case DW_OP_const8u: lp->lc_number = secp->Get_64 (); break; case DW_OP_const8s: { signed long long x; x = secp->Get_64 (); lp->lc_number = x; } break; case DW_OP_plus_uconst: case DW_OP_constu: lp->lc_number = secp->GetULEB128 (); break; case DW_OP_consts: lp->lc_number = secp->GetSLEB128 (); break; // Stack operations case DW_OP_pick: case DW_OP_deref_size: case DW_OP_xderef_size: lp->lc_number = secp->Get_8 (); break; case DW_OP_dup: case DW_OP_drop: case DW_OP_over: case DW_OP_swap: case DW_OP_rot: case DW_OP_deref: case DW_OP_xderef: // Arithmetic and Logical Operations case DW_OP_abs: case DW_OP_and: case DW_OP_div: case DW_OP_minus: case DW_OP_mod: case DW_OP_mul: case DW_OP_neg: case DW_OP_not: case DW_OP_or: case DW_OP_plus: case DW_OP_shl: case DW_OP_shr: case DW_OP_shra: case DW_OP_xor: case DW_OP_le: case DW_OP_ge: case DW_OP_eq: case DW_OP_lt: case DW_OP_gt: case DW_OP_ne: case DW_OP_nop: break; case DW_OP_skip: case DW_OP_bra: lp->lc_number = secp->Get_16 (); break; case DW_OP_piece: lp->lc_number = secp->GetULEB128 (); break; case DW_OP_push_object_address: /* DWARF3 */ break; case DW_OP_call2: /* DWARF3 */ lp->lc_number = secp->Get_16 (); break; case DW_OP_call4: /* DWARF3 */ lp->lc_number = secp->Get_32 (); break; case DW_OP_call_ref: /* DWARF3 */ lp->lc_number = secp->GetADDR (); break; default: return (NULL); } return lp; } char * DwrCU::tag2str (int tag) { static char buf[128]; char *s; switch (tag) { CASE_S (DW_TAG_array_type); CASE_S (DW_TAG_class_type); CASE_S (DW_TAG_entry_point); CASE_S (DW_TAG_enumeration_type); CASE_S (DW_TAG_formal_parameter); CASE_S (DW_TAG_imported_declaration); CASE_S (DW_TAG_label); CASE_S (DW_TAG_lexical_block); CASE_S (DW_TAG_member); CASE_S (DW_TAG_pointer_type); CASE_S (DW_TAG_reference_type); CASE_S (DW_TAG_compile_unit); CASE_S (DW_TAG_string_type); CASE_S (DW_TAG_structure_type); CASE_S (DW_TAG_subroutine_type); CASE_S (DW_TAG_typedef); CASE_S (DW_TAG_union_type); CASE_S (DW_TAG_unspecified_parameters); CASE_S (DW_TAG_variant); CASE_S (DW_TAG_common_block); CASE_S (DW_TAG_common_inclusion); CASE_S (DW_TAG_inheritance); CASE_S (DW_TAG_inlined_subroutine); CASE_S (DW_TAG_module); CASE_S (DW_TAG_ptr_to_member_type); CASE_S (DW_TAG_set_type); CASE_S (DW_TAG_subrange_type); CASE_S (DW_TAG_with_stmt); CASE_S (DW_TAG_access_declaration); CASE_S (DW_TAG_base_type); CASE_S (DW_TAG_catch_block); CASE_S (DW_TAG_const_type); CASE_S (DW_TAG_constant); CASE_S (DW_TAG_enumerator); CASE_S (DW_TAG_file_type); CASE_S (DW_TAG_friend); CASE_S (DW_TAG_namelist); CASE_S (DW_TAG_namelist_item); CASE_S (DW_TAG_packed_type); CASE_S (DW_TAG_subprogram); CASE_S (DW_TAG_template_type_param); CASE_S (DW_TAG_template_value_param); CASE_S (DW_TAG_thrown_type); CASE_S (DW_TAG_try_block); CASE_S (DW_TAG_variant_part); CASE_S (DW_TAG_variable); CASE_S (DW_TAG_volatile_type); CASE_S (DW_TAG_dwarf_procedure); CASE_S (DW_TAG_restrict_type); CASE_S (DW_TAG_interface_type); CASE_S (DW_TAG_namespace); CASE_S (DW_TAG_imported_module); CASE_S (DW_TAG_unspecified_type); CASE_S (DW_TAG_partial_unit); CASE_S (DW_TAG_imported_unit); CASE_S (DW_TAG_lo_user); CASE_S (DW_TAG_MIPS_loop); CASE_S (DW_TAG_format_label); CASE_S (DW_TAG_function_template); CASE_S (DW_TAG_class_template); CASE_S (DW_TAG_GNU_BINCL); CASE_S (DW_TAG_GNU_EINCL); CASE_S (DW_TAG_GNU_call_site); CASE_S (DW_TAG_GNU_call_site_parameter); CASE_S (DW_TAG_SUN_codeflags); CASE_S (DW_TAG_SUN_memop_info); CASE_S (DW_TAG_hi_user); CASE_S (DW_TAG_icc_compile_unit); CASE_S (DW_TAG_rvalue_reference_type); CASE_S (DW_TAG_coarray_type); CASE_S (DW_TAG_generic_subrange); CASE_S (DW_TAG_dynamic_type); CASE_S (DW_TAG_atomic_type); CASE_S (DW_TAG_call_site); CASE_S (DW_TAG_call_site_parameter); CASE_S (DW_TAG_skeleton_unit); CASE_S (DW_TAG_immutable_type); CASE_S (0); default: s = NTXT ("???"); break; } snprintf (buf, sizeof (buf), NTXT ("%s(%d)"), s, tag); buf[sizeof (buf) - 1] = 0; return buf; } char * DwrCU::at2str (int tag) { static char buf[128]; char *s; switch (tag) { CASE_S (DW_AT_sibling); CASE_S (DW_AT_location); CASE_S (DW_AT_name); CASE_S (DW_AT_ordering); CASE_S (DW_AT_subscr_data); CASE_S (DW_AT_byte_size); CASE_S (DW_AT_bit_offset); CASE_S (DW_AT_bit_size); CASE_S (DW_AT_element_list); CASE_S (DW_AT_stmt_list); CASE_S (DW_AT_low_pc); CASE_S (DW_AT_high_pc); CASE_S (DW_AT_language); CASE_S (DW_AT_member); CASE_S (DW_AT_discr); CASE_S (DW_AT_discr_value); CASE_S (DW_AT_visibility); CASE_S (DW_AT_import); CASE_S (DW_AT_string_length); CASE_S (DW_AT_common_reference); CASE_S (DW_AT_comp_dir); CASE_S (DW_AT_const_value); CASE_S (DW_AT_containing_type); CASE_S (DW_AT_default_value); CASE_S (DW_AT_inline); CASE_S (DW_AT_is_optional); CASE_S (DW_AT_lower_bound); CASE_S (DW_AT_producer); CASE_S (DW_AT_prototyped); CASE_S (DW_AT_return_addr); CASE_S (DW_AT_start_scope); CASE_S (DW_AT_stride_size); CASE_S (DW_AT_upper_bound); CASE_S (DW_AT_abstract_origin); CASE_S (DW_AT_accessibility); CASE_S (DW_AT_address_class); CASE_S (DW_AT_artificial); CASE_S (DW_AT_base_types); CASE_S (DW_AT_calling_convention); CASE_S (DW_AT_count); CASE_S (DW_AT_data_member_location); CASE_S (DW_AT_decl_column); CASE_S (DW_AT_decl_file); CASE_S (DW_AT_decl_line); CASE_S (DW_AT_declaration); CASE_S (DW_AT_discr_list); CASE_S (DW_AT_encoding); CASE_S (DW_AT_external); CASE_S (DW_AT_frame_base); CASE_S (DW_AT_friend); CASE_S (DW_AT_identifier_case); CASE_S (DW_AT_macro_info); CASE_S (DW_AT_namelist_item); CASE_S (DW_AT_priority); CASE_S (DW_AT_segment); CASE_S (DW_AT_specification); CASE_S (DW_AT_static_link); CASE_S (DW_AT_type); CASE_S (DW_AT_use_location); CASE_S (DW_AT_variable_parameter); CASE_S (DW_AT_virtuality); CASE_S (DW_AT_vtable_elem_location); CASE_S (DW_AT_allocated); CASE_S (DW_AT_associated); CASE_S (DW_AT_data_location); CASE_S (DW_AT_byte_stride); CASE_S (DW_AT_entry_pc); CASE_S (DW_AT_use_UTF8); CASE_S (DW_AT_extension); CASE_S (DW_AT_ranges); CASE_S (DW_AT_trampoline); CASE_S (DW_AT_call_column); CASE_S (DW_AT_call_file); CASE_S (DW_AT_call_line); CASE_S (DW_AT_description); CASE_S (DW_AT_binary_scale); CASE_S (DW_AT_decimal_scale); CASE_S (DW_AT_small); CASE_S (DW_AT_decimal_sign); CASE_S (DW_AT_digit_count); CASE_S (DW_AT_picture_string); CASE_S (DW_AT_mutable); CASE_S (DW_AT_threads_scaled); CASE_S (DW_AT_explicit); CASE_S (DW_AT_object_pointer); CASE_S (DW_AT_endianity); CASE_S (DW_AT_elemental); CASE_S (DW_AT_pure); CASE_S (DW_AT_recursive); CASE_S (DW_AT_signature); CASE_S (DW_AT_main_subprogram); CASE_S (DW_AT_data_bit_offset); CASE_S (DW_AT_const_expr); CASE_S (DW_AT_enum_class); CASE_S (DW_AT_linkage_name); CASE_S (DW_AT_lo_user); CASE_S (DW_AT_MIPS_fde); CASE_S (DW_AT_MIPS_loop_begin); CASE_S (DW_AT_MIPS_tail_loop_begin); CASE_S (DW_AT_MIPS_epilog_begin); CASE_S (DW_AT_MIPS_loop_unroll_factor); CASE_S (DW_AT_MIPS_software_pipeline_depth); CASE_S (DW_AT_MIPS_linkage_name); CASE_S (DW_AT_MIPS_stride); CASE_S (DW_AT_MIPS_abstract_name); CASE_S (DW_AT_MIPS_clone_origin); CASE_S (DW_AT_MIPS_has_inlines); CASE_S (DW_AT_sf_names); CASE_S (DW_AT_src_info); CASE_S (DW_AT_mac_info); CASE_S (DW_AT_src_coords); CASE_S (DW_AT_body_begin); CASE_S (DW_AT_body_end); CASE_S (DW_AT_GNU_vector); CASE_S (DW_AT_GNU_guarded_by); CASE_S (DW_AT_GNU_pt_guarded_by); CASE_S (DW_AT_GNU_guarded); CASE_S (DW_AT_GNU_pt_guarded); CASE_S (DW_AT_GNU_locks_excluded); CASE_S (DW_AT_GNU_exclusive_locks_required); CASE_S (DW_AT_GNU_shared_locks_required); CASE_S (DW_AT_GNU_odr_signature); CASE_S (DW_AT_GNU_template_name); CASE_S (DW_AT_GNU_call_site_value); CASE_S (DW_AT_GNU_call_site_data_value); CASE_S (DW_AT_GNU_call_site_target); CASE_S (DW_AT_GNU_call_site_target_clobbered); CASE_S (DW_AT_GNU_tail_call); CASE_S (DW_AT_GNU_all_tail_call_sites); CASE_S (DW_AT_GNU_all_call_sites); CASE_S (DW_AT_GNU_all_source_call_sites); CASE_S (DW_AT_GNU_locviews); CASE_S (DW_AT_GNU_entry_view); CASE_S (DW_AT_SUN_command_line); CASE_S (DW_AT_SUN_func_offsets); CASE_S (DW_AT_SUN_cf_kind); CASE_S (DW_AT_SUN_func_offset); CASE_S (DW_AT_SUN_memop_type_ref); CASE_S (DW_AT_SUN_profile_id); CASE_S (DW_AT_SUN_memop_signature); CASE_S (DW_AT_SUN_obj_dir); CASE_S (DW_AT_SUN_obj_file); CASE_S (DW_AT_SUN_original_name); CASE_S (DW_AT_SUN_link_name); CASE_S (DW_AT_hi_user); CASE_S (DW_AT_icc_flags); CASE_S (DW_AT_string_length_bit_size); CASE_S (DW_AT_string_length_byte_size); CASE_S (DW_AT_rank); CASE_S (DW_AT_str_offsets_base); CASE_S (DW_AT_addr_base); CASE_S (DW_AT_rnglists_base); CASE_S (DW_AT_dwo_name); CASE_S (DW_AT_reference); CASE_S (DW_AT_rvalue_reference); CASE_S (DW_AT_macros); CASE_S (DW_AT_call_all_calls); CASE_S (DW_AT_call_all_source_calls); CASE_S (DW_AT_call_all_tail_calls); CASE_S (DW_AT_call_return_pc); CASE_S (DW_AT_call_value); CASE_S (DW_AT_call_origin); CASE_S (DW_AT_call_parameter); CASE_S (DW_AT_call_pc); CASE_S (DW_AT_call_tail_call); CASE_S (DW_AT_call_target); CASE_S (DW_AT_call_target_clobbered); CASE_S (DW_AT_call_data_location); CASE_S (DW_AT_call_data_value); CASE_S (DW_AT_noreturn); CASE_S (DW_AT_alignment); CASE_S (DW_AT_export_symbols); CASE_S (DW_AT_deleted); CASE_S (DW_AT_defaulted); CASE_S (DW_AT_loclists_base); default: s = NTXT ("???"); break; } snprintf (buf, sizeof (buf), NTXT ("%s(%d)"), s, tag); buf[sizeof (buf) - 1] = 0; return buf; } char * DwrCU::form2str (int tag) { static char buf[128]; char *s; switch (tag) { CASE_S (DW_FORM_addr); CASE_S (DW_FORM_block2); CASE_S (DW_FORM_block4); CASE_S (DW_FORM_data2); CASE_S (DW_FORM_data4); CASE_S (DW_FORM_data8); CASE_S (DW_FORM_data16); CASE_S (DW_FORM_line_strp); CASE_S (DW_FORM_implicit_const); CASE_S (DW_FORM_string); CASE_S (DW_FORM_block); CASE_S (DW_FORM_block1); CASE_S (DW_FORM_data1); CASE_S (DW_FORM_flag); CASE_S (DW_FORM_sdata); CASE_S (DW_FORM_strp); CASE_S (DW_FORM_udata); CASE_S (DW_FORM_ref_addr); CASE_S (DW_FORM_ref1); CASE_S (DW_FORM_ref2); CASE_S (DW_FORM_ref4); CASE_S (DW_FORM_ref8); CASE_S (DW_FORM_ref_udata); CASE_S (DW_FORM_indirect); CASE_S (DW_FORM_sec_offset); CASE_S (DW_FORM_exprloc); CASE_S (DW_FORM_flag_present); CASE_S (DW_FORM_ref_sig8); default: s = NTXT ("???"); break; } snprintf (buf, sizeof (buf), NTXT ("%s(%d)"), s, tag); buf[sizeof (buf) - 1] = 0; return buf; } char * DwrCU::lnct2str (int ty) { static char buf[128]; char *s; switch (ty) { CASE_S (DW_LNCT_path); CASE_S (DW_LNCT_directory_index); CASE_S (DW_LNCT_timestamp); CASE_S (DW_LNCT_size); CASE_S (DW_LNCT_MD5); CASE_S (DW_LNCT_lo_user); CASE_S (DW_LNCT_hi_user); default: s = NTXT ("???"); break; } snprintf (buf, sizeof (buf), NTXT ("%s(%d)"), s, ty); buf[sizeof (buf) - 1] = 0; return buf; } void Dwr_Tag::dump () { Dprintf (DUMP_DWARFLIB, "\n<%2d>:<0x%08llx> %-30s offset=0x%llx %s\n", (int) level, (long long) die, DwrCU::tag2str (tag), (long long) num, (long long) offset, hasChild ? NTXT ("DW_children_yes") : NTXT ("DW_children_no")); for (int i1 = firstAttribute; i1 < lastAttribute; i1++) { Dwr_Attr *atrp = abbrevAtForm->get (i1); Dprintf (DUMP_DWARFLIB, " %-30s ", DwrCU::at2str (atrp->at_name)); switch (atrp->at_form) { case DW_FORM_strp: case DW_FORM_string: case DW_FORM_line_strp: case DW_FORM_strp_sup: case DW_FORM_implicit_const: Dprintf (DUMP_DWARFLIB, " \"%s\"", atrp->u.str ? atrp->u.str : ""); break; case DW_FORM_block: case DW_FORM_block1: case DW_FORM_block2: case DW_FORM_block4: case DW_FORM_data16: Dprintf (DUMP_DWARFLIB, " len=%3ld %p", (long) atrp->len, atrp->u.str); break; case DW_FORM_addr: case DW_FORM_data2: case DW_FORM_data4: case DW_FORM_data8: case DW_FORM_data1: case DW_FORM_flag: case DW_FORM_sdata: case DW_FORM_udata: case DW_FORM_ref_addr: case DW_FORM_ref1: case DW_FORM_ref2: case DW_FORM_ref4: case DW_FORM_ref8: case DW_FORM_ref_udata: case DW_FORM_indirect: case DW_FORM_sec_offset: case DW_FORM_exprloc: case DW_FORM_ref_sig8: case DW_FORM_flag_present: Dprintf (DUMP_DWARFLIB, " 0x%llx (%lld)", (long long) atrp->u.val, (long long) atrp->u.val); break; default: DEBUG_CODE { Dprintf (1, "Attribute form 0x%llx (%lld) is not implemented\n", (long long) atrp->at_form, (long long) atrp->at_form); assert (false); } } Dprintf (DUMP_DWARFLIB, NTXT ("\n")); } } ////////////////////////////////////////////////////////// // class DwrSec DwrSec::DwrSec (unsigned char *_data, uint64_t _size, bool _need_swap_endian, bool _addr32) { isCopy = false; data = _data; sizeSec = _size; size = (data ? _size : 0); offset = 0; fmt64 = false; reloc = NULL; need_swap_endian = _need_swap_endian; addr32 = _addr32; } DwrSec::DwrSec (DwrSec *secp, uint64_t _offset) { isCopy = true; data = secp->data; sizeSec = secp->sizeSec; size = secp->size; offset = _offset; fmt64 = secp->fmt64; reloc = secp->reloc; need_swap_endian = secp->need_swap_endian; addr32 = secp->addr32; } DwrSec::~DwrSec () { if (!isCopy) delete reloc; } bool DwrSec::bounds_violation (uint64_t sz) { if (offset + sz > size) { Dprintf (DEBUG_ERR_MSG, "DwrSec::bounds_violation: offset=%lld + sz=%lld > size=%lld\n", (long long) offset, (long long) sz, (long long) size); return true; } return false; } uint64_t DwrSec::ReadLength () { fmt64 = false; uint64_t val = Get_32 (); if (((uint32_t) val) == 0xffffffff) { fmt64 = true; val = Get_64 (); } size = (val + offset < sizeSec) ? val + offset : sizeSec; return size; } unsigned char DwrSec::Get_8 () { unsigned char n = 0; if (bounds_violation (sizeof (char))) return n; n = data[offset]; offset += sizeof (char); return n; } unsigned short DwrSec::Get_16 () { unsigned short n = 0; if (bounds_violation (sizeof (short))) return n; memcpy ((char *) &n, data + offset, sizeof (short)); offset += sizeof (short); if (need_swap_endian) SWAP_ENDIAN (n); return n; } uint32_t DwrSec::Get_24 () { uint32_t n = 0; if (bounds_violation (3)) return n; memcpy ((char *) &n, data + offset, 3); offset += 3; if (need_swap_endian) SWAP_ENDIAN (n); return n; } uint32_t DwrSec::Get_32 () { uint32_t n = 0; if (bounds_violation (sizeof (uint32_t))) return n; memcpy ((char *) &n, data + offset, sizeof (uint32_t)); offset += sizeof (uint32_t); if (need_swap_endian) SWAP_ENDIAN (n); return n; } uint64_t DwrSec::Get_64 () { uint64_t n = 0; if (bounds_violation (sizeof (uint64_t))) return n; memcpy ((char *) &n, data + offset, sizeof (uint64_t)); offset += sizeof (uint64_t); if (need_swap_endian) SWAP_ENDIAN (n); return n; } char * DwrSec::GetData (uint64_t len) { char *s = ((char *) data) + offset; if (bounds_violation (len)) s = NULL; offset += len; return s; } char * DwrSec::GetString () { uint64_t off = offset; while (offset < size) if (data[offset++] == 0) { // '\0' is inside section if (off + 1 == offset) return NULL; return ((char *) data) + off; } return NULL; // The section is not '\0' terminated } uint64_t DwrSec::GetLong () { if (fmt64) return Get_64 (); return Get_32 (); } uint64_t DwrSec::GetADDR_32 () { uint64_t res = reloc ? reloc->get_reloc_addr (offset) : 0; res += Get_32 (); return res; } uint64_t DwrSec::GetADDR_64 () { uint64_t res = reloc ? reloc->get_reloc_addr (offset) : 0; res += Get_64 (); return res; } uint64_t DwrSec::GetADDR () { if (addr32) return GetADDR_32 (); return GetADDR_64 (); } uint64_t DwrSec::GetRef () { if (fmt64) return GetADDR_64 (); return GetADDR_32 (); } ULEB128 DwrSec::GetULEB128 () { ULEB128 res = 0; for (int shift = 0;; shift += 7) { ULEB128 val = Get_8 (); res |= (val & 0x7f) << shift; if ((val & 0x80) == 0) break; } return res; } SLEB128 DwrSec::GetSLEB128 () { ULEB128 res = 0, val = 0; size_t shift; for (shift = 0;;) { val = Get_8 (); res |= (val & 0x7f) << shift; shift += 7; if ((val & 0x80) == 0) break; } if ((val & 0x40) && (shift < 8 * sizeof (res))) res |= -(((ULEB128) 1) << shift); return (SLEB128) res; } uint64_t DwrSec::get_value (int dw_form) { uint64_t v; switch (dw_form) { case DW_FORM_line_strp: case DW_FORM_strp: case DW_FORM_strp_sup: return GetRef (); case DW_FORM_data1: return Get_8 (); case DW_FORM_data2: return Get_16 (); case DW_FORM_data4: return Get_32 (); case DW_FORM_data8: return Get_64 (); case DW_FORM_udata: return GetULEB128 (); case DW_FORM_data16: offset += 16; return offset - 16; case DW_FORM_block: v = GetULEB128 (); offset += v; return offset - v; } return 0; } static void fillBuf (unsigned char *s, int len, int col, unsigned char *buf) { const char *nameX = "0123456789abcdef"; int i, n, posCh = 2 * col + col / 4 + 5; if (len >= col) len = col; for (i = n = 0; i < len; i++, n += 2) { if ((i % 4) == 0 && i > 0) { buf[n] = ' '; n++; } buf[n] = nameX[s[i] >> 4]; buf[n + 1] = nameX[s[i] & 0xf]; buf[posCh + i] = isprint (s[i]) ? s[i] : ' '; } buf[posCh + i] = 0; for (i = n; i < posCh; i++) buf[i] = ' '; } static void dumpArr (unsigned char *s, int len, int col, int num) { unsigned char buf[128]; if (col <= 0) return; for (int i = 0; i < len; i += col, num += col) { fillBuf (s + i, len - i, col, buf); Dprintf (DUMP_DWARFLIB, "%5d: %s\n", num, buf); } } void DwrSec::dump (char *msg) { if (sizeSec > 0) { Dprintf (DUMP_DWARFLIB, NTXT ("======= DwrSec::dump\n")); if (msg) Dprintf (DUMP_DWARFLIB, NTXT ("%s:\n"), msg); dumpArr (data, (int) sizeSec, 32, 0); Dprintf (DUMP_DWARFLIB, NTXT ("\n")); } } ////////////////////////////////////////////////////////// // class DwrFileNames DwrFileName::DwrFileName (char *_fname) { path = NULL; fname = dbe_strdup (_fname); dir_index = 0; timestamp = 0; file_size = 0; isUsed = false; } DwrFileName::~DwrFileName () { if (path != fname) free (path); } ////////////////////////////////////////////////////////// // class DwrLine DwrLine::DwrLine () { address = 0; file = 0; line = 0; column = 0; } DwrLine::~DwrLine () { } ////////////////////////////////////////////////////////// // class DwrLineRegs static int LineRegsCmp (const void *a, const void *b) { DwrLine *item1 = *((DwrLine **) a); DwrLine *item2 = *((DwrLine **) b); return item1->address == item2->address ? 0 : item1->address > item2->address ? 1 : -1; } DwrLineRegs::DwrLineRegs (Dwarf *_dwarf, DwrSec *secp, char *dirName) { dwarf = _dwarf; dir_names = NULL; file_names = NULL; lines = NULL; fname = NULL; // `dwarfdump -vv -l` shows a line section (.debug_line) debug_lineSec = secp; uint64_t stmt_offset = debug_lineSec->offset; uint64_t next_cu_offset = debug_lineSec->ReadLength (); uint64_t header_offset = debug_lineSec->offset; debug_lineSec->size = next_cu_offset; version = debug_lineSec->Get_16 (); if (version == 5) { debug_lineSec->address_size = debug_lineSec->Get_8(); debug_lineSec->segment_selector_size = debug_lineSec->Get_8(); } header_length = debug_lineSec->GetLong (); opcode_start = debug_lineSec->offset + header_length; minimum_instruction_length = debug_lineSec->Get_8 (); op_index_register = 0; if (version >= 4) maximum_operations_per_instruction = debug_lineSec->Get_8 (); else maximum_operations_per_instruction = 1; default_is_stmt = debug_lineSec->Get_8 (); is_stmt = (default_is_stmt != 0); line_base = debug_lineSec->Get_8 (); line_range = debug_lineSec->Get_8 (); opcode_base = debug_lineSec->Get_8 (); standard_opcode_length = (Dwarf_Small*) debug_lineSec->GetData (opcode_base - 1); if (DUMP_DWR_LINE_REGS) { Dprintf (DUMP_DWR_LINE_REGS, "\n.debug_line version=%d stmt_offset=0x%llx" " header_offset=0x%llx size=%lld dirname='%s'\n" " header_length=0x%llx opcode_start=0x%llx" " minimum_instruction_length=%d default_is_stmt=%d\n" " line_base=%d line_range=%d opcode_base=%d\n", (int) version, (long long) stmt_offset, (long long) header_offset, (long long) (next_cu_offset - header_offset), STR (dirName), (long long) header_length, (long long) opcode_start, (int) minimum_instruction_length, (int) default_is_stmt, (int) line_base, (int) line_range, (int) opcode_base); if (standard_opcode_length == NULL) Dprintf (DUMP_DWR_LINE_REGS, "ERROR: standard_opcode_length is NULL\n"); for (int i = 0, sz = standard_opcode_length ? opcode_base - 1 : 0; i < sz; i++) Dprintf (DUMP_DWR_LINE_REGS, " opcode[%2d] length %2d\n", i, (int) standard_opcode_length[i]); } if (version == 5) { dir_names = read_file_names_dwarf5 (); file_names = read_file_names_dwarf5 (); } else { dir_names = new Vector; dir_names->append (new DwrFileName (dirName)); while (true) { char *s = debug_lineSec->GetString (); if (s == NULL) break; dir_names->append (new DwrFileName (s)); } file_names = new Vector; file_names->append (new DwrFileName (dirName)); while (true) { char *s = debug_lineSec->GetString (); if (s == NULL) break; DwrFileName *fnp = new DwrFileName (s); fnp->dir_index = debug_lineSec->GetULEB128_32 (); fnp->timestamp = debug_lineSec->GetULEB128 (); fnp->file_size = debug_lineSec->GetULEB128 (); file_names->append (fnp); } } dump (); } DwrLineRegs::~DwrLineRegs () { Destroy (dir_names); Destroy (file_names); Destroy (lines); delete debug_lineSec; } Vector * DwrLineRegs::read_file_names_dwarf5 () { typedef struct { int type_code; int form_code; } t_entry_fmt; int efmt_cnt = debug_lineSec->Get_8 (); Dprintf (DUMP_DWR_LINE_REGS, "\nRead names: offset=0x%llx entry_fmt_cnt=%d\n", (long long) debug_lineSec->offset, efmt_cnt); if (efmt_cnt == 0) return NULL; t_entry_fmt *efmt = (t_entry_fmt *) malloc (sizeof (t_entry_fmt) * efmt_cnt); for (int i = 0; i < efmt_cnt; i++) { efmt[i].type_code = debug_lineSec->GetULEB128 (); efmt[i].form_code = debug_lineSec->GetULEB128 (); Dprintf (DUMP_DWR_LINE_REGS, " %2d %20s %s\n", i, DwrCU::lnct2str (efmt[i].type_code), DwrCU::form2str (efmt[i].form_code)); } int cnt = debug_lineSec->GetULEB128_32 (); Dprintf (DUMP_DWR_LINE_REGS, "\nRead names: offset=0x%llx names_cnt=%d\n", (long long) debug_lineSec->offset, cnt); Vector *fnames = new Vector (cnt); for (int i = 0; i < cnt; i++) { int ind = 0; uint64_t off = 0; uint64_t tstamp = 0; uint64_t fsize = 0; char *nm = NULL; for (int k = 0; k < efmt_cnt; k++) switch (efmt[k].type_code) { case DW_LNCT_path: if (efmt[k].form_code == DW_FORM_string) nm = debug_lineSec->GetString (); else { off = debug_lineSec->get_value (efmt[k].form_code); if (efmt[k].form_code == DW_FORM_line_strp) nm = get_string (dwarf->debug_line_strSec, off); else if (efmt[k].form_code == DW_FORM_strp) nm = get_string (dwarf->debug_strSec, off); } break; case DW_LNCT_directory_index: ind = debug_lineSec->get_value (efmt[k].form_code); break; case DW_LNCT_timestamp: tstamp = debug_lineSec->get_value (efmt[k].form_code); break; case DW_LNCT_size: fsize = debug_lineSec->get_value (efmt[k].form_code); break; case DW_LNCT_MD5: (void) debug_lineSec->get_value (efmt[k].form_code); break; } Dprintf (DUMP_DWR_LINE_REGS, " %3d ind=%d off=0x%08llx %s\n", i, ind, (long long) off, STR (nm)); DwrFileName *fnp = new DwrFileName (nm); fnp->dir_index = ind; fnp->timestamp = tstamp; fnp->file_size = fsize; fnames->append (fnp); } free (efmt); return fnames; } void DwrLineRegs::dump () { if (!DUMP_DWR_LINE_REGS) return; if (dir_names) dir_names->dump ("dir_names"); if (file_names) file_names->dump ("file_names"); Dprintf (DUMP_DWR_LINE_REGS, NTXT ("\nfile_names size=%lld\n"), (long long) VecSize (file_names)); for (long i = 0, sz = VecSize (file_names); i < sz; i++) { DwrFileName *fnp = file_names->get (i); Dprintf (DUMP_DWR_LINE_REGS, NTXT (" %2lld %-40s dir_index=%4lld timestamp=%8lld file_size=%lld\n"), (long long) i, STR (fnp->fname), (long long) fnp->dir_index, (long long) fnp->timestamp, (long long) fnp->file_size); } if (lines) lines->dump (fname); Dprintf (DUMP_DWR_LINE_REGS, NTXT ("\n\n")); } void DwrLineRegs::DoExtendedOpcode () { uint64_t size = debug_lineSec->GetULEB128 (); if (size == 0) { Dprintf (DUMP_DWR_LINE_REGS, NTXT ("%-20s"), NTXT ("ExtendedOpCode: size=0")); return; } Dwarf_Small opcode = debug_lineSec->Get_8 (); Dprintf (DUMP_DWR_LINE_REGS, NTXT ("%-20s"), extended_opcode2str (opcode)); switch (opcode) { case DW_LNE_end_sequence: end_sequence = true; reset (); break; case DW_LNE_set_address: address = debug_lineSec->GetADDR (); break; case DW_LNE_define_file: // TODO, add file to file list fname = debug_lineSec->GetString (); dir_index = debug_lineSec->GetULEB128 (); timestamp = debug_lineSec->GetULEB128 (); file_size = debug_lineSec->GetULEB128 (); break; default: debug_lineSec->GetData (size - 1); // skip unknown opcode break; } } void DwrLineRegs::DoStandardOpcode (int opcode) { switch (opcode) { case DW_LNS_copy: basic_block = false; EmitLine (); break; case DW_LNS_advance_pc: address += debug_lineSec->GetULEB128 () * minimum_instruction_length; break; case DW_LNS_advance_line: line += (int) debug_lineSec->GetSLEB128 (); break; case DW_LNS_set_file: file = debug_lineSec->GetULEB128_32 (); break; case DW_LNS_set_column: column = debug_lineSec->GetULEB128_32 (); break; case DW_LNS_negate_stmt: is_stmt = -is_stmt; break; case DW_LNS_set_basic_block: basic_block = true; break; case DW_LNS_const_add_pc: address += ((255 - opcode_base) / line_range) * minimum_instruction_length; break; case DW_LNS_fixed_advance_pc: address += debug_lineSec->Get_16 (); break; default: // skip unknown opcode/operands debug_lineSec->GetData (standard_opcode_length ? standard_opcode_length[opcode] : 1); break; } } void DwrLineRegs::DoSpecialOpcode (int opcode) { int max_op_per_instr = maximum_operations_per_instruction == 0 ? 1 : maximum_operations_per_instruction; int operation_advance = (opcode / line_range); address += minimum_instruction_length * ((op_index_register + operation_advance) / max_op_per_instr); op_index_register = (op_index_register + operation_advance) % max_op_per_instr; line += line_base + (opcode % line_range); basic_block = false; EmitLine (); } void DwrLineRegs::reset () { dir_index = 0; timestamp = 0; file_size = 0; address = 0; file = 1; line = 1; column = 0; is_stmt = (default_is_stmt != 0); basic_block = false; end_sequence = false; } void DwrLineRegs::EmitLine () { DwrLine *lnp = new DwrLine; lnp->file = file; lnp->line = line; lnp->column = column; lnp->address = address; lines->append (lnp); if ((file > 0) && (file < VecSize (file_names))) { DwrFileName *fnp = file_names->get (file); fnp->isUsed = true; } } Vector * DwrLineRegs::get_lines () { if (lines == NULL) { lines = new Vector; debug_lineSec->offset = opcode_start; reset (); Dprintf (DUMP_DWR_LINE_REGS, "\n offset code address (file, line, column) stmt blck end_seq \n"); while (debug_lineSec->offset < debug_lineSec->size) { Dprintf (DUMP_DWR_LINE_REGS, NTXT ("0x%08llx "), (long long) debug_lineSec->offset); Dwarf_Small opcode = debug_lineSec->Get_8 (); if (opcode == 0) DoExtendedOpcode (); else if (opcode < opcode_base) { DoStandardOpcode (opcode); Dprintf (DUMP_DWR_LINE_REGS, NTXT ("%-20s"), standard_opcode2str (opcode)); } else { DoSpecialOpcode (opcode - opcode_base); Dprintf (DUMP_DWR_LINE_REGS, NTXT ("%-20s"), special_opcode2str (opcode - opcode_base)); } Dprintf (DUMP_DWR_LINE_REGS, " 0x%08llx (%lld, %lld, %lld) %c %c %c\n", (long long) address, (long long) file, (long long) line, (long long) column, is_stmt ? 'T' : 'F', basic_block ? 'T' : 'F', end_sequence ? 'T' : 'F'); } lines->sort (LineRegsCmp); if (DUMP_DWR_LINE_REGS) lines->dump (fname); } return lines; } char * DwrLineRegs::getPath (int fn) { if (fn >= VecSize (file_names) || fn < 0) { Dprintf (DEBUG_ERR_MSG, NTXT ("DwrLineRegs::getPath: fn=0x%lld file_names->size()=%lld\n"), (long long) fn, (long long) VecSize (file_names)); return NULL; } DwrFileName *fnp = file_names->fetch (fn); if (fnp->fname == NULL) return NULL; if (fnp->path) return fnp->path; fnp->path = fnp->fname; if (fnp->fname[0] == '/') return fnp->path; char *dir = NULL; if (dir_names) { if (fnp->dir_index < dir_names->size () && fnp->dir_index >= 0) dir = dir_names->get (fnp->dir_index)->fname; } if (dir == NULL || *dir == 0) return fnp->path; char *dir1 = NULL; if (*dir != '/') dir1 = dir_names->get(0)->fname; if (dir1 && *dir != 0) fnp->path = dbe_sprintf ("%s/%s/%s", dir1, dir, fnp->fname); else fnp->path = dbe_sprintf ("%s/%s", dir, fnp->fname); fnp->path = canonical_path (fnp->path); return fnp->path; } DwrCU::DwrCU (Dwarf *_dwarf) { dwarf = _dwarf; cu_offset = dwarf->debug_infoSec->offset; debug_infoSec = new DwrSec (dwarf->debug_infoSec, cu_offset); next_cu_offset = debug_infoSec->ReadLength (); if (next_cu_offset > debug_infoSec->sizeSec) { Dprintf (DEBUG_ERR_MSG, "DwrCU::DwrCU: next_cu_offset(0x%llx) > debug_infoSec->sizeSec(%llx)\n", (long long) next_cu_offset, (long long) debug_infoSec->sizeSec); next_cu_offset = debug_infoSec->sizeSec; } debug_infoSec->size = next_cu_offset; version = debug_infoSec->Get_16 (); if (version == 5) { unit_type = debug_infoSec->Get_8 (); address_size = debug_infoSec->Get_8 (); debug_abbrev_offset = debug_infoSec->GetLong (); } else { unit_type = DW_UT_compile; debug_abbrev_offset = debug_infoSec->GetLong (); address_size = debug_infoSec->Get_8 (); } cu_header_offset = debug_infoSec->offset; comp_dir = NULL; module = NULL; abbrevTable = NULL; dwrInlinedSubrs = NULL; srcFiles = NULL; stmt_list_offset = NO_STMT_LIST; dwrLineReg = NULL; isMemop = false; isGNU = false; dwrTag.level = 0; build_abbrevTable (dwarf->debug_abbrevSec, debug_abbrev_offset); #ifdef DEBUG if (DUMP_DWARFLIB) { Dprintf (DUMP_DWARFLIB, "CU_HEADER: header_offset = 0x%08llx %lld" " next_header_offset=0x%08llx %lld\n" " abbrev_offset = 0x%08llx %lld\n" " unit_length = %lld\n" " version = %d\n" " address_size = %d\n" " fmt64 = %s\n" "debug_info: need_swap_endian=%s fmt64=%s addr32=%s\n", (long long) cu_offset, (long long) cu_offset, (long long) next_cu_offset, (long long) next_cu_offset, (long long) debug_abbrev_offset, (long long) debug_abbrev_offset, (long long) (next_cu_offset - cu_offset), (int) version, (int) address_size, debug_infoSec->fmt64 ? "true" : "false", debug_infoSec->need_swap_endian ? "true" : "false", debug_infoSec->fmt64 ? "true" : "false", debug_infoSec->addr32 ? "true" : "false"); Dprintf (DUMP_DWARFLIB, "\n.debug_abbrev cnt=%d offset=0x%08llx %lld\n", (int) VecSize (abbrevTable), (long long) debug_abbrev_offset, (long long) debug_abbrev_offset); for (int i = 1, sz = VecSize (abbrevTable); i < sz; i++) { DwrAbbrevTable *abbTbl = abbrevTable->get (i); Dprintf (DUMP_DWARFLIB, NTXT ("%5d: %-30s %-20s offset=0x%08llx\n"), (int) i, DwrCU::tag2str (abbTbl->tag), abbTbl->hasChild ? "DW_children_yes" : "DW_children_no", (long long) abbTbl->offset); for (int i1 = abbTbl->firstAtForm; i1 < abbTbl->lastAtForm; i1++) { Dwr_Attr *atf = abbrevAtForm->get (i1); Dprintf (DUMP_DWARFLIB, " %-30s %s\n", DwrCU::at2str (atf->at_name), DwrCU::form2str (atf->at_form)); } } } #endif } DwrCU::~DwrCU () { delete debug_infoSec; delete abbrevTable; delete abbrevAtForm; Destroy (dwrInlinedSubrs); delete srcFiles; delete dwrLineReg; free (comp_dir); } void DwrCU::build_abbrevTable (DwrSec *_debug_abbrevSec, uint64_t _offset) { if (abbrevTable) return; DwrSec *debug_abbrevSec = new DwrSec (_debug_abbrevSec, _offset); abbrevTable = new DbeArray (128); abbrevAtForm = new DbeArray (512); abbrevTable->allocate (1); // skip first abbrevAtForm->allocate (1); // skip first for (int i = 1; debug_abbrevSec->offset < debug_abbrevSec->size; i++) { DwrAbbrevTable abbTbl; abbTbl.offset = debug_abbrevSec->offset; abbTbl.code = debug_abbrevSec->GetULEB128_32 (); if (abbTbl.code == 0) break; else if (i != abbTbl.code) { dwarf->elf->append_msg (CMSG_ERROR, GTXT ("%s: the abbreviations table is corrupted (%lld <--> %lld)\n"), get_basename (dwarf->elf->get_location ()), (long long) i, (long long) abbTbl.code); break; } abbTbl.tag = debug_abbrevSec->GetULEB128_32 (); abbTbl.hasChild = (DW_children_yes == debug_abbrevSec->Get_8 ()); abbTbl.firstAtForm = abbrevAtForm->size (); while (debug_abbrevSec->offset < debug_abbrevSec->size) { Dwr_Attr atf; atf.len = 0; atf.u.str = NULL; atf.at_name = debug_abbrevSec->GetULEB128_32 (); atf.at_form = debug_abbrevSec->GetULEB128_32 (); if (atf.at_name == 0 && atf.at_form == 0) break; switch (atf.at_form) { case DW_FORM_implicit_const: atf.len = debug_abbrevSec->GetSLEB128 (); break; } abbrevAtForm->append (atf); } abbTbl.lastAtForm = abbrevAtForm->size (); abbrevTable->append (abbTbl); } delete debug_abbrevSec; } int DwrCU::set_die (Dwarf_Die die) { if (die > 0) debug_infoSec->offset = die; if (debug_infoSec->offset < cu_header_offset || debug_infoSec->offset >= debug_infoSec->size) return DW_DLV_ERROR; dwrTag.offset = debug_infoSec->offset; dwrTag.die = debug_infoSec->offset - cu_offset; dwrTag.num = debug_infoSec->GetULEB128_32 (); if (dwrTag.num == 0) return DW_DLV_NO_ENTRY; dwrTag.abbrevAtForm = abbrevAtForm; DwrAbbrevTable *abbTbl = abbrevTable->get (dwrTag.num); if (abbTbl == NULL) { // corrupt dwarf dwarf->elf->append_msg (CMSG_ERROR, GTXT ("%s: the abbreviation code (%lld) does not match for the Dwarf entry (0x%llx)\n"), get_basename (dwarf->elf->get_location ()), (long long) dwrTag.num, (long long) dwrTag.offset); return DW_DLV_ERROR; } dwrTag.tag = abbTbl->tag; dwrTag.hasChild = abbTbl->hasChild; dwrTag.firstAttribute = abbTbl->firstAtForm; dwrTag.lastAttribute = abbTbl->lastAtForm; for (int k = abbTbl->firstAtForm; k < abbTbl->lastAtForm; k++) { Dwr_Attr *atf = abbrevAtForm->get (k); int at_form = atf->at_form; if (at_form == DW_FORM_indirect) at_form = debug_infoSec->GetULEB128_32 (); switch (at_form) { case DW_FORM_addr: atf->u.offset = (address_size == 4) ? debug_infoSec->GetADDR_32 () : debug_infoSec->GetADDR_64 (); break; case DW_FORM_flag: atf->u.offset = debug_infoSec->Get_8 (); break; case DW_FORM_block: atf->len = debug_infoSec->GetULEB128 (); atf->u.str = debug_infoSec->GetData (atf->len); break; case DW_FORM_block1: atf->len = debug_infoSec->Get_8 (); atf->u.str = debug_infoSec->GetData (atf->len); break; case DW_FORM_block2: atf->len = debug_infoSec->Get_16 (); atf->u.str = debug_infoSec->GetData (atf->len); break; case DW_FORM_block4: atf->len = debug_infoSec->Get_32 (); atf->u.str = debug_infoSec->GetData (atf->len); break; case DW_FORM_ref1: atf->u.offset = debug_infoSec->Get_8 (); break; case DW_FORM_ref2: atf->u.offset = debug_infoSec->Get_16 (); break; case DW_FORM_ref4: atf->u.offset = debug_infoSec->Get_32 (); break; case DW_FORM_ref8: atf->u.offset = debug_infoSec->Get_64 (); break; case DW_FORM_ref_udata: atf->u.offset = debug_infoSec->GetULEB128 (); break; case DW_FORM_data1: atf->u.offset = debug_infoSec->Get_8 (); break; case DW_FORM_data2: atf->u.offset = debug_infoSec->Get_16 (); break; case DW_FORM_data4: atf->u.offset = debug_infoSec->Get_32 (); break; case DW_FORM_data8: atf->u.offset = debug_infoSec->Get_64 (); break; case DW_FORM_string: atf->u.offset = debug_infoSec->offset; atf->u.str = debug_infoSec->GetString (); break; case DW_FORM_strp: atf->u.offset = debug_infoSec->GetRef (); atf->u.str = get_string (dwarf->debug_strSec, atf->u.offset); break; case DW_FORM_sdata: atf->u.val = debug_infoSec->GetSLEB128 (); break; case DW_FORM_udata: atf->u.offset = debug_infoSec->GetULEB128 (); break; case DW_FORM_ref_addr: if (version > 2) atf->u.offset = debug_infoSec->GetRef (); else atf->u.offset = debug_infoSec->GetADDR (); break; case DW_FORM_sec_offset: atf->u.offset = debug_infoSec->GetRef (); break; case DW_FORM_exprloc: atf->u.offset = debug_infoSec->GetULEB128 (); debug_infoSec->offset += atf->u.offset; break; case DW_FORM_flag_present: atf->u.val = 1; break; case DW_FORM_ref_sig8: atf->u.offset = debug_infoSec->GetADDR_64 (); break; case DW_FORM_data16: // we never use this data. Skip 16 bytes atf->len = 16; (void) debug_infoSec->Get_64 (); (void) debug_infoSec->Get_64 (); break; case DW_FORM_addrx: case DW_FORM_strx: case DW_FORM_loclistx: case DW_FORM_rnglistx: atf->u.offset = debug_infoSec->GetULEB128 (); break; case DW_FORM_addrx1: case DW_FORM_strx1: atf->u.offset = debug_infoSec->Get_8 (); break; case DW_FORM_addrx2: case DW_FORM_strx2: atf->u.offset = debug_infoSec->Get_16 (); break; case DW_FORM_addrx3: case DW_FORM_strx3: atf->u.offset = debug_infoSec->Get_24 (); break; case DW_FORM_addrx4: case DW_FORM_strx4: case DW_FORM_ref_sup4: atf->u.offset = debug_infoSec->Get_32 (); break; case DW_FORM_ref_sup8: atf->u.offset = debug_infoSec->Get_64 (); break; case DW_FORM_line_strp: atf->u.offset = debug_infoSec->GetRef (); atf->u.str = get_string (dwarf->debug_line_strSec, atf->u.offset); break; case DW_FORM_strp_sup: atf->u.offset = debug_infoSec->GetRef (); atf->u.str = NULL; atf->len = 0; break; case DW_FORM_implicit_const: atf->u.str = NULL; break; default: DEBUG_CODE { Dprintf (1, "Attribute form 0x%llx (%lld) is not implemented\n", (long long) atf->at_form, (long long) atf->at_form); assert (0); } atf->u.str = NULL; atf->len = 0; break; } } dwrTag.dump (); return DW_DLV_OK; } static char * composePath (char *dname, char *fname) { char *s; if (*fname == '/' || dname == NULL) s = dbe_sprintf (NTXT ("%s"), fname); else s = dbe_sprintf (NTXT ("%s/%s"), dname, fname); return canonical_path (s); } Module * DwrCU::parse_cu_header (LoadObject *lo) { // Is tag always DW_TAG_compile_unit? if (dwrTag.tag != DW_TAG_compile_unit) { Dprintf (DEBUG_ERR_MSG, "parse_cu_header: die=0x%llx tag=%lld is not DW_TAG_compile_unit\n", (long long) cu_offset, (long long) dwrTag.tag); return NULL; } char *name = Dwarf_string (DW_AT_name); if (name == NULL) name = NTXT ("UnnamedUnit"); int64_t v; if (read_data_attr(DW_AT_stmt_list, &v) == DW_DLV_OK) stmt_list_offset = v; comp_dir = dbe_strdup (Dwarf_string (DW_AT_comp_dir)); char *dir_name = comp_dir ? StrChr (comp_dir, ':') : NULL; char *orig_name = Dwarf_string (DW_AT_SUN_original_name); char *path = composePath (dir_name, orig_name ? orig_name : name); module = dwarf->stabs->append_Module (lo, path); free (path); if (module == NULL) return NULL; module->hasDwarf = true; if (orig_name) module->linkerStabName = composePath (dir_name, name); module->lang_code = Dwarf_lang (); module->comp_flags = dbe_strdup (Dwarf_string (DW_AT_SUN_command_line)); if (module->comp_flags == NULL) module->comp_flags = dbe_strdup (Dwarf_string (DW_AT_icc_flags)); module->comp_dir = dbe_strdup (dir_name); char *obj_file = Dwarf_string (DW_AT_SUN_obj_file); char *obj_dir = Dwarf_string (DW_AT_SUN_obj_dir); if (obj_dir && obj_file) { // object information may not be available dir_name = StrChr (obj_dir, ':'); path = composePath (dir_name, obj_file); if (module->dot_o_file == NULL) module->dot_o_file = module->createLoadObject (path); } else path = dbe_strdup (dwarf->stabs->path); module->set_name (path); return module; } Dwr_Attr * Dwr_Tag::get_attr (Dwarf_Half attr) { for (long i = firstAttribute; i < lastAttribute; i++) { Dwr_Attr *atf = abbrevAtForm->get (i); if (atf->at_name == attr) return atf; } return NULL; } char * DwrCU::Dwarf_string (Dwarf_Half attr) { Dwr_Attr *dwrAttr = dwrTag.get_attr (attr); return dwrAttr ? dwrAttr->u.str : NULL; } uint64_t DwrCU::get_high_pc (uint64_t low_pc) { Dwr_Attr *dwrAttr = dwrTag.get_attr (DW_AT_high_pc); if (dwrAttr) switch (dwrAttr->at_form) { case DW_FORM_addr: return dwrAttr->u.offset; default: return dwrAttr->u.offset + low_pc; } return 0; } Dwarf_Addr DwrCU::Dwarf_addr (Dwarf_Half attr) { Dwr_Attr *dwrAttr = dwrTag.get_attr (attr); if (dwrAttr) switch (dwrAttr->at_form) { case DW_FORM_addr: return dwrAttr->u.offset; } return 0; } DwrSec* DwrCU::Dwarf_block (Dwarf_Half attr) { Dwr_Attr *dwrAttr = dwrTag.get_attr (attr); if (dwrAttr && dwrAttr->u.block) switch (dwrAttr->at_form) { case DW_FORM_block: case DW_FORM_block1: case DW_FORM_block2: case DW_FORM_block4: return new DwrSec (dwrAttr->u.block, dwrAttr->len, dwarf->elf->need_swap_endian, dwarf->elf->elf_getclass () == ELFCLASS32); } return NULL; } int DwrCU::read_data_attr (Dwarf_Half attr, int64_t *retVal) { Dwr_Attr *dwrAttr = dwrTag.get_attr (attr); if (dwrAttr) switch (dwrAttr->at_form) { case DW_FORM_data1: case DW_FORM_data2: case DW_FORM_data4: case DW_FORM_data8: case DW_FORM_data16: case DW_FORM_udata: case DW_FORM_sec_offset: *retVal = dwrAttr->u.val; return DW_DLV_OK; } return DW_DLV_ERROR; } int DwrCU::read_ref_attr (Dwarf_Half attr, int64_t *retVal) { Dwr_Attr *dwrAttr = dwrTag.get_attr (attr); if (dwrAttr) switch (dwrAttr->at_form) { case DW_FORM_ref1: case DW_FORM_ref2: case DW_FORM_ref4: case DW_FORM_ref8: case DW_FORM_ref_udata: case DW_FORM_sec_offset: case DW_FORM_exprloc: case DW_FORM_ref_sig8: *retVal = dwrAttr->u.val; return DW_DLV_OK; } return DW_DLV_ERROR; } int64_t DwrCU::Dwarf_data (Dwarf_Half attr) { int64_t retVal; if (read_data_attr (attr, &retVal) == DW_DLV_OK) return retVal; return 0; } int64_t DwrCU::Dwarf_ref (Dwarf_Half attr) { int64_t retVal; if (read_ref_attr (attr, &retVal) == DW_DLV_OK) return retVal; return 0; } Dwarf_Addr DwrCU::Dwarf_location (Dwarf_Attribute attr) { DwrSec *secp = Dwarf_block (attr); if (secp) { DwrLocation loc; DwrLocation *lp = dwr_get_location (secp, &loc); delete secp; if (lp) return lp->lc_number; } return 0; } void DwrCU::map_dwarf_lines (Module *mod) { DwrLineRegs *lineReg = get_dwrLineReg (); long inlinedSubrCnt = VecSize (dwrInlinedSubrs); if (isGNU && (inlinedSubrCnt > 0)) { Function *func = NULL; mod->inlinedSubr = (InlinedSubr *) malloc (inlinedSubrCnt * sizeof (InlinedSubr)); for (long i = 0; i < inlinedSubrCnt; i++) { DwrInlinedSubr *inlinedSubr = dwrInlinedSubrs->get (i); uint64_t low_pc; Function *f = dwarf->stabs->map_PC_to_func (inlinedSubr->low_pc, low_pc, mod->functions); if (f == NULL) continue; if (func != f) { func = f; func->inlinedSubrCnt = 0; func->inlinedSubr = mod->inlinedSubr + i; } InlinedSubr *p = func->inlinedSubr + func->inlinedSubrCnt; func->inlinedSubrCnt++; int fileno = inlinedSubr->file - 1; SourceFile *sf = ((fileno >= 0) && (fileno < VecSize (srcFiles))) ? srcFiles->get (fileno) : dbeSession->get_Unknown_Source (); p->dbeLine = sf->find_dbeline (inlinedSubr->line); p->high_pc = inlinedSubr->high_pc - low_pc; p->low_pc = inlinedSubr->low_pc - low_pc; p->level = inlinedSubr->level; p->func = NULL; p->fname = NULL; if (set_die (inlinedSubr->abstract_origin) == DW_DLV_OK) p->fname = dbe_strdup (Dwarf_string (DW_AT_name)); if (p->fname) p->func = Stabs::find_func (p->fname, mod->functions, Stabs::is_fortran (mod->lang_code)); } } if (lineReg == NULL) return; Vector *lines = lineReg->get_lines (); Include *includes = new Include; includes->new_src_file (mod->getMainSrc (), 0, NULL); char *path = NULL; SourceFile *cur_src = NULL; Function *cur_func = NULL; for (long i = 0, sz = VecSize (lines); i < sz; i++) { DwrLine *dwrLine = lines->get (i); char *filename = lineReg->getPath (dwrLine->file); if (filename == NULL) continue; uint64_t pc = dwrLine->address; int lineno = dwrLine->line; if (path != filename) { path = filename; char *name = StrChr (path, ':'); SourceFile *src = mod->setIncludeFile (name); if (cur_src != src) { includes->new_src_file (src, lineno, cur_func); cur_src = src; } } uint64_t low_pc; Function *func = dwarf->stabs->map_PC_to_func (pc, low_pc, mod->functions); if (func && (func->module == mod)) { if (func != cur_func) { if (cur_func) while (cur_func->popSrcFile () != NULL) ; cur_func = func; includes->push_src_files (cur_func); } cur_func->add_PC_info (pc - low_pc, lineno); } } if (cur_func) while (cur_func->popSrcFile ()) ; delete includes; } DwrLineRegs * DwrCU::get_dwrLineReg () { if (dwrLineReg == NULL && stmt_list_offset != NO_STMT_LIST) dwrLineReg = new DwrLineRegs (dwarf, new DwrSec (dwarf->debug_lineSec, stmt_list_offset), comp_dir); return dwrLineReg; } void DwrCU::parse_inlined_subroutine (Dwarf_cnt *ctx) { int64_t abstract_origin = Dwarf_ref (DW_AT_abstract_origin); int fileno = (int) Dwarf_data (DW_AT_call_file); int lineno = (int) Dwarf_data (DW_AT_call_line); int level = ctx->inlinedSubr ? (ctx->inlinedSubr->level + 1) : 0; DwrInlinedSubr *inlinedSubr_old = ctx->inlinedSubr; if (dwrInlinedSubrs == NULL) dwrInlinedSubrs = new Vector; Dwr_Attr *dwrAttr = dwrTag.get_attr (DW_AT_ranges); if (dwrAttr) { uint64_t ranges = Dwarf_ref (DW_AT_ranges); if (dwarf->debug_rangesSec && (ranges < dwarf->debug_rangesSec->size)) { dwarf->debug_rangesSec->offset = ranges; for (;;) { uint64_t low_pc = dwarf->debug_rangesSec->GetADDR (); uint64_t high_pc = dwarf->debug_rangesSec->GetADDR (); if ((low_pc > 0) && (low_pc <= high_pc)) { DwrInlinedSubr *p = new DwrInlinedSubr (abstract_origin, low_pc, high_pc, fileno, lineno, level); dwrInlinedSubrs->append (p); ctx->inlinedSubr = p; } else break; } } } else { uint64_t low_pc = Dwarf_addr (DW_AT_low_pc); uint64_t high_pc = get_high_pc (low_pc); if ((low_pc > 0) && (low_pc <= high_pc)) { DwrInlinedSubr *p = new DwrInlinedSubr (abstract_origin, low_pc, high_pc, fileno, lineno, level); dwrInlinedSubrs->append (p); ctx->inlinedSubr = p; } } parseChild (ctx); ctx->inlinedSubr = inlinedSubr_old; } ////////////////////////////////////////////////////////// // class DwrInlinedSubr DwrInlinedSubr::DwrInlinedSubr (int64_t _abstract_origin, uint64_t _low_pc, uint64_t _high_pc, int _file, int _line, int _level) { abstract_origin = _abstract_origin; low_pc = _low_pc; high_pc = _high_pc; file = _file; line = _line; level = _level; } void DwrInlinedSubr::dump () { Dprintf (DUMP_DWARFLIB, " level=%d 0x%08llx [0x%08llx - 0x%08llx] file=%d line=%d\n", (int) level, (long long) abstract_origin, (long long) low_pc, (long long) high_pc, (int) file, (int) line); }