xref: /DragonOS/kernel/src/debug/kallsyms.c (revision 9029414af2089cbe7d2d2097be2e116c09beb6dd)
12813126eSlogin /**
22813126eSlogin  * @file kallsyms.c
32813126eSlogin  * @author longjin (longjin@RinGoTek.cn)
42813126eSlogin  * @brief 内核栈跟踪
52813126eSlogin  * @version 0.1
62813126eSlogin  * @date 2022-06-22
72813126eSlogin  *
82813126eSlogin  * @copyright Copyright (c) 2022
92813126eSlogin  *
102813126eSlogin  */
112813126eSlogin #include <stdint.h>
122813126eSlogin #include <stdio.h>
132813126eSlogin #include <stdlib.h>
142813126eSlogin #include <string.h>
152813126eSlogin 
162813126eSlogin /**
172813126eSlogin  * @brief 判断符号是否需要被输出(只输出text段内的符号)
182813126eSlogin  *
192813126eSlogin  */
202813126eSlogin #define symbol_to_write(vaddr, tv, etv) \
212813126eSlogin     ((vaddr < tv || vaddr > etv) ? 0 : 1)
222813126eSlogin 
232813126eSlogin /**
242813126eSlogin  * @brief 使用nm命令提取出来的信息存到这个结构体之中
252813126eSlogin  *
262813126eSlogin  */
272813126eSlogin struct kernel_symbol_entry_t
282813126eSlogin {
292813126eSlogin     uint64_t vaddr;
302813126eSlogin     char type;
312813126eSlogin     char *symbol;
322813126eSlogin     int symbol_length;
332813126eSlogin };
342813126eSlogin 
352813126eSlogin struct kernel_symbol_entry_t *symbol_table;
362813126eSlogin // 符号表最大能容纳的entry数量
372813126eSlogin uint64_t table_size = 0;
382813126eSlogin // 符号表当前的entry数量
392813126eSlogin uint64_t entry_count = 0;
402813126eSlogin // 符号表中,text和etext的下标
412813126eSlogin uint64_t text_vaddr, etext_vaddr;
422813126eSlogin 
432813126eSlogin /**
442813126eSlogin  * @brief 读取一个符号到entry之中
452813126eSlogin  *
462813126eSlogin  * @param filp stdin的文件指针
472813126eSlogin  * @param entry 待填写的entry
482813126eSlogin  * @return int 返回码
492813126eSlogin  */
502813126eSlogin int read_symbol(FILE *filp, struct kernel_symbol_entry_t *entry)
512813126eSlogin {
522813126eSlogin     // 本函数假设nm命令输出的结果中,每行最大512字节
532813126eSlogin     char str[512] = {0};
54*9029414aSzhaoyao73     char* s = fgets(str, sizeof(str), filp);
55*9029414aSzhaoyao73     if (s != str) {
56*9029414aSzhaoyao73         return -1;
572813126eSlogin     }
582813126eSlogin 
59*9029414aSzhaoyao73     char symbol_name[512] = {0};
60*9029414aSzhaoyao73     int retval = sscanf(str, "%llx %c %512c", &entry->vaddr, &entry->type, symbol_name);
61*9029414aSzhaoyao73 
62*9029414aSzhaoyao73     // 如果当前行不符合要求
63*9029414aSzhaoyao73     if (retval != 3) {
642813126eSlogin         return -1;
652813126eSlogin     }
662813126eSlogin     // malloc一块内存,然后把str的内容拷贝进去,接着修改symbol指针
67*9029414aSzhaoyao73     size_t len = strlen(symbol_name);
68*9029414aSzhaoyao73     if (len >= 1 && symbol_name[len - 1] == '\n') {
69*9029414aSzhaoyao73         symbol_name[len - 1] = '\0';
70*9029414aSzhaoyao73         len--;
71*9029414aSzhaoyao73     }
72*9029414aSzhaoyao73     entry->symbol = strdup(symbol_name);
73*9029414aSzhaoyao73     entry->symbol_length = len + 1; // +1的原因是.asciz指令会在字符串末尾自动添加结束符\0
742813126eSlogin     return 0;
752813126eSlogin }
762813126eSlogin 
772813126eSlogin /**
782813126eSlogin  * @brief 接收标准输入流的数据,解析nm命令输出的内容
792813126eSlogin  *
802813126eSlogin  * @param filp
812813126eSlogin  */
822813126eSlogin void read_map(FILE *filp)
832813126eSlogin {
842813126eSlogin     // 循环读入数据直到输入流结束
852813126eSlogin     while (!feof(filp))
862813126eSlogin     {
872813126eSlogin         // 给符号表扩容
882813126eSlogin         if (entry_count >= table_size)
892813126eSlogin         {
902813126eSlogin             table_size += 100;
912813126eSlogin             // 由于使用了realloc,因此符号表原有的内容会被自动的copy过去
922813126eSlogin             symbol_table = (struct kernel_symbol_entry_t *)realloc(symbol_table, sizeof(struct kernel_symbol_entry_t) * table_size);
932813126eSlogin         }
942813126eSlogin 
952813126eSlogin         // 若成功读取符号表的内容,则将计数器+1
962813126eSlogin         if (read_symbol(filp, &symbol_table[entry_count]) == 0)
972813126eSlogin             ++entry_count;
982813126eSlogin     }
992813126eSlogin 
1002813126eSlogin     // 查找符号表中的text和etext标签
1012813126eSlogin     for (uint64_t i = 0; i < entry_count; ++i)
1022813126eSlogin     {
103*9029414aSzhaoyao73         if (text_vaddr == 0ULL && strcmp(symbol_table[i].symbol, "_text") == 0)
1042813126eSlogin             text_vaddr = symbol_table[i].vaddr;
105*9029414aSzhaoyao73         if (etext_vaddr == 0ULL && strcmp(symbol_table[i].symbol, "_etext") == 0)
1062813126eSlogin             etext_vaddr = symbol_table[i].vaddr;
107*9029414aSzhaoyao73         if (text_vaddr != 0ULL && etext_vaddr != 0ULL)
108*9029414aSzhaoyao73             break;
1092813126eSlogin     }
1102813126eSlogin }
1112813126eSlogin 
1122813126eSlogin /**
1132813126eSlogin  * @brief 输出最终的kallsyms汇编代码文件
1142813126eSlogin  * 直接输出到stdout,通过命令行的 > 命令,写入文件
1152813126eSlogin  */
1162813126eSlogin void generate_result()
1172813126eSlogin {
1182813126eSlogin     printf(".section .rodata\n\n");
1192813126eSlogin     printf(".global kallsyms_address\n");
1202813126eSlogin     printf(".align 8\n\n");
1212813126eSlogin 
1222813126eSlogin     printf("kallsyms_address:\n"); // 地址数组
1232813126eSlogin 
1242813126eSlogin     uint64_t last_vaddr = 0;
1252813126eSlogin     uint64_t total_syms_to_write = 0; // 真正输出的符号的数量
1262813126eSlogin 
1272813126eSlogin     // 循环写入地址数组
1282813126eSlogin     for (uint64_t i = 0; i < entry_count; ++i)
1292813126eSlogin     {
1302813126eSlogin         // 判断是否为text段的符号
1312813126eSlogin         if (!symbol_to_write(symbol_table[i].vaddr, text_vaddr, etext_vaddr))
1322813126eSlogin             continue;
1332813126eSlogin 
1342813126eSlogin         if (symbol_table[i].vaddr == last_vaddr)
1352813126eSlogin             continue;
1362813126eSlogin 
1372813126eSlogin         // 输出符号地址
1382813126eSlogin         printf("\t.quad\t%#llx\n", symbol_table[i].vaddr);
1392813126eSlogin         ++total_syms_to_write;
1402813126eSlogin 
1412813126eSlogin         last_vaddr = symbol_table[i].vaddr;
1422813126eSlogin     }
1432813126eSlogin 
1442813126eSlogin     putchar('\n');
1452813126eSlogin 
1462813126eSlogin     // 写入符号表的表项数量
1472813126eSlogin     printf(".global kallsyms_num\n");
1482813126eSlogin     printf(".align 8\n");
1492813126eSlogin     printf("kallsyms_num:\n");
1502813126eSlogin     printf("\t.quad\t%lld\n", total_syms_to_write);
1512813126eSlogin 
1522813126eSlogin     putchar('\n');
1532813126eSlogin 
1542813126eSlogin     // 循环写入符号名称的下标索引
1552813126eSlogin     printf(".global kallsyms_names_index\n");
1562813126eSlogin     printf(".align 8\n");
1572813126eSlogin     printf("kallsyms_names_index:\n");
1582813126eSlogin     uint64_t position = 0;
1592813126eSlogin     last_vaddr = 0;
1602813126eSlogin     for (uint64_t i = 0; i < entry_count; ++i)
1612813126eSlogin     {
1622813126eSlogin         // 判断是否为text段的符号
1632813126eSlogin         if (!symbol_to_write(symbol_table[i].vaddr, text_vaddr, etext_vaddr))
1642813126eSlogin             continue;
1652813126eSlogin 
1662813126eSlogin         if (symbol_table[i].vaddr == last_vaddr)
1672813126eSlogin             continue;
1682813126eSlogin 
1692813126eSlogin         // 输出符号名称的偏移量
1702813126eSlogin         printf("\t.quad\t%lld\n", position);
1712813126eSlogin         position += symbol_table[i].symbol_length;
1722813126eSlogin         last_vaddr = symbol_table[i].vaddr;
1732813126eSlogin     }
1742813126eSlogin 
1752813126eSlogin     putchar('\n');
1762813126eSlogin 
1772813126eSlogin     // 输出符号名
1782813126eSlogin     printf(".global kallsyms_names\n");
1792813126eSlogin     printf(".align 8\n");
1802813126eSlogin     printf("kallsyms_names:\n");
1812813126eSlogin 
1822813126eSlogin     last_vaddr = 0;
1832813126eSlogin     for (uint64_t i = 0; i < entry_count; ++i)
1842813126eSlogin     {
1852813126eSlogin         // 判断是否为text段的符号
1862813126eSlogin         if (!symbol_to_write(symbol_table[i].vaddr, text_vaddr, etext_vaddr))
1872813126eSlogin             continue;
1882813126eSlogin 
1892813126eSlogin         if (symbol_table[i].vaddr == last_vaddr)
1902813126eSlogin             continue;
1912813126eSlogin 
1922813126eSlogin         // 输出符号名称
1932813126eSlogin         printf("\t.asciz\t\"%s\"\n", symbol_table[i].symbol);
1942813126eSlogin 
1952813126eSlogin         last_vaddr = symbol_table[i].vaddr;
1962813126eSlogin     }
1972813126eSlogin 
1982813126eSlogin     putchar('\n');
1992813126eSlogin 
2002813126eSlogin }
2012813126eSlogin int main(int argc, char **argv)
2022813126eSlogin {
2032813126eSlogin     read_map(stdin);
2042813126eSlogin 
2052813126eSlogin     generate_result();
2062813126eSlogin }