2 * aout2hux - convert a.out/ELF executable to Human68k .x format
4 * Read two a.out/ELF format executables with different load addresses
5 * and generate Human68k .x format executable.
7 * written by Yasha (ITOH Yasufumi)
11 * aout2hux [ -o output.x ] a.out1 loadaddr1 a.out2 loadaddr2
13 * The input files must be static OMAGIC/NMAGIC m68k a.out executables
14 * or m68k ELF executables.
15 * Two executables must have different loading addresses.
16 * Each of the load address must be a hexadecimal number.
17 * Load address shall be multiple of 4 for as / ld of NetBSD/m68k.
20 * % cc -N -static -Wl,-Ttext,0 -o aout1 *.o
21 * % cc -N -static -Wl,-Ttext,10203040 -o aout2 *.o
22 * % aout2hux -o foo.x aout1 0 aout2 10203040
24 * $NetBSD: aout2hux.c,v 1.11 2009/11/15 18:24:23 dholland Exp $
27 #include <sys/types.h>
37 #include "type_local.h"
41 /* fseek() offset type */
44 #ifndef DEFAULT_OUTPUT_FILE
45 # define DEFAULT_OUTPUT_FILE "out.x"
49 # define DPRINTF(x) printf x
55 foff_t text_off
; /* file offset of text section */
56 foff_t data_off
; /* file offset of data section */
57 u_int32_t text_size
; /* size of text section */
58 u_int32_t text_pad
; /* pad between text and data */
59 u_int32_t data_size
; /* size of data section */
60 u_int32_t bss_size
; /* size of bss */
61 u_int32_t entry_addr
; /* entry point address */
64 unsigned get_uint16(be_uint16_t
*be
);
65 u_int32_t
get_uint32(be_uint32_t
*be
);
66 void put_uint16(be_uint16_t
*be
, unsigned v
);
67 void put_uint32(be_uint32_t
*be
, u_int32_t v
);
68 void *do_realloc(void *p
, size_t s
);
70 static int open_aout(const char *fn
, struct aout_m68k
*hdr
,
71 struct exec_info
*inf
);
72 static int open_elf(const char *fn
, FILE *fp
, struct elf_m68k_hdr
*hdr
,
73 struct exec_info
*inf
);
74 FILE *open_exec(const char *fn
, struct exec_info
*inf
);
75 int check_2_exec_inf(struct exec_info
*inf1
, struct exec_info
*inf2
);
76 int aout2hux(const char *fn1
, const char *fn2
,
77 u_int32_t loadadr1
, u_int32_t loadadr2
, const char *fnx
);
78 int gethex(u_int32_t
*pval
, const char *str
);
79 void usage(const char *name
);
81 #if !defined(bzero) && defined(__SVR4)
82 # define bzero(d, n) memset((d), 0, (n))
86 * read/write big-endian integer
90 get_uint16(be_uint16_t
*be
)
93 return be
->val
[0] << 8 | be
->val
[1];
97 get_uint32(be_uint32_t
*be
)
100 return be
->val
[0]<<24 | be
->val
[1]<<16 | be
->val
[2]<<8 | be
->val
[3];
104 put_uint16(be_uint16_t
*be
, unsigned v
)
107 be
->val
[0] = (u_int8_t
) (v
>> 8);
108 be
->val
[1] = (u_int8_t
) v
;
112 put_uint32(be_uint32_t
*be
, u_int32_t v
)
115 be
->val
[0] = (u_int8_t
) (v
>> 24);
116 be
->val
[1] = (u_int8_t
) (v
>> 16);
117 be
->val
[2] = (u_int8_t
) (v
>> 8);
118 be
->val
[3] = (u_int8_t
) v
;
122 do_realloc(void *p
, size_t s
)
125 p
= p
? realloc(p
, s
) : malloc(s
); /* for portability */
128 fprintf(stderr
, "malloc failed\n");
139 open_aout(const char *fn
, struct aout_m68k
*hdr
, struct exec_info
*inf
)
143 DPRINTF(("%s: is an a.out\n", fn
));
145 if ((i
= AOUT_GET_MID(hdr
)) != AOUT_MID_M68K
&& i
!= AOUT_MID_M68K4K
) {
146 fprintf(stderr
, "%s: wrong architecture (mid %d)\n", fn
, i
);
150 /* if unsolved relocations exist, not an executable but an object */
151 if (hdr
->a_trsize
.hostval
|| hdr
->a_drsize
.hostval
) {
152 fprintf(stderr
, "%s: not an executable (object file?)\n", fn
);
156 if (AOUT_GET_FLAGS(hdr
) & (AOUT_FLAG_PIC
| AOUT_FLAG_DYNAMIC
)) {
157 fprintf(stderr
, "%s: PIC and DYNAMIC are not supported\n", fn
);
161 inf
->text_size
= get_uint32(&hdr
->a_text
);
162 inf
->data_size
= get_uint32(&hdr
->a_data
);
163 inf
->bss_size
= get_uint32(&hdr
->a_bss
);
164 inf
->entry_addr
= get_uint32(&hdr
->a_entry
);
165 inf
->text_off
= sizeof(struct aout_m68k
);
166 inf
->data_off
= sizeof(struct aout_m68k
) + inf
->text_size
;
167 inf
->text_pad
= -inf
->text_size
& (AOUT_PAGESIZE(hdr
) - 1);
173 * digest ELF structure
176 open_elf(const char *fn
, FILE *fp
, struct elf_m68k_hdr
*hdr
, struct exec_info
*inf
)
180 struct elf_m68k_phdr phdr
[2];
182 DPRINTF(("%s: is an ELF\n", fn
));
184 if (hdr
->e_ident
[EI_VERSION
] != EV_CURRENT
||
185 get_uint32(&hdr
->e_version
) != EV_CURRENT
) {
186 fprintf(stderr
, "%s: unknown ELF version\n", fn
);
190 if (get_uint16(&hdr
->e_type
) != ET_EXEC
) {
191 fprintf(stderr
, "%s: not an executable\n", fn
);
195 if ((i
= get_uint16(&hdr
->e_machine
)) != EM_68K
) {
196 fprintf(stderr
, "%s: wrong architecture (%d)\n", fn
, i
);
200 if ((i
= get_uint16(&hdr
->e_shentsize
)) != SIZE_ELF68K_SHDR
) {
201 fprintf(stderr
, "%s: size shdr %d should be %d\n", fn
, i
,
202 (int)SIZE_ELF68K_SHDR
);
206 if ((i
= get_uint16(&hdr
->e_phentsize
)) != SIZE_ELF68K_PHDR
) {
207 fprintf(stderr
, "%s: size phdr %d should be %d\n", fn
, i
,
208 (int)SIZE_ELF68K_PHDR
);
212 if ((nphdr
= get_uint16(&hdr
->e_phnum
)) != 1 && nphdr
!= 2) {
214 "%s: has %lu loadable segments (should be 1 or 2)\n",
215 fn
, (unsigned long)nphdr
);
219 /* Read ELF program header table. */
220 if (fseek(fp
, (foff_t
) get_uint32(&hdr
->e_phoff
), SEEK_SET
)) {
224 if (fread(phdr
, sizeof phdr
[0], nphdr
, fp
) != nphdr
) {
225 fprintf(stderr
, "%s: can't read ELF program header\n", fn
);
229 /* Just error checking. */
230 for (i
= 0; i
< (int) nphdr
; i
++) {
231 if (get_uint32(&phdr
[i
].p_type
) != PT_LOAD
) {
233 "%s: program header #%d is not loadable\n",
239 if (nphdr
== 1 && (get_uint32(&phdr
[0].p_flags
) & PF_W
)) {
241 * Only one writable section --- probably "ld -N" executable.
242 * Find out the start of data segment.
244 struct elf_m68k_shdr shdr
;
247 nshdr
= get_uint16(&hdr
->e_shnum
);
249 /* section #0 always exists and reserved --- skip */
252 (foff_t
) (get_uint32(&hdr
->e_shoff
) + sizeof shdr
),
257 for (i
= 1; i
< nshdr
; i
++) {
258 if (fread(&shdr
, sizeof shdr
, 1, fp
) != 1) {
260 "%s: can't read ELF section header\n",
265 DPRINTF(("%s: section header #%d: flags 0x%x\n",
266 fn
, i
, get_uint32(&shdr
.sh_flags
)));
268 if (ELF68K_ISDATASEG(&shdr
)) {
270 * data section is found.
272 DPRINTF(("%s: one section, data found\n", fn
));
273 inf
->text_off
= get_uint32(&phdr
[0].p_offset
);
274 inf
->text_size
= get_uint32(&shdr
.sh_offset
) -
277 inf
->data_off
= inf
->text_off
+ inf
->text_size
;
278 inf
->data_size
= get_uint32(&phdr
[0].p_filesz
) -
280 inf
->bss_size
= get_uint32(&phdr
[0].p_memsz
) -
281 get_uint32(&phdr
[0].p_filesz
);
282 inf
->entry_addr
= get_uint32(&hdr
->e_entry
);
287 * No data section found --- probably text + bss.
289 DPRINTF(("%s: one section, no data section\n", fn
));
290 inf
->text_size
= get_uint32(&phdr
[0].p_filesz
);
292 inf
->bss_size
= get_uint32(&phdr
[0].p_memsz
) - inf
->text_size
;
293 inf
->entry_addr
= get_uint32(&hdr
->e_entry
);
294 inf
->text_off
= get_uint32(&phdr
[0].p_offset
);
298 } else if (nphdr
== 1) {
300 * Only one non-writable section --- pure text program?
302 DPRINTF(("%s: one RO section\n", fn
));
303 inf
->text_size
= get_uint32(&phdr
[0].p_filesz
);
306 inf
->entry_addr
= get_uint32(&hdr
->e_entry
);
307 inf
->text_off
= get_uint32(&phdr
[0].p_offset
);
309 inf
->text_pad
= get_uint32(&phdr
[0].p_memsz
) - inf
->text_size
;
313 * text + data assumed.
315 int t
= 0, d
= 1, tmp
; /* first guess */
316 #define SWAP_T_D tmp = t, t = d, d = tmp
318 DPRINTF(("%s: two sections\n", fn
));
320 /* Find out text and data. */
321 if (get_uint32(&phdr
[t
].p_vaddr
) > get_uint32(&phdr
[d
].p_vaddr
))
324 if ((get_uint32(&phdr
[t
].p_flags
) & PF_X
) == 0 &&
325 get_uint32(&phdr
[d
].p_flags
) & PF_X
)
328 if ((get_uint32(&phdr
[d
].p_flags
) & PF_W
) == 0 &&
329 get_uint32(&phdr
[t
].p_flags
) & PF_W
)
333 /* Are the text/data sections correctly detected? */
334 if (get_uint32(&phdr
[t
].p_vaddr
) >
335 get_uint32(&phdr
[d
].p_vaddr
)) {
336 fprintf(stderr
, "%s: program sections not in order\n",
341 if ((get_uint32(&phdr
[t
].p_flags
) & PF_X
) == 0)
342 fprintf(stderr
, "%s: warning: text is not executable\n",
345 if ((get_uint32(&phdr
[d
].p_flags
) & PF_W
) == 0)
346 fprintf(stderr
, "%s: warning: data is not writable\n",
349 inf
->text_size
= get_uint32(&phdr
[t
].p_filesz
);
350 inf
->data_size
= get_uint32(&phdr
[d
].p_filesz
);
351 inf
->bss_size
= get_uint32(&phdr
[d
].p_memsz
) - inf
->data_size
;
352 inf
->entry_addr
= get_uint32(&hdr
->e_entry
);
353 inf
->text_off
= get_uint32(&phdr
[t
].p_offset
);
354 inf
->data_off
= get_uint32(&phdr
[d
].p_offset
);
355 inf
->text_pad
= get_uint32(&phdr
[d
].p_vaddr
) -
356 (get_uint32(&phdr
[t
].p_vaddr
) + inf
->text_size
);
366 open_exec(const char *fn
, struct exec_info
*inf
)
371 struct aout_m68k u_aout
;
372 struct elf_m68k_hdr u_elf
;
374 #define hdra (&buf.u_aout)
375 #define hdre (&buf.u_elf)
377 if (!(fp
= fopen(fn
, "r"))) {
379 return (FILE *) NULL
;
386 if (fread(hdra
, sizeof(struct aout_m68k
), 1, fp
) != 1) {
387 fprintf(stderr
, "%s: can't read a.out header\n", fn
);
391 if ((i
= AOUT_GET_MAGIC(hdra
)) != AOUT_OMAGIC
&& i
!= AOUT_NMAGIC
)
394 if (open_aout(fn
, hdra
, inf
))
405 if (hdre
->e_ident
[EI_MAG0
] != ELFMAG0
||
406 hdre
->e_ident
[EI_MAG1
] != ELFMAG1
||
407 hdre
->e_ident
[EI_MAG2
] != ELFMAG2
||
408 hdre
->e_ident
[EI_MAG3
] != ELFMAG3
||
409 hdre
->e_ident
[EI_CLASS
] != ELFCLASS32
||
410 hdre
->e_ident
[EI_DATA
] != ELFDATA2MSB
) {
412 "%s: not an OMAGIC or NMAGIC a.out, or a 32bit BE ELF\n",
417 /* ELF header is longer than a.out header. Read the rest. */
419 sizeof(struct elf_m68k_hdr
) - sizeof(struct aout_m68k
),
421 fprintf(stderr
, "%s: can't read ELF header\n", fn
);
425 if (open_elf(fn
, fp
, hdre
, inf
))
432 return (FILE *) NULL
;
438 * compare two executables and check if they are compatible
441 check_2_exec_inf(struct exec_info
*inf1
, struct exec_info
*inf2
)
444 if (inf1
->text_size
!= inf2
->text_size
||
445 inf1
->text_pad
!= inf2
->text_pad
||
446 inf1
->data_size
!= inf2
->data_size
||
447 inf1
->bss_size
!= inf2
->bss_size
)
453 /* allocation unit (in bytes) of relocation table */
454 #define RELTBL_CHUNK 8192
457 * add an entry to the relocation table
459 #define ADD_RELTBL(adr) \
460 if (relsize + sizeof(struct relinf_l) > relallocsize) \
461 reltbl = do_realloc(reltbl, relallocsize += RELTBL_CHUNK); \
462 if ((adr) < reladdr + HUX_MINLREL) { \
463 struct relinf_s *r = (struct relinf_s *)(reltbl + relsize); \
464 put_uint16(&r->locoff_s, (unsigned)((adr) - reladdr)); \
465 relsize += sizeof(struct relinf_s); \
466 DPRINTF(("short")); \
468 struct relinf_l *r = (struct relinf_l *)(reltbl + relsize); \
469 put_uint16(&r->lrelmag, HUXLRELMAGIC); \
470 put_uint32((be_uint32_t *)r->locoff_l, (adr) - reladdr); \
471 relsize += sizeof(struct relinf_l); \
472 DPRINTF(("long ")); \
474 DPRINTF((" reloc 0x%06x", (adr))); \
477 #define ERR1 { if (ferror(fpa1)) perror(fn1); \
478 else fprintf(stderr, "%s: unexpected EOF\n", fn1); \
480 #define ERR2 { if (ferror(fpa2)) perror(fn2); \
481 else fprintf(stderr, "%s: unexpected EOF\n", fn2); \
483 #define ERRC { fprintf(stderr, "files %s and %s are inconsistent\n", \
488 * read input executables and output .x body
489 * and create relocation table
491 #define CREATE_RELOCATION(segsize) \
492 while (segsize > 0 || nbuf) { \
494 if (fread(&b1.half[0], SIZE_16, 1, fpa1) != 1) \
496 if (fread(&b2.half[0], SIZE_16, 1, fpa2) != 1) \
499 segsize -= SIZE_16; \
500 } else if (nbuf == 1) { \
501 if (segsize == 0) { \
502 if (b1.half[0].hostval != b2.half[0].hostval) \
504 fwrite(&b1.half[0], SIZE_16, 1, fpx); \
508 if (fread(&b1.half[1], SIZE_16, 1, fpa1) != 1)\
510 if (fread(&b2.half[1], SIZE_16, 1, fpa2) != 1)\
513 segsize -= SIZE_16; \
515 } else /* if (nbuf == 2) */ { \
516 if (b1.hostval != b2.hostval && \
517 get_uint32(&b1) - loadadr1 \
518 == get_uint32(&b2) - loadadr2) {\
519 /* do relocation */ \
522 put_uint32(&b1, get_uint32(&b1) - loadadr1); \
523 DPRINTF((" v 0x%08x\t", get_uint32(&b1))); \
524 fwrite(&b1, SIZE_32, 1, fpx); \
527 } else if (b1.half[0].hostval == b2.half[0].hostval) {\
528 fwrite(&b1.half[0], SIZE_16, 1, fpx); \
530 b1.half[0] = b1.half[1]; \
531 b2.half[0] = b2.half[1]; \
539 aout2hux(const char *fn1
, const char *fn2
, u_int32_t loadadr1
, u_int32_t loadadr2
, const char *fnx
)
541 int status
= 1; /* the default is "failed" */
542 FILE *fpa1
= NULL
, *fpa2
= NULL
;
543 struct exec_info inf1
, inf2
;
546 u_int32_t textsize
, datasize
, paddingsize
, execoff
;
553 /* for relocation table */
554 size_t relsize
, relallocsize
;
560 * check load addresses
562 if (loadadr1
== loadadr2
) {
563 fprintf(stderr
, "two load addresses must be different\n");
568 * open input executables and check them
570 if (!(fpa1
= open_exec(fn1
, &inf1
)) || !(fpa2
= open_exec(fn2
, &inf2
)))
574 * check for consistency
576 if (check_2_exec_inf(&inf1
, &inf2
)) {
577 fprintf(stderr
, "files %s and %s are incompatible\n",
581 /* check entry address */
582 if (inf1
.entry_addr
- loadadr1
!= inf2
.entry_addr
- loadadr2
) {
583 fprintf(stderr
, "address of %s or %s may be incorrect\n",
589 * get information of the executables
591 textsize
= inf1
.text_size
;
592 paddingsize
= inf1
.text_pad
;
593 datasize
= inf1
.data_size
;
594 execoff
= inf1
.entry_addr
- loadadr1
;
596 DPRINTF(("text: %u, data: %u, pad: %u, bss: %u, exec: %u\n",
597 textsize
, datasize
, paddingsize
, inf1
.bss_size
, execoff
));
600 fprintf(stderr
, "text size is not even\n");
604 fprintf(stderr
, "data size is not even\n");
607 if (execoff
>= textsize
&&
608 (execoff
< textsize
+ paddingsize
||
609 execoff
>= textsize
+ paddingsize
+ datasize
)) {
610 fprintf(stderr
, "exec addr is not in text or data segment\n");
615 * prepare for .x header
617 memset((void *) &xhdr
, 0, sizeof xhdr
);
618 put_uint16(&xhdr
.x_magic
, HUXMAGIC
);
619 put_uint32(&xhdr
.x_entry
, execoff
);
620 put_uint32(&xhdr
.x_text
, textsize
+ paddingsize
);
621 put_uint32(&xhdr
.x_data
, inf1
.data_size
);
622 put_uint32(&xhdr
.x_bss
, inf1
.bss_size
);
627 if (!(fpx
= fopen(fnx
, "w")) ||
628 fseek(fpx
, (foff_t
) sizeof xhdr
, SEEK_SET
)) { /* skip header */
636 relsize
= relallocsize
= 0;
642 if (fseek(fpa1
, inf1
.text_off
, SEEK_SET
)) {
646 if (fseek(fpa2
, inf2
.text_off
, SEEK_SET
)) {
650 CREATE_RELOCATION(textsize
)
656 while (paddingsize
--)
662 if (fseek(fpa1
, inf1
.data_off
, SEEK_SET
)) {
666 if (fseek(fpa2
, inf2
.data_off
, SEEK_SET
)) {
670 CREATE_RELOCATION(datasize
)
673 * error check of the above
676 fprintf(stderr
, "%s: write failure\n", fnx
);
681 * write relocation table
685 if (fwrite(reltbl
, 1, relsize
, fpx
) != relsize
) {
692 * write .x header at the top of the output file
694 put_uint32(&xhdr
.x_rsize
, relsize
);
695 if (fseek(fpx
, (foff_t
) 0, SEEK_SET
) ||
696 fwrite(&xhdr
, sizeof xhdr
, 1, fpx
) != 1) {
701 status
= 0; /* all OK */
711 if (fclose(fpx
) && status
== 0) {
712 /* Alas, final flush failed! */
736 be_uint32_t be32x2
[2];
738 be16
.val
[0] = 0x12; be16
.val
[1] = 0x34;
739 be32
.val
[0] = 0xfe; be32
.val
[1] = 0xdc;
740 be32
.val
[2] = 0xba; be32
.val
[3] = 0x98;
742 put_uint16(&be32x2
[0].half
[1], 0x4567);
743 put_uint32(&be32x2
[1], 0xa9876543);
745 if (sizeof(u_int8_t
) != 1 || sizeof(u_int16_t
) != 2 ||
746 sizeof(u_int32_t
) != 4 ||
747 SIZE_16
!= 2 || SIZE_32
!= 4 || sizeof be32x2
!= 8 ||
748 sizeof(struct relinf_s
) != 2 || sizeof(struct relinf_l
) != 6 ||
749 SIZE_ELF68K_HDR
!= 52 || SIZE_ELF68K_SHDR
!= 40 ||
750 SIZE_ELF68K_PHDR
!= 32 ||
751 get_uint16(&be16
) != 0x1234 || get_uint32(&be32
) != 0xfedcba98 ||
752 get_uint16(&be32x2
[0].half
[1]) != 0x4567 ||
753 get_uint32(&be32x2
[1]) != 0xa9876543) {
754 fprintf(stderr
, "BIST failed\n");
761 gethex(u_int32_t
*pval
, const char *str
)
763 const unsigned char *p
= (const unsigned char *) str
;
767 /* skip leading "0x" if exists */
768 if (p
[0] == '0' && (p
[1] == 'x' || p
[1] == 'X'))
774 for (val
= 0, over
= 0; *p
; p
++) {
778 case '0': case '1': case '2': case '3': case '4':
779 case '5': case '6': case '7': case '8': case '9':
782 case 'a': case 'A': digit
= 10; break;
783 case 'b': case 'B': digit
= 11; break;
784 case 'c': case 'C': digit
= 12; break;
785 case 'd': case 'D': digit
= 13; break;
786 case 'e': case 'E': digit
= 14; break;
787 case 'f': case 'F': digit
= 15; break;
791 if (val
>= 0x10000000)
793 val
= (val
<< 4) | digit
;
797 fprintf(stderr
, "warning: %s: constant overflow\n", str
);
801 DPRINTF(("gethex: %s -> 0x%x\n", str
, val
));
806 fprintf(stderr
, "%s: not a hexadecimal number\n", str
);
811 usage(const char *name
)
815 usage: %s [ -o output.x ] a.out1 loadaddr1 a.out2 loadaddr2\n\n\
816 The input files must be static OMAGIC/NMAGIC m68k a.out executables\n\
817 or m68k ELF executables.\n\
818 Two executables must have different loading addresses.\n\
819 Each of the load address must be a hexadecimal number.\n\
820 The default output filename is \"%s\".\n" ,name
, DEFAULT_OUTPUT_FILE
);
826 main(int argc
, char *argv
[])
828 const char *outfile
= DEFAULT_OUTPUT_FILE
;
829 u_int32_t adr1
, adr2
;
835 if (argc
> 2 && argv
[1][0] == '-' && argv
[1][1] == 'o' && !argv
[1][2]) {
844 if (gethex(&adr1
, argv
[2]) || gethex(&adr2
, argv
[4]))
847 return aout2hux(argv
[1], argv
[3], adr1
, adr2
, outfile
);