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 }