Load Library

lua-users home
wiki

On Windows, binary modules are dynamically loaded into Lua as [DLLs]. Windows almost always loads DLLs via the [LoadLibrary] or [LoadLibraryEx] Win32 API functions. These functions have Unicode (LoadLibraryW/LoadLibraryEx) and ANSI (LoadLibraryA/LoadLibraryExA) variants for the file name parameter. In Lua 5.1, loadlib.c calls LoadLibraryA. In Lua 5.2-beta, loadlib.c calls LoadLibraryExA, and its third parameter is taken from the LUA_LLE_FLAGS define (which defaults to 0).

Dll Path Rules

See [Dynamic-Link Library Search Order] for the rules governing the order of search paths that Windows looks for the DLL in. These rules are complicated and tend to change between versions of Windows. (Also, Windows Embedded (CE) [1]/Mobile [2] have special rules described later below.) In particular...

First, "If the string specifies a fully qualified path, the function searches only that path for the module." [3]. No further rules below are applied. Also, if you use [DLL redirection] or manifests (see manifest/WinSxS links at bottom), those techniques are used to find the DLL before the other techniques below.

Otherwise, the following two rules are applied next:

If a match is still not found, a search order (standard or alternate) is done as described below.

Windows Embedded (CE) [1] / Mobile [2] have these special behaviors:

Placement of DLLs

A typical use case involves an application process (e.g. lua.exe) that loads binary Lua modules (e.g. foo.dll). lua.exe and foo.dll may in turn also depend directly or indirectly on other (non-Lua) library/system DLLs (e.g. bar.dll). The simplest file organization is often to put all these files in the same directory, which "largely" works under the above search rules. (There is, however, possible confusion if the binary Lua module DLL and library/system DLL names happen to collide, as is discussed later below.) Many people (e.g. LuaForWindows?) feel that the DLLs clutter up the directory containing the EXE, and they prefer to place the DLLs in a separate subdirectory that they don't typically need to look at. You can place both types of DLLs in the same subdirectory or you can further split these into two subdirectories. (Linux distributions of many scripting languages do the latter to avoid the scripting language libraries littering the system libraries.) In any case, putting these files in different directories can complicate the situation because you then need to have a mechanism for the files to find each other. Some solutions include

Caveats

Conflicts with system DLL names using relative paths: As an example, if you want to name your Lua module "winmm", which you compile to a DLL named winmm.dll, this will happen to have the same name as winmm.dll in the Windows system directory. If your place your winmm.dll in the current directory (which we'll assume differs from the EXE's directory), then the following:

package.loadlib('winmm.dll', 'luaopen_winmm')
package.loadlib('.\\winmm.dll', 'luaopen_winmm')

will both fail under "Standard Search Order for Desktop Applications" with "SafeDllSearchMode enabled". If the path provided to LoadLibraryEx is relative, as both are considered here, Windows first looks in the EXE directory (where lua.exe is located) and then in the Windows system directories before looking in the current directory. (Moreover, if your application calls [SetDllDirectory], then it will never look in the current directory.)

Note: [DLL Preloading Attacks] recommends, for security reasons, that 'If you never have a need to load a DLL from the current directory, just call SetDllDirectory with an argument of "".'

If, however, you place your winmm.dll in the same directory as lua.exe, then the above two statements usually will both work. However, if your process (or even a DLL indirectly loaded into your process by the operating system, outside of your control) tries to load the system "winmm.dll" (which it typically will try to load without an absolute path), it will instead receive your winmm.dll, which won't work and could crash. (Maybe we can avoid that problem with manifests?)

We could alternately pass an absolute path:

package.loadlib([[c:\path\to\winmm.dll]], 'luaopen_winmm')

Now, Windows will only try to load the module from the specified location, which it will begin to do. However, if your process later (rather than before) tries to also load the system "winmm.dll" (typically without an absolute path), it will fail because it sees a DLL with the same name already loaded and uses that, which is incorrect.

That above problems largely do not seem to affect the case if your module were named "foo.winmm", located in .\foo\winmm.dll, and loaded with a relative path. When .\foo\winmm.dll is passed to LoadLibraryEx, Windows appends .\foo\winmm.dll to all its search paths, and of course none of these likely exists. However, we still have problems if your process later tries to load the system "winmm.dll" without a path.

These problems could be compounded if scripting languages other than Lua also try to use a similar module naming scheme.

The above problems could be avoided by ensuring Lua module DLL names never conflict with system DLL names. You might do this by giving Lua module DLL names a globally unique suffix and/or extension--e.g. rather than ".dll" maybe ".luad" (analogous to Python .pyd and following similar conventions to .luac), ".luadll", ".lua.dll" (might not be a good idea since "Many things break when you have an extra dot in DLL names" -- MikePall in [11]), or "-lua.dll". LuaList:2011-09/msg00398.html . You will need to adjust package.cpath appropriately to handle this. On Linux, system shared libraries are prefixed by lib [12].

The above solutions, however, are insufficient on Windows CE/Mobile/Embedded, which have the additional restriction that "all path [and extension] information is ignored when determining if the DLL is already loaded". This has caused LuaSocket mime/core.dll and socket/core.dll to conflict [6]. It would also prevent the unique extension solution above, with the exception of things like -lua.dll (where the unique extension is actually part of the file name). One overall solution here, at least for embedded versions of Windows, is to have DLL names like socket_core-lua.dll, which will never conflict with mime_core-lua.dll nor any possible core.dll. A change may be made to findfile() in loadlib.c so that dots in the module name are replaced with "_" rather than "/" [6]. Lua 5.2 provides a LUA_CSUBSEP variable for this.

Other Comments

Comparisons

The common theme is that there is an effort to keep distinct DLL naming conventions for scripting language binary C modules and system libraries.

Possible Conclusions

(Footnote: Perhaps LUA_PATH should match .luac files too. On the other hand, renaming .luac files to .lua works without complicating the path.)

DLL Test code

The following test DLL in conjunction with package.loadlib may be useful in understanding this behavior.

//cl test.c /link /DLL /out:test.dll winmm.lib

#include <stdio.h>
#include <windows.h>

__declspec(dllexport) int __cdecl luaopen_winmm(void * x) {
  printf("called test, %d\n", (int)timeGetTime());
  return 0;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
  printf("load/unload my DLL\n");
  return TRUE;
}

Links

Related Topics


RecentChanges · preferences
edit · history
Last edited December 15, 2011 2:40 am GMT (diff)