mirror of
git://git.yoctoproject.org/linux-yocto.git
synced 2025-10-22 15:03:53 +02:00
gendwarfksyms: Add symbol versioning
Calculate symbol versions from the fully expanded type strings in type_map, and output the versions in a genksyms-compatible format. Signed-off-by: Sami Tolvanen <samitolvanen@google.com> Reviewed-by: Petr Pavlu <petr.pavlu@suse.com> Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>
This commit is contained in:
parent
ab4439981f
commit
7137888801
|
@ -8,4 +8,4 @@ gendwarfksyms-objs += dwarf.o
|
|||
gendwarfksyms-objs += symbols.o
|
||||
gendwarfksyms-objs += types.o
|
||||
|
||||
HOSTLDLIBS_gendwarfksyms := -ldw -lelf
|
||||
HOSTLDLIBS_gendwarfksyms := -ldw -lelf -lz
|
||||
|
|
|
@ -740,12 +740,33 @@ static int process_type(struct state *state, struct die *parent, Dwarf_Die *die)
|
|||
/*
|
||||
* Exported symbol processing
|
||||
*/
|
||||
static struct die *get_symbol_cache(struct state *state, Dwarf_Die *die)
|
||||
{
|
||||
struct die *cache;
|
||||
|
||||
cache = die_map_get(die, DIE_SYMBOL);
|
||||
|
||||
if (cache->state != DIE_INCOMPLETE)
|
||||
return NULL; /* We already processed a symbol for this DIE */
|
||||
|
||||
cache->tag = dwarf_tag(die);
|
||||
return cache;
|
||||
}
|
||||
|
||||
static void process_symbol(struct state *state, Dwarf_Die *die,
|
||||
die_callback_t process_func)
|
||||
{
|
||||
struct die *cache;
|
||||
|
||||
symbol_set_die(state->sym, die);
|
||||
|
||||
cache = get_symbol_cache(state, die);
|
||||
if (!cache)
|
||||
return;
|
||||
|
||||
debug("%s", state->sym->name);
|
||||
check(process_func(state, NULL, die));
|
||||
state->sym->state = SYMBOL_MAPPED;
|
||||
check(process_func(state, cache, die));
|
||||
cache->state = DIE_SYMBOL;
|
||||
if (dump_dies)
|
||||
fputs("\n", stderr);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ int dump_dies;
|
|||
int dump_die_map;
|
||||
/* Print out type strings (i.e. type_map) */
|
||||
int dump_types;
|
||||
/* Print out expanded type strings used for symbol versions */
|
||||
int dump_versions;
|
||||
/* Write a symtypes file */
|
||||
int symtypes;
|
||||
static const char *symtypes_file;
|
||||
|
@ -35,6 +37,7 @@ static void usage(void)
|
|||
" --dump-dies Dump DWARF DIE contents\n"
|
||||
" --dump-die-map Print debugging information about die_map changes\n"
|
||||
" --dump-types Dump type strings\n"
|
||||
" --dump-versions Dump expanded type strings used for symbol versions\n"
|
||||
" -T, --symtypes file Write a symtypes file\n"
|
||||
" -h, --help Print this message\n"
|
||||
"\n",
|
||||
|
@ -69,9 +72,10 @@ static int process_module(Dwfl_Module *mod, void **userdata, const char *name,
|
|||
} while (cu);
|
||||
|
||||
/*
|
||||
* Use die_map to expand type strings and write them to `symfile`.
|
||||
* Use die_map to expand type strings, write them to `symfile`, and
|
||||
* calculate symbol versions.
|
||||
*/
|
||||
generate_symtypes(symfile);
|
||||
generate_symtypes_and_versions(symfile);
|
||||
die_map_free();
|
||||
|
||||
return DWARF_CB_OK;
|
||||
|
@ -93,6 +97,7 @@ int main(int argc, char **argv)
|
|||
{ "dump-dies", 0, &dump_dies, 1 },
|
||||
{ "dump-die-map", 0, &dump_die_map, 1 },
|
||||
{ "dump-types", 0, &dump_types, 1 },
|
||||
{ "dump-versions", 0, &dump_versions, 1 },
|
||||
{ "symtypes", 1, NULL, 'T' },
|
||||
{ "help", 0, NULL, 'h' },
|
||||
{ 0, 0, NULL, 0 }
|
||||
|
@ -166,6 +171,7 @@ int main(int argc, char **argv)
|
|||
if (symfile)
|
||||
check(fclose(symfile));
|
||||
|
||||
symbol_print_versions();
|
||||
symbol_free();
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -23,6 +23,7 @@ extern int debug;
|
|||
extern int dump_dies;
|
||||
extern int dump_die_map;
|
||||
extern int dump_types;
|
||||
extern int dump_versions;
|
||||
extern int symtypes;
|
||||
|
||||
/*
|
||||
|
@ -95,6 +96,7 @@ static inline unsigned int addr_hash(uintptr_t addr)
|
|||
enum symbol_state {
|
||||
SYMBOL_UNPROCESSED,
|
||||
SYMBOL_MAPPED,
|
||||
SYMBOL_PROCESSED
|
||||
};
|
||||
|
||||
struct symbol_addr {
|
||||
|
@ -109,6 +111,7 @@ struct symbol {
|
|||
struct hlist_node name_hash;
|
||||
enum symbol_state state;
|
||||
uintptr_t die_addr;
|
||||
unsigned long crc;
|
||||
};
|
||||
|
||||
typedef void (*symbol_callback_t)(struct symbol *, void *arg);
|
||||
|
@ -116,6 +119,10 @@ typedef void (*symbol_callback_t)(struct symbol *, void *arg);
|
|||
void symbol_read_exports(FILE *file);
|
||||
void symbol_read_symtab(int fd);
|
||||
struct symbol *symbol_get(const char *name);
|
||||
void symbol_set_die(struct symbol *sym, Dwarf_Die *die);
|
||||
void symbol_set_crc(struct symbol *sym, unsigned long crc);
|
||||
void symbol_for_each(symbol_callback_t func, void *arg);
|
||||
void symbol_print_versions(void);
|
||||
void symbol_free(void);
|
||||
|
||||
/*
|
||||
|
@ -126,7 +133,8 @@ enum die_state {
|
|||
DIE_INCOMPLETE,
|
||||
DIE_UNEXPANDED,
|
||||
DIE_COMPLETE,
|
||||
DIE_LAST = DIE_COMPLETE
|
||||
DIE_SYMBOL,
|
||||
DIE_LAST = DIE_SYMBOL
|
||||
};
|
||||
|
||||
enum die_fragment_type {
|
||||
|
@ -156,6 +164,7 @@ static inline const char *die_state_name(enum die_state state)
|
|||
CASE_CONST_TO_STR(DIE_INCOMPLETE)
|
||||
CASE_CONST_TO_STR(DIE_UNEXPANDED)
|
||||
CASE_CONST_TO_STR(DIE_COMPLETE)
|
||||
CASE_CONST_TO_STR(DIE_SYMBOL)
|
||||
}
|
||||
|
||||
error("unexpected die_state: %d", state);
|
||||
|
@ -252,6 +261,6 @@ void process_cu(Dwarf_Die *cudie);
|
|||
* types.c
|
||||
*/
|
||||
|
||||
void generate_symtypes(FILE *file);
|
||||
void generate_symtypes_and_versions(FILE *file);
|
||||
|
||||
#endif /* __GENDWARFKSYMS_H */
|
||||
|
|
|
@ -66,6 +66,36 @@ static unsigned int for_each(const char *name, symbol_callback_t func,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void set_crc(struct symbol *sym, void *data)
|
||||
{
|
||||
unsigned long *crc = data;
|
||||
|
||||
if (sym->state == SYMBOL_PROCESSED && sym->crc != *crc)
|
||||
warn("overriding version for symbol %s (crc %lx vs. %lx)",
|
||||
sym->name, sym->crc, *crc);
|
||||
|
||||
sym->state = SYMBOL_PROCESSED;
|
||||
sym->crc = *crc;
|
||||
}
|
||||
|
||||
void symbol_set_crc(struct symbol *sym, unsigned long crc)
|
||||
{
|
||||
if (for_each(sym->name, set_crc, &crc) == 0)
|
||||
error("no matching symbols: '%s'", sym->name);
|
||||
}
|
||||
|
||||
static void set_die(struct symbol *sym, void *data)
|
||||
{
|
||||
sym->die_addr = (uintptr_t)((Dwarf_Die *)data)->addr;
|
||||
sym->state = SYMBOL_MAPPED;
|
||||
}
|
||||
|
||||
void symbol_set_die(struct symbol *sym, Dwarf_Die *die)
|
||||
{
|
||||
if (for_each(sym->name, set_die, die) == 0)
|
||||
error("no matching symbols: '%s'", sym->name);
|
||||
}
|
||||
|
||||
static bool is_exported(const char *name)
|
||||
{
|
||||
return for_each(name, NULL, NULL) > 0;
|
||||
|
@ -120,6 +150,16 @@ struct symbol *symbol_get(const char *name)
|
|||
return sym;
|
||||
}
|
||||
|
||||
void symbol_for_each(symbol_callback_t func, void *arg)
|
||||
{
|
||||
struct hlist_node *tmp;
|
||||
struct symbol *sym;
|
||||
|
||||
hash_for_each_safe(symbol_names, sym, tmp, name_hash) {
|
||||
func(sym, arg);
|
||||
}
|
||||
}
|
||||
|
||||
typedef void (*elf_symbol_callback_t)(const char *name, GElf_Sym *sym,
|
||||
Elf32_Word xndx, void *arg);
|
||||
|
||||
|
@ -246,6 +286,19 @@ void symbol_read_symtab(int fd)
|
|||
elf_for_each_global(fd, elf_set_symbol_addr, NULL);
|
||||
}
|
||||
|
||||
void symbol_print_versions(void)
|
||||
{
|
||||
struct hlist_node *tmp;
|
||||
struct symbol *sym;
|
||||
|
||||
hash_for_each_safe(symbol_names, sym, tmp, name_hash) {
|
||||
if (sym->state != SYMBOL_PROCESSED)
|
||||
warn("no information for symbol %s", sym->name);
|
||||
|
||||
printf("#SYMVER %s 0x%08lx\n", sym->name, sym->crc);
|
||||
}
|
||||
}
|
||||
|
||||
void symbol_free(void)
|
||||
{
|
||||
struct hlist_node *tmp;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#define _GNU_SOURCE
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "gendwarfksyms.h"
|
||||
|
||||
|
@ -178,6 +179,33 @@ static void type_map_free(void)
|
|||
hash_init(type_map);
|
||||
}
|
||||
|
||||
/*
|
||||
* CRC for a type, with an optional fully expanded type string for
|
||||
* debugging.
|
||||
*/
|
||||
struct version {
|
||||
struct type_expansion type;
|
||||
unsigned long crc;
|
||||
};
|
||||
|
||||
static void version_init(struct version *version)
|
||||
{
|
||||
version->crc = crc32(0, NULL, 0);
|
||||
type_expansion_init(&version->type);
|
||||
}
|
||||
|
||||
static void version_free(struct version *version)
|
||||
{
|
||||
type_expansion_free(&version->type);
|
||||
}
|
||||
|
||||
static void version_add(struct version *version, const char *s)
|
||||
{
|
||||
version->crc = crc32(version->crc, (void *)s, strlen(s));
|
||||
if (dump_versions)
|
||||
type_expansion_append(&version->type, s, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Type reference format: <prefix>#<name>, where prefix:
|
||||
* s -> structure
|
||||
|
@ -187,6 +215,12 @@ static void type_map_free(void)
|
|||
*
|
||||
* Names with spaces are additionally wrapped in single quotes.
|
||||
*/
|
||||
static inline bool is_type_prefix(const char *s)
|
||||
{
|
||||
return (s[0] == 's' || s[0] == 'u' || s[0] == 'e' || s[0] == 't') &&
|
||||
s[1] == '#';
|
||||
}
|
||||
|
||||
static char get_type_prefix(int tag)
|
||||
{
|
||||
switch (tag) {
|
||||
|
@ -214,6 +248,8 @@ static char *get_type_name(struct die *cache)
|
|||
warn("found incomplete cache entry: %p", cache);
|
||||
return NULL;
|
||||
}
|
||||
if (cache->state == DIE_SYMBOL)
|
||||
return NULL;
|
||||
if (!cache->fqn || !*cache->fqn)
|
||||
return NULL;
|
||||
|
||||
|
@ -231,6 +267,39 @@ static char *get_type_name(struct die *cache)
|
|||
return name;
|
||||
}
|
||||
|
||||
static void __calculate_version(struct version *version, struct list_head *list)
|
||||
{
|
||||
struct type_list_entry *entry;
|
||||
struct type_expansion *e;
|
||||
|
||||
/* Calculate a CRC over an expanded type string */
|
||||
list_for_each_entry(entry, list, list) {
|
||||
if (is_type_prefix(entry->str)) {
|
||||
check(type_map_get(entry->str, &e));
|
||||
|
||||
/*
|
||||
* It's sufficient to expand each type reference just
|
||||
* once to detect changes.
|
||||
*/
|
||||
if (cache_was_expanded(&expansion_cache, e)) {
|
||||
version_add(version, entry->str);
|
||||
} else {
|
||||
cache_mark_expanded(&expansion_cache, e);
|
||||
__calculate_version(version, &e->expanded);
|
||||
}
|
||||
} else {
|
||||
version_add(version, entry->str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void calculate_version(struct version *version, struct list_head *list)
|
||||
{
|
||||
version_init(version);
|
||||
__calculate_version(version, list);
|
||||
cache_free(&expansion_cache);
|
||||
}
|
||||
|
||||
static void __type_expand(struct die *cache, struct type_expansion *type,
|
||||
bool recursive);
|
||||
|
||||
|
@ -337,7 +406,49 @@ static void expand_type(struct die *cache, void *arg)
|
|||
free(name);
|
||||
}
|
||||
|
||||
void generate_symtypes(FILE *file)
|
||||
static void expand_symbol(struct symbol *sym, void *arg)
|
||||
{
|
||||
struct type_expansion type;
|
||||
struct version version;
|
||||
struct die *cache;
|
||||
|
||||
/*
|
||||
* No need to expand again unless we want a symtypes file entry
|
||||
* for the symbol. Note that this means `sym` has the same address
|
||||
* as another symbol that was already processed.
|
||||
*/
|
||||
if (!symtypes && sym->state == SYMBOL_PROCESSED)
|
||||
return;
|
||||
|
||||
if (__die_map_get(sym->die_addr, DIE_SYMBOL, &cache))
|
||||
return; /* We'll warn about missing CRCs later. */
|
||||
|
||||
type_expand(cache, &type, false);
|
||||
|
||||
/* If the symbol already has a version, don't calculate it again. */
|
||||
if (sym->state != SYMBOL_PROCESSED) {
|
||||
calculate_version(&version, &type.expanded);
|
||||
symbol_set_crc(sym, version.crc);
|
||||
debug("%s = %lx", sym->name, version.crc);
|
||||
|
||||
if (dump_versions) {
|
||||
checkp(fputs(sym->name, stderr));
|
||||
checkp(fputs(" ", stderr));
|
||||
type_list_write(&version.type.expanded, stderr);
|
||||
checkp(fputs("\n", stderr));
|
||||
}
|
||||
|
||||
version_free(&version);
|
||||
}
|
||||
|
||||
/* These aren't needed in type_map unless we want a symtypes file. */
|
||||
if (symtypes)
|
||||
type_map_add(sym->name, &type);
|
||||
|
||||
type_expansion_free(&type);
|
||||
}
|
||||
|
||||
void generate_symtypes_and_versions(FILE *file)
|
||||
{
|
||||
cache_init(&expansion_cache);
|
||||
|
||||
|
@ -355,7 +466,14 @@ void generate_symtypes(FILE *file)
|
|||
die_map_for_each(expand_type, NULL);
|
||||
|
||||
/*
|
||||
* 2. If a symtypes file is requested, write type_map contents to
|
||||
* 2. For each exported symbol, expand the die_map type, and use
|
||||
* type_map expansions to calculate a symbol version from the
|
||||
* fully expanded type string.
|
||||
*/
|
||||
symbol_for_each(expand_symbol, NULL);
|
||||
|
||||
/*
|
||||
* 3. If a symtypes file is requested, write type_map contents to
|
||||
* the file.
|
||||
*/
|
||||
type_map_write(file);
|
||||
|
|
Loading…
Reference in New Issue
Block a user