免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 12971 | 回复: 17
打印 上一主题 下一主题

[C] 心得:MinGW 链接 stdcall 的 DLL [复制链接]

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-12-22 22:28 |只看该作者 |倒序浏览

    以前一直用MinGW,导出的DLL一直都没有出问题,但是这次居然出了很严重的问题,
主要是遇到了以前从来没有遇到过的 stdcall 调用约定。

    假设一个函数叫做 Function,根据调用约定的常识,如果是cdecl(C的调用约定)
,则在内部的链接名字就是_Function,而导出到DLL的名字却会根据导出的方式而变,无
论如何,真正在DLL里面的导出符号都是没有下划线前缀的。

    然而,导出就意味着导入,如果我们通过__declspec(dllimport)期望从动态库导入
文件,那么事情会很不一样了,在这种情况下,期待被导入的函数并不叫_Function甚至
Function,他会期望一个叫做__imp_Function的名字。

    通常这并不是什么大问题,因为 MinGW 不仅可以从 DLL 直接链接,而且在产生 DLL
的时候,也可以再产生一个.a的标准格式的导入库(VC则是至始至终都会产生这个导出库
的)。在导出库里,则会有__imp_Function这么一个静态的小函数,这个函数存在的目的
就是动态地去调用DLL里面真正导出了的函数。因为这么一个原因,如果我们在声明函数
时带上了 __declspec(dllexport) ,则我们就失去了直接链接DLL的能力了,则必须链接
ld帮我们生成的.a导入库了。

    而ld里面跟这个有关的参数就那么几个,我们是通过gcc把参数传递给ld的,因此我
们需要在参数前加入-Wl,前缀,并且用,作为参数的分隔符:

    -Wl,--output-def=<def文件名>  : 产生一个跟导出有关的def文件。
    -Wl,--out-implib=<.a文件名>   :产生一个用于提供__imp_符号的.a导入库。

    另外,如果在产生DLL文件的时候,并没有产生.a文件,也是有办法的。首先,可以
通过一个额外的工具 pexports 通过 DLL 得到 .def 文件,然后可以通过 MinGW 附带的
dlltool工具将.def 转换为.a文件。

    通常,对于C调用标准的DLL文件,MinGW是可以直接链接到它的,完全不需要
__declspec(dllexport) 这样的修饰,再加上上面的了解,基本上对DLL的使用就很清晰
了。

    然而,如果一遇上__stdcall约定的函数,那么整个局面就复杂起来了。

    首先,其实 __stdcall 约定才是Windows下面DLL的标准调用约定。如果采用 def 文
