From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pl1-f176.google.com (mail-pl1-f176.google.com [209.85.214.176]) (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 B67D327A91D for ; Sat, 10 Oct 2026 00:52:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.176 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1791593556; cv=none; b=nup/CLL1TBt6onP4e7ll//Q6nEQSm9934Y606EfQxEJTjhjpNI3IQTO40al8SHq3ALDptipkum5LClPOizJRvbJPjONZaNW+2YRGeicxqAeBYu3zNaBFHY8pF49i1R1bh6plUMn03En6cZUHzlAKm3pxg5h+cobsanbOtLqY6I8= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1791593556; c=relaxed/simple; bh=Jsp6LgbTK4cfWWmLeUTZqtKixJIB0Y9Ie/1lUZvCJv4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=WZMzYbQQsIIFhZDV8dDl12GO1RG9GlRXV4gEX/V+rpN7gLxzoKvVIt6AV/FODnBbbb9lpetwj9JGDEBgGf0haRrvnQeJi5ONcUbi+FE/aHR7hrL1SPjYnnNkpBtTmjlQnIIDDv3JqauHpsEC6kjVFtNe2FBayDhpYAIri9tUWzY= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=alanderk.com; spf=pass smtp.mailfrom=alanderk.com; dkim=pass (2048-bit key) header.d=alanderk.com header.i=@alanderk.com header.b=q+WFkCR9; arc=none smtp.client-ip=209.85.214.176 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=alanderk.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=alanderk.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=alanderk.com header.i=@alanderk.com header.b="q+WFkCR9" Received: by mail-pl1-f176.google.com with SMTP id d9443c01a7336-2dd9cbb661bso1908025ad.3 for ; Fri, 09 Oct 2026 17:52:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=alanderk.com; s=googol; t=1791593554; x=1792198354; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=azgHsYvqsrDkaxXDY0hb9Qx2rxZr9Q0YSKH5ea/vOcw=; b=q+WFkCR9Q0N3SfJ53hzM7L+p3nfAcMOQPyfhfsk60XS60HzxgTOS6MHnPOTSvSA4T+ ot3wHcMyQ9PQdDhu4y7jnoGGObzqmL6UNeFvPA48n8Gvh0Lk71usvpqdJAy5foOsBkV2 c4occv7lwExxyM5fAgmLK9JA0RIKzZujwhGBOBRSv/f33k+hRHoZdjBS0p/Re7Cljgc4 Go38huxnrFMtBu93hJAENaxQvORRbBfjEK3qekPwYNAglfgEw/FsEgZ2ifYmeA9u8Ues KAlj8yI1xLWnf6esUmLlFnEgdqHgW/a6dcF217DbuCa+6JqUadL41hmd35OjWOUiZcmV vNLw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1791593554; x=1792198354; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=azgHsYvqsrDkaxXDY0hb9Qx2rxZr9Q0YSKH5ea/vOcw=; b=zXS6M6aA2oW0aIfFNfoNPNy4a8NzdB5pVYvi+GlVpC5rtI9P27IP473h6qi8knrNFz R92Gk2uQh7oXWfocclyW/ZTrYEcbnkeC+SWOg9ecxAV85OoZaDbL+b92+B2148I3/Jm/ tDY13Av1owwLF461unlZVq+gO35GNXB5uMM9HAb4a/3BOA4EUyxOdtherlgGO0p3B4xF XST6hJObyVvAi22Cf+Gr/GfbX5qT5/Z2IBdCmskcdlZL69oA1bLkUj5K3gb5W4uiLaIM 8iKzRgszDhjTH7uL+vOaIDp3Y3xaj10rdda3DP7nPXucAeQCGWwqURwtRmFWfB0sx9r9 BOHA== X-Forwarded-Encrypted: i=1; AKwUvByF8GGsNJh6tmfuipHaZ5zHh2Z4THW8FxY6sEf7DjVAQKr4CdbcBuJtCAAHPZJ9WQlti0aazq2g3qjHNxzFyQ==@vger.kernel.org X-Gm-Message-State: AFq9FYI4SxRdANQd+WAqkvgOGrhu4UEhvlTEosRHm4/haCNUD+z642d1 kDnxHIx+GoNMN2NvKy1yMUld5EDz6KAmWQ5L0zRyfdNJIFudAlJ5UZpUckhDmD2UyN8= X-Gm-Gg: AYBFou0NC/hCGjVhxSPAsVjEsMjIjZy2nse2d5knxIG5WM3NVIkA/Jxf/2y/+20AeGu wIZdLrWmP8/JQPbYLG9CSqKBRKFXzNTufiSW5vdOzGXx1YgLX0hygGR3RRHSbmtG4dAy3CMCAtV 9Djiwg4juKPfPXKCSgDOz52uVELzUKUS0P7Qjx1YhmRndjFmpSmRqwJV0VI1O4bKmub82zGbTNZ 2iBUjflKOSqnWqIFDnzAkzCw9oxkPrbwRG3SumOdV5AoHtvdrGK+oFiCFSS9gP3YneP9lKxhSRZ LGb7q4Sjk51l+C/PtlxXrMhQeLkCCIbYyJyAdGDwDhaEJ11nqTN4BGm761QaTNJhdm6UBg8t8dy rhH14CyRDD0L5wMVaIRgTMMgkgBLZNnTyXs6bEOWDgllzLIZV+jPa3wILyisfsfrJTlHqQcCLf8 ZB+hlGjLeQpvQx3+KXa4VJwyJUMmMMQGqg0gLEEeqNHLXhjNdaO08QxsKI/mTSW1vcRIc0hjBxZ vjLD3+N7wIgWsbRRY0b1nEGuZ69gDT7dQ== X-Received: by 2002:a17:90a:e7ca:b0:3a4:f75d:d194 with SMTP id 98e67ed59e1d1-3ab3a8ce801mr3278439a91.36.1791593554040; Fri, 09 Oct 2026 17:52:34 -0700 (PDT) Received: from macmini4.tapir-palermo.ts.net ([138.199.43.94]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3ab50ce9d98sm4078226a91.9.2026.10.09.17.52.31 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Fri, 09 Oct 2026 17:52:33 -0700 (PDT) From: Alan Derk To: Arnaldo Carvalho de Melo , Alan Maguire Cc: dwarves@vger.kernel.org, bpf@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH dwarves v2 1/2] dwarf_loader: Take the merged CU language from a CU that is kept Date: Sat, 10 Oct 2026 00:52:26 +0000 Message-ID: <20261010005227.976-2-linux@alanderk.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20261010005227.976-1-linux@alanderk.com> References: <20261010005227.976-1-linux@alanderk.com> Precedence: bulk X-Mailing-List: rust-for-linux@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit When the CUs of an object are merged, as for clang LTO objects, the merged CU takes the language of the first CU. Since commit b98565e7b17ec24d ("dwarf_loader: Honour --lang_exclude when merging LTO built CUs") the DIEs of excluded CUs are skipped while merging, but pahole_stealer() then applies the same filter to the merged CU. If the first CU is in an excluded language, the whole merged CU is dropped, including the CUs that were kept, and with -J pahole writes no BTF and still exits 0. Kernel modules mixing Rust and C hit this in clang LTO builds with CONFIG_DEBUG_INFO_BTF_MODULES: kbuild passes --lang_exclude=rust, and the Rust CU comes first in the linked module even when the Rust object is listed last, as the C objects are bitcode compiled at link time. scripts/gen-btf.sh then fails, here for a Rust sample module: FAILED: load BTF from samples/rust/rust_minimal.ko.BTF.1: No such file or directory This is what is left of the problem behind kernel commit 5daa0c35a1f0 ("rust: Disallow BTF generation with Rust + LTO"), which keeps RUST off when LTO and BTF are both on. With pahole 1.32 a ThinLTO vmlinux with Rust built in gets clean BTF, but its Rust modules fail as above, so that restriction can't be relaxed yet. Distributions with LTO kernels, such as CachyOS's default kernel, keep BTF for sched_ext and BPF tools and go without Rust; out-of-tree modules that build their own Rust objects there hit this today. Take the merged CU's language from the first CU the filter keeps, and its producer too, as producer_clang also steers how the merged DIEs are processed. Run the filter before checking for DIEs, as the unmerged path does, so that a first CU without DIEs, such as an assembler CU, still counts and the output without --lang_exclude does not change. If every CU is filtered out, the merged CU keeps the first CU's language and is dropped as before. Before, with a C++ object linked before a C one: $ pahole -V --btf_features=force_cu_merging --lang_exclude=c++14 merged.o Filtering CU first.cpp written in c++14. Filtering CU written in c++14. After: $ pahole -V --btf_features=force_cu_merging --lang_exclude=c++14 merged.o Filtering CU first.cpp written in c++14. struct c_only { int b; /* 0 4 */ ... Add tests/lang_exclude_merged_cu.sh for this. Tested with clang 21 builds of 7.2.9 with RUST enabled: x86_64 with ThinLTO (DWARF 4 and 5), full LTO and distributed ThinLTO, and arm64 and riscv64 with ThinLTO. The Rust sample modules get BTF and load with MODULE_ALLOW_BTF_MISMATCH=n, and the BTF of vmlinux and of the C modules does not change. Fixes: b98565e7b17ec24d ("dwarf_loader: Honour --lang_exclude when merging LTO built CUs") Assisted-by: LLM Signed-off-by: Alan Derk --- Part of the dwarf_loader.c diff is re-indentation; git show -w is easier to read. dwarf_loader.c | 34 +++++++++++------ tests/lang_exclude_merged_cu.sh | 67 +++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 12 deletions(-) create mode 100755 tests/lang_exclude_merged_cu.sh diff --git a/dwarf_loader.c b/dwarf_loader.c index 01049e9..fbed36e 100644 --- a/dwarf_loader.c +++ b/dwarf_loader.c @@ -5329,6 +5329,7 @@ static int cus__merge_and_process_cu(struct cus *cus, struct conf_load *conf, struct dwarf_cu *dcu = NULL; Dwarf_Off off = 0, noff; struct cu *cu = NULL; + bool kept_cu = false; size_t cuhl; while (dwarf_nextcu(dw, off, &noff, &cuhl, NULL, &pointer_size, @@ -5441,23 +5442,32 @@ static int cus__merge_and_process_cu(struct cus *cus, struct conf_load *conf, cus__add(cus, cu); } - Dwarf_Die child; - if (dwarf_child(cu_die, &child) == 0) { - bool filtered = false; + bool filtered = false; - if (conf->early_cu_filter) { - struct cu unmerged_cu = { - .name = attr_string(cu_die, DW_AT_name, conf), - .language = attr_numeric(cu_die, DW_AT_language), - }; + if (conf->early_cu_filter) { + struct cu unmerged_cu = { + .name = attr_string(cu_die, DW_AT_name, conf), + .language = attr_numeric(cu_die, DW_AT_language), + }; - filtered = conf->early_cu_filter(&unmerged_cu) == NULL; - } + filtered = conf->early_cu_filter(&unmerged_cu) == NULL; + } - if (!filtered && die__process_unit(&child, cu, conf, 0) != 0) - goto out_abort; + /* + * pahole filters the merged CU by language again, so it must + * not keep the language of a CU filtered out here. + */ + if (!filtered && !kept_cu) { + cu->language = attr_numeric(cu_die, DW_AT_language); + cu->producer_clang = attr_producer_clang(cu_die); + kept_cu = true; } + Dwarf_Die child; + if (!filtered && dwarf_child(cu_die, &child) == 0 && + die__process_unit(&child, cu, conf, 0) != 0) + goto out_abort; + off = noff; } diff --git a/tests/lang_exclude_merged_cu.sh b/tests/lang_exclude_merged_cu.sh new file mode 100755 index 0000000..507c6ac --- /dev/null +++ b/tests/lang_exclude_merged_cu.sh @@ -0,0 +1,67 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-only +# +# Test --lang_exclude when CUs are merged, as for LTO objects: excluding the +# language of the first CU must not drop the CUs that follow it. + +. "$(dirname "$0")/test_lib.sh" +outdir=$(make_tmpdir) + +trap cleanup EXIT + +title_log "Language filtering of merged CUs." + +CC=${CC:-gcc} +CXX=${CXX:-g++} +LD=${LD:-ld} +for tool in "$CC" "$CXX" "$LD"; do + if ! command -v "${tool%% *}" > /dev/null 2>&1; then + info_log "skip: $tool not available" + test_skip + fi +done + +cat > "$outdir/first.cpp" << 'EOF' +struct cpp_only { + int a; +}; + +struct cpp_only cpp_var; +EOF + +cat > "$outdir/second.c" << 'EOF' +struct c_only { + int b; +}; + +struct c_only c_var; +EOF + +obj="$outdir/merged.o" + +if ! $CXX -g -c -o "$outdir/first.o" "$outdir/first.cpp" 2>/dev/null || + ! $CC -g -c -o "$outdir/second.o" "$outdir/second.c" 2>/dev/null || + ! $LD -r -o "$obj" "$outdir/first.o" "$outdir/second.o" 2>/dev/null; then + error_log "FAIL: compilation failed" + test_fail +fi + +output=$(pahole --btf_features=force_cu_merging -F dwarf "$obj" 2>/dev/null) +if ! echo "$output" | grep -q "struct cpp_only" || + ! echo "$output" | grep -q "struct c_only"; then + error_log "FAIL: merged CU lacks types without --lang_exclude" + test_fail +fi + +output=$(pahole --btf_features=force_cu_merging -F dwarf \ + --lang_exclude=c++,c++03,c++11,c++14 "$obj" 2>/dev/null) +if echo "$output" | grep -q "struct cpp_only"; then + error_log "FAIL: --lang_exclude=c++ kept the C++ CU" + test_fail +fi +if ! echo "$output" | grep -q "struct c_only"; then + error_log "FAIL: --lang_exclude=c++ dropped the C CU merged after it" + test_fail +fi + +test_pass -- 2.55.0