Lua Proxy Dll Two

lua-users home
wiki

The utility mkproxy.lua presented below is intended for building a proxy DLL which redirects its function calls to a "host" library file containing functions with the same names. The host library file can be either a DLL or an EXE, provided it has all the required exported functions.

The utility can build proxy DLL's for hosts of any kind, it is not restricted by Lua-related hosts.

The utility assumes (and requires) MinGW development system.

The utility contains one "public" function: CreateProxyDll. The function accepts one argument, a table, that must have the following fields specified:

PROXY   : (path)name of the proxy DLL to build
HOST    : (path)name of the host library file (file must exist)
DEFFILE : (path)name of the definition file (file must exist)

Example 1: allow combined use of applications or libraries linked with either of lua5.1.dll and lua51.dll:

CreateProxyDll {
  PROXY   = "lua51.dll",
  HOST    = "c:\\exe\\lua5.1.dll",
  DEFFILE = "liblua5.1.def",
}

Example 2: make possible for a statically built Lua interpreter to safely require() external libraries:

CreateProxyDll {
  PROXY   = "lua5.1.dll",
  HOST    = "c:\\exe\\lua51x.exe",
  DEFFILE = "liblua5.1.def",
}

mkproxy.lua:

-- Name:       mkproxy.lua
-- Goal:       Create a proxy DLL that redirects all calls to the "host" library
-- Written by: Shmuel Zeigerman
-- License:    public domain

local CFile = [[
#include <windows.h>
BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD Reason, LPVOID Reserved) {
  return TRUE;
}
]]

local function execute(s) assert(0 == os.execute(s)) end

local function CreateObjectFile (objname)
  assert(objname:match("%.o$"))
  local cname = objname:gsub("%.o$", ".c")
  local fp = assert(io.open(cname, "w"))
  fp:write (CFile)
  fp:close()
  execute("gcc -c " .. cname .. " -o " .. objname)
  os.remove(cname)
end

function CreateProxyDll (config)
  assert(type(config) == "table", "argument #1: a table expected")
  assert(config.PROXY,   "config.PROXY not specified")
  assert(config.HOST,    "config.HOST not specified")
  assert(config.DEFFILE, "config.DEFFILE not specified")
  assert(io.open(config.HOST)):close()
  assert(io.open(config.DEFFILE)):close()
  -----------------------------------------------------------------------------
  local hostpath, hostname = config.HOST:match ("^(.+)[\\/](.+)$")
  if not hostpath then hostpath, hostname = ".", config.HOST end
  local temp_dll = (hostname:sub(-4):lower() ~= ".dll") and "temp_dll.dll"
  local temp_obj = "temp_obj.o"
  CreateObjectFile(temp_obj)
  local cmdline = table.concat ({
    "gcc -shared -s -nostdlib",
    temp_obj,
    config.DEFFILE,
    "-L" .. (temp_dll and "." or hostpath),
    "-l" .. (temp_dll or hostname):sub(1,-5),
    "-o" .. config.PROXY,
  }, " ")
  if temp_dll then execute("copy " .. config.HOST .. " " .. temp_dll) end
  execute(cmdline)
  os.remove(temp_obj)
  if temp_dll then os.remove(temp_dll) end
end

-- ShmuelZeigerman

Comments

--I've wondered why LuaBinaries renames lua51.dll as lua5.1.dll, or, if this is a better approach, why Lua itself doesn't follow. This all just makes things needlessly more complicated for new users and for module authors not familiar with the rules. It's not a Good Thing(TM), for we come to the above. I see LuaBinaries is now distributed with such a wrapper too. --DavidManura

This was already discussed on the mailing list: [1] --ShmuelZeigerman

The following build is for MSVC2005. It uses the "nodefaultlib" linker option. The final proxy DLL was about 9 KB.

local DEFFILE  = "lua5.1.def" -- .def file for lua5.1.dll
local LIBFILE  = "lua5.1.lib" -- .lib file for lua5.1.dll
local CFILE   = "luaproxy.c"-- output file
local MAKFILE = "luaproxy.mak"  -- output file
----------------------------------------------------------------------
local cfile = assert(io.open(CFILE, "w"))
cfile:write [=[
#include <windows.h>
    BOOL APIENTRY
DllMain(HANDLE module, DWORD reason, LPVOID reserved)
    { return TRUE; }
]=]
cfile:close()
----------------------------------------------------------------------
local makfile = assert(io.open(MAKFILE, "w"))
makfile:write(string.format([=[
lua51.dll : luaproxy.c
	cl -I../lua/src /O2 /LD /GS- luaproxy.c /link /def:%s %s \
	   /out:lua51.dll /nodefaultlib /entry:DllMain
]=], DEFFILE, LIBFILE))
makfile:close()
----------------------------------------------------------------------

--DavidManura

It seems that what you are doing is building an empty C file which links to the stubs in the import library, and reexposing those stubs as a dll interface. This would mean that the DLL you generate:

1. does contain code (DllMain?, and the import library)
2. add a function call overhead to each API call
This is not necessary. There is a mechanism in Windows DLL format that allow to make the DLL loader create aliases on the fly when the DLL is loaded. When you open the DLL in depends.exe you can clearly see these entry points marked as forwards/aliases.

Tell me if I misunderstood your code.

-- JeromeVuarand

See Also


RecentChanges · preferences
edit · history
Last edited September 5, 2008 12:26 pm GMT (diff)