none
C、C++言語作成結合モジュールのスタートアップルーチンとポストルーチン RRS feed

  • 質問

  • C、C++言語で開発し結合した実行モジュールには、リンク・ライブラリは別にして
    自分が作成したコードとは別に、スタートアップルーチンとポストルーチンがリンクされるはずです。
     
    これらのエントリ・アドレスをなんとか見つけることはできないものでしょうか?
     
    あるとすれば、たとえば次に示すようなどんな方式であってもかまいません。
     
    (1) スタートアップルーチンや、ポストルーチンの実リンク名
     
    (2) "*.exe"や"*.pdb"ファイルの特定項目の参照
     
    (3) システムAPI。 あるとすればDbgHelp APIか、Debug Interface Access SDK APIの組み合わせに
    なるかと思う。
     
    (4) MSVC用のスタートアップルーチンやポストルーチンのソース・コードが
    どこかで見つかる。
     
    見つからなければ、main関数やWinMain関数のアドレスと、
    自分作成コードの終了する(exitする)ポイントを見つける方式を検討することになりますが、
    特に終了するポイントを見つける方法についても助言を頂けると助かります。
     
    あるアプリにおけるターゲット・プロセスのシステムAPI呼び出しトレースで、
    現在スタートアップルーチンやポストルーチンのコードまでトレースされてしまっています。
    ここでの呼び出しをスルーすることに、利用しようと考えています。
     
     

    ななち
    • 編集済み ななち 2010年10月16日 3:41 文字化け訂正(NNTP Bridgeからだとどうして発生するの?)
    2010年10月16日 3:35

回答

  • でしたらやはり

    デバッグから除外したいのなら、main()とexit()にブレークポイントを置き、main()以前、exit()以降は無視すればいいかと。

    に相当する処理をご自身の作られているデバッガに実装すればいいのでは? main()と簡単に書きましたが、実際にはmain()、wmain()、WinMain()、wWinMain()のいずれかの可能性があります。
    シンボルテーブルからこれらの関数アドレスの検索の仕方や、ブレークポイントの実装方法を質問されているわけではありませんよね?
    # あれっ、追記を読むとそういう質問ですか?

    一応、私はWaitForDebugEvent() を使ってデバッガ相当を作成した経験はあります。その上でななちさんがなぜスタートアップルーチンにこだわっているのかよくわかりません。プロセスとしてはスタートアップルーチン / メインルーチン / ポストルーチンとステージ分けされている訳ではなく、CALL / RETを繰り返しながら一連の命令列を実行しているだけです。

    • 回答としてマーク ななち 2010年10月16日 6:23
    2010年10月16日 6:01

