C語言10- C語言與匯編

20:C語言與匯編

20.1:調(diào)用約定之匯編

x86調(diào)用約定:

  1. cdecl:參數(shù)從右往左依次入棧熬尺,調(diào)用者棧平衡(C語言缺省的調(diào)用約定,支持可變參數(shù))
  2. stdcall:參數(shù)從右往左依次入棧圆存,被調(diào)用者棧平衡
  3. fastcall:前兩個參數(shù)ecx冤留、edx拓春,后面參數(shù)從右往左依次入棧,被調(diào)用者棧平衡(x86)


    image.png

當調(diào)用fun函數(shù)開始時甩挫,三者的作用。

  1. eip寄存器:存儲的是CPU下次要執(zhí)行的指令的地址趁蕊。也就是調(diào)用完fun函數(shù)后,讓CPU知道應該執(zhí)行main函數(shù)中的printf("函數(shù)調(diào)用結束")語句了仔役。
  2. ebp寄存器:存儲的是是棧的棧底指針掷伙,通常叫棧基址骂因,這個是一開始進行fun()函數(shù)調(diào)用之前炎咖,由esp傳遞給ebp的。(在函數(shù)調(diào)用前你可以這么理解:esp存儲的是棧頂?shù)刂泛ǎ彩菞5椎刂烦伺巍#?/li>
  3. esp寄存器:存儲的是在調(diào)用函數(shù)fun()之后,棧的棧頂俄烁。并且始終指向棧頂绸栅。

20.1.1:cdecl

語法:

  1. int func (int x ,int y) //默認的C調(diào)用約定
  2. int __cdecl func (int x, int y)//明確指出C調(diào)用約定
//由于每次函數(shù)調(diào)用都要由編譯器產(chǎn)生還原棧的代碼,所以使用__cdecl方式編譯的程序比使用__stdcall方式編譯的程序要大很多页屠,但是__cdecl調(diào)用方式是由函數(shù)調(diào)用者負責清除棧中的函數(shù)參數(shù)粹胯,所以這種方式支持可變參數(shù),比如printf()和Windows的API wsprintf()就是__cdecl調(diào)用方式辰企。
//由于參數(shù)按照從右向左順序壓棧风纠,因此最開始的參數(shù)在最接近棧頂?shù)奈恢茫虼水敳捎貌欢▊€數(shù)參數(shù)時牢贸,第一個參數(shù)在棧中的位置肯定能知道竹观,只要不定的參數(shù)個數(shù)能夠根據(jù)第一個后者后續(xù)的明確的參數(shù)確定下來,就可以使用不定參數(shù)了潜索。

int __cdecl func2(int x, int y) {
    return x+y;
}

//被調(diào)用者匯編代碼:
int __cdecl func2(int x, int y)//采用cdecl調(diào)用約定 
{
0042D680 push ebp //ebp入棧)(ebp棾粼觯基址)
0042D681 mov ebp,esp //ebp指向esp(esp棧頂)
0042D683 sub esp,0C0h//esp增長0xC0個空間
0042D689 push ebx //ebx,esi,edi寄存器入棧,這三個寄存器操作系統(tǒng)沒有備份
0042D68A push esi 
0042D68B push edi
0042D68C lea edi,[ebp-0C0h]// 將edi賦值為ebp-0C0h(lea取得偏移地址) ;[ebp-0C0h]即棧頂竹习,將cccccccc拷貝到棧的局部空間中誊抛;INT3指令的機器碼為CC,即CC是斷點指令(作用:一旦程序出了問題整陌,eip指針拗窃,如果eip中的指針指向棧上,開始執(zhí)行就執(zhí)行int 3h)
0042D692 mov ecx,30h//注意到30h * 4 = 0C0h 
0042D697 mov eax,0CCCCCCCCh//0CCCCCCCCh為系統(tǒng)中斷int 3h;
0040042D69C rep stos dword ptr es:[edi]//拷貝開始
    return x+y;
0042D69E mov eax,dword ptr [x] 
0042D6A1 add eax,dword ptr [y] //eax最通用的用法放函數(shù)返回值
}
0042D6A4 pop edi//edi,esi,ebx出棧
0042D6A5 pop esi
0042D6A6 pop ebx 
0042D6A7 mov esp,ebp//esp收縮空間
0042D6A9 pop ebp//ebp出棧
00000042D6AA ret//被調(diào)用者直接返回,不用恢復棧平衡泌辫,由調(diào)用者負責

