1 /* mod-extract.c: module extractor for signing
3 * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
22 #include <asm/byteorder.h>
24 void extract_elf64(void *buffer, size_t size, Elf64_Ehdr *hdr);
25 void extract_elf32(void *buffer, size_t size, Elf32_Ehdr *hdr);
28 uint16_t (*get16)(const uint16_t *);
29 uint32_t (*get32)(const uint32_t *);
30 uint64_t (*get64)(const uint64_t *);
31 void (*set16)(uint16_t *, uint16_t);
32 void (*set32)(uint32_t *, uint32_t);
33 void (*set64)(uint64_t *, uint64_t);
36 uint16_t get16_le(const uint16_t *p) { return __le16_to_cpu(*p); }
37 uint32_t get32_le(const uint32_t *p) { return __le32_to_cpu(*p); }
38 uint64_t get64_le(const uint64_t *p) { return __le64_to_cpu(*p); }
39 uint16_t get16_be(const uint16_t *p) { return __be16_to_cpu(*p); }
40 uint32_t get32_be(const uint32_t *p) { return __be32_to_cpu(*p); }
41 uint64_t get64_be(const uint64_t *p) { return __be64_to_cpu(*p); }
43 void set16_le(uint16_t *p, uint16_t n) { *p = __cpu_to_le16(n); }
44 void set32_le(uint32_t *p, uint32_t n) { *p = __cpu_to_le32(n); }
45 void set64_le(uint64_t *p, uint64_t n) { *p = __cpu_to_le64(n); }
46 void set16_be(uint16_t *p, uint16_t n) { *p = __cpu_to_be16(n); }
47 void set32_be(uint32_t *p, uint32_t n) { *p = __cpu_to_be32(n); }
48 void set64_be(uint64_t *p, uint64_t n) { *p = __cpu_to_be64(n); }
50 const struct byteorder byteorder_le = {
51 get16_le, get32_le, get64_le,
52 set16_le, set32_le, set64_le
54 const struct byteorder byteorder_be = {
55 get16_be, get32_be, get64_be,
56 set16_be, set32_be, set64_be
58 const struct byteorder *order;
60 uint16_t get16(const uint16_t *p) { return order->get16(p); }
61 uint32_t get32(const uint32_t *p) { return order->get32(p); }
62 uint64_t get64(const uint64_t *p) { return order->get64(p); }
63 void set16(uint16_t *p, uint16_t n) { order->set16(p, n); }
64 void set32(uint32_t *p, uint32_t n) { order->set32(p, n); }
65 void set64(uint64_t *p, uint64_t n) { order->set64(p, n); }
70 void write_out(const void *data, size_t size)
72 const uint8_t *p = data;
75 for (loop = 0; loop < size; loop++) {
80 if (fwrite(data, 1, size, outfd) != size) {
86 #define write_out_val(VAL) write_out(&(VAL), sizeof(VAL))
90 void verbose(const char *fmt, ...) __attribute__((format(printf,1,2)));
91 void verbose(const char *fmt, ...)
102 void usage(void) __attribute__((noreturn));
105 fprintf(stderr, "Usage: mod-extract [-v] <modulefile> <extractfile>\n");
112 int main(int argc, char **argv)
121 while (argc > 1 && strcmp("-v", argv[1]) == 0) {
130 /* map the module into memory */
131 fd = open(argv[1], O_RDONLY);
133 perror("open input");
137 if (fstat(fd, &st) < 0) {
144 buffer = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
145 if (buffer == MAP_FAILED) {
151 perror("close input");
155 /* check it's an ELF object */
159 if (hdr32->e_ident[EI_MAG0] != ELFMAG0 ||
160 hdr32->e_ident[EI_MAG1] != ELFMAG1 ||
161 hdr32->e_ident[EI_MAG2] != ELFMAG2 ||
162 hdr32->e_ident[EI_MAG3] != ELFMAG3
164 fprintf(stderr, "Module does not appear to be ELF\n");
168 /* determine endianness and word size */
169 b64 = (hdr32->e_ident[EI_CLASS] == ELFCLASS64);
170 be = (hdr32->e_ident[EI_DATA] == ELFDATA2MSB);
171 order = be ? &byteorder_be : &byteorder_le;
173 verbose("Module is %s-bit %s-endian\n",
175 be ? "big" : "little");
177 /* open the output file */
178 outfd = fopen(argv[2], "w");
180 perror("open output");
184 /* perform the extraction */
186 extract_elf64(buffer, len, hdr64);
188 extract_elf32(buffer, len, hdr32);
191 if (fclose(outfd) == EOF) {
192 perror("close output");
200 * extract a RELA table
201 * - need to canonicalise the entries in case section addition/removal has
202 * rearranged the symbol table and the section table
204 void extract_elf64_rela(const void *buffer, int secix, int targetix,
205 const Elf64_Rela *relatab, size_t nrels,
206 const Elf64_Sym *symbols, size_t nsyms,
207 const Elf64_Shdr *sections, size_t nsects, int *canonmap,
208 const char *strings, size_t nstrings,
221 } __attribute__((packed)) relocation;
223 const Elf64_Sym *symbol;
226 /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
227 for (loop = 0; loop < nrels; loop++) {
228 Elf64_Section st_shndx;
231 /* decode the relocation */
232 r_info = get64(&relatab[loop].r_info);
233 relocation.r_offset = relatab[loop].r_offset;
234 relocation.r_addend = relatab[loop].r_addend;
235 set32(&relocation.r_type, ELF64_R_TYPE(r_info));
237 if (ELF64_R_SYM(r_info) >= nsyms) {
238 fprintf(stderr, "Invalid symbol ID %lx in relocation %zu\n",
239 ELF64_R_SYM(r_info), loop);
243 /* decode the symbol referenced by the relocation */
244 symbol = &symbols[ELF64_R_SYM(r_info)];
245 relocation.st_info = symbol->st_info;
246 relocation.st_other = symbol->st_other;
247 relocation.st_value = symbol->st_value;
248 relocation.st_size = symbol->st_size;
249 relocation.st_shndx = symbol->st_shndx;
250 st_shndx = get16(&symbol->st_shndx);
252 /* canonicalise the section used by the symbol */
253 if (st_shndx > SHN_UNDEF && st_shndx < nsects)
254 set16(&relocation.st_shndx, canonmap[st_shndx]);
256 write_out_val(relocation);
258 /* undefined symbols must be named if referenced */
259 if (st_shndx == SHN_UNDEF) {
260 const char *name = strings + get32(&symbol->st_name);
261 write_out(name, strlen(name) + 1);
265 verbose("%02x %4d %s [canon]\n", csum, secix, sh_name);
269 * extract a REL table
270 * - need to canonicalise the entries in case section addition/removal has
271 * rearranged the symbol table and the section table
273 void extract_elf64_rel(const void *buffer, int secix, int targetix,
274 const Elf64_Rel *relatab, size_t nrels,
275 const Elf64_Sym *symbols, size_t nsyms,
276 const Elf64_Shdr *sections, size_t nsects, int *canonmap,
277 const char *strings, size_t nstrings,
289 } __attribute__((packed)) relocation;
291 const Elf64_Sym *symbol;
294 /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
295 for (loop = 0; loop < nrels; loop++) {
296 Elf64_Section st_shndx;
299 /* decode the relocation */
300 r_info = get64(&relatab[loop].r_info);
301 relocation.r_offset = relatab[loop].r_offset;
302 set32(&relocation.r_type, ELF64_R_TYPE(r_info));
304 if (ELF64_R_SYM(r_info) >= nsyms) {
305 fprintf(stderr, "Invalid symbol ID %lx in relocation %zi\n",
306 ELF64_R_SYM(r_info), loop);
310 /* decode the symbol referenced by the relocation */
311 symbol = &symbols[ELF64_R_SYM(r_info)];
312 relocation.st_info = symbol->st_info;
313 relocation.st_other = symbol->st_other;
314 relocation.st_value = symbol->st_value;
315 relocation.st_size = symbol->st_size;
316 relocation.st_shndx = symbol->st_shndx;
317 st_shndx = get16(&symbol->st_shndx);
319 /* canonicalise the section used by the symbol */
320 if (st_shndx > SHN_UNDEF && st_shndx < nsects)
321 set16(&relocation.st_shndx, canonmap[st_shndx]);
323 write_out_val(relocation);
325 /* undefined symbols must be named if referenced */
326 if (st_shndx == SHN_UNDEF) {
327 const char *name = strings + get32(&symbol->st_name);
328 write_out(name, strlen(name) + 1);
332 verbose("%02x %4d %s [canon]\n", csum, secix, sh_name);
336 * extract the data from a 64-bit module
338 void extract_elf64(void *buffer, size_t len, Elf64_Ehdr *hdr)
340 const Elf64_Sym *symbols;
341 Elf64_Shdr *sections;
342 const char *secstrings, *strings;
343 size_t nsyms, nstrings;
344 int loop, shnum, *canonlist, *canonmap, canon, changed, tmp;
346 sections = buffer + get64(&hdr->e_shoff);
347 secstrings = buffer + get64(§ions[get16(&hdr->e_shstrndx)].sh_offset);
348 shnum = get16(&hdr->e_shnum);
350 /* find the symbol table and the string table and produce a list of
351 * index numbers of sections that contribute to the kernel's module
354 canonlist = calloc(sizeof(int), shnum * 2);
359 canonmap = canonlist + shnum;
367 for (loop = 1; loop < shnum; loop++) {
368 const char *sh_name = secstrings + get32(§ions[loop].sh_name);
369 Elf64_Word sh_type = get32(§ions[loop].sh_type);
370 Elf64_Xword sh_size = get64(§ions[loop].sh_size);
371 Elf64_Xword sh_flags = get64(§ions[loop].sh_flags);
372 Elf64_Off sh_offset = get64(§ions[loop].sh_offset);
373 void *data = buffer + sh_offset;
375 /* quick sanity check */
376 if (sh_type != SHT_NOBITS && len < sh_offset + sh_size) {
377 fprintf(stderr, "Section goes beyond EOF\n");
381 /* we only need to canonicalise allocatable sections */
382 if (sh_flags & SHF_ALLOC)
383 canonlist[canon++] = loop;
385 /* keep track of certain special sections */
388 if (strcmp(sh_name, ".symtab") == 0) {
390 nsyms = sh_size / sizeof(Elf64_Sym);
395 if (strcmp(sh_name, ".strtab") == 0) {
407 fprintf(stderr, "Couldn't locate symbol table\n");
412 fprintf(stderr, "Couldn't locate strings table\n");
416 /* canonicalise the index numbers of the contributing section */
420 for (loop = 0; loop < canon - 1; loop++) {
421 const char *x = secstrings + get32(§ions[canonlist[loop + 0]].sh_name);
422 const char *y = secstrings + get32(§ions[canonlist[loop + 1]].sh_name);
423 if (strcmp(x, y) > 0) {
424 tmp = canonlist[loop + 0];
425 canonlist[loop + 0] = canonlist[loop + 1];
426 canonlist[loop + 1] = tmp;
433 for (loop = 0; loop < canon; loop++)
434 canonmap[canonlist[loop]] = loop + 1;
436 if (is_verbose > 1) {
437 printf("\nSection canonicalisation map:\n");
438 for (loop = 1; loop < shnum; loop++) {
439 const char *x = secstrings + get32(§ions[loop].sh_name);
440 printf("%4d %s\n", canonmap[loop], x);
443 printf("\nAllocated section list in canonical order:\n");
444 for (loop = 0; loop < canon; loop++) {
445 const char *x = secstrings + get32(§ions[canonlist[loop]].sh_name);
446 printf("%4d %s\n", canonlist[loop], x);
450 memset(canonlist, 0, sizeof(int) * shnum);
452 /* iterate through the section table looking for sections we want to
453 * contribute to the signature */
455 verbose("FILE POS CS SECT NAME\n");
456 verbose("======== == ==== ==============================\n");
458 for (loop = 1; loop < shnum; loop++) {
459 const char *sh_name = secstrings + get32(§ions[loop].sh_name);
460 Elf64_Word sh_type = get32(§ions[loop].sh_type);
461 Elf64_Xword sh_size = get64(§ions[loop].sh_size);
462 Elf64_Xword sh_flags = get64(§ions[loop].sh_flags);
463 Elf64_Word sh_info = get32(§ions[loop].sh_info);
464 Elf64_Off sh_offset = get64(§ions[loop].sh_offset);
465 void *data = buffer + sh_offset;
469 /* include canonicalised relocation sections */
470 if (sh_type == SHT_REL || sh_type == SHT_RELA) {
471 if (sh_info <= 0 && sh_info >= hdr->e_shnum) {
473 "Invalid ELF - REL/RELA sh_info does"
474 " not refer to a valid section\n");
478 if (canonlist[sh_info]) {
481 verbose("%08lx ", ftell(outfd));
483 set32(&xsh_info, canonmap[sh_info]);
485 /* write out selected portions of the section
487 write_out(sh_name, strlen(sh_name));
488 write_out_val(sections[loop].sh_type);
489 write_out_val(sections[loop].sh_flags);
490 write_out_val(sections[loop].sh_size);
491 write_out_val(sections[loop].sh_addralign);
492 write_out_val(xsh_info);
494 if (sh_type == SHT_RELA)
495 extract_elf64_rela(buffer, loop, sh_info,
496 data, sh_size / sizeof(Elf64_Rela),
498 sections, shnum, canonmap,
502 extract_elf64_rel(buffer, loop, sh_info,
503 data, sh_size / sizeof(Elf64_Rel),
505 sections, shnum, canonmap,
513 /* include allocatable loadable sections */
514 if (sh_type != SHT_NOBITS && sh_flags & SHF_ALLOC)
515 goto include_section;
517 /* not this section */
521 verbose("%08lx ", ftell(outfd));
523 /* write out selected portions of the section header */
524 write_out(sh_name, strlen(sh_name));
525 write_out_val(sections[loop].sh_type);
526 write_out_val(sections[loop].sh_flags);
527 write_out_val(sections[loop].sh_size);
528 write_out_val(sections[loop].sh_addralign);
530 /* write out the section data */
531 write_out(data, sh_size);
533 verbose("%02x %4d %s\n", csum, loop, sh_name);
535 /* note the section has been written */
539 verbose("%08lx (%lu bytes csum 0x%02x)\n",
540 ftell(outfd), ftell(outfd), xcsum);
544 * extract a RELA table
545 * - need to canonicalise the entries in case section addition/removal has
546 * rearranged the symbol table and the section table
548 void extract_elf32_rela(const void *buffer, int secix, int targetix,
549 const Elf32_Rela *relatab, size_t nrels,
550 const Elf32_Sym *symbols, size_t nsyms,
551 const Elf32_Shdr *sections, size_t nsects, int *canonmap,
552 const char *strings, size_t nstrings,
565 } __attribute__((packed)) relocation;
567 const Elf32_Sym *symbol;
570 /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
571 for (loop = 0; loop < nrels; loop++) {
572 Elf32_Section st_shndx;
575 /* decode the relocation */
576 r_info = get32(&relatab[loop].r_info);
577 relocation.r_offset = relatab[loop].r_offset;
578 relocation.r_addend = relatab[loop].r_addend;
579 relocation.r_type = ELF32_R_TYPE(r_info);
581 if (ELF32_R_SYM(r_info) >= nsyms) {
582 fprintf(stderr, "Invalid symbol ID %x in relocation %zu\n",
583 ELF32_R_SYM(r_info), loop);
587 /* decode the symbol referenced by the relocation */
588 symbol = &symbols[ELF32_R_SYM(r_info)];
589 relocation.st_info = symbol->st_info;
590 relocation.st_other = symbol->st_other;
591 relocation.st_value = symbol->st_value;
592 relocation.st_size = symbol->st_size;
593 relocation.st_shndx = symbol->st_shndx;
594 st_shndx = get16(&symbol->st_shndx);
596 /* canonicalise the section used by the symbol */
597 if (st_shndx > SHN_UNDEF && st_shndx < nsects)
598 set16(&relocation.st_shndx, canonmap[st_shndx]);
600 write_out_val(relocation);
602 /* undefined symbols must be named if referenced */
603 if (st_shndx == SHN_UNDEF) {
604 const char *name = strings + get32(&symbol->st_name);
605 write_out(name, strlen(name) + 1);
609 verbose("%02x %4d %s [canon]\n", csum, secix, sh_name);
613 * extract a REL table
614 * - need to canonicalise the entries in case section addition/removal has
615 * rearranged the symbol table and the section table
617 void extract_elf32_rel(const void *buffer, int secix, int targetix,
618 const Elf32_Rel *relatab, size_t nrels,
619 const Elf32_Sym *symbols, size_t nsyms,
620 const Elf32_Shdr *sections, size_t nsects, int *canonmap,
621 const char *strings, size_t nstrings,
633 } __attribute__((packed)) relocation;
635 const Elf32_Sym *symbol;
638 /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
639 for (loop = 0; loop < nrels; loop++) {
640 Elf32_Section st_shndx;
643 /* decode the relocation */
644 r_info = get32(&relatab[loop].r_info);
645 relocation.r_offset = relatab[loop].r_offset;
646 relocation.r_type = ELF32_R_TYPE(r_info);
648 if (ELF32_R_SYM(r_info) >= nsyms) {
649 fprintf(stderr, "Invalid symbol ID %x in relocation %zu\n",
650 ELF32_R_SYM(r_info), loop);
654 /* decode the symbol referenced by the relocation */
655 symbol = &symbols[ELF32_R_SYM(r_info)];
656 relocation.st_info = symbol->st_info;
657 relocation.st_other = symbol->st_other;
658 relocation.st_value = symbol->st_value;
659 relocation.st_size = symbol->st_size;
660 relocation.st_shndx = symbol->st_shndx;
661 st_shndx = get16(&symbol->st_shndx);
663 /* canonicalise the section used by the symbol */
664 if (st_shndx > SHN_UNDEF && st_shndx < nsects)
665 set16(&relocation.st_shndx, canonmap[st_shndx]);
667 write_out_val(relocation);
669 /* undefined symbols must be named if referenced */
670 if (st_shndx == SHN_UNDEF) {
671 const char *name = strings + get32(&symbol->st_name);
672 write_out(name, strlen(name) + 1);
676 verbose("%02x %4d %s [canon]\n", csum, secix, sh_name);
680 * extract the data from a 32-bit module
682 void extract_elf32(void *buffer, size_t len, Elf32_Ehdr *hdr)
684 const Elf32_Sym *symbols;
685 Elf32_Shdr *sections;
686 const char *secstrings, *strings;
687 size_t nsyms, nstrings;
688 int loop, shnum, *canonlist, *canonmap, canon, changed, tmp;
690 sections = buffer + get32(&hdr->e_shoff);
691 secstrings = buffer + get32(§ions[get16(&hdr->e_shstrndx)].sh_offset);
692 shnum = get16(&hdr->e_shnum);
694 /* find the symbol table and the string table and produce a list of
695 * index numbers of sections that contribute to the kernel's module
698 canonlist = calloc(sizeof(int), shnum * 2);
703 canonmap = canonlist + shnum;
711 for (loop = 1; loop < shnum; loop++) {
712 const char *sh_name = secstrings + get32(§ions[loop].sh_name);
713 Elf32_Word sh_type = get32(§ions[loop].sh_type);
714 Elf32_Xword sh_size = get32(§ions[loop].sh_size);
715 Elf32_Xword sh_flags = get32(§ions[loop].sh_flags);
716 Elf32_Off sh_offset = get32(§ions[loop].sh_offset);
717 void *data = buffer + sh_offset;
719 /* quick sanity check */
720 if (sh_type != SHT_NOBITS && len < sh_offset + sh_size) {
721 fprintf(stderr, "Section goes beyond EOF\n");
725 /* we only need to canonicalise allocatable sections */
726 if (sh_flags & SHF_ALLOC)
727 canonlist[canon++] = loop;
729 /* keep track of certain special sections */
732 if (strcmp(sh_name, ".symtab") == 0) {
734 nsyms = sh_size / sizeof(Elf32_Sym);
739 if (strcmp(sh_name, ".strtab") == 0) {
751 fprintf(stderr, "Couldn't locate symbol table\n");
756 fprintf(stderr, "Couldn't locate strings table\n");
760 /* canonicalise the index numbers of the contributing section */
764 for (loop = 0; loop < canon - 1; loop++) {
765 const char *x = secstrings + get32(§ions[canonlist[loop + 0]].sh_name);
766 const char *y = secstrings + get32(§ions[canonlist[loop + 1]].sh_name);
767 if (strcmp(x, y) > 0) {
768 tmp = canonlist[loop + 0];
769 canonlist[loop + 0] = canonlist[loop + 1];
770 canonlist[loop + 1] = tmp;
777 for (loop = 0; loop < canon; loop++)
778 canonmap[canonlist[loop]] = loop + 1;
780 if (is_verbose > 1) {
781 printf("\nSection canonicalisation map:\n");
782 for (loop = 1; loop < shnum; loop++) {
783 const char *x = secstrings + get32(§ions[loop].sh_name);
784 printf("%4d %s\n", canonmap[loop], x);
787 printf("\nAllocated section list in canonical order:\n");
788 for (loop = 0; loop < canon; loop++) {
789 const char *x = secstrings + get32(§ions[canonlist[loop]].sh_name);
790 printf("%4d %s\n", canonlist[loop], x);
794 memset(canonlist, 0, sizeof(int) * shnum);
796 /* iterate through the section table looking for sections we want to
797 * contribute to the signature */
799 verbose("FILE POS CS SECT NAME\n");
800 verbose("======== == ==== ==============================\n");
802 for (loop = 1; loop < shnum; loop++) {
803 const char *sh_name = secstrings + get32(§ions[loop].sh_name);
804 Elf32_Word sh_type = get32(§ions[loop].sh_type);
805 Elf32_Xword sh_size = get32(§ions[loop].sh_size);
806 Elf32_Xword sh_flags = get32(§ions[loop].sh_flags);
807 Elf32_Word sh_info = get32(§ions[loop].sh_info);
808 Elf32_Off sh_offset = get32(§ions[loop].sh_offset);
809 void *data = buffer + sh_offset;
813 /* quick sanity check */
814 if (sh_type != SHT_NOBITS && len < sh_offset + sh_size) {
815 fprintf(stderr, "section goes beyond EOF\n");
819 /* include canonicalised relocation sections */
820 if (sh_type == SHT_REL || sh_type == SHT_RELA) {
821 if (sh_info <= 0 && sh_info >= hdr->e_shnum) {
823 "Invalid ELF - REL/RELA sh_info does"
824 " not refer to a valid section\n");
828 if (canonlist[sh_info]) {
831 verbose("%08lx ", ftell(outfd));
833 set32(&xsh_info, canonmap[sh_info]);
835 /* write out selected portions of the section header */
836 write_out(sh_name, strlen(sh_name));
837 write_out_val(sections[loop].sh_type);
838 write_out_val(sections[loop].sh_flags);
839 write_out_val(sections[loop].sh_size);
840 write_out_val(sections[loop].sh_addralign);
841 write_out_val(xsh_info);
843 if (sh_type == SHT_RELA)
844 extract_elf32_rela(buffer, loop, sh_info,
845 data, sh_size / sizeof(Elf32_Rela),
847 sections, shnum, canonmap,
851 extract_elf32_rel(buffer, loop, sh_info,
852 data, sh_size / sizeof(Elf32_Rel),
854 sections, shnum, canonmap,
862 /* include allocatable loadable sections */
863 if (sh_type != SHT_NOBITS && sh_flags & SHF_ALLOC)
864 goto include_section;
866 /* not this section */
870 verbose("%08lx ", ftell(outfd));
872 /* write out selected portions of the section header */
873 write_out(sh_name, strlen(sh_name));
874 write_out_val(sections[loop].sh_type);
875 write_out_val(sections[loop].sh_flags);
876 write_out_val(sections[loop].sh_size);
877 write_out_val(sections[loop].sh_addralign);
879 /* write out the section data */
880 write_out(data, sh_size);
882 verbose("%02x %4d %s\n", csum, loop, sh_name);
884 /* note the section has been written */
888 verbose("%08lx (%lu bytes csum 0x%02x)\n",
889 ftell(outfd), ftell(outfd), xcsum);