mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-12-27 01:28:46 +00:00
[gdb/symtab] Support .debug_line with DW_FORM_line_strp
I noticed a new gcc option -gdwarf64 and tried it out (using gcc 11.2.1).
With a test-case hello.c:
...
int
main (void)
{
printf ("hello\n");
return 0;
}
...
compiled like this:
...
$ gcc -g -gdwarf64 ~/hello.c
...
I ran into:
...
$ gdb -q -batch a.out
DW_FORM_line_strp pointing outside of .debug_line_str section \
[in module a.out]
...
Debugging gdb revealed that the string offset is:
...
(gdb) up
objfile=0x182ab70, str_offset=1378684502312,
form_name=0xeae9b5 "DW_FORM_line_strp")
at src/gdb/dwarf2/section.c:208
208 error (_("%s pointing outside of %s section [in module %s]"),
(gdb) p /x str_offset
$1 = 0x14100000128
(gdb)
...
which is read when parsing a .debug_line entry at 0x1e0.
Looking with readelf at the 0x1e0 entry, we have:
...
The Directory Table (offset 0x202, lines 2, columns 1):
Entry Name
0 (indirect line string, offset: 0x128): /data/gdb_versions/devel
1 (indirect line string, offset: 0x141): /home/vries
...
which in a hexdump looks like:
...
0x00000200 1f022801 00004101 00000201 1f020f02
...
What happens is the following:
- readelf interprets the DW_FORM_line_strp reference to .debug_line_str as
a 4 byte value, and sees entries 0x00000128 and 0x00000141.
- gdb instead interprets it as an 8 byte value, and sees as first entry
0x0000014100000128, which is too big so it bails out.
AFAIU, gdb is wrong. It assumes DW_FORM_line_strp is 8 bytes on the basis
that the corresponding CU is 64-bit DWARF. However, the .debug_line
contribution has it's own initial_length field, and encodes there that it's
32-bit DWARF.
Fix this by using the correct offset size for DW_FORM_line_strp references
in .debug_line.
Note: the described test-case does trigger this complaint (both with and
without this patch):
...
$ gdb -q -batch -iex "set complaints 10" a.out
During symbol reading: intermixed 32-bit and 64-bit DWARF sections
...
The reason that the CU has 64-bit dwarf is because -gdwarf64 was passed to
gcc. The reason that the .debug_line entry has 32-bit dwarf is because that's
what gas generates. Perhaps this is complaint-worthy, but I don't think it
is wrong.
Tested on x86_64-linux, using native and target board dwarf64.exp.
This commit is contained in:
@@ -137,7 +137,7 @@ read_checked_initial_length_and_offset (bfd *abfd, const gdb_byte *buf,
|
||||
static void
|
||||
read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
|
||||
const gdb_byte **bufp, struct line_header *lh,
|
||||
const struct comp_unit_head *cu_header,
|
||||
unsigned int offset_size,
|
||||
void (*callback) (struct line_header *lh,
|
||||
const char *name,
|
||||
dir_index d_index,
|
||||
@@ -187,9 +187,12 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd,
|
||||
break;
|
||||
|
||||
case DW_FORM_line_strp:
|
||||
string.emplace
|
||||
(per_objfile->read_line_string (buf, cu_header, &bytes_read));
|
||||
buf += bytes_read;
|
||||
{
|
||||
const char *str
|
||||
= per_objfile->read_line_string (buf, offset_size);
|
||||
string.emplace (str);
|
||||
buf += offset_size;
|
||||
}
|
||||
break;
|
||||
|
||||
case DW_FORM_data1:
|
||||
@@ -372,7 +375,7 @@ dwarf_decode_line_header (sect_offset sect_off, bool is_dwz,
|
||||
{
|
||||
/* Read directory table. */
|
||||
read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
|
||||
cu_header,
|
||||
offset_size,
|
||||
[] (struct line_header *header, const char *name,
|
||||
dir_index d_index, unsigned int mod_time,
|
||||
unsigned int length)
|
||||
@@ -382,7 +385,7 @@ dwarf_decode_line_header (sect_offset sect_off, bool is_dwz,
|
||||
|
||||
/* Read file name table. */
|
||||
read_formatted_entries (per_objfile, abfd, &line_ptr, lh.get (),
|
||||
cu_header,
|
||||
offset_size,
|
||||
[] (struct line_header *header, const char *name,
|
||||
dir_index d_index, unsigned int mod_time,
|
||||
unsigned int length)
|
||||
|
||||
@@ -20304,6 +20304,18 @@ read_indirect_string (dwarf2_per_objfile *per_objfile, bfd *abfd,
|
||||
|
||||
/* See read.h. */
|
||||
|
||||
const char *
|
||||
dwarf2_per_objfile::read_line_string (const gdb_byte *buf,
|
||||
unsigned int offset_size)
|
||||
{
|
||||
bfd *abfd = objfile->obfd;
|
||||
ULONGEST str_offset = read_offset (abfd, buf, offset_size);
|
||||
|
||||
return per_bfd->line_str.read_string (objfile, str_offset, "DW_FORM_line_strp");
|
||||
}
|
||||
|
||||
/* See read.h. */
|
||||
|
||||
const char *
|
||||
dwarf2_per_objfile::read_line_string (const gdb_byte *buf,
|
||||
const struct comp_unit_head *cu_header,
|
||||
|
||||
@@ -517,6 +517,11 @@ struct dwarf2_per_objfile
|
||||
const struct comp_unit_head *cu_header,
|
||||
unsigned int *bytes_read_ptr);
|
||||
|
||||
/* Return pointer to string at .debug_line_str offset as read from BUF.
|
||||
The offset_size is OFFSET_SIZE. */
|
||||
const char *read_line_string (const gdb_byte *buf,
|
||||
unsigned int offset_size);
|
||||
|
||||
/* Return true if the symtab corresponding to PER_CU has been set,
|
||||
false otherwise. */
|
||||
bool symtab_set_p (const dwarf2_per_cu_data *per_cu) const;
|
||||
|
||||
@@ -37,18 +37,19 @@ proc line_for { l } {
|
||||
}
|
||||
|
||||
# Execute test.
|
||||
proc test_1 { _cv _cdw64 _lv _ldw64 } {
|
||||
proc test_1 { _cv _cdw64 _lv _ldw64 {_string_form ""}} {
|
||||
global srcfile srcfile2 testfile
|
||||
global cv cdw64 lv ldw64
|
||||
global cv cdw64 lv ldw64 string_form
|
||||
set cv $_cv
|
||||
set cdw64 $_cdw64
|
||||
set lv $_lv
|
||||
set ldw64 $_ldw64
|
||||
set string_form $_string_form
|
||||
|
||||
set asm_file [standard_output_file $srcfile2]
|
||||
Dwarf::assemble $asm_file {
|
||||
declare_labels Llines
|
||||
global srcdir subdir srcfile cv cdw64 lv ldw64
|
||||
global srcdir subdir srcfile cv cdw64 lv ldw64 string_form
|
||||
global func_info_vars
|
||||
foreach var $func_info_vars {
|
||||
global $var
|
||||
@@ -75,7 +76,7 @@ proc test_1 { _cv _cdw64 _lv _ldw64 } {
|
||||
}
|
||||
}
|
||||
|
||||
lines [list version $lv is_64 $ldw64] Llines {
|
||||
lines [list version $lv is_64 $ldw64 string_form $string_form] Llines {
|
||||
include_dir "${srcdir}/${subdir}"
|
||||
file_name "$srcfile" 1
|
||||
|
||||
@@ -121,12 +122,18 @@ proc test_1 { _cv _cdw64 _lv _ldw64 } {
|
||||
|
||||
|
||||
# Add unique test prefix.
|
||||
proc test { cv cdw64 lv ldw64 } {
|
||||
proc test { cv cdw64 lv ldw64 {string_form ""}} {
|
||||
with_test_prefix cv=$cv {
|
||||
with_test_prefix cdw=[expr $cdw64 ? 64 : 32] {
|
||||
with_test_prefix lv=$lv {
|
||||
with_test_prefix ldw=[expr $ldw64 ? 64 : 32] {
|
||||
test_1 $cv $cdw64 $lv $ldw64
|
||||
if { $string_form == "" } {
|
||||
test_1 $cv $cdw64 $lv $ldw64
|
||||
} else {
|
||||
with_test_prefix string_form=$string_form {
|
||||
test_1 $cv $cdw64 $lv $ldw64 $string_form
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -157,6 +164,7 @@ for { set cv $cv_low } { $cv <= $cv_high } { incr cv } {
|
||||
|
||||
foreach cdw64 { 0 1 } {
|
||||
foreach ldw64 { 0 1 } {
|
||||
test 5 $cdw64 5 $ldw64
|
||||
test 5 $cdw64 5 $ldw64 string
|
||||
test 5 $cdw64 5 $ldw64 line_strp
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2185,9 +2185,11 @@ namespace eval Dwarf {
|
||||
variable _line_saw_program
|
||||
variable _line_header_end_label
|
||||
variable _line_unit_version
|
||||
variable _line_is_64
|
||||
variable _line_string_form
|
||||
|
||||
# Establish the defaults.
|
||||
set is_64 0
|
||||
set _line_is_64 0
|
||||
set _line_unit_version 4
|
||||
set _unit_addr_size default
|
||||
set _line_saw_program 0
|
||||
@@ -2197,14 +2199,17 @@ namespace eval Dwarf {
|
||||
set _line_header_finalized 0
|
||||
set _default_is_stmt 1
|
||||
set _seg_sel_size 0
|
||||
#set _line_string_form string
|
||||
set _line_string_form line_strp
|
||||
|
||||
foreach { name value } $options {
|
||||
switch -exact -- $name {
|
||||
is_64 { set is_64 $value }
|
||||
is_64 { set _line_is_64 $value }
|
||||
version { set _line_unit_version $value }
|
||||
addr_size { set _unit_addr_size $value }
|
||||
seg_sel_size { set _seg_sel_size $value }
|
||||
default_is_stmt { set _default_is_stmt $value }
|
||||
string_form { set $_line_string_form $value }
|
||||
default { error "unknown option $name" }
|
||||
}
|
||||
}
|
||||
@@ -2231,7 +2236,7 @@ namespace eval Dwarf {
|
||||
set header_len_label [_compute_label "line${_line_count}_header_start"]
|
||||
set _line_header_end_label [_compute_label "line${_line_count}_header_end"]
|
||||
|
||||
if {$is_64} {
|
||||
if {$_line_is_64} {
|
||||
_op .4byte 0xffffffff
|
||||
_op .8byte "$unit_end_label - $unit_len_label" "unit_length"
|
||||
} else {
|
||||
@@ -2248,7 +2253,7 @@ namespace eval Dwarf {
|
||||
_op .byte $_seg_sel_size "seg_sel_size"
|
||||
}
|
||||
|
||||
if {$is_64} {
|
||||
if {$_line_is_64} {
|
||||
_op .8byte "$_line_header_end_label - $header_len_label" "header_length"
|
||||
} else {
|
||||
_op .4byte "$_line_header_end_label - $header_len_label" "header_length"
|
||||
@@ -2306,12 +2311,22 @@ namespace eval Dwarf {
|
||||
variable _line_file_names
|
||||
|
||||
variable _line_unit_version
|
||||
variable _line_is_64
|
||||
variable _line_string_form
|
||||
if { $_line_unit_version >= 5 } {
|
||||
_op .byte 1 "directory_entry_format_count"
|
||||
_op .uleb128 1 \
|
||||
"directory_entry_format (content type code: DW_LNCT_path)"
|
||||
_op .uleb128 0x08 \
|
||||
"directory_entry_format (form: DW_FORM_string)"
|
||||
switch $_line_string_form {
|
||||
string {
|
||||
_op .uleb128 0x08 \
|
||||
"directory_entry_format (form: DW_FORM_string)"
|
||||
}
|
||||
line_strp {
|
||||
_op .uleb128 0x1f \
|
||||
"directory_entry_format (form: DW_FORM_line_strp)"
|
||||
}
|
||||
}
|
||||
|
||||
set nr_dirs [llength $_line_include_dirs]
|
||||
# For entry 0.
|
||||
@@ -2324,14 +2339,38 @@ namespace eval Dwarf {
|
||||
[concat [list $dirname] $_line_include_dirs]
|
||||
|
||||
foreach dirname $_line_include_dirs {
|
||||
_op .ascii [_quote $dirname]
|
||||
switch $_line_string_form {
|
||||
string {
|
||||
_op .ascii [_quote $dirname]
|
||||
}
|
||||
line_strp {
|
||||
declare_labels string_ptr
|
||||
_defer_output .debug_line_str {
|
||||
string_ptr:
|
||||
_op .ascii [_quote $dirname]
|
||||
}
|
||||
if { $_line_is_64 } {
|
||||
_op .8byte $string_ptr
|
||||
} else {
|
||||
_op .4byte $string_ptr
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_op .byte 2 "file_name_entry_format_count"
|
||||
_op .uleb128 1 \
|
||||
"file_name_entry_format (content type code: DW_LNCT_path)"
|
||||
_op .uleb128 0x08 \
|
||||
"file_name_entry_format (form: DW_FORM_string)"
|
||||
switch $_line_string_form {
|
||||
string {
|
||||
_op .uleb128 0x08 \
|
||||
"directory_entry_format (form: DW_FORM_string)"
|
||||
}
|
||||
line_strp {
|
||||
_op .uleb128 0x1f \
|
||||
"directory_entry_format (form: DW_FORM_line_strp)"
|
||||
}
|
||||
}
|
||||
_op .uleb128 2 \
|
||||
"file_name_entry_format (content type code: DW_LNCT_directory_index)"
|
||||
_op .uleb128 0x0f \
|
||||
@@ -2349,7 +2388,23 @@ namespace eval Dwarf {
|
||||
[concat [list $filename $diridx] $_line_file_names]
|
||||
|
||||
foreach { filename diridx } $_line_file_names {
|
||||
_op .ascii [_quote $filename]
|
||||
switch $_line_string_form {
|
||||
string {
|
||||
_op .ascii [_quote $filename]
|
||||
}
|
||||
line_strp {
|
||||
declare_labels string_ptr
|
||||
_defer_output .debug_line_str {
|
||||
string_ptr:
|
||||
_op .ascii [_quote $filename]
|
||||
}
|
||||
if { $_line_is_64 } {
|
||||
_op .8byte $string_ptr
|
||||
} else {
|
||||
_op .4byte $string_ptr
|
||||
}
|
||||
}
|
||||
}
|
||||
_op .uleb128 $diridx
|
||||
}
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user