From: Sami Tolvanen <samitolvanen@google.com>
To: Masahiro Yamada <masahiroy@kernel.org>,
Luis Chamberlain <mcgrof@kernel.org>,
Miguel Ojeda <ojeda@kernel.org>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Matthew Maurer <mmaurer@google.com>,
Alex Gaynor <alex.gaynor@gmail.com>, Gary Guo <gary@garyguo.net>,
Petr Pavlu <petr.pavlu@suse.com>,
Daniel Gomez <da.gomez@samsung.com>, Neal Gompa <neal@gompa.dev>,
Hector Martin <marcan@marcan.st>, Janne Grunau <j@jannau.net>,
Miroslav Benes <mbenes@suse.cz>,
Asahi Linux <asahi@lists.linux.dev>,
Sedat Dilek <sedat.dilek@gmail.com>,
linux-kbuild@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-modules@vger.kernel.org, rust-for-linux@vger.kernel.org,
Sami Tolvanen <samitolvanen@google.com>
Subject: [PATCH v5 13/19] gendwarfksyms: Add symbol versioning
Date: Wed, 30 Oct 2024 17:01:20 +0000 [thread overview]
Message-ID: <20241030170106.1501763-34-samitolvanen@google.com> (raw)
In-Reply-To: <20241030170106.1501763-21-samitolvanen@google.com>
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 | 10 ++-
scripts/gendwarfksyms/gendwarfksyms.h | 13 ++-
scripts/gendwarfksyms/symbols.c | 53 +++++++++++
scripts/gendwarfksyms/types.c | 122 +++++++++++++++++++++++++-
5 files changed, 215 insertions(+), 8 deletions(-)
diff --git a/scripts/gendwarfksyms/dwarf.c b/scripts/gendwarfksyms/dwarf.c
index 3d2c1aa24960..1f63f28add4b 100644
--- a/scripts/gendwarfksyms/dwarf.c
+++ b/scripts/gendwarfksyms/dwarf.c
@@ -725,12 +725,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 76a38b733ad2..fd2429ea198f 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 } };
@@ -164,6 +169,7 @@ int main(int argc, char **argv)
if (symfile)
check(fclose(symfile));
+ symbol_print_versions();
symbol_free();
return 0;
diff --git a/scripts/gendwarfksyms/gendwarfksyms.h b/scripts/gendwarfksyms/gendwarfksyms.h
index 835ebcea6e97..88a9c1c5db20 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);
void symbol_free(void);
/*
@@ -129,7 +136,8 @@ enum die_state {
DIE_INCOMPLETE,
DIE_UNEXPANDED,
DIE_COMPLETE,
- DIE_LAST = DIE_COMPLETE
+ DIE_SYMBOL,
+ DIE_LAST = DIE_SYMBOL
};
enum die_fragment_type {
@@ -159,6 +167,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);
@@ -246,6 +255,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 6ed0c4769e6f..e0c9007f7250 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);
@@ -244,6 +284,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;
diff --git a/scripts/gendwarfksyms/types.c b/scripts/gendwarfksyms/types.c
index c05811299eba..08886063363c 100644
--- a/scripts/gendwarfksyms/types.c
+++ b/scripts/gendwarfksyms/types.c
@@ -3,6 +3,7 @@
* Copyright (C) 2024 Google LLC
*/
+#include <crc32.h>
#include "gendwarfksyms.h"
static struct expansion_cache expansion_cache;
@@ -174,6 +175,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 = 0xffffffff;
+ 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 = partial_crc32(s, version->crc);
+ if (dump_versions)
+ type_expansion_append(&version->type, s, NULL);
+}
+
/*
* Type reference format: <prefix>#<name>, where prefix:
* s -> structure
@@ -183,6 +211,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) {
@@ -210,6 +244,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;
@@ -227,6 +263,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_clear_expanded(&expansion_cache);
+}
+
static void __type_expand(struct die *cache, struct type_expansion *type,
bool recursive);
@@ -333,7 +402,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)
{
hash_init(expansion_cache.cache);
@@ -351,7 +462,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);
--
2.47.0.163.g1226f6d8fa-goog
next prev parent reply other threads:[~2024-10-30 17:01 UTC|newest]
Thread overview: 36+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-10-30 17:01 [PATCH v5 00/19] Implement DWARF modversions Sami Tolvanen
2024-10-30 17:01 ` [PATCH v5 01/19] scripts: move genksyms crc32 implementation to a common include Sami Tolvanen
2024-11-12 4:05 ` Masahiro Yamada
2024-11-13 14:04 ` Neal Gompa
2024-11-13 19:35 ` Luis Chamberlain
2024-11-14 1:08 ` Neal Gompa
2024-11-13 17:53 ` Sami Tolvanen
2024-11-16 9:08 ` Masahiro Yamada
2024-11-18 21:58 ` Sami Tolvanen
2024-11-19 20:48 ` Darrick J. Wong
2024-11-19 21:37 ` Sami Tolvanen
2024-10-30 17:01 ` [PATCH v5 02/19] tools: Add gendwarfksyms Sami Tolvanen
2024-10-30 17:01 ` [PATCH v5 03/19] gendwarfksyms: Add address matching Sami Tolvanen
2024-10-30 17:01 ` [PATCH v5 04/19] gendwarfksyms: Expand base_type Sami Tolvanen
2024-10-30 17:01 ` [PATCH v5 05/19] gendwarfksyms: Add a cache for processed DIEs Sami Tolvanen
2024-10-30 17:01 ` [PATCH v5 06/19] gendwarfksyms: Expand type modifiers and typedefs Sami Tolvanen
2024-10-30 17:01 ` [PATCH v5 07/19] gendwarfksyms: Expand subroutine_type Sami Tolvanen
2024-10-30 17:01 ` [PATCH v5 08/19] gendwarfksyms: Expand array_type Sami Tolvanen
2024-10-30 17:01 ` [PATCH v5 09/19] gendwarfksyms: Expand structure types Sami Tolvanen
2024-10-30 17:01 ` [PATCH v5 10/19] gendwarfksyms: Limit structure expansion Sami Tolvanen
2024-11-20 21:54 ` Sami Tolvanen
2024-10-30 17:01 ` [PATCH v5 11/19] gendwarfksyms: Add die_map debugging Sami Tolvanen
2024-10-30 17:01 ` [PATCH v5 12/19] gendwarfksyms: Add symtypes output Sami Tolvanen
2024-10-30 17:01 ` Sami Tolvanen [this message]
2024-10-30 17:01 ` [PATCH v5 14/19] gendwarfksyms: Add support for kABI rules Sami Tolvanen
2024-10-30 17:01 ` [PATCH v5 15/19] gendwarfksyms: Add support for reserved and ignored fields Sami Tolvanen
2024-10-30 17:01 ` [PATCH v5 16/19] gendwarfksyms: Add support for symbol type pointers Sami Tolvanen
2024-10-30 17:01 ` [PATCH v5 17/19] export: Add __gendwarfksyms_ptr_ references to exported symbols Sami Tolvanen
2024-10-30 17:01 ` [PATCH v5 18/19] kbuild: Add gendwarfksyms as an alternative to genksyms Sami Tolvanen
2024-11-12 4:08 ` Masahiro Yamada
2024-11-13 17:48 ` Sami Tolvanen
2024-10-30 17:01 ` [PATCH v5 19/19] Documentation/kbuild: Add DWARF module versioning Sami Tolvanen
2024-10-30 20:59 ` [PATCH v5 00/19] Implement DWARF modversions Sedat Dilek
2024-10-30 21:14 ` Sami Tolvanen
2024-10-31 1:56 ` Sedat Dilek
2024-10-31 6:18 ` Sedat Dilek
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20241030170106.1501763-34-samitolvanen@google.com \
--to=samitolvanen@google.com \
--cc=alex.gaynor@gmail.com \
--cc=asahi@lists.linux.dev \
--cc=da.gomez@samsung.com \
--cc=gary@garyguo.net \
--cc=gregkh@linuxfoundation.org \
--cc=j@jannau.net \
--cc=linux-kbuild@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-modules@vger.kernel.org \
--cc=marcan@marcan.st \
--cc=masahiroy@kernel.org \
--cc=mbenes@suse.cz \
--cc=mcgrof@kernel.org \
--cc=mmaurer@google.com \
--cc=neal@gompa.dev \
--cc=ojeda@kernel.org \
--cc=petr.pavlu@suse.com \
--cc=rust-for-linux@vger.kernel.org \
--cc=sedat.dilek@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).