• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 1995
3  *	Ted Lemon (hereinafter referred to as the author)
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  * 3. The name of the author may not be used to endorse or promote products
14  *    derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 /* elf2ecoff.c
30 
31    This program converts an elf executable to an ECOFF executable.
32    No symbol table is retained.	  This is useful primarily in building
33    net-bootable kernels for machines (e.g., DECstation and Alpha) which
34    only support the ECOFF object file format. */
35 
36 #include <stdio.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <sys/types.h>
40 #include <fcntl.h>
41 #include <unistd.h>
42 #include <elf.h>
43 #include <limits.h>
44 #include <netinet/in.h>
45 #include <stdlib.h>
46 
47 #include "ecoff.h"
48 
49 /*
50  * Some extra ELF definitions
51  */
52 #define PT_MIPS_REGINFO 	0x70000000	/* Register usage information */
53 #define PT_MIPS_ABIFLAGS	0x70000003	/* Records ABI related flags  */
54 
55 /* -------------------------------------------------------------------- */
56 
57 struct sect {
58 	unsigned long vaddr;
59 	unsigned long len;
60 };
61 
62 int *symTypeTable;
63 int must_convert_endian;
64 int format_bigendian;
65 
copy(int out,int in,off_t offset,off_t size)66 static void copy(int out, int in, off_t offset, off_t size)
67 {
68 	char ibuf[4096];
69 	int remaining, cur, count;
70 
71 	/* Go to the start of the ELF symbol table... */
72 	if (lseek(in, offset, SEEK_SET) < 0) {
73 		perror("copy: lseek");
74 		exit(1);
75 	}
76 
77 	remaining = size;
78 	while (remaining) {
79 		cur = remaining;
80 		if (cur > sizeof ibuf)
81 			cur = sizeof ibuf;
82 		remaining -= cur;
83 		if ((count = read(in, ibuf, cur)) != cur) {
84 			fprintf(stderr, "copy: read: %s\n",
85 				count ? strerror(errno) :
86 				"premature end of file");
87 			exit(1);
88 		}
89 		if ((count = write(out, ibuf, cur)) != cur) {
90 			perror("copy: write");
91 			exit(1);
92 		}
93 	}
94 }
95 
96 /*
97  * Combine two segments, which must be contiguous.   If pad is true, it's
98  * okay for there to be padding between.
99  */
combine(struct sect * base,struct sect * new,int pad)100 static void combine(struct sect *base, struct sect *new, int pad)
101 {
102 	if (!base->len)
103 		*base = *new;
104 	else if (new->len) {
105 		if (base->vaddr + base->len != new->vaddr) {
106 			if (pad)
107 				base->len = new->vaddr - base->vaddr;
108 			else {
109 				fprintf(stderr,
110 					"Non-contiguous data can't be converted.\n");
111 				exit(1);
112 			}
113 		}
114 		base->len += new->len;
115 	}
116 }
117 
phcmp(const void * v1,const void * v2)118 static int phcmp(const void *v1, const void *v2)
119 {
120 	const Elf32_Phdr *h1 = v1;
121 	const Elf32_Phdr *h2 = v2;
122 
123 	if (h1->p_vaddr > h2->p_vaddr)
124 		return 1;
125 	else if (h1->p_vaddr < h2->p_vaddr)
126 		return -1;
127 	else
128 		return 0;
129 }
130 
saveRead(int file,off_t offset,off_t len,char * name)131 static char *saveRead(int file, off_t offset, off_t len, char *name)
132 {
133 	char *tmp;
134 	int count;
135 	off_t off;
136 	if ((off = lseek(file, offset, SEEK_SET)) < 0) {
137 		fprintf(stderr, "%s: fseek: %s\n", name, strerror(errno));
138 		exit(1);
139 	}
140 	if (!(tmp = (char *) malloc(len))) {
141 		fprintf(stderr, "%s: Can't allocate %ld bytes.\n", name,
142 			len);
143 		exit(1);
144 	}
145 	count = read(file, tmp, len);
146 	if (count != len) {
147 		fprintf(stderr, "%s: read: %s.\n",
148 			name,
149 			count ? strerror(errno) : "End of file reached");
150 		exit(1);
151 	}
152 	return tmp;
153 }
154 
155 #define swab16(x) \
156 	((unsigned short)( \
157 		(((unsigned short)(x) & (unsigned short)0x00ffU) << 8) | \
158 		(((unsigned short)(x) & (unsigned short)0xff00U) >> 8) ))
159 
160 #define swab32(x) \
161 	((unsigned int)( \
162 		(((unsigned int)(x) & (unsigned int)0x000000ffUL) << 24) | \
163 		(((unsigned int)(x) & (unsigned int)0x0000ff00UL) <<  8) | \
164 		(((unsigned int)(x) & (unsigned int)0x00ff0000UL) >>  8) | \
165 		(((unsigned int)(x) & (unsigned int)0xff000000UL) >> 24) ))
166 
convert_elf_hdr(Elf32_Ehdr * e)167 static void convert_elf_hdr(Elf32_Ehdr * e)
168 {
169 	e->e_type = swab16(e->e_type);
170 	e->e_machine = swab16(e->e_machine);
171 	e->e_version = swab32(e->e_version);
172 	e->e_entry = swab32(e->e_entry);
173 	e->e_phoff = swab32(e->e_phoff);
174 	e->e_shoff = swab32(e->e_shoff);
175 	e->e_flags = swab32(e->e_flags);
176 	e->e_ehsize = swab16(e->e_ehsize);
177 	e->e_phentsize = swab16(e->e_phentsize);
178 	e->e_phnum = swab16(e->e_phnum);
179 	e->e_shentsize = swab16(e->e_shentsize);
180 	e->e_shnum = swab16(e->e_shnum);
181 	e->e_shstrndx = swab16(e->e_shstrndx);
182 }
183 
convert_elf_phdrs(Elf32_Phdr * p,int num)184 static void convert_elf_phdrs(Elf32_Phdr * p, int num)
185 {
186 	int i;
187 
188 	for (i = 0; i < num; i++, p++) {
189 		p->p_type = swab32(p->p_type);
190 		p->p_offset = swab32(p->p_offset);
191 		p->p_vaddr = swab32(p->p_vaddr);
192 		p->p_paddr = swab32(p->p_paddr);
193 		p->p_filesz = swab32(p->p_filesz);
194 		p->p_memsz = swab32(p->p_memsz);
195 		p->p_flags = swab32(p->p_flags);
196 		p->p_align = swab32(p->p_align);
197 	}
198 
199 }
200 
convert_elf_shdrs(Elf32_Shdr * s,int num)201 static void convert_elf_shdrs(Elf32_Shdr * s, int num)
202 {
203 	int i;
204 
205 	for (i = 0; i < num; i++, s++) {
206 		s->sh_name = swab32(s->sh_name);
207 		s->sh_type = swab32(s->sh_type);
208 		s->sh_flags = swab32(s->sh_flags);
209 		s->sh_addr = swab32(s->sh_addr);
210 		s->sh_offset = swab32(s->sh_offset);
211 		s->sh_size = swab32(s->sh_size);
212 		s->sh_link = swab32(s->sh_link);
213 		s->sh_info = swab32(s->sh_info);
214 		s->sh_addralign = swab32(s->sh_addralign);
215 		s->sh_entsize = swab32(s->sh_entsize);
216 	}
217 }
218 
convert_ecoff_filehdr(struct filehdr * f)219 static void convert_ecoff_filehdr(struct filehdr *f)
220 {
221 	f->f_magic = swab16(f->f_magic);
222 	f->f_nscns = swab16(f->f_nscns);
223 	f->f_timdat = swab32(f->f_timdat);
224 	f->f_symptr = swab32(f->f_symptr);
225 	f->f_nsyms = swab32(f->f_nsyms);
226 	f->f_opthdr = swab16(f->f_opthdr);
227 	f->f_flags = swab16(f->f_flags);
228 }
229 
convert_ecoff_aouthdr(struct aouthdr * a)230 static void convert_ecoff_aouthdr(struct aouthdr *a)
231 {
232 	a->magic = swab16(a->magic);
233 	a->vstamp = swab16(a->vstamp);
234 	a->tsize = swab32(a->tsize);
235 	a->dsize = swab32(a->dsize);
236 	a->bsize = swab32(a->bsize);
237 	a->entry = swab32(a->entry);
238 	a->text_start = swab32(a->text_start);
239 	a->data_start = swab32(a->data_start);
240 	a->bss_start = swab32(a->bss_start);
241 	a->gprmask = swab32(a->gprmask);
242 	a->cprmask[0] = swab32(a->cprmask[0]);
243 	a->cprmask[1] = swab32(a->cprmask[1]);
244 	a->cprmask[2] = swab32(a->cprmask[2]);
245 	a->cprmask[3] = swab32(a->cprmask[3]);
246 	a->gp_value = swab32(a->gp_value);
247 }
248 
convert_ecoff_esecs(struct scnhdr * s,int num)249 static void convert_ecoff_esecs(struct scnhdr *s, int num)
250 {
251 	int i;
252 
253 	for (i = 0; i < num; i++, s++) {
254 		s->s_paddr = swab32(s->s_paddr);
255 		s->s_vaddr = swab32(s->s_vaddr);
256 		s->s_size = swab32(s->s_size);
257 		s->s_scnptr = swab32(s->s_scnptr);
258 		s->s_relptr = swab32(s->s_relptr);
259 		s->s_lnnoptr = swab32(s->s_lnnoptr);
260 		s->s_nreloc = swab16(s->s_nreloc);
261 		s->s_nlnno = swab16(s->s_nlnno);
262 		s->s_flags = swab32(s->s_flags);
263 	}
264 }
265 
main(int argc,char * argv[])266 int main(int argc, char *argv[])
267 {
268 	Elf32_Ehdr ex;
269 	Elf32_Phdr *ph;
270 	Elf32_Shdr *sh;
271 	int i, pad;
272 	struct sect text, data, bss;
273 	struct filehdr efh;
274 	struct aouthdr eah;
275 	struct scnhdr esecs[6];
276 	int infile, outfile;
277 	unsigned long cur_vma = ULONG_MAX;
278 	int addflag = 0;
279 	int nosecs;
280 
281 	text.len = data.len = bss.len = 0;
282 	text.vaddr = data.vaddr = bss.vaddr = 0;
283 
284 	/* Check args... */
285 	if (argc < 3 || argc > 4) {
286 	      usage:
287 		fprintf(stderr,
288 			"usage: elf2ecoff <elf executable> <ecoff executable> [-a]\n");
289 		exit(1);
290 	}
291 	if (argc == 4) {
292 		if (strcmp(argv[3], "-a"))
293 			goto usage;
294 		addflag = 1;
295 	}
296 
297 	/* Try the input file... */
298 	if ((infile = open(argv[1], O_RDONLY)) < 0) {
299 		fprintf(stderr, "Can't open %s for read: %s\n",
300 			argv[1], strerror(errno));
301 		exit(1);
302 	}
303 
304 	/* Read the header, which is at the beginning of the file... */
305 	i = read(infile, &ex, sizeof ex);
306 	if (i != sizeof ex) {
307 		fprintf(stderr, "ex: %s: %s.\n",
308 			argv[1],
309 			i ? strerror(errno) : "End of file reached");
310 		exit(1);
311 	}
312 
313 	if (ex.e_ident[EI_DATA] == ELFDATA2MSB)
314 		format_bigendian = 1;
315 
316 	if (ntohs(0xaa55) == 0xaa55) {
317 		if (!format_bigendian)
318 			must_convert_endian = 1;
319 	} else {
320 		if (format_bigendian)
321 			must_convert_endian = 1;
322 	}
323 	if (must_convert_endian)
324 		convert_elf_hdr(&ex);
325 
326 	/* Read the program headers... */
327 	ph = (Elf32_Phdr *) saveRead(infile, ex.e_phoff,
328 				     ex.e_phnum * sizeof(Elf32_Phdr),
329 				     "ph");
330 	if (must_convert_endian)
331 		convert_elf_phdrs(ph, ex.e_phnum);
332 	/* Read the section headers... */
333 	sh = (Elf32_Shdr *) saveRead(infile, ex.e_shoff,
334 				     ex.e_shnum * sizeof(Elf32_Shdr),
335 				     "sh");
336 	if (must_convert_endian)
337 		convert_elf_shdrs(sh, ex.e_shnum);
338 
339 	/* Figure out if we can cram the program header into an ECOFF
340 	   header...  Basically, we can't handle anything but loadable
341 	   segments, but we can ignore some kinds of segments.	We can't
342 	   handle holes in the address space.  Segments may be out of order,
343 	   so we sort them first. */
344 
345 	qsort(ph, ex.e_phnum, sizeof(Elf32_Phdr), phcmp);
346 
347 	for (i = 0; i < ex.e_phnum; i++) {
348 		/* Section types we can ignore... */
349 		switch (ph[i].p_type) {
350 		case PT_NULL:
351 		case PT_NOTE:
352 		case PT_PHDR:
353 		case PT_MIPS_REGINFO:
354 		case PT_MIPS_ABIFLAGS:
355 			continue;
356 
357 		case PT_LOAD:
358 			/* Writable (data) segment? */
359 			if (ph[i].p_flags & PF_W) {
360 				struct sect ndata, nbss;
361 
362 				ndata.vaddr = ph[i].p_vaddr;
363 				ndata.len = ph[i].p_filesz;
364 				nbss.vaddr = ph[i].p_vaddr + ph[i].p_filesz;
365 				nbss.len = ph[i].p_memsz - ph[i].p_filesz;
366 
367 				combine(&data, &ndata, 0);
368 				combine(&bss, &nbss, 1);
369 			} else {
370 				struct sect ntxt;
371 
372 				ntxt.vaddr = ph[i].p_vaddr;
373 				ntxt.len = ph[i].p_filesz;
374 
375 				combine(&text, &ntxt, 0);
376 			}
377 			/* Remember the lowest segment start address. */
378 			if (ph[i].p_vaddr < cur_vma)
379 				cur_vma = ph[i].p_vaddr;
380 			break;
381 
382 		default:
383 			/* Section types we can't handle... */
384 			fprintf(stderr,
385 				"Program header %d type %d can't be converted.\n",
386 				ex.e_phnum, ph[i].p_type);
387 			exit(1);
388 		}
389 	}
390 
391 	/* Sections must be in order to be converted... */
392 	if (text.vaddr > data.vaddr || data.vaddr > bss.vaddr ||
393 	    text.vaddr + text.len > data.vaddr
394 	    || data.vaddr + data.len > bss.vaddr) {
395 		fprintf(stderr,
396 			"Sections ordering prevents a.out conversion.\n");
397 		exit(1);
398 	}
399 
400 	/* If there's a data section but no text section, then the loader
401 	   combined everything into one section.   That needs to be the
402 	   text section, so just make the data section zero length following
403 	   text. */
404 	if (data.len && !text.len) {
405 		text = data;
406 		data.vaddr = text.vaddr + text.len;
407 		data.len = 0;
408 	}
409 
410 	/* If there is a gap between text and data, we'll fill it when we copy
411 	   the data, so update the length of the text segment as represented in
412 	   a.out to reflect that, since a.out doesn't allow gaps in the program
413 	   address space. */
414 	if (text.vaddr + text.len < data.vaddr)
415 		text.len = data.vaddr - text.vaddr;
416 
417 	/* We now have enough information to cons up an a.out header... */
418 	eah.magic = OMAGIC;
419 	eah.vstamp = 200;
420 	eah.tsize = text.len;
421 	eah.dsize = data.len;
422 	eah.bsize = bss.len;
423 	eah.entry = ex.e_entry;
424 	eah.text_start = text.vaddr;
425 	eah.data_start = data.vaddr;
426 	eah.bss_start = bss.vaddr;
427 	eah.gprmask = 0xf3fffffe;
428 	memset(&eah.cprmask, '\0', sizeof eah.cprmask);
429 	eah.gp_value = 0;	/* unused. */
430 
431 	if (format_bigendian)
432 		efh.f_magic = MIPSEBMAGIC;
433 	else
434 		efh.f_magic = MIPSELMAGIC;
435 	if (addflag)
436 		nosecs = 6;
437 	else
438 		nosecs = 3;
439 	efh.f_nscns = nosecs;
440 	efh.f_timdat = 0;	/* bogus */
441 	efh.f_symptr = 0;
442 	efh.f_nsyms = 0;
443 	efh.f_opthdr = sizeof eah;
444 	efh.f_flags = 0x100f;	/* Stripped, not sharable. */
445 
446 	memset(esecs, 0, sizeof esecs);
447 	strcpy(esecs[0].s_name, ".text");
448 	strcpy(esecs[1].s_name, ".data");
449 	strcpy(esecs[2].s_name, ".bss");
450 	if (addflag) {
451 		strcpy(esecs[3].s_name, ".rdata");
452 		strcpy(esecs[4].s_name, ".sdata");
453 		strcpy(esecs[5].s_name, ".sbss");
454 	}
455 	esecs[0].s_paddr = esecs[0].s_vaddr = eah.text_start;
456 	esecs[1].s_paddr = esecs[1].s_vaddr = eah.data_start;
457 	esecs[2].s_paddr = esecs[2].s_vaddr = eah.bss_start;
458 	if (addflag) {
459 		esecs[3].s_paddr = esecs[3].s_vaddr = 0;
460 		esecs[4].s_paddr = esecs[4].s_vaddr = 0;
461 		esecs[5].s_paddr = esecs[5].s_vaddr = 0;
462 	}
463 	esecs[0].s_size = eah.tsize;
464 	esecs[1].s_size = eah.dsize;
465 	esecs[2].s_size = eah.bsize;
466 	if (addflag) {
467 		esecs[3].s_size = 0;
468 		esecs[4].s_size = 0;
469 		esecs[5].s_size = 0;
470 	}
471 	esecs[0].s_scnptr = N_TXTOFF(efh, eah);
472 	esecs[1].s_scnptr = N_DATOFF(efh, eah);
473 #define ECOFF_SEGMENT_ALIGNMENT(a) 0x10
474 #define ECOFF_ROUND(s, a) (((s)+(a)-1)&~((a)-1))
475 	esecs[2].s_scnptr = esecs[1].s_scnptr +
476 	    ECOFF_ROUND(esecs[1].s_size, ECOFF_SEGMENT_ALIGNMENT(&eah));
477 	if (addflag) {
478 		esecs[3].s_scnptr = 0;
479 		esecs[4].s_scnptr = 0;
480 		esecs[5].s_scnptr = 0;
481 	}
482 	esecs[0].s_relptr = esecs[1].s_relptr = esecs[2].s_relptr = 0;
483 	esecs[0].s_lnnoptr = esecs[1].s_lnnoptr = esecs[2].s_lnnoptr = 0;
484 	esecs[0].s_nreloc = esecs[1].s_nreloc = esecs[2].s_nreloc = 0;
485 	esecs[0].s_nlnno = esecs[1].s_nlnno = esecs[2].s_nlnno = 0;
486 	if (addflag) {
487 		esecs[3].s_relptr = esecs[4].s_relptr
488 		    = esecs[5].s_relptr = 0;
489 		esecs[3].s_lnnoptr = esecs[4].s_lnnoptr
490 		    = esecs[5].s_lnnoptr = 0;
491 		esecs[3].s_nreloc = esecs[4].s_nreloc = esecs[5].s_nreloc =
492 		    0;
493 		esecs[3].s_nlnno = esecs[4].s_nlnno = esecs[5].s_nlnno = 0;
494 	}
495 	esecs[0].s_flags = 0x20;
496 	esecs[1].s_flags = 0x40;
497 	esecs[2].s_flags = 0x82;
498 	if (addflag) {
499 		esecs[3].s_flags = 0x100;
500 		esecs[4].s_flags = 0x200;
501 		esecs[5].s_flags = 0x400;
502 	}
503 
504 	/* Make the output file... */
505 	if ((outfile = open(argv[2], O_WRONLY | O_CREAT, 0777)) < 0) {
506 		fprintf(stderr, "Unable to create %s: %s\n", argv[2],
507 			strerror(errno));
508 		exit(1);
509 	}
510 
511 	if (must_convert_endian)
512 		convert_ecoff_filehdr(&efh);
513 	/* Write the headers... */
514 	i = write(outfile, &efh, sizeof efh);
515 	if (i != sizeof efh) {
516 		perror("efh: write");
517 		exit(1);
518 
519 		for (i = 0; i < nosecs; i++) {
520 			printf
521 			    ("Section %d: %s phys %lx  size %lx	 file offset %lx\n",
522 			     i, esecs[i].s_name, esecs[i].s_paddr,
523 			     esecs[i].s_size, esecs[i].s_scnptr);
524 		}
525 	}
526 	fprintf(stderr, "wrote %d byte file header.\n", i);
527 
528 	if (must_convert_endian)
529 		convert_ecoff_aouthdr(&eah);
530 	i = write(outfile, &eah, sizeof eah);
531 	if (i != sizeof eah) {
532 		perror("eah: write");
533 		exit(1);
534 	}
535 	fprintf(stderr, "wrote %d byte a.out header.\n", i);
536 
537 	if (must_convert_endian)
538 		convert_ecoff_esecs(&esecs[0], nosecs);
539 	i = write(outfile, &esecs, nosecs * sizeof(struct scnhdr));
540 	if (i != nosecs * sizeof(struct scnhdr)) {
541 		perror("esecs: write");
542 		exit(1);
543 	}
544 	fprintf(stderr, "wrote %d bytes of section headers.\n", i);
545 
546 	pad = (sizeof(efh) + sizeof(eah) + nosecs * sizeof(struct scnhdr)) & 15;
547 	if (pad) {
548 		pad = 16 - pad;
549 		i = write(outfile, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0", pad);
550 		if (i < 0) {
551 			perror("ipad: write");
552 			exit(1);
553 		}
554 		fprintf(stderr, "wrote %d byte pad.\n", i);
555 	}
556 
557 	/*
558 	 * Copy the loadable sections.	 Zero-fill any gaps less than 64k;
559 	 * complain about any zero-filling, and die if we're asked to zero-fill
560 	 * more than 64k.
561 	 */
562 	for (i = 0; i < ex.e_phnum; i++) {
563 		/* Unprocessable sections were handled above, so just verify that
564 		   the section can be loaded before copying. */
565 		if (ph[i].p_type == PT_LOAD && ph[i].p_filesz) {
566 			if (cur_vma != ph[i].p_vaddr) {
567 				unsigned long gap =
568 				    ph[i].p_vaddr - cur_vma;
569 				char obuf[1024];
570 				if (gap > 65536) {
571 					fprintf(stderr,
572 						"Intersegment gap (%ld bytes) too large.\n",
573 						gap);
574 					exit(1);
575 				}
576 				fprintf(stderr,
577 					"Warning: %ld byte intersegment gap.\n",
578 					gap);
579 				memset(obuf, 0, sizeof obuf);
580 				while (gap) {
581 					int count =
582 					    write(outfile, obuf,
583 						  (gap >
584 						   sizeof obuf ? sizeof
585 						   obuf : gap));
586 					if (count < 0) {
587 						fprintf(stderr,
588 							"Error writing gap: %s\n",
589 							strerror(errno));
590 						exit(1);
591 					}
592 					gap -= count;
593 				}
594 			}
595 			fprintf(stderr, "writing %d bytes...\n",
596 				ph[i].p_filesz);
597 			copy(outfile, infile, ph[i].p_offset,
598 			     ph[i].p_filesz);
599 			cur_vma = ph[i].p_vaddr + ph[i].p_filesz;
600 		}
601 	}
602 
603 	/*
604 	 * Write a page of padding for boot PROMS that read entire pages.
605 	 * Without this, they may attempt to read past the end of the
606 	 * data section, incur an error, and refuse to boot.
607 	 */
608 	{
609 		char obuf[4096];
610 		memset(obuf, 0, sizeof obuf);
611 		if (write(outfile, obuf, sizeof(obuf)) != sizeof(obuf)) {
612 			fprintf(stderr, "Error writing PROM padding: %s\n",
613 				strerror(errno));
614 			exit(1);
615 		}
616 	}
617 
618 	/* Looks like we won... */
619 	exit(0);
620 }
621