x86_64汇编之六:系统调用(system call)

本文将主要讲述如何在汇编语言代码中调用Linux的系统调用。

一、32位系统的系统调用

在32位x86 Linux系统中,可用的系统调用定义在/usr/include/asm/unistd_32.h头文件中。

每个系统调用都对应一个编号 以及 若干个参数。如果想使用汇编语言调用系统调用,那么在调用之前,需要将系统调用编号存到%eax,将参数依次存到%ebx, %ecx, %edx, %esi, %edi, %ebp中,然后再执行int $0x80指令即可。

每个系统调用的编号和参数列表可以参考:https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md#x86-32_bit

下面的汇编代码通过write系统调用往屏幕上打印出了Hello

.section .data
output:
	.string "Hello\n"

.text
...

movl $4, %eax # write对应编号为4
movl $1, %ebx # 第1个参数文件描述符为1,表示stdout
leal output(%rip), %ecx # 第2个参数,字符串指针
movl $6, %edx # 第3个参数,写入的字节数
int $0x80

movq $1, %eax # exit系统调用
xor %edi, %edi # 第1个参数,0,即exit(0)
int $0x80

二、64位系统的系统调用

在64位x86_64 Linux系统中,可用的系统调用定义在/usr/include/asm/unistd_64.h头文件中。

每个系统调用都对应一个编号 以及 若干个参数。如果想使用汇编语言调用系统调用,那么在调用之前,需要将系统调用编号存到%rax,将参数依次存到%rdi, %rsi, %rdx, %r10, %r8, %r9中,然后再执行syscall指令即可。

每个系统调用的编号和参数列表可以参考:
https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md#x86_64-64_bit

下面的汇编代码通过write系统调用往屏幕上打印出了Hello

.section .rodata
msg:
	.string "Hello\n"

.text
.globl _start
_start:
	pushq %rax # for stack alignment

	movq $1, %rax 
	movq $1, %rdi
	leaq msg(%rip), %rsi
	movq $6, %rdx
	syscall

	# exit(0)
	movq $60, %rax
	xor %rdi, %rdi
	syscall

需要注意,上述代码中第一行是为了实现栈对齐,关于栈对齐请参考x86_64汇编之四:函数调用、调用约定。

如何运行上述代码?

as -o a.o a.s
ld -o a a.o 
./a

三、调用C函数

除了直接使用Linux提供的系统调用接口之外,我们还可以在汇编代码中直接使用C函数,例如直接调用call printf来调用printf函数。参数传递的规范遵循System V AMD64调用约定

.section .rodata
msg:
	.ascii "Hello, %d\n"
.text
.globl main
main:
	pushq %rbp # for stack alignment

	leaq msg(%rip), %rdi # 1st param
	movq $1, %rsi # 2nd param
	movb $0, %al # variable length param function:
				  # number of vector registers 
	callq printf

	# exit(0)
	movq $0, %rdi
	callq exit

注意上述的movb $0, %al:因为printf是可变参数函数,因此根据System V AMD64调用约定需要在%al寄存器中设置它使用的向量寄存器个数。

汇编并链接上述代码:

as -o a.o a.s
gcc -o a a.o # 由于调用了C标准库的函数,所以使用gcc进行链接
./a

最终打印出Hello, 1

你可能感兴趣的:(汇编)