You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
159 lines
6.2 KiB
159 lines
6.2 KiB
|
|
|
|
#include <map>
|
|
|
|
#include <cstring>
|
|
|
|
|
|
#include <alloc.h>
|
|
|
|
#include <util.hh>
|
|
#include <types.hh>
|
|
#include <macros.h>
|
|
|
|
template<typename T = int>
|
|
using type_hash_t = decltype(types::type_hash<int>());
|
|
|
|
template<typename T = int>
|
|
using type_hash_ref = std::remove_reference_t<type_hash_t<T>> const&;
|
|
|
|
template<typename T = int>
|
|
using type_hash_ptr = std::remove_reference_t<type_hash_t<T>> const*;
|
|
|
|
extern "C" {
|
|
base_allocator::~base_allocator() {}
|
|
}
|
|
|
|
namespace alloc {
|
|
|
|
// Base class for all deallocations that happen within an `anon_raw_secmem` (managed, static, polymorphic, unmanaged, etc.) (TODO: See below, the base should be moved into the header so typed children can be templated...)
|
|
FrozenAllocator::deleter::~deleter() {
|
|
//apply_finalizer_group(); // XXX: Do we actually need finalizer groups now? (see note below about `m_values`.
|
|
|
|
}
|
|
FrozenAllocator::deleter::deleter(std::shared_ptr<anon_raw_secmem>&& p)
|
|
: m_manager_ref(std::move(p)) {}
|
|
// , m_gorup_ptr(static_cast<deleter*>(this)) {}
|
|
|
|
|
|
void scramble_memory(std::initializer_list<std::pair<void*>> ptrs) const noexcept
|
|
{
|
|
for(auto [ps, pe] : ptrs) {
|
|
// ps: start of range, pe: end of range (or nullptr, if range must be looked up in `m_manager_ref`.)
|
|
if(UNLIKELY(!pe)) {
|
|
if(const auto& alloc = m_manager_ref->lookup_alloc(ps))
|
|
pe = alloc.range().second;
|
|
else continue;
|
|
}
|
|
else if(UNLIKELY(ps == pe)) continue;
|
|
|
|
intptr_t psa = std::to_address(ps),
|
|
pea = std::to_address(pe);
|
|
ASSUME(pea > psa);
|
|
explicit_bzero(ps, util::ptr_diff(psa, pea));
|
|
}
|
|
}
|
|
|
|
[[gnu::nonnull(2)]]
|
|
std::pair<void*> FrozenAllocator::deleter::apply_delete(void* restrict p, bool clean) const noexcept {
|
|
//(XXX: NOTE: `m_values` map removal *causes* this to be invoked, i.e: A value removed from the map calls `deleter::operator()(uniq_p)`, which then calls `apply_delete(uniq_p, true)`
|
|
|
|
// Lookup the allocation info for pointer `p`.
|
|
const auto& alloc = m_manager_ref->lookup_alloc(p);
|
|
if(UNLIKELY(!alloc)) return {p, p};
|
|
|
|
// TODO: allow the allocation (the memory corresponding to `p` from `m_manager_ref`) to be mutable (`munlock()` it.)
|
|
|
|
// Get the full range (including alignment padding)
|
|
auto al_range = alloc.range();
|
|
// Then, if `clean == true` (default), `bzero_explicit()` the memory (range for `p` obtained from `m_manager_ref`.)
|
|
if(LIKELY(clean)) scramble_memory(al_range);
|
|
|
|
// Return the range (or the end pointer of `p`'s range for use in / as an iterator.)
|
|
return al_range;
|
|
}
|
|
// This is the *true manager* of the allocation arena, when it is destroyed, the memory is cleared
|
|
struct FrozenAllocator::anon_raw_secmem final
|
|
: virtual id::unique
|
|
{
|
|
// Deleter that works on `alloc_value`s directly
|
|
struct deleter final : public deleter_for<alloc_value> {
|
|
//TODO: This ^
|
|
private:
|
|
void apply_delete_typed(alloc_value* ptr) const noexcept override {
|
|
//TODO: Destroy the typed object inside `ptr`. (XXX: Where should destructor info be kept? In `alloc_value` or `alloc_info`? I think `alloc_value`.
|
|
|
|
// Run `alloc_value` itself's dtor now.
|
|
deleter_for<alloc_value>::apply_delete_typed(ptr);
|
|
}
|
|
};
|
|
|
|
virtual ~anon_raw_secmem() {
|
|
//TODO: Clear and `munmap()` the used page(s)
|
|
//XXX: Due to how this is managed via `shared_ptr<>`, it is UB for this to be called *before* all actual allocation of the memory are unallocated (via `deleter`, which they must *all* be managed by.
|
|
}
|
|
};
|
|
|
|
struct FrozenAllocator::alloc_info {
|
|
id::uuid alloc_id; // ID of this allocation
|
|
id::unique_ref owner; // Reference to the unique ID of the `anon_raw_secmem` that manages this allocation.
|
|
|
|
type_hash_ptr type; // Static pointer to the `types::type_hash<T>()` result of the `T` that this allocation is. (Basic RTTI: `type_hash_t` should only exist in static storage, otherwise we use `type_hash_ref` or `type_hash_ptr`.)
|
|
|
|
struct {
|
|
size_t size, align;
|
|
} meta;
|
|
|
|
void* area_start;
|
|
void* area;
|
|
};
|
|
struct FrozenAllocator::alloc_value {
|
|
typedef bool (*vt_ctor)(alloc_value* to, ...);
|
|
typedef bool (*vt_copy)(const alloc_value& from, alloc_value* to);
|
|
typedef bool (*vt_move)(alloc_value&& from, alloc_value* to);
|
|
typedef bool (*vt_assign_copy)(const alloc_value& from, alloc_value* to);
|
|
typedef bool (*vt_assign_move)(alloc_value&& from, alloc_value* to);
|
|
typedef bool (*vt_destroy)(alloc_value* obj);
|
|
typedef void* (*vt_this)(const alloc_value& from);
|
|
typedef bool (*vt_cast_into)(const alloc_value& a, alloc_value* b);
|
|
/// vt_type_info: if info not null: return if `a` is type referred to in `info`, else if `type` not null: set `*type` to be type of `a`, return false if that is not possible with the other arguments given.
|
|
typedef bool (*vt_type_info)(const alloc_value& a, const alloc_info* info, type_hash_ptr *restrict type);
|
|
|
|
//TODO: How to create? Overloaded operator placement new, inside `alloc_info` or `anon_raw_secmem`? Since the storage for these are allocated and managed *by* `anon_raw_secmem`, that would make the most sense I think... `alloc_info` holds the pointer to the specific allocation, its ID, etc; stuff for ordered allocation lookup. This is managed (entirely) by `anon_raw_secmem`, and `std::unique_ptr<alloc_value, anon_raw_secmem::deleter>` ensures it is not deleted naturally, but only removed from `anon_raw_secmem`.
|
|
|
|
//! Basic RTTI impl that holds type-erased, alignment padded values and gives out aligned void* pointers to it.
|
|
//! NOTE: This class does *not* apply any destructor when destroyed, `anon_raw_secmem::deleter` should be used for that.
|
|
struct {
|
|
struct {
|
|
vt_ctor _create;
|
|
vt_copy _copy;
|
|
vt_move _move;
|
|
vt_destroy _destroy;
|
|
vt_this _this;
|
|
vt_type_info _typeinfo;
|
|
// We don't need the others, they can be constructed by combining calls to these. (e.g.: assign_copy(new, old) = `destroy(old), copy(new, old)`.)
|
|
} vt;
|
|
size_t size, align;
|
|
} meta;
|
|
|
|
unsigned char data[];
|
|
};
|
|
|
|
struct FrozenAllocator::_impl {
|
|
std::shared_ptr<anon_raw_secmem> m_manager;
|
|
std::map<alloc_info, std::unique_ptr<alloc_value, anon_raw_secmem::deleter>> m_values;
|
|
};
|
|
|
|
#define $CLASS FrozenAllocator
|
|
$ctor_move(m) noexcept
|
|
: inner_(std::move(m.inner_)) {}
|
|
$assign_move(m) {
|
|
if($LIKELY(this != &m))
|
|
inner_ = std::move(m.inner_);
|
|
return *this;
|
|
}
|
|
$dtor() {}
|
|
#undef $CLASS /* FrozenAllocator */
|
|
|
|
}
|