diff options
Diffstat (limited to 'lld/ELF/Arch/AVR.cpp')
-rw-r--r-- | lld/ELF/Arch/AVR.cpp | 142 |
1 files changed, 130 insertions, 12 deletions
diff --git a/lld/ELF/Arch/AVR.cpp b/lld/ELF/Arch/AVR.cpp index cb33ff448ba4..4513a970b32d 100644 --- a/lld/ELF/Arch/AVR.cpp +++ b/lld/ELF/Arch/AVR.cpp @@ -36,9 +36,8 @@ using namespace llvm; using namespace llvm::object; using namespace llvm::support::endian; using namespace llvm::ELF; - -namespace lld { -namespace elf { +using namespace lld; +using namespace lld::elf; namespace { class AVR final : public TargetInfo { @@ -46,7 +45,8 @@ public: AVR(); RelExpr getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const override; - void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; + void relocate(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; }; } // namespace @@ -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; + } } -void AVR::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { - switch (type) { +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; @@ -67,14 +187,12 @@ void AVR::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { break; } default: - error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); + error(getErrorLocation(loc) + "unrecognized relocation " + + toString(rel.type)); } } -TargetInfo *getAVRTargetInfo() { +TargetInfo *elf::getAVRTargetInfo() { static AVR target; return ⌖ } - -} // namespace elf -} // namespace lld |