在添加#pragma GCC optimize("O0") 后 Linux 内核构建期间,从一个函数到另一个函数的部分不匹配
Section mismatch in reference from a function to another function during Linux kernel build after adding #pragma GCC optimize("O0")
在Linux5.4.21源码中,我把
#pragma GCC push_options
#pragma GCC optimize ("O0")
和
#pragma GCC pop_options
围绕文件 /drivers/irqchip/irq-gic-v3.c
中的函数 static int __init gic_init_bases
。
当我构建它时,我收到此警告消息(部分不匹配)。后来我发现它实际上是导致它的 #pragma GCC optimize ("O0")
行。
CALL scripts/atomic/check-atomics.sh
CALL scripts/checksyscalls.sh
CHK include/generated/compile.h
CC arch/arm64/kernel/irq.o
CC arch/arm64/kernel/setup.o
CC drivers/irqchip/irq-gic-v3.o
AS arch/arm64/kernel/head.o
AR arch/arm64/kernel/built-in.a
AR arch/arm64/built-in.a
AR drivers/irqchip/built-in.a
AR drivers/built-in.a
GEN .version
CHK include/generated/compile.h
UPD include/generated/compile.h
CC init/version.o
AR init/built-in.a
LD vmlinux.o
MODPOST vmlinux.o
WARNING: vmlinux.o(.text+0x227cc0): Section mismatch in reference from the function gic_smp_init() to the function .init.text:set_smp_cross_call()
The function gic_smp_init() references
the function __init set_smp_cross_call().
This is often because gic_smp_init lacks a __init
annotation or the annotation of set_smp_cross_call is wrong.
MODINFO modules.builtin.modinfo
LD .tmp_vmlinux1
KSYM .tmp_kallsyms1.o
LD .tmp_vmlinux2
KSYM .tmp_kallsyms2.o
LD vmlinux
SORTEX vmlinux
SYSMAP System.map
OBJCOPY arch/arm64/boot/Image
函数调用链是这样的:gic_init_bases -> gic_smp_init() -> set_smp_cross_call
(目前是CONFIG_SMP=y
)。该消息似乎说 set_smp_cross_call
是用 __init
注释的(意思是它放在 .init.text
中),但 gic_smp_init
不是。
没有#pragma
调试设置,就没有这种警告。我不确定是否可以将 __init
添加到 gic_smp_init()
(或从 set_smp_cross_call
中删除 _init
)。正确的修复方法是什么?
如果我将 __init
添加到 gic_smp_init
,此警告就会消失,但我认为这会使该函数在初始化后被删除(也许可以吗?)。
TL;DR: gic_smp_init()
应该用 __init
注释。
更新:这已在内核 v5.8 中修复,这里是 relevant commit.
在我看来您发现了一个错误:gic_smp_init()
仅由 __init gic_init_bases()
调用,因此没有真正的理由不使用 __init
进行注释。函数 set_smp_cross_call()
被注释为 __init
,因此调用者也应被注释为 __init
.
如果您使用优化(没有 pragma)进行编译,这种不一致就会消失,因为编译器只是将 gic_smp_init()
的整个主体内联到 gic_init_bases()
中:那么调用链就是 gic_init_bases -> set_smp_cross_call
一切都很好,因为它们都带有注释 __init
。但是,当您禁用优化 (#pragma GCC optimize ("O0")
) 时,编译器不再内联对 gic_smp_init()
的调用,它作为一个实际函数保持独立,并且不一致暴露出来。
模块的作者可能错过了 set_smp_cross_call()
的 __init
注释,或者注释是后来添加的,但没有人注意到它也需要在该驱动程序代码中“传播”。
set_smp_cross_call()
的注释之间似乎也存在不一致:在 C 文件 (/arch/{arm,arm64}/kernel/smp.c
) 中,它被注释为 __init
,而在头文件中 (/arch/{arm,arm64}/include/asm/smp.h
) 它不是。后面的实例也应该被注释为 the doc-comment 对于 __init
宏建议:
* If the function has a prototype somewhere, you can also add
* __init between closing brace of the prototype and semicolon:
*
* extern int initialize_foobar_device(int, int, int) __init;
在Linux5.4.21源码中,我把
#pragma GCC push_options
#pragma GCC optimize ("O0")
和
#pragma GCC pop_options
围绕文件 /drivers/irqchip/irq-gic-v3.c
中的函数 static int __init gic_init_bases
。
当我构建它时,我收到此警告消息(部分不匹配)。后来我发现它实际上是导致它的 #pragma GCC optimize ("O0")
行。
CALL scripts/atomic/check-atomics.sh
CALL scripts/checksyscalls.sh
CHK include/generated/compile.h
CC arch/arm64/kernel/irq.o
CC arch/arm64/kernel/setup.o
CC drivers/irqchip/irq-gic-v3.o
AS arch/arm64/kernel/head.o
AR arch/arm64/kernel/built-in.a
AR arch/arm64/built-in.a
AR drivers/irqchip/built-in.a
AR drivers/built-in.a
GEN .version
CHK include/generated/compile.h
UPD include/generated/compile.h
CC init/version.o
AR init/built-in.a
LD vmlinux.o
MODPOST vmlinux.o
WARNING: vmlinux.o(.text+0x227cc0): Section mismatch in reference from the function gic_smp_init() to the function .init.text:set_smp_cross_call()
The function gic_smp_init() references
the function __init set_smp_cross_call().
This is often because gic_smp_init lacks a __init
annotation or the annotation of set_smp_cross_call is wrong.
MODINFO modules.builtin.modinfo
LD .tmp_vmlinux1
KSYM .tmp_kallsyms1.o
LD .tmp_vmlinux2
KSYM .tmp_kallsyms2.o
LD vmlinux
SORTEX vmlinux
SYSMAP System.map
OBJCOPY arch/arm64/boot/Image
函数调用链是这样的:gic_init_bases -> gic_smp_init() -> set_smp_cross_call
(目前是CONFIG_SMP=y
)。该消息似乎说 set_smp_cross_call
是用 __init
注释的(意思是它放在 .init.text
中),但 gic_smp_init
不是。
没有#pragma
调试设置,就没有这种警告。我不确定是否可以将 __init
添加到 gic_smp_init()
(或从 set_smp_cross_call
中删除 _init
)。正确的修复方法是什么?
如果我将 __init
添加到 gic_smp_init
,此警告就会消失,但我认为这会使该函数在初始化后被删除(也许可以吗?)。
TL;DR: gic_smp_init()
应该用 __init
注释。
更新:这已在内核 v5.8 中修复,这里是 relevant commit.
在我看来您发现了一个错误:gic_smp_init()
仅由 __init gic_init_bases()
调用,因此没有真正的理由不使用 __init
进行注释。函数 set_smp_cross_call()
被注释为 __init
,因此调用者也应被注释为 __init
.
如果您使用优化(没有 pragma)进行编译,这种不一致就会消失,因为编译器只是将 gic_smp_init()
的整个主体内联到 gic_init_bases()
中:那么调用链就是 gic_init_bases -> set_smp_cross_call
一切都很好,因为它们都带有注释 __init
。但是,当您禁用优化 (#pragma GCC optimize ("O0")
) 时,编译器不再内联对 gic_smp_init()
的调用,它作为一个实际函数保持独立,并且不一致暴露出来。
模块的作者可能错过了 set_smp_cross_call()
的 __init
注释,或者注释是后来添加的,但没有人注意到它也需要在该驱动程序代码中“传播”。
set_smp_cross_call()
的注释之间似乎也存在不一致:在 C 文件 (/arch/{arm,arm64}/kernel/smp.c
) 中,它被注释为 __init
,而在头文件中 (/arch/{arm,arm64}/include/asm/smp.h
) 它不是。后面的实例也应该被注释为 the doc-comment 对于 __init
宏建议:
* If the function has a prototype somewhere, you can also add
* __init between closing brace of the prototype and semicolon:
*
* extern int initialize_foobar_device(int, int, int) __init;