File: mem_map.h

package info (click to toggle)
android-platform-art 11.0.0%2Br48-5
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 78,932 kB
  • sloc: cpp: 459,858; java: 163,268; asm: 22,644; python: 9,815; sh: 6,330; ansic: 4,117; xml: 2,855; perl: 77; makefile: 73
file content (427 lines) | stat: -rw-r--r-- 15,755 bytes parent folder | download
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * 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.
 */

#ifndef ART_LIBARTBASE_BASE_MEM_MAP_H_
#define ART_LIBARTBASE_BASE_MEM_MAP_H_

#include <stddef.h>
#include <sys/types.h>

#include <map>
#include <mutex>
#include <string>

#include "android-base/thread_annotations.h"
#include "macros.h"

namespace art {

#if defined(__LP64__) && !defined(__Fuchsia__) && (defined(__aarch64__) || defined(__APPLE__))
#define USE_ART_LOW_4G_ALLOCATOR 1
#else
#if defined(__LP64__) && !defined(__Fuchsia__) && !defined(__x86_64__)
#error "Unrecognized 64-bit architecture."
#endif
#define USE_ART_LOW_4G_ALLOCATOR 0
#endif

#ifdef __linux__
static constexpr bool kMadviseZeroes = true;
#define HAVE_MREMAP_SYSCALL true
#else
static constexpr bool kMadviseZeroes = false;
// We cannot ever perform MemMap::ReplaceWith on non-linux hosts since the syscall is not
// present.
#define HAVE_MREMAP_SYSCALL false
#endif

// Used to keep track of mmap segments.
//
// On 64b systems not supporting MAP_32BIT, the implementation of MemMap will do a linear scan
// for free pages. For security, the start of this scan should be randomized. This requires a
// dynamic initializer.
// For this to work, it is paramount that there are no other static initializers that access MemMap.
// Otherwise, calls might see uninitialized values.
class MemMap {
 public:
  static constexpr bool kCanReplaceMapping = HAVE_MREMAP_SYSCALL;

  // Creates an invalid mapping.
  MemMap() {}

  // Creates an invalid mapping. Used when we want to be more explicit than MemMap().
  static MemMap Invalid() {
    return MemMap();
  }

  MemMap(MemMap&& other) noexcept REQUIRES(!MemMap::mem_maps_lock_);
  MemMap& operator=(MemMap&& other) noexcept REQUIRES(!MemMap::mem_maps_lock_) {
    Reset();
    swap(other);
    return *this;
  }

  // Releases the memory mapping.
  ~MemMap() REQUIRES(!MemMap::mem_maps_lock_);

  // Swap two MemMaps.
  void swap(MemMap& other);

  void Reset() {
    if (IsValid()) {
      DoReset();
    }
  }

  bool IsValid() const {
    return base_size_ != 0u;
  }

  // Replace the data in this memmmap with the data in the memmap pointed to by source. The caller
  // relinquishes ownership of the source mmap.
  //
  // For the call to be successful:
  //   * The range [dest->Begin, dest->Begin() + source->Size()] must not overlap with
  //     [source->Begin(), source->End()].
  //   * Neither source nor dest may be 'reused' mappings (they must own all the pages associated
  //     with them.
  //   * kCanReplaceMapping must be true.
  //   * Neither source nor dest may use manual redzones.
  //   * Both source and dest must have the same offset from the nearest page boundary.
  //   * mremap must succeed when called on the mappings.
  //
  // If this call succeeds it will return true and:
  //   * Invalidate *source
  //   * The protection of this will remain the same.
  //   * The size of this will be the size of the source
  //   * The data in this will be the data from source.
  //
  // If this call fails it will return false and make no changes to *source or this. The ownership
  // of the source mmap is returned to the caller.
  bool ReplaceWith(/*in-out*/MemMap* source, /*out*/std::string* error);

  // Set a debug friendly name for a map. It will be prefixed with "dalvik-".
  static void SetDebugName(void* map_ptr, const char* name, size_t size);

  // Request an anonymous region of length 'byte_count' and a requested base address.
  // Use null as the requested base address if you don't care.
  //
  // `reuse` allows re-mapping an address range from an existing mapping which retains the
  // ownership of the memory. Alternatively, `reservation` allows re-mapping the start of an
  // existing reservation mapping, transferring the ownership of the memory to the new MemMap.
  //
  // The word "anonymous" in this context means "not backed by a file". The supplied
  // 'name' will be used -- on systems that support it -- to give the mapping
  // a name.
  //
  // On success, returns returns a valid MemMap.  On failure, returns an invalid MemMap.
  static MemMap MapAnonymous(const char* name,
                             uint8_t* addr,
                             size_t byte_count,
                             int prot,
                             bool low_4gb,
                             bool reuse,
                             /*inout*/MemMap* reservation,
                             /*out*/std::string* error_msg,
                             bool use_debug_name = true);
  static MemMap MapAnonymous(const char* name,
                             size_t byte_count,
                             int prot,
                             bool low_4gb,
                             /*out*/std::string* error_msg) {
    return MapAnonymous(name,
                        /*addr=*/ nullptr,
                        byte_count,
                        prot,
                        low_4gb,
                        /*reuse=*/ false,
                        /*reservation=*/ nullptr,
                        error_msg);
  }
  static MemMap MapAnonymous(const char* name,
                             size_t byte_count,
                             int prot,
                             bool low_4gb,
                             MemMap* reservation,
                             /*out*/std::string* error_msg) {
    return MapAnonymous(name,
                        /*addr=*/ (reservation != nullptr) ? reservation->Begin() : nullptr,
                        byte_count,
                        prot,
                        low_4gb,
                        /*reuse=*/ false,
                        reservation,
                        error_msg);
  }

  // Create placeholder for a region allocated by direct call to mmap.
  // This is useful when we do not have control over the code calling mmap,
  // but when we still want to keep track of it in the list.
  // The region is not considered to be owned and will not be unmmaped.
  static MemMap MapDummy(const char* name, uint8_t* addr, size_t byte_count);

  // Map part of a file, taking care of non-page aligned offsets.  The
  // "start" offset is absolute, not relative.
  //
  // On success, returns returns a valid MemMap.  On failure, returns an invalid MemMap.
  static MemMap MapFile(size_t byte_count,
                        int prot,
                        int flags,
                        int fd,
                        off_t start,
                        bool low_4gb,
                        const char* filename,
                        std::string* error_msg) {
    return MapFileAtAddress(nullptr,
                            byte_count,
                            prot,
                            flags,
                            fd,
                            start,
                            /*low_4gb=*/ low_4gb,
                            filename,
                            /*reuse=*/ false,
                            /*reservation=*/ nullptr,
                            error_msg);
  }

  // Map part of a file, taking care of non-page aligned offsets.  The "start" offset is absolute,
  // not relative. This version allows requesting a specific address for the base of the mapping.
  //
  // `reuse` allows re-mapping an address range from an existing mapping which retains the
  // ownership of the memory. Alternatively, `reservation` allows re-mapping the start of an
  // existing reservation mapping, transferring the ownership of the memory to the new MemMap.
  //
  // If error_msg is null then we do not print /proc/maps to the log if MapFileAtAddress fails.
  // This helps improve performance of the fail case since reading and printing /proc/maps takes
  // several milliseconds in the worst case.
  //
  // On success, returns returns a valid MemMap.  On failure, returns an invalid MemMap.
  static MemMap MapFileAtAddress(uint8_t* addr,
                                 size_t byte_count,
                                 int prot,
                                 int flags,
                                 int fd,
                                 off_t start,
                                 bool low_4gb,
                                 const char* filename,
                                 bool reuse,
                                 /*inout*/MemMap* reservation,
                                 /*out*/std::string* error_msg);

  const std::string& GetName() const {
    return name_;
  }

  bool Sync();

  bool Protect(int prot);

  void MadviseDontNeedAndZero();
  int MadviseDontFork();

  int GetProtect() const {
    return prot_;
  }

  uint8_t* Begin() const {
    return begin_;
  }

  size_t Size() const {
    return size_;
  }

  // Resize the mem-map by unmapping pages at the end. Currently only supports shrinking.
  void SetSize(size_t new_size);

  uint8_t* End() const {
    return Begin() + Size();
  }

  void* BaseBegin() const {
    return base_begin_;
  }

  size_t BaseSize() const {
    return base_size_;
  }

  void* BaseEnd() const {
    return reinterpret_cast<uint8_t*>(BaseBegin()) + BaseSize();
  }

  bool HasAddress(const void* addr) const {
    return Begin() <= addr && addr < End();
  }

  // Unmap the pages at end and remap them to create another memory map.
  MemMap RemapAtEnd(uint8_t* new_end,
                    const char* tail_name,
                    int tail_prot,
                    std::string* error_msg,
                    bool use_debug_name = true);

  // Unmap the pages of a file at end and remap them to create another memory map.
  MemMap RemapAtEnd(uint8_t* new_end,
                    const char* tail_name,
                    int tail_prot,
                    int tail_flags,
                    int fd,
                    off_t offset,
                    std::string* error_msg,
                    bool use_debug_name = true);

  // Take ownership of pages at the beginning of the mapping. The mapping must be an
  // anonymous reservation mapping, owning entire pages. The `byte_count` must not
  // exceed the size of this reservation.
  //
  // Returns a mapping owning `byte_count` bytes rounded up to entire pages
  // with size set to the passed `byte_count`.
  MemMap TakeReservedMemory(size_t byte_count);

