blob: 63ee590153cd8c350e9c8f6d08e17edf75ab2d61 [file] [log] [blame]
##--------------------------------------------------------------------##
##--- Startup and shutdown code for Valgrind. ---##
##--- vg_startup.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
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"
#---------------------------------------------------------------------
#
# Startup and shutdown code for Valgrind. Particularly hairy.
#
# The dynamic linker, ld.so, will run the contents of the .init
# section, once it has located, mmap-d and and linked the shared
# libraries needed by the program. Valgrind is itself a shared
# library. ld.so then runs code in the .init sections of each
# library in turn, in order to give them a chance to initialise
# themselves. We hijack this mechanism. Our startup routine
# does return -- and execution continues -- except on the
# synthetic CPU, not the real one. But ld.so, and the program
# it is starting, cant tell the difference.
#
# The management apologise for the lack of apostrophes in these
# comments. GNU as seems to object to them, for some reason.
.section .init
call VG_(startup)
.section .fini
call VG_(shutdown)
.section .data
valgrind_already_initted:
.word 0
.section .text
.global VG_(startup)
VG_(startup):
cmpl $0, valgrind_already_initted
je really_start_up
ret
really_start_up:
movl $1, valgrind_already_initted
# Record %esp as it was when we got here. This is because argv/c
# and envp[] are passed as args to this function, and we need to see
# envp so we can get at the env var VG_ARGS without help from libc.
# The stack layout at this point depends on the version of glibc in
# use. See process_cmd_line_options() in vg_main.c for details.
movl %esp, VG_(esp_at_startup)
# We have control! Save the state of the machine in
# the simulators state, and switch stacks.
# Except ... we cant copy the machines registers into their
# final places in vg_baseBlock, because the offsets to them
# have not yet been set up. Instead, they are copied to a
# temporary place (m_state_static). In vg_main.c, once the
# baseBlock offsets are set up, values are copied into baseBlock.
movl %eax, VG_(m_state_static)+0
movl %ecx, VG_(m_state_static)+4
movl %edx, VG_(m_state_static)+8
movl %ebx, VG_(m_state_static)+12
movl %esp, VG_(m_state_static)+16
movl %ebp, VG_(m_state_static)+20
movl %esi, VG_(m_state_static)+24
movl %edi, VG_(m_state_static)+28
pushfl
popl %eax
movl %eax, VG_(m_state_static)+32
fwait
fnsave VG_(m_state_static)+40
frstor VG_(m_state_static)+40
# keep the first and last 10 words free to check for overruns
movl $VG_(stack)+39996 -40, %esp
# Now some real magic. We need this procedure to return,
# since thats what ld.so expects, but running on the
# simulator. So vg_main starts the simulator running at
# the insn labelled first_insn_to_simulate.
movl $first_insn_to_simulate, VG_(m_state_static)+36
jmp VG_(main)
first_insn_to_simulate:
# Nothing else to do -- just return in the "normal" way.
ret
VG_(shutdown):
# Just return, and ignore any attempt by ld.so to call
# valgrind.sos exit function. We just run the client all
# the way to the final exit() syscall. This sidesteps
# problems caused by ld.so calling the finalisation code
# of other .sos *after* it shuts down valgrind, which
# was causing big problems with threads.
ret
.global VG_(switch_to_real_CPU)
VG_(switch_to_real_CPU):
# Once Valgrind has decided it needs to exit,
# because the specified number of insns have been completed
# during a debugging run, it jumps here, which copies the
# simulators state into the real machine state. Execution
# of the rest of the program continues on the real CPU,
# and there is no way for the simulator to regain control
# after this point.
frstor VG_(m_state_static)+40
movl VG_(m_state_static)+32, %eax
pushl %eax
popfl
movl VG_(m_state_static)+0, %eax
movl VG_(m_state_static)+4, %ecx
movl VG_(m_state_static)+8, %edx
movl VG_(m_state_static)+12, %ebx
movl VG_(m_state_static)+16, %esp
movl VG_(m_state_static)+20, %ebp
movl VG_(m_state_static)+24, %esi
movl VG_(m_state_static)+28, %edi
pushal
pushfl
# We hope that vg_sigshutdown_actions does not alter
# the FPU state.
call VG_(sigshutdown_actions)
popfl
popal
# re-restore the FPU state anyway ...
frstor VG_(m_state_static)+40
jmp *VG_(m_state_static)+36
/*------------------------------------------------------------*/
/*--- A function to temporarily copy %ESP/%EBP into ---*/
/*--- %esp/%ebp and then start up GDB. ---*/
/*------------------------------------------------------------*/
/*
extern void VG_(swizzle_esp_then_start_GDB) ( Addr m_eip_at_error,
Addr m_esp_at_error,
Addr m_ebp_at_error );
*/
/*--- This is clearly not re-entrant! ---*/
.data
vg_ebp_saved_over_GDB_start:
.long 0
vg_esp_saved_over_GDB_start:
.long 0
.text
.global VG_(swizzle_esp_then_start_GDB)
VG_(swizzle_esp_then_start_GDB):
pushal
# remember the simulators current stack/frame pointers
movl %ebp, vg_ebp_saved_over_GDB_start
movl %esp, vg_esp_saved_over_GDB_start
# get args into regs
movl 44(%esp), %eax # client %EBP
movl 40(%esp), %ebx # client %ESP
movl 36(%esp), %ecx # client %EIP
# Now that we dont need to refer to simulators stack any more,
# put %ESP into %esp
movl %ebx, %esp
### %esp now refers to clients stack
### mess with the clients stack to make it look as if it
### called this procedure, since otherwise it will look to gdb
### as if the top (currently executing) stack frame of the
### client is missing.
# push %EIP. This is a faked-up return address.
pushl %ecx
# push %EBP. This is a faked %ebp-chain pointer.
pushl %eax
movl %esp, %ebp
call VG_(start_GDB_whilst_on_client_stack)
# restore the simulators stack/frame pointer
movl vg_ebp_saved_over_GDB_start, %ebp
movl vg_esp_saved_over_GDB_start, %esp
popal
ret
# gcc puts this construction at the end of every function. I think it
# allows the linker to figure out the size of the function. So we do
# the same, in the vague hope that it might help GDBs navigation.
.Lend_of_swizzle:
.size VG_(swizzle_esp_then_start_GDB), .Lend_of_swizzle-VG_(swizzle_esp_then_start_GDB)
##--------------------------------------------------------------------##
##--- end vg_startup.S ---##
##--------------------------------------------------------------------##