Nasm에서 콘솔에 출력하는 코드를
만드는 것은 상당히 복잡하기 때문에
직접 만들어서 사용하려면 한계가 있습니다.
어셈블리어도 뭔가를 콘솔에
출력해보는 일이 많기 때문에
뭔가 대안이 필요한데요.
이럴 때는 '수레바퀴를 새로 발명하지 말라'
(Don't reinvent the wheel) 라는 오랜
프로그래밍 명언을 귀담아 들을 필요가 있습니다.
바로 C언어의 printf 함수를 가져와서
사용하는 것 입니다. printf 는 다양한
포맷을 사용할 수 있으니까 유용합니다.
%include "utilities.inc"
%macro printString 1
mov rax, %1
call _printfString
%endmacro
%macro printInt 1
mov rax, %1
call _printfInt
%endmacro
section .data
text1: db "[NASM Assembly Language]",0
text2: db "1. call printf function",0
stringFormat: db "%s",10,0
integerFormat: db "- value: (%ld)",10,0
section .text
global main
main:
printString text1
printString text2
printInt 157
printInt 9567
_last:
exit
_printfString:
push rbp
mov rdi, stringFormat
mov rsi, rax
mov rax, 0
call printf wrt ..plt
pop rbp
ret
_printfInt:
push rbp
mov rdi, integerFormat
mov rsi, rax
mov rax, 0
call printf wrt ..plt
pop rbp
ret
utilities.inc 파일은 합쳐도 상관없는데
메인 파일을 정리하다 보면
자연스럽게 분리가 됩니다.
;C library function
extern printf
;terminate program
%macro exit 0
mov rax, SYS_EXIT
mov rdi, EXIT_SUCCESS
syscall
%endmacro
;Linux system call ID
SYS_READ equ 0
SYS_WRITE equ 1
SYS_OPEN equ 2
SYS_CLOSE equ 3
SYS_EXIT equ 60
;file descriptor
STDIN equ 0
STDOUT equ 1
STDERR equ 2
;exit code
EXIT_SUCCESS equ 0
extern printf 가 필요하고
_printfString 을 보면 함수 호출을 위한
rbp 포인터를 스택에 보존했다가
printf 함수가 끝난다음에 다시 복구합니다.
printf 함수의 문자열 포맷을 rdi로 넘깁니다.
rax 는 매크로에서 텍스트를 넘겨주고,
준비가 끝나면 printf 를 호출합니다.
여기서 wrt ..plt 가 없으면 에러가 나더군요.
yasm 메뉴얼에 elf32 스페셜 심볼이라고
되어 있는데 자세한 이유는 모르겠습니다.
아마 64비트에서 printf 를
호출할 때 필요한 듯... 추측합니다.
어셈블리어와 컴파일러는 이해안되는게 많아서;;;
9.6. elf32 Special Symbols and WRT (tortall.net)
nasm 컴파일 후 gcc로 링크하면 C언어의
printf 함수를 사용할 수 있습니다.
all:
nasm -f elf64 -o main.o -l main.lst main.asm
gcc main.o -o main
./main
gcc를 사용하는 경우 entry symbol이 _start 가
아니라 main 이어야 인식이됩니다.
*참고문서
Calling printf from the C standard library in assembly | Mourtada.se
tutorials/Assembly/Nasm/basics/extern_printf at main · neokayken/tutorials (github.com)