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>,
Wedson Almeida Filho <wedsonaf@gmail.com>,
Gary Guo <gary@garyguo.net>,
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 11/15] gendwarfksyms: Limit structure expansion
Date: Mon, 17 Jun 2024 17:58:30 +0000 [thread overview]
Message-ID: <20240617175818.58219-28-samitolvanen@google.com> (raw)
In-Reply-To: <20240617175818.58219-17-samitolvanen@google.com>
Expand each structure type only once per exported symbol. This
is necessary to support self-referential structures, which would
otherwise result in infinite recursion. Expanding each structure type
just once is enough to catch ABI changes.
For pointers to structure types, limit expansion to three levels
inside the pointer. This should be plenty for catching ABI differences
and stops us from pulling in half the kernel for structs that contain
pointers to large structs like task_struct.
Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
---
tools/gendwarfksyms/cache.c | 49 ++++++++++++++++
tools/gendwarfksyms/gendwarfksyms.h | 9 ++-
tools/gendwarfksyms/types.c | 87 ++++++++++++++++++++++++++---
3 files changed, 136 insertions(+), 9 deletions(-)
diff --git a/tools/gendwarfksyms/cache.c b/tools/gendwarfksyms/cache.c
index 85a2adeb649d..1d6eb27d799d 100644
--- a/tools/gendwarfksyms/cache.c
+++ b/tools/gendwarfksyms/cache.c
@@ -158,3 +158,52 @@ int cache_add_die(struct cached_die *cd, Dwarf_Die *die)
ci->type = DIE;
return 0;
}
+
+/* A list of structure types that were already expanded for the current symbol */
+struct expanded {
+ uintptr_t addr;
+ struct hlist_node hash;
+};
+
+/* die->addr -> struct expanded */
+DEFINE_HASHTABLE(expansion_cache, DIE_HASH_BITS);
+
+int cache_mark_expanded(Dwarf_Die *die)
+{
+ struct expanded *es;
+
+ es = malloc(sizeof(struct expanded));
+ if (!es) {
+ error("malloc failed");
+ return -1;
+ }
+
+ es->addr = (uintptr_t)die->addr;
+ hash_add(expansion_cache, &es->hash, es->addr);
+ return 0;
+}
+
+bool cache_was_expanded(Dwarf_Die *die)
+{
+ struct expanded *es;
+ uintptr_t addr = (uintptr_t)die->addr;
+
+ hash_for_each_possible(expansion_cache, es, hash, addr) {
+ if (es->addr == addr)
+ return true;
+ }
+
+ return false;
+}
+
+void cache_clear_expanded(void)
+{
+ struct hlist_node *tmp;
+ struct expanded *es;
+ int i;
+
+ hash_for_each_safe(expansion_cache, i, tmp, es, hash) {
+ free(es);
+ }
+ hash_init(expansion_cache);
+}
diff --git a/tools/gendwarfksyms/gendwarfksyms.h b/tools/gendwarfksyms/gendwarfksyms.h
index 4646eaf5c85e..568f3727017e 100644
--- a/tools/gendwarfksyms/gendwarfksyms.h
+++ b/tools/gendwarfksyms/gendwarfksyms.h
@@ -95,7 +95,7 @@ struct cached_item {
struct cached_item *next;
};
-enum cached_die_state { INCOMPLETE, COMPLETE };
+enum cached_die_state { INCOMPLETE, UNEXPANDED, COMPLETE };
struct cached_die {
enum cached_die_state state;
@@ -111,6 +111,10 @@ extern int cache_add_linebreak(struct cached_die *pd, int linebreak);
extern int cache_add_die(struct cached_die *pd, Dwarf_Die *die);
extern void cache_free(void);
+extern int cache_mark_expanded(Dwarf_Die *die);
+extern bool cache_was_expanded(Dwarf_Die *die);
+extern void cache_clear_expanded(void);
+
/*
* types.c
*/
@@ -120,6 +124,9 @@ struct state {
Dwarf *dbg;
struct symbol *sym;
Dwarf_Die origin;
+ unsigned int ptr_expansion_depth;
+ bool in_pointer_type;
+ bool expand;
unsigned long crc;
};
diff --git a/tools/gendwarfksyms/types.c b/tools/gendwarfksyms/types.c
index fa74e6fc26e3..da3aa2ad9f57 100644
--- a/tools/gendwarfksyms/types.c
+++ b/tools/gendwarfksyms/types.c
@@ -381,14 +381,21 @@ static int __process_structure_type(struct state *state,
check(process(state, cache, " {"));
check(process_linebreak(cache, 1));
- check(process_die_container(state, cache, die, process_func,
- match_func));
+ if (state->expand) {
+ check(cache_mark_expanded(die));
+ check(process_die_container(state, cache, die, process_func,
+ match_func));
+ } else {
+ check(process(state, cache, "<unexpanded>"));
+ }
check(process_linebreak(cache, -1));
check(process(state, cache, "}"));
- check(process_byte_size_attr(state, cache, die));
- check(process_alignment_attr(state, cache, die));
+ if (state->expand) {
+ check(process_byte_size_attr(state, cache, die));
+ check(process_alignment_attr(state, cache, die));
+ }
return 0;
}
@@ -475,9 +482,38 @@ static int process_cached(struct state *state, struct cached_die *cache,
static void state_init(struct state *state)
{
+ state->ptr_expansion_depth = 0;
+ state->in_pointer_type = false;
+ state->expand = true;
state->crc = 0xffffffff;
}
+static void state_restore(struct state *state, struct state *saved)
+{
+ state->ptr_expansion_depth = saved->ptr_expansion_depth;
+ state->in_pointer_type = saved->in_pointer_type;
+ state->expand = saved->expand;
+}
+
+static void state_save(struct state *state, struct state *saved)
+{
+ state_restore(saved, state);
+}
+
+static bool is_pointer_type(int tag)
+{
+ return tag == DW_TAG_pointer_type || tag == DW_TAG_reference_type;
+}
+
+static bool is_expanded_type(int tag)
+{
+ return tag == DW_TAG_class_type || tag == DW_TAG_structure_type ||
+ tag == DW_TAG_union_type || tag == DW_TAG_enumeration_type;
+}
+
+/* The maximum depth for expanding structures in pointers */
+#define MAX_POINTER_EXPANSION_DEPTH 3
+
#define PROCESS_TYPE(type) \
case DW_TAG_##type##_type: \
check(process_##type##_type(state, cache, die)); \
@@ -486,19 +522,52 @@ static void state_init(struct state *state)
static int process_type(struct state *state, struct cached_die *parent,
Dwarf_Die *die)
{
+ enum cached_die_state want_state = COMPLETE;
struct cached_die *cache = NULL;
+ struct state saved;
int tag = dwarf_tag(die);
+ state_save(state, &saved);
+
/*
- * If we have the DIE already cached, use it instead of walking
+ * Structures and enumeration types are expanded only once per
+ * exported symbol. This is sufficient for detecting ABI changes
+ * within the structure.
+ *
+ * If the exported symbol contains a pointer to a structure,
+ * at most MAX_POINTER_EXPANSION_DEPTH levels are expanded into
+ * the referenced structure.
+ */
+ state->in_pointer_type = saved.in_pointer_type || is_pointer_type(tag);
+
+ if (state->in_pointer_type &&
+ state->ptr_expansion_depth >= MAX_POINTER_EXPANSION_DEPTH)
+ state->expand = false;
+ else
+ state->expand = saved.expand && !cache_was_expanded(die);
+
+ /* Keep track of pointer expansion depth */
+ if (state->expand && state->in_pointer_type && is_expanded_type(tag))
+ state->ptr_expansion_depth++;
+
+ /*
+ * If we have want_state already cached, use it instead of walking
* through DWARF.
*/
if (!no_cache) {
- check(cache_get(die, COMPLETE, &cache));
+ if (!state->expand && is_expanded_type(tag))
+ want_state = UNEXPANDED;
+
+ check(cache_get(die, want_state, &cache));
+
+ if (cache->state == want_state) {
+ if (want_state == COMPLETE && is_expanded_type(tag))
+ check(cache_mark_expanded(die));
- if (cache->state == COMPLETE) {
check(process_cached(state, cache, die));
check(cache_add_die(parent, die));
+
+ state_restore(state, &saved);
return 0;
}
}
@@ -540,10 +609,11 @@ static int process_type(struct state *state, struct cached_die *parent,
if (!no_cache) {
/* Update cache state and append to the parent (if any) */
- cache->state = COMPLETE;
+ cache->state = want_state;
check(cache_add_die(parent, die));
}
+ state_restore(state, &saved);
return 0;
}
@@ -596,6 +666,7 @@ static int process_exported_symbols(struct state *state,
else
check(process_variable(state, die));
+ cache_clear_expanded();
return check(
symbol_set_crc(state->sym, state->crc ^ 0xffffffff));
default:
--
2.45.2.627.g7a2c4fd464-goog
next prev parent reply other threads:[~2024-06-17 17:58 UTC|newest]
Thread overview: 37+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-06-17 17:58 [PATCH 00/15] Implement MODVERSIONS for Rust Sami Tolvanen
2024-06-17 17:58 ` [PATCH 01/15] tools: Add gendwarfksyms Sami Tolvanen
2024-06-17 17:58 ` [PATCH 02/15] gendwarfksyms: Add symbol list input handling Sami Tolvanen
2024-06-17 17:58 ` [PATCH 03/15] gendwarfksyms: Add CRC calculation Sami Tolvanen
2024-06-17 17:58 ` [PATCH 04/15] gendwarfksyms: Expand base_type Sami Tolvanen
2024-06-17 17:58 ` [PATCH 05/15] gendwarfksyms: Add a cache Sami Tolvanen
2024-06-17 17:58 ` [PATCH 06/15] gendwarfksyms: Expand type modifiers and typedefs Sami Tolvanen
2024-06-17 17:58 ` [PATCH 07/15] gendwarfksyms: Add pretty-printing Sami Tolvanen
2024-06-17 17:58 ` [PATCH 08/15] gendwarfksyms: Expand subroutine_type Sami Tolvanen
2024-06-17 17:58 ` [PATCH 09/15] gendwarfksyms: Expand array_type Sami Tolvanen
2024-06-17 17:58 ` [PATCH 10/15] gendwarfksyms: Expand structure types Sami Tolvanen
2024-06-17 17:58 ` Sami Tolvanen [this message]
2024-06-17 17:58 ` [PATCH 12/15] gendwarfksyms: Add inline debugging Sami Tolvanen
2024-06-17 17:58 ` [PATCH 13/15] modpost: Add support for hashing long symbol names Sami Tolvanen
2024-06-18 16:47 ` Masahiro Yamada
2024-06-18 20:07 ` Sami Tolvanen
2024-06-17 17:58 ` [PATCH 14/15] module: Support hashed symbol names when checking modversions Sami Tolvanen
2024-06-17 17:58 ` [PATCH 15/15] kbuild: Use gendwarfksyms to generate Rust symbol versions Sami Tolvanen
2024-06-18 16:28 ` [PATCH 00/15] Implement MODVERSIONS for Rust Masahiro Yamada
2024-06-18 20:05 ` Sami Tolvanen
2024-06-18 16:44 ` Greg Kroah-Hartman
2024-06-18 16:50 ` Masahiro Yamada
2024-06-18 17:18 ` Greg Kroah-Hartman
2024-06-18 19:03 ` Masahiro Yamada
2024-06-18 20:19 ` Sami Tolvanen
2024-06-18 19:42 ` Luis Chamberlain
2024-06-18 21:19 ` Sami Tolvanen
2024-06-18 23:32 ` Luis Chamberlain
2024-07-10 7:30 ` Petr Pavlu
2024-07-15 20:39 ` Sami Tolvanen
2024-07-16 7:12 ` Greg Kroah-Hartman
2024-07-18 17:04 ` Sami Tolvanen
2024-07-22 8:20 ` Petr Pavlu
2024-07-26 21:05 ` Sami Tolvanen
2024-07-31 20:46 ` Neal Gompa
2024-08-01 11:22 ` Petr Pavlu
2024-08-01 19:38 ` Sami Tolvanen
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=20240617175818.58219-28-samitolvanen@google.com \
--to=samitolvanen@google.com \
--cc=alex.gaynor@gmail.com \
--cc=gary@garyguo.net \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kbuild@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-modules@vger.kernel.org \
--cc=masahiroy@kernel.org \
--cc=mcgrof@kernel.org \
--cc=mmaurer@google.com \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=wedsonaf@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).