【问题标题】:legacy gcc compiler issues遗留 gcc 编译器问题
【发布时间】:2010-10-21 06:23:44
【问题描述】:

我们正在使用基于 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,因为某些软件部分不再可用)。

是否有人对我们可以对更现代的安装做些什么以使这个旧版编译器运行有任何想法?

编辑

回答一些cmets...

遗憾的是,我们芯片特有的源代码更改丢失了。这种损失发生在两个主要的公司重组和几个系统管理员(其中几个确实留下了一个烂摊子)。我们现在使用配置控制,但这对于这个问题来说太迟了。

使用虚拟机是个好主意,而且可能是我们最终要做的事情。谢谢你的想法。

最后,我按照 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=1vm.overcommit_memory=2 没有任何更改,并且我已经使用 /etc/sysctl.conf 中保存的 vm.legacy_va_layout=1kernel.randomize_va_space=0 设置重新启动。还是没有变化。

编辑

在内核 2.6.26(和 2.6.25)上使用 kernel.randomize_va_space=0 会导致 strace legacy-gcc 报告以下 brk() 调用:

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 设置。

【问题讨论】:

  • 澄清变更集的消失——您是否丢失了编译器(部分)的源代码?
  • 我感受到了你的痛苦,但我真的更担心......好吧,几乎任何其他风险,而不是无法访问特定 2.6 内核版本的 Linux 源代码的风险未来。
  • 嗯,所以动态链接到libc.so.5,问题出在哪里?一个可怕的 hack,但 glibc 应该兼容足够,这样如果你执行ln -s libc.so.6 libc.so.5,你的代码可能仍然可以运行。
  • 一个丑陋的黑客,但正如我的一个朋友所说:“如果它有效,它就是美丽的。”不幸的是它不起作用(遗留的 gcc 报告“无法处理 reloc 类型 ”)......它仍然很丑陋:-) 这并不奇怪,因为 libc5->libc6 很重要。
  • 我看到最近对 Linux 内核的提交似乎与这个问题有关:git.kernel.org/linus/4471a675dfc7ca676c165079e91c712b09dc9ce4

标签: linux gcc legacy cross-compiling virtual-memory


【解决方案1】:

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

【讨论】:

  • +1。将其放入 VMWare Player(或其他,但我会选择 VMWare),然后您可以将其从一台机器上随意移动到另一台机器上。备份就像保存整个 VM 目录一样简单。
【解决方案2】:

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

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

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

【讨论】:

  • 不要阻止查理,告诉我们你真正的感受。
  • 如果他们 30 年后仍然不听你的话,也许是时候考虑放弃了 :-)
  • 它的阴险之处在于:每隔一段时间就有有人得到它。只是经常足以让你上钩,而不足以降低血压。
  • 丢失的资源比您想象的更常见。亚特兰大有一家公司只为丢失源代码的白痴反编译二进制文件。你不会想让那些好人失业吧?
  • @norman 我认为它们再普通不过了,文明会停滞不前。而且,是的,实际上。
【解决方案3】:

你能strace gcc-2.6.0 可执行文件吗?它可能正在执行类似阅读/proc/$$/maps 的操作,并且当输出以微不足道的方式发生变化时会感到困惑。类似的问题是recently noticed 在 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_spacesysctl kernel.randomize_va_space=0(等效),看看您的问题是否会消失。

如果是这样,请将kernel.randomize_va_space = 0 添加到/etc/sysctl.conf 或将norandmaps 添加到内核命令行(等效),然后再次开心。

【讨论】:

  • 将 GCC 视为没有源代码的二进制文件真是太奇怪了……如果可能的话,我肯定会走查理的路线。
  • 响应您的编辑,上面,我试过了......不高兴。我在这个问题中包含了更多注释。它实际上看起来像旧版 gcc 正在尝试更改数据段的结尾,但系统已经认为结束是 超出旧版 gcc 试图将其扩展到的位置。
【解决方案4】:

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

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

【讨论】:

  • 您提供的链接似乎更针对在嵌入式 linux 中运行程序,我在 PC 上运行旧版 gcc。但我确实尝试了 objdump,遗留的 gcc 是 elf32-i386。
【解决方案5】:

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

在 .plt(过程链接表)中的每个 libc 调用上放置断点我看到 malloc(在 libc.so.5 中)调用 getenv() 来获取:

MALLOC_TRIM_THRESHOLD_ MALLOC_TOP_PAD_ MALLOC_MMAP_THRESHOLD_ MALLOC_MMAP_MAX_ MALLOC_CHECK_

所以我在网上搜索了这些并找到了建议的this

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”查看共享库依赖项:legacy gcc 只需要 libc5,但 legacy nld 还需要 libg++.so.27、libstdc++.so.27、libm.so.5 和显然有 libc5 版本的 libg++.so.27 (libg++27-altdev ??) 那么 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 kernel.randomize_va_space=0 时,这适用于较新的内核。这对我来说不是一个令人满意的解决方案,因为我盲目地修补一个关键的二进制可执行文件,即使在较新的内核上运行修补的链接器会产生与在旧内核上运行的原始链接器相同的输出,但这并不能证明其他一些链接器输入(即我们更改被链接的程序)也会产生相同的结果。

【讨论】:

    【解决方案6】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-09
      • 1970-01-01
      • 2020-09-22
      • 2017-01-07
      • 1970-01-01
      相关资源
      最近更新 更多