OS开发中链接器报“file format not recognized”错误求助
问题排查与修复
问题背景
开发简易操作系统内核时,链接器先后出现两个错误:
- 初始使用
-f obj编译汇编文件时,报错file format not recognized - 改为
-f elf后,报错undefined reference to main
代码文件
Makefile
BUILD?=build/ ASM?=nasm ASMFLAGS?=-f obj CC=i386-elf-gcc LD=i386-elf-ld CCFLAGS=-m32 -std=c11 -O2 -g -Wall -Wextra -Wpedantic -Wstrict-aliasing CCFLAGS+=-Wno-pointer-arith -Wno-unused-parameter CCFLAGS+=-nostdlib -nostdinc -ffreestanding -fno-pie -fno-stack-protector CCFLAGS+=-fno-builtin-function -fno-builtin LDFLAGS= SOURCES_C=$(wildcard *.c) SOURCES_ASM=$(wildcard *.asm) OBJECTS_C=$(patsubst %.c,$(BUILD)/kernel/c/%.obj,$(SOURCES_C)) OBJECTS_ASM=$(patsubst %.asm,$(BUILD)/kernel/asm/%.obj,$(SOURCES_ASM)) all: kernel.bin kernel.bin: $(OBJECTS_ASM) $(OBJECTS_C) $(LD) -o $(BUILD)/kernel.bin $< $(LDFLAGS) -Tlink.ld $(BUILD)/kernel/c/%.obj: %.c always $(CC) $(CCFLAGS) -o $@ $< $(BUILD)/kernel/asm/%.obj: %.asm always $(ASM) $(ASMFLAGS) -o $@ $< always: mkdir -p $(BUILD)/kernel/c mkdir -p $(BUILD)/kernel/asm
汇编代码
[bits 32] [extern main] section .text global _start _start: call main
C代码
void main(){ volatile char* video_memory = (volatile char*)0xb800; *video_memory = 'X'; }
错误原因与修复方案
1. 目标文件格式不匹配
- 原因:
-f obj生成的是DOS/16位格式目标文件,与i386-elf工具链要求的ELF格式不兼容,导致链接器无法识别。 - 修复:将ASMFLAGS改为
-f elf32(明确指定32位ELF格式,匹配32位内核):ASMFLAGS?=-f elf32
2. 链接时遗漏C目标文件
- 原因:链接命令中使用
$<仅传入了第一个依赖文件(汇编目标),未包含C目标文件,导致链接器找不到main的实现。 - 修复:将链接命令中的
$<替换为$^(代表所有依赖文件):kernel.bin: $(OBJECTS_ASM) $(OBJECTS_C) $(LD) -o $(BUILD)/kernel.bin $^ $(LDFLAGS) -Tlink.ld
3. C编译未生成目标文件
- 原因:当前C编译命令缺少
-c参数,gcc会直接尝试生成可执行文件而非纯目标文件,导致生成的.obj文件格式错误。 - 修复:在C编译规则中添加
-c参数:$(BUILD)/kernel/c/%.obj: %.c always $(CC) $(CCFLAGS) -c -o $@ $<
4. C函数名的下划线前缀问题
- 原因:i386-elf-gcc默认会给C函数名添加下划线前缀(如
main会被编译为_main),但汇编代码中引用的是main,导致符号不匹配。 - 修复(二选一):
- 修改汇编代码,引用带下划线的符号:
[extern _main] ... call _main - 或在C编译参数中添加
-fno-leading-underscore,关闭下划线前缀行为:CCFLAGS+=-fno-leading-underscore
- 修改汇编代码,引用带下划线的符号:
内容的提问来源于stack exchange,提问作者neta cohen
相关产品推荐
相关产品推荐

