hammackj

C++ 11 and LuaJIT Integration

Last weekend I was trying to integrate LuaJIT into my custom game engine but I was getting this strange lua runtime error:

[hammackj@taco:~/luajit_playground/luajit_static]$ ./static
Hello from Lua
Couldn't load file luatest.lua : luatest.lua:10: dlsym(RTLD_DEFAULT, test_ffi): symbol not found

After a bit of googling I couldn't really find an answer, all the examples I found just worked. So I started to look a little deeper.

[hammackj@taco:~/luajit_playground/luajit_static]$ nm -gU static
0000000100000000 T ____mh_execute_header
0000000100001110 T ____main

By looking at all of the final symbols from the binary I could see my test_ffi function was missing. From here I was fairly certain clang was stripping all of the dead code that wasn't being directly called. Which was causing my problem as I was using the FFI interface to export my C++ wrappers to lua. After a few more hours of googling I found a stackoverflow question about dead code striping that led me to adding this -Wl,-force_load,libstatic.a, to my linker command.

[hammackj@taco:~/luajit_playground/luajit_static]$ nm -gU static
0000000100000000 T ____mh_execute_header
0000000100001110 T ____main
0000000100001c40 T ____test_ffi

The symbol was now being correct "exported" from the static library to the host binary and not bring stripped. The output now worked correctly.

[hammackj@taco:~/luajit_playground/luajit_static]$ ./static
Hello from Lua
Hello from C++

Below is all the source code used. You can also use -Wl,-all_load, which forces all symbols / functions to be imported even if unused. This will dramatically increase the size of the binary.

//Main.cpp
#include <iostream>
#include <luajit-2.0/lua.hpp>

int main(int argc, char const *argv[])
{
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    const char *path = "luatest.lua";

    if (luaL_loadfile(L, path) || lua_pcall(L, 0, LUA_MULTRET, 0))
    {
        std::cerr << "Couldn't load file " << path << " : " << lua_tostring(L, -1) << std::endl;
    }

    lua_close(L);

    return 0;
}
//static.hpp
#include <iostream>

extern "C"
{
    void test_ffi();
}
//static.cpp
#include "static.hpp"

void test_ffi()
{
    std::cout << "Hello from C++" << std::endl;
}
-- luatest.lua
print "Hello from Lua"

local ffi = require('ffi')

ffi.cdef([[
void test_ffi();
]])
local C = ffi.C

C.test_ffi()
CC=c++
CFLAGS=-g -Wall -I/usr/local/include -L/usr/local/lib -L. -std=c++11 -pagezero_size 10000 -image_base 100000000
LDFLAGS=-lluajit-5.1 -lstatic -Wl,-force_load,libstatic.a

#-Wl,-all_load

FILES=main.cpp
STATIC_FILES=static.cpp

OUTPUT=static

all: static standalone

static: $(STATIC_FILES)
    $(CC) -MMD -MP -g -std=c++11 -c $(STATIC_FILES) -o static.o
    ar -rcs libstatic.a static.o

standalone: $(static) $(FILES)
    $(CC) $(CFLAGS) -o $(OUTPUT) $(FILES) $(LDFLAGS)

clean:
    rm -rf *.dSYM static *.d *.a *.o

You can find all the source code on my github. Which includes a simple standalone single binary version, the above static library version and a dynamic library version. The code is written for OSX and a brew based install of LuaJIT. I didn't test if this was a issue on gcc or msvc++.