Skip to content
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

Merged
merged 7 commits into from
Jul 10, 2018
Merged

Conversation

chfast
Copy link
Member

@chfast chfast commented Jul 5, 2018

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.

@chfast chfast force-pushed the loader branch 3 times, most recently from 575eb79 to 9f82448 Compare July 5, 2018 12:20
@chfast chfast requested review from axic and gumb0 July 5, 2018 13:11
@chfast
Copy link
Member Author

chfast commented Jul 9, 2018

Can you review this?

EVMC_ERRC_INVALID_ARGUMENT,
};

struct evmc_instance* evmc_load(const char* filename, enum evmc_loader_error_code* error_code);
Copy link
Member

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.

Copy link
Member

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.


enum evmc_loader_error_code
{
EVMC_ERRC_SUCCESS = 0,
Copy link
Member

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?

Copy link
Member

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.

Copy link
Member Author

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.

Copy link
Member

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_

* - 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_,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain this?

Copy link
Member Author

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.

Copy link
Member

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?

Copy link
Member

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.

Copy link
Member

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?

Copy link
Member

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.

Copy link
Member Author

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);
Copy link
Member

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?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Then I would need sizeof(prefix) - 1.
  2. The compiler is replacing strlen(prefix) with a constant.

Copy link
Member

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?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

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 😉

Copy link
Member Author

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.

};

/**
* Dynamically loads the shared object and with an EVM implementation.
Copy link
Member

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"?

* 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does not exist

#define HAVE_STRCPY_S 0
#endif

#define PATH_MAX_LENGHT 4096
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

length

strcpy_s(name, sizeof(name), prefix);

// Find filename in the path.
const char* sep_pos = strrchr(filename, '/');
Copy link
Member

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

Copy link
Member Author

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.

Copy link
Member

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;
Copy link
Member

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?

Copy link
Member

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?

Copy link
Member

@gumb0 gumb0 Jul 9, 2018

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

Copy link
Member

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.

Copy link
Member Author

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);
Copy link
Member

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

Copy link
Member Author

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.

@chfast
Copy link
Member Author

chfast commented Jul 10, 2018

Ready. Rebase is needed before merge.

@chfast chfast merged commit 4ae7660 into master Jul 10, 2018
@chfast chfast deleted the loader branch July 10, 2018 11:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants