xref: /DragonOS/kernel/src/debug/kallsyms.c (revision 3c82aa56d1b784ea7371100b3e906365be8332fd)
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  */
read_symbol(FILE * filp,struct kernel_symbol_entry_t * entry)502813126eSlogin int read_symbol(FILE *filp, struct kernel_symbol_entry_t *entry)
512813126eSlogin {
522813126eSlogin     // 本函数假设nm命令输出的结果中,每行最大512字节
532813126eSlogin     char str[512] = {0};
549029414aSzhaoyao73     char *s = fgets(str, sizeof(str), filp);
55*3c82aa56SChiichen     if (s != str)
56*3c82aa56SChiichen     {
579029414aSzhaoyao73         return -1;
582813126eSlogin     }
592813126eSlogin 
609029414aSzhaoyao73     char symbol_name[512] = {0};
619029414aSzhaoyao73     int retval = sscanf(str, "%llx %c %512c", &entry->vaddr, &entry->type, symbol_name);
629029414aSzhaoyao73 
639029414aSzhaoyao73     // 如果当前行不符合要求
64*3c82aa56SChiichen     if (retval != 3)
65*3c82aa56SChiichen     {
662813126eSlogin         return -1;
672813126eSlogin     }
682813126eSlogin     // malloc一块内存,然后把str的内容拷贝进去,接着修改symbol指针
699029414aSzhaoyao73     size_t len = strlen(symbol_name);
70*3c82aa56SChiichen     if (len >= 1 && symbol_name[len - 1] == '\n')
71*3c82aa56SChiichen     {
729029414aSzhaoyao73         symbol_name[len - 1] = '\0';
739029414aSzhaoyao73         len--;
749029414aSzhaoyao73     }
75*3c82aa56SChiichen     // 转义双引号
76*3c82aa56SChiichen     for (int i = 0; i < len; i++)
77*3c82aa56SChiichen     {
78*3c82aa56SChiichen         if (symbol_name[i] == '"')
79*3c82aa56SChiichen         {
80*3c82aa56SChiichen             char temp[len - i];
81*3c82aa56SChiichen             memcpy(temp, symbol_name + i, len - i);
82*3c82aa56SChiichen             symbol_name[i] = '\\';
83*3c82aa56SChiichen             memcpy(symbol_name + i + 1, temp, len - i);
84*3c82aa56SChiichen             i++;
85*3c82aa56SChiichen         }
86*3c82aa56SChiichen     }
879029414aSzhaoyao73     entry->symbol = strdup(symbol_name);
889029414aSzhaoyao73     entry->symbol_length = len + 1; // +1的原因是.asciz指令会在字符串末尾自动添加结束符\0
892813126eSlogin     return 0;
902813126eSlogin }
912813126eSlogin 
922813126eSlogin /**
932813126eSlogin  * @brief 接收标准输入流的数据,解析nm命令输出的内容
942813126eSlogin  *
952813126eSlogin  * @param filp
962813126eSlogin  */
read_map(FILE * filp)972813126eSlogin void read_map(FILE *filp)
982813126eSlogin {
992813126eSlogin     // 循环读入数据直到输入流结束
1002813126eSlogin     while (!feof(filp))
1012813126eSlogin     {
1022813126eSlogin         // 给符号表扩容
1032813126eSlogin         if (entry_count >= table_size)
1042813126eSlogin         {
1052813126eSlogin             table_size += 100;
1062813126eSlogin             // 由于使用了realloc,因此符号表原有的内容会被自动的copy过去
1072813126eSlogin             symbol_table = (struct kernel_symbol_entry_t *)realloc(symbol_table, sizeof(struct kernel_symbol_entry_t) * table_size);
1082813126eSlogin         }
1092813126eSlogin 
1102813126eSlogin         // 若成功读取符号表的内容,则将计数器+1
1112813126eSlogin         if (read_symbol(filp, &symbol_table[entry_count]) == 0)
1122813126eSlogin             ++entry_count;
1132813126eSlogin     }
1142813126eSlogin 
1152813126eSlogin     // 查找符号表中的text和etext标签
1162813126eSlogin     for (uint64_t i = 0; i < entry_count; ++i)
1172813126eSlogin     {
1189029414aSzhaoyao73         if (text_vaddr == 0ULL && strcmp(symbol_table[i].symbol, "_text") == 0)
1192813126eSlogin             text_vaddr = symbol_table[i].vaddr;
1209029414aSzhaoyao73         if (etext_vaddr == 0ULL && strcmp(symbol_table[i].symbol, "_etext") == 0)
1212813126eSlogin             etext_vaddr = symbol_table[i].vaddr;
1229029414aSzhaoyao73         if (text_vaddr != 0ULL && etext_vaddr != 0ULL)
1239029414aSzhaoyao73             break;
1242813126eSlogin     }
1252813126eSlogin }
1262813126eSlogin 
1272813126eSlogin /**
1282813126eSlogin  * @brief 输出最终的kallsyms汇编代码文件
1292813126eSlogin  * 直接输出到stdout,通过命令行的 > 命令,写入文件
1302813126eSlogin  */
generate_result()1312813126eSlogin void generate_result()
1322813126eSlogin {
1332813126eSlogin     printf(".section .rodata\n\n");
1342813126eSlogin     printf(".global kallsyms_address\n");
1352813126eSlogin     printf(".align 8\n\n");
1362813126eSlogin 
1372813126eSlogin     printf("kallsyms_address:\n"); // 地址数组
1382813126eSlogin 
1392813126eSlogin     uint64_t last_vaddr = 0;
1402813126eSlogin     uint64_t total_syms_to_write = 0; // 真正输出的符号的数量
1412813126eSlogin 
1422813126eSlogin     // 循环写入地址数组
1432813126eSlogin     for (uint64_t i = 0; i < entry_count; ++i)
1442813126eSlogin     {
1452813126eSlogin         // 判断是否为text段的符号
1462813126eSlogin         if (!symbol_to_write(symbol_table[i].vaddr, text_vaddr, etext_vaddr))
1472813126eSlogin             continue;
1482813126eSlogin 
1492813126eSlogin         if (symbol_table[i].vaddr == last_vaddr)
1502813126eSlogin             continue;
1512813126eSlogin 
1522813126eSlogin         // 输出符号地址
1532813126eSlogin         printf("\t.quad\t%#llx\n", symbol_table[i].vaddr);
1542813126eSlogin         ++total_syms_to_write;
1552813126eSlogin 
1562813126eSlogin         last_vaddr = symbol_table[i].vaddr;
1572813126eSlogin     }
1582813126eSlogin 
1592813126eSlogin     putchar('\n');
1602813126eSlogin 
1612813126eSlogin     // 写入符号表的表项数量
1622813126eSlogin     printf(".global kallsyms_num\n");
1632813126eSlogin     printf(".align 8\n");
1642813126eSlogin     printf("kallsyms_num:\n");
1652813126eSlogin     printf("\t.quad\t%lld\n", total_syms_to_write);
1662813126eSlogin 
1672813126eSlogin     putchar('\n');
1682813126eSlogin 
1692813126eSlogin     // 循环写入符号名称的下标索引
1702813126eSlogin     printf(".global kallsyms_names_index\n");
1712813126eSlogin     printf(".align 8\n");
1722813126eSlogin     printf("kallsyms_names_index:\n");
1732813126eSlogin     uint64_t position = 0;
1742813126eSlogin     last_vaddr = 0;
1752813126eSlogin     for (uint64_t i = 0; i < entry_count; ++i)
1762813126eSlogin     {
1772813126eSlogin         // 判断是否为text段的符号
1782813126eSlogin         if (!symbol_to_write(symbol_table[i].vaddr, text_vaddr, etext_vaddr))
1792813126eSlogin             continue;
1802813126eSlogin 
1812813126eSlogin         if (symbol_table[i].vaddr == last_vaddr)
1822813126eSlogin             continue;
1832813126eSlogin 
1842813126eSlogin         // 输出符号名称的偏移量
1852813126eSlogin         printf("\t.quad\t%lld\n", position);
1862813126eSlogin         position += symbol_table[i].symbol_length;
1872813126eSlogin         last_vaddr = symbol_table[i].vaddr;
1882813126eSlogin     }
1892813126eSlogin 
1902813126eSlogin     putchar('\n');
1912813126eSlogin 
1922813126eSlogin     // 输出符号名
1932813126eSlogin     printf(".global kallsyms_names\n");
1942813126eSlogin     printf(".align 8\n");
1952813126eSlogin     printf("kallsyms_names:\n");
1962813126eSlogin 
1972813126eSlogin     last_vaddr = 0;
1982813126eSlogin     for (uint64_t i = 0; i < entry_count; ++i)
1992813126eSlogin     {
2002813126eSlogin         // 判断是否为text段的符号
2012813126eSlogin         if (!symbol_to_write(symbol_table[i].vaddr, text_vaddr, etext_vaddr))
2022813126eSlogin             continue;
2032813126eSlogin 
2042813126eSlogin         if (symbol_table[i].vaddr == last_vaddr)
2052813126eSlogin             continue;
2062813126eSlogin 
2072813126eSlogin         // 输出符号名称
2082813126eSlogin         printf("\t.asciz\t\"%s\"\n", symbol_table[i].symbol);
2092813126eSlogin 
2102813126eSlogin         last_vaddr = symbol_table[i].vaddr;
2112813126eSlogin     }
2122813126eSlogin 
2132813126eSlogin     putchar('\n');
2142813126eSlogin }
main(int argc,char ** argv)2152813126eSlogin int main(int argc, char **argv)
2162813126eSlogin {
2172813126eSlogin     read_map(stdin);
2182813126eSlogin 
2192813126eSlogin     generate_result();
2202813126eSlogin }