Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Tom de Vries <tdevries@suse.de>
To: gdb-patches@sourceware.org
Subject: [PATCH v4 09/11] [gdb] Handle sections when disassembling
Date: Wed,  7 Oct 2026 11:40:55 +0200	[thread overview]
Message-ID: <20261007094057.1811886-10-tdevries@suse.de> (raw)
In-Reply-To: <20261007094057.1811886-1-tdevries@suse.de>

Consider the following session:
...
$ gdb -q -batch a.out -ex start -ex "disassemble _init,0x0000000000401030"
  ...
Dump of assembler code from 0x401000 to 0x401030:
   ...
   0x000000000040101a <_init+26>:	ret
   0x000000000040101b:	add    %al,(%rax)
   0x000000000040101d:	add    %al,(%rax)
   0x000000000040101f:	add    %bh,%bh
   0x0000000000401021:	xor    $0x2fca,%eax
   0x0000000000401026:	jmp    *0x2fcc(%rip)
   0x000000000040102c:	nopl   0x0(%rax)
End of assembler dump.
...

If disassemble with objdump instead, we get:
...
Disassembly of section .init:

0000000000401000 <_init>:
  ...
  40101a:       c3                      ret

Disassembly of section .plt:

0000000000401020 <puts@plt-0x10>:
  401020:       ff 35 ca 2f 00 00       push   0x2fca(%rip)
  401026:       ff 25 cc 2f 00 00       jmp    *0x2fcc(%rip)
  40102c:       0f 1f 40 00             nopl   0x0(%rax)
...

Note the xor vs push.

So gdb:
- first disassembles the .init section
- then disassembles the section hole in between .init and .plt
- interprets the first byte of .plt as the last byte of an instruction
  starting in the section hole
- continues disassembling at .plt+1 instead of .plt+0

Fix this in gdb_disassembly by making it section-aware, and not disassembling
across a section change, getting us instead:
...
   0x000000000040101a <_init+26>:	ret
   0x000000000040101b:	add    %al,(%rax)
   0x000000000040101d:	add    %al,(%rax)
   0x000000000040101f:	add    %bh,%bh
   0x0000000000401020:	push   0x2fca(%rip)        # 0x403ff0
   0x0000000000401026:	jmp    *0x2fcc(%rip)        # 0x403ff8
   0x000000000040102c:	nopl   0x0(%rax)
...

Likewise in tui_disassemble.

Also add a test-case for the fix in gdb_disassembly:
gdb.base/disasm-section-sync.exp.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=34617
---
 gdb/disasm.c                                  | 50 +++++++++-
 gdb/testsuite/gdb.base/disasm-section-sync.S  | 47 +++++++++
 .../gdb.base/disasm-section-sync.exp          | 28 ++++++
 gdb/tui/tui-disasm.c                          | 99 ++++++++++++++-----
 4 files changed, 195 insertions(+), 29 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/disasm-section-sync.S
 create mode 100644 gdb/testsuite/gdb.base/disasm-section-sync.exp

diff --git a/gdb/disasm.c b/gdb/disasm.c
index ec922fbd323..23d0c880777 100644
--- a/gdb/disasm.c
+++ b/gdb/disasm.c
@@ -34,6 +34,7 @@
 #include "cli/cli-style.h"
 #include "objfiles.h"
 #include "inferior.h"
+#include "addrmap.h"
 
 /* Disassemble functions.
    FIXME: We should get rid of all the duplicate code in gdb that does
@@ -1145,12 +1146,12 @@ gdb_disassembler::print_insn (CORE_ADDR memaddr,
   return length;
 }
 
-/* See disasm.h.  */
+/* Helper function for gdb_disassembly.  */
 