件的导出方式的话,那么__stdcall函数和普通函数其实没有任何区别,其导出的符号名
依然是Function,坏就坏在,__stdcall函数本身会清理调用它时传递的参数,因此,一
些编译器就必须得知道,这个函数调用以后,到底会把栈指针减去多少,从而预先把栈指
针加上去。很不幸的,GCC就是这么一种编译器……

    根据从这里(http://www.willus.com/mingw/yongweiwu_stdcall.html)得到的一张
表,可以看出,对于__stdcall函数,各种不同的编译器有完全不同的命名方法,而且就
算是相同的编译器,如果导出的方式不同(dllexport?或者def文件?)其最终导出的符
号也不同。

    但是,总的来说,落实到DLL上面去,也就是两种符号而已:Function,和
Function@n,这里的n是一个数字,代表Function执行完以后,会弹多少栈元素。

    对于MinGW,要和对方进行交互,我们需要考虑下面的四种情况:

    1. 如何导出 Function 名字
    2. 如何导出 Function@n 名字
    3. 如何链接到 Function 名字
    4. 如何链接到 Function@n 名字

    这里所有的讨论都没有涉及到之前提到的导出库,对于导出库的问题,我们稍后来解
释。

    导出 Function 名字还是很简单的:只要你能提供一个 def 文件,那么就可以将
stdcall函数导出为Function名字了,不过这里还有问题:gcc会给你一个警告,说会将
导出的Function链接到Function@n,提供-Wl,--enable-stdcall-fixup则会去掉这个警告
,默认执行转换,当然如果disable,则就会因为找不到需要导出的符号而链接失败了。

    导出 Function@n 的名字,一方面也可以通过 def 文件,但是这样你就得手工去算
函数会使用多少栈空间了,也许有工具可以做到这一点,但是我暂时还不知道怎么办(可
能跟nm啊objdump啊什么的有关,希望知道的童鞋能告知一下),但是,最简单的方式还
是采用__declspec(dllexport)的方法了。

    那么,如果在这种情况下,依然想通过def做点事儿,怎么办呢?上面的网站给出了
一些方法来得到def文件,并修改后重新链接,但是反正最简单的方法还是需要
__declspec(dllexport)的,这里我们就不讨论了。

    那么,现在就是链接问题了:如何链接到有@n后缀和没有@n后缀的函数去呢?

    首先,如果导出的DLL里面有@n后缀,那么事情就非常简单了,只需要直接链接即可
,但是如果DLL里面没有@n后缀,那么你直接的链接肯定是会链接失败的,这里发现一个
方法,就是依然采用-Wl,--enable-stdcall-fixup,这样可以链接到没有@n后缀的函数去


    但是,我这里遇到了更麻烦的问题……

    首先,上面的所有讨论,都基于一个特点:我们直接链接的是DLL,而且我们没有采
用任何__dllspec(dllexport)修饰,这意味着,我们不打算链接到__imp_这样的符号上去
,但是,如果DLL上面的某个符号没有导出名字,怎么办?

    我最近就遇到了这样的问题,主要是Google的ANGLE项目,即利用DirectX来实现
OpenGL ES 2.0 接口的一个库。它的libglesv2.dll这个dll里面,有六个符号是没有导出
的,只有位置,没有名字,因此,如果直接链接这个dll,链接是一定会出错的,因为没
有名字嘛。

    那么,链接我们产生的libglesv2.dll.a行不行呢?不行。因为为了保证DLL导出的符
号的顺序,这个dll的符号导出全部采用了def的方式,而def里面没有@n后缀!

    在这种情况下,我们产生的libglesv2.dll.a里面的符号,也是没有后缀的,即使是
__imp_符号,也没有@n的后缀,完全无法链接。

    我最后找到了Mozilla的网站,看样子火狐用的也是这个库来提供WebGL支持,他们的
解决方法是,直接在def文件后面加两行:

  1.     ; GCC has problems with linking to undecored stdcall functions,
  2.     ; so we explicitly add aliases for APIs used by EGL
  3.     glGetProcAddress@4=glGetProcAddress
  4.     glBindTexImage@4=glBindTexImage
  5.    
复制代码
给DLL里面的某些符号起了别名,这样才导入成功。感觉这个办法治标不治本啊,万
一隐藏符号太多,岂不是必须一个一个的这么去加么= =而且这样,本质上是把隐藏符号
给变得不隐藏了呗,最终还是可以在DLL文件的导出表里面看到这两个加了@的符号。

    所以,虽然问题解决了,但是我还是留存了两个疑问,记在这里,如果有牛人知道答
案的可以告知一下,或者以后我找到了答案,我再回来补完。

    1. MinGW如何链接.a导入库里面没有@n后缀的名字?
    2. 如何让MinGW即使是在有def文件的情况下,也始终在.a导入库里面产生@n和不带
    @n的名字呢?已经试过了--add-stdcall-alias,没效果,最理想的的状态是这样的
    ,对于每个符号,在.a里面有四个符号导出:Function, Function@n,
    __imp_Function, __imp_Function@n,这样就不愁找不到符号的问题了。

论坛徽章:
1
处女座
日期:2014-01-21 13:20:51
2 [报告]
发表于 2013-12-22 22:36 |只看该作者

想不到linux也用dll

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
3 [报告]
发表于 2013-12-22 22:38 |只看该作者
我现在在混Windows啊……没办法,电脑是N卡的双显卡,目前的大黄蜂技术不成熟,在Linux我没法用我的主显卡……

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
4 [报告]
发表于 2013-12-22 22:43 |只看该作者
https://bugzilla.mozilla.org/show_bug.cgi?id=945292

这是在mozilla找到的bug贴

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
5 [报告]
发表于 2013-12-22 22:54 |只看该作者
本帖最后由 群雄逐鹿中原 于 2013-12-22 22:54 编辑

非得那么麻烦吗。
我看到某软件,它的插件dll只export一个函数。大概意思是这样 ---

typedef struct
{
    void (*func1)(int, int);
    void (*func2)(int, int, int);
    int (*func3)(int, int);
   。。。
} FUNCS_T;

export这个函数。。
FUNCS_T *get_functions();


论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
6 [报告]
发表于 2013-12-22 23:02 |只看该作者
回复 5# 群雄逐鹿中原


    我也想这样啊……而且对OpenGL来说,是有一个事实标准的东西的,叫glGetProcAddress,理论上有这个就OK了。

问题是,谷歌看上去不打算这样搞啊……他们都是直接上VC了,根本不考虑那么多问题…………从这个角度看起来,这的确就是MinGW本身的问题了,应该是本身就对stdcall的支持有问题吧。

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
7 [报告]
发表于 2013-12-22 23:22 |只看该作者
回复 6# starwing83

看来 stdcall 一点也不 std,
何不用 VC写个中间dll,将 stdcall  转成普通调用
    _Function@n  -->  proxy_Function

mingw的程序只调用 proxy_Function。傻了点,应该有效。

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
8 [报告]
发表于 2013-12-22 23:26 |只看该作者
回复 7# 群雄逐鹿中原


    现在的问题根本就不是我想怎么办啊……现在的问题是,ANGLE这个库根本在MinGW下编译不过嘛……

这也不是第一次Google的东西在MinGW下面编译不过了,上次是skia………………

总的来说,Google什么的………………


PS:编译不过的原因是,里面的一个组件用到了另外一个组件的未命名导出函数,而MinGW没法处理这种情况……

论坛徽章:
0
9 [报告]
发表于 2013-12-23 00:09 |只看该作者
用vc连接mitab库也有类似的问题,按照mitab头文件的声明,vc2012要找的是Function@n格式的,但是mitab导出的是_Function@n。

方法是先用dumpbin从dll中提取def,
然后修改def,将所有_Function@n 替换为 Function@n
然后用def生成lib,项目中导入这个lib,就OK了。

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
10 [报告]
发表于 2013-12-23 00:18 |只看该作者
回复 9# sonicling


    恩,MinGW的导出格式就是_Function@n类型的。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP