我了解引导扇区编程。我是按照这篇文章做的。一切顺利,直到对输出“Hello, World!”进行编程为止。在C中。这是文章中给出的代码(实际上,我只是复制它):
__asm__(".code16\n");
__asm__("jmpl $0x0000, $main\n");
void printString(const char* pStr) {
while(*pStr) {
__asm__ __volatile__ (
"int $0x10" : : "a"(0x0e00 | *pStr), "b"(0x0007)
);
++pStr;
}
}
void main() {
printString("Hello, World");
}
我又按照那篇文章编译、链接等:
gcc -c -g -Os -m32 -ffreestanding -Wall -Werror test.c -o test.o
ld -melf_i386 -static -Ttest.ld -nostdlib --nmagic -o test.elf test.o
objcopy -O binary test.elf test.bin
dd if=/dev/zero of=floppy.img bs=512 count=2880
dd if=test.bin of=floppy.img
为了以防万一,我给出test.ld的内容:
ENTRY(main);
SECTIONS
{
. = 0x7C00;
.text : AT(0x7C00)
{
*(.text);
}
.sig : AT(0x7DFE)
{
SHORT(0xaa55);
}
}
我运行 bochs,但我没有打印“Hello, World”,而是只得到一个“S”字符。
将函数移至 main:
__asm__(".code16\n");
__asm__("jmpl $0x0000, $main\n");
void main()
{
char s[] = "Hello, World";
char *str = s;
while (*str)
{
__asm__ __volatile__ (
"int $0x10" : : "a"(0x0e00 | *str), "b"(0x0007)
);
++str;
}
}
之后,他原则上不再展示任何东西。-Os 优化标志的异常有所帮助。结论是正确的。经过一些试验,我意识到这种优化“杀死”了循环。
然而,相同的代码,但移至一个函数,仍然输出“S”字符。
我寻求帮助解决这个问题。并且,如果可能的话,解释为什么优化标志“杀死”循环。谢谢!
更新。
来自 test.bin 的十六进制:
66 EA 3A 7C 00 00 00 00 66 55 66 89 E5 EB 19 67 66 8B 45 08 67 66 0F B6 00 66 0F BE C0 80 CC 0E CD 10 67 66 83 45 08 01 67 66 8B 45 08 67 66 0F B6 00 84 C0 75 D9 90 66 5D C3 66 55 66 89 E5 66 68 4F 7C 00 00 E8 C0 FF 66 83 C4 04 90 C9 C3 48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 00 14 00 00 00 00 00 00 00 01 7A 52 00 01 7C 08 01 1B 0C 04 04 88 01 00 00 1C 00 00 00 1C 00 00 00 8C FF FF FF 32 00 00 00 00 42 0E 08 85 02 43 0D 05 6C C5 0C 04 04 00 00 1C 00 00 00 3C 00 00 00 9E FF FF FF 15 00 00 00 00 42 0E 08 85 02 43 0D 05 4F C5 0C 04 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA
该文件是从以下来源获得的:
__asm__(".code16\n");
__asm__("jmpl $0x0000, $main\n");
void printString(const char* str)
{
while (*str)
{
__asm__ __volatile__ (
"int $0x10" : : "a"(0x0e00 | *str));
++str;
}
}
void main()
{
printString("Hello, World");
}
更新。
test.elf文件反汇编结果:
% objdump -d test.elf
test.elf: file format elf32-i386
Disassembly of section .text:
00007c00 <printString-0x8>:
7c00: 66 ea 3a 7c 00 00 ljmpw $0x0,$0x7c3a
...
00007c08 <printString>:
7c08: 66 55 push %bp
7c0a: 66 89 e5 mov %sp,%bp
7c0d: eb 19 jmp 7c28 <printString+0x20>
7c0f: 67 66 8b 45 08 mov 0x8(%di),%ax
7c14: 67 66 0f b6 00 movzbw (%bx,%si),%ax
7c19: 66 0f be c0 movsbw %al,%ax
7c1d: 80 cc 0e or $0xe,%ah
7c20: cd 10 int $0x10
7c22: 67 66 83 45 08 01 addw $0x1,0x8(%di)
7c28: 67 66 8b 45 08 mov 0x8(%di),%ax
7c2d: 67 66 0f b6 00 movzbw (%bx,%si),%ax
7c32: 84 c0 test %al,%al
7c34: 75 d9 jne 7c0f <printString+0x7>
7c36: 90 nop
7c37: 66 5d pop %bp
7c39: c3 ret
00007c3a <main>:
7c3a: 66 55 push %bp
7c3c: 66 89 e5 mov %sp,%bp
7c3f: 66 68 4f 7c pushw $0x7c4f
7c43: 00 00 add %al,(%eax)
7c45: e8 c0 ff 66 83 call 83677c0a <main+0x8366ffd0>
7c4a: c4 04 90 les (%eax,%edx,4),%eax
7c4d: c9 leave
7c4e: c3 ret
更新。
这是处理器看到程序时对 hiew 的反汇编:
7c00: 66EA3A7C00000000 jmpf 00000:07C3A
7c08: 6655 3push ebp
7c0A: 6689E5 mov ebp,esp
7c0D: EB19 jmps 000000028 --↓1
7c0F: 67668B4508 2mov eax,[ebp][8]
7c14: 67660FB600 movzx eax,b,[eax]
7c19: 660FBEC0 movsx eax,al
7c1D: 80CC0E or ah,00E
7c20: CD10 int 010
7c22: 676683450801 add d,[ebp][8],1
7c28: 67668B4508 1mov eax,[ebp][8]
7c2D: 67660FB600 movzx eax,b,[eax]
7c32: 84C0 test al,al
7c34: 75D9 jnz 00000000F --↑2
7c36: 90 nop
7c37: 665D pop ebp
7c39: C3 retn ;
7c3A: 6655 push ebp
7c3C: 6689E5 mov ebp,esp
7c3F: 66684F7C0000 push 000007C4F ;' |O'
7c45: E8C0FF call 000000008 --↑3
7c48: 6683C404 add esp,4
7c4C: 90 nop
7c4C: 90 nop
7c4D: C9 leave
7c4E: C3 retn ;
7c4F: 48 65 6C 6C 6F-2C 20 57 6F-72 6C 64 Hellow, World
随机地,我注意到指针确实需要移动。对于引导区,需要将代码放在磁盘的第一个扇区,偏移量为0x7C00字节。我在 test.ld 文件中指定它。我试图展示我所拥有的一切。在一堆不同的符号中,潜伏着想要的“你好,世界”。同时,更改代码需要更改偏移量,但我几乎可以肯定它总是大于 0x7C00。仍然需要了解如何正确跟踪此偏移量......
在编写引导扇区的情况下,请使用反汇编程序检查您得到了什么。对于 Linux 我不知道如何,对于 Windows hiew.exe 是一个很好的反汇编程序。不要忘记在开始时 - 处理器处于 16 位指令模式和 16 位寻址模式 - 这很重要 1) 在构建二进制文件时 2) 了解发生了什么 3) 在反汇编时。
对该案例的分析表明,代码被编译器或链接器“错误地”编译。代码 66 - 切换到 32 位指令模式,代码 67 - 切换到 32 位寻址
这些过渡应该紧张。您可以使用它们 (66),但要明智。最好完全避免 32 位寻址 (67),直到切换到保护模式,但主加载程序不会。作为结论 - 如果代码不起作用,虽然它被检查,并且图片没有加起来,那么问题出在构建选项上。
具体来说,在这种情况下,它有助于将指令
code16(它在新版本的 gcc 中消失)更改为code16gcc. 指定code16或code16gcc取决于 GCC 的版本。