You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

内核模块编译报错:hci_uart.ko中func未定义的排查求助

Linux内核模块链接错误:undefined reference to "func"

问题背景

我正在修改一个基于Linux内核的大型仓库,简化后的文件结构如下:

├── asm
│   ├── mytee.h
├── kernel
│   ├── mytee.S
├── drivers
│   ├── bluetooth
|   |   ├── hci_uart.h
|   |   ├── hci_h4.c
│   ├── video
|   |   ├── fbmem.c

mytee.h中声明的func函数在mytee.S中通过汇编实现:

ENTRY(func)
// assembly code
ENDPROC(func)

在hci_h4.c中调用func时出现链接错误:

ERROR: "func" [drivers/bluetooth/hci_uart.ko] undefined!

hci_uart.ko是hci_h4.c对应的内核模块,但func在fbmem.c中可正常调用,未修改前仓库编译正常,推测是链接环节问题。相关Kbuild片段:

obj-$(CONFIG_FB)                  += fb.o
fb-y                              := fbmem.o fbmon.o fbcmap.o fbsysfs.o \
                                     modedb.o fbcvt.o
fb-objs                           := $(fb-y)

已完成的排查步骤:

  • 确认func已正确定义
  • hci_h4.c和fbmem.c均已包含mytee.h
  • 未发现两者的Makefile、Kbuild、Kconfig存在相关差异
  • 两者对func的调用方式完全一致
  • 确认fbmem.c和hci_h4.c在Makefile中晚于mytee.S被添加到drivers-y
  • 不存在同名文件导致符号混淆

我的疑问:

  1. C文件与汇编文件如何链接?未发现mytee.h与mytee.S的引用关系
  2. 带Hypercall参数的函数工作机制是怎样的?
  3. mytee.h未在任何Makefile中列出,是否会被编译?若未编译为何函数可被声明?
  4. hci_uart.ko出现在module.order但不在modules.builtin中,而fb.ko相反

我对C语言和Makefile较为陌生,可提供更多信息,请求协助排查问题。


问题根源分析与解决方案

核心错误原因

fb.ko是内置模块(出现在modules.builtin),会直接链接到内核镜像中;而hci_uart.ko是可加载模块(仅在module.order),需要单独链接。func定义在kernel/mytee.S中,属于内核核心部分:内置模块可直接引用内核核心的所有符号,但可加载模块默认只能访问内核导出的符号,这就是两者调用结果不同的原因。

疑问逐一解答

1. C文件与汇编文件的链接逻辑

  • 汇编文件mytee.S会被编译成目标文件(.o),最终链接到内核核心镜像中。
  • C文件调用汇编函数时,只需通过mytee.h声明函数原型(告知编译器函数的参数、返回值类型),编译阶段编译器生成符号引用,链接阶段再从内核镜像或导出符号表中查找定义。
  • mytee.h和mytee.S不需要在Makefile中建立直接引用关系,只要mytee.S被编译进内核核心,且C文件包含头文件声明即可。

2. 带Hypercall参数的函数工作机制

Hypercall是内核态向虚拟机监控器(VMM)发起的调用,这类函数的典型工作流程是:

  1. 汇编代码中将Hypercall的参数、调用号放入指定寄存器
  2. 执行特定指令(如x86的vmcall、ARM的hvc)陷入VMM
  3. VMM根据调用号处理对应逻辑,完成后返回内核态
  4. 汇编函数整理返回值,传递给调用的C函数

3. mytee.h是否需要被编译?

头文件不需要单独编译,它只是在C文件编译时通过#include被引入,提供函数声明、宏定义等信息,让编译器知道如何处理对func的调用。只要C文件包含了mytee.h,编译器就能识别函数原型,不会在编译阶段报错,链接阶段才会检查符号是否存在。

4. module.order与modules.builtin的区别

  • modules.builtin:记录内置模块,这类模块会被直接编译进内核镜像,启动时自动加载,可直接访问内核核心的所有符号。
  • module.order:记录所有可编译的模块(包括内置和可加载),不在modules.builtin中的就是可加载模块,这类模块只能访问内核通过EXPORT_SYMBOL或EXPORT_SYMBOL_GPL导出的符号。

解决链接错误的两种方案

方案1:将hci_uart.ko改为内置模块

在对应的Kconfig配置中,将CONFIG_BT_HCI_UART的设置从m(编译为可加载模块)改为y(编译为内置模块),这样编译时会把它直接链接到内核镜像,即可正常访问func符号。

方案2:导出func符号

在mytee.S中为func添加符号导出声明(不同架构语法略有差异,以x86为例):

ENTRY(func)
// assembly code
ENDPROC(func)
EXPORT_SYMBOL(func)

添加后,hci_uart.ko作为可加载模块就能通过内核导出符号表找到func的定义。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 08:13:12