源码
//dll.h
#pragma once
void func1();
__declspec(dllimport) void func2();
#include "dll.h"
#pragma comment(lib,"../Debug/Dll.lib")
int main()
{
func1(); //输出"func1"
func2(); //输出"func2"
return 0;
}
OD调试
func1
011F16AE E8 CEFAFFFF call 011F1181
011F16B3 8BF4 mov esi, esp
011F16B5 FF15 00901F01 call dword ptr [<&Dll.#2>] ; Dll.func2
011F1181 /E9 58050000 jmp func1 ; jmp 到 offset Dll.func1
011F1186 |E9 59050000 jmp func2 ; jmp 到 offset Dll.func2
offset Dll.func1:
011F16DE >- FF25 04901F01 jmp dword ptr [<&Dll.func1>] ; Dll.func1
011F16E4 >- FF25 00901F01 jmp dword ptr [<&Dll.#2>] ; Dll.func2
dll.func1:
0FE41140 > /E9 1B0A0000 jmp func1
0FE41145 > |E9 66120000 jmp func2
func1:
0FE41B60 > 55 push ebp
0FE41B61 8BEC mov ebp, esp
...
func2
011F16AE E8 CEFAFFFF call 011F1181
011F16B3 8BF4 mov esi, esp
011F16B5 FF15 00901F01 call dword ptr [<&Dll.#2>] ; Dll.func2
0FE41140 > /E9 1B0A0000 jmp func1
0FE41145 > |E9 66120000 jmp func2
func2没有跳转到offset Dll.func1,而是直接跳转到dll.func2。
原理
输入函数有高效和低效两种调用情况。
默认对输入api使用低效形式。
解释:编译器无法区分输入函数调用和普通函数调用,对每个函数调用,编译器使用call指令。call后的地址由链接器填充,不是函数指针,而是代码中jmp指令的实际地址。
由于使用了额外的jmp,所以执行时间长一些。
要想优化,可以在声明处加上__declspec(dllimport)
,告诉编译器这个函数来自dll。所以主函数的func2是这样的:
011F16B5 FF15 00901F01 call dword ptr [<&Dll.#2>] ; Dll.func2
这种jmp有点像c++的虚函数表。