[PATCH dwarves v9 5/6] btf_encoder: Avoid comparing inlined and out-of-line function prototypes

Yonghong Song <[email protected]> Tue, 23 Jun 2026 22:26:19 -0700
Newsgroups org.kernel.vger.dwarves,org.kernel.vger.bpf
Message-ID <[email protected]>
When true_signature is enabled, the same function can appear as two kinds
of saved states: an inlined (abstract) instance that carries the original
source signature with no address, and an out-of-line instance that carrie=
s
the reconstructed true signature. These two legitimately differ, so
comparing them in saved_functions_combine() wrongly flags the function as
having an inconsistent prototype and drops it from BTF.

Record which kind a state is by adding an 'inlined' bit to
btf_encoder_func_state, set from function__inlined() in
btf_encoder__save_func().

Use it in two places:
 - saved_functions_cmp() breaks ties by 'inlined' so that out-of-line
   (real) instances sort before inlined (abstract) ones. Since
   saved_functions_combine() compares every state in a group against the
   first (anchor) state, keeping a real instance as the anchor ensures re=
al
   instances are still compared against each other for prototype
   consistency.
 - saved_functions_combine() only runs the prototype-consistency check wh=
en
   both states are of the same kind (a->inlined =3D=3D b->inlined). The t=
rue
   signature always comes from the out-of-line instance, which is the one
   picked by btf_encoder__select_canonical_state().

Signed-off-by: Yonghong Song <[email protected]>
---
 btf_encoder.c | 27 +++++++++++++++++++++++++--
 1 file changed, 25 insertions(+), 2 deletions(-)

diff --git a/btf_encoder.c b/btf_encoder.c
index 38455a4..81bfda3 100644
--- a/btf_encoder.c
+++ b/btf_encoder.c
@@ -96,6 +96,7 @@ struct btf_encoder_func_state {
 	uint8_t uncertain_parm_loc:1;
 	uint8_t reordered_parm:1;
 	uint8_t ambiguous_addr:1;
+	uint8_t inlined:1;
 	int ret_type_id;
 	struct btf_encoder_func_parm *parms;
 	struct btf_encoder_func_annot *annots;
@@ -1295,6 +1296,7 @@ static int32_t btf_encoder__save_func(struct btf_en=
coder *encoder, struct functi
 	state->optimized_parms =3D ftype->optimized_parms;
 	state->uncertain_parm_loc =3D ftype->uncertain_parm_loc;
 	state->reordered_parm =3D ftype->reordered_parm;
+	state->inlined =3D function__inlined(fn);
 	ftype__for_each_parameter(ftype, param) {
 		const char *name;
 		char *final_name =3D NULL;
@@ -1489,8 +1491,19 @@ static int saved_functions_cmp(const void *_a, con=
st void *_b)
 {
 	const struct btf_encoder_func_state *a =3D _a;
 	const struct btf_encoder_func_state *b =3D _b;
+	int ret;
=20
-	return elf_function__name_cmp(a->elf, b->elf);
+	ret =3D elf_function__name_cmp(a->elf, b->elf);
+	if (ret)
+		return ret;
+
+	/* For the same function, sort the out-of-line (real) instances before
+	 * the inlined (abstract) ones. saved_functions_combine() compares ever=
y
+	 * state in a group against the first (anchor) one, so keeping a real
+	 * instance as the anchor ensures real instances are still compared
+	 * against each other for prototype consistency.
+	 */
+	return a->inlined - b->inlined;
 }
=20
 static int saved_functions_combine(struct btf_encoder *encoder,
@@ -1507,7 +1520,17 @@ static int saved_functions_combine(struct btf_enco=
der *encoder,
 	inconsistent =3D a->inconsistent_proto | b->inconsistent_proto;
 	uncertain_parm_loc =3D a->uncertain_parm_loc | b->uncertain_parm_loc;
 	reordered_parm =3D a->reordered_parm | b->reordered_parm;
-	if (!unexpected && !inconsistent && !reordered_parm && !funcs__match(en=
coder, a, b))
+
+	/* Only compare prototypes that are of the same kind. An inlined
+	 * (abstract) instance carries the original signature while the
+	 * out-of-line instance carries the true signature, so the two will
+	 * legitimately differ; comparing them would wrongly flag the function
+	 * as having an inconsistent prototype and drop it from BTF. The true
+	 * signature always comes from the out-of-line (real) instance, which i=
s
+	 * the one selected by btf_encoder__select_canonical_state().
+	 */
+	if (a->inlined =3D=3D b->inlined &&
+	    !unexpected && !inconsistent && !reordered_parm && !funcs__match(en=
coder, a, b))
 		inconsistent =3D 1;
 	a->optimized_parms =3D b->optimized_parms =3D optimized;
 	a->unexpected_reg =3D b->unexpected_reg =3D unexpected;
--=20
2.53.0-Meta