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 ---##
+##--------------------------------------------------------------------##