Lua Proxy Dll

lua-users home
wiki

This implements a thin proxy Win32 DLL named lua5.1.dll and that is a drop-in replacement of the LuaBinaries DLL of the same name. It has almost no implementation of its own but instead transparently passes calls to a copy of the Lua core statically linked into the EXE. This allows your EXE to statically link Lua but still be able to use Lua extension modules that dynamically link to lua5.1.dll. See comments in luaproxydll.h for more details.

-- expand.lua - Generates Lua proxy DLL source code.

local function map(f, t)
  local t = {unpack(t)}
  for k,v in ipairs(t) do t[k] = f(v) end
  return t
end
local function foreach(fmt, t)
  return table.concat(map(function(s) return string.format(fmt, s) end, t))
end

-- API functions defined in LuaBinaries - http://luabinaries.luaforge.net/
local apifuncs = {
  "lua_tolstring","lua_typename","lua_pushfstring","lua_pushvfstring",
  "lua_getlocal","lua_getupvalue","lua_setlocal","lua_setupvalue",
  "lua_topointer","lua_iscfunction","lua_isnumber","lua_isstring",
  "lua_isuserdata","lua_toboolean","lua_type","lua_equal",
  "lua_lessthan","lua_rawequal","lua_checkstack","lua_cpcall",
  "lua_error","lua_getmetatable","lua_gettop","lua_load",
  "lua_next","lua_pcall","lua_pushthread","lua_setfenv",
  "lua_setmetatable","lua_resume","lua_status","lua_yield",
  "lua_dump","lua_gc","lua_gethook","lua_gethookcount",
  "lua_gethookmask","lua_getinfo","lua_getstack","lua_sethook",
  "lua_getallocf","lua_tocfunction","lua_atpanic","lua_tointeger",
  "lua_tonumber","lua_tothread","lua_newstate","lua_newthread",
  "lua_objlen","lua_touserdata","lua_close","lua_call",
  "lua_concat","lua_createtable","lua_getfenv","lua_getfield",
  "lua_gettable","lua_insert","lua_pushboolean","lua_pushcclosure",
  "lua_pushinteger","lua_pushlightuserdata","lua_pushlstring",
  "lua_pushnil","lua_pushnumber","lua_pushstring","lua_pushvalue",
  "lua_rawget","lua_rawgeti","lua_rawset","lua_rawseti",
  "lua_remove","lua_replace","lua_setfield","lua_settable",
  "lua_settop","lua_xmove","lua_newuserdata","lua_setallocf",
  "luaL_prepbuffer","luaL_checklstring","luaL_findtable","luaL_gsub",
  "luaL_optlstring","luaL_newmetatable","luaL_argerror","luaL_callmeta",
  "luaL_checkoption","luaL_error","luaL_getmetafield","luaL_loadbuffer",
  "luaL_loadfile","luaL_loadstring","luaL_ref","luaL_typerror",
  "luaL_checkinteger","luaL_optinteger","luaL_checknumber",
  "luaL_optnumber","luaL_newstate","luaL_openlib","luaL_addlstring",
  "luaL_addstring","luaL_addvalue","luaL_buffinit","luaL_checkany",
  "luaL_checkstack","luaL_checktype","luaL_pushresult","luaL_register",
  "luaL_unref","luaL_where","luaL_checkudata","luaopen_base",
  "luaopen_debug","luaopen_io","luaopen_math","luaopen_os",
  "luaopen_package","luaopen_string","luaopen_table","luaL_openlibs",
  "luaU_dump","luaM_toobig","luaM_realloc_","luaS_newlstr",
  "luaD_growstack","luaF_newproto"
}


local luaproxydll_h = [[
/**
  luaproxydll.h - C header file for Lua proxy DLL (lua5.1.dll).
  (autogenerated from expand.lua)
 
  == Discussion ==

  Lua extension modules in Windows typically dynamically link
  against the lua5.1.dll defined in Lua Binaries
  (http://luabinaries.luaforge.net/).  This assumes
  your EXE also dynamically links against lua5.1.dll.
  If your EXE instead statically links against lua5.1.dll,
  then you have two copies of the Lua core, which is
  redundant and also can cause subtle problems.

  The proxy DLL defined here is a drop-in-replacement for
  the standard lua5.1.dll.  It acts as a thin proxy that
  exports the same interface as the standard lua5.1.dll and
  transparently forwards calls to the Lua core functions
  defined in the EXE.

  Before extension modules attempt to load and call the
  proxy DLL, the EXE must load the proxy DLL and call the
  additional proxy_init() function in the proxy DLL, passing
  it the addresses of the Lua core functions in the EXE.

  The resultant DLL should be small (less than 10K).
  The compilation can omit the C run-time.

  To use this in your EXE, include luaproxydll.h and then run


  LUAPROXYDLL_INIT().  You may need to remove the "LUAI_FUNC" before
  these functions in the Lua source:
  
    luaU_dump
    luaM_toobig
    luaM_realloc_
    luaS_newlstr
    luaD_growstack
    luaF_newproto

  Note: LUAPROXYDLL_IMPLEMENTATION is undefined in the EXE.
  
  Author: (c) David Manura, 2007-01-30
  Licensed under the same terms as Lua 5.1 itself.
*/

#ifndef LUAPROXYDLL_H
#define LUAPROXYDLL_H

#ifdef __cplusplus
extern "C" {
#endif

typedef const void * const luaproxydll_t;

/** Pointers to Lua core functions in the EXE.
    The EXE should prepare this and pass it to
    the luaproxydll_init function.
  */
struct luaproxydll_Funcs {
]] .. foreach([[
    luaproxydll_t %s;
]], apifuncs) .. [[
};
/* not passed: luaP_opmodes, luaP_opnames */


/* These are only used by the EXE */
#ifndef LUAPROXYDLL_IMPLEMENTATION

/* reference Lua core functions */
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
/* void luaU_dump();
   void luaM_toobig();
   void luaM_realloc_();
   void luaS_newlstr();
   void luaD_growstack();
   void luaF_newproto(); */
#include <lfunc.h>
#include <lundump.h>
#include <lstring.h>
#include <ldo.h>

/** Table of Lua core function pointers. */
const struct luaproxydll_Funcs luaproxydll_funcs = {
]] .. foreach([[
    %s,
]], apifuncs) .. [[
};
/* not passed: luaP_opmodes, luaP_opnames */


/** load proxy DLL. Call this in your EXE to load and initialize the proxy DLL. */
#define LUAPROXYDLL_INIT() \
{ \
    HMODULE hmodule = LoadLibrary("lua5.1.dll"); \
    if (hmodule) { \
        typedef void (*func_t)(const luaproxydll_Funcs *); \
        func_t initfunc = (func_t)GetProcAddress(hmodule, "luaproxydll_init"); \
        if (initfunc) { \
            initfunc(&luaproxydll_funcs); \
        } \
    } \
}

/* Register Lua core functions from the EXE into the proxy DLL.
   funcs must remain valid until the proxy DLL is
   unloaded or no longer used.
  */
void luaproxydll_init(struct luaproxydll_Funcs * funcs);

#endif /* not LUAPROXYDLL_IMPLEMENTATION */

#ifdef __cplusplus
}
#endif

#endif /* first include */
]]

local luaproxydll_c = [[
/***
  luaproxydll.c - C implementation file for the Lua proxy dll.
  (autogenerated from expand.lua)
  
  Author: (c) David Manura, 2007-01-30
  Licensed under the same terms as Lua 5.1 itself.
 ***/

#define LUAPROXYDLL_IMPLEMENTATION /* used by luaproxydll.h */

#include <windows.h>
#include "luaproxydll.h"

static struct luaproxydll_Funcs s_funcs;

/* Macro for defining a proxy function.
   This is a direct jump (single "jmp" assembly instruction"),
   preserving stack and return address.
   The following uses MSVC inline assembly which may not be
   portable with other compilers.
 */

#define PROXY(name) \
  void __declspec(naked) name() { __asm { jmp s_funcs.name } }

/* Define all the proxy functions specified in lua5.1.def. */
]] .. foreach([[
PROXY(%s)
]], apifuncs) .. [[
/* note: luaP_opmodes and luaP_opnames are defined in lopcodes.c */


    BOOL APIENTRY
DllMain(HANDLE module, DWORD reason, LPVOID reserved)
{
    /* DEBUG: MessageBox(NULL, "proxyload", "", MB_OK); */
    return TRUE;
}

    void
luaproxydll_init(struct luaproxydll_Funcs * funcs)
{
    int n;
    for (n=0; n < sizeof(struct luaproxydll_Funcs); n++) {
      ((unsigned char*)(void*)&s_funcs)[n] = 
        ((unsigned char*)(void*)funcs)[n];
    }
    /* nope: s_funcs = funcs; */
}
]]

local GNUmakefile = [[
# GNUmakefile - Makefile for luaproxydll.dll for MSVC compiler.
# (autogenerated from expand.lua)

all:
	cl /O2 /LD /GS- luaproxydll.c ../lua/src/lopcodes.c \
	    /link /def:lua5.1.def /out:lua5.1.dll /nodefaultlib /entry:DllMain

clean:
	rm -f *~ *.obj *.exp *.lib
purge: clean
	rm -f luaproxydll.[hc] lua5.1.def GNUmakefile lua5.1.dll
]]

local lua5_1_def = [[
; lua5.1.def
; (autogenerated from expand.lua)
VERSION 5.1
EXPORTS
]] .. foreach([[
  %s
]], apifuncs) .. [[
  luaP_opmodes
  luaP_opnames

  ; special
  luaproxydll_init
]]

for k,v in pairs{
  [luaproxydll_h] = "luaproxydll.h",
  [luaproxydll_c] = "luaproxydll.c",
  [lua5_1_def] = "lua5.1.def",
  [GNUmakefile] = "GNUmakefile"
} do
  io.output(v); io.write(k)
end

--DavidManura

David, what are the benefits of the above configuration over the common one (lua.exe + lua5.1.dll)? --ShmuelZeigerman

It's partly a matter of taste in terms of packaging. In the standard configuration you have (app.exe + lua5.1.dll + extension.dll), where app.exe always depends on lua5.1.dll. In the proxy configuration you also have (app.exe + lua5.1.dll + extension.dll), but here lua5.1.dll is under 10K and app.exe does not depend on lua5.1.dll except if extension.dll needs to be used. If you don't use the extension, then app.exe will run fine by itself. Also, any patches or variant builds of Lua are generally applied to app.exe, and lua5.1.dll is left untouched. In fact, you could have app1.exe with one variant build of Lua (e.g. linked against the VC6 CRT) and app2.exe with another variant build (e.g. linked against the VC8 CRT), both of which would proxy through the same lua5.1.dll file (which is possible since lua5.1.dll does not link to the CRT, and each process loads a separate in-memory instance of the DLL). Maybe others have ideas for improvements--there's some hackery in the above. --DavidManura

You can also use this trick (I used version 3 below) to make a normal looking lua5.1.dll that proxies to a tweaked distribution like LuaPlus_1100.dll. Once again for the purpose of letting LuaBinaries dll's work. --ferrix

See Also

Any ideas for making these page names more descriptive?


RecentChanges · preferences
edit · history
Last edited September 6, 2008 3:56 pm GMT (diff)