diff options
author | Michael Shalayeff <mickey@cvs.openbsd.org> | 2004-02-03 12:09:48 +0000 |
---|---|---|
committer | Michael Shalayeff <mickey@cvs.openbsd.org> | 2004-02-03 12:09:48 +0000 |
commit | ee64ff9774a8a97d8ac9159076e5aa096f4b0465 (patch) | |
tree | 56c0a44d5a11182f89b55b68c79f750f9a093bc6 /sys/arch/amd64/stand/installboot | |
parent | 74d9ef0d94f422fd01f5c329723d4a06537c0884 (diff) |
das boot; das cloned das from das i386
Diffstat (limited to 'sys/arch/amd64/stand/installboot')
-rw-r--r-- | sys/arch/amd64/stand/installboot/Makefile | 19 | ||||
-rw-r--r-- | sys/arch/amd64/stand/installboot/installboot.8 | 135 | ||||
-rw-r--r-- | sys/arch/amd64/stand/installboot/installboot.c | 535 | ||||
-rw-r--r-- | sys/arch/amd64/stand/installboot/nlist.c | 547 |
4 files changed, 1236 insertions, 0 deletions
diff --git a/sys/arch/amd64/stand/installboot/Makefile b/sys/arch/amd64/stand/installboot/Makefile new file mode 100644 index 00000000000..400dd87b217 --- /dev/null +++ b/sys/arch/amd64/stand/installboot/Makefile @@ -0,0 +1,19 @@ +# $OpenBSD: Makefile,v 1.1 2004/02/03 12:09:47 mickey Exp $ + +MAN= installboot.8 + +.if ${MACHINE} == "amd64" +PROG= installboot +CPPFLAGFS+=-I${.CURDIR} + +LDADD= -lutil +DPADD= ${LIBUTIL} +# Need this to work in the miniroot +LDSTATIC= -static + +SADIR= ${.CURDIR}/.. +.else +NOPROG= +.endif + +.include <bsd.prog.mk> diff --git a/sys/arch/amd64/stand/installboot/installboot.8 b/sys/arch/amd64/stand/installboot/installboot.8 new file mode 100644 index 00000000000..fc1836b2ac7 --- /dev/null +++ b/sys/arch/amd64/stand/installboot/installboot.8 @@ -0,0 +1,135 @@ +.\" $OpenBSD: installboot.8,v 1.1 2004/02/03 12:09:47 mickey Exp $ +.\" +.\" Copyright (c) 1997 Michael Shalayeff +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" +.Dd December 23, 2003 +.Dt INSTALLBOOT 8 i386 +.Os +.Sh NAME +.Nm installboot +.Nd installs a bootstrap on an FFS disk or partition +.Sh SYNOPSIS +.Nm installboot +.Op Fl nv +.Ar boot +.Ar biosboot +.Ar disk +.Sh DESCRIPTION +.Nm +is used to install a +.Dq first-stage +boot program into the boot area of an FFS disk partition. +It inserts the block number and offset of the inode of +the second-stage boot program +.Xr boot 8 +so that the +.Xr biosboot 8 +program can load it. +Various filesystem parameters are also patched into the boot block. +.Pp +The options are as follows: +.Bl -tag -width flag_opt +.It Fl n +Do not actually write anything on the disk. +.It Fl v +Be verbose, printing out the data that are stored in +.Ar biosboot +along with lots of other information. +.El +.Pp +The arguments are: +.Bl -tag -width biosboot +.It Ar boot +The name of the second-stage boot program in the mounted file system +where the first-stage boot program is to be installed. +This should be a full pathname. +.It Ar biosboot +The name of the prototype file for the first stage boot program, +usually +.Pa /usr/mdec/biosboot . +.It Ar disk +The name of the disk containing the partition in which the second-stage +boot program resides and the first-stage boot program is to be installed. +This can either be specified in short form (e.g., +.Sq sd0 +or +.Sq wd0 ) , +or as the explicit device node, such as +.Pa /dev/rsd0c +or +.Pa /dev/rwd0c . +.Pp +Note that you must be in single-user mode or have your kernel in +insecure mode (see the +.Xr sysctl 8 +.Va kern.securelevel +variable or +.Pa /etc/rc.securelevel ) +to enable access to the raw partition of a mounted disk. +.Pp +The +.Sq c +partition is always used to represent the +.Dq entire +disk on i386. +.El +.Sh EXAMPLES +The typical use is +.Bd -literal -offset indent +# cp /usr/mdec/boot /boot +# /usr/mdec/installboot -n -v /boot /usr/mdec/biosboot sd0 +.Ed +.Pp +And if the information supplied looks right, run the above without the +.Fl n +flag. +If you are upgrading an old system, you may need to perform +some additional steps first. +For example: +.Bd -literal -offset indent +boot the floppy.fs filesystem floppy +# fsck /dev/rsd0a +# mount /dev/sd0a /mnt +# cp /usr/mdec/boot /mnt/boot +# /usr/mdec/installboot -v /mnt/boot /usr/mdec/biosboot sd0 +.Ed +.Sh SEE ALSO +.Xr biosboot 8 , +.Xr boot 8 , +.Xr disklabel 8 , +.Xr fdisk 8 , +.Xr init 8 +.Sh CAVEATS +The disklabel +.Va d_type +field must be set to a value other than +.Dq unknown . +.Pp +.Pa /boot +must be on the drive/partition specified by +.Pa disk ; +you cannot perform cross-device +.Nm Ns s . diff --git a/sys/arch/amd64/stand/installboot/installboot.c b/sys/arch/amd64/stand/installboot/installboot.c new file mode 100644 index 00000000000..f834d769143 --- /dev/null +++ b/sys/arch/amd64/stand/installboot/installboot.c @@ -0,0 +1,535 @@ +/* $OpenBSD: installboot.c,v 1.1 2004/02/03 12:09:47 mickey Exp $ */ +/* $NetBSD: installboot.c,v 1.5 1995/11/17 23:23:50 gwr Exp $ */ + +/* + * Copyright (c) 2003 Tom Cosgrove <tom.cosgrove@arches-consulting.com> + * Copyright (c) 1997 Michael Shalayeff + * Copyright (c) 1994 Paul Kranenburg + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Paul Kranenburg. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define ELFSIZE 32 + +#include <sys/param.h> +#include <sys/mount.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/disklabel.h> +#include <sys/dkio.h> +#include <sys/ioctl.h> +#include <ufs/ufs/dinode.h> +#include <ufs/ufs/dir.h> +#include <ufs/ffs/fs.h> +#include <sys/reboot.h> + +#include <uvm/uvm_extern.h> +#include <sys/sysctl.h> + +#include <machine/cpu.h> +#include <machine/biosvar.h> + +#include <err.h> +#include <a.out.h> +#include <sys/exec_elf.h> +#include <fcntl.h> +#include <nlist.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <util.h> + +#include "nlist.c" + +struct sym_data { + char *sym_name; /* Must be initialised */ + int sym_size; /* And this one */ + int sym_set; /* Rest set at runtime */ + u_int32_t sym_value; +}; + +extern char *__progname; +int verbose, nowrite = 0; +char *boot, *proto, *dev, *realdev; +struct sym_data pbr_symbols[] = { + {"_fs_bsize_p", 2}, + {"_fs_bsize_s", 2}, + {"_fsbtodb", 1}, + {"_p_offset", 4}, + {"_inodeblk", 4}, + {"_inodedbl", 4}, + {"_nblocks", 2}, + {NULL} +}; + +#define INODESEG 0x07e0 /* where we will put /boot's inode's block */ +#define BOOTSEG 0x07c0 /* biosboot loaded here */ + +#define INODEOFF ((INODESEG-BOOTSEG) << 4) + +static char *loadproto(char *, long *); +static int getbootparams(char *, int, struct disklabel *); +static void devread(int, void *, daddr_t, size_t, char *); +static void sym_set_value(struct sym_data *, char *, u_int32_t); +static void pbr_set_symbols(char *, char *, struct sym_data *); +static void usage(void); + +static void +usage(void) +{ + fprintf(stderr, "usage: %s [-nv] boot biosboot device\n", __progname); + exit(1); +} + +/* + * Read information about /boot's inode and filesystem parameters, then + * put biosboot (partition boot record) on the target drive with these + * parameters patched in. + */ +int +main(int argc, char *argv[]) +{ + int c; + int devfd; + char *protostore; + long protosize; + struct stat sb; + struct disklabel dl; + struct dos_mbr mbr; + struct dos_partition *dp; + off_t startoff = 0; + + while ((c = getopt(argc, argv, "vn")) != -1) { + switch (c) { + case 'n': + /* Do not actually write the bootblock to disk. */ + nowrite = 1; + break; + case 'v': + /* Give more information. */ + verbose = 1; + break; + default: + usage(); + } + } + + if (argc - optind < 3) + usage(); + + boot = argv[optind]; + proto = argv[optind + 1]; + realdev = dev = argv[optind + 2]; + + /* Open and check raw disk device. */ + if ((devfd = opendev(dev, (nowrite? O_RDONLY:O_RDWR), + OPENDEV_PART, &realdev)) < 0) + err(1, "open: %s", realdev); + + if (verbose) { + fprintf(stderr, "boot: %s\n", boot); + fprintf(stderr, "proto: %s\n", proto); + fprintf(stderr, "device: %s\n", realdev); + } + + if (ioctl(devfd, DIOCGDINFO, &dl) != 0) + err(1, "disklabel: %s", realdev); + + /* Check disklabel. */ + if (dl.d_magic != DISKMAGIC) + err(1, "bad disklabel magic=%0x8x", dl.d_magic); + + /* Warn on unknown disklabel types. */ + if (dl.d_type == 0) + warnx("disklabel type unknown"); + + /* Load proto blocks into core. */ + if ((protostore = loadproto(proto, &protosize)) == NULL) + exit(1); + + /* XXX - Paranoia: Make sure size is aligned! */ + if (protosize & (DEV_BSIZE - 1)) + err(1, "proto %s bad size=%ld", proto, protosize); + + /* Write patched proto bootblock(s) into the superblock. */ + if (protosize > SBSIZE - DEV_BSIZE) + errx(1, "proto bootblocks too big"); + + if (fstat(devfd, &sb) < 0) + err(1, "stat: %s", realdev); + + if (!S_ISCHR(sb.st_mode)) + errx(1, "%s: not a character device", realdev); + + /* Get bootstrap parameters that are to be patched into proto. */ + if (getbootparams(boot, devfd, &dl) != 0) + exit(1); + + /* Patch the parameters into the proto bootstrap sector. */ + pbr_set_symbols(proto, protostore, pbr_symbols); + + if (!nowrite) { + /* Sync filesystems (to clean in-memory superblock?). */ + sync(); sleep(1); + } + + if (dl.d_type != 0 && dl.d_type != DTYPE_FLOPPY && + dl.d_type != DTYPE_VND) { + if (lseek(devfd, (off_t)DOSBBSECTOR, SEEK_SET) < 0 || + read(devfd, &mbr, sizeof(mbr)) < sizeof(mbr)) + err(4, "can't read master boot record"); + + if (mbr.dmbr_sign != DOSMBR_SIGNATURE) + errx(1, "broken MBR"); + + /* Find OpenBSD partition. */ + for (dp = mbr.dmbr_parts; dp < &mbr.dmbr_parts[NDOSPART]; + dp++) { + if (dp->dp_size && dp->dp_typ == DOSPTYP_OPENBSD) { + startoff = (off_t)dp->dp_start * dl.d_secsize; + fprintf(stderr, "using MBR partition %ld: " + "type %d (0x%02x) offset %d (0x%x)\n", + (long)(dp - mbr.dmbr_parts), + dp->dp_typ, dp->dp_typ, + dp->dp_start, dp->dp_start); + break; + } + } + /* Don't check for old part number, that is ;-p */ + if (dp >= &mbr.dmbr_parts[NDOSPART]) + errx(1, "no OpenBSD partition"); + } + + if (!nowrite) { + if (lseek(devfd, startoff, SEEK_SET) < 0 || + write(devfd, protostore, protosize) != protosize) + err(1, "write bootstrap"); + } + + (void)close(devfd); + + return 0; +} + +/* + * Load the prototype boot sector (biosboot) into memory. + */ +static char * +loadproto(char *fname, long *size) +{ + int fd; + size_t tdsize; /* text+data size */ + char *bp; + Elf_Ehdr eh; + Elf_Word phsize; + Elf_Phdr *ph; + + if ((fd = open(fname, O_RDONLY)) < 0) + err(1, "%s", fname); + + if (read(fd, &eh, sizeof(eh)) != sizeof(eh)) + errx(1, "%s: read failed", fname); + + if (!IS_ELF(eh)) + errx(1, "%s: bad magic: 0x%02x%02x%02x%02x", + boot, + eh.e_ident[EI_MAG0], eh.e_ident[EI_MAG1], + eh.e_ident[EI_MAG2], eh.e_ident[EI_MAG3]); + + /* + * We have to include the exec header in the beginning of + * the buffer, and leave extra space at the end in case + * the actual write to disk wants to skip the header. + */ + + /* Program load header. */ + if (eh.e_phnum != 1) + errx(1, "%s: %u ELF load sections (only support 1)", + boot, eh.e_phnum); + + phsize = eh.e_phnum * sizeof(Elf_Phdr); + ph = malloc(phsize); + if (ph == NULL) + err(1, NULL); + + lseek(fd, eh.e_phoff, SEEK_SET); + + if (read(fd, ph, phsize) != phsize) + errx(1, "%s: can't read header", boot); + + tdsize = ph->p_filesz; + + /* + * Allocate extra space here because the caller may copy + * the boot block starting at the end of the exec header. + * This prevents reading beyond the end of the buffer. + */ + if ((bp = calloc(tdsize, 1)) == NULL) { + err(1, NULL); + } + + /* Read the rest of the file. */ + lseek(fd, ph->p_offset, SEEK_SET); + if (read(fd, bp, tdsize) != tdsize) { + errx(1, "%s: read failed", fname); + } + + *size = tdsize; /* not aligned to DEV_BSIZE */ + + if (verbose) { + fprintf(stderr, "%s: entry point %#x\n", fname, eh.e_entry); + fprintf(stderr, "proto bootblock size %ld\n", *size); + } + + close(fd); + return bp; +} + +static void +devread(int fd, void *buf, daddr_t blk, size_t size, char *msg) +{ + if (lseek(fd, dbtob((off_t)blk), SEEK_SET) != dbtob((off_t)blk)) + err(1, "%s: devread: lseek", msg); + + if (read(fd, buf, size) != size) + err(1, "%s: devread: read", msg); +} + +static char sblock[SBSIZE]; + +/* + * Read information about /boot's inode, then put this and filesystem + * parameters from the superblock into pbr_symbols. + */ +static int +getbootparams(char *boot, int devfd, struct disklabel *dl) +{ + int fd; + struct stat statbuf, sb; + struct statfs statfsbuf; + struct partition *pl; + struct fs *fs; + char *buf; + daddr_t blk, *ap; + struct ufs1_dinode *ip; + int ndb; + int mib[4]; + size_t size; + dev_t dev; + + /* + * Open 2nd-level boot program and record enough details about + * where it is on the filesystem represented by `devfd' + * (inode block, offset within that block, and various filesystem + * parameters essentially taken from the superblock) for biosboot + * to be able to load it later. + */ + + /* Make sure the (probably new) boot file is on disk. */ + sync(); sleep(1); + + if ((fd = open(boot, O_RDONLY)) < 0) + err(1, "open: %s", boot); + + if (fstatfs(fd, &statfsbuf) != 0) + err(1, "statfs: %s", boot); + + if (strncmp(statfsbuf.f_fstypename, "ffs", MFSNAMELEN) && + strncmp(statfsbuf.f_fstypename, "ufs", MFSNAMELEN) ) + errx(1, "%s: not on an FFS filesystem", boot); + +#if 0 + if (read(fd, &eh, sizeof(eh)) != sizeof(eh)) + errx(1, "read: %s", boot); + + if (!IS_ELF(eh)) { + errx(1, "%s: bad magic: 0x%02x%02x%02x%02x", + boot, + eh.e_ident[EI_MAG0], eh.e_ident[EI_MAG1], + eh.e_ident[EI_MAG2], eh.e_ident[EI_MAG3]); + } +#endif + + if (fsync(fd) != 0) + err(1, "fsync: %s", boot); + + if (fstat(fd, &statbuf) != 0) + err(1, "fstat: %s", boot); + + if (fstat(devfd, &sb) != 0) + err(1, "fstat: %s", realdev); + + /* Check devices. */ + mib[0] = CTL_MACHDEP; + mib[1] = CPU_CHR2BLK; + mib[2] = sb.st_rdev; + size = sizeof(dev); + if (sysctl(mib, 3, &dev, &size, NULL, 0) >= 0) { + if (statbuf.st_dev / MAXPARTITIONS != dev / MAXPARTITIONS) + errx(1, "cross-device install"); + } + + pl = &dl->d_partitions[DISKPART(statbuf.st_dev)]; + close(fd); + + /* Read superblock. */ + devread(devfd, sblock, pl->p_offset + SBLOCK, SBSIZE, "superblock"); + fs = (struct fs *)sblock; + + /* Sanity-check super-block. */ + if (fs->fs_magic != FS_MAGIC) + errx(1, "Bad magic number in superblock"); + if (fs->fs_inopb <= 0) + err(1, "Bad inopb=%d in superblock", fs->fs_inopb); + + /* Read inode. */ + if ((buf = malloc(fs->fs_bsize)) == NULL) + err(1, NULL); + + blk = fsbtodb(fs, ino_to_fsba(fs, statbuf.st_ino)); + + devread(devfd, buf, pl->p_offset + blk, fs->fs_bsize, "inode"); + ip = (struct ufs1_dinode *)(buf) + ino_to_fsbo(fs, statbuf.st_ino); + + /* + * Have the inode. Figure out how many filesystem blocks (not disk + * sectors) there are for biosboot to load. + */ + ndb = howmany(ip->di_size, fs->fs_bsize); + if (ndb <= 0) + errx(1, "No blocks to load"); + if (verbose) + fprintf(stderr, "%s is %d blocks x %d bytes\n", + boot, ndb, fs->fs_bsize); + + /* + * Now set the values that will need to go into biosboot + * (the partition boot record, a.k.a. the PBR). + */ + sym_set_value(pbr_symbols, "_fs_bsize_p", (fs->fs_bsize / 16)); + sym_set_value(pbr_symbols, "_fs_bsize_s", (fs->fs_bsize / 512)); + sym_set_value(pbr_symbols, "_fsbtodb", fs->fs_fsbtodb); + sym_set_value(pbr_symbols, "_p_offset", pl->p_offset); + sym_set_value(pbr_symbols, "_inodeblk", + ino_to_fsba(fs, statbuf.st_ino)); + ap = ip->di_db; + sym_set_value(pbr_symbols, "_inodedbl", + ((((char *)ap) - buf) + INODEOFF)); + sym_set_value(pbr_symbols, "_nblocks", ndb); + + return 0; +} + +static void +sym_set_value(struct sym_data *sym_list, char *sym, u_int32_t value) +{ + struct sym_data *p; + + for (p = sym_list; p->sym_name != NULL; p++) { + if (strcmp(p->sym_name, sym) == 0) + break; + } + + if (p->sym_name == NULL) + errx(1, "%s: no such symbol", sym); + + if (p->sym_set) + errx(1, "%s already set", p->sym_name); + + p->sym_value = value; + p->sym_set = 1; +} + +/* + * Write the parameters stored in sym_list into the in-memory copy of + * the prototype biosboot (proto), ready for it to be written to disk. + */ +static void +pbr_set_symbols(char *fname, char *proto, struct sym_data *sym_list) +{ + struct sym_data *sym; + struct nlist *nl; + char *vp; + u_int32_t *lp; + u_int16_t *wp; + u_int8_t *bp; + + for (sym = sym_list; sym->sym_name != NULL; sym++) { + if (!sym->sym_set) + errx(1, "%s not set", sym->sym_name); + + /* Allocate space for 2; second is null-terminator for list. */ + nl = calloc(2, sizeof(struct nlist)); + if (nl == NULL) + err(1, NULL); + + nl->n_un.n_name = sym->sym_name; + + if (nlist(fname, nl) != 0) + errx(1, "%s: symbol %s not found", + fname, sym->sym_name); + + if (nl->n_type != (N_TEXT)) + errx(1, "%s: %s: wrong type (%x)", + fname, sym->sym_name, nl->n_type); + + /* Get a pointer to where the symbol's value needs to go. */ + vp = proto + nl->n_value; + + switch (sym->sym_size) { + case 4: /* u_int32_t */ + lp = (u_int32_t *) vp; + *lp = sym->sym_value; + break; + case 2: /* u_int16_t */ + if (sym->sym_value >= 0x10000) /* out of range */ + errx(1, "%s: symbol out of range (%u)", + sym->sym_name, sym->sym_value); + wp = (u_int16_t *) vp; + *wp = (u_int16_t) sym->sym_value; + break; + case 1: /* u_int16_t */ + if (sym->sym_value >= 0x100) /* out of range */ + errx(1, "%s: symbol out of range (%u)", + sym->sym_name, sym->sym_value); + bp = (u_int8_t *) vp; + *bp = (u_int8_t) sym->sym_value; + break; + default: + errx(1, "%s: bad symbol size %d", + sym->sym_name, sym->sym_size); + /* NOTREACHED */ + } + + free(nl); + + if (verbose) + fprintf(stderr, "%s = %u\n", + sym->sym_name, sym->sym_value); + } +} diff --git a/sys/arch/amd64/stand/installboot/nlist.c b/sys/arch/amd64/stand/installboot/nlist.c new file mode 100644 index 00000000000..477c6c0f738 --- /dev/null +++ b/sys/arch/amd64/stand/installboot/nlist.c @@ -0,0 +1,547 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char rcsid[] = "$OpenBSD: nlist.c,v 1.1 2004/02/03 12:09:47 mickey Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/mman.h> +#include <sys/stat.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <a.out.h> /* pulls in nlist.h */ + +#ifdef _NLIST_DO_ELF +#include <elf_abi.h> +#include <olf_abi.h> +#endif + +#ifdef _NLIST_DO_ECOFF +#include <sys/exec_ecoff.h> +#endif + +int __fdnlist(int, struct nlist *); +int __aout_fdnlist(int, struct nlist *); +int __ecoff_fdnlist(int, struct nlist *); +int __elf_fdnlist(int, struct nlist *); +#ifdef _NLIST_DO_ELF +int __elf_is_okay__(register Elf_Ehdr *ehdr); +#endif + +#define ISLAST(p) (p->n_un.n_name == 0 || p->n_un.n_name[0] == 0) + +#ifdef _NLIST_DO_AOUT +int +__aout_fdnlist(fd, list) + register int fd; + register struct nlist *list; +{ + register struct nlist *p, *s; + register char *strtab; + register off_t symoff, stroff; + register u_long symsize; + register int nent, cc; + int strsize, usemalloc = 0; + struct nlist nbuf[1024]; + struct exec exec; + + if (pread(fd, &exec, sizeof(exec), (off_t)0) != sizeof(exec) || + N_BADMAG(exec) || exec.a_syms == NULL) + return (-1); + + stroff = N_STROFF(exec); + symoff = N_SYMOFF(exec); + symsize = exec.a_syms; + + /* Read in the size of the string table. */ + if (pread(fd, (void *)&strsize, sizeof(strsize), stroff) != + sizeof(strsize)) + return (-1); + else + stroff += sizeof(strsize); + + /* + * Read in the string table. We try mmap, but that will fail + * for /dev/ksyms so fall back on malloc. Since OpenBSD's malloc(3) + * returns memory to the system on free this does not cause bloat. + */ + strsize -= sizeof(strsize); + strtab = mmap(NULL, (size_t)strsize, PROT_READ, MAP_SHARED|MAP_FILE, + fd, stroff); + if (strtab == MAP_FAILED) { + usemalloc = 1; + if ((strtab = (char *)malloc(strsize)) == NULL) + return (-1); + errno = EIO; + if (pread(fd, strtab, strsize, stroff) != strsize) { + nent = -1; + goto aout_done; + } + } + + /* + * clean out any left-over information for all valid entries. + * Type and value defined to be 0 if not found; historical + * versions cleared other and desc as well. Also figure out + * the largest string length so don't read any more of the + * string table than we have to. + * + * XXX clearing anything other than n_type and n_value violates + * the semantics given in the man page. + */ + nent = 0; + for (p = list; !ISLAST(p); ++p) { + p->n_type = 0; + p->n_other = 0; + p->n_desc = 0; + p->n_value = 0; + ++nent; + } + + while (symsize > 0) { + cc = MIN(symsize, sizeof(nbuf)); + if (pread(fd, nbuf, cc, symoff) != cc) + break; + symsize -= cc; + symoff += cc; + for (s = nbuf; cc > 0; ++s, cc -= sizeof(*s)) { + char *sname = strtab + s->n_un.n_strx - sizeof(int); + + if (s->n_un.n_strx == 0 || (s->n_type & N_STAB) != 0) + continue; + for (p = list; !ISLAST(p); p++) { + char *pname = p->n_un.n_name; + + if (*sname != '_' && *pname == '_') + pname++; + if (!strcmp(sname, pname)) { + p->n_value = s->n_value; + p->n_type = s->n_type; + p->n_desc = s->n_desc; + p->n_other = s->n_other; + if (--nent <= 0) + break; + } + } + } + } +aout_done: + if (usemalloc) + free(strtab); + else + munmap(strtab, strsize); + return (nent); +} +#endif /* _NLIST_DO_AOUT */ + +#ifdef _NLIST_DO_ECOFF +#define check(off, size) ((off < 0) || (off + size > mappedsize)) +#define BAD do { rv = -1; goto out; } while (0) +#define BADUNMAP do { rv = -1; goto unmap; } while (0) + +int +__ecoff_fdnlist(fd, list) + register int fd; + register struct nlist *list; +{ + struct nlist *p; + struct ecoff_exechdr *exechdrp; + struct ecoff_symhdr *symhdrp; + struct ecoff_extsym *esyms; + struct stat st; + char *mappedfile; + size_t mappedsize; + u_long symhdroff, extstroff; + u_int symhdrsize; + int rv, nent; + long i, nesyms; + + rv = -3; + + if (fstat(fd, &st) < 0) + BAD; + if (st.st_size > SIZE_T_MAX) { + errno = EFBIG; + BAD; + } + mappedsize = st.st_size; + mappedfile = mmap(NULL, mappedsize, PROT_READ, MAP_SHARED|MAP_FILE, + fd, 0); + if (mappedfile == MAP_FAILED) + BAD; + + if (check(0, sizeof *exechdrp)) + BADUNMAP; + exechdrp = (struct ecoff_exechdr *)&mappedfile[0]; + + if (ECOFF_BADMAG(exechdrp)) + BADUNMAP; + + symhdroff = exechdrp->f.f_symptr; + symhdrsize = exechdrp->f.f_nsyms; + + if (check(symhdroff, sizeof *symhdrp) || + sizeof *symhdrp != symhdrsize) + BADUNMAP; + symhdrp = (struct ecoff_symhdr *)&mappedfile[symhdroff]; + + nesyms = symhdrp->esymMax; + if (check(symhdrp->cbExtOffset, nesyms * sizeof *esyms)) + BADUNMAP; + esyms = (struct ecoff_extsym *)&mappedfile[symhdrp->cbExtOffset]; + extstroff = symhdrp->cbSsExtOffset; + + /* + * clean out any left-over information for all valid entries. + * Type and value defined to be 0 if not found; historical + * versions cleared other and desc as well. + * + * XXX clearing anything other than n_type and n_value violates + * the semantics given in the man page. + */ + nent = 0; + for (p = list; !ISLAST(p); ++p) { + p->n_type = 0; + p->n_other = 0; + p->n_desc = 0; + p->n_value = 0; + ++nent; + } + + for (i = 0; i < nesyms; i++) { + for (p = list; !ISLAST(p); p++) { + char *nlistname; + char *symtabname; + + nlistname = p->n_un.n_name; + if (*nlistname == '_') + nlistname++; + symtabname = + &mappedfile[extstroff + esyms[i].es_strindex]; + + if (!strcmp(symtabname, nlistname)) { + p->n_value = esyms[i].es_value; + p->n_type = N_EXT; /* XXX */ + p->n_desc = 0; /* XXX */ + p->n_other = 0; /* XXX */ + if (--nent <= 0) + break; + } + } + } + rv = nent; + +unmap: + munmap(mappedfile, mappedsize); +out: + return (rv); +} +#endif /* _NLIST_DO_ECOFF */ + +#ifdef _NLIST_DO_ELF +/* + * __elf_is_okay__ - Determine if ehdr really + * is ELF and valid for the target platform. + * + * WARNING: This is NOT a ELF ABI function and + * as such it's use should be restricted. + */ +int +__elf_is_okay__(ehdr) + register Elf_Ehdr *ehdr; +{ + register int retval = 0; + /* + * We need to check magic, class size, endianess, + * and version before we look at the rest of the + * Elf_Ehdr structure. These few elements are + * represented in a machine independent fashion. + */ + if ((IS_ELF(*ehdr) || IS_OLF(*ehdr)) && + ehdr->e_ident[EI_CLASS] == ELF_TARG_CLASS && + ehdr->e_ident[EI_DATA] == ELF_TARG_DATA && + ehdr->e_ident[EI_VERSION] == ELF_TARG_VER) { + + /* Now check the machine dependant header */ + if (ehdr->e_machine == ELF_TARG_MACH && + ehdr->e_version == ELF_TARG_VER) + retval = 1; + } + + return retval; +} + +int +__elf_fdnlist(fd, list) + register int fd; + register struct nlist *list; +{ + register struct nlist *p; + register caddr_t strtab; + register Elf_Off symoff = 0, symstroff = 0; + register Elf_Word symsize = 0, symstrsize = 0; + register Elf_Sword nent, cc, i; + Elf_Sym sbuf[1024]; + Elf_Sym *s; + Elf_Ehdr ehdr; + Elf_Shdr *shdr = NULL; + Elf_Word shdr_size; + struct stat st; + int usemalloc = 0; + + /* Make sure obj is OK */ + if (pread(fd, &ehdr, sizeof(Elf_Ehdr), (off_t)0) != sizeof(Elf_Ehdr) || + /* !__elf_is_okay__(&ehdr) || */ fstat(fd, &st) < 0) + return (-1); + + /* calculate section header table size */ + shdr_size = ehdr.e_shentsize * ehdr.e_shnum; + + /* Make sure it's not too big to mmap */ + if (shdr_size > SIZE_T_MAX) { + errno = EFBIG; + return (-1); + } + + /* mmap section header table */ + shdr = (Elf_Shdr *)mmap(NULL, (size_t)shdr_size, PROT_READ, + MAP_SHARED|MAP_FILE, fd, (off_t) ehdr.e_shoff); + if (shdr == MAP_FAILED) { + usemalloc = 1; + if ((shdr = malloc(shdr_size)) == NULL) + return (-1); + if (pread(fd, shdr, shdr_size, ehdr.e_shoff) != shdr_size) { + free(shdr); + return (-1); + } + } + + /* + * Find the symbol table entry and it's corresponding + * string table entry. Version 1.1 of the ABI states + * that there is only one symbol table but that this + * could change in the future. + */ + for (i = 0; i < ehdr.e_shnum; i++) { + if (shdr[i].sh_type == SHT_SYMTAB) { + symoff = shdr[i].sh_offset; + symsize = shdr[i].sh_size; + symstroff = shdr[shdr[i].sh_link].sh_offset; + symstrsize = shdr[shdr[i].sh_link].sh_size; + break; + } + } + + /* Flush the section header table */ + if (usemalloc) + free(shdr); + else + munmap((caddr_t)shdr, shdr_size); + + /* Check for files too large to mmap. */ + /* XXX is this really possible? */ + if (symstrsize > SIZE_T_MAX) { + errno = EFBIG; + return (-1); + } + /* + * Map string table into our address space. This gives us + * an easy way to randomly access all the strings, without + * making the memory allocation permanent as with malloc/free + * (i.e., munmap will return it to the system). + */ + if (usemalloc) { + if ((strtab = malloc(symstrsize)) == NULL) + return (-1); + if (pread(fd, strtab, symstrsize, symstroff) != symstrsize) { + free(strtab); + return (-1); + } + } else { + strtab = mmap(NULL, (size_t)symstrsize, PROT_READ, + MAP_SHARED|MAP_FILE, fd, (off_t) symstroff); + if (strtab == MAP_FAILED) + return (-1); + } + /* + * clean out any left-over information for all valid entries. + * Type and value defined to be 0 if not found; historical + * versions cleared other and desc as well. Also figure out + * the largest string length so don't read any more of the + * string table than we have to. + * + * XXX clearing anything other than n_type and n_value violates + * the semantics given in the man page. + */ + nent = 0; + for (p = list; !ISLAST(p); ++p) { + p->n_type = 0; + p->n_other = 0; + p->n_desc = 0; + p->n_value = 0; + ++nent; + } + + /* Don't process any further if object is stripped. */ + /* ELFism - dunno if stripped by looking at header */ + if (symoff == 0) + goto elf_done; + + while (symsize > 0) { + cc = MIN(symsize, sizeof(sbuf)); + if (pread(fd, sbuf, cc, symoff) != cc) + break; + symsize -= cc; + symoff += cc; + for (s = sbuf; cc > 0; ++s, cc -= sizeof(*s)) { + int soff = s->st_name; + + if (soff == 0) + continue; + for (p = list; !ISLAST(p); p++) { + char *sym; + + /* + * First we check for the symbol as it was + * provided by the user. If that fails, + * skip the first char if it's an '_' and + * try again. + * XXX - What do we do when the user really + * wants '_foo' and the are symbols + * for both 'foo' and '_foo' in the + * table and 'foo' is first? + */ + sym = p->n_un.n_name; + if (strcmp(&strtab[soff], sym) != 0 && + ((sym[0] == '_') && + strcmp(&strtab[soff], sym + 1) != 0)) + continue; + + p->n_value = s->st_value; + + /* XXX - type conversion */ + /* is pretty rude. */ + switch(ELF_ST_TYPE(s->st_info)) { + case STT_NOTYPE: + switch (s->st_shndx) { + case SHN_UNDEF: + p->n_type = N_UNDF; + break; + case SHN_ABS: + p->n_type = N_ABS; + break; + case SHN_COMMON: + p->n_type = N_COMM; + break; + default: + p->n_type = N_COMM | N_EXT; + break; + } + break; + case STT_OBJECT: + p->n_type = N_DATA; + break; + case STT_FUNC: + p->n_type = N_TEXT; + break; + case STT_FILE: + p->n_type = N_FN; + break; + } + if (ELF_ST_BIND(s->st_info) == + STB_LOCAL) + p->n_type = N_EXT; + p->n_desc = 0; + p->n_other = 0; + if (--nent <= 0) + break; + } + } + } +elf_done: + if (usemalloc) + free(strtab); + else + munmap(strtab, symstrsize); + return (nent); +} +#endif /* _NLIST_DO_ELF */ + + +static struct nlist_handlers { + int (*fn)(int fd, struct nlist *list); +} nlist_fn[] = { +#ifdef _NLIST_DO_AOUT + { __aout_fdnlist }, +#endif +#ifdef _NLIST_DO_ELF + { __elf_fdnlist }, +#endif +#ifdef _NLIST_DO_ECOFF + { __ecoff_fdnlist }, +#endif +}; + +int +__fdnlist(fd, list) + register int fd; + register struct nlist *list; +{ + int n = -1, i; + + for (i = 0; i < sizeof(nlist_fn)/sizeof(nlist_fn[0]); i++) { + n = (nlist_fn[i].fn)(fd, list); + if (n != -1) + break; + } + return (n); +} + + +int +nlist(name, list) + const char *name; + struct nlist *list; +{ + int fd, n; + + fd = open(name, O_RDONLY, 0); + if (fd < 0) + return (-1); + n = __fdnlist(fd, list); + (void)close(fd); + return (n); +} |