-
Notifications
You must be signed in to change notification settings - Fork 310
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add evmc::loader library to support dynamic loading #40
Conversation
575eb79
to
9f82448
Compare
Can you review this? |
include/evmc/loader.h
Outdated
EVMC_ERRC_INVALID_ARGUMENT, | ||
}; | ||
|
||
struct evmc_instance* evmc_load(const char* filename, enum evmc_loader_error_code* error_code); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should have documentation here. Also mention that error_code
can be NULL
and in that case it won't be set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay this was added with a subsequent commit.
include/evmc/loader.h
Outdated
|
||
enum evmc_loader_error_code | ||
{ | ||
EVMC_ERRC_SUCCESS = 0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not EVMC_LOADER_SUCCESS
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@chfast this is still outstanding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At first I wanted to use a subset of error codes from C++ errc: https://en.cppreference.com/w/cpp/error/errc, but in the end they didn't match.
We can either make it evmc-generic: evmc_loader_error_code -> evmc_error_code, or use EVMC_LOADER_
prefix as you proposed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd vote for EVMC_LOADER_
include/evmc/loader.h
Outdated
* - the filename is taken from the path, | ||
* - the "lib" prefix and file extension are stripped from the name, | ||
* - all "-" are replaced with "_" to construct _full name_, | ||
* - the last stem from the _full name_ separated with "_" is taken to construct the _short name_, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about?
The full name is split by "_" char and the last item is taken to form the short name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you give an example of a filename having both short and full names?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, example in the comment would be helpful, like for "libfull-evm-name.so" the full name "evmc_create_full_evm_name" and "evmc_create_name" are checked in the library
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also. why do we need these complicated rules, can't we just have one hard-coded name for create function in any evmc library?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Want to support both static and dynamic linking of the same source.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On POSIX we could use symbol alias in the .so library. On Windows it is much harder. In the end I had to just define evmc_create()
function that executes evmc_create_interpreter()
and disable the evmc_create()
when building a static library. This generally gets messy and requires toolchain specific build flags. And I expect that to be even harder for rust or nim. So I decided to provide this naming convention and keep the implementation of it in this single place.
|
||
// Create name buffer with the prefix. | ||
const char prefix[] = "evmc_create_"; | ||
const size_t prefix_length = strlen(prefix); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't sizeof(prefix)
work here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Then I would need
sizeof(prefix) - 1
. - The compiler is replacing
strlen(prefix)
with a constant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure it replaces it with a constant?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice. Damn compilers, they're becoming way too smart 😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not super smart. They have hardcoded knowledge of some C standard library functions.
include/evmc/loader.h
Outdated
}; | ||
|
||
/** | ||
* Dynamically loads the shared object and with an EVM implementation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some verb missing after "and"?
include/evmc/loader.h
Outdated
* 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 exists or is not a valid shared library the ::EVMC_ERRC_CANNOT_OPEN error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does not exist
lib/loader/loader.c
Outdated
#define HAVE_STRCPY_S 0 | ||
#endif | ||
|
||
#define PATH_MAX_LENGHT 4096 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
length
lib/loader/loader.c
Outdated
strcpy_s(name, sizeof(name), prefix); | ||
|
||
// Find filename in the path. | ||
const char* sep_pos = strrchr(filename, '/'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will not work when windows path provided
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True. I have to add a test for it. What do you think about doing a normalization on Windows before this by replacing \
with /
. I believe on Windows you can have your separators mixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it should work
exit: | ||
if (error_code) | ||
*error_code = ec; | ||
return create_fn; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So handle
stays open forever after sucessful evmc_load
, is this ok?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you can close it if there's a pointer into it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On Windows there is FreeLibrary
, and GetProcAddress
doesn't influence it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Documentation is not clear, but I would assume that if the flow is
lib = dlopen()
fn = dlsym(lib, "hello")
dlclose(lib)
fn()
Then it should crash. man page for dlclose
states it uses reference counting and if it hits zero it will run all the deallocation code (such as atexit
if a similar thing exists for libraries) and remove the shared library from memory. Not sure if that reference counting also considers anything returned by dlsym
, since that would be really hard to track. Instead I would assume it just counts how many time dlopen
was called for the same thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The dlclose()
will unload the library from the memory so you cannot use functions from it (similarly on Windows). We are doing so only when we could not find the create function because then the library is not needed any more. In case we use the library, it is never unloaded.
if (short_name_pos) | ||
{ | ||
short_name_pos += 1; | ||
memmove(name + prefix_length, short_name_pos, strlen(short_name_pos) + 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this still work fine when file name ends with _
? Add a test for it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It works. For lib_.so
the full name is "_", and short name is "". I'm adding unit tests for it.
Ready. Rebase is needed before merge. |
Added evmc::loader library that have to code for dynamically loading the EVM implementations. It's going to be used by vmtester (already migrated), aleth and go/evmc package. Warning! A lot of C language used.