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

MFXInit() in libmfx.a segfaults when called from shared object

(虽然英特尔的论坛是一个更自然的提问 this question 的地方,但我在这里发帖希望 activity 比英特尔目前完全缺乏论坛更多 activity)

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

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

但是,此 libmfx.a 库似乎将所有调用委托给 dlopened dynamic/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

然后(动态地)linked 到 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 创建为 static 库时, 不会 崩溃。使用断点和反汇编程序,我设法制作了以下回溯快照,在这两种情况下 #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

因为静态和共享的 Intel 库公开了相同的 API 函数,我 可以 link 直接进入 libmfxhw64.so 胆量,但是我想绕过静态 "dispatcher" 是没有保证的(?)

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

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


在 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 以确认问题。

(好吧,既然没有人急于求成,那我就做点不雅的事,然后 post 回答我自己的问题)。

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

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

There is slight difference between using Dispatcher library from executable module or from shared object. To mitigate symbol conflict between itself and SDK shared object on Linux*, application should: 1) link against libdispatch_shared.a instead of libmfx.a 2) define MFX_DISPATCHER_EXPOSED_PREFIX before any SDK includes

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

如果你安装"Intel Media Server Studio Professional 2016",你可以找到这个文件。有一个免费的社区版。源文件和 PDF 将在 /opt/intel/mediasdk/opensource/

中找到