Back to home page

OSCL-LXR

 
 

    


0001 /* Generate assembler source containing symbol information
0002  *
0003  * Copyright 2002       by Kai Germaschewski
0004  *
0005  * This software may be used and distributed according to the terms
0006  * of the GNU General Public License, incorporated herein by reference.
0007  *
0008  * Usage: nm -n vmlinux | scripts/kallsyms [--all-symbols] > symbols.S
0009  *
0010  *      Table compression uses all the unused char codes on the symbols and
0011  *  maps these to the most used substrings (tokens). For instance, it might
0012  *  map char code 0xF7 to represent "write_" and then in every symbol where
0013  *  "write_" appears it can be replaced by 0xF7, saving 5 bytes.
0014  *      The used codes themselves are also placed in the table so that the
0015  *  decompresion can work without "special cases".
0016  *      Applied to kernel symbols, this usually produces a compression ratio
0017  *  of about 50%.
0018  *
0019  */
0020 
0021 #include <stdbool.h>
0022 #include <stdio.h>
0023 #include <stdlib.h>
0024 #include <string.h>
0025 #include <ctype.h>
0026 #include <limits.h>
0027 
0028 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
0029 
0030 #define KSYM_NAME_LEN       128
0031 
0032 struct sym_entry {
0033     unsigned long long addr;
0034     unsigned int len;
0035     unsigned int start_pos;
0036     unsigned int percpu_absolute;
0037     unsigned char sym[];
0038 };
0039 
0040 struct addr_range {
0041     const char *start_sym, *end_sym;
0042     unsigned long long start, end;
0043 };
0044 
0045 static unsigned long long _text;
0046 static unsigned long long relative_base;
0047 static struct addr_range text_ranges[] = {
0048     { "_stext",     "_etext"     },
0049     { "_sinittext", "_einittext" },
0050 };
0051 #define text_range_text     (&text_ranges[0])
0052 #define text_range_inittext (&text_ranges[1])
0053 
0054 static struct addr_range percpu_range = {
0055     "__per_cpu_start", "__per_cpu_end", -1ULL, 0
0056 };
0057 
0058 static struct sym_entry **table;
0059 static unsigned int table_size, table_cnt;
0060 static int all_symbols;
0061 static int absolute_percpu;
0062 static int base_relative;
0063 
0064 static int token_profit[0x10000];
0065 
0066 /* the table that holds the result of the compression */
0067 static unsigned char best_table[256][2];
0068 static unsigned char best_table_len[256];
0069 
0070 
0071 static void usage(void)
0072 {
0073     fprintf(stderr, "Usage: kallsyms [--all-symbols] [--absolute-percpu] "
0074             "[--base-relative] < in.map > out.S\n");
0075     exit(1);
0076 }
0077 
0078 static char *sym_name(const struct sym_entry *s)
0079 {
0080     return (char *)s->sym + 1;
0081 }
0082 
0083 static bool is_ignored_symbol(const char *name, char type)
0084 {
0085     /* Symbol names that exactly match to the following are ignored.*/
0086     static const char * const ignored_symbols[] = {
0087         /*
0088          * Symbols which vary between passes. Passes 1 and 2 must have
0089          * identical symbol lists. The kallsyms_* symbols below are
0090          * only added after pass 1, they would be included in pass 2
0091          * when --all-symbols is specified so exclude them to get a
0092          * stable symbol list.
0093          */
0094         "kallsyms_addresses",
0095         "kallsyms_offsets",
0096         "kallsyms_relative_base",
0097         "kallsyms_num_syms",
0098         "kallsyms_names",
0099         "kallsyms_markers",
0100         "kallsyms_token_table",
0101         "kallsyms_token_index",
0102         /* Exclude linker generated symbols which vary between passes */
0103         "_SDA_BASE_",       /* ppc */
0104         "_SDA2_BASE_",      /* ppc */
0105         NULL
0106     };
0107 
0108     /* Symbol names that begin with the following are ignored.*/
0109     static const char * const ignored_prefixes[] = {
0110         "$",            /* local symbols for ARM, MIPS, etc. */
0111         ".L",           /* local labels, .LBB,.Ltmpxxx,.L__unnamed_xx,.LASANPC, etc. */
0112         "__crc_",       /* modversions */
0113         "__efistub_",       /* arm64 EFI stub namespace */
0114         "__kvm_nvhe_$",     /* arm64 local symbols in non-VHE KVM namespace */
0115         "__kvm_nvhe_.L",    /* arm64 local symbols in non-VHE KVM namespace */
0116         "__AArch64ADRPThunk_",  /* arm64 lld */
0117         "__ARMV5PILongThunk_",  /* arm lld */
0118         "__ARMV7PILongThunk_",
0119         "__ThumbV7PILongThunk_",
0120         "__LA25Thunk_",     /* mips lld */
0121         "__microLA25Thunk_",
0122         NULL
0123     };
0124 
0125     /* Symbol names that end with the following are ignored.*/
0126     static const char * const ignored_suffixes[] = {
0127         "_from_arm",        /* arm */
0128         "_from_thumb",      /* arm */
0129         "_veneer",      /* arm */
0130         NULL
0131     };
0132 
0133     /* Symbol names that contain the following are ignored.*/
0134     static const char * const ignored_matches[] = {
0135         ".long_branch.",    /* ppc stub */
0136         ".plt_branch.",     /* ppc stub */
0137         NULL
0138     };
0139 
0140     const char * const *p;
0141 
0142     for (p = ignored_symbols; *p; p++)
0143         if (!strcmp(name, *p))
0144             return true;
0145 
0146     for (p = ignored_prefixes; *p; p++)
0147         if (!strncmp(name, *p, strlen(*p)))
0148             return true;
0149 
0150     for (p = ignored_suffixes; *p; p++) {
0151         int l = strlen(name) - strlen(*p);
0152 
0153         if (l >= 0 && !strcmp(name + l, *p))
0154             return true;
0155     }
0156 
0157     for (p = ignored_matches; *p; p++) {
0158         if (strstr(name, *p))
0159             return true;
0160     }
0161 
0162     if (type == 'U' || type == 'u')
0163         return true;
0164     /* exclude debugging symbols */
0165     if (type == 'N' || type == 'n')
0166         return true;
0167 
0168     if (toupper(type) == 'A') {
0169         /* Keep these useful absolute symbols */
0170         if (strcmp(name, "__kernel_syscall_via_break") &&
0171             strcmp(name, "__kernel_syscall_via_epc") &&
0172             strcmp(name, "__kernel_sigtramp") &&
0173             strcmp(name, "__gp"))
0174             return true;
0175     }
0176 
0177     return false;
0178 }
0179 
0180 static void check_symbol_range(const char *sym, unsigned long long addr,
0181                    struct addr_range *ranges, int entries)
0182 {
0183     size_t i;
0184     struct addr_range *ar;
0185 
0186     for (i = 0; i < entries; ++i) {
0187         ar = &ranges[i];
0188 
0189         if (strcmp(sym, ar->start_sym) == 0) {
0190             ar->start = addr;
0191             return;
0192         } else if (strcmp(sym, ar->end_sym) == 0) {
0193             ar->end = addr;
0194             return;
0195         }
0196     }
0197 }
0198 
0199 static struct sym_entry *read_symbol(FILE *in)
0200 {
0201     char name[500], type;
0202     unsigned long long addr;
0203     unsigned int len;
0204     struct sym_entry *sym;
0205     int rc;
0206 
0207     rc = fscanf(in, "%llx %c %499s\n", &addr, &type, name);
0208     if (rc != 3) {
0209         if (rc != EOF && fgets(name, 500, in) == NULL)
0210             fprintf(stderr, "Read error or end of file.\n");
0211         return NULL;
0212     }
0213     if (strlen(name) >= KSYM_NAME_LEN) {
0214         fprintf(stderr, "Symbol %s too long for kallsyms (%zu >= %d).\n"
0215                 "Please increase KSYM_NAME_LEN both in kernel and kallsyms.c\n",
0216             name, strlen(name), KSYM_NAME_LEN);
0217         return NULL;
0218     }
0219 
0220     if (strcmp(name, "_text") == 0)
0221         _text = addr;
0222 
0223     /* Ignore most absolute/undefined (?) symbols. */
0224     if (is_ignored_symbol(name, type))
0225         return NULL;
0226 
0227     check_symbol_range(name, addr, text_ranges, ARRAY_SIZE(text_ranges));
0228     check_symbol_range(name, addr, &percpu_range, 1);
0229 
0230     /* include the type field in the symbol name, so that it gets
0231      * compressed together */
0232 
0233     len = strlen(name) + 1;
0234 
0235     sym = malloc(sizeof(*sym) + len + 1);
0236     if (!sym) {
0237         fprintf(stderr, "kallsyms failure: "
0238             "unable to allocate required amount of memory\n");
0239         exit(EXIT_FAILURE);
0240     }
0241     sym->addr = addr;
0242     sym->len = len;
0243     sym->sym[0] = type;
0244     strcpy(sym_name(sym), name);
0245     sym->percpu_absolute = 0;
0246 
0247     return sym;
0248 }
0249 
0250 static int symbol_in_range(const struct sym_entry *s,
0251                const struct addr_range *ranges, int entries)
0252 {
0253     size_t i;
0254     const struct addr_range *ar;
0255 
0256     for (i = 0; i < entries; ++i) {
0257         ar = &ranges[i];
0258 
0259         if (s->addr >= ar->start && s->addr <= ar->end)
0260             return 1;
0261     }
0262 
0263     return 0;
0264 }
0265 
0266 static int symbol_valid(const struct sym_entry *s)
0267 {
0268     const char *name = sym_name(s);
0269 
0270     /* if --all-symbols is not specified, then symbols outside the text
0271      * and inittext sections are discarded */
0272     if (!all_symbols) {
0273         if (symbol_in_range(s, text_ranges,
0274                     ARRAY_SIZE(text_ranges)) == 0)
0275             return 0;
0276         /* Corner case.  Discard any symbols with the same value as
0277          * _etext _einittext; they can move between pass 1 and 2 when
0278          * the kallsyms data are added.  If these symbols move then
0279          * they may get dropped in pass 2, which breaks the kallsyms
0280          * rules.
0281          */
0282         if ((s->addr == text_range_text->end &&
0283              strcmp(name, text_range_text->end_sym)) ||
0284             (s->addr == text_range_inittext->end &&
0285              strcmp(name, text_range_inittext->end_sym)))
0286             return 0;
0287     }
0288 
0289     return 1;
0290 }
0291 
0292 /* remove all the invalid symbols from the table */
0293 static void shrink_table(void)
0294 {
0295     unsigned int i, pos;
0296 
0297     pos = 0;
0298     for (i = 0; i < table_cnt; i++) {
0299         if (symbol_valid(table[i])) {
0300             if (pos != i)
0301                 table[pos] = table[i];
0302             pos++;
0303         } else {
0304             free(table[i]);
0305         }
0306     }
0307     table_cnt = pos;
0308 
0309     /* When valid symbol is not registered, exit to error */
0310     if (!table_cnt) {
0311         fprintf(stderr, "No valid symbol.\n");
0312         exit(1);
0313     }
0314 }
0315 
0316 static void read_map(FILE *in)
0317 {
0318     struct sym_entry *sym;
0319 
0320     while (!feof(in)) {
0321         sym = read_symbol(in);
0322         if (!sym)
0323             continue;
0324 
0325         sym->start_pos = table_cnt;
0326 
0327         if (table_cnt >= table_size) {
0328             table_size += 10000;
0329             table = realloc(table, sizeof(*table) * table_size);
0330             if (!table) {
0331                 fprintf(stderr, "out of memory\n");
0332                 exit (1);
0333             }
0334         }
0335 
0336         table[table_cnt++] = sym;
0337     }
0338 }
0339 
0340 static void output_label(const char *label)
0341 {
0342     printf(".globl %s\n", label);
0343     printf("\tALGN\n");
0344     printf("%s:\n", label);
0345 }
0346 
0347 /* Provide proper symbols relocatability by their '_text' relativeness. */
0348 static void output_address(unsigned long long addr)
0349 {
0350     if (_text <= addr)
0351         printf("\tPTR\t_text + %#llx\n", addr - _text);
0352     else
0353         printf("\tPTR\t_text - %#llx\n", _text - addr);
0354 }
0355 
0356 /* uncompress a compressed symbol. When this function is called, the best table
0357  * might still be compressed itself, so the function needs to be recursive */
0358 static int expand_symbol(const unsigned char *data, int len, char *result)
0359 {
0360     int c, rlen, total=0;
0361 
0362     while (len) {
0363         c = *data;
0364         /* if the table holds a single char that is the same as the one
0365          * we are looking for, then end the search */
0366         if (best_table[c][0]==c && best_table_len[c]==1) {
0367             *result++ = c;
0368             total++;
0369         } else {
0370             /* if not, recurse and expand */
0371             rlen = expand_symbol(best_table[c], best_table_len[c], result);
0372             total += rlen;
0373             result += rlen;
0374         }
0375         data++;
0376         len--;
0377     }
0378     *result=0;
0379 
0380     return total;
0381 }
0382 
0383 static int symbol_absolute(const struct sym_entry *s)
0384 {
0385     return s->percpu_absolute;
0386 }
0387 
0388 static void write_src(void)
0389 {
0390     unsigned int i, k, off;
0391     unsigned int best_idx[256];
0392     unsigned int *markers;
0393     char buf[KSYM_NAME_LEN];
0394 
0395     printf("#include <asm/bitsperlong.h>\n");
0396     printf("#if BITS_PER_LONG == 64\n");
0397     printf("#define PTR .quad\n");
0398     printf("#define ALGN .balign 8\n");
0399     printf("#else\n");
0400     printf("#define PTR .long\n");
0401     printf("#define ALGN .balign 4\n");
0402     printf("#endif\n");
0403 
0404     printf("\t.section .rodata, \"a\"\n");
0405 
0406     if (!base_relative)
0407         output_label("kallsyms_addresses");
0408     else
0409         output_label("kallsyms_offsets");
0410 
0411     for (i = 0; i < table_cnt; i++) {
0412         if (base_relative) {
0413             /*
0414              * Use the offset relative to the lowest value
0415              * encountered of all relative symbols, and emit
0416              * non-relocatable fixed offsets that will be fixed
0417              * up at runtime.
0418              */
0419 
0420             long long offset;
0421             int overflow;
0422 
0423             if (!absolute_percpu) {
0424                 offset = table[i]->addr - relative_base;
0425                 overflow = (offset < 0 || offset > UINT_MAX);
0426             } else if (symbol_absolute(table[i])) {
0427                 offset = table[i]->addr;
0428                 overflow = (offset < 0 || offset > INT_MAX);
0429             } else {
0430                 offset = relative_base - table[i]->addr - 1;
0431                 overflow = (offset < INT_MIN || offset >= 0);
0432             }
0433             if (overflow) {
0434                 fprintf(stderr, "kallsyms failure: "
0435                     "%s symbol value %#llx out of range in relative mode\n",
0436                     symbol_absolute(table[i]) ? "absolute" : "relative",
0437                     table[i]->addr);
0438                 exit(EXIT_FAILURE);
0439             }
0440             printf("\t.long\t%#x\n", (int)offset);
0441         } else if (!symbol_absolute(table[i])) {
0442             output_address(table[i]->addr);
0443         } else {
0444             printf("\tPTR\t%#llx\n", table[i]->addr);
0445         }
0446     }
0447     printf("\n");
0448 
0449     if (base_relative) {
0450         output_label("kallsyms_relative_base");
0451         output_address(relative_base);
0452         printf("\n");
0453     }
0454 
0455     output_label("kallsyms_num_syms");
0456     printf("\t.long\t%u\n", table_cnt);
0457     printf("\n");
0458 
0459     /* table of offset markers, that give the offset in the compressed stream
0460      * every 256 symbols */
0461     markers = malloc(sizeof(unsigned int) * ((table_cnt + 255) / 256));
0462     if (!markers) {
0463         fprintf(stderr, "kallsyms failure: "
0464             "unable to allocate required memory\n");
0465         exit(EXIT_FAILURE);
0466     }
0467 
0468     output_label("kallsyms_names");
0469     off = 0;
0470     for (i = 0; i < table_cnt; i++) {
0471         if ((i & 0xFF) == 0)
0472             markers[i >> 8] = off;
0473 
0474         printf("\t.byte 0x%02x", table[i]->len);
0475         for (k = 0; k < table[i]->len; k++)
0476             printf(", 0x%02x", table[i]->sym[k]);
0477         printf("\n");
0478 
0479         off += table[i]->len + 1;
0480     }
0481     printf("\n");
0482 
0483     output_label("kallsyms_markers");
0484     for (i = 0; i < ((table_cnt + 255) >> 8); i++)
0485         printf("\t.long\t%u\n", markers[i]);
0486     printf("\n");
0487 
0488     free(markers);
0489 
0490     output_label("kallsyms_token_table");
0491     off = 0;
0492     for (i = 0; i < 256; i++) {
0493         best_idx[i] = off;
0494         expand_symbol(best_table[i], best_table_len[i], buf);
0495         printf("\t.asciz\t\"%s\"\n", buf);
0496         off += strlen(buf) + 1;
0497     }
0498     printf("\n");
0499 
0500     output_label("kallsyms_token_index");
0501     for (i = 0; i < 256; i++)
0502         printf("\t.short\t%d\n", best_idx[i]);
0503     printf("\n");
0504 }
0505 
0506 
0507 /* table lookup compression functions */
0508 
0509 /* count all the possible tokens in a symbol */
0510 static void learn_symbol(const unsigned char *symbol, int len)
0511 {
0512     int i;
0513 
0514     for (i = 0; i < len - 1; i++)
0515         token_profit[ symbol[i] + (symbol[i + 1] << 8) ]++;
0516 }
0517 
0518 /* decrease the count for all the possible tokens in a symbol */
0519 static void forget_symbol(const unsigned char *symbol, int len)
0520 {
0521     int i;
0522 
0523     for (i = 0; i < len - 1; i++)
0524         token_profit[ symbol[i] + (symbol[i + 1] << 8) ]--;
0525 }
0526 
0527 /* do the initial token count */
0528 static void build_initial_tok_table(void)
0529 {
0530     unsigned int i;
0531 
0532     for (i = 0; i < table_cnt; i++)
0533         learn_symbol(table[i]->sym, table[i]->len);
0534 }
0535 
0536 static unsigned char *find_token(unsigned char *str, int len,
0537                  const unsigned char *token)
0538 {
0539     int i;
0540 
0541     for (i = 0; i < len - 1; i++) {
0542         if (str[i] == token[0] && str[i+1] == token[1])
0543             return &str[i];
0544     }
0545     return NULL;
0546 }
0547 
0548 /* replace a given token in all the valid symbols. Use the sampled symbols
0549  * to update the counts */
0550 static void compress_symbols(const unsigned char *str, int idx)
0551 {
0552     unsigned int i, len, size;
0553     unsigned char *p1, *p2;
0554 
0555     for (i = 0; i < table_cnt; i++) {
0556 
0557         len = table[i]->len;
0558         p1 = table[i]->sym;
0559 
0560         /* find the token on the symbol */
0561         p2 = find_token(p1, len, str);
0562         if (!p2) continue;
0563 
0564         /* decrease the counts for this symbol's tokens */
0565         forget_symbol(table[i]->sym, len);
0566 
0567         size = len;
0568 
0569         do {
0570             *p2 = idx;
0571             p2++;
0572             size -= (p2 - p1);
0573             memmove(p2, p2 + 1, size);
0574             p1 = p2;
0575             len--;
0576 
0577             if (size < 2) break;
0578 
0579             /* find the token on the symbol */
0580             p2 = find_token(p1, size, str);
0581 
0582         } while (p2);
0583 
0584         table[i]->len = len;
0585 
0586         /* increase the counts for this symbol's new tokens */
0587         learn_symbol(table[i]->sym, len);
0588     }
0589 }
0590 
0591 /* search the token with the maximum profit */
0592 static int find_best_token(void)
0593 {
0594     int i, best, bestprofit;
0595 
0596     bestprofit=-10000;
0597     best = 0;
0598 
0599     for (i = 0; i < 0x10000; i++) {
0600         if (token_profit[i] > bestprofit) {
0601             best = i;
0602             bestprofit = token_profit[i];
0603         }
0604     }
0605     return best;
0606 }
0607 
0608 /* this is the core of the algorithm: calculate the "best" table */
0609 static void optimize_result(void)
0610 {
0611     int i, best;
0612 
0613     /* using the '\0' symbol last allows compress_symbols to use standard
0614      * fast string functions */
0615     for (i = 255; i >= 0; i--) {
0616 
0617         /* if this table slot is empty (it is not used by an actual
0618          * original char code */
0619         if (!best_table_len[i]) {
0620 
0621             /* find the token with the best profit value */
0622             best = find_best_token();
0623             if (token_profit[best] == 0)
0624                 break;
0625 
0626             /* place it in the "best" table */
0627             best_table_len[i] = 2;
0628             best_table[i][0] = best & 0xFF;
0629             best_table[i][1] = (best >> 8) & 0xFF;
0630 
0631             /* replace this token in all the valid symbols */
0632             compress_symbols(best_table[i], i);
0633         }
0634     }
0635 }
0636 
0637 /* start by placing the symbols that are actually used on the table */
0638 static void insert_real_symbols_in_table(void)
0639 {
0640     unsigned int i, j, c;
0641 
0642     for (i = 0; i < table_cnt; i++) {
0643         for (j = 0; j < table[i]->len; j++) {
0644             c = table[i]->sym[j];
0645             best_table[c][0]=c;
0646             best_table_len[c]=1;
0647         }
0648     }
0649 }
0650 
0651 static void optimize_token_table(void)
0652 {
0653     build_initial_tok_table();
0654 
0655     insert_real_symbols_in_table();
0656 
0657     optimize_result();
0658 }
0659 
0660 /* guess for "linker script provide" symbol */
0661 static int may_be_linker_script_provide_symbol(const struct sym_entry *se)
0662 {
0663     const char *symbol = sym_name(se);
0664     int len = se->len - 1;
0665 
0666     if (len < 8)
0667         return 0;
0668 
0669     if (symbol[0] != '_' || symbol[1] != '_')
0670         return 0;
0671 
0672     /* __start_XXXXX */
0673     if (!memcmp(symbol + 2, "start_", 6))
0674         return 1;
0675 
0676     /* __stop_XXXXX */
0677     if (!memcmp(symbol + 2, "stop_", 5))
0678         return 1;
0679 
0680     /* __end_XXXXX */
0681     if (!memcmp(symbol + 2, "end_", 4))
0682         return 1;
0683 
0684     /* __XXXXX_start */
0685     if (!memcmp(symbol + len - 6, "_start", 6))
0686         return 1;
0687 
0688     /* __XXXXX_end */
0689     if (!memcmp(symbol + len - 4, "_end", 4))
0690         return 1;
0691 
0692     return 0;
0693 }
0694 
0695 static int compare_symbols(const void *a, const void *b)
0696 {
0697     const struct sym_entry *sa = *(const struct sym_entry **)a;
0698     const struct sym_entry *sb = *(const struct sym_entry **)b;
0699     int wa, wb;
0700 
0701     /* sort by address first */
0702     if (sa->addr > sb->addr)
0703         return 1;
0704     if (sa->addr < sb->addr)
0705         return -1;
0706 
0707     /* sort by "weakness" type */
0708     wa = (sa->sym[0] == 'w') || (sa->sym[0] == 'W');
0709     wb = (sb->sym[0] == 'w') || (sb->sym[0] == 'W');
0710     if (wa != wb)
0711         return wa - wb;
0712 
0713     /* sort by "linker script provide" type */
0714     wa = may_be_linker_script_provide_symbol(sa);
0715     wb = may_be_linker_script_provide_symbol(sb);
0716     if (wa != wb)
0717         return wa - wb;
0718 
0719     /* sort by the number of prefix underscores */
0720     wa = strspn(sym_name(sa), "_");
0721     wb = strspn(sym_name(sb), "_");
0722     if (wa != wb)
0723         return wa - wb;
0724 
0725     /* sort by initial order, so that other symbols are left undisturbed */
0726     return sa->start_pos - sb->start_pos;
0727 }
0728 
0729 static void sort_symbols(void)
0730 {
0731     qsort(table, table_cnt, sizeof(table[0]), compare_symbols);
0732 }
0733 
0734 static void make_percpus_absolute(void)
0735 {
0736     unsigned int i;
0737 
0738     for (i = 0; i < table_cnt; i++)
0739         if (symbol_in_range(table[i], &percpu_range, 1)) {
0740             /*
0741              * Keep the 'A' override for percpu symbols to
0742              * ensure consistent behavior compared to older
0743              * versions of this tool.
0744              */
0745             table[i]->sym[0] = 'A';
0746             table[i]->percpu_absolute = 1;
0747         }
0748 }
0749 
0750 /* find the minimum non-absolute symbol address */
0751 static void record_relative_base(void)
0752 {
0753     unsigned int i;
0754 
0755     for (i = 0; i < table_cnt; i++)
0756         if (!symbol_absolute(table[i])) {
0757             /*
0758              * The table is sorted by address.
0759              * Take the first non-absolute symbol value.
0760              */
0761             relative_base = table[i]->addr;
0762             return;
0763         }
0764 }
0765 
0766 int main(int argc, char **argv)
0767 {
0768     if (argc >= 2) {
0769         int i;
0770         for (i = 1; i < argc; i++) {
0771             if(strcmp(argv[i], "--all-symbols") == 0)
0772                 all_symbols = 1;
0773             else if (strcmp(argv[i], "--absolute-percpu") == 0)
0774                 absolute_percpu = 1;
0775             else if (strcmp(argv[i], "--base-relative") == 0)
0776                 base_relative = 1;
0777             else
0778                 usage();
0779         }
0780     } else if (argc != 1)
0781         usage();
0782 
0783     read_map(stdin);
0784     shrink_table();
0785     if (absolute_percpu)
0786         make_percpus_absolute();
0787     sort_symbols();
0788     if (base_relative)
0789         record_relative_base();
0790     optimize_token_table();
0791     write_src();
0792 
0793     return 0;
0794 }