【DynamoRIO 入門教程】五:modxfer.c

一、功能說明

統計模塊之間轉移指令的個數。強調是通過 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主體函數

1、dr_client_main()

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 裏封裝的函數,用來創建一個日誌文件。

2、event_module_load()

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 一開始就是空的,則將新節點的信息設置爲 新加載 的模塊。

3、event_module_unload()

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 。
然後解鎖。

4、event_analyze_bb()

/* 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語言水平產生了懷疑。這不是個雙重指針嗎,括號裏面的不是指針的強制轉換嗎,爲什麼把一個雙重指針當作 一重指針用呢?
別慌,接着看後面。

5、event_insert_instrumentation()

/* 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() 到底進行了什麼操作。

6、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 二維數組裏。

7、event_exit()

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
                       ? "<unknown>"
                       : 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 <string.h>

#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
                       ? "<unknown>"
                       : 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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章