diff options
Diffstat (limited to 'scripts/mod')
-rw-r--r-- | scripts/mod/list.h | 213 | ||||
-rw-r--r-- | scripts/mod/modpost.c | 674 | ||||
-rw-r--r-- | scripts/mod/modpost.h | 31 | ||||
-rw-r--r-- | scripts/mod/sumversion.c | 19 |
4 files changed, 589 insertions, 348 deletions
diff --git a/scripts/mod/list.h b/scripts/mod/list.h new file mode 100644 index 000000000000..a924a6c4aa4d --- /dev/null +++ b/scripts/mod/list.h @@ -0,0 +1,213 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef LIST_H +#define LIST_H + +#include <stdbool.h> +#include <stddef.h> + +/* Are two types/vars the same type (ignoring qualifiers)? */ +#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b)) + +/** + * container_of - cast a member of a structure out to the containing structure + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + void *__mptr = (void *)(ptr); \ + _Static_assert(__same_type(*(ptr), ((type *)0)->member) || \ + __same_type(*(ptr), void), \ + "pointer type mismatch in container_of()"); \ + ((type *)(__mptr - offsetof(type, member))); }) + +#define LIST_POISON1 ((void *) 0x100) +#define LIST_POISON2 ((void *) 0x122) + +/* + * Circular doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +/** + * INIT_LIST_HEAD - Initialize a list_head structure + * @list: list_head structure to be initialized. + * + * Initializes the list_head to point to itself. If it is a list header, + * the result is an empty list. + */ +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head *prev, struct list_head *next) +{ + next->prev = prev; + prev->next = next; +} + +static inline void __list_del_entry(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty() on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del_entry(entry); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + +/** + * list_is_head - tests whether @list is the list @head + * @list: the entry to test + * @head: the head of the list + */ +static inline int list_is_head(const struct list_head *list, const struct list_head *head) +{ + return list == head; +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_first_entry - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +/** + * list_next_entry - get the next element in list + * @pos: the type * to cursor + * @member: the name of the list_head within the struct. + */ +#define list_next_entry(pos, member) \ + list_entry((pos)->member.next, typeof(*(pos)), member) + +/** + * list_entry_is_head - test if the entry points to the head of the list + * @pos: the type * to cursor + * @head: the head for your list. + * @member: the name of the list_head within the struct. + */ +#define list_entry_is_head(pos, head, member) \ + (&pos->member == (head)) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_first_entry(head, typeof(*pos), member); \ + !list_entry_is_head(pos, head, member); \ + pos = list_next_entry(pos, member)) + +/** + * list_for_each_entry_safe - iterate over list of given type. Safe against removal of list entry + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_head within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_first_entry(head, typeof(*pos), member), \ + n = list_next_entry(pos, member); \ + !list_entry_is_head(pos, head, member); \ + pos = n, n = list_next_entry(n, member)) + +#endif /* LIST_H */ diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index ed9d056d2108..42e949cbc255 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -23,20 +23,20 @@ #include "../../include/linux/license.h" /* Are we using CONFIG_MODVERSIONS? */ -static int modversions = 0; +static bool modversions; /* Is CONFIG_MODULE_SRCVERSION_ALL set? */ -static int all_versions = 0; +static bool all_versions; /* If we are modposting external module set to 1 */ -static int external_module = 0; +static bool external_module; /* Only warn about unresolved symbols */ -static int warn_unresolved = 0; -/* How a symbol is exported */ -static int sec_mismatch_count = 0; -static int sec_mismatch_warn_only = true; +static bool warn_unresolved; + +static int sec_mismatch_count; +static bool sec_mismatch_warn_only = true; /* ignore missing files */ -static int ignore_missing_files; +static bool ignore_missing_files; /* If set to 1, only warn (instead of error) about missing ns imports */ -static int allow_missing_ns_imports; +static bool allow_missing_ns_imports; static bool error_occurred; @@ -47,12 +47,6 @@ static bool error_occurred; #define MAX_UNRESOLVED_REPORTS 10 static unsigned int nr_unresolved; -enum export { - export_plain, - export_gpl, - export_unknown -}; - /* In kernel, this size is defined in linux/module.h; * here we use Elf_Addr instead of long for covering cross-compile */ @@ -165,16 +159,17 @@ char *get_line(char **stringp) } /* A list of all modules we processed */ -static struct module *modules; +LIST_HEAD(modules); static struct module *find_module(const char *modname) { struct module *mod; - for (mod = modules; mod; mod = mod->next) + list_for_each_entry(mod, &modules, list) { if (strcmp(mod->name, modname) == 0) - break; - return mod; + return mod; + } + return NULL; } static struct module *new_module(const char *modname) @@ -184,12 +179,22 @@ static struct module *new_module(const char *modname) mod = NOFAIL(malloc(sizeof(*mod) + strlen(modname) + 1)); memset(mod, 0, sizeof(*mod)); - /* add to list */ + INIT_LIST_HEAD(&mod->exported_symbols); + INIT_LIST_HEAD(&mod->unresolved_symbols); + INIT_LIST_HEAD(&mod->missing_namespaces); + INIT_LIST_HEAD(&mod->imported_namespaces); + strcpy(mod->name, modname); mod->is_vmlinux = (strcmp(modname, "vmlinux") == 0); - mod->gpl_compatible = -1; - mod->next = modules; - modules = mod; + + /* + * Set mod->is_gpl_compatible to true by default. If MODULE_LICENSE() + * is missing, do not check the use for EXPORT_SYMBOL_GPL() becasue + * modpost will exit wiht error anyway. + */ + mod->is_gpl_compatible = true; + + list_add_tail(&mod->list, &modules); return mod; } @@ -201,13 +206,14 @@ static struct module *new_module(const char *modname) struct symbol { struct symbol *next; + struct list_head list; /* link to module::exported_symbols or module::unresolved_symbols */ struct module *module; - unsigned int crc; - int crc_valid; char *namespace; - unsigned int weak:1; - unsigned int is_static:1; /* 1 if symbol is not global */ - enum export export; /* Type of export */ + unsigned int crc; + bool crc_valid; + bool weak; + bool is_static; /* true if symbol is not global */ + bool is_gpl_only; /* exported by EXPORT_SYMBOL_GPL */ char name[]; }; @@ -230,32 +236,37 @@ static inline unsigned int tdb_hash(const char *name) * Allocate a new symbols for use in the hash of exported symbols or * the list of unresolved symbols per module **/ -static struct symbol *alloc_symbol(const char *name, unsigned int weak, - struct symbol *next) +static struct symbol *alloc_symbol(const char *name) { struct symbol *s = NOFAIL(malloc(sizeof(*s) + strlen(name) + 1)); memset(s, 0, sizeof(*s)); strcpy(s->name, name); - s->weak = weak; - s->next = next; - s->is_static = 1; + s->is_static = true; return s; } /* For the hash of exported symbols */ -static struct symbol *new_symbol(const char *name, struct module *module, - enum export export) +static void hash_add_symbol(struct symbol *sym) { unsigned int hash; - hash = tdb_hash(name) % SYMBOL_HASH_SIZE; - symbolhash[hash] = alloc_symbol(name, 0, symbolhash[hash]); + hash = tdb_hash(sym->name) % SYMBOL_HASH_SIZE; + sym->next = symbolhash[hash]; + symbolhash[hash] = sym; +} + +static void sym_add_unresolved(const char *name, struct module *mod, bool weak) +{ + struct symbol *sym; - return symbolhash[hash]; + sym = alloc_symbol(name); + sym->weak = weak; + + list_add_tail(&sym->list, &mod->unresolved_symbols); } -static struct symbol *find_symbol(const char *name) +static struct symbol *sym_find_with_module(const char *name, struct module *mod) { struct symbol *s; @@ -264,67 +275,44 @@ static struct symbol *find_symbol(const char *name) name++; for (s = symbolhash[tdb_hash(name) % SYMBOL_HASH_SIZE]; s; s = s->next) { - if (strcmp(s->name, name) == 0) + if (strcmp(s->name, name) == 0 && (!mod || s->module == mod)) return s; } return NULL; } -static bool contains_namespace(struct namespace_list *list, - const char *namespace) +static struct symbol *find_symbol(const char *name) { - for (; list; list = list->next) + return sym_find_with_module(name, NULL); +} + +struct namespace_list { + struct list_head list; + char namespace[]; +}; + +static bool contains_namespace(struct list_head *head, const char *namespace) +{ + struct namespace_list *list; + + list_for_each_entry(list, head, list) { if (!strcmp(list->namespace, namespace)) return true; + } return false; } -static void add_namespace(struct namespace_list **list, const char *namespace) +static void add_namespace(struct list_head *head, const char *namespace) { struct namespace_list *ns_entry; - if (!contains_namespace(*list, namespace)) { - ns_entry = NOFAIL(malloc(sizeof(struct namespace_list) + + if (!contains_namespace(head, namespace)) { + ns_entry = NOFAIL(malloc(sizeof(*ns_entry) + strlen(namespace) + 1)); strcpy(ns_entry->namespace, namespace); - ns_entry->next = *list; - *list = ns_entry; - } -} - -static bool module_imports_namespace(struct module *module, - const char *namespace) -{ - return contains_namespace(module->imported_namespaces, namespace); -} - -static const struct { - const char *str; - enum export export; -} export_list[] = { - { .str = "EXPORT_SYMBOL", .export = export_plain }, - { .str = "EXPORT_SYMBOL_GPL", .export = export_gpl }, - { .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; + list_add_tail(&ns_entry->list, head); } - return export_unknown; } static void *sym_get_data_by_offset(const struct elf_info *info, @@ -357,35 +345,6 @@ static const char *sec_name(const struct elf_info *info, int secindex) #define strstarts(str, prefix) (strncmp(str, prefix, strlen(prefix)) == 0) -static enum export export_from_secname(struct elf_info *elf, unsigned int sec) -{ - const char *secname = sec_name(elf, sec); - - if (strstarts(secname, "___ksymtab+")) - return export_plain; - else if (strstarts(secname, "___ksymtab_gpl+")) - return export_gpl; - else - return export_unknown; -} - -static enum export export_from_sec(struct elf_info *elf, unsigned int sec) -{ - if (sec == elf->export_sec) - return export_plain; - else if (sec == elf->export_gpl_sec) - return export_gpl; - else - return export_unknown; -} - -static const char *namespace_from_kstrtabns(const struct elf_info *info, - const Elf_Sym *sym) -{ - const char *value = sym_get_data(info, sym); - return value[0] ? value : NULL; -} - static void sym_update_namespace(const char *symname, const char *namespace) { struct symbol *s = find_symbol(symname); @@ -401,47 +360,33 @@ static void sym_update_namespace(const char *symname, const char *namespace) } free(s->namespace); - s->namespace = - namespace && namespace[0] ? NOFAIL(strdup(namespace)) : NULL; + s->namespace = namespace[0] ? NOFAIL(strdup(namespace)) : NULL; } -/** - * 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, - enum export export) + bool gpl_only) { struct symbol *s = find_symbol(name); - if (!s) { - s = new_symbol(name, mod, export); - } else if (!external_module || s->module->is_vmlinux || - s->module == mod) { - warn("%s: '%s' exported twice. Previous export was in %s%s\n", - mod->name, name, s->module->name, - s->module->is_vmlinux ? "" : ".ko"); - return s; + if (s && (!external_module || s->module->is_vmlinux || s->module == mod)) { + error("%s: '%s' exported twice. Previous export was in %s%s\n", + mod->name, name, s->module->name, + s->module->is_vmlinux ? "" : ".ko"); } + s = alloc_symbol(name); s->module = mod; - s->export = export; + s->is_gpl_only = gpl_only; + list_add_tail(&s->list, &mod->exported_symbols); + hash_add_symbol(s); + return s; } -static void sym_set_crc(const char *name, unsigned int crc) +static void sym_set_crc(struct symbol *sym, unsigned int crc) { - struct symbol *s = find_symbol(name); - - /* - * Ignore stand-alone __crc_*, which might be auto-generated symbols - * such as __*_veneer in ARM ELF. - */ - if (!s) - return; - - s->crc = crc; - s->crc_valid = 1; + sym->crc = crc; + sym->crc_valid = true; } static void *grab_file(const char *filename, size_t *size) @@ -576,10 +521,7 @@ static int parse_elf(struct elf_info *info, const char *filename) fatal("%s has NOBITS .modinfo\n", filename); 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_gpl") == 0) - info->export_gpl_sec = i; + } if (sechdrs[i].sh_type == SHT_SYMTAB) { unsigned int sh_link_idx; @@ -667,44 +609,9 @@ static int ignore_undef_symbol(struct elf_info *info, const char *symname) return 0; } -static void handle_modversion(const struct module *mod, - const struct elf_info *info, - const Elf_Sym *sym, const char *symname) -{ - unsigned int crc; - - if (sym->st_shndx == SHN_UNDEF) { - warn("EXPORT symbol \"%s\" [%s%s] version generation failed, symbol will not be versioned.\n" - "Is \"%s\" prototyped in <asm/asm-prototypes.h>?\n", - symname, mod->name, mod->is_vmlinux ? "" : ".ko", - symname); - - return; - } - - if (sym->st_shndx == SHN_ABS) { - crc = sym->st_value; - } else { - unsigned int *crcp; - - /* symbol points to the CRC in the ELF object */ - crcp = sym_get_data(info, sym); - crc = TO_NATIVE(*crcp); - } - sym_set_crc(symname, crc); -} - static void handle_symbol(struct module *mod, struct elf_info *info, const Elf_Sym *sym, const char *symname) { - enum export export; - const char *name; - - if (strstarts(symname, "__ksymtab")) - export = export_from_secname(info, get_secindex(info, sym)); - else - export = export_from_sec(info, get_secindex(info, sym)); - switch (sym->st_shndx) { case SHN_COMMON: if (strstarts(symname, "__gnu_lto_")) { @@ -732,20 +639,26 @@ static void handle_symbol(struct module *mod, struct elf_info *info, } } - mod->unres = alloc_symbol(symname, - ELF_ST_BIND(sym->st_info) == STB_WEAK, - mod->unres); + sym_add_unresolved(symname, mod, + ELF_ST_BIND(sym->st_info) == STB_WEAK); break; default: /* All exported symbols */ if (strstarts(symname, "__ksymtab_")) { + const char *name, *secname; + name = symname + strlen("__ksymtab_"); - sym_add_exported(name, mod, export); + secname = sec_name(info, get_secindex(info, sym)); + + if (strstarts(secname, "___ksymtab_gpl+")) + sym_add_exported(name, mod, true); + else if (strstarts(secname, "___ksymtab+")) + sym_add_exported(name, mod, false); } if (strcmp(symname, "init_module") == 0) - mod->has_init = 1; + mod->has_init = true; if (strcmp(symname, "cleanup_module") == 0) - mod->has_cleanup = 1; + mod->has_cleanup = true; break; } } @@ -2003,6 +1916,104 @@ static char *remove_dot(char *s) return s; } +/* + * The CRCs are recorded in .*.cmd files in the form of: + * #SYMVER <name> <crc> + */ +static void extract_crcs_for_object(const char *object, struct module *mod) +{ + char cmd_file[PATH_MAX]; + char *buf, *p; + const char *base; + int dirlen, ret; + + base = strrchr(object, '/'); + if (base) { + base++; + dirlen = base - object; + } else { + dirlen = 0; + base = object; + } + + ret = snprintf(cmd_file, sizeof(cmd_file), "%.*s.%s.cmd", + dirlen, object, base); + if (ret >= sizeof(cmd_file)) { + error("%s: too long path was truncated\n", cmd_file); + return; + } + + buf = read_text_file(cmd_file); + p = buf; + + while ((p = strstr(p, "\n#SYMVER "))) { + char *name; + size_t namelen; + unsigned int crc; + struct symbol *sym; + + name = p + strlen("\n#SYMVER "); + + p = strchr(name, ' '); + if (!p) + break; + + namelen = p - name; + p++; + + if (!isdigit(*p)) + continue; /* skip this line */ + + crc = strtol(p, &p, 0); + if (*p != '\n') + continue; /* skip this line */ + + name[namelen] = '\0'; + + /* + * sym_find_with_module() may return NULL here. + * It typically occurs when CONFIG_TRIM_UNUSED_KSYMS=y. + * Since commit e1327a127703, genksyms calculates CRCs of all + * symbols, including trimmed ones. Ignore orphan CRCs. + */ + sym = sym_find_with_module(name, mod); + if (sym) + sym_set_crc(sym, crc); + } + + free(buf); +} + +/* + * The symbol versions (CRC) are recorded in the .*.cmd files. + * Parse them to retrieve CRCs for the current module. + */ +static void mod_set_crcs(struct module *mod) +{ + char objlist[PATH_MAX]; + char *buf, *p, *obj; + int ret; + + if (mod->is_vmlinux) { + strcpy(objlist, ".vmlinux.objs"); + } else { + /* objects for a module are listed in the *.mod file. */ + ret = snprintf(objlist, sizeof(objlist), "%s.mod", mod->name); + if (ret >= sizeof(objlist)) { + error("%s: too long path was truncated\n", objlist); + return; + } + } + + buf = read_text_file(objlist); + p = buf; + + while ((obj = strsep(&p, "\n")) && obj[0]) + extract_crcs_for_object(obj, mod); + + free(buf); +} + static void read_symbols(const char *modname) { const char *symname; @@ -2034,10 +2045,8 @@ static void read_symbols(const char *modname) if (!license) error("missing MODULE_LICENSE() in %s\n", modname); while (license) { - if (license_is_gpl_compatible(license)) - mod->gpl_compatible = 1; - else { - mod->gpl_compatible = 0; + if (!license_is_gpl_compatible(license)) { + mod->is_gpl_compatible = false; break; } license = get_next_modinfo(&info, "license", license); @@ -2064,12 +2073,7 @@ static void read_symbols(const char *modname) /* Apply symbol namespaces from __kstrtabns_<symbol> entries. */ if (strstarts(symname, "__kstrtabns_")) sym_update_namespace(symname + strlen("__kstrtabns_"), - namespace_from_kstrtabns(&info, - sym)); - - if (strstarts(symname, "__crc_")) - handle_modversion(mod, &info, sym, - symname + strlen("__crc_")); + sym_get_data(&info, sym)); } // check for static EXPORT_SYMBOL_* functions && global vars @@ -2082,7 +2086,7 @@ static void read_symbols(const char *modname) sym->st_name)); if (s) - s->is_static = 0; + s->is_static = false; } } @@ -2097,12 +2101,17 @@ static void read_symbols(const char *modname) parse_elf_finish(&info); - /* Our trick to get versioning for module struct etc. - it's - * never passed as an argument to an exported function, so - * the automatic versioning doesn't pick it up, but it's really - * important anyhow */ - if (modversions) - mod->unres = alloc_symbol("module_layout", 0, mod->unres); + if (modversions) { + /* + * Our trick to get versioning for module struct etc. - it's + * never passed as an argument to an exported function, so + * the automatic versioning doesn't pick it up, but it's really + * important anyhow. + */ + sym_add_unresolved("module_layout", mod, false); + + mod_set_crcs(mod); + } } static void read_symbols_from_files(const char *filename) @@ -2155,34 +2164,30 @@ void buf_write(struct buffer *buf, const char *s, int len) buf->pos += len; } -static void check_for_gpl_usage(enum export exp, const char *m, const char *s) -{ - switch (exp) { - case export_gpl: - error("GPL-incompatible module %s.ko uses GPL-only symbol '%s'\n", - m, s); - break; - case export_plain: - case export_unknown: - /* ignore */ - break; - } -} - static void check_exports(struct module *mod) { struct symbol *s, *exp; - for (s = mod->unres; s; s = s->next) { + list_for_each_entry(s, &mod->unresolved_symbols, list) { const char *basename; exp = find_symbol(s->name); - if (!exp || exp->module == mod) { + if (!exp) { if (!s->weak && nr_unresolved++ < MAX_UNRESOLVED_REPORTS) modpost_log(warn_unresolved ? LOG_WARN : LOG_ERROR, "\"%s\" [%s.ko] undefined!\n", s->name, mod->name); continue; } + if (exp->module == mod) { + error("\"%s\" [%s.ko] was exported without definition\n", + s->name, mod->name); + continue; + } + + s->module = exp->module; + s->crc_valid = exp->crc_valid; + s->crc = exp->crc; + basename = strrchr(mod->name, '/'); if (basename) basename++; @@ -2190,15 +2195,16 @@ static void check_exports(struct module *mod) basename = mod->name; if (exp->namespace && - !module_imports_namespace(mod, exp->namespace)) { + !contains_namespace(&mod->imported_namespaces, exp->namespace)) { modpost_log(allow_missing_ns_imports ? LOG_WARN : LOG_ERROR, "module %s uses symbol %s from namespace %s, but does not import it.\n", basename, exp->name, exp->namespace); add_namespace(&mod->missing_namespaces, exp->namespace); } - if (!mod->gpl_compatible) - check_for_gpl_usage(exp->export, basename, exp->name); + if (!mod->is_gpl_compatible && exp->is_gpl_only) + error("GPL-incompatible module %s.ko uses GPL-only symbol '%s'\n", + basename, exp->name); } } @@ -2228,6 +2234,7 @@ static void add_header(struct buffer *b, struct module *mod) buf_printf(b, "#define INCLUDE_VERMAGIC\n"); buf_printf(b, "#include <linux/build-salt.h>\n"); buf_printf(b, "#include <linux/elfnote-lto.h>\n"); + buf_printf(b, "#include <linux/export-internal.h>\n"); buf_printf(b, "#include <linux/vermagic.h>\n"); buf_printf(b, "#include <linux/compiler.h>\n"); buf_printf(b, "\n"); @@ -2248,26 +2255,41 @@ static void add_header(struct buffer *b, struct module *mod) "#endif\n"); buf_printf(b, "\t.arch = MODULE_ARCH_INIT,\n"); buf_printf(b, "};\n"); -} -static void add_intree_flag(struct buffer *b, int is_intree) -{ - if (is_intree) + if (!external_module) buf_printf(b, "\nMODULE_INFO(intree, \"Y\");\n"); -} -/* Cannot check for assembler */ -static void add_retpoline(struct buffer *b) -{ - buf_printf(b, "\n#ifdef CONFIG_RETPOLINE\n"); - buf_printf(b, "MODULE_INFO(retpoline, \"Y\");\n"); - buf_printf(b, "#endif\n"); + buf_printf(b, + "\n" + "#ifdef CONFIG_RETPOLINE\n" + "MODULE_INFO(retpoline, \"Y\");\n" + "#endif\n"); + + if (strstarts(mod->name, "drivers/staging")) + buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n"); } -static void add_staging_flag(struct buffer *b, const char *name) +static void add_exported_symbols(struct buffer *buf, struct module *mod) { - if (strstarts(name, "drivers/staging")) - buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n"); + struct symbol *sym; + + if (!modversions) + return; + + /* record CRCs for exported symbols */ + buf_printf(buf, "\n"); + list_for_each_entry(sym, &mod->exported_symbols, list) { + if (!sym->crc_valid) { + warn("EXPORT symbol \"%s\" [%s%s] version generation failed, symbol will not be versioned.\n" + "Is \"%s\" prototyped in <asm/asm-prototypes.h>?\n", + sym->name, mod->name, mod->is_vmlinux ? "" : ".ko", + sym->name); + continue; + } + + buf_printf(buf, "SYMBOL_CRC(%s, 0x%08x, \"%s\");\n", + sym->name, sym->crc, sym->is_gpl_only ? "_gpl" : ""); + } } /** @@ -2275,16 +2297,7 @@ static void add_staging_flag(struct buffer *b, const char *name) **/ static void add_versions(struct buffer *b, struct module *mod) { - struct symbol *s, *exp; - - for (s = mod->unres; s; s = s->next) { - exp = find_symbol(s->name); - if (!exp || exp->module == mod) - continue; - s->module = exp->module; - s->crc_valid = exp->crc_valid; - s->crc = exp->crc; - } + struct symbol *s; if (!modversions) return; @@ -2293,7 +2306,7 @@ static void add_versions(struct buffer *b, struct module *mod) buf_printf(b, "static const struct modversion_info ____versions[]\n"); buf_printf(b, "__used __section(\"__versions\") = {\n"); - for (s = mod->unres; s; s = s->next) { + list_for_each_entry(s, &mod->unresolved_symbols, list) { if (!s->module) continue; if (!s->crc_valid) { @@ -2319,13 +2332,14 @@ static void add_depends(struct buffer *b, struct module *mod) int first = 1; /* Clear ->seen flag of modules that own symbols needed by this. */ - for (s = mod->unres; s; s = s->next) + list_for_each_entry(s, &mod->unresolved_symbols, list) { if (s->module) s->module->seen = s->module->is_vmlinux; + } buf_printf(b, "\n"); buf_printf(b, "MODULE_INFO(depends, \""); - for (s = mod->unres; s; s = s->next) { + list_for_each_entry(s, &mod->unresolved_symbols, list) { const char *p; if (!s->module) continue; @@ -2333,7 +2347,7 @@ static void add_depends(struct buffer *b, struct module *mod) if (s->module->seen) continue; - s->module->seen = 1; + s->module->seen = true; p = strrchr(s->module->name, '/'); if (p) p++; @@ -2358,6 +2372,9 @@ static void write_buf(struct buffer *b, const char *fname) { FILE *file; + if (error_occurred) + return; + file = fopen(fname, "w"); if (!file) { perror(fname); @@ -2408,6 +2425,47 @@ static void write_if_changed(struct buffer *b, const char *fname) write_buf(b, fname); } +static void write_vmlinux_export_c_file(struct module *mod) +{ + struct buffer buf = { }; + + buf_printf(&buf, + "#include <linux/export-internal.h>\n"); + + add_exported_symbols(&buf, mod); + write_if_changed(&buf, ".vmlinux.export.c"); + free(buf.p); +} + +/* do sanity checks, and generate *.mod.c file */ +static void write_mod_c_file(struct module *mod) +{ + struct buffer buf = { }; + char fname[PATH_MAX]; + int ret; + + check_modname_len(mod); + check_exports(mod); + + add_header(&buf, mod); + add_exported_symbols(&buf, mod); + add_versions(&buf, mod); + add_depends(&buf, mod); + add_moddevtable(&buf, mod); + add_srcversion(&buf, mod); + + ret = snprintf(fname, sizeof(fname), "%s.mod.c", mod->name); + if (ret >= sizeof(fname)) { + error("%s: too long path was truncated\n", fname); + goto free; + } + + write_if_changed(&buf, fname); + +free: + free(buf.p); +} + /* parse Module.symvers file. line format: * 0x12345678<tab>symbol<tab>module<tab>export<tab>namespace **/ @@ -2427,6 +2485,7 @@ static void read_dump(const char *fname) unsigned int crc; struct module *mod; struct symbol *s; + bool gpl_only; if (!(symname = strchr(line, '\t'))) goto fail; @@ -2444,14 +2503,24 @@ static void read_dump(const char *fname) crc = strtoul(line, &d, 16); if (*symname == '\0' || *modname == '\0' || *d != '\0') goto fail; + + if (!strcmp(export, "EXPORT_SYMBOL_GPL")) { + gpl_only = true; + } else if (!strcmp(export, "EXPORT_SYMBOL")) { + gpl_only = false; + } else { + error("%s: unknown license %s. skip", symname, export); + continue; + } + mod = find_module(modname); if (!mod) { mod = new_module(modname); - mod->from_dump = 1; + mod->from_dump = true; } - s = sym_add_exported(symname, mod, export_no(export)); - s->is_static = 0; - sym_set_crc(symname, crc); + s = sym_add_exported(symname, mod, gpl_only); + s->is_static = false; + sym_set_crc(s, crc); sym_update_namespace(symname, namespace); } free(buf); @@ -2464,22 +2533,17 @@ fail: static void write_dump(const char *fname) { struct buffer buf = { }; - struct symbol *symbol; - const char *namespace; - int n; + struct module *mod; + struct symbol *sym; - for (n = 0; n < SYMBOL_HASH_SIZE ; n++) { - symbol = symbolhash[n]; - while (symbol) { - if (!symbol->module->from_dump) { - namespace = symbol->namespace; - buf_printf(&buf, "0x%08x\t%s\t%s\t%s\t%s\n", - symbol->crc, symbol->name, - symbol->module->name, - export_str(symbol->export), - namespace ? namespace : ""); - } - symbol = symbol->next; + list_for_each_entry(mod, &modules, list) { + if (mod->from_dump) + continue; + list_for_each_entry(sym, &mod->exported_symbols, list) { + buf_printf(&buf, "0x%08x\t%s\t%s\tEXPORT_SYMBOL%s\t%s\n", + sym->crc, sym->name, mod->name, + sym->is_gpl_only ? "_GPL" : "", + sym->namespace ?: ""); } } write_buf(&buf, fname); @@ -2492,14 +2556,14 @@ static void write_namespace_deps_files(const char *fname) struct namespace_list *ns; struct buffer ns_deps_buf = {}; - for (mod = modules; mod; mod = mod->next) { + list_for_each_entry(mod, &modules, list) { - if (mod->from_dump || !mod->missing_namespaces) + if (mod->from_dump || list_empty(&mod->missing_namespaces)) continue; buf_printf(&ns_deps_buf, "%s.ko:", mod->name); - for (ns = mod->missing_namespaces; ns; ns = ns->next) + list_for_each_entry(ns, &mod->missing_namespaces, list) buf_printf(&ns_deps_buf, " %s", ns->namespace); buf_printf(&ns_deps_buf, "\n"); @@ -2510,55 +2574,53 @@ static void write_namespace_deps_files(const char *fname) } struct dump_list { - struct dump_list *next; + struct list_head list; const char *file; }; int main(int argc, char **argv) { struct module *mod; - struct buffer buf = { }; char *missing_namespace_deps = NULL; char *dump_write = NULL, *files_source = NULL; int opt; int n; - struct dump_list *dump_read_start = NULL; - struct dump_list **dump_read_iter = &dump_read_start; + LIST_HEAD(dump_lists); + struct dump_list *dl, *dl2; while ((opt = getopt(argc, argv, "ei:mnT:o:awENd:")) != -1) { switch (opt) { case 'e': - external_module = 1; + external_module = true; break; case 'i': - *dump_read_iter = - NOFAIL(calloc(1, sizeof(**dump_read_iter))); - (*dump_read_iter)->file = optarg; - dump_read_iter = &(*dump_read_iter)->next; + dl = NOFAIL(malloc(sizeof(*dl))); + dl->file = optarg; + list_add_tail(&dl->list, &dump_lists); break; case 'm': - modversions = 1; + modversions = true; break; case 'n': - ignore_missing_files = 1; + ignore_missing_files = true; break; case 'o': dump_write = optarg; break; case 'a': - all_versions = 1; + all_versions = true; break; case 'T': files_source = optarg; break; case 'w': - warn_unresolved = 1; + warn_unresolved = true; break; case 'E': sec_mismatch_warn_only = false; break; case 'N': - allow_missing_ns_imports = 1; + allow_missing_ns_imports = true; break; case 'd': missing_namespace_deps = optarg; @@ -2568,13 +2630,10 @@ int main(int argc, char **argv) } } - while (dump_read_start) { - struct dump_list *tmp; - - read_dump(dump_read_start->file); - tmp = dump_read_start->next; - free(dump_read_start); - dump_read_start = tmp; + list_for_each_entry_safe(dl, dl2, &dump_lists, list) { + read_dump(dl->file); + list_del(&dl->list); + free(dl); } while (optind < argc) @@ -2583,28 +2642,14 @@ int main(int argc, char **argv) if (files_source) read_symbols_from_files(files_source); - for (mod = modules; mod; mod = mod->next) { - char fname[PATH_MAX]; - - if (mod->is_vmlinux || mod->from_dump) + list_for_each_entry(mod, &modules, list) { + if (mod->from_dump) continue; - buf.pos = 0; - - check_modname_len(mod); - check_exports(mod); - - add_header(&buf, mod); - add_intree_flag(&buf, !external_module); - add_retpoline(&buf); - add_staging_flag(&buf, mod->name); - add_versions(&buf, mod); - add_depends(&buf, mod); - add_moddevtable(&buf, mod); - add_srcversion(&buf, mod); - - sprintf(fname, "%s.mod.c", mod->name); - write_if_changed(&buf, fname); + if (mod->is_vmlinux) + write_vmlinux_export_c_file(mod); + else + write_mod_c_file(mod); } if (missing_namespace_deps) @@ -2620,9 +2665,8 @@ int main(int argc, char **argv) for (s = symbolhash[n]; s; s = s->next) { if (s->is_static) - error("\"%s\" [%s] is a static %s\n", - s->name, s->module->name, - export_str(s->export)); + error("\"%s\" [%s] is a static EXPORT_SYMBOL\n", + s->name, s->module->name); } } @@ -2630,7 +2674,5 @@ int main(int argc, char **argv) warn("suppressed %u unresolved symbol warnings because there were too many)\n", nr_unresolved - MAX_UNRESOLVED_REPORTS); - free(buf.p); - return error_occurred ? 1 : 0; } diff --git a/scripts/mod/modpost.h b/scripts/mod/modpost.h index 0c47ff95c0e2..d9daeff07b83 100644 --- a/scripts/mod/modpost.h +++ b/scripts/mod/modpost.h @@ -1,4 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <stdarg.h> @@ -10,6 +11,7 @@ #include <unistd.h> #include <elf.h> +#include "list.h" #include "elfconfig.h" /* On BSD-alike OSes elf.h defines these according to host's word size */ @@ -109,26 +111,22 @@ buf_printf(struct buffer *buf, const char *fmt, ...); void buf_write(struct buffer *buf, const char *s, int len); -struct namespace_list { - struct namespace_list *next; - char namespace[]; -}; - struct module { - struct module *next; - int gpl_compatible; - struct symbol *unres; - int from_dump; /* 1 if module was loaded from *.symvers */ - int is_vmlinux; - int seen; - int has_init; - int has_cleanup; + struct list_head list; + struct list_head exported_symbols; + struct list_head unresolved_symbols; + bool is_gpl_compatible; + bool from_dump; /* true if module was loaded from *.symvers */ + bool is_vmlinux; + bool seen; + bool has_init; + bool has_cleanup; struct buffer dev_table_buf; char srcversion[25]; // Missing namespace dependencies - struct namespace_list *missing_namespaces; + struct list_head missing_namespaces; // Actual imported namespaces - struct namespace_list *imported_namespaces; + struct list_head imported_namespaces; char name[]; }; @@ -138,8 +136,6 @@ struct elf_info { Elf_Shdr *sechdrs; Elf_Sym *symtab_start; Elf_Sym *symtab_stop; - Elf_Section export_sec; - Elf_Section export_gpl_sec; char *strtab; char *modinfo; unsigned int modinfo_len; @@ -178,7 +174,6 @@ static inline unsigned int get_secindex(const struct elf_info *info, } /* file2alias.c */ -extern unsigned int cross_build; void handle_moddevtable(struct module *mod, struct elf_info *info, Elf_Sym *sym, const char *symname); void add_moddevtable(struct buffer *buf, struct module *mod); diff --git a/scripts/mod/sumversion.c b/scripts/mod/sumversion.c index 905c0ec291e1..6bf9caca0968 100644 --- a/scripts/mod/sumversion.c +++ b/scripts/mod/sumversion.c @@ -290,13 +290,11 @@ static int parse_file(const char *fname, struct md4_ctx *md) return 1; } /* Check whether the file is a static library or not */ -static int is_static_library(const char *objfile) +static bool is_static_library(const char *objfile) { int len = strlen(objfile); - if (objfile[len - 2] == '.' && objfile[len - 1] == 'a') - return 1; - else - return 0; + + return objfile[len - 2] == '.' && objfile[len - 1] == 'a'; } /* We have dir/file.o. Open dir/.file.o.cmd, look for source_ and deps_ line @@ -387,7 +385,7 @@ out_file: /* Calc and record src checksum. */ void get_src_version(const char *modname, char sum[], unsigned sumlen) { - char *buf, *pos, *firstline; + char *buf; struct md4_ctx md; char *fname; char filelist[PATH_MAX + 1]; @@ -397,15 +395,8 @@ void get_src_version(const char *modname, char sum[], unsigned sumlen) buf = read_text_file(filelist); - pos = buf; - firstline = get_line(&pos); - if (!firstline) { - warn("bad ending versions file for %s\n", modname); - goto free; - } - md4_init(&md); - while ((fname = strsep(&firstline, " "))) { + while ((fname = strsep(&buf, "\n"))) { if (!*fname) continue; if (!(is_static_library(fname)) && |