7

我们正在使用基于 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 设置。

4

6 回答 6

12

将 linux + 旧的 gcc 安装到虚拟机上。

于 2009-04-23T01:34:24.740 回答
5

你有这个自定义编译器的源代码吗?如果您可以恢复 2.6.0 基线(这应该相对容易),那么 diff 和 patch 应该可以恢复您的更改集。

然后我建议使用该更改集针对最新的 gcc 构建新版本。然后将其置于配置控制之下。

对不起,不想大喊大叫。只是我在 30 年的大部分时间里一直在说同样的话。

于 2009-04-23T01:36:25.297 回答
2

strace可执行文件可以gcc-2.6.0吗?它可能正在做一些类似阅读的事情/proc/$$/maps,并且当输出以微不足道的方式发生变化时会感到困惑。最近在 2.6.28 和 2.6.29 之间发现了一个类似的问题。

如果是这样,您可以破解/usr/src/linux/fs/proc/task_mmu.c或在附近恢复旧的输出,或设置一些$LD_PRELOADgcc以读取另一个文件。

编辑

既然你提到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.confnorandmaps

于 2009-04-23T15:57:05.073 回答
1

我遇到了这个并考虑了你的问题。也许您可以找到一种方法来使用二进制文件将其转换为 ELF 格式?或者可能是无关紧要的,但使用 objdump 可以为您提供更多信息。

你能看看进程内存映射吗?

于 2009-04-24T15:03:17.410 回答
1

所以我已经解决了一些问题......这不是一个完整的解决方案,但它确实解决了我在使用旧版 gcc 时遇到的原始问题。

在.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

那么遗留的 gcc 工作!!!!

但不是免费的,它在失败之前就连接到了构建中的链接,所以我们拥有的传统 nld 有一些进一步的进展:-( 它正在报告:

    “新”中超出虚拟内存

在 /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++.so.27 (libg++27-altdev ??) 的 libc5 版本,那么 libc5-compat 呢?

所以,正如我所说,还没有回家……越来越近了。我可能会发布一个关于 nld 问题的新问题。

编辑

我原本打算避免“接受”这个答案,因为我仍然对相应的遗留链接器有问题,但为了至少在这个问题上获得一些最终结果,我正在重新考虑这个立场。

谢谢你出去:

  • an0nym0usc0ward 建议使用虚拟机(最终可能成为接受的答案)
  • 建议使用 strace 并帮助使用 stackoverflow
  • shodanex 建议使用 objdump

编辑

下面是我最后学到的东西,现在我将接受 VM 解决方案,因为我无法以任何其他方式完全解决它(至少在为此分配的时间内)。

较新的内核有一个 CONFIG_COMPAT_BRK 构建标志来允许使用 libc5,所以大概用这个标志构建一个新内核会解决这个问题(并且通过内核 src,它看起来会,但我不能确定,因为我做了不要遵循所有的路径)。还有另一种记录方式允许在运行时(而不是在内核构建时)使用 libc5:sudo sysctl -w kernel.randomize_va_space=0。然而,这并不能完成一项完整的工作,一些(大多数?)libc5 应用程序仍然会中断,例如我们的旧版编译器和链接器。这似乎是由于新旧内核之间的对齐假设存在差异。我已经修补了链接器二进制文件,使其认为它具有更大的 bss 部分,以便将 bss 的末尾带到页面边界,并且当 sysctl var 内核时,这适用于较新的内核。randomize_va_space=0。这对我来说不是一个令人满意的解决方案,因为我盲目地修补一个关键的二进制可执行文件,即使在较新的内核上运行修补的链接器会产生与在旧内核上运行的原始链接器相同的输出,但这并不能证明其他一些链接器输入(即我们更改被链接的程序)也会产生相同的结果。

于 2009-04-27T17:03:24.023 回答
0

难道你不能简单地制作一个可以在系统死机时重新安装的光盘映像吗?或者做一个虚拟机?

于 2009-04-23T01:36:49.413 回答