// syscall-i386.h structtarget_pt_regs { long ebx; long ecx; long edx; long esi; long edi; long ebp; long eax; int xds; int xes; long orig_eax; long eip; int xcs; long eflags; long esp; int xss; };
在qemu中,可能需要并发执行多个任务。每个任务可以有自己的 CPU 寄存器状态、栈空间等。TaskState可以用于管理这些任务的状态,并在任务切换时保存和恢复寄存器状态,用next指针将任务链接起来。
1 2 3 4 5 6 7 8
// linux-user/qemu.h typedefstructTaskState { structTaskState *next; structtarget_vm86plus_struct *target_v86; structvm86_saved_statevm86_saved_regs; int used; /* non zero if used */ uint8_tstack[0]; } __attribute__((aligned(16))) TaskState;
/* * This structure is used to hold the arguments that are * used when loading binaries. * 此结构体用于记录加载二进制文件时所持有的参数信息 */ structlinux_binprm { char buf[128]; unsignedlong page[MAX_ARG_PAGES]; /* 每个元素都是一块内存的首地址,内存大小4k,用于辅助构建elf镜像的栈底数据 */ unsignedlong p; int sh_bang; int fd; /* elf程序的文件描述符 */ int e_uid, e_gid; int argc; /* 参数个数 */ int envc; /* 二进制文件的名称 */ char * filename; /* Name of binary */ unsignedlong loader; unsignedlong exec; int dont_iput; /* binfmt handler has put inode */ };
// linux-user/elfload.c /* 加载elf文件,这个函数实际实现了一个elf程序加载器 * @param regs 寄存器信息 */ staticintload_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, struct image_info * info) { structelfhdrelf_ex; structelfhdrinterp_elf_ex; structexecinterp_ex; int interpreter_fd = -1; /* avoid warning */ unsignedlong load_addr, load_bias; int load_addr_set = 0; unsignedint interpreter_type = INTERPRETER_NONE; unsignedchar ibcs2_interpreter; int i; void * mapped_addr; structelf_phdr *elf_ppnt; structelf_phdr *elf_phdata; unsignedlong elf_bss, k, elf_brk; int retval; char * elf_interpreter; unsignedlong elf_entry, interp_load_addr = 0; int status; unsignedlong start_code, end_code, end_data; unsignedlong elf_stack; char passed_fileno[6]; ibcs2_interpreter = 0; status = 0; load_addr = 0; load_bias = 0; elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */ assert(elf_ex.e_ident[0] == 0x7f); assert(!strncmp(&elf_ex.e_ident[1], "ELF",3)); /* 校验elf头部标识,保证是elf文件 */ /* 现在读取所有的头部信息 */ elf_phdata = (struct elf_phdr *)malloc(elf_ex.e_phentsize*elf_ex.e_phnum); /* 读取程序头表,加载elf程序,实际只需要头表信息即可 */ retval = lseek(bprm->fd, elf_ex.e_phoff, SEEK_SET); if (retval > 0) { retval = read(bprm->fd, (char *) elf_phdata, elf_ex.e_phentsize * elf_ex.e_phnum); } assert(retval >= 0); elf_ppnt = elf_phdata; elf_bss = 0; elf_brk = 0; elf_stack = ~0UL; elf_interpreter = NULL; start_code = ~0UL; end_code = 0; end_data = 0; /* 这里可以默认elf_phdr为elf32_phdr * Phdr表示Program Header Table 也就是程序表头 */ for(i = 0; i < elf_ex.e_phnum; i++) { /* 链接器,用户写的代码并不能直接跑,还需要一个libc.so来引导,程序入口并不是main * 而是libc中的_start */ if (elf_ppnt->p_type == PT_INTERP) { /* This is the program interpreter used for * shared libraries - for now assume that this * is an a.out format binary */ elf_interpreter = (char *)malloc(elf_ppnt->p_filesz); /* 根据偏移来读取Segment(段) */ retval = lseek(bprm->fd, elf_ppnt->p_offset, SEEK_SET); if (retval >= 0) { retval = read(bprm->fd, elf_interpreter, elf_ppnt->p_filesz); } assert(retval >= 0); /* If the program interpreter is one of these two, then assume an iBCS2 image. Otherwise assume a native linux image. */ /* JRP - Need to add X86 lib dir stuff here... */ /* 链接器的路径 */ if (strcmp(elf_interpreter,"/usr/lib/libc.so.1") == 0 || strcmp(elf_interpreter,"/usr/lib/ld.so.1") == 0) { ibcs2_interpreter = 1; } printf("Using ELF interpreter %s\n", elf_interpreter); /* 打印解析器的路径 */ /* 加载链接器, elf_interpreter记录了链接器的路径 */ retval = open(path(elf_interpreter), O_RDONLY); assert(retval >= 0); interpreter_fd = retval; retval = lseek(interpreter_fd, 0, SEEK_SET); assert(retval >= 0) { retval = read(interpreter_fd,bprm->buf,128); } interp_ex = *((struct exec *) bprm->buf); /* aout exec-header */ interp_elf_ex=*((struct elfhdr *) bprm->buf); /* elf exec-header */ } elf_ppnt++; /* 指向下一个程序头 */ } /* Some simple consistency checks for the interpreter */ if (elf_interpreter) { // 为了简单,后续默认解析器格式为ELF interpreter_type = INTERPRETER_ELF; // ... }
/* OK, we are done with that, now set up the arg stuff, and then start this sucker up */ // ... /* OK, This is the point of no return */ info->end_data = 0; info->end_code = 0; info->start_mmap = (unsignedlong)ELF_START_MMAP; /* 硬编码程序起始地址0x80000000 */ info->mmap = 0; /* 入口地址,规定ELF程序的入口虚拟地址,操作系统在加载完该程序后从这个地址开始执行进程的指令,可重定向 * 文件一般没有入口地址,则这个值为0 */ elf_entry = (unsignedlong) elf_ex.e_entry;
/* Do this so that we can load the interpreter, if need be. We will change some of these later */ info->rss = 0; bprm->p = setup_arg_pages(bprm->p, bprm, info); /* 初始化栈 */ info->start_stack = bprm->p; /* 记录下栈顶 */ /* Now we do a little grungy work by mmaping the ELF image into * the correct location in memory. At this point, we assume that * the image should be loaded at fixed address, not at a variable * address. * 将ELF镜像映射到内存中正确的位置,在这个时候,我们假定镜像应当被加载到固定的地址,而不是一个可变的地址 * ELF之中,segment会按照虚拟地址从小到大排序.正常情况就第一个Segment为代码段,最后一个为数据段 * Program Headers: * Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align * PHDR 0x000040 0x00040 0x00040 0x0000002d8 0x0000002d8 R 0x8 * INTERP 0x000318 0x00318 0x00318 0x00000001c 0x00000001c R 0x1 * [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] * LOAD 0x001000 0x01000 0x01000 0x000000225 0x000000225 R E 0x1000 # 代码段 * LOAD 0x002000 0x02000 0x02000 0x000000190 0x000000190 R 0x1000 * LOAD 0x002db8 0x03db8 0x03db8 0x000000260 0x000000268 RW 0x1000 # 数据段 */ for (i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) { int elf_prot = 0; /* 属性 */ int elf_flags = 0; unsignedlong error; /* 只有LOAD类型的segment才是需要被映射的 */ if (elf_ppnt->p_type != PT_LOAD) /* 只处理LOAD类型的segment */ continue; if (elf_ppnt->p_flags & PF_R) elf_prot |= PROT_READ; if (elf_ppnt->p_flags & PF_W) elf_prot |= PROT_WRITE; if (elf_ppnt->p_flags & PF_X) elf_prot |= PROT_EXEC; elf_flags = MAP_PRIVATE | MAP_DENYWRITE; if (elf_ex.e_type == ET_EXEC || load_addr_set) { elf_flags |= MAP_FIXED; /* 映射到固定的位置 */ } elseif (elf_ex.e_type == ET_DYN) /* 动态库 */ { /* Try and get dynamic programs out of the way of the default mmap base, as well as whatever program they might try to exec. This is because the brk will follow the loader, and is not movable. */ /* NOTE: for qemu, we do a big mmap to get enough space without harcoding any address */ error = (unsignedlong)mmap4k(NULL, ET_DYN_MAP_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); /* 到这里,error记录的是实际映射的位置,这个和elf中期望映射的地址其实还是有偏差的 * 因此就出现了load_bias */ load_bias = X86_ELF_PAGESTART(error - elf_ppnt->p_vaddr); } /* 这里直接做了映射操作,为每一个segment都执行一个映射 * elf_ppnt->p_vaddr记录的是虚拟地址 */ error = (unsignedlong)mmap4k( X86_ELF_PAGESTART(load_bias + elf_ppnt->p_vaddr), /* elf中指定了段加载的虚拟地址 */ (elf_ppnt->p_filesz + X86_ELF_PAGEOFFSET(elf_ppnt->p_vaddr)), /* p_filesz记录了段大小 */ elf_prot, /* 段属性 */ (MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE), bprm->fd, /* 文件映射 */ (elf_ppnt->p_offset - X86_ELF_PAGEOFFSET(elf_ppnt->p_vaddr))); if (!load_addr_set) { load_addr_set = 1; /* 加载地址确定了 */ /* load_addr记录实际的虚拟加载地址 * 这里举一个简单的例子: * Program Headers: * Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align * LOAD 0x000000 0x08048000 0x08048000 0x000ac 0x000ac R E 0x1000 * 在这个segment中, p_vaddr为0x08048000, p_offset为0 * load_bias在大多数情况下,都是0 */ load_addr = elf_ppnt->p_vaddr - elf_ppnt->p_offset; /* 也就是从虚拟地址load_addr处开始映射segment(段) */ if (elf_ex.e_type == ET_DYN) /* 动态库 */ { load_bias += error - X86_ELF_PAGESTART(load_bias + elf_ppnt->p_vaddr); load_addr += load_bias; } } /* 一般来说,代码段在低地址,数据段在高地址 */ k = elf_ppnt->p_vaddr; /* 期望的虚拟地址 */ if (k < start_code) start_code = k; /* 记录下代码段的起始地址(第一个load类型的segment的起始地址) */ /* p_filesz表示segment在elf文件中所占用空间的长度 */ k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz; /* 一般而言,p_filesz <= p_memsz */ /* .bss段不占用文件空间,但是要置为0 */ if (k > elf_bss) elf_bss = k; /* elf_bss指向bss段的开始 */ if ((elf_ppnt->p_flags & PF_X) && end_code < k) /* 可执行的段就是代码段 */ end_code = k; /* end_code是代码段的尾部地址 */ if (end_data < k) end_data = k; /* p_memsz表示segment在进程虚拟地址空间中所占的长度 */ k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz; if (k > elf_brk) elf_brk = k; } /* elf_entry是ELF程序的入口虚拟地址,在x86下,这个值一般为0x804874 */ elf_entry += load_bias; elf_bss += load_bias; elf_brk += load_bias; start_code += load_bias; end_code += load_bias; end_data += load_bias; if (elf_interpreter) /* 动态链接器 */ { /* elf_entry是程序应当执行的第一条指令所在的虚拟地址 */ elf_entry = load_elf_interp(&interp_elf_ex, interpreter_fd, &interp_load_addr); /* 加载elf格式的动态链接器 */ // ... assert(elf_entry); } free(elf_phdata); if (interpreter_type != INTERPRETER_AOUT) close(bprm->fd); info->personality = (ibcs2_interpreter ? PER_SVR4 : PER_LINUX); bprm->p = (unsignedlong) create_elf_tables((char *)bprm->p, bprm->argc, bprm->envc, (interpreter_type == INTERPRETER_ELF ? &elf_ex : NULL), load_addr, load_bias, interp_load_addr, (interpreter_type == INTERPRETER_AOUT ? 0 : 1), info); info->start_brk = info->brk = elf_brk; /* 程序可以从这个位置开始执行brk */ info->end_code = end_code; info->start_code = start_code; info->end_data = end_data; info->start_stack = bprm->p; /* 堆栈位置 */ /* Calling set_brk effectively mmaps the pages that we need for the bss and break sections */ set_brk(elf_bss, elf_brk); /* elf_bss以及elf_brk之间的内存为bss段 */ padzero(elf_bss); /* 将bss段清零 */ // ... info->entry = elf_entry; /* 程序的执行的第一条指令的地址来自动态解析器,一般是_start函数 */ return0; }
来看堆栈初始化代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
//... /* Do this so that we can load the interpreter, if need be. We will change some of these later */ info->rss = 0; bprm->p = setup_arg_pages(bprm->p, bprm, info); /* 初始化栈 */ info->start_stack = bprm->p; /* 记录下栈顶 */ //... /* 堆栈初始化的收尾工作在create_elf_tables中执行 */ bprm->p = (unsignedlong) create_elf_tables((char *)bprm->p, bprm->argc, bprm->envc, (interpreter_type == INTERPRETER_ELF ? &elf_ex : NULL), load_addr, load_bias, interp_load_addr, (interpreter_type == INTERPRETER_AOUT ? 0 : 1), info);
/* Symbolic values for the entries in the auxiliary table put on the initial stack */ #define AT_NULL 0 /* end of vector */ #define AT_IGNORE 1 /* entry should be ignored */ #define AT_EXECFD 2 /* file descriptor of program */ #define AT_PHDR 3 /* program headers for program */ #define AT_PHENT 4 /* size of program header entry */ #define AT_PHNUM 5 /* number of program headers */ #define AT_PAGESZ 6 /* system page size */ #define AT_BASE 7 /* base address of interpreter */ #define AT_FLAGS 8 /* flags */ #define AT_ENTRY 9 /* entry point of program */ #define AT_NOTELF 10 /* program is not ELF */ #define AT_UID 11 /* real uid */ #define AT_EUID 12 /* effective uid */ #define AT_GID 13 /* real gid */ #define AT_EGID 14 /* effective gid */ #define AT_PLATFORM 15 /* string identifying CPU for optimizations */ #define AT_HWCAP 16 /* arch dependent hints at CPU capabilities */ #define AT_CLKTCK 17 /* frequency at which times() increments */
/* 加载动态链接器 * @return 加载的动态链接器的第一条指令的地址 */ staticunsignedlongload_elf_interp(struct elfhdr * interp_elf_ex, int interpreter_fd, unsignedlong *interp_load_addr) { structelf_phdr *elf_phdata =NULL; structelf_phdr *eppnt; unsignedlong load_addr = 0; int load_addr_set = 0; int retval; unsignedlong last_bss, elf_bss; unsignedlong error; int i; elf_bss = 0; last_bss = 0; error = 0; // ... 跳过校验代码 /* Now read in all of the header information */ if (sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > X86_PAGE_SIZE) return ~0UL; elf_phdata = (struct elf_phdr *) malloc(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum); // ... retval = lseek(interpreter_fd, interp_elf_ex->e_phoff, SEEK_SET); if (retval >= 0) { retval = read(interpreter_fd, (char *) elf_phdata, sizeof(struct elf_phdr) * interp_elf_ex->e_phnum); } assert(retval >= 0);
if (interp_elf_ex->e_type == ET_DYN) { /* in order to avoid harcoding the interpreter load address in qemu, we allocate a big enough memory zone */ error = (unsignedlong)mmap4k(NULL, INTERP_MAP_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); assert(error != -1); load_addr = error; /* 因为是动态库,所以加载地址随机 */ load_addr_set = 1; }
eppnt = elf_phdata; for (i = 0; i < interp_elf_ex->e_phnum; i++, eppnt++) /* 遍历程序头 */ if (eppnt->p_type == PT_LOAD) { int elf_type = MAP_PRIVATE | MAP_DENYWRITE; int elf_prot = 0; unsignedlong vaddr = 0; unsignedlong k; if (eppnt->p_flags & PF_R) elf_prot = PROT_READ; if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE; if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC; if (interp_elf_ex->e_type == ET_EXEC || load_addr_set) { elf_type |= MAP_FIXED; vaddr = eppnt->p_vaddr; } /* 执行映射操作 */ error = (unsignedlong)mmap4k(load_addr+X86_ELF_PAGESTART(vaddr), eppnt->p_filesz + X86_ELF_PAGEOFFSET(eppnt->p_vaddr), elf_prot, elf_type, interpreter_fd, eppnt->p_offset - X86_ELF_PAGEOFFSET(eppnt->p_vaddr)); // ... ignore error if (!load_addr_set && interp_elf_ex->e_type == ET_DYN) { load_addr = error; load_addr_set = 1; } /* * Find the end of the file mapping for this phdr, and keep * track of the largest address we see for this. * elf_bss记录最大的地址 */ k = load_addr + eppnt->p_vaddr + eppnt->p_filesz; if (k > elf_bss) elf_bss = k; /* * Do the same thing for the memory mapping - between * elf_bss and last_bss is the bss section. */ k = load_addr + eppnt->p_memsz + eppnt->p_vaddr; if (k > last_bss) last_bss = k; }
/* Now use mmap to map the library into memory. */ close(interpreter_fd); /* * Now fill out the bss section. First pad the last page up * to the page boundary, and then perform a mmap to make sure * that there are zeromapped pages up to and including the last * bss page. * 现在填充bss section. */ padzero(elf_bss); elf_bss = X86_ELF_PAGESTART(elf_bss + ALPHA_PAGE_SIZE - 1); /* What we have mapped so far */ /* Map the last of the bss segment */ if (last_bss > elf_bss) { mmap4k(elf_bss, last_bss-elf_bss, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); } free(elf_phdata); *interp_load_addr = load_addr; return ((unsignedlong) interp_elf_ex->e_entry) + load_addr; }
/* handle VM86 interrupt (NOTE: the CPU core currently does not support TSS interrupt revectoring, so this code is always executed) */ staticvoiddo_int(CPUX86State *env, int intno) { TaskState *ts = env->opaque; uint32_t *int_ptr, segoffs;
if (env->segs[R_CS] == TARGET_BIOSSEG) goto cannot_handle; /* XXX: I am not sure this is really useful */ if (is_revectored(intno, &ts->target_v86->int_revectored)) goto cannot_handle; if (intno == 0x21 && is_revectored((env->regs[R_EAX] >> 8) & 0xff, &ts->target_v86->int21_revectored)) goto cannot_handle; int_ptr = (uint32_t *)(intno << 2); segoffs = tswap32(*int_ptr); if ((segoffs >> 16) == TARGET_BIOSSEG) goto cannot_handle; #ifdef DEBUG_VM86 printf("VM86: emulating int 0x%x. CS:IP=%04x:%04x\n", intno, segoffs >> 16, segoffs & 0xffff); #endif /* save old state */ pushw(env, get_vflags(env)); pushw(env, env->segs[R_CS]); pushw(env, env->eip); /* goto interrupt handler */ env->eip = segoffs & 0xffff; /* 直接设置eip寄存器,让程序跑到中断向量处执行 */ cpu_x86_load_seg(env, R_CS, segoffs >> 16); env->eflags &= ~(VIF_MASK | TF_MASK); return; cannot_handle: #ifdef DEBUG_VM86 printf("VM86: return to 32 bits int 0x%x\n", intno); #endif return_to_32bit(env, TARGET_VM86_INTx | (intno << 8)); }
typedefstructTranslationBlock { unsignedlong pc; /* simulated PC corresponding to this block (EIP + CS base) */ unsignedlong cs_base; /* CS base for this block */ unsignedint flags; /* flags defining in which context the code was generated */ uint8_t *tc_ptr; /* pointer to the translated code */ structTranslationBlock *hash_next;/* next matching block */ } TranslationBlock;
typedefstructDisasContexttypedefstructDisasContext { /* current insn context */ int override; /* -1 if no override */ int prefix; int aflag, dflag; uint8_t *pc; /* pc = eip + cs_base */ int is_jmp; /* 1 = means jump (stop translation), 2 means CPU static state change (stop translation) */ /* current block context */ uint8_t *cs_base; /* base of CS segment */ int code32; /* 32 bit code segment */ int ss32; /* 32 bit stack segment */ int cc_op; /* current CC operation */ int addseg; /* non zero if either DS/ES/SS have a non zero base */ int f_st; /* currently unused */ int vm86; /* vm86 mode */ } DisasContext;
// linux-user/syscall.c longdo_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, long arg4, long arg5, long arg6) { long ret; structstatst; structkernel_statfs *stfs; #ifdef DEBUG gemu_log("syscall %d\n", num); #endif switch(num) { case TARGET_NR_exit: #ifdef HAVE_GPROF _mcleanup(); #endif /* XXX: should free thread stack and CPU env */ _exit(arg1); ret = 0; /* avoid warning */ break; case TARGET_NR_read: ret = get_errno(read(arg1, (void *)arg2, arg3)); break; case TARGET_NR_write: ret = get_errno(write(arg1, (void *)arg2, arg3)); break; case TARGET_NR_open: ret = get_errno(open(path((constchar *)arg1), arg2, arg3)); break; case TARGET_NR_close: ret = get_errno(close(arg1)); break; case TARGET_NR_brk: ret = do_brk((char *)arg1); break; case TARGET_NR_fork: ret = get_errno(do_fork(cpu_env, SIGCHLD, 0)); break; case TARGET_NR_waitpid:
/* CPU标志(flags)计算优化,这个函数属于优化,从功能的角度,可以省略掉 */ staticvoidoptimize_flags(uint16_t *opc_buf, int opc_buf_len) { uint16_t *opc_ptr; int live_flags, write_flags, op;
opc_ptr = opc_buf + opc_buf_len; /* 指向最后一条指令 */ /* live_flags contains the flags needed by the next instructions in the code. At the end of the bloc, we consider that all the flags are live. */ /* live_flags包含被下一条指令所需的标志,在bloc的最后,我们认为所有的标志都需要 */ live_flags = CC_OSZAPC; while (opc_ptr > opc_buf) { op = *--opc_ptr; /* 从后往前遍历 */ /* if none of the flags written by the instruction is used, then we can try to find a simpler instruction */ /* 如果指令不会更改任何标志,那么我们可以尝试将其替换为一条更加简单的指令 * live_flags & write_flags为0表示本条指令不会更改下一条指令所读取的标志 * 更加直白一点,那就是本条指令和下一条指令无关联 */ write_flags = opc_write_flags[op]; if ((live_flags & write_flags) == 0) { *opc_ptr = opc_simpler[op]; } /* compute the live flags before the instruction */ /* 计算该指令的前一条指令所需要的live flags */ live_flags &= ~write_flags; /* 指令需要读取的标志,记住,这个live_flags供上一条指令使用,它表示下一条指令(也就是 * 本指令)所需要的标志 */ live_flags |= opc_read_flags[op]; } }
// cpu-i386.h /* CPUX86State这个结构体记录qemu所模拟的x86 cpu的内部状态 */ typedefstructCPUX86State { /* standard registers */ uint32_t regs[8]; uint32_t eip; uint32_t eflags; /* eflags register. During CPU emulation, CC flags and DF are set to zero because they are store elsewhere */
/* emulator internal eflags handling */ uint32_t cc_src; uint32_t cc_dst; uint32_t cc_op; int32_t df; /* D flag : 1 if D = 0, -1 if D = 1 */ // ... } // op-i386.c /* QEMU存储一个操作数CC_SRC,结果CC_DST,操作的类型CC_OP * 举一个例子 R = A + B * CC_SRC=A * CC_DST=R * CC_OP=CC_OP_ADDL */ #define CC_SRC (env->cc_src) #define CC_DST (env->cc_dst) #define CC_OP (env->cc_op)
typedefstructCCTable { int (*compute_all)(void); /* return all the flags */ int (*compute_c)(void); /* return the C flag */ } CCTable;
// cpu-i386.h enum { CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */ CC_OP_EFLAGS, /* all cc are explicitely computed, CC_SRC = flags */ CC_OP_MUL, /* modify all flags, C, O = (CC_SRC != 0) */
staticintcompute_all_mul(void) { int cf, pf, af, zf, sf, of; cf = (CC_SRC != 0); /* cf的值是一个估计值,只要CC_SRC不为0,那么乘法就有可能会进位,因此cf为1 */ pf = 0; /* undefined */ af = 0; /* undefined */ zf = 0; /* undefined */ sf = 0; /* undefined */ of = cf << 11; /* overflow */ return cf | pf | af | zf | sf | of; } /* 获取cf标志 */ staticintcompute_c_mul(void) { int cf; cf = (CC_SRC != 0); return cf; }
CC_OP的优化
在翻译中间码的时候,qemu就已经在考虑cc_op了。
1 2 3 4 5 6 7 8 9 10 11 12 13
typedefstructDisasContext { /* current insn context */ // ... uint8_t *pc; /* pc = eip + cs_base */ int is_jmp; /* 1 = means jump (stop translation), 2 means CPU static state change (stop translation) */ /* current block context */ uint8_t *cs_base; /* base of CS segment */ int code32; /* 32 bit code segment */ int ss32; /* 32 bit stack segment */ int cc_op; /* current CC operation */ // ... } DisasContext;
dc->is_jmp = 0; pc_ptr = pc_start; do { ret = disas_insn(dc, pc_ptr); // 反汇编指令 if (ret == -1) { /* we trigger an illegal instruction operation only if it is the first instruction. Otherwise, we simply stop generating the code just before it */ if (pc_ptr == pc_start) return-1; else break; } pc_ptr = (void *)ret; } while (!dc->is_jmp && gen_opc_ptr < gen_opc_end); // ... }
intcpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size, int *gen_code_size_ptr, uint8_t *pc_start, uint8_t *cs_base, int flags) { DisasContext dc1, *dc = &dc1; // .... /* we must store the eflags state if it is not already done */ if (dc->cc_op != CC_OP_DYNAMIC) gen_op_set_cc_op(dc->cc_op); // ...
/* optimize flag computations */ optimize_flags(gen_opc_buf, gen_opc_ptr - gen_opc_buf);
// EXTERNAL_CHANNEL
COMMENTS