From 14c5356ae66ad4c2bec3948fd90363f18ee7ae94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 16 Apr 2019 18:04:42 +0200 Subject: [PATCH] loader: Expose DLL load errors with evmc_last_error_msg() Currently only works for errors by dlopen() on Linux and macos, otherwise returns NULL. --- bindings/go/evmc/evmc.go | 8 +++++++- include/evmc/loader.h | 17 +++++++++++++++-- lib/loader/loader.c | 14 ++++++++++++++ test/vmtester/CMakeLists.txt | 2 +- test/vmtester/vmtester.cpp | 8 +++++++- 5 files changed, 44 insertions(+), 5 deletions(-) diff --git a/bindings/go/evmc/evmc.go b/bindings/go/evmc/evmc.go index 6b3f66a..a456869 100644 --- a/bindings/go/evmc/evmc.go +++ b/bindings/go/evmc/evmc.go @@ -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: diff --git a/include/evmc/loader.h b/include/evmc/loader.h index a2bbacf..8b7378b 100644 --- a/include/evmc/loader.h +++ b/include/evmc/loader.h @@ -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. */ /** @@ -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 diff --git a/lib/loader/loader.c b/lib/loader/loader.c index c01d337..c92438c 100644 --- a/lib/loader/loader.c +++ b/lib/loader/loader.c @@ -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; @@ -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; } @@ -130,9 +138,15 @@ exit: 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) diff --git a/test/vmtester/CMakeLists.txt b/test/vmtester/CMakeLists.txt index 33d5630..d856f37 100644 --- a/test/vmtester/CMakeLists.txt +++ b/test/vmtester/CMakeLists.txt @@ -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") diff --git a/test/vmtester/vmtester.cpp b/test/vmtester/vmtester.cpp index 005e434..91c5f7d 100644 --- a/test/vmtester/vmtester.cpp +++ b/test/vmtester/vmtester.cpp @@ -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(ec); + } case EVMC_LOADER_SYMBOL_NOT_FOUND: std::cerr << "EVMC create function not found in " << evmc_module << "\n"; return static_cast(ec);