gdb: Add support for DW_LNS_set_prologue_end in line-table
authorLancelot SIX <lancelot.six@amd.com>
Fri, 25 Feb 2022 23:41:47 +0000 (23:41 +0000)
committerLancelot SIX <lancelot.six@amd.com>
Mon, 4 Apr 2022 22:03:32 +0000 (23:03 +0100)
Add support for DW_LNS_set_prologue_end when building line-tables.  This
attribute can be set by the compiler to indicate that an instruction is
an adequate place to set a breakpoint just after the prologue of a
function.

The compiler might set multiple prologue_end, but considering how
current skip_prologue_using_sal works, this commit modifies it to accept
the first instruction with this marker (if any) to be the place where a
breakpoint should be placed to be at the end of the prologue.

The need for this support came from a problematic usecase generated by
hipcc (i.e. clang).  The problem is as follows:  There's a function
(lets call it foo) which covers PC from 0xa800 to 0xa950.  The body of
foo begins with a call to an inlined function, covering from 0xa800 to
0xa94c.   The issue is that when placing a breakpoint at 'foo', GDB
inserts the breakpoint at 0xa818.  The 0x18 offset is what GDB thinks is
foo's first address past the prologue.

Later, when hitting the breakpoint, GDB reports the stop within the
inlined function because the PC falls in its range while the user
expects to stop in FOO.

Looking at the line-table for this location, we have:

    INDEX  LINE   ADDRESS            IS-STMT
    [...]
    14     293    0x000000000000a66c Y
    15     END    0x000000000000a6e0 Y
    16     287    0x000000000000a800 Y
    17     END    0x000000000000a818 Y
    18     287    0x000000000000a824 Y
    [...]

For comparison, let's look at llvm-dwarfdump's output for this CU:

    Address            Line   Column File   ISA Discriminator Flags
    ------------------ ------ ------ ------ --- ------------- -------------
    [...]
    0x000000000000a66c    293     12      2   0             0  is_stmt
    0x000000000000a6e0     96     43     82   0             0  is_stmt
    0x000000000000a6f8    102     18     82   0             0  is_stmt
    0x000000000000a70c    102     24     82   0             0
    0x000000000000a710    102     18     82   0             0
    0x000000000000a72c    101     16     82   0             0  is_stmt
    0x000000000000a73c   2915     50     83   0             0  is_stmt
    0x000000000000a74c    110      1      1   0             0  is_stmt
    0x000000000000a750    110      1      1   0             0  is_stmt end_sequence
    0x000000000000a800    107      0      1   0             0  is_stmt
    0x000000000000a800    287     12      2   0             0  is_stmt prologue_end
    0x000000000000a818    114     59     81   0             0  is_stmt
    0x000000000000a824    287     12      2   0             0  is_stmt
    0x000000000000a828    100     58     82   0             0  is_stmt
    [...]

The main difference we are interested in here is that llvm-dwarfdump's
output tells us that 0xa800 is an adequate place to place a breakpoint
past a function prologue.  Since we know that foo covers from 0xa800 to
0xa94c, 0xa800 is the address at which the breakpoint should be placed
if the user wants to break in foo.

This commit proposes to add support for the prologue_end flag in the
line-program processing.

The processing of this prologue_end flag is made in skip_prologue_sal,
before it calls gdbarch_skip_prologue_noexcept.  The intent is that if
the compiler gave information on where the prologue ends, we should use
this information and not try to rely on architecture dependent logic to
guess it.

The testsuite have been executed using this patch on GNU/Linux x86_64.
Testcases have been compiled with both gcc/g++ (verison 9.4.0) and
clang/clang++ (version 10.0.0) since at the time of writing GCC does not
set the prologue_end marker.  Tests done with GCC 11.2.0 (not over the
entire testsuite) show that it does not emit this flag either.

No regression have been observed with GCC or Clang.  Note that when
using Clang, this patch fixes a failure in
gdb.opt/inline-small-func.exp.

Change-Id: I720449a8a9b2e1fb45b54c6095d3b1e9da9152f8

