函数调用背后的机制(从汇编语言的角度)

我们天天写python,写C;写函数,写if分支语句,写for循环;我们也知道什么是全局变量,什么是局部变量。但我们是否真正思考过:这些语言的实现,在计算机最底层,也就是汇编语言上是怎么实现的呢?

有人可能会说,我从不用汇编,学它何用?我以前也这么认为。

一个不懂汽车体系结构的车手当然也能开车,可当车在高速上突然熄火,那就很难堪了。而且不懂汽车结构的车手,开起车来还费油。我认为学汇编也是这样,你可能不是专业的“汽车修理工”,但哪天程序突然“熄火”,你有更多解决bug的思路,能从底层看到问题。你也能从底层优化程序。这是学汇编的理由之一。

其二,学汇编能培养我们对计算机的兴趣,人天性喜欢刨根问底,只是因为后天应试教育,一点点磨掉了。而通过汇编能让我们脱掉它身上的外衣,甚至“内裤”,直达它的灵魂。其三,说不定有了底层的知识,你可以发明出自己的语言。《道德经》有言:一生二,二生三,三生万物。

自从看了《编码》《程序是怎样跑起来的》,我也开始学汇编,学计算机的底层知识,正在路上,水平很有限。但自从开始学这些,我对编程更有兴趣,也能体会到计算机科学的一点点美感 。这才是更有意义的,兴趣可以保证大周期时间地专研,精进…

今天先说函数的调用过程:
我们定义两个函数:

int AddNum(int a, int b)
{
	return a + b
}

//调用AddNum函数
void MyFunc()
{
	int c;
	c = AddNum(123, 456);
}

如何转换成汇编语言?不同的编译器,翻译成的汇编语言不同。不同的操作系统,不同的cpu都会得到不同的汇编语言,当然也会有不同的机器码。

对应的汇编语言如下:

_TEXT  segment dword public use32 'CODE' 
_TEXT  ends 
_DATA  segment dword public use32 'DATA' 
_DATA  ends _BSS   segment dword public use32 'BSS' 
_BSS   ends DGROUP group   
_BSS,_DATA 
_TEXT  segment dword public use32 'CODE' 
_AddNum        proc    near    
	;     
	;        
		push      ebp        
		mov       ebp,esp    
	;    
	;  	      
		mov       eax,dword ptr [ebp+8]        
		add       eax,dword ptr [ebp+12]    
	;    
 
	;        
		pop       ebp        
		ret

_AddNum        endp

_MyFunc        proc    near    
	;      
	;        
		push      ebp        
		mov       ebp,esp    
	;    
	;   {    
		push      456        
		push      123        
		call      _AddNum        
		add       esp,8    
	;    
	;   }    
	;        
		pop       ebp        
		ret 

	_MyFunc        endp 

	_TEXT  ends        
			end   

我先提出几个问题:

  • 1调用函数后,如何返回到原来的函数代码处?
  • 2系统中只有一个esp,ebp寄存器,每个函数都要有ebp,esp,如果实现呢?把旧的,原来调用函数的值保存一下,存在哪呢?栈中?
  • 3这里是一个什么思想呢?cpu中的寄存器有限。如果实现不同的函数共享这些寄存器?如何管理?这种思想可以用在其它什么地方吗?我以前碰到过吗?
  • 4程序一般是按顺序执行的,那么如何实现不按顺序执行呢。比如分支跳转,比如函数调用,它们都是不按顺序执行的。我觉得有两种,一种是改变程序计数器的值,一种是是通过地址。那么函数调用用的是哪种?
  • 5函数里定义的变量,在汇编语言里是用什么来表示呢?

先说说栈的使用,在函数调用过程中,栈发挥了重大作用。比如如何把参数传给被调用函数,被调用函数如何接收参数,以及调用完函数之后,如何返回到下句继续执行?这些都要通过栈来实现。我们一边分析汇编代码一边画出栈的状态演化图。

1

解读:

  • 1如上图所示,在进入被调用函数之前,先把ebp压入栈中,这是因为每个函数在调用其它函数之前,ebp里面都有值。而ebp只有一个,被调用函数会改变它的值。在调用函数之后再把它弹出,就可以实现恢复它之前的值了。
  • 2然后再压入123,456,这是因为它们是局部变量,不用存入在内存中占用空间,放在栈中,函数实现完可以弹出释放内存空间。
  • 3在进入被调用函数之前,系统会自动把下一句的内存地址压入栈中,记住现在的地址,这样等执行完被调用函数后,就可以“记得”回来执行原函数的下一句了。

