Skip to main content

idakit_sys/
runtime.rs

1//! idalib runtime lifecycle, kernel thread-affinity, error reporting, and the fatal-exit trap.
2
3use std::ffi::{c_char, c_int};
4
5// idalib lifecycle entry points (plain C ABI from libidalib.so)
6unsafe extern "C" {
7    pub fn init_library(argc: c_int, argv: *mut *mut c_char) -> c_int;
8    /// Facade wrapper: force headless (`TVHEADLESS`) then [`init_library`]; returns its rc.
9    pub fn idakit_init_library() -> c_int;
10    /// Set IDA's `batch` global: nonzero suppresses dialogs/auto-answers prompts, zero restores
11    /// interactive behavior.
12    pub fn idakit_set_batch(on: c_int);
13    pub fn get_library_version(major: *mut c_int, minor: *mut c_int, build: *mut c_int) -> bool;
14    pub fn open_database(path: *const c_char, run_auto: bool, args: *const c_char) -> c_int;
15    pub fn close_database(save: bool);
16    pub fn enable_console_messages(enable: bool);
17}
18
19// kernel thread-affinity (plain C ABI from libida.so). `is_main_thread` reads
20// libida's nullable `g_main`: non-null -> compares to caller; null -> claims caller.
21unsafe extern "C" {
22    pub fn is_main_thread() -> bool;
23}
24
25// auto-analysis (plain C ABI from libida.so). `open_database(run_auto=true)` only
26// *enables* the analysis queue; `auto_wait` blocks until it drains, so a caller that
27// wants a fully analyzed database calls it after opening (mirrors idalib).
28unsafe extern "C" {
29    pub fn auto_wait() -> bool;
30}
31
32// IDA's thread-safe error reporting (plain C ABI from libida.so). `error_t` is an
33// `int`; `get_qerrno` reads the thread's last code and `qstrerror` describes one
34// (folding in the C `errno` text for the `eOS` code).
35unsafe extern "C" {
36    pub fn get_qerrno() -> c_int;
37    /// Describe an `error_t`. The returned pointer borrows IDA's static/thread-local
38    /// storage: it must not be freed, and a later `qstrerror` call on the same thread may
39    /// overwrite it. Copy it into a `CStr`/`String` before the next IDA call.
40    pub fn qstrerror(code: c_int) -> *const c_char;
41}
42
43// Fatal trap. The guarded entry points (open, auto_wait, close, decompile) wrap their SDK
44// call in a `setjmp` guard and redirect libida's GOT `exit`/`abort` slots to handlers that
45// `longjmp` back, turning IDA's fatal paths (unaccepted license, LLVM/libc++ asserts) into a
46// return value. A guarded call returns its normal rc, or `IDAKIT_EXIT_TRAPPED` (and sets
47// `idakit_was_trapped`); `idakit_last_exit_code`/`idakit_last_output` then carry the detail.
48pub const IDAKIT_EXIT_TRAPPED: c_int = -0x7FFF_FFFF;
49unsafe extern "C" {
50    pub fn idakit_guarded_open(path: *const c_char, run_auto: c_int) -> c_int;
51    pub fn idakit_guarded_auto_wait() -> c_int;
52    pub fn idakit_guarded_close(save: c_int) -> c_int;
53    pub fn idakit_last_exit_code() -> c_int;
54    pub fn idakit_was_trapped() -> c_int;
55    pub fn idakit_last_output(buf: *mut c_char, cap: usize) -> usize;
56    pub fn idakit_reg_read_int(name: *const c_char, defval: c_int) -> c_int;
57    pub fn idakit_accept_eula() -> c_int;
58}
59
60/// [`idakit_test_fatal`] kind: run `exit()` inside the guarded call.
61#[doc(hidden)]
62pub const IDAKIT_FATAL_EXIT: c_int = 0;
63/// [`idakit_test_fatal`] kind: run `abort()` inside the guarded call.
64#[doc(hidden)]
65pub const IDAKIT_FATAL_ABORT: c_int = 1;
66/// [`idakit_test_fatal`] kind: run `interr()` inside the guarded call.
67#[doc(hidden)]
68pub const IDAKIT_FATAL_INTERR: c_int = 2;
69
70// Fault-injection hooks, always compiled but `#[doc(hidden)]` so they stay off the public API.
71// Run the chosen fatal inside `guarded<>` so the trap tests can prove it becomes
72// `IDAKIT_EXIT_TRAPPED`; `idakit_test_fatal` arms its own guard, so it can't terminate the process.
73unsafe extern "C" {
74    #[doc(hidden)]
75    pub fn idakit_test_fatal(kind: c_int) -> c_int;
76    /// Read back the `batch` global, to prove bring-up wired [`idakit_set_batch`].
77    #[doc(hidden)]
78    pub fn idakit_get_batch() -> c_int;
79    /// Arm `guarded<>`, then reach the chosen fatal *through* a cxx `Result`-shim, so the trap's
80    /// `longjmp` (exit/abort) must unwind across the shim's `try/catch` frame. Returns
81    /// [`IDAKIT_EXIT_TRAPPED`] when the longjmp fired, or `1` when cxx caught the throw first
82    /// (interr, which is a `std::exception`) and reported a Rust `Err` instead of trapping.
83    #[doc(hidden)]
84    pub fn idakit_test_fatal_through_cxx(kind: c_int) -> c_int;
85}