https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84908
Bug ID: 84908 Summary: retpoline weirdness: 7.3.0-1 and 4.8.5-16: with -fPIC: R_X86_64_PC32 against undefined symbol `__x86_indirect_thunk_rax' Product: gcc Version: unknown Status: UNCONFIRMED Severity: normal Priority: P3 Component: c Assignee: unassigned at gcc dot gnu.org Reporter: jason.vas.dias at gmail dot com Target Milestone: --- This bug occurs under Linux x86_64 with gcc-4.8.5-16, as delivered in the latest RHEL7-4x package: gcc-4.8.5-16.el7_4.2.x86_64 and with the compiler used by kernel.org's automated patch tester: kbuild test robot<l...@intel.com> config: x86_64-rhel compiler: gcc-7 (Debian 7.3.0-1) 7.3.0 , ONLY when trying to build the Linux vDSO, ONLY IFF the switch in ${LINUX_KERNEL_SOURCE}/arch/x86/entry/vclock_gettime.c 's __vdso_gettime() function contains MORE THAN 5 CLAUSES, such as: <quote><pre><code> notrace int __vdso_clock_gettime(clockid_t clock, struct timespec *ts) { switch (clock) { case CLOCK_REALTIME: if (do_realtime(ts) == VCLOCK_NONE) goto fallback; break; case CLOCK_MONOTONIC: if (do_monotonic(ts) == VCLOCK_NONE) goto fallback; break; case CLOCK_MONOTONIC_RAW: if (do_monotonic_raw(ts) == VCLOCK_NONE) goto fallback; break; case CLOCK_REALTIME_COARSE: do_realtime_coarse(ts); break; case CLOCK_MONOTONIC_COARSE: do_monotonic_coarse(ts); break; default: goto fallback; } return 0; fallback: return vdso_fallback_gettime(clock, ts); } </code></pre></quote> The 7.3.0-1 compiler and 4.8.5-16 compilers both now complain on the final link : <quote><pre><code> arch/x86/entry/vdso/vclock_gettime.o: In function `__vdso_clock_gettime': vclock_gettime.c:(.text+0xf7): undefined reference to `__x86_indirect_thunk_rax' /usr/bin/ld: arch/x86/entry/vdso/vclock_gettime.o: relocation R_X86_64_PC32 against undefined symbol `__x86_indirect_thunk_rax' can not be used when making a shared object; recompile with -fPIC /usr/bin/ld: final link failed: Bad value </code></pre></quote> Note all objects are being compiled with -fPIC in this case. I can reproduce this with the 4.8.5-16 compiler under RHEL-7.4 : <quote><pre><code> $ gcc -v Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/lto-wrapper Target: x86_64-redhat-linux Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-initfini-array --disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/cloog-install --enable-gnu-indirect-function --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux Thread model: posix gcc version 4.8.5 20150623 (Red Hat 4.8.5-16) (GCC) $ cd $LINUX_KERNEL_SOURCE $ make arch/x86/vdso/ ... make KBUILD_MODULES=1 \ -f scripts/Makefile.build obj=arch/x86/vdso gcc -Wp,-MD,arch/x86/vdso/.vclock_gettime.o.d -nostdinc -isystem /usr/lib/gcc/x86_64-redhat-linux/4.8.5/include -I./arch/x86/include -Iarch/x86/include/generated -Iinclude -I./arch/x86/include/uapi -Iarch/x86/include/generated/uapi -I./include/uapi -Iinclude/generated/uapi -include ./include/linux/kconfig.h -D__KERNEL__ -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -Werror-implicit-function-declaration -Wno-format-security -fno-delete-null-pointer-checks -std=gnu89 -Werror -O2 -m64 -mno-mmx -mno-sse -mpreferred-stack-boundary=3 -mtune=generic -mno-red-zone -mcmodel=kernel -funit-at-a-time -maccumulate-outgoing-args -Wframe-larger-than=2048 -DCONFIG_AS_CFI=1 -DCONFIG_AS_CFI_SIGNAL_FRAME=1 -DCONFIG_AS_CFI_SECTIONS=1 -DCONFIG_AS_FXSAVEQ=1 -DCONFIG_AS_AVX=1 -DCONFIG_AS_AVX2=1 -DCONFIG_AS_AVX512=1 -DCONFIG_AS_SHA1_NI=1 -DCONFIG_AS_SHA256_NI=1 -pipe -Wno-sign-compare -fno-asynchronous-unwind-tables -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -mindirect-branch=thunk-extern -mindirect-branch-register -DRETPOLINE -Wframe-larger-than=2048 -fstack-protector-strong -Wno-unused-but-set-variable -fno-omit-frame-pointer -fno-optimize-sibling-calls -g -mfentry -DCC_USING_FENTRY -fno-inline-functions-called-once -Wdeclaration-after-statement -Wno-pointer-sign -fno-strict-overflow -fconserve-stack -DCC_HAVE_ASM_GOTO -mcmodel=small -fPIC -O2 -fasynchronous-unwind-tables -m64 -g -fno-stack-protector -fno-omit-frame-pointer -foptimize-sibling-calls -D"KBUILD_STR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(vclock_gettime)" -D"KBUILD_MODNAME=KBUILD_STR(vclock_gettime)" -c -o arch/x86/vdso/.tmp_vclock_gettime.o arch/x86/vdso/vclock_gettime.c if [ "" = "-pg" ]; then if [ arch/x86/vdso/vclock_gettime.o != "scripts/mod/empty.o" ]; then ./scripts/recordmcount "arch/x86/vdso/vclock_gettime.o"; fi; fi; gcc -nostdlib -o arch/x86/vdso/vdso.so.dbg -fPIC -shared -Wl,--hash-style=sysv -m64 -Wl,-soname=linux-vdso.so.1 -Wl,--no-undefined -Wl,-z,max-page-size=4096 -Wl,-z,common-page-size=4096 -Wl,-T,arch/x86/vdso/vdso.lds arch/x86/vdso/vdso-note.o arch/x86/vdso/vclock_gettime.o arch/x86/vdso/vgetcpu.o && sh ./arch/x86/vdso/checkundef.sh 'nm' 'arch/x86/vdso/vdso.so.dbg' arch/x86/vdso/vclock_gettime.o: In function `__vdso_clock_gettime': /home/devel/redhat/BUILD/kernel-3.10.0-693.21.1.el7/linux-3.10.0-693.21.1.el7.jvd.x86_64/arch/x86/vdso/vclock_gettime.c:279: undefined reference to `__x86_indirect_thunk_rax' /usr/bin/ld: arch/x86/vdso/vclock_gettime.o: relocation R_X86_64_PC32 against undefined symbol `__x86_indirect_thunk_rax' can not be used when making a shared object; recompile with -fPIC /usr/bin/ld: final link failed: Bad value collect2: error: ld returned 1 exit status objcopy -S arch/x86/vdso/vdso.so.dbg arch/x86/vdso/vdso.so objcopy: 'arch/x86/vdso/vdso.so.dbg': No such file make[1]: *** [arch/x86/vdso/vdso.so] Error 1 </code></pre><quote> The problem is very simply fixed, by simply making sure that the switch statement has no more than 5 clauses: <quote><pre><code> notrace int __vdso_clock_gettime(clockid_t clock, struct timespec *ts) { switch (clock) { case CLOCK_MONOTONIC_RAW: if (do_monotonic_raw(ts) == VCLOCK_NONE) goto fallback; break; default: switch (clock) { case CLOCK_REALTIME: if (do_realtime(ts) == VCLOCK_NONE) goto fallback; break; case CLOCK_MONOTONIC: if (do_monotonic(ts) == VCLOCK_NONE) goto fallback; break; case CLOCK_REALTIME_COARSE: do_realtime_coarse(ts); break; case CLOCK_MONOTONIC_COARSE: do_monotonic_coarse(ts); break; default: goto fallback; } } </code></pre></quote> So with only that change, the code compiles fine and no relocation against __x86_indirect_thunk_rax remains. This problem did not happen with the previous version of the RHEL compiler (4.8.5-16.el7_4.2.x86_64), which compiled the 6 clause version of the switch shown at the top with no problems, when I first developed the patch against kernel-3.10.0-693.17.1.el7 . In order for the bug to be triggered, the extra clause in the switch must invoke some function that returns the 'ret' value and references &vvar_vsycall_gtod_data: <quote><pre><code> static inline __always_inline int test(void) { unsigned long seq; do{ seq = read_seqcount_begin(>od->seq); } while(unlikely(read_seqcount_retry, >od->seq, seq)); return 0; } notrace int __vdso_clock_gettime(clockid_t clock, struct timespec *ts) { ... int ret; switch (clock) { case CLOCK_REALTIME: ret = do_realtime(ts); break; case CLOCK_MONOTONIC: ret = do_monotonic(ts); break; case CLOCK_MONOTONIC_RAW: ret = test(); break; case CLOCK_REALTIME_COARSE: ret = do_realtime_coarse(ts); break; case CLOCK_MONOTONIC_COARSE: ret = do_monotonic_coarse(ts); break; default: break; } if (ret == VCLOCK_NONE) return vdso_fallback_gettime(clock, ts); return 0; } </code></pre></quote> So the problem is not to do with my implementation of do_monotonic_raw() , but with accessing the special vvar_vsyscall_gtod_data, and possibly with an interaction between the 'smp_rmb' used by read_seqcount_begin() (now gtod_read_begin() in modern kernels). So now when I've upgraded the patch to the latest 4.16 / 4.15 version (shown at the top of this bug report) the 7.3.0-1 compiler used by the Kernel's automated tester is also getting this problem. It is a shame to have to insert an extra test of the 'clock' parameter here, just to work around this problem, but it appears to be the only way. I don't think GCC should be inserting any relocations with -fPIC in this situation, should it ? And since the previous RHEL-7 compiler compiled the same code fine, I think this is a regression / bug. I'd greatly appreciate any advice on what the precise cause of this is and how to fix without inserting the extra test / switch statement. Thanks & Best Regards, Jason