PPxPのバージョンアップ、のつもりが


PPxP-0.98082821.tar.gzがリリースされたので、パッケージにしてみた。それまではppxp-0.98071017.tar.gzをベースにしていたのだが、細かなbug fixがなされた感じで、大きく機能が変わっているようなことはなさそうだ。PPxPはすでに安定して稼働しているので特に差し替える必要性はないのだが…

なんらかの業務などで使っていたりするのなら、安定しているもんを差し替えるような、ある意味では危険な行動は避けるべきなのかも知れないが、家のPCで使ってるだけだもんねぇ。ここはやはり差し替えて人柱となるのがコンダラの道だ。万が一、未発見のバグが潜んでいて、オレの環境でたまたま顔を出す可能性だって0じゃない。修正はできないまでも作者の方に報告だけでもできよう。なにしろ使わなくちゃバグも見付からないからねぇ。

てなワケで、もともと作ってあったPPxP-98071017のパッケージをベースに、tarballだけ新しいのに差し替えてサクッとパッケージを作り直してみた。楽勝ね楽勝。

とオレも思ってた。だがそううまくはいかなかったのだ。

PPxPにはqdialという、CUIながらも使いやすい設定ツールが含まれている。さらに、このqdial、ちゃんとLOCALEを見ていて、表示するメッセージからなにから、英語か日本語かを自動的に切り替えて動作する。仕組みとしては、ppxp.catなどのcatalogファイル(メッセージがインデクスと一緒に固まって入ってるバイナリ・ファイル)が、赤帽だと/usr/share/locale/ja_JP.ujisなどの日本語LOCALEのディレクトリにインストールされるようになっていて、環境変数LANGがja_JP.ujisなら日本語メッセージが表示されるワケだ。

が、どういうワケか、このcatalogファイルの生成に失敗しているらしい。なんじゃそら、と思ってディレクトリの中身を見てみると、展開したソースツリーの、ppxp/lib/catsの中にcoreが転がっている…

このcore吐いたの誰だ? Makefileをいろいろと眺めていると、どうやらこのcoreは、gencatというツールが吐いてるらしい。which gencat とすると、/usr/bin/gencat だそうだ。これって一体何するもの?? catalogファイルの作り方なんてよく知らなかったので、最初はワケがわからなかった。man gencatとしても何も出てこないしね。で、

rpm -qf /usr/bin/gencat として、一体どのパッケージに含まれて入ってきたのか表示させてみた。すると…

glibc-2.0.7-13_wcsmbs2 だって。

ギクギク…や〜な予感がしてきましたよあたしゃ(笑)

なんだよglibcがらみ?お手上げかぁ??そういや、PPxP-98071017は、まだwcsmbs環境にする前に作ったパッケージだったかも知れん。COMPAQのノートPCを取り出し、wcsmbsなしのglibc、つまりRedHatのCD-ROMに入ってるヤツに強制的に差し替え(glibc+wcsmbsで安定稼働してたのに…(泣))て、PPxP-98082821をmakeしてみることにする。

どうだったと思う? そう。そのとおり、サクッと何事もなくmakeが終ったのだ。はぅ〜(T_T)

コレはやはり、デバッガで見てみるしかなかろう。幸い、core吐いてるgencatそのものは独立した実行形式のファイルなので、デバッガで追うこと自体は難しくなさそうだし。

こういうシチュエーションでこそ、X Windowの真価が発揮できるってもんだ。メインの赤帽でxxgdbを開くのは当然として、COMPAQノート側のxxgdbだって、なにもわざわざノートPCの狭い画面に打ちにくいキーボードでせこせこ作業しないでも、メインの赤帽からtelnetして、COMPAQノート上でxxgdbを起動し、画面をメイン赤帽のディスプレイに飛ばせばいいんだもんね。WindowMakerのWorkSpace機能もフルに使って、このWorkSpaceはCOMPAQノートに割り当てよう、と決めてしまえば、WorkSpaceをAlt+数字キーで切り替えるだけで行ったり来たりできる。それこそ1ステップごとに変数の比較だってできるぜ。やっぱデバッグはこうありたいよなぁ。

で、xxgdbでgencatの動きを見てみることにする。PPxPのソースには、ja_JP.inという、catalogファイルの元データが ppxp/console/std/cats と ppxp/lib/cats に2つ含まれている。それぞれ、ppxp.cat と ppxpd.cat に最終的にはなるのだが、実際に問題となっているgencatそのものは、

gencat --new ja_JP.cat ja_JP.tmp

などという形式で使われて、coreを吐いているようだ。ではこのja_JP.tmpという入力ファイルはどこから来ているのか、というと、gencatの前のフェーズで、mkmsgというツールでmakeの最中に生成されている。さらにはこのmkmsgというツールそのものもppxpのソースツリーに含まれていて、ppxpのその他のソースと同様にmakeされ、ja_JP.inというファイルからja_JP.tmpを作るためだけに使われているようだ。

ja-JP.inは単純なテキストファイルで、形式としては

$ E_SETLINE
LINEが設定されていません
$ …
…

という風に、シンボルとメッセージがペアになったものが大量に収められている。であれば、メッセージが1つだけでも問題ないはずだ。試しにja-JP.inの最初の1組(上記のE_SETLINEのペア)を取り出してtest.inなどとデッチ上げ、mkmsgに食わすと首尾良くtest.tmpというファイルができた。それがこのtest.tmpだ。このファイルを、wcsmbsなしのCOMPAQノートでgencatに食わせると何事もなくtest.catができるのに対して、wcsmbsありのメイン赤帽ではcoreを吐いてしまう。wcsmbsな環境の人は、ぜひtest.tmpを落して

gencat --new test.cat test.tmp

としてどうなるか見てみて欲しい。オレの環境ではcoreを吐いた。glibcのバージョンは佐藤さんのところからもらってきたglibc-2.0.7-13_wcsmbs2.i386.rpm と wcsmbs-locale-0.4-1.i386.rpm だ。

デバッグオプション付きでgencatをコンパイルし、xxgdbで中を覗いてみると、現象そのものはすぐに見当がついた。結論としては、gencatのバグだと思う。

指定されたファイルから1行1行読み込んできては、メモリを確保し、シンボル、メッセージ、その他のデータを、確保したメモリに格納していく処理があるのだが、その際に、確保したメモリの初期化にモレがあるようだ。

シンボルその他を格納するための構造体が定義されていて、その構造体1個のバイト数分だけメモリをmallocで確保しているのだが、その構造体のメンバには文字列へのポインタがいくつか含まれている。構造体を置けるだけの領域は確かにメモリ上に確保されたが、そのメンバのポインタは、まだどこも指していない。実際の文字列を置く領域はまだ確保されていないワケだ。で、この後に続く処理で、文字列を実際に格納するための領域をあらためて確保するようになっているのだが…

