Here is piece of my nasm code:
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
Output:
[b@l .K]$ nasm test.asm -f elf64 -o test.o && gcc test.o -o test && ./test
Nasm
Segmentation fault (core dumped)
Whan I replace:
print fmts, msg1
print fmti, [num]
xor ah, ah
int 0x16
then
[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 works greatly but 0x16 crush my code. I'm on fedora 29, Intel core i5
int 0x80
The int
, sysenter
and syscall
instructions are special variants of a call
instruction:
These instructions call a special function, a so-called "handler".
int 0x80
is a handler in the Linux operating system intended for 32-bit Linux programs. Calling int 0x80
from 64-bit programs (and your program obviously is 64-bit) may work, but it may also not work.
In 64-bit Linux you use syscall
instead of int 0x80
. The exit
system call should (*) look like this:
mov $60, %rax # In 64-bit Linux sys_exit is 60, not 1
mov $0, %rdi # Exit code; this would be %ebx in 32-bit Linux
syscall
int 0x16
is a handler in the BIOS. You can only call BIOS handlers from 16-bit real-mode (**) programs. You can neither call this handler from 32- nor from 64-bit programs.
(*) Unfortunately, I have written assembly programs for 32-bit Linux only, so I'm not sure if this is correct.
(**) The CPU supports two different operating modes when executing 16-bit code. BIOS handlers will only work in one of these two modes.
wait for keyboard
In Linux there are no explicit keyboard functions.
You have to use the termios
functions to switch the behavior of the stdin
file handle (file handle 0). In assembler, this would be done by a sys_ioctl
call.
The default behavior is that Linux processes the input line-wise (e.g. if you press "AB"+"backspace"+"CD"+"enter", Linux will return "ACD"+"enter" to the program).
The default behavior is also that sys_read
will wait until some data is available. Using termios
you may change this behavior in a way that all keyboard presses are returned to the program and/or that sys_read
will not wait for input.
Then you call sys_read
to read from stdin
.