C++中静态链接时,lib OSMesa离屏上下文创建失败

3
我制作了一个用于离屏渲染3D模型的C++工具,使用OSMesa库进行渲染。
这个软件运行良好已经超过一年了,我大约6个月前停止更新它。在这期间,我的开发环境被多次更新。
现在我再次编译它时发现了一个意外的错误。
普通版本的软件仍然按预期工作,但静态链接的版本会出现段错误。
我假设错误在于OSmesa配置/编译/链接过程中我的问题而不是库代码中的问题,但任何关于更好地调试段错误的建议都将不胜感激。
尝试了许多编译过程的变化,但没有成功,我现在相当困惑。 有人能看到我在下面描述的步骤中做了什么愚蠢的事情吗?
我使用相同版本的共享库重新编译了OSmesa库的静态版本(12.0.6),禁用了所有不需要的功能(使用基于Ubuntu的系统,存储库中没有OSmesa的静态版本):
./configure \ --disable-xvmc \ --disable-glx \ --disable-dri \ --with-dri-drivers="" \ --with-gallium-drivers="" \ --disable-shared-glapi \ --disable-egl \ --with-egl-platforms="" \ --enable-osmesa \ --enable-gallium-llvm=no \ --disable-gles1 \ --disable-gles2 \ --enable-static \ --disable-shared
这是我离屏渲染工具的编译命令:
g++ -std=c++11 -Wall -O3 -g -static -static-libgcc -static-libstdc++ ./src/measure_model.cpp model.o thumbnail.o -o measure_model_debug -pthread -lOSMesa -ldl -lm -lpng -lz -lcrypto
这是我静态编译使用OSmesa时得到的警告,即使一年前使用的可用的静态二进制文件也有这个警告:
/home/XXX/XXX/backend/lambda/mesa/mesa-12.0.6/src/mesa/main/dlopen.h:52: warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
这是运行工具时得到的结果:
Segmentation fault (core dumped)
但是,如果我简单地跳过OSmesa上下文创建步骤(显然也跳过了所有的3D渲染),就不会产生段错误。
这是回溯信息:
#0  0x0000000000000000 在 ?? ()
#1  0x00000000004af20a 中的mtx_init (type=4, mtx=0xe10f70)位于../../include/c11/threads_posix.h:215处
#2  在main/hash.c中的_mesa_NewHashTable ()位于第135行
#3  _mesa_alloc_shared_state (ctx=ctx@entry=0xdcc9b0) 在main/shared.c中位于第67行
#4  在main/context.c中的_mesa_initialize_context (ctx=ctx@entry=0xdcc9b0, api=api@entry=API_OPENGL_COMPAT, visual=, share_list=share_list@entry=0x0, driverFunctions=driverFunctions@entry=0x7fffffffcd40)位于第1192行
#5  在osmesa.c中的OSMesaCreateContextAttribs (attribList=attribList@entry=0x7fffffffd290, sharelist=)位于第834行
#6  在osmesa.c中的OSMesaCreateContextExt (format=, depthBits=, stencilBits=, accumBits=, sharelist=)位于第660行
#7  在generate_thumbnail(Model*,Json::Value)中的generate_thumbnail()位于第46,8742行
#8  在./src/measure_model.cpp中的main (argc=,argv=)位于第107行

必须使用静态链接的二进制文件。

该段错误发生在我编译工具的同一台机器上(OSmesa静态库也是在同一台机器上编译的),但是在相同工具的非静态链接版本中没有段错误。


1
请在gdb下运行出错的程序;在segv后发布实时输出 btinfo regframe 1; disassemblemtx_init使用了一些pthread 互斥锁/互斥锁属性函数,您在静态程序中使用pthread时遇到了一些问题。这可能是个坏主意,请尝试更改您的要求严格程度(动态链接到glibc和pthread,对于在旧操作系统上运行,使用自己的glibc+pthreads副本和rpath链接到它们)。 - osgx
谢谢osgX,我打算进行这个额外的调试并更新问题。是否有已知的问题在静态链接程序中使用pthread? - pangon
1
非常好,经过一些测试,事实证明静态链接的pthread库导致了问题。我的实际使用情况需要静态链接大多数库,但不是核心库。可以将dl和pthread动态链接,解决我的问题。非常感谢。我很失望看到C++二进制文件链接到pthread的这种限制!我希望在研究我在网上看到的案例后,能够找出这种pthread限制的意义。再次感谢@osgx,如果您可以回答这个问题,我会将其标记为正确,并给予奖励:) - pangon
这是 https://bugzilla.redhat.com/show_bug.cgi?id=115157 的副本,报告了数十年前 Jakub Jelinek 提出的解决方案:“首先,如果可以的话,请避免使用-static,它只会制造问题”和“如果你真的需要.. **只需使用-Wl,--whole-archive -lpthread -Wl,--no-whole-archive而不是-pthread**”。 - osgx
1个回答

