Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/lld/ELF/Arch/AVR.cpp
34878 views
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(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(loc, (read16le(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, val, 8, rel);
123
*loc = val;
124
break;
125
case R_AVR_8_LO8:
126
checkUInt(loc, val, 32, rel);
127
*loc = val & 0xff;
128
break;
129
case R_AVR_8_HI8:
130
checkUInt(loc, val, 32, rel);
131
*loc = (val >> 8) & 0xff;
132
break;
133
case R_AVR_8_HLO8:
134
checkUInt(loc, val, 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(loc, val & 0xffff);
142
break;
143
case R_AVR_16_PM:
144
checkAlignment(loc, val, 2, rel);
145
checkUInt(loc, val >> 1, 16, rel);
146
write16le(loc, val >> 1);
147
break;
148
case R_AVR_32:
149
checkUInt(loc, val, 32, rel);
150
write32le(loc, val);
151
break;
152
153
case R_AVR_LDI:
154
checkUInt(loc, val, 8, rel);
155
writeLDI(loc, val & 0xff);
156
break;
157
158
case R_AVR_LO8_LDI_NEG:
159
writeLDI(loc, -val & 0xff);
160
break;
161
case R_AVR_LO8_LDI:
162
writeLDI(loc, val & 0xff);
163
break;
164
case R_AVR_HI8_LDI_NEG:
165
writeLDI(loc, (-val >> 8) & 0xff);
166
break;
167
case R_AVR_HI8_LDI:
168
writeLDI(loc, (val >> 8) & 0xff);
169
break;
170
case R_AVR_HH8_LDI_NEG:
171
writeLDI(loc, (-val >> 16) & 0xff);
172
break;
173
case R_AVR_HH8_LDI:
174
writeLDI(loc, (val >> 16) & 0xff);
175
break;
176
case R_AVR_MS8_LDI_NEG:
177
writeLDI(loc, (-val >> 24) & 0xff);
178
break;
179
case R_AVR_MS8_LDI:
180
writeLDI(loc, (val >> 24) & 0xff);
181
break;
182
183
case R_AVR_LO8_LDI_GS:
184
checkUInt(loc, val, 17, rel);
185
[[fallthrough]];
186
case R_AVR_LO8_LDI_PM:
187
checkAlignment(loc, val, 2, rel);
188
writeLDI(loc, (val >> 1) & 0xff);
189
break;
190
case R_AVR_HI8_LDI_GS:
191
checkUInt(loc, val, 17, rel);
192
[[fallthrough]];
193
case R_AVR_HI8_LDI_PM:
194
checkAlignment(loc, val, 2, rel);
195
writeLDI(loc, (val >> 9) & 0xff);
196
break;
197
case R_AVR_HH8_LDI_PM:
198
checkAlignment(loc, val, 2, rel);
199
writeLDI(loc, (val >> 17) & 0xff);
200
break;
201
202
case R_AVR_LO8_LDI_PM_NEG:
203
checkAlignment(loc, val, 2, rel);
204
writeLDI(loc, (-val >> 1) & 0xff);
205
break;
206
case R_AVR_HI8_LDI_PM_NEG:
207
checkAlignment(loc, val, 2, rel);
208
writeLDI(loc, (-val >> 9) & 0xff);
209
break;
210
case R_AVR_HH8_LDI_PM_NEG:
211
checkAlignment(loc, val, 2, rel);
212
writeLDI(loc, (-val >> 17) & 0xff);
213
break;
214
215
case R_AVR_LDS_STS_16: {
216
checkUInt(loc, val, 7, rel);
217
const uint16_t hi = val >> 4;
218
const uint16_t lo = val & 0xf;
219
write16le(loc, (read16le(loc) & 0xf8f0) | ((hi << 8) | lo));
220
break;
221
}
222
223
case R_AVR_PORT5:
224
checkUInt(loc, val, 5, rel);
225
write16le(loc, (read16le(loc) & 0xff07) | (val << 3));
226
break;
227
case R_AVR_PORT6:
228
checkUInt(loc, val, 6, rel);
229
write16le(loc, (read16le(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, val - 2, 8, rel);
235
checkAlignment(loc, val, 2, rel);
236
const uint16_t target = (val - 2) >> 1;
237
write16le(loc, (read16le(loc) & 0xfc07) | ((target & 0x7f) << 3));
238
break;
239
}
240
case R_AVR_13_PCREL: {
241
checkAlignment(loc, val, 2, rel);
242
const uint16_t target = (val - 2) >> 1;
243
write16le(loc, (read16le(loc) & 0xf000) | (target & 0xfff));
244
break;
245
}
246
247
case R_AVR_6:
248
checkInt(loc, val, 6, rel);
249
write16le(loc, (read16le(loc) & 0xd3f8) | (val & 0x20) << 8 |
250
(val & 0x18) << 7 | (val & 0x07));
251
break;
252
case R_AVR_6_ADIW:
253
checkInt(loc, val, 6, rel);
254
write16le(loc, (read16le(loc) & 0xff30) | (val & 0x30) << 2 | (val & 0x0F));
255
break;
256
257
case R_AVR_CALL: {
258
checkAlignment(loc, val, 2, rel);
259
uint16_t hi = val >> 17;
260
uint16_t lo = val >> 1;
261
write16le(loc, read16le(loc) | ((hi >> 1) << 4) | (hi & 1));
262
write16le(loc + 2, lo);
263
break;
264
}
265
default:
266
llvm_unreachable("unknown relocation");
267
}
268
}
269
270
TargetInfo *elf::getAVRTargetInfo() {
271
static AVR target;
272
return &target;
273
}
274
275
static uint32_t getEFlags(InputFile *file) {
276
return cast<ObjFile<ELF32LE>>(file)->getObj().getHeader().e_flags;
277
}
278
279
uint32_t AVR::calcEFlags() const {
280
assert(!ctx.objectFiles.empty());
281
282
uint32_t flags = getEFlags(ctx.objectFiles[0]);
283
bool hasLinkRelaxFlag = flags & EF_AVR_LINKRELAX_PREPARED;
284
285
for (InputFile *f : ArrayRef(ctx.objectFiles).slice(1)) {
286
uint32_t objFlags = getEFlags(f);
287
if ((objFlags & EF_AVR_ARCH_MASK) != (flags & EF_AVR_ARCH_MASK))
288
error(toString(f) +
289
": cannot link object files with incompatible target ISA");
290
if (!(objFlags & EF_AVR_LINKRELAX_PREPARED))
291
hasLinkRelaxFlag = false;
292
}
293
294
if (!hasLinkRelaxFlag)
295
flags &= ~EF_AVR_LINKRELAX_PREPARED;
296
297
return flags;
298
}
299
300