From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from 66-220-155-179.mail-mxout.facebook.com (66-220-155-179.mail-mxout.facebook.com [66.220.155.179]) (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 B1D19490BE4 for ; Thu, 1 Oct 2026 13:31:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=66.220.155.179 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790861512; cv=none; b=avKlBy4NneWOu8PnZ/dqdpMF2nfv8lh6T6y18h5lXX7JNCtAPcn9oW8bRWKDG3e/53pxkgzcT25QQ5syEkOL253M52zP8Hs4AFsJGMuZmn/8UfCNFY0lrOj8scxShWXiPIan/I7OdImeMcj4j2DtzAdCL80A+g1suU2o9X2iJOk= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790861512; c=relaxed/simple; bh=ZyeHHBBor3W1wyVgVLkXRiw5PG2EKcxKBz4KkMLK2Mw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=R3viqLAfcEv+LIQzGOe9vze0Pk4lh1INdLZqCOZ+eAuBhO0RRkfh5AMvfZzY2R40mLY2WIZWKWQjf9ofMmhjRr/+un49HN0fFG8YRkvtbhlIlWTr1uiM+lYg6rZcyqjyzP4Z34rvXfS6/egccbNAV9ySFwOz52QC7vHSLrSGCls= 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=66.220.155.179 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 DD7642E6E124B2; Thu, 1 Oct 2026 06:31:35 -0700 (PDT) From: Yonghong Song To: bpf@vger.kernel.org Cc: Alexei Starovoitov , Andrii Nakryiko , Daniel Borkmann , Eduard Zingerman , kernel-team@fb.com Subject: [PATCH bpf-next v8 17/22] libbpf: Carry the exception cleanup table through the light skeleton Date: Thu, 1 Oct 2026 06:31:35 -0700 Message-ID: <20261001133135.1344989-1-yonghong.song@linux.dev> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20261001133006.1335369-1-yonghong.song@linux.dev> References: <20261001133006.1335369-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 A light skeleton does not call bpf_prog_load(). bpf_gen__prog_load() buil= ds its own union bpf_attr field by field, and the loader program it emits is what issues BPF_PROG_LOAD when the skeleton runs -- so a program loaded this way reached the kernel without the table the previous patch collecte= d for it, and the verifier refused it with "unreachable insn", which names neither the skeleton nor the table. Carry it the way func_info and line_info are carried: the records go into the loader's blob of bytes, the count and record size into the attr, and = a relocation stores the blob's address into attr.cleanup_info once that address is known. For a program with records the attr grows to its new last field, cleanup_info_cnt. One without keeps the old size: the longer attr also takes in log_true_size, which the kernel writes back, into the loader's read-only data. Records are 4-byte fields like the other info blobs, so a cross-endian build has to swap them too. Signed-off-by: Yonghong Song --- tools/lib/bpf/gen_loader.c | 34 ++++++++++++++++++++++++++++++--- tools/lib/bpf/libbpf_internal.h | 7 +++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c index 251392aa8b41..4017fb366384 100644 --- a/tools/lib/bpf/gen_loader.c +++ b/tools/lib/bpf/gen_loader.c @@ -994,13 +994,15 @@ static void cleanup_relos(struct bpf_gen *gen, int = insns) cleanup_core_relo(gen); } =20 -/* Convert func, line, and core relo info blobs to target endianness */ +/* Convert func, line, core relo and cleanup info blobs to target endian= ness */ static void info_blob_bswap(struct bpf_gen *gen, int func_info, int line= _info, - int core_relos, struct bpf_prog_load_opts *load_attr) + int core_relos, int cleanup_info, + struct bpf_prog_load_opts *load_attr) { struct bpf_func_info *fi =3D gen->data_start + func_info; struct bpf_line_info *li =3D gen->data_start + line_info; struct bpf_core_relo *cr =3D gen->data_start + core_relos; + struct bpf_cleanup_info *ci =3D gen->data_start + cleanup_info; int i; =20 for (i =3D 0; i < load_attr->func_info_cnt; i++) @@ -1011,6 +1013,9 @@ static void info_blob_bswap(struct bpf_gen *gen, in= t func_info, int line_info, =20 for (i =3D 0; i < gen->core_relo_cnt; i++) bpf_core_relo_bswap(cr++); + + for (i =3D 0; i < load_attr->cleanup_info_cnt; i++) + bpf_cleanup_info_bswap(ci++); } =20 void bpf_gen__prog_load(struct bpf_gen *gen, @@ -1024,10 +1029,20 @@ void bpf_gen__prog_load(struct bpf_gen *gen, load_attr->line_info_rec_size; int core_relo_tot_sz =3D gen->core_relo_cnt * sizeof(struct bpf_core_relo); + int cleanup_info_tot_sz =3D load_attr->cleanup_info_cnt * + load_attr->cleanup_info_rec_size; int prog_load_attr, license_off, insns_off, func_info, line_info, core_= relos; int attr_size =3D offsetofend(union bpf_attr, core_relo_rec_size); + int cleanup_info; union bpf_attr attr; =20 + /* + * Reach the cleanup fields only when there are records: the attr then + * also covers log_true_size, which the kernel writes back, and the + * attr lives in the loader's read-only data. + */ + if (load_attr->cleanup_info_cnt) + attr_size =3D offsetofend(union bpf_attr, cleanup_info_cnt); memset(&attr, 0, attr_size); /* add license string to blob of bytes */ license_off =3D add_data(gen, license, strlen(license) + 1); @@ -1074,9 +1089,17 @@ void bpf_gen__prog_load(struct bpf_gen *gen, core_relos, gen->core_relo_cnt, sizeof(struct bpf_core_relo)); =20 + attr.cleanup_info_rec_size =3D tgt_endian(load_attr->cleanup_info_rec_s= ize); + attr.cleanup_info_cnt =3D tgt_endian(load_attr->cleanup_info_cnt); + cleanup_info =3D add_data(gen, load_attr->cleanup_info, cleanup_info_to= t_sz); + pr_debug("gen: prog_load: cleanup_info: off %d cnt %u rec size %u\n", + cleanup_info, load_attr->cleanup_info_cnt, + load_attr->cleanup_info_rec_size); + /* convert all info blobs to target endianness */ if (gen->swapped_endian && !gen->error) - info_blob_bswap(gen, func_info, line_info, core_relos, load_attr); + info_blob_bswap(gen, func_info, line_info, core_relos, cleanup_info, + load_attr); =20 libbpf_strlcpy(attr.prog_name, prog_name, sizeof(attr.prog_name)); prog_load_attr =3D add_data(gen, &attr, attr_size); @@ -1098,6 +1121,11 @@ void bpf_gen__prog_load(struct bpf_gen *gen, /* populate union bpf_attr with a pointer to core_relos */ emit_rel_store(gen, attr_field(prog_load_attr, core_relos), core_relos)= ; =20 + /* with no records there is no blob of them to point the attr at */ + if (load_attr->cleanup_info_cnt) + emit_rel_store(gen, attr_field(prog_load_attr, cleanup_info), + cleanup_info); + /* populate union bpf_attr fd_array with a pointer to data where map_fd= s are saved */ emit_rel_store(gen, attr_field(prog_load_attr, fd_array), gen->fd_array= ); =20 diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_inter= nal.h index f1630f03d5f5..9d341839ca74 100644 --- a/tools/lib/bpf/libbpf_internal.h +++ b/tools/lib/bpf/libbpf_internal.h @@ -572,6 +572,13 @@ static inline void bpf_core_relo_bswap(struct bpf_co= re_relo *i) i->kind =3D bswap_32(i->kind); } =20 +static inline void bpf_cleanup_info_bswap(struct bpf_cleanup_info *i) +{ + i->begin_off =3D bswap_32(i->begin_off); + i->end_off =3D bswap_32(i->end_off); + i->landing_pad_off =3D bswap_32(i->landing_pad_off); +} + enum btf_field_iter_kind { BTF_FIELD_ITER_IDS, BTF_FIELD_ITER_STRS, --=20 2.53.0-Meta