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