Re: [PATCH v4 13/19] gendwarfksyms: Add symbol versioning

Petr Pavlu
2024-10-22T11:48:17Z

On 10/8/24 20:38, Sami Tolvanen wrote:
> 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>
> Acked-by: Neal Gompa <neal@gompa.dev>
> ---
> scripts/gendwarfksyms/dwarf.c | 25 +++++-
> scripts/gendwarfksyms/gendwarfksyms.c | 11 ++-
> scripts/gendwarfksyms/gendwarfksyms.h | 13 ++-
> scripts/gendwarfksyms/symbols.c | 59 +++++++++++++
> scripts/gendwarfksyms/types.c | 122 +++++++++++++++++++++++++-
> 5 files changed, 222 insertions(+), 8 deletions(-)
>
> diff --git a/scripts/gendwarfksyms/dwarf.c b/scripts/gendwarfksyms/dwarf.c
> index e1a9e9061b1d..a47a3a0f7a69 100644
> --- a/scripts/gendwarfksyms/dwarf.c
> +++ b/scripts/gendwarfksyms/dwarf.c
> @@ -723,12 +723,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);
> }
> diff --git a/scripts/gendwarfksyms/gendwarfksyms.c b/scripts/gendwarfksyms/gendwarfksyms.c
> index 24c87523fc3a..e90d909d259b 100644
> --- a/scripts/gendwarfksyms/gendwarfksyms.c
> +++ b/scripts/gendwarfksyms/gendwarfksyms.c
> @@ -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;
> @@ -92,6 +96,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 } };
> @@ -167,5 +172,7 @@ int main(int argc, char **argv)
> if (symfile)
> check(fclose(symfile));
>
> + symbol_print_versions();
> +
> return 0;
> }
> diff --git a/scripts/gendwarfksyms/gendwarfksyms.h b/scripts/gendwarfksyms/gendwarfksyms.h
> index e47b5e967520..814f53ef799e 100644
> --- a/scripts/gendwarfksyms/gendwarfksyms.h
> +++ b/scripts/gendwarfksyms/gendwarfksyms.h
> @@ -26,6 +26,7 @@ extern int debug;
> extern int dump_dies;
> extern int dump_die_map;
> extern int dump_types;
> +extern int dump_versions;
> extern int symtypes;
>
> /*
> @@ -98,6 +99,7 @@ static inline unsigned int addr_hash(uintptr_t addr)
> enum symbol_state {
> SYMBOL_UNPROCESSED,
> SYMBOL_MAPPED,
> + SYMBOL_PROCESSED
> };
>
> struct symbol_addr {
> @@ -112,6 +114,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);
> @@ -119,6 +122,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);
>
> /*
> * die.c
> @@ -128,7 +135,8 @@ enum die_state {
> DIE_INCOMPLETE,
> DIE_UNEXPANDED,
> DIE_COMPLETE,
> - DIE_LAST = DIE_COMPLETE
> + DIE_SYMBOL,
> + DIE_LAST = DIE_SYMBOL
> };
>
> enum die_fragment_type {
> @@ -158,6 +166,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);
> @@ -245,6 +254,6 @@ void process_cu(Dwarf_Die *cudie);
> * types.c
> */
>
> -void generate_symtypes(FILE *file);
> +void generate_symtypes_and_versions(FILE *file);
>
> #endif /* __GENDWARFKSYMS_H */
> diff --git a/scripts/gendwarfksyms/symbols.c b/scripts/gendwarfksyms/symbols.c
> index e414257333e5..e17f11a02f6e 100644
> --- a/scripts/gendwarfksyms/symbols.c
> +++ b/scripts/gendwarfksyms/symbols.c
> @@ -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);
>
> @@ -231,3 +271,22 @@ 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);
> +
> + free((void *)sym->name);
> + free(sym);
> + }
> +
> + hash_init(symbol_addrs);
> + hash_init(symbol_names);
> +}

I had some minor comment about adjusting the name of function
symbol_print_versions() and possibly changing sym->name to 'char *' on
the v2 of the patch:
https://lore.kernel.org/all/286b1cc5-1757-4f0a-bb66-0875f4608c7e@suse.com/
Please have a look, it seems it felt through the cracks.

In any case, the patch looks ok to me, feel free to add:
Reviewed-by: Petr Pavlu <petr.pavlu@suse.com>

--
Thanks,
Petr