網站首頁 編程語言 正文
?一、C函數棧幀開辟以及回退過程
__cdecl(C語言默認調用方式,函數參數8字節以內,使用push。本節采用此方式)
main函數的棧幀調用sum函數的棧幀,sum函數棧幀使用完了以后回退都是怎么進行的,要搞清楚這個問題必須得看匯編代碼,匯編代碼分為兩種:inter x86(windows)和AT&T(unix)。這兩種匯編非常相似,x86的匯編是從右向左看,unix的匯編是從左向右看的。
局部變量都是通過棧底指針ebp偏移訪問,不生成符號,不屬于數據,屬于指令。
形參壓棧在C/C++中是從右向左壓棧,因為要支持可變長參數,如果從左向右,編譯器就不知道用戶傳入了多少實參,形參內存是在調用函數棧幀中開辟,每壓棧一個實參,都會開辟一個形參的空間,棧頂指針esp都會減4字節。
實參壓棧完成后需要調用call指令來執行sum函數,執行完sum函數后,執行完sum函數后需要回到調用指令(call)的下一條指令繼續執行。
call指令做兩件事:
- 1.把下一行指令的地址入棧
- 2.jmp跳轉
棧空間圖:
執行call指令后,程序調到這里,不是sum函數的指令部分
在編譯階段,所有匯編指令代碼引用符號的地方全部不是合法的地址(因為我們當前文件可能引用外部的符號,而編譯階段是獨立編譯的,我們鏈接的時候才會進行符號解析、合并符號表等操作,之后再給符號分配內存地址),對于數據符號來說是零地址,對于函數符號來說是-4。那么當我們在鏈接階段符號解析完成以后,得到每一個符號的具體地址,給數據符號分配的地址是絕對地址,給函數符號分配的地址是與下一行指令地址的一個偏移量,這樣當程序需要跳轉到某個函數地址的時候,取出PC寄存器保存的地址與該偏移量相加就得到函數的入口地址。
計算相對偏移量后就進入了sum函數,先執行下面一段指令,才執行我們寫的sum函數
每次執行一個函數前要執行三個操作:
- 把調用方的棧底地址入棧(push ebp),讓ebp指針指向當前函數的棧底(mov ebp,esp)
- 移動棧頂指針esp,給被調用函數開辟棧幀(sub esp 44h)
- 初始化新棧幀內存,把esp和ebp之間所有的棧內存全部初始化成0xCCCCCCCC(rep stos dword ptr [edi])無效值。
Linux為棧幀不分配初始值,windows會分配初始值,為0xCCCCCCCC(-858993460)
然后執行sum函數的指令:
局部變量通過棧底指針ebp負向偏移訪問,形參通過ebp正向偏移訪問,eax為a+b的計算結果,將計算結果賦值給temp,return的時候將temp的值賦值給eax,給調用方返回
棧幀清退:
可以看到,開辟棧幀的時候我們對占內存進行了初始化,但是棧幀清退的時候僅僅就是修改了esp和ebp,沒有做其他任何操作,如果我們此時通過一些手段去訪問已被清退棧幀的內存,還是可以訪問到的,因為數據還存在
參數清除:
sum函數執行完成后,從PC寄存器中取出地址繼續執行,這里PC寄存器存放的是call指令的下一行地址,這條指令做的操作是回退形參變量占的內存,形參內存由調用方開辟和釋放。sum函數返回值由eax寄存器帶回來。
在被調用方執行完成后,通過pop ebp就知道應該回到哪(恢復棧底),再通過ret指令就知道回到哪以后從哪一行指令開始運行(取出棧頂元素放到PC寄存器里面,而棧頂元素存的就是call的下一行地址)
sum函數棧底保存的是main的棧底地址,main函數棧底保存的是調用main的函數的棧底地址
二、C函數調用約定和返回值
函數調用約定和返回值
原文鏈接:https://blog.csdn.net/qq_41721746/article/details/124557537
相關推薦
- 2022-07-02 一個Python優雅的數據分塊方法詳解_python
- 2023-01-15 SqlServer?多種分頁方式?詳解(含簡單速度測試)_MsSql
- 2021-12-10 Android實現socket通信統一接口的方法_Android
- 2022-04-20 Flutter如何輕松實現動態更新ListView淺析_Android
- 2022-12-29 Python?PyQt5學習之樣式設置詳解_python
- 2022-07-03 kafka?rabbitMQ及rocketMQ隊列的消息可靠性保證分析_相關技巧
- 2022-10-13 解析django的csrf跨站請求偽造_python
- 2023-02-25 深入理解go?reflect反射慢的原因_Golang
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支