get_kernel_syms - 获取内核符号表信息(已废弃)
1. 函数介绍
get_kernel_syms 是一个已废弃的 Linux 系统调用,用于获取内核符号表的信息。它曾经被用来检索内核中导出的符号(函数和变量)的列表,包括它们的地址、类型和名称。
data-ad-format="fluid"
data-ad-layout-key="-7k+ex-4a-9w+4a">
重要说明:这个系统调用在现代 Linux 内核中已被废弃和移除,不再推荐使用。现代系统应该使用其他方式来获取内核符号信息。
2. 函数原型
1 2 3 4
| #include <linux/kernel.h>
int get_kernel_syms(struct kernel_sym *table);
|
3. 功能
获取内核符号表中导出符号的信息,包括符号名称、地址和类型。主要用于内核调试、模块加载和系统分析工具。
4. 参数
struct kernel_sym *table: 指向 kernel_sym 结构体数组的指针
如果为 NULL:返回符号表中的符号总数
如果非 NULL:将符号信息填充到该数组中
5. kernel_sym 结构体定义
1 2 3 4 5
| struct kernel_sym { unsigned long ks_addr; /* 符号地址 */ char ks_name[60]; /* 符号名称(最多59个字符+null终止符)*/ };
|
6. 返回值
成功时:
失败时返回 -1,并设置 errno
7. 常见 errno 错误码
8. 相似函数,或关联函数
/proc/kallsyms: 现代系统中获取内核符号的标准方式
/proc/sys/kernel/kptr_restrict: 控制内核指针显示的设置
klogctl(): 控制内核日志缓冲区
uname(): 获取系统信息
sysinfo(): 获取系统统计信息
nm 命令:用户态工具查看目标文件符号表
/boot/System.map: 内核构建时生成的符号映射文件
9. 示例代码
示例1:检查系统是否支持 get_kernel_syms
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/syscall.h> #include <errno.h> #include <string.h>
#ifndef SYS_get_kernel_syms # define SYS_get_kernel_syms 177 // x86_64 架构下的系统调用号 #endif
int main() { long result; printf("检查系统是否支持 get_kernel_syms...\n"); // 首先尝试获取符号数量(传入 NULL) result = syscall(SYS_get_kernel_syms, NULL); if (result == -1) { printf("get_kernel_syms 调用失败\n"); printf("errno = %d: %s\n", errno, strerror(errno)); switch (errno) { case ENOSYS: printf("系统调用已废弃或不支持\n"); break; case EPERM: printf("权限不足,需要特权权限\n"); break; default: printf("其他错误\n"); break; } } else { printf("系统支持 get_kernel_syms,符号数量: %ld\n", result); } return 0; }
|
示例2:传统使用方式(仅在旧系统上可能工作)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/syscall.h> #include <errno.h> #include <string.h>
#ifndef SYS_get_kernel_syms # define SYS_get_kernel_syms 177 #endif
// 注意:现代 glibc 可能不包含这个结构体定义 struct kernel_sym { unsigned long ks_addr; char ks_name[60]; };
int main() { long sym_count; struct kernel_sym *sym_table; long result; int i; printf("=== get_kernel_syms 测试 ===\n"); // 第一次调用:获取符号数量 sym_count = syscall(SYS_get_kernel_syms, NULL); if (sym_count == -1) { printf("获取符号数量失败: %s\n", strerror(errno)); if (errno == ENOSYS) { printf("提示: get_kernel_syms 已在现代内核中废弃\n"); printf("请使用 /proc/kallsyms 替代\n"); } return 1; } printf("内核符号数量: %ld\n", sym_count); if (sym_count == 0) { printf("没有可用的内核符号\n"); return 0; } // 限制获取的符号数量以避免内存问题 if (sym_count > 1000) { printf("符号数量过多,限制为前1000个\n"); sym_count = 1000; } // 分配内存 sym_table = malloc(sym_count * sizeof(struct kernel_sym)); if (sym_table == NULL) { perror("内存分配失败"); return 1; } // 第二次调用:获取符号信息 result = syscall(SYS_get_kernel_syms, sym_table); if (result == -1) { printf("获取符号信息失败: %s\n", strerror(errno)); free(sym_table); return 1; } printf("成功获取 %ld 个符号信息\n", result); // 显示前几个符号作为示例 printf("\n前10个内核符号:\n"); printf("%-18s %s\n", "地址", "符号名称"); printf("%-18s %s\n", "----", "--------"); for (i = 0; i < result && i < 10; i++) { printf("0x%016lx %s\n", sym_table[i].ks_addr, sym_table[i].ks_name); } free(sym_table); return 0; }
|
10. 现代替代方案
由于 get_kernel_syms 已被废弃,现代系统应该使用以下替代方案:
方案1:使用 /proc/kallsyms
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| #include <stdio.h> #include <stdlib.h>
int main() { FILE *fp; char line[256]; char address[32]; char type; char symbol[128]; int count = 0; fp = fopen("/proc/kallsyms", "r"); if (fp == NULL) { perror("打开 /proc/kallsyms 失败"); return 1; } printf("读取内核符号表 (/proc/kallsyms):\n"); printf("%-18s %-8s %s\n", "地址", "类型", "符号"); printf("%-18s %-8s %s\n", "----", "----", "----"); // 读取前20个符号 while (fgets(line, sizeof(line), fp) && count < 20) { if (sscanf(line, "%s %c %s", address, &type, symbol) == 3) { printf("%-18s %-8c %s\n", address, type, symbol); count++; } } fclose(fp); // 检查 kptr_restrict 设置 fp = fopen("/proc/sys/kernel/kptr_restrict", "r"); if (fp != NULL) { int restrict_level; if (fscanf(fp, "%d", &restrict_level) == 1) { printf("\nkptr_restrict 级别: %d\n", restrict_level); if (restrict_level > 0) { printf("注意: 内核指针可能被限制显示\n"); } } fclose(fp); } return 0; }
|
方案2:使用 System.map 文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| #include <stdio.h> #include <stdlib.h>
int main() { FILE *fp; char line[256]; char address[32]; char type; char symbol[128]; int count = 0; // 尝试打开 System.map 文件 const char *system_map_paths[] = { "/boot/System.map-$(uname -r)", // 需要 shell 展开 "/boot/System.map", "/lib/modules/$(uname -r)/System.map", // 需要 shell 展开 NULL }; // 简化版本:直接尝试常见路径 fp = fopen("/boot/System.map", "r"); if (fp == NULL) { printf("System.map 文件不可用\n"); printf("现代系统推荐使用 /proc/kallsyms\n"); return 1; } printf("读取 System.map 文件:\n"); printf("%-18s %-8s %s\n", "地址", "类型", "符号"); printf("%-18s %-8s %s\n", "----", "----", "----"); // 读取前10个符号 while (fgets(line, sizeof(line), fp) && count < 10) { if (sscanf(line, "%s %c %s", address, &type, symbol) == 3) { printf("%-18s %-8c %s\n", address, type, symbol); count++; } } fclose(fp); return 0; }
|
11. 符号类型说明
在 /proc/kallsyms 中,符号类型字符含义:
12. 安全考虑
现代 Linux 系统出于安全考虑,可能会限制内核符号信息的访问:
kptr_restrict: 控制内核指针的显示
0: 允许显示内核指针
1: 普通用户看不到内核指针(显示为0)
2: 所有用户都看不到内核指针
权限要求: 访问内核符号信息通常需要特殊权限
总结
get_kernel_syms 是一个已废弃的系统调用,现代 Linux 系统开发应该:
避免使用:该系统调用在新内核中已不可用
使用替代方案:推荐使用 /proc/kallsyms 获取内核符号信息
注意安全限制:了解 kptr_restrict 对符号显示的影响
权限管理:确保有足够的权限访问内核信息
兼容性考虑:在代码中检查系统调用是否可用
对于内核调试、模块开发和系统分析需求,现代工具链提供了更安全、更可靠的替代方案。
get_kernel_syms系统调用及示例-CSDN博客