Question

I'm debugging some assembly, but when I step over (si) a division (div), the current instruction does not change.

Specifically I'm writing a bootloader (for grins and giggles), and I'm getting stuck on a div instruction while attempting to convert an LBA address to a CHS one.

Here's a annotated version of my debugging session:

# 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 ?? ()
…

And here's the code:

.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

I'm also using the following linker script:

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);
    }
}

And this to build:

#!/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

What's going on here?

Était-ce utile?

La solution

Instruction div %bx divides 32-bit value DX:AX by 16-bit value BX, so you must set DX to zero before dividing.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top