https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81658
Bug ID: 81658 Summary: gcc configured with --enable-default-pie on SPARC produces buggy executable from working .o files Product: gcc Version: 6.4.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: driver Assignee: unassigned at gcc dot gnu.org Reporter: bruno at clisp dot org Target Milestone: --- Created attachment 41886 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=41886&action=edit tarball of object files GCC, configured with --enable-default-pie on SPARC, links object files that happen to access global variables in such a way that the resulting executable is broken: it crashes when attempting to access the global variable. Test case: Find attached a tarball with 4 object files. They were produced from https://haible.de/bruno/gnu/libffcall-2.0-20170731.tar.gz on a Linux/sparc machine with gcc 6.3.0 (that is NOT configured with --enable-default-pie). On that machine: $ gcc -m32 vacall-libapi.o vacall.o vacall-structcpy.o minitests.o $ ./a.out <lots of good output, succeeds> Unpack the same tarball and link the same object files on a machine with a GCC configured with --enable-default-pie: $ gcc -v Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/lib/gcc/sparc64-linux-gnu/6/lto-wrapper Target: sparc64-linux-gnu Configured with: ../src/configure -v --with-pkgversion='Debian 6.4.0-2' --with-bugurl=file:///usr/share/doc/gcc-6/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-6 --program-prefix=sparc64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-libquadmath --enable-plugin --enable-default-pie --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-6-sparc64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-6-sparc64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-6-sparc64 --with-arch-directory=sparc64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc=auto --enable-multiarch --enable-targets=all --with-cpu-32=ultrasparc --with-long-double-128 --enable-multilib --enable-checking=release --build=sparc64-linux-gnu --host=sparc64-linux-gnu --target=sparc64-linux-gnu Thread model: posix gcc version 6.4.0 20170724 (Debian 6.4.0-2) $ gcc -m32 vacall-libapi.o vacall.o vacall-structcpy.o minitests.o $ ./a.out void f(void): Segmentation fault Using "gcc -v", I see that the link command is essentially /usr/lib/gcc/sparc64-linux-gnu/6/collect2 --sysroot=/ --build-id --eh-frame-hdr -m elf32_sparc -dynamic-linker /lib/ld-linux.so.2 -relax -pie -o a.out /usr/lib32/Scrt1.o /usr/lib32/crti.o /usr/lib/gcc/sparc64-linux-gnu/6/32/crtbeginS.o -L/usr/lib/gcc/sparc64-linux-gnu/6/32 -L/usr/lib32 -L/lib32 -L/usr/lib32 -L/usr/lib/gcc/sparc64-linux-gnu/6 -L/usr/lib vacall-libapi.o vacall.o vacall-structcpy.o minitests.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/sparc64-linux-gnu/6/32/crtend.o /usr/lib32/crtn.o If I execute it manually, the resulting a.out file crashes: $ ./a.out void f(void): Segmentation fault But if I execute it manually without the "-pie" option, the resulting a.out file works. $ ./a.out <lots of good output, succeeds> The difference between the two is at the beginning of function vacall_receiver. 0x5b48 <vacall_receiver>: save %sp, -144, %sp 0x5b4c <vacall_receiver+4>: sethi %hi(0), %o0 0x5b50 <vacall_receiver+8>: sethi %hi(0x1c400), %l7 0x5b54 <vacall_receiver+12>: call 0x5b40 0x5b58 <vacall_receiver+16>: add %l7, 0xac, %l7 ! 0x1c4ac 0x5b5c <vacall_receiver+20>: or %o0, 0x180, %o0 0x5b60 <vacall_receiver+24>: ld [ %l7 + %o0 ], %o1 At the instruction ld [ %l7 + %o0 ], %o1 the values are: In the case that works: %l7 + %o0 = 0x32180 => loads the word 0x32b58 into %o1 &vacall_function = 0x32b58 In the case that does not work: %l7 + %o0 = 0x70022180 => loads the word 0x700456b0 into %o1 &vacall_function = 0x70022b58 The value in memory is wrong! 0x700456b0 is too high by 0x22b58 (strange coincidence of numbers - looks like a relocation has been applied twice instead of just once) When I set a watchpoint at that location, I see that it's modified only once: (gdb) watch *(void**)0x70022180 Hardware watchpoint 1: *(void**)0x70022180 (gdb) run Starting program: /home/haible/vacall/a.out Watchpoint 1: *(void**)0x70022180 Old value = (void *) 0x22b58 New value = (void *) 0x700456b0 elf_dynamic_do_Rela (skip_ifunc=<optimized out>, lazy=0, nrelative=<optimized out>, relsize=<optimized out>, reladdr=<optimized out>, map=0xf7ffaa10) at do-rel.h:112 112 do-rel.h: No such file or directory. I'm reporting this as a bug in GCC, because it's GCC which passes the option '-pie' to the linker, and it is this option which causes the executable to be non-functional.