我在看这个:使用 __declspec(dllimport) 导入函数调用 我不明白为什么真的需要 __declspec(dllimport)?为什么链接器不能在链接阶段将调用 func1 修补为调用 DWORD PTR __imp_func1(IAT 表地址)从而避免在源中单独声明的需要?
补丁说明空间不足可能是一个原因,但不是主要原因。即使有足够的空间,链接器仍然不会修补它以获得更好的性能。原因是链接器不做或修改编译器后端生成的代码,它只是用重定位信息修补地方。下面的例子不是很好的编码风格,但清楚地解释了问题。为
bar()
生成的指令是 call dword[_imp__bar]
,它有 6 个字节长,即使链接器知道 bar()
在最终映像中定义,它也不会用只需要 5 个字节的 call OffsetOfBar
修补它字节。延迟代码生成到链接时间(将 /GL 传递给 cl.exe)可以解决这个问题,因为链接器将在链接期间调用编译器后端,并且后端将生成优化的 call
指令,因为它知道 call
目标在同一个模块中。
// main.c
// compile it with 'cl.exe /Zi /O2 main.c bar.c'
__declspec(dllimport) void bar();
int main() {
bar();
}
// bar.c
void __declspec(noinline) bar() {}
引用 Raymond Chen:call func 是 5 个字节长,但 call dword ptr [_imp_func] 是 6 个字节长。补丁不合适。一个字节太大了。在每条调用指令之后添加一个 nop(以防万一它恰好被导入)听起来太过分了。
在 VS 2022 中(可能很久很久以前),编译器(不是链接器)确实能够在有或没有
__declspec(dllimport)
的情况下优化 thunk。今天它完全是多余的,希望文档有一天会反映出来。