mirror of
https://github.com/radareorg/r2pipe.rs.git
synced 2026-11-08 01:08:20 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c3277646a | ||
|
|
c504deaff8 | ||
|
|
7cd1560585 | ||
|
|
ee36c46657 | ||
|
|
1208b28183 | ||
|
|
522a9f7a4f |
No files matched your search
@@ -1,4 +1,7 @@
|
||||
name: R2pipe Rust CI
|
||||
name: ci
|
||||
|
||||
env:
|
||||
R2V: 6.1.0
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -10,10 +13,10 @@ on:
|
||||
|
||||
jobs:
|
||||
build_and_test:
|
||||
name: R2pipe.rs
|
||||
name: R2pipe.rs (Ubuntu)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: wget https://github.com/radareorg/radare2/releases/download/5.8.0/radare2_5.8.0_amd64.deb && sudo dpkg -i radare2*.deb
|
||||
- run: wget https://github.com/radareorg/radare2/releases/download/${{env.R2V}}/radare2_${{env.R2V}}_amd64.deb && sudo dpkg -i radare2*.deb
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
@@ -22,17 +25,36 @@ jobs:
|
||||
override: true
|
||||
- run: rustup component add rustfmt clippy
|
||||
- uses: actions-rs/cargo@v1
|
||||
name: Building
|
||||
with:
|
||||
command: build
|
||||
args: --release --all-features
|
||||
- uses: actions-rs/cargo@v1
|
||||
name: Testing
|
||||
with:
|
||||
command: test
|
||||
- uses: actions-rs/cargo@v1
|
||||
name: Format
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
- uses: actions-rs/cargo@v1
|
||||
name: Clippy
|
||||
with:
|
||||
command: clippy
|
||||
args: -- -D warnings
|
||||
|
||||
build_and_test_windows:
|
||||
name: R2pipe.rs (Windows)
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install radare2
|
||||
run: |
|
||||
Invoke-WebRequest -Uri "https://github.com/radareorg/radare2/releases/download/${{env.R2V}}/radare2-${{env.R2V}}-w64.zip" -OutFile "radare2.zip"
|
||||
Expand-Archive -Path "radare2.zip" -DestinationPath "C:\radare2"
|
||||
$r2path = (Get-ChildItem -Path "C:\radare2" -Directory | Select-Object -First 1).FullName
|
||||
echo "$r2path\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- run: cargo build --release --all-features
|
||||
- run: cargo test
|
||||
+6
-1
@@ -20,8 +20,13 @@ readme = "README.md"
|
||||
# License information
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[features]
|
||||
docsrs = []
|
||||
http = []
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.81"
|
||||
libloading = "0.8"
|
||||
libc = "0.2"
|
||||
serde = "1.0.118"
|
||||
serde_json = "1.0.60"
|
||||
serde_derive = "1.0.118"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||

