##--------------------------------------------------------------------## ##--- The core dispatch loop, for jumping to a code address. ---## ##--- x86/dispatch.S ---## ##--------------------------------------------------------------------## /* This file is part of Valgrind, an extensible x86 protected-mode emulator for monitoring program execution on x86-Unixes. Copyright (C) 2000-2004 Julian Seward jseward@acm.org This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The GNU General Public License is contained in the file COPYING. */ #include "core_asm.h" /*------------------------------------------------------------*/ /*--- The normal-case dispatch machinery. ---*/ /*------------------------------------------------------------*/ /* To transfer to an (original) code address, load it into %eax and jump to vg_dispatch. This fragment of code tries to find the address of the corresponding translation by searching the translation table. If it fails, a new translation is made, added to the translation table, and then jumped to. Almost all the hard work is done by C routines; this code simply handles the common case fast -- when the translation address is found in the translation cache. At entry, %eax is the only live (real-machine) register; the entire simulated state is tidily saved in vg_m_state. */ #define TT_LOOKUP(reg, fail) \ movl %eax, reg; \ andl $VG_TT_FAST_MASK, reg; \ movl VG_(tt_fast)(,reg,4), reg; \ cmpl %eax, (reg); \ jnz fail /* The C world needs a way to get started simulating. So we provide a function void vg_run_innerloop ( void ), which starts running from vg_m_eip, and exits when the counter reaches zero. This loop can also exit if vg_oursignalhandler() catches a non-resumable signal, for example SIGSEGV. It then longjmp()s back past here. */ .globl VG_(run_innerloop) VG_(run_innerloop): /* OYNK(1000) */ /* ----- entry point to VG_(run_innerloop) ----- */ pushl %ebx pushl %ecx pushl %edx pushl %esi pushl %edi pushl %ebp /* check to see if we're doing pointer checking */ movb VG_(clo_pointercheck), %al testb %al,%al jz 1f pushl %fs /* save %fs */ mov $(VG_POINTERCHECK_SEGIDX << 3) + 7, %eax /* load new %fs */ movw %ax,%fs 1: /* Set up the baseBlock pointer */ movl $VG_(baseBlock), %ebp /* fetch m_eip into %eax */ movl VGOFF_(m_eip), %esi movl (%ebp, %esi, 4), %eax dispatch_main: /* Jump here to do a new dispatch. %eax holds destination (original) address. %ebp indicates further details of the control transfer requested to the address in %eax. If ebp == & VG_(baseBlock), just jump next to %eax. If ebp == VG_EBP_JMP_SYSCALL, do a system call before continuing at eax. If ebp == VG_EBP_JMP_CLIENTREQ, do a client request before continuing at eax. If %ebp has any other value, we panic. */ /*cmpl $VG_(baseBlock), %ebp*/ /*jnz dispatch_exceptional*/ /* fall into main loop */ dispatch_boring: /* save the jump address at VG_(baseBlock)[VGOFF_(m_eip)] */ movl VGOFF_(m_eip), %esi movl %eax, (%ebp, %esi, 4) /* Are we out of timeslice? If yes, defer to scheduler. */ cmpl $0, VG_(dispatch_ctr) jz counter_is_zero /* try a fast lookup in the translation cache */ TT_LOOKUP(%ebx, fast_lookup_failed) /* Found a match. Call the tce.payload field (+VG_CODE_OFFSET) */ addl $VG_CODE_OFFSET, %ebx incl VG_(unchained_jumps_done) /* update stats */ call *%ebx cmpl $VG_(baseBlock), %ebp jz dispatch_boring jmp dispatch_exceptional fast_lookup_failed: /* %EIP is up to date here since dispatch_boring dominates */ movl $VG_TRC_INNER_FASTMISS, %eax jmp run_innerloop_exit counter_is_zero: /* %EIP is up to date here since dispatch_boring dominates */ movl $VG_TRC_INNER_COUNTERZERO, %eax jmp run_innerloop_exit run_innerloop_exit: movb VG_(clo_pointercheck), %bl testb %bl,%bl jz 1f /* restore %fs */ popl %fs 1: popl %ebp popl %edi popl %esi popl %edx popl %ecx popl %ebx ret /* Other ways of getting out of the inner loop. Placed out-of-line to make it look cleaner. */ dispatch_exceptional: /* this is jumped to only, not fallen-through from above */ cmpl $VG_TRC_INNER_COUNTERZERO, %ebp jz counter_is_zero /* save %eax in %EIP and defer to sched */ movl VGOFF_(m_eip), %esi movl %eax, VG_(baseBlock)(,%esi, 4) movl %ebp, %eax jmp run_innerloop_exit /* This is the translation chainer, our run-time linker, if you like. VG_(patch_me) patches the call instruction in the jump site with a jump to the generated code for the branch target. %eax contains the original program's EIP - if we get a hit in tt_fast, then the call is patched into a jump; otherwise it simply drops back into the dispatch loop for normal processing. The callsite is expected to look like: call VG_(patch_me) it will be transformed into jmp $TARGETADDR The environment we're expecting on entry is: %eax = branch target address (original code EIP) *(%esp) = just after call */ .globl VG_(patch_me) VG_(patch_me): /* try a fast lookup in the translation cache */ TT_LOOKUP(%ebx, 1f) /* Patch call instruction at callsite into a chained jmp */ popl %eax /* eax = just after (VG_PATCHME_CALLSZ byte) call */ addl $VG_CODE_OFFSET, %ebx /* ebx = target eip */ subl %eax, %ebx /* ebx = delta */ movb $0xE9, -(VG_PATCHME_CALLSZ-0)(%eax) /* 0xe9 = jmp */ movl %ebx, -(VG_PATCHME_CALLSZ-1)(%eax) /* store delta */ addl %eax, %ebx incl VG_(bb_enchain_count) /* update stats */ jmp *%ebx /* jmp to dest */ /* tt_fast miss: return into main dispatch loop */ 1: addl $4, %esp /* remove our call address */ ret /* return into main dispatch loop above */ .data panic_msg_ebp: .ascii "vg_dispatch: %ebp has invalid value!" .byte 0 .text /* Let the linker know we don't need an executable stack */ .section .note.GNU-stack,"",@progbits ##--------------------------------------------------------------------## ##--- end ---## ##--------------------------------------------------------------------##