@ -3,20 +3,27 @@ use std::{collections::HashMap, io, mem, ptr, str::FromStr};
use thiserror ::Error ;
use thiserror ::Error ;
use crate ::{
use crate ::{
btf ::{
fields_are_compatible , types_are_compatible , Array , Btf , BtfError , BtfMember , BtfType ,
IntEncoding , Struct , Union , MAX_SPEC_LEN ,
} ,
generated ::{
generated ::{
bpf_core_relo , bpf_core_relo_kind ::* , bpf_insn , BPF_ALU , BPF_ALU64 , BPF_B , BPF_DW , BPF_H ,
bpf_core_relo , bpf_core_relo_kind ::* , bpf_insn , BPF_ALU , BPF_ALU64 , BPF_B , BPF_DW , BPF_H ,
BPF_K , BPF_LD , BPF_LDX , BPF_ST , BPF_STX , BPF_W , BTF_INT_SIGNED ,
BPF_K , BPF_LD , BPF_LDX , BPF_ST , BPF_STX , BPF_W , BTF_INT_SIGNED ,
} ,
} ,
obj ::{
Object , Program , ProgramSection ,
btf ::{
fields_are_compatible , types_are_compatible , Array , BtfMember , BtfType , IntEncoding ,
Struct , Union , MAX_SPEC_LEN ,
} ,
Btf , BtfError , Object , Program , ProgramSection ,
} ,
BpfError ,
} ;
} ;
#[ derive(Error, Debug) ]
#[ error( " error relocating `{section}` " ) ]
pub struct BtfRelocationError {
/// The function name
pub section : String ,
#[ source ]
/// The original error
error : RelocationError ,
}
#[ derive(Error, Debug) ]
#[ derive(Error, Debug) ]
enum RelocationError {
enum RelocationError {
#[ error(transparent) ]
#[ error(transparent) ]
@ -78,6 +85,9 @@ enum RelocationError {
type_id : u32 ,
type_id : u32 ,
ins_index : usize ,
ins_index : usize ,
} ,
} ,
#[ error( " invalid BTF " ) ]
BtfError ( #[ from ] BtfError ) ,
}
}
fn err_type_name ( name : & Option < String > ) -> String {
fn err_type_name ( name : & Option < String > ) -> String {
@ -154,7 +164,7 @@ impl Relocation {
}
}
impl Object {
impl Object {
pub fn relocate_btf ( & mut self , target_btf : & Btf ) -> Result < ( ) , B pf Error> {
pub fn relocate_btf ( & mut self , target_btf : & Btf ) -> Result < ( ) , B tfRelocation Error> {
let ( local_btf , btf_ext ) = match ( & self . btf , & self . btf_ext ) {
let ( local_btf , btf_ext ) = match ( & self . btf , & self . btf_ext ) {
( Some ( btf ) , Some ( btf_ext ) ) = > ( btf , btf_ext ) ,
( Some ( btf ) , Some ( btf_ext ) ) = > ( btf , btf_ext ) ,
_ = > return Ok ( ( ) ) ,
_ = > return Ok ( ( ) ) ,
@ -162,7 +172,10 @@ impl Object {
let mut candidates_cache = HashMap ::< u32 , Vec < Candidate > > ::new ( ) ;
let mut candidates_cache = HashMap ::< u32 , Vec < Candidate > > ::new ( ) ;
for ( sec_name_off , relos ) in btf_ext . relocations ( ) {
for ( sec_name_off , relos ) in btf_ext . relocations ( ) {
let section_name = local_btf . string_at ( * sec_name_off ) ? ;
let section_name = local_btf . string_at ( * sec_name_off ) . map_err ( | e | BtfRelocationError {
section : format ! ( "section@{sec_name_off}" ) ,
error : RelocationError ::BtfError ( e ) ,
} ) ? ;
let program_section = match ProgramSection ::from_str ( & section_name ) {
let program_section = match ProgramSection ::from_str ( & section_name ) {
Ok ( program ) = > program ,
Ok ( program ) = > program ,
@ -173,20 +186,17 @@ impl Object {
let program = self
let program = self
. programs
. programs
. get_mut ( section_name )
. get_mut ( section_name )
. ok_or ( B pfError:: RelocationError {
. ok_or ( B tf RelocationError {
fun ction: section_name . to_owned ( ) ,
se ction: section_name . to_owned ( ) ,
error : Box ::new ( RelocationError ::ProgramNotFound ) ,
error : RelocationError ::ProgramNotFound ,
} ) ? ;
} ) ? ;
match relocate_btf_program ( program , relos , local_btf , target_btf , & mut candidates_cache )
match relocate_btf_program ( program , relos , local_btf , target_btf , & mut candidates_cache )
{
{
Ok ( _ ) = > { }
Ok ( _ ) = > { }
Err ( ErrorWrapper ::BtfError ( e ) ) = > return Err ( e . into ( ) ) ,
Err ( error ) = > return Err ( BtfRelocationError {
Err ( ErrorWrapper ::RelocationError ( error ) ) = > {
section : section_name . to_owned ( ) ,
return Err ( BpfError ::RelocationError {
error ,
function : section_name . to_owned ( ) ,
} ) ,
error : Box ::new ( error ) ,
} )
}
}
}
}
}
@ -200,7 +210,7 @@ fn relocate_btf_program<'target>(
local_btf : & Btf ,
local_btf : & Btf ,
target_btf : & ' target Btf ,
target_btf : & ' target Btf ,
candidates_cache : & mut HashMap < u32 , Vec < Candidate < ' target > > > ,
candidates_cache : & mut HashMap < u32 , Vec < Candidate < ' target > > > ,
) -> Result < ( ) , ErrorWrappe r> {
) -> Result < ( ) , Relocation Error> {
for rel in relos {
for rel in relos {
let instructions = & mut program . function . instructions ;
let instructions = & mut program . function . instructions ;
let ins_index = rel . ins_offset / std ::mem ::size_of ::< bpf_insn > ( ) ;
let ins_index = rel . ins_offset / std ::mem ::size_of ::< bpf_insn > ( ) ;
@ -209,8 +219,7 @@ fn relocate_btf_program<'target>(
index : ins_index ,
index : ins_index ,
num_instructions : instructions . len ( ) ,
num_instructions : instructions . len ( ) ,
relocation_number : rel . number ,
relocation_number : rel . number ,
}
} ) ;
. into ( ) ) ;
}
}
let local_ty = local_btf . type_by_id ( rel . type_id ) ? ;
let local_ty = local_btf . type_by_id ( rel . type_id ) ? ;
@ -266,8 +275,7 @@ fn relocate_btf_program<'target>(
return Err ( RelocationError ::ConflictingCandidates {
return Err ( RelocationError ::ConflictingCandidates {
type_name : local_name . to_string ( ) ,
type_name : local_name . to_string ( ) ,
candidates : conflicts ,
candidates : conflicts ,
}
} ) ;
. into ( ) ) ;
}
}
target_comp_rel
target_comp_rel
} else {
} else {
@ -317,7 +325,7 @@ fn find_candidates<'target>(
fn match_candidate < ' target > (
fn match_candidate < ' target > (
local_spec : & AccessSpec ,
local_spec : & AccessSpec ,
candidate : & ' target Candidate ,
candidate : & ' target Candidate ,
) -> Result < Option < AccessSpec < ' target > > , ErrorWrappe r> {
) -> Result < Option < AccessSpec < ' target > > , Relocation Error> {
let mut target_spec = AccessSpec {
let mut target_spec = AccessSpec {
btf : candidate . btf ,
btf : candidate . btf ,
root_type_id : candidate . type_id ,
root_type_id : candidate . type_id ,
@ -419,8 +427,7 @@ fn match_candidate<'target>(
if target_spec . parts . len ( ) = = MAX_SPEC_LEN {
if target_spec . parts . len ( ) = = MAX_SPEC_LEN {
return Err ( RelocationError ::MaximumNestingLevelReached {
return Err ( RelocationError ::MaximumNestingLevelReached {
type_name : Some ( candidate . name . clone ( ) ) ,
type_name : Some ( candidate . name . clone ( ) ) ,
}
} ) ;
. into ( ) ) ;
}
}
target_spec . parts . push ( accessor . index ) ;
target_spec . parts . push ( accessor . index ) ;
@ -446,7 +453,7 @@ fn match_member<'target>(
target_btf : & ' target Btf ,
target_btf : & ' target Btf ,
target_id : u32 ,
target_id : u32 ,
target_spec : & mut AccessSpec < ' target > ,
target_spec : & mut AccessSpec < ' target > ,
) -> Result < Option < u32 > , ErrorWrappe r> {
) -> Result < Option < u32 > , Relocation Error> {
let local_ty = local_btf . type_by_id ( local_accessor . type_id ) ? ;
let local_ty = local_btf . type_by_id ( local_accessor . type_id ) ? ;
let local_member = match local_ty {
let local_member = match local_ty {
// this won't panic, bounds are checked when local_spec is built in AccessSpec::new
// this won't panic, bounds are checked when local_spec is built in AccessSpec::new
@ -470,8 +477,7 @@ fn match_member<'target>(
let root_ty = target_spec . btf . type_by_id ( target_spec . root_type_id ) ? ;
let root_ty = target_spec . btf . type_by_id ( target_spec . root_type_id ) ? ;
return Err ( RelocationError ::MaximumNestingLevelReached {
return Err ( RelocationError ::MaximumNestingLevelReached {
type_name : target_spec . btf . err_type_name ( root_ty ) ,
type_name : target_spec . btf . err_type_name ( root_ty ) ,
}
} ) ;
. into ( ) ) ;
}
}
// this will not panic as we've already established these are fields types
// this will not panic as we've already established these are fields types
@ -532,7 +538,7 @@ impl<'a> AccessSpec<'a> {
root_type_id : u32 ,
root_type_id : u32 ,
spec : & str ,
spec : & str ,
relocation : Relocation ,
relocation : Relocation ,
) -> Result < AccessSpec < ' a > , ErrorWrappe r> {
) -> Result < AccessSpec < ' a > , Relocation Error> {
let parts = spec
let parts = spec
. split ( ':' )
. split ( ':' )
. map ( | s | s . parse ::< usize > ( ) )
. map ( | s | s . parse ::< usize > ( ) )
@ -552,8 +558,7 @@ impl<'a> AccessSpec<'a> {
if parts ! = [ 0 ] {
if parts ! = [ 0 ] {
return Err ( RelocationError ::InvalidAccessString {
return Err ( RelocationError ::InvalidAccessString {
access_str : spec . to_string ( ) ,
access_str : spec . to_string ( ) ,
}
} ) ;
. into ( ) ) ;
}
}
AccessSpec {
AccessSpec {
btf ,
btf ,
@ -569,8 +574,7 @@ impl<'a> AccessSpec<'a> {
if parts . len ( ) ! = 1 {
if parts . len ( ) ! = 1 {
return Err ( RelocationError ::InvalidAccessString {
return Err ( RelocationError ::InvalidAccessString {
access_str : spec . to_string ( ) ,
access_str : spec . to_string ( ) ,
}
} ) ;
. into ( ) ) ;
}
}
let index = parts [ 0 ] ;
let index = parts [ 0 ] ;
if index > = en . variants . len ( ) {
if index > = en . variants . len ( ) {
@ -580,8 +584,7 @@ impl<'a> AccessSpec<'a> {
index ,
index ,
max_index : en . variants . len ( ) ,
max_index : en . variants . len ( ) ,
error : "tried to access nonexistant enum variant" . to_string ( ) ,
error : "tried to access nonexistant enum variant" . to_string ( ) ,
}
} ) ;
. into ( ) ) ;
}
}
let accessors = vec! [ Accessor {
let accessors = vec! [ Accessor {
type_id ,
type_id ,
@ -607,8 +610,7 @@ impl<'a> AccessSpec<'a> {
relocation_kind : format ! ( "{:?}" , relocation . kind ) ,
relocation_kind : format ! ( "{:?}" , relocation . kind ) ,
type_kind : format ! ( "{:?}" , ty . kind ( ) ) ,
type_kind : format ! ( "{:?}" , ty . kind ( ) ) ,
error : "enum relocation on non-enum type" . to_string ( ) ,
error : "enum relocation on non-enum type" . to_string ( ) ,
}
} )
. into ( ) )
}
}
} ,
} ,
@ -638,8 +640,7 @@ impl<'a> AccessSpec<'a> {
index ,
index ,
max_index : members . len ( ) ,
max_index : members . len ( ) ,
error : "out of bounds struct or union access" . to_string ( ) ,
error : "out of bounds struct or union access" . to_string ( ) ,
}
} ) ;
. into ( ) ) ;
}
}
let member = & members [ index ] ;
let member = & members [ index ] ;
@ -675,8 +676,7 @@ impl<'a> AccessSpec<'a> {
index ,
index ,
max_index : array . len as usize ,
max_index : array . len as usize ,
error : "array index out of bounds" . to_string ( ) ,
error : "array index out of bounds" . to_string ( ) ,
}
} ) ;
. into ( ) ) ;
}
}
accessors . push ( Accessor {
accessors . push ( Accessor {
type_id ,
type_id ,
@ -693,8 +693,7 @@ impl<'a> AccessSpec<'a> {
type_kind : format ! ( "{:?}" , ty . kind ( ) ) ,
type_kind : format ! ( "{:?}" , ty . kind ( ) ) ,
error : "field relocation on a type that doesn't have fields"
error : "field relocation on a type that doesn't have fields"
. to_string ( ) ,
. to_string ( ) ,
}
} ) ;
. into ( ) ) ;
}
}
} ;
} ;
}
}
@ -747,7 +746,7 @@ impl ComputedRelocation {
rel : & Relocation ,
rel : & Relocation ,
local_spec : & AccessSpec ,
local_spec : & AccessSpec ,
target_spec : Option < & AccessSpec > ,
target_spec : Option < & AccessSpec > ,
) -> Result < ComputedRelocation , ErrorWrappe r> {
) -> Result < ComputedRelocation , Relocation Error> {
use RelocationKind ::* ;
use RelocationKind ::* ;
let ret = match rel . kind {
let ret = match rel . kind {
FieldByteOffset | FieldByteSize | FieldExists | FieldSigned | FieldLShift64
FieldByteOffset | FieldByteSize | FieldExists | FieldSigned | FieldLShift64
@ -774,7 +773,7 @@ impl ComputedRelocation {
rel : & Relocation ,
rel : & Relocation ,
local_btf : & Btf ,
local_btf : & Btf ,
target_btf : & Btf ,
target_btf : & Btf ,
) -> Result < ( ) , ErrorWrappe r> {
) -> Result < ( ) , Relocation Error> {
let instructions = & mut program . function . instructions ;
let instructions = & mut program . function . instructions ;
let num_instructions = instructions . len ( ) ;
let num_instructions = instructions . len ( ) ;
let ins_index = rel . ins_offset / std ::mem ::size_of ::< bpf_insn > ( ) ;
let ins_index = rel . ins_offset / std ::mem ::size_of ::< bpf_insn > ( ) ;
@ -799,8 +798,7 @@ impl ComputedRelocation {
relocation_number : rel . number ,
relocation_number : rel . number ,
index : ins_index ,
index : ins_index ,
error : format ! ( "invalid src_reg={src_reg:x} expected {BPF_K:x}" ) ,
error : format ! ( "invalid src_reg={src_reg:x} expected {BPF_K:x}" ) ,
}
} ) ;
. into ( ) ) ;
}
}
ins . imm = target_value as i32 ;
ins . imm = target_value as i32 ;
@ -811,8 +809,7 @@ impl ComputedRelocation {
relocation_number : rel . number ,
relocation_number : rel . number ,
index : ins_index ,
index : ins_index ,
error : format ! ( "value `{target_value}` overflows 16 bits offset field" ) ,
error : format ! ( "value `{target_value}` overflows 16 bits offset field" ) ,
}
} ) ;
. into ( ) ) ;
}
}
ins . off = target_value as i16 ;
ins . off = target_value as i16 ;
@ -837,8 +834,7 @@ impl ComputedRelocation {
err_type_name ( & target_btf . err_type_name ( target_ty ) ) ,
err_type_name ( & target_btf . err_type_name ( target_ty ) ) ,
self . target . size ,
self . target . size ,
) ,
) ,
}
} )
. into ( ) )
}
}
}
}
@ -852,8 +848,7 @@ impl ComputedRelocation {
relocation_number : rel . number ,
relocation_number : rel . number ,
index : ins_index ,
index : ins_index ,
error : format ! ( "invalid target size {size}" ) ,
error : format ! ( "invalid target size {size}" ) ,
}
} )
. into ( ) )
}
}
} as u8 ;
} as u8 ;
ins . code = ins . code & 0xE0 | size | ins . code & 0x07 ;
ins . code = ins . code & 0xE0 | size | ins . code & 0x07 ;
@ -876,8 +871,7 @@ impl ComputedRelocation {
relocation_number : rel . number ,
relocation_number : rel . number ,
index : ins_index ,
index : ins_index ,
error : format ! ( "invalid instruction class {class:x}" ) ,
error : format ! ( "invalid instruction class {class:x}" ) ,
}
} )
. into ( ) )
}
}
} ;
} ;
@ -887,7 +881,7 @@ impl ComputedRelocation {
fn compute_enum_relocation (
fn compute_enum_relocation (
rel : & Relocation ,
rel : & Relocation ,
spec : Option < & AccessSpec > ,
spec : Option < & AccessSpec > ,
) -> Result < ComputedRelocationValue , ErrorWrappe r> {
) -> Result < ComputedRelocationValue , Relocation Error> {
use RelocationKind ::* ;
use RelocationKind ::* ;
let value = match ( rel . kind , spec ) {
let value = match ( rel . kind , spec ) {
( EnumVariantExists , spec ) = > spec . is_some ( ) as u32 ,
( EnumVariantExists , spec ) = > spec . is_some ( ) as u32 ,
@ -918,7 +912,7 @@ impl ComputedRelocation {
fn compute_field_relocation (
fn compute_field_relocation (
rel : & Relocation ,
rel : & Relocation ,
spec : Option < & AccessSpec > ,
spec : Option < & AccessSpec > ,
) -> Result < ComputedRelocationValue , ErrorWrappe r> {
) -> Result < ComputedRelocationValue , Relocation Error> {
use RelocationKind ::* ;
use RelocationKind ::* ;
if let FieldExists = rel . kind {
if let FieldExists = rel . kind {
@ -963,8 +957,7 @@ impl ComputedRelocation {
relocation_kind : format ! ( "{rel_kind:?}" ) ,
relocation_kind : format ! ( "{rel_kind:?}" ) ,
type_kind : format ! ( "{:?}" , ty . kind ( ) ) ,
type_kind : format ! ( "{:?}" , ty . kind ( ) ) ,
error : "invalid relocation kind for array type" . to_string ( ) ,
error : "invalid relocation kind for array type" . to_string ( ) ,
}
} ) ;
. into ( ) ) ;
}
}
} ;
} ;
}
}
@ -979,8 +972,7 @@ impl ComputedRelocation {
relocation_kind : format ! ( "{:?}" , rel . kind ) ,
relocation_kind : format ! ( "{:?}" , rel . kind ) ,
type_kind : format ! ( "{:?}" , ty . kind ( ) ) ,
type_kind : format ! ( "{:?}" , ty . kind ( ) ) ,
error : "field relocation on a type that doesn't have fields" . to_string ( ) ,
error : "field relocation on a type that doesn't have fields" . to_string ( ) ,
}
} ) ;
. into ( ) ) ;
}
}
} ;
} ;
@ -1055,7 +1047,7 @@ impl ComputedRelocation {
rel : & Relocation ,
rel : & Relocation ,
local_spec : & AccessSpec ,
local_spec : & AccessSpec ,
target_spec : Option < & AccessSpec > ,
target_spec : Option < & AccessSpec > ,
) -> Result < ComputedRelocationValue , ErrorWrappe r> {
) -> Result < ComputedRelocationValue , Relocation Error> {
use RelocationKind ::* ;
use RelocationKind ::* ;
let value = match ( rel . kind , target_spec ) {
let value = match ( rel . kind , target_spec ) {
@ -1081,14 +1073,3 @@ impl ComputedRelocation {
} )
} )
}
}
}
}
// this exists only to simplify propagating errors from relocate_btf() and to associate
// RelocationError(s) with their respective program name
#[ derive(Error, Debug) ]
enum ErrorWrapper {
#[ error(transparent) ]
BtfError ( #[ from ] BtfError ) ,
#[ error(transparent) ]
RelocationError ( #[ from ] RelocationError ) ,
}