web-dev-qa-db-ja.com

「アドレス00000000で」アクセス違反を追跡する方法

エラーメッセージに実際のアドレスが含まれている場合、アクセス違反エラーを追跡するために.mapファイルを作成する方法を知っています。

しかし、もしエラーメッセージが言ったら

Access violation at address 00000000. Read of address 00000000.

この問題の原因はどこから探し始めますか?

29
ObiWanKenobi

アドレス「00000000」に近い場所でのアクセス違反は、ヌルポインターアクセスを示します。何かを作成する前、ほとんどの場合、またはFreeAndNil() 'dした後に使用しています。

多くの場合、これはフォームの作成中に間違った場所にあるコンポーネントにアクセスするか、メインフォームがまだ作成されていないデータモジュール内の何かにアクセスしようとすることが原因です。

MadExcept を使用すると、これらの事柄を簡単に追跡でき、非営利的な使用には無料です。 (実際には、商用使用ライセンスもかなり安価で、お金に見合うだけの価値があります。)

29
Ken White

受け入れられた答えは、ストーリー全体を伝えるものではありません。

はい、ゼロが表示されるたびに、NULLポインターが関係します。これは、NULL定義によりゼロであるためです。したがって、ゼロNULLを呼び出すことはあまり意味がありません。

is取得するメッセージについて興味深いのは、NULLが記載されているという事実ですtwice。実際、報告するメッセージは、Windowsブランドのオペレーティングシステムがユーザーに表示するメッセージに少し似ています。

メッセージはaddressNULLreadを試みたと言うNULL。それはどういう意味ですか?具体的には、アドレスはそれ自体をどのように読み取りますか?

通常、特定のアドレスでメモリから読み取りおよび書き込みを行うアドレスでの命令を考えます。これを知っていると、エラーメッセージを解析できます。メッセージは、命令addressNULLは、readNULLを試みました。

もちろん、アドレスNULLには命令がありません。そのため、コードでNULLを特別なものと考えています。しかし、すべての指示は、それ自体を読む試みから始まると考えることができます。 CPU EIPレジスタがアドレスNULLにある場合、CPUはアドレス0x00000000(NULL)からの命令に対してopcodeの読み取りを試みます。 。 NULLを読み取ろうとすると失敗し、受信したメッセージが生成されます。

デバッガーで、このメッセージを受信すると、EIPが0x00000000に等しいことに注意してください。これは私があなたに与えた説明を確認します。

質問は、「私のプログラムがNULLアドレスを実行しようとする理由」になります。思い浮かぶ3つの可能性があります。

  • 宣言し、NULLに割り当てられ、他の方法で初期化されず、逆参照している関数ポインターを介して関数呼び出しを行おうとしました。
  • 同様に、オブジェクトのvtableにNULLエントリがある「抽象」C++メソッドを呼び出している可能性があります。これらは、構文virtual function_name()=0を使用してコード内に作成されます。
  • コードでは、ゼロの書き込み中にスタックバッファがオーバーフローしました。ゼロは、スタックバッファーの終わりを超えて、保存されている戻りアドレスに書き込まれています。関数が後でそのret命令を実行すると、値0x00000000(NULL)が上書きされたメモリスポットからロードされます。このタイプのエラー、スタックオーバーフローは、フォーラムの略語です。

あなたはサードパーティのライブラリを呼び出していることに言及しているので、何らかのAPIへの入力として非NULL関数ポインタを提供することを期待しているライブラリの状況かもしれないことを指摘します。これらは、「コールバック」機能と呼ばれることもあります。

デバッガを使用して問題の原因をさらに絞り込む必要がありますが、上記の可能性は謎を解決するのに役立ちます。

33
Heath Hunnicutt

knowが実行したコードの近くを探し始め、knowが実行しなかったコードに到達すると探しを停止します。

あなたが探しているのは、おそらくプログラムが関数ポインターを介して関数を呼び出す場所ですが、そのポインターはヌルです。

スタックが破損している可能性もあります。関数のリターンアドレスをゼロで上書きした可能性があり、例外は関数の最後に発生します。バッファオーバーフローの可能性を確認し、DLL関数を呼び出している場合は、正しい呼び出し規約とパラメーターカウントを使用していることを確認してください。

これは、割り当てられていないオブジェクト参照やPCharのように、nullポインターを使用する通常のケースではありません。これらの場合、「at address x」の値がゼロ以外になります。命令はアドレス0で発生したため、CPUの命令ポインタが有効な命令を指していないことがわかります。そのため、デバッガーは、どのコード行が問題を引き起こしたかを表示できません。そこにはisコード行はありません。 CPUが無効なアドレスにジャンプした場所に至るコードを見つけることで、それを見つける必要があります。

コールスタックはそのままである可​​能性があり、少なくとも目標にかなり近づくはずです。ただし、スタックが破損している場合、コールスタックを信頼できない可能性があります。

7
Rob Kennedy

「アドレス00000000でアクセス違反」が発生した場合、割り当てられていない関数ポインター(おそらくイベントハンドラーまたはコールバック関数)を呼び出しています。

例えば

type
TTest = class(TForm);
protected
  procedure DoCustomEvent;
public
  property OnCustomEvent : TNotifyEvent read FOnCustomEvent  write FOnCustomEvent;
end;

procedure TTest.DoCustomEvent;
begin
  FOnCustomEvent(Self);  
end;

の代わりに

procedure TTest.DoCustomEvent;
begin
  if Assigned(FOnCustomEvent) then // need to check event handler is assigned!
    FOnCustomEvent(Self);  
end;

エラーがサードパーティのコンポーネントにあり、問題のあるコードを追跡できる場合は、空のイベントハンドラを使用してAVを防ぎます。

3
Gerry Coll

私がこの問題に出くわしたとき、私は通常、FreeAndNil()またはxxx:= NILだけの場所を見始めます。変数とその後のコード。

他に何も役に立たなかったときに、実行中にさまざまな疑わしい場所からメッセージを出力するLog()関数を追加し、後でそのログを見て、コード内のアクセス違反の発生箇所を追跡しました。

もちろん、これらの違反を追跡するために利用できるより多くのエレガントなソリューションがありますが、それらを自由に使用できない場合は、昔ながらの試行錯誤の方法がうまく機能します。

2
K.Sandell

2番目のmadExceptとEurekalogのような同様のツールを使用しますが、FastMMでも良い方法が得られると思います。完全なデバッグモードを有効にすると、何が間違っているかの手がかりが得られます。

とにかく、DelphiはデフォルトでFastMMを使用しますが、ロギングをさらに制御するために完全なFastMMを取得する価値があります。

1
Vegar

MadExceptを使用します。またはJclDebug。

1
Warren P

これはおそらく、NULLポインターにアクセスするライブラリー呼び出しを直接または間接的に使用しているためです。この特定のケースでは、NULLアドレスにジャンプしたように見えます。

私の経験では、これらを追跡する最も簡単な方法は、デバッガで実行し、スタックトレースをダンプすることです。

または、「手動」でそれを実行し、この違反が発生した機能(および場合によってはLOC)を正確に追跡できるまで、大量のログを追加できます。

Stack Tracer をご覧ください。デバッグの改善に役立ちます。

1
0xfe