【问题标题】:DllMain() and __attribute__((constructor)) order of executionDllMain() 和 __attribute__((constructor)) 执行顺序
【发布时间】:2012-09-13 02:10:46
【问题描述】:

:如果我在共享库中写了一个DllMain()函数,同时又写了一个带有__attribute__((constructor))的函数,加载库时哪个会先执行?

:如果我在链接到该共享库的可执行文件中有带有__attribute__((constructor)) 的函数,那么首先调用什么 - 库的DllMain() 或可执行文件的__attribute__((constructor)) 函数?

【问题讨论】:

    标签: winapi dll constructor


    【解决方案1】:

    A:函数按此顺序调用:

    • DLL constructor
    • DLL DllMain() (process attach)
    • EXE constructor
    • EXE main()
    • EXE main() ends
    • EXE destructor
    • DLL DllMain() (process detach)
    • DLL destructor

    如果链接是在运行时完成的 (LoadLibrary()/FreeLibrary()),则函数按以下顺序调用:

    • EXE constructor
    • EXE main()
    • EXE LoadLibrary()
    • DLL constructor
    • DLL DllMain() (process attach)
    • EXE main() continues
    • EXE FreeLibaray()
    • DLL DllMain() (process detach)
    • DLL destructor
    • EXE main() ends
    • EXE destructor

    如果你忘记释放库,那么顺序是这样的:

    • EXE constructor
    • EXE main()
    • EXE LoadLibrary()
    • DLL constructor
    • DLL DllMain() (process attach)
    • EXE main() continues
    • EXE main() ends
    • EXE destructor
    • EXE FreeLibaray() (system cleans up for you)
    • DLL DllMain() (process detach)
    • DLL destructor

    【讨论】:

      【解决方案2】:

      考虑代码

      EXE:

      int main ()
      {
      
          printf("Executable Main, loading library\n");
      #ifdef HAVE_WINDOWS
          HMODULE lib = LoadLibraryA ("testdll.dll"); 
      #elif defined(HAVE_LINUX)
          void * lib  = dlopen("testdll.so", RTLD_LAZY);  
      #endif 
      
          if (lib) {
              printf("Executable Main, Freeing library\n");
          #ifdef HAVE_WINDOWS
              FreeLibrary (lib); 
          #elif defined(HAVE_LINUX)
              dlclose(lib);   
          #endif 
          }
          printf("Executable Main, exiting\n");
          return 0;
      }
      

      DLL

      struct Moo
      {
          Moo() { printf("DLL Moo, constructor\n"); }
          ~Moo() { printf("DLL Moo, destructor\n"); }
      };
      
      Moo m;
      
      #ifdef HAVE_WINDOWS
      BOOL APIENTRY DllMain( HMODULE hModule,
                             DWORD  ul_reason_for_call,
                             LPVOID lpReserved)
      {
          switch (ul_reason_for_call)
          {
          case DLL_PROCESS_ATTACH:
              printf("DllMain, DLL_PROCESS_ATTACH\n");
              break;
          case DLL_THREAD_ATTACH:
              printf("DllMain, DLL_THREAD_ATTACH\n");
              break;
          case DLL_THREAD_DETACH:
              printf("DllMain, DLL_THREAD_DETACH\n");
              break;
          case DLL_PROCESS_DETACH:
              printf("DllMain, DLL_PROCESS_DETACH\n");
              break;
          default:
              printf("DllMain, ????\n");
              break;
          }
          return TRUE;
      }
      #else
      CP_BEGIN_EXTERN_C
      __attribute__((constructor))
      /**
       * initializer of the dylib.
       */
      static void Initializer(int argc, char** argv, char** envp)
      {
          printf("DllInitializer\n");
      }
      
      __attribute__((destructor))
      /** 
       * It is called when dylib is being unloaded.
       * 
       */
      static void Finalizer()
      {
          printf("DllFinalizer\n");
      }
      
      CP_END_EXTERN_C
      #endif
      

      输出不同: 在窗户上 可执行主程序,加载库 DLL Moo,构造函数 DllMain, DLL_PROCESS_ATTACH 可执行主,释放库 DllMain, DLL_PROCESS_DETACH DLL Moo,析构函数 可执行主程序,正在退出

      Linux 可执行主程序,加载库 DllInitializer DLL Moo,构造函数 可执行主,释放库 DLL终结​​器 DLL Moo,析构函数 可执行主程序,正在退出

      在 Windows 上,Moo 构造函数在 DLLMain 之前调用,而在 linux 上,它在使用 attribute((constructor)) 定义的 Initializer 之后调用。

      为什么?

      【讨论】:

      • 我不知道。我 怀疑 是在 C++ 类构造函数之前调用了 shlib 构造函数(即使用属性((构造函数))定义的函数),因为它们是由相同的初始化代码调用的(你从未见过,因为你的工具链自动链接它)。另一方面,DllMain 是由操作系统调用的,这就是为什么这么晚才调用它的原因。我的猜测是,如果你用#endif 替换#else(并在最后删除#endif),并允许在NT 上编译shlib 构造函数和析构函数,你会发现它们在NT 上的行为与在Linux 上的行为相同.
      • 感谢 LRN 的回复。我在 Windows 上使用 VC++ 编译器,是否支持 __attribute__((constructor))?
      • 没有,据我所知。 属性 似乎是 GCC 的东西。
      猜你喜欢
      • 1970-01-01
      • 2012-12-28
      • 2014-01-28
      • 2020-05-29
      • 2012-01-16
      • 2022-01-06
      • 2019-06-12
      • 2011-01-04
      相关资源
      最近更新 更多