从共享对象调用时,libmfx.a 中的 MFXInit() 出现段错误

Posted

技术标签:

【中文标题】从共享对象调用时,libmfx.a 中的 MFXInit() 出现段错误【英文标题】:MFXInit() in libmfx.a segfaults when called from shared object 【发布时间】:2015-04-28 11:32:23 【问题描述】:

(虽然英特尔的论坛是提问 this question 的一个更自然的地方,但我在这里发布它是希望比英特尔完全缺乏的活动更多——到目前为止)

我无法创建使用 Intel Media SDK(Linux 服务器)处理 h264 视频的动态链接库,并注意到 MFX 库的设计存在问题。按照我的理解,程序应该链接到 static 库,例如:

$ g++ .... -L/opt/intel/mediasdk/lib/lin_x64 -lmfx

然而,这个libmfx.a 库似乎将所有调用委托给dlopened 动态/opt/intel/mediasdk/lib64/libmfxhw64.so。值得注意的是,静态库和动态库公开的函数名称(和签名)是​​相同的,这有点令人困惑和危险。

虽然我不明白这种设计背后的基本原理,但它本身不应该是一个问题,当(静态)libmfx.a 包含在库中时,库中的一些静态/全局初始化显然不会造成严重破坏一个共享对象。即:

    +------+     +-----------+
    | main | <-- | mylib.so  |
    +------+     |           |          +---------------+
                 | libmfx.a  | (dlopen) | libmfxhw64.so |
                 |          <-------------              |
                 |+---------+|          |+-------------+|
                 ||MFXInit()||          ||  MFXInit()  ||
                 ||...      ||          ||  ...        ||
                 ||         ||          ||             ||
                 +===========+          +===============+

上面的库可以这样组装:

$ g++ -shared -o mylib.so my1.o my2.o -lmfx

然后(动态)链接到main.o,如下所示:

$ g++ -o main main.o mylib.so -ldl

(请注意,额外的libdl 是允许libmfx.adlopen() libmfxhw64.so 所必需的。)

不幸的是,在第一次调用 MFXInit() 时,程序会导致分段错误(访问地址 0x0000400)。 GDB 回溯:

#0  0x0000000000000400 in ?? ()
#1  0x00007ffff61fb4cd in MFXInit () from /opt/intel/mediasdk/lib64/libmfxhw64-p.so.1.13
#2  0x00007ffff7bd3a1f in MFX_DISP_HANDLE::LoadSelectedDLL(char const*, eMfxImplType, int, int) () from ./lib-a.so
#3  0x00007ffff7bd12b1 in MFXInit () from ./lib-a.so
#4  0x00007ffff7bd09c8 in test_mfx () at lib.c:12
#5  0x0000000000400744 in main (argc=1, argv=0x7fffffffe0d8) at main.c:8

