From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from 69-171-232-181.mail-mxout.facebook.com (69-171-232-181.mail-mxout.facebook.com [69.171.232.181]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6B623339853 for ; Fri, 20 Mar 2026 19:09:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=69.171.232.181 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774033782; cv=none; b=EdhSiDVIiCf6vIeLarlzZ5T2O+X9FT7+CQSEwzYTJ4AU/ZRz23iv4I1wLMTbdxR9SvD8VUmaSqaxOaGYSES5nwfGAHgPb0TshGsnxGqDQfuUI34XzaGeQAVGWAg6liq8N4Nef5iRUhww79yrSvJyjuvgszS3uTpmoGJnFI6wyjA= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774033782; c=relaxed/simple; bh=bWm+vprp48+NWOxwmcAS46rvvqDhNU4B+Pop4mbMtyc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=YSlHxpVNJRbIWzszLzRajSWKLPB/cDdQUW1Nk5qzYgU9rE7rMssCNi7dIrPYaaDF4bZ3DieushTL8c86s/mdI0iWuIheJYiH6imyZ+47d6uqYbo6UAJHzsTJr8W/kQyRcynH/9rKmUIN+f+URubF+WrZe782iv2P8rSPBNfCMpI= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=linux.dev; spf=fail smtp.mailfrom=linux.dev; arc=none smtp.client-ip=69.171.232.181 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=linux.dev Received: by devvm16039.vll0.facebook.com (Postfix, from userid 128203) id 9E1132A7EACCF; Fri, 20 Mar 2026 12:09:22 -0700 (PDT) From: Yonghong Song To: Alan Maguire , Arnaldo Carvalho de Melo , dwarves@vger.kernel.org Cc: Alexei Starovoitov , Andrii Nakryiko , bpf@vger.kernel.org, kernel-team@fb.com Subject: [PATCH dwarves v3 1/9] dwarf_loader: Reduce parameter checking with clang DW_AT_calling_convention attr Date: Fri, 20 Mar 2026 12:09:22 -0700 Message-ID: <20260320190922.1970709-1-yonghong.song@linux.dev> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260320190917.1970524-1-yonghong.song@linux.dev> References: <20260320190917.1970524-1-yonghong.song@linux.dev> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Currently every function is checked for its parameters to identify whethe= r the signature changed or not. If signature indeed changed, pahole may do some adjustment for parameters for true signatures. In clang, any function with the following attribute DW_AT_calling_convention (DW_CC_nocall) indicates this function having signature changed. pahole can take advantage of this to avoid parameter checking if DW_AT_calling_convention is not DW_CC_nocall. But more importantly, DW_CC_nocall can identify signature-changed functio= ns and parameters can be checked one-after-another to create the true signatures. Otherwise, it takes more effort to identify whether a function has signature changed or not. For example, for funciton __bpf_kfunc static void bbr_main(struct sock *sk, u32 ack, int flag, const struct rate_sample *rs) { ... } and bbr_main() is a callback function in .cong_control =3D bbr_main in 'struct tcp_congestion_ops tcp_bbr_cong_ops'. In the above bbr_main(...), parameter 'ack' and 'flag' are not used. The following are some details: 0x0a713b8d: DW_TAG_formal_parameter DW_AT_location (indexed (0x28) loclist =3D 0x016= 6d452: [0xffffffff83e77fd9, 0xffffffff83e78016): DW_OP_reg5= RDI ... DW_AT_name ("sk") DW_AT_type (0x0a6f5b2b "sock *") ... 0x0a713b98: DW_TAG_formal_parameter DW_AT_name ("ack") DW_AT_type (0x0a6f58fd "u32") ... 0x0a713ba2: DW_TAG_formal_parameter DW_AT_name ("flag") DW_AT_type (0x0a6f57d1 "int") ... 0x0a713bac: DW_TAG_formal_parameter DW_AT_location (indexed (0x29) loclist =3D 0x016= 6d4a8: [0xffffffff83e77fd9, 0xffffffff83e78016): DW_OP_reg2= RCX ... DW_AT_name ("rs") DW_AT_type (0x0a710da5 "const rate_sample *") DW_AT_decl_line (1027) Some analysis for the above dwarf can conclude that the 'ark' and 'flag' may be related to RSI and RDX, considering the last one is RCX. Basically= this requires all parameters are available to collectively decide whether the true signature can be found or not. In such case, DW_CC_nocall can make t= hings easier as parameter can be checked one after another. For a clang built bpf-next kernel, in non-LTO setup, the number of kernel= functions is 69103 and the number of signature changed functions is 875, based on DW_AT_calling_convention (DW_CC_nocall) indication. Among 875 signature changed functions, after this patch, 495 functions can have proper true signatures, mostly due to simple dead argument elimination. The number of remaining functions, which cannot get the true signature, is 379. They will be addressed in the subsequent commits. In llvm23, I implemented [1] which added DW_CC_nocall for ArgumentPromoti= on pass. This compiler pass can add additional DW_CC_nocall cases for the followin= g compilation: - Flag -O3 or FullLTO So once llvm23 available, we may have more DW_CC_nocall cases, hence more potential true signatures if the kernel is built with -O3 or with FullLTO (CONFIG_LTO_CLANG_FULL). [1] https://github.com/llvm/llvm-project/pull/178973 Signed-off-by: Yonghong Song --- dwarf_loader.c | 70 ++++++++++++++++++++++++++++++++++++++++++-------- dwarves.h | 1 + 2 files changed, 60 insertions(+), 11 deletions(-) diff --git a/dwarf_loader.c b/dwarf_loader.c index 16fb7be..21f8bf1 100644 --- a/dwarf_loader.c +++ b/dwarf_loader.c @@ -1190,6 +1190,10 @@ static ptrdiff_t __dwarf_getlocations(Dwarf_Attrib= ute *attr, return ret; } =20 +struct func_info { + bool signature_changed; +}; + /* For DW_AT_location 'attr': * - if first location is DW_OP_regXX with expected number, return the r= egister; * otherwise save the register for later return @@ -1252,7 +1256,8 @@ out: } =20 static struct parameter *parameter__new(Dwarf_Die *die, struct cu *cu, - struct conf_load *conf, int param_idx) + struct conf_load *conf, int param_idx, + struct func_info *info) { struct parameter *parm =3D tag__alloc(cu, sizeof(*parm)); =20 @@ -1263,8 +1268,12 @@ static struct parameter *parameter__new(Dwarf_Die = *die, struct cu *cu, tag__init(&parm->tag, cu, die); parm->name =3D attr_string(die, DW_AT_name, conf); parm->idx =3D param_idx; - if (param_idx >=3D cu->nr_register_params || param_idx < 0) + if (param_idx < 0) return parm; + if (!info->signature_changed) { + if (cu->producer_clang || param_idx >=3D cu->nr_register_params) + return parm; + } /* Parameters which use DW_AT_abstract_origin to point at * the original parameter definition (with no name in the DIE) * are the result of later DWARF generation during compilation @@ -1337,7 +1346,7 @@ static int formal_parameter_pack__load_params(struc= t formal_parameter_pack *pack continue; } =20 - struct parameter *param =3D parameter__new(die, cu, conf, -1); + struct parameter *param =3D parameter__new(die, cu, conf, -1, NULL); =20 if (param =3D=3D NULL) return -1; @@ -1502,6 +1511,29 @@ static struct ftype *ftype__new(Dwarf_Die *die, st= ruct cu *cu) return ftype; } =20 +static bool function__signature_changed(struct function *func, Dwarf_Die= *die) +{ + /* The inlined DW_TAG_subprogram typically has the original source type= for + * abstract origin of a concrete function with address range, inlined s= ubroutine, + * or call site. + */ + if (func->inlined) + return false; + + if (!func->abstract_origin) + return attr_numeric(die, DW_AT_calling_convention) =3D=3D DW_CC_nocall= ; + + Dwarf_Attribute attr; + if (dwarf_attr(die, DW_AT_abstract_origin, &attr)) { + Dwarf_Die origin; + if (dwarf_formref_die(&attr, &origin)) + return attr_numeric(&origin, DW_AT_calling_convention) =3D=3D DW_CC_n= ocall; + } + + /* This should not happen */ + return false; +} + static struct function *function__new(Dwarf_Die *die, struct cu *cu, str= uct conf_load *conf) { struct function *func =3D tag__alloc(cu, sizeof(*func)); @@ -1800,9 +1832,9 @@ static struct tag *die__create_new_parameter(Dwarf_= Die *die, struct ftype *ftype, struct lexblock *lexblock, struct cu *cu, struct conf_load *conf, - int param_idx) + int param_idx, struct func_info *info) { - struct parameter *parm =3D parameter__new(die, cu, conf, param_idx); + struct parameter *parm =3D parameter__new(die, cu, conf, param_idx, inf= o); =20 if (parm =3D=3D NULL) return NULL; @@ -1889,7 +1921,7 @@ static struct tag *die__create_new_subroutine_type(= Dwarf_Die *die, tag__print_not_supported(die); continue; case DW_TAG_formal_parameter: - tag =3D die__create_new_parameter(die, ftype, NULL, cu, conf, -1); + tag =3D die__create_new_parameter(die, ftype, NULL, cu, conf, -1, NUL= L); break; case DW_TAG_unspecified_parameters: ftype->unspec_parms =3D 1; @@ -2118,7 +2150,8 @@ out_enomem: } =20 static int die__process_function(Dwarf_Die *die, struct ftype *ftype, - struct lexblock *lexblock, struct cu *cu, struct conf_load *conf); + struct lexblock *lexblock, struct cu *cu, struct conf_load *conf, + struct func_info *info); =20 static int die__create_new_lexblock(Dwarf_Die *die, struct cu *cu, struct lexblock *father, struct conf_load *conf) @@ -2126,7 +2159,7 @@ static int die__create_new_lexblock(Dwarf_Die *die, struct lexblock *lexblock =3D lexblock__new(die, cu); =20 if (lexblock !=3D NULL) { - if (die__process_function(die, NULL, lexblock, cu, conf) !=3D 0) + if (die__process_function(die, NULL, lexblock, cu, conf, NULL) !=3D 0) goto out_delete; } if (father !=3D NULL) @@ -2246,7 +2279,8 @@ static struct tag *die__create_new_inline_expansion= (Dwarf_Die *die, } =20 static int die__process_function(Dwarf_Die *die, struct ftype *ftype, - struct lexblock *lexblock, struct cu *cu, struct conf_load *conf) + struct lexblock *lexblock, struct cu *cu, struct conf_load *conf, + struct func_info *info) { int param_idx =3D 0; Dwarf_Die child; @@ -2320,7 +2354,7 @@ static int die__process_function(Dwarf_Die *die, st= ruct ftype *ftype, continue; } case DW_TAG_formal_parameter: - tag =3D die__create_new_parameter(die, ftype, lexblock, cu, conf, par= am_idx++); + tag =3D die__create_new_parameter(die, ftype, lexblock, cu, conf, par= am_idx++, info); break; case DW_TAG_variable: tag =3D die__create_new_variable(die, cu, conf, 0); @@ -2391,9 +2425,10 @@ out_enomem: static struct tag *die__create_new_function(Dwarf_Die *die, struct cu *c= u, struct conf_load *conf) { struct function *function =3D function__new(die, cu, conf); + struct func_info info =3D { function__signature_changed(function, die) = }; =20 if (function !=3D NULL && - die__process_function(die, &function->proto, &function->lexblock, c= u, conf) !=3D 0) { + die__process_function(die, &function->proto, &function->lexblock, c= u, conf, &info) !=3D 0) { function__delete(function, cu); function =3D NULL; } @@ -3045,6 +3080,17 @@ static unsigned long long dwarf_tag__orig_id(const= struct tag *tag, return cu->extra_dbg_info ? dtag->id : 0; } =20 +static bool attr_producer_clang(Dwarf_Die *die) +{ + const char *producer; + + producer =3D attr_string(die, DW_AT_producer, NULL); + if (!producer) + return false; + + return !!strstr(producer, "clang"); +} + struct debug_fmt_ops dwarf__ops; =20 static int die__process(Dwarf_Die *die, struct cu *cu, struct conf_load = *conf) @@ -3082,6 +3128,7 @@ static int die__process(Dwarf_Die *die, struct cu *= cu, struct conf_load *conf) } =20 cu->language =3D attr_numeric(die, DW_AT_language); + cu->producer_clang =3D attr_producer_clang(die); =20 if (conf->early_cu_filter) cu =3D conf->early_cu_filter(cu); @@ -3841,6 +3888,7 @@ static int cus__merge_and_process_cu(struct cus *cu= s, struct conf_load *conf, cu->priv =3D dcu; cu->dfops =3D &dwarf__ops; cu->language =3D attr_numeric(cu_die, DW_AT_language); + cu->producer_clang =3D attr_producer_clang(cu_die); cus__add(cus, cu); } =20 diff --git a/dwarves.h b/dwarves.h index d7c6474..4cabab0 100644 --- a/dwarves.h +++ b/dwarves.h @@ -302,6 +302,7 @@ struct cu { uint8_t has_addr_info:1; uint8_t uses_global_strings:1; uint8_t little_endian:1; + uint8_t producer_clang:1; uint8_t nr_register_params; int register_params[ARCH_MAX_REGISTER_PARAMS]; int functions_saved; --=20 2.52.0