ftmemsim-valgrind/vg_startup.S
Julian Seward 67b0b0e404 Modify the startup mechanism so that any call into valgrind's libpthread.so
will start up valgrind if it is not already running.  This more or less
sidesteps the problem that sometimes valgrind.so isn't init'd first by
the dynamic linker.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@257
2002-05-10 21:03:56 +00:00

234 lines
7.4 KiB
ArmAsm

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