Re: [patch] Parse DW_AT_ranges into PSYMTABS (for childless CU, for vDSO32)

Jan Kratochvil
2007-10-09T18:54:50Z

[-- Attachment #1: Type: text/plain, Size: 1585 bytes --]

On Tue, 09 Oct 2007 20:17:01 +0200, Daniel Jacobowitz wrote:
> On Tue, Oct 09, 2007 at 08:02:46PM +0200, Jan Kratochvil wrote:
> > DW_TAG_compile_unit has no children and it has neither DW_AT_low_pc nor
> > DW_AT_high_pc but it has DW_AT_ranges instead. So far DW_AT_ranges was not
> > parsed into PSYMTABS and so this file was ignored during the debug info search
> > later. Reasons for the discontinuous vDSO32 layout/hack are in the Linux
> > kernel sources.
>
> I'm curious, why is this? I couldn't find them in my kernel tree.

linux-2.6.22/arch/x86_64/ia32/vsyscall.lds
/* This is an 32bit object and we cannot easily get the offsets
into the 64bit kernel. Just hardcode them here. This assumes
that all the stubs don't need more than 0x100 bytes. */
. = VSYSCALL_BASE + 0x500;

> > +# Avoid `-lm' from `lib/ada.exp' as it would fail with out `-nostdlib'.
> > +# Provide BOARD for SET_BOARD_INFO.
> > +set board [target_info name]
> > +set_board_info mathlib ""
>
> Actually this is from DejaGNU. And it's global state, so you have
> just made other tests behave differently depending on whether you run
> this one first or not.

Overlooked it, thanks. Saved/restored it in the attached updated patch.
I understand it is very ugly hack, it would be probably more appropriate to fix
`lib/ada.exp' instead.

> I bet you don't need a custom linker script and -nostdlib to test
> this. Gas must be generating the DW_AT_ranges itself regardless of
> whether there's a gap.

Which one, .space, .org +x? These all fill the space with flesh bytes.

Regards,
Jan

[-- Attachment #2: gdb-6.6-cu-ranges.patch --]
[-- Type: text/plain, Size: 13755 bytes --]

2007-10-09 Jan Kratochvil <jan.kratochvil@redhat.com>

* dwarf2read.c (dwarf2_get_pc_bounds): Moved the `DW_AT_ranges' parsing
code with its variables OBJFILE, CU_HEADER and OBFD into ...
(dwarf2_ranges_read): ... a new function.
(read_partial_die): Implemented the parsing of `DW_AT_ranges'.

2007-10-09 Jan Kratochvil <jan.kratochvil@redhat.com>

* gdb.dwarf2/dw2-ranges.S, gdb.dwarf2/dw2-ranges.exp,
gdb.dwarf2/dw2-ranges.lds: New files.

--- ./gdb/dwarf2read.c 26 Sep 2007 13:59:54 -0000 1.232
+++ ./gdb/dwarf2read.c 9 Oct 2007 15:03:09 -0000
@@ -3075,6 +3075,124 @@ read_lexical_block_scope (struct die_inf
local_symbols = new->locals;
}

+/* Get low and high pc attributes from DW_AT_ranges attribute value OFFSET.
+ Return 1 if the attributes are present and valid, otherwise, return 0. */
+
+static int
+dwarf2_ranges_read (unsigned offset, CORE_ADDR *low_return,
+ CORE_ADDR *high_return, struct dwarf2_cu *cu)
+{
+ struct objfile *objfile = cu->objfile;
+ struct comp_unit_head *cu_header = &cu->header;
+ bfd *obfd = objfile->obfd;
+ unsigned int addr_size = cu_header->addr_size;
+ CORE_ADDR mask = ~(~(CORE_ADDR)1 << (addr_size * 8 - 1));
+ /* Base address selection entry. */
+ CORE_ADDR base;
+ int found_base;
+ unsigned int dummy;
+ gdb_byte *buffer;
+ CORE_ADDR marker;
+ int low_set;
+ CORE_ADDR low = 0;
+ CORE_ADDR high = 0;
+
+ found_base = cu_header->base_known;
+ base = cu_header->base_address;
+
+ if (offset >= dwarf2_per_objfile->ranges_size)
+ {
+ complaint (&symfile_complaints,
+ _("Offset %d out of bounds for DW_AT_ranges attribute"),
+ offset);
+ return 0;
+ }
+ buffer = dwarf2_per_objfile->ranges_buffer + offset;
+
+ /* Read in the largest possible address. */
+ marker = read_address (obfd, buffer, cu, &dummy);
+ if ((marker & mask) == mask)
+ {
+ /* If we found the largest possible address, then
+ read the base address. */
+ base = read_address (obfd, buffer + addr_size, cu, &dummy);
+ buffer += 2 * addr_size;
+ offset += 2 * addr_size;
+ found_base = 1;
+ }
+
+ low_set = 0;
+
+ while (1)
+ {
+ CORE_ADDR range_beginning, range_end;
+
+ range_beginning = read_address (obfd, buffer, cu, &dummy);
+ buffer += addr_size;
+ range_end = read_address (obfd, buffer, cu, &dummy);
+ buffer += addr_size;
+ offset += 2 * addr_size;
+
+ /* An end of list marker is a pair of zero addresses. */
+ if (range_beginning == 0 && range_end == 0)
+ /* Found the end of list entry. */
+ break;
+
+ /* Each base address selection entry is a pair of 2 values.
+ The first is the largest possible address, the second is
+ the base address. Check for a base address here. */
+ if ((range_beginning & mask) == mask)
+ {
+ /* If we found the largest possible address, then
+ read the base address. */
+ base = read_address (obfd, buffer + addr_size, cu, &dummy);
+ found_base = 1;
+ continue;
+ }
+
+ if (!found_base)
+ {
+ /* We have no valid base address for the ranges
+ data. */
+ complaint (&symfile_complaints,
+ _("Invalid .debug_ranges data (no base address)"));
+ return 0;
+ }
+
+ range_beginning += base;
+ range_end += base;
+
+ /* FIXME: This is recording everything as a low-high
+ segment of consecutive addresses. We should have a
+ data structure for discontiguous block ranges
+ instead. */
+ if (! low_set)
+ {
+ low = range_beginning;
+ high = range_end;
+ low_set = 1;
+ }
+ else
+ {
+ if (range_beginning < low)
+ low = range_beginning;
+ if (range_end > high)
+ high = range_end;
+ }
+ }
+
+ if (! low_set)
+ /* If the first entry is an end-of-list marker, the range
+ describes an empty scope, i.e. no instructions. */
+ return 0;
+
+ if (low_return)
+ *low_return = low;
+ if (high_return)
+ *high_return = high;
+ return 1;
+}
+
/* Get low and high pc attributes from a die. Return 1 if the attributes
are present and valid, otherwise, return 0. Return -1 if the range is
discontinuous, i.e. derived from DW_AT_ranges information. */
@@ -3082,10 +3200,7 @@ static int
dwarf2_get_pc_bounds (struct die_info *die, CORE_ADDR *lowpc,
CORE_ADDR *highpc, struct dwarf2_cu *cu)
{
- struct objfile *objfile = cu->objfile;
- struct comp_unit_head *cu_header = &cu->header;
struct attribute *attr;
- bfd *obfd = objfile->obfd;
CORE_ADDR low = 0;
CORE_ADDR high = 0;
int ret = 0;
@@ -3109,108 +3224,11 @@ dwarf2_get_pc_bounds (struct die_info *d
attr = dwarf2_attr (die, DW_AT_ranges, cu);
if (attr != NULL)
{
- unsigned int addr_size = cu_header->addr_size;
- CORE_ADDR mask = ~(~(CORE_ADDR)1 << (addr_size * 8 - 1));
/* Value of the DW_AT_ranges attribute is the offset in the
.debug_ranges section. */
- unsigned int offset = DW_UNSND (attr);
- /* Base address selection entry. */
- CORE_ADDR base;
- int found_base;
- unsigned int dummy;
- gdb_byte *buffer;
- CORE_ADDR marker;
- int low_set;
-
- found_base = cu_header->base_known;
- base = cu_header->base_address;
-
- if (offset >= dwarf2_per_objfile->ranges_size)
- {
- complaint (&symfile_complaints,
- _("Offset %d out of bounds for DW_AT_ranges attribute"),
- offset);
- return 0;
- }
- buffer = dwarf2_per_objfile->ranges_buffer + offset;
-
- /* Read in the largest possible address. */
- marker = read_address (obfd, buffer, cu, &dummy);
- if ((marker & mask) == mask)
- {
- /* If we found the largest possible address, then
- read the base address. */
- base = read_address (obfd, buffer + addr_size, cu, &dummy);
- buffer += 2 * addr_size;
- offset += 2 * addr_size;
- found_base = 1;
- }
-
- low_set = 0;
-
- while (1)
- {
- CORE_ADDR range_beginning, range_end;
-
- range_beginning = read_address (obfd, buffer, cu, &dummy);
- buffer += addr_size;
- range_end = read_address (obfd, buffer, cu, &dummy);
- buffer += addr_size;
- offset += 2 * addr_size;
-
- /* An end of list marker is a pair of zero addresses. */
- if (range_beginning == 0 && range_end == 0)
- /* Found the end of list entry. */
- break;
-
- /* Each base address selection entry is a pair of 2 values.
- The first is the largest possible address, the second is
- the base address. Check for a base address here. */
- if ((range_beginning & mask) == mask)
- {
- /* If we found the largest possible address, then
- read the base address. */
- base = read_address (obfd, buffer + addr_size, cu, &dummy);
- found_base = 1;
- continue;
- }
-
- if (!found_base)
- {
- /* We have no valid base address for the ranges
- data. */
- complaint (&symfile_complaints,
- _("Invalid .debug_ranges data (no base address)"));
- return 0;
- }
-
- range_beginning += base;
- range_end += base;
-
- /* FIXME: This is recording everything as a low-high
- segment of consecutive addresses. We should have a
- data structure for discontiguous block ranges
- instead. */
- if (! low_set)
- {
- low = range_beginning;
- high = range_end;
- low_set = 1;
- }
- else
- {
- if (range_beginning < low)
- low = range_beginning;
- if (range_end > high)
- high = range_end;
- }
- }
-
- if (! low_set)
- /* If the first entry is an end-of-list marker, the range
- describes an empty scope, i.e. no instructions. */
+ if (!dwarf2_ranges_read (DW_UNSND (attr), &low, &high, cu))
return 0;
-
+ /* Found discontinuous range of addresses. */
ret = -1;
}
}
@@ -5571,6 +5589,11 @@ read_partial_die (struct partial_die_inf
has_high_pc_attr = 1;
part_die->highpc = DW_ADDR (&attr);
break;
+ case DW_AT_ranges:
+ if (dwarf2_ranges_read (DW_UNSND (&attr), &part_die->lowpc,
+ &part_die->highpc, cu))
+ has_low_pc_attr = has_high_pc_attr = 1;
+ break;
case DW_AT_location:
/* Support the .debug_loc offsets */
if (attr_form_is_block (&attr))
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ ./gdb/testsuite/gdb.dwarf2/dw2-ranges.S 9 Oct 2007 15:03:10 -0000
@@ -0,0 +1,33 @@
+/*
+ Copyright 2007 Free Software Foundation, Inc.
+
+ 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 of the License, 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, see <http://www.gnu.org/licenses/>.
+ */
+
+ .text
+
+ .section .text._start, "ax", @progbits
+ .globl _start
+ .func _start
+_start: call func
+ .endfunc
+ .size _start, . - _start
+
+ .section .text.func, "ax", @progbits
+ .globl func
+ .func func
+func: int3
+ nop
+ .endfunc
+ .size func, . - func
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ ./gdb/testsuite/gdb.dwarf2/dw2-ranges.exp 9 Oct 2007 15:03:10 -0000
@@ -0,0 +1,98 @@
+# Copyright 2007 Free Software Foundation, Inc.
+
+# 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 of the License, 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, see <http://www.gnu.org/licenses/>.
+
+# Test DW_TAG_compile_unit with no children and with neither DW_AT_low_pc nor
+# DW_AT_high_pc but with DW_AT_ranges instead. We created the hole there for
+# DW_AT_ranges by the linker script.
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+# For now pick a sampling of likely targets.
+if {![istarget *-*-linux*]
+ && ![istarget *-*-gnu*]
+ && ![istarget *-*-elf*]
+ && ![istarget *-*-openbsd*]
+ && ![istarget arm-*-eabi*]
+ && ![istarget powerpc-*-eabi*]} {
+ verbose "Skipping i386/amd64 DW_AT_ranges test."
+ return 0
+}
+if { ![istarget "i?86-*-*"] && ![istarget "x86_64-*-*"] } then {
+ verbose "Skipping i386/amd64 DW_AT_ranges test."
+ return 0
+}
+
+set testfile "dw2-ranges"
+set srcfile ${testfile}.S
+set ldsfile ${testfile}.lds
+set binfile ${objdir}/${subdir}/${testfile}
+
+# Avoid `-lm' from `lib/ada.exp' as it would fail with our `-nostdlib'.
+# Provide BOARD for SET_BOARD_INFO.
+set board [target_info name]
+if [board_info $board exists mathlib] {
+ set mathlib_orig [board_info $board mathlib]
+} else {
+ unset -nocomplain mathlib_orig
+}
+set_board_info mathlib ""
+
+set compiled [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable [list debug "additional_flags=-Wl,--script=${srcdir}/${subdir}/${ldsfile} -nostdlib"]]
+
+# Restore the board info.
+if [info exists mathlib_orig] {
+ # SET_BOARD_INFO does not change an already set value.
+ set board_info($board,mathlib) $mathlib_orig
+} else {
+ # There is no deleting function for the board info.
+ unset board_info($board,mathlib)
+}
+
+if { $compiled != "" } {
+ return -1
+}
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+# Former wrong output:
+# Program received signal SIGTRAP, Trace/breakpoint trap.
+# 0x000000000000002b in func ()
+# (gdb) bt
+# #0 0x000000000000002b in func ()
+# #1 0x0000000000000029 in _start ()
+# (gdb) _
+# Correct output:
+# Program received signal SIGTRAP, Trace/breakpoint trap.
+# func () at dw2-ranges.S:14
+# 14 nop
+# Current language: auto; currently asm
+# (gdb) bt
+# #0 func () at dw2-ranges.S:14
+# #1 0x0000000000000029 in _start () at dw2-ranges.S:6
+# (gdb) _
+# The entry #1 is missing on i386.
+# "#0 +func \\(\\) at .*dw2-ranges.S:\[0-9\]+\r\n#1 .*in _start \\(\\) at .*dw2-ranges.S:\[0-9\]+"
+
+gdb_run_cmd
+set test "run"
+gdb_test_multiple "" "$test" {
+ -re "Program received signal SIGTRAP,.*$gdb_prompt $" {
+ pass $test
+ }
+}
+
+gdb_test "backtrace" "#0 +func \\(\\) at .*dw2-ranges.S:\[0-9\]+"
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ ./gdb/testsuite/gdb.dwarf2/dw2-ranges.lds 9 Oct 2007 15:03:10 -0000
@@ -0,0 +1,25 @@
+/*
+ Copyright 2007 Free Software Foundation, Inc.
+
+ 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 of the License, 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, see <http://www.gnu.org/licenses/>.
+ */
+
+SECTIONS
+{
+ .text._start : { *(.text._start) }
+ /* Create the hole. */
+ . = . + 1;
+ .text.func : { *(.text.func) }
+}
+ENTRY(_start)