@ -19,7 +19,7 @@ use relocation::*;
use crate ::{
bpf_map_def ,
generated ::{ bpf_insn , bpf_map_type ::BPF_MAP_TYPE_ARRAY },
generated ::{ bpf_insn , bpf_map_type ::BPF_MAP_TYPE_ARRAY , BPF_F_RDONLY_PROG },
obj ::btf ::{ Btf , BtfError , BtfExt } ,
BpfError ,
} ;
@ -43,11 +43,34 @@ pub struct Object {
pub ( crate ) symbols_by_index : HashMap < usize , Symbol > ,
}
#[ derive(Debug, Clone, PartialEq) ]
pub ( crate ) enum MapKind {
Bss ,
Data ,
Rodata ,
Other ,
}
impl From < & str > for MapKind {
fn from ( s : & str ) -> Self {
if s = = ".bss" {
MapKind ::Bss
} else if s . starts_with ( ".data" ) {
MapKind ::Data
} else if s . starts_with ( ".rodata" ) {
MapKind ::Rodata
} else {
MapKind ::Other
}
}
}
#[ derive(Debug, Clone) ]
pub struct Map {
pub ( crate ) def : bpf_map_def ,
pub ( crate ) section_index : usize ,
pub ( crate ) data : Vec < u8 > ,
pub ( crate ) kind : MapKind ,
}
#[ derive(Debug, Clone) ]
@ -238,6 +261,51 @@ impl Object {
}
}
pub fn patch_map_data ( & mut self , globals : HashMap < & str , & [ u8 ] > ) -> Result < ( ) , ParseError > {
let symbols : HashMap < String , & Symbol > = self
. symbols_by_index
. iter ( )
. filter ( | ( _ , s ) | s . name . is_some ( ) )
. map ( | ( _ , s ) | ( s . name . as_ref ( ) . unwrap ( ) . clone ( ) , s ) )
. collect ( ) ;
for ( name , data ) in globals {
if let Some ( symbol ) = symbols . get ( name ) {
if data . len ( ) as u64 ! = symbol . size {
return Err ( ParseError ::InvalidGlobalData {
name : name . to_string ( ) ,
sym_size : symbol . size ,
data_size : data . len ( ) ,
} ) ;
}
let ( _ , map ) = self
. maps
. iter_mut ( )
// assumption: there is only one map created per section where we're trying to
// patch data. this assumption holds true for the .rodata section at least
. find ( | ( _ , m ) | symbol . section_index = = Some ( SectionIndex ( m . section_index ) ) )
. ok_or_else ( | | ParseError ::MapNotFound {
index : symbol . section_index . unwrap_or ( SectionIndex ( 0 ) ) . 0 ,
} ) ? ;
let start = symbol . address as usize ;
let end = start + symbol . size as usize ;
if start > end | | end > map . data . len ( ) {
return Err ( ParseError ::InvalidGlobalData {
name : name . to_string ( ) ,
sym_size : symbol . size ,
data_size : data . len ( ) ,
} ) ;
}
map . data . splice ( start .. end , data . iter ( ) . cloned ( ) ) ;
} else {
return Err ( ParseError ::SymbolNotFound {
name : name . to_owned ( ) ,
} ) ;
}
}
Ok ( ( ) )
}
fn parse_btf ( & mut self , section : & Section ) -> Result < ( ) , BtfError > {
self . btf = Some ( Btf ::parse ( section . data , self . endianness ) ? ) ;
@ -417,6 +485,19 @@ pub enum ParseError {
#[ error( " invalid symbol, index `{index}` name: {} " , .name.as_ref().unwrap_or(& " [unknown] " .into())) ]
InvalidSymbol { index : usize , name : Option < String > } ,
#[ error( " symbol {name} has size `{sym_size}`, but provided data is of size `{data_size}` " ) ]
InvalidGlobalData {
name : String ,
sym_size : u64 ,
data_size : usize ,
} ,
#[ error( " symbol with name {name} not found in the symbols table " ) ]
SymbolNotFound { name : String } ,
#[ error( " map for section with index {index} not found " ) ]
MapNotFound { index : usize } ,
}
#[ derive(Debug) ]
@ -570,27 +651,32 @@ impl From<KernelVersion> for u32 {
}
fn parse_map ( section : & Section , name : & str ) -> Result < Map , ParseError > {
let ( def , data ) = if name = = ".bss" | | name . starts_with ( ".data" ) | | name . starts_with ( ".rodata" )
{
let def = bpf_map_def {
map_type : BPF_MAP_TYPE_ARRAY as u32 ,
key_size : mem ::size_of ::< u32 > ( ) as u32 ,
// We need to use section.size here since
// .bss will always have data.len() == 0
value_size : section . size as u32 ,
max_entries : 1 ,
map_flags : 0 , /* FIXME: set rodata readonly */
.. Default ::default ( )
} ;
( def , section . data . to_vec ( ) )
} else {
( parse_map_def ( name , section . data ) ? , Vec ::new ( ) )
let kind = MapKind ::from ( name ) ;
let ( def , data ) = match kind {
MapKind ::Bss | MapKind ::Data | MapKind ::Rodata = > {
let def = bpf_map_def {
map_type : BPF_MAP_TYPE_ARRAY as u32 ,
key_size : mem ::size_of ::< u32 > ( ) as u32 ,
// We need to use section.size here since
// .bss will always have data.len() == 0
value_size : section . size as u32 ,
max_entries : 1 ,
map_flags : if kind = = MapKind ::Rodata {
BPF_F_RDONLY_PROG
} else {
0
} ,
.. Default ::default ( )
} ;
( def , section . data . to_vec ( ) )
}
MapKind ::Other = > ( parse_map_def ( name , section . data ) ? , Vec ::new ( ) ) ,
} ;
Ok ( Map {
section_index : section . index . 0 ,
def ,
data ,
kind ,
} )
}
@ -634,7 +720,6 @@ fn copy_instructions(data: &[u8]) -> Result<Vec<bpf_insn>, ParseError> {
mod tests {
use matches ::assert_matches ;
use object ::Endianness ;
use std ::slice ;
use super ::* ;
use crate ::PinningType ;
@ -662,8 +747,8 @@ mod tests {
}
fn bytes_of < T > ( val : & T ) -> & [ u8 ] {
let size = mem ::size_of ::< T > ( ) ;
unsafe { slice ::from_raw_parts ( slice ::from_ref ( val ) . as_ptr ( ) . cast ( ) , size ) }
// Safety: This is for testing only
unsafe { crate ::util ::bytes_of ( val ) }
}
#[ test ]
@ -786,7 +871,7 @@ mod tests {
#[ test ]
fn test_parse_map_error ( ) {
assert! ( matches! (
parse_map ( & fake_section ( BpfSectionKind ::Maps , "maps/foo" , & [ ] ) , "foo" ),
parse_map ( & fake_section ( BpfSectionKind ::Maps , "maps/foo" , & [ ] ) , "foo" , ),
Err ( ParseError ::InvalidMapDefinition { .. } )
) ) ;
}
@ -821,7 +906,8 @@ mod tests {
id : 0 ,
pinning : PinningType ::None ,
} ,
data
data ,
..
} ) if data . is_empty ( )
) )
}
@ -849,8 +935,9 @@ mod tests {
id : 0 ,
pinning : PinningType ::None ,
} ,
data
} ) if data = = map_data & & value_size = = map_data . len ( ) as u32
data ,
kind
} ) if data = = map_data & & value_size = = map_data . len ( ) as u32 & & kind = = MapKind ::Bss
) )
}
@ -871,7 +958,7 @@ mod tests {
BpfSectionKind ::Program ,
"kprobe/foo" ,
& 42 u32 . to_ne_bytes ( ) ,
) , ),
) ),
Err ( ParseError ::InvalidProgramCode )
) ;
}
@ -913,7 +1000,7 @@ mod tests {
map_flags : 5 ,
.. Default ::default ( )
} )
) , ),
) ),
Ok ( ( ) )
) ;
assert! ( obj . maps . get ( "foo" ) . is_some ( ) ) ;
@ -923,13 +1010,13 @@ mod tests {
fn test_parse_section_data ( ) {
let mut obj = fake_obj ( ) ;
assert_matches ! (
obj . parse_section ( fake_section ( BpfSectionKind ::Data , ".bss" , b" map data " ) , ),
obj . parse_section ( fake_section ( BpfSectionKind ::Data , ".bss" , b" map data " ) ),
Ok ( ( ) )
) ;
assert! ( obj . maps . get ( ".bss" ) . is_some ( ) ) ;
assert_matches ! (
obj . parse_section ( fake_section ( BpfSectionKind ::Data , ".rodata" , b" map data " ) , ),
obj . parse_section ( fake_section ( BpfSectionKind ::Data , ".rodata" , b" map data " ) ),
Ok ( ( ) )
) ;
assert! ( obj . maps . get ( ".rodata" ) . is_some ( ) ) ;
@ -939,19 +1026,19 @@ mod tests {
BpfSectionKind ::Data ,
".rodata.boo" ,
b" map data "
) , ),
) ),
Ok ( ( ) )
) ;
assert! ( obj . maps . get ( ".rodata.boo" ) . is_some ( ) ) ;
assert_matches ! (
obj . parse_section ( fake_section ( BpfSectionKind ::Data , ".data" , b" map data " ) , ),
obj . parse_section ( fake_section ( BpfSectionKind ::Data , ".data" , b" map data " ) ),
Ok ( ( ) )
) ;
assert! ( obj . maps . get ( ".data" ) . is_some ( ) ) ;
assert_matches ! (
obj . parse_section ( fake_section ( BpfSectionKind ::Data , ".data.boo" , b" map data " ) , ),
obj . parse_section ( fake_section ( BpfSectionKind ::Data , ".data.boo" , b" map data " ) ),
Ok ( ( ) )
) ;
assert! ( obj . maps . get ( ".data.boo" ) . is_some ( ) ) ;
@ -1240,4 +1327,45 @@ mod tests {
} )
) ;
}
#[ test ]
fn test_patch_map_data ( ) {
let mut obj = fake_obj ( ) ;
obj . maps . insert (
".rodata" . to_string ( ) ,
Map {
def : bpf_map_def {
map_type : BPF_MAP_TYPE_ARRAY as u32 ,
key_size : mem ::size_of ::< u32 > ( ) as u32 ,
value_size : 3 ,
max_entries : 1 ,
map_flags : BPF_F_RDONLY_PROG ,
id : 1 ,
pinning : PinningType ::None ,
} ,
section_index : 1 ,
data : vec ! [ 0 , 0 , 0 ] ,
kind : MapKind ::Rodata ,
} ,
) ;
obj . symbols_by_index . insert (
1 ,
Symbol {
index : 1 ,
section_index : Some ( SectionIndex ( 1 ) ) ,
name : Some ( "my_config" . to_string ( ) ) ,
address : 0 ,
size : 3 ,
is_definition : true ,
is_text : false ,
} ,
) ;
let test_data : & [ u8 ] = & [ 1 , 2 , 3 ] ;
obj . patch_map_data ( HashMap ::from ( [ ( "my_config" , test_data ) ] ) )
. unwrap ( ) ;
let map = obj . maps . get ( ".rodata" ) . unwrap ( ) ;
assert_eq! ( test_data , map . data ) ;
}
}