Repository navigation
Emit debuginfo for foreign fns/statics - #164715
yonghong-song wants to merge 2 commits into
Conversation
|
Some changes occurred in compiler/rustc_attr_parsing cc @jdonszelmann, @JonathanBrouwer Some changes occurred in compiler/rustc_codegen_llvm/src/debuginfo cc @Walnut356 |
|
Thanks for the pull request, and welcome! The Rust Project has assigned @adwinwhite (or someone else) to review your changes, you should hear from them (or someone else) within the next two weeks. Please see the contribution instructions and our LLM policy for more information. Why was this reviewer chosen?The reviewer was selected based on:
|
This comment has been minimized.
This comment has been minimized.
|
r? Michal Rostecki |
|
Error: Failed to set assignee to
Please file an issue on GitHub at triagebot if there's a problem with this bot, or reach out on #triagebot on Zulip. |
This comment has been minimized.
This comment has been minimized.
e125d2c to
35153dd
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
35153dd to
e945db9
Compare
| if tcx.sess.target.arch == Arch::Bpf { | ||
| let attrs = tcx.codegen_fn_attrs(instance_def_id); | ||
| base::set_link_section(llfn, attrs); | ||
| // Declare kfuncs weak, as vmlinux.h does with `__weak __ksym`, so a kernel |
There was a problem hiding this comment.
This seems to mark all the symbols in the .ksyms section as week, but I would say we still want to differentiate between weak and strong ksyms like we do in clang.
There is an #[linkage = "extern_weak"] attribute that we should honor, and in the absence of it we should mark the ksym as strong.
I will propose an exact code change later today.
There was a problem hiding this comment.
I actually debate myself whether I should have default 'extern_weak' or not. The reason is the current vmlinux.h which has 'extern_weak' like below:
/* BPF kfuncs */
#ifndef BPF_NO_KFUNC_PROTOTYPES
extern void __bpf_trap(void) __weak __ksym;
extern bool __scx_bpf_dsq_insert_vtime(struct task_struct *p, struct scx_bpf_dsq_insert_vtime_args *args) __weak __ksym;
extern s32 __scx_bpf_select_cpu_and(struct task_struct *p, const struct cpumask *cpus_allowed, struct scx_bpf_select_cpu_and_args *args) __weak __ksym;
extern void bbr_cwnd_event_tx_start(struct sock *sk) __weak __ksym;
extern void bbr_init(struct sock *sk) __weak __ksym;
extern void bbr_main(struct sock *sk, u32 ack, int flag, const struct rate_sample *rs) __weak __ksym;
extern u32 bbr_min_tso_segs(struct sock *sk) __weak __ksym;
extern void bbr_set_state(struct sock *sk, u8 new_state) __weak __ksym;
extern u32 bbr_sndbuf_expand(struct sock *sk) __weak __ksym;
extern u32 bbr_ssthresh(struct sock *sk) __weak __ksym;
extern u32 bbr_undo_cwnd(struct sock *sk) __weak __ksym;
...
But agree with you that should not have '__weak' as the default in rust frontend. It is easier for users to mark it if needed. Also it may have other non kfunc functions which may not need __weak.
|
|
||
| /// The type a static with `#[linkage]` points to: a raw pointer, or an `Option` of a reference | ||
| /// (see `check_static_linkage`). `None` if it is unsized or not one of those. | ||
| fn extern_pointee_ty<'tcx>(cx: &CodegenCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { |
There was a problem hiding this comment.
This seems to be a helper with only a single user. Perhaps just move the code there, to check_and_apply_linkage?
There was a problem hiding this comment.
Right, it is a single user, I can inline into the call site.
| ); | ||
|
|
||
| fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> { | ||
| // On BPF, an extern placed in ".ksyms" is a kfunc or ksym, as with clang's __ksym. |
There was a problem hiding this comment.
Do we really need to differentiate between allowed targets for BPF and for other CPU targets? Having such a differentiation in compiler frontend is unfortunate.
Later you say "Elsewhere a section on an extern declaration has no effect" - but what does it exactly mean? If we allow compiling programs with foreign functions and statics with custom link sections for, let's say, x86_64-unknown-linux-gnu target, do we risk emitting some invalid code that can cause UBI, or is it just an useless, but harmless thing that the ELF loaders will ignore? I'm guessing the latter, but let me know if I'm wrong.
Also, the comment is specifically mentioning the .ksyms link section, but the code allows any foreign functions and statics to be placed in any link section. One could write the following BPF code:
unsafe extern "C" {
#[unsafe(link_section = ".foo")]
fn bar(a: u32, p: *mut c_void) -> u32;
}That this code also has no effect, but I think that with your proposed change, the compiler would allow it. But that's not a big issue, because BPF program loader like libbpf will just ignore it.
Of course you could extend this if statement to check also for the section name, and allow only .ksyms, but I'm personally leaning towards not policing this and just allowing any foreign fns/statics in any section for any target.arch, so we don't end up bloating compiler frontend with implementation details of different CPU architectures.
There was a problem hiding this comment.
Hm, I'm not sure how the new attribute infrastructure treats attributes that are not relevant or applicable. Like, it is useful to inform users that they did something that doesn't actually have any effect. This was only partly the case with the old attribute infrastructure where nobody reading the attribute would produce a warning that it wasn't used. In that scheme allowing this attribute through would still be fine.
But I wouldn't be surprised if now this warning infrastructure is no longer present as the new attribute parsing scheme is much more strict about what's allowed where. And if that's the case I would be mildly reluctant to allow this attribute wholesale.
That said, I'm not too familiar with post-attr-rework guidelines for how things should be done. Maybe @jdonszelmann has better insight.
There was a problem hiding this comment.
Do we really need to differentiate between allowed targets for BPF and for other CPU targets? Having such a differentiation in compiler frontend is unfortunate.
Good question. I am working on BPF so my implementation focus on BPF. I have no rejection to expand to all targets as long as people agrees.
There was a problem hiding this comment.
Actually the point about extern functions in custom sections "not taking effect outside BPF" is not even true. I tried the following experiment:
$ cat foo.c
#include <stdint.h>
__attribute__((section(".multiply")))
uint32_t multiply(uint32_t x, uint32_t y) {
return x * y;
}
$ clang -shared -fPIC foo.c -o libfoo.so
$ cat extern.rs
unsafe extern "C" {
#[unsafe(link_section = ".multiply")]
fn multiply(x: u32, y: u32) -> u32;
}
pub fn main() {
let x = unsafe { multiply(2, 3) };
println!("x: {x}");
}
$ rustc -C link-arg=-L. -C link-arg=-lfoo extern.rs
warning: the `link_section` attribute cannot be used on foreign functions
--> extern.rs:2:14
|
2 | #[unsafe(link_section = ".multiply")]
| ^^^^^^^^^^^^
|
= help: the `link_section` attribute can be applied to functions with a body and statics
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: `#[warn(unused_attributes)]` (part of `#[warn(unused)]`) on by default
warning: 1 warning emitted
$ LD_LIBRARY_PATH="/tmp/playground" ./extern
x: 6The program works! Placing custom functions in custom sections is absolutely fine - both GNU ld and glibc were able to link it.
The answer to @nagisa's question is - rustc emits just a warning. But given that the program works, I think it shouldn't even warn. We should just allow link_section on extern functions indiscriminately. I suppose the same program would work on most operating systems.
There was a problem hiding this comment.
Even better example, with .so file emitted by Rust and with extern statics:
$ cat foo.rs
#[unsafe(no_mangle)]
#[unsafe(link_section = ".foo")]
pub static FOO: u32 = 2;
#[unsafe(no_mangle)]
#[unsafe(link_section = ".bar")]
pub static BAR: u32 = 3;
#[unsafe(no_mangle)]
#[unsafe(link_section = ".multiply")]
pub extern "C" fn multiply(x: u32, y: u32) -> u32 {
x * y
}
$ rustc --crate-type=cdylib foo.rs -o libfoo.so
$ cat extern.rs
unsafe extern "C" {
#[unsafe(link_section = ".foo")]
static FOO: u32;
#[unsafe(link_section = ".bar")]
static BAR: u32;
#[unsafe(link_section = ".multiply")]
fn multiply(x: u32, y: u32) -> u32;
}
pub fn main() {
let x = unsafe { multiply(FOO, BAR) };
println!("x: {x}");
}
$ rustc -C link-arg=-L. -C link-arg=-lfoo extern.rs
warning: the `link_section` attribute cannot be used on foreign statics
--> extern.rs:2:14
|
2 | #[unsafe(link_section = ".foo")]
| ^^^^^^^^^^^^
|
= help: the `link_section` attribute can be applied to functions and statics
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: `#[warn(unused_attributes)]` (part of `#[warn(unused)]`) on by default
warning: the `link_section` attribute cannot be used on foreign statics
--> extern.rs:5:14
|
5 | #[unsafe(link_section = ".bar")]
| ^^^^^^^^^^^^
|
= help: the `link_section` attribute can be applied to functions and statics
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
warning: the `link_section` attribute cannot be used on foreign functions
--> extern.rs:8:14
|
8 | #[unsafe(link_section = ".multiply")]
| ^^^^^^^^^^^^
|
= help: the `link_section` attribute can be applied to functions with a body and statics
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
warning: 3 warnings emitted
$ LD_LIBRARY_PATH="/tmp/playground" ./extern
x: 6All the warnings seem useless, since everything works just fine.
There was a problem hiding this comment.
$ readelf -S libfoo.so | grep -e foo -e bar -e multiply
[11] .bar PROGBITS 000000000000760c 0000760c
[12] .foo PROGBITS 0000000000007610 00007610
[19] .multiply PROGBITS 000000000004a1e0 000491e0
| fn wants_foreign_item_debuginfo(&self) -> bool { | ||
| self.dbg_cx.is_some() | ||
| && self.sess().opts.debuginfo == DebugInfo::Full | ||
| && self.sess().target.arch == Arch::Bpf |
There was a problem hiding this comment.
Similar question here - do we really have to differentiate here? DWARF information containing a foreign fn/static might be of no effect, but it shouldn't be incorrect in a sense of breaking the standard or causing some bad behavior. I would also lean towards not policing it and emitting DI for such items indiscriminately.
There was a problem hiding this comment.
The reason I have guard is because in clang frontend. See
diff --git a/clang/lib/Basic/Targets/BPF.h b/clang/lib/Basic/Targets/BPF.h
index 117f81430bf4..b2f1831e960e 100644
--- a/clang/lib/Basic/Targets/BPF.h
+++ b/clang/lib/Basic/Targets/BPF.h
@@ -76,6 +76,8 @@ public:
return None;
}
+ bool allowDebugInfoForExternalVar() const override { return true; }
+
CallingConvCheckResult checkCallingConvention(CallingConv CC) const override {
switch (CC) {
default:
The default is false, and BPF is true.
I have no rejection if people want support all architectures.
There was a problem hiding this comment.
I think #164715 (comment) and #164715 (comment) are sufficient to conclude - let's support all architectures.
There was a problem hiding this comment.
I think #164715 (comment) and #164715 (comment) are sufficient to conclude - let's support all architectures.
Thanks for clarification! Let me move forward with this.
|
|
||
| // Arguments types | ||
| if cx.sess().target.is_like_msvc { | ||
| // FIXME(#42800): |
There was a problem hiding this comment.
It seems like we should try to actually fix that bug (#42800), instead of merging a workaround. I can try to do it.
There was a problem hiding this comment.
Indeed, I see that. it is a good idea to fix it.
|
There were already two PRs aiming to do the same thing:
These two being very similar to each other, and yours being a reimplementation from scratch that looks very differently. On surface, the other PRs look simplier. From the quick read, the practical differences I could notice are:
Anything else I missed? Would be great to mention the difference (perhaps in the PR description). |
| // is called directly, only when it exists, as clang does. libbpf resolves a missing weak | ||
| // kfunc to 0, and the verifier then prunes the call. | ||
|
|
||
| // CHECK-LABEL: call_weak: |
There was a problem hiding this comment.
Any chance you could move assertions closer to the relevant code?
| // LLVM-C's `LLVMDIBuilderCreateGlobalVariableExpression` always marks the | ||
| // variable as defined, which is wrong for an extern global that is only | ||
| // declared in this module. |
There was a problem hiding this comment.
This comment looks a lot like Claudeism. I don't think this comment is particularly useful, given that a single look to the C definition will reveal the difference right away.
| // Only BPF accepts `#[link_section]` on foreign statics (".ksyms" for ksyms); | ||
| // other targets warn that it is unused, so ignore it. |
There was a problem hiding this comment.
Inverse reasoning. The comment is superfluous regardless as the code implies exactly what's written in the comment. Just remove it.
| /// (see `check_static_linkage`). `None` if it is unsized or not one of those. | ||
| fn extern_pointee_ty<'tcx>(cx: &CodegenCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { | ||
| let ptr_ty = match ty.kind() { | ||
| ty::Adt(def, args) if cx.tcx.is_lang_item(def.did(), LangItem::Option) => args.type_at(0), |
There was a problem hiding this comment.
AFAIR is only valid for types that participate in layout optimization. e.g. Option<&T> is indeed just a *const T with null representing None, while Option<*const T> is a proper tagged enum.
I'm sure your LLM should be able to find a ready-made function or method or a pattern used elsewhere that's applicable here if you re-query it.
| && self.sess().target.arch == Arch::Bpf | ||
| } | ||
|
|
||
| /// Attaches a declaration `DISubprogram` to `llfn`, the declaration of a foreign function. |
There was a problem hiding this comment.
This sentence is hard to parse.
How about
| /// Attaches a declaration `DISubprogram` to `llfn`, the declaration of a foreign function. | |
| /// Attaches `DISubprogram` to a foreign function declaration `llfn` |
Please also review the other comments you've added for obvious claudeisms.
| if !self.wants_foreign_item_debuginfo() { | ||
| return; | ||
| } |
There was a problem hiding this comment.
How harmful would it be if we did this for all targets not just bpf?
There was a problem hiding this comment.
If we allow all other targets, the other targets will have debug info for those extern functions. And then other targets can decide whether they want to do anything with thoese extern function debuginfo.
In BPF, we will use them to generate BTF.
For other architectures, it depends on their specific needs. They can certainly ignore them.
|
|
||
| // Arguments types | ||
| if cx.sess().target.is_like_msvc { | ||
| // FIXME(#42800): |
Problem
=======
Currently rustc does not support debuginfo for foreign functions and foreign
statics (i.e. variables defined elsewhere), even with -Cdebuginfo=2. On BPF,
that means the BPF backend emits no BTF for them, but libbpf and the kernel
rely on those BTF entries in order to resolve kfuncs and ksyms.
For example, without this patch set, compiling
unsafe extern "C" {
#[unsafe(link_section = ".ksyms")]
#[linkage = "extern_weak"]
static bpf_kfunc: Option<unsafe extern "C" fn(i32, u64) -> i32>;
#[unsafe(link_section = ".ksyms")]
static bpf_ksym_var: i32;
fn do_work(x: i32) -> i32;
}
#[no_mangle]
pub fn prog() -> i32 { unsafe { match bpf_kfunc { Some(f) => f(1, 2), None => 0 } } }
#[no_mangle]
pub fn read_var() -> i32 { unsafe { bpf_ksym_var } }
#[no_mangle]
pub fn prog2() -> unsafe extern "C" fn(i32) -> i32 { do_work }
with --target bpfel-unknown-none -Copt-level=2 -Cdebuginfo=2, rustc will
issue a warning:
...
warning: the `link_section` attribute cannot be used on foreign statics
...
and the compiler gives declarations with no !dbg and no section:
@bpf_ksym_var = external local_unnamed_addr global i32
declare extern_weak noundef i32 @bpf_kfunc(i32 noundef, i64 noundef) ...
declare noundef i32 @do_work(i32 noundef) ...
and BTF only for the functions defined in the crate:
[3] FUNC 'prog' type_id=1 linkage=global
[7] FUNC 'prog2' type_id=4 linkage=global
[9] FUNC 'read_var' type_id=8 linkage=global
with no entries for bpf_kfunc, do_work or bpf_ksym_var and no .ksyms
DATASEC, so libbpf cannot resolve the kfunc or the ksym.
How clang solved this
=====================
Clang solved this in its frontend for BPF. LLVM commit e3d8ee3
("[DebugInfo] Support to emit debugInfo for extern variables") emits a
non-defining DIGlobalVariable for extern variables, and commit
a2a3ca8 ("BPF: emit debuginfo for Function of DeclRefExpr if
requested") emits a declaration DISubprogram for extern functions
referenced by address. Both are guarded by
TargetInfo::allowDebugInfoForExternalRef, which only BPF enables.
Commit ae0d2244a235 later moved extern functions onto the same
mechanism as extern variables, so clang now covers every extern
function a BPF program references.
With the C equivalent of the example above,
extern int bpf_kfunc(int a, unsigned long b) __weak __ksym;
extern const int bpf_ksym_var __ksym;
extern int do_work(int);
clang -O2 -g gives
@bpf_ksym_var = external ... constant i32, section ".ksyms", ... !dbg !0
declare !dbg !25 extern_weak ... i32 @bpf_kfunc(...) ... section ".ksyms"
declare !dbg !44 ... i32 @do_work(i32 noundef) rust-lang#1
and the BTF has the entries libbpf needs:
[6] FUNC 'bpf_kfunc' type_id=4 linkage=extern
[11] FUNC 'do_work' type_id=10 linkage=extern
[16] VAR 'bpf_ksym_var' type_id=15, linkage=extern
[17] DATASEC '.ksyms' size=0 vlen=2
type_id=6 offset=0 size=0 (FUNC 'bpf_kfunc')
type_id=16 offset=0 size=4 (VAR 'bpf_ksym_var')
How rustc solves this
=====================
A declaration DISubprogram is attached to each declared foreign function
and a non-defining DIGlobalVariable to each declared foreign static when
compiling with full debuginfo (-Cdebuginfo=2). The BPF backend then lowers
debuginfo to BTF.
A kfunc or ksym is marked with #[link_section = ".ksyms"], as clang's
__ksym does. The kfunc or ksym can also have #[linkage = "extern_weak"].
The above warning in the 'Problem' section disappears and the 'link_section'
attribute is applied.
Where Rust differs from C, rustc does the following:
- Names: the debuginfo is named after the symbol rather than the Rust
item name, since BTF matches extern functions and variables by name.
This also honors #[link_name].
- void *: Rust's c_void is an enum, so pointers to c_void get void *
debuginfo instead of a pointer to that enum; otherwise kfunc
prototypes taking void * would not match the kernel's BTF.
Support Other Architectures
===========================
The `link_section` attribute is now accepted on foreign items on all targets,
not just BPF. The major benefits include simpler code and no or minimal
per-target checks, and all these architectures can get debuginfo
(with -Cdebuginfo=2) for foreign fns and statics. Users can decide how to deal
with debuginfo for those foreign fns and statics. As an example, some x86 tests
are added to show debuginfo from foreign fns and statics.
On BPF, `*mut c_void` needs special handling in order to have BTF compatibility.
The specific effect is to treat it like `void *`, not a pointer to Rust's
`c_void` enum.
Test results
============
With this commit, the example from "Problem" compiles without
warnings, and the declarations get debuginfo and the .ksyms section,
with bpf_kfunc declared weak:
@bpf_ksym_var = external local_unnamed_addr global i32,
section ".ksyms", !dbg !0
declare !dbg !33 extern_weak noundef i32 @bpf_kfunc(...) ...
section ".ksyms"
declare !dbg !37 noundef i32 @do_work(i32 noundef) ...
The BTF now has the same extern entries and .ksyms DATASEC as clang's:
[3] FUNC 'prog' type_id=1 linkage=global
[6] FUNC 'bpf_kfunc' type_id=4 linkage=extern
[10] FUNC 'prog2' type_id=7 linkage=global
[12] FUNC 'do_work' type_id=11 linkage=extern
[14] FUNC 'read_var' type_id=13 linkage=global
[15] VAR 'bpf_ksym_var' type_id=2, linkage=extern
[16] DATASEC '.ksyms' size=0 vlen=2
type_id=6 offset=0 size=0 (FUNC 'bpf_kfunc')
type_id=15 offset=0 size=4 (VAR 'bpf_ksym_var')
and bpf_kfunc is a weak undefined symbol in the object file:
8: 0000000000000000 0 NOTYPE WEAK DEFAULT UND bpf_kfunc
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND do_work
12: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND bpf_ksym_var
LLM Disclosure
==============
I guided claude to support foreign fns/statics. I wrote some tests by
myself. All code changes and new tests are reviewed and tested manually.
e945db9 to
0af8440
Compare
|
This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
|
This comment has been minimized.
This comment has been minimized.
0af8440 to
398d267
Compare
398d267 to
283958a
Compare
This comment has been minimized.
This comment has been minimized.
The following is a list of tests:
- Codegen debuginfo tests. Tests bpf-foreign-debuginfo.rs and
x86-foreign-debuginfo.rs are tested with debuginfo.
- The test link-section-foreign-items.rs validates foreign
declarations, with no debuginfo.
- The test link-section.rs previously expected the "being phased out"
warning. Now the test passes without a warning.
- ui/attributes/positions/link-section-bpf.rs accepts foreign fns and
statics with no lint.
- run-make btf tests. A helper print_btf.py is used to dump
easy-to-understand BTF. extern.rs covers strong foreign items, and
extern_weak.rs covers weak ones. extern_weak.rs also checks the
generated code and symbols.
- Some other tests include attr-on-mac-call,
codegen_attr_on_required_trait_method,
cfg_attr-attr-syntax-validation and
issue-43106-gating-of-builtin-attrs. Only the `help:` line listing
allowed targets changed.
Also ignore print_btf.py file when doing tidy.
283958a to
29b8657
Compare
|
Okay, I pushed the second revision. The following is the update:
One more thing, the above mentioned print_btf.py has some issues in CI, so workaround like below: Let me know if you have better solution. |
|
☔ The latest upstream changes (presumably #165375) made this pull request unmergeable. Please resolve the merge conflicts by rebasing. |
Okay, I will rebase on latest main branch. |
View all comments
Problem
Currently rustc does not support debuginfo for foreign functions and foreign
statics (i.e. variables defined elsewhere), even with -Cdebuginfo=2. On BPF,
that means the BPF backend emits no BTF for them, but libbpf and the kernel
rely on those BTF entries in order to resolve kfuncs and ksyms.
For example, without this patch set, compiling
with --target bpfel-unknown-none -Copt-level=2 -Cdebuginfo=2, rustc will
issue a warning:
and the compiler gives declarations with no !dbg and no section:
and BTF only for the functions defined in the crate:
with no entries for bpf_kfunc, do_work or bpf_ksym_var and no .ksyms
DATASEC, so libbpf cannot resolve the kfunc or the ksym.
How clang solved this
Clang solved this in its frontend for BPF. LLVM commit e3d8ee3
("[DebugInfo] Support to emit debugInfo for extern variables") emits a
non-defining DIGlobalVariable for extern variables, and commit
a2a3ca8 ("BPF: emit debuginfo for Function of DeclRefExpr if
requested") emits a declaration DISubprogram for extern functions
referenced by address. Both are guarded by
TargetInfo::allowDebugInfoForExternalRef, which only BPF enables.
Commit ae0d2244a235 later moved extern functions onto the same
mechanism as extern variables, so clang now covers every extern
function a BPF program references.
With the C equivalent of the example above,
clang -O2 -g gives
and the BTF has the entries libbpf needs:
How rustc solves this
A declaration DISubprogram is attached to each declared foreign function
and a non-defining DIGlobalVariable to each declared foreign static when
compiling with full debuginfo (-Cdebuginfo=2). The BPF backend then lowers
debuginfo to BTF.
A kfunc or ksym is marked with
#[link_section = ".ksyms"], as clang's__ksym does. The kfunc or ksym can also have
#[linkage = "extern_weak"].The above warning in the 'Problem' section disappears and the 'link_section'
attribute is applied.
Where Rust differs from C, rustc does the following:
Names: the debuginfo is named after the symbol rather than the Rust
item name, since BTF matches extern functions and variables by name.
This also honors
#[link_name].void *: Rust's c_void is an enum, so pointers to c_void get void *
debuginfo instead of a pointer to that enum; otherwise kfunc
prototypes taking void * would not match the kernel's BTF.
Support Other Architectures
The
link_sectionattribute is now accepted on foreign items on all targets,not just BPF. The major benefits include simpler code and no or minimal
per-target checks, and all these architectures can get debuginfo
(with -Cdebuginfo=2) for foreign fns and statics. Users can decide how to deal
with debuginfo for those foreign fns and statics. As an example, some x86 tests
are added to show debuginfo from foreign fns and statics.
On BPF,
*mut c_voidneeds special handling in order to have BTF compatibility.The specific effect is to treat it like
void *, not a pointer to Rust'sc_voidenum.Test results
With this commit, the example from "Problem" compiles without
warnings, and the declarations get debuginfo and the .ksyms section,
with bpf_kfunc declared weak:
The BTF now has the same extern entries and .ksyms DATASEC as clang's:
and bpf_kfunc is a weak undefined symbol in the object file:
LLM Disclosure
I guided claude to support foreign fns/statics. I wrote some tests by
myself. All code changes and new tests are reviewed and tested manually.