*
* 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
#include <ctype.h>
#include "modpost.h"
-#include "../../include/linux/license.h"
/* Are we using CONFIG_MODVERSIONS? */
int modversions = 0;
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;
-/* 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, ...)
+void
+fatal(const char *fmt, ...)
{
va_list arglist;
exit(1);
}
-void warn(const char *fmt, ...)
+void
+warn(const char *fmt, ...)
{
va_list arglist;
va_end(arglist);
}
-static int is_vmlinux(const char *modname)
-{
- const char *myname;
-
- if ((myname = strrchr(modname, '/')))
- myname++;
- else
- myname = modname;
-
- return strcmp(myname, "vmlinux") == 0;
-}
-
void *do_nofail(void *ptr, const char *expr)
{
if (!ptr) {
static struct module *modules;
-static struct module *find_module(char *modname)
+struct module *
+find_module(char *modname)
{
struct module *mod;
return mod;
}
-static struct module *new_module(char *modname)
+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));
/* add to list */
mod->name = p;
- mod->gpl_compatible = -1;
mod->next = modules;
modules = mod;
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 */
- enum export export; /* Type of export */
char name[0];
};
return (1103515243 * value + 12345);
}
-/**
- * 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)
+/* Allocate a new symbols for use in the hash of exported symbols or
+ * the list of unresolved symbols per module */
+
+struct symbol *
+alloc_symbol(const char *name, unsigned int weak, struct symbol *next)
{
struct symbol *s = NOFAIL(malloc(sizeof(*s) + strlen(name) + 1));
}
/* For the hash of exported symbols */
-static struct symbol *new_symbol(const char *name, struct module *module,
- enum export export)
+
+void
+new_symbol(const char *name, struct module *module, unsigned int *crc)
{
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;
+ if (crc) {
+ new->crc = *crc;
+ new->crc_valid = 1;
+ }
}
-static struct symbol *find_symbol(const char *name)
+struct symbol *
+find_symbol(const char *name)
{
struct symbol *s;
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,
- enum export export)
+/* Add an exported symbol - it may have already been added without a
+ * CRC, in this case just update the CRC */
+void
+add_exported_symbol(const char *name, struct module *module, unsigned int *crc)
{
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");
- }
+ new_symbol(name, module, crc);
+ return;
+ }
+ if (crc) {
+ s->crc = *crc;
+ s->crc_valid = 1;
}
- 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, enum export export)
-{
- struct symbol *s = find_symbol(name);
-
- if (!s)
- s = new_symbol(name, mod, export);
- s->crc = crc;
- s->crc_valid = 1;
}
-void *grab_file(const char *filename, unsigned long *size)
+void *
+grab_file(const char *filename, unsigned long *size)
{
struct stat st;
void *map;
return map;
}
-/**
- * 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)
+/*
+ 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;
return NULL;
}
-void release_file(void *file, unsigned long size)
+void
+release_file(void *file, unsigned long size)
{
munmap(file, size);
}
-static void parse_elf(struct elf_info *info, const char *filename)
+void
+parse_elf(struct elf_info *info, const char *filename)
{
unsigned int i;
Elf_Ehdr *hdr = info->hdr;
hdr = grab_file(filename, &info->size);
if (!hdr) {
perror(filename);
- exit(1);
+ abort();
}
info->hdr = hdr;
if (info->size < sizeof(*hdr))
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;
- secname = secstrings + sechdrs[i].sh_name;
- if (strcmp(secname, ".modinfo") == 0) {
+ if (strcmp(secstrings+sechdrs[i].sh_name, ".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) {
- fatal("%s has no symtab?\n", filename);
+ fprintf(stderr, "modpost: %s no symtab?\n", filename);
+ abort();
}
/* Fix endianness in symbols */
for (sym = info->symtab_start; sym < info->symtab_stop; sym++) {
return;
truncated:
- fatal("%s is truncated.\n", filename);
+ fprintf(stderr, "modpost: %s is truncated.\n", filename);
+ abort();
}
-static void parse_elf_finish(struct elf_info *info)
+void
+parse_elf_finish(struct elf_info *info)
{
release_file(info->hdr, info->size);
}
-#define CRC_PFX MODULE_SYMBOL_PREFIX "__crc_"
-#define KSYMTAB_PFX MODULE_SYMBOL_PREFIX "__ksymtab_"
+#define CRC_PFX "__crc_"
+#define KSYMTAB_PFX "__ksymtab_"
-static void handle_modversions(struct module *mod, struct elf_info *info,
- Elf_Sym *sym, const char *symname)
+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:
- warn("\"%s\" [%s] is COMMON symbol\n", symname, mod->name);
+ fprintf(stderr, "*** Warning: \"%s\" [%s] is COMMON symbol\n",
+ symname, mod->name);
break;
case SHN_ABS:
/* 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,
- export);
+ add_exported_symbol(symname + strlen(CRC_PFX),
+ mod, &crc);
}
break;
case SHN_UNDEF:
/* 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 +
default:
/* All exported symbols */
if (memcmp(symname, KSYMTAB_PFX, strlen(KSYMTAB_PFX)) == 0) {
- sym_add_exported(symname + strlen(KSYMTAB_PFX), mod,
- export);
+ add_exported_symbol(symname + strlen(KSYMTAB_PFX),
+ mod, NULL);
}
if (strcmp(symname, MODULE_SYMBOL_PREFIX "init_module") == 0)
mod->has_init = 1;
}
}
-/**
- * Parse tag=value strings from .modinfo section
- **/
+int
+is_vmlinux(const char *modname)
+{
+ const char *myname;
+
+ if ((myname = strrchr(modname, '/')))
+ myname++;
+ else
+ myname = modname;
+
+ return strcmp(myname, "vmlinux") == 0;
+}
+
+/* Parse tag=value strings from .modinfo section */
static char *next_string(char *string, unsigned long *secsize)
{
/* Skip non-zero chars */
return string;
}
-static char *get_next_modinfo(void *modinfo, unsigned long modinfo_len,
- const char *tag, char *info)
+static char *get_modinfo(void *modinfo, unsigned long modinfo_len,
+ const char *tag)
{
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;
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 *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",
- 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;
-
- return f1 && f2;
-}
-
-/**
- * Find symbol based on relocation record info.
- * In some cases the symbol supplied is a valid symbol so
- * return refsym. If st_name != 0 we assume this is a valid symbol.
- * In other cases the symbol needs to be looked up in the symbol table
- * based on section and address.
- * **/
-static Elf_Sym *find_elf_symbol(struct elf_info *elf, Elf_Addr addr,
- Elf_Sym *relsym)
-{
- Elf_Sym *sym;
-
- if (relsym->st_name != 0)
- return relsym;
- for (sym = elf->symtab_start; sym < elf->symtab_stop; sym++) {
- if (sym->st_shndx != relsym->st_shndx)
- continue;
- if (sym->st_value == addr)
- return sym;
- }
- return NULL;
-}
-
-/*
- * 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,
- Elf_Sym **before, Elf_Sym **after)
-{
- Elf_Sym *sym;
- Elf_Ehdr *hdr = elf->hdr;
- Elf_Addr beforediff = ~0;
- Elf_Addr afterdiff = ~0;
- const char *secstrings = (void *)hdr +
- elf->sechdrs[hdr->e_shstrndx].sh_offset;
-
- *before = NULL;
- *after = NULL;
-
- for (sym = elf->symtab_start; sym < elf->symtab_stop; sym++) {
- const char *symsec;
-
- if (sym->st_shndx >= SHN_LORESERVE)
- continue;
- symsec = secstrings + elf->sechdrs[sym->st_shndx].sh_name;
- if (strcmp(symsec, sec) != 0)
- continue;
- if (sym->st_value <= addr) {
- if ((addr - sym->st_value) < beforediff) {
- beforediff = addr - sym->st_value;
- *before = sym;
- }
- else if ((addr - sym->st_value) == beforediff) {
- /* equal offset, valid name? */
- const char *name = elf->strtab + sym->st_name;
- if (name && strlen(name))
- *before = sym;
- }
- }
- else
- {
- if ((sym->st_value - addr) < afterdiff) {
- 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;
- }
- }
- }
-}
-
-/**
- * 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)
-{
- const char *refsymname = "";
- Elf_Sym *before, *after;
- Elf_Sym *refsym;
- Elf_Ehdr *hdr = elf->hdr;
- Elf_Shdr *sechdrs = elf->sechdrs;
- 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(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",
- modname, secname, refsymname, fromsec,
- elf->strtab + before->st_name,
- (long long)r.r_offset,
- elf->strtab + after->st_name);
- } else if (before) {
- warn("%s - Section mismatch: reference to %s:%s from %s "
- "after '%s' (at offset 0x%llx)\n",
- modname, secname, refsymname, fromsec,
- elf->strtab + before->st_name,
- (long long)r.r_offset);
- } 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 + after->st_name,
- (long long)r.r_offset);
- } else {
- warn("%s - Section mismatch: reference to %s:%s from %s "
- "(offset 0x%llx)\n",
- modname, secname, fromsec, refsymname,
- (long long)r.r_offset);
- }
-}
-
-/**
- * A module includes a number of sections that are discarded
- * either when loaded or when used as built-in.
- * For loaded modules all functions marked __init and all data
- * marked __initdata will be discarded when the module has been intialized.
- * Likewise for modules used built-in the sections marked __exit
- * are discarded because __exit marked function are supposed to be called
- * only when a moduel is unloaded which never happes for built-in modules.
- * The check_sec_ref() function traverses all relocation records
- * to find all references to a section that reference a section that will
- * be discarded and warns about it.
- **/
-static void check_sec_ref(struct module *mod, const char *modname,
- struct elf_info *elf,
- int section(const char*),
- int section_ref_ok(const char *))
-{
- int i;
- Elf_Sym *sym;
- Elf_Ehdr *hdr = elf->hdr;
- 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++) {
- 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 (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++) {
- 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;
-
- 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);
- }
- }
- }
-}
-
-/**
- * Functions used only during module init is marked __init and is stored in
- * a .init.text section. Likewise data is marked __initdata and stored in
- * a .init.data section.
- * If this section is one of these sections return 1
- * See include/linux/init.h for the details
- **/
-static int init_section(const char *name)
-{
- if (strcmp(name, ".init") == 0)
- return 1;
- if (strncmp(name, ".init.", strlen(".init.")) == 0)
- return 1;
- return 0;
-}
-
-/**
- * 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,
- *
- * where vgacon_startup is __init. If you want to wade through the false
- * positives, take out the check for rodata.
- **/
-static int init_section_ref_ok(const char *name)
-{
- const char **s;
- /* Absolute section names */
- const char *namelist1[] = {
- ".init",
- ".opd", /* see comment [OPD] at exit_section_ref_ok() */
- ".toc1", /* used by ppc64 */
- ".stab",
- ".rodata",
- ".text.lock",
- "__bug_table", /* used by powerpc for BUG() */
- ".pci_fixup_header",
- ".pci_fixup_final",
- ".pdr",
- "__param",
- "__ex_table",
- ".fixup",
- ".smp_locks",
- ".plt", /* seen on ARCH=um build on x86_64. Harmless */
- NULL
- };
- /* Start of section names */
- const char *namelist2[] = {
- ".init.",
- ".altinstructions",
- ".eh_frame",
- ".debug",
- NULL
- };
- /* part of section name */
- const char *namelist3 [] = {
- ".unwind", /* sample: IA_64.unwind.init.text */
- NULL
- };
-
- for (s = namelist1; *s; s++)
- if (strcmp(*s, name) == 0)
- return 1;
- for (s = namelist2; *s; s++)
- if (strncmp(*s, name, strlen(*s)) == 0)
- return 1;
- for (s = namelist3; *s; s++)
- if (strstr(name, *s) != NULL)
- return 1;
- if (strrcmp(name, ".init") == 0)
- return 1;
- return 0;
-}
-
-/*
- * Functions used only during module exit is marked __exit and is stored in
- * a .exit.text section. Likewise data is marked __exitdata and stored in
- * a .exit.data section.
- * If this section is one of these sections return 1
- * See include/linux/init.h for the details
- **/
-static int exit_section(const char *name)
-{
- if (strcmp(name, ".exit.text") == 0)
- return 1;
- if (strcmp(name, ".exit.data") == 0)
- return 1;
- return 0;
-
-}
-
-/*
- * Identify sections from which references to a .exit section is OK.
- *
- * [OPD] Keith Ownes <kaos@sgi.com> 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)
-{
- const char **s;
- /* Absolute section names */
- const char *namelist1[] = {
- ".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",
- ".stab",
- "__ex_table",
- ".fixup",
- ".smp_locks",
- ".plt", /* seen on ARCH=um build on x86_64. Harmless */
- NULL
- };
- /* Start of section names */
- const char *namelist2[] = {
- ".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++)
- if (strncmp(*s, name, strlen(*s)) == 0)
- return 1;
- for (s = namelist3; *s; s++)
- if (strstr(name, *s) != NULL)
- return 1;
- return 0;
-}
-
-static void read_symbols(char *modname)
+void
+read_symbols(char *modname)
{
const char *symname;
char *version;
- char *license;
struct module *mod;
struct elf_info info = { };
Elf_Sym *sym;
/* When there's no vmlinux, don't print warnings about
* unresolved symbols (since there'll be too many ;) */
if (is_vmlinux(modname)) {
+ unsigned int fake_crc = 0;
have_vmlinux = 1;
+ add_exported_symbol("struct_module", mod, &fake_crc);
mod->skip = 1;
}
- license = get_modinfo(info.modinfo, info.modinfo_len, "license");
- while (license) {
- if (license_is_gpl_compatible(license))
- mod->gpl_compatible = 1;
- else {
- mod->gpl_compatible = 0;
- break;
- }
- license = get_next_modinfo(info.modinfo, info.modinfo_len,
- "license", license);
- }
-
for (sym = info.symtab_start; sym < info.symtab_stop; sym++) {
symname = info.strtab + sym->st_name;
handle_modversions(mod, &info, sym, symname);
handle_moddevtable(mod, &info, sym, symname);
}
- check_sec_ref(mod, modname, &info, init_section, init_section_ref_ok);
- check_sec_ref(mod, modname, &info, exit_section, exit_section_ref_ok);
version = get_modinfo(info.modinfo, info.modinfo_len, "version");
if (version)
* following helper, then compare to the file on disk and
* only update the later if anything changed */
-void __attribute__((format(printf, 2, 3))) buf_printf(struct buffer *buf,
- const char *fmt, ...)
+void __attribute__((format(printf, 2, 3)))
+buf_printf(struct buffer *buf, const char *fmt, ...)
{
char tmp[SZ];
int len;
va_list ap;
-
+
va_start(ap, fmt);
len = vsnprintf(tmp, SZ, fmt, ap);
buf_write(buf, tmp, len);
va_end(ap);
}
-void buf_write(struct buffer *buf, const char *s, int len)
+void
+buf_write(struct buffer *buf, const char *s, int len)
{
if (buf->size - buf->pos < len) {
buf->size += len + SZ;
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 */
-/**
- * Header for the generated file
- **/
-static void add_header(struct buffer *b, struct module *mod)
+void
+add_header(struct buffer *b, struct module *mod)
{
buf_printf(b, "#include <linux/module.h>\n");
buf_printf(b, "#include <linux/vermagic.h>\n");
buf_printf(b, "};\n");
}
-/**
- * Record CRCs for unresolved symbols
- **/
-static void add_versions(struct buffer *b, struct module *mod)
+/* Record CRCs for unresolved symbols */
+
+void
+add_versions(struct buffer *b, struct module *mod)
{
struct symbol *s, *exp;
exp = find_symbol(s->name);
if (!exp || exp->module == mod) {
if (have_vmlinux && !s->weak)
- warn("\"%s\" [%s.ko] undefined!\n",
- s->name, mod->name);
+ fprintf(stderr, "*** Warning: \"%s\" [%s.ko] "
+ "undefined!\n", s->name, mod->name);
continue;
}
s->module = exp->module;
continue;
}
if (!s->crc_valid) {
- warn("\"%s\" [%s.ko] has no CRC!\n",
+ fprintf(stderr, "*** Warning: \"%s\" [%s.ko] "
+ "has no CRC!\n",
s->name, mod->name);
continue;
}
buf_printf(b, "};\n");
}
-static void add_depends(struct buffer *b, struct module *mod,
- struct module *modules)
+void
+add_depends(struct buffer *b, struct module *mod, struct module *modules)
{
struct symbol *s;
struct module *m;
buf_printf(b, "\";\n");
}
-static void add_srcversion(struct buffer *b, struct module *mod)
+void
+add_srcversion(struct buffer *b, struct module *mod)
{
if (mod->srcversion[0]) {
buf_printf(b, "\n");
}
}
-static void write_if_changed(struct buffer *b, const char *fname)
+void
+write_if_changed(struct buffer *b, const char *fname)
{
char *tmp;
FILE *file;
fclose(file);
}
-/* parse Module.symvers file. line format:
- * 0x12345678<tab>symbol<tab>module[[<tab>export]<tab>something]
- **/
-static void read_dump(const char *fname, unsigned int kernel)
+void
+read_dump(const char *fname)
{
unsigned long size, pos = 0;
void *file = grab_file(fname, &size);
return;
while ((line = get_next_line(&pos, file, size))) {
- char *symname, *modname, *d, *export, *end;
+ char *symname, *modname, *d;
unsigned int crc;
struct module *mod;
- struct symbol *s;
if (!(symname = strchr(line, '\t')))
goto fail;
if (!(modname = strchr(symname, '\t')))
goto fail;
*modname++ = '\0';
- if ((export = strchr(modname, '\t')) != NULL)
- *export++ = '\0';
- if (export && ((end = strchr(export, '\t')) != NULL))
- *end = '\0';
+ if (strchr(modname, '\t'))
+ goto fail;
crc = strtoul(line, &d, 16);
if (*symname == '\0' || *modname == '\0' || *d != '\0')
goto fail;
mod = new_module(NOFAIL(strdup(modname)));
mod->skip = 1;
}
- s = sym_add_exported(symname, mod, export_no(export));
- s->kernel = kernel;
- s->preloaded = 1;
- sym_update_crc(symname, mod, crc, export_no(export));
+ add_exported_symbol(symname, mod, &crc);
}
return;
fail:
fatal("parse error in symbol dump file\n");
}
-/* For normal builds always dump all symbols.
- * For external modules only dump symbols
- * that are not read from kernel Module.symvers.
- **/
-static int dump_sym(struct symbol *sym)
-{
- if (!external_module)
- return 1;
- if (sym->vmlinux || sym->kernel)
- return 0;
- return 1;
-}
-
-static void write_dump(const char *fname)
+void
+write_dump(const char *fname)
{
struct buffer buf = { };
struct symbol *symbol;
for (n = 0; n < SYMBOL_HASH_SIZE ; n++) {
symbol = symbolhash[n];
while (symbol) {
- if (dump_sym(symbol))
- 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;
+ }
+ }
+
+ for (n = 0; n < SYMBOL_HASH_SIZE ; n++) {
+ symbol = symbolhash[n];
+ while (symbol) {
+ buf_printf(&buf, "0x%08x\t%s\t%s\n", symbol->crc,
+ symbol->name, symbol->module->name);
symbol = symbol->next;
}
}
write_if_changed(&buf, fname);
}
-int main(int argc, char **argv)
+int
+main(int argc, char **argv)
{
struct module *mod;
struct buffer buf = { };
char fname[SZ];
- char *kernel_read = NULL, *module_read = NULL;
- char *dump_write = NULL;
+ char *dump_read = NULL, *dump_write = NULL;
int opt;
- while ((opt = getopt(argc, argv, "i:I:mo:a")) != -1) {
+ while ((opt = getopt(argc, argv, "i:mo:a")) != -1) {
switch(opt) {
case 'i':
- kernel_read = optarg;
- break;
- case 'I':
- module_read = optarg;
- external_module = 1;
+ dump_read = optarg;
break;
case 'm':
modversions = 1;
}
}
- if (kernel_read)
- read_dump(kernel_read, 1);
- if (module_read)
- read_dump(module_read, 0);
+ if (dump_read)
+ read_dump(dump_read);
while (optind < argc) {
read_symbols(argv[optind++]);
}
- for (mod = modules; mod; mod = mod->next) {
- if (mod->skip)
- continue;
- check_exports(mod);
- }
-
for (mod = modules; mod; mod = mod->next) {
if (mod->skip)
continue;
return 0;
}
+