Skip to content

refactor: allow eBPF crates to compile on host architecture #1229

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@ default-members = [
"aya-ebpf-macros",
"aya-log-ebpf-macros",

# ebpf crates are omitted; they must be built with:
# --target bpfe{b,l}-unknown-none
# CARGO_CFG_BPF_TARGET_ARCH={x86_64,aarch64,arm,riscv64,powerpc64,s390x,mips}
"ebpf/aya-ebpf",
"ebpf/aya-ebpf-bindings",
"ebpf/aya-log-ebpf",
"test/integration-ebpf",
]

[workspace.package]
Expand Down
2 changes: 1 addition & 1 deletion ebpf-panic/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
//! #![no_std]
//!
//! use aya_ebpf::{macros::tracepoint, programs::TracePointContext};
//! #[cfg(not(test))]
//! #[cfg(target_arch = "bpf")]
//! extern crate ebpf_panic;
//!
//! #[tracepoint]
Expand Down
10 changes: 10 additions & 0 deletions ebpf/aya-ebpf/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,16 @@ pub fn check_bounds_signed(value: i64, lower: i64, upper: i64) -> bool {
}
}

#[macro_export]
macro_rules! main_stub {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am open to changing this to something else other than a macro by example if we can come up with something elegant.

AFAIK, eBPF progams can have multiple entry points so we cannot really attach it to another proc-macro, at least not in the general case.

Leaning onto the idea from @dave-tucker, we could define a aya_ebpf::main macro that people can add to their entrypoint:

#[xdp]
#[aya_ebpf::main]
pub fn handle(ctx: XdpContext) -> u32 {
	xdp_action::XDP_PASS
}

It is not very elegant though because the handler itself has nothing to do with the generated main stub, i.e. putting the attribute on any other function would also work.

Given that we already use nightly, we could try and build something on top of #![feature(custom_inner_attributes)]?

Using that, we could perhaps lump the no_std, no_main and main stub together so that users only have to define one macro, like:

#![aya_ebpf::program]

() => {
#[cfg(not(target_arch = "bpf"))]
fn main() {
panic!(r#"eBPF kernels are not designed to be executed in user-space. This main function is only a placeholder to allow the code to compile on the host system (i.e. on any system that is not `target_arch = "bpf"`). This works in tandem with the `no_main` attribute which is only applied when compiling for `target_arch = "bpf"`."#)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #1229 (comment)

This is not a meaningful message like I asked. This is basically just saying "we need this because no_main only on bpf" but doesn't explain why we can't keep no_main in place when targeting not-bpf.

Copy link
Contributor Author

@thomaseizinger thomaseizinger Mar 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please write out message that you'd like to see? 🙏

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd love to, but I actually don't know the reason.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The crate doesn't compile without a main function (for the host architecture). I guess the reasons is "binaries need an entrypoint"?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're still not answering the question, or am I missing something? Why can't no_main be used on the host?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't how else to say it, the code doesn't compile without a main function:

error[E0601]: `main` function not found in crate `tcx`
  --> test/integration-ebpf/src/tcx.rs:10:2
   |
10 | }
   |  ^ consider adding a `main` function to `test/integration-ebpf/src/tcx.rs`

If I add the no_main attribute back unconditionally, then it fails at link time:

error: linking with `/nix/store/xy542s1z1x4257pwdis32lxggff0ilkk-clang-wrapper-18.1.8/bin/clang` failed: exit status: 1
  |
  = note: LC_ALL="C" PATH="/home/thomas/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/bin:/home/thomas/.cargo/bin:/run/wrappers/bin:/home/thomas/.nix-profile/bin:/nix/profile/bin:/home/thomas/.local/state/nix/profile/bin:/etc/profiles/per-user/thomas/bin:/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin:/home/thomas/.zsh/plugins/powerlevel10k:/home/thomas/.zsh/plugins/zsh-nix-shell" VSLANG="1033" "/nix/store/xy542s1z1x4257pwdis32lxggff0ilkk-clang-wrapper-18.1.8/bin/clang" "-m64" "/tmp/rustcfWbebz/symbols.o" "<17 object files omitted>" "-Wl,--as-needed" "-Wl,-Bstatic" "/home/thomas/src/github.com/aya-rs/aya/target/debug/deps/{libintegration_common-7199cfc51e571138.rlib,libaya-29671cf570d77640.rlib,libonce_cell-d9c100d41321019c.rlib,libbitflags-859ae39afcf27678.rlib,libassert_matches-e66f24f8aa7afaf4.rlib,libtokio-5bb67de0f8e545f8.rlib,libsocket2-09fa8cf7b5bce950.rlib,libmio-1925bbb0f0827c81.rlib,libpin_project_lite-d0952d87a1353a36.rlib,liblibc-76a839478b7a8354.rlib,libaya_obj-5ff296ad2a1a8244.rlib,libthiserror-87232d1566e28900.rlib,libobject-89715a7346573fe7.rlib,libindexmap-9e0d9bcc9af9f37c.rlib,libhashbrown-060ab6f8299b021b.rlib,libfoldhash-e74b4bd1cf6c7986.rlib,libequivalent-4b5dae619175b0d3.rlib,liballocator_api2-0d18d2560b2240f9.rlib,libcrc32fast-3927d6bc625d2278.rlib,libcfg_if-7320f7c2ff486fd2.rlib,libmemchr-c683b493e630f757.rlib,liblog-0c18bbe7f7b3152f.rlib,libbytes-54e1836e6e796e5f.rlib,libserde-08bf56e3be229b6b.rlib,libaya_ebpf-d1e19b3d7ce546b7.rlib,libaya_ebpf_bindings-a4277a8e5542a5f7.rlib,libaya_ebpf_cty-92a90813681baf20.rlib}" "/home/thomas/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/{libstd-6273572f18644c87.rlib,libpanic_unwind-267e668abf74a283.rlib,libobject-ec6154ccae37a33e.rlib,libmemchr-500edd5521c440d4.rlib,libaddr2line-86d8d9428792e8ef.rlib,libgimli-10f06487503767c2.rlib,librustc_demangle-6a38424de1e5bca5.rlib,libstd_detect-de9763ea1c19dca3.rlib,libhashbrown-a7f5bb2f736d3c49.rlib,librustc_std_workspace_alloc-7e368919bdc4a44c.rlib,libminiz_oxide-376454d49910c786.rlib,libadler-fa99f5692b5dce85.rlib,libunwind-91cafdaf16f7fe40.rlib,libcfg_if-f7ee3f1ea78d9dae.rlib,liblibc-d3a35665f881365a.rlib,liballoc-715bc629a88bca60.rlib,librustc_std_workspace_core-ae70165d1278cff7.rlib,libcore-406129d0e3fbc101.rlib,libcompiler_builtins-1af05515ab19524a.rlib}" "-Wl,-Bdynamic" "-lgcc_s" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "/home/thomas/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "/home/thomas/src/github.com/aya-rs/aya/target/debug/deps/bpf_probe_read-449d9b05f6edf685" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-nodefaultlibs" "-fuse-ld=/nix/store/gbhcq80nkval1l1ssjs0c8sgrw83b7gc-mold-wrapper-2.34.1/bin/mold"
  = note: some arguments are omitted. use `--verbose` to show all linker arguments
  = note: mold: error: undefined symbol: main
          >>> referenced by /nix/store/maxa3xhmxggrc5v2vc0c3pjb79hjlkp9-glibc-2.40-66/lib/Scrt1.o:(.text)
          >>>               /nix/store/maxa3xhmxggrc5v2vc0c3pjb79hjlkp9-glibc-2.40-66/lib/Scrt1.o:(_start)
          clang: error: linker command failed with exit code 1 (use -v to see invocation)


error: could not compile `integration-ebpf` (bin "bpf_probe_read") due to 1 previous error

Based on the above, it appears to me that "Binaries don't compile without a main function and therefore we need a stub when compiling for the host architecture" is a pretty solid explanation but I am open to other suggestions if you have a specific wording in mind.

}
};
}