2

解读:

现在进入了被调用函数AddNum,同样的,它也有自己的ebp,也要先保存起来
3

解读:

执行完加法运算之后。弹出ebp,恢复它的在运算之前的值
4

解读:

ret,这条指令让系统pop出add,esp,8的内存地址,程序计数器回到原调用函数去了
5

解读:

调用完之后,清空栈空间,释放内存空间
6

解读:

弹出原调用函数的ebp

至此,整个调用过程分析完了,在分析之前提出的几个问题,是不是可以解答了呢?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PeachPy 是一个用于编写高性能汇编内核的 Python 框架,可在汇编中编写模块。 它自动化了一些细节,并允许使用 Python 生成重复的汇编代码序列。PeachPy 旨在简化编写优化的汇编内核,同时保留传统的汇编所有优化机会。一些特性:用于 Windows,Unix 和 Golang 程序集的通用汇编语法.PeachPy 可以直接生成 ELF,MS COFF 和 Mach-O 对象文件以及 Golang 工具链的汇编列表自动适应不同的调用约定和 ABIs用于不同平台的功能可以从汇编相同的源生成支持 Microsoft x64 ABI, System V x86-64 ABI (Linux 和 OS X), Linux x32 ABI, Native Client x86-64 SFI ABI, Golang AMD64 ABI, Golang AMD64p32 ABI自动分配寄存器PeachPy 是灵活的,而且允许在同一代码中混合自动分配寄存器和硬编码寄存器汇编编程中常规任务的自动化:函数 prolog 和 epilog 由 PeachPy 生成数据常量的重复数据删除 (e.g. Constant.float32x4(1.0))分析在函数中使用的 ISA 扩展支持 x86-64 指令,最高可达 AVX-512 和 SHA包含 3dnow! , XOP, FMA3, FMA4, TBM 和 BMI2.不包括 x87 FPU 和大多数系统指令使用自动生成的测试 auto-generated tests 进行严格测试,以生成与 binutils 相同的操作码自动生成元数据文件具有模块依赖性的Makefile (-MMD 和-MF 选项)C 头文件生成的函数JSON 格式的函数元数据基于 Python 的元编程和代码生成多个指令流的复用(有助于软件流水线)兼容 Python 2 和 Python 3,CPython 和 PyPy在线 DEMO: PeachPy.IO 标签:PeachPy
函数调用过程是程序中常见的一种操作,它通常涉及到参数传递、栈帧的建立与销毁、返回值的传递等多个方面。从汇编的角度来看,函数调用过程可以分为以下几个步骤: 1. 将函数的参数压入栈中。在调用函数时,需要将函数所需的参数传递给它。这些参数通常以一定的顺序压入栈中,以便在函数内部使用。在 x86 架构中,参数的传递是通过将参数压入栈顶实现的。 2. 调用函数函数调用的指令通常是 CALL 指令。在调用函数前,需要将函数的入口地址压入栈中,以便在函数执行完毕后返回到调用位置。CALL 指令会将当前的程序计数器(PC)压入栈中,并将函数的入口地址作为新的 PC。 3. 建立栈帧。在函数调用时,需要为函数建立一个独立的栈帧,以便在函数内部使用局部变量和临时变量。栈帧通常包括以下几个部分:返回地址、旧的基址指针、局部变量和临时变量。在 x86 架构中,栈帧的建立是通过将 ESP 寄存器减去一个固定的值实现的。 4. 执行函数。在函数调用后,CPU 会跳转到函数的入口地址并开始执行函数函数内部可以通过栈中的参数和局部变量完成相应的计算和操作。 5. 返回值传递。在函数执行完毕后,需要将函数的返回值传递给调用者。在 x86 架构中,函数的返回值通常通过 EAX 寄存器传递。 6. 销毁栈帧。在函数执行完毕后,需要将栈帧销毁,以便释放栈空间。栈帧的销毁通常是通过将 ESP 寄存器还原到旧的基址指针处实现的。 7. 返回到调用位置。在函数执行完毕后,需要返回到函数调用的位置。在 x86 架构中,返回指令通常是 RET 指令。RET 指令会将栈顶的返回地址弹出,并将其作为新的 PC。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值