统计模块之间转移指令的个数。强调是通过 indirect branches,不过我到现在都还没有明白 间接转移是什么意思。
typedef struct _module_array_t {
app_pc base;
app_pc end;
bool loaded;
module_data_t *info;
} module_array_t;
/* the last slot is where all non-module address go */
static module_array_t mod_array[MAX_NUM_MODULES];
mod_array 数组用来存储有关模块的信息,base 是模块基址,end 是模块结束地址,loaded用来表示是否加载,当一个模块被卸载时,会被设为 false。
static bool
module_data_same(const module_data_t *d1, const module_data_t *d2)
{
if (d1->start == d2->start && d1->end == d2->end &&
d1->entry_point == d2->entry_point &&
#ifdef WINDOWS
d1->checksum == d2->checksum && d1->timestamp == d2->timestamp &&
#endif
/* treat two modules w/ no name (there are some) as different */
dr_module_preferred_name(d1) != NULL && dr_module_preferred_name(d2) != NULL &&
strcmp(dr_module_preferred_name(d1), dr_module_preferred_name(d2)) == 0)
return true;
return false;
}
该函数用来判断两个 模块是否相同。
module_data_t是 DR 定义的一个结构体,可以在这里看http://dynamorio.org/docs/struct__module__data__t.html
DR_EXPORT void
dr_client_main(client_id_t id, int argc, const char *argv[])
{
/* We need no drreg slots ourselves, but we initialize drreg as we call * drreg_restore_app_values(), required since drx_insert_counter_update() * uses drreg when drmgr is used. */
drreg_options_t ops = { sizeof(ops) };
if (!drmgr_init() || drreg_init(&ops) != DRREG_SUCCESS)
DR_ASSERT(false);
drx_init();
/* register events */
dr_register_exit_event(event_exit);
if (!drmgr_register_bb_instrumentation_event(event_analyze_bb,
event_insert_instrumentation, NULL))
DR_ASSERT(false);
drmgr_register_module_load_event(event_module_load);
drmgr_register_module_unload_event(event_module_unload);
mod_lock = dr_mutex_create();
logfile = log_file_open(id, NULL /* drcontext */, NULL /* path */, "modxfer",
#ifndef WINDOWS
DR_FILE_CLOSE_ON_FORK |
#endif
DR_FILE_ALLOW_LARGE);
DR_ASSERT(logfile != INVALID_FILE);
}
开头就对 drreg 进行了初始化,注释里说了原因(因为 drx_insert_counter_update()),不过我们后面具体再说
经过初始化后,注册了 5个回调函数:
进程结束回调: event_exit
模块加载回调: event_module_load
模块卸载回调: evnet_module_unload
还有用drmgr_register_bb_instrumentation_event 注册的两个回调函数,注册了第二第三阶段。
mod_lock 是互斥锁。
log_file_open () 则是 utils.c 里封装的函数,用来创建一个日志文件。
static void
event_module_load(void *drcontext, const module_data_t *info, bool loaded)
{
int i;
dr_mutex_lock(mod_lock);
for (i = 0; i < num_mods; i++) {
/* check if it is the same as any unloaded module */
if (!mod_array[i].loaded && module_data_same(mod_array[i].info, info)) {
mod_array[i].loaded = true;
break;
}
}
if (i == num_mods) {
/* new module */
mod_array[i].base = info->start;
mod_array[i].end = info->end;
mod_array[i].loaded = true;
mod_array[i].info = dr_copy_module_data(info);
num_mods++;
}
DR_ASSERT(num_mods < UNKNOW_MODULE_IDX);
dr_mutex_unlock(mod_lock);
开头先加锁。
mod_array 是存放moduld信息的一个数组。
num_mods 则是module的数目。
第一个for循环,遍历module 数组,并利用 module_data_same 函数来判断新 加载的module 是否已经在 module array 里。如果找到,则将数组中该节点的 loaded 属性设为 true。 如果遍历完找不到或者说 module array 一开始就是空的,则将新节点的信息设置为 新加载 的模块。
static void
event_module_unload(void *drcontext, const module_data_t *info)
{
int i;
dr_mutex_lock(mod_lock);
for (i = 0; i < num_mods; i++) {
if (mod_array[i].loaded && module_data_same(mod_array[i].info, info)) {
/* Some module might be repeatedly loaded and unloaded, so instead * of clearing out the array entry, we keep the data for possible * reuse. */
mod_array[i].loaded = false;
break;
}
}
DR_ASSERT(i < num_mods);
dr_mutex_unlock(mod_lock);
}
先上锁,遍历 module array 数组,如果找到节点信息和 要卸载的模块信息相同。则将该节点的 loaded 属性设置为 false 。
然后解锁。
/* This event is passed the instruction list for the whole bb. */
static dr_emit_flags_t
event_analyze_bb(void *drcontext, void *tag, instrlist_t *bb, bool for_trace,
bool translating, void **user_data)
{
/* Count the instructions and pass the result to event_insert_instrumentation. */
instr_t *instr;
uint num_instrs;
for (instr = instrlist_first_app(bb), num_instrs = 0; instr != NULL;
instr = instr_get_next_app(instr)) {
num_instrs++;
}
*(uint *)user_data = num_instrs;
return DR_EMIT_DEFAULT;
}
注意一下,我们说drmgr 将 basic block event 分成了四个阶段,第2个是 分析阶段, 第3个是 插入阶段。 分析是针对整个代码段的,也就是说 一个 basic block 可能只会调用 event_analyze_bb 一次,但是每个指令都会调用一次 event_insert_instrumentation(插入回调)。
另外原文档里是说: analysis of the full application code (after any changes from its original form)
不过我不太明白这里所说的 改变 指的是什么。
回到这段代码,遍历一个 basic block 里的所有指令,统计当前基本快 的指令数,并将结果存储在 user_data 里。
关于 user_data ,原文档里说: The user_data parameter can be used to pass data from this stage to the third stage。
看来这个参数就是专门用来在第二第三阶段之间传递数据的。
但是当我看到 *(uint *)user_data = num_instrs 时,我一度对我的 c语言水平产生了怀疑。这不是个双重指针吗,括号里面的不是指针的强制转换吗,为什么把一个双重指针当作 一重指针用呢?
别慌,接着看后面。
/* This event is called separately for each individual instruction in the bb. */
static dr_emit_flags_t
event_insert_instrumentation(void *drcontext, void *tag, instrlist_t *bb, instr_t *instr,
bool for_trace, bool translating, void *user_data)
{
if (drmgr_is_first_instr(drcontext, instr)) {
uint num_instrs = (uint)(ptr_uint_t)user_data;
int i;
app_pc bb_addr = dr_fragment_app_pc(tag);
for (i = 0; i < num_mods; i++) {
if (mod_array[i].loaded && mod_array[i].base <= bb_addr &&
mod_array[i].end > bb_addr)
break;
}
if (i == num_mods)
i = UNKNOW_MODULE_IDX;
/* We pass SPILL_SLOT_MAX+1 as drx will use drreg for spilling. */
drx_insert_counter_update(drcontext, bb, instr, SPILL_SLOT_MAX + 1,
(void *)&mod_cnt[i], num_instrs, DRX_COUNTER_64BIT);
drx_insert_counter_update(drcontext, bb, instr, SPILL_SLOT_MAX + 1,
(void *)&ins_count, num_instrs, DRX_COUNTER_64BIT);
}
if (instr_is_mbr(instr) && !instr_is_return(instr)) {
/* Assuming most of the transfers between modules are paired, we * instrument indirect branches but not returns for better performance. * We assume that most cross module transfers happens via indirect * branches. * Direct branch with DGC or self-modify may also cross modules, but * it should be ok to ignore, and we can handle them more efficiently. */
/* dr_insert_mbr_instrumentation is going to read app values, so we need a * drreg lazy restore "barrier" here. */
drreg_status_t res =
drreg_restore_app_values(drcontext, bb, instr, instr_get_target(instr), NULL);
DR_ASSERT(res == DRREG_SUCCESS || res == DRREG_ERROR_NO_APP_VALUE);
dr_insert_mbr_instrumentation(drcontext, bb, instr, (void *)mbr_update,
SPILL_SLOT_1);
}
return DR_EMIT_DEFAULT;
}
看到了吗,第三阶段注册 的 回调函数里 参数 user_data 是个一重指针。我方了。
查查文档,两个函数类型的参数信息如下:
typedef dr_emit_flags_t(* drmgr_analysis_cb_t) (void *drcontext, void *tag,
instrlist_t *bb,
bool for_trace,
bool translating,
OUT void **user_data)
typedef dr_emit_flags_t(* drmgr_insertion_cb_t) (void *drcontext, void *tag,
instrlist_t *bb,
instr_t *inst,
bool for_trace,
bool translating,
void *user_data)
所以用一个指针去保存32bit 数据有什么优点吗?难道说是因为可以保存各种类型的数据?
回到函数本身。
第一个if 代码段,判断当前指令是否是 basic block 的第一条代码。
如果是,则读取出 user_data 里的数据,也就是前面我们所说的basic block 里的指令数。(不过关于user_data 的读写操作我也是醉了,先不管它了,文档里也找不到 ptr_uint_t 的信息。)
然后利用 dr_fragment_app_pc 获取这个段的起始地址,与各个模块地址范围进行比较,用 i 来标记当前basic block 属于哪个模块,如果都不属于,就定为 UNKNOW_MODULE_IDX。
然后使用 drx_insert_counter_update() 插入计数指令。这个函数的作用是插入一条指令,这条指令的作用是: 将第6个参数 加到 第5个参数指向的内容上。这样的话,以后每执行到这个 basic block 就会执行一次该指令,也就是 加一次 num_instrs。这样就可以统计一共执行了多少次指令(ins_count),和 每个模块分别 执行了多少次指令(mod_cnt)。
这里我们就要好好说道说道 drx_insert_counter_update(),文档里的描述是这样的:
DR_EXPORT bool drx_insert_counter_update ( void * drcontext,
instrlist_t * ilist,
instr_t * where,
dr_spill_slot_t slot,
IF_NOT_X86_(dr_spill_slot_t slot2) void * addr,
int value,
uint flags
)
Inserts into ilist prior to where meta-instruction(s) to add the constant value to the counter located at addr
When used with drmgr, this routine uses the drreg extension. It must be called from drmgr’s insertion phase. The drreg extension will be used to spill the arithmetic flags and any scratch registers needed. It is up to the caller to ensure that enough spill slots are available, through drreg’s initialization. The slot and slot2 parameters must be set to SPILL_SLOT_MAX+1.
When used without drmgr, the spill slot slot is used for storing arithmetic flags or a scratch register if necessary. The spill slot slot2 is used only on ARM for spilling a second scratch register.
正如文档里所说,该函数会添加指令进行 加法操作。如果有加法操作,那可能就会对算出标志位产生影响。这样的话,我们就需要用 drreg 扩展完成 寄存器溢出操作,所谓溢出就是换个位置保存该寄存器里的 原始应用程序值,方便后面我们使用该寄存器。
这就是我们使用 drreg 的原因,但是我们在使用 drreg_init() 初始化 drreg时,并没有设置 插槽的数量,这和文档里所说“ It is up to the caller to ensure that enough spill slots are available, through drreg’s initialization.” 好像有些不符,是我理解错了吗?
回到函数,我们再看第2各 if代码段:if (instr_is_mbr(instr) && !instr_is_return(instr))
这里限定了 instr 是一个 mbr 指令,并且不是 return 指令。所谓 mbr指令,文档里这样说:instr is a multi-way (indirect) branch: OP_jmp_ind, OP_call_ind, OP_ret, OP_jmp_far_ind, OP_call_far_ind, OP_ret_far, or OP_iret on x86; OP_bx, OP_bxj, OP_blx_ind, or any instruction with a destination register operand of DR_REG_PC on ARM。
然后使用了 drreg_restore_app_values 来恢复之前溢出的原始应用程序值。
然后调用 dr_insert_mbr_instrumentation 来再mbr指令之前插入指令 调用 mbr_update 函数。
那么问题来了,为什么要使用 drreg_restore_app_values 来恢复寄存器里的值呢?如果不恢复会怎么样?
关于drreg 的的功能 大家可以看这篇博客里的 drreg 部分:https://blog.csdn.net/m0_37921080/article/details/88029035
drreg 会自动实现寄存器的溢出保护,比如这里的drx_insert_counter_update() ,寄存器会自动对标志寄存器进行溢出,也就是说把该寄存器里的值取出来放在另外一个位置,从而进行保护。
drreg 有一个惰性恢复策略,当对一个寄存器进行溢出后,drreg 假设只有 应用程序指令 会对该寄存器进行访问,并且不到 应用程序读取该寄存器的时候,不进行寄存器恢复,哪怕客户端已经不需要这个寄存器了。确实很懒。这里的恢复指的是 DR自动调用 drreg_get_app_value() 进行恢复。
那么如果我们插入的元指令 也对这个寄存器 原应用程序值 进行了读操作呢?这个时候就需要我们手动调用 drreg_restore_app_values() 或者 drreg_get_app_value() 来进行恢复了。
而 dr_insert_mbr_instrumentation() 插入的指令就可能会对寄存器进行读操作,所以在其之前用了一个 drreg_restore_app_values() 来恢复。
但是对于drreg_restore_app_values() 恢复了哪些寄存器我还是有些疑问。
接下来看 mbr_update() 到底进行了什么操作。
/* Simple clean calls with two arguments will not be inlined, but the context * switch can be optimized for better performance. */
static void
mbr_update(app_pc instr_addr, app_pc target_addr)
{
int i, j;
/* XXX: this can be optimized by walking a tree instead */
/* find the source module */
for (i = 0; i < num_mods; i++) {
if (mod_array[i].loaded && mod_array[i].base <= instr_addr &&
mod_array[i].end > instr_addr)
break;
}
/* if cannot find a module, put it to the last */
if (i == num_mods)
i = UNKNOW_MODULE_IDX;
/* find the target module */
/* quick check if it is the same module */
if (i < UNKNOW_MODULE_IDX && mod_array[i].base <= target_addr &&
mod_array[i].end > target_addr) {
j = i;
} else {
for (j = 0; j < num_mods; j++) {
if (mod_array[j].loaded && mod_array[j].base <= target_addr &&
mod_array[j].end > target_addr)
break;
}
/* if cannot find a module, put it to the last */
if (j == num_mods)
j = UNKNOW_MODULE_IDX;
}
/* this is a racy update, but should be ok to be a few number off */
xfer_cnt[i][j]++;
}
代码很简单,该函数的两个参数是由 dr_insert_mbr_instrumentation 传进来的,是 mbr指令的 地址 和 跳转目标的地址。
这个函数就是判断mbr 指令所在的模块 和 目标地址所在的模块,并记录在 xfer_cnt 二维数组里。
static void
event_exit(void)
{
int i;
char msg[512];
int len;
int j;
uint64 xmod_xfer = 0;
uint64 self_xfer = 0;
for (i = 0; i < num_mods; i++) {
dr_fprintf(logfile, "module %3d: %s\n", i,
dr_module_preferred_name(mod_array[i].info) == NULL
? ""
: dr_module_preferred_name(mod_array[i].info));
dr_fprintf(logfile, "%20llu instruction executed\n", mod_cnt[i]);
}
if (mod_cnt[UNKNOW_MODULE_IDX] != 0) {
dr_fprintf(logfile, "unknown modules:\n%20llu instruction executed\n",
mod_cnt[UNKNOW_MODULE_IDX]);
}
for (i = 0; i < MAX_NUM_MODULES; i++) {
for (j = 0; j < num_mods; j++) {
if (xfer_cnt[i][j] != 0) {
dr_fprintf(logfile, "mod %3d => mod %3d: %8u\n", i, j, xfer_cnt[i][j]);
if (i == j)
self_xfer += xfer_cnt[i][j];
else
xmod_xfer += xfer_cnt[i][j];
}
}
}
len = dr_snprintf(msg, sizeof(msg) / sizeof(msg[0]),
"Instrumentation results:\n"
"\t%10llu instructions executed\n"
"\t%10llu (%2.3f%%) cross module indirect branches\n"
"\t%10llu (%2.3f%%) intra-module indirect branches\n",
ins_count, xmod_xfer, 100 * (float)xmod_xfer / ins_count, self_xfer,
100 * (float)self_xfer / ins_count);
DR_ASSERT(len > 0);
NULL_TERMINATE_BUFFER(msg);
dr_fprintf(logfile, "%s\n", msg);
dr_mutex_lock(mod_lock);
for (i = 0; i < num_mods; i++) {
DR_ASSERT(mod_array[i].info != NULL);
dr_free_module_data(mod_array[i].info);
}
dr_mutex_unlock(mod_lock);
dr_mutex_destroy(mod_lock);
log_file_close(logfile);
drx_exit();
if (!drmgr_unregister_bb_instrumentation_event(event_analyze_bb) ||
!drmgr_unregister_module_load_event(event_module_load) ||
!drmgr_unregister_module_unload_event(event_module_unload) ||
drreg_exit() != DRREG_SUCCESS)
DR_ASSERT(false);
drmgr_exit();
}
exit 回调函数,
这个就不说了,一堆打印,回收,解注册的代码。
本例子中涉及到的东西较多,有drreg 的使用,有模块的信息结构 module_data_t 。
例子可以分为两部分:
一部分是 通过 模块加载/卸载事件 来实现对模块信息的记录。
另一部分是 在 basic block 开头插入计数指令,统计代码执行次数; 在 mbr指令之前插入 clean call,判断跳转指令是从哪个模块 跳到哪个模块。
源码:
#include "utils.h"
#include "drmgr.h"
#include "drreg.h"
#include "drx.h"
#include
#define MAX_NUM_MODULES 0x1000
#define UNKNOW_MODULE_IDX (MAX_NUM_MODULES - 1)
typedef struct _module_array_t {
app_pc base;
app_pc end;
bool loaded;
module_data_t *info;
} module_array_t;
/* the last slot is where all non-module address go */
static module_array_t mod_array[MAX_NUM_MODULES];
static uint64 ins_count; /* number of instructions executed in total */
static void *mod_lock;
static int num_mods;
static uint xfer_cnt[MAX_NUM_MODULES][MAX_NUM_MODULES];
static uint64 mod_cnt[MAX_NUM_MODULES];
static file_t logfile;
static bool
module_data_same(const module_data_t *d1, const module_data_t *d2)
{
if (d1->start == d2->start && d1->end == d2->end &&
d1->entry_point == d2->entry_point &&
#ifdef WINDOWS
d1->checksum == d2->checksum && d1->timestamp == d2->timestamp &&
#endif
/* treat two modules w/ no name (there are some) as different */
dr_module_preferred_name(d1) != NULL && dr_module_preferred_name(d2) != NULL &&
strcmp(dr_module_preferred_name(d1), dr_module_preferred_name(d2)) == 0)
return true;
return false;
}
/* Simple clean calls with two arguments will not be inlined, but the context * switch can be optimized for better performance. */
static void
mbr_update(app_pc instr_addr, app_pc target_addr)
{
int i, j;
/* XXX: this can be optimized by walking a tree instead */
/* find the source module */
for (i = 0; i < num_mods; i++) {
if (mod_array[i].loaded && mod_array[i].base <= instr_addr &&
mod_array[i].end > instr_addr)
break;
}
/* if cannot find a module, put it to the last */
if (i == num_mods)
i = UNKNOW_MODULE_IDX;
/* find the target module */
/* quick check if it is the same module */
if (i < UNKNOW_MODULE_IDX && mod_array[i].base <= target_addr &&
mod_array[i].end > target_addr) {
j = i;
} else {
for (j = 0; j < num_mods; j++) {
if (mod_array[j].loaded && mod_array[j].base <= target_addr &&
mod_array[j].end > target_addr)
break;
}
/* if cannot find a module, put it to the last */
if (j == num_mods)
j = UNKNOW_MODULE_IDX;
}
/* this is a racy update, but should be ok to be a few number off */
xfer_cnt[i][j]++;
}
static void
event_exit(void);
static dr_emit_flags_t
event_analyze_bb(void *drcontext, void *tag, instrlist_t *bb, bool for_trace,
bool translating, void **user_data);
static dr_emit_flags_t
event_insert_instrumentation(void *drcontext, void *tag, instrlist_t *bb, instr_t *instr,
bool for_trace, bool translating, void *user_data);
static void
event_module_load(void *drcontext, const module_data_t *info, bool loaded);
static void
event_module_unload(void *drcontext, const module_data_t *info);
DR_EXPORT void
dr_client_main(client_id_t id, int argc, const char *argv[])
{
/* We need no drreg slots ourselves, but we initialize drreg as we call * drreg_restore_app_values(), required since drx_insert_counter_update() * uses drreg when drmgr is used. */
drreg_options_t ops = { sizeof(ops) };
if (!drmgr_init() || drreg_init(&ops) != DRREG_SUCCESS)
DR_ASSERT(false);
drx_init();
/* register events */
dr_register_exit_event(event_exit);
if (!drmgr_register_bb_instrumentation_event(event_analyze_bb,
event_insert_instrumentation, NULL))
DR_ASSERT(false);
drmgr_register_module_load_event(event_module_load);
drmgr_register_module_unload_event(event_module_unload);
mod_lock = dr_mutex_create();
logfile = log_file_open(id, NULL /* drcontext */, NULL /* path */, "modxfer",
#ifndef WINDOWS
DR_FILE_CLOSE_ON_FORK |
#endif
DR_FILE_ALLOW_LARGE);
DR_ASSERT(logfile != INVALID_FILE);
}
static void
event_exit(void)
{
int i;
char msg[512];
int len;
int j;
uint64 xmod_xfer = 0;
uint64 self_xfer = 0;
for (i = 0; i < num_mods; i++) {
dr_fprintf(logfile, "module %3d: %s\n", i,
dr_module_preferred_name(mod_array[i].info) == NULL
? ""
: dr_module_preferred_name(mod_array[i].info));
dr_fprintf(logfile, "%20llu instruction executed\n", mod_cnt[i]);
}
if (mod_cnt[UNKNOW_MODULE_IDX] != 0) {
dr_fprintf(logfile, "unknown modules:\n%20llu instruction executed\n",
mod_cnt[UNKNOW_MODULE_IDX]);
}
for (i = 0; i < MAX_NUM_MODULES; i++) {
for (j = 0; j < num_mods; j++) {
if (xfer_cnt[i][j] != 0) {
dr_fprintf(logfile, "mod %3d => mod %3d: %8u\n", i, j, xfer_cnt[i][j]);
if (i == j)
self_xfer += xfer_cnt[i][j];
else
xmod_xfer += xfer_cnt[i][j];
}
}
}
len = dr_snprintf(msg, sizeof(msg) / sizeof(msg[0]),
"Instrumentation results:\n"
"\t%10llu instructions executed\n"
"\t%10llu (%2.3f%%) cross module indirect branches\n"
"\t%10llu (%2.3f%%) intra-module indirect branches\n",
ins_count, xmod_xfer, 100 * (float)xmod_xfer / ins_count, self_xfer,
100 * (float)self_xfer / ins_count);
DR_ASSERT(len > 0);
NULL_TERMINATE_BUFFER(msg);
DISPLAY_STRING(msg);
dr_fprintf(logfile, "%s\n", msg);
dr_mutex_lock(mod_lock);
for (i = 0; i < num_mods; i++) {
DR_ASSERT(mod_array[i].info != NULL);
dr_free_module_data(mod_array[i].info);
}
dr_mutex_unlock(mod_lock);
dr_mutex_destroy(mod_lock);
log_file_close(logfile);
drx_exit();
if (!drmgr_unregister_bb_instrumentation_event(event_analyze_bb) ||
!drmgr_unregister_module_load_event(event_module_load) ||
!drmgr_unregister_module_unload_event(event_module_unload) ||
drreg_exit() != DRREG_SUCCESS)
DR_ASSERT(false);
drmgr_exit();
}
/* This event is passed the instruction list for the whole bb. */
static dr_emit_flags_t
event_analyze_bb(void *drcontext, void *tag, instrlist_t *bb, bool for_trace,
bool translating, void **user_data)
{
/* Count the instructions and pass the result to event_insert_instrumentation. */
instr_t *instr;
uint num_instrs;
for (instr = instrlist_first_app(bb), num_instrs = 0; instr != NULL;
instr = instr_get_next_app(instr)) {
num_instrs++;
}
*(uint *)user_data = num_instrs;
return DR_EMIT_DEFAULT;
}
/* This event is called separately for each individual instruction in the bb. */
static dr_emit_flags_t
event_insert_instrumentation(void *drcontext, void *tag, instrlist_t *bb, instr_t *instr,
bool for_trace, bool translating, void *user_data)
{
if (drmgr_is_first_instr(drcontext, instr)) {
uint num_instrs = (uint)(ptr_uint_t)user_data;
int i;
app_pc bb_addr = dr_fragment_app_pc(tag);
for (i = 0; i < num_mods; i++) {
if (mod_array[i].loaded && mod_array[i].base <= bb_addr &&
mod_array[i].end > bb_addr)
break;
}
if (i == num_mods)
i = UNKNOW_MODULE_IDX;
/* We pass SPILL_SLOT_MAX+1 as drx will use drreg for spilling. */
drx_insert_counter_update(drcontext, bb, instr, SPILL_SLOT_MAX + 1,
(void *)&mod_cnt[i], num_instrs, DRX_COUNTER_64BIT);
drx_insert_counter_update(drcontext, bb, instr, SPILL_SLOT_MAX + 1,
(void *)&ins_count, num_instrs, DRX_COUNTER_64BIT);
}
if (instr_is_mbr(instr) && !instr_is_return(instr)) {
/* Assuming most of the transfers between modules are paired, we * instrument indirect branches but not returns for better performance. * We assume that most cross module transfers happens via indirect * branches. * Direct branch with DGC or self-modify may also cross modules, but * it should be ok to ignore, and we can handle them more efficiently. */
/* dr_insert_mbr_instrumentation is going to read app values, so we need a * drreg lazy restore "barrier" here. */
drreg_status_t res =
drreg_restore_app_values(drcontext, bb, instr, instr_get_target(instr), NULL);
DR_ASSERT(res == DRREG_SUCCESS || res == DRREG_ERROR_NO_APP_VALUE);
dr_insert_mbr_instrumentation(drcontext, bb, instr, (void *)mbr_update,
SPILL_SLOT_1);
}
return DR_EMIT_DEFAULT;
}
static void
event_module_load(void *drcontext, const module_data_t *info, bool loaded)
{
int i;
dr_mutex_lock(mod_lock);
for (i = 0; i < num_mods; i++) {
/* check if it is the same as any unloaded module */
if (!mod_array[i].loaded && module_data_same(mod_array[i].info, info)) {
mod_array[i].loaded = true;
break;
}
}
if (i == num_mods) {
/* new module */
mod_array[i].base = info->start;
mod_array[i].end = info->end;
mod_array[i].loaded = true;
mod_array[i].info = dr_copy_module_data(info);
num_mods++;
}
DR_ASSERT(num_mods < UNKNOW_MODULE_IDX);
dr_mutex_unlock(mod_lock);
}
static void
event_module_unload(void *drcontext, const module_data_t *info)
{
int i;
dr_mutex_lock(mod_lock);
for (i = 0; i < num_mods; i++) {
if (mod_array[i].loaded && module_data_same(mod_array[i].info, info)) {
/* Some module might be repeatedly loaded and unloaded, so instead * of clearing out the array entry, we keep the data for possible * reuse. */
mod_array[i].loaded = false;
break;
}
}
DR_ASSERT(i < num_mods);
dr_mutex_unlock(mod_lock);
}