blob: 6a35d7d720c0a077d607ad0218f3aa2901348728 [file] [log] [blame]
/***************************************************************************
*
* memattr.cpp - source for C++ Standard Library helper functions
* to determine the attributes of regions of memory
*
* $Id$
*
***************************************************************************
*
* Copyright (c) 1994-2005 Quovadx, Inc., acting through its Rogue Wave
* Software division. Licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0. Unless required by
* applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under
* the License.
*
**************************************************************************/
#define _RWSTD_LIB_SRC
#include <errno.h> // for errno
#include <string.h> // for memchr
#ifdef __CYGWIN__
// use the Windows API on Cygwin
# define _WIN32
#endif
#if !defined (_WIN32) && !defined (_WIN64)
# ifdef __SUNPRO_CC
// working around SunOS bug #568
# include <time.h>
# endif
# include <unistd.h> // for sysconf
# include <sys/mman.h> // for mincore
# include <sys/types.h>
# ifndef _SC_PAGE_SIZE
// fall back on the alternative
# define _SC_PAGE_SIZE _SC_PAGESIZE
# endif
#else
# include <windows.h> // for everything (ugh)
#endif // _WIN{32,64}
#include <rw/_defs.h>
// define ENOMEM if it's not #defined (e.g., when using pure libc headers)
#ifndef ENOMEM
# define ENOMEM 12 /* HP-UX, Solaris, Tru64, ... */
#endif // ENOMEM
// FIXME: implement PROT_READ and PROT_WRITE correctly
#ifndef _RWSTD_PROT_READ
# define _RWSTD_PROT_READ -1
#endif
#ifndef _RWSTD_PROT_WRITE
# define _RWSTD_PROT_WRITE 0
#endif
#define DIST(addr1, addr2) \
( _RWSTD_REINTERPRET_CAST (const char*, addr1) \
- _RWSTD_REINTERPRET_CAST (const char*, addr2))
_RWSTD_NAMESPACE (__rw) {
_RWSTD_EXPORT _RWSTD_SSIZE_T
__rw_memattr (const void *addr, _RWSTD_SIZE_T nbytes, int attr)
{
// FIXME: allow attr to be set to the equivalent of PROT_READ,
// PROT_WRITE, and (perhaps also) PROT_EXEC, or any combination
// of the three, in addition to 0 (PROT_NONE)
_RWSTD_UNUSED (attr);
#if !defined (_WIN32) && !defined (_WIN64)
// determine the system page size in bytes
static const _RWSTD_SIZE_T pgsz = size_t (sysconf (_SC_PAGE_SIZE));
// compute the address of the beginning of the page
// to which the address `addr' belongs
caddr_t const page =
_RWSTD_REINTERPRET_CAST (caddr_t,
_RWSTD_REINTERPRET_CAST (_RWSTD_SIZE_T, addr) & ~(pgsz - 1));
// compute the maximum number of pages occuppied by the memory
// region pointed to by `addr' (nbytes may be -1 as a request
// to compute, in a safe way, the length of the string pointed
// to by `addr')
_RWSTD_SIZE_T npages = nbytes ? nbytes / pgsz + 1 : 0;
for (size_t i = 0; i < npages; ++i) {
const caddr_t next = _RWSTD_REINTERPRET_CAST (char*, page) + i * pgsz;
# ifdef _RWSTD_OS_SUNOS
char dummy = '\0';
// on Solaris use mincore() instead of madvise() since
// the latter is unreliable
if (-1 == mincore (next, 1, &dummy) && ENOMEM == errno)
return next == page ? -1 : DIST (next, addr);
# elif !defined (_RWSTD_NO_MADVISE)
// on HP-UX, Linux, use madvise() as opposed to mincore()
// since the latter fails for address ranges that aren't
// backed by a file (such as stack variables)
if (-1 == madvise (next, 1, MADV_WILLNEED) && ENOMEM == errno)
return next == page ? -1 : DIST (next, addr);
# endif
if (_RWSTD_SIZE_MAX == nbytes) {
// when the size of the region is unspecified look
// for the first NUL byte and when found, return
// the total size of the memory region between
// `addr' and the NUL byte (i.e., the length of
// the string pointed to by `addr')
const _RWSTD_SIZE_T maxpage =
next == page ? pgsz - DIST (addr, next) : pgsz;
const void* const pnul =
memchr (next == page ? addr : next, '\0', maxpage);
if (pnul) {
nbytes = DIST (pnul, addr);
npages = nbytes / pgsz + 1;
break;
}
}
}
return _RWSTD_STATIC_CAST (_RWSTD_SSIZE_T, nbytes);
#else // if defined (_WIN{32,64})
LPVOID const ptr = _RWSTD_CONST_CAST (LPVOID, addr);
if (_RWSTD_SIZE_MAX == nbytes) {
// treat the address as a pointer to a NUL-terminated string
if (IsBadStringPtr (_RWSTD_STATIC_CAST (LPCSTR, ptr), nbytes))
return -1;
// compute the length of the string
nbytes = strlen (_RWSTD_STATIC_CAST (const char*, addr));
// disable read checking below (since it was done above)
attr &= ~_RWSTD_PROT_READ;
}
if ((attr & _RWSTD_PROT_READ) && IsBadReadPtr (ptr, nbytes))
return -1;
if ((attr & _RWSTD_PROT_WRITE) && IsBadWritePtr (ptr, nbytes))
return -1;
return _RWSTD_STATIC_CAST (_RWSTD_SSIZE_T, nbytes);
#endif // _RWSTD_NO_INCORE
}
}