| |
| ##--------------------------------------------------------------------## |
| ##--- The core dispatch loop, for jumping to a code address. ---## |
| ##--- vg_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 vg_dispatch.S ---## |
| ##--------------------------------------------------------------------## |