Code Monkey home page Code Monkey logo

flexdll's Introduction

FlexDLL: an implementation of a dlopen-like API for Windows

Build status

Introduction

Under Windows, DLL (Dynamically-Linked Libraries) 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 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 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 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 developed for Unix.

About

FlexDLL is distributed under the terms of a zlib/libpng open source 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, 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 language.

Supported toolchains

MSVC: the 32-bit C compiler from Microsoft.

MSVC64: the 64-bit C compiler from Microsoft.

CYGWIN64: the 64-bit gcc compiler shipped with Cygwin.

MINGW: the 32-bit gcc compiler from the MinGW-w64 project, packaged in Cygwin (as i686-w64-mingw32-gcc).

MINGW64: the 64-bit gcc compiler from the MinGW-w64 project, packaged in Cygwin (as x86_64-w64-mingw32-gcc).

LD: an internal linker to produce .dll (only).

Download

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 cygwin64 -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 following 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 cygwin64 -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) contain relocation information that explain to the linker how some addresses in their code or data sections have to be patched, using the value of some global symbols. When the static linker is invoked to produce a DLL from a set of object files, it assumes that all the relocations can be performed: all the symbols which are used in relocations must be defined in some the objects linked together. FlexDLL drops this constraint following a very simple idea: when a relocation refers to a symbol which is not available, the relocation is turned into a piece of data that will be passed to the runtime support library.

In the example above, the plug1.obj object refers to a symbol api. When this object is turned into a DLL, FlexDLL produce a new temporary object file derived from plug1.obj without the relocation that mentions api. Instead, it adds an “import table”, which is just a piece of data that tells the FlexDLL support library which address has to be patched with the value of a symbol called api to be found somewhere else. You can see the list of such imported symbols by adding the -show-imports option to the flexlink command line:

$ ../flexlink -chain msvc -o plug1.dll plug1.obj -show-imports
** Imported symbols for plug1.obj:
_api

When the flexdll_dlopen function opens this DLL, it will look for an internal symbol that points to the import table, resolve the symbols and patch the code and data segments accordingly. The FlexDLL runtime library must thus maintain a set of symbols together with their concrete values (addresses). In particular, it knows about the global symbols defined in the main program. Indeed, when you link the main program with flexlink -exe, the wrapper produces a small fresh object file that contains a symbol table, mapping symbol names to their addresses.

$ ../flexlink -chain msvc -exe -o dump.exe dump.obj -show-exports
** Exported symbols:
_api
_flexdll_dlclose
_flexdll_dlerror
_flexdll_dlopen
_flexdll_dlsym
_flexdll_dump_exports
_flexdll_dump_relocations
_main

As you can see, all the global symbols (including those that comes from FlexDLL itself) appear in the global symbol table. FlexDLL knows not only about symbols that comes from the main program, but also about symbols exported by the DLL it loads. This is needed to implement the flexdll_dlsym function, but also to deal with import tables that mention symbols defined in previously loaded DLLs (for which the FLEXDLL_RTLD_GLOBAL was used). So the wrapper produces not only an import table for DLLs, but also an export table:

$ ../flexlink -chain msvc -o plug1.dll plug1.obj -show-imports -show-exports
** Imported symbols for plug1.obj:
_api
** Exported symbols:
_dump_x
_torun
_x

$ ../flexlink -chain msvc -o plug2.dll plug2.obj -show-imports -show-exports
** Imported symbols for plug2.obj:
_api
_dump_x
_x
** Exported symbols:
_torun

How does FlexDLL determine which symbols are imported or exported? It uses an algorithm similar to the linker itself. The command line mentions a number of object and library files. In a first pass, the wrapper computed which objects embedded in those libraries will be used. To do that, it looks at which symbols are used, and where they are defined. Then the wrapper considers that all the global (external) symbols are exported. Note that the /export or __declspec(dllexport) directives are not used: all the symbols are exported. (In a future version, FlexDLL will allow to control more precisely which symbols are exported). All the object files (given explicitly, or embedded in a library) that need to import symbols must be rewritten. The flexlink wrapper will produce new temporary object files for them. If you want to understand better how FlexDLL works, you can use the -v and -save-temps command options to tell the wrapper to show you the linker command line and to preserve those temporary files alive (by default, they are removed automatically).

Some object files can mention default libraries (they correspond to the /defaultlib linker flag, which is often embedded in the object .drectve section). FlexDLL will parse those libraries, but only to see which symbols they define. Those symbols are not considered as being imported by the DLL, but they won't be exported either. A typical case of default libraries are import libraries that behave as interfaces to (normal, non-FlexDLL) DLLs.

Advanced topic: __declspec(dllimport)

C compilers under Windows support a special declaration of external symbols. You can write:

__declspec(dllimport) extern int mysymbol;

Internally, this declaration has the same effect as declaring:

extern int *_imp__mysymbol;

and using &x instead of x everywhere in the current unit. In other words, even if your code seems to access x directly, each access actually goes through an extra indirection.

FlexDLL knows about this convention. When a object refers to a symbol of the form _imp__XXX which is not available in the objects that will form the DLL to be created, it resists the temptation of putting an entry for _imp__XXX in the import table. Instead, it adds the equivalent of the following declaration:

void *_imp__XXX = &XXX;

If the symbol XXX itself is not available, this will in turn produce an entry for XXX in the import table. All these new declarations are put in the same object file that contain the export table, which is global for the DLL to be produced. So, if all the external symbols in a given object files are accessed through this convention, the object file need not be patched at all.

Note that you can define and use the _imp__XXX symbols by hand, you don't have to use the __declspec(dllimport) notation (this is useful if you use a compiler that doesn't support this notation).

Anyway, there is no compelling reason for adopting this style. A very small advantage might be that there will be fewer relocations at runtime and that more code pages can be shared amongst several instances of the same DLL used by different processes.

Advanced topic: static constructors and the entry point

A Windows DLL can define an optional entry point. When the DLL is loaded, this function is automatically called. (The same function is called when the DLL is unloaded, or when threads are spawned or destroyed.)

Usually, the real entry point is provided by the C runtime library: _cygwin_dll_entry for Cygwin, DllMainCRTStartup for MinGW, _DllMainCRTStartup for MSVC. These functions perform various initialization for the C runtime library, invoke the code that has to be run automatically at load time (e.g for C++: constructors of static objects, or right-hand sides of non-constant initializers for global variables), and then call the function DllMain, which by default does nothing but can be overridden by the program to perform custom initialization of the DLL.

FlexDLL must take control before any custom code (static constructors, DllMain) so as to perform relocations (in case this code refers to symbols found in the main program or previously loaded DLLs). As a consequence, FlexDLL defines its own entry point, which first asks the main program to perform relocations and then calls the regular entry point of the C runtime library. This behavior is implemented in the flexdll_initer.c file, and the corresponding object file (whose name depends on the toolchain) is automatically included by flexlink.

It is possible to completely disable the DLL entry point with the -noentry option passed to flexlink. In this case, FlexDLL will perform relocations after the DLL has been opened.

The API

Here is the content of the flexdll.h file:

#define FLEXDLL_RTLD_GLOBAL 0x0001
#define FLEXDLL_RTLD_LOCAL  0x0000
#define FLEXDLL_RTLD_NOEXEC 0x0002

void *flexdll_dlopen(const char *, int);
#ifndef CYGWIN
void *flexdll_wdlopen(const wchar_t *, int);
#endif
void *flexdll_dlsym(void *, const char *);
void flexdll_dlclose(void *);
char *flexdll_dlerror(void);

void flexdll_dump_exports(void *);
void flexdll_dump_relocations(void *);

The flexdll_dl* functions are mostly compatible with their POSIX counterparts. Here is a short explanation of their semantics.

The most important function is flexdll_dlopen. The first argument gives the filename of a DLL to be opened. This DLL must have been produced by the flexlink wrapper. The function resolves the symbols mentioned in the DLL's import table and performs the relocations. The second argument is a mode, made of flags that can be or'ed together. The flag FLEXDLL_RTLD_GLOBAL means that the symbols exported by the opened DLL can be used to resolve relocations for DLLs to be opened later on. The flag FLEXDLL_RTLD_NOEXEC opens the DLL is a special mode, disabling the automatic loading of dependencies and the FlexDLL resolution pass. This is useful if you want to open a DLL only to check whether it defines some symbol.

The flexdll_dlopen function returns a pointer to an opaque handle that can be used as an argument to the other API functions. If the filename is NULL, the function returns a special handle which refers to the global unit: it includes all the static symbols, plus the symbols from the DLLs opened with the FLEXDLL_RTLD_GLOBAL flag. A given DLL will be opened only once, even if you call the function several times on the same file. The FLEXDLL_RTLD_GLOBAL flag is sticky: if one of the calls mentions it, it will stay forever, even if the corresponding handle is then passed to dlclose (this is because the same handle is actually returned for all the calls). If an error occurs during the call to flexdll_dlopen, the functions returns NULL and the error message can be retrieved using flexdll_dlerror.

