Compare commits
10 commits
b9e56b5add
...
c27e748f43
Author | SHA1 | Date | |
---|---|---|---|
Jan-Erik Rediger | c27e748f43 | ||
Jan-Erik Rediger | f76597cb7c | ||
Jan-Erik Rediger | 6dba4064f2 | ||
Jan-Erik Rediger | 53708c2bb4 | ||
Jan-Erik Rediger | 1297ce2668 | ||
Jan-Erik Rediger | ffec4b5769 | ||
Jan-Erik Rediger | c07018a3aa | ||
Jan-Erik Rediger | 992506e516 | ||
Jan-Erik Rediger | 25d945f084 | ||
Jan-Erik Rediger | e005020934 |
121
.github/workflows/CI.yml
vendored
Normal file
121
.github/workflows/CI.yml
vendored
Normal file
|
@ -0,0 +1,121 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- '*'
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
linux:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target: [x86_64, x86, aarch64]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- name: Build wheels
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
args: --release --out dist --find-interpreter
|
||||
sccache: 'true'
|
||||
manylinux: auto
|
||||
- name: Upload wheels
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
path: dist
|
||||
|
||||
windows:
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target: [x64, x86]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
architecture: ${{ matrix.target }}
|
||||
- name: Build wheels
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
args: --release --out dist --find-interpreter
|
||||
sccache: 'true'
|
||||
- name: Upload wheels
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
path: dist
|
||||
|
||||
macos:
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target: [x86_64, aarch64]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- name: Build wheels
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
args: --release --out dist --find-interpreter
|
||||
sccache: 'true'
|
||||
- name: Upload wheels
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
path: dist
|
||||
- name: Test
|
||||
run: |
|
||||
python3 -m venv ${PWD}/.venv
|
||||
. .venv/bin/activate
|
||||
maturin --version
|
||||
maturin develop
|
||||
python3 test.py
|
||||
|
||||
sdist:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build sdist
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
command: sdist
|
||||
args: --out dist
|
||||
- name: Upload sdist
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
path: dist
|
||||
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
if: "startsWith(github.ref, 'refs/tags/')"
|
||||
needs: [linux, windows, macos, sdist]
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
- name: Publish to PyPI
|
||||
uses: PyO3/maturin-action@v1
|
||||
env:
|
||||
MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
|
||||
with:
|
||||
command: upload
|
||||
args: --non-interactive --skip-existing *
|
16
Cargo.lock
generated
16
Cargo.lock
generated
|
@ -31,14 +31,6 @@ dependencies = [
|
|||
"scroll",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "goblin-pyo3"
|
||||
version = "0.0.2"
|
||||
dependencies = [
|
||||
"goblin",
|
||||
"pyo3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
|
@ -82,6 +74,14 @@ dependencies = [
|
|||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oelf"
|
||||
version = "0.0.4"
|
||||
dependencies = [
|
||||
"goblin",
|
||||
"pyo3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "goblin-pyo3"
|
||||
version = "0.0.2"
|
||||
name = "oelf"
|
||||
version = "0.0.4"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
|
|
@ -10,4 +10,4 @@ classifiers = [
|
|||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
"Programming Language :: Python :: Implementation :: PyPy",
|
||||
]
|
||||
|
||||
dynamic = ["version"]
|
||||
|
|
44
src/lib.rs
44
src/lib.rs
|
@ -3,19 +3,23 @@ use std::{
|
|||
io::Read,
|
||||
};
|
||||
|
||||
use goblin::mach::Mach;
|
||||
use goblin::mach::{Mach, SingleArch};
|
||||
use pyo3::{exceptions::PyTypeError, prelude::*};
|
||||
|
||||
mod exports;
|
||||
mod header;
|
||||
mod imports;
|
||||
mod load_commands;
|
||||
mod sections;
|
||||
mod segments;
|
||||
mod symbols;
|
||||
|
||||
use exports::Export;
|
||||
use header::Header;
|
||||
use imports::Import;
|
||||
use load_commands::LoadCommand;
|
||||
use sections::{Section, Sections};
|
||||
use segments::Segment;
|
||||
use symbols::Symbols;
|
||||
|
||||
#[pyclass]
|
||||
|
@ -57,6 +61,24 @@ impl Object {
|
|||
|
||||
let macho = match object {
|
||||
goblin::Object::Mach(Mach::Binary(macho)) => macho,
|
||||
goblin::Object::Mach(Mach::Fat(march)) => {
|
||||
let mut macho = None;
|
||||
for arch in &march {
|
||||
let arch = arch.map_err(|_| {
|
||||
PyErr::new::<PyTypeError, _>("cannot parse single arch from Mach-O file")
|
||||
})?;
|
||||
|
||||
if let SingleArch::MachO(m) = arch {
|
||||
macho = Some(m);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
match macho {
|
||||
Some(macho) => macho,
|
||||
None => return Err(PyErr::new::<PyTypeError, _>("not a macho file")),
|
||||
}
|
||||
}
|
||||
_ => return Err(PyErr::new::<PyTypeError, _>("not a macho file")),
|
||||
};
|
||||
|
||||
|
@ -82,13 +104,23 @@ impl Object {
|
|||
Symbols::from(self.macho().symbols())
|
||||
}
|
||||
|
||||
fn segments(&self) -> Vec<Segment> {
|
||||
self.macho()
|
||||
.segments
|
||||
.into_iter()
|
||||
.map(|seg| seg.into())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn sections(&self) -> Sections {
|
||||
let macho = self.macho();
|
||||
let mut sections = vec![];
|
||||
let mut idx = 0;
|
||||
for sect_iter in macho.segments.sections() {
|
||||
sections.extend(sect_iter.map(|section| {
|
||||
idx += 1;
|
||||
let (sect, _data) = section.unwrap();
|
||||
Section::from(sect)
|
||||
Section::from((idx, sect))
|
||||
}));
|
||||
}
|
||||
Sections { sections }
|
||||
|
@ -119,6 +151,14 @@ impl Object {
|
|||
.map_err(|_| PyErr::new::<PyTypeError, _>("failed"))?;
|
||||
Ok(imports.into_iter().map(|exp| exp.into()).collect())
|
||||
}
|
||||
|
||||
fn load_commands(&self) -> Vec<LoadCommand> {
|
||||
self.macho()
|
||||
.load_commands
|
||||
.iter()
|
||||
.map(|cmd| cmd.into())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Object {
|
||||
|
|
26
src/load_commands.rs
Normal file
26
src/load_commands.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
use pyo3::prelude::*;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[pyclass]
|
||||
pub struct LoadCommand {
|
||||
#[pyo3(get)]
|
||||
offset: usize,
|
||||
#[pyo3(get)]
|
||||
command: String,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl LoadCommand {
|
||||
fn __repr__(&self) -> String {
|
||||
format!("{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&goblin::mach::load_command::LoadCommand> for LoadCommand {
|
||||
fn from(lcmd: &goblin::mach::load_command::LoadCommand) -> Self {
|
||||
LoadCommand {
|
||||
offset: lcmd.offset,
|
||||
command: format!("{:?}", lcmd.command),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,8 @@ use pyo3::prelude::*;
|
|||
#[derive(Debug, Clone)]
|
||||
#[pyclass]
|
||||
pub struct Section {
|
||||
#[pyo3(get)]
|
||||
index: usize,
|
||||
#[pyo3(get)]
|
||||
name: Option<String>,
|
||||
#[pyo3(get)]
|
||||
|
@ -30,9 +32,10 @@ impl Section {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<goblin::mach::segment::Section> for Section {
|
||||
fn from(section: goblin::mach::segment::Section) -> Self {
|
||||
impl From<(usize, goblin::mach::segment::Section)> for Section {
|
||||
fn from((index, section): (usize, goblin::mach::segment::Section)) -> Self {
|
||||
Section {
|
||||
index,
|
||||
name: section.name().ok().map(|s| s.to_string()),
|
||||
segment: section.segname().ok().map(|s| s.to_string()),
|
||||
addr: section.addr,
|
||||
|
|
54
src/segments.rs
Normal file
54
src/segments.rs
Normal file
|
@ -0,0 +1,54 @@
|
|||
use pyo3::prelude::*;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[pyclass]
|
||||
pub struct Segment {
|
||||
#[pyo3(get)]
|
||||
cmd: u32,
|
||||
#[pyo3(get)]
|
||||
cmdsize: u32,
|
||||
#[pyo3(get)]
|
||||
name: Option<String>,
|
||||
#[pyo3(get)]
|
||||
vmaddr: u64,
|
||||
#[pyo3(get)]
|
||||
vmsize: u64,
|
||||
#[pyo3(get)]
|
||||
fileoff: u64,
|
||||
#[pyo3(get)]
|
||||
filesize: u64,
|
||||
#[pyo3(get)]
|
||||
maxprot: u32,
|
||||
#[pyo3(get)]
|
||||
initprot: u32,
|
||||
#[pyo3(get)]
|
||||
nsects: u32,
|
||||
#[pyo3(get)]
|
||||
flags: u32,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl Segment {
|
||||
fn __repr__(&self) -> String {
|
||||
format!("{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&goblin::mach::segment::Segment<'_>> for Segment {
|
||||
fn from(segm: &goblin::mach::segment::Segment) -> Self {
|
||||
let segname = segm.name().ok().map(|s| s.to_string());
|
||||
Segment {
|
||||
cmd: segm.cmd,
|
||||
cmdsize: segm.cmdsize,
|
||||
name: segname,
|
||||
vmaddr: segm.vmaddr,
|
||||
vmsize: segm.vmsize,
|
||||
fileoff: segm.fileoff,
|
||||
filesize: segm.filesize,
|
||||
maxprot: segm.maxprot,
|
||||
initprot: segm.initprot,
|
||||
nsects: segm.nsects,
|
||||
flags: segm.flags,
|
||||
}
|
||||
}
|
||||
}
|
48
test.py
48
test.py
|
@ -1,28 +1,17 @@
|
|||
import sys
|
||||
import oelf
|
||||
|
||||
g = oelf.Object("test.py")
|
||||
g = oelf.Object("mylib.dylib")
|
||||
print(g.header)
|
||||
print(g.name)
|
||||
path = "target/debug/liboelf.dylib"
|
||||
if len(sys.argv) > 1:
|
||||
path = sys.argv[1]
|
||||
|
||||
g = oelf.Object(path)
|
||||
print(f"{g.header=}")
|
||||
print(f"{g.name=}")
|
||||
|
||||
print("symbols")
|
||||
globsym = None
|
||||
start = -1
|
||||
end = 9999999999
|
||||
|
||||
for sym in g.symbols():
|
||||
if "META" in sym.name and sym.is_global:
|
||||
if globsym is None:
|
||||
globsym = sym
|
||||
start = sym.meta.n_value
|
||||
|
||||
for sym in g.symbols():
|
||||
if sym.meta.n_value > start and sym.meta.n_value < end:
|
||||
print(f"sym after the found one: {sym}")
|
||||
end = sym.meta.n_value
|
||||
|
||||
print(f"found symbol {globsym.name} from {start} to {end}, size: {end-start}")
|
||||
print(globsym)
|
||||
for symbol in g.symbols():
|
||||
print(symbol)
|
||||
|
||||
print("libs")
|
||||
print(g.libs)
|
||||
|
@ -31,12 +20,21 @@ print("rpaths")
|
|||
print(g.rpaths)
|
||||
|
||||
print("exports")
|
||||
print(len(g.exports()))
|
||||
for export in g.exports():
|
||||
print(export)
|
||||
|
||||
print("imports")
|
||||
print(len(g.imports()))
|
||||
for imp in g.imports():
|
||||
print(imp)
|
||||
|
||||
print("segments")
|
||||
for segment in g.segments():
|
||||
print(f"{segment}")
|
||||
|
||||
print("sections")
|
||||
for section in g.sections():
|
||||
print(f"{section}")
|
||||
|
||||
for idx, section in enumerate(g.sections()):
|
||||
print(f"{idx+1}. {section}")
|
||||
print("load commands")
|
||||
for lcmd in g.load_commands():
|
||||
print(lcmd)
|
||||
|
|
Loading…
Reference in a new issue