2020-03-08

Lua源文件编译为Lua...

早就听闻LuaJIT字节码有很好的代码保护措施,而且还可以加快代码的加载速度,趁着项目中期不太忙的时候,抽空做看了一下LuaJIT如何使用字节码。

顺便吐槽一下,公司之前那么多cocos项目,居然一个使用字节码的都没有,很好奇他们的项目是怎么做的客户端加密,哈哈。

我们项目是Unity3d项目,使用的xLua做热更方案。不过字节码这一块跟使用什么框架没有关系,官方文档说,字节码(bytecode)和源码(sourcecode)在使用上是没有区别的。所以不考虑32位移动平台的话,在框架和业务层面无需改动任何东西。

HOW

LuaJIT支持交叉编译,即在mac osx上编译出ios或android上系统可执行的字节码,如此,我们只需要针对32和64位的LuaJIT解释器分别编译一次,然后在打包过程中使用LuaJIT解释器将项目中的Lua源码编译成字节码即可。

LuaJIT字节码有平台兼容性的问题,如果需要兼容iphone 5s及以下32位平台的用户,则需要针对32位/64位各生成一套字节码。

如下给出一套在桌面平台生成移动平台上可用的字节码的流程。

1. 重新编译LuaJIT解释器

LuaJIT默认生成的字节码是32位的,如果在64位的ios机器上直接读取的话,会一个报错。针对这个种情况,而作者在2015年8月对LuaJIT做了一个更新,增加了LJ_GC64模式,这样我们可以重新编译LuaJIT解释器来生成64位的字节码。

过程很简单,只需要在 make 命令后加上一个参数即可。如下是mac os上编译LuaJIT解释器的命令。

1
2
3
4
5
6
7
8
// 控制台定位到LuaJIT解释器所在目录
cd LuaJIT-2.1.0-beta2

// 编译 32 位 LuaJIT 解释器
make

// 编译 64 位 LuaJIT 解释器
make CFLAGS=-DLUAJIT_ENABLE_GC6
2. 生成32/64位字节码

有了32/64位的LuaJIT解释器之后,我们就可以按照官方流程来生成LuaJIT来生成平台兼容的字节码了。

1
2
// 控制台定位到LuaJIT解释器所在目录
cd LuaJIT-2.1.0-beta2
mac osx平台
1
2
3
4
5
// 生成字节码
./luajit32 -b sourcecode.lua btyecode.out

// 测试
./luajit32 bytecode.out
windows平台
1
2
3
4
5
6
// 生成字节码
luajit32 -b sourcecode.lua btyecode.out


// 测试
luajit32 bytecode.out
3. 读取字节码

移动平台在加载Lua代码时,在Lua的加载器中根据当前手机的CPU架构来加载对应位数的字节码。

Android读取32位字节码,ios根据cpu来读取32/64位字节码即可。

##

官方文档搬运:LuaJIT编译生成字节码的方式

LuaJIT只有一个单独的可执行文件,POSIX系统上是luajit,Window平台上是luajit.exe,它可以使用命令行来执行Lua Statements 或者整个lua应用程序,也有交互模式(?)

  1. LuaJIT命令行选项 -b[options] input output

    -b 这个选项用于保存和列出字节码, 并且可以接受以下一些叠加选项

    -1 — Only list bytecode.
    -s — Strip debug info (this is the default).
    -g — Keep debug info.
    -n name — Set module name (default: auto-detect from input name)
    -t type — Set output file type (default: auto-detect from output name).
    -a arch — Override architecture for object files (default: native).
    -o os — Override OS for object files (default: native).
    -e chunk — Use chunk string as input.
    - (a single minus sign) — Use stdin as input and/or stdout as output.
输出文件类型是从输出文件名的拓展类型中自动得到的

   c — C source file, exported bytecode data.
   h — C header file, static bytecode data.
   obj or o — Object file, exported bytecode data (OS- and architecture-specific).
   raw or any other extension — Raw bytecode file (portable). 
  1. Notes:

    1. 请参阅string.dump()来获取字节码的可移植性和兼容性的信息。
    2. 一个raw bytecode格式的文件是自动获取的,然后可以被像Lua源代码一样被加载。举例来说类似于直接使用命令行或者使用loadfile(),dofile。
    3. 为了在应用程序中静态地嵌入模块的字节码,生成一个object文件并将其与您的应用程序连接起来。
    4. 在大多数基于elf的系统(如Linux)上,您需要在连接应用程序时显式地导出全局符号,例如:-wl-e
    5. require()尝试从导出的符号中加载嵌入的字节码数据(从 *.exe或Windows平台上上的lua5.1.dll)和来自package.cpath的共享库。
  1. string.dump(f [,strip]) 生成轻便的字节码

    string.dump()中加入了一个额外的参数,如果被设置为true, 将会生成没有调试信息的‘剥离式’字节码,这将会加速字节码的加载和减少内存用量。请参阅 -b 命令行选项。

    产生出来的字节码是轻便的而且可以被任意LuaJIT支持的架构加载,独立的字长和字节序,但是字节码的兼容版本号必须匹配。字节码版本在小版本之间保持兼容(x.y.0 -> x.y.1), 但是可能会在major或者minor版本或任意beta版本之间改变。外部字节码(例如 来自于lua 5.1 )是不兼容且不能被加载的。

参考链接:

LuaJIT官方文档

Luajit-2.1.0-beta1的发布和生成arm64用bytecode的解脱

64位lua引擎如何支持32位luac编译出来的二进制字节码?

【最新】LuaJIT 32/64 位字节码,从编译到使用全纪录

关于Xcode “Build Setting”中的Architectures详解

手机CPU各大厂商以及手机 cpu架构体系分类