13 files changed:
gdb/NEWS
gdb/buildsym.c
gdb/buildsym.h
gdb/doc/gdb.texinfo
gdb/dwarf2/read.c
gdb/symmisc.c
gdb/symtab.c
gdb/symtab.h
gdb/testsuite/gdb.dwarf2/dw2-out-of-range-end-of-seq.exp
gdb/testsuite/gdb.dwarf2/dw2-prologue-end.c [new file with mode: 0644]
gdb/testsuite/gdb.dwarf2/dw2-prologue-end.exp [new file with mode: 0644]
gdb/testsuite/gdb.dwarf2/dw2-ranges-base.exp
gdb/testsuite/lib/dwarf.exp

index d76157128beeaf980f0e020e680273e1bf4b48fd..ef4d8f478073a95f5b55bcebb397ff431a79ba22 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
 
 * DBX mode has been removed.
 
+* GDB now honours the DWARF prologue_end line-table entry flag the compiler can
+  emit to indicate where a breakpoint should be placed to break in a function
+  past its prologue.
+
+* Changed commands
+
+maintenance info line-table
+  Add a PROLOGUE-END column to the output which indicates that an
+  entry corresponds to an address where a breakpoint should be placed
+  to be at the first instruction past a function's prologue.
+
 * Python API
 
   ** New function gdb.format_address(ADDRESS, PROGSPACE, ARCHITECTURE),
index 08e4062fe171d5e50c07f5de438a5fa583dc01e7..4718b201f0368aeed22adfd1cae195d5efeb2708 100644 (file)
@@ -725,6 +725,7 @@ buildsym_compunit::record_line (struct subfile *subfile, int line,
   e->line = line;
   e->is_stmt = (flags & LEF_IS_STMT) != 0;
   e->pc = pc;
+  e->prologue_end = (flags & LEF_PROLOGUE_END) != 0;
 }
 
 \f
index 00e57787897e6a0d40e6e25c4ebc404003ad132d..4e9aaa41426be8594ad9255345bdb1070bac1e62 100644 (file)
@@ -116,6 +116,10 @@ enum linetable_entry_flag : unsigned
 {
   /* Indicates this PC is a good location to place a breakpoint at LINE.  */
   LEF_IS_STMT = 1 << 1,
+
+  /* Indicates this PC is a good location to place a breakpoint at the first
+     instruction past a function prologue.  */
+  LEF_PROLOGUE_END = 1 << 2,
 };
 DEF_ENUM_FLAGS_TYPE (enum linetable_entry_flag, linetable_entry_flags);
 
index af025175521d22fdf6a0b33af50c90df71a03b03..14d7581189e1800c997a86bf740001e2f6e8f5ae 100644 (file)
@@ -19794,6 +19794,33 @@ line 1574.
 List the @code{struct linetable} from all @code{struct symtab}
 instances whose name matches @var{regexp}.  If @var{regexp} is not
 given, list the @code{struct linetable} from all @code{struct symtab}.
+For example:
+
+@smallexample
+(@value{GDBP}) maint info line-table
+objfile: /home/gnu/build/a.out ((struct objfile *) 0x6120000e0d40)
+compunit_symtab: simple.cpp ((struct compunit_symtab *) 0x6210000ff450)
+symtab: /home/gnu/src/simple.cpp ((struct symtab *) 0x6210000ff4d0)
+linetable: ((struct linetable *) 0x62100012b760):
+INDEX  LINE   ADDRESS            IS-STMT PROLOGUE-END
+0      3      0x0000000000401110 Y
+1      4      0x0000000000401114 Y       Y
+2      9      0x0000000000401120 Y
+3      10     0x0000000000401124 Y       Y
+4      10     0x0000000000401129
+5      15     0x0000000000401130 Y
+6      16     0x0000000000401134 Y       Y
+7      16     0x0000000000401139
+8      21     0x0000000000401140 Y
+9      22     0x000000000040114f Y       Y
+10     22     0x0000000000401154
+11     END    0x000000000040115a Y
+@end smallexample
+@noindent
+The @samp{IS-STMT} column indicates if the address is a recommended breakpoint
+location to represent a line or a statement.  The @samp{PROLOGUE-END} column
+indicates that a given address is an adequate place to set a breakpoint at the
+first instruction following a function prologue.
 
 @kindex maint set symbol-cache-size
 @cindex symbol cache size
