青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

S.l.e!ep.¢%

像打了激速一樣,以四倍的速度運轉,開心的工作
簡單、開放、平等的公司文化;尊重個性、自由與個人價值;
posts - 1098, comments - 335, trackbacks - 0, articles - 1
  C++博客 :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

在上一篇博客中小覽call stack(調用棧) (一)中,我展示了如何在windbg中觀察調用棧的相關信息:函數的返回地址,參數,返回值。這些信息都按照一定的規則存儲在固定的地方。這個規則就是調用約定(calling convention)。

?

調用約定在計算機界不是什么新鮮的概念,已經有許多相關的文獻給予詳細的介紹。比較全面的介紹可以參見wikipedia上的相關頁面。然而,如果你和我一樣,在第一次接觸調用約定的時候,覺得這個概念是個高深神秘的冬冬,那么就請跟隨我一起,在這篇博客中看看他的由來,他的范疇以及他的用途。

?

為什么需要調用約定?

在具體介紹調用約定的定義之前,我們先來看看為什么我們需要一個稱之為調用約定的冬冬。如果各位了解匯編語言(不了解的話,看下面的這段會稍微有些費力,不過我盡可能把匯編的相關知識解釋的清楚一些),那么回憶一下我們是怎么來做一個函數調用的。

?

匯編語言提供了一條指令,call ptr,其功能是把CS:IP (指令段:指令指針,決定著下一條執行指令的地址)壓棧,并且修改CPU的指令指針,作一個跳轉。在函數結束的地方,我們使用另一條指令,ret,其功能是把棧中的返回地址取出,并且跳轉到那條指令。

?

在這里匯編語言只提供了指令跳轉的命令,作為函數調用另一個重要組成部分的參數傳遞,其方式就很靈活,你可以通過寄存器傳值,可以通過調用棧傳值,可以通過某一塊具體的內存傳值(類似全局變量)。然后在被調用函數中,從寄存器,?;蛘呤莾却嬷凶x取這些信息。想象一下如果被調用函數是某一個程序員所編寫的,調用者是另一個程序員,那么他倆之間對于參數的傳遞方式就有了一個約定。

?

高級語言的出現,把這個問題隱藏了起來。我們在編寫一般的c++程序的時候,通常不需要顧慮參數傳遞的底層實現,但是,這并不意味著這一問題不再出現——我們只是把責任推給了編譯器。編譯器作為一個計算機程序,總是遵照一定的規則工作,每一個規則對應了一種調用約定。

?

久而久之,那些經典的規則所產生的調用約定,就成了耳熟能詳的冬冬:

?

耳熟能詳的調用約定

在介紹這些調用規范之前,我想先說明的是,下面所涉及的調用規范是在32位x86處理器windows平臺上的。把范疇限定在32位處理器的原因是:16位處理器已經退出CPU的歷史舞臺,64微處理器無論是IA64還是AMD64都只有一個調用規范——只有32位處理器呈現百家成名,百花齊放的景象。(對了,你當然明白調用規范是綁定在處理器架構上的概念,因為它涉及太多的諸如寄存器之類的處理器架構細節。)聚焦于windows則是因為我現在的工作只涉及這一平臺。

下表的出處來自于The Old New Thing以及張羿的csdn專欄,并作了適當修改。


首先來看所有的調用規范都遵循的規定:返回值存儲在EDX:EAX中,EDI,ESI,EBP,EBX是保留的存儲器。(即函數可以任意使用這些寄存器,無需擔心破壞了調用者的寄存器狀態)

調用約定名稱
?清理堆棧
?參數壓棧順序
?備注
?
cdecl
?調用者 (Caller)
?從右往左?
?因為是調用者清理Stack,因此允許變參 (如printf)
?
stdcall
?被調用者 (Callee)
?從右往左?
?一般在Windows API和COM中使用,也是.NET和Native代碼調用的缺省Calling Convention。
順便提一下,Windows中API的Calling Convention所使用到的WINAPI宏在PC機上是__stdcall,而在WinCE上則是__cdecl,并非一成不變。
?
Thiscall (Microsoft)
?被調用者 (Callee)
?從右往左
?基本上等價stdcall, 除了this指針用ECX傳遞
?
Fastcall (Microsoft)
?被調用者 (Callee)
?從右往左
?和Stdcall類似,但是會選擇兩個從左往右數最先可以放在寄存器里面的參數放在ECX和EDX中
?


