295 lines · cpp
1//===- AVR.cpp ------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// AVR is a Harvard-architecture 8-bit microcontroller designed for small10// baremetal programs. All AVR-family processors have 32 8-bit registers.11// The tiniest AVR has 32 byte RAM and 1 KiB program memory, and the largest12// one supports up to 2^24 data address space and 2^22 code address space.13//14// Since it is a baremetal programming, there's usually no loader to load15// ELF files on AVRs. You are expected to link your program against address16// 0 and pull out a .text section from the result using objcopy, so that you17// can write the linked code to on-chip flush memory. You can do that with18// the following commands:19//20// ld.lld -Ttext=0 -o foo foo.o21// objcopy -O binary --only-section=.text foo output.bin22//23// Note that the current AVR support is very preliminary so you can't24// link any useful program yet, though.25//26//===----------------------------------------------------------------------===//27 28#include "InputFiles.h"29#include "Symbols.h"30#include "Target.h"31#include "Thunks.h"32#include "llvm/BinaryFormat/ELF.h"33#include "llvm/Support/Endian.h"34 35using namespace llvm;36using namespace llvm::object;37using namespace llvm::support::endian;38using namespace llvm::ELF;39using namespace lld;40using namespace lld::elf;41 42namespace {43class AVR final : public TargetInfo {44public:45 AVR(Ctx &ctx) : TargetInfo(ctx) { needsThunks = true; }46 uint32_t calcEFlags() const override;47 RelExpr getRelExpr(RelType type, const Symbol &s,48 const uint8_t *loc) const override;49 bool needsThunk(RelExpr expr, RelType type, const InputFile *file,50 uint64_t branchAddr, const Symbol &s,51 int64_t a) const override;52 void relocate(uint8_t *loc, const Relocation &rel,53 uint64_t val) const override;54};55} // namespace56 57RelExpr AVR::getRelExpr(RelType type, const Symbol &s,58 const uint8_t *loc) const {59 switch (type) {60 case R_AVR_6:61 case R_AVR_6_ADIW:62 case R_AVR_8:63 case R_AVR_8_LO8:64 case R_AVR_8_HI8:65 case R_AVR_8_HLO8:66 case R_AVR_16:67 case R_AVR_16_PM:68 case R_AVR_32:69 case R_AVR_LDI:70 case R_AVR_LO8_LDI:71 case R_AVR_LO8_LDI_NEG:72 case R_AVR_HI8_LDI:73 case R_AVR_HI8_LDI_NEG:74 case R_AVR_HH8_LDI_NEG:75 case R_AVR_HH8_LDI:76 case R_AVR_MS8_LDI_NEG:77 case R_AVR_MS8_LDI:78 case R_AVR_LO8_LDI_GS:79 case R_AVR_LO8_LDI_PM:80 case R_AVR_LO8_LDI_PM_NEG:81 case R_AVR_HI8_LDI_GS:82 case R_AVR_HI8_LDI_PM:83 case R_AVR_HI8_LDI_PM_NEG:84 case R_AVR_HH8_LDI_PM:85 case R_AVR_HH8_LDI_PM_NEG:86 case R_AVR_LDS_STS_16:87 case R_AVR_PORT5:88 case R_AVR_PORT6:89 case R_AVR_CALL:90 return R_ABS;91 case R_AVR_7_PCREL:92 case R_AVR_13_PCREL:93 return R_PC;94 default:95 Err(ctx) << getErrorLoc(ctx, loc) << "unknown relocation (" << type.v96 << ") against symbol " << &s;97 return R_NONE;98 }99}100 101static void writeLDI(uint8_t *loc, uint64_t val) {102 write16le(loc, (read16le(loc) & 0xf0f0) | (val & 0xf0) << 4 | (val & 0x0f));103}104 105bool AVR::needsThunk(RelExpr expr, RelType type, const InputFile *file,106 uint64_t branchAddr, const Symbol &s, int64_t a) const {107 switch (type) {108 case R_AVR_LO8_LDI_GS:109 case R_AVR_HI8_LDI_GS:110 // A thunk is needed if the symbol's virtual address is out of range111 // [0, 0x1ffff].112 return s.getVA(ctx) >= 0x20000;113 default:114 return false;115 }116}117 118void AVR::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {119 switch (rel.type) {120 case R_AVR_8:121 checkUInt(ctx, loc, val, 8, rel);122 *loc = val;123 break;124 case R_AVR_8_LO8:125 checkUInt(ctx, loc, val, 32, rel);126 *loc = val & 0xff;127 break;128 case R_AVR_8_HI8:129 checkUInt(ctx, loc, val, 32, rel);130 *loc = (val >> 8) & 0xff;131 break;132 case R_AVR_8_HLO8:133 checkUInt(ctx, loc, val, 32, rel);134 *loc = (val >> 16) & 0xff;135 break;136 case R_AVR_16:137 // Note: this relocation is often used between code and data space, which138 // are 0x800000 apart in the output ELF file. The bitmask cuts off the high139 // bit.140 write16le(loc, val & 0xffff);141 break;142 case R_AVR_16_PM:143 checkAlignment(ctx, loc, val, 2, rel);144 checkUInt(ctx, loc, val >> 1, 16, rel);145 write16le(loc, val >> 1);146 break;147 case R_AVR_32:148 checkUInt(ctx, loc, val, 32, rel);149 write32le(loc, val);150 break;151 152 case R_AVR_LDI:153 checkUInt(ctx, loc, val, 8, rel);154 writeLDI(loc, val & 0xff);155 break;156 157 case R_AVR_LO8_LDI_NEG:158 writeLDI(loc, -val & 0xff);159 break;160 case R_AVR_LO8_LDI:161 writeLDI(loc, val & 0xff);162 break;163 case R_AVR_HI8_LDI_NEG:164 writeLDI(loc, (-val >> 8) & 0xff);165 break;166 case R_AVR_HI8_LDI:167 writeLDI(loc, (val >> 8) & 0xff);168 break;169 case R_AVR_HH8_LDI_NEG:170 writeLDI(loc, (-val >> 16) & 0xff);171 break;172 case R_AVR_HH8_LDI:173 writeLDI(loc, (val >> 16) & 0xff);174 break;175 case R_AVR_MS8_LDI_NEG:176 writeLDI(loc, (-val >> 24) & 0xff);177 break;178 case R_AVR_MS8_LDI:179 writeLDI(loc, (val >> 24) & 0xff);180 break;181 182 case R_AVR_LO8_LDI_GS:183 checkUInt(ctx, loc, val, 17, rel);184 [[fallthrough]];185 case R_AVR_LO8_LDI_PM:186 checkAlignment(ctx, loc, val, 2, rel);187 writeLDI(loc, (val >> 1) & 0xff);188 break;189 case R_AVR_HI8_LDI_GS:190 checkUInt(ctx, loc, val, 17, rel);191 [[fallthrough]];192 case R_AVR_HI8_LDI_PM:193 checkAlignment(ctx, loc, val, 2, rel);194 writeLDI(loc, (val >> 9) & 0xff);195 break;196 case R_AVR_HH8_LDI_PM:197 checkAlignment(ctx, loc, val, 2, rel);198 writeLDI(loc, (val >> 17) & 0xff);199 break;200 201 case R_AVR_LO8_LDI_PM_NEG:202 checkAlignment(ctx, loc, val, 2, rel);203 writeLDI(loc, (-val >> 1) & 0xff);204 break;205 case R_AVR_HI8_LDI_PM_NEG:206 checkAlignment(ctx, loc, val, 2, rel);207 writeLDI(loc, (-val >> 9) & 0xff);208 break;209 case R_AVR_HH8_LDI_PM_NEG:210 checkAlignment(ctx, loc, val, 2, rel);211 writeLDI(loc, (-val >> 17) & 0xff);212 break;213 214 case R_AVR_LDS_STS_16: {215 checkUInt(ctx, loc, val, 7, rel);216 const uint16_t hi = val >> 4;217 const uint16_t lo = val & 0xf;218 write16le(loc, (read16le(loc) & 0xf8f0) | ((hi << 8) | lo));219 break;220 }221 222 case R_AVR_PORT5:223 checkUInt(ctx, loc, val, 5, rel);224 write16le(loc, (read16le(loc) & 0xff07) | (val << 3));225 break;226 case R_AVR_PORT6:227 checkUInt(ctx, loc, val, 6, rel);228 write16le(loc, (read16le(loc) & 0xf9f0) | (val & 0x30) << 5 | (val & 0x0f));229 break;230 231 // Since every jump destination is word aligned we gain an extra bit232 case R_AVR_7_PCREL: {233 checkInt(ctx, loc, val - 2, 8, rel);234 checkAlignment(ctx, loc, val, 2, rel);235 const uint16_t target = (val - 2) >> 1;236 write16le(loc, (read16le(loc) & 0xfc07) | ((target & 0x7f) << 3));237 break;238 }239 case R_AVR_13_PCREL: {240 checkAlignment(ctx, loc, val, 2, rel);241 const uint16_t target = (val - 2) >> 1;242 write16le(loc, (read16le(loc) & 0xf000) | (target & 0xfff));243 break;244 }245 246 case R_AVR_6:247 checkInt(ctx, loc, val, 6, rel);248 write16le(loc, (read16le(loc) & 0xd3f8) | (val & 0x20) << 8 |249 (val & 0x18) << 7 | (val & 0x07));250 break;251 case R_AVR_6_ADIW:252 checkInt(ctx, loc, val, 6, rel);253 write16le(loc, (read16le(loc) & 0xff30) | (val & 0x30) << 2 | (val & 0x0F));254 break;255 256 case R_AVR_CALL: {257 checkAlignment(ctx, loc, val, 2, rel);258 uint16_t hi = val >> 17;259 uint16_t lo = val >> 1;260 write16le(loc, read16le(loc) | ((hi >> 1) << 4) | (hi & 1));261 write16le(loc + 2, lo);262 break;263 }264 default:265 llvm_unreachable("unknown relocation");266 }267}268 269void elf::setAVRTargetInfo(Ctx &ctx) { ctx.target.reset(new AVR(ctx)); }270 271static uint32_t getEFlags(InputFile *file) {272 return cast<ObjFile<ELF32LE>>(file)->getObj().getHeader().e_flags;273}274 275uint32_t AVR::calcEFlags() const {276 assert(!ctx.objectFiles.empty());277 278 uint32_t flags = getEFlags(ctx.objectFiles[0]);279 bool hasLinkRelaxFlag = flags & EF_AVR_LINKRELAX_PREPARED;280 281 for (InputFile *f : ArrayRef(ctx.objectFiles).slice(1)) {282 uint32_t objFlags = getEFlags(f);283 if ((objFlags & EF_AVR_ARCH_MASK) != (flags & EF_AVR_ARCH_MASK))284 ErrAlways(ctx)285 << f << ": cannot link object files with incompatible target ISA";286 if (!(objFlags & EF_AVR_LINKRELAX_PREPARED))287 hasLinkRelaxFlag = false;288 }289 290 if (!hasLinkRelaxFlag)291 flags &= ~EF_AVR_LINKRELAX_PREPARED;292 293 return flags;294}295