对 32 位 Windows 可执行文件使用 /LARGEADDRESSAWARE 的缺点?
我们需要将我们的可执行文件之一与此标志链接,因为它使用大量内存。
但为什么要对一个 EXE 文件进行特殊处理呢?为什么不在 /LARGEADDRESSAWARE
上进行标准化?
所以问题是:即使你不需要它,使用 /LARGEADDRESSAWARE
有什么问题吗?为什么不使用它作为所有 EXE 文件的标准?
We need to link one of our executables with this flag as it uses lots of memory.
But why give one EXE file special treatment. Why not standardize on /LARGEADDRESSAWARE
?
So the question is: Is there anything wrong with using /LARGEADDRESSAWARE
even if you don't need it. Why not use it as standard for all EXE files?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
盲目地将
LargeAddressAware
标志应用到 32 位可执行文件会部署一个定时炸弹!通过设置此标志您正在向操作系统作证:
但你真的能保证吗?
您是否对您的进程可能使用的所有系统 DLL、Microsoft 可再发行组件和第 3 方模块负责?
通常,内存分配按从低到高的顺序返回虚拟地址。因此,除非您的进程消耗大量内存(或者它的虚拟地址空间非常分散),否则它永远不会使用超出 2 GB 边界的地址。这隐藏了与高地址相关的错误。
如果存在此类错误,则很难识别。他们“迟早”会偶尔出现。这只是时间问题。
幸运的是,Windows 操作系统内置了一个非常方便的系统范围开关:
出于测试目的,请使用 MEM_TOP_DOWN 注册表设置。
这迫使所有内存分配从上到下,而不是正常的从下到上。
(这是十六进制 0x100000。当然,需要重新启动 Windows)
启用此开关后,您将“更快”而不是“稍后”发现问题。
理想情况下,您会“从一开始”就看到它们。
旁注:对于第一次分析,我强烈推荐工具
VMmap
(SysInternals)。结论:
必须在设置了 TopDown
AllocationPreference
开关的 x64 操作系统上对其进行全面测试。将 LAA 标志应用于 32 位可执行文件时, 自己的代码您也许能够修复它们。
仅举一个非常明显的例子:使用无符号整数而不是有符号整数作为内存指针。
当遇到第三方模块的问题时,您需要要求作者修复他的错误。除非这样做,否则最好从可执行文件中删除 LargeAddressAware 标志。
关于测试的说明:
MemTopDown 注册表开关未达到由“测试运行程序”执行的单元测试的预期结果,而“测试运行程序”本身不是 LAA 已启用。
请参阅:x86 LargeAddressAware 兼容性的单元测试
PS:
同样非常“相关”并且非常有趣的是从 32 位代码到 64 位的迁移。
有关示例,请参阅:
blindly applying the
LargeAddressAware
flag to your 32bit executable deploys a ticking time bomb!by setting this flag you are testifying to the OS:
but can you really guarantee?
do you take responsibility for all the system DLLs, microsoft redistributables and 3rd-party modules your process may use?
usually, memory allocation returns virtual addresses in low-to-high order. so, unless your process consumes a lot of memory (or it has a very fragmented virtual address space), it will never use addresses beyond the 2 GB boundary. this is hiding bugs related to high addresses.
if such bugs exist they are hard to identify. they will sporadically show up "sooner or later". it's just a matter of time.
luckily there is an extremely handy system-wide switch built into the windows OS:
for testing purposes use the MEM_TOP_DOWN registry setting.
this forces all memory allocations to go from the top down, instead of the normal bottom up.
(this is hex 0x100000. requires windows reboot, of course)
with this switch enabled you will identify issues "sooner" rather than "later".
ideally you'll see them "right from the beginning".
side note: for first analysis i strongly recommend the tool
VMmap
(SysInternals).conclusions:
when applying the LAA flag to your 32bit executable it is mandatory to fully test it on a x64 OS with the TopDown
AllocationPreference
switch set.for issues in your own code you may be able to fix them.
just to name one very obvious example: use unsigned integers instead of signed integers for memory pointers.
when encountering issues with 3rd-party modules you need to ask the author to fix his bugs. unless this is done you better remove the LargeAddressAware flag from your executable.
a note on testing:
the MemTopDown registry switch is not achieving the desired results for unit tests that are executed by a "test runner" that itself is not LAA enabled.
see: Unit Testing for x86 LargeAddressAware compatibility
PS:
also very "related" and quite interesting is the migration from 32bit code to 64bit.
for examples see:
因为许多遗留代码都是在“负”指针无效的情况下编写的。 32 位进程的前 2 GB 中的任何内容都具有 msb 集。
因此,微软更容易采取安全措施,并要求 (a) 需要完整 4Gb 和 (b) 已在大内存场景中开发和测试的应用程序,以简单地设置标志。
正如您所注意到的,这并不难。
Raymond Chen - 在他的博客 旧的新事物 - 涵盖了为所有(32 位)应用程序打开它的问题。
Because lots of legacy code is written with the expectation that "negative" pointers are invalid. Anything in the top two Gb of a 32bit process has the msb set.
As such, its far easier for Microsoft to play it safe, and require applications that (a) need the full 4Gb and (b) have been developed and tested in a large memory scenario, to simply set the flag.
It's not - as you have noticed - that hard.
Raymond Chen - in his blog The Old New Thing - covers the issues with turning it on for all (32bit) applications.
不,在此上下文(C/C++)中的“遗留代码”不仅仅是对指针的 MSB 进行丑陋把戏的代码。
它还包括所有使用“int”来存储两个指针之间的差异或内存区域的长度的代码,而不是使用正确的类型“size_t”:有符号的“int”有31位,并且无法处理值超过 2 GB。
解决大部分代码的方法是检查它并纠正所有那些无害的“混合签名和未签名”警告。它应该可以很好地完成工作,至少如果您还没有定义其中 int 类型的参数实际上是内存长度的函数。
尽管如此,即使您没有进行任何更正,“遗留代码”显然仍将在很长一段时间内正常工作。
这是因为只有当您在一个块中分配超过 2 GB 的空间时,它才会中断。或者当您要比较两个相距超过 2 Gb 的不相关指针时。
由于比较不相关的指针在技术上无论如何都是未定义的行为,因此您不会遇到那么多执行此操作的代码(但您永远无法确定)。
对于第一种情况,非常频繁的是,即使您总共需要超过 2Gb,您的程序实际上也不会进行大于该值的单个分配。事实上,在 Windows 中,即使使用 LARGEADDRESSAWARE,默认情况下您也无法分配那么多内存,因为内存的组织方式是这样的。您需要对系统 DLL 进行洗牌才能获得超过 2Gb 的连续块
,但是墨菲定律表明这种代码有一天会被破坏,只是它会在您很久之后发生。我在没有检查的情况下启用了 LARGEADDRESSAWARE,当没有人记得这已经完成时。
No, "legacy code" in this context (C/C++) is not exclusively code that plays ugly tricks with the MSB of pointers.
It also includes all the code that uses 'int' to store the difference between two pointer, or the length of a memory area, instead of using the correct type 'size_t' : 'int' being signed has 31 bits, and can not handle a value of more than 2 Gb.
A way to cure a good part of your code is to go over it and correct all of those innocuous "mixing signed and unsigned" warnings. It should do a good part of the job, at least if you haven't defined function where an argument of type int is actually a memory length.
Still that "legacy code" will apparently work correctly for a very long while, even if you correct nothing.
That's because it will break only when you'll allocate more than 2 Gb in one block. Or when you'll compare two unrelated pointers that are more than 2 Gb away from each other.
As comparing unrelated pointers is technically an undefined behaviour anyway, you won't encounter that much code that does it (but you can never be sure).
For the first case, very frequently even if in total you need more than 2Gb, your program actually never makes single allocations that are larger than that. In fact in Windows, even with LARGEADDRESSAWARE you won't be able by default to allocate that much given the way the memory is organized. You'd need to shuffle the system DLL around to get a continuous block of more than 2Gb
But Murphy's laws says that kind of code will break one day, it's just that it will happen very long after you've enabled LARGEADDRESSAWARE without checking, and when nobody will remember this has been done.