rizin/librz/analysis/platform_profile.c
2022-06-22 17:22:13 +02:00

246 lines
6.6 KiB
C

// SPDX-FileCopyrightText: 2021 RizinOrg <info@rizin.re>
// SPDX-License-Identifier: LGPL-3.0-only
#include <rz_platform.h>
#include <string.h>
/**
* \brief Frees an RzPlatformProfile type
*
* Frees the hashtables used for MMIO and extended
* registers
*/
RZ_API void rz_platform_profile_free(RzPlatformProfile *p) {
if (!p) {
return;
}
ht_up_free(p->registers_mmio);
ht_up_free(p->registers_extended);
free(p);
}
static void free_mmio_kv(HtUPKv *kv) {
free(kv->value);
}
/**
* \brief Creates a new RzPlatformProfile type
*/
RZ_API RZ_OWN RzPlatformProfile *rz_platform_profile_new() {
RzPlatformProfile *profile = RZ_NEW0(RzPlatformProfile);
if (!profile) {
return NULL;
}
profile->registers_mmio = ht_up_new((HtUPDupValue)strdup, free_mmio_kv, (HtUPCalcSizeV)strlen);
if (!profile->registers_mmio) {
free(profile);
return NULL;
}
profile->registers_extended = ht_up_new((HtUPDupValue)strdup, free_mmio_kv, (HtUPCalcSizeV)strlen);
if (!profile->registers_extended) {
ht_up_free(profile->registers_mmio);
free(profile);
return NULL;
}
return profile;
}
/**
* \brief Creates a new RzPlatformTarget type
*/
RZ_API RZ_OWN RzPlatformTarget *rz_platform_target_new() {
RzPlatformTarget *profile = RZ_NEW0(RzPlatformTarget);
if (!profile) {
return NULL;
}
profile->profile = rz_platform_profile_new();
if (!profile->profile) {
free(profile);
return NULL;
}
return profile;
}
/**
* \brief Frees an RzPlatformTarget type
*
* Frees the pointer to the SDB and the RzPlatformProfile
*/
RZ_API void rz_platform_target_free(RzPlatformTarget *t) {
if (!t) {
return;
}
rz_platform_profile_free(t->profile);
free(t->cpu);
free(t->arch);
free(t);
}
/**
* \brief Resolves an address and returns the linked mmio
*/
RZ_API RZ_BORROW const char *rz_platform_profile_resolve_mmio(RZ_NONNULL RzPlatformProfile *profile, ut64 address) {
rz_return_val_if_fail(profile, NULL);
return ht_up_find(profile->registers_mmio, (ut64)address, NULL);
}
/**
* \brief Resolves an address and returns the linked extended register
*/
RZ_API RZ_BORROW const char *rz_platform_profile_resolve_extended_register(RZ_NONNULL RzPlatformProfile *profile, ut64 address) {
rz_return_val_if_fail(profile, NULL);
return ht_up_find(profile->registers_extended, (ut64)address, NULL);
}
static inline bool cpu_reload_needed(RzPlatformTarget *c, const char *cpu, const char *arch) {
if (!c->arch || strcmp(c->arch, arch)) {
return true;
}
return !c->cpu || strcmp(c->cpu, cpu);
}
static bool sdb_load_arch_profile(RzPlatformTarget *t, Sdb *sdb) {
rz_return_val_if_fail(t && sdb, false);
SdbKv *kv;
SdbListIter *iter;
RzPlatformProfile *c = rz_platform_profile_new();
if (!c) {
return false;
}
SdbList *l = sdb_foreach_list(sdb, false);
ls_foreach (l, iter, kv) {
if (!strcmp(sdbkv_key(kv), "PC")) {
c->pc = rz_num_math(NULL, sdbkv_value(kv));
} else if (!strcmp(sdbkv_key(kv), "EEPROM_SIZE")) {
c->eeprom_size = rz_num_math(NULL, sdbkv_value(kv));
} else if (!strcmp(sdbkv_key(kv), "IO_SIZE")) {
c->io_size = rz_num_math(NULL, sdbkv_value(kv));
} else if (!strcmp(sdbkv_key(kv), "SRAM_START")) {
c->sram_start = rz_num_math(NULL, sdbkv_value(kv));
} else if (!strcmp(sdbkv_key(kv), "SRAM_SIZE")) {
c->sram_size = rz_num_math(NULL, sdbkv_value(kv));
} else if (!strcmp(sdbkv_key(kv), "PAGE_SIZE")) {
c->page_size = rz_num_math(NULL, sdbkv_value(kv));
} else if (!strcmp(sdbkv_key(kv), "ROM_SIZE")) {
c->rom_size = rz_num_math(NULL, sdbkv_value(kv));
} else if (!strcmp(sdbkv_key(kv), "ROM_ADDRESS")) {
c->rom_address = rz_num_math(NULL, sdbkv_value(kv));
} else if (!strcmp(sdbkv_key(kv), "RAM_SIZE")) {
c->ram_size = rz_num_math(NULL, sdbkv_value(kv));
}
if (!strcmp(sdbkv_value(kv), "io")) {
char *io_name = sdbkv_key(kv);
char *argument_key = rz_str_newf("%s.address", io_name);
ut64 io_address = sdb_num_get(sdb, argument_key, NULL);
free(argument_key);
ht_up_insert(c->registers_mmio, io_address, io_name);
}
if (!strcmp(sdbkv_value(kv), "ext_io")) {
char *ext_io_name = sdbkv_key(kv);
char *argument_key = rz_str_newf("%s.address", ext_io_name);
ut64 ext_io_address = sdb_num_get(sdb, argument_key, NULL);
free(argument_key);
ht_up_insert(c->registers_extended, ext_io_address, ext_io_name);
}
}
ls_free(l);
rz_platform_profile_free(t->profile);
t->profile = c;
return true;
}
static bool sdb_load_arch_profile_by_path(RZ_NONNULL RzPlatformTarget *t, const char *path) {
Sdb *db = sdb_new(0, path, 0);
bool result = sdb_load_arch_profile(t, db);
sdb_close(db);
sdb_free(db);
return result;
}
/**
* \brief Loads the contents of the CPU Profile to the RzPlatformProfile
*
* \param t reference to RzPlatformTarget
* \param path reference to path of the SDB file
*/
RZ_API bool rz_platform_load_profile_sdb(RzPlatformTarget *t, const char *path) {
if (!rz_file_exists(path)) {
return false;
}
return sdb_load_arch_profile_by_path(t, path);
}
static bool is_cpu_valid(const char *cpu_dir, const char *cpu) {
RzList *files = rz_sys_dir(cpu_dir);
if (!files) {
return false;
}
RzListIter *it;
char *filename = NULL;
char *arch_cpu = NULL;
rz_list_foreach (files, it, filename) {
char *cpu_name = NULL;
if (!strcmp(filename, "..") || !strcmp(filename, "..")) {
continue;
}
arch_cpu = rz_str_ndup(filename, strlen(filename) - 4);
if (!arch_cpu) {
continue;
}
cpu_name = strchr(arch_cpu, '-');
if (!cpu_name) {
free(arch_cpu);
continue;
}
cpu_name[0] = '\0';
if (!strcmp(cpu_name + 1, cpu)) {
rz_list_free(files);
free(arch_cpu);
return true;
}
free(arch_cpu);
}
rz_list_free(files);
return false;
}
/**
* \brief Initializes RzPlatformProfile by loading the path to the SDB file
* of the CPU profile
*
* \param t reference to RzPlatformTarget
* \param cpu reference to the selected CPU (value of `asm.cpu`)
* \param arch reference to the selected architecture (value of `asm.arch`)
* \param cpus_dir reference to the directory containing cpu files
*/
RZ_API bool rz_platform_profiles_init(RzPlatformTarget *t, const char *cpu, const char *arch, const char *cpus_dir) {
if (!cpu_reload_needed(t, cpu, arch)) {
return false;
}
if (!cpus_dir || !arch || !cpu) {
return false;
}
char buf[50];
char *path = rz_file_path_join(cpus_dir, rz_strf(buf, "%s-%s.sdb", arch, cpu));
if (!path) {
return false;
}
if (!is_cpu_valid(cpus_dir, cpu)) {
if (!strcmp(arch, "avr")) {
free(path);
path = rz_file_path_join(cpus_dir, "avr-ATmega8.sdb");
}
}
free(t->cpu);
free(t->arch);
t->cpu = strdup(cpu);
t->arch = strdup(arch);
rz_platform_load_profile_sdb(t, path);
free(path);
return true;
}