/* Copyright Motorola, Inc. 1993, 1994 ALL RIGHTS RESERVED You are hereby granted a copyright license to use, modify, and distribute the SOFTWARE so long as this entire notice is retained without alteration in any modified and/or redistributed versions, and that such modified versions are clearly identified as such. No licenses are granted by implication, estoppel or otherwise under any patents or trademarks of Motorola, Inc. The SOFTWARE is provided on an "AS IS" basis and without warranty. To the maximum extent permitted by applicable law, MOTOROLA DISCLAIMS ALL WARRANTIES WHETHER EXPRESS OR IMPLIED, INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE AND ANY WARRANTY AGAINST INFRINGEMENT WITH REGARD TO THE SOFTWARE (INCLUDING ANY MODIFIED VERSIONS THEREOF) AND ANY ACCOMPANYING WRITTEN MATERIALS. To the maximum extent permitted by applicable law, IN NO EVENT SHALL MOTOROLA BE LIABLE FOR ANY DAMAGES WHATSOEVER (INCLUDING WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR OTHER PECUNIARY LOSS) ARISING OF THE USE OR INABILITY TO USE THE SOFTWARE. Motorola assumes no responsibility for the maintenance and support of the SOFTWARE. */ /*M************************************************************************ MODULE NAME: xc2srec - convert a.out file to s-record file DESCRIPTION: xc2srec converts a System V/68 a.out file to a Motorola S-record file. Synopsis: xc2srec [-e entry] [-p address] [-a] afile mxfile where: afile is the a.out file to convert mxfile is the output S-record file To create this program, enter: cc -O -o xc2srec xc2srec.c -lld CONTENTS: UPDATE HISTORY: Date Name Rev Changes ------ ---- --- ------- 051784 John Waycott 1.0 Modified original to conform to standard Use pmesg() to print messages 061884 John Waycott 1.1 Added code to check for bad options and bad COFF files. 072084 John Waycott 1.2 Added getopt() to check for options. Added code to set .bss section to zeroes. 082284 John Waycott 1.3 Fixed problem with S2 record checksum calculation; cleanup. 121184 Dave S. 1.4 Made version of xc2srec that is stand-alone (doesn't rely on pmesg) and works on the vax. 031390 Don A. 1.5 Ported to MVME181 (88000 based) - eliminated check in p_coff() function for MAGIC and changed HEX() define to convert upper case char to lower case. 6/15/93 Matt H. 1.6 Several changes: *Changed offset (given by -p) to an unsigned offset. NOTE: This precludes moving things down in memory! This was accomplished by changing declaration of o_addr to unsigned long, and under the -p case, calling strtoul unstead of strtol. *The SRecord builder skips over the xcoff section called ".loader" This section will overwrite some previously loaded segments. *Added some feedback so we know what sections are being SRecord-ized. *Included a new option (-s) that switches the bytes with 64-bit boundaries. This hack helps us go from a big to little endian platforms. ***************************************************************************/ #include #include #include #include #include #include #include /* following should be defined for the particular machine */ #define MAGIC MC68MAGIC /* system V/68 */ /* #define HEX(c) (c - (isdigit(c) ? '0' : 'a' - 10)) */ #define HEX(c) (tolower(c) - (isdigit(tolower(c)) ? '0' : 'a' - 10)) #define HEXTOI(s) (HEX(s[0]) * 16 + HEX(s[1])) /* S-record constants, see S-record file format */ #define S_TYPE 2 #define S_REC_LENGTH 2 #define S_CKSUM 2 #define S_TOT_LENGTH 72 #define DIRSIZ 14 /* max characters in unix filename */ #define MSGPATH "versatools xc2srec " /* pmesg() message path */ /* external functions */ extern char *malloc(); extern long strtol(); /* module-wide functions */ void fatal(), /* print message and abort */ p_s123_rec(), /* start record */ s_flush_rec(), /* flush record to disk */ s_putbuf(), /* add characters to buffer */ put_hex(), /* write hex number */ put_str_hex(); /* write hex number */ FILE *ofp; /* output file pointer */ FILE *ifp; /* input file pointer */ LDFILE *ldptr = NULL; /* pointer to common object file */ AOUTHDR optionalhdr; /* optional header */ SCNHDR secthead; /* section header */ long entry; /* entry point */ int p_addr = 0; /* current address */ unsigned long o_addr = 0;/* offset requested by user */ int line_addr = 0; /* current position in output S-record line */ int e_o_recflg = 1; /* need to start new S-record */ char s_buf[S_TOT_LENGTH+10]; /* buffer to hold S-record */ char *ptr_s_buf; /* pointer to above buffer */ /* We use this as a flag to determine if the user wants us to do some byte-swapping on the instructions in order to get them into a little-endian format. */ char big_to_little = 0; /*F************************************************************************ FUNCTION NAME: main - mainline for xc2srec DESCRIPTION: This is the main routine for xc2srec. It opens the files, checks arguments, and performs the conversion of the a.out file to S-record file. RECEIVES: argc, argv - command line arguments. RETURNS: exits with a zero status if conversion was successful, else returns with a non-zero status. ***************************************************************************/ main(argc,argv) int argc; char *argv[]; { int c; int ifile = 0; char outfile[80]; int allflg = 0; extern int optind; extern char *optarg; void p_coff(), p_file(), cr_name(), p_s0_rec(), p_s789_rec(); if(argc < 2) fatal("usage"); *outfile = NULL; while ((c = getopt(argc, argv, "ap:es")) != EOF) { switch(c) { case 'a': allflg++; break; case 'p': o_addr = strtoul(optarg,(char **)NULL,0); break; case 'e': entry = strtol(optarg,(char **)NULL,0); break; case 's': big_to_little = 1; printf( "Byte-swapping within 64-bit boundaries!\n" ); break; case '?': fatal("usage"); break; } } if ((ifile = optind++) >= argc) fatal("no-ifile"); if((ifp=fopen(argv[ifile],"r")) == NULL) fatal("bad-ifile", argv[ifile]); if (optind < argc) strcpy(outfile,argv[optind]); if(!*outfile) /* create output file name if needed */ cr_name(argv[ifile],outfile); if((ofp=fopen(outfile,"w")) == NULL) fatal("bad-ofile",outfile); ptr_s_buf = s_buf; p_s0_rec((unsigned char *)argv[ifile]); /* header record S0 */ if(!allflg) p_coff(argv[ifile]); else p_file(); p_s789_rec(); /* tail record S7, S8, or S9 record */ fclose(ifp); fclose(ofp); } /*F************************************************************************ FUNCTION NAME: p_coff - process coff file DESCRIPTION: p_coff() reads each section of a coff file and outputs S-records accordingly. Addresses are adjusted if necessary to take into account the user's requested offset. RECEIVES: fname - name of coff file RETURNS: none. ***************************************************************************/ void p_coff(fname) char *fname; { int i; unsigned short sect; /* current coff section being processed */ /* I use these for shuffling between big and little endian formats. See below. */ char ch[8]; signed char cnt; char pad; long p_addr1; if((ldptr = ldopen(fname,ldptr)) == NULL ) fatal("bad-coff",fname); /* seek to, and read optional header */ ldohseek(ldptr); FREAD(&optionalhdr,sizeof(struct aouthdr),1,ldptr); /* process each section */ for(sect = 1; sect <= HEADER(ldptr).f_nscns; sect++) { printf( "\b\b" ); printf( "Now encoding section: %s\n", secthead.s_name ); ldshread(ldptr,sect,§head); if(secthead.s_size <= 0L) continue; FSEEK(ldptr,secthead.s_scnptr,0); p_addr = secthead.s_paddr + o_addr; if (secthead.s_scnptr != NULL) { for(i = 0; i < secthead.s_size; i++) { /* Here, I'm checking for the ".loader" section because we don't want to look at that particular section. I don't know why, but this section overwrites memory that I've defined via previous SRecords. The brute-force approach is to just skip over this section and it seems to be working. */ if((strcmp(secthead.s_name,".loader") != 0)) { if( big_to_little == 1 ) { /* If we get here, we are going to produce little-endian formatted SRecords. We read in 8 chars and reverse them. */ pad = p_addr & 7; p_addr = p_addr - pad; for(cnt=0;cnt=0;cnt--) { /* === DEBUG STATEMENT === printf( "0x%08lx\t%d\n", p_addr, ch[cnt] ); */ p_s123_rec( ch[cnt] ); } } else { /* If we get here, process executable into SRecords without any byte-switching. */ ch[0] = GETC( ldptr ); /* === DEBUG STATEMENT === printf( "0x%08lx\t%d\n", p_addr, ch[cnt] ); */ p_s123_rec( ch[0] ); } } } /* end of "for loop" */ } else { p_s123_rec(0); } if(!e_o_recflg) { s_flush_rec(); } } ldclose(ldptr); if(!entry) entry = optionalhdr.entry + o_addr; printf( "\b\b" ); } /*F************************************************************************ FUNCTION NAME: p_file - process an ascii file DESCRIPTION: p_file() converts a file to S-records without regard to its contents. RECEIVES: none. RETURNS: none. ***************************************************************************/ void p_file() { unsigned char c; for(c = getc(ifp); !feof(ifp); c = getc(ifp)) p_s123_rec(c); if(!e_o_recflg) s_flush_rec(); } /*F************************************************************************ FUNCTION NAME: p_s0_rec - output s0 header record DESCRIPTION: p_s0_rec() outputs the initial S0 record to the output file. RECEIVES: fname - file name RETURNS: none. ***************************************************************************/ void p_s0_rec(fname) unsigned char *fname; { fprintf(ofp,"S0"); put_hex(0); /* address field */ put_hex(0); /* address field */ while(*fname) put_hex(*fname++); s_flush_rec(); } /*F************************************************************************ FUNCTION NAME: p_s123_rec - output data records DESCRIPTION: p_s123_rec() outputs a data buffer as an S1, S2, or S3 record, depending on the address size. RECEIVES: cbuf - data buffer. RETURNS: none. ***************************************************************************/ void p_s123_rec(cbuf) unsigned char cbuf; { void p_123_header(); if(e_o_recflg) { p_123_header(); e_o_recflg = 0; } put_hex((int)cbuf); line_addr += 2; p_addr++; if(line_addr >= S_TOT_LENGTH) s_flush_rec(); } /*F************************************************************************ FUNCTION NAME: p_123_header - output record type header DESCRIPTION: p_123_header() outputs the record header and load address of each record. RECEIVES: none. RETURNS: none. ***************************************************************************/ void p_123_header() { char tbuf[10]; char *type; int addr_size; sprintf(tbuf,"%X",p_addr); addr_size = strlen(tbuf); if(addr_size < 5) { type = "S1"; addr_size = 4; } else if(addr_size < 7) { type = "S2"; addr_size = 6; } else { type = "S3"; addr_size = 8; } fprintf(ofp,"%s",type); put_str_hex(p_addr,addr_size); line_addr = S_TYPE + S_REC_LENGTH + addr_size; } /*F************************************************************************ FUNCTION NAME: p_s789_rec - output termination record DESCRIPTION: p_s789_rec() writes the final output record to the mx file. S7, S8, or S9 is chosen depending on the size of the entry address. RECEIVES: none. RETURNS: none. ***************************************************************************/ void p_s789_rec() { char tbuf[10]; int i; if(entry) { sprintf(tbuf,"%lX",entry); i = strlen(tbuf); if(i < 5) { fputs("S9",ofp); put_str_hex(entry,4); } else if(i < 7) { fputs("S8",ofp); put_str_hex(entry,6); } else { fputs("S7",ofp); put_str_hex(entry,8); } s_flush_rec(); } else fputs("S9030000FC\n",ofp); /* entry at 0 */ } /*F************************************************************************ FUNCTION NAME: put_hex - output hex byte to mx file put_str_hex - output hex bytes to mx file DESCRIPTION: put_hex() and put_str_hex() write hex bytes to the mx file. put_hex() accepts and outputs one byte, while put_str_hex() accepts one byte and a field length, and provides leading zeroes to fill the entire field. RECEIVES: num - integer to output. fs - field size (put_str_hex) RETURNS: none. ***************************************************************************/ void put_hex(num) int num; { char tbuf[10]; sprintf(tbuf,"%2.02X",num); s_putbuf(tbuf); } void put_str_hex(num,fs) long num; int fs; { char tbuf[10],tbuf1[10]; sprintf(tbuf1,"%%%d.0%dlX",fs,fs); /* format ie: %4.04lX */ sprintf(tbuf,tbuf1,num); s_putbuf(tbuf); } /*F************************************************************************ FUNCTION NAME: s_putbuf - write data to buffer DESCRIPTION: s_putbuf() writes the characters in the argument to the buffer, and updates the buffer pointer. RECEIVES: s - character string to append to buffer RETURNS: none. ***************************************************************************/ void s_putbuf(s) char *s; { while(*ptr_s_buf = *s++) ptr_s_buf++; } /*F************************************************************************ FUNCTION NAME: s_flush_rec - flush record to mx file DESCRIPTION: s_flush_rec() writes the current buffer, count, and checksum to the mx file. RECEIVES: length - length of record RETURNS: none. ***************************************************************************/ void s_flush_rec() { char tbuf[10]; unsigned char checksum; int length; *ptr_s_buf = NULL; length = strlen(s_buf) / 2 + 1; /* write out length of record */ sprintf(tbuf,"%2.02X",length); /* length is in pairs of chars */ fputs(tbuf,ofp); /* calculate the checksum */ checksum = length; for (ptr_s_buf = s_buf; *ptr_s_buf; ptr_s_buf += 2) checksum += HEXTOI(ptr_s_buf); /* write out and reset buffer */ fputs(s_buf,ofp); ptr_s_buf = s_buf; e_o_recflg = 1; /* write out checksum and newline character */ checksum = ~checksum; fprintf(ofp, "%2.02X\n", checksum); } /*F************************************************************************ FUNCTION NAME: cr_name - create output file name DESCRIPTION: cr_name() creates the output filename by appending a .mx to the end of the input file name. If the input file has an extension of .o, it is removed. RECEIVES: ifile - input filename OUTPUTS: ofile - output filename RETURNS: none. ***************************************************************************/ void cr_name(ifile,ofile) char *ifile, *ofile; { char *p1,*p2; int len; len = strlen(ifile); for(p1 = ifile, p2 = ofile; *p1; p1++) if(*p1 == '/') { len = strlen((char *)p1 + 1); p2 = ofile; } else *p2++ = *p1; if(len > DIRSIZ-3) fatal("ofile-toolong"); strcpy(p2,".mx"); } /*F************************************************************************ FUNCTION NAME: fatal - print fatal message and abort DESCRIPTION: prints a message from the message file and aborts. RECEIVES: msg - message topic arg1 - option argument RETURNS: does not return. ***************************************************************************/ /* VARARGS1 */ void fatal(msg, arg1) char *msg, *arg1; { char topic[256]; #ifdef rs6000 /* was vax */ #define MATCH 0 if (strcmp(msg,"usage") == MATCH) { fprintf(stderr,"Invalid command line syntax.\n"); fprintf(stderr,"usage: xc2srec [-a] [-p addr] [-e entry] ifile [mxfile]\n"); fprintf(stderr,"Where:\n"); fprintf(stderr," -a process file as an ascii text file\n"); fprintf(stderr," -p addr Use addr as the base memory address.\n"); fprintf(stderr," -e entry Use entry as the entry point address.\n"); fprintf(stderr,"\n"); fprintf(stderr," ifile input filename\n"); fprintf(stderr," mxfile output filename (defaults to ifile.mx)\n"); } else if (strcmp(msg,"no-ifile") == MATCH) { fprintf(stderr,"You must specify an input file.\n"); } else if (strcmp(msg,"bad-ifile") == MATCH) { fprintf(stderr,"Cannot open file %s for reading.\n"); } else if (strcmp(msg,"bad-ofile") == MATCH) { fprintf(stderr,"Cannot open file %s for writing.\n"); } else if (strcmp(msg,"bad-coff") == MATCH) { fprintf(stderr,"Unable to read COFF file.\n"); fprintf(stderr,"Check to make sure it is a valid COFF file for this system.\n"); } else if (strcmp(msg,"ofile-toolong") == MATCH) { fprintf(stderr,"Input filename is too long to append .mx extension.\n"); } else { fprintf(stderr,"Internal error: unknown xc2srec topic : %s.\n",msg); } #else strcpy(topic, MSGPATH); strcat(topic, msg); /* pmesg(stderr, topic, arg1); */ #endif exit(1); }