为什么我不能跨过这个div指令?

时间:2014-01-19 01:33:45

标签: assembly x86 bootloader

我正在调试一些程序集,但当我跳过(si)一个除法(div)时,当前指令不会改变。

具体来说,我正在编写一个引导加载程序(对于笑话和咯咯笑声),并且在尝试将LBA地址转换为CHS地址时,我遇到了div指令。

这是我的调试会话的注释版本:

# Attach to QEmu instance:
(gdb) target remote localhost:1234
Remote debugging using localhost:1234
0x0000fff0 in ?? ()

# Tell gdb we're debugging 16-bit x86.
(gdb) set architecture i8086
warning: A handler for the OS ABI "GNU/Linux" is not built into this configuration
of GDB.  Attempting to continue with the default i8086 settings.

The target architecture is assumed to be i8086

# Break at the start of the booloader.
(gdb) b *0x7c00
Breakpoint 1 at 0x7c00
# (and go there now)
(gdb) c
Continuing.

# Start of bootloader.
Breakpoint 1, 0x00007c00 in ?? ()
(gdb) disassemble 0x7c00,+50
Dump of assembler code from 0x7c00 to 0x7c32:
=> 0x00007c00:  ljmp   $0x0,$0x7c05
   0x00007c05:  xor    %ax,%ax
   0x00007c07:  mov    %ax,%ds
   0x00007c09:  mov    %ax,%es
   0x00007c0b:  mov    %ax,%ss
   0x00007c0d:  mov    $0x800,%sp
   0x00007c10:  sti    
   0x00007c11:  mov    $0x7ce0,%bx
   0x00007c14:  call   0x7c28
   0x00007c17:  call   0x7c3a
   0x00007c1a:  call   0x7c1f   # <= stop here, at the call to read_stage_2
   0x00007c1d:  cli    
   0x00007c1e:  hlt    
   0x00007c1f:  xor    %ax,%ax
   0x00007c21:  mov    $0x1,%ax
   0x00007c24:  call   0x7c56
   0x00007c27:  ret    
   0x00007c28:  mov    (%bx),%al
   0x00007c2a:  cmp    $0x0,%al
   0x00007c2c:  je     0x7c39
   0x00007c2e:  push   %bx
   0x00007c2f:  xor    %bx,%bx
   0x00007c31:  mov    $0xe,%ah
End of assembler dump.

# Stop at `call read_stage_2`; go there
(gdb) b *0x7c1a
Breakpoint 2 at 0x7c1a
(gdb) c
Continuing.

Breakpoint 2, 0x00007c1a in ?? ()

# Step into the call.
(gdb) si
0x00007c1f in ?? ()
# Show the disassembly of read_stage_2:
(gdb) disassemble 0x7c1f,+10
Dump of assembler code from 0x7c1f to 0x7c29:
=> 0x00007c1f:  xor    %ax,%ax
   0x00007c21:  mov    $0x1,%ax
   0x00007c24:  call   0x7c56
   0x00007c27:  ret    
   0x00007c28:  mov    (%bx),%al
End of assembler dump.
(gdb) si  # si over xor.
0x00007c21 in ?? ()
(gdb) si  # si over mov.
0x00007c24 in ?? ()
# Double-check that I'm at the call.
(gdb) disassemble 0x7c1f,+10
Dump of assembler code from 0x7c1f to 0x7c29:
   0x00007c1f:  xor    %ax,%ax
   0x00007c21:  mov    $0x1,%ax
=> 0x00007c24:  call   0x7c56
   0x00007c27:  ret    
   0x00007c28:  mov    (%bx),%al
End of assembler dump.
# Yup; step into lba_to_chs
(gdb) si
0x00007c56 in ?? ()

# Show me the source of lba_to_chs
(gdb) disassemble 0x7c56,+30
Dump of assembler code from 0x7c56 to 0x7c74:
=> 0x00007c56:  xor    %bx,%bx
   0x00007c58:  mov    0x803,%bl
   0x00007c5c:  div    %bx
   0x00007c5e:  inc    %ax
   0x00007c5f:  push   %ax
   0x00007c60:  xor    %bx,%bx
   0x00007c62:  mov    0x802,%bl
   0x00007c66:  div    %bx
   0x00007c68:  mov    %dx,%bx
   0x00007c6a:  mov    %al,%dh
   0x00007c6c:  pop    %ax
   0x00007c6d:  ret    
   0x00007c6e:  mov    %bl,%ch
   0x00007c70:  shr    $0x2,%bx
   0x00007c73:  mov    %bl,%cl
End of assembler dump.
(gdb) si  # si over xor
0x00007c58 in ?? ()
(gdb) si  # si over mov
0x00007c5c in ?? ()

# We should be at the div, indeed, we are.
(gdb) disassemble 0x7c56,+30
Dump of assembler code from 0x7c56 to 0x7c74:
   0x00007c56:  xor    %bx,%bx
   0x00007c58:  mov    0x803,%bl
