使用 makefile 编译 x86 和 c

Compiling x86 and c with makefile

我已经写了 2-3 年的 C,最近开始学习汇编,但是当我使用 Windows 之前我从来不需要使用 make 文件,因为我刚刚使用 Visual Studio.我正在尝试使用 Cygwin 和 i686 交叉编译器来编译 c 和汇编文件,然后 link 将它们一起编译成代表我的操作系统的二进制文件。我是制作文件的新手,所以我不知道如何正确执行此操作。这是我到目前为止为 Makefile:

CC = i686-elf-gcc
CC_FLAGS = -c -std=gnu99 -ffreestanding -O2 -Wall -Wextra -I./include
AS = i686-elf-as
LD = i686-elf-gcc -T linker.ld -o myos.bin
LD_FLAGS = -ffreestanding -O2 -nostdlib -lgcc
O_FILES = $(wildcard src/*.o)

all: $(O_FILES)
    $(LD) $(LD_FLAGS) $(O_FILES)

src/%.o: src/%.c
    $(CC) $(CC_FLAGS) -o $@ $<

src/%.o: src/%.asm
    $(AS) -o $@ $<

我收到一条错误消息,指出 link 用户找不到输入符号 _start,所以显然没有任何编译。我该如何解决这个问题?

我的 src/linker.ld 文件将 _start 定义为入口点是:

ENTRY(_start)

SECTIONS
{
        . = 1M;

        .text BLOCK(4K) : ALIGN(4K)
        {
                *(.multiboot)
                *(.text)
        }

        .rodata BLOCK(4K) : ALIGN(4K)
        {
                *(.rodata)
        }

        .data BLOCK(4K) : ALIGN(4K)
        {
                *(.data)
        }

        .bss BLOCK(4K) : ALIGN(4K)
        {
                *(COMMON)
                *(.bss)
        }
}

我正在使用的定义我的 _start 标签的 src/boot.asm 文件是:

# Declare constants for the multiboot header.
.set ALIGN,    1<<0             # align loaded modules on page boundaries
.set MEMINFO,  1<<1             # provide memory map
.set FLAGS,    ALIGN | MEMINFO  # this is the Multiboot 'flag' field
.set MAGIC,    0x1BADB002       # 'magic number' lets bootloader find the header
.set CHECKSUM, -(MAGIC + FLAGS) # checksum of above, to prove we are multiboot

.section .multiboot
.align 4
.long MAGIC
.long FLAGS
.long CHECKSUM

.section .bss
.align 16
stack_bottom:
.skip 16384 # 16 KiB
stack_top:

.section .text
.global _start
.type _start, @function
_start:
        mov $stack_top, %esp

        call kernel_main

        cli
1:      hlt
        jmp 1b

.size _start, . - _start

您需要将 Makefile 修改为如下内容:

CC = i686-elf-gcc
AS = i686-elf-as
LD = i686-elf-gcc
AS_FLAGS =
LD_FLAGS = -ffreestanding -nostdlib -lgcc -Tlinker.ld
CC_FLAGS = -c -std=gnu99 -ffreestanding -O2 -Wall -Wextra -I./include

C_FILES := $(wildcard src/*.c)
ASM_FILES := $(wildcard src/*.asm)
O_FILES := $(C_FILES:.c=.o) $(ASM_FILES:.asm=.o)
KERNEL_BIN := myos.bin

all: $(KERNEL_BIN)

clean:
        rm -f $(KERNEL_BIN) $(O_FILES)

$(KERNEL_BIN): $(O_FILES)
        $(LD) $(LD_FLAGS) -o $@ $^

%.o: %.c
        $(CC) $(CC_FLAGS) -o $@ $<

%.o: %.asm
        $(AS) $(AS_FLAGS) -o $@ $<

这个Makefile不同的是我们构造了一个ASM文件列表和C文件。我还稍微清理了 LD_FLAGS 并添加了一个额外的规则来创建 myos.binclean 对象和 bin 文件。

在您当前的代码中,扩展后的 Makefile 变量中包含以下内容:

C_FILES   = src/string.c src/tty.c src/kernel.c
ASM_FILES = src/boot.asm
O_FILES   = src/string.o src/tty.o src/kernel.o src/boot.o

O_FILES 源自将两个文件列表连接在一起并将 .c.asm 扩展名替换为 .o。这是需要从其源文件生成的所有对象的列表。

使用 GNU 汇编程序 通常使用具有 .s 扩展名的文件(或者 .S 如果您想使用 C 预处理器)而不是 .asm


未找到 _start 标签的原因是程序集文件未被处理。这意味着 boot.asm 没有变成 boot.o,因此根本没有被链接。