玩式草子─ソフトウェアとたわむれる日々

第51回X、MesaそしてLLVMその2]

ふと気づくと朝晩はずいぶん冷えこむようになりました。ここ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*

ライブラリの中にはMipsAArch64(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用、r300r600radeonsiはAMD RADEON GPUの各世代用、svgaswrastはこれら以外の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)の方を調べることにしました。

複数のディレクトリ階層に配置されたファイル群から、あるキーワードを持つファイルだけを見つけだすような作業には、findgrepを組み合わせると便利です。

以下の例では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がビルドできました。

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のドライバとしてビルドされているようです。

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-server-1.14.2

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はどれくらい働いているのでしょうか? 次回はそのあたりを調べてみようと思います。

おすすめ記事

記事・ニュース一覧