1 /* $NetBSD: elf2aout.c,v 1.6 1998/11/27 05:09:49 simonb 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 /* elf2aout.c 32 33 This program converts an elf executable to a NetBSD a.out executable. 34 The minimal symbol table is copied, but the debugging symbols and 35 other informational sections are not. */ 36 37 #include <sys/types.h> 38 #include <sys/exec_aout.h> 39 #include <sys/exec_elf.h> 40 41 #include <a.out.h> 42 #include <err.h> 43 #include <errno.h> 44 #include <fcntl.h> 45 #include <limits.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <unistd.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 struct sect { 60 unsigned long vaddr; 61 unsigned long len; 62 }; 63 64 void combine __P((struct sect *, struct sect *, int)); 65 int phcmp __P((const void *, const void *)); 66 char *saveRead __P((int file, off_t offset, off_t len, char *name)); 67 void copy __P((int, int, off_t, off_t)); 68 void translate_syms __P((int, int, off_t, off_t, off_t, off_t)); 69 70 int *symTypeTable; 71 72 int 73 main(int argc, char **argv) 74 { 75 Elf32_Ehdr ex; 76 Elf32_Phdr *ph; 77 Elf32_Shdr *sh; 78 char *shstrtab; 79 int strtabix, symtabix; 80 int i; 81 struct sect text, data, bss; 82 struct exec aex; 83 int infile, outfile; 84 unsigned long cur_vma = ULONG_MAX; 85 int symflag = 0; 86 87 strtabix = symtabix = 0; 88 text.len = data.len = bss.len = 0; 89 text.vaddr = data.vaddr = bss.vaddr = 0; 90 91 /* Check args... */ 92 if (argc < 3 || argc > 4) { 93 usage: 94 fprintf(stderr, 95 "usage: elf2aout <elf executable> <a.out executable> [-s]\n"); 96 exit(1); 97 } 98 if (argc == 4) { 99 if (strcmp(argv[3], "-s")) 100 goto usage; 101 symflag = 1; 102 } 103 /* Try the input file... */ 104 if ((infile = open(argv[1], O_RDONLY)) < 0) { 105 fprintf(stderr, "Can't open %s for read: %s\n", 106 argv[1], strerror(errno)); 107 exit(1); 108 } 109 /* Read the header, which is at the beginning of the file... */ 110 i = read(infile, &ex, sizeof ex); 111 if (i != sizeof ex) { 112 fprintf(stderr, "ex: %s: %s.\n", 113 argv[1], i ? strerror(errno) : "End of file reached"); 114 exit(1); 115 } 116 /* Read the program headers... */ 117 ph = (Elf32_Phdr *) saveRead(infile, ex.e_phoff, 118 ex.e_phnum * sizeof(Elf32_Phdr), "ph"); 119 /* Read the section headers... */ 120 sh = (Elf32_Shdr *) saveRead(infile, ex.e_shoff, 121 ex.e_shnum * sizeof(Elf32_Shdr), "sh"); 122 /* Read in the section string table. */ 123 shstrtab = saveRead(infile, sh[ex.e_shstrndx].sh_offset, 124 sh[ex.e_shstrndx].sh_size, "shstrtab"); 125 126 /* Find space for a table matching ELF section indices to a.out symbol 127 * types. */ 128 symTypeTable = (int *) malloc(ex.e_shnum * sizeof(int)); 129 if (!symTypeTable) { 130 fprintf(stderr, "symTypeTable: can't allocate.\n"); 131 exit(1); 132 } 133 memset(symTypeTable, 0, ex.e_shnum * sizeof(int)); 134 135 /* Look for the symbol table and string table... Also map section 136 * indices to symbol types for a.out */ 137 for (i = 0; i < ex.e_shnum; i++) { 138 char *name = shstrtab + sh[i].sh_name; 139 if (!strcmp(name, ".symtab")) 140 symtabix = i; 141 else 142 if (!strcmp(name, ".strtab")) 143 strtabix = i; 144 else 145 if (!strcmp(name, ".text") || !strcmp(name, ".rodata")) 146 symTypeTable[i] = N_TEXT; 147 else 148 if (!strcmp(name, ".data") || !strcmp(name, ".sdata") || 149 !strcmp(name, ".lit4") || !strcmp(name, ".lit8")) 150 symTypeTable[i] = N_DATA; 151 else 152 if (!strcmp(name, ".bss") || !strcmp(name, ".sbss")) 153 symTypeTable[i] = N_BSS; 154 } 155 156 /* Figure out if we can cram the program header into an a.out 157 * header... Basically, we can't handle anything but loadable 158 * segments, but we can ignore some kinds of segments. We can't 159 * handle holes in the address space, and we handle start addresses 160 * other than 0x1000 by hoping that the loader will know where to load 161 * - a.out doesn't have an explicit load address. Segments may be 162 * out of order, so we sort them first. */ 163 qsort(ph, ex.e_phnum, sizeof(Elf32_Phdr), phcmp); 164 for (i = 0; i < ex.e_phnum; i++) { 165 /* Section types we can ignore... */ 166 if (ph[i].p_type == Elf_pt_null || ph[i].p_type == Elf_pt_note || 167 ph[i].p_type == Elf_pt_phdr || ph[i].p_type == Elf_pt_mips_reginfo) 168 continue; 169 /* Section types we can't handle... */ 170 else 171 if (ph[i].p_type != Elf_pt_load) 172 errx(1, "Program header %d type %d can't be converted.", i, ph[i].p_type); 173 /* Writable (data) segment? */ 174 if (ph[i].p_flags & PF_W) { 175 struct sect ndata, nbss; 176 177 ndata.vaddr = ph[i].p_vaddr; 178 ndata.len = ph[i].p_filesz; 179 nbss.vaddr = ph[i].p_vaddr + ph[i].p_filesz; 180 nbss.len = ph[i].p_memsz - ph[i].p_filesz; 181 182 combine(&data, &ndata, 0); 183 combine(&bss, &nbss, 1); 184 } else { 185 struct sect ntxt; 186 187 ntxt.vaddr = ph[i].p_vaddr; 188 ntxt.len = ph[i].p_filesz; 189 190 combine(&text, &ntxt, 0); 191 } 192 /* Remember the lowest segment start address. */ 193 if (ph[i].p_vaddr < cur_vma) 194 cur_vma = ph[i].p_vaddr; 195 } 196 197 /* Sections must be in order to be converted... */ 198 if (text.vaddr > data.vaddr || data.vaddr > bss.vaddr || 199 text.vaddr + text.len > data.vaddr || data.vaddr + data.len > bss.vaddr) { 200 fprintf(stderr, "Sections ordering prevents a.out conversion.\n"); 201 exit(1); 202 } 203 /* If there's a data section but no text section, then the loader 204 * combined everything into one section. That needs to be the text 205 * section, so just make the data section zero length following text. */ 206 if (data.len && !text.len) { 207 text = data; 208 data.vaddr = text.vaddr + text.len; 209 data.len = 0; 210 } 211 /* If there is a gap between text and data, we'll fill it when we copy 212 * the data, so update the length of the text segment as represented 213 * in a.out to reflect that, since a.out doesn't allow gaps in the 214 * program address space. */ 215 if (text.vaddr + text.len < data.vaddr) 216 text.len = data.vaddr - text.vaddr; 217 218 /* We now have enough information to cons up an a.out header... */ 219 aex.a_midmag = htonl((symflag << 26) | (MID_PMAX << 16) | OMAGIC); 220 aex.a_text = text.len; 221 aex.a_data = data.len; 222 aex.a_bss = bss.len; 223 aex.a_entry = ex.e_entry; 224 aex.a_syms = (sizeof(struct nlist) * 225 (symtabix != -1 226 ? sh[symtabix].sh_size / sizeof(Elf32_Sym) : 0)); 227 aex.a_trsize = 0; 228 aex.a_drsize = 0; 229 230 /* Make the output file... */ 231 if ((outfile = open(argv[2], O_WRONLY | O_CREAT, 0777)) < 0) { 232 fprintf(stderr, "Unable to create %s: %s\n", argv[2], strerror(errno)); 233 exit(1); 234 } 235 /* Truncate file... */ 236 if (ftruncate(outfile, 0)) { 237 warn("ftruncate %s", argv[2]); 238 } 239 /* Write the header... */ 240 i = write(outfile, &aex, sizeof aex); 241 if (i != sizeof aex) { 242 perror("aex: write"); 243 exit(1); 244 } 245 /* Copy the loadable sections. Zero-fill any gaps less than 64k; 246 * complain about any zero-filling, and die if we're asked to 247 * zero-fill more than 64k. */ 248 for (i = 0; i < ex.e_phnum; i++) { 249 /* Unprocessable sections were handled above, so just verify 250 * that the section can be loaded before copying. */ 251 if (ph[i].p_type == Elf_pt_load && ph[i].p_filesz) { 252 if (cur_vma != ph[i].p_vaddr) { 253 unsigned long gap = ph[i].p_vaddr - cur_vma; 254 char obuf[1024]; 255 if (gap > 65536) 256 errx(1, 257 "Intersegment gap (%ld bytes) too large.", (long) gap); 258 #ifdef DEBUG 259 warnx("Warning: %ld byte intersegment gap.", 260 (long)gap); 261 #endif 262 memset(obuf, 0, sizeof obuf); 263 while (gap) { 264 int count = write(outfile, obuf, (gap > sizeof obuf 265 ? sizeof obuf : gap)); 266 if (count < 0) { 267 fprintf(stderr, "Error writing gap: %s\n", 268 strerror(errno)); 269 exit(1); 270 } 271 gap -= count; 272 } 273 } 274 copy(outfile, infile, ph[i].p_offset, ph[i].p_filesz); 275 cur_vma = ph[i].p_vaddr + ph[i].p_filesz; 276 } 277 } 278 279 /* Copy and translate the symbol table... */ 280 translate_syms(outfile, infile, 281 sh[symtabix].sh_offset, sh[symtabix].sh_size, 282 sh[strtabix].sh_offset, sh[strtabix].sh_size); 283 284 /* Looks like we won... */ 285 exit(0); 286 } 287 /* translate_syms (out, in, offset, size) 288 289 Read the ELF symbol table from in at offset; translate it into a.out 290 nlist format and write it to out. */ 291 292 void 293 translate_syms(out, in, symoff, symsize, stroff, strsize) 294 int out, in; 295 off_t symoff, symsize; 296 off_t stroff, strsize; 297 { 298 #define SYMS_PER_PASS 64 299 Elf32_Sym inbuf[64]; 300 struct nlist outbuf[64]; 301 int i, remaining, cur; 302 char *oldstrings; 303 char *newstrings, *nsp; 304 int newstringsize; 305 306 /* Zero the unused fields in the output buffer.. */ 307 memset(outbuf, 0, sizeof outbuf); 308 309 /* Find number of symbols to process... */ 310 remaining = symsize / sizeof(Elf32_Sym); 311 312 /* Suck in the old string table... */ 313 oldstrings = saveRead(in, stroff, strsize, "string table"); 314 315 /* Allocate space for the new one. XXX We make the wild assumption 316 * that no two symbol table entries will point at the same place in 317 * the string table - if that assumption is bad, this could easily 318 * blow up. */ 319 newstringsize = strsize + remaining; 320 newstrings = (char *) malloc(newstringsize); 321 if (!newstrings) { 322 fprintf(stderr, "No memory for new string table!\n"); 323 exit(1); 324 } 325 /* Initialize the table pointer... */ 326 nsp = newstrings; 327 328 /* Go the the start of the ELF symbol table... */ 329 if (lseek(in, symoff, SEEK_SET) < 0) { 330 perror("translate_syms: lseek"); 331 exit(1); 332 } 333 /* Translate and copy symbols... */ 334 while (remaining) { 335 cur = remaining; 336 if (cur > SYMS_PER_PASS) 337 cur = SYMS_PER_PASS; 338 remaining -= cur; 339 if ((i = read(in, inbuf, cur * sizeof(Elf32_Sym))) 340 != cur * sizeof(Elf32_Sym)) { 341 if (i < 0) 342 perror("translate_syms"); 343 else 344 fprintf(stderr, "translate_syms: premature end of file.\n"); 345 exit(1); 346 } 347 /* Do the translation... */ 348 for (i = 0; i < cur; i++) { 349 int binding, type; 350 351 /* Copy the symbol into the new table, but prepend an 352 * underscore. */ 353 *nsp = '_'; 354 strcpy(nsp + 1, oldstrings + inbuf[i].st_name); 355 outbuf[i].n_un.n_strx = nsp - newstrings + 4; 356 nsp += strlen(nsp) + 1; 357 358 type = ELF_SYM_TYPE(inbuf[i].st_info); 359 binding = ELF_SYM_BIND(inbuf[i].st_info); 360 361 /* Convert ELF symbol type/section/etc info into a.out 362 * type info. */ 363 if (type == Elf_estt_file) 364 outbuf[i].n_type = N_FN; 365 else 366 if (inbuf[i].st_shndx == Elf_eshn_undefined) 367 outbuf[i].n_type = N_UNDF; 368 else 369 if (inbuf[i].st_shndx == Elf_eshn_absolute) 370 outbuf[i].n_type = N_ABS; 371 else 372 if (inbuf[i].st_shndx == Elf_eshn_common || 373 inbuf[i].st_shndx == Elf_eshn_mips_acommon) 374 outbuf[i].n_type = N_COMM; 375 else 376 outbuf[i].n_type = symTypeTable[inbuf[i].st_shndx]; 377 if (binding == Elf_estb_global) 378 outbuf[i].n_type |= N_EXT; 379 /* Symbol values in executables should be compatible. */ 380 outbuf[i].n_value = inbuf[i].st_value; 381 } 382 /* Write out the symbols... */ 383 if ((i = write(out, outbuf, cur * sizeof(struct nlist))) 384 != cur * sizeof(struct nlist)) { 385 fprintf(stderr, "translate_syms: write: %s\n", strerror(errno)); 386 exit(1); 387 } 388 } 389 /* Write out the string table length... */ 390 if (write(out, &newstringsize, sizeof newstringsize) 391 != sizeof newstringsize) { 392 fprintf(stderr, 393 "translate_syms: newstringsize: %s\n", strerror(errno)); 394 exit(1); 395 } 396 /* Write out the string table... */ 397 if (write(out, newstrings, newstringsize) != newstringsize) { 398 fprintf(stderr, "translate_syms: newstrings: %s\n", strerror(errno)); 399 exit(1); 400 } 401 } 402 403 void 404 copy(out, in, offset, size) 405 int out, in; 406 off_t offset, size; 407 { 408 char ibuf[4096]; 409 int remaining, cur, count; 410 411 /* Go the the start of the ELF symbol table... */ 412 if (lseek(in, offset, SEEK_SET) < 0) { 413 perror("copy: lseek"); 414 exit(1); 415 } 416 remaining = size; 417 while (remaining) { 418 cur = remaining; 419 if (cur > sizeof ibuf) 420 cur = sizeof ibuf; 421 remaining -= cur; 422 if ((count = read(in, ibuf, cur)) != cur) { 423 fprintf(stderr, "copy: read: %s\n", 424 count ? strerror(errno) : "premature end of file"); 425 exit(1); 426 } 427 if ((count = write(out, ibuf, cur)) != cur) { 428 perror("copy: write"); 429 exit(1); 430 } 431 } 432 } 433 /* Combine two segments, which must be contiguous. If pad is true, it's 434 okay for there to be padding between. */ 435 void 436 combine(base, new, pad) 437 struct sect *base, *new; 438 int pad; 439 { 440 if (!base->len) 441 *base = *new; 442 else 443 if (new->len) { 444 if (base->vaddr + base->len != new->vaddr) { 445 if (pad) 446 base->len = new->vaddr - base->vaddr; 447 else { 448 fprintf(stderr, 449 "Non-contiguous data can't be converted.\n"); 450 exit(1); 451 } 452 } 453 base->len += new->len; 454 } 455 } 456 457 int 458 phcmp(vh1, vh2) 459 const void *vh1, *vh2; 460 { 461 Elf32_Phdr *h1, *h2; 462 h1 = (Elf32_Phdr *) vh1; 463 h2 = (Elf32_Phdr *) vh2; 464 465 if (h1->p_vaddr > h2->p_vaddr) 466 return 1; 467 else 468 if (h1->p_vaddr < h2->p_vaddr) 469 return -1; 470 else 471 return 0; 472 } 473 474 char * 475 saveRead(int file, off_t offset, off_t len, char *name) 476 { 477 char *tmp; 478 int count; 479 off_t off; 480 if ((off = lseek(file, offset, SEEK_SET)) < 0) { 481 fprintf(stderr, "%s: fseek: %s\n", name, strerror(errno)); 482 exit(1); 483 } 484 if (!(tmp = (char *) malloc(len))) 485 errx(1, "%s: Can't allocate %ld bytes.", name, (long)len); 486 count = read(file, tmp, len); 487 if (count != len) { 488 fprintf(stderr, "%s: read: %s.\n", 489 name, count ? strerror(errno) : "End of file reached"); 490 exit(1); 491 } 492 return tmp; 493 } 494