index d1c0ec2be2dc681564a206b286992e99c9aa44be..d8268de8031c9e611b1876fcca2033c173fee647 100644 (file)
@@ -20943,6 +20943,7 @@ public:
   {
     record_line (false);
     m_discriminator = 0;
+    m_flags &= ~LEF_PROLOGUE_END;
   }
 
   /* Handle DW_LNE_end_sequence.  */
@@ -20951,6 +20952,12 @@ public:
     m_currently_recording_lines = true;
   }
 
+  /* Handle DW_LNS_set_prologue_end.  */
+  void handle_set_prologue_end ()
+  {
+    m_flags |= LEF_PROLOGUE_END;
+  }
+
 private:
   /* Advance the line by LINE_DELTA.  */
   void advance_line (int line_delta)
@@ -21042,6 +21049,7 @@ lnp_state_machine::handle_special_opcode (unsigned char op_code)
   advance_line (line_delta);
   record_line (false);
   m_discriminator = 0;
+  m_flags &= ~LEF_PROLOGUE_END;
 }
 
 void
@@ -21179,10 +21187,11 @@ lnp_state_machine::record_line (bool end_sequence)
     {
       gdb_printf (gdb_stdlog,
                  "Processing actual line %u: file %u,"
-                 " address %s, is_stmt %u, discrim %u%s\n",
+                 " address %s, is_stmt %u, prologue_end %u, discrim %u%s\n",
                  m_line, m_file,
                  paddress (m_gdbarch, m_address),
                  (m_flags & LEF_IS_STMT) != 0,
+                 (m_flags & LEF_PROLOGUE_END) != 0,
                  m_discriminator,
                  (end_sequence ? "\t(end sequence)" : ""));
     }
@@ -21505,6 +21514,9 @@ dwarf_decode_lines_1 (struct line_header *lh, struct dwarf2_cu *cu,
                state_machine.handle_fixed_advance_pc (addr_adj);
              }
              break;
+           case DW_LNS_set_prologue_end:
+             state_machine.handle_set_prologue_end ();
+             break;
            default:
              {
                /* Unknown standard opcode, ignore it.  */
index d33ea0fed9a4e4b9d01d7607564bc0787a7e7d4f..e8092e8af08a67639c245d824f993a5537c893d4 100644 (file)
@@ -983,11 +983,12 @@ maintenance_print_one_line_table (struct symtab *symtab, void *data)
       /* Leave space for 6 digits of index and line number.  After that the
         tables will just not format as well.  */
       struct ui_out *uiout = current_uiout;
-      ui_out_emit_table table_emitter (uiout, 4, -1, "line-table");
+      ui_out_emit_table table_emitter (uiout, 5, -1, "line-table");
       uiout->table_header (6, ui_left, "index", _("INDEX"));
       uiout->table_header (6, ui_left, "line", _("LINE"));
       uiout->table_header (18, ui_left, "address", _("ADDRESS"));
-      uiout->table_header (1, ui_left, "is-stmt", _("IS-STMT"));
+      uiout->table_header (7, ui_left, "is-stmt", _("IS-STMT"));
+      uiout->table_header (12, ui_left, "prologue-end", _("PROLOGUE-END"));
       uiout->table_body ();
 
       for (int i = 0; i < linetable->nitems; ++i)
@@ -1004,6 +1005,7 @@ maintenance_print_one_line_table (struct symtab *symtab, void *data)
          uiout->field_core_addr ("address", objfile->arch (),
                                  item->pc);
          uiout->field_string ("is-stmt", item->is_stmt ? "Y" : "");
+         uiout->field_string ("prologue-end", item->prologue_end ? "Y" : "");
          uiout->text ("\n");
        }
     }
index b3b7489a235bc01410c747becf764f95ab862311..57a55a5e0e823b928351592d05e56e58198adcf2 100644 (file)
@@ -3818,6 +3818,44 @@ skip_prologue_using_lineinfo (CORE_ADDR func_addr, struct symtab *symtab)
   return func_addr;
 }
 
