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                                                          ---*/
+/*--------------------------------------------------------------------*/
+