diff options
-rw-r--r-- | gdb/block.h | 44 | ||||
-rw-r--r-- | gdb/dwarf2/read.c | 83 | ||||
-rw-r--r-- | gdb/objfiles.c | 1 | ||||
-rw-r--r-- | gdb/testsuite/gdb.dwarf2/dw2-entry-pc.c | 51 | ||||
-rw-r--r-- | gdb/testsuite/gdb.dwarf2/dw2-entry-pc.exp | 487 | ||||
-rw-r--r-- | gdb/testsuite/gdb.opt/inline-entry.c | 41 | ||||
-rw-r--r-- | gdb/testsuite/gdb.opt/inline-entry.exp | 51 |
7 files changed, 745 insertions, 13 deletions
diff --git a/gdb/block.h b/gdb/block.h index c3babad..3bd2c51 100644 --- a/gdb/block.h +++ b/gdb/block.h @@ -179,27 +179,35 @@ struct block : public allocate_on_obstack<block> /* Return the "entry PC" of this block. - The entry PC is the lowest (start) address for the block when all addresses - within the block are contiguous. If non-contiguous, then use the start - address for the first range in the block. - - At the moment, this almost matches what DWARF specifies as the entry - pc. (The missing bit is support for DW_AT_entry_pc which should be - preferred over range data and the low_pc.) + If the entry PC has been set to a specific value then this is + returned. Otherwise, the entry PC is the lowest (start) address for + the block when all addresses within the block are contiguous. If + non-contiguous, then use the start address for the first range in the + block. - Once support for DW_AT_entry_pc is added, I expect that an entry_pc - field will be added to one of these data structures. Once that's done, - the entry_pc field can be set from the dwarf reader (and other readers - too). ENTRY_PC can then be redefined to be less DWARF-centric. */ + This almost matches what DWARF specifies as the entry pc, except that + the final case, using the first address of the first range, is a GDB + extension. However, the DWARF reader sets the specific entry PC + wherever possible, so this non-standard fallback case is only used as + a last resort. */ CORE_ADDR entry_pc () const { - if (this->is_contiguous ()) + if (m_entry_pc != 0) + return m_entry_pc; + else if (this->is_contiguous ()) return this->start (); else return this->ranges ()[0].start (); } + /* Set this block's entry PC. */ + + void set_entry_pc (CORE_ADDR addr) + { + m_entry_pc = addr; + } + /* Return the objfile of this block. */ struct objfile *objfile () const; @@ -344,6 +352,18 @@ private: startaddr and endaddr above. */ struct blockranges *m_ranges = nullptr; + + /* The entry address for this block. The value 0 is special and + indicates that the entry address has not been set. + + Using 0 as a special value is not ideal, targets for which 0 is a + valid code address might run into problems if they want to use 0 as a + block's entry address, but the alternative is to carry a flag + indicating if m_entry_pc is valid or not, but that would make 'struct + block' even bigger, and we want to keep 'struct block' as small as + possible (we might have a lot of blocks). */ + + CORE_ADDR m_entry_pc = 0; }; /* The global block is singled out so that we can provide a back-link diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index 6ac6f7c..28f7010 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -11305,8 +11305,87 @@ get_scope_pc_bounds (struct die_info *die, *highpc = best_high; } +/* Return the base address for DIE (which is represented by BLOCK) within + CU. The base address is the DW_AT_low_pc, or if that is not present, + the first address in the first range defined by DW_AT_ranges. + + The DWARF standard actually says that if DIE has neither DW_AT_low_pc or + DW_AT_ranges then we should search in the parent of DIE for those + properties, and so on up the hierarchy, until we find a die with one of + those attributes, and use that as the base address. We don't implement + that yet simply because we've never encountered a need for it. */ + +static std::optional<CORE_ADDR> +dwarf2_die_base_address (struct die_info *die, struct block *block, + struct dwarf2_cu *cu) +{ + dwarf2_per_objfile *per_objfile = cu->per_objfile; + + struct attribute *attr = dwarf2_attr (die, DW_AT_low_pc, cu); + if (attr != nullptr) + return per_objfile->relocate (attr->as_address ()); + else if (block->ranges ().size () > 0) + return block->ranges ()[0].start (); + + return {}; +} + +/* Set the entry PC for BLOCK which represents DIE from CU. Relies on the + range information (if present) already having been read from DIE and + stored into BLOCK. */ + +static void +dwarf2_record_block_entry_pc (struct die_info *die, struct block *block, + struct dwarf2_cu *cu) +{ + dwarf2_per_objfile *per_objfile = cu->per_objfile; + + /* Set the block's entry PC where possible. */ + struct attribute *attr = dwarf2_attr (die, DW_AT_entry_pc, cu); + if (attr != nullptr) + { + /* DWARF-5 allows for the DW_AT_entry_pc to be an unsigned constant + offset from the containing DIE's base address. We don't limit the + constant handling to DWARF-5 though. If a broken compiler emits + this for DWARF-4 then we handle it just as we would for DWARF-5. */ + if (attr->form_is_constant ()) + { + if (attr->form_is_unsigned ()) + { + CORE_ADDR offset = attr->as_unsigned (); + + std::optional<CORE_ADDR> base + = dwarf2_die_base_address (die, block, cu); + + if (base.has_value ()) + block->set_entry_pc (base.value () + offset); + } + else + { + /* We could possibly handle signed constants, but this is out + of spec, so for now, just complain and ignore it. */ + complaint (_("Unhandled constant for DW_AT_entry_pc, value (%s)"), + plongest (attr->as_nonnegative ())); + } + } + else + { + CORE_ADDR entry_pc = per_objfile->relocate (attr->as_address ()); + block->set_entry_pc (entry_pc); + } + } + else + { + std::optional<CORE_ADDR> entry + = dwarf2_die_base_address (die, block, cu); + + if (entry.has_value ()) + block->set_entry_pc (entry.value ()); + } +} + /* Record the address ranges for BLOCK, offset by BASEADDR, as given - in DIE. */ + in DIE. Also set the entry PC for BLOCK. */ static void dwarf2_record_block_ranges (struct die_info *die, struct block *block, @@ -11361,6 +11440,8 @@ dwarf2_record_block_ranges (struct die_info *die, struct block *block, block->set_ranges (make_blockranges (objfile, blockvec)); } + + dwarf2_record_block_entry_pc (die, block, cu); } /* Check whether the producer field indicates either of GCC < 4.6, or the diff --git a/gdb/objfiles.c b/gdb/objfiles.c index 555195d..761a090 100644 --- a/gdb/objfiles.c +++ b/gdb/objfiles.c @@ -621,6 +621,7 @@ objfile_relocate1 (struct objfile *objfile, { b->set_start (b->start () + delta[block_line_section]); b->set_end (b->end () + delta[block_line_section]); + b->set_entry_pc (b->entry_pc () + delta[block_line_section]); for (blockrange &r : b->ranges ()) { diff --git a/gdb/testsuite/gdb.dwarf2/dw2-entry-pc.c b/gdb/testsuite/gdb.dwarf2/dw2-entry-pc.c new file mode 100644 index 0000000..363781d --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw2-entry-pc.c @@ -0,0 +1,51 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2024 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/>. */ + +volatile int global_var = 0; + +void +foo (void) /* foo decl line */ +{ + /* This label is used to find the start of 'foo' when generating the + debug information. */ + asm ("foo_label: .globl foo_label"); + + /* These labels define a range within foo. */ + asm ("foo_r1_s: .globl foo_r1_s"); + ++global_var; + asm ("foo_r1_e: .globl foo_r1_e"); + + ++global_var; + + asm ("foo_r2_s: .globl foo_r2_s"); + ++global_var; + asm ("foo_middle: .globl foo_middle"); + ++global_var; + asm ("foo_r2_e: .globl foo_r2_e"); + + ++global_var; + + asm ("foo_r3_s: .globl foo_r3_s"); + ++global_var; + asm ("foo_r3_e: .globl foo_r3_e"); +} + +int +main (void) +{ + asm ("main_label: .globl main_label"); +} diff --git a/gdb/testsuite/gdb.dwarf2/dw2-entry-pc.exp b/gdb/testsuite/gdb.dwarf2/dw2-entry-pc.exp new file mode 100644 index 0000000..718848d --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw2-entry-pc.exp @@ -0,0 +1,487 @@ +# Copyright 2024 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 different ways in which DW_AT_entry_pc can be expressed in the +# DWARF. Also test with DWARF-4 and DWARF-5. See the individule test +# procs below precise details of what DW_AT_entry_pc forms are tested. + +load_lib dwarf.exp + +require dwarf2_support + +standard_testfile + +# This compiles the source file and starts and stops GDB, so run it +# before calling prepare_for_testing otherwise GDB will have exited. +get_func_info foo + +if { [prepare_for_testing "failed to prepare" ${testfile} \ + [list ${srcfile}]] } { + return -1 +} + +if ![runto_main] { + return -1 +} + +# Address for the middle of foo. This is used as our entry point when +# the entry_pc is defined as an address. +set foo_middle_addr [get_hexadecimal_valueof "&foo_middle" "UNKNOWN" \ + "get address for middle of foo"] + +# The FOO_START and FOO_END we get from get_func_info is an expression +# involving symbols and offsets. To check the 'maint info blocks' +# output we need these converted into actual addresses. +set foo_start_addr [get_hexadecimal_valueof "$foo_start" "UNKNOWN" \ + "get address for start of foo"] +set foo_end_addr [get_hexadecimal_valueof "$foo_end" "UNKNOWN" \ + "get address for end of foo"] + +# The ranges within foo. Used when foo is defined using ranges rather +# than a low pc and high pc pair. The entry point is in the middle of +# the second range. +foreach var { r1_s r1_e r2_s r2_e r3_s r3_e } { + set $var [get_hexadecimal_valueof "&foo_$var" "UNKNOWN" \ + "get address for foo_$var"] +} + +if [is_ilp32_target] { + set ptr_type "data4" +} else { + set ptr_type "data8" +} + +# Generate a suffix number. Called from each of the test procs below +# to acquire a unique suffix for naming asm files and executables. + +set global_test_suffix 0 +proc get_next_suffix {} { + global global_test_suffix + incr global_test_suffix + + return $global_test_suffix +} + +# Helper for the two build_and_test_* procs below. Combine ASM_FILE +# with the global SRCFILE and build an executable. Use SUFFIX to give +# the executable a unique name. + +proc build_and_runto_main { suffix asm_file } { + if {[prepare_for_testing "failed to prepare" "${::testfile}-${suffix}" \ + [list $::srcfile $asm_file] {nodebug}]} { + return false + } + + if ![runto_main] { + return false + } + + return true +} + + +# Combine ASM_FILE with the global SRCFILE and build an executable, +# use SUFFIX to make the executable name unique. +# +# Then check the blocks at the symbol `foo_middle'. The inner most +# block should be a block for 'foo' with a continuous address range +# and an entry address of ENTRY_PC. + +proc build_and_test_continuous { suffix asm_file entry_pc } { + if { ![build_and_runto_main $suffix $asm_file] } { + return false + } + + gdb_test "maint info blocks foo_middle" \ + [multi_line \ + "\\\[\[^\]\]+\\\] $::foo_start_addr\.\.$::foo_end_addr" \ + " entry pc: $entry_pc" \ + " function: foo" \ + " is contiguous"] +} + +# Combine ASM_FILE with the global SRCFILE and build an executable, +# use SUFFIX to make the executable name unique. +# +# Then check the blocks at the symbol `foo_middle'. The inner most +# block should be a block for 'foo' which has 3 address ranges and an +# entry address of ENTRY_PC. + +proc build_and_test_ranged { suffix asm_file entry_pc } { + if { ![build_and_runto_main $suffix $asm_file] } { + return false + } + + gdb_test "maint info blocks foo_middle" \ + [multi_line \ + "\\\[\[^\]\]+\\\] $::r1_s\.\.$::r3_e" \ + " entry pc: $entry_pc" \ + " function: foo" \ + " address ranges:" \ + " $::r1_s\.\.$::r1_e" \ + " $::r2_s\.\.$::r2_e" \ + " $::r3_s\.\.$::r3_e" ] +} + +# The function's address range is defined using low/high bounds and +# the entry_pc attribute is not given. The function's entry PC will +# default to the low address. + +proc_with_prefix use_low_high_bounds_without_entry_pc { dwarf_vesion } { + set suffix [get_next_suffix] + + # Make some DWARF for the test. + set asm_file [standard_output_file "$::testfile-dw-$suffix.S"] + Dwarf::assemble $asm_file { + global srcfile + + declare_labels lines_table + + set foo_decl_line [gdb_get_line_number "foo decl line"] + + cu { version $::dwarf_version } { + compile_unit { + {producer "gcc"} + {language @DW_LANG_C} + {name ${srcfile}} + {comp_dir /tmp} + {stmt_list $lines_table DW_FORM_sec_offset} + } { + subprogram { + {name foo} + {decl_file 1 data1} + {decl_line $foo_decl_line data1} + {decl_column 1 data1} + {low_pc $::foo_start addr} + {high_pc $::foo_len $::ptr_type} + {external 1 flag} + } + } + } + + lines {version 2} lines_table { + include_dir "$::srcdir/$::subdir" + file_name "$srcfile" 1 + } + } + + build_and_test_continuous $suffix $asm_file $::foo_start_addr +} + +# The function's address range is defined using low/high bounds and an +# entry_pc attribute is given (which contains an address), which will +# be used as the function's entry address. + +proc_with_prefix use_low_high_bounds_with_entry_pc { dwarf_version } { + set suffix [get_next_suffix] + + # Make some DWARF for the test. + set asm_file [standard_output_file "$::testfile-dw-$suffix.S"] + Dwarf::assemble $asm_file { + global srcfile + + declare_labels lines_table + + set foo_decl_line [gdb_get_line_number "foo decl line"] + + cu { version $::dwarf_version } { + compile_unit { + {producer "gcc"} + {language @DW_LANG_C} + {name ${srcfile}} + {comp_dir /tmp} + {stmt_list $lines_table DW_FORM_sec_offset} + } { + subprogram { + {name foo} + {decl_file 1 data1} + {decl_line $foo_decl_line data1} + {decl_column 1 data1} + {low_pc $::foo_start addr} + {high_pc $::foo_len $::ptr_type} + {external 1 flag} + {entry_pc foo_middle addr} + } + } + } + + lines {version 2} lines_table { + include_dir "$::srcdir/$::subdir" + file_name "$srcfile" 1 + } + } + + build_and_test_continuous $suffix $asm_file $::foo_middle_addr +} + +# The function's address range is defined using low/high bounds and an +# entry_pc attribute is given (which contains an offset from the base +# address), which will be used to compute the function's entry address. + +proc_with_prefix use_low_high_bounds_with_entry_offset { dwarf_version } { + set suffix [get_next_suffix] + + # Make some DWARF for the test. + set asm_file [standard_output_file "$::testfile-dw-$suffix.S"] + Dwarf::assemble $asm_file { + global srcfile + + declare_labels lines_table + + set foo_decl_line [gdb_get_line_number "foo decl line"] + + set foo_offset [expr $::foo_middle_addr - $::foo_start_addr] + + cu { version $::dwarf_version } { + compile_unit { + {producer "gcc"} + {language @DW_LANG_C} + {name ${srcfile}} + {comp_dir /tmp} + {stmt_list $lines_table DW_FORM_sec_offset} + } { + subprogram { + {name foo} + {decl_file 1 data1} + {decl_line $foo_decl_line data1} + {decl_column 1 data1} + {low_pc $::foo_start addr} + {high_pc $::foo_len $::ptr_type} + {external 1 flag} + {entry_pc $foo_offset data4} + } + } + } + + lines {version 2} lines_table { + include_dir "$::srcdir/$::subdir" + file_name "$srcfile" 1 + } + } + + build_and_test_continuous $suffix $asm_file $::foo_middle_addr +} + +# The function's address range is defined using range information. No +# entry_pc attribute is used. The entry PC for the function will +# default to the first address of the first range. + +proc_with_prefix use_ranges_without_entry_pc { dwarf_version } { + set suffix [get_next_suffix] + + # Make some DWARF for the test. + set asm_file [standard_output_file "$::testfile-dw-$suffix.S"] + Dwarf::assemble $asm_file { + upvar dwarf_version dwarf_version + global srcfile + + declare_labels lines_table ranges_label + + set foo_decl_line [gdb_get_line_number "foo decl line"] + + cu { version $::dwarf_version } { + compile_unit { + {producer "gcc"} + {language @DW_LANG_C} + {name ${srcfile}} + {comp_dir /tmp} + {stmt_list $lines_table DW_FORM_sec_offset} + {low_pc 0 addr} + } { + subprogram { + {name foo} + {decl_file 1 data1} + {decl_line $foo_decl_line data1} + {decl_column 1 data1} + {external 1 flag} + {ranges ${ranges_label} DW_FORM_sec_offset} + } + } + } + + lines {version 2} lines_table { + include_dir "$::srcdir/$::subdir" + file_name "$srcfile" 1 + } + + if { $dwarf_version == 5 } { + rnglists {} { + table {} { + ranges_label: list_ { + start_end foo_r1_s foo_r1_e + start_end foo_r2_s foo_r2_e + start_end foo_r3_s foo_r3_e + } + } + } + } else { + ranges { } { + ranges_label: sequence { + range foo_r1_s foo_r1_e + range foo_r2_s foo_r2_e + range foo_r3_s foo_r3_e + } + } + } + } + + build_and_test_ranged $suffix $asm_file $::r1_s +} + +# The function's address range is defined using range information and +# an entry_pc attribute (which is an address) is used, this will be +# the entry PC for the function. + +proc_with_prefix use_ranges_with_entry_pc { dwarf_version } { + set suffix [get_next_suffix] + + # Make some DWARF for the test. + set asm_file [standard_output_file "$::testfile-dw-$suffix.S"] + Dwarf::assemble $asm_file { + upvar dwarf_version dwarf_version + global srcfile + + declare_labels lines_table ranges_label + + set foo_decl_line [gdb_get_line_number "foo decl line"] + + cu { version $::dwarf_version } { + compile_unit { + {producer "gcc"} + {language @DW_LANG_C} + {name ${srcfile}} + {comp_dir /tmp} + {stmt_list $lines_table DW_FORM_sec_offset} + {low_pc 0 addr} + } { + subprogram { + {name foo} + {decl_file 1 data1} + {decl_line $foo_decl_line data1} + {decl_column 1 data1} + {external 1 flag} + {ranges ${ranges_label} DW_FORM_sec_offset} + {entry_pc foo_middle addr} + } + } + } + + lines {version 2} lines_table { + include_dir "$::srcdir/$::subdir" + file_name "$srcfile" 1 + } + + if { $dwarf_version == 5 } { + rnglists {} { + table {} { + ranges_label: list_ { + start_end foo_r1_s foo_r1_e + start_end foo_r2_s foo_r2_e + start_end foo_r3_s foo_r3_e + } + } + } + } else { + ranges { } { + ranges_label: sequence { + range foo_r1_s foo_r1_e + range foo_r2_s foo_r2_e + range foo_r3_s foo_r3_e + } + } + } + } + + build_and_test_ranged $suffix $asm_file $::foo_middle_addr +} + +# The function's address range is defined using range information and +# an entry_pc attribute (which is an offset) is used, this will be +# used to calculate the entry PC for the function. + +proc_with_prefix use_ranges_with_entry_offset { dwarf_version } { + set suffix [get_next_suffix] + + # Make some DWARF for the test. + set asm_file [standard_output_file "$::testfile-dw-$suffix.S"] + Dwarf::assemble $asm_file { + upvar dwarf_version dwarf_version + global srcfile + + declare_labels lines_table ranges_label + + set foo_decl_line [gdb_get_line_number "foo decl line"] + + set foo_offset [expr $::foo_middle_addr - $::r1_s] + + cu { version $::dwarf_version } { + compile_unit { + {producer "gcc"} + {language @DW_LANG_C} + {name ${srcfile}} + {comp_dir /tmp} + {stmt_list $lines_table DW_FORM_sec_offset} + {low_pc 0 addr} + } { + subprogram { + {name foo} + {decl_file 1 data1} + {decl_line $foo_decl_line data1} + {decl_column 1 data1} + {external 1 flag} + {ranges ${ranges_label} DW_FORM_sec_offset} + {entry_pc $foo_offset data4} + } + } + } + + lines {version 2} lines_table { + include_dir "$::srcdir/$::subdir" + file_name "$srcfile" 1 + } + + if { $dwarf_version == 5 } { + rnglists {} { + table {} { + ranges_label: list_ { + start_end foo_r1_s foo_r1_e + start_end foo_r2_s foo_r2_e + start_end foo_r3_s foo_r3_e + } + } + } + } else { + ranges { } { + ranges_label: sequence { + range foo_r1_s foo_r1_e + range foo_r2_s foo_r2_e + range foo_r3_s foo_r3_e + } + } + } + } + + build_and_test_ranged $suffix $asm_file $::foo_middle_addr +} + +# Run the tests. +foreach_with_prefix dwarf_version { 4 5 } { + use_low_high_bounds_without_entry_pc $dwarf_version + use_low_high_bounds_with_entry_offset $dwarf_version + use_low_high_bounds_with_entry_pc $dwarf_version + use_ranges_without_entry_pc $dwarf_version + use_ranges_with_entry_pc $dwarf_version + use_ranges_with_entry_offset $dwarf_version +} diff --git a/gdb/testsuite/gdb.opt/inline-entry.c b/gdb/testsuite/gdb.opt/inline-entry.c new file mode 100644 index 0000000..891b22a --- /dev/null +++ b/gdb/testsuite/gdb.opt/inline-entry.c @@ -0,0 +1,41 @@ +/* Copyright 2024 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/>. */ + +#include "attributes.h" + +volatile int global = 0; + +__attribute__((noinline)) ATTRIBUTE_NOCLONE void +foo (int arg) +{ + global += arg; +} + +inline __attribute__((always_inline)) int +bar (int val) +{ + if (global == val) + return 1; + foo (1); + return 1; +} + +int +main (void) +{ + if ((global && bar (1)) || bar (2)) + return 0; + return 1; +} diff --git a/gdb/testsuite/gdb.opt/inline-entry.exp b/gdb/testsuite/gdb.opt/inline-entry.exp new file mode 100644 index 0000000..2427ebb --- /dev/null +++ b/gdb/testsuite/gdb.opt/inline-entry.exp @@ -0,0 +1,51 @@ +# Copyright 2024 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 some code which relies on GDB interpreting the DW_AT_entry_pc +# correctly in order to place the breakpoints. This was tested with +# versions of GCC between 8.4 and 14.2 and in all cases the entry_pc +# was required. +# +# Testing with Clang 9.0.1 and 15.0.2 seemed to indicate that the +# Clang generated code didn't depend on the entry_pc being parsed. + +standard_testfile + +set options {debug optimize=-O2} +lappend_include_file options $srcdir/lib/attributes.h + +if { [prepare_for_testing "failed to prepare" $binfile $srcfile $options] } { + return +} + +if ![runto_main] { + return +} + +gdb_breakpoint "bar" +set bp_bar_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \ + "get number of bar breakpoint"] + +gdb_breakpoint "foo" +set bp_foo_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \ + "get number of foo breakpoint"] + +gdb_test "continue" \ + "Breakpoint ${bp_bar_num}(?:\\.$decimal)?, bar .*" "continue to bar" + +gdb_test "continue" \ + "Breakpoint ${bp_foo_num}(?:\\.$decimal)?, foo .*" "continue to foo" + +gdb_continue_to_end |