Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>On systems which support binutils, you can also 'compile' a Lua file into a .o with 'ld -r', link the .o into a shared object, and then link your application to the shared library. At runtime, you dlsym(RTLD_DEFAULT,...) in the lua text and can then evaluate it as you like.</p> <p>To create some_stuff.o from some_stuff.lua:</p> <pre><code>ld -s -r -o some_stuff.o -b binary some_stuff.lua objcopy --rename-section .data=.rodata,alloc,load,readonly,data,contents some_stuff.o some_stuff.o </code></pre> <p>This will get you an object file with symbols that delimit the start, end, and size of your lua data. These symbols are, as far as I know, determined by ld from the filename. You don't have control over the names, but they are consistently derived. You will get something like:</p> <pre><code>$ nm some_stuff.o 000000000000891d R _binary_some_stuff_lua_end 000000000000891d A _binary_some_stuff_lua_size 0000000000000000 R _binary_some_stuff_lua_start </code></pre> <p>Now link some_stuff.o into a shared object like any other object file. Then, within your app, write a function that will take the name "some_stuff_lua", and do the appropriate dlsym magic. Something like the following C++, which assumes you have a wrapper around lua_State called SomeLuaStateWrapper:</p> <pre><code>void SomeLuaStateWrapper::loadEmbedded(const std::string&amp; embeddingName) { const std::string prefix = "_binary_"; const std::string data_start = prefix + embeddingName + "_start"; const std::string data_end = prefix + embeddingName + "_end"; const char* const data_start_addr = reinterpret_cast&lt;const char*&gt;( dlsym(RTLD_DEFAULT, data_start.c_str())); const char* const data_end_addr = reinterpret_cast&lt;const char*&gt;( dlsym(RTLD_DEFAULT, data_end.c_str())); THROW_ASSERT( data_start_addr &amp;&amp; data_end_addr, "Couldn't obtain addresses for start/end symbols " &lt;&lt; data_start &lt;&lt; " and " &lt;&lt; data_end &lt;&lt; " for embedding " &lt;&lt; embeddingName); const ptrdiff_t delta = data_end_addr - data_start_addr; THROW_ASSERT( delta &gt; 0, "Non-positive offset between lua start/end symbols " &lt;&lt; data_start &lt;&lt; " and " &lt;&lt; data_end &lt;&lt; " for embedding " &lt;&lt; embeddingName); // NOTE: You should also load the size and verify it matches. static const ssize_t kMaxLuaEmbeddingSize = 16 * 1024 * 1024; THROW_ASSERT( delta &lt;= kMaxLuaEmbeddingSize, "Embedded lua chunk exceeds upper bound of " &lt;&lt; kMaxLuaEmbeddingSize &lt;&lt; " bytes"); namespace io = boost::iostreams; io::stream_buffer&lt;io::array_source&gt; buf(data_start_addr, data_end_addr); std::istream stream(&amp;buf); // Call the code that knows how to feed a // std::istream to lua_load with the current lua_State. // If you need details on how to do that, leave a comment // and I'll post additional details. load(stream, embeddingName.c_str()); } </code></pre> <p>So, now within your application, assuming you have linked or dlopen'ed the library containing some_stuff.o, you can just say:</p> <pre><code>SomeLuaStateWrapper wrapper; wrapper.loadEmbedded("some_stuff_lua"); </code></pre> <p>and the original contents of some_stuff.lua will have been lua_load'ed in the context of 'wrapper'.</p> <p>If, in addition, you want the shared library containing some_stuff.lua to be able to be loaded from Lua with 'require', simply give the same library that contains some_stuff.o a luaopen entry point in some other C/C++ file:</p> <pre><code>extern "C" { int luaopen_some_stuff(lua_State* L) { SomeLuaStateWrapper wrapper(L); wrapper.loadEmbedded("some_stuff_lua"); return 1; } } // extern "C" </code></pre> <p>Your embedded Lua is now available via require as well. This works particularly well with luabind.</p> <p>With SCons, it is fairly easy to educate the build system that when it sees a .lua file in the sources section of a SharedLibrary that it should 'compile' the file with the ld/objcopy steps above:</p> <pre><code># NOTE: The 'cd'ing is annoying, but unavoidable, since # ld in '-b binary' mode uses the name of the input file to # set the symbol names, and if there is path info on the # filename that ends up as part of the symbol name, which is # no good. So we have to cd into the source directory so we # can use the unqualified name of the source file. We need to # abspath $TARGET since it might be a relative path, which # would be invalid after the cd. env['SHDATAOBJCOM'] = 'cd $$(dirname $SOURCE) &amp;&amp; ld -s -r -o $TARGET.abspath -b binary $$(basename $SOURCE)' env['SHDATAOBJROCOM'] = 'objcopy --rename-section .data=.rodata,alloc,load,readonly,data,contents $ TARGET $TARGET' env['BUILDERS']['SharedLibrary'].add_src_builder( SCons.Script.Builder( action = [ SCons.Action.Action( "$SHDATAOBJCOM", "$SHDATAOBJCOMSTR" ), SCons.Action.Action( "$SHDATAOBJROCOM", "$SHDATAOBJROCOMSTR" ), ], suffix = '$SHOBJSUFFIX', src_suffix='.lua', emitter = SCons.Defaults.SharedObjectEmitter)) </code></pre> <p>I'm sure it is possible to do something like this with other modern build systems like CMake as well.</p> <p>This technique is of course not limited to Lua, but can be used to embed just about any resource in a binary.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload