在支持binutils的系统上,你也可以使用'ld -r'将Lua文件“编译”成.o文件,然后将.o链接到共享对象中,最后将应用程序链接到共享库。运行时,您可以使用dlsym(RTLD_DEFAULT,...)在Lua文本中进行符号查找,并随意评估它。
要从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<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);
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);
load(stream, embeddingName.c_str());
}
因此,在您的应用程序中,假设您已经链接或dlopen了包含some_stuff.o的库,您只需说:
SomeLuaStateWrapper wrapper
wrapper.loadEmbedded("some_stuff_lua")
如果你想让包含some_stuff.lua的共享库能够通过Lua的“require”加载,那么只需在另一个C/C++文件中给包含some_stuff.o的同一库添加一个luaopen入口点:
这样,在'wrapper'上下文中,一些stuff.lua的原始内容将被lua_load加载。
extern "C" {
int luaopen_some_stuff(lua_State* L)
{
SomeLuaStateWrapper wrapper(L);
wrapper.loadEmbedded("some_stuff_lua");
return 1;
}
}
你的嵌入式Lua现在也可以通过require来使用了。这在使用luabind时特别方便。
使用SCons,很容易让构建系统知道当它看到共享库源代码部分中的.lua文件时,应该使用上述ld/objcopy步骤“编译”该文件。
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,而且可以用于在二进制文件中嵌入几乎任何资源。
-std=c99
编写现代(C99)的C代码。C99要求编译器支持长度达到4095个字符的字符串字面量。然而,在实践中,任何值得信赖的非嵌入式环境编译器都不会对字符串长度有任何限制,因此这个警告应该被禁用或忽略。 - R.. GitHub STOP HELPING ICEconst char lua_script[] = "...";
以避免不必要的指针。 - Jonathan Lefflerchar *
为指针和字符串初始化程序分配空间(该指针可以更改);char []
为字符串初始化程序分配空间,并且有一个地址可以传递给函数,但无法更改并且不占用任何空间。 - Jonathan Lefflerbin2c
时添加-std=c99
,因为编译器已经开始提示sqlite源代码中的一个long long
问题。 - BMitch