1 #include "traceback.h"
2 #include <common/printk.h>
3 #include <common/string.h>
4 #include <process/process.h>
5
lookup_kallsyms(uint64_t addr,int level)6 int lookup_kallsyms(uint64_t addr, int level)
7 {
8 const char *str = (const char *)&kallsyms_names;
9
10 // 暴力查找符合要求的symbol
11 // todo: 改用二分搜索。
12 // 由于符号表使用nm -n生成,因此是按照地址升序排列的,因此可以二分
13 uint64_t index = 0;
14 for (index = 0; index < kallsyms_num - 1; ++index)
15 {
16 if (addr > kallsyms_address[index] && addr <= kallsyms_address[index + 1])
17 break;
18 }
19
20 if (index < kallsyms_num) // 找到对应的函数
21 {
22 // 依次输出函数名称、rip离函数起始处的偏移量、函数执行的rip
23 printk("function:%s() \t(+) %04d address:%#018lx\n", &str[kallsyms_names_index[index]], addr - kallsyms_address[index], addr);
24 return 0;
25 }
26 else
27 return -1;
28 }
29
addr_from_symbol(const char * symbol)30 uint64_t addr_from_symbol(const char *symbol)
31 {
32 const char *str = (const char *)&kallsyms_names;
33 for (uint64_t i = 0; i < kallsyms_num; ++i)
34 {
35 if (strcmp(&str[kallsyms_names_index[i]], symbol) == 0)
36 return kallsyms_address[i];
37 }
38 return 0;
39
40 }
41
42 /**
43 * @brief 追溯内核栈调用情况
44 *
45 * @param regs 内核栈结构体
46 */
traceback(struct pt_regs * regs)47 void traceback(struct pt_regs *regs)
48 {
49 // 先检验是否为用户态出错,若为用户态出错,则直接返回
50 if (verify_area(regs->rbp, 0))
51 {
52 printk_color(YELLOW, BLACK, "Kernel traceback: Fault in userland. pid=%ld, rbp=%#018lx\n", rs_current_pcb_pid(), regs->rbp);
53 return;
54 }
55
56 uint64_t *rbp = (uint64_t *)regs->rbp;
57 printk_color(YELLOW, BLACK, "======== Kernel traceback =======\n");
58 // printk("&kallsyms_address:%#018lx,kallsyms_address:%#018lx\n", &kallsyms_address, kallsyms_address);
59 // printk("&kallsyms_syms_num:%#018lx,kallsyms_syms_num:%d\n", &kallsyms_num, kallsyms_num);
60 // printk("&kallsyms_index:%#018lx\n", &kallsyms_names_index);
61 // printk("&kallsyms_names:%#018lx,kallsyms_names:%s\n", &kallsyms_names, &kallsyms_names);
62
63 uint64_t ret_addr = regs->rip;
64 // 最大追踪10层调用栈
65 for (int i = 0; i < 10; ++i)
66 {
67 if (lookup_kallsyms(ret_addr, i) != 0)
68 break;
69
70 // 当前栈帧的rbp的地址大于等于内核栈的rbp的时候,表明调用栈已经到头了,追踪结束。
71 // 当前rbp的地址为用户空间时,直接退出
72 if ((uint64_t)(rbp) >= rs_current_pcb_thread_rbp() || ((uint64_t)rbp < regs->rsp))
73 break;
74
75 printk_color(ORANGE, BLACK, "rbp:%#018lx,*rbp:%#018lx\n", rbp, *rbp);
76
77 // 由于x86处理器在执行call指令时,先将调用返回地址压入栈中,然后再把函数的rbp入栈,最后将rsp设为新的rbp。
78 // 因此,此处的rbp就是上一层的rsp,那么,*(rbp+1)得到的就是上一层函数的返回地址
79 ret_addr = *(rbp + 1);
80 rbp = (uint64_t *)(*rbp);
81 printk("\n");
82 }
83 printk_color(YELLOW, BLACK, "======== Kernel traceback end =======\n");
84 }