cregit-Linux how code gets into the kernel

Release 4.15 scripts/mod/modpost.c

Directory: scripts/mod
/* Postprocess module symbol versions
 *
 * Copyright 2003       Kai Germaschewski
 * Copyright 2002-2004  Rusty Russell, IBM Corporation
 * Copyright 2006-2008  Sam Ravnborg
 * Based in part on module-init-tools/depmod.c,file2alias
 *
 * This software may be used and distributed according to the terms
 * of the GNU General Public License, incorporated herein by reference.
 *
 * Usage: modpost vmlinux module1.o module2.o ...
 */


#define _GNU_SOURCE
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <limits.h>
#include <stdbool.h>
#include <errno.h>
#include "modpost.h"
#include "../../include/generated/autoconf.h"
#include "../../include/linux/license.h"
#include "../../include/linux/export.h"

/* Are we using CONFIG_MODVERSIONS? */

static int modversions = 0;
/* Warn about undefined symbols? (do so if we have vmlinux) */

static int have_vmlinux = 0;
/* Is CONFIG_MODULE_SRCVERSION_ALL set? */

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 */

static int sec_mismatch_count = 0;

static int sec_mismatch_verbose = 1;

static int sec_mismatch_fatal = 0;
/* ignore missing files */

static int ignore_missing_files;


enum export {
	


export_plain,      export_unused,     export_gpl,
	


export_unused_gpl, export_gpl_future, export_unknown
};

/* In kernel, this size is defined in linux/module.h;
 * here we use Elf_Addr instead of long for covering cross-compile
 */


#define MODULE_NAME_LEN (64 - sizeof(Elf_Addr))


#define PRINTF __attribute__ ((format (printf, 1, 2)))


PRINTF void fatal(const char *fmt, ...) { va_list arglist; fprintf(stderr, "FATAL: "); va_start(arglist, fmt); vfprintf(stderr, fmt, arglist); va_end(arglist); exit(1); }

Contributors

PersonTokensPropCommitsCommitProp
Kai Germaschewski4795.92%133.33%
Sam Ravnborg12.04%133.33%
Andi Kleen12.04%133.33%
Total49100.00%3100.00%


PRINTF void warn(const char *fmt, ...) { va_list arglist; fprintf(stderr, "WARNING: "); va_start(arglist, fmt); vfprintf(stderr, fmt, arglist); va_end(arglist); }

Contributors

PersonTokensPropCommitsCommitProp
Kai Germaschewski4295.45%133.33%
Andi Kleen12.27%133.33%
Sam Ravnborg12.27%133.33%
Total44100.00%3100.00%


PRINTF void merror(const char *fmt, ...) { va_list arglist; fprintf(stderr, "ERROR: "); va_start(arglist, fmt); vfprintf(stderr, fmt, arglist); va_end(arglist); }

Contributors

PersonTokensPropCommitsCommitProp
Matthew Wilcox4397.73%150.00%
Andi Kleen12.27%150.00%
Total44100.00%2100.00%