(请注意堆栈帧#3 中的MFXInit()libmfx.a 中的那个,而#1 中的那个在libmfxhw64.so 中。)

请注意,当mylib 创建为静态 库时,不会 崩溃。使用断点和反汇编程序,我设法制作了以下回溯快照,在这两种情况下#1 都位于MFXInit+424,但它们似乎命中了MFXQueryVersion不同 版本(绝对地址没有意义,因为搬迁):

#0  0x00007ffff6411980 in MFXQueryVersion () from /opt/intel/mediasdk/lib64/libmfxhw64-p.so.1.13
#1  0x00007ffff640c4cd in MFXInit () from /opt/intel/mediasdk/lib64/libmfxhw64-p.so.1.13
#2  0x000000000040484f in MFX_DISP_HANDLE::LoadSelectedDLL(char const*, eMfxImplType, int, int) ()
#3  0x00000000004020e1 in MFXInit ()
#4  0x0000000000401800 in test_mfx () at lib.c:12
#5  0x0000000000401794 in main (argc=1, argv=0x7fffffffe0e8) at main.c:8

因为静态和共享英特尔库都公开了相同的 API 函数,我可以直接链接到 libmfxhw64.so 胆量,但我认为绕过静态“调度程序”是没有保证的(?)

有人能解释一下英特尔在上述设计背后的想法吗?规范,为什么要提供一个只委托给具有相同接口的.so 的静态库?

此外,SEGV 似乎是由libmfx.alibmfxhw64.so 中的静态/全局数据引起的。有没有办法在动态加载的静态/全局部分上强制执行特定的执行顺序?调试此类问题的最佳方法是什么?


在 Intel Haswell i7-4790 @3.6Ghz 上使用 Intel Media SDK R2 (ubuntu 12) 和 Intel Media SDK 2015R3-R5 (Centos 7, 1.13/1.15) 进行测试

如果您有工作的英特尔 MSDK 设置,请编译 my example code 以确认问题。

【问题讨论】:

【参考方案1】:

在最近发布的调度程序源代码中,文件“readme-dispatcher-linux.pdf”的最后是这样的:

使用 Dispatcher 库与从 可执行模块或来自共享对象。减轻符号冲突 在其自身和 Linux* 上的 SDK 共享对象之间,应用程序应该: 1) 链接到 libdispatch_shared.a 而不是 libmfx.a 2) 在任何 SDK 包含之前定义 MFX_DISPATCHER_EXPOSED_PREFIX

我用过这个,它可以解决你描述的符号冲突问题。

如果您安装“Intel Media Server Studio Professional 2016”,您可以找到此文件。有一个免费的社区版。源文件和 PDF 位于 /opt/intel/mediasdk/opensource/

【讨论】:

【参考方案2】:

(好吧,因为似乎没有人急于求成,所以我会做不雅的事情并发布我自己问题的答案)。

经过大量研究试图打破无意的循环链接,我发现ld 选项--exclude-libs 提供了安慰。本质上,我正在寻找一种方法来强制删除任何 libmfx.a 符号,然后在创建 DLL 时使用它们来解决 lib.o 中的依赖关系。这可以通过像这样创建so 来完成:

g++ -shared -o lib-a.so lib.o -L/opt/intel/mediasdk/lib/lin_x64 -lmfx -Wl,--exclude-libs=libmfx

一旦像这样创建了库,Bob 就是你的叔叔了:

g++ -o main-so-a main.o lib-a.so -ldl

(请注意,仍然需要libdl,因为英特尔的 MFX(现在在lib-a.so 内部)仍然使用dlopen 来发现libmfxhw64.so

来自ld 手册页:

   --exclude-libs lib,lib,...
       Specifies a list of archive libraries from which symbols should not be
       automatically exported.  The library names may be delimited by commas or
       colons.  Specifying "--exclude-libs ALL" excludes symbols in all archive
       libraries from automatic export.  This option is available only for the
       i386 PE targeted port of the linker and for ELF targeted ports.  For i386
       PE, symbols explicitly listed in a .def file are still exported,
       regardless of this option.  For ELF targeted ports, symbols affected
       by this option will be treated as hidden.

因此,本质上,诀窍是不确保相关的 ELF 符号被标记为隐藏。通常这将由库开发人员(即英特尔)通过#pragmas 处理,但由于他们的疏忽,在这种情况下需要对其进行改造。

我想--version-script 映射文件也可以实现相同的功能,但结果可能会更加脆弱,因为无论如何我们都希望完全封装libmfx.a

【讨论】:

以上是关于从共享对象调用时,libmfx.a 中的 MFXInit() 出现段错误的主要内容,如果未能解决你的问题,请参考以下文章

从多个线程同步对共享对象的方法调用

Qt:无法从 javascript 调用共享对象方法/属性

C++:动态共享库中的虚函数产生段错误

静态变量和成员变量有什么区别?应该怎么调用?

内存映射mmap 和 共享内存

在 Swift 中访问共享容器中的自定义对象