/* * strip TOS executable format files of symbol table info * usage: strip {-g | -k | -l names} [-t] files ... * * Original version by ++jrb bammi@dsrgsun.ces.cwru.edu * (i.e. Jwahar Bammi) * Modified to add extra options -g -k and -l by * Michal Jaegermann ntomczak@ualtavm.bitnet * November 1st, 1990 * * -g keep all global symbols * -k keep _stksize symbol, so stack size can be adjusted * even for nearly-stripped gcc produced executables * -l nms keep all symbols listed in a file 'nms' * -t remove additional data from end of file * * Modified sources compile both with gcc and Sozobon C * * Added code to deal correctly with extended symbols produced * by -G option of gcc * Both -k and -l options convert extended symbols into * regular ones. * * ++jrb 4/25/91: * Macroized the code to take care of WORD_ALIGNED cross environments. * Minor fixes for a !atari !gcc compiler. * * AL 08/02/92: (alexlehm@iti.informatik.th-darmstadt.de) * Added -t option to strip TurboC / PureC style executables * Support for little endian (BYTE_SWAPed) machines */ #include #ifdef atarist #ifdef __GNUC__ # include # include # include #else #include extern long lread(); extern long lwrite(); extern long lseek(); #endif #endif #ifdef unix # include # define lwrite write # define lread read extern char *malloc(), *realloc(); #else # include #endif #include #define NEWBUFSIZ 16384L char mybuf[NEWBUFSIZ]; char tmpname[128]; #define SYMLEN 8 #define GST_SYMLEN 22 typedef char symstr_t[GST_SYMLEN]; symstr_t stklist[] = {"__stksize", "__initial_stack"}; #ifdef atarist long _stksize = 1L; #endif #ifndef __PROTO # if __STDC__ || __cplusplus # define __PROTO(s) s # else # define __PROTO(s) () # endif #endif /* function pointer type for the select functions */ typedef long (*select_fp) __PROTO((int, int, long, symstr_t *)); void usage __PROTO((char *s )); int main __PROTO((int argc , char **argv )); int strip __PROTO((char *name,symstr_t *nmlist,select_fp select,int cuttrail)); long copy __PROTO((int from , int to , long bytes )); long copy_relo __PROTO((char *name,int from,int to)); void report __PROTO((char *s )); symstr_t *mklist __PROTO((char *fname )); long sel_globs __PROTO((int fd , int tfd , long sbytes , symstr_t *nmlist )); long sel_listed __PROTO((int fd , int tfd , long sbytes , symstr_t *nmlist )); extern char *getenv __PROTO((const char *)); int main (argc, argv) int argc; char **argv; { int status = 0; int flag = -1; int cuttrail = 0; symstr_t *nmlist = (symstr_t *) 0; select_fp select=(select_fp)NULL; #ifdef atarist char *tmpdir; register int l; #endif /* process arguments */ while (argv++, --argc) { if ('-' != **argv) break; (*argv)++; if( **argv=='t' ) { cuttrail = 1; } else { if ((-1) != flag) usage ("only one option at a time is accepted\n"); flag = **argv; switch (flag) { case 'g': select = sel_globs; break; case 'k': nmlist = stklist; select = sel_listed; break; case 'l': (*argv)++; if ('\0' == **argv) { --argc; argv++; if( argc==0 ) usage("missing names file\n"); } if ((symstr_t *) NULL == (nmlist = mklist (*argv))) usage ("cannot create a list of reserved names\n"); select = sel_listed; break; default: usage (""); break; } } } if (argc < 1) { usage (""); } #ifdef __GNUC__ #ifdef atarist tmpname[0] = '\0'; if ((tmpdir = getenv ("TEMP")) != NULL) { strcpy (tmpname, tmpdir); l = (int) strlen (tmpname) - 1; if (tmpname[l] == '\\') tmpname[l] = '\0'; } strcat (tmpname, "\\STXXXXXX"); #else strcpy (tmpname, "/tmp/STXXXXXX"); #endif mktemp (tmpname); #else /* not __GNUC__ */ # ifdef atarist if ((tmpdir = getenv ("TEMP")) != NULL) { strcpy (tmpname, tmpdir); l = (int) strlen (tmpname) - 1; if (tmpname[l] != '\\') { l++; tmpname[l] = '\\'; } l++; } else { l = 0; } tmpnam (&tmpname[l]); # else strcpy (tmpname, "/tmp/STXXXXXX"); mktemp(tmpname); # endif #endif /* __GNUC__ */ do { status |= strip (*argv++, nmlist, select, cuttrail); } while (--argc > 0); unlink (tmpname); return status; } void usage (s) char *s; { report (s); report ("Usage: strip {-k | -l names | -g} [-t] files ...\n"); exit (1); } /* define macro for big and function for little endian machines */ #ifdef BYTE_SWAP short swap_short __PROTO((short s)); long swap_long __PROTO((long l)); #else #define swap_short(s) (s) #define swap_long(l) (l) #endif #if (defined(__GNUC__)) && (!defined(unix)) #include #else /* include relevant fragments of file directly */ struct aexec { short a_magic; /* magic number */ unsigned long a_text; /* size of text segment */ unsigned long a_data; /* size of initialized data */ unsigned long a_bss; /* size of uninitialized data */ unsigned long a_syms; /* size of symbol table */ unsigned long a_AZero1; /* always zero */ unsigned long a_AZero2; /* always zero */ unsigned short a_isreloc; /* is reloc info present */ }; #define CMAGIC 0x601A /* contiguous text */ /* * Format of a symbol table entry */ struct asym { char a_name[SYMLEN]; /* symbol name */ unsigned short a_type; /* type flag */ unsigned long a_value; /* value of this symbol (or sdb offset) */ }; #define A_GLOBL 0x2000 /* global */ #define A_LNAM 0x0048 /* extended name */ #endif /* __GNUC__ */ #if !__STDC__ && !__cplusplus # ifndef offsetof # define offsetof(TYPE, MEMBER) ((unsigned long) &((TYPE *)0L)->MEMBER) # endif # ifndef CROSSHPUX typedef unsigned long size_t; # endif #endif #ifdef WORD_ALIGNED # define SIZEOF_AEXEC ((2*sizeof(short)) + (6*sizeof(long))) # define SIZEOF_ASYM ((SYMLEN*sizeof(char)) + sizeof(short) + sizeof(long)) # define SYM_OFFSET (sizeof(short) + (3*sizeof(long))) int read_head __PROTO((int fd, struct aexec *a)); #else # define SIZEOF_AEXEC (sizeof(struct aexec)) # define SIZEOF_ASYM (sizeof(struct asym)) # define SYM_OFFSET (offsetof (struct aexec, a_syms)) #endif int strip (name, nmlist, select, cuttrail) char *name; symstr_t * nmlist; select_fp select; int cuttrail; { register int fd; register int tfd; register long count, rbytes, sbytes; long lbytes, swap_lbytes; struct aexec ahead; if ((fd = open (name, O_RDONLY, 0666)) < 0) { perror (name); return 2; } if ((tfd = open (tmpname, O_WRONLY | O_TRUNC | O_CREAT, 0644)) < 0) { perror (tmpname); close (fd); return 4; } #ifndef WORD_ALIGNED if ((count = lread (fd, &ahead, (long) sizeof (ahead))) != (long) sizeof (ahead)) { #else if (read_head (fd, &ahead)) { #endif perror (name); close (tfd); close (fd); return 8; } if (swap_short(ahead.a_magic)!=CMAGIC) { report (name); report (": Bad Magic number\n"); close (tfd); close (fd); return 0x10; } sbytes = swap_long(ahead.a_syms); if (0L == sbytes && !cuttrail) { report (name); report (": Already Stripped\n"); close (tfd); close (fd); return 0x20; } if (lseek (fd, 0L, 0) < 0) { report (name); report (": seek error\n"); close (tfd); close (fd); return 0x40; } count = SIZEOF_AEXEC + swap_long(ahead.a_text) + swap_long(ahead.a_data); if (copy (fd, tfd, count) != count) { close (tfd); close (fd); return 0x80; } if (NULL == select) { /* remove whole symbol table */ lbytes = 0L; if (lseek (fd, sbytes, 1) < 0) { report (name); report (": seek error\n"); close (tfd); close (fd); return 0x100; } } else { lbytes = ( *select )(fd, tfd, sbytes, nmlist); } if( cuttrail ) { if((rbytes = copy_relo(name,fd,tfd))<0) { close(tfd); close(fd); return 0x8000; } if( lread( fd, &cuttrail, 1 )!=1 ) { report (name); report (": Already Stripped\n"); close (tfd); close (fd); return 0x20; } } else { if ((rbytes = copy (fd, tfd, 0x7fffffffL)) < 0) { close (tfd); close (fd); return 0x200; } } if (lseek (tfd, (long)(SYM_OFFSET), 0) < 0) { close (tfd); close (fd); return 0x400; } swap_lbytes=swap_long(lbytes); if (lwrite (tfd, &swap_lbytes, (long)sizeof (lbytes)) != (long) sizeof (lbytes)) { close (tfd); close (fd); return 0x800; } close (tfd); close (fd); if (rename(tmpname, name) == 0) return 0; /* try to rename it */ if ((fd = open (name, O_WRONLY | O_TRUNC | O_CREAT, 0666)) < 0) { perror (name); return 0x1000; } if ((tfd = open (tmpname, O_RDONLY, 0666)) < 0) { perror (tmpname); close (fd); return 0x2000; } count = SIZEOF_AEXEC + swap_long(ahead.a_text) + swap_long(ahead.a_data) + rbytes + lbytes; if (copy (tfd, fd, count) != count) { close (tfd); close (fd); return 0x4000; } close (tfd); close (fd); return 0; } /* * copy from, to in NEWBUFSIZ chunks upto bytes or EOF whichever occurs first * returns # of bytes copied */ long copy (from, to, bytes) int from, to; long bytes; { register long todo, done = 0L, remaining = bytes, actual; while (done != bytes) { todo = (remaining > NEWBUFSIZ) ? NEWBUFSIZ : remaining; if ((actual = lread (from, mybuf, todo)) != todo) { if (actual < 0) { report ("Error Reading\n"); return -done; } } if (lwrite (to, mybuf, actual) != actual) { report ("Error Writing\n"); return -done; } done += actual; if (actual != todo) /* eof reached */ return done; remaining -= actual; } return done; } /* copy TOS relocation table from `from` to `to`. Copy bytes until NUL byte or first 4 bytes if == 0l. returns length of relocation table or -1 in case of an error */ long copy_relo(name,from,to) char *name; int from,to; { long res=0; long bytes; char *end; int finished=0; res=lread( from, mybuf, sizeof(long) ); if( res!=0 && res!=sizeof(long) ) { report( "Error reading\n" ); return -1; } if( res==0 ) { report("Warning: "); /* I think empty relocation tables are allowed, report(name); but could cause trouble with certain programs */ report( ": No relocation table\n" ); return 0; } if( lwrite(to,mybuf,res)!=res ) { report(name); report( ": Error writing\n" ); return -1; } res=sizeof(long); if( *(long*)mybuf == 0 ) return res; /* This is a clean version of an empty relocation table */ while(!finished) { if( (bytes=lread(from,mybuf,NEWBUFSIZ))<0 ) { report(name); report( ": Error reading\n" ); return -1; } if( bytes==0 ) { report( "Warning: " ); report(name); report( ": Unterminated relocation table\n" ); return res; } /* dont know if sozobon has memchr */ end=memchr(mybuf,0,bytes); if(end) { bytes=end-mybuf+1; finished=1; } if( lwrite(to,mybuf,bytes)!=bytes ) { report(name); report(": Error writing\n"); return -1; } } } void report (s) char * s; { lwrite (2, s, (long) strlen (s)); } /* * Given a name of a file with reserved symbols create an array of * reserved symbol names. To terminate create an entry which starts * with a null character. */ #define LBUFSIZE 128 #define NMSTEP 10 symstr_t * mklist (fname) char * fname; { FILE *fp; symstr_t *list = (symstr_t *) NULL; int left = 0; int pos = 0; int i; size_t max_size = 1; char lbuf[LBUFSIZE]; char *in, *out; #ifdef atarist if (NULL == (fp = fopen (fname, "rt"))) { #else if (NULL == (fp = fopen (fname, "r"))) { #endif report (fname); report (" -- "); usage ("cannot open this file\n"); } while (NULL != fgets (lbuf, LBUFSIZE, fp)) { if (0 == left) { max_size += NMSTEP; if ((symstr_t *) NULL == list) { list = (symstr_t *) malloc ( max_size * sizeof (symstr_t)); } else { list = (symstr_t *) realloc ((void *) list, max_size * sizeof (symstr_t)); } if ((symstr_t *) NULL == list) { report ("out of memory making symbol list\n"); exit (-3); } left = NMSTEP; } /* strip all leading white space */ in = lbuf; while (' ' == *in || '\t' == *in) in++; if ('\n' == *in) continue; /* empty line - skip it */ out = &list[pos][0]; for (i = GST_SYMLEN; i > 0; --i) { if ('\n' == *in || ' ' == *in || '\t' == *in) { while (i-- > 0) *out++ = '\0'; break; } *out++ = *in++; } pos++; --left; } /* while */ if ((symstr_t *) NULL != list) { list[pos][0] = '\0'; /* terminate created list */ } return (list); } /* * From a file handle fd to a file handle tfd copy up to 'sbytes' bytes * of a symbol table selecting only those symbols which have A_GLOBL * flag set. Table nmlist is not really used, but is here for a uniform * interface. Returns a number of bytes copied. */ long sel_globs (fd, tfd, sbytes, nmlist) int fd, tfd; long sbytes; symstr_t * nmlist; { long lbytes = 0; struct asym cur_sym; int cont = 0; while (sbytes) { if ((long)SIZEOF_ASYM != lread (fd, &cur_sym, (long)SIZEOF_ASYM)) { report ("error on reading symbol table\n"); break; } if (0 == cont) { /* if we are not dealing with the second part */ cont = (0 != (swap_short(cur_sym.a_type) & A_LNAM)); if (swap_short(cur_sym.a_type) & A_GLOBL) { cont = -cont; if ((long)SIZEOF_ASYM != lwrite (tfd, &cur_sym, (long)SIZEOF_ASYM)) { report ("error on writing symbol table\n"); break; } lbytes += SIZEOF_ASYM; } } else { /* this was an extended symbol */ if (cont < 0) { /* if global then write */ if ((long)SIZEOF_ASYM != lwrite (tfd, &cur_sym, (long)SIZEOF_ASYM)) { report ("error on writing symbol table\n"); break; } lbytes += SIZEOF_ASYM; } cont = 0; } sbytes -= SIZEOF_ASYM; } return (lbytes); } /* * From a file handle fd to a file handle tfd copy up to 'sbytes' bytes * of a symbol table selecting only those symbols which are on nmlist. * Free nmlist if different from a default global one. * Returns a number of bytes copied. */ long sel_listed (fd, tfd, sbytes, nmlist) int fd, tfd; long sbytes; symstr_t * nmlist; { long lbytes = 0; symstr_t *kname; struct asym cur_sym, spare; if ((symstr_t *) NULL == nmlist) return (0L); while (sbytes) { if ((long)SIZEOF_ASYM != lread (fd, &cur_sym, (long) SIZEOF_ASYM)) { report ("error on reading symbol table\n"); break; } for (kname = nmlist; '\0' != **kname; kname++) { if (0 == strncmp (&(*kname)[0], &cur_sym.a_name[0], SYMLEN)) { if ((A_LNAM & swap_short(cur_sym.a_type)) == A_LNAM) { /* if extended */ if ((long)SIZEOF_ASYM != lread (fd, &spare, (long)SIZEOF_ASYM)) { report ("error on reading symbol table\n"); goto leave; /* skip two loop levels */ } sbytes -= SIZEOF_ASYM; if (strncmp (&(*kname)[SYMLEN], (char *)&spare, GST_SYMLEN-SYMLEN)) continue; } if ((long)SIZEOF_ASYM != lwrite (tfd, &cur_sym, (long)SIZEOF_ASYM)) { report ("error on writing symbol table\n"); goto leave; } if ((swap_short(cur_sym.a_type) & A_LNAM) == A_LNAM) { if (lwrite (tfd, &spare, (long)SIZEOF_ASYM) != SIZEOF_ASYM) { report ("error on writing symbol table\n"); goto leave; } lbytes += SIZEOF_ASYM; } lbytes += SIZEOF_ASYM; break; } } /* for */ sbytes -= SIZEOF_ASYM; } /* while */ leave: if (nmlist != stklist) { free (nmlist); nmlist = (symstr_t *) NULL; } return (lbytes); } #ifdef WORD_ALIGNED /* * read header -- return !0 on err */ #define ck_read(fd, addr, siz) \ if((long)siz != lread(fd, addr, (long)siz)) return !0; int read_head (fd, a) int fd; struct aexec *a; { ck_read(fd, &a->a_magic, sizeof(a->a_magic)); ck_read(fd, &a->a_text, sizeof(a->a_text)); ck_read(fd, &a->a_data, sizeof(a->a_data)); ck_read(fd, &a->a_bss, sizeof(a->a_bss)); ck_read(fd, &a->a_syms, sizeof(a->a_syms)); ck_read(fd, &a->a_AZero1, sizeof(a->a_AZero1)); ck_read(fd, &a->a_AZero2, sizeof(a->a_AZero2)); ck_read(fd, &a->a_isreloc, sizeof(a->a_isreloc)); return 0; } #endif #ifdef CROSSHPUX char *xmalloc(n) unsigned n; { extern char *malloc(); char *ret = malloc(n); if(!ret) { fprintf(stderr,"Out of memory!\n"); exit(1); } return ret; } #endif #ifdef BYTE_SWAP /* these routines work for big endian machines as well, but are not needed */ long swap_long(l) long l; { long l1=l; unsigned char *p=(unsigned char *)&l1; return (long)p[3]|((long)p[2]<<8)|((long)p[1]<<16)|((long)p[0]<<24); } short swap_short(s) short s; { short s1=s; unsigned char *p=(unsigned char *)&s1; return (short)p[1]|((short)p[0]<<8); } #endif