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.
27 #include <sys/param.h>
32 /* XXX make this work as an i386/amd64 cross-tool */
36 #include <netinet/in.h>
38 #include "imgact_aout.h"
51 #define BTX_PATH "/sys/boot/i386/btx"
53 #define I_LDR 0 /* BTX loader */
54 #define I_BTX 1 /* BTX kernel */
55 #define I_CLNT 2 /* Client program */
57 #define F_BIN 0 /* Binary */
58 #define F_AOUT 1 /* ZMAGIC a.out */
59 #define F_ELF 2 /* 32-bit ELF */
60 #define F_CNT 3 /* Number of formats */
62 #define IMPURE 1 /* Writable text */
63 #define MAXU32 0xffffffff /* Maximum unsigned 32-bit quantity */
65 #define align(x, y) (((x) + (y) - 1) & ~((y) - 1))
68 uint32_t fmt
; /* Format */
69 uint32_t flags
; /* Bit flags */
70 uint32_t size
; /* Size of file */
71 uint32_t text
; /* Size of text segment */
72 uint32_t data
; /* Size of data segment */
73 uint32_t bss
; /* Size of bss segment */
74 uint32_t org
; /* Program origin */
75 uint32_t entry
; /* Program entry point */
78 static const char *const fmtlist
[] = {"bin", "aout", "elf"};
80 static const char binfo
[] =
81 "kernel: ver=%u.%02u size=%x load=%x entry=%x map=%uM "
83 static const char cinfo
[] =
84 "client: fmt=%s size=%x text=%x data=%x bss=%x entry=%x\n";
85 static const char oinfo
[] =
86 "output: fmt=%s size=%x text=%x data=%x org=%x entry=%x\n";
88 /* BTX loader and kernel is only provided from command line */
89 static const char *lname
= NULL
;
90 static const char *bname
= NULL
;
91 static const char *oname
=
92 "a.out"; /* Output filename */
94 static int ppage
= -1; /* First page present */
95 static int wpage
= -1; /* First page writable */
97 static unsigned int format
; /* Output format */
99 static uint32_t centry
; /* Client entry address */
100 static uint32_t lentry
; /* Loader entry address */
102 static int Eflag
; /* Client entry option */
104 static int quiet
; /* Inhibit warnings */
105 static int verbose
; /* Display information */
107 static const char *tname
; /* Temporary output file */
108 static const char *fname
; /* Current input file */
110 static void cleanup(void);
111 static void btxld(const char *);
112 static void getbtx(int, struct btx_hdr
*);
113 static void gethdr(int, struct hdr
*);
114 static void puthdr(int, struct hdr
*);
115 static void copy(int, int, size_t, off_t
);
116 static size_t readx(int, void *, size_t, off_t
);
117 static void writex(int, const void *, size_t);
118 static void seekx(int, off_t
);
119 static unsigned int optfmt(const char *);
120 static uint32_t optaddr(const char *);
121 static int optpage(const char *, int);
122 static void Warn(const char *, const char *, ...);
123 static void usage(void);
124 extern void add_version(const char *, char *);
127 * A link editor for BTX clients.
130 main(int argc
, char *argv
[])
133 char *version
= NULL
;
135 while ((c
= getopt(argc
, argv
, "qvb:E:e:f:l:o:P:V:W:")) != -1)
147 centry
= optaddr(optarg
);
151 lentry
= optaddr(optarg
);
154 format
= optfmt(optarg
);
163 ppage
= optpage(optarg
, 1);
169 wpage
= optpage(optarg
, BTX_MAXCWR
);
179 if (lname
!= NULL
&& bname
!= NULL
)
182 add_version(oname
, version
);
187 * Clean up after errors.
197 * Read the input files; write the output file; display information.
200 btxld(const char *iname
)
202 char name
[FILENAME_MAX
];
203 struct btx_hdr btx
, btxle
;
204 struct hdr ihdr
, ohdr
;
205 unsigned int ldr_size
, cwr
;
210 for (i
= I_LDR
; i
<= I_CLNT
; i
++) {
211 fname
= i
== I_LDR
? lname
: i
== I_BTX
? bname
: iname
;
212 if ((fdi
[i
] = open(fname
, O_RDONLY
)) == -1)
216 gethdr(fdi
[i
], &ihdr
);
217 if (ihdr
.fmt
!= F_BIN
)
218 Warn(fname
, "Loader format is %s; processing as %s",
219 fmtlist
[ihdr
.fmt
], fmtlist
[F_BIN
]);
220 ldr_size
= ihdr
.size
;
223 getbtx(fdi
[i
], &btx
);
226 gethdr(fdi
[i
], &ihdr
);
227 if (ihdr
.org
&& ihdr
.org
!= BTX_PGSIZE
)
229 "Client origin is 0x%x; expecting 0 or 0x%x",
230 ihdr
.org
, BTX_PGSIZE
);
233 memset(&ohdr
, 0, sizeof(ohdr
));
235 ohdr
.text
= ldr_size
;
236 ohdr
.data
= btx
.btx_textsz
+ ihdr
.size
;
240 if (wpage
> 0 || (wpage
== -1 && !(ihdr
.flags
& IMPURE
))) {
244 cwr
= howmany(ihdr
.text
, BTX_PGSIZE
);
245 if (cwr
> BTX_MAXCWR
)
249 if (ppage
> 0 || (ppage
&& wpage
&& ihdr
.org
>= BTX_PGSIZE
)) {
250 btx
.btx_flags
|= BTX_MAPONE
;
254 btx
.btx_pgctl
-= cwr
;
255 btx
.btx_entry
= Eflag
? centry
: ihdr
.entry
;
256 if ((size_t)snprintf(name
, sizeof(name
), "%s.tmp", oname
) >= sizeof(name
))
257 errx(2, "%s: Filename too long", oname
);
258 if ((fdo
= open(name
, O_CREAT
| O_TRUNC
| O_WRONLY
, 0666)) == -1)
260 if (!(tname
= strdup(name
)))
263 for (i
= I_LDR
; i
<= I_CLNT
; i
++) {
264 fname
= i
== I_LDR
? lname
: i
== I_BTX
? bname
: iname
;
267 copy(fdi
[i
], fdo
, ldr_size
, 0);
268 seekx(fdo
, ohdr
.size
+= ohdr
.text
);
272 btxle
.btx_pgctl
= htole16(btxle
.btx_pgctl
);
273 btxle
.btx_textsz
= htole16(btxle
.btx_textsz
);
274 btxle
.btx_entry
= htole32(btxle
.btx_entry
);
275 writex(fdo
, &btxle
, sizeof(btxle
));
276 copy(fdi
[i
], fdo
, btx
.btx_textsz
- sizeof(btx
),
280 copy(fdi
[i
], fdo
, ihdr
.size
, 0);
281 if (ftruncate(fdo
, ohdr
.size
+= ohdr
.data
))
289 if (rename(tname
, oname
))
290 err(2, "%s: Can't rename to %s", tname
, oname
);
293 printf(binfo
, btx
.btx_majver
, btx
.btx_minver
, btx
.btx_textsz
,
294 BTX_ORIGIN(btx
), BTX_ENTRY(btx
), BTX_MAPPED(btx
) *
295 BTX_PGSIZE
/ 0x100000, !!(btx
.btx_flags
& BTX_MAPONE
),
296 BTX_MAPPED(btx
) - btx
.btx_pgctl
- BTX_PGBASE
/
297 BTX_PGSIZE
- BTX_MAPPED(btx
) * 4 / BTX_PGSIZE
);
298 printf(cinfo
, fmtlist
[ihdr
.fmt
], ihdr
.size
, ihdr
.text
,
299 ihdr
.data
, ihdr
.bss
, ihdr
.entry
);
300 printf(oinfo
, fmtlist
[ohdr
.fmt
], ohdr
.size
, ohdr
.text
,
301 ohdr
.data
, ohdr
.org
, ohdr
.entry
);
306 * Read BTX file header.
309 getbtx(int fd
, struct btx_hdr
* btx
)
311 if (readx(fd
, btx
, sizeof(*btx
), 0) != sizeof(*btx
) ||
312 btx
->btx_magic
[0] != BTX_MAG0
||
313 btx
->btx_magic
[1] != BTX_MAG1
||
314 btx
->btx_magic
[2] != BTX_MAG2
)
315 errx(1, "%s: Not a BTX kernel", fname
);
316 btx
->btx_pgctl
= le16toh(btx
->btx_pgctl
);
317 btx
->btx_textsz
= le16toh(btx
->btx_textsz
);
318 btx
->btx_entry
= le32toh(btx
->btx_entry
);
322 * Get file size and read a.out or ELF header.
325 gethdr(int fd
, struct hdr
*hdr
)
328 const struct exec
*ex
;
329 const Elf32_Ehdr
*ee
;
330 const Elf32_Phdr
*ep
;
332 unsigned int fmt
, x
, n
, i
;
334 memset(hdr
, 0, sizeof(*hdr
));
337 if (sb
.st_size
> MAXU32
)
338 errx(1, "%s: Too big", fname
);
339 hdr
->size
= sb
.st_size
;
342 if ((p
= mmap(NULL
, hdr
->size
, PROT_READ
, MAP_SHARED
, fd
,
345 for (fmt
= F_CNT
- 1; !hdr
->fmt
&& fmt
; fmt
--)
349 if (hdr
->size
>= sizeof(struct exec
) && !N_BADMAG(*ex
)) {
352 if (x
== OMAGIC
|| x
== NMAGIC
) {
354 Warn(fname
, "Treating %s NMAGIC as OMAGIC",
356 hdr
->flags
|= IMPURE
;
358 hdr
->text
= le32toh(ex
->a_text
);
359 hdr
->data
= le32toh(ex
->a_data
);
360 hdr
->bss
= le32toh(ex
->a_bss
);
361 hdr
->entry
= le32toh(ex
->a_entry
);
362 if (le32toh(ex
->a_entry
) >= BTX_PGSIZE
)
363 hdr
->org
= BTX_PGSIZE
;
368 if (hdr
->size
>= sizeof(Elf32_Ehdr
) && IS_ELF(*ee
)) {
370 for (n
= i
= 0; i
< le16toh(ee
->e_phnum
); i
++) {
371 ep
= (void *)((uint8_t *)p
+ le32toh(ee
->e_phoff
) +
372 le16toh(ee
->e_phentsize
) * i
);
373 if (le32toh(ep
->p_type
) == PT_LOAD
)
376 hdr
->text
= le32toh(ep
->p_filesz
);
377 hdr
->org
= le32toh(ep
->p_paddr
);
378 if (le32toh(ep
->p_flags
) & PF_W
)
379 hdr
->flags
|= IMPURE
;
382 hdr
->data
= le32toh(ep
->p_filesz
);
383 hdr
->bss
= le32toh(ep
->p_memsz
) -
384 le32toh(ep
->p_filesz
);
388 "Ignoring extra %s PT_LOAD segments",
392 hdr
->entry
= le32toh(ee
->e_entry
);
395 if (munmap(p
, hdr
->size
))
400 * Write a.out or ELF header.
403 puthdr(int fd
, struct hdr
*hdr
)
410 memset(&ex
, 0, sizeof(ex
));
411 N_SETMAGIC(ex
, ZMAGIC
, MID_I386
, 0);
412 hdr
->text
= N_ALIGN(ex
, hdr
->text
);
413 ex
.a_text
= htole32(hdr
->text
);
414 hdr
->data
= N_ALIGN(ex
, hdr
->data
);
415 ex
.a_data
= htole32(hdr
->data
);
416 ex
.a_entry
= htole32(hdr
->entry
);
417 writex(fd
, &ex
, sizeof(ex
));
418 hdr
->size
= N_ALIGN(ex
, sizeof(ex
));
419 seekx(fd
, hdr
->size
);
423 eh
.e
.e_entry
= htole32(hdr
->entry
);
424 eh
.p
[0].p_vaddr
= eh
.p
[0].p_paddr
= htole32(hdr
->org
);
425 eh
.p
[0].p_filesz
= eh
.p
[0].p_memsz
= htole32(hdr
->text
);
426 eh
.p
[1].p_offset
= htole32(le32toh(eh
.p
[0].p_offset
) +
427 le32toh(eh
.p
[0].p_filesz
));
428 eh
.p
[1].p_vaddr
= eh
.p
[1].p_paddr
=
429 htole32(align(le32toh(eh
.p
[0].p_paddr
) + le32toh(eh
.p
[0].p_memsz
),
431 eh
.p
[1].p_filesz
= eh
.p
[1].p_memsz
= htole32(hdr
->data
);
432 eh
.sh
[2].sh_addr
= eh
.p
[0].p_vaddr
;
433 eh
.sh
[2].sh_offset
= eh
.p
[0].p_offset
;
434 eh
.sh
[2].sh_size
= eh
.p
[0].p_filesz
;
435 eh
.sh
[3].sh_addr
= eh
.p
[1].p_vaddr
;
436 eh
.sh
[3].sh_offset
= eh
.p
[1].p_offset
;
437 eh
.sh
[3].sh_size
= eh
.p
[1].p_filesz
;
438 writex(fd
, &eh
, sizeof(eh
));
439 hdr
->size
= sizeof(eh
);
444 * Safe copy from input file to output file.
447 copy(int fdi
, int fdo
, size_t nbyte
, off_t offset
)
453 if ((n
= sizeof(buf
)) > nbyte
)
455 if (readx(fdi
, buf
, n
, offset
) != n
)
456 errx(2, "%s: Short read", fname
);
464 * Safe read from input file.
467 readx(int fd
, void *buf
, size_t nbyte
, off_t offset
)
471 if (offset
!= -1 && lseek(fd
, offset
, SEEK_SET
) != offset
)
473 if ((n
= read(fd
, buf
, nbyte
)) == -1)
479 * Safe write to output file.
482 writex(int fd
, const void *buf
, size_t nbyte
)
486 if ((n
= write(fd
, buf
, nbyte
)) == -1)
488 if ((size_t)n
!= nbyte
)
489 errx(2, "%s: Short write", tname
);
493 * Safe seek in output file.
496 seekx(int fd
, off_t offset
)
498 if (lseek(fd
, offset
, SEEK_SET
) != offset
)
503 * Convert an option argument to a format code.
506 optfmt(const char *arg
)
510 for (i
= 0; i
< F_CNT
&& strcmp(arg
, fmtlist
[i
]); i
++);
512 errx(1, "%s: Unknown format", arg
);
517 * Convert an option argument to an address.
520 optaddr(const char *arg
)
526 x
= strtoul(arg
, &s
, 0);
527 if (errno
|| !*arg
|| *s
|| x
> MAXU32
)
528 errx(1, "%s: Illegal address", arg
);
533 * Convert an option argument to a page number.
536 optpage(const char *arg
, int hi
)
542 x
= strtol(arg
, &s
, 0);
543 if (errno
|| !*arg
|| *s
|| x
< 0 || x
> hi
)
544 errx(1, "%s: Illegal page number", arg
);
552 Warn(const char *locus
, const char *fmt
, ...)
558 asprintf(&s
, "%s: Warning: %s", locus
, fmt
);
567 * Display usage information.
572 fprintf(stderr
, "%s\n%s\n",
573 "usage: btxld [-qv] [-b file] [-E address] [-e address] [-f format]",
574 " [-l file] [-o filename] [-P page] [-W page] file");