From patchwork Tue Nov 3 10:35:06 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 9520 Received: (qmail 115639 invoked by alias); 3 Nov 2015 10:35:17 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 115580 invoked by uid 89); 3 Nov 2015 10:35:14 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.7 required=5.0 tests=AWL, BAYES_00, RCVD_IN_DNSWL_LOW autolearn=ham version=3.3.2 X-HELO: mail-wi0-f174.google.com Received: from mail-wi0-f174.google.com (HELO mail-wi0-f174.google.com) (209.85.212.174) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-GCM-SHA256 encrypted) ESMTPS; Tue, 03 Nov 2015 10:35:11 +0000 Received: by wijp11 with SMTP id p11so68672974wij.0 for ; Tue, 03 Nov 2015 02:35:08 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-type:content-disposition:in-reply-to :user-agent; bh=7VNK6fWFjRmT3yY+kqErv40/x+N/QNaigWmUDUQte0A=; b=aOG9/ZqKgqi6TYlmN6V77x+Jm59TH6/79G+1+SZt0R+aUxYQ1fYkDuVNkuDDNOeJAO EwMYbgcrBqDjYttKODdk+RCvO92E1Jiv++aXlAu4KE74vdmOyW5MYiybDwzZVldH2dwF PzxJn1epIj5RCN+RX164oLQ5UWwSIfG3i3v914GB6SCXorDjoxVeYSR+TOVKRdyZkY1A Dj2EWdQSrGDqsdLziLltrarWAfrqFb30dP3SFRq3CqLphcETV7l6u/MmdSc1QIm3yiH0 QeYrIEbS7RBBjWRUCiJPMwohG4MR0niPJ0ctMoc1PpUevVLA5tmdfRgraJp4D2aido5O plAw== X-Gm-Message-State: ALoCoQkddmE+ZarUdIU6picmGikH0y3gPLy9hFz2pBWR9kG8GMs5ETk7aW2ruukXoaw6fU7NqQ0N X-Received: by 10.194.134.135 with SMTP id pk7mr32474411wjb.48.1446546908647; Tue, 03 Nov 2015 02:35:08 -0800 (PST) Received: from localhost (cust64-dsl91-135-5.idnet.net. [91.135.5.64]) by smtp.gmail.com with ESMTPSA id v9sm26766611wjv.45.2015.11.03.02.35.07 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 03 Nov 2015 02:35:07 -0800 (PST) Date: Tue, 3 Nov 2015 10:35:06 +0000 From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Doug Evans Subject: [PATCH v2] gdb: Handle multiple base address in debug_ranges data. Message-ID: <20151103103505.GO23628@embecosm.com> References: <81cb8e0b755217ed2ea2afdffb3a5c44421b72cd.1444985270.git.andrew.burgess@embecosm.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <81cb8e0b755217ed2ea2afdffb3a5c44421b72cd.1444985270.git.andrew.burgess@embecosm.com> X-Editor: GNU Emacs [ http://www.gnu.org/software/emacs ] User-Agent: Mutt/1.5.24 (2015-08-30) X-IsSubscribed: yes Below is a second attempt at fixing the multiple base address bug in the gdb DWARF parsing code. The actual fix is identical to the first patch, the big change here is in the testing. Gone is the x86-64 assembler example, and instead I have tried[1] to extend the dwarf assembler to generate sufficient information to trigger this bug, this required extensions to the .debug_line generation, and new code to handle .debug_ranges generation. I've tested this new tests on x86-64 Linux compiling as a 64-bit target, and when passing '-m32' to the test to compile as a 32-bit target. My TCL skills are pretty weak[2] so constructive guidance on how to improve this code would be great, alternatively if someone (anyone) would like to show me how easy this is, please do improve on this patch. Alternatively, how do you feel about letting this in ... for now. Thanks, Andrew [1] Really, I tried. No matter how bad the TCL code might look, please don't think I've not tried :) [2] As evidence I present .... this patch :) --- It is possible to use multiple base addresses within a single address range series, within the .debug_ranges section. The following is a simplified example for 32-bit addresses: .section ".debug_ranges" .4byte 0xffffffff .4byte BASE_1 .4byte START_OFFSET_1 .4byte END_OFFSET_1 .4byte START_OFFSET_2 .4byte END_OFFSET_2 .4byte 0xffffffff .4byte BASE_2 .4byte START_OFFSET_3 .4byte END_OFFSET_3 .4byte 0 .4byte 0 In this example START/END 1 and 2 are relative to BASE_1, while START/END 3 are relative to BASE_2. Currently gdb does not correctly parse this DWARF, resulting in corrupted address range information. This commit fixes this issue, and adds a new test to cover this case. In order to support testing of this feature extensions were made to the testsuite dwarf assembler, additional functionality was added to the .debug_line generation function, and a new function for generating the .debug_ranges section was added. gdb/ChangeLog: * dwarf2read.c (dwarf2_ranges_read): Unify and fix base address reading code. gdb/testsuite/ChangeLog: * gdb.dwarf2/dw2-ranges-base.c: New file. * gdb.dwarf2/dw2-ranges-base.exp: New file. * lib/dwarf.exp (namespace eval Dwarf): Add new variables to support additional line table, and debug ranges generation. (Dwarf::ranges): New function, generate .debug_ranges. (Dwarf::lines): Support generating simple line table programs. (Dwarf::assemble): Initialise new namespace variables. --- gdb/ChangeLog | 5 + gdb/dwarf2read.c | 19 +-- gdb/testsuite/ChangeLog | 10 ++ gdb/testsuite/gdb.dwarf2/dw2-ranges-base.c | 36 ++++++ gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp | 143 ++++++++++++++++++++++ gdb/testsuite/lib/dwarf.exp | 175 +++++++++++++++++++++++++-- 6 files changed, 362 insertions(+), 26 deletions(-) create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-ranges-base.c create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 38a42ea..3a9e992 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,8 @@ +2015-10-15 Andrew Burgess + + * dwarf2read.c (dwarf2_ranges_read): Unify and fix base address + reading code. + 2015-10-30 Pedro Alves * breakpoint.c (breakpoint_in_range_p) diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c index 87dc8b4..a560ed8 100644 --- a/gdb/dwarf2read.c +++ b/gdb/dwarf2read.c @@ -11894,7 +11894,6 @@ dwarf2_ranges_read (unsigned offset, CORE_ADDR *low_return, int found_base; unsigned int dummy; const gdb_byte *buffer; - CORE_ADDR marker; int low_set; CORE_ADDR low = 0; CORE_ADDR high = 0; @@ -11913,18 +11912,6 @@ dwarf2_ranges_read (unsigned offset, CORE_ADDR *low_return, } 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; baseaddr = ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile)); @@ -11949,9 +11936,9 @@ dwarf2_ranges_read (unsigned offset, CORE_ADDR *low_return, 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); + /* If we found the largest possible address, then we already + have the base address in range_end. */ + base = range_end; found_base = 1; continue; } diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index e01ee86..5ab6199 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,13 @@ +2015-10-16 Andrew Burgess + + * gdb.dwarf2/dw2-ranges-base.c: New file. + * gdb.dwarf2/dw2-ranges-base.exp: New file. + * lib/dwarf.exp (namespace eval Dwarf): Add new variables to + support additional line table, and debug ranges generation. + (Dwarf::ranges): New function, generate .debug_ranges. + (Dwarf::lines): Support generating simple line table programs. + (Dwarf::assemble): Initialise new namespace variables. + 2015-10-30 Yao Qi * gdb.threads/wp-replication.c (watch_count_done): Remove. diff --git a/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.c b/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.c new file mode 100644 index 0000000..4d52b6e --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.c @@ -0,0 +1,36 @@ +/* + Copyright 2015 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 . */ + +void __attribute__ ((section (".text.3"))) +frame3 (void) +{ + asm ("frame3_label: .globl frame3_label"); +} + +void __attribute__ ((section (".text.2"))) +frame2 (void) +{ + asm ("frame2_label: .globl frame2_label"); + frame3 (); +} + +void __attribute__ ((section (".text.1"))) +main (void) +{ + asm ("main_label: .globl main_label"); + frame2 (); +} + diff --git a/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp b/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp new file mode 100644 index 0000000..ae891c3 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp @@ -0,0 +1,143 @@ +# Copyright 2015 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 . +load_lib dwarf.exp + +# 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. + +# This test can only be run on targets which support DWARF-2 and use gas. +if {![dwarf2_support]} { + verbose "Skipping DW_AT_ranges test." + return 0 +} + +# The .c files use __attribute__. +if [get_compiler_info] { + return -1 +} +if !$gcc_compiled { + verbose "Skipping DW_AT_ranges test." + return 0 +} + +standard_testfile dw2-ranges-base.c dw2-ranges-base-dw.S + +set asm_file [standard_output_file $srcfile2] +Dwarf::assemble $asm_file { + global srcdir subdir srcfile srcfile2 + declare_labels ranges_label; + declare_labels L; + + # Find start address and length for our functions. + set main_func \ + [function_range main [list ${srcdir}/${subdir}/$srcfile]] + set frame2_func \ + [function_range frame2 [list ${srcdir}/${subdir}/$srcfile]] + set frame3_func \ + [function_range frame3 [list ${srcdir}/${subdir}/$srcfile]] + + # Very simple info for this test program. We don't care about + # this information being correct (w.r.t. funtion / argument types) + # just so long as the compilation using makes use of the + # .debug_ranges data then the test achieves its objective. + cu {} { + compile_unit { + {language @DW_LANG_C} + {name dw-ranges-base.c} + {stmt_list $L DW_FORM_sec_offset} + {ranges ${ranges_label} DW_FORM_sec_offset} + } { + subprogram { + {external 1 flag} + {name main} + } + subprogram { + {external 1 flag} + {name frame2} + } + subprogram { + {external 1 flag} + {name frame3} + } + } + } + + lines {version 2} L { + include_dir "${srcdir}/${subdir}" + file_name "$srcfile" 1 + + # Generate simple line table program. The line table + # information contained here is not correct, and we really + # don't care, just so long as each function has some line + # table data associated with it. We do make use of the fake + # line numbers that we pick here in the tests below. + program { + {DW_LNE_set_address [lindex $main_func 0]} + {DW_LNS_advance_line 10} + {DW_LNS_copy} + {DW_LNS_advance_pc [lindex $main_func 1]} + {DW_LNS_advance_line 19} + {DW_LNS_copy} + {DW_LNE_end_sequence} + + {DW_LNE_set_address [lindex $frame2_func 0]} + {DW_LNS_advance_line 20} + {DW_LNS_copy} + {DW_LNS_advance_pc [lindex $frame2_func 1]} + {DW_LNS_advance_line 29} + {DW_LNS_copy} + {DW_LNE_end_sequence} + + {DW_LNE_set_address [lindex $frame3_func 0]} + {DW_LNS_advance_line 30} + {DW_LNS_copy} + {DW_LNS_advance_pc [lindex $frame3_func 1]} + {DW_LNS_advance_line 39} + {DW_LNS_copy} + {DW_LNE_end_sequence} + } + } + + # Generate ranges data. This is the point of this whole test + # file, we must have multiple bases specified, so we use a new + # base for each function. + ranges {is_64 [is_64_target]} { + ranges_label: sequence { + {base [lindex $main_func 0]} + {range 0 [lindex $main_func 1]} + {base [lindex $frame2_func 0]} + {range 0 [lindex $frame2_func 1]} + {base [lindex $frame3_func 0]} + {range 0 [lindex $frame3_func 1]} + } + } +} + +if { [prepare_for_testing ${testfile}.exp ${testfile} \ + [list $srcfile $asm_file] {nodebug}] } { + return -1 +} + +if ![runto_main] { + return -1 +} + +# Make use of the line numbers we faked in the .debug_line table above. +gdb_test "info line main" \ + "Line 11 of .* starts at address .* and ends at .*" +gdb_test "info line frame2" \ + "Line 21 of .* starts at address .* and ends at .*" +gdb_test "info line frame3" \ + "Line 31 of .* starts at address .* and ends at .*" diff --git a/gdb/testsuite/lib/dwarf.exp b/gdb/testsuite/lib/dwarf.exp index 5dc7ea8..03b59ce 100644 --- a/gdb/testsuite/lib/dwarf.exp +++ b/gdb/testsuite/lib/dwarf.exp @@ -303,6 +303,15 @@ namespace eval Dwarf { # Whether a file_name entry was seen. variable _line_saw_file + # Whether a line table program has been seen. + variable _line_saw_program + + # A Label for line table header generation. + variable _line_header_end_label + + # The address size for debug ranges section. + variable _debug_ranges_64_bit + proc _process_one_constant {name value} { variable _constants variable _AT @@ -981,7 +990,7 @@ namespace eval Dwarf { set _cu_label [_compute_label "cu${cu_num}_begin"] set start_label [_compute_label "cu${cu_num}_start"] set end_label [_compute_label "cu${cu_num}_end"] - + define_label $_cu_label if {$is_64} { _op .4byte 0xffffffff @@ -1118,6 +1127,78 @@ namespace eval Dwarf { define_label $end_label } + # Emit a DWARF .debug_ranges unit. + # OPTIONS is a list with an even number of elements containing + # option-name and option-value pairs. + # Current options are: + # is_64 0|1 - boolean indicating if you want to emit 64-bit DWARF + # default = 0 (32-bit) + # + # BODY is Tcl code that emits the content of the .debug_ranges + # unit, it is evaluated in the caller's context. + proc ranges {options body} { + variable _debug_ranges_64_bit + + foreach { name value } $options { + switch -exact -- $name { + is_64 { set _debug_ranges_64_bit [subst $value] } + default { error "unknown option $name" } + } + } + + set section ".debug_ranges" + _section $section + + proc sequence {{ranges {}}} { + variable _debug_ranges_64_bit + + # Emit the sequence of addresses. + set base "" + foreach range $ranges { + set range [uplevel 1 "subst \"$range\""] + set type [lindex $range 0] + switch -exact -- $type { + base { + set base [lrange $range 1 end] + + if { $_debug_ranges_64_bit } then { + _op .8byte 0xffffffffffffffff "Base Marker" + _op .8byte $base "Base Address" + } else { + _op .4byte 0xffffffff "Base Marker" + _op .4byte $base "Base Address" + } + } + range { + set start [lindex $range 1] + set end [lrange $range 2 end] + + if { $_debug_ranges_64_bit } then { + _op .8byte $start "Start Address" + _op .8byte $end "End Address" + } else { + _op .4byte $start "Start Address" + _op .4byte $end "End Address" + } + } + default { error "unknown range type: $type " } + } + } + + # End of the sequence. + if { $_debug_ranges_64_bit } then { + _op .8byte 0x0 "End of Sequence Marker (Part 1)" + _op .8byte 0x0 "End of Sequence Marker (Part 2)" + } else { + _op .4byte 0x0 "End of Sequence Marker (Part 1)" + _op .4byte 0x0 "End of Sequence Marker (Part 2)" + } + } + + uplevel $body + } + + # Emit a DWARF .debug_line unit. # OPTIONS is a list with an even number of elements containing # option-name and option-value pairs. @@ -1146,6 +1227,8 @@ namespace eval Dwarf { proc lines {options label body} { variable _line_count variable _line_saw_file + variable _line_saw_program + variable _line_header_end_label # Establish the defaults. set is_64 0 @@ -1181,7 +1264,7 @@ namespace eval Dwarf { set unit_len_label [_compute_label "line${_line_count}_start"] set unit_end_label [_compute_label "line${_line_count}_end"] set header_len_label [_compute_label "line${_line_count}_header_start"] - set header_end_label [_compute_label "line${_line_count}_header_end"] + set _line_header_end_label [_compute_label "line${_line_count}_header_end"] if {$is_64} { _op .4byte 0xffffffff @@ -1195,20 +1278,34 @@ namespace eval Dwarf { _op .2byte $_unit_version version if {$is_64} { - _op .8byte "$header_end_label - $header_len_label" "header_length" + _op .8byte "$_line_header_end_label - $header_len_label" "header_length" } else { - _op .4byte "$header_end_label - $header_len_label" "header_length" + _op .4byte "$_line_header_end_label - $header_len_label" "header_length" } define_label $header_len_label _op .byte 1 "minimum_instruction_length" - _op .byte 0 "default_is_stmt" + _op .byte 1 "default_is_stmt" _op .byte 1 "line_base" _op .byte 1 "line_range" - _op .byte 1 "opcode_base" - # Since we emit opcode_base==1, we skip - # standard_opcode_length table altogether. + _op .byte 10 "opcode_base" + + # The standard_opcode_lengths table. The number of arguments + # for each of the standard opcodes. Generating 9 entries here + # matches the use of 10 in the opcode_base above. These 9 + # entries match the 9 standard opcodes for DWARF2, making use + # of only 9 should be fine, even if we are generating DWARF3 + # or DWARF4. + _op .byte 0 "standard opcode 1" + _op .byte 1 "standard opcode 2" + _op .byte 1 "standard opcode 3" + _op .byte 1 "standard opcode 4" + _op .byte 1 "standard opcode 5" + _op .byte 0 "standard opcode 6" + _op .byte 0 "standard opcode 7" + _op .byte 0 "standard opcode 8" + _op .byte 1 "standard opcode 9" proc include_dir {dirname} { _op .ascii [_quote $dirname] @@ -1228,6 +1325,57 @@ namespace eval Dwarf { _op .sleb128 0 "length" } + proc program {statements} { + variable _line_saw_program + variable _line_header_end_label + + if "! $_line_saw_program" { + # Terminate the file list. + _op .byte 0 "Terminator." + define_label $_line_header_end_label + set _line_saw_program 1 + } + + proc DW_LNE_set_address {addr} { + _op .byte 0 + set start [new_label "set_address_start"] + set end [new_label "set_address_end"] + _op .uleb128 "${end} - ${start}" + define_label ${start} + _op .byte 2 + if {[is_64_target]} { + _op .8byte ${addr} + } else { + _op .4byte ${addr} + } + define_label ${end} + } + + proc DW_LNE_end_sequence {} { + _op .byte 0 + _op .uleb128 1 + _op .byte 1 + } + + proc DW_LNS_copy {} { + _op .byte 1 + } + + proc DW_LNS_advance_pc {offset} { + _op .byte 2 + _op .uleb128 ${offset} + } + + proc DW_LNS_advance_line {offset} { + _op .byte 3 + _op .sleb128 ${offset} + } + + foreach statement $statements { + uplevel 1 $statement + } + } + uplevel $body rename include_dir "" @@ -1239,9 +1387,11 @@ namespace eval Dwarf { } # Terminate the file list. - _op .byte 0 "Terminator." + if "! $_line_saw_program" { + _op .byte 0 "Terminator." + define_label $_line_header_end_label + } - define_label $header_end_label define_label $unit_end_label } @@ -1326,6 +1476,9 @@ namespace eval Dwarf { variable _cu_count variable _line_count variable _line_saw_file + variable _line_saw_program + variable _line_header_end_label + variable _debug_ranges_64_bit if {!$_initialized} { _read_constants @@ -1341,6 +1494,8 @@ namespace eval Dwarf { set _line_count 0 set _line_saw_file 0 + set _line_saw_program 0 + set _debug_ranges_64_bit [is_64_target] # Not "uplevel" here, because we want to evaluate in this # namespace. This is somewhat bad because it means we can't