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