-int
-gdb_disassembly (struct gdbarch *gdbarch, struct ui_out *uiout,
-		 gdb_disassembly_flags flags, int how_many,
-		 CORE_ADDR low, CORE_ADDR high)
+static int
+gdb_disassembly_1 (struct gdbarch *gdbarch, struct ui_out *uiout,
+		   gdb_disassembly_flags flags, int how_many,
+		   CORE_ADDR low, CORE_ADDR high)
 {
   struct symtab *symtab;
   int nlines = -1;
@@ -1182,6 +1183,45 @@ gdb_disassembly (struct gdbarch *gdbarch, struct ui_out *uiout,
   return num_displayed;
 }
 
+/* See disasm.h.  */
+
+int
+gdb_disassembly (struct gdbarch *gdbarch, struct ui_out *uiout,
+		 gdb_disassembly_flags flags, int how_many,
+		 CORE_ADDR low, CORE_ADDR high)
+{
+  std::unique_ptr<addrmap_mutable> map = section_addrmap ();
+  bool update_how_many = how_many != -1;
+  int num_displayed = 0;
+  while (low < high)
+    {
+      CORE_ADDR tmp_high = high;
+
+      CORE_ADDR range_high;
+      map->find (low, nullptr, &range_high);
+
+      /* Don't disassemble past a section change.  */
+      if (range_high != (CORE_ADDR)-1)
+	tmp_high = std::min (tmp_high, range_high + 1);
+
+      int res
+	= gdb_disassembly_1 (gdbarch, uiout, flags, how_many, low,
+			     tmp_high);
+      num_displayed += res;
+
+      if (update_how_many)
+	{
+	  how_many -= res;
+	  if (how_many <= 0)
+	    break;
+	}
+
+      low = tmp_high;
+    }
+
+  return num_displayed;
+}
+
 /* Print the instruction at address MEMADDR in debugged memory,
    on STREAM.  Returns the length of the instruction, in bytes,
    and, if requested, the number of branch delay slot instructions.  */
diff --git a/gdb/testsuite/gdb.base/disasm-section-sync.S b/gdb/testsuite/gdb.base/disasm-section-sync.S
new file mode 100644
index 00000000000..06949c4b444
--- /dev/null
+++ b/gdb/testsuite/gdb.base/disasm-section-sync.S
@@ -0,0 +1,47 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright (C) 2026 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/>.  */
+
+	.section        .text_1,"ax",@progbits
+	.globl  foo_1
+	.type   foo_1, @function
+foo_1:
+	endbr64
+	sub    $0x8,%rsp
+	mov    0x2fd1(%rip),%rax
+	test   %rax,%rax
+	je     401016
+	call   *%rax
+	add    $0x8,%rsp
+	ret
+
+	# Get instruction decoding out of sync.
+	.byte 0
+
+	.section        .text_2,"ax",@progbits
+	.globl  foo_2
+	.type   foo_2, @function
+foo_2:
+	push   0x2fca(%rip)
+	jmp    *0x2fcc(%rip)
+	nopl   0x0(%rax)
+
+	.globl  foo_3
+	.type   foo_3, @function
+foo_3:
+	nop
+
+	.section        .note.GNU-stack,"",@progbits
diff --git a/gdb/testsuite/gdb.base/disasm-section-sync.exp b/gdb/testsuite/gdb.base/disasm-section-sync.exp
new file mode 100644
index 00000000000..2f8b01cae52
--- /dev/null
+++ b/gdb/testsuite/gdb.base/disasm-section-sync.exp
@@ -0,0 +1,28 @@
+# Copyright (C) 2026 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 disassembling stops and starts at section boundaries.
+
+require is_x86_64_m64_target
+
+standard_testfile .S main.c
+
+if { [prepare_for_testing "failed to prepare" $testfile \
+	  [list $srcfile $srcfile2] {nodebug}] } {
+    return
+}
+
+# Regression test for PR34617.
+gdb_test "disassemble foo_1,foo_3" "push.*"
diff --git a/gdb/tui/tui-disasm.c b/gdb/tui/tui-disasm.c
index c242614be45..ba11b8e8188 100644
--- a/gdb/tui/tui-disasm.c
+++ b/gdb/tui/tui-disasm.c
@@ -38,6 +38,7 @@
 #include "tui/tui-location.h"
 #include "gdbsupport/selftest.h"
 #include "inferior.h"
+#include "addrmap.h"
 
 struct tui_asm_line
 {
@@ -80,23 +81,13 @@ len_without_escapes (const std::string &str)
   return len;
 }
 
-/* Function to disassemble up to COUNT instructions starting from address
-   PC into the ASM_LINES vector (which will be emptied of any previous
-   contents).  Return the address after the last disassembled instruction.
-   When ADDR_SIZE is non-null then place the maximum size of an address and
-   label into the value pointed to by ADDR_SIZE, and set the addr_size
-   field on each item in ASM_LINES, otherwise the addr_size fields within
-   ASM_LINES are undefined.
+/* Helper function for tui_disassemble.  */
 
-   It is worth noting that ASM_LINES might not have COUNT entries when this
-   function returns.  If the disassembly is truncated for some other
-   reason, for example, we hit invalid memory, then ASM_LINES can have
-   fewer entries than requested.  */
 static CORE_ADDR
-tui_disassemble (struct gdbarch *gdbarch,
-		 std::vector<tui_asm_line> &asm_lines,
-		 CORE_ADDR pc, int count,
-		 size_t *addr_size = nullptr)
+tui_disassemble_1 (struct gdbarch *gdbarch,
+		   std::vector<tui_asm_line> &asm_lines, CORE_ADDR pc,
+		   std::optional<CORE_ADDR> high_pc, int count,
+		   size_t *addr_size)
 {
   bool term_out = disassembler_styling && gdb_stdout->can_emit_style_escape ();
   string_file gdb_dis_out (term_out);
@@ -104,20 +95,29 @@ tui_disassemble (struct gdbarch *gdbarch,
 			    ? (decltype (stream))&null_stream
 			    : (decltype (stream))&gdb_dis_out);
 
-  /* Must start with an empty list.  */
-  asm_lines.clear ();
-
   /* Now construct each line.  */
   for (int i = 0; i < count; ++i)
     {
       tui_asm_line tal;
 
+      if (high_pc.has_value () && pc >= *high_pc)
+	break;
+
       /* Save the instruction address.  */
       tal.addr = pc;
 
       try
 	{
-	  pc += gdb_print_insn (gdbarch, pc, stream, NULL);
+	  int len = gdb_print_insn (gdbarch, pc, stream, NULL);
+	  CORE_ADDR next_pc = pc + len;
+	  if (high_pc.has_value () && next_pc > *high_pc)
+	    {
+	      /* Instruction spans high_pc.  */
+	      gdb_dis_out = "(bad)";
+	      next_pc = *high_pc;
+	    }
+
+	  pc = next_pc;
 	}
       catch (const gdb_exception_error &except)
 	{
@@ -153,6 +153,53 @@ tui_disassemble (struct gdbarch *gdbarch,
   return pc;
 }
 
+/* Function to disassemble up to COUNT instructions starting from address
+   PC into the ASM_LINES vector (which will be emptied of any previous
+   contents).  Return the address after the last disassembled instruction.
+   When ADDR_SIZE is non-null then place the maximum size of an address and
+   label into the value pointed to by ADDR_SIZE, and set the addr_size
+   field on each item in ASM_LINES, otherwise the addr_size fields within
+   ASM_LINES are undefined.
+
+   MAP contains a map from adressess to sections and corresponding ranges.
+
+   It is worth noting that ASM_LINES might not have COUNT entries when this
+   function returns.  If the disassembly is truncated for some other
+   reason, for example, we hit invalid memory, then ASM_LINES can have
+   fewer entries than requested.  */
+
+static CORE_ADDR
+tui_disassemble (struct gdbarch *gdbarch,
+		 std::vector<tui_asm_line> &asm_lines,
+		 addrmap_mutable &map, CORE_ADDR pc, int count,
+		 size_t *addr_size = nullptr)
+{
+  /* Must start with an empty list.  */
+  asm_lines.clear ();
+
+  while (count > 0)
+    {
+      CORE_ADDR range_high;
+      map.find (pc, nullptr, &range_high);
+
+      /* Don't disassemble past a section change.  */
+      std::optional<CORE_ADDR> high_pc;
+      if (range_high != (CORE_ADDR)-1)
+	high_pc = range_high + 1;
+
+      int prev_nr_lines = asm_lines.size ();
+      pc = tui_disassemble_1 (gdbarch, asm_lines, pc, high_pc, count,
+			      addr_size);
+      int nr_lines = asm_lines.size () - prev_nr_lines;
+      if (nr_lines == 0)
+	break;
+
+      count -= nr_lines;
+    }
+
+  return pc;
+}
+
 /* Look backward from ADDR for an address from which we can start
    disassembling, this needs to be something we can be reasonably
    confident will fall on an instruction boundary.  We use msymbol
@@ -197,13 +244,14 @@ tui_find_disassembly_address (struct gdbarch *gdbarch, CORE_ADDR pc, int from)
     return pc;
 
   std::vector<tui_asm_line> asm_lines;
+  std::unique_ptr<addrmap_mutable> map = section_addrmap ();
 
   if (from > 0)
     {
       /* Always disassemble 1 extra instruction here, then if the last
 	 instruction fails to disassemble we will take the address of the
 	 previous instruction that did disassemble as the result.  */
-      tui_disassemble (gdbarch, asm_lines, pc, max_lines + 1);
+      tui_disassemble (gdbarch, asm_lines, *map, pc, max_lines + 1);
       if (asm_lines.empty ())
 	return pc;
       return asm_lines.back ().addr;
@@ -249,7 +297,8 @@ tui_find_disassembly_address (struct gdbarch *gdbarch, CORE_ADDR pc, int from)
 	break;
 
       /* Disassemble forward.  */
-      next_addr = tui_disassemble (gdbarch, asm_lines, new_low, max_lines);
+      next_addr
+	= tui_disassemble (gdbarch, asm_lines, *map, new_low, max_lines);
       if (asm_lines.empty ())
 	{
 	  new_low = prev_low;
@@ -280,7 +329,8 @@ tui_find_disassembly_address (struct gdbarch *gdbarch, CORE_ADDR pc, int from)
 
       /* Take the best possible match we have.  */
       new_low = *possible_new_low;
-      next_addr = tui_disassemble (gdbarch, asm_lines, new_low, max_lines);
+      next_addr
+	= tui_disassemble (gdbarch, asm_lines, *map, new_low, max_lines);
     }
 
   /* The following walk forward assumes that ASM_LINES contains exactly
@@ -303,7 +353,7 @@ tui_find_disassembly_address (struct gdbarch *gdbarch, CORE_ADDR pc, int from)
 	CORE_ADDR old_next_addr = next_addr;
 	std::vector<tui_asm_line> single_asm_line;
 	next_addr = tui_disassemble (gdbarch, single_asm_line,
-				     next_addr, 1);
+				     *map, next_addr, 1);
 	/* If there are some problems while disassembling exit.  */
 	if (next_addr <= old_next_addr)
 	  return pc;
@@ -347,8 +397,9 @@ tui_disasm_window::set_contents (struct gdbarch *arch,
 
   /* Get temporary table that will hold all strings (addr & insn).  */
   std::vector<tui_asm_line> asm_lines;
+  std::unique_ptr<addrmap_mutable> map = section_addrmap ();
   size_t addr_size = 0;
-  tui_disassemble (m_gdbarch, asm_lines, pc, max_lines, &addr_size);
+  tui_disassemble (m_gdbarch, asm_lines, *map, pc, max_lines, &addr_size);
 
   /* Align instructions to the same column.  */
   insn_pos = (1 + (addr_size / tab_len)) * tab_len;
-- 
2.51.0


  parent reply	other threads:[~2026-10-07  9:44 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-07  9:40 [PATCH v4 00/11] [gdb/tui] Some section hole handling fixes Tom de Vries
2026-10-07  9:40 ` [PATCH v4 01/11] [gdb/tui] Use early exit in tui_find_disassembly_address Tom de Vries
2026-10-07  9:40 ` [PATCH v4 02/11] [gdb/tui] Make low_prev loop-local " Tom de Vries
2026-10-07  9:40 ` [PATCH v4 03/11] [gdb/tui] Disassemble less often " Tom de Vries
2026-10-07  9:40 ` [PATCH v4 04/11] [gdb/tui] Fix buglet " Tom de Vries
2026-10-07  9:40 ` [PATCH v4 05/11] [gdb] Factor out update_section_map Tom de Vries
2026-10-07  9:40 ` [PATCH v4 06/11] [gdb] Add section_addrmap Tom de Vries
2026-10-07  9:40 ` [PATCH v4 07/11] [gdb] Extend addrmap::find with low/high results Tom de Vries
2026-10-07  9:40 ` [PATCH v4 08/11] [gdb] Make gdb_disassembly return number of insns Tom de Vries
2026-10-07  9:40 ` Tom de Vries [this message]
2026-10-07  9:40 ` [PATCH v4 10/11] [gdb] Don't stop disassembly at section hole Tom de Vries
2026-10-07  9:40 ` [PATCH v4 11/11] [RFC, gdb/tui] Don't scroll into non-code sections Tom de Vries

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261007094057.1811886-10-tdevries@suse.de \
    --to=tdevries@suse.de \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox