summaryrefslogtreecommitdiff
path: root/standalone/mem_map_base.h
blob: 0560f4102d8600c4462ac0cc66c6a24d92028167 (plain)
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
//===-- mem_map_base.h ------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef SCUDO_MEM_MAP_BASE_H_
#define SCUDO_MEM_MAP_BASE_H_

#include "common.h"

namespace scudo {

// In Scudo, every memory operation will be fulfilled through a
// platform-specific `MemMap` instance. The essential APIs are listed in the
// `MemMapBase` below. This is implemented in CRTP, so for each implementation,
// it has to implement all of the 'Impl' named functions.
template <class Derived> class MemMapBase {
public:
  constexpr MemMapBase() = default;

  // This is used to map a new set of contiguous pages. Note that the `Addr` is
  // only a suggestion to the system.
  bool map(uptr Addr, uptr Size, const char *Name, uptr Flags = 0) {
    DCHECK(!isAllocated());
    return invokeImpl(&Derived::mapImpl, Addr, Size, Name, Flags);
  }

  // This is used to unmap partial/full pages from the beginning or the end.
  // I.e., the result pages are expected to be still contiguous.
  void unmap(uptr Addr, uptr Size) {
    DCHECK(isAllocated());
    DCHECK((Addr == getBase()) || (Addr + Size == getBase() + getCapacity()));
    invokeImpl(&Derived::unmapImpl, Addr, Size);
  }

  // This is used to remap a mapped range (either from map() or dispatched from
  // ReservedMemory). For example, we have reserved several pages and then we
  // want to remap them with different accessibility.
  bool remap(uptr Addr, uptr Size, const char *Name, uptr Flags = 0) {
    DCHECK(isAllocated());
    DCHECK((Addr >= getBase()) || (Addr + Size <= getBase() + getCapacity()));
    return invokeImpl(&Derived::remapImpl, Addr, Size, Name, Flags);
  }

  // This is used to update the pages' access permission. For example, mark
  // pages as no read/write permission.
  void setMemoryPermission(uptr Addr, uptr Size, uptr Flags) {
    DCHECK(isAllocated());
    DCHECK((Addr >= getBase()) || (Addr + Size <= getBase() + getCapacity()));
    return static_cast<Derived *>(this)->setMemoryPermissionImpl(Addr, Size,
                                                                 Flags);
  }

  // Suggest releasing a set of contiguous physical pages back to the OS. Note
  // that only physical pages are supposed to be released. Any release of
  // virtual pages may lead to undefined behavior.
  void releasePagesToOS(uptr From, uptr Size) {
    DCHECK(isAllocated());
    DCHECK((From >= getBase()) || (From + Size <= getBase() + getCapacity()));
    invokeImpl(&Derived::releasePagesToOSImpl, From, Size);
  }
  // This is similar to the above one except that any subsequent access to the
  // released pages will return with zero-filled pages.
  void releaseAndZeroPagesToOS(uptr From, uptr Size) {
    DCHECK(isAllocated());
    DCHECK((From >= getBase()) || (From + Size <= getBase() + getCapacity()));
    invokeImpl(&Derived::releaseAndZeroPagesToOSImpl, From, Size);
  }

  uptr getBase() { return invokeImpl(&Derived::getBaseImpl); }
  uptr getCapacity() { return invokeImpl(&Derived::getCapacityImpl); }

  bool isAllocated() { return getBase() != 0U; }

protected:
  template <typename R, typename... Args>
  R invokeImpl(R (Derived::*MemFn)(Args...), Args... args) {
    return (static_cast<Derived *>(this)->*MemFn)(args...);
  }
};

// `ReservedMemory` is a special memory handle which can be viewed as a page
// allocator. `ReservedMemory` will reserve a contiguous pages and the later
// page request can be fulfilled at the designated address. This is used when
// we want to ensure the virtual address of the MemMap will be in a known range.
// This is implemented in CRTP, so for each
// implementation, it has to implement all of the 'Impl' named functions.
template <class Derived, typename MemMapTy> class ReservedMemory {
public:
  using MemMapT = MemMapTy;
  constexpr ReservedMemory() = default;

  // Reserve a chunk of memory at a suggested address.
  bool create(uptr Addr, uptr Size, const char *Name, uptr Flags = 0) {
    DCHECK(!isCreated());
    return invokeImpl(&Derived::createImpl, Addr, Size, Name, Flags);
  }

  // Release the entire reserved memory.
  void release() {
    DCHECK(isCreated());
    invokeImpl(&Derived::releaseImpl);
  }

  // Dispatch a sub-range of reserved memory. Note that any fragmentation of
  // the reserved pages is managed by each implementation.
  MemMapT dispatch(uptr Addr, uptr Size) {
    DCHECK(isCreated());
    DCHECK((Addr >= getBase()) || (Addr + Size <= getBase() + getCapacity()));
    return invokeImpl(&Derived::dispatchImpl, Addr, Size);
  }

  uptr getBase() { return invokeImpl(&Derived::getBaseImpl); }
  uptr getCapacity() { return invokeImpl(&Derived::getCapacityImpl); }

  bool isCreated() { return getBase() != 0U; }

protected:
  template <typename R, typename... Args>
  R invokeImpl(R (Derived::*MemFn)(Args...), Args... args) {
    return (static_cast<Derived *>(this)->*MemFn)(args...);
  }
};

} // namespace scudo

#endif // SCUDO_MEM_MAP_BASE_H_