调用 0x16 时出现段错误

Segmentation fault when call 0x16

这是我的 nasm 代码的一部分:

extern printf

%macro print 2
        mov  rdi, %1
        mov  rsi, %2
        mov  rax, 0
        call printf
%endmacro

section .data

        msg1:   db 'Nasm', 0
        len1:   equ $ - msg1
        fmts:   db "%s", 10, 0 ; printf format string 
        fmti:   db "%d", 10, 0

section .bss           ;Uninitialized data
   num resb 5

section  .text
        global main    ; declaring for gcc
main:
        push    rbp            ; save rbp

        print   fmts, msg1
        xor     ah, ah
        int     0x16
        print   fmti, [num]

exit:
        leave
        mov     rax,1       ;system call number (sys_exit)
        int     0x80        ;call kernel

输出:

[b@l .K]$ nasm test.asm -f elf64 -o test.o && gcc test.o -o test && ./test
Nasm
Segmentation fault (core dumped)

当我替换:

        print   fmts, msg1
        print   fmti, [num]
        xor     ah, ah
        int     0x16

然后

[b@l .K]$ nasm test.asm -f elf64 -o test.o && gcc test.o -o test && ./test
Nasm
0
Segmentation fault (core dumped)

int 0x80 工作得很好,但 0x16 破坏了我的代码。我在 fedora 29,Intel core i5

int 0x80

intsysentersyscall 指令是 call 指令的特殊变体:

这些指令调用一个特殊函数,即所谓的"handler"。

int 0x80 是 Linux 操作系统中用于 32 位 Linux 程序的处理程序。从 64 位程序调用 int 0x80(您的程序显然是 64 位)可能 有效,但 可能 也无效.

在 64 位 Linux 中,您使用 syscall 而不是 int 0x80exit 系统调用应该 (*) 如下所示:

mov , %rax  # In 64-bit Linux sys_exit is 60, not 1
mov [=11=], %rdi   # Exit code; this would be %ebx in 32-bit Linux
syscall

int 0x16 是 BIOS 中的处理程序。您只能从 16 位实模式 (**) 程序调用 BIOS 处理程序。您既不能从 32 位程序也不能从 64 位程序调用此处理程序。


(*) 不幸的是,我只编写了 32 位 Linux 的汇编程序,所以我不确定这是否正确。

(**) CPU 在执行 16 位代码时支持两种不同的操作模式。 BIOS 处理程序只能在这两种模式之一下工作。


wait for keyboard

在 Linux 中没有明确的键盘功能。

您必须使用 termios 函数来切换 stdin 文件句柄(文件句柄 0)的行为。在汇编程序中,这将通过 sys_ioctl 调用来完成。

默认行为是 Linux 按行处理输入(例如,如果您按 "AB"+"backspace"+"CD"+"enter", Linux 将 return "ACD"+"enter" 添加到程序中。

默认行为也是 sys_read 将等待直到某些数据可用。使用 termios,您可以通过以下方式更改此行为:所有键盘按下都 returned 到程序 and/or,sys_read 不会等待输入。

然后你调用 sys_readstdin 读取。