126 lines
4.1 KiB
C++
126 lines
4.1 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 "config.h"
|
|
#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>
|
|
|
|
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;
|
|
constexpr unsigned char g_dynafunc_ptr1[] = {0xef, 0xbe, 0xad, 0xde, 0xef, 0xbe, 0xad, 0xde};
|
|
constexpr unsigned char g_dynafunc_ptr2[] = {0x0d, 0xf0, 0xdd, 0xe0, 0xfe, 0x0f, 0xdc, 0xba};
|
|
|
|
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) {
|
|
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);
|
|
}
|
|
} //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) {
|
|
unsigned char* const mem = allocate_chunk();
|
|
make_dynafunc_asm(mem, vm, cb);
|
|
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
|