__imp_符号的语义

调用DLL函数涉及到链接器生成一个存根, 除非该函数被声明为__declspec(dllimport)在这种情况下,存根可以被绕过,直接间接调用直接导入更有效的导入表,例如

 __declspec(dllimport) void ExitProcess(int); ExitProcess(0); 

生成

 call qword ptr [__imp_ExitProcess] 

其中__imp_ExitProcessparsing为可执行文件中导入表中的位置。

我想弄清楚如何解决__imp_ExitProcess 。 它确实发生在kernel32.lib中的一个符号,但该符号具有存储类IMAGE_SYM_CLASS_EXTERNAL ,节号为零,值为零,相当于只是说“这将在别的地方定义”而没有实际定义它。

__imp_前缀是否有特殊含义,即链接器是否注意到前缀并将其作为一个指令将符号parsing为DLL名称已被删除的DLL函数的导入表项? 或者还有其他的事情呢?

kernel32.lib成员不是普通的对象文件,而是特殊的占位符。 从PE / COFF规格 :

传统的导入库,即描述从另一个映像导出以供另一个映像使用的库,通常遵循第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 KERNEL32.dll的导入名称ExitProcess 。 链接器可以使用它在导入部分中创建必要的元数据。

现在,上面只讨论了__imp_ExitProcess符号是如何解析的。 我不是100%确定的,但是我认为如果一个符号(例如ExitProcess )已经被解析为这样一个导入存根,并且它__imp_开始,那么链接器必须生成一个跳转存根(用于代码符号),或者间接访问(用于数据访问)IAT插槽。

链接器添加到您的程序的功能,如:

 void (*__imp_ExitProcess)(int) = ...; void ExitProcess(int n) { return (*__imp_ExitProcess)(n); } 

其中__imp_ExitProcess指向KERNEL32.DLL中的“真实”ExitProcess。

在你的代码中声明这个:

 __declspec(dllimport) void ExitProcess(int); 

相当于:

 extern void (*__imp_ExitProcess)(int); #define ExitProcess (*__imp_ExitProcess) 

除了__declspec(dllimport)由编译器处理,而不是由预处理器处理。

刚刚提出了一个测试,给出了一个数据点。 以下程序:

 __declspec(dllimport) void ExitProcess(int); void __imp_ExitProcess(int x) { } int main() { ExitProcess(0); } 

使用Microsoft编译器编译并运行时崩溃(但运行没问题,如果空函数重命名为其他任何东西); 因此,似乎链接器的行为就像__imp_名称是特殊的,至少在这种意义上,链接器的行为将在Microsoft链接器所有情况下生成正确的可执行文件,除非我错过了某些东西。