evmc/lib/loader/loader.c

148 lines
3.8 KiB
C

/* EVMC: Ethereum Client-VM Connector API.
* Copyright 2018 Pawel Bylica.
* Licensed under the MIT License. See the LICENSE file.
*/
#include <evmc/loader.h>
#include <evmc/evmc.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;
}
struct evmc_instance* evmc_load_and_create(const char* filename, enum evmc_loader_error_code* error_code)
{
evmc_create_fn create_fn = evmc_load(filename, error_code);
if (!create_fn)
return NULL;
struct evmc_instance* instance = create_fn();
if (!instance)
{
*error_code = EVMC_LOADER_INSTANCE_CREATION_FAILURE;
return NULL;
}
if (instance->abi_version != EVMC_ABI_VERSION)
{
*error_code = EVMC_LOADER_ABI_VERSION_MISMATCH;
return NULL;
}
return instance;
}