.
├── bsp
│ └── beaglebone
│ ├── applications
│ │ └── application.c
│ ├── drivers
│ │ └── serial.c
│ ├── rtconfig.h
│ └── rtconfig.py
├── components
│ └── gdb
│ ├── gdb_stub.c
│ ├── gdb_stub.h
│ ├── hal_stub.c
│ ├── libcpu
│ │ └── arm
│ │ ├── arch_gdb.h
│ │ └── arm_stub.c
│ └── SConscript
├── libcpu
│ └── arm
│ └── am335x
│ ├── start_gcc.S
│ └── trap.c
├── README.md
├── readme-zh.txt
libcpu
目录下有2个文件(作用是异常处理)
start_gcc.S
对比原先的start_gcc.S,这个文件主要做的改变有
1).
修改了stack_size,主要是UND模式和ABT模式
2).
完善了vector_undef ,vector_dabt中断向量具体说明见之前博文异常向量处理有关说明,不过具体实现还请见代码,因为那篇里的代码有少量BUG
trap.c
1).
在rt_hw_trap_dabt添加了
#ifdef RT_USING_GDB
if (gdb_mem_fault_handler) {
regs->pc = (unsigned long)gdb_mem_fault_handler;
return;
}
#endif
作用见之前博文probe_kernel_write
2).
rt_hw_trap_udef
#ifdef RT_USING_GDB
regs->pc -= 4; //lr in undef is pc + 4
if (gdb_undef_hook(regs))
return;
#endif
这里把pc寄存器的值减了4,原因是arm进入undef模式后lr寄存器保存的是进去前指令的下一条指令,而GDB需要的是进去前指令
gdb_undef_hook函数是我们在armstub.c注册的钩子
他会检查触发undef的指令是不是我们注册的指令,即arch_arm.h注册的两个宏
#define GDB_BREAKINST 0xe7ffdefe
#define GDB_COMPILED_BREAK 0xe7ffdeff
这里其实要根据大端小端作区分- -偷了下懒
如果符合 则进入gdb 不符合,就进入正常处理步骤
bsp
beaglebone/rtconfig.h
添加了gdb相关宏
beaglebone/rtconfig.py
改为debug模式 改为-g选项
beaglebone/applications/application.c
gdb的调用
gdb_set_device("uart4");
gdb_start();
作用见readme-zh.txt
beaglebone/drivers/serial.c
注册了gdb使用的串口驱动
相比原来的改动,有
/* disable RX interrupt */
UART_IER_REG(uart4.base) = 0x00;
静止了uart4的中断
static int am33xx_putc_poll(struct rt_serial_device *serial, char c)
{
struct am33xx_uart* uart;
RT_ASSERT(serial != RT_NULL);
uart = (struct am33xx_uart *)serial->parent.user_data;
while (!(UART_LSR_REG(uart->base) & 0x20));
UART_THR_REG(uart->base) = c;
return 1;
}
static int am33xx_getc_poll(struct rt_serial_device *serial)
{
int ch;
struct am33xx_uart* uart;
RT_ASSERT(serial != RT_NULL);
uart = (struct am33xx_uart *)serial->parent.user_data;
ch = -1;
while(!(UART_LSR_REG(uart->base) & 0x01));
ch = UART_RHR_REG(uart->base) & 0xff;
return ch;
}
改变了uart_getc和putc,使之阻塞
components
gdb_stub.h
gdb_stub各种资源的声明和定义
分别有
Signal definitions
gdb_bptype,gdb_bpstate等软件断点枚举变量的定义
一些结构体
arch_stub.h
gdb_stub的适配
定义了
#define BREAK_INSTR_SIZE 4
断点的长度,也就是目标平台的位宽
#define GDB_BREAKINST 0xe7ffdefe
断点的特征,用于区分是软件断点还是普通的异常
#define CACHE_FLUSH_IS_SAFE 1
是否支持icache
#define GDB_MAX_REGS (ARM_GP_REGS + (ARM_FP_REGS * 3) + ARM_EXTRA_REGS)
寄存器数量
#define BUFMAX 400
gdb缓存的大小
hal_stub.c
定义了io相关和调用接口的函数
主要难看的地方在
/* polling */
int gdb_uart_getc()
{
int ch;
/*ch = rt_device_read(gdb_dev, 0, &ch, 1);*/
ch = -1;
do {
ch = gdb_serial->ops->getc(gdb_serial);
} while (ch == -1);
#ifdef RT_GDB_DEBUG
rt_kprintf("%c",ch);
#endif
return ch;
}
我一开始尝试的是直接rt_device_read
但是似乎速度跟不上,就只好这么做了
arm_stub.c
见程序结构以及实现细节
gdb_stub.c
见程序结构以及实现细节
程序结构
流程图
实现细节
更多细节还是见代码!!
int gdb_handle_exception(int signo, void *regs)
{
int error;
gs.signo = signo;
if (!gdb_io_ready(1)) {
error = 1;
return error; /* No I/O connection, so resume the system */
}
gdb_set_register(regs);
gdb_deactivate_sw_breakpoints();
/* Clear the out buffer. */
memset(remcom_out_buffer, 0, sizeof(remcom_out_buffer));
if (gdb_connected) {
char *ptr;
gdb_io_ops.write_char('\n');
/* Reply to host that an exception has occurred */
ptr = remcom_out_buffer;
*ptr++ = 'T';
*ptr++ = tohex((gs.signo >> 4) &0xf);
*ptr++ = tohex(gs.signo & 0xf);
/*ptr += strlen(strcpy(ptr, "thread:"));*/
/**ptr++ = ';';*/
put_packet(remcom_out_buffer);
}
gs.pass_exception = 0;
while (gdb_process_exception());
error = gs.pass_exception;
return error;
}
这就是处理函数,
具体行为参加上面的流程图
最终还是要进入gdb_process_exception()循环
int gdb_process_exception()
{
int status;
do {
get_packet(remcom_in_buffer);
status = process_packet(remcom_in_buffer);
} while (status == 0);
if (status < 0)
return 0;
else
return 1;
}
/*more about packet in https://www.sourceware.org/gdb/current/onlinedocs/gdb/Packets.html#Packets*/
static int process_packet(char *pkt)
{
int status = 0;
int tmp;
gdb_arch_handle_exception(remcom_in_buffer,
remcom_out_buffer);
remcom_out_buffer[0] = 0;
switch (pkt[0]) {
case '?':/* gdbserial status */
gdb_cmd_status(&gs);
break;
case 'q':/* query command */
gdb_cmd_query(&gs);
break;
case 'p': /* return the value of a single CPU register */
case 'g': /* return the value of the CPU registers */
gdb_cmd_getregs(&gs);
break;
case 'P': /* set the value of a single CPU registers - return OK */
case 'G': /* set the value of the CPU registers - return OK */
gdb_cmd_setregs(&gs);
break;
case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
gdb_cmd_memread(&gs);
break;
case 'X':/* XAA..AA,LLLL: Write LLLL escaped binary bytes at address AA.AA*/
gdb_cmd_binwrite(&gs);
break;
case 'M':/* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
gdb_cmd_memwrite(&gs);
break;
case 'D': /* Debugger detach */
case 'k': /* Debugger detach via kill */
gdb_cmd_detachkill(&gs);
status = -1;
break;
case 'R': /* Reset */
gdb_cmd_reboot(&gs);
break;
case 'C':/* Exception passing */
tmp = gdb_cmd_exception_pass(&gs);
if (tmp > 0)
process_packet(remcom_in_buffer);
if (tmp == 0)
break;
case 's': /* sAA..AA step form address AA..AA (optional) */
status = -1;
gdb_activate_sw_breakpoints();
break;
case 'c': /* cAA..AA Continue at address AA..AA (optional) */
gdb_activate_sw_breakpoints();
status = -1;
break;
case 'z':/* Break point remove */
case 'Z':/* Break point set */
gdb_cmd_break(&gs);
break;
case 'H':/* task related */
break;
case 'T':/* Query thread status */
break;
case 'b': /* bBB... Set baud rate to BB... */
break;
}
if(status)
return status;
put_packet(remcom_out_buffer);
return 0;
}
代码长- - 直接跳过把
都是比较容易理解的
这里要注意
gdb_arch_handle_exception(remcom_in_buffer,
remcom_out_buffer);
是定义再arm_stub.c的函数
为什么这么做呢?
因为对不同的包我们不同平台需要做的处理不同,所以要抽象分离开来
对于ARM来说
处理的主要是C,S包
/*
* If this was a compiled breakpoint, we need to move
* to the next instruction or we will breakpoint
* over and over again
*/
ptr = &remcom_in_buffer[1];
if (gdb_hex2long(&ptr, &addr))
regs->pc = addr;
else if (compiled_break == 1)
regs->pc += 4;
compiled_break = 0;
return 0;
因为我们要是通过自定义指令触发的异常,我们返回的时候必须要跳过他,也就是pc+4
还有就是分析单步执行也是在arch_handle做的,见上上篇博文
process_packet里调用的cmd函数其实都很简单拉
没什么好分析的
挑几个把
/* Handle the 'z' or 'Z' breakpoint remove or set packets */
static void gdb_cmd_break(struct gdb_state *gs)
{
/*
* Since GDB-5.3, it's been drafted that '0' is a software
* breakpoint, '1' is a hardware breakpoint, so let's do that.
*/
char *bpt_type = &remcom_in_buffer[1];
char *ptr = &remcom_in_buffer[2];
unsigned long addr;
unsigned long length;
int error = 0;
if (arch_gdb_ops.set_hw_breakpoint && *bpt_type >= '1') {
/* Unsupported */
if (*bpt_type > '4')
return;
} else {
if (*bpt_type != '0' && *bpt_type != '1')
/* Unsupported. */
return;
}
/*
* Test if this is a hardware breakpoint, and
* if we support it:
*/
if (*bpt_type == '1' && !(arch_gdb_ops.flags)) {
/* Unsupported. */
return;
}
if (*(ptr++) != ',') {
error_packet(remcom_out_buffer, -1);
return;
}
if (!gdb_hex2long(&ptr, &addr)) {
error_packet(remcom_out_buffer, -1);
return;
}
if (*(ptr++) != ',' ||
!gdb_hex2long(&ptr, &length)) {
error_packet(remcom_out_buffer, -1);
return;
}
if (remcom_in_buffer[0] == 'Z' && *bpt_type == '0')
error = gdb_set_sw_break(addr);
else if (remcom_in_buffer[0] == 'z' && *bpt_type == '0')
error = gdb_remove_sw_break(addr);
else if (remcom_in_buffer[0] == 'Z')
error = arch_gdb_ops.set_hw_breakpoint(addr,
(int)length, *bpt_type - '0');
else if (remcom_in_buffer[0] == 'z')
error = arch_gdb_ops.remove_hw_breakpoint(addr,
(int) length, *bpt_type - '0');
if (error == 0)
strcpy(remcom_out_buffer, "OK");
else
error_packet(remcom_out_buffer, error);
}
这是我们处理断点包的函数
简单的理一下思路~~~~
小知识:rsp协议中z0是软件断点,z1是硬件断点,往后数据断点(此处有错误,对ARM来说,z包定义见 https://www.sourceware.org/gdb/current/onlinedocs/gdb/ARM-Breakpoint-Kinds.html#ARM-Breakpoint-Kinds)
先是判断是不是数据断点拉~~~是就跳出
然后判断如果是硬件断点的话 支不支持拉~~~ 不支持就跳出
要注意arch_gdb_ops
struct gdb_arch {
unsigned char gdb_bpt_instr[BREAK_INSTR_SIZE];
unsigned long flags;
int (*set_hw_breakpoint)(unsigned long, int, enum gdb_bptype);
int (*remove_hw_breakpoint)(unsigned long, int, enum gdb_bptype);
void (*disable_hw_break)();
void (*remove_all_hw_break)(void);
void (*correct_hw_break)(void);
};
是设置硬件断点相关和断点特征值的地方
gdb_set_sw_break
gdb_remove_sw_break
要注意这两个函数只是对断点的标记做修改而已
并没有真正去设置断点
还有就是寄存器部分拉
/* Handle the 'G' or 'P' set registers request */
static void gdb_cmd_setregs(struct gdb_state *gs)
{
char len = sizeof(long);
/*set one registers*/
if (remcom_in_buffer[0] == 'P'){
char *p = &remcom_in_buffer[1];
unsigned long regno = 0;
if (gdb_hex2long(&p, ®no) && *p++ == '='){
gdb_get_register((unsigned long *)gdb_regs);
gdb_hex2mem(p, ((char *)gdb_regs) + regno * len, len);
gdb_put_register(gdb_regs);
strcpy(remcom_out_buffer, "OK");
}
return;
}
gdb_hex2mem(&remcom_in_buffer[1], (char *)gdb_regs, NUMREGBYTES);
gdb_put_register(gdb_regs);
strcpy(remcom_out_buffer, "OK");
}
寄存器这里好乱......