• 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 
31 #include <inttypes.h>
32 #include <string.h>
33 #include <sys/mman.h>
34 #include <sys/param.h>
35 #include <sys/prctl.h>
36 #include <unistd.h>
37 
38 #include "linker_debug.h"
39 
40 static constexpr size_t kAllocateSize = PAGE_SIZE * 100;
41 static_assert(kAllocateSize % PAGE_SIZE == 0, "Invalid kAllocateSize.");
42 
43 struct LinkerBlockAllocatorPage {
44   LinkerBlockAllocatorPage* next;
45   uint8_t bytes[kAllocateSize - 16] __attribute__((aligned(16)));
46 };
47 
48 struct FreeBlockInfo {
49   void* next_block;
50   size_t num_free_blocks;
51 };
52 
53 static_assert(kBlockSizeAlign >= alignof(FreeBlockInfo));
54 static_assert(kBlockSizeMin == sizeof(FreeBlockInfo));
55 
LinkerBlockAllocator(size_t block_size)56 LinkerBlockAllocator::LinkerBlockAllocator(size_t block_size)
57     : block_size_(__BIONIC_ALIGN(MAX(block_size, kBlockSizeMin), kBlockSizeAlign)),
58       page_list_(nullptr),
59       free_block_list_(nullptr),
60       allocated_(0) {}
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   CHECK(page != nullptr);
92 
93   ssize_t offset = reinterpret_cast<uint8_t*>(block) - page->bytes;
94   CHECK((offset % block_size_) == 0);
95 
96   memset(block, 0, block_size_);
97 
98   FreeBlockInfo* block_info = reinterpret_cast<FreeBlockInfo*>(block);
99 
100   block_info->next_block = free_block_list_;
101   block_info->num_free_blocks = 1;
102 
103   free_block_list_ = block_info;
104 
105   --allocated_;
106 }
107 
protect_all(int prot)108 void LinkerBlockAllocator::protect_all(int prot) {
109   for (LinkerBlockAllocatorPage* page = page_list_; page != nullptr; page = page->next) {
110     if (mprotect(page, kAllocateSize, prot) == -1) {
111       async_safe_fatal("mprotect(%p, %zu, %d) failed: %m", page, kAllocateSize, prot);
112     }
113   }
114 }
115 
create_new_page()116 void LinkerBlockAllocator::create_new_page() {
117   static_assert(sizeof(LinkerBlockAllocatorPage) == kAllocateSize,
118                 "Invalid sizeof(LinkerBlockAllocatorPage)");
119 
120   LinkerBlockAllocatorPage* page = reinterpret_cast<LinkerBlockAllocatorPage*>(
121       mmap(nullptr, kAllocateSize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0));
122   CHECK(page != MAP_FAILED);
123 
124   prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, page, kAllocateSize, "linker_alloc");
125 
126   FreeBlockInfo* first_block = reinterpret_cast<FreeBlockInfo*>(page->bytes);
127   first_block->next_block = free_block_list_;
128   first_block->num_free_blocks = sizeof(page->bytes) / block_size_;
129 
130   free_block_list_ = first_block;
131 
132   page->next = page_list_;
133   page_list_ = page;
134 }
135 
find_page(void * block)136 LinkerBlockAllocatorPage* LinkerBlockAllocator::find_page(void* block) {
137   CHECK(block != nullptr);
138 
139   LinkerBlockAllocatorPage* page = page_list_;
140   while (page != nullptr) {
141     const uint8_t* page_ptr = reinterpret_cast<const uint8_t*>(page);
142     if (block >= (page_ptr + sizeof(page->next)) && block < (page_ptr + kAllocateSize)) {
143       return page;
144     }
145 
146     page = page->next;
147   }
148 
149   async_safe_fatal("couldn't find page for %p", block);
150 }
151 
purge()152 void LinkerBlockAllocator::purge() {
153   if (allocated_) {
154     return;
155   }
156 
157   LinkerBlockAllocatorPage* page = page_list_;
158   while (page) {
159     LinkerBlockAllocatorPage* next = page->next;
160     munmap(page, kAllocateSize);
161     page = next;
162   }
163   page_list_ = nullptr;
164   free_block_list_ = nullptr;
165 }
166