=> 0x00007c5c:  div    %bx
   0x00007c5e:  inc    %ax
   0x00007c5f:  push   %ax
   0x00007c60:  xor    %bx,%bx
   0x00007c62:  mov    0x802,%bl
   0x00007c66:  div    %bx
   0x00007c68:  mov    %dx,%bx
   0x00007c6a:  mov    %al,%dh
   0x00007c6c:  pop    %ax
   0x00007c6d:  ret    
   0x00007c6e:  mov    %bl,%ch
   0x00007c70:  shr    $0x2,%bx
   0x00007c73:  mov    %bl,%cl
End of assembler dump.
# Show me the registers. We should be dividing LBA 1 by the total number of
# sectors. (%ax / %bx). Here %ax is 1 (correct), and %bx is 63 (likely
# correct).
(gdb) info registers 
eax            0x1  1
ecx            0x3f 63
edx            0x3f01   16129
ebx            0x3f 63
esp            0x7fc    0x7fc
ebp            0x0  0x0
esi            0x0  0
edi            0x0  0
eip            0x7c5c   0x7c5c
eflags         0x242    [ ZF IF ]
cs             0x0  0
ss             0x0  0
ds             0x0  0
es             0x0  0
fs             0x0  0
gs             0x0  0
# Step over the div.
(gdb) si
0x00007c5c in ?? ()

# Why are we still at the div instruction?
(gdb) disassemble 0x7c56,+30
Dump of assembler code from 0x7c56 to 0x7c74:
   0x00007c56:  xor    %bx,%bx
   0x00007c58:  mov    0x803,%bl
=> 0x00007c5c:  div    %bx
   0x00007c5e:  inc    %ax
   0x00007c5f:  push   %ax
   0x00007c60:  xor    %bx,%bx
   0x00007c62:  mov    0x802,%bl
   0x00007c66:  div    %bx
   0x00007c68:  mov    %dx,%bx
   0x00007c6a:  mov    %al,%dh
   0x00007c6c:  pop    %ax
   0x00007c6d:  ret    
   0x00007c6e:  mov    %bl,%ch
   0x00007c70:  shr    $0x2,%bx
   0x00007c73:  mov    %bl,%cl
End of assembler dump.

# IP refuses to advance?
(gdb) si
0x00007c5c in ?? ()
(gdb) si
0x00007c5c in ?? ()
# Frustration ensues…
(gdb) si
0x00007c5c in ?? ()
(gdb) si
0x00007c5c in ?? ()
(gdb) si
0x00007c5c in ?? ()
…

这是代码:

.code16

.global entry


# Memory:
# 0x500 - 0x800 stack
# 0x800: drive geometry
#   2 bytes: cylinders in drive
#   1 byte: max head number
#   1 byte: max sector number

.section .bss

drive_cylinders:
    .space 2
drive_heads:
    .space 1
drive_sectors:
    .space 1


.section .text


entry:
    # Jump incase we're not a 0000:7c00
    ljmp $0x0,$start

start:
    # Set es, ss to 0x0000
    xor %ax, %ax
    mov %ax, %ds
    mov %ax, %es
    mov %ax, %ss
    # Put the stack at 0x500 - 0x800 (256 * 3 bytes)
    mov $0x800, %sp

    sti

    mov $msg_greeting, %bx
    call print

    call get_disk_geometry
    call read_stage_2       # <-- The third call in the above disassembly.

    cli
    hlt


read_stage_2:
    xor %ax, %ax
    mov $0x01, %ax
    call lba_to_chs
    ret


print:
« omitted »
    ret


get_disk_geometry:
    xor %ax, %ax
    mov %ax, %es
    mov %ax, %di
    mov $0x08, %ah
    int $0x13
    jc _io_error
    inc %dh
    mov %dh, (drive_heads)
    mov %cl, %dh
    and $0x3f, %dh
    mov %dh, (drive_sectors)
    ret


lba_to_chs:
    # Stores CHS in:
    # bx = cylinder
    # dh = head
    # ax = sector
    # LBA in ax.
    xor %bx, %bx
    mov (drive_sectors), %bl
    div %bx   # <--- The problematic div.
    inc %ax
    push %ax
    xor %bx, %bx
    mov (drive_heads), %bl
    div %bx
    mov %dx, %bx
    mov %al, %dh
    pop %ax
    ret

我也在使用以下链接器脚本:

ENTRY(entry);
SECTIONS
{
    . = 0x7C00;
    .text : AT(0x7C00)
    {
        _text = .;
        *(.text);
        _text_end = .;
    }
    .data :
    {
        _data = .;
        *(.data);
        *(.rodata*);
        *(COMMON)
        _data_end = .;
    }
    .sig : AT(0x7DFE)
    {
        SHORT(0xaa55);
    }
    . = 0x800;
    .bss : AT(0x800)
    {
        *(.bss);
        *(.bss*);
    }
    /DISCARD/ :
    {
        *(.note*);
        *(.iplt*);
        *(.igot*);
        *(.rel*);
        *(.comment);
    }
}

这要建立:

#!/bin/bash

set -e

as bootsector.S -o bootsector.o
ld -static -Tbootsector.ld -nostdlib --nmagic -o bootsector.elf bootsector.o
objcopy -O binary -R .bss bootsector.elf bootsector.bin

这里发生了什么?

1 个答案:

答案 0 :(得分:1)

指令div %bx将32位值DX:AX除以16位值BX,因此必须在划分前将DX设置为零。