すべての返信

  • 質問の意図がわかりませんでした。スタートアップルーチンを書いたことはありますか?

    スタートアップルーチンがどう呼び出されるか知っていればすぐにわかることです。
    つまり、PEヘッダを見てもいいですし(IMAGE_OPTIONAL_HEADER::AddressOfEntryPointね)、CreateProcess()DEBUG_PROCESSフラグ 付きで起動 してbreakした場所でも、手段はいくらでもあります。

    このような質問になるのは、スタートアップルーチンを呼び出す更なるスタートアップルーチンが.EXE内に含まれていると誤解しているのではないでしょうか?
    とりあえず C:\Program Files\Microsoft Visual Studio 10.0\VC\crt\src\crt0.c を読まれることをお勧めします。UNIXでもこの辺りは一緒です。 exit( main( argc, argv, env ) ); となっているのをポストルーチンと呼ばれても…。

    • 回答としてマーク ななち 2010年10月16日 4:34
    • 回答としてマークされていない ななち 2010年10月16日 4:41
    • 編集済み 佐祐理 2010年10月16日 6:02 リンク訂正
    2010年10月16日 4:03
  •  
    "佐祐理" <=?utf-8?B?5L2Q56WQ55CG?=> wrote in message news:00b08dcc-ad3d-42d1-8607-e33b3479aea2...

    スタートアップルーチンがどう呼び出されるか知っていればすぐにわかることです。
    つまり、PEヘッダを見てもいいですし(IMAGE_OPTIONAL_HEADER::AddressOfEntryPointね)、CreateProcess()をDEBUG_PROCESSフラグ付きで起動 してbreakした場所でも、手段はいくらでもあります。

    それがわからなかったから、お聞きしたわけなんです。
    MSVCのスタートアップ・ルーチンのソースコードが公開されていたなんて、思ってもいませんでした。

    このコードは見たことがあります。
    このコードを見ると、どうも求めているものと違うような気がしてきました。
    整理し直して、再度質問し直した方がよさそうです。

    どちらにしても、ヒントになり助かりました。

     
    CreateProcess()をDEBUG_PROCESSフラグ付きで起動した時のファースト・ブレークが
    main関数が呼び出される以前のどこかですが、どこでブレークされているのかが不明でした。
    C、C++のスタートアップ・ルーチンと解釈したわけです。
     
    たとえば、main関数が呼び出される以前に、グローバル・クラス・オブジェクトのコンストラクタ呼び出されます。
    その呼び出し実態は、これからコードを読んで確認しますが、
    スタートアップルーチンに間違いありませんよね? 
     

    とりあえず C:\Program Files\Microsoft Visual Studio 10.0\VC\crt\src\crt0.c を読まれることをお勧めします。UNIXでもこの辺りは一緒です。 exit( main( argc, argv, env ) ); となっているのをポストルーチンと呼ばれても…。

    C++の場合main関数がリターンしてから、グローバル・クラス・オブジェクトのデストラクタが呼び出されます。
    スタートアップと同様、
    グローバル・クラス・オブジェクトのデストラクタを呼び出す実態がポストルーチンだと思っていましたが、
    MSVCでは別の方法なんでしょうか?
     
    昔のC言語にトランスレータするトランスレータ形式のC++言語では、
    名前はともかくmain関数が終了してから、後処理するルーチンが必要だったはずです。
    直接コードはみておりませんが、最終的にはアプリにリンクされていたように思います。
     

    ななち
    • 編集済み ななち 2010年10月16日 8:49
    2010年10月16日 4:33
  • やっぱり何を求めているのかわかりませんでした。

    先に書いた通り、crt0.cには exit( main( argc, argv, env ) ); とあります。プロセスが起動してからmain()が呼び出されるまでがスタートアップルーチンであり、exit()が呼ばれてからプロセスが終了するまでがポストルーチンです。デバッグから除外したいのなら、main()とexit()にブレークポイントを置き、main()以前、exit()以降は無視すればいいかと。

    例えば、スタートアップルーチン内でargvを用意するためにmalloc()やstrcpy()を呼んだりしていますが、これらはスタートアップルーチンとは異なる中立の立場です。もしかして、関数アドレスだけでスタートアップルーチンかどうか判断可能と誤解していませんか? コールスタックを見なければ判断できません。

    なお、スタートアップルーチンから呼び出すコンストラクタや、ポストルーチンから呼び出すデストラクタ・atexit()で登録された関数などなどは、先に挙げたcrt0.cから呼び出されていますので、あとはソースコードを追いかけてみてください。

    それから蛇足ですが、スタートアップルーチンがどう決定されているか、それはリンカの/ENTRYオプション で指定された関数です。このオプションのデフォルト値がcrt0.cの関数名になっているわけです。

    2010年10月16日 4:59
  •  
    "佐祐理" <=?utf-8?B?5L2Q56WQ55CG?=> wrote in message news:f4b52186-3379-4cb4-9fd0-c06c9ebc366d...

    やっぱり何を求めているのかわかりませんでした。

    先に書いた通り、crt0.cには exit( main( argc, argv, env ) ); とあります。プロセスが起動してからmain()が呼び出されるまでがスタートアップルーチンであり、exit()が呼ばれてからプロセスが終了するまでがポストルーチンです。デバッグから除外したいのなら、main()とexit()にブレークポイントを置き、main()以前、exit()以降は無視すればいいかと。

     
    かなり説明を省略していて、誤解されてしまったようですね。
     
    まずは、自プロセスのデバッグについての質問ではありません。
     
    デバッグ対象のプログラムについてではなく、簡単に言ってしまうとデバッガ製造上の
    問題についての質問です。
     
    もちろん、デバッガを作るわけではありません。
    デバッガの技術を応用したものを作ろうとしているだけです。
     
    基本的な仕組みはできてました。
    でも、ターゲット・プロセスの自分がコーディングした範囲内だけを監視するようにしたいのですが、
    現在それ以外のところも監視してしまいます。

    ※回答に必要かもしれませんが、監視内容の説明についてはご容赦下さい。
     
    とりあえず除外したいのは、
    デバッガ側ファースト・ブレークからターゲットの自分のコードが開始される以前と、
    そしてターゲットの自分のコードが終了してからプロセスが完全に終了するまでの範囲です。
     
    その方法を、いろいろと調査しているところです。
     
    質問の意図は、ご理解頂けたでしょうか?

    方法は、なんだってかまいません。

    とりあえず思いついたのが、ターゲット・プロセスのスタートアップ・ルーチンと後処理ルーチンを見つけ出し、
    この範囲は監視しないようにするということです。
    現在、プロセス開始時において、監視している内容がスタートアップ・コードとは完全には一致していないように
    思えます。

    作ろうとしているものがデバッガ相当ですから、ターゲット・プロセスをデバッガにかけて動作を追跡すると
    いうことができません。
    なにかうまい方法がありますでしょうか?

     


    ななち
    2010年10月16日 5:23
  • でしたらやはり

    デバッグから除外したいのなら、main()とexit()にブレークポイントを置き、main()以前、exit()以降は無視すればいいかと。

    に相当する処理をご自身の作られているデバッガに実装すればいいのでは? main()と簡単に書きましたが、実際にはmain()、wmain()、WinMain()、wWinMain()のいずれかの可能性があります。
    シンボルテーブルからこれらの関数アドレスの検索の仕方や、ブレークポイントの実装方法を質問されているわけではありませんよね?
    # あれっ、追記を読むとそういう質問ですか?

    一応、私はWaitForDebugEvent() を使ってデバッガ相当を作成した経験はあります。その上でななちさんがなぜスタートアップルーチンにこだわっているのかよくわかりません。プロセスとしてはスタートアップルーチン / メインルーチン / ポストルーチンとステージ分けされている訳ではなく、CALL / RETを繰り返しながら一連の命令列を実行しているだけです。

    • 回答としてマーク ななち 2010年10月16日 6:23
    2010年10月16日 6:01
  •  
    "佐祐理" <=?utf-8?B?5L2Q56WQ55CG?=> wrote in message news:15c74282-513c-4278-9c31-d62e66676cb1...

    でしたらやはり

    デバッグから除外したいのなら、main()とexit()にブレークポイントを置き、main()以前、exit()以降は無視すればいいかと。

     

     

    たびたび、ありがとうございます。
     
    ご回答の通りですね。
    ご回答の方法が、もっとも簡単です。
    馬鹿みたいでした。
    開発中は時々こんな馬鹿な思い込みをやってしまいますわ、とほほ!
     
    でも、グローバル・オブジェクトのコンストラクタ、デストラクの実行は監視したいと
    この方法についても、考えなければなりません。
     
    別に方法をこだわっているわけじゃなくて・・・
     
    最終的はその処理が必要だし、開発中では余計なイベントを拾うので邪魔くさいから
    簡単に避ける方法はないかと?たまたま思いついた手段についてお聞きしました。

    ななち
    • 編集済み ななち 2010年10月16日 8:39
    2010年10月16日 6:18
  •  
    "佐祐理" <=?utf-8?B?5L2Q56WQ55CG?=> wrote in message news:15c74282-513c-4278-9c31-d62e66676cb1...

    一応、私はWaitForDebugEvent() を使ってデバッガ相当を作成した経験はあります。その上でななちさんがなぜスタートアップルーチンにこだわっているのかよくわかりません。プロセスとしてはスタートアップルーチン / メインルーチン / ポストルーチンとステージ分けされている訳ではなく、CALL / RETを繰り返しながら一連の命令列を実行しているだけです。

     
    よくよく考えてみれば、佐祐理さんの言われる通りです。
     
    昔トランスレータ・タイプのAT&T C++のコンパイラ・ドライバを書かざるをえなかった時、
    最終的には、リンク・モジュールにプリプロセッサ(と呼んでいたと思います)と
    “munch”とかいうライブラリもリンクしなければなりませんでした。

    “munch”は、ポストプロセッサと呼んでいたと思います。
    “munch”のソース・コードは、見ていません。
     
    ポストルーチンという呼び方は、その時の思い込みをずっと引きずってしまったものです。

     


    ななち
    2010年10月16日 6:46
  • main()以前に実行されるコードについてはCRTの初期化 に説明があります。crt0.cで言うと_initterm_e()や_initterm()辺りです。デストラクタやatexit()も同様です。

    これらはまぁ厄介ですね。先ほどの例示したmalloc()等のように関数アドレスで判定できませんので、頑張ってくださいとしか言えません。

    CygwinやMinGWといったVC++以外のラインタイムライブラリもありますし、VC++ランタイムを使用しながら、エントリポイントを差し替えたりもできますから、後はどこまで頑張るか次第です。

    でも大抵のデバッガはmain()以前、exit()以降はユーザーが明示的にブレークポイントを置かない限りは初期化処理としてスキップしているような。

    2010年10月16日 6:50
  •  
    "佐祐理" <=?utf-8?B?5L2Q56WQ55CG?=> wrote in message news:ac08783f-0014-4f8f-b933-c28296eeaf07...

    main()以前に実行されるコードについてはCRTの初期化 に説明があります。crt0.cで言うと_initterm_e()や_initterm()辺りです。デストラクタやatexit()も同様です。

    これらはまぁ厄介ですね。先ほどの例示したmalloc()等のように関数アドレスで判定できませんので、頑張ってくださいとしか言えません。

    CygwinやMinGWといったVC++以外のラインタイムライブラリもありますし、VC++ランタイムを使用しながら、エントリポイントを差し替えたりもできますから、後はどこまで頑張るか次第です。

    いろいろとありがとうございました。
    非常に助かりました。
     
    監視しているイベントが、ターゲット開始前と終了後に、山のよう発生してうんざりしました。
    ま、対処できないにしても、起こっている事象については、説明できるよう求められています。
     
    LINUX版も作らなければなりません(こっちが本命?)が、佐祐理さんにはその時にも助言がいただけそうです。
    でも、その時はどこで?

    ななち
    2010年10月16日 7:13