2

このエラーは、最適化を実行する「フリー」(リリース) の WinDDK nmake コンパイラでビルドした場合にのみ発生します。「チェック済み」ビルドまたは VS でのコンパイルでは、これを再現できません。

私のコードで何が起こっているかの疑似コードは次のとおりです。

main()
{
   //variable init and other code
    fprintf(log, "pre nullValue: %lu\n", NULL);  //printf added for debugging
   otherFunc();
   funcWithError(str1, str2, NULL);
    fprintf(log, "post nullValue: %lu\n", NULL); 
    fprintf(log, "post2 nullValue: %lu, %lu, %lu\n", NULL, 0, NULL);
}

BOOL otherFunc()
{
   //variable init and other code
   callToDll();
    //...doing stuff
   libusb_usb_open(var1);    //If I remove this line there is no problem!!
    //...doing more stuff
}

BOOL funcWithError(char* s1, char* s2, FUNC_PTR fp)
{
    fprintf(log, "inFunc nullValue: %lu, %lu\n", NULL, fp);
   if(fp != NULL)
      return FALSE;        //This line is being executed erroneously!!
}

ログに出力:
pre nullValue: 0
inFunc nullValue: 0, 251208
post nullValue: 251208
post2 nullValue: 251208, 251208, 251208
注: 再発生番号 (251208) は、プログラムが実行されるたびに異なる番号です

その1行を変更するだけで修正/原因になります。libusb usb_open呼び出しです 。

  1. 最終的に私の質問は、問題を解決する方法を見つけることです (私はその電話を避けることはできません)
  2. しかし、スタック/メモリ管理レベルでは、ゼロ以外の NULL を持ち、リテラル値 '0' をゼロ以外として出力することはどのように可能でしょうか?

他に役立つ情報を教えてください...

4

2 に答える 2

3

完全なスラムダンクではありません。しかし、スタックが不均衡になっている可能性が非常に高いです。デバッガーで (はい、リリース ビルドをデバッグできます)、呼び出しの前後に ESP レジスターの値を確認します。それは同じであるべきです。関数の呼び出し規約が間違っている場合はそうではありません。__stdcall と __cdecl のように。

これは、nmake.exe を使用してプログラムをビルドするときに、うまく隠れることができます。デバッグ ビルドで /RTCs オプションをオンにするのを忘れがちなので、スタック チェック コードが出力されます。また、ESP レジスタは、関数の戻り時にそれ自体を復元する傾向があります。関数がインライン化され、EBP の使用が最適化されるリリース バージョンをビルドするまでは、ESP はそれ自体を復元しません。

于 2011-05-07T00:56:50.920 に答える
0

更新:それで、ついにwindbgがdllに侵入していくつかのことを調べるようになりました。私が最初に疑ったことと Hans が指摘したように、それは呼び出し規則の不一致によって引き起こされたスタックの破損でした。

リリース ビルドでのみ確認できるようになったのは、コンパイラが 0/Null 値を最適化して、0 を渡すのではなく、ebx レジスタ値を使用するようにしたことです。 () がスタックを破損した後、OtherFunc() がスタックをポップして元の ebx 値を復元しようとすると、「0」ではなくガベージが復元されました。そのため、main() に戻ると、NULL への最適化されたすべての参照がこのガベージ値を使用していました。

注: 他の dll 呼び出しがスタックを破損しなかった理由は、それらがパラメーターなしだったからです。

結論として、答えは次のとおりです。

  1. 正しい規則を使用して libusb を呼び出します (libusb は __cdecl 呼び出し規則を使用し、NMAKE はデフォルトで __stdcall を使用します)。
  2. NULL と '0' はソース コードでハード コードされていますが、コンパイラは値を渡すのではなくレジスタを使用するように最適化でき、レジスタは不正なコードによって破損する可能性があります。
于 2011-05-10T21:02:04.040 に答える