runtime/dyninst/offptr.h - systemtap

Global variables defined

Data types defined

Functions defined

Macros defined

Source code

/* pointers based on relative offsets, for shared memory
* Copyright (C) 2012 Red Hat Inc.
*
* This file is part of systemtap, and is free software.  You can
* redistribute it and/or modify it under the terms of the GNU General
* Public License (GPL); either version 2, or (at your option) any
* later version.
*/

#ifndef _OFFPTR_H
#define _OFFPTR_H

/* An offset pointer refers to memory without using an absolute address.  This
* is useful for shared memory between processes, and perhaps also for cases
* where memory may move, as with realloc.  */

/* Implementation NB: By nature, NULL is never ever a relative pointer, always
* absolute.  If it's treated as a plain offset like any other pointer, then it
* will definitely be wrong when the base is changed.  Thus, NULL must be
* treated as a special case.  */

/* Here we have a few different implementations for testing and comparison:
*
*   OFFPTR_IMPL_GLOBAL: Offsets are stored relative to a global pointer, the
*   shared-memory base.  This means it will only work for pointers within shm.
*
*   OFFPTR_IMPL_SELF: Offsets are stored relative to the offptr_t itself.
*   This has a little more flexibility, but copying offptr_t values requires
*   more pointer arithmetic.
*
*   OFFPTR_IMPL_POINTERS: Plain pointers, not suitable for use where the
*   relative functionality is actually needed!  (e.g. anything multiprocess)
*
* The default for now is OFFPTR_IMPL_GLOBAL.
*/
#if !defined(OFFPTR_IMPL_GLOBAL) \
    && !defined(OFFPTR_IMPL_SELF) \
    && !defined(OFFPTR_IMPL_POINTERS)
#define OFFPTR_IMPL_GLOBAL 1
#endif

#if defined(OFFPTR_IMPL_GLOBAL)
/* OFFPTR_IMPL_GLOBAL: In this mode, the offset is stored relative to the
* shared-memory base pointer (see runtime/dyninst/shm.c).  This has the
* advantage of very easy offptr_t copies.  The disadvantage is that it only
* works for pointers that are part of shared memory.  It may also be costly to
* dereference the global pointer all the time, but that can be measured.
*
* For NULL, we can get away with the special-case of offset 0, so long as we
* accept that the very base of shared memory is not a valid offptr_t target.
* Given that, offset 0 should be simpler for code gen, and insulates against
* bugs slightly since a calloced offptr_t will already represent NULL.
*/

typedef struct {
    /* The offset is always relative to the global shared-memory base.
     * NULL is special-cased as offset==0.  */
    ptrdiff_t offset;
} offptr_t;

static void* _stp_shm_base; /* from runtime/dyninst/shm.c */

static inline void *
offptr_get(offptr_t* op)
{
    return op->offset ? (_stp_shm_base + op->offset) : NULL;
}

static inline void
offptr_set(offptr_t* op, void* ptr)
{
    op->offset = ptr ? (ptr - _stp_shm_base) : 0;
}

#elif defined(OFFPTR_IMPL_SELF)
/* OFFPTR_IMPL_SELF: In this mode, the offset is stored relative to the
* offptr_t itself.  The advantage of this is better abstraction, as different
* offptr_t could refer to different memory blocks (as long as they're
* self-contained).  The disadvantage is that it requires more math to copy
* offptr_t values around, as with linked-list updates.
*
* For NULL, we could let offset 0 be special, but linked lists often want to
* link back to themselves, which would be a legitimate offset 0.  Instead,
* we'll let offset 1 represent NULL, which should never happen naturally as
* it's in the middle of the offptr_t itself.
*/

typedef struct {
    /* The offset is always relative to the offptr_t itself.
     * NULL is special-cased as offset==1.  */
    ptrdiff_t offset;
} offptr_t;

static inline void *
offptr_get(offptr_t* op)
{
    return (op->offset == 1) ? NULL : (op->offset + (void*)op);
}

static inline void
offptr_set(offptr_t* op, void* ptr)
{
    op->offset = (ptr == NULL) ? 1 : (ptr - (void*)op);
}

#elif defined(OFFPTR_IMPL_POINTERS)
/* OFFPTR_IMPL_POINTERS: In this mode, offptr_t is basically a plain pointer
* again.  Despite the level of abstraction, it should compile down to the same
* as using direct pointers would.
*
* Since this is always absolute, the NULL caveat doesn't apply.
*/
typedef struct { void* pointer; } offptr_t;
static inline void * offptr_get(offptr_t* op) { return op->pointer; }
static inline void offptr_set(offptr_t* op, void* ptr) { op->pointer = ptr; }

#else
#error "No offptr_t implementation?!"
#endif

/* Since offptr_t is untyped, this template-like macro lets you define
* accessors with the appropriate pointer types enforced.  */
#define DEFINE_OFFPTR_GETSET(prefix, T1, T2, member)            \
    static inline T2* prefix##_##member(T1* ptr) {            \
        return (T2*) offptr_get(&ptr->member);            \
    }                                \
    static inline void prefix##_set_##member(T1* ptr, T2* val) {    \
        offptr_set(&ptr->member, val);                \
    }

#endif /* _OFFPTR_H */