rizin/librz/syscall/syscall.c
2025-11-04 23:38:16 +08:00

459 lines
10 KiB
C

// SPDX-FileCopyrightText: 2008-2020 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: LGPL-3.0-only
#include <rz_types.h>
#include <rz_util.h>
#include <rz_syscall.h>
#include <stdio.h>
#include <string.h>
#include <sdb.h>
RZ_LIB_VERSION(rz_syscall);
RZ_API RzSyscall *rz_syscall_ref(RzSyscall *sc) {
sc->refs++;
return sc;
}
/**
* \brief Creates a new RzSysregItem type
*
* \param name Name of the SysregItem (sysregitem->name)
*/
RZ_API RZ_OWN RzSysregItem *rz_sysreg_item_new(RZ_NULLABLE const char *name) {
RzSysregItem *sysregitem = RZ_NEW0(RzSysregItem);
if (!sysregitem) {
return NULL;
}
sysregitem->comment = NULL;
sysregitem->type = NULL;
sysregitem->name = rz_str_dup(name);
return sysregitem;
}
/**
* \brief Creates a new RzSysregDB type
*/
RZ_API RzSysregsDB *rz_sysregs_db_new() {
RzSysregsDB *sysregdb = RZ_NEW0(RzSysregsDB);
if (!sysregdb) {
return NULL;
}
sysregdb->port = ht_up_new(NULL, (HtUPFreeValue)rz_sysreg_item_free);
if (!sysregdb->port) {
free(sysregdb);
return NULL;
}
return sysregdb;
}
/**
* \brief Frees a RzSysregDB type
*/
RZ_API void rz_sysregs_db_free(RzSysregsDB *sysregdb) {
if (!sysregdb) {
return;
}
ht_up_free(sysregdb->port);
free(sysregdb);
}
/**
* \brief Creates a new RzSyscall type
*/
RZ_API RzSyscall *rz_syscall_new(void) {
RzSyscall *rs = RZ_NEW0(RzSyscall);
if (rs) {
rs->srdb = rz_sysregs_db_new(); // sysregs database
rs->db = sdb_new0();
}
return rs;
}
/**
* \brief Frees an RzSyscall type
*/
RZ_API void rz_syscall_free(RzSyscall *s) {
if (s) {
if (s->refs > 0) {
s->refs--;
return;
}
sdb_free(s->db);
free(s->os);
free(s->cpu);
free(s->arch);
rz_sysregs_db_free(s->srdb);
free(s);
}
}
/**
* \brief Frees an RzSysregItem type
*/
RZ_API void rz_sysreg_item_free(RzSysregItem *s) {
if (!s) {
return;
}
free(s->comment);
free(s->name);
free(s->type);
free(s);
}
static bool load_sdb(Sdb **db, RZ_BORROW RZ_NONNULL RzPath *sys_path, const char *name) {
rz_return_val_if_fail(db && sys_path, false);
char *sdb_path = rz_path_system(sys_path, RZ_SDB);
if (!sdb_path) {
return false;
}
char *file_name = rz_str_newf("%s.sdb", name);
char *file = rz_file_path_join(sdb_path, file_name);
free(file_name);
free(sdb_path);
if (rz_file_exists(file)) {
if (*db) {
sdb_reset(*db);
sdb_open(*db, file);
} else {
*db = sdb_new(0, file, 0);
}
free(file);
return true;
}
free(file);
return false;
}
static inline bool syscall_reload_needed(RzSyscall *s, const char *os, const char *arch, int bits) {
if (!s->os || strcmp(s->os, os)) {
return true;
}
if (!s->arch || strcmp(s->arch, arch)) {
return true;
}
return s->bits != bits;
}
static inline bool sysregs_reload_needed(RzSyscall *s, const char *arch, int bits, const char *cpu) {
if (!s->arch || strcmp(s->arch, arch)) {
return true;
}
if (s->bits != bits) {
return true;
}
return !s->cpu || strcmp(s->cpu, cpu);
}
static bool sdb_load_sysregs(RzSysregsDB *sysregdb, Sdb *sdb) {
rz_return_val_if_fail(sysregdb && sdb, false);
void **iter;
RzPVector *items = sdb_get_items(sdb, false);
rz_pvector_foreach (items, iter) {
SdbKv *kv = *iter;
if (!strcmp(sdbkv_value(kv), "mmio") || !strcmp(sdbkv_value(kv), "reg")) {
const char *name = sdbkv_key(kv);
RzSysregItem *sysregitem = rz_sysreg_item_new(name);
char *argument_key = rz_str_newf("%s.address", name);
if (!argument_key) {
rz_sysreg_item_free(sysregitem);
return false;
}
ut64 address = sdb_num_get(sdb, argument_key);
free(argument_key);
if (!address) {
rz_sysreg_item_free(sysregitem);
return false;
}
argument_key = rz_str_newf("%s.comment", name);
char *comment = sdb_get(sdb, argument_key);
free(argument_key);
sysregitem->type = sdbkv_dup_value(kv);
sysregitem->comment = comment;
ht_up_insert(sysregdb->port, address, sysregitem);
}
}
rz_pvector_free(items);
return true;
}
static bool sdb_load_by_path(RZ_NONNULL RzSysregsDB *sysregdb, const char *path) {
Sdb *db = sdb_new(0, path, 0);
bool result = sdb_load_sysregs(sysregdb, db);
sdb_close(db);
sdb_free(db);
return result;
}
/**
* \brief Loads the RzSysregDB from the path to the hashtable
*
* \param s reference to RzSysregDB
* \param path reference to path of the SDB file
*/
RZ_API bool rz_sysreg_load_sdb(RzSysregsDB *sysregdb, const char *path) {
if (!rz_file_exists(path) || !sysregdb) {
return false;
}
return sdb_load_by_path(sysregdb, path);
}
/**
* \brief Sets the architecture for sysregs during analysis and
* loads up the sysregs SDB database
*
* \param s reference to RzSyscall
* \param arch reference to value of asm.arch
* \param regs_dir path where registers SDB files are located
*/
RZ_API bool rz_sysreg_set_arch(RzSyscall *s, RZ_NONNULL const char *arch, RZ_NONNULL const char *regs_dir) {
rz_return_val_if_fail(s && regs_dir && arch, false);
char buf[60];
char *path = rz_file_path_join(regs_dir, rz_strf(buf, "%s-%s-%d.sdb", arch, s->cpu, s->bits));
if (!path) {
return true;
}
rz_sysregs_db_free(s->srdb);
s->srdb = rz_sysregs_db_new();
if (!rz_sysreg_load_sdb(s->srdb, path)) {
free(path);
rz_sysregs_db_free(s->srdb);
s->srdb = rz_sysregs_db_new();
return false;
}
free(path);
return true;
}
// TODO: should be renamed to rz_syscall_use();
RZ_API bool rz_syscall_setup(RzSyscall *s, RZ_BORROW RZ_NONNULL RzPath *sys_path, const char *arch, int bits, const char *cpu, const char *os) {
rz_return_val_if_fail(sys_path, false);
bool syscall_changed, sysregs_changed;
if (!os || !*os) {
os = RZ_SYS_OS;
}
if (!arch) {
arch = RZ_SYS_ARCH;
}
if (!cpu) {
cpu = arch;
}
syscall_changed = syscall_reload_needed(s, os, arch, bits);
sysregs_changed = sysregs_reload_needed(s, arch, bits, cpu);
free(s->os);
s->os = rz_str_dup(os);
free(s->cpu);
s->cpu = rz_str_dup(cpu);
free(s->arch);
s->arch = rz_str_dup(arch);
s->bits = bits;
if (!strcmp(os, "any")) { // ignored
return true;
} else if (!strcmp(os, "darwin") || !strcmp(os, "osx") || !strcmp(os, "macos")) {
os = "darwin";
}
if (syscall_changed) {
char *dbName = rz_str_newf(RZ_JOIN_2_PATHS("syscall", "%s-%s-%d"),
os, arch, bits);
if (dbName) {
if (!load_sdb(&s->db, sys_path, dbName)) {
sdb_free(s->db);
s->db = NULL;
}
free(dbName);
}
}
if (sysregs_changed) {
char *regs_dir = rz_path_system(sys_path, RZ_SDB_REG);
if (!regs_dir) {
free(regs_dir);
return false;
}
rz_sysreg_set_arch(s, arch, regs_dir);
free(regs_dir);
}
if (s->fd) {
fclose(s->fd);
s->fd = NULL;
}
return true;
}
RZ_API RzSyscallItem *rz_syscall_item_new_from_string(const char *name, const char *s) {
RzSyscallItem *si;
char *o;
if (!name || !s) {
return NULL;
}
o = rz_str_dup(s);
int cols = rz_str_split(o, ',');
if (cols < 3) {
free(o);
return NULL;
}
si = RZ_NEW0(RzSyscallItem);
if (!si) {
free(o);
return NULL;
}
si->name = rz_str_dup(name);
si->swi = (int)rz_num_get(NULL, rz_str_word_get0(o, 0));
si->num = (int)rz_num_get(NULL, rz_str_word_get0(o, 1));
si->args = (int)rz_num_get(NULL, rz_str_word_get0(o, 2));
si->sargs = calloc(si->args + 1, sizeof(char));
if (!si->sargs) {
free(si);
free(o);
return NULL;
}
if (cols > 3) {
strncpy(si->sargs, rz_str_word_get0(o, 3), si->args);
}
free(o);
return si;
}
RZ_API void rz_syscall_item_free(RzSyscallItem *si) {
if (!si) {
return;
}
free(si->name);
free(si->sargs);
free(si);
}
static int getswi(RzSyscall *s, int swi) {
if (s && swi == -1) {
return rz_syscall_get_swi(s);
}
return swi;
}
RZ_API int rz_syscall_get_swi(RzSyscall *s) {
return (int)sdb_num_get(s->db, "_");
}
RZ_API RzSyscallItem *rz_syscall_get(RzSyscall *s, int num, int swi) {
rz_return_val_if_fail(s, NULL);
if (!s->db) {
return NULL;
}
const char *ret, *ret2, *key;
char tmpbuf[64] = { 0 };
swi = getswi(s, swi);
if (swi < 16) {
key = rz_strf(tmpbuf, "%d.%d", swi, num);
} else {
key = rz_strf(tmpbuf, "0x%02x.%d", swi, num);
}
ret = sdb_const_get(s->db, key);
if (!ret) {
key = rz_strf(tmpbuf, "0x%02x.0x%02x", swi, num); // Workaround until Syscall SDB is fixed
ret = sdb_const_get(s->db, key);
if (!ret) {
key = rz_strf(tmpbuf, "0x%02x.%d", num, swi); // Workaround until Syscall SDB is fixed
ret = sdb_const_get(s->db, key);
if (!ret) {
return NULL;
}
}
}
ret2 = sdb_const_get(s->db, ret);
if (!ret2) {
return NULL;
}
return rz_syscall_item_new_from_string(ret, ret2);
}
/**
* \brief Retrieves the syscall number for a given syscall name.
*
* \param s Reference to RzSyscall instance containing the syscall database.
* \param str Syscall name whose number should be retrieved.
* \param num Reference where the resolved syscall number will be stored.
*
* \return True, if syscall is successfully fetched from the db, false otherwise.
*/
RZ_API bool rz_syscall_get_num(RZ_NONNULL RZ_BORROW RzSyscall *s, RZ_NONNULL const char *str, RZ_OUT RZ_NULLABLE int *num) {
rz_return_val_if_fail(s && str && num, false);
if (!s->db) {
return -1;
}
const char *v = sdb_const_get(s->db, "_");
int idx = (v && atoi(v) >= 0) ? 1 : 0;
ut64 sn = 0;
if (!sdb_array_get_num(s->db, str, idx, &sn)) {
return false;
}
*num = (int)sn;
return true;
}
RZ_API const char *rz_syscall_get_i(RzSyscall *s, int num, int swi) {
rz_return_val_if_fail(s, NULL);
if (!s->db) {
return NULL;
}
char foo[32];
swi = getswi(s, swi);
snprintf(foo, sizeof(foo), "0x%x.%d", swi, num);
return sdb_const_get(s->db, foo);
}
static bool callback_list(void *u, const SdbKv *kv) {
RzList *list = (RzList *)u;
if (!strchr(sdbkv_key(kv), '.')) {
RzSyscallItem *si = rz_syscall_item_new_from_string(sdbkv_key(kv), sdbkv_value(kv));
if (!si) {
return true;
}
if (!strchr(si->name, '.')) {
rz_list_append(list, si);
} else {
rz_syscall_item_free(si);
}
}
return true; // continue loop
}
RZ_API RzList /*<RzSyscallItem *>*/ *rz_syscall_list(RzSyscall *s) {
rz_return_val_if_fail(s, NULL);
if (!s->db) {
return NULL;
}
RzList *list = rz_list_newf((RzListFree)rz_syscall_item_free);
sdb_foreach(s->db, callback_list, list);
return list;
}
/**
* \brief Searches the sysregs SDB database inside librz/reg/d for the type (mmio/reg) and
* returns its value as a string
*
* \param s reference to RzSyscall
* \param type reference to the type of key: mmio/reg
* \param port reference to the io port
*/
RZ_API const char *rz_sysreg_get(RzSyscall *s, const char *type, ut64 port) {
rz_return_val_if_fail(s, NULL);
if (!s->srdb) {
return NULL;
}
RzSysregItem *item = ht_up_find(s->srdb->port, port, NULL);
if (item && !strcmp(item->type, type)) {
return item->comment;
}
return NULL;
}