でぶあん 2003年 夏 原稿 VIA C3 Ezla を Debian でサポートする ごとむ ○ はじめに 今回は VIA C3 Ezla プロセッサ話をネタにすることにした。 話の発端は、VIA C3 プロセッサ上で Debian 最新版 (sid, 次期 sarge) を使 用すると、特定のライブラリをリンクしたバイナリ (具体的には /usr/bin/ssh など) を使用すると、プログラムが必ず Illegal Instruction で落ちてしまうというバグが Debian BTS (Bug Tracking System) に報告さ れたことである。どうも原因が glibc (GNU C Library) にあるらしい、と いうことで (もしくはこの手のライブラリ問題は、Debian ではとにかくまず glibc パッケージに押しつける傾向にあるようだ :-) バグ潰しの一貫として 取り組み始めた。しかし、話は単なるバグに留まらない難しい問題であること が後に分かることになる。 ○ VIA C3 プロセッサ まず、話の主題となる VIA 社製プロセッサ C3 Ezla を紹介しよう。 これは Intel 互換の CPU であり、省電力や発熱の少なさなどから、 静音マシンや小型マシンとしてマニアに一定の人気のあるプロセッサである。 この VIA C3 プロセッサ、表向きは i686 クラスプロセッサとして売られており、 事実プロセッサの情報を引き出すと 686 であると報告される。 しかし、この VIA C3 プロセッサ、i386, i486, i586 互換プロセッサとして 見た場合は問題ないのだが、i686 互換プロセッサとしてみると実は完全な i686 ではない。特に「CMOV 命令」がサポートされていないのだ。 ○ CMOV 命令って? CMOV (Conditional MOVe) 命令とは、i686 クラスプロセッサに新規追加され たインストラクションであり、伝え聞くところによるとi686 クラスプロセッ サとして追加された命令のうち、もっとも性能に寄与するとされる重要なもの らしい。ただし、686 の仕様書によれば、CMOVはあくまでも686であるときに 必須とはされないオプショナルな命令であるとのこと。そのため、VIA はチッ プダイの面積を削るためか、それとも考慮していなかったのか、この命令をサ ポートしていない。 なお、lkml (linux-kernel mailing list) に流れていた情報であるが、 VIA C3 用にコードを生成する場合、実は i486 向け最適化がもっと良い性能 を発揮するらしい。このあたりからして、Ezla コアの内部はそもそも 本当にi686 プロセッサと呼べるレベルにあるのか疑問符がつく。 ○ 「CMOV 命令」はいつ使用されるの? さて、この CMOV 命令、686 とそれ以降でしか使用できないため、386-586 マ シンで動作させることも考え、通常のプログラムをコンパイルしても生成され ない。 しかし、gcc (GNU Compiler Collection) は i686 アーキテクチャ向けにプロ グラムを最適化指定でコンパイルしたとき、この CMOV 命令を使ってより性能 を出そうと試みる。例えば Linux kernel 2.5.75 を PentiumIII 向けに gcc 3.3.1 を使用してコンパイルして、実際に CMOV 命令が使用されているかどう かを確認することができる (PentiumIII も 686 アーキテクチャファミリであ る)。 rhodes:~> grep CONFIG_MPENTIUMIII kernel/linux-2.5.75/.config CONFIG_MPENTIUMIII=y rhodes:~> ls -al kernel/linux-2.5.75/vmlinux -rwxr-xr-x 1 gotom gotom 7118169 2003-07-12 01:34 kernel/linux-2.5.75/vmlinux rhodes:~> objdump -d kernel/linux-2.5.75/vmlinux | grep cmov c0105711: 0f 43 85 80 fa ff ff cmovae 0xfffffa80(%ebp),%eax c010573e: 0f 46 8d 80 fa ff ff cmovbe 0xfffffa80(%ebp),%ecx ... rhodes:~> objdump -d kernel/linux-2.5.75/vmlinux | grep cmov | wc -l 4403 多用されている様子が分かる。 ○ なぜ特定のライブラリをリンクしたバイナリだけ C3 上で問題が発生したの? さて、先述したとおり VIA C3 は 686 プロセッサとうたっているにも関わらず、 CMOV 命令をサポートしていない。もし、CMOV 命令を含むバイナリを 動作させようとすると、 > ssh Illugal Instruction 「不正な命令」という理由で落ちてしまう。より詳しく調べると、 これは CMOV 命令を含むコードが実行されたからであることが分かる。 そして、これこそがVIA C3 上で特定のバイナリを動作させたときに 落ちる現象だったのだ。 しかし、/usr/bin/ssh 自体は、別に 686 向けに最適化されているわけではな い。ssh バイナリ自体を objdump で逆アセンブルしても CMOV 命令は使われていない。 rhodes:~> objdump -d /usr/bin/ssh | grep -i cmov 0 原因は、実は /usr/bin/ssh にリンクしたライブラリにあった。 rhodes:~> ldd /usr/bin/ssh ... libcrypto.so.0.9.7 => /usr/lib/i686/libcrypto.so.0.9.7 (0x4003d000) libc.so.6 => /lib/libc.so.6 (0x4012e000) libdl.so.2 => /lib/libdl.so.2 (0x4023e000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000) この中に 含まれる libcrypto.so.0.9.7 の置いてあるディレクトリに 注目して欲しい。"/usr/lib/i686" とある。実は /usr/lib/ にも libcrypto.so は置いてあるが、686 アーキテクチャ向けに最適化した ライブラリをi686 ディレクトリの下に置き、それを glibc のダイナミックローダ に優先的にロードさせることができるのだ (このように、同じ命令アーキテクチャを持つ CPU の、チップ毎にある 機能の差異のことを Hardware Capability、特に glibc では略して HWCAP と呼ぶ)。 そして、i686 と称する VIA C3 上で、CMOV 命令を持つ i686 最適化 ライブラリをロードすることで、Illegal Instruction が発生していたのだ。 ○ 問題は分かった。では解決策は? 以上で VIA C3 上でのみ、CMOV 命令を含むコードを実行してIllegal Instruction で止まってしまう問題は分かった。問題が分かれば後は直すだけ なのだが…実はこれが一筋縄にはいかないのだ。というより、問題に関係する ソフトウェアの、それぞれどれも悪いといえば悪いし、どれも悪くないと言え ば悪くないといった状況なのだ。 そこで、簡単に「問題の原因」と、どうしてその方法がとられな かったのかの「反論」を示す。 うまくいかないものを読んでもらっても、なかなか理解しにくいとは 思うが、まあおつき合い願いたい。 (1) 問題の原因: gcc が 686 最適化で CMOV 命令を生成してしまうのが問題だ。 反論: 確かに CMOV 命令は i686 でオプショナルな命令であるため、 gcc が 686 最適化で CMOV 命令を生成するのはおかしい。 しかし gcc チームが言うには、 i686 プロセッサを使うマシンのうち、VIA C3 を使う台数は全体のせ いぜい0.01%もいかないはずであるから、残り99.99%が最適 化を犠牲にするのはおかしいし、CMOV は性能向上のための貴重な 命令である。 確かに、そもそも 686 と言えば intel 製 CPU であって、VIA 社の模倣 CPU の怠慢をかぶるのは筋違いであると言える。 そこで gcc 3.3 には VIA C3 用最適化命令が用意されているが、 依然として i686 用最適化を行なうと CMOV 命令が使用される。 (2) 問題の原因: glibc が CMOV 命令に最適化されているかどうかを判別させ、VIA C3 の時だけ glibc に何らかの方法で CMOV 命令のライブラリをロードさせないようにする 反論: ダイナミックライブラリをロードするのは glibc の仕事である。 今回の問題は、CMOV 命令込みの i686 最適化ライブラリをロードしてしまった glibc であるので、glibc にロードさせないようなマジックを用意してやればよい。 実はすでにそのような仕組み(HWCAP) が用意されている。i386 向けには 今のところ /usr/lib/mmx にライブラリを置くことで MMX に対応した プロセッサにのみライブラリがロードされるよう、チェックされるように なっているのだ。そこで、cmov ディレクトリも追加でチェックすれば良い。 ただし、この方法は LD_LIBRARY_PATH 環境変数が使用されている場合に、 チェックするディレクトリが増えると言う問題がある(後述)。 (3) 問題の原因: カーネルが VIA C3 を i686 と報告するのが間違いではないのだろうか。 反論: Linux カーネルは、glibc が ELF ライブラリをロードするときに、 現在動作しているアーキテクチャが何であるのかを教えてあげるよう になっている。glibc が VIA C3 を i686 と認識してしまうのは、カー ネルのせいなのだ。従って、カーネルが VIA C3 を i586 クラスプロ セッサであると glibc に教えてあげれば、問題は解決する。 端的に言えば、/proc/cpuinfo で i586 と報告してしまえ、ということだ。 しかし、Linux カーネルの VIA C3 をメンテナンスしている Dave Jones はこの意見を却下している。彼によれば、VIA C3 はまがりな りにも i686 の機能をいくつか持っており、CMOV 命令問題だけで i586 と報告すべきではないとしている。確かにそのとおりだ。 (4) 問題の原因: ライブラリが自分自身で i686 最適化を扱うべきではないのか。 反論: mplayer など、最適化命令を明示的に使用しているバイナリは、自分 自身で現在どのプロセッサ上で動作し、どの最適化バイナリを使用す れば良いかを判断している。 しかし、ライブラリは CMOV 命令の問題など想定していないため、最 適化されているかどうかの初期化コードをいれてないことが普通であ る。今回問題になったライブラリはセキュリティ関係のコードである ため、頻繁に呼び出され、かつ速度が要求されるために、Debian メ ンテナが最適化されたライブラリとして使えるよう望んだことが原因 でもある。 しかし、自分自身で最適化用アセンブラコードを持っていれば別であ るが、この問題は他のライブラリでも問題になりそうであるため、自 分自身で最適化を扱うのは必ずしも正しい解決方法ではない。 (5) 問題の原因: Debian で i686 を2種類に分け、通常の i686 と VIA C3 向け i686 を作成すれば良い。 反論: biarch として i686 と i686-viac3 を用意する、というものだ。も ちろん、わずかな VIA C3 ユーザのために用意するのは、あまりにリ ソースを無駄にしてしまうため、ダメなのは明らかだ。 (6) 問題の原因: VIA C3 にだけ i686 最適化バイナリを入れないような仕掛けを用意すべきではないか。 反論: これは、具体的には dpkg を最適化バイナリ対応し、インストールす るかどうかを判別させるものである。確かに真っ当な方法であり、現 在 amd64 (x86-64) に対応させるべく、このようなアーキテクチャ種 別毎のインストール方法が検討されているが、技術的に議論の余地が 多く、今すぐ使うことはできない状態である。また、dpkg が明示的 に現在動作中のプロセッサが VIA C3 かどうかを /proc/cpuinfo を 見なければならないし、また i686 マシンでインストールしたあと、 CPU だけ取り替えたケースに対応できない欠点がある。 なお、Alan Cox によると、RedHat がとっている方法が、インストー ラでVIA C3かどうかを判別し、その場合は i686 最適化したバイナリ やライブラリをインストールさせないというものである(調べる過程 で、該当コードにはtypoがあることを発見してしまったので、ちゃん と動いているかどうか知らないが…)。しかし、インストーラのレベ ルで制限をかけても、apt-get でアップグレードしたときどうするの かという問題は依然残ったままであり、Debianではとれない方法であ る。 (4) 問題の原因: CMOV 命令を持たない VIA C3 がマーケティング要素から、i686 と名乗っているのがそもそも間違っている。VIA が C3 を回収して CMOV 命令をつけろ! 反論: そうあって欲しいが、もちろん、これは無理 :-) 各ソフトウェアのメンテナ全員にそれぞれ真っ当な主張があり、 Debian 側のどこかで妥協しなければならない状況である。 ○ では、今回はどうした? 以上を鑑みて、私が Philip Bluhndell (debian-glibc チームメンバであると ともに ARM の glibc upstream ポートメンテナ) と決定した方法は、 - Debian glibc パッケージのローカルパッチとして、i386 HWCAP に CMOV 命令を追加し、libcrypto.so を /usr/lib/i686/cmov においてもらう。 であった。先ほどで言うと (2) の方法である。 Debian で CPU 向けの最適化がとりわけ望まれているのは libcrypto だけであり、現在のところ i686 最適化を行なうライブラリがこれだけしかないからである。 ちなみに、ここまで長々と書いたが、これを実現する glibc 向け パッチはたったの1行である。 --- sysdeps/unix/sysv/linux/i386/dl-procinfo.h +++ sysdeps/unix/sysv/linux/i386/dl-procinfo.h.new @@ -92,7 +92,7 @@ HWCAP_I386_AMD3D = 1 << 31, /* XXX Which others to add here? */ - HWCAP_IMPORTANT = (HWCAP_I386_MMX) + HWCAP_IMPORTANT = (HWCAP_I386_MMX | HWCAP_I386_CMOV) HWCAP へ、前述の MMX の他に CMOV もハードコードで追加する方法である (実は glibc はバグっていて、上記だけではちゃん と動作しない。別のパッチが必要なのだがここでは触れない。詳しく知りたければDebian glibc ソースパッケージをとってきて debian/patches/glibc23-cmov.dpatch を見るべし)。 なお、このパッチを投げたところ Ulrich Drepper (glibc の取りまとめ役の1 人) や Jakub Jelenik (prelink の作者であり RedHat glibc .rpm メンテナ) からは、LD_LIBRARY_PATH のディレクトリ検索時間がかかるので困るといった理由で reject されてしまった。 これは、チェックする最適化ディレクトリの組合せがもともと i686, i686/mmx, mmx の3通りだけだったのが、 i686, i686/mmx, mmx, i686/cmov, i686/mmx/cmov, cmov, mmx/cmov に増えてしまうという問題があることにある。RedHat9 では、tls (Thread Local Storage) と呼ばれるディレクトリも検索パスに追加しているため、 組合せ爆発が無視できないと判断したのだろう。 Debian glibc は RedHat のように TLS パスを検索対象に (現在のところは) 追加していないこと、ほぼ全てのユーザはlookup の回数が 増えたことに気づかないと考えられるため、この問題に目をつぶることにした (実際、locale や他のライブラリの lookup の失敗回数の方が圧倒的に多く、 cmov は今のところは比率的に誤差とも言える数である)。 ○ まとめてみよう 複雑なので、もう一度まとめてみよう: - VIA はダイ面積とマーケティングのために CMOV サポートしていない C3 を i686 プロセッサと主張している - gcc メンテナは、VIA C3 ごときのために gcc の i686 最適化から CMOV サポートを外すのをいやがっている - linux kernel メンテナは、あくまでこれは i686 であると主張している - glibc メンテナは、VIA C3 のためにダイナミックライブラリのロードを遅くすることをいやがっている - Debian は、いつでもパッケージアップグレードを考えなければならない。従って、RedHat のようにインストール時にアーキテクチャを決めてしまうような方式をとれない。 そして、今回は Debian の glibc にローカルパッチをあて、libcrypto.so を i686/cmov ディレクトリに置くことで問題を回避した。 ○ お後がよろしいようで VIA は、C3 Ezla プロセッサの後継として Nehemiah コアベースの新プロセッ サを発表した。そして、これは待望の CMOV 命令に対応している。 VIA C3 が CMOV 命令を省いたことに端を発するこの問題に対し、 linux-kernel, glibc, gcc の各 upstream コミュニティは、いずれも問題が 自分のところにはないことを主張した。そして、それぞれの言い分はどれも的 を得ている。しかし、ディストリビューションから見た場合、これほど解決に 困る状況はない。今回は我々が妥協する方法をとるしかなかった。 何はともあれ、問題は解決したのではあるが、これで終ったわけではない。私 のとった方法では、前述の性能劣化問題がある (誰も気づいていないと思うが :-)。今後の検討事項である。