一发不可收拾的学习

来源:互联网 时间:1970-01-01

昨天还在感叹自己面对Windows开发就像一个傻瓜,今天随便找了个突破口,期待能够将自己掌握的知识联系起来,结果就是一发不可收拾,越看越迷糊,越想越凌乱。不管怎么样,先将今天看到的新面孔记个流水帐吧。
首先,突破口就是WinMain函数的修饰符WINAPI。从WINDEF.H这个头文件中,我得知WINAPI实质上就是__stdcall。那么什么是__stdcall呢?
__stdcall属于Microsoft定义的函数修饰符,除了她之外,还有__fastcall、__cdecl和thiscall定义了函数被调用的方式,其中,thiscall不是关键字。以前关注的都是函数如何去定义,而函数调用都是很平常的事情,从来都没有去思考在函数调用的过程中,发生了什么事情。以下文字摘自MSDN,对于函数调用的过程中发生的事情做了描述:
All arguments are widened to 32 bits when they are passed. Return values are also widened to 32 bits and returned in the EAX register, except for 8-byte structures, which are returned in the EDX:EAX register pair. Larger structures are returned in the EAX register as pointers to hidden return structures. Parameters are pushed onto the stack from right to left.
The compiler generates prolog and epilog code to save and restore the ESI, EDI, EBX, and EBP registers, if they are used in the function.
译文:所有的实参在传递的时候都会被扩展为32位,返回值也是如此,并且会存储至EAX寄存器中(天啊,寄存器都来了)。如果返回的是8字节的结构体,那么她就会被存在EDX:EAX寄存器对中。如果是体积更大的结构体返回的话,那么EAX存放的将是一个指向结构体所在地址的指针。型参按照从右至左的顺序被推入栈中。
在ESI、EDI、EBX和EBP寄存器当中,如果她们在程序中被使用到的话,编译器会生成序言(prolog)和结语(epilog)代码来存储和恢复这些被使用的寄存器的内容。

再次回到刚刚提到的几种定义函数调用的修饰符上来。大家先看如下的函数定义和调用(以下代码和图片均来自MSDN):

void calltype MyFunc( char c, short s, int i, double f );

void MyFunc( char c, short s, int i, double f )
{

}

MyFunc ('x', 12, 8192, 2.7183);

对于以上的函数定义,calltype用__fastcall、__cdecl等替代。那么,在函数调用的过程中,不同的修饰符下的内存结构如下图:

calltype = __cdecl


calltype = __stdcall 和 thiscall


calltype = __fastcall

最后,由prolog和epilog又引出了Naked Function Calls的概念。所谓Nake Function Calls就是由自己来写prolog和epilog,而不是由编译器来生成。而这里面还有很多的学问,譬如Nake Function Calls的限制,prolog和epilog应该如何写等等。以下就先给出一个Naked Function的例子吧(来自MSDN):

__declspec(naked) int __fastcall power(int i, int j)
{
/**//* calculates i^j, assumes that j >= 0 */

/**//* prolog */
__asm {
push ebp
mov ebp, esp
sub esp, __LOCAL_SIZE
// store ECX and EDX into stack locations allocated for i and j
mov i, ecx
mov j, edx
}

{
int k=1; // return value
while (j-- > 0) k *= i;
__asm { mov eax, k };
}

/**//* epilog */
__asm
{
mov esp, ebp
pop ebp
ret
}
}

int main()
{
}

噢,我已经有点眩晕了。一天吃不成胖子,脑袋也装不下那么多的东西,就到这里吧。还是不要冷落WinMain,毕竟这才是主体,__stdcall只是一个修饰符而已啊。Oh, My God.



相关阅读:
Top