60 Commits
Author SHA1 Message Date
pancake c72b4cba60 fi 2026-02-20 13:49:51 +01:00
pancake 391641f2ff Use libload instead of dlopen for portability reasons 2026-02-20 13:40:45 +01:00
Jonathan Creekmore 1208b28183 Provide a proper Default impl for R2PipeSpawnOptions 2023-06-08 18:28:46 -04:00
RHL120 522a9f7a4f Fix docs link in README 2022-12-30 13:05:38 -05:00
meme 89db8759ce 0.7.0 2022-12-29 12:17:09 -05:00
RHL120 a66ddad123 Add support for radare as a shlib 2022-12-29 10:40:29 -05:00
RHL120 50c689f4bc Install radare2 in CI and add a simple test 2022-12-29 10:15:35 -05:00
RHL120 c0260db239 Fix an issue with Windows 2022-12-25 08:26:35 -05:00
RHL120 14079c97e9 Add call and callj methods 2022-12-17 12:09:35 -05:00
RHL120 43b68268ca Add default implementation for cmdj 2022-12-07 17:25:44 -05:00
RHL120 8cb8d248a8 Box pipe code 2022-12-07 17:25:01 -05:00
RHL120 e67a685a5b Clean up in_session code 2022-12-07 17:19:36 -05:00
RHL120 08335660d4 Clean up code in R2Pipe
* Remove some repeated code
* Add a default implementation for close
2022-11-14 13:55:19 -05:00
meme 32572abbb2 0.6.0 2021-12-04 19:57:07 -05:00
aemmitt-ns ee3fee9bb4 Merge pull request #42 from aemmitt-ns/reqwestless
replace single reqwest::get with tcpstream, remove dependency
2021-12-04 12:19:23 -05:00
aemmitt 4ee88ba031 use write_all, appease clippy 2021-12-04 11:44:17 -05:00
aemmitt 3796776e11 correct format 2021-12-04 11:39:38 -05:00
aemmitt a622e4926c replace single reqwest::get with tcpstream, remove dependency 2021-12-04 11:25:55 -05:00
Cyrill Leutwiler 9310015b18 Taking Pipe child process handle 2021-05-26 20:06:02 -04:00
Cyrill Leutwiler 8dae2fcb73 Wait for child on close (#40) 2021-05-22 08:51:13 -04:00
meme 15a83699ae Result and Error (#39)
* Result and Error

Instead of unwrapping or panicking haphazardly, instead use a unified
error type

* Update Error for reqwest

* Update examples to use Result
2021-05-21 10:03:02 -04:00
memeandmeme 3950fe4f0d Add clippy and fmt to CI (#38)
Co-authored-by: meme <[email protected]>
2021-05-16 12:06:11 -04:00
memeandmeme 591ac5c1b8 Move reqwest into an "http" feature (#37)
This is a very heavy dependency, and most may not need it. Instead it
should be a compile-time option.

Co-authored-by: meme <[email protected]>
2021-05-15 09:06:11 +02:00
Aswin C c2858b09d7 Bumb up some of the dependencies' versions (#36) 2021-01-01 16:21:49 +01:00
pancake eec57de9d6 Stick libc version 2020-06-13 18:19:36 +02:00
Cyrill Leutwiler 924eff860d Update README.md
Some more love for this crate
2020-06-13 01:20:53 +08:00
Anton Kochkov 08ab5a8f32 Add GitHub Actions 2020-06-12 13:15:36 +08:00
Anton Kochkov 5f1c34eb8d Better Travis CI configuration 2020-06-12 13:15:36 +08:00
Anton Kochkov 3b631b623e Fix formatting 2020-06-12 13:15:36 +08:00
Anton Kochkov 71f3fa40f0 Upgrade to Rust 2018 2020-06-12 13:15:36 +08:00
pancake b8a8c07ab6 Update dependencies #33 2020-06-11 20:57:01 +02:00
xermicus 76fa9f540b remove warnings in cargo test 2020-06-11 20:14:17 +02:00
xermicus a951524738 update deps and fix lints 2020-06-11 20:09:48 +02:00
Anton Kochkov 7c189bd4a7 Update reqwest dependency 2019-05-30 20:02:57 +08:00
Anton Kochkov f2fe7c74f9 Merge pull request #30 from xermicus/async
Add callback support in async api
2018-10-15 19:35:22 +08:00
0xrmc 6abb084735 add callback support in async api 2018-10-10 21:14:18 +02:00
0xrmc 43ad9657df add support for threads 2018-09-16 00:38:09 +02:00
kriw 61aa661f32 Update README.md 2018-08-10 18:40:23 +02:00
kriw 2494daf0ab Add example 2018-08-10 18:40:23 +02:00
kriw c1135945a6 Support Http interface 2018-08-10 18:40:23 +02:00
HMPerson1 07e22c8c01 Add appveyor.yml 2018-08-01 05:34:43 +01:00
HMPerson1 c75fd773da Fix build on windows 2018-08-01 05:34:43 +01:00
Michael Zhang c7476920c4 Update README.md 2018-07-28 09:09:05 +01:00
HMPerson1 5ddfda5a5d Add support TCP connections 2018-07-28 05:11:50 +01:00
HMPerson1 a2323b759f Remove clippy dependency
clippy now ships with rustup
2018-07-28 05:11:50 +01:00
Chinmay Deshpande 09622db2aa Removes higher level API functions from r2pipe.rs (#23)
* This aims to make the following changes:

* Remove all higher-level structs
* Remove certain functions used to interact with the r2 instance and
retrieve specific information

This functionality aims to be moved to r2pipe.rs-frontend repository.

* Removes outdated test, bumps version

* Removes init()
2017-07-15 22:15:43 +02:00
Joe Rozner 1972186180 Replace rustc_serialize with serde 2017-06-12 11:42:48 +02:00
Sushant 110dc301e2 Fixes local variable information
Closes #17
2016-12-30 19:12:47 -05:00
Chinmay Deshpande fdd1c5a329 Aims to fix the following issues: (#16)
* #15: which deals with usage of excess 'a' for r2 analysis call.
* If no function locals were found, a 'null' was written into the JSON
  which resulted in a parser error
* Unrequired usage of struct `Foo`
2016-12-28 10:04:41 +01:00
Sushant 23018f8320 Updates struct to reflect changes in r2 2016-07-14 21:35:07 +05:30
Sushant 3ee0bc88a6 Merge branch 'master' of github.com:radare/r2pipe.rs 2016-07-03 19:24:48 +05:30
Sushant c2aabb0daf Adds local vars information to functions 2016-07-03 19:24:33 +05:30
Sushant bed6e45e9c Uses specific version of dependencies 2016-06-29 14:05:01 +05:30
pancake 18d090c6ac Release 0.4.0 2016-06-29 10:24:21 +02:00
Sushant c5f5c0c614 Fixes FunctionInfo struct 2016-06-29 02:46:54 +05:30
Sushant 2bd48b5e64 Adds structs and helpers for strings 2016-06-20 13:52:25 +05:30
Sushant c972f48f8b Adds methods for fn_list and sections 2016-06-20 11:39:29 +05:30
Sushant d703d0448d Adds Function and SectionInfo 2016-06-20 09:26:32 +05:30
pancake b31c0ade0f Fix #11 - Add Travis badge in README 2016-06-08 10:35:23 +02:00
pancake 9ad7dbd790 Fix #4 - Trim all commands and results 2016-06-08 10:32:56 +02:00
20 changed files with 742 additions and 413 deletions

No files matched your search

+38
View File
@@ -0,0 +1,38 @@
name: R2pipe Rust CI
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build_and_test:
name: R2pipe.rs
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
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- run: rustup component add rustfmt clippy
- uses: actions-rs/cargo@v1
with:
command: build
args: --release --all-features
- uses: actions-rs/cargo@v1
with:
command: test
- uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
- uses: actions-rs/cargo@v1
with:
command: clippy
args: -- -D warnings
+3
View File
@@ -0,0 +1,3 @@
target
Cargo.lock
*.swp
+27 -21
View File
@@ -1,24 +1,30 @@
# travis.yml for testing r2pipe.rs. Initially written in 2016, by pancake.
os:
- linux
- osx
cache:
- ccache
- apt
language: rust
rust: stable
sudo: false
jobs:
include:
- rust: 1.41.0
env:
- ALL=' '
- rust: stable
env:
- CHECKFMT=1
- rust: beta
- rust: nightly
- rust: nightly
env:
- BENCH=1
branches:
only:
- master
before_script:
- |
if [ "${CHECKFMT}" == "1"]; then
rustup component add rustfmt --toolchain stable
fi
script:
- cargo build
- cargo test
- |
cargo build --verbose --no-default-features &&
cargo test --verbose --no-default-features &&
if [ "${CHECKFMT}" == "1"]; then
cargo +stable fmt -- --check
fi
notifications:
irc: "chat.freenode.net#radare"
on_success: never
on_failure: always
+9 -6
View File
@@ -1,11 +1,11 @@
[package]
name = "r2pipe"
version = "0.3.0"
version = "0.7.0"
authors = [
"pancake <[email protected]>",
"sushant94 <[email protected]>"
]
edition = "2018"
description = "Library to interact with radare2 using r2pipes."
# Repository
@@ -21,9 +21,12 @@ readme = "README.md"
license = "MIT OR Apache-2.0"
[features]
default = []
docsrs = []
[dependencies]
libc = "*"
rustc-serialize = "*"
clippy = {version = "0.0.61", optional = true}
libloading = "0.8"
libc = "0.2"
serde = "1.0.118"
serde_json = "1.0.60"
serde_derive = "1.0.118"
thiserror = "1"
+14 -4
View File
@@ -1,17 +1,27 @@
![R2pipe Rust CI](https://github.com/radareorg/r2pipe.rs/workflows/R2pipe%20Rust%20CI/badge.svg)
[![Travis](https://travis-ci.org/radareorg/r2pipe.rs.svg?branch=master)](https://travis-ci.org/radareorg/r2pipe.rs)
[![Documentation](https://docs.rs/r2pipe/badge.svg)](https://docs.rs/r2pipe)
[![Cargo](https://img.shields.io/crates/v/r2pipe.svg)](https://crates.io/crates/r2pipe)
[![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](
https://github.com/radareorg/r2pipe.rs)
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**
Structures and API functions have been moved to [radare2-r2pipe-api](https://github.com/radare/radare2-r2pipe-api)
repository. Please make sure you update your dependencies and imports.
TODO
----
* Support async API
* Support HTTP connectivity
* Support futures API
* Add custom r2 start flags for spawn method
* Add plain TCP api
* Better error handling
## License
+16
View File
@@ -0,0 +1,16 @@
clone_depth: 1
environment:
matrix:
- TARGET: i686-pc-windows-msvc
- TARGET: x86_64-pc-windows-msvc
install:
- curl -sSf -o rustup-init.exe https://win.rustup.rs/
- rustup-init.exe -y --default-host %TARGET% --default-toolchain nightly
- set PATH=%PATH%;%USERPROFILE%\.cargo\bin
- rustc -Vv
- cargo -Vv
build_script:
- cargo build -vv
test_script:
- cargo test -vv
deploy: off
+21 -18
View File
@@ -1,11 +1,10 @@
extern crate r2pipe;
use r2pipe::R2Pipe;
use std::thread;
use std::sync::Arc;
use std::sync::mpsc::channel;
use std::sync::mpsc::Sender;
use std::sync::mpsc::Receiver;
use std::sync::mpsc::Sender;
use std::sync::Arc;
use std::thread;
const FILENAME: &'static str = "/bin/ls";
@@ -14,7 +13,7 @@ pub struct R2PipeAsync {
rx: Receiver<String>,
tx2: Sender<String>,
rx2: Receiver<String>,
cbs: Vec<Arc<Fn(String)>>,
cbs: Vec<Arc<dyn Fn(String)>>,
}
impl R2PipeAsync {
@@ -30,7 +29,7 @@ impl R2PipeAsync {
}
}
pub fn cmd(&mut self, str: &'static str, cb: Arc<Fn(String)>) {
pub fn cmd(&mut self, str: &'static str, cb: Arc<dyn Fn(String)>) {
self.cbs.insert(0, cb);
self.tx.send(str.to_string()).unwrap();
}
@@ -44,10 +43,10 @@ impl R2PipeAsync {
let child_tx = self.tx2.clone();
let child = thread::spawn(move || {
let mut r2p = match R2Pipe::in_session() {
Some(_) => R2Pipe::open(),
None => R2Pipe::spawn(FILENAME.to_owned(), None),
}
.unwrap();
Some(_) => R2Pipe::open(),
None => R2Pipe::spawn(FILENAME.to_owned(), None),
}
.unwrap();
loop {
let msg = child_rx.recv().unwrap();
if msg == "q" {
@@ -82,14 +81,18 @@ impl R2PipeAsync {
fn main() {
let mut r2pa = R2PipeAsync::open();
r2pa.cmd("?e One",
Arc::new(|x| {
println!("One: {}", x);
}));
r2pa.cmd("?e Two",
Arc::new(|x| {
println!("Two: {}", x);
}));
r2pa.cmd(
"?e One",
Arc::new(|x| {
println!("One: {}", x);
}),
);
r2pa.cmd(
"?e Two",
Arc::new(|x| {
println!("Two: {}", x);
}),
);
r2pa.quit();
r2pa.mainloop();
}
+21
View File
@@ -0,0 +1,21 @@
use r2pipe::Result;
fn main() -> Result<()> {
#[cfg(feature = "http")]
{
use r2pipe::R2Pipe;
use serde_json;
let mut r2p = R2Pipe::http("http://localhost:9080")?;
let json = r2p.cmdj("ij")?;
println!("{}", serde_json::to_string_pretty(&json)?);
println!("ARCH {}", json["bin"]["arch"]);
println!("BITS {}", json["bin"]["bits"]);
println!("Disasm:\n{}", r2p.cmd("pd 20")?);
println!("Hexdump:\n{}", r2p.cmd("px 64")?);
r2p.close();
}
Ok(())
}
+31 -23
View File
@@ -1,30 +1,38 @@
#[macro_use]
extern crate r2pipe;
use r2pipe::R2Pipe;
use serde_json;
use r2pipe::R2PipeSpawnOptions;
use r2pipe::{R2Pipe, Result};
fn main() {
// let mut r2p = open_pipe!().unwrap();
let opts = R2PipeSpawnOptions { exepath: "radare2".to_owned(), ..Default::default() };
fn test_trim() -> Result<()> {
let mut ns = R2Pipe::spawn("/bin/ls".to_owned(), None)?;
println!("(({}))", ns.cmd("\n\n?e hello world\n\n")?);
println!("(({}))", ns.cmd("\n\n?e hello world\n\n")?);
println!("(({}))", ns.cmd("\n\n?e hello world\n\n")?);
ns.close();
Ok(())
}
fn main() -> Result<()> {
test_trim()?;
let opts = R2PipeSpawnOptions {
exepath: "radare2".to_owned(),
..Default::default()
};
let mut r2p = match R2Pipe::in_session() {
Some(_) => R2Pipe::open(),
None => R2Pipe::spawn("/bin/ls".to_owned(), Some(opts)),
}
.unwrap();
Some(_) => R2Pipe::open()?,
None => R2Pipe::spawn("/bin/ls".to_owned(), Some(opts))?,
};
println!("{}", r2p.cmd("?e Hello World").unwrap());
println!("{}", r2p.cmd("?e Hello World")?);
let json = r2p.cmdj("ij").unwrap();
println!("{}", json.pretty());
println!("ARCH {}", json.find_path(&["bin", "arch"]).unwrap());
let json = r2p.cmdj("ij")?;
println!("{}", serde_json::to_string_pretty(&json)?);
println!("ARCH {}", json["bin"]["arch"]);
println!("BITS {}", json["bin"]["bits"]);
println!("Disasm:\n{}", r2p.cmd("pd 20")?);
println!("Hexdump:\n{}", r2p.cmd("px 64")?);
r2p.close();
// println!("BITS 0x{:x}",json.find_path(&["bin","bits"]).unwrap().as_u64().unwrap());
if let Some(bits) = json.find_path(&["bin", "bits"]) {
if let Some(n_bits) = bits.as_u64() {
println!("BITS 0x{:x}", n_bits);
}
}
println!("Disasm:\n{}", r2p.cmd("pd 20").unwrap());
println!("Hexdump:\n{}", r2p.cmd("px 64").unwrap());
r2p.close();
Ok(())
}
+5 -6
View File
@@ -1,10 +1,8 @@
#[macro_use]
extern crate r2pipe;
use r2pipe::R2Pipe;
use r2pipe::{R2Pipe, Result};
fn main() {
let mut r2p = R2Pipe::spawn("/bin/ls".to_owned(), None).unwrap();
println!("{}", r2p.cmd("?e Hello").unwrap());
fn main() -> Result<()> {
let mut r2p = R2Pipe::spawn("/bin/ls".to_owned(), None)?;
println!("{}", r2p.cmd("?e Hello")?);
if let Err(_) = r2p.cmd("q") {
// !killall r2") {
println!("Quit happens!");
@@ -18,4 +16,5 @@ fn main() {
}
}
println!("Byebye");
Ok(())
}
+19
View File
@@ -0,0 +1,19 @@
use serde_json;
use r2pipe::{R2Pipe, Result};
fn main() -> Result<()> {
let mut r2p = R2Pipe::tcp("localhost:9080")?;
println!("{}", r2p.cmd("?e Hello World")?);
let json = r2p.cmdj("ij")?;
println!("{}", serde_json::to_string_pretty(&json)?);
println!("ARCH {}", json["bin"]["arch"]);
println!("BITS {}", json["bin"]["bits"]);
println!("Disasm:\n{}", r2p.cmd("pd 20")?);
println!("Hexdump:\n{}", r2p.cmd("px 64")?);
r2p.close();
Ok(())
}
+42
View File
@@ -0,0 +1,42 @@
use r2pipe::{R2Pipe, Result};
fn main() -> Result<()> {
// Lets spawn some r2pipes to open some binaries
// First two arguments for R2Pipe::threads() are the same as for R2Pipe::spawn() but inside vectors
// Third and last argument is an option of a callback function
let pipes = match R2Pipe::threads(
vec!["/bin/ls", "/bin/id", "/bin/cat"],
vec![None, None, None],
None,
) {
Ok(p) => p,
Err(e) => {
println!("Error spawning Pipes: {}", e);
return Ok(());
}
};
// At this point we can iter through all of our r2pipes and send some commands
for p in pipes.iter() {
if let Ok(_) = p.send("ij".to_string()) {};
}
// do_other_stuff_here();
// Lets iter again and see what the pipes got
for p in pipes.iter() {
// this will block, do "p.recv(false)" for non-blocking receive inside a loop
if let Ok(msg) = p.recv(true) {
println!("Pipe #{} says: {}", p.id, msg);
}
}
// Finally properly close all pipes
// Note: For "join()" we need to borrow so pipes.iter() won't work for this
for p in pipes {
if let Ok(_) = p.send("q".to_string()) {};
p.handle.join().unwrap()?;
}
Ok(())
}
+42
View File
@@ -0,0 +1,42 @@
use r2pipe::{R2Pipe, Result};
use std::sync::Arc;
fn main() -> Result<()> {
// First we define a callback. It doesn't block and gets called after a thread receives output from r2pipe
// Note: First argument to the callback is the thread id, second one the r2pipe output
let callback = Arc::new(|id, result| {
println!("Pipe #{} says: {}", id, result);
});
// First two arguments for R2Pipe::threads() are the same as for R2Pipe::spawn() but inside vectors
// Third and last argument is an option to a callback function
// The callback function takes two Arguments: Thread ID and r2pipe output
let pipes = match R2Pipe::threads(
vec!["/bin/ls", "/bin/id", "/bin/cat"],
vec![None, None, None],
Some(callback),
) {
Ok(p) => p,
Err(e) => {
println!("Error spawning Pipes: {}", e);
return Ok(());
}
};
// At this point we can iter through all of our r2pipes and send some commands
for p in pipes.iter() {
if let Ok(_) = p.send("ij".to_string()) {};
}
// Meanwhile: Expecting callbacks
std::thread::sleep(std::time::Duration::from_millis(1000));
// Finally properly close all pipes
// Note: For "join()" we need to borrow so pipes.iter() won't work for this
for p in pipes {
if let Ok(_) = p.send("q".to_string()) {};
p.handle.join().unwrap()?;
}
Ok(())
}
-4
View File
@@ -1,4 +0,0 @@
format_strings = true
reorder_imports = true
take_source_hints = false
struct_lit_multiline_style = "ForceMulti"
+29
View File
@@ -0,0 +1,29 @@
use crate::Result;
use libloading::{Library, Symbol};
use std::sync::Mutex;
/// Contains a handle to the dynamically loaded library.
pub struct LibHandle(Mutex<Library>);
impl LibHandle {
/// Load a shared library by name with platform-specific extension.
pub fn new(name: &str, end: Option<&str>) -> Result<LibHandle> {
let ext = if cfg!(windows) {
".dll"
} else if cfg!(target_os = "macos") {
".dylib"
} else {
".so"
};
let lib_name = format!("{}{}{}", name, ext, end.unwrap_or(""));
let lib = unsafe { Library::new(&lib_name) }?;
Ok(LibHandle(Mutex::new(lib)))
}
/// 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 lib = self.0.lock().unwrap();
let sym: Symbol<T> = lib.get(name.as_bytes())?;
Ok(std::mem::transmute_copy::<_, T>(&*sym))
}
}
+47
View File
@@ -0,0 +1,47 @@
use std::sync::mpsc::{RecvError, SendError, TryRecvError};
use std::{io, str};
use thiserror::Error;
/// Custom `Error` for r2pipe.rs.
#[derive(Error, Debug)]
pub enum Error {
/// An I/O error occurred.
#[error("I/O error")]
Io(#[from] io::Error),
/// No open radare2 session, perhaps path was not specified.
#[error("No open session")]
NoSession,
/// Response had invalid/missing JSON.
#[error("Empty response from JSON")]
EmptyResponse,
/// Incorrect number of arguments, or incorrect format.
#[error("Argument mismatch")]
ArgumentMismatch,
/// An error occurred inside of serde.
#[error("Serde deserialization error")]
SerdeError(#[from] serde_json::Error),
/// Error during UTF-8 decoding.
#[error("UTF-8 decoding error")]
Utf8(#[from] str::Utf8Error),
/// Error receiving data from channel.
#[error("Receive channel data error")]
ChannelReceiveError(#[from] RecvError),
/// Error trying to receive data from channel.
#[error("Trying receive channel data error")]
ChannelTryReceiveError(#[from] TryRecvError),
/// Error sending data through channel.
#[error("Send channel data error")]
ChannelSendError(#[from] SendError<String>),
/// Error loading radare2 shared library.
#[error("Shared library error: {0}")]
SharedLibraryError(#[from] libloading::Error),
}
+19 -13
View File
@@ -1,4 +1,6 @@
//! `R2Pipe` provides functions to interact with [radare2](http://rada.re/r/)
//! `R2Pipe` provides functions to interact with [radare2](http://rada.re/r/).
//! This aims to be a raw API. For more higher-level functions and structs to abstract
//! over the generated output, see [r2pipe.rs-frontend]().
//!
//! Hence this requires you to have radare2 installed on you system. For more
//! information refer to the r2 [repository](https://github.com/radare/radare2).
@@ -31,34 +33,38 @@
//! ```no_run
//! #[macro_use]
//! extern crate r2pipe;
//! extern crate serde_json;
//! use r2pipe::R2Pipe;
//! fn main() {
//! let path = Some("/bin/ls".to_owned());
//! let mut r2p = open_pipe!(path).unwrap();
//! println!("{}", r2p.cmd("?e Hello World").unwrap());
//! if let Ok(json) = r2p.cmdj("ij") {
//! println!("{}", json.pretty());
//! println!("ARCH {}", json.find_path(&["bin","arch"]).unwrap());
//! println!("{}", serde_json::to_string_pretty(&json).unwrap());
//! println!("ARCH {}", json["bin"]["arch"]);
//! }
//! r2p.close();
//! }
//! ```
//!
//! 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(docsrs, feature(doc_cfg))]
#![doc(html_root_url = "https://radare.github.io/r2pipe.rs/")]
// Support for extra lints for clippy
#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]
extern crate libc;
extern crate rustc_serialize;
#[macro_use]
pub mod r2pipe;
mod dlfcn;
pub mod r2;
pub mod structs;
mod error;
pub use error::*;
#[doc(hidden)]
pub type Result<T> = std::result::Result<T, error::Error>;
// Rexport to bring it out one module.
pub use self::r2pipe::R2PipeSpawnOptions;
pub use self::r2pipe::R2Pipe;
pub use self::r2::R2;
pub use self::r2pipe::R2Pipe;
pub use self::r2pipe::R2PipeSpawnOptions;
+25 -97
View File
@@ -5,26 +5,14 @@
// This file may not be copied, modified, or distributed
// except according to those terms.
//! `R2` struct to make interaction with r2 easier
//! Few functions for initialization, communication and termination of r2.
//!
//! This module is for convenience purposes. It provides a nicer way to
//! interact with r2 by
//! parsing the JSON into structs. Note that all commands are not supported and
//! this
//! module is updated only on a need basis. r2 commands that are not supported
//! by this module can
//! still be called by using the send() and recv() that `R2` provides. If this
//! is a command that
//! you see yourself using frequently and feel it is important to have nice
//! rust wrappers
//! feel free to raise an issue, or better yet a pull request implementing the
//! same.
//! If you wish to write wrappers for certain r2 functionalities,
//! contribute to the r2pipe.rs-frontend project. This aims to be a
//! barebones implementation of the pipe concept.
use r2pipe::R2Pipe;
use rustc_serialize::json::{DecodeResult, Json};
use rustc_serialize::json;
use super::structs::*;
use crate::{r2pipe::R2Pipe, Error, Result};
use serde_json::Value;
pub struct R2 {
pipe: R2Pipe,
@@ -33,7 +21,7 @@ pub struct R2 {
impl Default for R2 {
fn default() -> R2 {
R2::new(None).expect("Unable to spawn r2 or find an open r2pipe")
R2::new::<&str>(None).expect("Unable to spawn r2 or find an open r2pipe")
}
}
@@ -41,26 +29,21 @@ impl Default for R2 {
// i.e. The ones that are not currently abstracted by the R2 API.
// Ideally, all commonly used commands must be supported for easier use.
impl R2 {
// TODO: Use an error type
pub fn new(path: Option<String>) -> Result<R2, String> {
pub fn new<T: AsRef<str>>(path: Option<T>) -> Result<R2> {
if path.is_none() && !R2::in_session() {
let e = "No r2 session open. Please specify path!".to_owned();
return Err(e);
return Err(Error::NoSession);
}
// This means that path is `Some` or we have an open session.
let pipe = open_pipe!(path).unwrap();
let pipe = open_pipe!(path.as_ref())?;
Ok(R2 {
pipe: pipe,
pipe,
readin: String::new(),
})
}
pub fn in_session() -> bool {
match R2Pipe::in_session() {
Some(_) => true,
None => false,
}
R2Pipe::in_session().is_some()
}
pub fn from(r2p: R2Pipe) -> R2 {
@@ -70,20 +53,14 @@ impl R2 {
}
}
// Does some basic configurations (sane defaults).
pub fn init(&mut self) {
self.send("e asm.esil = true");
self.send("e scr.color = false");
self.send("aaa");
self.flush();
pub fn close(&mut self) -> Result<()> {
self.send("q!")?;
Ok(())
}
pub fn close(&mut self) {
self.send("q!");
}
pub fn send(&mut self, cmd: &str) {
self.readin = self.pipe.cmd(cmd).unwrap();
pub fn send(&mut self, cmd: &str) -> Result<()> {
self.readin = self.pipe.cmd(cmd)?;
Ok(())
}
pub fn recv(&mut self) -> String {
@@ -92,65 +69,16 @@ impl R2 {
res
}
pub fn recv_json(&mut self) -> Json {
let res = self.recv().replace("\n", "");
Json::from_str(&*res).unwrap()
pub fn recv_json(&mut self) -> Result<Value> {
let mut res = self.recv().replace('\n', "");
if res.is_empty() {
res = "{}".to_owned();
}
Ok(serde_json::from_str(&res)?)
}
pub fn flush(&mut self) {
self.readin = String::from("");
}
pub fn analyze(&mut self) {
self.send("aa");
self.flush();
}
pub fn get_function(&mut self, func: &str) -> DecodeResult<LFunctionInfo> {
let cmd = format!("pdfj @ {}", func);
self.send(&*cmd);
let raw_json = self.recv();
// Handle Error here.
json::decode(&*raw_json)
}
// get 'n' (or 16) instructions at 'offset' (or current position if offset in
// `None`)
pub fn get_insts(&mut self,
n: Option<u64>,
offset: Option<&str>)
-> DecodeResult<Vec<LOpInfo>> {
let n = n.unwrap_or(16);
let offset: &str = offset.unwrap_or_default();
let mut cmd = format!("pdj{}", n);
if !offset.is_empty() {
cmd = format!("{} @ {}", cmd, offset);
}
self.send(&*cmd);
let raw_json = self.recv();
json::decode(&*raw_json)
}
pub fn get_reg_info(&mut self) -> DecodeResult<LRegInfo> {
self.send("drpj");
let raw_json = self.recv();
json::decode(&*raw_json)
}
pub fn get_flag_info(&mut self) -> DecodeResult<Vec<LFlagInfo>> {
self.send("fj");
let raw_json = self.recv();
json::decode(&*raw_json)
}
pub fn get_bin_info(&mut self) -> DecodeResult<LBinInfo> {
self.send("ij");
let raw_json = self.recv();
json::decode(&*raw_json)
}
pub fn get_fn_list(&mut self) -> Json {
self.send("aflj");
self.recv_json()
}
}
+334 -135
View File
@@ -2,19 +2,24 @@
//!
//! Please check crate level documentation for more details and example.
use std::os::unix::io::FromRawFd;
use rustc_serialize::json::Json;
use crate::dlfcn;
use crate::{Error, Result};
use libc;
use std::process::Command;
use std::process::Stdio;
use std::process;
use std::env;
use std::str;
use std::path::Path;
use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;
use std::net::{SocketAddr, TcpStream, ToSocketAddrs};
use std::path::Path;
use std::process;
use std::process::Command;
use std::process::Stdio;
use std::str;
use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
use serde_json::Value;
/// File descriptors to the parent r2 process.
pub struct R2PipeLang {
@@ -26,74 +31,112 @@ pub struct R2PipeLang {
pub struct R2PipeSpawn {
read: BufReader<process::ChildStdout>,
write: process::ChildStdin,
child: Option<process::Child>,
}
#[derive(Default)]
/// Stores the socket address of the r2 process.
pub struct R2PipeTcp {
socket_addr: SocketAddr,
}
pub struct R2PipeHttp {
host: String,
}
/// Stores thread metadata
/// It stores both a sending and receiving end to the thread, allowing convenient interaction
/// So we can send commands using R2PipeThread::send() and fetch outputs using R2PipeThread::recv()
pub struct R2PipeThread {
r2recv: mpsc::Receiver<String>,
r2send: mpsc::Sender<String>,
pub id: u16,
pub handle: thread::JoinHandle<Result<()>>,
}
#[derive(Clone)]
pub struct R2PipeSpawnOptions {
pub exepath: String,
pub args: Vec<&'static str>,
}
/// Provides abstraction between the two invocation methods.
pub enum R2Pipe {
Pipe(R2PipeSpawn),
Lang(R2PipeLang),
}
impl Default for R2PipeSpawnOptions {
fn default() -> Self {
let exepath = if cfg!(windows) { "radare2.exe" } else { "r2" };
fn atoi(k: &str) -> i32 {
match k.parse::<i32>() {
Ok(val) => val,
Err(_) => -1,
}
}
fn getenv(k: &str) -> i32 {
match env::var(k) {
Ok(val) => atoi(&val),
Err(_) => -1,
}
}
fn process_result(res: Vec<u8>) -> Result<String, String> {
let len = res.len();
let out = if len > 0 {
let res_without_zero = &res[..len - 1];
if let Ok(utf8str) = str::from_utf8(res_without_zero) {
String::from(utf8str)
} else {
return Err("Failed".to_owned());
R2PipeSpawnOptions {
exepath: exepath.to_string(),
args: Vec::default(),
}
}
}
/// Provides abstraction between the three invocation methods.
pub struct R2Pipe(Box<dyn Pipe>);
pub trait Pipe {
fn cmd(&mut self, cmd: &str) -> Result<String>;
fn cmdj(&mut self, cmd: &str) -> Result<Value> {
let result = self.cmd(cmd)?;
if result.is_empty() {
return Err(Error::EmptyResponse);
}
Ok(serde_json::from_str(&result)?)
}
/// Escape the command before executing, valid only as of r2 v.5.8.0 "icebucket"
fn call(&mut self, cmd: &str) -> Result<String> {
self.cmd(&format!("\"\"{}", cmd))
}
/// Escape the command before executing and convert it to a json value,
/// valid only as of r2 v.5.8.0 "icebucket"
fn callj(&mut self, cmd: &str) -> Result<Value> {
self.cmdj(&format!("\"\"{}", cmd))
}
fn close(&mut self) {}
}
fn getenv(k: &str) -> Option<i32> {
match env::var(k) {
Ok(val) => val.parse::<i32>().ok(),
Err(_) => None,
}
}
fn process_result(res: Vec<u8>) -> Result<String> {
let len = res.len();
if len == 0 {
Err(Error::EmptyResponse)
} else {
"".to_owned()
};
Ok(out)
Ok(str::from_utf8(&res[..len - 1])?.to_string())
}
}
#[macro_export]
macro_rules! open_pipe {
() => {
R2Pipe::open(),
() => {
R2Pipe::open(),
};
($x: expr) => {
match $x {
Some(path) => R2Pipe::load_native(&path.clone()).or_else(|_| R2Pipe::spawn(path, None)),
None => R2Pipe::open(),
}
};
($x: expr) => {
match $x {
Some(path) => R2Pipe::spawn(path, None),
None => R2Pipe::open(),
}
};
($x: expr, $y: expr) => {
match $x $y {
Some(path, opts) => R2Pipe::spawn(path, opts),
(None, None) => R2Pipe::open(),
}
}
($x: expr, $y: expr) => {
match $x $y {
Some(path, opts) => R2Pipe::spawn(path, opts),
(None, None) => R2Pipe::open(),
}
}
}
impl R2Pipe {
pub fn open() -> Result<R2Pipe, &'static str> {
let (f_in, f_out) = match R2Pipe::in_session() {
Some(x) => x,
None => return Err("Pipe not open. Please run from r2"),
};
pub fn load_native<T: AsRef<str>>(path: T) -> Result<R2Pipe> {
Ok(R2Pipe(Box::new(R2PipeNative::open(path.as_ref())?)))
}
#[cfg(not(windows))]
pub fn open() -> Result<R2Pipe> {
use std::os::unix::io::FromRawFd;
let (f_in, f_out) = R2Pipe::in_session().ok_or(Error::NoSession)?;
let res = unsafe {
// dup file descriptors to avoid from_raw_fd ownership issue
let (d_in, d_out) = (libc::dup(f_in), libc::dup(f_out));
@@ -102,36 +145,37 @@ impl R2Pipe {
write: File::from_raw_fd(d_out),
}
};
Ok(R2Pipe::Lang(res))
Ok(R2Pipe(Box::new(res)))
}
pub fn cmd(&mut self, cmd: &str) -> Result<String, String> {
match *self {
R2Pipe::Pipe(ref mut x) => x.cmd(cmd),
R2Pipe::Lang(ref mut x) => x.cmd(cmd),
}
#[cfg(windows)]
pub fn open() -> Result<R2Pipe> {
unimplemented!()
}
pub fn cmd(&mut self, cmd: &str) -> Result<String> {
self.0.cmd(cmd.trim())
}
pub fn cmdj(&mut self, cmd: &str) -> Result<Json, String> {
match *self {
R2Pipe::Pipe(ref mut x) => x.cmdj(cmd),
R2Pipe::Lang(ref mut x) => x.cmdj(cmd),
}
pub fn cmdj(&mut self, cmd: &str) -> Result<Value> {
self.0.cmdj(cmd.trim())
}
pub fn close(&mut self) {
match *self {
R2Pipe::Pipe(ref mut x) => x.close(),
R2Pipe::Lang(ref mut x) => x.close(),
}
self.0.close();
}
/// Escape the command before executing, valid only as of r2 v.5.8.0 "icebucket"
pub fn call(&mut self, cmd: &str) -> Result<String> {
self.0.call(cmd)
}
/// Escape the command before executing and convert it to a json value,
/// valid only as of r2 v.5.8.0 "icebucket"
pub fn callj(&mut self, cmd: &str) -> Result<Value> {
self.0.callj(cmd)
}
pub fn in_session() -> Option<(i32, i32)> {
let f_in = getenv("R2PIPE_IN");
let f_out = getenv("R2PIPE_OUT");
if f_in < 0 || f_out < 0 {
return None;
}
let f_in = getenv("R2PIPE_IN")?;
let f_out = getenv("R2PIPE_OUT")?;
Some((f_in, f_out))
}
@@ -144,98 +188,253 @@ impl R2Pipe {
}
/// Creates a new R2PipeSpawn.
pub fn spawn(name: String, opts: Option<R2PipeSpawnOptions>) -> Result<R2Pipe, &'static str> {
if name == "" {
if let Some(_) = R2Pipe::in_session() {
return R2Pipe::open();
}
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(),
_ => "r2".to_owned(),
};
let args = match opts {
Some(ref opt) => opt.args.clone(),
_ => vec![],
};
let path = Path::new(&*name);
let child = match Command::new(exepath)
let R2PipeSpawnOptions { exepath, args } = opts.take().unwrap_or_default();
let path = Path::new(name.as_ref());
let mut child = Command::new(exepath)
.arg("-q0")
.args(&args)
.arg(path)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn() {
Ok(c) => c,
Err(_) => return Err("Unable to spawn r2."),
};
.spawn()?;
let sin = child.stdin.unwrap();
let mut sout = child.stdout.unwrap();
// If stdin/stdout is not available, hard error
let sin = child.stdin.take().unwrap();
let mut sout = child.stdout.take().unwrap();
// flush out the initial null byte.
let mut w = [0; 1];
sout.read(&mut w).unwrap();
sout.read_exact(&mut w)?;
let res = R2PipeSpawn {
read: BufReader::new(sout),
write: sin,
child: Some(child),
};
Ok(R2Pipe::Pipe(res))
Ok(R2Pipe(Box::new(res)))
}
/// Creates a new R2PipeTcp
pub fn tcp<A: ToSocketAddrs>(addr: A) -> Result<R2Pipe> {
// use `connect` to figure out which socket address works
let stream = TcpStream::connect(addr)?;
let addr = stream.peer_addr()?;
Ok(R2Pipe(Box::new(R2PipeTcp { socket_addr: addr })))
}
/// Creates a new R2PipeHttp
pub fn http(host: &str) -> R2Pipe {
R2Pipe(Box::new(R2PipeHttp {
host: host.to_string(),
}))
}
/// Creates new pipe threads
/// First two arguments for R2Pipe::threads() are the same as for R2Pipe::spawn() but inside vectors
/// Third and last argument is an option to a callback function
/// The callback function takes two Arguments: Thread ID and r2pipe output
pub fn threads(
names: Vec<&'static str>,
opts: Vec<Option<R2PipeSpawnOptions>>,
callback: Option<Arc<dyn Fn(u16, String) + Sync + Send>>,
) -> Result<Vec<R2PipeThread>> {
if names.len() != opts.len() {
return Err(Error::ArgumentMismatch);
}
let mut pipes = Vec::new();
for n in 0..names.len() {
let (htx, rx) = mpsc::channel();
let (tx, hrx) = mpsc::channel();
let name = names[n];
let opt = opts[n].clone();
let cb = callback.clone();
let t = thread::spawn(move || -> Result<()> {
let mut r2 = R2Pipe::spawn(name, opt)?;
loop {
let cmd: String = hrx.recv()?;
if cmd == "q" {
break;
}
let res = r2.cmdj(&cmd)?.to_string();
htx.send(res.clone())?;
if let Some(cbs) = cb.clone() {
thread::spawn(move || {
cbs(n as u16, res);
});
};
}
Ok(())
});
pipes.push(R2PipeThread {
r2recv: rx,
r2send: tx,
id: n as u16,
handle: t,
});
}
Ok(pipes)
}
}
impl R2PipeThread {
pub fn send(&self, cmd: String) -> Result<()> {
Ok(self.r2send.send(cmd)?)
}
pub fn recv(&self, block: bool) -> Result<String> {
if block {
Ok(self.r2recv.recv()?)
} else {
Ok(self.r2recv.try_recv()?)
}
}
}
impl Pipe for R2PipeSpawn {
fn cmd(&mut self, cmd: &str) -> Result<String> {
let cmd = cmd.to_owned() + "\n";
self.write.write_all(cmd.as_bytes())?;
let mut res: Vec<u8> = Vec::new();
self.read.read_until(0u8, &mut res)?;
process_result(res)
}
fn close(&mut self) {
let _ = self.cmd("q!");
if let Some(child) = &mut self.child {
let _ = child.wait();
}
}
}
impl R2PipeSpawn {
pub fn cmd(&mut self, cmd: &str) -> Result<String, String> {
let cmd_ = cmd.to_owned() + "\n";
if let Err(e) = self.write.write(cmd_.as_bytes()) {
return Err(e.to_string());
}
/// Attempts to take the pipes underlying child process handle.
/// On success the handle is returned.
/// If `None` is returned the child handle was already taken previously.
/// By using this method you take over the responsibility to `wait()` the child process in order to free all of it's resources.
pub fn take_child(&mut self) -> Option<process::Child> {
self.child.take()
}
}
impl Pipe for R2PipeLang {
fn cmd(&mut self, cmd: &str) -> Result<String> {
self.write.write_all(cmd.as_bytes())?;
let mut res: Vec<u8> = Vec::new();
if let Err(e) = self.read.read_until(0u8, &mut res) {
return Err(e.to_string());
}
self.read.read_until(0u8, &mut res)?;
process_result(res)
}
}
pub fn cmdj(&mut self, cmd: &str) -> Result<Json, String> {
if let Ok(res) = self.cmd(cmd) {
if res == "" {
return Err("Empty JSON".to_string());
}
if let Ok(jstr) = Json::from_str(&res) {
Ok(jstr)
} else {
Err(format!("Invalid JSON for {} ({})", cmd, res))
}
impl Pipe for R2PipeHttp {
fn cmd(&mut self, cmd: &str) -> Result<String> {
let host = if self.host.starts_with("http://") {
&self.host[7..]
} else {
Err("oops cmd".to_string())
}
}
&self.host
};
let mut stream = TcpStream::connect(host)?;
let req = format!("GET /cmd/{} HTTP/1.1\r\n", cmd);
let mut resp = Vec::with_capacity(1024);
stream.write_all(req.as_bytes())?;
stream.read_to_end(&mut resp)?;
pub fn close(&mut self) {
let _ = self.cmd("q!");
// index of the start of response body
let index = resp
.windows(4)
.position(|w| w == "\r\n\r\n".as_bytes())
.map(|i| i + 4)
.unwrap_or(0);
Ok(str::from_utf8(&resp[index..]).map(|s| s.to_string())?)
}
}
impl R2PipeLang {
pub fn cmd(&mut self, cmd: &str) -> Result<String, String> {
self.write.write(cmd.as_bytes()).unwrap();
impl Pipe for R2PipeTcp {
fn cmd(&mut self, cmd: &str) -> Result<String> {
let mut stream = TcpStream::connect(self.socket_addr)?;
stream.write_all(cmd.as_bytes())?;
let mut res: Vec<u8> = Vec::new();
self.read.read_until(0u8, &mut res).unwrap();
stream.read_to_end(&mut res)?;
res.push(0);
process_result(res)
}
}
pub fn cmdj(&mut self, cmd: &str) -> Result<Json, String> {
let res = try!(self.cmd(cmd));
Ok(Json::from_str(&res).unwrap())
}
pub struct R2PipeNative {
lib: dlfcn::LibHandle,
r_core: std::sync::Mutex<*mut libc::c_void>,
r_core_cmd_str_handle: fn(*mut libc::c_void, *const libc::c_char) -> *mut libc::c_char,
}
pub fn close(&mut self) {
// self.read.close();
// self.write.close();
impl R2PipeNative {
pub fn open(file: &str) -> Result<R2PipeNative> {
let mut lib = dlfcn::LibHandle::new("libr_core", 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() {
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 = 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 {
Ok(unsafe { std::ffi::CStr::from_ptr(res).to_str()?.to_string() })
}
}
}
impl Drop for R2PipeNative {
fn drop(&mut self) {
let r_core = *self.r_core.lock().unwrap();
if let Ok(r_core_free) =
unsafe { self.lib.load_sym::<fn(*mut libc::c_void)>("r_core_free") }
{
r_core_free(r_core);
}
}
}
#[cfg(test)]
mod test {
use super::Pipe;
use super::R2PipeNative;
use crate::R2Pipe;
#[test]
fn spawn_test() {
let mut pipe = R2Pipe::spawn("/bin/ls", None).unwrap();
assert_eq!(pipe.cmd("echo test").unwrap(), "test\n");
}
#[test]
fn native_test() {
let mut r2p = R2PipeNative::open("/bin/ls").unwrap();
assert_eq!("a\n", r2p.cmd("echo a").unwrap());
}
}
-86
View File
@@ -1,86 +0,0 @@
// Copyright (c) 2015, The Radare Project. All rights reserved.
// See the COPYING file at the top-level directory of this distribution.
// Licensed under the BSD 3-Clause License:
// <http://opensource.org/licenses/BSD-3-Clause>
// This file may not be copied, modified, or distributed
// except according to those terms.
//! Basic structs for JSON encoding and decoding.
use rustc_serialize::{Decodable, Decoder};
#[derive(RustcEncodable, Debug, Clone, Default)]
pub struct LOpInfo {
pub esil: Option<String>,
pub offset: Option<u64>,
pub opcode: Option<String>,
pub optype: Option<String>,
pub size: Option<u64>,
}
#[derive(RustcDecodable, RustcEncodable, Debug, Clone, Default)]
pub struct LFunctionInfo {
pub addr: Option<u64>,
pub name: Option<String>,
pub ops: Option<Vec<LOpInfo>>,
}
#[derive(RustcDecodable, RustcEncodable, Debug, Clone, Default)]
pub struct LRegInfo {
pub alias_info: Vec<LAliasInfo>,
pub reg_info: Vec<LRegProfile>,
}
#[derive(RustcDecodable, RustcEncodable, Debug, Clone, Default)]
pub struct LAliasInfo {
pub reg: String,
pub role: u64,
pub role_str: String,
}
#[derive(RustcDecodable, RustcEncodable, Debug, Clone, Default)]
pub struct LRegProfile {
pub name: String,
pub offset: usize,
pub size: usize,
pub type_str: String,
}
#[derive(RustcDecodable, RustcEncodable, Debug, Clone, Default)]
pub struct LFlagInfo {
pub offset: u64,
pub name: String,
pub size: u64,
}
#[derive(RustcDecodable, RustcEncodable, Debug, Clone, Default)]
pub struct LBinInfo {
pub core: Option<LCoreInfo>,
pub bin: Option<LBin>,
}
#[derive(RustcDecodable, RustcEncodable, Debug, Clone, Default)]
pub struct LCoreInfo {
pub file: Option<String>,
pub size: Option<usize>,
}
#[derive(RustcDecodable, RustcEncodable, Debug, Clone, Default)]
pub struct LBin {
pub arch: Option<String>,
}
impl Decodable for LOpInfo {
fn decode<D: Decoder>(d: &mut D) -> Result<LOpInfo, D::Error> {
d.read_struct("root", 0, |d_| {
let op = LOpInfo {
esil: d_.read_struct_field("esil", 0, |d| Decodable::decode(d)).ok(),
offset: d_.read_struct_field("offset", 0, |d| Decodable::decode(d)).ok(),
opcode: d_.read_struct_field("opcode", 0, |d| Decodable::decode(d)).ok(),
optype: d_.read_struct_field("type", 0, |d| Decodable::decode(d)).ok(),
size: d_.read_struct_field("size", 0, |d| Decodable::decode(d)).ok(),
};
Ok(op)
})
}
}