[X86] Don't emit R_X86_64_[REX_]GOTPCRELX for a GOT load with an offset
authorFangrui Song <i@maskray.me>
Mon, 30 Nov 2020 16:27:30 +0000 (08:27 -0800)
committerFangrui Song <i@maskray.me>
Mon, 30 Nov 2020 16:27:31 +0000 (08:27 -0800)
clang may produce `movl x@GOTPCREL+4(%rip), %eax` when loading the high
32 bits of the address of a global variable in -fpic/-fpie mode.

If assembled by GNU as, the fixup emits R_X86_64_GOTPCRELX with an addend != -4.
The instruction loads from the GOT entry with an offset and thus it is incorrect
to relax the instruction.

This patch does not emit a relaxable relocation for a GOT load with an offset
because R_X86_64_[REX_]GOTPCRELX do not make sense for instructions which cannot
be relaxed.  The result is good enough for LLD to work. GNU ld relaxes
mov+GOTPCREL as well, but it suppresses the relaxation if addend != -4.

Reviewed By: jhenderson

Differential Revision: https://reviews.llvm.org/D92114

llvm/lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp
llvm/test/MC/ELF/got-relaxed-rex.s
llvm/test/MC/X86/gotpcrelx.s

index 714f45c..4e5e45f 100644 (file)
@@ -397,10 +397,14 @@ void X86MCCodeEmitter::emitMemModRMByte(const MCInst &MI, unsigned Op,
     emitByte(modRMByte(0, RegOpcodeField, 5), OS);
 
     unsigned Opcode = MI.getOpcode();
-    // movq loads are handled with a special relocation form which allows the
-    // linker to eliminate some loads for GOT references which end up in the
-    // same linkage unit.
-    unsigned FixupKind = [=]() {
+    unsigned FixupKind = [&]() {
+      // Enable relaxed relocation only for a MCSymbolRefExpr.  We cannot use a
+      // relaxed relocation if an offset is present (e.g. x@GOTPCREL+4).
+      if (!(Disp.isExpr() && isa<MCSymbolRefExpr>(Disp.getExpr())))
+        return X86::reloc_riprel_4byte;
+
+      // Certain loads for GOT references can be relocated against the symbol
+      // directly if the symbol ends up in the same linkage unit.
       switch (Opcode) {
       default:
         return X86::reloc_riprel_4byte;
@@ -416,6 +420,9 @@ void X86MCCodeEmitter::emitMemModRMByte(const MCInst &MI, unsigned Op,
       case X86::XOR32rm:
         return X86::reloc_riprel_4byte_relax;
       case X86::MOV64rm:
+        // movq loads is a subset of reloc_riprel_4byte_relax_rex. It is a
+        // special case because COFF and Mach-O don't support ELF's more
+        // flexible R_X86_64_REX_GOTPCRELX relaxation.
         assert(HasREX);
         return X86::reloc_riprel_4byte_movq_load;
       case X86::CALL64m:
index e694d74..1924bdd 100644 (file)
         sub sub@GOTPCREL(%rip), %rax
         xor xor@GOTPCREL(%rip), %rax
 
+.section .norelax,"ax"
+## This expression loads the GOT entry with an offset.
+## Don't emit R_X86_64_REX_GOTPCRELX.
+        movq mov@GOTPCREL+1(%rip), %rax
+
 // CHECK:      Relocations [
 // CHECK-NEXT:   Section ({{.*}}) .rela.text {
 // CHECK-NEXT:     R_X86_64_REX_GOTPCRELX mov
@@ -26,4 +31,6 @@
 // CHECK-NEXT:     R_X86_64_REX_GOTPCRELX sub
 // CHECK-NEXT:     R_X86_64_REX_GOTPCRELX xor
 // CHECK-NEXT:   }
-// CHECK-NEXT: ]
+// CHECK-NEXT:   Section ({{.*}}) .rela.norelax {
+// CHECK-NEXT:     R_X86_64_GOTPCREL mov
+// CHECK-NEXT:   }
index 4afc762..3889835 100644 (file)
@@ -1,8 +1,10 @@
-# RUN: llvm-mc -filetype=obj -triple=x86_64 %s | llvm-readobj -r - | FileCheck %s
-# RUN: llvm-mc -filetype=obj -triple=x86_64 -relax-relocations=false %s | llvm-readobj -r - | FileCheck --check-prefix=NORELAX %s
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
+# RUN: llvm-readobj -r %t.o | FileCheck %s --check-prefixes=CHECK,COMMON
+# RUN: llvm-mc -filetype=obj -triple=x86_64 -relax-relocations=false %s -o %t1.o
+# RUN: llvm-readobj -r %t1.o | FileCheck %s --check-prefixes=NORELAX,COMMON
 
-# CHECK:      Relocations [
-# CHECK-NEXT:   Section ({{.*}}) .rela.text {
+# COMMON:     Relocations [
+# COMMON-NEXT:  Section ({{.*}}) .rela.text {
 # CHECK-NEXT:     R_X86_64_GOTPCRELX mov
 # CHECK-NEXT:     R_X86_64_GOTPCRELX test
 # CHECK-NEXT:     R_X86_64_GOTPCRELX adc
 # CHECK-NEXT:     R_X86_64_GOTPCRELX call
 # CHECK-NEXT:     R_X86_64_GOTPCRELX jmp
 # CHECK-NEXT:   }
-# CHECK-NEXT: ]
 
-# NORELAX:      Relocations [
-# NORELAX-NEXT:   Section ({{.*}}) .rela.text {
 # NORELAX-NEXT:     R_X86_64_GOTPCREL mov
 # NORELAX-NEXT:     R_X86_64_GOTPCREL test
 # NORELAX-NEXT:     R_X86_64_GOTPCREL adc
@@ -33,7 +32,6 @@
 # NORELAX-NEXT:     R_X86_64_GOTPCREL call
 # NORELAX-NEXT:     R_X86_64_GOTPCREL jmp
 # NORELAX-NEXT:   }
-# NORELAX-NEXT: ]
 
 movl mov@GOTPCREL(%rip), %eax
 test %eax, test@GOTPCREL(%rip)
@@ -47,3 +45,16 @@ sub sub@GOTPCREL(%rip), %eax
 xor xor@GOTPCREL(%rip), %eax
 call *call@GOTPCREL(%rip)
 jmp *jmp@GOTPCREL(%rip)
+
+# COMMON-NEXT:   Section ({{.*}}) .rela.norelax {
+# COMMON-NEXT:     R_X86_64_GOTPCREL mov 0x0
+# COMMON-NEXT:     R_X86_64_GOTPCREL mov 0xFFFFFFFFFFFFFFFC
+# COMMON-NEXT:   }
+# COMMON-NEXT: ]
+
+.section .norelax,"ax",@progbits
+## Clang may emit this expression to load the high 32-bit of the GOT entry.
+## Don't emit R_X86_64_GOTPCRELX.
+movl mov@GOTPCREL+4(%rip), %eax
+## We could emit R_X86_64_GOTPCRELX, but it is probably unnecessary.
+movl mov@GOTPCREL+0(%rip), %eax