本文首發於博客園(https://www.cnblogs.com/ArrowKeys/p/12152602.html),禁止轉載。
更新
已發現該錯誤的根本原因:官方網站上直接下載的CUDA安裝包安裝時會直接覆蓋系統的libGL.so等相關文件。
解決方案:參考 https://github.com/ValveSoftware/steam-for-linux/issues/5778#issuecomment-575334890
其中提到的nvidia-drivers PPA是 http://ppa.launchpad.net/graphics-drivers/ppa
背景
本機環境:NVIDIA GTX 1660Ti,Ubuntu 18.04,CUDA 10.2(顯卡驅動版本440.33.01)
筆者按照wszqkzqk的方案安裝了最新的deepin QQ(9.1.8),安裝了CUDA 10.2(自帶440.33.01顯卡驅動)並切換至NVIDIA模式登錄系統。此時發現QQ無法正常啟動。
經過若干驗證,基本確定如下現象:
-
在Intel核顯模式下可正常啟動QQ 8.9.4和QQ 9.1.8。
-
在NVIDIA獨顯模式下可正常啟動QQ 8.9.4,無法啟動QQ 9.1.8。
為了觀察錯誤情況,在命令行中使用/opt/deepinwine/apps/Deepin-QQ/run.sh啟動之,打印出錯誤如下:libGL error: No matching fbConfigs or visuals found
libGL error: failed to load driver: swrast
X Error of failed request: GLXBadContext
Major opcode of failed request: 152 (GLX)
Minor opcode of failed request: 6 (X_GLXIsDirect)
Serial number of failed request: 204
Current serial number in output stream: 203
探索
錯誤報告明顯指向libGL.so的錯誤使用。結合兩種顯卡模式下的不同表現,猜測問題出在使用NVIDIA顯卡時不應使用現在版本的libGL.so,即可能兩顯卡各有自己適用的libGL.so。(順便吐槽一句,之前的960M卡就沒出問題……)
Deepin QQ調用的是32位庫,於是嘗試將軟鏈接/usr/lib/i386-linux-gnu/libGL.so.1指向系統中的各種libGL庫文件,但都沒有解決問題。
偶然發現
由於筆者每次修改libGL.so.1的指向時都使用如下命令:
mv libGL.so.1 libGL.so.1.backup
ln -s xxx libGL.so.1
某次修改時,因為手誤,僅僅進行了mv就再次運行QQ,但意外地成功啟動了。至於QQ是直接忽略了該庫還是按加載優先級順序加載了另一優先級較低的庫,筆者至今未探明,但這一發現使筆者找到了一種比較hack的方案以啟動QQ。
不完美但可用
為了避免妨礙其他程序正常運行,顯然不能直接刪除libGL.so.1。方便起見,也不能每次運行QQ前都手動mv一遍,等成功啟動后再改回來。能直接想到的方案就是寫一個啟動腳本,每次先mv再啟動QQ再mv,但在libGL.so.1所在目錄下操作需要sudo獲取權限,這也算是另一種麻煩。想要在加載時唯獨跳過這個so文件也沒有合適的環境變量可供使用。最后可用的方案就是劫持加載so的過程(即dlopen()函數),使得QQ當且僅當加載libGL.so.1時加載失敗。
撰寫mydlopen.c如下(參考StackOverflow的某問題,對代碼稍作了格式上的修改):
#define _GNU_SOURCE
#include <dlfcn.h>
#include <string.h>
void *dlopen (char const *Fnm, int Flg)
{
void *(*real_dlopen) (char const *, int);
*(void**)(&real_dlopen) = dlsym(RTLD_NEXT, "dlopen");
if (strcmp("libGL.so.1", Fnm) == 0) {
return NULL;
} else {
return real_dlopen(Fnm, Flg);
}
}
使用命令gcc mydlopen.c -fPIC -ldl -shared -m32 -o mydlopen32.so將其編譯為32位動態鏈接庫(需gcc支持multilib,可通過apt-get install gcc-7-multilib安裝支持)。
最后,使用env LD_PRELOAD=/path/to/mydlopen32.so /opt/deepinwine/apps/Deepin-QQ/run.sh運行QQ。(應用程序菜單中的QQ啟動命令可在/usr/share/applications/deepin.com.qq.im.desktop文件中修改)
