njn | 945ed2e | 2005-06-24 03:28:30 +0000 | [diff] [blame] | 1 | |
| 2 | /*--------------------------------------------------------------------*/ |
| 3 | /*--- Stack management. m_stacks.c ---*/ |
| 4 | /*--------------------------------------------------------------------*/ |
| 5 | |
| 6 | /* |
| 7 | This file is part of Valgrind, a dynamic binary instrumentation |
| 8 | framework. |
| 9 | |
| 10 | Copyright (C) 2000-2005 Julian Seward |
| 11 | jseward@acm.org |
| 12 | |
| 13 | This program is free software; you can redistribute it and/or |
| 14 | modify it under the terms of the GNU General Public License as |
| 15 | published by the Free Software Foundation; either version 2 of the |
| 16 | License, or (at your option) any later version. |
| 17 | |
| 18 | This program is distributed in the hope that it will be useful, but |
| 19 | WITHOUT ANY WARRANTY; without even the implied warranty of |
| 20 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 21 | General Public License for more details. |
| 22 | |
| 23 | You should have received a copy of the GNU General Public License |
| 24 | along with this program; if not, write to the Free Software |
| 25 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
| 26 | 02111-1307, USA. |
| 27 | |
| 28 | The GNU General Public License is contained in the file COPYING. |
| 29 | */ |
| 30 | |
| 31 | #include "pub_core_basics.h" |
| 32 | #include "pub_core_libcprint.h" |
| 33 | #include "pub_core_mallocfree.h" |
| 34 | #include "pub_core_options.h" |
| 35 | #include "pub_core_stacks.h" |
| 36 | #include "pub_core_tooliface.h" |
| 37 | |
| 38 | /* |
| 39 | The stack |
| 40 | ~~~~~~~~~ |
| 41 | The stack's segment seems to be dynamically extended downwards by |
| 42 | the kernel as the stack pointer moves down. Initially, a 1-page |
| 43 | (4k) stack is allocated. When SP moves below that for the first |
| 44 | time, presumably a page fault occurs. The kernel detects that the |
| 45 | faulting address is in the range from SP - VG_STACK_REDZONE_SZB |
| 46 | upwards to the current valid stack. It then extends the stack |
| 47 | segment downwards for enough to cover the faulting address, and |
| 48 | resumes the process (invisibly). The process is unaware of any of |
| 49 | this. |
| 50 | |
| 51 | That means that Valgrind can't spot when the stack segment is being |
| 52 | extended. Fortunately, we want to precisely and continuously |
| 53 | update stack permissions around SP, so we need to spot all writes |
| 54 | to SP anyway. |
| 55 | |
| 56 | The deal is: when SP is assigned a lower value, the stack is being |
| 57 | extended. Create suitably-permissioned pages to fill in any holes |
| 58 | between the old stack ptr and this one, if necessary. Then mark |
| 59 | all bytes in the area just "uncovered" by this SP change as |
| 60 | write-only. |
| 61 | |
| 62 | When SP goes back up, mark the area receded over as unreadable and |
| 63 | unwritable. |
| 64 | |
| 65 | Just to record the SP boundary conditions somewhere convenient: |
| 66 | SP - VG_STACK_REDZONE_SZB always points to the lowest live byte in |
| 67 | the stack. All addresses below SP - VG_STACK_REDZONE_SZB are not |
| 68 | live; those at and above it are. |
| 69 | |
| 70 | We do not concern ourselves here with the VG_STACK_REDZONE_SZB |
| 71 | bias; that is handled by new_mem_stack/die_mem_stack. |
| 72 | */ |
| 73 | |
| 74 | /* |
| 75 | * This structure holds information about the start and end addresses of |
| 76 | * registered stacks. There's always at least one stack registered: |
| 77 | * the main process stack. It will be the first stack registered and |
| 78 | * so will have a stack id of 0. The user does not need to register |
| 79 | * this stack: Valgrind does it automatically right before it starts |
| 80 | * running the client. No other stacks are automatically registered by |
| 81 | * Valgrind, however. |
| 82 | */ |
| 83 | typedef struct _Stack { |
| 84 | UWord id; |
| 85 | Addr start; |
| 86 | Addr end; |
| 87 | struct _Stack *next; |
| 88 | } Stack; |
| 89 | |
| 90 | static Stack *stacks; |
| 91 | static UWord next_id; /* Next id we hand out to a newly registered stack */ |
| 92 | |
| 93 | /* |
| 94 | * These are the id, start and end values of the current stack. If the |
| 95 | * stack pointer falls outside the range of the current stack, we search |
| 96 | * the stacks list above for a matching stack. |
| 97 | */ |
njn | 4e72d20 | 2005-08-14 04:12:40 +0000 | [diff] [blame] | 98 | static Stack current_stack; |
njn | 945ed2e | 2005-06-24 03:28:30 +0000 | [diff] [blame] | 99 | |
| 100 | /* Find what stack an address falls into. */ |
njn | 4e72d20 | 2005-08-14 04:12:40 +0000 | [diff] [blame] | 101 | static Stack* find_stack_by_addr(Addr sp) |
njn | 945ed2e | 2005-06-24 03:28:30 +0000 | [diff] [blame] | 102 | { |
| 103 | Stack *i = stacks; |
njn | 4e72d20 | 2005-08-14 04:12:40 +0000 | [diff] [blame] | 104 | while (i) { |
| 105 | if (sp >= i->start && sp <= i->end) { |
| 106 | return i; |
njn | 945ed2e | 2005-06-24 03:28:30 +0000 | [diff] [blame] | 107 | } |
| 108 | i = i->next; |
| 109 | } |
njn | 4e72d20 | 2005-08-14 04:12:40 +0000 | [diff] [blame] | 110 | return NULL; |
njn | 945ed2e | 2005-06-24 03:28:30 +0000 | [diff] [blame] | 111 | } |
| 112 | |
| 113 | /* |
| 114 | * Register a new stack from start - end. This is invoked from the |
| 115 | * VALGRIND_STACK_REGISTER client request, and is also called just before |
| 116 | * we start the client running, to register the main process stack. |
| 117 | */ |
| 118 | UWord VG_(register_stack)(Addr start, Addr end) |
| 119 | { |
| 120 | Stack *i; |
| 121 | if (start > end) { |
| 122 | Addr t = end; |
| 123 | end = start; |
| 124 | start = t; |
| 125 | } |
| 126 | |
| 127 | i = (Stack *)VG_(arena_malloc)(VG_AR_CORE, sizeof(Stack)); |
| 128 | i->start = start; |
| 129 | i->end = end; |
| 130 | i->id = next_id++; |
| 131 | i->next = stacks; |
| 132 | stacks = i; |
| 133 | |
njn | 4e72d20 | 2005-08-14 04:12:40 +0000 | [diff] [blame] | 134 | if (i->id == 0) { |
| 135 | current_stack = *i; |
njn | 945ed2e | 2005-06-24 03:28:30 +0000 | [diff] [blame] | 136 | } |
| 137 | |
| 138 | return i->id; |
| 139 | } |
| 140 | |
| 141 | /* |
| 142 | * Deregister a stack. This is invoked from the VALGRIND_STACK_DEREGISTER |
| 143 | * client request. |
| 144 | */ |
| 145 | void VG_(deregister_stack)(UWord id) |
| 146 | { |
| 147 | Stack *i = stacks; |
| 148 | Stack *prev = NULL; |
| 149 | |
njn | 4e72d20 | 2005-08-14 04:12:40 +0000 | [diff] [blame] | 150 | if (current_stack.id == id) { |
njn | 945ed2e | 2005-06-24 03:28:30 +0000 | [diff] [blame] | 151 | return; |
| 152 | } |
| 153 | |
| 154 | while(i) { |
| 155 | if (i->id == id) { |
| 156 | if(prev == NULL) { |
| 157 | stacks = i->next; |
| 158 | } else { |
| 159 | prev->next = i->next; |
| 160 | } |
| 161 | VG_(arena_free)(VG_AR_CORE, i); |
| 162 | return; |
| 163 | } |
| 164 | prev = i; |
| 165 | i = i->next; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | /* |
| 170 | * Change a stack. This is invoked from the VALGRIND_STACK_CHANGE client |
| 171 | * request and from the stack growth stuff the signals module when |
| 172 | * extending the main process stack. |
| 173 | */ |
| 174 | void VG_(change_stack)(UWord id, Addr start, Addr end) |
| 175 | { |
| 176 | Stack *i = stacks; |
| 177 | |
njn | 4e72d20 | 2005-08-14 04:12:40 +0000 | [diff] [blame] | 178 | if (id == current_stack.id) { |
| 179 | current_stack.start = start; |
| 180 | current_stack.end = end; |
njn | 945ed2e | 2005-06-24 03:28:30 +0000 | [diff] [blame] | 181 | } |
| 182 | |
njn | 4e72d20 | 2005-08-14 04:12:40 +0000 | [diff] [blame] | 183 | while (i) { |
njn | 945ed2e | 2005-06-24 03:28:30 +0000 | [diff] [blame] | 184 | if (i->id == id) { |
| 185 | i->start = start; |
| 186 | i->end = end; |
| 187 | return; |
| 188 | } |
| 189 | i = i->next; |
| 190 | } |
| 191 | } |
| 192 | |
| 193 | /* This function gets called if new_mem_stack and/or die_mem_stack are |
| 194 | tracked by the tool, and one of the specialised cases |
| 195 | (eg. new_mem_stack_4) isn't used in preference. |
| 196 | */ |
| 197 | VG_REGPARM(2) |
| 198 | void VG_(unknown_SP_update)( Addr old_SP, Addr new_SP ) |
| 199 | { |
| 200 | static Int moans = 3; |
| 201 | Word delta = (Word)new_SP - (Word)old_SP; |
| 202 | |
| 203 | /* Check if the stack pointer is still in the same stack as before. */ |
njn | 4e72d20 | 2005-08-14 04:12:40 +0000 | [diff] [blame] | 204 | if (new_SP < current_stack.start || new_SP > current_stack.end) { |
| 205 | Stack* new_stack = find_stack_by_addr(new_SP); |
| 206 | if (new_stack && new_stack->id != current_stack.id) { |
njn | 945ed2e | 2005-06-24 03:28:30 +0000 | [diff] [blame] | 207 | /* The stack pointer is now in another stack. Update the current |
| 208 | stack information and return without doing anything else. */ |
njn | 4e72d20 | 2005-08-14 04:12:40 +0000 | [diff] [blame] | 209 | current_stack = *new_stack; |
njn | 945ed2e | 2005-06-24 03:28:30 +0000 | [diff] [blame] | 210 | return; |
| 211 | } |
| 212 | } |
| 213 | |
| 214 | if (delta < -VG_(clo_max_stackframe) || VG_(clo_max_stackframe) < delta) { |
| 215 | /* SP has changed by more than some threshold amount (by |
| 216 | default, 2MB). We take this to mean that the application is |
| 217 | switching to a new stack, for whatever reason. |
| 218 | |
| 219 | JRS 20021001: following discussions with John Regehr, if a stack |
| 220 | switch happens, it seems best not to mess at all with memory |
| 221 | permissions. Seems to work well with Netscape 4.X. Really the |
| 222 | only remaining difficulty is knowing exactly when a stack switch is |
| 223 | happening. */ |
| 224 | if (VG_(clo_verbosity) > 0 && moans > 0) { |
| 225 | moans--; |
| 226 | VG_(message)(Vg_UserMsg, |
| 227 | "Warning: client switching stacks? " |
| 228 | "SP change: %p --> %p", old_SP, new_SP); |
| 229 | VG_(message)(Vg_UserMsg, |
| 230 | " to suppress, use: --max-stackframe=%d or greater", |
| 231 | (delta < 0 ? -delta : delta)); |
| 232 | if (moans == 0) |
| 233 | VG_(message)(Vg_UserMsg, |
| 234 | " further instances of this message " |
| 235 | "will not be shown."); |
| 236 | } |
| 237 | } else if (delta < 0) { |
| 238 | VG_TRACK( new_mem_stack, new_SP, -delta ); |
| 239 | |
| 240 | } else if (delta > 0) { |
| 241 | VG_TRACK( die_mem_stack, old_SP, delta ); |
| 242 | } |
| 243 | } |
| 244 | |
| 245 | /*--------------------------------------------------------------------*/ |
| 246 | /*--- end ---*/ |
| 247 | /*--------------------------------------------------------------------*/ |
| 248 | |