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