1157188Sache/*
221308Sache * Copyright (c) 1998 Robert Nordier
321308Sache * All rights reserved.
4157188Sache *
521308Sache * Redistribution and use in source and binary forms, with or without
621308Sache * modification, are permitted provided that the following conditions
721308Sache * are met:
821308Sache * 1. Redistributions of source code must retain the above copyright
921308Sache *    notice, this list of conditions and the following disclaimer.
1021308Sache * 2. Redistributions in binary form must reproduce the above copyright
1158314Sache *    notice, this list of conditions and the following disclaimer in the
1221308Sache *    documentation and/or other materials provided with the distribution.
1321308Sache *
1421308Sache * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND
1521308Sache * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1621308Sache * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1721308Sache * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
1821308Sache * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
1921308Sache * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
2021308Sache * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
2121308Sache * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
2258314Sache * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
23123279Sobrien * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
2421308Sache * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2521308Sache */
2621308Sache
2747563Sache#ifndef lint
2847563Sachestatic const char rcsid[] =
2947563Sache  "$FreeBSD$";
3047563Sache#endif /* not lint */
3121308Sache
3247563Sache#include <sys/param.h>
33119614Sache#include <sys/endian.h>
3421308Sache#include <sys/stat.h>
3521308Sache#include <sys/mman.h>
3621308Sache
3747563Sache/* XXX make this work as an i386/amd64 cross-tool */
38119614Sache#include <machine/exec.h>
3921308Sache#undef __LDPGSZ
4021308Sache#define __LDPGSZ	4096
4121308Sache
4221308Sache#include <netinet/in.h>
43119614Sache
44165675Sache#include <a.out.h>
45136652Sache#include <err.h>
46165675Sache#include <errno.h>
47119614Sache#include <fcntl.h>
4821308Sache#include <stdarg.h>
4921308Sache#include <stdio.h>
5021308Sache#include <stdlib.h>
5121308Sache#include <string.h>
5221308Sache#include <unistd.h>
5321308Sache
5421308Sache#include "btx.h"
5521308Sache#include "elfh.h"
5621308Sache
5721308Sache#define BTX_PATH		"/sys/boot/i386/btx"
5821308Sache
5921308Sache#define I_LDR	0		/* BTX loader */
6021308Sache#define I_BTX	1		/* BTX kernel */
6121308Sache#define I_CLNT	2		/* Client program */
6221308Sache
6321308Sache#define F_BIN	0		/* Binary */
6421308Sache#define F_AOUT	1		/* ZMAGIC a.out */
6521308Sache#define F_ELF	2		/* 32-bit ELF */
6621308Sache#define F_CNT	3		/* Number of formats */
6721308Sache
6821308Sache#define IMPURE	1		/* Writable text */
6921308Sache#define MAXU32	0xffffffff	/* Maximum unsigned 32-bit quantity */
7021308Sache
7175409Sache#define align(x, y) (((x) + (y) - 1) & ~((y) - 1))
7275409Sache
7321308Sachestruct hdr {
7421308Sache    uint32_t fmt;		/* Format */
7521308Sache    uint32_t flags;		/* Bit flags */
7621308Sache    uint32_t size;		/* Size of file */
7747563Sache    uint32_t text;		/* Size of text segment */
7847563Sache    uint32_t data;		/* Size of data segment */
7947563Sache    uint32_t bss;		/* Size of bss segment */
8047563Sache    uint32_t org;		/* Program origin */
8147563Sache    uint32_t entry;		/* Program entry point */
8221308Sache};
8347563Sache
84119614Sachestatic const char *const fmtlist[] = {"bin", "aout", "elf"};
85119614Sache
8621308Sachestatic const char binfo[] =
8747563Sache    "kernel: ver=%u.%02u size=%x load=%x entry=%x map=%uM "
88119614Sache    "pgctl=%x:%x\n";
89119614Sachestatic const char cinfo[] =
90119614Sache    "client: fmt=%s size=%x text=%x data=%x bss=%x entry=%x\n";
91119614Sachestatic const char oinfo[] =
92119614Sache    "output: fmt=%s size=%x text=%x data=%x org=%x entry=%x\n";
93119614Sache
94119614Sachestatic const char *lname =
95119614Sache    BTX_PATH "/btxldr/btxldr";	/* BTX loader */
96119614Sachestatic const char *bname =
97119614Sache    BTX_PATH "/btx/btx";	/* BTX kernel */
98119614Sachestatic const char *oname =
99119614Sache    "a.out";			/* Output filename */
100119614Sache
10121308Sachestatic int ppage = -1;		/* First page present */
10247563Sachestatic int wpage = -1;		/* First page writable */
103119614Sache
104119614Sachestatic unsigned int format; 	/* Output format */
105119614Sache
106119614Sachestatic uint32_t centry; 	/* Client entry address */
107119614Sachestatic uint32_t lentry; 	/* Loader entry address */
108119614Sache
109119614Sachestatic int Eflag;		/* Client entry option */
110119614Sache
111119614Sachestatic int quiet;		/* Inhibit warnings */
112119614Sachestatic int verbose;		/* Display information */
113119614Sache
11447563Sachestatic const char *tname;	/* Temporary output file */
11547563Sachestatic const char *fname;	/* Current input file */
116119614Sache
117119614Sachestatic void cleanup(void);
118119614Sachestatic void btxld(const char *);
11947563Sachestatic void getbtx(int, struct btx_hdr *);
12047563Sachestatic void gethdr(int, struct hdr *);
121119614Sachestatic void puthdr(int, struct hdr *);
122119614Sachestatic void copy(int, int, size_t, off_t);
12347563Sachestatic size_t readx(int, void *, size_t, off_t);
12447563Sachestatic void writex(int, const void *, size_t);
125119614Sachestatic void seekx(int, off_t);
126119614Sachestatic unsigned int optfmt(const char *);
12747563Sachestatic uint32_t optaddr(const char *);
12847563Sachestatic int optpage(const char *, int);
129119614Sachestatic void Warn(const char *, const char *, ...);
130119614Sachestatic void usage(void);
131119614Sache
132119614Sache/*
13347563Sache * A link editor for BTX clients.
13447563Sache */
135119614Sacheint
136119614Sachemain(int argc, char *argv[])
13747563Sache{
13847563Sache    int c;
139119614Sache
140119614Sache    while ((c = getopt(argc, argv, "qvb:E:e:f:l:o:P:W:")) != -1)
14147563Sache	switch (c) {
142119614Sache	case 'q':
143119614Sache	    quiet = 1;
144119614Sache	    break;
14547563Sache	case 'v':
146119614Sache	    verbose = 1;
147119614Sache	    break;
148119614Sache	case 'b':
149119614Sache	    bname = optarg;
15047563Sache	    break;
15147563Sache	case 'E':
152119614Sache	    centry = optaddr(optarg);
153119614Sache	    Eflag = 1;
154119614Sache	    break;
155119614Sache	case 'e':
15647563Sache	    lentry = optaddr(optarg);
15747563Sache	    break;
158119614Sache	case 'f':
159119614Sache	    format = optfmt(optarg);
160119614Sache	    break;
161119614Sache	case 'l':
162119614Sache	    lname = optarg;
163119614Sache	    break;
164136652Sache	case 'o':
165119614Sache	    oname = optarg;
166119614Sache	    break;
167119614Sache	case 'P':
168119614Sache	    ppage = optpage(optarg, 1);
169119614Sache	    break;
170119614Sache	case 'W':
171119614Sache	    wpage = optpage(optarg, BTX_MAXCWR);
172119614Sache	    break;
173119614Sache	default:
17475409Sache	    usage();
17575409Sache	}
176119614Sache    argc -= optind;
17735493Sache    argv += optind;
17835493Sache    if (argc != 1)
17947563Sache	usage();
180119614Sache    atexit(cleanup);
181119614Sache    btxld(*argv);
18221308Sache    return 0;
18347563Sache}
184119614Sache
185119614Sache/*
186119614Sache * Clean up after errors.
18721308Sache */
18847563Sachestatic void
189119614Sachecleanup(void)
190119614Sache{
19147563Sache    if (tname)
19247563Sache	remove(tname);
193119614Sache}
19447563Sache
19547563Sache/*
196119614Sache * Read the input files; write the output file; display information.
197119614Sache */
19847563Sachestatic void
19947563Sachebtxld(const char *iname)
200119614Sache{
201119614Sache    char name[FILENAME_MAX];
20247563Sache    struct btx_hdr btx, btxle;
20347563Sache    struct hdr ihdr, ohdr;
204119614Sache    unsigned int ldr_size, cwr;
205119614Sache    int fdi[3], fdo, i;
206119614Sache
207119614Sache    ldr_size = 0;
208119614Sache
209119614Sache    for (i = I_LDR; i <= I_CLNT; i++) {
21047563Sache	fname = i == I_LDR ? lname : i == I_BTX ? bname : iname;
21158314Sache	if ((fdi[i] = open(fname, O_RDONLY)) == -1)
212119614Sache	    err(2, "%s", fname);
21347563Sache	switch (i) {
21447563Sache	case I_LDR:
215119614Sache	    gethdr(fdi[i], &ihdr);
216119614Sache	    if (ihdr.fmt != F_BIN)
217119614Sache		Warn(fname, "Loader format is %s; processing as %s",
21847563Sache		     fmtlist[ihdr.fmt], fmtlist[F_BIN]);
21921308Sache	    ldr_size = ihdr.size;
22047563Sache	    break;
221119614Sache	case I_BTX:
222119614Sache	    getbtx(fdi[i], &btx);
223119614Sache	    break;
224119614Sache	case I_CLNT:
225119614Sache	    gethdr(fdi[i], &ihdr);
226119614Sache	    if (ihdr.org && ihdr.org != BTX_PGSIZE)
227119614Sache		Warn(fname,
228119614Sache		     "Client origin is 0x%x; expecting 0 or 0x%x",
229119614Sache		     ihdr.org, BTX_PGSIZE);
230119614Sache	}
231119614Sache    }
232119614Sache    memset(&ohdr, 0, sizeof(ohdr));
233119614Sache    ohdr.fmt = format;
234119614Sache    ohdr.text = ldr_size;
235119614Sache    ohdr.data = btx.btx_textsz + ihdr.size;
236119614Sache    ohdr.org = lentry;
237119614Sache    ohdr.entry = lentry;
238119614Sache    cwr = 0;
239119614Sache    if (wpage > 0 || (wpage == -1 && !(ihdr.flags & IMPURE))) {
240119614Sache	if (wpage > 0)
241119614Sache	    cwr = wpage;
242119614Sache	else {
243119614Sache	    cwr = howmany(ihdr.text, BTX_PGSIZE);
244119614Sache	    if (cwr > BTX_MAXCWR)
245157188Sache		cwr = BTX_MAXCWR;
246119614Sache	}
247119614Sache    }
248119614Sache    if (ppage > 0 || (ppage && wpage && ihdr.org >= BTX_PGSIZE)) {
249119614Sache	btx.btx_flags |= BTX_MAPONE;
250119614Sache	if (!cwr)
251119614Sache	    cwr++;
252119614Sache    }
253119614Sache    btx.btx_pgctl -= cwr;
254119614Sache    btx.btx_entry = Eflag ? centry : ihdr.entry;
255119614Sache    if ((size_t)snprintf(name, sizeof(name), "%s.tmp", oname) >= sizeof(name))
256119614Sache	errx(2, "%s: Filename too long", oname);
257119614Sache    if ((fdo = open(name, O_CREAT | O_TRUNC | O_WRONLY, 0666)) == -1)
25821308Sache	err(2, "%s", name);
25947563Sache    if (!(tname = strdup(name)))
260119614Sache	err(2, NULL);
261119614Sache    puthdr(fdo, &ohdr);
262119614Sache    for (i = I_LDR; i <= I_CLNT; i++) {
26321308Sache	fname = i == I_LDR ? lname : i == I_BTX ? bname : iname;
264136652Sache	switch (i) {
265136652Sache	case I_LDR:
26647563Sache	    copy(fdi[i], fdo, ldr_size, 0);
267119614Sache	    seekx(fdo, ohdr.size += ohdr.text);
268119614Sache	    break;
269119614Sache	case I_BTX:
270119614Sache	    btxle = btx;
271119614Sache	    btxle.btx_pgctl = htole16(btxle.btx_pgctl);
272119614Sache	    btxle.btx_textsz = htole16(btxle.btx_textsz);
27321308Sache	    btxle.btx_entry = htole32(btxle.btx_entry);
27421308Sache	    writex(fdo, &btxle, sizeof(btxle));
27521308Sache	    copy(fdi[i], fdo, btx.btx_textsz - sizeof(btx),
27621308Sache		 sizeof(btx));
27721308Sache	    break;
27821308Sache	case I_CLNT:
27921308Sache	    copy(fdi[i], fdo, ihdr.size, 0);
28021308Sache	    if (ftruncate(fdo, ohdr.size += ohdr.data))
28121308Sache		err(2, "%s", tname);
282119614Sache	}
28321308Sache	if (close(fdi[i]))
284119614Sache	    err(2, "%s", fname);
285119614Sache    }
28675409Sache    if (close(fdo))
287119614Sache	err(2, "%s", tname);
28821308Sache    if (rename(tname, oname))
28975409Sache	err(2, "%s: Can't rename to %s", tname, oname);
290119614Sache    tname = NULL;
29121308Sache    if (verbose) {
29247563Sache	printf(binfo, btx.btx_majver, btx.btx_minver, btx.btx_textsz,
293119614Sache	       BTX_ORIGIN(btx), BTX_ENTRY(btx), BTX_MAPPED(btx) *
294119614Sache	       BTX_PGSIZE / 0x100000, !!(btx.btx_flags & BTX_MAPONE),
295119614Sache	       BTX_MAPPED(btx) - btx.btx_pgctl - BTX_PGBASE /
296119614Sache	       BTX_PGSIZE - BTX_MAPPED(btx) * 4 / BTX_PGSIZE);
297119614Sache	printf(cinfo, fmtlist[ihdr.fmt], ihdr.size, ihdr.text,
298136652Sache	       ihdr.data, ihdr.bss, ihdr.entry);
299136652Sache	printf(oinfo, fmtlist[ohdr.fmt], ohdr.size, ohdr.text,
300119614Sache	       ohdr.data, ohdr.org, ohdr.entry);
301119614Sache    }
302136652Sache}
303136652Sache
304136652Sache/*
305136652Sache * Read BTX file header.
306119614Sache */
307157188Sachestatic void
308157188Sachegetbtx(int fd, struct btx_hdr * btx)
309119614Sache{
31047563Sache    if (readx(fd, btx, sizeof(*btx), 0) != sizeof(*btx) ||
311136652Sache	btx->btx_magic[0] != BTX_MAG0 ||
312136652Sache	btx->btx_magic[1] != BTX_MAG1 ||
313136652Sache	btx->btx_magic[2] != BTX_MAG2)
31421308Sache	errx(1, "%s: Not a BTX kernel", fname);
315119614Sache    btx->btx_pgctl = le16toh(btx->btx_pgctl);
31621308Sache    btx->btx_textsz = le16toh(btx->btx_textsz);
31747563Sache    btx->btx_entry = le32toh(btx->btx_entry);
318119614Sache}
319119614Sache
32021308Sache/*
321119614Sache * Get file size and read a.out or ELF header.
322119614Sache */
32321308Sachestatic void
324119614Sachegethdr(int fd, struct hdr *hdr)
325119614Sache{
326119614Sache    struct stat sb;
32747563Sache    const struct exec *ex;
328119614Sache    const Elf32_Ehdr *ee;
329119614Sache    const Elf32_Phdr *ep;
330119614Sache    void *p;
33121308Sache    unsigned int fmt, x, n, i;
332119614Sache
333119614Sache    memset(hdr, 0, sizeof(*hdr));
33421308Sache    if (fstat(fd, &sb))
33547563Sache	err(2, "%s", fname);
336119614Sache    if (sb.st_size > MAXU32)
337119614Sache	errx(1, "%s: Too big", fname);
338119614Sache    hdr->size = sb.st_size;
339119614Sache    if (!hdr->size)
340119614Sache	return;
341119614Sache    if ((p = mmap(NULL, hdr->size, PROT_READ, MAP_SHARED, fd,
342119614Sache		  0)) == MAP_FAILED)
343119614Sache	err(2, "%s", fname);
344119614Sache    for (fmt = F_CNT - 1; !hdr->fmt && fmt; fmt--)
34575409Sache	switch (fmt) {
346119614Sache	case F_AOUT:
347119614Sache	    ex = p;
34847563Sache	    if (hdr->size >= sizeof(struct exec) && !N_BADMAG(*ex)) {
34947563Sache		hdr->fmt = fmt;
350119614Sache		x = N_GETMAGIC(*ex);
351119614Sache		if (x == OMAGIC || x == NMAGIC) {
35275409Sache		    if (x == NMAGIC)
35375409Sache			Warn(fname, "Treating %s NMAGIC as OMAGIC",
354119614Sache			     fmtlist[fmt]);
35547563Sache		    hdr->flags |= IMPURE;
35647563Sache		}
357119614Sache		hdr->text = le32toh(ex->a_text);
35847563Sache		hdr->data = le32toh(ex->a_data);
35947563Sache		hdr->bss = le32toh(ex->a_bss);
360119614Sache		hdr->entry = le32toh(ex->a_entry);
361119614Sache		if (le32toh(ex->a_entry) >= BTX_PGSIZE)
362119614Sache		    hdr->org = BTX_PGSIZE;
363119614Sache	    }
364119614Sache	    break;
365119614Sache	case F_ELF:
36647563Sache	    ee = p;
36721308Sache	    if (hdr->size >= sizeof(Elf32_Ehdr) && IS_ELF(*ee)) {
368119614Sache		hdr->fmt = fmt;
369119614Sache		for (n = i = 0; i < le16toh(ee->e_phnum); i++) {
370119614Sache		    ep = (void *)((uint8_t *)p + le32toh(ee->e_phoff) +
371119614Sache				  le16toh(ee->e_phentsize) * i);
372119614Sache		    if (le32toh(ep->p_type) == PT_LOAD)
373119614Sache			switch (n++) {
374119614Sache			case 0:
37521308Sache			    hdr->text = le32toh(ep->p_filesz);
376136652Sache			    hdr->org = le32toh(ep->p_paddr);
377119614Sache			    if (le32toh(ep->p_flags) & PF_W)
37821308Sache				hdr->flags |= IMPURE;
37921308Sache			    break;
38021308Sache			case 1:
38121308Sache			    hdr->data = le32toh(ep->p_filesz);
382119614Sache			    hdr->bss = le32toh(ep->p_memsz) -
38375409Sache				le32toh(ep->p_filesz);
38421308Sache			    break;
385119614Sache			case 2:
38621308Sache			    Warn(fname,
38747563Sache				 "Ignoring extra %s PT_LOAD segments",
388119614Sache				 fmtlist[fmt]);
389119614Sache			}
39047563Sache		}
39121308Sache		hdr->entry = le32toh(ee->e_entry);
392119614Sache	    }
393119614Sache	}
394119614Sache    if (munmap(p, hdr->size))
395119614Sache	err(2, "%s", fname);
396119614Sache}
39721308Sache
39847563Sache/*
399119614Sache * Write a.out or ELF header.
400119614Sache */
401119614Sachestatic void
402136652Sacheputhdr(int fd, struct hdr *hdr)
40321308Sache{
404119614Sache    struct exec ex;
405119614Sache    struct elfh eh;
406119614Sache
407119614Sache    switch (hdr->fmt) {
408157188Sache    case F_AOUT:
40921308Sache	memset(&ex, 0, sizeof(ex));
410119614Sache	N_SETMAGIC(ex, ZMAGIC, MID_I386, 0);
411119614Sache	hdr->text = N_ALIGN(ex, hdr->text);
41247563Sache	ex.a_text = htole32(hdr->text);
413119614Sache	hdr->data = N_ALIGN(ex, hdr->data);
414119614Sache	ex.a_data = htole32(hdr->data);
415119614Sache	ex.a_entry = htole32(hdr->entry);
416119614Sache	writex(fd, &ex, sizeof(ex));
417119614Sache	hdr->size = N_ALIGN(ex, sizeof(ex));
418119614Sache	seekx(fd, hdr->size);
41947563Sache	break;
42075409Sache    case F_ELF:
421119614Sache	eh = elfhdr;
422119614Sache	eh.e.e_entry = htole32(hdr->entry);
423119614Sache	eh.p[0].p_vaddr = eh.p[0].p_paddr = htole32(hdr->org);
42475409Sache	eh.p[0].p_filesz = eh.p[0].p_memsz = htole32(hdr->text);
42547563Sache	eh.p[1].p_offset = htole32(le32toh(eh.p[0].p_offset) +
426119614Sache	    le32toh(eh.p[0].p_filesz));
427119614Sache	eh.p[1].p_vaddr = eh.p[1].p_paddr =
428119614Sache	    htole32(align(le32toh(eh.p[0].p_paddr) + le32toh(eh.p[0].p_memsz),
429119614Sache	    4096));
430119614Sache	eh.p[1].p_filesz = eh.p[1].p_memsz = htole32(hdr->data);
43147563Sache	eh.sh[2].sh_addr = eh.p[0].p_vaddr;
432119614Sache	eh.sh[2].sh_offset = eh.p[0].p_offset;
43321308Sache	eh.sh[2].sh_size = eh.p[0].p_filesz;
43475409Sache	eh.sh[3].sh_addr = eh.p[1].p_vaddr;
435119614Sache	eh.sh[3].sh_offset = eh.p[1].p_offset;
436119614Sache	eh.sh[3].sh_size = eh.p[1].p_filesz;
437119614Sache	writex(fd, &eh, sizeof(eh));
43847563Sache	hdr->size = sizeof(eh);
43921308Sache    }
440119614Sache}
441119614Sache
44221308Sache/*
443119614Sache * Safe copy from input file to output file.
444119614Sache */
445119614Sachestatic void
44621308Sachecopy(int fdi, int fdo, size_t nbyte, off_t offset)
447119614Sache{
448119614Sache    char buf[8192];
449124575Sobrien    size_t n;
45075409Sache
451119614Sache    while (nbyte) {
452119614Sache	if ((n = sizeof(buf)) > nbyte)
453119614Sache	    n = nbyte;
454119614Sache	if (readx(fdi, buf, n, offset) != n)
45575409Sache	    errx(2, "%s: Short read", fname);
456119614Sache	writex(fdo, buf, n);
457119614Sache	nbyte -= n;
458119614Sache	offset = -1;
45975409Sache    }
460119614Sache}
461119614Sache
462119614Sache/*
463119614Sache * Safe read from input file.
46475409Sache */
46521308Sachestatic size_t
46621308Sachereadx(int fd, void *buf, size_t nbyte, off_t offset)
46721308Sache{
46821308Sache    ssize_t n;
46921308Sache
47021308Sache    if (offset != -1 && lseek(fd, offset, SEEK_SET) != offset)
47121308Sache	err(2, "%s", fname);
472119614Sache    if ((n = read(fd, buf, nbyte)) == -1)
473119614Sache	err(2, "%s", fname);
47421308Sache    return n;
47558314Sache}
47658314Sache
47758314Sache/*
47875409Sache * Safe write to output file.
47975409Sache */
48075409Sachestatic void
48175409Sachewritex(int fd, const void *buf, size_t nbyte)
48275409Sache{
48375409Sache    ssize_t n;
48475409Sache
485119614Sache    if ((n = write(fd, buf, nbyte)) == -1)
486119614Sache	err(2, "%s", tname);
487119614Sache    if ((size_t)n != nbyte)
488119614Sache	errx(2, "%s: Short write", tname);
48921308Sache}
49021308Sache
49175409Sache/*
49221308Sache * Safe seek in output file.
49326500Sache */
49426500Sachestatic void
49526500Sacheseekx(int fd, off_t offset)
49626500Sache{
49721308Sache    if (lseek(fd, offset, SEEK_SET) != offset)
49821308Sache	err(2, "%s", tname);
49921308Sache}
50021308Sache
50175409Sache/*
50275409Sache * Convert an option argument to a format code.
50321308Sache */
50447563Sachestatic unsigned int
50521308Sacheoptfmt(const char *arg)
50621308Sache{
50747563Sache    unsigned int i;
50847563Sache
50921308Sache    for (i = 0; i < F_CNT && strcmp(arg, fmtlist[i]); i++);
51021308Sache    if (i == F_CNT)
51147563Sache	errx(1, "%s: Unknown format", arg);
51221308Sache    return i;
51321308Sache}
51426500Sache
51526500Sache/*
51626500Sache * Convert an option argument to an address.
51730974Sache */
51826500Sachestatic uint32_t
51975409Sacheoptaddr(const char *arg)
52075409Sache{
52175409Sache    char *s;
52275409Sache    unsigned long x;
52375409Sache
52475409Sache    errno = 0;
52575409Sache    x = strtoul(arg, &s, 0);
52675409Sache    if (errno || !*arg || *s || x > MAXU32)
52775409Sache	errx(1, "%s: Illegal address", arg);
52875409Sache    return x;
52921308Sache}
53075409Sache
53121308Sache/*
53221308Sache * Convert an option argument to a page number.
53375409Sache */
53475409Sachestatic int
53521308Sacheoptpage(const char *arg, int hi)
536157188Sache{
537157188Sache    char *s;
538157188Sache    long x;
539157188Sache
540157188Sache    errno = 0;
54121308Sache    x = strtol(arg, &s, 0);
54221308Sache    if (errno || !*arg || *s || x < 0 || x > hi)
54375409Sache	errx(1, "%s: Illegal page number", arg);
54421308Sache    return x;
54547563Sache}
54647563Sache
54747563Sache/*
54875409Sache * Display a warning.
54947563Sache */
55021308Sachestatic void
55121308SacheWarn(const char *locus, const char *fmt, ...)
55275409Sache{
55321308Sache    va_list ap;
55475409Sache    char *s;
55575409Sache
55675409Sache    if (!quiet) {
55721308Sache	asprintf(&s, "%s: Warning: %s", locus, fmt);
55875409Sache	va_start(ap, fmt);
55975409Sache	vwarnx(s, ap);
56075409Sache	va_end(ap);
56175409Sache	free(s);
56275409Sache    }
56321308Sache}
56421308Sache
56521308Sache/*
56621308Sache * Display usage information.
56747563Sache */
56847563Sachestatic void
56947563Sacheusage(void)
57047563Sache{
57147563Sache    fprintf(stderr, "%s\n%s\n",
57247563Sache    "usage: btxld [-qv] [-b file] [-E address] [-e address] [-f format]",
57358314Sache    "             [-l file] [-o filename] [-P page] [-W page] file");
57458314Sache    exit(1);
57558314Sache}
57658314Sache