1use std::collections::{HashMap, HashSet};
17use std::fs::File;
18use std::io::Write;
19use std::ops::Deref;
20use std::path::{Path, PathBuf};
21
22use fugue_db::backend::{Backend, Imported};
23use fugue_db::Error as ExportError;
24use iset::IntervalSet;
25use itertools::Itertools;
26use r2pipe::R2Pipe;
27pub use r2pipe::R2PipeSpawnOptions;
28use serde::Deserialize;
29use thiserror::Error;
30use url::Url;
31use which::{which, which_in};
32
33mod schema;
34
35#[derive(Debug, Error)]
36pub enum Error {
37 #[error("r2/rizin is not available as a backend")]
38 NotAvailable,
39 #[error("invalid path to r2/rizin: {0}")]
40 InvalidPath(which::Error),
41 #[error("invalid import URL: {0}")]
42 InvalidImportPath(String),
43 #[error("invalid shared memory mapping: {0}")]
44 InvalidImportShm(shared_memory::ShmemError),
45 #[error("invalid file-system path: {0}")]
46 InvalidImportFile(std::io::Error),
47 #[error("invalid segment")]
48 InvalidSegment,
49 #[error("coult not export to file: {0}")]
50 CannotExportToFile(std::io::Error),
51 #[error("coult not map file: {0}")]
52 CannotMapFile(std::io::Error),
53 #[error("could not deserialise output from `{0}`: {1}")]
54 Deserialise(&'static str, #[source] serde_json::Error),
55 #[error("pipe communication error: {0}")]
56 Pipe(#[from] r2pipe::Error),
57 #[error("unsupported architecture: {0}")]
58 UnsupportedArch(String),
59 #[error("unsupported format: {0}")]
60 UnsupportedFormat(String),
61 #[error("unsupported URL scheme: {0}")]
62 UnsupportedScheme(String),
63}
64
65impl From<Error> for ExportError {
66 fn from(e: Error) -> Self {
67 ExportError::importer_error("fugue-radare", e)
68 }
69}
70
71#[derive(Debug, Deserialize)]
72struct MetadataBin<'d> {
73 arch: &'d str,
74 bintype: &'d str,
75 bits: u32,
76 endian: &'d str,
77}
78
79#[derive(Debug, Deserialize)]
80struct MetadataCore<'d> {
81 file: &'d str,
82 #[allow(unused)]
83 size: u32,
84}
85
86#[derive(Debug, Deserialize)]
87struct MetadataHashes<'d> {
88 #[allow(unused)]
89 md5: &'d str,
90 #[allow(unused)]
91 sha1: &'d str,
92 #[allow(unused)]
93 sha256: &'d str,
94}
95
96#[derive(Debug, Deserialize)]
97struct Metadata<'d> {
98 #[serde(borrow)]
99 core: MetadataCore<'d>,
100 #[serde(borrow)]
101 bin: MetadataBin<'d>,
102}
103
104impl<'d> Metadata<'d> {
105 fn format(format: &str) -> Result<&'static str, Error> {
106 Ok(match format {
107 "elf" | "elf64" => "ELF",
108 "mach0" | "mach064" => "Mach-O",
109 "pe" | "pe64" => "PE",
110 "te" => "TE",
111 "any" => "Raw",
112 _ => return Err(Error::UnsupportedFormat(format.to_string())),
113 })
114 }
115
116 fn processor(processor: &str, bits: u32) -> Result<(&'static str, &'static str), Error> {
117 Ok(match processor {
118 "arm" => {
119 if bits == 64 {
120 ("AARCH64", "v8A")
121 } else {
122 ("ARM", "v7")
123 }
124 }
125 "mips" => ("MIPS", "default"),
126 "ppc" => ("PowerPC", "default"),
127 "x86" => ("x86", "default"), _ => return Err(Error::UnsupportedArch(processor.to_string())),
129 })
130 }
131
132 fn endian(endian: &str) -> bool {
133 endian != "little" && endian != "LE"
134 }
135}
136
137#[derive(Debug, Deserialize)]
138struct Version<'d> {
139 name: &'d str,
140 version: &'d str,
141}
142
143#[derive(Debug, Deserialize)]
144struct SegmentInfo<'d> {
145 name: &'d str,
146 size: u32,
147 vsize: u32,
148 perm: &'d str,
149 #[serde(default)]
150 paddr: u64,
151 vaddr: i128,
152}
153
154impl<'d> SegmentInfo<'d> {
155 fn is_executable(&self) -> bool {
156 self.perm.contains("x")
157 }
158
159 fn is_readable(&self) -> bool {
160 self.perm.contains("r")
161 }
162
163 fn is_writable(&self) -> bool {
164 self.perm.contains("w")
165 }
166
167 fn address(&self) -> Option<u64> {
168 if self.vaddr < 0 || self.vaddr > u64::MAX as i128 {
169 None
170 } else {
171 Some(self.vaddr as u64)
172 }
173 }
174
175 fn is_code(&self) -> bool {
176 self.is_executable()
177 }
178
179 fn is_data(&self) -> bool {
180 !self.is_code()
181 }
182}
183
184#[derive(Debug, Deserialize)]
185struct Symbol<'d> {
186 realname: &'d str, #[serde(rename = "type")]
188 kind: &'d str,
189 #[serde(default)]
190 vaddr: u64,
191 #[serde(default)]
192 #[allow(unused)]
193 paddr: u64,
194 is_imported: bool,
195}
196
197#[derive(Debug, Deserialize)]
198struct Relocation<'d> {
199 name: Option<&'d str>,
200 vaddr: u64,
201}
202
203#[derive(Debug, Deserialize)]
204struct InterRef<'d> {
205 from: u64, #[serde(rename = "type")]
207 kind: &'d str, fcn_addr: Option<u64>, }
210
211#[derive(Debug, Default, Deserialize)]
212struct BasicBlock {
213 addr: u64,
214 size: u32,
215 jump: Option<u64>,
216 fail: Option<u64>,
217}
218
219#[derive(Debug, Deserialize)]
220struct Function<'d> {
221 offset: u64,
222 name: &'d str, }
224
225pub enum Backing {
226 M(File, memmap2::Mmap),
227 S(shared_memory::Shmem),
228}
229
230impl Deref for Backing {
231 type Target = [u8];
232
233 fn deref(&self) -> &Self::Target {
234 match self {
235 Self::M(_, ref m) => m.deref(),
236 Self::S(ref s) => unsafe { s.as_slice() },
237 }
238 }
239}
240
241pub struct RadareExporter<'db> {
242 config: R2PipeSpawnOptions,
243
244 builder: flatbuffers::FlatBufferBuilder<'db>,
245 pipe: R2Pipe,
246
247 backing: Backing,
248
249 endian: bool, address_size: u32,
251 bits: u32,
252}
253
254impl<'db> RadareExporter<'db> {
255 pub fn new_with<P: AsRef<str>>(path: P, mut config: R2PipeSpawnOptions) -> Result<Self, Error> {
256 if config.exepath.is_empty() {
257 config.exepath.push_str("r2");
258 }
259
260 let path = path.as_ref();
261
262 let backing = if let Some(id) = path
263 .strip_prefix("shm:/")
264 .and_then(|rest| rest.rsplit_once("/").map(|(v, _)| v))
265 {
266 let sc = shared_memory::ShmemConf::new().os_id(id);
267 Backing::S(sc.open().map_err(Error::InvalidImportShm)?)
268 } else {
269 let path = path.strip_prefix("file://").unwrap_or(&path);
270 let file = File::open(path).map_err(Error::InvalidImportFile)?;
271
272 let mm = unsafe { memmap2::Mmap::map(&file) }.map_err(Error::CannotMapFile)?;
273
274 Backing::M(file, mm)
275 };
276
277 Ok(Self {
278 builder: flatbuffers::FlatBufferBuilder::new(),
279 pipe: R2Pipe::spawn(path, Some(config.clone()))?,
280 config,
281 backing,
282 endian: Default::default(),
283 address_size: Default::default(),
284 bits: Default::default(),
285 })
286 }
287
288 pub fn new<P: AsRef<str>>(path: P) -> Result<Self, Error> {
289 Self::new_with(path, Default::default())
290 }
291
292 pub fn analyse_with<S: AsRef<str>>(&mut self, command: S) -> Result<(), Error> {
293 self.pipe
294 .cmd(command.as_ref())
295 .map(|_| ())
296 .map_err(Error::Pipe)
297 }
298
299 pub fn analyse(&mut self) -> Result<(), Error> {
300 self.analyse_with("aaaa")
301 }
302
303 fn export_project(&mut self) -> Result<flatbuffers::WIPOffset<schema::Project<'db>>, Error> {
304 let (arch, meta) = self.export_metadata()?;
305 let avec = self.builder.create_vector_from_iter(std::iter::once(arch));
306
307 let (segs, seg_ivt) = self.export_segments()?;
308 let svec = self.builder.create_vector_from_iter(segs.into_iter());
309
310 let fcns = self.export_functions(&seg_ivt)?;
311 let fvec = self.builder.create_vector_from_iter(fcns.into_iter());
312
313 let mut dbuilder = schema::ProjectBuilder::new(&mut self.builder);
314
315 dbuilder.add_architectures(avec);
316 dbuilder.add_segments(svec);
317 dbuilder.add_functions(fvec);
318 dbuilder.add_metadata(meta);
319
320 Ok(dbuilder.finish())
321 }
322
323 fn export_version(&mut self) -> Result<String, Error> {
324 let vers = if self.config.exepath.ends_with("rizin")
326 || self.config.exepath.ends_with("rizin.exe")
327 {
328 self.pipe.cmd(&format!("!{} -v", self.config.exepath))?
329 } else {
330 let value3 = self.pipe.cmd(&format!("!{} -vj", self.config.exepath))?;
331 let version = serde_json::from_str::<Version>(&value3)
332 .map_err(|e| Error::Deserialise("!r2 -vj", e))?;
333 format!("{} {}", version.name, version.version)
334 };
335
336 Ok(vers)
337 }
338
339 fn export_metadata(
340 &mut self,
341 ) -> Result<
342 (
343 flatbuffers::WIPOffset<schema::Architecture<'db>>,
344 flatbuffers::WIPOffset<schema::Metadata<'db>>,
345 ),
346 Error,
347 > {
348 let value1 = self.pipe.cmd("ij")?;
349 let corebin =
350 serde_json::from_str::<Metadata>(&value1).map_err(|e| Error::Deserialise("ij", e))?;
351
352 let md5 = md5::compute(&*self.backing);
353 let sha256 = <sha2::Sha256 as sha2::Digest>::digest(&*self.backing);
354
355 let mut exporter = self
356 .export_version()
357 .unwrap_or_else(|_| "radare (unknown version)".to_string());
358
359 if exporter.is_empty() {
360 exporter = Path::new(&self.config.exepath)
361 .file_name()
362 .and_then(|path| path.to_str().map(ToOwned::to_owned))
363 .unwrap_or_else(|| self.config.exepath.clone());
364 }
365
366 let (def_arch_processor, def_arch_variant) =
367 Metadata::processor(&corebin.bin.arch, corebin.bin.bits)?;
368 let endian = Metadata::endian(&corebin.bin.endian);
369
370 self.endian = endian;
371 self.bits = corebin.bin.bits;
372 self.address_size = corebin.bin.bits;
373
374 let processor = self.builder.create_string(def_arch_processor);
375 let variant = self.builder.create_string(def_arch_variant);
376
377 let arch = {
378 let mut abuilder = schema::ArchitectureBuilder::new(&mut self.builder);
379
380 abuilder.add_processor(processor);
381 abuilder.add_bits(corebin.bin.bits);
382 abuilder.add_endian(endian);
383 abuilder.add_variant(variant);
384
385 abuilder.finish()
386 };
387
388 let meta = {
389 let input_path = self.builder.create_string(&corebin.core.file);
390 let input_md5 = self.builder.create_vector(&*md5);
391 let input_sha256 = self.builder.create_vector(&*sha256);
392 let input_format = self
393 .builder
394 .create_string(Metadata::format(&corebin.bin.bintype)?);
395 let exporter = self.builder.create_string(&exporter);
396
397 let mut mbuilder = schema::MetadataBuilder::new(&mut self.builder);
398
399 mbuilder.add_input_path(input_path);
400 mbuilder.add_input_md5(input_md5);
401 mbuilder.add_input_sha256(input_sha256);
402 mbuilder.add_input_format(input_format);
403 mbuilder.add_input_size(self.backing.len() as u32);
404 mbuilder.add_exporter(exporter);
405
406 mbuilder.finish()
407 };
408
409 Ok((arch, meta))
410 }
411
412 fn export_segment(
413 &mut self,
414 seg: SegmentInfo,
415 ) -> Result<flatbuffers::WIPOffset<schema::Segment<'db>>, Error> {
416 let content = if seg.size > 0 {
417 if seg.size > seg.vsize {
418 return Err(Error::InvalidSegment);
419 }
420
421 let start = seg.paddr as usize;
422 let end = start
423 .checked_add(seg.size as usize)
424 .ok_or(Error::InvalidSegment)?;
425 self.backing.get(start..end).ok_or(Error::InvalidSegment)?
426 } else {
427 &[]
428 };
429
430 let name = self.builder.create_string(seg.name);
431 let bytes = self.builder.create_vector(&content);
432
433 let mut sbuilder = schema::SegmentBuilder::new(&mut self.builder);
434
435 sbuilder.add_name(name);
436 sbuilder.add_address(seg.address().unwrap());
437 sbuilder.add_size_(seg.vsize);
438 sbuilder.add_alignment_(1); sbuilder.add_address_size(self.address_size);
440 sbuilder.add_endian(self.endian);
441 sbuilder.add_bits(self.bits);
442 sbuilder.add_code(seg.is_code());
443 sbuilder.add_data(seg.is_data());
444 sbuilder.add_external(false);
445 sbuilder.add_executable(seg.is_executable());
446 sbuilder.add_readable(seg.is_readable());
447 sbuilder.add_writable(seg.is_writable());
448 sbuilder.add_bytes(bytes);
449
450 Ok(sbuilder.finish())
451 }
452
453 fn export_segments(
454 &mut self,
455 ) -> Result<
456 (
457 Vec<flatbuffers::WIPOffset<schema::Segment<'db>>>,
458 IntervalSet<u64>,
459 ),
460 Error,
461 > {
462 let segsv = self.pipe.cmd("iSj")?;
463 let seginfos = serde_json::from_str::<Vec<SegmentInfo>>(&segsv)
464 .map_err(|e| Error::Deserialise("iSj", e))?;
465
466 let mut seg_ivt = IntervalSet::new();
467 let segs = seginfos
468 .into_iter()
469 .filter_map(|s| {
470 if s.name.is_empty() && s.size == 0 {
471 None
472 } else {
473 let vaddr = s.address()?;
474 seg_ivt.insert(vaddr..vaddr.checked_add(s.vsize as u64)?);
475 Some(self.export_segment(s))
476 }
477 })
478 .collect::<Result<Vec<_>, Error>>()?;
479
480 Ok((segs, seg_ivt))
481 }
482
483 fn export_interref(
484 &mut self,
485 from: InterRef,
486 to_id: u32,
487 is_call: bool,
488 fcn_map: &HashMap<u64, u32>,
489 ) -> flatbuffers::WIPOffset<schema::InterRef<'db>> {
490 let mut ibuilder = schema::InterRefBuilder::new(&mut self.builder);
491
492 ibuilder.add_address(from.from);
493 ibuilder.add_source(
494 *from
495 .fcn_addr
496 .as_ref()
497 .and_then(|addr| fcn_map.get(addr))
498 .unwrap_or(&0xffff_ffff),
499 );
500 ibuilder.add_target(to_id);
501 ibuilder.add_call(is_call);
502
503 ibuilder.finish()
504 }
505
506 fn export_pred_intraref(
507 &mut self,
508 fcn_id: u32,
509 from: u64,
510 to_id: u64,
511 blk_map: &HashMap<u64, u64>,
512 ) -> flatbuffers::WIPOffset<schema::IntraRef<'db>> {
513 let source = blk_map[&from];
514 let mut ibuilder = schema::IntraRefBuilder::new(&mut self.builder);
515
516 ibuilder.add_source(source);
517 ibuilder.add_target(to_id);
518 ibuilder.add_function(fcn_id);
519
520 ibuilder.finish()
521 }
522
523 fn export_succ_intraref(
524 &mut self,
525 fcn_id: u32,
526 from_id: u64,
527 to: u64,
528 blk_map: &HashMap<u64, u64>,
529 ) -> flatbuffers::WIPOffset<schema::IntraRef<'db>> {
530 let target = blk_map[&to];
531 let mut ibuilder = schema::IntraRefBuilder::new(&mut self.builder);
532
533 ibuilder.add_source(from_id);
534 ibuilder.add_target(target);
535 ibuilder.add_function(fcn_id);
536
537 ibuilder.finish()
538 }
539
540 fn export_block(
541 &mut self,
542 fcn: u32,
543 block: BasicBlock,
544 id: u64,
545 preds: Vec<u64>,
546 succs: Vec<u64>,
547 blk_map: &HashMap<u64, u64>,
548 ) -> flatbuffers::WIPOffset<schema::BasicBlock<'db>> {
549 let predsi = preds
550 .into_iter()
551 .map(|pred| self.export_pred_intraref(fcn, pred, id, blk_map))
552 .collect::<Vec<_>>();
553
554 let preds = self.builder.create_vector_from_iter(predsi.into_iter());
555
556 let succsi = succs
557 .into_iter()
558 .filter(|succ| blk_map.contains_key(succ)) .map(|succ| self.export_succ_intraref(fcn, id, succ, blk_map))
560 .collect::<Vec<_>>();
561
562 let succs = self.builder.create_vector_from_iter(succsi.into_iter());
563
564 let mut bbuilder = schema::BasicBlockBuilder::new(&mut self.builder);
565
566 bbuilder.add_address(block.addr);
567 bbuilder.add_size_(block.size);
568 bbuilder.add_predecessors(preds);
569 bbuilder.add_successors(succs);
570
571 bbuilder.finish()
572 }
573
574 fn export_function(
575 &mut self,
576 name: String,
577 address: u64,
578 imported: bool,
579 id: u32,
580 fcn_map: &HashMap<u64, u32>,
581 seg_ivt: &IntervalSet<u64>,
582 ) -> Result<flatbuffers::WIPOffset<schema::Function<'db>>, Error> {
583 let (entry, blocks) = if !imported {
584 let blksv = self.pipe.cmd(&format!("afbj @ {:#x}", address))?;
585 let blks = serde_json::from_str::<Vec<BasicBlock>>(&blksv)
586 .map_err(|e| Error::Deserialise("afbj", e))?;
587
588 let mut blk_map = HashMap::with_capacity(blks.len());
589 let mut blk_ibps_map =
590 HashMap::<u64, (u64, BasicBlock, Vec<u64>, Vec<u64>)>::with_capacity(blks.len());
591
592 for (i, blk) in blks
593 .into_iter()
594 .filter(|b| seg_ivt.has_overlap(b.addr..=b.addr))
595 .enumerate()
596 {
597 let bid = (id as u64) << 32 | (i as u64);
598 let addr = blk.addr;
599
600 let mut succs = Vec::new();
601 if let Some(addr) = blk.jump {
602 succs.push(addr);
603 }
604
605 if let Some(addr) = blk.fail {
606 succs.push(addr);
607 }
608
609 for succ in succs.iter() {
610 let ibps = blk_ibps_map.entry(*succ).or_default();
611 ibps.2.push(addr); }
613
614 blk_map.insert(addr, bid);
615
616 let ibps = blk_ibps_map.entry(addr).or_default();
617 ibps.0 = bid;
618 ibps.1 = blk;
619 ibps.3.extend(succs.into_iter());
620 }
621
622 let entry = *blk_map.get(&address).unwrap_or(&0xffffffff_ffffffff);
623 let blocks = blk_ibps_map
624 .into_iter()
625 .sorted_by_key(|(_, (bid, _, _, _))| *bid)
626 .filter(|(addr, _)| blk_map.contains_key(&addr)) .map(|(_, (bid, blk, preds, succs))| {
628 self.export_block(id, blk, bid, preds, succs, &blk_map)
629 })
630 .collect::<Vec<_>>();
631 (entry, blocks)
632 } else {
633 (0xffffffff_ffffffff, Vec::default())
634 };
635
636 let refsv = self.pipe.cmd(&format!("axtj @ {:#x}", address))?;
637 let refs = serde_json::from_str::<Vec<InterRef>>(&refsv)
638 .map_err(|e| Error::Deserialise("axtj", e))?;
639
640 let references = refs
641 .into_iter()
642 .filter(|r| r.kind == "CALL" || (r.kind == "CODE" && r.fcn_addr != Some(address)))
644 .map(|r| {
645 let is_call = r.kind == "CALL";
646 self.export_interref(r, id, is_call, fcn_map)
647 })
648 .collect::<Vec<_>>();
649
650 let symbol = self.builder.create_string(name.trim());
651 let basic_blocks = self.builder.create_vector_from_iter(blocks.into_iter());
652 let references = self.builder.create_vector_from_iter(references.into_iter());
653
654 let mut fbuilder = schema::FunctionBuilder::new(&mut self.builder);
655
656 fbuilder.add_address(address);
657 fbuilder.add_entry(entry);
658 fbuilder.add_symbol(symbol);
659 fbuilder.add_blocks(basic_blocks);
660 fbuilder.add_references(references);
661
662 Ok(fbuilder.finish())
663 }
664
665 fn export_functions(
666 &mut self,
667 seg_ivt: &IntervalSet<u64>,
668 ) -> Result<Vec<flatbuffers::WIPOffset<schema::Function<'db>>>, Error> {
669 let symbolsv = self.pipe.cmd("isj")?;
670 let symbols = serde_json::from_str::<Vec<Symbol>>(&symbolsv)
671 .map_err(|e| Error::Deserialise("isj", e))?;
672
673 let relocsv = self.pipe.cmd("irj")?;
674 let relocs = serde_json::from_str::<Vec<Relocation>>(&relocsv)
675 .map_err(|e| Error::Deserialise("irj", e))?;
676
677 let fcnsv = self.pipe.cmd("aflj")?; let fcns = serde_json::from_str::<Vec<Function>>(&fcnsv)
679 .map_err(|e| Error::Deserialise("aflj", e))?;
680
681 let mut n2a_map = HashMap::new();
682 let mut a2n_map = HashMap::new(); for (id, fcn) in fcns.iter().enumerate() {
685 let id = id as u32;
686 a2n_map.insert(fcn.offset, id);
687 n2a_map.insert(fcn.name.to_owned(), (fcn.offset, false, id));
688 }
689
690 let mut unmatched = HashSet::new();
691
692 for sym in symbols
693 .into_iter()
694 .filter(|s| s.kind == "FUNC" && s.is_imported)
695 {
696 if sym.vaddr != 0 {
697 let id = n2a_map.len() as u32;
698 a2n_map.insert(sym.vaddr, id);
699 n2a_map.insert(sym.realname.to_string(), (sym.vaddr, true, id));
700 } else {
701 unmatched.insert(sym.realname);
702 }
703 }
704
705 for (addr, name) in relocs.into_iter().filter_map(|r| {
706 if let Some(name) = r.name {
707 Some((r.vaddr, name))
708 } else {
709 None
710 }
711 }) {
712 if unmatched.contains(name) {
713 let id = n2a_map.len() as u32;
714 a2n_map.insert(addr, id);
715 n2a_map.insert(name.to_string(), (addr, true, id));
716 }
717 }
718
719 n2a_map
720 .into_iter()
721 .sorted_by_key(|(_name, (_addr, _imported, id))| *id)
722 .map(|(name, (addr, imported, id))| {
723 self.export_function(name, addr, imported, id, &a2n_map, seg_ivt)
724 })
725 .collect::<Result<Vec<_>, Error>>()
726 }
727
728 fn export(&mut self) -> Result<(), Error> {
729 let project = self.export_project()?;
730 schema::finish_project_buffer(&mut self.builder, project);
731 Ok(())
732 }
733
734 pub fn to_file<P: AsRef<Path>>(&mut self, path: P) -> Result<(), Error> {
735 self.builder.reset();
736 self.export()?;
737
738 let path = path.as_ref();
739 let mut file = File::create(path).map_err(Error::CannotExportToFile)?;
740
741 file.write_all(self.builder.finished_data())
742 .map_err(Error::CannotExportToFile)?;
743
744 Ok(())
745 }
746
747 pub fn to_bytes(&mut self) -> Result<&[u8], Error> {
748 self.builder.reset();
749 self.export()?;
750 Ok(self.builder.finished_data())
751 }
752}
753
754#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
755pub struct Radare {
756 r2_path: Option<PathBuf>,
757 fdb_path: Option<PathBuf>,
758 overwrite: bool,
759 analysis_commands: Vec<String>,
760}
761
762impl Default for Radare {
763 fn default() -> Self {
764 Self {
765 r2_path: None,
766 fdb_path: None,
767 overwrite: false,
768 analysis_commands: Vec::default(),
769 }
770 }
771}
772
773impl Radare {
774 fn find_r2<F: Fn(&str) -> Result<PathBuf, Error>>(f: F) -> Result<PathBuf, Error> {
775 if let Ok(local) = f("radare2").or_else(|_| f("r2")).or_else(|_| f("rizin")) {
776 Ok(local)
777 } else {
778 f("radare2.exe")
779 .or_else(|_| f("r2.exe"))
780 .or_else(|_| f("rizin.exe"))
781 }
782 }
783
784 fn find_rz<F: Fn(&str) -> Result<PathBuf, Error>>(f: F) -> Result<PathBuf, Error> {
785 if let Ok(local) = f("rizin") {
786 Ok(local)
787 } else {
788 f("rizin.exe")
789 }
790 }
791
792 pub fn new() -> Result<Self, Error> {
793 if let Ok(r2_path) = Self::find_r2(|p| which(p).map_err(Error::InvalidPath)) {
794 Ok(Self {
795 r2_path: Some(r2_path),
796 ..Default::default()
797 })
798 } else {
799 Err(Error::NotAvailable)
800 }
801 }
802
803 pub fn new_rizin() -> Result<Self, Error> {
804 if let Ok(rz_path) = Self::find_rz(|p| which(p).map_err(Error::InvalidPath)) {
805 Ok(Self {
806 r2_path: Some(rz_path),
807 ..Default::default()
808 })
809 } else {
810 Err(Error::NotAvailable)
811 }
812 }
813
814 pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
815 let root_dir = path.as_ref();
816 let r2_path =
817 Self::find_r2(|p| which_in(p, Some(root_dir), ".").map_err(Error::InvalidPath))?;
818
819 Ok(Self {
820 r2_path: Some(r2_path),
821 ..Default::default()
822 })
823 }
824
825 pub fn export_path<P: AsRef<Path>>(mut self, path: P, overwrite: bool) -> Self {
826 self.fdb_path = Some(path.as_ref().to_owned());
827 self.overwrite = overwrite;
828 self
829 }
830
831 pub fn with_analysis<S: AsRef<str>>(mut self, command: S) -> Self {
832 self.analysis_commands.push(command.as_ref().to_owned());
833 self
834 }
835}
836
837impl Backend for Radare {
838 type Error = Error;
839
840 fn name(&self) -> &'static str {
841 "fugue-radare"
842 }
843
844 fn is_available(&self) -> bool {
845 self.r2_path.is_some()
846 }
847
848 fn is_preferred_for(&self, path: &Url) -> Option<bool> {
849 Some(path.scheme() == "shm")
850 }
851
852 fn import(&self, program: &Url) -> Result<Imported, Self::Error> {
853 let program = if program.scheme() == "file" {
854 program
855 .to_file_path()
856 .map_err(|_| Error::UnsupportedScheme(program.scheme().to_owned()))?
857 .to_string_lossy()
858 .to_string()
859 } else if program.scheme() == "shm" {
860 program.to_string()
861 } else {
862 return Err(Error::UnsupportedScheme(program.scheme().to_owned()));
863 };
864
865 let r2_path = self.r2_path.as_ref().ok_or_else(|| Error::NotAvailable)?;
866 let config = R2PipeSpawnOptions {
867 exepath: format!("{}", r2_path.display()),
868 ..Default::default()
869 };
870
871 let mut exporter = RadareExporter::new_with(&program, config)?;
872 if self.analysis_commands.is_empty() {
873 exporter.analyse()?;
874 } else {
875 for cmd in self.analysis_commands.iter() {
876 exporter.analyse_with(cmd)?;
877 }
878 }
879
880 if let Some(ref fdb_path) = self.fdb_path {
881 if fdb_path.exists() && !self.overwrite {
882 return Ok(Imported::File(fdb_path.clone()));
883 } else {
884 exporter.to_file(fdb_path)?;
885 Ok(Imported::File(fdb_path.clone()))
886 }
887 } else {
888 Ok(Imported::Bytes(exporter.to_bytes()?.to_vec()))
889 }
890 }
891}
892
893#[cfg(test)]
894mod test {
895 use super::*;
896
897 #[test]
898 fn test_available() -> Result<(), Error> {
899 let r2 = Radare::new()?;
900 assert!(r2.is_available());
901 Ok(())
902 }
903
904 #[cfg(target_os = "linux")]
905 #[test]
906 fn test_available_bwrap() -> Result<(), Error> {
907 let r2 = Radare::from_path("./tests")?;
908 assert!(r2.is_available());
909 Ok(())
910 }
911
912 #[test]
913 fn test_import_true() -> Result<(), Error> {
914 let mut ex = RadareExporter::new_with(
915 "./tests/true",
916 R2PipeSpawnOptions {
917 exepath: "radare2".to_string(),
918 ..Default::default()
919 },
920 )?;
921 ex.analyse()?;
922
923 let _ = ex.to_bytes()?;
924 Ok(())
925 }
926
927 #[cfg(target_os = "linux")]
928 #[test]
929 fn test_import_true_bwrap() -> Result<(), Error> {
930 let mut ex = RadareExporter::new_with(
931 "/bin/true",
932 R2PipeSpawnOptions {
933 exepath: "./tests/radare2".to_string(),
934 ..Default::default()
935 },
936 )?;
937
938 ex.analyse()?;
939 let _ = ex.to_bytes()?;
940
941 Ok(())
942 }
943
944 #[test]
945 fn test_import_true_rizin() -> Result<(), Error> {
946 let mut ex = RadareExporter::new_with(
947 "./tests/true",
948 R2PipeSpawnOptions {
949 exepath: "rizin".to_string(),
950 ..Default::default()
951 },
952 )?;
953 ex.analyse()?;
954
955 let _ = ex.to_bytes()?;
956 Ok(())
957 }
958
959 #[test]
960 fn test_import_true_rizin_shm() -> Result<(), Box<dyn std::error::Error>> {
961 use std::io::Read;
962
963 let mut file = File::open("./tests/true")?;
964 let mut bytes = Vec::new();
965 file.read_to_end(&mut bytes)?;
966
967 let mut shm = shared_memory::ShmemConf::new().size(bytes.len()).create()?;
968
969 unsafe {
970 shm.as_slice_mut().copy_from_slice(&bytes);
971 }
972
973 let path = format!("shm:/{}/{}", shm.get_os_id(), bytes.len());
974 let purl = Url::parse(&path)?;
975
976 let riz = Radare::new_rizin()?.export_path("/tmp/ls-rz.fdb", true);
977 let _imp = riz.import(&purl)?;
978
979 Ok(())
980 }
981
982 #[test]
983 fn test_import_true_export() -> Result<(), Error> {
984 let mut ex = RadareExporter::new("./tests/true")?;
985 ex.analyse()?;
986 let _ = ex.to_file("/tmp/ls.fdb")?;
987 Ok(())
988 }
989
990 #[test]
991 fn test_import_efi_export() -> Result<(), Error> {
992 let mut ex = RadareExporter::new("./tests/tetris.efi")?;
993 ex.analyse()?;
994 let _ = ex.to_file("/tmp/tetris.fdb")?;
995 Ok(())
996 }
997
998 #[test]
999 fn test_db() -> Result<(), Box<dyn std::error::Error>> {
1000 use fugue::db::DatabaseImporter;
1001 use fugue::ir::disassembly::IRBuilderArena;
1002 use fugue::ir::LanguageDB;
1003
1004 let ldb = LanguageDB::from_directory_with("./tests", true)?;
1005 let irb = IRBuilderArena::with_capacity(4096);
1006 let mut dbi = DatabaseImporter::new("./tests/tetris.efi");
1007
1008 dbi.register_backend(Radare::new_rizin()?);
1009 let db = dbi.import(&ldb)?;
1010
1011 let mut ctx = db.translators().next().unwrap().context_database();
1012
1013 for f in db.functions() {
1014 println!("=== function: {:x} ===", f.address());
1015 for b in f.blocks() {
1016 println!("=== block insns: {:x} ===", b.address());
1017
1018 for insn in b.disassemble(&irb)? {
1019 println!("{}", insn.display());
1020 }
1021
1022 println!("=== block pcode: {:x} ===", b.address());
1023
1024 for stmt in b.lift_with(&mut ctx)? {
1025 println!("{}", stmt.display());
1026 }
1027 }
1028 }
1029
1030 Ok(())
1031 }
1032}