2 * Copyright (c) 1998 Robert Nordier
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
28 static const char rcsid
[] =
32 #include <sys/param.h>
33 #include <sys/endian.h>
37 /* XXX make this work as an i386/amd64 cross-tool */
38 #include <machine/exec.h>
42 #include <netinet/in.h>
57 #define BTX_PATH "/sys/boot/i386/btx"
59 #define I_LDR 0 /* BTX loader */
60 #define I_BTX 1 /* BTX kernel */
61 #define I_CLNT 2 /* Client program */
63 #define F_BIN 0 /* Binary */
64 #define F_AOUT 1 /* ZMAGIC a.out */
65 #define F_ELF 2 /* 32-bit ELF */
66 #define F_CNT 3 /* Number of formats */
68 #define IMPURE 1 /* Writable text */
69 #define MAXU32 0xffffffff /* Maximum unsigned 32-bit quantity */
71 #define align(x, y) (((x) + (y) - 1) & ~((y) - 1))
74 uint32_t fmt
; /* Format */
75 uint32_t flags
; /* Bit flags */
76 uint32_t size
; /* Size of file */
77 uint32_t text
; /* Size of text segment */
78 uint32_t data
; /* Size of data segment */
79 uint32_t bss
; /* Size of bss segment */
80 uint32_t org
; /* Program origin */
81 uint32_t entry
; /* Program entry point */
84 static const char *const fmtlist
[] = {"bin", "aout", "elf"};
86 static const char binfo
[] =
87 "kernel: ver=%u.%02u size=%x load=%x entry=%x map=%uM "
89 static const char cinfo
[] =
90 "client: fmt=%s size=%x text=%x data=%x bss=%x entry=%x\n";
91 static const char oinfo
[] =
92 "output: fmt=%s size=%x text=%x data=%x org=%x entry=%x\n";
94 static const char *lname
=
95 BTX_PATH
"/btxldr/btxldr"; /* BTX loader */
96 static const char *bname
=
97 BTX_PATH
"/btx/btx"; /* BTX kernel */
98 static const char *oname
=
99 "a.out"; /* Output filename */
101 static int ppage
= -1; /* First page present */
102 static int wpage
= -1; /* First page writable */
104 static unsigned int format
; /* Output format */
106 static uint32_t centry
; /* Client entry address */
107 static uint32_t lentry
; /* Loader entry address */
109 static int Eflag
; /* Client entry option */
111 static int quiet
; /* Inhibit warnings */
112 static int verbose
; /* Display information */
114 static const char *tname
; /* Temporary output file */
115 static const char *fname
; /* Current input file */
117 static void cleanup(void);
118 static void btxld(const char *);
119 static void getbtx(int, struct btx_hdr
*);
120 static void gethdr(int, struct hdr
*);
121 static void puthdr(int, struct hdr
*);
122 static void copy(int, int, size_t, off_t
);
123 static size_t readx(int, void *, size_t, off_t
);
124 static void writex(int, const void *, size_t);
125 static void seekx(int, off_t
);
126 static unsigned int optfmt(const char *);
127 static uint32_t optaddr(const char *);
128 static int optpage(const char *, int);
129 static void Warn(const char *, const char *, ...);
130 static void usage(void);
133 * A link editor for BTX clients.
136 main(int argc
, char *argv
[])
140 while ((c
= getopt(argc
, argv
, "qvb:E:e:f:l:o:P:W:")) != -1)
152 centry
= optaddr(optarg
);
156 lentry
= optaddr(optarg
);
159 format
= optfmt(optarg
);
168 ppage
= optpage(optarg
, 1);
171 wpage
= optpage(optarg
, BTX_MAXCWR
);
186 * Clean up after errors.
196 * Read the input files; write the output file; display information.
199 btxld(const char *iname
)
201 char name
[FILENAME_MAX
];
202 struct btx_hdr btx
, btxle
;
203 struct hdr ihdr
, ohdr
;
204 unsigned int ldr_size
, cwr
;
209 for (i
= I_LDR
; i
<= I_CLNT
; i
++) {
210 fname
= i
== I_LDR
? lname
: i
== I_BTX
? bname
: iname
;
211 if ((fdi
[i
] = open(fname
, O_RDONLY
)) == -1)
215 gethdr(fdi
[i
], &ihdr
);
216 if (ihdr
.fmt
!= F_BIN
)
217 Warn(fname
, "Loader format is %s; processing as %s",
218 fmtlist
[ihdr
.fmt
], fmtlist
[F_BIN
]);
219 ldr_size
= ihdr
.size
;
222 getbtx(fdi
[i
], &btx
);
225 gethdr(fdi
[i
], &ihdr
);
226 if (ihdr
.org
&& ihdr
.org
!= BTX_PGSIZE
)
228 "Client origin is 0x%x; expecting 0 or 0x%x",
229 ihdr
.org
, BTX_PGSIZE
);
232 memset(&ohdr
, 0, sizeof(ohdr
));
234 ohdr
.text
= ldr_size
;
235 ohdr
.data
= btx
.btx_textsz
+ ihdr
.size
;
239 if (wpage
> 0 || (wpage
== -1 && !(ihdr
.flags
& IMPURE
))) {
243 cwr
= howmany(ihdr
.text
, BTX_PGSIZE
);
244 if (cwr
> BTX_MAXCWR
)
248 if (ppage
> 0 || (ppage
&& wpage
&& ihdr
.org
>= BTX_PGSIZE
)) {
249 btx
.btx_flags
|= BTX_MAPONE
;
253 btx
.btx_pgctl
-= cwr
;
254 btx
.btx_entry
= Eflag
? centry
: ihdr
.entry
;
255 if ((size_t)snprintf(name
, sizeof(name
), "%s.tmp", oname
) >= sizeof(name
))
256 errx(2, "%s: Filename too long", oname
);
257 if ((fdo
= open(name
, O_CREAT
| O_TRUNC
| O_WRONLY
, 0666)) == -1)
259 if (!(tname
= strdup(name
)))
262 for (i
= I_LDR
; i
<= I_CLNT
; i
++) {
263 fname
= i
== I_LDR
? lname
: i
== I_BTX
? bname
: iname
;
266 copy(fdi
[i
], fdo
, ldr_size
, 0);
267 seekx(fdo
, ohdr
.size
+= ohdr
.text
);
271 btxle
.btx_pgctl
= htole16(btxle
.btx_pgctl
);
272 btxle
.btx_textsz
= htole16(btxle
.btx_textsz
);
273 btxle
.btx_entry
= htole32(btxle
.btx_entry
);
274 writex(fdo
, &btxle
, sizeof(btxle
));
275 copy(fdi
[i
], fdo
, btx
.btx_textsz
- sizeof(btx
),
279 copy(fdi
[i
], fdo
, ihdr
.size
, 0);
280 if (ftruncate(fdo
, ohdr
.size
+= ohdr
.data
))
288 if (rename(tname
, oname
))
289 err(2, "%s: Can't rename to %s", tname
, oname
);
292 printf(binfo
, btx
.btx_majver
, btx
.btx_minver
, btx
.btx_textsz
,
293 BTX_ORIGIN(btx
), BTX_ENTRY(btx
), BTX_MAPPED(btx
) *
294 BTX_PGSIZE
/ 0x100000, !!(btx
.btx_flags
& BTX_MAPONE
),
295 BTX_MAPPED(btx
) - btx
.btx_pgctl
- BTX_PGBASE
/
296 BTX_PGSIZE
- BTX_MAPPED(btx
) * 4 / BTX_PGSIZE
);
297 printf(cinfo
, fmtlist
[ihdr
.fmt
], ihdr
.size
, ihdr
.text
,
298 ihdr
.data
, ihdr
.bss
, ihdr
.entry
);
299 printf(oinfo
, fmtlist
[ohdr
.fmt
], ohdr
.size
, ohdr
.text
,
300 ohdr
.data
, ohdr
.org
, ohdr
.entry
);
305 * Read BTX file header.
308 getbtx(int fd
, struct btx_hdr
* btx
)
310 if (readx(fd
, btx
, sizeof(*btx
), 0) != sizeof(*btx
) ||
311 btx
->btx_magic
[0] != BTX_MAG0
||
312 btx
->btx_magic
[1] != BTX_MAG1
||
313 btx
->btx_magic
[2] != BTX_MAG2
)
314 errx(1, "%s: Not a BTX kernel", fname
);
315 btx
->btx_pgctl
= le16toh(btx
->btx_pgctl
);
316 btx
->btx_textsz
= le16toh(btx
->btx_textsz
);
317 btx
->btx_entry
= le32toh(btx
->btx_entry
);
321 * Get file size and read a.out or ELF header.
324 gethdr(int fd
, struct hdr
*hdr
)
327 const struct exec
*ex
;
328 const Elf32_Ehdr
*ee
;
329 const Elf32_Phdr
*ep
;
331 unsigned int fmt
, x
, n
, i
;
333 memset(hdr
, 0, sizeof(*hdr
));
336 if (sb
.st_size
> MAXU32
)
337 errx(1, "%s: Too big", fname
);
338 hdr
->size
= sb
.st_size
;
341 if ((p
= mmap(NULL
, hdr
->size
, PROT_READ
, MAP_SHARED
, fd
,
344 for (fmt
= F_CNT
- 1; !hdr
->fmt
&& fmt
; fmt
--)
348 if (hdr
->size
>= sizeof(struct exec
) && !N_BADMAG(*ex
)) {
351 if (x
== OMAGIC
|| x
== NMAGIC
) {
353 Warn(fname
, "Treating %s NMAGIC as OMAGIC",
355 hdr
->flags
|= IMPURE
;
357 hdr
->text
= le32toh(ex
->a_text
);
358 hdr
->data
= le32toh(ex
->a_data
);
359 hdr
->bss
= le32toh(ex
->a_bss
);
360 hdr
->entry
= le32toh(ex
->a_entry
);
361 if (le32toh(ex
->a_entry
) >= BTX_PGSIZE
)
362 hdr
->org
= BTX_PGSIZE
;
367 if (hdr
->size
>= sizeof(Elf32_Ehdr
) && IS_ELF(*ee
)) {
369 for (n
= i
= 0; i
< le16toh(ee
->e_phnum
); i
++) {
370 ep
= (void *)((uint8_t *)p
+ le32toh(ee
->e_phoff
) +
371 le16toh(ee
->e_phentsize
) * i
);
372 if (le32toh(ep
->p_type
) == PT_LOAD
)
375 hdr
->text
= le32toh(ep
->p_filesz
);
376 hdr
->org
= le32toh(ep
->p_paddr
);
377 if (le32toh(ep
->p_flags
) & PF_W
)
378 hdr
->flags
|= IMPURE
;
381 hdr
->data
= le32toh(ep
->p_filesz
);
382 hdr
->bss
= le32toh(ep
->p_memsz
) -
383 le32toh(ep
->p_filesz
);
387 "Ignoring extra %s PT_LOAD segments",
391 hdr
->entry
= le32toh(ee
->e_entry
);
394 if (munmap(p
, hdr
->size
))
399 * Write a.out or ELF header.
402 puthdr(int fd
, struct hdr
*hdr
)
409 memset(&ex
, 0, sizeof(ex
));
410 N_SETMAGIC(ex
, ZMAGIC
, MID_ZERO
, 0);
411 hdr
->text
= N_ALIGN(ex
, hdr
->text
);
412 ex
.a_text
= htole32(hdr
->text
);
413 hdr
->data
= N_ALIGN(ex
, hdr
->data
);
414 ex
.a_data
= htole32(hdr
->data
);
415 ex
.a_entry
= htole32(hdr
->entry
);
416 writex(fd
, &ex
, sizeof(ex
));
417 hdr
->size
= N_ALIGN(ex
, sizeof(ex
));
418 seekx(fd
, hdr
->size
);
422 eh
.e
.e_entry
= htole32(hdr
->entry
);
423 eh
.p
[0].p_vaddr
= eh
.p
[0].p_paddr
= htole32(hdr
->org
);
424 eh
.p
[0].p_filesz
= eh
.p
[0].p_memsz
= htole32(hdr
->text
);
425 eh
.p
[1].p_offset
= htole32(le32toh(eh
.p
[0].p_offset
) +
426 le32toh(eh
.p
[0].p_filesz
));
427 eh
.p
[1].p_vaddr
= eh
.p
[1].p_paddr
=
428 htole32(align(le32toh(eh
.p
[0].p_paddr
) + le32toh(eh
.p
[0].p_memsz
),
430 eh
.p
[1].p_filesz
= eh
.p
[1].p_memsz
= htole32(hdr
->data
);
431 eh
.sh
[2].sh_addr
= eh
.p
[0].p_vaddr
;
432 eh
.sh
[2].sh_offset
= eh
.p
[0].p_offset
;
433 eh
.sh
[2].sh_size
= eh
.p
[0].p_filesz
;
434 eh
.sh
[3].sh_addr
= eh
.p
[1].p_vaddr
;
435 eh
.sh
[3].sh_offset
= eh
.p
[1].p_offset
;
436 eh
.sh
[3].sh_size
= eh
.p
[1].p_filesz
;
437 writex(fd
, &eh
, sizeof(eh
));
438 hdr
->size
= sizeof(eh
);
443 * Safe copy from input file to output file.
446 copy(int fdi
, int fdo
, size_t nbyte
, off_t offset
)
452 if ((n
= sizeof(buf
)) > nbyte
)
454 if (readx(fdi
, buf
, n
, offset
) != n
)
455 errx(2, "%s: Short read", fname
);
463 * Safe read from input file.
466 readx(int fd
, void *buf
, size_t nbyte
, off_t offset
)
470 if (offset
!= -1 && lseek(fd
, offset
, SEEK_SET
) != offset
)
472 if ((n
= read(fd
, buf
, nbyte
)) == -1)
478 * Safe write to output file.
481 writex(int fd
, const void *buf
, size_t nbyte
)
485 if ((n
= write(fd
, buf
, nbyte
)) == -1)
487 if ((size_t)n
!= nbyte
)
488 errx(2, "%s: Short write", tname
);
492 * Safe seek in output file.
495 seekx(int fd
, off_t offset
)
497 if (lseek(fd
, offset
, SEEK_SET
) != offset
)
502 * Convert an option argument to a format code.
505 optfmt(const char *arg
)
509 for (i
= 0; i
< F_CNT
&& strcmp(arg
, fmtlist
[i
]); i
++);
511 errx(1, "%s: Unknown format", arg
);
516 * Convert an option argument to an address.
519 optaddr(const char *arg
)
525 x
= strtoul(arg
, &s
, 0);
526 if (errno
|| !*arg
|| *s
|| x
> MAXU32
)
527 errx(1, "%s: Illegal address", arg
);
532 * Convert an option argument to a page number.
535 optpage(const char *arg
, int hi
)
541 x
= strtol(arg
, &s
, 0);
542 if (errno
|| !*arg
|| *s
|| x
< 0 || x
> hi
)
543 errx(1, "%s: Illegal page number", arg
);
551 Warn(const char *locus
, const char *fmt
, ...)
557 asprintf(&s
, "%s: Warning: %s", locus
, fmt
);
566 * Display usage information.
571 fprintf(stderr
, "%s\n%s\n",
572 "usage: btxld [-qv] [-b file] [-E address] [-e address] [-f format]",
573 " [-l file] [-o filename] [-P page] [-W page] file");