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).
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に出てこないけど、どこに説明が載ってるんだろ?)