//調(diào)用者代碼:
func2(1, 2);//采用cdecl調(diào)用約定随夸,參數(shù)從右往左依次入棧,調(diào)用者負責棧平衡 
//0042D737 push 2//參數(shù)從右往左依次入棧甥郑,2入棧 
//0042D739 push 1//參數(shù)從右往左依次入棧,1入棧 
//0042D73B call func2 (42B3FCh)
//0042D740 add esp, 8 //調(diào)用者負責棧平衡荤西,esp+8澜搅,等于2個入棧參數(shù)的長度

20.1.2:stdcall

語法:

  1. int __stdcall func(int x, int y)
//函數(shù)名自動加前導的下劃線伍俘,后面緊跟一個@符號,其后緊跟著參數(shù)的尺寸勉躺。
int __stdcall func1(int x, int y) {
    return x+y;
}

//被調(diào)用者代碼:
int __stdcall func1(int x, int y)//采用stdcall
{
42D640 push ebp 
0042D641 mov ebp,esp 
0042D643 sub esp,0C0h 
0042D649 push ebx 
0042D64A push esi
0042D64B push edi
0042D64C lea edi,[ebp-0C0h] 
0042D652 mov ecx,30h 
0042D657 mov eax,0CCCCCCCCh 
0042D65C rep stos dword ptr es:[edi] 
   return x+y;
0042D65E mov eax,dword ptr [x] 
0042D661 add eax,dword ptr [y]

} 
0042D664 pop edi 
0042D665 pop esi 
0042D666 pop ebx 
0042D667 mov esp,ebp //ebp(調(diào)用前的棧頂)放入esp中癌瘾,然后出棧,恢復老ebp
0042D669 pop ebp 
0042D66A ret 8 //被調(diào)用者負責棧平衡饵溅,ret 8,esp += 8; 

//調(diào)用者代碼
func1(1, 2); //采用stdcall,參數(shù)從右往左依次入棧,被調(diào)用者負責棧平衡
//0042D72E push 2 //參數(shù)從右往左依次入棧,2入棧
//0042D730 push 1 //參數(shù)從右往左依次入棧换途,1入棧
//0042D732 call func1 (42B6F4h)

20.1.3:fastcall

語法:

  1. int fastcall func (int x, int y)
//函數(shù)名自動加前導的下劃線路呜,后面緊跟一個@符號,其后緊跟著參數(shù)的尺寸轻掩。以fastcall聲明執(zhí)行的函數(shù)幸乒,具有較快的執(zhí)行速度,因為部分參數(shù)通過寄存器來進行傳遞的唇牧。

int __fastcall func3(int x, int y, int z) {
    return x+y+z;
}

//被調(diào)用者匯編代碼:
int __fastcall func3(int x, int y, int z)//采用fastcall調(diào)用約定 
{
0042D6C0 push ebp 
0042D6C1 mov ebp,esp 
0042D6C3 sub esp,0D8h 
0042D6C9 push ebx 
0042D6CA push esi 
0042D6CB push edi
0042D6CC push ecx //存放參數(shù)入棧
0042D6CD lea edi,[ebp-0D8h] 
0042D6D3 mov ecx,36h 
0042D6D8 mov eax,0CCCCCCCCh
0042D6DD rep stos dword ptr es:[edi] 
0042D6DF pop ecx 
0042D6E0 mov dword ptr [ebp-14h],edx //前2個參數(shù)放在了ecx和edx中
0040042D6E3 mov dword ptr [ebp-8],ecx//前2個參數(shù)放在了ecx和edx中
    return x+y+z;
0042D6E6 mov eax,dword ptr [x] 
0042D6E9 add eax,dword ptr [y] 
0042D6EC add eax,dword ptr [z] 
}
0042D6EF pop edi 
0042D6F0 pop esi
0042D6F1 pop ebx 
0042D6F2 mov esp,ebp 
0042D6F4 pop ebp 
0040042D6F5 ret 4 //第3個參數(shù)占4個字節(jié)罕扎,從棧上傳遞,所以棧平衡是彈出4個字節(jié)

