@@ -294,10 +294,7 @@ pub fn patch_elf_map_fds(elf: &[u8], map_fds: &HashMap<String, i32>) -> Result<V
294294
295295/// BPF helper function IDs for helpers used in the relay XDP program.
296296/// Values from Linux kernel `include/uapi/linux/bpf.h` (stable, append-only ABI).
297- static BPF_HELPER_IDS : & [ ( & str , i32 ) ] = & [
298- ( "bpf_xdp_adjust_head" , 44 ) ,
299- ( "bpf_xdp_adjust_tail" , 65 ) ,
300- ] ;
297+ static BPF_HELPER_IDS : & [ ( & str , i32 ) ] = & [ ( "bpf_xdp_adjust_head" , 44 ) , ( "bpf_xdp_adjust_tail" , 65 ) ] ;
301298
302299/// Patches standard BPF helper call instructions in the `xdp` section.
303300///
@@ -377,10 +374,7 @@ pub fn patch_elf_bpf_helpers(elf: &[u8]) -> Result<Vec<u8>> {
377374
378375 let insn_file_pos = xdp_file_offset + r_offset;
379376 if insn_file_pos + BPF_INSN_SIZE > patched. len ( ) {
380- bail ! (
381- "BPF helper call at xdp+0x{:x} is out of bounds" ,
382- r_offset
383- ) ;
377+ bail ! ( "BPF helper call at xdp+0x{:x} is out of bounds" , r_offset) ;
384378 }
385379
386380 // Verify BPF_CALL opcode at byte 0 of the instruction.
@@ -409,8 +403,7 @@ pub fn patch_elf_bpf_helpers(elf: &[u8]) -> Result<Vec<u8>> {
409403 patched[ insn_file_pos + 1 ] = regs_byte & 0x0f ;
410404
411405 // Patch imm: write helper ID as little-endian i32 at bytes 4-7.
412- patched[ insn_file_pos + 4 ..insn_file_pos + 8 ]
413- . copy_from_slice ( & helper_id. to_le_bytes ( ) ) ;
406+ patched[ insn_file_pos + 4 ..insn_file_pos + 8 ] . copy_from_slice ( & helper_id. to_le_bytes ( ) ) ;
414407
415408 patched_count += 1 ;
416409 }
@@ -622,7 +615,9 @@ pub fn find_module_btf(module_name: &str) -> Result<(i32, Vec<u8>)> {
622615 // Found the module - fetch the raw BTF bytes
623616 log:: info!(
624617 "find_module_btf: found '{}' BTF id={} btf_size={} bytes" ,
625- found_name, next_id, btf_size
618+ found_name,
619+ next_id,
620+ btf_size
626621 ) ;
627622 if btf_size == 0 {
628623 bail ! (
@@ -745,7 +740,12 @@ pub fn parse_btf_func_ids(btf: &[u8], names: &[&str]) -> Result<HashMap<String,
745740
746741 log:: info!(
747742 "parse_btf_func_ids: btf_len={} hdr_len={} type_off={} type_len={} str_off={} str_len={}" ,
748- btf. len( ) , hdr_len, type_off, type_len, str_off, str_len
743+ btf. len( ) ,
744+ hdr_len,
745+ type_off,
746+ type_len,
747+ str_off,
748+ str_len
749749 ) ;
750750
751751 let type_section_start = hdr_len + type_off;
@@ -756,116 +756,166 @@ pub fn parse_btf_func_ids(btf: &[u8], names: &[&str]) -> Result<HashMap<String,
756756 if type_section_end > btf. len ( ) || str_section_end > btf. len ( ) {
757757 bail ! (
758758 "BTF type/string section out of bounds: btf_len={} type={}..{} str={}..{}" ,
759- btf. len( ) , type_section_start, type_section_end, str_section_start, str_section_end
759+ btf. len( ) ,
760+ type_section_start,
761+ type_section_end,
762+ str_section_start,
763+ str_section_end
760764 ) ;
761765 }
762766
763767 let type_bytes = & btf[ type_section_start..type_section_end] ;
764768 let str_bytes = & btf[ str_section_start..str_section_end] ;
765769
766- // Compute base type ID offset from vmlinux BTF so the IDs we return are
767- // the correct "global" IDs that the BPF verifier expects when resolving
768- // kfunc calls via fd_array. Module BTF is split BTF: its raw bytes only
769- // contain the module-local types, but the kernel numbers them starting at
770- // vmlinux_nr_types + 1. Reading vmlinux from sysfs is the most reliable
771- // way to get the base count.
772- let base_nr_types = get_vmlinux_nr_types ( ) . unwrap_or_else ( |e| {
773- log:: warn!( "failed to get vmlinux type count ({}), assuming base_nr_types=0; kfunc IDs may be wrong" , e) ;
774- 0
770+ // Module BTF is split BTF (kernel 5.13+). The raw bytes returned by
771+ // BPF_OBJ_GET_INFO_BY_FD contain only the module-local type and string
772+ // sections. HOWEVER, name_off values in module type entries are indices
773+ // into the COMBINED string table (vmlinux strings || module strings), not
774+ // into the module-local string section alone. Similarly, type IDs must be
775+ // the global IDs (vmlinux_nr_types + local_type_id) for the BPF verifier.
776+ //
777+ // We read /sys/kernel/btf/vmlinux to obtain:
778+ // base_nr_types - number of types in vmlinux (type ID offset)
779+ // base_str_len - size of vmlinux string section (name_off adjustment)
780+ // base_strings - vmlinux string bytes (for resolving names from vmlinux)
781+ let ( base_nr_types, base_str_len, base_strings) = load_vmlinux_btf_info ( ) . unwrap_or_else ( |e| {
782+ log:: warn!(
783+ "failed to load vmlinux BTF ({}); kfunc type IDs and string offsets may be wrong" ,
784+ e
785+ ) ;
786+ ( 0 , 0 , Vec :: new ( ) )
775787 } ) ;
776788 log:: info!(
777- "parse_btf_func_ids: vmlinux base_nr_types={} -> module type IDs start at {}" ,
778- base_nr_types, base_nr_types + 1
789+ "parse_btf_func_ids: vmlinux base_nr_types={} base_str_len={} -> module IDs start at {}" ,
790+ base_nr_types,
791+ base_str_len,
792+ base_nr_types + 1
779793 ) ;
780794
781795 let mut result = HashMap :: new ( ) ;
782796 let mut pos: usize = 0 ;
783- let mut local_type_id: u32 = 1 ; // 1-based within module BTF section
797+ let mut local_type_id: u32 = 1 ; // 1-based within this module's type section
784798 let mut func_count: u32 = 0 ;
785799
786800 while pos + 12 <= type_bytes. len ( ) {
787- let name_off = u32:: from_le_bytes ( type_bytes[ pos..pos + 4 ] . try_into ( ) . expect ( "4" ) ) as usize ;
801+ let raw_name_off =
802+ u32:: from_le_bytes ( type_bytes[ pos..pos + 4 ] . try_into ( ) . expect ( "4" ) ) as usize ;
788803 let info = u32:: from_le_bytes ( type_bytes[ pos + 4 ..pos + 8 ] . try_into ( ) . expect ( "4" ) ) ;
789804 let kind = ( info >> 24 ) & 0x1f ;
790805 let vlen = ( info & 0xffff ) as usize ;
791806
792- // BTF_KIND_FUNC = 12 - function declaration (0 extra bytes)
807+ // BTF_KIND_FUNC = 12 - function declaration (no extra bytes)
793808 if kind == 12 {
794809 func_count += 1 ;
795- // Global BTF type ID = vmlinux base count + local position
796810 let global_type_id = base_nr_types + local_type_id;
797- if name_off < str_bytes . len ( ) {
798- let name_bytes = & str_bytes [ name_off.. ] ;
799- if let Ok ( cstr ) = std :: ffi :: CStr :: from_bytes_until_nul ( name_bytes ) {
800- let name = cstr . to_string_lossy ( ) ;
801- log :: info! (
802- "parse_btf_func_ids: FUNC local_id={} global_id={} name='{}'" ,
803- local_type_id , global_type_id , name
804- ) ;
805- if names . contains ( & name . as_ref ( ) ) {
806- result . insert ( name . into_owned ( ) , global_type_id ) ;
807- }
811+
812+ // Resolve the name from the appropriate string section.
813+ // name_off is a global offset into (vmlinux_strings || module_strings).
814+ let name_opt = if raw_name_off < base_str_len {
815+ // Name is in the vmlinux string section
816+ if raw_name_off < base_strings . len ( ) {
817+ std :: ffi :: CStr :: from_bytes_until_nul ( & base_strings [ raw_name_off.. ] )
818+ . ok ( )
819+ . map ( |c| c . to_string_lossy ( ) . into_owned ( ) )
820+ } else {
821+ None
808822 }
809823 } else {
810- log:: warn!(
811- "parse_btf_func_ids: FUNC local_id={} name_off={} out of str_bytes (len={})" ,
812- local_type_id, name_off, str_bytes. len( )
824+ // Name is in the module-local string section
825+ let local_off = raw_name_off - base_str_len;
826+ if local_off < str_bytes. len ( ) {
827+ std:: ffi:: CStr :: from_bytes_until_nul ( & str_bytes[ local_off..] )
828+ . ok ( )
829+ . map ( |c| c. to_string_lossy ( ) . into_owned ( ) )
830+ } else {
831+ log:: warn!(
832+ "parse_btf_func_ids: FUNC local_id={} raw_name_off={} local_off={} out of str_bytes (len={}), base_str_len={}" ,
833+ local_type_id, raw_name_off, local_off, str_bytes. len( ) , base_str_len
834+ ) ;
835+ None
836+ }
837+ } ;
838+
839+ if let Some ( name) = name_opt {
840+ log:: info!(
841+ "parse_btf_func_ids: FUNC local_id={} global_id={} name='{}'" ,
842+ local_type_id,
843+ global_type_id,
844+ name
813845 ) ;
846+ if names. contains ( & name. as_str ( ) ) {
847+ result. insert ( name, global_type_id) ;
848+ }
814849 }
815850 }
816851
817- // Advance past this type entry: 12 base bytes + extra bytes per kind
818852 let extra = btf_kind_extra_bytes ( kind, vlen) ;
819853 log:: debug!(
820854 "parse_btf_func_ids: pos={} kind={} vlen={} extra={} local_id={}" ,
821- pos, kind, vlen, extra, local_type_id
855+ pos,
856+ kind,
857+ vlen,
858+ extra,
859+ local_type_id
822860 ) ;
823861 pos += 12 + extra;
824862 local_type_id += 1 ;
825863 }
826864
827865 log:: info!(
828866 "parse_btf_func_ids: iterated {} types, found {} FUNC entries, matched {}/{}" ,
829- local_type_id - 1 , func_count, result. len( ) , names. len( )
867+ local_type_id - 1 ,
868+ func_count,
869+ result. len( ) ,
870+ names. len( )
830871 ) ;
831872
832- // Verify all requested names were found
833873 for name in names {
834874 if !result. contains_key ( * name) {
835875 bail ! (
836- "BTF func '{}' not found in module BTF (base_nr_types={}, {} types parsed, {} FUNC entries ) - module may need rebuild" ,
837- name, base_nr_types, local_type_id - 1 , func_count
876+ "BTF func '{}' not found in module BTF (base_nr_types={} base_str_len= {} {} types {} FUNCs ) - module may need rebuild" ,
877+ name, base_nr_types, base_str_len , local_type_id - 1 , func_count
838878 ) ;
839879 }
840880 }
841881
842882 Ok ( result)
843883}
844884
845- /// Counts the number of types in the vmlinux BTF by reading /sys/kernel/btf/vmlinux.
846- /// Returns the total type count so module BTF type IDs can be correctly offset.
847- fn get_vmlinux_nr_types ( ) -> Result < u32 > {
848- let vmlinux_btf = std:: fs:: read ( "/sys/kernel/btf/vmlinux" )
885+ /// Parses /sys/kernel/btf/vmlinux and returns:
886+ /// (nr_types, str_len, str_bytes)
887+ /// nr_types - total number of types (used as base offset for module type IDs)
888+ /// str_len - total length of vmlinux string section (used as base offset for module name_off)
889+ /// str_bytes - vmlinux string section bytes (for resolving names that live in vmlinux)
890+ fn load_vmlinux_btf_info ( ) -> Result < ( u32 , usize , Vec < u8 > ) > {
891+ let btf = std:: fs:: read ( "/sys/kernel/btf/vmlinux" )
849892 . context ( "failed to read /sys/kernel/btf/vmlinux" ) ?;
850893
851- if vmlinux_btf . len ( ) < 24 {
852- bail ! ( "vmlinux BTF too short" ) ;
894+ if btf . len ( ) < 24 {
895+ bail ! ( "vmlinux BTF too short ({} bytes)" , btf . len ( ) ) ;
853896 }
854897
855- let hdr_len = u32:: from_le_bytes ( vmlinux_btf[ 4 ..8 ] . try_into ( ) . expect ( "4" ) ) as usize ;
856- let type_off = u32:: from_le_bytes ( vmlinux_btf[ 8 ..12 ] . try_into ( ) . expect ( "4" ) ) as usize ;
857- let type_len = u32:: from_le_bytes ( vmlinux_btf[ 12 ..16 ] . try_into ( ) . expect ( "4" ) ) as usize ;
898+ let hdr_len = u32:: from_le_bytes ( btf[ 4 ..8 ] . try_into ( ) . expect ( "4" ) ) as usize ;
899+ let type_off = u32:: from_le_bytes ( btf[ 8 ..12 ] . try_into ( ) . expect ( "4" ) ) as usize ;
900+ let type_len = u32:: from_le_bytes ( btf[ 12 ..16 ] . try_into ( ) . expect ( "4" ) ) as usize ;
901+ let str_off = u32:: from_le_bytes ( btf[ 16 ..20 ] . try_into ( ) . expect ( "4" ) ) as usize ;
902+ let str_len = u32:: from_le_bytes ( btf[ 20 ..24 ] . try_into ( ) . expect ( "4" ) ) as usize ;
858903
859904 let type_start = hdr_len + type_off;
860905 let type_end = type_start + type_len;
861- if type_end > vmlinux_btf. len ( ) {
862- bail ! ( "vmlinux BTF type section out of bounds" ) ;
906+ let str_start = hdr_len + str_off;
907+ let str_end = str_start + str_len;
908+
909+ if type_end > btf. len ( ) || str_end > btf. len ( ) {
910+ bail ! ( "vmlinux BTF sections out of bounds (btf_len={})" , btf. len( ) ) ;
863911 }
864912
865- let type_bytes = & vmlinux_btf[ type_start..type_end] ;
913+ let type_bytes = & btf[ type_start..type_end] ;
914+ let str_bytes = btf[ str_start..str_end] . to_vec ( ) ;
915+
916+ // Count types by iterating the type section
866917 let mut pos = 0usize ;
867918 let mut nr_types: u32 = 0 ;
868-
869919 while pos + 12 <= type_bytes. len ( ) {
870920 let info = u32:: from_le_bytes ( type_bytes[ pos + 4 ..pos + 8 ] . try_into ( ) . expect ( "4" ) ) ;
871921 let kind = ( info >> 24 ) & 0x1f ;
@@ -875,7 +925,7 @@ fn get_vmlinux_nr_types() -> Result<u32> {
875925 nr_types += 1 ;
876926 }
877927
878- Ok ( nr_types)
928+ Ok ( ( nr_types, str_len , str_bytes ) )
879929}
880930
881931/// Returns extra bytes after the 12-byte type base depending on BTF kind.
0 commit comments