diff --git a/gdb/block.h b/gdb/block.h index b70c82949d1..05daa734642 100644 --- a/gdb/block.h +++ b/gdb/block.h @@ -178,27 +178,31 @@ struct block : public allocate_on_obstack bool is_contiguous () const { return this->ranges ().size () <= 1; } - /* Return the "entry PC" of this 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.) - - 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. */ + If the entry PC has been set to a specific value then this is + returned. Otherwise, the default_entry_pc() address is returned. */ CORE_ADDR entry_pc () const { - if (this->is_contiguous ()) - return this->start (); - else - return this->ranges ()[0].start (); + return default_entry_pc () + m_entry_pc_offset; + } + + /* Set this block's entry-pc to ADDR, which must lie between start() and + end(). The entry-pc is stored as the signed offset from the + default_entry_pc() address. + + Note that block sub-ranges can be out of order, as such the offset of + the entry-pc might be negative. */ + + void set_entry_pc (CORE_ADDR addr) + { + CORE_ADDR start = default_entry_pc (); + + gdb_assert (addr >= this->start () && addr < this->end ()); + gdb_assert (start >= this->start () && start < this->end ()); + + m_entry_pc_offset = addr - start; } /* Return the objfile of this block. */ @@ -309,6 +313,26 @@ struct block : public allocate_on_obstack private: + /* Return the default entry-pc of this block. The default is the address + we use if the debug information hasn't specifically set a different + entry-pc value. This is the lowest 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. + + 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 default_entry_pc () const + { + if (this->is_contiguous ()) + return this->start (); + else + return this->ranges ()[0].start (); + } + /* If the namespace_info is NULL, allocate it via OBSTACK and initialize its members to zero. */ void initialize_namespace (struct obstack *obstack); @@ -345,6 +369,21 @@ private: startaddr and endaddr above. */ struct blockranges *m_ranges = nullptr; + + /* The offset of the actual entry-pc value from the default entry-pc + value. If space was no object then we'd store an actual address along + with a flag to indicate if the address has been set or not. But we'd + like to keep the size of block low, so we'd like to use a single + member variable. + + We would also like to avoid using 0 as a special address; some targets + do allow for accesses to address 0. + + So instead we store the offset of the defined entry-pc from the + default entry-pc. See default_entry_pc() for the definition of the + default entry-pc. See entry_pc() for how this offset is used. */ + + LONGEST m_entry_pc_offset = 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 665e00bb8e7..5f0b0d4e5d6 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -11313,8 +11313,106 @@ 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 +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; + + /* Filled with the entry-pc if we can find it. */ + std::optional entry; + + /* 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 base + = dwarf2_die_base_address (die, block, cu); + + if (base.has_value ()) + entry.emplace (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 + entry.emplace (per_objfile->relocate (attr->as_address ())); + } + else + entry = dwarf2_die_base_address (die, block, cu); + + if (entry.has_value ()) + { + CORE_ADDR entry_pc = entry.value (); + + /* Some compilers (e.g. GCC) will have the DW_AT_entry_pc point at an + empty sub-range, which by a strict reading of the DWARF means that + the entry-pc is outside the blocks code range. If we continue + using this address then GDB will confuse itself, breakpoints will + be placed at the entry-pc, but once stopped there, GDB will not + recognise that it is inside this block. + + To avoid this, ignore entry-pc values that are outside the block's + range, GDB will then select a suitable default entry-pc. */ + if (entry_pc >= block->start () && entry_pc < block->end ()) + block->set_entry_pc (entry_pc); + else + complaint (_("in %s, DIE %s, DW_AT_entry_pc (%s) outside " + "block range (%s -> %s)"), + objfile_name (per_objfile->objfile), + sect_offset_str (die->sect_off), + paddress (per_objfile->objfile->arch (), entry_pc), + paddress (per_objfile->objfile->arch (), block->start ()), + paddress (per_objfile->objfile->arch (), block->end ())); + } +} + /* 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, @@ -11369,6 +11467,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/testsuite/gdb.dwarf2/dw2-entry-pc.c b/gdb/testsuite/gdb.dwarf2/dw2-entry-pc.c new file mode 100644 index 00000000000..363781d22bf --- /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 . */ + +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 00000000000..eb4d7cd8bb4 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw2-entry-pc.exp @@ -0,0 +1,483 @@ +# 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 . + +# 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"] +} + +# Line on which 'foo' is declared. Used in generated debug. +set foo_decl_line [gdb_get_line_number "foo decl line"] + +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 + } + + # Ensure the CU containing 'foo' is expanded, so the blocks are + # visible. + gdb_test "info function foo" \ + "File \[^\r\n\]+/$::srcfile:\r\n$::foo_decl_line:\\s+void foo\\(\\);.*" + + 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 + + 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 + + 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_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 + + 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 + + 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_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 00000000000..891b22a28b1 --- /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 . */ + +#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 00000000000..6f87ea3a00d --- /dev/null +++ b/gdb/testsuite/gdb.opt/inline-entry.exp @@ -0,0 +1,58 @@ +# 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 . + +# 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 +} + +# This test makes use of inline functions. +get_debug_format +if { [skip_inline_frame_tests] } { + untested "skipping inline frame tests" + 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