static inline bool strends(const char *str, const char *postfix) { if (strlen(str) < strlen(postfix)) return false; return strcmp(str + strlen(str) - strlen(postfix), postfix) == 0; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell52100.00%1100.00%
Total52100.00%1100.00%


static int is_vmlinux(const char *modname) { const char *myname; myname = strrchr(modname, '/'); if (myname) myname++; else myname = modname; return (strcmp(myname, "vmlinux") == 0) || (strcmp(myname, "vmlinux.o") == 0); }

Contributors

PersonTokensPropCommitsCommitProp
Sam Ravnborg60100.00%3100.00%
Total60100.00%3100.00%


void *do_nofail(void *ptr, const char *expr) { if (!ptr) fatal("modpost: Memory allocation failure: %s.\n", expr); return ptr; }

Contributors

PersonTokensPropCommitsCommitProp
Kai Germaschewski2996.67%150.00%
Andreas Gruenbacher13.33%150.00%
Total30100.00%2100.00%

/* A list of all modules we processed */ static struct module *modules;
static struct module *find_module(char *modname) { struct module *mod; for (mod = modules; mod; mod = mod->next) if (strcmp(mod->name, modname) == 0) break; return mod; }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Morton4491.67%133.33%
Kai Germaschewski36.25%133.33%
Sam Ravnborg12.08%133.33%
Total48100.00%3100.00%


static struct module *new_module(const char *modname) { struct module *mod; char *p; mod = NOFAIL(malloc(sizeof(*mod))); memset(mod, 0, sizeof(*mod)); p = NOFAIL(strdup(modname)); /* strip trailing .o */ if (strends(p, ".o")) { p[strlen(p) - 2] = '\0'; mod->is_dot_o = 1; } /* add to list */ mod->name = p; mod->gpl_compatible = -1; mod->next = modules; modules = mod; return mod; }

Contributors

PersonTokensPropCommitsCommitProp
Kai Germaschewski6051.72%327.27%
Andrew Morton2824.14%436.36%
Rusty Russell1210.34%19.09%
Sam Ravnborg86.90%218.18%
Frank Rowand86.90%19.09%
Total116100.00%11100.00%

/* A hash of all exported symbols, * struct symbol is also used for lists of unresolved symbols */ #define SYMBOL_HASH_SIZE 1024 struct symbol { struct symbol *next; struct module *module; unsigned int crc; int crc_valid; unsigned int weak:1; unsigned int vmlinux:1; /* 1 if symbol is defined in vmlinux */ unsigned int kernel:1; /* 1 if symbol is from kernel * (only for external modules) **/ unsigned int preloaded:1; /* 1 if symbol from Module.symvers, or crc */ enum export export; /* Type of export */ char name[0]; }; static struct symbol *symbolhash[SYMBOL_HASH_SIZE]; /* This is based on the hash agorithm from gdbm, via tdb */
static inline unsigned int tdb_hash(const char *name) { unsigned value; /* Used to compute the hash value. */ unsigned i; /* Used to cycle through random values. */ /* Set the initial value from the key size. */ for (value = 0x238F13AF * strlen(name), i = 0; name[i]; i++) value = (value + (((unsigned char *)name)[i] << (i*5 % 24))); return (1103515243 * value + 12345); }

Contributors

PersonTokensPropCommitsCommitProp
Kai Germaschewski82100.00%1100.00%
Total82100.00%1100.00%

/** * 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) { 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; return s; }

Contributors

PersonTokensPropCommitsCommitProp
Kai Germaschewski6173.49%125.00%
Andrew Morton1113.25%125.00%
Petr Vandrovec1012.05%125.00%
Sam Ravnborg11.20%125.00%
Total83100.00%4100.00%

/* For the hash of exported symbols */
static struct symbol *new_symbol(const char *name, struct module *module, enum export export) { unsigned int hash; struct symbol *new; hash = tdb_hash(name) % SYMBOL_HASH_SIZE; new = symbolhash[hash] = alloc_symbol(name, 0, symbolhash[hash]); new->module = module; new->export = export; return new; }

Contributors

PersonTokensPropCommitsCommitProp
Kai Germaschewski3344.59%116.67%
Andrew Morton2331.08%116.67%
Ram Pai1013.51%116.67%
Sam Ravnborg68.11%233.33%
Petr Vandrovec22.70%116.67%
Total74100.00%6100.00%


static struct symbol *find_symbol(const char *name) { struct symbol *s; /* For our purposes, .foo matches foo. PPC64 needs this. */ if (name[0] == '.') name++; for (s = symbolhash[tdb_hash(name) % SYMBOL_HASH_SIZE]; s; s = s->next) { if (strcmp(s->name, name) == 0) return s; } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Kai Germaschewski7297.30%133.33%
Sam Ravnborg11.35%133.33%
Keith M. Wesolowski11.35%133.33%
Total74100.00%3100.00%

static const 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; }

Contributors

PersonTokensPropCommitsCommitProp
Ram Pai20100.00%1100.00%
Total20100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Ram Pai6288.57%150.00%
Sam Ravnborg811.43%150.00%
Total70100.00%2100.00%


static const char *sech_name(struct elf_info *elf, Elf_Shdr *sechdr) { return (void *)elf->hdr + elf->sechdrs[elf->secindex_strings].sh_offset + sechdr->sh_name; }

Contributors

PersonTokensPropCommitsCommitProp
Masahiro Yamada41100.00%1100.00%
Total41100.00%1100.00%


static const char *sec_name(struct elf_info *elf, int secindex) { return sech_name(elf, &elf->sechdrs[secindex]); }

Contributors

PersonTokensPropCommitsCommitProp
Masahiro Yamada1653.33%150.00%
Alessio Igor Bogani1446.67%150.00%
Total30100.00%2100.00%

#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_unused+")) return export_unused; else if (strstarts(secname, "___ksymtab_gpl+")) return export_gpl; else if (strstarts(secname, "___ksymtab_unused_gpl+")) return export_unused_gpl; else if (strstarts(secname, "___ksymtab_gpl_future+")) return export_gpl_future; else return export_unknown; }

Contributors

PersonTokensPropCommitsCommitProp
Alessio Igor Bogani96100.00%1100.00%
Total96100.00%1100.00%


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_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; }

