bfd: Add Support for DW_FORM_strx* and DW_FORM_addrx*

This commit is contained in:
Potharla, Rupesh
2022-05-24 00:01:49 +00:00
committed by Alan Modra
parent 9e2bb0cb5e
commit f67741e172

@ -189,6 +189,18 @@ struct dwarf2_debug_file
/* Length of the loaded .debug_str section. */ /* Length of the loaded .debug_str section. */
bfd_size_type dwarf_str_size; bfd_size_type dwarf_str_size;
/* Pointer to the .debug_str_offsets section loaded into memory. */
bfd_byte *dwarf_str_offsets_buffer;
/* Length of the loaded .debug_str_offsets section. */
bfd_size_type dwarf_str_offsets_size;
/* Pointer to the .debug_addr section loaded into memory. */
bfd_byte *dwarf_addr_buffer;
/* Length of the loaded .debug_addr section. */
bfd_size_type dwarf_addr_size;
/* Pointer to the .debug_line_str section loaded into memory. */ /* Pointer to the .debug_line_str section loaded into memory. */
bfd_byte *dwarf_line_str_buffer; bfd_byte *dwarf_line_str_buffer;
@ -382,6 +394,12 @@ struct comp_unit
/* Used when iterating over trie leaves to know which units we have /* Used when iterating over trie leaves to know which units we have
already seen in this iteration. */ already seen in this iteration. */
bool mark; bool mark;
/* Base address of debug_addr section. */
size_t dwarf_addr_offset;
/* Base address of string offset table. */
size_t dwarf_str_offset;
}; };
/* This data structure holds the information of an abbrev. */ /* This data structure holds the information of an abbrev. */
@ -424,6 +442,8 @@ const struct dwarf_debug_section dwarf_debug_sections[] =
{ ".debug_static_vars", ".zdebug_static_vars" }, { ".debug_static_vars", ".zdebug_static_vars" },
{ ".debug_str", ".zdebug_str", }, { ".debug_str", ".zdebug_str", },
{ ".debug_str", ".zdebug_str", }, { ".debug_str", ".zdebug_str", },
{ ".debug_str_offsets", ".zdebug_str_offsets", },
{ ".debug_addr", ".zdebug_addr", },
{ ".debug_line_str", ".zdebug_line_str", }, { ".debug_line_str", ".zdebug_line_str", },
{ ".debug_types", ".zdebug_types" }, { ".debug_types", ".zdebug_types" },
/* GNU DWARF 1 extensions */ /* GNU DWARF 1 extensions */
@ -458,6 +478,8 @@ enum dwarf_debug_section_enum
debug_static_vars, debug_static_vars,
debug_str, debug_str,
debug_str_alt, debug_str_alt,
debug_str_offsets,
debug_addr,
debug_line_str, debug_line_str,
debug_types, debug_types,
debug_sfnames, debug_sfnames,
@ -1307,12 +1329,92 @@ is_int_form (const struct attribute *attr)
} }
} }
static const char * /* Returns true if the form is strx[1-4]. */
read_indexed_string (bfd_uint64_t idx ATTRIBUTE_UNUSED,
struct comp_unit * unit ATTRIBUTE_UNUSED) static inline bool
is_strx_form (enum dwarf_form form)
{ {
/* FIXME: Add support for indexed strings. */ return (form == DW_FORM_strx
return "<indexed strings not yet supported>"; || form == DW_FORM_strx1
|| form == DW_FORM_strx2
|| form == DW_FORM_strx3
|| form == DW_FORM_strx4);
}
/* Return true if the form is addrx[1-4]. */
static inline bool
is_addrx_form (enum dwarf_form form)
{
return (form == DW_FORM_addrx
|| form == DW_FORM_addrx1
|| form == DW_FORM_addrx2
|| form == DW_FORM_addrx3
|| form == DW_FORM_addrx4);
}
/* Returns the address in .debug_addr section using DW_AT_addr_base.
Used to implement DW_FORM_addrx*. */
static bfd_vma
read_indexed_address (bfd_uint64_t idx,
struct comp_unit *unit)
{
struct dwarf2_debug *stash = unit->stash;
struct dwarf2_debug_file *file = unit->file;
size_t addr_base = unit->dwarf_addr_offset;
bfd_byte *info_ptr;
if (stash == NULL)
return 0;
if (!read_section (unit->abfd, &stash->debug_sections[debug_addr],
file->syms, 0,
&file->dwarf_addr_buffer, &file->dwarf_addr_size))
return 0;
info_ptr = file->dwarf_addr_buffer + addr_base + idx * unit->offset_size;
if (unit->offset_size == 4)
return bfd_get_32 (unit->abfd, info_ptr);
else
return bfd_get_64 (unit->abfd, info_ptr);
}
/* Returns the string using DW_AT_str_offsets_base.
Used to implement DW_FORM_strx*. */
static const char *
read_indexed_string (bfd_uint64_t idx,
struct comp_unit *unit)
{
struct dwarf2_debug *stash = unit->stash;
struct dwarf2_debug_file *file = unit->file;
bfd_byte *info_ptr;
unsigned long str_offset;
if (stash == NULL)
return NULL;
if (!read_section (unit->abfd, &stash->debug_sections[debug_str],
file->syms, 0,
&file->dwarf_str_buffer, &file->dwarf_str_size))
return NULL;
if (!read_section (unit->abfd, &stash->debug_sections[debug_str_offsets],
file->syms, 0,
&file->dwarf_str_offsets_buffer,
&file->dwarf_str_offsets_size))
return NULL;
info_ptr = (file->dwarf_str_offsets_buffer
+ unit->dwarf_str_offset
+ idx * unit->offset_size);
if (unit->offset_size == 4)
str_offset = bfd_get_32 (unit->abfd, info_ptr);
else
str_offset = bfd_get_64 (unit->abfd, info_ptr);
return (const char *) file->dwarf_str_buffer + str_offset;
} }
/* Read and fill in the value of attribute ATTR as described by FORM. /* Read and fill in the value of attribute ATTR as described by FORM.
@ -1381,21 +1483,37 @@ read_attribute_value (struct attribute * attr,
case DW_FORM_ref1: case DW_FORM_ref1:
case DW_FORM_flag: case DW_FORM_flag:
case DW_FORM_data1: case DW_FORM_data1:
case DW_FORM_addrx1:
attr->u.val = read_1_byte (abfd, &info_ptr, info_ptr_end); attr->u.val = read_1_byte (abfd, &info_ptr, info_ptr_end);
break; break;
case DW_FORM_addrx1:
attr->u.val = read_1_byte (abfd, &info_ptr, info_ptr_end);
/* dwarf_addr_offset value 0 indicates the attribute DW_AT_addr_base
is not yet read. */
if (unit->dwarf_addr_offset != 0)
attr->u.val = read_indexed_address (attr->u.val, unit);
break;
case DW_FORM_data2: case DW_FORM_data2:
case DW_FORM_addrx2:
case DW_FORM_ref2: case DW_FORM_ref2:
attr->u.val = read_2_bytes (abfd, &info_ptr, info_ptr_end); attr->u.val = read_2_bytes (abfd, &info_ptr, info_ptr_end);
break; break;
case DW_FORM_addrx2:
attr->u.val = read_2_bytes (abfd, &info_ptr, info_ptr_end);
if (unit->dwarf_addr_offset != 0)
attr->u.val = read_indexed_address (attr->u.val, unit);
break;
case DW_FORM_addrx3: case DW_FORM_addrx3:
attr->u.val = read_3_bytes (abfd, &info_ptr, info_ptr_end); attr->u.val = read_3_bytes (abfd, &info_ptr, info_ptr_end);
if (unit->dwarf_addr_offset != 0)
attr->u.val = read_indexed_address(attr->u.val, unit);
break; break;
case DW_FORM_ref4: case DW_FORM_ref4:
case DW_FORM_data4: case DW_FORM_data4:
attr->u.val = read_4_bytes (abfd, &info_ptr, info_ptr_end);
break;
case DW_FORM_addrx4: case DW_FORM_addrx4:
attr->u.val = read_4_bytes (abfd, &info_ptr, info_ptr_end); attr->u.val = read_4_bytes (abfd, &info_ptr, info_ptr_end);
if (unit->dwarf_addr_offset != 0)
attr->u.val = read_indexed_address (attr->u.val, unit);
break; break;
case DW_FORM_data8: case DW_FORM_data8:
case DW_FORM_ref8: case DW_FORM_ref8:
@ -1416,23 +1534,30 @@ read_attribute_value (struct attribute * attr,
break; break;
case DW_FORM_strx1: case DW_FORM_strx1:
attr->u.val = read_1_byte (abfd, &info_ptr, info_ptr_end); attr->u.val = read_1_byte (abfd, &info_ptr, info_ptr_end);
/* dwarf_str_offset value 0 indicates the attribute DW_AT_str_offsets_base
is not yet read. */
if (unit->dwarf_str_offset != 0)
attr->u.str = (char *) read_indexed_string (attr->u.val, unit); attr->u.str = (char *) read_indexed_string (attr->u.val, unit);
break; break;
case DW_FORM_strx2: case DW_FORM_strx2:
attr->u.val = read_2_bytes (abfd, &info_ptr, info_ptr_end); attr->u.val = read_2_bytes (abfd, &info_ptr, info_ptr_end);
if (unit->dwarf_str_offset != 0)
attr->u.str = (char *) read_indexed_string (attr->u.val, unit); attr->u.str = (char *) read_indexed_string (attr->u.val, unit);
break; break;
case DW_FORM_strx3: case DW_FORM_strx3:
attr->u.val = read_3_bytes (abfd, &info_ptr, info_ptr_end); attr->u.val = read_3_bytes (abfd, &info_ptr, info_ptr_end);
if (unit->dwarf_str_offset != 0)
attr->u.str = (char *) read_indexed_string (attr->u.val, unit); attr->u.str = (char *) read_indexed_string (attr->u.val, unit);
break; break;
case DW_FORM_strx4: case DW_FORM_strx4:
attr->u.val = read_4_bytes (abfd, &info_ptr, info_ptr_end); attr->u.val = read_4_bytes (abfd, &info_ptr, info_ptr_end);
if (unit->dwarf_str_offset != 0)
attr->u.str = (char *) read_indexed_string (attr->u.val, unit); attr->u.str = (char *) read_indexed_string (attr->u.val, unit);
break; break;
case DW_FORM_strx: case DW_FORM_strx:
attr->u.val = _bfd_safe_read_leb128 (abfd, &info_ptr, attr->u.val = _bfd_safe_read_leb128 (abfd, &info_ptr,
false, info_ptr_end); false, info_ptr_end);
if (unit->dwarf_str_offset != 0)
attr->u.str = (char *) read_indexed_string (attr->u.val, unit); attr->u.str = (char *) read_indexed_string (attr->u.val, unit);
break; break;
case DW_FORM_exprloc: case DW_FORM_exprloc:
@ -1455,9 +1580,14 @@ read_attribute_value (struct attribute * attr,
break; break;
case DW_FORM_ref_udata: case DW_FORM_ref_udata:
case DW_FORM_udata: case DW_FORM_udata:
attr->u.val = _bfd_safe_read_leb128 (abfd, &info_ptr,
false, info_ptr_end);
break;
case DW_FORM_addrx: case DW_FORM_addrx:
attr->u.val = _bfd_safe_read_leb128 (abfd, &info_ptr, attr->u.val = _bfd_safe_read_leb128 (abfd, &info_ptr,
false, info_ptr_end); false, info_ptr_end);
if (unit->dwarf_addr_offset != 0)
attr->u.val = read_indexed_address (attr->u.val, unit);
break; break;
case DW_FORM_indirect: case DW_FORM_indirect:
form = _bfd_safe_read_leb128 (abfd, &info_ptr, form = _bfd_safe_read_leb128 (abfd, &info_ptr,
@ -2396,6 +2526,11 @@ read_formatted_entries (struct comp_unit *unit, bfd_byte **bufp,
{ {
case DW_FORM_string: case DW_FORM_string:
case DW_FORM_line_strp: case DW_FORM_line_strp:
case DW_FORM_strx:
case DW_FORM_strx1:
case DW_FORM_strx2:
case DW_FORM_strx3:
case DW_FORM_strx4:
*stringp = attr.u.str; *stringp = attr.u.str;
break; break;
@ -4031,6 +4166,80 @@ scan_unit_for_symbols (struct comp_unit *unit)
return false; return false;
} }
/* Read the attributes of the form strx and addrx. */
static void
reread_attribute (struct comp_unit *unit,
struct attribute *attr,
bfd_vma *low_pc,
bfd_vma *high_pc,
bool *high_pc_relative,
bool compunit)
{
if (is_strx_form (attr->form))
attr->u.str = (char *) read_indexed_string (attr->u.val, unit);
if (is_addrx_form (attr->form))
attr->u.val = read_indexed_address (attr->u.val, unit);
switch (attr->name)
{
case DW_AT_stmt_list:
unit->stmtlist = 1;
unit->line_offset = attr->u.val;
break;
case DW_AT_name:
if (is_str_form (attr))
unit->name = attr->u.str;
break;
case DW_AT_low_pc:
*low_pc = attr->u.val;
if (compunit)
unit->base_address = *low_pc;
break;
case DW_AT_high_pc:
*high_pc = attr->u.val;
*high_pc_relative = attr->form != DW_FORM_addr;
break;
case DW_AT_ranges:
if (!read_rangelist (unit, &unit->arange,
&unit->file->trie_root, attr->u.val))
return;
break;
case DW_AT_comp_dir:
{
char *comp_dir = attr->u.str;
if (!is_str_form (attr))
{
_bfd_error_handler
(_("DWARF error: DW_AT_comp_dir attribute encountered "
"with a non-string form"));
comp_dir = NULL;
}
if (comp_dir)
{
char *cp = strchr (comp_dir, ':');
if (cp && cp != comp_dir && cp[-1] == '.' && cp[1] == '/')
comp_dir = cp + 1;
}
unit->comp_dir = comp_dir;
break;
}
case DW_AT_language:
unit->lang = attr->u.val;
default:
break;
}
}
/* Parse a DWARF2 compilation unit starting at INFO_PTR. UNIT_LENGTH /* Parse a DWARF2 compilation unit starting at INFO_PTR. UNIT_LENGTH
includes the compilation unit header that proceeds the DIE's, but includes the compilation unit header that proceeds the DIE's, but
does not include the length field that precedes each compilation does not include the length field that precedes each compilation
@ -4064,6 +4273,10 @@ parse_comp_unit (struct dwarf2_debug *stash,
bfd *abfd = file->bfd_ptr; bfd *abfd = file->bfd_ptr;
bool high_pc_relative = false; bool high_pc_relative = false;
enum dwarf_unit_type unit_type; enum dwarf_unit_type unit_type;
struct attribute *str_addrp = NULL;
size_t str_count = 0;
size_t str_alloc = 0;
bool compunit_flag = false;
version = read_2_bytes (abfd, &info_ptr, end_ptr); version = read_2_bytes (abfd, &info_ptr, end_ptr);
if (version < 2 || version > 5) if (version < 2 || version > 5)
@ -4168,11 +4381,33 @@ parse_comp_unit (struct dwarf2_debug *stash,
unit->file = file; unit->file = file;
unit->info_ptr_unit = info_ptr_unit; unit->info_ptr_unit = info_ptr_unit;
if (abbrev->tag == DW_TAG_compile_unit)
compunit_flag = true;
for (i = 0; i < abbrev->num_attrs; ++i) for (i = 0; i < abbrev->num_attrs; ++i)
{ {
info_ptr = read_attribute (&attr, &abbrev->attrs[i], unit, info_ptr, end_ptr); info_ptr = read_attribute (&attr, &abbrev->attrs[i], unit, info_ptr, end_ptr);
if (info_ptr == NULL) if (info_ptr == NULL)
return NULL; goto err_exit;
/* Identify attributes of the form strx* and addrx* which come before
DW_AT_str_offsets_base and DW_AT_addr_base respectively in the CU.
Store the attributes in an array and process them later. */
if ((unit->dwarf_str_offset == 0 && is_strx_form (attr.form))
|| (unit->dwarf_addr_offset == 0 && is_addrx_form (attr.form)))
{
if (str_count <= str_alloc)
{
str_alloc = 2 * str_alloc + 200;
str_addrp = bfd_realloc (str_addrp,
str_alloc * sizeof (*str_addrp));
if (str_addrp == NULL)
goto err_exit;
}
str_addrp[str_count] = attr;
str_count++;
continue;
}
/* Store the data if it is of an attribute we want to keep in a /* Store the data if it is of an attribute we want to keep in a
partial symbol table. */ partial symbol table. */
@ -4198,7 +4433,7 @@ parse_comp_unit (struct dwarf2_debug *stash,
/* If the compilation unit DIE has a DW_AT_low_pc attribute, /* If the compilation unit DIE has a DW_AT_low_pc attribute,
this is the base address to use when reading location this is the base address to use when reading location
lists or range lists. */ lists or range lists. */
if (abbrev->tag == DW_TAG_compile_unit) if (compunit_flag)
unit->base_address = low_pc; unit->base_address = low_pc;
} }
break; break;
@ -4215,7 +4450,7 @@ parse_comp_unit (struct dwarf2_debug *stash,
if (is_int_form (&attr) if (is_int_form (&attr)
&& !read_rangelist (unit, &unit->arange, && !read_rangelist (unit, &unit->arange,
&unit->file->trie_root, attr.u.val)) &unit->file->trie_root, attr.u.val))
return NULL; goto err_exit;
break; break;
case DW_AT_comp_dir: case DW_AT_comp_dir:
@ -4248,21 +4483,40 @@ parse_comp_unit (struct dwarf2_debug *stash,
unit->lang = attr.u.val; unit->lang = attr.u.val;
break; break;
case DW_AT_addr_base:
unit->dwarf_addr_offset = attr.u.val;
break;
case DW_AT_str_offsets_base:
unit->dwarf_str_offset = attr.u.val;
break;
default: default:
break; break;
} }
} }
for (i = 0; i < str_count; ++i)
reread_attribute (unit, &str_addrp[i], &low_pc, &high_pc,
&high_pc_relative, compunit_flag);
if (high_pc_relative) if (high_pc_relative)
high_pc += low_pc; high_pc += low_pc;
if (high_pc != 0) if (high_pc != 0)
{ {
if (!arange_add (unit, &unit->arange, &unit->file->trie_root, if (!arange_add (unit, &unit->arange, &unit->file->trie_root,
low_pc, high_pc)) low_pc, high_pc))
return NULL; goto err_exit;
} }
unit->first_child_die_ptr = info_ptr; unit->first_child_die_ptr = info_ptr;
free (str_addrp);
return unit; return unit;
err_exit:
free (str_addrp);
return NULL;
} }
/* Return TRUE if UNIT may contain the address given by ADDR. When /* Return TRUE if UNIT may contain the address given by ADDR. When