编译可执行文件遇链接错误:boot.asm中main未定义引用
链接错误:undefined reference to `main' 解决方法
问题背景
我有kernel.c和boot.asm两个文件,内容如下:
boot.asm
[bits 32] section .text align 4 dd 0x1BADB002 dd 0x00 dd - (0x1BADB002 + 0x00) global start extern main start: cli mov esp, stack_space jmp main hlt stack_space:
kernel.c
char *vidmem = (char*)0xb8000; //змінна для відео пам'яті void printString(void){ const char *nameOS = "Welcome to CyberDOS"; unsigned int i = 0, j = 0; while (nameOS[j] != '\0'){ vidmem[i] = nameOS[j]; vidmem[i+1] = 0x07; ++j; i += 2; } } void clearScreen(){ unsigned int i = 0, j = 0; while (j < 80 * 25 * 2){//резервуємо 25 рядків по 80 символів vidmem[j] = ' '; vidmem[j+1] = 0x07; j += 2; } j = 0; } void main(void){ clearScreen(); printString(); }
我用以下命令编译链接:
cd C:\Users\Ukraine\AppData\Local\bin\NASM nasm -f elf32 C:\Users\Ukraine\Documents\Test\boot.asm -o C:\Users\Ukraine\Documents\Test\output\boot.o gcc -m32 -c C:\Users\Ukraine\Documents\Test\kernel\kernel.c -o C:\Users\Ukraine\Documents\Test\output\kernel.o cd C:\Users\Ukraine\Documents\Test\output gcc -m32 -o kernel.exe -Wl,-e,start boot.o kernel.o objcopy -O elf32-i386 C:\Users\Ukraine\Documents\Test\output\kernel.exe C:\Users\Ukraine\Documents\Test\output\kernel.elf cd C:/Program Files/qemu/qemu-system-i386 -output output cd C:\Users\Ukraine\Documents\Test pause
执行后出现链接错误:
C:\Users\Ukraine\Documents\Test\output>gcc -m32 -o kernel.exe -Wl,-e,start boot.o kernel.o C:/TDM-GCC-64/bin/../lib/gcc/x86_64-w64-mingw32/10.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: boot.o: in function `start': C:\Users\Ukraine\Documents\Test\boot.asm:(.text+0x13): undefined reference to `main' collect2.exe: error: ld returned 1 exit status
我知道问题和extern语句有关,但找不到具体问题,求解决这个链接错误。
解决方案
问题根源
GCC编译C代码时,会自动给C函数名添加下划线前缀(C语言的名称修饰规则)。你在boot.asm中声明extern main,但实际编译后main函数的符号名是_main,链接器找不到main符号所以报错。
修复方法
有两种可行的修复方式:
方式1:修改汇编文件的符号引用
将boot.asm中的extern main改为extern _main,同时把jmp main改成jmp _main,修改后的代码如下:
[bits 32] section .text align 4 dd 0x1BADB002 dd 0x00 dd - (0x1BADB002 + 0x00) global start extern _main start: cli mov esp, stack_space jmp _main hlt stack_space:
方式2:编译C代码时禁用下划线前缀
编译kernel.c时添加-fno-leading-underscore参数,让编译器不给C函数名加下划线:
gcc -m32 -c -fno-leading-underscore C:\Users\Ukraine\Documents\Test\kernel\kernel.c -o C:\Users\Ukraine\Documents\Test\output\kernel.o
额外优化提示
- 你的汇编代码中
stack_space只是一个标签,没有分配实际栈空间,运行时可能出现栈溢出问题,建议添加栈空间分配:
stack_space: resb 8192 ; 分配8KB栈空间
- 当前命令先生成PE格式的
kernel.exe再转成ELF,其实可以直接用ld生成ELF文件,避免格式转换问题:
ld -m elf_i386 -Ttext 0x100000 -o kernel.elf boot.o kernel.o
(-Ttext 0x100000指定代码段加载地址,符合Multiboot规范的常见地址)
内容的提问来源于stack exchange,提问作者Dmitriy Parkhomenko
相关产品推荐
相关产品推荐

