]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/compiler-rt/lib/scudo/scudo_allocator_secondary.h
Merge ^/head r319779 through r319800.
[FreeBSD/FreeBSD.git] / contrib / compiler-rt / lib / scudo / scudo_allocator_secondary.h
1 //===-- scudo_allocator_secondary.h -----------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 ///
10 /// Scudo Secondary Allocator.
11 /// This services allocation that are too large to be serviced by the Primary
12 /// Allocator. It is directly backed by the memory mapping functions of the
13 /// operating system.
14 ///
15 //===----------------------------------------------------------------------===//
16
17 #ifndef SCUDO_ALLOCATOR_SECONDARY_H_
18 #define SCUDO_ALLOCATOR_SECONDARY_H_
19
20 #ifndef SCUDO_ALLOCATOR_H_
21 # error "This file must be included inside scudo_allocator.h."
22 #endif
23
24 class ScudoLargeMmapAllocator {
25  public:
26
27   void Init(bool AllocatorMayReturnNull) {
28     PageSize = GetPageSizeCached();
29     atomic_store_relaxed(&MayReturnNull, AllocatorMayReturnNull);
30   }
31
32   void *Allocate(AllocatorStats *Stats, uptr Size, uptr Alignment) {
33     uptr UserSize = Size - AlignedChunkHeaderSize;
34     // The Scudo frontend prevents us from allocating more than
35     // MaxAllowedMallocSize, so integer overflow checks would be superfluous.
36     uptr MapSize = Size + SecondaryHeaderSize;
37     if (Alignment > MinAlignment)
38       MapSize += Alignment;
39     MapSize = RoundUpTo(MapSize, PageSize);
40     // Account for 2 guard pages, one before and one after the chunk.
41     MapSize += 2 * PageSize;
42
43     uptr MapBeg = reinterpret_cast<uptr>(MmapNoAccess(MapSize));
44     if (MapBeg == ~static_cast<uptr>(0))
45       return ReturnNullOrDieOnOOM();
46     // A page-aligned pointer is assumed after that, so check it now.
47     CHECK(IsAligned(MapBeg, PageSize));
48     uptr MapEnd = MapBeg + MapSize;
49     // The beginning of the user area for that allocation comes after the
50     // initial guard page, and both headers. This is the pointer that has to
51     // abide by alignment requirements.
52     uptr UserBeg = MapBeg + PageSize + HeadersSize;
53     uptr UserEnd = UserBeg + UserSize;
54
55     // In the rare event of larger alignments, we will attempt to fit the mmap
56     // area better and unmap extraneous memory. This will also ensure that the
57     // offset and unused bytes field of the header stay small.
58     if (Alignment > MinAlignment) {
59       if (!IsAligned(UserBeg, Alignment)) {
60         UserBeg = RoundUpTo(UserBeg, Alignment);
61         CHECK_GE(UserBeg, MapBeg);
62         uptr NewMapBeg = RoundDownTo(UserBeg - HeadersSize, PageSize) -
63             PageSize;
64         CHECK_GE(NewMapBeg, MapBeg);
65         if (NewMapBeg != MapBeg) {
66           UnmapOrDie(reinterpret_cast<void *>(MapBeg), NewMapBeg - MapBeg);
67           MapBeg = NewMapBeg;
68         }
69         UserEnd = UserBeg + UserSize;
70       }
71       uptr NewMapEnd = RoundUpTo(UserEnd, PageSize) + PageSize;
72       if (NewMapEnd != MapEnd) {
73         UnmapOrDie(reinterpret_cast<void *>(NewMapEnd), MapEnd - NewMapEnd);
74         MapEnd = NewMapEnd;
75       }
76       MapSize = MapEnd - MapBeg;
77     }
78
79     CHECK_LE(UserEnd, MapEnd - PageSize);
80     // Actually mmap the memory, preserving the guard pages on either side.
81     CHECK_EQ(MapBeg + PageSize, reinterpret_cast<uptr>(
82         MmapFixedOrDie(MapBeg + PageSize, MapSize - 2 * PageSize)));
83     uptr Ptr = UserBeg - AlignedChunkHeaderSize;
84     SecondaryHeader *Header = getHeader(Ptr);
85     Header->MapBeg = MapBeg;
86     Header->MapSize = MapSize;
87     // The primary adds the whole class size to the stats when allocating a
88     // chunk, so we will do something similar here. But we will not account for
89     // the guard pages.
90     {
91       SpinMutexLock l(&StatsMutex);
92       Stats->Add(AllocatorStatAllocated, MapSize - 2 * PageSize);
93       Stats->Add(AllocatorStatMapped, MapSize - 2 * PageSize);
94     }
95
96     return reinterpret_cast<void *>(Ptr);
97   }
98
99   void *ReturnNullOrDieOnOOM() {
100     if (atomic_load_relaxed(&MayReturnNull))
101       return nullptr;
102     ReportAllocatorCannotReturnNull(true);
103   }
104
105   void Deallocate(AllocatorStats *Stats, void *Ptr) {
106     SecondaryHeader *Header = getHeader(Ptr);
107     {
108       SpinMutexLock l(&StatsMutex);
109       Stats->Sub(AllocatorStatAllocated, Header->MapSize - 2 * PageSize);
110       Stats->Sub(AllocatorStatMapped, Header->MapSize - 2 * PageSize);
111     }
112     UnmapOrDie(reinterpret_cast<void *>(Header->MapBeg), Header->MapSize);
113   }
114
115   uptr GetActuallyAllocatedSize(void *Ptr) {
116     SecondaryHeader *Header = getHeader(Ptr);
117     // Deduct PageSize as MapSize includes the trailing guard page.
118     uptr MapEnd = Header->MapBeg + Header->MapSize - PageSize;
119     return MapEnd - reinterpret_cast<uptr>(Ptr);
120   }
121
122  private:
123   // A Secondary allocated chunk header contains the base of the mapping and
124   // its size, which comprises the guard pages.
125   struct SecondaryHeader {
126     uptr MapBeg;
127     uptr MapSize;
128   };
129   // Check that sizeof(SecondaryHeader) is a multiple of MinAlignment.
130   COMPILER_CHECK((sizeof(SecondaryHeader) & (MinAlignment - 1)) == 0);
131
132   SecondaryHeader *getHeader(uptr Ptr) {
133     return reinterpret_cast<SecondaryHeader*>(Ptr - sizeof(SecondaryHeader));
134   }
135   SecondaryHeader *getHeader(const void *Ptr) {
136     return getHeader(reinterpret_cast<uptr>(Ptr));
137   }
138
139   const uptr SecondaryHeaderSize = sizeof(SecondaryHeader);
140   const uptr HeadersSize = SecondaryHeaderSize + AlignedChunkHeaderSize;
141   uptr PageSize;
142   SpinMutex StatsMutex;
143   atomic_uint8_t MayReturnNull;
144 };
145
146 #endif  // SCUDO_ALLOCATOR_SECONDARY_H_