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 "lld/Common/ErrorHandler.h" |
33 | #include "llvm/BinaryFormat/ELF.h" |
34 | #include "llvm/Support/Endian.h" |
35 | |
36 | using namespace llvm; |
37 | using namespace llvm::object; |
38 | using namespace llvm::support::endian; |
39 | using namespace llvm::ELF; |
40 | using namespace lld; |
41 | using namespace lld::elf; |
42 | |
43 | namespace { |
44 | class AVR final : public TargetInfo { |
45 | public: |
46 | AVR() { needsThunks = true; } |
47 | uint32_t calcEFlags() const override; |
48 | RelExpr getRelExpr(RelType type, const Symbol &s, |
49 | const uint8_t *loc) const override; |
50 | bool needsThunk(RelExpr expr, RelType type, const InputFile *file, |
51 | uint64_t branchAddr, const Symbol &s, |
52 | int64_t a) const override; |
53 | void relocate(uint8_t *loc, const Relocation &rel, |
54 | uint64_t val) const override; |
55 | }; |
56 | } // namespace |
57 | |
58 | RelExpr AVR::getRelExpr(RelType type, const Symbol &s, |
59 | const uint8_t *loc) const { |
60 | switch (type) { |
61 | case R_AVR_6: |
62 | case R_AVR_6_ADIW: |
63 | case R_AVR_8: |
64 | case R_AVR_8_LO8: |
65 | case R_AVR_8_HI8: |
66 | case R_AVR_8_HLO8: |
67 | case R_AVR_16: |
68 | case R_AVR_16_PM: |
69 | case R_AVR_32: |
70 | case R_AVR_LDI: |
71 | case R_AVR_LO8_LDI: |
72 | case R_AVR_LO8_LDI_NEG: |
73 | case R_AVR_HI8_LDI: |
74 | case R_AVR_HI8_LDI_NEG: |
75 | case R_AVR_HH8_LDI_NEG: |
76 | case R_AVR_HH8_LDI: |
77 | case R_AVR_MS8_LDI_NEG: |
78 | case R_AVR_MS8_LDI: |
79 | case R_AVR_LO8_LDI_GS: |
80 | case R_AVR_LO8_LDI_PM: |
81 | case R_AVR_LO8_LDI_PM_NEG: |
82 | case R_AVR_HI8_LDI_GS: |
83 | case R_AVR_HI8_LDI_PM: |
84 | case R_AVR_HI8_LDI_PM_NEG: |
85 | case R_AVR_HH8_LDI_PM: |
86 | case R_AVR_HH8_LDI_PM_NEG: |
87 | case R_AVR_LDS_STS_16: |
88 | case R_AVR_PORT5: |
89 | case R_AVR_PORT6: |
90 | case R_AVR_CALL: |
91 | return R_ABS; |
92 | case R_AVR_7_PCREL: |
93 | case R_AVR_13_PCREL: |
94 | return R_PC; |
95 | default: |
96 | error(msg: getErrorLocation(loc) + "unknown relocation (" + Twine(type) + |
97 | ") against symbol " + toString(s)); |
98 | return R_NONE; |
99 | } |
100 | } |
101 | |
102 | static void writeLDI(uint8_t *loc, uint64_t val) { |
103 | write16le(P: loc, V: (read16le(P: loc) & 0xf0f0) | (val & 0xf0) << 4 | (val & 0x0f)); |
104 | } |
105 | |
106 | bool AVR::needsThunk(RelExpr expr, RelType type, const InputFile *file, |
107 | uint64_t branchAddr, const Symbol &s, int64_t a) const { |
108 | switch (type) { |
109 | case R_AVR_LO8_LDI_GS: |
110 | case R_AVR_HI8_LDI_GS: |
111 | // A thunk is needed if the symbol's virtual address is out of range |
112 | // [0, 0x1ffff]. |
113 | return s.getVA() >= 0x20000; |
114 | default: |
115 | return false; |
116 | } |
117 | } |
118 | |
119 | void AVR::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { |
120 | switch (rel.type) { |
121 | case R_AVR_8: |
122 | checkUInt(loc, v: val, n: 8, rel); |
123 | *loc = val; |
124 | break; |
125 | case R_AVR_8_LO8: |
126 | checkUInt(loc, v: val, n: 32, rel); |
127 | *loc = val & 0xff; |
128 | break; |
129 | case R_AVR_8_HI8: |
130 | checkUInt(loc, v: val, n: 32, rel); |
131 | *loc = (val >> 8) & 0xff; |
132 | break; |
133 | case R_AVR_8_HLO8: |
134 | checkUInt(loc, v: val, n: 32, rel); |
135 | *loc = (val >> 16) & 0xff; |
136 | break; |
137 | case R_AVR_16: |
138 | // Note: this relocation is often used between code and data space, which |
139 | // are 0x800000 apart in the output ELF file. The bitmask cuts off the high |
140 | // bit. |
141 | write16le(P: loc, V: val & 0xffff); |
142 | break; |
143 | case R_AVR_16_PM: |
144 | checkAlignment(loc, v: val, n: 2, rel); |
145 | checkUInt(loc, v: val >> 1, n: 16, rel); |
146 | write16le(P: loc, V: val >> 1); |
147 | break; |
148 | case R_AVR_32: |
149 | checkUInt(loc, v: val, n: 32, rel); |
150 | write32le(P: loc, V: val); |
151 | break; |
152 | |
153 | case R_AVR_LDI: |
154 | checkUInt(loc, v: val, n: 8, rel); |
155 | writeLDI(loc, val: val & 0xff); |
156 | break; |
157 | |
158 | case R_AVR_LO8_LDI_NEG: |
159 | writeLDI(loc, val: -val & 0xff); |
160 | break; |
161 | case R_AVR_LO8_LDI: |
162 | writeLDI(loc, val: val & 0xff); |
163 | break; |
164 | case R_AVR_HI8_LDI_NEG: |
165 | writeLDI(loc, val: (-val >> 8) & 0xff); |
166 | break; |
167 | case R_AVR_HI8_LDI: |
168 | writeLDI(loc, val: (val >> 8) & 0xff); |
169 | break; |
170 | case R_AVR_HH8_LDI_NEG: |
171 | writeLDI(loc, val: (-val >> 16) & 0xff); |
172 | break; |
173 | case R_AVR_HH8_LDI: |
174 | writeLDI(loc, val: (val >> 16) & 0xff); |
175 | break; |
176 | case R_AVR_MS8_LDI_NEG: |
177 | writeLDI(loc, val: (-val >> 24) & 0xff); |
178 | break; |
179 | case R_AVR_MS8_LDI: |
180 | writeLDI(loc, val: (val >> 24) & 0xff); |
181 | break; |
182 | |
183 | case R_AVR_LO8_LDI_GS: |
184 | checkUInt(loc, v: val, n: 17, rel); |
185 | [[fallthrough]]; |
186 | case R_AVR_LO8_LDI_PM: |
187 | checkAlignment(loc, v: val, n: 2, rel); |
188 | writeLDI(loc, val: (val >> 1) & 0xff); |
189 | break; |
190 | case R_AVR_HI8_LDI_GS: |
191 | checkUInt(loc, v: val, n: 17, rel); |
192 | [[fallthrough]]; |
193 | case R_AVR_HI8_LDI_PM: |
194 | checkAlignment(loc, v: val, n: 2, rel); |
195 | writeLDI(loc, val: (val >> 9) & 0xff); |
196 | break; |
197 | case R_AVR_HH8_LDI_PM: |
198 | checkAlignment(loc, v: val, n: 2, rel); |
199 | writeLDI(loc, val: (val >> 17) & 0xff); |
200 | break; |
201 | |
202 | case R_AVR_LO8_LDI_PM_NEG: |
203 | checkAlignment(loc, v: val, n: 2, rel); |
204 | writeLDI(loc, val: (-val >> 1) & 0xff); |
205 | break; |
206 | case R_AVR_HI8_LDI_PM_NEG: |
207 | checkAlignment(loc, v: val, n: 2, rel); |
208 | writeLDI(loc, val: (-val >> 9) & 0xff); |
209 | break; |
210 | case R_AVR_HH8_LDI_PM_NEG: |
211 | checkAlignment(loc, v: val, n: 2, rel); |
212 | writeLDI(loc, val: (-val >> 17) & 0xff); |
213 | break; |
214 | |
215 | case R_AVR_LDS_STS_16: { |
216 | checkUInt(loc, v: val, n: 7, rel); |
217 | const uint16_t hi = val >> 4; |
218 | const uint16_t lo = val & 0xf; |
219 | write16le(P: loc, V: (read16le(P: loc) & 0xf8f0) | ((hi << 8) | lo)); |
220 | break; |
221 | } |
222 | |
223 | case R_AVR_PORT5: |
224 | checkUInt(loc, v: val, n: 5, rel); |
225 | write16le(P: loc, V: (read16le(P: loc) & 0xff07) | (val << 3)); |
226 | break; |
227 | case R_AVR_PORT6: |
228 | checkUInt(loc, v: val, n: 6, rel); |
229 | write16le(P: loc, V: (read16le(P: loc) & 0xf9f0) | (val & 0x30) << 5 | (val & 0x0f)); |
230 | break; |
231 | |
232 | // Since every jump destination is word aligned we gain an extra bit |
233 | case R_AVR_7_PCREL: { |
234 | checkInt(loc, v: val - 2, n: 7, rel); |
235 | checkAlignment(loc, v: val, n: 2, rel); |
236 | const uint16_t target = (val - 2) >> 1; |
237 | write16le(P: loc, V: (read16le(P: loc) & 0xfc07) | ((target & 0x7f) << 3)); |
238 | break; |
239 | } |
240 | case R_AVR_13_PCREL: { |
241 | checkInt(loc, v: val - 2, n: 13, rel); |
242 | checkAlignment(loc, v: val, n: 2, rel); |
243 | const uint16_t target = (val - 2) >> 1; |
244 | write16le(P: loc, V: (read16le(P: loc) & 0xf000) | (target & 0xfff)); |
245 | break; |
246 | } |
247 | |
248 | case R_AVR_6: |
249 | checkInt(loc, v: val, n: 6, rel); |
250 | write16le(P: loc, V: (read16le(P: loc) & 0xd3f8) | (val & 0x20) << 8 | |
251 | (val & 0x18) << 7 | (val & 0x07)); |
252 | break; |
253 | case R_AVR_6_ADIW: |
254 | checkInt(loc, v: val, n: 6, rel); |
255 | write16le(P: loc, V: (read16le(P: loc) & 0xff30) | (val & 0x30) << 2 | (val & 0x0F)); |
256 | break; |
257 | |
258 | case R_AVR_CALL: { |
259 | checkAlignment(loc, v: val, n: 2, rel); |
260 | uint16_t hi = val >> 17; |
261 | uint16_t lo = val >> 1; |
262 | write16le(P: loc, V: read16le(P: loc) | ((hi >> 1) << 4) | (hi & 1)); |
263 | write16le(P: loc + 2, V: lo); |
264 | break; |
265 | } |
266 | default: |
267 | llvm_unreachable("unknown relocation" ); |
268 | } |
269 | } |
270 | |
271 | TargetInfo *elf::getAVRTargetInfo() { |
272 | static AVR target; |
273 | return ⌖ |
274 | } |
275 | |
276 | static uint32_t getEFlags(InputFile *file) { |
277 | return cast<ObjFile<ELF32LE>>(Val: file)->getObj().getHeader().e_flags; |
278 | } |
279 | |
280 | uint32_t AVR::calcEFlags() const { |
281 | assert(!ctx.objectFiles.empty()); |
282 | |
283 | uint32_t flags = getEFlags(file: ctx.objectFiles[0]); |
284 | bool hasLinkRelaxFlag = flags & EF_AVR_LINKRELAX_PREPARED; |
285 | |
286 | for (InputFile *f : ArrayRef(ctx.objectFiles).slice(N: 1)) { |
287 | uint32_t objFlags = getEFlags(file: f); |
288 | if ((objFlags & EF_AVR_ARCH_MASK) != (flags & EF_AVR_ARCH_MASK)) |
289 | error(msg: toString(f) + |
290 | ": cannot link object files with incompatible target ISA" ); |
291 | if (!(objFlags & EF_AVR_LINKRELAX_PREPARED)) |
292 | hasLinkRelaxFlag = false; |
293 | } |
294 | |
295 | if (!hasLinkRelaxFlag) |
296 | flags &= ~EF_AVR_LINKRELAX_PREPARED; |
297 | |
298 | return flags; |
299 | } |
300 | |