Repository navigation
Linux 下 GLFW/OpenGL 程序运行时无法创建窗口:mcpp 自带 glibc(2.39)低于宿主 Mesa 要求(2.43),且 compat-x-glx-runtime 指向 32 位库 #352
Description
Activity
- added 4 commits that reference this issue
on Aug 4, 2026 - added a commit that references this issue
on Aug 7, 2026 - added a commit that references this issue
on Aug 7, 2026 - added a commit that references this issue
on Aug 8, 2026 进展与剩余缺口(2026-08-08,实测)
这条 issue 的两半里,一半已解决,另一半定位到了确切的一步。issue 保持打开。
已解决
① mcpp 侧:subos 声明的环境变量到达被运行的程序(2026.8.8.1)。此前 mcpp 读的是自己想象出来的
subos_info.envs线格式,整条修复是空转的;现按 xlings 实际写出的格式解析,并用逐字取自真实 xlings 输出的 fixture 钉住。② 索引侧:GL 来自生态而非
/usr/lib(mcpp-index #181)。compat.glx-runtime不再从宿主符号链接 libGL/libEGL。③ 引擎侧:产物绑哪个 glibc 不再靠猜(2026.8.8.2)。这其实是 #181 第一次落地(#179)被 revert 的原因:mesa 的
xim:glibc@>=2.38会在 2.39 旁边装进 2.44,而当时 mcpp 用readdir第一项选 payload,于是编译侧取 2.44、产物 interpreter 仍是 2.39,报错落在asio-module/core这些与图形无关的成员上。现由权威决定并计入指纹。密闭性实测(NVIDIA RTX 4080 宿主,CN 与 GLOBAL 镜像各一次):
产物 interp → payload glibc 2.44 强制用宿主库 → libm.so.6: GLIBC_2.43 not found ← 反证确实绑载荷 NVIDIA vendor 供给 → 50 个 nvidia 文件在 subos lib,RUNPATH 完整剩余缺口:GLX vendor 选择
EGL 路径已通,GLX 路径不通。 而 GLFW 在 X11 上默认走 GLX —— 这就是用户看到「无法创建窗口」的直接原因。
用一个直接询问 glvnd 的探针测到:
__GLX_VENDOR_LIBRARY_NAMEglXGetFBConfigsglXChooseFBConfigmesa18 9 nvidia0 0 不设 0 0 glXQueryVersion : ok (1.4) ← 服务端有 GLX 扩展 has GLX_EXT_libglvnd : NO ← 服务端不广告它,glvnd 无从自动选 vendor client GLX_VENDOR : <null> ← 一个 vendor 都没加载两条结论:
- 载荷 GLX 栈本身是好的 —— mesa vendor 下拿到 18 个 FBConfig。
- nvidia vendor 被找到却装载不成 —— 载入器在真实进程中尝试了正确路径 29 次,而单独用载荷 loader + 正确搜索路径解析该库时,它和全部依赖都在载荷内解析成功(
libnvidia-glsi/libnvidia-tls/libnvidia-glcore/libX11/libc全部落在 subos lib)。失败原因被 glvnd 吞掉,__GLVND_DEBUG=1也不吐。
顺带记一条会误导后来人的观察:共享 registry 里生成的
libGL.so.1,其 RUNPATH 末项烙的是首个生成它的项目的 subos 路径。另一个项目的产物会沿着别人的 subos 去找 vendor —— 在本机恰好都指向同一份宿主驱动的软链,所以不是本次失败的原因,但它是一个真实的跨项目耦合。下一步
从「找到了却装不成」这一步继续:需要拿到 glvnd 吞掉的那个 dlopen 失败原因(glvnd 的 GLX vendor 装载路径里有 ABI 版本检查 ——
__glXMappingInit/ vendor 的__glx_Main入口点版本协商,不匹配时它会静默拒绝并回落)。这是本 issue 唯一还没被排除的环节。根因定位完成:转发桩把依赖解析交给了一个在载荷里不存在的搜索路径
接上条。「nvidia vendor 被找到却装载不成」这一步查清了,是
xim:nvidia-gl-host-link的问题,不在 mcpp 也不在 mcpp-index。链条
libGLX_nvidia.so.0在载荷里是一个转发桩,它唯一的NEEDED是宿主的绝对路径:NEEDED /lib/x86_64-linux-gnu/libGLX_nvidia.so.0载入器顺着它加载宿主真库,然后去找那个库的依赖 ——
LD_DEBUG=libs原文:find library=libnvidia-glsi.so.550.144.03 [0]; searching search cache=/home/xlings/.xlings_data/xim/xpkgs/fromsource-x-glibc/2.44/etc/ld.so.cache search path= (system search path)这次查找不带任何 RUNPATH。 三个事实叠在一起:
宿主 libGLX_nvidia.so.550.144.03RUNPATH 条目数 0 —— 它依赖发行版的 ld.so.cache host-link包里那份 550 库同样没有 RUNPATH 载荷 glibc 的 ld.so.cache 指向 /home/xlings/.xlings_data/...,构建机路径,本机不存在于是
libnvidia-glsi无处可找 → 宿主真库装不进来 → 转发桩失败 → glvnd 静默回落 →glXGetFBConfigs返回 0 → GLFW 报No GLXFBConfigs returned。为什么之前的排查一路指错方向
- 桩自己有完整 RUNPATH,且单独
dlopen+dlsym("__glx_Main")成功 —— 所以「桩坏了」被排除 libnvidia-glsi确实在 subos lib 里,单独用载荷 loader + 正确LD_LIBRARY_PATH解析时,它和全部依赖都在载荷内解析成功 —— 所以「缺件」也被排除
失败只发生在转发进宿主库之后的那一跳,因为那一跳换了对象、也就换了 RUNPATH 上下文。glvnd 吞掉 dlopen 错误(
__GLVND_DEBUG=1也不吐),所以从表面完全看不出来。可能的修法(在
nvidia-gl-host-link)- 给被链接的驱动库补
$ORIGINRUNPATH —— 让它们的同目录兄弟能互相找到,这与该包已经在做的「重链」是同一类操作 - 或不走绝对路径转发,直接把宿主驱动库以正常
NEEDED(soname)链接,让载荷搜索路径生效
顺带:载荷 glibc 烙进去的
ld.so.cache路径指向构建机,这本身是个独立的小问题 —— 任何依赖 cache 回落的库在载荷里都会踩到。现状
- EGL 路径可用(实测
exit=0,窗口创建并渲染) - GLX 路径不可用,原因如上;GLFW 在 X11 默认走 GLX
- 载荷 GLX 栈本身是好的:
__GLX_VENDOR_LIBRARY_NAME=mesa下拿到 18 个 FBConfig
- 桩自己有完整 RUNPATH,且单独
修法已通过实验确定(未实施 —— 需要改
xim:nvidia-gl-host-link)接上条。把链条一路走到 vendor 真的装载成功,确认了修法形状,也排除了两条看似合理但行不通的路。
缺的到底是什么
__nvidia_entries()只链 NVIDIA 名字的库:"^libnvidia%-", "^libGLX_nvidia%.", "^libEGL_nvidia%.", "^libGLESv1_CM_nvidia%.", "^libGLESv2_nvidia%."
而宿主
libGLX_nvidia.so.550.144.03的DT_NEEDED还包括libX11.so.6、libXext.so.6,以及 glibc 侧的libdl.so.2/libpthread.so.0/libc.so.6。描述符自己的注释写着:
install() links each of these into this package's own lib dir, so declaring them is not decoration — it is where they come from.
代码没有这个行为。 与我这轮在 mcpp 侧修过的是同一种病:注释描述了一件代码不做的事。
逐步验证(每一步都推进了一层)
起点 dlopen 失败: libXext.so.6 not found 补 libX11/libXext 软链 → dlopen 失败: libdl.so.2 not found 再补 glibc 侧 5 个 → dlopen ok; __glx_Main=found ← vendor 装载成功但「补软链」不是正确修法 —— 两条排除
① 把 glibc 软链进该目录很危险。 我这样做并把目录放上
LD_LIBRARY_PATH后,宿主的timeout进程也去用了载荷 libc:symbol lookup error: .../libc.so.6: undefined symbol: __pointer_chk_guard, version GLIBC_PRIVATE这就是 GLIBC_PRIVATE 版本锁。载荷 glibc 不能以裸 soname 出现在任何可能被宿主进程搜到的目录里。(我已把这些软链全部删除,环境已还原;宿主驱动文件全程未被改动。)
② 光让依赖「可被找到」不够 ——
DT_RUNPATH不向下继承。 真实程序里做查找的是宿主真库(它自己 RUNPATH 条目数为 0),而不是有完整 RUNPATH 的转发桩。所以即使把目录喂给LD_LIBRARY_PATH能让 dlprobe 通过,真实程序仍然不通 —— 除非污染全局LD_LIBRARY_PATH,而那正是 ① 的坑。因此:让桩把这些依赖也 NEED 上
libGLX_nvidia.so.0是本包生成的转发桩(21KB 实体文件,不是软链),它已经有覆盖libX11/libXext/glibc 载荷目录的完整 RUNPATH。若生成时把宿主 vendor 的DT_NEEDED一并声明为桩自己的 NEEDED,则:- 这些依赖经桩的 RUNPATH 解析(nvidia 那几个就在同目录,是 RUNPATH 首项)
- 等宿主真库被拉进来时,它的 NEEDED 已在 link map 中被满足
- 不需要任何环境变量,也不需要把 glibc 软链散到别处
这与该包已有的「重链」是同一类操作,且不违反「不复制 NVIDIA 库」——桩里只有名字,没有代码。
现状小结
EGL 路径 ✅ 可用( exit=0,窗口创建并渲染)GLX 路径 ❌ vendor 装不进(原因如上) 载荷 GLX 栈本身 ✅ __GLX_VENDOR_LIBRARY_NAME=mesa下 18 个 FBConfig宿主驱动文件 ✅ 全程未被改动(root 所有,原始 mtime) 已修复并合入
根因是一个词:转发桩携带的是
DT_RUNPATH,而设计要的是DT_RPATH。nvidia-gl-host-link.lua的注释早就写明了这一点 —— 「RPATH 沿加载链传递,RUNPATH 不会」。桩用绝对路径 NEED 宿主真 vendor,于是随后去查libnvidia-glsi/libX11/libc的那个对象是宿主的库,而它自身 RPATH/RUNPATH 条目为 0。RUNPATH 不传递,桩算出的闭包对那次查找完全不可见,glibc 回落到一个用构建机前缀烙成的 ld.so.cache 加一条空 system path。什么都找不到,glvnd 吞掉 dlopen 错误,用户只看到GLX: No GLXFBConfigs returned。合入
PR 内容 openxlings/xim-pkgindex#564 装完把 tag 改回 DT_RPATH(值从--print-rpath读回,不手写第二份闭包)openxlings/xim-pkgindex#566 补丁没打上时告警 —— 两步一步在 try里、一步依赖 PATH 上有 patchelf,静默跳过会完整重现本缺陷#383 顺带修掉的: prepend把调用方的值整个丢掉(对 PATH 形状变量即用户的整条搜索路径)验证
A/B,同一桩文件只换 tag,其余无任何差别:
RUNPATH → GLFW error 65542,exit 11 RPATH → exit 0,libGLX_nvidia 初始化 2 次,libnvidia-glcore 初始化 1 次产出 tag 的那段 Lua 也在
xlings script里单独实跑过:读到 494 字符 rpath,patch 后 tag 变RPATH。没有验证到的那一环,说清楚: 本机上「全新安装跑一遍 install()」没能验成 —— 已有 payload 让重装成为 no-op,而全新隔离 home 撞到
same-owner registration omits an existing member of group这个与本改动无关的 xvm 注册问题。两个 PR 的 CI(含linux-install-test)全绿。排查中被否掉的两条路(记下来免得重走)
- 把缺的库软链进包的 lib 目录:能让隔离的 dlopen 通过,真实程序仍然不通(还是 RUNPATH 不传递);而且把载荷 glibc 放进那个目录很危险 —— 实测宿主
timeout进程因此拿到载荷 libc,报__pointer_chk_guard, version GLIBC_PRIVATE。 - 给桩补 DT_NEEDED:可行,但要算传递闭包,且必须把宿主 X11 链(libbsd/libmd)排除在外——那些在载荷里不存在。能跑通,但远比换一个 tag 复杂,也更容易漂移。
这条 issue 的三半
- subos 声明的环境变量到达程序(mcpp 2026.8.8.1)
- GL 来自生态而非
/usr/lib(mcpp-index#181) - NVIDIA vendor 装得进(本次)
全部完成。宿主驱动文件全程未被改动(root 所有,原始 mtime,已核)。
- 把缺的库软链进包的 lib 目录:能让隔离的 dlopen 通过,真实程序仍然不通(还是 RUNPATH 不传递);而且把载荷 glibc 放进那个目录很危险 —— 实测宿主
环境
DISPLAY=:0)现象
mcpp build完全正常;mcpp run后进程立即退出、返回码 255(即 -1)、stdout/stderr 无任何输出,窗口不出现。最小复现
任何用到 GLFW + OpenGL 的 mcpp 项目都可复现,例如:
src/app.cpp只需定义app::dslAppConfig()和app::compose()(eui-neo 的 GLFW 入口core/app/glfw_app_main.cpp提供main())。然后:诊断过程与根因
用项目
target/里已编译的 glfw 对象文件链接一个带glfwSetErrorCallback的最小测试,逐层定位,发现三个互相独立的问题叠加:问题 1:eui-neo/GLFW 初始化失败时完全静默(可观测性)
glfw_app_main.cpp的main()在glfwInit()、createWindow()、createRenderBackend()、app::initialize()任一处失败都只return -1,没有安装 GLFW error callback,也没有 stderr 输出,用户完全无法判断失败原因。问题 2:compat-x-glx-runtime 包在 Fedora 系发行版上损坏(32 位符号链接)
GLFW 报
GLX: Failed to load GLX(65542)。LD_DEBUG=libs显示 dlopenlibGLX.so.0/libGL.so.1时只搜索了 mcpp 自己的目录(二进制 RUNPATH + xlings 私有 ld.so.cache),不含/usr/lib64,因此宿主 GL 库天然找不到。mcpp 显然预见到了这一点 —— 二进制的 RUNPATH 里已经包含
compat-x-glx-runtime/2026.06.03/mcpp_generated/glx_runtime/lib。但该包的内容是指向
/usr/lib/*的符号链接:Debian/Ubuntu 的 64 位库在
/usr/lib/x86_64-linux-gnu或/usr/lib,而 Fedora/RHEL/openSUSE 的/usr/lib是 32 位库目录,64 位库在/usr/lib64。于是 dlopen 直接报:即该包按 Debian 布局假设生成,在 Fedora 布局下整体失效。
问题 3(核心):mcpp 自带 glibc 2.39 与宿主图形栈的 glibc 要求不兼容
绕过问题 2(手工把
/usr/lib64的 64 位库放进搜索路径)之后,所有 GL 库都能生成 link map,但 Mesa 在初始化时崩溃于:mcpp 二进制的
PT_INTERP是xim-x-glibc/2.39/lib64/ld-linux-x86-64.so.2,进程内 glibc 固定为 2.39;而宿主的 Mesa/GLX/EGL 栈是按系统 glibc 2.43 编译的。Linux 桌面 GUI 程序必须使用宿主机的 GL 驱动栈(它还要和宿主 X/Wayland、DRM 设备通信),因此"自带 glibc 沙盒运行时"这个对 CLI 程序很友好的设计,在 GUI 场景下与宿主图形栈产生硬性版本冲突:宿主 Mesa 只会随发行版越来越新,自带 glibc 不升级就必然断。另外实测
LD_LIBRARY_PATH=/usr/lib64也不能作为变通:进程仍由 2.39 的 ld.so 加载,ld.so 与 libc.so.6 版本不匹配会直接段错误。临时解决方案(workaround)
用系统动态链接器启动,让进程整体跑在系统 glibc 2.43 上(mcpp 编译产物按 2.39 编译,在 2.43 上向前兼容;自带的 X11 库也照常可用):
效果:GLX 初始化成功、窗口正常创建、中文渲染正常(已截图验证)。
修复建议
/usr/lib是 64 位目录。建议改为探测ldconfig -p输出中的 64 位库实际路径,或按发行版布局选择/usr/lib64//usr/lib/x86_64-linux-gnu。xim-x-glibc(≥ 2.43);或mcpp run --system-glibc,本质上就是上面的 workaround:用/lib64/ld-linux-x86-64.so.2 --library-path /usr/lib64:...启动,并把/usr/lib64注入 dlopen 搜索路径)。glfw_app_main.cpp在main()开头安装glfwSetErrorCallback并向 stderr 打印错误;各初始化失败分支return -1前输出失败阶段名称。这一条能省掉用户 80% 的定位时间。附:诊断中用到的关键命令