Add negative repeat count to 'x' command
This change adds support for specifying a negative repeat count to all the formats of the 'x' command to examine memory backward. A new testcase 'examine-backward' is added to cover this new feature. Here's the example output from the new feature: <format 'i'> (gdb) bt #0 Func1 (n=42, p=0x40432e "hogehoge") at main.cpp:5 #1 0x00000000004041fa in main (argc=1, argv=0x7fffffffdff8) at main.cpp:19 (gdb) x/-4i 0x4041fa 0x4041e5 <main(int, char**)+11>: mov %rsi,-0x10(%rbp) 0x4041e9 <main(int, char**)+15>: lea 0x13e(%rip),%rsi 0x4041f0 <main(int, char**)+22>: mov $0x2a,%edi 0x4041f5 <main(int, char**)+27>: callq 0x404147 <format 'x'> (gdb) x/-4xw 0x404200 0x4041f0 <main(int, char**)+22>: 0x00002abf 0xff4de800 0x76e8ffff 0xb8ffffff (gdb) x/-4 0x4041e0 <main(int, char**)+6>: 0x7d8910ec 0x758948fc 0x358d48f0 0x0000013e gdb/ChangeLog: * NEWS: Mention that GDB now supports a negative repeat count in the 'x' command. * printcmd.c (decode_format): Allow '-' in the parameter "string_ptr" to accept a negative repeat count. (find_instruction_backward): New function. (read_memory_backward): New function. (integer_is_zero): New function. (find_string_backward): New function. (do_examine): Use new functions to examine memory backward. (_initialize_printcmd): Mention that 'x' command supports a negative repeat count. gdb/doc/ChangeLog: * gdb.texinfo (Examining Memory): Document negative repeat count in the 'x' command. gdb/testsuite/ChangeLog: * gdb.base/examine-backward.c: New file. * gdb.base/examine-backward.exp: New file.
This commit is contained in:
parent
c040f3fb55
commit
bb556f1fac
8 changed files with 741 additions and 3 deletions
|
@ -1,3 +1,17 @@
|
|||
2016-06-09 Toshihito Kikuchi <k.toshihito@yahoo.de>
|
||||
|
||||
* NEWS: Mention that GDB now supports a negative repeat count in
|
||||
the 'x' command.
|
||||
* printcmd.c (decode_format): Allow '-' in the parameter
|
||||
"string_ptr" to accept a negative repeat count.
|
||||
(find_instruction_backward): New function.
|
||||
(read_memory_backward): New function.
|
||||
(integer_is_zero): New function.
|
||||
(find_string_backward): New function.
|
||||
(do_examine): Use new functions to examine memory backward.
|
||||
(_initialize_printcmd): Mention that 'x' command supports a negative
|
||||
repeat count.
|
||||
|
||||
2016-06-09 Toshihito Kikuchi <k.toshihito@yahoo.de>
|
||||
|
||||
* MAINTAINERS (Write After Approval): Add Toshihito Kikuchi.
|
||||
|
|
14
gdb/NEWS
14
gdb/NEWS
|
@ -3,6 +3,20 @@
|
|||
|
||||
*** Changes since GDB 7.11
|
||||
|
||||
* GDB now supports a negative repeat count in the 'x' command to examine
|
||||
memory backward from the given address. For example:
|
||||
|
||||
(gdb) bt
|
||||
#0 Func1 (n=42, p=0x40061c "hogehoge") at main.cpp:4
|
||||
#1 0x400580 in main (argc=1, argv=0x7fffffffe5c8) at main.cpp:8
|
||||
(gdb) x/-5i 0x0000000000400580
|
||||
0x40056a <main(int, char**)+8>: mov %edi,-0x4(%rbp)
|
||||
0x40056d <main(int, char**)+11>: mov %rsi,-0x10(%rbp)
|
||||
0x400571 <main(int, char**)+15>: mov $0x40061c,%esi
|
||||
0x400576 <main(int, char**)+20>: mov $0x2a,%edi
|
||||
0x40057b <main(int, char**)+25>:
|
||||
callq 0x400536 <Func1(int, char const*)>
|
||||
|
||||
* Fortran: Support structures with fields of dynamic types and
|
||||
arrays of dynamic types.
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
2016-06-09 Toshihito Kikuchi <k.toshihito@yahoo.de>
|
||||
|
||||
* gdb.texinfo (Examining Memory): Document negative repeat
|
||||
count in the 'x' command.
|
||||
|
||||
2016-06-06 Simon Marchi <simon.marchi@ericsson.com>
|
||||
|
||||
* gdb.texinfo (GDB/MI Async Records): Document method and
|
||||
|
|
|
@ -9295,7 +9295,8 @@ Several commands set convenient defaults for @var{addr}.
|
|||
@table @r
|
||||
@item @var{n}, the repeat count
|
||||
The repeat count is a decimal integer; the default is 1. It specifies
|
||||
how much memory (counting by units @var{u}) to display.
|
||||
how much memory (counting by units @var{u}) to display. If a negative
|
||||
number is specified, memory is examined backward from @var{addr}.
|
||||
@c This really is **decimal**; unaffected by 'set radix' as of GDB
|
||||
@c 4.1.2.
|
||||
|
||||
|
@ -9350,6 +9351,10 @@ starting at address @code{0x54320}. @samp{x/4xw $sp} prints the four
|
|||
words (@samp{w}) of memory above the stack pointer (here, @samp{$sp};
|
||||
@pxref{Registers, ,Registers}) in hexadecimal (@samp{x}).
|
||||
|
||||
You can also specify a negative repeat count to examine memory backward
|
||||
from the given address. For example, @samp{x/-3uh 0x54320} prints three
|
||||
halfwords (@code{h}) at @code{0x54314}, @code{0x54328}, and @code{0x5431c}.
|
||||
|
||||
Since the letters indicating unit sizes are all distinct from the
|
||||
letters specifying output formats, you do not have to remember whether
|
||||
unit size or format comes first; either order works. The output
|
||||
|
@ -9366,6 +9371,13 @@ follow the last instruction that is within the count. The command
|
|||
@code{disassemble} gives an alternative way of inspecting machine
|
||||
instructions; see @ref{Machine Code,,Source and Machine Code}.
|
||||
|
||||
If a negative repeat count is specified for the formats @samp{s} or @samp{i},
|
||||
the command displays null-terminated strings or instructions before the given
|
||||
address as many as the absolute value of the given number. For the @samp{i}
|
||||
format, we use line number information in the debug info to accurately locate
|
||||
instruction boundaries while disassembling backward. If line info is not
|
||||
available, the command stops examining memory with an error message.
|
||||
|
||||
All the defaults for the arguments to @code{x} are designed to make it
|
||||
easy to continue scanning memory with minimal specifications each time
|
||||
you use @code{x}. For example, after you have inspected three machine
|
||||
|
|
262
gdb/printcmd.c
262
gdb/printcmd.c
|
@ -186,8 +186,13 @@ decode_format (const char **string_ptr, int oformat, int osize)
|
|||
val.count = 1;
|
||||
val.raw = 0;
|
||||
|
||||
if (*p == '-')
|
||||
{
|
||||
val.count = -1;
|
||||
p++;
|
||||
}
|
||||
if (*p >= '0' && *p <= '9')
|
||||
val.count = atoi (p);
|
||||
val.count *= atoi (p);
|
||||
while (*p >= '0' && *p <= '9')
|
||||
p++;
|
||||
|
||||
|
@ -785,6 +790,221 @@ print_address_demangle (const struct value_print_options *opts,
|
|||
}
|
||||
|
||||
|
||||
/* Find the address of the instruction that is INST_COUNT instructions before
|
||||
the instruction at ADDR.
|
||||
Since some architectures have variable-length instructions, we can't just
|
||||
simply subtract INST_COUNT * INSN_LEN from ADDR. Instead, we use line
|
||||
number information to locate the nearest known instruction boundary,
|
||||
and disassemble forward from there. If we go out of the symbol range
|
||||
during disassembling, we return the lowest address we've got so far and
|
||||
set the number of instructions read to INST_READ. */
|
||||
|
||||
static CORE_ADDR
|
||||
find_instruction_backward (struct gdbarch *gdbarch, CORE_ADDR addr,
|
||||
int inst_count, int *inst_read)
|
||||
{
|
||||
/* The vector PCS is used to store instruction addresses within
|
||||
a pc range. */
|
||||
CORE_ADDR loop_start, loop_end, p;
|
||||
VEC (CORE_ADDR) *pcs = NULL;
|
||||
struct symtab_and_line sal;
|
||||
struct cleanup *cleanup = make_cleanup (VEC_cleanup (CORE_ADDR), &pcs);
|
||||
|
||||
*inst_read = 0;
|
||||
loop_start = loop_end = addr;
|
||||
|
||||
/* In each iteration of the outer loop, we get a pc range that ends before
|
||||
LOOP_START, then we count and store every instruction address of the range
|
||||
iterated in the loop.
|
||||
If the number of instructions counted reaches INST_COUNT, return the
|
||||
stored address that is located INST_COUNT instructions back from ADDR.
|
||||
If INST_COUNT is not reached, we subtract the number of counted
|
||||
instructions from INST_COUNT, and go to the next iteration. */
|
||||
do
|
||||
{
|
||||
VEC_truncate (CORE_ADDR, pcs, 0);
|
||||
sal = find_pc_sect_line (loop_start, NULL, 1);
|
||||
if (sal.line <= 0)
|
||||
{
|
||||
/* We reach here when line info is not available. In this case,
|
||||
we print a message and just exit the loop. The return value
|
||||
is calculated after the loop. */
|
||||
printf_filtered (_("No line number information available "
|
||||
"for address "));
|
||||
wrap_here (" ");
|
||||
print_address (gdbarch, loop_start - 1, gdb_stdout);
|
||||
printf_filtered ("\n");
|
||||
break;
|
||||
}
|
||||
|
||||
loop_end = loop_start;
|
||||
loop_start = sal.pc;
|
||||
|
||||
/* This loop pushes instruction addresses in the range from
|
||||
LOOP_START to LOOP_END. */
|
||||
for (p = loop_start; p < loop_end;)
|
||||
{
|
||||
VEC_safe_push (CORE_ADDR, pcs, p);
|
||||
p += gdb_insn_length (gdbarch, p);
|
||||
}
|
||||
|
||||
inst_count -= VEC_length (CORE_ADDR, pcs);
|
||||
*inst_read += VEC_length (CORE_ADDR, pcs);
|
||||
}
|
||||
while (inst_count > 0);
|
||||
|
||||
/* After the loop, the vector PCS has instruction addresses of the last
|
||||
source line we processed, and INST_COUNT has a negative value.
|
||||
We return the address at the index of -INST_COUNT in the vector for
|
||||
the reason below.
|
||||
Let's assume the following instruction addresses and run 'x/-4i 0x400e'.
|
||||
Line X of File
|
||||
0x4000
|
||||
0x4001
|
||||
0x4005
|
||||
Line Y of File
|
||||
0x4009
|
||||
0x400c
|
||||
=> 0x400e
|
||||
0x4011
|
||||
find_instruction_backward is called with INST_COUNT = 4 and expected to
|
||||
return 0x4001. When we reach here, INST_COUNT is set to -1 because
|
||||
it was subtracted by 2 (from Line Y) and 3 (from Line X). The value
|
||||
4001 is located at the index 1 of the last iterated line (= Line X),
|
||||
which is simply calculated by -INST_COUNT.
|
||||
The case when the length of PCS is 0 means that we reached an area for
|
||||
which line info is not available. In such case, we return LOOP_START,
|
||||
which was the lowest instruction address that had line info. */
|
||||
p = VEC_length (CORE_ADDR, pcs) > 0
|
||||
? VEC_index (CORE_ADDR, pcs, -inst_count)
|
||||
: loop_start;
|
||||
|
||||
/* INST_READ includes all instruction addresses in a pc range. Need to
|
||||
exclude the beginning part up to the address we're returning. That
|
||||
is, exclude {0x4000} in the example above. */
|
||||
if (inst_count < 0)
|
||||
*inst_read += inst_count;
|
||||
|
||||
do_cleanups (cleanup);
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Backward read LEN bytes of target memory from address MEMADDR + LEN,
|
||||
placing the results in GDB's memory from MYADDR + LEN. Returns
|
||||
a count of the bytes actually read. */
|
||||
|
||||
static int
|
||||
read_memory_backward (struct gdbarch *gdbarch,
|
||||
CORE_ADDR memaddr, gdb_byte *myaddr, int len)
|
||||
{
|
||||
int errcode;
|
||||
int nread; /* Number of bytes actually read. */
|
||||
|
||||
/* First try a complete read. */
|
||||
errcode = target_read_memory (memaddr, myaddr, len);
|
||||
if (errcode == 0)
|
||||
{
|
||||
/* Got it all. */
|
||||
nread = len;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Loop, reading one byte at a time until we get as much as we can. */
|
||||
memaddr += len;
|
||||
myaddr += len;
|
||||
for (nread = 0; nread < len; ++nread)
|
||||
{
|
||||
errcode = target_read_memory (--memaddr, --myaddr, 1);
|
||||
if (errcode != 0)
|
||||
{
|
||||
/* The read was unsuccessful, so exit the loop. */
|
||||
printf_filtered (_("Cannot access memory at address %s\n"),
|
||||
paddress (gdbarch, memaddr));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nread;
|
||||
}
|
||||
|
||||
/* Returns true if X (which is LEN bytes wide) is the number zero. */
|
||||
|
||||
static int
|
||||
integer_is_zero (const gdb_byte *x, int len)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while (i < len && x[i] == 0)
|
||||
++i;
|
||||
return (i == len);
|
||||
}
|
||||
|
||||
/* Find the start address of a string in which ADDR is included.
|
||||
Basically we search for '\0' and return the next address,
|
||||
but if OPTIONS->PRINT_MAX is smaller than the length of a string,
|
||||
we stop searching and return the address to print characters as many as
|
||||
PRINT_MAX from the string. */
|
||||
|
||||
static CORE_ADDR
|
||||
find_string_backward (struct gdbarch *gdbarch,
|
||||
CORE_ADDR addr, int count, int char_size,
|
||||
const struct value_print_options *options,
|
||||
int *strings_counted)
|
||||
{
|
||||
const int chunk_size = 0x20;
|
||||
gdb_byte *buffer = NULL;
|
||||
struct cleanup *cleanup = NULL;
|
||||
int read_error = 0;
|
||||
int chars_read = 0;
|
||||
int chars_to_read = chunk_size;
|
||||
int chars_counted = 0;
|
||||
int count_original = count;
|
||||
CORE_ADDR string_start_addr = addr;
|
||||
|
||||
gdb_assert (char_size == 1 || char_size == 2 || char_size == 4);
|
||||
buffer = (gdb_byte *) xmalloc (chars_to_read * char_size);
|
||||
cleanup = make_cleanup (xfree, buffer);
|
||||
while (count > 0 && read_error == 0)
|
||||
{
|
||||
int i;
|
||||
|
||||
addr -= chars_to_read * char_size;
|
||||
chars_read = read_memory_backward (gdbarch, addr, buffer,
|
||||
chars_to_read * char_size);
|
||||
chars_read /= char_size;
|
||||
read_error = (chars_read == chars_to_read) ? 0 : 1;
|
||||
/* Searching for '\0' from the end of buffer in backward direction. */
|
||||
for (i = 0; i < chars_read && count > 0 ; ++i, ++chars_counted)
|
||||
{
|
||||
int offset = (chars_to_read - i - 1) * char_size;
|
||||
|
||||
if (integer_is_zero (buffer + offset, char_size)
|
||||
|| chars_counted == options->print_max)
|
||||
{
|
||||
/* Found '\0' or reached print_max. As OFFSET is the offset to
|
||||
'\0', we add CHAR_SIZE to return the start address of
|
||||
a string. */
|
||||
--count;
|
||||
string_start_addr = addr + offset + char_size;
|
||||
chars_counted = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Update STRINGS_COUNTED with the actual number of loaded strings. */
|
||||
*strings_counted = count_original - count;
|
||||
|
||||
if (read_error != 0)
|
||||
{
|
||||
/* In error case, STRING_START_ADDR is pointing to the string that
|
||||
was last successfully loaded. Rewind the partially loaded string. */
|
||||
string_start_addr -= chars_counted * char_size;
|
||||
}
|
||||
|
||||
do_cleanups (cleanup);
|
||||
return string_start_addr;
|
||||
}
|
||||
|
||||
/* Examine data at address ADDR in format FMT.
|
||||
Fetch it from memory and print on gdb_stdout. */
|
||||
|
||||
|
@ -798,6 +1018,8 @@ do_examine (struct format_data fmt, struct gdbarch *gdbarch, CORE_ADDR addr)
|
|||
int i;
|
||||
int maxelts;
|
||||
struct value_print_options opts;
|
||||
int need_to_update_next_address = 0;
|
||||
CORE_ADDR addr_rewound = 0;
|
||||
|
||||
format = fmt.format;
|
||||
size = fmt.size;
|
||||
|
@ -868,6 +1090,38 @@ do_examine (struct format_data fmt, struct gdbarch *gdbarch, CORE_ADDR addr)
|
|||
|
||||
get_formatted_print_options (&opts, format);
|
||||
|
||||
if (count < 0)
|
||||
{
|
||||
/* This is the negative repeat count case.
|
||||
We rewind the address based on the given repeat count and format,
|
||||
then examine memory from there in forward direction. */
|
||||
|
||||
count = -count;
|
||||
if (format == 'i')
|
||||
{
|
||||
next_address = find_instruction_backward (gdbarch, addr, count,
|
||||
&count);
|
||||
}
|
||||
else if (format == 's')
|
||||
{
|
||||
next_address = find_string_backward (gdbarch, addr, count,
|
||||
TYPE_LENGTH (val_type),
|
||||
&opts, &count);
|
||||
}
|
||||
else
|
||||
{
|
||||
next_address = addr - count * TYPE_LENGTH (val_type);
|
||||
}
|
||||
|
||||
/* The following call to print_formatted updates next_address in every
|
||||
iteration. In backward case, we store the start address here
|
||||
and update next_address with it before exiting the function. */
|
||||
addr_rewound = (format == 's'
|
||||
? next_address - TYPE_LENGTH (val_type)
|
||||
: next_address);
|
||||
need_to_update_next_address = 1;
|
||||
}
|
||||
|
||||
/* Print as many objects as specified in COUNT, at most maxelts per line,
|
||||
with the address of the next one at the start of each line. */
|
||||
|
||||
|
@ -913,6 +1167,9 @@ do_examine (struct format_data fmt, struct gdbarch *gdbarch, CORE_ADDR addr)
|
|||
printf_filtered ("\n");
|
||||
gdb_flush (gdb_stdout);
|
||||
}
|
||||
|
||||
if (need_to_update_next_address)
|
||||
next_address = addr_rewound;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -2522,7 +2779,8 @@ Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal),\n\
|
|||
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\
|
||||
according to the format. If a negative number is specified, memory is\n\
|
||||
examined backward from the address.\n\n\
|
||||
Defaults for format and size letters are those previously used.\n\
|
||||
Default count is 1. Default address is following last thing printed\n\
|
||||
with this command or \"print\"."));
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
2016-06-09 Toshihito Kikuchi <k.toshihito@yahoo.de>
|
||||
|
||||
* gdb.base/examine-backward.c: New file.
|
||||
* gdb.base/examine-backward.exp: New file.
|
||||
|
||||
2016-06-06 Simon Marchi <simon.marchi@ericsson.com>
|
||||
|
||||
* gdb.mi/mi-record-changed.exp: Adjust =record-started output
|
||||
|
|
106
gdb/testsuite/gdb.base/examine-backward.c
Normal file
106
gdb/testsuite/gdb.base/examine-backward.c
Normal file
|
@ -0,0 +1,106 @@
|
|||
/* This testcase is part of GDB, the GNU debugger.
|
||||
|
||||
Copyright 2015-2016 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/>. */
|
||||
|
||||
/*
|
||||
Define TestStrings, TestStringsH, and TestStringsW to test utf8, utf16,
|
||||
and utf32 strings respectively.
|
||||
To avoid compile errors due to old compiler mode, we don't use string
|
||||
literals. The content of each array is the same as followings:
|
||||
|
||||
const char TestStrings[] = {
|
||||
"ABCD"
|
||||
"EFGHIJKLMNOPQRSTUVWXYZ\0"
|
||||
"\0"
|
||||
"\0"
|
||||
"\u307B\u3052\u307B\u3052\0"
|
||||
"012345678901234567890123456789\0"
|
||||
"!!!!!!\0"
|
||||
};
|
||||
*/
|
||||
|
||||
const char TestStrings[] = {
|
||||
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
|
||||
0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
|
||||
0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
||||
0x59, 0x5a, 0x00, 0x00, 0x00, 0xe3, 0x81, 0xbb,
|
||||
0xe3, 0x81, 0x92, 0xe3, 0x81, 0xbb, 0xe3, 0x81,
|
||||
0x92, 0x00, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
|
||||
0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33,
|
||||
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31,
|
||||
0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
|
||||
0x00, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x00,
|
||||
0x00
|
||||
};
|
||||
|
||||
const short TestStringsH[] = {
|
||||
0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048,
|
||||
0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050,
|
||||
0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
|
||||
0x0059, 0x005a, 0x0000, 0x0000, 0x0000, 0x307b, 0x3052, 0x307b,
|
||||
0x3052, 0x0000, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035,
|
||||
0x0036, 0x0037, 0x0038, 0x0039, 0x0030, 0x0031, 0x0032, 0x0033,
|
||||
0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x0030, 0x0031,
|
||||
0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039,
|
||||
0x0000, 0x0021, 0x0021, 0x0021, 0x0021, 0x0021, 0x0021, 0x0000,
|
||||
0x0000
|
||||
};
|
||||
|
||||
const int TestStringsW[] = {
|
||||
0x00000041, 0x00000042, 0x00000043, 0x00000044,
|
||||
0x00000045, 0x00000046, 0x00000047, 0x00000048,
|
||||
0x00000049, 0x0000004a, 0x0000004b, 0x0000004c,
|
||||
0x0000004d, 0x0000004e, 0x0000004f, 0x00000050,
|
||||
0x00000051, 0x00000052, 0x00000053, 0x00000054,
|
||||
0x00000055, 0x00000056, 0x00000057, 0x00000058,
|
||||
0x00000059, 0x0000005a, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x0000307b, 0x00003052, 0x0000307b,
|
||||
0x00003052, 0x00000000, 0x00000030, 0x00000031,
|
||||
0x00000032, 0x00000033, 0x00000034, 0x00000035,
|
||||
0x00000036, 0x00000037, 0x00000038, 0x00000039,
|
||||
0x00000030, 0x00000031, 0x00000032, 0x00000033,
|
||||
0x00000034, 0x00000035, 0x00000036, 0x00000037,
|
||||
0x00000038, 0x00000039, 0x00000030, 0x00000031,
|
||||
0x00000032, 0x00000033, 0x00000034, 0x00000035,
|
||||
0x00000036, 0x00000037, 0x00000038, 0x00000039,
|
||||
0x00000000, 0x00000021, 0x00000021, 0x00000021,
|
||||
0x00000021, 0x00000021, 0x00000021, 0x00000000,
|
||||
0x00000000
|
||||
};
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
/* Backward disassemble test requires at least 20 instructions in
|
||||
this function. Adding a simple bubble sort. */
|
||||
int i, j;
|
||||
int n[] = {3, 1, 4, 1, 5, 9};
|
||||
int len = sizeof (n) / sizeof (n[0]);
|
||||
|
||||
for (i = 0; i < len - 1; ++i)
|
||||
{
|
||||
for (j = i; j < len; ++j)
|
||||
{
|
||||
if (n[j] < n[i])
|
||||
{
|
||||
int tmp = n[i];
|
||||
n[i] = n[j];
|
||||
n[j] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 42;
|
||||
}
|
324
gdb/testsuite/gdb.base/examine-backward.exp
Normal file
324
gdb/testsuite/gdb.base/examine-backward.exp
Normal file
|
@ -0,0 +1,324 @@
|
|||
# Copyright 2015-2016 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/>.
|
||||
|
||||
# This testsuite is to test examining memory backward by specifying a negative
|
||||
# number in the 'x' command.
|
||||
|
||||
standard_testfile
|
||||
if { [prepare_for_testing "failed to prepare for examine-backward" \
|
||||
${testfile} ${srcfile}] } {
|
||||
return -1
|
||||
}
|
||||
|
||||
if ![runto_main] {
|
||||
untested "could not run to main"
|
||||
return -1
|
||||
}
|
||||
|
||||
proc get_first_mapped_address {} {
|
||||
global gdb_prompt
|
||||
|
||||
set addr "0"
|
||||
gdb_test_multiple "info proc mappings" "info proc mappings" {
|
||||
-re "objfile\[\r\n\t \]+(0x\[0-9a-fA-F\]+).*\[\r\n\]*$gdb_prompt $" {
|
||||
set addr $expect_out(1,string)
|
||||
}
|
||||
-re "$gdb_prompt $" {
|
||||
unsupported "Current target does not support 'info proc mappings'"
|
||||
}
|
||||
}
|
||||
return ${addr}
|
||||
}
|
||||
|
||||
with_test_prefix "invalid format" {
|
||||
gdb_test "x/- 10xb main" "Invalid number \"10xb\"\." \
|
||||
"a whitespace after a leading hyphen"
|
||||
gdb_test "x/--10xb main" "Invalid number \"10xb\"\." \
|
||||
"double hyphen"
|
||||
gdb_test "x/-a10xb main" "Invalid number \"10xb\"\." \
|
||||
"an alphabet after a leading hyphen"
|
||||
gdb_test_no_output "x/-0i main" "zero with backward disassemble"
|
||||
gdb_test_no_output "x/-0sh main" "zero with backward examine string"
|
||||
}
|
||||
|
||||
with_test_prefix "memory page boundary" {
|
||||
set boundary [get_first_mapped_address]
|
||||
if {![is_address_zero_readable] && $boundary != 0} {
|
||||
gdb_test_no_output "set print elements 0"
|
||||
gdb_test_sequence "x/3s ${boundary}" "take 3 strings forward" {
|
||||
"0x"
|
||||
"0x"
|
||||
"0x"
|
||||
}
|
||||
gdb_test_sequence "x/-4s" "take 4 strings backward" {
|
||||
"Cannot access memory at address 0x"
|
||||
"0x"
|
||||
"0x"
|
||||
"0x"
|
||||
}
|
||||
gdb_test_sequence "x/3s ${boundary}" "take 3 strings forward again" {
|
||||
"0x"
|
||||
"0x"
|
||||
"0x"
|
||||
}
|
||||
gdb_test_sequence "x/-3s" "take 3 strings backward" {
|
||||
"Cannot access memory at address 0x"
|
||||
"0x"
|
||||
"0x"
|
||||
"0x"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
with_test_prefix "address zero boundary" {
|
||||
if {[is_address_zero_readable]} {
|
||||
set address_zero "0x0"
|
||||
set byte "\t0x\[0-9a-f\]+"
|
||||
gdb_test "x/3xb ${address_zero}" \
|
||||
"0x\[0-9a-f\]+00.*:${byte}${byte}${byte}" \
|
||||
"examine 3 bytes forward from ${address_zero}"
|
||||
gdb_test "x/-6x" \
|
||||
"0x\[0-9a-f\]+fd.*:${byte}${byte}${byte}${byte}${byte}${byte}" \
|
||||
"examine 6 bytes backward"
|
||||
gdb_test "x/-3x ${address_zero}" \
|
||||
"0x\[0-9a-f\]+fd.*:${byte}${byte}${byte}" \
|
||||
"examine 3 bytes backward from ${address_zero}"
|
||||
}
|
||||
}
|
||||
|
||||
gdb_test_no_output "set charset ASCII"
|
||||
|
||||
with_test_prefix "char-width=1, print-max=20" {
|
||||
gdb_test_no_output "set print elements 20"
|
||||
gdb_test_sequence "x/6s &TestStrings" "take 6 strings forward" {
|
||||
"\"ABCDEFGHIJKLMNOPQRST\"\.\.\."
|
||||
"\"UVWXYZ\""
|
||||
"\"\""
|
||||
"\"\""
|
||||
"\"[^\"]+\""
|
||||
"\"01234567890123456789\"\.\.\."
|
||||
}
|
||||
gdb_test "x/-1xb" "0x39" "take 1 char backward"
|
||||
gdb_test_sequence "x/-6s" "take 6 strings backward" {
|
||||
"\"ABCDEFGHIJKLMNOPQRST\"\.\.\."
|
||||
"\"UVWXYZ\""
|
||||
"\"\""
|
||||
"\"\""
|
||||
"\"[^\"]+\""
|
||||
"\"01234567890123456789\"\.\.\."
|
||||
}
|
||||
gdb_test_sequence "x/6s &TestStrings" "take 6 strings forward again" {
|
||||
"\"ABCDEFGHIJKLMNOPQRST\"\.\.\."
|
||||
"\"UVWXYZ\""
|
||||
"\"\""
|
||||
"\"\""
|
||||
"\"[^\"]+\""
|
||||
"\"01234567890123456789\"\.\.\."
|
||||
}
|
||||
gdb_test "x/-xb" "0x39" "take 1 char backward again"
|
||||
gdb_test "x/-s" "\"01234567890123456789\"\.\.\." \
|
||||
"take 1 string backward (1/6)"
|
||||
gdb_test "x/-s" "\".+\"" \
|
||||
"take 1 string backward (2/6)"
|
||||
gdb_test "x/-s" "\"\"" \
|
||||
"take 1 string backward (3/6)"
|
||||
gdb_test "x/-s" "\"\"" \
|
||||
"take 1 string backward (4/6)"
|
||||
gdb_test "x/-s" "\"GHIJKLMNOPQRSTUVWXYZ\"" \
|
||||
"take 1 string backward (5/6)"
|
||||
gdb_test "x/-s" "\"ABCDEFGHIJKLMNOPQRST\"\.\.\." \
|
||||
"take 1 string backward (6/6)"
|
||||
}
|
||||
|
||||
with_test_prefix "char-width=2, print-max=20" {
|
||||
gdb_test_no_output "set print elements 20"
|
||||
gdb_test_sequence "x/6sh &TestStringsH" "take 6 strings forward" {
|
||||
"u\"ABCDEFGHIJKLMNOPQRST\"\.\.\."
|
||||
"u\"UVWXYZ\""
|
||||
"u\"\""
|
||||
"u\"\""
|
||||
"u\"[^\"]+\""
|
||||
"u\"01234567890123456789\"\.\.\."
|
||||
}
|
||||
gdb_test "x/-1xh" "0x0039" "take 1 char backward"
|
||||
gdb_test_sequence "x/-6sh" "take 6 strings backward" {
|
||||
"u\"ABCDEFGHIJKLMNOPQRST\"\.\.\."
|
||||
"u\"UVWXYZ\""
|
||||
"u\"\""
|
||||
"u\"\""
|
||||
"u\"[^\"]+\""
|
||||
"u\"01234567890123456789\"\.\.\."
|
||||
}
|
||||
gdb_test_sequence "x/6sh &TestStringsH" "take 6 strings forward again" {
|
||||
"u\"ABCDEFGHIJKLMNOPQRST\"\.\.\."
|
||||
"u\"UVWXYZ\""
|
||||
"u\"\""
|
||||
"u\"\""
|
||||
"u\"[^\"]+\""
|
||||
"u\"01234567890123456789\"\.\.\."
|
||||
}
|
||||
gdb_test "x/-xh" "0x0039" "take 1 char backward again"
|
||||
gdb_test "x/-sh" "u\"01234567890123456789\"\.\.\." \
|
||||
"take 1 string backward (1/6)"
|
||||
gdb_test "x/-sh" "u\".+\"" \
|
||||
"take 1 string backward (2/6)"
|
||||
gdb_test "x/-sh" "u\"\"" \
|
||||
"take 1 string backward (3/6)"
|
||||
gdb_test "x/-sh" "u\"\"" \
|
||||
"take 1 string backward (4/6)"
|
||||
gdb_test "x/-sh" "u\"GHIJKLMNOPQRSTUVWXYZ\"" \
|
||||
"take 1 string backward (5/6)"
|
||||
gdb_test "x/-sh" "u\"ABCDEFGHIJKLMNOPQRST\"\.\.\." \
|
||||
"take 1 string backward (6/6)"
|
||||
}
|
||||
|
||||
with_test_prefix "char-width=4, print-max=20" {
|
||||
gdb_test_no_output "set print elements 20"
|
||||
gdb_test_sequence "x/6sw &TestStringsW" "take 6 strings forward" {
|
||||
"U\"ABCDEFGHIJKLMNOPQRST\"\.\.\."
|
||||
"U\"UVWXYZ\""
|
||||
"U\"\""
|
||||
"U\"\""
|
||||
"U\"[^\"]+\""
|
||||
"U\"01234567890123456789\"\.\.\."
|
||||
}
|
||||
gdb_test "x/-1xw" "0x00000039" "take 1 char backward"
|
||||
gdb_test_sequence "x/-6sw" "take 6 strings backward" {
|
||||
"U\"ABCDEFGHIJKLMNOPQRST\"\.\.\."
|
||||
"U\"UVWXYZ\""
|
||||
"U\"\""
|
||||
"U\"\""
|
||||
"U\"[^\"]+\""
|
||||
"U\"01234567890123456789\"\.\.\."
|
||||
}
|
||||
gdb_test_sequence "x/6sw &TestStringsW" "take 6 strings forward again" {
|
||||
"U\"ABCDEFGHIJKLMNOPQRST\"\.\.\."
|
||||
"U\"UVWXYZ\""
|
||||
"U\"\""
|
||||
"U\"\""
|
||||
"U\"[^\"]+\""
|
||||
"U\"01234567890123456789\"\.\.\."
|
||||
}
|
||||
gdb_test "x/-xw" "0x00000039" "take 1 char backward again"
|
||||
gdb_test "x/-sw" "U\"01234567890123456789\"\.\.\." \
|
||||
"take 1 string backward (1/6)"
|
||||
gdb_test "x/-sw" "U\".+\"" \
|
||||
"take 1 string backward (2/6)"
|
||||
gdb_test "x/-sw" "U\"\"" \
|
||||
"take 1 string backward (3/6)"
|
||||
gdb_test "x/-sw" "U\"\"" \
|
||||
"take 1 string backward (4/6)"
|
||||
gdb_test "x/-sw" "U\"GHIJKLMNOPQRSTUVWXYZ\"" \
|
||||
"take 1 string backward (5/6)"
|
||||
gdb_test "x/-sw" "U\"ABCDEFGHIJKLMNOPQRST\"\.\.\." \
|
||||
"take 1 string backward (6/6)"
|
||||
}
|
||||
|
||||
with_test_prefix "char-width=2, print-max=0" {
|
||||
gdb_test_no_output "set print elements 0"
|
||||
gdb_test_sequence "x/6sh &TestStringsH" "take 6 strings forward" {
|
||||
"u\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\""
|
||||
"u\"\""
|
||||
"u\"\""
|
||||
"u\"\\\\x307b\\\\x3052\\\\x307b\\\\x3052\""
|
||||
"u\"012345678901234567890123456789\""
|
||||
"u\"!!!!!!\""
|
||||
}
|
||||
gdb_test "x/-4xh" "0x0021\[\t \]+0x0021\[\t \]+0x0021\[\t \]+0x0000" \
|
||||
"take 4 characters backward"
|
||||
gdb_test_sequence "x/-6sh" "take 6 strings backward" {
|
||||
"u\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\""
|
||||
"u\"\""
|
||||
"u\"\""
|
||||
"u\"[^\"]+\""
|
||||
"u\"012345678901234567890123456789\""
|
||||
"u\"!!!!!!\""
|
||||
}
|
||||
gdb_test_sequence "x/6sh &TestStringsH" "take 6 strings forward again" {
|
||||
"u\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\""
|
||||
"u\"\""
|
||||
"u\"\""
|
||||
"u\"\\\\x307b\\\\x3052\\\\x307b\\\\x3052\""
|
||||
"u\"012345678901234567890123456789\""
|
||||
"u\"!!!!!!\""
|
||||
}
|
||||
gdb_test "x/-xh" "0x0000" "take 1 char backward"
|
||||
gdb_test "x/-sh" "u\"!!!!!!\"" \
|
||||
"take 1 string backward (1/6)"
|
||||
gdb_test "x/-sh" "u\"012345678901234567890123456789\"" \
|
||||
"take 1 string backward (2/6)"
|
||||
gdb_test "x/-sh" "u\".+\"" \
|
||||
"take 1 string backward (3/6)"
|
||||
gdb_test "x/-sh" "u\"\"" \
|
||||
"take 1 string backward (4/6)"
|
||||
gdb_test "x/-sh" "u\"\"" \
|
||||
"take 1 string backward (5/6)"
|
||||
gdb_test "x/-sh" "u\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"" \
|
||||
"take 1 string backward (6/6)"
|
||||
}
|
||||
|
||||
with_test_prefix "char-width=1, print-max=4" {
|
||||
gdb_test_no_output "set print elements 4"
|
||||
gdb_test_sequence "x/9s &TestStrings" "take 9 strings forward" {
|
||||
"\"ABCD\"\.\.\."
|
||||
"\"EFGH\"\.\.\."
|
||||
"\"IJKL\"\.\.\."
|
||||
"\"MNOP\"\.\.\."
|
||||
"\"QRST\"\.\.\."
|
||||
"\"UVWX\"\.\.\."
|
||||
"\"YZ\""
|
||||
"\"\""
|
||||
"\"\""
|
||||
}
|
||||
gdb_test "x/-xb" "0x00" "take 1 byte backward"
|
||||
gdb_test_sequence "x/-4s" "take 4 strings backward (1/2)" {
|
||||
"\"TUVW\"\.\.\."
|
||||
"\"XYZ\""
|
||||
"\"\""
|
||||
"\"\""
|
||||
}
|
||||
gdb_test_sequence "x/-4s" "take 4 strings backward (2/2)" {
|
||||
"\"CDEF\"\.\.\."
|
||||
"\"GHIJ\"\.\.\."
|
||||
"\"KLMN\"\.\.\."
|
||||
"\"OPQR\"\.\.\."
|
||||
}
|
||||
}
|
||||
|
||||
with_test_prefix "backward disassemble general" {
|
||||
set length_to_examine {1 2 3 4 10}
|
||||
set disassmbly {}
|
||||
|
||||
gdb_test "x/i main" "0x\[0-9a-fA-F\]+ <main>:\t.*" \
|
||||
"move the current position to main (x/i)"
|
||||
gdb_test "x/-i" "0x\[0-9a-fA-F\]+ <main>:\t.*" \
|
||||
"move the current position to main (x/-i)"
|
||||
for {set i 0} {$i < [llength $length_to_examine]} {incr i} {
|
||||
set len [lindex $length_to_examine $i]
|
||||
set instructions [capture_command_output "x/${len}i" ""]
|
||||
lappend disassmbly $instructions
|
||||
}
|
||||
for {set i 0} {$i < [llength $length_to_examine]} {incr i} {
|
||||
set idx [expr [llength $length_to_examine] - $i - 1]
|
||||
set len [lindex $length_to_examine $idx]
|
||||
set actual [capture_command_output "x/-${len}i" ""]
|
||||
set expected [lindex $disassmbly $idx]
|
||||
if {$actual == $expected} {
|
||||
pass "inst:$idx"
|
||||
} else {
|
||||
fail "inst:$idx"
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue