移植GDB(2) native v0.1
teawater<teawater@gmail.com>
修改记录:
v0.1
2006-02-25,修改了1章中的一些错误。
v0.0
2006-02-21,v0.0版本编写完成。
没有详细介绍GDB/gdb/solib.c上动态链接库支持的扩展。
2006-02-02,文档创建。
目录
1.写在前面
2.long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data)
3.struct user
4.GDB/gdb/config/ARCH/TARGET.mh
5.GDB/gdb/config/ARCH/nm-TARGET.h
6.GDB/gdb/ARCH-NAT.c 和 GDB/gdb/ARCH-TARGET-NAT.c
6.1.概述
6.2.跟寄存器读取有关的函数
6.3.gregset相关函数
7.本地core文件支持
7.1.概述
7.2.struct core_fns
7.3.GDB分析core文件的过程
7.4.让TARGET支持core文件分析的方法
8.动态连接库支持
1.写在前面
本文针对GDB-6.3进行编写。
native表示对某个ARCH在某个OS上(称为一个TARGET)调试的支持,当然要让ARCH支持本地调试首先要让GDB支持这个TARGET,这个可以参见“移植GDB(1)”。
GDB对被调试程序的调试主要是通过调用当前TARGET提供的调试接口(ptrace、procfs等,后面将详细介绍)对被调试程序进行控制,同时取得这个程序的寄存器和内存的信息,所以这个调试接口相关的代码就是native代码中比较重要的部分。一般来说不同ARCH在同一个OS会使用同一个调试接口,只是其中略有不同,所以移植一个ARCH到GDB已经支持的一个OS中的时候,只需要根据这个ARCH的情况进行一些编码就可以。而关于调试接口的编写将在“移植GDB(3)”中进行介绍。
在“GDBINT 11. Native Debugging”中,是对native的介绍,本文的部分内容也将取自其中。
下面是本文将使用的缩写:
GDBINT GDB Internals Manual的缩写。
GDB 指GDB源文件目录。
ARCH 体系结构名称。
TARGET 体系结构下的调试目标,一般是一种操作系统,比如Linux。
2.long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data)
ptrace是一个系统调用,其作用是让一个进程观测和控制另一个进程的执行,因为后面有不少跟其相关的内容,所以先在这里对其进行介绍。注意不同的OS下ptrace的参数等可能略有不同,这里只是简单介绍,具体情况要以实际OS为准。
不同的参数request值表示不同的控制功能,参数pid是被控制进程的进程ID。
ptrace的请求和功能说明如下:
PTRACE_TRACEME,被控制进程向控制进程发出跟踪请求。
PTRACE_PEEKTEXT,读取被控制进程代码段的一个字,addr给出地址。
PTRACE_PEEKDATA,读取被控制进程数据段的一个字,addr给出地址。
PTRACE_PEEKUSR,读取被控制进程user结构中的一个字,addr给出地址。
PTRACE_POKETEXT,向被控制进程代码段写入一个字,addr给出地址,data给出数据。
PTRACE_POKEDATA,向被控制进程数据段写入一个字,addr给出地址,data给出数据。
PTRACE_POKEUSR,向被控制进程user结构写入一个字,addr给出地址,data给出数据。
PTRACE_CONT,如果data参数为non-zero并且不是SIGSTOP时,它被解释成一个信号传递给被控制进程,否则,无信号被传递。
PTRACE_KILL,向被控制进程发送SIGKILL信号来终止其。
PTRACE_SINGLESTEP,使被控制进程单步执行。
3.struct user
这个结构在Linux中被定义在Linux Kernel目录include/asm-ARCH/user.h中,因为其中记录了寄存器等跟ARCH和OS相关的信息,所以不同的ARCH和OS是不同的。
在某些OS比如Linux的很多ARCH的ptrace实现中,PTRACE_PEEKUSR和PTRACE_POKEUSR都只对寄存器相关的参数读写起作用,其他部分根本不进行实际的操作,所以实际对user结构的使用要视实际情况来做。
4.GDB/gdb/config/ARCH/TARGET.mh
这个文件是设置一些编译GDB需要的跟当前TARGET有关的文件。下面介绍一下其中可设置的信息。
NAT_FILE=
这个是指定描述TARGET的头文件,一般来说这个头文件的写法为nm-TARGET.h存放在GDB/gdb/config/ARCH/目录中,具体内容将在后面介绍。
NATDEPFILES=
这个是指定要编译的.o文件,后面将介绍的跟TARGET有关的.c文件编译成的.o文件需要在这里指出。这个TARGET需要的ARCH-TARGET-NAT.o文件也需要在这里指定,有些ARCH会有一个通用的ARCH-NAT.o文件也在这里指定。
下面介绍一下常见的.o文件:
inftarg.o
这个文件中包含了通过ptrace对被调试程序进行控制的调试接口。
procfs.o
这个文件中包含了通过/proc对被调试程序进行控制的调试接口。
sol-thread.o
这个文件中包含了Solaris线程调试接口。
corelow.o
这个文件包含了core调试接口,给GDB增加了对core文件进行分析的功能。
core-aout.o
这个文件向上面的corelow.o提供了对未知格式的core文件的支持。
core-regset.o
这个文件向上面的corelow.o提供了对elf格式同时支持regsets结构的OS的core文件的支持。
infptrace.o
这个文件中包含了一些使用ptrace的接口,如果NATDEPFILES=包含inftarg.o则最好包含这个文件。
fork-child.o
这个文件中包含了fork和exec启动被调试程序的接口,一般的NATDEPFILES=都需要包含这个文件。
gcore.o
给GDB增加一个gcore的命令,用来给当前被调试程序产生core文件。
thread-db.o
这个.o文件包含了对target multi-thread的支持。
proc-service.o
用来向不支持proc_service的编译环境提供proc-service函数,thread-db.o文件需要proc-service函数。
linux-nat.o
这个.o文件包含了很多Linux相关的命令等,建议当TARGET所在的OS是Linux的时候包含这个.o文件。
solib.o
这个.o文件中包含了通用的动态链接库处理函数,具体可见下面对SOLIB_ADD等宏的介绍。
solib-TYPE.o
有一系列.o文件,比如solib-svr4.o solib-aix.o,他们是向solib.o提供对某个具体动态库支持的.o文件。
这其中跟Linux比较密切的有solib-svr4.o和solib-legacy.o。
LOADLIBES=
指定连接时候需要的库文件,如果TARGET的OS是Linux,因为需要在GDB启动的时候转载动态链接库libthread_db,所以通常使用“-ldl -rdynamic”。
具体这个文件的编写还可以参照其他TARGET的.mh文件。
5.GDB/gdb/config/ARCH/nm-TARGET.h
这个文件是TARGET的头文件,在native的代码这个文件是比较重要的。
一般来说这个文件都包含一个“config/nm-TARGET.h”名称的头文件,因为这个TARGET的OS在GDB中已经支持,比如config/nm-linux.h(注意:TARGET和OS名称一样),则可以包含这个头文件来设置一些跟这个OS相关的选项。
在GDBINT 11.6中介绍了头文件中可以设置的宏,下面将对其中一部分进行介绍:
FETCH_INFERIOR_REGISTERS
定义这个宏表明当前的TARGET将定义自己的fetch_inferior_registers函数和store_inferior_registers函数。具体这两个函数将在后面进行介绍。
KERNEL_U_SIZE
定义user结构的长度,一般是定义一个函数,然后函数中返回sizeof(struct user)。比较独特的是MIPS的Nm-linux.h,直接写了个504,也是其的sizeof(struct user)的长度,注释说这么写的好处是对交叉编译的支持更好,不过我觉得要是头文件都没配置好,再友好后面也要出错吧。
KERNEL_U_ADDR
定义user结构在Kernel中的地址。这个宏的作用是这样的,因为user结构中有些指针是指向这个结构中另一个参数,通过取得指针的值然后减去KERNEL_U_ADDR,就可以取得指针指向参数相对于user结构的偏移,也就可以方便的取得其中的值。
很多系统不需要这个值,可以直接将其设置为0。
U_REGS_OFFSET
定义user结构中寄存器信息元素(一般命名为regs)在整个结构中的偏移,比如很多TARGET的user结构中的寄存器信息元素在最开头,这个宏就会定义为0。
REGISTER_U_ADDR (addr, blockend, regno)
这个宏的作用是根据从U_REGS_OFFSET得到的寄存器信息在user结构中的偏移blockend取得序号为regno的寄存器在user结构中的偏移并将其存入addr。
这个宏实际只有一个作用,就是如果定义这个宏则在GDB/gdb/core-aout.c中生成一个函数register_addr,所以也可以不在头文件中定义REGISTER_U_ADDR而直接在TARGET相关的.c文件中编写一个register_addr,但是不要REGISTER_U_ADDR宏和register_addr同时编写。具体register_addr将在后面进行详细介绍。
还要注意如果你的TARGET编译的.o文件中不包含core-aout.o也就是默认生成register_addr的库文件,则需要在你自己的项目库文件中包含register_addr,光在头文件中包含REGISTER_U_ADDR就不行了。同时因为register_addr函数的作用是在GDB自带的fetch_inferior_registers函数和store_inferior_registers函数中被调用,也就是没定义FETCH_INFERIOR_REGISTERS的时候,所以如果当前TARGET定义了FETCH_INFERIOR_REGISTERS,REGISTER_U_ADDR和register_addr都不需要被定义。
CHILD_PREPARE_TO_STORE
这个宏会在ptrace、procfs和gnuhurd调试接口的child_prepare_to_store函数使用,这个函数的作用是在对寄存器进行设置以前,有时候需要先进行一些操作,比如在某些TARGET下无法设置一个寄存器,只能设置全部寄存器,则需要将全部寄存器的值读出来,再进行设置,这个操作就将在child_prepare_to_store调用。
当TARGET需要这种设置寄存器以前的操作的时候,可以将函数定义为这个宏。
在GDB的现有代码中,只有GDB/gdb/config/nm-gnu.h中设置了这个宏。
I386_USE_GENERIC_WATCHPOINTS
顾名思义这个宏只有ARCH为I386的TARGET需要,定义这个宏表明使用GDB/gdb/i386-nat.c中的特定断点功能,我估计其中使用了i386的调试寄存器。
ONE_PROCESS_WRITETEXT
定义这个宏表明被调试进程的TEXT节只允许一个进程对其写,当设置断点的时候如果发生错误则会返回相应的错误信息。在现有的GDB代码中没有TARGET使用这个宏。
SHELL_COMMAND_CONCAT
当GDB运行被调试程序的时候,这个宏将被添加到程序名称的前面,一般来说这个宏不需要设置。
SHELL_FILE
这个宏用来指定GDB运行被调试程序的SHELL的目录名,一般不需要设置,使用GDB/gdb/fork-child.c中默认指定的"/bin/sh"就可以。
SOLIB_ADD (filename, from_tty, targ, readsyms)
增加动态链接库的符号信息到当前GDB的符号表中,一般情况可以使用GDB/gdb/solib.c中的代码,直接包含solib.h就可以,注意前面介绍过的config/nm-linux.h包含solib.h文件。这个GDB/gdb/solib.c中的代码是可以通过向struct target_so_ops *current_target_so_ops中增加结构来扩展支持很多类型的动态库,现在支持的有aix、sunos、svr4等,关于其扩展的方式将在以后的版本中进行介绍。
SOLIB_CREATE_INFERIOR_HOOK
这个宏将在运行被调试程序后调用,一般运行一些动态连接库重定位相关的代码。跟上面一个宏一样使用GDB/gdb/solib.c中的代码,直接包含solib.h就可以。
CLEAR_SOLIB
这个宏会在GDB清除所有符号的时候调用,用来清除动态链接库的信息。跟上面一个宏一样使用GDB/gdb/solib.c中的代码,直接包含solib.h就可以。
START_INFERIOR_TRAPS_EXPECTED
当启动被调试程序的时候,一般会有两次TRAP(指被调试程序被断点中断,进入KERNEL),第一次是SHELL的执行,第二次是被调试程序的执行。如果编写的TARGET是两次TRAP则不需要进行设置,如果不是可以用这个宏进行指定。
根据现有GDB的代码,建议定义这个宏的时候对其先undef一下。举例:
#undef START_INFERIOR_TRAPS_EXPECTED
#define START_INFERIOR_TRAPS_EXPECTED 4
DEBUG_PTRACE
在GDB/gdb/infptrace.c文件137行开始也就是判断是否定义了DEBUG_PTRACE开始,使用前面定义的call_ptrace函数来替换ptrace进行调试,这样可以在call_ptrace设置断点方便调试GDB。
PTRACE_TYPE_ARG5
判断的位置跟DEBUG_PTRACE一样,但是目的不同。这个宏用来确定当前TARGET的ptrace是否有第5个参数,如果是则使用call_ptrace函数替换ptrace进行调试,而在call_ptrace对是否定义了PTRACE_TYPE_ARG5进行了检查,保证可以正常编译。
这个宏应该是定义在configure的时候生成的GDB/gdb/config.h文件中的,不需要在文件中指定。
PTRACE_ARG3_TYPE
ptrace系统调用的第三个参数的类型。如果不设置则会按照GDB/gdb/inferior.h文件中的将其定义为PTRACE_TYPE_ARG3,而这个PTRACE_TYPE_ARG3定义在configure的时候生成的GDB/gdb/config.h文件中。
PTRACE_XFER_TYPE
这个宏用来标记传输ptrace数据的类型,主要是用来帮助计算数据的长度。
注意这个宏并不是一个通用的宏,只是在部分TARGET的.c代码中进行了使用,所以如果你没使用可以不进行设置。
USE_PROC_FS
如果定义这个宏则前面介绍过的跟当前TARGET相关的.o文件中的进行寄存器信息转换(GDB内部表示方式和/proc表示方式)函数进行编译,否则不进行编译。
HAVE_OPTIONAL_PROC_FS
只有少数的TARGET使用这个宏,这个宏的作用是表明当前TARGET同时支持ptrace和procfs两种调试接口,在当前系统有/proc文件系统的时候优先使用procfs调试接口。在GDB/gdb/inftarg.c的初始化函数_initialize_inftarg中,就可以看到先会检查系统中的/proc文件系统是否正常,如果正常就不初始化ptrace调试接口。
PROC_NAME_FMT
这个宏就是在上面这个宏中介绍的检查代码中使用的,其用来标明当前/proc文件系统中下进程文件的名称。
6.GDB/gdb/ARCH-NAT.c 和 GDB/gdb/ARCH-TARGET-NAT.c
6.1.概述
这两个文件是跟TARGET相关的.c文件,主要包含一些TARGET相关的函数。而这个两个函数的关系可以说第一个可以比较方便的应用于同一ARCH的多个TARGET,所以主要是一些ARCH相关的函数,当然实际使用的时候可以编写者自己灵活掌握。
6.2.跟寄存器读取有关的函数
CORE_ADDR register_addr (int regno, CORE_ADDR blockend)
前面介绍宏REGISTER_U_ADDR的时候已经介绍过这个函数,这个函数跟宏REGISTER_U_ADDR的功能一样,根据从U_REGS_OFFSET得到的寄存器信息在user结构中的偏移blockend取得序号为regno的寄存器在user结构中的偏移并返回。
其他关于这个函数信息可以看上面REGISTER_U_ADDR的介绍内容。
void fetch_inferior_registers (int regno)
这个函数只有在定义了FETCH_INFERIOR_REGISTERS宏后才能包含在TARGET自己的库函数里。这个函数的作用是将指定序号regno的寄存器的值通过调试接口比如ptrace从被调试程序中读出,然后调用GDB/gdb/regcache.c:regcache_raw_supply函数将这个寄存器的信息存储到GDB/gdb/regcache.c:current_regcache中。如果regno为-1则表示要将所有寄存器的信息存储到GDB/gdb/regcache.c:current_regcache中。
void store_inferior_registers (int regno)
这个函数只有在定义了FETCH_INFERIOR_REGISTERS宏后才能包含在TARGET自己的库函数里。这个函数的作用从GDB/gdb/regcache.c:current_regcache中通过GDB/gdb/regcache.c:regcache_raw_collect函数读出regno指定的寄存器信息,然后通过调试接口比如ptrace写如被调试程序中。如果regno为-1则表示要将所有GDB/gdb/regcache.c:current_regcache中的寄存器的信息存储到被调试程序中。
6.3.gregset相关函数
下面介绍的四个函数是用来将GDB内部使用的格式的寄存器信息和本地OS使用的gregset格式的寄存器信息(在Linux中被定义在Linux Kernel目录include/asm-ARCH/elf.h中)进行相互转化的函数。其中gdb_gregset_t存储普通寄存器和控制寄存器信息,gdb_fpregset_t存储浮点寄存器的信息。注意有这几个函数的时候别忘记包含头文件gregset.h。
void fill_gregset (gdb_gregset_t *gregsetp, int regno)
这个函数从GDB/gdb/regcache.c:current_regcache中通过GDB/gdb/regcache.c:regcache_raw_collect函数读出regno指定的寄存器信息,然后转化成gdb_gregset_t的格式,存入gregsetp中。如果regno为-1则表示要将所有GDB/gdb/regcache.c:current_regcache中的寄存器的信息转化存储到gregsetp中。
void supply_gregset (gdb_gregset_t *gregsetp)
将gregsetp中的信息先转化成GDB内部使用的寄存器格式,然后调用GDB/gdb/regcache.c:regcache_raw_supply函数这些信息存储到GDB/gdb/regcache.c:current_regcache的每个寄存器中。
void fill_fpregset (gdb_fpregset_t *fpregsetp, int regno)
这个函数从GDB/gdb/regcache.c:current_regcache中通过GDB/gdb/regcache.c:regcache_raw_collect函数读出regno指定的浮点寄存器信息,然后转化成gdb_fpregset_t的格式,存入fpregsetp中。如果regno为-1则表示要将所有GDB/gdb/regcache.c:current_regcache中的浮点寄存器的信息转化存储到fpregsetp中。
void supply_fpregset (gdb_fpregset_t *fpregsetp)
将fpregsetp中的信息先转化成GDB内部使用的浮点寄存器格式,然后调用GDB/gdb/regcache.c:regcache_raw_supply函数这些信息存储到GDB/gdb/regcache.c:current_regcache的每个浮点寄存器中。
还有两个函数supply_fpxregset和fill_fpxregset是用来对i386扩展浮点寄存器(SSE)进行格式转化用的,因为只有i386使用,而且结构比较清晰,所以就不进行介绍了。
7.本地core文件支持
7.1.概述
前面已经介绍过,core文件的支持是通过corelow.o也就是GDB/gdb/corelow.c文件来实现的,其中的调试接口在用户用GDB的-c选项或者使用target core命令的方式分析core文件的时候将会被调用,而在调用这些调试接口的时候,将先会进行一些处理,然后对core_file_fns列表中注册的每个core_fns结构的进行扫描,选择合适的core_fns结构,调用其中的函数。也有可能是调用“移植GDB(1)”中介绍过的用set_gdbarch_regset_from_core_section注册的函数。具体整个过程将在下面进行详细的介绍。
7.2.struct core_fns
这个结构定义在文件GDB/gdb/gdbcore.h中,因为core文件有不同的类型,所以需要可以根据情况进行扩展,而core_fns结构就是其扩展接口。
下面来介绍一下core_fns结构中的每个元素:
enum bfd_flavour core_flavour;
这个变量表示注册的这组函数关联的core文件的类型,在GDB中常见的就是bfd_target_unknown_flavour和bfd_target_elf_flavour,分别代表未知和elf格式。对core_flavour和core文件类型进行比较的只有一个函数GDB/gdb/corelow.c:default_core_sniffer,具体使用在介绍core_sniffer的时候进行介绍。
int (*check_format) (bfd *);
并不是每个被指定来的当作core文件分析的文件都是BFD支持的core文件,比如一个特殊的core_fns结构,分析的core文件并不符合BFD结构中bfd_core格式,为了也能对其进行分析,则需要注册check_format函数。函数如果确定当前core文件的格式可以用当前的core_fns结构分析就非0,否则返回0。如果支持的core文件就是BFD支持的bfd_core格式,就注册函数GDB/gdb/corelow.c:default_check_format,这个函数不会进行任何判断而直接返回0。
int (*core_sniffer) (struct core_fns *, bfd *);
注册在这个函数指针的函数会根据当前core文件的BFD格式和当前的core_fns结构支持的BFD格式,如果相同就返回非0,不同就返回0。一般这个指针注册前面提到的函数GDB/gdb/corelow.c:default_core_sniffer,这个函数结构很简单,取出当前core文件的BFD格式和当前core_fns结构的core_flavour结构进行比较,相同就返回真。
注意如果前面的check_format返回了真,则要保证在这里也返回真,否则将影响GDB的运行,后面7.3会作详细解释。
void (*core_read_registers) (char *core_reg_sect, unsigned core_reg_size, int which, CORE_ADDR reg_addr);
注册在这个指针上的函数用来从core文件内容中将寄存器信息读出然后进行转化用函数regcache_raw_supply存储到current_regcache中。其参数core_reg_sect是指向core文件中寄存器信息的指针;core_reg_size是这些信息的长度;which是信息的类型,0是普通寄存器,2是浮点寄存器,3是前面提过的扩展浮点寄存器;reg_addr,指从u.u_ar0到寄存器信息的偏移,用来确定老式的core文件的section中的寄存器信息的位置,具体看注释。
一般来说这个函数的编写方式是先根据which确定core_reg_sect中信息的格式是普通类型gdb_gregset_t或者浮点类型gdb_fpregset_t,然后进行转化然后调用regcache_raw_supply将每个寄存器的信息写入current_regcache或者直接调用前面介绍过的supply_gregset进行这个工作。
struct core_fns *next;
指向下一个core_fns结构。将一个core_fns结构加入列表core_file_fns使用函数GDB/gdb/corelow.c:deprecated_add_core_fns,这个函数会将每个结构依次用next连起来。
7.3.GDB分析core文件的过程
第一步,当用户用GDB的-c选项或者使用target core命令的方式分析core文件的时候,会调用GDB/gdb/corelow.c:core_open函数。经过一些打开等的操作,就会调用GDB/bfd/format.c:bfd_check_format和GDB/gdb/corelow.c:gdb_check_format,这里的作用就是检查core文件的格式是否正确。前面的bfd_check_format函数是检查当前打开的core文件格式是否是bfd_core,而后面的函数gdb_check_format会循环调用core_file_fns列表中每个core_fns结构的check_format函数指针,只要有一个返回真就返回真,这两个函数如果都返回假则报错退出。这里调用的目的就是判断当前的core文件格式是否能被GDB处理。
第二步,经过一部分的处理,然后调用函数GDB/gdb/corelow.c:sniff_core_bfd。这个函数会先用函数GDB/gdb/gdbarch.c:gdbarch_regset_from_core_section_p检查当前的core_gdbarch是否支持regset_from_core_section,如果是则直接返回,这么做因为支持regset_from_core_section就可以通过其取得寄存器信息,不需要使用core_fns结构中的core_read_registers(下面介绍到取得寄存器信息的部分中很清晰),所以不需要再执行后面的代码,关于regset_from_core_section的信息可以见“移植GDB(1)5.5”最后部分的介绍。
后面开始的代码就是调用core_file_fns列表中每个core_fns结构的core_sniffer函数指针,如果返回真就记录下这个core_fns结构,最后进行检查,如果没有记录到任何core_fns结构,就以core_file_fns列表中第一个core_fns结构设置进去。
最后sniff_core_bfd返回,返回值存储在本地全局变量core_vec中,后面需要使用core_fns结构的时候,都将直接使用core_vec。这里也说明了core_sniffer函数指针和设置core_vec的密切关系,所以在前面介绍core_sniffer函数指针的时候说前面的check_format返回了真,则要保证在这里也返回真。
第三步,最后经过一系列的处理,调用GDB/gdb/target.h:target_fetch_registers宏,取得全部寄存器的信息,这个宏会调用current_target.to_fetch_registers,最终会调用GDB/gdb/corelow.c:get_core_registers。
在get_core_registers函数中,会先检查core_gdbarch中的regset_from_core_section是否设置以及core_vec中的core_read_registers是否可用,如果他们都不可用则会报错返回。这里再次说明了这2个指针的存在作用,都是用来从core文件中取得寄存器信息,并且只设置其中一个就可以。
然后是三次调用GDB/gdb/corelow.c:get_core_register_section,这三次调用分别是对普通寄存器,浮点寄存器和扩展浮点寄存器的读取。在get_core_register_section函数中,先根据参数name也就是存储了寄存器信息的section的名称取得了section的长度size和内容contents。然后检查core_gdbarch中的regset_from_core_section是否设置,如果设置了则调用gdbarch_regset_from_core_section根据name和size取得一个regset结构,然后调用regset结构中的函数supply_regset设置寄存器的信息,在“移植GDB(1)5.5”中已经介绍过这些函数等,所以这里不作详细介绍。最后调用core_vec->core_read_registers读取寄存器信息。函数get_core_register_section返回。
get_core_registers最后调用GDB/gdb/regcache.c:deprecated_registers_fetched函数,标记全部寄存器已经都被从被调试程序取出值了。
7.4.让TARGET支持core文件分析的方法
通过前面的介绍,可以知道如果想在TARGET中支持core文件的分析,可以使用两种方式:第一种是给gdbarch用set_gdbarch_regset_from_core_section设置regset_from_core_section;第二种是用deprecated_add_core_fns向列表core_file_fns设置core_fns结构。
第一种方式需要在gdbarch的代码中进行设置,其不能方便的支持多种core文件结构,而且如果core文件不是标准的BFD的结构bfd_core也不能用这种方式支持。
第二种方式可以在TARGET的GDB/gdb/ARCH-TARGET-NAT.c文件中的_initialize_名称类型的初始化函数(这个函数的初始化原理将在“移植GDB(3)”中进行介绍)中用deprecated_add_core_fns向列表core_file_fns设置自己定义的core_fns结构;也可以在GDB/gdb/config/ARCH/TARGET.mh中定义前面介绍过的core-aout.o文件和core-regset.o文件,这两个文件关联的格式分别是bfd_target_unknown_flavour和bfd_target_elf_flavour。实际的使用中这两种设置core_fns结构的方式可以根据需要混合使用。
8.动态连接库支持
主要的支持方式可以见前面SOLIB_ADD、SOLIB_CREATE_INFERIOR_HOOK、CLEAR_SOLIB几个宏的介绍。