トップ 検索 一覧 ヘルプ RSS ログイン

mainとスレッドの前後処理(3)の変更点

  • 追加された行はこのように表示されます。
  • 削除された行はこのように表示されます。
この記事は[[mainとスレッドの前後処理(2)]]の続きです。

この記事は早くも古いものとなりました。詳細は[[MinGW+pthread2010.03]]を参照。

!!!mainとスレッドの前後処理(3)

前回までで、やっとプロセスとスレッドの開始時・終了時に処理を割り込ませる方法に見当がついた。理論重視とは言いつつも、結局実装の検証に過ぎない部分も多々あったことは否めないが、とにかくもなんとか納得してコードが書けそうだ。

!!サンプルコード

TLSコールバックはプロセス・スレッドの開始・終了を捉えるので、これ一つで事足りる。
そしてその為のコードは以下のようなものだ。
確認はMinGW猫科研究所パックa004で行った。GCCは4.4.1-tdm-2だ。

{{pre
#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++の範囲で行う場合。
{{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)]]の続きです。