免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12
最近访问板块 发新帖
楼主: starwing83

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

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
发表于 2013-12-23 02:52 |显示全部楼层
群雄逐鹿中原 发表于 2013-12-22 22:54
typedef struct
{
    void (*func1)(int, int);
    void (*func2)(int, int, int);
    int (*func3)(int, int);
   。。。
} FUNCS_T;

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


如果要减缓PE/ELF,...以及F,_F,_F@n,..之类的各种麻烦我觉得这是最保险的方式。  完全将平台本身的一些功能架空。
不过不是所有的库都会这么做。。。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
发表于 2013-12-23 03:18 |显示全部楼层
回复 1# starwing83

做一个产生导入库的工具?
以前也遇到过类似问题。 不记得有没有找到不那么ad-hoc的方法了。 不过貌似Mozilla的做法也是凑合着能用, 估计是没有这样的工具。。。
主要依赖的是PE格式以及windows加载它的行为。  编译器肯定会顺从这些行为, 但在一些细节上就各种见招拆招。
如果一个工具能桥接这两者之间的行为而不需要等到编译器提供支持 —— 比如要是想用tcc该怎么办? 它会有--enable-stdcall-fixup这种参数么? 即使有也要很熟悉才能找到 —— 还是有实用价值的?

导入库的格式是很死板的。
每个函数有两个符号。 一个是函数指针。 在加载时会初始化为对应的函数地址。 另一个是很简单的函数。 获取对应的函数指针变量里初始化后地址然后跳转过去。
这里已经没有函数签名的概念了。 于是不需要对头文件做什么手脚。
拿不准的地方是:
1. 命名
究竟哪个是_f哪个是_imp_f我记不清了。。。  假设_imp_f 是指针。 而且它们之间的命名好像在PE格式里有强制的几种关联。  也许可以不使用PE的这部分而自己产生这两个符号来避免这种关联。  反正无论是否使用PE格式的相关部分最终都是会产生这两个符号。
2. 变量
变量是肯定有一个指针符号的。 _imp_x 。 至于_x的情况忘记了。 而且导入/导出变量的情况本身就少。
3. i386
以前没有随时可用的64位机器。 于是经验都是32位上的。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
发表于 2013-12-23 03:41 |显示全部楼层
为了阐述清楚。 这里给出一个原型。

我从这里: http://www.dll-files.com/dllindex/dll-files.shtml?libglesv2 抓到的 libglesv2.dll 。 Linux上。。。 想编译都编译不了。。。

  1. $ objdump -p libglesv2.dll | awk -f implib.awk > libglesv2.cpp
  2. $ cat implib.awk
  3. #!/usr/bin/awk -f
  4. BEGIN {
  5.   FS="[][ \t]+"
  6.   print "#include <windows.h>"
  7.   print "extern \"C\" {"
  8.   printf "static HMODULE libglesv2 = LoadLibrary(%s);\n", "\"libglesv2.dll\""
  9. }
  10. END { print "}" }

  11. /^\[Ordinal\/Name Pointer\] Table$/ , /^$/ {
  12.   if ($2!="Ordinal/Name" && $3)
  13.   {
  14.     printf "int (*__imp_%s)(void) = (int(*)(void))GetProcAddress(libglesv2,(LPCSTR)%s);\n",$3,$2
  15.     printf "int %s(void) { return (*__imp_%s)(); }\n",$3,$3
  16.   }
  17. }
复制代码
产生的cpp文件大致是这样:

  1. #include <windows.h>
  2. extern "C" {
  3. static HMODULE libglesv2 = LoadLibrary("libglesv2.dll");
  4. int (*__imp_glActiveTexture)(void) = (int(*)(void))GetProcAddress(libglesv2,(LPCSTR)0);
  5. int glActiveTexture(void) { return (*__imp_glActiveTexture)(); }
  6. int (*__imp_glAttachShader)(void) = (int(*)(void))GetProcAddress(libglesv2,(LPCSTR)1);
  7. int glAttachShader(void) { return (*__imp_glAttachShader)(); }
  8. ...
  9. }
复制代码
将它编译之后就是一个导入库了。 也许吧。。。  能检查的地方我尽量检查了。  包括g++ -O2 -c 后会使用尾调用优化。 这样就与实际的签名无关了。
但没有真正环境很恼火。。。

导入库大致也就是做这个事情。需要改进的地方:
1. 写一个真正的PE分析器而不是objdump -p | awk 。。。 来得到导出的名字与序号。
2. 如果c++文件编译出的目标文件能和其他c文件编译出的目标文件顺利链接起来 —— 尤其是会将cpp产生的初始化代码放到合适的位置 —— 其实就不需要写一个PE的产生器了?
直接用穷人的元编程。。。
还有一些细节的地方就是用cpp的初始化机制好像比“普通”的__imp_f要来得晚可能会造成问题。 (至于错误检查这里为了简单就没有加)。
3. 如果就是不想用c++编译器。。。 其实也可以写一个PE产生器。 如果想避免 _f 与 __imp_f 名字之间的关联可以把PE的相应部分架空, 为每个指令集写分别写一点点代码。 其实也就2个指令。。。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
发表于 2013-12-23 03:57 |显示全部楼层
回复 1# starwing83

有没有兴趣把这个想法做成实际的工具?

