gdb脚本的流程控制及变量
一、问题
如果我们希望在一个断点的command中再设置一个breakpoint的command,从gdb的文档看,当前是不支持这种功能的,所以此时需要考虑其他的方法。
下面是gdb文档说明
Any other commands in the command list, after a command that resumes execution, are ignored. This is because any time you resume execution (even with a simple next or step), you may encounter another breakpoint—which could have its own command list, leading to ambiguities about which list to execute.
二、变量的名字
1、gdb文档的说明
在gdb文档中,这些变量的专业名字是Convenience-Vars,
文档说明,这种$开始的变量解析时判断是否是系统寄存器(也就是用户不能使用和系统寄存器名称相同的变量名——其实也不是不能使用,而是使用的话引用的是寄存器),或者如果之后的名称全是数字则表示一些变量引用。
Convenience variables are prefixed with ‘$’. Any name preceded by ‘$’ can be used for a convenience variable, unless it is one of the predefined machine-specific register names (see Registers). (Value history references, in contrast, are numbers preceded by ‘$’. See Value History.)
变量在使用的时候会创建,也就是不需要声明,但是刚创建变量的值是void类型。这个变量可以在任意时候修改指向的内容,这么看它其实更像是一个动态语言中的变量,它只是一个标签,指向一个union结构(或者某个基类的对象指针)。
Using a convenience variable for the first time creates it, but its value is void until you assign a new value. You can alter the value with another assignment at any time.
所以内部变量使用前最好还是要明确的初始化一下
(gdb) p $notexist
$12 = void
(gdb) p $notexist > 0
Invalid type combination in ordering comparison.
(gdb)
2、gdb内部变量的类型定义
可以看到,类型是通过"tag + union"的模式声明的
gdb-7.2\gdb\value.c
struct internalvar *
create_internalvar (const char *name)
{
struct internalvar *var;
var = (struct internalvar *) xmalloc (sizeof (struct internalvar));
var->name = concat (name, (char *)NULL);
var->kind = INTERNALVAR_VOID;
var->next = internalvars;
internalvars = var;
return var;
}
对应的变量类型
/* Internal variables. These are variables within the debugger
that hold values assigned by debugger commands.
The user refers to them with a '$' prefix
that does not appear in the variable names stored internally. */
struct internalvar
{
struct internalvar *next;
char *name;
/* We support various different kinds of content of an internal variable.
enum internalvar_kind specifies the kind, and union internalvar_data
provides the data associated with this particular kind. */
enum internalvar_kind
{
……
}
……
union internalvar_data
{
/* A value object used with INTERNALVAR_VALUE. */
struct value *value;
/* The call-back routine used with INTERNALVAR_MAKE_VALUE. */
internalvar_make_value make_value;
……
/* An integer value used with INTERNALVAR_INTEGER. */
struct
{
/* If type is non-NULL, it will be used as the type to generate
a value for this internal variable. If type is NULL, a default
integer type for the architecture is used. */
struct type *type;
LONGEST val;
} integer;
/* A pointer value used with INTERNALVAR_POINTER. */
struct
{
struct type *type;
CORE_ADDR val;
} pointer;
/* A string value used with INTERNALVAR_STRING. */
char *string;
} u;
……
}
3、gdb对于变量的解析
从下面的解析可以看到,寄存器解析的优先级是最高的,然后是内部变量。这也就是说,你不能够定义一个和寄存器同名的内部变量。
gdb-7.2\gdb\parse.c
/* Recognize tokens that start with '$'. These include:
$regname A native register name or a "standard
register name".
$variable A convenience variable with a name chosen
by the user.
$digits Value history with index <digits>, starting
from the first value which has index 1.
$$digits Value history with index <digits> relative
to the last value. I.E. $$0 is the last
value, $$1 is the one previous to that, $$2
is the one previous to $$1, etc.
$ | $0 | $$0 The last value in the value history.
$$ An abbreviation for the second to the last
value in the value history, I.E. $$1
*/
void
write_dollar_variable (struct stoken str)
{
……
/* Handle tokens that refer to machine registers:
$ followed by a register name. */
i = user_reg_map_name_to_regnum (parse_gdbarch,
str.ptr + 1, str.length - 1);
if (i >= 0)
goto handle_register;
/* Any names starting with $ are probably debugger internal variables. */
isym = lookup_only_internalvar (copy_name (str) + 1);
……
}
三、控制流程
这种控制流在gdb的文档中成为“canned sequence of commands”,在gdb的文档中有专门的一节介绍这个内容。
其实gdb只提供了最基本的控制流程,最基础的是if和while两个命令(注意:它们是命令而不是专门的gdb脚本控制流),gdb解析的时候是和break、continue这种基本的常用指令一样作为一个单独的指令解析的,这就是说,它们的使用同样是和break、continue一样,都是每个语句占用单独一行。由于它是单独占一行的,所以其实不需要类似于C语言if/while语法后面的表达式,而是类似于bash的语法结构,这个表达式是不需要放到括弧中的,当然放到里面也没有什么问题,就好像你在在括弧外面再加上任意多层的括弧一样,并不影响表达式的取值。
由于gdb不是把if、while作为控制流(而是作为基本命令),所以这些命令需要和end指令配合来定界控制流的结束。
end不是作为一个命令,而是作为一个特殊的定界符来处理
gdb-7.2\gdb\cli\cli-script.c
/* Process one input line. If the command is an "end",
return such an indication to the caller. If PARSE_COMMANDS is true,
strip leading whitespace (trailing whitespace is always stripped)
in the line, attempt to recognize GDB control commands, and also
return an indication if the command is an "else" or a nop.
Otherwise, only "end" is recognized. */
static enum misc_command_type
process_next_line (char *p, struct command_line **command, int parse_commands,
void (*validator)(char *, void *), void *closure)
{
……
/* 'end' is always recognized, regardless of parse_commands value.
We also permit whitespace before end and after. */
if (p_end - p_start == 3 && !strncmp (p_start, "end", 3))
return end_command;
……
}
四、对于内置变量的引用
由于可以使用内部变量,所以可以通过曲线方式,通过在command中设置变量的形式来进行执行流控制。
tsecer@harry: cat -n gdb.script.seq.cpp
1 #include <stdio.h>
2
3 int main(int argc, const char *argv[])
4 {
5 printf("argc %d\n", argc);
6
7 for (int i = 0; i < 10 ; i++)
8 {
9 printf("%d\n", i);
10 }
11 return 0;
12 }
tsecer@harry: cat -n script.gdb
1 b gdb.script.seq.cpp:5
2
3 set $bp1=0
4
5 command
6 p argc
7 set $bp1=1
8 continue
9 end
10
11 b gdb.script.seq.cpp:9
12 command
13 if $bp1 == 1
14 cont
15 else
16 quit
17 end
18 end
19
20 set pagination off
21
22 run
23
tsecer@harry: gdb -x script.gdb ./a.out
GNU gdb (GDB) 10.1
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./a.out...
Breakpoint 1 at 0x400556: file gdb.script.seq.cpp, line 5.
Breakpoint 2 at 0x400577: file gdb.script.seq.cpp, line 9.
Breakpoint 1, main (argc=1, argv=0x7fffffffea78) at gdb.script.seq.cpp:5
5 printf("argc %d\n", argc);
$1 = 1
argc 1
Breakpoint 2, main (argc=1, argv=0x7fffffffea78) at gdb.script.seq.cpp:9
9 printf("%d\n", i);
0
Breakpoint 2, main (argc=1, argv=0x7fffffffea78) at gdb.script.seq.cpp:9
9 printf("%d\n", i);
1
Breakpoint 2, main (argc=1, argv=0x7fffffffea78) at gdb.script.seq.cpp:9
9 printf("%d\n", i);
2
Breakpoint 2, main (argc=1, argv=0x7fffffffea78) at gdb.script.seq.cpp:9
9 printf("%d\n", i);
3
Breakpoint 2, main (argc=1, argv=0x7fffffffea78) at gdb.script.seq.cpp:9
9 printf("%d\n", i);
4
Breakpoint 2, main (argc=1, argv=0x7fffffffea78) at gdb.script.seq.cpp:9
9 printf("%d\n", i);
5
Breakpoint 2, main (argc=1, argv=0x7fffffffea78) at gdb.script.seq.cpp:9
9 printf("%d\n", i);
6
Breakpoint 2, main (argc=1, argv=0x7fffffffea78) at gdb.script.seq.cpp:9
9 printf("%d\n", i);
7
Breakpoint 2, main (argc=1, argv=0x7fffffffea78) at gdb.script.seq.cpp:9
9 printf("%d\n", i);
8
Breakpoint 2, main (argc=1, argv=0x7fffffffea78) at gdb.script.seq.cpp:9
9 printf("%d\n", i);
9
[Inferior 1 (process 30687) exited normally]
(gdb)