mirror of https://github.com/status-im/evmc.git
Merge pull request #40 from ethereum/loader
Add evmc::loader library to support dynamic loading
This commit is contained in:
commit
4ae766069c
|
@ -25,5 +25,6 @@ build_script: |
|
|||
cmake --build . --config %CONFIGURATION% --target install
|
||||
|
||||
after_build: |
|
||||
C:\projects\evmc\build\test\Release\evmc-test.exe
|
||||
cd C:\projects\evmc\build\test
|
||||
Release\evmc-test.exe
|
||||
C:\install\bin\evmc-vmtester.exe C:\install\bin\evmc-examplevm.dll
|
||||
|
|
|
@ -15,7 +15,8 @@ jobs:
|
|||
command: cmake --build ~/build
|
||||
- run:
|
||||
name: "Unit tests"
|
||||
command: ~/build/test/evmc-test
|
||||
working_directory: ~/build/test
|
||||
command: ./evmc-test
|
||||
- run:
|
||||
name: "Test"
|
||||
command: cmake --build ~/build --target test
|
||||
|
|
|
@ -6,6 +6,6 @@
|
|||
# Hunter is going to be initialized only if building with tests,
|
||||
# where it is needed to get dependencies.
|
||||
HunterGate(
|
||||
URL "https://github.com/ruslo/hunter/archive/v0.20.37.tar.gz"
|
||||
SHA1 "51886d10428c924cc21756abc17623bcf4986386"
|
||||
URL "https://github.com/ruslo/hunter/archive/v0.22.23.tar.gz"
|
||||
SHA1 "16c562a69489ff9c1b5266a12d5e903084de693a"
|
||||
)
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/* EVMC: Ethereum Client-VM Connector API.
|
||||
* Copyright 2018 Pawel Bylica.
|
||||
* Licensed under the MIT License. See the LICENSE file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** The function pointer type for EVMC create functions. */
|
||||
typedef struct evmc_instance* (*evmc_create_fn)(void);
|
||||
|
||||
/** Error codes for the EVMC loader. */
|
||||
enum evmc_loader_error_code
|
||||
{
|
||||
EVMC_LOADER_SUCCESS = 0,
|
||||
EVMC_LOADER_CANNOT_OPEN,
|
||||
EVMC_LOADER_SYMBOL_NOT_FOUND,
|
||||
EVMC_LOADER_INVALID_ARGUMENT,
|
||||
};
|
||||
|
||||
/**
|
||||
* Dynamically loads the shared object (DLL) with an EVM implementation.
|
||||
*
|
||||
* This function tries to open a DLL at the given `filename`. On UNIX-like systems dlopen() function
|
||||
* is used. On Windows LoadLibrary() function is used.
|
||||
*
|
||||
* If the file does not exist or is not a valid shared library the ::EVMC_ERRC_CANNOT_OPEN error
|
||||
* code is signaled and NULL is returned.
|
||||
*
|
||||
* After the DLL is successfully loaded the function tries to find the EVM create function in the
|
||||
* library. The `filename` is used to guess the EVM name and the name of the create function.
|
||||
* The create function name is constructed by the following rules. Consider example path:
|
||||
* "/ethereum/libexample-interpreter.so".
|
||||
* - the filename is taken from the path:
|
||||
* "libexample-interpreter.so",
|
||||
* - the "lib" prefix and file extension are stripped from the name:
|
||||
* "example-interpreter"
|
||||
* - all "-" are replaced with "_" to construct _full name_:
|
||||
* "example_interpreter",
|
||||
* - the _full name_ is split by "_" char and the last item is taken to form the _short name_:
|
||||
* "interpreter",
|
||||
* - the name "evmc_create_" + _full name_ is checked in the library:
|
||||
* "evmc_create_example_interpreter",
|
||||
* - the name "evmc_create_" + _short name_ is checked in the library:
|
||||
* "evmc_create_interpreter".
|
||||
*
|
||||
* If the create function is found in the library, the pointer to the function is returned.
|
||||
* Otherwise, the ::EVMC_ERRC_SYMBOL_NOT_FOUND error code is signaled and NULL is returned.
|
||||
*
|
||||
* @param filename The null terminated path (absolute or relative) to the shared library
|
||||
* containing the EVM implementation. If the value is NULL, an empty C-string
|
||||
* or longer than the path maximum length the ::EVMC_ERRC_INVALID_ARGUMENT is
|
||||
* signaled.
|
||||
* @param error_code The pointer to the error code. If not NULL the value is set to
|
||||
* ::EVMC_ERRC_SUCCESS on success or any other error code as described above.
|
||||
* @return The pointer to the EVM create function or NULL.
|
||||
*/
|
||||
evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* error_code);
|
||||
|
||||
#if __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -3,3 +3,4 @@
|
|||
# Licensed under the MIT License. See the LICENSE file.
|
||||
|
||||
add_subdirectory(instructions)
|
||||
add_subdirectory(loader)
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
# EVMC: Ethereum Client-VM Connector API.
|
||||
# Copyright 2018 Pawel Bylica.
|
||||
# Licensed under the MIT License. See the LICENSE file.
|
||||
|
||||
add_library(
|
||||
loader STATIC
|
||||
${include_dir}/evmc/loader.h
|
||||
loader.c
|
||||
)
|
||||
|
||||
add_library(evmc::loader ALIAS loader)
|
||||
set_target_properties(loader PROPERTIES OUTPUT_NAME evmc-loader)
|
||||
target_include_directories(loader PUBLIC $<BUILD_INTERFACE:${include_dir}>$<INSTALL_INTERFACE:include>)
|
||||
target_link_libraries(loader INTERFACE ${CMAKE_DL_LIBS})
|
||||
|
||||
install(TARGETS loader EXPORT evmcTargets DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
|
@ -0,0 +1,123 @@
|
|||
/* EVMC: Ethereum Client-VM Connector API.
|
||||
* Copyright 2018 Pawel Bylica.
|
||||
* Licensed under the MIT License. See the LICENSE file.
|
||||
*/
|
||||
|
||||
#include <evmc/loader.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#if _WIN32
|
||||
#include <Windows.h>
|
||||
#define DLL_HANDLE HMODULE
|
||||
#define DLL_OPEN(filename) LoadLibrary(filename)
|
||||
#define DLL_CLOSE(handle) FreeLibrary(handle)
|
||||
#define DLL_GET_CREATE_FN(handle, name) (evmc_create_fn) GetProcAddress(handle, name)
|
||||
#define HAVE_STRCPY_S 1
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#define DLL_HANDLE void*
|
||||
#define DLL_OPEN(filename) dlopen(filename, RTLD_LAZY)
|
||||
#define DLL_CLOSE(handle) dlclose(handle)
|
||||
#define DLL_GET_CREATE_FN(handle, name) (evmc_create_fn)(uintptr_t) dlsym(handle, name)
|
||||
#define HAVE_STRCPY_S 0
|
||||
#endif
|
||||
|
||||
#define PATH_MAX_LENGTH 4096
|
||||
|
||||
#if !HAVE_STRCPY_S
|
||||
static void strcpy_s(char* dest, size_t destsz, const char* src)
|
||||
{
|
||||
size_t len = strlen(src);
|
||||
if (len > destsz - 1)
|
||||
len = destsz - 1;
|
||||
memcpy(dest, src, len);
|
||||
dest[len] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* error_code)
|
||||
{
|
||||
enum evmc_loader_error_code ec = EVMC_LOADER_SUCCESS;
|
||||
evmc_create_fn create_fn = NULL;
|
||||
|
||||
if (!filename)
|
||||
{
|
||||
ec = EVMC_LOADER_INVALID_ARGUMENT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
const size_t length = strlen(filename);
|
||||
if (length == 0 || length > PATH_MAX_LENGTH)
|
||||
{
|
||||
ec = EVMC_LOADER_INVALID_ARGUMENT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
DLL_HANDLE handle = DLL_OPEN(filename);
|
||||
if (!handle)
|
||||
{
|
||||
ec = EVMC_LOADER_CANNOT_OPEN;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Create name buffer with the prefix.
|
||||
const char prefix[] = "evmc_create_";
|
||||
const size_t prefix_length = strlen(prefix);
|
||||
char name[sizeof(prefix) + PATH_MAX_LENGTH];
|
||||
strcpy_s(name, sizeof(name), prefix);
|
||||
|
||||
// Find filename in the path.
|
||||
const char* sep_pos = strrchr(filename, '/');
|
||||
#if _WIN32
|
||||
// On Windows check also Windows classic path separator.
|
||||
const char* sep_pos_windows = strrchr(filename, '\\');
|
||||
sep_pos = sep_pos_windows > sep_pos ? sep_pos_windows : sep_pos;
|
||||
#endif
|
||||
const char* name_pos = sep_pos ? sep_pos + 1 : filename;
|
||||
|
||||
// Skip "lib" prefix if present.
|
||||
const char lib_prefix[] = "lib";
|
||||
const size_t lib_prefix_length = strlen(lib_prefix);
|
||||
if (strncmp(name_pos, lib_prefix, lib_prefix_length) == 0)
|
||||
name_pos += lib_prefix_length;
|
||||
|
||||
strcpy_s(name + prefix_length, PATH_MAX_LENGTH, name_pos);
|
||||
|
||||
// Trim the file extension.
|
||||
char* ext_pos = strrchr(name, '.');
|
||||
if (ext_pos)
|
||||
*ext_pos = 0;
|
||||
|
||||
// Replace all "-" with "_".
|
||||
char* dash_pos = name;
|
||||
while ((dash_pos = strchr(dash_pos, '-')) != NULL)
|
||||
*dash_pos++ = '_';
|
||||
|
||||
// Search for the "full name" based function name.
|
||||
create_fn = DLL_GET_CREATE_FN(handle, name);
|
||||
if (!create_fn)
|
||||
{
|
||||
// Try the "short name" based function name.
|
||||
const char* short_name_pos = strrchr(name, '_');
|
||||
if (short_name_pos)
|
||||
{
|
||||
short_name_pos += 1;
|
||||
memmove(name + prefix_length, short_name_pos, strlen(short_name_pos) + 1);
|
||||
create_fn = DLL_GET_CREATE_FN(handle, name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!create_fn)
|
||||
{
|
||||
DLL_CLOSE(handle);
|
||||
ec = EVMC_LOADER_SYMBOL_NOT_FOUND;
|
||||
}
|
||||
|
||||
exit:
|
||||
if (error_code)
|
||||
*error_code = ec;
|
||||
return create_fn;
|
||||
}
|
|
@ -2,10 +2,37 @@
|
|||
# Copyright 2018 Pawel Bylica.
|
||||
# Licensed under the MIT License. See the LICENSE file.
|
||||
|
||||
add_library(vm-mock SHARED vm_mock.c)
|
||||
target_link_libraries(vm-mock PRIVATE evmc)
|
||||
|
||||
if(UNIX)
|
||||
set(cmd create_symlink)
|
||||
else()
|
||||
set(cmd copy)
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
TARGET vm-mock POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E ${cmd} $<TARGET_FILE:vm-mock> libaaa.so
|
||||
COMMAND ${CMAKE_COMMAND} -E ${cmd} $<TARGET_FILE:vm-mock> double_prefix_aaa.evm
|
||||
COMMAND ${CMAKE_COMMAND} -E ${cmd} $<TARGET_FILE:vm-mock> double-prefix-aaa.evm
|
||||
COMMAND ${CMAKE_COMMAND} -E ${cmd} $<TARGET_FILE:vm-mock> eee-bbb.dll
|
||||
COMMAND ${CMAKE_COMMAND} -E ${cmd} $<TARGET_FILE:vm-mock> libeee1.so
|
||||
COMMAND ${CMAKE_COMMAND} -E ${cmd} $<TARGET_FILE:vm-mock> eee2.so
|
||||
COMMAND ${CMAKE_COMMAND} -E ${cmd} $<TARGET_FILE:vm-mock> libeee3.x
|
||||
COMMAND ${CMAKE_COMMAND} -E ${cmd} $<TARGET_FILE:vm-mock> eee4
|
||||
COMMAND ${CMAKE_COMMAND} -E ${cmd} $<TARGET_FILE:vm-mock> _
|
||||
COMMAND ${CMAKE_COMMAND} -E ${cmd} $<TARGET_FILE:vm-mock> lib_.so
|
||||
COMMAND ${CMAKE_COMMAND} -E ${cmd} $<TARGET_FILE:vm-mock> ../aaa.evm
|
||||
COMMAND ${CMAKE_COMMAND} -E touch empty.file
|
||||
)
|
||||
|
||||
add_executable(
|
||||
evmc-test
|
||||
test_instructions.cpp
|
||||
test_loader.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(evmc-test PRIVATE instructions GTest::gtest GTest::main)
|
||||
target_link_libraries(evmc-test PRIVATE instructions loader GTest::gtest GTest::main)
|
||||
set_target_properties(evmc-test PROPERTIES RUNTIME_OUTPUT_DIRECTORY ..)
|
||||
add_dependencies(evmc-test vm-mock)
|
||||
|
|
|
@ -0,0 +1,246 @@
|
|||
// EVMC: Ethereum Client-VM Connector API.
|
||||
// Copyright 2018 Pawel Bylica.
|
||||
// Licensed under the MIT License. See the LICENSE file.
|
||||
|
||||
#include <evmc/loader.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#if _WIN32
|
||||
static constexpr bool is_windows = true;
|
||||
#else
|
||||
static constexpr bool is_windows = false;
|
||||
#endif
|
||||
|
||||
TEST(loader, nonexistent)
|
||||
{
|
||||
evmc_loader_error_code ec;
|
||||
auto x = evmc_load("nonexistent", &ec);
|
||||
EXPECT_EQ(ec, EVMC_LOADER_CANNOT_OPEN);
|
||||
EXPECT_EQ(x, nullptr);
|
||||
|
||||
x = evmc_load("nonexistent", nullptr);
|
||||
EXPECT_EQ(x, nullptr);
|
||||
}
|
||||
|
||||
TEST(loader, longpath)
|
||||
{
|
||||
std::vector<char> path(5000, 'a');
|
||||
*path.rbegin() = 0;
|
||||
|
||||
evmc_loader_error_code ec;
|
||||
auto x = evmc_load(path.data(), &ec);
|
||||
EXPECT_EQ(ec, EVMC_LOADER_INVALID_ARGUMENT);
|
||||
EXPECT_EQ(x, nullptr);
|
||||
|
||||
x = evmc_load(path.data(), nullptr);
|
||||
EXPECT_EQ(x, nullptr);
|
||||
}
|
||||
|
||||
TEST(loader, not_so)
|
||||
{
|
||||
auto path = "unittests/empty.file";
|
||||
|
||||
evmc_loader_error_code ec;
|
||||
auto x = evmc_load(path, &ec);
|
||||
EXPECT_EQ(ec, EVMC_LOADER_CANNOT_OPEN);
|
||||
EXPECT_EQ(x, nullptr);
|
||||
|
||||
x = evmc_load(path, nullptr);
|
||||
EXPECT_EQ(x, nullptr);
|
||||
}
|
||||
|
||||
TEST(loader, null_path)
|
||||
{
|
||||
evmc_loader_error_code ec;
|
||||
auto x = evmc_load(nullptr, &ec);
|
||||
EXPECT_EQ(ec, EVMC_LOADER_INVALID_ARGUMENT);
|
||||
EXPECT_EQ(x, nullptr);
|
||||
|
||||
x = evmc_load(nullptr, nullptr);
|
||||
EXPECT_EQ(x, nullptr);
|
||||
}
|
||||
|
||||
TEST(loader, empty_path)
|
||||
{
|
||||
evmc_loader_error_code ec;
|
||||
auto x = evmc_load("", &ec);
|
||||
EXPECT_EQ(ec, EVMC_LOADER_INVALID_ARGUMENT);
|
||||
EXPECT_EQ(x, nullptr);
|
||||
|
||||
x = evmc_load("", nullptr);
|
||||
EXPECT_EQ(x, nullptr);
|
||||
}
|
||||
|
||||
TEST(loader, aaa)
|
||||
{
|
||||
auto path = "unittests/libaaa.so";
|
||||
|
||||
evmc_loader_error_code ec;
|
||||
auto fn = evmc_load(path, &ec);
|
||||
ASSERT_NE(fn, nullptr);
|
||||
EXPECT_EQ(ec, EVMC_LOADER_SUCCESS);
|
||||
EXPECT_EQ((uintptr_t)fn(), 0xaaa);
|
||||
|
||||
fn = evmc_load(path, nullptr);
|
||||
ASSERT_NE(fn, nullptr);
|
||||
EXPECT_EQ((uintptr_t)fn(), 0xaaa);
|
||||
}
|
||||
|
||||
TEST(loader, prefix_aaa)
|
||||
{
|
||||
auto paths = {"unittests/double-prefix-aaa.evm", "unittests/double_prefix_aaa.evm"};
|
||||
|
||||
for (auto& path : paths)
|
||||
{
|
||||
evmc_loader_error_code ec;
|
||||
auto fn = evmc_load(path, &ec);
|
||||
ASSERT_NE(fn, nullptr);
|
||||
EXPECT_EQ(ec, EVMC_LOADER_SUCCESS);
|
||||
EXPECT_EQ((uintptr_t)fn(), 0xaaa);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(loader, eee_bbb)
|
||||
{
|
||||
auto path = "unittests/eee-bbb.dll";
|
||||
|
||||
evmc_loader_error_code ec;
|
||||
auto fn = evmc_load(path, &ec);
|
||||
ASSERT_NE(fn, nullptr);
|
||||
EXPECT_EQ(ec, EVMC_LOADER_SUCCESS);
|
||||
EXPECT_EQ((uintptr_t)fn(), 0xeeebbb);
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
TEST(loader, nextto)
|
||||
{
|
||||
// On Unix dlopen searches for system libs when the path does not contain "/".
|
||||
|
||||
auto path = "aaa.evm";
|
||||
|
||||
evmc_loader_error_code ec;
|
||||
auto fn = evmc_load(path, &ec);
|
||||
ASSERT_NE(fn, nullptr);
|
||||
EXPECT_EQ(ec, EVMC_LOADER_SUCCESS);
|
||||
EXPECT_EQ((uintptr_t)fn(), 0xaaa);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(loader, windows_path)
|
||||
{
|
||||
auto paths = {
|
||||
"./aaa.evm",
|
||||
".\\aaa.evm",
|
||||
"./unittests/eee-bbb.dll",
|
||||
"./unittests\\eee-bbb.dll",
|
||||
".\\unittests\\eee-bbb.dll",
|
||||
".\\unittests/eee-bbb.dll",
|
||||
"unittests\\eee-bbb.dll",
|
||||
};
|
||||
|
||||
for (auto& path : paths)
|
||||
{
|
||||
bool is_windows_path = std::strchr(path, '\\') != nullptr;
|
||||
|
||||
if (is_windows_path && !is_windows)
|
||||
{
|
||||
evmc_loader_error_code ec;
|
||||
auto fn = evmc_load(path, &ec);
|
||||
EXPECT_EQ(fn, nullptr);
|
||||
EXPECT_EQ(ec, EVMC_LOADER_CANNOT_OPEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
evmc_loader_error_code ec;
|
||||
auto fn = evmc_load(path, &ec);
|
||||
EXPECT_NE(fn, nullptr);
|
||||
EXPECT_EQ(ec, EVMC_LOADER_SUCCESS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(loader, eee1)
|
||||
{
|
||||
auto path = "unittests/libeee1.so";
|
||||
|
||||
evmc_loader_error_code ec;
|
||||
auto x = evmc_load(path, &ec);
|
||||
EXPECT_EQ(ec, EVMC_LOADER_SYMBOL_NOT_FOUND);
|
||||
EXPECT_EQ(x, nullptr);
|
||||
|
||||
x = evmc_load(path, nullptr);
|
||||
EXPECT_EQ(x, nullptr);
|
||||
}
|
||||
|
||||
TEST(loader, eee2)
|
||||
{
|
||||
auto path = "unittests/eee2.so";
|
||||
|
||||
evmc_loader_error_code ec;
|
||||
auto x = evmc_load(path, &ec);
|
||||
EXPECT_EQ(ec, EVMC_LOADER_SYMBOL_NOT_FOUND);
|
||||
EXPECT_EQ(x, nullptr);
|
||||
|
||||
x = evmc_load(path, nullptr);
|
||||
EXPECT_EQ(x, nullptr);
|
||||
}
|
||||
|
||||
TEST(loader, eee3)
|
||||
{
|
||||
auto path = "unittests/libeee3.x";
|
||||
|
||||
evmc_loader_error_code ec;
|
||||
auto x = evmc_load(path, &ec);
|
||||
EXPECT_EQ(ec, EVMC_LOADER_SYMBOL_NOT_FOUND);
|
||||
EXPECT_EQ(x, nullptr);
|
||||
|
||||
x = evmc_load(path, nullptr);
|
||||
EXPECT_EQ(x, nullptr);
|
||||
}
|
||||
|
||||
#if !_WIN32
|
||||
TEST(loader, eee4)
|
||||
{
|
||||
// Windows is not loading DLLs without extensions.
|
||||
auto path = "unittests/eee4";
|
||||
|
||||
evmc_loader_error_code ec;
|
||||
auto x = evmc_load(path, &ec);
|
||||
EXPECT_EQ(ec, EVMC_LOADER_SYMBOL_NOT_FOUND);
|
||||
EXPECT_EQ(x, nullptr);
|
||||
|
||||
x = evmc_load(path, nullptr);
|
||||
EXPECT_EQ(x, nullptr);
|
||||
}
|
||||
|
||||
TEST(loader, _)
|
||||
{
|
||||
// Windows is not loading DLLs without extensions.
|
||||
auto path = "unittests/_";
|
||||
|
||||
evmc_loader_error_code ec;
|
||||
auto x = evmc_load(path, &ec);
|
||||
EXPECT_EQ(ec, EVMC_LOADER_SYMBOL_NOT_FOUND);
|
||||
EXPECT_EQ(x, nullptr);
|
||||
|
||||
x = evmc_load(path, nullptr);
|
||||
EXPECT_EQ(x, nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(loader, lib_)
|
||||
{
|
||||
// Windows is not loading DLLs without extensions.
|
||||
auto path = "unittests/lib_.so";
|
||||
|
||||
evmc_loader_error_code ec;
|
||||
auto x = evmc_load(path, &ec);
|
||||
EXPECT_EQ(ec, EVMC_LOADER_SYMBOL_NOT_FOUND);
|
||||
EXPECT_EQ(x, nullptr);
|
||||
|
||||
x = evmc_load(path, nullptr);
|
||||
EXPECT_EQ(x, nullptr);
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
/* EVMC: Ethereum Client-VM Connector API.
|
||||
* Copyright 2018 Pawel Bylica.
|
||||
* Licensed under the MIT License. See the LICENSE file.
|
||||
*/
|
||||
|
||||
#include <evmc/utils.h>
|
||||
|
||||
EVMC_EXPORT void* evmc_create_aaa()
|
||||
{
|
||||
return (void*)0xaaa;
|
||||
}
|
||||
|
||||
EVMC_EXPORT void* evmc_create_eee_bbb()
|
||||
{
|
||||
return (void*)0xeeebbb;
|
||||
}
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
hunter_add_package(Boost COMPONENTS program_options filesystem system)
|
||||
find_package(Boost CONFIG REQUIRED program_options filesystem system)
|
||||
hunter_add_package(CLI11)
|
||||
find_package(CLI11)
|
||||
|
||||
add_executable(evmc-vmtester vmtester.hpp vmtester.cpp tests.cpp)
|
||||
set_target_properties(evmc-vmtester PROPERTIES RUNTIME_OUTPUT_DIRECTORY ..)
|
||||
|
@ -14,11 +14,9 @@ target_link_libraries(
|
|||
evmc-vmtester
|
||||
PRIVATE
|
||||
evmc
|
||||
loader
|
||||
GTest::gtest
|
||||
Boost::program_options
|
||||
Boost::filesystem
|
||||
Boost::system
|
||||
${CMAKE_DL_LIBS}
|
||||
CLI11::CLI11
|
||||
)
|
||||
|
||||
install(TARGETS evmc-vmtester RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
|
|
@ -4,23 +4,16 @@
|
|||
|
||||
#include "vmtester.hpp"
|
||||
|
||||
#include <boost/dll.hpp>
|
||||
#include <boost/dll/alias.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/program_options.hpp>
|
||||
#include <evmc/loader.h>
|
||||
|
||||
#include <CLI/CLI.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
namespace dll = boost::dll;
|
||||
namespace opts = boost::program_options;
|
||||
|
||||
extern "C" using evmc_create_fn = evmc_instance * ();
|
||||
|
||||
namespace
|
||||
{
|
||||
boost::function<evmc_create_fn> create_fn;
|
||||
evmc_create_fn create_fn;
|
||||
|
||||
std::unique_ptr<evmc_instance, evmc_destroy_fn> create_vm()
|
||||
{
|
||||
|
@ -35,96 +28,33 @@ evmc_instance* get_vm_instance()
|
|||
return vm.get();
|
||||
}
|
||||
|
||||
std::vector<std::string> get_vm_names(const fs::path path)
|
||||
{
|
||||
std::vector<std::string> names;
|
||||
|
||||
// Get the filename without extension.
|
||||
auto name = path.stem().string();
|
||||
|
||||
// Skip the optional library name prefix.
|
||||
const std::string lib_name_prefix{"lib"};
|
||||
if (name.find(lib_name_prefix) == 0)
|
||||
name = name.substr(lib_name_prefix.size());
|
||||
|
||||
size_t hyphen_pos = 0;
|
||||
const std::string hyphen{"-"};
|
||||
if ((hyphen_pos = name.find(hyphen)) != std::string::npos)
|
||||
{
|
||||
// Replace the hyphen with underscore.
|
||||
name.replace(hyphen_pos, hyphen.size(), "_");
|
||||
names.emplace_back(name);
|
||||
|
||||
// Also add the name without the hyphen-separated prefix.
|
||||
names.emplace_back(name.substr(hyphen_pos + hyphen.size()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add the filename as the name.
|
||||
names.emplace_back(std::move(name));
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
fs::path vm_path;
|
||||
|
||||
opts::options_description desc("EVMC VM Tester Options");
|
||||
auto add_option = desc.add_options();
|
||||
add_option("help", "Show help message");
|
||||
add_option("vm", opts::value(&vm_path)->value_name("path")->required(),
|
||||
"Path to the VM shared library to be tested");
|
||||
|
||||
opts::positional_options_description positional;
|
||||
positional.add("vm", 1);
|
||||
std::string vm_path;
|
||||
|
||||
CLI::App app{"EVMC VM Tester"};
|
||||
app.add_option("vm", vm_path, "Path to the VM shared library to be tested");
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
opts::variables_map variables_map;
|
||||
opts::store(
|
||||
opts::command_line_parser(argc, argv).options(desc).positional(positional).run(),
|
||||
variables_map);
|
||||
|
||||
if (variables_map.count("help"))
|
||||
std::cout << "Testing " << vm_path << "\n";
|
||||
evmc_loader_error_code ec;
|
||||
create_fn = evmc_load(vm_path.c_str(), &ec);
|
||||
switch (ec)
|
||||
{
|
||||
std::cout << "\n" << desc << "\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
opts::notify(variables_map);
|
||||
|
||||
std::cout << "Testing " << vm_path.filename().string() << "\n"
|
||||
<< "Path: " << vm_path.string() << "\n";
|
||||
|
||||
for (auto&& name : get_vm_names(vm_path))
|
||||
{
|
||||
try
|
||||
{
|
||||
const std::string create_fn_name = "evmc_create_" + name;
|
||||
std::cout << "Seeking `" << create_fn_name << "`... ";
|
||||
create_fn = dll::import<evmc_create_fn>(vm_path, create_fn_name);
|
||||
std::cout << "found.\n";
|
||||
break;
|
||||
}
|
||||
catch (boost::system::system_error& err)
|
||||
{
|
||||
using namespace boost::system;
|
||||
const error_code windows_error{127, system_category()};
|
||||
constexpr auto posix_error = errc::invalid_seek;
|
||||
if (err.code() != posix_error && err.code() != windows_error)
|
||||
throw; // Error other than "symbol not found".
|
||||
std::cout << "not found.\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!create_fn)
|
||||
{
|
||||
std::cerr << "EVMC create function not found in " << vm_path.string() << "\n";
|
||||
return 2;
|
||||
case EVMC_LOADER_SUCCESS:
|
||||
break;
|
||||
case EVMC_LOADER_CANNOT_OPEN:
|
||||
std::cerr << "Cannot open " << vm_path << "\n";
|
||||
return static_cast<int>(ec);
|
||||
case EVMC_LOADER_SYMBOL_NOT_FOUND:
|
||||
std::cerr << "EVMC create function not found in " << vm_path << "\n";
|
||||
return static_cast<int>(ec);
|
||||
default:
|
||||
std::cerr << "Unexpected error in evmc_load(): " << ec << "\n";
|
||||
return static_cast<int>(ec);
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
|
|
Loading…
Reference in New Issue