/* * Copyright (C) 2021, Ivanov Arkady * * Drcov - a DynamoRIO-based tool that collects coverage information * from a binary. Primary goal this script is to have coverage log * files that work in Lighthouse. * * License: GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. */ #include #include #include #include #include #include #include #include #include QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; static char header[] = "DRCOV VERSION: 2\n" "DRCOV FLAVOR: drcov-64\n" "Module Table: version 2, count 1\n" "Columns: id, base, end, entry, path\n"; static FILE *fp; static const char *file_name = "file.drcov.trace"; static GMutex lock; typedef struct { uint32_t start; uint16_t size; uint16_t mod_id; bool exec; } bb_entry_t; /* Translated blocks */ static GPtrArray *blocks; static void printf_header(unsigned long count) { fprintf(fp, "%s", header); const char *path = qemu_plugin_path_to_binary(); uint64_t start_code = qemu_plugin_start_code(); uint64_t end_code = qemu_plugin_end_code(); uint64_t entry = qemu_plugin_entry_code(); fprintf(fp, "0, 0x%lx, 0x%lx, 0x%lx, %s\n", start_code, end_code, entry, path); fprintf(fp, "BB Table: %ld bbs\n", count); } static void printf_char_array32(uint32_t data) { const uint8_t *bytes = (const uint8_t *)(&data); fwrite(bytes, sizeof(char), sizeof(data), fp); } static void printf_char_array16(uint16_t data) { const uint8_t *bytes = (const uint8_t *)(&data); fwrite(bytes, sizeof(char), sizeof(data), fp); } static void printf_el(gpointer data, gpointer user_data) { bb_entry_t *bb = (bb_entry_t *)data; if (bb->exec) { printf_char_array32(bb->start); printf_char_array16(bb->size); printf_char_array16(bb->mod_id); } g_free(bb); } static void count_block(gpointer data, gpointer user_data) { unsigned long *count = (unsigned long *) user_data; bb_entry_t *bb = (bb_entry_t *)data; if (bb->exec) { *count = *count + 1; } } static void plugin_exit(qemu_plugin_id_t id, void *p) { unsigned long count = 0; g_mutex_lock(&lock); g_ptr_array_foreach(blocks, count_block, &count); /* Print function */ printf_header(count); g_ptr_array_foreach(blocks, printf_el, NULL); /* Clear */ g_ptr_array_free(blocks, true); fclose(fp); g_mutex_unlock(&lock); } static void plugin_init(void) { fp = fopen(file_name, "wb"); blocks = g_ptr_array_sized_new(128); } static void vcpu_tb_exec(unsigned int cpu_index, void *udata) { bb_entry_t *bb = (bb_entry_t *) udata; g_mutex_lock(&lock); bb->exec = true; g_mutex_unlock(&lock); } static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) { uint64_t pc = qemu_plugin_tb_vaddr(tb); size_t n = qemu_plugin_tb_n_insns(tb); g_mutex_lock(&lock); bb_entry_t *bb = g_new0(bb_entry_t, 1); for (int i = 0; i < n; i++) { bb->size += qemu_plugin_insn_size(qemu_plugin_tb_get_insn(tb, i)); } bb->start = pc; bb->mod_id = 0; bb->exec = false; g_ptr_array_add(blocks, bb); g_mutex_unlock(&lock); qemu_plugin_register_vcpu_tb_exec_cb(tb, vcpu_tb_exec, QEMU_PLUGIN_CB_NO_REGS, (void *)bb); } QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, int argc, char **argv) { for (int i = 0; i < argc; i++) { g_autofree char **tokens = g_strsplit(argv[i], "=", 2); if (g_strcmp0(tokens[0], "filename") == 0) { file_name = g_strdup(tokens[1]); } } plugin_init(); qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); return 0; }