X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=scripts%2Fmod%2Fmodpost.c;h=82e019bf2dc587c6b5444b519d9632f463a26839;hb=118c0ace1b61433311ae4e067fdd32841baa7f91;hp=5b076ef51996629bc8bdc24f0fa37268019844e5;hpb=fededcd2af6219de69b252b7d3ea4b4ec2f33c7a;p=safe%2Fjmp%2Flinux-2.6 diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 5b076ef..82e019b 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -2,7 +2,7 @@ * * Copyright 2003 Kai Germaschewski * Copyright 2002-2004 Rusty Russell, IBM Corporation - * + * Copyright 2006 Sam Ravnborg * Based in part on module-init-tools/depmod.c,file2alias * * This software may be used and distributed according to the terms @@ -13,6 +13,7 @@ #include #include "modpost.h" +#include "../../include/linux/license.h" /* Are we using CONFIG_MODVERSIONS? */ int modversions = 0; @@ -22,6 +23,13 @@ int have_vmlinux = 0; static int all_versions = 0; /* If we are modposting external module set to 1 */ static int external_module = 0; +/* 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 +}; void fatal(const char *fmt, ...) { @@ -85,7 +93,7 @@ static struct module *new_module(char *modname) { struct module *mod; char *p, *s; - + mod = NOFAIL(malloc(sizeof(*mod))); memset(mod, 0, sizeof(*mod)); p = NOFAIL(strdup(modname)); @@ -97,6 +105,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 +127,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 +163,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 +172,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,19 +191,66 @@ 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: duplicate symbol '%s' previous definition " + warn("%s: '%s' exported twice. Previous export " "was in %s%s\n", mod->name, name, s->module->name, is_vmlinux(s->module->name) ?"":".ko"); @@ -200,16 +259,17 @@ static struct symbol *sym_add_exported(const char *name, struct 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; } @@ -283,7 +343,7 @@ static void parse_elf(struct elf_info *info, const char *filename) hdr = grab_file(filename, &info->size); if (!hdr) { perror(filename); - abort(); + exit(1); } info->hdr = hdr; if (info->size < sizeof(*hdr)) @@ -309,20 +369,32 @@ static void parse_elf(struct elf_info *info, const char *filename) 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) { + 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; info->symtab_start = (void *)hdr + sechdrs[i].sh_offset; - info->symtab_stop = (void *)hdr + sechdrs[i].sh_offset + info->symtab_stop = (void *)hdr + sechdrs[i].sh_offset + sechdrs[i].sh_size; - info->strtab = (void *)hdr + + info->strtab = (void *)hdr + sechdrs[sechdrs[i].sh_link].sh_offset; } if (!info->symtab_start) { @@ -346,13 +418,14 @@ static void parse_elf_finish(struct elf_info *info) release_file(info->hdr, info->size); } -#define CRC_PFX "__crc_" -#define KSYMTAB_PFX "__ksymtab_" +#define CRC_PFX MODULE_SYMBOL_PREFIX "__crc_" +#define KSYMTAB_PFX MODULE_SYMBOL_PREFIX "__ksymtab_" 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 +435,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: @@ -387,15 +461,15 @@ static void handle_modversions(struct module *mod, struct elf_info *info, /* Ignore register directives. */ if (ELF_ST_TYPE(sym->st_info) == STT_SPARC_REGISTER) break; - if (symname[0] == '.') { - char *munged = strdup(symname); - munged[0] = '_'; - munged[1] = toupper(munged[1]); - symname = munged; - } + if (symname[0] == '.') { + char *munged = strdup(symname); + munged[0] = '_'; + munged[1] = toupper(munged[1]); + symname = munged; + } } #endif - + if (memcmp(symname, MODULE_SYMBOL_PREFIX, strlen(MODULE_SYMBOL_PREFIX)) == 0) mod->unres = alloc_symbol(symname + @@ -406,7 +480,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 +512,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 +531,108 @@ 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 + **/ +static int strrcmp(const char *s, const char *sub) +{ + int slen, sublen; + + if (!s || !sub) + return 1; + + slen = strlen(s); + sublen = strlen(sub); + + if ((slen == 0) || (sublen == 0)) + return 1; + + if (sublen > slen) + return 1; + + return memcmp(s + slen - sublen, sub, sublen); +} + +/** + * Whitelist to allow certain references to pass with no warning. + * Pattern 1: + * If a module parameter is declared __initdata and permissions=0 + * then this is legal despite the warning generated. + * We cannot see value of permissions here, so just ignore + * this pattern. + * The pattern is identified by: + * tosec = .init.data + * fromsec = .data* + * atsym =__param* + * + * Pattern 2: + * 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 | .init.data + * fromsec = .data + * atsym = *driver, *_template, *_sht, *_ops, *_probe, *probe_one + **/ +static int secref_whitelist(const char *modname, const char *tosec, + const char *fromsec, const char *atsym) +{ + int f1 = 1, f2 = 1; + const char **s; + const char *pat2sym[] = { + "driver", + "_template", /* scsi uses *_template 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; + + if (f1) + return f1; + + /* Check for pattern 2 */ + if ((strcmp(tosec, ".init.text") != 0) && + (strcmp(tosec, ".exit.text") != 0) && + (strcmp(tosec, ".init.data") != 0)) + f2 = 0; + if (strcmp(fromsec, ".data") != 0) + f2 = 0; + + for (s = pat2sym; *s; s++) + if (strrcmp(atsym, *s) == 0) + f1 = 1; + if (f1 && f2) + return 1; + + /* Whitelist all references from .pci_fixup section if vmlinux */ + if (is_vmlinux(modname)) { + if ((strcmp(fromsec, ".pci_fixup") == 0) && + (strcmp(tosec, ".init.text") == 0)) + return 1; + } + return 0; +} + /** * Find symbol based on relocation record info. * In some cases the symbol supplied is a valid symbol so @@ -475,7 +657,10 @@ static Elf_Sym *find_elf_symbol(struct elf_info *elf, Elf_Addr addr, } /* - * Find symbols before or equal addr and after addr - in the section sec + * 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. + * The ELF format may have a better way to detect what type of symbol + * it is, but this works for now. **/ static void find_symbols_between(struct elf_info *elf, Elf_Addr addr, const char *sec, @@ -487,7 +672,7 @@ static void find_symbols_between(struct elf_info *elf, Elf_Addr addr, Elf_Addr afterdiff = ~0; const char *secstrings = (void *)hdr + elf->sechdrs[hdr->e_shstrndx].sh_offset; - + *before = NULL; *after = NULL; @@ -504,6 +689,12 @@ static void find_symbols_between(struct elf_info *elf, Elf_Addr addr, 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; + } } else { @@ -511,6 +702,12 @@ static void find_symbols_between(struct elf_info *elf, Elf_Addr addr, afterdiff = sym->st_value - 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; + } } } } @@ -518,6 +715,7 @@ static void find_symbols_between(struct elf_info *elf, Elf_Addr addr, /** * Print a warning about a section mismatch. * Try to find symbols near it so user can find it. + * Check whitelist before warning - it may be a false positive. **/ static void warn_sec_mismatch(const char *modname, const char *fromsec, struct elf_info *elf, Elf_Sym *sym, Elf_Rela r) @@ -530,13 +728,19 @@ static void warn_sec_mismatch(const char *modname, const char *fromsec, const char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; const char *secname = secstrings + sechdrs[sym->st_shndx].sh_name; - + find_symbols_between(elf, r.r_offset, fromsec, &before, &after); refsym = find_elf_symbol(elf, r.r_addend, sym); if (refsym && strlen(elf->strtab + refsym->st_name)) refsymname = elf->strtab + refsym->st_name; - + + /* check whitelist - we may ignore it */ + if (before && + secref_whitelist(modname, secname, fromsec, + elf->strtab + before->st_name)) + return; + if (before && after) { warn("%s - Section mismatch: reference to %s:%s from %s " "between '%s' (at offset 0x%llx) and '%s'\n", @@ -547,14 +751,14 @@ static void warn_sec_mismatch(const char *modname, const char *fromsec, } else if (before) { warn("%s - Section mismatch: reference to %s:%s from %s " "after '%s' (at offset 0x%llx)\n", - modname, secname, refsymname, fromsec, + modname, secname, refsymname, fromsec, elf->strtab + before->st_name, (long long)r.r_offset); } else if (after) { warn("%s - Section mismatch: reference to %s:%s from %s " "before '%s' (at offset -0x%llx)\n", - modname, secname, refsymname, fromsec, - elf->strtab + before->st_name, + modname, secname, refsymname, fromsec, + elf->strtab + after->st_name, (long long)r.r_offset); } else { warn("%s - Section mismatch: reference to %s:%s from %s " @@ -587,32 +791,82 @@ static void check_sec_ref(struct module *mod, const char *modname, Elf_Shdr *sechdrs = elf->sechdrs; const char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; - + /* 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) { + r_sym = ELF64_MIPS_R_SYM(rela->r_info); + r_sym = TO_NATIVE(r_sym); + } 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) { + r_sym = ELF64_MIPS_R_SYM(rel->r_info); + r_sym = TO_NATIVE(r_sym); + } 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; + 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); + } } } } @@ -635,13 +889,13 @@ static int init_section(const char *name) /** * Identify sections from which references to a .init section is OK. - * + * * Unfortunately references to read only data that referenced .init * sections had to be excluded. Almost all of these are false * positives, they are created by gcc. The downside of excluding rodata * is that there really are some user references from rodata to * init code, e.g. drivers/video/vgacon.c: - * + * * const struct consw vga_con = { * con_startup: vgacon_startup, * @@ -654,13 +908,23 @@ static int init_section_ref_ok(const char *name) /* Absolute section names */ const char *namelist1[] = { ".init", + ".opd", /* see comment [OPD] at exit_section_ref_ok() */ + ".toc1", /* used by ppc64 */ ".stab", ".rodata", + ".parainstructions", ".text.lock", + "__bug_table", /* used by powerpc for BUG() */ ".pci_fixup_header", ".pci_fixup_final", ".pdr", "__param", + "__ex_table", + ".fixup", + ".smp_locks", + ".plt", /* seen on ARCH=um build on x86_64. Harmless */ + "__ftr_fixup", /* powerpc cpu feature fixup */ + "__fw_ftr_fixup", /* powerpc firmware feature fixup */ NULL }; /* Start of section names */ @@ -669,15 +933,26 @@ static int init_section_ref_ok(const char *name) ".altinstructions", ".eh_frame", ".debug", + ".parainstructions", + NULL + }; + /* part of section name */ + const char *namelist3 [] = { + ".unwind", /* sample: IA_64.unwind.init.text */ NULL }; - + for (s = namelist1; *s; s++) if (strcmp(*s, name) == 0) return 1; - for (s = namelist2; *s; s++) + 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; + if (strrcmp(name, ".init") == 0) + return 1; return 0; } @@ -695,16 +970,17 @@ static int exit_section(const char *name) if (strcmp(name, ".exit.data") == 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. + * [.rodata] like for .init.text we ignore .rodata references -same reason **/ static int exit_section_ref_ok(const char *name) { @@ -714,12 +990,20 @@ static int exit_section_ref_ok(const char *name) ".exit.text", ".exit.data", ".init.text", + ".rodata", ".opd", /* See comment [OPD] */ + ".toc1", /* used by ppc64 */ ".altinstructions", ".pdr", + "__bug_table", /* used by powerpc for BUG() */ ".exitcall.exit", ".eh_frame", + ".parainstructions", ".stab", + "__ex_table", + ".fixup", + ".smp_locks", + ".plt", /* seen on ARCH=um build on x86_64. Harmless */ NULL }; /* Start of section names */ @@ -727,13 +1011,21 @@ static int exit_section_ref_ok(const char *name) ".debug", 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++) + 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; } @@ -741,6 +1033,7 @@ static void read_symbols(char *modname) { const char *symname; char *version; + char *license; struct module *mod; struct elf_info info = { }; Elf_Sym *sym; @@ -756,6 +1049,18 @@ 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; @@ -795,28 +1100,84 @@ void __attribute__((format(printf, 2, 3))) buf_printf(struct buffer *buf, char tmp[SZ]; int len; va_list ap; - + 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 **/ @@ -843,16 +1204,19 @@ static void add_header(struct buffer *b, struct module *mod) /** * 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) + if (have_vmlinux && !s->weak) { warn("\"%s\" [%s.ko] undefined!\n", s->name, mod->name); + err = warn_unresolved ? 0 : 1; + } continue; } s->module = exp->module; @@ -861,7 +1225,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"); @@ -881,6 +1245,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, @@ -967,6 +1333,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; @@ -978,7 +1347,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; @@ -989,8 +1358,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; @@ -1002,10 +1373,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: @@ -1024,7 +1395,7 @@ static int dump_sym(struct symbol *sym) return 0; return 1; } - + static void write_dump(const char *fname) { struct buffer buf = { }; @@ -1035,9 +1406,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", - symbol->crc, symbol->name, - symbol->module->name); + buf_printf(&buf, "0x%08x\t%s\t%s\t%s\n", + symbol->crc, symbol->name, + symbol->module->name, + export_str(symbol->export)); symbol = symbol->next; } } @@ -1052,8 +1424,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:mo:aw")) != -1) { switch(opt) { case 'i': kernel_read = optarg; @@ -1071,6 +1444,9 @@ int main(int argc, char **argv) case 'a': all_versions = 1; break; + case 'w': + warn_unresolved = 1; + break; default: exit(1); } @@ -1088,11 +1464,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); @@ -1104,5 +1488,5 @@ int main(int argc, char **argv) if (dump_write) write_dump(dump_write); - return 0; + return err; }