{"id":2653,"date":"2013-11-15T07:00:00","date_gmt":"2013-11-15T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2013\/11\/15\/restoring-symbols-to-a-stack-trace-originally-generated-without-symbols\/"},"modified":"2013-11-15T07:00:00","modified_gmt":"2013-11-15T07:00:00","slug":"restoring-symbols-to-a-stack-trace-originally-generated-without-symbols","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20131115-00\/?p=2653","title":{"rendered":"Restoring symbols to a stack trace originally generated without symbols"},"content":{"rendered":"<p>\n<a HREF=\"https:\/\/www.youtube.com\/watch?v=08xQLGWTSag\">\nHas this ever happened to you<\/a>?\n<\/p>\n<pre>\nlitware!Ordinal3+0x6042\nlitware!DllInstall+0x4c90\nlitware!DllInstall+0x4b9e\ncontoso!DllGetClassObject+0x93c3\ncontoso!DllGetClassObject+0x97a9\ncontoso!DllGetClassObject+0x967c\ncontoso!DllGetClassObject+0x94d7\ncontoso!DllGetClassObject+0x25ce\ncontoso!DllGetClassObject+0x2f7b\ncontoso!DllGetClassObject+0xad55\ncontoso!DllGetClassObject+0xaec7\ncontoso!DllGetClassObject+0xadf7\ncontoso!DllGetClassObject+0x3c00\ncontoso!DllGetClassObject+0x3b2a\ncontoso!DllGetClassObject+0x462b\nUSER32!UserCallWinProcCheckWow+0x13a\nUSER32!DispatchMessageWorker+0x1a7\ncontoso!DllCanUnloadNow+0x19b6\ncontoso!DllGetClassObject+0xeaf2\ncontoso+0x1d6c\nlitware!LitImportReportProfile+0x11c4\nlitware!LitImportReportProfile+0x1897\nlitware!LitImportReportProfile+0x1a3b\nKERNEL32!BaseThreadInitThunk+0x18\nntdll!RtlUserThreadStart+0x1d\n<\/pre>\n<p>\nUgh.\nA stack trace taken without working symbols.\n(There&#8217;s no way that\n<code>Dll&shy;Get&shy;Class&shy;Object<\/code>\nis a deeply recursive 60KB function.\nJust by casual inspection, you know that the symbols are wrong.)\n<\/p>\n<p>\nTo see how to fix this, you just have to understand what the\ndebugger does when it has no symbols to work from:\nIt uses the symbols from the exported function table.\nFor every address it wants to resolve,\nit looks for the nearest exported function whose address is\n<a HREF=\"https:\/\/www.youtube.com\/watch?v=1ytCEuuW2_A\">\nless than or equal to<\/a> the target value.\n<\/p>\n<p>\nFor example, suppose <code>CONTOSO.DLL<\/code>\nhas the following exported symbols:\n<\/p>\n<table BORDER=\"0\">\n<tr>\n<th>Symbol<\/th>\n<th>Offset<\/th>\n<\/tr>\n<tr>\n<td><code>Dll&shy;Get&shy;Class&shy;Object<\/code><\/td>\n<td ALIGN=\"right\"><code>0x5132<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>Dll&shy;Can&shy;Unload&shy;Now<\/code><\/td>\n<td ALIGN=\"right\"><code>0xFB0B<\/code><\/td>\n<\/tr>\n<\/table>\n<p>\nLook at it this way:\nThe debugger is given the following information about your module:\n(Diagram not to scale.)\n<\/p>\n<table BORDER=\"0\" STYLE=\"border-collapse: collapse\" TITLE=\"An empty horizontal box with the label DllGetClassObject at approximately 20% and the label DllCanUnloadNow at approximately 60%.\">\n<tr>\n<td COLSPAN=\"5\" STYLE=\"border: solid black .75pt\">&nbsp;<\/td>\n<\/tr>\n<tr>\n<td STYLE=\"width: 2em\">&nbsp;<\/td>\n<td STYLE=\"width: 1em\" ALIGN=\"center\">&uarr;<\/td>\n<td STYLE=\"padding-right: 4em\">Dll&shy;Get&shy;Class&shy;Object<\/td>\n<td STYLE=\"width: 1em\" ALIGN=\"center\">&uarr;<\/td>\n<td STYLE=\"padding-right: 8em\">Dll&shy;Can&shy;Unload&shy;Now<\/td>\n<\/tr>\n<\/table>\n<p>\nIt needs to assign a function to every byte in the module.\nIn the absence of any better information, it does it like this:\n<\/p>\n<table BORDER=\"0\" STYLE=\"border-collapse: collapse\" TITLE=\"A horizontal box in three parts. From 0% to 20% is labeled with question marks. From 20% to 60% is labeled DllGetClassObject. From 60% to 100% is labeled DllCanUnloadNow.\">\n<tr>\n<td STYLE=\"border: solid black .75pt;width: 2em;background-color: #FFC0C0\" ALIGN=\"center\">???<\/td>\n<td STYLE=\"border: solid black .75pt;padding-right: 5em;background-color: #C0FFFF\" ALIGN=\"right\">Dll&shy;Get&shy;Class&shy;Object<\/td>\n<td STYLE=\"border: solid black .75pt;padding-right: 9em;background-color: #FFC0FF\">Dll&shy;Can&shy;Unload&shy;Now<\/td>\n<\/tr>\n<\/table>\n<p>\nIn words, it assumes that every function begins at the location specified\nby the export table,\nand it ends one byte before the start of the next function.\nThe debugger is trying to make the best of a bad situation.\n<\/p>\n<p>\nSuppose your DLL was loaded at\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2012\/12\/27\/10380848.aspx\">\n<code>0x10000000<\/code><\/a>,\nand the debugger needs to generate a symbolic name for the address\n<code>0x1000E4F5<\/code>.\n<\/p>\n<p>\nFirst, it converts the address into a relative virtual address\nby subtracting the DLL base address,\nleaving <code>0xE4F5<\/code>.\n<\/p>\n<p>\nNext, it looks to see what function &#8220;contains&#8221; that address.\nFrom the algorithm described above,\nthe debugger concludes that the address <code>0xE4F5<\/code> is\n&#8220;part of&#8221; the\n<code>Dll&shy;Get&shy;Class&shy;Object<\/code> function,\nwhich began at\nbegins at <code>0x5132<\/code>.\nThe offset into the function is therefore\n<code>0xE4F5 - 0x5132 = 0x93C3<\/code>,\nand it is reported in the debugger as\n<code>contoso!Dll&shy;Get&shy;Class&shy;Object+0x93c3<\/code>.\n<\/p>\n<p>\nRepeat this exercise for each address that the debugger needs to resolve,\nand you get the stack trace above.\n<\/p>\n<p>\nFine, now that you know how the bad symbols were generated,\nhow do you fix it?\n<\/p>\n<p>\nYou fix it by undoing what the debugger did, and then redoing it\nwith better symbols.\n<\/p>\n<p>\nYou need to find the better symbols.\nThis is not too difficult if you still have a matching binary\nand symbol file,\nbecause you can just load up the binary into the debugger\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/doronh\/archive\/2006\/03\/20\/556053.aspx\">\nin the style of a dump file<\/a>.\nLike Doron, you can then let the debugger do the hard work.\n<\/p>\n<pre>\nC:&gt; ntsd -z contoso.dll\nModLoad: 10000000 10030000   contoso.dll\n<\/pre>\n<p>\nNow you just ask the debugger,\n&#8220;Could you disassemble this function for me?&#8221;\nYou give it the broken symbol+offset above.\nThe debugger looks up the symbol,\napplies the offset,\nand then <i>looks up the correct symbol<\/i> when disassembling.\n<\/p>\n<pre>\n0:000&gt; u contoso!DllGetClassObject+0x93c3\n<font COLOR=\"red\">contoso!CReportViewer::ActivateReport+0xe9<\/font>:\n10000e4f5 eb05            jmp     contoso!CReportViewer::ActivateReport+0xf0\n<\/pre>\n<p>\nRepeat for each broken symbol in the stack trace,\nand you have yourself a repaired stack trace.\n<\/p>\n<pre>\nlitware!Ordinal3+0x6042 <font COLOR=\"red\">&larr; oops<\/font>\nlitware!CViewFrame::SetInitialKeyboardFocus+0x58\nlitware!CViewFrame::ActivateViewInFrame+0xf2\ncontoso!CReportViewer::ActivateReport+0xe9\ncontoso!CReportViewer::LoadReport+0x12c\ncontoso!CReportViewer::OnConnectionCreated+0x13f\ncontoso!CViewer::OnConnectionEvent+0x7f\ncontoso!CConnectionManager::OnConnectionCreated+0x85\ncontoso!CReportFactory::BeginCreateConnection+0x87\ncontoso!CReportViewer::CreateConnectionForReport+0x20d\ncontoso!CViewer::CreateNewConnection+0x87\ncontoso!CReportViewer::CreateNewReport+0x213\ncontoso!CViewer::OnChangeView+0xec\ncontoso!CReportViewer::WndProc+0x9a7\ncontoso!CView::s_WndProc+0xf1\nUSER32!UserCallWinProcCheckWow+0x13a\nUSER32!DispatchMessageWorker+0x1a7\ncontoso!CViewer::MessageLoop+0x24e\ncontoso!CViewReportTask::RunViewer+0x12\ncontoso+0x1d6c <font COLOR=\"red\">&larr; oops<\/font>\nlitware!CThreadTask::Run+0x40\nlitware!CThread::ThreadProc+0xe5\nlitware!CThread::s_ThreadProc+0x42\nKERNEL32!BaseThreadInitThunk+0x18\nntdll!RtlUserThreadStart+0x1d\n<\/pre>\n<p>\nOops, our trick doesn&#8217;t work for that first entry in the stack trace,\nthe one with <code>Ordinal3<\/code>.\nWhat&#8217;s up with that?\nThere is no function called <code>Ordinal3<\/code>!\n<\/p>\n<p>\nIf your module exports functions by ordinal without a name,\nthen the debugger doesn&#8217;t know what name to print for the function\n(since the name was stripped from the module),\nso it just prints the ordinal number.\nYou will have to go back to your DLL&#8217;s <code>DEF<\/code> file\nto convert the ordinal back to a function name.\nOr you can\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2011\/05\/13\/10164020.aspx\">\ndump the exports from the DLL\nto see what functions match up with what ordinals<\/a>.\n(Of course, for that trick to work, you need to have a matching PDB file\nin the symbol search path.)\n<\/p>\n<p>\nIn our example, suppose <code>litware.dll<\/code> ordinal&nbsp;3\ncorresponds to the function\n<code>Lit&shy;Debug&shy;Report&shy;Profile<\/code>.\nWe would then ask the debugger\n<\/p>\n<pre>\n0:001&gt; u litware!LitDebugReportProfile+0x6042\n<font COLOR=\"red\">litware!CViewFrame::FindInitialFocusControl+0x66<\/font>:\n1000084f5 33db            xor     ebx,ebx\n<\/pre>\n<p>\nOkay, that takes care of our first oops.\nWhat about the second one?\n<\/p>\n<p>\nIn the second case, the address the debugger was asked to\ngenerate a symbol for came before the first symbol in the module.\nIn our diagram above, it was in the area marked with question marks.\nThe debugger has absolutely nothing to work with, so it just\ndisassembles as relative to the start of the module.\n<\/p>\n<p>\nTo resolve this symbol, you take the offset and add it to the\nbase of the module as it was loaded into the debugger,\nwhich was reported in the <code>ModLoad<\/code> output:\n<\/p>\n<pre>\nModLoad: <font COLOR=\"red\">10000000<\/font> 10030000   contoso.dll\n<\/pre>\n<p>\nIf that output scrolled off the screen, you can ask the debugger\nto show it again with the help of the <code>lmm<\/code> command.\n<\/p>\n<pre>\n0:001&gt;lmm contoso*\nstart    end        module name\n10000000 10030000   contoso    (export symbols)       contoso.dll\n<\/pre>\n<p>\nOnce you have the base address, you\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2007\/04\/25\/2265923.aspx\">\nadd the offset back<\/a>\nand ask the debugger what&#8217;s there:\n<\/p>\n<pre>\n0:001&gt; u 0x10000000+0x1d6c\n<font COLOR=\"red\">contoso!CViewReportTask::Run+0x102<\/font>:\n100001d6c 50              push    eax\n<\/pre>\n<p>\nOkay, now that we patched up all our oopses,\nwe have the full stack trace with symbols:\n<\/p>\n<pre>\nlitware!CViewFrame::FindInitialFocusControl+0x66\nlitware!CViewFrame::SetInitialKeyboardFocus+0x58\nlitware!CViewFrame::ActivateViewInFrame+0xf2\ncontoso!CReportViewer::ActivateReport+0xe9\ncontoso!CReportViewer::LoadReport+0x12c\ncontoso!CReportViewer::OnConnectionCreated+0x13f\ncontoso!CViewer::OnConnectionEvent+0x7f\ncontoso!CConnectionManager::OnConnectionCreated+0x85\ncontoso!CReportFactory::BeginCreateConnection+0x87\ncontoso!CReportViewer::CreateConnectionForReport+0x20d\ncontoso!CViewer::CreateNewConnection+0x87\ncontoso!CReportViewer::CreateNewReport+0x213\ncontoso!CViewer::OnChangeView+0xec\ncontoso!CReportViewer::WndProc+0x9a7\ncontoso!CView::s_WndProc+0xf1\nUSER32!UserCallWinProcCheckWow+0x13a\nUSER32!DispatchMessageWorker+0x1a7\ncontoso!CViewer::MessageLoop+0x24e\ncontoso!CViewReportTask::RunViewer+0x12\ncontoso!CViewReportTask::Run+0x102\nlitware!CThreadTask::Run+0x40\nlitware!CThread::ThreadProc+0xe5\nlitware!CThread::s_ThreadProc+0x42\nKERNEL32!BaseThreadInitThunk+0x18\nntdll!RtlUserThreadStart+0x1d\n<\/pre>\n<p>\nNow the fun actually starts:\nFiguring out why there was a break in\n<code>CView&shy;Frame::Find&shy;Initial&shy;Focus&shy;Control<\/code>.\nHappy debugging!\n<\/p>\n<p>\n<b>Bonus tip<\/b>:\nBy default,\n<code>ntsd<\/code> does not include line numbers when resolving symbols.\nType <code>.lines<\/code> to toggle line number support.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Has this ever happened to you? litware!Ordinal3+0x6042 litware!DllInstall+0x4c90 litware!DllInstall+0x4b9e contoso!DllGetClassObject+0x93c3 contoso!DllGetClassObject+0x97a9 contoso!DllGetClassObject+0x967c contoso!DllGetClassObject+0x94d7 contoso!DllGetClassObject+0x25ce contoso!DllGetClassObject+0x2f7b contoso!DllGetClassObject+0xad55 contoso!DllGetClassObject+0xaec7 contoso!DllGetClassObject+0xadf7 contoso!DllGetClassObject+0x3c00 contoso!DllGetClassObject+0x3b2a contoso!DllGetClassObject+0x462b USER32!UserCallWinProcCheckWow+0x13a USER32!DispatchMessageWorker+0x1a7 contoso!DllCanUnloadNow+0x19b6 contoso!DllGetClassObject+0xeaf2 contoso+0x1d6c litware!LitImportReportProfile+0x11c4 litware!LitImportReportProfile+0x1897 litware!LitImportReportProfile+0x1a3b KERNEL32!BaseThreadInitThunk+0x18 ntdll!RtlUserThreadStart+0x1d Ugh. A stack trace taken without working symbols. (There&#8217;s no way that Dll&shy;Get&shy;Class&shy;Object is a deeply recursive 60KB function. Just by casual inspection, you [&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-2653","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-other"],"acf":[],"blog_post_summary":"<p>Has this ever happened to you? litware!Ordinal3+0x6042 litware!DllInstall+0x4c90 litware!DllInstall+0x4b9e contoso!DllGetClassObject+0x93c3 contoso!DllGetClassObject+0x97a9 contoso!DllGetClassObject+0x967c contoso!DllGetClassObject+0x94d7 contoso!DllGetClassObject+0x25ce contoso!DllGetClassObject+0x2f7b contoso!DllGetClassObject+0xad55 contoso!DllGetClassObject+0xaec7 contoso!DllGetClassObject+0xadf7 contoso!DllGetClassObject+0x3c00 contoso!DllGetClassObject+0x3b2a contoso!DllGetClassObject+0x462b USER32!UserCallWinProcCheckWow+0x13a USER32!DispatchMessageWorker+0x1a7 contoso!DllCanUnloadNow+0x19b6 contoso!DllGetClassObject+0xeaf2 contoso+0x1d6c litware!LitImportReportProfile+0x11c4 litware!LitImportReportProfile+0x1897 litware!LitImportReportProfile+0x1a3b KERNEL32!BaseThreadInitThunk+0x18 ntdll!RtlUserThreadStart+0x1d Ugh. A stack trace taken without working symbols. (There&#8217;s no way that Dll&shy;Get&shy;Class&shy;Object is a deeply recursive 60KB function. Just by casual inspection, you [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/2653","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=2653"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/2653\/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=2653"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=2653"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=2653"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}