|
||||
[](https://travis-ci.org/radareorg/r2pipe.rs)
|
||||
[](https://github.com/radareorg/r2pipe.rs/actions/workflows/main.yml)
|
||||
[](https://docs.rs/r2pipe)
|
||||
[](https://crates.io/crates/r2pipe)
|
||||
[](
|
||||
@@ -10,7 +9,7 @@ r2pipe.rs
|
||||
|
||||
|
||||
The Rust Crate to interact with radare2.
|
||||
Please check [Documentation](https://radare.github.io/r2pipe.rs) to get
|
||||
Please check [Documentation](https://docs.rs/r2pipe/latest/r2pipe/) to get
|
||||
started.
|
||||
|
||||
**NOTICE**
|
||||
|
||||
+70
-49
@@ -1,60 +1,81 @@
|
||||
use crate::{Error, Result};
|
||||
use libc::*;
|
||||
use crate::Result;
|
||||
use libloading::{Library, Symbol};
|
||||
use std::sync::Mutex;
|
||||
|
||||
// Contains a handle to the library.
|
||||
pub struct LibHandle(std::sync::Mutex<*mut c_void>);
|
||||
|
||||
pub fn to_cstr(s: &str) -> Result<*const c_char> {
|
||||
Ok(std::ffi::CString::new(s)
|
||||
.map_err(|_| Error::SharedLibraryLoadError)?
|
||||
.into_raw())
|
||||
}
|
||||
|
||||
pub fn free_cstr(ptr: *const c_char) {
|
||||
let _ = unsafe { std::ffi::CString::from_raw(ptr as *mut _) };
|
||||
}
|
||||
/// Contains a handle to the dynamically loaded library.
|
||||
pub struct LibHandle(Mutex<Library>);
|
||||
|
||||
impl LibHandle {
|
||||
#[cfg(windows)]
|
||||
fn load_library(path: &str) -> std::result::Result<Library, libloading::Error> {
|
||||
use libloading::os::windows::{
|
||||
Library as WindowsLibrary, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS,
|
||||
LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR,
|
||||
};
|
||||
|
||||
if path.contains('\\') || path.contains('/') {
|
||||
unsafe {
|
||||
WindowsLibrary::load_with_flags(
|
||||
path,
|
||||
LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS,
|
||||
)
|
||||
.map(Into::into)
|
||||
}
|
||||
} else {
|
||||
unsafe { Library::new(path) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn load_library(path: &str) -> std::result::Result<Library, libloading::Error> {
|
||||
unsafe { Library::new(path) }
|
||||
}
|
||||
|
||||
/// Load a shared library by name with platform-specific extension.
|
||||
pub fn new(name: &str, end: Option<&str>) -> Result<LibHandle> {
|
||||
let name = to_cstr(&format!(
|
||||
"{}{}{}",
|
||||
name,
|
||||
if cfg!(windows) {
|
||||
".dll"
|
||||
} else if cfg!(macos) {
|
||||
".dylib"
|
||||
} else {
|
||||
".so"
|
||||
},
|
||||
end.unwrap_or("")
|
||||
))?;
|
||||
let ret = unsafe { dlopen(name, RTLD_LAZY) };
|
||||
free_cstr(name);
|
||||
if ret.is_null() {
|
||||
Err(Error::SharedLibraryLoadError)
|
||||
let ext = if cfg!(windows) {
|
||||
".dll"
|
||||
} else if cfg!(target_os = "macos") {
|
||||
".dylib"
|
||||
} else {
|
||||
Ok(LibHandle(std::sync::Mutex::new(ret)))
|
||||
".so"
|
||||
};
|
||||
let lib_name = format!("{}{}{}", name, ext, end.unwrap_or(""));
|
||||
|
||||
// Prepare list of paths to try
|
||||
let mut lib_paths = vec![lib_name.clone()];
|
||||
|
||||
// On Windows, add common radare2 installation paths
|
||||
if cfg!(windows) {
|
||||
// CI installs to C:\radare2\<version>\bin, where version changes
|
||||
// So we need to search for the latest version directory
|
||||
lib_paths.push(format!("C:\\radare2\\bin\\{}", lib_name));
|
||||
// Also try the versioned path pattern from CI
|
||||
if let Ok(entries) = std::fs::read_dir("C:\\radare2") {
|
||||
for entry in entries.flatten() {
|
||||
if entry.file_type().is_ok_and(|ft| ft.is_dir()) {
|
||||
let version_path = format!("{}\\bin\\{}", entry.path().display(), lib_name);
|
||||
lib_paths.push(version_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try each path until we find one that works
|
||||
for lib_path in &lib_paths {
|
||||
if let Ok(lib) = Self::load_library(lib_path) {
|
||||
return Ok(LibHandle(Mutex::new(lib)));
|
||||
}
|
||||
}
|
||||
|
||||
// If none worked, return the error from the first attempt
|
||||
Err(Self::load_library(&lib_paths[0]).unwrap_err().into())
|
||||
}
|
||||
|
||||
/// Load a symbol from the library and transmute it to the desired type.
|
||||
pub unsafe fn load_sym<T>(&mut self, name: &str) -> Result<T> {
|
||||
let handle = *self.0.lock().unwrap();
|
||||
let name = to_cstr(name)?;
|
||||
let sym = dlsym(handle, name);
|
||||
free_cstr(name);
|
||||
if sym.is_null() {
|
||||
Err(Error::SharedLibraryLoadError)
|
||||
} else {
|
||||
Ok(std::mem::transmute_copy(&sym))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for LibHandle {
|
||||
fn drop(&mut self) {
|
||||
let handle = *self.0.lock().unwrap();
|
||||
// In some cases, dlclose is not required to do anything. We allow it
|
||||
// to silently fail.
|
||||
let _ = unsafe { libc::dlclose(handle) };
|
||||
let lib = self.0.lock().unwrap();
|
||||
let sym: Symbol<T> = lib.get(name.as_bytes())?;
|
||||
Ok(std::mem::transmute_copy::<_, T>(&*sym))
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -42,6 +42,6 @@ pub enum Error {
|
||||
ChannelSendError(#[from] SendError<String>),
|
||||
|
||||
/// Error loading radare2 shared library.
|
||||
#[error("Shared library load error")]
|
||||
SharedLibraryLoadError,
|
||||
#[error("Shared library error: {0}")]
|
||||
SharedLibraryError(#[from] libloading::Error),
|
||||
}
|
||||
+1
-1
@@ -50,7 +50,7 @@
|
||||
//! The crate offers various methods to interact with r2pipe, eg. via process (multi-threadable), http or tcp.
|
||||
//! Check the examples/ dir for more complete examples.
|
||||
|
||||
#![cfg_attr(doc_cfg, feature(doc_cfg))]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![doc(html_root_url = "https://radare.github.io/r2pipe.rs/")]
|
||||
|
||||
#[macro_use]
|
||||
|
||||
+30
-30
@@ -53,12 +53,23 @@ pub struct R2PipeThread {
|
||||
pub handle: thread::JoinHandle<Result<()>>,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
#[derive(Clone)]
|
||||
pub struct R2PipeSpawnOptions {
|
||||
pub exepath: String,
|
||||
pub args: Vec<&'static str>,
|
||||
}
|
||||
|
||||
impl Default for R2PipeSpawnOptions {
|
||||
fn default() -> Self {
|
||||
let exepath = if cfg!(windows) { "radare2.exe" } else { "r2" };
|
||||
|
||||
R2PipeSpawnOptions {
|
||||
exepath: exepath.to_string(),
|
||||
args: Vec::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides abstraction between the three invocation methods.
|
||||
pub struct R2Pipe(Box<dyn Pipe>);
|
||||
pub trait Pipe {
|
||||
@@ -177,26 +188,13 @@ impl R2Pipe {
|
||||
}
|
||||
|
||||
/// Creates a new R2PipeSpawn.
|
||||
pub fn spawn<T: AsRef<str>>(name: T, opts: Option<R2PipeSpawnOptions>) -> Result<R2Pipe> {
|
||||
pub fn spawn<T: AsRef<str>>(name: T, mut opts: Option<R2PipeSpawnOptions>) -> Result<R2Pipe> {
|
||||
if name.as_ref() == "" && R2Pipe::in_session().is_some() {
|
||||
return R2Pipe::open();
|
||||
}
|
||||
|
||||
let exepath = match opts {
|
||||
Some(ref opt) => opt.exepath.clone(),
|
||||
_ => {
|
||||
if cfg!(windows) {
|
||||
"radare2.exe"
|
||||
} else {
|
||||
"r2"
|
||||
}
|
||||
}
|
||||
.to_owned(),
|
||||
};
|
||||
let args = match opts {
|
||||
Some(ref opt) => opt.args.clone(),
|
||||
_ => vec![],
|
||||
};
|
||||
let R2PipeSpawnOptions { exepath, args } = opts.take().unwrap_or_default();
|
||||
|
||||
let path = Path::new(name.as_ref());
|
||||
let mut child = Command::new(exepath)
|
||||
.arg("-q0")
|
||||
@@ -381,29 +379,29 @@ pub struct R2PipeNative {
|
||||
|
||||
impl R2PipeNative {
|
||||
pub fn open(file: &str) -> Result<R2PipeNative> {
|
||||
let mut lib = dlfcn::LibHandle::new("libr_core", None)?;
|
||||
let lib_name = if cfg!(windows) { "r_core" } else { "libr_core" };
|
||||
let mut lib = dlfcn::LibHandle::new(lib_name, None)?;
|
||||
let r_core_new: fn() -> *mut libc::c_void = unsafe { lib.load_sym("r_core_new")? };
|
||||
let r_core_cmd_str_handle = unsafe { lib.load_sym("r_core_cmd_str")? };
|
||||
let r_core = r_core_new();
|
||||
if r_core.is_null() {
|
||||
Err(Error::SharedLibraryLoadError)
|
||||
} else {
|
||||
let mut ret = R2PipeNative {
|
||||
lib,
|
||||
r_core: std::sync::Mutex::new(r_core),
|
||||
r_core_cmd_str_handle,
|
||||
};
|
||||
ret.cmd(&format!("o {}", file))?;
|
||||
Ok(ret)
|
||||
return Err(Error::EmptyResponse);
|
||||
}
|
||||
let mut ret = R2PipeNative {
|
||||
lib,
|
||||
r_core: std::sync::Mutex::new(r_core),
|
||||
r_core_cmd_str_handle,
|
||||
};
|
||||
ret.cmd(&format!("o {}", file))?;
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
impl Pipe for R2PipeNative {
|
||||
fn cmd(&mut self, cmd: &str) -> Result<String> {
|
||||
let r_core = *self.r_core.lock().unwrap();
|
||||
let cmd = dlfcn::to_cstr(cmd)?;
|
||||
let res = (self.r_core_cmd_str_handle)(r_core, cmd);
|
||||
let cmd = std::ffi::CString::new(cmd).map_err(|_| Error::ArgumentMismatch)?;
|
||||
let res = (self.r_core_cmd_str_handle)(r_core, cmd.as_ptr());
|
||||
if res.is_null() {
|
||||
Err(Error::EmptyResponse)
|
||||
} else {
|
||||
@@ -427,9 +425,11 @@ impl Drop for R2PipeNative {
|
||||
mod test {
|
||||
use super::Pipe;
|
||||
use super::R2PipeNative;
|
||||
#[cfg(not(windows))]
|
||||
use crate::R2Pipe;
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn spawn_test() {
|
||||
let mut pipe = R2Pipe::spawn("/bin/ls", None).unwrap();
|
||||
assert_eq!(pipe.cmd("echo test").unwrap(), "test\n");
|
||||
@@ -437,7 +437,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn native_test() {
|
||||
let mut r2p = R2PipeNative::open("/bin/ls").unwrap();
|
||||
let mut r2p = R2PipeNative::open("malloc://32").unwrap();
|
||||
assert_eq!("a\n", r2p.cmd("echo a").unwrap());
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user