The function flexdll_wdlopen is a wide-character version of flexdll_dlopen. The filename argument to flexdll_wdlopen is a wide-character string. flexdll_wdlopen and flexdll_dlopen behave identically otherwise.

The second most important function is flexdll_dlsym which looks for a symbol whose name is the second argument. The first argument can be either a regular handle returned by flexdll_dlopen (the symbol is searched only in the corresponding DLL), the special handle for the global unit as return by a call to flexdll_dlopen(NULL,...) (the symbol is searched amongst the static symbols plus the ones in the DLL opened with the flag FLEXDLL_RTLD_GLOBAL), or NULL (the symbol is searched only amongst the static symbols). If the symbol cannot be found, the function returns NULL.

The same symbol name can be defined several times. The policy used to choose amongst the various definitions is not specified. This applies both to the automatic resolution that happens when you open a DLL and to the explicit resolution performed by dlsym.

The function flexdll_dlclose must be used with caution. It decrements the reference counter for the given handle and releases the DLL from memory when the counter reaches 0. After that time, symbols defined in this DLL are no longer used for resolution. You most probably don't want to close a DLL if you still hold pointers to some of its symbols.

The two functions flexdll_dump_exports and flexdll_dump_relocations are used to display (to the standard output) the internal tables associated with a given DLL handle.

Command line for the flexlink wrapper

Usage:
  flexlink -o <result.dll> file1.obj file2.obj ... -- <extra linker arguments>

  -o                  Choose the name of the output file
  -exe                Link the main program as an exe file
  -maindll            Link the main program as a dll file
  -noflexdllobj       Do not add the Flexdll runtime object (for exe)
  -noentry            Do not use the Flexdll entry point (for dll)
  -noexport           Do not export any symbol
  -I <dir>            Add a directory where to search for files
  -L <dir>            Add a directory where to search for files
  -l <lib>            Library file
  -chain {msvc|msvc64|cygwin64|mingw|mingw64|ld}
                      Choose which linker to use
  -defaultlib <obj>   External object (no export, no import)
  -save-temps         Do not delete intermediate files
  -implib             Do not delete the generated import library
  -outdef             Produce a def file with exported symbols
  -v                  Increment verbosity (can be repeated)
  -show-exports       Show exported symbols
  -show-imports       Show imported symbols
  -dry                Show the linker command line, do not actually run it
  -dump               Only dump the content of object files
  -nocygpath          Do not use cygpath (default for msvc)
  -cygpath            Use cygpath (default for cygwin)
  -no-merge-manifest  Do not merge the manifest (takes precedence over -merge-manifest)
  -merge-manifest     Merge manifest to the dll or exe (if generated)
  -real-manifest      Use the generated manifest (default behavior)
  -default-manifest   Use the default manifest (default.manifest/default_amd64.manifest)
  -export <sym>       Explicitly export a symbol
  -noreexport         Do not reexport symbols imported from import libraries
  -where              Show the FlexDLL directory
  -nounderscore       Normal symbols are not prefixed with an underscore
  -nodefaultlibs      Do not assume any default library
  -builtin            Use built-in linker to produce a dll
  -explain            Explain why library objects are linked
  -subsystem <id>     Set the subsystem (default: console)
  -custom-crt         Use a custom CRT
  -link <option>      Next argument is passed verbatim to the linker
  -D <symbol>         (Ignored)
  -U <symbol>         (Ignored)
  --                  Following arguments are passed verbatim to the linker
  -help               Display this list of options
  --help              Display this list of options

The files given on the command line can be object files (.obj/.o), library files (.a/.lib), or C files (.c). C files will be compiled using the toolchain's C compiler and the resulting object will be used for the actual linking.

The argument for the -l, -I and -L options does not need to be separated by whitespace from the option (i.e. -LXXX is equivalent to -L XXX).

There is a single set of search directories for all kinds of files. The -I and -L options are synonyms.

As usual, object files included in libraries are linked in only if one of the symbol they export is needed.

The -export option explicitly exports a symbol. If this symbols is found in a library, it will force the corresponding object file to be included.

The -defaultlib option is used to tell flexlink that some object (usually, a library) does not need any relocations (that is, all the symbols it refers to are defined in one of the objects being linked) and that we don't want to re-export the symbols exported by this object. This is usually used for system libraries.

The -cygpath and -nocygpath options control whether flexlink uses the cygpath command or not (default is: no under MSVC, yes under Cygwin/MinGW if cygpath can be found in the PATH). When it uses cygpath, flexlink tries to resolve file names directly and otherwise calls cygpath -m (to produce Windows paths from Cygwin paths) if cygpath is available in the path.

The -maindll option is used to build a DLL that behaves as the main program from the point of view of FlexDLL. It cannot have unresolved symbols.

By default, flexlink looks for FlexDLL's object files in the same directory as flexlink.exe itself. It is possible to specify another directory with the FLEXDIR environment variable.

Extra arguments can be passed to flexlink.exe through the environment variable FLEXLINKFLAGS. The arguments coming from this variable are parsed before those coming from the command line.

Performance

FlexDLL performs relocations at runtime in the code of the DLL. The good consequence is that there is no indirection: a function call or a reference to a global variable where the target symbol is not in the current DLL be compiled as if it were. This might improve performance, especially because an indirection would consume a register that might be better used for something more interesting. The bad consequence is that the memory pages that contains relocations cannot be shared between different processes.

Bugs, limitations

FlexDLL relies on a parser and generator for COFF files. However, some features are not very well specified, and some well specified features have not been fully implemented. Normally, you should get some assertion failure in these cases. Please report them, so that I can improve FlexDLL.

FlexDLL works for 32 and 64 bits version of Windows. The 32 bits version has been tested under XP and Vista, with the three supported toolchains. The 64 bits version has been tested under Windows Vista x64 with the Microsoft Platform SDK and under Windows 7 64-bit with the Win7 SDK toolchain (no Cygwin).

Real-world examples

Please let me know if you use FlexDLL!

Dynamic loading for OCaml

The initial motivation for FlexDLL was to add dynamic linking of native code to Windows ports of OCaml (Cygwin, MinGW, MSVC). A side-effect was to simplify the dynamic loading of C libraries (e.g. for the toplevel) and to make it work under the Cygwin port, to simplify Makefiles of libraries (now shared between Unix and Windows ports), and to create a native toplevel.

Links

flexdll's People

Contributors

alainfrisch avatar bryphe avatar bschommer avatar damiendoligez avatar db4 avatar dra27 avatar elfmimi avatar jmid avatar jonahbeckford avatar kanigsson avatar misterda avatar msoegtropimc avatar nevor avatar nojb avatar sbriais avatar shym avatar yakobowski avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

flexdll's Issues

Build against ocaml-4.08.0+beta3 fails

Build against ocaml-4.08.0+beta3 fails as Pervasives.compare is no longer available.

This fix worked for my specific situation:

--- reloc.ml.orig	2019-04-24 09:44:22.081768600 -0700
+++ reloc.ml	2019-04-24 09:46:11.136207400 -0700
@@ -498,7 +498,7 @@
   let strings = Buffer.create 1024 in
   let strsym = Symbol.intern sect 0l in
   obj.symbols <- strsym :: (Symbol.export symname sect 0l) :: obj.symbols;
-  let exports = List.sort Pervasives.compare exports in
+  let exports = List.sort compare exports in
   (* The runtime library assumes that names are sorted! *)
   int_to_buf data (List.length exports);
   List.iter

suggestion: detect obj wordsize mismatching chain type

I was just stumped for a while by a dumb mistake: I used -chain msvc to process x64 .obj files. The error that manifested was Unsupported relocation kind 0008; it would be nice if I had got .obj wordsize (64) mismatches selected chain wordsize (32) instead.

Maybe there's a technical reason for this check not being present, but I think it's more likely nobody's ever done anything as stupid as what I did before.

mingw64 (and presumably mingw) fails on French-language Windows 10

This is the output of x86_64-w64-mingw32-gcc -print-search-dirs on a French-configured windows 10:

installés: /usr/lib/gcc/x86_64-w64-mingw32/5.4.0/
programmes : =/usr/lib/gcc/x86_64-w64-mingw32/5.4.0/:/usr/lib/gcc/x86_64-w64-mingw32/5.4.0/:/usr/lib/gcc/x86_64-w64-mingw32/:/usr/lib/gcc/x86_64-w64-mingw32/5.4.0/:/usr/lib/gcc/x86_64-w64-mingw32/:/usr/lib/gcc/x86_64-w64-mingw32/5.4.0/../../../../x86_64-w64-mingw32/bin/x86_64-w64-mingw32/5.4.0/:/usr/lib/gcc/x86_64-w64-mingw32/5.4.0/../../../../x86_64-w64-mingw32/bin/
libraries : =/usr/lib/gcc/x86_64-w64-mingw32/5.4.0/:/usr/lib/gcc/x86_64-w64-mingw32/5.4.0/../../../../x86_64-w64-mingw32/lib/x86_64-w64-mingw32/5.4.0/:/usr/lib/gcc/x86_64-w64-mingw32/5.4.0/../../../../x86_64-w64-mingw32/lib/../lib/:/usr/x86_64-w64-mingw32/sys-root/mingw/lib/x86_64-w64-mingw32/5.4.0/:/usr/x86_64-w64-mingw32/sys-root/mingw/lib/../lib/:/usr/lib/gcc/x86_64-w64-mingw32/5.4.0/../../../../x86_64-w64-mingw32/lib/:/usr/x86_64-w64-mingw32/sys-root/mingw/lib/

