|
|
@ -51,7 +51,7 @@ pub struct Map {
|
|
|
|
pub(crate) struct Program {
|
|
|
|
pub(crate) struct Program {
|
|
|
|
pub(crate) license: CString,
|
|
|
|
pub(crate) license: CString,
|
|
|
|
pub(crate) kernel_version: KernelVersion,
|
|
|
|
pub(crate) kernel_version: KernelVersion,
|
|
|
|
pub(crate) kind: ProgramKind,
|
|
|
|
pub(crate) section: ProgramSection,
|
|
|
|
pub(crate) function: Function,
|
|
|
|
pub(crate) function: Function,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -64,49 +64,86 @@ pub(crate) struct Function {
|
|
|
|
pub(crate) instructions: Vec<bpf_insn>,
|
|
|
|
pub(crate) instructions: Vec<bpf_insn>,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub enum ProgramKind {
|
|
|
|
pub enum ProgramSection {
|
|
|
|
KProbe,
|
|
|
|
KRetProbe { name: String },
|
|
|
|
KRetProbe,
|
|
|
|
KProbe { name: String },
|
|
|
|
UProbe,
|
|
|
|
UProbe { name: String },
|
|
|
|
URetProbe,
|
|
|
|
URetProbe { name: String },
|
|
|
|
TracePoint,
|
|
|
|
TracePoint { name: String },
|
|
|
|
SocketFilter,
|
|
|
|
SocketFilter { name: String },
|
|
|
|
Xdp,
|
|
|
|
Xdp { name: String },
|
|
|
|
SkMsg,
|
|
|
|
SkMsg { name: String },
|
|
|
|
SkSkbStreamParser,
|
|
|
|
SkSkbStreamParser { name: String },
|
|
|
|
SkSkbStreamVerdict,
|
|
|
|
SkSkbStreamVerdict { name: String },
|
|
|
|
SockOps,
|
|
|
|
SockOps { name: String },
|
|
|
|
SchedClassifier,
|
|
|
|
SchedClassifier { name: String },
|
|
|
|
CgroupSkbIngress,
|
|
|
|
CgroupSkbIngress { name: String },
|
|
|
|
CgroupSkbEgress,
|
|
|
|
CgroupSkbEgress { name: String },
|
|
|
|
LircMode2,
|
|
|
|
LircMode2 { name: String },
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl FromStr for ProgramKind {
|
|
|
|
impl ProgramSection {
|
|
|
|
|
|
|
|
fn name(&self) -> &str {
|
|
|
|
|
|
|
|
match self {
|
|
|
|
|
|
|
|
ProgramSection::KRetProbe { name } => name,
|
|
|
|
|
|
|
|
ProgramSection::KProbe { name } => name,
|
|
|
|
|
|
|
|
ProgramSection::UProbe { name } => name,
|
|
|
|
|
|
|
|
ProgramSection::URetProbe { name } => name,
|
|
|
|
|
|
|
|
ProgramSection::TracePoint { name } => name,
|
|
|
|
|
|
|
|
ProgramSection::SocketFilter { name } => name,
|
|
|
|
|
|
|
|
ProgramSection::Xdp { name } => name,
|
|
|
|
|
|
|
|
ProgramSection::SkMsg { name } => name,
|
|
|
|
|
|
|
|
ProgramSection::SkSkbStreamParser { name } => name,
|
|
|
|
|
|
|
|
ProgramSection::SkSkbStreamVerdict { name } => name,
|
|
|
|
|
|
|
|
ProgramSection::SockOps { name } => name,
|
|
|
|
|
|
|
|
ProgramSection::SchedClassifier { name } => name,
|
|
|
|
|
|
|
|
ProgramSection::CgroupSkbIngress { name } => name,
|
|
|
|
|
|
|
|
ProgramSection::CgroupSkbEgress { name } => name,
|
|
|
|
|
|
|
|
ProgramSection::LircMode2 { name } => name,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl FromStr for ProgramSection {
|
|
|
|
type Err = ParseError;
|
|
|
|
type Err = ParseError;
|
|
|
|
|
|
|
|
|
|
|
|
fn from_str(kind: &str) -> Result<ProgramKind, ParseError> {
|
|
|
|
fn from_str(section: &str) -> Result<ProgramSection, ParseError> {
|
|
|
|
use ProgramKind::*;
|
|
|
|
use ProgramSection::*;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// parse the common case, eg "xdp/program_name" or
|
|
|
|
|
|
|
|
// "sk_skb/stream_verdict/program_name"
|
|
|
|
|
|
|
|
let mut parts = section.rsplitn(2, "/").collect::<Vec<_>>();
|
|
|
|
|
|
|
|
if parts.len() == 1 {
|
|
|
|
|
|
|
|
parts.push(parts[0]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
let kind = parts[1];
|
|
|
|
|
|
|
|
let name = parts[0].to_owned();
|
|
|
|
|
|
|
|
|
|
|
|
Ok(match kind {
|
|
|
|
Ok(match kind {
|
|
|
|
"kprobe" => KProbe,
|
|
|
|
"kprobe" => KProbe { name },
|
|
|
|
"kretprobe" => KRetProbe,
|
|
|
|
"kretprobe" => KRetProbe { name },
|
|
|
|
"uprobe" => UProbe,
|
|
|
|
"uprobe" => UProbe { name },
|
|
|
|
"uretprobe" => URetProbe,
|
|
|
|
"uretprobe" => URetProbe { name },
|
|
|
|
"xdp" => Xdp,
|
|
|
|
"xdp" => Xdp { name },
|
|
|
|
"tracepoint" => TracePoint,
|
|
|
|
_ if kind.starts_with("tracepoint") || kind.starts_with("tp") => {
|
|
|
|
"socket_filter" => SocketFilter,
|
|
|
|
// tracepoint sections are named `tracepoint/category/event_name`,
|
|
|
|
"sk_msg" => SkMsg,
|
|
|
|
// and we want to parse the name as "category/event_name"
|
|
|
|
"sk_skb/stream_parser" => SkSkbStreamParser,
|
|
|
|
let name = section.splitn(2, "/").last().unwrap().to_owned();
|
|
|
|
"sk_skb/stream_verdict" => SkSkbStreamVerdict,
|
|
|
|
TracePoint { name }
|
|
|
|
"sockops" => SockOps,
|
|
|
|
}
|
|
|
|
"classifier" => SchedClassifier,
|
|
|
|
"socket_filter" => SocketFilter { name },
|
|
|
|
"cgroup_skb/ingress" => CgroupSkbIngress,
|
|
|
|
"sk_msg" => SkMsg { name },
|
|
|
|
"cgroup_skb/egress" => CgroupSkbEgress,
|
|
|
|
"sk_skb/stream_parser" => SkSkbStreamParser { name },
|
|
|
|
"lirc_mode2" => LircMode2,
|
|
|
|
"sk_skb/stream_verdict" => SkSkbStreamVerdict { name },
|
|
|
|
|
|
|
|
"sockops" => SockOps { name },
|
|
|
|
|
|
|
|
"classifier" => SchedClassifier { name },
|
|
|
|
|
|
|
|
"cgroup_skb/ingress" => CgroupSkbIngress { name },
|
|
|
|
|
|
|
|
"cgroup_skb/egress" => CgroupSkbEgress { name },
|
|
|
|
|
|
|
|
"lirc_mode2" => LircMode2 { name },
|
|
|
|
_ => {
|
|
|
|
_ => {
|
|
|
|
return Err(ParseError::InvalidProgramKind {
|
|
|
|
return Err(ParseError::InvalidProgramSection {
|
|
|
|
kind: kind.to_string(),
|
|
|
|
section: section.to_owned(),
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
@ -181,18 +218,15 @@ impl Object {
|
|
|
|
Ok(())
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn parse_program(
|
|
|
|
fn parse_program(&self, section: &Section) -> Result<Program, ParseError> {
|
|
|
|
&self,
|
|
|
|
let prog_sec = ProgramSection::from_str(section.name)?;
|
|
|
|
section: &Section,
|
|
|
|
let name = prog_sec.name().to_owned();
|
|
|
|
ty: &str,
|
|
|
|
|
|
|
|
name: &str,
|
|
|
|
|
|
|
|
) -> Result<Program, ParseError> {
|
|
|
|
|
|
|
|
Ok(Program {
|
|
|
|
Ok(Program {
|
|
|
|
license: self.license.clone(),
|
|
|
|
license: self.license.clone(),
|
|
|
|
kernel_version: self.kernel_version,
|
|
|
|
kernel_version: self.kernel_version,
|
|
|
|
kind: ProgramKind::from_str(ty)?,
|
|
|
|
section: prog_sec,
|
|
|
|
function: Function {
|
|
|
|
function: Function {
|
|
|
|
name: name.to_owned(),
|
|
|
|
name,
|
|
|
|
address: section.address,
|
|
|
|
address: section.address,
|
|
|
|
section_index: section.index,
|
|
|
|
section_index: section.index,
|
|
|
|
section_offset: 0,
|
|
|
|
section_offset: 0,
|
|
|
@ -276,38 +310,23 @@ impl Object {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
match parts.as_slice() {
|
|
|
|
match section.name {
|
|
|
|
&[name]
|
|
|
|
name if name == ".bss" || name.starts_with(".data") || name.starts_with(".rodata") => {
|
|
|
|
if name == ".bss" || name.starts_with(".data") || name.starts_with(".rodata") =>
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
self.maps
|
|
|
|
self.maps
|
|
|
|
.insert(name.to_string(), parse_map(§ion, name)?);
|
|
|
|
.insert(name.to_string(), parse_map(§ion, name)?);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
&[name] if name.starts_with(".text") => self.parse_text_section(section)?,
|
|
|
|
name if name.starts_with(".text") => self.parse_text_section(section)?,
|
|
|
|
&[".BTF"] => self.parse_btf(§ion)?,
|
|
|
|
".BTF" => self.parse_btf(§ion)?,
|
|
|
|
&[".BTF.ext"] => self.parse_btf_ext(§ion)?,
|
|
|
|
".BTF.ext" => self.parse_btf_ext(§ion)?,
|
|
|
|
&["maps", name] => {
|
|
|
|
map if map.starts_with("maps/") => {
|
|
|
|
|
|
|
|
let name = map.splitn(2, "/").last().unwrap();
|
|
|
|
self.maps
|
|
|
|
self.maps
|
|
|
|
.insert(name.to_string(), parse_map(§ion, name)?);
|
|
|
|
.insert(name.to_string(), parse_map(§ion, name)?);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
&[ty @ "kprobe", name]
|
|
|
|
name if is_program_section(name) => {
|
|
|
|
| &[ty @ "kretprobe", name]
|
|
|
|
let program = self.parse_program(§ion)?;
|
|
|
|
| &[ty @ "uprobe", name]
|
|
|
|
|
|
|
|
| &[ty @ "uretprobe", name]
|
|
|
|
|
|
|
|
| &[ty @ "socket_filter", name]
|
|
|
|
|
|
|
|
| &[ty @ "xdp", name]
|
|
|
|
|
|
|
|
| &[ty @ "tracepoint", name]
|
|
|
|
|
|
|
|
| &[ty @ "sk_msg", name]
|
|
|
|
|
|
|
|
| &[ty @ "sk_skb/stream_parser", name]
|
|
|
|
|
|
|
|
| &[ty @ "sk_skb/stream_verdict", name]
|
|
|
|
|
|
|
|
| &[ty @ "sockops", name]
|
|
|
|
|
|
|
|
| &[ty @ "classifier", name]
|
|
|
|
|
|
|
|
| &[ty @ "cgroup_skb/ingress", name]
|
|
|
|
|
|
|
|
| &[ty @ "cgroup_skb/egress", name]
|
|
|
|
|
|
|
|
| &[ty @ "cgroup/skb", name]
|
|
|
|
|
|
|
|
| &[ty @ "lirc_mode2", name] => {
|
|
|
|
|
|
|
|
self.programs
|
|
|
|
self.programs
|
|
|
|
.insert(name.to_string(), self.parse_program(§ion, ty, name)?);
|
|
|
|
.insert(program.section.name().to_owned(), program);
|
|
|
|
if !section.relocations.is_empty() {
|
|
|
|
if !section.relocations.is_empty() {
|
|
|
|
self.relocations.insert(
|
|
|
|
self.relocations.insert(
|
|
|
|
section.index,
|
|
|
|
section.index,
|
|
|
@ -351,8 +370,8 @@ pub enum ParseError {
|
|
|
|
#[error("unsupported relocation target")]
|
|
|
|
#[error("unsupported relocation target")]
|
|
|
|
UnsupportedRelocationTarget,
|
|
|
|
UnsupportedRelocationTarget,
|
|
|
|
|
|
|
|
|
|
|
|
#[error("invalid program kind `{kind}`")]
|
|
|
|
#[error("invalid program section `{section}`")]
|
|
|
|
InvalidProgramKind { kind: String },
|
|
|
|
InvalidProgramSection { section: String },
|
|
|
|
|
|
|
|
|
|
|
|
#[error("invalid program code")]
|
|
|
|
#[error("invalid program code")]
|
|
|
|
InvalidProgramCode,
|
|
|
|
InvalidProgramCode,
|
|
|
@ -516,6 +535,34 @@ fn copy_instructions(data: &[u8]) -> Result<Vec<bpf_insn>, ParseError> {
|
|
|
|
Ok(instructions)
|
|
|
|
Ok(instructions)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn is_program_section(name: &str) -> bool {
|
|
|
|
|
|
|
|
for prefix in &[
|
|
|
|
|
|
|
|
"classifier",
|
|
|
|
|
|
|
|
"cgroup/skb",
|
|
|
|
|
|
|
|
"cgroup_skb/egress",
|
|
|
|
|
|
|
|
"cgroup_skb/ingress",
|
|
|
|
|
|
|
|
"kprobe",
|
|
|
|
|
|
|
|
"kretprobe",
|
|
|
|
|
|
|
|
"lirc_mode2",
|
|
|
|
|
|
|
|
"sk_msg",
|
|
|
|
|
|
|
|
"sk_skb/stream_parser",
|
|
|
|
|
|
|
|
"sk_skb/stream_verdict",
|
|
|
|
|
|
|
|
"socket_filter",
|
|
|
|
|
|
|
|
"sockops",
|
|
|
|
|
|
|
|
"tp",
|
|
|
|
|
|
|
|
"tracepoint",
|
|
|
|
|
|
|
|
"uprobe",
|
|
|
|
|
|
|
|
"uretprobe",
|
|
|
|
|
|
|
|
"xdp",
|
|
|
|
|
|
|
|
] {
|
|
|
|
|
|
|
|
if name.starts_with(prefix) {
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
false
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
mod tests {
|
|
|
|
use matches::assert_matches;
|
|
|
|
use matches::assert_matches;
|
|
|
@ -723,11 +770,7 @@ mod tests {
|
|
|
|
let obj = fake_obj();
|
|
|
|
let obj = fake_obj();
|
|
|
|
|
|
|
|
|
|
|
|
assert_matches!(
|
|
|
|
assert_matches!(
|
|
|
|
obj.parse_program(
|
|
|
|
obj.parse_program(&fake_section("kprobe/foo", &42u32.to_ne_bytes(),),),
|
|
|
|
&fake_section("kprobe/foo", &42u32.to_ne_bytes(),),
|
|
|
|
|
|
|
|
"kprobe",
|
|
|
|
|
|
|
|
"foo"
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
Err(ParseError::InvalidProgramCode)
|
|
|
|
Err(ParseError::InvalidProgramCode)
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -737,11 +780,11 @@ mod tests {
|
|
|
|
let obj = fake_obj();
|
|
|
|
let obj = fake_obj();
|
|
|
|
|
|
|
|
|
|
|
|
assert_matches!(
|
|
|
|
assert_matches!(
|
|
|
|
obj.parse_program(&fake_section("kprobe/foo", bytes_of(&fake_ins())), "kprobe", "foo"),
|
|
|
|
obj.parse_program(&fake_section("kprobe/foo", bytes_of(&fake_ins()))),
|
|
|
|
Ok(Program {
|
|
|
|
Ok(Program {
|
|
|
|
license,
|
|
|
|
license,
|
|
|
|
kernel_version: KernelVersion::Any,
|
|
|
|
kernel_version: KernelVersion::Any,
|
|
|
|
kind: ProgramKind::KProbe,
|
|
|
|
section: ProgramSection::KProbe { .. },
|
|
|
|
function: Function {
|
|
|
|
function: Function {
|
|
|
|
name,
|
|
|
|
name,
|
|
|
|
address: 0,
|
|
|
|
address: 0,
|
|
|
@ -820,7 +863,7 @@ mod tests {
|
|
|
|
assert_matches!(
|
|
|
|
assert_matches!(
|
|
|
|
obj.programs.get("foo"),
|
|
|
|
obj.programs.get("foo"),
|
|
|
|
Some(Program {
|
|
|
|
Some(Program {
|
|
|
|
kind: ProgramKind::KProbe,
|
|
|
|
section: ProgramSection::KProbe { .. },
|
|
|
|
..
|
|
|
|
..
|
|
|
|
})
|
|
|
|
})
|
|
|
|
);
|
|
|
|
);
|
|
|
@ -837,7 +880,7 @@ mod tests {
|
|
|
|
assert_matches!(
|
|
|
|
assert_matches!(
|
|
|
|
obj.programs.get("foo"),
|
|
|
|
obj.programs.get("foo"),
|
|
|
|
Some(Program {
|
|
|
|
Some(Program {
|
|
|
|
kind: ProgramKind::UProbe,
|
|
|
|
section: ProgramSection::UProbe { .. },
|
|
|
|
..
|
|
|
|
..
|
|
|
|
})
|
|
|
|
})
|
|
|
|
);
|
|
|
|
);
|
|
|
@ -854,7 +897,19 @@ mod tests {
|
|
|
|
assert_matches!(
|
|
|
|
assert_matches!(
|
|
|
|
obj.programs.get("foo"),
|
|
|
|
obj.programs.get("foo"),
|
|
|
|
Some(Program {
|
|
|
|
Some(Program {
|
|
|
|
kind: ProgramKind::TracePoint,
|
|
|
|
section: ProgramSection::TracePoint { .. },
|
|
|
|
|
|
|
|
..
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assert_matches!(
|
|
|
|
|
|
|
|
obj.parse_section(fake_section("tp/foo/bar", bytes_of(&fake_ins()))),
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_matches!(
|
|
|
|
|
|
|
|
obj.programs.get("foo/bar"),
|
|
|
|
|
|
|
|
Some(Program {
|
|
|
|
|
|
|
|
section: ProgramSection::TracePoint { .. },
|
|
|
|
..
|
|
|
|
..
|
|
|
|
})
|
|
|
|
})
|
|
|
|
);
|
|
|
|
);
|
|
|
@ -871,7 +926,7 @@ mod tests {
|
|
|
|
assert_matches!(
|
|
|
|
assert_matches!(
|
|
|
|
obj.programs.get("foo"),
|
|
|
|
obj.programs.get("foo"),
|
|
|
|
Some(Program {
|
|
|
|
Some(Program {
|
|
|
|
kind: ProgramKind::SocketFilter,
|
|
|
|
section: ProgramSection::SocketFilter { .. },
|
|
|
|
..
|
|
|
|
..
|
|
|
|
})
|
|
|
|
})
|
|
|
|
);
|
|
|
|
);
|
|
|
@ -888,7 +943,7 @@ mod tests {
|
|
|
|
assert_matches!(
|
|
|
|
assert_matches!(
|
|
|
|
obj.programs.get("foo"),
|
|
|
|
obj.programs.get("foo"),
|
|
|
|
Some(Program {
|
|
|
|
Some(Program {
|
|
|
|
kind: ProgramKind::Xdp,
|
|
|
|
section: ProgramSection::Xdp { .. },
|
|
|
|
..
|
|
|
|
..
|
|
|
|
})
|
|
|
|
})
|
|
|
|
);
|
|
|
|
);
|
|
|
|