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

NASM与Clang/LLVM生成不同目标文件:Multiboot内核适配问题

Multiboot内核GNU AS版本无法运行的问题解决

问题背景

基于Multiboot标准编写简易内核时,NASM实现的Multiboot header可正常工作,但改用MacOS上Clang的GNU AS语法重写后出现异常。推测是Multiboot header被放置在8K以外的地址,导致引导加载器无法识别,且全程使用相同的链接脚本。

相关代码与输出

可正常运行的NASM代码

MBALIGN  equ  1 << 0
MEMINFO  equ  1 << 1
FLAGS    equ  MBALIGN | MEMINFO
MAGIC    equ  0x1BADB002
CHECKSUM equ -(MAGIC + FLAGS)
 
section .multiboot_header
header_start:
align 4
    dd MAGIC
    dd FLAGS
    dd CHECKSUM
header_end:

section .text
global start
start:
    mov dword [0xb8000], 0x2f4b2f4f
    hlt

无法正常运行的GNU AS代码

.set MBALIGN,  1 << 0
.set MEMINFO, 1 << 1
.set FLAGS, MBALIGN | MEMINFO
.set MAGIC, 0x1BADB002
.set CHECKSUM, -(MAGIC + FLAGS)
 
.section .multiboot_header
header_start:
.align 4
    .long MAGIC
    .long FLAGS
    .long CHECKSUM
header_end:

.section .text
.global start
start:
    movl $0x2f4b2f4f, (0xb8000)
    hlt

链接脚本

ENTRY(start)

SECTIONS {
    . = 1M;

    .boot : ALIGN(4K)
    {
        /* ensure that the multiboot header is at the beginning */
        *(.multiboot_header)
    }

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

nm工具输出

kernel-nasm.bin:
e4524ffb a CHECKSUM
00000003 a FLAGS
1badb002 a MAGIC
00000001 a MBALIGN
00000002 a MEMINFO
0010000c r header_end
00100000 r header_start
00101000 T start

kernel-gas.bin:
e4524ffb a CHECKSUM
00000003 a FLAGS
1badb002 a MAGIC
00000001 a MBALIGN
00000002 a MEMINFO
0000000c n header_end
00000000 n header_start
00100000 T start

编译命令(Homebrew LLVM 14.0.6)

# NASM版本编译
nasm -felf32 kernel-nasm.asm -o kernel-nasm.o
ld.lld -n -o kernel-nasm.bin -T linker.ld kernel-nasm.o

# GNU AS版本编译
as --target=i386-pc-none-elf kernel-gas.S -o kernel-gas.o
ld.lld -n -o kernel-gas.bin -T linker.ld kernel-gas.o

objdump -x kernel-nasm.bin输出

kernel-nasm.bin:     file format elf32-i386
kernel-nasm.bin
architecture: i386, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x00101000

Program Header:
    LOAD off    0x00001000 vaddr 0x00100000 paddr 0x00100000 align 2**12
         filesz 0x0000000c memsz 0x0000000c flags r--
    LOAD off    0x00002000 vaddr 0x00101000 paddr 0x00101000 align 2**12
         filesz 0x0000000b memsz 0x0000000b flags r-x
   STACK off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**0
         filesz 0x00000000 memsz 0x00000000 flags rw-

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .boot         0000000c  00100000  00100000  00001000  2**12
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .text         0000000b  00101000  00101000  00002000  2**12
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  2 .comment      0000001c  00000000  00000000  0000200b  2**0
                  CONTENTS, READONLY
SYMBOL TABLE:
00000000 l    df *ABS*  00000000 hdr.asm
00000001 l       *ABS*  00000000 MBALIGN
00000002 l       *ABS*  00000000 MEMINFO
00000003 l       *ABS*  00000000 FLAGS
1badb002 l       *ABS*  00000000 MAGIC
e4524ffb l       *ABS*  00000000 CHECKSUM
00100000 l       .boot  00000000 header_start
0010000c l       .boot  00000000 header_end
00101000 g       .text  00000000 start

objdump -x kernel-gas.bin输出

kernel-gas.bin:     file format elf32-i386
kernel-gas.bin
architecture: i386, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x00100000

Program Header:
    LOAD off    0x00001000 vaddr 0x00100000 paddr 0x00100000 align 2**12
         filesz 0x0000000b memsz 0x0000000b flags r-x
   STACK off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**0
         filesz 0x00000000 memsz 0x00000000 flags rw-

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .boot         0000000c  00000000  00000000  00002000  2**12
                  CONTENTS, READONLY
  1 .comment      0000001c  00000000  00000000  0000200c  2**0
                  CONTENTS, READONLY
  2 .text         0000000b  00100000  00100000  00001000  2**12
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
SYMBOL TABLE:
e4524ffb l       *ABS*  00000000 CHECKSUM
00000003 l       *ABS*  00000000 FLAGS
1badb002 l       *ABS*  00000000 MAGIC
00000001 l       *ABS*  00000000 MBALIGN
00000002 l       *ABS*  00000000 MEMINFO
0000000c l       .boot  00000000 header_end
00000000 l       .boot  00000000 header_start
00100000 g       .text  00000000 start

问题分析

对比两个版本的objdump输出可以发现:

  • NASM版本的.boot段被标记为ALLOC和LOAD,VMA(虚拟内存地址)是0x00100000(即1M地址,符合链接脚本的设置),会被加载到内存中。
  • GNU AS版本的.boot段没有ALLOC和LOAD标记,VMA是0x00000000,不会被加载到内存,导致引导加载器无法找到Multiboot header。

原因是GNU AS默认创建的.section没有ALLOC属性,而NASM的section指令默认会添加ALLOC属性,链接器只会处理带有ALLOC属性的段,将其纳入内存布局。

解决方法

修改GNU AS代码中的.multiboot_header段声明,显式添加ALLOC属性:

.set MBALIGN,  1 << 0
.set MEMINFO, 1 << 1
.set FLAGS, MBALIGN | MEMINFO
.set MAGIC, 0x1BADB002
.set CHECKSUM, -(MAGIC + FLAGS)
 
// 添加"a"属性表示ALLOC
.section .multiboot_header,"a"
header_start:
.align 4
    .long MAGIC
    .long FLAGS
    .long CHECKSUM
header_end:

.section .text
.global start
start:
    movl $0x2f4b2f4f, (0xb8000)
    hlt

重新编译后,.boot段会被正确标记为ALLOC和LOAD,VMA设置为0x00100000,引导加载器就能在内存的前8K范围内找到Multiboot header,内核即可正常启动。

内容的提问来源于stack exchange,提问作者Michael M.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 17:10:25