[英]Lua script: embed C code to run automatically at the start of a while loop
[英]How to embed a Lua script within a C binary?
我在 shell 世界中被寵壞了,我可以做:
./lua <<EOF
> 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
警告,我想讓這個程序不僅是獨立的,而且可以移植到其他程序編譯器。
在 C 程序中包含大型 Lua 腳本並且不作為單獨文件提供給最終用戶的最佳方法是什么? 理想情況下,我想將腳本移動到單獨的 *.lua 文件中以簡化測試和更改控制,並以某種方式將該文件編譯到可執行文件中。
在支持 binutils 的系統上,您還可以使用“ld -r”將 Lua 文件“編譯”為 ao,將.o 鏈接到共享的 object,然后將您的應用程序鏈接到共享庫。 在運行時,您 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
這將為您提供一個 object 文件,其中包含分隔 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 鏈接到共享的 object 中,就像任何其他 object 文件一樣。 然后,在您的應用程序中,編寫一個名為“some_stuff_lua”的 function,並執行適當的 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);
// 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());
}
因此,現在在您的應用程序中,假設您已鏈接或 dlopen'ed 包含 some_stuff.o 的庫,您可以說:
SomeLuaStateWrapper wrapper;
wrapper.loadEmbedded("some_stuff_lua");
並且 some_stuff.lua 的原始內容將在 'wrapper' 的上下文中被 lua_load'ed。
此外,如果您希望包含 some_stuff.lua 的共享庫能夠使用“require”從 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 的源部分中看到一個 .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,而是可以用來在二進制文件中嵌入幾乎任何資源。
一種非常便宜但不容易改變的方法是使用 bin2c 之類的東西從選定的 lua 文件(或其編譯的字節碼,更快更小)中生成 header,然后您可以將其傳遞給 ZF8DBBBDB23B80B4F19708 執行。
您也可以嘗試將其作為資源嵌入,但我不知道它在 Visual Studio/Windows 之外是如何工作的。
取決於你想做什么,你甚至可能會發現exeLua有用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.