+/* Try to locate the address where a breakpoint should be placed past the
+   prologue of function starting at FUNC_ADDR using the line table.
+
+   Return the address associated with the first entry in the line-table for
+   the function starting at FUNC_ADDR which has prologue_end set to true if
+   such entry exist, otherwise return an empty optional.  */
+
+static gdb::optional<CORE_ADDR>
+skip_prologue_using_linetable (CORE_ADDR func_addr)
+{
+  CORE_ADDR start_pc, end_pc;
+
+  if (!find_pc_partial_function (func_addr, nullptr, &start_pc, &end_pc))
+    return {};
+
+  const struct symtab_and_line prologue_sal = find_pc_line (start_pc, 0);
+  if (prologue_sal.symtab != nullptr
+      && prologue_sal.symtab->language () != language_asm)
+    {
+      struct linetable *linetable = prologue_sal.symtab->linetable ();
+
+      auto it = std::lower_bound
+       (linetable->item, linetable->item + linetable->nitems, start_pc,
+        [] (const linetable_entry &lte, CORE_ADDR pc) -> bool
+        {
+          return lte.pc < pc;
+        });
+
+      for (;
+          it < linetable->item + linetable->nitems && it->pc <= end_pc;
+          it++)
+       if (it->prologue_end)
+         return {it->pc};
+    }
+
+  return {};
+}
+
 /* Adjust SAL to the first instruction past the function prologue.
    If the PC was explicitly specified, the SAL is not changed.
    If the line number was explicitly specified then the SAL can still be
@@ -3901,6 +3939,21 @@ skip_prologue_sal (struct symtab_and_line *sal)
     {
       pc = saved_pc;
 
+      /* Check if the compiler explicitly indicated where a breakpoint should
+         be placed to skip the prologue.  */
+      if (skip)
+       {
+         gdb::optional<CORE_ADDR> linetable_pc
+           = skip_prologue_using_linetable (pc);
+         if (linetable_pc)
+           {
+             pc = *linetable_pc;
+             start_sal = find_pc_sect_line (pc, section, 0);
+             force_skip = 1;
+             continue;
+           }
+       }
+
       /* If the function is in an unmapped overlay, use its unmapped LMA address,
         so that gdbarch_skip_prologue has something unique to work on.  */
       if (section_is_overlay (section) && !section_is_mapped (section))
index d12eee6e9d8c8d3fdeec7606474ddef7ee9d1bc7..25b4f7d2704bad9fad6005b1cb63ec0d19b89b9f 100644 (file)
@@ -1398,6 +1398,10 @@ struct linetable_entry
   /* True if this PC is a good location to place a breakpoint for LINE.  */
   unsigned is_stmt : 1;
 
+  /* True if this location is a good location to place a breakpoint after a
+     function prologue.  */
+  bool prologue_end : 1;
+
   /* The address for this entry.  */
   CORE_ADDR pc;
 };
