ARM9学习之调试

1、gdb的安装

  • 去官网下载gdb压缩包:http://ftp.gnu.org/gnu/gdb/

  • 解压源码包,进入解压目录并配置–target=arm-linux代表程序运行目标为arm-linux

./configure --target=arm-linux
  • 编译
make
  • 安装到当前文件夹下的tmp目录
mkdir tmp
make install prefix=$PWD/tmp
  • 配置安装gdbserver
./configure --host=arm-linux make
  • 拷贝gdbser到开发板上面

2、使用gdb进行测试

  • 在开发板上面运行
gdbserver 192.168.2.100:1234 ./test
  • PC机上面运行(在test程序目录)
arm-linux-gdb test
//输入连接命令
target remote 192.168.2.100:1234
  • help all :查看所有的命令
   
   
   
   
l :查看源代码 c :运行到断点 bt :调用栈 break :打断点 step :单步运行

3、系统调用

  • 添加自定义系统函数/fs/read_write.c 。在里面添加
asmlinkage void sys_mycall(char *buf, int count)
{
    char buf_mod[1024];

    if(*buf){
        copy_from_user(buf_mod, buf, (count < 1023) ? count : 1023);
        buf_mod[1023] = '\0';
    }else{
        buf_mod[0] = '\0';
    }

    printk("do_mysyscall : %s\n", buf_mod);
}
  • arch/arm/kernel/calls.s
    添加
CALL(sys_mycall)
  • include/linux/syscalls.h
    添加
asmlinkage void sys_mycall(char *buf, int count);
  • 重新编译内核,使用新内核启动

  • 编写应用程序

#include <errno.h>
#include <unistd.h>

void mycall(char *buf, int count)
{
    /* SWI */

    asm(
        "mov r0, %0\n"  /* save the first argument into R0 */
        "mov r1, %1\n"  /* save the first argument into R0 */
        "swi %2\n"  /* do system calls */
        :/* output list */
        : "r"(buf), "r"(count), "i" (0x900000 + 352)
        : "r0", "r1"    /* the register list who were changed */
    );
}

int main(int argc, char *argv[])
{
    printf("Test for mycall\n");
    mycall("Powered by YellowMax\n", 21);

    return 0;
}
  • 编译运行,得到正确的运行结果
   
   
   
   
./syscall Test for mycall do_mysyscall : Powered by YellowMax

4、printk的原理以及使用

4.1、关于__setup

在printk.c中有下面一句话

__setup("console=", console_setup);

在init.h中有以下关于__setup的宏定义

#define __setup(str, fn) \
    __setup_param(str, fn, fn, 0)

#define __setup_param(str, unique_id, fn, early) \
    static char __setup_str_##unique_id[] __initdata = str; \
    static struct obs_kernel_param __setup_##unique_id \
        __attribute_used__              \
        __attribute__((__section__(".init.setup"))) \
        __attribute__((aligned((sizeof(long)))))    \
        = { __setup_str_##unique_id, fn, early }

__setup("console=", console_setup);代入最终就会得到

/* __initdata:#define __initdata __attribute__ ((__section__ (".init.data"))) * 代表此常量放在.init.data段,内核启动时有传入的参数的时候就会去.init.data段找相应的静态描述,以及其对应的结构体 */
static char __setup_str_console_setup[] __initdata = "console=";    \
static struct obs_kernel_param __setup_console_setup    \
    __attribute_used__              \               //变量被启用
    __attribute__((__section__(".init.setup"))) \   //.init.setup段
    __attribute__((aligned((sizeof(long)))))    \   //四字节对齐
    = { __setup_str_console_setup, console_setup, 0 }
/* obs_kernel_param结构体:包括其对应的名字,对应的设置函数 */
struct obs_kernel_param {
    const char *str;
    int (*setup_func)(char *);
    int early;
};

可以想象到,内核编译时会把console等等参数以及其处理函数放在一个obs_kernel_param结构体里面,此结构体被编译到.init.setup段,内核启动的时候如果有接收到来自u-boot传入的参数,就会在.init.setup段进行查找,如果有与之同名的参数时就会调用到对应的setup函数进行相关参数的设置。

static int __init console_setup(char *str)
    add_preferred_console
        /* 把所有控制台(不超过8个),全部添加到console_cmdline结构体数组中去 */
        for(i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++)
        if (strcmp(console_cmdline[i].name, name) == 0 &&
              console_cmdline[i].index == idx) {
                selected_console = i;
                return 0;
        }

register_console    //在里面比较传入的console结构体里面的名字是否与console_cmdline里面的名字相同,如果相同,就把console_cmdline里面的名字,设备,驱动等传给console结构体,由printk函数来对console结构体进行调用并使用其成员,包括write函数等等。

/* console_cmdline结构体,包括:控制台名字,设备号,驱动 */
struct console_cmdline
{
    char    name[8];            /* Name of the driver */
    int index;              /* Minor dev. to use */
    char    *options;           /* Options for the driver */
};

4.2、printk函数

在printk里面定义有各个打印级别

/* printk's without a loglevel use this.. */
#define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING,默认打印级别,警告级别 */

#define MINIMUM_CONSOLE_LOGLEVEL 1 /* Minimum loglevel we let people use,最低打印级别 */
#define DEFAULT_CONSOLE_LOGLEVEL 7 /* anything MORE serious than KERN_DEBUG,最高打印级别 */

如果级别大于等于4,也就是上面的警告级别,信息就会被打印出来,如果改动默认的打印级别就会导致更多或者更少的信息被打印

printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); //打印出来所在的文件,所在的函数,所在的行数
__FILE__, FUNCTION, __LINE__。通过编译器实现编译的时候代入字符串常量

你可能感兴趣的:(调试,ARM)