# 運作古老的 QPE 執行檔 ## Qt Palmtop Environment Qt 是由 Trolltech 公司(現為 Qt Group)開發的跨平台程式開發框架,其跨平台特性使得使用 Qt 撰寫的軟體,可在 Microsoft Windows, macOS, Linux,和 BSD 家族作業系統執行。Trolltech 也曾針對嵌入式環境推出 Qt/Embedded 產品,與桌面版本不同,Qt/Embedded 取代 X Server 和 X Library 等元件。Trolltech 公司推出針對 PDA 軟體的整體解決方案為 QPE (Qt Palmtop Environment),從底層的圖形使用者介面、視窗管理器、軟體鍵盤到上層的個人資訊管理、檔案瀏覽器、多媒體等,全部一手包辦。 ## 體驗古老的執行檔 SourceForge 網站保存 Trolltech 公司舊版的 [QPE](https://sourceforge.net/projects/qpe/) 檔案,其中 [QPE-1.3.1 ](https://sourceforge.net/projects/qpe/files/QPE/1.3.1/) 的執行檔距今超過 23 年,如今的軟硬體已有顯著差異,我們能否運作呢?以主流的 x86-64 架構來說,我們可在 Debian / Ubuntu 安裝以下套件: ```shell $ sudo apt install lib32stdc++6 ``` 首先要確保 X 視窗系統正確運作,接著取得 `qpe-1.3.1-bin-x86.tar.gz`,本文假設在 `/tmp` 操作以下命令: ```shell $ tar zxvf qpe-1.3.1-bin-x86.tar.gz $ cd qpe-1.3.1-bin-x86 ``` 上述目錄中,包含一個名為 `run_demo` 的腳本,執行後會發現欠缺 `libstdc++-libc6.1-2.so.3` 這個動態連結函式庫,該檔案距今已有 24 年的歷史,若貿然在 Google 茫茫大海搜尋,大概只會落得一場空。既然我們只想執行古老的 QPE 執行檔,不用理會程式的細部相容性議題。觀察稍早安裝的 `lib32stdc++6` 套件: ```shell $ dpkg -L lib32stdc++6 /usr/lib32/libstdc++.so.6.0.32 /usr/lib32/libstdc++.so.6 ... $ file /usr/lib32/libstdc++.so.6.0.32 /usr/lib32/libstdc++.so.6.0.32: ELF 32-bit LSB shared object, Intel 80386, version 1 (GNU/Linux), dynamically linked, BuildID[sha1]=f039e7071f444bdad22464673d49ba36ca7ffb36, stripped ``` `/usr/lib32/libstdc++.so.6.0.32` 是預先編譯、符合 i386 ABI 規範的 Linux 動態連結函式庫,提供 C++ 標準函式庫和執行時期所需的符號。我們嘗試用此取代 QPE-1.3.1 執行檔預期的 `libstdc++-libc6.1-2.so.3`: ```shell $ ln -s /usr/lib32/libstdc++.so.6.0.32 lib/libstdc++-libc6.1-2.so.3 ``` 接著執行 `./run_demo`,很快會看到以下錯誤訊息: ``` ./qpe: symbol lookup error: /tmp/qpe-1.3.1-bin-x86/lib/libqte.so.2: undefined symbol: __builtin_vec_new ``` 這個 `__builtin_vec_new` 符號是早期 GCC 針對 C++ 程式提供的符號,現行的 GNU/Linux 已闕失。為此,我們可善用 `LD_PRELOAD` 機制,建立一個名為 `wrap.c` 的檔案,其內容如下: ```c #include <malloc.h> void *__builtin_new(int size) { return malloc(size); } void *__builtin_vec_new(int size) { return __builtin_new(size); } void __builtin_delete(void *ptr) { free(ptr); } void __builtin_vec_delete(void *ptr) { __builtin_delete(ptr); } ``` 編譯為 32 位元的動態連結函式庫: ```shell gcc -m32 -o wrap.so -shared wrap.c ``` 接著執行以下命令來啟動 QPE: ```shell $ LD_PRELOAD=/tmp/qpe-1.3.1-bin-x86/wrap.so ./run_demo ``` 注意 `/tmp/qpe-1.3.1-bin-x86/wrap.so` 是上述編譯的動態連結函式庫所在的路徑。接著,終於啟動 QPE! ![QPE](https://hackmd.io/_uploads/SyzvOeltR.png =60%x)