11

大規模なシステムでクラッシュするコードがあります。ただし、コードは基本的に次の擬似コードに要約されます。これを最小限の骨にまで煮詰めようとしたので、詳細の多くを削除しました。私はこれが決定的なものを見逃すとは思わない。

// in a DLL:

#ifdef _DLL
#define DLLEXP __declspec(dllexport)
#else
#define DLLEXP __declspec(dllimport)
#endif

class DLLEXP MyClass // base class; virtual
{
public:
  MyClass() {};
  virtual ~MyClass() {};

  some_method () = 0; // pure virtual

  // no member data
};

class DLLEXP MyClassImp : public MyClass
{
public:
  MyClassImp( some_parameters )
  { 
    // some assignments...
  }

  virtual ~MyClassImp() {};

private:
  // some member data...
};

と:

// in the EXE:

MyClassImp* myObj = new MyClassImp ( some_arguments ); // scalar new
// ... and literally next (as part of my cutting-down)...
delete myObj; // scalar delete

一致するスカラー new とスカラー delete が使用されていることに注意してください。

Visual Studio (2008 Pro) のデバッグ ビルドでは、Microsoft の <dbgheap.c> で次のアサーションが失敗します。

_ASSERTE(_CrtIsValidHeapPointer(pUserData));

スタックの一番上には、次のアイテムがあります。

mydll_d.dll!operator delete()
mydll_d.dll!MyClassImp::`vector deleting destructor'()

こうあるべきだと思う

mydll_d.dll!MyClassImp::`scalar deleting destructor'()

つまり、プログラムは私が書いたかのように動作しています

MyClassImp* myObj = new MyClassImp ( some_arguments );
delete[] newObj; // array delete

のアドレスpUserDataは、myObj(メンバーではなく) それ自体のアドレスです。そのアドレス周辺のメモリは次のようになります。

                                ... FD FD FD FD
(address here)
VV VV VV VV MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
FD FD FD FD AB AB AB AB AB AB AB AB EE FE EE FE
...

ここで、4 つVVの s はおそらく仮想関数テーブルのアドレスであり、MM...MM認識可能なメンバー データであり、他のバイトはデバッガーによって配置されたさまざまな特別なマーカーです (たとえば、FD FDs はオブジェクトのストレージの周りの「ガード バイト」です)。

アサーションが失敗する少し前に、VVs が変更されているのを確認しました。これは、基本クラスの仮想関数テーブルへの切り替えによるものではないかと考えています。

クラス階層の間違ったレベルが破壊されるという問題を認識しています。それはここでは問題ではありません。私のデストラクタはすべて仮想です。

Microsoft のページ "BUG: エクスポートされたクラスに対して間違った演算子の削除が呼び出されました" http://support.microsoft.com/kb/122675に注意 してください。データ。

私の場合、デストラクタを削除するという間違った「フレーバー」が適用されているようです。つまり、スカラーではなくベクトルです。

私は、まだ問題が発生する最小限のカットダウン コードを作成しようとしています。

ただし、この問題をさらに調査する方法に役立つヒントやヒントをいただければ幸いです。

おそらく、ここでの最大の手がかりmydll_d.dll!operator delete()はスタックにあります。s が「失われた」myexe_d.exe!operator delete()ことを示す であると期待する必要がありますか?DLLEXP

これは二重削除のインスタンスである可能性があると思います(しかし、そうは思いません)。

_CrtIsValidHeapPointer何をチェックするかに関して私が読むことができる良い参考文献はありますか?

4

5 に答える 5

8

これは、あるヒープから割り当てて、別のヒープで削除しようとする問題である可能性があります。これは、dll が独自のヒープを持っているため、dll からオブジェクトを割り当てるときに問題になる可能性があります。あなたが示しているコードからは、これが問題になるとは思われませんが、単純化して何かが失われたのでしょうか? 過去に、このようなコードdestroyでは、オブジェクトに対してファクトリ関数と仮想メソッドを使用して、割り当てと削除が dll コードで確実に行われるようにしているのを見てきました。

于 2010-07-30T16:07:19.673 に答える
1

この動作は、仮想デストラクタを持つエクスポートされたクラスの削除演算子が暗黙的に生成され、関連付けられたフラグを使用してベクター dtor にマングルされる MSVC 9 に特有です。ここで、1 は (スカラー) を意味し、3 は (ベクター) を意味します。

このことの本当の問題は、それが new/delete の標準的な形式を壊すことです。クライアント コーダーは、ベクトル削除演算子を使用するのは悪い考えだと考えた場合、そのコードでベクトル削除演算子を無効にすることができません。

さらに、実装が存在するモジュールとは別のモジュールに new が割り当てられ、参照カウントを介して静的変数に保持されている場合、ベクター dtor も間違って実行されるようです。プロセスのシャットダウン時に使用されます)。

