1 /* Generate assembler source containing symbol information 2 * 3 * Copyright 2002 by Kai Germaschewski 4 * 5 * This software may be used and distributed according to the terms 6 * of the GNU General Public License, incorporated herein by reference. 7 * 8 * Usage: kallsyms [--all-symbols] [--absolute-percpu] 9 * [--lto-clang] in.map > out.S 10 * 11 * Table compression uses all the unused char codes on the symbols and 12 * maps these to the most used substrings (tokens). For instance, it might 13 * map char code 0xF7 to represent "write_" and then in every symbol where 14 * "write_" appears it can be replaced by 0xF7, saving 5 bytes. 15 * The used codes themselves are also placed in the table so that the 16 * decompresion can work without "special cases". 17 * Applied to kernel symbols, this usually produces a compression ratio 18 * of about 50%. 19 * 20 */ 21 22 #include <errno.h> 23 #include <getopt.h> 24 #include <stdbool.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <ctype.h> 29 #include <limits.h> 30 31 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) 32 33 #define KSYM_NAME_LEN 512 34 35 struct sym_entry { 36 unsigned long long addr; 37 unsigned int len; 38 unsigned int seq; 39 bool percpu_absolute; 40 unsigned char sym[]; 41 }; 42 43 struct addr_range { 44 const char *start_sym, *end_sym; 45 unsigned long long start, end; 46 }; 47 48 static unsigned long long _text; 49 static unsigned long long relative_base; 50 static struct addr_range text_ranges[] = { 51 { "_stext", "_etext" }, 52 { "_sinittext", "_einittext" }, 53 }; 54 #define text_range_text (&text_ranges[0]) 55 #define text_range_inittext (&text_ranges[1]) 56 57 static struct addr_range percpu_range = { 58 "__per_cpu_start", "__per_cpu_end", -1ULL, 0 59 }; 60 61 static struct sym_entry **table; 62 static unsigned int table_size, table_cnt; 63 static int all_symbols; 64 static int absolute_percpu; 65 static int lto_clang; 66 67 static int token_profit[0x10000]; 68 69 /* the table that holds the result of the compression */ 70 static unsigned char best_table[256][2]; 71 static unsigned char best_table_len[256]; 72 73 74 static void usage(void) 75 { 76 fprintf(stderr, "Usage: kallsyms [--all-symbols] [--absolute-percpu] " 77 "[--lto-clang] in.map > out.S\n"); 78 exit(1); 79 } 80 81 static char *sym_name(const struct sym_entry *s) 82 { 83 return (char *)s->sym + 1; 84 } 85 86 static bool is_ignored_symbol(const char *name, char type) 87 { 88 if (type == 'u' || type == 'n') 89 return true; 90 91 if (toupper(type) == 'A') { 92 /* Keep these useful absolute symbols */ 93 if (strcmp(name, "__kernel_syscall_via_break") && 94 strcmp(name, "__kernel_syscall_via_epc") && 95 strcmp(name, "__kernel_sigtramp") && 96 strcmp(name, "__gp")) 97 return true; 98 } 99 100 return false; 101 } 102 103 static void check_symbol_range(const char *sym, unsigned long long addr, 104 struct addr_range *ranges, int entries) 105 { 106 size_t i; 107 struct addr_range *ar; 108 109 for (i = 0; i < entries; ++i) { 110 ar = &ranges[i]; 111 112 if (strcmp(sym, ar->start_sym) == 0) { 113 ar->start = addr; 114 return; 115 } else if (strcmp(sym, ar->end_sym) == 0) { 116 ar->end = addr; 117 return; 118 } 119 } 120 } 121 122 static struct sym_entry *read_symbol(FILE *in, char **buf, size_t *buf_len) 123 { 124 char *name, type, *p; 125 unsigned long long addr; 126 size_t len; 127 ssize_t readlen; 128 struct sym_entry *sym; 129 130 errno = 0; 131 readlen = getline(buf, buf_len, in); 132 if (readlen < 0) { 133 if (errno) { 134 perror("read_symbol"); 135 exit(EXIT_FAILURE); 136 } 137 return NULL; 138 } 139 140 if ((*buf)[readlen - 1] == '\n') 141 (*buf)[readlen - 1] = 0; 142 143 addr = strtoull(*buf, &p, 16); 144 145 if (*buf == p || *p++ != ' ' || !isascii((type = *p++)) || *p++ != ' ') { 146 fprintf(stderr, "line format error\n"); 147 exit(EXIT_FAILURE); 148 } 149 150 name = p; 151 len = strlen(name); 152 153 if (len >= KSYM_NAME_LEN) { 154 fprintf(stderr, "Symbol %s too long for kallsyms (%zu >= %d).\n" 155 "Please increase KSYM_NAME_LEN both in kernel and kallsyms.c\n", 156 name, len, KSYM_NAME_LEN); 157 return NULL; 158 } 159 160 if (strcmp(name, "_text") == 0) 161 _text = addr; 162 163 /* Ignore most absolute/undefined (?) symbols. */ 164 if (is_ignored_symbol(name, type)) 165 return NULL; 166 167 check_symbol_range(name, addr, text_ranges, ARRAY_SIZE(text_ranges)); 168 check_symbol_range(name, addr, &percpu_range, 1); 169 170 /* include the type field in the symbol name, so that it gets 171 * compressed together */ 172 len++; 173 174 sym = malloc(sizeof(*sym) + len + 1); 175 if (!sym) { 176 fprintf(stderr, "kallsyms failure: " 177 "unable to allocate required amount of memory\n"); 178 exit(EXIT_FAILURE); 179 } 180 sym->addr = addr; 181 sym->len = len; 182 sym->sym[0] = type; 183 strcpy(sym_name(sym), name); 184 sym->percpu_absolute = false; 185 186 return sym; 187 } 188 189 static int symbol_in_range(const struct sym_entry *s, 190 const struct addr_range *ranges, int entries) 191 { 192 size_t i; 193 const struct addr_range *ar; 194 195 for (i = 0; i < entries; ++i) { 196 ar = &ranges[i]; 197 198 if (s->addr >= ar->start && s->addr <= ar->end) 199 return 1; 200 } 201 202 return 0; 203 } 204 205 static bool string_starts_with(const char *s, const char *prefix) 206 { 207 return strncmp(s, prefix, strlen(prefix)) == 0; 208 } 209 210 static int symbol_valid(const struct sym_entry *s) 211 { 212 const char *name = sym_name(s); 213 214 /* if --all-symbols is not specified, then symbols outside the text 215 * and inittext sections are discarded */ 216 if (!all_symbols) { 217 /* 218 * Symbols starting with __start and __stop are used to denote 219 * section boundaries, and should always be included: 220 */ 221 if (string_starts_with(name, "__start_") || 222 string_starts_with(name, "__stop_")) 223 return 1; 224 225 if (symbol_in_range(s, text_ranges, 226 ARRAY_SIZE(text_ranges)) == 0) 227 return 0; 228 /* Corner case. Discard any symbols with the same value as 229 * _etext _einittext; they can move between pass 1 and 2 when 230 * the kallsyms data are added. If these symbols move then 231 * they may get dropped in pass 2, which breaks the kallsyms 232 * rules. 233 */ 234 if ((s->addr == text_range_text->end && 235 strcmp(name, text_range_text->end_sym)) || 236 (s->addr == text_range_inittext->end && 237 strcmp(name, text_range_inittext->end_sym))) 238 return 0; 239 } 240 241 return 1; 242 } 243 244 /* remove all the invalid symbols from the table */ 245 static void shrink_table(void) 246 { 247 unsigned int i, pos; 248 249 pos = 0; 250 for (i = 0; i < table_cnt; i++) { 251 if (symbol_valid(table[i])) { 252 if (pos != i) 253 table[pos] = table[i]; 254 pos++; 255 } else { 256 free(table[i]); 257 } 258 } 259 table_cnt = pos; 260 } 261 262 static void read_map(const char *in) 263 { 264 FILE *fp; 265 struct sym_entry *sym; 266 char *buf = NULL; 267 size_t buflen = 0; 268 269 fp = fopen(in, "r"); 270 if (!fp) { 271 perror(in); 272 exit(1); 273 } 274 275 while (!feof(fp)) { 276 sym = read_symbol(fp, &buf, &buflen); 277 if (!sym) 278 continue; 279 280 sym->seq = table_cnt; 281 282 if (table_cnt >= table_size) { 283 table_size += 10000; 284 table = realloc(table, sizeof(*table) * table_size); 285 if (!table) { 286 fprintf(stderr, "out of memory\n"); 287 fclose(fp); 288 exit (1); 289 } 290 } 291 292 table[table_cnt++] = sym; 293 } 294 295 free(buf); 296 fclose(fp); 297 } 298 299 static void output_label(const char *label) 300 { 301 printf(".globl %s\n", label); 302 printf("\tALGN\n"); 303 printf("%s:\n", label); 304 } 305 306 /* Provide proper symbols relocatability by their '_text' relativeness. */ 307 static void output_address(unsigned long long addr) 308 { 309 if (_text <= addr) 310 printf("\tPTR\t_text + %#llx\n", addr - _text); 311 else 312 printf("\tPTR\t_text - %#llx\n", _text - addr); 313 } 314 315 /* uncompress a compressed symbol. When this function is called, the best table 316 * might still be compressed itself, so the function needs to be recursive */ 317 static int expand_symbol(const unsigned char *data, int len, char *result) 318 { 319 int c, rlen, total=0; 320 321 while (len) { 322 c = *data; 323 /* if the table holds a single char that is the same as the one 324 * we are looking for, then end the search */ 325 if (best_table[c][0]==c && best_table_len[c]==1) { 326 *result++ = c; 327 total++; 328 } else { 329 /* if not, recurse and expand */ 330 rlen = expand_symbol(best_table[c], best_table_len[c], result); 331 total += rlen; 332 result += rlen; 333 } 334 data++; 335 len--; 336 } 337 *result=0; 338 339 return total; 340 } 341 342 static bool symbol_absolute(const struct sym_entry *s) 343 { 344 return s->percpu_absolute; 345 } 346 347 static void cleanup_symbol_name(char *s) 348 { 349 char *p; 350 351 /* 352 * ASCII[.] = 2e 353 * ASCII[0-9] = 30,39 354 * ASCII[A-Z] = 41,5a 355 * ASCII[_] = 5f 356 * ASCII[a-z] = 61,7a 357 * 358 * As above, replacing the first '.' in ".llvm." with '\0' does not 359 * affect the main sorting, but it helps us with subsorting. 360 */ 361 p = strstr(s, ".llvm."); 362 if (p) 363 *p = '\0'; 364 } 365 366 static int compare_names(const void *a, const void *b) 367 { 368 int ret; 369 const struct sym_entry *sa = *(const struct sym_entry **)a; 370 const struct sym_entry *sb = *(const struct sym_entry **)b; 371 372 ret = strcmp(sym_name(sa), sym_name(sb)); 373 if (!ret) { 374 if (sa->addr > sb->addr) 375 return 1; 376 else if (sa->addr < sb->addr) 377 return -1; 378 379 /* keep old order */ 380 return (int)(sa->seq - sb->seq); 381 } 382 383 return ret; 384 } 385 386 static void sort_symbols_by_name(void) 387 { 388 qsort(table, table_cnt, sizeof(table[0]), compare_names); 389 } 390 391 static void write_src(void) 392 { 393 unsigned int i, k, off; 394 unsigned int best_idx[256]; 395 unsigned int *markers, markers_cnt; 396 char buf[KSYM_NAME_LEN]; 397 398 printf("#include <asm/bitsperlong.h>\n"); 399 printf("#if BITS_PER_LONG == 64\n"); 400 printf("#define PTR .quad\n"); 401 printf("#define ALGN .balign 8\n"); 402 printf("#else\n"); 403 printf("#define PTR .long\n"); 404 printf("#define ALGN .balign 4\n"); 405 printf("#endif\n"); 406 407 printf("\t.section .rodata, \"a\"\n"); 408 409 output_label("kallsyms_num_syms"); 410 printf("\t.long\t%u\n", table_cnt); 411 printf("\n"); 412 413 /* table of offset markers, that give the offset in the compressed stream 414 * every 256 symbols */ 415 markers_cnt = (table_cnt + 255) / 256; 416 markers = malloc(sizeof(*markers) * markers_cnt); 417 if (!markers) { 418 fprintf(stderr, "kallsyms failure: " 419 "unable to allocate required memory\n"); 420 exit(EXIT_FAILURE); 421 } 422 423 output_label("kallsyms_names"); 424 off = 0; 425 for (i = 0; i < table_cnt; i++) { 426 if ((i & 0xFF) == 0) 427 markers[i >> 8] = off; 428 table[i]->seq = i; 429 430 /* There cannot be any symbol of length zero. */ 431 if (table[i]->len == 0) { 432 fprintf(stderr, "kallsyms failure: " 433 "unexpected zero symbol length\n"); 434 exit(EXIT_FAILURE); 435 } 436 437 /* Only lengths that fit in up-to-two-byte ULEB128 are supported. */ 438 if (table[i]->len > 0x3FFF) { 439 fprintf(stderr, "kallsyms failure: " 440 "unexpected huge symbol length\n"); 441 exit(EXIT_FAILURE); 442 } 443 444 /* Encode length with ULEB128. */ 445 if (table[i]->len <= 0x7F) { 446 /* Most symbols use a single byte for the length. */ 447 printf("\t.byte 0x%02x", table[i]->len); 448 off += table[i]->len + 1; 449 } else { 450 /* "Big" symbols use two bytes. */ 451 printf("\t.byte 0x%02x, 0x%02x", 452 (table[i]->len & 0x7F) | 0x80, 453 (table[i]->len >> 7) & 0x7F); 454 off += table[i]->len + 2; 455 } 456 for (k = 0; k < table[i]->len; k++) 457 printf(", 0x%02x", table[i]->sym[k]); 458 459 /* 460 * Now that we wrote out the compressed symbol name, restore the 461 * original name and print it in the comment. 462 */ 463 expand_symbol(table[i]->sym, table[i]->len, buf); 464 strcpy((char *)table[i]->sym, buf); 465 printf("\t/* %s */\n", table[i]->sym); 466 } 467 printf("\n"); 468 469 output_label("kallsyms_markers"); 470 for (i = 0; i < markers_cnt; i++) 471 printf("\t.long\t%u\n", markers[i]); 472 printf("\n"); 473 474 free(markers); 475 476 output_label("kallsyms_token_table"); 477 off = 0; 478 for (i = 0; i < 256; i++) { 479 best_idx[i] = off; 480 expand_symbol(best_table[i], best_table_len[i], buf); 481 printf("\t.asciz\t\"%s\"\n", buf); 482 off += strlen(buf) + 1; 483 } 484 printf("\n"); 485 486 output_label("kallsyms_token_index"); 487 for (i = 0; i < 256; i++) 488 printf("\t.short\t%d\n", best_idx[i]); 489 printf("\n"); 490 491 output_label("kallsyms_offsets"); 492 493 for (i = 0; i < table_cnt; i++) { 494 /* 495 * Use the offset relative to the lowest value 496 * encountered of all relative symbols, and emit 497 * non-relocatable fixed offsets that will be fixed 498 * up at runtime. 499 */ 500 501 long long offset; 502 int overflow; 503 504 if (!absolute_percpu) { 505 offset = table[i]->addr - relative_base; 506 overflow = (offset < 0 || offset > UINT_MAX); 507 } else if (symbol_absolute(table[i])) { 508 offset = table[i]->addr; 509 overflow = (offset < 0 || offset > INT_MAX); 510 } else { 511 offset = relative_base - table[i]->addr - 1; 512 overflow = (offset < INT_MIN || offset >= 0); 513 } 514 if (overflow) { 515 fprintf(stderr, "kallsyms failure: " 516 "%s symbol value %#llx out of range in relative mode\n", 517 symbol_absolute(table[i]) ? "absolute" : "relative", 518 table[i]->addr); 519 exit(EXIT_FAILURE); 520 } 521 printf("\t.long\t%#x\t/* %s */\n", (int)offset, table[i]->sym); 522 } 523 printf("\n"); 524 525 output_label("kallsyms_relative_base"); 526 output_address(relative_base); 527 printf("\n"); 528 529 if (lto_clang) 530 for (i = 0; i < table_cnt; i++) 531 cleanup_symbol_name((char *)table[i]->sym); 532 533 sort_symbols_by_name(); 534 output_label("kallsyms_seqs_of_names"); 535 for (i = 0; i < table_cnt; i++) 536 printf("\t.byte 0x%02x, 0x%02x, 0x%02x\t/* %s */\n", 537 (unsigned char)(table[i]->seq >> 16), 538 (unsigned char)(table[i]->seq >> 8), 539 (unsigned char)(table[i]->seq >> 0), 540 table[i]->sym); 541 printf("\n"); 542 } 543 544 545 /* table lookup compression functions */ 546 547 /* count all the possible tokens in a symbol */ 548 static void learn_symbol(const unsigned char *symbol, int len) 549 { 550 int i; 551 552 for (i = 0; i < len - 1; i++) 553 token_profit[ symbol[i] + (symbol[i + 1] << 8) ]++; 554 } 555 556 /* decrease the count for all the possible tokens in a symbol */ 557 static void forget_symbol(const unsigned char *symbol, int len) 558 { 559 int i; 560 561 for (i = 0; i < len - 1; i++) 562 token_profit[ symbol[i] + (symbol[i + 1] << 8) ]--; 563 } 564 565 /* do the initial token count */ 566 static void build_initial_token_table(void) 567 { 568 unsigned int i; 569 570 for (i = 0; i < table_cnt; i++) 571 learn_symbol(table[i]->sym, table[i]->len); 572 } 573 574 static unsigned char *find_token(unsigned char *str, int len, 575 const unsigned char *token) 576 { 577 int i; 578 579 for (i = 0; i < len - 1; i++) { 580 if (str[i] == token[0] && str[i+1] == token[1]) 581 return &str[i]; 582 } 583 return NULL; 584 } 585 586 /* replace a given token in all the valid symbols. Use the sampled symbols 587 * to update the counts */ 588 static void compress_symbols(const unsigned char *str, int idx) 589 { 590 unsigned int i, len, size; 591 unsigned char *p1, *p2; 592 593 for (i = 0; i < table_cnt; i++) { 594 595 len = table[i]->len; 596 p1 = table[i]->sym; 597 598 /* find the token on the symbol */ 599 p2 = find_token(p1, len, str); 600 if (!p2) continue; 601 602 /* decrease the counts for this symbol's tokens */ 603 forget_symbol(table[i]->sym, len); 604 605 size = len; 606 607 do { 608 *p2 = idx; 609 p2++; 610 size -= (p2 - p1); 611 memmove(p2, p2 + 1, size); 612 p1 = p2; 613 len--; 614 615 if (size < 2) break; 616 617 /* find the token on the symbol */ 618 p2 = find_token(p1, size, str); 619 620 } while (p2); 621 622 table[i]->len = len; 623 624 /* increase the counts for this symbol's new tokens */ 625 learn_symbol(table[i]->sym, len); 626 } 627 } 628 629 /* search the token with the maximum profit */ 630 static int find_best_token(void) 631 { 632 int i, best, bestprofit; 633 634 bestprofit=-10000; 635 best = 0; 636 637 for (i = 0; i < 0x10000; i++) { 638 if (token_profit[i] > bestprofit) { 639 best = i; 640 bestprofit = token_profit[i]; 641 } 642 } 643 return best; 644 } 645 646 /* this is the core of the algorithm: calculate the "best" table */ 647 static void optimize_result(void) 648 { 649 int i, best; 650 651 /* using the '\0' symbol last allows compress_symbols to use standard 652 * fast string functions */ 653 for (i = 255; i >= 0; i--) { 654 655 /* if this table slot is empty (it is not used by an actual 656 * original char code */ 657 if (!best_table_len[i]) { 658 659 /* find the token with the best profit value */ 660 best = find_best_token(); 661 if (token_profit[best] == 0) 662 break; 663 664 /* place it in the "best" table */ 665 best_table_len[i] = 2; 666 best_table[i][0] = best & 0xFF; 667 best_table[i][1] = (best >> 8) & 0xFF; 668 669 /* replace this token in all the valid symbols */ 670 compress_symbols(best_table[i], i); 671 } 672 } 673 } 674 675 /* start by placing the symbols that are actually used on the table */ 676 static void insert_real_symbols_in_table(void) 677 { 678 unsigned int i, j, c; 679 680 for (i = 0; i < table_cnt; i++) { 681 for (j = 0; j < table[i]->len; j++) { 682 c = table[i]->sym[j]; 683 best_table[c][0]=c; 684 best_table_len[c]=1; 685 } 686 } 687 } 688 689 static void optimize_token_table(void) 690 { 691 build_initial_token_table(); 692 693 insert_real_symbols_in_table(); 694 695 optimize_result(); 696 } 697 698 /* guess for "linker script provide" symbol */ 699 static int may_be_linker_script_provide_symbol(const struct sym_entry *se) 700 { 701 const char *symbol = sym_name(se); 702 int len = se->len - 1; 703 704 if (len < 8) 705 return 0; 706 707 if (symbol[0] != '_' || symbol[1] != '_') 708 return 0; 709 710 /* __start_XXXXX */ 711 if (!memcmp(symbol + 2, "start_", 6)) 712 return 1; 713 714 /* __stop_XXXXX */ 715 if (!memcmp(symbol + 2, "stop_", 5)) 716 return 1; 717 718 /* __end_XXXXX */ 719 if (!memcmp(symbol + 2, "end_", 4)) 720 return 1; 721 722 /* __XXXXX_start */ 723 if (!memcmp(symbol + len - 6, "_start", 6)) 724 return 1; 725 726 /* __XXXXX_end */ 727 if (!memcmp(symbol + len - 4, "_end", 4)) 728 return 1; 729 730 return 0; 731 } 732 733 static int compare_symbols(const void *a, const void *b) 734 { 735 const struct sym_entry *sa = *(const struct sym_entry **)a; 736 const struct sym_entry *sb = *(const struct sym_entry **)b; 737 int wa, wb; 738 739 /* sort by address first */ 740 if (sa->addr > sb->addr) 741 return 1; 742 if (sa->addr < sb->addr) 743 return -1; 744 745 /* sort by "weakness" type */ 746 wa = (sa->sym[0] == 'w') || (sa->sym[0] == 'W'); 747 wb = (sb->sym[0] == 'w') || (sb->sym[0] == 'W'); 748 if (wa != wb) 749 return wa - wb; 750 751 /* sort by "linker script provide" type */ 752 wa = may_be_linker_script_provide_symbol(sa); 753 wb = may_be_linker_script_provide_symbol(sb); 754 if (wa != wb) 755 return wa - wb; 756 757 /* sort by the number of prefix underscores */ 758 wa = strspn(sym_name(sa), "_"); 759 wb = strspn(sym_name(sb), "_"); 760 if (wa != wb) 761 return wa - wb; 762 763 /* sort by initial order, so that other symbols are left undisturbed */ 764 return sa->seq - sb->seq; 765 } 766 767 static void sort_symbols(void) 768 { 769 qsort(table, table_cnt, sizeof(table[0]), compare_symbols); 770 } 771 772 static void make_percpus_absolute(void) 773 { 774 unsigned int i; 775 776 for (i = 0; i < table_cnt; i++) 777 if (symbol_in_range(table[i], &percpu_range, 1)) { 778 /* 779 * Keep the 'A' override for percpu symbols to 780 * ensure consistent behavior compared to older 781 * versions of this tool. 782 */ 783 table[i]->sym[0] = 'A'; 784 table[i]->percpu_absolute = true; 785 } 786 } 787 788 /* find the minimum non-absolute symbol address */ 789 static void record_relative_base(void) 790 { 791 unsigned int i; 792 793 for (i = 0; i < table_cnt; i++) 794 if (!symbol_absolute(table[i])) { 795 /* 796 * The table is sorted by address. 797 * Take the first non-absolute symbol value. 798 */ 799 relative_base = table[i]->addr; 800 return; 801 } 802 } 803 804 int main(int argc, char **argv) 805 { 806 while (1) { 807 static const struct option long_options[] = { 808 {"all-symbols", no_argument, &all_symbols, 1}, 809 {"absolute-percpu", no_argument, &absolute_percpu, 1}, 810 {"lto-clang", no_argument, <o_clang, 1}, 811 {}, 812 }; 813 814 int c = getopt_long(argc, argv, "", long_options, NULL); 815 816 if (c == -1) 817 break; 818 if (c != 0) 819 usage(); 820 } 821 822 if (optind >= argc) 823 usage(); 824 825 read_map(argv[optind]); 826 shrink_table(); 827 if (absolute_percpu) 828 make_percpus_absolute(); 829 sort_symbols(); 830 record_relative_base(); 831 optimize_token_table(); 832 write_src(); 833 834 return 0; 835 } 836