大家可能對清理堆棧,參數壓棧順序這些概念不是很清楚,在這里我會通過一個具體的例子來說明。下面列出了一小段程序和它的匯編代碼:

view plaincopy to clipboardprint?
#include <stdio.h>??
int __stdcall Test(int a, char b, short c)??
{??
??? printf("%d %c %d", a, b, c);??
??? return a+c;??
}??
void main()??
{??
??? int a = Test(5, 'a', 10);??
}?
#include <stdio.h>
int __stdcall Test(int a, char b, short c)
{
??? printf("%d %c %d", a, b, c);
??? return a+c;
}
void main()
{
??? int a = Test(5, 'a', 10);
}

在main中對Test的調用對應了如下的匯編代碼:

view plaincopy to clipboardprint?
00412004 6a0a??????????? push??? 0Ah??
00412006 6a61??????????? push??? 61h??
00412008 6a05??????????? push??? 5??
0041200a e800f0feff????? call??? test!ILT+10(?TestYGHHDFZ) (0040100f)??
0041200f 8945fc????????? mov???? dword ptr [ebp-4],eax ss:002b:001?
00412004 6a0a??????????? push??? 0Ah
00412006 6a61??????????? push??? 61h
00412008 6a05??????????? push??? 5
0041200a e800f0feff????? call??? test!ILT+10(?TestYGHHDFZ) (0040100f)
0041200f 8945fc????????? mov???? dword ptr [ebp-4],eax ss:002b:001

?

在這個例子中,我們可以觀察到如下信息:

1. 壓棧順序:棧中首先壓入的是0A(十進制中的10),是最后一個參數,其次是’a’,最后是5,所以說__stdcall的壓棧順序是從右向左。

2. 返回值存放在eax中:在call指令之后,把eax的值存入到[ebp-4]中,對應了c++代碼中對a的賦值,可見eax是返回值的存放之所。

3. 被調用函數清理棧:在call指令和mov指令沒有額外的其他指令,可見之前放到棧里的參數,都已經被函數Test清理了(Test的最后一條指令是ret 0c),把棧的指針調整了三個變量的位置。

4. 函數更名:細心的讀者會發現call指令后面跟的是如同亂碼般的test!ILT+10(?TestYGHHDFZ),這是編譯器做的手腳(name mangling),不同的調用規范下,編譯器會按照不同的規則對函數進行更名。我不想細究的原因在于:一方便,函數更名的規則本身就在變化,我目前使用的編譯器,會按照以前__thiscall的規則來更名__stdcall的函數。另一方面,許多debuger比如windbg,會自動的把命名調整回來。


如何指定調用約定

通常,我們真正需要考慮到調用約定的場景,是對一些外部類庫的使用。舉例來說,如果我們要調用的函數由另外一個類庫提供,那么,我們需要根據這個函數所聲明的調用約定來使用這個函數。也就是說,我們要告訴編譯器,請按照這個調用約定,生成相關的代碼,來使用那個來自于類庫的函數。對于MSVC的編譯器來說,有下面的這些開關:

編譯器開關
?調用規范
?
/Gd
?__cdecl
?
/Gr
?__fastcall
?
/Gz
?__stdcall
?

其中/Gz是c++的默認選項。

?

另外一個例子是,提供給別人的回調函數,需要根據調用者的要求,聲明調用約定,舉一個例子來說,在windows中開始一個新的線程。

這時候,可以在函數聲明的語句中,在返回值類型后面插入相關的調用規范,如前面的例子中所示。

view plaincopy to clipboardprint?
int __stdcall Test(int a, char b, short c)?
int __stdcall Test(int a, char b, short c)?


如果你是一個.NET用戶(終于,我可以談及一些我們的產品了),那么你在P/Invoke的時候仍然需要調用約定。DllImportAttibute中,有一個字段CallingConvention,就是對應這個需求生成的。

?

