[PATCH dwarves 1/2] btf_encoder: Only skip optimized parms when ABI changed
Alan Maguire <[email protected]> Tue, 14 Jul 2026 14:50:34 +0100
| Newsgroups | org.kernel.vger.dwarves,org.kernel.vger.bpf |
|---|---|
| Message-ID | <[email protected]> |
The true-signature series made optimized-parameter state more visible to the BTF encoder. Skipping every function with optimized parameters is too broad, though: an unused parameter can still be part of the real calling ABI, so default BTF should continue to encode that function. Limit the optimized-parameter skip to cases where default BTF cannot represent the concrete ABI safely: - clang functions marked as signature-changed via DW_CC_nocall - GCC optimized clones with ELF suffixes such as .isra or .constprop When multiple saved states share the same base ELF function, prefer a canonical state that does not require this optimized-parameter skip, so a usable concrete instance is not dropped just because another optimized clone exists. Signed-off-by: Alan Maguire <[email protected]> --- btf_encoder.c | 97 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 73 insertions(+), 24 deletions(-) diff --git a/btf_encoder.c b/btf_encoder.c index 38455a4..2e6eab0 100644 --- a/btf_encoder.c +++ b/btf_encoder.c @@ -95,6 +95,8 @@ struct btf_encoder_func_state { uint8_t inconsistent_proto:1; uint8_t uncertain_parm_loc:1; uint8_t reordered_parm:1; + uint8_t signature_changed:1; + uint8_t optimized_symbol:1; uint8_t ambiguous_addr:1; int ret_type_id; struct btf_encoder_func_parm *parms; @@ -1209,27 +1211,44 @@ static struct btf_encoder_func_state *btf_encoder__alloc_func_state(struct btf_e return state; } -/* some "." suffixes do not correspond to real functions; - * - .part for partial inline - * - .cold for rarely-used codepath extracted for better code locality - */ -static bool str_contains_non_fn_suffix(const char *str) { - static const char *skip[] = { - ".cold", - ".part" - }; +static bool str_contains_suffix(const char *str, const char * const *suffixes, int nr_suffixes) +{ const char *suffix = strchr(str, '.'); int i; if (!suffix) return false; - for (i = 0; i < ARRAY_SIZE(skip); i++) { - if (strstr(suffix, skip[i])) + for (i = 0; i < nr_suffixes; i++) { + if (strstr(suffix, suffixes[i])) return true; } return false; } +/* some "." suffixes do not correspond to real functions; + * - .part for partial inline + * - .cold for rarely-used codepath extracted for better code locality + */ +static bool str_contains_non_fn_suffix(const char *str) +{ + static const char * const skip[] = { + ".cold", + ".part" + }; + + return str_contains_suffix(str, skip, ARRAY_SIZE(skip)); +} + +static bool str_contains_optimized_fn_suffix(const char *str) +{ + static const char * const suffixes[] = { + ".constprop", + ".isra", + }; + + return str_contains_suffix(str, suffixes, ARRAY_SIZE(suffixes)); +} + static bool elf_function__has_ambiguous_address(struct elf_function *func) { struct elf_function_sym *sym; @@ -1295,6 +1314,17 @@ static int32_t btf_encoder__save_func(struct btf_encoder *encoder, struct functi state->optimized_parms = ftype->optimized_parms; state->uncertain_parm_loc = ftype->uncertain_parm_loc; state->reordered_parm = ftype->reordered_parm; + state->signature_changed = ftype->signature_changed; + if (state->addr) { + for (int i = 0; i < func->sym_cnt; i++) { + if (state->addr != func->syms[i].addr) + continue; + if (str_contains_optimized_fn_suffix(func->syms[i].name)) { + state->optimized_symbol = 1; + break; + } + } + } ftype__for_each_parameter(ftype, param) { const char *name; char *final_name = NULL; @@ -1497,19 +1527,17 @@ static int saved_functions_combine(struct btf_encoder *encoder, struct btf_encoder_func_state *a, struct btf_encoder_func_state *b) { - uint8_t optimized, unexpected, inconsistent, uncertain_parm_loc, reordered_parm; + uint8_t unexpected, inconsistent, uncertain_parm_loc, reordered_parm; if (a->elf != b->elf) return 1; - optimized = a->optimized_parms | b->optimized_parms; unexpected = a->unexpected_reg | b->unexpected_reg; inconsistent = a->inconsistent_proto | b->inconsistent_proto; uncertain_parm_loc = a->uncertain_parm_loc | b->uncertain_parm_loc; reordered_parm = a->reordered_parm | b->reordered_parm; if (!unexpected && !inconsistent && !reordered_parm && !funcs__match(encoder, a, b)) inconsistent = 1; - a->optimized_parms = b->optimized_parms = optimized; a->unexpected_reg = b->unexpected_reg = unexpected; a->inconsistent_proto = b->inconsistent_proto = inconsistent; a->uncertain_parm_loc = b->uncertain_parm_loc = uncertain_parm_loc; @@ -1574,7 +1602,16 @@ static int btf_encoder__add_true_signature(struct btf_encoder *encoder, return 0; } -static struct btf_encoder_func_state *btf_encoder__select_canonical_state(struct btf_encoder_func_state *combined_states, +static bool btf_encoder_func_state__skip_optimized_parms(struct btf_encoder *encoder, + struct btf_encoder_func_state *state) +{ + return !encoder->true_signature && + state->optimized_parms && + (state->signature_changed || state->optimized_symbol); +} + +static struct btf_encoder_func_state *btf_encoder__select_canonical_state(struct btf_encoder *encoder, + struct btf_encoder_func_state *combined_states, int combined_cnt) { int i, j; @@ -1584,17 +1621,22 @@ static struct btf_encoder_func_state *btf_encoder__select_canonical_state(struct * as per saved_functions_combine(). */ struct elf_function *elf = combined_states[0].elf; + struct btf_encoder_func_state *first = NULL; for (i = 0; i < combined_cnt; i++) { struct btf_encoder_func_state *state = &combined_states[i]; for (j = 0; j < elf->sym_cnt; j++) { - if (state->addr == elf->syms[j].addr) + if (state->addr != elf->syms[j].addr) + continue; + if (!first) + first = state; + if (!btf_encoder_func_state__skip_optimized_parms(encoder, state)) return state; } } - return &combined_states[0]; + return first ?: &combined_states[0]; } static int btf_encoder__add_saved_funcs(struct btf_encoder *encoder, bool skip_encoding_inconsistent_proto) @@ -1613,6 +1655,7 @@ static int btf_encoder__add_saved_funcs(struct btf_encoder *encoder, bool skip_e for (i = 0; i < nr_saved_fns; i = j) { struct btf_encoder_func_state *state = &saved_fns[i]; + struct btf_encoder_func_state *canonical_state; char *skip_reason = NULL; /* Compare across sorted functions that match by name/prefix; @@ -1640,11 +1683,16 @@ static int btf_encoder__add_saved_funcs(struct btf_encoder *encoder, bool skip_e } } - /* do not exclude functions with optimized-out parameters; they - * may still be _called_ with the right parameter values, they - * just do not _use_ them. Only exclude functions with - * unexpected register use, multiple inconsistent prototypes or - * uncertain parameters location + canonical_state = j - i > 1 ? + btf_encoder__select_canonical_state(encoder, state, j - i) : state; + + /* Do not exclude functions with optimized-out parameters by + * default; they may still be called with the right parameter + * values, and just not use them. Exclude optimized parameters + * only when default BTF cannot represent a changed ABI + * signature; true_signature can rewrite those signatures. + * Also exclude functions with unexpected register use, multiple + * inconsistent prototypes or uncertain parameter locations. */ if (state->unexpected_reg) skip_reason = "unexpected register usage for parameter\n"; @@ -1654,6 +1702,8 @@ static int btf_encoder__add_saved_funcs(struct btf_encoder *encoder, bool skip_e skip_reason = "uncertain parameter location\n"; if (state->reordered_parm) skip_reason = "reordered parameters\n"; + if (btf_encoder_func_state__skip_optimized_parms(encoder, canonical_state)) + skip_reason = "optimized parameters\n"; if (state->elf->ambiguous_addr) skip_reason = "ambiguous address\n"; @@ -1669,8 +1719,7 @@ static int btf_encoder__add_saved_funcs(struct btf_encoder *encoder, bool skip_e * select and emit BTF for the most canonical * function definition. */ - if (j - i > 1) - state = btf_encoder__select_canonical_state(state, j - i); + state = canonical_state; if (is_kfunc_state(state)) err = btf_encoder__add_bpf_kfunc(encoder, state); else -- 2.43.5