Retro68/binutils/gprofng/src/DwarfLib.cc
Wolfgang Thaller f485e125c4 binutils 2.39
2022-10-27 20:45:45 +02:00

2204 lines
56 KiB
C++

/* Copyright (C) 2021 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 <ctype.h>
#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 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<DwrInlinedSubr *>
::dump (const char *msg)
{
Dprintf (1, NTXT ("%s Vector<DwrInlinedSubr *> [%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<DwrLine *>
::dump (const char *msg)
{
Dprintf (1, "%s Vector<DwrLine *> [%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"));
}
//////////////////////////////////////////////////////////
// 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<Sreloc *> *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<Sreloc*>;
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);
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_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);
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_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;
}
void
Dwr_Tag::dump ()
{
Dprintf (DUMP_DWARFLIB,
"\n<%2d>:<0x%08llx> %-30s <abbrev %lld> 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:
Dprintf (DUMP_DWARFLIB, " \"%s\" len=%ld",
atrp->u.str ? atrp->u.str : NTXT ("<NULL>"),
(long) atrp->len);
break;
case DW_FORM_block:
case DW_FORM_block1:
case DW_FORM_block2:
case DW_FORM_block4:
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_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 *lenp)
{
if (offset < size)
{
uint64_t len = 0;
for (char *s = ((char *) data) + offset; offset + len < size; len++)
{
if (s[len] == 0)
{ // '\0' is inside section
offset += len + 1;
if (len == 0)
return NULL;
if (lenp)
*lenp = len + 1;
return s;
}
}
offset += len;
return NULL; // The section is not '\0' terminated
}
return NULL;
}
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;
}
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 = _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 (DwrSec *secp, char *dirName)
{
// `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 ();
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]);
}
include_directories = new Vector<char *>;
include_directories->append (dirName);
while (true)
{
char *s = debug_lineSec->GetString (NULL);
if (s == NULL)
break;
include_directories->append (s);
}
file_names = new Vector<DwrFileName *>;
while (true)
{
char *s = debug_lineSec->GetString (NULL);
if (s == NULL)
break;
DwrFileName *fnp = new DwrFileName (s);
fnp->path = NULL;
fnp->fname = s;
fnp->dir_index = debug_lineSec->GetULEB128_32 ();
fnp->timestamp = debug_lineSec->GetULEB128 ();
fnp->file_size = debug_lineSec->GetULEB128 ();
file_names->append (fnp);
}
lines = NULL;
dump ();
}
DwrLineRegs::~DwrLineRegs ()
{
Destroy (file_names);
Destroy (lines);
delete debug_lineSec;
delete include_directories;
}
void
DwrLineRegs::dump ()
{
if (!DUMP_DWR_LINE_REGS)
return;
Dprintf (DUMP_DWR_LINE_REGS, NTXT ("\ninclude_directories size=%lld\n"), (long long) VecSize (include_directories));
for (long i = 0, sz = VecSize (include_directories); i < sz; i++)
{
char *s = include_directories->get (i);
Dprintf (DUMP_DWR_LINE_REGS, NTXT (" %2lld %s\n"), (long long) i, STR (s));
}
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 (NULL);
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<DwrLine *> *
DwrLineRegs::get_lines ()
{
if (lines == NULL)
{
lines = new Vector<DwrLine *>;
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)
{
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->path)
return fnp->path;
char *dir = fnp->dir_index < include_directories->size () ?
include_directories->fetch (fnp->dir_index) : NULL;
if ((fnp->fname[0] == '/') || (dir == NULL) || (*dir == 0))
{
fnp->path = fnp->fname;
return fnp->path;
}
StringBuilder sb;
if (*dir != '/')
{ // not absolute
char *s = include_directories->fetch (0);
sb.append (s);
sb.append ('/');
}
sb.append (dir);
sb.append ('/');
sb.append (fnp->fname);
fnp->path = canonical_path (sb.toString ());
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 ();
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 = 0;
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 <DwrAbbrevTable>(128);
abbrevAtForm = new DbeArray <Dwr_Attr>(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.at_name = debug_abbrevSec->GetULEB128_32 ();
atf.at_form = debug_abbrevSec->GetULEB128_32 ();
if (atf.at_name == 0 && atf.at_form == 0)
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.str = debug_infoSec->GetString (&atf->len);
break;
case DW_FORM_strp:
atf->u.offset = debug_infoSec->GetRef ();
if (dwarf->debug_strSec == NULL)
{
atf->u.str = NULL;
atf->len = 0;
}
else
{
dwarf->debug_strSec->offset = atf->u.offset;
atf->u.str = dwarf->debug_strSec->GetString (&atf->len);
}
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:
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;
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");
stmt_list_offset = Dwarf_data (DW_AT_stmt_list);
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_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));
}
}
Vector<DwrLine *> *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 = dwrLineReg->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)
dwrLineReg = new DwrLineRegs (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<DwrInlinedSubr*>;
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);
}