电子说
本文是在qemu-virt64-aarch64下运行smart-fetch的调试记录
准备
编译
按照userapps的readme进行编译,编译前键入如下命令,即可编译出带调试信息的userapps
xmake f --mode=debug
gdb配置
在调试过程中需要进入内核,所以需要同时加载内核和应用程序的elf符号表。设置.gdbinit文件如下:
target remote localhost:1234
file ../../../rt-thread/bsp/qemu-virt64-aarch64/rtthread.elf
add-symbol-file ../../apps/build/rootfs/bin/smart-fetch
首先使用readelf工具查看smart-fetch程序,发现其entry point是0x200000的_start,于是在gdb中打断点b *0x200000(或b _start),在输入smart-fetch后成功暂停
libc库查看
本工程采用musl libc,可以自行下载查看源码
调试记录
以下按函数执行的顺序来进行记录
_start
该函数将当前用户态栈指针设置为0xffff80000000,为用户空间较高的地址,之后跳到_start_c
_start_c
所传入的参数*p地址为smart预先设置的0xfffffffff000。这里存放了应用程序初始化所需的参数,由操作系统设置,最后调用传参为__libc_start_main(main, 1, "/bin/smart-fetch",_init,_fini, 0 )
__libc_start_main
首先获得程序的环境变量(操作系统设置,含有操作系统名称,执行文件的信息等)
调用__init_libc(envp, "/bin/smart-fetch")
asm ( "" : "+r"(stage2) : : "memory" )表示保证在之后stage2采用更新后的值,避免使用缓存的值
__init_libc
该函数前面主要是通过envp来获取一些程序信息如pagesize等,接下来执行__init_tls和__init_ssp
__init_tls
该函数目的是初始化线程局部存储(thread local storage)。它需要根据elf文件的类型为PT_TLS的段来对线程局部存储进行初始化。这里由于smart-fetch不是多线程程序,没有PT_TLS类型的段,故该函数在这里没有进行实际的操作。
__init_ssp
该函数是初始化栈保护机制,但这里并未实现该机制。执行dummy函数,不做任何操作
libc_start_main_stage2
首先执行__libc_start_init进行初始化
之后调用main函数获取返回值
之后调用exit(main_ret)来进行清理
__libc_start_init
调用事先注册好的初始化函数列表__init_array_start。
本处调用了frame_dummy函数,该函数并不是musl库里的函数,而是gcc编译时插入的函数。该函数调用初始化函数(即定义了__attribute__((constructor))的函数),并调用register_tm_clones来注册多线程下的克隆函数。因为这个程序无初始化函数和多线程的特性,所以该函数实际上并未执行任何操作。
exit
__funcs_on_exit:无操作
__libc_exit_fini:该函数对应于__libc_start_init,调用解构函数,注销多线程克隆函数,在这里也没有实质性的操作
_stdio_exit:关闭所有打开文件及标准输入、输出、错误文件
_Exit:调用SYS_exit_group系统调用,之后就是操作系统进行资源回收
缺页异常处理
当访问一个不存在页时,aarch改变的寄存器
sp 0xffff80000000 -> 0xffff0000002b6520
pc 0x20008 -> 0xffff0000000c6400
cpsr 0 -> 0x3c5
elr_el1 0x200000 -> 0x200008
sp_el0 0 -> 0xffff80000000
ESR_EL1 0-> 0x92000045
FAR_EL1 0 -> 0xffff7fffffd0
aarch64自动做的事(通过对比缺页触发前后寄存器值得出):
全部0条评论
快来发表一下你的评论吧 !