DragonOS/kernel/debug/kallsyms.c
2022-06-22 23:31:47 +08:00

200 lines
5.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @file kallsyms.c
* @author longjin (longjin@RinGoTek.cn)
* @brief 内核栈跟踪
* @version 0.1
* @date 2022-06-22
*
* @copyright Copyright (c) 2022
*
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
* @brief 判断符号是否需要被输出只输出text段内的符号
*
*/
#define symbol_to_write(vaddr, tv, etv) \
((vaddr < tv || vaddr > etv) ? 0 : 1)
/**
* @brief 使用nm命令提取出来的信息存到这个结构体之中
*
*/
struct kernel_symbol_entry_t
{
uint64_t vaddr;
char type;
char *symbol;
int symbol_length;
};
struct kernel_symbol_entry_t *symbol_table;
// 符号表最大能容纳的entry数量
uint64_t table_size = 0;
// 符号表当前的entry数量
uint64_t entry_count = 0;
// 符号表中text和etext的下标
uint64_t text_vaddr, etext_vaddr;
/**
* @brief 读取一个符号到entry之中
*
* @param filp stdin的文件指针
* @param entry 待填写的entry
* @return int 返回码
*/
int read_symbol(FILE *filp, struct kernel_symbol_entry_t *entry)
{
// 本函数假设nm命令输出的结果中每行最大512字节
char str[512] = {0};
int retval = fscanf(filp, "%llx %c %510s\n", &entry->vaddr, &entry->type, str);
// 如果当前行不符合要求
if (retval != 3)
{
if (retval != EOF)
{
// 如果不是输入流的结尾,说明该行不符合要求,将其过滤
fgets(str, 512, filp);
}
return -1;
}
// malloc一块内存然后把str的内容拷贝进去接着修改symbol指针
entry->symbol = strdup(str);
entry->symbol_length = strlen(str) + 1; // +1的原因是.asciz指令会在字符串末尾自动添加结束符\0
return 0;
}
/**
* @brief 接收标准输入流的数据解析nm命令输出的内容
*
* @param filp
*/
void read_map(FILE *filp)
{
// 循环读入数据直到输入流结束
while (!feof(filp))
{
// 给符号表扩容
if (entry_count >= table_size)
{
table_size += 100;
// 由于使用了realloc因此符号表原有的内容会被自动的copy过去
symbol_table = (struct kernel_symbol_entry_t *)realloc(symbol_table, sizeof(struct kernel_symbol_entry_t) * table_size);
}
// 若成功读取符号表的内容,则将计数器+1
if (read_symbol(filp, &symbol_table[entry_count]) == 0)
++entry_count;
}
// 查找符号表中的text和etext标签
for (uint64_t i = 0; i < entry_count; ++i)
{
if (strcmp(symbol_table[i].symbol, "_text")==0)
text_vaddr = symbol_table[i].vaddr;
if (strcmp(symbol_table[i].symbol, "_etext")==0)
etext_vaddr = symbol_table[i].vaddr;
}
}
/**
* @brief 输出最终的kallsyms汇编代码文件
* 直接输出到stdout通过命令行的 > 命令,写入文件
*/
void generate_result()
{
printf(".section .rodata\n\n");
printf(".global kallsyms_address\n");
printf(".align 8\n\n");
printf("kallsyms_address:\n"); // 地址数组
uint64_t last_vaddr = 0;
uint64_t total_syms_to_write = 0; // 真正输出的符号的数量
// 循环写入地址数组
for (uint64_t i = 0; i < entry_count; ++i)
{
// 判断是否为text段的符号
if (!symbol_to_write(symbol_table[i].vaddr, text_vaddr, etext_vaddr))
continue;
if (symbol_table[i].vaddr == last_vaddr)
continue;
// 输出符号地址
printf("\t.quad\t%#llx\n", symbol_table[i].vaddr);
++total_syms_to_write;
last_vaddr = symbol_table[i].vaddr;
}
putchar('\n');
// 写入符号表的表项数量
printf(".global kallsyms_num\n");
printf(".align 8\n");
printf("kallsyms_num:\n");
printf("\t.quad\t%lld\n", total_syms_to_write);
putchar('\n');
// 循环写入符号名称的下标索引
printf(".global kallsyms_names_index\n");
printf(".align 8\n");
printf("kallsyms_names_index:\n");
uint64_t position = 0;
last_vaddr = 0;
for (uint64_t i = 0; i < entry_count; ++i)
{
// 判断是否为text段的符号
if (!symbol_to_write(symbol_table[i].vaddr, text_vaddr, etext_vaddr))
continue;
if (symbol_table[i].vaddr == last_vaddr)
continue;
// 输出符号名称的偏移量
printf("\t.quad\t%lld\n", position);
position += symbol_table[i].symbol_length;
last_vaddr = symbol_table[i].vaddr;
}
putchar('\n');
// 输出符号名
printf(".global kallsyms_names\n");
printf(".align 8\n");
printf("kallsyms_names:\n");
last_vaddr = 0;
for (uint64_t i = 0; i < entry_count; ++i)
{
// 判断是否为text段的符号
if (!symbol_to_write(symbol_table[i].vaddr, text_vaddr, etext_vaddr))
continue;
if (symbol_table[i].vaddr == last_vaddr)
continue;
// 输出符号名称
printf("\t.asciz\t\"%s\"\n", symbol_table[i].symbol);
last_vaddr = symbol_table[i].vaddr;
}
putchar('\n');
}
int main(int argc, char **argv)
{
read_map(stdin);
generate_result();
}