Emulating DLL
Craig
Craig at Tonsils.com
Wed Mar 20 19:44:52 UTC 2019
On Wednesday, 20 March 2019 at 14:27:54 UTC, Ethan wrote:
> On Tuesday, 19 March 2019 at 19:50:15 UTC, Craig wrote:
>> For example, with windows I could simply compile to a dll then
>> extract the code, or just use LoadLibrary and it effectively
>> does all the work(steps 1 & 2 & 3).
>
> LoadLibrary performs steps 1 and part of step 2. The DllMain
> function of a library is called separately by Windows for each
> thread in your system, which is where the bulk of step 2 is
> handled. Step 3 isn't handled at all by LoadLibrary, and is
> instead entirely up to you to deal with via the GetProcAddress
> function.
>
> If you want DLLs to operate in that step 1-2-3 manner, then the
> compiler can generate a static library that handles that all
> for you. But, as you might expect, that removes the hot
> reloading capability as that is all handled before WinMain is
> entered.
>
> If you expect hot reloading to work without effort, you're
> going to have to compile your DLLs with -betterC. The D Runtime
> is not yet in a standalone DLL (unless I've missed an
> announcement over the last couple of months). Each DLL you
> build will require the D runtime to be embedded. Things get
> really tricky from there.
>
> If you ever use your DLL for more than calling functions with
> basic types, or value aggregates (structs) containing basic
> types, then be aware that without -betterC that there is
> potential for the moduleinfo and typeinfo systems to go a bit
> screwy. Especially once druntime lives in its own DLL, as
> druntime expects those systems to be initialised once at
> startup. POSIX libraries will have the same issues, but I have
> nod had a need to investigate further over the last few months.
>
>> LoadLibrary is not portable though and seems excessive to do
>> something that should be rather simple unless there is
>> something I'm missing that has to be done that is complex.
>
> LoadLibrary is the equivalent to dlopen on POSIX systems, with
> one very important difference: Windows does not provide lazy
> symbol evaluation. As such, extra work is required to handle
> that. The average usecase is covered by the mentioned static
> lib, but any hot reloading is handled in a custom manner by
> every Windows codebase you'll encounter.
>
> The static lib I mention, in fact, redirects every cross-DLL
> call to a jump table. And cross-DLL data sharing gets hairy in
> Windows. Function parameters aren't a problem, but Windows
> explicitly disallows thread local variables to cross the DLL
> boundary for example (even if you expose it for export and
> perform a symbol lookup, it won't work and you'll get garbage
> back).
>
> As I said, it's not a simple thing by any means. My work has
> attempted to hide all that complexity by boiling it all down to
> "include these headers/modules, link this lib, and get on with
> life". If you find the resulting code too complex (and I freely
> admit that I haven't got the integration in to as foolproof a
> manner as I am aiming for) then very definitely read through
> the presentations to get a clear idea of why something that
> should be so simple really isn't.
You are making this more complicated because you don't understand
that I don't need a general purpose solution. I simply need to
call simple functions that do not do much, not full blown
applications.
For example, Suppose I want to write a fractal display
application and instead of hard coding the function to display,
it's external.
e.g.,
import std.complex;
alias C = Complex!double;
C Mandelbrot(C z, C c) { return z*z + c; }
This function does not require much of anything to run. The
machine code can be copied and pasted at will to any executable
memory location and ran... it can, in fact, be inlined with a
little caution(reserve enough space and ignore the stack code).
No initialization needs to be nor garbage collecting.
For a little more robustness one would want to allow for GC,
which should not be hard, and context passing so more useful
functionality can be had.
If D had a Dstring2Machine that compiled the above code to
machine code then it could be used directly without issue.
Because these functions are deterministic and the host determines
their usage, there is little issues to worry about. Hot swapping
is simply changing a pointer when the function is not being
called.
As long as certain guidelines are followed one can do this quite
simply without too much issue and it allows for a scripting like
solution for D apps. It allows one to inject functionality in to
an application that runs at full speed and is relatively fast to
compile.
The idea is not to be able to connect two full blow external apps
but to connect little pieces of functionality to an app that
allow for vast flexibility. Since each unit of functionality is
essentially pure, there are no major issues.
It's this simple:
1. Take source and compile it in to machine code.
2. App extracts machine code and inserts it in to it's memory
space to be able to execute.
Do you agree that, at least in some cases, this is entirely
feasible and easy?
I'll give you a hint:
1.
module simple;
void foo() { };
compile directly in to machine code(the most raw form possible
containing foo).
2.
App load the machine code, execute, reload and execute if
desired...
Since foo does nothing it's just a nop. We are just copying
around a ret. It's impossible for anything to ever go awry here
excluding buggy code/compiler/etc.
From here one can expand the model to include something that is
more useful.
The idea is to expand it enough to make it generally quite useful
but not so much one has to include the kitchen sink.
I don't need a heavy version of binderoo, I need a light one.
Something that lets me do basic "scripting" so I can modify the
application behavior at runtime without recompiling the
application.
If you think the example is stupid and useless then
int age() { return 43; }
and nothing changes.
of course you will then think that example is stupid because we
could just put it in a text file and read that rather than go
through all this trouble.
so then
C calc(C z, C c) { return z*z*z*z*z*z + c*z + c; }
and to get around this without leveraging the D compiler one has
to make a full blown parser... even though it's still just
relatively basic code like age. It just includes some
calculations.
It's no different from foo though. Just basic code.
But we can go further by allowing more information to be passed
to and from the function. The function can also be impure such as
void foo() { writeln("I'm IMPURE!"); }
which will work without much work(might require fixing up a few
symbols/pointers).
All it is, is delayed compilation. I'm simply wanting to delay
the compilation of a piece of code in an app from the main
compilation. All the other stuff about the OS and TLS are
irrelevant in this context.
One could sort of do this with the object files and just
recompile the functions and relink everything. But with a little
work this step can be avoided since it's not needed and
slower(but more robust).
More information about the Digitalmars-d-learn
mailing list