[klibc] Re: klibc-0.72 released

Rusty Russell rusty@rustcorp.com.au
Wed, 08 Jan 2003 22:53:26 +1100


In message <3E1A68F5.1000704@zytor.com> you write:
> This adds [f]getc() and fgets() for parsing config files.  Probably hard 
> to avoid.  Still trying to decide if I actually want to add system() or not.
> 
> 	-hpa

I was hoping to test this first, but here's the mini-modprobe.
Unfortunately I've had to travel away for a funeral, so all I can say
is that it compiles.

Add features to taste.

/* Minature, simplified, self-standing modprobe. */
#include <stdio.h>
#include <elf.h>
#include <dirent.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/unistd.h>

/* All modules are in this directory. */
#define MODDIR "/lib/modules"

/* All modules end in this extension, eg. /lib/modules/rusty.ko */
#define MODEXT ".ko"

/* All option files end in this extension, eg. /lib/modules/rusty.ko.options */
#define OPTEXT ".options"

struct symbol
{
	struct symbol *next;
	const char *owner;
	char name[0];
};

#define SYMBOL_HASH_SIZE 1024
static struct symbol *symbolhash[SYMBOL_HASH_SIZE];

#define error(fmt , ...) fprintf(stderr, fmt , ## __VA_ARGS__)
#define fatal(fmt , ...) \
	do { fprintf(stderr, fmt , ## __VA_ARGS__); exit(1); } while(0)

static int modprobe(const char *modname, unsigned int depth);

/* Read in the entire file. */
static void *read_in(const char *entname, unsigned long *len, int noexistok)
{
	char *ret;
	unsigned long done = 0;
	int r, fd;
	struct stat statbuf;
	char filename[strlen(MODDIR) + 1 + strlen(entname) + 1];

	sprintf(filename, "%s/%s", MODDIR, entname);
	fd = open(filename, O_RDONLY);
	if (fd < 0) {
		if (errno == ENOENT && noexistok) {
			*len = 0;
			return strdup("");
		}
		fatal("Failed to open %s: %s\n", filename, strerror(errno));
	}
	fstat(fd, &statbuf);
	*len = statbuf.st_size;
	ret = malloc(*len + 1);
	
	while (done < *len) {
		r = read(fd, ret + done, *len - done);
		if (r <= 0)
			fatal("Failed to read %s: %s\n", filename,
			      strerror(errno));
		done += r;
	}
	ret[*len] = '\0';
	return ret;
}

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

static void add_symbol(const char *name, const char *owner)
{
	unsigned int hash;
	struct symbol *new = malloc(sizeof *new + strlen(name) + 1);

	new->owner = owner;
	strcpy(new->name, name);
	hash = tdb_hash(name) % SYMBOL_HASH_SIZE;
	new->next = symbolhash[hash];
	symbolhash[hash] = new;
}

static struct symbol *get_symbol(const char *name)
{
	struct symbol *s;

	for (s = symbolhash[tdb_hash(name) % SYMBOL_HASH_SIZE]; s; s = s->next)
		if (strcmp(s->name, name) == 0)
			break;
	return s;
}

struct kernel_symbol32 {
	char value[4];
	char name[64 - 4];
};

struct kernel_symbol64 {
	char value[8];
	char name[64 - 8];
};

static void load_symbols32(Elf32_Ehdr *hdr, const char *module)
{
	unsigned int i, j;
	struct kernel_symbol32 *ksyms;
	Elf32_Shdr *sechdrs = (void *)hdr + hdr->e_shoff;
	char *secnames = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;

	for (i = 1; i < hdr->e_shnum; i++)
		if (strcmp(secnames+sechdrs[i].sh_name, "__ksymtab") == 0) {
			ksyms = (void *)hdr + sechdrs[i].sh_offset;
			for (j = 0; j < sechdrs[i].sh_size/sizeof(*ksyms); j++)
				add_symbol(ksyms[j].name, module);
		}
}

static void load_symbols64(Elf64_Ehdr *hdr, const char *module)
{
	unsigned int i, j;
	struct kernel_symbol64 *ksyms;
	Elf64_Shdr *sechdrs = (void *)hdr + hdr->e_shoff;
	char *secnames = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;

	for (i = 1; i < hdr->e_shnum; i++)
		if (strcmp(secnames+sechdrs[i].sh_name, "__ksymtab") == 0) {
			ksyms = (void *)hdr + sechdrs[i].sh_offset;
			for (j = 0; j < sechdrs[i].sh_size/sizeof(*ksyms); j++)
				add_symbol(ksyms[j].name, module);
		}
}

/* "\177ELF" <byte> where byte = 001 for 32-bit, 002 for 64 */
static int elf_type(void *hdr, const char *entname)
{
	if (memcmp(hdr, ELFMAG, SELFMAG) != 0) {
		error("Module %s is not ELF!\n", entname);
		return ELFCLASSNONE;
	}
	return (((char *)hdr)[EI_CLASS]);
}

static void load_symbols(const char *entname)
{
	void *file;
	unsigned long len;

	file = read_in(entname, &len, 0);
	if (elf_type(file, entname) == ELFCLASS32)
		load_symbols32(file, entname);
	else if (elf_type(file, entname) == ELFCLASS64)
		load_symbols64(file, entname);
	free(file);
}

static void get_needs32(Elf32_Ehdr *hdr, unsigned int depth)
{
	Elf32_Shdr *sechdrs = (void *)hdr + hdr->e_shoff;
	Elf32_Sym *symtab;
	char *strtab;
	unsigned int i, j;
	struct symbol *sym;

	for (i = 1; i < hdr->e_shnum; i++) {
		if (sechdrs[i].sh_type != SHT_SYMTAB)
			continue;
		symtab = (void *)hdr + sechdrs[i].sh_offset;
		strtab = (void *)hdr + sechdrs[sechdrs[i].sh_link].sh_offset;
		for (j = 0; j<sechdrs[i].sh_size/sizeof(*symtab); j++) {
			if (symtab[j].st_shndx != SHN_UNDEF)
				continue;
			if ((sym = get_symbol(strtab + symtab[i].st_name))) 
				modprobe(sym->owner, depth);
		}
	}
}

static void get_needs64(Elf64_Ehdr *hdr, unsigned int depth)
{
	Elf64_Shdr *sechdrs = (void *)hdr + hdr->e_shoff;
	Elf64_Sym *symtab;
	char *strtab;
	unsigned int i, j;
	struct symbol *sym;

	for (i = 1; i < hdr->e_shnum; i++) {
		if (sechdrs[i].sh_type != SHT_SYMTAB)
			continue;
		symtab = (void *)hdr + sechdrs[i].sh_offset;
		strtab = (void *)hdr + sechdrs[sechdrs[i].sh_link].sh_offset;
		for (j = 0; j<sechdrs[i].sh_size/sizeof(*symtab); j++) {
			if (symtab[j].st_shndx != SHN_UNDEF)
				continue;
			if ((sym = get_symbol(strtab + symtab[i].st_name))) 
				modprobe(sym->owner, depth);
		}
	}
}

static int modprobe(const char *modname, unsigned int depth)
{
	int ret;
	void *file;
	char *options, *ptr;
	unsigned long len, optlen;
	char optionname[strlen(modname) + 1 + sizeof(OPTEXT)];

	if (depth > 50)
		fatal("Modprobe loop: %s\n", modname);

	/* Grab options if any: convert \n to ' ' */
	sprintf(optionname, "%s%s", modname, OPTEXT);
	options = read_in(optionname, &optlen, 1);
	while ((ptr = strchr(options, '\n')) != NULL)
		*ptr = ' ';

	/* Grab dependencies */
	file = read_in(modname, &len, 0);
	if (elf_type(file, modname) == ELFCLASS32)
		get_needs32(file, depth+1);
	else if (elf_type(file, modname) == ELFCLASS64)
		get_needs64(file, depth+1);

	ret = init_module(file, len, options);
	if (ret < 0 && errno != EEXIST)
		fprintf(stderr, "Error loading module %s: %s\n",
			modname, strerror(errno));
	free(file);
	free(options);
	return ret;
}

int main(int argc, char *argv[])
{
	DIR *dir;
	struct dirent *dirent;
	char modname[strlen(argv[1]?:"") + sizeof(MODEXT)];

	if (argc != 1)
		fatal("Usage: %s modulename\n"
		      "  Where modules are kept in "MODDIR"\n"
		      "  And options have extension "OPTEXT"\n", argv[0]);

	if (!(dir = opendir(MODDIR)))
		fatal("Could not open directory %s\n", MODDIR);

	while ((dirent = readdir(dir)) != NULL) {
		int len = strlen(dirent->d_name);
		if (len >= strlen(MODEXT)
		    && strcmp(dirent->d_name+len-strlen(MODEXT), MODEXT) == 0)
			load_symbols(dirent->d_name);
	}

	sprintf(modname, "%s%s", argv[1], MODEXT);
	return modprobe(modname, 0);
}



--
  Anyone who quotes me in their sig is an idiot. -- Rusty Russell.