文字列を格納する領域が確保済みかどうか、の判定を、そのポインタがNULLか否か、でやってるんだよね。でも、そのポインタに誰がどこでNULLをセットしてるの?というと、誰もしてないぞ(-_-#) mallocしたままやんけ。

だから、mallocした領域の中で、そのポインタのところ辺りがたまたま0の時はうまいこと判定されて、文字列の領域を新規に確保しようとするんだが、ゴミが入ってると、それをまっとうなアドレスだと思ってそこに文字列を突っ込もうとしてドカン。SIGSEGVでお亡くなりって寸法だ。

オレの書いてることだけ読むと、よくこれでいままで動いてたなぁ、って思うでしょ? オレもそう思った。で、COMPAQノートのwcsmbsなしの環境で、まったく同じgencatをxxgdbで追いかけてみると…

不思議なことに、確保する領域が常に0でクリアされているのだ。何回やっても、いっつもまっさらなメモリが確保されてくる。だから落ちないで動く。

なんなのこの違いは? やっぱ、glibcの違いかねぇ。でもさ、mallocって一応、要求された領域の確保に成功しても、そこが0でクリアされていることを保証しないってのが仕様だよねぇ。0で絶対にクリアしといてもらいたいときは、callocを使うんじゃなかったっけ?

どうしてこういう振る舞いになるのかは半信半疑だけど、やっぱりポインタを初期化しないのはまずかろう。てなワケでバータリーなパッチをでっち上げた。これをあててgencatをmakeしなおすと、core吐いてブチ落ちることはなくなる。なんとかPPxP-98082821のパッケージも無事に作ることができた。今オレのメイン赤帽で元気に動いている。使ってみようか、という人は、パッケージ置き場から持っていってね。

だが、ここでちょっと気になることが。今使っているglibcはglibc-2.0.7-13_wcsmbs2だ。リビジョン番号は実質的には13だね。中身はglibc_2.0.7r.orig.tar.gzというtarballをベースにしている。だが、RedHatのftpサイトのupdatesを見ると、すでにglibc-2.0.7-19というのが出ているし、Debianの方でも、すでにglibc_2.0.7t.orig.tar.gzをベースにしたパッケージが出ている。Debian-JPなんて、さらにコイツをwcsmbs化してたりもする。

ひょっとして、新しいglibcなら直ってるんじゃないのか?という疑問に抗し切れなくなったオレは、RedHatのサイトからglibc-2.0.7-19.src.rpmを、Debianのサイトからglibc_2.0.7t.orig.tar.gzを持ってきて、両方とも展開して中を見てみた。だけど全然変わってねぇし(-_-#)

こうなりゃやるこたひとつしかないよな(にやり)

そう。gencatの場当たりパッチをあて、Debian-JPで公開されているglibc_2.0.7t用のwcsmbs化パッチを持ってきて、現行のglibc-2.0.7-13_wcsmbs2に含まれる赤帽用のwcsmbs化パッチと見比べつつ、glibc-2.0.7-19.src.rpmをwcsmbs化する。

コレしかないだろ、重いコンダラ引いてることだし(笑) だけど、実際には、もうとっくに誰かがglibc-2.0.7-19.src.rpmのwcsmbs化なんてやってても良さそうだよねぇ。そう思ってwebやらftpやら検索してみたんだけど、引っかからなかった。佐藤さんのページにも置いてないみたいだし…

なもんで、やって見たよ試しに。手順としては簡単で、

1. glibc-2.0.7-19.src.rpmを展開する。

glibc-2.0.7-19.src.rpmは、glibc-2.0.7-980711.tar.gzという不思議な名前の、RedHat独自のtarballをベースにしているので、これを普通のglibc_2.0.7t.orig.tar.gzに置き換えた。この状態でrpm -bpとし、もともとのglibc-2.0.7-19.src.rpmに含まれていたパッチをみんなあてるところまでやらせてみた。

2. glibc-2.0.7-gafton.patchを無効にする。

ほとんどすべてのパッチがrejectされずに無事にあたり安心していると、どうやら複数あるパッチのうち、最後にあてられるglibc-2.0.7-gafton.patchというヤツだけがうまくない。よく見てみると、このパッチで変更する箇所はすでにglibc_2.0.7t.orig.tar.gzに含まれているようで、リバース・パッチするか?などと聞かれているようだ。SPECファイルをさらに編集して、glibc-2.0.7-gafton.patchをあてに行かないように修正してしまおう。

3. glibc_2.0.7t-1.wcsmbs.4.1.diff.gzをもらっちゃう。

Debian-JPで公開されているglibc+wcsmbsのソースから、glibc_2.0.7t-1.wcsmbs.4.1.diff.gzというキモのファイルをもらってきてあるので、コイツを2.の状態までパッチがあたったソースツリーに対してあててみた。すると、日本語で書かれたREADMEなどに始まって、Debian固有のパッチがいろいろ含まれていて、その辺でうまくパッチがあたらない部分がありまくり。パッチファイルそのものをエディタで見てみると、要するにDebian/hogehoge/fugafugaといったファイルが対象になっている部分は全て不要なのでバッサリとカットする。これで試すと、とりあえずパッチは1箇所を除いて全てあてることができた。このパッチをとりあえずglibc_2.0.7t-1.wcsmbs.4.1.rh.diffと名付けて保存しておく。

4. あたらなかったパッチの調整

どういうワケだか、赤帽ではglibcのソース・パッケージの中にglibc-linuxthreads-2.0.7pre5.tar.gzなどというthread関係のヘッダ・ファイルやソースが含まれていて、そこに対してあたらないパッチがあったのだ。Debianではおそらくthread関係は違うものを使っているのだろう。linuxthreads/sysdeps/pthread/libc-lock.hというのが問題のファイルで、__libc_cleanup_end()という引数付きマクロに対してパッチをあてようとするのだが、このマクロ自体が赤帽で使っているglibc-linuxthreads-2.0.7pre5.tar.gzなファイルには存在しないので、パッチに失敗してしまう。この部分はパッチファイルからエディタで削除して、見なかったことにした。

5. 一通りパッチをあて、コンパイルしてみる。

パッチファイルの調整は良さそうなので、rpm -bp で通してパッチが問題なくあたることを確認し、次は rpm -bc で通してコンパイルしてみる。が、ここで問題が。赤帽の linuxthreads/sysdeps/pthread/libc-lock.h の中に __libc_cleanup_end() マクロがないから、と4.の段階でパッチを修正したのだが、なんとこのマクロを使ってるお茶目なヤツがいたのだ。問題のファイルは stdio-common/vfscanf.c だ。そう。関数 vfscanf() の実体だ。この中に __libc_cleanup_end (0); などとしている箇所が4つほどあり、未解決なシンボルとしてリンクに失敗してしまう。むぅ〜。

6. glibc-2.0.7-13_wcsmbs2.src.rpm と比べてさらに微調整

どう処理していいものかわからなかったので、COMPAQノートの方に glibc-2.0.7-13_wcsmbs2.src.pm を展開し、パッチをあてるところまでやり、ソースツリーを展開させて中を見ることにした。だけど、全部のファイルにgrepかけても、__libc_cleanup_end なんてかすりもしないんだよね。__libc_cleanup_region_end なら山ほど見付かるんだけど… どうなってんだろ、と半信半疑でお互いの vfscanf.c を diff してみると、COMPAQノート側の vfscanf.c との違いはまさに __libc_cleanup_end があるかないかだけだった。あ、そうなの? この __libc_cleanup_end ってマクロ、使わなくてもいいのかな。だったらパッチファイルの最後に __libc_cleanup_end を消すような差分を追加しちゃえ。これでとりあえず glibc_2.0.7t-1.wcsmbs.4.1.rh.diff はいいだろう。

7. パッケージのできあがり

コンパイルも無事に最後まで通るようになったので、rpm -ba でパッケージを作成してみた。これも無事に終り、できあがったパッケージの中身を見てみると、特にファイルが足りないなどの不備はないようだ。まぁそりゃそうだよな。SPECファイルをほとんどいじってないんだもん。コンパイルさえ通ればほぼ同じ品揃えのパッケージができて当然だ。この辺はrpmのいいところだね。で、問題はテストなんだけど…

と、いう具合に、なんとかパッケージをつくるところまでこぎつけた。もともとはPPxPのパッケージ更新のために、gencatがcoreを吐くのを見付けたところから始まってるんだが(笑)

メインの赤帽に入れるのはちょっと(だいぶ)恐いが、かといって入れてみないことには始まらない。だもんで、gencatのデバッグのときにwcsmbsなしに戻された哀れなCOMPAQノートを例によって捨て石環境とし、できあがったばかりのglibc-2.0.7-19_wcsmbs.rpmや、その関連パッケージをブチ込んでみることにした。

インストールして、一応念のためリブートして様子を見るが、特におかしな挙動はしないようだ。GTK+1.0.4をワイドキャラクタ化したときに活躍したtest_mblenを、Xを立ち上げずにコンソールから実行してみた。wcsmbsが正しく動いていれば、LOCALEをサポートしている結果が出るはずた。どうだ…

locale type: LIBC_LOCALE
set to ja_JP.ujis
current locale: ja_JP.ujis
[ 2] 漢字
[ 1] ABC
set to fr_FR
current locale: fr_FR
[-1] 漢字
[ 1] ABC
set to C
current locale: C
[-1] 漢字
[ 1] ABC
set to ja_JP.ujis
current locale: ja_JP.ujis
[ 2] 漢字
[ 1] ABC

よし。動いてるな。

fr_FRやCの時に-1になっちゃうのはこれでいいんだろうか?というのは置いとくとして、こりゃWindowMakerもGTK+もまともに動きそうだってんで、Xを立ち上げてみる。WindowMakerが立ち上がり、画面の何もないところで右ボタンを押してみた。

おぉ、いいじゃんいいじゃん、ちゃんと日本語メニューも出るぜ。ダイアログボックスも日本語だ。大丈夫そうだなぁ。gimpを起動して、テキスト・エリアに日本語を入力してみたが、これも問題なかった。なんだ、モヂラも動くしXEmacsも動くじゃん。全然問題ねぇよ(ほんとかよ)。平気平気(笑)

てなワケで、メインの赤帽にも勝負でブチ込んでみた(笑) まだ3日くらいしか経ってないけど、普段とまったく同じように使って、特に不安定なそぶりも見えない。gencatのパッチも含めたので、catalogファイルの作成も問題なくなった。PPxPもサクサクッと通しでmakeできたしね。

できたパッケージは「パッケージ置場」には置かない。いったん置いてはみたものの、ディスク食うから消しちった。ソースだけ置いたから、使ってみたい人は自分でmakeしよう。glibcがコケたら被害は計り知れね〜からね。ブッ飛んでもいいマシンでどうぞ。

ま、でもとりあえずオレのとこでは動いているよ、glibc-2.0.7-19_wcsmbs.i386.rpm も。次は疑惑の関数vfscanf()を使った簡単なプログラムでも書いて、悲惨な動きをしないか見極めよう。パッチで黙らせてうやむやにしちったからね。

などと書いたのだが、PJE-0.3Alphaには、もっとまっとうな筋でwcsmbs化された、glibc-2.0.7-19が置いてある。αステータスとはいっても、PJEだし、それにどうせ人柱するならPJE版を使うほうが世のため人のためってもんだ。だもんで、オレも近々PJE-0.3Alpha版のglibc+wcsmbsに差し換えるつもり。


メール 戻る