linux 汇编 函数调用

Linux 汇编-函数调用

GNU 汇编函数

定义函数

  • 格式
.type func_name, @function
func_name:
	#content
ret
  • 注解

.type 指令指定 func_name 为汇编程序调用此函数的地址

fun_name 为函数名

@function 表示函数内容开始

ret 表示函数结束,返回到父函数调用子函数处

调用函数

  • 格式
call fun_name
  • 注解

call 是调用子函数的汇编命令

fun_name 是定义子函数时,.type 指定的函数地址

编写汇编函数

使用寄存器和全局变量传递函数参数

即在主函数中先将数据保存到某寄存器,然后在函数中直接使用这个寄存器的指。

# ASM function use register give the inut data
.section .data
	str:.ascii "ASM THIRD DAY\n"
	sl=.-str

.section .text
.global _start
_start:
	movl $0, 	%eax		# Init eax
	movl $2,  	%ebx		# Prepare data in ebx first

	call 		add_fun		# Then call child function
	
	# System call: write()

	movl $1, 	%ebx
	movl $str, 	%ecx
	movl $sl, 	%edx

	int $0x80

	movl $1, 	%eax
	
	# System call: exit
	movl $0, 	%ebx
	int $0x80 	

# The method of GNU define a function
.type add_fun, @function
add_func:
	add %ebx, 	%ebx
	movl %ebx, 	%eax
ret
root@root:~# as fun_register.s -o fun.o
root@root:~# ld fun.o -o fun
root@root:~# .fun
  • 注解

主程序中,movl $2, %ebx 将常量 2 赋值给寄存器 ebx, 为子函数 add_func 准备好数据。

接下来调用子函数,使用 add 指令对寄存器 ebx 数据进行处理。

通过全局变量给子函数传递数据,只需在数据段(.data 或者 .bss)定义变量,在主函数中初始化后在调用子函数来使用变量。

在父函数调用子函数时,一定要符合子函数的需求:准备好要用到的寄存器和变量,准备好子函数输出结果的保存地址,同时主函数中也要直到函数输出保存的地址。

使用 C 风格传参方式

C 风格传参方式使用栈来给子函数准备数据,适合复杂的汇编程序

只在栈顶发生操作,入栈的新栈顶,出栈移除当前栈顶元素,入栈 push,出栈 pop

堆栈寄存器 esp 需要始终指向栈顶。

ebp 用于保存栈基址。

高地址内存叫栈底,低地址内存叫栈顶。

入栈后,esp 值减小,即指向更小的地址。

除使用 pushpop 指令外,还可操作栈地址来实现。通常将 esp 值拷贝到 ebp 来操作。

  • 通过栈为函数准备数据

将子函数需要的数据存到栈里,需要保证存在栈里的数据与子函数使用的顺序相对应。

注意: 当调用子函数时,系统会将当前地址压入栈中,当子函数执行完之后返回之前地址,以便程序继续执行。

# Given data to child function by stack
.section .data
	str:.ascii "ASM FOURTH DAY\n"
	sl=.-str

.section .text
.global _start
_start:
	push $4			# Be ready data for child func by stack
	call give_value_to_eax

	movl $1, 	%ebx	# System call: write()
	movl $str, 	%ecx
	movl $sl,	%edx
	
	int $0x80

	movl $1, 	%eax	# System call exit
	movl $0, 	%ebx
	int $0x80

# Child func
.type give_value_to_eax, @function
give_value_to_eax:
	pushl %ebp		# Save ebp value
	movl %esp, %ebp		# Get stack top address for ebp

	movl 8(%ebp), %eax 	# eax's value is the bottom value of the stack

	pop %ebp 			# Get the old ebp value again
ret

root@root:~# as fun_stack.s -o fun.o
root@root:~# ld fun.o -o fun
root@root:~# .fun
  • 注解

主函数中,是两个系统调用代码。

write 系统调用代码中,eax 的值通过子函数 give_value_to_eax 来赋予。

在 give_value_to_eax 子函数中,使用在主函数中在栈中准备的数据来给 eax 赋值。因为压入栈中的还有当前调用函数的地址,因此使用 ebp 来访问原先压入栈中的数据。

子函数中,第一行为备份 ebp 值,第二行将栈顶的 esp 值赋给 ebp,第三行通过 ebp 找到主函数为子函数准备的数据,并赋给 eax。

使用 ebp 来获取 esp 值,然后通过 ebp 来访问栈中数据是为了保证 esp 一直指向栈顶。

 Program Stack
+-------------------------+
|                         |
|                         | Indirect addresing
|-------------------------|
| Function parameter 3    | 16(%ebp)
|-------------------------|
| Function parameter 2    | 1(%ebp)
|-------------------------|
| Function parameter 1    | 8(%ebp)
|-------------------------|
| Return Address          | 4(%ebp)
|-------------------------|
| Old EBP Value           | (%ebp)  <----- ESP
+-------------------------+

函数末尾 pop %ebp 之后,栈顶就是 Return Address 了。

当要用栈来存储函数内的局部变量时,只需要将 esp 继续开辟新的栈顶增加即可:

 Program Stack
+-------------------------+
|                         |
|                         | Indirect addresing
|-------------------------|
| Function parameter 3    | 16(%ebp)
|-------------------------|
| Function parameter 2    | 1(%ebp)
|-------------------------|
| Function parameter 1    | 8(%ebp)
|-------------------------|
| Return Address          | 4(%ebp)
|-------------------------|
| Old EBP Value           | (%ebp)
|-------------------------|
| Local Variable 1        | -4(%ebp)
|-------------------------|
| Local Variable 2        | -8(%ebp)
|-------------------------|
| Local Variable 3        | -12(%ebp)  <---- ESP
|-------------------------|
|                         | 
|-------------------------|
|                         | 
+-------------------------+
原文地址:https://www.cnblogs.com/openxyz/p/6616123.html