1 /* $NetBSD: elf2aout.c,v 1.11 2004/04/23 02:55:11 simonb Exp $ */
5 * Ted Lemon (hereinafter referred to as the author)
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
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. */
37 #include <sys/types.h>
38 #include <sys/exec_aout.h>
39 #include <sys/exec_elf.h>
57 void combine
__P((struct sect
*, struct sect
*, int));
58 int phcmp
__P((const void *, const void *));
59 char *saveRead
__P((int file
, off_t offset
, off_t len
, const char *name
));
60 void copy
__P((int, int, off_t
, off_t
));
61 void translate_syms
__P((int, int, off_t
, off_t
, off_t
, off_t
));
66 main(int argc
, char **argv
)
72 int strtabix
, symtabix
;
74 struct sect text
, data
, bss
;
77 unsigned long cur_vma
= ULONG_MAX
;
80 strtabix
= symtabix
= 0;
81 text
.len
= data
.len
= bss
.len
= 0;
82 text
.vaddr
= data
.vaddr
= bss
.vaddr
= 0;
85 if (argc
< 3 || argc
> 4) {
88 "usage: elf2aout <elf executable> <a.out executable> [-s]\n");
92 if (strcmp(argv
[3], "-s"))
96 /* Try the input file... */
97 if ((infile
= open(argv
[1], O_RDONLY
)) < 0) {
98 fprintf(stderr
, "Can't open %s for read: %s\n",
99 argv
[1], strerror(errno
));
102 /* Read the header, which is at the beginning of the file... */
103 i
= read(infile
, &ex
, sizeof ex
);
104 if (i
!= sizeof ex
) {
105 fprintf(stderr
, "ex: %s: %s.\n",
106 argv
[1], i
? strerror(errno
) : "End of file reached");
109 /* Read the program headers... */
110 ph
= (Elf32_Phdr
*) saveRead(infile
, ex
.e_phoff
,
111 ex
.e_phnum
* sizeof(Elf32_Phdr
), "ph");
112 /* Read the section headers... */
113 sh
= (Elf32_Shdr
*) saveRead(infile
, ex
.e_shoff
,
114 ex
.e_shnum
* sizeof(Elf32_Shdr
), "sh");
115 /* Read in the section string table. */
116 shstrtab
= saveRead(infile
, sh
[ex
.e_shstrndx
].sh_offset
,
117 sh
[ex
.e_shstrndx
].sh_size
, "shstrtab");
119 /* Find space for a table matching ELF section indices to a.out symbol
121 symTypeTable
= (int *) malloc(ex
.e_shnum
* sizeof(int));
123 fprintf(stderr
, "symTypeTable: can't allocate.\n");
126 memset(symTypeTable
, 0, ex
.e_shnum
* sizeof(int));
128 /* Look for the symbol table and string table... Also map section
129 * indices to symbol types for a.out */
130 for (i
= 0; i
< ex
.e_shnum
; i
++) {
131 char *name
= shstrtab
+ sh
[i
].sh_name
;
132 if (!strcmp(name
, ".symtab"))
135 if (!strcmp(name
, ".strtab"))
138 if (!strcmp(name
, ".text") || !strcmp(name
, ".rodata"))
139 symTypeTable
[i
] = N_TEXT
;
141 if (!strcmp(name
, ".data") || !strcmp(name
, ".sdata") ||
142 !strcmp(name
, ".lit4") || !strcmp(name
, ".lit8"))
143 symTypeTable
[i
] = N_DATA
;
145 if (!strcmp(name
, ".bss") || !strcmp(name
, ".sbss"))
146 symTypeTable
[i
] = N_BSS
;
149 /* Figure out if we can cram the program header into an a.out
150 * header... Basically, we can't handle anything but loadable
151 * segments, but we can ignore some kinds of segments. We can't
152 * handle holes in the address space, and we handle start addresses
153 * other than 0x1000 by hoping that the loader will know where to load
154 * - a.out doesn't have an explicit load address. Segments may be
155 * out of order, so we sort them first. */
156 qsort(ph
, ex
.e_phnum
, sizeof(Elf32_Phdr
), phcmp
);
157 for (i
= 0; i
< ex
.e_phnum
; i
++) {
158 /* Section types we can ignore... */
159 if (ph
[i
].p_type
== PT_NULL
|| ph
[i
].p_type
== PT_NOTE
||
160 ph
[i
].p_type
== PT_PHDR
|| ph
[i
].p_type
== PT_MIPS_REGINFO
)
162 /* Section types we can't handle... */
164 if (ph
[i
].p_type
!= PT_LOAD
)
165 errx(1, "Program header %d type %d can't be converted.", i
, ph
[i
].p_type
);
166 /* Writable (data) segment? */
167 if (ph
[i
].p_flags
& PF_W
) {
168 struct sect ndata
, nbss
;
170 ndata
.vaddr
= ph
[i
].p_vaddr
;
171 ndata
.len
= ph
[i
].p_filesz
;
172 nbss
.vaddr
= ph
[i
].p_vaddr
+ ph
[i
].p_filesz
;
173 nbss
.len
= ph
[i
].p_memsz
- ph
[i
].p_filesz
;
175 combine(&data
, &ndata
, 0);
176 combine(&bss
, &nbss
, 1);
180 ntxt
.vaddr
= ph
[i
].p_vaddr
;
181 ntxt
.len
= ph
[i
].p_filesz
;
183 combine(&text
, &ntxt
, 0);
185 /* Remember the lowest segment start address. */
186 if (ph
[i
].p_vaddr
< cur_vma
)
187 cur_vma
= ph
[i
].p_vaddr
;
190 /* Sections must be in order to be converted... */
191 if (text
.vaddr
> data
.vaddr
|| data
.vaddr
> bss
.vaddr
||
192 text
.vaddr
+ text
.len
> data
.vaddr
|| data
.vaddr
+ data
.len
> bss
.vaddr
) {
193 fprintf(stderr
, "Sections ordering prevents a.out conversion.\n");
196 /* If there's a data section but no text section, then the loader
197 * combined everything into one section. That needs to be the text
198 * section, so just make the data section zero length following text. */
199 if (data
.len
&& !text
.len
) {
201 data
.vaddr
= text
.vaddr
+ text
.len
;
204 /* If there is a gap between text and data, we'll fill it when we copy
205 * the data, so update the length of the text segment as represented
206 * in a.out to reflect that, since a.out doesn't allow gaps in the
207 * program address space. */
208 if (text
.vaddr
+ text
.len
< data
.vaddr
)
209 text
.len
= data
.vaddr
- text
.vaddr
;
211 /* We now have enough information to cons up an a.out header... */
212 aex
.a_midmag
= htonl((symflag
<< 26) | (MID_PMAX
<< 16) | OMAGIC
);
213 if (ex
.e_machine
== EM_PPC
)
214 aex
.a_midmag
= htonl((symflag
<< 26) | (MID_POWERPC
<< 16)
217 aex
.a_text
= text
.len
;
218 aex
.a_data
= data
.len
;
220 aex
.a_entry
= ex
.e_entry
;
221 aex
.a_syms
= (sizeof(struct nlist
) *
223 ? sh
[symtabix
].sh_size
/ sizeof(Elf32_Sym
) : 0));
227 /* Make the output file... */
228 if ((outfile
= open(argv
[2], O_WRONLY
| O_CREAT
, 0777)) < 0) {
229 fprintf(stderr
, "Unable to create %s: %s\n", argv
[2], strerror(errno
));
232 /* Truncate file... */
233 if (ftruncate(outfile
, 0)) {
234 warn("ftruncate %s", argv
[2]);
236 /* Write the header... */
237 i
= write(outfile
, &aex
, sizeof aex
);
238 if (i
!= sizeof aex
) {
239 perror("aex: write");
242 /* Copy the loadable sections. Zero-fill any gaps less than 64k;
243 * complain about any zero-filling, and die if we're asked to
244 * zero-fill more than 64k. */
245 for (i
= 0; i
< ex
.e_phnum
; i
++) {
246 /* Unprocessable sections were handled above, so just verify
247 * that the section can be loaded before copying. */
248 if (ph
[i
].p_type
== PT_LOAD
&& ph
[i
].p_filesz
) {
249 if (cur_vma
!= ph
[i
].p_vaddr
) {
250 unsigned long gap
= ph
[i
].p_vaddr
- cur_vma
;
254 "Intersegment gap (%ld bytes) too large.", (long) gap
);
256 warnx("Warning: %ld byte intersegment gap.",
259 memset(obuf
, 0, sizeof obuf
);
261 int count
= write(outfile
, obuf
, (gap
> sizeof obuf
262 ? sizeof obuf
: gap
));
264 fprintf(stderr
, "Error writing gap: %s\n",
271 copy(outfile
, infile
, ph
[i
].p_offset
, ph
[i
].p_filesz
);
272 cur_vma
= ph
[i
].p_vaddr
+ ph
[i
].p_filesz
;
276 /* Copy and translate the symbol table... */
277 translate_syms(outfile
, infile
,
278 sh
[symtabix
].sh_offset
, sh
[symtabix
].sh_size
,
279 sh
[strtabix
].sh_offset
, sh
[strtabix
].sh_size
);
281 /* Looks like we won... */
284 /* translate_syms (out, in, offset, size)
286 Read the ELF symbol table from in at offset; translate it into a.out
287 nlist format and write it to out. */
290 translate_syms(out
, in
, symoff
, symsize
, stroff
, strsize
)
292 off_t symoff
, symsize
;
293 off_t stroff
, strsize
;
295 #define SYMS_PER_PASS 64
297 struct nlist outbuf
[64];
298 int i
, remaining
, cur
;
300 char *newstrings
, *nsp
;
303 /* Zero the unused fields in the output buffer.. */
304 memset(outbuf
, 0, sizeof outbuf
);
306 /* Find number of symbols to process... */
307 remaining
= symsize
/ sizeof(Elf32_Sym
);
309 /* Suck in the old string table... */
310 oldstrings
= saveRead(in
, stroff
, strsize
, "string table");
312 /* Allocate space for the new one. XXX We make the wild assumption
313 * that no two symbol table entries will point at the same place in
314 * the string table - if that assumption is bad, this could easily
316 newstringsize
= strsize
+ remaining
;
317 newstrings
= (char *) malloc(newstringsize
);
319 fprintf(stderr
, "No memory for new string table!\n");
322 /* Initialize the table pointer... */
325 /* Go the start of the ELF symbol table... */
326 if (lseek(in
, symoff
, SEEK_SET
) < 0) {
327 perror("translate_syms: lseek");
330 /* Translate and copy symbols... */
333 if (cur
> SYMS_PER_PASS
)
336 if ((i
= read(in
, inbuf
, cur
* sizeof(Elf32_Sym
)))
337 != cur
* (long)sizeof(Elf32_Sym
)) {
339 perror("translate_syms");
341 fprintf(stderr
, "translate_syms: premature end of file.\n");
344 /* Do the translation... */
345 for (i
= 0; i
< cur
; i
++) {
348 /* Copy the symbol into the new table, but prepend an
351 strcpy(nsp
+ 1, oldstrings
+ inbuf
[i
].st_name
);
352 outbuf
[i
].n_un
.n_strx
= nsp
- newstrings
+ 4;
353 nsp
+= strlen(nsp
) + 1;
355 type
= ELF32_ST_TYPE(inbuf
[i
].st_info
);
356 binding
= ELF32_ST_BIND(inbuf
[i
].st_info
);
358 /* Convert ELF symbol type/section/etc info into a.out
360 if (type
== STT_FILE
)
361 outbuf
[i
].n_type
= N_FN
;
363 if (inbuf
[i
].st_shndx
== SHN_UNDEF
)
364 outbuf
[i
].n_type
= N_UNDF
;
366 if (inbuf
[i
].st_shndx
== SHN_ABS
)
367 outbuf
[i
].n_type
= N_ABS
;
369 if (inbuf
[i
].st_shndx
== SHN_COMMON
||
370 inbuf
[i
].st_shndx
== SHN_MIPS_ACOMMON
)
371 outbuf
[i
].n_type
= N_COMM
;
373 outbuf
[i
].n_type
= symTypeTable
[inbuf
[i
].st_shndx
];
374 if (binding
== STB_GLOBAL
)
375 outbuf
[i
].n_type
|= N_EXT
;
376 /* Symbol values in executables should be compatible. */
377 outbuf
[i
].n_value
= inbuf
[i
].st_value
;
379 /* Write out the symbols... */
380 if ((i
= write(out
, outbuf
, cur
* sizeof(struct nlist
)))
381 != cur
* (long)sizeof(struct nlist
)) {
382 fprintf(stderr
, "translate_syms: write: %s\n", strerror(errno
));
386 /* Write out the string table length... */
387 if (write(out
, &newstringsize
, sizeof newstringsize
)
388 != sizeof newstringsize
) {
390 "translate_syms: newstringsize: %s\n", strerror(errno
));
393 /* Write out the string table... */
394 if (write(out
, newstrings
, newstringsize
) != newstringsize
) {
395 fprintf(stderr
, "translate_syms: newstrings: %s\n", strerror(errno
));
401 copy(out
, in
, offset
, size
)
406 int remaining
, cur
, count
;
408 /* Go to the start of the ELF symbol table... */
409 if (lseek(in
, offset
, SEEK_SET
) < 0) {
410 perror("copy: lseek");
416 if (cur
> (long)sizeof ibuf
)
419 if ((count
= read(in
, ibuf
, cur
)) != cur
) {
420 fprintf(stderr
, "copy: read: %s\n",
421 count
? strerror(errno
) : "premature end of file");
424 if ((count
= write(out
, ibuf
, cur
)) != cur
) {
425 perror("copy: write");
430 /* Combine two segments, which must be contiguous. If pad is true, it's
431 okay for there to be padding between. */
433 combine(base
, new, pad
)
434 struct sect
*base
, *new;
441 if (base
->vaddr
+ base
->len
!= new->vaddr
) {
443 base
->len
= new->vaddr
- base
->vaddr
;
446 "Non-contiguous data can't be converted.\n");
450 base
->len
+= new->len
;
456 const void *vh1
, *vh2
;
458 const Elf32_Phdr
*h1
, *h2
;
459 h1
= (const Elf32_Phdr
*) vh1
;
460 h2
= (const Elf32_Phdr
*) vh2
;
462 if (h1
->p_vaddr
> h2
->p_vaddr
)
465 if (h1
->p_vaddr
< h2
->p_vaddr
)
472 saveRead(int file
, off_t offset
, off_t len
, const char *name
)
477 if ((off
= lseek(file
, offset
, SEEK_SET
)) < 0) {
478 fprintf(stderr
, "%s: fseek: %s\n", name
, strerror(errno
));
481 if (!(tmp
= (char *) malloc(len
)))
482 errx(1, "%s: Can't allocate %ld bytes.", name
, (long)len
);
483 count
= read(file
, tmp
, len
);
485 fprintf(stderr
, "%s: read: %s.\n",
486 name
, count
? strerror(errno
) : "End of file reached");