| |
| ##--------------------------------------------------------------------## |
| ##--- The core dispatch loop, for jumping to a code address. ---## |
| ##--- dispatch-x86.S ---## |
| ##--------------------------------------------------------------------## |
| |
| /* |
| This file is part of Valgrind, a dynamic binary instrumentation |
| framework. |
| |
| Copyright (C) 2000-2005 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 "pub_tool_basics_asm.h" |
| #include "pub_core_dispatch_asm.h" |
| #include "pub_core_transtab_asm.h" |
| #include "libvex_guest_offsets.h" /* for OFFSET_x86_EIP */ |
| |
| |
| /*------------------------------------------------------------*/ |
| /*--- The dispatch loop. ---*/ |
| /*------------------------------------------------------------*/ |
| |
| /* signature: UWord VG_(run_innerloop) ( void* guest_state ) */ |
| |
| .globl VG_(run_innerloop) |
| VG_(run_innerloop): |
| /* 4(%esp) holds guest_state */ |
| |
| /* ----- entry point to VG_(run_innerloop) ----- */ |
| pushl %ebx |
| pushl %ecx |
| pushl %edx |
| pushl %esi |
| pushl %edi |
| pushl %ebp |
| |
| /* 28(%esp) holds guest_state */ |
| |
| /* Set up the guest state pointer */ |
| movl 28(%esp), %ebp |
| |
| /* fetch %EIP into %eax */ |
| movl OFFSET_x86_EIP(%ebp), %eax |
| |
| /* set host FPU control word to the default mode expected |
| by VEX-generated code. See comments in libvex.h for |
| more info. */ |
| finit |
| pushl $0x027F |
| fldcw (%esp) |
| addl $4, %esp |
| |
| /* set host SSE control word to the default mode expected |
| by VEX-generated code. */ |
| pushl $0x1F80 |
| ldmxcsr (%esp) |
| addl $4, %esp |
| |
| /* set dir flag to known value */ |
| cld |
| |
| /* fall into main loop */ |
| |
| /* Here, %eax is the only live (real) register. The entire |
| simulated state is saved in the ThreadState. */ |
| |
| dispatch_boring: |
| /* save the jump address in the guest state */ |
| movl %eax, OFFSET_x86_EIP(%ebp) |
| |
| /* Are we out of timeslice? If yes, defer to scheduler. */ |
| subl $1, VG_(dispatch_ctr) |
| jz counter_is_zero |
| |
| /* try a fast lookup in the translation cache */ |
| movl %eax, %ebx |
| andl $VG_TT_FAST_MASK, %ebx |
| movl VG_(tt_fast)(,%ebx,4), %ecx |
| cmpl %eax, (%ecx) |
| jnz fast_lookup_failed |
| /* increment bb profile counter */ |
| movl VG_(tt_fastN)(,%ebx,4), %edx |
| incl (%edx) |
| |
| /* Found a match. Call tce[1], which is 8 bytes along, since |
| each tce element is a 64-bit int. */ |
| addl $8, %ecx |
| call *%ecx |
| |
| /* |
| %eax holds destination (original) address. |
| %ebp indicates further details of the control transfer |
| requested to the address in %eax. |
| |
| If ebp is unchanged (== * 28(%esp)), just jump next to %eax. |
| |
| Otherwise fall out, back to the scheduler, and let it |
| figure out what to do next. |
| */ |
| |
| cmpl 28(%esp), %ebp |
| jz dispatch_boring |
| |
| jmp dispatch_exceptional |
| |
| |
| |
| /* All exits from the dispatcher go through here. %eax holds |
| the return value. |
| */ |
| run_innerloop_exit: |
| /* We're leaving. Check that nobody messed with |
| %mxcsr or %fpucw. We can't mess with %eax here as it |
| holds the tentative return value, but any other is OK. */ |
| pushl $0 |
| fstcw (%esp) |
| cmpl $0x027F, (%esp) |
| popl %esi /* get rid of the word without trashing %eflags */ |
| jnz invariant_violation |
| pushl $0 |
| stmxcsr (%esp) |
| andl $0xFFFFFFC0, (%esp) /* mask out status flags */ |
| cmpl $0x1F80, (%esp) |
| popl %esi |
| jnz invariant_violation |
| /* otherwise we're OK */ |
| jmp run_innerloop_exit_REALLY |
| |
| invariant_violation: |
| movl $VG_TRC_INVARIANT_FAILED, %eax |
| jmp run_innerloop_exit_REALLY |
| |
| run_innerloop_exit_REALLY: |
| 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 */ |
| |
| /* save %eax in %EIP and defer to sched */ |
| movl 28(%esp), %edi |
| movl %eax, OFFSET_x86_EIP(%edi) |
| movl %ebp, %eax |
| jmp run_innerloop_exit |
| |
| fast_lookup_failed: |
| /* %EIP is up to date here since dispatch_boring dominates */ |
| addl $1, VG_(dispatch_ctr) |
| movl $VG_TRC_INNER_FASTMISS, %eax |
| jmp run_innerloop_exit |
| |
| counter_is_zero: |
| /* %EIP is up to date here since dispatch_boring dominates */ |
| addl $1, VG_(dispatch_ctr) |
| movl $VG_TRC_INNER_COUNTERZERO, %eax |
| jmp run_innerloop_exit |
| |
| |
| /* Let the linker know we don't need an executable stack */ |
| .section .note.GNU-stack,"",@progbits |
| |
| ##--------------------------------------------------------------------## |
| ##--- end ---## |
| ##--------------------------------------------------------------------## |