遗留 gcc 编译器问题
-
13-09-2019 - |
题
我们正在使用基于 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 randomization”,但是 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”)
我已经尝试过它们,它们似乎确实有效果,因为 brk() 返回值与没有它们时不同(并且始终相同)(在内核 2.6.25 和 2.6.26 上都尝试过),但是 brk()仍然失败,因此旧版 gcc 仍然失败:-(。
另外我还设置了 vm.legacy_va_layout=1
和 vm.overcommit_memory=2
没有任何变化,我已经重新启动 vm.legacy_va_layout=1
和 kernel.randomize_va_space=0
设置保存在 /etc/sysctl.conf 中。还是没有变化。
编辑:
使用 kernel.randomize_va_space=0
在内核 2.6.26(和 2.6.25)上,会导致以下 brk() 调用被报告 strace legacy-gcc
:
brk(0x80556d4) = 0x8056000
这表明 brk() 失败,但看起来失败是因为数据段已经超出了请求的范围。使用 objdump,我可以看到数据段应以 0x805518c 结束,而失败的 brk() 表明数据段当前以 0x8056000 结束:
Sections: Idx Name Size VMA LMA File off Algn 0 .interp 00000013 080480d4 080480d4 000000d4 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 1 .hash 000001a0 080480e8 080480e8 000000e8 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 2 .dynsym 00000410 08048288 08048288 00000288 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 3 .dynstr 0000020e 08048698 08048698 00000698 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 4 .rel.bss 00000038 080488a8 080488a8 000008a8 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 5 .rel.plt 00000158 080488e0 080488e0 000008e0 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 6 .init 00000008 08048a40 08048a40 00000a40 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE 7 .plt 000002c0 08048a48 08048a48 00000a48 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 8 .text 000086cc 08048d10 08048d10 00000d10 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE 9 .fini 00000008 080513e0 080513e0 000093e0 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE 10 .rodata 000027d0 080513e8 080513e8 000093e8 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 11 .data 000005d4 08054bb8 08054bb8 0000bbb8 2**2 CONTENTS, ALLOC, LOAD, DATA 12 .ctors 00000008 0805518c 0805518c 0000c18c 2**2 CONTENTS, ALLOC, LOAD, DATA 13 .dtors 00000008 08055194 08055194 0000c194 2**2 CONTENTS, ALLOC, LOAD, DATA 14 .got 000000b8 0805519c 0805519c 0000c19c 2**2 CONTENTS, ALLOC, LOAD, DATA 15 .dynamic 00000088 08055254 08055254 0000c254 2**2 CONTENTS, ALLOC, LOAD, DATA 16 .bss 000003b8 080552dc 080552dc 0000c2dc 2**3 ALLOC 17 .note 00000064 00000000 00000000 0000c2dc 2**0 CONTENTS, READONLY 18 .comment 00000062 00000000 00000000 0000c340 2**0 CONTENTS, READONLY SYMBOL TABLE: no symbols
编辑:
回应下面的 ephemient 评论:“将 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 设置。
解决方案
将Linux +旧的GCC安装到虚拟机上。
其他提示
你有吗 来源 对于此自定义编译器?如果您可以恢复2.6.0基线(这应该相对容易),则diff和patch应该恢复您的更改集。
然后,我建议使用该更改集来构建针对最新GCC的新版本。然后将其放在配置控件下。
抱歉,不要大喊。 30年来,我一直在说同样的话。
你可以吗 strace
这 gcc-2.6.0
可执行的?它可能正在做一些事情,比如读书 /proc/$$/maps
, ,并且当输出以微不足道的方式变化时会感到困惑。类似的问题是 最近注意到 2.6.28 至 2.6.29 之间。
如果是这样,你可以破解 /usr/src/linux/fs/proc/task_mmu.c
或大约恢复旧的输出,或设置一些 $LD_PRELOAD
伪造 gcc
读取另一个文件。
编辑
既然你提到了 brk
...
CONFIG_COMPAT_BRK
设为默认值 kernel.randomize_va_space=1
代替 2
, ,但这仍然会随机化除堆之外的所有内容(brk
).
看看您的问题是否会消失 echo 0 > /proc/sys/kernel/randomize_va_space
或者 sysctl kernel.randomize_va_space=0
(相等的)。
如果是这样,请添加 kernel.randomize_va_space = 0
到 /etc/sysctl.conf
或添加 norandmaps
到内核命令行(等效),然后再次高兴。
我碰到 这个 并想到你的问题。也许您可以找到一种使用二进制文件将其移至精灵格式的方法吗?或可能是无关紧要的,但是与Objdump一起玩可以为您提供更多信息。
您可以查看过程内存图吗?
因此,我已经解决了一些问题……这不是一个完整的解决方案,但是它确实已经解决了我对传统海湾合作委员会的原始问题。
在.plt(过程链接表)中的每个libc调用中列出断点,我看到malloc(在libc.so.5中)调用getenv()获取:
MALLOC_TRIM_THRESHOLD_ MALLOC_TOP_PAD_ MALLOC_MMAP_THRESHOLD_ MALLOC_MMAP_MAX_ MALLOC_CHECK_
所以我网上搜索了这些并找到了 这个 建议
setenv MALLOC_TOP_PAD_ 536870912
然后,传统海湾合作委员会有效!!!!
但是,不自由家庭,它在失败之前就可以到达构建中的链接,因此我们拥有的旧版NLD进一步发生了一些事情:-(它正在报告:
Virtual memory exceeded in `new'
在/etc/sysctl.conf中,我有:
kernel.randomize_va_space=0 vm.legacy_va_layout=1
如果它仍然有效
kernel.randomize_va_space=1 vm.legacy_va_layout=0
但不要
kernel.randomize_va_space=2
有一个建议使用“ LDD”来查看共享库的依赖性:传统GCC只需要libc5,但是旧版NLD也需要libg ++。so.27,libstdc ++ so.27,libm.so.5,显然有那里有。 libg ++的libc5版本so.27(libg ++ 27-altdev ??)和libc5-compat呢?
因此,正如我所说的,还没有自由回家...越来越近。我可能会发布一个有关NLD问题的新问题。
编辑:
我最初会避免“接受”此答案,因为我仍然对相应的旧链接器有问题,但是至少为了在这个问题上获得一些最终性,我正在重新考虑该立场。
谢谢,你出去了:
- an0nym0usc0ward建议使用VM(最终可能成为公认的答案)
- 用于建议使用strace并帮助Stackoverflow使用的临时使用者
- Shodanex建议使用objdump
编辑
以下是我学到的最后一件事,现在我将接受VM解决方案,因为我无法完全解决任何其他方法(至少在为此分配的时间)。
较新的内核有一个config_compat_brk构建标志来允许使用libc5,因此大概可以用此标志构建新的内核可以解决该问题(并浏览核心SRC,看起来它会这样,但是我不能确定,因为我做到了,因为我做到了不遵循所有路径)。还有另一种记录的方法可以在运行时(而不是在内核构建时间)使用LIBC5:sudo sysctl -w kernel.randomize_va_space = 0。但是,这并不是完成完整的工作,有些(大多数?)LIBC5应用程序仍然会破裂,例如我们的传统编译器和链接器。这似乎是由于较新内核和旧内核之间的一致性假设的差异所致。我已经修补了链接器二进制文件,以使其认为它具有较大的BSS部分,以使BSS的末端到达页面边界,当Sysctl var kernel.randomize_va_space = 0时,这在较新的内核上起作用。这对我来说不是一个令人满意的解决方案,因为我盲目地修补了关键的二进制可执行文件,即使在较新的内核上运行修补的链接器,也对旧核上的原始链接器产生了一个相同的输出,但并不能证明这一点其他一些链接器输入(即我们更改正在链接的程序)也将产生相同的结果。
如果系统死亡,您是否可以简单地制作可以重新安装的光盘映像?还是制作VM?