+/* mod-extract.c: module extractor for signing
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
-
-
-#include <errno.h>
-#include <error.h>
-#include <fcntl.h>
-#include <stdlib.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdarg.h>
#include <string.h>
-#include <bfd.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <elf.h>
+#include <asm/byteorder.h>
-/* This should be set in a Makefile somehow */
-#define TARGET "i686-pc-linux-gnu"
+void extract_elf64(void *buffer, size_t size, Elf64_Ehdr *hdr);
+void extract_elf32(void *buffer, size_t size, Elf32_Ehdr *hdr);
-static FILE *out;
+struct byteorder {
+ uint16_t (*get16)(const uint16_t *);
+ uint32_t (*get32)(const uint32_t *);
+ uint64_t (*get64)(const uint64_t *);
+ void (*set16)(uint16_t *, uint16_t);
+ void (*set32)(uint32_t *, uint32_t);
+ void (*set64)(uint64_t *, uint64_t);
+};
-static void dump_data(bfd *abfd)
-{
- asection *section;
- bfd_byte *data = 0;
- bfd_size_type size;
- bfd_size_type addr_offset;
- bfd_size_type stop_offset;
- unsigned int opb = bfd_octets_per_byte(abfd);
- unsigned int cksum;
+uint16_t get16_le(const uint16_t *p) { return __le16_to_cpu(*p); }
+uint32_t get32_le(const uint32_t *p) { return __le32_to_cpu(*p); }
+uint64_t get64_le(const uint64_t *p) { return __le64_to_cpu(*p); }
+uint16_t get16_be(const uint16_t *p) { return __be16_to_cpu(*p); }
+uint32_t get32_be(const uint32_t *p) { return __be32_to_cpu(*p); }
+uint64_t get64_be(const uint64_t *p) { return __be64_to_cpu(*p); }
- for (section = abfd->sections; section != NULL; section = section->next) {
- if (section->flags & SEC_HAS_CONTENTS) {
- if (bfd_section_size(abfd, section) == 0)
- continue;
+void set16_le(uint16_t *p, uint16_t n) { *p = __cpu_to_le16(n); }
+void set32_le(uint32_t *p, uint32_t n) { *p = __cpu_to_le32(n); }
+void set64_le(uint64_t *p, uint64_t n) { *p = __cpu_to_le64(n); }
+void set16_be(uint16_t *p, uint16_t n) { *p = __cpu_to_be16(n); }
+void set32_be(uint32_t *p, uint32_t n) { *p = __cpu_to_be32(n); }
+void set64_be(uint64_t *p, uint64_t n) { *p = __cpu_to_be64(n); }
- /* We only care about sections with "text" or "data" in their names */
- if ((strstr(section->name, "text") == NULL) &&
- (strstr(section->name, "data") == NULL))
- continue;
+const struct byteorder byteorder_le = {
+ get16_le, get32_le, get64_le,
+ set16_le, set32_le, set64_le
+};
+const struct byteorder byteorder_be = {
+ get16_be, get32_be, get64_be,
+ set16_be, set32_be, set64_be
+};
+const struct byteorder *order;
- cksum = 0;
+uint16_t get16(const uint16_t *p) { return order->get16(p); }
+uint32_t get32(const uint32_t *p) { return order->get32(p); }
+uint64_t get64(const uint64_t *p) { return order->get64(p); }
+void set16(uint16_t *p, uint16_t n) { order->set16(p, n); }
+void set32(uint32_t *p, uint32_t n) { order->set32(p, n); }
+void set64(uint64_t *p, uint64_t n) { order->set64(p, n); }
- size = bfd_section_size(abfd, section) / opb;
+FILE *outfd;
+uint8_t csum, xcsum;
- printf("Contents of section %s size %lu", section->name, size);
+void write_out(const void *data, size_t size)
+{
+ const uint8_t *p = data;
+ size_t loop;
- data = (bfd_byte *) malloc(size);
+ for (loop = 0; loop < size; loop++) {
+ csum += p[loop];
+ xcsum += p[loop];
+ }
- bfd_get_section_contents(abfd, section, (PTR) data, 0, size);
+ if (fwrite(data, 1, size, outfd) != size) {
+ perror("write");
+ exit(1);
+ }
+}
- stop_offset = size;
+#define write_out_val(VAL) write_out(&(VAL), sizeof(VAL))
- printf(" idata %02x%02x%02x%02x", data[0], data[1], data[2], data[3]);
+int is_verbose;
- for (addr_offset = 0; addr_offset < stop_offset; ++addr_offset) {
- cksum += (unsigned int) data[addr_offset];
- fputc(data[addr_offset], out);
- }
- free (data);
+void verbose(const char *fmt, ...) __attribute__((format(printf,1,2)));
+void verbose(const char *fmt, ...)
+{
+ va_list va;
- printf(" checksum %08x\n", cksum);
- }
+ if (is_verbose) {
+ va_start(va, fmt);
+ vprintf(fmt, va);
+ va_end(va);
}
}
-void set_default_bfd_target(void)
+void usage(void) __attribute__((noreturn));
+void usage(void)
{
- const char *target = TARGET;
-
- if (!bfd_set_default_target(target))
- fprintf(stderr, "can't set BFD default target to `%s': %s", target, bfd_errmsg (bfd_get_error ()));
+ fprintf(stderr, "Usage: mod-extract [-v] <modulefile> <extractfile>\n");
+ exit(2);
}
-int main (int argc, char *argv[])
+/*****************************************************************************/
+/*
+ *
+ */
+int main(int argc, char **argv)
{
- char *in_file;
- char *out_file;
- bfd *file;
- char **matching;
+ struct stat st;
+ Elf32_Ehdr *hdr32;
+ Elf64_Ehdr *hdr64;
+ size_t len;
+ void *buffer;
+ int fd, be, b64;
- if (argc != 3) {
- fprintf(stderr, "%s [infile] [outfile]\n", argv[0]);
- exit(1);
+ while (argc > 1 && strcmp("-v", argv[1]) == 0) {
+ argv++;
+ argc--;
+ is_verbose++;
}
- in_file = argv[1];
- out_file = argv[2];
+ if (argc != 3)
+ usage();
- bfd_init();
- set_default_bfd_target();
+ /* map the module into memory */
+ fd = open(argv[1], O_RDONLY);
+ if (fd < 0) {
+ perror("open input");
+ exit(1);
+ }
+ if (fstat(fd, &st) < 0) {
+ perror("fstat");
+ exit(1);
+ }
-// file = bfd_openr(in_file, "elf32-i386");
- file = bfd_openr(in_file, NULL);
- if (file == NULL) {
- fprintf(stderr, "error \"%s\" trying to open %s\n", strerror(errno), in_file);
+ len = st.st_size;
+
+ buffer = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
+ if (buffer == MAP_FAILED) {
+ perror("mmap");
exit(1);
}
- out = fopen(out_file, "w");
- if (out == NULL) {
- fprintf(stderr, "error \"%s\" trying to create %s\n", strerror(errno), out_file);
+ if (close(fd) < 0) {
+ perror("close input");
exit(1);
}
- if (bfd_check_format_matches(file, bfd_object, &matching)) {
- dump_data (file);
+ /* check it's an ELF object */
+ hdr32 = buffer;
+ hdr64 = buffer;
+
+ if (hdr32->e_ident[EI_MAG0] != ELFMAG0 ||
+ hdr32->e_ident[EI_MAG1] != ELFMAG1 ||
+ hdr32->e_ident[EI_MAG2] != ELFMAG2 ||
+ hdr32->e_ident[EI_MAG3] != ELFMAG3
+ ) {
+ fprintf(stderr, "Module does not appear to be ELF\n");
+ exit(3);
}
- fclose(out);
+ /* determine endianness and word size */
+ b64 = (hdr32->e_ident[EI_CLASS] == ELFCLASS64);
+ be = (hdr32->e_ident[EI_DATA] == ELFDATA2MSB);
+ order = be ? &byteorder_be : &byteorder_le;
+
+ verbose("Module is %s-bit %s-endian\n",
+ b64 ? "64" : "32",
+ be ? "big" : "little");
+
+ /* open the output file */
+ outfd = fopen(argv[2], "w");
+ if (!outfd) {
+ perror("open output");
+ exit(1);
+ }
+
+ /* perform the extraction */
+ if (b64)
+ extract_elf64(buffer, len, hdr64);
+ else
+ extract_elf32(buffer, len, hdr32);
+
+ /* done */
+ if (fclose(outfd) == EOF) {
+ perror("close output");
+ exit(1);
+ }
return 0;
-}
+
+} /* end main() */
+
+/*****************************************************************************/
+/*
+ * extract a RELA table
+ * - need to canonicalise the entries in case section addition/removal has
+ * rearranged the symbol table and the section table
+ */
+void extract_elf64_rela(const void *buffer, int secix, int targetix,
+ const Elf64_Rela *relatab, size_t nrels,
+ const Elf64_Sym *symbols, size_t nsyms,
+ const Elf64_Shdr *sections, size_t nsects, int *canonmap,
+ const char *strings, size_t nstrings,
+ const char *sh_name)
+{
+ struct {
+ uint64_t r_offset;
+ uint64_t r_addend;
+ uint64_t st_value;
+ uint64_t st_size;
+ uint32_t r_type;
+ uint16_t st_shndx;
+ uint8_t st_info;
+ uint8_t st_other;
+
+ } __attribute__((packed)) relocation;
+
+ const Elf64_Sym *symbol;
+ size_t loop;
+
+ /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
+ for (loop = 0; loop < nrels; loop++) {
+ Elf64_Section st_shndx;
+ Elf64_Xword r_info;
+
+ /* decode the relocation */
+ r_info = get64(&relatab[loop].r_info);
+ relocation.r_offset = relatab[loop].r_offset;
+ relocation.r_addend = relatab[loop].r_addend;
+ set32(&relocation.r_type, ELF64_R_TYPE(r_info));
+
+ if (ELF64_R_SYM(r_info) >= nsyms) {
+ fprintf(stderr, "Invalid symbol ID %lx in relocation %zu\n",
+ ELF64_R_SYM(r_info), loop);
+ exit(1);
+ }
+
+ /* decode the symbol referenced by the relocation */
+ symbol = &symbols[ELF64_R_SYM(r_info)];
+ relocation.st_info = symbol->st_info;
+ relocation.st_other = symbol->st_other;
+ relocation.st_value = symbol->st_value;
+ relocation.st_size = symbol->st_size;
+ relocation.st_shndx = symbol->st_shndx;
+ st_shndx = get16(&symbol->st_shndx);
+
+ /* canonicalise the section used by the symbol */
+ if (st_shndx > SHN_UNDEF && st_shndx < nsects)
+ set16(&relocation.st_shndx, canonmap[st_shndx]);
+
+ write_out_val(relocation);
+
+ /* undefined symbols must be named if referenced */
+ if (st_shndx == SHN_UNDEF) {
+ const char *name = strings + get32(&symbol->st_name);
+ write_out(name, strlen(name) + 1);
+ }
+ }
+
+ verbose("%02x %4d %s [canon]\n", csum, secix, sh_name);
+
+} /* end extract_elf64_rela() */
+
+/*****************************************************************************/
+/*
+ * extract a REL table
+ * - need to canonicalise the entries in case section addition/removal has
+ * rearranged the symbol table and the section table
+ */
+void extract_elf64_rel(const void *buffer, int secix, int targetix,
+ const Elf64_Rel *relatab, size_t nrels,
+ const Elf64_Sym *symbols, size_t nsyms,
+ const Elf64_Shdr *sections, size_t nsects, int *canonmap,
+ const char *strings, size_t nstrings,
+ const char *sh_name)
+{
+ struct {
+ uint64_t r_offset;
+ uint64_t st_value;
+ uint64_t st_size;
+ uint32_t r_type;
+ uint16_t st_shndx;
+ uint8_t st_info;
+ uint8_t st_other;
+
+ } __attribute__((packed)) relocation;
+
+ const Elf64_Sym *symbol;
+ size_t loop;
+
+ /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
+ for (loop = 0; loop < nrels; loop++) {
+ Elf64_Section st_shndx;
+ Elf64_Xword r_info;
+
+ /* decode the relocation */
+ r_info = get64(&relatab[loop].r_info);
+ relocation.r_offset = relatab[loop].r_offset;
+ set32(&relocation.r_type, ELF64_R_TYPE(r_info));
+
+ if (ELF64_R_SYM(r_info) >= nsyms) {
+ fprintf(stderr, "Invalid symbol ID %lx in relocation %zi\n",
+ ELF64_R_SYM(r_info), loop);
+ exit(1);
+ }
+
+ /* decode the symbol referenced by the relocation */
+ symbol = &symbols[ELF64_R_SYM(r_info)];
+ relocation.st_info = symbol->st_info;
+ relocation.st_other = symbol->st_other;
+ relocation.st_value = symbol->st_value;
+ relocation.st_size = symbol->st_size;
+ relocation.st_shndx = symbol->st_shndx;
+ st_shndx = get16(&symbol->st_shndx);
+
+ /* canonicalise the section used by the symbol */
+ if (st_shndx > SHN_UNDEF && st_shndx < nsects)
+ set16(&relocation.st_shndx, canonmap[st_shndx]);
+
+ write_out_val(relocation);
+
+ /* undefined symbols must be named if referenced */
+ if (st_shndx == SHN_UNDEF) {
+ const char *name = strings + get32(&symbol->st_name);
+ write_out(name, strlen(name) + 1);
+ }
+ }
+
+ verbose("%02x %4d %s [canon]\n", csum, secix, sh_name);
+
+} /* end extract_elf64_rel() */
+
+/*****************************************************************************/
+/*
+ * extract the data from a 64-bit module
+ */
+void extract_elf64(void *buffer, size_t len, Elf64_Ehdr *hdr)
+{
+ const Elf64_Sym *symbols;
+ Elf64_Shdr *sections;
+ const char *secstrings, *strings;
+ size_t nsyms, nstrings;
+ int loop, shnum, *canonlist, *canonmap, canon, changed, tmp;
+
+ sections = buffer + get64(&hdr->e_shoff);
+ secstrings = buffer + get64(§ions[get16(&hdr->e_shstrndx)].sh_offset);
+ shnum = get16(&hdr->e_shnum);
+
+ /* find the symbol table and the string table and produce a list of
+ * index numbers of sections that contribute to the kernel's module
+ * image
+ */
+ canonlist = calloc(sizeof(int), shnum * 2);
+ if (!canonlist) {
+ perror("calloc");
+ exit(1);
+ }
+ canonmap = canonlist + shnum;
+ canon = 0;
+
+ symbols = NULL;
+ strings = NULL;
+
+ for (loop = 1; loop < shnum; loop++) {
+ const char *sh_name = secstrings + get32(§ions[loop].sh_name);
+ Elf64_Word sh_type = get32(§ions[loop].sh_type);
+ Elf64_Xword sh_size = get64(§ions[loop].sh_size);
+ Elf64_Xword sh_flags = get64(§ions[loop].sh_flags);
+ Elf64_Off sh_offset = get64(§ions[loop].sh_offset);
+ void *data = buffer + sh_offset;
+
+ /* quick sanity check */
+ if (sh_type != SHT_NOBITS && len < sh_offset + sh_size) {
+ fprintf(stderr, "Section goes beyond EOF\n");
+ exit(3);
+ }
+
+ /* we only need to canonicalise allocatable sections */
+ if (sh_flags & SHF_ALLOC)
+ canonlist[canon++] = loop;
+
+ /* keep track of certain special sections */
+ switch (sh_type) {
+ case SHT_SYMTAB:
+ if (strcmp(sh_name, ".symtab") == 0) {
+ symbols = data;
+ nsyms = sh_size / sizeof(Elf64_Sym);
+ }
+ break;
+
+ case SHT_STRTAB:
+ if (strcmp(sh_name, ".strtab") == 0) {
+ strings = data;
+ nstrings = sh_size;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (!symbols) {
+ fprintf(stderr, "Couldn't locate symbol table\n");
+ exit(3);
+ }
+
+ if (!strings) {
+ fprintf(stderr, "Couldn't locate strings table\n");
+ exit(3);
+ }
+
+ /* canonicalise the index numbers of the contributing section */
+ do {
+ changed = 0;
+
+ for (loop = 0; loop < canon - 1; loop++) {
+ const char *x = secstrings + get32(§ions[canonlist[loop + 0]].sh_name);
+ const char *y = secstrings + get32(§ions[canonlist[loop + 1]].sh_name);
+ if (strcmp(x, y) > 0) {
+ tmp = canonlist[loop + 0];
+ canonlist[loop + 0] = canonlist[loop + 1];
+ canonlist[loop + 1] = tmp;
+ changed = 1;
+ }
+ }
+
+ } while(changed);
+
+ for (loop = 0; loop < canon; loop++)
+ canonmap[canonlist[loop]] = loop + 1;
+
+ if (is_verbose > 1) {
+ printf("\nSection canonicalisation map:\n");
+ for (loop = 1; loop < shnum; loop++) {
+ const char *x = secstrings + get32(§ions[loop].sh_name);
+ printf("%4d %s\n", canonmap[loop], x);
+ }
+
+ printf("\nAllocated section list in canonical order:\n");
+ for (loop = 0; loop < canon; loop++) {
+ const char *x = secstrings + get32(§ions[canonlist[loop]].sh_name);
+ printf("%4d %s\n", canonlist[loop], x);
+ }
+ }
+
+ memset(canonlist, 0, sizeof(int) * shnum);
+
+ /* iterate through the section table looking for sections we want to
+ * contribute to the signature */
+ verbose("\n");
+ verbose("FILE POS CS SECT NAME\n");
+ verbose("======== == ==== ==============================\n");
+
+ for (loop = 1; loop < shnum; loop++) {
+ const char *sh_name = secstrings + get32(§ions[loop].sh_name);
+ Elf64_Word sh_type = get32(§ions[loop].sh_type);
+ Elf64_Xword sh_size = get64(§ions[loop].sh_size);
+ Elf64_Xword sh_flags = get64(§ions[loop].sh_flags);
+ Elf64_Word sh_info = get32(§ions[loop].sh_info);
+ Elf64_Off sh_offset = get64(§ions[loop].sh_offset);
+ void *data = buffer + sh_offset;
+
+ csum = 0;
+
+ /* include canonicalised relocation sections */
+ if (sh_type == SHT_REL || sh_type == SHT_RELA) {
+ if (sh_info <= 0 && sh_info >= hdr->e_shnum) {
+ fprintf(stderr,
+ "Invalid ELF - REL/RELA sh_info does"
+ " not refer to a valid section\n");
+ exit(3);
+ }
+
+ if (canonlist[sh_info]) {
+ Elf32_Word xsh_info;
+
+ verbose("%08lx ", ftell(outfd));
+
+ set32(&xsh_info, canonmap[sh_info]);
+
+ /* write out selected portions of the section
+ * header */
+ write_out(sh_name, strlen(sh_name));
+ write_out_val(sections[loop].sh_type);
+ write_out_val(sections[loop].sh_flags);
+ write_out_val(sections[loop].sh_size);
+ write_out_val(sections[loop].sh_addralign);
+ write_out_val(xsh_info);
+
+ if (sh_type == SHT_RELA)
+ extract_elf64_rela(buffer, loop, sh_info,
+ data, sh_size / sizeof(Elf64_Rela),
+ symbols, nsyms,
+ sections, shnum, canonmap,
+ strings, nstrings,
+ sh_name);
+ else
+ extract_elf64_rel(buffer, loop, sh_info,
+ data, sh_size / sizeof(Elf64_Rel),
+ symbols, nsyms,
+ sections, shnum, canonmap,
+ strings, nstrings,
+ sh_name);
+ }
+
+ continue;
+ }
+
+ /* include allocatable loadable sections */
+ if (sh_type != SHT_NOBITS && sh_flags & SHF_ALLOC)
+ goto include_section;
+
+ /* not this section */
+ continue;
+
+ include_section:
+ verbose("%08lx ", ftell(outfd));
+
+ /* write out selected portions of the section header */
+ write_out(sh_name, strlen(sh_name));
+ write_out_val(sections[loop].sh_type);
+ write_out_val(sections[loop].sh_flags);
+ write_out_val(sections[loop].sh_size);
+ write_out_val(sections[loop].sh_addralign);
+
+ /* write out the section data */
+ write_out(data, sh_size);
+
+ verbose("%02x %4d %s\n", csum, loop, sh_name);
+
+ /* note the section has been written */
+ canonlist[loop] = 1;
+ }
+
+ verbose("%08lx (%lu bytes csum 0x%02x)\n",
+ ftell(outfd), ftell(outfd), xcsum);
+
+} /* end extract_elf64() */
+
+/*****************************************************************************/
+/*
+ * extract a RELA table
+ * - need to canonicalise the entries in case section addition/removal has
+ * rearranged the symbol table and the section table
+ */
+void extract_elf32_rela(const void *buffer, int secix, int targetix,
+ const Elf32_Rela *relatab, size_t nrels,
+ const Elf32_Sym *symbols, size_t nsyms,
+ const Elf32_Shdr *sections, size_t nsects, int *canonmap,
+ const char *strings, size_t nstrings,
+ const char *sh_name)
+{
+ struct {
+ uint32_t r_offset;
+ uint32_t r_addend;
+ uint32_t st_value;
+ uint32_t st_size;
+ uint16_t st_shndx;
+ uint8_t r_type;
+ uint8_t st_info;
+ uint8_t st_other;
+
+ } __attribute__((packed)) relocation;
+
+ const Elf32_Sym *symbol;
+ size_t loop;
+
+ /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
+ for (loop = 0; loop < nrels; loop++) {
+ Elf32_Section st_shndx;
+ Elf32_Word r_info;
+
+ /* decode the relocation */
+ r_info = get32(&relatab[loop].r_info);
+ relocation.r_offset = relatab[loop].r_offset;
+ relocation.r_addend = relatab[loop].r_addend;
+ relocation.r_type = ELF32_R_TYPE(r_info);
+
+ if (ELF32_R_SYM(r_info) >= nsyms) {
+ fprintf(stderr, "Invalid symbol ID %x in relocation %zu\n",
+ ELF32_R_SYM(r_info), loop);
+ exit(1);
+ }
+
+ /* decode the symbol referenced by the relocation */
+ symbol = &symbols[ELF32_R_SYM(r_info)];
+ relocation.st_info = symbol->st_info;
+ relocation.st_other = symbol->st_other;
+ relocation.st_value = symbol->st_value;
+ relocation.st_size = symbol->st_size;
+ relocation.st_shndx = symbol->st_shndx;
+ st_shndx = get16(&symbol->st_shndx);
+
+ /* canonicalise the section used by the symbol */
+ if (st_shndx > SHN_UNDEF && st_shndx < nsects)
+ set16(&relocation.st_shndx, canonmap[st_shndx]);
+
+ write_out_val(relocation);
+
+ /* undefined symbols must be named if referenced */
+ if (st_shndx == SHN_UNDEF) {
+ const char *name = strings + get32(&symbol->st_name);
+ write_out(name, strlen(name) + 1);
+ }
+ }
+
+ verbose("%02x %4d %s [canon]\n", csum, secix, sh_name);
+
+} /* end extract_elf32_rela() */
+
+/*****************************************************************************/
+/*
+ * extract a REL table
+ * - need to canonicalise the entries in case section addition/removal has
+ * rearranged the symbol table and the section table
+ */
+void extract_elf32_rel(const void *buffer, int secix, int targetix,
+ const Elf32_Rel *relatab, size_t nrels,
+ const Elf32_Sym *symbols, size_t nsyms,
+ const Elf32_Shdr *sections, size_t nsects, int *canonmap,
+ const char *strings, size_t nstrings,
+ const char *sh_name)
+{
+ struct {
+ uint32_t r_offset;
+ uint32_t st_value;
+ uint32_t st_size;
+ uint16_t st_shndx;
+ uint8_t r_type;
+ uint8_t st_info;
+ uint8_t st_other;
+
+ } __attribute__((packed)) relocation;
+
+ const Elf32_Sym *symbol;
+ size_t loop;
+
+ /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
+ for (loop = 0; loop < nrels; loop++) {
+ Elf32_Section st_shndx;
+ Elf32_Word r_info;
+
+ /* decode the relocation */
+ r_info = get32(&relatab[loop].r_info);
+ relocation.r_offset = relatab[loop].r_offset;
+ relocation.r_type = ELF32_R_TYPE(r_info);
+
+ if (ELF32_R_SYM(r_info) >= nsyms) {
+ fprintf(stderr, "Invalid symbol ID %x in relocation %zu\n",
+ ELF32_R_SYM(r_info), loop);
+ exit(1);
+ }
+
+ /* decode the symbol referenced by the relocation */
+ symbol = &symbols[ELF32_R_SYM(r_info)];
+ relocation.st_info = symbol->st_info;
+ relocation.st_other = symbol->st_other;
+ relocation.st_value = symbol->st_value;
+ relocation.st_size = symbol->st_size;
+ relocation.st_shndx = symbol->st_shndx;
+ st_shndx = get16(&symbol->st_shndx);
+
+ /* canonicalise the section used by the symbol */
+ if (st_shndx > SHN_UNDEF && st_shndx < nsects)
+ set16(&relocation.st_shndx, canonmap[st_shndx]);
+
+ write_out_val(relocation);
+
+ /* undefined symbols must be named if referenced */
+ if (st_shndx == SHN_UNDEF) {
+ const char *name = strings + get32(&symbol->st_name);
+ write_out(name, strlen(name) + 1);
+ }
+ }
+
+ verbose("%02x %4d %s [canon]\n", csum, secix, sh_name);
+
+} /* end extract_elf32_rel() */
+
+/*****************************************************************************/
+/*
+ * extract the data from a 32-bit module
+ */
+void extract_elf32(void *buffer, size_t len, Elf32_Ehdr *hdr)
+{
+ const Elf32_Sym *symbols;
+ Elf32_Shdr *sections;
+ const char *secstrings, *strings;
+ size_t nsyms, nstrings;
+ int loop, shnum, *canonlist, *canonmap, canon, changed, tmp;
+
+ sections = buffer + get32(&hdr->e_shoff);
+ secstrings = buffer + get32(§ions[get16(&hdr->e_shstrndx)].sh_offset);
+ shnum = get16(&hdr->e_shnum);
+
+ /* find the symbol table and the string table and produce a list of
+ * index numbers of sections that contribute to the kernel's module
+ * image
+ */
+ canonlist = calloc(sizeof(int), shnum * 2);
+ if (!canonlist) {
+ perror("calloc");
+ exit(1);
+ }
+ canonmap = canonlist + shnum;
+ canon = 0;
+
+ symbols = NULL;
+ strings = NULL;
+
+ for (loop = 1; loop < shnum; loop++) {
+ const char *sh_name = secstrings + get32(§ions[loop].sh_name);
+ Elf32_Word sh_type = get32(§ions[loop].sh_type);
+ Elf32_Xword sh_size = get32(§ions[loop].sh_size);
+ Elf32_Xword sh_flags = get32(§ions[loop].sh_flags);
+ Elf32_Off sh_offset = get32(§ions[loop].sh_offset);
+ void *data = buffer + sh_offset;
+
+ /* quick sanity check */
+ if (sh_type != SHT_NOBITS && len < sh_offset + sh_size) {
+ fprintf(stderr, "Section goes beyond EOF\n");
+ exit(3);
+ }
+
+ /* we only need to canonicalise allocatable sections */
+ if (sh_flags & SHF_ALLOC)
+ canonlist[canon++] = loop;
+
+ /* keep track of certain special sections */
+ switch (sh_type) {
+ case SHT_SYMTAB:
+ if (strcmp(sh_name, ".symtab") == 0) {
+ symbols = data;
+ nsyms = sh_size / sizeof(Elf32_Sym);
+ }
+ break;
+
+ case SHT_STRTAB:
+ if (strcmp(sh_name, ".strtab") == 0) {
+ strings = data;
+ nstrings = sh_size;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (!symbols) {
+ fprintf(stderr, "Couldn't locate symbol table\n");
+ exit(3);
+ }
+
+ if (!strings) {
+ fprintf(stderr, "Couldn't locate strings table\n");
+ exit(3);
+ }
+
+ /* canonicalise the index numbers of the contributing section */
+ do {
+ changed = 0;
+
+ for (loop = 0; loop < canon - 1; loop++) {
+ const char *x = secstrings + get32(§ions[canonlist[loop + 0]].sh_name);
+ const char *y = secstrings + get32(§ions[canonlist[loop + 1]].sh_name);
+ if (strcmp(x, y) > 0) {
+ tmp = canonlist[loop + 0];
+ canonlist[loop + 0] = canonlist[loop + 1];
+ canonlist[loop + 1] = tmp;
+ changed = 1;
+ }
+ }
+
+ } while(changed);
+
+ for (loop = 0; loop < canon; loop++)
+ canonmap[canonlist[loop]] = loop + 1;
+
+ if (is_verbose > 1) {
+ printf("\nSection canonicalisation map:\n");
+ for (loop = 1; loop < shnum; loop++) {
+ const char *x = secstrings + get32(§ions[loop].sh_name);
+ printf("%4d %s\n", canonmap[loop], x);
+ }
+
+ printf("\nAllocated section list in canonical order:\n");
+ for (loop = 0; loop < canon; loop++) {
+ const char *x = secstrings + get32(§ions[canonlist[loop]].sh_name);
+ printf("%4d %s\n", canonlist[loop], x);
+ }
+ }
+
+ memset(canonlist, 0, sizeof(int) * shnum);
+
+ /* iterate through the section table looking for sections we want to
+ * contribute to the signature */
+ verbose("\n");
+ verbose("FILE POS CS SECT NAME\n");
+ verbose("======== == ==== ==============================\n");
+
+ for (loop = 1; loop < shnum; loop++) {
+ const char *sh_name = secstrings + get32(§ions[loop].sh_name);
+ Elf32_Word sh_type = get32(§ions[loop].sh_type);
+ Elf32_Xword sh_size = get32(§ions[loop].sh_size);
+ Elf32_Xword sh_flags = get32(§ions[loop].sh_flags);
+ Elf32_Word sh_info = get32(§ions[loop].sh_info);
+ Elf32_Off sh_offset = get32(§ions[loop].sh_offset);
+ void *data = buffer + sh_offset;
+
+ csum = 0;
+
+ /* quick sanity check */
+ if (sh_type != SHT_NOBITS && len < sh_offset + sh_size) {
+ fprintf(stderr, "section goes beyond EOF\n");
+ exit(3);
+ }
+
+ /* include canonicalised relocation sections */
+ if (sh_type == SHT_REL || sh_type == SHT_RELA) {
+ if (sh_info <= 0 && sh_info >= hdr->e_shnum) {
+ fprintf(stderr,
+ "Invalid ELF - REL/RELA sh_info does"
+ " not refer to a valid section\n");
+ exit(3);
+ }
+
+ if (canonlist[sh_info]) {
+ Elf32_Word xsh_info;
+
+ verbose("%08lx ", ftell(outfd));
+
+ set32(&xsh_info, canonmap[sh_info]);
+
+ /* write out selected portions of the section header */
+ write_out(sh_name, strlen(sh_name));
+ write_out_val(sections[loop].sh_type);
+ write_out_val(sections[loop].sh_flags);
+ write_out_val(sections[loop].sh_size);
+ write_out_val(sections[loop].sh_addralign);
+ write_out_val(xsh_info);
+
+ if (sh_type == SHT_RELA)
+ extract_elf32_rela(buffer, loop, sh_info,
+ data, sh_size / sizeof(Elf32_Rela),
+ symbols, nsyms,
+ sections, shnum, canonmap,
+ strings, nstrings,
+ sh_name);
+ else
+ extract_elf32_rel(buffer, loop, sh_info,
+ data, sh_size / sizeof(Elf32_Rel),
+ symbols, nsyms,
+ sections, shnum, canonmap,
+ strings, nstrings,
+ sh_name);
+ }
+
+ continue;
+ }
+
+ /* include allocatable loadable sections */
+ if (sh_type != SHT_NOBITS && sh_flags & SHF_ALLOC)
+ goto include_section;
+
+ /* not this section */
+ continue;
+
+ include_section:
+ verbose("%08lx ", ftell(outfd));
+
+ /* write out selected portions of the section header */
+ write_out(sh_name, strlen(sh_name));
+ write_out_val(sections[loop].sh_type);
+ write_out_val(sections[loop].sh_flags);
+ write_out_val(sections[loop].sh_size);
+ write_out_val(sections[loop].sh_addralign);
+
+ /* write out the section data */
+ write_out(data, sh_size);
+
+ verbose("%02x %4d %s\n", csum, loop, sh_name);
+
+ /* note the section has been written */
+ canonlist[loop] = 1;
+ }
+
+ verbose("%08lx (%lu bytes csum 0x%02x)\n",
+ ftell(outfd), ftell(outfd), xcsum);
+
+} /* end extract_elf32() */