[klibc] Re: klibc-0.72 released

Arnd Bergmann arnd@bergmann-dalldorf.de
Sat, 11 Jan 2003 13:12:38 +0100


On Wednesday 08 January 2003 12:53, Rusty Russell wrote:
> I was hoping to test this first, but here's the mini-modprobe.
Ok, I just gave it a try, here's what I found so far. 
I have attached a patch that adds this modprobe to klibc-0.72,
with the problems I found fixed. It now works on i386 and
at least compiles for s390 and s390x.

> #include <sys/unistd.h>
klibc only has <unistd.h>, not <sys/unistd.h>

> 		}
> 		fatal("Failed to open %s: %s\n", filename, strerror(errno));
> 	}
strerror() is disabled in klibc, you can only print errno here.

> 	ret = init_module(file, len, options);
The new system calls still have to be added to klibc/SYSCALLS

>                        if (symtab[j].st_shndx != SHN_UNDEF)
>                                continue;
>                        if ((sym = get_symbol(strtab + symtab[i].st_name))) 
>                                modprobe(sym->owner, depth);
                                                              ^^^^
that should read symtab[j]

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

	Arnd <><

--- klibc-0.72/utils/modprobe.c	1970-01-01 01:00:00.000000000 +0100
+++ klibc-0.72-modprobe/utils/modprobe.c	2003-01-10 19:48:47.000000000 +0100
@@ -0,0 +1,268 @@
+/* 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 <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/module.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: %d\n", filename, 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: %d\n", filename, 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[j].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[j].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: %d\n",
+                        modname, 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 != 2)
+                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) {
+                unsigned 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);
+}
diff -ur klibc-0.72/klibc/SYSCALLS klibc-0.72-modprobe/klibc/SYSCALLS
--- klibc-0.72/klibc/SYSCALLS	2002-09-11 07:00:58.000000000 +0200
+++ klibc-0.72-modprobe/klibc/SYSCALLS	2003-01-10 19:34:17.000000000 +0100
@@ -138,9 +138,7 @@
 int uname(struct utsname *)
 int setdomainname(const char *, size_t)
 int sethostname(const char *, size_t)
-int init_module(const char *, struct module *)
-void * create_module(const char *, size_t)
-int delete_module(const char *)
-int query_module(const char *, int, void *, size_t, size_t)
+int init_module(void *, unsigned long, char *)
+long delete_module(const char *, unsigned int)
 int reboot::__reboot(int, int, int, void *)
 int syslog::klogctl(int, char *, int)
diff -ur klibc-0.72/klibc/include/sys/module.h klibc-0.72-modprobe/klibc/include/sys/module.h
--- klibc-0.72/klibc/include/sys/module.h	2002-08-06 06:28:41.000000000 +0200
+++ klibc-0.72-modprobe/klibc/include/sys/module.h	2003-01-10 20:03:06.000000000 +0100
@@ -1,158 +1,11 @@
 /*
  * sys/module.h
- *
- * This is a bastardized version of linux/module.h, since the latter
- * doesn't have __KERNEL__ guards where it needs them...
  */
 
 #ifndef _SYS_MODULE_H
 #define _SYS_MODULE_H
 
