You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

从汇编调用C++函数时出现kmain未定义引用错误的问题咨询

为什么汇编调用C++函数会出现未定义引用,以及解决方法

问题根源:C++的名字修饰(Name Mangling)

你遇到的问题核心在于C++编译器会对函数名进行「名字修饰」,而C语言不会。

C为了支持函数重载、命名空间等特性,编译器会把函数的参数类型、数量等信息编码到最终的符号名里。比如你的kmain(void),经过GCC编译后,实际的符号名会变成类似_Z5kmainv这样的形式。但你的汇编代码里直接用call kmain,链接器会去寻找名为kmain的符号,自然找不到被修饰过的C函数符号,所以抛出「undefined reference to kmain」的错误。

而当你把文件改成.c后缀时,GCC会以C语言规则编译,不会对kmain进行名字修饰,符号名就是kmain,和汇编里的调用完全匹配,所以链接成功。


解决方法:让C++函数以C语言的符号规则暴露

最通用、可移植的解决方法是用extern "C"修饰你的C++函数,告诉编译器按照C语言的规则处理这个函数的符号名,不进行名字修饰。

修改你的cppfoo.cpp文件如下:

extern "C" void kmain(void) { }

这样编译后,kmain的符号名就会保持原样,和汇编代码里的调用完全匹配,再执行你原来的编译链接命令:

nasm -f elf32 asmfoo.asm -o asmfoo.o
gcc -m32 -c cppfoo.cpp -o cppfoo.o
ld -m elf_i386 -T ./linker.ld -o foo-output asmfoo.o cppfoo.o

就能正常链接,不会再出现错误。


不推荐的替代方法:直接调用修饰后的符号

如果你非要在汇编里直接调用C++修饰后的符号,也可以——但不推荐,因为不同编译器(比如GCC和MSVC)的名字修饰规则不一样,代码会失去移植性。

比如GCC对kmain(void)的修饰名是_Z5kmainv,你可以把汇编代码里的call kmain改成:

call _Z5kmainv

但这种方法只适用于特定编译器,一旦更换工具链就会失效,所以优先用extern "C"的方案。

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

火山引擎 最新活动