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

重构OS源码后Makefile出现putstring未定义引用问题求助

问题描述

在小型操作系统开发中,重构源码目录后使用Makefile编译出现putstring函数未定义引用的链接错误。此前所有文件在同一目录时编译正常,当前目录分为boot、libc、libk等模块,kmain.c调用putstring(size_t posx, size_t posy, const char *str)时触发错误。

目录结构

├── boot
│   ├── boot.s
│   └── grub.cfg
├── kmain.c
├── libc
│   ├── include
│   │   └── string.h
│   └── string.c
├── libk
│   ├── font.c
│   ├── include
│   │   ├── font.h
│   │   └── vga.h
│   └── vga.c
├── linker.ld
└── makefile

编译错误输出

mkdir -p ../build/libc
i686-elf-gcc -c libc/string.c -o ../build/libc/string.o -std=gnu99 -ffreestanding -O2 -Wall -Wextra
mkdir -p ../build/libk
i686-elf-gcc -c libk/font.c -o ../build/libk/vga.o -std=gnu99 -ffreestanding -O2 -Wall -Wextra
mkdir -p ../build/boot
i686-elf-as boot/boot.s -o ../build/boot/boot.o
i686-elf-gcc -c kmain.c -o ../build/libk/kmain.o -std=gnu99 -ffreestanding -O2 -Wall -Wextra
i686-elf-gcc -T linker.ld -o ../build/kernel.bin -ffreestanding -O2 -nostdlib \
    ../build/libc/string.o \
    ../build/libk/vga.o \
    ../build/libk/kmain.o \
    ../build/boot/boot.o \
    -lgcc
/home/neon/tools/crc/lib/gcc/i686-elf/12.2.0/../../../../i686-elf/bin/ld: ../build/libk/kmain.o: in function `kmain':
kmain.c:(.text+0x20): undefined reference to `putstring'
collect2: error: ld returned 1 exit status
make: *** [makefile:58: kernel] Fehler 1
问题根源

从编译日志能看到关键错误:i686-elf-gcc -c libk/font.c -o ../build/libk/vga.o —— Makefile错误地将libk/font.c编译成了vga.o,而实际包含putstring实现的libk/vga.c完全没被编译。

问题出在libk的编译规则:

$(LIBK_OBJ_DIR)/%.o: $(wildcard libk/*.c) | $(LIBK_OBJ_DIR)
    $(CC) -c $< -o $@ $(CFLAGS)

这里用$(wildcard libk/*.c)作为依赖,会展开为libk/font.c libk/vga.c,而$<代表第一个依赖文件(即libk/font.c)。当Make尝试生成vga.o时,会用font.c编译,直接跳过vga.c,导致putstring的实现没有被包含到目标文件中。

修复方案

修改Makefile中libc和libk的编译规则,让每个.c文件对应生成同名的.o文件,替换原来的wildcard批量匹配逻辑。

修正后的libc规则

# Rules for libc
.PHONY: libc
libc: $(LIBC_OBJ)

$(LIBC_OBJ_DIR)/%.o: libc/%.c | $(LIBC_OBJ_DIR)
    $(CC) -c $< -o $@ $(CFLAGS)

$(LIBC_OBJ_DIR):
    mkdir -p $(LIBC_OBJ_DIR)

修正后的libk规则

首先更新LIBK_OBJ变量,加入font.o的依赖:

LIBK_OBJ = \
    $(LIBK_OBJ_DIR)/vga.o \
    $(LIBK_OBJ_DIR)/font.o

再修改编译规则:

# Rules for libk
.PHONY: libk
libk: $(LIBK_OBJ)

$(LIBK_OBJ_DIR)/%.o: libk/%.c | $(LIBK_OBJ_DIR)
    $(CC) -c $< -o $@ $(CFLAGS)

$(LIBK_OBJ_DIR):
    mkdir -p $(LIBK_OBJ_DIR)

更通用的优化方案

可以自动收集libk下的所有.c文件并生成对应.o文件,避免手动维护依赖:

# 自动收集libk下的所有.c文件
LIBK_SRC := $(wildcard libk/*.c)
LIBK_OBJ := $(patsubst libk/%.c, $(LIBK_OBJ_DIR)/%.o, $(LIBK_SRC))

.PHONY: libk
libk: $(LIBK_OBJ)

$(LIBK_OBJ_DIR)/%.o: libk/%.c | $(LIBK_OBJ_DIR)
    $(CC) -c $< -o $@ $(CFLAGS)

后续新增.c文件时,无需手动修改Makefile,会自动编译对应的.o文件。

验证修复

重新执行make kernel,编译日志会正确显示:

i686-elf-gcc -c libk/vga.c -o ../build/libk/vga.o ...
i686-elf-gcc -c libk/font.c -o ../build/libk/font.o ...

此时vga.o中包含了putstring的实现,链接时不会再出现未定义引用错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 10:48:22