]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Merge r6105:
authorJulian Seward <jseward@acm.org>
Tue, 17 Oct 2006 01:23:57 +0000 (01:23 +0000)
committerJulian Seward <jseward@acm.org>
Tue, 17 Oct 2006 01:23:57 +0000 (01:23 +0000)
Refactor the address space manager, so there are two implementations
of it, plus a small common supporting library.

git-svn-id: svn://svn.valgrind.org/valgrind/trunk@6252

coregrind/m_aspacemgr/aspacemgr-aix5.c [new file with mode: 0644]
coregrind/m_aspacemgr/aspacemgr-common.c
coregrind/m_aspacemgr/aspacemgr-linux.c

diff --git a/coregrind/m_aspacemgr/aspacemgr-aix5.c b/coregrind/m_aspacemgr/aspacemgr-aix5.c
new file mode 100644 (file)
index 0000000..ee238a6
--- /dev/null
@@ -0,0 +1,2611 @@
+
+/*--------------------------------------------------------------------*/
+/*--- The address space manager: segment initialisation and        ---*/
+/*--- tracking, stack operations                                   ---*/
+/*---                                                              ---*/
+/*--- Implementation for AIX5                   m_aspacemgr-aix5.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2006-2006 OpenWorks LLP
+      info@open-works.co.uk
+
+   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.
+*/
+
+/* *************************************************************
+   DO NOT INCLUDE ANY OTHER FILES HERE.
+   ADD NEW INCLUDES ONLY TO priv_aspacemgr.h
+   AND THEN ONLY AFTER READING DIRE WARNINGS THERE TOO.
+   ************************************************************* */
+
+#include "priv_aspacemgr.h"
+
+
+/* Note: many of the exported functions implemented below are
+   described more fully in comments in pub_core_aspacemgr.h.
+*/
+
+/* This provides a minimal address space management facility for AIX5.
+   It is not as comprehensive, robust or efficient as its Linux
+   counterpart.
+
+   It does implement the advise/notify concept described in
+   aspacemgr-linux.c, but minimally.  It only keeps track of the
+   mappings belonging to Valgrind; the client can do what it likes so
+   long as it doesn't trash Valgrind's mappings.
+
+   This is unfortunate, but the root problem is that it is impossible
+   to find out on AIX what the complete set of mappings for a process
+   is.  Sure, AIX does have /proc/pid/map, but it's weak compared to
+   Linux's: it just shows some small subset of the mappings, not all
+   of them.  So it is not very useful: it can't be used to discover
+   the true initial process mapping state, and it can't be used to
+   cross-check Valgrind's internal mapping table, as is done at
+   --sanity-level=3 and above on Linux.
+*/
+
+
+/*-----------------------------------------------------------------*/
+/*---                                                           ---*/
+/*--- The Address Space Manager's state.                        ---*/
+/*---                                                           ---*/
+/*-----------------------------------------------------------------*/
+
+/* Describes AIX5-specific segment kinds */
+typedef
+   enum {
+      ASkFree=1,  // free space
+      ASkMText,   // module text (code) mapping
+      ASkMData,   // module data (& bss) mapping
+      ASkFileV,   // file mapping belonging to valgrind
+      ASkAnonC,   // anonymous mapping belonging to the client
+      ASkAnonV,   // anonymous mapping belonging to valgrind
+      ASkShmemC,  // shm mapping belonging to the client
+      ASkPreAlloc // area preallocated from sbrk
+   }
+   AixSegKind;
+
+/* Segment table entries, in summary:
+
+   ASkFree   start end
+   ASkMText  start end r w x sibling ismainexe fname mname
+   ASkMData  start end r w x sibling
+   FileV     start end r w x fname offset
+   AnonC     start end r w x fromP isCH
+   AnonV     start end r w x fromP
+   ShmemC    start end r w x
+   PreAlloc  start end
+
+   Entries are non-overlapping and cover the entire address space
+   exactly (as in the Linux aspacem).  Unlike Linux there are no
+   alignment constraints, since we're just recording what's going on,
+   rather than controlling it.
+
+   MText/MData are XCOFF mapped modules, as determined by looking at
+   /proc/../map.  MText is the primary entry and contains the text
+   range.  MData contains the data range, if the module has a data
+   mapping (usually but not always).  MText also holds the avma of the
+   corresponding data segment start, if any, (sibling field) so it can
+   be found and the two added/removed together.  Similarly MData
+   contains the address of the corresponding MText (also sibling).
+
+   fname/mname only apply to MText.  To find the fname/mname for MData
+   you have to look at the corresponding MText entry, which is
+   guaranteed to exist.  MText may exist without a corresponding MData
+   but not vice versa.  Kludge: in fact fname/mname have to be
+   allowed in MData, else read_procselfmap doesn't work.
+
+   MText may have a zero sibling pointer, indicating that there is no
+   corresponding MData.  But MData must have a nonzero sibling pointer
+   since MData without MText is not allowed.  Implication is that
+   neither MText nor MData may be mapped at zero as this would mess up
+   the representation, but I don't think that will ever happen since
+   AIX uses page zero as a readonly const-zero area.
+
+   For MData entries, the data section size acquired from /proc/../map
+   appears to also include the bss, so there is no need for any
+   further handling of that.
+
+   isCH indicates whether an AnonC area is part of the client heap
+   or not.  May not be set for any other kind of area.
+
+   File and member names are entries into the string table.
+
+   fromP, for AnonC/AnonV, if True, indicates that the segment was
+   allocated from a PreAlloc area, and so should be returned to that
+   state upon deallocation.  If False, indicates that the segment
+   should be unmapped on deallocation.
+*/
+typedef
+   struct {
+      AixSegKind kind;
+
+      /* ALL: extent */
+      /* Note: zero-length segments are not allowed.  That guarantees
+         that start <= end. */
+      Addr start;  // lowest addr in range (ALL)
+      Addr end;    // highest addr in range (ALL)
+
+      /* ALL except Free */
+      Bool hasR;
+      Bool hasW;
+      Bool hasX;
+
+      /* misc */
+      Addr   sibling;   // MText, MData only: addr of MData/MText
+      Bool   isMainExe; // MText only: is this the main executable?
+      Bool   isCH;      // AnonC only: is this part of the client's heap?
+      Bool   fromP;     // AnonC, AnonV only: originated from PreAlloc?
+      UChar* fname;     // MText, FileV only: filename
+      UChar* mname;     // MText only: member name if present
+      ULong  offset;    // FileV only: file offset
+   }
+   AixSegment;
+
+
+#define VG_N_ASEGMENTS 5000
+
+typedef
+   struct {
+      AixSegment seg[VG_N_ASEGMENTS];
+      Int        used;
+   }
+   AixSegments;
+
+
+/* ------ start of STATE for the address-space manager ------ */
+
+/* A table of zero-terminated strings (file names etc).  This
+   is only ever added to. */
+
+#define VG_N_ASTRTAB 200000
+static Int strtab_used = 0;
+static UChar strtab[VG_N_ASTRTAB];
+
+#define Addr_MIN ((Addr)0)
+#define Addr_MAX ((Addr)(-1ULL))
+
+/* The main array of AixSegments, in order as required. */
+
+static AixSegments asegs_pri;
+
+/* and two auxiliary arrays. */
+
+static AixSegments asegs_tnew;
+static AixSegments asegs_told;
+
+/* The assumed size of the main thread's stack, so that we can add a
+   segment for it at startup. */
+
+#define N_FAKE_STACK_PAGES 4096 /* 16M fake stack */
+
+/* Hacks which are probably for AIX 'millicode'.  Note: ensure
+   these stay page aligned. */
+
+#define MAGIC_PAGES_1_BASE  0x3000
+#define MAGIC_PAGES_1_SIZE  (2*0x1000)
+
+#define MAGIC_PAGES_2_BASE  0xC000
+#define MAGIC_PAGES_2_SIZE  (4*0x1000)
+
+
+#define AM_SANITY_CHECK(_who)                                  \
+   do {                                                        \
+      if (VG_(clo_sanity_level >= 3)) {                        \
+         Bool ok = sane_AixSegments(&asegs_pri);               \
+         if (!ok)                                              \
+            VG_(debugLog)(0,"aspace", "sanity check failed, "  \
+                                      "who = %s\n", _who);     \
+            aspacem_assert(ok);                                \
+      }                                                        \
+   } while (0)
+
+/* When preallocating a block from sbrk-world, how much extra
+   should we pre-emptively acquire? */
+
+//#define AM_PREALLOC_EXTRA (512 * 1024)
+//#define AM_PREALLOC_EXTRA 0x0800000  /* 8 M */
+#define AM_PREALLOC_EXTRA 0x4000000  /* 64 M */
+
+/* The AIX5 aspacem implementation needs to be told when it is and
+   isn't allowed to use sbrk to allocate memory.  Hence: */
+Bool VG_(am_aix5_sbrk_allowed) = True;
+
+/* ------ end of STATE for the address-space manager ------ */
+
+/* ------ Forwards decls ------ */
+static void parse_procselfmap ( /*OUT*/ AixSegments* );
+
+
+/*-----------------------------------------------------------------*/
+/*---                                                           ---*/
+/*--- Stuff for 4K (small-page-size) rounding.                  ---*/
+/*---                                                           ---*/
+/*-----------------------------------------------------------------*/
+
+#define AM_4K_PAGESZ 4096
+
+static Bool AM_IS_4K_ALIGNED ( UWord w )
+{
+   UWord m = AM_4K_PAGESZ-1;
+   return toBool( (w & m) == 0 );
+}
+
+static UWord AM_4K_ROUNDUP ( UWord w )
+{
+   UWord m = AM_4K_PAGESZ-1;
+   return (w+m) & (~m);
+}
+
+static UWord AM_64K_ROUNDUP ( UWord w )
+{
+   UWord m = 0x10000-1;
+   return (w+m) & (~m);
+}
+
+
+/*-----------------------------------------------------------------*/
+/*---                                                           ---*/
+/*--- String table management.                                  ---*/
+/*---                                                           ---*/
+/*-----------------------------------------------------------------*/
+
+/* Add the given string into the string table (or find an existing
+   copy of it) and return a pointer to the in-table version.  The
+   pointer will be valid for the entire rest of the run. */
+
+static UChar* add_to_strtab ( UChar* str )
+{
+   Int off, len;
+   /* First, look for the string. */
+   off = 0;
+   while (off < strtab_used) {
+      if (0 == VG_(strcmp)(str, &strtab[off]))
+         return &strtab[off];
+      off += VG_(strlen)(&strtab[off]) + 1;
+   }
+   /* not present?  we'll have to copy it then. */
+   len = VG_(strlen)(str);
+   if (len + 1 + strtab_used > VG_N_ASTRTAB)
+      ML_(am_barf_toolow)("VG_N_ASTRTAB");
+   off = strtab_used;
+   for (; *str; str++)
+      strtab[strtab_used++] = *str;
+   strtab[strtab_used++] = 0;
+   aspacem_assert(strtab_used <= VG_N_ASTRTAB);
+   return &strtab[off];
+}
+
+
+static Bool is_in_strtab ( UChar* str )
+{
+   if (str < &strtab[0]) 
+      return False;
+   if (str >= &strtab[strtab_used])
+      return False;
+   if (str > &strtab[0] && str[-1] != 0)
+      return False;
+   return True;
+}
+
+
+/*-----------------------------------------------------------------*/
+/*---                                                           ---*/
+/*--- Low level AixSegment stuff.                               ---*/
+/*---                                                           ---*/
+/*-----------------------------------------------------------------*/
+
+static void init_AixSegment ( AixSegment* s )
+{
+   s->kind      = 0; /* invalid */
+   s->start     = 0;
+   s->end       = 0;
+   s->hasR      = False;
+   s->hasW      = False;
+   s->hasX      = False;
+   s->sibling   = 0;
+   s->isMainExe = False;
+   s->isCH      = False;
+   s->fromP     = False;
+   s->fname     = NULL;
+   s->mname     = NULL;
+   s->offset    = 0;
+}
+
+
+static HChar* name_of_AixSegKind ( AixSegKind sk )
+{
+   switch (sk) {
+      case ASkFree:     return "Free ";
+      case ASkMText:    return "MText";
+      case ASkMData:    return "MData";
+      case ASkAnonV:    return "AnonV";
+      case ASkAnonC:    return "AnonC";
+      case ASkFileV:    return "FileV";
+      case ASkShmemC:   return "ShmC ";
+      case ASkPreAlloc: return "PreAl";
+      default:        ML_(am_barf)("name_of_AixSegKind");
+                      /*NOTREACHED*/
+                      return NULL;
+   }
+}
+
+
+static 
+void show_AixSegment ( Int logLevel, Int segNo, AixSegment* seg )
+{
+   HChar* segName = name_of_AixSegKind( seg->kind );
+   switch (seg->kind) {
+      case ASkFree:
+         VG_(debugLog)(logLevel, "aspacem",
+            "%3d: %s %010llx-%010llx\n",
+            segNo, /*segName*/"     ",
+            (ULong)seg->start, (ULong)seg->end
+         );
+         break;
+      case ASkMText:
+         VG_(debugLog)(logLevel, "aspacem",
+            "%3d: %s %010llx-%010llx %c%c%c-- (d %010llx) %s%s%s%s\n",
+            segNo, seg->isMainExe ? "MTEXT" : "MText",
+            (ULong)seg->start, (ULong)seg->end,
+            seg->hasR ? 'r' : '-',
+            seg->hasW ? 'w' : '-',
+            seg->hasX ? 'x' : '-',
+            (ULong)seg->sibling,
+            seg->fname,
+            seg->mname ? "(" : "",
+            seg->mname ? (HChar*)seg->mname : "",
+            seg->mname ? ")" : ""
+         );
+         break;
+      case ASkMData:
+         VG_(debugLog)(logLevel, "aspacem",
+            "%3d: %s %010llx-%010llx %c%c%c-- (t %010llx)\n",
+            segNo, "MData",
+            (ULong)seg->start, (ULong)seg->end,
+            seg->hasR ? 'r' : '-',
+            seg->hasW ? 'w' : '-',
+            seg->hasX ? 'x' : '-',
+            (ULong)seg->sibling
+         );
+         break;
+      case ASkFileV: 
+         VG_(debugLog)(logLevel, "aspacem",
+            "%3d: %s %010llx-%010llx %c%c%c-- %6lld %s\n",
+            segNo, segName,
+            (ULong)seg->start, (ULong)seg->end,
+            seg->hasR ? 'r' : '-',
+            seg->hasW ? 'w' : '-',
+            seg->hasX ? 'x' : '-',
+            seg->offset,
+            seg->fname
+         );
+         break;
+      case ASkAnonV: 
+      case ASkAnonC:
+      case ASkShmemC:
+         VG_(debugLog)(logLevel, "aspacem",
+            "%3d: %s %010llx-%010llx %c%c%c%c%c\n",
+            segNo, segName,
+            (ULong)seg->start, (ULong)seg->end,
+            seg->hasR ? 'r' : '-',
+            seg->hasW ? 'w' : '-',
+            seg->hasX ? 'x' : '-',
+            seg->kind==ASkAnonC && seg->isCH ? 'H' : '-',
+            seg->fromP ? 'P' : '-'
+         );
+         break;
+      case ASkPreAlloc:
+         VG_(debugLog)(logLevel, "aspacem",
+            "%3d: %s %010llx-%010llx %c%c%c-- (size %llu)\n",
+            segNo, segName,
+            (ULong)seg->start, (ULong)seg->end,
+            seg->hasR ? 'r' : '-',
+            seg->hasW ? 'w' : '-',
+            seg->hasX ? 'x' : '-',
+            (ULong)seg->end - (ULong)seg->start + 1
+         );
+         break;
+      default:
+         VG_(debugLog)(logLevel, "aspacem",
+                       "%3d: show_AixSegment: unknown segment\n", 
+                       segNo);
+         break;
+   }
+}
+
+
+static void init_AixSegments ( AixSegments* segs )
+{
+   segs->used = 1;
+   init_AixSegment( &segs->seg[0] );
+   segs->seg[0].kind  = ASkFree;
+   segs->seg[0].start = Addr_MIN;
+   segs->seg[0].end   = Addr_MAX;
+}
+
+
+static 
+void show_AixSegments ( Int logLevel, HChar* who, AixSegments* segs )
+{
+   Int i;
+   VG_(debugLog)(logLevel, "aspacem", "<<< %s\n", who);
+   for (i = 0; i < segs->used; i++)
+      show_AixSegment( logLevel, i, &segs->seg[i] );
+   VG_(debugLog)(logLevel, "aspacem", ">>>\n");
+}
+
+
+static Bool sane_AixSegment ( AixSegment* seg )
+{
+   /* disallow zero and negative length segments */
+   if (seg->end < seg->start)
+      return False;
+
+   switch (seg->kind) {
+      case ASkFree:
+         if (seg->hasR || seg->hasW || seg->hasX)
+            return False;
+         if (seg->isMainExe || seg->sibling != 0 || seg->offset != 0)
+            return False;
+         if (seg->fname || seg->mname)
+            return False;
+         if (seg->isCH || seg->fromP)
+            return False;
+         break;
+      case ASkMText:
+         if (!is_in_strtab(seg->fname))
+            return False;
+         if (seg->mname && !is_in_strtab(seg->mname))
+            return False;
+         if (seg->offset != 0)
+            return False;
+         if (seg->isCH || seg->fromP)
+            return False;
+         break;
+      case ASkMData:
+         if (seg->isMainExe || seg->sibling == 0 || seg->offset != 0)
+            return False;
+         /* fname/mname have to be allowed in MData, else
+            read_procselfmap doesn't work.  Unfortunately. */
+         /*
+         if (seg->fname || seg->mname)
+            return False;
+         */
+         if (seg->isCH || seg->fromP)
+            return False;
+         break;
+      case ASkFileV:
+         if (!is_in_strtab(seg->fname))
+            return False;
+         if (seg->mname != NULL)
+            return False;
+         if (seg->isMainExe || seg->sibling != 0)
+            return False;
+         if (seg->isCH || seg->fromP)
+            return False;
+         break;
+      case ASkShmemC:
+      case ASkAnonV:
+      case ASkAnonC:
+         if (seg->fname || seg->mname)
+            return False;
+         if (seg->isMainExe || seg->sibling != 0)
+            return False;
+         if (seg->offset != 0)
+            return False;
+         if (seg->kind != ASkAnonC && seg->isCH)
+            return False;
+         if ( (!(seg->kind == ASkAnonV || seg->kind == ASkAnonC))
+              && seg->fromP)
+            return False;
+         break;
+      case ASkPreAlloc:
+         if (seg->fname || seg->mname)
+            return False;
+         if (seg->isMainExe || seg->sibling != 0)
+            return False;
+         if (seg->offset != 0)
+            return False;
+         if (seg->kind != ASkAnonC && seg->isCH)
+            return False;
+         if (seg->fromP)
+            return False;
+         if (!AM_IS_4K_ALIGNED(seg->start))
+            return False;
+         if (!AM_IS_4K_ALIGNED(seg->end + 1))
+            return False;
+         if (!(seg->hasR && seg->hasW && seg->hasX))
+            return False;
+         break;
+      default:
+         return False;
+   }
+   return True;
+}
+
+
+/* Binary search the interval array for a given address.  Since the
+   array covers the entire address space the search cannot fail. */
+static Int find_asegment_idx ( AixSegments* segs, Addr a )
+{
+   Addr a_mid_lo, a_mid_hi;
+   Int  mid,
+        lo = 0,
+        hi = segs->used-1;
+   aspacem_assert(lo <= hi);
+   while (True) {
+      /* current unsearched space is from lo to hi, inclusive. */
+      if (lo > hi) {
+         /* Not found.  This can't happen. */
+         ML_(am_barf)("find_nsegment_idx: not found");
+      }
+      mid      = (lo + hi) / 2;
+      a_mid_lo = segs->seg[mid].start;
+      a_mid_hi = segs->seg[mid].end;
+
+      if (a < a_mid_lo) { hi = mid-1; continue; }
+      if (a > a_mid_hi) { lo = mid+1; continue; }
+      aspacem_assert(a >= a_mid_lo && a <= a_mid_hi);
+      aspacem_assert(0 <= mid && mid < segs->used);
+      return mid;
+   }
+}
+
+
+static Bool sane_AixSegments ( AixSegments* segs )
+{
+   Int i;
+
+   /* Check endpoints */
+   if (segs->used < 1 || segs->used > VG_N_ASEGMENTS) {
+      VG_(debugLog)(0, "aspacem", "sane_AixSegments: bad ->used");
+      return False;
+   }
+   if (segs->seg[0].start != Addr_MIN
+       || segs->seg[segs->used-1].end != Addr_MAX) {
+      VG_(debugLog)(0, "aspacem", "sane_AixSegments: bad endpoints");
+      return False;
+   }
+
+   /* Check each segment, and check entire range is covered. */
+   for (i = 0; i < segs->used; i++) {
+      if (!sane_AixSegment( &segs->seg[i] )) {
+         VG_(debugLog)(0, "aspacem", 
+                          "sane_AixSegments: bad segment %d\n", i);
+         return False;
+      }
+   }
+   for (i = 1; i < segs->used; i++) {
+      if (segs->seg[i-1].end + 1 != segs->seg[i].start) {
+         VG_(debugLog)(0, "aspacem", 
+                          "sane_AixSegments: bad transition at %d/%d\n", i-1,i);
+         return False;
+      }
+   }
+
+   /* Now we know 'seg' is safe for use in find_asegment_idx().
+      Check the sibling pointers for MText/MData.
+
+      Also check that the segment starting at address zero is neither
+      MText nor MData (since this would mess up the sibling pointer
+      representation; see comments above.)  Failure of this is not per
+      se a logic failure, but it does indicate that the kernel
+      unexpectedly placed MText or MData at zero, and our
+      representation is therefore inadequate.
+   */
+   if (segs->seg[0].kind == ASkMText || segs->seg[0].kind == ASkMData) {
+      VG_(debugLog)(0, "aspacem", 
+                       "sane_AixSegments: ASkMText/ASkMData at address zero\n");
+      return False;
+   }
+
+   for (i = 0; i < segs->used-1; i++) {
+
+      AixSegment *s1, *s2;
+
+      s1 = &segs->seg[i];
+
+      if (s1->kind == ASkMData) {
+         s2 = &segs->seg[ find_asegment_idx(segs, s1->sibling) ];
+         if (s2->kind != ASkMText
+             || find_asegment_idx(segs, s2->sibling) != i) {
+            VG_(debugLog)(0, "aspacem", "sane_AixSegments: bad sibling "
+                                        "link(s) for ASkData\n");
+            return False;
+         }
+      }
+
+      if (s1->kind == ASkMText && s1->sibling != 0) {
+         s2 = &segs->seg[ find_asegment_idx(segs, s1->sibling) ];
+         if (s2->kind != ASkMData
+             || find_asegment_idx(segs, s2->sibling) != i) {
+            VG_(debugLog)(0, "aspacem", "sane_AixSegments: bad sibling "
+                                        "link(s) for ASkText\n");
+            return False;
+         }
+      }
+
+   }
+
+   return True;
+}
+
+
+/* Try merging s2 into s1, if possible.  If successful, s1 is
+   modified, and True is returned.  Otherwise s1 is unchanged and
+   False is returned. */
+
+static Bool maybe_merge_asegments ( AixSegment* s1, AixSegment* s2 )
+{
+   if (s1->kind != s2->kind) 
+      return False;
+
+   if (s1->end+1 != s2->start)
+      return False;
+
+   switch (s1->kind) {
+
+      case ASkFree:
+         s1->end = s2->end;
+         return True;
+
+      case ASkAnonC:
+      case ASkAnonV:
+         if (s1->hasR == s2->hasR && s1->hasW == s2->hasW 
+             && s1->hasX == s2->hasX && s1->isCH == s2->isCH
+             && s1->fromP == s2->fromP) {
+            s1->end = s2->end;
+            return True;
+         }
+         break;
+
+      /* not really necessary, but .. */
+      case SkFileV:
+         if (s1->hasR == s2->hasR
+             && s1->hasW == s2->hasW && s1->hasX == s2->hasX
+             && s1->fname == s2->fname
+             && s2->offset == s1->offset
+                + ((ULong)s2->start) - ((ULong)s1->start) ) {
+            s1->end = s2->end;
+            return True;
+         }
+         break;
+
+      /* it's important to merge PreAlloc's back together to avoid
+         fragmenting PreAlloc'd space unnecessarily */
+      case ASkPreAlloc:
+         s1->end = s2->end;
+         return True;
+
+      default:
+         break;
+   }
+
+   return False;
+}
+
+
+/* Merge mergable segments in SEGS. */
+
+static void preen_asegments ( AixSegments* segs )
+{
+   Int r, w;
+
+   aspacem_assert(segs->used >= 1);
+   if (segs->used == 1)
+      return;
+
+   w = 0;
+   for (r = 1; r < segs->used; r++) {
+      if (maybe_merge_asegments(&segs->seg[w], &segs->seg[r])) {
+         /* nothing */
+      } else {
+         w++;
+         if (w != r) 
+            segs->seg[w] = segs->seg[r];
+      }
+   }
+   w++;
+   aspacem_assert(w > 0 && w <= segs->used);
+   segs->used = w;
+}
+
+
+/*-----------------------------------------------------------------*/
+/*---                                                           ---*/
+/*--- Modifying a segment array, and constructing segments.     ---*/
+/*---                                                           ---*/
+/*-----------------------------------------------------------------*/
+
+/* Split the segment containing 'a' into two, so that 'a' is
+   guaranteed to be the start of a new segment.  If 'a' is already the
+   start of a segment, do nothing. */
+
+static void split_asegment_at ( AixSegments* segs, Addr a )
+{
+   Int i, j;
+
+   aspacem_assert(a > 0);
+   aspacem_assert(segs->used >= 1);
+   i = find_asegment_idx(segs, a);
+   aspacem_assert(i >= 0 && i < segs->used);
+
+   if (segs->seg[i].start == a)
+      /* 'a' is already the start point of a segment, so nothing to be
+         done. */
+      return;
+
+   /* else we have to slide the segments upwards to make a hole */
+   if (segs->used >= VG_N_ASEGMENTS)
+      ML_(am_barf_toolow)("VG_N_ASEGMENTS");
+   for (j = segs->used-1; j > i; j--)
+      segs->seg[j+1] = segs->seg[j];
+   segs->used++;
+
+   segs->seg[i+1]       = segs->seg[i];
+   segs->seg[i+1].start = a;
+   segs->seg[i].end     = a-1;
+
+   if (segs->seg[i].kind == ASkFileV /* || segs->seg[i].kind == ASkFileC*/)
+      segs->seg[i+1].offset 
+         += ((ULong)segs->seg[i+1].start) - ((ULong)segs->seg[i].start);
+
+   aspacem_assert(sane_AixSegment(&segs->seg[i]));
+   aspacem_assert(sane_AixSegment(&segs->seg[i+1]));
+}
+
+
+/* Do the minimum amount of segment splitting necessary to ensure that
+   sLo is the first address denoted by some segment and sHi is the
+   highest address denoted by some other segment.  Returns the indices
+   of the lowest and highest segments in the range. */
+
+static 
+void split_asegments_lo_and_hi ( AixSegments* segs,
+                                 Addr sLo, Addr sHi,
+                                 /*OUT*/Int* iLo,
+                                 /*OUT*/Int* iHi )
+{
+   aspacem_assert(sLo < sHi);
+
+   if (sLo > 0)
+      split_asegment_at(segs, sLo);
+   if (sHi < Addr_MAX)
+      split_asegment_at(segs, sHi+1);
+
+   *iLo = find_asegment_idx(segs,sLo);
+   *iHi = find_asegment_idx(segs,sHi);
+   aspacem_assert(0 <= *iLo && *iLo < segs->used);
+   aspacem_assert(0 <= *iHi && *iHi < segs->used);
+   aspacem_assert(*iLo <= *iHi);
+   aspacem_assert(segs->seg[*iLo].start == sLo);
+   aspacem_assert(segs->seg[*iHi].end == sHi);
+   /* Not that I'm overly paranoid or anything, definitely not :-) */
+}
+
+
+/* Add SEG to the collection, deleting/truncating any it overlaps.
+   This deals with all the tricky cases of splitting up segments as
+   needed.  Contents of SEG are copied. */
+
+static void add_asegment ( AixSegments* segs, AixSegment* seg )
+{
+   Int  i, iLo, iHi, delta;
+   Bool segment_is_sane;
+
+   Addr sStart = seg->start;
+   Addr sEnd   = seg->end;
+
+   aspacem_assert(sStart <= sEnd);
+
+   segment_is_sane = sane_AixSegment(seg);
+   if (!segment_is_sane) show_AixSegment(0,0,seg);
+   aspacem_assert(segment_is_sane);
+
+   split_asegments_lo_and_hi( segs, sStart, sEnd, &iLo, &iHi );
+
+   /* Now iLo .. iHi inclusive is the range of segment indices which
+      seg will replace.  If we're replacing more than one segment,
+      slide those above the range down to fill the hole. */
+   delta = iHi - iLo;
+   aspacem_assert(delta >= 0);
+   if (delta > 0) {
+      for (i = iLo; i < segs->used-delta; i++)
+         segs->seg[i] = segs->seg[i+delta];
+      segs->used -= delta;
+   }
+   aspacem_assert(segs->used >= 1);
+
+   segs->seg[iLo] = *seg;
+
+   preen_asegments(segs);
+   if (0) VG_(am_show_nsegments)(0,"AFTER preen (add_segment)");
+}
+
+
+/* Convert everything in SEG except MData and MText into Free,
+   then preen, so as to retain normalised form. */
+
+static void knockout_non_module_segs ( AixSegments* segs )
+{
+   Int i;
+   Addr s, e;
+   for (i = 0; i < segs->used; i++) {
+      if (segs->seg[i].kind == ASkFree
+          || segs->seg[i].kind == ASkMText
+          || segs->seg[i].kind == ASkMData)
+         continue;
+      s = segs->seg[i].start;
+      e = segs->seg[i].end;
+      init_AixSegment( &segs->seg[i] );
+      segs->seg[i].start = s;
+      segs->seg[i].end = e;
+      segs->seg[i].kind = ASkFree;
+   }
+   preen_asegments(segs);
+   aspacem_assert( sane_AixSegments(segs) );
+}
+
+
+/* Copy a segment array. */
+
+static void copy_asegments_d_s ( AixSegments* dst, AixSegments* src )
+{
+   Int i;
+   aspacem_assert(src->used >= 1 && src->used < VG_N_ASEGMENTS);
+   dst->used = src->used;
+   for (i = 0; i < src->used; i++)
+      dst->seg[i] = src->seg[i];
+}
+
+
+/*-----------------------------------------------------------------*/
+/*---                                                           ---*/
+/*--- Re-reading /proc/../map and updating MText/MData segments ---*/
+/*---                                                           ---*/
+/*-----------------------------------------------------------------*/
+
+/* Find out the size of the AixCodeSegChange that must be
+   presented to VG_(am_aix5_reread_procmap). */
+
+Int VG_(am_aix5_reread_procmap_howmany_directives)(void)
+{
+   /* In the worst imaginable case, all the tracked modules could have
+      disappeared and been replaced with different ones.  Hence: */
+   return 2 * VG_N_ASEGMENTS;
+}
+
+
+static 
+void add_pri_text_and_data_segs ( AixSegment* tnew, AixSegment* dnew )
+{
+   Bool dExists = (dnew->end - dnew->start + 1) != 0;
+   aspacem_assert(tnew->kind == ASkMText);
+   aspacem_assert(dnew->kind == ASkMData);
+   if (dExists) {
+      aspacem_assert(tnew->sibling == dnew->start);
+      aspacem_assert(dnew->sibling == tnew->start);
+      add_asegment(&asegs_pri, tnew);
+      add_asegment(&asegs_pri, dnew);
+   } else {
+      aspacem_assert(tnew->sibling == 0);
+      add_asegment(&asegs_pri, tnew);
+   }
+}
+
+static 
+void del_pri_text_and_data_segs ( AixSegment* told, AixSegment* dold )
+{
+   AixSegment fre;
+   Bool       dExists = (dold->end - dold->start + 1) != 0;
+   aspacem_assert(told->kind == ASkMText);
+   aspacem_assert(dold->kind == ASkMData);
+   init_AixSegment( &fre );
+   fre.kind = ASkFree;
+   if (dExists) {
+      aspacem_assert(told->sibling == dold->start);
+      aspacem_assert(dold->sibling == told->start);
+      fre.start = told->start;
+      fre.end   = told->end;
+      add_asegment(&asegs_pri, &fre);
+      fre.start = dold->start;
+      fre.end   = dold->end;
+      add_asegment(&asegs_pri, &fre);
+   } else {
+      aspacem_assert(told->sibling == 0);
+      fre.start = told->start;
+      fre.end   = told->end;
+      add_asegment(&asegs_pri, &fre);
+   }
+}
+
+
+/* Tell aspacem that /proc/<pid>/map may have changed (eg following
+   __loadx) and so it should be re-read, and the code/data segment
+   list updated accordingly.  The resulting array of AixCodeChangeSeg
+   directives are written to 'directives', and the number of entries
+   to *ndirectives. */
+
+void VG_(am_aix5_reread_procmap)
+     ( /*OUT*/AixCodeSegChange* directives, /*OUT*/Int* ndirectives )
+{
+   Int        ixold, ixnew;
+   Bool       done_old, done_new;
+   AixSegment *olds, *news;
+
+   /* First, read /proc/../map into asegs_tnew.  Copy asegs_pri into
+      asegs_told, and remove everything except MData and MText, so as
+      to generate something we can sanely compare with asegs_tnew.
+      Walk asegs_told and asegs_tnew together, writing the differences
+      to 'directives', and modifying asegs_pri accordingly. */
+   parse_procselfmap( &asegs_tnew );
+   copy_asegments_d_s( &asegs_told, &asegs_pri );
+   knockout_non_module_segs( &asegs_told );
+
+   *ndirectives = 0;
+
+#  define MODIFY_PRI(_dir, _asegs, _ixt, _acquire) \
+      do { \
+         Int        _ixd; \
+         AixSegment *_segt, *_segd; \
+         AixSegment _segd_dummy; \
+         aspacem_assert(_ixt >= 0 && _ixt < _asegs.used); \
+         _segt = &_asegs.seg[_ixt]; \
+         aspacem_assert(_segt->kind == ASkMText); \
+         if (_segt->sibling) { \
+            _ixd = find_asegment_idx( &_asegs, _segt->sibling ); \
+            _segd = &_asegs.seg[_ixd]; \
+            aspacem_assert(_segd->kind == ASkMData); \
+            aspacem_assert(_segt->sibling == _segd->start); \
+         } else { \
+            init_AixSegment( &_segd_dummy ); \
+            _segd_dummy.kind = ASkMData; \
+            _segd_dummy.start = 1; \
+            _segd_dummy.end   = 0; \
+            _segd = &_segd_dummy; \
+         } \
+         if (_segd != &_segd_dummy) \
+            aspacem_assert(_segd->sibling == _segt->start); \
+         \
+         (_dir).code_start = (_segt)->start; \
+         (_dir).code_len   = (_segt)->end - (_segt)->start + 1; \
+         (_dir).data_start = (_segd)->start; \
+         (_dir).data_len   = (_segd)->end - (_segd)->start + 1; \
+         (_dir).file_name  = (_segt)->fname; \
+         (_dir).mem_name   = (_segt)->mname; \
+         (_dir).is_mainexe = (_acquire) ? (_segt)->isMainExe : False; \
+         (_dir).acquire    = (_acquire); \
+         \
+         if (_acquire) { \
+            add_pri_text_and_data_segs( _segt, _segd ); \
+         } else { \
+            del_pri_text_and_data_segs( _segt, _segd ); \
+         } \
+      } while (0)
+
+   ixold = 0; /* indexes asegs_told */
+   ixnew = 0; /* indexes asegs_tnew */
+
+   while (True) {
+
+      aspacem_assert(ixold >= 0 && ixold < asegs_told.used);
+      aspacem_assert(ixnew >= 0 && ixnew < asegs_tnew.used);
+
+      /* Advance ixold and ixnew to the next MText in their
+         respective arrays. */
+      while (ixold < asegs_told.used 
+             && asegs_told.seg[ixold].kind != ASkMText) {
+         aspacem_assert(asegs_told.seg[ixold].kind == ASkFree
+                        || asegs_told.seg[ixold].kind == ASkMData);
+         ixold++;
+      }
+      while (ixnew < asegs_tnew.used 
+             && asegs_tnew.seg[ixnew].kind != ASkMText) {
+         aspacem_assert(asegs_tnew.seg[ixnew].kind == ASkFree
+                        || asegs_tnew.seg[ixnew].kind == ASkMData);
+         ixnew++;
+      }
+
+      aspacem_assert(ixold >= 0 && ixold <= asegs_told.used);
+      aspacem_assert(ixnew >= 0 && ixnew <= asegs_tnew.used);
+
+      done_old = ixold == asegs_told.used;
+      done_new = ixnew == asegs_tnew.used;
+
+      if (done_old && done_new)
+         goto both_done;
+      if (done_old && !done_new)
+         goto finishup_new;
+      if (done_new && !done_old)
+         goto finishup_old;
+
+      olds = &asegs_told.seg[ixold];
+      news = &asegs_tnew.seg[ixnew];
+
+      aspacem_assert(olds->kind == ASkMText);
+      aspacem_assert(news->kind == ASkMText);
+
+      if (0) {
+         show_AixSegment(0,ixold,&asegs_told.seg[ixold]); 
+         show_AixSegment(0,ixnew,&asegs_tnew.seg[ixnew]); 
+         VG_(debugLog)(0, "aspacem", "\n");
+      }
+
+      /* Here, if olds->start < news->start, then the old sequence has
+         an entry which the new one doesn't, so a module has been
+         unloaded.  If news->start < olds->start then the new sequence
+         has a module the old one doesn't, so a module has been
+         loaded.  If news->start ==olds->start then the module is
+         unchanged.  Except, we should check a bit more carefully in
+         the zero case. */
+      if (olds->start == news->start) {
+         if (olds->start == news->start
+             && olds->end == news->end
+             && olds->fname == news->fname
+             && olds->mname == news->mname
+             && olds->sibling == news->sibling
+             && olds->isMainExe == news->isMainExe) {
+            /* really identical, do nothing */
+         } else {
+            /* Dubious; mark it as an unload of old and load of
+               new. */
+            MODIFY_PRI(directives[*ndirectives], asegs_told, ixold, False);
+            (*ndirectives)++;
+            aspacem_assert(*ndirectives <= 2 * VG_N_ASEGMENTS);
+            MODIFY_PRI(directives[*ndirectives], asegs_tnew, ixnew, True);
+            (*ndirectives)++;
+            aspacem_assert(*ndirectives <= 2 * VG_N_ASEGMENTS);
+         }
+         ixold++;
+         ixnew++;
+         continue;
+      }
+
+      if (olds->start < news->start) {
+         /* discard olds */
+         MODIFY_PRI(directives[*ndirectives], asegs_told, ixold, False);
+         (*ndirectives)++;
+         aspacem_assert(*ndirectives <= 2 * VG_N_ASEGMENTS);
+         ixold++;
+         continue;
+      }
+
+      if (news->start < olds->start) {
+         /* acquire news */
+         MODIFY_PRI(directives[*ndirectives], asegs_tnew, ixnew, True);
+         (*ndirectives)++;
+         aspacem_assert(*ndirectives <= 2 * VG_N_ASEGMENTS);
+         ixnew++;
+         continue;
+      }
+      /* NOTREACHED */
+      aspacem_assert(0);
+   }
+
+  finishup_new:
+   olds = NULL;
+   aspacem_assert(ixold == asegs_told.used);
+   aspacem_assert(ixnew < asegs_tnew.used);
+   while (ixnew < asegs_tnew.used) {
+      news = &asegs_tnew.seg[ixnew];
+      aspacem_assert(news->kind == ASkMText || news->kind == ASkMData
+                     || news->kind == ASkFree);
+      if (news->kind == ASkMText) {
+         MODIFY_PRI(directives[*ndirectives], asegs_tnew, ixnew, True);
+         (*ndirectives)++;
+         aspacem_assert(*ndirectives <= 2 * VG_N_ASEGMENTS);
+      }
+      ixnew++;
+   }
+   goto both_done;
+
+  finishup_old:
+   news = NULL;
+   aspacem_assert(ixnew == asegs_tnew.used);
+   aspacem_assert(ixold < asegs_told.used);
+   while (ixold < asegs_told.used) {
+      olds = &asegs_told.seg[ixold];
+      aspacem_assert(olds->kind == ASkMText || olds->kind == ASkMData
+                     || olds->kind == ASkFree);
+      if (olds->kind == ASkMText) {
+         MODIFY_PRI(directives[*ndirectives], asegs_told, ixold, False);
+         (*ndirectives)++;
+         aspacem_assert(*ndirectives <= 2 * VG_N_ASEGMENTS);
+      }
+      ixold++;
+   }
+   goto both_done;
+
+  both_done:
+   aspacem_assert(ixold == asegs_told.used);
+   aspacem_assert(ixnew == asegs_tnew.used);
+
+   asegs_tnew.used = 0;
+   asegs_told.used = 0;
+
+   aspacem_assert( sane_AixSegments(&asegs_pri) );
+
+#  undef MODIFY_PRI
+}
+
+
+/* Set the initial stack segment.  Contains kludgery.  Also take the
+   opportunity to create fake segs for the millicode areas. */
+
+void VG_(am_aix5_set_initial_client_sp)( Addr sp )
+{
+   static Bool done = False;
+   AixSegment  seg;
+
+   aspacem_assert(!done);
+   done = True;
+
+   /* We are given the initial client SP (that of the root thread).
+      Already on the stack are argv and env.  How far up does it
+      extend?  We assume to the next 64k boundary.  How far down does
+      it extend?  We assume N_FAKE_STACK_PAGES small pages - by
+      default 16M.  Establish those limits and add an AnonC rwx
+      segment. */
+
+   /* The 64k boundary is "justified" as follows.  On 32-bit AIX 5.3,
+      a typical initial SP is 0x2FF22xxx, but the accessible (rw) area
+      beyond that extends up to 0x2FF2FFFF - the next 64k boundary.
+      In 64-bit mode, a typical initial SP might be
+      0xFFF'FFFF'FFFF'E920, and the accessible area extends to
+      0xFFF'FFFF'FFFF'FFFF.  So in both cases, (64k roundup of sp) - 1
+      gives the end of the accessible area. */
+
+   VG_(debugLog)(1,"aspacem", "aix5_set_initial_client_sp( %p )\n",
+                   (void*)sp);
+
+   init_AixSegment( &seg );
+   seg.kind  = ASkAnonC;
+   seg.hasR  = seg.hasW = seg.hasX = True;
+
+   if (sizeof(void*) == 4
+       && ((sp & 0xFFFF0000) == 0x2FF20000
+           || (sp & 0xFFFF0000) == 0x2FF10000)) {
+      /* Gaaah.  Special-case 32-bit mode. */
+      seg.end = 0x2FF2FFFF;
+   } else {
+      seg.end = AM_64K_ROUNDUP(sp) - 1;
+   }
+
+   seg.start = seg.end+1 - N_FAKE_STACK_PAGES * VKI_PAGE_SIZE;
+
+   VG_(debugLog)(1,"aspacem", "aix5_set_initial_client_sp: stack seg:\n");
+   show_AixSegment(1,0, &seg);
+   add_asegment( &asegs_pri, &seg );
+
+   init_AixSegment( &seg );
+   seg.kind  = ASkAnonC;
+   seg.hasR  = seg.hasX = True;
+   seg.start = MAGIC_PAGES_1_BASE;
+   seg.end   = MAGIC_PAGES_1_BASE + MAGIC_PAGES_1_SIZE - 1;
+   VG_(debugLog)(1,"aspacem", "am_aix5_set_initial_client_sp: FAKE1 seg:\n");
+   show_AixSegment(1,0, &seg);
+   add_asegment( &asegs_pri, &seg );
+
+   init_AixSegment( &seg );
+   seg.kind  = ASkAnonC;
+   seg.hasR  = seg.hasX = True;
+   seg.start = MAGIC_PAGES_2_BASE;
+   seg.end   = MAGIC_PAGES_2_BASE + MAGIC_PAGES_2_SIZE - 1;
+   VG_(debugLog)(1,"aspacem", "am_aix5_set_initial_client_sp: FAKE2 seg:\n");
+   show_AixSegment(1,0, &seg);
+   add_asegment( &asegs_pri, &seg );
+}
+
+
+/*-----------------------------------------------------------------*/
+/*---                                                           ---*/
+/*--- Getting segment-starts.                                   ---*/
+/*---                                                           ---*/
+/*-----------------------------------------------------------------*/
+
+/* Print out the segment array (debugging only!). */
+void VG_(am_show_nsegments) ( Int logLevel, HChar* who )
+{
+   show_AixSegments( logLevel, who, &asegs_pri );
+}
+
+/* Get the filename corresponding to this segment, if known and if it
+   has one.  The returned name's storage cannot be assumed to be
+   persistent, so the caller should immediately copy the name
+   elsewhere. */
+HChar* VG_(am_get_filename)( NSegment* seg )
+{
+   ML_(am_barf)("unimplemented: VG_(am_get_filename)");
+   return NULL; /* placate gcc -Wall */
+}
+
+/* Collect up the start addresses of all non-free, non-resvn segments.
+   The interface is a bit strange in order to avoid potential
+   segment-creation races caused by dynamic allocation of the result
+   buffer *starts.
+
+   The function first computes how many entries in the result
+   buffer *starts will be needed.  If this number <= nStarts,
+   they are placed in starts[0..], and the number is returned.
+   If nStarts is not large enough, nothing is written to
+   starts[0..], and the negation of the size is returned.
+
+   Correct use of this function may mean calling it multiple times in
+   order to establish a suitably-sized buffer. */
+
+Int VG_(am_get_segment_starts)( Addr* starts, Int nStarts )
+{
+   Int i, j, nSegs;
+
+   /* don't pass dumbass arguments */
+   aspacem_assert(nStarts >= 0);
+
+   nSegs = 0;
+   for (i = 0; i < asegs_pri.used; i++) {
+      if (asegs_pri.seg[i].kind == ASkFree
+          || asegs_pri.seg[i].kind == ASkPreAlloc)
+         continue;
+      nSegs++;
+   }
+
+   if (nSegs > nStarts) {
+      /* The buffer isn't big enough.  Tell the caller how big it needs
+         to be. */
+      return -nSegs;
+   }
+
+   /* There's enough space.  So write into the result buffer. */
+   aspacem_assert(nSegs <= nStarts);
+
+   j = 0;
+   for (i = 0; i < asegs_pri.used; i++) {
+      if (asegs_pri.seg[i].kind == ASkFree
+          || asegs_pri.seg[i].kind == ASkPreAlloc)
+         continue;
+      starts[j++] = asegs_pri.seg[i].start;
+   }
+
+   aspacem_assert(j == nSegs); /* this should not fail */
+   return nSegs;
+}
+
+
+/*-----------------------------------------------------------------*/
+/*---                                                           ---*/
+/*--- Sanity checking and preening of the segment array.        ---*/
+/*---                                                           ---*/
+/*-----------------------------------------------------------------*/
+
+Bool VG_(am_do_sync_check) ( const HChar* fn, 
+                             const HChar* file, Int line )
+{
+   /* There's nothing we can do here; just return a dummy value. */
+   return False; /* placate gcc */
+}
+
+/* Hook to allow sanity checks to be done from aspacemgr-common.c. */
+void ML_(am_do_sanity_check)( void )
+{
+   Bool ok = sane_AixSegments( &asegs_pri );
+   aspacem_assert(ok);
+}
+
+
+/*-----------------------------------------------------------------*/
+/*---                                                           ---*/
+/*--- Finding segments.                                         ---*/
+/*---                                                           ---*/
+/*-----------------------------------------------------------------*/
+
+/* Finds the segment containing 'a'.  Only returns file/anon/resvn
+   segments.  On AIX5 this is pretty bogus; we fake up an entry as
+   best we can by snooping round for useful information in
+   asegs_pri. */
+
+NSegment const* VG_(am_find_nsegment) ( Addr a )
+{
+   Int             i;
+   AixSegment*     aseg;
+   static NSegment bogus;
+
+   /* Fill in default info. */
+   bogus.kind   = SkAnonC;
+   bogus.start  = 0;
+   bogus.end    = 0;
+   bogus.smode  = SmFixed;
+   bogus.dev    = 0;
+   bogus.ino    = 0;
+   bogus.mode   = 0;
+   bogus.offset = 0;
+   bogus.fnIdx  = -1;
+   bogus.hasR   = bogus.hasW = bogus.hasX = False;
+   bogus.hasT   = False;
+   bogus.isCH   = False;
+   bogus.mark   = False;
+
+   /* Go look for it in the segment table. */
+   i = find_asegment_idx( &asegs_pri, a );
+   aspacem_assert(i >= 0 && i <= asegs_pri.used);
+
+   aseg = &asegs_pri.seg[i];
+   if (aseg->kind == ASkFree || aseg->kind == ASkPreAlloc)
+      return NULL;
+
+   bogus.start  = aseg->start;
+   bogus.end    = aseg->end;
+
+   /* Refine */
+   switch (aseg->kind) {
+      case ASkMText:
+         bogus.kind = SkAnonC; /* hmm, pretty darn bogus */
+         bogus.hasR = bogus.hasX = True;
+         break;
+      case ASkMData:
+         bogus.kind = SkAnonC; /* hmm, pretty darn bogus */
+         bogus.hasR = bogus.hasW = True;
+         break;
+      case ASkShmemC:
+         bogus.kind = SkShmC;
+         bogus.hasR = aseg->hasR;
+         bogus.hasW = aseg->hasW;
+         bogus.hasX = aseg->hasX;
+         break;
+      case ASkAnonC:
+         bogus.kind = SkAnonC;
+         bogus.hasR = aseg->hasR;
+         bogus.hasW = aseg->hasW;
+         bogus.hasX = aseg->hasX;
+         bogus.isCH = aseg->isCH;
+         break;
+      case ASkAnonV:
+         bogus.kind = SkAnonV;
+         bogus.hasR = aseg->hasR;
+         bogus.hasW = aseg->hasW;
+         bogus.hasX = aseg->hasX;
+         break;
+      case ASkFileV:
+         bogus.kind = SkFileV;
+         bogus.hasR = aseg->hasR;
+         bogus.hasW = aseg->hasW;
+         bogus.hasX = aseg->hasX;
+         bogus.offset = aseg->offset;
+         break;
+      default:
+         aspacem_assert(0);
+   }
+
+   return &bogus;
+}
+
+
+/* Find the next segment along from 'here', if it is a file/anon/resvn
+   segment. */
+NSegment const* VG_(am_next_nsegment) ( NSegment* here, Bool fwds )
+{
+   ML_(am_barf)("unimplemented: VG_(am_next_nsegment)");
+   return NULL; /* placate gcc */
+}
+
+
+/* Trivial fn: return the total amount of space in anonymous mappings,
+   both for V and the client.  Is used for printing stats in
+   out-of-memory messages. */
+ULong VG_(am_get_anonsize_total)( void )
+{
+   Int   i;
+   ULong total = 0;
+   for (i = 0; i < asegs_pri.used; i++) {
+      if (asegs_pri.seg[i].kind == ASkAnonC 
+          || asegs_pri.seg[i].kind == ASkAnonV) {
+         total += (ULong)asegs_pri.seg[i].end
+                  - (ULong)asegs_pri.seg[i].start + 1ULL;
+      }
+   }
+   return total;
+}
+
+
+/* Test if a piece of memory is addressable by the client with at
+   least the "prot" protection permissions by examining the underlying
+   segments. */
+Bool VG_(am_is_valid_for_client)( Addr start, SizeT len, 
+                                  UInt prot )
+{
+   NSegment const * const fake = VG_(am_find_nsegment)(start);
+   if (!fake)
+      return False;
+   aspacem_assert(fake->start <= start);
+   aspacem_assert(start + len - 1 <= fake->end);
+   if (fake->kind == SkAnonV || fake->kind == SkFileV)
+      return False;
+   if ((prot & VKI_PROT_READ) && !fake->hasR)
+      return False;
+   if ((prot & VKI_PROT_WRITE) && !fake->hasW)
+      return False;
+   if ((prot & VKI_PROT_EXEC) && !fake->hasX)
+      return False;
+   return True;
+}
+
+/* Variant of VG_(am_is_valid_for_client) which allows free areas to
+   be considered part of the client's addressable space.  It also
+   considers reservations to be allowable, since from the client's
+   point of view they don't exist. */
+Bool VG_(am_is_valid_for_client_or_free_or_resvn)
+   ( Addr start, SizeT len, UInt prot )
+{
+   ML_(am_barf)("unimplemented: "
+                "VG_(am_is_valid_for_client_or_free_or_resvn)");
+   /*NOTREACHED*/
+   return False;
+}
+
+
+/*-----------------------------------------------------------------*/
+/*---                                                           ---*/
+/*--- Startup, including reading /proc/self/maps.               ---*/
+/*---                                                           ---*/
+/*-----------------------------------------------------------------*/
+
+/* Initialise the address space manager, setting up the initial
+   segment list, and reading /proc/self/maps into it.  This must
+   be called before any other function.
+
+   Takes a pointer to the SP at the time V gained control.  This is
+   taken to be the highest usable address (more or less).  Based on
+   that (and general consultation of tea leaves, etc) return a
+   suggested end address for the client's stack. */
+
+Addr VG_(am_startup) ( Addr sp_at_startup )
+{
+   aspacem_assert(sizeof(Word)   == sizeof(void*));
+   aspacem_assert(sizeof(Addr)   == sizeof(void*));
+   aspacem_assert(sizeof(SizeT)  == sizeof(void*));
+   aspacem_assert(sizeof(SSizeT) == sizeof(void*));
+
+   asegs_tnew.used = 0;
+   asegs_told.used = 0;
+
+   asegs_pri.used  = 1;
+   init_AixSegments( &asegs_pri );
+   aspacem_assert( sane_AixSegments(&asegs_pri) );
+
+   if (0)
+      VG_(am_show_nsegments)(0,"AFTER VG_(am_startup)");
+
+   /* We do not make an initial read of /proc/../map since doing so
+      would leave us without a way to communicate the results to a
+      caller.  Hence we expect that the caller (m_main) will call
+      VG_(am_aix5_reread_procmap) soon after this call so as to get
+      the initial code/data segments recorded. */
+
+   /* Return value is irrelevant since we don't lay out the
+      client's stack; it is already done. */
+   return 0; 
+}
+
+
+/*-----------------------------------------------------------------*/
+/*---                                                           ---*/
+/*--- Preallocation (acquiring space from sbrk).                ---*/
+/*---                                                           ---*/
+/*-----------------------------------------------------------------*/
+
+static
+SysRes local_do_sbrk_NO_NOTIFY( Word delta )
+{
+   SysRes res;
+   aspacem_assert(__NR_AIX5_sbrk != __NR_AIX5_UNKNOWN);
+   res = VG_(do_syscall1)(__NR_AIX5_sbrk, (UWord)delta);
+   /* kernel produces (-1, VKI_ENOMEM) on failure.  I think that's
+      ok. */
+   return res;
+}
+
+
+/* Find the ix of a prealloc section containing at least req_sz bytes,
+   or -1 if not found.  Uses best-fit. */
+
+static Int find_prealloc_idx ( SizeT req_sz )
+{
+   SizeT best_sz, this_sz;
+   Int   best_ix, i;
+   aspacem_assert(sizeof(SizeT) == sizeof(Addr));
+   aspacem_assert(req_sz > 0);
+   aspacem_assert(AM_IS_4K_ALIGNED(req_sz));
+
+   best_sz = Addr_MAX;
+   best_ix = -1;
+
+   for (i = 0; i < asegs_pri.used; i++) {
+      AixSegment* s = &asegs_pri.seg[i];
+      if (s->kind != ASkPreAlloc)
+         continue;
+      this_sz
+        = s->end + 1 - s->start;
+      aspacem_assert(this_sz > 0);
+      aspacem_assert(AM_IS_4K_ALIGNED(this_sz));
+      if (this_sz >= req_sz && this_sz < best_sz) {
+         best_sz = this_sz;
+         best_ix = i;
+      }
+   }
+
+   return best_ix;
+}
+
+
+/* Create a new prealloc section containing req_sz bytes.  Returns
+   False if failed, True on success. */
+
+static Bool new_prealloc ( SizeT req_sz )
+{
+   SysRes     sres;
+   AixSegment seg;
+   Addr       start;
+   SSizeT     delta;
+   HChar*     why = NULL;
+
+   aspacem_assert(req_sz > 0);
+   aspacem_assert(AM_IS_4K_ALIGNED(req_sz));
+
+   /* m_syswrap may have decided that it's not currently safe to allow
+      allocations from sbrk-world.  If so, we have to fail. */
+   if (0 && !VG_(am_aix5_sbrk_allowed)) {
+      why = "sbrk disallowed";
+      goto fail;
+   }
+
+   /* Get the current limit. */
+   sres = local_do_sbrk_NO_NOTIFY(0);
+   if (sres.isError) {
+      why = "initial sbrk failed";
+      goto fail;
+   }
+
+   /* Get it page aligned */
+   delta = AM_4K_ROUNDUP(sres.res) - sres.res;
+   aspacem_assert(delta >= 0 && delta < AM_4K_PAGESZ);
+   if (delta > 0) {
+      sres = local_do_sbrk_NO_NOTIFY(delta);
+      if (sres.isError) {
+         why = "aligning sbrk failed";
+         goto fail;
+      }
+   }
+
+   /* Now the brk is aligned.  Try to acquire the block. */
+   sres = local_do_sbrk_NO_NOTIFY(0);
+   if (sres.isError)
+      return False;
+   start = sres.res;
+   aspacem_assert( AM_IS_4K_ALIGNED( start ));
+
+   sres = local_do_sbrk_NO_NOTIFY( req_sz );
+   if (sres.isError) {
+      why = "main sbrk failed";
+      goto fail;
+   }
+
+   /* If this fails, the kernel is acting strange. */
+   aspacem_assert( sres.res == start );
+
+   init_AixSegment( &seg );
+   seg.start = start;
+   seg.end   = start + req_sz - 1;
+   seg.kind  = ASkPreAlloc;
+   seg.hasR  = seg.hasW = seg.hasX = True; /* presumably */
+   add_asegment( &asegs_pri, &seg );
+
+   VG_(debugLog)(
+      1, "aspacem", "new_prealloc: SUCCESS at 0x%llx size %lld\n", 
+         (ULong)start, (ULong)req_sz
+   );
+   return True;
+
+  fail:
+   VG_(debugLog)(1, "aspacem", "new_prealloc: FAILED: %s\n", why);
+   return False;
+}
+
+
+/* Find the ix of a prealloc section capable of holding a block of
+   size req_sz.  If none exists, try to create one first.  Returns -1
+   on failure. */
+
+static Int find_or_create_prealloc_idx ( SizeT req_sz )
+{
+   Int   ix;
+   SizeT req_szX;
+   Bool  alloc_ok;
+
+   if (0)
+      VG_(debugLog)(0, "zz", " find_or_create_prealloc_idx ( %lu )\n", 
+                       req_sz);
+
+   aspacem_assert(sizeof(SizeT) == sizeof(Addr));
+   aspacem_assert(req_sz > 0);
+   aspacem_assert(AM_IS_4K_ALIGNED(req_sz));
+
+   ix = find_prealloc_idx ( req_sz );
+   if (ix >= 0 && ix < asegs_pri.used)
+      return ix;
+
+   /* Not found.  We'll have to allocate one.  Allocate some extra at
+      the same time, so as to give a reservoir from which to satisfy
+      future requests. */
+   aspacem_assert(ix == -1);
+
+   req_szX = req_sz + AM_PREALLOC_EXTRA;
+   aspacem_assert(req_szX > 0);
+   aspacem_assert(AM_IS_4K_ALIGNED(req_szX));
+
+   alloc_ok = new_prealloc( req_szX );
+   if (!alloc_ok)
+      return -1; /* failed */
+
+   /* We should now be able to find it in the segment table. */
+   ix = find_prealloc_idx( req_sz );
+   aspacem_assert(ix >= 0 && ix < asegs_pri.used);
+   return ix;
+}
+
+
+/*-----------------------------------------------------------------*/
+/*---                                                           ---*/
+/*--- The core query-notify mechanism.                          ---*/
+/*---                                                           ---*/
+/*-----------------------------------------------------------------*/
+
+/* Query aspacem to ask where a mapping should go. */
+
+Addr VG_(am_get_advisory) ( MapRequest*  req, 
+                            Bool         forClient, 
+                            /*OUT*/Bool* ok )
+{
+   ML_(am_barf)("unimplemented: VG_(am_get_advisory)");
+   /*NOTREACHED*/
+   return 0; /* placate gcc -Wall */
+}
+
+
+/* Convenience wrapper for VG_(am_get_advisory) for client floating or
+   fixed requests.  If start is zero, a floating request is issued; if
+   nonzero, a fixed request at that address is issued.  Same comments
+   about return values apply. */
+
+Addr VG_(am_get_advisory_client_simple) ( Addr start, SizeT len, 
+                                          /*OUT*/Bool* ok )
+{
+   ML_(am_barf)("unimplemented: VG_(am_get_advisory_client_simple)");
+   /*NOTREACHED*/
+   return 0; /* placate gcc -Wall */
+}
+
+
+/* Notifies aspacem that the client completed an mmap successfully.
+   The segment array is updated accordingly.  If the returned Bool is
+   True, the caller should immediately discard translations from the
+   specified address range. */
+
+Bool
+VG_(am_notify_client_mmap)( Addr a, SizeT len, UInt prot, UInt flags,
+                            Int fd, Off64T offset )
+{
+   AixSegment seg;
+   Bool       needDiscard;
+
+   if (len == 0)
+      return False;
+
+   /* Discard is needed if any of the just-trashed range had T. */
+   needDiscard = True; /* conservative but safe */
+
+   init_AixSegment( &seg );
+   seg.kind   = ASkAnonC; /* XXX bogus: could be a file */
+   seg.start  = a;
+   seg.end    = a + len - 1;
+   seg.hasR   = toBool(prot & VKI_PROT_READ);
+   seg.hasW   = toBool(prot & VKI_PROT_WRITE);
+   seg.hasX   = toBool(prot & VKI_PROT_EXEC);
+
+   if (0)
+   VG_(debugLog)(0,"aspacem","notify mmap ( %p, %ld, %ld, %ld )\n", 
+                             (void*)a, len, (UWord)prot, (UWord)flags);
+
+   add_asegment( &asegs_pri, &seg );
+   AM_SANITY_CHECK("am_notify_client_mmap");
+   return needDiscard;
+}
+
+
+/* Notifies aspacem that the client completed a shmat successfully.
+   The segment array is updated accordingly.  If the returned Bool is
+   True, the caller should immediately discard translations from the
+   specified address range. */
+
+Bool
+VG_(am_notify_client_shmat)( Addr a, SizeT len, UInt prot )
+{
+   AixSegment seg;
+   init_AixSegment( &seg );
+   seg.kind  = ASkShmemC;
+   seg.start = a;
+   seg.end   = seg.start + len - 1;
+   seg.hasR  = (prot & VKI_PROT_READ)  ? True : False;
+   seg.hasW  = (prot & VKI_PROT_WRITE) ? True : False;
+   seg.hasX  = (prot & VKI_PROT_EXEC)  ? True : False;
+   add_asegment( &asegs_pri, &seg );
+   AM_SANITY_CHECK("am_notify_client_shmat");
+   if (0) VG_(am_show_nsegments)(0, "after shmat");
+   return True; /* be paranoid */
+}
+
+
+/* Notifies aspacem that an mprotect was completed successfully.  The
+   segment array is updated accordingly.  Note, as with
+   VG_(am_notify_munmap), it is not the job of this function to reject
+   stupid mprotects, for example the client doing mprotect of
+   non-client areas.  Such requests should be intercepted earlier, by
+   the syscall wrapper for mprotect.  This function merely records
+   whatever it is told.  If the returned Bool is True, the caller
+   should immediately discard translations from the specified address
+   range. */
+
+Bool VG_(am_notify_mprotect)( Addr start, SizeT len, UInt prot )
+{
+   Int  i, iLo, iHi;
+   Bool newR, newW, newX, needDiscard;
+
+   if (len == 0)
+      return False;
+
+   newR = toBool(prot & VKI_PROT_READ);
+   newW = toBool(prot & VKI_PROT_WRITE);
+   newX = toBool(prot & VKI_PROT_EXEC);
+
+   /* Discard is needed if we're dumping X permission */
+   needDiscard = True; /* conservative but correct */
+
+   split_asegments_lo_and_hi( &asegs_pri, start, start+len-1, &iLo, &iHi );
+
+   iLo = find_asegment_idx(&asegs_pri, start);
+   iHi = find_asegment_idx(&asegs_pri, start + len - 1);
+
+   for (i = iLo; i <= iHi; i++) {
+      aspacem_assert(i >= 0 && i < asegs_pri.used);
+      /* Apply the permissions to all relevant segments. */
+      if (asegs_pri.seg[i].kind != ASkFree) {
+         asegs_pri.seg[i].hasR = newR;
+         asegs_pri.seg[i].hasW = newW;
+         asegs_pri.seg[i].hasX = newX;
+         aspacem_assert(sane_AixSegment(&asegs_pri.seg[i]));
+      }
+   }
+   if (0)
+   VG_(debugLog)(0,"aspacem","notify mprotect ( %p, %ld, %ld )\n", 
+                             (void*)start, len, (UWord)prot);
+   /* Changing permissions could have made previously un-mergable
+      segments mergeable.  Therefore have to re-preen them. */
+   preen_asegments(&asegs_pri);
+   AM_SANITY_CHECK("am_notify_mprotect");
+   return needDiscard;
+}
+
+
+/* Notifies aspacem that an munmap completed successfully.  The
+   segment array is updated accordingly.  As with
+   VG_(am_notify_munmap), we merely record the given info, and don't
+   check it for sensibleness.  If the returned Bool is True, the
+   caller should immediately discard translations from the specified
+   address range. */
+
+Bool VG_(am_notify_munmap)( Addr start, SizeT len )
+{
+   Bool       needDiscard = True; /* conservative but safe */
+   AixSegment seg;
+
+   if (len == 0)
+      return False;
+
+   init_AixSegment( &seg );
+   seg.kind  = ASkFree;
+   seg.start = start;
+   seg.end   = start + len - 1;
+   add_asegment( &asegs_pri, &seg );
+   AM_SANITY_CHECK("am_notify_munmap");
+
+   return needDiscard;
+}
+
+
+/*-----------------------------------------------------------------*/
+/*---                                                           ---*/
+/*--- Handling mappings which do not arise directly from the    ---*/
+/*--- simulation of the client.                                 ---*/
+/*---                                                           ---*/
+/*-----------------------------------------------------------------*/
+
+/* --- --- --- map, unmap, protect  --- --- --- */
+
+/* Map a file at a fixed address for the client, and update the
+   segment array accordingly. */
+
+SysRes VG_(am_mmap_file_fixed_client)
+     ( Addr start, SizeT length, UInt prot, Int fd, Off64T offset )
+{
+   SysRes r = {0,0};
+   ML_(am_barf)("unimplemented: VG_(am_mmap_file_fixed_client)");
+   /*NOTREACHED*/
+   return r;
+}
+
+
+/* Map anonymously at a fixed address for the client, and update
+   the segment array accordingly. */
+
+SysRes VG_(am_mmap_anon_fixed_client) ( Addr start, SizeT length, UInt prot )
+{
+   SysRes r = {0,0};
+   ML_(am_barf)("unimplemented: VG_(am_mmap_anon_fixed_client)");
+   /*NOTREACHED*/
+   return r;
+}
+
+
+/* Map anonymously at an unconstrained address for the client, and
+   update the segment array accordingly.  */
+
+SysRes VG_(am_mmap_anon_float_client) ( SizeT length, Int prot )
+{
+   SysRes     sres;
+   AixSegment seg;
+
+   /* Not allowable. */
+   if (length == 0)
+      return VG_(mk_SysRes_Error)( VKI_EINVAL );
+
+   /* AIX seems to demand fd == -1 in anonymous mappings. hence: */
+   sres = VG_(am_do_mmap_NO_NOTIFY)(
+             0, length,
+             prot,
+             VKI_MAP_PRIVATE|VKI_MAP_ANONYMOUS,
+             -1, 0
+          );
+
+   if (!sres.isError) {
+      init_AixSegment( &seg );
+      seg.kind  = ASkAnonC;
+      seg.start = sres.res;
+      seg.end   = seg.start + length - 1;
+      seg.hasR  = toBool((prot & VKI_PROT_READ) > 0);
+      seg.hasW  = toBool((prot & VKI_PROT_WRITE) > 0);
+      seg.hasX  = toBool((prot & VKI_PROT_EXEC) > 0);
+      seg.fromP = False;
+      add_asegment( &asegs_pri, &seg );
+      VG_(debugLog)(2, "aspacem", "new AnonC from mmap, size %lu\n", 
+                       length );
+   }
+
+   return sres;
+}
+
+
+/* Similarly, acquire new address space for the client but with
+   considerable restrictions on what can be done with it: (1) the
+   actual protections may exceed those stated in 'prot', (2) the
+   area's protections cannot be later changed using any form of
+   mprotect, and (3) the area cannot be freed using any form of
+   munmap.  On Linux this behaves the same as
+   VG_(am_mmap_anon_float_client).  On AIX5 this *may* allocate memory
+   by using sbrk, so as to make use of large pages on AIX. */
+
+SysRes VG_(am_sbrk_anon_float_client) ( SizeT length, Int prot )
+{
+   Int        ix;
+   SysRes     sres;
+   AixSegment seg;
+   SizeT      lenX = AM_4K_ROUNDUP(length);
+
+   /* Not allowable. */
+   if (length == 0)
+      return VG_(mk_SysRes_Error)( VKI_EINVAL );
+
+   /* First see if we can get space from sbrk-world. */
+   ix = find_or_create_prealloc_idx ( lenX );
+   if (ix >= 0 && ix < asegs_pri.used) {
+      init_AixSegment( &seg );
+      seg.kind  = ASkAnonC;
+      seg.start = asegs_pri.seg[ix].start;
+      seg.end   = seg.start + lenX - 1;
+      seg.hasR  = toBool((prot & VKI_PROT_READ) > 0);
+      seg.hasW  = toBool((prot & VKI_PROT_WRITE) > 0);
+      seg.hasX  = toBool((prot & VKI_PROT_EXEC) > 0);
+      seg.fromP = True;
+      add_asegment( &asegs_pri, &seg );
+      sres = VG_(mk_SysRes_Success)( seg.start );
+      VG_(debugLog)(2, "aspacem", "new AnonC from prealloc, size %lu\n", 
+                       length );
+      return sres;
+   }
+
+   /* That didn't work out.  Try mmap-world instead. */
+   aspacem_assert(ix == -1);
+   return VG_(am_mmap_anon_float_client)( length, prot );
+}
+
+
+/* Map anonymously at an unconstrained address for V, and update the
+   segment array accordingly.  This is fundamentally how V allocates
+   itself more address space when needed. */
+
+SysRes VG_(am_mmap_anon_float_valgrind)( SizeT length )
+{
+   SysRes     sres;
+   AixSegment seg;
+
+   /* Not allowable. */
+   if (length == 0)
+      return VG_(mk_SysRes_Error)( VKI_EINVAL );
+
+   /* AIX seems to demand fd == -1 in anonymous mappings. hence: */
+   sres = VG_(am_do_mmap_NO_NOTIFY)(
+             0, length,
+             VKI_PROT_READ|VKI_PROT_WRITE|VKI_PROT_EXEC,
+             VKI_MAP_PRIVATE|VKI_MAP_ANONYMOUS,
+             -1, 0
+          );
+
+   if (!sres.isError) {
+      init_AixSegment( &seg );
+      seg.kind  = ASkAnonV;
+      seg.start = sres.res;
+      seg.end   = seg.start + length - 1;
+      seg.hasR  = seg.hasW = seg.hasX = True;
+      seg.fromP = False;
+      add_asegment( &asegs_pri, &seg );
+      VG_(debugLog)(2, "aspacem", "new AnonV from mmap, size %lu\n", 
+                       length );
+   }
+
+   return sres;
+}
+
+
+/* Same comments apply as per VG_(am_sbrk_anon_float_client).  On
+   Linux this behaves the same as VG_(am_mmap_anon_float_valgrind). */
+SysRes VG_(am_sbrk_anon_float_valgrind)( SizeT length )
+{
+   Int        ix;
+   SysRes     sres;
+   AixSegment seg;
+   SizeT      lenX = AM_4K_ROUNDUP(length);
+
+   /* Not allowable. */
+   if (length == 0)
+      return VG_(mk_SysRes_Error)( VKI_EINVAL );
+
+   /* First see if we can get space from sbrk-world. */
+   ix = find_or_create_prealloc_idx ( lenX );
+   if (ix >= 0 && ix < asegs_pri.used) {
+      init_AixSegment( &seg );
+      seg.kind  = ASkAnonV;
+      seg.start = asegs_pri.seg[ix].start;
+      seg.end   = seg.start + lenX - 1;
+      seg.hasR  = True;
+      seg.hasW  = True;
+      seg.hasX  = True;
+      seg.fromP = True;
+      add_asegment( &asegs_pri, &seg );
+      sres = VG_(mk_SysRes_Success)( seg.start );
+      VG_(debugLog)(2, "aspacem", "new AnonV from prealloc, size %lu\n", 
+                       length );
+      return sres;
+   }
+
+   /* That didn't work out.  Try mmap-world instead. */
+   aspacem_assert(ix == -1);
+   return VG_(am_mmap_anon_float_valgrind)( length );
+}
+
+
+/* Really just a wrapper around VG_(am_sbrk_anon_float_valgrind). */
+
+void* VG_(am_shadow_alloc)(SizeT size)
+{
+   SysRes sres = VG_(am_sbrk_anon_float_valgrind)( size );
+   return sres.isError ? NULL : (void*)sres.res;
+}
+
+
+/* Map a file at an unconstrained address for V, and update the
+   segment array accordingly.  This is used by V for transiently
+   mapping in object files to read their debug info. */
+
+SysRes VG_(am_mmap_file_float_valgrind) ( SizeT length, UInt prot, 
+                                          Int fd, Off64T offset )
+{
+   SysRes sres;
+
+   /* Not allowable. */
+   if (length == 0 || !VG_IS_PAGE_ALIGNED(offset))
+      return VG_(mk_SysRes_Error)( VKI_EINVAL );
+
+   sres = VG_(am_do_mmap_NO_NOTIFY)(
+             0, length,
+             prot, VKI_MAP_PRIVATE,
+             fd, offset
+          );
+   if (!sres.isError) {
+      AixSegment seg;
+      init_AixSegment( &seg );
+      seg.kind = SkFileV;
+      seg.start = sres.res;
+      seg.end = seg.start + length - 1;
+      seg.hasR   = toBool(prot & VKI_PROT_READ);
+      seg.hasW   = toBool(prot & VKI_PROT_WRITE);
+      seg.hasX   = toBool(prot & VKI_PROT_EXEC);
+      seg.fname  = add_to_strtab("(FileV-float, unknown name)");
+      add_asegment( &asegs_pri, &seg );
+      aspacem_assert( sane_AixSegments( &asegs_pri ));
+   }
+   return sres;
+}
+
+
+/* Unmap the given address range and update the segment array
+   accordingly.  This fails if the range isn't valid for the client.
+   If *need_discard is True after a successful return, the caller
+   should immediately discard translations from the specified address
+   range. */
+
+SysRes VG_(am_munmap_client)( /*OUT*/Bool* need_discard,
+                              Addr start, SizeT len )
+{
+   SysRes r = {0,0}; 
+   ML_(am_barf)("unimplemented: VG_(am_munmap_client)");
+   /*NOTREACHED*/
+   return r;
+}
+
+
+/* Unmap the given address range and update the segment array
+   accordingly.  This fails if the range isn't valid for valgrind. */
+/* Also, if the specified range doesn't fall within a single segment,
+   it barfs.  This simplifies the implementation; we shouldn't need to
+   deal with anything but the simplest cases. */
+
+SysRes VG_(am_munmap_valgrind)( Addr start, SizeT len )
+{
+   AixSegment* seg;
+   AixSegment  seg2;
+   Addr        end;
+   SysRes      sres;
+   Int         ixS, ixE;
+   Bool        debug = False;
+
+   if (debug)
+      VG_(debugLog)(0,"aspacem", 
+                      "am_munmap_valgrind(%p, %lu)\n", (void*)start, len);
+
+   if (len == 0)
+      return VG_(mk_SysRes_Success)(0);
+
+   /* We have to be a bit careful here.  If the area being unmapped is
+      AnonV which originated from a preallocated area (hence from
+      sbrk-land) then we will have to return it to the preallocated
+      state, rather than unmapping it.  */
+   end = start + len - 1;
+   aspacem_assert(start <= end); // else have wraparound?!
+
+   ixS = find_asegment_idx( &asegs_pri, start );
+   ixE = find_asegment_idx( &asegs_pri, end );
+
+   aspacem_assert(ixS >= 0 && ixS < asegs_pri.used);
+   aspacem_assert(ixE >= 0 && ixE < asegs_pri.used);
+
+   /* Preconditions: See comment at start of fn */
+   aspacem_assert(ixS == ixE);
+
+   /* For the segment S denoted by ixS:
+
+      - if S is AnonV from prealloc and S entirely within start .. end,
+        return it to prealloc
+
+      - if S is AnonV not from prealloc and S entirely within start .. end,
+        munmap it
+
+      - if S is FileV and S entirely within start .. end, munmap it
+
+      Otherwise, leave it alone (too complex to handle).  In theory
+      this could cause a leak; in practice I don't think it will.
+   */
+   seg = &asegs_pri.seg[ixS];
+
+   if (debug)
+      show_AixSegment( 0, ixS, seg );
+
+   /* Invariants */
+   aspacem_assert(seg->start <= start);
+   aspacem_assert(end <= seg->end);
+
+   if (seg->kind == ASkFileV
+       || (seg->kind == ASkAnonV && (!seg->fromP))) {
+      if (debug)
+         VG_(debugLog)(0,"aspacem", "am_munmap_valgrind: !fromP: %p-%p\n",
+                         (void*)start, (void*)end);
+      sres = ML_(am_do_munmap_NO_NOTIFY)( start, len );
+      if (sres.isError)
+         goto bad;
+      init_AixSegment( &seg2 );
+      seg2.start = start;
+      seg2.end   = end;
+      seg2.kind  = ASkFree;
+      add_asegment( &asegs_pri, &seg2 );
+   }
+   else
+   if (seg->kind == ASkAnonV && seg->fromP) {
+      if (debug)
+         VG_(debugLog)(0,"aspacem", "am_munmap_valgrind:  fromP: %p-%p\n",
+                         (void*)start, (void*)end);
+      init_AixSegment( &seg2 );
+      seg2.start = start;
+      seg2.end   = end;
+      seg2.kind  = ASkPreAlloc;
+      seg2.hasR  = seg2.hasW = seg2.hasX = True;
+      add_asegment( &asegs_pri, &seg2 );
+   }
+   else {
+      /* shouldn't be asked to handle any other cases */
+      aspacem_assert(0);
+   }
+
+   aspacem_assert( sane_AixSegments( &asegs_pri ));
+   return VG_(mk_SysRes_Success)(0);
+
+  bad:
+   aspacem_assert( sane_AixSegments( &asegs_pri ));
+   return VG_(mk_SysRes_Error)(VKI_EINVAL);
+}
+
+
+/* Let (start,len) denote an area within a single Valgrind-owned
+  segment (anon or file).  Change the ownership of [start, start+len)
+  to the client instead.  Fails if (start,len) does not denote a
+  suitable segment. */
+
+Bool VG_(am_change_ownership_v_to_c)( Addr start, SizeT len )
+{
+   return True;
+}
+
+
+/* 'seg' must be NULL or have been obtained from
+   VG_(am_find_nsegment), and still valid.  If non-NULL, and if it
+   denotes a SkAnonC (anonymous client mapping) area, set the .isCH
+   (is-client-heap) flag for that area.  Otherwise do nothing.
+   (Bizarre interface so that the same code works for both Linux and
+   AIX and does not impose inefficiencies on the Linux version.) */
+/* AIX: presumably this is a faked-up segment our VG_(am_find_segment)
+   came up with.  So we have to find the corresponding AixSegment. */
+
+void VG_(am_set_segment_isCH_if_SkAnonC)( NSegment* seg )
+{
+   Int i;
+   if (seg == NULL)
+      return;
+   i = find_asegment_idx( &asegs_pri, seg->start );
+   aspacem_assert(i >= 0 && i < asegs_pri.used );
+   if (asegs_pri.seg[i].kind == ASkAnonC) {
+      asegs_pri.seg[i].isCH = True;
+      if (0)
+         VG_(debugLog)(0,"aspacem","set isCH for %p\n", (void*)seg->start );
+   } else {
+      aspacem_assert(asegs_pri.seg[i].isCH == False);
+   }
+}
+
+
+/* Same idea as VG_(am_set_segment_isCH_if_SkAnonC), except set the
+   segment's hasT bit (has-cached-code) if this is SkFileC or SkAnonC
+   segment. */
+/* AIX: we ignore these complexities by conservatively assuming that
+   all segments had translations taken from them.  Hence we can safely
+   ignore this. */
+void VG_(am_set_segment_hasT_if_SkFileC_or_SkAnonC)( NSegment* seg )
+{
+}
+
+
+/* --- --- --- reservations --- --- --- */
+
+/* Create a reservation from START .. START+LENGTH-1, with the given
+   ShrinkMode.  When checking whether the reservation can be created,
+   also ensure that at least abs(EXTRA) extra free bytes will remain
+   above (> 0) or below (< 0) the reservation.
+
+   The reservation will only be created if it, plus the extra-zone,
+   falls entirely within a single free segment.  The returned Bool
+   indicates whether the creation succeeded. */
+
+Bool VG_(am_create_reservation) ( Addr start, SizeT length, 
+                                  ShrinkMode smode, SSizeT extra )
+{
+   ML_(am_barf)("unimplemented: VG_(am_create_reservation)");
+   /*NOTREACHED*/
+   return False;
+}
+
+
+/* Let SEG be an anonymous client mapping.  This fn extends the
+   mapping by DELTA bytes, taking the space from a reservation section
+   which must be adjacent.  If DELTA is positive, the segment is
+   extended forwards in the address space, and the reservation must be
+   the next one along.  If DELTA is negative, the segment is extended
+   backwards in the address space and the reservation must be the
+   previous one.  DELTA must be page aligned.  abs(DELTA) must not
+   exceed the size of the reservation segment minus one page, that is,
+   the reservation segment after the operation must be at least one
+   page long. */
+
+Bool VG_(am_extend_into_adjacent_reservation_client) ( NSegment* seg, 
+                                                       SSizeT    delta )
+{
+   ML_(am_barf)("unimplemented: "
+                "VG_(am_extend_into_adjacent_reservation_client)");
+   /*NOTREACHED*/
+   return False;
+}
+
+
+/* --- --- --- resizing/move a mapping --- --- --- */
+
+/* Let SEG be a client mapping (anonymous or file).  This fn extends
+   the mapping forwards only by DELTA bytes, and trashes whatever was
+   in the new area.  Fails if SEG is not a single client mapping or if
+   the new area is not accessible to the client.  Fails if DELTA is
+   not page aligned.  *seg is invalid after a successful return.  If
+   *need_discard is True after a successful return, the caller should
+   immediately discard translations from the new area. */
+
+Bool VG_(am_extend_map_client)( /*OUT*/Bool* need_discard,
+                                NSegment* seg, SizeT delta )
+{
+   ML_(am_barf)("unimplemented: VG_(am_extend_map_client)");
+   /*NOTREACHED*/
+   return False;
+}
+
+
+/* Remap the old address range to the new address range.  Fails if any
+   parameter is not page aligned, if the either size is zero, if any
+   wraparound is implied, if the old address range does not fall
+   entirely within a single segment, if the new address range overlaps
+   with the old one, or if the old address range is not a valid client
+   mapping.  If *need_discard is True after a successful return, the
+   caller should immediately discard translations from both specified
+   address ranges.  */
+
+Bool VG_(am_relocate_nooverlap_client)( /*OUT*/Bool* need_discard,
+                                        Addr old_addr, SizeT old_len,
+                                        Addr new_addr, SizeT new_len )
+{
+   ML_(am_barf)("unimplemented: VG_(am_relocate_nooverlap_client)");
+   /*NOTREACHED*/
+   return False;
+}
+
+
+
+/*-----------------------------------------------------------------*/
+/*---                                                           ---*/
+/*--- A simple parser for /proc/<pid>/map on AIX5.              ---*/
+/*--- Almost completely independent of the stuff above.  The    ---*/
+/*--- only function it 'exports' to the code above this comment ---*/
+/*--- is parse_procselfmaps.                                    ---*/
+/*---                                                           ---*/
+/*-----------------------------------------------------------------*/
+
+/* --- !!! --- EXTERNAL HEADERS start --- !!! --- */
+#include <sys/procfs.h>
+/* --- !!! --- EXTERNAL HEADERS end --- !!! --- */
+
+
+/* Size of a smallish table used to read /proc/<pid>/map entries. */
+#define M_APROCMAP_BUF 100000
+
+/* static ... to keep it out of the stack frame. */
+static HChar procmap_buf[M_APROCMAP_BUF];
+
+/* Records length of /proc/<pid>/map read into procmap_buf. */
+static Int buf_n_tot;
+
+/* Helper fns. */
+
+/* Get the contents of /proc/<pid>/map into a static buffer.  If
+   there's a syntax error, it won't fit, or other failure, just
+   abort. */
+
+static void read_procselfmap_into_buf ( void )
+{
+   Char   fname[50];
+   Int    n_chunk;
+   SysRes fd;
+
+   ML_(am_sprintf)( fname, "/proc/%d/map", ML_(am_getpid)() );
+
+   /* Read the initial memory mapping from the /proc filesystem. */
+   fd = ML_(am_open)( fname, VKI_O_RDONLY, 0 );
+   if (fd.isError)
+      ML_(am_barf)("can't open /proc/<pid>/map");
+
+   buf_n_tot = 0;
+   do {
+      n_chunk = ML_(am_read)( fd.res, &procmap_buf[buf_n_tot],
+                              M_APROCMAP_BUF - buf_n_tot );
+      buf_n_tot += n_chunk;
+   } while ( n_chunk > 0 && buf_n_tot < M_APROCMAP_BUF );
+
+   ML_(am_close)(fd.res);
+
+   if (buf_n_tot >= M_APROCMAP_BUF-5)
+      ML_(am_barf_toolow)("M_APROCMAP_BUF");
+   if (buf_n_tot == 0)
+      ML_(am_barf)("I/O error on /proc/<pid>/map");
+
+   procmap_buf[buf_n_tot] = 0;
+}
+
+
+/* /proc/<pid>/map appears to give out a non-absolute path name for
+   the main executable.  Fortunately we can reliably identify the main
+   executable via the MA_MAINEXEC bit, and if we find the path is
+   non-absolute, replace it with /proc/<pid>/object/a.out instead.
+   AIX guarantees the latter is another name for the main
+   executable. */
+
+static HChar* kludge_exe_file_name ( HChar* file_name, prmap_t* map )
+{
+   static Int   my_pid = -1;
+   static HChar a_out_name[64];
+   if (file_name == NULL)
+      return NULL;
+   if (file_name[0] != '/' && (map->pr_mflags & MA_MAINEXEC)) {
+      if (my_pid == -1)
+         my_pid = ML_(am_getpid)();
+      ML_(am_sprintf)(a_out_name, "/proc/%d/object/a.out", my_pid);
+      file_name = a_out_name;
+   }
+   return file_name;
+}
+
+
+
+/* Parse /proc/<pid>/map, copying the entries in it into an
+   AixSegments structure.  Returns a properly formed AixSegments, with
+   ASkMText/ASkMData entries, with sibling pointers set up, and
+   ASkFree everywhere else.
+*/
+static void parse_procselfmap ( /*OUT*/AixSegments* segs )
+{
+   UChar      rr, ww, xx, mm, ss;
+   prmap_t*   map;
+   UChar*     file_name;
+   UChar*     member_name;
+   Bool       show_map;
+   Int        off, i, j;
+   AixSegment s;
+
+   const UInt valid_pr_mflags
+      = MA_MAINEXEC | MA_KERNTEXT | MA_READ | MA_WRITE 
+        | MA_EXEC | MA_SHARED | MA_BREAK | MA_STACK;
+
+   segs->used = 1;
+   init_AixSegments(segs);
+   aspacem_assert( sane_AixSegments(segs) );
+
+   read_procselfmap_into_buf();
+
+   if (0)
+      VG_(debugLog)(0, "procselfmaps", "got %d bytes\n", buf_n_tot);
+
+   off = 0;
+   while (True) {
+
+      /* stay sane .. */
+      if (off + sizeof(prmap_t) > buf_n_tot)
+         break;
+      map = (prmap_t*)&procmap_buf[off];
+      off += sizeof(prmap_t);
+
+      /* When should we stop reading the array?
+         /usr/include/sys/procfs.h says that "Array entries continue
+         until an entry with a pr_size field of 0 and invalid
+         pr_mflags occurs."  It unhelpfully fails to define what
+         "invalid" means here.  However, the following test _seems_ to
+         work. */
+      if (map->pr_size == 0 
+          && (map->pr_mflags & valid_pr_mflags) == 0)
+         break;
+
+      /* Ok, keep going, but ignore any zero-sized mappings: */
+      if (map->pr_size == 0)
+         continue;
+
+      mm = (map->pr_mflags & MA_MAINEXEC) > 0;
+      rr = (map->pr_mflags & MA_READ) > 0;
+      ww = (map->pr_mflags & MA_WRITE) > 0;
+      xx = (map->pr_mflags & MA_EXEC) > 0;
+      ss = (map->pr_mflags & MA_SHARED) > 0;
+
+      if (map->pr_pathoff > 0) {
+         file_name   = &procmap_buf[map->pr_pathoff];
+         member_name = file_name + VG_(strlen)(file_name) + 1;
+         if (*member_name == 0)
+            member_name = NULL;
+      } else {
+         file_name = member_name = NULL;
+      }
+      file_name = kludge_exe_file_name( file_name, map );
+
+      /* Now file_name and member_name are NULL or ordinary strings.
+         Convert them to string-table resident strings. */
+      if (file_name)
+         file_name = add_to_strtab(file_name);
+      if (member_name)
+         member_name = add_to_strtab(member_name);
+
+      /* Create a suitable kind of segment.  Initially we will start
+         with bogus sibling pointers, and allow ASkMData entries to
+         have file names, since we cannot assume anything about the
+         ordering of entries in the procmap file.  In a second pass,
+         we will set up the sibling pointers based on those file
+         names, then remove the MData file names. */
+      init_AixSegment(&s);
+      show_map = False;
+      if (rr && (!ww) && xx) {
+         if (map->pr_size > 0) {
+            /* r-x segment; add bounds for a text area. */
+            s.kind    = ASkMText;
+            s.start   = (Addr)map->pr_vaddr;
+            s.end     = (Addr)map->pr_vaddr + (Addr)map->pr_size - 1;
+            s.isMainExe = mm;
+            s.sibling = 0;
+            s.fname   = file_name;
+            s.mname   = member_name;
+            s.hasR = rr;
+            s.hasW = ww;
+            s.hasX = xx;
+            add_asegment(segs, &s);
+         }
+      }
+      else
+      if (rr && ww && (!xx)) {
+         if (map->pr_size > 0) {
+            /* rw- segment; add bounds for a data area. */
+            s.kind    = ASkMData;
+            s.start   = (Addr)map->pr_vaddr;
+            s.end     = (Addr)map->pr_vaddr + (Addr)map->pr_size - 1;
+            /* Set a bogus non-zero sibling pointer, since sanity
+               checking will reject zero sibling pointers on MData.
+               It doesn't matter since the loops following this one
+               below fix up the sibling pointers. */
+            s.sibling = 1;
+            s.fname   = file_name;
+            s.mname   = member_name;
+            s.hasR = rr;
+            s.hasW = ww;
+            s.hasX = xx;
+            add_asegment(segs, &s);
+         }
+      }
+      else {
+         /* unclassifiable; we better complain. */
+         show_map = True;
+         VG_(debugLog)(0, "aspacem", "parse_procselfmap: unclassifiable:\n");
+      }
+
+      if (show_map)
+         VG_(debugLog)(1,"aspacem",
+                       "  %010llx-%010llx %c%c%c%c%c %s%s%s%s\n",
+                       (ULong)map->pr_vaddr,
+                       (ULong)map->pr_vaddr + (ULong)map->pr_size,
+                       mm ? 'M' : '-',
+                       rr ? 'r' : '-',
+                       ww ? 'w' : '-',
+                       xx ? 'x' : '-',
+                       ss ? 'S' : '-',
+                       file_name ? file_name : (UChar*)"(none)",
+                       member_name ? "(" : "",
+                       member_name ? member_name : (UChar*)"",
+                       member_name ? ")" : ""
+         );
+
+   }
+
+   /* Set up sibling pointers.  For each MData, find an MText with the
+      same file/member names, or complain.  This is really ugly in
+      that it makes the process quadratic in the number of modules
+      mapped in, but I can't think of a (simple) better way.  */
+
+   for (i = 0; i < segs->used; i++) {
+      if (segs->seg[i].kind != ASkMData)
+         continue;
+      for (j = 0; j < segs->used; j++) {
+         if (segs->seg[j].kind == ASkMText 
+             && segs->seg[j].fname == segs->seg[i].fname
+             && segs->seg[j].mname == segs->seg[i].mname)
+            break;
+      }
+      if (j == segs->used) {
+         VG_(debugLog)(0, "aspacem", "parse_procselfmap: "
+            "data segment with no associated text segment:\n");
+         VG_(debugLog)(0, "aspacem", "module = %s(%s)\n",
+                          segs->seg[i].fname,
+                          segs->seg[i].mname ? segs->seg[i].mname 
+                                             : (UChar*)"(none)");
+         aspacem_assert(0);
+      }
+      aspacem_assert(j >= 0 && j < segs->used && j != i);
+      segs->seg[i].sibling = segs->seg[j].start;
+   }
+
+   /* (Almost) dually, for each MText, find an MData with same
+      file/member names, but don't complain if not present. */
+
+   for (i = 0; i < segs->used; i++) {
+      if (segs->seg[i].kind != ASkMText)
+         continue;
+      for (j = 0; j < segs->used; j++) {
+         if (segs->seg[j].kind == ASkMData 
+             && segs->seg[j].fname == segs->seg[i].fname
+             && segs->seg[j].mname == segs->seg[i].mname)
+            break;
+      }
+      if (j == segs->used) {
+         /* no corresponding MData found; harmless. */
+      } else {
+         aspacem_assert(j >= 0 && j < segs->used && j != i);
+         segs->seg[i].sibling = segs->seg[j].start;
+      }
+   }
+
+   /* Finally, get rid of fname/mname pointers on MDatas, so as to
+      adhere to the necessary representational invariants. */
+   for (i = 0; i < segs->used; i++) {
+      if (segs->seg[i].kind == ASkMData){
+         segs->seg[i].fname = segs->seg[i].mname = NULL;
+      }
+   }
+
+   aspacem_assert( sane_AixSegments(segs) );
+   if (0)
+      show_AixSegments(0, "as read from procmap", segs);
+}
+
+
+/*--------------------------------------------------------------------*/
+/*--- end                                                          ---*/
+/*--------------------------------------------------------------------*/
index f79a58a8eb875d0e2d6e69f84450e08b5e2dce37..42355dd9d6ddeaeec91b5f46bfa8fac2f8196e3c 100644 (file)
@@ -95,7 +95,7 @@ Int ML_(am_getpid)( void )
 {
    SysRes sres = VG_(do_syscall0)(__NR_getpid);
    aspacem_assert(!sres.isError);
-   return sres.val;
+   return sres.res;
 }
 
 
