Home | History | Annotate | Line # | Download | only in aout2hux
aout2hux.c revision 1.1
      1 /*
      2  *	aout2hux - convert a.out executable to Human68k .x format
      3  *
      4  *	Read two a.out format executables with different load addresses
      5  *	and generate Human68k .x format executable.
      6  *
      7  *	written by Yasha (ITOH Yasufumi)
      8  *	public domain
      9  *
     10  * usage:
     11  *	aout2hux [ -o output.x ] a.out1 loadaddr1 a.out2 loadaddr2
     12  *
     13  *	The input a.out files must be static OMAGIC/NMAGIC m68k executables.
     14  *	Two a.out files must have different loading addresses.
     15  *	Each of the load address must be a hexadecimal number.
     16  *	Load address shall be multiple of 4 for as / ld of NetBSD/m68k.
     17  *
     18  * example:
     19  *	% cc -N -static -Wl,-T,0        -o aout1 *.o
     20  *	% cc -N -static -Wl,-T,10203040 -o aout2 *.o
     21  *	% aout2hux -o foo.x aout1 0 aout2 10203040
     22  *
     23  *	$NetBSD: aout2hux.c,v 1.1 1998/09/01 19:51:08 itohy Exp $
     24  */
     25 
     26 #include <sys/types.h>
     27 #ifndef NO_UNISTD
     28 # include <unistd.h>
     29 #endif
     30 #ifndef NO_STDLIB
     31 # include <stdlib.h>
     32 #endif
     33 #include <stdio.h>
     34 #include <string.h>
     35 
     36 #include "type_local.h"
     37 #include "aout68k.h"
     38 #include "hux.h"
     39 
     40 #ifndef DEFAULT_OUTPUT_FILE
     41 # define DEFAULT_OUTPUT_FILE	"out.x"
     42 #endif
     43 
     44 #ifdef DEBUG
     45 # define DPRINTF(x)	printf x
     46 #else
     47 # define DPRINTF(x)
     48 #endif
     49 
     50 unsigned get_uint16 PROTO((be_uint16_t *be));
     51 u_int32_t get_uint32 PROTO((be_uint32_t *be));
     52 void put_uint16 PROTO((be_uint16_t *be, unsigned v));
     53 void put_uint32 PROTO((be_uint32_t *be, u_int32_t v));
     54 void *do_realloc PROTO((void *p, size_t s));
     55 
     56 FILE *open_aout PROTO((const char *fn, struct aout_m68k *hdr));
     57 int check_2_aout_hdrs PROTO((struct aout_m68k *h1, struct aout_m68k *h2));
     58 int aout2hux PROTO((const char *fn1, const char *fn2,
     59 		u_int32_t loadadr1, u_int32_t loadadr2, const char *fnx));
     60 int gethex PROTO((u_int32_t *pval, const char *str));
     61 void usage PROTO((const char *name));
     62 int main PROTO((int argc, char *argv[]));
     63 
     64 #if !defined(bzero) && defined(__SVR4)
     65 # define bzero(d, n)	memset((d), 0, (n))
     66 #endif
     67 
     68 /*
     69  * read/write big-endian integer
     70  */
     71 
     72 unsigned
     73 get_uint16(be)
     74 	be_uint16_t *be;
     75 {
     76 
     77 	return be->val[0] << 8 | be->val[1];
     78 }
     79 
     80 u_int32_t
     81 get_uint32(be)
     82 	be_uint32_t *be;
     83 {
     84 
     85 	return be->val[0]<<24 | be->val[1]<<16 | be->val[2]<<8 | be->val[3];
     86 }
     87 
     88 void
     89 put_uint16(be, v)
     90 	be_uint16_t *be;
     91 	unsigned v;
     92 {
     93 
     94 	be->val[0] = (u_int8_t) (v >> 8);
     95 	be->val[1] = (u_int8_t) v;
     96 }
     97 
     98 void
     99 put_uint32(be, v)
    100 	be_uint32_t *be;
    101 	u_int32_t v;
    102 {
    103 
    104 	be->val[0] = (u_int8_t) (v >> 24);
    105 	be->val[1] = (u_int8_t) (v >> 16);
    106 	be->val[2] = (u_int8_t) (v >> 8);
    107 	be->val[3] = (u_int8_t) v;
    108 }
    109 
    110 void *
    111 do_realloc(p, s)
    112 	void *p;
    113 	size_t s;
    114 {
    115 
    116 	p = p ? realloc(p, s) : malloc(s);	/* for portability */
    117 
    118 	if (!p) {
    119 		fprintf(stderr, "malloc failed\n");
    120 		exit(1);
    121 	}
    122 
    123 	return p;
    124 }
    125 
    126 /*
    127  * open an a.out and check the header
    128  */
    129 FILE *
    130 open_aout(fn, hdr)
    131 	const char *fn;
    132 	struct aout_m68k *hdr;
    133 {
    134 	FILE *fp;
    135 	int i;
    136 
    137 	if (!(fp = fopen(fn, "r"))) {
    138 		perror(fn);
    139 		return (FILE *) NULL;
    140 	}
    141 	if (fread(hdr, sizeof(struct aout_m68k), 1, fp) != 1) {
    142 		fprintf(stderr, "%s: can't read a.out header\n", fn);
    143 		goto out;
    144 	}
    145 
    146 	if ((i = AOUT_GET_MAGIC(hdr)) != AOUT_OMAGIC && i != AOUT_NMAGIC) {
    147 		fprintf(stderr, "%s: not a OMAGIC or NMAGIC a.out\n", fn);
    148 		goto out;
    149 	}
    150 
    151 	if ((i = AOUT_GET_MID(hdr)) != AOUT_MID_M68K && i != AOUT_MID_M68K4K) {
    152 		fprintf(stderr, "%s: wrong architecture (mid %d)\n", fn, i);
    153 		goto out;
    154 	}
    155 
    156 	/* if unsolved relocations exist, not an executable but an object */
    157 	if (hdr->a_trsize.hostval || hdr->a_drsize.hostval) {
    158 		fprintf(stderr, "%s: not an executable (object file?)\n", fn);
    159 		goto out;
    160 	}
    161 
    162 	if (AOUT_GET_FLAGS(hdr) & (AOUT_FLAG_PIC | AOUT_FLAG_DYNAMIC)) {
    163 		fprintf(stderr, "%s: PIC and DYNAMIC are not supported\n", fn);
    164 		goto out;
    165 	}
    166 
    167 	/* OK! */
    168 	return fp;
    169 
    170 out:	fclose(fp);
    171 	return (FILE *) NULL;
    172 }
    173 
    174 /*
    175  * look at two a.out headers and check if they are compatible
    176  */
    177 int
    178 check_2_aout_hdrs(h1, h2)
    179 	struct aout_m68k *h1, *h2;
    180 {
    181 
    182 	if (/* compare magic */
    183 	    h1->a_magic.hostval != h2->a_magic.hostval ||
    184 	    /* compare section size */
    185 	    h1->a_text.hostval != h2->a_text.hostval ||
    186 	    h1->a_data.hostval != h2->a_data.hostval ||
    187 	    h1->a_bss.hostval != h2->a_bss.hostval)
    188 		return -1;
    189 
    190 	return 0;
    191 }
    192 
    193 /* allocation unit (in bytes) of relocation table */
    194 #define RELTBL_CHUNK	8192
    195 
    196 /*
    197  * add an entry to the relocation table
    198  */
    199 #define ADD_RELTBL(adr)	\
    200 	if (relsize + sizeof(struct relinf_l) > relallocsize)		    \
    201 		reltbl = do_realloc(reltbl, relallocsize += RELTBL_CHUNK);  \
    202 	if ((adr) < reladdr + HUX_MINLREL) {				    \
    203 		struct relinf_s *r = (struct relinf_s *)(reltbl + relsize); \
    204 		put_uint16(&r->locoff_s, (unsigned)((adr) - reladdr));	    \
    205 		relsize += sizeof(struct relinf_s);			    \
    206 		DPRINTF(("short"));					    \
    207 	} else {							    \
    208 		struct relinf_l *r = (struct relinf_l *)(reltbl + relsize); \
    209 		put_uint16(&r->lrelmag, HUXLRELMAGIC);			    \
    210 		put_uint32((be_uint32_t *)r->locoff_l, (adr) - reladdr);    \
    211 		relsize += sizeof(struct relinf_l);			    \
    212 		DPRINTF(("long "));					    \
    213 	}								    \
    214 	DPRINTF((" reloc 0x%06x", (adr)));				    \
    215 	reladdr = (adr);
    216 
    217 #define ERR1	{ if (ferror(fpa1)) perror(fn1);			\
    218 		  else fprintf(stderr, "%s: unexpected EOF\n", fn1);	\
    219 		  goto out; }
    220 #define ERR2	{ if (ferror(fpa2)) perror(fn2);			\
    221 		  else fprintf(stderr, "%s: unexpected EOF\n", fn2);	\
    222 		  goto out; }
    223 #define ERRC	{ fprintf(stderr, "files %s and %s are inconsistent\n",	\
    224 				  fn1, fn2);				\
    225 		  goto out; }
    226 
    227 /*
    228  * read a.out files and output .x body
    229  * and create relocation table
    230  */
    231 #define CREATE_RELOCATION(segsize)	\
    232 	while (segsize > 0 || nbuf) {					\
    233 		if (nbuf == 0) {					\
    234 			if (fread(&b1.half[0], SIZE_16, 1, fpa1) != 1)	\
    235 				ERR1					\
    236 			if (fread(&b2.half[0], SIZE_16, 1, fpa2) != 1)	\
    237 				ERR2					\
    238 			nbuf = 1;					\
    239 			segsize -= SIZE_16;				\
    240 		} else if (nbuf == 1) {					\
    241 			if (segsize == 0) {				\
    242 				if (b1.half[0].hostval != b2.half[0].hostval) \
    243 					ERRC				\
    244 				fwrite(&b1.half[0], SIZE_16, 1, fpx);	\
    245 				nbuf = 0;				\
    246 				addr += SIZE_16;			\
    247 			} else {					\
    248 				if (fread(&b1.half[1], SIZE_16, 1, fpa1) != 1)\
    249 					ERR1				\
    250 				if (fread(&b2.half[1], SIZE_16, 1, fpa2) != 1)\
    251 					ERR2				\
    252 				nbuf = 2;				\
    253 				segsize -= SIZE_16;			\
    254 			}						\
    255 		} else /* if (nbuf == 2) */ {				\
    256 			if (b1.hostval != b2.hostval &&			\
    257 			    get_uint32(&b1) - loadadr1			\
    258 					== get_uint32(&b2) - loadadr2) {\
    259 				/* do relocation */			\
    260 				ADD_RELTBL(addr)			\
    261 									\
    262 				put_uint32(&b1, get_uint32(&b1) - loadadr1);  \
    263 				DPRINTF((" v 0x%08x\t", get_uint32(&b1)));    \
    264 				fwrite(&b1, SIZE_32, 1, fpx);		\
    265 				nbuf = 0;				\
    266 				addr += SIZE_32;			\
    267 			} else if (b1.half[0].hostval == b2.half[0].hostval) {\
    268 				fwrite(&b1.half[0], SIZE_16, 1, fpx);	\
    269 				addr += SIZE_16;			\
    270 				b1.half[0] = b1.half[1];		\
    271 				b2.half[0] = b2.half[1];		\
    272 				nbuf = 1;				\
    273 			} else						\
    274 				ERRC					\
    275 		}							\
    276 	}
    277 
    278 int
    279 aout2hux(fn1, fn2, loadadr1, loadadr2, fnx)
    280 	const char *fn1, *fn2, *fnx;
    281 	u_int32_t loadadr1, loadadr2;
    282 {
    283 	int status = 1;			/* the default is "failed" */
    284 	FILE *fpa1 = NULL, *fpa2 = NULL;
    285 	struct aout_m68k hdr1, hdr2;
    286 	FILE *fpx = NULL;
    287 	struct huxhdr xhdr;
    288 	u_int32_t textsize, datasize, pagesize, paddingsize, execoff;
    289 
    290 	/* for relocation */
    291 	be_uint32_t b1, b2;
    292 	int nbuf;
    293 	u_int32_t addr;
    294 
    295 	/* for relocation table */
    296 	size_t relsize, relallocsize;
    297 	u_int32_t reladdr;
    298 	char *reltbl = NULL;
    299 
    300 
    301 	/*
    302 	 * check load addresses
    303 	 */
    304 	if (loadadr1 == loadadr2) {
    305 		fprintf(stderr, "two load addresses must be different\n");
    306 		return 1;
    307 	}
    308 
    309 	/*
    310 	 * open a.out files and check the headers
    311 	 */
    312 	if (!(fpa1 = open_aout(fn1, &hdr1)) || !(fpa2 = open_aout(fn2, &hdr2)))
    313 		goto out;
    314 
    315 	/*
    316 	 * check for consistency
    317 	 */
    318 	if (check_2_aout_hdrs(&hdr1, &hdr2)) {
    319 		fprintf(stderr, "files %s and %s are incompatible\n",
    320 				fn1, fn2);
    321 		goto out;
    322 	}
    323 	/* check entry address */
    324 	if (get_uint32(&hdr1.a_entry) - loadadr1 !=
    325 					get_uint32(&hdr2.a_entry) - loadadr2) {
    326 		fprintf(stderr, "address of %s or %s may be incorrect\n",
    327 				fn1, fn2);
    328 		goto out;
    329 	}
    330 
    331 	/*
    332 	 * get information from a.out header
    333 	 */
    334 	textsize = get_uint32(&hdr1.a_text);
    335 	pagesize = AOUT_PAGESIZE(&hdr1);
    336 	paddingsize = ((textsize + pagesize - 1) & ~(pagesize - 1)) - textsize;
    337 	datasize = get_uint32(&hdr1.a_data);
    338 	execoff = get_uint32(&hdr1.a_entry) - loadadr1;
    339 
    340 	DPRINTF(("text: %u, data: %u, page: %u, pad: %u, bss: %u, exec: %u\n",
    341 		textsize, datasize, pagesize, paddingsize,
    342 		get_uint32(&hdr1.a_bss), execoff));
    343 
    344 	if (textsize & 1) {
    345 		fprintf(stderr, "text size is not even\n");
    346 		goto out;
    347 	}
    348 	if (datasize & 1) {
    349 		fprintf(stderr, "data size is not even\n");
    350 		goto out;
    351 	}
    352 	if (execoff >= textsize &&
    353 	    (execoff < textsize + paddingsize ||
    354 	     execoff >= textsize + paddingsize + datasize)) {
    355 		fprintf(stderr, "exec addr is not in text or data segment\n");
    356 		goto out;
    357 	}
    358 
    359 	/*
    360 	 * prepare for .x header
    361 	 */
    362 	bzero((void *) &xhdr, sizeof xhdr);
    363 	put_uint16(&xhdr.x_magic, HUXMAGIC);
    364 	put_uint32(&xhdr.x_entry, execoff);
    365 	put_uint32(&xhdr.x_text, textsize + paddingsize);
    366 	xhdr.x_data.hostval = hdr1.a_data.hostval;
    367 	xhdr.x_bss.hostval = hdr1.a_bss.hostval;
    368 
    369 	/*
    370 	 * create output file
    371 	 */
    372 	if (!(fpx = fopen(fnx, "w")) ||
    373 	    fseek(fpx, (off_t) sizeof xhdr, SEEK_SET)) { /* skip header */
    374 		perror(fnx);
    375 		goto out;
    376 	}
    377 
    378 	addr = 0;
    379 	nbuf = 0;
    380 
    381 	relsize = relallocsize = 0;
    382 	reladdr = 0;
    383 
    384 	/*
    385 	 * text segment
    386 	 */
    387 	CREATE_RELOCATION(textsize)
    388 
    389 	/*
    390 	 * page boundary
    391 	 */
    392 	addr += paddingsize;
    393 	while (paddingsize--)
    394 		putc('\0', fpx);
    395 
    396 	/*
    397 	 * data segment
    398 	 */
    399 	CREATE_RELOCATION(datasize)
    400 
    401 	/*
    402 	 * error check of the above
    403 	 */
    404 	if (ferror(fpx)) {
    405 		fprintf(stderr, "%s: write failure\n", fnx);
    406 		goto out;
    407 	}
    408 
    409 	/*
    410 	 * write relocation table
    411 	 */
    412 	if (relsize > 0) {
    413 		DPRINTF(("\n"));
    414 		if (fwrite(reltbl, 1, relsize, fpx) != relsize) {
    415 			perror(fnx);
    416 			goto out;
    417 		}
    418 	}
    419 
    420 	/*
    421 	 * write .x header at the top of the output file
    422 	 */
    423 	put_uint32(&xhdr.x_rsize, relsize);
    424 	if (fseek(fpx, (off_t) 0, SEEK_SET) ||
    425 	    fwrite(&xhdr, sizeof xhdr, 1, fpx) != 1) {
    426 		perror(fnx);
    427 		goto out;
    428 	}
    429 
    430 	status = 0;	/* all OK */
    431 
    432 out:	/*
    433 	 * cleanup
    434 	 */
    435 	if (fpa1)
    436 		fclose(fpa1);
    437 	if (fpa2)
    438 		fclose(fpa2);
    439 	if (fpx) {
    440 		if (fclose(fpx) && status == 0) {
    441 			/* Alas, final flush failed! */
    442 			perror(fnx);
    443 			status = 1;
    444 		}
    445 		if (status)
    446 			remove(fnx);
    447 	}
    448 	if (reltbl)
    449 		free(reltbl);
    450 
    451 	return status;
    452 }
    453 
    454 #ifndef NO_BIST
    455 void bist PROTO((void));
    456 
    457 /*
    458  * built-in self test
    459  */
    460 void
    461 bist()
    462 {
    463 	be_uint16_t be16;
    464 	be_uint32_t be32;
    465 	be_uint32_t be32x2[2];
    466 
    467 	be16.val[0] = 0x12; be16.val[1] = 0x34;
    468 	be32.val[0] = 0xfe; be32.val[1] = 0xdc;
    469 	be32.val[2] = 0xba; be32.val[3] = 0x98;
    470 
    471 	put_uint16(&be32x2[0].half[1], 0x4567);
    472 	put_uint32(&be32x2[1], 0xa9876543);
    473 
    474 	if (sizeof(u_int8_t) != 1 || sizeof(u_int16_t) != 2 ||
    475 	    sizeof(u_int32_t) != 4 ||
    476 	    SIZE_16 != 2 || SIZE_32 != 4 || sizeof be32x2 != 8 ||
    477 	    sizeof(struct relinf_s) != 2 || sizeof(struct relinf_l) != 6 ||
    478 	    get_uint16(&be16) != 0x1234 || get_uint32(&be32) != 0xfedcba98 ||
    479 	    get_uint16(&be32x2[0].half[1]) != 0x4567 ||
    480 	    get_uint32(&be32x2[1]) != 0xa9876543) {
    481 		fprintf(stderr, "BIST failed\n");
    482 		exit(1);
    483 	}
    484 }
    485 #endif
    486 
    487 int
    488 gethex(pval, str)
    489 	u_int32_t *pval;
    490 	const char *str;
    491 {
    492 	const unsigned char *p = (const unsigned char *) str;
    493 	u_int32_t val;
    494 	int over;
    495 
    496 	/* skip leading "0x" if exists */
    497 	if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X'))
    498 		p += 2;
    499 
    500 	if (!*p)
    501 		goto bad;
    502 
    503 	for (val = 0, over = 0; *p; p++) {
    504 		int digit;
    505 
    506 		switch (*p) {
    507 		case '0': case '1': case '2': case '3': case '4':
    508 		case '5': case '6': case '7': case '8': case '9':
    509 			digit = *p - '0';
    510 			break;
    511 		case 'a': case 'A':	digit = 10; break;
    512 		case 'b': case 'B':	digit = 11; break;
    513 		case 'c': case 'C':	digit = 12; break;
    514 		case 'd': case 'D':	digit = 13; break;
    515 		case 'e': case 'E':	digit = 14; break;
    516 		case 'f': case 'F':	digit = 15; break;
    517 		default:
    518 			goto bad;
    519 		}
    520 		if (val >= 0x10000000 && !over)
    521 			over = 1;
    522 		val = (val << 4) | digit;
    523 	}
    524 
    525 	if (over)
    526 		fprintf(stderr, "warning: %s: constant overflow\n", str);
    527 
    528 	*pval = val;
    529 
    530 	DPRINTF(("gethex: %s -> 0x%x\n", str, val));
    531 
    532 	return 0;
    533 
    534 bad:
    535 	fprintf(stderr, "%s: not a hexadecimal number\n", str);
    536 	return 1;
    537 }
    538 
    539 void
    540 usage(name)
    541 	const char *name;
    542 {
    543 
    544 	fprintf(stderr, "\
    545 usage: %s [ -o output.x ] a.out1 loadaddr1 a.out2 loadaddr2\n\n\
    546 The input a.out files must be static OMAGIC/NMAGIC m68k executable.\n\
    547 Two a.out files must have different loading addresses.\n\
    548 Each of the load address must be a hexadecimal number.\n\
    549 The default output filename is \"%s\".\n" ,name, DEFAULT_OUTPUT_FILE);
    550 
    551 	exit(1);
    552 }
    553 
    554 int
    555 main(argc, argv)
    556 	int argc;
    557 	char *argv[];
    558 {
    559 	const char *outfile = DEFAULT_OUTPUT_FILE;
    560 	u_int32_t adr1, adr2;
    561 
    562 #ifndef NO_BIST
    563 	bist();
    564 #endif
    565 
    566 	if (argc > 2 && argv[1][0] == '-' && argv[1][1] == 'o' && !argv[1][2]) {
    567 		outfile = argv[2];
    568 		argv += 2;
    569 		argc -= 2;
    570 	}
    571 
    572 	if (argc != 5)
    573 		usage(argv[0]);
    574 
    575 	if (gethex(&adr1, argv[2]) || gethex(&adr2, argv[4]))
    576 		usage(argv[0]);
    577 
    578 	return aout2hux(argv[1], argv[3], adr1, adr2, outfile);
    579 }
    580