| #if defined(ELF32BIT) |
| |
| #define PERBIT(x) x##32 |
| #define ElfPERBIT(x) Elf32_##x |
| #define ELFPERBIT(x) ELF32_##x |
| /* 32-bit unsigned integer */ |
| #define Elf32_Uint Elf32_Word |
| |
| #elif defined(ELF64BIT) |
| |
| #define PERBIT(x) x##64 |
| #define ElfPERBIT(x) Elf64_##x |
| #define ELFPERBIT(x) ELF64_##x |
| /* 64-bit unsigned integer */ |
| #define Elf64_Uint Elf64_Xword |
| |
| #else |
| # error "Undefined ELF word length" |
| #endif |
| |
| static void *PERBIT(get_section)(struct elf_file *module, |
| const char *secname, |
| ElfPERBIT(Shdr) **sechdr, |
| unsigned long *secsize) |
| { |
| void *data = module->data; |
| unsigned long len = module->len; |
| int conv = module->conv; |
| |
| ElfPERBIT(Ehdr) *hdr; |
| ElfPERBIT(Shdr) *sechdrs; |
| ElfPERBIT(Off) e_shoff; |
| ElfPERBIT(Half) e_shnum, e_shstrndx; |
| ElfPERBIT(Off) secoffset; |
| |
| const char *secnames; |
| unsigned int i; |
| |
| *secsize = 0; |
| |
| if (len <= 0 || len < sizeof(*hdr)) |
| return NULL; |
| |
| hdr = data; |
| e_shoff = END(hdr->e_shoff, conv); |
| e_shnum = END(hdr->e_shnum, conv); |
| e_shstrndx = END(hdr->e_shstrndx, conv); |
| |
| if (len < e_shoff + e_shnum * sizeof(sechdrs[0])) |
| return NULL; |
| |
| sechdrs = data + e_shoff; |
| |
| if (len < END(sechdrs[e_shstrndx].sh_offset, conv)) |
| return NULL; |
| |
| /* Find section by name; return header, pointer and size. */ |
| secnames = data + END(sechdrs[e_shstrndx].sh_offset, conv); |
| for (i = 1; i < e_shnum; i++) { |
| if (streq(secnames + END(sechdrs[i].sh_name, conv), secname)) { |
| *secsize = END(sechdrs[i].sh_size, conv); |
| secoffset = END(sechdrs[i].sh_offset, conv); |
| if (sechdr) |
| *sechdr = sechdrs + i; |
| if (len < secoffset + *secsize) |
| return NULL; |
| return data + secoffset; |
| } |
| } |
| return NULL; |
| } |
| |
| /* Load the given section: NULL on error. */ |
| static void *PERBIT(load_section)(struct elf_file *module, |
| const char *secname, |
| unsigned long *secsize) |
| { |
| return PERBIT(get_section)(module, secname, NULL, secsize); |
| } |
| |
| static struct string_table *PERBIT(load_strings)(struct elf_file *module, |
| const char *secname, |
| struct string_table *tbl) |
| { |
| unsigned long size; |
| const char *strings; |
| |
| strings = PERBIT(load_section)(module, secname, &size); |
| if (strings) { |
| if (strings[size-1] != 0) { |
| warn("%s may be corrupt; an unterminated string" |
| " was found at the end of section %s\n", |
| module->pathname, secname); |
| } |
| /* Skip any zero padding. */ |
| while (!strings[0]) { |
| strings++; |
| if (size-- <= 1) |
| return tbl; |
| } |
| for (; strings; strings = next_string(strings, &size)) |
| tbl = NOFAIL(strtbl_add(strings, tbl)); |
| } |
| return tbl; |
| } |
| |
| static struct string_table *PERBIT(load_symbols)(struct elf_file *module, |
| uint64_t **versions) |
| { |
| struct string_table *symtbl = NULL; |
| |
| if (versions) { |
| static const char crc[] = "__crc_"; |
| static const int crc_len = sizeof(crc) - 1; |
| unsigned int num_syms, i; |
| unsigned long size; |
| ElfPERBIT(Sym) *syms; |
| char *strings; |
| int conv; |
| |
| *versions = NULL; |
| strings = PERBIT(load_section)(module, ".strtab", &size); |
| syms = PERBIT(load_section)(module, ".symtab", &size); |
| if (!strings || !syms) |
| goto fallback; |
| num_syms = size / sizeof(syms[0]); |
| *versions = NOFAIL(calloc(sizeof(**versions), num_syms)); |
| |
| conv = module->conv; |
| for (i = 1; i < num_syms; i++) { |
| const char *name; |
| name = strings + END(syms[i].st_name, conv); |
| if (strncmp(name, crc, crc_len) != 0) |
| continue; |
| name += crc_len; |
| symtbl = NOFAIL(strtbl_add(name, symtbl)); |
| (*versions)[symtbl->cnt - 1] = END(syms[i].st_value, |
| conv); |
| } |
| if (!symtbl) { |
| /* Either this module does not export any symbols, or |
| * it was compiled without CONFIG_MODVERSIONS. If the |
| * latter, we will print a warning in load_dep_syms, |
| * so just silently fallback to __ksymtab_strings in |
| * both cases. |
| */ |
| free(*versions); |
| *versions = NULL; |
| goto fallback; |
| } |
| return symtbl; |
| } |
| fallback: |
| return PERBIT(load_strings)(module, "__ksymtab_strings", symtbl); |
| } |
| |
| static char *PERBIT(get_aliases)(struct elf_file *module, unsigned long *size) |
| { |
| return PERBIT(load_section)(module, ".modalias", size); |
| } |
| |
| static char *PERBIT(get_modinfo)(struct elf_file *module, unsigned long *size) |
| { |
| return PERBIT(load_section)(module, ".modinfo", size); |
| } |
| |
| #ifndef STT_REGISTER |
| #define STT_REGISTER 13 /* Global register reserved to app. */ |
| #endif |
| |
| static struct string_table *PERBIT(load_dep_syms)(struct elf_file *module, |
| struct string_table **types, |
| uint64_t **versions) |
| { |
| unsigned int i, num_syms; |
| unsigned int j, num_symvers, versions_size; |
| unsigned long size; |
| char *strings; |
| ElfPERBIT(Sym) *syms; |
| ElfPERBIT(Ehdr) *hdr; |
| struct PERBIT(modver_info) **symvers; |
| int handle_register_symbols; |
| struct string_table *names; |
| int conv; |
| |
| names = NULL; |
| *types = NULL; |
| symvers = NULL; |
| num_symvers = versions_size = 0; |
| |
| if (versions) { |
| int ok = 1; |
| *versions = NULL; |
| struct PERBIT(modver_info) *symvers_sec; |
| |
| symvers_sec = module->ops->load_section(module, "__versions", |
| &size); |
| if (!symvers_sec) { |
| warn("%s is built without modversions", |
| module->pathname); |
| ok = 0; |
| } |
| if (size % sizeof(symvers[0]) != 0) { |
| warn("invalid __versions section size in %s", |
| module->pathname); |
| ok = 0; |
| } |
| if (ok) { |
| num_symvers = size / sizeof(symvers_sec[0]); |
| /* symvers is used to keep track of each visited entry. |
| * The table also contains the fake struct_module / |
| * module_layout symbol which we don't want to miss. |
| */ |
| symvers = NOFAIL(malloc(num_symvers * |
| sizeof(symvers[0]))); |
| for (j = 0; j < num_symvers; j++) |
| symvers[j] = &symvers_sec[j]; |
| } else { |
| versions = NULL; |
| } |
| } |
| |
| strings = PERBIT(load_section)(module, ".strtab", &size); |
| syms = PERBIT(load_section)(module, ".symtab", &size); |
| if (!strings || !syms) { |
| warn("Couldn't find symtab and strtab in module %s\n", |
| module->pathname); |
| goto out; |
| } |
| |
| num_syms = size / sizeof(syms[0]); |
| hdr = module->data; |
| conv = module->conv; |
| if (versions) { |
| versions_size = num_syms; |
| *versions = NOFAIL(calloc(sizeof(**versions), versions_size)); |
| } |
| |
| handle_register_symbols = |
| (END(hdr->e_machine, conv) == EM_SPARC || |
| END(hdr->e_machine, conv) == EM_SPARCV9); |
| |
| for (i = 1; i < num_syms; i++) { |
| if (END(syms[i].st_shndx, conv) == SHN_UNDEF) { |
| /* Look for symbol */ |
| const char *name; |
| int weak; |
| |
| name = strings + END(syms[i].st_name, conv); |
| |
| /* Not really undefined: sparc gcc 3.3 creates |
| U references when you have global asm |
| variables, to avoid anyone else misusing |
| them. */ |
| if (handle_register_symbols |
| && (ELFPERBIT(ST_TYPE)(END(syms[i].st_info, conv)) |
| == STT_REGISTER)) |
| continue; |
| |
| weak = (ELFPERBIT(ST_BIND)(END(syms[i].st_info, conv)) |
| == STB_WEAK); |
| names = NOFAIL(strtbl_add(name, names)); |
| *types = NOFAIL(strtbl_add(weak ? weak_sym : undef_sym, |
| *types)); |
| |
| if (!versions) |
| continue; |
| /* Not optimal, but the number of required symbols |
| * is usually not huge and this is only called by |
| * depmod. |
| */ |
| for (j = 0; j < num_symvers; j++) { |
| struct PERBIT(modver_info) *info = symvers[j]; |
| |
| if (!info) |
| continue; |
| if (streq(name, info->name)) { |
| (*versions)[names->cnt - 1] = |
| END(info->crc, conv); |
| symvers[j] = NULL; |
| break; |
| } |
| } |
| } |
| } |
| /* add struct_module / module_layout */ |
| for (j = 0; j < num_symvers; j++) { |
| struct PERBIT(modver_info) *info = symvers[j]; |
| |
| if (!info) |
| continue; |
| if ((names ? names->cnt : 0) >= versions_size) { |
| versions_size++; |
| *versions = NOFAIL(realloc(*versions, versions_size)); |
| } |
| names = NOFAIL(strtbl_add(info->name, names)); |
| *types = NOFAIL(strtbl_add(undef_sym, *types)); |
| (*versions)[names->cnt - 1] = END(info->crc, conv); |
| } |
| out: |
| free(symvers); |
| return names; |
| } |
| |
| static void *PERBIT(deref_sym)(ElfPERBIT(Ehdr) *hdr, |
| ElfPERBIT(Shdr) *sechdrs, |
| ElfPERBIT(Sym) *sym, |
| unsigned int *secsize, |
| int conv) |
| { |
| /* In BSS? Happens for empty device tables on |
| * recent GCC versions. */ |
| if (END(sechdrs[END(sym->st_shndx, conv)].sh_type,conv) == SHT_NOBITS) |
| return NULL; |
| |
| if (secsize) |
| *secsize = END(sym->st_size, conv); |
| return (void *)hdr |
| + END(sechdrs[END(sym->st_shndx, conv)].sh_offset, conv) |
| + END(sym->st_value, conv); |
| } |
| |
| /* FIXME: Check size, unless we end up using aliases anyway --RR */ |
| static void PERBIT(fetch_tables)(struct elf_file *module, |
| struct module_tables *tables) |
| { |
| unsigned int i; |
| unsigned long size; |
| char *strings; |
| ElfPERBIT(Ehdr) *hdr; |
| ElfPERBIT(Sym) *syms; |
| ElfPERBIT(Shdr) *sechdrs; |
| int conv; |
| |
| hdr = module->data; |
| conv = module->conv; |
| |
| sechdrs = (void *)hdr + END(hdr->e_shoff, conv); |
| strings = PERBIT(load_section)(module, ".strtab", &size); |
| syms = PERBIT(load_section)(module, ".symtab", &size); |
| |
| /* Don't warn again: we already have above */ |
| if (!strings || !syms) |
| return; |
| |
| memset(tables, 0x00, sizeof(struct module_tables)); |
| |
| for (i = 0; i < size / sizeof(syms[0]); i++) { |
| char *name = strings + END(syms[i].st_name, conv); |
| |
| if (!tables->pci_table && streq(name, "__mod_pci_device_table")) { |
| tables->pci_size = PERBIT(PCI_DEVICE_SIZE); |
| tables->pci_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i], |
| NULL, conv); |
| } |
| else if (!tables->usb_table && streq(name, "__mod_usb_device_table")) { |
| tables->usb_size = PERBIT(USB_DEVICE_SIZE); |
| tables->usb_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i], |
| NULL, conv); |
| } |
| else if (!tables->ccw_table && streq(name, "__mod_ccw_device_table")) { |
| tables->ccw_size = PERBIT(CCW_DEVICE_SIZE); |
| tables->ccw_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i], |
| NULL, conv); |
| } |
| else if (!tables->ieee1394_table && streq(name, "__mod_ieee1394_device_table")) { |
| tables->ieee1394_size = PERBIT(IEEE1394_DEVICE_SIZE); |
| tables->ieee1394_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i], |
| NULL, conv); |
| } |
| else if (!tables->pnp_table && streq(name, "__mod_pnp_device_table")) { |
| tables->pnp_size = PERBIT(PNP_DEVICE_SIZE); |
| tables->pnp_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i], |
| NULL, conv); |
| } |
| else if (!tables->pnp_card_table && streq(name, "__mod_pnp_card_device_table")) { |
| tables->pnp_card_size = PERBIT(PNP_CARD_DEVICE_SIZE); |
| tables->pnp_card_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i], |
| NULL, conv); |
| tables->pnp_card_offset = PERBIT(PNP_CARD_DEVICE_OFFSET); |
| } |
| else if (!tables->input_table && streq(name, "__mod_input_device_table")) { |
| tables->input_size = PERBIT(INPUT_DEVICE_SIZE); |
| tables->input_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i], |
| &tables->input_table_size, |
| conv); |
| } |
| else if (!tables->serio_table && streq(name, "__mod_serio_device_table")) { |
| tables->serio_size = PERBIT(SERIO_DEVICE_SIZE); |
| tables->serio_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i], |
| NULL, conv); |
| } |
| else if (!tables->of_table && streq(name, "__mod_of_device_table")) { |
| tables->of_size = PERBIT(OF_DEVICE_SIZE); |
| tables->of_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i], |
| NULL, conv); |
| } |
| } |
| } |
| |
| /* |
| * strip_section - tell the kernel to ignore the named section |
| */ |
| static void PERBIT(strip_section)(struct elf_file *module, const char *secname) |
| { |
| void *p; |
| ElfPERBIT(Shdr) *sechdr; |
| unsigned long secsize; |
| |
| p = PERBIT(get_section)(module, secname, &sechdr, &secsize); |
| if (p) { |
| ElfPERBIT(Uint) mask; |
| mask = ~((ElfPERBIT(Uint))SHF_ALLOC); |
| sechdr->sh_flags &= END(mask, module->conv); |
| } |
| } |
| |
| static int PERBIT(dump_modversions)(struct elf_file *module) |
| { |
| unsigned long secsize; |
| struct PERBIT(modver_info) *info; |
| int n = 0; |
| |
| info = module->ops->load_section(module, "__versions", &secsize); |
| if (!info) |
| return 0; /* not a kernel module */ |
| if (secsize % sizeof(*info) != 0) |
| return -1; /* invalid section size */ |
| |
| for (n = 0; n < secsize / sizeof(*info); n++) { |
| #if defined(ELF32BIT) |
| printf("0x%08lx\t%s\n", (unsigned long) |
| #else /* defined(ELF64BIT) */ |
| printf("0x%08llx\t%s\n", (unsigned long long) |
| #endif |
| END(info[n].crc, module->conv), |
| skip_dot(info[n].name)); |
| } |
| return n; |
| } |
| |
| struct module_ops PERBIT(mod_ops) = { |
| .load_section = PERBIT(load_section), |
| .load_strings = PERBIT(load_strings), |
| .load_symbols = PERBIT(load_symbols), |
| .load_dep_syms = PERBIT(load_dep_syms), |
| .fetch_tables = PERBIT(fetch_tables), |
| .get_aliases = PERBIT(get_aliases), |
| .get_modinfo = PERBIT(get_modinfo), |
| .strip_section = PERBIT(strip_section), |
| .dump_modvers = PERBIT(dump_modversions), |
| }; |
| |
| #undef PERBIT |
| #undef ElfPERBIT |
| #undef ELFPERBIT |