[ELF,SPARC] Handle GOTDATA relocations (#213497)
A PIC data reference forms the symbol's GOT offset with R_SPARC_GOTDATA_OP_HIX22 and R_SPARC_GOTDATA_OP_LOX10 and loads through it, with R_SPARC_GOTDATA_OP marking the load: ``` sethi %gdop_hix22(sym), %g1 xor %g1, %gdop_lox10(sym), %g1 ldx [%l7 + %g1], %g1, %gdop(sym) ``` When the symbol is neither preemptible nor absolute, optimize the load to `add %l7, %g1, %g1` over the symbol's GOT-relative address. An absolute symbol keeps the GOT load, as it can be arbitrarily far from .got and we don't want the X86_64::relaxOpt complexity. Co-authored-by: Kirill A. Korinsky <kirill@korins.ky> Co-authored-by: LemonBoy <thatlemon@gmail.com> Co-authored-by: Alex Rønne Petersen <alex@alexrp.com>
This commit is contained in:
parent
303e63dbd8
commit
676d48fb49
2 changed files with 173 additions and 0 deletions
|
|
@ -166,6 +166,20 @@ void SPARCV9::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
|
|||
expr = R_GOT_OFF;
|
||||
break;
|
||||
|
||||
// Optimize the GOT load to an add of the symbol's GOT-relative address if
|
||||
// applicable. Exclude absolute symbol, which can be arbitrarily far. %l7
|
||||
// points at .got, which must be retained.
|
||||
case R_SPARC_GOTDATA_OP_HIX22:
|
||||
case R_SPARC_GOTDATA_OP_LOX10:
|
||||
case R_SPARC_GOTDATA_OP:
|
||||
if (sym.isPreemptible || isAbsolute(sym)) {
|
||||
expr = R_GOT_OFF;
|
||||
} else {
|
||||
ctx.in.got->hasGotOffRel.store(true, std::memory_order_relaxed);
|
||||
expr = R_GOTREL;
|
||||
}
|
||||
break;
|
||||
|
||||
// TLS LE relocations:
|
||||
case R_SPARC_TLS_LE_HIX22:
|
||||
case R_SPARC_TLS_LE_LOX10:
|
||||
|
|
@ -322,6 +336,24 @@ void SPARCV9::relocate(uint8_t *loc, const Relocation &rel,
|
|||
// T-simm13
|
||||
write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff) | 0x1C00);
|
||||
break;
|
||||
case R_SPARC_GOTDATA_OP_HIX22: {
|
||||
// V-imm22. sethi encodes the complement of a negative value, which the
|
||||
// paired xor undoes, so the encodable range is 33 bits.
|
||||
checkInt(ctx, loc, val, 33, rel);
|
||||
uint64_t v = int64_t(val) < 0 ? ~val : val;
|
||||
write32be(loc, (read32be(loc) & ~0x003fffff) | ((v >> 10) & 0x003fffff));
|
||||
break;
|
||||
}
|
||||
case R_SPARC_GOTDATA_OP_LOX10:
|
||||
// T-simm13. Only a negative value needs the sign extension bits.
|
||||
write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff) |
|
||||
(int64_t(val) < 0 ? 0x1c00 : 0));
|
||||
break;
|
||||
case R_SPARC_GOTDATA_OP:
|
||||
// ldx [%rs1 + %rs2], %rd -> add %rs1, %rs2, %rd
|
||||
if (rel.expr == R_GOTREL)
|
||||
write32be(loc, (read32be(loc) & 0x3e07c01f) | 0x80000000);
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("unknown relocation");
|
||||
}
|
||||
|
|
|
|||
141
lld/test/ELF/sparcv9-gotdata.s
Normal file
141
lld/test/ELF/sparcv9-gotdata.s
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
# REQUIRES: sparc
|
||||
## Test R_SPARC_GOTDATA_OP_HIX22, R_SPARC_GOTDATA_OP_LOX10 and R_SPARC_GOTDATA_OP.
|
||||
## The GOT load is optimized to an add of the symbol's GOT-relative address
|
||||
## unless the symbol is preemptible or absolute.
|
||||
|
||||
# RUN: rm -rf %t && split-file %s %t && cd %t
|
||||
# RUN: llvm-mc -filetype=obj -triple=sparcv9 a.s -o a.o
|
||||
# RUN: llvm-mc -filetype=obj -triple=sparcv9 b.s -o b.o
|
||||
# RUN: llvm-mc -filetype=obj -triple=sparcv9 c.s -o c.o
|
||||
# RUN: llvm-mc -filetype=obj -triple=sparcv9 ifunc.s -o ifunc.o
|
||||
|
||||
# RUN: ld.lld -shared a.o c.o -o a.so
|
||||
# RUN: llvm-readelf -S -r a.so | FileCheck %s --check-prefix=RELOC
|
||||
# RUN: llvm-objdump -d -j .text --no-show-raw-insn --no-print-imm-hex a.so | FileCheck %s
|
||||
|
||||
# RELOC: [ 8] .got PROGBITS 0000000000200390 {{[0-9a-f]+}} 000020
|
||||
# RELOC: Relocation section '.rela.dyn' {{.*}} contains 1 entries:
|
||||
# RELOC: 0000000000200398 {{.*}} R_SPARC_GLOB_DAT {{.*}} foo + 0
|
||||
|
||||
## foo is preemptible, so the GOT load stays.
|
||||
## .got[1] - _GLOBAL_OFFSET_TABLE_ = 0x200398 - 0x200390 = 8.
|
||||
# CHECK-LABEL: <_start>:
|
||||
# CHECK-NEXT: sethi 0, %l1
|
||||
# CHECK-NEXT: xor %l1, 8, %l1
|
||||
# CHECK-NEXT: ldx [%l7+%l1], %l2
|
||||
## hid0 - _GLOBAL_OFFSET_TABLE_ = 0x3003b0 - 0x200390 = 0x100020.
|
||||
# CHECK-NEXT: sethi 1024, %g1
|
||||
# CHECK-NEXT: xor %g1, 32, %g1
|
||||
# CHECK-NEXT: add %l7, %g1, %g1
|
||||
## hid1 - _GLOBAL_OFFSET_TABLE_ = 0x1002ec - 0x200390 = -0x1000a4. sethi holds
|
||||
## the complement, which the sign-extended xor operand undoes.
|
||||
# CHECK-NEXT: sethi 1024, %o2
|
||||
# CHECK-NEXT: xor %o2, -164, %o2
|
||||
# CHECK-NEXT: add %i0, %o2, %i5
|
||||
## An undefined symbol and an absolute symbol both have an absolute address, so
|
||||
## neither is optimized.
|
||||
# CHECK-NEXT: sethi 0, %l1
|
||||
# CHECK-NEXT: xor %l1, 16, %l1
|
||||
# CHECK-NEXT: ldx [%l7+%l1], %l2
|
||||
# CHECK-NEXT: sethi 0, %g4
|
||||
# CHECK-NEXT: xor %g4, 24, %g4
|
||||
# CHECK-NEXT: ldx [%l7+%g4], %o3
|
||||
|
||||
## foo is not preemptible in an executable and is optimized as well. undefweak
|
||||
## and absolute keep the GOT load in every mode.
|
||||
# RUN: ld.lld a.o c.o -o a
|
||||
# RUN: llvm-objdump -d -j .text --no-show-raw-insn --no-print-imm-hex a | FileCheck %s --check-prefix=NOPIE
|
||||
# NOPIE-LABEL: <_start>:
|
||||
# NOPIE-NEXT: sethi 1024, %l1
|
||||
# NOPIE-NEXT: xor %l1, -4, %l1
|
||||
# NOPIE-NEXT: add %l7, %l1, %l2
|
||||
# NOPIE: sethi 0, %l1
|
||||
# NOPIE-NEXT: xor %l1, 8, %l1
|
||||
# NOPIE-NEXT: ldx [%l7+%l1], %l2
|
||||
# NOPIE-NEXT: sethi 0, %g4
|
||||
# NOPIE-NEXT: xor %g4, 16, %g4
|
||||
# NOPIE-NEXT: ldx [%l7+%g4], %o3
|
||||
|
||||
## .got holds nothing but the header, yet must be kept: %l7 points at it.
|
||||
# RUN: ld.lld -shared b.o -o b.so
|
||||
# RUN: llvm-readelf -S b.so | FileCheck %s --check-prefix=HEADER
|
||||
# HEADER: [ 7] .got PROGBITS 00000000002002b8 {{[0-9a-f]+}} 000008
|
||||
|
||||
# RUN: not ld.lld -shared ifunc.o 2>&1 | FileCheck %s --check-prefix=IFUNC --implicit-check-not=error:
|
||||
# IFUNC: error: {{.*}}relocation R_SPARC_WDISP19 out of range
|
||||
|
||||
## The optimization is decided before addresses are known, so an out-of-range
|
||||
## offset is an error rather than a fallback to the GOT load. The offset is
|
||||
## reported as signed, not as the complement the sethi encodes. Only hid0, in
|
||||
## .data, is out of range; the other symbols stay next to .got.
|
||||
# RUN: not ld.lld -shared a.o c.o --section-start=.text=0x10000 --section-start=.got=0x20000 \
|
||||
# RUN: --section-start=.data=0x200000000 2>&1 | FileCheck %s --check-prefix=ERR -DVAL=8589803520 --implicit-check-not=error:
|
||||
# RUN: not ld.lld -shared a.o c.o --section-start=.data=0x10000 --section-start=.text=0x200000000 \
|
||||
# RUN: --section-start=.got=0x200020000 2>&1 | FileCheck %s --check-prefix=ERR -DVAL=-8590000128 --implicit-check-not=error:
|
||||
# ERR: error: a.o:(.text+0xc): relocation R_SPARC_GOTDATA_OP_HIX22 out of range: [[VAL]] is not in [-4294967296, 4294967295]; references 'hid0'
|
||||
|
||||
#--- a.s
|
||||
.text
|
||||
.globl _start
|
||||
_start:
|
||||
sethi %gdop_hix22(foo), %l1
|
||||
xor %l1, %gdop_lox10(foo), %l1
|
||||
ldx [%l7 + %l1], %l2, %gdop(foo)
|
||||
|
||||
sethi %gdop_hix22(hid0), %g1
|
||||
xor %g1, %gdop_lox10(hid0), %g1
|
||||
ldx [%l7 + %g1], %g1, %gdop(hid0)
|
||||
|
||||
## The add keeps rs1, rs2 and rd of the load, and ld is optimized like ldx.
|
||||
sethi %gdop_hix22(hid1), %o2
|
||||
xor %o2, %gdop_lox10(hid1), %o2
|
||||
ld [%i0 + %o2], %i5, %gdop(hid1)
|
||||
|
||||
sethi %gdop_hix22(undefweak), %l1
|
||||
xor %l1, %gdop_lox10(undefweak), %l1
|
||||
ldx [%l7 + %l1], %l2, %gdop(undefweak)
|
||||
|
||||
sethi %gdop_hix22(absolute), %g4
|
||||
xor %g4, %gdop_lox10(absolute), %g4
|
||||
ldx [%l7 + %g4], %o3, %gdop(absolute)
|
||||
|
||||
.globl foo, hid0, hid1
|
||||
.hidden hid0, hid1
|
||||
foo:
|
||||
hid1:
|
||||
nop
|
||||
|
||||
.weak undefweak
|
||||
.hidden undefweak
|
||||
|
||||
.data
|
||||
hid0:
|
||||
.xword 0
|
||||
|
||||
#--- b.s
|
||||
.globl _start
|
||||
_start:
|
||||
sethi %gdop_hix22(local), %l1
|
||||
xor %l1, %gdop_lox10(local), %l1
|
||||
ldx [%l7 + %l1], %l2, %gdop(local)
|
||||
local:
|
||||
nop
|
||||
|
||||
#--- c.s
|
||||
## Separate file to test %gdop_lox10(absolute) relocations in a.s
|
||||
.globl absolute
|
||||
.hidden absolute
|
||||
absolute = 0x1234
|
||||
|
||||
#--- ifunc.s
|
||||
.globl _start
|
||||
_start:
|
||||
sethi %gdop_hix22(ifunc), %l1
|
||||
xor %l1, %gdop_lox10(ifunc), %l1
|
||||
ldx [%l7 + %l1], %l2, %gdop(ifunc)
|
||||
|
||||
.globl ifunc
|
||||
.hidden ifunc
|
||||
.type ifunc, @gnu_indirect_function
|
||||
ifunc:
|
||||
nop
|
||||
Loading…
Reference in a new issue