为何.data段的HELLO_WORLD字符串无法在引导加载程序中加载?
引导加载程序中.data段字符串无法加载的原因分析
在编写16位实模式引导加载程序时,尝试用section组织代码,发现将HELLO_WORLD字符串放在.data段时,自定义printf函数无法加载该字符串,程序无输出;但把字符串移到.text段的printf函数下方时,程序能正常输出Hello World!,具体代码和运行结果如下:
第一段代码(.data段存放字符串)
; Set Code to run at 0x7c00 org 0x7c00 ; Put into real mode bits 16 ; Variables without values section .bss ; Our constant values section .data HELLO_WORLD: db 'Hello World!', 0 ; Where our code runs section .text _start: mov si, HELLO_WORLD ; Moves address for string into si register call printf ; Calls printf function jmp $ ; Jump forever printf: lodsb ; Load the next character cmp al, 0 ; Compares al to 0 je _printf_done ; If they are equal... call print_char ; Call Print Char jmp printf ; Jump to the loop _printf_done: ret ; Return print_char: mov ah, 0x0e ; tty mode int 0x10 ; Video interrupt ret ; Return ; Fills the rest of the data with 0 times 510-($-$$) db 0 ; BIOS boot magic number dw 0xaa55
运行结果
Booting into hard drive...
第二段代码(.text段存放字符串)
; Set Code to run at 0x7c00 org 0x7c00 ; Put into real mode bits 16 ; Variables without values section .bss ; Our constant values section .data ; Where our code runs section .text _start: mov si, HELLO_WORLD ; Moves address for string into si register call printf ; Calls printf function jmp $ ; Jump forever printf: lodsb ; Loads next character cmp al, 0 ; Compares al to 0 je _printf_done ; If they are equal... call print_char ; Call Print Char jmp printf ; Jump to the loop _printf_done: ret ; Return print_char: mov ah, 0x0e ; tty mode int 0x10 ; Video interrupt ret ; Return HELLO_WORLD: db 'Hello World!', 0 ; Fills the rest of the data with 0 times 510-($-$$) db 0 ; BIOS boot magic number dw 0xaa55
运行结果
Booting into hard drive... Hello World!
问题原因
核心原因是引导扇区的加载范围限制:
- BIOS只会从磁盘加载前512字节到内存的
0x7c00位置,这512字节必须包含完整的引导代码和结束标志0xaa55。 - 汇编器默认按
.text→.data→.bss的顺序排列段内容。第一段代码中,.data段在.text段之后,而.text段末尾的times 510-($-$$) db 0指令会填充当前.text段到510字节,再加上0xaa55刚好占满512字节,导致.data段的字符串被放到512字节之外,BIOS根本没有加载这部分内容到内存中。 - 当程序执行
mov si, HELLO_WORLD时,SI寄存器指向的是内存中不存在的地址,lodsb读取不到预期字符,自然无法输出内容。 - 第二段代码中,字符串放在
.text段内部且位于times指令之前,属于512字节的引导扇区范围内,会被BIOS正常加载到内存,因此程序能正确读取并输出字符串。
补充说明:$代表当前汇编地址,$$代表当前段的起始地址,times 510-($-$$)的作用是填充字节,让当前.text段的总长度达到510字节,确保最后的0xaa55刚好在第511和512字节位置,符合BIOS引导扇区的要求。
内容的提问来源于stack exchange,提问作者ThatPrimitive_Remastered
相关产品推荐
相关产品推荐

