mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-07 04:38:00 +00:00
1331 lines
37 KiB
C
1331 lines
37 KiB
C
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- Memory-related stuff: segment initialisation and tracking, ---*/
|
|
/*--- stack operations ---*/
|
|
/*--- vg_memory.c ---*/
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
/*
|
|
This file is part of Valgrind, a dynamic binary instrumentation
|
|
framework.
|
|
|
|
Copyright (C) 2000-2005 Julian Seward
|
|
jseward@acm.org
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the
|
|
License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
02111-1307, USA.
|
|
|
|
The GNU General Public License is contained in the file COPYING.
|
|
*/
|
|
|
|
#include "core.h"
|
|
|
|
/* Define to debug the memory-leak-detector. */
|
|
/* #define VG_DEBUG_LEAKCHECK */
|
|
|
|
static const Bool mem_debug = False;
|
|
|
|
#define IS_PAGE_ALIGNED(_aa) (((_aa) & (VKI_PAGE_SIZE-1)) == 0)
|
|
|
|
|
|
/*--------------------------------------------------------------*/
|
|
/*--- A simple, self-contained ordered array of segments. ---*/
|
|
/*--------------------------------------------------------------*/
|
|
|
|
/* Max number of segments we can track. */
|
|
#define VG_N_SEGMENTS 1000
|
|
|
|
/* Max number of segment file names we can track. */
|
|
#define VG_N_SEGNAMES 200
|
|
|
|
/* Max length of a segment file name. */
|
|
#define VG_MAX_SEGNAMELEN 1000
|
|
|
|
|
|
/* ------ STATE for the address-space manager ------ */
|
|
|
|
/* Array [0 .. segments_used-1] of all mappings. */
|
|
/* Sorted by .addr field. */
|
|
/* I: len may not be zero. */
|
|
/* I: overlapping segments are not allowed. */
|
|
/* Each segment can optionally hold an index into the filename table. */
|
|
|
|
static Segment segments[VG_N_SEGMENTS];
|
|
static Int segments_used = 0;
|
|
|
|
typedef
|
|
struct {
|
|
Bool inUse;
|
|
Bool mark;
|
|
HChar fname[VG_MAX_SEGNAMELEN];
|
|
}
|
|
SegName;
|
|
|
|
/* Filename table. _used is the high water mark; an entry is only
|
|
valid if its index >= 0, < _used, and its .inUse field == True.
|
|
The .mark field is used to garbage-collect dead entries.
|
|
*/
|
|
static SegName segnames[VG_N_SEGNAMES];
|
|
static Int segnames_used = 0;
|
|
|
|
|
|
/* ------ end of STATE for the address-space manager ------ */
|
|
|
|
|
|
/* Searches the filename table to find an index for the given name.
|
|
If none is found, an index is allocated and the name stored. If no
|
|
space is available we just give up. If the string is too long to
|
|
store, return -1.
|
|
*/
|
|
static Int allocate_segname ( const HChar* name )
|
|
{
|
|
Int i, j, len;
|
|
|
|
vg_assert(name);
|
|
|
|
if (0) VG_(printf)("alloc_segname %s\n", name);
|
|
|
|
len = VG_(strlen)(name);
|
|
if (len >= VG_MAX_SEGNAMELEN-1) {
|
|
return -1;
|
|
}
|
|
|
|
/* first see if we already have the name. */
|
|
for (i = 0; i < segnames_used; i++) {
|
|
if (!segnames[i].inUse)
|
|
continue;
|
|
if (0 == VG_(strcmp)(name, &segnames[i].fname[0])) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
/* no we don't. So look for a free slot. */
|
|
for (i = 0; i < segnames_used; i++)
|
|
if (!segnames[i].inUse)
|
|
break;
|
|
|
|
if (i == segnames_used) {
|
|
/* no free slots .. advance the high-water mark. */
|
|
if (segnames_used+1 < VG_N_SEGNAMES) {
|
|
i = segnames_used;
|
|
segnames_used++;
|
|
} else {
|
|
VG_(printf)(
|
|
"coregrind/vg_memory.c:\n"
|
|
" VG_N_SEGNAMES is too small: "
|
|
"increase it and rebuild Valgrind.\n"
|
|
);
|
|
VG_(printf)(
|
|
"coregrind/vg_memory.c:\n"
|
|
" giving up now.\n\n"
|
|
);
|
|
VG_(exit)(0);
|
|
}
|
|
}
|
|
|
|
/* copy it in */
|
|
segnames[i].inUse = True;
|
|
for (j = 0; j < len; j++)
|
|
segnames[i].fname[j] = name[j];
|
|
vg_assert(len < VG_MAX_SEGNAMELEN);
|
|
segnames[i].fname[len] = 0;
|
|
return i;
|
|
}
|
|
|
|
|
|
/* Returns -1 if 'a' denotes an address prior to seg, 1 if it denotes
|
|
an address after it, and 0 if it denotes an address covered by
|
|
seg.
|
|
*/
|
|
static Int compare_addr_with_seg ( Addr a, Segment* seg )
|
|
{
|
|
if (a < seg->addr)
|
|
return -1;
|
|
if (a >= seg->addr + seg->len)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Find the (index of the) segment that contains 'a', or -1 if
|
|
none.
|
|
*/
|
|
static Int find_segment ( Addr a )
|
|
{
|
|
Int i;
|
|
for (i = 0; i < segments_used; i++) {
|
|
if (compare_addr_with_seg(a, &segments[i]) == 0)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
/* Assumes that 'a' is not in any segment. Finds the index of the
|
|
lowest-addressed segment above 'a', or -1 if none. Passing 'a'
|
|
which is in fact in a segment is a checked error.
|
|
*/
|
|
static Int find_segment_above_unmapped ( Addr a )
|
|
{
|
|
Int i, r;
|
|
for (i = 0; i < segments_used; i++) {
|
|
r = compare_addr_with_seg(a, &segments[i]);
|
|
vg_assert(r != 0); /* 'a' should not be in any segment. */
|
|
if (r == 1)
|
|
continue;
|
|
vg_assert(r == -1);
|
|
break;
|
|
}
|
|
|
|
if (i == segments_used)
|
|
return -1; /* not found */
|
|
else
|
|
return i;
|
|
}
|
|
|
|
|
|
/* Assumes that 'a' is in some segment. Finds the next segment along,
|
|
or NULL if none. Passing 'a' which is in fact not in a segment is
|
|
a checked error.
|
|
*/
|
|
static Int find_segment_above_mapped ( Addr a )
|
|
{
|
|
Int i, r;
|
|
for (i = 0; i < segments_used; i++) {
|
|
r = compare_addr_with_seg(a, &segments[i]);
|
|
if (r == 1)
|
|
continue; /* not yet there */
|
|
if (r == 0)
|
|
break; /* found it */
|
|
vg_assert(0);
|
|
/* we shouldn't get here -- r == -1 and so it means we went past
|
|
'a' without seeing it -- it is therefore unmapped. */
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
vg_assert(i < segments_used);
|
|
if (i == segments_used-1)
|
|
return -1; /* not found */
|
|
else
|
|
return i+1;
|
|
}
|
|
|
|
|
|
/* Shift segments[i .. segments_used-1] up by one. */
|
|
static void make_space_at ( Int i )
|
|
{
|
|
Int j;
|
|
vg_assert(i >= 0 && i <= segments_used);
|
|
vg_assert(segments_used >= 0);
|
|
if (segments_used+1 == VG_N_SEGMENTS) {
|
|
VG_(printf)(
|
|
"coregrind/vg_memory.c:\n"
|
|
" VG_N_SEGMENTS is too small: "
|
|
"increase it and rebuild Valgrind.\n"
|
|
);
|
|
VG_(printf)(
|
|
"coregrind/vg_memory.c:\n"
|
|
" giving up now.\n\n"
|
|
);
|
|
VG_(exit)(0);
|
|
}
|
|
vg_assert(segments_used+1 < VG_N_SEGMENTS);
|
|
for (j = segments_used; j > i; j--)
|
|
segments[j] = segments[j-1];
|
|
segments_used++;
|
|
}
|
|
|
|
|
|
/* Shift segments [i+1 .. segments_used-1] down by one, and decrement
|
|
segments_used.
|
|
*/
|
|
static void delete_segment_at ( Int i )
|
|
{
|
|
Int j;
|
|
vg_assert(i >= 0 && i < segments_used);
|
|
for (j = i+1; j < segments_used; j++)
|
|
segments[j-1] = segments[j];
|
|
segments_used--;
|
|
vg_assert(segments_used >= 0 && segments_used < VG_N_SEGMENTS);
|
|
}
|
|
|
|
|
|
/* Fill the i'th record all with zeroes. */
|
|
static void zeroise_segment ( Int i )
|
|
{
|
|
vg_assert(i >= 0 && i < segments_used);
|
|
segments[i].prot = 0;
|
|
segments[i].flags = 0;
|
|
segments[i].addr = 0;
|
|
segments[i].len = 0;
|
|
segments[i].offset = 0;
|
|
segments[i].filename = NULL;
|
|
segments[i].fnIdx = -1;
|
|
segments[i].dev = 0;
|
|
segments[i].ino = 0;
|
|
segments[i].symtab = NULL;
|
|
}
|
|
|
|
|
|
/* Create a segment to contain 'a', and return its index. Or -1 if
|
|
this failed because some other segment already contains 'a'. If
|
|
successful, fill in the segment's .addr field with 'a' but leave
|
|
all other fields alone.
|
|
*/
|
|
static Int create_segment ( Addr a )
|
|
{
|
|
Int i, r;
|
|
for (i = 0; i < segments_used; i++) {
|
|
r = compare_addr_with_seg( a, &segments[i] );
|
|
if (r == 1)
|
|
continue; /* seg[i] precedes a */
|
|
if (r == 0)
|
|
return -1; /* seg[i] contains a. Give up */
|
|
vg_assert(r == -1);
|
|
break;
|
|
}
|
|
/* a precedes seg[i]. Shift segs at i and above up one, and use
|
|
this slot. */
|
|
make_space_at(i);
|
|
zeroise_segment(i);
|
|
segments[i].addr = a;
|
|
return i;
|
|
}
|
|
|
|
|
|
/* Print out the segment array (debugging only!). Note, this calls
|
|
VG_(printf), and I'm not 100% clear that that wouldn't require
|
|
dynamic memory allocation and hence more segments to be allocated.
|
|
*/
|
|
static void show_segments ( HChar* who )
|
|
{
|
|
Int i;
|
|
VG_(printf)("<<< SHOW_SEGMENTS: %s (%d segments, %d segnames)\n",
|
|
who, segments_used, segnames_used);
|
|
for (i = 0; i < segnames_used; i++) {
|
|
if (!segnames[i].inUse)
|
|
continue;
|
|
VG_(printf)("(%2d) %s\n", i, segnames[i].fname);
|
|
}
|
|
for (i = 0; i < segments_used; i++) {
|
|
VG_(printf)(
|
|
"%3d: %08p-%08p %7llu pr=0x%x fl=0x%04x d=0x%03x i=%-7d o=%-7lld (%d)\n",
|
|
i,
|
|
segments[i].addr, segments[i].addr + segments[i].len,
|
|
(ULong)segments[i].len, segments[i].prot,
|
|
segments[i].flags, segments[i].dev, segments[i].ino,
|
|
(Long)segments[i].offset,
|
|
segments[i].fnIdx);
|
|
}
|
|
VG_(printf)(">>>\n");
|
|
}
|
|
|
|
|
|
/* Find the segment containing 'a' and split it into two pieces at
|
|
'a'. Does nothing if no segment contains 'a', or if the split
|
|
would cause either of the pieces to have zero size.
|
|
|
|
If 'a' is not found, or if no splitting happens, -1 is returned.
|
|
|
|
If a value 'r' other than -1 is returned, this is the index of the
|
|
higher-addressed segment resulting from the split, and the index of
|
|
the lower-addressed segment is r-1.
|
|
*/
|
|
static Int split_segment ( Addr a )
|
|
{
|
|
Int r;
|
|
HWord delta;
|
|
vg_assert(IS_PAGE_ALIGNED(a));
|
|
r = find_segment(a);
|
|
if (r == -1)
|
|
/* not found */
|
|
return -1;
|
|
if (segments[r].addr == a)
|
|
/* segment starts at 'a', so splitting it would create a
|
|
zero-sized segment */
|
|
return -1;
|
|
|
|
/* copy original; make adjustments. */
|
|
vg_assert(a > segments[r].addr);
|
|
delta = a - segments[r].addr;
|
|
make_space_at(r);
|
|
segments[r] = segments[r+1];
|
|
segments[r].len = delta;
|
|
segments[r+1].len -= delta;
|
|
segments[r+1].addr += delta;
|
|
segments[r+1].offset += delta;
|
|
return r+1;
|
|
}
|
|
|
|
|
|
/* Return true if two segments are adjacent and mergable (s1 is
|
|
assumed to have a lower ->addr than s2) */
|
|
static inline Bool segments_are_mergeable(Segment *s1, Segment *s2)
|
|
{
|
|
if (s1->addr+s1->len != s2->addr)
|
|
return False;
|
|
|
|
if (s1->flags != s2->flags)
|
|
return False;
|
|
|
|
if (s1->prot != s2->prot)
|
|
return False;
|
|
|
|
if (s1->symtab != s2->symtab)
|
|
return False;
|
|
|
|
if (s1->flags & SF_FILE){
|
|
if ((s1->offset + s1->len) != s2->offset)
|
|
return False;
|
|
if (s1->dev != s2->dev)
|
|
return False;
|
|
if (s1->ino != s2->ino)
|
|
return False;
|
|
if (s1->fnIdx != s2->fnIdx)
|
|
return False;
|
|
}
|
|
|
|
return True;
|
|
}
|
|
|
|
|
|
/* Clean up and sanity check the segment array:
|
|
- check segments are in ascending order
|
|
- check segments do not overlap
|
|
- check no segment has zero size
|
|
- merge adjacent where possible
|
|
- perform checks on the filename table, and reclaim dead entries
|
|
*/
|
|
static void preen_segments ( void )
|
|
{
|
|
Int i, j, rd, wr;
|
|
Segment *s, *s1;
|
|
vg_assert(segments_used >= 0 && segments_used < VG_N_SEGMENTS);
|
|
vg_assert(segnames_used >= 0 && segnames_used < VG_N_SEGNAMES);
|
|
|
|
if (0) show_segments("before preen");
|
|
|
|
/* clear string table mark bits */
|
|
for (i = 0; i < segnames_used; i++)
|
|
segnames[i].mark = False;
|
|
|
|
/* check for non-zero size, and set mark bits for any used strings */
|
|
for (i = 0; i < segments_used; i++) {
|
|
vg_assert(segments[i].len > 0);
|
|
j = segments[i].fnIdx;
|
|
vg_assert(j >= -1 && j < segnames_used);
|
|
if (j >= 0) {
|
|
vg_assert(segnames[j].inUse);
|
|
segnames[j].mark = True;
|
|
}
|
|
}
|
|
|
|
/* check ascendingness and non-overlap */
|
|
for (i = 0; i < segments_used-1; i++) {
|
|
s = &segments[i];
|
|
s1 = &segments[i+1];
|
|
vg_assert(s->addr < s1->addr);
|
|
vg_assert(s->addr + s->len <= s1->addr);
|
|
}
|
|
|
|
/* merge */
|
|
if (segments_used < 1)
|
|
return;
|
|
|
|
wr = 1;
|
|
for (rd = 1; rd < segments_used; rd++) {
|
|
s = &segments[wr-1];
|
|
s1 = &segments[rd];
|
|
if (segments_are_mergeable(s,s1)) {
|
|
if (0)
|
|
VG_(printf)("merge %p-%p with %p-%p\n",
|
|
s->addr, s->addr+s->len,
|
|
s1->addr, s1->addr+s1->len);
|
|
s->len += s1->len;
|
|
continue;
|
|
}
|
|
if (wr < rd)
|
|
segments[wr] = segments[rd];
|
|
wr++;
|
|
}
|
|
vg_assert(wr >= 0 && wr <= segments_used);
|
|
segments_used = wr;
|
|
|
|
/* Free up any strings which are no longer referenced. */
|
|
for (i = 0; i < segnames_used; i++) {
|
|
if (segnames[i].mark == False) {
|
|
segnames[i].inUse = False;
|
|
segnames[i].fname[0] = 0;
|
|
}
|
|
}
|
|
|
|
if (0) show_segments("after preen");
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------*/
|
|
/*--- Maintain an ordered list of all the client's mappings ---*/
|
|
/*--------------------------------------------------------------*/
|
|
|
|
Bool VG_(seg_contains)(const Segment *s, Addr p, SizeT len)
|
|
{
|
|
Addr se = s->addr+s->len;
|
|
Addr pe = p+len;
|
|
vg_assert(pe >= p);
|
|
|
|
return (p >= s->addr && pe <= se);
|
|
}
|
|
|
|
Bool VG_(seg_overlaps)(const Segment *s, Addr p, SizeT len)
|
|
{
|
|
Addr se = s->addr+s->len;
|
|
Addr pe = p+len;
|
|
vg_assert(pe >= p);
|
|
|
|
return (p < se && pe > s->addr);
|
|
}
|
|
|
|
#if 0
|
|
/* 20050228: apparently unused */
|
|
/* Prepare a Segment structure for recycling by freeing everything
|
|
hanging off it. */
|
|
static void recycleseg(Segment *s)
|
|
{
|
|
if (s->flags & SF_CODE)
|
|
VG_(discard_translations)(s->addr, s->len);
|
|
|
|
if (s->filename != NULL)
|
|
VG_(arena_free)(VG_AR_CORE, (Char *)s->filename);
|
|
|
|
/* keep the SegInfo, if any - it probably still applies */
|
|
}
|
|
|
|
/* When freeing a Segment, also clean up every one else's ideas of
|
|
what was going on in that range of memory */
|
|
static void freeseg(Segment *s)
|
|
{
|
|
recycleseg(s);
|
|
if (s->symtab != NULL) {
|
|
VG_(symtab_decref)(s->symtab, s->addr);
|
|
s->symtab = NULL;
|
|
}
|
|
|
|
VG_(SkipNode_Free)(&sk_segments, s);
|
|
}
|
|
#endif
|
|
|
|
|
|
/* Get rid of any translations arising from s. */
|
|
/* Note, this is not really the job of the low level memory manager.
|
|
When it comes time to rewrite this subsystem, clean this up. */
|
|
static void dump_translations_from ( Segment* s )
|
|
{
|
|
if (s->flags & SF_CODE) {
|
|
VG_(discard_translations)(s->addr, s->len);
|
|
if (0)
|
|
VG_(printf)("dumping translations in %p .. %p\n",
|
|
s->addr, s->addr+s->len);
|
|
}
|
|
}
|
|
|
|
|
|
/* This unmaps all the segments in the range [addr, addr+len); any
|
|
partial mappings at the ends are truncated. */
|
|
void VG_(unmap_range)(Addr addr, SizeT len)
|
|
{
|
|
static const Bool debug = False || mem_debug;
|
|
Segment* s;
|
|
Addr end, s_end;
|
|
Int i;
|
|
Bool deleted;
|
|
|
|
if (len == 0)
|
|
return;
|
|
|
|
len = PGROUNDUP(len);
|
|
|
|
if (debug)
|
|
VG_(printf)("unmap_range(%p, %d)\n", addr, len);
|
|
if (0) show_segments("unmap_range(BEFORE)");
|
|
end = addr+len;
|
|
|
|
/* Everything must be page-aligned */
|
|
vg_assert(IS_PAGE_ALIGNED(addr));
|
|
vg_assert(IS_PAGE_ALIGNED(len));
|
|
|
|
for (i = 0; i < segments_used; i++) {
|
|
|
|
/* do not delete .. even though it looks stupid */
|
|
vg_assert(i >= 0);
|
|
|
|
deleted = False;
|
|
s = &segments[i];
|
|
s_end = s->addr + s->len;
|
|
|
|
if (0 && debug)
|
|
VG_(printf)("unmap: addr=%p-%p s=%p ->addr=%p-%p len=%d\n",
|
|
addr, end, s, s->addr, s_end, s->len);
|
|
|
|
if (!VG_(seg_overlaps)(s, addr, len)) {
|
|
if (0 && debug)
|
|
VG_(printf)(" (no overlap)\n");
|
|
continue;
|
|
}
|
|
|
|
/* 4 cases: */
|
|
if (addr > s->addr &&
|
|
addr < s_end &&
|
|
end >= s_end) {
|
|
/* this segment's tail is truncated by [addr, addr+len)
|
|
-> truncate tail
|
|
*/
|
|
dump_translations_from(s);
|
|
s->len = addr - s->addr;
|
|
|
|
if (debug)
|
|
VG_(printf)(" case 1: s->len=%d\n", s->len);
|
|
} else if (addr <= s->addr && end > s->addr && end < s_end) {
|
|
/* this segment's head is truncated by [addr, addr+len)
|
|
-> truncate head
|
|
*/
|
|
Word delta = end - s->addr;
|
|
|
|
if (debug)
|
|
VG_(printf)(" case 2: s->addr=%p s->len=%d delta=%d\n",
|
|
s->addr, s->len, delta);
|
|
|
|
dump_translations_from(s);
|
|
s->addr += delta;
|
|
s->offset += delta;
|
|
s->len -= delta;
|
|
|
|
vg_assert(s->len != 0);
|
|
} else if (addr <= s->addr && end >= s_end) {
|
|
/* this segment is completely contained within [addr, addr+len)
|
|
-> delete segment
|
|
*/
|
|
dump_translations_from(s);
|
|
delete_segment_at(i);
|
|
deleted = True;
|
|
|
|
if (debug)
|
|
VG_(printf)(" case 3: seg %d deleted\n", i);
|
|
} else if (addr > s->addr && end < s_end) {
|
|
/* [addr, addr+len) is contained within a single segment
|
|
-> split segment into 3, delete middle portion
|
|
*/
|
|
Int i_middle;
|
|
dump_translations_from(s);
|
|
i_middle = split_segment(addr);
|
|
vg_assert(i_middle != -1);
|
|
(void)split_segment(addr+len);
|
|
vg_assert(segments[i_middle].addr == addr);
|
|
delete_segment_at(i_middle);
|
|
deleted = True;
|
|
|
|
if (debug)
|
|
VG_(printf)(" case 4: subrange %p-%p deleted\n",
|
|
addr, addr+len);
|
|
}
|
|
|
|
/* If we deleted this segment (or any above), those above will
|
|
have been moved down to fill in the hole in the segment
|
|
array. In order that we don't miss them, we have to
|
|
re-consider this slot number; hence the i--. */
|
|
if (deleted)
|
|
i--;
|
|
}
|
|
preen_segments();
|
|
if (0) show_segments("unmap_range(AFTER)");
|
|
}
|
|
|
|
|
|
/* Add a binding of [addr,addr+len) to
|
|
(prot,flags,dev,ino,off,filename) in the segment array.
|
|
Delete/truncate any previous mapping(s) covering that range.
|
|
*/
|
|
void
|
|
VG_(map_file_segment)( Addr addr, SizeT len,
|
|
UInt prot, UInt flags,
|
|
UInt dev, UInt ino, ULong off,
|
|
const Char *filename)
|
|
{
|
|
static const Bool debug = False || mem_debug;
|
|
Segment* s;
|
|
Int idx;
|
|
|
|
if (debug)
|
|
VG_(printf)(
|
|
"\n"
|
|
"map_file_segment(addr=%p len=%llu prot=0x%x flags=0x%x\n"
|
|
" dev=0x%4x ino=%d off=%ld\n"
|
|
" filename='%s')\n",
|
|
addr, (ULong)len, prot, flags, dev, ino, off, filename);
|
|
|
|
/* Everything must be page-aligned */
|
|
vg_assert(IS_PAGE_ALIGNED(addr));
|
|
len = PGROUNDUP(len);
|
|
|
|
/* Nuke/truncate any existing segment(s) covering [addr,addr+len) */
|
|
VG_(unmap_range)(addr, len);
|
|
|
|
/* and now install this one */
|
|
idx = create_segment(addr);
|
|
vg_assert(segments_used >= 0 && segments_used <= VG_N_SEGMENTS);
|
|
vg_assert(idx != -1);
|
|
vg_assert(idx >= 0 && idx < segments_used);
|
|
|
|
s = &segments[idx];
|
|
vg_assert(s->addr == addr);
|
|
s->prot = prot;
|
|
s->flags = flags;
|
|
s->len = len;
|
|
s->offset = off;
|
|
s->fnIdx = filename==NULL ? -1 : allocate_segname(filename);
|
|
s->filename = s->fnIdx==-1 ? NULL : &segnames[s->fnIdx].fname[0];
|
|
s->dev = dev;
|
|
s->ino = ino;
|
|
s->symtab = NULL;
|
|
|
|
/* Clean up right now */
|
|
preen_segments();
|
|
if (0) show_segments("after map_file_segment");
|
|
|
|
/* If this mapping is of the beginning of a file, isn't part of
|
|
Valgrind, is at least readable and seems to contain an object
|
|
file, then try reading symbols from it.
|
|
*/
|
|
if (s->symtab == NULL
|
|
&& (addr+len < VG_(valgrind_base) || addr > VG_(valgrind_last))
|
|
&& (flags & (SF_MMAP|SF_NOSYMS)) == SF_MMAP) {
|
|
if (off == 0
|
|
&& s->fnIdx != -1
|
|
&& (prot & (VKI_PROT_READ|VKI_PROT_EXEC)) == (VKI_PROT_READ|VKI_PROT_EXEC)
|
|
&& len >= VKI_PAGE_SIZE
|
|
&& VG_(is_object_file)((void *)addr)) {
|
|
s->symtab = VG_(read_seg_symbols)(s);
|
|
if (s->symtab != NULL) {
|
|
s->flags |= SF_DYNLIB;
|
|
}
|
|
} else if (flags & SF_MMAP) {
|
|
#if 0
|
|
const SegInfo *info;
|
|
|
|
/* Otherwise see if an existing symtab applies to this Segment */
|
|
for(info = VG_(next_seginfo)(NULL);
|
|
info != NULL;
|
|
info = VG_(next_seginfo)(info)) {
|
|
if (VG_(seg_overlaps)(s, VG_(seg_start)(info), VG_(seg_size)(info)))
|
|
{
|
|
s->symtab = (SegInfo *)info;
|
|
VG_(symtab_incref)((SegInfo *)info);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/* clean up */
|
|
preen_segments();
|
|
}
|
|
|
|
void VG_(map_fd_segment)(Addr addr, SizeT len, UInt prot, UInt flags,
|
|
Int fd, ULong off, const Char *filename)
|
|
{
|
|
struct vki_stat st;
|
|
Char *name = NULL;
|
|
|
|
st.st_dev = 0;
|
|
st.st_ino = 0;
|
|
|
|
if (fd != -1 && (flags & SF_FILE)) {
|
|
vg_assert((off & (VKI_PAGE_SIZE-1)) == 0);
|
|
|
|
if (VG_(fstat)(fd, &st) < 0)
|
|
flags &= ~SF_FILE;
|
|
}
|
|
|
|
if ((flags & SF_FILE) && filename == NULL && fd != -1)
|
|
name = VG_(resolve_filename_nodup)(fd);
|
|
|
|
if (filename == NULL)
|
|
filename = name;
|
|
|
|
VG_(map_file_segment)(addr, len, prot, flags,
|
|
st.st_dev, st.st_ino, off, filename);
|
|
}
|
|
|
|
void VG_(map_segment)(Addr addr, SizeT len, UInt prot, UInt flags)
|
|
{
|
|
flags &= ~SF_FILE;
|
|
|
|
VG_(map_file_segment)(addr, len, prot, flags, 0, 0, 0, 0);
|
|
}
|
|
|
|
/* set new protection flags on an address range */
|
|
void VG_(mprotect_range)(Addr a, SizeT len, UInt prot)
|
|
{
|
|
Int r;
|
|
static const Bool debug = False || mem_debug;
|
|
|
|
if (debug)
|
|
VG_(printf)("\nmprotect_range(%p, %d, %x)\n", a, len, prot);
|
|
|
|
if (0) show_segments( "mprotect_range(before)" );
|
|
|
|
/* Everything must be page-aligned */
|
|
vg_assert(IS_PAGE_ALIGNED(a));
|
|
len = PGROUNDUP(len);
|
|
|
|
split_segment(a);
|
|
split_segment(a+len);
|
|
|
|
r = find_segment(a);
|
|
vg_assert(r != -1);
|
|
segments[r].prot = prot;
|
|
|
|
preen_segments();
|
|
|
|
if (0) show_segments( "mprotect_range(after)");
|
|
}
|
|
|
|
|
|
/* Try to find a map space for [addr,addr+len). If addr==0, it means
|
|
the caller is prepared to accept a space at any location; if not,
|
|
we will try for addr, but fail if we can't get it. This mimics
|
|
mmap fixed vs mmap not-fixed.
|
|
*/
|
|
Addr VG_(find_map_space)(Addr addr, SizeT len, Bool for_client)
|
|
{
|
|
static const Bool debug = False || mem_debug;
|
|
Addr ret;
|
|
Addr addrOrig = addr;
|
|
Addr limit = (for_client ? VG_(client_end)-1 : VG_(valgrind_last));
|
|
Addr base = (for_client ? VG_(client_mapbase) : VG_(valgrind_base));
|
|
Addr hole_start, hole_end, hstart_any, hstart_fixed, hstart_final;
|
|
Int i, i_any, i_fixed, i_final;
|
|
SizeT hole_len;
|
|
|
|
Bool fixed;
|
|
|
|
if (debug) {
|
|
VG_(printf)("\n\n");
|
|
VG_(printf)("find_map_space(%p, %d, %d) ...\n",
|
|
addr, len, for_client);
|
|
}
|
|
|
|
if (0) show_segments("find_map_space: start");
|
|
|
|
if (addr == 0) {
|
|
fixed = False;
|
|
} else {
|
|
fixed = True;
|
|
/* leave space for redzone and still try to get the exact
|
|
address asked for */
|
|
addr -= VKI_PAGE_SIZE;
|
|
}
|
|
|
|
/* Everything must be page-aligned */
|
|
vg_assert((addr & (VKI_PAGE_SIZE-1)) == 0);
|
|
len = PGROUNDUP(len);
|
|
|
|
len += VKI_PAGE_SIZE * 2; /* leave redzone gaps before and after mapping */
|
|
|
|
/* Scan the segment list, looking for a hole which satisfies the
|
|
requirements. At each point i we ask the question "can we use
|
|
the hole in between segments[i-1] and segments[i] ?" */
|
|
i_any = i_fixed = -1;
|
|
hstart_any = hstart_fixed = 0;
|
|
|
|
hole_start = hole_end = 0;
|
|
|
|
/* Iterate over all possible holes, generating them into
|
|
hole_start/hole_end. Filter out invalid ones. Then see if any
|
|
are usable; if so set i_fixed/i_any and hstart_fixed/hstart_any.
|
|
*/
|
|
for (i = 0; i <=/*yes,really*/ segments_used; i++) {
|
|
if (i == 0) {
|
|
hole_start = 0;
|
|
hole_end = segments[0].addr-1;
|
|
}
|
|
else {
|
|
vg_assert(segments_used > 0);
|
|
if (i == segments_used) {
|
|
hole_start = segments[i-1].addr + segments[i-1].len;
|
|
hole_end = ~(Addr)0;
|
|
} else {
|
|
hole_start = segments[i-1].addr + segments[i-1].len;
|
|
hole_end = segments[i].addr - 1;
|
|
}
|
|
}
|
|
|
|
vg_assert(hole_start <= hole_end || hole_start == hole_end+1);
|
|
|
|
/* ignore zero-sized holes */
|
|
if (hole_start == hole_end+1)
|
|
continue;
|
|
|
|
vg_assert(IS_PAGE_ALIGNED(hole_start));
|
|
vg_assert(IS_PAGE_ALIGNED(hole_end+1));
|
|
|
|
/* ignore holes which fall outside the allowable area */
|
|
if (!(hole_start >= base && hole_end <= limit))
|
|
continue;
|
|
|
|
vg_assert(hole_end > hole_start);
|
|
hole_len = hole_end - hole_start + 1;
|
|
vg_assert(IS_PAGE_ALIGNED(hole_len));
|
|
|
|
if (hole_len >= len && i_any == -1) {
|
|
/* It will at least fit in this hole. */
|
|
i_any = i;
|
|
hstart_any = hole_start;
|
|
}
|
|
|
|
if (fixed && hole_start <= addr
|
|
&& hole_start+hole_len >= addr+len) {
|
|
/* We were asked for a fixed mapping, and this hole works.
|
|
Bag it -- and stop searching as further searching is
|
|
pointless. */
|
|
i_fixed = i;
|
|
hstart_fixed = addr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Summarise the final decision into i_final/hstart_final. */
|
|
i_final = -1;
|
|
hstart_final = 0;
|
|
|
|
if (fixed) {
|
|
i_final = i_fixed;
|
|
hstart_final = hstart_fixed + VKI_PAGE_SIZE; /* skip leading redzone */
|
|
} else {
|
|
i_final = i_any;
|
|
hstart_final = hstart_any;
|
|
}
|
|
|
|
|
|
if (i_final != -1)
|
|
ret = hstart_final;
|
|
else
|
|
ret = 0; /* not found */
|
|
|
|
if (debug)
|
|
VG_(printf)("find_map_space(%p, %d, %d) -> %p\n\n",
|
|
addr, len, for_client, ret);
|
|
|
|
if (fixed) {
|
|
vg_assert(ret == 0 || ret == addrOrig);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Pad the entire process address space, from "start"
|
|
to VG_(valgrind_last) by creating an anonymous and inaccessible
|
|
mapping over any part of the address space which is not covered
|
|
by an entry in the segment list.
|
|
|
|
This is designed for use around system calls which allocate
|
|
memory in the process address space without providing a way to
|
|
control its location such as io_setup. By choosing a suitable
|
|
address with VG_(find_map_space) and then adding a segment for
|
|
it and padding the address space valgrind can ensure that the
|
|
kernel has no choice but to put the memory where we want it. */
|
|
void VG_(pad_address_space)(Addr start)
|
|
{
|
|
Addr addr = (start == 0) ? VG_(client_base) : start;
|
|
Addr ret;
|
|
|
|
Int i = 0;
|
|
Segment* s = i >= segments_used ? NULL : &segments[i];
|
|
|
|
while (s && addr <= VG_(valgrind_last)) {
|
|
if (addr < s->addr) {
|
|
VGP_DO_MMAP(ret, addr, s->addr - addr, 0,
|
|
VKI_MAP_FIXED | VKI_MAP_PRIVATE | VKI_MAP_ANONYMOUS,
|
|
-1, 0);
|
|
}
|
|
addr = s->addr + s->len;
|
|
i++;
|
|
s = i >= segments_used ? NULL : &segments[i];
|
|
}
|
|
|
|
if (addr <= VG_(valgrind_last)) {
|
|
VGP_DO_MMAP(ret, addr, VG_(valgrind_last) - addr + 1, 0,
|
|
VKI_MAP_FIXED | VKI_MAP_PRIVATE | VKI_MAP_ANONYMOUS,
|
|
-1, 0);
|
|
}
|
|
}
|
|
|
|
/* Remove the address space padding added by VG_(pad_address_space)
|
|
by removing any mappings that it created. */
|
|
void VG_(unpad_address_space)(Addr start)
|
|
{
|
|
Addr addr = (start == 0) ? VG_(client_base) : start;
|
|
Int ret;
|
|
|
|
Int i = 0;
|
|
Segment* s = i >= segments_used ? NULL : &segments[i];
|
|
|
|
while (s && addr <= VG_(valgrind_last)) {
|
|
if (addr < s->addr) {
|
|
ret = VG_(do_syscall2)(__NR_munmap, addr, s->addr - addr);
|
|
}
|
|
addr = s->addr + s->len;
|
|
i++;
|
|
s = i >= segments_used ? NULL : &segments[i];
|
|
}
|
|
|
|
if (addr <= VG_(valgrind_last)) {
|
|
ret = VG_(do_syscall2)(__NR_munmap, addr,
|
|
(VG_(valgrind_last) - addr) + 1);
|
|
}
|
|
}
|
|
|
|
/* Find the segment holding 'a', or NULL if none. */
|
|
Segment *VG_(find_segment)(Addr a)
|
|
{
|
|
Int r = find_segment(a);
|
|
if (0) show_segments("find_segment");
|
|
if (r == -1) return NULL;
|
|
return &segments[r];
|
|
}
|
|
|
|
/* Assumes that 'a' is not in any segment. Finds the lowest-addressed
|
|
segment above 'a', or NULL if none. Passing 'a' which is in fact in
|
|
a segment is a checked error.
|
|
*/
|
|
Segment *VG_(find_segment_above_unmapped)(Addr a)
|
|
{
|
|
Int r = find_segment_above_unmapped(a);
|
|
if (0) show_segments("find_segment_above_unmapped");
|
|
if (r == -1) return NULL;
|
|
return &segments[r];
|
|
}
|
|
|
|
/* Assumes that 'a' is in some segment. Finds the next segment along,
|
|
or NULL if none. Passing 'a' which is in fact not in a segment is
|
|
a checked error.
|
|
*/
|
|
Segment *VG_(find_segment_above_mapped)(Addr a)
|
|
{
|
|
Int r = find_segment_above_mapped(a);
|
|
if (0) show_segments("find_segment_above_mapped");
|
|
if (r == -1) return NULL;
|
|
return &segments[r];
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- Tracking permissions around %esp changes. ---*/
|
|
/*------------------------------------------------------------*/
|
|
|
|
/*
|
|
The stack
|
|
~~~~~~~~~
|
|
The stack's segment seems to be dynamically extended downwards
|
|
by the kernel as the stack pointer moves down. Initially, a
|
|
1-page (4k) stack is allocated. When %esp moves below that for
|
|
the first time, presumably a page fault occurs. The kernel
|
|
detects that the faulting address is in the range from %esp upwards
|
|
to the current valid stack. It then extends the stack segment
|
|
downwards for enough to cover the faulting address, and resumes
|
|
the process (invisibly). The process is unaware of any of this.
|
|
|
|
That means that Valgrind can't spot when the stack segment is
|
|
being extended. Fortunately, we want to precisely and continuously
|
|
update stack permissions around %esp, so we need to spot all
|
|
writes to %esp anyway.
|
|
|
|
The deal is: when %esp is assigned a lower value, the stack is
|
|
being extended. Create a secondary maps to fill in any holes
|
|
between the old stack ptr and this one, if necessary. Then
|
|
mark all bytes in the area just "uncovered" by this %esp change
|
|
as write-only.
|
|
|
|
When %esp goes back up, mark the area receded over as unreadable
|
|
and unwritable.
|
|
|
|
Just to record the %esp boundary conditions somewhere convenient:
|
|
%esp always points to the lowest live byte in the stack. All
|
|
addresses below %esp are not live; those at and above it are.
|
|
*/
|
|
|
|
/* Kludgey ... how much does %esp have to change before we reckon that
|
|
the application is switching stacks ? */
|
|
#define VG_PLAUSIBLE_STACK_SIZE 8000000
|
|
#define VG_HUGE_DELTA (VG_PLAUSIBLE_STACK_SIZE / 4)
|
|
|
|
/* This function gets called if new_mem_stack and/or die_mem_stack are
|
|
tracked by the tool, and one of the specialised cases (eg. new_mem_stack_4)
|
|
isn't used in preference */
|
|
REGPARM(2)
|
|
void VG_(unknown_SP_update)( Addr old_SP, Addr new_SP )
|
|
{
|
|
Word delta = (Word)new_SP - (Word)old_SP;
|
|
|
|
if (delta < -(VG_HUGE_DELTA) || VG_HUGE_DELTA < delta) {
|
|
/* %esp has changed by more than HUGE_DELTA. We take this to mean
|
|
that the application is switching to a new stack, for whatever
|
|
reason.
|
|
|
|
JRS 20021001: following discussions with John Regehr, if a stack
|
|
switch happens, it seems best not to mess at all with memory
|
|
permissions. Seems to work well with Netscape 4.X. Really the
|
|
only remaining difficulty is knowing exactly when a stack switch is
|
|
happening. */
|
|
if (VG_(clo_verbosity) > 1)
|
|
VG_(message)(Vg_UserMsg, "Warning: client switching stacks? "
|
|
"%%esp: %p --> %p", old_SP, new_SP);
|
|
} else if (delta < 0) {
|
|
VG_TRACK( new_mem_stack, new_SP, -delta );
|
|
|
|
} else if (delta > 0) {
|
|
VG_TRACK( die_mem_stack, old_SP, delta );
|
|
}
|
|
}
|
|
|
|
/*
|
|
Test if a piece of memory is addressable with at least the "prot"
|
|
protection permissions by examining the underlying segments.
|
|
|
|
Really this is a very stupid algorithm and we could do much
|
|
better by iterating through the segment array instead of through
|
|
the address space.
|
|
*/
|
|
Bool VG_(is_addressable)(Addr p, SizeT size, UInt prot)
|
|
{
|
|
Segment *seg;
|
|
|
|
if ((p + size) < p)
|
|
return False; /* reject wraparounds */
|
|
if (size == 0)
|
|
return True; /* isn't this a bit of a strange case? */
|
|
|
|
p = PGROUNDDN(p);
|
|
size = PGROUNDUP(size);
|
|
vg_assert(IS_PAGE_ALIGNED(p));
|
|
vg_assert(IS_PAGE_ALIGNED(size));
|
|
|
|
for (; size > 0; size -= VKI_PAGE_SIZE) {
|
|
seg = VG_(find_segment)(p);
|
|
if (!seg)
|
|
return False;
|
|
if ((seg->prot & prot) != prot)
|
|
return False;
|
|
p += VKI_PAGE_SIZE;
|
|
}
|
|
|
|
return True;
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- Manage allocation of memory on behalf of the client ---*/
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
// Returns 0 on failure.
|
|
Addr VG_(get_memory_from_mmap_for_client)
|
|
(Addr addr, SizeT len, UInt prot, UInt sf_flags)
|
|
{
|
|
len = PGROUNDUP(len);
|
|
|
|
tl_assert(!(sf_flags & SF_FIXED));
|
|
tl_assert(0 == addr);
|
|
|
|
addr = (Addr)VG_(mmap)((void *)addr, len, prot,
|
|
VKI_MAP_PRIVATE | VKI_MAP_ANONYMOUS | VKI_MAP_CLIENT,
|
|
sf_flags | SF_CORE, -1, 0);
|
|
if ((Addr)-1 != addr)
|
|
return addr;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* We'll call any RW mmaped memory segment, within the client address
|
|
range, which isn't SF_CORE, a root.
|
|
*/
|
|
void VG_(find_root_memory)(void (*add_rootrange)(Addr a, SizeT sz))
|
|
{
|
|
Int i;
|
|
UInt flags;
|
|
Segment *s;
|
|
|
|
for (i = 0; i < segments_used; i++) {
|
|
s = &segments[i];
|
|
flags = s->flags & (SF_SHARED|SF_MMAP|SF_VALGRIND
|
|
|SF_CORE|SF_STACK|SF_DEVICE);
|
|
if (flags != SF_MMAP && flags != SF_STACK)
|
|
continue;
|
|
if ((s->prot & (VKI_PROT_READ|VKI_PROT_WRITE))
|
|
!= (VKI_PROT_READ|VKI_PROT_WRITE))
|
|
continue;
|
|
if (!VG_(is_client_addr)(s->addr) ||
|
|
!VG_(is_client_addr)(s->addr+s->len))
|
|
continue;
|
|
|
|
(*add_rootrange)(s->addr, s->len);
|
|
}
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- Querying memory layout ---*/
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
Bool VG_(is_client_addr)(Addr a)
|
|
{
|
|
return a >= VG_(client_base) && a < VG_(client_end);
|
|
}
|
|
|
|
Bool VG_(is_shadow_addr)(Addr a)
|
|
{
|
|
return a >= VG_(shadow_base) && a < VG_(shadow_end);
|
|
}
|
|
|
|
Bool VG_(is_valgrind_addr)(Addr a)
|
|
{
|
|
vg_assert(0);
|
|
return a >= VG_(valgrind_base) && a <= VG_(valgrind_last);
|
|
}
|
|
|
|
Addr VG_(get_client_base)(void)
|
|
{
|
|
vg_assert(0);
|
|
return VG_(client_base);
|
|
}
|
|
|
|
Addr VG_(get_client_end)(void)
|
|
{
|
|
vg_assert(0);
|
|
return VG_(client_end);
|
|
}
|
|
|
|
Addr VG_(get_client_size)(void)
|
|
{
|
|
vg_assert(0);
|
|
return VG_(client_end)-VG_(client_base);
|
|
}
|
|
|
|
Addr VG_(get_shadow_base)(void)
|
|
{
|
|
vg_assert(0);
|
|
return VG_(shadow_base);
|
|
}
|
|
|
|
Addr VG_(get_shadow_end)(void)
|
|
{
|
|
vg_assert(0);
|
|
return VG_(shadow_end);
|
|
}
|
|
|
|
Addr VG_(get_shadow_size)(void)
|
|
{
|
|
return VG_(shadow_end)-VG_(shadow_base);
|
|
}
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- Handling shadow memory ---*/
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
void VG_(init_shadow_range)(Addr p, UInt sz, Bool call_init)
|
|
{
|
|
vg_assert(0);
|
|
if (0)
|
|
VG_(printf)("init_shadow_range(%p, %d)\n", p, sz);
|
|
|
|
vg_assert(VG_(needs).shadow_memory);
|
|
vg_assert(VG_(defined_init_shadow_page)());
|
|
|
|
sz = PGROUNDUP(p+sz) - PGROUNDDN(p);
|
|
p = PGROUNDDN(p);
|
|
|
|
VG_(mprotect)((void *)p, sz, VKI_PROT_READ|VKI_PROT_WRITE);
|
|
|
|
if (call_init)
|
|
while(sz) {
|
|
/* ask the tool to initialize each page */
|
|
VG_TRACK( init_shadow_page, PGROUNDDN(p) );
|
|
|
|
p += VKI_PAGE_SIZE;
|
|
sz -= VKI_PAGE_SIZE;
|
|
}
|
|
}
|
|
|
|
void *VG_(shadow_alloc)(UInt size)
|
|
{
|
|
static Addr shadow_alloc = 0;
|
|
Addr try_here;
|
|
Int r;
|
|
|
|
if (0) show_segments("shadow_alloc(before)");
|
|
|
|
vg_assert(VG_(needs).shadow_memory);
|
|
vg_assert(!VG_(defined_init_shadow_page)());
|
|
|
|
size = PGROUNDUP(size);
|
|
|
|
if (shadow_alloc == 0)
|
|
shadow_alloc = VG_(shadow_base);
|
|
|
|
if (shadow_alloc >= VG_(shadow_end))
|
|
goto failed;
|
|
|
|
try_here = shadow_alloc;
|
|
vg_assert(IS_PAGE_ALIGNED(try_here));
|
|
vg_assert(IS_PAGE_ALIGNED(size));
|
|
vg_assert(size > 0);
|
|
|
|
if (0)
|
|
VG_(printf)("shadow_alloc: size %d, trying at %p\n", size, (void*)try_here);
|
|
|
|
/* this is big-bang allocated, so we don't expect to find a listed
|
|
segment for it. */
|
|
/* This is really an absolute disgrace. Sometimes the big-bang
|
|
mapping is in the list (due to re-reads of /proc/self/maps,
|
|
presumably) and sometimes it isn't. */
|
|
#if 0
|
|
r = find_segment(try_here);
|
|
vg_assert(r == -1);
|
|
r = find_segment(try_here+size-1);
|
|
vg_assert(r == -1);
|
|
#endif
|
|
|
|
r = VG_(mprotect_native)( (void*)try_here,
|
|
size, VKI_PROT_READ|VKI_PROT_WRITE );
|
|
|
|
if (r != 0)
|
|
goto failed;
|
|
|
|
shadow_alloc += size;
|
|
return (void*)try_here;
|
|
|
|
failed:
|
|
VG_(printf)(
|
|
"valgrind: Could not allocate address space (0x%x bytes)\n"
|
|
"valgrind: for shadow memory chunk.\n",
|
|
size
|
|
);
|
|
VG_(exit)(1);
|
|
}
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- end vg_memory.c ---*/
|
|
/*--------------------------------------------------------------------*/
|