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(&gtod->seq);
          } while(unlikely(read_seqcount_retry, &gtod->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

Reply via email to