INT 3→NOPの現象が一時止まったがまた元の状態に

午後8時起床,晴れ.朝食はネギと油揚げの卵うどん.STOPマクロで使っているアセンブラーコードの INT 3 が NOP に書き換えられてしまうという問題にどう対処すればよいか?STOP文の内容は以下のようなものだ.

#define STOP { do{__asm {int 3}} while (0); }

whileループは実際には回らないので実際には __asm {int 3} を一回だけ実施するのと等価だが,歴史的な理由でこのような構文になっている. これを以下のような関数呼び出しに書き換えてみたがうまくゆかない.

void stop(void)
{
     BPTR bp = (BPTR)stop;
     unsigned long offset = *(unsigned long *)(bp + 1);
     bp += offset + 0x4E;
     __asm{ int 3 };
     try {
         *bp = 0xCC;
     }
     catch (…) {}
}

*bp = 0xCC という行が実行できないためだ.「昔」はコードセグメントをダイナミックに書き換えるようなことは普通にやっていたのだが,いまは「遠い昔」だ.対策としては以下が考えられる.

  1. マイクロソフトの Assertionで代用する
  2. リリースモードのASSERT_NEVER と同等の動作に書き換える.つまり,パネルを出すことによって一時停止するようにする
  3. stop() 関数の中で例外をスローし,関数内でキャッチする
  4. Visual Studio のブレークポイントをstop()の中に設定する

項目1の難点は継続実行ができないという点だ.2の方法なら継続実行は可能だがパネルが表示されるために,トレースするのに一手間掛かってしまうという難点がある.3の方法もエラーハンドラーで停止するためにはパネルを出すしかないので意味がない.

stop関数の中にブレークポイントを置けば従来方式と実質的に同じ動作になるが,「恒久的にブレークポイントを設定する」ことができないというのが最大の問題だ.馬場研究所のデバッグメソッドでは,一つの問題が片付いたときには「すべてのブレークポイントを削除」するという手順が確立している.stop文にブレークポイントを設定し直せばよいというだけだが,余分なところに注意を振り向けなくてはならない.

次善の策として以下のような方法を採用することにした.「stop文には従来通りINT 3を仕掛けておく.ヒットするとそこで一度だけ停止するので,そのタイミングでブレークポイントを設定する」という方法だ.これまでは何も考えなくてもトレースに集中できていたのだが,これしかないと思う.ともかくこの環境に「慣れ」なくてはならない.

▲アプリ起動時,「Invalid parameter passed to C runtime function」というエラーが発生する.このエラーはCWinApp::GetPrinterDeviceDefaultsde で起きている.GetPrinterDeviceDefaultsde は外部関数なので対処しようがない… 実害は特にないように思われるので無視とする.

なぜだろう?動作が変化してしまった.INT 3を実行してもNOPに書き換えられるという現象が起こらなくなった.何もしていないつもりなのだが… やったことといえば,ソースファイルをバックアップに戻したこととipchの「フォールバック位置」を設定したというだけだ.「Invalid parameter passed to C runtime function」というエラーは出ているが,これはまぁどうでもよい.

ipchというのはインテリセンスに関係するファイルで大きなファイルになるので,プロジェクトから切り離したというだけで動作に影響するということは考え辛い.シンボルファイルはすべて読み込むという設定になっている.もしこれで動作するのなら,完全に平常復帰したということになる.ともかく,デバッグを続けることにしよう.

Mouse without Border でマウス共有を実現しているが,かなりリスクがある.マウスから手を話しているときひじなどが触れてマウスが別PCのウィンドウに移動し,キー入力がそちらに入ってしまうことがある.編集中の画面ではキーが入らないので打ち直しして進めていると,別画面のソースファイルが壊れていたりする現象が発生する.

これを避けるためにはPCの位置関係を逆転させて左のPCの左から出て,右のPCの右に入るようにするというのが効果があるが,慣れるまで少し時間が掛かる.しかし,ソースファイルを壊すよりはましなのでそうすることにしよう.しかし,この歳になると「環境に慣れる」のが大変だね.ツールバーのアイコンは今までより小さく見える上,自分好みに移動しても元に戻ってしまう.諦めてあるがままの状態に慣れることにしたのだが…さて,ようやくデバッグの本番だ.

AIの親はAL+AGとAFで,AE系列ということはあり得ないと考えられるので,AI(1)の所属系列がAEになるというところで誤っているものと考えられる.この操作はGoDownStreamで実施されている.GoDownStream ではAf系列はAE系列より先に処理されているにも関わらず,AIが処理されていない.これはかなりおかしい.AGの結婚が処理されていない.AGは妻帯で子ども2人を持っている.この結婚が処理済みになっている.

ALは始系列AMに属しているため,ここで処理されている.ということは,AF系列でAIを優先ノードに指定していること自体が誤りということになる.しかし,AF系列に入っているのは,もう一人の親であるAAだ.このノードが処理されていないのではないか?いや,ちょっと読み損なっている.AAはAE系列と思われる.つまり,AIとAFはまったく繋がりがない.従って,AIがAFの優先ノードになっている時点で誤りということになる.

Windows の更新が入った.「再起動が必要」ということらしい.メインマシンには更新が入ったが,タブレットには通知が入ってこない.同時ということはないにしても,精々タイムラグは精々一日程度と考えられる.更新をチェックしてみよう.確かに更新はあるようだ.「アクティブ時間」というのを設定したことはないが,この値が違うのかもしれない.

系列優先ノードの変更はTRIBEBOX::setprimaryで実施しているが,どうもこの関数が呼び出されていないように思われる.ではどこで書き換えているのか?いや,書き換えにはこの関数が必ず使われている.ダメだ.やはりINT 3の書き換えは実施されている.仕方ない.冒頭で決めたような方式でかわすことにしよう.逆アセンブルで見ると,「27ミリ秒経過」とあるので,INT 3を実行後一定時間が経過するとNOPに書き換えるという動作が入るようになっているようだ.なぜ,こんな込み入ったことをする必要があるのか?その真意が分からない…

AF系列の優先ノード設定はTOPOLOGY::TribeDecomposition⇒TRIBELIST::MakeTribeBoxで実行されている.TribeDecompositionが誤っているのではないだろうか?そもそもAIとAFは繋がっていないはずなのだが…

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA