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