c++动态调用链接库的问题

2019-09-27 18:09:33 +08:00
 BruceAuyeung
c++完全门外汉向大家问个问题,假如 c++二进制里面没有包含某个符号(对应一个 so 里面的函数),这个二进制能调到外部 so 里面的这个函数吗?我设想的场景:
我要开发一个 c++程序,会调用外部 so/dll 文件(出于描述方便考虑,后文就只提 so,so 可能是 c 开发的,也可能是 c++开发的),但我不知道会有哪些 so,so 里面有哪些函数以及函数入参出参,所以就设计一个 xml 文件,用于描述有哪些 so,各自路径,各自的方法列表和参数说明,我 c++二进制运行的时候加载这个 xml 完成解析并完成 so 方法的注册,这样 c++程序就能调用这些方法啦
请问,这可行吗?
3717 次点击
所在节点    程序员
27 条回复
wbing
2019-09-27 18:18:30 +08:00
“但我不知道会有哪些 so,so 里面有哪些函数以及函数入参出参,所以就设计一个 xml 文件,用于描述有哪些 so,各自路径,各自的方法列表和参数说明”
—————-
感觉这有点前后矛盾啊,你都不知道会有哪些 so,那这个 xml 文件是怎么生成的。
wevsty
2019-09-27 18:26:10 +08:00
没有导出的函数是不能从外部调用的。

编译 dll/so 的时候必须导出外部需要的函数,但是你可以使用自己的方法来描述导出的函数。比如你说的通过加载 xml 来确定导出了什么函数,然后动态加载 dll/so 再来调用是可以的。
ysc3839
2019-09-27 18:26:47 +08:00
怀疑这是个 X-Y Problem https://coolshell.cn/articles/10804.html
建议你直接说你要实现什么。
yrand
2019-09-27 18:52:21 +08:00
我猜是想实现插件之类的功能。必须要定好接口才行
Foreverdxa
2019-09-27 19:20:00 +08:00
lib
dll
头文件 这些不好使???
BruceAuyeung
2019-09-27 19:32:47 +08:00
@wbing 是指编译我的 c++程序时,我是不知道有哪些 so 的
BruceAuyeung
2019-09-27 19:33:15 +08:00
@wevsty 外部 so 我们假定是已经导出了的
BruceAuyeung
2019-09-27 19:35:59 +08:00
@ysc3839 就是我希望我的 c++编译完之后,能调用任意外部 so 里面的任意方法
BruceAuyeung
2019-09-27 19:40:09 +08:00
@Foreverdxa 这个在编译的时候已经知道会调哪些 so 的哪些方法了吧
across
2019-09-27 19:41:44 +08:00
不是脑筋急转弯吧,没想到什么方法,如果能解,大概就是加入个 Lua 脚本之类的进行粘合?
pursuer
2019-09-27 19:43:09 +08:00
dlopen RTLD_GLOBAL 可以让加载的 so 的导出对后续加载的 so 可见,但这和 xml 啥的没什么关系,不过这个方式可能动态库卸载不了吧,我也不确定。你说的注册机制倒有点像 windows com 的设计。
BruceAuyeung
2019-09-27 19:52:42 +08:00
https://github.com/node-ffi-napi/node-ffi-napi
下面是一段 JS 代码,ffi-napi 是用 C++写的 nodejs addon。你看 ffi-napi 编译的时候是不知道要调用 ceil 方法的,ffi-napi 是通过 ffi.Library 方法才知道有个 libm 外部动态库,里面有 ceil 方法。这样做的好处是当外部 libm 新增导出方法时,ffi-napi 无需重新编译,只需要修改前段 JS 代码就可以直接使用 ceil 方法了。
~~~
var ffi = require('ffi-napi');

var libm = ffi.Library('libm', {
'ceil': [ 'double', [ 'double' ] ]
});
libm.ceil(1.5); // 2

// You can also access just functions in the current process by passing a null
var current = ffi.Library(null, {
'atoi': [ 'int', [ 'string' ] ]
});
current.atoi('1234'); // 1234
~~~
HHehr0ow
2019-09-27 20:41:51 +08:00
是可行的。
以 Windows 下 DLL 为例,使用 LoadLibrary 加载 module 之后获得 handle,再使用 GetProcAddress 获得目标函数指针 pFooTarget。
之后神奇的部分就发生了,假定 FooTarget 使用了 x86 cdecl calling convention,可以根据 xml 中描述的变量信息,在调用 pFooTarget 之前自行按照 cdecl 的规则进行参数压栈,最后一句汇编 CALL,即可完成函数的调用。
同样,调用完毕后,需要自行到寄存器或者栈上取回返回值,比如 eax。
missdeer
2019-09-27 20:51:49 +08:00
13 楼正解。
Windows 上有个叫 rundll32.exe 的程序就是这个功能。
iceheart
2019-09-27 20:57:57 +08:00
可以实现,但是复杂程度不是你能接受的。
举个例子: 一个 so 库的某个导出函数,需要一个复杂的结构体指针作为参数。
你要构造一个什么样的 xml 来描述这个参数的全部信息?
再假设你做到了,你如何构建数据的存储结构呢?
你不是为这一个特殊结构构建实现,而是为所有可能出现的描述做解析做处理。你觉得最初要的灵活性,还能实现么?
chingyat
2019-09-27 21:07:10 +08:00
1. dlopen 打开动态库
2. dlsym 找到符号
3. cast 为相应的函数指针
4. 调用

这样不行吗?
zealot0630
2019-09-27 21:18:52 +08:00
可以,搜索 dlopen/dlsym
402124773
2019-09-27 21:34:53 +08:00
搜索 windows 下 com 机制调用,好像有类似的情况。
BruceAuyeung
2019-09-27 21:38:35 +08:00
@chingyat 根据你们的提示,我搜索了下
https://github.com/node-ffi-napi/node-ffi-napi/search?q=dlopen&unscoped_q=dlopen

好像 node-ffi-napi 就是这么实现的
BruceAuyeung
2019-09-27 21:44:07 +08:00
@iceheart 谢谢。参数类型可以做出约束,不需要过于复杂。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/604915

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX