blob: 52231946e2393df5326551b002bd1db69d4ba0e3 [file] [log] [blame]
##--------------------------------------------------------------------##
##--- 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 ---##
##--------------------------------------------------------------------##