声明:本文纯属自我学习记录,如果错误,请指正!谢谢!
s2e\libcpu\src\cpu-exec.c
/** qemu部分流程:
* main
* cpu_init
* qemu_init_vcpu
* qemu_tcg_init_vcpu
* qemu_tcg_cpu_thread_fn
*/
//-------------------
/**
* 这个函数是主要的执行循环,这里第一次翻译TB,TB被初始化为(TranslationBlock *tb) ,然后不停的执行异常处理。
* 这其中嵌套了两个无限循环 find tb_find_fast() 和tcg_xxx_tb_exec();[qemu源码是tcg_qemu_tb_exec()]
* s2e代码中这两个无限循环调用在fetch_and_run_tb()函数中
*/
int cpu_exec(CPUArchState *env) { //执行主函数, 主要是处理中断异常, 找到代码翻译块,然后执行.
........
for (;;) { //死循环用于翻译执行代码
ret = process_exceptions(env); //用于捕获异常,处理中断等
if (ret) {
if (ret == EXCP_HLT && env->interrupt_request) {
env->exception_index = -1;
env->halted = 0;
continue;
}
break;
}
if (execution_loop(env)) { //execution_loop主要执行函数
break;
}
} else {
#ifdef CONFIG_SYMBEX
g_sqi.exec.cleanup_tb_exec();
if (!g_sqi.exec.is_runnable()) {
cpu_single_env = NULL;
env->current_tb = NULL;
return EXCP_SE;
}
#endif
/* Reload env after longjmp - the compiler may have smashed all
* local variables as longjmp is marked 'noreturn'. */
env = cpu_single_env;
}
} /* for(;;) */
.........
}
s2e\libcpu\src\cpu-exec.c
static bool execution_loop(CPUArchState *env) {
uintptr_t last_tb = 0;
int last_tb_exit_code = 0;
TranslationBlock *ltb = NULL;
for (;;) {
bool has_interrupt = false;
int tcgvm_run_interrupt = env->tcgvm_run_interrupt;
if(unlikely(!tcgvm_run_interrupt)){
if (process_interrupt_request(env)) {
// Ensure that no TB jump will be modified as
// the program flow was changed
ltb = NULL;
has_interrupt = true;
}
}
if (unlikely(!has_interrupt && env->exit_request)) {
DPRINTF(" execution_loop: exit_request\n");
env->exit_request = 0;
env->exception_index = EXCP_INTERRUPT;
// XXX: return status code instead
cpu_loop_exit(env);
}
.........
//qemu会将翻译好到代码块暂存起来,因此首先会去查看该pc对应的代码是否已经翻译,如果已经存在直接返回,否则就进入tb_find_slow,进行翻译。
last_tb = fetch_and_run_tb(ltb, last_tb_exit_code, env); //fetch_and_run_tb---> tb_find_fast -->tb_find_slow
last_tb_exit_code = last_tb & TB_EXIT_MASK;
ltb = (TranslationBlock *) (last_tb & ~TB_EXIT_MASK);
.................
if (env->kvm_request_interrupt_window && (env->mflags & IF_MASK)) {
env->kvm_request_interrupt_window = 0;
return true;
}
}
return false;
}
s2e\libcpu\src\cpu-exec.c
/**
* struct TranslationBlock {/exec-all.h}:
*
* 结构体TranslationBlock包含下面的成员:PC,
* CS_BASE, Flags (表明TB), tc_ptr (指向这个TB翻译代码的指针), tb_next_offset[2],
* tb_jmp_offset[2] (接下去的Tb), *jmp_next[2], *jmp_first (之前的TB).
*
*/
static uintptr_t fetch_and_run_tb(TranslationBlock *prev_tb, int tb_exit_code, CPUArchState *env) {
uint8_t *tc_ptr;
uintptr_t last_tb;
TranslationBlock *tb = tb_find_fast(env); //
.........
#if defined(CONFIG_SYMBEX)
env->se_current_tb = tb;
int tcgvm_run_interrupt = env->tcgvm_run_interrupt;
if (likely(*g_sqi.mode.fast_concrete_invocation && !tcgvm_run_interrupt)) {
**g_sqi.mode.running_exception_emulation_code = 0;
last_tb = tcg_libcpu_tb_exec(env, tc_ptr);
//printf("===tcg_libcpu_tb_exec: %lu== \n", last_tb);
} else {
last_tb = g_sqi.exec.tb_exec(env, tb);
//printf("===g_sqi.exec.tb_exec last_tb: %lu== \n", last_tb);
}
env->se_current_tb = NULL;
#else
last_tb = tcg_libcpu_tb_exec(env, tc_ptr);
#endif
}
s2e\libcpu\src\cpu-exec.c
/**
* tb_find_fast负责找到下一个需要执行的tb,tcg_libcpu_tb_exec或者g_sqi.exec.tb_exec负责执行这个translation block。
* 在tb_find_fast中,tb本身是有二级缓存的,hash索引是使用代码执行的pc指针,
* tb_find_fast负责找到下一个需要执行的tb,tcg_libcpu_tb_exec或者g_sqi.exec.tb_exec负责执行这个translation block。
* 在tb_find_fast中,tb本身是有二级缓存的,hash索引是使用代码执行的pc指针,
*
* 函数通过调用获得程序指针计数器,然后传到一个哈希函数从 tb_jmp_cache[] (一个哈希表)得到TB的所以,所以使用tb_jmp_cache可以找到下一个TB。如果没有找到下一个TB,则使用tb_find_slow
*/
static inline TranslationBlock *tb_find_fast(CPUArchState *env) {
TranslationBlock *tb;
target_ulong cs_base, pc;
int flags;
/**
* Plugin code cannot usually invalidate the TB cache safely
* because it would also detroy the currently running code.
* Instead, flush the cache at the next TB fetch.
*/
#ifdef CONFIG_SYMBEX
if (tb_invalidate_before_fetch) {
tb_invalidate_before_fetch = 0;
tb_flush(env);
}
#endif
/* we record a subset of the CPU state. It will
always be the same before a given translated block
is executed. */
cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags); //读取缓存就需要当前tb对应的pc值,
tb = env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)]; //在tb_find_slow中,tb本身是有二级缓存的,hash索引是使用代码执行的pc指针
#ifdef CONFIG_SYMBEX
int llvm_nok = env->generate_llvm && (!tb || !tb->llvm_function);
#else
int llvm_nok = 0;
#endif
//因为本身tb_jmp_cache是有hash冲突的
if (unlikely(!tb || tb->pc != pc || tb->cs_base != cs_base || tb->flags != flags || llvm_nok)) {
tb = tb_find_slow(env, pc, cs_base, flags);
} else {gen_intermediate_code
++g_cpu_stats.tb_hits;
}
return tb;
}
s2e\libcpu\src\cpu-exec.c
/**
* tb_find_slow,又是一级缓存,tb_find_physical,缓存放在tcg_ctx全局变量中,以pc对应的hva作为索引。
* 这个是在快速查找失败以后试图去访问物理内存,寻找TB。
*/
static TranslationBlock *tb_find_slow(CPUArchState *env, target_ulong pc, target_ulong cs_base, uint64_t flags) {
TranslationBlock *tb, **ptb1;
unsigned int h;
tb_page_addr_t phys_pc, phys_page1;
target_ulong virt_page2;
tb_invalidated_flag = 0;
/* find translated block using physical mappings */
phys_pc = get_page_addr_code(env, pc);
phys_page1 = phys_pc & TARGET_PAGE_MASK;
h = tb_phys_hash_func(phys_pc);
ptb1 = &tb_phys_hash[h];
for (;;) {
tb = *ptb1;
if (!tb) {
goto not_found;
}
int llvm_nok = 0;
#if defined(CONFIG_SYMBEX_MP)
if (env->generate_llvm && !tb->llvm_function) {
llvm_nok = 1;
}
#endif
if (tb->pc == pc && tb->page_addr[0] == phys_page1 && tb->cs_base == cs_base && tb->flags == flags &&
!llvm_nok) {
/* check next page if needed */
if (tb->page_addr[1] != -1) {
tb_page_addr_t phys_page2;
virt_page2 = (pc & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
phys_page2 = get_page_addr_code(env, virt_page2);
if (tb->page_addr[1] == phys_page2) {
++g_cpu_stats.tb_misses;
}
goto found;
} else {
++g_cpu_stats.tb_misses;
goto found;
}
}
ptb1 = &tb->phys_hash_next;
}
not_found:
/* if no translated code available, then translate it now */
tb = tb_gen_code(env, pc, cs_base, flags, 0); //开始分配一个新的TB,TB的PC是刚刚从CPUstate里面通过using get_page_addr_code()找到的
++g_cpu_stats.tb_regens;
found:
/* Move the last found TB to the head of the list */
if (likely(*ptb1)) {
*ptb1 = tb->phys_hash_next;
tb->phys_hash_next = tb_phys_hash[h];
tb_phys_hash[h] = tb;
}
/* we add the TB in the virtual pc hash table */
env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)] = tb;
return tb;
}
s2e\libcpu\src\exec-tb.c
/**
* 真正的代码翻译在tb_gen_code函数中,它完成了tb的代码翻译过程,新tb加入了缓存中,cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)] = tb。
* tb_gen_code 开始分配一个新的TB,TB的PC是刚刚从CPUstate里面通过using get_page_addr_code()找到的
* ph当调用cpu_gen_code() 以后,接着会调用tb_link_page(),它将增加一个新的TB,并且指向它的物理页表。
*/
TranslationBlock *tb_gen_code(CPUArchState *env, target_ulong pc, target_ulong cs_base, int flags, int cflags) {
TranslationBlock *tb;
tb_page_addr_t phys_pc, phys_page2;
target_ulong virt_page2;
phys_pc = get_page_addr_code(env, pc);
again:
//tb分配并初始化
tb = tb_alloc(pc);
if (!tb) {
/* flush must be done */
tb_flush(env);
/* cannot fail at this point */
tb = tb_alloc(pc);
/* Don't forget to invalidate previous TB info. */
tb_invalidated_flag = 1;
}
tb->cflags = 0;
tb->pc = pc;
tb->cs_base = cs_base;
tb->flags = flags;
tb->cflags = cflags | CF_HAS_INTERRUPT_EXIT;
if (cpu_gen_code(env, tb) < 0) {
tb_flush(env);
goto again;
}
/* check next page if needed */
virt_page2 = (pc + tb->size - 1) & TARGET_PAGE_MASK;
phys_page2 = -1;
if ((pc & TARGET_PAGE_MASK) != virt_page2) {
phys_page2 = get_page_addr_code(env, virt_page2);
}
tb_link_page(tb, phys_pc, phys_page2);
tcg_tb_insert(tb);
return tb;
}
s2e\libcpu\src\translate-all.c
/**
* 在qemu源码中,函数初始化真正的代码生成,在这个函数里面有下面的函数调用:
* gen_intermediate_code(){/target-arch/translate.c}->gen_intermediate_code_internal(){/target-arch/translate.c
* }->disas_insn(){/target-arch/translate.c}
*
* 在qemu源码中,进入如下流程:
* disas_insn(){/target-arch/translate.c} 函数disas_insn() 真正的实现将客户机代码翻译成TCG代码,它通过一长串的switch case,将不同的指令做不同的翻译,最后调用tcg_gen_code。
*
* tcg_gen_code(...){/tcg/tcg.c}: 这个函数将TCG的代码转化成主机代码,这个就不细细说明了,和前面类似。
*
* #define tcg_qemu_tb_exec(...){/tcg/tcg.g}: next_tb = tcg_qemu_tb_exec(tc_ptr) :
* extern uint8_t code_gen_prologue[];
* #define tcg_qemu_tb_exec(tb_ptr) ((long REGPARM(*)(void *)) code_gen_prologue)(tb_ptr)
*/
int cpu_gen_code(CPUArchState *env, TranslationBlock *tb) {
TCGContext *s = tcg_ctx;
uint8_t *gen_code_buf;
int gen_code_size;
tb->tc.ptr = tcg_ctx->code_gen_ptr;
启动tcg上下文
tcg_func_start(s);
//翻译中间码
gen_intermediate_code(env, tb);
/* generate machine code */
gen_code_buf = tb->tc.ptr;
tb->jmp_reset_offset[0] = TB_JMP_RESET_OFFSET_INVALID;
tb->jmp_reset_offset[1] = TB_JMP_RESET_OFFSET_INVALID;
tcg_ctx->tb_jmp_reset_offset = tb->jmp_reset_offset;
if (TCG_TARGET_HAS_direct_jump) {
tcg_ctx->tb_jmp_insn_offset = tb->jmp_target_arg;
tcg_ctx->tb_jmp_target_addr = NULL;
} else {
tcg_ctx->tb_jmp_insn_offset = NULL;
tcg_ctx->tb_jmp_target_addr = tb->jmp_target_arg;
}
gen_code_size = tcg_gen_code(s, tb);
if (unlikely(gen_code_size < 0)) {
return -1;
}
if (libcpu_loglevel_mask(CPU_LOG_TB_OUT_ASM)) {
libcpu_log("----------------\n");
libcpu_log("OUT %#" PRIx64 " - cs:eip=%#" PRIx64 ":%#" PRIx64 "\n", (uint64_t) tb->pc, (uint64_t) tb->cs_base,
(uint64_t) env->eip);
log_host_disas(tb->tc.ptr, gen_code_size);
libcpu_log("\n");
}
int search_size = encode_search(s, tb, (void *) gen_code_buf + gen_code_size);
if (unlikely(search_size < 0)) {
abort();
}
atomic_set(&tcg_ctx->code_gen_ptr,
(void *) ROUND_UP((uintptr_t) gen_code_buf + gen_code_size + search_size, CODE_GEN_ALIGN));
tb->tc.size = gen_code_size;
#if defined(CONFIG_SYMBEX_MP) || defined(STATIC_TRANSLATOR)
if (env->generate_llvm) {
assert(tb->llvm_function == NULL);
tb->llvm_function = tcg_llvm_gen_code(tcg_llvm_translator, s, tb);//LLVM翻译
g_sqi.tb.set_tb_function(tb->se_tb, tb->llvm_function);
}
#endif
/* init jump list */
tb->jmp_lock = SPIN_LOCK_UNLOCKED;
tb->jmp_list_head = (uintptr_t) NULL;
tb->jmp_list_next[0] = (uintptr_t) NULL;
tb->jmp_list_next[1] = (uintptr_t) NULL;
tb->jmp_dest[0] = (uintptr_t) NULL;
tb->jmp_dest[1] = (uintptr_t) NULL;
/* init original jump addresses which have been set during tcg_gen_code() */
if (tb->jmp_reset_offset[0] != TB_JMP_RESET_OFFSET_INVALID) {
tb_reset_jump(tb, 0);
}
if (tb->jmp_reset_offset[1] != TB_JMP_RESET_OFFSET_INVALID) {
tb_reset_jump(tb, 1);
}
#ifdef CONFIG_SYMBEX
//执行翻译中间码(tcg: mirco-op)
tb->instrumented = g_sqi.tb.is_tb_instrumented(tb->se_tb);
g_sqi.tb.increment_tb_stats(tb);
#endif
return 0;
}