From 489afcc88b46014857b9f6d38ba2f3a6939005ee Mon Sep 17 00:00:00 2001 From: Jan-Erik Rediger Date: Fri, 8 Dec 2023 17:28:36 +0100 Subject: [PATCH] section parsing --- crates/goblin-pyo3/src/lib.rs | 38 ++++++++++---- crates/goblin-pyo3/src/sections.rs | 79 ++++++++++++++++++++++++++++++ crates/goblin-pyo3/src/symbols.rs | 79 +++++++++++++++++++++++++++--- crates/goblin-pyo3/test.py | 10 +++- 4 files changed, 189 insertions(+), 17 deletions(-) create mode 100644 crates/goblin-pyo3/src/sections.rs diff --git a/crates/goblin-pyo3/src/lib.rs b/crates/goblin-pyo3/src/lib.rs index 3eb1f35..a0f9a60 100644 --- a/crates/goblin-pyo3/src/lib.rs +++ b/crates/goblin-pyo3/src/lib.rs @@ -1,19 +1,18 @@ -use std::{ - fs::File, - io::Read, -}; +use std::{fs::File, io::Read}; use goblin::mach::Mach; -use pyo3::{prelude::*, exceptions::PyTypeError}; +use pyo3::{exceptions::PyTypeError, prelude::*}; mod exports; mod header; mod imports; +mod sections; mod symbols; use exports::Export; use header::Header; use imports::Import; +use sections::{Sections, Section}; use symbols::Symbols; #[pyclass] @@ -74,6 +73,23 @@ impl Object { } } + fn sections(&self) -> Sections { + match self.inner.as_ref().unwrap() { + goblin::Object::Mach(Mach::Binary(macho)) => { + let mut sections = vec![]; + for sect_iter in macho.segments.sections() { + sections.extend(sect_iter.map(|section| { + let (sect, _data) = section.unwrap(); + Section::from(sect) + + })); + } + Sections { sections } + }, + _ => unimplemented!(), + } + } + #[getter] fn libs(&self) -> Vec<&str> { match self.inner.as_ref().unwrap() { @@ -93,9 +109,11 @@ impl Object { fn exports(&self) -> Result, PyErr> { match self.inner.as_ref().unwrap() { goblin::Object::Mach(Mach::Binary(macho)) => { - let exports = macho.exports().map_err(|_| PyErr::new::("failed"))?; + let exports = macho + .exports() + .map_err(|_| PyErr::new::("failed"))?; Ok(exports.into_iter().map(|exp| exp.into()).collect()) - }, + } _ => unimplemented!(), } } @@ -103,9 +121,11 @@ impl Object { fn imports(&self) -> Result, PyErr> { match self.inner.as_ref().unwrap() { goblin::Object::Mach(Mach::Binary(macho)) => { - let imports = macho.imports().map_err(|_| PyErr::new::("failed"))?; + let imports = macho + .imports() + .map_err(|_| PyErr::new::("failed"))?; Ok(imports.into_iter().map(|exp| exp.into()).collect()) - }, + } _ => unimplemented!(), } } diff --git a/crates/goblin-pyo3/src/sections.rs b/crates/goblin-pyo3/src/sections.rs new file mode 100644 index 0000000..9cf4878 --- /dev/null +++ b/crates/goblin-pyo3/src/sections.rs @@ -0,0 +1,79 @@ +use pyo3::prelude::*; + +#[derive(Debug, Clone)] +#[pyclass] +pub struct Section { + #[pyo3(get)] + name: Option, + #[pyo3(get)] + segment: Option, + #[pyo3(get)] + addr: u64, + #[pyo3(get)] + size: u64, + #[pyo3(get)] + offset: u32, + #[pyo3(get)] + align: u32, + #[pyo3(get)] + reloff: u32, + #[pyo3(get)] + nreloc: u32, + #[pyo3(get)] + flags: u32, +} + +#[pymethods] +impl Section { + fn __repr__(&self) -> String { + format!("{:?}", self) + } +} + +impl From for Section { + fn from(section: goblin::mach::segment::Section) -> Self { + Section { + name: section.name().ok().map(|s| s.to_string()), + segment: section.segname().ok().map(|s| s.to_string()), + addr: section.addr, + size: section.size, + offset: section.offset, + align: section.align, + reloff: section.reloff, + nreloc: section.nreloc, + flags: section.flags, + } + } +} + +#[derive(Clone)] +#[pyclass] +pub struct Sections { + pub(crate) sections: Vec
, +} + +#[pymethods] +impl Sections { + fn __iter__(slf: PyRef<'_, Self>) -> PyResult> { + let iter = SectionIter { + inner: slf.sections.clone().into_iter(), + }; + Py::new(slf.py(), iter) + } +} + +#[pyclass] +struct SectionIter { + inner: std::vec::IntoIter
, +} + +#[pymethods] +impl SectionIter { + fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> { + slf + } + + fn __next__(mut slf: PyRefMut<'_, Self>) -> Option
{ + slf.inner.next() + } +} diff --git a/crates/goblin-pyo3/src/symbols.rs b/crates/goblin-pyo3/src/symbols.rs index 58acb09..8b9e793 100644 --- a/crates/goblin-pyo3/src/symbols.rs +++ b/crates/goblin-pyo3/src/symbols.rs @@ -1,12 +1,74 @@ -use goblin::mach::symbols::Nlist; +use goblin::mach::symbols::{N_TYPE, n_type_to_str, N_EXT, N_WEAK_REF, N_WEAK_DEF, N_UNDF, N_STAB}; use pyo3::prelude::*; +#[derive(Debug, Clone)] +#[pyclass] +struct Nlist { + #[pyo3(get)] + n_strx: usize, + #[pyo3(get)] + n_type: u8, + #[pyo3(get)] + n_sect: usize, + #[pyo3(get)] + n_desc: u16, + #[pyo3(get)] + n_value: u64, +} + +#[pymethods] +impl Nlist { + fn __repr__(&self) -> String { + format!("{:?}", self) + } +} + +impl Nlist { + /// Gets this symbol's type in bits 0xe + pub fn get_type(&self) -> u8 { + self.n_type & N_TYPE + } + /// Gets the str representation of the type of this symbol + pub fn type_str(&self) -> &'static str { + n_type_to_str(self.get_type()) + } + /// Whether this symbol is global or not + pub fn is_global(&self) -> bool { + self.n_type & N_EXT != 0 + } + /// Whether this symbol is weak or not + pub fn is_weak(&self) -> bool { + self.n_desc & (N_WEAK_REF | N_WEAK_DEF) != 0 + } + /// Whether this symbol is undefined or not + pub fn is_undefined(&self) -> bool { + self.n_sect == 0 && self.n_type & N_TYPE == N_UNDF + } + /// Whether this symbol is a symbolic debugging entry + pub fn is_stab(&self) -> bool { + self.n_type & N_STAB != 0 + } +} + +impl From for Nlist { + fn from(list: goblin::mach::symbols::Nlist) -> Self { + Self { + n_strx: list.n_strx, + n_type: list.n_type, + n_sect: list.n_sect, + n_desc: list.n_desc, + n_value: list.n_value, + } + } +} + #[derive(Debug, Clone)] #[pyclass] struct Symbol { #[pyo3(get)] name: String, + #[pyo3(get)] meta: Nlist, } @@ -37,14 +99,21 @@ impl Symbol { self.meta.is_stab() } + #[getter] + fn section(&self) -> usize { + self.meta.n_sect + } + fn __repr__(&self) -> String { format!( - "Symbol {{ name: {}, global: {}, weak: {}, undefined: {}, stab: {} }}", + "Symbol {{ name: {}, type: {}, global: {}, weak: {}, undefined: {}, stab: {}, meta: {:?} }}", self.name, + self.typ(), self.global(), self.weak(), self.undefined(), - self.stab() + self.stab(), + self.meta, ) } } @@ -71,7 +140,7 @@ impl From> for Symbols { let (symname, meta) = sym.unwrap(); Symbol { name: symname.to_string(), - meta, + meta: meta.into(), } }) .collect(); @@ -95,5 +164,3 @@ impl SymbolIter { slf.inner.next() } } - - diff --git a/crates/goblin-pyo3/test.py b/crates/goblin-pyo3/test.py index d6a8724..47e2a0f 100644 --- a/crates/goblin-pyo3/test.py +++ b/crates/goblin-pyo3/test.py @@ -6,8 +6,9 @@ print(g.name) print("symbols") for sym in g.symbols(): - print(sym) - break + if not sym.undefined: + print(sym) + break print("libs") print(g.libs) @@ -20,3 +21,8 @@ print(len(g.exports())) print("imports") print(len(g.imports())) + +print("sections") +sections = iter(g.sections()) +print(next(sections)) +print(next(sections))