-
-
Notifications
You must be signed in to change notification settings - Fork 261
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
thread: Support hardware breakpoints and watchpoints
So they can be set and unset. Co-authored-by: Håvard Sørbø <[email protected]>
- Loading branch information
Showing
27 changed files
with
2,011 additions
and
243 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
/* | ||
* Copyright (C) 2020-2024 Ole André Vadla Ravnås <[email protected]> | ||
* Copyright (C) 2023 Francesco Tamagni <[email protected]> | ||
* Copyright (C) 2024 Håvard Sørbø <[email protected]> | ||
* | ||
* Licence: wxWindows Library Licence, Version 3.1 | ||
*/ | ||
|
@@ -10,6 +11,7 @@ | |
|
||
#include "gumquickcore.h" | ||
#include "gumquickmodule.h" | ||
#include "gumquickthread.h" | ||
|
||
G_BEGIN_DECLS | ||
|
||
|
@@ -19,6 +21,7 @@ typedef struct _GumQuickExceptionHandler GumQuickExceptionHandler; | |
struct _GumQuickProcess | ||
{ | ||
GumQuickModule * module; | ||
GumQuickThread * thread; | ||
GumQuickCore * core; | ||
|
||
JSValue main_module_value; | ||
|
@@ -30,7 +33,8 @@ struct _GumQuickProcess | |
}; | ||
|
||
G_GNUC_INTERNAL void _gum_quick_process_init (GumQuickProcess * self, | ||
JSValue ns, GumQuickModule * module, GumQuickCore * core); | ||
JSValue ns, GumQuickModule * module, GumQuickThread * thread, | ||
GumQuickCore * core); | ||
G_GNUC_INTERNAL void _gum_quick_process_flush (GumQuickProcess * self); | ||
G_GNUC_INTERNAL void _gum_quick_process_dispose (GumQuickProcess * self); | ||
G_GNUC_INTERNAL void _gum_quick_process_finalize (GumQuickProcess * self); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
/* | ||
* Copyright (C) 2020-2024 Ole André Vadla Ravnås <[email protected]> | ||
* Copyright (C) 2024 DaVinci <[email protected]> | ||
* Copyright (C) 2024 Håvard Sørbø <[email protected]> | ||
* | ||
* Licence: wxWindows Library Licence, Version 3.1 | ||
*/ | ||
|
@@ -18,12 +19,35 @@ enum _GumBacktracerType | |
GUMJS_DECLARE_FUNCTION (gumjs_thread_backtrace) | ||
GUMJS_DECLARE_FUNCTION (gumjs_thread_sleep) | ||
|
||
static const JSCFunctionListEntry gumjs_thread_entries[] = | ||
GUMJS_DECLARE_CONSTRUCTOR (gumjs_thread_construct) | ||
GUMJS_DECLARE_FUNCTION (gumjs_thread_set_hardware_breakpoint) | ||
GUMJS_DECLARE_FUNCTION (gumjs_thread_unset_hardware_breakpoint) | ||
GUMJS_DECLARE_FUNCTION (gumjs_thread_set_hardware_watchpoint) | ||
GUMJS_DECLARE_FUNCTION (gumjs_thread_unset_hardware_watchpoint) | ||
|
||
static const JSClassDef gumjs_thread_def = | ||
{ | ||
.class_name = "Thread", | ||
}; | ||
|
||
static const JSCFunctionListEntry gumjs_thread_module_entries[] = | ||
{ | ||
JS_CFUNC_DEF ("_backtrace", 0, gumjs_thread_backtrace), | ||
JS_CFUNC_DEF ("sleep", 0, gumjs_thread_sleep), | ||
}; | ||
|
||
static const JSCFunctionListEntry gumjs_thread_entries[] = | ||
{ | ||
JS_CFUNC_DEF ("setHardwareBreakpoint", 0, | ||
gumjs_thread_set_hardware_breakpoint), | ||
JS_CFUNC_DEF ("unsetHardwareBreakpoint", 0, | ||
gumjs_thread_unset_hardware_breakpoint), | ||
JS_CFUNC_DEF ("setHardwareWatchpoint", 0, | ||
gumjs_thread_set_hardware_watchpoint), | ||
JS_CFUNC_DEF ("unsetHardwareWatchpoint", 0, | ||
gumjs_thread_unset_hardware_watchpoint), | ||
}; | ||
|
||
static const JSCFunctionListEntry gumjs_backtracer_entries[] = | ||
{ | ||
JS_PROP_INT32_DEF ("ACCURATE", GUM_BACKTRACER_ACCURATE, JS_PROP_C_W_E), | ||
|
@@ -36,16 +60,23 @@ _gum_quick_thread_init (GumQuickThread * self, | |
GumQuickCore * core) | ||
{ | ||
JSContext * ctx = core->ctx; | ||
JSValue obj; | ||
JSValue obj, proto, ctor; | ||
|
||
self->core = core; | ||
|
||
_gum_quick_core_store_module_data (core, "thread", self); | ||
|
||
obj = JS_NewObject (ctx); | ||
JS_SetPropertyFunctionList (ctx, obj, gumjs_thread_entries, | ||
_gum_quick_create_class (ctx, &gumjs_thread_def, core, &self->thread_class, | ||
&proto); | ||
ctor = JS_NewCFunction2 (ctx, gumjs_thread_construct, | ||
gumjs_thread_def.class_name, 0, JS_CFUNC_constructor, 0); | ||
JS_SetConstructor (ctx, ctor, proto); | ||
JS_SetPropertyFunctionList (ctx, ctor, gumjs_thread_module_entries, | ||
G_N_ELEMENTS (gumjs_thread_module_entries)); | ||
JS_SetPropertyFunctionList (ctx, proto, gumjs_thread_entries, | ||
G_N_ELEMENTS (gumjs_thread_entries)); | ||
JS_DefinePropertyValueStr (ctx, ns, "Thread", obj, JS_PROP_C_W_E); | ||
JS_DefinePropertyValueStr (ctx, ns, gumjs_thread_def.class_name, ctor, | ||
JS_PROP_C_W_E); | ||
|
||
obj = JS_NewObject (ctx); | ||
JS_SetPropertyFunctionList (ctx, obj, gumjs_backtracer_entries, | ||
|
@@ -159,3 +190,186 @@ GUMJS_DEFINE_FUNCTION (gumjs_thread_sleep) | |
|
||
return JS_UNDEFINED; | ||
} | ||
|
||
JSValue | ||
_gum_quick_thread_new (JSContext * ctx, | ||
const GumThreadDetails * details, | ||
GumQuickThread * parent) | ||
{ | ||
GumQuickCore * core = parent->core; | ||
JSValue thread; | ||
|
||
thread = JS_NewObjectClass (ctx, parent->thread_class); | ||
|
||
JS_SetOpaque (thread, GSIZE_TO_POINTER (details->id)); | ||
|
||
JS_DefinePropertyValue (ctx, thread, | ||
GUM_QUICK_CORE_ATOM (core, id), | ||
JS_NewInt64 (ctx, details->id), | ||
JS_PROP_C_W_E); | ||
if (details->name != NULL) | ||
{ | ||
JS_DefinePropertyValue (ctx, thread, | ||
GUM_QUICK_CORE_ATOM (core, name), | ||
JS_NewString (ctx, details->name), | ||
JS_PROP_C_W_E); | ||
} | ||
JS_DefinePropertyValue (ctx, thread, | ||
GUM_QUICK_CORE_ATOM (core, state), | ||
_gum_quick_thread_state_new (ctx, details->state), | ||
JS_PROP_C_W_E); | ||
JS_DefinePropertyValue (ctx, thread, | ||
GUM_QUICK_CORE_ATOM (core, context), | ||
_gum_quick_cpu_context_new (ctx, (GumCpuContext *) &details->cpu_context, | ||
GUM_CPU_CONTEXT_READONLY, core, NULL), | ||
JS_PROP_C_W_E); | ||
|
||
return thread; | ||
} | ||
|
||
static gboolean | ||
gum_thread_get (JSContext * ctx, | ||
JSValueConst val, | ||
GumQuickCore * core, | ||
GumThreadId * thread_id) | ||
{ | ||
return _gum_quick_unwrap (ctx, val, | ||
gumjs_get_parent_module (core)->thread_class, core, | ||
(gpointer *) thread_id); | ||
} | ||
|
||
GUMJS_DEFINE_CONSTRUCTOR (gumjs_thread_construct) | ||
{ | ||
JSValue wrapper = JS_NULL; | ||
GumThreadId thread_id; | ||
JSValue proto; | ||
|
||
if (!_gum_quick_args_parse (args, "Z", &thread_id)) | ||
return JS_EXCEPTION; | ||
|
||
proto = JS_GetProperty (ctx, new_target, | ||
GUM_QUICK_CORE_ATOM (core, prototype)); | ||
wrapper = JS_NewObjectProtoClass (ctx, proto, | ||
gumjs_get_parent_module (core)->thread_class); | ||
JS_FreeValue (ctx, proto); | ||
if (JS_IsException (wrapper)) | ||
return JS_EXCEPTION; | ||
|
||
JS_SetOpaque (wrapper, GSIZE_TO_POINTER (thread_id)); | ||
|
||
return wrapper; | ||
} | ||
|
||
GUMJS_DEFINE_FUNCTION (gumjs_thread_set_hardware_breakpoint) | ||
{ | ||
GumThreadId thread_id; | ||
guint breakpoint_id; | ||
gpointer address; | ||
GError * error; | ||
|
||
if (!gum_thread_get (ctx, this_val, core, &thread_id)) | ||
return JS_EXCEPTION; | ||
|
||
if (!_gum_quick_args_parse (args, "up", &breakpoint_id, &address)) | ||
return JS_EXCEPTION; | ||
|
||
error = NULL; | ||
gum_thread_set_hardware_breakpoint (thread_id, breakpoint_id, | ||
GUM_ADDRESS (address), &error); | ||
if (error != NULL) | ||
return _gum_quick_throw_error (ctx, &error); | ||
|
||
return JS_UNDEFINED; | ||
} | ||
|
||
GUMJS_DEFINE_FUNCTION (gumjs_thread_unset_hardware_breakpoint) | ||
{ | ||
GumThreadId thread_id; | ||
guint breakpoint_id; | ||
GError * error; | ||
|
||
if (!gum_thread_get (ctx, this_val, core, &thread_id)) | ||
return JS_EXCEPTION; | ||
|
||
if (!_gum_quick_args_parse (args, "u", &breakpoint_id)) | ||
return JS_EXCEPTION; | ||
|
||
error = NULL; | ||
gum_thread_unset_hardware_breakpoint (thread_id, breakpoint_id, &error); | ||
if (error != NULL) | ||
return _gum_quick_throw_error (ctx, &error); | ||
|
||
return JS_UNDEFINED; | ||
} | ||
|
||
GUMJS_DEFINE_FUNCTION (gumjs_thread_set_hardware_watchpoint) | ||
{ | ||
GumThreadId thread_id; | ||
guint watchpoint_id; | ||
gpointer address; | ||
gsize size; | ||
const gchar * conditions_str; | ||
GumWatchConditions conditions; | ||
const gchar * ch; | ||
GError * error; | ||
|
||
if (!gum_thread_get (ctx, this_val, core, &thread_id)) | ||
return JS_EXCEPTION; | ||
|
||
if (!_gum_quick_args_parse (args, "upZs", &watchpoint_id, &address, &size, | ||
&conditions_str)) | ||
return JS_EXCEPTION; | ||
|
||
conditions = 0; | ||
for (ch = conditions_str; *ch != '\0'; ch++) | ||
{ | ||
switch (*ch) | ||
{ | ||
case 'r': | ||
conditions |= GUM_WATCH_READ; | ||
break; | ||
case 'w': | ||
conditions |= GUM_WATCH_WRITE; | ||
break; | ||
default: | ||
goto invalid_conditions; | ||
} | ||
} | ||
if (conditions == 0) | ||
goto invalid_conditions; | ||
|
||
error = NULL; | ||
gum_thread_set_hardware_watchpoint (thread_id, watchpoint_id, | ||
GUM_ADDRESS (address), size, conditions, &error); | ||
if (error != NULL) | ||
return _gum_quick_throw_error (ctx, &error); | ||
|
||
return JS_UNDEFINED; | ||
|
||
invalid_conditions: | ||
{ | ||
_gum_quick_throw_literal (ctx, | ||
"expected a string specifying watch conditions, e.g. 'rw'"); | ||
return JS_EXCEPTION; | ||
} | ||
} | ||
|
||
GUMJS_DEFINE_FUNCTION (gumjs_thread_unset_hardware_watchpoint) | ||
{ | ||
GumThreadId thread_id; | ||
guint watchpoint_id; | ||
GError * error; | ||
|
||
if (!gum_thread_get (ctx, this_val, core, &thread_id)) | ||
return JS_EXCEPTION; | ||
|
||
if (!_gum_quick_args_parse (args, "u", &watchpoint_id)) | ||
return JS_EXCEPTION; | ||
|
||
error = NULL; | ||
gum_thread_unset_hardware_watchpoint (thread_id, watchpoint_id, &error); | ||
if (error != NULL) | ||
return _gum_quick_throw_error (ctx, &error); | ||
|
||
return JS_UNDEFINED; | ||
} |
Oops, something went wrong.