[llvm] f04d92a - [X86] Produce R_X86_64_GOTPCRELX for test/binop instructions (MOV32rm/TEST32rm/...) when -Wa, -mrelax-relocations=yes is enabled

Stephan Bergmann via llvm-commits llvm-commits at lists.llvm.org
Mon Oct 26 15:07:40 PDT 2020


On 25/10/2020 00:14, Fangrui Song via llvm-commits wrote:
> 
> Author: Fangrui Song
> Date: 2020-10-24T15:14:17-07:00
> New Revision: f04d92af94a8d763e91ae38fe35319e426dc466c
> 
> URL: https://github.com/llvm/llvm-project/commit/f04d92af94a8d763e91ae38fe35319e426dc466c
> DIFF: https://github.com/llvm/llvm-project/commit/f04d92af94a8d763e91ae38fe35319e426dc466c.diff
> 
> LOG: [X86] Produce R_X86_64_GOTPCRELX for test/binop instructions (MOV32rm/TEST32rm/...) when -Wa,-mrelax-relocations=yes is enabled
> 
> We have been producing R_X86_64_REX_GOTPCRELX (MOV64rm/TEST64rm/...) and
> R_X86_64_GOTPCRELX for CALL64m/JMP64m without the REX prefix since 2016 (to be
> consistent with GNU as), but not for MOV32rm/TEST32rm/...
> 
> Added:
>      llvm/test/MC/X86/gotpcrelx.s
> 
> Modified:
>      llvm/lib/ExecutionEngine/JITLink/ELF_x86_64.cpp
>      llvm/lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp
>      llvm/test/MC/ELF/got.s
> 
> Removed:
>      llvm/test/MC/ELF/got-relaxed.s

At least on Linux x86-64 in combination with lld, this causes 
miscompilation when an expression like

   ((long) otherfunction) >> 32

computing the upper 32 bits of a function's address is emitted as

   movl otherfunction at GOTPCREL+4(%rip), %eax

which lld then optimizes as

   lea otherfunction+4(%rip), %eax

computing the lower 32 bits of the function's address + 4.

The simplest reproducer I came up with is

> $ cat test.c
> void otherfunction(void);
> int __attribute__ ((visibility("default"))) test1(void) { return (long) otherfunction; }
> int __attribute__ ((visibility("default"))) test2(void) { return ((long) otherfunction) >> 32; }
> 
> $ cat otherfunction.c
> void otherfunction(void) {}
> 
> $ cat main.c
> #include <stdio.h>
> int test1(void);
> int test2(void);
> int main() {
>   printf("%08X %08X\n", test1(), test2());
>   return 0;
> }
> 
> $ llvm/inst/bin/clang -fpic -fvisibility=hidden -O -c test.c
> $ objdump -dr test.o | grep -F -A3 '<test'
> 0000000000000000 <test1>:
>    0:   8b 05 00 00 00 00       mov    0x0(%rip),%eax        # 6 <test1+0x6>
>                         2: R_X86_64_GOTPCRELX   otherfunction-0x4
>    6:   c3                      retq
>    7:   66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)
> --
> 0000000000000010 <test2>:
>   10:   8b 05 00 00 00 00       mov    0x0(%rip),%eax        # 16 <test2+0x6>
>                         12: R_X86_64_GOTPCRELX  otherfunction
>   16:   c3                      retq
> 
> $ llvm/inst/bin/clang -fpic -fvisibility=hidden -c otherfunction.c
> 
> $ llvm/inst/bin/clang -fuse-ld=lld --ld-path=/home/sbergman/llvm/inst/bin/ld.lld -shared test.o otherfunction.o -o libtest.so
> $ objdump -dR libtest.so | grep -F -A3 '<test'
> 00000000000015d0 <test1>:
>     15d0:       8d 05 1a 00 00 00       lea    0x1a(%rip),%eax        # 15f0 <otherfunction>
>     15d6:       c3                      retq
>     15d7:       66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)
> --
> 00000000000015e0 <test2>:
>     15e0:       8d 05 0e 00 00 00       lea    0xe(%rip),%eax        # 15f4 <otherfunction+0x4>
>     15e6:       c3                      retq
>     15e7:       cc                      int3
> 
> $ llvm/inst/bin/clang main.c libtest.so
> $ LD_LIBRARY_PATH=. ./a.out
> 602065F0 602065F4

where using R_X86_64_GOTPCRELX instead of R_X86_64_GOTPCREL in test2 
causes the second value printed out to be (first value + 4) instead of 
some 00007F55 or similar.



More information about the llvm-commits mailing list