• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef PANDA_LIBPANDABASE_OS_MEM_H_
17 #define PANDA_LIBPANDABASE_OS_MEM_H_
18 
19 #include "file.h"
20 #include "macros.h"
21 #include "utils/expected.h"
22 #include "utils/span.h"
23 #include "mem/mem.h"
24 
25 #include <cstddef>
26 #ifdef PANDA_TARGET_UNIX
27 #include "unix/unix_mem.h"
28 #elif PANDA_TARGET_WINDOWS
29 #include "windows/windows_mem.h"
30 #else
31 #error "Unsupported target: please provide mmap API"
32 #endif
33 
34 #include <functional>
35 #include <memory>
36 #include <optional>
37 #include <type_traits>
38 
39 namespace panda::os::mem {
40 
41 void MmapDeleter(std::byte *ptr, size_t size) noexcept;
42 
43 /**
44  * \brief Make memory region \param mem with size \param size readable and executable
45  * @param mem  Pointer to memory region (should be aligned to page size)
46  * @param size Size of memory region
47  * @return Error object if any errors occur
48  */
49 std::optional<Error> MakeMemReadExec(void *mem, size_t size);
50 
51 /**
52  * \brief Make memory region \param mem with size \param size readable and writable
53  * @param mem  Pointer to memory region (should be aligned to page size)
54  * @param size Size of memory region
55  * @return Error object if any errors occur
56  */
57 std::optional<Error> MakeMemReadWrite(void *mem, size_t size);
58 
59 /**
60  * \brief Make memory region \param mem with size \param size readable
61  * @param mem  Pointer to memory region (should be aligned to page size)
62  * @param size Size of memory region
63  * @return Error object if any errors occur
64  */
65 std::optional<Error> MakeMemReadOnly(void *mem, size_t size);
66 
67 /**
68  * \brief Align addr \param addr to page size to pass it to MakeMem functions
69  * @param addr Address to align
70  * @return Aligned address
71  */
72 uintptr_t AlignDownToPageSize(uintptr_t addr);
73 
74 /**
75  * @param alignment_in_bytes - alignment in bytes
76  * @param size - min required size in bytes
77  * @return
78  */
79 void *AlignedAlloc(size_t alignment_in_bytes, size_t size);
80 
81 template <class T>
82 class MapRange {
83 public:
MapRange(T * ptr,size_t size)84     MapRange(T *ptr, size_t size) : sp_(reinterpret_cast<std::byte *>(ptr), size) {}
85 
GetSubRange(size_t offset,size_t size)86     MapRange GetSubRange(size_t offset, size_t size)
87     {
88         return MapRange(sp_.SubSpan(offset, size));
89     }
90 
MakeReadExec()91     Expected<const std::byte *, Error> MakeReadExec()
92     {
93         auto res = MakeMemReadExec(sp_.Data(), sp_.Size());
94         if (res) {
95             return Unexpected(res.value());
96         }
97 
98         return sp_.Data();
99     }
100 
MakeReadOnly()101     Expected<const std::byte *, Error> MakeReadOnly()
102     {
103         auto res = MakeMemReadOnly(sp_.Data(), sp_.Size());
104         if (res) {
105             return Unexpected(res.value());
106         }
107 
108         return sp_.Data();
109     }
110 
MakeReadWrite()111     Expected<std::byte *, Error> MakeReadWrite()
112     {
113         auto res = MakeMemReadWrite(sp_.Data(), sp_.Size());
114         if (res) {
115             return Unexpected(res.value());
116         }
117 
118         return sp_.Data();
119     }
120 
Align()121     MapRange<T> Align() const
122     {
123         auto unaligned = reinterpret_cast<uintptr_t>(sp_.Data());
124         auto aligned = AlignDownToPageSize(unaligned);
125         Span<std::byte> sp(reinterpret_cast<std::byte *>(aligned), sp_.Size() + unaligned - aligned);
126         return MapRange<T>(sp);
127     }
128 
GetSize()129     size_t GetSize() const
130     {
131         return sp_.Size();
132     }
133 
GetData()134     std::byte *GetData()
135     {
136         return sp_.Data();
137     }
138 
139     virtual ~MapRange() = default;
140 
141     DEFAULT_COPY_SEMANTIC(MapRange);
142     NO_MOVE_SEMANTIC(MapRange);
143 
144 private:
MapRange(const Span<std::byte> & sp)145     explicit MapRange(const Span<std::byte> &sp) : sp_(sp) {}
146 
147     Span<std::byte> sp_;
148 };
149 
150 enum class MapPtrType { CONST, NON_CONST };
151 
152 template <class T, MapPtrType type>
153 class MapPtr {
154 public:
155     using Deleter = void (*)(T *, size_t) noexcept;
156 
MapPtr(T * ptr,size_t size,Deleter deleter)157     MapPtr(T *ptr, size_t size, Deleter deleter) : ptr_(ptr), size_(size), page_offset_(0), deleter_(deleter) {}
MapPtr(T * ptr,size_t size,size_t page_offset,Deleter deleter)158     MapPtr(T *ptr, size_t size, size_t page_offset, Deleter deleter)
159         : ptr_(ptr), size_(size), page_offset_(page_offset), deleter_(deleter)
160     {
161     }
162 
MapPtr(MapPtr && other)163     MapPtr(MapPtr &&other) noexcept
164     {
165         ptr_ = other.ptr_;
166         page_offset_ = other.page_offset_;
167         size_ = other.size_;
168         deleter_ = other.deleter_;
169         other.ptr_ = nullptr;
170         other.deleter_ = nullptr;
171     }
172 
173     MapPtr &operator=(MapPtr &&other) noexcept
174     {
175         ptr_ = other.ptr_;
176         page_offset_ = other.page_offset_;
177         size_ = other.size_;
178         deleter_ = other.deleter_;
179         other.ptr_ = nullptr;
180         other.deleter_ = nullptr;
181         return *this;
182     }
183 
Get()184     std::conditional_t<type == MapPtrType::CONST, const T *, T *> Get() const
185     {
186         return ptr_;
187     }
188 
GetSize()189     size_t GetSize() const
190     {
191         return size_;
192     }
193 
GetMapRange()194     MapRange<T> GetMapRange() const
195     {
196         return MapRange(ptr_, size_);
197     }
198 
GetMapRange()199     MapRange<T> GetMapRange()
200     {
201         return MapRange(ptr_, size_);
202     }
203 
ToConst()204     MapPtr<T, MapPtrType::CONST> ToConst()
205     {
206         MapPtr<T, MapPtrType::CONST> res(ptr_, size_, page_offset_, deleter_);
207         ptr_ = nullptr;
208         return res;
209     }
210 
211     /*
212      * memory layout for mmap
213      *
214      *             addr(is )
215      *              ^
216      *          page_offset_ |   size_
217      *              |--------|-----------|
218      *  P0          P1       |  P2       |  P3          P4
219      *  |           |        |  |        |  |           |   4 pages
220      *  +-----------+--------S--+--------E--+-----------+
221      *                       ^
222      *                       |
223      *                      ptr_
224      *              |--------------------| mmap memory
225      *                       size
226      *
227      * S: file start; E: file end
228      * Available space: [ptr_...(ptr_ + size_ - 1)]
229      * addr should be page aligned for file map but it is not guaranteed for anonymous map
230      * For anonymous map, page_offset_ = 0
231      */
~MapPtr()232     ~MapPtr()
233     {
234         if (ptr_ == nullptr) {
235             return;
236         }
237         uintptr_t addr = reinterpret_cast<uintptr_t>(ptr_) - page_offset_;
238         // LINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
239         size_t size = size_ + page_offset_;
240         deleter_(reinterpret_cast<T *>(addr), size);
241     }
242 
243 private:
244     T *ptr_;
245     size_t size_;
246     size_t page_offset_;
247     Deleter deleter_;
248 
249     NO_COPY_SEMANTIC(MapPtr);
250 };
251 
252 using ByteMapRange = MapRange<std::byte>;
253 using BytePtr = MapPtr<std::byte, MapPtrType::NON_CONST>;
254 using ConstBytePtr = MapPtr<std::byte, MapPtrType::CONST>;
255 
256 /**
257  * Map the specified file into memory.
258  * The interface is similar to POSIX mmap.
259  * @param file - file to map
260  * @param prot - memory protection flags, a combination of MMAP_PROT_XXX values.
261  * @param flags - memory map flags, a combination of MMAP_FLAG_XXX values.
262  * @param file_offset - an offset in the file. If the offset is not multiple of page size
263  * the function handles this situation. The resulting BytePtr will point to desired data.
264  * @param hint - an desired address to map file to.
265  */
266 BytePtr MapFile(file::File file, uint32_t prot, uint32_t flags, size_t size, size_t file_offset = 0,
267                 void *hint = nullptr);
268 
269 /**
270  * \brief allocates executed memory of size \param size
271  * @param size Size of memory region
272  * @return
273  */
274 BytePtr MapExecuted(size_t size);
275 
276 /**
277  * Anonymous mmap with READ | WRITE protection for pages
278  * Note: returned memory will be poisoned in ASAN targets,
279  * if you need other behavior - consider to change interface, or use manual unpoisoning.
280  * @param size - size in bytes, should be multiple of PAGE_SIZE
281  * @param force_poison - poison mmaped memory
282  * @return
283  */
284 void *MapRWAnonymousRaw(size_t size, bool force_poison = true);
285 
286 /**
287  * Anonymous mmap with READ | WRITE protection for pages.
288  * Returned address will be aligned as \param aligment_in_bytes.
289  * Note: returned memory will be poisoned in ASAN targets,
290  * if you need other behavior - consider to change interface, or use manual unpoisoning.
291  * @param size - size in bytes, should be multiple of PAGE_SIZE
292  * @param aligment_in_bytes - alignment in bytes, should be multiple of PAGE_SIZE
293  * @param force_poison - poison mmaped memory
294  * @return
295  */
296 void *MapRWAnonymousWithAlignmentRaw(size_t size, size_t aligment_in_bytes, bool force_poison = true);
297 
298 // ASAN mapped its structures at this magic address (shadow offset)
299 // Therefore, we can successfully allocate memory at fixed address started somewhere at lower addresses
300 // and it can overlap sanitizer address space and mmap with MAP_FIXED flag finished successfully.
301 // (one can look at the MAP_FIXED flag description of Linux mmap)
302 // However, all load/store from this memory is prohibited.
303 // We can get an error during mmap call only if we use MAP_FIXED_NOREPLACE argument,
304 // but it is supported only since Linux 4.17 (Ubuntu 18 has 4.15)
305 #ifdef PANDA_TARGET_ARM64
306 static constexpr uint64_t MMAP_FIXED_MAGIC_ADDR_FOR_ASAN = 1ULL << 36ULL;
307 #else
308 static constexpr uint64_t MMAP_FIXED_MAGIC_ADDR_FOR_ASAN = 0x7fff8000ULL;
309 #endif
310 
311 /**
312  * Anonymous mmap with fixed address and READ | WRITE protection for pages
313  * Note: returned memory will be poisoned in ASAN targets,
314  * if you need other behavior - consider to change interface, or use manual unpoisoning.
315  * @param mem used address
316  * @param size size in bytes, should be multiple of PAGE_SIZE
317  * @param force_poison poison mmaped memory
318  * @return pointer to the mapped area
319  */
320 void *MapRWAnonymousFixedRaw(void *mem, size_t size, bool force_poison = true);
321 
322 /**
323  * Unmap previously mapped memory.
324  * Note: memory will be unpoisoned before unmapping in ASAN targets.
325  * @param mem - pointer to the memory
326  * @param size - size of memory to unmap
327  * @return Error object if any error occur
328  */
329 std::optional<Error> UnmapRaw(void *mem, size_t size);
330 
331 /**
332  * \brief Get page size for the system
333  * @return
334  */
335 uint32_t GetPageSize();
336 
337 /**
338  * Release pages [pages_start, pages_end] to os.
339  * @param pages_start - address of pages beginning, should be multiple of PAGE_SIZE
340  * @param pages_end - address of pages ending, should be multiple of PAGE_SIZE
341  * @return
342  */
ReleasePages(uintptr_t pages_start,uintptr_t pages_end)343 inline void ReleasePages([[maybe_unused]] uintptr_t pages_start, [[maybe_unused]] uintptr_t pages_end)
344 {
345     ASSERT(pages_start % os::mem::GetPageSize() == 0);
346     ASSERT(pages_end % os::mem::GetPageSize() == 0);
347     ASSERT(pages_end >= pages_start);
348 #ifdef PANDA_TARGET_UNIX
349     madvise(ToVoidPtr(pages_start), pages_end - pages_start, MADV_DONTNEED);
350 #else
351     UNREACHABLE();
352 #endif
353 }
354 
355 /**
356  * Tag anonymous memory with a debug name.
357  * @param mem - pointer to the memory
358  * @param size - size of memory to tag
359  * @param tag - pointer to the debug name (must be a literal or heap object)
360  * @return Error object if any error occur
361  */
362 std::optional<Error> TagAnonymousMemory(const void *mem, size_t size, const char *tag);
363 
364 static constexpr size_t DEFAULT_NATIVE_BYTES_FROM_MALLINFO = 100000;
365 
366 size_t GetNativeBytesFromMallinfo();
367 
368 }  // namespace panda::os::mem
369 
370 #endif  // PANDA_LIBPANDABASE_OS_MEM_H_
371