ふと気づくと朝晩はずいぶん冷えこむようになりました。ここ8年ぐらい使っているディスプレイは、室温が低いと、電源コードを何度か挿し直すまでスイッチが入りません。今朝、今季はじめてその症状が出て、いよいよ冬が近づいてきたことを実感しました(苦笑) 。
さて今回は、前回 ビルドしたLLVMを用いてMesaLibやX Window Systemをビルドしていく段階に進みます。
MesaLibのコンパイル
前回紹介したように、64ビット用ライブラリの配置に多少苦労したものの、MesaLibに必要なLLVMを用意することができました。LLVMは「コンパイラ基盤 」としてさまざまな機能をライブラリ・モジュールとして提供しており、今回ビルドしたLLVM-3.3では100を越えるライブラリ群が提供されています
$ ls /usr/lib64/libLLVM*
/usr/lib64/libLLVMAArch64AsmParser.so* /usr/lib64/libLLVMMipsCodeGen.so*
/usr/lib64/libLLVMAArch64AsmPrinter.so* /usr/lib64/libLLVMMipsDesc.so*
/usr/lib64/libLLVMAArch64CodeGen.so* /usr/lib64/libLLVMMipsDisassembler.so*
....
/usr/lib64/libLLVMMSP430Info.so* /usr/lib64/libLLVMipa.so*
/usr/lib64/libLLVMMipsAsmParser.so* /usr/lib64/libLLVMipo.so*
/usr/lib64/libLLVMMipsAsmPrinter.so*
ライブラリの中にはMips やAArch64 (64ビット版ARM) 、X86 といったCPU名を持つものが多数あり、LLVMの対応ハードウェアの多さがうかがえます。これらのうちR600 という名称を持つライブラリが、今回注目しているAMD(ATI) Radeon GPU用のコードを扱うライブラリです。
$ ls /usr/lib64/libLLVMR600*
/usr/lib64/libLLVMR600AsmPrinter.so* /usr/lib64/libLLVMR600Desc.so*
/usr/lib64/libLLVMR600CodeGen.so* /usr/lib64/libLLVMR600Info.so*
LLVM側の準備が整ったので、次にMesaLibのビルドにかかります。MesaLibにはconfigureスクリプトが付属していて機能設定は容易なものの、指定可能な機能の多さには戸惑います。今回は、BLFS(Beyond Linux From Scratch)の設定例を参考に、以下のような指定にしてみました。
OPT_CONFIG='--enable-texture-float
--enable-gles1 --enable-gles2
--enable-openvg --enable-osmesa --enable-xa --enable-gbm
--enable-gallium-egl --enable-gallium-gbm --enable-gallium-llvm
--enable-glx-tls --with-llvm-shared-libs
--with-egl-platforms=drm,x11
--with-gallium-drivers=nouveau,r300,r600,radeonsi,svga,swrast'
MesaLibではOpenGLが定義しているさまざまな機能を細かく指定できるためconfigureオプションは多岐にわたるものの、ほとんどは以前に使っていた設定をそのまま引きつぎ、Gallium3Dフレームワークに関する機能を新しく追加してみました。Gallium3D用にビルドしようとしているドライバのうち、nouveau がNVIDIAのGPU用、r300 、r600 、radeonsi はAMD RADEON GPUの各世代用、svga 、swrast はこれら以外のGPU用に、必要な機能をソフトウェア的にエミュレートするためのドライバです。
この指定でconfigureは問題なく通ったものの、いざMesaLib-9.2.2をビルドしようとすると、コンパイル作業は途中でエラー終了してしまいました。
CXX gallivm/lp_bld_debug.lo
In file included from /usr/include/llvm/Pass.h:32:0,
from /usr/include/llvm/Target/TargetMachine.h:18,
from gallivm/lp_bld_debug.cpp:31:
/usr/include/llvm/Support/Compiler.h:309:39: fatal error: sanitizer/msan_interface.h: No such file or directory
compilation terminated.
make[3]: *** [gallivm/lp_bld_debug.lo] エラー 1
...
エラーを起している/usr/include/llvm/Support/Compiler.hの309行目前後はこんなコードになっていました。
308 #if defined(HAVE_SANITIZER_MSAN_INTERFACE_H)
309 # include <sanitizer/msan_interface.h>
310 #else
311 # define __msan_allocated_memory(p, size)
312 # define __msan_unpoison(p, size)
313 #endif
コード自体は、「 HAVE_SANITIZER_MSAN_INTERFACE_Hが定義されていれば、<sanitizer/msan_interface.h>をインクルードせよ」 、というごく単純な指示で、このインクルードファイルが見つからないためエラーになっているようです。
「インクルードファイルが見つからない」というエラーは、たいていの場合、ファイルを探しに行くべきディレクトリの指定漏れが原因で、configure時に必要なディレクトリをCFLAGSあたりで指示してやれば解決します。
そう思ってmsan_interface.hがどこにあるかを探してみましたが、意外なことに/usr/include/以下には見つかりませんでした。
$ find /usr/include -name msan_interface.h
$
あれれ、とインストール済みのパッケージリストから探してみると、このファイルはclangパッケージの一部として /usr/lib64/clang/3.3/include/sanitizer/msan_interface.h にあることがわかりました。
$ grep msan_interface.h /var/log/packages/*
/var/log/packages/clang:usr/lib64/clang/3.3/include/sanitizer/msan_interface.h
この配置から考えるに、このファイルはClangと緊密に結びつけられていて、GCCでコンパイルする際にはインクルードすべきではなさそうです。そのため、このファイルを読み込むように指定しているマクロ定義(HAVE_SANITIZER_MSAN_INTERFACE_H)の方を調べることにしました。
複数のディレクトリ階層に配置されたファイル群から、あるキーワードを持つファイルだけを見つけだすような作業には、find とgrep を組み合わせると便利です。
以下の例ではfindの引数に-type f を指定して対象を一般ファイルに限り、-exec grep .. {} \; でそれらのファイルにgrepを実施して、grepで引っかかったファイルを-print で出力しています。
$ find /usr/include -type f -a -exec grep HAVE_SANITIZER_MSAN_INTERFACE_H {} \; -print
#define HAVE_SANITIZER_MSAN_INTERFACE_H 1
/usr/include/llvm/Config/llvm-config.h
#if defined(HAVE_SANITIZER_MSAN_INTERFACE_H)
/usr/include/llvm/Support/Compiler.h
この結果を見ると、/usr/include/llvm/Config/llvm-config.hでHAVE_SANITIZER_MSAN_INTERFACE_Hが1に定義されていることがわかりました。このllvm-config.hは、文字通りLLVMが利用する各種定義をまとめているファイルで、この定義は一番最後の部分にありました。
127 /* Define to 1 if you have the header file. */
128 #define HAVE_SANITIZER_MSAN_INTERFACE_H 1
129
コメントを見る限り、この定義を外せば、先に見たCompiler.hは、#else 以下にある自前の定義を使うようです。そこで、この定義をコメントアウトしてから再度コンパイルを行うと、無事MesaLib-9.2.2がビルドできました。
GCCではなくClangを使えばmsan_interface.hを利用できて、そのままでMesaLibをビルドできるのでは、と思われる方もいらっしゃるでしょう。実はそう考えて、手元でもClang/Clang++でMesaLibをビルドしてみました。その結果、Clang/Clang++でもMesaLibはエラーなくビルドできるものの、こうして作ったMesaLibを使うとOpenGLを利用するクライアント(glxinfoやKDE環境のkwinウィンドウマネージャ)が起動時にクラッシュしてしまい、正しく動作しませんでした。
このあたりは、ClangとMesaLibだけでなく、X Window SystemやQtあたりもからんでいる複雑な部分なので問題の詳しい切り分けはできていませんが、GCCとClangの間にはまだ多少の不整合が残っているような印象を持っています。
MesaLibは、OpenGLの機能を提供する各種ライブラリと共に、GPUを操作するドライバ類 を提供しており、それらは/usr/lib64/dri/以下に配置されます。今回ビルドしたMesaLib-9.2.2では、以下のようなドライバが作成されました。
$ ls /usr/lib64/dri/*so
/usr/lib64/dri/i915_dri.so* /usr/lib64/dri/r600_dri.so*
/usr/lib64/dri/i965_dri.so* /usr/lib64/dri/radeon_dri.so*
/usr/lib64/dri/nouveau_dri.so* /usr/lib64/dri/radeonsi_dri.so*
/usr/lib64/dri/nouveau_vieux_dri.so* /usr/lib64/dri/swrast_dri.so*
/usr/lib64/dri/r200_dri.so* /usr/lib64/dri/vmwgfx_dri.so*
/usr/lib64/dri/r300_dri.so*
今回注目しているr600_dri.soの依存関係を調べると、ちゃんとLLVMのライブラリをリンクしています。
$ ldd /usr/lib64/dri/r600_dri.so
linux-vdso.so.1 (0x00007fffbfffe000)
libz.so.1 => /usr/lib64/../lib64/libz.so.1 (0x00007f3a65130000)
libexpat.so.1 => /usr/lib64/../lib64/libexpat.so.1 (0x00007f3a64f06000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f3a64cb3000)
..
libLLVMipo.so => /usr/lib64/../lib64/libLLVMipo.so (0x00007f3a64241000)
libLLVMVectorize.so => /usr/lib64/../lib64/libLLVMVectorize.so (0x00007f3a64009000)
libLLVMBitReader.so => /usr/lib64/../lib64/libLLVMBitReader.so (0x00007f3a63de4000)
libLLVMR600CodeGen.so => /usr/lib64/../lib64/libLLVMR600CodeGen.so (0x00007f3a63b57000)
libLLVMR600Desc.so => /usr/lib64/../lib64/libLLVMR600Desc.so (0x00007f3a638f7000)
libLLVMR600Info.so => /usr/lib64/../lib64/libLLVMR600Info.so (0x00007f3a636f6000)
...
他はどうなってるかと、lddした結果をgrepするようなスクリプトを流してみました。
$ for i in /usr/lib64/dri/* ; do
chk=`ldd $i | grep LLVM`
if [ "$chk.x" != ".x" ]; then
echo $i;
fi
done
/usr/lib64/dri/nouveau_dri.so
/usr/lib64/dri/r300_dri.so
/usr/lib64/dri/r600_dri.so
/usr/lib64/dri/radeonsi_dri.so
/usr/lib64/dri/swrast_dri.so
/usr/lib64/dri/vmwgfx_dri.so
確かにconfigureオプションで指定したドライバはGallium3D/LLVMを使うようにビルドされており、それ以外は古いMesa3Dのドライバとしてビルドされているようです。
後で調べたところ、Intelのi915はGallium3Dベースのドライバも開発されており、configure時の--with-gallium-drivers=に'i915'を追加すればGallium3Dベースのドライバがビルドされます。一方、i965はGallium3D開発の初期のころにドライバが開発されていたものの、完成を見ないまま放棄され、現在は新たなアプローチが模索されているそうです。
X Window Systemのコンパイル
LLVM、MesaLibとビルドを進め、やっとX Window System に辿りつきました。前回も触れましたが、最近のX Window Systemは、構成するソフトウェアがそれぞれ個別のプロジェクトに分割してメンテナンスされており、最新の X Window Systemをビルドするためには、各ソフトウェアごとに最新版を揃える必要があります。X11R7.7 としてリリースされた際には、その時点での最新版がひとつのディレクトリにまとめて提供されていたのでダウンロードは簡単でしたが、自力で各ソフトウェアの最新版を集めようとすると、あちこちのディレクトリを探し回ることになります。
Xorgサーバは、周辺機器を認識して必要なドライバを自動的に組み込むLinuxカーネルと同様、認識したグラフィックカードやマウス、ジョイスティックなどに合わせて必要なドライバを自動的に組み込む 機能を持っています。
モジュール化されたドライバを動的に組み込む機能は便利なものの、それを実現するためにはカーネルやサーバ本体と各種ドライバのABI (Application Binary Interface)をきちんと合わせる必要があります。
Linuxカーネルの場合、主要なドライバはカーネルのソースコードに含まれていて、カーネルをビルドする際にあわせてビルドしてしまうので、ABIについて意識することはほとんどありません。一方、X Window Systemのように各部品がそれぞれ独立のプロジェクトとして開発、配布されている場合、両者のABIを合わせるには、各ソフトウェアを適切な順番 でビルド、インストールしていく必要があります。
X Window Systemの場合、まずはXのプロトコル定義ファイル や各種データ集 をインストールし、次にそれらの機能を提供するライブラリ を用意します。そしてライブラリの上に構築されるXorgサーバ をビルドしてから、Xorgサーバにリンクされる各種ドライバ をビルドしていく、という順番になります。
今回は、XのFTPサイトで公開されているソースコードのバージョンをX11R77時点のバージョンと比較して、バージョンが上がっているソフトウェアのみを拾ってみました。以下では今回更新したソフトウェアを簡単に紹介しておきます。
まずはXのプロトコル定義ファイルや設定ファイルです。X Window Systemが利用している基本プロトコルは1987年に公開されたバージョン11のままですが、その後も技術進歩やニーズに合わせてさまざまな機能が拡張プロトコルとして追加され、それらの更新は現在も続いています。
dri2proto-2.8, glproto-1.4.16, inputproto-2.3, randrproto-1.4.0,videoproto-2.3.2, xproto-7.0.24, xcursor-themes-1.0.4, xkeyboard-config-2.9
追加、修正されたこれらのプロトコルはライブラリによって実現されており、以下のようなライブラリが更新されていました。
libFS-1.0.5, libX11-1.6.1, libXau-1.0.8, libXext-1.3.2, libXi-1.7.1,libXinerama-1.1.3, libXp-1.0.2, libXrandr-1.4.1, libXres-1.0.7,libXt-1.1.4, libXtst-1.2.2, libXv-1.0.9, libXvMc-1.0.8,libXxf86dga-1.1.4, libXxf86vm-1.1.3, libdmx-1.1.3, libpciaccess-0.13.2
最近のX Window SystemではC言語とのバインディングにXCB(X C Binding)を使っており、XCB回りもバージョンが上がっていました。
xcb-proto-1.8, libxcb-1.9.1
これらを順番にビルド、インストールしてきて、やっとXorgサーバのビルドにかかれます。X11R77当時のXorgサーバは1.12系(1.12.2)でしたが、現在は1.14系になっています。
Xorgサーバが利用するドライバ類もずいぶん更新が進んでいます。以下のうちglamor-eglやlibvaはドライバではありませんが、最近のIntel用ドライバが必要としています。
glamor-egl-0.5.0, libva-1.2.1, libva-intel-driver-1.2.0, xf86-input-evdev-2.8.1, xf86-input-synaptics-1.7.1, xf86-input-vmmouse-13.0.0, xf86-input-wacom-0.22.1, xf86-video-ati-7.2.0, xf86-video-cirrus-1.5.2, xf86-video-fbdev-0.4.3, xf86-video-intel-2.21.15, xf86-video-mach64-6.9.4, xf86-video-mga-1.6.2, xf86-video-nouveau-1.0.9, xf86-video-openchrome-0.3.3, xf86-video-savage-2.3.6, xf86-video-sis-0.10.7, xf86-video-tdfx-1.4.5, xf86-video-vesa-2.3.2, xf86-video-vmaware-1.3.0.1
更新されているドライバのリストを眺めると、MGAやCirrusなどずいぶん懐しい名前も散見されます。変更箇所の詳細は確認していませんが、現在でもこれらのドライバのメンテナンスは続いているようです。もっとも最近では、グラフィックカードのメーカも整理・統合が進み、現在、使われているのはAMD(ATI) 、NVIDIA、Intelの3種ぐらいで、更新もこれらのドライバに集中しているようです。
クライアント側のアプリケーションもバージョンが上がっているものがありました。
bdftopcf-1.0.4, iceauth-1.0.6, mkfontscale-1.1.1, sessreg-1.0.8, xbacklight-1.2.0, xclock-1.0.6, xdpyinfo-1.3.1, xev-1.2.1, xhost-1.0.6, xkbutils-1.0.4, xmessage-1.0.4, xprop-1.2.2, xrandr-1.4.1, xrefresh-1.0.5, xset-1.2.3, xsetroot-1.1.1, xterm-296, xvinfo-1.1.2, xwd-1.0.6, xwininfo-1.1.3
これら昔から存在していたXクライアントは、新しく追加されたプロトコル等は利用していないはずなので、更新しなくても動作に問題ないとは思うものの、ChangeLog等を眺めるとコンパイラの警告メッセージを消したり、unlink()の代りにremove()を使ったり、といった細かな修正があれこれ入っているようなので、念のため追従しておきました。
以上紹介してきたソフトウェアを順にビルド、インストールした上で、X Window Systemを起動し直すと、無事、新しいXorgサーバ(1.14.2)が立ちあがりました。
とりあえずxdpyinfo で動作しているXサーバの情報を確認してみたところ、確かに新しくビルドしたXorgサーバ1.14.2で起動しています。
$ xdpyinfo
name of display: :0
version number: 11.0
vendor string: The X.Org Foundation
vendor release number: 11402000
X.Org version: 1.14.2
..
number of extensions: 27
BIG-REQUESTS
Composite
DAMAGE
DOUBLE-BUFFER
DPMS
DRI2
GLX
....
さて、それでは今回苦労してビルドしたMesaLibはどれくらい働いているのでしょうか? 次回はそのあたりを調べてみようと思います。