@@ -24,6 +24,7 @@ use crate::{
2424 info:: { FuncSecInfo , LineSecInfo } ,
2525 relocation:: Relocation ,
2626 } ,
27+ extern_types:: ExternCollection ,
2728 generated:: { btf_ext_header, btf_header} ,
2829 util:: { HashMap , bytes_of} ,
2930} ;
@@ -264,6 +265,8 @@ pub struct Btf {
264265 strings : Vec < u8 > ,
265266 types : BtfTypes ,
266267 _endianness : Endianness ,
268+ /// Extern functions parsed from ksyms section
269+ pub ( crate ) externs : ExternCollection ,
267270}
268271
269272fn add_type ( header : & mut btf_header , types : & mut BtfTypes , btf_type : BtfType ) -> u32 {
@@ -292,6 +295,7 @@ impl Btf {
292295 strings : vec ! [ 0 ] ,
293296 types : BtfTypes :: default ( ) ,
294297 _endianness : Endianness :: default ( ) ,
298+ externs : ExternCollection :: new ( ) ,
295299 }
296300 }
297301
@@ -364,6 +368,7 @@ impl Btf {
364368 strings,
365369 types,
366370 _endianness : endianness,
371+ externs : ExternCollection :: new ( ) ,
367372 } )
368373 }
369374
@@ -501,6 +506,9 @@ impl Btf {
501506 symbol_offsets : & HashMap < String , u64 > ,
502507 features : & BtfFeatures ,
503508 ) -> Result < ( ) , BtfError > {
509+ if !self . externs . is_empty ( ) {
510+ self . fixup_ksyms_datasec ( self . externs . datasec_id , self . externs . dummy_ksym_var_id ) ?;
511+ }
504512 let enum64_placeholder_id = OnceCell :: new ( ) ;
505513 let filler_var_id = OnceCell :: new ( ) ;
506514 let mut types = mem:: take ( & mut self . types ) ;
@@ -812,6 +820,128 @@ impl Btf {
812820 self . types = types;
813821 Ok ( ( ) )
814822 }
823+
824+ /// Fixes up BTF for `.ksyms` datasec entries containing extern kernel symbol and
825+ /// makes it acceptable by the kernel:
826+ ///
827+ /// * Changes linkage of extern functions to `GLOBAL`, fixes parameter names, injects
828+ /// a dummy variable representing them in datasec.
829+ /// * Changes linkage of extern variables to `GLOBAL_ALLOCATED`, replaces their type
830+ /// with `int`.
831+ pub ( crate ) fn fixup_ksyms_datasec (
832+ & mut self ,
833+ datasec_id : Option < u32 > ,
834+ dummy_var_id : Option < u32 > ,
835+ ) -> Result < ( ) , BtfError > {
836+ // Extract dummy variable's name offset and type ID for patching func_proto names and datasec entries.
837+ // If dummy var exists: use its name_offset (for string table patching) and btf_type (underlying int type).
838+ // If no dummy var (fallback): search for a 4-byte int type directly, with no name_offset.
839+ // Both paths provide an int_btf_id to ensure type consistency in datasec variable entries.
840+ let ( dummy_var_name_offset, int_btf_id) = if let Some ( dummy_id) = dummy_var_id {
841+ let dummy_type = & self . types . types [ dummy_id as usize ] ;
842+ if let BtfType :: Var ( v) = dummy_type {
843+ ( Some ( v. name_offset ) , v. btf_type )
844+ } else {
845+ return Err ( BtfError :: InvalidDatasec ) ;
846+ }
847+ } else {
848+ let int_id = self
849+ . types
850+ . types
851+ . iter ( )
852+ . enumerate ( )
853+ . find_map ( |( idx, t) | {
854+ if let BtfType :: Int ( int_type) = t {
855+ ( int_type. size == 4 ) . then_some ( idx as u32 )
856+ } else {
857+ None
858+ }
859+ } )
860+ . ok_or ( BtfError :: InvalidDatasec ) ?;
861+
862+ ( None , int_id)
863+ } ;
864+
865+ let datasec_id = datasec_id. ok_or ( BtfError :: InvalidDatasec ) ?;
866+
867+ let datasec_name = {
868+ let datasec = & self . types . types [ datasec_id as usize ] ;
869+ let BtfType :: DataSec ( d) = datasec else {
870+ return Err ( BtfError :: InvalidDatasec ) ;
871+ } ;
872+ self . string_at ( d. name_offset ) ?. into_owned ( )
873+ } ;
874+
875+ debug ! ( "DATASEC {datasec_name}: fixing up extern ksyms" ) ;
876+
877+ let entry_type_ids: Vec < u32 > = {
878+ let BtfType :: DataSec ( d) = & self . types . types [ datasec_id as usize ] else {
879+ return Err ( BtfError :: InvalidDatasec ) ;
880+ } ;
881+ d. entries . iter ( ) . map ( |e| e. btf_type ) . collect ( )
882+ } ;
883+
884+ let mut offset = 0u32 ;
885+ let size = mem:: size_of :: < i32 > ( ) as u32 ;
886+
887+ for ( i, & type_id) in entry_type_ids. iter ( ) . enumerate ( ) {
888+ match & self . types . types [ type_id as usize ] {
889+ BtfType :: Func ( f) => {
890+ let ( func_name, proto_id) =
891+ { ( self . string_at ( f. name_offset ) ?. into_owned ( ) , f. btf_type ) } ;
892+
893+ if let BtfType :: Func ( f) = & mut self . types . types [ type_id as usize ] {
894+ f. set_linkage ( FuncLinkage :: Global ) ;
895+ }
896+
897+ if let Some ( dummy_name_off) = dummy_var_name_offset {
898+ if let BtfType :: FuncProto ( func_proto) =
899+ & mut self . types . types [ proto_id as usize ]
900+ {
901+ for param in & mut func_proto. params {
902+ if param. btf_type != 0 && param. name_offset == 0 {
903+ param. name_offset = dummy_name_off;
904+ }
905+ }
906+ }
907+ }
908+
909+ if let ( Some ( dummy_id) , BtfType :: DataSec ( d) ) =
910+ ( dummy_var_id, & mut self . types . types [ datasec_id as usize ] )
911+ {
912+ d. entries [ i] . btf_type = dummy_id;
913+ }
914+
915+ debug ! ( "DATASEC {datasec_name}: FUNC {func_name}: fixup offset {offset}" ) ;
916+ }
917+ BtfType :: Var ( v) => {
918+ let var_name = { self . string_at ( v. name_offset ) ?. into_owned ( ) } ;
919+
920+ if let BtfType :: Var ( v) = & mut self . types . types [ type_id as usize ] {
921+ v. linkage = VarLinkage :: Global ;
922+ v. btf_type = int_btf_id;
923+ }
924+
925+ debug ! ( "DATASEC {datasec_name}: VAR {var_name}: fixup offset {offset}" ) ;
926+ }
927+ _ => unreachable ! ( ) ,
928+ }
929+
930+ if let BtfType :: DataSec ( d) = & mut self . types . types [ datasec_id as usize ] {
931+ d. entries [ i] . offset = offset;
932+ d. entries [ i] . size = size;
933+ }
934+
935+ offset += size;
936+ }
937+
938+ if let BtfType :: DataSec ( d) = & mut self . types . types [ datasec_id as usize ] {
939+ d. size = offset;
940+ debug ! ( "DATASEC {datasec_name}: fixup size to {offset}" ) ;
941+ }
942+
943+ Ok ( ( ) )
944+ }
815945}
816946
817947impl Default for Btf {
0 commit comments