0

私のアプリケーションにはかなりの数のvoidポインタがあります(これは歴史的な理由によるもので、アプリケーションは元々純粋なCで書かれていました)。私のモジュールの1つでは、voidポインターが、既知の基本クラスから継承できるクラスのインスタンスを指していることを知っていますが、100%確信することはできません。したがって、voidポインタでdynamic_castを実行すると、問題が発生する可能性があります。おそらく、voidポインターはプレーン構造体を指していることさえあります(したがって、構造体にvptrはありません)。

void-pointerが指しているメモリの最初の4バイトを調べて、これが有効なvtableのアドレスであるかどうかを確認したいと思います。これはプラットフォームであり、コンパイラバージョン固有である可能性もありますが、アプリケーションを前進させ、限られた期間(たとえば、3年)ですべてのボイドポインタを取り除くのに役立つ可能性があります。

アプリケーション内のすべてのvtableのリストを取得する方法、またはポインターが有効なvtableを指しているかどうか、およびvtableを指しているそのインスタンスが既知の基本クラスを継承しているかどうかを確認する方法はありますか?

4

4 に答える 4

4

void-pointerが指しているメモリの最初の4バイトを調べて、これが有効なvtableのアドレスであるかどうかを確認したいと思います。

あなたはそれを行うことができますが、それが機能するという保証はありません。void*がvtableを指すかどうかさえわかりません。前回これを調べたとき(5年以上前)、一部のコンパイラは、インスタンスが指すアドレスのにvtableポインタを格納したと思います*。

私はこれがプラットフォームであることを知っています、おそらくコンパイラバージョン固有です、

使用する最適化などによっては、コンパイラオプション固有の場合もあります。

しかし、それは私がアプリケーションを前進させ、限られた期間(たとえば3年)ですべてのボイドポインターを取り除くのに役立つ可能性があります。

これは、アプリケーションを前進させるために表示できる唯一のオプションですか?他の人を考慮しましたか?

アプリケーション内のすべてのvtableのリストを取得する方法はありますか?

いいえ :(

または、ポインタが有効なvtableを指しているかどうかを確認する方法。

標準的な方法はありません。できることは、お気に入りのデバッガーでいくつかのクラスポインターを開き(またはメモリをバイトにキャストしてファイルに記録し)、それを比較して意味があるかどうかを確認することです。それでも、データ(またはアプリケーション内の他のポインター)が(バイトとしてキャストされたときに)十分に類似していないため、好きなコードを混乱させるという保証はありません。

そして、vtableを指すそのインスタンスが既知の基本クラスを継承するかどうか。

いいえ、もう一度。

ここにいくつかの質問があります(あなたはすでにそれらを考慮しているかもしれません)。これらへの回答はあなたにもっと多くの選択肢を与えるかもしれません、あるいは私たちに提案する他のアイデアを与えるかもしれません:

  • コードベースの大きさはどれくらいですか?グローバルな変更を導入することは可能ですか、それともそのために機能を広めることができますか?

  • すべてのポインターを均一に扱いますか(つまり、プラグインして独自のメタデータを追加できる共通のポイントがソースコードにありますか?)

  • ソースコードで何を変更できますか?(メモリ割り当てサブルーチンにアクセスできる場合、またはたとえば独自のサブルーチンをプラグインできる場合は、独自のメタデータをプラグインできる可能性があります)。

  • コードのさまざまな部分でさまざまなデータ型がvoid*にキャストされている場合、後でそれらのポインターに何があるかをどのように決定しますか?void *を識別するコードを使用して、それらがクラスであるかどうかを判断できますか?

  • あなたのコードベースはリファクタリングの方法論を可能にしますか?(コードの一部に代替実装をプラグインし、最初の実装を削除してすべてをテストすることにより、小さな反復でリファクタリングします)

編集(提案された解決策):

次の手順を実行します。

  • メタデータ(ベース)クラスを定義する

  • メモリ割り当てルーチンを、標準/古いルーチンを参照するだけのカスタムルーチンに置き換えます(コードがカスタムルーチンで引き続き機能することを確認します)。

  • 割り当てごとに、割り当てthe requested size + sizeof(Metadata*)ます(そして、コードが引き続き機能することを確認します)。

  • 割り当ての最初の バイトを、簡単にテストできる標準のバイトシーケンスに置き換えsizeof(Metadata*)ます(私は0xDEADBEEF:Dに部分的です)。次に、[allocated address] + sizeof(Metadata*)アプリケーションに戻ります。割り当て解除時に、受信したポインタを取得し、 `sizeof(Metadata *)でデクリメントしてから、システム/前のルーチンを呼び出して割り当て解除を実行します。これで、特に各割り当てのメタデータ用に、コードに追加のバッファーが割り当てられました。

  • メタデータを使用する場合は、メタデータクラスポインターを作成/取得してから、0xDEADBEEFゾーンに設定します。メタデータをチェックする必要がある場合reinterpret_cast<Metadata*>([your void* here])は、それをデクリメントしてから、ポインタ値が0xDEADBEEF(メタデータなし)か何か他のものであるかどうかをチェックします。

このコードはリファクタリングのためだけに存在する必要があることに注意してください。本番コードの場合、速度が遅く、エラーが発生しやすく、一般的に本番コードにしたくないその他の悪いことがあります。このすべてのコードを、メタデータクラスが製品リリースの光を見ることができないマクロに依存するREFACTORING_SUPPORT_ENABLEDようにします(ビルドのテストを除く)。

于 2010-06-14T08:40:14.700 に答える
0

より簡単な方法はoperator new、特定の基本クラスをオーバーロードすることです。そうすれば、void *ポインターがオブジェクトをヒープすることを知っている場合は、それらがオブジェクトを指しているかどうかを100%確実に判断することもできます。

于 2010-06-14T11:13:16.980 に答える
0

関連する参考文献(ヘッダー宣言)なしでは不可能だと思います。

于 2010-06-14T07:41:07.100 に答える
0

これらのvoidポインターを置き換えてインターフェースの種類を修正したい場合は、これを自動化すると思います。

  1. コードベースを調べて、仮想関数を持つすべてのクラスのリストを取得します。Perlのようなスクリプトを作成することで、これをすばやく実行できます。

  2. void *ポインタを入力として受け取る関数を記述し、それらのクラスを反復処理してそれをdynamic_castし、成功した場合はインターフェイスタイプ、コード行などの情報をログに記録します

  3. void *ポインターを使用した場所ならどこでもこの関数を呼び出すことができます。おそらく、ファイルや行の情報を簡単に取得できるように、マクロでラップすることができます。

  4. 完全自動化を実行し(ある場合)、出力を分析します。

于 2010-06-14T07:55:36.957 に答える