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 }