/* DWARF 2 support.
- Copyright (C) 1994-2020 Free Software Foundation, Inc.
+ Copyright (C) 1994-2021 Free Software Foundation, Inc.
Adapted from gdb/dwarf2read.c by Gavin Koch of Cygnus Solutions
(gavin@cygnus.com).
/* Length of the loaded .debug_ranges section. */
bfd_size_type dwarf_ranges_size;
+ /* Pointer to the .debug_rnglists section loaded into memory. */
+ bfd_byte *dwarf_rnglists_buffer;
+
+ /* Length of the loaded .debug_rnglists section. */
+ bfd_size_type dwarf_rnglists_size;
+
/* A list of all previously read comp_units. */
struct comp_unit *all_comp_units;
{ ".debug_pubnames", ".zdebug_pubnames" },
{ ".debug_pubtypes", ".zdebug_pubtypes" },
{ ".debug_ranges", ".zdebug_ranges" },
+ { ".debug_rnglists", ".zdebug_rnglist" },
{ ".debug_static_func", ".zdebug_static_func" },
{ ".debug_static_vars", ".zdebug_static_vars" },
{ ".debug_str", ".zdebug_str", },
debug_pubnames,
debug_pubtypes,
debug_ranges,
+ debug_rnglists,
debug_static_func,
debug_static_vars,
debug_str,
bfd_byte ** section_buffer,
bfd_size_type * section_size)
{
- asection *msec;
const char *section_name = sec->uncompressed_name;
bfd_byte *contents = *section_buffer;
- bfd_size_type amt;
/* The section may have already been read. */
if (contents == NULL)
{
+ bfd_size_type amt;
+ asection *msec;
+ ufile_ptr filesize;
+
msec = bfd_get_section_by_name (abfd, section_name);
- if (! msec)
+ if (msec == NULL)
{
section_name = sec->compressed_name;
if (section_name != NULL)
msec = bfd_get_section_by_name (abfd, section_name);
}
- if (! msec)
+ if (msec == NULL)
{
_bfd_error_handler (_("DWARF error: can't find %s section."),
sec->uncompressed_name);
return FALSE;
}
- *section_size = msec->rawsize ? msec->rawsize : msec->size;
+ amt = bfd_get_section_limit_octets (abfd, msec);
+ filesize = bfd_get_file_size (abfd);
+ if (amt >= filesize)
+ {
+ /* PR 26946 */
+ _bfd_error_handler (_("DWARF error: section %s is larger than its filesize! (0x%lx vs 0x%lx)"),
+ section_name, (long) amt, (long) filesize);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+ *section_size = amt;
/* Paranoia - alloc one extra so that we can make sure a string
section is NUL terminated. */
- amt = *section_size + 1;
+ amt += 1;
if (amt == 0)
{
+ /* Paranoia - this should never happen. */
bfd_set_error (bfd_error_no_memory);
return FALSE;
}
static inline bfd_boolean
is_str_attr (enum dwarf_form form)
{
- return (form == DW_FORM_string || form == DW_FORM_strp
- || form == DW_FORM_line_strp || form == DW_FORM_GNU_strp_alt);
+ return (form == DW_FORM_string
+ || form == DW_FORM_strp
+ || form == DW_FORM_strx
+ || form == DW_FORM_strx1
+ || form == DW_FORM_strx2
+ || form == DW_FORM_strx3
+ || form == DW_FORM_strx4
+ || form == DW_FORM_line_strp
+ || form == DW_FORM_GNU_strp_alt);
+}
+
+static const char *
+read_indexed_string (bfd_uint64_t idx ATTRIBUTE_UNUSED,
+ struct comp_unit * unit ATTRIBUTE_UNUSED)
+{
+ /* FIXME: Add support for indexed strings. */
+ return "<indexed strings not yet supported>";
}
/* Read and fill in the value of attribute ATTR as described by FORM.
switch (form)
{
+ case DW_FORM_flag_present:
+ attr->u.val = 1;
+ break;
case DW_FORM_ref_addr:
/* DW_FORM_ref_addr is an address in DWARF2, and an offset in
DWARF3. */
- if (unit->version == 3 || unit->version == 4)
+ if (unit->version >= 3)
{
if (unit->offset_size == 4)
attr->u.val = read_4_bytes (unit->abfd, info_ptr, info_ptr_end);
info_ptr = read_n_bytes (info_ptr, info_ptr_end, blk);
attr->u.blk = blk;
break;
+ case DW_FORM_ref1:
+ case DW_FORM_flag:
+ case DW_FORM_data1:
+ case DW_FORM_addrx1:
+ attr->u.val = read_1_byte (abfd, info_ptr, info_ptr_end);
+ info_ptr += 1;
+ break;
case DW_FORM_data2:
+ case DW_FORM_ref2:
attr->u.val = read_2_bytes (abfd, info_ptr, info_ptr_end);
info_ptr += 2;
break;
+ case DW_FORM_addrx3:
+ attr->u.val = read_4_bytes (abfd, info_ptr, info_ptr_end);
+ attr->u.val &= 0xffffff;
+ info_ptr += 3;
+ break;
+ case DW_FORM_ref4:
case DW_FORM_data4:
+ case DW_FORM_addrx4:
attr->u.val = read_4_bytes (abfd, info_ptr, info_ptr_end);
info_ptr += 4;
break;
case DW_FORM_data8:
+ case DW_FORM_ref8:
+ case DW_FORM_ref_sig8:
attr->u.val = read_8_bytes (abfd, info_ptr, info_ptr_end);
info_ptr += 8;
break;
attr->u.str = read_alt_indirect_string (unit, info_ptr, info_ptr_end, &bytes_read);
info_ptr += bytes_read;
break;
+ case DW_FORM_strx1:
+ attr->u.val = read_1_byte (abfd, info_ptr, info_ptr_end);
+ info_ptr += 1;
+ attr->u.str = (char *) read_indexed_string (attr->u.val, unit);
+ break;
+ case DW_FORM_strx2:
+ attr->u.val = read_2_bytes (abfd, info_ptr, info_ptr_end);
+ info_ptr += 2;
+ attr->u.str = (char *) read_indexed_string (attr->u.val, unit);
+ break;
+ case DW_FORM_strx3:
+ attr->u.val = read_4_bytes (abfd, info_ptr, info_ptr_end);
+ info_ptr += 3;
+ attr->u.val &= 0xffffff;
+ attr->u.str = (char *) read_indexed_string (attr->u.val, unit);
+ break;
+ case DW_FORM_strx4:
+ attr->u.val = read_4_bytes (abfd, info_ptr, info_ptr_end);
+ info_ptr += 4;
+ attr->u.str = (char *) read_indexed_string (attr->u.val, unit);
+ break;
+ case DW_FORM_strx:
+ attr->u.val = _bfd_safe_read_leb128 (abfd, info_ptr, &bytes_read,
+ FALSE, info_ptr_end);
+ info_ptr += bytes_read;
+ attr->u.str = (char *) read_indexed_string (attr->u.val, unit);
+ break;
case DW_FORM_exprloc:
case DW_FORM_block:
amt = sizeof (struct dwarf_block);
info_ptr = read_n_bytes (info_ptr, info_ptr_end, blk);
attr->u.blk = blk;
break;
- case DW_FORM_data1:
- attr->u.val = read_1_byte (abfd, info_ptr, info_ptr_end);
- info_ptr += 1;
- break;
- case DW_FORM_flag:
- attr->u.val = read_1_byte (abfd, info_ptr, info_ptr_end);
- info_ptr += 1;
- break;
- case DW_FORM_flag_present:
- attr->u.val = 1;
- break;
case DW_FORM_sdata:
attr->u.sval = _bfd_safe_read_leb128 (abfd, info_ptr, &bytes_read,
TRUE, info_ptr_end);
info_ptr += bytes_read;
break;
- case DW_FORM_udata:
- attr->u.val = _bfd_safe_read_leb128 (abfd, info_ptr, &bytes_read,
- FALSE, info_ptr_end);
- info_ptr += bytes_read;
- break;
- case DW_FORM_ref1:
- attr->u.val = read_1_byte (abfd, info_ptr, info_ptr_end);
- info_ptr += 1;
- break;
- case DW_FORM_ref2:
- attr->u.val = read_2_bytes (abfd, info_ptr, info_ptr_end);
- info_ptr += 2;
- break;
- case DW_FORM_ref4:
- attr->u.val = read_4_bytes (abfd, info_ptr, info_ptr_end);
- info_ptr += 4;
- break;
- case DW_FORM_ref8:
- attr->u.val = read_8_bytes (abfd, info_ptr, info_ptr_end);
- info_ptr += 8;
- break;
- case DW_FORM_ref_sig8:
- attr->u.val = read_8_bytes (abfd, info_ptr, info_ptr_end);
- info_ptr += 8;
- break;
case DW_FORM_ref_udata:
+ case DW_FORM_udata:
+ case DW_FORM_addrx:
attr->u.val = _bfd_safe_read_leb128 (abfd, info_ptr, &bytes_read,
FALSE, info_ptr_end);
info_ptr += bytes_read;
attr->form = DW_FORM_sdata;
attr->u.sval = implicit_const;
break;
+ case DW_FORM_data16:
+ /* This is really a "constant", but there is no way to store that
+ so pretend it is a 16 byte block instead. */
+ amt = sizeof (struct dwarf_block);
+ blk = (struct dwarf_block *) bfd_alloc (abfd, amt);
+ if (blk == NULL)
+ return NULL;
+ blk->size = 16;
+ info_ptr = read_n_bytes (info_ptr, info_ptr_end, blk);
+ attr->u.blk = blk;
+ break;
+
default:
_bfd_error_handler (_("DWARF error: invalid or unhandled FORM value: %#x"),
form);
struct arange arange;
/* Where the symbol is defined. */
asection * sec;
+ /* The offset of the funcinfo from the start of the unit. */
+ bfd_uint64_t unit_offset;
};
struct lookup_funcinfo
case DW_FORM_udata:
*uintp = attr.u.val;
break;
+
+ case DW_FORM_data16:
+ /* MD5 data is in the attr.blk, but we are ignoring those. */
+ break;
}
}
- if (!callback (table, fe.name, fe.dir, fe.time, fe.size))
- return FALSE;
+ /* Skip the first "zero entry", which is the compilation dir/file. */
+ if (datai != 0)
+ if (!callback (table, fe.name, fe.dir, fe.time, fe.size))
+ return FALSE;
}
*bufp = buf;
(_("DWARF error: mangled line number section"));
bfd_set_error (bfd_error_bad_value);
line_fail:
- if (filename != NULL)
- free (filename);
+ free (filename);
goto fail;
}
break;
filenum = _bfd_safe_read_leb128 (abfd, line_ptr, &bytes_read,
FALSE, line_end);
line_ptr += bytes_read;
- if (filename)
- free (filename);
+ free (filename);
filename = concat_filename (table, filenum);
break;
}
}
}
- if (filename)
- free (filename);
+ free (filename);
}
if (unit->line_offset == 0)
table->sequences = table->sequences->prev_sequence;
free (seq);
}
- if (table->files != NULL)
- free (table->files);
- if (table->dirs != NULL)
- free (table->dirs);
+ free (table->files);
+ free (table->dirs);
return NULL;
}
&file->dwarf_ranges_buffer, &file->dwarf_ranges_size);
}
+/* Read in the .debug_rnglists section for future reference. */
+
+static bfd_boolean
+read_debug_rnglists (struct comp_unit * unit)
+{
+ struct dwarf2_debug *stash = unit->stash;
+ struct dwarf2_debug_file *file = unit->file;
+
+ return read_section (unit->abfd, &stash->debug_sections[debug_rnglists],
+ file->syms, 0,
+ &file->dwarf_rnglists_buffer, &file->dwarf_rnglists_size);
+}
+
/* Function table functions. */
static int
}
static bfd_boolean
-read_rangelist (struct comp_unit *unit, struct arange *arange,
- bfd_uint64_t offset)
+read_ranges (struct comp_unit *unit, struct arange *arange,
+ bfd_uint64_t offset)
{
bfd_byte *ranges_ptr;
bfd_byte *ranges_end;
return TRUE;
}
+static bfd_boolean
+read_rnglists (struct comp_unit *unit, struct arange *arange,
+ bfd_uint64_t offset)
+{
+ bfd_byte *rngs_ptr;
+ bfd_byte *rngs_end;
+ bfd_vma base_address = unit->base_address;
+ bfd_vma low_pc;
+ bfd_vma high_pc;
+ bfd *abfd = unit->abfd;
+
+ if (! unit->file->dwarf_rnglists_buffer)
+ {
+ if (! read_debug_rnglists (unit))
+ return FALSE;
+ }
+
+ rngs_ptr = unit->file->dwarf_rnglists_buffer + offset;
+ if (rngs_ptr < unit->file->dwarf_rnglists_buffer)
+ return FALSE;
+ rngs_end = unit->file->dwarf_rnglists_buffer;
+ rngs_end += unit->file->dwarf_rnglists_size;
+
+ for (;;)
+ {
+ enum dwarf_range_list_entry rlet;
+ unsigned int bytes_read;
+
+ if (rngs_ptr + 1 > rngs_end)
+ return FALSE;
+
+ rlet = read_1_byte (abfd, rngs_ptr, rngs_end);
+ rngs_ptr++;
+
+ switch (rlet)
+ {
+ case DW_RLE_end_of_list:
+ return TRUE;
+
+ case DW_RLE_base_address:
+ if (rngs_ptr + unit->addr_size > rngs_end)
+ return FALSE;
+ base_address = read_address (unit, rngs_ptr, rngs_end);
+ rngs_ptr += unit->addr_size;
+ continue;
+
+ case DW_RLE_start_length:
+ if (rngs_ptr + unit->addr_size > rngs_end)
+ return FALSE;
+ low_pc = read_address (unit, rngs_ptr, rngs_end);
+ rngs_ptr += unit->addr_size;
+ high_pc = low_pc;
+ high_pc += _bfd_safe_read_leb128 (abfd, rngs_ptr, &bytes_read,
+ FALSE, rngs_end);
+ rngs_ptr += bytes_read;
+ break;
+
+ case DW_RLE_offset_pair:
+ low_pc = base_address;
+ low_pc += _bfd_safe_read_leb128 (abfd, rngs_ptr, &bytes_read,
+ FALSE, rngs_end);
+ rngs_ptr += bytes_read;
+ high_pc = base_address;
+ high_pc += _bfd_safe_read_leb128 (abfd, rngs_ptr, &bytes_read,
+ FALSE, rngs_end);
+ rngs_ptr += bytes_read;
+ break;
+
+ case DW_RLE_start_end:
+ if (rngs_ptr + 2 * unit->addr_size > rngs_end)
+ return FALSE;
+ low_pc = read_address (unit, rngs_ptr, rngs_end);
+ rngs_ptr += unit->addr_size;
+ high_pc = read_address (unit, rngs_ptr, rngs_end);
+ rngs_ptr += unit->addr_size;
+ break;
+
+ /* TODO x-variants need .debug_addr support used for split-dwarf. */
+ case DW_RLE_base_addressx:
+ case DW_RLE_startx_endx:
+ case DW_RLE_startx_length:
+ default:
+ return FALSE;
+ }
+
+ if (!arange_add (unit, arange, low_pc, high_pc))
+ return FALSE;
+ }
+}
+
+static bfd_boolean
+read_rangelist (struct comp_unit *unit, struct arange *arange,
+ bfd_uint64_t offset)
+{
+ if (unit->version <= 4)
+ return read_ranges (unit, arange, offset);
+ else
+ return read_rnglists (unit, arange, offset);
+}
+
+static struct funcinfo *
+lookup_func_by_offset (bfd_uint64_t offset, struct funcinfo * table)
+{
+ for (; table != NULL; table = table->prev_func)
+ if (table->unit_offset == offset)
+ return table;
+ return NULL;
+}
+
static struct varinfo *
lookup_var_by_offset (bfd_uint64_t offset, struct varinfo * table)
{
bfd_byte *info_ptr = unit->first_child_die_ptr;
bfd_byte *info_ptr_end = unit->end_ptr;
int nesting_level = 0;
- struct nest_funcinfo {
+ struct nest_funcinfo
+ {
struct funcinfo *func;
} *nested_funcs;
int nested_funcs_size;
return FALSE;
nested_funcs[nesting_level].func = 0;
+ /* PR 27484: We must scan the DIEs twice. The first time we look for
+ function and variable tags and accumulate them into their respective
+ tables. The second time through we process the attributes of the
+ functions/variables and augment the table entries. */
while (nesting_level >= 0)
{
unsigned int abbrev_number, bytes_read, i;
struct abbrev_info *abbrev;
- struct attribute attr;
struct funcinfo *func;
struct varinfo *var;
- bfd_vma low_pc = 0;
- bfd_vma high_pc = 0;
- bfd_boolean high_pc_relative = FALSE;
bfd_uint64_t current_offset;
/* PR 17512: file: 9f405d9d. */
FALSE, info_ptr_end);
info_ptr += bytes_read;
- if (! abbrev_number)
+ if (abbrev_number == 0)
{
nesting_level--;
continue;
goto fail;
func->tag = abbrev->tag;
func->prev_func = unit->function_table;
+ func->unit_offset = current_offset;
unit->function_table = func;
unit->number_of_functions++;
BFD_ASSERT (!unit->cached);
else
{
func = NULL;
- if (abbrev->tag == DW_TAG_variable)
+ if (abbrev->tag == DW_TAG_variable
+ || abbrev->tag == DW_TAG_member)
{
size_t amt = sizeof (struct varinfo);
+
var = (struct varinfo *) bfd_zalloc (abfd, amt);
if (var == NULL)
goto fail;
nested_funcs[nesting_level].func = 0;
}
+ for (i = 0; i < abbrev->num_attrs; ++i)
+ {
+ struct attribute attr;
+
+ info_ptr = read_attribute (&attr, &abbrev->attrs[i],
+ unit, info_ptr, info_ptr_end);
+ if (info_ptr == NULL)
+ goto fail;
+ }
+
+ if (abbrev->has_children)
+ {
+ nesting_level++;
+
+ if (nesting_level >= nested_funcs_size)
+ {
+ struct nest_funcinfo *tmp;
+
+ nested_funcs_size *= 2;
+ tmp = (struct nest_funcinfo *)
+ bfd_realloc (nested_funcs,
+ nested_funcs_size * sizeof (*nested_funcs));
+ if (tmp == NULL)
+ goto fail;
+ nested_funcs = tmp;
+ }
+ nested_funcs[nesting_level].func = 0;
+ }
+ }
+
+ /* This is the second pass over the abbrevs. */
+ info_ptr = unit->first_child_die_ptr;
+ nesting_level = 0;
+
+ while (nesting_level >= 0)
+ {
+ unsigned int abbrev_number, bytes_read, i;
+ struct abbrev_info *abbrev;
+ struct attribute attr;
+ struct funcinfo *func;
+ struct varinfo *var;
+ bfd_vma low_pc = 0;
+ bfd_vma high_pc = 0;
+ bfd_boolean high_pc_relative = FALSE;
+ bfd_uint64_t current_offset;
+
+ /* PR 17512: file: 9f405d9d. */
+ if (info_ptr >= info_ptr_end)
+ goto fail;
+
+ current_offset = info_ptr - unit->info_ptr_unit;
+ abbrev_number = _bfd_safe_read_leb128 (abfd, info_ptr, &bytes_read,
+ FALSE, info_ptr_end);
+ info_ptr += bytes_read;
+
+ if (! abbrev_number)
+ {
+ nesting_level--;
+ continue;
+ }
+
+ abbrev = lookup_abbrev (abbrev_number, unit->abbrevs);
+ /* This should have been handled above. */
+ BFD_ASSERT (abbrev != NULL);
+
+ func = NULL;
+ var = NULL;
+ if (abbrev->tag == DW_TAG_subprogram
+ || abbrev->tag == DW_TAG_entry_point
+ || abbrev->tag == DW_TAG_inlined_subroutine)
+ {
+ func = lookup_func_by_offset (current_offset, unit->function_table);
+ if (func == NULL)
+ goto fail;
+ }
+ else if (abbrev->tag == DW_TAG_variable
+ || abbrev->tag == DW_TAG_member)
+ {
+ var = lookup_var_by_offset (current_offset, unit->variable_table);
+ if (var == NULL)
+ goto fail;
+ }
+
for (i = 0; i < abbrev->num_attrs; ++i)
{
info_ptr = read_attribute (&attr, &abbrev->attrs[i],
{
struct varinfo * spec_var;
- spec_var = lookup_var_by_offset (attr.u.val, unit->variable_table);
+ spec_var = lookup_var_by_offset (attr.u.val,
+ unit->variable_table);
if (spec_var == NULL)
- {
- _bfd_error_handler
- (_("DWARF error: could not find variable specification at offset %lx"),
- (unsigned long) attr.u.val);
+ {
+ _bfd_error_handler (_("DWARF error: could not find "
+ "variable specification "
+ "at offset 0x%lx"),
+ (unsigned long) attr.u.val);
break;
}
if (var->name == NULL)
var->name = spec_var->name;
- if (var->file == NULL)
+ if (var->file == NULL && spec_var->file != NULL)
var->file = strdup (spec_var->file);
if (var->line == 0)
var->line = spec_var->line;
}
}
+ if (abbrev->has_children)
+ nesting_level++;
+
if (high_pc_relative)
high_pc += low_pc;
if (!arange_add (unit, &func->arange, low_pc, high_pc))
goto fail;
}
-
- if (abbrev->has_children)
- {
- nesting_level++;
-
- if (nesting_level >= nested_funcs_size)
- {
- struct nest_funcinfo *tmp;
-
- nested_funcs_size *= 2;
- tmp = (struct nest_funcinfo *)
- bfd_realloc (nested_funcs,
- nested_funcs_size * sizeof (*nested_funcs));
- if (tmp == NULL)
- goto fail;
- nested_funcs = tmp;
- }
- nested_funcs[nesting_level].func = 0;
- }
}
free (nested_funcs);
}
for (msec = abfd->sections; msec != NULL; msec = msec->next)
- if (CONST_STRNEQ (msec->name, GNU_LINKONCE_INFO))
+ if (startswith (msec->name, GNU_LINKONCE_INFO))
return msec;
return NULL;
if (look != NULL && strcmp (msec->name, look) == 0)
return msec;
- if (CONST_STRNEQ (msec->name, GNU_LINKONCE_INFO))
+ if (startswith (msec->name, GNU_LINKONCE_INFO))
return msec;
}
continue;
is_debug_info = (strcmp (sect->name, debug_info_name) == 0
- || CONST_STRNEQ (sect->name, GNU_LINKONCE_INFO));
+ || startswith (sect->name, GNU_LINKONCE_INFO));
if (!((sect->flags & SEC_ALLOC) != 0 && abfd == orig_bfd)
&& !is_debug_info)
continue;
is_debug_info = (strcmp (sect->name, debug_info_name) == 0
- || CONST_STRNEQ (sect->name, GNU_LINKONCE_INFO));
+ || startswith (sect->name, GNU_LINKONCE_INFO));
if (!((sect->flags & SEC_ALLOC) != 0 && abfd == orig_bfd)
&& !is_debug_info)
free (each->line_table->dirs);
}
- if (each->lookup_funcinfo_table)
- {
- free (each->lookup_funcinfo_table);
- each->lookup_funcinfo_table = NULL;
- }
+ free (each->lookup_funcinfo_table);
+ each->lookup_funcinfo_table = NULL;
while (function_table)
{
- if (function_table->file)
- {
- free (function_table->file);
- function_table->file = NULL;
- }
- if (function_table->caller_file)
- {
- free (function_table->caller_file);
- function_table->caller_file = NULL;
- }
+ free (function_table->file);
+ function_table->file = NULL;
+ free (function_table->caller_file);
+ function_table->caller_file = NULL;
function_table = function_table->prev_func;
}
while (variable_table)
{
- if (variable_table->file)
- {
- free (variable_table->file);
- variable_table->file = NULL;
- }
+ free (variable_table->file);
+ variable_table->file = NULL;
variable_table = variable_table->prev_var;
}
}