sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 1 | |
| 2 | ##--------------------------------------------------------------------## |
| 3 | ##--- Startup and shutdown code for Valgrind. ---## |
| 4 | ##--- vg_startup.S ---## |
| 5 | ##--------------------------------------------------------------------## |
| 6 | |
| 7 | /* |
| 8 | This file is part of Valgrind, an x86 protected-mode emulator |
| 9 | designed for debugging and profiling binaries on x86-Unixes. |
| 10 | |
| 11 | Copyright (C) 2000-2002 Julian Seward |
| 12 | jseward@acm.org |
| 13 | Julian_Seward@muraroa.demon.co.uk |
| 14 | |
| 15 | This program is free software; you can redistribute it and/or |
| 16 | modify it under the terms of the GNU General Public License as |
| 17 | published by the Free Software Foundation; either version 2 of the |
| 18 | License, or (at your option) any later version. |
| 19 | |
| 20 | This program is distributed in the hope that it will be useful, but |
| 21 | WITHOUT ANY WARRANTY; without even the implied warranty of |
| 22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 23 | General Public License for more details. |
| 24 | |
| 25 | You should have received a copy of the GNU General Public License |
| 26 | along with this program; if not, write to the Free Software |
| 27 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
| 28 | 02111-1307, USA. |
| 29 | |
| 30 | The GNU General Public License is contained in the file LICENSE. |
| 31 | */ |
| 32 | |
| 33 | #include "vg_constants.h" |
| 34 | |
| 35 | |
| 36 | #--------------------------------------------------------------------- |
| 37 | # |
| 38 | # Startup and shutdown code for Valgrind. Particularly hairy. |
| 39 | # |
| 40 | # The dynamic linker, ld.so, will run the contents of the .init |
| 41 | # section, once it has located, mmap-d and and linked the shared |
| 42 | # libraries needed by the program. Valgrind is itself a shared |
| 43 | # library. ld.so then runs code in the .init sections of each |
| 44 | # library in turn, in order to give them a chance to initialise |
| 45 | # themselves. We hijack this mechanism. Our startup routine |
| 46 | # does return -- and execution continues -- except on the |
| 47 | # synthetic CPU, not the real one. But ld.so, and the program |
| 48 | # it is starting, cant tell the difference. |
| 49 | # |
| 50 | # The management apologise for the lack of apostrophes in these |
| 51 | # comments. GNU as seems to object to them, for some reason. |
| 52 | |
| 53 | |
| 54 | .section .init |
| 55 | call VG_(startup) |
| 56 | .section .fini |
| 57 | call VG_(shutdown) |
| 58 | .section .text |
| 59 | |
| 60 | |
| 61 | |
| 62 | VG_(startup): |
| 63 | # Record %esp as it was when we got here. This is because argv/c |
| 64 | # and envp[] are passed as args to this function, and we need to see |
| 65 | # envp so we can get at the env var VG_ARGS without help from libc. |
| 66 | # The stack layout at this point depends on the version of glibc in |
| 67 | # use. See process_cmd_line_options() in vg_main.c for details. |
| 68 | movl %esp, VG_(esp_at_startup) |
| 69 | |
| 70 | # We have control! Save the state of the machine in |
| 71 | # the simulators state, and switch stacks. |
| 72 | # Except ... we cant copy the machines registers into their |
| 73 | # final places in vg_baseBlock, because the offsets to them |
| 74 | # have not yet been set up. Instead, they are copied to a |
| 75 | # temporary place (m_state_static). In vg_main.c, once the |
| 76 | # baseBlock offsets are set up, values are copied into baseBlock. |
| 77 | movl %eax, VG_(m_state_static)+0 |
| 78 | movl %ecx, VG_(m_state_static)+4 |
| 79 | movl %edx, VG_(m_state_static)+8 |
| 80 | movl %ebx, VG_(m_state_static)+12 |
| 81 | movl %esp, VG_(m_state_static)+16 |
| 82 | movl %ebp, VG_(m_state_static)+20 |
| 83 | movl %esi, VG_(m_state_static)+24 |
| 84 | movl %edi, VG_(m_state_static)+28 |
| 85 | pushfl |
| 86 | popl %eax |
| 87 | movl %eax, VG_(m_state_static)+32 |
| 88 | fwait |
| 89 | fnsave VG_(m_state_static)+40 |
| 90 | frstor VG_(m_state_static)+40 |
| 91 | |
| 92 | # keep the first and last 10 words free to check for overruns |
| 93 | movl $VG_(stack)+39996 -40, %esp |
| 94 | |
| 95 | # Now some real magic. We need this procedure to return, |
| 96 | # since thats what ld.so expects, but running on the |
| 97 | # simulator. So vg_main starts the simulator running at |
| 98 | # the insn labelled first_insn_to_simulate. |
| 99 | |
| 100 | movl $first_insn_to_simulate, VG_(m_state_static)+36 |
| 101 | jmp VG_(main) |
| 102 | first_insn_to_simulate: |
| 103 | # Nothing else to do -- just return in the "normal" way. |
| 104 | ret |
| 105 | |
| 106 | |
| 107 | |
| 108 | .global VG_(shutdown) |
| 109 | VG_(shutdown): |
| 110 | # ld.so will call here after execution of the program proper |
| 111 | # is complete, to allow libraries to close down cleanly. |
| 112 | # Note that we will enter here on the synthetic CPU, not |
| 113 | # the real one! So the interpreter must notice when this |
| 114 | # procedure is called, and use that as its cue to switch |
| 115 | # back to the real CPU. That means the code placed here is |
| 116 | # utterly irrelevant, since it will never get run, but I |
| 117 | # place a RET here anyway, since it is the traditional way |
| 118 | # to return from a subroutine :-) |
| 119 | ret |
| 120 | |
| 121 | |
| 122 | |
| 123 | .global VG_(switch_to_real_CPU) |
| 124 | VG_(switch_to_real_CPU): |
| 125 | # Once Valgrind has decided it needs to exit, either |
| 126 | # because it has detected a call to vg_shutdown, or |
| 127 | # because the specified number of insns have been completed |
| 128 | # during a debugging run, it jumps here, which copies the |
| 129 | # simulators state into the real machine state. Execution |
| 130 | # of the rest of the program continues on the real CPU, |
| 131 | # and there is no way for the simulator to regain control |
| 132 | # after this point. |
| 133 | frstor VG_(m_state_static)+40 |
| 134 | movl VG_(m_state_static)+32, %eax |
| 135 | pushl %eax |
| 136 | popfl |
| 137 | movl VG_(m_state_static)+0, %eax |
| 138 | movl VG_(m_state_static)+4, %ecx |
| 139 | movl VG_(m_state_static)+8, %edx |
| 140 | movl VG_(m_state_static)+12, %ebx |
| 141 | movl VG_(m_state_static)+16, %esp |
| 142 | movl VG_(m_state_static)+20, %ebp |
| 143 | movl VG_(m_state_static)+24, %esi |
| 144 | movl VG_(m_state_static)+28, %edi |
| 145 | |
| 146 | pushal |
| 147 | pushfl |
| 148 | # We hope that vg_sigshutdown_actions does not alter |
| 149 | # the FPU state. |
| 150 | call VG_(sigshutdown_actions) |
| 151 | popfl |
| 152 | popal |
| 153 | # re-restore the FPU state anyway ... |
| 154 | frstor VG_(m_state_static)+40 |
| 155 | jmp *VG_(m_state_static)+36 |
| 156 | |
| 157 | |
| 158 | |
| 159 | /*------------------------------------------------------------*/ |
| 160 | /*--- A function to temporarily copy %ESP/%EBP into ---*/ |
| 161 | /*--- %esp/%ebp and then start up GDB. ---*/ |
| 162 | /*------------------------------------------------------------*/ |
| 163 | |
| 164 | /*--- This is clearly not re-entrant! ---*/ |
| 165 | .data |
| 166 | vg_ebp_saved_over_GDB_start: |
| 167 | .word 0 |
| 168 | vg_esp_saved_over_GDB_start: |
| 169 | .word 0 |
| 170 | .text |
| 171 | |
| 172 | .global VG_(swizzle_esp_then_start_GDB) |
| 173 | VG_(swizzle_esp_then_start_GDB): |
| 174 | pushal |
| 175 | |
| 176 | # remember the simulators current stack/frame pointers |
| 177 | movl %ebp, vg_ebp_saved_over_GDB_start |
| 178 | movl %esp, vg_esp_saved_over_GDB_start |
| 179 | |
| 180 | movl $VG_(baseBlock), %ebx |
| 181 | |
| 182 | # fetch %ESP into %esp |
| 183 | movl VGOFF_(m_esp), %esi |
| 184 | movl (%ebx, %esi, 4), %esp |
| 185 | |
| 186 | ### %esp now refers to clients stack |
| 187 | ### mess with the clients stack to make it look as if it |
| 188 | ### called this procedure, since otherwise it will look to gdb |
| 189 | ### as if the top (currently executing) stack frame of the |
| 190 | ### client is missing. |
| 191 | |
| 192 | # push %EIP, via %eax. This is a faked-up return address. |
| 193 | movl VGOFF_(m_eip), %esi |
| 194 | movl (%ebx, %esi, 4), %eax |
| 195 | pushl %eax |
| 196 | |
| 197 | # push %EBP, via %eax. This is a faked %ebp-chain pointer. |
| 198 | movl VGOFF_(m_ebp), %esi |
| 199 | movl (%ebx, %esi, 4), %eax |
| 200 | pushl %eax |
| 201 | |
| 202 | movl %esp, %ebp |
| 203 | |
| 204 | call VG_(start_GDB_whilst_on_client_stack) |
| 205 | |
| 206 | # restore the simulators stack/frame pointer |
| 207 | movl vg_ebp_saved_over_GDB_start, %ebp |
| 208 | movl vg_esp_saved_over_GDB_start, %esp |
| 209 | |
| 210 | popal |
| 211 | ret |
| 212 | |
| 213 | # gcc puts this construction at the end of every function. I think it |
| 214 | # allows the linker to figure out the size of the function. So we do |
| 215 | # the same, in the vague hope that it might help GDBs navigation. |
| 216 | .Lend_of_swizzle: |
| 217 | .size VG_(swizzle_esp_then_start_GDB), .Lend_of_swizzle-VG_(swizzle_esp_then_start_GDB) |
| 218 | |
| 219 | ##--------------------------------------------------------------------## |
| 220 | ##--- end vg_startup.S ---## |
| 221 | ##--------------------------------------------------------------------## |