1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
|
/* 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 */
|