diff options
Diffstat (limited to 'sbin/modload/modload.c')
-rw-r--r-- | sbin/modload/modload.c | 499 |
1 files changed, 241 insertions, 258 deletions
diff --git a/sbin/modload/modload.c b/sbin/modload/modload.c index ee634bebeba..ae3e07580bd 100644 --- a/sbin/modload/modload.c +++ b/sbin/modload/modload.c @@ -1,5 +1,5 @@ -/* $OpenBSD: modload.c,v 1.28 2002/01/07 19:38:28 ericj Exp $ */ -/* $NetBSD: modload.c,v 1.13 1995/05/28 05:21:58 jtc Exp $ */ +/* $OpenBSD: modload.c,v 1.29 2002/01/08 21:28:38 ericj Exp $ */ +/* $NetBSD: modload.c,v 1.30 2001/11/08 15:33:15 christos Exp $ */ /* * Copyright (c) 1993 Terrence R. Lambert. @@ -39,97 +39,85 @@ #include <sys/mount.h> #include <sys/lkm.h> #include <sys/stat.h> -#include <sys/wait.h> -#include <a.out.h> +#include <sys/file.h> #include <err.h> #include <errno.h> -#include <fcntl.h> -#include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <nlist.h> #include "pathnames.h" -#define min(a, b) ((a) < (b) ? (a) : (b)) +#define TRUE 1 +#define FALSE 0 -/* - * Expected linker options: - * - * -A executable to link against - * -e entry point - * -o output file - * -T address to link to in hex (assumes it's a page boundry) - * <target> object file - */ +#include "modload.h" + +#ifndef DFLT_ENTRY +#define DFLT_ENTRY "xxxinit" +#endif /* !DFLT_ENTRY */ +#ifndef DFLT_ENTRYEXT +#define DFLT_ENTRYEXT "_lkmentry" +#endif /* !DFLT_ENTRYEXT */ int debug = 0; int verbose = 0; -int symtab = 1; -int quiet = 0; -int dounlink = 0; - -#if defined(__alpha) || defined(__mips) -#define LDSYMTABLE "-R" -#define LDTEXTSTART "-Ttext" -#define LDSYMPREFIX "" -#else -#define LDSYMTABLE "-A" -#define LDTEXTSTART "-T" -#define LDSYMPREFIX "_" -#define MAGICCHECK -#endif - -void -linkcmd(kernel, entry, outfile, address, object) - char *kernel, *entry, *outfile; - u_int address; /* XXX */ - char *object; +char *out = NULL; +int symtab = 0; +int Sflag; + +static void cleanup __P((void)); + +/* prelink the module */ +static int +prelink(const char *kernel, + const char *entry, + const char *outfile, + const void *address, + const char *object) { - char addrbuf[32], entrybuf[_POSIX2_LINE_MAX]; - pid_t pid; - int status; + char cmdbuf[1024]; + int error = 0; - snprintf(entrybuf, sizeof entrybuf, "%s%s", LDSYMPREFIX, entry); - snprintf(addrbuf, sizeof addrbuf, "%x", address); + linkcmd(cmdbuf, sizeof(cmdbuf), + kernel, entry, outfile, address, object); if (debug) - printf("%s %s %s -e %s -o %s %s %s %s\n", - _PATH_LD, LDSYMTABLE, kernel, entrybuf, - outfile, LDTEXTSTART, addrbuf, object); - - if ((pid = fork()) < 0) - err(18, "fork"); - - if (pid == 0) { - execl(_PATH_LD, "ld", LDSYMTABLE, kernel, "-e", entrybuf, "-o", - outfile, LDTEXTSTART, addrbuf, object, (char *)NULL); - exit(128 + errno); - } - - waitpid(pid, &status, 0); - - if (WIFSIGNALED(status)) { - errx(1, "%s got signal: %s", _PATH_LD, - sys_siglist[WTERMSIG(status)]); - } + fprintf(stderr, "%s\n", cmdbuf); - if (WEXITSTATUS(status) > 128) { - errno = WEXITSTATUS(status) - 128; - err(1, "exec(%s)", _PATH_LD); + switch (system(cmdbuf)) { + case 0: /* SUCCESS! */ + break; + case 1: /* uninformitive error */ + /* + * Someone needs to fix the return values from the NetBSD + * ld program -- it's totally uninformative. + * + * No such file (4 on SunOS) + * Can't write output (2 on SunOS) + * Undefined symbol (1 on SunOS) + * etc. + */ + case 127: /* can't load shell */ + case 32512: + default: + error = 1; + break; } - if (WEXITSTATUS(status) != 0) - errx(1, "%s: return code %d", _PATH_LD, WEXITSTATUS(status)); + return error; } static void -usage() +usage(void) { - fprintf(stderr, "usage: modload [-dvsqu] [-A <kernel>] [-e <entry]\n"); + fprintf(stderr, "usage:\n"); + fprintf(stderr, "modload [-d] [-v] [-n] [-s] [-S] " + "[-A <kernel>] [-e <entry>]\n"); fprintf(stderr, - "\t[-p <postinstall>] [-o <output file>] <input file>\n"); + " [-p <postinstall>] [-o <output file>] <input file>\n"); exit(1); } @@ -137,59 +125,131 @@ int fileopen = 0; #define DEV_OPEN 0x01 #define MOD_OPEN 0x02 #define PART_RESRV 0x04 +#define OUTFILE_CREAT 0x08 + int devfd, modfd; struct lmc_resrv resrv; -char modout[MAXPATHLEN]; -/* - * Must be safe for two calls. One in case of the -p option, and one from - * the atexit() call... - */ -void -cleanup() +static void +cleanup(void) { + if (fileopen & PART_RESRV) { /* * Free up kernel memory */ if (ioctl(devfd, LMUNRESRV, 0) == -1) warn("can't release slot 0x%08x memory", resrv.slot); - fileopen &= ~PART_RESRV; } - if (fileopen & DEV_OPEN) { + if (fileopen & DEV_OPEN) close(devfd); - fileopen &= ~DEV_OPEN; - } - if (fileopen & MOD_OPEN) { + if (fileopen & MOD_OPEN) close(modfd); - fileopen &= ~MOD_OPEN; + + if (fileopen & OUTFILE_CREAT) + unlink(out); +} + +static int +verify_entry(const char *entry, char *filename) +{ + struct nlist names[2]; + int n; + char *s; + + memset(names, 0, sizeof(names)); + s = malloc(strlen(entry) + 2); + sprintf(s, "_%s", entry); /* safe */ +#ifdef _AOUT_INCLUDE_ + names[0].n_un.n_name = s; +#else + names[0].n_name = s; +#endif + + n = nlist(filename, names); + if (n == -1) + err(1, "nlist %s", filename); + return n; +} + +/* + * Transfer data to kernel memory in chunks + * of MODIOBUF size at a time. + */ +void +loadbuf(void *buf, size_t len) +{ + struct lmc_loadbuf ldbuf; + size_t n; + char *p = buf; + + while (len) { + n = MIN(len, MODIOBUF); + ldbuf.cnt = n; + ldbuf.data = p; + if (ioctl(devfd, LMLOADBUF, &ldbuf) == -1) + err(11, "error loading buffer"); + len -= n; + p += n; + } +} + +/* Transfer some empty space. */ +void +loadspace(size_t len) +{ + char buf[MODIOBUF]; + size_t n; + + memset(buf, 0, sizeof(buf)); + while (len) { + n = MIN(len, sizeof(buf)); + loadbuf(buf, n); + len -= n; } +} - if (dounlink && unlink(modout) != 0) { - err(17, "unlink(%s)", modout); - dounlink = 0; +/* + * Transfer symbol table to kernel memory in chunks + * of MODIOBUF size at a time. + */ +void +loadsym(void *buf, size_t len) +{ + struct lmc_loadbuf ldbuf; + size_t n; + char *p = buf; + + while (len) { + n = MIN(len, MODIOBUF); + ldbuf.cnt = n; + ldbuf.data = p; + if (ioctl(devfd, LMLOADSYMS, &ldbuf) == -1) + err(11, "error loading buffer"); + len -= n; + p += n; } } +/* Transfer some empty space. */ int -main(argc, argv) - int argc; - char *argv[]; +main(int argc, char **argv) { + int c; char *kname = _PATH_UNIX; - char *entry = NULL, *post = NULL, *out = NULL, *modobj, *p; - struct exec info_buf; + char *entry = DFLT_ENTRY; + char *post = NULL; + char *modobj; + char modout[80], *p; struct stat stb; - u_int modsize; /* XXX */ - u_long modentry; /* XXX */ - int strtablen, c; - struct lmc_loadbuf ldbuf; - int sz, bytesleft, old = 0; - char buf[MODIOBUF]; + int strtablen; + size_t modsize; /* XXX */ + void* modentry; /* XXX */ + int noready = 0, old = 0; - while ((c = getopt(argc, argv, "dvsuqA:e:p:o:")) != -1) { + while ((c = getopt(argc, argv, "dnvsA:Se:p:o:")) != -1) { switch (c) { case 'd': debug = 1; @@ -197,12 +257,6 @@ main(argc, argv) case 'v': verbose = 1; break; /* verbose */ - case 'u': - dounlink = 1; - break; /* unlink tmp file */ - case 'q': - quiet = 1; - break; /* be quiet */ case 'A': kname = optarg; break; /* kernel */ @@ -215,11 +269,21 @@ main(argc, argv) case 'o': out = optarg; break; /* output file */ + case 'n': + noready = 1; + break; case 's': - symtab = 0; + symtab = 1; break; - default: + case 'S': + Sflag = 1; + break; + case '?': usage(); + /* NOTREACHED */ + default: + printf("default!\n"); + break; } } argc -= optind; @@ -241,65 +305,64 @@ main(argc, argv) err(3, _PATH_LKM); fileopen |= DEV_OPEN; - p = strrchr(modobj, '.'); - if (!p || p[1] != 'o' || p[2] != '\0') + strncpy(modout, modobj, sizeof(modout) - 1); + modout[sizeof(modout) - 1] = '\0'; + + p = strrchr(modout, '.'); + if (!p || strcmp(p, ".o")) errx(2, "module object must end in .o"); - if (out == NULL) { - p = strrchr(modobj, '/'); - if (p) - p++; /* skip over '/' */ - else - p = modobj; - snprintf(modout, sizeof modout, "%s%s.XXXXXXXX.o", - _PATH_TMP, p); - if ((modfd = mkstemps(modout, strlen(".o"))) == -1) - err(1, "creating %s", modout); - close(modfd); + *p = '\0'; + if (out == NULL) out = modout; + + /* + * Verify that the entry point for the module exists. + */ + if (verify_entry(entry, modobj)) { /* - * reverse meaning of -u - if we've generated a /tmp - * file, remove it automatically... + * Try <modobj>_init if entry is DFLT_ENTRY. */ - dounlink = !dounlink; - } - - if (!entry) { /* calculate default entry point */ - entry = strrchr(modobj, '/'); - if (entry) - entry++; /* skip over '/' */ - else - entry = modobj; - entry = strdup(entry); /* so we can modify it */ - if (!entry) - errx(1, "Could not allocate memory"); - entry[strlen(entry) - 2] = '\0'; /* chop off .o */ + if (strcmp(entry, DFLT_ENTRY) == 0) { + if ((p = strrchr(modout, '/'))) + p++; + else + p = modout; + entry = malloc(strlen(p) + strlen(DFLT_ENTRYEXT) + 1); + sprintf(entry, "%s%s", p, DFLT_ENTRYEXT); /* safe */ + if (verify_entry(entry, modobj)) + errx(1, "entry point _%s not found in %s", + entry, modobj); + } else + errx(1, "entry point _%s not found in %s", entry, + modobj); } - /* * Prelink to get file size */ - linkcmd(kname, entry, out, 0, modobj); - - /* - * Pre-open the 0-linked module to get the size information - */ - if ((modfd = open(out, O_RDONLY)) == -1) + if (prelink(kname, entry, out, 0, modobj)) + errx(1, "can't prelink `%s' creating `%s'", modobj, out); + if (Sflag == 0) + fileopen |= OUTFILE_CREAT; + + /* + * Pre-open the 0-linked module to get the size information + */ + if ((modfd = open(out, O_RDONLY, 0)) == -1) err(4, "%s", out); fileopen |= MOD_OPEN; /* - * Get the load module post load size... do this by reading the - * header and doing page counts. + * stat for filesize to figure out string table size */ - if (read(modfd, &info_buf, sizeof(struct exec)) == -1) - err(3, "read `%s'", out); + if (fstat(modfd, &stb) == -1) + err(3, "fstat `%s'", out); /* - * stat for filesize to figure out string table size + * work out various sizes and fill in resrv bits */ - if (fstat(modfd, &stb) == -1) - err(3, "fstat `%s'", out); + if (mod_sizes(modfd, &modsize, &strtablen, &resrv, &stb) != 0) + err(1, "can't get module sizes"); /* * Close the dummy module -- we have our sizing information. @@ -307,19 +370,6 @@ main(argc, argv) close(modfd); fileopen &= ~MOD_OPEN; -#ifdef MAGICCHECK - /* - * Magic number... - */ - if (N_BADMAG(info_buf)) - errx(4, "not an a.out format file"); -#endif - - /* - * Calculate the size of the module - */ - modsize = info_buf.a_text + info_buf.a_data + info_buf.a_bss; - /* * Reserve the required amount of kernel memory -- this may fail * to be successful. @@ -328,116 +378,52 @@ main(argc, argv) resrv.name = modout; /* objname w/o ".o" */ resrv.slot = -1; /* returned */ resrv.addr = 0; /* returned */ - strtablen = stb.st_size - N_STROFF(info_buf); - if (symtab) { - /* - * XXX TODO: grovel through symbol table looking - * for just the symbol table stuff from the new module, - * and skip the stuff from the kernel. - */ - resrv.sym_size = info_buf.a_syms + strtablen; - resrv.sym_symsize = info_buf.a_syms; - } else - resrv.sym_size = resrv.sym_symsize = 0; + + if (verbose) + warnx("reserving %lu bytes of memory", (unsigned long)modsize); if (ioctl(devfd, LMRESERV, &resrv) == -1) { - if (symtab) - warn("not loading symbols: " - "kernel does not support symbol table loading"); + if (symtab) { + warn("not loading symbols: kernel does not support " + "symbol table loading"); + } doold: - symtab = 0; - if (ioctl(devfd, LMRESERV_O, &resrv) == -1) - err(9, "can't reserve memory"); - old = 1; + symtab = 0; + if (ioctl(devfd, LMRESERV_O, &resrv) == -1) + err(9, "can't reserve memory"); + old = TRUE; } fileopen |= PART_RESRV; /* * Relink at kernel load address */ - linkcmd(kname, entry, out, resrv.addr, modobj); + if (prelink(kname, entry, out, (void*)resrv.addr, modobj)) + errx(1, "can't link `%s' creating `%s' bound to %p", + modobj, out, (void*)resrv.addr); /* * Open the relinked module to load it... */ - if ((modfd = open(out, O_RDONLY)) == -1) + if ((modfd = open(out, O_RDONLY, 0)) == -1) err(4, "%s", out); fileopen |= MOD_OPEN; - /* - * Reread the header to get the actual entry point *after* the - * relink. - */ - if (read(modfd, &info_buf, sizeof(struct exec)) == -1) - err(3, "read `%s'", out); - - /* - * Get the entry point (for initialization) - */ - modentry = info_buf.a_entry; /* place to call */ - - /* - * Seek to the text offset to start loading... - */ - if (lseek(modfd, N_TXTOFF(info_buf), 0) == -1) - err(12, "lseek"); - - /* - * Transfer the relinked module to kernel memory in chunks of - * MODIOBUF size at a time. - */ - for (bytesleft = info_buf.a_text + info_buf.a_data; bytesleft > 0; - bytesleft -= sz) { - sz = min(bytesleft, MODIOBUF); - if (read(modfd, buf, sz) != sz) - err(14, "read"); - ldbuf.cnt = sz; - ldbuf.data = buf; - if (ioctl(devfd, LMLOADBUF, &ldbuf) == -1) - err(11, "error transferring buffer"); - } - - if (symtab) { - /* - * Seek to the symbol table to start loading it... - */ - if (lseek(modfd, N_SYMOFF(info_buf), SEEK_SET) == -1) - err(12, "lseek"); + modentry = mod_load(modfd); + if (debug) + (void)fprintf(stderr, "modentry = %p\n", modentry); - /* - * Read and load the symbol table entries. - */ - for (bytesleft = info_buf.a_syms; bytesleft > 0; - bytesleft -= sz) { - sz = min(bytesleft, MODIOBUF); - if (read(modfd, buf, sz) != sz) - err(14, "read"); - ldbuf.cnt = sz; - ldbuf.data = buf; - if (ioctl(devfd, LMLOADSYMS, &ldbuf) == -1) - err(11, "error transferring sym buffer"); - } - - /* - * Read the string table and load it. - */ - for (bytesleft = strtablen; bytesleft > 0; - bytesleft -= sz) { - sz = min(bytesleft, MODIOBUF); - if (read(modfd, buf, sz) != sz) - err(14, "read"); - ldbuf.cnt = sz; - ldbuf.data = buf; - if (ioctl(devfd, LMLOADSYMS, &ldbuf) == -1) - err(11, "error transferring stringtable buffer"); - } - } + if (symtab) + mod_symload(strtablen); /* * Save ourselves before disaster (potentitally) strikes... */ sync(); + if (noready) + return 0; + /* * Trigger the module as loaded by calling the entry procedure; * this will do all necessary table fixup to ensure that state @@ -450,8 +436,8 @@ main(argc, argv) close(modfd); /* * PART_RESRV is not true since the kernel cleans - * up after a failed LMREADY - */ + * up after a failed LMREADY. + */ fileopen &= ~(MOD_OPEN|PART_RESRV); /* try using oldstyle */ warn("module failed to load using new version; " @@ -465,8 +451,7 @@ main(argc, argv) * Success! */ fileopen &= ~PART_RESRV; /* loaded */ - if (!quiet) - printf("Module loaded as ID %d\n", resrv.slot); + printf("Module loaded as ID %d\n", resrv.slot); /* * Execute the post-install program, if specified. @@ -474,26 +459,24 @@ main(argc, argv) if (post) { struct lmc_stat sbuf; char name[MAXLKMNAME] = ""; - char id[16], type[16], offset[32]; + char id[16], type[16], offset[16]; sbuf.id = resrv.slot; sbuf.name = name; if (ioctl(devfd, LMSTAT, &sbuf) == -1) err(15, "error fetching module stats for post-install"); - sprintf(id, "%d", sbuf.id); - sprintf(type, "0x%x", sbuf.type); - sprintf(offset, "%lu", sbuf.offset); + (void)snprintf(id, sizeof(id), "%d", sbuf.id); + (void)snprintf(type, sizeof(type), "0x%x", sbuf.type); + (void)snprintf(offset, sizeof(offset), "%ld", + (long)sbuf.offset); /* * XXX * The modload docs say that drivers can install bdevsw & * cdevsw, but the interface only supports one at a time. */ - - cleanup(); - execl(post, post, id, type, offset, (char *)NULL); err(16, "can't exec `%s'", post); } - return 0; + exit (0); } |