This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Add a new format letter to dump instructions backward


Hi all,

With the attached patch, I'd like to add a new format letter "j" to
the "x" command to dump instructions in backward direction.
This feature is basically equal to "ub" command in Windows debugger.

Below is the sample output:

(gdb) disas main
Dump of assembler code for function main(int, char**):
   0x4040a7 <+0>: push   %rbp
   0x4040a8 <+1>: mov    %rsp,%rbp
   0x4040ab <+4>: sub    $0x10,%rsp
   0x4040af <+8>: mov    %edi,-0x4(%rbp)
   0x4040b2 <+11>:  mov    %rsi,-0x10(%rbp)
=> 0x4040b6 <+15>:  lea    0x116(%rip),%rdi        # 0x4041d3
   0x4040bd <+22>:  callq  0x400e30 <puts@plt>
   0x4040c2 <+27>:  mov    $0x0,%eax
   0x4040c7 <+32>:  leaveq 
   0x4040c8 <+33>:  retq   
End of assembler dump.
(gdb) x/6j $rip
   0x4040a6 <previous_function(...)+119>:  retq   
   0x4040a7 <main(int, char**)>:  push   %rbp
   0x4040a8 <main(int, char**)+1>:  mov    %rsp,%rbp
   0x4040ab <main(int, char**)+4>:  sub    $0x10,%rsp
   0x4040af <main(int, char**)+8>:  mov    %edi,-0x4(%rbp)
   0x4040b2 <main(int, char**)+11>: mov    %rsi,-0x10(%rbp)
(gdb) x/2j
   0x4040a2 <previous_function(...)+115>:  add    %rdx,%rax
   0x4040a5 <previous_function(...)+118>:  leaveq 

Unlike forward disassemble, theoretically it's impossible to
solve a correct frame only using the code. In the example below, we cannot
decide whether the instruction before "pop" is "nop" or "mov".

<correct frame>
   0x4045bb <+89>:  eb b4  jmp    0x404571
   0x4045bd <+91>:  90     nop
   0x4045be <+92>:  5d     pop    %rbp
   0x4045bf <+93>:  c3     retq   

<incorrect frame>
   0x4045bc <+90>:  b4 90  mov    $0x90,%ah
   0x4045be <+92>:  5d     pop    %rbp
   0x4045bf <+93>:  c3     retq 

To solve this, the change takes the help of line number information.

I introduced a new function "disassemble_backward" which goes backward
by referring the start address of each line.If we go out of the symbol range, we cancel backtrack and dump
the smallest address with the message.

Tested on x86_64 GNU/Linux.


Thanks,
Toshihito

Attachment: ChangeLog
Description: Binary data

diff --git a/gdb/printcmd.c b/gdb/printcmd.c
index f5c4211..a6cf141 100644
--- a/gdb/printcmd.c
+++ b/gdb/printcmd.c
@@ -47,6 +47,7 @@
 #include "cli/cli-utils.h"
 #include "format.h"
 #include "source.h"
+#include "linespec.h"
 
 #ifdef TUI
 #include "tui/tui.h"		/* For tui_active et al.   */
@@ -785,6 +786,97 @@ print_address_demangle (const struct value_print_options *opts,
 }
 
 
