移植GDB(2) native v0.1

teawater<teawater@gmail.com>





修改记录:

v0.1

2006-02-25,修改了1章中的一些错误。

v0.0

2006-02-21v0.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提供的调试接口(ptraceprocfs等,后面将详细介绍)对被调试程序进行控制,同时取得这个程序的寄存器和内存的信息,所以这个调试接口相关的代码就是native代码中比较重要的部分。一般来说不同ARCH在同一个OS会使用同一个调试接口,只是其中略有不同,所以移植一个ARCHGDB已经支持的一个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是一个系统调用,其作用是让一个进程观测和控制另一个进程的执行,因为后面有不少跟其相关的内容,所以先在这里对其进行介绍。注意不同的OSptrace的参数等可能略有不同,这里只是简单介绍,具体情况要以实际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中,因为其中记录了寄存器等跟ARCHOS相关的信息,所以不同的ARCHOS是不同的。


在某些OS比如Linux的很多ARCHptrace实现中,PTRACE_PEEKUSRPTRACE_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结构的OScore文件的支持。

infptrace.o

这个文件中包含了一些使用ptrace的接口,如果NATDEPFILES=包含inftarg.o则最好包含这个文件。

fork-child.o

这个文件中包含了forkexec启动被调试程序的接口,一般的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所在的OSLinux的时候包含这个.o文件。

solib.o

这个.o文件中包含了通用的动态链接库处理函数,具体可见下面对SOLIB_ADD等宏的介绍。

solib-TYPE.o

有一系列.o文件,比如solib-svr4.o solib-aix.o,他们是向solib.o提供对某个具体动态库支持的.o文件。

这其中跟Linux比较密切的有solib-svr4.osolib-legacy.o


LOADLIBES=

指定连接时候需要的库文件,如果TARGETOSLinux,因为需要在GDB启动的时候转载动态链接库libthread_db,所以通常使用“-ldl -rdynamic”


具体这个文件的编写还可以参照其他TARGET.mh文件。





5.GDB/gdb/config/ARCH/nm-TARGET.h

这个文件是TARGET的头文件,在native的代码这个文件是比较重要的。

一般来说这个文件都包含一个“config/nm-TARGET.h”名称的头文件,因为这个TARGETOSGDB中已经支持,比如config/nm-linux.h(注意:TARGETOS名称一样),则可以包含这个头文件来设置一些跟这个OS相关的选项。


GDBINT 11.6中介绍了头文件中可以设置的宏,下面将对其中一部分进行介绍:

FETCH_INFERIOR_REGISTERS

定义这个宏表明当前的TARGET将定义自己的fetch_inferior_registers函数和store_inferior_registers函数。具体这两个函数将在后面进行介绍。


KERNEL_U_SIZE

定义user结构的长度,一般是定义一个函数,然后函数中返回sizeof(struct user)。比较独特的是MIPSNm-linux.h,直接写了个504,也是其的sizeof(struct user)的长度,注释说这么写的好处是对交叉编译的支持更好,不过我觉得要是头文件都没配置好,再友好后面也要出错吧。


KERNEL_U_ADDR

定义user结构在Kernel中的地址。这个宏的作用是这样的,因为user结构中有些指针是指向这个结构中另一个参数,通过取得指针的值然后减去KERNEL_U_ADDR,就可以取得指针指向参数相对于user结构的偏移,也就可以方便的取得其中的值。

很多系统不需要这个值,可以直接将其设置为0


U_REGS_OFFSET

定义user结构中寄存器信息元素(一般命名为regs)在整个结构中的偏移,比如很多TARGETuser结构中的寄存器信息元素在最开头,这个宏就会定义为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_REGISTERSREGISTER_U_ADDRregister_addr都不需要被定义。


CHILD_PREPARE_TO_STORE

这个宏会在ptraceprocfsgnuhurd调试接口的child_prepare_to_store函数使用,这个函数的作用是在对寄存器进行设置以前,有时候需要先进行一些操作,比如在某些TARGET下无法设置一个寄存器,只能设置全部寄存器,则需要将全部寄存器的值读出来,再进行设置,这个操作就将在child_prepare_to_store调用。

TARGET需要这种设置寄存器以前的操作的时候,可以将函数定义为这个宏。

GDB的现有代码中,只有GDB/gdb/config/nm-gnu.h中设置了这个宏。


I386_USE_GENERIC_WATCHPOINTS

顾名思义这个宏只有ARCHI386TARGET需要,定义这个宏表明使用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中增加结构来扩展支持很多类型的动态库,现在支持的有aixsunossvr4等,关于其扩展的方式将在以后的版本中进行介绍。


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一样,但是目的不同。这个宏用来确定当前TARGETptrace是否有第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同时支持ptraceprocfs两种调试接口,在当前系统有/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_fpxregsetfill_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_flavourbfd_target_elf_flavour,分别代表未知和elf格式。对core_flavourcore文件类型进行比较的只有一个函数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_formatGDB/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根据namesize取得一个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文件的分析,可以使用两种方式:第一种是给gdbarchset_gdbarch_regset_from_core_section设置regset_from_core_section;第二种是用deprecated_add_core_fns向列表core_file_fns设置core_fns结构。

第一种方式需要在gdbarch的代码中进行设置,其不能方便的支持多种core文件结构,而且如果core文件不是标准的BFD的结构bfd_core也不能用这种方式支持。

第二种方式可以在TARGETGDB/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_flavourbfd_target_elf_flavour。实际的使用中这两种设置core_fns结构的方式可以根据需要混合使用。





8.动态连接库支持

主要的支持方式可以见前面SOLIB_ADDSOLIB_CREATE_INFERIOR_HOOKCLEAR_SOLIB几个宏的介绍。