{"id":43433,"date":"2014-12-10T07:00:00","date_gmt":"2014-12-10T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2014\/12\/10\/if-you-get-a-procedure-address-by-ordinal-you-had-better-be-absolutely-sure-its-there-because-the-failure-mode-is-usually-indistinguishable-from-success\/"},"modified":"2014-12-10T07:00:00","modified_gmt":"2014-12-10T07:00:00","slug":"if-you-get-a-procedure-address-by-ordinal-you-had-better-be-absolutely-sure-its-there-because-the-failure-mode-is-usually-indistinguishable-from-success","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20141210-00\/?p=43433","title":{"rendered":"If you get a procedure address by ordinal, you had better be absolutely sure it&#039;s there, because the failure mode is usually indistinguishable from success"},"content":{"rendered":"<p>\nA customer reported that the <code>Get&shy;Proc&shy;Address<\/code>\nfunction was behaving strangely.\n<\/p>\n<blockquote CLASS=\"q\">\n<p>\nWe have this code in one of our tests:\n<\/p>\n<pre>\ntypedef int (CALLBACK *T_FOO)(int);\nvoid TestFunctionFoo(HINSTANCE hDLL)\n{\n  \/\/ Function Foo is ordinal 1 in our DLL\n  T_FOO pfnFoo = (T_FOO)GetProcAddress(hDLL, (PCSTR)1);\n  if (pfnFoo) {\n    ... run tests on pfnFoo ...\n  }\n}\n<\/pre>\n<p>\nRecently, this test started failing in bizarre ways.\nWhen we stepped through the code, we discovered that\n<code>pfnFoo<\/code> ends up calling\n<code>Bar<\/code> instead of <code>Foo<\/code>.\nThe first time we try to test <code>pfnFoo<\/code>,\nwe get stack corruption because\n<code>Bar<\/code> has a different function prototype\nfrom\n<code>Foo<\/code>,\nand of course on top of that the test fails horribly because\nit&#8217;s calling the wrong function!\n<\/p>\n<p>\nWhen trying to narrow the problem, we found that the issue\nbegan when the test was run against a version of the DLL\nthat was missing the <code>Foo<\/code> function entirely.\nThe line\n<\/p>\n<pre>\n    Foo @1\n<\/pre>\n<p>\nwas removed from the DEF file.\nWhy did the call to\n<code>Get&shy;Proc&shy;Address<\/code> succeed and return the wrong\nfunction?\nWe expected it to fail.\n<\/p>\n<\/blockquote>\n<p>\nLet&#8217;s first consider the case where a DLL exports no functions\nby ordinal.\n<\/p>\n<pre>\nEXPORTS\n    Foo\n    Bar\n    <a HREF=\"http:\/\/plugh.com\/\">Plugh<\/a>\n<\/pre>\n<p>\nThe linker builds a list of\nall the exported functions (in an unspecified order)\nand fills in two arrays based on that list.\nIf you look in the DLL image, you&#8217;ll see something like this:\n<\/p>\n<pre>\nExported Function Table\n00049180 address of Bar\n00049184 address of Foo\n0004918C address of Plugh\nExported Names\n00049190 address of the string \"Bar\"\n00049194 address of the string \"Foo\"\n00049198 address of the string \"Plugh\"\n<\/pre>\n<p>\nThere are two parallel arrays,\none with function addresses and one with function names.\nThe string <code>\"Bar\"<\/code> is the first entry in the\nexported names table,\nand the function <code>Bar<\/code> is the first entry in the\nexported function table.\nIn general, the string in the\n<var>N<\/var>th entry in the exported names table\ncorresponds to the function in the\n<var>N<\/var>th entry of the exported function table.\n<\/p>\n<p>\nSince it is only the relative position that matters, let&#8217;s replace\nthe addresses with indices.\n<\/p>\n<pre>\nExported Function Table\n[1] address of Bar\n[2] address of Foo\n[3] address of Plugh\nExported Names\n[1] address of the string \"Bar\"\n[2] address of the string \"Foo\"\n[3] address of the string \"Plugh\"\n<\/pre>\n<p>\nOkay, now let&#8217;s introduce functions exported by ordinal.\nWhen you do that, you&#8217;re telling the linker,\n&#8220;Make sure this function goes into the <var>NN<\/var>th slot in the exported\nfunction table.&#8221;\nSuppose your DEF file went like this:\n<\/p>\n<pre>\nEXPORTS\n    Foo @1\n    Bar\n    Plugh\n<\/pre>\n<p>\nThis says &#8220;<a HREF=\"http:\/\/bartleby.com\/73\/1017.html\">First thing we do<\/a>\nis put <code>Foo<\/code> in slot 1.\nOnce that&#8217;s done, fill in the rest arbitrarily.&#8221;\n<\/p>\n<p>\nThe linker says,\n&#8220;Okay, I have a total of three functions, so let me build two tables\nwith three entries each.&#8221;\n<\/p>\n<pre>\nExported Function Table\n[1] address of ?\n[2] address of ?\n[3] address of ?\nExported Names\n[1] address of ?\n[2] address of ?\n[3] address of ?\n<\/pre>\n<p>\n&#8220;Now I place <code>Foo<\/code> in slot 1.&#8221;\n<\/p>\n<pre>\nExported Function Table\n[1] address of Foo\n[2] address of ?\n[3] address of ?\nExported Names\n[1] address of the string \"Foo\"\n[2] address of ?\n[3] address of ?\n<\/pre>\n<p>\n&#8220;Now I fill in the rest arbitrarily.&#8221;\n<\/p>\n<pre>\nExported Function Table\n[1] address of Foo\n[2] address of Bar\n[3] address of Plugh\nExported Names\n[1] address of the string \"Foo\"\n[2] address of the string \"Bar\"\n[3] address of the string \"Plugh\"\n<\/pre>\n<p>\nSince you explicitly placed <code>Foo<\/code> in slot 1,\nwhen you do a\n<code>Get&shy;Proc&shy;Address(hDLL, 1)<\/code>,\nyou will get\n<code>Foo<\/code>.\nOn the other hand, if you do a\n<code>Get&shy;Proc&shy;Address(hDLL, 2)<\/code>,\nyou will get <code>Bar<\/code>,\nor at least you will with this build.\nWith the next build, you may get something else,\nbecause the linker just fills in the slots arbitrarily,\nand next time, it may choose to fill them arbitrarily\nin some other order.\nFurthermore,\nif you do a\n<code>Get&shy;Proc&shy;Address(hDLL, 6)<\/code>,\nyou will get <code>NULL<\/code> because the table\nhas only three functions in it.\n<\/p>\n<p>\nI hope you see where this is going.\n<\/p>\n<p>\nIf you delete <code>Foo<\/code> from the <code>EXPORTS<\/code>\nsection,\nthis stops exporting <code>Foo<\/code> but says nothing about\nwhat goes into slot 1.\nAs a result, the linker is free to put anything it wants into that slot.\n<\/p>\n<pre>\nExported Function Table\n[1] address of Bar\n[2] address of Plugh\nExported Names\n[1] address of the string \"Bar\"\n[2] address of the string \"Plugh\"\n<\/pre>\n<p>\nNow, when you do a\n<code>Get&shy;Proc&shy;Address(hDLL, 1)<\/code>,\nyou get <code>Bar<\/code>,\nsince that&#8217;s the function that happened to fall into slot 1\nthis time.\n<\/p>\n<p>\nThe moral of the story is that if you try to obtain a function\nby ordinal,\nthen it had better be there,\nbecause there is no reliable way of being sure that the function\nyou got is one that was explicitly placed there,\nas opposed to some other function that happened to be assigned that\nslot arbitrarily.\n<\/p>\n<p>\n<b>Related reading<\/b>:\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2006\/07\/18\/669668.aspx\">\nHow are DLL functions exported in 32-bit Windows?<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>A customer reported that the Get&shy;Proc&shy;Address function was behaving strangely. We have this code in one of our tests: typedef int (CALLBACK *T_FOO)(int); void TestFunctionFoo(HINSTANCE hDLL) { \/\/ Function Foo is ordinal 1 in our DLL T_FOO pfnFoo = (T_FOO)GetProcAddress(hDLL, (PCSTR)1); if (pfnFoo) { &#8230; run tests on pfnFoo &#8230; } } Recently, this test [&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":[25],"class_list":["post-43433","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>A customer reported that the Get&shy;Proc&shy;Address function was behaving strangely. We have this code in one of our tests: typedef int (CALLBACK *T_FOO)(int); void TestFunctionFoo(HINSTANCE hDLL) { \/\/ Function Foo is ordinal 1 in our DLL T_FOO pfnFoo = (T_FOO)GetProcAddress(hDLL, (PCSTR)1); if (pfnFoo) { &#8230; run tests on pfnFoo &#8230; } } Recently, this test [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/43433","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=43433"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/43433\/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=43433"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=43433"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=43433"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}