我之前的笔记本里有很多和PE格式相关的原型代码。 不过已经给我爸用了。 拆下来的硬盘估计不是随便找个机器就能启动的。。。 而且还放在办公室里的。。。
也不想把家里的其他电脑上弄个环境。。。 我估计明天就会去办公室。

我也许能挤出一点时间来实现。 但短时间内应该没有机会去真正使用。 原因你懂的。。。
感觉你遇到这种问题的机会也比较多。 因为你经常用MinGW。。。

论坛徽章:
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
发表于 2013-12-23 04:56 |显示全部楼层
回复 14# OwnWaterloo


    Lua有个pe-parser,貌似可以完成很多事情,我还没去看,准备搞定现在的麻烦事儿(OpenGL ES的一堆shader要写)以后就去研究这个,我记在TODO里面了。

你这方法敢情不错,直接下载个dll……恩,我也打算这样了,这几天编译ANGLE把我编译伤了,而且MinGW产生的dll居然比VC的大了三倍还不止(直接从Chrome取的686KB,自己编译的2120KB),一怒之下下了个Visual Studio 2013 Express还没试,不过至少项目里面有sln文件还是好事儿……恩,写到这里的时候刚刚打开sln文件试了试,编译出来的有1039kb,还没strip,不过就这样也小了一半了,MinGW这东西没有lto什么的完全没法玩C++啊……(所有文件都未包含其他的导入库,除了MinGW包含了一个msvcrt.dll,VC编译的甚至连msvcrt.dll都没包含……)

我之前想的工具什么的差不多就是你干的事情了,无非就是获取某个dumpbin什么的结果然后扔awk/sed处理一下然后做成def扔给gcc神马的,当然产生C++什么的最好了。。

说到C++,最近写向量运算算是服气了,这东西没C++完全没法写……一个vec2用C写简直是要死人……C++直接加减乘除完事儿。不过就是需要时刻对写下的代码到底做了什么要十分谨慎,路径顶点处理这绝逼是性能热点什么的……

反正最近就是这样吧……我大概对MinGW有某种神奇的选项能够智能识别@n和不带@n的符号(就像VC的lib导出库那样的)已经不抱多少希望了……

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
发表于 2013-12-23 08:41 |显示全部楼层
回复 15# starwing83

别这样。。。 我又没打算执行或者加载这个dll。 只是看看它的内容而已。 反正linux下它也掀不起什么浪。
如果是要实际使用。。。 从这种不太可信的网站下载下来的东西靠谱么?  从Chrome里抓会比较好?

你不是用的MinGW4.7+么? 没有lto? 还是gcc在windows下不支持lto? 记得好像是将它的中间代码写在目标文件的某个位置。 难道只管elf不管pe的?

awk/sed 这类工具。。。 quick and dirty地搞搞原型也许还凑合。。。  要正儿八经地写个程序还是有点困难。。。
如果坚持都用gnu那套也还行。。。 cygwin就是。 msys不记得了。
ubuntu里gawk是要装的。。。  mac os x下的gnu sed还不知道怎么弄。。。   否则默认的mawk和不知道哪来的sed功能很弱。。。

有可以扩展含义的中缀四则运算符其实很幸福。。。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
发表于 2013-12-23 09:00 |显示全部楼层
回复 15# starwing83

执行与否与从哪里获取这个dll先不考虑。。。

dll里有些符号是用序号导出的? 那使用这个dll的程序或者库怎么发布?
使用这个dll程序和它附带在一起? 使用这个dll的库。。。 只能让库的使用者去选该用那个libglesv2.dll了吧?

用序号导出不是作死的节奏么。 那6个符号本来就是不打算公开使用还是怎么的?

论坛徽章:
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
发表于 2013-12-23 21:21 |显示全部楼层
回复 17# OwnWaterloo


    是这样的,ANGLE的DLL是同时发布编号和函数名的……除了六个函数以外。

这六个函数(其实就是类似内部函数神马的),是libGLESv2.dll专门提供给libEGL.dll使用的,因此特意没有导出函数名。使用的时候,编译libGLESv2.dll不是会有exp和lib么,VC的lib里面就有符号和入口点,只是DLL里面没有罢了,VC本身是可以很轻易地链接OK的,这点我试过了。

MinGW因为其DLL的导出符号名字的问题,基本上无解了……而且是很无语的无解方法,有两个符号是__stdcall的,即使是在def文件里面写了NONAME,但是导出库里的符号仍然是没有带@n,然后后果你就懂了……MinGW啊……无语了……

mingw-w64官网的说法是lto还不成熟,我这边能编译带lto支持的MinGW,但是:
1. 据说32位还是64位某一个对lto支持不佳
2. 我用的自己编译的MinGW,如果要lto支持,需要一些其他的库编译进去
3. 需要在运行时增加-flto参数,且(官网说的)很可能导致内存不足问题

总而言之就是不成熟神马的……哎……真正好用的,还是等clang吧。

顺便说一下,拿别人编译的DLL基本上是破产了,所有其他第三方(除了firefox的以外)都试验过了,结果是,只有我自己编译的那个可以工作,其余的,都会出现“EGL无法初始化”的错误……等我有时间了详细调试一下吧。

总的来说就是……真坑爹啊……
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP