X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=scripts%2Fmodsign%2Fmod-extract.c;h=cbb77c19bb63920dcc827617f56c4797ac079933;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=f8502e71e323ceb075f42a8d7727397a861eea2d;hpb=cee37fe97739d85991964371c1f3a745c00dd236;p=linux-2.6.git diff --git a/scripts/modsign/mod-extract.c b/scripts/modsign/mod-extract.c index f8502e71e..cbb77c19b 100644 --- a/scripts/modsign/mod-extract.c +++ b/scripts/modsign/mod-extract.c @@ -1,109 +1,900 @@ +/* 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 -#include -#include -#include #include +#include +#include +#include #include -#include +#include +#include +#include +#include +#include +#include -/* 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] \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() */