mirror of
https://github.com/hardkernel/linux.git
synced 2026-04-01 18:53:02 +09:00
PD#SWPL-31258 Problem: Kernel RO data is too large, about 4.5mb on 32bit and 5.9mb on 64bit kernel Solution: 1, optimize kallsyms compress code. This can help to increase about 18% of compress ratio and save about 200 ~ 500KB under different config. Signed-off-by: Tao Zeng <tao.zeng@amlogic.com> Change-Id: I4c058fbb22d89bc50c81fa3266ee0f7613f076f2 Signed-off-by: Tao Zeng <tao.zeng@amlogic.com>
2158 lines
49 KiB
C
2158 lines
49 KiB
C
/* Generate assembler source containing symbol information
|
|
*
|
|
* Copyright 2002 by Kai Germaschewski
|
|
*
|
|
* This software may be used and distributed according to the terms
|
|
* of the GNU General Public License, incorporated herein by reference.
|
|
*
|
|
* Usage: nm -n vmlinux | scripts/kallsyms [--all-symbols] > symbols.S
|
|
*
|
|
* Table compression uses all the unused char codes on the symbols and
|
|
* maps these to the most used substrings (tokens). For instance, it might
|
|
* map char code 0xF7 to represent "write_" and then in every symbol where
|
|
* "write_" appears it can be replaced by 0xF7, saving 5 bytes.
|
|
* The used codes themselves are also placed in the table so that the
|
|
* decompresion can work without "special cases".
|
|
* Applied to kernel symbols, this usually produces a compression ratio
|
|
* of about 50%.
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <limits.h>
|
|
#include <time.h>
|
|
|
|
/*----------------- import code for rb_tree ------------------*/
|
|
#define RB_RED 0
|
|
#define RB_BLACK 1
|
|
|
|
struct rb_node {
|
|
unsigned long __rb_parent_color;
|
|
struct rb_node *rb_right;
|
|
struct rb_node *rb_left;
|
|
};
|
|
/* The alignment might seem pointless, but allegedly CRIS needs it */
|
|
|
|
struct rb_root {
|
|
struct rb_node *rb_node;
|
|
};
|
|
|
|
#undef offsetof
|
|
#ifdef __compiler_offsetof
|
|
#define offsetof(TYPE, MEMBER) __compiler_offsetof(TYPE, MEMBER)
|
|
#else
|
|
#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
|
|
#endif
|
|
|
|
#define container_of(ptr, type, member) ({ \
|
|
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
|
|
(type *)( (char *)__mptr - offsetof(type,member) );})
|
|
|
|
#define __rb_color(pc) ((pc) & 1)
|
|
#define __rb_is_black(pc) __rb_color(pc)
|
|
#define __rb_is_red(pc) (!__rb_color(pc))
|
|
#define rb_color(rb) __rb_color((rb)->__rb_parent_color)
|
|
#define rb_is_red(rb) __rb_is_red((rb)->__rb_parent_color)
|
|
#define rb_is_black(rb) __rb_is_black((rb)->__rb_parent_color)
|
|
|
|
#define rb_parent(r) ((struct rb_node *)((r)->__rb_parent_color & ~3))
|
|
|
|
#define rb_entry(ptr, type, member) container_of(ptr, type, member)
|
|
|
|
/* 'empty' nodes are nodes that are known not to be inserted in an rbtree */
|
|
#define RB_EMPTY_NODE(node) \
|
|
((node)->__rb_parent_color == (unsigned long)(node))
|
|
static void dummy_rotate(struct rb_node *old, struct rb_node *new)
|
|
{
|
|
|
|
}
|
|
|
|
static inline void rb_set_parent_color(struct rb_node *rb,
|
|
struct rb_node *p, int color)
|
|
{
|
|
rb->__rb_parent_color = (unsigned long)p | color;
|
|
}
|
|
|
|
static inline void
|
|
__rb_change_child(struct rb_node *old, struct rb_node *new,
|
|
struct rb_node *parent, struct rb_root *root)
|
|
{
|
|
if (parent) {
|
|
if (parent->rb_left == old)
|
|
parent->rb_left = new;
|
|
else
|
|
parent->rb_right = new;
|
|
} else
|
|
root->rb_node = new;
|
|
}
|
|
|
|
static inline void
|
|
__rb_rotate_set_parents(struct rb_node *old, struct rb_node *new,
|
|
struct rb_root *root, int color)
|
|
{
|
|
struct rb_node *parent = rb_parent(old);
|
|
|
|
new->__rb_parent_color = old->__rb_parent_color;
|
|
rb_set_parent_color(old, new, color);
|
|
__rb_change_child(old, new, parent, root);
|
|
}
|
|
|
|
|
|
static inline void rb_link_node(struct rb_node *node, struct rb_node *parent,
|
|
struct rb_node **rb_link)
|
|
{
|
|
node->__rb_parent_color = (unsigned long)parent;
|
|
node->rb_left = node->rb_right = NULL;
|
|
|
|
*rb_link = node;
|
|
}
|
|
|
|
static inline struct rb_node *rb_red_parent(struct rb_node *red)
|
|
{
|
|
return (struct rb_node *)red->__rb_parent_color;
|
|
}
|
|
|
|
static void __rb_insert(struct rb_node *node, struct rb_root *root,
|
|
void (*augment_rotate)(struct rb_node *old,
|
|
struct rb_node *new))
|
|
{
|
|
struct rb_node *parent = rb_red_parent(node), *gparent, *tmp;
|
|
|
|
while (1) {
|
|
if (!parent) {
|
|
rb_set_parent_color(node, NULL, RB_BLACK);
|
|
break;
|
|
} else if (rb_is_black(parent))
|
|
break;
|
|
|
|
gparent = rb_red_parent(parent);
|
|
|
|
tmp = gparent->rb_right;
|
|
if (parent != tmp) { /* parent == gparent->rb_left */
|
|
if (tmp && rb_is_red(tmp)) {
|
|
rb_set_parent_color(tmp, gparent, RB_BLACK);
|
|
rb_set_parent_color(parent, gparent, RB_BLACK);
|
|
node = gparent;
|
|
parent = rb_parent(node);
|
|
rb_set_parent_color(node, parent, RB_RED);
|
|
continue;
|
|
}
|
|
|
|
tmp = parent->rb_right;
|
|
if (node == tmp) {
|
|
tmp = node->rb_left;
|
|
parent->rb_right = tmp;
|
|
node->rb_left = parent;
|
|
if (tmp)
|
|
rb_set_parent_color(tmp, parent,
|
|
RB_BLACK);
|
|
rb_set_parent_color(parent, node, RB_RED);
|
|
augment_rotate(parent, node);
|
|
parent = node;
|
|
tmp = node->rb_right;
|
|
}
|
|
|
|
gparent->rb_left = tmp; /* == parent->rb_right */
|
|
parent->rb_right = gparent;
|
|
if (tmp)
|
|
rb_set_parent_color(tmp, gparent, RB_BLACK);
|
|
__rb_rotate_set_parents(gparent, parent, root, RB_RED);
|
|
augment_rotate(gparent, parent);
|
|
break;
|
|
} else {
|
|
tmp = gparent->rb_left;
|
|
if (tmp && rb_is_red(tmp)) {
|
|
rb_set_parent_color(tmp, gparent, RB_BLACK);
|
|
rb_set_parent_color(parent, gparent, RB_BLACK);
|
|
node = gparent;
|
|
parent = rb_parent(node);
|
|
rb_set_parent_color(node, parent, RB_RED);
|
|
continue;
|
|
}
|
|
|
|
tmp = parent->rb_left;
|
|
if (node == tmp) {
|
|
tmp = node->rb_right;
|
|
parent->rb_left = tmp;
|
|
node->rb_right = parent;
|
|
if (tmp)
|
|
rb_set_parent_color(tmp, parent,
|
|
RB_BLACK);
|
|
rb_set_parent_color(parent, node, RB_RED);
|
|
augment_rotate(parent, node);
|
|
parent = node;
|
|
tmp = node->rb_left;
|
|
}
|
|
|
|
/* Case 3 - left rotate at gparent */
|
|
gparent->rb_right = tmp; /* == parent->rb_left */
|
|
parent->rb_left = gparent;
|
|
if (tmp)
|
|
rb_set_parent_color(tmp, gparent, RB_BLACK);
|
|
__rb_rotate_set_parents(gparent, parent, root, RB_RED);
|
|
augment_rotate(gparent, parent);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void rb_insert_color(struct rb_node *node, struct rb_root *root)
|
|
{
|
|
__rb_insert(node, root, dummy_rotate);
|
|
}
|
|
|
|
/*
|
|
* This function returns the first node (in sort order) of the tree.
|
|
*/
|
|
static struct rb_node *rb_first(const struct rb_root *root)
|
|
{
|
|
struct rb_node *n;
|
|
|
|
n = root->rb_node;
|
|
if (!n)
|
|
return NULL;
|
|
while (n->rb_left)
|
|
n = n->rb_left;
|
|
return n;
|
|
}
|
|
|
|
static struct rb_node *rb_next(const struct rb_node *node)
|
|
{
|
|
struct rb_node *parent;
|
|
|
|
if (RB_EMPTY_NODE(node))
|
|
return NULL;
|
|
|
|
if (node->rb_right) {
|
|
node = node->rb_right;
|
|
while (node->rb_left)
|
|
node = node->rb_left;
|
|
return (struct rb_node *)node;
|
|
}
|
|
|
|
while ((parent = rb_parent(node)) && node == parent->rb_right)
|
|
node = parent;
|
|
|
|
return parent;
|
|
}
|
|
|
|
/*--------------------- end rb_tree porting ---------------------*/
|
|
|
|
#ifndef ARRAY_SIZE
|
|
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
|
|
#endif
|
|
|
|
#define KSYM_NAME_LEN 128
|
|
|
|
struct sym_entry {
|
|
unsigned long long addr;
|
|
unsigned int len;
|
|
unsigned int start_pos;
|
|
unsigned char *sym;
|
|
unsigned int percpu_absolute;
|
|
};
|
|
|
|
struct addr_range {
|
|
const char *start_sym, *end_sym;
|
|
unsigned long long start, end;
|
|
};
|
|
|
|
#define SPLIT_CHARS 2
|
|
#define IDX_TYPE (SPLIT_CHARS * 4)
|
|
struct key_word {
|
|
struct key_word *next;
|
|
char word[KSYM_NAME_LEN];
|
|
unsigned int total_cnt;
|
|
unsigned short word_len;
|
|
unsigned short idx_type[IDX_TYPE];
|
|
};
|
|
|
|
#define KEY_WORD_BASE 0x8000
|
|
#define KEY_WORD_HASH_SIZE 4096
|
|
|
|
struct final_word {
|
|
unsigned char *str;
|
|
int len;
|
|
int cnt;
|
|
int node_cnt;
|
|
struct rb_root tree;
|
|
};
|
|
|
|
struct merge_word {
|
|
struct rb_node entry;
|
|
unsigned int code;
|
|
unsigned int count;
|
|
};
|
|
|
|
struct merge_tree {
|
|
struct rb_node entry;
|
|
unsigned int key;
|
|
unsigned int new;
|
|
};
|
|
|
|
struct short_base {
|
|
unsigned long base;
|
|
unsigned int start_idx;
|
|
unsigned int syms;
|
|
unsigned short *table;
|
|
};
|
|
static int short_base_cnt;
|
|
|
|
struct rb_root merge_trees;
|
|
|
|
static struct final_word final_words[KEY_WORD_BASE] = {};
|
|
static int finnal_sym_len;
|
|
static int final_words_cnt = 8;
|
|
static int max_sym_words;
|
|
|
|
static unsigned long long _text;
|
|
static struct addr_range text_ranges[] = {
|
|
{ "_stext", "_etext" },
|
|
{ "_sinittext", "_einittext" },
|
|
{ "_stext_l1", "_etext_l1" }, /* Blackfin on-chip L1 inst SRAM */
|
|
{ "_stext_l2", "_etext_l2" }, /* Blackfin on-chip L2 SRAM */
|
|
};
|
|
#define text_range_text (&text_ranges[0])
|
|
#define text_range_inittext (&text_ranges[1])
|
|
|
|
static struct addr_range percpu_range = {
|
|
"__per_cpu_start", "__per_cpu_end", -1ULL, 0
|
|
};
|
|
|
|
static struct sym_entry *table;
|
|
static unsigned int table_size, table_cnt;
|
|
static int all_symbols = 0;
|
|
static char symbol_prefix_char = '\0';
|
|
|
|
static unsigned long input_len;
|
|
|
|
int token_profit[128 * 128];
|
|
|
|
#define DEBUG_COMPRESS 0
|
|
#if DEBUG_COMPRESS
|
|
#define tprintf(format, args...) printf(format, ##args)
|
|
#else
|
|
#define tprintf(format, args...)
|
|
#endif
|
|
|
|
/* the table that holds the result of the compression */
|
|
struct best_table {
|
|
unsigned char table[128][2];
|
|
unsigned char len[128];
|
|
};
|
|
|
|
struct best_table sym_best = {};
|
|
struct best_table word_best = {};
|
|
static unsigned long long start_time;
|
|
|
|
static unsigned long long nanotime(void)
|
|
{
|
|
struct timespec t;
|
|
|
|
t.tv_sec = t.tv_nsec = 0;
|
|
clock_gettime(CLOCK_MONOTONIC, &t);
|
|
return t.tv_sec * 1000000000LL + t.tv_nsec;
|
|
}
|
|
|
|
static void usage(void)
|
|
{
|
|
fprintf(stderr, "Usage: kallsyms [--all-symbols] "
|
|
"[--symbol-prefix=<prefix char>] "
|
|
"[--page-offset=<CONFIG_PAGE_OFFSET>] "
|
|
"[--base-relative] < in.map > out.S\n");
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* This ignores the intensely annoying "mapping symbols" found
|
|
* in ARM ELF files: $a, $t and $d.
|
|
*/
|
|
static inline int is_arm_mapping_symbol(const char *str)
|
|
{
|
|
return str[0] == '$' && strchr("axtd", str[1])
|
|
&& (str[2] == '\0' || str[2] == '.');
|
|
}
|
|
|
|
static int check_symbol_range(const char *sym, unsigned long long addr,
|
|
struct addr_range *ranges, int entries)
|
|
{
|
|
size_t i;
|
|
struct addr_range *ar;
|
|
|
|
for (i = 0; i < entries; ++i) {
|
|
ar = &ranges[i];
|
|
|
|
if (strcmp(sym, ar->start_sym) == 0) {
|
|
ar->start = addr;
|
|
return 0;
|
|
} else if (strcmp(sym, ar->end_sym) == 0) {
|
|
ar->end = addr;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int read_symbol(FILE *in, struct sym_entry *s)
|
|
{
|
|
char str[500];
|
|
char *sym, stype;
|
|
int rc;
|
|
|
|
rc = fscanf(in, "%llx %c %499s\n", &s->addr, &stype, str);
|
|
if (rc != 3) {
|
|
if (rc != EOF && fgets(str, 500, in) == NULL)
|
|
fprintf(stderr, "Read error or end of file.\n");
|
|
return -1;
|
|
}
|
|
if (strlen(str) > KSYM_NAME_LEN) {
|
|
fprintf(stderr, "Symbol %s too long for kallsyms (%zu vs %d).\n"
|
|
"Please increase KSYM_NAME_LEN both in kernel and kallsyms.c\n",
|
|
str, strlen(str), KSYM_NAME_LEN);
|
|
return -1;
|
|
}
|
|
|
|
sym = str;
|
|
/* skip prefix char */
|
|
if (symbol_prefix_char && str[0] == symbol_prefix_char)
|
|
sym++;
|
|
|
|
/* Ignore most absolute/undefined (?) symbols. */
|
|
if (strcmp(sym, "_text") == 0) {
|
|
_text = s->addr;
|
|
} else if (check_symbol_range(sym, s->addr, text_ranges,
|
|
ARRAY_SIZE(text_ranges)) == 0) {
|
|
/* nothing to do */;
|
|
} else if (toupper(stype) == 'A') {
|
|
/* Keep these useful absolute symbols */
|
|
if (strcmp(sym, "__kernel_syscall_via_break") &&
|
|
strcmp(sym, "__kernel_syscall_via_epc") &&
|
|
strcmp(sym, "__kernel_sigtramp") &&
|
|
strcmp(sym, "__gp"))
|
|
return -1;
|
|
|
|
} else if (toupper(stype) == 'U' ||
|
|
is_arm_mapping_symbol(sym))
|
|
return -1;
|
|
/* exclude also MIPS ELF local symbols ($L123 instead of .L123) */
|
|
else if (str[0] == '$')
|
|
return -1;
|
|
/* exclude debugging symbols */
|
|
else if (stype == 'N')
|
|
return -1;
|
|
|
|
/* include the type field in the symbol name, so that it gets
|
|
* compressed together
|
|
*/
|
|
s->len = strlen(str) + 1;
|
|
s->sym = malloc(s->len + 1);
|
|
if (!s->sym) {
|
|
fprintf(stderr, "kallsyms failure: "
|
|
"unable to allocate required amount of memory\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
strcpy((char *)s->sym + 1, str);
|
|
s->sym[0] = stype;
|
|
|
|
s->percpu_absolute = 0;
|
|
|
|
/* Record if we've found __per_cpu_start/end. */
|
|
check_symbol_range(sym, s->addr, &percpu_range, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int symbol_in_range(struct sym_entry *s, struct addr_range *ranges,
|
|
int entries)
|
|
{
|
|
size_t i;
|
|
struct addr_range *ar;
|
|
|
|
for (i = 0; i < entries; ++i) {
|
|
ar = &ranges[i];
|
|
|
|
if (s->addr >= ar->start && s->addr <= ar->end)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int symbol_valid(struct sym_entry *s)
|
|
{
|
|
/* Symbols which vary between passes. Passes 1 and 2 must have
|
|
* identical symbol lists. The kallsyms_* symbols below are only added
|
|
* after pass 1, they would be included in pass 2 when --all-symbols is
|
|
* specified so exclude them to get a stable symbol list.
|
|
*/
|
|
static char *special_symbols[] = {
|
|
"kallsyms_addresses",
|
|
"kallsyms_offsets",
|
|
"kallsyms_relative_base",
|
|
"kallsyms_num_syms",
|
|
"kallsyms_names",
|
|
"kallsyms_markers",
|
|
"kallsyms_token_table",
|
|
"kallsyms_token_index",
|
|
/* add for word compress */
|
|
"kallsyms_short_base",
|
|
"kallsyms_short_cnt",
|
|
"kallsyms_num_words",
|
|
"kallsyms_words",
|
|
"kallsyms_word_markers",
|
|
"kallsyms_word_table",
|
|
"kallsyms_word_index",
|
|
|
|
/* Exclude linker generated symbols which vary between passes */
|
|
"_SDA_BASE_", /* ppc */
|
|
"_SDA2_BASE_", /* ppc */
|
|
NULL };
|
|
|
|
static char *special_suffixes[] = {
|
|
"_veneer", /* arm */
|
|
"_from_arm", /* arm */
|
|
"_from_thumb", /* arm */
|
|
NULL };
|
|
|
|
int i;
|
|
char *sym_name = (char *)s->sym + 1;
|
|
|
|
/* skip prefix char */
|
|
if (symbol_prefix_char && *sym_name == symbol_prefix_char)
|
|
sym_name++;
|
|
|
|
|
|
/* if --all-symbols is not specified, then symbols outside the text
|
|
* and inittext sections are discarded */
|
|
if (!all_symbols) {
|
|
if (symbol_in_range(s, text_ranges,
|
|
ARRAY_SIZE(text_ranges)) == 0)
|
|
return 0;
|
|
/* Corner case. Discard any symbols with the same value as
|
|
* _etext _einittext; they can move between pass 1 and 2 when
|
|
* the kallsyms data are added. If these symbols move then
|
|
* they may get dropped in pass 2, which breaks the kallsyms
|
|
* rules.
|
|
*/
|
|
if ((s->addr == text_range_text->end &&
|
|
strcmp(sym_name,
|
|
text_range_text->end_sym)) ||
|
|
(s->addr == text_range_inittext->end &&
|
|
strcmp(sym_name,
|
|
text_range_inittext->end_sym)))
|
|
return 0;
|
|
}
|
|
|
|
/* Exclude symbols which vary between passes. */
|
|
for (i = 0; special_symbols[i]; i++)
|
|
if (strcmp(sym_name, special_symbols[i]) == 0)
|
|
return 0;
|
|
|
|
for (i = 0; special_suffixes[i]; i++) {
|
|
int l = strlen(sym_name) - strlen(special_suffixes[i]);
|
|
|
|
if (l >= 0 && strcmp(sym_name + l, special_suffixes[i]) == 0)
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void read_map(FILE *in)
|
|
{
|
|
while (!feof(in)) {
|
|
if (table_cnt >= table_size) {
|
|
table_size += 10000;
|
|
table = realloc(table, sizeof(*table) * table_size);
|
|
if (!table) {
|
|
fprintf(stderr, "out of memory\n");
|
|
exit (1);
|
|
}
|
|
}
|
|
if (read_symbol(in, &table[table_cnt]) == 0) {
|
|
table[table_cnt].start_pos = table_cnt;
|
|
table_cnt++;
|
|
}
|
|
}
|
|
#if DEBUG_COMPRESS
|
|
tprintf("%s, table_cnt:%d\n", __func__, table_cnt);
|
|
#endif
|
|
}
|
|
|
|
static void output_label(char *label)
|
|
{
|
|
if (symbol_prefix_char)
|
|
printf(".globl %c%s\n", symbol_prefix_char, label);
|
|
else
|
|
printf(".globl %s\n", label);
|
|
printf("\tALGN\n");
|
|
if (symbol_prefix_char)
|
|
printf("%c%s:\n", symbol_prefix_char, label);
|
|
else
|
|
printf("%s:\n", label);
|
|
}
|
|
|
|
/* uncompress a compressed symbol. When this function is called, the best table
|
|
* might still be compressed itself, so the function needs to be recursive
|
|
*/
|
|
static int expand_symbol(unsigned char *data, int len, char *result, struct best_table *b)
|
|
{
|
|
int c, rlen, total = 0;
|
|
|
|
while (len) {
|
|
c = *data;
|
|
/* if the table holds a single char that is the same as the one
|
|
* we are looking for, then end the search
|
|
*/
|
|
if (b->table[c][0] == c && b->len[c] == 1) {
|
|
*result++ = c;
|
|
total++;
|
|
} else {
|
|
/* if not, recurse and expand */
|
|
rlen = expand_symbol(b->table[c], b->len[c], result, b);
|
|
total += rlen;
|
|
result += rlen;
|
|
}
|
|
data++;
|
|
len--;
|
|
}
|
|
*result = 0;
|
|
|
|
return total;
|
|
}
|
|
|
|
static void *zalloc(int size)
|
|
{
|
|
void *ret;
|
|
|
|
ret = malloc(size);
|
|
if (ret) {
|
|
memset(ret, 0, size);
|
|
} else {
|
|
fprintf(stderr, "alloc memory %d bytes failed\n", size);
|
|
exit(-1);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void process_for_short_base(void)
|
|
{
|
|
long long offset;
|
|
unsigned long long base_addr = 0;
|
|
int syms = 0, bc = 0, i;
|
|
struct short_base *base;
|
|
|
|
printf("\tALGN\n");
|
|
base = zalloc((short_base_cnt + 1) * sizeof(*base));
|
|
for (i = 0; i < table_cnt; i++) {
|
|
if (!base_addr) {
|
|
base[bc].base = table[i].addr;
|
|
base[bc].start_idx = i;
|
|
base_addr = table[i].addr;
|
|
printf(".Lshort_group%d:\n", bc);
|
|
bc++;
|
|
}
|
|
if (table[i].addr - base_addr < 0x10000) { /* in 64k range */
|
|
offset = table[i].addr - base_addr;
|
|
printf("\t.short\t%#x\n", (unsigned short)offset);
|
|
syms++;
|
|
} else { /* overflow */
|
|
base[bc - 1].syms = syms;
|
|
base[bc].base = table[i].addr;
|
|
base[bc].start_idx = i;
|
|
base_addr = table[i].addr;
|
|
syms = 1;
|
|
printf("\n.Lshort_group%d:\n", bc);
|
|
printf("\t.short\t%#x\n", (unsigned short)0);
|
|
bc++;
|
|
}
|
|
}
|
|
base[bc - 1].syms = syms;
|
|
|
|
output_label("kallsyms_short_base");
|
|
for (i = 0; i < short_base_cnt; i++) {
|
|
printf("\tPTR\t%#lx\n", base[i].base);
|
|
printf("\t.word\t%#x\n", base[i].start_idx);
|
|
printf("\t.word\t%#x\n", base[i].syms);
|
|
printf("\tPTR\t.Lshort_group%d\n", i);
|
|
}
|
|
output_label("kallsyms_short_cnt");
|
|
printf("\t.word\t%#x\n", short_base_cnt);
|
|
}
|
|
|
|
static void write_src(void)
|
|
{
|
|
unsigned int i, k, off;
|
|
unsigned int best_idx[128];
|
|
unsigned int *markers;
|
|
unsigned int sym_size, sym_token_size;
|
|
unsigned int word_size, word_token_size;
|
|
char buf[KSYM_NAME_LEN];
|
|
|
|
printf("#include <asm/types.h>\n");
|
|
printf("#if BITS_PER_LONG == 64\n");
|
|
printf("#define PTR .quad\n");
|
|
printf("#define ALGN .align 3\n");
|
|
printf("#else\n");
|
|
printf("#define PTR .long\n");
|
|
printf("#define ALGN .align 2\n");
|
|
printf("#endif\n");
|
|
|
|
printf("\t.section .rodata, \"a\"\n");
|
|
|
|
/* Provide proper symbols relocatability by their relativeness
|
|
* to a fixed anchor point in the runtime image, either '_text'
|
|
* for absolute address tables, in which case the linker will
|
|
* emit the final addresses at build time. Otherwise, use the
|
|
* offset relative to the lowest value encountered of all relative
|
|
* symbols, and emit non-relocatable fixed offsets that will be fixed
|
|
* up at runtime.
|
|
*
|
|
* The symbol names cannot be used to construct normal symbol
|
|
* references as the list of symbols contains symbols that are
|
|
* declared static and are private to their .o files. This prevents
|
|
* .tmp_kallsyms.o or any other object from referencing them.
|
|
*/
|
|
process_for_short_base();
|
|
printf("\n");
|
|
|
|
output_label("kallsyms_num_syms");
|
|
printf("\t.word\t%d\n", table_cnt);
|
|
printf("\n");
|
|
|
|
/* table of offset markers, that give the offset in the compressed stream
|
|
* every 256 symbols
|
|
*/
|
|
markers = malloc(sizeof(unsigned int) * ((table_cnt + 255) / 256));
|
|
if (!markers) {
|
|
fprintf(stderr, "kallsyms failure: "
|
|
"unable to allocate required memory\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
output_label("kallsyms_names");
|
|
off = 0;
|
|
for (i = 0; i < table_cnt; i++) {
|
|
if ((i & 0xFF) == 0)
|
|
markers[i >> 8] = off;
|
|
|
|
printf("\t.byte 0x%02x", table[i].len);
|
|
for (k = 0; k < table[i].len; k++)
|
|
printf(", 0x%02x", table[i].sym[k]);
|
|
printf("\n");
|
|
|
|
off += table[i].len + 1;
|
|
}
|
|
printf("\n");
|
|
sym_size = off;
|
|
|
|
output_label("kallsyms_markers");
|
|
for (i = 0; i < ((table_cnt + 255) >> 8); i++)
|
|
printf("\t.word\t%d\n", markers[i]);
|
|
printf("\n");
|
|
|
|
free(markers);
|
|
|
|
output_label("kallsyms_token_table");
|
|
off = 0;
|
|
for (i = 0; i < 128; i++) {
|
|
best_idx[i] = off;
|
|
expand_symbol(sym_best.table[i], sym_best.len[i], buf, &sym_best);
|
|
printf("\t.asciz\t\"%s\"\n", buf);
|
|
off += strlen(buf) + 1;
|
|
}
|
|
printf("\n");
|
|
sym_token_size = off;
|
|
|
|
output_label("kallsyms_token_index");
|
|
for (i = 0; i < 128; i++)
|
|
printf("\t.short\t%d\n", best_idx[i]);
|
|
printf("\n");
|
|
|
|
/*---- output for word compress ----*/
|
|
output_label("kallsyms_num_words");
|
|
printf("\t.word\t%d\n", final_words_cnt);
|
|
printf("\n");
|
|
|
|
markers = malloc(sizeof(unsigned int) * ((final_words_cnt + 255) / 256));
|
|
if (!markers) {
|
|
fprintf(stderr, "kallsyms failure: "
|
|
"unable to allocate required memory\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
output_label("kallsyms_words");
|
|
off = 0;
|
|
for (i = 0; i < final_words_cnt; i++) {
|
|
if ((i & 0xFF) == 0)
|
|
markers[i >> 8] = off;
|
|
|
|
printf("\t.byte 0x%02x", final_words[i].len);
|
|
for (k = 0; k < final_words[i].len; k++)
|
|
printf(", 0x%02x", final_words[i].str[k]);
|
|
printf("\n");
|
|
|
|
off += final_words[i].len + 1;
|
|
}
|
|
printf("\n");
|
|
word_size = off;
|
|
|
|
output_label("kallsyms_word_markers");
|
|
for (i = 0; i < ((final_words_cnt + 255) >> 8); i++)
|
|
printf("\t.word\t%d\n", markers[i]);
|
|
printf("\n");
|
|
|
|
free(markers);
|
|
|
|
output_label("kallsyms_word_table");
|
|
off = 0;
|
|
for (i = 0; i < 128; i++) {
|
|
best_idx[i] = off;
|
|
expand_symbol(word_best.table[i], word_best.len[i], buf, &word_best);
|
|
printf("\t.asciz\t\"%s\"\n", buf);
|
|
off += strlen(buf) + 1;
|
|
}
|
|
printf("\n");
|
|
word_token_size = off;
|
|
|
|
output_label("kallsyms_word_index");
|
|
for (i = 0; i < 128; i++)
|
|
printf("\t.short\t%d\n", best_idx[i]);
|
|
printf("\n");
|
|
|
|
/* print something for statistics */
|
|
printf("/* ------ kallsym statistics ------\n");
|
|
printf(" * kallsyms_num_syms:%d, offset size:%ld\n",
|
|
table_cnt, table_cnt * sizeof(int));
|
|
printf(" * kallsyms_names:%d, marker size:%ld\n",
|
|
sym_size, (table_cnt + 255) / 256 * sizeof(int));
|
|
printf(" * kallsyms_token size:%d, token table size:%ld\n",
|
|
sym_token_size, 128 * sizeof(short));
|
|
printf(" * kallsyms_word_cnt:%d\n", final_words_cnt);
|
|
printf(" * kallsyms_words:%d, marker size:%ld\n",
|
|
word_size, (final_words_cnt + 255) / 256 * sizeof(int));
|
|
printf(" * kallsyms_token size:%d, token table size:%ld\n",
|
|
word_token_size, 128 * sizeof(short));
|
|
printf(" * input size:%ld, final cost size:%ld, compress time:%lld us\n",
|
|
input_len, sym_size + (table_cnt + 255) / 256 * sizeof(int) +
|
|
sym_token_size + 128 * sizeof(short) + word_size +
|
|
(final_words_cnt + 255) / 256 * sizeof(int) + word_token_size +
|
|
128 * sizeof(short), start_time);
|
|
printf(" */\n");
|
|
}
|
|
|
|
|
|
static unsigned int hash_str(unsigned char *str, int len)
|
|
{
|
|
unsigned int hash = 0, i;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
/* simple prim number for hash */
|
|
hash = hash * 131 + str[i];
|
|
}
|
|
|
|
return hash & (KEY_WORD_HASH_SIZE - 1);
|
|
}
|
|
|
|
/*-------------------- process for key workd ------------------*/
|
|
static struct key_word *key_words[KEY_WORD_HASH_SIZE] = {};
|
|
static unsigned int hash_cnt[KEY_WORD_HASH_SIZE] = {};
|
|
static unsigned int total_word;
|
|
|
|
static int is_split_char(char c)
|
|
{
|
|
if (c == '_' || c == '.')
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
static struct key_word *find_exist_key_word(unsigned int hash,
|
|
unsigned char *str,
|
|
int len)
|
|
{
|
|
struct key_word *k;
|
|
|
|
if (key_words[hash] == NULL)
|
|
return NULL;
|
|
|
|
k = key_words[hash];
|
|
while (k) {
|
|
if ((k->word_len == len) &&
|
|
(memcmp(k->word, str, len) == 0)) {
|
|
return k;
|
|
}
|
|
k = k->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void remember_word(unsigned char *str, struct sym_entry *s,
|
|
int len)
|
|
{
|
|
unsigned int hash, i;
|
|
struct key_word *k;
|
|
|
|
if (len < 2) /* no need remember too small string */
|
|
return;
|
|
hash = hash_str(str, len);
|
|
k = find_exist_key_word(hash, str, len);
|
|
if (!k) {
|
|
k = zalloc(sizeof(*k));
|
|
memcpy(k->word, str, len);
|
|
k->word_len = len;
|
|
k->total_cnt = 1;
|
|
for (i = 0; i < IDX_TYPE; i++)
|
|
k->idx_type[i] = 0xffff;
|
|
|
|
if (key_words[hash]) /* insert new key word */
|
|
k->next = key_words[hash];
|
|
key_words[hash] = k;
|
|
hash_cnt[hash]++;
|
|
total_word++;
|
|
} else {
|
|
k->total_cnt++;
|
|
}
|
|
}
|
|
|
|
static void split_symbol(struct sym_entry *s)
|
|
{
|
|
unsigned char *p1, *p2 = NULL;
|
|
int i = 0, sublen = 0, splited = 0;
|
|
|
|
p1 = s->sym;
|
|
for (i = 0; i < s->len; i++) {
|
|
if (is_split_char(s->sym[i])) {
|
|
p2 = p1 + sublen;
|
|
remember_word(p1, s, sublen);
|
|
do { /* ignore contigous split char */
|
|
i++;
|
|
p2++;
|
|
} while (is_split_char(s->sym[i]));
|
|
p1 = p2;
|
|
sublen = 0;
|
|
splited++;
|
|
}
|
|
sublen++;
|
|
}
|
|
if (!p2) { /* none of split sub string */
|
|
remember_word(p1, s, s->len);
|
|
}
|
|
if (splited) { /* end of string */
|
|
remember_word(p1, s, sublen);
|
|
}
|
|
if (splited > max_sym_words)
|
|
max_sym_words = splited;
|
|
}
|
|
|
|
#if DEBUG_COMPRESS
|
|
static void show_keyword(struct key_word *k, int hash)
|
|
{
|
|
int i;
|
|
|
|
tprintf("k:%p, hash:%4d, len:%d, total:%d, ",
|
|
k, hash, k->word_len, k->total_cnt);
|
|
for (i = 0; i < k->word_len; i++)
|
|
tprintf("%c", k->word[i]);
|
|
|
|
tprintf("\n");
|
|
}
|
|
|
|
static void show_all_key_word(struct key_word *words[])
|
|
{
|
|
int i;
|
|
struct key_word *k;
|
|
int total = 0;
|
|
|
|
for (i = 0; i < KEY_WORD_HASH_SIZE; i++) {
|
|
tprintf("hash:%4d, cnt:%4d\n", i, hash_cnt[i]);
|
|
k = words[i];
|
|
while (k) {
|
|
show_keyword(k, i);
|
|
k = k->next;
|
|
total++;
|
|
}
|
|
}
|
|
tprintf("%s, total word:%d\n", __func__, total);
|
|
}
|
|
#endif
|
|
|
|
static void remove_valueless_key_word(void)
|
|
{
|
|
int i;
|
|
struct key_word *k, *p, *n;
|
|
|
|
for (i = 0; i < KEY_WORD_HASH_SIZE; i++) {
|
|
p = key_words[i];
|
|
k = p;
|
|
while (k) {
|
|
n = k->next;
|
|
if ((k->total_cnt < 2) || (k->word_len <= 2)) {
|
|
if (k == key_words[i]) /* remove first node */
|
|
key_words[i] = n;
|
|
else
|
|
p->next = n;
|
|
} else {
|
|
p = k;
|
|
}
|
|
k = n;
|
|
}
|
|
}
|
|
#if DEBUG_COMPRESS
|
|
show_all_key_word(key_words);
|
|
#endif
|
|
}
|
|
|
|
static int build_store_word(struct key_word *k, char split, int type)
|
|
{
|
|
unsigned char *store_word = NULL;
|
|
|
|
if (final_words_cnt >= (KEY_WORD_BASE - 2)) {
|
|
fprintf(stderr, "final_keyword out of range\n");
|
|
return -1;
|
|
}
|
|
|
|
store_word = zalloc(KSYM_NAME_LEN);
|
|
final_words[final_words_cnt].str = store_word;
|
|
switch (type) {
|
|
case 0: /* xxxxxx */
|
|
memcpy(store_word, k->word, k->word_len);
|
|
final_words[final_words_cnt].len = k->word_len;
|
|
break;
|
|
case 1: /* xxx_ */
|
|
memcpy(store_word, k->word, k->word_len);
|
|
store_word[k->word_len] = split;
|
|
final_words[final_words_cnt].len = k->word_len + 1;
|
|
break;
|
|
case 2: /* _xxxx */
|
|
case 5: /* .xx */
|
|
store_word[0] = split;
|
|
memcpy(store_word + 1, k->word, k->word_len);
|
|
final_words[final_words_cnt].len = k->word_len + 1;
|
|
break;
|
|
case 3: /* _xx_ */
|
|
case 4: /* .xx. */
|
|
store_word[0] = split;
|
|
memcpy(store_word + 1, k->word, k->word_len);
|
|
store_word[k->word_len + 1] = split;
|
|
final_words[final_words_cnt].len = k->word_len + 2;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
k->idx_type[type] = final_words_cnt;
|
|
finnal_sym_len += final_words[final_words_cnt].len;
|
|
#if DEBUG_COMPRESS
|
|
tprintf("%s, used:%4d, type:%d, split:%c, word:%s\n",
|
|
__func__, final_words_cnt, type, split,
|
|
final_words[final_words_cnt].str);
|
|
#endif
|
|
final_words_cnt++;
|
|
return 0;
|
|
}
|
|
|
|
#if DEBUG_COMPRESS
|
|
static int decode_final_str(struct final_word *f, char *buf)
|
|
{
|
|
int i, dlen = 0;
|
|
int data;
|
|
|
|
if (f->str[0] < 0x80) {
|
|
strcpy(buf, (char *)f->str);
|
|
return f->len;
|
|
}
|
|
|
|
for (i = 0; i < f->len; i += 2) {
|
|
data = (f->str[i] << 8) | f->str[i + 1];
|
|
data &= ~KEY_WORD_BASE;
|
|
dlen += decode_final_str(final_words + data, buf + dlen);
|
|
}
|
|
return dlen;
|
|
}
|
|
|
|
static void show_sym(struct sym_entry *s, int decode)
|
|
{
|
|
int i, data;
|
|
struct final_word *f;
|
|
char buf[128];
|
|
|
|
for (i = 0; i < s->len; i++) {
|
|
if (decode && (s->sym[i] >= 0x80)) {
|
|
memset(buf, 0, sizeof(buf));
|
|
data = (s->sym[i] << 8) | s->sym[i + 1];
|
|
data -= KEY_WORD_BASE;
|
|
f = &final_words[data];
|
|
decode_final_str(f, buf);
|
|
tprintf("%s", buf);
|
|
i++;
|
|
} else if (!decode && (s->sym[i] >= 0x80)) {
|
|
tprintf("<%02x %02x>", s->sym[i], s->sym[i + 1]);
|
|
i++;
|
|
} else {
|
|
tprintf("%c", s->sym[i]);
|
|
}
|
|
}
|
|
tprintf(", len:%d\n", s->len);
|
|
}
|
|
#endif
|
|
|
|
static unsigned char *compress_a_keyword(unsigned char *str,
|
|
struct sym_entry *s,
|
|
int len, int *relen)
|
|
{
|
|
unsigned int hash, final_idx, data, type;
|
|
unsigned int raw_len, remain_len, word_len = len, pre_len;
|
|
struct key_word *k;
|
|
char *word, first_split, end_split, st;
|
|
|
|
if (len < 2) /* no need remember too small string */
|
|
return NULL;
|
|
|
|
word = (char *)str;
|
|
first_split = str[0];
|
|
end_split = str[len];
|
|
while (is_split_char(word[0])) { /* skip split char for hash */
|
|
word++;
|
|
word_len--;
|
|
}
|
|
hash = hash_str((unsigned char *)word, word_len);
|
|
k = find_exist_key_word(hash, (unsigned char *)word, word_len);
|
|
if (!k)
|
|
return NULL;
|
|
|
|
if (!is_split_char(first_split)) {
|
|
if (end_split == '_') {
|
|
/* xx_ */
|
|
st = end_split;
|
|
type = 1;
|
|
} else {
|
|
/* xx or xx */
|
|
st = 0;
|
|
type = 0;
|
|
}
|
|
} else {
|
|
st = first_split;
|
|
if (first_split == '.') {
|
|
if (end_split == '.')
|
|
type = 4;
|
|
else
|
|
type = 5;
|
|
} else{
|
|
if (end_split == '_')
|
|
type = 3;
|
|
else
|
|
type = 2;
|
|
}
|
|
}
|
|
if (k->idx_type[type] == 0xffff) {
|
|
/* init for this type */
|
|
if (build_store_word(k, st, type) < 0)
|
|
return NULL;
|
|
}
|
|
final_idx = k->idx_type[type];
|
|
raw_len = final_words[final_idx].len;
|
|
data = final_idx + KEY_WORD_BASE;
|
|
str[0] = data >> 8;
|
|
str[1] = data & 0xff;
|
|
pre_len = (unsigned long)str - (unsigned long)s->sym;
|
|
remain_len = s->len - pre_len - raw_len;
|
|
*relen = remain_len;
|
|
memmove(str + 2, str + raw_len, remain_len + 1);
|
|
s->len -= (raw_len - 2);
|
|
final_words[final_idx].cnt++;
|
|
|
|
return str + 2;
|
|
}
|
|
|
|
static int restore_valueless_keyword(void)
|
|
{
|
|
int i, mov_len, j, idx, valueless_cnt = 0;
|
|
struct final_word *f;
|
|
struct sym_entry *s;
|
|
unsigned char *p;
|
|
|
|
for (i = 0; i < table_cnt; i++) {
|
|
s = table + i;
|
|
for (j = 0; j < s->len; j++) {
|
|
if (s->sym[j] >= 0x80) {
|
|
idx = (s->sym[j] << 8) +
|
|
(s->sym[j + 1] & 0xff);
|
|
idx -= KEY_WORD_BASE;
|
|
f = &final_words[idx];
|
|
if (f->cnt * f->len <= (f->len + 2 * f->cnt)) {
|
|
mov_len = s->len - (j + 2);
|
|
p = s->sym + j + 2;
|
|
/* move to high area and restore word */
|
|
memmove(p + (f->len - 2), p, mov_len);
|
|
memcpy(s->sym + j, f->str, f->len);
|
|
s->len += (f->len - 2);
|
|
if (f->cnt) {
|
|
/* mark as invalid entry*/
|
|
f->cnt = 0;
|
|
finnal_sym_len -= f->len;
|
|
valueless_cnt++;
|
|
}
|
|
} else {
|
|
j++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return valueless_cnt;
|
|
}
|
|
|
|
static void init_special_token(void)
|
|
{
|
|
int i;
|
|
struct final_word *f;
|
|
char *special_toke[] = {
|
|
"t_",
|
|
"T_",
|
|
"d_",
|
|
"D_",
|
|
"r_",
|
|
"R_",
|
|
"b_",
|
|
"B_"
|
|
};
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
f = &final_words[i];
|
|
f->str = zalloc(KSYM_NAME_LEN);
|
|
strcpy((char *)f->str, special_toke[i]);
|
|
f->len = 2;
|
|
f->cnt = 0;
|
|
f->node_cnt = 0;
|
|
f->tree.rb_node = NULL;
|
|
}
|
|
}
|
|
|
|
static void process_special_token(struct sym_entry *s)
|
|
{
|
|
int i, data;
|
|
struct final_word *f;
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
f = &final_words[i];
|
|
if (s->sym[0] == f->str[0] && s->sym[1] == f->str[1]) {
|
|
data = KEY_WORD_BASE + i;
|
|
s->sym[0] = data >> 8;
|
|
s->sym[1] = (data & 0xff);
|
|
f->cnt++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void count_merge_info(unsigned int d1, unsigned int d2, int *max)
|
|
{
|
|
struct final_word *f1;
|
|
struct merge_word *m;
|
|
struct rb_node **rb, *parent = NULL;
|
|
|
|
f1 = final_words + d1;
|
|
rb = &f1->tree.rb_node;
|
|
while (*rb) {
|
|
parent = *rb;
|
|
m = rb_entry(parent, struct merge_word, entry);
|
|
if (m->code == d2) { /* find */
|
|
m->count++;
|
|
if (m->count > *max)
|
|
*max = m->count;
|
|
return;
|
|
} else if (d2 < m->code) {
|
|
rb = &m->entry.rb_left;
|
|
} else
|
|
rb = &m->entry.rb_right;
|
|
}
|
|
|
|
/* not found, build a new node */
|
|
m = zalloc(sizeof(*m));
|
|
m->code = d2;
|
|
m->count = 1;
|
|
rb_link_node(&m->entry, parent, rb);
|
|
rb_insert_color(&m->entry, &f1->tree);
|
|
f1->node_cnt++;
|
|
}
|
|
|
|
static void build_merge_word(struct sym_entry *sym, int *max_cnt)
|
|
{
|
|
int d1, d2, i;
|
|
unsigned char *s;
|
|
|
|
s = sym->sym;
|
|
for (i = 0; i < sym->len - 3; i++) {
|
|
if (s[i] >= 0x80) { /* first large sym */
|
|
if (s[i + 2] >= 0x80) {
|
|
d1 = (s[i] << 8) + (s[i + 1] & 0xff);
|
|
d2 = (s[i + 2] << 8) + (s[i + 3] & 0xff);
|
|
d1 -= KEY_WORD_BASE;
|
|
d2 -= KEY_WORD_BASE;
|
|
count_merge_info(d1, d2, max_cnt);
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if DEBUG_COMPRESS
|
|
static void show_merge_word(struct final_word *f)
|
|
{
|
|
struct rb_node *rb;
|
|
struct merge_word *m;
|
|
int i;
|
|
struct final_word *f2;
|
|
char buf1[KSYM_NAME_LEN] = {};
|
|
char buf2[KSYM_NAME_LEN] = {};
|
|
|
|
decode_final_str(f, buf1);
|
|
tprintf("%s, cnt:%d, node:%d, tree:%p, s:%s\n",
|
|
__func__, f->cnt, f->node_cnt, f->tree.rb_node, buf1);
|
|
for (rb = rb_first(&f->tree); rb; rb = rb_next(rb)) {
|
|
m = rb_entry(rb, struct merge_word, entry);
|
|
f2 = &final_words[m->code];
|
|
tprintf("%5d, cnt:%5d, %s -> ", m->code, m->count, buf1);
|
|
decode_final_str(f2, buf2);
|
|
tprintf("%s\n", buf2);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void count_merge_count(struct final_word *f, unsigned int *cnt)
|
|
{
|
|
struct rb_node *rb;
|
|
struct merge_word *m;
|
|
|
|
for (rb = rb_first(&f->tree); rb; rb = rb_next(rb)) {
|
|
m = rb_entry(rb, struct merge_word, entry);
|
|
cnt[m->count]++;
|
|
}
|
|
}
|
|
|
|
static int calculate_merge_cnt_threshold(int available, int max_cnt)
|
|
{
|
|
unsigned int *cnt_cnt, i, ret = 0, node_cnt = 0;
|
|
|
|
if (available < 0)
|
|
return -1;
|
|
|
|
cnt_cnt = zalloc((max_cnt + 2) * sizeof(int));
|
|
for (i = 0; i < final_words_cnt; i++) {
|
|
if (final_words[i].node_cnt) {
|
|
count_merge_count(final_words + i, cnt_cnt);
|
|
node_cnt += final_words[i].node_cnt;
|
|
}
|
|
}
|
|
if (node_cnt < available) {
|
|
ret = 5;
|
|
} else {
|
|
for (i = 0; i <= max_cnt; i++) {
|
|
if (cnt_cnt[i]) {
|
|
node_cnt -= cnt_cnt[i];
|
|
if (node_cnt < available && !ret) {
|
|
ret = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
free(cnt_cnt);
|
|
if (!ret)
|
|
return -1;
|
|
if (ret < 5)
|
|
ret = 5;
|
|
return ret;
|
|
}
|
|
|
|
static int find_empty_idx(int last)
|
|
{
|
|
int ret;
|
|
|
|
for (ret = last; ret < KEY_WORD_BASE; ret++) {
|
|
if (final_words[ret].cnt == 0)
|
|
return ret;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static struct merge_tree *find_merge_code(int d1, int d2)
|
|
{
|
|
struct merge_tree *mt;
|
|
struct rb_node *rb;
|
|
unsigned int data = (d1 << 16 | d2);
|
|
|
|
rb = merge_trees.rb_node;
|
|
while (rb) {
|
|
mt = rb_entry(rb, struct merge_tree, entry);
|
|
if (mt->key == data) { /* find */
|
|
return mt;
|
|
} else if (data < mt->key) {
|
|
rb = mt->entry.rb_left;
|
|
} else
|
|
rb = mt->entry.rb_right;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int merge_able(struct final_word *f1, int d2, int thresh)
|
|
{
|
|
struct merge_word *mw;
|
|
struct rb_node *rb;
|
|
|
|
rb = f1->tree.rb_node;
|
|
while (rb) {
|
|
mw = rb_entry(rb, struct merge_word, entry);
|
|
if (mw->code == d2) { /* find */
|
|
if (mw->count >= thresh)
|
|
return 1;
|
|
return 0;
|
|
} else if (d2 < mw->code) {
|
|
rb = mw->entry.rb_left;
|
|
} else
|
|
rb = mw->entry.rb_right;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static struct merge_tree *build_merge_tree(int d1, int d2, int *last)
|
|
{
|
|
int new_code;
|
|
unsigned int key;
|
|
struct merge_tree *mt;
|
|
struct rb_node **rb, *parent = NULL;
|
|
|
|
new_code = find_empty_idx(*last);
|
|
if (new_code < 0)
|
|
return NULL;
|
|
|
|
*last = new_code;
|
|
key = (d1 << 16) | d2;
|
|
rb = &merge_trees.rb_node;
|
|
while (*rb) {
|
|
parent = *rb;
|
|
mt = rb_entry(parent, struct merge_tree, entry);
|
|
if (mt->key == key) { /* find */
|
|
fprintf(stderr, "BUG: %s find exist merge tree:%d\n",
|
|
__func__, key);
|
|
exit(-1);
|
|
} else if (key < mt->key) {
|
|
rb = &mt->entry.rb_left;
|
|
} else
|
|
rb = &mt->entry.rb_right;
|
|
}
|
|
|
|
/* not found, build a new node */
|
|
mt = zalloc(sizeof(*mt));
|
|
mt->key = key;
|
|
mt->new = new_code;
|
|
rb_link_node(&mt->entry, parent, rb);
|
|
rb_insert_color(&mt->entry, &merge_trees);
|
|
return mt;
|
|
}
|
|
|
|
#if DEBUG_COMPRESS
|
|
static void show_final_str(struct final_word *f, int decode)
|
|
{
|
|
int i;
|
|
char buf[KSYM_NAME_LEN] = {};
|
|
|
|
tprintf("final_str, len:%2d, cnt:%4d, nodes:%4d, str:",
|
|
f->len, f->cnt, f->node_cnt);
|
|
if (decode) {
|
|
if (f->str) {
|
|
decode_final_str(f, buf);
|
|
tprintf("%s", buf);
|
|
}
|
|
} else if (f->str) {
|
|
for (i = 0; i < f->len; i++) {
|
|
if (f->str[i] >= 0x80) {
|
|
tprintf("<%02x %02x>", f->str[i], f->str[i + 1]);
|
|
i++;
|
|
} else {
|
|
tprintf("%c", f->str[i]);
|
|
}
|
|
}
|
|
}
|
|
tprintf("\n");
|
|
}
|
|
#endif
|
|
|
|
static void init_merge_tree(struct merge_tree *mt, int d1, int d2)
|
|
{
|
|
struct final_word *newf;
|
|
|
|
newf = &final_words[mt->new];
|
|
d1 |= KEY_WORD_BASE;
|
|
d2 |= KEY_WORD_BASE;
|
|
if (!newf->str)
|
|
newf->str = zalloc(KSYM_NAME_LEN);
|
|
newf->str[0] = d1 >> 8;
|
|
newf->str[1] = d1 & 0xff;
|
|
newf->str[2] = d2 >> 8;
|
|
newf->str[3] = d2 & 0xff;
|
|
newf->len = 4;
|
|
newf->cnt = 0;
|
|
newf->node_cnt = 0;
|
|
newf->tree.rb_node = NULL;
|
|
}
|
|
|
|
static int merge_words(struct sym_entry *s, int *last, int thresh)
|
|
{
|
|
int i, d1, d2, new, rlen, len, used = 0;
|
|
struct final_word *f1, *nf;
|
|
struct merge_tree *mt;
|
|
|
|
len = s->len;
|
|
for (i = 0; i < len - 3; i++) {
|
|
if (s->sym[i] < 0x80)
|
|
continue;
|
|
|
|
if (s->sym[i + 2] >= 0x80) {
|
|
d1 = (s->sym[i] << 8) + s->sym[i + 1];
|
|
d2 = (s->sym[i + 2] << 8) + s->sym[i + 3];
|
|
d1 -= KEY_WORD_BASE;
|
|
f1 = &final_words[d1];
|
|
if (!f1->node_cnt) { /* this word can't merge */
|
|
i++;
|
|
continue;
|
|
}
|
|
d2 -= KEY_WORD_BASE;
|
|
mt = find_merge_code(d1, d2);
|
|
if (!mt) { /* check it can merge? */
|
|
if (!merge_able(f1, d2, thresh)) {
|
|
i++;
|
|
continue;
|
|
}
|
|
mt = build_merge_tree(d1, d2, last);
|
|
init_merge_tree(mt, d1, d2);
|
|
used = 1;
|
|
}
|
|
/* merge word */
|
|
new = mt->new;
|
|
nf = &final_words[new];
|
|
new |= KEY_WORD_BASE;
|
|
s->sym[i] = (new >> 8);
|
|
s->sym[i + 1] = (new & 0xff);
|
|
rlen = s->len - i - 4;
|
|
memmove(&s->sym[i] + 2, &s->sym[i] + 4, rlen);
|
|
len -= 2;
|
|
s->len -= 2;
|
|
nf->cnt++;
|
|
#if DEBUG_COMPRESS
|
|
tprintf("%s, merge %04x %04x to %04x, i:%d last:%d\n",
|
|
__func__, d1, d2, new, i, *last);
|
|
show_final_str(final_words + d1, 0);
|
|
show_final_str(final_words + d2, 0);
|
|
show_final_str(nf, 0);
|
|
#endif
|
|
break;
|
|
}
|
|
/* no contiguous word */
|
|
i++;
|
|
}
|
|
return used;
|
|
}
|
|
|
|
static void delete_merge_word(struct rb_node *rb)
|
|
{
|
|
struct merge_word *m;
|
|
struct rb_node *parent;
|
|
|
|
if (!rb->rb_left && !rb->rb_right) {
|
|
parent = rb_parent(rb);
|
|
if (parent) {
|
|
if (rb == parent->rb_left)
|
|
parent->rb_left = NULL;
|
|
else
|
|
parent->rb_right = NULL;
|
|
}
|
|
m = rb_entry(rb, struct merge_word, entry);
|
|
free(m);
|
|
return;
|
|
}
|
|
|
|
if (rb->rb_left)
|
|
delete_merge_word(rb->rb_left);
|
|
if (rb->rb_right)
|
|
delete_merge_word(rb->rb_right);
|
|
delete_merge_word(rb);
|
|
}
|
|
|
|
static void free_merge_word(void)
|
|
{
|
|
struct final_word *f;
|
|
int i;
|
|
|
|
for (i = 0; i < KEY_WORD_BASE; i++) {
|
|
f = final_words + i;
|
|
if (!f->node_cnt)
|
|
continue;
|
|
delete_merge_word(f->tree.rb_node);
|
|
f->tree.rb_node = NULL;
|
|
f->node_cnt = 0;
|
|
}
|
|
}
|
|
|
|
static void delete_merge_tree(struct rb_node *rb)
|
|
{
|
|
struct merge_tree *m;
|
|
struct rb_node *parent;
|
|
|
|
if (!rb->rb_left && !rb->rb_right) {
|
|
parent = rb_parent(rb);
|
|
if (parent) {
|
|
if (rb == parent->rb_left)
|
|
parent->rb_left = NULL;
|
|
else
|
|
parent->rb_right = NULL;
|
|
}
|
|
m = rb_entry(rb, struct merge_tree, entry);
|
|
free(m);
|
|
return;
|
|
}
|
|
|
|
if (rb->rb_left)
|
|
delete_merge_tree(rb->rb_left);
|
|
if (rb->rb_right)
|
|
delete_merge_tree(rb->rb_right);
|
|
delete_merge_tree(rb);
|
|
}
|
|
|
|
static void free_merge_tree(void)
|
|
{
|
|
delete_merge_tree(merge_trees.rb_node);
|
|
merge_trees.rb_node = NULL;
|
|
}
|
|
|
|
#if DEBUG_COMPRESS
|
|
static void print_output_size(void)
|
|
{
|
|
int i, output_len = 0, fstr_len = 0;
|
|
|
|
for (i = 0; i < table_cnt; i++)
|
|
output_len += table[i].len;
|
|
|
|
for (i = 0; i < final_words_cnt; i++) {
|
|
if (final_words[i].cnt)
|
|
fstr_len += final_words[i].len;
|
|
}
|
|
tprintf("%s, input len:%ld, output len:%d, fstr_len:%d, words:%d, total:%d\n",
|
|
__func__, input_len, output_len, fstr_len, final_words_cnt,
|
|
output_len + fstr_len + final_words_cnt);
|
|
}
|
|
#endif
|
|
|
|
static void compress_key_word(void)
|
|
{
|
|
unsigned char *p1, *r, *p2 = NULL;
|
|
int i = 0, j, k, sublen = 0, splited = 0, relen;
|
|
struct sym_entry *s;
|
|
int max, last_idx, thresh, value_less, code_room;
|
|
|
|
/* 1st step compress each symbol by word */
|
|
for (i = 0; i < table_cnt; i++) {
|
|
s = &table[i];
|
|
p1 = s->sym;
|
|
p2 = p1;
|
|
k = 0;
|
|
relen = s->len;
|
|
sublen = 0;
|
|
while (k < relen) {
|
|
if (is_split_char(p1[k])) {
|
|
r = compress_a_keyword(p2, s, sublen, &relen);
|
|
splited++;
|
|
if (!r) { /* not compressed */
|
|
p2 += sublen;
|
|
sublen = 1;
|
|
k++;
|
|
} else { /* compressed */
|
|
p2 = r;
|
|
p1 = r;
|
|
k = 0;
|
|
sublen = 0;
|
|
}
|
|
} else {
|
|
sublen++;
|
|
k++;
|
|
}
|
|
}
|
|
if (p2 == s->sym) { /* none of split sub string */
|
|
compress_a_keyword(p2, s, s->len, &relen);
|
|
}
|
|
if (splited) { /* end of string */
|
|
compress_a_keyword(p2, s, sublen, &relen);
|
|
}
|
|
}
|
|
value_less = restore_valueless_keyword();
|
|
|
|
init_special_token();
|
|
for (i = 0; i < table_cnt; i++)
|
|
process_special_token(table + i);
|
|
|
|
/* 2nd step, merge high freq keyword */
|
|
code_room = (int)KEY_WORD_BASE - final_words_cnt + value_less;
|
|
for (k = 0; k < max_sym_words && code_room > 0; k++) {
|
|
/* merge words every round */
|
|
max = 0;
|
|
for (i = 0; i < table_cnt; i++) {
|
|
if (table[i].len >= 4)
|
|
build_merge_word(table + i, &max);
|
|
}
|
|
|
|
#if DEBUG_COMPRESS
|
|
for (i = 0; i < final_words_cnt; i++) {
|
|
if (final_words[i].node_cnt)
|
|
show_merge_word(final_words + i);
|
|
}
|
|
#endif
|
|
|
|
thresh = calculate_merge_cnt_threshold(code_room, max);
|
|
if (thresh < 0 || thresh > max) {
|
|
free_merge_word();
|
|
break;
|
|
}
|
|
|
|
j = 0;
|
|
last_idx = 0;
|
|
for (i = 0; i < table_cnt; i++)
|
|
j += merge_words(table + i, &last_idx, thresh);
|
|
|
|
code_room -= j;
|
|
if (KEY_WORD_BASE - code_room > final_words_cnt)
|
|
final_words_cnt = KEY_WORD_BASE - code_room;
|
|
free_merge_word();
|
|
free_merge_tree();
|
|
}
|
|
|
|
#if DEBUG_COMPRESS
|
|
print_output_size();
|
|
for (i = 0; i < table_cnt; i++)
|
|
show_sym(&table[i], 1);
|
|
|
|
for (i = 0; i < final_words_cnt; i++) {
|
|
tprintf("%5d, ", i);
|
|
show_final_str(final_words + i, 1);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* table lookup compression functions */
|
|
|
|
/* count all the possible tokens in a symbol */
|
|
static void learn_symbol(unsigned char *symbol, int len)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < len - 1; i++) {
|
|
if (symbol[i] >= 0x80) { /* skip high word */
|
|
i++;
|
|
continue;
|
|
}
|
|
if (symbol[i + 1] < 0x80)
|
|
token_profit[symbol[i] + (symbol[i + 1] * 128)]++;
|
|
else
|
|
i += 2;
|
|
}
|
|
}
|
|
|
|
/* decrease the count for all the possible tokens in a symbol */
|
|
static void forget_symbol(unsigned char *symbol, int len)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < len - 1; i++) {
|
|
if (symbol[i] >= 0x80) { /* skip high word */
|
|
i++;
|
|
continue;
|
|
}
|
|
if (symbol[i + 1] < 0x80)
|
|
token_profit[symbol[i] + (symbol[i + 1] * 128)]--;
|
|
else
|
|
i += 2;
|
|
}
|
|
}
|
|
|
|
/* remove all the invalid symbols from the table and do the initial token count */
|
|
static void build_initial_key_word(void)
|
|
{
|
|
unsigned int i, pos;
|
|
unsigned long long base_addr = 0;
|
|
|
|
pos = 0;
|
|
for (i = 0; i < table_cnt; i++) {
|
|
if (symbol_valid(&table[i])) {
|
|
if (pos != i)
|
|
table[pos] = table[i];
|
|
|
|
if (!base_addr) {
|
|
base_addr = table[i].addr;
|
|
short_base_cnt++;
|
|
}
|
|
if (table[i].addr - base_addr >= 0x10000) {
|
|
base_addr = table[i].addr;
|
|
short_base_cnt++;
|
|
}
|
|
input_len += table[pos].len;
|
|
split_symbol(&table[pos]);
|
|
pos++;
|
|
}
|
|
}
|
|
table_cnt = pos;
|
|
#if DEBUG_COMPRESS
|
|
tprintf("%s, table_cnt:%d, max_sym_words:%d, nr_short:%d\n",
|
|
__func__, table_cnt, max_sym_words, short_base_cnt);
|
|
#endif
|
|
}
|
|
|
|
/*------------- token compress ---------------------*/
|
|
static unsigned char *get_sym_char(int i)
|
|
{
|
|
return table[i].sym;
|
|
}
|
|
|
|
static unsigned int get_sym_len(int i)
|
|
{
|
|
return table[i].len;
|
|
}
|
|
|
|
static unsigned char *get_word_char(int i)
|
|
{
|
|
return final_words[i].str;
|
|
}
|
|
|
|
static unsigned int get_word_len(int i)
|
|
{
|
|
return final_words[i].len;
|
|
}
|
|
|
|
static void set_sym_len(int i, int len)
|
|
{
|
|
table[i].len = len;
|
|
}
|
|
|
|
static void set_word_len(int i, int len)
|
|
{
|
|
final_words[i].len = len;
|
|
}
|
|
|
|
typedef unsigned char *get_char(int i);
|
|
typedef unsigned int get_len(int i);
|
|
typedef void set_len(int i, int len);
|
|
|
|
static void *find_token(unsigned char *str, int len, unsigned char *token)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < len - 1; i++) {
|
|
if (str[i] >= 0x80) { /* skip large word */
|
|
i++;
|
|
continue;
|
|
}
|
|
if (str[i] == token[0] && str[i+1] == token[1])
|
|
return &str[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* replace a given token in all the valid symbols. Use the sampled symbols
|
|
* to update the counts
|
|
*/
|
|
static void compress_symbols(unsigned char *str, int idx, int count,
|
|
get_char get_chars, get_len get_lens,
|
|
set_len set_lens)
|
|
{
|
|
unsigned int i, len, size;
|
|
unsigned char *p1, *p2;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
len = get_lens(i);
|
|
p1 = get_chars(i);
|
|
if (!p1)
|
|
continue;
|
|
|
|
/* find the token on the symbol */
|
|
p2 = find_token(p1, len, str);
|
|
if (!p2)
|
|
continue;
|
|
|
|
/* decrease the counts for this symbol's tokens */
|
|
forget_symbol(p1, len);
|
|
|
|
size = len;
|
|
|
|
do {
|
|
*p2 = idx;
|
|
p2++;
|
|
size = size - (p2 - p1) - 1;
|
|
memmove(p2, p2 + 1, size);
|
|
p1 = p2;
|
|
len--;
|
|
|
|
if (size < 2)
|
|
break;
|
|
|
|
/* find the token on the symbol */
|
|
p2 = find_token(p1, size, str);
|
|
|
|
} while (p2);
|
|
|
|
set_lens(i, len);
|
|
p1 = get_chars(i);
|
|
|
|
/* increase the counts for this symbol's new tokens */
|
|
learn_symbol(p1, len);
|
|
}
|
|
}
|
|
|
|
/* search the token with the maximum profit */
|
|
static int find_best_token(void)
|
|
{
|
|
int i, best, bestprofit;
|
|
|
|
bestprofit = -10000;
|
|
best = 0;
|
|
|
|
for (i = 0; i < 128 * 128; i++) {
|
|
if (token_profit[i] > bestprofit) {
|
|
best = i;
|
|
bestprofit = token_profit[i];
|
|
}
|
|
}
|
|
return best;
|
|
}
|
|
|
|
/* this is the core of the algorithm: calculate the "best" table */
|
|
static void optimize_result(struct best_table *b, int count,
|
|
get_char get_chars, get_len get_lens,
|
|
set_len set_lens)
|
|
{
|
|
int i, best = 0;
|
|
|
|
/* using the '\0' symbol last allows compress_symbols to use standard
|
|
* fast string functions
|
|
*/
|
|
for (i = 127; i >= 0; i--) {
|
|
|
|
/* if this table slot is empty (it is not used by an actual
|
|
* original char code
|
|
*/
|
|
if (!b->len[i]) {
|
|
|
|
/* find the token with the breates profit value */
|
|
best = find_best_token();
|
|
if (token_profit[best] == 0)
|
|
break;
|
|
|
|
/* place it in the "best" table */
|
|
b->len[i] = 2;
|
|
b->table[i][0] = best & 0x7F;
|
|
b->table[i][1] = (best / 128) & 0x7F;
|
|
|
|
/* replace this token in all the valid symbols */
|
|
compress_symbols(b->table[i], i, count,
|
|
get_chars, get_lens, set_lens);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* remove all the invalid symbols from the table and do the initial token count */
|
|
static void build_initial_tok_table(int count, get_char get_chars,
|
|
get_len get_lens)
|
|
{
|
|
unsigned int i;
|
|
unsigned char *p;
|
|
int len;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
p = get_chars(i);
|
|
if (!p)
|
|
continue;
|
|
len = get_lens(i);
|
|
learn_symbol(p, len);
|
|
}
|
|
}
|
|
|
|
/* start by placing the symbols that are actually used on the table */
|
|
static void insert_real_symbols_in_table(struct best_table *best, int count,
|
|
get_char get_chars, get_len get_lens)
|
|
{
|
|
unsigned int i, j, c, len;
|
|
unsigned char *p;
|
|
|
|
memset(best, 0, sizeof(*best));
|
|
|
|
for (i = 0; i < count; i++) {
|
|
len = get_lens(i);
|
|
p = get_chars(i);
|
|
if (!p)
|
|
continue;
|
|
|
|
for (j = 0; j < len; j++) {
|
|
c = p[j];
|
|
if (c >= 0x80) {
|
|
j++;
|
|
continue;
|
|
}
|
|
best->table[c][0] = c;
|
|
best->len[c] = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void optimize_token_table(void)
|
|
{
|
|
/* 1st, compress with splited key word */
|
|
start_time = nanotime();
|
|
build_initial_key_word();
|
|
if (!table_cnt) {
|
|
fprintf(stderr, "No valid symbol.\n");
|
|
exit(1);
|
|
}
|
|
remove_valueless_key_word();
|
|
compress_key_word();
|
|
|
|
/* 2nd, compress remain chars / final_str table */
|
|
build_initial_tok_table(table_cnt, get_sym_char, get_sym_len);
|
|
insert_real_symbols_in_table(&sym_best, table_cnt,
|
|
get_sym_char, get_sym_len);
|
|
optimize_result(&sym_best, table_cnt,
|
|
get_sym_char, get_sym_len, set_sym_len);
|
|
|
|
memset(token_profit, 0, sizeof(token_profit));
|
|
build_initial_tok_table(final_words_cnt,
|
|
get_word_char, get_word_len);
|
|
insert_real_symbols_in_table(&word_best, final_words_cnt,
|
|
get_word_char, get_word_len);
|
|
optimize_result(&word_best, final_words_cnt,
|
|
get_word_char, get_word_len, set_word_len);
|
|
|
|
start_time = nanotime() - start_time;
|
|
#if DEBUG_COMPRESS
|
|
print_output_size();
|
|
#endif
|
|
}
|
|
|
|
/* guess for "linker script provide" symbol */
|
|
static int may_be_linker_script_provide_symbol(const struct sym_entry *se)
|
|
{
|
|
const char *symbol = (char *)se->sym + 1;
|
|
int len = se->len - 1;
|
|
|
|
if (len < 8)
|
|
return 0;
|
|
|
|
if (symbol[0] != '_' || symbol[1] != '_')
|
|
return 0;
|
|
|
|
/* __start_XXXXX */
|
|
if (!memcmp(symbol + 2, "start_", 6))
|
|
return 1;
|
|
|
|
/* __stop_XXXXX */
|
|
if (!memcmp(symbol + 2, "stop_", 5))
|
|
return 1;
|
|
|
|
/* __end_XXXXX */
|
|
if (!memcmp(symbol + 2, "end_", 4))
|
|
return 1;
|
|
|
|
/* __XXXXX_start */
|
|
if (!memcmp(symbol + len - 6, "_start", 6))
|
|
return 1;
|
|
|
|
/* __XXXXX_end */
|
|
if (!memcmp(symbol + len - 4, "_end", 4))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int prefix_underscores_count(const char *str)
|
|
{
|
|
const char *tail = str;
|
|
|
|
while (*tail == '_')
|
|
tail++;
|
|
|
|
return tail - str;
|
|
}
|
|
|
|
static int compare_symbols(const void *a, const void *b)
|
|
{
|
|
const struct sym_entry *sa;
|
|
const struct sym_entry *sb;
|
|
int wa, wb;
|
|
|
|
sa = a;
|
|
sb = b;
|
|
|
|
/* sort by address first */
|
|
if (sa->addr > sb->addr)
|
|
return 1;
|
|
if (sa->addr < sb->addr)
|
|
return -1;
|
|
|
|
/* sort by "weakness" type */
|
|
wa = (sa->sym[0] == 'w') || (sa->sym[0] == 'W');
|
|
wb = (sb->sym[0] == 'w') || (sb->sym[0] == 'W');
|
|
if (wa != wb)
|
|
return wa - wb;
|
|
|
|
/* sort by "linker script provide" type */
|
|
wa = may_be_linker_script_provide_symbol(sa);
|
|
wb = may_be_linker_script_provide_symbol(sb);
|
|
if (wa != wb)
|
|
return wa - wb;
|
|
|
|
/* sort by the number of prefix underscores */
|
|
wa = prefix_underscores_count((const char *)sa->sym + 1);
|
|
wb = prefix_underscores_count((const char *)sb->sym + 1);
|
|
if (wa != wb)
|
|
return wa - wb;
|
|
|
|
/* sort by initial order, so that other symbols are left undisturbed */
|
|
return sa->start_pos - sb->start_pos;
|
|
}
|
|
|
|
static void sort_symbols(void)
|
|
{
|
|
qsort(table, table_cnt, sizeof(struct sym_entry), compare_symbols);
|
|
}
|
|
|
|
/* find the minimum non-absolute symbol address */
|
|
int main(int argc, char **argv)
|
|
{
|
|
if (argc >= 2) {
|
|
int i;
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
if (strcmp(argv[i], "--all-symbols") == 0) {
|
|
all_symbols = 1;
|
|
} else if (strcmp(argv[i], "--absolute-percpu") == 0) {
|
|
/* not supported */
|
|
} else if (strncmp(argv[i], "--symbol-prefix=", 16) == 0) {
|
|
char *p = &argv[i][16];
|
|
/* skip quote */
|
|
if ((*p == '"' && *(p+2) == '"') || (*p == '\'' && *(p+2) == '\''))
|
|
p++;
|
|
symbol_prefix_char = *p;
|
|
} else if (strcmp(argv[i], "--base-relative") == 0) {
|
|
/* not supported */
|
|
} else {
|
|
usage();
|
|
}
|
|
}
|
|
} else if (argc != 1)
|
|
usage();
|
|
|
|
read_map(stdin);
|
|
sort_symbols();
|
|
optimize_token_table();
|
|
write_src();
|
|
|
|
return 0;
|
|
}
|