view plaincopy to clipboardprint?
[DllImport("ole32.dll", EntryPoint="CoCreateInstance", CallingConvention=CallingConvention.StdCall)]??
public static extern? int CoCreateInstance(ref Guid rclsid, IntPtr pUnkOuter, uint dwClsContext, ref Guid riid, ref System.IntPtr ppv) ;?
[DllImport("ole32.dll", EntryPoint="CoCreateInstance", CallingConvention=CallingConvention.StdCall)]
public static extern? int CoCreateInstance(ref Guid rclsid, IntPtr pUnkOuter, uint dwClsContext, ref Guid riid, ref System.IntPtr ppv) ;?


調用約定的用武之地

看了上面的介紹之后,你可能會想,我們只需要根據文檔上聲明的調用約定,在自己的代碼中指定相應的調用約定就可以了。那么,了解清楚每一個調用約定的具體內容對我們有什么幫助呢?

我認為,了解調用約定首先可以幫助我們深入了解函數調用部分的匯編代碼的原理。有很多時候,錯誤的使用了調用規范是一個很難察覺的bug。

其次,了解調用約定在只擁有公共符號(public symbol)進行調試的時候對我們幫助很大,公共符號通常只能讓我們觀察到調用棧信息。那么了解了調用約定之后,我們至少能利用調用棧找到函數參數,函數返回值等信息。

?

總結以及下期預告

今天我花費了蠻多筆墨講解調用規范,對于這一系列的主題“調用棧”來說,調用規范是一個息息相關的概念。下一次,我將通過一個windbg調試腳本來觀察遵循stdcall的調用棧,作為這一系列的收尾,敬請期待。


