{"id":27123,"date":"2007-04-25T10:00:00","date_gmt":"2007-04-25T10:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2007\/04\/25\/identifying-an-object-whose-underlying-dll-has-been-unloaded\/"},"modified":"2007-04-25T10:00:00","modified_gmt":"2007-04-25T10:00:00","slug":"identifying-an-object-whose-underlying-dll-has-been-unloaded","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20070425-00\/?p=27123","title":{"rendered":"Identifying an object whose underlying DLL has been unloaded"},"content":{"rendered":"<p>\nOkay, so I gave it away in the title, but follow along anyway.\n<\/p>\n<p>\nYour program chugs along and then suddenly it crashes like this:\n<\/p>\n<pre>\neax=06bad8e8 ebx=00000000 ecx=1e1cfdf0 edx=00000000 esi=06b9a680 edi=01812950\neip=1180ab57 esp=001178b4 ebp=001178c0 iopl=0         nv up ei pl nz na pe nc\ncs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206\nABC!FunctionX+0x1f:\n1180ab57 ff5108          call    dword ptr [ecx+8]    ds:0023:1e1cfdf8=????????\n0:000&gt;&gt;\n<\/pre>\n<p>\nInstantly you recognize the following:\n<\/p>\n<ul>\n<li>This is a virtual method call.\n    (Call indirect through register plus offset.)\n    &mdash; Very high confidence.<\/p>\n<li>The vtable is in <code>ecx<\/code>.\n    (That is the base register of the indirect call.)\n    &mdash; Very high confidence.<\/p>\n<li>The underlying DLL for this object has been unloaded.\n    (The memory that contains the vtable is not valid and its address\n    is consistent with once having been in valid code.)\n    &mdash; High confidence.<\/p>\n<li>This is a <code>IUnknown::Release<\/code> call.\n    (<code>Release<\/code> is the third function of <code>IUnknown<\/code>\n    and therefore resides at offset 8 on x86.)\n    &mdash; High confidence.\n<\/ul>\n<p>\nOf course, all of the above &#8220;instant conclusions&#8221; are merely\n&#8220;highly-educated guesses&#8221;, but life is full of highly-educated guesses.\n(Every morning, I guess that my plates are still in the cupboard.)\n<\/p>\n<p>\nLet&#8217;s run with our theory that the object was in an unloaded DLL\nand look for confirmation.\n<\/p>\n<pre>\n0:000&gt; lm\nstart    end        module name\n...\nUnloaded modules:\n10340000 10348000   DEF.DLL\n1e1c0000 1e781000   GHI.DLL\n25a90000 25a96000   JKL.DLL\n0:000&gt;\n<\/pre>\n<p>\nAha, our presumed vtable address lies right inside the address space\nwhere <code>GHI.DLL<\/code> used to be loaded.\nLet&#8217;s see what used to be loaded at that address.\nFor this, I borrow a trick from\n<a HREF=\"http:\/\/blogs.msdn.com\/doronh\/\">\nDoron<\/a>, namely\n<a HREF=\"http:\/\/blogs.msdn.com\/doronh\/archive\/2006\/03\/10\/549036.aspx\">\nloading a module as a dump file<\/a>.\nThis &#8220;virtually loads&#8221; the library so you can poke around inside it.\n<\/p>\n<pre>\nC:\\Program Files\\ABC&gt; ntsd -z GHI.DLL\nMicrosoft (R) Windows Debugger\nCopyright (c) Microsoft Corporation. All rights reserved.\nLoading Dump File [C:\\Program Files\\ABC\\GHI.DLL]\n...\nModLoad: 15800000 15dc1000   C:\\Program Files\\ABC\\GHI.DLL\neax=00000000 ebx=00000000 ecx=00000000 edx=00000000 esi=00000000 edi=00000000\neip=15807366 esp=00000000 ebp=00000000 iopl=0         nv up di pl nz na pe nc\ncs=0000  ss=0000  ds=0000  es=0000  fs=0000  gs=0000             efl=00000000\nGHI!_DllMainCRTStartup:\n15807366 8bff             mov     edi,edi\n0:000&gt;\n<\/pre>\n<p>\nThat module-load notification tells you where the DLL got\nvirtually-loaded;\nin our case, it got loaded to 0x15800000.\nThis isn&#8217;t the same address as it was in our crashed process,\nso we&#8217;ll have to do some mental arithmetic to account for the\ndiscrepancy.\n<\/p>\n<p>\nGoing back to the original register dump, we see that our\nputative vtable is at <code>ecx=1e1cfdf0<\/code> relative\nto the load address <code>1e1c0000<\/code>.\nSince our DLL-loaded-as-a-dump-file was loaded at <code>0x1580000<\/code>\nwe need to adjust the address to be relative to the new location.\n<\/p>\n<pre>\n\/\/ working with the second copy of ntsd\n0:000&gt; ln 0x1580fdf0\n(1580fdf0)   GHI!CAlphaStream::`vftable'\n<\/pre>\n<p>\nThat magic number <code>0x1580fdf0<\/code> is just the result of\nsome mental arithmetic.\nFirst:\n<\/p>\n<table BORDER=\"0\">\n<tr>\n<td ALIGN=\"right\"><code>0x1e1cfdf0<\/code><\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"right\" STYLE=\"border-bottom: solid black .75pt\"><code>-0x1e1c0000<\/code><\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"right\"><code>0x0000fdf0<\/code><\/td>\n<\/tr>\n<\/table>\n<p>\nThis is the address of the vtable in the crashed process\nrelative to the load address of the DLL in the crashed process.\nNext:\n<\/p>\n<table BORDER=\"0\">\n<tr>\n<td ALIGN=\"right\"><code>0x15800000<\/code><\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"right\" STYLE=\"border-bottom: solid black .75pt\"><code>+0x0000fdf0<\/code><\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"right\"><code>0x1580fdf0<\/code><\/td>\n<\/tr>\n<\/table>\n<p>\nThis is the address of the vtable in the DLL-loaded-as-a-dump-file\nrelative to the load address of the DLL in the DLL-loaded-as-a-dump-file.\nThe math really isn&#8217;t that hard, as you can see, since a lot of things\ncancel out.\nThis happens a lot.\n<\/p>\n<p>\nWhen we asked the debugger to tell us what symbol is nearest to that\naddress, we hit the jackpot: It is exactly a vtable for the\n<code>CAlphaStream<\/code> object.\nThis confirms our original theory.\nWe can even confirm the <code>IUnknown::Release<\/code> theory\nby dumping the vtable.\n<\/p>\n<pre>\n0:000&gt; dds 1580fdf0\n1580fdf0  159234b3 GHI!CAlphaStream::QueryInterface\n1580fdf4  15810539 GHI!<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2005\/03\/22\/400373.aspx\">CBetaState::AddRef<\/a>\n1580fdf8  15923cfc GHI!CAlphaStream::Release\n1580fdfc  15923d30 GHI!CAlphaStream::Read\n...\n<\/pre>\n<p>\nYup, that&#8217;s a <code>CAlphaStream<\/code> vtable all right.\n<\/p>\n<p>\nSince I&#8217;m not familiar with the <code>GHI.DLL<\/code> file,\nlet&#8217;s ask the debugger where the source code is so we can take a closer\nlook:\n<\/p>\n<pre>\n0:000&gt; .lines\nLine number information will be loaded\n0:000&gt; dds 1580fdf0\n1580fdf0  159234b3 GHI!CAlphaStream::QueryInterface\n                   [c:\\dev\\fabricam\\synergy\\proactive\\winwin.cpp @ 2624]\n1580fdf4  15810539 GHI!CBetaState::AddRef\n                   [c:\\dev\\fabricam\\leverage\\paradigm\\initiative.cpp @ 427]\n1580fdf8  15923cfc GHI!CAlphaStream::Release\n                   [c:\\dev\\fabricam\\synergy\\proactive\\winwin.cpp @ 2638]\n1580fdfc  15923d30 GHI!CAlphaStream::Read\n                   [c:\\dev\\fabricam\\synergy\\proactive\\winwin.cpp @ 2649]\n<\/pre>\n<p>\nNow that we know where the source code to\n<code>CAlphaStream<\/code> is, we can hop on over to take a quick peek\nand confirm that, oh look, the object doesn&#8217;t increment the DLL object\ncount when it is constructed (or decrement it when it is destructed).\nAs a result, when COM calls <code>DllCanUnloadNow<\/code>, the\n<code>GHI.DLL<\/code> says, &#8220;Sure, go ahead!&#8221;\nThe DLL is unloaded even though <code>ABC<\/code> still has a reference\nto it, and then when <code>ABC<\/code> goes to release that reference,\nwe crash because <code>GHI<\/code> is already gone.\n<\/p>\n<p>\nAfter I wrote this up, I discovered that\n<a HREF=\"http:\/\/blogs.msdn.com\/tonyschr\/\">\nTony Schreiner<\/a>\nwent through pretty much\n<a HREF=\"http:\/\/blogs.msdn.com\/tonyschr\/archive\/2006\/01\/09\/511029.aspx\">\nthe same exercise<\/a>\nwith a third-party Internet Explorer toolbar,\nexcept he had the extra bonus challenge of not having source code\nfor the plug-in!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Okay, so I gave it away in the title, but follow along anyway. Your program chugs along and then suddenly it crashes like this: eax=06bad8e8 ebx=00000000 ecx=1e1cfdf0 edx=00000000 esi=06b9a680 edi=01812950 eip=1180ab57 esp=001178b4 ebp=001178c0 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206 ABC!FunctionX+0x1f: 1180ab57 ff5108 call dword ptr [&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-27123","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-other"],"acf":[],"blog_post_summary":"<p>Okay, so I gave it away in the title, but follow along anyway. Your program chugs along and then suddenly it crashes like this: eax=06bad8e8 ebx=00000000 ecx=1e1cfdf0 edx=00000000 esi=06b9a680 edi=01812950 eip=1180ab57 esp=001178b4 ebp=001178c0 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206 ABC!FunctionX+0x1f: 1180ab57 ff5108 call dword ptr [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/27123","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=27123"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/27123\/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=27123"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=27123"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=27123"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}