如何在C二进制文件中嵌入Lua脚本?

我已经在shell世界中被宠坏了,我可以做:

./lua < x="hello world" > print (x) > EOF hello world 

现在我试图在C应用程序中包含一个Lua脚本,我期望它会随着时间的推移而增长。 我从一个简单的开始:

 const char *lua_script="x=\"hello world\"\n" "print(x)\n"; luaL_loadstring(L, lua_script); lua_pcall(L, 0, 0, 0); 

但这有几个缺点。 首先,我必须逃避换行和引用。 但是现在我的string length '1234' is greater than the length '509' ISO C90 compilers are required to support在使用gcc进行string length '1234' is greater than the length '509' ISO C90 compilers are required to support警告,我想保持这个程序不仅是自包含的,而且可以移植到其他编译器。

在C程序中包含大型Lua脚本的最佳方法是什么,而不是作为单独的文件提供给最终用户? 理想情况下,我想将脚本移动到单独的* .lua文件中以简化测试和更改控制,并将该文件以某种方式编译到可执行文件中。

一个非常便宜,但不那么容易改变的方法是使用像bin2c这样的东西来生成一个选定的lua文件(或者它编译的字节码,它更快更小)的标题,然后你可以将它传递给lua来执行。

您也可以尝试将其作为资源嵌入,但我不知道它是如何在visual studio / windows之外工作的。

根据你想做什么,你甚至可以找到exeLua使用。

在支持binutils的系统上,您还可以使用“ld -r”将Lua文件“编译”为.o,将.o链接到共享对象,然后将应用程序链接到共享库。 在运行时,您可以在lua文本中使用dlsym(RTLD_DEFAULT,…),然后可以根据需要对其进行评估。

要从some_stuff.lua创建some_stuff.o:

 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 ++,它假设你有一个名为SomeLuaStateWrapper的lua_State包装器:

 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( dlsym(RTLD_DEFAULT, data_start.c_str())); const char* const data_end_addr = reinterpret_cast( 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 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()); } 

所以,现在在你的应用程序中,假设你已经链接或dlopen’ed包含some_stuff.o的库,你可以说:

 SomeLuaStateWrapper wrapper; wrapper.loadEmbedded("some_stuff_lua"); 

some_stuff.lua的原始内容将在’wrapper’的上下文中被lua_load’。

此外,如果您希望能够使用’require’从Lua加载包含some_stuff.lua的共享库,只需在其他一些C / C ++文件中提供包含some_stuff.oa luaopen入口点的相同库:

 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,教会构建系统相当容易,当它在SharedLibrary的sources部分中看到一个.lua文件时,应该使用上面的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,而是可以用于嵌入二进制中的任何资源。