この記事はmainとスレッドの前後処理(2)の続きです。
この記事は早くも古いものとなりました。詳細はMinGW+pthread2010.03?を参照。
mainとスレッドの前後処理(3)
前回までで、やっとプロセスとスレッドの開始時・終了時に処理を割り込ませる方法に見当がついた。理論重視とは言いつつも、結局実装の検証に過ぎない部分も多々あったことは否めないが、とにかくもなんとか納得してコードが書けそうだ。
サンプルコード
TLSコールバックはプロセス・スレッドの開始・終了を捉えるので、これ一つで事足りる。そしてその為のコードは以下のようなものだ。確認はMinGW猫科研究所パックa004で行った。GCCは4.4.1-tdm-2だ。
#include <windows.h> #include <winnt.h> 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++の範囲で行う場合。
class Foo { public: Foo(){ cout << "cpp constructor!" << endl; }; ~Foo(){ cout << "cpp destructor!" << endl; }; }; Foo foo;
先に書いた通り、グローバル変数のコンストラクタ・デストラクタを利用する。
次に、GCCの機能を使用する場合。
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機能でも、以下のコードは動作しなかった。
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なんじゃない?」と指摘された。………おぉぉ、その通りだ(汗
__attribute__((constructor)) static void c_ctors() { puts("c_ctors!"); } __attribute__((destructor)) static void c_dtors() { puts("c_dtors!"); }
で無事通る。「セクション+関数ポインタ」に気を取られすぎてその形式しか頭になかったが、一般的に言えば関数のattributeであるのは当然だ。間違っているのは大抵人間の方だというのを体現してしまった。
最後に、以下のように直接セクションに配置するコードも動作しなかった。
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
- ELFにおける.ctorsと.dtorsの悪用について。
- glibcの初期化プロセスを解説。
- Overwriting the .dtors section.
- attribute ((constructor/destructor))がELFの.ctorsと.dtorsにマップされること等。
- 詳細はgcc-2.95.2/gcc/collect2.cを見よとあるのでこの頃から使用できるらしい。
- Function Attributes
- GCCのマニュアル、関数のattributeに関して。
- constructor/destructorを見よ。
- Variable Attributes
- GCCのマニュアル、変数のattributeに関して。
- sectionを見よ。
- Thread-Local
- GCCのマニュアル、Thread-Local Storageに関して。
- 2007-02-03 - memologue
- .ctorsのより詳細版、attribute((init_priority(N)))について。
- オブジェクトファイルについて
- ELFのセクションに関する概説。
- Nabble - MinGW - User - Pre-initialization using .CRT$X?? section hacks
- 「MinGWで.CRT$X??とTLSを使うには?」という内容の、ML上での問答。
- attribute((constructor))とMAGE_TLS_DIRECTORYを使えという結論で簡潔にまとまってる。
VC(Visual C++)とMicrosoftのCOFF/PE(Portable Executable)
- CRT の初期化
- MSDNの公式な記述。.CRT$XCA/XCZについて触れられている。
- init_seg
- MSDNの公式な記述。具体的なセクション名との関連については触れられていない模様。
- HOW TO: Use #pragma init_seg to Control Static Construction
- 同じくMSDNの公式な記述。
- Under the Hood: Reduce EXE and DLL Size with LIBCTINY.LIB
- MSDN MAGAZINEの記事。
- .EXEや.DLLのサイズを減らすという観点だが.CRT$X??セクションに関して触れられている。
- Visual C++ Team Blog : CRT Initialization
- Visual C++のライブラリ(ATL, MFC, CRT, STL)チームによる.CRT$X??セクションの解説。
- about #pragma init_seg(compiler)
- Visual C++のDeveloper Centerのフォーラムに投稿された質問と回答。
- 質問内容とその英語のレベルは高くないが、回答は#pragma init_segとセクションの対応に触れている。
- Optimizing Data Segment Usage Developer.com
- 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
- #pragma init_segについて。
- Direct2D 入門 ≫ Blog Archive ≫ VC++拡張 1
- #pragma init_segの使い方。「静的オブジェクトの初期化順序制御」を見よ。
- かなり強引な方法で DLL のインポートを横取りする (未参照を防ぐ)
- 今回の件とは別だが#pragma init_segを使う方法の例にはなる。
- Network Geographics: Weblog
- #pragma init_segと.CRT$XCLの関係について。
- Operating Systems Development Series
- 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
- .CRT$X??セクションの使用に関して64bit版との整合性のあるサンプル。
- Running Code Before and After Main CodeGuru.com
- main関数の前・後に実行される.CRT$X??セクションの解説、boostもこれをベースにしている?
- hype-free: Playing tricks with the Windows PE Loader
- TLSの使用例と解説。
- Manually create a Thread Local Storage (TLS) Callback
- PEとTLS-Callbackに関する解説。
- OllyDbg等を用いて具体的にどういう事かを詳説している。
- PEの構造を見るためのStudPEというツールが紹介されている。非常に有用。
- How Malware Defends Itself Using TLS Callback Functions
- TLS-Callbackがマルウェアにどのように利用されているか、という解説。
- TLS strikes back - Mais en fait, non ! - Le blog de Sylvain Sarmejeanne(フランス語)
- IMAGE_TLS_DIRECTORYのバイナリイメージでの解説。
- main が呼ばれる前に実行されるコードのコールスタック - やや温め納豆
- TLSコールバックが「いつ呼ばれるか」を詳細に検証した記事。
- 前提知識ナシで読むには若干辛いが日本語だしわかりやすい。
- Uninformed - vol 3 article 7
- アンチデバッガとしてTLS-Callbackが使用されている例。
- nzight: March 2007
- IMAGE_TLS_DIRECTORY構造体の意味を含む、TLSとTLS-Callbackの解説。
- CodeProject: Thread Local Storage - The C++ Way. Free source code and programming help
- Thread Local Storage Callbackに関して。VC6からサポートされていたがバグがあるらしい。
- boostでの使用とTlsAlloc等のAPIに関しても言及。
- 第2回 実行メカニズムの理解に欠かせない「スレッド」の概念 - (新)APIから知るWindowsの仕組み:ITpro
- スレッドとTLSの解説。本質を外してる気はするが日本語であるし読みやすい。
- S.S’S HOMEPAGE 逆アセのスス乂 目次
- MicrosoftのCOFFフォーマットの解説の日本語訳、.tlsセクションの記述あり。
- DirectX Graphicsの隠し設定を利用した開発テクニック(2/4):CodeZine
- CRTとマルチスレッドに関するコラムが本件と関連している。
このほか、boostのMLで非常に多くの議論が交わされていて読みきれないほど。「Windows MSVC thread exit handler for staticly linked Boost.Thread」で検索すればすぐ見つかる。たとえばコードの例示がなされてる。
gccとVCのハイブリッドな解説
- C++ - OSDev Wiki
- ctors/dtors, .CRT$X??, #pragma init_seg等の解説がなされている。
- ItaniumのABIとして触れられている?
pthreads-w32関連
- pthreads-win32 updated - IMPORTANT ≪ autobuilds log
- 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版) お気に入りの動画を携帯で見よう
- ffmpegのバイナリを配布しているサイト。上記autostaticを利用している例。
- diff:http://abechin.sakura.ne.jp/sblo_files/k-tai-douga/ffmpeg/pthreads-autostatic.diff
- PDF:プログラムが main()にたどりつくまで
- 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
- 一般的なセクションに関する説明。ELFが主なターゲットだが他の理解にも役立つ。
- /SECTION (Specify Section Attributes)(日本語)
- Visual C++のリンカオプションである/SECTIONの説明。
- セクションの属性(Read/Write/Execute等)についての説明なので今回の件とは若干離れる。
- Executable-File Header Format
- NE(New Executable)形式のヘッダに関する情報。今回の件とはかけ離れるが。
- Microsoft Portable Executable and Common Object File Format Specification
- PE/COFFフォーマットに関するMicrosoft謹製の仕様。
この他、マイクロソフトのサイトにVCの標準ライブラリのソースらしきものがあった。が、EULAの関係で見ない方が良さそうなのでここでは表示しない。今回の記事でもその内容に触れない範囲にしているし、MinGWでTLSコールバックさえ扱えれば必要ないだろう。
この記事はmainとスレッドの前後処理(2)の続きです。
最終更新時間:2010年02月18日 21時24分59秒