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 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/Makefile.am b/coregrind/Makefile.am
index 69b3b46..8a1b9c4 100644
--- a/coregrind/Makefile.am
+++ b/coregrind/Makefile.am
@@ -63,6 +63,7 @@
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 @@
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 56d4307..4bd4ce8 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 @@
}
-/*------------------------------------------------------------*/
-/*--- 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 @@
}
/*--------------------------------------------------------------------*/
-/*--- end aspacemgr.c ---*/
+/*--- end ---*/
/*--------------------------------------------------------------------*/
diff --git a/coregrind/m_main.c b/coregrind/m_main.c
index 242818e..2b794e9 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 @@
//--------------------------------------------------------------
// 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 587aa32..dada6b1 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 @@
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 f82cffe..a41b48f 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 @@
/* 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 0000000..6bda9bb
--- /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 d1dd4b7..6c9bce5 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 f7feaab..703f146 100644
--- a/coregrind/pub_core_aspacemgr.h
+++ b/coregrind/pub_core_aspacemgr.h
@@ -132,13 +132,6 @@
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 0000000..50e59e2
--- /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 ---*/
+/*--------------------------------------------------------------------*/
+