Release 4.10 scripts/mod/modpost.c
/* 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
};
#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
| Person | Tokens | Prop | Commits | CommitProp |
kai germaschewski | kai germaschewski | 47 | 95.92% | 1 | 33.33% |
sam ravnborg | sam ravnborg | 1 | 2.04% | 1 | 33.33% |
andi kleen | andi kleen | 1 | 2.04% | 1 | 33.33% |
| Total | 49 | 100.00% | 3 | 100.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
| Person | Tokens | Prop | Commits | CommitProp |
kai germaschewski | kai germaschewski | 42 | 95.45% | 1 | 33.33% |
sam ravnborg | sam ravnborg | 1 | 2.27% | 1 | 33.33% |
andi kleen | andi kleen | 1 | 2.27% | 1 | 33.33% |
| Total | 44 | 100.00% | 3 | 100.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
| Person | Tokens | Prop | Commits | CommitProp |
matthew wilcox | matthew wilcox | 43 | 97.73% | 1 | 50.00% |
andi kleen | andi kleen | 1 | 2.27% | 1 | 50.00% |
| Total | 44 | 100.00% | 2 | 100.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
| Person | Tokens | Prop | Commits | CommitProp |
rusty russell | rusty russell | 52 | 100.00% | 1 | 100.00% |
| Total | 52 | 100.00% | 1 | 100.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
| Person | Tokens | Prop | Commits | CommitProp |
sam ravnborg | sam ravnborg | 60 | 100.00% | 3 | 100.00% |
| Total | 60 | 100.00% | 3 | 100.00% |
void *do_nofail(void *ptr, const char *expr)
{
if (!ptr)
fatal("modpost: Memory allocation failure: %s.\n", expr);
return ptr;
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
kai germaschewski | kai germaschewski | 29 | 96.67% | 1 | 50.00% |
andreas gruenbacher | andreas gruenbacher | 1 | 3.33% | 1 | 50.00% |
| Total | 30 | 100.00% | 2 | 100.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
| Person | Tokens | Prop | Commits | CommitProp |
andrew morton | andrew morton | 44 | 91.67% | 1 | 33.33% |
kai germaschewski | kai germaschewski | 3 | 6.25% | 1 | 33.33% |
sam ravnborg | sam ravnborg | 1 | 2.08% | 1 | 33.33% |
| Total | 48 | 100.00% | 3 | 100.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
| Person | Tokens | Prop | Commits | CommitProp |
kai germaschewski | kai germaschewski | 60 | 51.72% | 3 | 27.27% |
andrew morton | andrew morton | 28 | 24.14% | 4 | 36.36% |
rusty russell | rusty russell | 12 | 10.34% | 1 | 9.09% |
frank rowand | frank rowand | 8 | 6.90% | 1 | 9.09% |
sam ravnborg | sam ravnborg | 8 | 6.90% | 2 | 18.18% |
| Total | 116 | 100.00% | 11 | 100.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
| Person | Tokens | Prop | Commits | CommitProp |
kai germaschewski | kai germaschewski | 82 | 100.00% | 1 | 100.00% |
| Total | 82 | 100.00% | 1 | 100.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
| Person | Tokens | Prop | Commits | CommitProp |
kai germaschewski | kai germaschewski | 61 | 73.49% | 1 | 25.00% |
andrew morton | andrew morton | 11 | 13.25% | 1 | 25.00% |
petr vandrovec* | petr vandrovec* | 10 | 12.05% | 1 | 25.00% |
sam ravnborg | sam ravnborg | 1 | 1.20% | 1 | 25.00% |
| Total | 83 | 100.00% | 4 | 100.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
| Person | Tokens | Prop | Commits | CommitProp |
kai germaschewski | kai germaschewski | 33 | 44.59% | 1 | 16.67% |
andrew morton | andrew morton | 23 | 31.08% | 1 | 16.67% |
ram pai | ram pai | 10 | 13.51% | 1 | 16.67% |
sam ravnborg | sam ravnborg | 6 | 8.11% | 2 | 33.33% |
petr vandrovec* | petr vandrovec* | 2 | 2.70% | 1 | 16.67% |
| Total | 74 | 100.00% | 6 | 100.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
| Person | Tokens | Prop | Commits | CommitProp |
kai germaschewski | kai germaschewski | 72 | 97.30% | 1 | 33.33% |
sam ravnborg | sam ravnborg | 1 | 1.35% | 1 | 33.33% |
keith m. wesolowski | keith m. wesolowski | 1 | 1.35% | 1 | 33.33% |
| Total | 74 | 100.00% | 3 | 100.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
| Person | Tokens | Prop | Commits | CommitProp |
ram pai | ram pai | 20 | 100.00% | 1 | 100.00% |
| Total | 20 | 100.00% | 1 | 100.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
| Person | Tokens | Prop | Commits | CommitProp |
ram pai | ram pai | 62 | 88.57% | 1 | 50.00% |
sam ravnborg | sam ravnborg | 8 | 11.43% | 1 | 50.00% |
| Total | 70 | 100.00% | 2 | 100.00% |
static const char *sec_name(struct elf_info *elf, 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_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
| Person | Tokens | Prop | Commits | CommitProp |
alessio igor bogani | alessio igor bogani | 96 | 100.00% | 1 | 100.00% |
| Total | 96 | 100.00% | 1 | 100.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
| Person | Tokens | Prop | Commits | CommitProp |
ram pai | ram pai | 53 | 67.09% | 1 | 33.33% |
sam ravnborg | sam ravnborg | 24 | 30.38% | 1 | 33.33% |
denys vlasenko | denys vlasenko | 2 | 2.53% | 1 | 33.33% |
| Total | 79 | 100.00% | 3 | 100.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
| Person | Tokens | Prop | Commits | CommitProp |
sam ravnborg | sam ravnborg | 77 | 57.04% | 4 | 44.44% |
kai germaschewski | kai germaschewski | 35 | 25.93% | 1 | 11.11% |
ram pai | ram pai | 12 | 8.89% | 1 | 11.11% |
trent piepho | trent piepho | 9 | 6.67% | 1 | 11.11% |
andrew morton | andrew morton | 1 | 0.74% | 1 | 11.11% |
paul bolle | paul bolle | 1 | 0.74% | 1 | 11.11% |
| Total | 135 | 100.00% | 9 | 100.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
| Person | Tokens | Prop | Commits | CommitProp |
kai germaschewski | kai germaschewski | 41 | 57.75% | 1 | 25.00% |
sam ravnborg | sam ravnborg | 15 | 21.13% | 1 | 25.00% |
rusty russell | rusty russell | 9 | 12.68% | 1 | 25.00% |
ram pai | ram pai | 6 | 8.45% | 1 | 25.00% |
| Total | 71 | 100.00% | 4 | 100.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
| Person | Tokens | Prop | Commits | CommitProp |
kai germaschewski | kai germaschewski | 78 | 73.58% | 2 | 33.33% |
andrew morton | andrew morton | 15 | 14.15% | 3 | 50.00% |
jesper juhl | jesper juhl | 13 | 12.26% | 1 | 16.67% |
| Total | 106 | 100.00% | 6 | 100.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
| Person | Tokens | Prop | Commits | CommitProp |
andrew morton | andrew morton | 142 | 98.61% | 1 | 33.33% |
tom rini | tom rini | 2 | 1.39% | 2 | 66.67% |
| Total | 144 | 100.00% | 3 | 100.00% |
void release_file(void *file, unsigned long size)
{
munmap(file, size);
}
Contributors
| Person | Tokens | Prop | Commits | CommitProp |
andrew morton | andrew morton | 15 | 75.00% | 1 | 33.33% |
kai germaschewski | kai germaschewski | 4 | 20.00% | 1 | 33.33% |
sam ravnborg | sam ravnborg | 1 | 5.00% | 1 | 33.33% |
| Total | 20 | 100.00% | 3 | 100.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;
if (sechdrs[i].sh_type == SHT_SYMTAB) {
unsigned int sh_link_idx;
symtab_idx = i;
info->symtab_start = (void *)hdr +
sechdrs[i].sh_offset;
info->symtab_stop = (void *)hdr +
sechdrs[i].sh_offset + sechdrs[i].sh_size;
sh_link_idx = sechdrs[i].sh_link;
info->strtab = (void *)hdr +
sechdrs[sh_link_idx].sh_offset;
}
/* 32bit section no. table? ("more than 64k sections") */
if (sechdrs[i].sh_type == SHT_SYMTAB_SHNDX) {
symtab_shndx_idx = i;
info->symtab_shndx_start = (void *)hdr +
sechdrs[i].sh_offset;
info->symtab_shndx_stop = (void *)hdr +
sechdrs[i].sh_offset + sechdrs[i].sh_size;
}
}
if (!info->symtab_start)
fatal("%s has no symtab?\n", filename);