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

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,其功能是把棧中的返回地址取出,并且跳轉到那條指令。

?

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

?

高級語言的出現,把這個問題隱藏了起來。我們在編寫一般的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>
            欧美成人国产| 国产精品美女www爽爽爽| 亚洲高清av在线| 亚洲欧美在线网| 午夜精品视频网站| 欧美午夜一区二区福利视频| 91久久久在线| 亚洲伦理在线| 欧美日韩视频在线观看一区二区三区| 牛夜精品久久久久久久99黑人 | av成人免费观看| 欧美精选一区| 亚洲特级片在线| 狂野欧美一区| 亚洲视频欧洲视频| 国产欧美一区二区三区久久人妖| 欧美综合激情网| 亚洲精品国产精品久久清纯直播 | 久久精品亚洲国产奇米99| 国产精品一级在线| 欧美激情91| 亚洲永久视频| 亚洲国产精品激情在线观看| 亚洲欧美国产va在线影院| 欧美一级久久久| 欧美一区二区三区另类| 欧美一区二区三区精品电影| 亚洲国产二区| 欧美一级免费视频| 亚洲天堂黄色| 亚洲福利视频专区| 亚洲女人天堂av| 亚洲午夜一级| 亚洲美女色禁图| 久久美女性网| 美女露胸一区二区三区| 美女图片一区二区| 免费不卡视频| 亚洲国产小视频| 欧美精品三级| 激情丁香综合| 亚洲高清在线观看一区| 亚洲伊人观看| 午夜一区在线| 久久夜色精品国产亚洲aⅴ| 99re这里只有精品6| 亚洲欧洲日本专区| 亚洲国产一区二区在线| 久久久久国产成人精品亚洲午夜| 亚洲午夜久久久| 亚洲国产欧美在线人成| 乱中年女人伦av一区二区| 欧美成人免费全部| 久久精品国产清高在天天线| 另类激情亚洲| 国产精品久99| 在线观看三级视频欧美| 亚洲性色视频| 一区二区三区日韩欧美| 亚洲国产欧美不卡在线观看| 一区二区三区日韩精品视频| 欧美日韩精品福利| 精品福利av| 亚洲天堂黄色| 香蕉国产精品偷在线观看不卡| 美女精品在线| 久久成年人视频| 在线观看一区欧美| 亚洲在线电影| 亚洲欧洲精品一区| 久久福利影视| 亚洲欧洲精品一区二区三区| 亚洲在线观看视频网站| 女主播福利一区| 欧美成黄导航| 国模私拍视频一区| 亚洲第一中文字幕在线观看| 欧美一区二区女人| 欧美三级日本三级少妇99| 欧美一区二区免费| 亚洲免费播放| 猫咪成人在线观看| 亚洲一区激情| 亚洲精品久久视频| 国产精品系列在线| 亚洲视频免费| 亚洲精品影视在线观看| 国产欧美在线| 欧美亚洲色图校园春色| 亚洲欧美国产高清va在线播| 亚洲综合色视频| 欧美午夜视频在线| 久久中文久久字幕| 欧美专区中文字幕| 国产女主播一区| 欧美激情一区二区在线| 久久嫩草精品久久久精品一| 国产欧美va欧美va香蕉在| 免费成人黄色片| 久久先锋资源| 国产精品国产a| 亚洲成人在线视频播放| 老色批av在线精品| 欧美一区二区三区四区高清 | 噜噜噜久久亚洲精品国产品小说| 在线亚洲电影| 久久国产精品久久精品国产| 在线亚洲一区| 亚洲乱码精品一二三四区日韩在线| 亚洲人午夜精品| 欧美性片在线观看| 亚洲女同同性videoxma| 亚洲欧美日韩视频二区| 性欧美暴力猛交另类hd| 国产一区二区三区在线观看网站| 欧美一区二区三区日韩视频| 欧美日本国产一区| 亚洲网站在线| 欧美精品久久久久久久| 亚洲调教视频在线观看| 亚洲性xxxx| 中文一区字幕| 欧美在线看片| 亚洲人久久久| 亚洲一区二区三区免费在线观看| 美女视频网站黄色亚洲| 一本色道久久综合亚洲精品不 | 欧美freesex8一10精品| 亚洲免费一区二区| 香蕉亚洲视频| 久久综合一区二区| 欧美国产欧美亚洲国产日韩mv天天看完整 | 亚洲日本va午夜在线影院| 欧美日本一区二区三区| 亚洲国产另类精品专区| 欧美精品在欧美一区二区少妇| 在线一区二区三区四区| 欧美日韩国产电影| 亚洲制服少妇| 国产麻豆综合| 亚洲日本无吗高清不卡| 国产精品亚洲成人| 午夜精品剧场| 一本高清dvd不卡在线观看| 久久国产福利国产秒拍| 中文一区二区在线观看| 久久久久国产精品麻豆ai换脸| 一区二区欧美日韩| 欧美专区中文字幕| 一区二区三区免费网站| 国产精品99久久久久久白浆小说 | 国产一区二区三区自拍| 亚洲电影免费观看高清完整版在线观看 | 久久av一区二区三区| 免费不卡视频| 性做久久久久久久久| 欧美另类一区二区三区| 欧美1区2区| 国产精品成人一区二区网站软件 | 亚洲精品久久7777| 欧美激情综合亚洲一二区| 久久久久久久激情视频| 久久大综合网| 新狼窝色av性久久久久久| 国产一区二区主播在线| 亚洲无亚洲人成网站77777| 亚洲欧洲另类| 国产精品高清网站| 亚洲国内欧美| 午夜精品在线观看| 欧美日韩另类在线| 模特精品裸拍一区| 亚洲免费视频在线观看| 欧美了一区在线观看| 欧美一区二区三区在线观看视频| 99视频一区二区| 母乳一区在线观看| 亚洲欧美不卡| 亚洲欧美精品伊人久久| 在线不卡中文字幕播放| 午夜精品久久久久久久99黑人| 欧美电影资源| 久久国产精品99精品国产| 性欧美8khd高清极品| 亚洲精品在线一区二区| 蜜桃av噜噜一区| 亚洲男女自偷自拍图片另类| 欧美一级精品大片| 国产日韩欧美另类| 久久精品卡一| 欧美国内亚洲| 欧美aa国产视频| 免费成人小视频| 欧美日韩在线不卡| 麻豆国产精品777777在线| 欧美国产一区二区在线观看| 欧美一区二区三区播放老司机| 国产精品一区二区欧美| 欧美精品v日韩精品v国产精品| 99香蕉国产精品偷在线观看|