FlexDLL: an implementation of a dlopen-like API for Windows
Introduction
------------
Under Windows, DLL ([Dynamically-Linked
Libraries](http://en.wikipedia.org/wiki/Dynamic-link_library)) are
generally used to improve code modularity and sharing. A DLL can be
loaded automatically when the program is loaded (if it requires the
DLL). The program can also explicitly request Windows to load a DLL at
any moment during runtime, using the
[`LoadLibrary`](https://msdn.microsoft.com/en-us/library/windows/desktop/ms684175(v=vs.85).aspx)
function from the Win32 API.
This naturally suggests to use DLLs as a plugin mechanism. For instance,
a web server could load extensions modules stored in DLLs at runtime.
But Windows does not really make it easy to implement plugins that way.
The reason is that when you try to create a DLL from a set of object
files, the linker needs to resolve all the symbols, which leads to the
very problem solved by FlexDLL:
**Windows DLL cannot refer to symbols defined in the main application or
in previously loaded DLLs.**
Some usual solutions exist, but they are not very flexible. A notable
exception is the [edll](http://edll.sourceforge.net/) library (its
homepage also describes the usual solutions), which follows a rather
drastic approach; indeed, edll implements a new dynamic linker which can
directly load object files (without creating a Windows DLL).
FlexDLL is another solution to the same problem. Contrary to edll, it
relies on the native static and dynamic linkers. Also, it works both
with the Microsoft environment (MS linker, Visual Studio compilers) and
with Cygwin (GNU linker and compilers, in Cygwin or MinGW mode).
Actually, FlexDLL implements mostly the usual
[`dlopen`](http://www.opengroup.org/onlinepubs/009695399/functions/dlopen.html)
POSIX API, without trying to be fully conformant though (e.g. it does
not respect the official priority ordering for symbol resolution). This
should make it easy to port applications developped for Unix.
About
-----
FlexDLL is distributed under the terms of a zlib/libpng open source
[license](LICENSE). The copyright holder is the Institut
National de Recherche en Informatique et en Automatique (INRIA). The
project was started when I (= Alain Frisch) was working at INRIA. I'm
now working for [LexiFi](http://www.lexifi.com), which is kind enough to
let me continue my work on FlexDLL. My office mate at INRIA,
Jean-Baptiste Tristan, coined the name FlexDLL.
The runtime support library is written in C. The **`flexlink`** wrapper
is implemented in the wonderful [OCaml](http://ocaml.org)
language.
Supported toolchains
--------------------
MSVC: the 32-bit C compiler from Microsoft.
MSVC64: the 64-bit C compiler from Microsoft.
CYGWIN: the 32-bit gcc compiler shipped with Cygwin.
MINGW: the 32-bit gcc compiler from the Mingw64 project, packaged in
Cygwin (as i686-w64-mingw32-gcc).
MINGW64: the 64-bit gcc compiler from the Mingw64 project, packaged in
Cygwin (as x86\_64-w64-mingw32-gcc).
LD: an internal linker to produce .dll (only).
Download
--------
- [Source releases](https://github.com/alainfrisch/flexdll/releases).
- [Binary release 0.35 (self-extracting
installer)](http://alain.frisch.fr/flexdll/flexdll-0.35-setup.exe).
- [Binary release 0.35 (.zip
file)](http://alain.frisch.fr/flexdll/flexdll-bin-0.35.zip).
- [Development version](https://github.com/alainfrisch/flexdll).
- [Changelog](CHANGES).
**Installation instructions:** Simply run the installer and add the
resulting directory (e.g. `C:\Program Files\flexdll` or
`C:\Program Files (x86)\flexdll`) to the PATH. You can also create this
directory by hand and unzip the .zip file in it.
**Compiling from sources:** To compile the code from sources, you'll
need a working installation of OCaml, GNU Make, and a C
toolchain (compiler + linker) either the one from Microsoft (any version
of Visual Studio should work), Cygwin, or Mingw. It is probably a good
idea to use a native version of ocamlopt (not the Cygwin port) to
compile flexlink. By default, the **`Makefile`** will compile support
objects for the supported toolchains; you can choose a subset with the
**`CHAINS`** variable, e.g.: **`make CHAINS="mingw msvc"`**.
Overview
--------
FlexDLL has two components: a wrapper around the static linker, and a
tiny runtime library to be linked with the main application. The wrapper
must be called in place of the normal linker when you want to produce a
DLL or to link the main application. The runtime library relies
internally on the native **`LoadLibrary`** API to implement a
dlopen-like interface.
Let's see a simple example of a plugin. Here is the code for the main
program (file **`dump.c`**):
````
#include <stdlib.h>
#include "flexdll.h"
typedef void torun();
void api(char *msg){ printf("API: %s\n", msg); }
int main(int argc, char **argv)
{
void *sym;
void *handle;
int i;
torun *torun;
for (i = 1; i < argc; i++) {
handle = flexdll_dlopen(argv[i], FLEXDLL_RTLD_GLOBAL);
if (NULL == handle) { printf("error: %s\n", flexdll_dlerror()); exit(2); }
torun = flexdll_dlsym(handle, "torun");
if (torun) torun();
}
exit(0);
}
````
This application opens in turn all the DLLs given on its command line,
using the FlexDLL function **`flexdll_dlopen`**. For each DLL, the
program looks for a symbol named **`torun`** (which is supposed to refer
to a function) and if the symbol is available in the DLL, the function
is called. The program also provides a very simple API to its plugin:
the **`api`** function. The **`FLEX_RTLD_GLOBAL`** flag makes all the
symbols exported by each DLL available for the DLL to be loaded later.
This main program can be compiled and linked like the commands below
(the **`[...]`** refers to the directory where FlexDLL is installed).
````
# MSVC
cl /nologo /MD -I[...] -c dump.c
flexlink -chain msvc -exe -o dump.exe dump.obj
# MINGW
i686-w64-mingw32-gcc -I[...] -c dump.c
flexlink -chain mingw -exe -o dump.exe dump.o
# CYGWIN
gcc -I[...] -c dump.c
flexlink -chain cygwin -exe -o dump.exe dump.o
````
The compilation step is completely standard, but in order to link the
main application, you must call the **`flexlink`** tool, which is the
wrapper around the linker. The **`-chain`** command line switch selects
which linker to use, and the **`-exe`** option tells the wrapper that it
must produce a stand-alone application (not a DLL).
Now we can provide a first plugin (file **`plug1.c`**):
````
int x = 3;
void dump_x() { printf("x=%i\n", x); }
void torun() { api("plug1.torun();"); }
````
Note that the plugin uses the **`api`** symbol from the main application
(it would be cleaner to introduce it with an **`extern`** declaration).
You can compile and link this plugin (into a DLL) with the followind
commands:
````
# MSVC
cl /nologo /MD -c plug1.c
flexlink -chain msvc -o plug1.dll plug1.obj
# MINGW
i686-w64-mingw32-gcc -c plug1.c
flexlink -chain mingw -o plug1.dll plug1.o
# CYGWIN
gcc -D_CYGWIN_ -c plug1.c
flexlink -chain cygwin -o plug1.dll plug1.o
````
And now you can ask the main program to load the plugin:
````
$ ./dump plug1.dll
API: plug1.torun();
````
Here is the code for a second plugin (file **`plug2.c`**) that refers to
symbols (a function and a global variable) defined in the first plugin:
````
extern int x;
void torun() {
api("plug2.torun();");
dump_x();
x = 100;
dump_x();
}
````
Since the second plugin depends on the first one, you need to load both:
````
$ ./dump plug2.dll
error: Cannot resolve dump_x
$ ./dump plug1.dll plug2.dll;
API: plug1.torun();
API: plug2.torun();
x=3
x=100
````
Simple, isn't it? No **`declspec`** declaration, no import library to
deal with,...
How it works
------------
Object files (.obj/.o)
评论0
最新资源