index f60f622067ee9ac2ac5e02bf3713576406d2a1a7..23df111e73adb86301e028c6de192c06445c57d5 100644 (file)
@@ -86,10 +86,10 @@ if ![runto_main] {
 
 set test "END with address 1 eliminated"
 gdb_test_multiple "maint info line-table $srcfile$" $test {
-    -re -wrap "END *0x0*1 *Y \r\n.*" {
+    -re -wrap "END *0x0*1 *Y *\r\n.*" {
        fail $gdb_test_name
     }
-    -re -wrap "END *$hex *Y " {
+    -re -wrap "END *$hex *Y *" {
        pass $gdb_test_name
     }
 }
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-prologue-end.c b/gdb/testsuite/gdb.dwarf2/dw2-prologue-end.c
new file mode 100644 (file)
index 0000000..d166540
--- /dev/null
@@ -0,0 +1,28 @@
+/* Copyright 2022 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/>.  */
+
+
+int
+main (void)
+{                                                      /* main prologue */
+  asm ("main_label: .global main_label");
+  int m = 42;                                          /* main assign m */
+  asm ("main_assign_n: .global main_assign_n");
+  int n = 54;                                          /* main assign n */
+  asm ("main_end_prologue: .global main_end_prologue");
+  int o = 96;                                          /* main assign o */
+  asm ("main_end: .global main_end");                  /* main end */
+  return m + n - o;
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-prologue-end.exp b/gdb/testsuite/gdb.dwarf2/dw2-prologue-end.exp
new file mode 100644 (file)
index 0000000..0de13ae
--- /dev/null
@@ -0,0 +1,91 @@
+# Copyright 2022 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/>.
+
+# Check that GDB can honor the prologue_end flag the compiler can place
+# in the line-table data.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+standard_testfile .c .S
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcdir subdir srcfile srcfile2
+    declare_labels lines_label
+
+    get_func_info main
+
+    cu {} {
+       compile_unit {
+           {language @DW_LANG_C}
+           {name dw2-prologue-end.c}
+           {stmt_list ${lines_label} DW_FORM_sec_offset}
+       } {
+           subprogram {
+               {external 1 flag}
+               {name main}
+               {low_pc $main_start addr}
+               {high_pc "$main_start + $main_len" addr}
+           }
+       }
+    }
+
+    lines {version 5} lines_label {
+       include_dir "${srcdir}/${subdir}"
+       file_name "$srcfile" 1
+
+       program {
+           {DW_LNE_set_address $main_start}
+           {line [gdb_get_line_number "main prologue"]}
+           {DW_LNS_copy}
+
+           {DW_LNE_set_address main_label}
+           {line [gdb_get_line_number "main assign m"]}
+           {DW_LNS_copy}
+
+           {DW_LNE_set_address main_assign_n}
+           {line [gdb_get_line_number "main assign n"]}
+           {DW_LNS_copy}
+
+           {DW_LNE_set_address main_end_prologue}
+           {line [gdb_get_line_number "main assign o"]}
+           {DW_LNS_set_prologue_end}
+           {DW_LNS_copy}
+
+           {DW_LNE_set_address main_end}
+           {line [gdb_get_line_number "main end"]}
+           {DW_LNS_copy}
+
+           {DW_LNE_end_sequence}
+       }
+    }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+         [list $srcfile $asm_file] {nodebug}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+set prologue_end_line [gdb_get_line_number "main assign o"]
+gdb_test "frame" ".*main \\\(\\\) at \[^\r\n\]*:$prologue_end_line\r\n.*"
index ae44dc49329b40645477b6dbd2a695f7b9ef96d2..ec31f1f9d234ba09fea3fe0101a60c6831964dfd 100644 (file)
@@ -183,7 +183,7 @@ gdb_test_multiple "maint info line-table gdb.dwarf2/dw2-ranges-base.c" \
        -re ".*linetable: \\(\\(struct linetable \\*\\) 0x0\\):\r\nNo line table.\r\n" {
            exp_continue
        }
-       -re ".*linetable: \\(\\(struct linetable \\*\\) $hex\\):\r\nINDEX\[ \t\]+LINE\[ \t\]+ADDRESS\[ \t\]+IS-STMT *\r\n" {
+       -re ".*linetable: \\(\\(struct linetable \\*\\) $hex\\):\r\nINDEX\[ \t\]+LINE\[ \t\]+ADDRESS\[ \t\]+IS-STMT\[ \t\]PROLOGUE-END *\r\n" {
            exp_continue
        }
     }
index 6e8b1ccbe7f30b8a7a00830e3b9b30e9536e0902..ed7ad3831809c7c86d40bf9b8c328162b15c4ea0 100644 (file)
@@ -2269,14 +2269,13 @@ namespace eval Dwarf {
        _op .byte $_default_is_stmt "default_is_stmt"
        _op .byte 1 "line_base"
        _op .byte 1 "line_range"
-       _op .byte 10 "opcode_base"
+       _op .byte 11 "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.
+       # for each of the standard opcodes.  Generating 10 entries here
+       # matches the use of 11 in the opcode_base above.  These 10
+       # entries match the 9 standard opcodes for DWARF2 plus
+       # DW_LNS_prologue_end from DWARF3.
        _op .byte 0 "standard opcode 1"
        _op .byte 1 "standard opcode 2"
        _op .byte 1 "standard opcode 3"
@@ -2286,6 +2285,7 @@ namespace eval Dwarf {
        _op .byte 0 "standard opcode 7"
        _op .byte 0 "standard opcode 8"
        _op .byte 1 "standard opcode 9"
+       _op .byte 0 "standard opcode 10"
 
        proc include_dir {dirname} {
            variable _line_include_dirs
@@ -2481,6 +2481,10 @@ namespace eval Dwarf {
                _op .byte 6
            }
 
+           proc DW_LNS_set_prologue_end {} {
+               _op .byte 0x0a
+           }
+
            proc DW_LNS_advance_pc {offset} {
                _op .byte 2
                _op .uleb128 ${offset}