我们正在使用基于 gcc 2.6.0 的旧版编译器来交叉编译我们仍在使用的旧嵌入式处理器(是的,它自 1994 年以来仍在使用!)。为这个芯片做 gcc 端口的工程师早就离开了。虽然我们可能能够从网络上的某个地方恢复 gcc 2.6.0 源,但该芯片的更改集已经在公司历史的大厅中消失了。直到最近,我们一直在糊涂,因为编译器仍在运行并生成可运行的可执行文件,但从 linux 内核 2.6.25(以及 2.6.26)开始,它会失败并显示消息gcc: virtual memory exhausted
... 即使在没有参数或仅使用-v
. 我已经使用 2.6.24 内核重新启动了我的开发系统(从 2.6.26 开始)并且编译器再次工作(使用 2.6.25 重新启动不会)。
我们有一个系统保持在 2.6.24,只是为了构建这个芯片,但是感觉有点暴露,以防 linux 世界发展到我们不能再重建一个可以运行的系统编译器(即我们的 2.6.24 系统死掉了,我们无法在新系统上安装和运行 2.6.24,因为某些软件部分不再可用)。
有没有人知道我们可以对更现代的安装做些什么来让这个遗留编译器运行?
编辑:
回答一些评论...
可悲的是,丢失了特定于我们芯片的源代码更改。这种损失发生在两个主要的公司重组和几个系统管理员(其中几个确实留下了一个烂摊子)。我们现在使用配置控制,但这对于这个问题来说太迟了。
使用虚拟机是一个好主意,并且可能是我们最终要做的事情。谢谢你的想法。
最后,我按照ehemient的建议尝试了strace,发现最后一个系统调用是brk(),在新系统(2.6.26内核)上返回错误,在旧系统(2.6.24内核)上返回成功。这表明我的虚拟内存真的用完了,除了 tcsh "limit" 在旧系统和新系统上返回相同的值,并且 /proc/meminfo 显示新系统有更多的内存和更多的交换空间。也许是碎片问题或程序加载的位置?
我做了一些进一步的研究,并在内核 2.6.25 中添加了“brk 随机化”,但CONFIG_COMPAT_BRK
据说默认启用(禁用 brk 随机化)。
编辑:
好的,更多信息:看起来 brk 随机化确实是罪魁祸首,旧版 gcc 正在调用 brk() 来更改数据段的结尾,现在失败了,导致旧版 gcc 报告“虚拟内存耗尽”。有一些记录在案的方法可以禁用 brk 随机化:
sudo echo 0 > /proc/sys/kernel/randomize_va_space
sudo sysctl -w kernel.randomize_va_space=0
setarch i386 -R tcsh
用(或“-R -L”)开始一个新的shell
我已经尝试过它们,它们似乎确实有效果,因为 brk() 返回值与没有它们时不同(并且始终相同)(在内核 2.6.25 和 2.6.26 上都尝试过),但是 brk()仍然失败,所以旧版 gcc 仍然失败:-(。
此外,我已经设置vm.legacy_va_layout=1
并且vm.overcommit_memory=2
没有任何更改,并且我已经使用/etc/sysctl.conf 中保存的设置vm.legacy_va_layout=1
重新启动。kernel.randomize_va_space=0
还是没有变化。
编辑:
在内核 2.6.26(和 2.6.25)上使用kernel.randomize_va_space=0
会导致以下 brk() 调用被报告strace legacy-gcc
:
brk(0x80556d4) = 0x8056000
这表明 brk() 失败,但看起来它失败了,因为数据段已经超出了请求的范围。使用 objdump,我可以看到数据段应该在 0x805518c 结束,而失败的 brk() 表明数据段当前在 0x8056000 结束:
部分: Idx 名称大小 VMA LMA 文件关闭 Algn 0 .interp 00000013 080480d4 080480d4 000000d4 2**0 内容、分配、加载、只读、数据 1 .hash 000001a0 080480e8 080480e8 000000e8 2**2 内容、分配、加载、只读、数据 2 .dynsym 00000410 08048288 08048288 00000288 2**2 内容、分配、加载、只读、数据 3 .dynstr 0000020e 08048698 08048698 00000698 2**0 内容、分配、加载、只读、数据 4.rel.bss 00000038 080488a8 080488a8 000008a8 2**2 内容、分配、加载、只读、数据 5.rel.plt 00000158 080488e0 080488e0 000008e0 2**2 内容、分配、加载、只读、数据 6 .init 00000008 08048a40 08048a40 00000a40 2**4 内容、分配、加载、只读、代码 7.plt 000002c0 08048a48 08048a48 00000a48 2**2 内容、分配、加载、只读、代码 8 .文本 000086cc 08048d10 08048d10 00000d10 2**4 内容、分配、加载、只读、代码 9.fini 00000008 080513e0 080513e0 000093e0 2**4 内容、分配、加载、只读、代码 10 .rodata 000027d0 080513e8 080513e8 000093e8 2**0 内容、分配、加载、只读、数据 11 .数据 000005d4 08054bb8 08054bb8 0000bbb8 2**2 内容、分配、加载、数据 12 .ctors 00000008 0805518c 0805518c 0000c18c 2**2 内容、分配、加载、数据 13 .dtors 00000008 08055194 08055194 0000c194 2**2 内容、分配、加载、数据 14.得到000000b8 0805519c 0805519c 0000c19c 2**2 内容、分配、加载、数据 15.动态00000088 08055254 08055254 0000c254 2**2 内容、分配、加载、数据 16 .bss 000003b8 080552dc 080552dc 0000c2dc 2**3 分配 17 .注 00000064 00000000 00000000 0000c2dc 2**0 内容,只读 18 .comment 00000062 00000000 00000000 0000c340 2**0 内容,只读 符号表: 没有符号
编辑:
在下面回应 ehemient 的评论:“将 GCC 视为没有源代码的二进制文件真是太奇怪了”!
因此,使用 strace、objdump、gdb 以及我对 386 汇编器和体系结构的有限理解,我将问题追溯到遗留代码中的第一个 malloc 调用。旧版 gcc 调用 malloc,它返回 NULL,这会导致 stderr 上出现“虚拟内存耗尽”消息。这个 malloc 在 libc.so.5 中,它多次调用 getenv 并最终调用 brk()...我想增加堆...失败了。
由此我只能推测问题不仅仅是 brk 随机化,或者我没有完全禁用 brk 随机化,尽管 randomize_va_space=0 和 legacy_va_layout=1 sysctl 设置。