Contributors

PersonTokensPropCommitsCommitProp
Ram Pai5367.09%133.33%
Sam Ravnborg2430.38%133.33%
Denys Vlasenko22.53%133.33%
Total79100.00%3100.00%

/** * 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) { struct symbol *s = find_symbol(name); if (!s) { 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 Module.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; }

Contributors

PersonTokensPropCommitsCommitProp
Sam Ravnborg7757.04%444.44%
Kai Germaschewski3525.93%111.11%
Ram Pai128.89%111.11%
Trent Piepho96.67%111.11%
Paul Bolle10.74%111.11%
Andrew Morton10.74%111.11%
Total135100.00%9100.00%


static void sym_update_crc(const char *name, struct module *mod, unsigned int crc, enum export export) { struct symbol *s = find_symbol(name); if (!s) { s = new_symbol(name, mod, export); /* Don't complain when we find it later. */ s->preloaded = 1; } s->crc = crc; s->crc_valid = 1; }

Contributors

PersonTokensPropCommitsCommitProp
Kai Germaschewski4157.75%125.00%
Sam Ravnborg1521.13%125.00%
Rusty Russell912.68%125.00%
Ram Pai68.45%125.00%
Total71100.00%4100.00%


void *grab_file(const char *filename, unsigned long *size) { struct stat st; void *map = MAP_FAILED; int fd; fd = open(filename, O_RDONLY); if (fd < 0) return NULL; if (fstat(fd, &st)) goto failed; *size = st.st_size; map = mmap(NULL, *size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); failed: close(fd); if (map == MAP_FAILED) return NULL; return map; }

Contributors

PersonTokensPropCommitsCommitProp
Kai Germaschewski7873.58%233.33%
Andrew Morton1514.15%350.00%
Jesper Juhl1312.26%116.67%
Total106100.00%6100.00%

