[中期报告]gdbstub4rtt代码结构介绍

工程的文件结构大致是这样的

.
├── 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, &regno) && *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");
}
寄存器这里好乱......





  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值