调用DLL函数涉及链接器生成存根,除非函数被声明为__declspec(dllimport)
,在这种情况下可以绕过存根以支持直接调用导入表的间接调用。效率稍高,例如
__declspec(dllimport) void ExitProcess(int);
ExitProcess(0);
产生
call qword ptr [__imp_ExitProcess]
其中__imp_ExitProcess
解析为可执行文件中导入表中的某个位置。
我正在努力弄清楚__imp_ExitProcess
到底是如何解决的。它确实在kernel32.lib中作为符号出现,但该符号具有存储类IMAGE_SYM_CLASS_EXTERNAL
,段号为零且值为零,这相当于只说“这将在其他地方定义”而实际上并没有定义它
__imp_
前缀是否具有特殊含义,即链接器是否注意到该前缀并将其作为指令将该符号解析为其名称已删除该前缀的DLL函数的导入表条目?或者还有其他事情发生了吗?
答案 0 :(得分:4)
链接器为您的程序添加如下函数:
void (*__imp_ExitProcess)(int) = ...;
void ExitProcess(int n)
{
return (*__imp_ExitProcess)(n);
}
其中__imp_ExitProcess指向" real" KERNEL32.DLL中的ExitProcess。
在您的代码中声明:
__declspec(dllimport) void ExitProcess(int);
相当于:
extern void (*__imp_ExitProcess)(int);
#define ExitProcess (*__imp_ExitProcess)
除了__declspec(dllimport)由编译器处理,而不是由预处理器处理。
答案 1 :(得分:3)
刚刚提出了一个提供数据点的测试。以下程序:
__declspec(dllimport) void ExitProcess(int);
void __imp_ExitProcess(int x) {
}
int main() {
ExitProcess(0);
}
使用Microsoft编译器编译并运行时崩溃(但如果将空函数重命名为其他任何内容,则运行正常);因此,似乎链接器的行为好像__imp_
名称是特殊的,至少在某种意义上说,行为方式的链接器将在Microsoft链接器这样做的所有情况下生成正确的可执行文件,除非我是遗失了什么。
答案 2 :(得分:2)
kernel32.lib
的成员不是普通的目标文件,而是特殊的占位符。来自PE/COFF spec:
传统的导入库,即描述的库 从一个图像导出以供另一个图像使用,通常遵循布局 在第7节“存档(库)文件格式”中描述 区别在于导入库成员包含伪目标文件 而不是真实的,其中每个成员包括该部分 构建导入表所需的贡献 在第6.4节“.idata部分”中描述。链接器生成 在构建导出应用程序时使用此存档。
导入的部分贡献可以从一小组信息中推断出来。链接器可以生成完整的, 将详细信息导入到每个成员的导入库中 图书馆创作时间或只写规范信息 到库中,让以后使用它的应用程序生成 必要的数据。
[...]简短的导入库编写如下:
Archive member header Import header Null-terminated import name string Null-terminated DLL name string
这是足以准确重建会员在使用时的全部内容的信息。
在我的机器上的kernel32.lib
中,第一个和第二个链接器成员(符号列表)中提到了__imp_ExitProcess
,并指向描述导入的特定伪对象:
Archive member name at 8: /
50107C36 time/date Thu Jul 26 01:07:34 2012
uid
gid
0 mode
106CA size
correct header end
2515 public symbols
[...]
3C874 ExitProcess
3C874 __imp_ExitProcess
[...]
Archive member name at 3C874: KERNEL32.dll/
50107639 time/date Thu Jul 26 00:42:01 2012
uid
gid
0 mode
2D size
correct header end
Version : 0
Machine : 8664 (x64)
TimeDateStamp: 50107639 Thu Jul 26 00:42:01 2012
SizeOfData : 00000019
DLL name : KERNEL32.dll
Symbol name : ExitProcess
Type : code
Name type : name
Hint : 371
Name : ExitProcess
因此,正如您所看到的,.lib中的数据明确表示它引用了DLL ExitProcess
中的导入名称KERNEL32.dll
。链接器可以使用它在导入部分中构建必要的元数据。
现在,上面仅讨论了如何解析__imp_ExitProcess
符号。我不是100%肯定,但我认为如果符号(例如ExitProcess
)已经解析为这样的导入存根,并且不以__imp_
开头,然后链接器必须生成跳转存根(用于代码符号),或者对IAT插槽的间接访问(用于数据访问)。