]> git.ipfire.org Git - thirdparty/kmod.git/blob - libkmod/libkmod-elf.c
elf: add get_symbols()
[thirdparty/kmod.git] / libkmod / libkmod-elf.c
1 /*
2 * libkmod - interface to kernel module operations
3 *
4 * Copyright (C) 2011 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, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include <elf.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <assert.h>
25 #include <errno.h>
26
27 #include "libkmod.h"
28 #include "libkmod-private.h"
29
30 enum kmod_elf_class {
31 KMOD_ELF_32 = (1 << 1),
32 KMOD_ELF_64 = (1 << 2),
33 KMOD_ELF_LSB = (1 << 3),
34 KMOD_ELF_MSB = (1 << 4)
35 };
36
37 #ifdef WORDS_BIGENDIAN
38 static const enum kmod_elf_class native_endianess = KMOD_ELF_MSB;
39 #else
40 static const enum kmod_elf_class native_endianess = KMOD_ELF_LSB;
41 #endif
42
43 struct kmod_elf {
44 const uint8_t *memory;
45 uint8_t *changed;
46 uint64_t size;
47 enum kmod_elf_class class;
48 struct kmod_elf_header {
49 struct {
50 uint64_t offset;
51 uint16_t count;
52 uint16_t entry_size;
53 } section;
54 struct {
55 uint16_t section; /* index of the strings section */
56 uint64_t size;
57 uint64_t offset;
58 uint32_t nameoff; /* offset in strings itself */
59 } strings;
60 } header;
61 };
62
63 //#define ENABLE_ELFDBG 1
64
65 #if defined(ENABLE_LOGGING) && defined(ENABLE_ELFDBG)
66 #define ELFDBG(elf, ...) \
67 _elf_dbg(elf, __FILE__, __LINE__, __func__, __VA_ARGS__);
68
69 static inline void _elf_dbg(const struct kmod_elf *elf, const char *fname, unsigned line, const char *func, const char *fmt, ...)
70 {
71 va_list args;
72
73 fprintf(stderr, "ELFDBG-%d%c: %s:%u %s() ",
74 (elf->class & KMOD_ELF_32) ? 32 : 64,
75 (elf->class & KMOD_ELF_MSB) ? 'M' : 'L',
76 fname, line, func);
77 va_start(args, fmt);
78 vfprintf(stderr, fmt, args);
79 va_end(args);
80 }
81 #else
82 #define ELFDBG(elf, ...)
83 #endif
84
85
86 static int elf_identify(const void *memory, uint64_t size)
87 {
88 const uint8_t *p = memory;
89 int class = 0;
90
91 if (size <= EI_NIDENT || memcmp(p, ELFMAG, SELFMAG) != 0)
92 return -ENOEXEC;
93
94 switch (p[EI_CLASS]) {
95 case ELFCLASS32:
96 if (size <= sizeof(Elf32_Ehdr))
97 return -EINVAL;
98 class |= KMOD_ELF_32;
99 break;
100 case ELFCLASS64:
101 if (size <= sizeof(Elf64_Ehdr))
102 return -EINVAL;
103 class |= KMOD_ELF_64;
104 break;
105 default:
106 return -EINVAL;
107 }
108
109 switch (p[EI_DATA]) {
110 case ELFDATA2LSB:
111 class |= KMOD_ELF_LSB;
112 break;
113 case ELFDATA2MSB:
114 class |= KMOD_ELF_MSB;
115 break;
116 default:
117 return -EINVAL;
118 }
119
120 return class;
121 }
122
123 static inline uint64_t elf_get_uint(const struct kmod_elf *elf, uint64_t offset, uint16_t size)
124 {
125 const uint8_t *p;
126 uint64_t ret = 0;
127 size_t i;
128
129 assert(size <= sizeof(uint64_t));
130 assert(offset + size <= elf->size);
131 if (offset + size > elf->size) {
132 ELFDBG(elf, "out of bounds: %"PRIu64" + %"PRIu16" = %"PRIu64"> %"PRIu64" (ELF size)\n",
133 offset, size, offset + size, elf->size);
134 return (uint64_t)-1;
135 }
136
137 p = elf->memory + offset;
138 if (elf->class & KMOD_ELF_MSB) {
139 for (i = 0; i < size; i++)
140 ret = (ret << 8) | p[i];
141 } else {
142 for (i = 1; i <= size; i++)
143 ret = (ret << 8) | p[size - i];
144 }
145
146 ELFDBG(elf, "size=%"PRIu16" offset=%"PRIu64" value=%"PRIu64"\n",
147 size, offset, ret);
148
149 return ret;
150 }
151
152 static inline int elf_set_uint(struct kmod_elf *elf, uint64_t offset, uint64_t size, uint64_t value)
153 {
154 uint8_t *p;
155 size_t i;
156
157 ELFDBG(elf, "size=%"PRIu16" offset=%"PRIu64" value=%"PRIu64" write memory=%p\n",
158 size, offset, value, elf->changed);
159
160 assert(size <= sizeof(uint64_t));
161 assert(offset + size <= elf->size);
162 if (offset + size > elf->size) {
163 ELFDBG(elf, "out of bounds: %"PRIu64" + %"PRIu16" = %"PRIu64"> %"PRIu64" (ELF size)\n",
164 offset, size, offset + size, elf->size);
165 return -1;
166 }
167
168 if (elf->changed == NULL) {
169 elf->changed = malloc(elf->size);
170 if (elf->changed == NULL)
171 return -errno;
172 memcpy(elf->changed, elf->memory, elf->size);
173 elf->memory = elf->changed;
174 ELFDBG(elf, "copied memory to allow writing.\n");
175 }
176
177 p = elf->changed + offset;
178 if (elf->class & KMOD_ELF_MSB) {
179 for (i = 1; i <= size; i++) {
180 p[size - i] = value & 0xff;
181 value = (value & 0xffffffffffffff00) >> 8;
182 }
183 } else {
184 for (i = 0; i < size; i++) {
185 p[i] = value & 0xff;
186 value = (value & 0xffffffffffffff00) >> 8;
187 }
188 }
189
190 return 0;
191 }
192
193 static inline const void *elf_get_mem(const struct kmod_elf *elf, uint64_t offset)
194 {
195 assert(offset < elf->size);
196 if (offset >= elf->size) {
197 ELFDBG(elf, "out-of-bounds: %"PRIu64" >= %"PRIu64" (ELF size)\n",
198 offset, elf->size);
199 return NULL;
200 }
201 return elf->memory + offset;
202 }
203
204 static inline const void *elf_get_section_header(const struct kmod_elf *elf, uint16_t idx)
205 {
206 assert(idx != SHN_UNDEF);
207 assert(idx < elf->header.section.count);
208 if (idx == SHN_UNDEF || idx >= elf->header.section.count) {
209 ELFDBG(elf, "invalid section number: %"PRIu16", last=%"PRIu16"\n",
210 idx, elf->header.section.count);
211 return NULL;
212 }
213 return elf_get_mem(elf, elf->header.section.offset +
214 idx * elf->header.section.entry_size);
215 }
216
217 static inline int elf_get_section_info(const struct kmod_elf *elf, uint16_t idx, uint64_t *offset, uint64_t *size, uint32_t *nameoff)
218 {
219 const uint8_t *p = elf_get_section_header(elf, idx);
220 uint64_t min_size, off = p - elf->memory;
221
222 if (p == NULL) {
223 ELFDBG(elf, "no section at %"PRIu16"\n", idx);
224 *offset = 0;
225 *size = 0;
226 *nameoff = 0;
227 return -EINVAL;
228 }
229
230 #define READV(field) \
231 elf_get_uint(elf, off + offsetof(typeof(*hdr), field), sizeof(hdr->field))
232
233 if (elf->class & KMOD_ELF_32) {
234 const Elf32_Shdr *hdr = (const Elf32_Shdr *)p;
235 *size = READV(sh_size);
236 *offset = READV(sh_offset);
237 *nameoff = READV(sh_name);
238 } else {
239 const Elf64_Shdr *hdr = (const Elf64_Shdr *)p;
240 *size = READV(sh_size);
241 *offset = READV(sh_offset);
242 *nameoff = READV(sh_name);
243 }
244 #undef READV
245
246 min_size = *offset + *size;
247 if (min_size > elf->size) {
248 ELFDBG(elf, "out-of-bounds: %"PRIu64" >= %"PRIu64" (ELF size)\n",
249 min_size, elf->size);
250 return -EINVAL;
251 }
252
253 ELFDBG(elf, "section=%"PRIu16" is: offset=%"PRIu64" size=%"PRIu64" nameoff=%"PRIu32"\n",
254 idx, *offset, *size, *nameoff);
255
256 return 0;
257 }
258
259 static const char *elf_get_strings_section(const struct kmod_elf *elf, uint64_t *size)
260 {
261 *size = elf->header.strings.size;
262 return elf_get_mem(elf, elf->header.strings.offset);
263 }
264
265 struct kmod_elf *kmod_elf_new(const void *memory, off_t size)
266 {
267 struct kmod_elf *elf;
268 size_t hdr_size, shdr_size, min_size;
269 int class;
270
271 assert(sizeof(uint16_t) == sizeof(Elf32_Half));
272 assert(sizeof(uint16_t) == sizeof(Elf64_Half));
273 assert(sizeof(uint32_t) == sizeof(Elf32_Word));
274 assert(sizeof(uint32_t) == sizeof(Elf64_Word));
275
276 class = elf_identify(memory, size);
277 if (class < 0) {
278 errno = -class;
279 return NULL;
280 }
281
282 elf = malloc(sizeof(struct kmod_elf));
283 if (elf == NULL) {
284 return NULL;
285 }
286
287 elf->memory = memory;
288 elf->changed = NULL;
289 elf->size = size;
290 elf->class = class;
291
292 #define READV(field) \
293 elf_get_uint(elf, offsetof(typeof(*hdr), field), sizeof(hdr->field))
294
295 #define LOAD_HEADER \
296 elf->header.section.offset = READV(e_shoff); \
297 elf->header.section.count = READV(e_shnum); \
298 elf->header.section.entry_size = READV(e_shentsize); \
299 elf->header.strings.section = READV(e_shstrndx)
300 if (elf->class & KMOD_ELF_32) {
301 const Elf32_Ehdr *hdr = elf_get_mem(elf, 0);
302 LOAD_HEADER;
303 hdr_size = sizeof(Elf32_Ehdr);
304 shdr_size = sizeof(Elf32_Shdr);
305 } else {
306 const Elf64_Ehdr *hdr = elf_get_mem(elf, 0);
307 LOAD_HEADER;
308 hdr_size = sizeof(Elf64_Ehdr);
309 shdr_size = sizeof(Elf64_Shdr);
310 }
311 #undef LOAD_HEADER
312 #undef READV
313
314 ELFDBG(elf, "section: offset=%"PRIu64" count=%"PRIu16" entry_size=%"PRIu16" strings index=%"PRIu16"\n",
315 elf->header.section.offset,
316 elf->header.section.count,
317 elf->header.section.entry_size,
318 elf->header.strings.section);
319
320 if (elf->header.section.entry_size != shdr_size) {
321 ELFDBG(elf, "unexpected section entry size: %"PRIu16", expected %"PRIu16"\n",
322 elf->header.section.entry_size, shdr_size);
323 goto invalid;
324 }
325 min_size = hdr_size + shdr_size * elf->header.section.count;
326 if (min_size >= elf->size) {
327 ELFDBG(elf, "file is too short to hold sections\n");
328 goto invalid;
329 }
330
331 if (elf_get_section_info(elf, elf->header.strings.section,
332 &elf->header.strings.offset,
333 &elf->header.strings.size,
334 &elf->header.strings.nameoff) < 0) {
335 ELFDBG(elf, "could not get strings section\n");
336 goto invalid;
337 } else {
338 uint64_t slen;
339 const char *s = elf_get_strings_section(elf, &slen);
340 if (slen == 0 || s[slen - 1] != '\0') {
341 ELFDBG(elf, "strings section does not ends with \\0\n");
342 goto invalid;
343 }
344 }
345
346 return elf;
347
348 invalid:
349 free(elf);
350 errno = EINVAL;
351 return NULL;
352 }
353
354 void kmod_elf_unref(struct kmod_elf *elf)
355 {
356 free(elf->changed);
357 free(elf);
358 }
359
360 const void *kmod_elf_get_memory(const struct kmod_elf *elf)
361 {
362 return elf->memory;
363 }
364
365 static int kmod_elf_get_section(const struct kmod_elf *elf, const char *section, const void **buf, uint64_t *buf_size)
366 {
367 uint64_t nameslen;
368 const char *names = elf_get_strings_section(elf, &nameslen);
369 uint16_t i;
370
371 *buf = NULL;
372 *buf_size = 0;
373
374 for (i = 1; i < elf->header.section.count; i++) {
375 uint64_t off, size;
376 uint32_t nameoff;
377 const char *n;
378 int err = elf_get_section_info(elf, i, &off, &size, &nameoff);
379 if (err < 0)
380 continue;
381 if (nameoff >= nameslen)
382 continue;
383 n = names + nameoff;
384 if (!streq(section, n))
385 continue;
386
387 *buf = elf_get_mem(elf, off);
388 *buf_size = size;
389 return 0;
390 }
391
392 return -ENOENT;
393 }
394
395 /* array will be allocated with strings in a single malloc, just free *array */
396 int kmod_elf_get_strings(const struct kmod_elf *elf, const char *section, char ***array)
397 {
398 size_t i, j, count;
399 uint64_t size;
400 const void *buf;
401 const char *strings;
402 char *s, **a;
403 int err;
404
405 *array = NULL;
406
407 err = kmod_elf_get_section(elf, section, &buf, &size);
408 if (err < 0)
409 return err;
410
411 strings = buf;
412 if (strings == NULL || size == 0)
413 return 0;
414
415 /* skip zero padding */
416 while (strings[0] == '\0' && size > 1) {
417 strings++;
418 size--;
419 }
420
421 if (size <= 1)
422 return 0;
423
424 for (i = 0, count = 0; i < size; ) {
425 if (strings[i] != '\0') {
426 i++;
427 continue;
428 }
429
430 while (strings[i] == '\0' && i < size)
431 i++;
432
433 count++;
434 }
435
436 if (strings[i - 1] != '\0')
437 count++;
438
439 *array = a = malloc(size + 1 + sizeof(char *) * (count + 1));
440 if (*array == NULL)
441 return -errno;
442
443 s = (char *)(a + count + 1);
444 memcpy(s, strings, size);
445
446 /* make sure the last string is NULL-terminated */
447 s[size] = '\0';
448 a[count] = NULL;
449 a[0] = s;
450
451 for (i = 0, j = 1; j < count && i < size; ) {
452 if (s[i] != '\0') {
453 i++;
454 continue;
455 }
456
457 while (strings[i] == '\0' && i < size)
458 i++;
459
460 a[j] = &s[i];
461 j++;
462 }
463
464 return count;
465 }
466
467 /* array will be allocated with strings in a single malloc, just free *array */
468 int kmod_elf_get_modversions(const struct kmod_elf *elf, struct kmod_modversion **array)
469 {
470 size_t off, offcrc, slen;
471 uint64_t size;
472 struct kmod_modversion *a;
473 const void *buf;
474 char *itr;
475 int i, count, err;
476 struct kmod_modversion32 {
477 uint32_t crc;
478 char name[64 - sizeof(uint32_t)];
479 };
480 struct kmod_modversion64 {
481 uint64_t crc;
482 char name[64 - sizeof(uint64_t)];
483 };
484 #define MODVERSION_SEC_SIZE (sizeof(struct kmod_modversion64))
485
486 assert(sizeof(struct kmod_modversion64) ==
487 sizeof(struct kmod_modversion32));
488
489 if (elf->class == KMOD_ELF_32)
490 offcrc = sizeof(uint32_t);
491 else
492 offcrc = sizeof(uint64_t);
493
494 *array = NULL;
495
496 err = kmod_elf_get_section(elf, "__versions", &buf, &size);
497 if (err < 0)
498 return err;
499
500 if (buf == NULL || size == 0)
501 return 0;
502
503 if (size % MODVERSION_SEC_SIZE != 0)
504 return -EINVAL;
505
506 count = size / MODVERSION_SEC_SIZE;
507
508 off = (const uint8_t *)buf - elf->memory;
509 slen = 0;
510
511 for (i = 0; i < count; i++, off += MODVERSION_SEC_SIZE) {
512 const char *symbol = elf_get_mem(elf, off + offcrc);
513
514 if (symbol[0] == '.')
515 symbol++;
516
517 slen += strlen(symbol) + 1;
518 }
519
520 *array = a = malloc(sizeof(struct kmod_modversion) * count + slen);
521 if (*array == NULL)
522 return -errno;
523
524 itr = (char *)(a + count);
525 off = (const uint8_t *)buf - elf->memory;
526
527 for (i = 0; i < count; i++, off += MODVERSION_SEC_SIZE) {
528 uint64_t crc = elf_get_uint(elf, off, offcrc);
529 const char *symbol = elf_get_mem(elf, off + offcrc);
530 size_t symbollen;
531
532 if (symbol[0] == '.')
533 symbol++;
534
535 a[i].crc = crc;
536 a[i].symbol = itr;
537 symbollen = strlen(symbol) + 1;
538 memcpy(itr, symbol, symbollen);
539 itr += symbollen;
540 }
541
542 return count;
543 }
544
545 int kmod_elf_strip_section(struct kmod_elf *elf, const char *section)
546 {
547 uint64_t size, off;
548 const void *buf;
549 int err = kmod_elf_get_section(elf, section, &buf, &size);
550 if (err < 0)
551 return err;
552
553 off = (const uint8_t *)buf - elf->memory;
554
555 #define WRITEV(field, value) \
556 elf_set_uint(elf, off + offsetof(typeof(*hdr), field), sizeof(hdr->field), value)
557 if (elf->class & KMOD_ELF_32) {
558 const Elf32_Shdr *hdr = buf;
559 uint32_t val = ~(uint32_t)SHF_ALLOC;
560 return WRITEV(sh_flags, val);
561 } else {
562 const Elf64_Shdr *hdr = buf;
563 uint64_t val = ~(uint64_t)SHF_ALLOC;
564 return WRITEV(sh_flags, val);
565 }
566 #undef WRITEV
567 }
568
569 int kmod_elf_strip_vermagic(struct kmod_elf *elf)
570 {
571 uint64_t i, size;
572 const void *buf;
573 const char *strings;
574 int err;
575
576 err = kmod_elf_get_section(elf, ".modinfo", &buf, &size);
577 if (err < 0)
578 return err;
579 strings = buf;
580 if (strings == NULL || size == 0)
581 return 0;
582
583 /* skip zero padding */
584 while (strings[0] == '\0' && size > 1) {
585 strings++;
586 size--;
587 }
588 if (size <= 1)
589 return 0;
590
591 for (i = 0; i < size; i++) {
592 const char *s;
593 size_t off, len;
594
595 if (strings[i] == '\0')
596 continue;
597 if (i + 1 >= size)
598 continue;
599
600 s = strings + i;
601 len = sizeof("vermagic=") - 1;
602 if (i + len >= size)
603 continue;
604 if (strncmp(s, "vermagic=", len) != 0) {
605 i += strlen(s);
606 continue;
607 }
608 s += len;
609 off = (const uint8_t *)s - elf->memory;
610
611 if (elf->changed == NULL) {
612 elf->changed = malloc(elf->size);
613 if (elf->changed == NULL)
614 return -errno;
615 memcpy(elf->changed, elf->memory, elf->size);
616 elf->memory = elf->changed;
617 ELFDBG(elf, "copied memory to allow writing.\n");
618 }
619
620 len = strlen(s);
621 ELFDBG(elf, "clear .modinfo vermagic \"%s\" (%zd bytes)\n",
622 s, len);
623 memset(elf->changed + off, '\0', len);
624 return 0;
625 }
626
627 ELFDBG(elf, "no vermagic found in .modinfo\n");
628 return -ENOENT;
629 }
630
631
632 static int kmod_elf_get_symbols_symtab(const struct kmod_elf *elf, struct kmod_modversion **array)
633 {
634 uint64_t i, last, size;
635 const void *buf;
636 const char *strings;
637 char *itr;
638 struct kmod_modversion *a;
639 int count, err;
640
641 *array = NULL;
642
643 err = kmod_elf_get_section(elf, "__ksymtab_strings", &buf, &size);
644 if (err < 0)
645 return err;
646 strings = buf;
647 if (strings == NULL || size == 0)
648 return 0;
649
650 /* skip zero padding */
651 while (strings[0] == '\0' && size > 1) {
652 strings++;
653 size--;
654 }
655 if (size <= 1)
656 return 0;
657
658 last = 0;
659 for (i = 0, count = 0; i < size; i++) {
660 if (strings[i] == '\0') {
661 if (last == i) {
662 last = i + 1;
663 continue;
664 }
665 count++;
666 last = i + 1;
667 }
668 }
669 if (strings[i - 1] != '\0')
670 count++;
671
672 *array = a = malloc(size + 1 + sizeof(struct kmod_modversion) * count);
673 if (*array == NULL)
674 return -errno;
675
676 itr = (char *)(a + count);
677 last = 0;
678 for (i = 0, count = 0; i < size; i++) {
679 if (strings[i] == '\0') {
680 size_t slen = i - last;
681 if (last == i) {
682 last = i + 1;
683 continue;
684 }
685 a[count].crc = 0;
686 a[count].symbol = itr;
687 memcpy(itr, strings + last, slen);
688 itr[slen] = '\0';
689 itr += slen + 1;
690 count++;
691 last = i + 1;
692 }
693 }
694 if (strings[i - 1] != '\0') {
695 size_t slen = i - last;
696 a[count].crc = 0;
697 a[count].symbol = itr;
698 memcpy(itr, strings + last, slen);
699 itr[slen] = '\0';
700 itr += slen + 1;
701 count++;
702 }
703
704 return count;
705 }
706
707 /* array will be allocated with strings in a single malloc, just free *array */
708 int kmod_elf_get_symbols(const struct kmod_elf *elf, struct kmod_modversion **array)
709 {
710 static const char crc_str[] = "__crc_";
711 static const size_t crc_strlen = sizeof(crc_str) - 1;
712 uint64_t strtablen, symtablen, str_off, sym_off;
713 const void *strtab, *symtab;
714 struct kmod_modversion *a;
715 char *itr;
716 size_t slen, symlen;
717 int i, count, symcount, err;
718
719 err = kmod_elf_get_section(elf, ".strtab", &strtab, &strtablen);
720 if (err < 0) {
721 ELFDBG(elf, "no .strtab found.\n");
722 goto fallback;
723 }
724
725 err = kmod_elf_get_section(elf, ".symtab", &symtab, &symtablen);
726 if (err < 0) {
727 ELFDBG(elf, "no .symtab found.\n");
728 goto fallback;
729 }
730
731 if (elf->class & KMOD_ELF_32)
732 symlen = sizeof(Elf32_Sym);
733 else
734 symlen = sizeof(Elf64_Sym);
735
736 if (symtablen % symlen != 0) {
737 ELFDBG(elf, "unexpected .symtab of length %"PRIu64", not multiple of %"PRIu64" as expected.\n", symtablen, symlen);
738 goto fallback;
739 }
740
741 symcount = symtablen / symlen;
742 count = 0;
743 slen = 0;
744 str_off = (const uint8_t *)strtab - elf->memory;
745 sym_off = (const uint8_t *)symtab - elf->memory + symlen;
746 for (i = 1; i < symcount; i++, sym_off += symlen) {
747 const char *name;
748 uint32_t name_off;
749
750 #define READV(field) \
751 elf_get_uint(elf, sym_off + offsetof(typeof(*s), field),\
752 sizeof(s->field))
753 if (elf->class & KMOD_ELF_32) {
754 Elf32_Sym *s;
755 name_off = READV(st_name);
756 } else {
757 Elf64_Sym *s;
758 name_off = READV(st_name);
759 }
760 #undef READV
761 if (name_off >= strtablen) {
762 ELFDBG(elf, ".strtab is %"PRIu64" bytes, but .symtab entry %d wants to access offset %"PRIu32".\n", strtablen, i, name_off);
763 goto fallback;
764 }
765
766 name = elf_get_mem(elf, str_off + name_off);
767
768 if (strncmp(name, crc_str, crc_strlen) != 0)
769 continue;
770 slen += strlen(name + crc_strlen) + 1;
771 count++;
772 }
773
774 if (count == 0)
775 goto fallback;
776
777 *array = a = malloc(sizeof(struct kmod_modversion) * count + slen);
778 if (*array == NULL)
779 return -errno;
780
781 itr = (char *)(a + count);
782 count = 0;
783 str_off = (const uint8_t *)strtab - elf->memory;
784 sym_off = (const uint8_t *)symtab - elf->memory + symlen;
785 for (i = 1; i < symcount; i++, sym_off += symlen) {
786 const char *name;
787 uint32_t name_off;
788 uint64_t crc;
789
790 #define READV(field) \
791 elf_get_uint(elf, sym_off + offsetof(typeof(*s), field),\
792 sizeof(s->field))
793 if (elf->class & KMOD_ELF_32) {
794 Elf32_Sym *s;
795 name_off = READV(st_name);
796 crc = READV(st_value);
797 } else {
798 Elf64_Sym *s;
799 name_off = READV(st_name);
800 crc = READV(st_value);
801 }
802 #undef READV
803 name = elf_get_mem(elf, str_off + name_off);
804 if (strncmp(name, crc_str, crc_strlen) != 0)
805 continue;
806 name += crc_strlen;
807
808 a[count].crc = crc;
809 a[count].symbol = itr;
810 slen = strlen(name);
811 memcpy(itr, name, slen);
812 itr[slen] = '\0';
813 itr += slen + 1;
814 count++;
815 }
816 return count;
817
818 fallback:
819 ELFDBG(elf, "Falling back to __ksymtab_strings!\n");
820 return kmod_elf_get_symbols_symtab(elf, array);
821 }