@@ -235,7 +235,7 @@ SysRes ML_(am_open) ( const Char* pathname, Int flags, Int mode )
 Int ML_(am_read) ( Int fd, void* buf, Int count)
 {
    SysRes res = VG_(do_syscall3)(__NR_read, fd, (UWord)buf, count);
-   return res.isError ? -1 : res.val;
+   return res.isError ? -1 : res.res;
 }
 
 void ML_(am_close) ( Int fd )
@@ -247,7 +247,7 @@ Int ML_(am_readlink)(HChar* path, HChar* buf, UInt bufsiz)
 {
    SysRes res;
    res = VG_(do_syscall3)(__NR_readlink, (UWord)path, (UWord)buf, bufsiz);
-   return res.isError ? -1 : res.val;
+   return res.isError ? -1 : res.res;
 }
 
 /* Get the dev, inode and mode info for a file descriptor, if
@@ -310,7 +310,7 @@ VgStack* VG_(am_alloc_VgStack)( /*OUT*/Addr* initial_sp )
    if (sres.isError)
       return NULL;
 
-   stack = (VgStack*)sres.val;
+   stack = (VgStack*)sres.res;
 
    aspacem_assert(VG_IS_PAGE_ALIGNED(szB));
    aspacem_assert(VG_IS_PAGE_ALIGNED(stack));
