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