kobj_machdep.c revision 1.1 1 /* $NetBSD: kobj_machdep.c,v 1.1 2018/08/15 11:10:45 ryo Exp $ */
2
3 /*
4 * Copyright (c) 2018 Ryo Shimizu <ryo (at) nerv.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
25 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: kobj_machdep.c,v 1.1 2018/08/15 11:10:45 ryo Exp $");
31
32 #define ELFSIZE ARCH_ELFSIZE
33
34 #include "opt_ddb.h"
35
36 #include <sys/param.h>
37 #include <sys/kobj.h>
38 #include <sys/exec.h>
39 #include <sys/exec_elf.h>
40 #include <sys/errno.h>
41 #include <sys/queue.h>
42 #include <sys/tree.h>
43
44 #include <aarch64/cpufunc.h>
45
46 /* #define KOBJ_MACHDEP_DEBUG */
47
48 #ifdef KOBJ_MACHDEP_DEBUG
49 #ifdef DDB
50 #include <aarch64/db_machdep.h> /* for strdisasm() */
51 #endif
52
53 struct rtypeinfo {
54 Elf_Word rtype;
55 const char *name;
56 };
57
58 static const struct rtypeinfo rtypetbl[] = {
59 { R_AARCH64_ABS64, "R_AARCH64_ABS64" },
60 { R_AARCH64_ADD_ABS_LO12_NC, "R_AARCH64_ADD_ABS_LO12_NC" },
61 { R_AARCH_LDST64_ABS_LO12_NC, "R_AARCH64_LDST64_ABS_LO12_NC" },
62 { R_AARCH_LDST32_ABS_LO12_NC, "R_AARCH64_LDST32_ABS_LO12_NC" },
63 { R_AARCH_LDST16_ABS_LO12_NC, "R_AARCH64_LDST16_ABS_LO12_NC" },
64 { R_AARCH64_LDST8_ABS_LO12_NC, "R_AARCH64_LDST8_ABS_LO12_NC" },
65 { R_AARCH64_ADR_PREL_PG_HI21_NC, "R_AARCH64_ADR_PREL_PG_HI21_NC"},
66 { R_AARCH64_ADR_PREL_PG_HI21, "R_AARCH64_ADR_PREL_PG_HI21" },
67 { R_AARCH_JUMP26, "R_AARCH64_JUMP26" },
68 { R_AARCH_CALL26, "R_AARCH64_CALL26" },
69 { R_AARCH64_PREL32, "R_AARCH64_PREL32" },
70 { R_AARCH64_PREL16, "R_AARCH64_PREL16" }
71 };
72
73 static const char *
74 strrtype(Elf_Word rtype)
75 {
76 int i;
77 static char buf[64];
78
79 for (i = 0; i < __arraycount(rtypetbl); i++) {
80 if (rtypetbl[i].rtype == rtype)
81 return rtypetbl[i].name;
82 }
83 snprintf(buf, sizeof(buf), "RELOCATION-TYPE-%d", rtype);
84 return buf;
85 }
86 #endif /* KOBJ_MACHDEP_DEBUG */
87
88 static inline bool
89 checkalign(Elf_Addr addr, int alignbyte, void *where, Elf64_Addr off)
90 {
91 if ((addr & (alignbyte - 1)) != 0) {
92 printf("kobj_reloc: Relocation 0x%jx unaligned at %p"
93 " (base+0x%jx). must be aligned %d\n",
94 (uintptr_t)addr, where, off, alignbyte);
95 return true;
96 }
97 return false;
98 }
99
100 static inline bool
101 checkoverflow(Elf_Addr addr, int bitwidth, Elf_Addr targetaddr,
102 const char *bitscale, void *where, Elf64_Addr off)
103 {
104 const Elf_Addr mask = ~__BITS(bitwidth - 1, 0);
105
106 if (((addr & mask) != 0) && ((addr & mask) != mask)) {
107 printf("kobj_reloc: Relocation 0x%jx too far from %p"
108 " (base+0x%jx) for %dbit%s\n",
109 (uintptr_t)targetaddr, where, off, bitwidth, bitscale);
110 return true;
111 }
112 return false;
113 }
114
115 #define WIDTHMASK(w) (0xffffffffffffffffUL >> (64 - (w)))
116
117 int
118 kobj_reloc(kobj_t ko, uintptr_t relocbase, const void *data,
119 bool isrela, bool local)
120 {
121 Elf_Addr saddr, addend, raddr, val;
122 Elf64_Addr off, *where;
123 Elf32_Addr *where32;
124 uint16_t *where16;
125 Elf_Word rtype, symidx;
126 const Elf_Rela *rela;
127 int error;
128 uint32_t *insn, immhi, immlo, shift;
129 bool nc = false;
130 #ifdef KOBJ_MACHDEP_DEBUG
131 #ifdef DDB
132 char disasmbuf[256];
133 #endif
134 Elf_Addr old;
135 #endif /* KOBJ_MACHDEP_DEBUG */
136
137
138 #ifdef KOBJ_MACHDEP_DEBUG
139 printf("%s:%d: ko=%p, relocbase=0x%jx, data=%p"
140 ", isrela=%d, local=%d\n", __func__, __LINE__,
141 ko, relocbase, data, isrela, local);
142 #endif /* KOBJ_MACHDEP_DEBUG */
143
144 if (!isrela) {
145 printf("kobj_reloc: REL relocations not supported");
146 error = 1;
147 goto done;
148 }
149
150 rela = (const Elf_Rela *)data;
151 addend = rela->r_addend;
152 rtype = ELF_R_TYPE(rela->r_info);
153 symidx = ELF_R_SYM(rela->r_info);
154 off = rela->r_offset;
155 where = (Elf_Addr *)(relocbase + off);
156
157 /* pointer to 32bit, 16bit, and instruction */
158 where32 = (void *)where;
159 where16 = (void *)where;
160 insn = (uint32_t *)where;
161
162 /* no need to lookup any symbols */
163 switch (rtype) {
164 case R_AARCH64_NONE:
165 case R_AARCH64_NONE2:
166 return 0;
167 }
168
169 error = kobj_sym_lookup(ko, symidx, &saddr);
170 if (error != 0) {
171 printf("kobj_reloc: symidx %d lookup failure."
172 " relocation type %d at %p (base+0x%jx)\n",
173 symidx, rtype, where, off);
174 goto done;
175 }
176
177 #ifdef KOBJ_MACHDEP_DEBUG
178 printf("%s:%d: symidx=%d, saddr=0x%jx, addend=0x%jx\n",
179 __func__, __LINE__, symidx, (uintptr_t)saddr, (uintptr_t)addend);
180 printf("%s:%d: rtype=%s, where=%p (base+0x%jx)\n",
181 __func__, __LINE__, strrtype(rtype), where, off);
182 old = *where;
183 #ifdef DDB
184 snprintf(disasmbuf, sizeof(disasmbuf), "%08x %s",
185 *insn, strdisasm((vaddr_t)insn));
186 #endif
187 #endif /* KOBJ_MACHDEP_DEBUG */
188
189 switch (rtype) {
190 case R_AARCH64_ABS64:
191 /*
192 * S + A
193 * e.g.) .quad <sym>+addend
194 */
195 *where = saddr + addend;
196 break;
197 case R_AARCH64_ABS32:
198 /*
199 * S + A
200 * e.g.) .word <sym>+addend
201 */
202 *where32 = saddr + addend;
203 break;
204 case R_AARCH64_ABS16:
205 /*
206 * S + A
207 * e.g.) .short <sym>+addend
208 */
209 *where16 = saddr + addend;
210 break;
211 case R_AARCH64_ADD_ABS_LO12_NC:
212 case R_AARCH64_LDST8_ABS_LO12_NC:
213 case R_AARCH_LDST16_ABS_LO12_NC:
214 case R_AARCH_LDST32_ABS_LO12_NC:
215 case R_AARCH_LDST64_ABS_LO12_NC:
216 switch (rtype) {
217 case R_AARCH64_ADD_ABS_LO12_NC:
218 case R_AARCH64_LDST8_ABS_LO12_NC:
219 shift = 0;
220 break;
221 case R_AARCH_LDST16_ABS_LO12_NC:
222 shift = 1;
223 break;
224 case R_AARCH_LDST32_ABS_LO12_NC:
225 shift = 2;
226 break;
227 case R_AARCH_LDST64_ABS_LO12_NC:
228 shift = 3;
229 break;
230 default:
231 panic("illegal rtype: %d\n", rtype);
232 }
233 /*
234 * S + A
235 * e.g.) add x0,x0,#:lo12:<sym>+<addend>
236 * ldrb w0,[x0,#:lo12:<sym>+<addend>]
237 * ldrh w0,[x0,#:lo12:<sym>+<addend>]
238 * ldr w0,[x0,#:lo12:<sym>+<addend>]
239 * ldr x0,[x0,#:lo12:<sym>+<addend>]
240 */
241 val = saddr + addend;
242 if (checkalign(val, 1 << shift, where, off)) {
243 error = 1;
244 break;
245 }
246 val &= WIDTHMASK(12);
247 val >>= shift;
248 *insn = (*insn & ~__BITS(21,10)) | (val << 10);
249 break;
250
251 case R_AARCH64_ADR_PREL_PG_HI21_NC:
252 nc = true;
253 /* FALLTHRU */
254 case R_AARCH64_ADR_PREL_PG_HI21:
255 /*
256 * Page(S + A) - Page(P)
257 * e.g.) adrp x0,<sym>+<addend>
258 */
259 val = saddr + addend;
260 val = val >> 12;
261 raddr = val << 12;
262 val -= (uintptr_t)where >> 12;
263 if (!nc && checkoverflow(val, 21, val, " x 4k", where, off)) {
264 error = 1;
265 break;
266 }
267 immlo = val & WIDTHMASK(2);
268 immhi = (val >> 2) & WIDTHMASK(19);
269 *insn = (*insn & ~(__BITS(30,29) | __BITS(23,5))) |
270 (immlo << 29) | (immhi << 5);
271 break;
272
273 case R_AARCH_JUMP26:
274 case R_AARCH_CALL26:
275 /*
276 * S + A - P
277 * e.g.) b <sym>+<addend>
278 * bl <sym>+<addend>
279 */
280 raddr = saddr + addend;
281 val = raddr - (uintptr_t)where;
282 if (checkalign(val, 4, where, off)) {
283 error = 1;
284 break;
285 }
286 val = (intptr_t)val >> 2;
287 if (checkoverflow(val, 26, val, " word", where, off)) {
288 error = 1;
289 break;
290 }
291 val &= WIDTHMASK(26);
292 *insn = (*insn & ~__BITS(25,0)) | val;
293 break;
294
295 case R_AARCH64_PREL64:
296 /*
297 * S + A - P
298 * e.g.) 1: .quad <sym>+<addend>-1b
299 */
300 raddr = saddr + addend;
301 val = raddr - (uintptr_t)where;
302 if (checkoverflow(val, 64, val, "", where, off)) {
303 error = 1;
304 break;
305 }
306 *where = val;
307 break;
308 case R_AARCH64_PREL32:
309 /*
310 * S + A - P
311 * e.g.) 1: .word <sym>+<addend>-1b
312 */
313 raddr = saddr + addend;
314 val = raddr - (uintptr_t)where;
315 if (checkoverflow(val, 32, val, "", where, off)) {
316 error = 1;
317 break;
318 }
319 *where32 = val;
320 break;
321 case R_AARCH64_PREL16:
322 /*
323 * S + A - P
324 * e.g.) 1: .short <sym>+<addend>-1b
325 */
326 raddr = saddr + addend;
327 val = raddr - (uintptr_t)where;
328 if (checkoverflow(val, 16, val, "", where, off)) {
329 error = 1;
330 break;
331 }
332 *where16 = val;
333 break;
334 default:
335 printf("kobj_reloc: unsupported relocation type %d"
336 " at %p (base+0x%jx) symidx %u\n",
337 rtype, where, off, symidx);
338 error = 1;
339 break;
340 }
341
342 #ifdef KOBJ_MACHDEP_DEBUG
343 printf("%s: reloc\n", __func__);
344 printf("%s: *where %016jx\n", __func__, (uintptr_t)old);
345 printf("%s: -> %016jx\n", __func__, (uintptr_t)*where);
346 #ifdef DDB
347 printf("%s: insn %s\n", __func__, disasmbuf);
348 printf("%s: -> %08x %s\n", __func__,
349 *insn, strdisasm((vaddr_t)insn));
350 #endif
351 printf("\n");
352 #endif /* KOBJ_MACHDEP_DEBUG */
353
354 done:
355 if (error != 0)
356 return -1;
357 return 0;
358 }
359
360 int
361 kobj_machdep(kobj_t ko, void *base, size_t size, bool load)
362 {
363 return 0;
364 }
365