Skip to content

Commit

Permalink
loader: Expose DLL load errors with evmc_last_error_msg()
Browse files Browse the repository at this point in the history
Currently only works for errors by dlopen() on Linux and macos, otherwise returns NULL.
  • Loading branch information
chfast committed Apr 16, 2019
1 parent 4408da2 commit 14c5356
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 5 deletions.
8 changes: 7 additions & 1 deletion bindings/go/evmc/evmc.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,13 @@ func Load(filename string) (instance *Instance, err error) {
case C.EVMC_LOADER_SUCCESS:
instance = &Instance{handle}
case C.EVMC_LOADER_CANNOT_OPEN:
err = fmt.Errorf("evmc loader: cannot open %s", filename)
optionalErrMsg := C.evmc_last_error_msg()
if optionalErrMsg != nil {
msg := C.GoString(optionalErrMsg)
err = fmt.Errorf("evmc loader: %s", msg)
} else {
err = fmt.Errorf("evmc loader: cannot open %s", filename)
}
case C.EVMC_LOADER_SYMBOL_NOT_FOUND:
err = fmt.Errorf("evmc loader: the EVMC create function not found in %s", filename)
case C.EVMC_LOADER_INVALID_ARGUMENT:
Expand Down
17 changes: 15 additions & 2 deletions include/evmc/loader.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* EVMC: Ethereum Client-VM Connector API.
* Copyright 2018 The EVMC Authors.
* Licensed under the Apache License, Version 2.0. See the LICENSE file.
* Copyright 2019 The EVMC Authors.
* Licensed under the Apache License, Version 2.0.
*/

/**
Expand Down Expand Up @@ -103,6 +103,19 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro
struct evmc_instance* evmc_load_and_create(const char* filename,
enum evmc_loader_error_code* error_code);

/**
* Returns the human-readable message describing the most recent error
* that occurred in EVMC loading.
*
* In case any loading function returned ::EVMC_LOADER_SUCCESS this function always returns NULL.
* In case of error code other than success returned, this function MAY return the error message.
* This function is not thread-safe.
*
* @return Error message or NULL if no additional information is available.
* The returned pointer MUST NOT be freed by the caller.
*/
const char* evmc_last_error_msg();

#if __cplusplus
}
#endif
Expand Down
14 changes: 14 additions & 0 deletions lib/loader/loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,12 @@ static void strcpy_s(char* dest, size_t destsz, const char* src)
}
#endif

static const char* last_error_msg = NULL;


evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* error_code)
{
last_error_msg = NULL; // Reset last error.
enum evmc_loader_error_code ec = EVMC_LOADER_SUCCESS;
evmc_create_fn create_fn = NULL;

Expand All @@ -66,6 +69,11 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro
DLL_HANDLE handle = DLL_OPEN(filename);
if (!handle)
{
#if !defined(EVMC_LOADER_MOCK) && !_WIN32
// If available, get the error message from dlerror().
last_error_msg = dlerror();
#endif

ec = EVMC_LOADER_CANNOT_OPEN;
goto exit;
}
Expand Down Expand Up @@ -130,9 +138,15 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro
return create_fn;
}

const char* evmc_last_error_msg()
{
return last_error_msg;
}

struct evmc_instance* evmc_load_and_create(const char* filename,
enum evmc_loader_error_code* error_code)
{
// First load the DLL. This also resets the last_error_msg;
evmc_create_fn create_fn = evmc_load(filename, error_code);

if (!create_fn)
Expand Down
2 changes: 1 addition & 1 deletion test/vmtester/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ add_test(NAME vmtester/help COMMAND evmc-vmtester --version --help)
set_tests_properties(vmtester/help PROPERTIES PASS_REGULAR_EXPRESSION "Usage:")

add_test(NAME vmtester/nonexistingvm COMMAND evmc-vmtester nonexistingvm)
set_tests_properties(vmtester/nonexistingvm PROPERTIES PASS_REGULAR_EXPRESSION "Cannot open")
set_tests_properties(vmtester/nonexistingvm PROPERTIES PASS_REGULAR_EXPRESSION "[Cc]annot open")

add_test(NAME vmtester/noarg COMMAND evmc-vmtester)
set_tests_properties(vmtester/noarg PROPERTIES PASS_REGULAR_EXPRESSION "is required")
Expand Down
8 changes: 7 additions & 1 deletion test/vmtester/vmtester.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,14 @@ int main(int argc, char* argv[])
case EVMC_LOADER_SUCCESS:
break;
case EVMC_LOADER_CANNOT_OPEN:
std::cerr << "Cannot open " << evmc_module << "\n";
{
const auto error = evmc_last_error_msg();
if (error)
std::cerr << error << "\n";
else
std::cerr << "Cannot open " << evmc_module << "\n";
return static_cast<int>(ec);
}
case EVMC_LOADER_SYMBOL_NOT_FOUND:
std::cerr << "EVMC create function not found in " << evmc_module << "\n";
return static_cast<int>(ec);
Expand Down

0 comments on commit 14c5356

Please sign in to comment.