调用 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
int
、sysenter
和 syscall
指令是 call
指令的特殊变体:
这些指令调用一个特殊函数,即所谓的"handler"。
int 0x80
是 Linux 操作系统中用于 32 位 Linux 程序的处理程序。从 64 位程序调用 int 0x80
(您的程序显然是 64 位)可能 有效,但 可能 也无效.
在 64 位 Linux 中,您使用 syscall
而不是 int 0x80
。 exit
系统调用应该 (*) 如下所示:
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_read
从 stdin
读取。
这是我的 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
int
、sysenter
和 syscall
指令是 call
指令的特殊变体:
这些指令调用一个特殊函数,即所谓的"handler"。
int 0x80
是 Linux 操作系统中用于 32 位 Linux 程序的处理程序。从 64 位程序调用 int 0x80
(您的程序显然是 64 位)可能 有效,但 可能 也无效.
在 64 位 Linux 中,您使用 syscall
而不是 int 0x80
。 exit
系统调用应该 (*) 如下所示:
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_read
从 stdin
读取。