| Commit | Line | Data |
|---|---|---|
| 984263bc MD |
1 | /* |
| 2 | * Copyright (c) 1998 Robert Nordier | |
| 3 | * All rights reserved. | |
| 4 | * | |
| 5 | * Redistribution and use in source and binary forms, with or without | |
| 6 | * modification, are permitted provided that the following conditions | |
| 7 | * are met: | |
| 8 | * 1. Redistributions of source code must retain the above copyright | |
| 9 | * notice, this list of conditions and the following disclaimer. | |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 11 | * notice, this list of conditions and the following disclaimer in the | |
| 12 | * documentation and/or other materials provided with the distribution. | |
| 13 | * | |
| 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS``AS IS'' AND | |
| 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
| 17 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS | |
| 18 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, | |
| 19 | * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT | |
| 20 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | |
| 21 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
| 22 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE | |
| 23 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | |
| 24 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 1de703da MD |
25 | * |
| 26 | * $FreeBSD: src/usr.sbin/btxld/btxld.c,v 1.4 2000/01/04 14:10:36 marcel Exp $ | |
| 0f24e7a1 | 27 | * $DragonFly: src/usr.sbin/btxld/btxld.c,v 1.3 2004/08/19 21:38:30 joerg Exp $ |
| 984263bc MD |
28 | */ |
| 29 | ||
| 79b4bbd6 | 30 | #define AOUT_H_FORCE32 |
| 0f24e7a1 | 31 | #include <sys/param.h> |
| 984263bc MD |
32 | #include <sys/stat.h> |
| 33 | #include <sys/mman.h> | |
| 34 | ||
| 35 | #include <err.h> | |
| 36 | #include <errno.h> | |
| 37 | #include <fcntl.h> | |
| 38 | #include <stdarg.h> | |
| 39 | #include <stdio.h> | |
| 40 | #include <stdlib.h> | |
| 41 | #include <string.h> | |
| 42 | #include <unistd.h> | |
| 43 | ||
| 44 | #include <a.out.h> | |
| 45 | ||
| 46 | #include "btx.h" | |
| 47 | #include "elfh.h" | |
| 48 | ||
| 49 | #define BTX_PATH "/sys/boot/i386/btx" | |
| 50 | ||
| 51 | #define I_LDR 0 /* BTX loader */ | |
| 52 | #define I_BTX 1 /* BTX kernel */ | |
| 53 | #define I_CLNT 2 /* Client program */ | |
| 54 | ||
| 55 | #define F_BIN 0 /* Binary */ | |
| 56 | #define F_AOUT 1 /* ZMAGIC a.out */ | |
| 57 | #define F_ELF 2 /* 32-bit ELF */ | |
| 58 | #define F_CNT 3 /* Number of formats */ | |
| 59 | ||
| 60 | #define IMPURE 1 /* Writable text */ | |
| 61 | #define MAXU32 0xffffffff /* Maximum unsigned 32-bit quantity */ | |
| 62 | ||
| 63 | #define align(x, y) (((x) + (y) - 1) & ~((y) - 1)) | |
| 64 | ||
| 65 | struct hdr { | |
| 66 | uint32_t fmt; /* Format */ | |
| 67 | uint32_t flags; /* Bit flags */ | |
| 68 | uint32_t size; /* Size of file */ | |
| 69 | uint32_t text; /* Size of text segment */ | |
| 70 | uint32_t data; /* Size of data segment */ | |
| 71 | uint32_t bss; /* Size of bss segment */ | |
| 72 | uint32_t org; /* Program origin */ | |
| 73 | uint32_t entry; /* Program entry point */ | |
| 74 | }; | |
| 75 | ||
| 76 | static const char *const fmtlist[] = {"bin", "aout", "elf"}; | |
| 77 | ||
| 78 | static const char binfo[] = | |
| 79 | "kernel: ver=%u.%02u size=%x load=%x entry=%x map=%uM " | |
| 80 | "pgctl=%x:%x\n"; | |
| 81 | static const char cinfo[] = | |
| 82 | "client: fmt=%s size=%x text=%x data=%x bss=%x entry=%x\n"; | |
| 83 | static const char oinfo[] = | |
| 84 | "output: fmt=%s size=%x text=%x data=%x org=%x entry=%x\n"; | |
| 85 | ||
| 86 | static const char *lname = | |
| 87 | BTX_PATH "/btxldr/btxldr"; /* BTX loader */ | |
| 88 | static const char *bname = | |
| 89 | BTX_PATH "/btx/btx"; /* BTX kernel */ | |
| 90 | static const char *oname = | |
| 91 | "a.out"; /* Output filename */ | |
| 92 | ||
| 93 | static int ppage = -1; /* First page present */ | |
| 94 | static int wpage = -1; /* First page writable */ | |
| 95 | ||
| 96 | static unsigned int format; /* Output format */ | |
| 97 | ||
| 98 | static uint32_t centry; /* Client entry address */ | |
| 99 | static uint32_t lentry; /* Loader entry address */ | |
| 100 | ||
| 101 | static int Eflag; /* Client entry option */ | |
| 102 | ||
| 103 | static int quiet; /* Inhibit warnings */ | |
| 104 | static int verbose; /* Display information */ | |
| 105 | ||
| 106 | static const char *tname; /* Temporary output file */ | |
| 107 | static const char *fname; /* Current input file */ | |
| 108 | ||
| 109 | static void cleanup(void); | |
| 110 | static void btxld(const char *); | |
| 111 | static void getbtx(int, struct btx_hdr *); | |
| 112 | static void gethdr(int, struct hdr *); | |
| 113 | static void puthdr(int, struct hdr *); | |
| 114 | static void copy(int, int, size_t, off_t); | |
| 115 | static size_t readx(int, void *, size_t, off_t); | |
| 116 | static void writex(int, const void *, size_t); | |
| 117 | static void seekx(int, off_t); | |
| 118 | static unsigned int optfmt(const char *); | |
| 119 | static uint32_t optaddr(const char *); | |
| 120 | static int optpage(const char *, int); | |
| 121 | static void Warn(const char *, const char *, ...); | |
| 122 | static void usage(void); | |
| 123 | ||
| 124 | /* | |
| 125 | * A link editor for BTX clients. | |
| 126 | */ | |
| 127 | int | |
| 128 | main(int argc, char *argv[]) | |
| 129 | { | |
| 130 | int c; | |
| 131 | ||
| 132 | while ((c = getopt(argc, argv, "qvb:E:e:f:l:o:P:W:")) != -1) | |
| 133 | switch (c) { | |
| 134 | case 'q': | |
| 135 | quiet = 1; | |
| 136 | break; | |
| 137 | case 'v': | |
| 138 | verbose = 1; | |
| 139 | break; | |
| 140 | case 'b': | |
| 141 | bname = optarg; | |
| 142 | break; | |
| 143 | case 'E': | |
| 144 | centry = optaddr(optarg); | |
| 145 | Eflag = 1; | |
| 146 | break; | |
| 147 | case 'e': | |
| 148 | lentry = optaddr(optarg); | |
| 149 | break; | |
| 150 | case 'f': | |
| 151 | format = optfmt(optarg); | |
| 152 | break; | |
| 153 | case 'l': | |
| 154 | lname = optarg; | |
| 155 | break; | |
| 156 | case 'o': | |
| 157 | oname = optarg; | |
| 158 | break; | |
| 159 | case 'P': | |
| 160 | ppage = optpage(optarg, 1); | |
| 161 | break; | |
| 162 | case 'W': | |
| 163 | wpage = optpage(optarg, BTX_MAXCWR); | |
| 164 | break; | |
| 165 | default: | |
| 166 | usage(); | |
| 167 | } | |
| 168 | argc -= optind; | |
| 169 | argv += optind; | |
| 170 | if (argc != 1) | |
| 171 | usage(); | |
| 172 | atexit(cleanup); | |
| 173 | btxld(*argv); | |
| 174 | return 0; | |
| 175 | } | |
| 176 | ||
| 177 | /* | |
| 178 | * Clean up after errors. | |
| 179 | */ | |
| 180 | static void | |
| 181 | cleanup(void) | |
| 182 | { | |
| 183 | if (tname) | |
| 184 | remove(tname); | |
| 185 | } | |
| 186 | ||
| 187 | /* | |
| 188 | * Read the input files; write the output file; display information. | |
| 189 | */ | |
| 190 | static void | |
| 191 | btxld(const char *iname) | |
| 192 | { | |
| 193 | char name[FILENAME_MAX]; | |
| 194 | struct btx_hdr btx; | |
| 195 | struct hdr ihdr, ohdr; | |
| 196 | unsigned int ldr_size, cwr; | |
| 197 | int fdi[3], fdo, i; | |
| 198 | ||
| 199 | ldr_size = 0; | |
| 200 | ||
| 201 | for (i = I_LDR; i <= I_CLNT; i++) { | |
| 202 | fname = i == I_LDR ? lname : i == I_BTX ? bname : iname; | |
| 203 | if ((fdi[i] = open(fname, O_RDONLY)) == -1) | |
| 204 | err(2, "%s", fname); | |
| 205 | switch (i) { | |
| 206 | case I_LDR: | |
| 207 | gethdr(fdi[i], &ihdr); | |
| 208 | if (ihdr.fmt != F_BIN) | |
| 209 | Warn(fname, "Loader format is %s; processing as %s", | |
| 210 | fmtlist[ihdr.fmt], fmtlist[F_BIN]); | |
| 211 | ldr_size = ihdr.size; | |
| 212 | break; | |
| 213 | case I_BTX: | |
| 214 | getbtx(fdi[i], &btx); | |
| 215 | break; | |
| 216 | case I_CLNT: | |
| 217 | gethdr(fdi[i], &ihdr); | |
| 218 | if (ihdr.org && ihdr.org != BTX_PGSIZE) | |
| 219 | Warn(fname, | |
| 220 | "Client origin is 0x%x; expecting 0 or 0x%x", | |
| 221 | ihdr.org, BTX_PGSIZE); | |
| 222 | } | |
| 223 | } | |
| 224 | memset(&ohdr, 0, sizeof(ohdr)); | |
| 225 | ohdr.fmt = format; | |
| 226 | ohdr.text = ldr_size; | |
| 227 | ohdr.data = btx.btx_textsz + ihdr.size; | |
| 228 | ohdr.org = lentry; | |
| 229 | ohdr.entry = lentry; | |
| 230 | cwr = 0; | |
| 231 | if (wpage > 0 || (wpage == -1 && !(ihdr.flags & IMPURE))) { | |
| 232 | if (wpage > 0) | |
| 233 | cwr = wpage; | |
| 234 | else { | |
| 235 | cwr = howmany(ihdr.text, BTX_PGSIZE); | |
| 236 | if (cwr > BTX_MAXCWR) | |
| 237 | cwr = BTX_MAXCWR; | |
| 238 | } | |
| 239 | } | |
| 240 | if (ppage > 0 || (ppage && wpage && ihdr.org >= BTX_PGSIZE)) { | |
| 241 | btx.btx_flags |= BTX_MAPONE; | |
| 242 | if (!cwr) | |
| 243 | cwr++; | |
| 244 | } | |
| 245 | btx.btx_pgctl -= cwr; | |
| 246 | btx.btx_entry = Eflag ? centry : ihdr.entry; | |
| 247 | if (snprintf(name, sizeof(name), "%s.tmp", oname) >= sizeof(name)) | |
| 248 | errx(2, "%s: Filename too long", oname); | |
| 249 | if ((fdo = open(name, O_CREAT | O_TRUNC | O_WRONLY, 0666)) == -1) | |
| 250 | err(2, "%s", name); | |
| 251 | if (!(tname = strdup(name))) | |
| 252 | err(2, NULL); | |
| 253 | puthdr(fdo, &ohdr); | |
| 254 | for (i = I_LDR; i <= I_CLNT; i++) { | |
| 255 | fname = i == I_LDR ? lname : i == I_BTX ? bname : iname; | |
| 256 | switch (i) { | |
| 257 | case I_LDR: | |
| 258 | copy(fdi[i], fdo, ldr_size, 0); | |
| 259 | seekx(fdo, ohdr.size += ohdr.text); | |
| 260 | break; | |
| 261 | case I_BTX: | |
| 262 | writex(fdo, &btx, sizeof(btx)); | |
| 263 | copy(fdi[i], fdo, btx.btx_textsz - sizeof(btx), | |
| 264 | sizeof(btx)); | |
| 265 | break; | |
| 266 | case I_CLNT: | |
| 267 | copy(fdi[i], fdo, ihdr.size, 0); | |
| 268 | if (ftruncate(fdo, ohdr.size += ohdr.data)) | |
| 269 | err(2, "%s", tname); | |
| 270 | } | |
| 271 | if (close(fdi[i])) | |
| 272 | err(2, "%s", fname); | |
| 273 | } | |
| 274 | if (close(fdo)) | |
| 275 | err(2, "%s", tname); | |
| 276 | if (rename(tname, oname)) | |
| 277 | err(2, "%s: Can't rename to %s", tname, oname); | |
| 278 | tname = NULL; | |
| 279 | if (verbose) { | |
| 280 | printf(binfo, btx.btx_majver, btx.btx_minver, btx.btx_textsz, | |
| 281 | BTX_ORIGIN(btx), BTX_ENTRY(btx), BTX_MAPPED(btx) * | |
| 282 | BTX_PGSIZE / 0x100000, !!(btx.btx_flags & BTX_MAPONE), | |
| 283 | BTX_MAPPED(btx) - btx.btx_pgctl - BTX_PGBASE / | |
| 284 | BTX_PGSIZE - BTX_MAPPED(btx) * 4 / BTX_PGSIZE); | |
| 285 | printf(cinfo, fmtlist[ihdr.fmt], ihdr.size, ihdr.text, | |
| 286 | ihdr.data, ihdr.bss, ihdr.entry); | |
| 287 | printf(oinfo, fmtlist[ohdr.fmt], ohdr.size, ohdr.text, | |
| 288 | ohdr.data, ohdr.org, ohdr.entry); | |
| 289 | } | |
| 290 | } | |
| 291 | ||
| 292 | /* | |
| 293 | * Read BTX file header. | |
| 294 | */ | |
| 295 | static void | |
| 296 | getbtx(int fd, struct btx_hdr * btx) | |
| 297 | { | |
| 298 | if (readx(fd, btx, sizeof(*btx), 0) != sizeof(*btx) || | |
| 299 | btx->btx_magic[0] != BTX_MAG0 || | |
| 300 | btx->btx_magic[1] != BTX_MAG1 || | |
| 301 | btx->btx_magic[2] != BTX_MAG2) | |
| 302 | errx(1, "%s: Not a BTX kernel", fname); | |
| 303 | } | |
| 304 | ||
| 305 | /* | |
| 306 | * Get file size and read a.out or ELF header. | |
| 307 | */ | |
| 308 | static void | |
| 309 | gethdr(int fd, struct hdr *hdr) | |
| 310 | { | |
| 311 | struct stat sb; | |
| 312 | const struct exec *ex; | |
| 313 | const Elf32_Ehdr *ee; | |
| 314 | const Elf32_Phdr *ep; | |
| 315 | void *p; | |
| 316 | unsigned int fmt, x, n, i; | |
| 317 | ||
| 318 | memset(hdr, 0, sizeof(*hdr)); | |
| 319 | if (fstat(fd, &sb)) | |
| 320 | err(2, "%s", fname); | |
| 321 | if (sb.st_size > MAXU32) | |
| 322 | errx(1, "%s: Too big", fname); | |
| 323 | hdr->size = sb.st_size; | |
| 324 | if ((p = mmap(NULL, hdr->size, PROT_READ, MAP_SHARED, fd, | |
| 325 | 0)) == MAP_FAILED) | |
| 326 | err(2, "%s", fname); | |
| 327 | for (fmt = F_CNT - 1; !hdr->fmt && fmt; fmt--) | |
| 328 | switch (fmt) { | |
| 329 | case F_AOUT: | |
| 330 | ex = p; | |
| 331 | if (hdr->size >= sizeof(struct exec) && !N_BADMAG(*ex)) { | |
| 332 | hdr->fmt = fmt; | |
| 333 | x = N_GETMAGIC(*ex); | |
| 334 | if (x == OMAGIC || x == NMAGIC) { | |
| 335 | if (x == NMAGIC) | |
| 336 | Warn(fname, "Treating %s NMAGIC as OMAGIC", | |
| 337 | fmtlist[fmt]); | |
| 338 | hdr->flags |= IMPURE; | |
| 339 | } | |
| 340 | hdr->text = ex->a_text; | |
| 341 | hdr->data = ex->a_data; | |
| 342 | hdr->bss = ex->a_bss; | |
| 343 | hdr->entry = ex->a_entry; | |
| 344 | if (ex->a_entry >= BTX_PGSIZE) | |
| 345 | hdr->org = BTX_PGSIZE; | |
| 346 | } | |
| 347 | break; | |
| 348 | case F_ELF: | |
| 349 | ee = p; | |
| 350 | if (hdr->size >= sizeof(Elf32_Ehdr) && IS_ELF(*ee)) { | |
| 351 | hdr->fmt = fmt; | |
| 352 | for (n = i = 0; i < ee->e_phnum; i++) { | |
| 353 | ep = (void *)((uint8_t *)p + ee->e_phoff + | |
| 354 | ee->e_phentsize * i); | |
| 355 | if (ep->p_type == PT_LOAD) | |
| 356 | switch (n++) { | |
| 357 | case 0: | |
| 358 | hdr->text = ep->p_filesz; | |
| 359 | hdr->org = ep->p_paddr; | |
| 360 | if (ep->p_flags & PF_W) | |
| 361 | hdr->flags |= IMPURE; | |
| 362 | break; | |
| 363 | case 1: | |
| 364 | hdr->data = ep->p_filesz; | |
| 365 | hdr->bss = ep->p_memsz - ep->p_filesz; | |
| 366 | break; | |
| 367 | case 2: | |
| 368 | Warn(fname, | |
| 369 | "Ignoring extra %s PT_LOAD segments", | |
| 370 | fmtlist[fmt]); | |
| 371 | } | |
| 372 | } | |
| 373 | hdr->entry = ee->e_entry; | |
| 374 | } | |
| 375 | } | |
| 376 | if (munmap(p, hdr->size)) | |
| 377 | err(2, "%s", fname); | |
| 378 | } | |
| 379 | ||
| 380 | /* | |
| 381 | * Write a.out or ELF header. | |
| 382 | */ | |
| 383 | static void | |
| 384 | puthdr(int fd, struct hdr *hdr) | |
| 385 | { | |
| 386 | struct exec ex; | |
| 387 | struct elfh eh; | |
| 388 | ||
| 389 | switch (hdr->fmt) { | |
| 390 | case F_AOUT: | |
| 391 | memset(&ex, 0, sizeof(ex)); | |
| 392 | N_SETMAGIC(ex, ZMAGIC, MID_ZERO, 0); | |
| 393 | hdr->text = N_ALIGN(ex, hdr->text); | |
| 394 | ex.a_text = hdr->text; | |
| 395 | hdr->data = N_ALIGN(ex, hdr->data); | |
| 396 | ex.a_data = hdr->data; | |
| 397 | ex.a_entry = hdr->entry; | |
| 398 | writex(fd, &ex, sizeof(ex)); | |
| 399 | hdr->size = N_ALIGN(ex, sizeof(ex)); | |
| 400 | seekx(fd, hdr->size); | |
| 401 | break; | |
| 402 | case F_ELF: | |
| 403 | eh = elfhdr; | |
| 404 | eh.e.e_entry = hdr->entry; | |
| 405 | eh.p[0].p_vaddr = eh.p[0].p_paddr = hdr->org; | |
| 406 | eh.p[0].p_filesz = eh.p[0].p_memsz = hdr->text; | |
| 407 | eh.p[1].p_offset = eh.p[0].p_offset + eh.p[0].p_filesz; | |
| 408 | eh.p[1].p_vaddr = eh.p[1].p_paddr = align(eh.p[0].p_paddr + | |
| 409 | eh.p[0].p_memsz, 4); | |
| 410 | eh.p[1].p_filesz = eh.p[1].p_memsz = hdr->data; | |
| 411 | eh.sh[2].sh_addr = eh.p[0].p_vaddr; | |
| 412 | eh.sh[2].sh_offset = eh.p[0].p_offset; | |
| 413 | eh.sh[2].sh_size = eh.p[0].p_filesz; | |
| 414 | eh.sh[3].sh_addr = eh.p[1].p_vaddr; | |
| 415 | eh.sh[3].sh_offset = eh.p[1].p_offset; | |
| 416 | eh.sh[3].sh_size = eh.p[1].p_filesz; | |
| 417 | writex(fd, &eh, sizeof(eh)); | |
| 418 | hdr->size = sizeof(eh); | |
| 419 | } | |
| 420 | } | |
| 421 | ||
| 422 | /* | |
| 423 | * Safe copy from input file to output file. | |
| 424 | */ | |
| 425 | static void | |
| 426 | copy(int fdi, int fdo, size_t nbyte, off_t offset) | |
| 427 | { | |
| 428 | char buf[8192]; | |
| 429 | size_t n; | |
| 430 | ||
| 431 | while (nbyte) { | |
| 432 | if ((n = sizeof(buf)) > nbyte) | |
| 433 | n = nbyte; | |
| 434 | if (readx(fdi, buf, n, offset) != n) | |
| 435 | errx(2, "%s: Short read", fname); | |
| 436 | writex(fdo, buf, n); | |
| 437 | nbyte -= n; | |
| 438 | offset = -1; | |
| 439 | } | |
| 440 | } | |
| 441 | ||
| 442 | /* | |
| 443 | * Safe read from input file. | |
| 444 | */ | |
| 445 | static size_t | |
| 446 | readx(int fd, void *buf, size_t nbyte, off_t offset) | |
| 447 | { | |
| 448 | ssize_t n; | |
| 449 | ||
| 450 | if (offset != -1 && lseek(fd, offset, SEEK_SET) != offset) | |
| 451 | err(2, "%s", fname); | |
| 452 | if ((n = read(fd, buf, nbyte)) == -1) | |
| 453 | err(2, "%s", fname); | |
| 454 | return n; | |
| 455 | } | |
| 456 | ||
| 457 | /* | |
| 458 | * Safe write to output file. | |
| 459 | */ | |
| 460 | static void | |
| 461 | writex(int fd, const void *buf, size_t nbyte) | |
| 462 | { | |
| 463 | ssize_t n; | |
| 464 | ||
| 465 | if ((n = write(fd, buf, nbyte)) == -1) | |
| 466 | err(2, "%s", tname); | |
| 467 | if (n != nbyte) | |
| 468 | errx(2, "%s: Short write", tname); | |
| 469 | } | |
| 470 | ||
| 471 | /* | |
| 472 | * Safe seek in output file. | |
| 473 | */ | |
| 474 | static void | |
| 475 | seekx(int fd, off_t offset) | |
| 476 | { | |
| 477 | if (lseek(fd, offset, SEEK_SET) != offset) | |
| 478 | err(2, "%s", tname); | |
| 479 | } | |
| 480 | ||
| 481 | /* | |
| 482 | * Convert an option argument to a format code. | |
| 483 | */ | |
| 484 | static unsigned int | |
| 485 | optfmt(const char *arg) | |
| 486 | { | |
| 487 | unsigned int i; | |
| 488 | ||
| 489 | for (i = 0; i < F_CNT && strcmp(arg, fmtlist[i]); i++); | |
| 490 | if (i == F_CNT) | |
| 491 | errx(1, "%s: Unknown format", arg); | |
| 492 | return i; | |
| 493 | } | |
| 494 | ||
| 495 | /* | |
| 496 | * Convert an option argument to an address. | |
| 497 | */ | |
| 498 | static uint32_t | |
| 499 | optaddr(const char *arg) | |
| 500 | { | |
| 501 | char *s; | |
| 502 | unsigned long x; | |
| 503 | ||
| 504 | errno = 0; | |
| 505 | x = strtoul(arg, &s, 0); | |
| 506 | if (errno || !*arg || *s || x > MAXU32) | |
| 507 | errx(1, "%s: Illegal address", arg); | |
| 508 | return x; | |
| 509 | } | |
| 510 | ||
| 511 | /* | |
| 512 | * Convert an option argument to a page number. | |
| 513 | */ | |
| 514 | static int | |
| 515 | optpage(const char *arg, int hi) | |
| 516 | { | |
| 517 | char *s; | |
| 518 | long x; | |
| 519 | ||
| 520 | errno = 0; | |
| 521 | x = strtol(arg, &s, 0); | |
| 522 | if (errno || !*arg || *s || x < 0 || x > hi) | |
| 523 | errx(1, "%s: Illegal page number", arg); | |
| 524 | return x; | |
| 525 | } | |
| 526 | ||
| 527 | /* | |
| 528 | * Display a warning. | |
| 529 | */ | |
| 530 | static void | |
| 531 | Warn(const char *locus, const char *fmt, ...) | |
| 532 | { | |
| 533 | va_list ap; | |
| 534 | char *s; | |
| 535 | ||
| 536 | if (!quiet) { | |
| 537 | asprintf(&s, "%s: Warning: %s", locus, fmt); | |
| 538 | va_start(ap, fmt); | |
| 539 | vwarnx(s, ap); | |
| 540 | va_end(ap); | |
| 541 | free(s); | |
| 542 | } | |
| 543 | } | |
| 544 | ||
| 545 | /* | |
| 546 | * Display usage information. | |
| 547 | */ | |
| 548 | static void | |
| 549 | usage(void) | |
| 550 | { | |
| 551 | fprintf(stderr, "%s\n%s\n", | |
| 552 | "usage: btxld [-qv] [-b file] [-E address] [-e address] [-f format]", | |
| 553 | " [-l file] [-o filename] [-P page] [-W page] file"); | |
| 554 | exit(1); | |
| 555 | } |