/** * Return a copy of the next line in a mmap'ed file. * spaces in the beginning of the line is trimmed away. * Return a pointer to a static buffer. **/
char *get_next_line(unsigned long *pos, void *file, unsigned long size) { static char line[4096]; int skip = 1; size_t len = 0; signed char *p = (signed char *)file + *pos; char *s = line; for (; *pos < size ; (*pos)++) { if (skip && isspace(*p)) { p++; continue; } skip = 0; if (*p != '\n' && (*pos < size)) { len++; *s++ = *p++; if (len > 4095) break; /* Too long, stop */ } else { /* End of string */ *s = '\0'; return line; } } /* End of buffer */ return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Morton14298.61%133.33%
Tom Rini21.39%266.67%
Total144100.00%3100.00%


void release_file(void *file, unsigned long size) { munmap(file, size); }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Morton1575.00%133.33%
Kai Germaschewski420.00%133.33%
Sam Ravnborg15.00%133.33%
Total20100.00%3100.00%


static int parse_elf(struct elf_info *info, const char *filename) { unsigned int i; Elf_Ehdr *hdr; Elf_Shdr *sechdrs; Elf_Sym *sym; const char *secstrings; unsigned int symtab_idx = ~0U, symtab_shndx_idx = ~0U; hdr = grab_file(filename, &info->size); if (!hdr) { if (ignore_missing_files) { fprintf(stderr, "%s: %s (ignored)\n", filename, strerror(errno)); return 0; } perror(filename); exit(1); } info->hdr = hdr; 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_type = TO_NATIVE(hdr->e_type); hdr->e_machine = TO_NATIVE(hdr->e_machine); hdr->e_version = TO_NATIVE(hdr->e_version); hdr->e_entry = TO_NATIVE(hdr->e_entry); hdr->e_phoff = TO_NATIVE(hdr->e_phoff); hdr->e_shoff = TO_NATIVE(hdr->e_shoff); hdr->e_flags = TO_NATIVE(hdr->e_flags); hdr->e_ehsize = TO_NATIVE(hdr->e_ehsize); hdr->e_phentsize = TO_NATIVE(hdr->e_phentsize); hdr->e_phnum = TO_NATIVE(hdr->e_phnum); hdr->e_shentsize = TO_NATIVE(hdr->e_shentsize); hdr->e_shnum = TO_NATIVE(hdr->e_shnum); hdr->e_shstrndx = TO_NATIVE(hdr->e_shstrndx); 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=%lu in file '%s' is bigger than " "filesize=%lu\n", (unsigned long)hdr->e_shoff, filename, info->size); return 0; } if (hdr->e_shnum == SHN_UNDEF) { /* * There are more than 64k sections, * read count from .sh_size. */ info->num_sections = TO_NATIVE(sechdrs[0].sh_size); } else { info->num_sections = hdr->e_shnum; } if (hdr->e_shstrndx == SHN_XINDEX) { info->secindex_strings = TO_NATIVE(sechdrs[0].sh_link); } else { info->secindex_strings = hdr->e_shstrndx; } /* Fix endianness in section headers */ for (i = 0; i < info->num_sections; i++) { sechdrs[i].sh_name = TO_NATIVE(sechdrs[i].sh_name); sechdrs[i].sh_type = TO_NATIVE(sechdrs[i].sh_type); sechdrs[i].sh_flags = TO_NATIVE(sechdrs[i].sh_flags); sechdrs[i].sh_addr = TO_NATIVE(sechdrs[i].sh_addr); sechdrs[i].sh_offset = TO_NATIVE(sechdrs[i].sh_offset); sechdrs[i].sh_size = TO_NATIVE(sechdrs[i].sh_size); sechdrs[i].sh_link = TO_NATIVE(sechdrs[i].sh_link); sechdrs[i].sh_info = TO_NATIVE(sechdrs[i].sh_info); sechdrs[i].sh_addralign = TO_NATIVE(sechdrs[i].sh_addralign); sechdrs[i].sh_entsize = TO_NATIVE(sechdrs[i].sh_entsize); } /* Find symbol table. */ secstrings = (void *)hdr + sechdrs[info->secindex_strings].sh_offset; for (i = 1; i < info->num_sections; i++) { const char *secname; int nobits = sechdrs[i].sh_type == SHT_NOBITS; if (!nobits && sechdrs[i].sh_offset > info->size) { fatal("%s is truncated. sechdrs[i].sh_offset=%lu > " "sizeof(*hrd)=%zu\n", filename, (unsigned long)sechdrs[i].sh_offset, sizeof(*hdr)); return 0; } secname = secstrings + sechdrs[i].sh_name; if (strcmp(secname, ".modinfo") == 0) { if (nobits) 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_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