xref: /illumos-gate/usr/src/tools/btxld/btxld.c (revision b59f980d)
10cc5983cSToomas Soome /*
20cc5983cSToomas Soome  * Copyright (c) 1998 Robert Nordier
30cc5983cSToomas Soome  * All rights reserved.
40cc5983cSToomas Soome  *
50cc5983cSToomas Soome  * Redistribution and use in source and binary forms, with or without
60cc5983cSToomas Soome  * modification, are permitted provided that the following conditions
70cc5983cSToomas Soome  * are met:
80cc5983cSToomas Soome  * 1. Redistributions of source code must retain the above copyright
90cc5983cSToomas Soome  *    notice, this list of conditions and the following disclaimer.
100cc5983cSToomas Soome  * 2. Redistributions in binary form must reproduce the above copyright
110cc5983cSToomas Soome  *    notice, this list of conditions and the following disclaimer in the
120cc5983cSToomas Soome  *    documentation and/or other materials provided with the distribution.
130cc5983cSToomas Soome  *
140cc5983cSToomas Soome  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND
150cc5983cSToomas Soome  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
160cc5983cSToomas Soome  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
170cc5983cSToomas Soome  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
180cc5983cSToomas Soome  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
190cc5983cSToomas Soome  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
200cc5983cSToomas Soome  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
210cc5983cSToomas Soome  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
220cc5983cSToomas Soome  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
230cc5983cSToomas Soome  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
240cc5983cSToomas Soome  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
250cc5983cSToomas Soome  */
260cc5983cSToomas Soome 
270cc5983cSToomas Soome #include <sys/param.h>
280cc5983cSToomas Soome #include "endian.h"
290cc5983cSToomas Soome #include <sys/stat.h>
300cc5983cSToomas Soome #include <sys/mman.h>
310cc5983cSToomas Soome 
320cc5983cSToomas Soome /* XXX make this work as an i386/amd64 cross-tool */
330cc5983cSToomas Soome #undef __LDPGSZ
340cc5983cSToomas Soome #define __LDPGSZ	4096
350cc5983cSToomas Soome 
360cc5983cSToomas Soome #include <netinet/in.h>
370cc5983cSToomas Soome 
380cc5983cSToomas Soome #include "imgact_aout.h"
390cc5983cSToomas Soome #include <err.h>
400cc5983cSToomas Soome #include <errno.h>
410cc5983cSToomas Soome #include <fcntl.h>
420cc5983cSToomas Soome #include <stdarg.h>
43*b59f980dSToomas Soome #include <stdint.h>
440cc5983cSToomas Soome #include <stdio.h>
450cc5983cSToomas Soome #include <stdlib.h>
460cc5983cSToomas Soome #include <string.h>
470cc5983cSToomas Soome #include <unistd.h>
480cc5983cSToomas Soome 
490cc5983cSToomas Soome #include "btx.h"
500cc5983cSToomas Soome #include "elfh.h"
510cc5983cSToomas Soome 
520cc5983cSToomas Soome #define BTX_PATH		"/sys/boot/i386/btx"
530cc5983cSToomas Soome 
540cc5983cSToomas Soome #define I_LDR	0		/* BTX loader */
550cc5983cSToomas Soome #define I_BTX	1		/* BTX kernel */
560cc5983cSToomas Soome #define I_CLNT	2		/* Client program */
570cc5983cSToomas Soome 
580cc5983cSToomas Soome #define F_BIN	0		/* Binary */
590cc5983cSToomas Soome #define F_AOUT	1		/* ZMAGIC a.out */
600cc5983cSToomas Soome #define F_ELF	2		/* 32-bit ELF */
610cc5983cSToomas Soome #define F_CNT	3		/* Number of formats */
620cc5983cSToomas Soome 
630cc5983cSToomas Soome #define IMPURE	1		/* Writable text */
640cc5983cSToomas Soome 
650cc5983cSToomas Soome #define align(x, y) (((x) + (y) - 1) & ~((y) - 1))
660cc5983cSToomas Soome 
670cc5983cSToomas Soome struct hdr {
680cc5983cSToomas Soome     uint32_t fmt;		/* Format */
690cc5983cSToomas Soome     uint32_t flags;		/* Bit flags */
700cc5983cSToomas Soome     uint32_t size;		/* Size of file */
710cc5983cSToomas Soome     uint32_t text;		/* Size of text segment */
720cc5983cSToomas Soome     uint32_t data;		/* Size of data segment */
730cc5983cSToomas Soome     uint32_t bss;		/* Size of bss segment */
740cc5983cSToomas Soome     uint32_t org;		/* Program origin */
750cc5983cSToomas Soome     uint32_t entry;		/* Program entry point */
760cc5983cSToomas Soome };
770cc5983cSToomas Soome 
780cc5983cSToomas Soome static const char *const fmtlist[] = {"bin", "aout", "elf"};
790cc5983cSToomas Soome 
800cc5983cSToomas Soome static const char binfo[] =
810cc5983cSToomas Soome     "kernel: ver=%u.%02u size=%x load=%x entry=%x map=%uM "
820cc5983cSToomas Soome     "pgctl=%x:%x\n";
830cc5983cSToomas Soome static const char cinfo[] =
840cc5983cSToomas Soome     "client: fmt=%s size=%x text=%x data=%x bss=%x entry=%x\n";
850cc5983cSToomas Soome static const char oinfo[] =
860cc5983cSToomas Soome     "output: fmt=%s size=%x text=%x data=%x org=%x entry=%x\n";
870cc5983cSToomas Soome 
88b960a270SToomas Soome /* BTX loader and kernel is only provided from command line */
89b960a270SToomas Soome static const char *lname = NULL;
90b960a270SToomas Soome static const char *bname = NULL;
910cc5983cSToomas Soome static const char *oname =
920cc5983cSToomas Soome     "a.out";			/* Output filename */
930cc5983cSToomas Soome 
940cc5983cSToomas Soome static int ppage = -1;		/* First page present */
950cc5983cSToomas Soome static int wpage = -1;		/* First page writable */
960cc5983cSToomas Soome 
97*b59f980dSToomas Soome static unsigned int format;	/* Output format */
980cc5983cSToomas Soome 
99*b59f980dSToomas Soome static uint32_t centry;		/* Client entry address */
100*b59f980dSToomas Soome static uint32_t lentry;		/* Loader entry address */
1010cc5983cSToomas Soome 
1020cc5983cSToomas Soome static int Eflag;		/* Client entry option */
1030cc5983cSToomas Soome 
1040cc5983cSToomas Soome static int quiet;		/* Inhibit warnings */
1050cc5983cSToomas Soome static int verbose;		/* Display information */
1060cc5983cSToomas Soome 
1070cc5983cSToomas Soome static const char *tname;	/* Temporary output file */
1080cc5983cSToomas Soome static const char *fname;	/* Current input file */
1090cc5983cSToomas Soome 
1100cc5983cSToomas Soome static void cleanup(void);
1110cc5983cSToomas Soome static void btxld(const char *);
1120cc5983cSToomas Soome static void getbtx(int, struct btx_hdr *);
1130cc5983cSToomas Soome static void gethdr(int, struct hdr *);
1140cc5983cSToomas Soome static void puthdr(int, struct hdr *);
1150cc5983cSToomas Soome static void copy(int, int, size_t, off_t);
1160cc5983cSToomas Soome static size_t readx(int, void *, size_t, off_t);
1170cc5983cSToomas Soome static void writex(int, const void *, size_t);
1180cc5983cSToomas Soome static void seekx(int, off_t);
1190cc5983cSToomas Soome static unsigned int optfmt(const char *);
1200cc5983cSToomas Soome static uint32_t optaddr(const char *);
1210cc5983cSToomas Soome static int optpage(const char *, int);
1220cc5983cSToomas Soome static void Warn(const char *, const char *, ...);
1230cc5983cSToomas Soome static void usage(void);
124*b59f980dSToomas Soome extern void add_version(const char *, const char *, char *);
1250cc5983cSToomas Soome 
1260cc5983cSToomas Soome /*
1270cc5983cSToomas Soome  * A link editor for BTX clients.
1280cc5983cSToomas Soome  */
1290cc5983cSToomas Soome int
main(int argc,char * argv[])1300cc5983cSToomas Soome main(int argc, char *argv[])
1310cc5983cSToomas Soome {
1320cc5983cSToomas Soome     int c;
1330cc5983cSToomas Soome     char *version = NULL;
1340cc5983cSToomas Soome 
1350cc5983cSToomas Soome     while ((c = getopt(argc, argv, "qvb:E:e:f:l:o:P:V:W:")) != -1)
1360cc5983cSToomas Soome 	switch (c) {
1370cc5983cSToomas Soome 	case 'q':
1380cc5983cSToomas Soome 	    quiet = 1;
1390cc5983cSToomas Soome 	    break;
1400cc5983cSToomas Soome 	case 'v':
1410cc5983cSToomas Soome 	    verbose = 1;
1420cc5983cSToomas Soome 	    break;
1430cc5983cSToomas Soome 	case 'b':
1440cc5983cSToomas Soome 	    bname = optarg;
1450cc5983cSToomas Soome 	    break;
1460cc5983cSToomas Soome 	case 'E':
1470cc5983cSToomas Soome 	    centry = optaddr(optarg);
1480cc5983cSToomas Soome 	    Eflag = 1;
1490cc5983cSToomas Soome 	    break;
1500cc5983cSToomas Soome 	case 'e':
1510cc5983cSToomas Soome 	    lentry = optaddr(optarg);
1520cc5983cSToomas Soome 	    break;
1530cc5983cSToomas Soome 	case 'f':
1540cc5983cSToomas Soome 	    format = optfmt(optarg);
1550cc5983cSToomas Soome 	    break;
1560cc5983cSToomas Soome 	case 'l':
1570cc5983cSToomas Soome 	    lname = optarg;
1580cc5983cSToomas Soome 	    break;
1590cc5983cSToomas Soome 	case 'o':
1600cc5983cSToomas Soome 	    oname = optarg;
1610cc5983cSToomas Soome 	    break;
1620cc5983cSToomas Soome 	case 'P':
1630cc5983cSToomas Soome 	    ppage = optpage(optarg, 1);
1640cc5983cSToomas Soome 	    break;
1650cc5983cSToomas Soome 	case 'V':
1660cc5983cSToomas Soome 	    version = optarg;
1670cc5983cSToomas Soome 	    break;
1680cc5983cSToomas Soome 	case 'W':
1690cc5983cSToomas Soome 	    wpage = optpage(optarg, BTX_MAXCWR);
1700cc5983cSToomas Soome 	    break;
1710cc5983cSToomas Soome 	default:
1720cc5983cSToomas Soome 	    usage();
1730cc5983cSToomas Soome 	}
1740cc5983cSToomas Soome     argc -= optind;
1750cc5983cSToomas Soome     argv += optind;
1760cc5983cSToomas Soome     if (argc != 1)
1770cc5983cSToomas Soome 	usage();
1780cc5983cSToomas Soome     atexit(cleanup);
179b960a270SToomas Soome     if (lname != NULL && bname != NULL)
180b960a270SToomas Soome 	btxld(*argv);
181*b59f980dSToomas Soome 
182*b59f980dSToomas Soome     if (version != NULL) {
183*b59f980dSToomas Soome 	if (tname != NULL) {
184*b59f980dSToomas Soome 		add_version(tname, oname, version);
185*b59f980dSToomas Soome 		cleanup();
186*b59f980dSToomas Soome 	} else {
187*b59f980dSToomas Soome 		add_version(*argv, oname, version);
188*b59f980dSToomas Soome 	}
189*b59f980dSToomas Soome     } else {
190*b59f980dSToomas Soome 	if (rename(tname, oname))
191*b59f980dSToomas Soome 		err(2, "%s: Can't rename to %s", tname, oname);
192*b59f980dSToomas Soome 	free((void *)(intptr_t)tname);
193*b59f980dSToomas Soome 	tname = NULL;
194*b59f980dSToomas Soome     }
1950cc5983cSToomas Soome     return 0;
1960cc5983cSToomas Soome }
1970cc5983cSToomas Soome 
1980cc5983cSToomas Soome /*
1990cc5983cSToomas Soome  * Clean up after errors.
2000cc5983cSToomas Soome  */
2010cc5983cSToomas Soome static void
cleanup(void)2020cc5983cSToomas Soome cleanup(void)
2030cc5983cSToomas Soome {
204*b59f980dSToomas Soome 	if (tname) {
205*b59f980dSToomas Soome 		(void) remove(tname);
206*b59f980dSToomas Soome 		free((void *)(intptr_t)tname);
207*b59f980dSToomas Soome 		tname = NULL;
208*b59f980dSToomas Soome 	}
2090cc5983cSToomas Soome }
2100cc5983cSToomas Soome 
2110cc5983cSToomas Soome /*
2120cc5983cSToomas Soome  * Read the input files; write the output file; display information.
2130cc5983cSToomas Soome  */
2140cc5983cSToomas Soome static void
btxld(const char * iname)2150cc5983cSToomas Soome btxld(const char *iname)
2160cc5983cSToomas Soome {
2170cc5983cSToomas Soome     char name[FILENAME_MAX];
2180cc5983cSToomas Soome     struct btx_hdr btx, btxle;
2190cc5983cSToomas Soome     struct hdr ihdr, ohdr;
2200cc5983cSToomas Soome     unsigned int ldr_size, cwr;
2210cc5983cSToomas Soome     int fdi[3], fdo, i;
2220cc5983cSToomas Soome 
2230cc5983cSToomas Soome     ldr_size = 0;
2240cc5983cSToomas Soome 
2250cc5983cSToomas Soome     for (i = I_LDR; i <= I_CLNT; i++) {
2260cc5983cSToomas Soome 	fname = i == I_LDR ? lname : i == I_BTX ? bname : iname;
2270cc5983cSToomas Soome 	if ((fdi[i] = open(fname, O_RDONLY)) == -1)
2280cc5983cSToomas Soome 	    err(2, "%s", fname);
2290cc5983cSToomas Soome 	switch (i) {
2300cc5983cSToomas Soome 	case I_LDR:
2310cc5983cSToomas Soome 	    gethdr(fdi[i], &ihdr);
2320cc5983cSToomas Soome 	    if (ihdr.fmt != F_BIN)
2330cc5983cSToomas Soome 		Warn(fname, "Loader format is %s; processing as %s",
2340cc5983cSToomas Soome 		     fmtlist[ihdr.fmt], fmtlist[F_BIN]);
2350cc5983cSToomas Soome 	    ldr_size = ihdr.size;
2360cc5983cSToomas Soome 	    break;
2370cc5983cSToomas Soome 	case I_BTX:
2380cc5983cSToomas Soome 	    getbtx(fdi[i], &btx);
2390cc5983cSToomas Soome 	    break;
2400cc5983cSToomas Soome 	case I_CLNT:
2410cc5983cSToomas Soome 	    gethdr(fdi[i], &ihdr);
2420cc5983cSToomas Soome 	    if (ihdr.org && ihdr.org != BTX_PGSIZE)
2430cc5983cSToomas Soome 		Warn(fname,
2440cc5983cSToomas Soome 		     "Client origin is 0x%x; expecting 0 or 0x%x",
2450cc5983cSToomas Soome 		     ihdr.org, BTX_PGSIZE);
2460cc5983cSToomas Soome 	}
2470cc5983cSToomas Soome     }
2480cc5983cSToomas Soome     memset(&ohdr, 0, sizeof(ohdr));
2490cc5983cSToomas Soome     ohdr.fmt = format;
2500cc5983cSToomas Soome     ohdr.text = ldr_size;
2510cc5983cSToomas Soome     ohdr.data = btx.btx_textsz + ihdr.size;
2520cc5983cSToomas Soome     ohdr.org = lentry;
2530cc5983cSToomas Soome     ohdr.entry = lentry;
2540cc5983cSToomas Soome     cwr = 0;
2550cc5983cSToomas Soome     if (wpage > 0 || (wpage == -1 && !(ihdr.flags & IMPURE))) {
2560cc5983cSToomas Soome 	if (wpage > 0)
2570cc5983cSToomas Soome 	    cwr = wpage;
2580cc5983cSToomas Soome 	else {
2590cc5983cSToomas Soome 	    cwr = howmany(ihdr.text, BTX_PGSIZE);
2600cc5983cSToomas Soome 	    if (cwr > BTX_MAXCWR)
2610cc5983cSToomas Soome 		cwr = BTX_MAXCWR;
2620cc5983cSToomas Soome 	}
2630cc5983cSToomas Soome     }
2640cc5983cSToomas Soome     if (ppage > 0 || (ppage && wpage && ihdr.org >= BTX_PGSIZE)) {
2650cc5983cSToomas Soome 	btx.btx_flags |= BTX_MAPONE;
2660cc5983cSToomas Soome 	if (!cwr)
2670cc5983cSToomas Soome 	    cwr++;
2680cc5983cSToomas Soome     }
2690cc5983cSToomas Soome     btx.btx_pgctl -= cwr;
2700cc5983cSToomas Soome     btx.btx_entry = Eflag ? centry : ihdr.entry;
2710cc5983cSToomas Soome     if ((size_t)snprintf(name, sizeof(name), "%s.tmp", oname) >= sizeof(name))
2720cc5983cSToomas Soome 	errx(2, "%s: Filename too long", oname);
2730cc5983cSToomas Soome     if ((fdo = open(name, O_CREAT | O_TRUNC | O_WRONLY, 0666)) == -1)
2740cc5983cSToomas Soome 	err(2, "%s", name);
2750cc5983cSToomas Soome     if (!(tname = strdup(name)))
2760cc5983cSToomas Soome 	err(2, NULL);
2770cc5983cSToomas Soome     puthdr(fdo, &ohdr);
2780cc5983cSToomas Soome     for (i = I_LDR; i <= I_CLNT; i++) {
2790cc5983cSToomas Soome 	fname = i == I_LDR ? lname : i == I_BTX ? bname : iname;
2800cc5983cSToomas Soome 	switch (i) {
2810cc5983cSToomas Soome 	case I_LDR:
2820cc5983cSToomas Soome 	    copy(fdi[i], fdo, ldr_size, 0);
2830cc5983cSToomas Soome 	    seekx(fdo, ohdr.size += ohdr.text);
2840cc5983cSToomas Soome 	    break;
2850cc5983cSToomas Soome 	case I_BTX:
2860cc5983cSToomas Soome 	    btxle = btx;
2870cc5983cSToomas Soome 	    btxle.btx_pgctl = htole16(btxle.btx_pgctl);
2880cc5983cSToomas Soome 	    btxle.btx_textsz = htole16(btxle.btx_textsz);
2890cc5983cSToomas Soome 	    btxle.btx_entry = htole32(btxle.btx_entry);
2900cc5983cSToomas Soome 	    writex(fdo, &btxle, sizeof(btxle));
2910cc5983cSToomas Soome 	    copy(fdi[i], fdo, btx.btx_textsz - sizeof(btx),
2920cc5983cSToomas Soome 		 sizeof(btx));
2930cc5983cSToomas Soome 	    break;
2940cc5983cSToomas Soome 	case I_CLNT:
2950cc5983cSToomas Soome 	    copy(fdi[i], fdo, ihdr.size, 0);
2960cc5983cSToomas Soome 	    if (ftruncate(fdo, ohdr.size += ohdr.data))
2970cc5983cSToomas Soome 		err(2, "%s", tname);
2980cc5983cSToomas Soome 	}
2990cc5983cSToomas Soome 	if (close(fdi[i]))
3000cc5983cSToomas Soome 	    err(2, "%s", fname);
3010cc5983cSToomas Soome     }
3020cc5983cSToomas Soome     if (close(fdo))
3030cc5983cSToomas Soome 	err(2, "%s", tname);
3040cc5983cSToomas Soome     if (verbose) {
3050cc5983cSToomas Soome 	printf(binfo, btx.btx_majver, btx.btx_minver, btx.btx_textsz,
3060cc5983cSToomas Soome 	       BTX_ORIGIN(btx), BTX_ENTRY(btx), BTX_MAPPED(btx) *
3070cc5983cSToomas Soome 	       BTX_PGSIZE / 0x100000, !!(btx.btx_flags & BTX_MAPONE),
3080cc5983cSToomas Soome 	       BTX_MAPPED(btx) - btx.btx_pgctl - BTX_PGBASE /
3090cc5983cSToomas Soome 	       BTX_PGSIZE - BTX_MAPPED(btx) * 4 / BTX_PGSIZE);
3100cc5983cSToomas Soome 	printf(cinfo, fmtlist[ihdr.fmt], ihdr.size, ihdr.text,
3110cc5983cSToomas Soome 	       ihdr.data, ihdr.bss, ihdr.entry);
3120cc5983cSToomas Soome 	printf(oinfo, fmtlist[ohdr.fmt], ohdr.size, ohdr.text,
3130cc5983cSToomas Soome 	       ohdr.data, ohdr.org, ohdr.entry);
3140cc5983cSToomas Soome     }
3150cc5983cSToomas Soome }
3160cc5983cSToomas Soome 
3170cc5983cSToomas Soome /*
3180cc5983cSToomas Soome  * Read BTX file header.
3190cc5983cSToomas Soome  */
3200cc5983cSToomas Soome static void
getbtx(int fd,struct btx_hdr * btx)3210cc5983cSToomas Soome getbtx(int fd, struct btx_hdr * btx)
3220cc5983cSToomas Soome {
3230cc5983cSToomas Soome     if (readx(fd, btx, sizeof(*btx), 0) != sizeof(*btx) ||
3240cc5983cSToomas Soome 	btx->btx_magic[0] != BTX_MAG0 ||
3250cc5983cSToomas Soome 	btx->btx_magic[1] != BTX_MAG1 ||
3260cc5983cSToomas Soome 	btx->btx_magic[2] != BTX_MAG2)
3270cc5983cSToomas Soome 	errx(1, "%s: Not a BTX kernel", fname);
3280cc5983cSToomas Soome     btx->btx_pgctl = le16toh(btx->btx_pgctl);
3290cc5983cSToomas Soome     btx->btx_textsz = le16toh(btx->btx_textsz);
3300cc5983cSToomas Soome     btx->btx_entry = le32toh(btx->btx_entry);
3310cc5983cSToomas Soome }
3320cc5983cSToomas Soome 
3330cc5983cSToomas Soome /*
3340cc5983cSToomas Soome  * Get file size and read a.out or ELF header.
3350cc5983cSToomas Soome  */
3360cc5983cSToomas Soome static void
gethdr(int fd,struct hdr * hdr)3370cc5983cSToomas Soome gethdr(int fd, struct hdr *hdr)
3380cc5983cSToomas Soome {
3390cc5983cSToomas Soome     struct stat sb;
3400cc5983cSToomas Soome     const struct exec *ex;
3410cc5983cSToomas Soome     const Elf32_Ehdr *ee;
3420cc5983cSToomas Soome     const Elf32_Phdr *ep;
3430cc5983cSToomas Soome     void *p;
3440cc5983cSToomas Soome     unsigned int fmt, x, n, i;
3450cc5983cSToomas Soome 
3460cc5983cSToomas Soome     memset(hdr, 0, sizeof(*hdr));
3470cc5983cSToomas Soome     if (fstat(fd, &sb))
3480cc5983cSToomas Soome 	err(2, "%s", fname);
349*b59f980dSToomas Soome     if (sb.st_size > UINT32_MAX)
3500cc5983cSToomas Soome 	errx(1, "%s: Too big", fname);
3510cc5983cSToomas Soome     hdr->size = sb.st_size;
3520cc5983cSToomas Soome     if (!hdr->size)
3530cc5983cSToomas Soome 	return;
3540cc5983cSToomas Soome     if ((p = mmap(NULL, hdr->size, PROT_READ, MAP_SHARED, fd,
3550cc5983cSToomas Soome 		  0)) == MAP_FAILED)
3560cc5983cSToomas Soome 	err(2, "%s", fname);
3570cc5983cSToomas Soome     for (fmt = F_CNT - 1; !hdr->fmt && fmt; fmt--)
3580cc5983cSToomas Soome 	switch (fmt) {
3590cc5983cSToomas Soome 	case F_AOUT:
3600cc5983cSToomas Soome 	    ex = p;
3610cc5983cSToomas Soome 	    if (hdr->size >= sizeof(struct exec) && !N_BADMAG(*ex)) {
3620cc5983cSToomas Soome 		hdr->fmt = fmt;
3630cc5983cSToomas Soome 		x = N_GETMAGIC(*ex);
3640cc5983cSToomas Soome 		if (x == OMAGIC || x == NMAGIC) {
3650cc5983cSToomas Soome 		    if (x == NMAGIC)
3660cc5983cSToomas Soome 			Warn(fname, "Treating %s NMAGIC as OMAGIC",
3670cc5983cSToomas Soome 			     fmtlist[fmt]);
3680cc5983cSToomas Soome 		    hdr->flags |= IMPURE;
3690cc5983cSToomas Soome 		}
3700cc5983cSToomas Soome 		hdr->text = le32toh(ex->a_text);
3710cc5983cSToomas Soome 		hdr->data = le32toh(ex->a_data);
3720cc5983cSToomas Soome 		hdr->bss = le32toh(ex->a_bss);
3730cc5983cSToomas Soome 		hdr->entry = le32toh(ex->a_entry);
3740cc5983cSToomas Soome 		if (le32toh(ex->a_entry) >= BTX_PGSIZE)
3750cc5983cSToomas Soome 		    hdr->org = BTX_PGSIZE;
3760cc5983cSToomas Soome 	    }
3770cc5983cSToomas Soome 	    break;
3780cc5983cSToomas Soome 	case F_ELF:
3790cc5983cSToomas Soome 	    ee = p;
3800cc5983cSToomas Soome 	    if (hdr->size >= sizeof(Elf32_Ehdr) && IS_ELF(*ee)) {
3810cc5983cSToomas Soome 		hdr->fmt = fmt;
3820cc5983cSToomas Soome 		for (n = i = 0; i < le16toh(ee->e_phnum); i++) {
3830cc5983cSToomas Soome 		    ep = (void *)((uint8_t *)p + le32toh(ee->e_phoff) +
3840cc5983cSToomas Soome 				  le16toh(ee->e_phentsize) * i);
3850cc5983cSToomas Soome 		    if (le32toh(ep->p_type) == PT_LOAD)
3860cc5983cSToomas Soome 			switch (n++) {
3870cc5983cSToomas Soome 			case 0:
3880cc5983cSToomas Soome 			    hdr->text = le32toh(ep->p_filesz);
3890cc5983cSToomas Soome 			    hdr->org = le32toh(ep->p_paddr);
3900cc5983cSToomas Soome 			    if (le32toh(ep->p_flags) & PF_W)
3910cc5983cSToomas Soome 				hdr->flags |= IMPURE;
3920cc5983cSToomas Soome 			    break;
3930cc5983cSToomas Soome 			case 1:
3940cc5983cSToomas Soome 			    hdr->data = le32toh(ep->p_filesz);
3950cc5983cSToomas Soome 			    hdr->bss = le32toh(ep->p_memsz) -
3960cc5983cSToomas Soome 				le32toh(ep->p_filesz);
3970cc5983cSToomas Soome 			    break;
3980cc5983cSToomas Soome 			case 2:
3990cc5983cSToomas Soome 			    Warn(fname,
4000cc5983cSToomas Soome 				 "Ignoring extra %s PT_LOAD segments",
4010cc5983cSToomas Soome 				 fmtlist[fmt]);
4020cc5983cSToomas Soome 			}
4030cc5983cSToomas Soome 		}
4040cc5983cSToomas Soome 		hdr->entry = le32toh(ee->e_entry);
4050cc5983cSToomas Soome 	    }
4060cc5983cSToomas Soome 	}
4070cc5983cSToomas Soome     if (munmap(p, hdr->size))
4080cc5983cSToomas Soome 	err(2, "%s", fname);
4090cc5983cSToomas Soome }
4100cc5983cSToomas Soome 
4110cc5983cSToomas Soome /*
4120cc5983cSToomas Soome  * Write a.out or ELF header.
4130cc5983cSToomas Soome  */
4140cc5983cSToomas Soome static void
puthdr(int fd,struct hdr * hdr)4150cc5983cSToomas Soome puthdr(int fd, struct hdr *hdr)
4160cc5983cSToomas Soome {
4170cc5983cSToomas Soome     struct exec ex;
4180cc5983cSToomas Soome     struct elfh eh;
4190cc5983cSToomas Soome 
4200cc5983cSToomas Soome     switch (hdr->fmt) {
4210cc5983cSToomas Soome     case F_AOUT:
4220cc5983cSToomas Soome 	memset(&ex, 0, sizeof(ex));
4230cc5983cSToomas Soome 	N_SETMAGIC(ex, ZMAGIC, MID_I386, 0);
4240cc5983cSToomas Soome 	hdr->text = N_ALIGN(ex, hdr->text);
4250cc5983cSToomas Soome 	ex.a_text = htole32(hdr->text);
4260cc5983cSToomas Soome 	hdr->data = N_ALIGN(ex, hdr->data);
4270cc5983cSToomas Soome 	ex.a_data = htole32(hdr->data);
4280cc5983cSToomas Soome 	ex.a_entry = htole32(hdr->entry);
4290cc5983cSToomas Soome 	writex(fd, &ex, sizeof(ex));
4300cc5983cSToomas Soome 	hdr->size = N_ALIGN(ex, sizeof(ex));
4310cc5983cSToomas Soome 	seekx(fd, hdr->size);
4320cc5983cSToomas Soome 	break;
4330cc5983cSToomas Soome     case F_ELF:
4340cc5983cSToomas Soome 	eh = elfhdr;
4350cc5983cSToomas Soome 	eh.e.e_entry = htole32(hdr->entry);
4360cc5983cSToomas Soome 	eh.p[0].p_vaddr = eh.p[0].p_paddr = htole32(hdr->org);
4370cc5983cSToomas Soome 	eh.p[0].p_filesz = eh.p[0].p_memsz = htole32(hdr->text);
4380cc5983cSToomas Soome 	eh.p[1].p_offset = htole32(le32toh(eh.p[0].p_offset) +
4390cc5983cSToomas Soome 	    le32toh(eh.p[0].p_filesz));
4400cc5983cSToomas Soome 	eh.p[1].p_vaddr = eh.p[1].p_paddr =
4410cc5983cSToomas Soome 	    htole32(align(le32toh(eh.p[0].p_paddr) + le32toh(eh.p[0].p_memsz),
4420cc5983cSToomas Soome 	    4096));
4430cc5983cSToomas Soome 	eh.p[1].p_filesz = eh.p[1].p_memsz = htole32(hdr->data);
4440cc5983cSToomas Soome 	eh.sh[2].sh_addr = eh.p[0].p_vaddr;
4450cc5983cSToomas Soome 	eh.sh[2].sh_offset = eh.p[0].p_offset;
4460cc5983cSToomas Soome 	eh.sh[2].sh_size = eh.p[0].p_filesz;
4470cc5983cSToomas Soome 	eh.sh[3].sh_addr = eh.p[1].p_vaddr;
4480cc5983cSToomas Soome 	eh.sh[3].sh_offset = eh.p[1].p_offset;
4490cc5983cSToomas Soome 	eh.sh[3].sh_size = eh.p[1].p_filesz;
4500cc5983cSToomas Soome 	writex(fd, &eh, sizeof(eh));
4510cc5983cSToomas Soome 	hdr->size = sizeof(eh);
4520cc5983cSToomas Soome     }
4530cc5983cSToomas Soome }
4540cc5983cSToomas Soome 
4550cc5983cSToomas Soome /*
4560cc5983cSToomas Soome  * Safe copy from input file to output file.
4570cc5983cSToomas Soome  */
4580cc5983cSToomas Soome static void
copy(int fdi,int fdo,size_t nbyte,off_t offset)4590cc5983cSToomas Soome copy(int fdi, int fdo, size_t nbyte, off_t offset)
4600cc5983cSToomas Soome {
4610cc5983cSToomas Soome     char buf[8192];
4620cc5983cSToomas Soome     size_t n;
4630cc5983cSToomas Soome 
4640cc5983cSToomas Soome     while (nbyte) {
4650cc5983cSToomas Soome 	if ((n = sizeof(buf)) > nbyte)
4660cc5983cSToomas Soome 	    n = nbyte;
4670cc5983cSToomas Soome 	if (readx(fdi, buf, n, offset) != n)
4680cc5983cSToomas Soome 	    errx(2, "%s: Short read", fname);
4690cc5983cSToomas Soome 	writex(fdo, buf, n);
4700cc5983cSToomas Soome 	nbyte -= n;
4710cc5983cSToomas Soome 	offset = -1;
4720cc5983cSToomas Soome     }
4730cc5983cSToomas Soome }
4740cc5983cSToomas Soome 
4750cc5983cSToomas Soome /*
4760cc5983cSToomas Soome  * Safe read from input file.
4770cc5983cSToomas Soome  */
4780cc5983cSToomas Soome static size_t
readx(int fd,void * buf,size_t nbyte,off_t offset)4790cc5983cSToomas Soome readx(int fd, void *buf, size_t nbyte, off_t offset)
4800cc5983cSToomas Soome {
4810cc5983cSToomas Soome     ssize_t n;
4820cc5983cSToomas Soome 
4830cc5983cSToomas Soome     if (offset != -1 && lseek(fd, offset, SEEK_SET) != offset)
4840cc5983cSToomas Soome 	err(2, "%s", fname);
4850cc5983cSToomas Soome     if ((n = read(fd, buf, nbyte)) == -1)
4860cc5983cSToomas Soome 	err(2, "%s", fname);
4870cc5983cSToomas Soome     return n;
4880cc5983cSToomas Soome }
4890cc5983cSToomas Soome 
4900cc5983cSToomas Soome /*
4910cc5983cSToomas Soome  * Safe write to output file.
4920cc5983cSToomas Soome  */
4930cc5983cSToomas Soome static void
writex(int fd,const void * buf,size_t nbyte)4940cc5983cSToomas Soome writex(int fd, const void *buf, size_t nbyte)
4950cc5983cSToomas Soome {
4960cc5983cSToomas Soome     ssize_t n;
4970cc5983cSToomas Soome 
4980cc5983cSToomas Soome     if ((n = write(fd, buf, nbyte)) == -1)
4990cc5983cSToomas Soome 	err(2, "%s", tname);
5000cc5983cSToomas Soome     if ((size_t)n != nbyte)
5010cc5983cSToomas Soome 	errx(2, "%s: Short write", tname);
5020cc5983cSToomas Soome }
5030cc5983cSToomas Soome 
5040cc5983cSToomas Soome /*
5050cc5983cSToomas Soome  * Safe seek in output file.
5060cc5983cSToomas Soome  */
5070cc5983cSToomas Soome static void
seekx(int fd,off_t offset)5080cc5983cSToomas Soome seekx(int fd, off_t offset)
5090cc5983cSToomas Soome {
5100cc5983cSToomas Soome     if (lseek(fd, offset, SEEK_SET) != offset)
5110cc5983cSToomas Soome 	err(2, "%s", tname);
5120cc5983cSToomas Soome }
5130cc5983cSToomas Soome 
5140cc5983cSToomas Soome /*
5150cc5983cSToomas Soome  * Convert an option argument to a format code.
5160cc5983cSToomas Soome  */
5170cc5983cSToomas Soome static unsigned int
optfmt(const char * arg)5180cc5983cSToomas Soome optfmt(const char *arg)
5190cc5983cSToomas Soome {
5200cc5983cSToomas Soome     unsigned int i;
5210cc5983cSToomas Soome 
5220cc5983cSToomas Soome     for (i = 0; i < F_CNT && strcmp(arg, fmtlist[i]); i++);
5230cc5983cSToomas Soome     if (i == F_CNT)
5240cc5983cSToomas Soome 	errx(1, "%s: Unknown format", arg);
5250cc5983cSToomas Soome     return i;
5260cc5983cSToomas Soome }
5270cc5983cSToomas Soome 
5280cc5983cSToomas Soome /*
5290cc5983cSToomas Soome  * Convert an option argument to an address.
5300cc5983cSToomas Soome  */
5310cc5983cSToomas Soome static uint32_t
optaddr(const char * arg)5320cc5983cSToomas Soome optaddr(const char *arg)
5330cc5983cSToomas Soome {
5340cc5983cSToomas Soome     char *s;
5350cc5983cSToomas Soome     unsigned long x;
5360cc5983cSToomas Soome 
5370cc5983cSToomas Soome     errno = 0;
5380cc5983cSToomas Soome     x = strtoul(arg, &s, 0);
539*b59f980dSToomas Soome     if (errno || !*arg || *s || x > UINT32_MAX)
5400cc5983cSToomas Soome 	errx(1, "%s: Illegal address", arg);
5410cc5983cSToomas Soome     return x;
5420cc5983cSToomas Soome }
5430cc5983cSToomas Soome 
5440cc5983cSToomas Soome /*
5450cc5983cSToomas Soome  * Convert an option argument to a page number.
5460cc5983cSToomas Soome  */
5470cc5983cSToomas Soome static int
optpage(const char * arg,int hi)5480cc5983cSToomas Soome optpage(const char *arg, int hi)
5490cc5983cSToomas Soome {
5500cc5983cSToomas Soome     char *s;
5510cc5983cSToomas Soome     long x;
5520cc5983cSToomas Soome 
5530cc5983cSToomas Soome     errno = 0;
5540cc5983cSToomas Soome     x = strtol(arg, &s, 0);
5550cc5983cSToomas Soome     if (errno || !*arg || *s || x < 0 || x > hi)
5560cc5983cSToomas Soome 	errx(1, "%s: Illegal page number", arg);
5570cc5983cSToomas Soome     return x;
5580cc5983cSToomas Soome }
5590cc5983cSToomas Soome 
5600cc5983cSToomas Soome /*
5610cc5983cSToomas Soome  * Display a warning.
5620cc5983cSToomas Soome  */
5630cc5983cSToomas Soome static void
Warn(const char * locus,const char * fmt,...)5640cc5983cSToomas Soome Warn(const char *locus, const char *fmt, ...)
5650cc5983cSToomas Soome {
5660cc5983cSToomas Soome     va_list ap;
5670cc5983cSToomas Soome     char *s;
5680cc5983cSToomas Soome 
5690cc5983cSToomas Soome     if (!quiet) {
5700cc5983cSToomas Soome 	asprintf(&s, "%s: Warning: %s", locus, fmt);
5710cc5983cSToomas Soome 	va_start(ap, fmt);
5720cc5983cSToomas Soome 	vwarnx(s, ap);
5730cc5983cSToomas Soome 	va_end(ap);
5740cc5983cSToomas Soome 	free(s);
5750cc5983cSToomas Soome     }
5760cc5983cSToomas Soome }
5770cc5983cSToomas Soome 
5780cc5983cSToomas Soome /*
5790cc5983cSToomas Soome  * Display usage information.
5800cc5983cSToomas Soome  */
5810cc5983cSToomas Soome static void
usage(void)5820cc5983cSToomas Soome usage(void)
5830cc5983cSToomas Soome {
5840cc5983cSToomas Soome     fprintf(stderr, "%s\n%s\n",
5850cc5983cSToomas Soome     "usage: btxld [-qv] [-b file] [-E address] [-e address] [-f format]",
5860cc5983cSToomas Soome     "             [-l file] [-o filename] [-P page] [-W page] file");
5870cc5983cSToomas Soome     exit(1);
5880cc5983cSToomas Soome }
589