If a lot of settings are ignored when set in a DLL, why bother even letting you set them on a DLL?


There are many settings that are ignored when set in a DLL. /LARGE­ADDRESS­AWARE. Size­Of­Stack­Reserve and Size­Of­Stack­Commit. There are plenty of others. Commenter 640k asks why these settings even exist for DLLs if they has no effect.

Because they are settings for PE modules in general.

If there were separate file formats for EXEs and DLLs, then there would have to be two different module loaders, one for EXEs and one for DLLs. This creates extra work for no particular benefit aside from satisfying some anal-retentive compulsion that nothing be wasted. As far as I can tell, all operating systems use a common file format for both executables and libraries.

If it really bothers you, you can consider this flags as Reserved for future use when applied to DLLs.

Comments (34)
  1. kantos says:

    /LARGEADDRESSAWARE does actually affect DLLs at least in 64bit. I've had problems on a legacy application that was being ported from 32 to 64bit that only showed up if that flag was set. Not surprisingly they were all related to casting pointers to ints and back. In this case setting the flag would cause the loader to load the DLL above 4gb in the address space thus corrupting the results of the casts if a pointer to a DLL resource or function was passed through it.

  2. SMW says:

    @kantos: I'm guessing you had the flag set on the build of the EXE and not on the build of the DLL.

  3. kantos says:

    @SMW We tested that, it actually was the DLL itself that mattered. We specifically tested the EXE with and without it set, with the flag not set on the DLL. We could only reproduce the issue with the flag set on the DLL itself. It was very frustrating to figure out that was what was causing it.

    On a side note: Windows 8 does seem to ignore it, but Windows 7 does not. As I was on Windows 8, my colleagues on Windows 7 were unable to reproduce the problem until the flag was set.

  4. Eric Wilson says:

    A think the more salient question would have been "given they have no effect, why does Visual Studio display these settings in dll projects?  Isn't that just causing confusion?"  

  5. alegr1 says:

    @kantos:

    Haven't you got a memo that you're supposed to use INT_PTR/DWORD_PTR/ULONG_PTR from like ten years ago?

  6. kantos says:

    @alegr1 or their C++ standard equivalents intptr_t, uintptr_t etc? Yes, however this code was ancient and originally written for Mac.

  7. Dave Bacher says:

    @kantos:

    I'd be fairly surprised if the code for LoadModule didn't look something like this:

    =====

    HMODULE LoadModuleKernel(…)

    {

       PE_HEADER PE = LoadHeader(…);

       BOOL bLOAD_BELOW_3GB = FALSE;

       if (!PE.LARGE_ADDRESS_AWARE)

           bLOAD_BELOW_3GB = TRUE;

       if (ProcIs32Bit())

           bLOAD_BELOW_3GB = TRUE;

       if (bLOAD_BELOW_3GB)

          return DoLameModuleLoad(…);

       else

          return DoSaneModuleLoad(…);

    }

    =====

    That is — I'd expect that it called the kernel, and asked for the module to load.  In times of yore (Windows 3.1, 2.0, 1.04), LoadModule didn't care if it was a DLL, an EXE — it just loaded the module.

    And so while this would not provide all the functionality for LARGE_ADDRESS_AWARE, it would have the observable side effects from the module loader.  And therefore if Microsoft changes the module loader in a way that removes those observable side effects — say due to ASLR — then you'd break.

  8. kantos says:

    @Dave Bacher I suspect that's exactly what goes on at least in win7, like I said the issue occured on win8 regardless. Given the changed semantics of /LARGEADRESSAWARE in 64 ( msdn.microsoft.com/…/wz223b1z.aspx and more importantly: msdn.microsoft.com/…/aa384271(v=vs.85).aspx ) it wouldn't surprise me.

  9. Brian_EE says:

    "[And what happens when a building is built further north than any previous building? Use negative numbers? "I'll meet you at building B-3." "Hey, I'm at B3 and I can't find you." "No, not B3. B-3. That's letter B, number negative 3." And good luck finding letters less than A. -Raymond]"

    I think that's why some maps have insets. Also why Alaska and Hawaii are not shown in tru locations on a lot of US maps. As long as there is a reference point on the inset (like a street intersection) it doesn't "fall apart" as you suggested in the linked to blog post.

    [So the building name is "Inset 1, B3"? -Raymond]
  10. Joshua says:

    Too bad SizeOfStackReserve is ignored. Oh well.

    Unfortunately the stack size the application needs to reserve for kernel32 calls isn't documented. Sometimes memory really is tight and reducing the size of spawned threads is a good idea.

  11. Mark (The Other Mark) says:

    [So the building name is "Inset 1, B3"? -Raymond]

    Could be even more flexible- Often, Insets overlap. So it might be B3 on Inset 1, but B17 on Inset 4.

  12. Ken Hagan says:

    "@alegr1 or their C++ standard equivalents intptr_t, uintptr_t etc? Yes, however this code was ancient and originally written for Mac."

    If it was really ancient, it might have been using ptrdiff_t and size_t, since C solved this particular problem several decades ago. (And yes, I'm aware that Win64 made a bit of a mess of the traditional C integer sizes.)

  13. JM says:

    @Ken: you're giving C too much credit. Neither size_t nor ptrdiff_t are necessarily adequate types for storing converted pointers, though they will be on a lot of implementations. The language doesn't care much how you do this conversion, since it's implementation-defined; if you want to do this, you're supposed to know how anyway. intptr_t and uintptr_t weren't added to C until C99, and even then they're optional.

    And even so, you can't expect the legions of people who "know" an int is exactly 32 bits to appreciate something as delicate as ptrdiff_t. Of course, the moment you start thinking "typedef long LONG;" is a good idea, you've already lost…

  14. Gabe says:

    [So the building name is "Inset 1, B3"? -Raymond]

    Don't be ridiculous! In order to be useful the insets can't overlap any actual building, so they can use the same grid. That way building B3 can be north of B1 even though numbers increase going southward.

    Of course eventually you'll run out of room for non-overlapping insets and will have to redraw the map with more grid squares. At that point the buildings will all get different coordinates, so you will need to have the building names qualified with the map version in order to eliminate ambiguities.

    In other words, the building once known as 1995A-B3 might now be known as 2014C-AX23, but once map revision 2014C is ubiquitous you'll be able to just call it AX23.

    See how easy it all could be? Now isn't that a small price to pay for not having to deal with seemingly random building names?

  15. M Hotchin says:

    It makes no sense for the loader to special case these flags, but what about the linker?  It already 'knows' if it's producing a DLL or an EXE, could it not issue a warning when nonsense flags are applied?

  16. JM says:

    Sorry, in the above, I obviously meant "that it's at least as big as a 'short'". Of course it's at least as big as a "char" as well, since "short" is, but if I'm not mentioning all integer types I should only mention the immediate neighbors.

  17. Joshua says:

    @JM: Prior to Windows x64, all the FAQs for C said for 64 bit integer on 64 bit machine use int. The documentation in the K&R book for ANSI C called for int to be the natural or most performant integer supported by the platform.

    > "The design error in C here went very long uncorrected."

    The design error of not having intptr_t.

  18. JM says:

    @Joshua: if memory is as tight as that, better not spawn threads to begin with. In fact, if *memory* is the restricting factor for a thread-heavy application on Windows, you're almost certainly doing it wrong.

    Source: painful memories of wrestling with code written to assume threads are free (some ported from other platforms, where they're at least cheaper).

  19. Myria says:

    By the way, /LARGEADDRESSAWARE actually *does* make a difference on 64-bit *EXEs*, it's just that the linker defaults it to on for obvious reasons.  If you link a 64-bit EXE with /LARGEADDRESSAWARE:NO, it will be limited to 2 GB =)  I suppose that you could use this along with GCC to make an "x32" environment like Linux has, but you'd get no help from the C runtime or Win32 in this regard – you'd still have to call Win32 with 64-bit pointers.

    @Joshua: The trick I use to have a large stack for the main thread but a small default stack size for spawned threads is to set the small stack size in the EXE header, then swap stacks in wWinMain.  To swap stacks to a larger one, ConvertThreadToFiber, CreateFiberEx with new stack size and wWinMain replacement, SwitchToFiber, DeleteFiber original fiber, ConvertFiberToThread to exit fiber mode.

    @Raymond: Is it *safe* to set /LARGEADDRESSAWARE and /TSAWARE on DLLs, or does doing so risk future incompatibility?  Just wondering, with the way you worded them about being reserved bits.

  20. Joshua says:

    > typedef long LONG

    The idea is to edit the typedef for different compilers or architectures. It's merely archaic not wrong. This way of thinking finally broke down when 64 bit came along and threw a monkey-wrench into everything when int needed to be 64 bit on some platforms thus making 32 bit integer a non-accessible type.

    (Windows use of int as a 32 bit integer is an error according to the C standard for all platforms other than x64 which has the correct instructions in its opcode table so that 32 bit memory reads to 64 bit registers perform as well as 64 bit memory reads to 64 bit registers).

    ptrdiff_t = intptr_t is true of all flat architectures and most others. The only exceptions I know are DOS compact and DOS large where a single object can't be >64kb in size. (Note that casting a code pointer to void * is not guaranteed to cast back except in flat architectures — use void (*)() instead). The design error in C here went very long uncorrected.

  21. JM says:

    @Joshua: "The idea is to edit the typedef for different compilers or architectures. It's merely archaic not wrong."

    C's integer sizes allow code to be flexible in sizing to whatever the architecture can support. Rather than writing for an exact number of bits, you can write for "natural" sizes, and the limits of your program are determined by your platform. Unfortunately, programmers basically said "screw that, I want a high-level assembly language and I need an integer type that is precisely 32 bits", when C offers no such type (or offered, I should say, since from C99 onwards it does offer such types if the platform has them). Arguably, writing for a type system that gives exact type sizes is easier, and the loss in flexibility is no great loss, given that portability (actual portability, not the one achieved by hiding everything behind #defines) is not on most people's priority lists anyway. The trouble only starts when people find out that, ironically, their exact-sized types weren't differentiated enough because (oh dear) it turns out pointers need to be not exactly 32 bits after all.

    As such, "typedef long INT32" would be barely acceptable, with the provision that you would replace "long" with whatever type on your platform is 32 bits wide. "typedef long LONG" helps nobody — if you want a C long, use "long", if you want an integer type that can hold values of at least 32 bits, use "long" (since that's what a C long *is*), if you want an integer type that is precisely 32 bits, use int32_t on C99 and whatever your platform has before that — but then don't call the typedef something confusing like LONG.

    "Windows use of int as a 32 bit integer is an error according to the C standard for all platforms other than x64 which has the correct instructions in its opcode table so that 32 bit memory reads to 64 bit registers perform as well as 64 bit memory reads to 64 bit registers"

    What you just said is meaningless in the context of the C standard, which says nothing about instructions, opcodes or performance. The standard says the type "int" is guaranteed to hold values in the range -32767 through 32767, that it's at least as big as a "char", that it's at most as big as a "long", and that's about it.

    "The design error in C here went very long uncorrected."

    Where was the design error in *C*? DOS was an unpleasant platform, but C supported it just fine.

  22. smf says:

    "The documentation in the K&R book for ANSI C called for int to be the natural or most performant integer supported by the platform."

    Which is absolutely ridiculous. If you're writing a for loop then you need to know how large a number the variable can contain. If you do:

    for (int i = 0; i < 2000; i++)

    and then compile that for a 6502 where the most performant integer is 8 bits then you have a problem.

    K&R are like your grandpa, they might have done great things during their lives but they don't have anywhere near enough exposure at the coal face these days to make any kind of recommendations.

    C++ would ideally have a way of saying "I need this variable to be exactly 32 bits" and also "I need this variable to hold this number of bits or more".

    Something like int8, int8+, int16, int16+, int32, int32+, int64, int64+ etc

    Potentially you could have "I need a variable that is as fast as possible, but no more then n bits". But I don't know how you'd write code that used that.

  23. Anonymous Coward says:

    @If there were separate file formats for EXEs and DLLs, then there would have to be two different module loaders, one for EXEs and one for DLLs.

    Or the file format could be designed to be solely concerned with mapping the image into memory. Everything that has nothing to do with that can go somewhere else.

  24. Cesar says:

    @Ken Hagan: if it was ancient, it might be using "a long is at least as wide as a pointer" (that is, a pointer fits within a long), which was true for any sane system before Win64 decided that a long was 32-bit even with 64-bit pointers. Before 1999, we didn't have C99 with its helpful stdint.h (AFAIK, the Microsoft C compilers still don't have stdint.h, but there are generic stdint.h headers for Windows which can be used), so if the code was older than that, it wouldn't be surprising for it to use a "long" to store a pointer, instead of uintptr_t.

  25. AC says:

    @smf: you might want to look at <cstdint>

  26. Steve says:

    "If it really bothers you, you can consider this flags as Reserved for future use when applied to DLLs."

    Fair point. I find myself no longer worried.

  27. AndyCadley says:

    @Joshua: In that case, it was the FAQs that were wrong, no how Windows handles things on x64. FWIW though, I believe both K&R have subsequently said that the whole business over "natural" sizes in the C spec turned out to be bad design and one of those things they'd have done differently with the benefit of hindsight.

  28. 640k says:

    @Myria

    No need to worry.

    As usual, MS wount re-use any flags on future windows versions becuase it risks breaking badly written apps.

    On 128-bit windows there will be a flag like /LARGEADDRESSAWARE128, instead of using any already well known flags.

    Now when I think about it, didn't it exist a image flag which 16-bit binaries could set which indicated they was aware of flat 32-bit addresses?

  29. Edward M. Grant says:

    "The design error of not having intptr_t."

    If there was a design error, it was allowing programmers to cast pointers to integers and vice-versa. We learned not to do that back in the early 90s, because it inevitably caused problems with any kind of portable code, and there was never a good reason to do it in user code. Yeah, maybe there was some optimization that saved a few clock cycles, but it cost days of developer time to debug when it broke on a new OS or architecture.

    Is there really any good reason to do such things in the 21st century, other than calling some weird system call interface that passes arbitrary data as an integer?

    Back on topic, I've also noticed that Windows 7 x64 seems to obey the flag when set on a DLL. I've tried flagging some games to use more than 2GB as they were crashing with big mods installed, and they didn't seem to do so unless I set the flag on every DLL they load. Not needing to do that would be good if true, but it doesn't seem to be the case?

  30. cheong00 says:

    @640k : Anyway because of physical limitation of CPU (also known as "5nm will be the end of Moore's law"), I doubt we'll see it be put into mass production before 2020 or even 2030.

  31. blah says:

    @smf:

    > C++ would ideally have a way of saying "I need this variable to be exactly 32 bits" and also "I need this variable to hold this number of bits or more".

    You mean like int_least32_t/int_fast32_t?

    en.cppreference.com/…/integer

  32. Joshua says:

    @Edward M. Grant: intptr_t was always the right type for the baton. Lots of places used void * instead.

    svn.haxx.se/…/0396.shtml

  33. smf says:

    @blah  

    Already pointed out on this thread, I obviously haven't kept up with all the new stuff added for c++11 (the c++ projects I work on are old and need to compile on old unsupported platforms too). Quite why they weren't in the original C standard has always been beyond me, register widths in 1978 were less standardised than they are now.

    I ended up having to write C for a Z80 project that for speed and size reasons, small loops would be written as

    unsigned char i;

    for (i = 0; i < 10; i++)

    using an int or even a char produced slower and larger code. The same code also ran on arm and intel processors but size and speed overheads were less of an issue there, so we just lived with it.

  34. Obnoxious Human says:

    In regards to the building numbers:

    I already knew that Microsoft took backwards compatibility to a ludicrous extreme. I had no idea that this also extended to the numbering of buildings! There was a major expansion; great time to update the building numbers! (yes, actually change a buildings designator! Shocking!) A couple of emails and maybe a week of "what's building 7 called now?", and everything is right with the world. Sheesh. You could apply this to Windows as well, and maybe it would have a little less entropy.

Comments are closed.