@jan thanks for that detailed post describing the problem in more depth, it was extremely useful for me, and it made me realize that perhaps we can find a solution.
As you say, it is a mess, but if we are able to think about our needs in a different way we may be able to find a solution.
The reason is that we don’t really need, for foreign functions, a full C runtime environment with dynamic linker, etc, our needs, I think, are rather modest.
In order to achieve foreign predicates with static and dynamic libraries what we need is:
- The address of the foreign function and some of its other properties, e.g. number of parameters, some flags
- The code of the function available in memory, ready to be executed
- To be able to call the function pointer and pass it the arguments
Dynamic libs
For regular dynamic libraries as we use today, the three needs are satisfied as follows in the current code:
- Number 1 is satisfied by
dlsym
and by the registration/installation function when the shared lib is loaded - Number 2 is satisfied by the dynamic linker
- Number 3 - There is no problem with this, C provides this functionality
Static binary
What I was thinking is that we are not bound by dlsym to get the function address, so we could store a per architecture foreign symbol table with the relative address of the foreign functions (relative to a stable function, like PL_initialise(...)
in the saved state.
This foreign symbol table (foreign function table) would also contain all the other needed information such as number of arguments, flags, etc.
The symbol table would be created by qsave_program/2 (which is run from the static swipl
binary that will be used for the standalone version), and stored in the saved state. This symbol table would later on be used by the same swipl static binary (to which the saved state is appended) to find the address in order to call the foreign function. This way we have a stand-alone file which has the static swipl binary including add-ons, foreign static libraries, packages, res:// and a saved state appended (which includes the foreign symbol table).
In this case the needs above would be satisfied as follows:
-
dlsym
would be substituted by code that reads from the foreign symbol table in the saved state. - The code of the function is already loaded because it is a static binary.
- This is no problem in any case.
To obtain the relative address of the foreign functions an initialization routine could be called for each static library that is used (the name of the registration function can be based on the name of the library, just like today we can have something like install_mylib()
for shared libraries). The relative address against PL_initialise
would then be calculated and this address stored in the foreign symbol table when qsave_program/2 is called. This address offset should be stable for each architecture (and, of course, the static binary used).
This is the main idea, some things would have to be flushed out, like versioning, building the swipl binary with required libs. etc. Am I missing something? Would this work?
EDIT: I don’t think static binaries are really needed for Windows since bundling dlls and needed files in a directory works just about everywhere.