elf2ecoff.c revision 1.7
1/* $NetBSD: elf2ecoff.c,v 1.7 1997/07/07 00:02:16 jonathan Exp $ */ 2 3/* 4 * Copyright (c) 1995 5 * Ted Lemon (hereinafter referred to as the author) 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31/* elf2ecoff.c 32 33 This program converts an elf executable to an ECOFF executable. 34 No symbol table is retained. This is useful primarily in building 35 net-bootable kernels for machines (e.g., DECstation and Alpha) which 36 only support the ECOFF object file format. */ 37 38#include <sys/types.h> 39#include <fcntl.h> 40#include <unistd.h> 41#include <sys/exec.h> 42#include <sys/exec_elf.h> 43#include <sys/exec_aout.h> 44#include <stdio.h> 45#include <sys/exec_ecoff.h> 46#include <sys/errno.h> 47#include <stdlib.h> 48#include <string.h> 49#include <limits.h> 50 51 52/* Elf Program segment permissions, in program header flags field */ 53 54#define PF_X (1 << 0) /* Segment is executable */ 55#define PF_W (1 << 1) /* Segment is writable */ 56#define PF_R (1 << 2) /* Segment is readable */ 57#define PF_MASKPROC 0xF0000000 /* Processor-specific reserved bits */ 58 59 60#define ISLAST(p) (p->n_un.n_name == 0 || p->n_un.n_name[0] == 0) 61 62struct sect { 63 unsigned long vaddr; 64 unsigned long len; 65}; 66 67int debug = 0; 68 69int phcmp (Elf32_Phdr *h1, Elf32_Phdr *h2); 70 71char *saveRead (int file, off_t offset, off_t len, char *name); 72void copy (int, int, off_t, off_t); 73void combine (struct sect *base, struct sect *new, int paddable); 74void translate_syms (int, int, off_t, off_t, off_t, off_t); 75int make_ecoff_section_hdrs(struct ecoff_exechdr *ep, 76 struct ecoff_scnhdr *esecs); 77 78void write_ecoff_symhdr(int outfile, struct ecoff_exechdr *ep, 79 struct ecoff_symhdr *symhdrp, 80 long nesyms, long extstroff); 81 82extern int errno; 83int *symTypeTable; 84 85int 86main (int argc, char **argv, char **envp) 87{ 88 Elf32_Ehdr ex; 89 Elf32_Phdr *ph; 90 Elf32_Shdr *sh; 91 char *shstrtab; 92 int strtabix, symtabix; 93 int i, pad; 94 struct sect text, data, bss; /* a.out-compatible sections */ 95 struct sect rdata, sdata, sbss; /* ECOFF-only sections */ 96 97 struct ecoff_exechdr ep; 98 struct ecoff_scnhdr esecs [6]; 99 100 int infile, outfile; 101 unsigned long cur_vma = ULONG_MAX; 102 int symflag = 0; 103 int nsecs = 0; 104 105 text.len = data.len = bss.len = 0; 106 text.vaddr = data.vaddr = bss.vaddr = 0; 107 108 rdata.len = sdata.len = sbss.len = 0; 109 rdata.vaddr = sdata.vaddr = sbss.vaddr = 0; 110 111 /* Check args... */ 112 if (argc < 3 || argc > 4) 113 { 114 usage: 115 fprintf (stderr, 116 "usage: elf2aout <elf executable> <a.out executable> [-s]\n"); 117 exit (1); 118 } 119 if (argc == 4) 120 { 121 if (strcmp (argv [3], "-s")) 122 goto usage; 123 symflag = 1; 124 } 125 126 /* Try the input file... */ 127 if ((infile = open (argv [1], O_RDONLY)) < 0) 128 { 129 fprintf (stderr, "Can't open %s for read: %s\n", 130 argv [1], strerror (errno)); 131 exit (1); 132 } 133 134 /* Read the header, which is at the beginning of the file... */ 135 i = read (infile, &ex, sizeof ex); 136 if (i != sizeof ex) 137 { 138 fprintf (stderr, "ex: %s: %s.\n", 139 argv [1], i ? strerror (errno) : "End of file reached"); 140 exit (1); 141 } 142 143 /* Read the program headers... */ 144 ph = (Elf32_Phdr *)saveRead (infile, ex.e_phoff, 145 ex.e_phnum * sizeof (Elf32_Phdr), "ph"); 146 /* Read the section headers... */ 147 sh = (Elf32_Shdr *)saveRead (infile, ex.e_shoff, 148 ex.e_shnum * sizeof (Elf32_Shdr), "sh"); 149 /* Read in the section string table. */ 150 shstrtab = saveRead (infile, sh [ex.e_shstrndx].sh_offset, 151 sh [ex.e_shstrndx].sh_size, "shstrtab"); 152 /* Read in the section string table. */ 153 shstrtab = saveRead (infile, sh [ex.e_shstrndx].sh_offset, 154 sh [ex.e_shstrndx].sh_size, "shstrtab"); 155 156 157 /* Look for the symbol table and string table... 158 Also map section indices to symbol types for a.out */ 159 symtabix = 0; 160 strtabix = 0; 161 for (i = 0; i < ex.e_shnum; i++) 162 { 163 char *name = shstrtab + sh [i].sh_name; 164 if (!strcmp (name, ".symtab")) 165 symtabix = i; 166 else if (!strcmp (name, ".strtab")) 167 strtabix = i; 168 169 } 170 171 /* Figure out if we can cram the program header into an ECOFF 172 header... Basically, we can't handle anything but loadable 173 segments, but we can ignore some kinds of segments. We can't 174 handle holes in the address space. Segments may be out of order, 175 so we sort them first. */ 176 177 qsort (ph, ex.e_phnum, sizeof (Elf32_Phdr), 178 ( int (*)(const void *, const void *))phcmp); 179 180 for (i = 0; i < ex.e_phnum; i++) 181 { 182 /* Section types we can ignore... */ 183 if (ph [i].p_type == Elf_pt_null || ph [i].p_type == Elf_pt_note || 184 ph [i].p_type == Elf_pt_phdr || 185 ph [i].p_type == Elf_pt_mips_reginfo) { 186 187 if (debug) { 188 fprintf(stderr," skipping PH %d type %d flags 0x%x\n", 189 i, ph[i].p_type, ph[i].p_flags); 190 } 191 continue; 192 } 193 194 /* Section types we can't handle... */ 195 else if (ph [i].p_type != Elf_pt_load) 196 { 197 fprintf (stderr, "Program header %d type %d can't be converted.\n", 198 i, ph[i].p_type); 199 exit (1); 200 } 201 /* Writable (data) segment? */ 202 if (ph [i].p_flags & PF_W) 203 { 204 struct sect ndata, nbss; 205 206 ndata.vaddr = ph [i].p_vaddr; 207 ndata.len = ph [i].p_filesz; 208 nbss.vaddr = ph [i].p_vaddr + ph [i].p_filesz; 209 nbss.len = ph [i].p_memsz - ph [i].p_filesz; 210 211 if (debug) { 212 printf(" combinining PH %d type %d flags 0x%x with data, ndata = %ld, nbss =%ld\n", i, ph[i].p_type, ph[i].p_flags, ndata.len, nbss.len); 213 } 214 215 combine (&data, &ndata, 0); 216 combine (&bss, &nbss, 1); 217 } 218 else 219 { 220 struct sect ntxt; 221 222 ntxt.vaddr = ph [i].p_vaddr; 223 ntxt.len = ph [i].p_filesz; 224 if (debug) { 225 226 printf(" combinining PH %d type %d flags 0x%x with text, len = %ld\n", 227 i, ph[i].p_type, ph[i].p_flags, ntxt.len); 228 } 229 230 231 combine (&text, &ntxt, 0); 232 } 233 /* Remember the lowest segment start address. */ 234 if (ph [i].p_vaddr < cur_vma) 235 cur_vma = ph [i].p_vaddr; 236 } 237 238 /* Sections must be in order to be converted... */ 239 if (text.vaddr > data.vaddr || data.vaddr > bss.vaddr || 240 text.vaddr + text.len > data.vaddr || data.vaddr + data.len > bss.vaddr) 241 { 242 fprintf (stderr, "Sections ordering prevents a.out conversion.\n"); 243 exit (1); 244 } 245 246 /* If there's a data section but no text section, then the loader 247 combined everything into one section. That needs to be the 248 text section, so just make the data section zero length following 249 text. */ 250 if (data.len && !text.len) 251 { 252 text = data; 253 data.vaddr = text.vaddr + text.len; 254 data.len = 0; 255 } 256 257 /* If there is a gap between text and data, we'll fill it when we copy 258 the data, so update the length of the text segment as represented in 259 a.out to reflect that, since a.out doesn't allow gaps in the program 260 address space. */ 261 if (text.vaddr + text.len < data.vaddr) 262 text.len = data.vaddr - text.vaddr; 263 264 /* We now have enough information to cons up an a.out header... */ 265 ep.a.magic = ECOFF_OMAGIC; 266 ep.a.vstamp = 2 * 256 + 10; /* compatible with version 2.10 */ 267 ep.a.tsize = text.len; 268 ep.a.dsize = data.len; 269 ep.a.bsize = bss.len; 270 ep.a.entry = ex.e_entry; 271 ep.a.text_start = text.vaddr; 272 ep.a.data_start = data.vaddr; 273 ep.a.bss_start = bss.vaddr; 274 ep.a.gprmask = 0xf3fffffe; 275 bzero (&ep.a.cprmask, sizeof ep.a.cprmask); 276 ep.a.gp_value = 0; /* unused. */ 277 278 ep.f.f_magic = ECOFF_MAGIC_MIPSEL; 279 ep.f.f_nscns = 6; 280 ep.f.f_timdat = 0; /* bogus */ 281 ep.f.f_symptr = 0; 282 ep.f.f_nsyms = sizeof(struct ecoff_symhdr); 283 ep.f.f_opthdr = sizeof ep.a; 284 ep.f.f_flags = 0x100f; /* Stripped, not sharable. */ 285 286 bzero(esecs, sizeof(esecs)); 287 288 /* Make ECOFF section headers, with empty stubs for .rdata/.sdata/.sbss. */ 289 make_ecoff_section_hdrs(&ep, esecs); 290 291 nsecs = ep.f.f_nscns; 292 293 /* Make the output file... */ 294 if ((outfile = open (argv [2], O_WRONLY | O_CREAT, 0777)) < 0) 295 { 296 fprintf (stderr, "Unable to create %s: %s\n", argv [2], strerror (errno)); 297 exit (1); 298 } 299 300 /* Write the headers... */ 301 i = write (outfile, &ep.f, sizeof ep.f); 302 if (i != sizeof ep.f) 303 { 304 perror ("ep.f: write"); 305 exit (1); 306 307 for (i = 0; i < nsecs; i++) 308 { 309 printf ("Section %d: %s phys %lx size %lx file offset %lx\n", 310 i, esecs [i].s_name, esecs [i].s_paddr, 311 esecs [i].s_size, esecs [i].s_scnptr); 312 } 313 } 314 fprintf (stderr, "wrote %d byte file header.\n", i); 315 316 i = write (outfile, &ep.a, sizeof ep.a); 317 if (i != sizeof ep.a) 318 { 319 perror ("ep.a: write"); 320 exit (1); 321 } 322 fprintf (stderr, "wrote %d byte a.out header.\n", i); 323 324 i = write (outfile, &esecs, sizeof (esecs[0]) * nsecs); 325 if (i != sizeof (esecs[0]) * nsecs) 326 { 327 perror ("esecs: write"); 328 exit (1); 329 } 330 fprintf (stderr, "wrote %d bytes of section headers.\n", i); 331 332 333 pad = ((sizeof ep.f + sizeof ep.a + sizeof esecs) & 15); 334 if (pad) 335 { 336 pad = 16 - pad; 337 i = write (outfile, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0", pad); 338 if (i < 0) 339 { 340 perror ("ipad: write"); 341 exit (1); 342 } 343 fprintf (stderr, "wrote %d byte pad.\n", i); 344 } 345 346 /* Copy the loadable sections. Zero-fill any gaps less than 64k; 347 complain about any zero-filling, and die if we're asked to zero-fill 348 more than 64k. */ 349 for (i = 0; i < ex.e_phnum; i++) 350 { 351 /* Unprocessable sections were handled above, so just verify that 352 the section can be loaded before copying. */ 353 if (ph [i].p_type == Elf_pt_load && ph [i].p_filesz) 354 { 355 if (cur_vma != ph [i].p_vaddr) 356 { 357 unsigned long gap = ph [i].p_vaddr - cur_vma; 358 char obuf [1024]; 359 if (gap > 65536) 360 { 361 fprintf (stderr, "Intersegment gap (%ld bytes) too large.\n", 362 gap); 363 exit (1); 364 } 365 fprintf (stderr, "Warning: %ld byte intersegment gap.\n", gap); 366 memset (obuf, 0, sizeof obuf); 367 while (gap) 368 { 369 int count = write (outfile, obuf, (gap > sizeof obuf 370 ? sizeof obuf : gap)); 371 if (count < 0) 372 { 373 fprintf (stderr, "Error writing gap: %s\n", 374 strerror (errno)); 375 exit (1); 376 } 377 gap -= count; 378 } 379 } 380fprintf (stderr, "writing %d bytes...\n", ph [i].p_filesz); 381 copy (outfile, infile, ph [i].p_offset, ph [i].p_filesz); 382 cur_vma = ph [i].p_vaddr + ph [i].p_filesz; 383 } 384 } 385 386 387 /* 388 * Write a page of padding for boot PROMS that read entire pages. 389 * Without this, they may attempt to read past the end of the 390 * data section, incur an error, and refuse to boot. 391 */ 392 { 393 char obuf [4096]; 394 memset (obuf, 0, sizeof obuf); 395 if (write(outfile, obuf, sizeof(obuf)) != sizeof(obuf)) { 396 fprintf(stderr, "Error writing PROM padding: %s\n", 397 strerror(errno)); 398 exit(1); 399 } 400 } 401 402 /* Looks like we won... */ 403 exit (0); 404} 405 406void 407copy (out, in, offset, size) 408 int out, in; 409 off_t offset, size; 410{ 411 char ibuf [4096]; 412 int remaining, cur, count; 413 414 /* Go the the start of the ELF symbol table... */ 415 if (lseek (in, offset, SEEK_SET) < 0) 416 { 417 perror ("copy: lseek"); 418 exit (1); 419 } 420 421 remaining = size; 422 while (remaining) 423 { 424 cur = remaining; 425 if (cur > sizeof ibuf) 426 cur = sizeof ibuf; 427 remaining -= cur; 428 if ((count = read (in, ibuf, cur)) != cur) 429 { 430 fprintf (stderr, "copy: read: %s\n", 431 count ? strerror (errno) : "premature end of file"); 432 exit (1); 433 } 434 if ((count = write (out, ibuf, cur)) != cur) 435 { 436 perror ("copy: write"); 437 exit (1); 438 } 439 } 440} 441 442/* Combine two segments, which must be contiguous. If pad is true, it's 443 okay for there to be padding between. */ 444void 445combine (base, new, pad) 446 struct sect *base, *new; 447 int pad; 448{ 449 if (!base -> len) 450 *base = *new; 451 else if (new -> len) 452 { 453 if (base -> vaddr + base -> len != new -> vaddr) 454 { 455 if (pad) 456 base -> len = new -> vaddr - base -> vaddr; 457 else 458 { 459 fprintf (stderr, 460 "Non-contiguous data can't be converted.\n"); 461 exit (1); 462 } 463 } 464 base -> len += new -> len; 465 } 466} 467 468int 469phcmp (h1, h2) 470 Elf32_Phdr *h1, *h2; 471{ 472 if (h1 -> p_vaddr > h2 -> p_vaddr) 473 return 1; 474 else if (h1 -> p_vaddr < h2 -> p_vaddr) 475 return -1; 476 else 477 return 0; 478} 479 480char *saveRead (int file, off_t offset, off_t len, char *name) 481{ 482 char *tmp; 483 int count; 484 off_t off; 485 if ((off = lseek (file, offset, SEEK_SET)) < 0) 486 { 487 fprintf (stderr, "%s: fseek: %s\n", name, strerror (errno)); 488 exit (1); 489 } 490 if (!(tmp = (char *)malloc (len))) 491 { 492 fprintf (stderr, "%s: Can't allocate %ld bytes.\n", name, (long)len); 493 exit (1); 494 } 495 count = read (file, tmp, len); 496 if (count != len) 497 { 498 fprintf (stderr, "%s: read: %s.\n", 499 name, count ? strerror (errno) : "End of file reached"); 500 exit (1); 501 } 502 return tmp; 503} 504 505 506/* 507 * Construct ECOFF section headers for .text, .data, and .bss, 508 * with empty stubs for .rdata/.sdata/.sbss. Follow the section ordering 509 * guaranteed by the mipsco toolchain: 510 * .text, .rdata, .data., .sdata, .sbss, .bss. 511 * 512 * The ELF kernel we are translating has no sections corresponding 513 * to .rdata, .sdata and .sbss. Output zero-length sections for each, 514 * with no file contents and the correct ELF section flags. 515 * Some DECstation proms will not boot without this. 516 * 517 * XXX scan the ELF sectoin headers and map ELF .rodata to ECOFF .rdata 518 */ 519int 520make_ecoff_section_hdrs(ep, esecs) 521 struct ecoff_exechdr *ep; 522 struct ecoff_scnhdr *esecs; 523 524{ 525 ep->f.f_nscns = 6; /* XXX */ 526 527 strcpy (esecs [0].s_name, ".text"); 528 strcpy (esecs [1].s_name, ".data"); 529 strcpy (esecs [2].s_name, ".bss"); 530 531 esecs [0].s_paddr = esecs [0].s_vaddr = ep->a.text_start; 532 esecs [1].s_paddr = esecs [1].s_vaddr = ep->a.data_start; 533 esecs [2].s_paddr = esecs [2].s_vaddr = ep->a.bss_start; 534 esecs [0].s_size = ep->a.tsize; 535 esecs [1].s_size = ep->a.dsize; 536 esecs [2].s_size = ep->a.bsize; 537 538 esecs [0].s_scnptr = ECOFF_TXTOFF (ep); 539 esecs [1].s_scnptr = ECOFF_DATOFF (ep); 540#if 0 541 esecs [2].s_scnptr = esecs [1].s_scnptr + 542 ECOFF_ROUND (esecs [1].s_size, ECOFF_SEGMENT_ALIGNMENT (ep)); 543#endif 544 545 esecs [0].s_relptr = esecs [1].s_relptr 546 = esecs [2].s_relptr = 0; 547 esecs [0].s_lnnoptr = esecs [1].s_lnnoptr 548 = esecs [2].s_lnnoptr = 0; 549 esecs [0].s_nreloc = esecs [1].s_nreloc = esecs [2].s_nreloc = 0; 550 esecs [0].s_nlnno = esecs [1].s_nlnno = esecs [2].s_nlnno = 0; 551 552 esecs[1].s_flags = 0x100; /* ECOFF rdata */ 553 esecs[3].s_flags = 0x200; /* ECOFF sdata */ 554 esecs[4].s_flags = 0x400; /* ECOFF sbss */ 555 556 /* 557 * Set the symbol-table offset to point at the end of any sections 558 * we loaded above, so later code can use it to write symbol table info.. 559 */ 560 ep->f.f_symptr = esecs[1].s_scnptr + esecs[1].s_size; 561 562 return(ep->f.f_nscns); 563} 564