在nasm中,调用C库函数,需要使用extern关键字,后面跟函数名称。
例子test6_1.asm
; test6_1.asm
; nasm -f elf64 -o test6_1.o test6_1.asm
; gcc -o test6_1 test6_1.o
; 或者链接: ld -o test6_1 test6_1.o -lc --dynamic-linker /lib/x86_64-linux-gnu/ld-2.19.so
; ./test6_1
extern printf
section .data
fmt_ld: db '%ld',0xa,0
fmt_d: db '%d',0xa,0
fmt_s: db '%s',-xa,0
var_int: dd 2222222
msg: db 'this is a message',0xa,0
section .text
global main
main:
push rbp
mov rdi, fmt_ld
mov rsi, 123
mov rax, 0
call printf
mov rdi, fmt_d
mov rsi, [var_int]
mov rax, 0
call printf
mov rdi, fmt_s
mov rsi, msg
mov rax, 0
call printf
pop rbp
mov rax, 60
syscall
ret
在数据段,定义了数据var_int,msg等,包括输出格式。
这里注意,_start换成了main,因为使用gcc链接时,默认入口是main。
在调用printf前,填充参数,还记得保存参数的寄存器吗?rdi,rsi,rdx,rcx,r8,r9.
mov rax, 0表示不使用xmm寄存器,xmm寄存器是128位的,也就是16字节长度。
call printf 调用C的printf函数。
mov rsi, [var_int],把var_int地址处的值去8个字节存放到rsi中。
mov rsi, msg,把字符串msg的首地址存放到rsi中。
汇编链接:
nasm -f elf64 -o test6_1.o test6_1.asm
gcc -o test6_1 test6_1.o
或者链接: ld -o test6_1 test6_1.o -lc --dynamic-linker /lib/x86_64-linux-gnu/ld-2.19.so
汇编使用nasm命令,在链接的时候可以使用ld,但是要链接动态链接库,在linux中动态链接库是以.so为后缀的,要使用C的库函数,就需要链接C的动态链接库,动态链接库要根据自己系统的版本进行寻找。
当然,你也可以直接使用gcc,gcc中包含链接器ld,而且会搜索需要链接的动态链接库。
链接之后,会发现test6_1文件大了增加了很多字节,这是添加了动态链接库的二进制代码的结果。
上面是使用寄存器来传递参数,也可以使用栈来传递参数,如下面的test6_2.asm
; test6_2.asm
; nasm -f elf64 -o test6_2.o test6_2.asm
; gcc -o test6_2 test6_2.o
; ./test6_2
extern printf
section .data
fmt_ld: db '%ld',0xa,0
section .text
global main
main:
push rbp
mov rsi, 123
mov rdi, fmt_ld
push rsi
push rdi
call printf
add rsp, 16
pop rbp
mov rax, 60
syscall
ret
将参数按从右往左压入堆栈中,然后调用printf。
因为C的函数默认调用方式是_cdecl,这种调用方式,由调用者在调用后调整堆栈。因此add rsp, 16 调整堆栈,栈中参数是两个,长度一共是16,栈是从高地址向低地址生长的,因此,将rsp加上16,栈就恢复成调用前的状态,不会影响后续栈的使用。