diff --git a/llvm/lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp b/llvm/lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp --- a/llvm/lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp +++ b/llvm/lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp @@ -397,10 +397,14 @@ 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(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 @@ 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: diff --git a/llvm/test/MC/ELF/got-relaxed-rex.s b/llvm/test/MC/ELF/got-relaxed-rex.s --- a/llvm/test/MC/ELF/got-relaxed-rex.s +++ b/llvm/test/MC/ELF/got-relaxed-rex.s @@ -13,6 +13,11 @@ 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: } diff --git a/llvm/test/MC/X86/gotpcrelx.s b/llvm/test/MC/X86/gotpcrelx.s --- a/llvm/test/MC/X86/gotpcrelx.s +++ b/llvm/test/MC/X86/gotpcrelx.s @@ -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 @@ -16,10 +18,7 @@ # 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 @@ 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