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