prxモジュールを作ってAPIをフックしたい(3)

前回(id:iselix:20080820:1219246261)関数のフックに成功した、って書いたけど、あれは大きな間違い。とてつもない勘違いをしていた。
まず、sceAudioChReserve の先のメモリの内容を見る。

sceAudioChReserve=0x8821E2A0
op rs rt rd shamt funct|immed| target
 0 1F  0  0     0     8|    8|F800020 jr $31
 0  0  8 15    11     C| AC4C| 22B130 syscall
 2 10  1  9    1D     3| 4F43|8053D0C j 0x8053D0C

これを見て、前回は関数が呼ばれたばっかりなのに jr $31 で return しているのはおかしい、と考え、j 0x8053D0C が呼ばれている、と考えてその先を書き換えた。
しかし、sceAudioChReserve を呼んで j 0x8053D0C が呼ばれることはない。やはり、最初の jr $31 が呼ばれて return している。しかし、何も実行せずに return しているわけではない。実は、MIPS には遅延スロットがある。つまり、ジャンプや分岐命令の直後の1命令は、ジャンプや分岐する前にその命令が実行されている(普段はコンパイラが最適化してくれたり、アセンブラが分岐命令の直後に nop を入れたりして気にしなくても動くようになっているが)。
この場合では、sceAudioChReserve を呼んだ場合、syscall が呼ばれ、元の場所に return している。syscall 命令は、用はシステムコールを呼ぶわけだけど、MIPS ではシステムコールは例外の一つとして実装されている。例外っていうのは、つまり割り込みのことだ。sceAudioChReserve をフックするには、syscall されたときに呼ばれる例外をフックできればいいことになる。というわけで、また英語の MIPS ドキュメントを読む仕事が始まる……。
例外を処理する位置が書いてある例外ベクトルの位置は、BEV が 0 か 1 かで変わる。で、その BEV ってのがどこにあるかというと、コプロセッサ0(CP0)の Status レジスタの中にあるらしい。インラインアセンブラの書き方を読みつつ、下のコードを動かしてみる。

int module_start(SceSize args, void *argp)
{
	unsigned int tmp;
	SceUID logf = sceIoOpen(LOG_NAME,PSP_O_CREAT|PSP_O_WRONLY|PSP_O_TRUNC,0777);

	asm volatile("mfc0 %0,$12":"=r"(tmp));/*Statusレジスタは12番レジスタ*/
	sceIoWrite(logf,&tmp,4);
	
	return 0;
}

どうやら、BEV は 0 らしい。ということは、例外ベクトルの位置は 0x80000180 にあるはず。ということで、そのアドレスを読みに行く、が、落ちてどうにもうまく行かない。これ以上はよくわからんなぁ……。MIPS 本でも探して読んでみるか。