Key*_*ing 4 x86 assembly real-mode gnu-assembler bootloader
我想写一个bootloader,它只是打印"Hello World!" 在屏幕上,我不知道为什么我的字节混淆了.我正在尝试用AT&T语法编写它(请不要推荐英特尔语法)并尝试将本教程中的代码转换为AT&T语法.
现在这里是我的bootloader的相当短的代码:
start:
.code16 #real mode
.text
.org 0x0
.globl _main
_main:
movw hello, %si
movb $0x0e, %ah
loophere:
lodsb
or %al, %al #is al==0 ?
jz halt #if previous instruction sets zero flag jump to halt
int $0x10 #run bios interrupt 0x10 (ah is set to 0x0e so a character is displayed)
jmp loophere
halt:
cli
hlt
hello: .ascii "Hello world!\0"
filloop:
.fill (510-(.-_main)),1,0 #I hope this works. Fill bootloader with 0's until byte 510
end:
.word 0xaa55
Run Code Online (Sandbox Code Playgroud)
现在,如果我用它编译
$as -o boot.o boot.as
$ld -Ttext 0x07c00 -o boot.elf boot.o
$objcopy -O binary boot.elf boot.bin
Run Code Online (Sandbox Code Playgroud)
以下命令
$objdump -d boot.elf
Run Code Online (Sandbox Code Playgroud)
给了我这个反汇编
Disassembly of section .text:
0000000000007c00 <_main>:
7c00: 8b 36 mov (%rsi),%esi
7c02: 11 7c b4 0e adc %edi,0xe(%rsp,%rsi,4)
0000000000007c06 <loophere>:
7c06: ac lods %ds:(%rsi),%al
7c07: 08 c0 or %al,%al
7c09: 74 04 je 7c0f <halt>
7c0b: cd 10 int $0x10
7c0d: eb f7 jmp 7c06 <loophere>
0000000000007c0f <halt>:
7c0f: fa cli
7c10: f4 hlt
0000000000007c11 <hello>:
7c11: 48 rex.W
7c12: 65 6c gs insb (%dx),%es:(%rdi)
7c14: 6c insb (%dx),%es:(%rdi)
7c15: 6f outsl %ds:(%rsi),(%dx)
7c16: 20 77 6f and %dh,0x6f(%rdi)
7c19: 72 6c jb 7c87 <filloop+0x69>
7c1b: 64 21 00 and %eax,%fs:(%rax)
0000000000007c1e <filloop>:
...
0000000000007dfe <end>:
7dfe: 55 push %rbp
7dff: aa stos %al,%es:(%rdi)
Run Code Online (Sandbox Code Playgroud)
如果我hexdump它(你也可以看到上面的反汇编中的字节)我的前6个字节是
8b 36
11 7c b4 0e
Run Code Online (Sandbox Code Playgroud)
与be 10 7c b4 0e教程相比(hexdump的其余部分与字节完全相同).现在我明白了ac是LODSB(loadstringbyte)的指令,以便b4 0e将不得不加载0e到%ah和be 10 7c必须指出%si的问候标签位于地址7c10(注意小尾数的).我用十六进制编辑器更改了相应的字节,它突然起作用了.虽然拆卸有点混淆了这样:
0000000000007c00 <_main>:
7c00: be 10 7c b4 0e mov $0xeb47c10,%esi
7c05: ac lods %ds:(%rsi),%al
Run Code Online (Sandbox Code Playgroud)
我原来的版本刚打印出一个大写'S'.有人可以帮助我为什么这些第一个指令字节设置不同?
我在Debian 9 64位上对所有这些进行编码,并在qemu-system-x86_64上以软盘运行它.
如果要将指令解码为16位,则需要使用该选项告诉OBJDUMP-Mi8086.由于您使用AS和LD创建了64位对象,因此默认情况下它将解码为64位指令.-M覆盖那个.i8086是16位指令解码.
您的代码中的许多问题都与未正确设置段寄存器(包括DS)有关.我在Bootloader Tips中讨论了很多这些问题.$如果你想要他们的地址(一个立即操作数),在AT&T语法中也需要在标签前面.movw hello, %si应该是movw $hello, %si.或者,您可以使用带内存操作数的LEA并仅计算地址(但不检索数据).在这种情况下,您不使用$标志.leaw hello, %si也应该工作.
使用INT 10h/AH = 0Eh时,应设置BH,即要显示的页码.0是可见页面.
考虑到这一切,这段代码应该有效:
start:
.code16 #real mode
.text
.globl _main
_main:
xor %ax, %ax # We are usin offset 0x7c00, thus we need to se segment to 0x0000
mov %ax, %ds
mov %ax, %es
mov %ax, %ss # Set the stack to grow down just below bootloader
mov $0x7c00, %sp
cld # Ensure forward movement of lods/movs/scas instructions
movw $hello, %si # We want the address of hello, not what it points at
#leaw hello, %si # Alternative way to get address with LEA instruction.
movb $0x0e, %ah
xor %bh, %bh # Make sure video page number is set (we want 0)
loophere:
lodsb
or %al, %al #is al==0 ?
jz halt #if previous instruction sets zero flag jump to halt
int $0x10 #run bios interrupt 0x10 (ah is set to 0x0e so a character is displayed)
jmp loophere
halt:
cli
hlt
hello: .ascii "Hello world!\0"
filloop:
.fill (510-(.-_main)),1,0 #I hope this works. Fill bootloader with 0's until byte 510
end:
.word 0xaa55
Run Code Online (Sandbox Code Playgroud)