Initial revision
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@2 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/vg_dispatch.S b/coregrind/vg_dispatch.S
new file mode 100644
index 0000000..5223194
--- /dev/null
+++ b/coregrind/vg_dispatch.S
@@ -0,0 +1,379 @@
+
+##--------------------------------------------------------------------##
+##--- The core dispatch loop, for jumping to a code address. ---##
+##--- vg_dispatch.S ---##
+##--------------------------------------------------------------------##
+
+/*
+ This file is part of Valgrind, an x86 protected-mode emulator
+ designed for debugging and profiling binaries on x86-Unixes.
+
+ Copyright (C) 2000-2002 Julian Seward
+ jseward@acm.org
+ Julian_Seward@muraroa.demon.co.uk
+
+ 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 LICENSE.
+*/
+
+#include "vg_constants.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.
+*/
+
+
+/* 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) -----
+ pushal
+ # Set up the baseBlock pointer
+ movl $VG_(baseBlock), %ebp
+
+ # fetch m_eip into %eax
+ movl VGOFF_(m_eip), %esi
+ movl (%ebp, %esi, 4), %eax
+
+ # fall thru to vg_dispatch
+
+.globl VG_(dispatch)
+VG_(dispatch):
+ # %eax holds destination (original) address
+ # To signal any kind of interruption, set vg_dispatch_ctr
+ # to 1, and vg_interrupt_reason to the appropriate value
+ # before jumping here.
+
+ # %ebp indicates further details of the control transfer
+ # requested to the address in %eax. The idea is that we
+ # want to check all jump targets to see if they are either
+ # VG_(signalreturn_bogusRA) or VG_(trap_here), both of which
+ # require special treatment. However, testing all branch
+ # targets is expensive, and anyway in most cases JITter knows
+ # that a jump cannot be to either of these two. We therefore
+ # adopt the following trick.
+ #
+ # If ebp == & VG_(baseBlock), which is what it started out as,
+ # this is a jump for which the JITter knows no check need be
+ # made.
+ #
+ # If it is ebp == VG_EBP_DISPATCH_CHECKED, we had better make
+ # the check.
+ #
+ # If %ebp has any other value, we panic.
+ #
+ # What the JITter assumes is that VG_(signalreturn_bogusRA) can
+ # only be arrived at from an x86 ret insn, and dually that
+ # VG_(trap_here) can only be arrived at from an x86 call insn.
+ # The net effect is that all call and return targets are checked
+ # but straightforward jumps are not.
+ #
+ # Thinks ... is this safe if the client happens to tailcall
+ # VG_(trap_here) ? I dont think that can happen -- if it did
+ # it would be a problem.
+ #
+ cmpl $VG_(baseBlock), %ebp
+ jnz dispatch_checked_maybe
+
+dispatch_unchecked:
+ # save the jump address at VG_(baseBlock)[VGOFF_(m_eip)],
+ # so that if this block takes a fault, we later know where we were.
+ movl VGOFF_(m_eip), %esi
+ movl %eax, (%ebp, %esi, 4)
+
+ # do we require attention?
+ # this check has to be after the call/ret transfer checks, because
+ # we have to ensure that any control transfer following a syscall
+ # return is an ordinary transfer. By the time we get here, we have
+ # established that the next transfer, which might get delayed till
+ # after a syscall return, is an ordinary one.
+ # All a bit subtle ...
+ #OYNK(1001)
+ decl VG_(dispatch_ctr)
+ jz counter_is_zero
+
+ #OYNK(1002)
+ # try a fast lookup in the translation cache
+ movl %eax, %ebx
+ andl $VG_TT_FAST_MASK, %ebx
+ # ebx = tt_fast index
+ movl VG_(tt_fast)(,%ebx,4), %ebx
+ # ebx points at a tt entry
+ # now compare target with the tte.orig_addr field (+0)
+ cmpl %eax, (%ebx)
+ jnz full_search
+ # Found a match. Set the tte.mru_epoch field (+8)
+ # and call the tte.trans_addr field (+4)
+ movl VG_(current_epoch), %ecx
+ movl %ecx, 8(%ebx)
+ call *4(%ebx)
+ jmp VG_(dispatch)
+
+full_search:
+ #no luck? try the full table search
+ pushl %eax
+ call VG_(search_transtab)
+ addl $4, %esp
+
+ # %eax has trans addr or zero
+ cmpl $0, %eax
+ jz need_translation
+ # full table search also zeroes the tte.last_use field,
+ # so we dont have to do so here.
+ call *%eax
+ jmp VG_(dispatch)
+
+need_translation:
+ OYNK(1003)
+ movl $VG_Y_TRANSLATE, VG_(interrupt_reason)
+counter_is_zero:
+ OYNK(1004)
+ popal
+ # ----- (the only) exit point from VG_(run_innerloop) -----
+ # ----- unless of course vg_oursignalhandler longjmp()s
+ # ----- back through it, due to an unmanagable signal
+ ret
+
+
+/* The normal way to get back to the translation loop is to put
+ the address of the next (original) address and return.
+ However, simulation of a RET insn requires a check as to whether
+ the next address is vg_signalreturn_bogusRA. If so, a signal
+ handler is returning, so we need to invoke our own mechanism to
+ deal with that, by calling vg_signal_returns(). This restores
+ the simulated machine state from the VgSigContext structure on
+ the stack, including the (simulated, of course) %eip saved when
+ the signal was delivered. We then arrange to jump to the
+ restored %eip.
+*/
+dispatch_checked_maybe:
+ # Possibly a checked dispatch. Sanity check ...
+ cmpl $VG_EBP_DISPATCH_CHECKED, %ebp
+ jz dispatch_checked
+ # ebp has an invalid value ... crap out.
+ pushl $panic_msg_ebp
+ call VG_(panic)
+ # (never returns)
+
+dispatch_checked:
+ OYNK(2000)
+ # first off, restore %ebp -- since it is currently wrong
+ movl $VG_(baseBlock), %ebp
+
+ # see if we need to mess with stack blocks
+ pushl %ebp
+ pushl %eax
+ call VG_(delete_client_stack_blocks_following_ESP_change)
+ popl %eax
+ popl %ebp
+
+ # is this a signal return?
+ cmpl $VG_(signalreturn_bogusRA), %eax
+ jz dispatch_to_signalreturn_bogusRA
+ # should we intercept this call?
+ cmpl $VG_(trap_here), %eax
+ jz dispatch_to_trap_here
+ # ok, its not interesting. Handle the normal way.
+ jmp dispatch_unchecked
+
+dispatch_to_signalreturn_bogusRA:
+ OYNK(2001)
+ pushal
+ call VG_(signal_returns)
+ popal
+ # %EIP will now point to the insn which should have followed
+ # the signal delivery. Jump to it. Since we no longer have any
+ # hint from the JITter about whether or not it is checkable,
+ # go via the conservative route.
+ movl VGOFF_(m_eip), %esi
+ movl (%ebp, %esi, 4), %eax
+ jmp dispatch_checked
+
+
+/* Similarly, check CALL targets to see if it is the ultra-magical
+ vg_trap_here(), and, if so, act accordingly. See vg_clientmalloc.c.
+ Be careful not to get the real and simulated CPUs,
+ stacks and regs mixed up ...
+*/
+dispatch_to_trap_here:
+ OYNK(111)
+ /* Considering the params to vg_trap_here(), we should have:
+ 12(%ESP) is what_to_do
+ 8(%ESP) is arg2
+ 4(%ESP) is arg1
+ 0(%ESP) is return address
+ */
+ movl VGOFF_(m_esp), %esi
+ movl (%ebp, %esi, 4), %ebx
+ # %ebx now holds simulated %ESP
+ cmpl $0x4000, 12(%ebx)
+ jz handle_malloc
+ cmpl $0x4001, 12(%ebx)
+ jz handle_malloc
+ cmpl $0x4002, 12(%ebx)
+ jz handle_malloc
+ cmpl $0x5000, 12(%ebx)
+ jz handle_free
+ cmpl $0x5001, 12(%ebx)
+ jz handle_free
+ cmpl $0x5002, 12(%ebx)
+ jz handle_free
+ cmpl $6666, 12(%ebx)
+ jz handle_calloc
+ cmpl $7777, 12(%ebx)
+ jz handle_realloc
+ cmpl $8888, 12(%ebx)
+ jz handle_memalign
+ push $panic_msg_trap
+ call VG_(panic)
+ # vg_panic never returns
+
+handle_malloc:
+ # %ESP is in %ebx
+ pushl 12(%ebx)
+ pushl 8(%ebx)
+ call VG_(client_malloc)
+ addl $8, %esp
+ # returned value is in %eax
+ jmp save_eax_and_simulate_RET
+
+handle_free:
+ # %ESP is in %ebx
+ pushl 12(%ebx)
+ pushl 8(%ebx)
+ call VG_(client_free)
+ addl $8, %esp
+ jmp simulate_RET
+
+handle_calloc:
+ # %ESP is in %ebx
+ pushl 8(%ebx)
+ pushl 4(%ebx)
+ call VG_(client_calloc)
+ addl $8, %esp
+ # returned value is in %eax
+ jmp save_eax_and_simulate_RET
+
+handle_realloc:
+ # %ESP is in %ebx
+ pushl 8(%ebx)
+ pushl 4(%ebx)
+ call VG_(client_realloc)
+ addl $8, %esp
+ # returned value is in %eax
+ jmp save_eax_and_simulate_RET
+
+handle_memalign:
+ # %ESP is in %ebx
+ pushl 8(%ebx)
+ pushl 4(%ebx)
+ call VG_(client_memalign)
+ addl $8, %esp
+ # returned value is in %eax
+ jmp save_eax_and_simulate_RET
+
+save_eax_and_simulate_RET:
+ movl VGOFF_(m_eax), %esi
+ movl %eax, (%ebp, %esi, 4) # %eax -> %EAX
+ # set %EAX bits to VALID
+ movl VGOFF_(sh_eax), %esi
+ movl $0x0 /* All 32 bits VALID */, (%ebp, %esi, 4)
+ # fall thru ...
+simulate_RET:
+ # standard return
+ movl VGOFF_(m_esp), %esi
+ movl (%ebp, %esi, 4), %ebx # %ESP -> %ebx
+ movl 0(%ebx), %eax # RA -> %eax
+ addl $4, %ebx # %ESP += 4
+ movl %ebx, (%ebp, %esi, 4) # %ebx -> %ESP
+ jmp dispatch_checked # jump to %eax
+
+.data
+panic_msg_trap:
+.ascii "dispatch_to_trap_here: unknown what_to_do"
+.byte 0
+panic_msg_ebp:
+.ascii "vg_dispatch: %ebp has invalid value!"
+.byte 0
+.text
+
+
+/*------------------------------------------------------------*/
+/*--- A helper for delivering signals when the client is ---*/
+/*--- (presumably) blocked in a system call. ---*/
+/*------------------------------------------------------------*/
+
+/* Returns, in %eax, the next orig_addr to run.
+ The caller needs to decide whether the returned orig_addr
+ requires special handling.
+
+ extern Addr VG_(run_singleton_translation) ( Addr trans_addr )
+*/
+
+/* should we take care to save the FPU state here? */
+
+.globl VG_(run_singleton_translation)
+VG_(run_singleton_translation):
+ movl 4(%esp), %eax # eax = trans_addr
+ pushl %ebx
+ pushl %ecx
+ pushl %edx
+ pushl %esi
+ pushl %edi
+ pushl %ebp
+
+ # set up ebp correctly for translations
+ movl $VG_(baseBlock), %ebp
+
+ # run the translation
+ call *%eax
+
+ # next orig_addr is correctly in %eax already
+
+ popl %ebp
+ popl %edi
+ popl %esi
+ popl %edx
+ popl %ecx
+ popl %ebx
+
+ ret
+
+##--------------------------------------------------------------------##
+##--- end vg_dispatch.S ---##
+##--------------------------------------------------------------------##