├── .gitattributes ├── .gitignore ├── .ocp-indent ├── CHANGES ├── Compat.ml.in ├── LICENSE ├── Makefile ├── README.md ├── appveyor.yml ├── appveyor_build.sh ├── checkenv ├── clear_appveyor_cache.sh ├── cmdline.ml ├── coff.ml ├── create_dll.ml ├── default.manifest ├── default_amd64.manifest ├── flexdll.c ├── flexdll.h ├── flexdll.install ├── flexdll.opam ├── flexdll_initer.c ├── installer.nsi ├── msvs-detect ├── reloc.ml ├── test ├── Makefile ├── api.h ├── dump.c ├── plug1.c └── plug2.c └── version.rc /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | msvs-detect text eol=lf 4 | checkenv text eol=lf 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.obj 3 | *.o 4 | *.cmx 5 | *.cmo 6 | *.cmi 7 | *.dll 8 | *.tar.gz 9 | *.zip 10 | flexlink.exe 11 | flexlink.opt 12 | test/dump.exe 13 | version.res 14 | version.ml 15 | Makefile.winsdk 16 | COMPILER-* 17 | Compat.ml 18 | -------------------------------------------------------------------------------- /.ocp-indent: -------------------------------------------------------------------------------- 1 | match_clause=4 2 | strict_with=auto 3 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | Version 0.44 2 | - GPR#127: Recognise hyphens in option names in the COFF .drectve section. 3 | Fixes #126 (Reza Barazesh) 4 | - GPR#136: Fix parallel access crashes and misbehavior 5 | (David Allsopp, Jan Midtgaard, Antonin Décimo) 6 | - GPR#141: Support relocation kind 0003 (IMAGE_REL_AMD64_ADDR32NB) and 7 | IMAGE_REL_I386_DIR32NB. Extend relative types to IMAGE_REL_AMD64_REL32_5. 8 | Fixes GPR#29 (Jonah Beckford) 9 | - GPR#148: Pass-through -pthread to the linker (Romain Beauxis) 10 | - GPR#150: Fix stack reserve incorrectly reading from the start of the PE 11 | executable instead of the COFF file header. Fix reading the file address of 12 | new exe header. 13 | (Antonin Décimo, review by David Allsopp and Nicolás Ojeda Bär) 14 | - GPR#153: Allow the names of the compiler binaries to be overridden without 15 | losing the flags which go with them to assist use of alternate compilers and 16 | for cross-compilation. Fix FlexDLL cross-build on macOS and BSD. 17 | (Antonin Décimo, review by Nicolás Ojeda Bär) 18 | - GPR#151: Use response files with all toolchains 19 | (Puneeth Chaganti, review by David Allsopp and Antonin Décimo) 20 | - GPR#152: Use a response file to pass arguments to cygpath 21 | (Puneeth Chaganti, review by David Allsopp and Antonin Décimo) 22 | - GPR#156: Allow choosing an alternative linker and manifest tool at runtime 23 | with -use-linker= and -use-mt=. 24 | (Antonin Décimo, review by David Allsopp) 25 | - GPR#133, GPR#147: Allow default libraries to be marked as optional 26 | (David Allsopp, report by Romain Beauxis) 27 | - GPR#157: Improve flexlink support on Unix (for cross-compiling contexts). 28 | (Antonin Décimo, review by David Allsopp) 29 | - GPR#158: Reuse the compilers used during the build for later operations, such 30 | as querying the compilers for their library search paths. Support parsing 31 | clang output. 32 | (Antonin Décimo, review by David Allsopp) 33 | 34 | Version 0.43 35 | - GPR#108: Add -lgcc_s to Cygwin's link libraries, upstreaming a patch from the 36 | Cygwin flexdll package (David Allsopp) 37 | - GPR#112: Put error global variables into thread-local storage (Samuel Hym, Nicolás Ojeda Bär) 38 | - GPR#114: Support for /alternatename directive. Fixes GPR#113 (Jonah Beckford) 39 | - GPR#116: Remove Cygwin32 (David Allsopp) 40 | - GPR#117: Fix handling of object names of length > 16 in archive files. Fixes GPR#101 (Nicolás Ojeda Bär) 41 | - GPR#118: Defer the detection of cygpath to the first time it's actually 42 | required, reducing the overall number of calls. Further, detect whether 43 | mingw-w64 gcc's library path requires cygpath, which further reduces the 44 | calls when using Cygwin's build of mingw-w64 as the search path is then 45 | converted only once. In passing, this trivially adds support for MSYS2's 46 | mingw-w64 compilers, fixing GPR#97. (David Allsopp) 47 | 48 | Version 0.42 49 | - GPR#106: Support -l: syntax, to allow static linking of specific libraries (David Allsopp) 50 | - GPR#103: Delete objects from C files compiled by flexlink (David Allsopp, report by Xavier Leroy) 51 | - GPR#85: Split multiple arguments passed with a single -Wl (David Allsopp) 52 | 53 | Version 0.41 54 | - GPR#98: Eliminate Warning 6 compiling coff.ml (David Allsopp) 55 | - GPR#99: Harden version number parsing from ocamlopt -version (David Allsopp) 56 | 57 | Version 0.40 58 | - GPR#65: Support for compiling objects with /bigobj (Extended COFF) (Dmitry Bely) 59 | - GPR#80: Silence MSVC filenames for long commands as well as short ones (David Allsopp) 60 | - GPR#94: Fix parsing of COFF archives to ignore headers, allowing the Windows 11 SDK to be used. (David Allsopp) 61 | - GPR#96: Suppress volatile metadata on versions of MSVC which support it (David Allsopp) 62 | - GPR#88: Correctly handle relative search paths coming from gcc -print-search-dirs (Boris Yakobowski) 63 | 64 | Version 0.39 65 | - GPR#89: Stop passing --image-base on Cygwin64 - Cygwin64 DLLs should load at 66 | 0x4:00000000-0x6:00000000 if not rebased or 0x2:00000000-0x4:00000000 if rebased. (David Allsopp) 67 | - GPR#90: Fix passing NULL to flexdll_dlopen on non-Cygwin builds (David Allsopp) 68 | 69 | Version 0.38 70 | - GPR#58: Bug fix: fix -custom-crt regression (Dmitry Bely) 71 | - GPR#61: Ignore x64 debug relocs (Dmitry Bely) 72 | - GPR#49: Adding support for 64bit GNAT compiler (Johannes Kanig) 73 | - GPR#64: Fix problem with setting page permission during relocation (report 74 | by Dmitry Bely) 75 | - GPR#48: Enable linking of C++ object files for mingw ports (David Allsopp) 76 | - GPR#81, GPR#84: use msvs-detect (from metastack/msvs-tools) for detecting Windows SDK 77 | or Visual Studio (David Allsopp) 78 | - GPR#78: Eliminate deprecation warnings on OCaml 4.08+ (Bernhard Schommer) 79 | - GPR#68: fix incorrect calculation of maximum path length (Bryan Phelps and David Allsopp) 80 | - GPR#3: Recognise data symbols in import libraries (Andreas Hauptmann) 81 | - GPR#73: Pass GCC-style linker options (-Wl,...) onto the the linker (Michael Soegtrop) 82 | 83 | Version 0.37 84 | - Bug fix: the IMAGE_SCN_LNK_NRELOC_OVFL section flag was not propertly reset when 85 | the number of relocations was reduced enough to fit in the 16-bit field, 86 | causing https://caml.inria.fr/mantis/view.php?id=7603 87 | - Support for passing argument through external files (-arg/-arg0) 88 | (Bernhard Schommer) 89 | - Fix encoding of high surrogate for U+10000-U+10FFFF in UTF-16 response files (David Allsopp) 90 | 91 | Version 0.36 92 | - Add Unicode support (patch by Nicolás Ojeda Bär) 93 | - Workaround apparent bug in VS 2017.3 link (patch by David Allsopp) 94 | - Additional heuristic for -lfoo on MSVC: try libfoo.lib but then try foo.lib (David Allsopp) 95 | 96 | Version 0.35 97 | - Fixes for clang (patch by Roven Gabriel) 98 | - Improve autodetection of WinSDK paths (patch by David Allsopp) 99 | - Add MSVC_DETECT Makefile variable which overrides all C Compiler detection logic when 0. 100 | - Add checkenv script which determines if the environment is sound specifying MSVC_DETECT=0 101 | - Improve performance of -builtin 102 | - Better support for @response files, which can contain options in 103 | addition to filenames (patch by Bernhard Schommer) 104 | - Ignore "-g" (passed by OCaml when building ocamlrund) (patch by whitequark) 105 | - Uses gcc -print-search-dirs instead of gcc -print-sysroot when figuring out the default 106 | library search paths (patch by whitequark) 107 | - Modernize code for OCaml 4.03 (patch for compatibility layers for earlier versions by David Allsopp) 108 | - Better support for long command line when calling the toolchain linker 109 | - Do not perform relocation from the DLL entry point when the DLL is loaded in "not for 110 | execution mode" (http://caml.inria.fr/mantis/view.php?id=7268) 111 | - Fix -stack for Cygwin (when the resulting executable is called XXX.exe, not XXX) 112 | 113 | Version 0.34 114 | - New option -norelrelocs to check that no relative flexdll relocations are created (this ensures that the generated DLLs can be loaded at any address), 115 | - New option -base to specify the image base address (msvc64 only). 116 | - Always generate a table for used __flimp_XXX symbols (even when X is defined in the generated image). 117 | - Fix bug with empty sections in "-builtin" mode 118 | - -builtin: Generate proper ADDR64 instead of HIGHLOW (32-bit) relocations in the dll 119 | 120 | Version 0.33 121 | - Turn 'too far' messages into proper errors, avoid terminating the process 122 | 123 | Version 0.31 124 | - Support for "@respfiles" arguments (for long command lines). 125 | Used to be implemented by OCaml runtime system. 126 | - Fix bug when relocation counter > 0xffff 127 | - Fix bug with REL32 relocations on non-zero base in DLLs 128 | - Fix bug with empty .a files 129 | 130 | Version 0.30 131 | - Allow the internal linker to create large DLLs (>= 16Mb) 132 | 133 | Version 0.29 134 | - use "cygpath -S", not "cygpath -v" which no longer exists 135 | 136 | Version 0.28 137 | - new option '-stack' to set the stack reserve 138 | - new option '-patch' (to be used in conjunction with -stack and -o to 139 | patch the an existing image) 140 | 141 | Version 0.27 142 | - support for mingw64; the mingw toolcgain now relies on the 32-bit version of mingw64; 143 | the old "gcc -mno-cygwin" is no longer supporte. 144 | Patch from Antointe Mine: 145 | http://caml.inria.fr/mantis/view.php?id=5179 146 | - fix directive parsing bug (http://caml.inria.fr/mantis/view.php?id=5339) 147 | - support for new file layout for cygwin's version of mingw 148 | - support for objects with more than 65536 relocations 149 | 150 | Version 0.26 151 | - fix for Win64 (use a PE32+ optional header, and a lower base address) 152 | - use _putenv_s instead of putenv for FLEXDLL_RELOCATE (thanks to Yasutaka Atarashi) 153 | - use /dev/null instead of NUL for Cygwin (thanks to Yasutaka Atarashi) 154 | 155 | Version 0.25 156 | - fix a bug with archive member names that contain a slash 157 | 158 | Version 0.24 159 | - add new toolchain msvc64; replace the old combination "-chain msvc -x64" (-x64 has been removed) 160 | - change build system to create a single binary version of flexdll that includes both the 32-bit and 64-bit 161 | version for MSVC's support objects 162 | - switch to Visual Studio 2008 (to compile the binary distribution and for the default manifests) 163 | 164 | Version 0.23 165 | - ignore -D and -U flags (OCaml PR #4979) 166 | - change limit before using a response file (OCaml PR #4645) 167 | 168 | Version 0.22 169 | - fix a bug when emitting IMAGE_COMDAT_SELECT_ASSOCIATIVE symbols 170 | 171 | Version 0.21 172 | - always use bash to call external commands under Cygwin (to make it work 173 | when gcc is a symlink) 174 | 175 | Version 0.20 176 | - (build) Avoid the use of the rc tool 177 | - (dist) add version.rc to the source release 178 | - Allow "/link XXX" as an equivalent to "-link XXX" (or "/linkXXX") 179 | - Use an explicit .def file under MinGW and Cygwin to force the two symbols reloctbl and symtbl to be exported 180 | - Fixes for x64 181 | - Allow to write sections with more than 0xffff relocations 182 | - Create a Win32 installer 183 | 184 | Version 0.19 185 | - fix bug introduced in version 0.17 about virtual addresses for the builtin 186 | linker mode 187 | 188 | Version 0.18 189 | - support for .sxdata section 190 | 191 | Version 0.17 192 | - patch from Lars Nilsson to ease compilation of flexdll.h with C++ 193 | - prefer using XXX.dll.a (import lib) over XXX.a (static lib) 194 | - fix bug introduced in commit 136 about virtual addresses 195 | 196 | Version 0.16 197 | - New -noexport command line option to force an empty symbol table 198 | 199 | Version 0.15, released on 2009-02-25 200 | - protects calls to cygpath with long command lines (patch from 201 | Matthieu Dubuget) 202 | - always pass msvcrt.lib to link.exe 203 | 204 | Version 0.14, released on 2008-28-01 205 | - mingw port: put /lib/mingw before /lib 206 | - support for a built-in linker 207 | 208 | Version 0.13, released on 2008-11-20 209 | - another technique to work around the lack of response file for gcc: 210 | put the command line in an external bash script and apply bash to it 211 | (relies on the fact that Cygwin programs can call Cygwin programs 212 | with long command lines); this idea is from Xavier Leroy 213 | - the -subsystem switch is now supported for the mingw toolchain 214 | 215 | Version 0.12, released on 2008-11-17 216 | - gcc 3 does not support response files; to support longer command lines, we use a 217 | custom replacement for cmd.exe (which restricts the length of the command line 218 | to 8kB whereas Windows supports 32kB) 219 | - patch for Richard Jones: do not find directories when looking for a file 220 | 221 | Version 0.11, released on 2008-11-10 222 | - compiled with mingw port so as to depend on msvcrt.dll, not msvcr80.dll 223 | - experimental support for directly calling "ld" instead of "gcc" 224 | - by default, reexport symbols imported from an import library 225 | - quote the response file argument 226 | - use slashes instead of backslashed in response file (needed for mingw's ld) 227 | - by default, use the real manifest 228 | 229 | Version 0.10, released on 2008-10-21 230 | - use "gcc -mno-cygwin -print-libgcc-file-name" to get the standard 231 | library directory for mingw 232 | - lower the length threshold to use a diversion file when calling the 233 | linker with many arguments 234 | 235 | Version 0.9, released on 2008-09-18 236 | - fix bug with COFF symbols for sections (with more than 1 auxilliary block) 237 | - ignore debug relocations on x86 (of kind 0x000a, 0x000b) 238 | 239 | Version 0.8, released on 2008-03-10 240 | - new -no-merge-manifest 241 | 242 | Version 0.7, released on 2008-03-09 243 | - allow .dll.a as automatic extension for libraries 244 | - minor bug fixes 245 | 246 | Version 0.6, released on 2008-02-11 247 | - support COMDAT section symbols 248 | - support mixed libraries (import library + regular objects) 249 | - optimizations (do not rewrite library objects when not needed, 250 | pass the lib to the linker) 251 | - new -real-manifest, replace -default-manifest 252 | - new -implib option 253 | - new -outdef option 254 | - new zlib/libpng license 255 | 256 | Version 0.5, released on 2008-01-11 257 | - new -default-manifest option (always on, currently) 258 | - use a temp file name for getting the output of commands (allow 259 | several instances of flexlink to run in parallel) 260 | 261 | Version 0.4, released on 2008-01-02 262 | - Code cleanup 263 | - FLEXLINKFLAGS env var to pass extra arguments 264 | 265 | Version 0.3, released on 2007-11-20 266 | - Make it work under x86_64 267 | - New -subsystem option (currently for MSVC chain only) 268 | - New -explain option 269 | - New -link option 270 | 271 | Version 0.2, released on 2007-11-06 272 | - New -maindll option (to build a DLL that contains the "main program" 273 | from FlexDLL's point of view) 274 | - Can now explicitly enable/disable the use of cygpath (on by default 275 | only for Cygwin) 276 | - New -L xxx (or -Lxxx) option 277 | - New -where option 278 | - FLEXDIR environment variable (where to look for FlexDLL's .obj files) 279 | 280 | Version 0.1, released on 2007-06-14 281 | - Initial release 282 | 283 | 284 | 285 | 286 | -------------------------------------------------------------------------------- /Compat.ml.in: -------------------------------------------------------------------------------- 1 | (************************************************************************) 2 | (* FlexDLL *) 3 | (* Alain Frisch *) 4 | (* *) 5 | (* Copyright 2007 Institut National de Recherche en Informatique et *) 6 | (* en Automatique. *) 7 | (************************************************************************) 8 | 9 | (* Compatibility shims. Each line is prefixed with the compiler at which the 10 | line ceases to be necessary. A function introduced in OCaml 4.01, therefore, 11 | is prefixed with "401:" *) 12 | 13 | 405:module Arg = struct 14 | 405: include Arg 15 | 405: 16 | 405: let trim_cr s = 17 | 405: let len = String.length s in 18 | 405: if len > 0 && String.get s (len - 1) = '\r' then 19 | 405: String.sub s 0 (len - 1) 20 | 405: else 21 | 405: s 22 | 405: 23 | 405: let read_aux trim sep file = 24 | 405: let ic = open_in_bin file in 25 | 405: let buf = Buffer.create 200 in 26 | 405: let words = ref [] in 27 | 405: let stash () = 28 | 405: let word = Buffer.contents buf in 29 | 405: let word = if trim then trim_cr word else word in 30 | 405: words := word :: !words; 31 | 405: Buffer.clear buf 32 | 405: in 33 | 405: begin 34 | 405: try while true do 35 | 405: let c = input_char ic in 36 | 405: if c = sep then stash () else Buffer.add_char buf c 37 | 405: done 38 | 405: with End_of_file -> () 39 | 405: end; 40 | 405: if Buffer.length buf > 0 then stash (); 41 | 405: close_in ic; 42 | 405: Array.of_list (List.rev !words) 43 | 405: 44 | 405: let read_arg = read_aux true '\n' 45 | 405: 46 | 405: let read_arg0 = read_aux false '\x00' 47 | 405: 48 | 405:end 49 | 50 | 403:module Uchar = struct 51 | 403: let unsafe_of_int c = c 52 | 403: 53 | 403: let to_int c = c 54 | 403:end 55 | 56 | 406:module Buffer = struct 57 | 406: include Buffer 58 | 406: 59 | 402: let to_bytes = contents 60 | 402: 61 | 406: let add_utf_16le_uchar b u = match Uchar.to_int u with 62 | 406: | u when u < 0 -> assert false 63 | 406: | u when u <= 0xFFFF -> 64 | 406: add_char b (Char.unsafe_chr (u land 0xFF)); 65 | 406: add_char b (Char.unsafe_chr (u lsr 8)) 66 | 406: | u when u <= 0x10FFFF -> 67 | 406: let u' = u - 0x10000 in 68 | 406: let hi = 0xD800 lor (u' lsr 10) in 69 | 406: let lo = 0xDC00 lor (u' land 0x3FF) in 70 | 406: add_char b (Char.unsafe_chr (hi land 0xFF)); 71 | 406: add_char b (Char.unsafe_chr (hi lsr 8)); 72 | 406: add_char b (Char.unsafe_chr (lo land 0xFF)); 73 | 406: add_char b (Char.unsafe_chr (lo lsr 8)) 74 | 406: | _ -> assert false 75 | 406:end 76 | 77 | 402:module Bytes = struct 78 | 402: include String 79 | 402: 80 | 402: let blit_string = blit 81 | 402: let sub_string = sub 82 | 402: let of_string x = x 83 | 402: let to_string x = x 84 | 402: let cat = (^) 85 | 402:end 86 | 87 | 403:module Char = struct 88 | 403: include Char 89 | 403: 90 | 403: let lowercase_ascii c = 91 | 403: if (c >= 'A' && c <= 'Z') 92 | 403: then unsafe_chr(code c + 32) 93 | 403: else c 94 | 403: 95 | 403: let uppercase_ascii c = 96 | 403: if (c >= 'a' && c <= 'z') 97 | 403: then unsafe_chr(code c - 32) 98 | 403: else c 99 | 403:end 100 | 101 | 408:module Option = struct 102 | 408: let some v = Some v 103 | 408: let value o ~default = match o with Some v -> v | None -> default 104 | 408: let fold ~none ~some = function Some v -> some v | None -> none 105 | 408:end 106 | 107 | 407:module Stdlib = Pervasives 108 | 109 | 404:module String = struct 110 | 404: include String 111 | 402: 112 | 402: let init n f = 113 | 402: let s = create n in 114 | 402: for i = 0 to n - 1 do 115 | 402: unsafe_set s i (f i) 116 | 402: done; 117 | 402: s 118 | 403: 119 | 403: let lowercase_ascii s = 120 | 403: init (length s) (fun i -> Char.lowercase_ascii (unsafe_get s i)) 121 | 403: let uppercase_ascii s = 122 | 403: init (length s) (fun i -> Char.uppercase_ascii (unsafe_get s i)) 123 | 404: 124 | 404: let split_on_char sep s = 125 | 404: let r = ref [] in 126 | 404: let j = ref (length s) in 127 | 404: for i = length s - 1 downto 0 do 128 | 404: if unsafe_get s i = sep then begin 129 | 404: r := sub s (i + 1) (!j - i - 1) :: !r; 130 | 404: j := i 131 | 404: end 132 | 404: done; 133 | 404: sub s 0 !j :: !r 134 | 404:end 135 | 136 | 401:module Sys = struct 137 | 401: include Sys 138 | 401: 139 | 401: let win32 = (Sys.os_type = "Win32") 140 | 401: let cygwin = (Sys.os_type = "Cygwin") 141 | 401: let unix = (Sys.os_type = "Unix") 142 | 401:end 143 | 144 | 402:type bytes = string 145 | 402:let output_bytes = output_string 146 | 147 | 401:let ( |> ) x f = f x 148 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The package FlexDLL is released under the terms of the zlib/libpng License. 2 | 3 | Copyright (c) 2007, 2008, 2009 4 | Institut National de Recherche en Informatique et en Automatique 5 | 6 | This software is provided 'as-is', without any express or implied 7 | warranty. In no event will the authors be held liable for any damages 8 | arising from the use of this software. 9 | 10 | Permission is granted to anyone to use this software for any purpose, 11 | including commercial applications, and to alter it and redistribute it 12 | freely, subject to the following restrictions: 13 | 14 | 1. The origin of this software must not be misrepresented; you must not 15 | claim that you wrote the original software. If you use this software 16 | in a product, an acknowledgment in the product documentation would be 17 | appreciated but is not required. 18 | 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 22 | 3. This notice may not be removed or altered from any source distribution. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ## Tips to build flexdll: 2 | ## 3 | ## To use an already configured 64-bit MSVC toolchain: 4 | ## make all MSVC_DETECT=0 CHAINS="mingw mingw64 cygwin64 msvc64" 5 | ## 6 | 7 | 8 | # Fetch the version number from its source, in flexdll.opam 9 | VERSION = \ 10 | $(eval VERSION := $$(shell sed -ne 's/^version: *"\(.*\)"/\1/p' flexdll.opam))$(VERSION) 11 | 12 | all: flexlink.exe support 13 | 14 | OCAML_CONFIG_FILE=$(shell cygpath -ad "$(shell ocamlopt -where 2>/dev/null)/Makefile.config" 2>/dev/null) 15 | include $(OCAML_CONFIG_FILE) 16 | OCAMLOPT=ocamlopt 17 | EMPTY= 18 | SPACE=$(EMPTY) $(EMPTY) 19 | COMMA=, 20 | OCAML_VERSION:=$(firstword $(subst ~, ,$(subst +, ,$(shell $(OCAMLOPT) -version 2>/dev/null)))) 21 | ifeq ($(OCAML_VERSION),) 22 | OCAML_VERSION:=0 23 | COMPAT_VERSION:=0 24 | else 25 | COMPAT_VERSION:=$(subst ., ,$(OCAML_VERSION)) 26 | COMPAT_VERSION:=$(subst $(SPACE),,$(firstword $(COMPAT_VERSION))$(foreach i,$(wordlist 2,$(words $(COMPAT_VERSION)),$(COMPAT_VERSION)),$(if $(filter 0 1 2 3 4 5 6 7 8 9,$(i)),0,)$(i))) 27 | endif 28 | 29 | GCC_FLAGS = -Wall 30 | 31 | MINGW_PREFIX = i686-w64-mingw32- 32 | MINCC = $(MINGW_PREFIX)gcc 33 | 34 | MINGW64_PREFIX = x86_64-w64-mingw32- 35 | MIN64CC = $(MINGW64_PREFIX)gcc 36 | 37 | CYGWIN64_PREFIX = x86_64-pc-cygwin- 38 | CYG64CC = $(CYGWIN64_PREFIX)gcc 39 | 40 | version.ml: Makefile flexdll.opam 41 | echo 'let version = "$(VERSION)"' > $@ 42 | echo 'let mingw_prefix = "$(MINGW_PREFIX)"' >> $@ 43 | echo 'let mingw64_prefix = "$(MINGW64_PREFIX)"' >> $@ 44 | echo 'let msvc = "$(notdir $(MSVCC))"' >> $@ 45 | echo 'let msvc64 = "$(notdir $(MSVCC64))"' >> $@ 46 | echo 'let cygwin64 = "$(notdir $(CYG64CC))"' >> $@ 47 | echo 'let mingw = "$(notdir $(MINCC))"' >> $@ 48 | echo 'let mingw64 = "$(notdir $(MIN64CC))"' >> $@ 49 | echo 'let gnat = "gcc"' >> $@ 50 | 51 | # Supported tool-chains 52 | 53 | CHAINS = mingw mingw64 cygwin64 msvc msvc64 54 | 55 | # Compilers 56 | 57 | # NB MSVC_DETECT is expected by OCaml's build system 58 | MSVC_DETECT=1 59 | 60 | # Attempt to locate the Windows SDK 61 | 62 | ifeq ($(MSVC_DETECT),1) 63 | ifeq ($(if $(MAKECMDGOALS),$(if $(strip $(filter-out clean,$(MAKECMDGOALS))),,1)),) 64 | include Makefile.winsdk 65 | endif 66 | endif 67 | 68 | Makefile.winsdk: msvs-detect 69 | bash ./msvs-detect --output=make > $@ 70 | 71 | MSVC_FLAGS = /nologo /MD -D_CRT_SECURE_NO_DEPRECATE /GS- /W3 72 | 73 | ifeq ($(MSVC_DETECT),0) 74 | # Assume that the environment is correctly set for a single Microsoft C Compiler; don't attempt to guess anything 75 | MSVCC_ROOT= 76 | MSVC_PREFIX= 77 | MSVC64_PREFIX= 78 | MSVCC = cl.exe 79 | MSVCC64 = cl.exe 80 | else 81 | ifeq ($(MSVS_PATH),) 82 | # Otherwise, assume the 32-bit version of VS 2008 or Win7 SDK is in the path. 83 | 84 | MSVCC_ROOT := $(shell which cl.exe 2>/dev/null | cygpath -f - -ad | xargs -d \\n dirname 2>/dev/null | cygpath -f - -m) 85 | MSVC_LIB1 = $(shell dirname $(MSVCC_ROOT)) 86 | MSVC_LIB2 = $(shell which ResGen.exe | cygpath -f - -ad | xargs -d \\n dirname | xargs -d \\n dirname | cygpath -f - -m) 87 | MSVC_LIB = $(MSVC_LIB1)/Lib;$(MSVC_LIB2)/Lib 88 | MSVC_INCLUDE = $(MSVC_LIB1)/Include;$(MSVC_LIB2)/Include 89 | MSVC_PREFIX=LIB="$(MSVC_LIB)" INCLUDE="$(MSVC_INCLUDE)" 90 | 91 | MSVC64_LIB = $(MSVC_LIB1)/Lib/amd64;$(MSVC_LIB2)/Lib/x64 92 | MSVC64_PREFIX=LIB="$(MSVC64_LIB)" INCLUDE="$(MSVC_INCLUDE)" 93 | 94 | MSVCC = $(MSVCC_ROOT)/cl.exe 95 | MSVCC64 = $(MSVCC_ROOT)/amd64/cl.exe 96 | else 97 | MSVCC_ROOT:= 98 | MSVC_PREFIX=PATH="$(MSVS_PATH)$(PATH)" LIB="$(MSVS_LIB)$(LIB)" INCLUDE="$(MSVS_INC)$(INCLUDE)" 99 | MSVC64_PREFIX=PATH="$(MSVS64_PATH)$(PATH)" LIB="$(MSVS64_LIB)$(LIB)" INCLUDE="$(MSVS64_INC)$(INCLUDE)" 100 | 101 | MSVCC = cl.exe 102 | MSVCC64 = cl.exe 103 | endif 104 | endif 105 | 106 | FLEXDLL_WARN_ERROR ?= 107 | ifeq ($(FLEXDLL_WARN_ERROR),true) 108 | GCC_FLAGS += -Wextra -std=c99 -Werror -fdiagnostics-color=always 109 | MSVC_FLAGS += /WX 110 | endif 111 | 112 | show_root: 113 | ifeq ($(MSVCC_ROOT),) 114 | @echo "$(MSVS_PATH)" 115 | @echo "$(MSVS_LIB)" 116 | else 117 | @echo "$(MSVCC_ROOT)" 118 | @echo "$(MSVC_LIB)" 119 | endif 120 | 121 | OCAMLOPT = ocamlopt -g 122 | 123 | #OCAMLOPT += -strict-sequence -strict-formats -safe-string -w +A-9 124 | 125 | #OCAMLOPT = FLEXLINKFLAGS=-real-manifest ocamlopt 126 | #LINKFLAGS = unix.cmxa 127 | 128 | ifeq ($(TOOLCHAIN), msvc) 129 | RC = rc 130 | RES=version.res 131 | ifeq ($(ARCH), i386) 132 | RES_PREFIX=$(MSVC_PREFIX) 133 | else 134 | RES_PREFIX=$(MSVC64_PREFIX) 135 | endif 136 | else 137 | RC = $(TOOLPREF)windres 138 | RES=version_res.o 139 | RES_PREFIX= 140 | endif 141 | 142 | ifeq ($(NATDYNLINK), false) 143 | #when ocaml is not built with flexlink i.e. -no-shared-libs 144 | LINKFLAGS = -cclib "$(RES)" 145 | else 146 | LINKFLAGS = -cclib "-link $(RES)" 147 | endif 148 | 149 | support: $(addprefix build_, $(CHAINS)) 150 | 151 | build_gnat: flexdll_gnat.o flexdll_initer_gnat.o 152 | build_msvc: flexdll_msvc.obj flexdll_initer_msvc.obj 153 | build_msvc64: flexdll_msvc64.obj flexdll_initer_msvc64.obj 154 | build_cygwin64: flexdll_cygwin64.o flexdll_initer_cygwin64.o 155 | build_mingw: flexdll_mingw.o flexdll_initer_mingw.o 156 | build_mingw64: flexdll_mingw64.o flexdll_initer_mingw64.o 157 | 158 | OBJS = version.ml Compat.ml coff.ml cmdline.ml create_dll.ml reloc.ml 159 | 160 | COMPILER-$(COMPAT_VERSION): 161 | rm -f COMPILER-* 162 | touch COMPILER-$(COMPAT_VERSION) 163 | 164 | test_ver = $(shell if [ $(COMPAT_VERSION) -ge $(1) ] ; then echo ge ; fi) 165 | 166 | # This list must be in order 167 | COMPAT_LEVEL := \ 168 | $(strip $(if $(call test_ver,40100),401) \ 169 | $(if $(call test_ver,40200),402) \ 170 | $(if $(call test_ver,40300),403) \ 171 | $(if $(call test_ver,40400),404) \ 172 | $(if $(call test_ver,40500),405) \ 173 | $(if $(call test_ver,40600),406) \ 174 | $(if $(call test_ver,40700),407) \ 175 | $(if $(call test_ver,40800),408)) 176 | 177 | Compat.ml: Compat.ml.in COMPILER-$(COMPAT_VERSION) 178 | sed -E -e '$(if $(COMPAT_LEVEL),/^$(subst $(SPACE),:|^,$(COMPAT_LEVEL)):/d;)s/^[0-9]*://' $< > $@ 179 | 180 | flexlink.exe: $(OBJS) $(RES) 181 | @echo Building flexlink.exe with TOOLCHAIN=$(TOOLCHAIN) for OCaml $(OCAML_VERSION) 182 | rm -f $@ 183 | $(RES_PREFIX) $(OCAMLOPT) -o $@ $(LINKFLAGS) $(OBJS) 184 | 185 | # VERSION at present is x.y, but there would be no reason not to have x.y.z in 186 | # future. Windows versions have four components. $(FLEXDLL_FULL_VERSION) adds 187 | # additional .0s to the right of $(VERSION) such that $(FLEXDLL_FULL_VERSION) 188 | # has four version components. 189 | # Thus if VERSION=0.43, then FLEXDLL_FULL_VERSION=0.43.0.0 190 | # $(FLEXDLL_VS_VERSION_INFO) is the same value, but using a ',' to separate the 191 | # items rather than a '.', as this is the format used in a VS_VERSION_INFO block 192 | # in Resource Compiler format. 193 | FLEXDLL_FULL_VERSION = \ 194 | $(subst $(SPACE),.,$(wordlist 1, 4, $(subst .,$(SPACE),$(VERSION)) 0 0 0)) 195 | FLEXDLL_VS_VERSION_INFO = $(subst .,$(COMMA),$(FLEXDLL_FULL_VERSION)) 196 | 197 | RC_FLAGS = \ 198 | /d FLEXDLL_VS_VERSION_INFO=$(FLEXDLL_VS_VERSION_INFO) \ 199 | /d FLEXDLL_FULL_VERSION=\"$(FLEXDLL_FULL_VERSION)\" 200 | 201 | # cf. https://sourceware.org/bugzilla/show_bug.cgi?id=27843 202 | WINDRES_FLAGS = \ 203 | -D FLEXDLL_VS_VERSION_INFO=$(FLEXDLL_VS_VERSION_INFO) \ 204 | -D FLEXDLL_FULL_VERSION=\\\"$(FLEXDLL_FULL_VERSION)\\\" 205 | 206 | version.res: version.rc flexdll.opam 207 | $(RES_PREFIX) $(RC) /nologo $(RC_FLAGS) $< 208 | 209 | version_res.o: version.rc flexdll.opam 210 | $(RC) $(WINDRES_FLAGS) -i $< -o $@ 211 | 212 | flexdll_msvc.obj: flexdll.c flexdll.h 213 | $(MSVC_PREFIX) $(MSVCC) $(MSVC_FLAGS) /DMSVC -c /Fo"$@" $< 214 | 215 | flexdll_msvc64.obj: flexdll.c flexdll.h 216 | $(MSVC64_PREFIX) $(MSVCC64) $(MSVC_FLAGS) /DMSVC /DMSVC64 -c /Fo"$@" $< 217 | 218 | flexdll_cygwin64.o: flexdll.c flexdll.h 219 | $(CYG64CC) $(GCC_FLAGS) -DCYGWIN -c -o $@ $< 220 | 221 | flexdll_mingw.o: flexdll.c flexdll.h 222 | $(MINCC) $(GCC_FLAGS) -DMINGW -c -o $@ $< 223 | 224 | flexdll_gnat.o: flexdll.c flexdll.h 225 | gcc -c -o $@ $< 226 | 227 | flexdll_mingw64.o: flexdll.c flexdll.h 228 | $(MIN64CC) $(GCC_FLAGS) -DMINGW -c -o $@ $< 229 | 230 | flexdll_initer_msvc.obj: flexdll_initer.c 231 | $(MSVC_PREFIX) $(MSVCC) $(MSVC_FLAGS) -c /Fo"$@" $< 232 | 233 | flexdll_initer_msvc64.obj: flexdll_initer.c 234 | $(MSVC64_PREFIX) $(MSVCC64) $(MSVC_FLAGS) -c /Fo"$@" $< 235 | 236 | flexdll_initer_cygwin64.o: flexdll_initer.c 237 | $(CYG64CC) $(GCC_FLAGS) -c -o $@ $< 238 | 239 | flexdll_initer_mingw.o: flexdll_initer.c 240 | $(MINCC) $(GCC_FLAGS) -c -o $@ $< 241 | 242 | flexdll_initer_gnat.o: flexdll_initer.c 243 | gcc -c -o $@ $< 244 | 245 | flexdll_initer_mingw64.o: flexdll_initer.c 246 | $(MIN64CC) $(GCC_FLAGS) -c -o $@ $< 247 | 248 | 249 | demo_msvc: flexlink.exe flexdll_msvc.obj flexdll_initer_msvc.obj 250 | $(MSVC_PREFIX) $(MAKE) -C test clean demo CHAIN=msvc CC="$(MSVCC)" CFLAGS="$(MSVC_FLAGS)" PLUG2_CFLAGS="/bigobj" O=obj 251 | 252 | demo_cygwin64: flexlink.exe flexdll_cygwin64.o flexdll_initer_cygwin64.o 253 | $(MAKE) -C test clean demo CHAIN=cygwin64 CC="$(CYG64CC)" CFLAGS="$(GCC_FLAGS)" O=o RUN="PATH=\"/cygdrive/c/cygwin64/bin:$(PATH)\"" 254 | 255 | demo_mingw: flexlink.exe flexdll_mingw.o flexdll_initer_mingw.o 256 | $(MAKE) -C test clean demo CHAIN=mingw CC="$(MINCC)" CFLAGS="$(GCC_FLAGS)" O=o 257 | 258 | demo_mingw64: flexlink.exe flexdll_mingw64.o flexdll_initer_mingw64.o 259 | $(MAKE) -C test clean demo CHAIN=mingw64 CC="$(MIN64CC)" CFLAGS="$(GCC_FLAGS)" O=o 260 | 261 | demo_msvc64: flexlink.exe flexdll_msvc64.obj flexdll_initer_msvc64.obj 262 | $(MSVC64_PREFIX) $(MAKE) -C test clean demo CHAIN=msvc64 CC="$(MSVCC64)" CFLAGS="$(MSVC_FLAGS)" PLUG2_CFLAGS="/bigobj" O=obj 263 | 264 | distclean: clean 265 | rm -f Makefile.winsdk 266 | 267 | clean: 268 | rm -f *.obj *.o *.lib *.a *.exe *.opt *.cmx *.dll *.exp *.cmi *.cmo *~ version.res version.ml COMPILER-* Compat.ml 269 | $(MAKE) -C test clean 270 | 271 | 272 | ## Packaging 273 | 274 | COMMON_FILES = LICENSE README.md CHANGES flexdll.h flexdll.c flexdll_initer.c default.manifest default_amd64.manifest 275 | URL = frisch@frisch.fr:www/flexdll/ 276 | 277 | # Source packages 278 | 279 | PACKAGE = flexdll-$(VERSION).tar.gz 280 | 281 | package_src: 282 | rm -Rf flexdll-$(VERSION) 283 | mkdir flexdll-$(VERSION) 284 | mkdir flexdll-$(VERSION)/test 285 | cp -a $(filter-out version.ml,$(OBJS:Compat.ml=Compat.ml.in)) Makefile msvs-detect $(COMMON_FILES) version.rc flexdll.install flexdll.opam flexdll-$(VERSION)/ 286 | cp -aR test/Makefile test/*.c flexdll-$(VERSION)/test/ 287 | tar czf $(PACKAGE) flexdll-$(VERSION) 288 | rm -Rf flexdll-$(VERSION) 289 | 290 | upload: 291 | rsync $(PACKAGE) CHANGES LICENSE $(URL) 292 | 293 | upload_dev: 294 | $(MAKE) VERSION=dev upload_src 295 | 296 | upload_src: package_src upload 297 | 298 | # Binary package 299 | 300 | PACKAGE_BIN = flexdll-bin-$(VERSION)$(PACKAGE_BIN_SUFFIX).zip 301 | INSTALLER = flexdll-$(VERSION)$(PACKAGE_BIN_SUFFIX)-setup.exe 302 | 303 | package_bin: 304 | $(MAKE) clean all 305 | rm -f $(PACKAGE_BIN) 306 | zip $(PACKAGE_BIN) $(COMMON_FILES) \ 307 | flexlink.exe flexdll_*.obj flexdll_*.o flexdll.c flexdll_initer.c 308 | 309 | do_upload_bin: 310 | rsync $(PACKAGE_BIN) $(URL) 311 | 312 | upload_bin: package_bin do_upload_bin 313 | 314 | show_toolchain: 315 | @echo Toolchain for the visible ocamlopt: $(TOOLCHAIN) 316 | 317 | swap: 318 | $(OCAMLOPT) -o flexlink-new.exe $(LINKFLAGS) $(OBJS) 319 | cp flexlink.exe flexlink.exe.bak 320 | cp flexlink-new.exe flexlink.exe 321 | 322 | PREFIX = "C:\Program Files (x86)\flexdll" 323 | 324 | install: 325 | mkdir -p $(PREFIX) 326 | cp $(COMMON_FILES) flexlink.exe flexdll_*.obj flexdll_*.o flexdll.c flexdll_initer.c $(PREFIX) 327 | 328 | installer: 329 | rm -rf flexdll_install_files 330 | mkdir flexdll_install_files 331 | (cd flexdll_install_files && unzip ../$(PACKAGE_BIN)) 332 | /cygdrive/c/Program\ Files\ \(x86\)/NSIS/makensis installer.nsi 333 | mv flexdll_setup.exe $(INSTALLER) 334 | 335 | upload_installer: 336 | rsync $(INSTALLER) $(URL) 337 | 338 | 339 | upload_all: 340 | $(MAKE) upload_src upload_bin installer upload_installer 341 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FlexDLL: an implementation of a dlopen-like API for Windows 2 | 3 | [![Build status](https://ci.appveyor.com/api/projects/status/github/ocaml/flexdll?svg=true)](https://ci.appveyor.com/project/dra27/flexdll-7sptc) 4 | 5 | ## Introduction 6 | 7 | Under Windows, DLL ([Dynamically-Linked Libraries][DLL]) are generally 8 | used to improve code modularity and sharing. A DLL can be loaded 9 | automatically when the program is loaded (if it requires the DLL). 10 | The program can also explicitly request Windows to load a DLL at any 11 | moment during runtime, using the [`LoadLibrary`][LoadLibrary] function 12 | from the Win32 API. 13 | 14 | This naturally suggests to use DLLs as a plugin mechanism. For instance, 15 | a web server could load extensions modules stored in DLLs at runtime. 16 | But Windows does not really make it easy to implement plugins that way. 17 | The reason is that when you try to create a DLL from a set of object 18 | files, the linker needs to resolve all the symbols, which leads to the 19 | very problem solved by FlexDLL: 20 | 21 | **Windows DLL cannot refer to symbols defined in the main application or 22 | in previously loaded DLLs.** 23 | 24 | Some usual solutions exist, but they are not very flexible. A notable 25 | exception is the [edll][] library (its homepage also describes the usual 26 | solutions), which follows a rather drastic approach; indeed, edll 27 | implements a new dynamic linker which can directly load object files 28 | (without creating a Windows DLL). 29 | 30 | FlexDLL is another solution to the same problem. Contrary to edll, it 31 | relies on the native static and dynamic linkers. Also, it works both 32 | with the Microsoft environment (MS linker, Visual Studio compilers) and 33 | with Cygwin (GNU linker and compilers, in Cygwin or MinGW mode). 34 | Actually, FlexDLL implements mostly the usual [`dlopen`][dlopen] POSIX 35 | API, without trying to be fully conformant though (e.g. it does not 36 | respect the official priority ordering for symbol resolution). This 37 | should make it easy to port applications developed for Unix. 38 | 39 | [DLL]: https://en.wikipedia.org/wiki/Dynamic-link_library 40 | [LoadLibrary]: https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryw 41 | [edll]: https://edll.sourceforge.net/ 42 | [dlopen]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/dlopen.html 43 | 44 | ## About 45 | 46 | FlexDLL is distributed under the terms of a zlib/libpng open source 47 | [license](LICENSE). The copyright holder is the Institut National de 48 | Recherche en Informatique et en Automatique (INRIA). The project was 49 | started when I (= Alain Frisch) was working at INRIA. I'm now working 50 | for [LexiFi][], which is kind enough to let me continue my work on 51 | FlexDLL. My office mate at INRIA, Jean-Baptiste Tristan, coined the name 52 | FlexDLL. 53 | 54 | The runtime support library is written in C. The `flexlink` wrapper is 55 | implemented in the wonderful [OCaml][] language. 56 | 57 | [LexiFi]: https://www.lexifi.com 58 | [OCaml]: https://ocaml.org/ 59 | 60 | ## Supported toolchains 61 | 62 | MSVC: the 32-bit C compiler from Microsoft. 63 | 64 | MSVC64: the 64-bit C compiler from Microsoft. 65 | 66 | CYGWIN64: the 64-bit gcc compiler shipped with Cygwin. 67 | 68 | MINGW: the 32-bit gcc compiler from the MinGW-w64 project, packaged in 69 | Cygwin (as i686-w64-mingw32-gcc). 70 | 71 | MINGW64: the 64-bit gcc compiler from the MinGW-w64 project, packaged in 72 | Cygwin (as x86\_64-w64-mingw32-gcc). 73 | 74 | LD: an internal linker to produce .dll (only). 75 | 76 | ## Download 77 | 78 | - [Source and binary releases](https://github.com/ocaml/flexdll/releases). 79 | - [Development version](https://github.com/ocaml/flexdll). 80 | - [Changelog](CHANGES). 81 | 82 | **Installation instructions:** Simply run the installer and add the 83 | resulting directory (e.g. `C:\Program Files\flexdll` or 84 | `C:\Program Files (x86)\flexdll`) to the PATH. You can also create this 85 | directory by hand and unzip the .zip file in it. 86 | 87 | **Compiling from sources:** To compile the code from sources, you'll 88 | need a working installation of OCaml, GNU Make, and a C toolchain 89 | (compiler + linker) either the one from Microsoft (any version of Visual 90 | Studio should work), Cygwin, or MinGW. It is probably a good idea to use 91 | a native version of ocamlopt (not the Cygwin port) to compile flexlink. 92 | By default, the `Makefile` will compile support objects for the 93 | supported toolchains; you can choose a subset with the `CHAINS` 94 | variable, e.g.: `make CHAINS="mingw msvc"`. 95 | 96 | 97 | ## Overview 98 | 99 | FlexDLL has two components: a wrapper around the static linker, and a 100 | tiny runtime library to be linked with the main application. The wrapper 101 | must be called in place of the normal linker when you want to produce a 102 | DLL or to link the main application. The runtime library relies 103 | internally on the native `LoadLibrary` API to implement a dlopen-like 104 | interface. 105 | 106 | Let's see a simple example of a plugin. Here is the code for the main 107 | program (file `dump.c`): 108 | 109 | ```c 110 | #include 111 | #include "flexdll.h" 112 | 113 | typedef void torun(void); 114 | 115 | void api(char *msg){ printf("API: %s\n", msg); } 116 | 117 | int main(int argc, char **argv) 118 | { 119 | void *sym; 120 | void *handle; 121 | int i; 122 | torun *torun; 123 | 124 | for (i = 1; i < argc; i++) { 125 | handle = flexdll_dlopen(argv[i], FLEXDLL_RTLD_GLOBAL); 126 | 127 | if (NULL == handle) { printf("error: %s\n", flexdll_dlerror()); exit(2); } 128 | 129 | torun = flexdll_dlsym(handle, "torun"); 130 | if (torun) torun(); 131 | } 132 | exit(0); 133 | } 134 | ``` 135 | 136 | This application opens in turn all the DLLs given on its command line, 137 | using the FlexDLL function `flexdll_dlopen`. For each DLL, the program 138 | looks for a symbol named `torun` (which is supposed to refer to a 139 | function) and if the symbol is available in the DLL, the function is 140 | called. The program also provides a very simple API to its plugin: the 141 | `api` function. The `FLEX_RTLD_GLOBAL` flag makes all the symbols 142 | exported by each DLL available for the DLL to be loaded later. 143 | 144 | This main program can be compiled and linked like the commands below 145 | (the `[...]` refers to the directory where FlexDLL is installed). 146 | 147 | ```sh 148 | # MSVC 149 | cl /nologo /MD -I[...] -c dump.c 150 | flexlink -chain msvc -exe -o dump.exe dump.obj 151 | 152 | # MINGW 153 | i686-w64-mingw32-gcc -I[...] -c dump.c 154 | flexlink -chain mingw -exe -o dump.exe dump.o 155 | 156 | # CYGWIN 157 | gcc -I[...] -c dump.c 158 | flexlink -chain cygwin64 -exe -o dump.exe dump.o 159 | ``` 160 | 161 | The compilation step is completely standard, but in order to link the 162 | main application, you must call the `flexlink` tool, which is the 163 | wrapper around the linker. The `-chain` command line switch selects 164 | which linker to use, and the `-exe` option tells the wrapper that it 165 | must produce a stand-alone application (not a DLL). 166 | 167 | Now we can provide a first plugin (file `plug1.c`): 168 | 169 | ```c 170 | int x = 3; 171 | void dump_x(void) { printf("x=%i\n", x); } 172 | void torun(void) { api("plug1.torun();"); } 173 | ``` 174 | 175 | Note that the plugin uses the `api` symbol from the main application (it 176 | would be cleaner to introduce it with an `extern` declaration). You can 177 | compile and link this plugin (into a DLL) with the following commands: 178 | 179 | ```sh 180 | # MSVC 181 | cl /nologo /MD -c plug1.c 182 | flexlink -chain msvc -o plug1.dll plug1.obj 183 | 184 | # MINGW 185 | i686-w64-mingw32-gcc -c plug1.c 186 | flexlink -chain mingw -o plug1.dll plug1.o 187 | 188 | # CYGWIN 189 | gcc -D_CYGWIN_ -c plug1.c 190 | flexlink -chain cygwin64 -o plug1.dll plug1.o 191 | ``` 192 | 193 | And now you can ask the main program to load the plugin: 194 | 195 | ```console 196 | $ ./dump plug1.dll 197 | API: plug1.torun(); 198 | ``` 199 | 200 | Here is the code for a second plugin (file `plug2.c`) that refers to 201 | symbols (a function and a global variable) defined in the first plugin: 202 | 203 | ```c 204 | extern int x; 205 | 206 | void torun(void) { 207 | api("plug2.torun();"); 208 | 209 | dump_x(); 210 | x = 100; 211 | dump_x(); 212 | } 213 | ``` 214 | 215 | Since the second plugin depends on the first one, you need to load both: 216 | 217 | ```console 218 | $ ./dump plug2.dll 219 | error: Cannot resolve dump_x 220 | $ ./dump plug1.dll plug2.dll; 221 | API: plug1.torun(); 222 | API: plug2.torun(); 223 | x=3 224 | x=100 225 | ``` 226 | 227 | Simple, isn't it? No `declspec` declaration, no import library to deal 228 | with, … 229 | 230 | 231 | ## How it works 232 | 233 | Object files (.obj/.o) contain relocation information that explain to 234 | the linker how some addresses in their code or data sections have to be 235 | patched, using the value of some global symbols. When the static linker 236 | is invoked to produce a DLL from a set of object files, it assumes that 237 | all the relocations can be performed: all the symbols which are used in 238 | relocations must be defined in some the objects linked together. 239 | FlexDLL drops this constraint following a very simple idea: when a 240 | relocation refers to a symbol which is not available, the relocation is 241 | turned into a piece of data that will be passed to the runtime support 242 | library. 243 | 244 | In the example above, the `plug1.obj` object refers to a symbol 245 | `api`. When this object is turned into a DLL, FlexDLL produce a new 246 | temporary object file derived from `plug1.obj` without the relocation 247 | that mentions `api`. Instead, it adds an “import table”, which is just a 248 | piece of data that tells the FlexDLL support library which address has 249 | to be patched with the value of a symbol called `api` to be found 250 | somewhere else. You can see the list of such imported symbols by adding 251 | the `-show-imports` option to the `flexlink` command line: 252 | 253 | ```console 254 | $ ../flexlink -chain msvc -o plug1.dll plug1.obj -show-imports 255 | ** Imported symbols for plug1.obj: 256 | _api 257 | ``` 258 | 259 | When the `flexdll_dlopen` function opens this DLL, it will look for an 260 | internal symbol that points to the import table, resolve the symbols and 261 | patch the code and data segments accordingly. The FlexDLL runtime 262 | library must thus maintain a set of symbols together with their concrete 263 | values (addresses). In particular, it knows about the global symbols 264 | defined in the main program. Indeed, when you link the main program with 265 | `flexlink -exe`, the wrapper produces a small fresh object file that 266 | contains a symbol table, mapping symbol names to their addresses. 267 | 268 | ```console 269 | $ ../flexlink -chain msvc -exe -o dump.exe dump.obj -show-exports 270 | ** Exported symbols: 271 | _api 272 | _flexdll_dlclose 273 | _flexdll_dlerror 274 | _flexdll_dlopen 275 | _flexdll_dlsym 276 | _flexdll_dump_exports 277 | _flexdll_dump_relocations 278 | _main 279 | ``` 280 | 281 | As you can see, all the global symbols (including those that comes from 282 | FlexDLL itself) appear in the global symbol table. FlexDLL knows not 283 | only about symbols that comes from the main program, but also about 284 | symbols exported by the DLL it loads. This is needed to implement the 285 | `flexdll_dlsym` function, but also to deal with import tables that 286 | mention symbols defined in previously loaded DLLs (for which the 287 | `FLEXDLL_RTLD_GLOBAL` was used). So the wrapper produces not only an 288 | import table for DLLs, but also an export table: 289 | 290 | ```console 291 | $ ../flexlink -chain msvc -o plug1.dll plug1.obj -show-imports -show-exports 292 | ** Imported symbols for plug1.obj: 293 | _api 294 | ** Exported symbols: 295 | _dump_x 296 | _torun 297 | _x 298 | 299 | $ ../flexlink -chain msvc -o plug2.dll plug2.obj -show-imports -show-exports 300 | ** Imported symbols for plug2.obj: 301 | _api 302 | _dump_x 303 | _x 304 | ** Exported symbols: 305 | _torun 306 | ``` 307 | 308 | How does FlexDLL determine which symbols are imported or exported? It 309 | uses an algorithm similar to the linker itself. The command line 310 | mentions a number of object and library files. In a first pass, the 311 | wrapper computed which objects embedded in those libraries will be used. 312 | To do that, it looks at which symbols are used, and where they are 313 | defined. Then the wrapper considers that all the global (external) 314 | symbols are exported. Note that the `/export` or `__declspec(dllexport)` 315 | directives are not used: all the symbols are exported. (In a future 316 | version, FlexDLL will allow to control more precisely which symbols are 317 | exported). All the object files (given explicitly, or embedded in a 318 | library) that need to import symbols must be rewritten. The `flexlink` 319 | wrapper will produce new temporary object files for them. If you want to 320 | understand better how FlexDLL works, you can use the `-v` and 321 | `-save-temps` command options to tell the wrapper to show you the linker 322 | command line and to preserve those temporary files alive (by default, they 323 | are removed automatically). 324 | 325 | Some object files can mention default libraries (they correspond to the 326 | `/defaultlib` linker flag, which is often embedded in the object 327 | `.drectve` section). FlexDLL will parse those libraries, but only to see 328 | which symbols they define. Those symbols are not considered as being 329 | imported by the DLL, but they won't be exported either. A typical case 330 | of default libraries are import libraries that behave as interfaces to 331 | (normal, non-FlexDLL) DLLs. 332 | 333 | 334 | ## Advanced topic: `__declspec(dllimport)` 335 | 336 | C compilers under Windows support a special declaration of external 337 | symbols. You can write: 338 | 339 | ```c 340 | __declspec(dllimport) extern int mysymbol; 341 | ``` 342 | 343 | Internally, this declaration has the same effect as declaring: 344 | 345 | ```c 346 | extern int *_imp__mysymbol; 347 | ``` 348 | 349 | and using `&x` instead of `x` everywhere in the current unit. In other 350 | words, even if your code seems to access `x` directly, each access 351 | actually goes through an extra indirection. 352 | 353 | FlexDLL knows about this convention. When a object refers to a symbol of 354 | the form `_imp__XXX` which is not available in the objects that will 355 | form the DLL to be created, it resists the temptation of putting an 356 | entry for `_imp__XXX` in the import table. Instead, it adds the 357 | equivalent of the following declaration: 358 | 359 | ```c 360 | void *_imp__XXX = &XXX; 361 | ``` 362 | 363 | If the symbol `XXX` itself is not available, this will in turn produce 364 | an entry for `XXX` in the import table. All these new declarations are 365 | put in the same object file that contain the export table, which is 366 | global for the DLL to be produced. So, if all the external symbols in a 367 | given object files are accessed through this convention, the object file 368 | need not be patched at all. 369 | 370 | Note that you can define and use the `_imp__XXX` symbols by hand, you 371 | don't have to use the `__declspec(dllimport)` notation (this is useful 372 | if you use a compiler that doesn't support this notation). 373 | 374 | There *are* compelling reasons to adopt this style. 375 | 376 | A very small advantage might be that there will be fewer relocations at 377 | runtime and that more code pages can be shared amongst several instances 378 | of the same DLL used by different processes. 379 | 380 | A big advantage on x86_64 systems is to avoid relocation errors like: 381 | 382 | ```text 383 | Fatal error: cannot load shared library plug1 384 | Reason: flexdll error: cannot relocate XXX 385 | RELOC_REL32, target is too far: FFFFFFFF2EDEC956 000000002EDEC956 386 | ``` 387 | 388 | Without `__declspec(dllimport)` your symbol (ex. `extern XXX`) generates 389 | the following pseudo-assembly: 390 | 391 | ```asm 392 | CALL XXX ; this is a 32-bit relative address 393 | ``` 394 | 395 | These 32-bit relative addresses are restricted to 2GiB jumps, and there is 396 | no guarantee that your main programs and all its DLLs will reside in the same 397 | 2GiB block of virtual memory. The more DLLs you have, and the bigger they 398 | are, the more likely you will encounter a `RELOC_REL32` error. 399 | 400 | flexdll will verify every 32-bit relative address `CALL` during 401 | `flexdll_dlopen` to ensure they are within 2GiB. 402 | However, the indirection introduced by `__declspec(dllimport) extern` makes 403 | the pseudo-assembly instead: 404 | 405 | ```asm 406 | void *_imp__XXX = &XXX; 407 | CALL [_imp__XXX] ; this is a 64-bit indirect address 408 | ``` 409 | 410 | which works well with flexdll. 411 | 412 | ## Advanced topic: static constructors and the entry point 413 | 414 | A Windows DLL can define an optional entry point. When the DLL is 415 | loaded, this function is automatically called. (The same function is 416 | called when the DLL is unloaded, or when threads are spawned or 417 | destroyed.) 418 | 419 | Usually, the real entry point is provided by the C runtime library: 420 | `_cygwin_dll_entry` for Cygwin, `DllMainCRTStartup` for MinGW, 421 | `_DllMainCRTStartup` for MSVC. These functions perform various 422 | initialization for the C runtime library, invoke the code that has to be 423 | run automatically at load time (e.g for C++: constructors of static 424 | objects, or right-hand sides of non-constant initializers for global 425 | variables), and then call the function [`DllMain`][DllMain], which by 426 | default does nothing but can be overridden by the program to perform 427 | custom initialization of the DLL. 428 | 429 | FlexDLL must take control before any custom code (static constructors, 430 | `DllMain`) so as to perform relocations (in case this code refers to 431 | symbols found in the main program or previously loaded DLLs). As a 432 | consequence, FlexDLL defines its own entry point, which first asks the 433 | main program to perform relocations and then calls the regular entry 434 | point of the C runtime library. This behavior is implemented in the 435 | `flexdll_initer.c` file, and the corresponding object file (whose name 436 | depends on the toolchain) is automatically included by `flexlink`. 437 | 438 | It is possible to completely disable the DLL entry point with the 439 | `-noentry` option passed to `flexlink`. In this case, FlexDLL will 440 | perform relocations after the DLL has been opened. 441 | 442 | [DllMain]: https://learn.microsoft.com/en-us/windows/win32/dlls/dllmain 443 | 444 | ## The API 445 | 446 | Here is the content of the `flexdll.h` file: 447 | 448 | ```c 449 | #define FLEXDLL_RTLD_GLOBAL 0x0001 450 | #define FLEXDLL_RTLD_LOCAL 0x0000 451 | #define FLEXDLL_RTLD_NOEXEC 0x0002 452 | 453 | void *flexdll_dlopen(const char *, int); 454 | #ifndef CYGWIN 455 | void *flexdll_wdlopen(const wchar_t *, int); 456 | #endif 457 | void *flexdll_dlsym(void *, const char *); 458 | void flexdll_dlclose(void *); 459 | char *flexdll_dlerror(void); 460 | 461 | void flexdll_dump_exports(void *); 462 | void flexdll_dump_relocations(void *); 463 | ``` 464 | 465 | The `flexdll_dl*` functions are mostly compatible with their POSIX 466 | counterparts. Here is a short explanation of their semantics. 467 | 468 | The most important function is `flexdll_dlopen`. The first argument 469 | gives the filename of a DLL to be opened. This DLL must have been 470 | produced by the `flexlink` wrapper. The function resolves the symbols 471 | mentioned in the DLL's import table and performs the relocations. 472 | The second argument is a mode, made of flags that can be or'ed together. 473 | The flag `FLEXDLL_RTLD_GLOBAL` means that the symbols exported by the 474 | opened DLL can be used to resolve relocations for DLLs to be opened 475 | later on. The flag `FLEXDLL_RTLD_NOEXEC` opens the DLL is a special 476 | mode, disabling the automatic loading of dependencies and the FlexDLL 477 | resolution pass. This is useful if you want to open a DLL only to check 478 | whether it defines some symbol. 479 | 480 | The `flexdll_dlopen` function returns a pointer to an opaque handle that 481 | can be used as an argument to the other API functions. If the filename 482 | is `NULL`, the function returns a special handle which refers to the 483 | global unit: it includes all the static symbols, plus the symbols from 484 | the DLLs opened with the `FLEXDLL_RTLD_GLOBAL` flag. A given DLL will be 485 | opened only once, even if you call the function several times on the 486 | same file. The `FLEXDLL_RTLD_GLOBAL` flag is sticky: if one of the calls 487 | mentions it, it will stay forever, even if the corresponding handle is 488 | then passed to `dlclose` (this is because the same handle is actually 489 | returned for all the calls). If an error occurs during the call to 490 | `flexdll_dlopen`, the functions returns `NULL` and the error message can 491 | be retrieved using `flexdll_dlerror`. 492 | 493 | The function `flexdll_wdlopen` is a wide-character version of 494 | `flexdll_dlopen`. The filename argument to `flexdll_wdlopen` is a 495 | wide-character string. `flexdll_wdlopen` and `flexdll_dlopen` behave 496 | identically otherwise. 497 | 498 | The second most important function is `flexdll_dlsym` which looks for a 499 | symbol whose name is the second argument. The first argument can be 500 | either a regular handle returned by `flexdll_dlopen` (the symbol is 501 | searched only in the corresponding DLL), the special handle for the 502 | global unit as return by a call to `flexdll_dlopen(NULL,...)` (the 503 | symbol is searched amongst the static symbols plus the ones in the DLL 504 | opened with the flag `FLEXDLL_RTLD_GLOBAL`), or `NULL` (the symbol is 505 | searched only amongst the static symbols). If the symbol cannot be 506 | found, the function returns `NULL`. 507 | 508 | The same symbol name can be defined several times. The policy used to 509 | choose amongst the various definitions is not specified. This applies 510 | both to the automatic resolution that happens when you open a DLL and to 511 | the explicit resolution performed by `dlsym`. 512 | 513 | The function `flexdll_dlclose` must be used with caution. It decrements 514 | the reference counter for the given handle and releases the DLL from memory 515 | when the counter reaches 0. After that time, symbols defined in this DLL 516 | are no longer used for resolution. You most probably don't want to close 517 | a DLL if you still hold pointers to some of its symbols. 518 | 519 | The two functions `flexdll_dump_exports` and `flexdll_dump_relocations` 520 | are used to display (to the standard output) the internal tables 521 | associated with a given DLL handle. 522 | 523 | 524 | ## Command line for the flexlink wrapper 525 | 526 | ``` 527 | Usage: 528 | flexlink -o file1.obj file2.obj ... -- 529 | 530 | -o Choose the name of the output file 531 | -exe Link the main program as an exe file 532 | -maindll Link the main program as a dll file 533 | -noflexdllobj Do not add the Flexdll runtime object (for exe) 534 | -noentry Do not use the Flexdll entry point (for dll) 535 | -noexport Do not export any symbol 536 | -norelrelocs Ensure that no relative relocation is generated 537 | -base Specify base address (Win64 only) 538 | -pthread Pass -pthread to the linker 539 | -I Add a directory where to search for files 540 | -L Add a directory where to search for files 541 | -l Library file 542 | -chain {msvc|msvc64|cygwin64|mingw|mingw64|gnat|gnat64|ld} 543 | Choose which linker to use 544 | -use-linker Choose an alternative linker to use 545 | -use-mt Choose an alternative manifest tool to use 546 | -x64 (Deprecated) 547 | -defaultlib External object (no export, no import) 548 | -save-temps Do not delete intermediate files 549 | -implib Do not delete the generated import library 550 | -outdef Produce a def file with exported symbols 551 | -v Increment verbosity (can be repeated) 552 | -show-exports Show exported symbols 553 | -show-imports Show imported symbols 554 | -dry Show the linker command line, do not actually run it 555 | -dump Only dump the content of object files 556 | -patch Only patch the target image (to be used with -stack) 557 | -nocygpath Do not use cygpath (default for msvc, mingw) 558 | -cygpath Use cygpath (default for cygwin) 559 | -no-merge-manifest Do not merge the manifest (takes precedence over -merge-manifest) 560 | -merge-manifest Merge manifest to the dll or exe (if generated) 561 | -real-manifest Use the generated manifest (default behavior) 562 | -default-manifest Use the default manifest (default.manifest/default_amd64.manifest) 563 | -export Explicitly export a symbol 564 | -noreexport Do not reexport symbols imported from import libraries 565 | -where Show the FlexDLL directory 566 | -nounderscore Normal symbols are not prefixed with an underscore 567 | -nodefaultlibs Do not assume any default library 568 | -builtin Use built-in linker to produce a dll 569 | -explain Explain why library objects are linked 570 | -subsystem Set the subsystem (default: console) 571 | -custom-crt Use a custom CRT 572 | -stack Set the stack reserve in the resulting image 573 | -link