forked from OSchip/llvm-project
[LLD][ELF][AVR] Implement the missing relocation types
Implements the missing relocation types for AVR target. The results have been cross-checked with binutils. Original patch by LemonBoy. Some changes by me. Differential Revision: https://reviews.llvm.org/D78741
This commit is contained in:
parent
d589372704
commit
69e60c9dc7
|
@ -54,11 +54,131 @@ AVR::AVR() { noneRel = R_AVR_NONE; }
|
|||
|
||||
RelExpr AVR::getRelExpr(RelType type, const Symbol &s,
|
||||
const uint8_t *loc) const {
|
||||
return R_ABS;
|
||||
switch (type) {
|
||||
case R_AVR_7_PCREL:
|
||||
case R_AVR_13_PCREL:
|
||||
return R_PC;
|
||||
default:
|
||||
return R_ABS;
|
||||
}
|
||||
}
|
||||
|
||||
static void writeLDI(uint8_t *loc, uint64_t val) {
|
||||
write16le(loc, (read16le(loc) & 0xf0f0) | (val & 0xf0) << 4 | (val & 0x0f));
|
||||
}
|
||||
|
||||
void AVR::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
|
||||
switch (rel.type) {
|
||||
case R_AVR_8:
|
||||
checkUInt(loc, val, 8, rel);
|
||||
*loc = val;
|
||||
break;
|
||||
case R_AVR_16:
|
||||
// Note: this relocation is often used between code and data space, which
|
||||
// are 0x800000 apart in the output ELF file. The bitmask cuts off the high
|
||||
// bit.
|
||||
write16le(loc, val & 0xffff);
|
||||
break;
|
||||
case R_AVR_16_PM:
|
||||
checkAlignment(loc, val, 2, rel);
|
||||
checkUInt(loc, val >> 1, 16, rel);
|
||||
write16le(loc, val >> 1);
|
||||
break;
|
||||
case R_AVR_32:
|
||||
checkUInt(loc, val, 32, rel);
|
||||
write32le(loc, val);
|
||||
break;
|
||||
|
||||
case R_AVR_LDI:
|
||||
checkUInt(loc, val, 8, rel);
|
||||
writeLDI(loc, val & 0xff);
|
||||
break;
|
||||
|
||||
case R_AVR_LO8_LDI_NEG:
|
||||
writeLDI(loc, -val & 0xff);
|
||||
break;
|
||||
case R_AVR_LO8_LDI:
|
||||
writeLDI(loc, val & 0xff);
|
||||
break;
|
||||
case R_AVR_HI8_LDI_NEG:
|
||||
writeLDI(loc, (-val >> 8) & 0xff);
|
||||
break;
|
||||
case R_AVR_HI8_LDI:
|
||||
writeLDI(loc, (val >> 8) & 0xff);
|
||||
break;
|
||||
case R_AVR_HH8_LDI_NEG:
|
||||
writeLDI(loc, (-val >> 16) & 0xff);
|
||||
break;
|
||||
case R_AVR_HH8_LDI:
|
||||
writeLDI(loc, (val >> 16) & 0xff);
|
||||
break;
|
||||
case R_AVR_MS8_LDI_NEG:
|
||||
writeLDI(loc, (-val >> 24) & 0xff);
|
||||
break;
|
||||
case R_AVR_MS8_LDI:
|
||||
writeLDI(loc, (val >> 24) & 0xff);
|
||||
break;
|
||||
|
||||
case R_AVR_LO8_LDI_PM:
|
||||
checkAlignment(loc, val, 2, rel);
|
||||
writeLDI(loc, (val >> 1) & 0xff);
|
||||
break;
|
||||
case R_AVR_HI8_LDI_PM:
|
||||
checkAlignment(loc, val, 2, rel);
|
||||
writeLDI(loc, (val >> 9) & 0xff);
|
||||
break;
|
||||
case R_AVR_HH8_LDI_PM:
|
||||
checkAlignment(loc, val, 2, rel);
|
||||
writeLDI(loc, (val >> 17) & 0xff);
|
||||
break;
|
||||
|
||||
case R_AVR_LO8_LDI_PM_NEG:
|
||||
checkAlignment(loc, val, 2, rel);
|
||||
writeLDI(loc, (-val >> 1) & 0xff);
|
||||
break;
|
||||
case R_AVR_HI8_LDI_PM_NEG:
|
||||
checkAlignment(loc, val, 2, rel);
|
||||
writeLDI(loc, (-val >> 9) & 0xff);
|
||||
break;
|
||||
case R_AVR_HH8_LDI_PM_NEG:
|
||||
checkAlignment(loc, val, 2, rel);
|
||||
writeLDI(loc, (-val >> 17) & 0xff);
|
||||
break;
|
||||
|
||||
case R_AVR_PORT5:
|
||||
checkUInt(loc, val, 5, rel);
|
||||
write16le(loc, (read16le(loc) & 0xff07) | (val << 3));
|
||||
break;
|
||||
case R_AVR_PORT6:
|
||||
checkUInt(loc, val, 6, rel);
|
||||
write16le(loc, (read16le(loc) & 0xf9f0) | (val & 0x30) << 5 | (val & 0x0f));
|
||||
break;
|
||||
|
||||
// Since every jump destination is word aligned we gain an extra bit
|
||||
case R_AVR_7_PCREL: {
|
||||
checkInt(loc, val, 7, rel);
|
||||
checkAlignment(loc, val, 2, rel);
|
||||
const uint16_t target = (val - 2) >> 1;
|
||||
write16le(loc, (read16le(loc) & 0xfc07) | ((target & 0x7f) << 3));
|
||||
break;
|
||||
}
|
||||
case R_AVR_13_PCREL: {
|
||||
checkAlignment(loc, val, 2, rel);
|
||||
const uint16_t target = (val - 2) >> 1;
|
||||
write16le(loc, (read16le(loc) & 0xf000) | (target & 0xfff));
|
||||
break;
|
||||
}
|
||||
|
||||
case R_AVR_6:
|
||||
checkInt(loc, val, 6, rel);
|
||||
write16le(loc, (read16le(loc) & 0xd3f8) | (val & 0x20) << 8 |
|
||||
(val & 0x18) << 7 | (val & 0x07));
|
||||
break;
|
||||
case R_AVR_6_ADIW:
|
||||
checkInt(loc, val, 6, rel);
|
||||
write16le(loc, (read16le(loc) & 0xff30) | (val & 0x30) << 2 | (val & 0x0F));
|
||||
break;
|
||||
|
||||
case R_AVR_CALL: {
|
||||
uint16_t hi = val >> 17;
|
||||
uint16_t lo = val >> 1;
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
; REQUIRES: avr
|
||||
; RUN: llvm-mc -filetype=obj -triple=avr -mcpu=atmega328p %s -o %t.o
|
||||
; RUN: ld.lld %t.o --defsym=a=0x12345678 --defsym=b=30 -o %t
|
||||
; RUN: llvm-objdump -d --print-imm-hex %t | FileCheck %s
|
||||
; RUN: llvm-objdump -s %t | FileCheck --check-prefix=HEX %s
|
||||
|
||||
.section .LDI,"ax",@progbits
|
||||
; CHECK-LABEL: section .LDI:
|
||||
; CHECK: ldi r20, 0x78
|
||||
; CHECK-NEXT: ldi r20, 0x56
|
||||
; CHECK-NEXT: ldi r20, 0x34
|
||||
; CHECK-NEXT: ldi r20, 0x12
|
||||
; CHECK-NEXT: ldi r20, 0x3c
|
||||
; CHECK-NEXT: ldi r20, 0x2b
|
||||
; CHECK-NEXT: ldi r20, 0x1a
|
||||
; CHECK-NEXT: ldi r20, 0xff
|
||||
ldi r20, lo8(a) ; R_AVR_LO8_LDI
|
||||
ldi r20, hi8(a) ; R_AVR_HI8_LDI
|
||||
ldi r20, hh8(a) ; R_AVR_HH8_LDI
|
||||
ldi r20, hhi8(a) ; R_AVR_MS8_LDI
|
||||
|
||||
ldi r20, pm_lo8(a) ; R_AVR_LO8_LDI_PM
|
||||
ldi r20, pm_hi8(a) ; R_AVR_HI8_LDI_PM
|
||||
ldi r20, pm_hh8(a) ; R_AVR_HH8_LDI_PM
|
||||
|
||||
ldi r20, b+225
|
||||
|
||||
.section .LDI_NEG,"ax",@progbits
|
||||
; CHECK-LABEL: section .LDI_NEG:
|
||||
; CHECK: ldi r20, 0x88
|
||||
; CHECK-NEXT: ldi r20, 0xa9
|
||||
; CHECK-NEXT: ldi r20, 0xcb
|
||||
; CHECK-NEXT: ldi r20, 0xed
|
||||
; CHECK-NEXT: ldi r20, 0xc4
|
||||
; CHECK-NEXT: ldi r20, 0xd4
|
||||
; CHECK-NEXT: ldi r20, 0xe5
|
||||
ldi r20, lo8(-(a)) ; R_AVR_LO8_LDI_NEG
|
||||
ldi r20, hi8(-(a)) ; R_AVR_HI8_LDI_NEG
|
||||
ldi r20, hh8(-(a)) ; R_AVR_HH8_LDI_NEG
|
||||
ldi r20, hhi8(-(a)) ; R_AVR_MS8_LDI_NEG
|
||||
|
||||
ldi r20, pm_lo8(-(a)) ; R_AVR_LO8_LDI_PM_NEG
|
||||
ldi r20, pm_hi8(-(a)) ; R_AVR_HI8_LDI_PM_NEG
|
||||
ldi r20, pm_hh8(-(a)) ; R_AVR_HH8_LDI_PM_NEG
|
||||
|
||||
;; The disassembler is not yet able to decode those opcodes
|
||||
;; 9e 8e std Y+30, r9
|
||||
;; 9e 8c ldd r9, Y+30
|
||||
;; 4e 96 adiw r24, 0x1e
|
||||
.section .SIX,"ax",@progbits
|
||||
; HEX-LABEL: section .SIX:
|
||||
; HEX-NEXT: 9e8e9e8c 4e96
|
||||
std Y+b, r9 ; R_AVR_6
|
||||
ldd r9, Y+b ; R_AVR_6
|
||||
adiw r24, b ; R_AVR_6_ADIW
|
||||
|
||||
.section .PORT,"ax",@progbits
|
||||
; CHECK-LABEL: section .PORT:
|
||||
; CHECK: in r20, 0x1e
|
||||
; CHECK-NEXT: sbic 0x1e, 0x1
|
||||
in r20, b ; R_AVR_PORT6
|
||||
sbic b, 1 ; R_AVR_PORT5
|
||||
|
||||
;; The disassembler is not yet able to decode those opcodes
|
||||
;; 0f c0 rjmp .+30
|
||||
;; ee cf rjmp .-36
|
||||
;; 69 f0 breq .+26
|
||||
;; 61 f3 breq .-40
|
||||
.section .PCREL,"ax",@progbits
|
||||
; HEX-LABEL: section .PCREL:
|
||||
; HEX-NEXT: 0fc0eecf 69f061f3
|
||||
foo:
|
||||
rjmp foo + 32 ; R_AVR_13_PCREL
|
||||
rjmp foo - 32 ; R_AVR_13_PCREL
|
||||
breq foo + 32 ; R_AVR_7_PCREL
|
||||
breq foo - 32 ; R_AVR_7_PCREL
|
||||
|
||||
.section .DATA,"ax",@progbits
|
||||
; HEX-LABEL: section .DATA:
|
||||
; HEX-NEXT: {{.*}} 1e1e000f 00785634 12
|
||||
.byte b ; R_AVR_8
|
||||
.short b ; R_AVR_16
|
||||
.short gs(b) ; R_AVR_16_PM
|
||||
.long a ; R_AVR_32
|
Loading…
Reference in New Issue