使用了未赋值的局部变量_macOS上的汇编入门(七)——字面量与局部变量
在上一篇文章中,我們分析了第一個(gè)匯編程序。
# exit.s .section __TEXT,__text.globl _main _main:movq $0, %raxretq這個(gè)匯編程序是我們所有匯編程序的框架,因?yàn)樗鼘?shí)現(xiàn)了程序進(jìn)入和程序退出的功能。我們接下來(lái)所有的程序都是在這個(gè)程序的基礎(chǔ)上進(jìn)行修改。
在這篇文章中,我主要介紹的是匯編語(yǔ)言中變量的使用。在x86-64架構(gòu)下,寄存器的數(shù)量很少。而且,寄存器的作用往往是用于運(yùn)算而不是用于存儲(chǔ)。那么,我們?cè)诔绦蛑性撊绾问褂米兞磕?#xff1f;
.equ定義字面量
最簡(jiǎn)單的定義變量的方式,是利用匯編器指令.equ. 這類似于C語(yǔ)言中的#define. 比如說(shuō),我在程序開(kāi)頭寫(xiě)上
.equ maxCount, 0x114514那么,我在之后的程序里就可以寫(xiě)
movq $maxCount, %rax來(lái)表示將0x114514賦值給rax寄存器。
同時(shí)這里應(yīng)當(dāng)指出,這個(gè)指令是匯編器指令,在匯編的時(shí)候,會(huì)自動(dòng)將所有的maxCount直接用0x114514替代。比如說(shuō),我有以下程序:
.text.globl _main.equ maxCount, 0x114514 _main:movq $maxCount, %raxretq我們通過(guò)匯編、鏈接以后,得到一個(gè)test可執(zhí)行文件。我們可以用之前提到的MachOView軟件,或者在終端中鍵入
otool -v -t ./test來(lái)查看生成的可執(zhí)行文件中__TEXT段__text節(jié)的內(nèi)容:
由此可知,最終生成的文件中,是直接替換得到的。
此外,.equ還有一個(gè)比較方便的地方在于,它可以支持簡(jiǎn)單的算術(shù)運(yùn)算,如加減乘除等。比如說(shuō),我可以寫(xiě).equ maxCount, 1919-810, 那么接下來(lái)所有出現(xiàn)maxCount的地方,都會(huì)用1109來(lái)替代。
但是,正如C語(yǔ)言中的#define定義的宏一樣,.equ定義的變量只是一個(gè)簡(jiǎn)單的替換,并不支持對(duì)這個(gè)變量重新賦值之類的操作。這個(gè)變量也沒(méi)有其地址,只是一個(gè)字面量。
局部變量
棧
我們知道,在C語(yǔ)言中,局部變量在棧上分配。在匯編語(yǔ)言中也是這樣。因此,我們來(lái)回憶一下「棧」的概念。
在操作系統(tǒng)基礎(chǔ)中,我們談到,在一個(gè)程序運(yùn)行的時(shí)候,系統(tǒng)會(huì)自動(dòng)給這個(gè)程序分配一個(gè)棧區(qū)。這個(gè)棧區(qū)和數(shù)據(jù)結(jié)構(gòu)中所說(shuō)的棧類似,也支持壓棧和彈棧的操作。棧區(qū)在邏輯地址空間里是一塊連續(xù)的空間,棧底是固定的,每次壓棧,都會(huì)使棧頂向邏輯地址減小的方向移動(dòng)。
在幾個(gè)寄存器中,有一個(gè)寄存器和棧的關(guān)系非常大,那就是rsp寄存器。從它的名字就可以看出來(lái),stack pointer, 它存儲(chǔ)的值永遠(yuǎn)是棧頂?shù)牡刂?#xff0c;所以它又被叫做棧頂指針。我們可以用(%rsp)來(lái)獲取棧頂存儲(chǔ)的值,通過(guò)a(%rsp), 其中a是任何一個(gè)整數(shù),來(lái)獲取地址是rsp存儲(chǔ)的值加a處的內(nèi)存單元的值。比如說(shuō),2(%rsp)就是棧頂上方(邏輯地址增大方向)2個(gè)字節(jié)處的值,-2(%rsp)就是棧頂下方(邏輯地址減小方向)2個(gè)字節(jié)處的值。關(guān)于這個(gè)記號(hào),我也會(huì)在之后的尋址方式中提到。
在匯編語(yǔ)言中,壓棧和彈棧的助記符分別是push和pop. 這兩個(gè)操作均有一個(gè)操作數(shù)。push的操作是將棧頂指針向下移動(dòng)(也就是將rsp內(nèi)的值減小),并將移動(dòng)后rsp對(duì)應(yīng)位置內(nèi)存區(qū)域的值賦為其操作數(shù),而pop則相反。這里“向下移動(dòng)”的距離是根據(jù)push后面跟著的字母決定的,如pushq就是把rsp內(nèi)的值減8.
此外,如果是想獲得棧頂?shù)闹?#xff0c;而不彈棧,可以直接用mov來(lái)實(shí)現(xiàn)。如popq %rax是將棧頂?shù)?個(gè)字節(jié)內(nèi)存儲(chǔ)的值賦給rax, 并且棧頂指針向上移動(dòng)8個(gè)字節(jié)。而movq (%rsp), %rax則是只將棧頂?shù)?個(gè)字節(jié)內(nèi)存儲(chǔ)的值賦給rax, 不涉及棧頂指針的移動(dòng)。而如果只想彈棧卻不想賦值,那么直接對(duì)rsp進(jìn)行add即可。如想把棧頂?shù)?個(gè)字節(jié)的數(shù)據(jù)彈棧,就直接addq $8, %rsp.
同時(shí),對(duì)于push而言,如果我們一下子準(zhǔn)備把許多值壓入棧內(nèi),那么可以先用sub指令減小rsp, 再用mov移動(dòng)。比如說(shuō):
# method 1 pushq $0x114514 pushq $0x1919 pushq $0x810# method 2 subq $24, %rsp movq $0x114514, 16(%rsp) movq $0x1919, 8(%rsp) movq $0x810, (%rsp)方法一和方法二的最終效果是一樣的。但是,我們建議使用方法二,也就是“先sub, 再mov”,因?yàn)檫@樣更高效。
使用局部變量
講完了棧的概念,接下來(lái)就是如何使用局部變量了。使用局部變量非常簡(jiǎn)單,就是將局部變量放到棧上,然后使用的時(shí)候直接去訪問(wèn)棧上對(duì)應(yīng)的地址空間就行。然后在返回之前,把棧恢復(fù)即可。
但是,這里有一個(gè)常用的技巧。像上面的例子中寫(xiě)的,我們是通過(guò)對(duì)rsp中存儲(chǔ)的地址加偏移量去訪問(wèn)局部變量,但是,如果我們之后又有了壓棧、彈棧的操作,那么,偏移量就會(huì)改變。這種不穩(wěn)定性十分不利于我們編程。因此,我們又用了另一個(gè)寄存器rbp來(lái)解決這個(gè)問(wèn)題。rbp, 顧名思義,base pointer, 基地址指針,一般是用來(lái)使用偏移量尋址的。我們使用的技巧是,先將rbppush進(jìn)棧(之所以保留我會(huì)在后面的調(diào)用約定里說(shuō)到),然后利用之前的手法對(duì)rspsub. 然后,利用rbp的偏移量來(lái)引用局部變量。最后在返回前,將rbp賦值給rsp, 此時(shí)棧頂指針指向的是最初對(duì)rbppush之后的位置,然后將棧頂pop出來(lái)給rbp,最后返回。
比如說(shuō),我有以下C程序:
int main() {int a = 0x114514;int b = 0x1919;int c = 0x810;return 0; }那么,它對(duì)應(yīng)的匯編程序如下:
_main:pushq %rbpmovq %rsp, %rbpsubq $24, %rspmovq $0x114514, -8(%rbp)movq $0x1919, -16(%rbp)movq $0x810, -24(%rbp)movq $0, %raxmovq %rbp, %rsppopq %rbpretq它對(duì)應(yīng)的棧的變化如圖所示:
由此可見(jiàn),在執(zhí)行完popq %rbp之后,棧又恢復(fù)為最初進(jìn)入時(shí)的模樣。
我們?cè)谑褂胷bp+偏移量來(lái)訪問(wèn)局部變量的時(shí)候,有時(shí)候會(huì)覺(jué)得要把變量對(duì)應(yīng)的偏移量記住,這會(huì)比較麻煩。我們可以結(jié)合上面講到的.equ定義字面量來(lái)解決這一問(wèn)題:
_main:.equ a, -8.equ b, -16.equ c, -24pushq %rbpmovq %rsp, %rbpsubq $24, %rspmovq $0x114514, a(%rbp)movq $0x1919, b(%rbp)movq $0x810, c(%rbp)movq $0, %raxmovq %rbp, %rsppopq %rbpretq這樣,我們只需要之后用a(%rbp)就可以指代a了。
可以在哪看到這系列文章
我在我的GitHub上,知乎專欄上和CSDN上同步更新。
上一篇文章:macOS上的匯編入門(六)——匯編語(yǔ)言初識(shí)
下一篇文章:macOS上的匯編入門(八)——尋址方式與全局變量
總結(jié)
以上是生活随笔為你收集整理的使用了未赋值的局部变量_macOS上的汇编入门(七)——字面量与局部变量的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: vba动态二维数组_VBA实战技巧05:
- 下一篇: 深度学习训练的时候gpu占用0_26秒单