{"id":15123,"date":"2010-02-01T07:00:00","date_gmt":"2010-02-01T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2010\/02\/01\/why-cant-i-use-the-linker-to-delay-load-a-function-from-kernel32\/"},"modified":"2010-02-01T07:00:00","modified_gmt":"2010-02-01T07:00:00","slug":"why-cant-i-use-the-linker-to-delay-load-a-function-from-kernel32","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20100201-00\/?p=15123","title":{"rendered":"Why can&#039;t I use the linker to delay-load a function from kernel32?"},"content":{"rendered":"<p>\nFor some time (I am too lazy to look up when it was introduced),\nthe Visual Studio linker has supported\na feature\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2008\/02\/04\/7439592.aspx\">\nknown as delay-loading<\/a>.\nBut why can&#8217;t you use this feature to delay-load a function\nfrom <code>kernel32<\/code>?\nIt would be very handy:\nIf you write\n<\/p>\n<pre>\nif (CurrentWindowsVersionSupportsKernelFunctionXyz())\n{\n  Xyz(...);\n}\n<\/pre>\n<p>\nthe program fails to load on versions of Windows which do not\nsupport the function <code>Xyz<\/code>\nbecause\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2003\/09\/16\/54938.aspx\">\nthe Win32 load rejects loading a module that contains unresolved\nreferences<\/a>.\nOn the other hand, if you could mark <code>kernel32<\/code> as\ndelay-loaded, then the code above would work,\nsince the call to <code>Xyz<\/code> would be redirected to a stub\nthat calls <code>GetProcAddress<\/code>.\nSince the <code>GetProcAddress<\/code>\nis performed only when the code path is hit,\nthe loader won&#8217;t complain at load time.\nBut if you try to delay-load <code>kernel32<\/code>,\nthe linker gets upset at you.\nWhy won&#8217;t it let me delay-load <code>kernel32<\/code>?\n<\/p>\n<p>\nThe linker delay-load feature operates on the DLL level,\nnot on the function level.\nWhen you put a DLL on the <code>\/DELAYLOAD<\/code> list,\nthe linker changes all calls to functions in that DLL\ninto calls to linker-generated stubs.\nThese stubs load the target DLL, call <code>GetProcAddress<\/code>,\nthen resume execution at the target function.\n<\/p>\n<p>\nSince the delay-load feature operates on the DLL level,\nif you put <code>kernel32<\/code>\non the delay-load list,\nthen <i>all<\/i> calls to functions in <code>kernel32<\/code>\nturn into calls to stubs.\n<\/p>\n<p>\nAnd then you are trapped in this Catch-22.\n<\/p>\n<p>\nWhen a function from <code>kernel32<\/code> gets called,\ntransfer goes to the stub function, which loads the\ntarget DLL (<code>kernel32<\/code>) to get the target function.\nExcept that loading the target DLL means calling\n<code>LoadLibrary<\/code>,\nand finding the target function means calling\n<code>GetProcAddress<\/code>,\nand these functions\n<i>themselves reside in <code>kernel32<\/code><\/i>.\n<\/p>\n<p>\nNow you&#8217;re trapped.\nTo load <code>kernel32<\/code>,\nwe need to call <code>LoadLibrary<\/code>,\nbut our call to <code>LoadLibrary<\/code> was redirected\nto a stub which&#8230; calls <code>LoadLibrary<\/code>.\n<\/p>\n<p>\nSure, the linker folks could have added special casing for\n<code>kernel32<\/code>, say, having a list of core functions\nlike <code>InitializeCriticalSection<\/code> which are\nnever delay-loaded and always go directly into <code>kernel32<\/code>.\nBut that&#8217;s really out of scope for the <code>\/DELAYLOAD<\/code> feature,\nwhose purpose is not to make it easier to call functions which\nmight not be there,\nbut rather to assist in application startup performance\nby avoiding the cost of loading the target DLL until a function from\nit is called.\nIf there were functions that went directly into <code>kernel32<\/code>,\nthen the stated purpose of delay-loading fails:\nthat import of <code>InitializeCriticalSection<\/code> forces\n<code>kernel32<\/code> to be loaded when the module is loaded,\ncompletely contrary to the aim of delay-loading to avoid\nloading <code>kernel32<\/code> at module load time.\n<\/p>\n<p>\nNow, it&#8217;s certainly a nice feature to be able to perform\ndelay-loading on a per-function level,\nin order to make it easier to write\ncode which changes behavior based on the current version of Windows,\nbut that&#8217;s a different problem from what the <code>\/DELAYLOAD<\/code>\nswitch was created to solve.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>For some time (I am too lazy to look up when it was introduced), the Visual Studio linker has supported a feature known as delay-loading. But why can&#8217;t you use this feature to delay-load a function from kernel32? It would be very handy: If you write if (CurrentWindowsVersionSupportsKernelFunctionXyz()) { Xyz(&#8230;); } the program fails to [&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-15123","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>For some time (I am too lazy to look up when it was introduced), the Visual Studio linker has supported a feature known as delay-loading. But why can&#8217;t you use this feature to delay-load a function from kernel32? It would be very handy: If you write if (CurrentWindowsVersionSupportsKernelFunctionXyz()) { Xyz(&#8230;); } the program fails to [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/15123","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=15123"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/15123\/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=15123"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=15123"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=15123"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}