これは、前述のヒープの問題「bshields」に一致します。dtor が間違ったヒープで実行され、シャットダウン時に「そのメモリの場所を読み取れない」または「アクセス違反」でコードがクラッシュします。このような問題は非常に一般的なようです。

このバグを回避する唯一の方法は、基本クラスから delete_this 関数の使用を強制することにより、仮想デストラクタの使用を禁止し、それを独自に実行することです。 . 次に、スカラー dtor も実行され、シャットダウン時に、モジュール間で共有される参照カウント オブジェクトを安全な方法でインスタンス化できます。これは、ヒープが常に元のモジュールに正しくアドレス指定されるためです。

このような問題があるかどうかを確認するには、単純にベクトル削除演算子の使用を禁止してください。

于 2011-01-21T23:16:01.190 に答える
1

Microsoft は、C ランタイムのソースを提供しています。そこをチェックして、何が行われているかを確認できます_CrtIsValidHeapPointer。私のインストールでは、下にありC:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\crt\src\dbgheap.cます。

もう1つの提案は、の分解を確認することです

delete newObj; // scalar delete

生成された逆アセンブリと比較します

delete[] newObj;

delete pointerToClassLikeMyClassThatIsInExeAndNotDll;

delete[]呼び出されることについてのあなたの理論をテストします。同様に、コールスタックを確認できます

delete pointerToClassLikeMyClassThatIsInExeAndNotDll;

mydll_d.dll!operator delete()対についての理論をテストしますmyexe_d.exe!operator delete()

于 2010-07-30T16:05:54.590 に答える
1

すべての回答とコメントに感謝します。すべてが有用で関連性があります。

さらなる情報は引き続き歓迎します。


以下は、Hans Passant からの私の質問に対するコメントです。

DLL からクラスをエクスポートし始めると、/MD を指定してコンパイルすることが非常に重要になります。私には /MT のように見えます。

その結果、プロジェクト全体でリンケージ設定を詳しく調べました。/MD および /MDd であるはずの /MT および /MTd の「埋もれた」インスタンスと、他の設定での関連する矛盾を見つけました。

これらを修正した結果、アサーションはスローされなくなり、コードは正しく動作しているように見えます。


実行時にクラッシュまたはアサーション エラーが発生し、スコープとデストラクタが呼び出されたときに確認する必要がある事項を次に示します。すべてのプロジェクト (依存関係を含む) とすべての構成 (特に問題のあるプロジェクト) で、次のことを確認してください。

(ここで、*.vcproj パスは </VisualStudioProject/Configurations/Configuration/> に対する相対パスです。)

  • C/C++ | で正しいランタイムが選択されます。コード生成 | ランタイム ライブラリ <Tool[@Name="VCCLCompilerTool"]/@RuntimeLibrary>;
  • 適切な定義 (存在する場合) は C/C++ | で作成されます。プリプロセッサ | プリプロセッサ定義 <Tool[@Name="VCCLCompilerTool"]/@PreprocessorDefinitions> 特に静的ライブラリと動的ライブラリの使用に関連する(例: STLport の _STLP_USE_STATIC_LIB と _STLP_USE_DYNAMIC_LIB など)。
  • 適切なバージョンのライブラリがリンカー | で選択されます。入力 | 追加の依存関係 <Tool[@Name="VCLinkerTool"]/@AdditionalDependencies> 特に静的ランタイム ライブラリと DLL の「ラッパー」に関連するもの(例: stlport_static.lib と stlport.NM .lib )。

興味深いことに、私が期待する削除のスカラー「フレーバー」はまだ呼び出されていないようです (ブレークポイントは決してヒットしません)。つまり、ベクトル削除デストラクタしか表示されません。したがって、それは「赤いニシン」だった可能性があります。

おそらく、これは単に Microsoft の実装上の問題であるか、あるいは私が見逃した他の微妙な点がまだあるのかもしれません。

于 2010-08-03T09:43:25.103 に答える
0

私の場合、デストラクタを削除するという間違った「フレーバー」が適用されているようです。つまり、スカラーではなくベクトルです。

それはここの問題ではありません。スカラーとベクターのミスマッチ new と deleteの疑似コードによると、 はscalar deleting destructor単にvector deleting descructor「ベクター破壊ではなくスカラー破壊を行う」というフラグを付けて を呼び出します。

他のポスターで指摘されているように、実際の問題は、あるヒープに割り当て、別のヒープで削除していることです。最も明確な解決策は、同様の質問への回答で説明したように、クラスに and のオーバーロードを与えることですoperator newoperator delete

于 2016-02-06T04:17:22.437 に答える