-/*
- * Dynamic loading of modules into the kernel.
- *
- * Rewritten by Richard Henderson <rth@tamu.edu> Dec 1996
- */
-
-#include <asm/atomic.h>
-
-/* Don't need to bring in all of uaccess.h just for this decl.  */
-struct exception_table_entry;
-
-/* Used by get_kernel_syms, which is obsolete.  */
-struct kernel_sym
-{
-	unsigned long value;
-	char name[60];		/* should have been 64-sizeof(long); oh well */
-};
-
-struct module_symbol
-{
-	unsigned long value;
-	const char *name;
-};
-
-struct module_ref
-{
-	struct module *dep;	/* "parent" pointer */
-	struct module *ref;	/* "child" pointer */
-	struct module_ref *next_ref;
-};
-
-/* TBD */
-struct module_persist;
-
-struct module
-{
-	unsigned long size_of_struct;	/* == sizeof(module) */
-	struct module *next;
-	const char *name;
-	unsigned long size;
-
-	union
-	{
-		atomic_t usecount;
-		long pad;
-	} uc;				/* Needs to keep its size - so says rth */
-
-	unsigned long flags;		/* AUTOCLEAN et al */
-
-	unsigned nsyms;
-	unsigned ndeps;
-
-	struct module_symbol *syms;
-	struct module_ref *deps;
-	struct module_ref *refs;
-	int (*init)(void);
-	void (*cleanup)(void);
-	const struct exception_table_entry *ex_table_start;
-	const struct exception_table_entry *ex_table_end;
-#ifdef __alpha__
-	unsigned long gp;
-#endif
-	/* Members past this point are extensions to the basic
-	   module support and are optional.  Use mod_member_present()
-	   to examine them.  */
-	const struct module_persist *persist_start;
-	const struct module_persist *persist_end;
-	int (*can_unload)(void);
-	int runsize;			/* In modutils, not currently used */
-	const char *kallsyms_start;	/* All symbols for kernel debugging */
-	const char *kallsyms_end;
-	const char *archdata_start;	/* arch specific data for module */
-	const char *archdata_end;
-	const char *kernel_data;	/* Reserved for kernel internal use */
-};
-
-struct module_info
-{
-	unsigned long addr;
-	unsigned long size;
-	unsigned long flags;
-	long usecount;
-};
-
-/* Bits of module.flags.  */
-
-#define MOD_UNINITIALIZED	0
-#define MOD_RUNNING		1
-#define MOD_DELETED		2
-#define MOD_AUTOCLEAN		4
-#define MOD_VISITED  		8
-#define MOD_USED_ONCE		16
-#define MOD_JUST_FREED		32
-#define MOD_INITIALIZING	64
-
-/* Values for query_module's which.  */
-
-#define QM_MODULES	1
-#define QM_DEPS		2
-#define QM_REFS		3
-#define QM_SYMBOLS	4
-#define QM_INFO		5
-
-/* Can the module be queried? */
-#define MOD_CAN_QUERY(mod) (((mod)->flags & (MOD_RUNNING | MOD_INITIALIZING)) && !((mod)->flags & MOD_DELETED))
-
-/* When struct module is extended, we must test whether the new member
-   is present in the header received from insmod before we can use it.  
-   This function returns true if the member is present.  */
-
-#define mod_member_present(mod,member) 					\
-	((unsigned long)(&((struct module *)0L)->member + 1)		\
-	 <= (mod)->size_of_struct)
-
-/*
- * Ditto for archdata.  Assumes mod->archdata_start and mod->archdata_end
- * are validated elsewhere.
- */
-#define mod_archdata_member_present(mod, type, member)			\
-	(((unsigned long)(&((type *)0L)->member) +			\
-	  sizeof(((type *)0L)->member)) <=				\
-	 ((mod)->archdata_end - (mod)->archdata_start))
-	 
-
-/* Check if an address p with number of entries n is within the body of module m */
-#define mod_bound(p, n, m) ((unsigned long)(p) >= ((unsigned long)(m) + ((m)->size_of_struct)) && \
-	         (unsigned long)((p)+(n)) <= (unsigned long)(m) + (m)->size)
-
-/* Backwards compatibility definition.  */
-
-#define GET_USE_COUNT(module)	(atomic_read(&(module)->uc.usecount))
-
-/* Poke the use count of a module.  */
-
-#define __MOD_INC_USE_COUNT(mod)					\
-	(atomic_inc(&(mod)->uc.usecount), (mod)->flags |= MOD_VISITED|MOD_USED_ONCE)
-#define __MOD_DEC_USE_COUNT(mod)					\
-	(atomic_dec(&(mod)->uc.usecount), (mod)->flags |= MOD_VISITED)
-#define __MOD_IN_USE(mod)						\
-	(mod_member_present((mod), can_unload) && (mod)->can_unload	\
-	 ? (mod)->can_unload() : atomic_read(&(mod)->uc.usecount))
-
-/* Indirect stringification.  */
-
-#define __MODULE_STRING_1(x)	#x
-#define __MODULE_STRING(x)	__MODULE_STRING_1(x)
+extern int init_module(void *, unsigned long, char *);
+extern long delete_module(const char *, unsigned int);
 
 #endif /* _SYS_MODULE_H */
diff -ur klibc-0.72/utils/Makefile klibc-0.72-modprobe/utils/Makefile
--- klibc-0.72/utils/Makefile	2002-08-23 23:06:05.000000000 +0200
+++ klibc-0.72-modprobe/utils/Makefile	2003-01-10 19:44:15.000000000 +0100
@@ -4,7 +4,7 @@
 MAKEDEPS = -Wp,-MD,.$(subst /,-,$*).d
 CFLAGS   = $(MAKEDEPS) $(OPTFLAGS) $(REQFLAGS) -W -Wall
 LIBS     = $(KLIBC) $(LIBGCC)
-PROGS    = chroot dd fstype mkdir mkfifo mount pivot_root umount
+PROGS    = chroot dd fstype mkdir mkfifo mount pivot_root umount modprobe
 
 all:	$(PROGS)
 
@@ -23,6 +23,9 @@
 mkfifo:	mkfifo.o file_mode.o $(CRT0) $(LIBS)
 	$(LD) $(LDFLAGS) -o $@ $(CRT0) mkfifo.o file_mode.o $(LIBS)
 
+modprobe: modprobe.o $(CRT0) $(LIBS)
+	$(LD) $(LDFLAGS) -o $@ $(CRT0) modprobe.o $(LIBS)
+	
 mount:	mount.o mount_opts.o $(CRT0) $(LIBS)
 	$(LD) $(LDFLAGS) -o $@ $(CRT0) mount.o mount_opts.o $(LIBS)