binaryninja/
llvm.rs

1//! LLVM functionality exposed by the core.
2//!
3//! Also see [`crate::demangle::demangle_llvm`].
4
5use binaryninjacore_sys::{
6    BNLlvmServicesAssemble, BNLlvmServicesAssembleFree, BNLlvmServicesDisasmInstruction,
7    BNLlvmServicesInit,
8};
9use std::ffi::{CStr, CString};
10use std::os::raw::{c_char, c_int};
11
12#[repr(i32)]
13pub enum LlvmServicesDialect {
14    Unspecified = 0,
15    Att = 1,
16    Intel = 2,
17}
18
19#[repr(i32)]
20pub enum LlvmServicesCodeModel {
21    Default = 0,
22    Small = 1,
23    Kernel = 2,
24    Medium = 3,
25    Large = 4,
26}
27
28#[repr(i32)]
29pub enum LlvmServicesRelocMode {
30    Static = 0,
31    PIC = 1,
32    DynamicNoPIC = 2,
33}
34
35pub fn llvm_assemble(
36    code: &str,
37    dialect: LlvmServicesDialect,
38    triplet: &str,
39    code_model: LlvmServicesCodeModel,
40    reloc_mode: LlvmServicesRelocMode,
41) -> Result<Vec<u8>, String> {
42    let code = CString::new(code).map_err(|_| "Invalid encoding in code string".to_string())?;
43    let arch_triple = CString::new(triplet)
44        .map_err(|_| "Invalid encoding in architecture triple string".to_string())?;
45    let mut out_bytes: *mut std::ffi::c_char = std::ptr::null_mut();
46    let mut out_bytes_len: std::ffi::c_int = 0;
47    let mut err_bytes: *mut std::ffi::c_char = std::ptr::null_mut();
48    let mut err_len: std::ffi::c_int = 0;
49
50    unsafe {
51        BNLlvmServicesInit();
52    }
53
54    let result = unsafe {
55        BNLlvmServicesAssemble(
56            code.as_ptr(),
57            dialect as i32,
58            arch_triple.as_ptr(),
59            code_model as i32,
60            reloc_mode as i32,
61            &mut out_bytes as *mut *mut std::ffi::c_char,
62            &mut out_bytes_len as *mut std::ffi::c_int,
63            &mut err_bytes as *mut *mut std::ffi::c_char,
64            &mut err_len as *mut std::ffi::c_int,
65        )
66    };
67
68    let out = if out_bytes_len == 0 {
69        Vec::new()
70    } else {
71        unsafe {
72            std::slice::from_raw_parts(
73                out_bytes as *const std::ffi::c_char as *const u8,
74                out_bytes_len as usize,
75            )
76        }
77        .to_vec()
78    };
79
80    let errors = if err_len == 0 {
81        "".into()
82    } else {
83        String::from_utf8_lossy(unsafe {
84            std::slice::from_raw_parts(
85                err_bytes as *const std::ffi::c_char as *const u8,
86                err_len as usize,
87            )
88        })
89        .into_owned()
90    };
91
92    unsafe {
93        BNLlvmServicesAssembleFree(out_bytes, err_bytes);
94    }
95
96    if result == 0 {
97        Ok(out)
98    } else {
99        Err(errors)
100    }
101}
102
103pub fn llvm_disassemble(triplet: &str, data: &[u8], address: u64) -> Option<(usize, String)> {
104    unsafe {
105        let triplet = CString::new(triplet).ok()?;
106        let mut src = data.to_vec();
107        let mut buf = vec![0u8; 256];
108        let instr_len = BNLlvmServicesDisasmInstruction(
109            triplet.as_ptr(),
110            src.as_mut_ptr(),
111            src.len() as c_int,
112            address,
113            buf.as_mut_ptr() as *mut c_char,
114            buf.len(),
115        );
116
117        if instr_len > 0 {
118            // Convert buf (u8) → &CStr by finding the first NUL
119            if let Some(z) = buf.iter().position(|&b| b == 0) {
120                let s = CStr::from_bytes_with_nul(&buf[..=z])
121                    .unwrap()
122                    .to_string_lossy()
123                    .into_owned();
124                Some((instr_len as usize, s))
125            } else {
126                // Callee didn't NULL terminate, return an empty string
127                Some((instr_len as usize, String::new()))
128            }
129        } else {
130            None
131        }
132    }
133}