index e347d022e29de689ba49a7ab2e71128cbd7e9f50..35ef5117373a0529b2e4f08669c020d5b213cf71 100644 (file)
@@ -1127,8 +1127,9 @@ static Int find_nsegment_idx ( Addr a )
 
 
 /* Finds the segment containing 'a'.  Only returns file/anon/resvn
-   segments. */
-NSegment* VG_(am_find_nsegment) ( Addr a )
+   segments.  This returns a 'NSegment const *' - a pointer to 
+   readonly data. */
+NSegment const * VG_(am_find_nsegment) ( Addr a )
 {
    Int i = find_nsegment_idx(a);
    aspacem_assert(i >= 0 && i < nsegments_used);
@@ -1159,7 +1160,7 @@ static Int segAddr_to_index ( NSegment* seg )
 
 /* Find the next segment along from 'here', if it is a file/anon/resvn
    segment. */
-NSegment* VG_(am_next_nsegment) ( NSegment* here, Bool fwds )
+NSegment const * VG_(am_next_nsegment) ( NSegment* here, Bool fwds )
 {
    Int i = segAddr_to_index(here);
    if (i < 0 || i >= nsegments_used)
@@ -2095,11 +2096,11 @@ SysRes VG_(am_mmap_file_fixed_client)
    if (sres.isError)
       return sres;
 
-   if (sres.val != start) {
+   if (sres.res != start) {
       /* I don't think this can happen.  It means the kernel made a
          fixed map succeed but not at the requested location.  Try to
          repair the damage, then return saying the mapping failed. */
-      (void)ML_(am_do_munmap_NO_NOTIFY)( sres.val, length );
+      (void)ML_(am_do_munmap_NO_NOTIFY)( sres.res, length );
       return VG_(mk_SysRes_Error)( VKI_EINVAL );
    }
 
@@ -2161,11 +2162,11 @@ SysRes VG_(am_mmap_anon_fixed_client) ( Addr start, SizeT length, UInt prot )
    if (sres.isError)
       return sres;
 
-   if (sres.val != start) {
+   if (sres.res != start) {
       /* I don't think this can happen.  It means the kernel made a
          fixed map succeed but not at the requested location.  Try to
          repair the damage, then return saying the mapping failed. */
-      (void)ML_(am_do_munmap_NO_NOTIFY)( sres.val, length );
+      (void)ML_(am_do_munmap_NO_NOTIFY)( sres.res, length );
       return VG_(mk_SysRes_Error)( VKI_EINVAL );
    }
 
@@ -2218,11 +2219,11 @@ SysRes VG_(am_mmap_anon_float_client) ( SizeT length, Int prot )
    if (sres.isError)
       return sres;
 
-   if (sres.val != advised) {
+   if (sres.res != advised) {
       /* I don't think this can happen.  It means the kernel made a
          fixed map succeed but not at the requested location.  Try to
          repair the damage, then return saying the mapping failed. */
-      (void)ML_(am_do_munmap_NO_NOTIFY)( sres.val, length );
+      (void)ML_(am_do_munmap_NO_NOTIFY)( sres.res, length );
       return VG_(mk_SysRes_Error)( VKI_EINVAL );
    }
 
@@ -2241,6 +2242,21 @@ SysRes VG_(am_mmap_anon_float_client) ( SizeT length, Int prot )
 }
 
 
+/* Similarly, acquire new address space for the client but with
+   considerable restrictions on what can be done with it: (1) the
+   actual protections may exceed those stated in 'prot', (2) the
+   area's protections cannot be later changed using any form of
+   mprotect, and (3) the area cannot be freed using any form of
+   munmap.  On Linux this behaves the same as
+   VG_(am_mmap_anon_float_client).  On AIX5 this *may* allocate memory
+   by using sbrk, so as to make use of large pages on AIX. */
+
+SysRes VG_(am_sbrk_anon_float_client) ( SizeT length, Int prot )
+{
+   return VG_(am_mmap_anon_float_client) ( length, prot );
+}
+
+
 /* Map anonymously at an unconstrained address for V, and update the
    segment array accordingly.  This is fundamentally how V allocates
    itself more address space when needed. */
@@ -2277,11 +2293,11 @@ SysRes VG_(am_mmap_anon_float_valgrind)( SizeT length )
    if (sres.isError)
       return sres;
 
-   if (sres.val != advised) {
+   if (sres.res != advised) {
       /* I don't think this can happen.  It means the kernel made a
          fixed map succeed but not at the requested location.  Try to
          repair the damage, then return saying the mapping failed. */
-      (void)ML_(am_do_munmap_NO_NOTIFY)( sres.val, length );
+      (void)ML_(am_do_munmap_NO_NOTIFY)( sres.res, length );
       return VG_(mk_SysRes_Error)( VKI_EINVAL );
    }
 
@@ -2304,9 +2320,16 @@ SysRes VG_(am_mmap_anon_float_valgrind)( SizeT length )
 void* VG_(am_shadow_alloc)(SizeT size)
 {
    SysRes sres = VG_(am_mmap_anon_float_valgrind)( size );
-   return sres.isError ? NULL : (void*)sres.val;
+   return sres.isError ? NULL : (void*)sres.res;
 }
 
+/* Same comments apply as per VG_(am_sbrk_anon_float_client).  On
+   Linux this behaves the same as VG_(am_mmap_anon_float_valgrind). */
+
+SysRes VG_(am_sbrk_anon_float_valgrind)( SizeT cszB )
+{
+   return VG_(am_mmap_anon_float_valgrind)( cszB );
+}
 
 
 /* Map a file at an unconstrained address for V, and update the
@@ -2348,18 +2371,18 @@ SysRes VG_(am_mmap_file_float_valgrind) ( SizeT length, UInt prot,
    if (sres.isError)
       return sres;
 
-   if (sres.val != advised) {
+   if (sres.res != advised) {
       /* I don't think this can happen.  It means the kernel made a
          fixed map succeed but not at the requested location.  Try to
          repair the damage, then return saying the mapping failed. */
-      (void)ML_(am_do_munmap_NO_NOTIFY)( sres.val, length );
+      (void)ML_(am_do_munmap_NO_NOTIFY)( sres.res, length );
       return VG_(mk_SysRes_Error)( VKI_EINVAL );
    }
 
    /* Ok, the mapping succeeded.  Now notify the interval map. */
    init_nsegment( &seg );
    seg.kind   = SkFileV;
-   seg.start  = sres.val;
+   seg.start  = sres.res;
    seg.end    = seg.start + VG_PGROUNDUP(length) - 1;
    seg.offset = offset;
    seg.hasR   = toBool(prot & VKI_PROT_READ);
@@ -2495,6 +2518,35 @@ Bool VG_(am_change_ownership_v_to_c)( Addr start, SizeT len )
    return True;
 }
 
+/* 'seg' must be NULL or have been obtained from
+   VG_(am_find_nsegment), and still valid.  If non-NULL, and if it
+   denotes a SkAnonC (anonymous client mapping) area, set the .isCH
+   (is-client-heap) flag for that area.  Otherwise do nothing.
+   (Bizarre interface so that the same code works for both Linux and
+   AIX and does not impose inefficiencies on the Linux version.) */
+void VG_(am_set_segment_isCH_if_SkAnonC)( NSegment* seg )
+{
+   Int i = segAddr_to_index( seg );
+   aspacem_assert(i >= 0 && i < nsegments_used);
+   if (nsegments[i].kind == SkAnonC) {
+      nsegments[i].isCH = True;
+   } else {
+      aspacem_assert(nsegments[i].isCH == False);
+   }
+}
+
+/* Same idea as VG_(am_set_segment_isCH_if_SkAnonC), except set the
+   segment's hasT bit (has-cached-code) if this is SkFileC or SkAnonC
+   segment. */
+void VG_(am_set_segment_hasT_if_SkFileC_or_SkAnonC)( NSegment* seg )
+{
+   Int i = segAddr_to_index( seg );
+   aspacem_assert(i >= 0 && i < nsegments_used);
+   if (nsegments[i].kind == SkAnonC || nsegments[i].kind == SkFileC) {
+      nsegments[i].hasT = True;
+   }
+}
+
 
 /* --- --- --- reservations --- --- --- */
 
@@ -2615,9 +2667,9 @@ Bool VG_(am_extend_into_adjacent_reservation_client) ( NSegment* seg,
              );
       if (sres.isError)
          return False; /* kernel bug if this happens? */
-      if (sres.val != nsegments[segR].start) {
+      if (sres.res != nsegments[segR].start) {
          /* kernel bug if this happens? */
-        (void)ML_(am_do_munmap_NO_NOTIFY)( sres.val, delta );
+        (void)ML_(am_do_munmap_NO_NOTIFY)( sres.res, delta );
         return False;
       }
 
@@ -2650,9 +2702,9 @@ Bool VG_(am_extend_into_adjacent_reservation_client) ( NSegment* seg,
              );
       if (sres.isError)
          return False; /* kernel bug if this happens? */
-      if (sres.val != nsegments[segA].start-delta) {
+      if (sres.res != nsegments[segA].start-delta) {
          /* kernel bug if this happens? */
-        (void)ML_(am_do_munmap_NO_NOTIFY)( sres.val, delta );
+        (void)ML_(am_do_munmap_NO_NOTIFY)( sres.res, delta );
         return False;
       }
 
@@ -2712,7 +2764,7 @@ Bool VG_(am_extend_map_client)( /*OUT*/Bool* need_discard,
       return False;
    } else {
       /* the area must not have moved */
-      aspacem_assert(sres.val == seg->start);
+      aspacem_assert(sres.res == seg->start);
    }
 
    *need_discard = any_Ts_in_range( seg_copy.end+1, delta );
@@ -2776,7 +2828,7 @@ Bool VG_(am_relocate_nooverlap_client)( /*OUT*/Bool* need_discard,
       AM_SANITY_CHECK;
       return False;
    } else {
-      aspacem_assert(sres.val == new_addr);
+      aspacem_assert(sres.res == new_addr);
    }
 
    *need_discard = any_Ts_in_range( old_addr, old_len )
@@ -2908,13 +2960,13 @@ static void read_procselfmaps_into_buf ( void )
 
    buf_n_tot = 0;
    do {
-      n_chunk = ML_(am_read)( fd.val, &procmap_buf[buf_n_tot],
+      n_chunk = ML_(am_read)( fd.res, &procmap_buf[buf_n_tot],
                               M_PROCMAP_BUF - buf_n_tot );
       if (n_chunk >= 0)
          buf_n_tot += n_chunk;
    } while ( n_chunk > 0 && buf_n_tot < M_PROCMAP_BUF );
 
-   ML_(am_close)(fd.val);
+   ML_(am_close)(fd.res);
 
    if (buf_n_tot >= M_PROCMAP_BUF-5)
       ML_(am_barf_toolow)("M_PROCMAP_BUF");