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

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>
            一本色道久久88综合亚洲精品ⅰ| 最新国产精品拍自在线播放| 亚洲欧美在线网| 亚洲精品精选| 欧美在线视频一区二区| 亚洲欧美日本另类| 亚洲欧美激情视频| 欧美一级专区| 久久免费黄色| 亚洲国产精品成人一区二区 | 亚洲欧美另类久久久精品2019| 亚洲国产欧美日韩精品| 亚洲片在线观看| 中文在线不卡视频| 久久精精品视频| 免费看黄裸体一级大秀欧美| 欧美激情视频网站| 一本在线高清不卡dvd | 嫩草成人www欧美| 欧美mv日韩mv亚洲| 欧美视频中文字幕在线| 国产亚洲aⅴaaaaaa毛片| 亚洲国产成人精品久久| av成人黄色| 久久成人免费日本黄色| 免费影视亚洲| 亚洲性视频网址| 免费短视频成人日韩| 欧美性色综合| 亚洲国产精品第一区二区| 日韩视频在线观看| 欧美在线一二三区| 国产午夜精品视频免费不卡69堂| 欧美.日韩.国产.一区.二区| 亚洲精品男同| 亚洲午夜三级在线| 久久亚洲精品一区| 国产精品入口| 99视频精品全国免费| 久久亚洲精选| 一区二区三区色| 久久夜色精品国产| 国产区欧美区日韩区| 99精品免费| 美女脱光内衣内裤视频久久影院| 99精品久久久| 欧美大片在线看| 狠狠爱成人网| 欧美一激情一区二区三区| 亚洲人成毛片在线播放| 久久久亚洲国产天美传媒修理工| 国产精品久久久久久福利一牛影视| 亚洲国产女人aaa毛片在线| 久久精品视频在线| 亚洲一区视频在线| 欧美日韩情趣电影| 99精品欧美一区| 亚洲国产精品黑人久久久| 久久久久久69| 狠狠v欧美v日韩v亚洲ⅴ| 欧美亚洲网站| 亚洲视频一区二区在线观看| 欧美精品福利在线| 亚洲看片免费| 91久久久久久国产精品| 欧美aⅴ99久久黑人专区| 在线观看欧美一区| 欧美成人一区二区| 欧美成人日本| 一二三区精品| 一本一本a久久| 国产精品久久久久77777| 亚洲影视九九影院在线观看| 亚洲在线视频观看| 国产一区二区久久久| 久久精品人人| 久久久亚洲综合| 亚洲黑丝一区二区| 亚洲久久一区| 国产精品一区二区你懂的| 久久国产综合精品| 久久欧美中文字幕| 日韩一区二区电影网| 亚洲视频欧美在线| 国产亚洲成年网址在线观看| 另类av一区二区| 欧美不卡一卡二卡免费版| 亚洲三级免费| 在线综合亚洲欧美在线视频| 国产亚洲精品久久久久动| 乱码第一页成人| 欧美激情一区二区三区全黄| 久久综合中文| 亚洲日韩欧美视频一区| 欧美日韩精品免费观看| 亚洲免费视频中文字幕| 午夜免费在线观看精品视频| 亚洲福利视频一区二区| 亚洲欧洲一区二区三区| 国产精品入口福利| 欧美黄免费看| 国产欧美精品在线| 欧美福利视频在线| 国产精品视频精品| 亚洲激情第一区| 国产午夜精品美女视频明星a级| 欧美高清视频www夜色资源网| 欧美视频免费看| 六月天综合网| 国产精品看片你懂得| 欧美二区不卡| 国产欧美日韩综合精品二区| 亚洲电影一级黄| 国产欧美综合一区二区三区| 亚洲人成在线播放网站岛国| 国产综合网站| 亚洲最新合集| 亚洲欧洲精品一区二区| 亚洲欧美视频在线观看| 亚洲黄色毛片| 香蕉国产精品偷在线观看不卡| 日韩一区二区久久| 久久三级视频| 久久激情视频免费观看| 欧美午夜精品| 亚洲国产精品一区二区尤物区| 国产一区二区日韩精品| 亚洲午夜一级| 亚洲视频你懂的| 欧美韩日一区二区三区| 欧美a级一区| 国内久久婷婷综合| 午夜精品久久久久久久男人的天堂 | 亚洲性av在线| 欧美日韩国产成人在线| 亚洲高清视频一区| 亚洲高清不卡一区| 久久精品水蜜桃av综合天堂| 久久精品亚洲精品国产欧美kt∨| 国产精品久久波多野结衣| 亚洲麻豆av| 中文欧美在线视频| 欧美日韩中文精品| 一区二区三区国产精华| 亚洲一级电影| 国产精品免费在线 | 99综合精品| 99一区二区| 欧美性大战久久久久久久蜜臀| 亚洲精品中文字幕在线观看| 一本久道久久综合中文字幕| 欧美精品在欧美一区二区少妇| 亚洲激情在线播放| 一区二区激情| 国产精品美女久久久久久2018| 亚洲蜜桃精久久久久久久| 亚洲成人在线网| 亚洲高清在线| 欧美国产一区在线| 一本色道久久综合亚洲精品不卡| 亚洲欧美激情诱惑| 国产一区二区丝袜高跟鞋图片| 久久精品一区二区三区不卡| 欧美chengren| 一本不卡影院| 国产精品中文在线| 久久免费黄色| 亚洲乱码国产乱码精品精| 亚洲伊人伊色伊影伊综合网| 国产日韩在线视频| 蜜桃伊人久久| 一区二区三区福利| 久久久久久一区二区| 亚洲精选一区| 国产精品麻豆va在线播放| 久久经典综合| 亚洲精品免费一区二区三区| 亚洲免费在线电影| 在线免费观看日韩欧美| 欧美日韩亚洲不卡| 久久久www免费人成黑人精品| 亚洲精品国产精品国自产在线| 午夜精品亚洲| 亚洲精品看片| 国产一区视频在线观看免费| 欧美精品福利在线| 久久精品国产亚洲5555| 一区二区冒白浆视频| 牛牛精品成人免费视频| 午夜精品久久久久久久久久久| 亚洲高清二区| 国产一区二区高清视频| 欧美色图五月天| 免费h精品视频在线播放| 亚洲欧美国产视频| 亚洲每日在线| 亚洲高清免费视频| 老鸭窝毛片一区二区三区| 亚洲欧美激情一区| 夜夜嗨av一区二区三区免费区|