The assembler instructions gcc generates for a 'goto' statement only respect
the low 16 bits of the destination address by default (or in when using PIC in
general). This causes the program to jump to the wrong location and soon
thereafter segfault. The -mno-explicit-relocs seems to work around this
problem, but is no solution for a PIC compilation.
This problem appears in both:
Using built-in specs.
Target: mips-unknown-linux-gnu
Configured with: ../gcc/configure --prefix=/home/terpstra/gcc.bin
--enable-languages=c
Thread model: posix
gcc version 4.4.4 (GCC)
and:
Using built-in specs.
Target: mips-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 4.4.4-3'
--with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs
--enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --enable-shared
--enable-multiarch --enable-linker-build-id --with-system-zlib
--libexecdir=/usr/lib --without-included-gettext --enable-threads=posix
--with-gxx-include-dir=/usr/include/c++/4.4 --program-suffix=-4.4 --enable-nls
--enable-clocale=gnu --enable-libstdcxx-debug --disable-libssp
--enable-targets=all --enable-checking=release --build=mips-linux-gnu
--host=mips-linux-gnu --target=mips-linux-gnu
Thread model: posix
gcc version 4.4.4 (Debian 4.4.4-3)
However, I've been having related problems since as far back as gcc-3.4 (at
least). The gcc-snapshot in debian also does not resolve this issue. Sometimes
different compiler versions and/or optimization flags cause compilation to
succeed. I presume this is because the jump offsets fit into 16 bits with
different optimization choices. I've seen a related problem where gcc -fPIC
creates invalid assembler that with 'branch out of range' .s files. However,
first I'll see if this bug being fixed resolves the other.
I've included an example C program which can produce the buggy assembler.
Compile with:
-std=gnu99 -O0 -fno-common -fno-strict-aliasing -fomit-frame-pointer -w
-S mlyacc.6.preprocessed-nolines.c -g -o bug.s
The problem assembler comes from the goto on line 2928:
goto leaveChunk;
... when tracing with gdb execution should resume on line 33861, however it
instead jumps to line 2749:
Inspecting the generated assembler:
.loc 1 2928 0
.setnoat
lw $1,%got($L894)($28)
nop
addiu $1,$1,%lo($L894)
jr $1
... it is easy to see that the correct label (L894) is used as destination.
However, only the low 16 bits of the address are used.
When compiled with -mno-explicit-relocs, the assembler reads as:
.loc 1 2928 0
.setnoat
la $1,$L894 #
jr $1
... which works. Modifying the -mexplicit-relocs version (gcc default) to use
'la' instead of the PIC %got results in correct program execution.
--
Summary: gcc produces bad MIPS jumps (in large C files)
Product: gcc
Version: 4.4.4
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c
AssignedTo: unassigned at gcc dot gnu dot org
ReportedBy: wesley at terpstra dot ca
GCC build triplet: mips-unknown-linux-gnu
GCC host triplet: mips-unknown-linux-gnu
GCC target triplet: mips-unknown-linux-gnu
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=44537