免责声明:YMMV。这里描述的所有行为都严重依赖于实现/版本/平台,我什至不完全相信我在这里陈述的一些事情。尽管如此,我认为这是一个很好的各种想法、方法和见解的集合,其中大多数实际上运行良好。
我描述的过程可以用作为 DLL 生成适当的 implib 的一般方法,而不仅仅是 stdcall 未损坏的;尽管如此,它们仍然是最难破解的坚果,所以我认为它们应该得到一些特殊的待遇
我们首先只有一个“外星人”(没有源代码、没有头文件等)DLL 文件。虽然有一些专用的 GUI 工具可以做到这一点(例如this),但我假设该任务应该在脚本中完成,所以没有任何 GUI,只有文本 shell 命令。
MinGW 用户注意事项:
如果您使用的是新的 MinGW (一些古老的版本不支持它,但所有最近的版本都支持),您通常根本不需要创建导入 LIB 就可以与 DLL 链接!你只需要:
a) 声明您的方法的原型(例如在.h
文件中),很可能没有 __declspec(dllimport)
- MinGW 对它们使用奇怪的装饰/修饰,并且(至少在我的设置中)它们不会链接,因为__imp_
链接器需要前缀而不是常规的__imp__
,
b)发布您的编译/链接附加-LDllDir -lDllNameWithoutDllExtension --enable-stdcall-fixup
,例如
g++ example.cpp -L. -lkernel32 --enable-stdcallfixup
这就是诀窍;该应用程序已正确链接到您的 DLL,即使它是未修饰的 stdcall 。
然而,在某些情况下,您将使用另一个编译器,或者出于某种原因(重定向、避免名称冲突等)只需要 LIB 本身。最简单的方法是只创建一个存根 DLL,即为您拥有的每个函数原型创建一个空函数体(请注意,虽然void
函数可以具有简单的{}
主体,但其他函数必须至少具有{ return 0; }
或等效,否则会引发错误),检查它是否有__stdcall
,将它们全部包装起来extern "C" { /* your prototypes here */ }
,然后将其编译为 DLL,将带有未修饰名称的 DEF 作为导出传递(有关1.
如何执行此操作的简单方法,请参见下文) - 丢弃生成的 DLL,保留 LIB,你是完毕。这可以通过脚本轻松完成(例如,通过替换;
为{}
ignore 'no value returned in non-void function' error
并可能通过设置或解析错误日志进行编译以添加return 0;
到有问题的行上)。可悲的是,这种方法需要您拥有.h
文件(或根据已知的 DLL 接口生成它),所以有时不可能这样做。
如果您没有标头,则必须生成所需的所有中间文件。该过程可以分为3个部分:
- 从 DLL 生成股票 DEF 文件,
- 定制 DEF 以使其可以从中生成适当的 LIB(取决于您的运气,您可能可以跳过此步骤),
- 从最终的 DEF 生成 LIB 文件,并确保 LIB 具有正确的损坏/未损坏/装饰/未装饰状态集。
1
第一项任务可以通过多种方式完成。最简单的方法是使用专用工具 - 例如pexports
,gendef
OSS 和 MinGW 扩展 repo 中都可用(默认情况下,除非您安装所有东西,否则 AFAIR 不会安装在 MinGW 中);使用起来dumpbin /exports
要复杂得多,因为它会在重定向等方面添加注释。还有许多其他工具可以做到这一点,比如expdef(它们通常足够简单,可以直接包含在更大的应用程序中,请参见impdef例如)。请注意,两者dlltool
通常nm
都会在这里失败!
gendef %1.dll
等等。请注意,虽然 DEF必须具有没有_
前缀、没有修饰等的函数名称。如果您正在处理未修饰的 stdcall DLL,则@size
通常需要后缀,见下文。
2
第二个任务有点棘手;需要注意的是,链接器将需要symbol@size
LIB 中的符号才能stdcall
正确链接调用,但您可能会使用自动化工具以完全未修饰的导入结束。有时gendef
可以重新生成该数据(YMMV)——如果是这样,你就可以调用或(见下文)。如果没有,并且您既没有原型,也没有可以从中重新生成原型的文档,也没有此特定 DLL 的任何其他类似版本的 LIB,我会说您大多不走运-使用,您必须基本上反汇编每种方法才能知道接受的参数的总大小是多少。有时您可以根据使用它的代码猜测/计算(例如dlltool
implib
stdcall
PUSH
es 在调用之前),但 IMO 不可能正确地自动化它 - 这些值需要手动放入 DEF。
请注意,如果您有一个标头但不想使用存根方法,您可以创建一个调用所有所需导入的虚拟方法 - 链接器将引发错误,这将为您提供包含其中的修饰名称@size
。或者,您可以从size_t
参数等中计算它。
3
只需使用dlltool
or implib
,从固定的 DEF 生成 LIB。例如,一般语法dlltool
是(在批处理文件中使用)
dlltool -d %1.def -D %1.dll -l %1.lib
结果,您获得了 LIB。
最后的警告:
- 您可能会注意到,虽然 MinGW 链接器很乐意接受类似于 的 DEF IMPORTS 别名
methodname1@size=methodname2
,但 Microsoft 将它们视为methodname1@size
,而不是别名;在我所知道的 MSVC 中,没有任何方法可以将修饰名称别名为未修饰名称,
- 声明你的导入
cdecl
永远不是解决方案 - 符号可能匹配,但除了最微不足道的情况外,调用将在所有情况下失败,
- 您可能会注意到(使用
dumpbin /exports /header
)在 MSVC implib 中将生成一个带有 eg 的 LIB Symbol name : _function@size Name type : name Name : _function@size
- 这不是您想要的;正确的结果(通过存根实现)是Symbol name : _function@size Name type : undecorate Name : function
- 要实际实现未修饰,您必须对 LIB 进行十六进制编辑以更改该标志(我知道没有implib
标志或 DEF 设置能够自动执行此操作)。LIB 文件格式实际上与 COFF/PE 相同,因此您只需编辑文件中的导入标头,更改相关字节,位于每个导入的导入标头,偏移量 18 - 进一步阅读此处(人性化)&这里(硬文档)。