(toppers-users 240) Re: Windows のシミュレーションに失敗

Takayuki WAKABAYASHI takayuki @ ertl.ics.tut.ac.jp
2001年 7月 29日 (日) 19:58:29 JST


豊橋技術科学大学の若林です。

「Windowsのシミュレーションに失敗」の問題が解決しましたので、
報告いたします。

先日頂いたデータにおかしな点が無かったので、
こちらでも同じ環境を再現してみましたところ、
同様の問題が起こることが判りました。
 # Microsoft Windows NT Server 4.0 + ServicePack 4
 # Microsoft Visual C++ 6.0 Enterprise
 # Microsoft Internet Explorer 4.01

問題を追跡したところ、原因は呼び出し規約の違いによるものでした。
 #単純にCALLBACKを付け忘れたという間抜けなミスでした

---8<---
場所
  KeyEventTrapper (serial_console.c:170)
   上記関数のプロトタイプ宣言 (serial_console.c:67)

修正
  関数宣言にCALLBACK (または__stdcall)を入れる
   #この問題はWinNT+SP4のみの組み合わせだけでなく、
   #全てのWindowsプラットフォームで起こる可能性があります。
   ##USER32.DLLに依存するエラー

  修正前  static LRESULT KeyEventTrapper(...省略...);
  修正後  static LRESULT CALLBACK KeyEventTrapper(...省略...);

発見までの経緯
  ・スレッド追跡を行ったところ、処理を行うメインスレッドが
     停止していることがわかった。
  ・問題発生時のスタック内容を調べたところ、スタックの中身が
     破壊されていることが判った。
  ・スタック追跡を行ったところ、スタック内にKeyEventTrapper関数
     を指すポインタがあることが判ったので、該当関数にブレークを
     設置したところ、常に3回目の実行で停止することがわかった。
      #3回目は1,2回目とは異なる関数から呼ばれており、その関数は
      #呼出前にスタックポインタを待避していなかった
  ・3回目の実行を追跡したところ、関数への引数の一部(Msg)が呼び出し
     元の戻り番地として扱われていることが判った。
  ・Windows内の関数のエピローグコードを調べたところ、全てスタック
     フレームは呼出先が壊していることがわかった。(RET imm16命令)
  ・MSDNで呼出規約を調べたところ、全てのWindows関数は__stdcall呼出
     でなければならないのに、当該関数は_cdeclのままになっていたこ
     とが判った。
      #_cdecl    : スタックフレームの破棄は呼出元が行う
      #__stdcall : スタックフレームの破棄は呼出先が行う 
---8<---

Windows2000やWindows98で障害が出ていないところを見ると、
これらのUSER32.DLLは「アプリを書く人は仕様通りのものを
作ってこない」ことを仮定したDLL設計がなされているのでしょう。

大堀さん、ご報告 感謝いたします。

+----------------------------------------------+
| Takayuki WAKABAYASHI (わかばやし たかゆき)   |
|  mailto: takayuki @ ertl.ics.tut.ac.jp         |
+----------------------------------------------+
| 豊橋技術科学大学 工学研究科 電子情報工学専攻 |
|   組込みリアルタイムシステム研究室           |
+----------------------------------------------+