X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=scripts%2Fmod%2Fmodpost.c;h=3a12c22cc2f8f0ca82f624a50c4878fce389e7bb;hb=6d9a89ea4b06146d29e1ffb4d6fded286fa07d29;hp=30f3ac8eb43c0c16a7d86a358d04ac62e31dc989;hpb=f7b05e64bdb2fcc4b2dc94a4bd9426adc70c9599;p=safe%2Fjmp%2Flinux-2.6 diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 30f3ac8..3a12c22 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -13,6 +13,7 @@ #include #include "modpost.h" +#include "../../include/linux/license.h" /* Are we using CONFIG_MODVERSIONS? */ int modversions = 0; @@ -22,8 +23,19 @@ int have_vmlinux = 0; static int all_versions = 0; /* If we are modposting external module set to 1 */ static int external_module = 0; +/* Warn about section mismatch in vmlinux if set to 1 */ +static int vmlinux_section_warnings = 1; +/* Only warn about unresolved symbols */ +static int warn_unresolved = 0; +/* How a symbol is exported */ +enum export { + export_plain, export_unused, export_gpl, + export_unused_gpl, export_gpl_future, export_unknown +}; + +#define PRINTF __attribute__ ((format (printf, 1, 2))) -void fatal(const char *fmt, ...) +PRINTF void fatal(const char *fmt, ...) { va_list arglist; @@ -36,7 +48,7 @@ void fatal(const char *fmt, ...) exit(1); } -void warn(const char *fmt, ...) +PRINTF void warn(const char *fmt, ...) { va_list arglist; @@ -47,6 +59,17 @@ void warn(const char *fmt, ...) va_end(arglist); } +PRINTF void merror(const char *fmt, ...) +{ + va_list arglist; + + fprintf(stderr, "ERROR: "); + + va_start(arglist, fmt); + vfprintf(stderr, fmt, arglist); + va_end(arglist); +} + static int is_vmlinux(const char *modname) { const char *myname; @@ -56,7 +79,8 @@ static int is_vmlinux(const char *modname) else myname = modname; - return strcmp(myname, "vmlinux") == 0; + return (strcmp(myname, "vmlinux") == 0) || + (strcmp(myname, "vmlinux.o") == 0); } void *do_nofail(void *ptr, const char *expr) @@ -97,6 +121,7 @@ static struct module *new_module(char *modname) /* add to list */ mod->name = p; + mod->gpl_compatible = -1; mod->next = modules; modules = mod; @@ -118,6 +143,7 @@ struct symbol { unsigned int kernel:1; /* 1 if symbol is from kernel * (only for external modules) **/ unsigned int preloaded:1; /* 1 if symbol from Module.symvers */ + enum export export; /* Type of export */ char name[0]; }; @@ -153,7 +179,8 @@ static struct symbol *alloc_symbol(const char *name, unsigned int weak, } /* For the hash of exported symbols */ -static struct symbol *new_symbol(const char *name, struct module *module) +static struct symbol *new_symbol(const char *name, struct module *module, + enum export export) { unsigned int hash; struct symbol *new; @@ -161,6 +188,7 @@ static struct symbol *new_symbol(const char *name, struct module *module) hash = tdb_hash(name) % SYMBOL_HASH_SIZE; new = symbolhash[hash] = alloc_symbol(name, 0, symbolhash[hash]); new->module = module; + new->export = export; return new; } @@ -179,37 +207,88 @@ static struct symbol *find_symbol(const char *name) return NULL; } +static struct { + const char *str; + enum export export; +} export_list[] = { + { .str = "EXPORT_SYMBOL", .export = export_plain }, + { .str = "EXPORT_UNUSED_SYMBOL", .export = export_unused }, + { .str = "EXPORT_SYMBOL_GPL", .export = export_gpl }, + { .str = "EXPORT_UNUSED_SYMBOL_GPL", .export = export_unused_gpl }, + { .str = "EXPORT_SYMBOL_GPL_FUTURE", .export = export_gpl_future }, + { .str = "(unknown)", .export = export_unknown }, +}; + + +static const char *export_str(enum export ex) +{ + return export_list[ex].str; +} + +static enum export export_no(const char * s) +{ + int i; + if (!s) + return export_unknown; + for (i = 0; export_list[i].export != export_unknown; i++) { + if (strcmp(export_list[i].str, s) == 0) + return export_list[i].export; + } + return export_unknown; +} + +static enum export export_from_sec(struct elf_info *elf, Elf_Section sec) +{ + if (sec == elf->export_sec) + return export_plain; + else if (sec == elf->export_unused_sec) + return export_unused; + else if (sec == elf->export_gpl_sec) + return export_gpl; + else if (sec == elf->export_unused_gpl_sec) + return export_unused_gpl; + else if (sec == elf->export_gpl_future_sec) + return export_gpl_future; + else + return export_unknown; +} + /** * Add an exported symbol - it may have already been added without a * CRC, in this case just update the CRC **/ -static struct symbol *sym_add_exported(const char *name, struct module *mod) +static struct symbol *sym_add_exported(const char *name, struct module *mod, + enum export export) { struct symbol *s = find_symbol(name); if (!s) { - s = new_symbol(name, mod); + s = new_symbol(name, mod, export); } else { if (!s->preloaded) { warn("%s: '%s' exported twice. Previous export " "was in %s%s\n", mod->name, name, s->module->name, is_vmlinux(s->module->name) ?"":".ko"); + } else { + /* In case Modules.symvers was out of date */ + s->module = mod; } } s->preloaded = 0; s->vmlinux = is_vmlinux(mod->name); s->kernel = 0; + s->export = export; return s; } static void sym_update_crc(const char *name, struct module *mod, - unsigned int crc) + unsigned int crc, enum export export) { struct symbol *s = find_symbol(name); if (!s) - s = new_symbol(name, mod); + s = new_symbol(name, mod, export); s->crc = crc; s->crc_valid = 1; } @@ -273,30 +352,46 @@ void release_file(void *file, unsigned long size) munmap(file, size); } -static void parse_elf(struct elf_info *info, const char *filename) +static int parse_elf(struct elf_info *info, const char *filename) { unsigned int i; - Elf_Ehdr *hdr = info->hdr; + Elf_Ehdr *hdr; Elf_Shdr *sechdrs; Elf_Sym *sym; hdr = grab_file(filename, &info->size); if (!hdr) { perror(filename); - abort(); + exit(1); } info->hdr = hdr; - if (info->size < sizeof(*hdr)) - goto truncated; - + if (info->size < sizeof(*hdr)) { + /* file too small, assume this is an empty .o file */ + return 0; + } + /* Is this a valid ELF file? */ + if ((hdr->e_ident[EI_MAG0] != ELFMAG0) || + (hdr->e_ident[EI_MAG1] != ELFMAG1) || + (hdr->e_ident[EI_MAG2] != ELFMAG2) || + (hdr->e_ident[EI_MAG3] != ELFMAG3)) { + /* Not an ELF file - silently ignore it */ + return 0; + } /* Fix endianness in ELF header */ hdr->e_shoff = TO_NATIVE(hdr->e_shoff); hdr->e_shstrndx = TO_NATIVE(hdr->e_shstrndx); hdr->e_shnum = TO_NATIVE(hdr->e_shnum); hdr->e_machine = TO_NATIVE(hdr->e_machine); + hdr->e_type = TO_NATIVE(hdr->e_type); sechdrs = (void *)hdr + hdr->e_shoff; info->sechdrs = sechdrs; + /* Check if file offset is correct */ + if (hdr->e_shoff > info->size) { + fatal("section header offset=%u in file '%s' is bigger then filesize=%lu\n", hdr->e_shoff, filename, info->size); + return 0; + } + /* Fix endianness in section headers */ for (i = 0; i < hdr->e_shnum; i++) { sechdrs[i].sh_type = TO_NATIVE(sechdrs[i].sh_type); @@ -304,18 +399,34 @@ static void parse_elf(struct elf_info *info, const char *filename) sechdrs[i].sh_size = TO_NATIVE(sechdrs[i].sh_size); sechdrs[i].sh_link = TO_NATIVE(sechdrs[i].sh_link); sechdrs[i].sh_name = TO_NATIVE(sechdrs[i].sh_name); + sechdrs[i].sh_info = TO_NATIVE(sechdrs[i].sh_info); + sechdrs[i].sh_addr = TO_NATIVE(sechdrs[i].sh_addr); } /* Find symbol table. */ for (i = 1; i < hdr->e_shnum; i++) { const char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; + const char *secname; - if (sechdrs[i].sh_offset > info->size) - goto truncated; - if (strcmp(secstrings+sechdrs[i].sh_name, ".modinfo") == 0) { + if (sechdrs[i].sh_offset > info->size) { + fatal("%s is truncated. sechdrs[i].sh_offset=%u > sizeof(*hrd)=%ul\n", filename, (unsigned int)sechdrs[i].sh_offset, sizeof(*hdr)); + return 0; + } + secname = secstrings + sechdrs[i].sh_name; + if (strcmp(secname, ".modinfo") == 0) { info->modinfo = (void *)hdr + sechdrs[i].sh_offset; info->modinfo_len = sechdrs[i].sh_size; - } + } else if (strcmp(secname, "__ksymtab") == 0) + info->export_sec = i; + else if (strcmp(secname, "__ksymtab_unused") == 0) + info->export_unused_sec = i; + else if (strcmp(secname, "__ksymtab_gpl") == 0) + info->export_gpl_sec = i; + else if (strcmp(secname, "__ksymtab_unused_gpl") == 0) + info->export_unused_gpl_sec = i; + else if (strcmp(secname, "__ksymtab_gpl_future") == 0) + info->export_gpl_future_sec = i; + if (sechdrs[i].sh_type != SHT_SYMTAB) continue; @@ -335,10 +446,7 @@ static void parse_elf(struct elf_info *info, const char *filename) sym->st_value = TO_NATIVE(sym->st_value); sym->st_size = TO_NATIVE(sym->st_size); } - return; - - truncated: - fatal("%s is truncated.\n", filename); + return 1; } static void parse_elf_finish(struct elf_info *info) @@ -353,6 +461,7 @@ static void handle_modversions(struct module *mod, struct elf_info *info, Elf_Sym *sym, const char *symname) { unsigned int crc; + enum export export = export_from_sec(info, sym->st_shndx); switch (sym->st_shndx) { case SHN_COMMON: @@ -362,7 +471,8 @@ static void handle_modversions(struct module *mod, struct elf_info *info, /* CRC'd symbol */ if (memcmp(symname, CRC_PFX, strlen(CRC_PFX)) == 0) { crc = (unsigned int) sym->st_value; - sym_update_crc(symname + strlen(CRC_PFX), mod, crc); + sym_update_crc(symname + strlen(CRC_PFX), mod, crc, + export); } break; case SHN_UNDEF: @@ -406,7 +516,8 @@ static void handle_modversions(struct module *mod, struct elf_info *info, default: /* All exported symbols */ if (memcmp(symname, KSYMTAB_PFX, strlen(KSYMTAB_PFX)) == 0) { - sym_add_exported(symname + strlen(KSYMTAB_PFX), mod); + sym_add_exported(symname + strlen(KSYMTAB_PFX), mod, + export); } if (strcmp(symname, MODULE_SYMBOL_PREFIX "init_module") == 0) mod->has_init = 1; @@ -437,13 +548,18 @@ static char *next_string(char *string, unsigned long *secsize) return string; } -static char *get_modinfo(void *modinfo, unsigned long modinfo_len, - const char *tag) +static char *get_next_modinfo(void *modinfo, unsigned long modinfo_len, + const char *tag, char *info) { char *p; unsigned int taglen = strlen(tag); unsigned long size = modinfo_len; + if (info) { + size -= info - (char *)modinfo; + modinfo = next_string(info, &size); + } + for (p = modinfo; p; p = next_string(p, &size)) { if (strncmp(p, tag, taglen) == 0 && p[taglen] == '=') return p + taglen + 1; @@ -451,6 +567,13 @@ static char *get_modinfo(void *modinfo, unsigned long modinfo_len, return NULL; } +static char *get_modinfo(void *modinfo, unsigned long modinfo_len, + const char *tag) + +{ + return get_next_modinfo(modinfo, modinfo_len, tag, NULL); +} + /** * Test if string s ends in string sub * return 0 if match @@ -474,8 +597,62 @@ static int strrcmp(const char *s, const char *sub) return memcmp(s + slen - sublen, sub, sublen); } +/* + * Functions used only during module init is marked __init and is stored in + * a .init.text section. Likewise data is marked __initdata and stored in + * a .init.data section. + * If this section is one of these sections return 1 + * See include/linux/init.h for the details + */ +static int init_section(const char *name) +{ + if (strcmp(name, ".init") == 0) + return 1; + if (strncmp(name, ".init.", strlen(".init.")) == 0) + return 1; + return 0; +} + +/* + * Functions used only during module exit is marked __exit and is stored in + * a .exit.text section. Likewise data is marked __exitdata and stored in + * a .exit.data section. + * If this section is one of these sections return 1 + * See include/linux/init.h for the details + **/ +static int exit_section(const char *name) +{ + if (strcmp(name, ".exit.text") == 0) + return 1; + if (strcmp(name, ".exit.data") == 0) + return 1; + return 0; + +} + +/* + * Data sections are named like this: + * .data | .data.rel | .data.rel.* + * Return 1 if the specified section is a data section + */ +static int data_section(const char *name) +{ + if ((strcmp(name, ".data") == 0) || + (strcmp(name, ".data.rel") == 0) || + (strncmp(name, ".data.rel.", strlen(".data.rel.")) == 0)) + return 1; + else + return 0; +} + /** * Whitelist to allow certain references to pass with no warning. + * + * Pattern 0: + * Do not warn if funtion/data are marked with __init_refok/__initdata_refok. + * The pattern is identified by: + * fromsec = .text.init.refok* | .data.init.refok* + * * Pattern 1: * If a module parameter is declared __initdata and permissions=0 * then this is legal despite the warning generated. @@ -487,51 +664,99 @@ static int strrcmp(const char *s, const char *sub) * atsym =__param* * * Pattern 2: - * Many drivers utilise a *_driver container with references to + * Many drivers utilise a *driver container with references to * add, remove, probe functions etc. * These functions may often be marked __init and we do not want to * warn here. * the pattern is identified by: - * tosec = .init.text | .exit.text - * fromsec = .data - * atsym = *_driver, *_ops, *_probe, *probe_one + * tosec = init or exit section + * fromsec = data section + * atsym = *driver, *_template, *_sht, *_ops, *_probe, *probe_one, *_console, *_timer + * + * Pattern 3: + * Whitelist all refereces from .text.head to .init.data + * Whitelist all refereces from .text.head to .init.text + * + * Pattern 4: + * Some symbols belong to init section but still it is ok to reference + * these from non-init sections as these symbols don't have any memory + * allocated for them and symbol address and value are same. So even + * if init section is freed, its ok to reference those symbols. + * For ex. symbols marking the init section boundaries. + * This pattern is identified by + * refsymname = __init_begin, _sinittext, _einittext + * + * Pattern 5: + * Xtensa uses literal sections for constants that are accessed PC-relative. + * Literal sections may safely reference their text sections. + * (Note that the name for the literal section omits any trailing '.text') + * tosec =
[.text] + * fromsec =
.literal **/ -static int secref_whitelist(const char *tosec, const char *fromsec, - const char *atsym) +static int secref_whitelist(const char *modname, const char *tosec, + const char *fromsec, const char *atsym, + const char *refsymname) { - int f1 = 1, f2 = 1; + int len; const char **s; const char *pat2sym[] = { - "_driver", + "driver", + "_template", /* scsi uses *_template a lot */ + "_timer", /* arm uses ops structures named _timer a lot */ + "_sht", /* scsi also used *_sht to some extent */ "_ops", "_probe", "_probe_one", + "_console", NULL }; - /* Check for pattern 1 */ - if (strcmp(tosec, ".init.data") != 0) - f1 = 0; - if (strncmp(fromsec, ".data", strlen(".data")) != 0) - f1 = 0; - if (strncmp(atsym, "__param", strlen("__param")) != 0) - f1 = 0; + const char *pat3refsym[] = { + "__init_begin", + "_sinittext", + "_einittext", + NULL + }; + + /* Check for pattern 0 */ + if ((strncmp(fromsec, ".text.init.refok", strlen(".text.init.refok")) == 0) || + (strncmp(fromsec, ".exit.text.refok", strlen(".exit.text.refok")) == 0) || + (strncmp(fromsec, ".data.init.refok", strlen(".data.init.refok")) == 0)) + return 1; - if (f1) - return f1; + /* Check for pattern 1 */ + if ((strcmp(tosec, ".init.data") == 0) && + (strncmp(fromsec, ".data", strlen(".data")) == 0) && + (strncmp(atsym, "__param", strlen("__param")) == 0)) + return 1; /* Check for pattern 2 */ - if ((strcmp(tosec, ".init.text") != 0) && - (strcmp(tosec, ".exit.text") != 0)) - f2 = 0; - if (strcmp(fromsec, ".data") != 0) - f2 = 0; + if ((init_section(tosec) || exit_section(tosec)) && data_section(fromsec)) + for (s = pat2sym; *s; s++) + if (strrcmp(atsym, *s) == 0) + return 1; + + /* Check for pattern 3 */ + if ((strcmp(fromsec, ".text.head") == 0) && + ((strcmp(tosec, ".init.data") == 0) || + (strcmp(tosec, ".init.text") == 0))) + return 1; - for (s = pat2sym; *s; s++) - if (strrcmp(atsym, *s) == 0) - f1 = 1; + /* Check for pattern 4 */ + for (s = pat3refsym; *s; s++) + if (strcmp(refsymname, *s) == 0) + return 1; - return f1 && f2; + /* Check for pattern 5 */ + if (strrcmp(tosec, ".text") == 0) + len = strlen(tosec) - strlen(".text"); + else + len = strlen(tosec); + if ((strncmp(tosec, fromsec, len) == 0) && (strlen(fromsec) > len) && + (strcmp(fromsec + len, ".literal") == 0)) + return 1; + + return 0; } /** @@ -551,12 +776,38 @@ static Elf_Sym *find_elf_symbol(struct elf_info *elf, Elf_Addr addr, for (sym = elf->symtab_start; sym < elf->symtab_stop; sym++) { if (sym->st_shndx != relsym->st_shndx) continue; + if (ELF_ST_TYPE(sym->st_info) == STT_SECTION) + continue; if (sym->st_value == addr) return sym; } return NULL; } +static inline int is_arm_mapping_symbol(const char *str) +{ + return str[0] == '$' && strchr("atd", str[1]) + && (str[2] == '\0' || str[2] == '.'); +} + +/* + * If there's no name there, ignore it; likewise, ignore it if it's + * one of the magic symbols emitted used by current ARM tools. + * + * Otherwise if find_symbols_between() returns those symbols, they'll + * fail the whitelist tests and cause lots of false alarms ... fixable + * only by merging __exit and __init sections into __text, bloating + * the kernel (which is especially evil on embedded platforms). + */ +static inline int is_valid_name(struct elf_info *elf, Elf_Sym *sym) +{ + const char *name = elf->strtab + sym->st_name; + + if (!name || !strlen(name)) + return 0; + return !is_arm_mapping_symbol(name); +} + /* * Find symbols before or equal addr and after addr - in the section sec. * If we find two symbols with equal offset prefer one with a valid name. @@ -585,16 +836,15 @@ static void find_symbols_between(struct elf_info *elf, Elf_Addr addr, symsec = secstrings + elf->sechdrs[sym->st_shndx].sh_name; if (strcmp(symsec, sec) != 0) continue; + if (!is_valid_name(elf, sym)) + continue; if (sym->st_value <= addr) { if ((addr - sym->st_value) < beforediff) { beforediff = addr - sym->st_value; *before = sym; } else if ((addr - sym->st_value) == beforediff) { - /* equal offset, valid name? */ - const char *name = elf->strtab + sym->st_name; - if (name && strlen(name)) - *before = sym; + *before = sym; } } else @@ -604,10 +854,7 @@ static void find_symbols_between(struct elf_info *elf, Elf_Addr addr, *after = sym; } else if ((sym->st_value - addr) == afterdiff) { - /* equal offset, valid name? */ - const char *name = elf->strtab + sym->st_name; - if (name && strlen(name)) - *after = sym; + *after = sym; } } } @@ -637,35 +884,107 @@ static void warn_sec_mismatch(const char *modname, const char *fromsec, refsymname = elf->strtab + refsym->st_name; /* check whitelist - we may ignore it */ - if (before && - secref_whitelist(secname, fromsec, elf->strtab + before->st_name)) + if (secref_whitelist(modname, secname, fromsec, + before ? elf->strtab + before->st_name : "", + refsymname)) return; if (before && after) { - warn("%s - Section mismatch: reference to %s:%s from %s " - "between '%s' (at offset 0x%llx) and '%s'\n", - modname, secname, refsymname, fromsec, + warn("%s(%s+0x%llx): Section mismatch: reference to %s:%s " + "(between '%s' and '%s')\n", + modname, fromsec, (unsigned long long)r.r_offset, + secname, refsymname, elf->strtab + before->st_name, - (long long)r.r_offset, elf->strtab + after->st_name); } else if (before) { - warn("%s - Section mismatch: reference to %s:%s from %s " - "after '%s' (at offset 0x%llx)\n", - modname, secname, refsymname, fromsec, - elf->strtab + before->st_name, - (long long)r.r_offset); + warn("%s(%s+0x%llx): Section mismatch: reference to %s:%s " + "(after '%s')\n", + modname, fromsec, (unsigned long long)r.r_offset, + secname, refsymname, + elf->strtab + before->st_name); } else if (after) { - warn("%s - Section mismatch: reference to %s:%s from %s " + warn("%s(%s+0x%llx): Section mismatch: reference to %s:%s " "before '%s' (at offset -0x%llx)\n", - modname, secname, refsymname, fromsec, - elf->strtab + before->st_name, - (long long)r.r_offset); + modname, fromsec, (unsigned long long)r.r_offset, + secname, refsymname, + elf->strtab + after->st_name); } else { - warn("%s - Section mismatch: reference to %s:%s from %s " - "(offset 0x%llx)\n", - modname, secname, fromsec, refsymname, - (long long)r.r_offset); + warn("%s(%s+0x%llx): Section mismatch: reference to %s:%s\n", + modname, fromsec, (unsigned long long)r.r_offset, + secname, refsymname); + } +} + +static unsigned int *reloc_location(struct elf_info *elf, + int rsection, Elf_Rela *r) +{ + Elf_Shdr *sechdrs = elf->sechdrs; + int section = sechdrs[rsection].sh_info; + + return (void *)elf->hdr + sechdrs[section].sh_offset + + (r->r_offset - sechdrs[section].sh_addr); +} + +static int addend_386_rel(struct elf_info *elf, int rsection, Elf_Rela *r) +{ + unsigned int r_typ = ELF_R_TYPE(r->r_info); + unsigned int *location = reloc_location(elf, rsection, r); + + switch (r_typ) { + case R_386_32: + r->r_addend = TO_NATIVE(*location); + break; + case R_386_PC32: + r->r_addend = TO_NATIVE(*location) + 4; + /* For CONFIG_RELOCATABLE=y */ + if (elf->hdr->e_type == ET_EXEC) + r->r_addend += r->r_offset; + break; + } + return 0; +} + +static int addend_arm_rel(struct elf_info *elf, int rsection, Elf_Rela *r) +{ + unsigned int r_typ = ELF_R_TYPE(r->r_info); + + switch (r_typ) { + case R_ARM_ABS32: + /* From ARM ABI: (S + A) | T */ + r->r_addend = (int)(long)(elf->symtab_start + ELF_R_SYM(r->r_info)); + break; + case R_ARM_PC24: + /* From ARM ABI: ((S + A) | T) - P */ + r->r_addend = (int)(long)(elf->hdr + elf->sechdrs[rsection].sh_offset + + (r->r_offset - elf->sechdrs[rsection].sh_addr)); + break; + default: + return 1; } + return 0; +} + +static int addend_mips_rel(struct elf_info *elf, int rsection, Elf_Rela *r) +{ + unsigned int r_typ = ELF_R_TYPE(r->r_info); + unsigned int *location = reloc_location(elf, rsection, r); + unsigned int inst; + + if (r_typ == R_MIPS_HI16) + return 1; /* skip this */ + inst = TO_NATIVE(*location); + switch (r_typ) { + case R_MIPS_LO16: + r->r_addend = inst & 0xffff; + break; + case R_MIPS_26: + r->r_addend = (inst & 0x03ffffff) << 2; + break; + case R_MIPS_32: + r->r_addend = inst; + break; + } + return 0; } /** @@ -694,50 +1013,166 @@ static void check_sec_ref(struct module *mod, const char *modname, /* Walk through all sections */ for (i = 0; i < hdr->e_shnum; i++) { - Elf_Rela *rela; - Elf_Rela *start = (void *)hdr + sechdrs[i].sh_offset; - Elf_Rela *stop = (void*)start + sechdrs[i].sh_size; - const char *name = secstrings + sechdrs[i].sh_name + - strlen(".rela"); + const char *name = secstrings + sechdrs[i].sh_name; + const char *secname; + Elf_Rela r; + unsigned int r_sym; /* We want to process only relocation sections and not .init */ - if (section_ref_ok(name) || (sechdrs[i].sh_type != SHT_RELA)) - continue; + if (sechdrs[i].sh_type == SHT_RELA) { + Elf_Rela *rela; + Elf_Rela *start = (void *)hdr + sechdrs[i].sh_offset; + Elf_Rela *stop = (void*)start + sechdrs[i].sh_size; + name += strlen(".rela"); + if (section_ref_ok(name)) + continue; - for (rela = start; rela < stop; rela++) { - Elf_Rela r; - const char *secname; - r.r_offset = TO_NATIVE(rela->r_offset); - r.r_info = TO_NATIVE(rela->r_info); - r.r_addend = TO_NATIVE(rela->r_addend); - sym = elf->symtab_start + ELF_R_SYM(r.r_info); - /* Skip special sections */ - if (sym->st_shndx >= SHN_LORESERVE) + for (rela = start; rela < stop; rela++) { + r.r_offset = TO_NATIVE(rela->r_offset); +#if KERNEL_ELFCLASS == ELFCLASS64 + if (hdr->e_machine == EM_MIPS) { + unsigned int r_typ; + r_sym = ELF64_MIPS_R_SYM(rela->r_info); + r_sym = TO_NATIVE(r_sym); + r_typ = ELF64_MIPS_R_TYPE(rela->r_info); + r.r_info = ELF64_R_INFO(r_sym, r_typ); + } else { + r.r_info = TO_NATIVE(rela->r_info); + r_sym = ELF_R_SYM(r.r_info); + } +#else + r.r_info = TO_NATIVE(rela->r_info); + r_sym = ELF_R_SYM(r.r_info); +#endif + r.r_addend = TO_NATIVE(rela->r_addend); + sym = elf->symtab_start + r_sym; + /* Skip special sections */ + if (sym->st_shndx >= SHN_LORESERVE) + continue; + + secname = secstrings + + sechdrs[sym->st_shndx].sh_name; + if (section(secname)) + warn_sec_mismatch(modname, name, + elf, sym, r); + } + } else if (sechdrs[i].sh_type == SHT_REL) { + Elf_Rel *rel; + Elf_Rel *start = (void *)hdr + sechdrs[i].sh_offset; + Elf_Rel *stop = (void*)start + sechdrs[i].sh_size; + name += strlen(".rel"); + if (section_ref_ok(name)) continue; - secname = secstrings + sechdrs[sym->st_shndx].sh_name; - if (section(secname)) - warn_sec_mismatch(modname, name, elf, sym, r); + for (rel = start; rel < stop; rel++) { + r.r_offset = TO_NATIVE(rel->r_offset); +#if KERNEL_ELFCLASS == ELFCLASS64 + if (hdr->e_machine == EM_MIPS) { + unsigned int r_typ; + r_sym = ELF64_MIPS_R_SYM(rel->r_info); + r_sym = TO_NATIVE(r_sym); + r_typ = ELF64_MIPS_R_TYPE(rel->r_info); + r.r_info = ELF64_R_INFO(r_sym, r_typ); + } else { + r.r_info = TO_NATIVE(rel->r_info); + r_sym = ELF_R_SYM(r.r_info); + } +#else + r.r_info = TO_NATIVE(rel->r_info); + r_sym = ELF_R_SYM(r.r_info); +#endif + r.r_addend = 0; + switch (hdr->e_machine) { + case EM_386: + if (addend_386_rel(elf, i, &r)) + continue; + break; + case EM_ARM: + if(addend_arm_rel(elf, i, &r)) + continue; + break; + case EM_MIPS: + if (addend_mips_rel(elf, i, &r)) + continue; + break; + } + sym = elf->symtab_start + r_sym; + /* Skip special sections */ + if (sym->st_shndx >= SHN_LORESERVE) + continue; + + secname = secstrings + + sechdrs[sym->st_shndx].sh_name; + if (section(secname)) + warn_sec_mismatch(modname, name, + elf, sym, r); + } } } } -/** - * Functions used only during module init is marked __init and is stored in - * a .init.text section. Likewise data is marked __initdata and stored in - * a .init.data section. - * If this section is one of these sections return 1 - * See include/linux/init.h for the details - **/ -static int init_section(const char *name) +/* + * Identify sections from which references to either a + * .init or a .exit section is OK. + * + * [OPD] Keith Ownes commented: + * For our future {in}sanity, add a comment that this is the ppc .opd + * section, not the ia64 .opd section. + * ia64 .opd should not point to discarded sections. + * [.rodata] like for .init.text we ignore .rodata references -same reason + */ +static int initexit_section_ref_ok(const char *name) { - if (strcmp(name, ".init") == 0) - return 1; - if (strncmp(name, ".init.", strlen(".init.")) == 0) - return 1; + const char **s; + /* Absolute section names */ + const char *namelist1[] = { + "__bug_table", /* used by powerpc for BUG() */ + "__ex_table", + ".altinstructions", + ".cranges", /* used by sh64 */ + ".fixup", + ".machvec", /* ia64 + powerpc uses these */ + ".machine.desc", + ".opd", /* See comment [OPD] */ + "__dbe_table", + ".parainstructions", + ".pdr", + ".plt", /* seen on ARCH=um build on x86_64. Harmless */ + ".smp_locks", + ".stab", + ".m68k_fixup", + ".xt.prop", /* xtensa informational section */ + ".xt.lit", /* xtensa informational section */ + NULL + }; + /* Start of section names */ + const char *namelist2[] = { + ".debug", + ".eh_frame", + ".note", /* ignore ELF notes - may contain anything */ + ".got", /* powerpc - global offset table */ + ".toc", /* powerpc - table of contents */ + NULL + }; + /* part of section name */ + const char *namelist3 [] = { + ".unwind", /* Sample: IA_64.unwind.exit.text */ + NULL + }; + + for (s = namelist1; *s; s++) + if (strcmp(*s, name) == 0) + return 1; + for (s = namelist2; *s; s++) + if (strncmp(*s, name, strlen(*s)) == 0) + return 1; + for (s = namelist3; *s; s++) + if (strstr(name, *s) != NULL) + return 1; return 0; } -/** + +/* * Identify sections from which references to a .init section is OK. * * Unfortunately references to read only data that referenced .init @@ -751,114 +1186,68 @@ static int init_section(const char *name) * * where vgacon_startup is __init. If you want to wade through the false * positives, take out the check for rodata. - **/ + */ static int init_section_ref_ok(const char *name) { const char **s; /* Absolute section names */ const char *namelist1[] = { + "__dbe_table", /* MIPS generate these */ + "__ftr_fixup", /* powerpc cpu feature fixup */ + "__fw_ftr_fixup", /* powerpc firmware feature fixup */ + "__param", + ".data.rel.ro", /* used by parisc64 */ ".init", - ".opd", /* see comment [OPD] at exit_section_ref_ok() */ - ".toc1", /* used by ppc64 */ - ".stab", - ".rodata", ".text.lock", - "__bug_table", /* used by powerpc for BUG() */ - ".pci_fixup_header", - ".pci_fixup_final", - ".pdr", - "__param", NULL }; /* Start of section names */ const char *namelist2[] = { ".init.", - ".altinstructions", - ".eh_frame", - ".debug", - NULL - }; - /* part of section name */ - const char *namelist3 [] = { - ".unwind", /* sample: IA_64.unwind.init.text */ + ".pci_fixup", + ".rodata", NULL }; + if (initexit_section_ref_ok(name)) + return 1; + for (s = namelist1; *s; s++) if (strcmp(*s, name) == 0) return 1; for (s = namelist2; *s; s++) if (strncmp(*s, name, strlen(*s)) == 0) return 1; - for (s = namelist3; *s; s++) - if (strstr(name, *s) != NULL) - return 1; - return 0; -} -/* - * Functions used only during module exit is marked __exit and is stored in - * a .exit.text section. Likewise data is marked __exitdata and stored in - * a .exit.data section. - * If this section is one of these sections return 1 - * See include/linux/init.h for the details - **/ -static int exit_section(const char *name) -{ - if (strcmp(name, ".exit.text") == 0) - return 1; - if (strcmp(name, ".exit.data") == 0) + /* If section name ends with ".init" we allow references + * as is the case with .initcallN.init, .early_param.init, .taglist.init etc + */ + if (strrcmp(name, ".init") == 0) return 1; return 0; - } /* * Identify sections from which references to a .exit section is OK. - * - * [OPD] Keith Ownes commented: - * For our future {in}sanity, add a comment that this is the ppc .opd - * section, not the ia64 .opd section. - * ia64 .opd should not point to discarded sections. - **/ + */ static int exit_section_ref_ok(const char *name) { const char **s; /* Absolute section names */ const char *namelist1[] = { - ".exit.text", ".exit.data", - ".init.text", - ".opd", /* See comment [OPD] */ - ".toc1", /* used by ppc64 */ - ".altinstructions", - ".pdr", - "__bug_table", /* used by powerpc for BUG() */ + ".exit.text", ".exitcall.exit", - ".eh_frame", - ".stab", - NULL - }; - /* Start of section names */ - const char *namelist2[] = { - ".debug", - NULL - }; - /* part of section name */ - const char *namelist3 [] = { - ".unwind", /* Sample: IA_64.unwind.exit.text */ + ".rodata", NULL }; + if (initexit_section_ref_ok(name)) + return 1; + for (s = namelist1; *s; s++) if (strcmp(*s, name) == 0) return 1; - for (s = namelist2; *s; s++) - if (strncmp(*s, name, strlen(*s)) == 0) - return 1; - for (s = namelist3; *s; s++) - if (strstr(name, *s) != NULL) - return 1; return 0; } @@ -866,11 +1255,13 @@ static void read_symbols(char *modname) { const char *symname; char *version; + char *license; struct module *mod; struct elf_info info = { }; Elf_Sym *sym; - parse_elf(&info, modname); + if (!parse_elf(&info, modname)) + return; mod = new_module(modname); @@ -881,14 +1272,28 @@ static void read_symbols(char *modname) mod->skip = 1; } + license = get_modinfo(info.modinfo, info.modinfo_len, "license"); + while (license) { + if (license_is_gpl_compatible(license)) + mod->gpl_compatible = 1; + else { + mod->gpl_compatible = 0; + break; + } + license = get_next_modinfo(info.modinfo, info.modinfo_len, + "license", license); + } + for (sym = info.symtab_start; sym < info.symtab_stop; sym++) { symname = info.strtab + sym->st_name; handle_modversions(mod, &info, sym, symname); handle_moddevtable(mod, &info, sym, symname); } - check_sec_ref(mod, modname, &info, init_section, init_section_ref_ok); - check_sec_ref(mod, modname, &info, exit_section, exit_section_ref_ok); + if (is_vmlinux(modname) && vmlinux_section_warnings) { + check_sec_ref(mod, modname, &info, init_section, init_section_ref_ok); + check_sec_ref(mod, modname, &info, exit_section, exit_section_ref_ok); + } version = get_modinfo(info.modinfo, info.modinfo_len, "version"); if (version) @@ -923,25 +1328,81 @@ void __attribute__((format(printf, 2, 3))) buf_printf(struct buffer *buf, va_start(ap, fmt); len = vsnprintf(tmp, SZ, fmt, ap); - if (buf->size - buf->pos < len + 1) { - buf->size += 128; - buf->p = realloc(buf->p, buf->size); - } - strncpy(buf->p + buf->pos, tmp, len + 1); - buf->pos += len; + buf_write(buf, tmp, len); va_end(ap); } void buf_write(struct buffer *buf, const char *s, int len) { if (buf->size - buf->pos < len) { - buf->size += len; + buf->size += len + SZ; buf->p = realloc(buf->p, buf->size); } strncpy(buf->p + buf->pos, s, len); buf->pos += len; } +static void check_for_gpl_usage(enum export exp, const char *m, const char *s) +{ + const char *e = is_vmlinux(m) ?"":".ko"; + + switch (exp) { + case export_gpl: + fatal("modpost: GPL-incompatible module %s%s " + "uses GPL-only symbol '%s'\n", m, e, s); + break; + case export_unused_gpl: + fatal("modpost: GPL-incompatible module %s%s " + "uses GPL-only symbol marked UNUSED '%s'\n", m, e, s); + break; + case export_gpl_future: + warn("modpost: GPL-incompatible module %s%s " + "uses future GPL-only symbol '%s'\n", m, e, s); + break; + case export_plain: + case export_unused: + case export_unknown: + /* ignore */ + break; + } +} + +static void check_for_unused(enum export exp, const char* m, const char* s) +{ + const char *e = is_vmlinux(m) ?"":".ko"; + + switch (exp) { + case export_unused: + case export_unused_gpl: + warn("modpost: module %s%s " + "uses symbol '%s' marked UNUSED\n", m, e, s); + break; + default: + /* ignore */ + break; + } +} + +static void check_exports(struct module *mod) +{ + struct symbol *s, *exp; + + for (s = mod->unres; s; s = s->next) { + const char *basename; + exp = find_symbol(s->name); + if (!exp || exp->module == mod) + continue; + basename = strrchr(mod->name, '/'); + if (basename) + basename++; + else + basename = mod->name; + if (!mod->gpl_compatible) + check_for_gpl_usage(exp->export, basename, exp->name); + check_for_unused(exp->export, basename, exp->name); + } +} + /** * Header for the generated file **/ @@ -962,22 +1423,31 @@ static void add_header(struct buffer *b, struct module *mod) buf_printf(b, "#ifdef CONFIG_MODULE_UNLOAD\n" " .exit = cleanup_module,\n" "#endif\n"); + buf_printf(b, " .arch = MODULE_ARCH_INIT,\n"); buf_printf(b, "};\n"); } /** * Record CRCs for unresolved symbols **/ -static void add_versions(struct buffer *b, struct module *mod) +static int add_versions(struct buffer *b, struct module *mod) { struct symbol *s, *exp; + int err = 0; for (s = mod->unres; s; s = s->next) { exp = find_symbol(s->name); if (!exp || exp->module == mod) { - if (have_vmlinux && !s->weak) - warn("\"%s\" [%s.ko] undefined!\n", - s->name, mod->name); + if (have_vmlinux && !s->weak) { + if (warn_unresolved) { + warn("\"%s\" [%s.ko] undefined!\n", + s->name, mod->name); + } else { + merror("\"%s\" [%s.ko] undefined!\n", + s->name, mod->name); + err = 1; + } + } continue; } s->module = exp->module; @@ -986,7 +1456,7 @@ static void add_versions(struct buffer *b, struct module *mod) } if (!modversions) - return; + return err; buf_printf(b, "\n"); buf_printf(b, "static const struct modversion_info ____versions[]\n"); @@ -1006,6 +1476,8 @@ static void add_versions(struct buffer *b, struct module *mod) } buf_printf(b, "};\n"); + + return err; } static void add_depends(struct buffer *b, struct module *mod, @@ -1025,6 +1497,7 @@ static void add_depends(struct buffer *b, struct module *mod, buf_printf(b, "__attribute__((section(\".modinfo\"))) =\n"); buf_printf(b, "\"depends="); for (s = mod->unres; s; s = s->next) { + const char *p; if (!s->module) continue; @@ -1032,8 +1505,11 @@ static void add_depends(struct buffer *b, struct module *mod, continue; s->module->seen = 1; - buf_printf(b, "%s%s", first ? "" : ",", - strrchr(s->module->name, '/') + 1); + if ((p = strrchr(s->module->name, '/')) != NULL) + p++; + else + p = s->module->name; + buf_printf(b, "%s%s", first ? "" : ",", p); first = 0; } buf_printf(b, "\";\n"); @@ -1092,6 +1568,9 @@ static void write_if_changed(struct buffer *b, const char *fname) fclose(file); } +/* parse Module.symvers file. line format: + * 0x12345678symbolmodule[[export]something] + **/ static void read_dump(const char *fname, unsigned int kernel) { unsigned long size, pos = 0; @@ -1103,7 +1582,7 @@ static void read_dump(const char *fname, unsigned int kernel) return; while ((line = get_next_line(&pos, file, size))) { - char *symname, *modname, *d; + char *symname, *modname, *d, *export, *end; unsigned int crc; struct module *mod; struct symbol *s; @@ -1114,8 +1593,10 @@ static void read_dump(const char *fname, unsigned int kernel) if (!(modname = strchr(symname, '\t'))) goto fail; *modname++ = '\0'; - if (strchr(modname, '\t')) - goto fail; + if ((export = strchr(modname, '\t')) != NULL) + *export++ = '\0'; + if (export && ((end = strchr(export, '\t')) != NULL)) + *end = '\0'; crc = strtoul(line, &d, 16); if (*symname == '\0' || *modname == '\0' || *d != '\0') goto fail; @@ -1127,10 +1608,10 @@ static void read_dump(const char *fname, unsigned int kernel) mod = new_module(NOFAIL(strdup(modname))); mod->skip = 1; } - s = sym_add_exported(symname, mod); + s = sym_add_exported(symname, mod, export_no(export)); s->kernel = kernel; s->preloaded = 1; - sym_update_crc(symname, mod, crc); + sym_update_crc(symname, mod, crc, export_no(export)); } return; fail: @@ -1160,9 +1641,10 @@ static void write_dump(const char *fname) symbol = symbolhash[n]; while (symbol) { if (dump_sym(symbol)) - buf_printf(&buf, "0x%08x\t%s\t%s\n", + buf_printf(&buf, "0x%08x\t%s\t%s\t%s\n", symbol->crc, symbol->name, - symbol->module->name); + symbol->module->name, + export_str(symbol->export)); symbol = symbol->next; } } @@ -1177,8 +1659,9 @@ int main(int argc, char **argv) char *kernel_read = NULL, *module_read = NULL; char *dump_write = NULL; int opt; + int err; - while ((opt = getopt(argc, argv, "i:I:mo:a")) != -1) { + while ((opt = getopt(argc, argv, "i:I:mso:aw")) != -1) { switch(opt) { case 'i': kernel_read = optarg; @@ -1196,6 +1679,12 @@ int main(int argc, char **argv) case 'a': all_versions = 1; break; + case 's': + vmlinux_section_warnings = 0; + break; + case 'w': + warn_unresolved = 1; + break; default: exit(1); } @@ -1213,11 +1702,19 @@ int main(int argc, char **argv) for (mod = modules; mod; mod = mod->next) { if (mod->skip) continue; + check_exports(mod); + } + + err = 0; + + for (mod = modules; mod; mod = mod->next) { + if (mod->skip) + continue; buf.pos = 0; add_header(&buf, mod); - add_versions(&buf, mod); + err |= add_versions(&buf, mod); add_depends(&buf, mod, modules); add_moddevtable(&buf, mod); add_srcversion(&buf, mod); @@ -1229,5 +1726,5 @@ int main(int argc, char **argv) if (dump_write) write_dump(dump_write); - return 0; + return err; }