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 v3 4/5] [gdb] Handle sections when disassembling
Date: Mon, 21 Sep 2026 22:55:56 +0200	[thread overview]
Message-ID: <20260921205557.2706300-5-tdevries@suse.de> (raw)
In-Reply-To: <20260921205557.2706300-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.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=34617
---
 gdb/disasm.c         | 72 ++++++++++++++++++++++++++++++-------
 gdb/tui/tui-disasm.c | 85 ++++++++++++++++++++++++++++++++++----------
 2 files changed, 125 insertions(+), 32 deletions(-)

diff --git a/gdb/disasm.c b/gdb/disasm.c
index 01361fe4323..9e8334b065e 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
@@ -524,7 +525,7 @@ dump_insns (struct gdbarch *gdbarch,
 
    N.B. This view is deprecated.  */
 
-static void
+static int
 do_mixed_source_and_assembly_deprecated
   (struct gdbarch *gdbarch, struct ui_out *uiout,
    struct symtab *symtab,
@@ -669,6 +670,8 @@ do_mixed_source_and_assembly_deprecated
       if (how_many >= 0 && num_displayed >= how_many)
 	break;
     }
+
+  return num_displayed;
 }
 
 /* The idea here is to present a source-O-centric view of a
@@ -676,7 +679,7 @@ do_mixed_source_and_assembly_deprecated
    in source order, with (possibly) out of order assembly
    immediately following.  */
 
-static void
+static int
 do_mixed_source_and_assembly (struct gdbarch *gdbarch,
 			      struct ui_out *uiout,
 			      struct symtab *main_symtab,
@@ -906,16 +909,18 @@ do_mixed_source_and_assembly (struct gdbarch *gdbarch,
       last_symtab = sal.symtab;
       last_line = sal.line;
     }
+
+  return num_displayed;
 }
 
-static void
+static int
 do_assembly_only (struct gdbarch *gdbarch, struct ui_out *uiout,
 		  CORE_ADDR low, CORE_ADDR high,
 		  int how_many, gdb_disassembly_flags flags)
 {
   ui_out_emit_list list_emitter (uiout, "asm_insns");
 
-  dump_insns (gdbarch, uiout, low, high, how_many, flags, NULL);
+  return dump_insns (gdbarch, uiout, low, high, how_many, flags, NULL);
 }
 
 /* Combine implicit and user disassembler options and return them
@@ -1141,10 +1146,12 @@ gdb_disassembler::print_insn (CORE_ADDR memaddr,
   return length;
 }
 
-void
-gdb_disassembly (struct gdbarch *gdbarch, struct ui_out *uiout,
-		 gdb_disassembly_flags flags, int how_many,
-		 CORE_ADDR low, CORE_ADDR high)
+/* Helper function for gdb_disassembly.  */
+
+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;
@@ -1155,19 +1162,58 @@ gdb_disassembly (struct gdbarch *gdbarch, struct ui_out *uiout,
   if (symtab != NULL && symtab->linetable () != NULL)
     nlines = symtab->linetable ()->nitems;
 
+  int num_displayed = 0;
   if (!(flags & (DISASSEMBLY_SOURCE_DEPRECATED | DISASSEMBLY_SOURCE))
       || nlines <= 0)
-    do_assembly_only (gdbarch, uiout, low, high, how_many, flags);
+    num_displayed
+      = do_assembly_only (gdbarch, uiout, low, high, how_many, flags);
 
   else if (flags & DISASSEMBLY_SOURCE)
-    do_mixed_source_and_assembly (gdbarch, uiout, symtab, low, high,
-				  how_many, flags);
+    num_displayed
+      = do_mixed_source_and_assembly (gdbarch, uiout, symtab, low, high,
+				      how_many, flags);
 
   else if (flags & DISASSEMBLY_SOURCE_DEPRECATED)
-    do_mixed_source_and_assembly_deprecated (gdbarch, uiout, symtab,
-					     low, high, how_many, flags);
+    num_displayed
+      = do_mixed_source_and_assembly_deprecated (gdbarch, uiout, symtab,
+						 low, high, how_many, flags);
 
   gdb_flush (gdb_stdout);
+  return num_displayed;
+}
+
+void
+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;
+
+  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);
+
+      if (update_how_many)
+	{
+	  how_many -= res;
+	  if (how_many <= 0)
+	    break;
+	}
+
+      low = tmp_high;
+    }
 }
 
 /* Print the instruction at address MEMADDR in debugged memory,
diff --git a/gdb/tui/tui-disasm.c b/gdb/tui/tui-disasm.c
index 1a1866003dd..26afc4bd978 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.  */
+	      tal.insn = "(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.
+
+   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)
+{
+  std::unique_ptr<addrmap_mutable> map = section_addrmap ();
+
+  /* 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
-- 
2.51.0


  parent reply	other threads:[~2026-09-21 20:57 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-21 20:55 [PATCH v3 0/5] [gdb/tui] Some section hole handling fixes Tom de Vries
2026-09-21 20:55 ` [PATCH v3 1/5] [gdb] Factor out update_section_map Tom de Vries
2026-09-21 20:55 ` [PATCH v3 2/5] [gdb] Add section_addrmap Tom de Vries
2026-09-21 20:55 ` [PATCH v3 3/5] [gdb] Extend addrmap::find with low/high results Tom de Vries
2026-09-21 20:55 ` Tom de Vries [this message]
2026-09-21 20:55 ` [PATCH v3 5/5] [gdb] Don't stop disassembly at section hole 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=20260921205557.2706300-5-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