  static bool CheckNoGaps(MemMap& begin_map, MemMap& end_map)
      REQUIRES(!MemMap::mem_maps_lock_);
  static void DumpMaps(std::ostream& os, bool terse = false)
      REQUIRES(!MemMap::mem_maps_lock_);

  // Init and Shutdown are NOT thread safe.
  // Both may be called multiple times and MemMap objects may be created any
  // time after the first call to Init and before the first call to Shutodwn.
  static void Init() REQUIRES(!MemMap::mem_maps_lock_);
  static void Shutdown() REQUIRES(!MemMap::mem_maps_lock_);

  // If the map is PROT_READ, try to read each page of the map to check it is in fact readable (not
  // faulting). This is used to diagnose a bug b/19894268 where mprotect doesn't seem to be working
  // intermittently.
  void TryReadable();

  // Align the map by unmapping the unaligned parts at the lower and the higher ends.
  void AlignBy(size_t size);

  // For annotation reasons.
  static std::mutex* GetMemMapsLock() RETURN_CAPABILITY(mem_maps_lock_) {
    return nullptr;
  }

  // Reset in a forked process the MemMap whose memory has been madvised MADV_DONTFORK
  // in the parent process.
  void ResetInForkedProcess();

 private:
  MemMap(const std::string& name,
         uint8_t* begin,
         size_t size,
         void* base_begin,
         size_t base_size,
         int prot,
         bool reuse,
         size_t redzone_size = 0) REQUIRES(!MemMap::mem_maps_lock_);

  void DoReset();
  void Invalidate();
  void SwapMembers(MemMap& other);

  static void DumpMapsLocked(std::ostream& os, bool terse)
      REQUIRES(MemMap::mem_maps_lock_);
  static bool HasMemMap(MemMap& map)
      REQUIRES(MemMap::mem_maps_lock_);
  static MemMap* GetLargestMemMapAt(void* address)
      REQUIRES(MemMap::mem_maps_lock_);
  static bool ContainedWithinExistingMap(uint8_t* ptr, size_t size, std::string* error_msg)
      REQUIRES(!MemMap::mem_maps_lock_);

  // Internal version of mmap that supports low 4gb emulation.
  static void* MapInternal(void* addr,
                           size_t length,
                           int prot,
                           int flags,
                           int fd,
                           off_t offset,
                           bool low_4gb)
      REQUIRES(!MemMap::mem_maps_lock_);
  static void* MapInternalArtLow4GBAllocator(size_t length,
                                             int prot,
                                             int flags,
                                             int fd,
                                             off_t offset)
      REQUIRES(!MemMap::mem_maps_lock_);

  // Release memory owned by a reservation mapping.
  void ReleaseReservedMemory(size_t byte_count);

  // member function to access real_munmap
  static bool CheckMapRequest(uint8_t* expected_ptr,
                              void* actual_ptr,
                              size_t byte_count,
                              std::string* error_msg);

  static bool CheckReservation(uint8_t* expected_ptr,
                               size_t byte_count,
                               const char* name,
                               const MemMap& reservation,
                               /*out*/std::string* error_msg);

  std::string name_;
  uint8_t* begin_ = nullptr;    // Start of data. May be changed by AlignBy.
  size_t size_ = 0u;            // Length of data.

  void* base_begin_ = nullptr;  // Page-aligned base address. May be changed by AlignBy.
  size_t base_size_ = 0u;       // Length of mapping. May be changed by RemapAtEnd (ie Zygote).
  int prot_ = 0;                // Protection of the map.

  // When reuse_ is true, this is just a view of an existing mapping
  // and we do not take ownership and are not responsible for
  // unmapping.
  bool reuse_ = false;

  // When already_unmapped_ is true the destructor will not call munmap.
  bool already_unmapped_ = false;

  size_t redzone_size_ = 0u;

#if USE_ART_LOW_4G_ALLOCATOR
  static uintptr_t next_mem_pos_;   // Next memory location to check for low_4g extent.

  static void* TryMemMapLow4GB(void* ptr,
                               size_t page_aligned_byte_count,
                               int prot,
                               int flags,
                               int fd,
                               off_t offset);
#endif

  static void TargetMMapInit();
  static void* TargetMMap(void* start, size_t len, int prot, int flags, int fd, off_t fd_off);
  static int TargetMUnmap(void* start, size_t len);

  static std::mutex* mem_maps_lock_;

  friend class MemMapTest;  // To allow access to base_begin_ and base_size_.
};

inline void swap(MemMap& lhs, MemMap& rhs) {
  lhs.swap(rhs);
}

std::ostream& operator<<(std::ostream& os, const MemMap& mem_map);

// Zero and release pages if possible, no requirements on alignments.
void ZeroAndReleasePages(void* address, size_t length);

}  // namespace art

#endif  // ART_LIBARTBASE_BASE_MEM_MAP_H_