@@ -113,7 +113,7 @@ pub fn features() -> &'static Features {
113113/// ```
114114#[ derive( Debug ) ]
115115pub struct EbpfLoader < ' a > {
116- btf : Option < Cow < ' a , Btf > > ,
116+ btf : TargetBtf < ' a > ,
117117 default_map_pin_directory : Option < PathBuf > ,
118118 globals : HashMap < & ' a str , ( & ' a [ u8 ] , bool ) > ,
119119 // Max entries overrides the max_entries field of the map that matches the provided name
@@ -128,6 +128,38 @@ pub struct EbpfLoader<'a> {
128128 allow_unsupported_maps : bool ,
129129}
130130
131+ #[ derive( Debug ) ]
132+ enum TargetBtf < ' a > {
133+ System ,
134+ Parsed ( Cow < ' a , Btf > ) ,
135+ Source ( BtfSource ) ,
136+ }
137+
138+ struct BtfSource {
139+ read : Box <
140+ dyn Fn ( ) -> io:: Result < Vec < u8 > >
141+ // Preserve `EbpfLoader`'s existing auto traits.
142+ + Send
143+ + Sync
144+ + std:: panic:: RefUnwindSafe
145+ + std:: panic:: UnwindSafe
146+ // Keep captured state independent of `EbpfLoader`'s borrowed configuration.
147+ + ' static ,
148+ > ,
149+ }
150+
151+ impl std:: fmt:: Debug for BtfSource {
152+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
153+ f. debug_struct ( "BtfSource" ) . finish_non_exhaustive ( )
154+ }
155+ }
156+
157+ impl BtfSource {
158+ fn read ( & self ) -> io:: Result < Vec < u8 > > {
159+ ( self . read ) ( )
160+ }
161+ }
162+
131163/// Builder style API for advanced loading of eBPF programs.
132164#[ deprecated( since = "0.13.0" , note = "use `EbpfLoader` instead" ) ]
133165pub type BpfLoader < ' a > = EbpfLoader < ' a > ;
@@ -157,7 +189,7 @@ impl<'a> EbpfLoader<'a> {
157189 /// Creates a new loader instance.
158190 pub fn new ( ) -> Self {
159191 Self {
160- btf : None ,
192+ btf : TargetBtf :: System ,
161193 default_map_pin_directory : None ,
162194 globals : HashMap :: new ( ) ,
163195 max_entries : HashMap :: new ( ) ,
@@ -174,6 +206,10 @@ impl<'a> EbpfLoader<'a> {
174206 /// the object contains CO-RE relocations or typed kernel symbols. Use this
175207 /// method to load `BTF` from a custom location.
176208 ///
209+ /// The parsed `BTF` can be shared across multiple loaders, avoiding repeated
210+ /// parsing. To lazily read and parse target `BTF`, use
211+ /// [`EbpfLoader::btf_source`].
212+ ///
177213 /// # Example
178214 ///
179215 /// ```no_run
@@ -190,7 +226,48 @@ impl<'a> EbpfLoader<'a> {
190226 /// # Ok::<(), aya::EbpfError>(())
191227 /// ```
192228 pub fn btf ( & mut self , btf : & ' a Btf ) -> & mut Self {
193- self . btf = Some ( Cow :: Borrowed ( btf) ) ;
229+ self . btf = TargetBtf :: Parsed ( Cow :: Borrowed ( btf) ) ;
230+ self
231+ }
232+
233+ /// Sets a factory for lazily reading the target [BTF](Btf) info.
234+ ///
235+ /// The factory is called only when the object contains CO-RE relocations or
236+ /// typed kernel symbols. Once parsed, the target `BTF` is cached and reused
237+ /// by this loader. To share one parsed value across multiple loaders, use
238+ /// [`EbpfLoader::btf`] instead.
239+ ///
240+ /// # Example
241+ ///
242+ /// ```no_run
243+ /// use std::fs::File;
244+ ///
245+ /// use aya::EbpfLoader;
246+ ///
247+ /// let bpf = EbpfLoader::new()
248+ /// .btf_source(|| File::open("/custom_btf_file"))
249+ /// .load_file("file.o")?;
250+ ///
251+ /// # Ok::<(), aya::EbpfError>(())
252+ /// ```
253+ pub fn btf_source < F , R > ( & mut self , source : F ) -> & mut Self
254+ where
255+ F : Fn ( ) -> io:: Result < R >
256+ + Send
257+ + Sync
258+ + std:: panic:: RefUnwindSafe
259+ + std:: panic:: UnwindSafe
260+ + ' static ,
261+ R : io:: Read ,
262+ {
263+ self . btf = TargetBtf :: Source ( BtfSource {
264+ read : Box :: new ( move || {
265+ let mut reader = source ( ) ?;
266+ let mut data = Vec :: new ( ) ;
267+ reader. read_to_end ( & mut data) ?;
268+ Ok ( data)
269+ } ) ,
270+ } ) ;
194271 self
195272 }
196273
@@ -493,29 +570,51 @@ impl<'a> EbpfLoader<'a> {
493570 None
494571 } ;
495572
496- if btf. is_none ( ) && ( obj. has_btf_relocations ( ) || obj. has_typed_ksyms ( ) ) {
497- match Btf :: from_sys_fs ( ) {
498- Ok ( kernel_btf) => * btf = Some ( Cow :: Owned ( kernel_btf) ) ,
499- Err ( err) => {
500- // CO-RE relocations and strong typed ksyms cannot be resolved without kernel
501- // BTF, so preserve the original loading error.
502- if obj. has_btf_relocations ( ) || obj. has_strong_typed_ksyms ( ) {
503- return Err ( err. into ( ) ) ;
504- }
573+ let target_btf: Option < & Btf > = if obj. has_btf_relocations ( ) || obj. has_typed_ksyms ( ) {
574+ let load_result = match btf {
575+ TargetBtf :: System => Some ( Btf :: from_sys_fs ( ) . map_err ( EbpfError :: from) ) ,
576+ TargetBtf :: Source ( source) => Some (
577+ source
578+ . read ( )
579+ . map_err ( EbpfError :: BtfSourceError )
580+ . and_then ( |data| {
581+ Btf :: parse ( & data, obj. endianness ) . map_err ( EbpfError :: BtfError )
582+ } ) ,
583+ ) ,
584+ TargetBtf :: Parsed ( _) => None ,
585+ } ;
586+ if let Some ( load_result) = load_result {
587+ match load_result {
588+ Ok ( target_btf) => * btf = TargetBtf :: Parsed ( Cow :: Owned ( target_btf) ) ,
589+ Err ( err) => {
590+ // CO-RE relocations and strong typed ksyms cannot be resolved without
591+ // target BTF, so preserve the original loading error.
592+ if obj. has_btf_relocations ( ) || obj. has_strong_typed_ksyms ( ) {
593+ return Err ( err) ;
594+ }
505595
506- // Unresolved weak typed ksyms are allowed and handled during extern
507- // relocation.
508- warn ! ( "kernel BTF is unavailable; weak typed ksyms will be unresolved: {err}" )
596+ // Unresolved weak typed ksyms are allowed and handled during extern
597+ // relocation.
598+ warn ! (
599+ "target BTF is unavailable; weak typed ksyms will be unresolved: {err}"
600+ )
601+ }
509602 }
510603 }
511- }
512604
513- let btf = btf. as_deref ( ) ;
514- if let Some ( btf) = btf {
515- obj. relocate_btf ( btf) ?;
605+ match btf {
606+ TargetBtf :: Parsed ( btf) => Some ( btf) ,
607+ _ => None ,
608+ }
609+ } else {
610+ None
611+ } ;
612+
613+ if let Some ( target_btf) = target_btf {
614+ obj. relocate_btf ( target_btf) ?;
516615 }
517616
518- obj. resolve_externs ( btf ) ?;
617+ obj. resolve_externs ( target_btf ) ?;
519618
520619 const fn is_map_of_maps ( map_type : bpf_map_type ) -> bool {
521620 matches ! (
@@ -1244,6 +1343,10 @@ pub enum EbpfError {
12441343 error : io:: Error ,
12451344 } ,
12461345
1346+ /// Error reading target BTF
1347+ #[ error( "error reading BTF source" ) ]
1348+ BtfSourceError ( #[ source] io:: Error ) ,
1349+
12471350 /// Unexpected pinning type
12481351 #[ error( "unexpected pinning type {name}" ) ]
12491352 UnexpectedPinningType {
0 commit comments