Index: ELF/Arch/AVR.cpp =================================================================== --- ELF/Arch/AVR.cpp +++ ELF/Arch/AVR.cpp @@ -35,12 +35,30 @@ using namespace llvm; using namespace llvm::object; +using namespace llvm::support; using namespace llvm::support::endian; using namespace llvm::ELF; using namespace lld; using namespace lld::elf; namespace { +uint16_t calculateForLDI(uint16_t X, uint16_t Val) { + return (X & 0xf0f0) | (Val & 0xf) | ((Val << 4) & 0xf00); +} + +// All PC-relative instructions need to be adjusted by -2 IIRC to account for +// the size of the current instruction. +void adjustRelativeBranch(uint16_t &Val) { + Val -= 2; // Branch instructions add 2 to the PC. +} + +// Adjusts a program memory address. This is a simple right-shift. +template void adjustPM(T &Val) { + Val >>= 1; // AVR addresses commands as words. +} + +void adjustNeg(int16_t &Val) { Val *= -1; } + class AVR final : public TargetInfo { public: RelExpr getRelExpr(uint32_t Type, const SymbolBody &S, const InputFile &File, @@ -52,7 +70,38 @@ RelExpr AVR::getRelExpr(uint32_t Type, const SymbolBody &S, const InputFile &File, const uint8_t *Loc) const { switch (Type) { + case R_AVR_7_PCREL: + case R_AVR_13_PCREL: + return R_PC; + case R_AVR_LO8_LDI: + case R_AVR_LDI: + case R_AVR_6: + case R_AVR_6_ADIW: + case R_AVR_HI8_LDI: + case R_AVR_HH8_LDI: + case R_AVR_MS8_LDI: + case R_AVR_LO8_LDI_NEG: + case R_AVR_HI8_LDI_NEG: + case R_AVR_HH8_LDI_NEG: + case R_AVR_MS8_LDI_NEG: + case R_AVR_LO8_LDI_GS: + case R_AVR_LO8_LDI_PM: + case R_AVR_HI8_LDI_GS: + case R_AVR_HI8_LDI_PM: + case R_AVR_HH8_LDI_PM: + case R_AVR_LO8_LDI_PM_NEG: + case R_AVR_HI8_LDI_PM_NEG: + case R_AVR_HH8_LDI_PM_NEG: + case R_AVR_8: + case R_AVR_8_LO8: + case R_AVR_8_HI8: + case R_AVR_8_HLO8: case R_AVR_CALL: + case R_AVR_16: + case R_AVR_16_PM: + case R_AVR_LDS_STS_16: + case R_AVR_PORT6: + case R_AVR_PORT5: return R_ABS; default: error(toString(&File) + ": unknown relocation type: " + toString(Type)); @@ -61,14 +110,149 @@ } void AVR::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const { + uint16_t SRel = Val; + uint16_t X = read16le(Loc); switch (Type) { - case R_AVR_CALL: { - uint16_t Hi = Val >> 17; - uint16_t Lo = Val >> 1; - write16le(Loc, read16le(Loc) | ((Hi >> 1) << 4) | (Hi & 1)); - write16le(Loc + 2, Lo); + case R_AVR_7_PCREL: + adjustRelativeBranch(SRel); + X = (X & 0xfc07) | (((SRel >> 1) << 3) & 0x3f8); + write16le(Loc, X); + break; + case R_AVR_13_PCREL: + adjustRelativeBranch(SRel); + adjustPM(SRel); + X = (X & 0xf000) | (SRel & 0xfff); + write16le(Loc, X); + break; + case R_AVR_LO8_LDI: + case R_AVR_LDI: + write16le(Loc, calculateForLDI(X, SRel)); + break; + case R_AVR_6: + X = (X & 0xd3f8) | ((SRel & 7) | ((SRel & (3 << 3)) << 7) + | ((SRel & (1 << 5)) << 8)); + write16le(Loc, X); + break; + case R_AVR_6_ADIW: + X = (X & 0xff30) | (SRel & 0xf) | ((SRel & 0x30) << 2); + write16le(Loc, X); + break; + case R_AVR_HI8_LDI: + SRel = (SRel >> 8) & 0xff; + write16le(Loc, calculateForLDI(X, SRel)); + break; + case R_AVR_HH8_LDI: + SRel = (SRel >> 16) & 0xff; + write16le(Loc, calculateForLDI(X, SRel)); + break; + case R_AVR_MS8_LDI: + SRel = (SRel >> 24) & 0xff; + write16le(Loc, calculateForLDI(X, SRel)); + break; + case R_AVR_LO8_LDI_NEG: { + int16_t SRel = Val; + adjustNeg(SRel); + write16le(Loc, calculateForLDI(X, (uint16_t)SRel)); + break; + } + case R_AVR_HI8_LDI_NEG: { + int16_t SRel = Val; + adjustNeg(SRel); + SRel = (SRel >> 8) & 0xff; + write16le(Loc, calculateForLDI(X, (uint16_t)SRel)); + break; + } + case R_AVR_HH8_LDI_NEG: { + int16_t SRel = Val; + adjustNeg(SRel); + SRel = (SRel >> 16) & 0xff; + write16le(Loc, calculateForLDI(X, (uint16_t)SRel)); + break; + } + case R_AVR_MS8_LDI_NEG: { + int16_t SRel = Val; + adjustNeg(SRel); + SRel = (SRel >> 24) & 0xff; + write16le(Loc, calculateForLDI(X, (uint16_t)SRel)); break; } + case R_AVR_LO8_LDI_GS: + case R_AVR_LO8_LDI_PM: + adjustPM(SRel); + write16le(Loc, calculateForLDI(X, SRel)); + break; + case R_AVR_HI8_LDI_GS: + case R_AVR_HI8_LDI_PM: + adjustPM(SRel); + SRel = (SRel >> 8) & 0xff; + write16le(Loc, calculateForLDI(X, SRel)); + break; + case R_AVR_HH8_LDI_PM: + adjustPM(SRel); + SRel = (SRel >> 16) & 0xff; + write16le(Loc, calculateForLDI(X, SRel)); + break; + case R_AVR_LO8_LDI_PM_NEG: { + int16_t SRel = Val; + adjustNeg(SRel); + adjustPM(SRel); + write16le(Loc, calculateForLDI(X, (uint16_t)SRel)); + break; + } + case R_AVR_HI8_LDI_PM_NEG: { + int16_t SRel = Val; + adjustNeg(SRel); + adjustPM(SRel); + SRel = (SRel >> 8) & 0xff; + write16le(Loc, calculateForLDI(X, (uint16_t)SRel)); + break; + } + case R_AVR_HH8_LDI_PM_NEG: { + int16_t SRel = Val; + adjustNeg(SRel); + adjustPM(SRel); + SRel = (SRel >> 16) & 0xff; + write16le(Loc, calculateForLDI(X, (uint16_t)SRel)); + break; + } + case R_AVR_8: + write16le(Loc, SRel & 0x000000ff); + break; + case R_AVR_8_LO8: + write16le(Loc, SRel & 0xffffff); + break; + case R_AVR_8_HI8: + write16le(Loc, (SRel >> 8) & 0xffffff); + break; + case R_AVR_8_HLO8: + write16le(Loc, (SRel >> 16) & 0xffffff); + break; + case R_AVR_CALL: + adjustPM(SRel); + X |= ((SRel & 0x10000) | ((SRel << 3) & 0x1f00000)) >> 16; + write16le(Loc, X); + write16le(Loc + 2, SRel & 0xffff); + break; + case R_AVR_16: + write16le(Loc, SRel & 0x00ffff); + break; + case R_AVR_16_PM: + adjustPM(SRel); + write16le(Loc, SRel & 0x00ffff); + break; + case R_AVR_LDS_STS_16: + SRel = SRel & 0x7f; + X |= (SRel & 0x0f) | ((SRel & 0x30) << 5) | ((SRel & 0x40) << 2); + write16le(Loc, X); + break; + case R_AVR_PORT6: + X = (X & 0xf9f0) | ((SRel & 0x30) << 5) | (SRel & 0x0f); + write16le(Loc, X); + break; + case R_AVR_PORT5: + X = (X & 0xff07) | ((SRel & 0x1f) << 3); + write16le(Loc, X); + break; default: error(getErrorLocation(Loc) + "unrecognized reloc " + toString(Type)); } Index: test/ELF/basic-avr.s =================================================================== --- test/ELF/basic-avr.s +++ test/ELF/basic-avr.s @@ -1,14 +1,77 @@ # REQUIRES: avr # RUN: llvm-mc -filetype=obj -triple=avr-unknown-linux -mcpu=atmega328p %s -o %t.o -# RUN: ld.lld %t.o -o %t.exe -Ttext=0 +# RUN: ld.lld %t.o -o %t.exe -Ttext=6 # RUN: llvm-objdump -d %t.exe | FileCheck %s main: call foo + .word gs(foo) + .byte foo + .byte lo8(foo) + .byte hi8(foo) + .byte hlo8(foo) + ldi r16, lo8(foo) + ldi r17, hi8(foo) + ldi r18, hh8(foo) + ldi r19, hhi8(foo) + ldi r20, foo + ldi r21, lo8(-(foo)) + ldi r22, hi8(-(foo)) + ldi r23, hh8(-(foo)) + ldi r24, hhi8(-(foo)) + ldi r25, pm_lo8(foo) + ldi r26, pm_hi8(foo) + ldi r27, pm_hh8(foo) + ldi r28, pm_lo8(-(foo)) + ldi r29, pm_hi8(-(foo)) + ldi r30, pm_hh8(-(foo)) + brne foo + rjmp foo foo: - jmp foo + ldd r2, Y+2 + ldd r0, Y+0 + ldd r9, Z+12 + ldd r7, Z+30 + ldd r9, Z+foo + lds r16, foo + ldi r17, lo8(gs(foo)) + ldi r18, hi8(gs(foo)) + in r20, foo+1 + sbi main, 0 + adiw r30, foo + .word gs(foo) # CHECK: main: -# CHECK-NEXT: 0: 0e 94 02 00 +# CHECK-NEXT: 6: 0e 94 19 00 +# CHECK-NEXT: a: 19 00 +# CHECK-NEXT: c: 32 32 +# CHECK-NEXT: e: 00 00 +# CHECK-NEXT: 10: 02 e3 +# CHECK-NEXT: 12: 10 e0 +# CHECK-NEXT: 14: 20 e0 +# CHECK-NEXT: 16: 30 e0 +# CHECK-NEXT: 18: 42 e3 +# CHECK-NEXT: 1a: 5e ec +# CHECK-NEXT: 1c: 6f ef +# CHECK-NEXT: 1e: 7f ef +# CHECK-NEXT: 20: 8f ef +# CHECK-NEXT: 22: 99 e1 +# CHECK-NEXT: 24: a0 e0 +# CHECK-NEXT: 26: b0 e0 +# CHECK-NEXT: 28: c7 ee +# CHECK-NEXT: 2a: df ef +# CHECK-NEXT: 2c: ef ef +# CHECK-NEXT: 2e: 09 f4 +# CHECK-NEXT: 30: 00 c0 # CHECK: foo: -# CHECK-NEXT: 4: 0c 94 02 00 +# CHECK-NEXT: 32: 2a 80 +# CHECK-NEXT: 34: 08 80 +# CHECK-NEXT: 36: 94 84 +# CHECK-NEXT: 38: 76 8c +# CHECK-NEXT: 3a: 92 a8 +# CHECK-NEXT: 3c: 00 91 32 00 +# CHECK-NEXT: 40: 19 e1 +# CHECK-NEXT: 42: 20 e0 +# CHECK-NEXT: 44: 43 b7 +# CHECK-NEXT: 46: 30 9a +# CHECK-NEXT: 48: f2 96