NASM org指令与链接脚本的等效性及二进制差异问题
org 0x7C00 与链接脚本实现MBR的差异及问题解决 org 0x7C00配合nasm -f bin和用链接脚本替代的方式并非完全等效,后者需要严格的配置才能达到前者的效果,你遇到的问题都是因为链接脚本的配置不符合MBR的要求。
1. org 0x7C00的本质作用
org 0x7C00是NASM的伪指令,核心作用有两个:
- 告诉汇编器,程序加载到内存的起始地址是
0x7C00,所有跳转标签、变量等符号的地址计算都会以0x7C00为基址。 - 配合
nasm -f bin时,汇编器直接生成从偏移0开始的纯二进制文件,你可以通过times 510-($-$$) db 0+dw 0xAA55精准控制文件大小为512字节,确保MBR魔数落在最后两个字节。
2. 链接脚本方式的核心要求
如果不用org,想用链接脚本实现同样的MBR效果,必须满足三个关键条件:
(1)指定输出格式为纯二进制
链接器默认生成ELF格式文件(包含段表、符号表等额外元数据),这就是你得到4040字节文件的原因——ELF文件远大于512字节,且BIOS无法识别。必须在链接脚本开头添加:
OUTPUT_FORMAT("binary")
(2)设置代码段起始地址为0x7C00
链接脚本需要明确告诉链接器,代码加载到内存的起始地址是0x7C00,对应二进制文件的偏移0:
ENTRY(start) SECTIONS { . = 0x7C00; /* 设置当前地址指针为0x7C00 */ .text : { *(.text) /* 包含所有目标文件的.text段内容 */ } }
(3)强制控制文件大小为512字节,并确保魔数位置正确
MBR要求文件大小必须是512字节,且最后两个字节为0xAA55。你需要在链接脚本中截断超出部分,并把魔数放在正确位置:
OUTPUT_FORMAT("binary") ENTRY(start) SECTIONS { . = 0x7C00; .text : { *(.text) /* 直接在链接脚本中添加魔数,确保落在最后两个字节 */ BYTE(0x55) BYTE(0xAA) } /* 将地址指针移到0x7C00+512,截断后续内容 */ . = 0x7C00 + 0x200; /* 丢弃不需要的段(比如数据段、bss段) */ /DISCARD/ : { *(.data) *(.bss) *(.comment) } }
3. 你遇到的问题根源
- 二进制大小4040字节:链接器默认生成了ELF格式文件,而非纯二进制,包含大量额外元数据。
- rizin显示起始地址差异:前者是纯二进制文件,rizin默认从0x0开始解析;后者如果是ELF文件,rizin会读取ELF头中的加载地址,显示为
0x7C00。 - 无法运行:BIOS只会读取磁盘前512字节作为MBR,而你的ELF文件中
0xAA55魔数不在前512字节的最后两位,BIOS无法识别为有效的MBR。
4. 正确的链接脚本使用流程
- 编写汇编代码(无需
org 0x7C00):
global start start: ; 你的bootloader核心代码 jmp $ ; 示例:无限循环
- 汇编为32位ELF目标文件:
nasm -f elf32 boot.s -o boot.o
- 用链接脚本生成MBR二进制:
ld -m elf_i386 -T link.ld boot.o -o boot.bin
此时生成的boot.bin应该是512字节,最后两位为0xAA55,可以正常被BIOS加载。
总结
org 0x7C00+nasm -f bin是编写MBR最直接、不易出错的方式,适合快速开发。- 链接脚本方式更灵活(比如支持多文件编译),但需要严格配置输出格式、地址布局和文件大小,才能达到和前者等效的效果。
内容的提问来源于stack exchange,提问作者trxgnyp1
相关产品推荐
相关产品推荐

