代码之家  ›  专栏  ›  技术社区  ›  user2138149

x86实模式函数调用未执行

  •  0
  • user2138149  · 技术社区  · 5 年前

    我有一些x86 realmode汇编代码,它的行为与预期不完全一样。我相信这个问题与错误计算的jmp/呼叫偏移有关,但我可能弄错了。

    以下是汇编语言代码:

    [org 0x7c00]
    
    mov ah, 0x0e
    
    mov al, 'h'
    int 0x10
    
    mov al, 'e'
    int 0x10
    
    mov al, 'l'
    int 0x10
    
    mov al, 'l'
    int 0x10
    
    mov al, 'o'
    int 0x10
    
    mov al, '!'
    ;int 0x10
    call print_char
    
    ;loop:
    ;    jmp loop
    
    mov si, mystring
    call print_string
    
    jmp $
    
    
    ; fill to 512 bytes
    times 510 - ($ - $$) db 0
    dw 0xAA55
    
    ; the address is stored in si
    print_string:
        pusha
        ; load character from si
        mov al, [si]
        cmp al, 0x00
        jz print_string_end
        call print_char ; print the char using the print_char function
        inc si ; increment the string printing index si
    print_string_end:
        popa
        ret
    
    ; print function: print a single character
    ; the character is stored in al
    print_char:
        pusha
        mov ah, 0x0e
        int 0x16
        popa            ; don't know what registers int 0x16 modifies
        ret
    
    mystring:
    db "loading operating system",0x00
    

    下面是大会: objdump -D -b binary -m i8086 -M intel bootsector.bin

    bootsector.bin:     file format binary
    
    
    Disassembly of section .data:
    
    00000000 <.data>:
       0:   b4 0e                   mov    ah,0xe
       2:   b0 68                   mov    al,0x68
       4:   cd 10                   int    0x10
       6:   b0 65                   mov    al,0x65
       8:   cd 10                   int    0x10
       a:   b0 6c                   mov    al,0x6c
       c:   cd 10                   int    0x10
       e:   b0 6c                   mov    al,0x6c
      10:   cd 10                   int    0x10
      12:   b0 6f                   mov    al,0x6f
      14:   cd 10                   int    0x10
      16:   b0 21                   mov    al,0x21
      18:   e8 f2 01                call   0x20d
      1b:   be 14 7e                mov    si,0x7e14
      1e:   e8 df 01                call   0x200
      21:   eb fe                   jmp    0x21
        ...
     1fb:   00 00                   add    BYTE PTR [bx+si],al
     1fd:   00 55 aa                add    BYTE PTR [di-0x56],dl
     200:   60                      pusha  
     201:   8a 04                   mov    al,BYTE PTR [si]
     203:   3c 00                   cmp    al,0x0
     205:   74 04                   je     0x20b
     207:   e8 03 00                call   0x20d
     20a:   46                      inc    si
     20b:   61                      popa   
     20c:   c3                      ret    
     20d:   60                      pusha  
     20e:   b4 0e                   mov    ah,0xe
     210:   cd 16                   int    0x16
     212:   61                      popa   
     213:   c3                      ret    
     214:   6c                      ins    BYTE PTR es:[di],dx
     215:   6f                      outs   dx,WORD PTR ds:[si]
     216:   61                      popa   
     217:   64 69 6e 67 20 6f       imul   bp,WORD PTR fs:[bp+0x67],0x6f20
     21d:   70 65                   jo     0x284
     21f:   72 61                   jb     0x282
     221:   74 69                   je     0x28c
     223:   6e                      outs   dx,BYTE PTR ds:[si]
     224:   67 20 73 79             and    BYTE PTR [ebx+0x79],dh
     228:   73 74                   jae    0x29e
     22a:   65 6d                   gs ins WORD PTR es:[di],dx
        ...
    

    文件是用 nasm bootsector.asm -f bin -o bootsector.bin

    在线 1e 这是说明 call 0x200 。除非我误解了,否则这会将当前(指令指针+1)推到堆栈上,并跳到偏移量处执行代码 0x200 .这是记忆中的某个地方,它的起源是 0x7c00 ,因此它似乎是一个不同于函数所在地址的地址 print_char 居住。

    至少我认为这是正在发生的事情,但我可能是完全错误的,因为我是新来的。

    还有——也许我不允许有一个超过512字节的文件作为引导扇区?

    0 回复  |  直到 5 年前
        1
  •  0
  •   rupertreynolds    5 年前

    (这可能会帮助您记住,英特尔汇编代码可以对一个汇编助记符使用多个操作码。因此,有几种不同版本的“调用”需要注意。)

    在偏移量+1e处分解的“调用0x200”编码为e8 df 01,CPU将作为对下一条指令+01df的相对调用执行。

    因为反汇编默认从偏移量+0开始,反汇编为21+1df(=0200)或十进制512。请记住,print_字符串是在512字节的引导扇区之后立即组装的,所以这就是总数。

    如果代码在0000:7c00处加载,则相对调用将转到0000:7e00,这是正确计算的,但正如其他人所说,代码将不在那里,因为BIOS不会加载代码,而BIOS只加载第一个扇区。

    我之前做过一个引导区,我的建议是 a) 很容易耗尽空间,所以使用紧凑的代码 b) 不要假设除了CS:IP之外的任何东西都指向你的代码。如果您依赖DS、ES、SS,您可能会发现它们由不同的BIOS和模拟器进行不同的设置,所以请尝试顶部附近的“mov ax、cs”和“mov DS、ax”等,以确保安全。

    代码使用“mov al,[si]”加载字符串数据。默认情况下,si与ds配对,因此它从ds:[si]加载。因此,您意外的输出可能是因为ds:[si]指向错误的数据。如果你设置了Bochs,你就能找到答案。