aya: relocate .text references

Handle relocations against .text symbols in all instructions not just
calls. Makes it so that let x = &some_function triggers linking of
some_function in the current program and handles the resulting
relocation accordingly.

Among other things, enables the use of bpf_for_each_map_elem.
pull/177/head
Alessandro Decina 3 years ago
parent 1904aeaef9
commit 8202105b7d

@ -47,6 +47,7 @@ pub struct Object {
// symbol_offset_by_name caches symbols that could be referenced from a // symbol_offset_by_name caches symbols that could be referenced from a
// BTF VAR type so the offsets can be fixed up // BTF VAR type so the offsets can be fixed up
pub(crate) symbol_offset_by_name: HashMap<String, u64>, pub(crate) symbol_offset_by_name: HashMap<String, u64>,
pub(crate) text_section_index: Option<usize>,
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -245,15 +246,13 @@ impl Object {
let sym = Symbol { let sym = Symbol {
index: symbol.index().0, index: symbol.index().0,
name: Some(name.clone()), name: Some(name.clone()),
section_index: symbol.section().index(), section_index: symbol.section().index().map(|i| i.0),
address: symbol.address(), address: symbol.address(),
size: symbol.size(), size: symbol.size(),
is_definition: symbol.is_definition(), is_definition: symbol.is_definition(),
is_text: symbol.kind() == SymbolKind::Text, kind: symbol.kind(),
}; };
bpf_obj bpf_obj.symbols_by_index.insert(symbol.index().0, sym);
.symbols_by_index
.insert(symbol.index().0, sym.clone());
if symbol.is_global() || symbol.kind() == SymbolKind::Data { if symbol.is_global() || symbol.kind() == SymbolKind::Data {
bpf_obj.symbol_offset_by_name.insert(name, symbol.address()); bpf_obj.symbol_offset_by_name.insert(name, symbol.address());
@ -298,6 +297,7 @@ impl Object {
symbols_by_index: HashMap::new(), symbols_by_index: HashMap::new(),
section_sizes: HashMap::new(), section_sizes: HashMap::new(),
symbol_offset_by_name: HashMap::new(), symbol_offset_by_name: HashMap::new(),
text_section_index: None,
} }
} }
@ -323,9 +323,9 @@ impl Object {
.iter_mut() .iter_mut()
// assumption: there is only one map created per section where we're trying to // 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 // patch data. this assumption holds true for the .rodata section at least
.find(|(_, m)| symbol.section_index == Some(SectionIndex(m.section_index))) .find(|(_, m)| symbol.section_index == Some(m.section_index))
.ok_or_else(|| ParseError::MapNotFound { .ok_or_else(|| ParseError::MapNotFound {
index: symbol.section_index.unwrap_or(SectionIndex(0)).0, index: symbol.section_index.unwrap_or(0),
})?; })?;
let start = symbol.address as usize; let start = symbol.address as usize;
let end = start + symbol.size as usize; let end = start + symbol.size as usize;
@ -398,10 +398,15 @@ impl Object {
} }
fn parse_text_section(&mut self, mut section: Section) -> Result<(), ParseError> { fn parse_text_section(&mut self, mut section: Section) -> Result<(), ParseError> {
self.text_section_index = Some(section.index.0);
let mut symbols_by_address = HashMap::new(); let mut symbols_by_address = HashMap::new();
for sym in self.symbols_by_index.values() { for sym in self.symbols_by_index.values() {
if sym.is_definition && sym.is_text && sym.section_index == Some(section.index) { if sym.is_definition
&& sym.kind == SymbolKind::Text
&& sym.section_index == Some(section.index.0)
{
if symbols_by_address.contains_key(&sym.address) { if symbols_by_address.contains_key(&sym.address) {
return Err(ParseError::SymbolTableConflict { return Err(ParseError::SymbolTableConflict {
section_index: section.index.0, section_index: section.index.0,
@ -1453,12 +1458,12 @@ mod tests {
1, 1,
Symbol { Symbol {
index: 1, index: 1,
section_index: Some(SectionIndex(1)), section_index: Some(1),
name: Some("my_config".to_string()), name: Some("my_config".to_string()),
address: 0, address: 0,
size: 3, size: 3,
is_definition: true, is_definition: true,
is_text: false, kind: SymbolKind::Data,
}, },
); );

@ -1,11 +1,12 @@
use std::{collections::HashMap, mem}; use std::{collections::HashMap, mem};
use object::SectionIndex; use log::debug;
use object::{SectionIndex, SymbolKind};
use thiserror::Error; use thiserror::Error;
use crate::{ use crate::{
generated::{ generated::{
bpf_insn, BPF_CALL, BPF_JMP, BPF_K, BPF_PSEUDO_CALL, BPF_PSEUDO_MAP_FD, bpf_insn, BPF_CALL, BPF_JMP, BPF_K, BPF_PSEUDO_CALL, BPF_PSEUDO_FUNC, BPF_PSEUDO_MAP_FD,
BPF_PSEUDO_MAP_VALUE, BPF_PSEUDO_MAP_VALUE,
}, },
maps::Map, maps::Map,
@ -52,12 +53,12 @@ pub(crate) struct Relocation {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct Symbol { pub(crate) struct Symbol {
pub(crate) index: usize, pub(crate) index: usize,
pub(crate) section_index: Option<SectionIndex>, pub(crate) section_index: Option<usize>,
pub(crate) name: Option<String>, pub(crate) name: Option<String>,
pub(crate) address: u64, pub(crate) address: u64,
pub(crate) size: u64, pub(crate) size: u64,
pub(crate) is_definition: bool, pub(crate) is_definition: bool,
pub(crate) is_text: bool, pub(crate) kind: SymbolKind,
} }
impl Object { impl Object {
@ -82,6 +83,7 @@ impl Object {
relocations.values(), relocations.values(),
&maps_by_section, &maps_by_section,
&self.symbols_by_index, &self.symbols_by_index,
self.text_section_index,
) )
.map_err(|error| BpfError::RelocationError { .map_err(|error| BpfError::RelocationError {
function: function.name.clone(), function: function.name.clone(),
@ -95,8 +97,12 @@ impl Object {
pub fn relocate_calls(&mut self) -> Result<(), BpfError> { pub fn relocate_calls(&mut self) -> Result<(), BpfError> {
for (name, program) in self.programs.iter_mut() { for (name, program) in self.programs.iter_mut() {
let linker = let linker = FunctionLinker::new(
FunctionLinker::new(&self.functions, &self.relocations, &self.symbols_by_index); self.text_section_index,
&self.functions,
&self.relocations,
&self.symbols_by_index,
);
linker linker
.link(program) .link(program)
.map_err(|error| BpfError::RelocationError { .map_err(|error| BpfError::RelocationError {
@ -114,6 +120,7 @@ fn relocate_maps<'a, I: Iterator<Item = &'a Relocation>>(
relocations: I, relocations: I,
maps_by_section: &HashMap<usize, (&str, &Map)>, maps_by_section: &HashMap<usize, (&str, &Map)>,
symbol_table: &HashMap<usize, Symbol>, symbol_table: &HashMap<usize, Symbol>,
text_section_index: Option<usize>,
) -> Result<(), RelocationError> { ) -> Result<(), RelocationError> {
let section_offset = fun.section_offset; let section_offset = fun.section_offset;
let instructions = &mut fun.instructions; let instructions = &mut fun.instructions;
@ -136,11 +143,6 @@ fn relocate_maps<'a, I: Iterator<Item = &'a Relocation>>(
} }
let ins_index = ins_offset / INS_SIZE; let ins_index = ins_offset / INS_SIZE;
// calls are relocated in a separate step
if is_call(&instructions[ins_index]) {
continue;
}
// a map relocation points to the ELF section that contains the map // a map relocation points to the ELF section that contains the map
let sym = symbol_table let sym = symbol_table
.get(&rel.symbol_index) .get(&rel.symbol_index)
@ -154,18 +156,23 @@ fn relocate_maps<'a, I: Iterator<Item = &'a Relocation>>(
None => continue, None => continue,
}; };
// calls and relocation to .text symbols are handled in a separate step
if insn_is_call(&instructions[ins_index]) || sym.section_index == text_section_index {
continue;
}
let (name, map) = let (name, map) =
maps_by_section maps_by_section
.get(&section_index.0) .get(&section_index)
.ok_or(RelocationError::SectionNotFound { .ok_or(RelocationError::SectionNotFound {
symbol_index: rel.symbol_index, symbol_index: rel.symbol_index,
symbol_name: sym.name.clone(), symbol_name: sym.name.clone(),
section_index: section_index.0, section_index,
})?; })?;
let map_fd = map.fd.ok_or_else(|| RelocationError::MapNotCreated { let map_fd = map.fd.ok_or_else(|| RelocationError::MapNotCreated {
name: (*name).into(), name: (*name).into(),
section_index: section_index.0, section_index,
})?; })?;
if !map.obj.data.is_empty() { if !map.obj.data.is_empty() {
@ -181,6 +188,7 @@ fn relocate_maps<'a, I: Iterator<Item = &'a Relocation>>(
} }
struct FunctionLinker<'a> { struct FunctionLinker<'a> {
text_section_index: Option<usize>,
functions: &'a HashMap<u64, Function>, functions: &'a HashMap<u64, Function>,
linked_functions: HashMap<u64, usize>, linked_functions: HashMap<u64, usize>,
relocations: &'a HashMap<SectionIndex, HashMap<u64, Relocation>>, relocations: &'a HashMap<SectionIndex, HashMap<u64, Relocation>>,
@ -189,11 +197,13 @@ struct FunctionLinker<'a> {
impl<'a> FunctionLinker<'a> { impl<'a> FunctionLinker<'a> {
fn new( fn new(
text_section_index: Option<usize>,
functions: &'a HashMap<u64, Function>, functions: &'a HashMap<u64, Function>,
relocations: &'a HashMap<SectionIndex, HashMap<u64, Relocation>>, relocations: &'a HashMap<SectionIndex, HashMap<u64, Relocation>>,
symbol_table: &'a HashMap<usize, Symbol>, symbol_table: &'a HashMap<usize, Symbol>,
) -> FunctionLinker<'a> { ) -> FunctionLinker<'a> {
FunctionLinker { FunctionLinker {
text_section_index,
functions, functions,
linked_functions: HashMap::new(), linked_functions: HashMap::new(),
relocations, relocations,
@ -242,17 +252,8 @@ impl<'a> FunctionLinker<'a> {
fn relocate(&mut self, program: &mut Function, fun: &Function) -> Result<(), RelocationError> { fn relocate(&mut self, program: &mut Function, fun: &Function) -> Result<(), RelocationError> {
let relocations = self.relocations.get(&fun.section_index); let relocations = self.relocations.get(&fun.section_index);
let rel_info = |offset| relocations.and_then(|rels| rels.get(&offset));
let rel_target_address = |rel: &Relocation, symbol_table: &HashMap<usize, Symbol>| {
let sym =
symbol_table
.get(&rel.symbol_index)
.ok_or(RelocationError::UnknownSymbol {
index: rel.symbol_index,
})?;
Ok(sym.address) debug!("relocating program {} function {}", program.name, fun.name);
};
let n_instructions = fun.instructions.len(); let n_instructions = fun.instructions.len();
let start_ins = program.instructions.len() - n_instructions; let start_ins = program.instructions.len() - n_instructions;
@ -260,26 +261,66 @@ impl<'a> FunctionLinker<'a> {
// process all the instructions. We can't only loop over relocations since we need to // process all the instructions. We can't only loop over relocations since we need to
// patch pc-relative calls too. // patch pc-relative calls too.
for ins_index in start_ins..start_ins + n_instructions { for ins_index in start_ins..start_ins + n_instructions {
if !is_call(&program.instructions[ins_index]) { let ins = program.instructions[ins_index];
let is_call = insn_is_call(&ins);
// only resolve relocations for calls or for instructions that
// reference symbols in the .text section (eg let callback =
// &some_fun)
let rel = if let Some(relocations) = relocations {
self.text_relocation_info(
relocations,
(fun.section_offset + (ins_index - start_ins) * INS_SIZE) as u64,
)?
// if not a call and not a .text reference, ignore the
// relocation (see relocate_maps())
.and_then(|(_, sym)| {
if is_call {
return Some(sym.address);
}
match sym.kind {
SymbolKind::Text => Some(sym.address),
SymbolKind::Section if sym.section_index == self.text_section_index => {
Some(sym.address + ins.imm as u64)
}
_ => None,
}
})
} else {
None
};
// some_fun() or let x = &some_fun trigger linking, everything else
// can be ignored here
if !is_call && rel.is_none() {
continue; continue;
} }
let callee_address = if let Some(rel) = let callee_address = if let Some(address) = rel {
rel_info((fun.section_offset + (ins_index - start_ins) * INS_SIZE) as u64)
{
// We have a relocation entry for the instruction at `ins_index`, the address of // We have a relocation entry for the instruction at `ins_index`, the address of
// the callee is the address of the relocation's target symbol. // the callee is the address of the relocation's target symbol.
rel_target_address(rel, self.symbol_table)? address
} else { } else {
// The caller and the callee are in the same ELF section and this is a pc-relative // The caller and the callee are in the same ELF section and this is a pc-relative
// call. Resolve the pc-relative imm to an absolute address. // call. Resolve the pc-relative imm to an absolute address.
let ins_size = INS_SIZE as i64; let ins_size = INS_SIZE as i64;
(fun.section_offset as i64 (fun.section_offset as i64
+ ((ins_index - start_ins) as i64) * ins_size + ((ins_index - start_ins) as i64) * ins_size
+ (program.instructions[ins_index].imm + 1) as i64 * ins_size) + (ins.imm + 1) as i64 * ins_size) as u64
as u64
}; };
debug!(
"relocating {} to callee address {} ({})",
if is_call { "call" } else { "reference" },
callee_address,
if rel.is_some() {
"relocation"
} else {
"relative"
},
);
// lookup and link the callee if it hasn't been linked already. `callee_ins_index` will // lookup and link the callee if it hasn't been linked already. `callee_ins_index` will
// contain the instruction index of the callee inside the program. // contain the instruction index of the callee inside the program.
let callee = let callee =
@ -289,6 +330,9 @@ impl<'a> FunctionLinker<'a> {
address: callee_address, address: callee_address,
caller_name: fun.name.clone(), caller_name: fun.name.clone(),
})?; })?;
debug!("callee is {}", callee.name);
let callee_ins_index = self.link_function(program, callee)?; let callee_ins_index = self.link_function(program, callee)?;
let mut ins = &mut program.instructions[ins_index]; let mut ins = &mut program.instructions[ins_index];
@ -297,8 +341,16 @@ impl<'a> FunctionLinker<'a> {
} else { } else {
(callee_ins_index - ins_index - 1) as i32 (callee_ins_index - ins_index - 1) as i32
}; };
if !is_call {
ins.set_src_reg(BPF_PSEUDO_FUNC as u8);
}
} }
debug!(
"finished relocating program {} function {}",
program.name, fun.name
);
Ok(()) Ok(())
} }
@ -321,16 +373,35 @@ impl<'a> FunctionLinker<'a> {
let line_info = &fun.line_info.line_info; let line_info = &fun.line_info.line_info;
let line_info = line_info.iter().map(|l| { let line_info = line_info.iter().map(|l| {
let mut new = *l; let mut new = *l;
new.insn_off = l.insn_off + off_adj as u32; new.insn_off = start as u32 + l.insn_off;
new new
}); });
program.line_info.line_info.extend(line_info); program.line_info.line_info.extend(line_info);
program.line_info.num_info = program.func_info.func_info.len() as u32; program.line_info.num_info = program.func_info.func_info.len() as u32;
Ok(()) Ok(())
} }
fn text_relocation_info(
&self,
relocations: &HashMap<u64, Relocation>,
offset: u64,
) -> Result<Option<(Relocation, Symbol)>, RelocationError> {
if let Some(rel) = relocations.get(&offset) {
let sym =
self.symbol_table
.get(&rel.symbol_index)
.ok_or(RelocationError::UnknownSymbol {
index: rel.symbol_index,
})?;
Ok(Some((*rel, sym.clone())))
} else {
Ok(None)
}
}
} }
fn is_call(ins: &bpf_insn) -> bool { fn insn_is_call(ins: &bpf_insn) -> bool {
let klass = (ins.code & 0x07) as u32; let klass = (ins.code & 0x07) as u32;
let op = (ins.code & 0xF0) as u32; let op = (ins.code & 0xF0) as u32;
let src = (ins.code & 0x08) as u32; let src = (ins.code & 0x08) as u32;

Loading…
Cancel
Save