ArchLinux 下 mpv: undefined symbol: vkCreateWaylandSurfaceKHR 问题解决
问题现象
- 在Gnome Terminal 直接执行
mpv filename.mp4
报错:
mpv: symbol lookup error: mpv: undefined symbol: vkCreateWaylandSurfaceKHR
- 使用 smplayer 打开同样的文件, 也是同样的报错.
解决办法
这个解决办法很简单.
但是我要说的是, 这个问题其实我花了很长时间才解决, 并且不是我自己想到的.
产生这个问题的时候, 一般是更新了某些软件之后.
当时我想, 这个是视频相关的, 可能跟显卡驱动相关. 但是后面发现, 即使重启系统, 或者重新编译mpv, 这个问题还是不能解决. 但是有时候重启系统后又能成功打开文件.
报错是 undefined symbol: vkCreateWaylandSurfaceKHR
, 但是其实这个问题跟 wayland 无关, 因为我用的是 N 卡 + X11.
搜索了一下, 得知 vkCreateWaylandSurfaceKHR
这个符号来自 libvulkan
, 但是本机有正常安装 libvulkan
.
❯ paru -Ss libvulkan | rg installed
extra/vulkan-icd-loader 1.2.203-1 [0B 481.45KiB] [Installed]
❯ ls -l /usr/lib/libvulkan*
lrwxrwxrwx root root 14 B Fri Jan 21 23:07:49 2022 /usr/lib/libvulkan.so ⇒ libvulkan.so.1
lrwxrwxrwx root root 20 B Fri Jan 21 23:07:49 2022 /usr/lib/libvulkan.so.1 ⇒ libvulkan.so.1.2.203
.rwxr-xr-x root root 470 KB Fri Jan 21 23:07:49 2022 /usr/lib/libvulkan.so.1.2.203
.rwxr-xr-x root root 11 MB Sat Mar 5 08:14:57 2022 /usr/lib/libvulkan_intel.so
❯ strings /usr/lib/libvulkan.so.1 | rg vkCreateWaylandSurfaceKHR
vkCreateWaylandSurfaceKHR
vkCreateWaylandSurfaceKHR
VK_KHR_wayland_surface extension not enabled. vkCreateWaylandSurfaceKHR not executed!
vkCreateWaylandSurfaceKHR: Invalid instance [VUID-vkCreateWaylandSurfaceKHR-instance-parameter]
- 重新编译 mpv 不能解决
- 问题只在有时候出现(条件不明确)
- undefined symbol 这种错误按以往的经常是特别容易解决的, 安装好对应的lib就好了, 但是现在的情况是, 有正确的 lib 安装了, 依旧报错
所以, 一时间找不到思路了.
直到有一天, 通过搜索看到了reddit 上一个回答. 终于找到了正确的答案.
This is because chrome provides a broken copy of vulkan which is loaded while it tries to execute mpv. The fix is to make a little wrapper at
/usr/local/bin/mpv
that preloads the system version of vulkan:#!/bin/sh export LD_PRELOAD=/usr/lib/libvulkan.so.1 exec /usr/bin/mpv "$@"
简单来说就是, Google Chrome 自带了一个 没有 vkCreateWaylandSurfaceKHR
符号的 libvulkan.so, 并且优先于系统的 libvulkan.so 加载了.
解决办法是通过一个脚本wrapper来启动mpv. 然后设置 smplayer 调用这个wrapper.
未解之迷
这个方法可以解决问题, 但是有一点老灯还没想明白.
为什么 Chrome 会影响整个系统的 LD_LIBRARY_PATH
环境变量 ? 一般来说, 通过一个 shell 脚本设置的变量, 不会影响其它 shell 的.
通过 kitty 执行 /usr/bin/env | rg LD_LIBRARY_PATH
无结果.
在 kitty 下面:
❯ ldd /usr/bin/mpv | rg libvulkan.so.1
libvulkan.so.1 => /usr/lib/libvulkan.so.1 (0x00007fa58880a000)
唯独在 gnome terminal 下得到的结果是 LD_LIBRARY_PATH=/opt/google/chrome:/opt/google/chrome/lib
自然而然, 加载的是 Google 的 /opt/google/chrome/libvulkan.so.1
❯ /usr/bin/env | rg LD_LIBRARY_PATH
LD_LIBRARY_PATH=/opt/google/chrome:/opt/google/chrome/lib
❯ ldd /usr/bin/mpv | rg libvulkan.so.1
libvulkan.so.1 => /opt/google/chrome/libvulkan.so.1 (0x00007fd41628b000)
所以, 现在的问题是, 为什么Chrome 启动脚本里的 LD_LIBRARY_PATH 会对 Gome Terminal 或者 smplayer 有效,
而在 kitty 等其它终端里面却获取不到?
尝试重启一次系统, 这次发现, 连 gnome terminal 下也没有 LD_LIBRARY_PATH 这个值了.
所以, 当gnome terminal 下能取到 LD_LIBRARY_PATH 这个值的时候, 发生了什么?
由于无法复现, 这里老灯也已经无法继续探索了.
/usr/lib/gnome-terminal-server (gnome terminal实际干活的进程) 和 smplayer 获取环境变量的方式, 和 kitty 有什么不同?
是否跟安装了 chrome-gnome-shell 有关系?
在 Linux 下面的 Google Chrome 启动实际上是通过 google-chrome 这个shell 脚本启动的 (edge 也一样, 只是重命名为了 microsoft-edge ), 在 ArchLinux 下这个路径是 /opt/google/chrome/google-chrome
其内容如下:
#!/bin/bash
#
# Copyright (c) 2011 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Let the wrapped binary know that it has been run through the wrapper.
export CHROME_WRAPPER="`readlink -f "$0"`"
HERE="`dirname "$CHROME_WRAPPER"`"
# We include some xdg utilities next to the binary, and we want to prefer them
# over the system versions when we know the system versions are very old. We
# detect whether the system xdg utilities are sufficiently new to be likely to
# work for us by looking for xdg-settings. If we find it, we leave $PATH alone,
# so that the system xdg utilities (including any distro patches) will be used.
if ! command -v xdg-settings &> /dev/null; then
# Old xdg utilities. Prepend $HERE to $PATH to use ours instead.
export PATH="$HERE:$PATH"
else
# Use system xdg utilities. But first create mimeapps.list if it doesn't
# exist; some systems have bugs in xdg-mime that make it fail without it.
xdg_app_dir="${XDG_DATA_HOME:-$HOME/.local/share/applications}"
mkdir -p "$xdg_app_dir"
[ -f "$xdg_app_dir/mimeapps.list" ] || touch "$xdg_app_dir/mimeapps.list"
fi
# Always use our versions of ffmpeg libs.
# This also makes RPMs find the compatibly-named library symlinks.
if [[ -n "$LD_LIBRARY_PATH" ]]; then
LD_LIBRARY_PATH="$HERE:$HERE/lib:$LD_LIBRARY_PATH"
else
LD_LIBRARY_PATH="$HERE:$HERE/lib"
fi
export LD_LIBRARY_PATH
export CHROME_VERSION_EXTRA="stable"
# We don't want bug-buddy intercepting our crashes. http://crbug.com/24120
export GNOME_DISABLE_CRASH_DIALOG=SET_BY_GOOGLE_CHROME
# Sanitize std{in,out,err} because they'll be shared with untrusted child
# processes (http://crbug.com/376567).
exec < /dev/null
exec > >(exec cat)
exec 2> >(exec cat >&2)
# Note: exec -a below is a bashism.
exec -a "$0" "$HERE/chrome" "$@"
搜索了系统其它地方(如 /etc/environment
, /etc/profile.d
等), 也没有发现设置 LD_LIBRARY_PATH
的, 唯一的只有这个脚本有 export LD_LIBRARY_PATH
, 而这个脚本只有在Chrome 启动的时候才会执行, 并且, 这样执行 export LD_LIBRARY_PATH
不应该影响到 gnome terminal 或 smplayer 的环境变量啊?