CygwinでLLVMのlliが動かない(その3)
だいたい原因が判明した……が、適切な直し方がわかりません(-◇-;
# (2009-04-11追記) とりあえずバグトラッカに投げ込んどいた。NEWのまま放置されてるけどorz
原因その1: dlopenの第1引数にNULLを渡すと適切な値が返ってこない
が、dlsymの第1引数にNULLを渡すと適切な値が返ってきたりする。
#include <stdio.h> #include <dlfcn.h> int main(void) { void *d; int (*f1)(const char *); int (*f2)(const char *); d = dlopen(NULL, RTLD_LAZY|RTLD_GLOBAL); f1 = dlsym(d, "puts"); f2 = dlsym(NULL, "puts"); printf("dlopen = %p\nputs(1) = %p\nputs(2) = %p\n", d, f1, f2); if (f1 != NULL) f1("Run puts(1)."); if (f2 != NULL) f2("Run puts(2)."); return 0; }
dlopen = 0x7f4d2b17c000 puts(1) = 0x7f4d2aa711c0 puts(2) = 0x7f4d2aa711c0 Run puts(1). Run puts(2).
- Cygwin on Windows XP:
dlopen = 0x400000 puts(1) = 0x0 puts(2) = 0x610969b4 Run puts(2).
これを解消する最も安直なパッチは以下の通り。
Index: lib/System/DynamicLibrary.cpp =================================================================== --- lib/System/DynamicLibrary.cpp (revision 67848) +++ lib/System/DynamicLibrary.cpp (working copy) @@ -65,6 +65,12 @@ bool DynamicLibrary::LoadLibraryPermanently(const char *Filename, std::string *ErrMsg) { +#ifdef __CYGWIN__ + if (Filename == 0) { + OpenedHandles.push_back(0); + return false; + } +#endif void *H = dlopen(Filename, RTLD_LAZY|RTLD_GLOBAL); if (H == 0) { if (ErrMsg)
原因その2: Cygwin(とMinGW?)限定で、bitcodeのmainが呼ばれるときにlli自身の__mainが呼ばれる
その結果、__main→do_global_ctorsによってグローバル変数(と呼んでいいのか?>C++)のコンストラクタが2回実行され、コンストラクタの実行過程で呼ばれるlib/VMCore/Pass.cppのRegisterPassが登録処理の二重呼び出しを検出してassertで落ちる。
これを引き起こすポイントは以下の2箇所。
- lib/Target/X86/X86ISelDAGToDAG.cpp
/// EmitSpecialCodeForMain - Emit any code that needs to be executed only in /// the main function. void X86DAGToDAGISel::EmitSpecialCodeForMain(MachineBasicBlock *BB, MachineFrameInfo *MFI) { const TargetInstrInfo *TII = TM.getInstrInfo(); if (Subtarget->isTargetCygMing()) BuildMI(BB, DebugLoc::getUnknownLoc(), TII->get(X86::CALLpcrel32)).addExternalSymbol("__main"); }
mainを実行する際、Cygwin(/MinGW?)なら__mainの呼び出しを加える
- lib/System/DynamicLibrary.cpp
void* DynamicLibrary::SearchForAddressOfSymbol(const char* symbolName) { /*略*/ #ifdef __CYGWIN__ { EXPLICIT_SYMBOL(_alloca); EXPLICIT_SYMBOL(__main); } #endif
とりあえず動くようにするには、X86DAGToDAGISel::EmitSpecialCodeForMainの中身をコメントアウトしてしまえばいい。
……が、わざわざこのような処理を行うよう記述されているわけで、単に消していいのか、正直疑問は残る。バグレポすべきなんだろうけど、何て言えばいいんだ? これ。
おまけ
LLVMをビルドするとき、configureに「--enable-debug-runtime」を指定すると、lliで「-debug」オプションが使えるようになる。(helpに出てこないけど、どこに説明が載ってるんだろ?)