mirror of
https://github.com/radareorg/radare2.git
synced 2024-10-07 02:23:58 +00:00
Initial support for RParse r2js plugins ##lang
This commit is contained in:
parent
70b5885545
commit
0614fc6fe1
@ -41,6 +41,7 @@ typedef struct r_parse_t {
|
||||
} RParse; // TODO rename to RAsmParseState
|
||||
|
||||
typedef struct r_parse_plugin_t {
|
||||
// TODO R2_600 Use RPluginMeta instead
|
||||
char *name;
|
||||
char *desc;
|
||||
bool (*init)(RParse *p, void *user); // returns an RAsmParseState*
|
||||
|
@ -31,6 +31,14 @@ typedef struct qjs_arch_plugin_t {
|
||||
// JSValue encode_func;
|
||||
} QjsArchPlugin;
|
||||
|
||||
typedef struct qjs_parse_plugin_t {
|
||||
char *name;
|
||||
RParsePlugin *iop;
|
||||
R_BORROW JSContext *ctx;
|
||||
JSValue fn_parse_js;
|
||||
// JSValue encode_func;
|
||||
} QjsParsePlugin;
|
||||
|
||||
typedef struct qjs_io_plugin_t {
|
||||
char *name;
|
||||
RIOPlugin *iop;
|
||||
@ -44,6 +52,10 @@ typedef struct qjs_io_plugin_t {
|
||||
// JSValue encode_func;
|
||||
} QjsIoPlugin;
|
||||
|
||||
static void parse_plugin_fini(QjsParsePlugin *cp) {
|
||||
free (cp->name);
|
||||
}
|
||||
|
||||
static void core_plugin_fini(QjsCorePlugin *cp) {
|
||||
free (cp->name);
|
||||
}
|
||||
@ -53,6 +65,7 @@ static void arch_plugin_fini(QjsArchPlugin *ap) {
|
||||
free (ap->arch);
|
||||
}
|
||||
|
||||
R_VEC_TYPE_WITH_FINI (RVecParsePlugin, QjsParsePlugin, parse_plugin_fini);
|
||||
R_VEC_TYPE_WITH_FINI (RVecCorePlugin, QjsCorePlugin, core_plugin_fini);
|
||||
R_VEC_TYPE_WITH_FINI (RVecArchPlugin, QjsArchPlugin, arch_plugin_fini);
|
||||
R_VEC_TYPE (RVecIoPlugin, QjsIoPlugin); // R2_590 add finalizer function
|
||||
@ -65,6 +78,7 @@ typedef struct qjs_plugin_manager_t {
|
||||
RVecCorePlugin core_plugins;
|
||||
RVecArchPlugin arch_plugins;
|
||||
RVecIoPlugin io_plugins;
|
||||
RVecParsePlugin parse_plugins;
|
||||
} QjsPluginManager;
|
||||
|
||||
static QjsPluginManager *Gpm = NULL;
|
||||
@ -74,6 +88,7 @@ static bool plugin_manager_init(QjsPluginManager *pm, RCore *core, JSRuntime *rt
|
||||
RVecCorePlugin_init (&pm->core_plugins);
|
||||
RVecArchPlugin_init (&pm->arch_plugins);
|
||||
RVecIoPlugin_init (&pm->io_plugins);
|
||||
RVecParsePlugin_init (&pm->parse_plugins);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -103,16 +118,40 @@ static QjsIoPlugin *plugin_manager_add_io_plugin(QjsPluginManager *pm, const cha
|
||||
return cp;
|
||||
}
|
||||
|
||||
static QjsParsePlugin *plugin_manager_add_parse_plugin(QjsPluginManager *pm, const char *name, JSContext *ctx, RParsePlugin *iop, JSValue func) {
|
||||
r_return_val_if_fail (pm, NULL);
|
||||
|
||||
QjsParsePlugin *cp = RVecParsePlugin_emplace_back (&pm->parse_plugins);
|
||||
if (cp) {
|
||||
cp->name = name? strdup (name): NULL;
|
||||
cp->ctx = ctx;
|
||||
cp->iop = iop;
|
||||
cp->fn_parse_js = func;
|
||||
}
|
||||
return cp;
|
||||
}
|
||||
|
||||
static inline int compare_core_plugin_name(const QjsCorePlugin *cp, const void *data) {
|
||||
const char *name = data;
|
||||
return strcmp (cp->name, name);
|
||||
}
|
||||
|
||||
static inline int compare_parse_plugin_name(const QjsParsePlugin *cp, const void *data) {
|
||||
const char *name = data;
|
||||
return strcmp (cp->name, name);
|
||||
}
|
||||
|
||||
static inline int compare_io_plugin_name(const QjsIoPlugin *cp, const void *data) {
|
||||
const char *name = data;
|
||||
return strcmp (cp->name, name);
|
||||
}
|
||||
|
||||
static QjsParsePlugin *plugin_manager_find_parse_plugin(const QjsPluginManager *pm, const char *name) {
|
||||
r_return_val_if_fail (pm, NULL);
|
||||
|
||||
return RVecParsePlugin_find (&pm->parse_plugins, (void*) name, compare_parse_plugin_name);
|
||||
}
|
||||
|
||||
static QjsCorePlugin *plugin_manager_find_core_plugin(const QjsPluginManager *pm, const char *name) {
|
||||
r_return_val_if_fail (pm, NULL);
|
||||
|
||||
@ -125,6 +164,19 @@ static QjsIoPlugin *plugin_manager_find_io_plugin(const QjsPluginManager *pm, co
|
||||
return RVecIoPlugin_find (&pm->io_plugins, (void*) name, compare_io_plugin_name);
|
||||
}
|
||||
|
||||
static bool plugin_manager_remove_parse_plugin(QjsPluginManager *pm, const char *name) {
|
||||
r_return_val_if_fail (pm, false);
|
||||
|
||||
ut64 index = RVecParsePlugin_find_index (&pm->parse_plugins, (void*) name, compare_parse_plugin_name);
|
||||
if (index != UT64_MAX) {
|
||||
pm->core->lang->cmdf (pm->core, "Lp-%s", name);
|
||||
RVecParsePlugin_remove (&pm->parse_plugins, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool plugin_manager_remove_core_plugin(QjsPluginManager *pm, const char *name) {
|
||||
r_return_val_if_fail (pm, false);
|
||||
|
||||
@ -180,10 +232,12 @@ static bool plugin_manager_remove_plugin(QjsPluginManager *pm, const char *type,
|
||||
r_return_val_if_fail (pm, false);
|
||||
|
||||
if (R_STR_ISNOTEMPTY (type)) {
|
||||
if (!strcmp (type, "parse")) {
|
||||
return plugin_manager_remove_parse_plugin (pm, plugin_id);
|
||||
}
|
||||
if (!strcmp (type, "core")) {
|
||||
return plugin_manager_remove_core_plugin (pm, plugin_id);
|
||||
}
|
||||
|
||||
if (!strcmp (type, "arch")) {
|
||||
return plugin_manager_remove_arch_plugin (pm, plugin_id);
|
||||
}
|
||||
@ -199,6 +253,7 @@ static void plugin_manager_fini (QjsPluginManager *pm) {
|
||||
RVecCorePlugin_fini (&pm->core_plugins);
|
||||
RVecArchPlugin_fini (&pm->arch_plugins);
|
||||
RVecIoPlugin_fini (&pm->io_plugins);
|
||||
RVecParsePlugin_fini (&pm->parse_plugins);
|
||||
// XXX leaks, but calling it causes crash because not all JS objects are freed
|
||||
// JS_FreeRuntime (pm->rt);
|
||||
pm->rt = NULL;
|
||||
@ -207,6 +262,7 @@ static void plugin_manager_fini (QjsPluginManager *pm) {
|
||||
#include "qjs/loader.c"
|
||||
#include "qjs/arch.c"
|
||||
#include "qjs/core.c"
|
||||
#include "qjs/parse.c"
|
||||
#include "qjs/io.c"
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
@ -303,6 +359,8 @@ static JSValue r2plugin(JSContext *ctx, JSValueConst this_val, int argc, JSValue
|
||||
return r2plugin_core_load (ctx, this_val, argc, argv);
|
||||
} else if (!strcmp (n, "arch")) {
|
||||
return r2plugin_arch_load (ctx, this_val, argc, argv);
|
||||
} else if (!strcmp (n, "parse")) {
|
||||
return r2plugin_parse_load (ctx, this_val, argc, argv);
|
||||
} else if (!strcmp (n, "io")) {
|
||||
return r2plugin_io (ctx, this_val, argc, argv);
|
||||
#if 0
|
||||
@ -311,7 +369,7 @@ static JSValue r2plugin(JSContext *ctx, JSValueConst this_val, int argc, JSValue
|
||||
#endif
|
||||
} else {
|
||||
// invalid throw exception here
|
||||
return JS_ThrowRangeError(ctx, "invalid r2plugin type");
|
||||
return JS_ThrowRangeError (ctx, "invalid r2plugin type");
|
||||
}
|
||||
}
|
||||
return JS_NewBool (ctx, false);
|
||||
|
134
libr/lang/p/qjs/parse.c
Normal file
134
libr/lang/p/qjs/parse.c
Normal file
@ -0,0 +1,134 @@
|
||||
#if 0
|
||||
|
||||
# QuickJS RPares plugin example
|
||||
|
||||
```js
|
||||
|
||||
(function() {
|
||||
let { log } = console;
|
||||
|
||||
function parseExample() {
|
||||
function parseCall(input) {
|
||||
return input.replace("sp, -0x60", "LOCALVAR");
|
||||
}
|
||||
return {
|
||||
name: "qjs",
|
||||
desc: "Example QJS RParse plugin (qjs://)",
|
||||
call: parseCall,
|
||||
};
|
||||
}
|
||||
|
||||
r2.plugin("jsparse", parseExample);
|
||||
r2.cmd("-e asm.parser=qjs");
|
||||
r2.cmd("-e asm.pseudo=true");
|
||||
r2.cmd("pd 10");
|
||||
})();
|
||||
|
||||
```
|
||||
|
||||
#endif
|
||||
|
||||
static int qjs_parse(RParse *p, const char *input, char *output) {
|
||||
RCore *core = p->user;
|
||||
QjsPluginManager *pm = R_UNWRAP4 (core, lang, session, plugin_data);
|
||||
|
||||
// Iterate over plugins until one returns "true" (meaning the plugin handled the input)
|
||||
QjsParsePlugin *plugin;
|
||||
R_VEC_FOREACH (&pm->parse_plugins, plugin) {
|
||||
JSContext *ctx = plugin->ctx;
|
||||
JSValueConst args[1] = { JS_NewString (ctx, input) };
|
||||
JSValue res = JS_Call (ctx, plugin->fn_parse_js, JS_UNDEFINED, countof (args), args);
|
||||
if (JS_IsString (res)) {
|
||||
size_t namelen;
|
||||
const char *nameptr = JS_ToCStringLen2 (ctx, &namelen, res, false);
|
||||
if (!nameptr) {
|
||||
R_LOG_WARN ("r2.plugin requires the function to return an object with the `name` field");
|
||||
return false;
|
||||
}
|
||||
strcpy (output, nameptr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static JSValue r2plugin_parse_load(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
JSRuntime *rt = JS_GetRuntime (ctx);
|
||||
QjsPluginManager *pm = JS_GetRuntimeOpaque (rt);
|
||||
|
||||
if (argc != 2) {
|
||||
return JS_ThrowRangeError (ctx, "r2.plugin expects two arguments");
|
||||
}
|
||||
|
||||
JSValueConst args[1] = { JS_NewString (ctx, ""), };
|
||||
|
||||
// check if res is an object
|
||||
JSValue res = JS_Call (ctx, argv[1], JS_UNDEFINED, countof (args), args);
|
||||
if (!JS_IsObject (res)) {
|
||||
return JS_ThrowRangeError (ctx, "r2.plugin function must return an object");
|
||||
}
|
||||
|
||||
JSValue name = JS_GetPropertyStr (ctx, res, "name");
|
||||
size_t namelen;
|
||||
const char *nameptr = JS_ToCStringLen2 (ctx, &namelen, name, false);
|
||||
if (!nameptr) {
|
||||
R_LOG_WARN ("r2.plugin requires the function to return an object with the `name` field");
|
||||
return JS_NewBool (ctx, false);
|
||||
}
|
||||
|
||||
JSValue fn_parse_js = JS_GetPropertyStr (ctx, res, "parse");
|
||||
|
||||
if (!JS_IsFunction (ctx, fn_parse_js)) {
|
||||
R_LOG_WARN ("r2.plugin('parse', X) expects X to be a function that returns an object with at least: parse");
|
||||
// return JS_ThrowRangeError (ctx, "r2.plugin requires the function to return an object with the `call` field to be a function");
|
||||
return JS_NewBool (ctx, false);
|
||||
}
|
||||
|
||||
QjsParsePlugin *cp = plugin_manager_find_parse_plugin (pm, nameptr);
|
||||
if (cp) {
|
||||
R_LOG_WARN ("r2.plugin with name %s is already registered", nameptr);
|
||||
// return JS_ThrowRangeError (ctx, "r2.plugin core already registered (only one exists)");
|
||||
return JS_NewBool (ctx, false);
|
||||
}
|
||||
|
||||
RParsePlugin *ap = R_NEW0 (RParsePlugin);
|
||||
if (!ap) {
|
||||
return JS_ThrowRangeError (ctx, "could not allocate qjs core plugin");
|
||||
}
|
||||
|
||||
JSValue desc = JS_GetPropertyStr (ctx, res, "desc");
|
||||
const char *descptr = JS_ToCStringLen2 (ctx, &namelen, desc, false);
|
||||
|
||||
#if 0
|
||||
JSValue license = JS_GetPropertyStr (ctx, res, "license");
|
||||
const char *licenseptr = JS_ToCStringLen2 (ctx, &namelen, license, false);
|
||||
RPluginMeta meta = {
|
||||
.name = strdup (nameptr),
|
||||
.desc = descptr ? strdup (descptr) : NULL,
|
||||
.license = descptr ? strdup (licenseptr) : NULL,
|
||||
};
|
||||
memcpy ((void*)&ap->meta, &meta, sizeof (RPluginMeta));
|
||||
#else
|
||||
ap->name = strdup (nameptr);
|
||||
ap->desc = descptr ? strdup (descptr) : NULL;
|
||||
// ap->license = strdup (licenseptr);
|
||||
#endif
|
||||
|
||||
ap->parse = qjs_parse; // Technically this could all be handled by a single generic plugin
|
||||
|
||||
QjsParsePlugin *pp = plugin_manager_add_parse_plugin (pm, nameptr, ctx, ap, fn_parse_js);
|
||||
pp->fn_parse_js = fn_parse_js;
|
||||
|
||||
RLibStruct *lib = R_NEW0 (RLibStruct);
|
||||
if (!lib) {
|
||||
free (ap);
|
||||
return JS_NewBool (ctx, false);
|
||||
}
|
||||
|
||||
lib->type = R_LIB_TYPE_PARSE;
|
||||
lib->data = ap;
|
||||
lib->version = R2_VERSION;
|
||||
int ret = r_lib_open_ptr (pm->core->lib, ap->name, NULL, lib);
|
||||
return JS_NewBool (ctx, ret == 1);
|
||||
}
|
Loading…
Reference in New Issue
Block a user