• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *  * Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  *  * Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in
12  *    the documentation and/or other materials provided with the
13  *    distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include "linker_block_allocator.h"
30 #include <inttypes.h>
31 #include <string.h>
32 #include <sys/mman.h>
33 #include <sys/prctl.h>
34 #include <unistd.h>
35 
36 static constexpr size_t kAllocateSize = PAGE_SIZE * 100;
37 static_assert(kAllocateSize % PAGE_SIZE == 0, "Invalid kAllocateSize.");
38 
39 // the multiplier should be power of 2
round_up(size_t size,size_t multiplier)40 static constexpr size_t round_up(size_t size, size_t multiplier) {
41   return (size + (multiplier - 1)) & ~(multiplier-1);
42 }
43 
44 struct LinkerBlockAllocatorPage {
45   LinkerBlockAllocatorPage* next;
46   uint8_t bytes[kAllocateSize - 16] __attribute__((aligned(16)));
47 };
48 
49 struct FreeBlockInfo {
50   void* next_block;
51   size_t num_free_blocks;
52 };
53 
LinkerBlockAllocator(size_t block_size)54 LinkerBlockAllocator::LinkerBlockAllocator(size_t block_size)
55   : block_size_(
56       round_up(block_size < sizeof(FreeBlockInfo) ? sizeof(FreeBlockInfo) : block_size, 16)),
57     page_list_(nullptr),
58     free_block_list_(nullptr),
59     allocated_(0)
60 {}
61 
alloc()62 void* LinkerBlockAllocator::alloc() {
63   if (free_block_list_ == nullptr) {
64     create_new_page();
65   }
66 
67   FreeBlockInfo* block_info = reinterpret_cast<FreeBlockInfo*>(free_block_list_);
68   if (block_info->num_free_blocks > 1) {
69     FreeBlockInfo* next_block_info = reinterpret_cast<FreeBlockInfo*>(
70       reinterpret_cast<char*>(free_block_list_) + block_size_);
71     next_block_info->next_block = block_info->next_block;
72     next_block_info->num_free_blocks = block_info->num_free_blocks - 1;
73     free_block_list_ = next_block_info;
74   } else {
75     free_block_list_ = block_info->next_block;
76   }
77 
78   memset(block_info, 0, block_size_);
79 
80   ++allocated_;
81 
82   return block_info;
83 }
84 
free(void * block)85 void LinkerBlockAllocator::free(void* block) {
86   if (block == nullptr) {
87     return;
88   }
89 
90   LinkerBlockAllocatorPage* page = find_page(block);
91 
92   if (page == nullptr) {
93     abort();
94   }
95 
96   ssize_t offset = reinterpret_cast<uint8_t*>(block) - page->bytes;
97 
98   if (offset % block_size_ != 0) {
99     abort();
100   }
101 
102   memset(block, 0, block_size_);
103 
104   FreeBlockInfo* block_info = reinterpret_cast<FreeBlockInfo*>(block);
105 
106   block_info->next_block = free_block_list_;
107   block_info->num_free_blocks = 1;
108 
109   free_block_list_ = block_info;
110 
111   --allocated_;
112 }
113 
protect_all(int prot)114 void LinkerBlockAllocator::protect_all(int prot) {
115   for (LinkerBlockAllocatorPage* page = page_list_; page != nullptr; page = page->next) {
116     if (mprotect(page, kAllocateSize, prot) == -1) {
117       abort();
118     }
119   }
120 }
121 
create_new_page()122 void LinkerBlockAllocator::create_new_page() {
123   static_assert(sizeof(LinkerBlockAllocatorPage) == kAllocateSize,
124                 "Invalid sizeof(LinkerBlockAllocatorPage)");
125 
126   LinkerBlockAllocatorPage* page = reinterpret_cast<LinkerBlockAllocatorPage*>(
127       mmap(nullptr, kAllocateSize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0));
128 
129   if (page == MAP_FAILED) {
130     abort(); // oom
131   }
132 
133   prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, page, kAllocateSize, "linker_alloc");
134 
135   FreeBlockInfo* first_block = reinterpret_cast<FreeBlockInfo*>(page->bytes);
136   first_block->next_block = free_block_list_;
137   first_block->num_free_blocks = sizeof(page->bytes) / block_size_;
138 
139   free_block_list_ = first_block;
140 
141   page->next = page_list_;
142   page_list_ = page;
143 }
144 
find_page(void * block)145 LinkerBlockAllocatorPage* LinkerBlockAllocator::find_page(void* block) {
146   if (block == nullptr) {
147     abort();
148   }
149 
150   LinkerBlockAllocatorPage* page = page_list_;
151   while (page != nullptr) {
152     const uint8_t* page_ptr = reinterpret_cast<const uint8_t*>(page);
153     if (block >= (page_ptr + sizeof(page->next)) && block < (page_ptr + kAllocateSize)) {
154       return page;
155     }
156 
157     page = page->next;
158   }
159 
160   abort();
161 }
162 
purge()163 void LinkerBlockAllocator::purge() {
164   if (allocated_) {
165     return;
166   }
167 
168   LinkerBlockAllocatorPage* page = page_list_;
169   while (page) {
170     LinkerBlockAllocatorPage* next = page->next;
171     munmap(page, kAllocateSize);
172     page = next;
173   }
174   page_list_ = nullptr;
175   free_block_list_ = nullptr;
176 }
177