//調(diào)用者代碼:
func3(1, 2, 3);//采用fastcall丐重,前2個參數(shù)依次放入ecx和edx寄存器腔召,剩余參數(shù)從右往左依次入棧,被調(diào)用者負責棧平衡
//0042D743 push 3 //剩余參數(shù)從右往左依次入棧扮惦,3入棧
//0042D745 mov edx,2 //前2個參數(shù)臀蛛,分別送往ecx和edx寄存器,2入edx
//0042D74A mov ecx,1 //前2個參數(shù)径缅,分別送往ecx和edx寄存器掺栅,1入ecx
//0042D74F call func3 (42B023h)23h)

x64默認的調(diào)用約定是fastcall;fastcall在x64上調(diào)用規(guī)則:

  1. 一個函數(shù)在調(diào)用時纳猪,前四個參數(shù)是從左至右依次存放于RCX氧卧、RDX、R8氏堤、R9寄存器里面沙绝,剩下的參數(shù)從右至左順序入棧;棧的增長方向為從高地址到低地址鼠锈。
  2. 浮點前4個參數(shù)傳入XMM0闪檬、XMM1、XMM2 和 XMM3 中购笆。其他參數(shù)傳遞到堆棧中粗悯。
  3. 調(diào)用者負責在棧上分配32字節(jié)的“shadow space”,用于存放那四個存放調(diào)用參數(shù)的寄存器的值(亦即前四個調(diào)用參數(shù))同欠;小于64位(bit)的參數(shù)傳遞時高位并不填充零(例如只傳遞ecx)样傍,大于64位需要按照地址傳遞横缔;
  4. 調(diào)用者負責棧平衡;
  5. 被調(diào)用函數(shù)的返回值是整數(shù)時衫哥,則返回值會被存放于RAX茎刚;浮點數(shù)返回在xmm0中
  6. RAX,RCX撤逢,RDX膛锭,R8,R9蚊荣,R10初狰,R11是“易揮發(fā)”的,不用特別保護(所謂保護就是使用前要push備份)妇押,其余寄存器需要保護跷究。(x86下只有eax, ecx, edx是易揮發(fā)的)
  7. 棧需要16字節(jié)對齊,“call”指令會入棧一個8字節(jié)的返回值(注:即函數(shù)調(diào)用前原來的RIP指令寄存器的值)敲霍,這樣一來俊马,棧就對不齊了(因為RCX、RDX肩杈、R8柴我、R9四個寄存器剛好是32個字節(jié),是16字節(jié)對齊的扩然,現(xiàn)在多出來了8個字節(jié))艘儒。所以,所有非葉子結點調(diào)用的函數(shù)夫偶,都必須調(diào)整棧RSP的地址為16n+8界睁,來使棧對齊。比如sub rsp,28h
  8. 對于 R8~R15 寄存器兵拢,我們可以使用 r8, r8d, r8w, r8b 分別代表 r8 寄存器的64位翻斟、低32位、低16位和低8位说铃。


    image.png

20.2:傳參之匯編

將實參數(shù)據(jù)傳遞給函數(shù)的方式访惜,分為:

  1. 傳值
  2. 傳指針
  3. C++中的傳引用

傳指針和傳引用都是針對實參來說的

20.2.1:傳值

傳值無法改變實參的值(傳值時存放在棧上的形參只是實參值的一個拷貝,無法改變實參)腻扇。

void func1(int x) {
    x=1;
}

