482 lines
11 KiB
C
482 lines
11 KiB
C
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include <rz_util.h>
|
|
#include <rz_lib.h>
|
|
|
|
RZ_LIB_VERSION(rz_lib);
|
|
|
|
/* TODO: avoid globals */
|
|
#define IFDBG if (__has_debug)
|
|
static bool __has_debug = false;
|
|
|
|
/* XXX : this must be registered in runtime */
|
|
static const char *rz_lib_types[] = {
|
|
"io", "dbg", "lang", "asm", "analysis", "parse", "bin", "bin_xtr", "bin_ldr",
|
|
"bp", "syscall", "fastcall", "crypto", "core", "egg", NULL
|
|
};
|
|
|
|
static const char *__lib_types_get(int idx) {
|
|
if (idx < 0 || idx > RZ_LIB_TYPE_LAST - 1) {
|
|
return "unk";
|
|
}
|
|
return rz_lib_types[idx];
|
|
}
|
|
|
|
RZ_API int rz_lib_types_get_i(const char *str) {
|
|
int i;
|
|
for (i = 0; rz_lib_types[i]; i++) {
|
|
if (!strcmp(str, rz_lib_types[i])) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
RZ_API void *rz_lib_dl_open(const char *libname) {
|
|
void *ret = NULL;
|
|
#if WANT_DYLINK
|
|
#if __UNIX__
|
|
if (libname) {
|
|
ret = dlopen(libname, RTLD_GLOBAL | RTLD_LAZY);
|
|
} else {
|
|
ret = dlopen(NULL, RTLD_NOW);
|
|
}
|
|
if (!ret && __has_debug) {
|
|
eprintf("rz_lib_dl_open: error: %s (%s)\n", libname, dlerror());
|
|
}
|
|
#elif __WINDOWS__
|
|
LPTSTR libname_;
|
|
if (libname && *libname) {
|
|
libname_ = rz_sys_conv_utf8_to_win(libname);
|
|
} else {
|
|
libname_ = calloc(MAX_PATH, sizeof(TCHAR));
|
|
if (!libname_) {
|
|
RZ_LOG_ERROR("lib/rz_lib_dl_open: Failed to allocate memory.\n");
|
|
return NULL;
|
|
}
|
|
if (!GetModuleFileName(NULL, libname_, MAX_PATH)) {
|
|
libname_[0] = '\0';
|
|
}
|
|
}
|
|
ret = LoadLibrary(libname_);
|
|
free(libname_);
|
|
if (!ret && __has_debug) {
|
|
eprintf("rz_lib_dl_open: error: %s\n", libname);
|
|
}
|
|
#endif
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
RZ_API void *rz_lib_dl_sym(void *handler, const char *name) {
|
|
#if WANT_DYLINK
|
|
#if __UNIX__
|
|
return dlsym(handler, name);
|
|
#elif __WINDOWS__
|
|
return GetProcAddress(handler, name);
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
RZ_API int rz_lib_dl_close(void *handler) {
|
|
#if __UNIX__
|
|
return dlclose(handler);
|
|
#else
|
|
return handler ? 0 : -1;
|
|
#endif
|
|
}
|
|
|
|
RZ_API char *rz_lib_path(const char *libname) {
|
|
#if __WINDOWS__
|
|
char *tmp = rz_str_newf("%s." RZ_LIB_EXT, libname);
|
|
if (!tmp) {
|
|
return NULL;
|
|
}
|
|
WCHAR *name = rz_utf8_to_utf16(tmp);
|
|
free(tmp);
|
|
WCHAR *path = NULL;
|
|
if (!name) {
|
|
goto err;
|
|
}
|
|
|
|
int count;
|
|
if (!(count = SearchPathW(NULL, name, NULL, 0, NULL, NULL))) {
|
|
rz_sys_perror("SearchPath");
|
|
goto err;
|
|
}
|
|
path = malloc(count * sizeof(WCHAR));
|
|
if (!path) {
|
|
goto err;
|
|
}
|
|
if (!(count = SearchPathW(NULL, name, NULL, count, path, NULL))) {
|
|
RZ_FREE(path);
|
|
rz_sys_perror("SearchPath");
|
|
goto err;
|
|
}
|
|
tmp = rz_utf16_to_utf8(path);
|
|
free(name);
|
|
free(path);
|
|
return tmp;
|
|
err:
|
|
free(name);
|
|
return NULL;
|
|
#else
|
|
#if __APPLE__
|
|
char *env = rz_sys_getenv("DYLD_LIBRARY_PATH");
|
|
env = rz_str_append(env, ":/lib:/usr/lib:/usr/local/lib");
|
|
#elif __UNIX__
|
|
char *env = rz_sys_getenv("LD_LIBRARY_PATH");
|
|
env = rz_str_append(env, ":/lib:/usr/lib:/usr/local/lib");
|
|
#endif
|
|
if (!env) {
|
|
env = strdup(".");
|
|
}
|
|
char *next, *path0 = env;
|
|
do {
|
|
next = strchr(path0, ':');
|
|
if (next) {
|
|
*next = 0;
|
|
}
|
|
char *libpath = rz_str_newf("%s/%s." RZ_LIB_EXT, path0, libname);
|
|
if (rz_file_exists(libpath)) {
|
|
free(env);
|
|
return libpath;
|
|
}
|
|
free(libpath);
|
|
path0 = next + 1;
|
|
} while (next);
|
|
free(env);
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
RZ_API RzLib *rz_lib_new(const char *symname, const char *symnamefunc) {
|
|
RzLib *lib = RZ_NEW(RzLib);
|
|
if (lib) {
|
|
__has_debug = rz_sys_getenv_asbool("RZ_DEBUG");
|
|
lib->handlers = rz_list_newf(free);
|
|
lib->plugins = rz_list_newf(free);
|
|
lib->symname = strdup(symname ? symname : RZ_LIB_SYMNAME);
|
|
lib->symnamefunc = strdup(symnamefunc ? symnamefunc : RZ_LIB_SYMFUNC);
|
|
}
|
|
return lib;
|
|
}
|
|
|
|
RZ_API void rz_lib_free(RzLib *lib) {
|
|
if (lib) {
|
|
rz_lib_close(lib, NULL);
|
|
rz_list_free(lib->handlers);
|
|
rz_list_free(lib->plugins);
|
|
free(lib->symname);
|
|
free(lib->symnamefunc);
|
|
free(lib);
|
|
}
|
|
}
|
|
|
|
static bool __lib_dl_check_filename(const char *file) {
|
|
return rz_str_endswith(file, "." RZ_LIB_EXT);
|
|
}
|
|
|
|
RZ_API int rz_lib_run_handler(RzLib *lib, RzLibPlugin *plugin, RzLibStruct *symbol) {
|
|
RzLibHandler *h = plugin->handler;
|
|
if (h && h->constructor) {
|
|
IFDBG eprintf("PLUGIN LOADED %p fcn %p\n", h, h->constructor);
|
|
return h->constructor(plugin, h->user, symbol->data);
|
|
}
|
|
IFDBG eprintf("Cannot find plugin constructor\n");
|
|
return -1;
|
|
}
|
|
|
|
RZ_API RzLibHandler *rz_lib_get_handler(RzLib *lib, int type) {
|
|
RzLibHandler *h;
|
|
RzListIter *iter;
|
|
rz_list_foreach (lib->handlers, iter, h) {
|
|
if (h->type == type) {
|
|
return h;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
RZ_API int rz_lib_close(RzLib *lib, const char *file) {
|
|
RzLibPlugin *p;
|
|
RzListIter *iter, *iter2;
|
|
rz_list_foreach_safe (lib->plugins, iter, iter2, p) {
|
|
if ((!file || !strcmp(file, p->file))) {
|
|
int ret = 0;
|
|
if (p->handler && p->handler->destructor) {
|
|
ret = p->handler->destructor(p, p->handler->user, p->data);
|
|
}
|
|
if (p->free) {
|
|
p->free(p->data);
|
|
}
|
|
free(p->file);
|
|
rz_list_delete(lib->plugins, iter);
|
|
if (file != NULL) {
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
if (!file) {
|
|
return 0;
|
|
}
|
|
// delete similar plugin name
|
|
rz_list_foreach (lib->plugins, iter, p) {
|
|
if (strstr(p->file, file)) {
|
|
int ret = 0;
|
|
if (p->handler && p->handler->destructor) {
|
|
ret = p->handler->destructor(p,
|
|
p->handler->user, p->data);
|
|
}
|
|
eprintf("Unloaded %s\n", p->file);
|
|
free(p->file);
|
|
rz_list_delete(lib->plugins, iter);
|
|
return ret;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static bool __already_loaded(RzLib *lib, const char *file) {
|
|
const char *fileName = rz_str_rstr(file, RZ_SYS_DIR);
|
|
RzLibPlugin *p;
|
|
RzListIter *iter;
|
|
if (fileName) {
|
|
rz_list_foreach (lib->plugins, iter, p) {
|
|
const char *pFileName = rz_str_rstr(p->file, RZ_SYS_DIR);
|
|
if (pFileName && !strcmp(fileName, pFileName)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
RZ_API int rz_lib_open(RzLib *lib, const char *file) {
|
|
/* ignored by filename */
|
|
if (!__lib_dl_check_filename(file)) {
|
|
eprintf("Invalid library extension: %s\n", file);
|
|
return -1;
|
|
}
|
|
|
|
if (__already_loaded(lib, file)) {
|
|
eprintf("Not loading library because it has already been loaded from somewhere else: '%s'\n", file);
|
|
return -1;
|
|
}
|
|
|
|
void *handler = rz_lib_dl_open(file);
|
|
if (!handler) {
|
|
IFDBG eprintf("Cannot open library: '%s'\n", file);
|
|
return -1;
|
|
}
|
|
|
|
RzLibStructFunc strf = (RzLibStructFunc)rz_lib_dl_sym(handler, lib->symnamefunc);
|
|
RzLibStruct *stru = NULL;
|
|
if (strf) {
|
|
stru = strf();
|
|
}
|
|
if (!stru) {
|
|
stru = (RzLibStruct *)rz_lib_dl_sym(handler, lib->symname);
|
|
}
|
|
if (!stru) {
|
|
IFDBG eprintf("Cannot find symbol '%s' in library '%s'\n",
|
|
lib->symname, file);
|
|
rz_lib_dl_close(handler);
|
|
return -1;
|
|
}
|
|
|
|
int res = rz_lib_open_ptr(lib, file, handler, stru);
|
|
if (strf) {
|
|
free(stru);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static char *major_minor(const char *s) {
|
|
char *a = strdup(s);
|
|
char *p = strchr(a, '.');
|
|
if (p) {
|
|
p = strchr(p + 1, '.');
|
|
if (p) {
|
|
*p = 0;
|
|
}
|
|
}
|
|
return a;
|
|
}
|
|
|
|
RZ_API int rz_lib_open_ptr(RzLib *lib, const char *file, void *handler, RzLibStruct *stru) {
|
|
rz_return_val_if_fail(lib && file && stru, -1);
|
|
if (stru->version) {
|
|
char *mm0 = major_minor(stru->version);
|
|
char *mm1 = major_minor(RZ_VERSION);
|
|
bool mismatch = strcmp(mm0, mm1);
|
|
free(mm0);
|
|
free(mm1);
|
|
if (mismatch) {
|
|
eprintf("Module version mismatch %s (%s) vs (%s)\n",
|
|
file, stru->version, RZ_VERSION);
|
|
if (stru->pkgname) {
|
|
const char *dot = strchr(stru->version, '.');
|
|
int major = atoi(stru->version);
|
|
int minor = dot ? atoi(dot + 1) : 0;
|
|
// The pkgname member was introduced in 4.2.0
|
|
if (major > 4 || (major == 4 && minor >= 2)) {
|
|
printf("rz_pm -ci %s\n", stru->pkgname);
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
RzLibPlugin *p = RZ_NEW0(RzLibPlugin);
|
|
p->type = stru->type;
|
|
p->data = stru->data;
|
|
p->file = strdup(file);
|
|
p->dl_handler = handler;
|
|
p->handler = rz_lib_get_handler(lib, p->type);
|
|
p->free = stru->free;
|
|
|
|
int ret = rz_lib_run_handler(lib, p, stru);
|
|
if (ret == -1) {
|
|
IFDBG eprintf("Library handler has failed for '%s'\n", file);
|
|
free(p->file);
|
|
free(p);
|
|
rz_lib_dl_close(handler);
|
|
} else {
|
|
rz_list_append(lib->plugins, p);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
RZ_API bool rz_lib_opendir(RzLib *lib, const char *path) {
|
|
#if WANT_DYLINK
|
|
rz_return_val_if_fail(lib && path, false);
|
|
#if __WINDOWS__
|
|
wchar_t file[1024];
|
|
WIN32_FIND_DATAW dir;
|
|
HANDLE fh;
|
|
wchar_t directory[MAX_PATH];
|
|
wchar_t *wcpath;
|
|
char *wctocbuff;
|
|
#else
|
|
char file[1024];
|
|
struct dirent *de;
|
|
DIR *dh;
|
|
#endif
|
|
#ifdef RZ_LIBR_PLUGINS
|
|
if (!path) {
|
|
path = RZ_LIBR_PLUGINS;
|
|
}
|
|
#endif
|
|
if (!path) {
|
|
return false;
|
|
}
|
|
#if __WINDOWS__
|
|
wcpath = rz_utf8_to_utf16(path);
|
|
if (!wcpath) {
|
|
return false;
|
|
}
|
|
swprintf(directory, _countof(directory), L"%ls\\*.*", wcpath);
|
|
fh = FindFirstFileW(directory, &dir);
|
|
if (fh == INVALID_HANDLE_VALUE) {
|
|
IFDBG eprintf("Cannot open directory %ls\n", wcpath);
|
|
free(wcpath);
|
|
return false;
|
|
}
|
|
do {
|
|
swprintf(file, _countof(file), L"%ls/%ls", wcpath, dir.cFileName);
|
|
wctocbuff = rz_utf16_to_utf8(file);
|
|
if (wctocbuff) {
|
|
if (__lib_dl_check_filename(wctocbuff)) {
|
|
rz_lib_open(lib, wctocbuff);
|
|
} else {
|
|
IFDBG eprintf("Cannot open %ls\n", dir.cFileName);
|
|
}
|
|
free(wctocbuff);
|
|
}
|
|
} while (FindNextFileW(fh, &dir));
|
|
FindClose(fh);
|
|
free(wcpath);
|
|
#else
|
|
dh = opendir(path);
|
|
if (!dh) {
|
|
IFDBG eprintf("Cannot open directory '%s'\n", path);
|
|
return false;
|
|
}
|
|
while ((de = (struct dirent *)readdir(dh))) {
|
|
if (de->d_name[0] == '.' || strstr(de->d_name, ".dSYM")) {
|
|
continue;
|
|
}
|
|
snprintf(file, sizeof(file), "%s/%s", path, de->d_name);
|
|
if (__lib_dl_check_filename(file)) {
|
|
IFDBG eprintf("Loading %s\n", file);
|
|
rz_lib_open(lib, file);
|
|
} else {
|
|
IFDBG eprintf("Cannot open %s\n", file);
|
|
}
|
|
}
|
|
closedir(dh);
|
|
#endif
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
RZ_API bool rz_lib_add_handler(RzLib *lib,
|
|
int type, const char *desc,
|
|
int (*cb)(RzLibPlugin *, void *, void *), /* constructor */
|
|
int (*dt)(RzLibPlugin *, void *, void *), /* destructor */
|
|
void *user) {
|
|
RzLibHandler *h;
|
|
RzListIter *iter;
|
|
RzLibHandler *handler = NULL;
|
|
|
|
rz_list_foreach (lib->handlers, iter, h) {
|
|
if (type == h->type) {
|
|
IFDBG eprintf("Redefining library handler constructor for %d\n", type);
|
|
handler = h;
|
|
break;
|
|
}
|
|
}
|
|
if (!handler) {
|
|
handler = RZ_NEW(RzLibHandler);
|
|
if (!handler) {
|
|
return false;
|
|
}
|
|
handler->type = type;
|
|
rz_list_append(lib->handlers, handler);
|
|
}
|
|
strncpy(handler->desc, desc, sizeof(handler->desc) - 1);
|
|
handler->user = user;
|
|
handler->constructor = cb;
|
|
handler->destructor = dt;
|
|
|
|
return true;
|
|
}
|
|
|
|
RZ_API bool rz_lib_del_handler(RzLib *lib, int type) {
|
|
RzLibHandler *h;
|
|
RzListIter *iter;
|
|
// TODO: remove all handlers for that type? or only one?
|
|
/* No _safe loop necessary because we return immediately after the delete. */
|
|
rz_list_foreach (lib->handlers, iter, h) {
|
|
if (type == h->type) {
|
|
rz_list_delete(lib->handlers, iter);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// TODO _list methods should not exist.. only used in ../core/cmd_log.c: rz_lib_list (core->lib);
|
|
RZ_API void rz_lib_list(RzLib *lib) {
|
|
RzListIter *iter;
|
|
RzLibPlugin *p;
|
|
rz_list_foreach (lib->plugins, iter, p) {
|
|
printf(" %5s %p %s \n", __lib_types_get(p->type),
|
|
p->dl_handler, p->file);
|
|
}
|
|
}
|