Improve relocation errors

Use BpfError::RelocationError for both maps and BTF relocations. The
error includes the name of the program that failed, and the source error
stored as Box<dyn Error>.

This hides the implementation details of the source errors - which are
unrecoverable anyway - while still allowing fine grained error messages.
pull/1/head
Alessandro Decina 4 years ago
parent 37c3a198c4
commit 54637eab04

@ -2,6 +2,7 @@ use std::{
cell::{Ref, RefCell, RefMut}, cell::{Ref, RefCell, RefMut},
collections::HashMap, collections::HashMap,
convert::TryFrom, convert::TryFrom,
error::Error,
io, io,
}; };
@ -9,8 +10,7 @@ use thiserror::Error;
use crate::{ use crate::{
maps::{Map, MapError}, maps::{Map, MapError},
obj::btf::RelocationError as BtfRelocationError, obj::{btf::BtfError, Object, ParseError},
obj::{btf::BtfError, Object, ParseError, RelocationError},
programs::{KProbe, Program, ProgramData, ProgramError, SocketFilter, TracePoint, UProbe, Xdp}, programs::{KProbe, Program, ProgramData, ProgramError, SocketFilter, TracePoint, UProbe, Xdp},
syscalls::bpf_map_update_elem_ptr, syscalls::bpf_map_update_elem_ptr,
}; };
@ -152,11 +152,11 @@ pub enum BpfError {
#[error("BTF error: {0}")] #[error("BTF error: {0}")]
BtfError(#[from] BtfError), BtfError(#[from] BtfError),
#[error("error relocating BPF object: {0}")] #[error("error relocating BPF program `{program_name}`: {error}")]
RelocationError(#[from] RelocationError), RelocationError {
program_name: String,
#[error(transparent)] error: Box<dyn Error>,
BtfRelocationError(#[from] BtfRelocationError), },
#[error("map error: {0}")] #[error("map error: {0}")]
MapError(#[from] MapError), MapError(#[from] MapError),

@ -22,10 +22,10 @@ pub enum BtfError {
#[error("error parsing BTF header")] #[error("error parsing BTF header")]
InvalidHeader, InvalidHeader,
#[error("invalid type info segment")] #[error("invalid BTF type info segment")]
InvalidTypeInfo, InvalidTypeInfo,
#[error("invalid relocation info segment")] #[error("invalid BTF relocation info segment")]
InvalidRelocationInfo, InvalidRelocationInfo,
#[error("invalid BTF type kind `{kind}`")] #[error("invalid BTF type kind `{kind}`")]

@ -27,17 +27,16 @@ pub enum RelocationError {
#[error(transparent)] #[error(transparent)]
IOError(#[from] io::Error), IOError(#[from] io::Error),
#[error("section `{name}` not found")] #[error("program not found")]
SectionNotFound { name: String }, ProgramNotFound,
#[error("invalid relocation access string {access_str}")] #[error("invalid relocation access string {access_str}")]
InvalidAccessString { access_str: String }, InvalidAccessString { access_str: String },
#[error("invalid instruction index #{index} referenced by relocation #{relocation_number} in section `{section_name}`")] #[error("invalid instruction index #{index} referenced by relocation #{relocation_number}, the program contains {num_instructions} instructions")]
InvalidInstructionIndex { InvalidInstructionIndex {
index: usize, index: usize,
num_instructions: usize, num_instructions: usize,
section_name: String,
relocation_number: usize, relocation_number: usize,
}, },
@ -170,7 +169,6 @@ 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)?;
@ -180,12 +178,42 @@ impl Object {
continue; continue;
} }
let section_name = parts[1]; let section_name = parts[1];
let program = self.programs.get_mut(section_name).ok_or_else(|| { let program = self
RelocationError::SectionNotFound { .programs
name: section_name.to_string(), .get_mut(section_name)
} .ok_or(BpfError::RelocationError {
program_name: section_name.to_owned(),
error: Box::new(RelocationError::ProgramNotFound),
})?; })?;
match relocate_btf_program(
program,
relos,
local_btf,
&target_btf,
&mut candidates_cache,
) {
Ok(_) => {}
Err(ErrorWrapper::BtfError(e)) => return Err(e)?,
Err(ErrorWrapper::RelocationError(error)) => {
return Err(BpfError::RelocationError {
program_name: section_name.to_owned(),
error: Box::new(error),
})
}
}
}
Ok(())
}
}
fn relocate_btf_program<'target>(
program: &mut Program,
relos: &[Relocation],
local_btf: &Btf,
target_btf: &'target Btf,
candidates_cache: &mut HashMap<u32, Vec<Candidate<'target>>>,
) -> Result<(), ErrorWrapper> {
for rel in relos { for rel in relos {
let instructions = &mut program.instructions; let instructions = &mut program.instructions;
let ins_index = rel.ins_offset as usize / std::mem::size_of::<bpf_insn>(); let ins_index = rel.ins_offset as usize / std::mem::size_of::<bpf_insn>();
@ -193,7 +221,6 @@ impl Object {
return Err(RelocationError::InvalidInstructionIndex { return Err(RelocationError::InvalidInstructionIndex {
index: ins_index, index: ins_index,
num_instructions: instructions.len(), num_instructions: instructions.len(),
section_name: section_name.to_string(),
relocation_number: rel.number, relocation_number: rel.number,
})?; })?;
} }
@ -211,7 +238,7 @@ impl Object {
None => { None => {
candidates_cache.insert( candidates_cache.insert(
rel.type_id, rel.type_id,
find_candidates(local_ty, local_name, &target_btf)?, find_candidates(local_ty, local_name, target_btf)?,
); );
candidates_cache.get(&rel.type_id).unwrap() candidates_cache.get(&rel.type_id).unwrap()
} }
@ -220,11 +247,8 @@ impl Object {
let mut matches = Vec::new(); let mut matches = Vec::new();
for candidate in candidates { for candidate in candidates {
if let Some(candidate_spec) = match_candidate(&local_spec, candidate)? { if let Some(candidate_spec) = match_candidate(&local_spec, candidate)? {
let comp_rel = ComputedRelocation::new( let comp_rel =
rel, ComputedRelocation::new(rel, &local_spec, Some(&candidate_spec))?;
&local_spec,
Some(&candidate_spec),
)?;
matches.push((candidate.name.clone(), candidate_spec, comp_rel)); matches.push((candidate.name.clone(), candidate_spec, comp_rel));
} }
} }
@ -264,13 +288,11 @@ impl Object {
ComputedRelocation::new(rel, &local_spec, None)? ComputedRelocation::new(rel, &local_spec, None)?
}; };
comp_rel.apply(program, rel, section_name, local_btf, &target_btf)?; comp_rel.apply(program, rel, local_btf, &target_btf)?;
}
} }
Ok(()) Ok(())
} }
}
fn flavorless_name(name: &str) -> &str { fn flavorless_name(name: &str) -> &str {
name.splitn(2, "___").next().unwrap() name.splitn(2, "___").next().unwrap()
@ -306,7 +328,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>>, BpfError> { ) -> Result<Option<AccessSpec<'target>>, ErrorWrapper> {
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,
@ -434,7 +456,7 @@ fn match_member<'local, '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>, BpfError> { ) -> Result<Option<u32>, ErrorWrapper> {
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 {
BtfType::Struct(_, members) | BtfType::Union(_, members) => { BtfType::Struct(_, members) | BtfType::Union(_, members) => {
@ -519,7 +541,7 @@ impl<'a> AccessSpec<'a> {
root_type_id: u32, root_type_id: u32,
spec: &str, spec: &str,
relocation: Relocation, relocation: Relocation,
) -> Result<AccessSpec<'a>, BpfError> { ) -> Result<AccessSpec<'a>, ErrorWrapper> {
let parts = spec let parts = spec
.split(":") .split(":")
.map(|s| s.parse::<usize>()) .map(|s| s.parse::<usize>())
@ -724,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, BpfError> { ) -> Result<ComputedRelocation, ErrorWrapper> {
use RelocationKind::*; use RelocationKind::*;
let ret = match rel.kind { let ret = match rel.kind {
FieldByteOffset | FieldByteSize | FieldExists | FieldSigned | FieldLShift64 FieldByteOffset | FieldByteSize | FieldExists | FieldSigned | FieldLShift64
@ -749,10 +771,9 @@ impl ComputedRelocation {
&self, &self,
program: &mut Program, program: &mut Program,
rel: &Relocation, rel: &Relocation,
section_name: &str,
local_btf: &Btf, local_btf: &Btf,
target_btf: &Btf, target_btf: &Btf,
) -> Result<(), BpfError> { ) -> Result<(), ErrorWrapper> {
let instructions = &mut program.instructions; let instructions = &mut program.instructions;
let num_instructions = instructions.len(); let num_instructions = instructions.len();
let ins_index = rel.ins_offset as usize / std::mem::size_of::<bpf_insn>(); let ins_index = rel.ins_offset as usize / std::mem::size_of::<bpf_insn>();
@ -762,7 +783,6 @@ impl ComputedRelocation {
.ok_or(RelocationError::InvalidInstructionIndex { .ok_or(RelocationError::InvalidInstructionIndex {
index: rel.ins_offset as usize, index: rel.ins_offset as usize,
num_instructions, num_instructions,
section_name: section_name.to_string(),
relocation_number: rel.number, relocation_number: rel.number,
})?; })?;
@ -840,7 +860,6 @@ impl ComputedRelocation {
RelocationError::InvalidInstructionIndex { RelocationError::InvalidInstructionIndex {
index: ins_index + 1, index: ins_index + 1,
num_instructions, num_instructions,
section_name: section_name.to_string(),
relocation_number: rel.number, relocation_number: rel.number,
}, },
)?; )?;
@ -862,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, BpfError> { ) -> Result<ComputedRelocationValue, ErrorWrapper> {
use RelocationKind::*; use RelocationKind::*;
let value = match rel.kind { let value = match rel.kind {
EnumVariantExists => spec.is_some() as u32, EnumVariantExists => spec.is_some() as u32,
@ -888,7 +907,7 @@ impl ComputedRelocation {
fn compute_field_relocation( fn compute_field_relocation(
rel: &Relocation, rel: &Relocation,
spec: Option<&AccessSpec>, spec: Option<&AccessSpec>,
) -> Result<ComputedRelocationValue, BpfError> { ) -> Result<ComputedRelocationValue, ErrorWrapper> {
use RelocationKind::*; use RelocationKind::*;
if let FieldExists = rel.kind { if let FieldExists = rel.kind {
@ -1013,7 +1032,7 @@ impl ComputedRelocation {
rel: &Relocation, rel: &Relocation,
local_spec: &AccessSpec, local_spec: &AccessSpec,
target_spec: Option<&AccessSpec>, target_spec: Option<&AccessSpec>,
) -> Result<ComputedRelocationValue, BpfError> { ) -> Result<ComputedRelocationValue, ErrorWrapper> {
use RelocationKind::*; use RelocationKind::*;
let value = match rel.kind { let value = match rel.kind {
TypeIdLocal => local_spec.root_type_id, TypeIdLocal => local_spec.root_type_id,
@ -1037,3 +1056,14 @@ 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),
}

@ -1,14 +1,17 @@
use std::{collections::HashMap, io}; use std::collections::HashMap;
use object::{RelocationKind, RelocationTarget, SectionIndex}; use object::{RelocationKind, RelocationTarget, SectionIndex, SymbolIndex};
use thiserror::Error; use thiserror::Error;
use crate::{ use crate::{
generated::{bpf_insn, BPF_PSEUDO_MAP_FD, BPF_PSEUDO_MAP_VALUE}, generated::{bpf_insn, BPF_PSEUDO_MAP_FD, BPF_PSEUDO_MAP_VALUE},
maps::Map, maps::Map,
obj::Object, obj::Object,
BpfError,
}; };
use super::Program;
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum RelocationError { pub enum RelocationError {
#[error("unknown symbol, index `{index}`")] #[error("unknown symbol, index `{index}`")]
@ -52,19 +55,37 @@ pub(crate) struct Symbol {
} }
impl Object { impl Object {
pub fn relocate_maps(&mut self, maps: &[Map]) -> Result<(), RelocationError> { pub fn relocate_maps(&mut self, maps: &[Map]) -> Result<(), BpfError> {
let maps_by_section = maps let maps_by_section = maps
.iter() .iter()
.map(|map| (map.obj.section_index, map)) .map(|map| (map.obj.section_index, map))
.collect::<HashMap<_, _>>(); .collect::<HashMap<_, _>>();
for program in self.programs.values_mut() { let symbol_table = &self.symbol_table;
for (program_name, program) in self.programs.iter_mut() {
if let Some(relocations) = self.relocations.get(&program.section_index) { if let Some(relocations) = self.relocations.get(&program.section_index) {
relocate_program(program, relocations, &maps_by_section, symbol_table).map_err(
|error| BpfError::RelocationError {
program_name: program_name.clone(),
error: Box::new(error),
},
)?;
}
}
Ok(())
}
}
fn relocate_program(
program: &mut Program,
relocations: &[Relocation],
maps_by_section: &HashMap<usize, &Map>,
symbol_table: &HashMap<SymbolIndex, Symbol>,
) -> Result<(), RelocationError> {
for (rel_n, rel) in relocations.iter().enumerate() { for (rel_n, rel) in relocations.iter().enumerate() {
match rel.target { match rel.target {
RelocationTarget::Symbol(index) => { RelocationTarget::Symbol(index) => {
let sym = self let sym = symbol_table
.symbol_table
.get(&index) .get(&index)
.ok_or(RelocationError::UnknownSymbol { index: index.0 })?; .ok_or(RelocationError::UnknownSymbol { index: index.0 })?;
@ -86,8 +107,7 @@ impl Object {
})?; })?;
let instructions = &mut program.instructions; let instructions = &mut program.instructions;
let ins_index = let ins_index = (rel.offset / std::mem::size_of::<bpf_insn>() as u64) as usize;
(rel.offset / std::mem::size_of::<bpf_insn>() as u64) as usize;
if ins_index >= instructions.len() { if ins_index >= instructions.len() {
return Err(RelocationError::InvalidInstructionIndex { return Err(RelocationError::InvalidInstructionIndex {
index: ins_index, index: ins_index,
@ -108,9 +128,5 @@ impl Object {
RelocationTarget::Absolute => todo!(), RelocationTarget::Absolute => todo!(),
} }
} }
}
}
Ok(()) Ok(())
} }
}

Loading…
Cancel
Save