Build fix in foreign_object.hpp Wrong assert when a module contains more than one class, dynafunc_maker.cpp
178 lines
5.9 KiB
C++
178 lines
5.9 KiB
C++
/* Copyright 2020-2022, Michele Santullo
|
|
* This file is part of wrenpp.
|
|
*
|
|
* Wrenpp is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Wrenpp is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with wrenpp. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#if !defined(_GNU_SOURCE)
|
|
//for sys/mman.h
|
|
# define _GNU_SOURCE
|
|
#endif
|
|
|
|
#include "dynafunc_maker.hpp"
|
|
#include "pvt_config.h"
|
|
#include "wrenpp/detail/strings_in_vector.hpp"
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <unistd.h> //for sysconf()
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <cstdlib> //for aligned_alloc()
|
|
#include <sys/mman.h> //for mprotect()
|
|
#include <memory>
|
|
#include <new>
|
|
#include <string_view>
|
|
#include <type_traits>
|
|
|
|
namespace wren {
|
|
namespace {
|
|
struct MemFree { void operator()(void* ptr) { std::free(ptr); }};
|
|
typedef std::unique_ptr<void, MemFree> unique_ptr_free_t;
|
|
|
|
//see src/dynafunc_amd64_gnu.asm
|
|
extern "C" const char g_dynafunc[];
|
|
extern "C" const char g_dynafunc_end[];
|
|
const unsigned int g_dynafunc_len = g_dynafunc_end - g_dynafunc;
|
|
|
|
constexpr unsigned int g_dynafunc_ptr1_size = ASM_PTR_SIZE;
|
|
constexpr unsigned int g_dynafunc_ptr2_size = ASM_FUNC_PTR_SIZE;
|
|
//For inspiration: https://www.liquisearch.com/deadbeef/magic_debug_values
|
|
//0xdeadbeefdeadbeef
|
|
constexpr unsigned char g_dynafunc_ptr1[] = {0xef, 0xbe, 0xad, 0xde, 0xef, 0xbe, 0xad, 0xde};
|
|
//0xbadc0ffee0ddf00d
|
|
constexpr unsigned char g_dynafunc_ptr2[] = {0x0d, 0xf0, 0xdd, 0xe0, 0xfe, 0x0f, 0xdc, 0xba};
|
|
//0x1badb0021badb002
|
|
constexpr unsigned char g_dynafunc_ptr3[] = {0x02, 0xb0, 0xad, 0x1b, 0x02, 0xb0, 0xad, 0x1b};
|
|
//0xfee1deadfee1dead
|
|
constexpr unsigned char g_dynafunc_ptr4[] = {0xad, 0xde, 0xe1, 0xfe, 0xad, 0xde, 0xe1, 0xfe};
|
|
|
|
static_assert(sizeof(g_dynafunc_ptr1) / sizeof(g_dynafunc_ptr1[0]) >= g_dynafunc_ptr1_size);
|
|
static_assert(sizeof(g_dynafunc_ptr2) / sizeof(g_dynafunc_ptr2[0]) >= g_dynafunc_ptr2_size);
|
|
|
|
template <typename T, unsigned int S>
|
|
void replace_ptr(
|
|
unsigned char* beg,
|
|
unsigned char* end,
|
|
const unsigned char (&search)[S],
|
|
T replace
|
|
) {
|
|
static_assert(sizeof(T) <= S, "Unexpected pointer size");
|
|
const auto subseq = std::search(beg, end, search, search + sizeof(T));
|
|
assert(subseq != end);
|
|
const auto ptr = reinterpret_cast<unsigned char*>(&replace);
|
|
std::copy(ptr, ptr + sizeof(T), subseq);
|
|
}
|
|
|
|
void make_dynafunc_asm(
|
|
unsigned char* out,
|
|
VM* ptr1,
|
|
foreign_method_t ptr2,
|
|
RawAccessModuleAndName mn
|
|
) {
|
|
static_assert(std::is_standard_layout<ModuleAndName>::value,
|
|
"Must be a standard layout to be compliant with the assembly functions");
|
|
static_assert(sizeof(void*) * 2 == sizeof(ModuleAndName),
|
|
"Must fit into 2 registers to be compliant with the assembly functions");
|
|
|
|
std::copy(g_dynafunc, g_dynafunc_end, out);
|
|
|
|
replace_ptr(out, out + g_dynafunc_len, g_dynafunc_ptr1, ptr1);
|
|
replace_ptr(out, out + g_dynafunc_len, g_dynafunc_ptr2, ptr2);
|
|
replace_ptr(out, out + g_dynafunc_len, g_dynafunc_ptr3, mn.raw.string);
|
|
replace_ptr(out, out + g_dynafunc_len, g_dynafunc_ptr4, mn.raw.data);
|
|
}
|
|
|
|
ModuleAndName store_module_name (
|
|
std::list<std::vector<char>>& dst,
|
|
std::string_view mod_name,
|
|
std::string_view cls_name
|
|
) {
|
|
typedef std::vector<char> CharVec;
|
|
CharVec new_entry = strings_to_vector(mod_name, cls_name);
|
|
|
|
const auto it_found = std::lower_bound(
|
|
dst.cbegin(),
|
|
dst.cend(),
|
|
new_entry,
|
|
[](const CharVec& a, const CharVec& b) {
|
|
return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end());
|
|
}
|
|
);
|
|
|
|
const CharVec* source_mem;
|
|
if (dst.cend() != it_found && std::equal(it_found->cbegin(), it_found->cend(), new_entry.cbegin(), new_entry.cend())) {
|
|
source_mem = &*it_found;
|
|
}
|
|
else {
|
|
source_mem = &*dst.insert(it_found, std::move(new_entry));
|
|
}
|
|
|
|
return {source_mem->data(), mod_name.size(), cls_name.size()};
|
|
}
|
|
} //unnamed namespace
|
|
|
|
DynafuncMaker::DynafuncMaker() :
|
|
m_page_size(sysconf(_SC_PAGE_SIZE)),
|
|
m_used_bytes(m_page_size)
|
|
{
|
|
if (m_page_size <= 0)
|
|
throw std::runtime_error("Invalid page size " + std::to_string(m_page_size));
|
|
if (m_page_size < g_dynafunc_len)
|
|
throw std::runtime_error("Page size " + std::to_string(m_page_size) + " is too small to accomodate assembly the whose size is " + std::to_string(g_dynafunc_len));
|
|
assert(m_page_size == m_used_bytes);
|
|
}
|
|
|
|
DynafuncMaker::~DynafuncMaker() noexcept {
|
|
clear();
|
|
}
|
|
|
|
void* DynafuncMaker::make (VM* vm, foreign_method_t cb, const char* mod_name, const char* cls_name) {
|
|
unsigned char* const mem = allocate_chunk();
|
|
const auto mn = store_module_name(m_string_params, mod_name, cls_name);
|
|
make_dynafunc_asm(mem, vm, cb, mn);
|
|
|
|
return mem;
|
|
}
|
|
|
|
void DynafuncMaker::clear() noexcept {
|
|
for (auto page : m_pages) {
|
|
std::free(page);
|
|
}
|
|
m_pages.clear();
|
|
m_used_bytes = m_page_size;
|
|
}
|
|
|
|
std::size_t DynafuncMaker::total_memory() const {
|
|
return m_pages.size() * m_page_size +
|
|
m_pages.capacity() * sizeof(decltype(m_pages)::value_type) +
|
|
0;
|
|
}
|
|
|
|
unsigned char* DynafuncMaker::allocate_chunk() {
|
|
if (m_page_size - m_used_bytes < g_dynafunc_len) {
|
|
unique_ptr_free_t page(std::aligned_alloc(m_page_size, m_page_size));
|
|
if (not page)
|
|
throw std::bad_alloc();
|
|
mprotect(page.get(), m_page_size, PROT_EXEC | PROT_READ | PROT_WRITE);
|
|
m_pages.push_back(page.get());
|
|
page.release();
|
|
m_used_bytes = 0;
|
|
}
|
|
|
|
assert(not m_pages.empty());
|
|
const auto ret = static_cast<unsigned char*>(m_pages.back()) + m_used_bytes;
|
|
m_used_bytes += g_dynafunc_len;
|
|
return ret;
|
|
}
|
|
} //namespace wren
|