139092Srnordier/*
239092Srnordier * Copyright (c) 1998 Robert Nordier
339092Srnordier * All rights reserved.
439092Srnordier *
539092Srnordier * Redistribution and use in source and binary forms, with or without
639092Srnordier * modification, are permitted provided that the following conditions
739092Srnordier * are met:
839092Srnordier * 1. Redistributions of source code must retain the above copyright
939092Srnordier *    notice, this list of conditions and the following disclaimer.
1039092Srnordier * 2. Redistributions in binary form must reproduce the above copyright
1139092Srnordier *    notice, this list of conditions and the following disclaimer in the
1239092Srnordier *    documentation and/or other materials provided with the distribution.
1339092Srnordier *
1439092Srnordier * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND
1539092Srnordier * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1639092Srnordier * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1739092Srnordier * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
1839092Srnordier * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
1939092Srnordier * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
2039092Srnordier * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
2139092Srnordier * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
2239092Srnordier * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
2339092Srnordier * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
2439092Srnordier * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2539092Srnordier */
2639092Srnordier
2739092Srnordier#ifndef lint
2839092Srnordierstatic const char rcsid[] =
2950479Speter  "$FreeBSD: releng/10.3/usr.sbin/btxld/btxld.c 263835 2014-03-27 20:19:11Z brueffer $";
3039092Srnordier#endif /* not lint */
3139092Srnordier
32103917Smike#include <sys/param.h>
33130927Sobrien#include <sys/endian.h>
3439092Srnordier#include <sys/stat.h>
3539092Srnordier#include <sys/mman.h>
3639092Srnordier
37142108Sru/* XXX make this work as an i386/amd64 cross-tool */
38142108Sru#include <machine/exec.h>
39142108Sru#undef __LDPGSZ
40142108Sru#define __LDPGSZ	4096
41142108Sru
42154711Sdelphij#include <netinet/in.h>
43154711Sdelphij
44130927Sobrien#include <a.out.h>
4539092Srnordier#include <err.h>
4639092Srnordier#include <errno.h>
4739092Srnordier#include <fcntl.h>
4839092Srnordier#include <stdarg.h>
4939092Srnordier#include <stdio.h>
5039092Srnordier#include <stdlib.h>
5139092Srnordier#include <string.h>
5239092Srnordier#include <unistd.h>
5339092Srnordier
5439092Srnordier#include "btx.h"
5539092Srnordier#include "elfh.h"
5639092Srnordier
5739092Srnordier#define BTX_PATH		"/sys/boot/i386/btx"
5839092Srnordier
5939092Srnordier#define I_LDR	0		/* BTX loader */
6039092Srnordier#define I_BTX	1		/* BTX kernel */
6139092Srnordier#define I_CLNT	2		/* Client program */
6239092Srnordier
6339092Srnordier#define F_BIN	0		/* Binary */
6439092Srnordier#define F_AOUT	1		/* ZMAGIC a.out */
6539092Srnordier#define F_ELF	2		/* 32-bit ELF */
6639092Srnordier#define F_CNT	3		/* Number of formats */
6739092Srnordier
6839092Srnordier#define IMPURE	1		/* Writable text */
6939092Srnordier#define MAXU32	0xffffffff	/* Maximum unsigned 32-bit quantity */
7039092Srnordier
7139092Srnordier#define align(x, y) (((x) + (y) - 1) & ~((y) - 1))
7239092Srnordier
7339092Srnordierstruct hdr {
7455416Smarcel    uint32_t fmt;		/* Format */
7555416Smarcel    uint32_t flags;		/* Bit flags */
7655416Smarcel    uint32_t size;		/* Size of file */
7755416Smarcel    uint32_t text;		/* Size of text segment */
7855416Smarcel    uint32_t data;		/* Size of data segment */
7955416Smarcel    uint32_t bss;		/* Size of bss segment */
8055416Smarcel    uint32_t org;		/* Program origin */
8155416Smarcel    uint32_t entry;		/* Program entry point */
8239092Srnordier};
8339092Srnordier
8439092Srnordierstatic const char *const fmtlist[] = {"bin", "aout", "elf"};
8539092Srnordier
8639092Srnordierstatic const char binfo[] =
8739092Srnordier    "kernel: ver=%u.%02u size=%x load=%x entry=%x map=%uM "
8839092Srnordier    "pgctl=%x:%x\n";
8939092Srnordierstatic const char cinfo[] =
9039092Srnordier    "client: fmt=%s size=%x text=%x data=%x bss=%x entry=%x\n";
9139092Srnordierstatic const char oinfo[] =
9239092Srnordier    "output: fmt=%s size=%x text=%x data=%x org=%x entry=%x\n";
9339092Srnordier
9439092Srnordierstatic const char *lname =
9539092Srnordier    BTX_PATH "/btxldr/btxldr";	/* BTX loader */
9639092Srnordierstatic const char *bname =
9739092Srnordier    BTX_PATH "/btx/btx";	/* BTX kernel */
9839092Srnordierstatic const char *oname =
9939092Srnordier    "a.out";			/* Output filename */
10039092Srnordier
10139092Srnordierstatic int ppage = -1;		/* First page present */
10239092Srnordierstatic int wpage = -1;		/* First page writable */
10339092Srnordier
10455416Smarcelstatic unsigned int format; 	/* Output format */
10539092Srnordier
10639092Srnordierstatic uint32_t centry; 	/* Client entry address */
10739092Srnordierstatic uint32_t lentry; 	/* Loader entry address */
10839092Srnordier
10939125Srnordierstatic int Eflag;		/* Client entry option */
11039125Srnordier
11139092Srnordierstatic int quiet;		/* Inhibit warnings */
11239092Srnordierstatic int verbose;		/* Display information */
11339092Srnordier
11439092Srnordierstatic const char *tname;	/* Temporary output file */
11539092Srnordierstatic const char *fname;	/* Current input file */
11639092Srnordier
11739092Srnordierstatic void cleanup(void);
11839092Srnordierstatic void btxld(const char *);
11939092Srnordierstatic void getbtx(int, struct btx_hdr *);
12039092Srnordierstatic void gethdr(int, struct hdr *);
12139092Srnordierstatic void puthdr(int, struct hdr *);
12239092Srnordierstatic void copy(int, int, size_t, off_t);
12339092Srnordierstatic size_t readx(int, void *, size_t, off_t);
12439092Srnordierstatic void writex(int, const void *, size_t);
12539092Srnordierstatic void seekx(int, off_t);
12655416Smarcelstatic unsigned int optfmt(const char *);
12739092Srnordierstatic uint32_t optaddr(const char *);
12839092Srnordierstatic int optpage(const char *, int);
12939092Srnordierstatic void Warn(const char *, const char *, ...);
13039092Srnordierstatic void usage(void);
13139092Srnordier
13239092Srnordier/*
13339092Srnordier * A link editor for BTX clients.
13439092Srnordier */
13539092Srnordierint
13639092Srnordiermain(int argc, char *argv[])
13739092Srnordier{
13839092Srnordier    int c;
13939092Srnordier
14039092Srnordier    while ((c = getopt(argc, argv, "qvb:E:e:f:l:o:P:W:")) != -1)
14139092Srnordier	switch (c) {
14239092Srnordier	case 'q':
14339092Srnordier	    quiet = 1;
14439092Srnordier	    break;
14539092Srnordier	case 'v':
14639092Srnordier	    verbose = 1;
14739092Srnordier	    break;
14839092Srnordier	case 'b':
14939092Srnordier	    bname = optarg;
15039092Srnordier	    break;
15139092Srnordier	case 'E':
15239092Srnordier	    centry = optaddr(optarg);
15339125Srnordier	    Eflag = 1;
15439092Srnordier	    break;
15539092Srnordier	case 'e':
15639092Srnordier	    lentry = optaddr(optarg);
15739092Srnordier	    break;
15839092Srnordier	case 'f':
15939092Srnordier	    format = optfmt(optarg);
16039092Srnordier	    break;
16139092Srnordier	case 'l':
16239092Srnordier	    lname = optarg;
16339092Srnordier	    break;
16439092Srnordier	case 'o':
16539092Srnordier	    oname = optarg;
16639092Srnordier	    break;
16739092Srnordier	case 'P':
16839092Srnordier	    ppage = optpage(optarg, 1);
16939092Srnordier	    break;
17039092Srnordier	case 'W':
17139092Srnordier	    wpage = optpage(optarg, BTX_MAXCWR);
17239092Srnordier	    break;
17339092Srnordier	default:
17439092Srnordier	    usage();
17539092Srnordier	}
17639092Srnordier    argc -= optind;
17739092Srnordier    argv += optind;
17839092Srnordier    if (argc != 1)
17939092Srnordier	usage();
18039092Srnordier    atexit(cleanup);
18139092Srnordier    btxld(*argv);
18239092Srnordier    return 0;
18339092Srnordier}
18439092Srnordier
18539092Srnordier/*
18639092Srnordier * Clean up after errors.
18739092Srnordier */
18839092Srnordierstatic void
18939092Srnordiercleanup(void)
19039092Srnordier{
19139092Srnordier    if (tname)
19239092Srnordier	remove(tname);
19339092Srnordier}
19439092Srnordier
19539092Srnordier/*
19639092Srnordier * Read the input files; write the output file; display information.
19739092Srnordier */
19839092Srnordierstatic void
19939092Srnordierbtxld(const char *iname)
20039092Srnordier{
20139092Srnordier    char name[FILENAME_MAX];
202112092Sru    struct btx_hdr btx, btxle;
20339092Srnordier    struct hdr ihdr, ohdr;
20455416Smarcel    unsigned int ldr_size, cwr;
20539092Srnordier    int fdi[3], fdo, i;
20639092Srnordier
20755416Smarcel    ldr_size = 0;
20855416Smarcel
20939092Srnordier    for (i = I_LDR; i <= I_CLNT; i++) {
21039092Srnordier	fname = i == I_LDR ? lname : i == I_BTX ? bname : iname;
21139092Srnordier	if ((fdi[i] = open(fname, O_RDONLY)) == -1)
21239092Srnordier	    err(2, "%s", fname);
21339092Srnordier	switch (i) {
21439092Srnordier	case I_LDR:
21539092Srnordier	    gethdr(fdi[i], &ihdr);
21639092Srnordier	    if (ihdr.fmt != F_BIN)
21739092Srnordier		Warn(fname, "Loader format is %s; processing as %s",
21839092Srnordier		     fmtlist[ihdr.fmt], fmtlist[F_BIN]);
21939092Srnordier	    ldr_size = ihdr.size;
22039092Srnordier	    break;
22139092Srnordier	case I_BTX:
22239092Srnordier	    getbtx(fdi[i], &btx);
22339092Srnordier	    break;
22439092Srnordier	case I_CLNT:
22539092Srnordier	    gethdr(fdi[i], &ihdr);
22639092Srnordier	    if (ihdr.org && ihdr.org != BTX_PGSIZE)
22739092Srnordier		Warn(fname,
22839092Srnordier		     "Client origin is 0x%x; expecting 0 or 0x%x",
22939092Srnordier		     ihdr.org, BTX_PGSIZE);
23039092Srnordier	}
23139092Srnordier    }
23239092Srnordier    memset(&ohdr, 0, sizeof(ohdr));
23339092Srnordier    ohdr.fmt = format;
23439092Srnordier    ohdr.text = ldr_size;
23539092Srnordier    ohdr.data = btx.btx_textsz + ihdr.size;
23639092Srnordier    ohdr.org = lentry;
23739092Srnordier    ohdr.entry = lentry;
23839092Srnordier    cwr = 0;
23955416Smarcel    if (wpage > 0 || (wpage == -1 && !(ihdr.flags & IMPURE))) {
24039092Srnordier	if (wpage > 0)
24139092Srnordier	    cwr = wpage;
24239092Srnordier	else {
24339092Srnordier	    cwr = howmany(ihdr.text, BTX_PGSIZE);
24439092Srnordier	    if (cwr > BTX_MAXCWR)
24539092Srnordier		cwr = BTX_MAXCWR;
24639092Srnordier	}
24755416Smarcel    }
24839092Srnordier    if (ppage > 0 || (ppage && wpage && ihdr.org >= BTX_PGSIZE)) {
24939092Srnordier	btx.btx_flags |= BTX_MAPONE;
25039092Srnordier	if (!cwr)
25139092Srnordier	    cwr++;
25239092Srnordier    }
25339092Srnordier    btx.btx_pgctl -= cwr;
25439125Srnordier    btx.btx_entry = Eflag ? centry : ihdr.entry;
255154711Sdelphij    if ((size_t)snprintf(name, sizeof(name), "%s.tmp", oname) >= sizeof(name))
25639092Srnordier	errx(2, "%s: Filename too long", oname);
25739092Srnordier    if ((fdo = open(name, O_CREAT | O_TRUNC | O_WRONLY, 0666)) == -1)
25839092Srnordier	err(2, "%s", name);
25939092Srnordier    if (!(tname = strdup(name)))
26039092Srnordier	err(2, NULL);
26139092Srnordier    puthdr(fdo, &ohdr);
26239092Srnordier    for (i = I_LDR; i <= I_CLNT; i++) {
26339092Srnordier	fname = i == I_LDR ? lname : i == I_BTX ? bname : iname;
26439092Srnordier	switch (i) {
26539092Srnordier	case I_LDR:
26639092Srnordier	    copy(fdi[i], fdo, ldr_size, 0);
26739092Srnordier	    seekx(fdo, ohdr.size += ohdr.text);
26839092Srnordier	    break;
26939092Srnordier	case I_BTX:
270112092Sru	    btxle = btx;
271130927Sobrien	    btxle.btx_pgctl = htole16(btxle.btx_pgctl);
272130927Sobrien	    btxle.btx_textsz = htole16(btxle.btx_textsz);
273130927Sobrien	    btxle.btx_entry = htole32(btxle.btx_entry);
274112092Sru	    writex(fdo, &btxle, sizeof(btxle));
27539092Srnordier	    copy(fdi[i], fdo, btx.btx_textsz - sizeof(btx),
27639092Srnordier		 sizeof(btx));
27739092Srnordier	    break;
27839092Srnordier	case I_CLNT:
27939092Srnordier	    copy(fdi[i], fdo, ihdr.size, 0);
28039092Srnordier	    if (ftruncate(fdo, ohdr.size += ohdr.data))
28139092Srnordier		err(2, "%s", tname);
28239092Srnordier	}
28339092Srnordier	if (close(fdi[i]))
28439092Srnordier	    err(2, "%s", fname);
28539092Srnordier    }
28639092Srnordier    if (close(fdo))
28739092Srnordier	err(2, "%s", tname);
28839092Srnordier    if (rename(tname, oname))
28939092Srnordier	err(2, "%s: Can't rename to %s", tname, oname);
29039092Srnordier    tname = NULL;
29139092Srnordier    if (verbose) {
29239092Srnordier	printf(binfo, btx.btx_majver, btx.btx_minver, btx.btx_textsz,
29339092Srnordier	       BTX_ORIGIN(btx), BTX_ENTRY(btx), BTX_MAPPED(btx) *
29439092Srnordier	       BTX_PGSIZE / 0x100000, !!(btx.btx_flags & BTX_MAPONE),
29539092Srnordier	       BTX_MAPPED(btx) - btx.btx_pgctl - BTX_PGBASE /
29639092Srnordier	       BTX_PGSIZE - BTX_MAPPED(btx) * 4 / BTX_PGSIZE);
29739092Srnordier	printf(cinfo, fmtlist[ihdr.fmt], ihdr.size, ihdr.text,
29839092Srnordier	       ihdr.data, ihdr.bss, ihdr.entry);
29939092Srnordier	printf(oinfo, fmtlist[ohdr.fmt], ohdr.size, ohdr.text,
30039092Srnordier	       ohdr.data, ohdr.org, ohdr.entry);
30139092Srnordier    }
30239092Srnordier}
30339092Srnordier
30439092Srnordier/*
30539092Srnordier * Read BTX file header.
30639092Srnordier */
30739092Srnordierstatic void
30839092Srnordiergetbtx(int fd, struct btx_hdr * btx)
30939092Srnordier{
31039092Srnordier    if (readx(fd, btx, sizeof(*btx), 0) != sizeof(*btx) ||
31139092Srnordier	btx->btx_magic[0] != BTX_MAG0 ||
31239092Srnordier	btx->btx_magic[1] != BTX_MAG1 ||
31339092Srnordier	btx->btx_magic[2] != BTX_MAG2)
31439092Srnordier	errx(1, "%s: Not a BTX kernel", fname);
315130927Sobrien    btx->btx_pgctl = le16toh(btx->btx_pgctl);
316130927Sobrien    btx->btx_textsz = le16toh(btx->btx_textsz);
317130927Sobrien    btx->btx_entry = le32toh(btx->btx_entry);
31839092Srnordier}
31939092Srnordier
32039092Srnordier/*
32139092Srnordier * Get file size and read a.out or ELF header.
32239092Srnordier */
32339092Srnordierstatic void
32439092Srnordiergethdr(int fd, struct hdr *hdr)
32539092Srnordier{
32639092Srnordier    struct stat sb;
327130927Sobrien    const struct exec *ex;
32839092Srnordier    const Elf32_Ehdr *ee;
32939092Srnordier    const Elf32_Phdr *ep;
33039092Srnordier    void *p;
33155416Smarcel    unsigned int fmt, x, n, i;
33239092Srnordier
33339092Srnordier    memset(hdr, 0, sizeof(*hdr));
33439092Srnordier    if (fstat(fd, &sb))
33539092Srnordier	err(2, "%s", fname);
33639092Srnordier    if (sb.st_size > MAXU32)
33739092Srnordier	errx(1, "%s: Too big", fname);
33839092Srnordier    hdr->size = sb.st_size;
339177933Sdfr    if (!hdr->size)
340177933Sdfr	return;
34139092Srnordier    if ((p = mmap(NULL, hdr->size, PROT_READ, MAP_SHARED, fd,
34239092Srnordier		  0)) == MAP_FAILED)
34339092Srnordier	err(2, "%s", fname);
34439092Srnordier    for (fmt = F_CNT - 1; !hdr->fmt && fmt; fmt--)
34539092Srnordier	switch (fmt) {
34639092Srnordier	case F_AOUT:
34739092Srnordier	    ex = p;
348130927Sobrien	    if (hdr->size >= sizeof(struct exec) && !N_BADMAG(*ex)) {
34939092Srnordier		hdr->fmt = fmt;
350130927Sobrien		x = N_GETMAGIC(*ex);
35139092Srnordier		if (x == OMAGIC || x == NMAGIC) {
35239092Srnordier		    if (x == NMAGIC)
35339092Srnordier			Warn(fname, "Treating %s NMAGIC as OMAGIC",
35439092Srnordier			     fmtlist[fmt]);
35539092Srnordier		    hdr->flags |= IMPURE;
35639092Srnordier		}
357130927Sobrien		hdr->text = le32toh(ex->a_text);
358130927Sobrien		hdr->data = le32toh(ex->a_data);
359130927Sobrien		hdr->bss = le32toh(ex->a_bss);
360130927Sobrien		hdr->entry = le32toh(ex->a_entry);
361130927Sobrien		if (le32toh(ex->a_entry) >= BTX_PGSIZE)
36239092Srnordier		    hdr->org = BTX_PGSIZE;
36339092Srnordier	    }
36439092Srnordier	    break;
36539092Srnordier	case F_ELF:
36639092Srnordier	    ee = p;
36739092Srnordier	    if (hdr->size >= sizeof(Elf32_Ehdr) && IS_ELF(*ee)) {
36839092Srnordier		hdr->fmt = fmt;
369130927Sobrien		for (n = i = 0; i < le16toh(ee->e_phnum); i++) {
370130927Sobrien		    ep = (void *)((uint8_t *)p + le32toh(ee->e_phoff) +
371130927Sobrien				  le16toh(ee->e_phentsize) * i);
372130927Sobrien		    if (le32toh(ep->p_type) == PT_LOAD)
37339092Srnordier			switch (n++) {
37439092Srnordier			case 0:
375130927Sobrien			    hdr->text = le32toh(ep->p_filesz);
376130927Sobrien			    hdr->org = le32toh(ep->p_paddr);
377130927Sobrien			    if (le32toh(ep->p_flags) & PF_W)
37839092Srnordier				hdr->flags |= IMPURE;
37939092Srnordier			    break;
38039092Srnordier			case 1:
381130927Sobrien			    hdr->data = le32toh(ep->p_filesz);
382130927Sobrien			    hdr->bss = le32toh(ep->p_memsz) -
383130927Sobrien				le32toh(ep->p_filesz);
38439092Srnordier			    break;
38539092Srnordier			case 2:
38639092Srnordier			    Warn(fname,
38739092Srnordier				 "Ignoring extra %s PT_LOAD segments",
38839092Srnordier				 fmtlist[fmt]);
38939092Srnordier			}
39039092Srnordier		}
391130927Sobrien		hdr->entry = le32toh(ee->e_entry);
39239092Srnordier	    }
39339092Srnordier	}
39439092Srnordier    if (munmap(p, hdr->size))
39539092Srnordier	err(2, "%s", fname);
39639092Srnordier}
39739092Srnordier
39839092Srnordier/*
39939092Srnordier * Write a.out or ELF header.
40039092Srnordier */
40139092Srnordierstatic void
40239092Srnordierputhdr(int fd, struct hdr *hdr)
40339092Srnordier{
404130927Sobrien    struct exec ex;
40539092Srnordier    struct elfh eh;
40639092Srnordier
40739092Srnordier    switch (hdr->fmt) {
40839092Srnordier    case F_AOUT:
40939092Srnordier	memset(&ex, 0, sizeof(ex));
410197051Simp	N_SETMAGIC(ex, ZMAGIC, MID_I386, 0);
411130927Sobrien	hdr->text = N_ALIGN(ex, hdr->text);
412130927Sobrien	ex.a_text = htole32(hdr->text);
413130927Sobrien	hdr->data = N_ALIGN(ex, hdr->data);
414130927Sobrien	ex.a_data = htole32(hdr->data);
415130927Sobrien	ex.a_entry = htole32(hdr->entry);
41639092Srnordier	writex(fd, &ex, sizeof(ex));
417130927Sobrien	hdr->size = N_ALIGN(ex, sizeof(ex));
41839092Srnordier	seekx(fd, hdr->size);
41939092Srnordier	break;
42039092Srnordier    case F_ELF:
42139092Srnordier	eh = elfhdr;
422130927Sobrien	eh.e.e_entry = htole32(hdr->entry);
423130927Sobrien	eh.p[0].p_vaddr = eh.p[0].p_paddr = htole32(hdr->org);
424130927Sobrien	eh.p[0].p_filesz = eh.p[0].p_memsz = htole32(hdr->text);
425130927Sobrien	eh.p[1].p_offset = htole32(le32toh(eh.p[0].p_offset) +
426130927Sobrien	    le32toh(eh.p[0].p_filesz));
427112092Sru	eh.p[1].p_vaddr = eh.p[1].p_paddr =
428130927Sobrien	    htole32(align(le32toh(eh.p[0].p_paddr) + le32toh(eh.p[0].p_memsz),
429263835Sbrueffer	    4096));
430130927Sobrien	eh.p[1].p_filesz = eh.p[1].p_memsz = htole32(hdr->data);
43139092Srnordier	eh.sh[2].sh_addr = eh.p[0].p_vaddr;
43239092Srnordier	eh.sh[2].sh_offset = eh.p[0].p_offset;
43339092Srnordier	eh.sh[2].sh_size = eh.p[0].p_filesz;
43439092Srnordier	eh.sh[3].sh_addr = eh.p[1].p_vaddr;
43539092Srnordier	eh.sh[3].sh_offset = eh.p[1].p_offset;
43639092Srnordier	eh.sh[3].sh_size = eh.p[1].p_filesz;
43739092Srnordier	writex(fd, &eh, sizeof(eh));
43839092Srnordier	hdr->size = sizeof(eh);
43939092Srnordier    }
44039092Srnordier}
44139092Srnordier
44239092Srnordier/*
44339092Srnordier * Safe copy from input file to output file.
44439092Srnordier */
44539092Srnordierstatic void
44639092Srnordiercopy(int fdi, int fdo, size_t nbyte, off_t offset)
44739092Srnordier{
44839092Srnordier    char buf[8192];
44939092Srnordier    size_t n;
45039092Srnordier
45139092Srnordier    while (nbyte) {
45239092Srnordier	if ((n = sizeof(buf)) > nbyte)
45339092Srnordier	    n = nbyte;
45439092Srnordier	if (readx(fdi, buf, n, offset) != n)
45539092Srnordier	    errx(2, "%s: Short read", fname);
45639092Srnordier	writex(fdo, buf, n);
45739092Srnordier	nbyte -= n;
45839092Srnordier	offset = -1;
45939092Srnordier    }
46039092Srnordier}
46139092Srnordier
46239092Srnordier/*
46339092Srnordier * Safe read from input file.
46439092Srnordier */
46539092Srnordierstatic size_t
46639092Srnordierreadx(int fd, void *buf, size_t nbyte, off_t offset)
46739092Srnordier{
46839092Srnordier    ssize_t n;
46939092Srnordier
47039092Srnordier    if (offset != -1 && lseek(fd, offset, SEEK_SET) != offset)
47139092Srnordier	err(2, "%s", fname);
47239092Srnordier    if ((n = read(fd, buf, nbyte)) == -1)
47339092Srnordier	err(2, "%s", fname);
47439092Srnordier    return n;
47539092Srnordier}
47639092Srnordier
47739092Srnordier/*
47839092Srnordier * Safe write to output file.
47939092Srnordier */
48039092Srnordierstatic void
48139092Srnordierwritex(int fd, const void *buf, size_t nbyte)
48239092Srnordier{
48339092Srnordier    ssize_t n;
48439092Srnordier
48539092Srnordier    if ((n = write(fd, buf, nbyte)) == -1)
48639092Srnordier	err(2, "%s", tname);
487154711Sdelphij    if ((size_t)n != nbyte)
48839092Srnordier	errx(2, "%s: Short write", tname);
48939092Srnordier}
49039092Srnordier
49139092Srnordier/*
49239092Srnordier * Safe seek in output file.
49339092Srnordier */
49439092Srnordierstatic void
49539092Srnordierseekx(int fd, off_t offset)
49639092Srnordier{
49739092Srnordier    if (lseek(fd, offset, SEEK_SET) != offset)
49839092Srnordier	err(2, "%s", tname);
49939092Srnordier}
50039092Srnordier
50139092Srnordier/*
50239092Srnordier * Convert an option argument to a format code.
50339092Srnordier */
50455416Smarcelstatic unsigned int
50539092Srnordieroptfmt(const char *arg)
50639092Srnordier{
50755416Smarcel    unsigned int i;
50839092Srnordier
50939092Srnordier    for (i = 0; i < F_CNT && strcmp(arg, fmtlist[i]); i++);
51039092Srnordier    if (i == F_CNT)
51139092Srnordier	errx(1, "%s: Unknown format", arg);
51239092Srnordier    return i;
51339092Srnordier}
51439092Srnordier
51539092Srnordier/*
51639092Srnordier * Convert an option argument to an address.
51739092Srnordier */
51839092Srnordierstatic uint32_t
51939092Srnordieroptaddr(const char *arg)
52039092Srnordier{
52139092Srnordier    char *s;
52239092Srnordier    unsigned long x;
52339092Srnordier
52439092Srnordier    errno = 0;
52539092Srnordier    x = strtoul(arg, &s, 0);
52639092Srnordier    if (errno || !*arg || *s || x > MAXU32)
52739092Srnordier	errx(1, "%s: Illegal address", arg);
52839092Srnordier    return x;
52939092Srnordier}
53039092Srnordier
53139092Srnordier/*
53239092Srnordier * Convert an option argument to a page number.
53339092Srnordier */
53439092Srnordierstatic int
53539092Srnordieroptpage(const char *arg, int hi)
53639092Srnordier{
53739092Srnordier    char *s;
53839092Srnordier    long x;
53939092Srnordier
54039092Srnordier    errno = 0;
54139092Srnordier    x = strtol(arg, &s, 0);
54239092Srnordier    if (errno || !*arg || *s || x < 0 || x > hi)
54339092Srnordier	errx(1, "%s: Illegal page number", arg);
54439092Srnordier    return x;
54539092Srnordier}
54639092Srnordier
54739092Srnordier/*
54839092Srnordier * Display a warning.
54939092Srnordier */
55039092Srnordierstatic void
55139092SrnordierWarn(const char *locus, const char *fmt, ...)
55239092Srnordier{
55339092Srnordier    va_list ap;
55439092Srnordier    char *s;
55539092Srnordier
55639092Srnordier    if (!quiet) {
55739092Srnordier	asprintf(&s, "%s: Warning: %s", locus, fmt);
55839092Srnordier	va_start(ap, fmt);
55939092Srnordier	vwarnx(s, ap);
56039092Srnordier	va_end(ap);
56139092Srnordier	free(s);
56239092Srnordier    }
56339092Srnordier}
56439092Srnordier
56539092Srnordier/*
56639092Srnordier * Display usage information.
56739092Srnordier */
56839092Srnordierstatic void
56939092Srnordierusage(void)
57039092Srnordier{
57139092Srnordier    fprintf(stderr, "%s\n%s\n",
57239092Srnordier    "usage: btxld [-qv] [-b file] [-E address] [-e address] [-f format]",
57339092Srnordier    "             [-l file] [-o filename] [-P page] [-W page] file");
57439092Srnordier    exit(1);
57539092Srnordier}
576