Skip to content

Commit

Permalink
added documentation and example plugins
Browse files Browse the repository at this point in the history
  • Loading branch information
Vasiliev Ivan committed Apr 11, 2017
1 parent 1217baf commit a93f835
Show file tree
Hide file tree
Showing 7 changed files with 246 additions and 1 deletion.
93 changes: 93 additions & 0 deletions docs/plugins.txt
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
1 change: 1 addition & 0 deletions plugins/plugins_src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export PLUGIN_PREFIX

PLUGINS :=
PLUGINS += contexts
PLUGINS += example1 example2
ifeq ($(TARGET_NAME), $(filter $(TARGET_NAME), i386 x86_64))
PLUGINS += syscalls_windows_xp syscalls_windows_7 syscalls_windows_8 syscalls_windows_8.1
PLUGINS += syscalls_linux
Expand Down
3 changes: 3 additions & 0 deletions plugins/plugins_src/Makefile.plugins
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ PLUGIN_DIR := $(PLUGIN_PREFIX)$(PLUGIN)/
# List of the files in the plugins
contexts-files := contexts.o

example1-files := example1.o
example2-files := example2.o

syscalls_linux-flags := -DGUEST_OS_LINUX
syscalls_linux-files := syscalls_linux.o syscall_lin.o guest_string.o syscall_processing.o

Expand Down
86 changes: 86 additions & 0 deletions plugins/plugins_src/example1.c
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;
}

9 changes: 9 additions & 0 deletions plugins/plugins_src/example1.h
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
54 changes: 54 additions & 0 deletions plugins/plugins_src/example2.c
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;
}

1 change: 0 additions & 1 deletion plugins/plugins_src/syscalls_windows.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ void pi_start(PluginInterface *pi)
pi->exit = cpus_exit;
cb = plugin_reg_signal("syscall");
plugin_subscribe(decode_instr, "qemu", "PLUGIN_QEMU_INSTR_TRANSLATE");
//tcg_plugin_subscribe(syscall_exception, "qemu", "PLUGIN_QEMU_EXCEPTION");
plugin_subscribe(syscall_tlb_add_page, "qemu", "PLUGIN_QEMU_TLB_SET_PAGE");
tcg_context_register_helper(
&tcg_ctx,
Expand Down

0 comments on commit a93f835

Please sign in to comment.