int a=0;
func1(a);

010414A5  mov       eax,dword ptr [a] //把a的值放入eax中债热,然后把eax入棧
010414A8  push      eax
010414A9  call      func1 (10411D6h)
010414AE  add       esp,4

20.2.2:傳指針

傳指針:形參x是一個指向整數(shù)類型數(shù)據(jù)的地址。在函數(shù)內(nèi)部通過*運算符來引用實參幼苛。

void func2(int *x) {
    *x=2;
}

int a=0;
func2(&a);

010414B1  lea eax,[a] //把a的地址傳入eax中
010414B4  push eax //把eax入棧 
010414B5  call func2 (104100Ah)
010414BA  add esp,4

20.2.3:C++中的傳引用

C++中的傳引用:形參部分使用的是&窒篱,而在函數(shù)內(nèi)部,可以直接把形參當做實參來使用,此時形參就是對實參的一個引用墙杯。

void func3(int &x) {
    x=3;
}

int a=0;
func3(a);

010414BD  lea eax,[a] //在匯編層與傳指針的方法完全一樣
010414C0  push eax 
010414C1  call func3 (104102Dh)
010414C6  add esp,4

20.3:C語句之匯編

20.3.1:i++

C語句中的i++對應的匯編語句如下:
int i = 0;
00FC14D4 mov dword ptr [i],0
i++;
00FC14DB mov eax,dword ptr [i]
00FC14DE add eax,1
00FC14E1 mov dword ptr [i],eax
/*
i++在匯編層由多條匯編指令組成济锄。
在多線程環(huán)境下,i++不是多線程安全的霍转,
因為它不是原子操作,因為一個線程執(zhí)行了i++某一條匯編指令一汽,CPU的時間片就有可能用完了避消,發(fā)生了切換,而另外一個線程切換進來之后召夹,又開始從頭開始執(zhí)行i++的匯編指令岩喷,因此造成多線程不一致性。
*/

20.3.2:循環(huán)語句(for,while,do-while)與匯編

for(int i=0;i<10;i++) {…}
//for循環(huán)語句的匯編代碼:
匯編代碼:
i=0;
jmp A;
B:
i++
A:
cmp i, 0ah//0ah(十六進制) = 10(十進制)
jge OUT;
.....

jmp B;
OUT:



while(i<10) { i++; }
//While循環(huán)語句的匯編代碼:
A:
cmp i,0ah
jge OUT;
......
i++;
jmp A;
OUT:

后續(xù)此章節(jié)再補充

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末监憎,一起剝皮案震驚了整個濱河市纱意,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鲸阔,老刑警劉巖偷霉,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異褐筛,居然都是意外死亡类少,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門渔扎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來硫狞,“玉大人,你說我怎么就攤上這事晃痴〔蟹裕” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵倘核,是天一觀的道長泣侮。 經(jīng)常有香客問我,道長笤虫,這世上最難降的妖魔是什么旁瘫? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮琼蚯,結果婚禮上酬凳,老公的妹妹穿的比我還像新娘。我一直安慰自己遭庶,他們只是感情好宁仔,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著峦睡,像睡著了一般翎苫。 火紅的嫁衣襯著肌膚如雪权埠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天煎谍,我揣著相機與錄音攘蔽,去河邊找鬼。 笑死呐粘,一個胖子當著我的面吹牛满俗,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播作岖,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼唆垃,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了痘儡?” 一聲冷哼從身側響起辕万,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎沉删,沒想到半個月后渐尿,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡矾瑰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年涡戳,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片脯倚。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡渔彰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出推正,到底是詐尸還是另有隱情恍涂,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布植榕,位于F島的核電站再沧,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏尊残。R本人自食惡果不足惜炒瘸,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望寝衫。 院中可真熱鬧顷扩,春花似錦、人聲如沸慰毅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至婶芭,卻和暖如春东臀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背犀农。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工惰赋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人呵哨。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓谤逼,卻偏偏與公主長得像,于是被迫代替她去往敵國和親仇穗。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容