{"id":35573,"date":"2005-05-23T08:57:05","date_gmt":"2005-05-23T08:57:05","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2005\/05\/23\/why-are-dlls-unloaded-in-the-wrong-order\/"},"modified":"2005-05-23T08:57:05","modified_gmt":"2005-05-23T08:57:05","slug":"why-are-dlls-unloaded-in-the-wrong-order","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20050523-05\/?p=35573","title":{"rendered":"Why are DLLs unloaded in the &#034;wrong&#034; order?"},"content":{"rendered":"<p>\nWhen a program starts or when a DLL is loaded,\nthe loader builds a dependency tree of all the DLLs\nreferenced by that program\/DLL, that DLL&#8217;s dependents, and so on.\nIt then determines the correct order in which to initialize\nthose DLLs so that no DLL is initialized until after all the\nDLLs upon which it is dependent have been initialized.\n(Of course, if you have a circular dependency, then this falls apart.\nAnd as you well know, calling\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/dllproc\/base\/loadlibrary.asp\">\nthe <code>LoadLibrary<\/code> function<\/a>\nor\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/dllproc\/base\/loadlibraryex.asp\">\nthe <code>LoadLibraryEx<\/code> function<\/a>\nfrom inside a DLL&#8217;s DLL_PROCESS_ATTACH notification also messes up\nthese dependency computations.)\n<\/p>\n<p>\nSimilarly, when you unload a DLL or when the program terminates,\nthe de-initialization occurs\nso that a DLL is de-initialized after all its dependents.\n<\/p>\n<p>\nBut when you load a DLL manually,\ncrucial information is lost:  Namely that the DLL that is calling\n<code>LoadLibrary<\/code> depends on the DLL being loaded.\nConsequently, if A.DLL manually loads B.DLL, then there is no\nguarantee that A.DLL will be unloaded before B.DLL.\nThis means, for example, that code like the following is\nnot reliable:\n<\/p>\n<pre>\n<i>HSOMETHING g_hSomething;\ntypedef HSOMETHING (WINAPI* GETSOMETHING)(void);\ntypedef void (WINAPI* FREESOMETHING)(HSOMETHING);\nGETSOMETHING GetSomething;\nFREESOMETHING FreeSomething;\n\/\/ Ignoring race conditions for expository purposes\nvoid LoadB()\n{\n HINSTANCE hinstB = LoadLibrary(TEXT(\"B.DLL\"));\n if (hinstB) {\n  GetSomething = (GETSOMETHING)\n          GetProcAddress(hinstB, \"GetSomething\");\n  FreeSomething = (FREESOMETHING)\n          FreeProcAddress(hinstB, \"FreeSomething\");\n }\n}\n\/\/ Ignoring race conditions for expository purposes\nHSOMETHING CacheSomethingFromB()\n{\n if (!g_hSomething &amp;&amp;\n     GetSomething &amp;&amp; FreeSomething) {\n  g_hSomething = GetSomething();\n }\n return g_hSomething;\n}\nBOOL CALLBACK DllMain(HINSTANCE hinst,\n      DWORD dwReason, LPVOID lpReserved)\n{\n switch (dwReason) {\n ...\n case DLL_PROCESS_DETACH:\n  if (g_hSomething) {\n   FreeSomething(g_hSomething); \/\/ oops\n  }\n  break;\n }\n return TRUE;\n}<\/i><\/pre>\n<p>\nAt the line marked &#8220;oops&#8221;, there is no guarantee that\n<code>B.DLL<\/code> is still in memory because <code>B.DLL<\/code>\ndoes not appear in the dependency list of <code>A.DLL<\/code>,\neven though there is a runtime-generated dependency caused by\nthe call to <code>LoadLibrary<\/code>.\n<\/p>\n<p>\nWhy can&#8217;t the loader keep track of this dynamic dependency?\nIn other words\nwhen <code>A.DLL<\/code> calls <code>LoadLibrary(TEXT(\"B.DLL\"))<\/code>,\nwhy can&#8217;t the loader automatically say &#8220;Okay, now A.DLL depends\non B.DLL&#8221;?\n<\/p>\n<p>\nFirst of all, because as I&#8217;ve noted before,\n<a href=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2004\/01\/01\/47042.aspx\">\nyou can&#8217;t trust the return address<\/a>.\n<\/p>\n<p>\nSecond, even if you could trust the return address,\nyou still can&#8217;t trust the return address.\nConsider:\n<\/p>\n<pre>\n\/\/ A.DLL - same as before except for one line\nvoid LoadB()\n{\n HINSTANCE hinstB = <font COLOR=\"blue\">MiddleFunction<\/font>(TEXT(\"B.DLL\"));\n if (hinstB) {\n  GetSomething = (GETSOMETHING)\n          GetProcAddress(hinstB, \"GetSomething\");\n  FreeSomething = (FREESOMETHING)\n          FreeProcAddress(hinstB, \"FreeSomething\");\n }\n}\n\/\/ MIDDLE.DLL\nHINSTANCE MiddleFunction(LPCTSTR pszDll)\n{\n return LoadLibrary(pszDll);\n}\n<\/pre>\n<p>\nIn this scenario, the load of <code>B.DLL<\/code> happens\nnot directly from <code>A.DLL<\/code>, but rather through\nan intermediary (in this case, <code>MiddleFunction<\/code>).\nEven if you could trust the return address, the dependency\nwould be assigned to <code>MIDDLE.DLL<\/code> instead of\n<code>A.DLL<\/code>.\n<\/p>\n<p>\n&#8220;What sort of crazy person would write a function like\n<code>MiddleFunction<\/code>?&#8221;, you ask.\nThis sort of intermediate function is common\nin <a HREF=\"http:\/\/go.microsoft.com\/?linkid=664920\">\nhelper\/wrapper libraries<\/a>\nor to\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/com\/htm\/cmf_a2c_95rt.asp\">\nprovide additional lifetime management functionality<\/a>\n(although it doesn&#8217;t do it any more, though it used to).\n<\/p>\n<p>\nThird, there is the case of\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/dllproc\/base\/getmodulehandle.asp\">\nthe <code>GetModuleHandle<\/code> function<\/a>.\n<\/p>\n<pre>\nvoid UseBIfAvailable()\n{\n HINSTANCE hinstB = GetModuleHandle(TEXT(\"B\"));\n if (hinstB) {\n  DOSOMETHING DoSomething = (DOSOMETHING)\n          GetProcAddress(hinstB, \"DoSomething\");\n  if (DoSomething) {\n   DoSomething();\n  }\n }\n}\n<\/pre>\n<p>\nShould this call to <code>GetModuleHandle<\/code>\ncreate a dependency?\n<\/p>\n<p>\nNote also that there are dependencies among DLLs\nthat go beyond just <code>LoadLibrary<\/code>.\nFor example, if you pass a callback function pointer\nto another DLL, you have created a reverse dependency.\n<\/p>\n<p>\nA final note is that this sort of implicit dependency,\nas hard as it is to see as written above, is even worse\nonce you toss global destructors into the mix.\n<\/p>\n<pre>\n<i>class SomethingHolder\n{\npublic:\n SomethingHolder() : m_hSomething(NULL);\n ~SomethingHolder()\n  { if (m_hSomething) FreeSomething(m_hSomething); }\n HSOMETHING m_hSomething;\n};\nSomethingHolder g_SomethingHolder;\n...\n<\/i><\/pre>\n<p>\nThe DLL dependency is now hidden inside the\n<code>SomethingHolder<\/code> class, and when\n<code>A.DLL<\/code> unloads, <code>g_SomethingHolder<\/code>&#8216;s\ndestructor will run and try to talk to <code>B.DLL<\/code>.\nHilarity ensues.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>When a program starts or when a DLL is loaded, the loader builds a dependency tree of all the DLLs referenced by that program\/DLL, that DLL&#8217;s dependents, and so on. It then determines the correct order in which to initialize those DLLs so that no DLL is initialized until after all the DLLs upon which [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[26],"class_list":["post-35573","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-other"],"acf":[],"blog_post_summary":"<p>When a program starts or when a DLL is loaded, the loader builds a dependency tree of all the DLLs referenced by that program\/DLL, that DLL&#8217;s dependents, and so on. It then determines the correct order in which to initialize those DLLs so that no DLL is initialized until after all the DLLs upon which [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/35573","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=35573"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/35573\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=35573"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=35573"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=35573"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}