-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added documentation and example plugins
- Loading branch information
Vasiliev Ivan
committed
Apr 11, 2017
1 parent
1217baf
commit a93f835
Showing
7 changed files
with
246 additions
and
1 deletion.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
Copyright (C) 2015 ISP RAS | ||
|
||
|
||
Using plugins | ||
------------- | ||
|
||
You should run configure with parameter "--enable-plugins". After that just run make | ||
and make install as usual | ||
|
||
To load a plugin with name "example_name" you should: | ||
* Stop qemu with "stop" command | ||
* Then execute "load_plugin example_name" command | ||
* Then happily continue the work of stopped qemu, you should be fine | ||
(sidenote: all commands should be executed via monitor) | ||
|
||
You can also execute "list_plugins" to see the list of loaded plugins, and | ||
"stop_plugin example_name" to stop plugin, as the name suggests. | ||
|
||
Writing plugins | ||
------------- | ||
|
||
All plugins should be located in path_to_qemu/plugins/plugins_src/ and be written in pure C. | ||
|
||
Every plugin should have mandatory parts (for reference check existing plugins): | ||
* Structure "init_info". Here you can identify which signals your plugin would provide, | ||
dependencies on other plugins, and os_version. If plugin generates signals, then | ||
they MUST be mentioned in "signals_list" field, otherwise automatic loading of plugins | ||
wouldn't work and needed plugins will have to be loaded manually. | ||
* Function "pi_start". Here you can subscribe to signals, define specific monitor commands | ||
for your plugin, register helper functions, and fill the table of functions that this plugin | ||
is providing in a straight-forward way, not through signals mechanism. | ||
Other parts of plugin are not obligatory, but are containing plugins' functionality. | ||
|
||
Basically, you perform instrumentation by subscribing to specific signal and providing | ||
function that will process gotten data. This would be performed during translation; if you | ||
want to process data during actual execution, you should use helper functions (look at function | ||
"tcg_context_register_helper" and "tcg_gen_callN"). | ||
|
||
To make plugin actually buildable, you should add corresponding info to plugins_src/Makefile | ||
and plugins_src/Makefile.plugins (again, check how it is done for existing plugins for | ||
reference). | ||
|
||
Use of signal-subscriber mechanism | ||
------------- | ||
|
||
To exchange the data between qemu and plugins, and between plugins themselves, we introduce | ||
signal-subsctiber mechanism. It works as following: firstly one of plugins registers signals | ||
which would be generated by it. Other plugin, that wishes to be notificated every time | ||
those signals emit, should subscribe to needed signals and set which function would process | ||
gotten data. At some place, that is specified in your plugin, a signal would be generated and | ||
data would be transfered to QEMU, which in it's turn would forward gotten data to all plugins, | ||
that were subscribed to this signal. | ||
|
||
How exactly these should be used: | ||
* First of all signals that would be provided by plugin should be mentioned in "init_info" | ||
structure (signal groups, to be more specific). | ||
* In "pi_start" those signals should be registered via "plugin_reg_signal(s1)" | ||
function, where s1 is a string identificator that identifies the group of signals (i.e. | ||
"syscalls"). This function will return pointer to "Signal_info", which should be used for | ||
signal generation. | ||
* To generate a signal you should use function "plugin_gen_signal(si, s2, data, env)", | ||
where si - is Signal_info that you got after signal registration, s2 - is a string | ||
identificator that identifies specific signal inside of group (i.e. "file_open"), data - | ||
data casted to void*, basically the useful info that signal is providing, and env - | ||
CPUArchState* var, that is widely used for analyses. The pair of s1 and s2 (signals group | ||
and specific signal inside of it) should be unique to your signal, and currently, this | ||
should be tracked by programmer himself. | ||
* To subscribe to a signal you should use a plugin_subscribe(func, s1, s2) function, where | ||
func - name of the function, that will process the signal, and s1 and s2 - are string ids, | ||
same as above. When you will try to subscribe to signal, mechanism would check whether | ||
corresponding plugins has already been loaded, and if not, would try to find and load them. | ||
That's exactly the reason why you should have all your signals mentioned in init_info | ||
structure, because that's the place where loader would look for them. If you wouldn't | ||
define signals there, you would have to load needed plugins (providers) by hand. | ||
* It also worth mentioning, that if you set "dependencies" in init_info structure, system | ||
will try to find and load mentioned dependency-plugins on load of your plugin. | ||
|
||
|
||
List of qemu-provided callbacks | ||
------------- | ||
|
||
I hope names are self-explanatory: | ||
|
||
PLUGIN_QEMU_BEFORE_GEN_TB, | ||
PLUGIN_QEMU_INSTR_TRANSLATE, | ||
PLUGIN_QEMU_EXCEPTION, | ||
PLUGIN_QEMU_EXCEPTION_HANDLER, | ||
PLUGIN_QEMU_INSTRUCTION_EXCEPTION, | ||
PLUGIN_QEMU_INTERRUPT, | ||
PLUGIN_QEMU_TLB_SET_PAGE, | ||
PLUGIN_QEMU_CPU_PAUSED, | ||
PLUGIN_QEMU_PAGE_DIR_UPD, | ||
PLUGIN_QEMU_CPUS_STOPPED |
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
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 |
---|---|---|
@@ -0,0 +1,86 @@ | ||
#include "qemu/osdep.h" | ||
#include "qapi/error.h" | ||
#include "plugins/plugin.h" | ||
|
||
//there could be some unused includes, but im too lazy to check this atm | ||
#include "exec/cpu_ldst.h" | ||
|
||
#include "tcg/tcg.h" | ||
#include "tcg/tcg-op.h" | ||
#include "exec/helper-proto.h" | ||
#include <exec/helper-head.h> | ||
#include "exec/exec-all.h" | ||
|
||
#include "example1.h" | ||
|
||
static SignalInfo *cb; | ||
static uint64_t scount; | ||
|
||
const struct pi_info init_info = | ||
{ | ||
.signals_list = (const char *[]){"test",NULL}, | ||
.dependencies = (const char *[]){"contexts", NULL}, | ||
.os_ver = NULL | ||
}; | ||
|
||
static void after_exec_opc(void) | ||
{ | ||
//qemu_log_mask(LOG_GUEST_ERROR, "System call again \n"); | ||
char data[] = "And I am a signal from helper. Isn't it great?\n"; | ||
plugin_gen_signal(cb, "HELPER", data, NULL); | ||
} | ||
|
||
static void decode_instr(void *data, CPUArchState *env) | ||
{ | ||
scount++; | ||
if(!(scount % 10000)) | ||
{ | ||
char data[] = "10 000 instructions has passed. Impressive! \n"; | ||
plugin_gen_signal(cb, "EVERY_10000", data, env); | ||
tcg_gen_callN(&tcg_ctx, after_exec_opc, dh_retvar(void), 0, NULL); | ||
} | ||
} | ||
|
||
static void cpus_paused(const PluginInterface *pi) | ||
{ | ||
fprintf(pi->output,"number of instructions = %" PRIu64 "\n", | ||
scount); | ||
} | ||
|
||
static void unload_signal(void) | ||
{ | ||
plugin_del_signal(cb); | ||
plugin_del_subscriber(decode_instr, "qemu", "PLUGIN_QEMU_INSTR_TRANSLATE"); | ||
} | ||
|
||
static void test_print_function_wout_params(void) | ||
{ | ||
fprintf(stderr, "Test function wout paramenters successfuly executed \n"); | ||
} | ||
static void test_print_function_w_params(int p1, int p2) | ||
{ | ||
fprintf(stderr, "Test function with parameters successfuly executed; param1 = %d & param2 =%d \n", p1,p2); | ||
} | ||
|
||
void pi_start(PluginInterface *pi) | ||
{ | ||
scount = 0; | ||
cb = plugin_reg_signal("test"); | ||
|
||
plugin_subscribe(decode_instr, "qemu", "PLUGIN_QEMU_INSTR_TRANSLATE"); | ||
|
||
tcg_context_register_helper( | ||
&tcg_ctx, | ||
after_exec_opc, | ||
"after_exec_opc", | ||
0, | ||
dh_sizemask(void, 0) | ||
); | ||
|
||
static const struct SSS funcs = { .f1 = test_print_function_wout_params, .f2 = test_print_function_w_params}; | ||
|
||
pi->exit = cpus_paused; | ||
pi->unload_signal = unload_signal; | ||
pi->funcs = &funcs; | ||
} | ||
|
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 |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#ifndef EXAMPLE1_H | ||
#define EXAMPLE1_H | ||
|
||
typedef void (* temp_func_wout_params)(void); | ||
typedef void (* temp_func_w_params)(int p1, int p2); | ||
|
||
struct SSS { temp_func_wout_params f1; temp_func_w_params f2; }; | ||
|
||
#endif |
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 |
---|---|---|
@@ -0,0 +1,54 @@ | ||
#include "qemu/osdep.h" | ||
#include "qapi/error.h" | ||
#include "plugins/plugin.h" | ||
|
||
#include "tcg/tcg.h" | ||
#include "tcg/tcg-op.h" | ||
#include "exec/helper-proto.h" | ||
#include <exec/helper-head.h> | ||
#include "exec/exec-all.h" | ||
#include "exec/cpu_ldst.h" | ||
|
||
#include "example1.h" | ||
|
||
const struct pi_info init_info = | ||
{ | ||
.signals_list = (const char *[]){NULL}, | ||
.dependencies = (const char *[]){NULL}, | ||
.os_ver = NULL | ||
}; | ||
|
||
static void test_f(void* data, CPUArchState *env) | ||
{ | ||
char *str = data; | ||
fprintf(stderr, "what is it? - %s ", | ||
str); | ||
} | ||
|
||
static void cpus_stopped(const PluginInterface *pi) | ||
{ | ||
fprintf(pi->output,"Example2 plugin has stopped\n"); | ||
} | ||
|
||
static void unload_signal(void) | ||
{ | ||
plugin_del_subscriber(test_f, "test", "EVERY_10000"); | ||
plugin_del_subscriber(test_f, "test", "HELPER"); | ||
} | ||
|
||
void pi_start(PluginInterface *pi) | ||
{ | ||
plugin_subscribe(test_f, "test", "EVERY_10000"); | ||
plugin_subscribe(test_f, "test", "HELPER"); | ||
|
||
struct SSS *funcs= (struct SSS*)plugin_get_functions_list("example1"); | ||
if(funcs != NULL) | ||
{ | ||
funcs->f1(); | ||
funcs->f2(1, 2); | ||
} | ||
|
||
pi->exit = cpus_stopped; | ||
pi->unload_signal = unload_signal; | ||
} | ||
|
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