在“int main() { return(0); }”中出现浮点异常(SIGFPE)

73

我正在尝试为两个不同的Linux环境构建一个简单的C程序。在一台设备上,程序可以正常运行;而在另一台设备上,程序会生成浮点异常。该程序仅从主函数返回0,这使我相信可能存在一些与启动代码(可能是ABI)不兼容的问题。

使用gcc编译程序时,采用了以下构建规格:

使用内置规格。目标:i386-redhat-linux。配置为: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-libgcj-multifile --enable-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --disable-plugin --with-java-home=/usr/lib/jvm/java-1.4.2-gcj-1.4.2.0/jre --with-cpu=generic --host=i386-redhat-linux 线程模型:posix,gcc版本4.1.2 20080704 (Red Hat 4.1.2-52)

该程序的源代码如下:

int main()
{
        return(0);
}

在 Celeron 设备上,在 GDB 下执行此程序会生成以下输出:

[root@n00200C30AA2F jrn]# /jrn/gdb fail GNU gdb Red Hat Linux (5.3post-0.20021129.18rh) (gdb) run Starting program: /jrn/fail 

Program received signal SIGFPE, Arithmetic exception. 0x40001cce in ?? () (gdb) bt
#0  0x40001cce in ?? ()
#1  0x4000c6b0 in ?? ()
#2  0x40000cb5 in ?? ()

以下是我能想到的细节,可以帮助找出发生了什么:

CELERON:  ( fails on this device )
2.6.8 #21 Mon Oct 1 11:41:47 PDT 2007 i686 i686 i386 GNU/Linux
============
[root@n00200C30AA2F proc]# cat cpuinfo 
processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 9
model name      : Intel(R) Celeron(R) M processor          600MHz
stepping        : 5
cpu MHz         : 599.925
cache size      : 512 KB
fdiv_bug        : no
hlt_bug         : no
f00f_bug        : no
coma_bug        : no
fpu             : yes
fpu_exception   : yes
cpuid level     : 2
wp              : yes
flags           : fpu vme de pse tsc msr mce cx8 sep mtrr pge mca cmov pat clflush dts acpi mmx fxsr sse sse2 tm pbe
bogomips        : 1179.64

GNU C Library stable release version 2.3.2, by Roland McGrath et al.
Compiled by GNU CC version 3.2.2 20030222 (Red Hat Linux 3.2.2-5).
Compiled on a Linux 2.4.20 system on 2003-03-13.
Available extensions:
        GNU libio by Per Bothner
        crypt add-on version 2.1 by Michael Glad and others
        linuxthreads-0.10 by Xavier Leroy
        BIND-8.2.3-T5B
        libthread_db work sponsored by Alpha Processor Inc
        NIS(YP)/NIS+ NSS modules 0.19 by Thorsten Kukuk

ATOM:  ( works fine on this device )
2.6.35 #25 SMP Mon Mar 12 09:02:45 PDT 2012 i686 i686 i386 GNU/Linux
==========
[root@n00E04B36ECE5 ~]# cat /proc/cpuinfo 
processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 28
model name      : Genuine Intel(R) CPU N270   @ 1.60GHz
stepping        : 2
cpu MHz         : 1599.874
cache size      : 512 KB
fdiv_bug        : no
hlt_bug         : no
f00f_bug        : no
coma_bug        : no
fpu             : yes
fpu_exception   : yes
cpuid level     : 10
wp              : yes
flags           : fpu vme de tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe nx constant_tsc up arch_perfmon pebs bts aperfmperf pni dtes64 monitor ds_cpl est tm2 ssse3 xtpr pdcm movbe lahf_lm
bogomips        : 3199.74
clflush size    : 64
cache_alignment : 64
address sizes   : 32 bits physical, 32 bits virtual
power management:


GNU C Library stable release version 2.5, by Roland McGrath et al.
Compiled by GNU CC version 4.1.2 20080704 (Red Hat 4.1.2-44).
Compiled on a Linux 2.6.9 system on 2009-09-02.
Available extensions:
        The C stubs add-on version 2.1.2.
        crypt add-on version 2.1 by Michael Glad and others
        GNU Libidn by Simon Josefsson
        GNU libio by Per Bothner
        NIS(YP)/NIS+ NSS modules 0.19 by Thorsten Kukuk
        Native POSIX Threads Library by Ulrich Drepper et al
        BIND-8.2.3-T5B
        RT using linux kernel aio
Thread-local storage support included.

我该怎么确定是什么导致了这个问题?

不妨尝试使用特定版本的libc进行静态链接,看是否能够解决问题。

当出现错误时,在GDB下执行以下命令:

(gdb) x/1i $eip
0x40001cce:     divl   0x164(%ecx)
(gdb) info reg
eax            0x6c994f 7117135
ecx            0x40012858       1073817688
edx            0x0      0
ebx            0x40012680       1073817216
esp            0xbffff740       0xbffff740
ebp            0xbffff898       0xbffff898
esi            0x8049580        134518144
edi            0x400125cc       1073817036
eip            0x40001cce       0x40001cce
eflags         0x10246  66118
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x0      0
(gdb) x/1wx 0x164+$ecx
0x400129bc:     0x00000000
(gdb) 

根据我得到的帮助,似乎是由于libc启动代码除以0的原因。

现在的问题是,是什么导致了这种明显的错误行为?某些东西一定与其他某些东西不兼容?

汇编输出:

