diff options
-rw-r--r-- | gdb/dwarf2/abbrev.c | 1 | ||||
-rw-r--r-- | gdb/dwarf2/cooked-index.h | 3 | ||||
-rw-r--r-- | gdb/dwarf2/index-write.c | 3 | ||||
-rw-r--r-- | gdb/dwarf2/read.c | 70 | ||||
-rw-r--r-- | gdb/testsuite/gdb.dwarf2/dw2-entry-points.c | 43 | ||||
-rw-r--r-- | gdb/testsuite/gdb.dwarf2/dw2-entry-points.exp | 213 | ||||
-rw-r--r-- | gdb/testsuite/gdb.fortran/entry-point.exp | 84 | ||||
-rw-r--r-- | gdb/testsuite/gdb.fortran/entry-point.f90 | 67 |
8 files changed, 479 insertions, 5 deletions
diff --git a/gdb/dwarf2/abbrev.c b/gdb/dwarf2/abbrev.c index 3a429fd..3d6e16a 100644 --- a/gdb/dwarf2/abbrev.c +++ b/gdb/dwarf2/abbrev.c @@ -88,6 +88,7 @@ tag_interesting_for_index (dwarf_tag tag) case DW_TAG_base_type: case DW_TAG_class_type: case DW_TAG_constant: + case DW_TAG_entry_point: case DW_TAG_enumeration_type: case DW_TAG_enumerator: case DW_TAG_imported_declaration: diff --git a/gdb/dwarf2/cooked-index.h b/gdb/dwarf2/cooked-index.h index 5675ea6..3c6b9c3 100644 --- a/gdb/dwarf2/cooked-index.h +++ b/gdb/dwarf2/cooked-index.h @@ -141,7 +141,8 @@ struct cooked_index_entry : public allocate_on_obstack || tag == DW_TAG_constant || tag == DW_TAG_enumerator); case FUNCTIONS_DOMAIN: - return tag == DW_TAG_subprogram; + return (tag == DW_TAG_subprogram + || tag == DW_TAG_entry_point); case TYPES_DOMAIN: return tag_is_type (tag); case MODULES_DOMAIN: diff --git a/gdb/dwarf2/index-write.c b/gdb/dwarf2/index-write.c index 842708a..a03320b 100644 --- a/gdb/dwarf2/index-write.c +++ b/gdb/dwarf2/index-write.c @@ -1307,7 +1307,8 @@ write_cooked_index (cooked_index *table, } gdb_index_symbol_kind kind; - if (entry->tag == DW_TAG_subprogram) + if (entry->tag == DW_TAG_subprogram + || entry->tag == DW_TAG_entry_point) kind = GDB_INDEX_SYMBOL_KIND_FUNCTION; else if (entry->tag == DW_TAG_variable || entry->tag == DW_TAG_constant diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index 6d95874..ec75276 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -6481,6 +6481,8 @@ process_die (struct die_info *die, struct dwarf2_cu *cu) && die->parent->tag == DW_TAG_subprogram) cu->processing_has_namespace_info = true; [[fallthrough]]; + /* Fall through. */ + case DW_TAG_entry_point: case DW_TAG_inlined_subroutine: read_func_scope (die, cu); break; @@ -6597,6 +6599,7 @@ die_needs_namespace (struct die_info *die, struct dwarf2_cu *cu) case DW_TAG_enumerator: case DW_TAG_subprogram: case DW_TAG_inlined_subroutine: + case DW_TAG_entry_point: case DW_TAG_member: case DW_TAG_imported_declaration: return 1; @@ -10987,6 +10990,41 @@ dwarf2_ranges_read_low_addrs (unsigned offset, struct dwarf2_cu *cu, }); } +/* Determine the low and high pc of a DW_TAG_entry_point. */ + +static pc_bounds_kind +dwarf2_get_pc_bounds_entry_point (die_info *die, unrelocated_addr *low, + unrelocated_addr *high, dwarf2_cu *cu) +{ + gdb_assert (low != nullptr); + gdb_assert (high != nullptr); + + if (die->parent->tag != DW_TAG_subprogram) + { + complaint (_("DW_TAG_entry_point not embedded in DW_TAG_subprogram")); + return PC_BOUNDS_INVALID; + } + + /* A DW_TAG_entry_point is embedded in an subprogram. Therefore, we can use + the highpc from its enveloping subprogram and get the lowpc from + DWARF. */ + const enum pc_bounds_kind bounds_kind = dwarf2_get_pc_bounds (die->parent, + low, high, + cu, nullptr, + nullptr); + if (bounds_kind == PC_BOUNDS_INVALID || bounds_kind == PC_BOUNDS_NOT_PRESENT) + return bounds_kind; + + attribute *attr_low = dwarf2_attr (die, DW_AT_low_pc, cu); + if (!attr_low) + { + complaint (_("DW_TAG_entry_point is missing DW_AT_low_pc")); + return PC_BOUNDS_INVALID; + } + *low = attr_low->as_address (); + return bounds_kind; +} + /* Determine the low and high pc using the DW_AT_low_pc and DW_AT_high_pc or DW_AT_ranges attributes of a DIE. */ @@ -11070,8 +11108,11 @@ dwarf2_get_pc_bounds (struct die_info *die, unrelocated_addr *lowpc, unrelocated_addr high = {}; enum pc_bounds_kind ret; - ret = dwarf_get_pc_bounds_ranges_or_highlow_pc (die, &low, &high, cu, map, - datum); + if (die->tag == DW_TAG_entry_point) + ret = dwarf2_get_pc_bounds_entry_point (die, &low, &high, cu); + else + ret = dwarf_get_pc_bounds_ranges_or_highlow_pc (die, &low, &high, cu, map, + datum); if (ret == PC_BOUNDS_NOT_PRESENT || ret == PC_BOUNDS_INVALID) return ret; @@ -16376,6 +16417,11 @@ cooked_indexer::index_dies (cutu_reader *reader, cooked_index_flag flags = IS_STATIC; sect_offset sibling {}; const cooked_index_entry *this_parent_entry = parent_entry; + + /* The scope of a DW_TAG_entry_point cooked_index_entry is the one of + its surrounding subroutine. */ + if (abbrev->tag == DW_TAG_entry_point) + this_parent_entry = parent_entry->parent_entry; info_ptr = scan_attributes (reader->cu->per_cu, reader, info_ptr, info_ptr, abbrev, &name, &linkage_name, &flags, &sibling, &this_parent_entry, @@ -18886,6 +18932,20 @@ new_symbol (struct die_info *die, struct type *type, struct dwarf2_cu *cu, sym->set_domain (LABEL_DOMAIN); add_symbol_to_list (sym, cu->list_in_scope); break; + case DW_TAG_entry_point: + /* SYMBOL_BLOCK_VALUE (sym) will be filled in later by + finish_block. */ + sym->set_aclass_index (LOC_BLOCK); + /* DW_TAG_entry_point provides an additional entry_point to an + existing sub_program. Therefore, we inherit the "external" + attribute from the sub_program to which the entry_point + belongs to. */ + attr2 = dwarf2_attr (die->parent, DW_AT_external, cu); + if (attr2 != nullptr && attr2->as_boolean ()) + list_to_add = cu->get_builder ()->get_global_symbols (); + else + list_to_add = cu->list_in_scope; + break; case DW_TAG_subprogram: /* SYMBOL_BLOCK_VALUE (sym) will be filled in later by finish_block. */ @@ -19631,6 +19691,7 @@ read_type_die_1 (struct die_info *die, struct dwarf2_cu *cu) case DW_TAG_enumeration_type: this_type = read_enumeration_type (die, cu); break; + case DW_TAG_entry_point: case DW_TAG_subprogram: case DW_TAG_subroutine_type: case DW_TAG_inlined_subroutine: @@ -19950,12 +20011,15 @@ determine_prefix (struct die_info *die, struct dwarf2_cu *cu) return ""; case DW_TAG_subprogram: /* Nested subroutines in Fortran get a prefix with the name - of the parent's subroutine. */ + of the parent's subroutine. Entry points are prefixed by the + parent's namespace. */ if (cu->lang () == language_fortran) { if ((die->tag == DW_TAG_subprogram) && (dwarf2_name (parent, cu) != NULL)) return dwarf2_name (parent, cu); + else if (die->tag == DW_TAG_entry_point) + return determine_prefix (parent, cu); } return ""; case DW_TAG_enumeration_type: diff --git a/gdb/testsuite/gdb.dwarf2/dw2-entry-points.c b/gdb/testsuite/gdb.dwarf2/dw2-entry-points.c new file mode 100644 index 0000000..87b4691 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw2-entry-points.c @@ -0,0 +1,43 @@ +/* Copyright 2023 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/>. */ + +/* In the generated DWARF, we'll use the locations of foo_entry_label and + foobar_entry_label as the low_pc's of our entry point TAGs. */ + +int I = 0; +int J = 0; +int K = 0; + +__attribute__ ((noinline)) +void +bar_helper (void) +{ + asm ("bar_helper_label: .globl bar_helper_label"); + I++; + J++; + asm ("foo_entry_label: .globl foo_entry_label"); + J++; + K++; + asm ("foobar_entry_label: .globl foobar_entry_label"); +} + +int +main (void) +{ + asm ("main_label: .globl main_label"); + bar_helper (); + + return 0; +} diff --git a/gdb/testsuite/gdb.dwarf2/dw2-entry-points.exp b/gdb/testsuite/gdb.dwarf2/dw2-entry-points.exp new file mode 100644 index 0000000..f361820 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw2-entry-points.exp @@ -0,0 +1,213 @@ +# Copyright 2023 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 that the DW_TAG_entry_point is handled properly by GDB and that we can +# set breakpoints on function entry points. + +load_lib dwarf.exp + +# This test can only be run on targets that support DWARF-2 and use +# gas. +require dwarf2_support + +standard_testfile .c -dw.S + +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { + return -1 +} + +# Make some DWARF for the test. +set asm_file [standard_output_file $srcfile2] +Dwarf::assemble $asm_file { + global srcfile + declare_labels int_label int2_label + + get_func_info main + get_func_info bar_helper + + set int_size [get_sizeof "int" 4] + + set prog_line 1 + set bar_line 2 + set foo_line 3 + set foobar_line 4 + + set global_I [gdb_target_symbol I] + set global_J [gdb_target_symbol J] + set global_K [gdb_target_symbol K] + + cu {} { + compile_unit { + {language @DW_LANG_Fortran90} + {name dw2-entry-points.f90} + {comp_dir /tmp} + } { + int_label: base_type { + {name "int"} + {byte_size $int_size sdata} + {encoding @DW_ATE_signed} + } + subprogram { + {name prog} + {decl_file 1 data1} + {decl_line $prog_line data1} + {low_pc $main_start addr} + {high_pc "$main_start + $main_len" addr} + {external 1 flag} + {main_subprogram 1 flag} + } + subprogram { + {name bar} + {decl_file 1 data1} + {decl_line $bar_line data1} + {external 1 flag} + {low_pc $bar_helper_start addr} + {high_pc "$bar_helper_start + $bar_helper_len" addr} + } { + formal_parameter { + {name I} + {type :$int_label} + {location {addr $global_I} SPECIAL_expr} + } + formal_parameter { + {name J} + {type :$int_label} + {location {addr $global_J} SPECIAL_expr} + } + entry_point { + {name foo} + {decl_file 1 data1} + {decl_line $foo_line data1} + {low_pc foo_entry_label addr} + } { + formal_parameter { + {name J} + {type :$int_label} + {location {addr $global_J} SPECIAL_expr} + } + formal_parameter { + {name K} + {type :$int_label} + {location {addr $global_K} SPECIAL_expr} + } + } + entry_point { + {name foobar} + {decl_file 1 data1} + {decl_line $foobar_line data1} + {low_pc foobar_entry_label addr} + } { + formal_parameter { + {name J} + {type :$int_label} + {location {addr $global_J} SPECIAL_expr} + } + } + } + } + } + + cu {} { + compile_unit { + {language @DW_LANG_Fortran90} + {name dw2-entry-points-2.f90} + {comp_dir /tmp} + } { + int2_label: base_type { + {name "int"} + {byte_size $int_size sdata} + {encoding @DW_ATE_signed} + } + subprogram { + {name barso} + {decl_file 1 data1} + {decl_line $bar_line data1} + {external 1 flag} + {low_pc $bar_helper_start addr} + {high_pc "$bar_helper_start + $bar_helper_len" addr} + } { + formal_parameter { + {name I} + {type :$int2_label} + {location {addr $global_I} SPECIAL_expr} + } + formal_parameter { + {name J} + {type :$int2_label} + {location {addr $global_J} SPECIAL_expr} + } + entry_point { + {name fooso} + {decl_file 1 data1} + {decl_line $foo_line data1} + {low_pc foo_entry_label addr} + } { + formal_parameter { + {name J} + {type :$int2_label} + {location {addr $global_J} SPECIAL_expr} + } + formal_parameter { + {name K} + {type :$int2_label} + {location {addr $global_K} SPECIAL_expr} + } + } + } + } + } +} + +if {[prepare_for_testing "failed to prepare" ${testfile} \ + [list $srcfile $asm_file] {nodebug}]} { + return -1 +} + +if ![runto_main] { + return -1 +} + +# Try whether we can set and hit breakpoints at the entry_points. +gdb_breakpoint "foo" +gdb_breakpoint "foobar" + +# Now hit the entry_point break point and check their call-stack. +gdb_continue_to_breakpoint "foo" +gdb_test "bt" [multi_line \ + "#0.*${hex} in foo \\(J=1, K=0\\).*" \ + "#1.*${hex} in prog \\(\\).*" \ + ] "bt foo" + +gdb_continue_to_breakpoint "foobar" +gdb_test "bt" [multi_line \ + "#0.*${hex} in foobar \\(J=2\\).*" \ + "#1.*${hex} in prog \\(\\).*" \ + ] "bt foobar" + +# Now try whether we can also set breakpoints on entry_points from other CUs. + +clean_restart ${testfile} + +if ![runto_main] { + return -1 +} + +gdb_breakpoint "fooso" +gdb_continue_to_breakpoint "foo_so" + +gdb_test "bt" [multi_line \ + "#0.*${hex} in foo \\(J=1, K=0\\).*" \ + "#1.*${hex} in prog \\(\\).*" \ +] "bt fooso" diff --git a/gdb/testsuite/gdb.fortran/entry-point.exp b/gdb/testsuite/gdb.fortran/entry-point.exp new file mode 100644 index 0000000..02d7b18 --- /dev/null +++ b/gdb/testsuite/gdb.fortran/entry-point.exp @@ -0,0 +1,84 @@ +# Copyright 2023 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 Fortran entry points for subroutines. + +require allow_fortran_tests + +standard_testfile .f90 +load_lib "fortran.exp" + +if { [prepare_for_testing $testfile.exp $testfile $srcfile {debug f90}] } { + return -1 +} + +if { ![fortran_runto_main] } { + untested "could not run to main" + return -1 +} + +# Test if we can set a breakpoint via the entry-point name. +set entry_point_name "foo" +gdb_breakpoint $entry_point_name +gdb_continue_to_breakpoint "continue to breakpoint: $entry_point_name" \ + ".*entry foo\\(J,K,L,I1\\).*" + +gdb_test "print j" "= 11" "print j, entered via $entry_point_name" +gdb_test "print k" "= 22" "print k, entered via $entry_point_name" +gdb_test "print l" "= 33" "print l, entered via $entry_point_name" +gdb_test "print i1" "= 44" "print i1, entered via $entry_point_name" +gdb_test "info args" \ + [multi_line "j = 11" \ + "k = 22" \ + "l = 33" \ + "i1 = 44"] \ + "info args, entered via $entry_point_name" + +# Test if we can set a breakpoint via the function name. +set entry_point_name "bar" +gdb_breakpoint $entry_point_name +gdb_continue_to_breakpoint "continue to breakpoint: $entry_point_name" \ + ".*subroutine bar\\(I,J,K,I1\\).*" + +gdb_test "print i" "= 444" "print i, entered via $entry_point_name" +gdb_test "print j" "= 555" "print j, entered via $entry_point_name" +gdb_test "print k" "= 666" "print k, entered via $entry_point_name" +gdb_test "print i1" "= 777" "print i1, entered via $entry_point_name" + +# Test a second entry point. +set entry_point_name "foobar" +gdb_breakpoint $entry_point_name +gdb_continue_to_breakpoint "continue to breakpoint: $entry_point_name" \ + ".* entry foobar\\(J\\).*" + +gdb_test "print j" "= 1" "print j, entered via $entry_point_name" +gdb_test "info args" "j = 1" "info args, entered via $entry_point_name" + +# Test breaking at the entrypoint defined inside the module mod via its +# scoped name. +set entry_point_name "mod::mod_foo" + +# GCC moves subroutines with entry points out of the module scope into the +# compile unit scope. +if {[test_compiler_info {gcc-*}]} { + setup_xfail "gcc/105272" "*-*-*" +} +gdb_breakpoint $entry_point_name + +if {[test_compiler_info {gcc-*}]} { + setup_xfail "gcc/105272" "*-*-*" +} +gdb_continue_to_breakpoint "continue to breakpoint: $entry_point_name" \ + ".* entry mod_foo\\(\\).*" diff --git a/gdb/testsuite/gdb.fortran/entry-point.f90 b/gdb/testsuite/gdb.fortran/entry-point.f90 new file mode 100644 index 0000000..7b1c7f9 --- /dev/null +++ b/gdb/testsuite/gdb.fortran/entry-point.f90 @@ -0,0 +1,67 @@ +! Copyright 2023 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/>. + +module mod +implicit none + +contains + subroutine mod_bar + integer :: I = 3 + + goto 100 + + entry mod_foo + I = 33 + +100 print *, I + end subroutine mod_bar +end module mod + + +subroutine bar(I,J,K,I1) + integer :: I,J,K,L,I1 + integer :: A + real :: C + + A = 0 + C = 0.0 + + A = I + K + I1 + goto 300 + + entry foo(J,K,L,I1) + A = J + K + L + I1 + +200 C = J + goto 300 + + entry foobar(J) + goto 200 + +300 A = C + 1 + C = J * 1.5 + + return +end subroutine + +program TestEntryPoint + use mod + + call foo(11,22,33,44) + call bar(444,555,666,777) + call foobar(1) + + call mod_foo() +end program TestEntryPoint |