1: /* 2: * Program Name: symcompact.c 3: * Date: January 21, 1994 4: * Author: S.M. Schultz 5: * 6: * ----------------- Modification History --------------- 7: * Version Date Reason For Modification 8: * 1.0 21Jan94 1. Initial release into the public domain. 9: * 1.1 11Feb94 2. Remove register symbols to save memory. 10: */ 11: 12: /* 13: * This program compacts the symbol table of an executable. This is 14: * done by removing '~symbol' references when _both_ the '~symbol' and 15: * '_symbol' have an overlay number of 0. The assembler always generates 16: * both forms. The only time both forms are needed is in an overlaid 17: * program and the routine has been relocated by the linker, in that event 18: * the '_' form is the overlay "thunk" and the '~' form is the actual 19: * routine itself. Only 'text' symbols have both forms. Reducing the 20: * number of symbols greatly speeds up 'nlist' processing as well as 21: * cutting down memory requirements for programs such as 'adb' and 'nm'. 22: * 23: * NOTE: This program attempts to hold both the string and symbol tables 24: * in memory. For the kernel which has not been 'strcompact'd this 25: * amounts to about 49kb. IF this program runs out of memory you should 26: * run 'strcompact' first - that program removes redundant strings, 27: * significantly reducing the amount of memory needed. Alas, this program 28: * will undo some of strcompact's work and you may/will need to run 29: * strcompact once more after removing excess symbols. 30: * 31: * Register symbols are removed to save memory. This program was initially 32: * used with a smaller kernel, adding an additional driver caused the symbol 33: * table to grow enough that memory couldn't be allocated for strings. See 34: * the comments in 'symorder.c' - they explain why register variables are 35: * no big loss. 36: */ 37: 38: #include <stdio.h> 39: #include <a.out.h> 40: #include <ctype.h> 41: #include <signal.h> 42: #include <string.h> 43: #include <sysexits.h> 44: #include <sys/file.h> 45: 46: char *Pgm; 47: static char strtmp[20]; 48: 49: main(argc, argv) 50: int argc; 51: char **argv; 52: { 53: FILE *fp, *strfp; 54: int cnt, nsyms, len, c, symsremoved = 0, i; 55: void cleanup(); 56: char *strtab; 57: char fbuf1[BUFSIZ], fbuf2[BUFSIZ]; 58: off_t symoff, stroff, ltmp; 59: long strsiz; 60: register struct nlist *sp, *sp2; 61: struct nlist *symtab, *symtabend, syment; 62: struct xexec xhdr; 63: 64: Pgm = argv[0]; 65: signal(SIGQUIT, cleanup); 66: signal(SIGINT, cleanup); 67: signal(SIGHUP, cleanup); 68: 69: if (argc != 2) 70: { 71: fprintf(stderr, "%s: filename argument missing\n", Pgm); 72: exit(EX_USAGE); 73: } 74: fp = fopen(argv[1], "r+"); 75: if (!fp) 76: { 77: fprintf(stderr, "%s: can't open '%s' for update\n", Pgm, 78: argv[1]); 79: exit(EX_NOINPUT); 80: } 81: setbuf(fp, fbuf1); 82: cnt = fread(&xhdr, 1, sizeof (xhdr), fp); 83: if (cnt < sizeof (xhdr.e)) 84: { 85: fprintf(stderr, "%s: Premature EOF reading header\n", Pgm); 86: exit(EX_DATAERR); 87: } 88: if (N_BADMAG(xhdr.e)) 89: { 90: fprintf(stderr, "%s: Bad magic number\n", Pgm); 91: exit(EX_DATAERR); 92: } 93: nsyms = xhdr.e.a_syms / sizeof (struct nlist); 94: if (!nsyms) 95: { 96: fprintf(stderr, "%s: '%s' stripped\n", Pgm); 97: exit(EX_OK); 98: } 99: stroff = N_STROFF(xhdr); 100: symoff = N_SYMOFF(xhdr); 101: /* 102: * Seek to the string table size longword and read it. Then attempt to 103: * malloc memory to hold the string table. First make a sanity check on 104: * the size. 105: */ 106: fseek(fp, stroff, L_SET); 107: fread(&strsiz, sizeof (long), 1, fp); 108: if (strsiz > 48 * 1024L) 109: { 110: fprintf(stderr, "%s: string table > 48kb\n", Pgm); 111: exit(EX_DATAERR); 112: } 113: strtab = (char *)malloc((int)strsiz); 114: if (!strtab) 115: { 116: fprintf(stderr, "%s: no memory for strings\n", Pgm); 117: exit(EX_OSERR); 118: } 119: /* 120: * Now read the string table into memory. Reduce the size read because 121: * we've already retrieved the string table size longword. Adjust the 122: * address used so that we don't have to adjust each symbol table entry's 123: * string offset. 124: */ 125: cnt = fread(strtab + sizeof (long), 1, (int)strsiz - sizeof (long), fp); 126: if (cnt != (int)strsiz - sizeof (long)) 127: { 128: fprintf(stderr, "%s: Premature EOF reading strings\n", Pgm); 129: exit(EX_DATAERR); 130: } 131: /* 132: * Seek to the symbol table. Scan it and count how many symbols are 133: * significant. 134: */ 135: fseek(fp, symoff, L_SET); 136: cnt = 0; 137: for (i = 0; i < nsyms; i++) 138: { 139: fread(&syment, sizeof (syment), 1, fp); 140: if (exclude(&syment)) 141: continue; 142: cnt++; 143: } 144: 145: /* 146: * Allocate memory for the symbol table. 147: */ 148: symtab = (struct nlist *)malloc(cnt * sizeof (struct nlist)); 149: if (!symtab) 150: { 151: fprintf(stderr, "%s: no memory for symbols\n", Pgm); 152: exit(EX_OSERR); 153: } 154: 155: /* 156: * Now read the symbols in, excluding the same ones as before, and 157: * assign the in-memory string addresses at the same time 158: */ 159: sp = symtab; 160: fseek(fp, symoff, L_SET); 161: 162: for (i = 0; i < nsyms; i++) 163: { 164: fread(&syment, sizeof (syment), 1, fp); 165: if (exclude(&syment)) 166: continue; 167: bcopy(&syment, sp, sizeof (syment)); 168: sp->n_un.n_name = strtab + (int)sp->n_un.n_strx; 169: sp++; 170: } 171: symtabend = &symtab[cnt]; 172: 173: /* 174: * Now look for symbols with overlay numbers of 0 (root/base segment) and 175: * of type 'text'. For each symbol found check if there exists both a '~' 176: * and '_' prefixed form of the symbol. Preserve the '_' form and clear 177: * the '~' entry by zeroing the string address of the '~' symbol. 178: */ 179: for (sp = symtab; sp < symtabend; sp++) 180: { 181: if (sp->n_ovly) 182: continue; 183: if ((sp->n_type & N_TYPE) != N_TEXT) 184: continue; 185: if (sp->n_un.n_name[0] != '~') 186: continue; 187: /* 188: * At this point we have the '~' form of a non overlaid text symbol. Look 189: * thru the symbol table for the '_' form. All of 1) symbol type, 2) Symbol 190: * value and 3) symbol name (starting after the first character) must match. 191: */ 192: for (sp2 = symtab; sp2 < symtabend; sp2++) 193: { 194: if (sp2->n_ovly) 195: continue; 196: if ((sp2->n_type & N_TYPE) != N_TEXT) 197: continue; 198: if (sp2->n_un.n_name[0] != '_') 199: continue; 200: if (sp2->n_value != sp->n_value) 201: continue; 202: if (strcmp(sp->n_un.n_name+1, sp2->n_un.n_name+1)) 203: continue; 204: /* 205: * Found a match. Null out the '~' symbol's string address. 206: */ 207: symsremoved++; 208: sp->n_un.n_strx = NULL; 209: break; 210: } 211: } 212: /* 213: * Done with the nested scanning of the symbol table. Now create a new 214: * string table (from the remaining symbols) in a temporary file. 215: */ 216: strcpy(strtmp, "/tmp/strXXXXXX"); 217: mktemp(strtmp); 218: strfp = fopen(strtmp, "w+"); 219: if (!strfp) 220: { 221: fprintf(stderr, "%s: can't create '%s'\n", Pgm, strtmp); 222: exit(EX_CANTCREAT); 223: } 224: setbuf(strfp, fbuf2); 225: 226: /* 227: * As each symbol is written to the tmp file the symbol's string offset 228: * is updated with the new file string table offset. 229: */ 230: ltmp = sizeof (long); 231: for (sp = symtab; sp < symtabend; sp++) 232: { 233: if (!sp->n_un.n_name) 234: continue; 235: len = strlen(sp->n_un.n_name) + 1; 236: fwrite(sp->n_un.n_name, len, 1, strfp); 237: sp->n_un.n_strx = ltmp; 238: ltmp += len; 239: } 240: /* 241: * We're done with the memory string table - give it back. Then reposition 242: * the new string table file to the beginning. 243: */ 244: free(strtab); 245: rewind(strfp); 246: 247: /* 248: * Position the executable file to where the symbol table begins. Truncate 249: * the file. Write out the valid symbols, counting each one so that the 250: * a.out header can be updated when we're done. 251: */ 252: nsyms = 0; 253: fseek(fp, symoff, L_SET); 254: ftruncate(fileno(fp), ftell(fp)); 255: for (sp = symtab; sp < symtabend; sp++) 256: { 257: if (sp->n_un.n_strx == 0) 258: continue; 259: nsyms++; 260: fwrite(sp, sizeof (struct nlist), 1, fp); 261: } 262: /* 263: * Next write out the string table size longword. 264: */ 265: fwrite(<mp, sizeof (long), 1, fp); 266: /* 267: * We're done with the in memory symbol table, release it. Then append 268: * the string table to the executable file. 269: */ 270: free(symtab); 271: while ((c = getc(strfp)) != EOF) 272: putc(c, fp); 273: fclose(strfp); 274: rewind(fp); 275: xhdr.e.a_syms = nsyms * sizeof (struct nlist); 276: fwrite(&xhdr.e, sizeof (xhdr.e), 1, fp); 277: fclose(fp); 278: printf("%s: %d symbols removed\n", Pgm, symsremoved); 279: cleanup(); 280: } 281: 282: void 283: cleanup() 284: { 285: if (strtmp[0]) 286: unlink(strtmp); 287: exit(EX_OK); 288: } 289: 290: /* 291: * Place any symbol exclusion rules in this routine, return 1 if the 292: * symbol is to be excluded, 0 if the symbol is to be retained. 293: */ 294: 295: exclude(sp) 296: register struct nlist *sp; 297: { 298: 299: if (sp->n_type == N_REG) 300: return(1); 301: if (sp->n_un.n_strx == 0) 302: return(1); 303: return(0); 304: }