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}