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    /// Initialize idalib. `argc`/`argv` are forwarded from the process; returns 0 on success.
8    pub fn init_library(argc: c_int, argv: *mut *mut c_char) -> c_int;
9    /// Force headless (`TVHEADLESS`), then initialize idalib; returns its rc.
10    pub fn init_headless() -> c_int;
11    /// Set IDA's `batch` global: nonzero suppresses dialogs/auto-answers prompts, zero restores
12    /// interactive behavior.
13    pub fn set_batch(on: c_int);
14    /// Read the running IDA's version into `major`/`minor`/`build`; returns whether it succeeded.
15    pub fn get_library_version(major: *mut c_int, minor: *mut c_int, build: *mut c_int) -> bool;
16    /// Open the database at `path`, optionally queuing auto-analysis (`run_auto`) with `args`
17    /// forwarded to it; returns its rc.
18    pub fn open_database(path: *const c_char, run_auto: bool, args: *const c_char) -> c_int;
19    /// Close the current database, saving it first when `save` is set.
20    pub fn close_database(save: bool);
21    /// Enable or disable idalib's console message output.
22    pub fn enable_console_messages(enable: bool);
23}
24
25// kernel thread-affinity (plain C ABI from libida.so). `is_main_thread` reads
26// libida's nullable `g_main`: non-null -> compares to caller; null -> claims caller.
27unsafe extern "C" {
28    /// Whether the calling thread is IDA's main (kernel) thread.
29    pub fn is_main_thread() -> bool;
30}
31
32// auto-analysis (plain C ABI from libida.so). `open_database(run_auto=true)` only
33// *enables* the analysis queue; `auto_wait` blocks until it drains, so a caller that
34// wants a fully analyzed database calls it after opening (mirrors idalib).
35unsafe extern "C" {
36    /// Block until the auto-analysis queue drains; returns whether it completed.
37    pub fn auto_wait() -> bool;
38}
39
40// IDA's thread-safe error reporting (plain C ABI from libida.so). `error_t` is an
41// `int`; `get_qerrno` reads the thread's last code and `qstrerror` describes one
42// (folding in the C `errno` text for the `eOS` code).
43unsafe extern "C" {
44    /// The calling thread's last `error_t` code.
45    pub fn get_qerrno() -> c_int;
46    /// Describe an `error_t`. The returned pointer borrows IDA's static/thread-local
47    /// storage: it must not be freed, and a later `qstrerror` call on the same thread may
48    /// overwrite it. Copy it into a `CStr`/`String` before the next IDA call.
49    pub fn qstrerror(code: c_int) -> *const c_char;
50}
51
52// Fatal trap. The guarded entry points (open, auto_wait, close, decompile) wrap their SDK
53// call in a `setjmp` guard and redirect libida's GOT `exit`/`abort` slots to handlers that
54// `longjmp` back, turning IDA's fatal paths (unaccepted license, LLVM/libc++ asserts) into a
55// return value. A guarded call returns its normal rc, or `crate::EXIT_TRAPPED` (and sets
56// `was_trapped`); `last_exit_code`/`last_output` then carry the detail.
57unsafe extern "C" {
58    /// Guarded [`open_database`]: same signature and rc, trapped against a fatal exit/abort.
59    pub fn guarded_open(path: *const c_char, run_auto: c_int) -> c_int;
60    /// Guarded [`auto_wait`], trapped against a fatal exit/abort; returns its rc.
61    pub fn guarded_auto_wait() -> c_int;
62    /// Guarded [`close_database`], trapped against a fatal exit/abort; returns its rc.
63    pub fn guarded_close(save: c_int) -> c_int;
64    /// The process exit code the most recent guarded call trapped, if any.
65    pub fn last_exit_code() -> c_int;
66    /// Whether the most recent guarded call trapped a fatal exit/abort.
67    pub fn was_trapped() -> c_int;
68    /// Copy the most recent guarded call's captured output into `buf`/`cap`, snprintf-style.
69    pub fn last_output(buf: *mut c_char, cap: usize) -> usize;
70    /// Read IDA registry integer `name`, or `defval` when unset.
71    #[link_name = "idakit_reg_read_int"]
72    pub fn reg_read_int(name: *const c_char, defval: c_int) -> c_int;
73    /// Accept the EULA programmatically, so headless bring-up never blocks on it.
74    pub fn accept_eula() -> c_int;
75}
76
77// Fault-injection hooks, always compiled but `#[doc(hidden)]` so they stay off the public API.
78// `kind` is one of `crate::FATAL_EXIT`/`FATAL_ABORT`/`FATAL_INTERR`. Run the chosen fatal inside
79// `guarded<>` so the trap tests can prove it becomes `crate::EXIT_TRAPPED`; `test_fatal` arms its
80// own guard, so it can't terminate the process.
81unsafe extern "C" {
82    #[doc(hidden)]
83    pub fn test_fatal(kind: c_int) -> c_int;
84    /// Read back the `batch` global, to prove bring-up wired [`set_batch`].
85    #[doc(hidden)]
86    pub fn get_batch() -> c_int;
87    /// Arm `guarded<>`, then reach the chosen fatal *through* a cxx `Result`-shim, so the trap's
88    /// `longjmp` (exit/abort) must unwind across the shim's `try/catch` frame. Returns
89    /// [`crate::EXIT_TRAPPED`] when the longjmp fired, or `1` when cxx caught the throw first
90    /// (interr, which is a `std::exception`) and reported a Rust `Err` instead of trapping.
91    #[doc(hidden)]
92    pub fn test_fatal_through_cxx(kind: c_int) -> c_int;
93}