/* Postprocess module symbol versions
*
* Copyright 2003 Kai Germaschewski
* Copyright 2002-2004 Rusty Russell, IBM Corporation
*
* 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 ...
*/
#include <ctype.h>
#include "modpost.h"
/* Are we using CONFIG_MODVERSIONS? */
int modversions = 0;
/* Warn about undefined symbols? (do so if we have vmlinux) */
int have_vmlinux = 0;
/* Is CONFIG_MODULE_SRCVERSION_ALL set? */
static int all_versions = 0;
void
fatal(const char *fmt, ...)
{
va_list arglist;
fprintf(stderr, "FATAL: ");
va_start(arglist, fmt);
vfprintf(stderr, fmt, arglist);
va_end(arglist);
exit(1);
}
void
warn(const char *fmt, ...)
{
va_list arglist;
fprintf(stderr, "WARNING: ");
va_start(arglist, fmt);
vfprintf(stderr, fmt, arglist);
va_end(arglist);
}
void *do_nofail(void *ptr, const char *expr)
{
if (!ptr) {
fatal("modpost: Memory allocation failure: %s.\n", expr);
}
return ptr;
}
/* A list of all modules we processed */
static struct module *modules;
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;
}
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));
/* strip trailing .o */
if ((s = strrchr(p, '.')) != NULL)
if (strcmp(s, ".o") == 0)
*s = '\0';
/* add to list */
mod->name = p;
mod->next = modules;
modules = mod;
return mod;
}
/* 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;
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);
}
/* 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));
memset(s, 0, sizeof(*s));
strcpy(s->name, name);
s->weak = weak;
s->next = next;
return s;
}
/* For the hash of exported symbols */
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;
if (crc) {
new->crc = *crc;
new->crc_valid = 1;
}
}
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;
}
/* 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) {
new_symbol(name, module, crc);
return;
}
if (crc) {
s->crc = *crc;
s->crc_valid = 1;
}
}
void *
grab_file(const char *filename, unsigned long *si