Move the linker allocator into libc
Rename LinkerMemoryAllocator -> BionicAllocator
Rename LinkerSmallObjectAllocator -> BionicSmallObjectAllocator
libc and the linker need to share an instance of the allocator for
allocating and freeing dynamic ELF TLS memory (DTVs and segments). The
linker also continues to use this allocator.
Bug: http://b/78026329
Test: /data/nativetest/bionic-unit-tests-static
Test: /data/nativetest64/bionic-unit-tests-static
Test: /data/nativetest/linker-unit-tests/linker-unit-tests32
Test: /data/nativetest64/linker-unit-tests/linker-unit-tests64
Change-Id: I2da037006ddf8041a75f3eba2071a8fcdcc223ce
diff --git a/linker/Android.bp b/linker/Android.bp
index 4991935..5ae09ba 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -4,7 +4,6 @@
recovery_available: true,
srcs: [
- "linker_allocator.cpp",
"linker_memory.cpp",
],
cflags: [
diff --git a/linker/linker_allocator.cpp b/linker/linker_allocator.cpp
deleted file mode 100644
index df7c999..0000000
--- a/linker/linker_allocator.cpp
+++ /dev/null
@@ -1,375 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include "linker_allocator.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/prctl.h>
-#include <unistd.h>
-
-#include <new>
-
-#include <async_safe/log.h>
-
-#include "private/bionic_page.h"
-
-//
-// LinkerMemeoryAllocator is general purpose allocator
-// designed to provide the same functionality as the malloc/free/realloc
-// libc functions.
-//
-// On alloc:
-// If size is >= 1k allocator proxies malloc call directly to mmap
-// If size < 1k allocator uses SmallObjectAllocator for the size
-// rounded up to the nearest power of two.
-//
-// On free:
-//
-// For a pointer allocated using proxy-to-mmap allocator unmaps
-// the memory.
-//
-// For a pointer allocated using SmallObjectAllocator it adds
-// the block to free_blocks_list in the corresponding page. If the number of
-// free pages reaches 2, SmallObjectAllocator munmaps one of the pages keeping
-// the other one in reserve.
-
-// Memory management for large objects is fairly straightforward, but for small
-// objects it is more complicated. If you are changing this code, one simple
-// way to evaluate the memory usage change is by running 'dd' and examine the
-// memory usage by 'showmap $(pidof dd)'. 'dd' is nice in that:
-// 1. It links in quite a few libraries, so you get some linker memory use.
-// 2. When run with no arguments, it sits waiting for input, so it is easy to
-// examine its memory usage with showmap.
-// 3. Since it does nothing while waiting for input, the memory usage is
-// determinisitic.
-
-static const char kSignature[4] = {'L', 'M', 'A', 1};
-
-static const size_t kSmallObjectMaxSize = 1 << kSmallObjectMaxSizeLog2;
-
-// This type is used for large allocations (with size >1k)
-static const uint32_t kLargeObject = 111;
-
-// Allocated pointers must be at least 16-byte aligned. Round up the size of
-// page_info to multiple of 16.
-static constexpr size_t kPageInfoSize = __BIONIC_ALIGN(sizeof(page_info), 16);
-
-static inline uint16_t log2(size_t number) {
- uint16_t result = 0;
- number--;
-
- while (number != 0) {
- result++;
- number >>= 1;
- }
-
- return result;
-}
-
-LinkerSmallObjectAllocator::LinkerSmallObjectAllocator(uint32_t type,
- size_t block_size)
- : type_(type),
- block_size_(block_size),
- blocks_per_page_((PAGE_SIZE - sizeof(small_object_page_info)) /
- block_size),
- free_pages_cnt_(0),
- page_list_(nullptr) {}
-
-void* LinkerSmallObjectAllocator::alloc() {
- CHECK(block_size_ != 0);
-
- if (page_list_ == nullptr) {
- alloc_page();
- }
-
- // Fully allocated pages are de-managed and removed from the page list, so
- // every page from the page list must be useable. Let's just take the first
- // one.
- small_object_page_info* page = page_list_;
- CHECK(page->free_block_list != nullptr);
-
- small_object_block_record* const block_record = page->free_block_list;
- if (block_record->free_blocks_cnt > 1) {
- small_object_block_record* next_free =
- reinterpret_cast<small_object_block_record*>(
- reinterpret_cast<uint8_t*>(block_record) + block_size_);
- next_free->next = block_record->next;
- next_free->free_blocks_cnt = block_record->free_blocks_cnt - 1;
- page->free_block_list = next_free;
- } else {
- page->free_block_list = block_record->next;
- }
-
- if (page->free_blocks_cnt == blocks_per_page_) {
- free_pages_cnt_--;
- }
-
- page->free_blocks_cnt--;
-
- memset(block_record, 0, block_size_);
-
- if (page->free_blocks_cnt == 0) {
- // De-manage fully allocated pages. These pages will be managed again if
- // a block is freed.
- remove_from_page_list(page);
- }
-
- return block_record;
-}
-
-void LinkerSmallObjectAllocator::free_page(small_object_page_info* page) {
- CHECK(page->free_blocks_cnt == blocks_per_page_);
- if (page->prev_page) {
- page->prev_page->next_page = page->next_page;
- }
- if (page->next_page) {
- page->next_page->prev_page = page->prev_page;
- }
- if (page_list_ == page) {
- page_list_ = page->next_page;
- }
- munmap(page, PAGE_SIZE);
- free_pages_cnt_--;
-}
-
-void LinkerSmallObjectAllocator::free(void* ptr) {
- small_object_page_info* const page =
- reinterpret_cast<small_object_page_info*>(
- PAGE_START(reinterpret_cast<uintptr_t>(ptr)));
-
- if (reinterpret_cast<uintptr_t>(ptr) % block_size_ != 0) {
- async_safe_fatal("invalid pointer: %p (block_size=%zd)", ptr, block_size_);
- }
-
- memset(ptr, 0, block_size_);
- small_object_block_record* const block_record =
- reinterpret_cast<small_object_block_record*>(ptr);
-
- block_record->next = page->free_block_list;
- block_record->free_blocks_cnt = 1;
-
- page->free_block_list = block_record;
- page->free_blocks_cnt++;
-
- if (page->free_blocks_cnt == blocks_per_page_) {
- if (++free_pages_cnt_ > 1) {
- // if we already have a free page - unmap this one.
- free_page(page);
- }
- } else if (page->free_blocks_cnt == 1) {
- // We just freed from a full page. Add this page back to the list.
- add_to_page_list(page);
- }
-}
-
-void LinkerSmallObjectAllocator::alloc_page() {
- void* const map_ptr = mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- if (map_ptr == MAP_FAILED) {
- async_safe_fatal("mmap failed: %s", strerror(errno));
- }
-
- prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, map_ptr, PAGE_SIZE,
- "linker_alloc_small_objects");
-
- small_object_page_info* const page =
- reinterpret_cast<small_object_page_info*>(map_ptr);
- memcpy(page->info.signature, kSignature, sizeof(kSignature));
- page->info.type = type_;
- page->info.allocator_addr = this;
-
- page->free_blocks_cnt = blocks_per_page_;
-
- // Align the first block to block_size_.
- const uintptr_t first_block_addr =
- __BIONIC_ALIGN(reinterpret_cast<uintptr_t>(page + 1), block_size_);
- small_object_block_record* const first_block =
- reinterpret_cast<small_object_block_record*>(first_block_addr);
-
- first_block->next = nullptr;
- first_block->free_blocks_cnt = blocks_per_page_;
-
- page->free_block_list = first_block;
-
- add_to_page_list(page);
-
- free_pages_cnt_++;
-}
-
-void LinkerSmallObjectAllocator::add_to_page_list(small_object_page_info* page) {
- page->next_page = page_list_;
- page->prev_page = nullptr;
- if (page_list_) {
- page_list_->prev_page = page;
- }
- page_list_ = page;
-}
-
-void LinkerSmallObjectAllocator::remove_from_page_list(
- small_object_page_info* page) {
- if (page->prev_page) {
- page->prev_page->next_page = page->next_page;
- }
- if (page->next_page) {
- page->next_page->prev_page = page->prev_page;
- }
- if (page_list_ == page) {
- page_list_ = page->next_page;
- }
- page->prev_page = nullptr;
- page->next_page = nullptr;
-}
-
-void LinkerMemoryAllocator::initialize_allocators() {
- if (allocators_ != nullptr) {
- return;
- }
-
- LinkerSmallObjectAllocator* allocators =
- reinterpret_cast<LinkerSmallObjectAllocator*>(allocators_buf_);
-
- for (size_t i = 0; i < kSmallObjectAllocatorsCount; ++i) {
- uint32_t type = i + kSmallObjectMinSizeLog2;
- new (allocators + i) LinkerSmallObjectAllocator(type, 1 << type);
- }
-
- allocators_ = allocators;
-}
-
-void* LinkerMemoryAllocator::alloc_mmap(size_t size) {
- size_t allocated_size = PAGE_END(size + kPageInfoSize);
- void* map_ptr = mmap(nullptr, allocated_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS,
- -1, 0);
-
- if (map_ptr == MAP_FAILED) {
- async_safe_fatal("mmap failed: %s", strerror(errno));
- }
-
- prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, map_ptr, allocated_size, "linker_alloc_lob");
-
- page_info* info = reinterpret_cast<page_info*>(map_ptr);
- memcpy(info->signature, kSignature, sizeof(kSignature));
- info->type = kLargeObject;
- info->allocated_size = allocated_size;
-
- return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(info) +
- kPageInfoSize);
-}
-
-void* LinkerMemoryAllocator::alloc(size_t size) {
- // treat alloc(0) as alloc(1)
- if (size == 0) {
- size = 1;
- }
-
- if (size > kSmallObjectMaxSize) {
- return alloc_mmap(size);
- }
-
- uint16_t log2_size = log2(size);
-
- if (log2_size < kSmallObjectMinSizeLog2) {
- log2_size = kSmallObjectMinSizeLog2;
- }
-
- return get_small_object_allocator(log2_size)->alloc();
-}
-
-page_info* LinkerMemoryAllocator::get_page_info(void* ptr) {
- page_info* info = reinterpret_cast<page_info*>(PAGE_START(reinterpret_cast<size_t>(ptr)));
- if (memcmp(info->signature, kSignature, sizeof(kSignature)) != 0) {
- async_safe_fatal("invalid pointer %p (page signature mismatch)", ptr);
- }
-
- return info;
-}
-
-void* LinkerMemoryAllocator::realloc(void* ptr, size_t size) {
- if (ptr == nullptr) {
- return alloc(size);
- }
-
- if (size == 0) {
- free(ptr);
- return nullptr;
- }
-
- page_info* info = get_page_info(ptr);
-
- size_t old_size = 0;
-
- if (info->type == kLargeObject) {
- old_size = info->allocated_size - kPageInfoSize;
- } else {
- LinkerSmallObjectAllocator* allocator = get_small_object_allocator(info->type);
- if (allocator != info->allocator_addr) {
- async_safe_fatal("invalid pointer %p (page signature mismatch)", ptr);
- }
-
- old_size = allocator->get_block_size();
- }
-
- if (old_size < size) {
- void *result = alloc(size);
- memcpy(result, ptr, old_size);
- free(ptr);
- return result;
- }
-
- return ptr;
-}
-
-void LinkerMemoryAllocator::free(void* ptr) {
- if (ptr == nullptr) {
- return;
- }
-
- page_info* info = get_page_info(ptr);
-
- if (info->type == kLargeObject) {
- munmap(info, info->allocated_size);
- } else {
- LinkerSmallObjectAllocator* allocator = get_small_object_allocator(info->type);
- if (allocator != info->allocator_addr) {
- async_safe_fatal("invalid pointer %p (invalid allocator address for the page)", ptr);
- }
-
- allocator->free(ptr);
- }
-}
-
-LinkerSmallObjectAllocator* LinkerMemoryAllocator::get_small_object_allocator(uint32_t type) {
- if (type < kSmallObjectMinSizeLog2 || type > kSmallObjectMaxSizeLog2) {
- async_safe_fatal("invalid type: %u", type);
- }
-
- initialize_allocators();
- return &allocators_[type - kSmallObjectMinSizeLog2];
-}
diff --git a/linker/linker_allocator.h b/linker/linker_allocator.h
deleted file mode 100644
index d2b8551..0000000
--- a/linker/linker_allocator.h
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#pragma once
-
-#include <errno.h>
-#include <stdlib.h>
-#include <sys/cdefs.h>
-#include <sys/mman.h>
-#include <sys/prctl.h>
-#include <stddef.h>
-#include <unistd.h>
-
-const uint32_t kSmallObjectMaxSizeLog2 = 10;
-const uint32_t kSmallObjectMinSizeLog2 = 4;
-const uint32_t kSmallObjectAllocatorsCount = kSmallObjectMaxSizeLog2 - kSmallObjectMinSizeLog2 + 1;
-
-class LinkerSmallObjectAllocator;
-
-// This structure is placed at the beginning of each addressable page
-// and has all information we need to find the corresponding memory allocator.
-struct page_info {
- char signature[4];
- uint32_t type;
- union {
- // we use allocated_size for large objects allocator
- size_t allocated_size;
- // and allocator_addr for small ones.
- LinkerSmallObjectAllocator* allocator_addr;
- };
-};
-
-struct small_object_block_record {
- small_object_block_record* next;
- size_t free_blocks_cnt;
-};
-
-// This structure is placed at the beginning of each page managed by
-// LinkerSmallObjectAllocator. Note that a page_info struct is expected at the
-// beginning of each page as well, and therefore this structure contains a
-// page_info as its *first* field.
-struct small_object_page_info {
- page_info info; // Must be the first field.
-
- // Doubly linked list for traversing all pages allocated by a
- // LinkerSmallObjectAllocator.
- small_object_page_info* next_page;
- small_object_page_info* prev_page;
-
- // Linked list containing all free blocks in this page.
- small_object_block_record* free_block_list;
-
- // Free blocks counter.
- size_t free_blocks_cnt;
-};
-
-class LinkerSmallObjectAllocator {
- public:
- LinkerSmallObjectAllocator(uint32_t type, size_t block_size);
- void* alloc();
- void free(void* ptr);
-
- size_t get_block_size() const { return block_size_; }
- private:
- void alloc_page();
- void free_page(small_object_page_info* page);
- void add_to_page_list(small_object_page_info* page);
- void remove_from_page_list(small_object_page_info* page);
-
- const uint32_t type_;
- const size_t block_size_;
- const size_t blocks_per_page_;
-
- size_t free_pages_cnt_;
-
- small_object_page_info* page_list_;
-};
-
-class LinkerMemoryAllocator {
- public:
- constexpr LinkerMemoryAllocator() : allocators_(nullptr), allocators_buf_() {}
- void* alloc(size_t size);
-
- // Note that this implementation of realloc never shrinks allocation
- void* realloc(void* ptr, size_t size);
- void free(void* ptr);
- private:
- void* alloc_mmap(size_t size);
- page_info* get_page_info(void* ptr);
- LinkerSmallObjectAllocator* get_small_object_allocator(uint32_t type);
- void initialize_allocators();
-
- LinkerSmallObjectAllocator* allocators_;
- uint8_t allocators_buf_[sizeof(LinkerSmallObjectAllocator)*kSmallObjectAllocatorsCount];
-};
diff --git a/linker/linker_block_allocator.h b/linker/linker_block_allocator.h
index 85e6bd9..458d092 100644
--- a/linker/linker_block_allocator.h
+++ b/linker/linker_block_allocator.h
@@ -68,18 +68,18 @@
* of a single fixed-size type. Allocations are backed by page-sized private
* anonymous mmaps.
*
- * The differences between this allocator and LinkerMemoryAllocator are:
- * 1. This allocator manages space more efficiently. LinkerMemoryAllocator
- * operates in power-of-two sized blocks up to 1k, when this implementation
- * splits the page to aligned size of structure; For example for structures
- * with size 513 this allocator will use 516 (520 for lp64) bytes of data
- * where generalized implementation is going to use 1024 sized blocks.
+ * The differences between this allocator and BionicAllocator are:
+ * 1. This allocator manages space more efficiently. BionicAllocator operates in
+ * power-of-two sized blocks up to 1k, when this implementation splits the
+ * page to aligned size of structure; For example for structures with size
+ * 513 this allocator will use 516 (520 for lp64) bytes of data where
+ * generalized implementation is going to use 1024 sized blocks.
*
* 2. Unless all allocated memory is freed, this allocator does not munmap
- * allocated memory, where LinkerMemoryAllocator does.
+ * allocated memory, where BionicAllocator does.
*
- * 3. This allocator provides mprotect services to the user, where LinkerMemoryAllocator
- * always treats it's memory as READ|WRITE.
+ * 3. This allocator provides mprotect services to the user, where BionicAllocator
+ * always treats its memory as READ|WRITE.
*/
template<typename T>
class LinkerTypeAllocator {
diff --git a/linker/linker_memory.cpp b/linker/linker_memory.cpp
index f2cce01..ce29997 100644
--- a/linker/linker_memory.cpp
+++ b/linker/linker_memory.cpp
@@ -26,7 +26,7 @@
* SUCH DAMAGE.
*/
-#include "linker_allocator.h"
+#include "private/bionic_allocator.h"
#include <stdlib.h>
#include <sys/cdefs.h>
@@ -36,7 +36,7 @@
#include <async_safe/log.h>
-static LinkerMemoryAllocator g_linker_allocator;
+static BionicAllocator g_bionic_allocator;
static std::atomic<pid_t> fallback_tid(0);
// Used by libdebuggerd_handler to switch allocators during a crash dump, in
@@ -56,16 +56,16 @@
}
}
-static LinkerMemoryAllocator& get_fallback_allocator() {
- static LinkerMemoryAllocator fallback_allocator;
+static BionicAllocator& get_fallback_allocator() {
+ static BionicAllocator fallback_allocator;
return fallback_allocator;
}
-static LinkerMemoryAllocator& get_allocator() {
+static BionicAllocator& get_allocator() {
if (__predict_false(fallback_tid) && __predict_false(gettid() == fallback_tid)) {
return get_fallback_allocator();
}
- return g_linker_allocator;
+ return g_bionic_allocator;
}
void* malloc(size_t byte_count) {
diff --git a/linker/tests/Android.mk b/linker/tests/Android.mk
index 9268e31..63e0555 100644
--- a/linker/tests/Android.mk
+++ b/linker/tests/Android.mk
@@ -43,10 +43,8 @@
linker_config_test.cpp \
linker_globals.cpp \
linked_list_test.cpp \
- linker_memory_allocator_test.cpp \
linker_sleb128_test.cpp \
linker_utils_test.cpp \
- ../linker_allocator.cpp \
../linker_block_allocator.cpp \
../linker_config.cpp \
../linker_utils.cpp \
diff --git a/linker/tests/linker_memory_allocator_test.cpp b/linker/tests/linker_memory_allocator_test.cpp
deleted file mode 100644
index c284eaa..0000000
--- a/linker/tests/linker_memory_allocator_test.cpp
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-
-#include <gtest/gtest.h>
-
-#include "../linker_allocator.h"
-
-#include <unistd.h>
-
-namespace {
-
-/*
- * this one has size below allocator cap which is 2*sizeof(void*)
- */
-struct test_struct_small {
- char dummy_str[5];
-};
-
-struct test_struct_large {
- char dummy_str[1009];
-};
-
-struct test_struct_huge {
- char dummy_str[73939];
-};
-
-struct test_struct_512 {
- char dummy_str[503];
-};
-
-};
-
-static size_t kPageSize = sysconf(_SC_PAGE_SIZE);
-
-TEST(linker_memory, test_alloc_0) {
- LinkerMemoryAllocator allocator;
- void* ptr = allocator.alloc(0);
- ASSERT_TRUE(ptr != nullptr);
- allocator.free(ptr);
-}
-
-TEST(linker_memory, test_free_nullptr) {
- LinkerMemoryAllocator allocator;
- allocator.free(nullptr);
-}
-
-TEST(linker_memory, test_realloc) {
- LinkerMemoryAllocator allocator;
- uint32_t* array = reinterpret_cast<uint32_t*>(allocator.alloc(512));
- const size_t array_size = 512 / sizeof(uint32_t);
-
- uint32_t model[1000];
-
- model[0] = 1;
- model[1] = 1;
-
- for (size_t i = 2; i < 1000; ++i) {
- model[i] = model[i - 1] + model[i - 2];
- }
-
- memcpy(array, model, array_size);
-
- uint32_t* reallocated_ptr = reinterpret_cast<uint32_t*>(allocator.realloc(array, 1024));
-
- ASSERT_TRUE(reallocated_ptr != nullptr);
- ASSERT_TRUE(reallocated_ptr != array);
-
- ASSERT_TRUE(memcmp(reallocated_ptr, model, array_size) == 0);
-
- array = reallocated_ptr;
-
- memcpy(array, model, 2*array_size);
-
- reallocated_ptr = reinterpret_cast<uint32_t*>(allocator.realloc(array, 62));
-
- ASSERT_TRUE(reallocated_ptr == array);
-
- reallocated_ptr = reinterpret_cast<uint32_t*>(allocator.realloc(array, 4000));
-
- ASSERT_TRUE(reallocated_ptr != nullptr);
- ASSERT_TRUE(reallocated_ptr != array);
- ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(reallocated_ptr) % 16);
-
- ASSERT_TRUE(memcmp(reallocated_ptr, model, array_size * 2) == 0);
-
- array = reallocated_ptr;
-
- memcpy(array, model, 4000);
-
- reallocated_ptr = reinterpret_cast<uint32_t*>(allocator.realloc(array, 64000));
-
- ASSERT_TRUE(reallocated_ptr != nullptr);
- ASSERT_TRUE(reallocated_ptr != array);
- ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(reallocated_ptr) % 16);
-
- ASSERT_TRUE(memcmp(reallocated_ptr, model, 4000) == 0);
-
- ASSERT_EQ(nullptr, allocator.realloc(reallocated_ptr, 0));
-}
-
-TEST(linker_memory, test_small_smoke) {
- LinkerMemoryAllocator allocator;
-
- uint8_t zeros[16];
- memset(zeros, 0, sizeof(zeros));
-
- test_struct_small* ptr1 =
- reinterpret_cast<test_struct_small*>(allocator.alloc(sizeof(test_struct_small)));
- test_struct_small* ptr2 =
- reinterpret_cast<test_struct_small*>(allocator.alloc(sizeof(test_struct_small)));
-
- ASSERT_TRUE(ptr1 != nullptr);
- ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr1) % 16);
- ASSERT_TRUE(ptr2 != nullptr);
- ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr2) % 16);
-
- ASSERT_EQ(reinterpret_cast<uintptr_t>(ptr1)+16, reinterpret_cast<uintptr_t>(ptr2));
- ASSERT_TRUE(memcmp(ptr1, zeros, 16) == 0);
-
- allocator.free(ptr1);
- allocator.free(ptr2);
-}
-
-TEST(linker_memory, test_huge_smoke) {
- LinkerMemoryAllocator allocator;
-
- // this should trigger proxy-to-mmap
- test_struct_huge* ptr1 =
- reinterpret_cast<test_struct_huge*>(allocator.alloc(sizeof(test_struct_huge)));
- test_struct_huge* ptr2 =
- reinterpret_cast<test_struct_huge*>(allocator.alloc(sizeof(test_struct_huge)));
-
- ASSERT_TRUE(ptr1 != nullptr);
- ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr1) % 16);
- ASSERT_TRUE(ptr2 != nullptr);
- ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr2) % 16);
-
- ASSERT_TRUE(
- reinterpret_cast<uintptr_t>(ptr1)/kPageSize != reinterpret_cast<uintptr_t>(ptr2)/kPageSize);
- allocator.free(ptr2);
- allocator.free(ptr1);
-}
-
-TEST(linker_memory, test_large) {
- LinkerMemoryAllocator allocator;
-
- test_struct_large* ptr1 =
- reinterpret_cast<test_struct_large*>(allocator.alloc(sizeof(test_struct_large)));
- test_struct_large* ptr2 =
- reinterpret_cast<test_struct_large*>(allocator.alloc(1024));
-
- ASSERT_TRUE(ptr1 != nullptr);
- ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr1) % 16);
- ASSERT_TRUE(ptr2 != nullptr);
- ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr2) % 16);
-
- ASSERT_EQ(reinterpret_cast<uintptr_t>(ptr1) + 1024, reinterpret_cast<uintptr_t>(ptr2));
-
- // let's allocate until we reach the next page.
- size_t n = kPageSize / sizeof(test_struct_large) + 1 - 2;
- test_struct_large* objects[n];
-
- for (size_t i = 0; i < n; ++i) {
- test_struct_large* obj_ptr =
- reinterpret_cast<test_struct_large*>(allocator.alloc(sizeof(test_struct_large)));
- ASSERT_TRUE(obj_ptr != nullptr);
- objects[i] = obj_ptr;
- }
-
- test_struct_large* ptr_to_free =
- reinterpret_cast<test_struct_large*>(allocator.alloc(sizeof(test_struct_large)));
-
- ASSERT_TRUE(ptr_to_free != nullptr);
- ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr_to_free) % 16);
-
- allocator.free(ptr1);
-
- for (size_t i=0; i<n; ++i) {
- allocator.free(objects[i]);
- }
-
- allocator.free(ptr2);
- allocator.free(ptr_to_free);
-}
-
-