本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/mountaintaiII/archive/2009/03/12/3985729.aspx

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            精品不卡一区| 国产精品盗摄一区二区三区| 国产精品久久77777| 一本色道**综合亚洲精品蜜桃冫| 欧美一级播放| 国产精品永久免费在线| 亚洲综合国产激情另类一区| 一本色道久久综合狠狠躁篇怎么玩| 欧美精品一区二区三区高清aⅴ| 99精品视频免费全部在线| 99视频国产精品免费观看| 亚洲制服丝袜在线| 国模精品一区二区三区| 亚洲成人在线免费| 久久久综合激的五月天| 狠狠色综合网| 最新成人av在线| 国产精品亚洲激情| 亚洲第一免费播放区| 欧美日韩精品欧美日韩精品| 欧美一区二区三区免费观看| 久久久久99精品国产片| 亚洲免费av观看| 午夜精品福利一区二区三区av| 国际精品欧美精品| 亚洲精选在线| 在线观看精品| 亚洲一区二区高清视频| 亚洲国产精品t66y| 亚洲性视频网址| 亚洲三级视频| 亚洲欧美卡通另类91av| 最新国产乱人伦偷精品免费网站| 99香蕉国产精品偷在线观看| 国际精品欧美精品| 一区二区三区高清在线| 亚洲国产欧洲综合997久久| 一区二区三区视频在线播放| 亚洲黄色av| 午夜欧美大片免费观看| 一区二区av| 麻豆精品视频在线| 久久黄金**| 欧美视频在线观看一区| 欧美激情aaaa| 黄色成人精品网站| 亚洲欧美日韩综合| 亚洲一区二区三区色| 免费在线成人| 欧美成人嫩草网站| 狠狠噜噜久久| 欧美怡红院视频一区二区三区| 亚洲图片在线| 欧美日韩在线另类| 亚洲激情偷拍| 亚洲国产精品久久久| 午夜在线电影亚洲一区| 午夜在线成人av| 国产精品女主播| 一本久道久久综合婷婷鲸鱼| 亚洲人成啪啪网站| 欧美大片免费观看| 欧美激情一区三区| 亚洲国产另类 国产精品国产免费| 欧美与欧洲交xxxx免费观看 | 欧美xart系列高清| 国产视频在线观看一区二区三区| 一区二区三区视频在线看| 99视频精品在线| 欧美日本一道本| 亚洲精品午夜| 亚洲欧美日韩在线一区| 欧美亚洲成人网| 亚洲欧美日本日韩| 久久aⅴ国产紧身牛仔裤| 国产欧美一区二区在线观看| 亚洲欧美日韩国产综合| 久久精品国产99| 精品不卡一区| 欧美人交a欧美精品| 日韩一级在线| 欧美一区二区三区播放老司机| 国产欧美日韩伦理| 久久久久天天天天| 欧美黄色日本| 亚洲综合日韩中文字幕v在线| 欧美视频在线观看 亚洲欧| 亚洲一区在线视频| 久久久久久久久岛国免费| 伊人久久综合97精品| 免播放器亚洲| 中国日韩欧美久久久久久久久| 欧美一区二区三区免费看| 国语自产偷拍精品视频偷| 美女诱惑一区| 亚洲一级影院| 欧美激情第8页| 新狼窝色av性久久久久久| 国产在线日韩| 欧美日韩一区二区在线观看 | 久久尤物视频| 亚洲午夜激情在线| 韩国成人福利片在线播放| 欧美激情一区二区三区在线视频观看 | 欧美xxx在线观看| 一区二区三区四区精品| 国产亚洲a∨片在线观看| 久久青草欧美一区二区三区| 亚洲裸体视频| 美女精品网站| 亚洲欧美日韩人成在线播放| 国内不卡一区二区三区| 欧美成人免费在线| 性欧美超级视频| 一本久久综合| 亚洲国产裸拍裸体视频在线观看乱了中文 | 亚洲另类视频| 狠狠色综合色综合网络| 国产精品日韩精品| 欧美日韩国语| 欧美国产极速在线| 久久精品一区二区三区不卡牛牛| 一区二区精品在线| 亚洲精品久久嫩草网站秘色| 久久人人爽爽爽人久久久| 亚洲欧美激情视频| 一区二区三区免费看| 欧美视频一区| 亚洲伦理自拍| 亚洲欧洲美洲综合色网| 久久视频在线视频| 久久爱www久久做| 午夜欧美不卡精品aaaaa| 亚洲乱码国产乱码精品精| 在线看片一区| 1000部国产精品成人观看| 国产手机视频一区二区| 国产精品免费看| 国产精品啊啊啊| 国产精品蜜臀在线观看| 国产精品国产一区二区| 欧美日韩国产综合视频在线观看中文| 久久夜色精品国产欧美乱极品| 欧美中文字幕在线播放| 欧美一级黄色网| 午夜一区二区三区在线观看| 亚洲一区二区三区四区五区午夜| 一本色道久久| 亚洲在线一区| 午夜欧美不卡精品aaaaa| 欧美亚洲一级| 久久午夜影视| 欧美精品v日韩精品v国产精品 | 亚洲欧美在线磁力| 欧美亚洲自偷自偷| 久久精选视频| 欧美劲爆第一页| 欧美午夜电影在线| 国产伦精品一区二区三区高清版| 国产日产欧产精品推荐色| 国产一区二区三区在线观看精品 | 久久精品免费观看| 欧美大片国产精品| 欧美亚男人的天堂| 国产午夜精品久久久久久免费视| 国内精品免费在线观看| 亚洲黑丝在线| 亚洲欧美日韩一区二区在线| 久久精品国产亚洲aⅴ| 欧美fxxxxxx另类| 亚洲精品一区二区三区在线观看| 中日韩午夜理伦电影免费| 香蕉久久夜色精品国产| 蜜桃av一区二区三区| 欧美色欧美亚洲高清在线视频| 国产欧美一区二区精品仙草咪 | 夜夜精品视频一区二区| 亚洲欧美日韩一区| 免费欧美高清视频| 99精品99| 久久久午夜视频| 国产精品jizz在线观看美国| 国产一区二区三区久久 | 欧美专区在线观看一区| 欧美成人免费在线视频| 亚洲夜晚福利在线观看| 美女主播一区| 国产视频综合在线| 亚洲婷婷综合久久一本伊一区| 久久亚洲国产成人| 亚洲性图久久| 欧美精品亚洲精品| 一区二区三区在线观看视频| 免费看av成人| 91久久视频| 亚洲午夜久久久久久尤物| 免费亚洲电影在线| 欧美亚洲日本网站| 国产精品福利av| 在线亚洲电影|