在How to make a .lib file when have a .dll file and a header file中描述了处理从DLL创建LIB的一般方法 - 仍然是为具有未修饰stdcall
函数的DLL创建导入库(例如核心WinAPI DLL' s,{ {1}}等等,人们必须经历一个相当漫长而复杂的过程(如here所述)。对于具有大量功能的DLL,此过程非常耗时且容易出错 - 当原始DLL发生更改时(例如,由于供应商更新),它在自动渲染情况下也很容易失败。
是否有一种有效的自动化方式?
答案 0 :(得分:4)
免责声明:YMMV。这里描述的所有行为都依赖于大量的实现/版本/平台,我甚至不完全相信我在这里陈述的一些事情。尽管如此,我认为这是一个很好的收集各种想法,方法和见解,其中大多数实际上工作得很好。
我描述的过程可以用作生成DLL的正确implib的一般方法,不仅适用于stdcall unmangled;不过,它们是最难破解的坚果,所以我认为它们应该得到一些特殊待遇
我们首先只有一个"外星人" (没有来源,没有标题等)DLL文件。虽然有一些专用的GUI工具可以做到这一点(例如this),但我假设任务应该在脚本中完成,所以没有任何GUI,只有文本shell命令。
如果你正在使用一个新的MinGW (一些古代版本不支持它,但所有最近的版本都是这样做的),你通常 不 必须创建导入LIB以便能够与DLL链接!您只需要:< / p>
a)声明方法的原型(例如在 b)使用其他 这就是诀窍;该应用程序与您的DLL正确链接,即使它是一个未修饰的stdcall。 但是,在某些情况下,您将使用另一个编译器,或者出于某种原因只需要LIB本身(重定向,避免名称冲突等)。最简单的方法是只需create a stub DLL,即对于你拥有的每个函数原型,创建一个空的函数体(注意,虽然 如果您没有标题,则必须生成所需的所有中间文件。该过程可分为3个部分: 第一项任务可以通过多种方式完成。最简单的是使用专用工具 - 例如OSS&amp; .h
文件中),很可能没有 __declspec(dllimport)
- MinGW对它们使用奇怪的装饰/修改,并且(至少在我的设置上)由于链接器预期__imp_
前缀而不是常规__imp__
前缀,他们不会链接,-LDllDir -lDllNameWithoutDllExtension --enable-stdcall-fixup
发出您的编辑/链接,例如:
g++ example.cpp -L. -lkernel32 --enable-stdcallfixup
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接口生成它),所以有时这样做是不可能的。
1
pexports
或gendef
在MinGW扩展存档中可用(默认情况下,AFAIR未安装在MinGW中,除非您安装所有内容);使用dumpbin /exports
更加棘手,因为它增加了对重定向等的评论。还有许多其他工具可以做到这一点,比如expdef(它们通常很简单,可以直接包含在更大的应用程序,请参阅impdef例如)。 请注意,dlltool
和nm
通常都会失败!
瞧,瞧。请注意,虽然DEF 必须具有没有gendef %1.dll
_
前缀的函数名称,但如果您正在处理未修饰的stdcall DLL,则不会出现错误等等,@size
后缀 ,见下文。
第二项任务有点棘手;需要注意的是,链接器需要LIB中的symbol@size
符号才能正确链接stdcall
个调用,但您可能会使用自动化工具以完全未修饰的导入结束。 gendef
可以有时重新生成该数据(YMMV) - 如果有,您可以全部调用dlltool
或implib
(见下文)。如果它没有,并且你既没有原型也没有你可以重新生成原型的文档,也没有这个特定DLL的任何其他类似版本的LIB,我会说你&# 39;主要是运气不好 - 使用stdcall
,您必须基本上反汇编每个方法,以了解所接受参数的总大小。有时您可以根据使用它的代码猜测/计算(例如,在调用之前计算PUSH
es的数量),但它的IMO无法正确自动化 - 需要放置值在DEF手动。
请注意,如果您有标题但不想使用存根方法,则可以创建一个调用所有所需导入的虚方法 - 链接器将引发错误,这些错误将为您提供包含的装饰名称其中@size
。或者,您可以从size_t
参数等计算它。
只需使用dlltool
或implib
,即可从固定的DEF生成LIB。例如dlltool
的一般语法将是(在批处理文件中使用)
dlltool -d %1.def -D %1.dll -l %1.lib
结果,你得到你的LIB。
最后的警告:
methodname1@size=methodname2
的DEF IMPORTS别名,但Microsoft会像methodname1@size
一样对待它们,而不是将它们混淆;在我知道的MSVC中,没有方法将装饰名称别名为未修饰的名称,cdecl
从不解决方案 - 这些符号可能匹配,但除了最微不足道的情况之外,所有调用都会失败,dumpbin /exports /header
)在MSVC中,implib会生成一个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 - 进一步阅读here(人类友好的)&amp; here(硬文档)。