В системах, которые поддерживают binutils, вы также можете «скомпилировать» файл Lua в .o с помощью «ld -r», связать .o с общим объектом, а затем связать ваше приложение с общей библиотекой. Во время выполнения вы dlsym (RTLD_DEFAULT, ...) в тексте lua и можете затем оценить его, как вам нравится.
Чтобы создать some_stuff.o из some_stuff.lua:
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
Это даст вам объектный файл с символами, которые разделяют начало, конец и размер ваших данных lua. Эти символы, насколько я знаю, определяются ld из имени файла. У вас нет контроля над именами, но они последовательно получены. Вы получите что-то вроде:
$ 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
Теперь свяжите some_stuff.o в общий объект, как и любой другой объектный файл. Затем в своем приложении напишите функцию, которая получит имя «some_stuff_lua», и выполните соответствующую магию dlsym. Что-то вроде следующего C ++, который предполагает, что у вас есть оболочка вокруг lua_State с именем SomeLuaStateWrapper:
void SomeLuaStateWrapper::loadEmbedded(const std::string& 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<const char*>(
dlsym(RTLD_DEFAULT, data_start.c_str()));
const char* const data_end_addr = reinterpret_cast<const char*>(
dlsym(RTLD_DEFAULT, data_end.c_str()));
THROW_ASSERT(
data_start_addr && data_end_addr,
"Couldn't obtain addresses for start/end symbols " <<
data_start << " and " << data_end << " for embedding " << embeddingName);
const ptrdiff_t delta = data_end_addr - data_start_addr;
THROW_ASSERT(
delta > 0,
"Non-positive offset between lua start/end symbols " <<
data_start << " and " << data_end << " for embedding " << embeddingName);
// NOTE: You should also load the size and verify it matches.
static const ssize_t kMaxLuaEmbeddingSize = 16 * 1024 * 1024;
THROW_ASSERT(
delta <= kMaxLuaEmbeddingSize,
"Embedded lua chunk exceeds upper bound of " << kMaxLuaEmbeddingSize << " bytes");
namespace io = boost::iostreams;
io::stream_buffer<io::array_source> buf(data_start_addr, data_end_addr);
std::istream stream(&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());
}
Итак, теперь внутри вашего приложения, если вы связали или удалили библиотеку, содержащую some_stuff.o, вы можете просто сказать:
SomeLuaStateWrapper wrapper;
wrapper.loadEmbedded("some_stuff_lua");
и исходное содержимое файла some_stuff.lua будет lua_load'ed в контексте 'wrapper'.
Если, кроме того, вы хотите, чтобы разделяемая библиотека, содержащая some_stuff.lua, могла загружаться из Lua с помощью 'require', просто предоставьте ту же библиотеку, которая содержит точку входа luaopen some_stuff.oa в каком-то другом файле C / C ++ :
extern "C" {
int luaopen_some_stuff(lua_State* L)
{
SomeLuaStateWrapper wrapper(L);
wrapper.loadEmbedded("some_stuff_lua");
return 1;
}
} // extern "C"
Ваш встроенный Lua теперь также доступен через require. Это особенно хорошо работает с luabind.
С помощью SCons довольно легко объяснить системе сборки, что, когда она видит файл .lua в разделе источников SharedLibrary, она должна «скомпилировать» файл с помощью шагов ld / objcopy, указанных выше:
# 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) && 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))
Я уверен, что можно сделать что-то подобное с другими современными системами сборки, такими как CMake.
Эта техника, конечно, не ограничивается Lua, но может использоваться для встраивания практически любого ресурса в двоичный файл.