{"id":36953,"date":"2004-12-17T07:00:00","date_gmt":"2004-12-17T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2004\/12\/17\/how-did-windows-95-rebase-dlls\/"},"modified":"2004-12-17T07:00:00","modified_gmt":"2004-12-17T07:00:00","slug":"how-did-windows-95-rebase-dlls","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20041217-00\/?p=36953","title":{"rendered":"How did Windows 95 rebase DLLs?"},"content":{"rendered":"<p>Windows&nbsp;95 handled DLL-rebasing very differently from Windows&nbsp;NT.<\/p>\n<p> When Windows&nbsp;NT detects that a DLL needs to be loaded at an address different from its preferred load address, it maps the entire DLL as copy-on-write, fixes it up (causing all pages that contain fixups to be dumped into the page file), then restores the read-only\/read-write state to the pages. (<a href=\"http:\/\/weblogs.asp.net\/larryosterman\/archive\/2004\/07\/06\/174516.aspx\">Larry Osterman went into greater detail on this subject earlier this year<\/a>.) <\/p>\n<p> Windows&nbsp;95, on the other hand, rebases the DLL incrementally. This is another concession to Windows&nbsp;95&#8217;s very tight memory requirements. Remember, it had to run on a 4MB machine. If it fixed up DLLs the way Windows&nbsp;NT did, then loading a 4MB DLL and fixing it up would consume all the memory on the machine, pushing out all the memory that was actually worth keeping! <\/p>\n<p> When a DLL needed to be rebased, Windows&nbsp;95 would merely make a note of the DLL&#8217;s new base address, but wouldn&#8217;t do much else. The real work happened when the pages of the DLL ultimately got swapped in. The raw page was swapped off the disk, then the fix-ups were applied on the fly to the raw page, thereby relocating it. The fixed-up page was then mapped into the process&#8217;s address space and the program was allowed to continue. <\/p>\n<p> This method has the advantage that the cost of fixing up a page is not paid until the page is actually needed, which can be a significant savings for large DLLs of mostly-dead code. Furthermore, when a fixed-up page needed to be swapped out, it was merely discarded, because the fix-ups could just be applied to the raw page again. <\/p>\n<p> And there you have it, demand-paging rebased DLLs instead of fixing up the entire DLL at load time. What could possibly go wrong? <\/p>\n<p> Hint: It&#8217;s a problem that is peculiar to the x86. <\/p>\n<p> The problem is fix-up that straddle page boundaries. This happens only on the x86 because <a href=\"http:\/\/weblogs.asp.net\/oldnewthing\/archive\/2004\/09\/14\/229387.aspx\"> the x86 architecture is the weirdo<\/a>, with variable-length instructions that can start at any address. If a page contains a fix-up that extends partially off the start of the page, you cannot apply it accurately until you know whether or not the part of the fix-up you can&#8217;t see generated a carry. If it did, then you have to add one to your partial fix-up. <\/p>\n<p> To record this information, the memory manager associates a flag with each page of a relocated DLL that indicates whether the page contained a carry off the end. This flag can have one of three states: <\/p>\n<ul>\n<li>Yes, there is a carry off the end. <\/li>\n<li>No, there is no carry off the end. <\/li>\n<li>I don&#8217;t know whether there is a carry off the end. <\/li>\n<\/ul>\n<p> To fix up a page that contains a fix-up that extends partially off the start of the page, you check the flag for the previous page. If the flag says &#8220;Yes&#8221;, then add one to your fix-up. If the flag says &#8220;No&#8221;, then do not add one. <\/p>\n<p> But what if the flag says &#8220;I don&#8217;t know?&#8221; <\/p>\n<p> If you don&#8217;t know, then you have to go find out. Fault in the previous page and fix it up. As part of the computations for the fix-up, the flag will get to indicate whether there is a carry out the end. Once the previous page has been fixed up, you can check the flag (which will no longer be a &#8220;Don&#8217;t know&#8221; flag), and that will tell you whether or not to add one to the current page. <\/p>\n<p> And there you have it, demand-paging rebased DLLs instead of fixing up the entire DLL at load time, even in the presence of fix-ups that straddle page boundaries. What could possibly go wrong? <\/p>\n<p> Hint: What goes wrong with recursion? <\/p>\n<p> The problem is that the previous page might itself have a fix-up that straddled a page boundary at its start, and the flag for the page two pages back might be in the &#8220;I don&#8217;t know&#8221; state. Now you have to fault in and fix up a third page. <\/p>\n<p> Fortunately, in practice this doesn&#8217;t go beyond three fix-ups. Three pages of chained fix-ups was the record. <\/p>\n<p> (Of course, another way to stop the recursion is to do only a partial fix-up of the previous page, applying only the straddling fix-up to see whether there is a carry out and not attempting to fix up the rest. But Windows&nbsp;95 went ahead and fixed up the rest of the page because it figured, hey, I paid for this page, I may as well use it.) <\/p>\n<p> What was my point here? I don&#8217;t think I have one. It was just a historical tidbit that I hoped somebody might find interesting. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Windows&nbsp;95 handled DLL-rebasing very differently from Windows&nbsp;NT. When Windows&nbsp;NT detects that a DLL needs to be loaded at an address different from its preferred load address, it maps the entire DLL as copy-on-write, fixes it up (causing all pages that contain fixups to be dumped into the page file), then restores the read-only\/read-write state to [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[2],"class_list":["post-36953","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-history"],"acf":[],"blog_post_summary":"<p>Windows&nbsp;95 handled DLL-rebasing very differently from Windows&nbsp;NT. When Windows&nbsp;NT detects that a DLL needs to be loaded at an address different from its preferred load address, it maps the entire DLL as copy-on-write, fixes it up (causing all pages that contain fixups to be dumped into the page file), then restores the read-only\/read-write state to [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/36953","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=36953"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/36953\/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=36953"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=36953"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=36953"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}