|
From: <sv...@va...> - 2006-10-17 01:24:03
|
Author: sewardj
Date: 2006-10-17 02:23:57 +0100 (Tue, 17 Oct 2006)
New Revision: 6252
Log:
Merge r6105:
Refactor the address space manager, so there are two implementations
of it, plus a small common supporting library.
Added:
trunk/coregrind/m_aspacemgr/aspacemgr-aix5.c
Modified:
trunk/coregrind/m_aspacemgr/aspacemgr-common.c
trunk/coregrind/m_aspacemgr/aspacemgr-linux.c
Added: trunk/coregrind/m_aspacemgr/aspacemgr-aix5.c
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/coregrind/m_aspacemgr/aspacemgr-aix5.c =
(rev 0)
+++ trunk/coregrind/m_aspacemgr/aspacemgr-aix5.c 2006-10-17 01:23:57 UTC =
(rev 6252)
@@ -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
+ in...@op...
+
+ 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=3D3 and above on Linux.
+*/
+
+
+/*-----------------------------------------------------------------*/
+/*--- ---*/
+/*--- The Address Space Manager's state. ---*/
+/*--- ---*/
+/*-----------------------------------------------------------------*/
+
+/* Describes AIX5-specific segment kinds */
+typedef
+ enum {
+ ASkFree=3D1, // 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 <=3D 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 =3D 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 >=3D 3)) { \
+ Bool ok =3D sane_AixSegments(&asegs_pri); \
+ if (!ok) \
+ VG_(debugLog)(0,"aspace", "sanity check failed, " \
+ "who =3D %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) =3D 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 =3D AM_4K_PAGESZ-1;
+ return toBool( (w & m) =3D=3D 0 );
+}
+
+static UWord AM_4K_ROUNDUP ( UWord w )
+{
+ UWord m =3D AM_4K_PAGESZ-1;
+ return (w+m) & (~m);
+}
+
+static UWord AM_64K_ROUNDUP ( UWord w )
+{
+ UWord m =3D 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 =3D 0;
+ while (off < strtab_used) {
+ if (0 =3D=3D VG_(strcmp)(str, &strtab[off]))
+ return &strtab[off];
+ off +=3D VG_(strlen)(&strtab[off]) + 1;
+ }
+ /* not present? we'll have to copy it then. */
+ len =3D VG_(strlen)(str);
+ if (len + 1 + strtab_used > VG_N_ASTRTAB)
+ ML_(am_barf_toolow)("VG_N_ASTRTAB");
+ off =3D strtab_used;
+ for (; *str; str++)
+ strtab[strtab_used++] =3D *str;
+ strtab[strtab_used++] =3D 0;
+ aspacem_assert(strtab_used <=3D VG_N_ASTRTAB);
+ return &strtab[off];
+}
+
+
+static Bool is_in_strtab ( UChar* str )
+{
+ if (str < &strtab[0])=20
+ return False;
+ if (str >=3D &strtab[strtab_used])
+ return False;
+ if (str > &strtab[0] && str[-1] !=3D 0)
+ return False;
+ return True;
+}
+
+
+/*-----------------------------------------------------------------*/
+/*--- ---*/
+/*--- Low level AixSegment stuff. ---*/
+/*--- ---*/
+/*-----------------------------------------------------------------*/
+
+static void init_AixSegment ( AixSegment* s )
+{
+ s->kind =3D 0; /* invalid */
+ s->start =3D 0;
+ s->end =3D 0;
+ s->hasR =3D False;
+ s->hasW =3D False;
+ s->hasX =3D False;
+ s->sibling =3D 0;
+ s->isMainExe =3D False;
+ s->isCH =3D False;
+ s->fromP =3D False;
+ s->fname =3D NULL;
+ s->mname =3D NULL;
+ s->offset =3D 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=20
+void show_AixSegment ( Int logLevel, Int segNo, AixSegment* seg )
+{
+ HChar* segName =3D 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:=20
+ 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:=20
+ 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=3D=3DASkAnonC && 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",=20
+ segNo);
+ break;
+ }
+}
+
+
+static void init_AixSegments ( AixSegments* segs )
+{
+ segs->used =3D 1;
+ init_AixSegment( &segs->seg[0] );
+ segs->seg[0].kind =3D ASkFree;
+ segs->seg[0].start =3D Addr_MIN;
+ segs->seg[0].end =3D Addr_MAX;
+}
+
+
+static=20
+void show_AixSegments ( Int logLevel, HChar* who, AixSegments* segs )
+{
+ Int i;
+ VG_(debugLog)(logLevel, "aspacem", "<<< %s\n", who);
+ for (i =3D 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 !=3D 0 || seg->offset !=3D 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 !=3D 0)
+ return False;
+ if (seg->isCH || seg->fromP)
+ return False;
+ break;
+ case ASkMData:
+ if (seg->isMainExe || seg->sibling =3D=3D 0 || seg->offset !=3D=
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 !=3D NULL)
+ return False;
+ if (seg->isMainExe || seg->sibling !=3D 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 !=3D 0)
+ return False;
+ if (seg->offset !=3D 0)
+ return False;
+ if (seg->kind !=3D ASkAnonC && seg->isCH)
+ return False;
+ if ( (!(seg->kind =3D=3D ASkAnonV || seg->kind =3D=3D ASkAnonC)=
)
+ && seg->fromP)
+ return False;
+ break;
+ case ASkPreAlloc:
+ if (seg->fname || seg->mname)
+ return False;
+ if (seg->isMainExe || seg->sibling !=3D 0)
+ return False;
+ if (seg->offset !=3D 0)
+ return False;
+ if (seg->kind !=3D 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 =3D 0,
+ hi =3D segs->used-1;
+ aspacem_assert(lo <=3D 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 =3D (lo + hi) / 2;
+ a_mid_lo =3D segs->seg[mid].start;
+ a_mid_hi =3D segs->seg[mid].end;
+
+ if (a < a_mid_lo) { hi =3D mid-1; continue; }
+ if (a > a_mid_hi) { lo =3D mid+1; continue; }
+ aspacem_assert(a >=3D a_mid_lo && a <=3D a_mid_hi);
+ aspacem_assert(0 <=3D 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 !=3D Addr_MIN
+ || segs->seg[segs->used-1].end !=3D Addr_MAX) {
+ VG_(debugLog)(0, "aspacem", "sane_AixSegments: bad endpoints");
+ return False;
+ }
+
+ /* Check each segment, and check entire range is covered. */
+ for (i =3D 0; i < segs->used; i++) {
+ if (!sane_AixSegment( &segs->seg[i] )) {
+ VG_(debugLog)(0, "aspacem",=20
+ "sane_AixSegments: bad segment %d\n", i);
+ return False;
+ }
+ }
+ for (i =3D 1; i < segs->used; i++) {
+ if (segs->seg[i-1].end + 1 !=3D segs->seg[i].start) {
+ VG_(debugLog)(0, "aspacem",=20
+ "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 =3D=3D ASkMText || segs->seg[0].kind =3D=3D ASk=
MData) {
+ VG_(debugLog)(0, "aspacem",=20
+ "sane_AixSegments: ASkMText/ASkMData at address z=
ero\n");
+ return False;
+ }
+
+ for (i =3D 0; i < segs->used-1; i++) {
+
+ AixSegment *s1, *s2;
+
+ s1 =3D &segs->seg[i];
+
+ if (s1->kind =3D=3D ASkMData) {
+ s2 =3D &segs->seg[ find_asegment_idx(segs, s1->sibling) ];
+ if (s2->kind !=3D ASkMText
+ || find_asegment_idx(segs, s2->sibling) !=3D i) {
+ VG_(debugLog)(0, "aspacem", "sane_AixSegments: bad sibling "
+ "link(s) for ASkData\n");
+ return False;
+ }
+ }
+
+ if (s1->kind =3D=3D ASkMText && s1->sibling !=3D 0) {
+ s2 =3D &segs->seg[ find_asegment_idx(segs, s1->sibling) ];
+ if (s2->kind !=3D ASkMData
+ || find_asegment_idx(segs, s2->sibling) !=3D 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 !=3D s2->kind)=20
+ return False;
+
+ if (s1->end+1 !=3D s2->start)
+ return False;
+
+ switch (s1->kind) {
+
+ case ASkFree:
+ s1->end =3D s2->end;
+ return True;
+
+ case ASkAnonC:
+ case ASkAnonV:
+ if (s1->hasR =3D=3D s2->hasR && s1->hasW =3D=3D s2->hasW=20
+ && s1->hasX =3D=3D s2->hasX && s1->isCH =3D=3D s2->isCH
+ && s1->fromP =3D=3D s2->fromP) {
+ s1->end =3D s2->end;
+ return True;
+ }
+ break;
+
+ /* not really necessary, but .. */
+ case SkFileV:
+ if (s1->hasR =3D=3D s2->hasR
+ && s1->hasW =3D=3D s2->hasW && s1->hasX =3D=3D s2->hasX
+ && s1->fname =3D=3D s2->fname
+ && s2->offset =3D=3D s1->offset
+ + ((ULong)s2->start) - ((ULong)s1->start) ) {
+ s1->end =3D 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 =3D 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 >=3D 1);
+ if (segs->used =3D=3D 1)
+ return;
+
+ w =3D 0;
+ for (r =3D 1; r < segs->used; r++) {
+ if (maybe_merge_asegments(&segs->seg[w], &segs->seg[r])) {
+ /* nothing */
+ } else {
+ w++;
+ if (w !=3D r)=20
+ segs->seg[w] =3D segs->seg[r];
+ }
+ }
+ w++;
+ aspacem_assert(w > 0 && w <=3D segs->used);
+ segs->used =3D 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 >=3D 1);
+=20
+ i =3D find_asegment_idx(segs, a);
+ aspacem_assert(i >=3D 0 && i < segs->used);
+
+ if (segs->seg[i].start =3D=3D 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 >=3D VG_N_ASEGMENTS)
+ ML_(am_barf_toolow)("VG_N_ASEGMENTS");
+ for (j =3D segs->used-1; j > i; j--)
+ segs->seg[j+1] =3D segs->seg[j];
+ segs->used++;
+
+ segs->seg[i+1] =3D segs->seg[i];
+ segs->seg[i+1].start =3D a;
+ segs->seg[i].end =3D a-1;
+
+ if (segs->seg[i].kind =3D=3D ASkFileV /* || segs->seg[i].kind =3D=3D =
ASkFileC*/)
+ segs->seg[i+1].offset=20
+ +=3D ((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=20
+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 =3D find_asegment_idx(segs,sLo);
+ *iHi =3D find_asegment_idx(segs,sHi);
+ aspacem_assert(0 <=3D *iLo && *iLo < segs->used);
+ aspacem_assert(0 <=3D *iHi && *iHi < segs->used);
+ aspacem_assert(*iLo <=3D *iHi);
+ aspacem_assert(segs->seg[*iLo].start =3D=3D sLo);
+ aspacem_assert(segs->seg[*iHi].end =3D=3D 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 =3D seg->start;
+ Addr sEnd =3D seg->end;
+
+ aspacem_assert(sStart <=3D sEnd);
+
+ segment_is_sane =3D 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 =3D iHi - iLo;
+ aspacem_assert(delta >=3D 0);
+ if (delta > 0) {
+ for (i =3D iLo; i < segs->used-delta; i++)
+ segs->seg[i] =3D segs->seg[i+delta];
+ segs->used -=3D delta;
+ }
+ aspacem_assert(segs->used >=3D 1);
+
+ segs->seg[iLo] =3D *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 =3D 0; i < segs->used; i++) {
+ if (segs->seg[i].kind =3D=3D ASkFree
+ || segs->seg[i].kind =3D=3D ASkMText
+ || segs->seg[i].kind =3D=3D ASkMData)
+ continue;
+ s =3D segs->seg[i].start;
+ e =3D segs->seg[i].end;
+ init_AixSegment( &segs->seg[i] );
+ segs->seg[i].start =3D s;
+ segs->seg[i].end =3D e;
+ segs->seg[i].kind =3D 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 >=3D 1 && src->used < VG_N_ASEGMENTS);
+ dst->used =3D src->used;
+ for (i =3D 0; i < src->used; i++)
+ dst->seg[i] =3D 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=20
+void add_pri_text_and_data_segs ( AixSegment* tnew, AixSegment* dnew )
+{
+ Bool dExists =3D (dnew->end - dnew->start + 1) !=3D 0;
+ aspacem_assert(tnew->kind =3D=3D ASkMText);
+ aspacem_assert(dnew->kind =3D=3D ASkMData);
+ if (dExists) {
+ aspacem_assert(tnew->sibling =3D=3D dnew->start);
+ aspacem_assert(dnew->sibling =3D=3D tnew->start);
+ add_asegment(&asegs_pri, tnew);
+ add_asegment(&asegs_pri, dnew);
+ } else {
+ aspacem_assert(tnew->sibling =3D=3D 0);
+ add_asegment(&asegs_pri, tnew);
+ }
+}
+
+static=20
+void del_pri_text_and_data_segs ( AixSegment* told, AixSegment* dold )
+{
+ AixSegment fre;
+ Bool dExists =3D (dold->end - dold->start + 1) !=3D 0;
+ aspacem_assert(told->kind =3D=3D ASkMText);
+ aspacem_assert(dold->kind =3D=3D ASkMData);
+ init_AixSegment( &fre );
+ fre.kind =3D ASkFree;
+ if (dExists) {
+ aspacem_assert(told->sibling =3D=3D dold->start);
+ aspacem_assert(dold->sibling =3D=3D told->start);
+ fre.start =3D told->start;
+ fre.end =3D told->end;
+ add_asegment(&asegs_pri, &fre);
+ fre.start =3D dold->start;
+ fre.end =3D dold->end;
+ add_asegment(&asegs_pri, &fre);
+ } else {
+ aspacem_assert(told->sibling =3D=3D 0);
+ fre.start =3D told->start;
+ fre.end =3D 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 =3D 0;
+
+# define MODIFY_PRI(_dir, _asegs, _ixt, _acquire) \
+ do { \
+ Int _ixd; \
+ AixSegment *_segt, *_segd; \
+ AixSegment _segd_dummy; \
+ aspacem_assert(_ixt >=3D 0 && _ixt < _asegs.used); \
+ _segt =3D &_asegs.seg[_ixt]; \
+ aspacem_assert(_segt->kind =3D=3D ASkMText); \
+ if (_segt->sibling) { \
+ _ixd =3D find_asegment_idx( &_asegs, _segt->sibling ); \
+ _segd =3D &_asegs.seg[_ixd]; \
+ aspacem_assert(_segd->kind =3D=3D ASkMData); \
+ aspacem_assert(_segt->sibling =3D=3D _segd->start); \
+ } else { \
+ init_AixSegment( &_segd_dummy ); \
+ _segd_dummy.kind =3D ASkMData; \
+ _segd_dummy.start =3D 1; \
+ _segd_dummy.end =3D 0; \
+ _segd =3D &_segd_dummy; \
+ } \
+ if (_segd !=3D &_segd_dummy) \
+ aspacem_assert(_segd->sibling =3D=3D _segt->start); \
+ \
+ (_dir).code_start =3D (_segt)->start; \
+ (_dir).code_len =3D (_segt)->end - (_segt)->start + 1; \
+ (_dir).data_start =3D (_segd)->start; \
+ (_dir).data_len =3D (_segd)->end - (_segd)->start + 1; \
+ (_dir).file_name =3D (_segt)->fname; \
+ (_dir).mem_name =3D (_segt)->mname; \
+ (_dir).is_mainexe =3D (_acquire) ? (_segt)->isMainExe : False; =
\
+ (_dir).acquire =3D (_acquire); \
+ \
+ if (_acquire) { \
+ add_pri_text_and_data_segs( _segt, _segd ); \
+ } else { \
+ del_pri_text_and_data_segs( _segt, _segd ); \
+ } \
+ } while (0)
+
+ ixold =3D 0; /* indexes asegs_told */
+ ixnew =3D 0; /* indexes asegs_tnew */
+
+ while (True) {
+
+ aspacem_assert(ixold >=3D 0 && ixold < asegs_told.used);
+ aspacem_assert(ixnew >=3D 0 && ixnew < asegs_tnew.used);
+
+ /* Advance ixold and ixnew to the next MText in their
+ respective arrays. */
+ while (ixold < asegs_told.used=20
+ && asegs_told.seg[ixold].kind !=3D ASkMText) {
+ aspacem_assert(asegs_told.seg[ixold].kind =3D=3D ASkFree
+ || asegs_told.seg[ixold].kind =3D=3D ASkMData);
+ ixold++;
+ }
+ while (ixnew < asegs_tnew.used=20
+ && asegs_tnew.seg[ixnew].kind !=3D ASkMText) {
+ aspacem_assert(asegs_tnew.seg[ixnew].kind =3D=3D ASkFree
+ || asegs_tnew.seg[ixnew].kind =3D=3D ASkMData);
+ ixnew++;
+ }
+
+ aspacem_assert(ixold >=3D 0 && ixold <=3D asegs_told.used);
+ aspacem_assert(ixnew >=3D 0 && ixnew <=3D asegs_tnew.used);
+
+ done_old =3D ixold =3D=3D asegs_told.used;
+ done_new =3D ixnew =3D=3D 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 =3D &asegs_told.seg[ixold];
+ news =3D &asegs_tnew.seg[ixnew];
+
+ aspacem_assert(olds->kind =3D=3D ASkMText);
+ aspacem_assert(news->kind =3D=3D ASkMText);
+
+ if (0) {
+ show_AixSegment(0,ixold,&asegs_told.seg[ixold]);=20
+ show_AixSegment(0,ixnew,&asegs_tnew.seg[ixnew]);=20
+ 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 =3D=3Dolds->start then the module is
+ unchanged. Except, we should check a bit more carefully in
+ the zero case. */
+ if (olds->start =3D=3D news->start) {
+ if (olds->start =3D=3D news->start
+ && olds->end =3D=3D news->end
+ && olds->fname =3D=3D news->fname
+ && olds->mname =3D=3D news->mname
+ && olds->sibling =3D=3D news->sibling
+ && olds->isMainExe =3D=3D 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, Fals=
e);
+ (*ndirectives)++;
+ aspacem_assert(*ndirectives <=3D 2 * VG_N_ASEGMENTS);
+ MODIFY_PRI(directives[*ndirectives], asegs_tnew, ixnew, True=
);
+ (*ndirectives)++;
+ aspacem_assert(*ndirectives <=3D 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 <=3D 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 <=3D 2 * VG_N_ASEGMENTS);
+ ixnew++;
+ continue;
+ }
+ /* NOTREACHED */
+ aspacem_assert(0);
+ }
+
+ finishup_new:
+ olds =3D NULL;
+ aspacem_assert(ixold =3D=3D asegs_told.used);
+ aspacem_assert(ixnew < asegs_tnew.used);
+ while (ixnew < asegs_tnew.used) {
+ news =3D &asegs_tnew.seg[ixnew];
+ aspacem_assert(news->kind =3D=3D ASkMText || news->kind =3D=3D ASk=
MData
+ || news->kind =3D=3D ASkFree);
+ if (news->kind =3D=3D ASkMText) {
+ MODIFY_PRI(directives[*ndirectives], asegs_tnew, ixnew, True);
+ (*ndirectives)++;
+ aspacem_assert(*ndirectives <=3D 2 * VG_N_ASEGMENTS);
+ }
+ ixnew++;
+ }
+ goto both_done;
+
+ finishup_old:
+ news =3D NULL;
+ aspacem_assert(ixnew =3D=3D asegs_tnew.used);
+ aspacem_assert(ixold < asegs_told.used);
+ while (ixold < asegs_told.used) {
+ olds =3D &asegs_told.seg[ixold];
+ aspacem_assert(olds->kind =3D=3D ASkMText || olds->kind =3D=3D ASk=
MData
+ || olds->kind =3D=3D ASkFree);
+ if (olds->kind =3D=3D ASkMText) {
+ MODIFY_PRI(directives[*ndirectives], asegs_told, ixold, False);
+ (*ndirectives)++;
+ aspacem_assert(*ndirectives <=3D 2 * VG_N_ASEGMENTS);
+ }
+ ixold++;
+ }
+ goto both_done;
+
+ both_done:
+ aspacem_assert(ixold =3D=3D asegs_told.used);
+ aspacem_assert(ixnew =3D=3D asegs_tnew.used);
+
+ asegs_tnew.used =3D 0;
+ asegs_told.used =3D 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 =3D False;
+ AixSegment seg;
+
+ aspacem_assert(!done);
+ done =3D 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 =3D ASkAnonC;
+ seg.hasR =3D seg.hasW =3D seg.hasX =3D True;
+
+ if (sizeof(void*) =3D=3D 4
+ && ((sp & 0xFFFF0000) =3D=3D 0x2FF20000
+ || (sp & 0xFFFF0000) =3D=3D 0x2FF10000)) {
+ /* Gaaah. Special-case 32-bit mode. */
+ seg.end =3D 0x2FF2FFFF;
+ } else {
+ seg.end =3D AM_64K_ROUNDUP(sp) - 1;
+ }
+
+ seg.start =3D 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 =3D ASkAnonC;
+ seg.hasR =3D seg.hasX =3D True;
+ seg.start =3D MAGIC_PAGES_1_BASE;
+ seg.end =3D 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 =3D ASkAnonC;
+ seg.hasR =3D seg.hasX =3D True;
+ seg.start =3D MAGIC_PAGES_2_BASE;
+ seg.end =3D 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 <=3D 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 >=3D 0);
+
+ nSegs =3D 0;
+ for (i =3D 0; i < asegs_pri.used; i++) {
+ if (asegs_pri.seg[i].kind =3D=3D ASkFree
+ || asegs_pri.seg[i].kind =3D=3D 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 <=3D nStarts);
+
+ j =3D 0;
+ for (i =3D 0; i < asegs_pri.used; i++) {
+ if (asegs_pri.seg[i].kind =3D=3D ASkFree
+ || asegs_pri.seg[i].kind =3D=3D ASkPreAlloc)
+ continue;
+ starts[j++] =3D asegs_pri.seg[i].start;
+ }
+
+ aspacem_assert(j =3D=3D nSegs); /* this should not fail */
+ return nSegs;
+}
+
+
+/*-----------------------------------------------------------------*/
+/*--- ---*/
+/*--- Sanity checking and preening of the segment array. ---*/
+/*--- ---*/
+/*-----------------------------------------------------------------*/
+
+Bool VG_(am_do_sync_check) ( const HChar* fn,=20
+ 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 =3D 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 =3D SkAnonC;
+ bogus.start =3D 0;
+ bogus.end =3D 0;
+ bogus.smode =3D SmFixed;
+ bogus.dev =3D 0;
+ bogus.ino =3D 0;
+ bogus.mode =3D 0;
+ bogus.offset =3D 0;
+ bogus.fnIdx =3D -1;
+ bogus.hasR =3D bogus.hasW =3D bogus.hasX =3D False;
+ bogus.hasT =3D False;
+ bogus.isCH =3D False;
+ bogus.mark =3D False;
+
+ /* Go look for it in the segment table. */
+ i =3D find_asegment_idx( &asegs_pri, a );
+ aspacem_assert(i >=3D 0 && i <=3D asegs_pri.used);
+
+ aseg =3D &asegs_pri.seg[i];
+ if (aseg->kind =3D=3D ASkFree || aseg->kind =3D=3D ASkPreAlloc)
+ return NULL;
+
+ bogus.start =3D aseg->start;
+ bogus.end =3D aseg->end;
+
+ /* Refine */
+ switch (aseg->kind) {
+ case ASkMText:
+ bogus.kind =3D SkAnonC; /* hmm, pretty darn bogus */
+ bogus.hasR =3D bogus.hasX =3D True;
+ break;
+ case ASkMData:
+ bogus.kind =3D SkAnonC; /* hmm, pretty darn bogus */
+ bogus.hasR =3D bogus.hasW =3D True;
+ break;
+ case ASkShmemC:
+ bogus.kind =3D SkShmC;
+ bogus.hasR =3D aseg->hasR;
+ bogus.hasW =3D aseg->hasW;
+ bogus.hasX =3D aseg->hasX;
+ break;
+ case ASkAnonC:
+ bogus.kind =3D SkAnonC;
+ bogus.hasR =3D aseg->hasR;
+ bogus.hasW =3D aseg->hasW;
+ bogus.hasX =3D aseg->hasX;
+ bogus.isCH =3D aseg->isCH;
+ break;
+ case ASkAnonV:
+ bogus.kind =3D SkAnonV;
+ bogus.hasR =3D aseg->hasR;
+ bogus.hasW =3D aseg->hasW;
+ bogus.hasX =3D aseg->hasX;
+ break;
+ case ASkFileV:
+ bogus.kind =3D SkFileV;
+ bogus.hasR =3D aseg->hasR;
+ bogus.hasW =3D aseg->hasW;
+ bogus.hasX =3D aseg->hasX;
+ bogus.offset =3D 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 =3D 0;
+ for (i =3D 0; i < asegs_pri.used; i++) {
+ if (asegs_pri.seg[i].kind =3D=3D ASkAnonC=20
+ || asegs_pri.seg[i].kind =3D=3D ASkAnonV) {
+ total +=3D (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,=20
+ UInt prot )
+{
+ NSegment const * const fake =3D VG_(am_find_nsegment)(start);
+ if (!fake)
+ return False;
+ aspacem_assert(fake->start <=3D start);
+ aspacem_assert(start + len - 1 <=3D fake->end);
+ if (fake->kind =3D=3D SkAnonV || fake->kind =3D=3D 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) =3D=3D sizeof(void*));
+ aspacem_assert(sizeof(Addr) =3D=3D sizeof(void*));
+ aspacem_assert(sizeof(SizeT) =3D=3D sizeof(void*));
+ aspacem_assert(sizeof(SSizeT) =3D=3D sizeof(void*));
+
+ asegs_tnew.used =3D 0;
+ asegs_told.used =3D 0;
+
+ asegs_pri.used =3D 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;=20
+}
+
+
+/*-----------------------------------------------------------------*/
+/*--- ---*/
+/*--- Preallocation (acquiring space from sbrk). ---*/
+/*--- ---*/
+/*-----------------------------------------------------------------*/
+
+static
+SysRes local_do_sbrk_NO_NOTIFY( Word delta )
+{
+ SysRes res;
+ aspacem_assert(__NR_AIX5_sbrk !=3D __NR_AIX5_UNKNOWN);
+ res =3D 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) =3D=3D sizeof(Addr));
+ aspacem_assert(req_sz > 0);
+ aspacem_assert(AM_IS_4K_ALIGNED(req_sz));
+
+ best_sz =3D Addr_MAX;
+ best_ix =3D -1;
+
+ for (i =3D 0; i < asegs_pri.used; i++) {
+ AixSegment* s =3D &asegs_pri.seg[i];
+ if (s->kind !=3D ASkPreAlloc)
+ continue;
+ this_sz
+ =3D s->end + 1 - s->start;
+ aspacem_assert(this_sz > 0);
+ aspacem_assert(AM_IS_4K_ALIGNED(this_sz));
+ if (this_sz >=3D req_sz && this_sz < best_sz) {
+ best_sz =3D this_sz;
+ best_ix =3D 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 =3D 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 =3D "sbrk disallowed";
+ goto fail;
+ }
+
+ /* Get the current limit. */
+ sres =3D local_do_sbrk_NO_NOTIFY(0);
+ if (sres.isError) {
+ why =3D "initial sbrk failed";
+ goto fail;
+ }
+
+ /* Get it page aligned */
+ delta =3D AM_4K_ROUNDUP(sres.res) - sres.res;
+ aspacem_assert(delta >=3D 0 && delta < AM_4K_PAGESZ);
+ if (delta > 0) {
+ sres =3D local_do_sbrk_NO_NOTIFY(delta);
+ if (sres.isError) {
+ why =3D "aligning sbrk failed";
+ goto fail;
+ }
+ }
+
+ /* Now the brk is aligned. Try to acquire the block. */
+ sres =3D local_do_sbrk_NO_NOTIFY(0);
+ if (sres.isError)
+ return False;
+ start =3D sres.res;
+ aspacem_assert( AM_IS_4K_ALIGNED( start ));
+
+ sres =3D local_do_sbrk_NO_NOTIFY( req_sz );
+ if (sres.isError) {
+ why =3D "main sbrk failed";
+ goto fail;
+ }
+
+ /* If this fails, the kernel is acting strange. */
+ aspacem_assert( sres.res =3D=3D start );
+
+ init_AixSegment( &seg );
+ seg.start =3D start;
+ seg.end =3D start + req_sz - 1;
+ seg.kind =3D ASkPreAlloc;
+ seg.hasR =3D seg.hasW =3D seg.hasX =3D True; /* presumably */
+ add_asegment( &asegs_pri, &seg );
+
+ VG_(debugLog)(
+ 1, "aspacem", "new_prealloc: SUCCESS at 0x%llx size %lld\n",=20
+ (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",=20
+ req_sz);
+
+ aspacem_assert(sizeof(SizeT) =3D=3D sizeof(Addr));
+ aspacem_assert(req_sz > 0);
+ aspacem_assert(AM_IS_4K_ALIGNED(req_sz));
+
+ ix =3D find_prealloc_idx ( req_sz );
+ if (ix >=3D 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 =3D=3D -1);
+
+ req_szX =3D req_sz + AM_PREALLOC_EXTRA;
+ aspacem_assert(req_szX > 0);
+ aspacem_assert(AM_IS_4K_ALIGNED(req_szX));
+
+ alloc_ok =3D new_prealloc( req_szX );
+ if (!alloc_ok)
+ return -1; /* failed */
+
+ /* We should now be able to find it in the segment table. */
+ ix =3D find_prealloc_idx( req_sz );
+ aspacem_assert(ix >=3D 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,=20
+ Bool forClient,=20
+ /*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,=20
+ /*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 =3D=3D 0)
+ return False;
+
+ /* Discard is needed if any of the just-trashed range had T. */
+ needDiscard =3D True; /* conservative but safe */
+
+ init_AixSegment( &seg );
+ seg.kind =3D ASkAnonC; /* XXX bogus: could be a file */
+ seg.start =3D a;
+ seg.end =3D a + len - 1;
+ seg.hasR =3D toBool(prot & VKI_PROT_READ);
+ seg.hasW =3D toBool(prot & VKI_PROT_WRITE);
+ seg.hasX =3D toBool(prot & VKI_PROT_EXEC);
+
+ if (0)
+ VG_(debugLog)(0,"aspacem","notify mmap ( %p, %ld, %ld, %ld )\n",=20
+ (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 =3D ASkShmemC;
+ seg.start =3D a;
+ seg.end =3D seg.start + len - 1;
+ seg.hasR =3D (prot & VKI_PROT_READ) ? True : False;
+ seg.hasW =3D (prot & VKI_PROT_WRITE) ? True : False;
+ seg.hasX =3D (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 i...
[truncated message content] |