
/*--------------------------------------------------------------------*/
/*--- 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-2009 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.

   Neither the names of the U.S. Department of Energy nor the
   University of California nor the names of its contributors may be
   used to endorse or promote products derived from this software
   without prior written permission.
*/

#if defined(VGO_aix5)

/* *************************************************************
   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
      Off64T 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_MIN 4096  /* 16M fake stack */ /* default size */
#define N_FAKE_STACK_PAGES_MAX 32768 /* 128M fake stack */ /* max size? */


/* 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;
   Word n_fake_stack_pages;
   Word m1 = 1048576;

   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;
   }

   n_fake_stack_pages = N_FAKE_STACK_PAGES_MIN;
   if (VG_(clo_main_stacksize) > 0 
       && ((m1+VG_(clo_main_stacksize)) / VKI_PAGE_SIZE) > n_fake_stack_pages) {
      n_fake_stack_pages = (m1+VG_(clo_main_stacksize)) / VKI_PAGE_SIZE;
   }
   if (n_fake_stack_pages > N_FAKE_STACK_PAGES_MAX) {
      /* Allocation of the stack failed.  We have to stop. */
      VG_(debugLog)(
         0, "aspacem",
            "valgrind: "
            "I failed to allocate space for the application's stack.\n");
      VG_(debugLog)(
         0, "aspacem",
            "valgrind: "
            "This may be the result of a very large --max-stackframe=\n");
      VG_(debugLog)(
         0, "aspacem",
            "valgrind: "
            "setting.  Cannot continue.  Sorry.\n\n");
      ML_(am_exit)(0);
   }

   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.  On AIX5, we don't know what this is (in general)
   so just return NULL. */
HChar* VG_(am_get_filename)( NSegment const* seg )
{
   return NULL;
}

/* 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);
}

#endif // defined(VGO_aix5)

/*--------------------------------------------------------------------*/
/*--- end                                                          ---*/
/*--------------------------------------------------------------------*/
