{"id":104125,"date":"2020-08-26T07:00:00","date_gmt":"2020-08-26T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=104125"},"modified":"2020-09-09T06:10:32","modified_gmt":"2020-09-09T13:10:32","slug":"20200826-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200826-00\/?p=104125","title":{"rendered":"Why are some system functions exported as stubs instead as forwarders?"},"content":{"rendered":"<p>If you do a little digging around inside some Windows system functions, you&#8217;ll see that, for example, the <code>Create\u00adProcessW<\/code> function looks like this:<\/p>\n<pre>kernel32!CreateProcessW:\r\n6b819ef0 mov     edi,edi\r\n6b819ef2 push    ebp\r\n6b819ef3 mov     ebp,esp\r\n6b819ef5 pop     ebp\r\n6b819ef6 jmp     dword ptr [kernel32!kernelbase_CreateProcessW]\r\n<\/pre>\n<p>The first four instructions have no net effect, so basically this is just an indirect jump to the <code>kernelbase!CreateProcessW<\/code> function. In other words, it&#8217;s a stub that forwards to the real implementation over in <code>kernelbase<\/code>.<\/p>\n<p>Why is it done this way? Why isn&#8217;t the <code>Create\u00adProcessW<\/code> function just a forwarder to <code>kernelbase<\/code>? That would avoid having to travel through <code>kernel32<\/code> just to reach <code>kernelbase<\/code>.<\/p>\n<p>Yes, this would normally be a forwarder, but it&#8217;s not. For backward compatibility.<\/p>\n<p>Wait, why is there a compatibility constraint that the <code>Create\u00adProcessW<\/code> function cannot be a forwarder?<\/p>\n<p>Set the time machine to 2001. <a href=\"https:\/\/en.wikipedia.org\/wiki\/Microsoft_Layer_for_Unicode\"> The Microsoft Layer for Unicode (MSLU)<\/a> was just released, also affectionately known as &#8220;Unicows&#8221;, after the DLL component of MSLU: <code>unicows.dll<\/code>.<\/p>\n<p>MSLU was a combination of a static library and a DLL. You wrote a Unicode application and linked it with the MSLU static library. This library contained its own definitions for a large number of functions, including <code>Create\u00adProcessW<\/code>. When your Unicode application called the alternate version of <code>Create\u00adProcessW<\/code>, the library checked whether it was running on a version of Windows that was ANSI-only (the Windows 95 series) or a version that supported Unicode (the Windows NT series).<\/p>\n<p>If it was running on an ANSI-only system, then the stub loaded the <code>unicows.dll<\/code> library and forwarded the call to a helper function in that library which did the work of thunking the Unicode parameters to ANSI, and then calling the <code>Create\u00adProcessA<\/code> function, and then converting the results back to Unicode, and returning that to the caller. If it was running on a Unicode system, then it forwarded the call to the operating system&#8217;s <code>Create\u00adProcessW<\/code> function.<\/p>\n<p>In other words, the static library contained a stub that decided whether to allow the Unicode call to go straight to the Unicode version of the underlying function, or whether it should convert the call to ANSI and call the ANSI version of the underlying function.<\/p>\n<p>Okay, great, so where do DLL forwarders come into the story?<\/p>\n<p>After the MSLU static library decides which code path it should use, it goes back and patches the the caller&#8217;s import table to point directly to the destination function. That way, the second and subsequent calls are direct and don&#8217;t go through the evaluation step again. (This is the same sort of trick that the delay-load stubs use.)<\/p>\n<p>In the case where the MSLU static library decided to pass the function straight to the Unicode version of the underlying function, it needs to get the address of that Unicode version of the underlying function. For reasons not entirely clear to me, it doesn&#8217;t use the <code>Get\u00adProc\u00adAddress<\/code> function.\u00b9 Instead it has a custom implementation of <code>Get\u00adProc\u00adAddress<\/code> which parses the DLL export table manually to find the function to forward to.<\/p>\n<p>That custom implementation of <code>Get\u00adProc\u00adAddress<\/code> doesn&#8217;t support forwarders. There&#8217;s even a comment acknowledging as much:<\/p>\n<pre>   \/\/ This is a forwarder - Ignore for now.\r\n<\/pre>\n<p>Therefore, any function supported by MSLU may not take the form of a DLL forwarder. It must be a stub. Just in case somebody runs a program from the early 2000s written with MSLU.<\/p>\n<p><b>Bonus chatter<\/b>: This requirement that the function be a stub and not be a forwarder applies only to the x86-32 version of Windows, since that&#8217;s the only architecture supported by the Windows 95 series, and therefore the only one supported by MSLU. However, the functions are stubs on all architectures, presumably for simplicity of implementation.<\/p>\n<p>\u00b9 My suspicion was that it does this to avoid certain reentrancy issues in the loader, but I&#8217;m not sure.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Compatibility, of course, at least for some of them.<\/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-104125","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Compatibility, of course, at least for some of them.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104125","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=104125"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104125\/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=104125"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=104125"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=104125"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}