1 /*
2 * libkmod - interface to kernel module operations
3 *
4 * Copyright (C) 2011-2013 ProFUSION embedded systems
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <assert.h>
21 #include <elf.h>
22 #include <errno.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include <shared/util.h>
27
28 #include "libkmod.h"
29 #include "libkmod-internal.h"
30
31 enum kmod_elf_class {
32 KMOD_ELF_32 = (1 << 1),
33 KMOD_ELF_64 = (1 << 2),
34 KMOD_ELF_LSB = (1 << 3),
35 KMOD_ELF_MSB = (1 << 4)
36 };
37
38 /* as defined in module-init-tools */
39 struct kmod_modversion32 {
40 uint32_t crc;
41 char name[64 - sizeof(uint32_t)];
42 };
43
44 struct kmod_modversion64 {
45 uint64_t crc;
46 char name[64 - sizeof(uint64_t)];
47 };
48
49 struct kmod_elf {
50 const uint8_t *memory;
51 uint8_t *changed;
52 uint64_t size;
53 enum kmod_elf_class class;
54 struct kmod_elf_header {
55 struct {
56 uint64_t offset;
57 uint16_t count;
58 uint16_t entry_size;
59 } section;
60 struct {
61 uint16_t section; /* index of the strings section */
62 uint64_t size;
63 uint64_t offset;
64 uint32_t nameoff; /* offset in strings itself */
65 } strings;
66 uint16_t machine;
67 } header;
68 };
69
70 //#define ENABLE_ELFDBG 1
71
72 #if defined(ENABLE_LOGGING) && defined(ENABLE_ELFDBG)
73 #define ELFDBG(elf, ...) \
74 _elf_dbg(elf, __FILE__, __LINE__, __func__, __VA_ARGS__);
75
_elf_dbg(const struct kmod_elf * elf,const char * fname,unsigned line,const char * func,const char * fmt,...)76 static inline void _elf_dbg(const struct kmod_elf *elf, const char *fname, unsigned line, const char *func, const char *fmt, ...)
77 {
78 va_list args;
79
80 fprintf(stderr, "ELFDBG-%d%c: %s:%u %s() ",
81 (elf->class & KMOD_ELF_32) ? 32 : 64,
82 (elf->class & KMOD_ELF_MSB) ? 'M' : 'L',
83 fname, line, func);
84 va_start(args, fmt);
85 vfprintf(stderr, fmt, args);
86 va_end(args);
87 }
88 #else
89 #define ELFDBG(elf, ...)
90 #endif
91
92
elf_identify(const void * memory,uint64_t size)93 static int elf_identify(const void *memory, uint64_t size)
94 {
95 const uint8_t *p = memory;
96 int class = 0;
97
98 if (size <= EI_NIDENT || memcmp(p, ELFMAG, SELFMAG) != 0)
99 return -ENOEXEC;
100
101 switch (p[EI_CLASS]) {
102 case ELFCLASS32:
103 if (size <= sizeof(Elf32_Ehdr))
104 return -EINVAL;
105 class |= KMOD_ELF_32;
106 break;
107 case ELFCLASS64:
108 if (size <= sizeof(Elf64_Ehdr))
109 return -EINVAL;
110 class |= KMOD_ELF_64;
111 break;
112 default:
113 return -EINVAL;
114 }
115
116 switch (p[EI_DATA]) {
117 case ELFDATA2LSB:
118 class |= KMOD_ELF_LSB;
119 break;
120 case ELFDATA2MSB:
121 class |= KMOD_ELF_MSB;
122 break;
123 default:
124 return -EINVAL;
125 }
126
127 return class;
128 }
129
elf_get_uint(const struct kmod_elf * elf,uint64_t offset,uint16_t size)130 static inline uint64_t elf_get_uint(const struct kmod_elf *elf, uint64_t offset, uint16_t size)
131 {
132 const uint8_t *p;
133 uint64_t ret = 0;
134 size_t i;
135
136 assert(size <= sizeof(uint64_t));
137 assert(offset + size <= elf->size);
138 if (offset + size > elf->size) {
139 ELFDBG(elf, "out of bounds: %"PRIu64" + %"PRIu16" = %"PRIu64"> %"PRIu64" (ELF size)\n",
140 offset, size, offset + size, elf->size);
141 return (uint64_t)-1;
142 }
143
144 p = elf->memory + offset;
145 if (elf->class & KMOD_ELF_MSB) {
146 for (i = 0; i < size; i++)
147 ret = (ret << 8) | p[i];
148 } else {
149 for (i = 1; i <= size; i++)
150 ret = (ret << 8) | p[size - i];
151 }
152
153 ELFDBG(elf, "size=%"PRIu16" offset=%"PRIu64" value=%"PRIu64"\n",
154 size, offset, ret);
155
156 return ret;
157 }
158
elf_set_uint(struct kmod_elf * elf,uint64_t offset,uint64_t size,uint64_t value)159 static inline int elf_set_uint(struct kmod_elf *elf, uint64_t offset, uint64_t size, uint64_t value)
160 {
161 uint8_t *p;
162 size_t i;
163
164 ELFDBG(elf, "size=%"PRIu16" offset=%"PRIu64" value=%"PRIu64" write memory=%p\n",
165 size, offset, value, elf->changed);
166
167 assert(size <= sizeof(uint64_t));
168 assert(offset + size <= elf->size);
169 if (offset + size > elf->size) {
170 ELFDBG(elf, "out of bounds: %"PRIu64" + %"PRIu16" = %"PRIu64"> %"PRIu64" (ELF size)\n",
171 offset, size, offset + size, elf->size);
172 return -1;
173 }
174
175 if (elf->changed == NULL) {
176 elf->changed = malloc(elf->size);
177 if (elf->changed == NULL)
178 return -errno;
179 memcpy(elf->changed, elf->memory, elf->size);
180 elf->memory = elf->changed;
181 ELFDBG(elf, "copied memory to allow writing.\n");
182 }
183
184 p = elf->changed + offset;
185 if (elf->class & KMOD_ELF_MSB) {
186 for (i = 1; i <= size; i++) {
187 p[size - i] = value & 0xff;
188 value = (value & 0xffffffffffffff00) >> 8;
189 }
190 } else {
191 for (i = 0; i < size; i++) {
192 p[i] = value & 0xff;
193 value = (value & 0xffffffffffffff00) >> 8;
194 }
195 }
196
197 return 0;
198 }
199
elf_get_mem(const struct kmod_elf * elf,uint64_t offset)200 static inline const void *elf_get_mem(const struct kmod_elf *elf, uint64_t offset)
201 {
202 assert(offset < elf->size);
203 if (offset >= elf->size) {
204 ELFDBG(elf, "out-of-bounds: %"PRIu64" >= %"PRIu64" (ELF size)\n",
205 offset, elf->size);
206 return NULL;
207 }
208 return elf->memory + offset;
209 }
210
elf_get_section_header(const struct kmod_elf * elf,uint16_t idx)211 static inline const void *elf_get_section_header(const struct kmod_elf *elf, uint16_t idx)
212 {
213 assert(idx != SHN_UNDEF);
214 assert(idx < elf->header.section.count);
215 if (idx == SHN_UNDEF || idx >= elf->header.section.count) {
216 ELFDBG(elf, "invalid section number: %"PRIu16", last=%"PRIu16"\n",
217 idx, elf->header.section.count);
218 return NULL;
219 }
220 return elf_get_mem(elf, elf->header.section.offset +
221 (uint64_t)(idx * elf->header.section.entry_size));
222 }
223
elf_get_section_info(const struct kmod_elf * elf,uint16_t idx,uint64_t * offset,uint64_t * size,uint32_t * nameoff)224 static inline int elf_get_section_info(const struct kmod_elf *elf, uint16_t idx, uint64_t *offset, uint64_t *size, uint32_t *nameoff)
225 {
226 const uint8_t *p = elf_get_section_header(elf, idx);
227 uint64_t min_size, off = p - elf->memory;
228
229 if (p == NULL) {
230 ELFDBG(elf, "no section at %"PRIu16"\n", idx);
231 *offset = 0;
232 *size = 0;
233 *nameoff = 0;
234 return -EINVAL;
235 }
236
237 #define READV(field) \
238 elf_get_uint(elf, off + offsetof(typeof(*hdr), field), sizeof(hdr->field))
239
240 if (elf->class & KMOD_ELF_32) {
241 const Elf32_Shdr *hdr _unused_ = (const Elf32_Shdr *)p;
242 *size = READV(sh_size);
243 *offset = READV(sh_offset);
244 *nameoff = READV(sh_name);
245 } else {
246 const Elf64_Shdr *hdr _unused_ = (const Elf64_Shdr *)p;
247 *size = READV(sh_size);
248 *offset = READV(sh_offset);
249 *nameoff = READV(sh_name);
250 }
251 #undef READV
252
253 if (addu64_overflow(*offset, *size, &min_size)
254 || min_size > elf->size) {
255 ELFDBG(elf, "out-of-bounds: %"PRIu64" >= %"PRIu64" (ELF size)\n",
256 min_size, elf->size);
257 return -EINVAL;
258 }
259
260 ELFDBG(elf, "section=%"PRIu16" is: offset=%"PRIu64" size=%"PRIu64" nameoff=%"PRIu32"\n",
261 idx, *offset, *size, *nameoff);
262
263 return 0;
264 }
265
elf_get_strings_section(const struct kmod_elf * elf,uint64_t * size)266 static const char *elf_get_strings_section(const struct kmod_elf *elf, uint64_t *size)
267 {
268 *size = elf->header.strings.size;
269 return elf_get_mem(elf, elf->header.strings.offset);
270 }
271
kmod_elf_new(const void * memory,off_t size)272 struct kmod_elf *kmod_elf_new(const void *memory, off_t size)
273 {
274 struct kmod_elf *elf;
275 uint64_t min_size;
276 size_t shdrs_size, shdr_size;
277 int class;
278
279 assert_cc(sizeof(uint16_t) == sizeof(Elf32_Half));
280 assert_cc(sizeof(uint16_t) == sizeof(Elf64_Half));
281 assert_cc(sizeof(uint32_t) == sizeof(Elf32_Word));
282 assert_cc(sizeof(uint32_t) == sizeof(Elf64_Word));
283
284 if (!memory) {
285 errno = -EINVAL;
286 return NULL;
287 }
288
289 class = elf_identify(memory, size);
290 if (class < 0) {
291 errno = -class;
292 return NULL;
293 }
294
295 elf = malloc(sizeof(struct kmod_elf));
296 if (elf == NULL) {
297 return NULL;
298 }
299
300 elf->memory = memory;
301 elf->changed = NULL;
302 elf->size = size;
303 elf->class = class;
304
305 #define READV(field) \
306 elf_get_uint(elf, offsetof(typeof(*hdr), field), sizeof(hdr->field))
307
308 #define LOAD_HEADER \
309 elf->header.section.offset = READV(e_shoff); \
310 elf->header.section.count = READV(e_shnum); \
311 elf->header.section.entry_size = READV(e_shentsize); \
312 elf->header.strings.section = READV(e_shstrndx); \
313 elf->header.machine = READV(e_machine)
314 if (elf->class & KMOD_ELF_32) {
315 const Elf32_Ehdr *hdr _unused_ = elf_get_mem(elf, 0);
316 LOAD_HEADER;
317 shdr_size = sizeof(Elf32_Shdr);
318 } else {
319 const Elf64_Ehdr *hdr _unused_ = elf_get_mem(elf, 0);
320 LOAD_HEADER;
321 shdr_size = sizeof(Elf64_Shdr);
322 }
323 #undef LOAD_HEADER
324 #undef READV
325
326 ELFDBG(elf, "section: offset=%"PRIu64" count=%"PRIu16" entry_size=%"PRIu16" strings index=%"PRIu16"\n",
327 elf->header.section.offset,
328 elf->header.section.count,
329 elf->header.section.entry_size,
330 elf->header.strings.section);
331
332 if (elf->header.section.entry_size != shdr_size) {
333 ELFDBG(elf, "unexpected section entry size: %"PRIu16", expected %"PRIu16"\n",
334 elf->header.section.entry_size, shdr_size);
335 goto invalid;
336 }
337 shdrs_size = shdr_size * elf->header.section.count;
338 if (addu64_overflow(shdrs_size, elf->header.section.offset, &min_size)
339 || min_size > elf->size) {
340 ELFDBG(elf, "file is too short to hold sections\n");
341 goto invalid;
342 }
343
344 if (elf_get_section_info(elf, elf->header.strings.section,
345 &elf->header.strings.offset,
346 &elf->header.strings.size,
347 &elf->header.strings.nameoff) < 0) {
348 ELFDBG(elf, "could not get strings section\n");
349 goto invalid;
350 } else {
351 uint64_t slen;
352 const char *s = elf_get_strings_section(elf, &slen);
353 if (slen == 0 || s[slen - 1] != '\0') {
354 ELFDBG(elf, "strings section does not ends with \\0\n");
355 goto invalid;
356 }
357 }
358
359 return elf;
360
361 invalid:
362 free(elf);
363 errno = EINVAL;
364 return NULL;
365 }
366
kmod_elf_unref(struct kmod_elf * elf)367 void kmod_elf_unref(struct kmod_elf *elf)
368 {
369 free(elf->changed);
370 free(elf);
371 }
372
kmod_elf_get_memory(const struct kmod_elf * elf)373 const void *kmod_elf_get_memory(const struct kmod_elf *elf)
374 {
375 return elf->memory;
376 }
377
elf_find_section(const struct kmod_elf * elf,const char * section)378 static int elf_find_section(const struct kmod_elf *elf, const char *section)
379 {
380 uint64_t nameslen;
381 const char *names = elf_get_strings_section(elf, &nameslen);
382 uint16_t i;
383
384 for (i = 1; i < elf->header.section.count; i++) {
385 uint64_t off, size;
386 uint32_t nameoff;
387 const char *n;
388 int err = elf_get_section_info(elf, i, &off, &size, &nameoff);
389 if (err < 0)
390 continue;
391 if (nameoff >= nameslen)
392 continue;
393 n = names + nameoff;
394 if (!streq(section, n))
395 continue;
396
397 return i;
398 }
399
400 return -ENODATA;
401 }
402
kmod_elf_get_section(const struct kmod_elf * elf,const char * section,const void ** buf,uint64_t * buf_size)403 int kmod_elf_get_section(const struct kmod_elf *elf, const char *section, const void **buf, uint64_t *buf_size)
404 {
405 uint64_t nameslen;
406 const char *names = elf_get_strings_section(elf, &nameslen);
407 uint16_t i;
408
409 *buf = NULL;
410 *buf_size = 0;
411
412 for (i = 1; i < elf->header.section.count; i++) {
413 uint64_t off, size;
414 uint32_t nameoff;
415 const char *n;
416 int err = elf_get_section_info(elf, i, &off, &size, &nameoff);
417 if (err < 0)
418 continue;
419 if (nameoff >= nameslen)
420 continue;
421 n = names + nameoff;
422 if (!streq(section, n))
423 continue;
424
425 *buf = elf_get_mem(elf, off);
426 *buf_size = size;
427 return 0;
428 }
429
430 return -ENODATA;
431 }
432
433 /* array will be allocated with strings in a single malloc, just free *array */
kmod_elf_get_strings(const struct kmod_elf * elf,const char * section,char *** array)434 int kmod_elf_get_strings(const struct kmod_elf *elf, const char *section, char ***array)
435 {
436 size_t i, j, count;
437 uint64_t size;
438 const void *buf;
439 const char *strings;
440 char *s, **a;
441 int err;
442
443 *array = NULL;
444
445 err = kmod_elf_get_section(elf, section, &buf, &size);
446 if (err < 0)
447 return err;
448
449 strings = buf;
450 if (strings == NULL || size == 0)
451 return 0;
452
453 /* skip zero padding */
454 while (strings[0] == '\0' && size > 1) {
455 strings++;
456 size--;
457 }
458
459 if (size <= 1)
460 return 0;
461
462 for (i = 0, count = 0; i < size; ) {
463 if (strings[i] != '\0') {
464 i++;
465 continue;
466 }
467
468 while (strings[i] == '\0' && i < size)
469 i++;
470
471 count++;
472 }
473
474 if (strings[i - 1] != '\0')
475 count++;
476
477 *array = a = malloc(size + 1 + sizeof(char *) * (count + 1));
478 if (*array == NULL)
479 return -errno;
480
481 s = (char *)(a + count + 1);
482 memcpy(s, strings, size);
483
484 /* make sure the last string is NULL-terminated */
485 s[size] = '\0';
486 a[count] = NULL;
487 a[0] = s;
488
489 for (i = 0, j = 1; j < count && i < size; ) {
490 if (s[i] != '\0') {
491 i++;
492 continue;
493 }
494
495 while (strings[i] == '\0' && i < size)
496 i++;
497
498 a[j] = &s[i];
499 j++;
500 }
501
502 return count;
503 }
504
505 /* array will be allocated with strings in a single malloc, just free *array */
kmod_elf_get_modversions(const struct kmod_elf * elf,struct kmod_modversion ** array)506 int kmod_elf_get_modversions(const struct kmod_elf *elf, struct kmod_modversion **array)
507 {
508 size_t off, offcrc, slen;
509 uint64_t size;
510 struct kmod_modversion *a;
511 const void *buf;
512 char *itr;
513 int i, count, err;
514 #define MODVERSION_SEC_SIZE (sizeof(struct kmod_modversion64))
515
516 assert_cc(sizeof(struct kmod_modversion64) ==
517 sizeof(struct kmod_modversion32));
518
519 if (elf->class & KMOD_ELF_32)
520 offcrc = sizeof(uint32_t);
521 else
522 offcrc = sizeof(uint64_t);
523
524 *array = NULL;
525
526 err = kmod_elf_get_section(elf, "__versions", &buf, &size);
527 if (err < 0)
528 return err;
529
530 if (buf == NULL || size == 0)
531 return 0;
532
533 if (size % MODVERSION_SEC_SIZE != 0)
534 return -EINVAL;
535
536 count = size / MODVERSION_SEC_SIZE;
537
538 off = (const uint8_t *)buf - elf->memory;
539 slen = 0;
540
541 for (i = 0; i < count; i++, off += MODVERSION_SEC_SIZE) {
542 const char *symbol = elf_get_mem(elf, off + offcrc);
543
544 if (symbol[0] == '.')
545 symbol++;
546
547 slen += strlen(symbol) + 1;
548 }
549
550 *array = a = malloc(sizeof(struct kmod_modversion) * count + slen);
551 if (*array == NULL)
552 return -errno;
553
554 itr = (char *)(a + count);
555 off = (const uint8_t *)buf - elf->memory;
556
557 for (i = 0; i < count; i++, off += MODVERSION_SEC_SIZE) {
558 uint64_t crc = elf_get_uint(elf, off, offcrc);
559 const char *symbol = elf_get_mem(elf, off + offcrc);
560 size_t symbollen;
561
562 if (symbol[0] == '.')
563 symbol++;
564
565 a[i].crc = crc;
566 a[i].bind = KMOD_SYMBOL_UNDEF;
567 a[i].symbol = itr;
568 symbollen = strlen(symbol) + 1;
569 memcpy(itr, symbol, symbollen);
570 itr += symbollen;
571 }
572
573 return count;
574 }
575
kmod_elf_strip_section(struct kmod_elf * elf,const char * section)576 int kmod_elf_strip_section(struct kmod_elf *elf, const char *section)
577 {
578 uint64_t off, size;
579 const void *buf;
580 int idx = elf_find_section(elf, section);
581 uint64_t val;
582
583 if (idx < 0)
584 return idx;
585
586 buf = elf_get_section_header(elf, idx);
587 off = (const uint8_t *)buf - elf->memory;
588
589 if (elf->class & KMOD_ELF_32) {
590 off += offsetof(Elf32_Shdr, sh_flags);
591 size = sizeof(((Elf32_Shdr *)buf)->sh_flags);
592 } else {
593 off += offsetof(Elf64_Shdr, sh_flags);
594 size = sizeof(((Elf64_Shdr *)buf)->sh_flags);
595 }
596
597 val = elf_get_uint(elf, off, size);
598 val &= ~(uint64_t)SHF_ALLOC;
599
600 return elf_set_uint(elf, off, size, val);
601 }
602
kmod_elf_strip_vermagic(struct kmod_elf * elf)603 int kmod_elf_strip_vermagic(struct kmod_elf *elf)
604 {
605 uint64_t i, size;
606 const void *buf;
607 const char *strings;
608 int err;
609
610 err = kmod_elf_get_section(elf, ".modinfo", &buf, &size);
611 if (err < 0)
612 return err;
613 strings = buf;
614 if (strings == NULL || size == 0)
615 return 0;
616
617 /* skip zero padding */
618 while (strings[0] == '\0' && size > 1) {
619 strings++;
620 size--;
621 }
622 if (size <= 1)
623 return 0;
624
625 for (i = 0; i < size; i++) {
626 const char *s;
627 size_t off, len;
628
629 if (strings[i] == '\0')
630 continue;
631 if (i + 1 >= size)
632 continue;
633
634 s = strings + i;
635 len = sizeof("vermagic=") - 1;
636 if (i + len >= size)
637 continue;
638 if (strncmp(s, "vermagic=", len) != 0) {
639 i += strlen(s);
640 continue;
641 }
642 off = (const uint8_t *)s - elf->memory;
643
644 if (elf->changed == NULL) {
645 elf->changed = malloc(elf->size);
646 if (elf->changed == NULL)
647 return -errno;
648 memcpy(elf->changed, elf->memory, elf->size);
649 elf->memory = elf->changed;
650 ELFDBG(elf, "copied memory to allow writing.\n");
651 }
652
653 len = strlen(s);
654 ELFDBG(elf, "clear .modinfo vermagic \"%s\" (%zd bytes)\n",
655 s, len);
656 memset(elf->changed + off, '\0', len);
657 return 0;
658 }
659
660 ELFDBG(elf, "no vermagic found in .modinfo\n");
661 return -ENODATA;
662 }
663
664
kmod_elf_get_symbols_symtab(const struct kmod_elf * elf,struct kmod_modversion ** array)665 static int kmod_elf_get_symbols_symtab(const struct kmod_elf *elf, struct kmod_modversion **array)
666 {
667 uint64_t i, last, size;
668 const void *buf;
669 const char *strings;
670 char *itr;
671 struct kmod_modversion *a;
672 int count, err;
673
674 *array = NULL;
675
676 err = kmod_elf_get_section(elf, "__ksymtab_strings", &buf, &size);
677 if (err < 0)
678 return err;
679 strings = buf;
680 if (strings == NULL || size == 0)
681 return 0;
682
683 /* skip zero padding */
684 while (strings[0] == '\0' && size > 1) {
685 strings++;
686 size--;
687 }
688 if (size <= 1)
689 return 0;
690
691 last = 0;
692 for (i = 0, count = 0; i < size; i++) {
693 if (strings[i] == '\0') {
694 if (last == i) {
695 last = i + 1;
696 continue;
697 }
698 count++;
699 last = i + 1;
700 }
701 }
702 if (strings[i - 1] != '\0')
703 count++;
704
705 *array = a = malloc(size + 1 + sizeof(struct kmod_modversion) * count);
706 if (*array == NULL)
707 return -errno;
708
709 itr = (char *)(a + count);
710 last = 0;
711 for (i = 0, count = 0; i < size; i++) {
712 if (strings[i] == '\0') {
713 size_t slen = i - last;
714 if (last == i) {
715 last = i + 1;
716 continue;
717 }
718 a[count].crc = 0;
719 a[count].bind = KMOD_SYMBOL_GLOBAL;
720 a[count].symbol = itr;
721 memcpy(itr, strings + last, slen);
722 itr[slen] = '\0';
723 itr += slen + 1;
724 count++;
725 last = i + 1;
726 }
727 }
728 if (strings[i - 1] != '\0') {
729 size_t slen = i - last;
730 a[count].crc = 0;
731 a[count].bind = KMOD_SYMBOL_GLOBAL;
732 a[count].symbol = itr;
733 memcpy(itr, strings + last, slen);
734 itr[slen] = '\0';
735 count++;
736 }
737
738 return count;
739 }
740
kmod_symbol_bind_from_elf(uint8_t elf_value)741 static inline uint8_t kmod_symbol_bind_from_elf(uint8_t elf_value)
742 {
743 switch (elf_value) {
744 case STB_LOCAL:
745 return KMOD_SYMBOL_LOCAL;
746 case STB_GLOBAL:
747 return KMOD_SYMBOL_GLOBAL;
748 case STB_WEAK:
749 return KMOD_SYMBOL_WEAK;
750 default:
751 return KMOD_SYMBOL_NONE;
752 }
753 }
754
kmod_elf_resolve_crc(const struct kmod_elf * elf,uint64_t crc,uint16_t shndx)755 static uint64_t kmod_elf_resolve_crc(const struct kmod_elf *elf, uint64_t crc, uint16_t shndx)
756 {
757 int err;
758 uint64_t off, size;
759 uint32_t nameoff;
760
761 if (shndx == SHN_ABS || shndx == SHN_UNDEF)
762 return crc;
763
764 err = elf_get_section_info(elf, shndx, &off, &size, &nameoff);
765 if (err < 0) {
766 ELFDBG("Cound not find section index %"PRIu16" for crc", shndx);
767 return (uint64_t)-1;
768 }
769
770 if (crc > (size - sizeof(uint32_t))) {
771 ELFDBG("CRC offset %"PRIu64" is too big, section %"PRIu16" size is %"PRIu64"\n",
772 crc, shndx, size);
773 return (uint64_t)-1;
774 }
775
776 crc = elf_get_uint(elf, off + crc, sizeof(uint32_t));
777 return crc;
778 }
779
780 /* array will be allocated with strings in a single malloc, just free *array */
kmod_elf_get_symbols(const struct kmod_elf * elf,struct kmod_modversion ** array)781 int kmod_elf_get_symbols(const struct kmod_elf *elf, struct kmod_modversion **array)
782 {
783 static const char crc_str[] = "__crc_";
784 static const size_t crc_strlen = sizeof(crc_str) - 1;
785 uint64_t strtablen, symtablen, str_off, sym_off;
786 const void *strtab, *symtab;
787 struct kmod_modversion *a;
788 char *itr;
789 size_t slen, symlen;
790 int i, count, symcount, err;
791
792 err = kmod_elf_get_section(elf, ".strtab", &strtab, &strtablen);
793 if (err < 0) {
794 ELFDBG(elf, "no .strtab found.\n");
795 goto fallback;
796 }
797
798 err = kmod_elf_get_section(elf, ".symtab", &symtab, &symtablen);
799 if (err < 0) {
800 ELFDBG(elf, "no .symtab found.\n");
801 goto fallback;
802 }
803
804 if (elf->class & KMOD_ELF_32)
805 symlen = sizeof(Elf32_Sym);
806 else
807 symlen = sizeof(Elf64_Sym);
808
809 if (symtablen % symlen != 0) {
810 ELFDBG(elf, "unexpected .symtab of length %"PRIu64", not multiple of %"PRIu64" as expected.\n", symtablen, symlen);
811 goto fallback;
812 }
813
814 symcount = symtablen / symlen;
815 count = 0;
816 slen = 0;
817 str_off = (const uint8_t *)strtab - elf->memory;
818 sym_off = (const uint8_t *)symtab - elf->memory + symlen;
819 for (i = 1; i < symcount; i++, sym_off += symlen) {
820 const char *name;
821 uint32_t name_off;
822
823 #define READV(field) \
824 elf_get_uint(elf, sym_off + offsetof(typeof(*s), field),\
825 sizeof(s->field))
826 if (elf->class & KMOD_ELF_32) {
827 Elf32_Sym *s;
828 name_off = READV(st_name);
829 } else {
830 Elf64_Sym *s;
831 name_off = READV(st_name);
832 }
833 #undef READV
834 if (name_off >= strtablen) {
835 ELFDBG(elf, ".strtab is %"PRIu64" bytes, but .symtab entry %d wants to access offset %"PRIu32".\n", strtablen, i, name_off);
836 goto fallback;
837 }
838
839 name = elf_get_mem(elf, str_off + name_off);
840
841 if (strncmp(name, crc_str, crc_strlen) != 0)
842 continue;
843 slen += strlen(name + crc_strlen) + 1;
844 count++;
845 }
846
847 if (count == 0)
848 goto fallback;
849
850 *array = a = malloc(sizeof(struct kmod_modversion) * count + slen);
851 if (*array == NULL)
852 return -errno;
853
854 itr = (char *)(a + count);
855 count = 0;
856 str_off = (const uint8_t *)strtab - elf->memory;
857 sym_off = (const uint8_t *)symtab - elf->memory + symlen;
858 for (i = 1; i < symcount; i++, sym_off += symlen) {
859 const char *name;
860 uint32_t name_off;
861 uint64_t crc;
862 uint8_t info, bind;
863 uint16_t shndx;
864
865 #define READV(field) \
866 elf_get_uint(elf, sym_off + offsetof(typeof(*s), field),\
867 sizeof(s->field))
868 if (elf->class & KMOD_ELF_32) {
869 Elf32_Sym *s;
870 name_off = READV(st_name);
871 crc = READV(st_value);
872 info = READV(st_info);
873 shndx = READV(st_shndx);
874 } else {
875 Elf64_Sym *s;
876 name_off = READV(st_name);
877 crc = READV(st_value);
878 info = READV(st_info);
879 shndx = READV(st_shndx);
880 }
881 #undef READV
882 name = elf_get_mem(elf, str_off + name_off);
883 if (strncmp(name, crc_str, crc_strlen) != 0)
884 continue;
885 name += crc_strlen;
886
887 if (elf->class & KMOD_ELF_32)
888 bind = ELF32_ST_BIND(info);
889 else
890 bind = ELF64_ST_BIND(info);
891
892 a[count].crc = kmod_elf_resolve_crc(elf, crc, shndx);
893 a[count].bind = kmod_symbol_bind_from_elf(bind);
894 a[count].symbol = itr;
895 slen = strlen(name);
896 memcpy(itr, name, slen);
897 itr[slen] = '\0';
898 itr += slen + 1;
899 count++;
900 }
901 return count;
902
903 fallback:
904 ELFDBG(elf, "Falling back to __ksymtab_strings!\n");
905 return kmod_elf_get_symbols_symtab(elf, array);
906 }
907
kmod_elf_crc_find(const struct kmod_elf * elf,const void * versions,uint64_t versionslen,const char * name,uint64_t * crc)908 static int kmod_elf_crc_find(const struct kmod_elf *elf, const void *versions, uint64_t versionslen, const char *name, uint64_t *crc)
909 {
910 size_t verlen, crclen, off;
911 uint64_t i;
912
913 if (elf->class & KMOD_ELF_32) {
914 struct kmod_modversion32 *mv;
915 verlen = sizeof(*mv);
916 crclen = sizeof(mv->crc);
917 } else {
918 struct kmod_modversion64 *mv;
919 verlen = sizeof(*mv);
920 crclen = sizeof(mv->crc);
921 }
922
923 off = (const uint8_t *)versions - elf->memory;
924 for (i = 0; i < versionslen; i += verlen) {
925 const char *symbol = elf_get_mem(elf, off + i + crclen);
926 if (!streq(name, symbol))
927 continue;
928 *crc = elf_get_uint(elf, off + i, crclen);
929 return i / verlen;
930 }
931
932 ELFDBG(elf, "could not find crc for symbol '%s'\n", name);
933 *crc = 0;
934 return -1;
935 }
936
937 /* from module-init-tools:elfops_core.c */
938 #ifndef STT_REGISTER
939 #define STT_REGISTER 13 /* Global register reserved to app. */
940 #endif
941
942 /* array will be allocated with strings in a single malloc, just free *array */
kmod_elf_get_dependency_symbols(const struct kmod_elf * elf,struct kmod_modversion ** array)943 int kmod_elf_get_dependency_symbols(const struct kmod_elf *elf, struct kmod_modversion **array)
944 {
945 uint64_t versionslen, strtablen, symtablen, str_off, sym_off, ver_off;
946 const void *versions, *strtab, *symtab;
947 struct kmod_modversion *a;
948 char *itr;
949 size_t slen, verlen, symlen, crclen;
950 int i, count, symcount, vercount, err;
951 bool handle_register_symbols;
952 uint8_t *visited_versions;
953 uint64_t *symcrcs;
954
955 err = kmod_elf_get_section(elf, "__versions", &versions, &versionslen);
956 if (err < 0) {
957 versions = NULL;
958 versionslen = 0;
959 verlen = 0;
960 crclen = 0;
961 } else {
962 if (elf->class & KMOD_ELF_32) {
963 struct kmod_modversion32 *mv;
964 verlen = sizeof(*mv);
965 crclen = sizeof(mv->crc);
966 } else {
967 struct kmod_modversion64 *mv;
968 verlen = sizeof(*mv);
969 crclen = sizeof(mv->crc);
970 }
971 if (versionslen % verlen != 0) {
972 ELFDBG(elf, "unexpected __versions of length %"PRIu64", not multiple of %zd as expected.\n", versionslen, verlen);
973 versions = NULL;
974 versionslen = 0;
975 }
976 }
977
978 err = kmod_elf_get_section(elf, ".strtab", &strtab, &strtablen);
979 if (err < 0) {
980 ELFDBG(elf, "no .strtab found.\n");
981 return -EINVAL;
982 }
983
984 err = kmod_elf_get_section(elf, ".symtab", &symtab, &symtablen);
985 if (err < 0) {
986 ELFDBG(elf, "no .symtab found.\n");
987 return -EINVAL;
988 }
989
990 if (elf->class & KMOD_ELF_32)
991 symlen = sizeof(Elf32_Sym);
992 else
993 symlen = sizeof(Elf64_Sym);
994
995 if (symtablen % symlen != 0) {
996 ELFDBG(elf, "unexpected .symtab of length %"PRIu64", not multiple of %"PRIu64" as expected.\n", symtablen, symlen);
997 return -EINVAL;
998 }
999
1000 if (versionslen == 0) {
1001 vercount = 0;
1002 visited_versions = NULL;
1003 } else {
1004 vercount = versionslen / verlen;
1005 visited_versions = calloc(vercount, sizeof(uint8_t));
1006 if (visited_versions == NULL)
1007 return -ENOMEM;
1008 }
1009
1010 handle_register_symbols = (elf->header.machine == EM_SPARC ||
1011 elf->header.machine == EM_SPARCV9);
1012
1013 symcount = symtablen / symlen;
1014 count = 0;
1015 slen = 0;
1016 str_off = (const uint8_t *)strtab - elf->memory;
1017 sym_off = (const uint8_t *)symtab - elf->memory + symlen;
1018
1019 symcrcs = calloc(symcount, sizeof(uint64_t));
1020 if (symcrcs == NULL) {
1021 free(visited_versions);
1022 return -ENOMEM;
1023 }
1024
1025 for (i = 1; i < symcount; i++, sym_off += symlen) {
1026 const char *name;
1027 uint64_t crc;
1028 uint32_t name_off;
1029 uint16_t secidx;
1030 uint8_t info;
1031 int idx;
1032
1033 #define READV(field) \
1034 elf_get_uint(elf, sym_off + offsetof(typeof(*s), field),\
1035 sizeof(s->field))
1036 if (elf->class & KMOD_ELF_32) {
1037 Elf32_Sym *s;
1038 name_off = READV(st_name);
1039 secidx = READV(st_shndx);
1040 info = READV(st_info);
1041 } else {
1042 Elf64_Sym *s;
1043 name_off = READV(st_name);
1044 secidx = READV(st_shndx);
1045 info = READV(st_info);
1046 }
1047 #undef READV
1048 if (secidx != SHN_UNDEF)
1049 continue;
1050
1051 if (handle_register_symbols) {
1052 uint8_t type;
1053 if (elf->class & KMOD_ELF_32)
1054 type = ELF32_ST_TYPE(info);
1055 else
1056 type = ELF64_ST_TYPE(info);
1057
1058 /* Not really undefined: sparc gcc 3.3 creates
1059 * U references when you have global asm
1060 * variables, to avoid anyone else misusing
1061 * them.
1062 */
1063 if (type == STT_REGISTER)
1064 continue;
1065 }
1066
1067 if (name_off >= strtablen) {
1068 ELFDBG(elf, ".strtab is %"PRIu64" bytes, but .symtab entry %d wants to access offset %"PRIu32".\n", strtablen, i, name_off);
1069 free(visited_versions);
1070 free(symcrcs);
1071 return -EINVAL;
1072 }
1073
1074 name = elf_get_mem(elf, str_off + name_off);
1075 if (name[0] == '\0') {
1076 ELFDBG(elf, "empty symbol name at index %"PRIu64"\n", i);
1077 continue;
1078 }
1079
1080 slen += strlen(name) + 1;
1081 count++;
1082
1083 idx = kmod_elf_crc_find(elf, versions, versionslen, name, &crc);
1084 if (idx >= 0 && visited_versions != NULL)
1085 visited_versions[idx] = 1;
1086 symcrcs[i] = crc;
1087 }
1088
1089 if (visited_versions != NULL) {
1090 /* module_layout/struct_module are not visited, but needed */
1091 ver_off = (const uint8_t *)versions - elf->memory;
1092 for (i = 0; i < vercount; i++) {
1093 if (visited_versions[i] == 0) {
1094 const char *name;
1095 name = elf_get_mem(elf, ver_off + i * verlen + crclen);
1096 slen += strlen(name) + 1;
1097
1098 count++;
1099 }
1100 }
1101 }
1102
1103 if (count == 0) {
1104 free(visited_versions);
1105 free(symcrcs);
1106 *array = NULL;
1107 return 0;
1108 }
1109
1110 *array = a = malloc(sizeof(struct kmod_modversion) * count + slen);
1111 if (*array == NULL) {
1112 free(visited_versions);
1113 free(symcrcs);
1114 return -errno;
1115 }
1116
1117 itr = (char *)(a + count);
1118 count = 0;
1119 str_off = (const uint8_t *)strtab - elf->memory;
1120 sym_off = (const uint8_t *)symtab - elf->memory + symlen;
1121 for (i = 1; i < symcount; i++, sym_off += symlen) {
1122 const char *name;
1123 uint64_t crc;
1124 uint32_t name_off;
1125 uint16_t secidx;
1126 uint8_t info, bind;
1127
1128 #define READV(field) \
1129 elf_get_uint(elf, sym_off + offsetof(typeof(*s), field),\
1130 sizeof(s->field))
1131 if (elf->class & KMOD_ELF_32) {
1132 Elf32_Sym *s;
1133 name_off = READV(st_name);
1134 secidx = READV(st_shndx);
1135 info = READV(st_info);
1136 } else {
1137 Elf64_Sym *s;
1138 name_off = READV(st_name);
1139 secidx = READV(st_shndx);
1140 info = READV(st_info);
1141 }
1142 #undef READV
1143 if (secidx != SHN_UNDEF)
1144 continue;
1145
1146 if (handle_register_symbols) {
1147 uint8_t type;
1148 if (elf->class & KMOD_ELF_32)
1149 type = ELF32_ST_TYPE(info);
1150 else
1151 type = ELF64_ST_TYPE(info);
1152
1153 /* Not really undefined: sparc gcc 3.3 creates
1154 * U references when you have global asm
1155 * variables, to avoid anyone else misusing
1156 * them.
1157 */
1158 if (type == STT_REGISTER)
1159 continue;
1160 }
1161
1162 name = elf_get_mem(elf, str_off + name_off);
1163 if (name[0] == '\0') {
1164 ELFDBG(elf, "empty symbol name at index %"PRIu64"\n", i);
1165 continue;
1166 }
1167
1168 if (elf->class & KMOD_ELF_32)
1169 bind = ELF32_ST_BIND(info);
1170 else
1171 bind = ELF64_ST_BIND(info);
1172 if (bind == STB_WEAK)
1173 bind = KMOD_SYMBOL_WEAK;
1174 else
1175 bind = KMOD_SYMBOL_UNDEF;
1176
1177 slen = strlen(name);
1178 crc = symcrcs[i];
1179
1180 a[count].crc = crc;
1181 a[count].bind = bind;
1182 a[count].symbol = itr;
1183 memcpy(itr, name, slen);
1184 itr[slen] = '\0';
1185 itr += slen + 1;
1186
1187 count++;
1188 }
1189
1190 free(symcrcs);
1191
1192 if (visited_versions == NULL)
1193 return count;
1194
1195 /* add unvisited (module_layout/struct_module) */
1196 ver_off = (const uint8_t *)versions - elf->memory;
1197 for (i = 0; i < vercount; i++) {
1198 const char *name;
1199 uint64_t crc;
1200
1201 if (visited_versions[i] != 0)
1202 continue;
1203
1204 name = elf_get_mem(elf, ver_off + i * verlen + crclen);
1205 slen = strlen(name);
1206 crc = elf_get_uint(elf, ver_off + i * verlen, crclen);
1207
1208 a[count].crc = crc;
1209 a[count].bind = KMOD_SYMBOL_UNDEF;
1210 a[count].symbol = itr;
1211 memcpy(itr, name, slen);
1212 itr[slen] = '\0';
1213 itr += slen + 1;
1214
1215 count++;
1216 }
1217 free(visited_versions);
1218 return count;
1219 }
1220