[jrn@localhost ~]$ more fail.s
        .file   "fail.c"
        .text
.globl main
        .type   main, @function
main:
        leal    4(%esp), %ecx
        andl    $-16, %esp
        pushl   -4(%ecx)
        pushl   %ebp
        movl    %esp, %ebp
        pushl   %ecx
        movl    $0, %eax
        popl    %ecx
        popl    %ebp
        leal    -4(%ecx), %esp
        ret
        .size   main, .-main
        .ident  "GCC: (GNU) 4.1.2 20080704 (Red Hat 4.1.2-52)"
        .section        .note.GNU-stack,"",@progbits

6
甚至gcc 4.1.2已经非常老旧了。当前的GCC版本是4.7! - Basile Starynkevitch
3
GCC已经进入了Firefox所引发的修改地狱。很快你就会看到我们将在几天内面对GCC 25.3! - user529758
4
当你在GDB下遇到FPE时,你能执行x/1i $eip吗? - ninjalj
1
你的libc启动代码中有些东西正在进行除以0的操作。为了找出是哪段代码以及原因,你需要弄清楚如何获取libc实现的调试符号。祝你好运。 - Adam Rosenfield
4
@Chimera:不,那是一个非常糟糕的想法。很可能,在那之后它会很快再次崩溃,即使你可以让它运行,它也可能开始在完全不相关的地方引起随机其他故障。 - Adam Rosenfield
显示剩余19条评论
2个回答

126

这可能听起来像一个非常遥远的可能性...但您可以尝试以下方法:

$ readelf -a fail

你需要查找GNU_HASH动态标签吗?我猜测该二进制文件使用了GNU_HASH,而你的ld.so版本过旧无法理解。对于GNU哈希部分的支持在2006年左右被添加到glibc中,主流发行版在2007或2008年开始只支持GNU-hash。你的Centrino的glibc是从2003年的,早于GNU哈希。

如果ld.so不理解GNU哈希,则会尝试使用旧的ELF哈希部分,该部分为空。特别地,我怀疑你的崩溃发生在elf/do-lookup.h此行代码处:

for (symidx = map->l_buckets[hash % map->l_nbuckets];

由于链接器可能不理解GNU哈希表,l_nbuckets 将为0,导致崩溃。请注意,map 是一个具有约100个结构元素的大型结构,而 l_nbuckets 在新版的 ld.so 中是该结构的第90个成员左右(0x164 = 4*89),因此在旧版的 ld.so 中它很可能就是这个成员。

要确定这是否是问题的根本原因,请使用 -Wl,--hash-style=sysv-Wl,--hash-style=both 进行构建,并查看崩溃是否消失。


谢谢,明天早上我到办公室后会尝试一下。 - Chimera
22
哇,简直太棒了。很好的分析。无论是否解决了原帖作者的问题,都要点赞。 - sehe
2
是的 - 这是一个非常好的尝试,应该是正确的(即使海报的名字似乎永远不会结束)。+1。 - Martin James
31
你,先生,具有出色的心灵调试技巧。 Raymond Chen 一定会感到自豪。 - Adam Rosenfield
12
你很棒!那就是问题所在。使用-Wl,--hash-style = both编译可以生成可在旧和新环境下运行的可执行文件。 - Chimera

4
由于它在ATOM上工作但在旧的Celeron上无法工作,我认为问题可能是编译器优化生成了Celeron无法执行的代码。尝试使用标志-O0进行编译。此外,建议添加-march = i686以明确说明架构。另外,为了帮助隔离问题,我还建议禁用与C ++运行时和JAVA的链接。
您是否只构建了一个测试程序并在每个设备上运行了它,还是为每个设备构建了不同的可执行文件?如果您正在构建一个可执行文件,则两个设备之间或设备与构建机器之间可能具有不同版本的libc、libstdc++。

1
编译标志没有任何影响,结果相同。是的,在一台机器上构建并在两个不同设备上运行可执行文件。Atom 设备环境正在运行 libc 2.5(与构建机器上的相同)。然而,可执行文件在失败的设备上具有 libc 2.3.2。因此,也许存在 libc 2.3.2 和 libc 2.5 的向后兼容性问题? - Chimera
glibcxx或libstdc++怎么样?此外,您是否静态链接这些库之一?我建议尝试针对libc 2.3.2的最低公共分母以及该设备所具有的C ++进行构建。 - syplex
2
不进行静态链接。[jrn@localhost ~]$ ldd fail linux-gate.so.1 => (0x0098f000) libc.so.6 => /lib/libc.so.6 (0x00bb0000) /lib/ld-linux.so.2 (0x00b91000) - Chimera
构建时使用较旧版本的libc(即2.3.2)。这很可能是导致您的问题的原因。 - syplex
是的,这也是我的怀疑,但我们正在尝试找到一种方法来升级我们的构建环境以使用更新的库等,但仍能创建可与具有不同版本libc的多个设备兼容的可执行文件。因此,我们可能会陷入困境,最终前进的方法是咬紧牙关,为旧的遗留设备升级环境。 - Chimera
我通常会使用旧的Linux发行版作为我的构建机器,这样可以更轻松地支持软件在许多Linux版本上使用单个二进制文件。您可以下载glibc 2.3.2源代码(http://ftp.gnu.org/gnu/libc/glibc-2.3.2.tar.gz),然后简单地更新您的Makefiles。这可能会有所帮助:http://tldp.org/HOWTO/Glibc2-HOWTO-6.html - syplex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接