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-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // AVR is a Harvard-architecture 8-bit microcontroller designed for small |
10 | // 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 largest |
12 | // 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 load |
15 | // ELF files on AVRs. You are expected to link your program against address |
16 | // 0 and pull out a .text section from the result using objcopy, so that you |
17 | // can write the linked code to on-chip flush memory. You can do that with |
18 | // the following commands: |
19 | // |
20 | // ld.lld -Ttext=0 -o foo foo.o |
21 | // objcopy -O binary --only-section=.text foo output.bin |
22 | // |
23 | // Note that the current AVR support is very preliminary so you can't |
24 | // 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 | |
35 | using namespace llvm; |
36 | using namespace llvm::object; |
37 | using namespace llvm::support::endian; |
38 | using namespace llvm::ELF; |
39 | using namespace lld; |
40 | using namespace lld::elf; |
41 | |
42 | namespace { |
43 | class AVR final : public TargetInfo { |
44 | public: |
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 | } // namespace |
56 | |
57 | RelExpr 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.v |
96 | << ") against symbol " << &s; |
97 | return R_NONE; |
98 | } |
99 | } |
100 | |
101 | static void writeLDI(uint8_t *loc, uint64_t val) { |
102 | write16le(P: loc, V: (read16le(P: loc) & 0xf0f0) | (val & 0xf0) << 4 | (val & 0x0f)); |
103 | } |
104 | |
105 | bool 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 range |
111 | // [0, 0x1ffff]. |
112 | return s.getVA(ctx) >= 0x20000; |
113 | default: |
114 | return false; |
115 | } |
116 | } |
117 | |
118 | void 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, v: val, n: 8, rel); |
122 | *loc = val; |
123 | break; |
124 | case R_AVR_8_LO8: |
125 | checkUInt(ctx, loc, v: val, n: 32, rel); |
126 | *loc = val & 0xff; |
127 | break; |
128 | case R_AVR_8_HI8: |
129 | checkUInt(ctx, loc, v: val, n: 32, rel); |
130 | *loc = (val >> 8) & 0xff; |
131 | break; |
132 | case R_AVR_8_HLO8: |
133 | checkUInt(ctx, loc, v: val, n: 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, which |
138 | // are 0x800000 apart in the output ELF file. The bitmask cuts off the high |
139 | // bit. |
140 | write16le(P: loc, V: val & 0xffff); |
141 | break; |
142 | case R_AVR_16_PM: |
143 | checkAlignment(ctx, loc, v: val, n: 2, rel); |
144 | checkUInt(ctx, loc, v: val >> 1, n: 16, rel); |
145 | write16le(P: loc, V: val >> 1); |
146 | break; |
147 | case R_AVR_32: |
148 | checkUInt(ctx, loc, v: val, n: 32, rel); |
149 | write32le(P: loc, V: val); |
150 | break; |
151 | |
152 | case R_AVR_LDI: |
153 | checkUInt(ctx, loc, v: val, n: 8, rel); |
154 | writeLDI(loc, val: val & 0xff); |
155 | break; |
156 | |
157 | case R_AVR_LO8_LDI_NEG: |
158 | writeLDI(loc, val: -val & 0xff); |
159 | break; |
160 | case R_AVR_LO8_LDI: |
161 | writeLDI(loc, val: val & 0xff); |
162 | break; |
163 | case R_AVR_HI8_LDI_NEG: |
164 | writeLDI(loc, val: (-val >> 8) & 0xff); |
165 | break; |
166 | case R_AVR_HI8_LDI: |
167 | writeLDI(loc, val: (val >> 8) & 0xff); |
168 | break; |
169 | case R_AVR_HH8_LDI_NEG: |
170 | writeLDI(loc, val: (-val >> 16) & 0xff); |
171 | break; |
172 | case R_AVR_HH8_LDI: |
173 | writeLDI(loc, val: (val >> 16) & 0xff); |
174 | break; |
175 | case R_AVR_MS8_LDI_NEG: |
176 | writeLDI(loc, val: (-val >> 24) & 0xff); |
177 | break; |
178 | case R_AVR_MS8_LDI: |
179 | writeLDI(loc, val: (val >> 24) & 0xff); |
180 | break; |
181 | |
182 | case R_AVR_LO8_LDI_GS: |
183 | checkUInt(ctx, loc, v: val, n: 17, rel); |
184 | [[fallthrough]]; |
185 | case R_AVR_LO8_LDI_PM: |
186 | checkAlignment(ctx, loc, v: val, n: 2, rel); |
187 | writeLDI(loc, val: (val >> 1) & 0xff); |
188 | break; |
189 | case R_AVR_HI8_LDI_GS: |
190 | checkUInt(ctx, loc, v: val, n: 17, rel); |
191 | [[fallthrough]]; |
192 | case R_AVR_HI8_LDI_PM: |
193 | checkAlignment(ctx, loc, v: val, n: 2, rel); |
194 | writeLDI(loc, val: (val >> 9) & 0xff); |
195 | break; |
196 | case R_AVR_HH8_LDI_PM: |
197 | checkAlignment(ctx, loc, v: val, n: 2, rel); |
198 | writeLDI(loc, val: (val >> 17) & 0xff); |
199 | break; |
200 | |
201 | case R_AVR_LO8_LDI_PM_NEG: |
202 | checkAlignment(ctx, loc, v: val, n: 2, rel); |
203 | writeLDI(loc, val: (-val >> 1) & 0xff); |
204 | break; |
205 | case R_AVR_HI8_LDI_PM_NEG: |
206 | checkAlignment(ctx, loc, v: val, n: 2, rel); |
207 | writeLDI(loc, val: (-val >> 9) & 0xff); |
208 | break; |
209 | case R_AVR_HH8_LDI_PM_NEG: |
210 | checkAlignment(ctx, loc, v: val, n: 2, rel); |
211 | writeLDI(loc, val: (-val >> 17) & 0xff); |
212 | break; |
213 | |
214 | case R_AVR_LDS_STS_16: { |
215 | checkUInt(ctx, loc, v: val, n: 7, rel); |
216 | const uint16_t hi = val >> 4; |
217 | const uint16_t lo = val & 0xf; |
218 | write16le(P: loc, V: (read16le(P: loc) & 0xf8f0) | ((hi << 8) | lo)); |
219 | break; |
220 | } |
221 | |
222 | case R_AVR_PORT5: |
223 | checkUInt(ctx, loc, v: val, n: 5, rel); |
224 | write16le(P: loc, V: (read16le(P: loc) & 0xff07) | (val << 3)); |
225 | break; |
226 | case R_AVR_PORT6: |
227 | checkUInt(ctx, loc, v: val, n: 6, rel); |
228 | write16le(P: loc, V: (read16le(P: loc) & 0xf9f0) | (val & 0x30) << 5 | (val & 0x0f)); |
229 | break; |
230 | |
231 | // Since every jump destination is word aligned we gain an extra bit |
232 | case R_AVR_7_PCREL: { |
233 | checkInt(ctx, loc, v: val - 2, n: 8, rel); |
234 | checkAlignment(ctx, loc, v: val, n: 2, rel); |
235 | const uint16_t target = (val - 2) >> 1; |
236 | write16le(P: loc, V: (read16le(P: loc) & 0xfc07) | ((target & 0x7f) << 3)); |
237 | break; |
238 | } |
239 | case R_AVR_13_PCREL: { |
240 | checkAlignment(ctx, loc, v: val, n: 2, rel); |
241 | const uint16_t target = (val - 2) >> 1; |
242 | write16le(P: loc, V: (read16le(P: loc) & 0xf000) | (target & 0xfff)); |
243 | break; |
244 | } |
245 | |
246 | case R_AVR_6: |
247 | checkInt(ctx, loc, v: val, n: 6, rel); |
248 | write16le(P: loc, V: (read16le(P: loc) & 0xd3f8) | (val & 0x20) << 8 | |
249 | (val & 0x18) << 7 | (val & 0x07)); |
250 | break; |
251 | case R_AVR_6_ADIW: |
252 | checkInt(ctx, loc, v: val, n: 6, rel); |
253 | write16le(P: loc, V: (read16le(P: loc) & 0xff30) | (val & 0x30) << 2 | (val & 0x0F)); |
254 | break; |
255 | |
256 | case R_AVR_CALL: { |
257 | checkAlignment(ctx, loc, v: val, n: 2, rel); |
258 | uint16_t hi = val >> 17; |
259 | uint16_t lo = val >> 1; |
260 | write16le(P: loc, V: read16le(P: loc) | ((hi >> 1) << 4) | (hi & 1)); |
261 | write16le(P: loc + 2, V: lo); |
262 | break; |
263 | } |
264 | default: |
265 | llvm_unreachable("unknown relocation" ); |
266 | } |
267 | } |
268 | |
269 | void elf::setAVRTargetInfo(Ctx &ctx) { ctx.target.reset(p: new AVR(ctx)); } |
270 | |
271 | static uint32_t getEFlags(InputFile *file) { |
272 | return cast<ObjFile<ELF32LE>>(Val: file)->getObj().getHeader().e_flags; |
273 | } |
274 | |
275 | uint32_t AVR::calcEFlags() const { |
276 | assert(!ctx.objectFiles.empty()); |
277 | |
278 | uint32_t flags = getEFlags(file: ctx.objectFiles[0]); |
279 | bool hasLinkRelaxFlag = flags & EF_AVR_LINKRELAX_PREPARED; |
280 | |
281 | for (InputFile *f : ArrayRef(ctx.objectFiles).slice(N: 1)) { |
282 | uint32_t objFlags = getEFlags(file: 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 | |