November 28, 2006
[雑記] プリズン・ブレイク
2006 年 12 月 2 日からプリズン・ブレイク (英題: Prison Break) の vol.8 〜 12 の DVD がレンタル開始だそうです.
このドラマは今月 9 日の夜中に日テレで放送(この日は所用で東京に居ました)されていた第 6 話を偶然観ただけなのですが,それだけで全部まとめて観たい! なんて思ってしまいました.というわけでそのうちレンタル屋で借りてくることにします.
November 24, 2006
[雑記] Solid State Society
今日ようやく攻殻機動隊 STAND ALONE COMPLEX Solid State Societyの DVD が手元に届きました.
スカパーのパーフェクトチョイス160で放送されてから 3 ヶ月も経ってますね.スカパーを視聴していない私は DVD を買うかレンタルするかしないと観られなかったわけですが,これでやっと観られるようになったわけです.
パッケージの外観および中身を携帯のカメラで撮影したものを載せておきます.こんな感じでした.
(正面)
(外袋開封)
(中身取り出し後)
ゆっくり鑑賞させて頂くことにします.
November 12, 2006
[C++] 参照で例外を捕まえる
More Effective C++ 日本語版の 65 ページに参照で例外を捕まえる
という項があります.初めてここを読んだときは驚きました.この本に書かれている通り,例外オブジェクトを値渡しすると,スライシングが発生する上に 2 度コピーコンストラクタが呼び出されるので,できれば参照で渡したいと思っていました.しかし,関数ローカルな例外オブジェクトを参照で渡してもいいのだろうか,という疑問も同時に浮上.
夜も眠れぬくらい非常に気になったので,その分の睡眠時間を削って,手近な環境(g++-4.1.1, i686-pc-linux-gnu)でテストしてみることに.
以下の簡単なコードでテストします.
class E {
public:
int a;
E() { a = 0x64; }
};
int func1() { throw E(); }
int main() {
int a = 0;
try { func1(); }
catch (E& e) { a = e.a; }
return 0;
}
これを g++ -O0 でコンパイルした後にリンクして,でき上がった ELF を逆アセンブルした結果が次の通りです.ただし,_fini などの余分な関数は除いています.
080485c0 <_Z5func1v>:
80485c0: 55 push ebp
80485c1: 89 e5 mov ebp,esp
80485c3: 53 push ebx
80485c4: 83 ec 14 sub esp,0x14
80485c7: c7 04 24 04 00 00 00 mov DWORD PTR [esp],0x4
80485ce: e8 dd fe ff ff call 80484b0 <__cxa_allocate_exception@plt>
80485d3: 89 c3 mov ebx,eax
80485d5: 89 d8 mov eax,ebx
80485d7: 89 04 24 mov DWORD PTR [esp],eax
80485da: e8 75 00 00 00 call 8048654 <_ZN1EC1Ev>
80485df: c7 44 24 08 00 00 00 mov DWORD PTR [esp+8],0x0
80485e6: 00
80485e7: c7 44 24 04 1c 87 04 mov DWORD PTR [esp+4],0x804871c
80485ee: 08
80485ef: 89 1c 24 mov DWORD PTR [esp],ebx
80485f2: e8 f9 fe ff ff call 80484f0 <__cxa_throw@plt>
80485f7: 90 nop
080485f8 <main>:
80485f8: 8d 4c 24 04 lea ecx,[esp+4]
80485fc: 83 e4 f0 and esp,0xfffffff0
80485ff: ff 71 fc push DWORD PTR [ecx-4]
8048602: 55 push ebp
8048603: 89 e5 mov ebp,esp
8048605: 51 push ecx
8048606: 83 ec 24 sub esp,0x24
8048609: c7 45 f4 00 00 00 00 mov DWORD PTR [ebp-12],0x0
8048610: e8 ab ff ff ff call 80485c0 <_Z5func1v>
8048615: eb 2e jmp 8048645 <main+0x4d>
8048617: 89 45 e8 mov DWORD PTR [ebp-24],eax
804861a: 83 fa 01 cmp edx,0x1
804861d: 74 0b je 804862a <main+0x32>
804861f: 8b 45 e8 mov eax,DWORD PTR [ebp-24]
8048622: 89 04 24 mov DWORD PTR [esp],eax
8048625: e8 b6 fe ff ff call 80484e0 <_Unwind_Resume@plt>
804862a: 8b 45 e8 mov eax,DWORD PTR [ebp-24]
804862d: 89 04 24 mov DWORD PTR [esp],eax
8048630: e8 8b fe ff ff call 80484c0 <__cxa_begin_catch@plt>
8048635: 89 45 f8 mov DWORD PTR [ebp-8],eax
8048638: 8b 45 f8 mov eax,DWORD PTR [ebp-8]
804863b: 8b 00 mov eax,DWORD PTR [eax]
804863d: 89 45 f4 mov DWORD PTR [ebp-12],eax
8048640: e8 5b fe ff ff call 80484a0 <__cxa_end_catch@plt>
8048645: b8 00 00 00 00 mov eax,0x0
804864a: 83 c4 24 add esp,0x24
804864d: 59 pop ecx
804864e: 5d pop ebp
804864f: 8d 61 fc lea esp,[ecx-4]
8048652: c3 ret
8048653: 90 nop
MASM ユーザな私には AT&T 記法は読みづらいので,ここでは逆アセンブル結果を得るために objdump -m i386:intel としています.
さて,main 関数から見てみます.まず最初にスタックフレームの作成とローカル変数の初期化をやってますね.その直後に _Z5func1v (func1 関数) をコールしてます.
_Z5func1v に移って,スタックフレームを作り... その直後に (アドレス 0x80485c7で) GOT 経由で __cxa_allocate_exception を呼び出してます.
この関数は何かというと... libstdc++ に含まれている,以下のような関数です.ここでは関数名を一部改変し,エラー処理を全て省略して示しています.
void* __cxa_allocate_exception(std::size_t thrown_size) throw()
{
void *ret;
thrown_size += sizeof (__cxa_exception);
ret = malloc (thrown_size);
__cxa_eh_globals *globals = __cxa_get_globals ();
globals->uncaughtExceptions += 1;
memset (ret, 0, sizeof (__cxa_exception));
return (void *)((char *)ret + sizeof (__cxa_exception));
}
見ての通り,例外オブジェクト用のメモリ(投げるオブジェクトのサイズ + sizeof(__cxa_exception))をヒープ上に確保しています.
その後で __cxa_throw を call しています.この call 命令の後ろには func1 関数に対応する ret 命令がないことから,__cxa_throw を呼び出した時点で main 関数の catch 節に制御が移るということになります.
その main 関数では他にもいろいろと例外処理に関する関数が呼ばれていますが,今回の目的は例外処理の全貌を解析することではなく,例外オブジェクトを参照で受け取ってもよいかどうかの調査ですので,これ以上調べなくても大丈夫でしょう.念のため,class E のコンストラクタと main 関数の catch 節の両方でそれぞれ例外オブジェクトのポインタの値を出力し,両者が一致していることを確認しました.
結局,ソースコードの上では関数ローカルに見えるオブジェクトが,実はヒープ上に確保されている,ということです.謎は解けました.これで安心して眠り... もとい,参照で catch ができます.めでたしめでたし.
VC の場合はどうなってるのかも気になるところです.また機会があればそのうち...
November 4, 2006
[Gentoo] Beryl を開発版スナップショットからダウングレード
何の気なしに Beryl 関連パッケージのバージョンを開発版スナップショットから 0.1.1 にダウングレードしたところ,スプラッシュスクリーンが綺麗に出るようになりました.あと,ウィンドウの透明度も正しく表示されるようになりました.
というわけで,現在インストールしている Beryl 関連パッケージのバージョンは以下の通りです.
- beryl-0.1.1-r2
- beryl-core-0.1.1
- beryl-plugins-0.1.1-r1
- beryl-dbus-0.1.1-r1
- beryl-manager-0.1.1
- beryl-settings-0.1.1
[Gentoo] 真っ白なウィンドウ in AIGLX
AIGLX を使用中に突然,ウィンドウが真っ白になってしまう現象が起こったので調べてみると,HOWTO XGL/Troubleshooting - Gentoo Linux Wiki に書いてありました.が,実行してみるも効果はなく,いろいろ試行していると,どうやら当方の環境ではインストールしている Mesa を media-libs/mesa-6.5.2_pre20061102 から media-libs/mesa-6.5.1-r1 にしたところ現象が治まりました.
[Gentoo] Xgl から AIGLX に戻す
glxinfo を実行したときに direct rendering: No と言われるのが気になるので,AIGLX に切り替えました.AIGLX だと Beryl のスプラッシュスクリーンが綺麗に表示されませんが,これは我慢することに.
AIGLX の状態で glxinfo を実行すると,direct rendering: Yes と表示される代わりに以下のメッセージが表示されます.
libGL warning: 3D driver claims to not support visual 0x5b
とりあえず目立った問題がないので無視することに.
November 3, 2006
[Gentoo] 再び Xgl (AIGLX + Beryl + Aquamarine) を試す
前回のインストールから約 2 ヶ月.もう一度 Xgl をインストールしてみることにしました.
前回とは違い,compiz-quinnstorm が Beryl という名称になっています.あと,KDE 用の window decolator (Aquamarine) が登場していました.これはいい感じです.
例によって Gentoo Linux Wiki や Gentoo Linux Wiki を参考にします.
後者を読んでいくうちに分かったのですが,どうやら当方の環境の場合,Xgl でなく AIGLX を有効にした X.org で十分そうです.
というわけで,まずはインストールから始めます.
layman を使って,xeffects の ebuild を取得します.
# layman -a xeffects
次は,/etc/portage/package.keywords と /etc/portage/package.unmask を変更します.
# cat >> /etc/portage/package.keywords <<EOF =x11-wm/beryl-0.1.1-r1 ~x86 =x11-wm/beryl-core-9999 -* =x11-plugins/beryl-plugins-9999 -* =x11-plugins/beryl-dbus-9999 -* =x11-misc/beryl-manager-9999 -* =x11-misc/beryl-settings-9999 -* =x11-apps/xlsclients-1.0.1 ~x86 =x11-libs/libwnck-2.16.1 ~x86 =x11-wm/aquamarine-9999 -* EOF # cat >> /etc/portage/package.unmask <<EOF x11-wm/aquamarine EOF #
そして X.org と Beryl のインストールです.X.org は既に 7.1 をインストールしていましたが,ちょうど aiglx という USE flag が新たに追加されていたので,それを有効にしてもう一度関連するパッケージを emerge することにします.
# vim /etc/make.conf (USE="aiglx" を追加) # emerge -puD --newuse world (関連パッケージの再 merge) # emerge beryl
無事に完了したら,/etc/X11/xorg.conf を編集します.
Section "ServerLayout" Option "AIGLX" "True" EndSection Section "Device" Option "DRI" "True" Option "XAANoOffscreenPixmaps" "True" EndSection Section "dri" Group 0 Mode 0666 EndSection Section "Extensions" Option "Composite" "True" EndSection
さらに,ログイン時に Beryl が起動するようにします.Gentoo Linux Wiki では,KDE を使用している場合は /etc/env.d/99kde-env もしくは /etc/env.d/*kdepaths* を書き換えるように書いてあります.さて,私は Aquamarine を使いますので,どうやら起動スクリプトを書く必要があるようです.スクリプトのファイル名は /usr/local/bin/aquamarine-start とでもしておきます.
#! /bin/sh beryl --replace & exec aquamarine --replace
スクリプトには実行属性を与えておきます.
# chmod 755 /usr/local/bin/aquamarine-start
あと,Gentoo Linux Wiki に書いてある通りに "dbus" 引数付きで beryl で起動すると以下のように落ちてくれます.
$ beryl --replace dbus XGL Absent, assuming AIGLX beryl: SmcOpenConnection failed: Could not open network socket Pop: Old number of plugins is 0. Plugin dbus is a new plugin. Plugin settings is a new plugin. New number of plugins is 2. Checking for new plugins. Seeking to load dbus. beryl: Couldn't load plugin 'dbus' segmentation fault $ ("dbus-launch beryl --replace dbus" としても同じように落ちます)
プラグイン dbus がロードできないと言われてます.プラグインファイル(/usr/lib/beryl/libdbus.so)はちゃんと存在しているので,ファイルが見つからなかっただけ,というオチではなさそうです.ただ,あまり深く調べるのも面倒なので,こっちの環境に何らかの問題があるんだろうなぁ,と思いつつ,今回は dbus 引数を外して起動します.
さて,起動してみましょう... 再び感動の瞬間です.
スクリーンショットはありませんが,Aquamarine のおかげで KDE 側で設定したウィンドウ装飾がちゃんと使えています.あと,2 ヶ月前とは違い,突然 X サーバが落ちる,といった現象も見られませんでした.
ただ,AIGLX を使っているせいか,Beryl のスプラッシュ画面が綺麗に表示されない(部分的に真っ黒に潰れたような表示 (以下))ので,試しに Xgl もインストールしてみました.
# emerge =xgl-0.0.1_pre20061020-r1
Gentoo Linux Wiki に従い,/etc/X11/gdm/gdm.conf を Xgl 用に合わせて書き換え,GDM を再起動し,あとは普通にログインするだけです.
Beryl のスプラッシュスクリーンが綺麗に表示されました.めでたしめでたし.