flexlink fails to parse it because of the space between libraries and :

I don't know who had the bright idea of localizing this part of gcc but it looks like you'll need to adapt.

Option -custom-crt should not add MSVC linker option /nodefaultlib:LIBCMT

When one wants to use LIBCMT.lib in place of default CRT library MSVCRT.lib, it is not possible with FlexDLL v0.35, because option -custom-crt prevents both to use MSVCRT.lib and LIBCMT.lib.
It should only disable usage of MSVCRT.lib.

See reloc.ml lines 1005-1006:
if !custom_crt then "/nodefaultlib:LIBCMT /nodefaultlib:MSVCRT " ^ extra_args
else "msvcrt.lib " ^ extra_args
should be:
if !custom_crt then "/nodefaultlib:MSVCRT " ^ extra_args
else "msvcrt.lib " ^ extra_args

Thanks

msvc linker response file should be UTF-16 encoded when possible

Currently we use a response file to pass arguments to the msvc linker when the command line is too long.

Supported encodings for the response file are ANSI (currently used) and UTF-16: see https://msdn.microsoft.com/en-us/library/xwy0e8f2(v=vs.140).aspx.

In order to make flexlink work with Unicode (cf ocaml/ocaml#1200) we need to use UTF-16 (here I am thinking about the situation when our OCaml strings are UTF-8). I made a quick proof of concept with a handwritten UTF-8 decoder and it fixed the problems I was seeing.

Ideally this functionality would be enabled as soon as flexlink is compiled with a Unicode-enabled ocamlc, but am not sure if this is easy to do.

Ideas ?

[Question] How does Flexdll handling name mangling

As a follow up to #75, I'm using MSVC toolchain all through out, to compile Skia bindings.

  1. skia.lib is compiled with MSVC as described in the official docs
  2. Skia APIs are written in C++ and dont have extern C. So the stubs are written in C++ by hand (no ctypes) contain the extern C prefixes.
  3. I'm using fdopen's opam in cygwin with MSVC switch. OCaml 4.07.1

Flexlink complains of unresolved symbols from Skia. Looks like a name mangling issue.

** Cannot resolve symbols for src\libskia_stubs.lib(skia.obj):
 ?setStrokeJoin@SkPaint@@QEAAXW4Join@1@@Z
 ?setStrokeMiter@SkPaint@@QEAAXM@Z
 ?setStrokeWidth@SkPaint@@QEAAXM@Z
 ?setStyle@SkPaint@@QEAAXW4Style@1@@Z
 ?setSubpixel@SkFont@@QEAAX_N@Z
 ?sk_abort_no_print@@YAXXZ
 ?toSkColor@?$SkRGBA4f@$02@@QEBAIXZ
File "caml_startup", line 1:
Error: Error during linking

(truncated for brevity)

stubs.cpp (the bindings) is linked against skia.lib

@lib.exe /out:bindings.lib bindings.obj %esy_skia_install%/out/Release/skia.lib

And this library file is passed to ocamlmklib

ocamlmklib -o skia_stubs bindings.lib

Tiny visual aid

 libskia_stubs <- bindings.lib (contain extern C) <- skia.lib (no extern C)

Can flexlink handling name mangling correctly in this case? Alternatively, how we use flexlink to link to C++ APIs that don't ship with extern "C"

"Cannot relocate" error with flexdll in OCaml for Windows 4.11 (and older versions)

Hello,

I just installed OCaml for Windows 4.11.1 (4.11.1+mingw64c) and, as before, I tried to install and run the frama-c package, which uses dynamic loading for its plugins. Compilation worked fine, but trying to run it fails with:

[kernel] User Error: cannot load plug-in 'num.core': cannot load module
  Details: error loading shared library: Dynlink.Error (Dynlink.Cannot_open_dll "Failure(\"flexdll error: cannot relocate RELOC_REL32, target is too far: ffffffff6a110d73  000000006a110d73\")")
[kernel] User Error: cannot load plug-in 'zip': cannot load module
  Details: error loading shared library: Dynlink.Error (Dynlink.Cannot_open_dll "Failure(\"flexdll error: cannot relocate RELOC_REL32, target is too far: fffffffe488b648d  00000000488b648d\")")
[kernel] User Error: cannot load plug-in 'why3': cannot load module
  Details: error loading shared library: Dynlink.Error (Dynlink.Cannot_open_dll "Failure(\"Cannot resolve camlGzip\")")
[kernel] User Error: cannot load plug-in 'frama-c-wp': cannot load module
  Details: error loading shared library: Dynlink.Error (Dynlink.Cannot_open_dll "Failure(\"Cannot resolve camlWhy3__Ident\")")

I had never seen this issue before; frama-c worked with several previous versions of the OCaml compiler. After testing, I noticed it also happens with older versions of Frama-C, and with OCaml as old as 4.08.1 (I didn't have the time to test other versions; each compilation takes about 1 hour...). So I wonder if something changed on Windows or in flexdll. The same issue happened to one of our users, so it's not just my machine. I'm not sure what to do on the Frama-C side to debug it.

Are there any known issues issues related to the "flexdll error: cannot relocate RELOC_REL32, target is too far" message? Would you have any suggestions on how to proceed to debug this?

By the way, running flexlink -help displays FlexDLL version 0.38fdopen1; so I'm not sure I should contact fdopen directly to ask for help; sorry if the issue is unrelated to you.

MSVC flexlink appears to attempt cygpath

From a report on a private channel, flexlink appears to fallback to calling cygpath if it fails to find .lib files (e.g. ws2_32.lib).

Possibly repro case is to configure OCaml as normal, then unset LIB and attempt to build.

Not clear if this is a regression, possibly from #118, or if it's just always been wrong.

flexlink produces an invalid dll when building lablgtk-2.18.3 on mingw64

Hi all,

I'm recently building lablgtk (a GTK2 wrapper for OCaml) using mingw64 toolchains provided by msys2. The package uses ocamlmklib (and thus flexlink) to create a dll library called dlllablgtk2.dll. Here are the version of the tools in my environment:

flexdll    0.34 (from http://alain.frisch.fr/flexdll.html; built from source)
ocaml    4.02.1 (built from source)

Flexlink generates the library without error, but the library is considered invalid by LoadLibraryEx:

Error: Error on dynamically loaded library: .\dlllablgtk2.dll: %1 is not a valid win32 application

The following toy program gives the same result.

$ cat testdll.c
#include <flexdll.h>
#include <stdio.h>
#include <windows.h>

int main(int argc, char *argv[]) {
    void *handle;
    printf("Try open: %s\n", argv[1]);
    handle = flexdll_dlopen(argv[1], FLEXDLL_RTLD_GLOBAL);
    printf("Handle: %p\n", handle);
    if (handle == NULL) {
            printf("Error code: %d\n", GetLastError());
            printf("Error message: %s\n", flexdll_dlerror());
    }
    return 0;
}

$ flexlink -chain mingw64 -exe -o testdll testdll.c
$ testdll.exe dlllablgtk2.dll
Try open: dlllablgtk2.dll
Handle: 0000000000000000
Error code: 193
Error message: %1 is not a valid win32 application

The library is created using 24 object files in addition to some system libraries. The command is:

flexlink -v -v -chain mingw64 -LD:/msys64/mingw64/x86_64-w64-mingw32/lib \
-o dlllablgtk2.dll -lpthread -LD:/msys64/mingw64/lib -lgtk-win32-2.0 \
-limm32 -lshell32 -lole32 -lpangocairo-1.0 -lpangoft2-1.0 -lpangowin32-1.0 -lgdi32 \
-lpango-1.0 -lm -latk-1.0 -lcairo -lpixman-1 -lfontconfig -lexpat -lfreetype -lexpat -lfreetype \
-lbz2 -lharfbuzz -lgdk_pixbuf-2.0 -lpng16 -lgio-2.0 -lz -lgmodule-2.0 -lgobject-2.0 -lffi \
-lglib-2.0 -lws2_32 -lole32 -lwinmm -lshlwapi -lintl \
ml_gobject.o ml_gpointer.o ml_gtk.o ml_gtkaction.o ml_gtkbin.o ml_gtkbroken.o ml_gtkbutton.o \
ml_gtkassistant.o ml_gtkedit.o ml_gtkfile.o ml_gtklist.o ml_gtkmenu.o ml_gtkmisc.o ml_gtkpack.o \
ml_gtkrange.o ml_gtkstock.o ml_gtktext.o ml_gtktree.o ml_gdkpixbuf.o ml_gdk.o ml_glib.o \
ml_pango.o ml_gvaluecaml.o wrappers.o

When I remove some of the objects (e.g. ml_gtktree.o), the generated library becomes valid.

$ testdll.exe dlllablgtk2.dll        # ml_gtktree.o removed from the command
Try open: dlllablgtk2.dll
Handle: 0000000000000000
Error code: 1114
Error message: Cannot resolve caml_failwith

It seems the issue is not raised by a single object. The library built without ml_gtktext.o (but with ml_gtktree.o) is also valid.

The binaries from https://github.com/shadinger/flexdll-win64 (version 0.26) does not suffer from this issue.

Here is the verbose log during linking.

** Use cygpath: true
** Search path:
D:/msys64/mingw64/lib
D:/msys64/mingw64/x86_64-w64-mingw32/lib
D:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/4.9.2
/mingw/lib
/mingw64/x86_64-w64-mingw32/lib
** Default libraries:
dllcrt2.o
-lmingw32
-lgcc
-lmoldname
-lmingwex
-lmsvcrt
-luser32
-lkernel32
-ladvapi32
-lshell32
** open: D:/msys64/mingw64/x86_64-w64-mingw32/lib\dllcrt2.o
** open: D:/msys64/mingw64/x86_64-w64-mingw32/lib\libmingw32.a
** open: D:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/4.9.2\libgcc.a
** open: D:/msys64/mingw64/x86_64-w64-mingw32/lib\libmoldname.a
** open: D:/msys64/mingw64/x86_64-w64-mingw32/lib\libmingwex.a
** open: D:/msys64/mingw64/x86_64-w64-mingw32/lib\libmsvcrt.a
** open: D:/msys64/mingw64/x86_64-w64-mingw32/lib\libuser32.a
** open: D:/msys64/mingw64/x86_64-w64-mingw32/lib\libkernel32.a
** open: D:/msys64/mingw64/x86_64-w64-mingw32/lib\libadvapi32.a
+ x86_64-w64-mingw32-gcc -mconsole -shared -Wl,-eFlexDLLiniter  -L. -I"D:/msys64/mingw64/lib" -I"D:/msys64/mingw64/x86_64-w64-mingw32/lib" -L"D:/msys64/mingw64/lib" -L"D:/msys64/mingw64/x86_64-w64-mingw32/lib" -o "test.dll" "D:\msys64\tmp\dyndll3ef3ef.o" "D:\msys64\mingw64\bin\flexdll_initer_mingw64.o" "D:/msys64/mingw64/x86_64-w64-mingw32/lib\libpthread.dll.a" "D:/msys64/mingw64/lib\libgtk-win32-2.0.dll.a" "D:/msys64/mingw64/x86_64-w64-mingw32/lib\libimm32.a" "D:/msys64/mingw64/x86_64-w64-mingw32/lib\libshell32.a" "D:/msys64/mingw64/x86_64-w64-mingw32/lib\libole32.a" "D:/msys64/mingw64/lib\libpangocairo-1.0.dll.a" "D:/msys64/mingw64/lib\libpangoft2-1.0.dll.a" "D:/msys64/mingw64/lib\libpangowin32-1.0.dll.a" "D:/msys64/mingw64/x86_64-w64-mingw32/lib\libgdi32.a" "D:/msys64/mingw64/lib\libpango-1.0.dll.a" "D:/msys64/mingw64/x86_64-w64-mingw32/lib\libm.a" "D:/msys64/mingw64/lib\libatk-1.0.dll.a" "D:/msys64/mingw64/lib\libcairo.dll.a" "D:/msys64/mingw64/lib\libpixman-1.dll.a" "D:/msys64/mingw64/lib\libfontconfig.dll.a" "D:/msys64/mingw64/lib\libexpat.dll.a" "D:/msys64/mingw64/lib\libfreetype.dll.a" "D:/msys64/mingw64/lib\libbz2.dll.a" "D:/msys64/mingw64/lib\libharfbuzz.dll.a" "D:/msys64/mingw64/lib\libgdk_pixbuf-2.0.dll.a" "D:/msys64/mingw64/lib\libpng16.dll.a" "D:/msys64/mingw64/lib\libgio-2.0.dll.a" "D:/msys64/mingw64/lib\libz.dll.a" "D:/msys64/mingw64/lib\libgmodule-2.0.dll.a" "D:/msys64/mingw64/lib\libgobject-2.0.dll.a" "D:/msys64/mingw64/lib\libffi.dll.a" "D:/msys64/mingw64/lib\libglib-2.0.dll.a" "D:/msys64/mingw64/x86_64-w64-mingw32/lib\libws2_32.a" "D:/msys64/mingw64/x86_64-w64-mingw32/lib\libwinmm.a" "D:/msys64/mingw64/x86_64-w64-mingw32/lib\libshlwapi.a" "D:/msys64/mingw64/lib\libintl.dll.a" "D:\msys64\tmp\dyndll00be4c.o" "D:\msys64\tmp\dyndlle902c0.o" "D:\msys64\tmp\dyndll54d32d.o" "D:\msys64\tmp\dyndll2e0163.o" "ml_gtkbin.o" "D:\msys64\tmp\dyndll7ac0f6.o" "D:\msys64\tmp\dyndll3f46a1.o" "D:\msys64\tmp\dyndll6e7d00.o" "D:\msys64\tmp\dyndll709dae.o" "D:\msys64\tmp\dyndll4b5dee.o" "D:\msys64\tmp\dyndll027612.o" "D:\msys64\tmp\dyndll478b19.o" "D:\msys64\tmp\dyndll0fdffc.o" "D:\msys64\tmp\dyndll533488.o" "D:\msys64\tmp\dyndllc5412c.o" "D:\msys64\tmp\dyndllb81a8b.o" "D:\msys64\tmp\dyndll5f1731.o" "D:\msys64\tmp\dyndll4bc469.o" "D:\msys64\tmp\dyndlleed2db.o" "D:\msys64\tmp\dyndlla2929b.o" "D:\msys64\tmp\dyndll56c73c.o" "D:\msys64\tmp\dyndllde988f.o" "ml_gvaluecaml.o" "D:\msys64\tmp\dyndll049034.o" "D:\msys64\tmp\flexlink250fe6.def"
(call with bash: D:\msys64\tmp\longcmd233aa5)

findlib sometimes cannot be built on Windows

I'm not sure if this is the right place to report this, but there seems to be a nondeterministic issue with the setup of OCaml/opam on Windows:

<><>Processing actions<><><><><><><><><><><><><><><><><><><><><><><><><><><><>
[ERROR] The compilation of ocamlfind failed at "D:\\cygwin\\bin\\make.exe opt".
-> installed conf-findutils.1
-> installed conf-gmp.4
-> installed dune.3.3.1
#===ERRORwhile compiling ocamlfind.1.9.5====================================#
#context     2.0.10 | win32/x86_64 | ocaml-variants.4.11.1+mingw64c | git+https://github.com/fdopen/opam-repository-mingw.git#opam2
#path        D:/a/fiat-crypto/fiat-crypto/_opam/.opam-switch/build/ocamlfind.1.9.5
#command     D:\cygwin\bin\make.exe opt
#exit-code   2
#env-file    D:/.opam/log/ocamlfind-1[38](https://github.com/mit-plv/fiat-crypto/runs/7199984869?check_suite_focus=true#step:14:39)4-03b1a4.env
#output-file D:/.opam/log/ocamlfind-1384-03b1a4.out
### output ###
# for p in findlib; do ( cd src/$p; /usr/bin/make opt ) || exit; done
# make[1]: Entering directory '/cygdrive/d/a/fiat-crypto/fiat-crypto/_opam/.opam-switch/build/ocamlfind.1.9.5/src/findlib'
# ocamlopt -I +compiler-libs -g -opaque  -I +unix -I +dynlink -c findlib_config.ml
# ocamlopt -I +compiler-libs -g -opaque  -I +unix -I +dynlink -c fl_split.ml
# ocamlopt -I +compiler-libs -g -opaque  -I +unix -I +dynlink -c fl_metatoken.ml
# ocamlopt -I +compiler-libs -g -opaque  -I +unix -I +dynlink -c fl_meta.ml
# ocamlopt -I +compiler-libs -g -opaque  -I +unix -I +dynlink -c fl_metascanner.ml
# ocamlopt -I +compiler-libs -g -opaque  -I +unix -I +dynlink -c fl_topo.ml
# ocamlopt -I +compiler-libs -g -opaque  -I +unix -I +dynlink -c fl_package_base.ml
# ocamlopt -I +compiler-libs -g -opaque  -I +unix -I +dynlink -c findlib.ml
# ocamlopt -I +compiler-libs -g -opaque  -I +unix -I +dynlink -c fl_args.ml
# ocamlopt -I +compiler-libs -g -opaque  -I +unix -I +dynlink -c fl_lint.ml
# ocamlopt -I +compiler-libs -g -a -o findlib.cmxa findlib_config.cmx fl_split.cmx fl_metatoken.cmx fl_meta.cmx fl_metascanner.cmx fl_topo.cmx fl_package_base.cmx findlib.cmx fl_args.cmx fl_lint.cmx
# if [ 1 -gt 0 ]; then \
#     ocamlopt -I +compiler-libs -g -shared -o findlib.cmxs findlib_config.cmx fl_split.cmx fl_metatoken.cmx fl_meta.cmx fl_metascanner.cmx fl_topo.cmx fl_package_base.cmx findlib.cmx fl_args.cmx fl_lint.cmx; \
# fi
# ** Fatal error: Cannot run cygpath -m findlib.cmxs
# File "caml_startup", line 1:
# Error: Error during linking (exit code 2)
# make[1]: *** [Makefile:71: findlib.cmxa] Error 2
# make[1]: Leaving directory '/cygdrive/d/a/fiat-crypto/fiat-crypto/_opam/.opam-switch/build/ocamlfind.1.9.5/src/findlib'
# make: *** [Makefile:18: opt] Error 2
<><>Error report<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
+- The following actions failed
| - build ocamlfind 1.9.5
+- 
+- The following changes have been performed (the rest was aborted)
| - install conf-findutils 1
| - install conf-gmp       4
| - install dune           3.3.1
+- 
[NOTE] Pinning command successful, but your installed packages may be out of sync.

https://github.com/mit-plv/fiat-crypto/runs/7199984869?check_suite_focus=true#step:14:68

Temporary files not deleted on exit

After a full build of OCaml 5 with the MinGW toolchain, I see about 30 dyndllXXXXXX.o files left over in /tmp.

I suspect these files were created by flexlink.

On CI machines, after a few hundred builds, /tmp becomes so cluttered as to slow down builds noticeably and cause problems with ssh logins.

flexlink should not use constant base address for Cygwin64

flexlink passes --image-base 0x10000 to the linker for Cygwin64 in order to ensure that relocations are always within a 32-bit range. This is causing problem when multiple DLLs are loaded by a single process (especially dllthreads.so and dllunix.so).

There is also the problem that nothing prevents Cygwin's rebaseall from re-setting the base address to something higher.

I think that two changes are ultimately required:

  • Some kind of DLL flag which Cygwin's rebase utility recognises as requiring a 32-bit compatible base address (this needs more detail - especially as regarding how the present Cygwin64 ocaml package gets around this)
  • flexlink needs to do whatever the linker would normally do to determine a "random" base address for the DLL, but with the added caveat of keeping it in a 32-bit range. Having done this, the change may as well be used for the MSVC64 and mingw64 ports, but it's not as critical.

No solutions so far, but this issue keeps cropping up from time-to-time and as far as I know it's not being tracked anywhere.

Compile ERROR with MSVC VS2019

I got 2 unresolved symbols during linking with MSVC VS2019:
static_symtable
reloctbl

I find that the two symbols are both extern declared in .c file but no any reference.
So, I change them to static declare and it can pass compiling and linking with my project.
However, when I try to load a DLL in windows, flexdll cannot fetch any symbols in my DLL, flexdll_dlsym API return NULL.
I try dumpbin.exe tool in widnows, and it truely got correct symbols.

Now, I begin getting confused, HOW can I use flexdll in my VS2019 project?

[Question] Linking MSVC libraries against MinGW

Can flexlink link MinGW and MSVC libraries to produce and exe/dll?

We are trying to write bindings for Skia (https://github.com/manuhornung/reason-skia)
and the it is recommended that we built Skia with MSVC on Windows (we are exploring mingw too)

Our first attempts were to compile the bindings with the mingw toolchain given the ecosystem support it has. To test these bindings we link the bindings into a test binary too (https://github.com/manuhornung/reason-skia/tree/master/bin_native)

Keeping in mind that the Skia library itself is an MSVC COFF, and that the bindings are being compiled with MinGW toolchain, I faced issues at two levels.

Compiling and linking the test binary with MinGW backed compiler, flexlink complained about unresolved MSVC symbols

** Cannot resolve symbols for C:\Users\manas\development\reason-skia\_esy\default\store\b\reason_skia-84a967d7\default\src\skia.lib(skia.obj):
 ??0SkFILEWStream@@QEAA@QEBD@Z
 ??0SkFont@@QEAA@V?$sk_sp@VSkTypeface@@@@MMM@Z
 ??0SkPaint@@QEAA@XZ
??0SkSurfaceProps@@QEAA@IW4SkPixelGeometry@@@Z

Those symbols are from skia.cpp stubs in reason-bindings

On trying to link with flexlink compiled with MSVC toolchain, I faced the following

** Cannot resolve symbols for C:\users\manas\development\flexdll\flexdll_msvc.obj:
 _memcpy
 _strcmp
 _strlen
** Cannot resolve symbols for C:\Users\manas\.esy\3_\i\ocaml-4.6.10-7ba95816\lib\ocaml\libasmrun.a(floats.o):
 __strtod
Fatal error: exception Failure("Unsupported relocation kind 0004 for __strtod in C:\\Users\\manas\\.esy\\3_\\i\\ocaml-4.6.10-7ba95816\\lib\\ocaml\\libasmrun.a(floats.o)")

I did so by cloning and checking out latest stable tag, and simply ran the linking subcommand that ocamlopt uses on Windows. Note that it is still the MinGW built OCaml compiler. So flexlink failed to link the MinGW archive against skia.lib (MSVC COFF)

  1. Does flexlink account for any kind of binary compatibility so that it can link the two libraries (from different compilers). I understand the chances are slim and MinGW itself very vaguely says yes.

  2. Is flexlink absolutely necessary for ocamlopt on Windows even for static linking?

  3. Is it wrong to expect flexlink to link MinGW and MSVC libraries together? And that we should have used a the compiler built with MSVC toolchain from the get-go?

Fail to find a function in the executable (pg_query)

When compiling pg_query on Windows (patched to make it compiled), I have a pg_check command which fails:

Fatal error: exception Dl.DL_error("dlsym: no such symbol: \"pg_query_parse\"")

However, the pg_query_parse symbol is available:

$nm /cygdrive/c/Users/frede/AppData/Local/opam/default/bin/pg_check|grep pg_query_parse
00000001400c61c0 T pg_query_parse
00000001400c6250 T pg_query_parse_opts
00000001400c62e0 T pg_query_parse_protobuf
00000001400c6350 T pg_query_parse_protobuf_opts

Note, the pg_query library is based on ctypes.foreign. Then the issue may appear in other libraries.

Get rid of flexdll

It says in the notes that on Windows DLLs can't refer to symbols in the executable or previously loaded DLLs. The latter claim is not quite correct, it should say implicitly.

This is not actually a problem, it is correct behaviour and should NOT be fixed. Instead ocaml should be fixed. The issue is due to the historical development of Unix. Modern Unix linkers allow the archaic incorrect behaviour for compatibility but are moving away from it. OSX has moved well away and Linux is catching up with Windows too.

The correct way to link is to explicitly refer to a library. As mentioned, on Windows you have to link against a library to refer to its symbols because Windows actually uses a linker which has NO SUPPORT AT ALL for dynamic linkage. Windows always used a linker which could only do static linkage, so special import libraries are built which you link against instead. Calls to the symbols in the library trigger run time dynamic loading of a DLL and binding of the symbol at that time.

Linux and OSX also provide visibility control now and two level linkage, something that should have been enforced a long time ago. Unfortunately C and more recently C++ have impeded correcting the age old design fault. Still, it works right on OSX.

The question is: how can we build Ocaml correctly so we don't need flexdll? Currently I get the tiresome error "** Cannot resolve symbols for C:\ocaml\lib\libasmrun.lib(win32.obj): flexdll_wdlopen". I did have it working for Ocaml 4.02 but this is an upgrade someone built for Ocaml 4.06. "ocaml" works fine, the problem is only for ocamlopt.

flexlink should cygpath %TEMP% before passing to bash

cd _boot && C:\ocamlmgw64\bin\ocamlopt.opt.exe -o ..\dune.exe -g -I +threads unix.cmxa threads.cmxa -no-alias-deps -w -49 fcntl_stubs.c -args mods_list
/bin/bash: C:UsersDRAAppDataLocalTemplongcmd7285c9: No such file or directory

Placeholder to check when I have more time, but changing TEMP to C:/Users/DRA/AppData/Local/Temp "fixed" the problem

reloc.ml: should not use String.lowercase_ascii

While investigating an issue related to ocaml/ocaml#1200, I noticed that there are several uses of String.lowercase_ascii applied to filenames in the module reloc.ml. It would be preferable not to use this function since those filenames may be UTF-8-encoded in the future.

Support for /alternatename: linker directive needed for x86

I can see the problem clearly in 32-bit code, although it doesn't look like it is limited to 32-bit code.

Here is the invocation of flexlink 0.42 (edited to split newlines):

flexlink.exe ^
-o src\ActorSystem\system.exe -U /out:src\ActorSystem\system.exe -implib ^
-L C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.26.28801\lib\x86 ^
-L C:\Program Files (x86)\Windows Kits\NETFXSDK\4.8\lib\um\x86 ^
-L C:\Program Files (x86)\Windows Kits\10\lib\10.0.19041.0\ucrt\x86 ^
-L C:\Program Files (x86)\Windows Kits\10\lib\10.0.19041.0\um\x86 ^
-L Z:\source\DkHelloWorldActor\build_community\DkSDKFiles\o\s\o\lib\ocaml ^
src\ActorSystem\DkHelloWorldActor_system.lib advapi32.lib ws2_32.lib version.lib -exe ^
-no-merge-manifest -custom-crt msvcrt.lib -chain msvc -stack 16777216 ^
-L Z:\source\DkHelloWorldActor\build_community\DkSDKFiles\o\s\o\lib\ocaml src\ActorSystem\DkHelloWorldActor_system.lib ^
advapi32.lib ws2_32.lib version.lib libasmrund.lib src\ActorSystem\CMakeFiles\system.dir\_main.c.obj ^
-- ^
/nologo /pdb:src\ActorSystem\system.pdb ^
/version:0.0 /ENTRY:wmainCRTStartup /machine:X86 /debug /INCREMENTAL /subsystem:console ^
/MANIFEST /MANIFESTFILE:src\ActorSystem\CMakeFiles\system.dir/intermediate.manifest ^
src\ActorSystem\CMakeFiles\system.dir/manifest.res

which gives:

failed (exit code 2) with the following output:
** Cannot resolve symbols for C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.26.28801\lib\x86\msvcrt.lib(d:\agent\_work\2\s\Intermediate\vctools\msvcrt.nativeproj_110336922\objr\x86\chandler4gs.obj):
 __filter_x86_sse2_floating_point_exception

The root cause seems to be the Linker Directives:

dumpbin /all 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.26.28801\lib\x86\msvcrt.lib' | grep -B100 -10 -E 'Archive member name|filter_x86_sse2'

...
   4950F4 __filter_x86_sse2_floating_point_exception_default
...
      4D6 __filter_x86_sse2_floating_point_exception_default
...
Archive member name at 65B67A: /124277         d:\agent\_work\2\s\Intermediate\vctools\msvcrt.nativeproj_110336922\objr\x86\chandler4gs.obj
5EA42E04 time/date Sat Apr 25 05:33:08 2020
         uid
         gid
  100666 mode
    54F8 size
correct header end

FILE HEADER VALUES
             14C machine (x86)
               7 number of sections
        5EA42E04 time date stamp Sat Apr 25 05:33:08 2020
            52C5 file pointer to symbol table
              16 number of symbols
               0 size of optional header
               0 characteristics

SECTION HEADER #1
.drectve name
       0 physical address
       0 virtual address
      70 size of raw data
     12C file pointer to raw data (0000012C to 0000019B)
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
  100A00 flags
         Info
         Remove
         1 byte align

RAW DATA #1
  00000000: 20 20 20 2F 61 6C 74 65 72 6E 61 74 65 6E 61 6D     /alternatenam
  00000010: 65 3A 5F 5F 66 69 6C 74 65 72 5F 78 38 36 5F 73  e:__filter_x86_s
  00000020: 73 65 32 5F 66 6C 6F 61 74 69 6E 67 5F 70 6F 69  se2_floating_poi
  00000030: 6E 74 5F 65 78 63 65 70 74 69 6F 6E 3D 5F 5F 66  nt_exception=__f
  00000040: 69 6C 74 65 72 5F 78 38 36 5F 73 73 65 32 5F 66  ilter_x86_sse2_f
  00000050: 6C 6F 61 74 69 6E 67 5F 70 6F 69 6E 74 5F 65 78  loating_point_ex
  00000060: 63 65 70 74 69 6F 6E 5F 64 65 66 61 75 6C 74 20  ception_default

   Linker Directives
   -----------------
   /alternatename:__filter_x86_sse2_floating_point_exception=__filter_x86_sse2_floating_point_exception_default

...
COFF SYMBOL TABLE
...
00D 00000000 UNDEF  notype ()    External     | __filter_x86_sse2_floating_point_exception

Issue with link order of archive files specified twice on the command line

I am currently in the process of moving the Coq Windows build from our old shell scripts to opam. Doing so, I have an issue with linking CoqIDE with flexlink. I (or ocamlfind) specify one archive twice on the command line like this:

-lpangocairo-1.0
: 
-llablgtk3_stubs
:
-lpangocairo-1.0

liblablgtk3_stubs needs symbols defined in lpangocairo-1.0 and the nm tool tells me that the symbols are there. Still I get an error that the symbols are undefined.

I looked into this with SysInternals ProcMon and all involved tools (flexlink, mingw gcc and ld) read the archives in the order libpangocairo and then lablgtk3_stubs. Notably pangocairo-1.0 is not read a second time although it is specified twice.
As far as I know at least ld does not use objects in an archive when they don't contain currently needed symbols, so in this order pangocairo-1.0 get's ignored.

Another interesting point is that the command line to gcc does not contain libpangocairo at all, although it does read it later - not sure what kind of magic this is (See line 9175 in the attached log).

I must admit I am not 100% sure what version of flexlink I have since it is supplied by opam and it doesn't seem to have an option to display this.

If this helps I can supply batch/shell scripts which install a fresh cygwin and bring you to the point of this error.

P.S.: I tried to attach the ProcMon log as txt or zip - it won't let me giving obscure errors. I will try again after creating the issue.

Would a pull request allowing "__nm_" prefixes be accepted?

Working with https://github.com/ocaml-cross/opam-cross-windows which relies on flexdll, I hit a strange problem: for some C++ DLL I'm calling from OCaml, some symbols where prefixed with "__nm_". I do not know why?

I solved this by patching flexdll (see ocaml-cross/opam-cross-windows#108). I submitted this patch to https://github.com/ocaml-cross/opam-cross-windows, but if the solution to my problem is sane, it would be better to include it into flexdll.

This patch modifies aliases function (https://github.com/alainfrisch/flexdll/blob/master/coff.ml#L757-L766) for it to store both the whole symbol and the symbol without the "__nm_" prefix in the alias hash-table:

let aliases x =
    let a = ref [] in
    List.iter
      (fun s ->
         (match s.extra_info with
           | `Alias s' -> a := (s.sym_name,s'.sym_name) :: !a
           | _ -> ());
         if starts_with ~what:"__nm_" s.sym_name then
           a := (String.sub s.sym_name 5 (String.length s.sym_name - 5),
                s.sym_name) :: !a
      )
      x.symbols;
    !a

It works, but re-reading it, I think it should have been something like the following instead:

  let aliases x =
    let a = ref [] in
    List.iter
      (fun s ->
        match s.extra_info with
        | `Alias s' ->
            a := (s.sym_name, s'.sym_name) :: !a;
            if starts_with ~what:"__nm_" s.sym_name then
              a :=
                ( String.sub s.sym_name 5 (String.length s.sym_name - 5),
                  s'.sym_name )
                :: !a
        | _ -> () )
      x.symbols;
    !a

Anyway: after I would correct and test this, would such a PR be considered to be accepted in flexdll, or does it solve my problem the wrong way?

`-print-search-dirs` may return `;`-delimited mixed paths on MSYS2

Partially a TODO item to check, unless someone else can confirm/disprove in the meantime, as I don't have a relevantly-configured machine to hand.

in:
https://github.com/alainfrisch/flexdll/blob/3255266d71813572b771f4946ed932912c760a7c/reloc.ml#L1260-L1263
there are several assumptions:

  • get_lib_search_dirs assumes that the list is :-separated
  • normalize_path assumes Unix-style paths

Both of these assumptions are valid for Cygwin-compiled mingw compilers, but I'm not sure they are valid for MSYS2, where gcc is (can be?) a native Windows executable and returns ;-separated mixed-mode paths.

MSYS2/ucrt64 support

By default MSYS2 doesn't have a prefixed windres tool in most of its environments (except for the mingw one which seems to fail due to unrelated reasons). When building flexdll vendored inside OCaml, the TOOLPREF variable gets set to x86_64-w64-mingw32-, which makes the compilation of flexdll fail under MSYS2 which doesn't have x86_64-w64-mingw32-windres but has windres

There is an issue upstream about the lack of prefixed windres tool here: msys2/MSYS2-packages#2595

However regardless of whether or not it is a good decision for such platform to have it or not, it would be nice for OCaml to compile without having to either patch the Makefile or create a custom windres binary/script/link.

While I'm not certain what this windres program is, maybe flexdll could do the following change:

version_res.o: version.rc
-        $(TOOLPREF)windres -i $< -o $@
+        $(TOOLPREF)windres -i $< -o $@ || windres -i $< -o $@

Relatedly there is also the following comment in configure.ac in ocaml/ocaml:

# Note: This is present for the flexdll bootstrap where it exposed as the old
# TOOLPREF variable. It would be better if flexdll where updated to require
# WINDRES instead.
AC_SUBST([ac_tool_prefix])

Side note: I remember talking with someone about this issue some months ago and I thought a ticket was opened somewhere but i couldn't find it anywhere so I'm creating it here to at least kickstart the discussion once more

Flexdll is incompatible with visual studio 2017.3

When compiling ocaml with visual studio 2017.3 I get the following error message during linking of the unix library:

Creating library C:\cygwin\tmp\dyndll_implib571fa3.lib and object 
 C:\cygwin\tmp\dyndll_implib571fa3.exp
 C:\cygwin\tmp\dyndll_implib571fa3.lib : fatal error LNK1136: invalid or corrupt file

Linking dlls?

I have two problems in trying to resurrect the wxwidgets ocaml binding.

  1. In trying to get flexdll to see a wx dll the -explain shows it not being visited at all.
    I'm too thick to understand whether flexdll is meant to only create a dll or whether it actually incorporates dependencies on others. It appears not though Gnu ld on my cygwin system does!

  2. Some missing symbol errors including:

__ZTVN10__cxxabiv117__class_type_infoE

Which should have been taken care of by -lstdc++
But -explain shows it picking up libstdc++.dll.a
which contains an indirection mark I for that symbol
but doesn't look at libstdc++.a which has an R mark for it, meaning in the read-only section.
What's going on?!

Support gcc "-Wl" linker pass through option

This is a follow up on the discussion on Coq PR 9729:

coq/coq#9279

If flexlink would support -Wl as synonym for -link, libraries which use -Wl options could be compiled without changes in the build instructions on Windows.

I will provide a PR, but only for simple cases of the -Wl syntax.

Command line too long

I'm using cygwin, the mingw64 toolchain and I get the error.
It comes from Reloc.cygpath, that in turns calls get_output with use_bash:false.
The quick hack was to force it to use bash, but what could be done (in the non use_bash case) is to use Unix.create_process instead of Sys.command, since the former does not use a shell (no cmd.exe, so more than 8k for argv). See for example sys_command in https://github.com/coq/coq/blob/trunk/lib/cUnix.ml

Best,

line in response file wrongly interpreted as option by MSVC linker

In win64 environment, a file to be linked may be of the form \server\path\to\obj

If the command line is too long, this is dumped into a response file, but the function build_diversion in reloc.ml, specifically converts file-paths to use "/" instead of "". As a result the above file is interpreted as an option to the "link" command then fails to link files.

There does not seem to be an easy workaround to this problem, as the option to use response file is not user-controllable

** Fatal error: Cannot parse directive: -exclude-symbols:__alloca

I'm currently getting this error while trying to build an ocaml package. I have no idea where -exclude-symbols:__alloca is coming from but it seems like flexlink is not able to parse it.
I cannot share the source code but I'll do my best to provide whatever additional context needed.
Thank you in advance

Compiling z3 ml's API with cygwin

It appears that can be a flexdll related issue. Could you guys confirm ? Maybe something related to elf lib file "libz3ml.a".

I'm using ocaml 4.03.0 and flexdll-0.35.

$ make
ocamlc  -i -I api/ml -c ../src/api/ml/z3enums.ml > api/ml/z3enums.mli
ocamlc  -I api/ml -o api/ml/z3enums.cmi -c api/ml/z3enums.mli
ocamlc  -I api/ml -o api/ml/z3enums.cmo -c ../src/api/ml/z3enums.ml
ocamlc  -i -I api/ml -c ../src/api/ml/z3native.ml > api/ml/z3native.mli
ocamlc  -I api/ml -o api/ml/z3native.cmi -c api/ml/z3native.mli
ocamlc  -I api/ml -o api/ml/z3native.cmo -c ../src/api/ml/z3native.ml
cp ../src/api/ml/z3.mli api/ml/z3.mli
ocamlc  -I api/ml -o api/ml/z3.cmi -c api/ml/z3.mli
ocamlc  -I api/ml -o api/ml/z3.cmo -c ../src/api/ml/z3.ml
ocamlc  -ccopt "-D_MP_INTERNAL -DNDEBUG -D_EXTERNAL_RELEASE -std=c++11 -fvisibility=hidden -c -mfpmath=sse -msse -msse2 -D_NO_OMP_ -O3 -D _EXTERNAL_RELEASE -fomit-frame-pointer -D_CYGWIN -fno-strict-aliasing -I /home/anmap/.opam/4.03.0/lib/ocaml -I ../src/api -I ../src/api/ml -o api/ml/z3native_stubs.o" -c ../src/api/ml/z3native_stubs.c
cc1: warning: command line option ‘-std=c++11’ is valid for C++/ObjC++ but not for C
ocamlmklib -o api/ml/z3ml -I api/ml api/ml/z3native_stubs.o api/ml/z3enums.cmo api/ml/z3native.cmo api/ml/z3.cmo  libz3.dll
ocamlopt  -I api/ml -o api/ml/z3enums.cmx -c ../src/api/ml/z3enums.ml
ocamlopt  -I api/ml -o api/ml/z3native.cmx -c ../src/api/ml/z3native.ml
ocamlopt  -I api/ml -o api/ml/z3.cmx -c ../src/api/ml/z3.ml
ocamlmklib -o api/ml/z3ml -I api/ml api/ml/z3native_stubs.o  api/ml/z3enums.cmx api/ml/z3native.cmx api/ml/z3.cmx libz3.dll
ocamlopt  -shared -o api/ml/z3ml.cmxs -I api/ml api/ml/z3ml.cmxa
** Fatal error: Error while reading api/ml/libz3ml.a: Invalid_argument("String.create")
File "caml_startup", line 1:
Error: Error during linking
make: *** [Makefile:4370: api/ml/z3ml.cmxs] Error 2

Cannot relocate on OCaml 4.13.1 [Windows]

I'm working on porting the Binary Analysis Platform / BAP to Windows.

Unless I am misunderstanding, the mingw64 chain , >=OCaml 4.12.1, and >=Binutils 2.36 should no longer be experiencing runtime plugin/dll relocation failures with RELOC_REL32.

Using MSYS2's UCRT shell/toolchain (I have this same issue in Cygwin, too.), MinGW GCC 11.2, OCaml 4.13.1 + flambda, and the latest git source of flexdll.

However, the LLVM-backend plugin for BAP is erroring out here. If I get flexlink to pass --default-image-base-low when linking the main executable and the breaking plugin, it's fine. Decorating the symbol as a dllimport, in the plugin code, didn't appear to make a difference.

No other plugins seem to have this issue, even when they are left with a high base address & the executable is low.

Failed to load plugin "bap-plugin-llvm": Failed to load bap_llvm: error loading shared library: Dynlink.Error (Dynlink.Cannot_open_dll "(Failure\n \"flexdll error: cannot relocate _ZN3bap21register_disassemblerENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESt10shared_ptrINS_14disasm_factoryEE RELOC_REL32, target is too far: FFFFFFFCCE4E7D5B FFFFFFFFCE4E7D5B\")")

bap::register_disassembler(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::shared_ptr<bap::disasm_factory>)

These are the instantiation of the register_disassembler function in the main executable:
https://github.com/BinaryAnalysisPlatform/bap/blob/master/lib/bap_disasm/disasm.hpp
https://github.com/BinaryAnalysisPlatform/bap/blob/master/lib/bap_disasm/disasm.cpp

And this is the plugin's usage of it:
https://github.com/BinaryAnalysisPlatform/bap/blob/master/lib/bap_llvm/llvm_disasm.cpp

I'll be honest and say that I'm only guessing that this problem lies with FlexDLL, but it could be in OCaml itself, BAP's source code, MinGW, etc.

If needed, I can provide a way to reproduce this; however, it's unfortunately not a quick process, and will require manual steps in the build process. At that, I could also provide the virtual machine image or the msys64 folder.

Fatal Error: Cannot run cygpath -m - command - The command line is too long

Our esy build fails on Windows when running ocamlopt.exe w/ lots of includes.

The relevant portion is:

** Fatal error: Cannot run cygpath -m "libadvapi32" "libadvapi32.lib" "libadvapi32.dll.a" "libadvapi32.a" "C:/Users/bryph/.esy/3_/i/opam__slash__ocamlfind-1.8.0-12c842a0/lib/ocaml\libadvapi32" "C:/Users/bryph/.esy/3_/i/opam__slash__ocamlfind-1.8.0-12c842a0/lib/ocaml\libadvapi32.lib" 
...

Full log

This is running ocaml 4.6.0, with v0.37 of flexdll.

Implementing a less memory intensive read function

When building the ocaml bibindings for Z3, it attempts to allocate ~10GiB before an oom error. This exceedingly high memory usage occurs while reading the import library (libz3.dll.a), and before the actual linker is called.

I don't know if there's a more efficient way to store these strings, or if it's possible to implement it such as to save results to disk and continue reading if memory usage is becoming too high. It seems very improbable that 33 GiB of strings are needed to make ~500KiB to 1.3MiB libraries.

The most pertinent information from the ctf is likely:

1391 samples of 10.4 GB allocations

 5.9 GB (57.0%) at Stdlib__Bytes.sub (bytes.ml:68:12-22, 68:12-22)
      including:
     2.9 GB via Coff.Lib.read_lib.read_member (97% of this)

 4.4 GB (42.4%) at Stdlib__Buffer.resize (buffer.ml:87:19-40)
      including:
     4.1 GB via Coff.Lib.read_lib.obj (56% of this)
     4.1 GB via Stdlib__Printf.ksprintf.k' (93% of this)
     4.1 GB via CamlinternalFormat.strput_acc (93% of this)
     4.1 GB via Stdlib__Buffer.add_string (93% of this)

The CTF is here, if you'd like to view it, though.
https://gist.github.com/EmmaJaneBonestell/98fda00c93a8d09ea09bada08972761d

Edit: I used a GCP server, and it took about 33 GiB of RAM and a total of 46 GiB of allocations to build.

Edit 2: UCRT had nothing to do with it, but rather, it seems to have the excessive memory usage with any of the MSYS2 environments' built dlls. If the dll is built with Cygwin's MinGW toolchain, rather than MSYS2's MINGW toolchain, it doesn't have a problem. E.g. libz3.dll.a built with Cygwin's MinGW, vs MSYS2's, won't consume excessive RAM.

Fix parallel access to global symbol structures

The list of loaded units is defined globally in flexdll.c:

static dlunit *units = NULL;

and is accessed in parallel both by flexdll_*dlopen and flexdll_dlsym without using a lock. Adding locks around flexdll_*dlopen is straightforward, but symbol lookup operates a most-recently-used queue on the global, and the performance impact either of removing that or, worse, of putting a lock around flexdll_dlsym is less clear.

In the meantime, reverting ocaml/ocaml#11607 provides an easy test-case for demonstrating the failure (the test usually fails within a few runs)

Upgrade from msvcrt.lib to ucrt.lib

With the minimum requirements for OCaml at Windows 10 (I think), we should consider moving off the legacy msvcrt.lib.

Here is the branch I have used for testing: https://github.com/jonahbeckford/flexdll/commits/0.43%2Bucrt

There are two blockers:

  1. #29 is probably related (I'm seeing this error only after upgrading to ucrt.lib.
  2. Some extra libraries are needed (not sure whether this belongs in ocaml/flexdll or ocaml/ocaml itself). I'll place each library in a separate comment below.

Switch to Unix.create_process for auxiliaries

Historically, flexlink eschews using the Unix library, but this is daft - we're going through ridiculous contortions to work around command line length limits, and falling back to bash has become ever more brittle.

Motivated by the issue reported on Discuss

flexlink: simplify static linking with libstdc++

Currently with flexlink, if you have a C++ binding and want to link it statically in your executable to avoid having to carry the dll around the only option that works is to manually set the name for the archive file like this (with MinGW):

-l:libstdc++.a -l:libpthread.a -Wl,-static

It would be nice if something like -lstdc++ -static was a thing so that flexlink can find the correct archive file as by default -lstdc++ -Wl,-static will use libstdc++.dll.a which is the shared version of libstdc++ and not the static one.

Cannot resolve symbols for swscanf

Using ocaml/setup-ocaml#794

#=== ERROR while compiling dune.3.15.2 ========================================#
# context     2.2.0~beta2 | win32/x86_64 | ocaml-base-compiler.4.14.2 | git+https://github.com/dra27/opam-repository.git#windows-initial
# path        D:\a\mirage-torrent\mirage-torrent\_opam\.opam-switch\build\dune.3.15.2
# command     D:\a\mirage-torrent\mirage-torrent\_opam\bin\ocaml.exe boot/bootstrap.ml -j 4
# exit-code   2
# env-file    D:\.opam\log\dune-8180-dff03d.env
# output-file D:\.opam\log\dune-8180-dff03d.out
### output ###
# ocamlc -output-complete-exe -w -24 -g -o .duneboot.exe -I boot unix.cma boot/libs.ml boot/duneboot.ml
# ** Cannot resolve symbols for D:/a/mirage-torrent/mirage-torrent/_opam/lib/ocaml\libcamlrun.a(startup_aux.b.o):
#  swscanf
# ** Cannot resolve symbols for descriptor object:
#  _vscwprintf
# File "boot/duneboot.ml", line 1:
# Error: Error while building custom runtime system

contrib - visual studio 2015 integrated example

dlltest10.zip

Here I've made a visual studio 2015 integrated example of using flexdll.
Open the sln, set startup project to exe, and set debug working directory to $(ProjectDir).run

You will now get working builds for debug/release & x86/x64. A working example prints this:

in main
99
now: 0

The project includes the latest master flexlink.exe build as of now (needed for x64 debugging reloc fixes); as well, I've had to rebuild the flexdll_*.obj using vs2015 toolchain (scripts for doing this are provided)

The project also contains my program linkwrap which I think was required to get the nice VS/msbuild building semantics I wanted. Chiefly, it rips apart the response file provided to msvc's link.exe from VS and analyzes it to determine wordsize and lib/obj dependencies for passing to flexdll's flexlink.exe

The vcxprojs have been modified simply to incorporate flexdll as an improved linker, by setting these things in the project:

<PropertyGroup Label="Globals">
  <LinkToolPath>$(ProjectDir)flexdll</LinkToolPath>
  <LinkToolExe>linkwrap.exe</LinkToolExe>
</PropertyGroup>

Note: you cannot use link-time code generation with flexdll. It's on by default in vcxprojs; I have turned it off. This should come as no great surprise as it probably incorporates a great deal of inscrutable proprietary junk (the problem will manifest as some kind of a premature EOF error while reading a .obj input)

The test confirms the main functionality I was interested in:

  1. a dll can leave an unresolved external to a global variable in the main program
  2. the dll can export functions without dllexport
  3. the global variable is correctly shared between the modules
  4. I can debug from the main and into the dll and back out

TODO - I haven't tested this with additional library directories. I will probably need to update it for that. I just need to get a WIP committed, so that's what you're seeing here.

Generate import library for exported function of the main executable file

There is a statement in your project home page:

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 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).

While, it looks like I can do this with the tool gendef and dlltool.

The method I was using is like:

For example, the exe_export.exe has some code like:

#include <Windows.h>
#include <stdio.h>

#define MYDLL_EXPORTS

#include "interface_exe.h"

void appli_printf7777(void)
{
   printf("7777");
   return;
}

void appli_printf8888(void)
{
   printf("8888");
   return;
}


int main(void)
{
   HMODULE hModule = LoadLibrary("dll.dll");
   FARPROC pFunc = GetProcAddress(hModule, "dllFunction");

   ((DLL_FUNC)pFunc)();

   return 0;
}

The content of the interface_exe.h is like below:

#ifndef INTERFACE_EXE_H_INCLUDED
#define INTERFACE_EXE_H_INCLUDED


 #ifdef  MYDLL_EXPORTS
    /*Enabled as "export" while compiling the dll project*/
    #define DLLEXPORT __declspec(dllexport)
 #else
    /*Enabled as "import" in the Client side for using already created dll file*/
    #define DLLEXPORT __declspec(dllimport)
 #endif

#ifdef __cplusplus
extern "C"
{
#endif

typedef void(*DLL_FUNC)(void);

DLLEXPORT void appli_printf7777(void);

DLLEXPORT void appli_printf8888(void);


#ifdef __cplusplus
}
#endif

#endif // INTERFACE_EXE_H_INCLUDED

And I have another dll.cpp file which is used to generate the dll.

#include <Windows.h>
#include <stdio.h>

#include "interface_exe.h"


#ifdef __cplusplus
extern "C"
{
#endif

__declspec (dllexport) void dllFunction(void);

#ifdef __cplusplus
}
#endif




typedef void(*APPLI_PRINTF7)(void);

__declspec(dllexport) void dllFunction(void)
{
   //HMODULE hModule = LoadLibrary("exe_export.exe");
   FARPROC pFunc = GetProcAddress(0, "appli_printf7777");

   if(pFunc)
      ((APPLI_PRINTF7)pFunc)();
   else
      printf("error = %d\n", GetLastError());

   appli_printf7777();
   appli_printf8888();

   return;
}

I first build the exe_export.exe file, and later I use the commands below to generate the import .a file:

gendef exe_export.exe
dlltool --dllname exe_export.exe --def exe_export.def --output-lib libexe_export.a

Then later, I link the libexe_export.a file to generate the dll.

Now, when I run the exe file, I have two methods to call the methods in the dll.

One is using the GetProcAddress method.

   //HMODULE hModule = LoadLibrary("exe_export.exe");
   FARPROC pFunc = GetProcAddress(0, "appli_printf7777");

   if(pFunc)
      ((APPLI_PRINTF7)pFunc)();
   else
      printf("error = %d\n", GetLastError());

The other is using the imported method, which I can directly call

   appli_printf7777();
   appli_printf8888();

All works fine.

Here are some reference:
1, the method I use is described as the .def and .a method, see this discussion:
Enhanced DLL / Discussion / Help and FAQ: The .def and .a method mentioned in the home page

2, https://www.codeproject.com/Articles/17697/Plugin-System-an-alternative-to-GetProcAddress-and
The method mentioned in this link is using MSVC, and I think it is similar with the gendef and dlltool method.

3, my demo code was changed from the sample code in this discussion:
Exporting symbols in a .exe for external DLLs

Hope the above method can help others.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.