#[inline]
fn insert<K, V>(
def: *mut bindings::bpf_map_def,
Expand Down
7 changes: 4 additions & 3 deletions test/integration-ebpf/src/bpf_probe_read.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![no_std]
#![no_main]
#![cfg_attr(target_arch = "bpf", no_std)]
#![cfg_attr(target_arch = "bpf", no_main)]
aya_ebpf::main_stub!();

use aya_ebpf::{
helpers::{bpf_probe_read_kernel_str_bytes, bpf_probe_read_user_str_bytes},
Expand All @@ -8,7 +9,7 @@ use aya_ebpf::{
programs::ProbeContext,
};
use integration_common::bpf_probe_read::{RESULT_BUF_LEN, TestResult};
#[cfg(not(test))]
#[cfg(target_arch = "bpf")]
extern crate ebpf_panic;

fn read_str_bytes(
Expand Down
7 changes: 4 additions & 3 deletions test/integration-ebpf/src/log.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
#![no_std]
#![no_main]
#![cfg_attr(target_arch = "bpf", no_std)]
#![cfg_attr(target_arch = "bpf", no_main)]
aya_ebpf::main_stub!();

use core::net::{IpAddr, Ipv4Addr, Ipv6Addr};

use aya_ebpf::{macros::uprobe, programs::ProbeContext};
use aya_log_ebpf::{debug, error, info, trace, warn};
#[cfg(not(test))]
#[cfg(target_arch = "bpf")]
extern crate ebpf_panic;

#[uprobe]
Expand Down
7 changes: 4 additions & 3 deletions test/integration-ebpf/src/map_test.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
// Socket Filter program for testing with an arbitrary program with maps.
// This is mainly used in tests with consideration for old kernels.

#![no_std]
#![no_main]
#![cfg_attr(target_arch = "bpf", no_std)]
#![cfg_attr(target_arch = "bpf", no_main)]
aya_ebpf::main_stub!();

use aya_ebpf::{
macros::{map, socket_filter},
maps::{Array, HashMap},
programs::SkBuffContext,
};
#[cfg(not(test))]
#[cfg(target_arch = "bpf")]
extern crate ebpf_panic;

// Introduced in kernel v3.19.
Expand Down
7 changes: 4 additions & 3 deletions test/integration-ebpf/src/memmove_test.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![no_std]
#![no_main]
#![cfg_attr(target_arch = "bpf", no_std)]
#![cfg_attr(target_arch = "bpf", no_main)]
aya_ebpf::main_stub!();

use core::mem;

Expand All @@ -9,7 +10,7 @@ use aya_ebpf::{
maps::HashMap,
programs::XdpContext,
};
#[cfg(not(test))]
#[cfg(target_arch = "bpf")]
extern crate ebpf_panic;
use network_types::{
eth::{EthHdr, EtherType},
Expand Down
7 changes: 4 additions & 3 deletions test/integration-ebpf/src/name_test.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
#![no_std]
#![no_main]
#![cfg_attr(target_arch = "bpf", no_std)]
#![cfg_attr(target_arch = "bpf", no_main)]
aya_ebpf::main_stub!();

use aya_ebpf::{bindings::xdp_action, macros::xdp, programs::XdpContext};
#[cfg(not(test))]
#[cfg(target_arch = "bpf")]
extern crate ebpf_panic;

#[xdp]
Expand Down
7 changes: 4 additions & 3 deletions test/integration-ebpf/src/pass.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
#![no_std]
#![no_main]
#![cfg_attr(target_arch = "bpf", no_std)]
#![cfg_attr(target_arch = "bpf", no_main)]
aya_ebpf::main_stub!();

use aya_ebpf::{bindings::xdp_action, macros::xdp, programs::XdpContext};
#[cfg(not(test))]
#[cfg(target_arch = "bpf")]
extern crate ebpf_panic;

// Note: the `frags` attribute causes this probe to be incompatible with kernel versions < 5.18.0.
Expand Down
7 changes: 4 additions & 3 deletions test/integration-ebpf/src/raw_tracepoint.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#![no_std]
#![no_main]
#![cfg_attr(target_arch = "bpf", no_std)]
#![cfg_attr(target_arch = "bpf", no_main)]
aya_ebpf::main_stub!();

use aya_ebpf::{
macros::{map, raw_tracepoint},
maps::Array,
programs::RawTracePointContext,
};
#[cfg(not(test))]
#[cfg(target_arch = "bpf")]
extern crate ebpf_panic;
use integration_common::raw_tracepoint::SysEnterEvent;

Expand Down
7 changes: 4 additions & 3 deletions test/integration-ebpf/src/redirect.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
#![no_std]
#![no_main]
#![cfg_attr(target_arch = "bpf", no_std)]
#![cfg_attr(target_arch = "bpf", no_main)]
aya_ebpf::main_stub!();

use aya_ebpf::{
bindings::xdp_action,
macros::{map, xdp},
maps::{Array, CpuMap, DevMap, DevMapHash, XskMap},
programs::XdpContext,
};
#[cfg(not(test))]
#[cfg(target_arch = "bpf")]
extern crate ebpf_panic;

#[map]
Expand Down
7 changes: 4 additions & 3 deletions test/integration-ebpf/src/relocations.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![no_std]
#![no_main]
#![cfg_attr(target_arch = "bpf", no_std)]
#![cfg_attr(target_arch = "bpf", no_main)]
aya_ebpf::main_stub!();

use core::hint;

Expand All @@ -8,7 +9,7 @@ use aya_ebpf::{
maps::Array,
programs::ProbeContext,
};
#[cfg(not(test))]
#[cfg(target_arch = "bpf")]
extern crate ebpf_panic;

#[map]
Expand Down
7 changes: 4 additions & 3 deletions test/integration-ebpf/src/ring_buf.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
#![no_std]
#![no_main]
#![cfg_attr(target_arch = "bpf", no_std)]
#![cfg_attr(target_arch = "bpf", no_main)]
aya_ebpf::main_stub!();

use aya_ebpf::{
macros::{map, uprobe},
maps::{PerCpuArray, RingBuf},
programs::ProbeContext,
};
use integration_common::ring_buf::Registers;
#[cfg(not(test))]
#[cfg(target_arch = "bpf")]
extern crate ebpf_panic;

#[map]
Expand Down
7 changes: 4 additions & 3 deletions test/integration-ebpf/src/simple_prog.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Socket Filter program for testing with an arbitrary program.
// This is mainly used in tests with consideration for old kernels.

#![no_std]
#![no_main]
#![cfg_attr(target_arch = "bpf", no_std)]
#![cfg_attr(target_arch = "bpf", no_main)]
aya_ebpf::main_stub!();

use aya_ebpf::{macros::socket_filter, programs::SkBuffContext};
#[cfg(not(test))]
#[cfg(target_arch = "bpf")]
extern crate ebpf_panic;

// Introduced in kernel v3.19.
Expand Down
7 changes: 4 additions & 3 deletions test/integration-ebpf/src/strncmp.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![no_std]
#![no_main]
#![cfg_attr(target_arch = "bpf", no_std)]
#![cfg_attr(target_arch = "bpf", no_main)]
aya_ebpf::main_stub!();

use aya_ebpf::{
cty::c_long,
Expand All @@ -9,7 +10,7 @@ use aya_ebpf::{
programs::ProbeContext,
};
use integration_common::strncmp::TestResult;
#[cfg(not(test))]
#[cfg(target_arch = "bpf")]
extern crate ebpf_panic;

#[map]
Expand Down
7 changes: 4 additions & 3 deletions test/integration-ebpf/src/tcx.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
#![no_std]
#![no_main]
#![cfg_attr(target_arch = "bpf", no_std)]
#![cfg_attr(target_arch = "bpf", no_main)]
aya_ebpf::main_stub!();

use aya_ebpf::{bindings::tcx_action_base::TCX_NEXT, macros::classifier, programs::TcContext};
#[cfg(not(test))]
#[cfg(target_arch = "bpf")]
extern crate ebpf_panic;

#[classifier]
Expand Down
7 changes: 4 additions & 3 deletions test/integration-ebpf/src/test.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![no_std]
#![no_main]
#![cfg_attr(target_arch = "bpf", no_std)]
#![cfg_attr(target_arch = "bpf", no_main)]
aya_ebpf::main_stub!();

use aya_ebpf::{
bindings::{bpf_ret_code, xdp_action},
Expand All @@ -8,7 +9,7 @@ use aya_ebpf::{
FlowDissectorContext, ProbeContext, RetProbeContext, TracePointContext, XdpContext,
},
};
#[cfg(not(test))]
#[cfg(target_arch = "bpf")]
extern crate ebpf_panic;

#[xdp]
Expand Down
7 changes: 4 additions & 3 deletions test/integration-ebpf/src/two_progs.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// Two programs in the same ELF section

#![no_std]
#![no_main]
#![cfg_attr(target_arch = "bpf", no_std)]
#![cfg_attr(target_arch = "bpf", no_main)]
aya_ebpf::main_stub!();

use aya_ebpf::{macros::tracepoint, programs::TracePointContext};
#[cfg(not(test))]
#[cfg(target_arch = "bpf")]
extern crate ebpf_panic;

#[tracepoint]
Expand Down
7 changes: 4 additions & 3 deletions test/integration-ebpf/src/uprobe_cookie.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
#![no_std]
#![no_main]
#![cfg_attr(target_arch = "bpf", no_std)]
#![cfg_attr(target_arch = "bpf", no_main)]
aya_ebpf::main_stub!();

use aya_ebpf::{
EbpfContext as _, helpers,
macros::{map, uprobe},
maps::RingBuf,
programs::ProbeContext,
};
#[cfg(not(test))]
#[cfg(target_arch = "bpf")]
extern crate ebpf_panic;

#[map]
Expand Down
7 changes: 4 additions & 3 deletions test/integration-ebpf/src/xdp_sec.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
#![no_std]
#![no_main]
#![cfg_attr(target_arch = "bpf", no_std)]
#![cfg_attr(target_arch = "bpf", no_main)]
aya_ebpf::main_stub!();

use aya_ebpf::{bindings::xdp_action::XDP_PASS, macros::xdp, programs::XdpContext};
#[cfg(not(test))]
#[cfg(target_arch = "bpf")]
extern crate ebpf_panic;

macro_rules! probe {
Expand Down
1 change: 1 addition & 0 deletions xtask/public-api/aya-ebpf.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2728,6 +2728,7 @@ pub fn aya_ebpf::programs::xdp::XdpContext::borrow_mut(&mut self) -> &mut T
impl<T> core::convert::From<T> for aya_ebpf::programs::xdp::XdpContext
pub fn aya_ebpf::programs::xdp::XdpContext::from(t: T) -> T
pub macro aya_ebpf::bpf_printk!
pub macro aya_ebpf::main_stub!
pub struct aya_ebpf::PtRegs
impl aya_ebpf::PtRegs
pub fn aya_ebpf::PtRegs::arg<T: aya_ebpf::args::FromPtRegs>(&self, n: usize) -> core::option::Option<T>
Expand Down
Loading