You need to enable JavaScript to run this app. 使用SYSCALL_DEFINE0实现自定义系统调用的内核模块编译报未定义符号错误
优惠活动
大模型
产品
解决方案
定价
更多

使用SYSCALL_DEFINE0实现自定义系统调用的内核模块编译报未定义符号错误

报错根因

本次编译出现的4个未定义符号(enter_syscall_print_funcs、event_class_syscall_enter、exit_syscall_print_funcs、event_class_syscall_exit),均是内核开启CONFIG_FTRACE_SYSCALLS配置时,SYSCALL_DEFINE系列宏自动关联的系统调用追踪内部钩子。
上述钩子函数属于内核trace子系统的内部实现,没有通过EXPORT_SYMBOL导出为外部可加载模块可访问的接口,在独立模块中使用SYSCALL_DEFINE0定义系统调用时,modpost模块检查阶段会因找不到这些未导出符号,直接中断编译流程。

解决办法

优先方案:绕开SYSCALL_DEFINE宏的自动逻辑

放弃在外部模块中使用SYSCALL_DEFINE0定义系统调用的写法,通过手动修改系统调用表的方式注册自定义系统调用,从根源上避开宏展开带来的未定义符号引用:

  • 移除原代码中的SYSCALL_DEFINE0代码块,将系统调用逻辑改写为普通的内核静态函数:
static long testsyscall_handler(void)
{
    printk(KERN_INFO "Test\n");
    return 0;
}
  • 模块初始化阶段,通过kallsyms_lookup_name接口获取未导出的sys_call_table内核符号地址,按x86平台要求临时关闭页表写保护,将系统调用号436对应的表项值替换为testsyscall_handler的函数地址;模块卸载时恢复该表项的原始值。
  • 补全原代码的内存回收逻辑:在cleanup_module中调用kfree(addr),避免模块卸载后出现永久内核内存泄漏。

该方案无需重新编译内核,Makefile保持原有配置即可正常完成编译。

备选方案:将系统调用编入内核镜像

如果必须使用SYSCALL_DEFINE系列宏的原生行为,可以将自定义系统调用的代码移入内核核心源码目录,修改对应的系统调用表注册逻辑后重新编译、安装内核。该方式虽能解决符号链接问题,但流程繁琐,调试效率极低,仅适合长期运行的定制化内核场景。

风险提示
  • 不要尝试通过硬编码地址、运行时符号解析的方式强行链接上述trace类内部符号,这类符号属于内核非公开实现,不同内核版本的内存布局、权限控制逻辑差异极大,强行引用会直接触发内核空指针异常、权限校验失效,导致系统崩溃。
  • 修改sys_call_table的操作必须临时关闭页表写保护位,操作完成后第一时间恢复原有写保护配置,避免破坏内核的内存安全防护机制。

内容的提问来源于stack exchange,提问作者BitFriends

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.23 13:36:03