+/* These empty functions are used in disassemble_backward to
+   suppress standard output during the call to gdbarch_print_insn. */
+
+static int
+null_fprintf (void *stream, const char *format ATTRIBUTE_UNUSED, ...)
+{
+  return 0;
+}
+
+static void
+null_print_address (bfd_vma addr, struct disassemble_info *info)
+{
+  return;
+}
+
+/* Rewind assembly from addr and return the start address after the given
+   number of lines are disassembled. To avoid disassembling in a wrong frame,
+   we get addresses in a correct frame using line information.
+   If we go out of the symbol range during disassembling, we return
+   the smallest address we've got so far. */
+
+static CORE_ADDR
+disassemble_backward(struct gdbarch *gdbarch, CORE_ADDR addr,
+                     int count, int *linesread)
+{
+  char addrstr[64];
+  struct symtabs_and_lines sals;
+  CORE_ADDR start_pc, end_pc, ret;
+
+  start_pc = end_pc = ret = 0;
+  *linesread = 0;
+
+  sprintf (addrstr, "*%p", (void *) (addr - 1));
+  sals = decode_line_with_last_displayed (addrstr, DECODE_LINE_FUNFIRSTLINE);
+  if (sals.nelts >= 1)
+    {
+      if ((!sals.sals[0].line)
+          || (!find_line_pc_range (sals.sals[0], &start_pc, &end_pc)))
+        {
+          printf_filtered (_("No line number information available "
+            "for address "));
+          wrap_here ("  ");
+          print_address (gdbarch, addr - 1, gdb_stdout);
+          printf_filtered ("\n");
+          start_pc = end_pc = 0;
+        }
+      xfree (sals.sals);
+    }
+
+  if (start_pc)
+    {
+      VEC (CORE_ADDR) *pcs = NULL;
+      struct disassemble_info di;
+      CORE_ADDR p;
+      int i;
+
+      VEC_reserve (CORE_ADDR, pcs, count);
+
+      di = gdb_disassemble_info (gdbarch, NULL);
+      di.fprintf_func = null_fprintf;
+      di.print_address_func = null_print_address;
+
+      p = start_pc;
+      for (i = 0; p < addr; ++i)
+        {
+          VEC_safe_push (CORE_ADDR, pcs, p);
+          p += gdbarch_print_insn (gdbarch, (CORE_ADDR)p, &di);
+        }
+
+      if (i >= count)
+        {
+          ret = VEC_index (CORE_ADDR, pcs, i - count);
+          *linesread = count;
+        }
+        else
+        {
+          int linesread_internal = 0;
+          ret = disassemble_backward (gdbarch, VEC_index (CORE_ADDR, pcs, 0),
+               count - i, &linesread_internal);
+          *linesread = i + linesread_internal;
+
+          /* Return the smallest valid address we've got
+             if the recursive call above failed. */
+          if (!ret)
+            ret = VEC_index (CORE_ADDR, pcs, 0);
+        }
+      VEC_free (CORE_ADDR, pcs);
+    }
+  return ret;
+}
+
 /* Examine data at address ADDR in format FMT.
    Fetch it from memory and print on gdb_stdout.  */
 
@@ -798,6 +890,7 @@ do_examine (struct format_data fmt, struct gdbarch *gdbarch, CORE_ADDR addr)
   int i;
   int maxelts;
   struct value_print_options opts;
+  CORE_ADDR addr_rewound = 0;
 
   format = fmt.format;
   size = fmt.size;
@@ -805,6 +898,24 @@ do_examine (struct format_data fmt, struct gdbarch *gdbarch, CORE_ADDR addr)
   next_gdbarch = gdbarch;
   next_address = addr;
 
+  if (format == 'j')
+    {
+      /* If 'j' is given, we get the address after going back
+         by 'count' instructions from addr and dump instructions
+         from it by setting format to 'i'.
+         At the end of this function, we adjust next_address
+         for subsequent command calls. */
+      int linesread = 0;
+      addr_rewound = next_address = disassemble_backward (gdbarch,
+           addr, count, &linesread);
+      if (!next_address)
+        {
+          return;
+        }
+      count = linesread;
+      format = 'i';
+    }
+
   /* Instruction format implies fetch single bytes
      regardless of the specified size.
      The case of strings is handled in decode_format, only explicit
@@ -913,6 +1024,9 @@ do_examine (struct format_data fmt, struct gdbarch *gdbarch, CORE_ADDR addr)
       printf_filtered ("\n");
       gdb_flush (gdb_stdout);
     }
+
+  if (addr_rewound)
+    next_address = addr_rewound;
 }
 
 static void
@@ -2518,8 +2632,8 @@ Examine memory: x/FMT ADDRESS.\n\
 ADDRESS is an expression for the memory address to examine.\n\
 FMT is a repeat count followed by a format letter and a size letter.\n\
 Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal),\n\
-  t(binary), f(float), a(address), i(instruction), c(char), s(string)\n\
-  and z(hex, zero padded on the left).\n\
+  t(binary), f(float), a(address), i(instruction), j(instruction backward),\n\
+  c(char), s(string) and z(hex, zero padded on the left).\n\
 Size letters are b(byte), h(halfword), w(word), g(giant, 8 bytes).\n\
 The specified number of objects of the specified size are printed\n\
 according to the format.\n\n\

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]