From: Nicholas Nethercote Date: Fri, 24 Jun 2005 03:28:30 +0000 (+0000) Subject: Moved Robert's stack tracking code out of m_aspacemgr into a new module X-Git-Tag: svn/VALGRIND_3_0_0~309 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=f4ee5eee2abc7667b533803085414acec5cd9ab6;p=thirdparty%2Fvalgrind.git Moved Robert's stack tracking code out of m_aspacemgr into a new module m_stacks, because it's a nicely distinct and stand-alone piece of functionality. This happily removes m_aspacemgr's dependence on m_mallocfree (there was an apparent dependence due to the #include, even if it didn't manifest itself in practice -- very important!) and m_options (not so important). git-svn-id: svn://svn.valgrind.org/valgrind/trunk@4009 --- diff --git a/coregrind/Makefile.am b/coregrind/Makefile.am index 69b3b46698..8a1b9c4b0f 100644 --- a/coregrind/Makefile.am +++ b/coregrind/Makefile.am @@ -63,6 +63,7 @@ noinst_HEADERS = \ pub_core_sigframe.h \ pub_core_signals.h \ pub_core_skiplist.h \ + pub_core_stacks.h \ pub_core_stacktrace.h \ pub_core_syswrap.h \ pub_core_threadmodel.h \ @@ -117,6 +118,7 @@ stage2_SOURCES = \ m_redir.c \ m_signals.c \ m_skiplist.c \ + m_stacks.c \ m_stacktrace.c \ m_syscall.c \ m_threadmodel.c \ diff --git a/coregrind/m_aspacemgr/aspacemgr.c b/coregrind/m_aspacemgr/aspacemgr.c index 56d4307530..4bd4ce8d86 100644 --- a/coregrind/m_aspacemgr/aspacemgr.c +++ b/coregrind/m_aspacemgr/aspacemgr.c @@ -2,7 +2,7 @@ /*--------------------------------------------------------------------*/ /*--- The address space manager: segment initialisation and ---*/ /*--- tracking, stack operations ---*/ -/*--- aspacemgr.c ---*/ +/*--- m_aspacemgr.c ---*/ /*--------------------------------------------------------------------*/ /* @@ -38,11 +38,9 @@ #include "pub_core_libcfile.h" // For VG_(fstat), VG_(resolve_filename_nodup) #include "pub_core_libcmman.h" #include "pub_core_libcprint.h" -#include "pub_core_mallocfree.h" -#include "pub_core_options.h" #include "pub_core_syscall.h" #include "pub_core_tooliface.h" -#include "pub_core_transtab.h" +#include "pub_core_transtab.h" // For VG_(discard_translations) #include "vki_unistd.h" @@ -1070,269 +1068,6 @@ Segment *VG_(find_segment_above_mapped)(Addr a) } -/*------------------------------------------------------------*/ -/*--- Tracking permissions around %esp changes. ---*/ -/*------------------------------------------------------------*/ - -/* - 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 Addr current_stack_start; -static Addr current_stack_end; -static UWord current_stack_id; - -/* Search for a particular stack by id number. */ -static Bool find_stack_by_id(UWord id, Addr *start, Addr *end) -{ - Stack *i = stacks; - while(i) { - if(i->id == id) { - *start = i->start; - *end = i->end; - return True; - } - i = i->next; - } - return False; -} - -/* Find what stack an address falls into. */ -static Bool find_stack_by_addr(Addr sp, Addr *start, Addr *end, UWord *id) -{ - Stack *i = stacks; - while(i) { - if(sp >= i->start && sp <= i->end) { - *start = i->start; - *end = i->end; - *id = i->id; - return True; - } - i = i->next; - } - return False; -} - -/* Change over to a new stack. */ -static Bool set_current_stack(UWord id) -{ - Addr start, end; - if (find_stack_by_id(id, &start, &end)) { - current_stack_id = id; - current_stack_start = start; - current_stack_end = end; - return True; - } - return False; -} - -/* - * 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. - * - * Note: this requires allocating a piece of memory to store the Stack - * structure, which places a dependency between this module and the - * mallocfree module. However, there is no real chance of a circular - * dependency here, since the mallocfree module would never call back to - * this function. - */ - -UWord VG_(handle_stack_register)(Addr start, Addr end) -{ - Stack *i; - if (start > end) { - Addr t = end; - end = start; - start = t; - } - - i = (Stack *)VG_(arena_malloc)(VG_AR_CORE, sizeof(Stack)); - i->start = start; - i->end = end; - i->id = next_id++; - i->next = stacks; - stacks = i; - - if(i->id == 0) { - set_current_stack(i->id); - } - - return i->id; -} - -/* - * Deregister a stack. This is invoked from the VALGRIND_STACK_DEREGISTER - * client request. - * - * Note: this requires freeing the piece of memory that was used to store - * the Stack structure, which places a dependency between this module - * and the mallocfree module. However, there is no real chance of - * a circular dependency here, since the mallocfree module would never - * call back to this function. - */ - -void VG_(handle_stack_deregister)(UWord id) -{ - Stack *i = stacks; - Stack *prev = NULL; - - if(current_stack_id == id) { - return; - } - - 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_(handle_stack_change)(UWord id, Addr start, Addr end) -{ - Stack *i = stacks; - - if (id == current_stack_id) { - current_stack_start = start; - current_stack_end = end; - } - - while(i) { - if (i->id == id) { - i->start = start; - i->end = end; - return; - } - i = i->next; - } -} - -/* This function gets 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. -*/ -VG_REGPARM(2) -void VG_(unknown_SP_update)( Addr old_SP, Addr new_SP ) -{ - static Int moans = 3; - Word delta = (Word)new_SP - (Word)old_SP; - - /* Check if the stack pointer is still in the same stack as before. */ - if (new_SP < current_stack_start || new_SP > current_stack_end) { - Addr start, end; - UWord new_id; - Bool found = find_stack_by_addr(new_SP, &start, &end, &new_id); - if (found && new_id != current_stack_id) { - /* The stack pointer is now in another stack. Update the current - stack information and return without doing anything else. */ - set_current_stack(new_id); - return; - } - } - - if (delta < -VG_(clo_max_stackframe) || VG_(clo_max_stackframe) < delta) { - /* 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. */ - if (VG_(clo_verbosity) > 0 && moans > 0) { - moans--; - VG_(message)(Vg_UserMsg, - "Warning: client switching stacks? " - "SP change: %p --> %p", old_SP, new_SP); - VG_(message)(Vg_UserMsg, - " to suppress, use: --max-stackframe=%d or greater", - (delta < 0 ? -delta : delta)); - if (moans == 0) - VG_(message)(Vg_UserMsg, - " further instances of this message " - "will not be shown."); - } - } else if (delta < 0) { - VG_TRACK( new_mem_stack, new_SP, -delta ); - - } else if (delta > 0) { - VG_TRACK( die_mem_stack, old_SP, delta ); - } -} - /* Test if a piece of memory is addressable with at least the "prot" protection permissions by examining the underlying segments. @@ -1544,5 +1279,5 @@ Bool VG_(setup_pointercheck)(Addr client_base, Addr client_end) } /*--------------------------------------------------------------------*/ -/*--- end aspacemgr.c ---*/ +/*--- end ---*/ /*--------------------------------------------------------------------*/ diff --git a/coregrind/m_main.c b/coregrind/m_main.c index 242818e1e3..2b794e97a8 100644 --- a/coregrind/m_main.c +++ b/coregrind/m_main.c @@ -52,6 +52,7 @@ #include "pub_core_redir.h" #include "pub_core_scheduler.h" #include "pub_core_signals.h" +#include "pub_core_stacks.h" // Needed for VG_(register_stack) #include "pub_core_syswrap.h" #include "pub_core_tooliface.h" #include "pub_core_trampoline.h" @@ -2715,7 +2716,7 @@ int main(int argc, char **argv, char **envp) //-------------------------------------------------------------- // register client stack //-------------------------------------------------------------- - VG_(clstk_id) = VG_(handle_stack_register)(VG_(clstk_base), VG_(clstk_end)); + VG_(clstk_id) = VG_(register_stack)(VG_(clstk_base), VG_(clstk_end)); //-------------------------------------------------------------- // Run! diff --git a/coregrind/m_scheduler/scheduler.c b/coregrind/m_scheduler/scheduler.c index 587aa323ff..dada6b1217 100644 --- a/coregrind/m_scheduler/scheduler.c +++ b/coregrind/m_scheduler/scheduler.c @@ -78,6 +78,7 @@ #include "pub_core_replacemalloc.h" #include "pub_core_scheduler.h" #include "pub_core_signals.h" +#include "pub_core_stacks.h" #include "pub_core_stacktrace.h" // For VG_(get_and_pp_StackTrace)() #include "pub_core_syscall.h" #include "pub_core_syswrap.h" @@ -948,17 +949,17 @@ void do_client_request ( ThreadId tid ) break; } case VG_USERREQ__STACK_REGISTER: { - UWord sid = VG_(handle_stack_register)((Addr)arg[1], (Addr)arg[2]); + UWord sid = VG_(register_stack)((Addr)arg[1], (Addr)arg[2]); SET_CLREQ_RETVAL( tid, sid ); break; } case VG_USERREQ__STACK_DEREGISTER: { - VG_(handle_stack_deregister)(arg[1]); + VG_(deregister_stack)(arg[1]); SET_CLREQ_RETVAL( tid, 0 ); /* return value is meaningless */ break; } case VG_USERREQ__STACK_CHANGE: { - VG_(handle_stack_change)(arg[1], (Addr)arg[2], (Addr)arg[3]); + VG_(change_stack)(arg[1], (Addr)arg[2], (Addr)arg[3]); SET_CLREQ_RETVAL( tid, 0 ); /* return value is meaningless */ break; } diff --git a/coregrind/m_signals.c b/coregrind/m_signals.c index f82cffe608..a41b48f118 100644 --- a/coregrind/m_signals.c +++ b/coregrind/m_signals.c @@ -97,6 +97,7 @@ #include "pub_core_scheduler.h" #include "pub_core_signals.h" #include "pub_core_sigframe.h" // For VG_(sigframe_create)() +#include "pub_core_stacks.h" // For VG_(change_stack)() #include "pub_core_stacktrace.h" // For VG_(get_and_pp_StackTrace)() #include "pub_core_syscall.h" #include "pub_core_syswrap.h" @@ -1737,7 +1738,7 @@ Bool VG_(extend_stack)(Addr addr, UInt maxsize) /* When we change the main stack, we have to let the stack handling code know about it. */ - VG_(handle_stack_change)(VG_(clstk_id), base, VG_(clstk_end)); + VG_(change_stack)(VG_(clstk_id), base, VG_(clstk_end)); if (0) VG_(printf)("extended stack: %p %d\n", diff --git a/coregrind/m_stacks.c b/coregrind/m_stacks.c new file mode 100644 index 0000000000..6bda9bbba9 --- /dev/null +++ b/coregrind/m_stacks.c @@ -0,0 +1,283 @@ + +/*--------------------------------------------------------------------*/ +/*--- Stack management. m_stacks.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2005 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_libcprint.h" +#include "pub_core_mallocfree.h" +#include "pub_core_options.h" +#include "pub_core_stacks.h" +#include "pub_core_tooliface.h" + +/* + 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 Addr current_stack_start; +static Addr current_stack_end; +static UWord current_stack_id; + +/* Search for a particular stack by id number. */ +static Bool find_stack_by_id(UWord id, Addr *start, Addr *end) +{ + Stack *i = stacks; + while(i) { + if(i->id == id) { + *start = i->start; + *end = i->end; + return True; + } + i = i->next; + } + return False; +} + +/* Find what stack an address falls into. */ +static Bool find_stack_by_addr(Addr sp, Addr *start, Addr *end, UWord *id) +{ + Stack *i = stacks; + while(i) { + if(sp >= i->start && sp <= i->end) { + *start = i->start; + *end = i->end; + *id = i->id; + return True; + } + i = i->next; + } + return False; +} + +/* Change over to a new stack. */ +static Bool set_current_stack(UWord id) +{ + Addr start, end; + if (find_stack_by_id(id, &start, &end)) { + current_stack_id = id; + current_stack_start = start; + current_stack_end = end; + return True; + } + return False; +} + +/* + * 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, sizeof(Stack)); + i->start = start; + i->end = end; + i->id = next_id++; + i->next = stacks; + stacks = i; + + if(i->id == 0) { + set_current_stack(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; + + if(current_stack_id == id) { + return; + } + + 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; + + if (id == current_stack_id) { + current_stack_start = start; + current_stack_end = end; + } + + while(i) { + if (i->id == id) { + i->start = start; + i->end = end; + return; + } + i = i->next; + } +} + +/* This function gets 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. +*/ +VG_REGPARM(2) +void VG_(unknown_SP_update)( Addr old_SP, Addr new_SP ) +{ + static Int moans = 3; + Word delta = (Word)new_SP - (Word)old_SP; + + /* Check if the stack pointer is still in the same stack as before. */ + if (new_SP < current_stack_start || new_SP > current_stack_end) { + Addr start, end; + UWord new_id; + Bool found = find_stack_by_addr(new_SP, &start, &end, &new_id); + if (found && new_id != current_stack_id) { + /* The stack pointer is now in another stack. Update the current + stack information and return without doing anything else. */ + set_current_stack(new_id); + return; + } + } + + if (delta < -VG_(clo_max_stackframe) || VG_(clo_max_stackframe) < delta) { + /* 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. */ + if (VG_(clo_verbosity) > 0 && moans > 0) { + moans--; + VG_(message)(Vg_UserMsg, + "Warning: client switching stacks? " + "SP change: %p --> %p", old_SP, new_SP); + VG_(message)(Vg_UserMsg, + " to suppress, use: --max-stackframe=%d or greater", + (delta < 0 ? -delta : delta)); + if (moans == 0) + VG_(message)(Vg_UserMsg, + " further instances of this message " + "will not be shown."); + } + } else if (delta < 0) { + VG_TRACK( new_mem_stack, new_SP, -delta ); + + } else if (delta > 0) { + VG_TRACK( die_mem_stack, old_SP, delta ); + } +} + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ + diff --git a/coregrind/m_translate.c b/coregrind/m_translate.c index d1dd4b7e01..6c9bce5bc0 100644 --- a/coregrind/m_translate.c +++ b/coregrind/m_translate.c @@ -40,6 +40,7 @@ #include "pub_core_profile.h" #include "pub_core_redir.h" // For VG_(code_redirect)() #include "pub_core_signals.h" // For VG_(synth_fault_{perms,mapping})() +#include "pub_core_stacks.h" // For VG_(unknown_SP_update)() #include "pub_core_tooliface.h" // For VG_(tdict) #include "pub_core_translate.h" #include "pub_core_transtab.h" diff --git a/coregrind/pub_core_aspacemgr.h b/coregrind/pub_core_aspacemgr.h index f7feaabbe8..703f146141 100644 --- a/coregrind/pub_core_aspacemgr.h +++ b/coregrind/pub_core_aspacemgr.h @@ -132,13 +132,6 @@ extern Segment *VG_(split_segment)(Addr a); extern void VG_(pad_address_space) (Addr start); extern void VG_(unpad_address_space)(Addr start); -extern UWord VG_(handle_stack_register)(Addr start, Addr end); -extern void VG_(handle_stack_deregister)(UWord id); -extern void VG_(handle_stack_change)(UWord id, Addr start, Addr end); - -extern VG_REGPARM(2) - void VG_(unknown_SP_update) ( Addr old_SP, Addr new_SP ); - ///* Search /proc/self/maps for changes which aren't reflected in the // segment list */ //extern void VG_(sync_segments)(UInt flags); diff --git a/coregrind/pub_core_stacks.h b/coregrind/pub_core_stacks.h new file mode 100644 index 0000000000..50e59e24cc --- /dev/null +++ b/coregrind/pub_core_stacks.h @@ -0,0 +1,51 @@ + +/*--------------------------------------------------------------------*/ +/*--- Stack management. m_stacks.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2005 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. +*/ + +#ifndef __PUB_CORE_STACKS_H +#define __PUB_CORE_STACKS_H + +//-------------------------------------------------------------------- +// PURPOSE: This module deals with the registration of stacks for the +// purposes of detecting stack switches. +//-------------------------------------------------------------------- + +extern UWord VG_(register_stack) ( Addr start, Addr end ); +extern void VG_(deregister_stack) ( UWord id ); +extern void VG_(change_stack) ( UWord id, Addr start, Addr end ); + +extern VG_REGPARM(2) + void VG_(unknown_SP_update) ( Addr old_SP, Addr new_SP ); + +#endif // __PUB_CORE_STACKS_H + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ +