| |
| /*--------------------------------------------------------------------*/ |
| /*--- Stack management. m_stacks.c ---*/ |
| /*--------------------------------------------------------------------*/ |
| |
| /* |
| This file is part of Valgrind, a dynamic binary instrumentation |
| framework. |
| |
| Copyright (C) 2000-2012 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 COPYING. |
| */ |
| |
| #include "pub_core_basics.h" |
| #include "pub_core_debuglog.h" |
| #include "pub_core_libcassert.h" |
| #include "pub_core_libcprint.h" |
| #include "pub_core_mallocfree.h" |
| #include "pub_core_options.h" |
| #include "pub_core_stacks.h" |
| #include "pub_core_tooliface.h" |
| |
| // For expensive debugging |
| #define EDEBUG(fmt, args...) //VG_(debugLog)(2, "stacks", fmt, ## args) |
| |
| /* |
| The stack |
| ~~~~~~~~~ |
| The stack's segment seems to be dynamically extended downwards by |
| the kernel as the stack pointer moves down. Initially, a 1-page |
| (4k) stack is allocated. When SP moves below that for the first |
| time, presumably a page fault occurs. The kernel detects that the |
| faulting address is in the range from SP - VG_STACK_REDZONE_SZB |
| upwards to the current valid stack. It then extends the stack |
| segment downwards for enough to cover the faulting address, and |
| resumes the process (invisibly). The process is unaware of any of |
| this. |
| |
| That means that Valgrind can't spot when the stack segment is being |
| extended. Fortunately, we want to precisely and continuously |
| update stack permissions around SP, so we need to spot all writes |
| to SP anyway. |
| |
| The deal is: when SP is assigned a lower value, the stack is being |
| extended. Create suitably-permissioned pages to fill in any holes |
| between the old stack ptr and this one, if necessary. Then mark |
| all bytes in the area just "uncovered" by this SP change as |
| write-only. |
| |
| When SP goes back up, mark the area receded over as unreadable and |
| unwritable. |
| |
| Just to record the SP boundary conditions somewhere convenient: |
| SP - VG_STACK_REDZONE_SZB always points to the lowest live byte in |
| the stack. All addresses below SP - VG_STACK_REDZONE_SZB are not |
| live; those at and above it are. |
| |
| We do not concern ourselves here with the VG_STACK_REDZONE_SZB |
| bias; that is handled by new_mem_stack/die_mem_stack. |
| */ |
| |
| /* |
| * This structure holds information about the start and end addresses of |
| * registered stacks. There's always at least one stack registered: |
| * the main process stack. It will be the first stack registered and |
| * so will have a stack id of 0. The user does not need to register |
| * this stack: Valgrind does it automatically right before it starts |
| * running the client. No other stacks are automatically registered by |
| * Valgrind, however. |
| */ |
| typedef struct _Stack { |
| UWord id; |
| Addr start; |
| Addr end; |
| struct _Stack *next; |
| } Stack; |
| |
| static Stack *stacks; |
| static UWord next_id; /* Next id we hand out to a newly registered stack */ |
| |
| /* |
| * These are the id, start and end values of the current stack. If the |
| * stack pointer falls outside the range of the current stack, we search |
| * the stacks list above for a matching stack. |
| */ |
| static Stack *current_stack; |
| |
| /* Find 'st' in the stacks_list and move it one step closer the the |
| front of the list, so as to make subsequent searches for it |
| cheaper. */ |
| static void move_Stack_one_step_forward ( Stack* st ) |
| { |
| Stack *st0, *st1, *st2; |
| if (st == stacks) |
| return; /* already at head of list */ |
| vg_assert(st != NULL); |
| st0 = stacks; |
| st1 = NULL; |
| st2 = NULL; |
| while (True) { |
| if (st0 == NULL || st0 == st) break; |
| st2 = st1; |
| st1 = st0; |
| st0 = st0->next; |
| } |
| vg_assert(st0 == st); |
| if (st0 != NULL && st1 != NULL && st2 != NULL) { |
| Stack* tmp; |
| /* st0 points to st, st1 to its predecessor, and st2 to st1's |
| predecessor. Swap st0 and st1, that is, move st0 one step |
| closer to the start of the list. */ |
| vg_assert(st2->next == st1); |
| vg_assert(st1->next == st0); |
| tmp = st0->next; |
| st2->next = st0; |
| st0->next = st1; |
| st1->next = tmp; |
| } |
| else |
| if (st0 != NULL && st1 != NULL && st2 == NULL) { |
| /* it's second in the list. */ |
| vg_assert(stacks == st1); |
| vg_assert(st1->next == st0); |
| st1->next = st0->next; |
| st0->next = st1; |
| stacks = st0; |
| } |
| } |
| |
| /* Find what stack an address falls into. */ |
| static Stack* find_stack_by_addr(Addr sp) |
| { |
| static UWord n_fails = 0; |
| static UWord n_searches = 0; |
| static UWord n_steps = 0; |
| Stack *i = stacks; |
| n_searches++; |
| if (0 && 0 == (n_searches % 10000)) |
| VG_(printf)("(hgdev) %lu searches, %lu steps, %lu fails\n", |
| n_searches, n_steps+1, n_fails); |
| /* fast track common case */ |
| if (i && sp >= i->start && sp <= i->end) |
| return i; |
| /* else search the list */ |
| while (i) { |
| n_steps++; |
| if (sp >= i->start && sp <= i->end) { |
| if (1 && (n_searches & 0x3F) == 0) { |
| move_Stack_one_step_forward( i ); |
| } |
| return i; |
| } |
| i = i->next; |
| } |
| n_fails++; |
| return NULL; |
| } |
| |
| /* |
| * Register a new stack from start - end. This is invoked from the |
| * VALGRIND_STACK_REGISTER client request, and is also called just before |
| * we start the client running, to register the main process stack. |
| */ |
| UWord VG_(register_stack)(Addr start, Addr end) |
| { |
| Stack *i; |
| |
| if (start > end) { |
| Addr t = end; |
| end = start; |
| start = t; |
| } |
| |
| i = (Stack *)VG_(arena_malloc)(VG_AR_CORE, "stacks.rs.1", sizeof(Stack)); |
| i->start = start; |
| i->end = end; |
| i->id = next_id++; |
| i->next = stacks; |
| stacks = i; |
| |
| if (i->id == 0) { |
| current_stack = i; |
| } |
| |
| VG_(debugLog)(2, "stacks", "register %p-%p as stack %lu\n", |
| (void*)start, (void*)end, i->id); |
| |
| return i->id; |
| } |
| |
| /* |
| * Deregister a stack. This is invoked from the VALGRIND_STACK_DEREGISTER |
| * client request. |
| */ |
| void VG_(deregister_stack)(UWord id) |
| { |
| Stack *i = stacks; |
| Stack *prev = NULL; |
| |
| VG_(debugLog)(2, "stacks", "deregister stack %lu\n", id); |
| |
| if (current_stack && current_stack->id == id) { |
| current_stack = NULL; |
| } |
| |
| while(i) { |
| if (i->id == id) { |
| if(prev == NULL) { |
| stacks = i->next; |
| } else { |
| prev->next = i->next; |
| } |
| VG_(arena_free)(VG_AR_CORE, i); |
| return; |
| } |
| prev = i; |
| i = i->next; |
| } |
| } |
| |
| /* |
| * Change a stack. This is invoked from the VALGRIND_STACK_CHANGE client |
| * request and from the stack growth stuff the signals module when |
| * extending the main process stack. |
| */ |
| void VG_(change_stack)(UWord id, Addr start, Addr end) |
| { |
| Stack *i = stacks; |
| |
| while (i) { |
| if (i->id == id) { |
| VG_(debugLog)(2, "stacks", "change stack %lu from %p-%p to %p-%p\n", |
| id, (void*)i->start, (void*)i->end, |
| (void*)start, (void*)end); |
| i->start = start; |
| i->end = end; |
| return; |
| } |
| i = i->next; |
| } |
| } |
| |
| /* |
| * Find the bounds of the stack (if any) which includes the |
| * specified stack pointer. |
| */ |
| void VG_(stack_limits)(Addr SP, Addr *start, Addr *end ) |
| { |
| Stack* stack = find_stack_by_addr(SP); |
| |
| if (stack) { |
| *start = stack->start; |
| *end = stack->end; |
| } |
| } |
| |
| |
| /* complaints_stack_switch reports that SP has changed by more than some |
| threshold amount (by default, 2MB). We take this to mean that the |
| application is switching to a new stack, for whatever reason. |
| |
| JRS 20021001: following discussions with John Regehr, if a stack |
| switch happens, it seems best not to mess at all with memory |
| permissions. Seems to work well with Netscape 4.X. Really the |
| only remaining difficulty is knowing exactly when a stack switch is |
| happening. */ |
| __attribute__((noinline)) |
| static void complaints_stack_switch (Addr old_SP, Addr new_SP) |
| { |
| static Int complaints = 3; |
| if (VG_(clo_verbosity) > 0 && complaints > 0 && !VG_(clo_xml)) { |
| Word delta = (Word)new_SP - (Word)old_SP; |
| complaints--; |
| VG_(message)(Vg_UserMsg, |
| "Warning: client switching stacks? " |
| "SP change: 0x%lx --> 0x%lx\n", old_SP, new_SP); |
| VG_(message)(Vg_UserMsg, |
| " to suppress, use: --max-stackframe=%ld " |
| "or greater\n", |
| (delta < 0 ? -delta : delta)); |
| if (complaints == 0) |
| VG_(message)(Vg_UserMsg, |
| " further instances of this message " |
| "will not be shown.\n"); |
| } |
| } |
| |
| /* The functions VG_(unknown_SP_update) and VG_(unknown_SP_update_w_ECU) |
| get called if new_mem_stack and/or die_mem_stack are |
| tracked by the tool, and one of the specialised cases |
| (eg. new_mem_stack_4) isn't used in preference. |
| |
| These functions are performance critical, so are built with macros. */ |
| |
| // preamble + check if stack has switched. |
| #define IF_STACK_SWITCH_SET_current_stack_AND_RETURN \ |
| Word delta = (Word)new_SP - (Word)old_SP; \ |
| \ |
| EDEBUG("current_stack %p-%p %lu new_SP %p old_SP %p\n", \ |
| (void *) (current_stack ? current_stack->start : 0x0), \ |
| (void *) (current_stack ? current_stack->end : 0x0), \ |
| current_stack ? current_stack->id : 0, \ |
| (void *)new_SP, (void *)old_SP); \ |
| \ |
| /* Check if the stack pointer is still in the same stack as before. */ \ |
| if (UNLIKELY(current_stack == NULL || \ |
| new_SP < current_stack->start || new_SP > current_stack->end)) { \ |
| Stack* new_stack = find_stack_by_addr(new_SP); \ |
| if (new_stack \ |
| && (current_stack == NULL || new_stack->id != current_stack->id)) { \ |
| /* The stack pointer is now in another stack. Update the current */ \ |
| /* stack information and return without doing anything else. */ \ |
| current_stack = new_stack; \ |
| EDEBUG("new current_stack %p-%p %lu \n", \ |
| (void *) current_stack->start, \ |
| (void *) current_stack->end, \ |
| current_stack->id); \ |
| return; \ |
| } else \ |
| EDEBUG("new current_stack not found\n"); \ |
| } |
| |
| #define IF_BIG_DELTA_complaints_AND_RETURN \ |
| if (UNLIKELY(delta < -VG_(clo_max_stackframe) \ |
| || VG_(clo_max_stackframe) < delta)) { \ |
| complaints_stack_switch(old_SP, new_SP); \ |
| return; \ |
| } |
| |
| #define IF_SMALLER_STACK_die_mem_stack_AND_RETURN \ |
| if (delta > 0) { \ |
| VG_TRACK( die_mem_stack, old_SP, delta ); \ |
| return; \ |
| } |
| |
| |
| VG_REGPARM(3) |
| void VG_(unknown_SP_update_w_ECU)( Addr old_SP, Addr new_SP, UInt ecu ) { |
| IF_STACK_SWITCH_SET_current_stack_AND_RETURN; |
| IF_BIG_DELTA_complaints_AND_RETURN; |
| IF_SMALLER_STACK_die_mem_stack_AND_RETURN; |
| if (delta < 0) { // IF_BIGGER_STACK |
| VG_TRACK( new_mem_stack_w_ECU, new_SP, -delta, ecu ); |
| return; |
| } |
| // SAME_STACK. nothing to do. |
| } |
| |
| VG_REGPARM(2) |
| void VG_(unknown_SP_update)( Addr old_SP, Addr new_SP ) { |
| IF_STACK_SWITCH_SET_current_stack_AND_RETURN; |
| IF_BIG_DELTA_complaints_AND_RETURN; |
| IF_SMALLER_STACK_die_mem_stack_AND_RETURN; |
| if (delta < 0) { // IF_BIGGER_STACK |
| VG_TRACK( new_mem_stack, new_SP, -delta ); |
| return; |
| } |
| // SAME_STACK. nothing to do. |
| } |
| |
| /*--------------------------------------------------------------------*/ |
| /*--- end ---*/ |
| /*--------------------------------------------------------------------*/ |
| |