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