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