この記事は[[mainとスレッドの前後処理(2)]]の続きです。 この記事は早くも古いものとなりました。詳細は[[MinGW+pthread2010.03]]を参照。 !!!mainとスレッドの前後処理(3) 前回までで、やっとプロセスとスレッドの開始時・終了時に処理を割り込ませる方法に見当がついた。理論重視とは言いつつも、結局実装の検証に過ぎない部分も多々あったことは否めないが、とにかくもなんとか納得してコードが書けそうだ。 !!サンプルコード TLSコールバックはプロセス・スレッドの開始・終了を捉えるので、これ一つで事足りる。 そしてその為のコードは以下のようなものだ。 確認はMinGW猫科研究所パックa004で行った。GCCは4.4.1-tdm-2だ。 {{pre #include #include static NTAPI void flpthread_tls_callback(PVOID hinstDLL, DWORD fdwReason, PVOID lpvReserved){ switch(fdwReason){ case DLL_PROCESS_ATTACH: puts("-> DLL_PROCESS_ATTACH"); break; case DLL_PROCESS_DETACH: puts("-> DLL_PROCESS_DETACH"); break; case DLL_THREAD_ATTACH: puts("-> DLL_THREAD_ATTACH"); break; case DLL_THREAD_DETACH: puts("-> DLL_THREAD_DETACH"); break; default: puts("-> UNKNOWN"); break; } } static void* flpthread_xla __attribute__((section(".CRT$XLA"))) = 0; static PIMAGE_TLS_CALLBACK flpthread_xly __attribute__((section(".CRT$XLY"), used)) = flpthread_tls_callback; static void* flpthread_xlz __attribute__((section(".CRT$XLZ"), used)) = 0; static DWORD flpthread_tls_data __attribute__((section(".tls"))) = 0; static HANDLE flpthread_tls_index = NULL; const IMAGE_TLS_DIRECTORY _tls_used __attribute__((section(".rdata$T"))) = { (DWORD)&flpthread_tls_data, (DWORD)&flpthread_tls_data, (DWORD)&flpthread_tls_index, (DWORD)(&flpthread_xla+1), (DWORD)0, // unused (not supported) (DWORD)0 }; }} あとは煮るなり焼くなり、好きにすればよい。 !プロセス開始時・終了時のみ スレッド側の処理が不要な場合は、より簡単な方法を取ることができるので、例示しておく。当初の目的が上記のTLS-Callbackで上手く行ってしまったため、あまりしっかりとは検証していないので注意。 まず、繰り返しになるが、標準C++の範囲で行う場合。 {{pre class Foo { public: Foo(){ cout << "cpp constructor!" << endl; }; ~Foo(){ cout << "cpp destructor!" << endl; }; }; Foo foo; }} 先に書いた通り、グローバル変数のコンストラクタ・デストラクタを利用する。 次に、GCCの機能を使用する場合。 {{pre static void c_ctors(){ puts("c_ctors!"); } static void c_dtors(){ puts("c_dtors!"); } void* startup __attribute__((section(".ctors"))) = c_ctors; void* termination __attribute__((section(".dtors"))) = c_dtors; }} MinGWは.ctorsと.dtorsを適切にPEのセクションに割り振ってくれるようだ。 しかし、同じGCCのattribute機能でも、以下のコードは動作しなかった。 {{pre static void c_ctors(){ puts("c_ctors!"); } static void c_dtors(){ puts("c_dtors!"); } void* startup __attribute__((constructor)) = c_ctors; void* termination __attribute__((destructor)) = c_dtors; }} ==理由は良く分からないが、=="warning: 'constructor' attribute ignored"と言われる。 ==個人的には、ELFの機能である.ctorsと.dtorsよりも、こちらに対応すべきだと思うのだが。== …と書いていたら、別の猫研メンバから一言、「それって変数じゃなく'''関数の'''attributeなんじゃない?」と指摘された。………'''おぉぉ、その通りだ(汗''' {{pre __attribute__((constructor)) static void c_ctors() { puts("c_ctors!"); } __attribute__((destructor)) static void c_dtors() { puts("c_dtors!"); } }} で無事通る。「セクション+関数ポインタ」に気を取られすぎてその形式しか頭になかったが、一般的に言えば関数のattributeであるのは当然だ。間違っているのは大抵人間の方だというのを体現してしまった。 最後に、以下のように直接セクションに配置するコードも動作しなかった。 {{pre static void c_ctors(){ puts("c_ctors!"); } static void c_dtors(){ puts("c_dtors!"); } void* xi_a __attribute__((section(".CRT$XIA"))) = 0; void* xi_b __attribute__((section(".CRT$XIB"))) = c_ctors; void* xi_z __attribute__((section(".CRT$XIZ"))) = 0; void* xt_a __attribute__((section(".CRT$XTA"))) = 0; void* xt_b __attribute__((section(".CRT$XTB"))) = c_dtors; void* xt_z __attribute__((section(".CRT$XTZ"))) = 0; }} こちらに関しては動作しない理由が全く分からない。何か間違っているのだろうが、詳しく検証してもいない。いずれにしても綺麗な方法ではないので、使うべきではないと思う。 !!リンク集 今回の件では、かなりのサイトを見て回った。 その一部のリンクをここに置いておく。 !gccとELF *[NewOrder - news: Abusing .CTORS and .DTORS for fun 'n profit|http://neworder.box.sk/newsread.php?newsid=13727] **ELFにおける.ctorsと.dtorsの悪用について。 **glibcの初期化プロセスを解説。 *[Overwriting the .dtors section.|http://www.synnergy.net/downloads/papers/dtors.txt] **__attribute__ ((constructor/destructor))がELFの.ctorsと.dtorsにマップされること等。 **詳細はgcc-2.95.2/gcc/collect2.cを見よとあるのでこの頃から使用できるらしい。 *[Function Attributes|http://gcc.gnu.org/onlinedocs/gcc-4.4.2/gcc/Function-Attributes.html] **GCCのマニュアル、関数の__attribute__に関して。 **constructor/destructorを見よ。 *[Variable Attributes|http://gcc.gnu.org/onlinedocs/gcc-4.4.2/gcc/Variable-Attributes.html] **GCCのマニュアル、変数の__attribute__に関して。 **sectionを見よ。 *[Thread-Local|http://gcc.gnu.org/onlinedocs/gcc-4.4.2/gcc/Thread_002dLocal.html#Thread_002dLocal] **GCCのマニュアル、Thread-Local Storageに関して。 *[2007-02-03 - memologue|http://d.hatena.ne.jp/yupo5656/20070203] **.ctorsのより詳細版、__attribute__((init_priority(N)))について。 *[オブジェクトファイルについて|http://shinh.skr.jp/binary/shdr.html] **ELFのセクションに関する概説。 *[Nabble - MinGW - User - Pre-initialization using .CRT$X?? section hacks|http://old.nabble.com/Pre-initialization-using-.CRT$X---section-hacks-td17407874.html] **「MinGWで.CRT$X??とTLSを使うには?」という内容の、ML上での問答。 **__attribute__((constructor))とMAGE_TLS_DIRECTORYを使えという結論で簡潔にまとまってる。 !VC(Visual C++)とMicrosoftのCOFF/PE(Portable Executable) *[CRT の初期化|http://msdn.microsoft.com/ja-jp/library/bb918180.aspx] **MSDNの公式な記述。.CRT$XCA/XCZについて触れられている。 *[init_seg|http://msdn.microsoft.com/en-us/library/7977wcck.aspx] **MSDNの公式な記述。具体的なセクション名との関連については触れられていない模様。 *[HOW TO: Use #pragma init_seg to Control Static Construction|http://support.microsoft.com/?scid=kb%3Ben-us%3B104248&x=14&y=10] **同じくMSDNの公式な記述。 *[Under the Hood: Reduce EXE and DLL Size with LIBCTINY.LIB|http://msdn.microsoft.com/ja-jp/magazine/cc301696(en-us).aspx] **MSDN MAGAZINEの記事。 **.EXEや.DLLのサイズを減らすという観点だが.CRT$X??セクションに関して触れられている。 *[Visual C++ Team Blog : CRT Initialization|http://blogs.msdn.com/vcblog/archive/2006/10/20/crt-initialization.aspx] **Visual C++のライブラリ(ATL, MFC, CRT, STL)チームによる.CRT$X??セクションの解説。 *[about #pragma init_seg(compiler)|http://social.msdn.microsoft.com/forums/en-US/vclanguage/thread/8ed6a785-2712-4de2-ad5f-c1b4bc30def7] **Visual C++のDeveloper Centerのフォーラムに投稿された質問と回答。 **質問内容とその英語のレベルは高くないが、回答は#pragma init_segとセクションの対応に触れている。 *[Optimizing Data Segment Usage Developer.com|http://www.developer.com/ws/pc/article.php/3083221/Optimizing-Data-Segment-Usage.htm] **Windows(CE?)でのセクションマップについて。 **一般的なセクションの解説だが.CRT$XCA/XCZ、CRT$XIA/XIZ、CRT$XPA/XPZ、CRT$XTA/XTZが存在するのがわかる。 *[CodeProject: Re: Visual C++ static member initialization. Free source code and programming help|http://www.codeproject.com/Messages/889522/Re-Visual-Cplusplus-static-member-initialization.aspx] **#pragma init_segについて。 *[Direct2D 入門 ≫ Blog Archive ≫ VC++拡張 1|http://www.tkzdev.net/?p=183] **#pragma init_segの使い方。「静的オブジェクトの初期化順序制御」を見よ。 *[かなり強引な方法で DLL のインポートを横取りする (未参照を防ぐ)|http://jet2.u-abel.net/program/tips/forceimp.htm] **今回の件とは別だが#pragma init_segを使う方法の例にはなる。 *[Network Geographics: Weblog|https://network-geographics.com/weblog/2008/04/static_initialization_order_in_v_1.php] **#pragma init_segと.CRT$XCLの関係について。 *[Operating Systems Development Series|http://www.brokenthorn.com/Resources/OSDevMSVC.html] **Initializing globals and static dataという部分で.CRT$X??セクションについて詳説。 *[Has anyone used M Roddys C++ kernel runtime in a 64-bit driver? - Page 2 - Microsoft Device Drivers|http://www.techtalkz.com/microsoft-device-drivers/294927-has-anyone-used-m-roddys-c-kernel-runtime-64-bit-driver-2.html] **.CRT$X??セクションの使用に関して64bit版との整合性のあるサンプル。 *[Running Code Before and After Main CodeGuru.com|http://www.codeguru.com/cpp/misc/misc/threadsprocesses/article.php/c6945__1/] **main関数の前・後に実行される.CRT$X??セクションの解説、boostもこれをベースにしている? *[hype-free: Playing tricks with the Windows PE Loader|http://hype-free.blogspot.com/2008/10/playing-tricks-with-windows-pe-loader.html] **TLSの使用例と解説。 *[Manually create a Thread Local Storage (TLS) Callback|http://www.cyberarmy.net/library/article/1653] **PEとTLS-Callbackに関する解説。 **OllyDbg等を用いて具体的にどういう事かを詳説している。 **PEの構造を見るための[StudPE|http://www.cgsoftlabs.ro/studpe.html]というツールが紹介されている。非常に有用。 *[How Malware Defends Itself Using TLS Callback Functions|http://isc.sans.org/diary.html?storyid=6655] **TLS-Callbackがマルウェアにどのように利用されているか、という解説。 *[TLS strikes back - Mais en fait, non ! - Le blog de Sylvain Sarmejeanne|http://sylv1.tuxfamily.org/2008/241/tls-strikes-back.html](フランス語) **IMAGE_TLS_DIRECTORYのバイナリイメージでの解説。 *[main が呼ばれる前に実行されるコードのコールスタック - やや温め納豆|http://d.hatena.ne.jp/egggarden/20091203/1259857341] **TLSコールバックが「いつ呼ばれるか」を詳細に検証した記事。 **前提知識ナシで読むには若干辛いが日本語だしわかりやすい。 *[Uninformed - vol 3 article 7|http://uninformed.org/index.cgi?v=3&a=7&p=4] **アンチデバッガとしてTLS-Callbackが使用されている例。 *[nzight: March 2007|http://blog.dkbza.org/2007_03_01_archive.html] **IMAGE_TLS_DIRECTORY構造体の意味を含む、TLSとTLS-Callbackの解説。 *[CodeProject: Thread Local Storage - The C++ Way. Free source code and programming help|http://www.codeproject.com/KB/threads/tls.aspx] **Thread Local Storage Callbackに関して。VC6からサポートされていたがバグがあるらしい。 **boostでの使用とTlsAlloc等のAPIに関しても言及。 *[第2回 実行メカニズムの理解に欠かせない「スレッド」の概念 - (新)APIから知るWindowsの仕組み:ITpro|http://itpro.nikkeibp.co.jp/article/COLUMN/20070416/268374/?ST=develop&P=3] **スレッドとTLSの解説。本質を外してる気はするが日本語であるし読みやすい。 *[S.S’S HOMEPAGE 逆アセのスス乂 目次|http://www.interq.or.jp/chubu/r6/reasm/PE_FORMAT/intro.html] **MicrosoftのCOFFフォーマットの解説の日本語訳、.tlsセクションの記述あり。 *[DirectX Graphicsの隠し設定を利用した開発テクニック(2/4):CodeZine|http://codezine.jp/article/detail/235?p=2#col5] **CRTとマルチスレッドに関するコラムが本件と関連している。 このほか、boostのMLで非常に多くの議論が交わされていて読みきれないほど。 「Windows MSVC thread exit handler for staticly linked Boost.Thread」で検索すればすぐ見つかる。 たとえば[コードの例示|http://lists.boost.org/Archives/boost/2004/08/69771.php]がなされてる。 !gccとVCのハイブリッドな解説 *[C++ - OSDev Wiki|http://wiki.osdev.org/C_PlusPlus] **ctors/dtors, .CRT$X??, #pragma init_seg等の解説がなされている。 **ItaniumのABIとして触れられている? !pthreads-w32関連 *[pthreads-win32 updated - IMPORTANT ≪ autobuilds log|http://ffmpeg.arrozcru.org/autobuilds/blog/2009/07/17/pthreads-win32-updated-important/] **pthreads-w32のstatic版で特別な関数の呼び出しが不要(autostatic)なバージョン。 **ソース:http://ffmpeg.arrozcru.org/autobuilds/extra/sources/pthreads-win32-CVS_17072009_1420-autostatic.tar.bz2 **バイナリ:http://ffmpeg.arrozcru.org/autobuilds/extra/mingw32/pthreads-win32-CVS_17072009_1420-autostatic-mingw32.tar.bz2 *[Pthreadsのコンパイル&インストール (autostatic版) お気に入りの動画を携帯で見よう|http://blog.k-tai-douga.com/article/31078165.html] **ffmpegのバイナリを配布しているサイト。上記autostaticを利用している例。 **diff:http://abechin.sakura.ne.jp/sblo_files/k-tai-douga/ffmpeg/pthreads-autostatic.diff *PDF:[プログラムが main()にたどりつくまで|http://ukai.jp/Slides/2006/1024-gree/binhacks.pdf] **BINARY HACKSの資料らしきもの。スライドなので解説テキストはない。 **第8回 オープンソーステクノロジー勉強会 2006/10/24 鵜飼文敏 Debian Developer !その他 *[The compiler, assembler, linker, loader and process address space programming tutorial - hacking the process of building programs using C language -notes and illustrations|http://www.tenouk.com/ModuleW.html] **一般的なセクションに関する説明。ELFが主なターゲットだが他の理解にも役立つ。 *[/SECTION (Specify Section Attributes)|http://msdn.microsoft.com/en-us/library/sf9b18xk.aspx]([日本語|http://msdn.microsoft.com/ja-jp/library/sf9b18xk.aspx]) **Visual C++のリンカオプションである/SECTIONの説明。 **セクションの属性(Read/Write/Execute等)についての説明なので今回の件とは若干離れる。 *[Executable-File Header Format|http://support.microsoft.com/?scid=kb%3Ben-us%3B65122&x=14&y=10] **NE(New Executable)形式のヘッダに関する情報。今回の件とはかけ離れるが。 *[Microsoft Portable Executable and Common Object File Format Specification|http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx] **PE/COFFフォーマットに関するMicrosoft謹製の仕様。 この他、マイクロソフトのサイトにVCの標準ライブラリのソースらしきものがあった。 が、EULAの関係で見ない方が良さそうなのでここでは表示しない。 今回の記事でもその内容に触れない範囲にしているし、 MinGWでTLSコールバックさえ扱えれば必要ないだろう。 //*[_crt.c Source|http://research.microsoft.com/en-us/um/redmond/projects/invisible/src/crt/md/i386/_crt.c.htm] //**Microsoftが.ctors/.dtors相当のセクションについて記述しているソース? //**.CRT$XCA/XCZ、CRT$XIA/XIZ、CRT$XPA/XPZ、CRT$XTA/XTZ等について。 //**EULAの関係で扱いが難しそうなので注意。 ---- この記事は[[mainとスレッドの前後処理(2)]]の続きです。