2
这是运行工具后的输出结果: Segmentation fault (core dumped) 但是,如果我简单地跳过OSmesa上下文创建步骤(显然也跳过了所有3D渲染),就不会产生分段错误。
因此,OSmesa创建存在问题。通过您提供的回溯信息,我们可以看到顶部函数从EIP为零(跳转到NULL / 调用NULL)执行,因此在mtx_init中调用了一些函数,它是OS Mesa上下文创建的一部分。
#0  0x0000000000000000 in ?? ()
#1  0x00000000004af20a in mtx_init (type=4, mtx=0xe10f70) at ../../include/c11/threads_posix.h:215
#2  _mesa_NewHashTable () at main/hash.c:135
#3  0x000000000052f295 in _mesa_alloc_shared_state (ctx=ctx@entry=0xdcc9b0) at main/shared.c:67
#4  0x000000000046e717 in _mesa_initialize_context (ctx=ctx@entry=0xdcc9b0, api=api@entry=API_OPENGL_COMPAT, visual=, share_list=share_list@entry=0x0, driverFunctions=driverFunctions@entry=0x7fffffffcd40) at main/context.c:1192
#5  0x000000000046c870 in OSMesaCreateContextAttribs (attribList=attribList@entry=0x7fffffffd290, sharelist=) at osmesa.c:834
#6  0x000000000046ccdc in OSMesaCreateContextExt (format=, depthBits=, stencilBits=, accumBits=, sharelist=) at osmesa.c:660
#7  0x0000000000468742 in generate_thumbnail(Model*, Json::Value) ()
#8  0x0000000000401c7d in main (argc=, argv=) at ./src/measure_model.cpp:107

什么是功能?根据 include/c11/threads_posix.h 上的在线资源: mtx_init() on github,只有对 libpthread (-lpthread) 的 pthread_mutex_initpthread_mutexattr_init 和其他几个互斥相关函数的调用。
为什么会产生对 NULL 而非实际函数的调用?可能是由于使用 glibc 和/或 libpthread 的静态链接。在此时,确切的问题尚未确定(我能够找到将静态链接的 libpthread.a 放入某些共享库中的报告,这是不正确的并且永远不会起作用)。
在您的情况下,在 glibc/nptl/pthread_mutex_init.c 中只有 pthread_mutex_init 的别名(强别名)(第 150 行)strong_alias (__pthread_mutex_init, pthread_mutex_init),并且 glibc 自身可能有一些符号的弱别名,可能未初始化。您的链接选项或/和 ld 的思维中出现了一些错误,并且他没有找到/链接 nptl/pthread_mutex_init.o(它是 libpthread.a 存档的一部分),并将重定位保持指向 NULL 的最终可执行文件。一些 glibc 的专家可能知道,Employed Russian 是 SO 上的专家之一。
我建议仅静态链接到您的内部库或可能还链接到正常的非系统库,如 mesa(您可以使用 -Wl,-Bstatic -lyour_lib -Wl,-Bdynamic 选项临时更改列出的库的链接方式; 或者使用欺骗选项 -l:-l:libYour_lib.a,由 Radek 在同一问题中发现)。但不要静态链接到 glibc 的大多数基本库,例如 libc、libpthread、librt(在使用 nss 时会出现一些静态链接 glibc 的问题:目标系统必须具有完全相同版本的动态 glibc 才能启用 nss 工作)。
如果您想为旧计算机打包您的应用程序并且需要一些glibc的功能,您可以尝试使用自己的共享glibc库的版本来打包应用程序;将它们放在某个子目录中,添加 rpath 链接器选项以更改库搜索路径,还要将 ABI ld-linux.so.2 加载器的 INTERP 部分从默认值更改为您自己版本的 glibc 中的 ld-linux.so.2,... 但是您仍然会遇到过于老旧的内核问题,因为更新的 glibcs 需要一些相当新的内核现代特性(系统调用、结构等)。

或者您可以将应用程序打包到某种容器中,例如 Docker,或其他隔离解决方案(或 chroot?)中,以始终拥有您的库版本...

更新:刚刚找到了一个类似的 bt 报告,其中互斥实现为 NULL 而不是 nptl:https://bugzilla.redhat.com/show_bug.cgi?id=163083 "静态链接 C++ 程序使用 pthreads 将 core dump"(2005-2007) pthread_mutex_init(&lock, NULL); g++ -g -static foo.cpp -o foo -lpthread where #0 0x00000000 in ?? () #1 0x08048232 in main () at foo.cpp:7

这显然是由于某些 pthreads 函数未包含在输出可执行文件中。此 bug 可能重复了 #115157,并且如果是这样,我很抱歉,但希望包含的测试用例将有用。

额外信息:

#115157 中强制链接所有 libpthread.a 是有效的解决方法。

https://bugzilla.redhat.com/show_bug.cgi?id=115157 "使用 /usr/lib/nptl/libpthread.a 静态链接的可执行文件失败" - 2004-2009 已关闭不修复

Jakub Jelinek 2004-10-29 05:26:10 EDT

首先,尽可能避免使用 -static,它只会产生问题,无论是可移植性还是其他方面。

如果您真的需要创建静态链接的二进制文件并链接 -lpthread,则只需使用 -Wl,--whole-archive -lpthread -Wl,--no-whole-archive而不是 -pthread。其他任何方法都会有很多问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接