【问题标题】:How to define C functions with LuaJIT?如何用 LuaJIT 定义 C 函数?
【发布时间】:2019-05-17 06:29:24
【问题描述】:

这个:

local ffi = require "ffi"

ffi.cdef[[
  int return_one_two_four(){
    return 124;
  }
]]

local function print124()
  print(ffi.C.return_one_two_four())
end

print124()

抛出错误:

Error: main.lua:10: cannot resolve symbol 'return_one_two_four': The specified procedure could not be found.

我对 C 有一定程度的掌握,并希望将它的一些优点用于一些事情,但我在 LuaJIT 的 FFI 库上找不到很多示例。似乎cdef 仅用于函数声明而不是定义。如何在 C 中创建函数,然后在 Lua 中使用它们?

【问题讨论】:

  • 你必须用 C 编写它们并将它们编译为共享库(然后通过 ffi.load 加载),你不能直接在 Lua 中编写它们
  • 有一个 workaround - 用机器代码而不是 C 编写函数,但它仅适用于您使用 Windows(x64 或 x86)并且您的 LuaJIT 是 x86 并且使用MinGW。

标签: c lua luajit


【解决方案1】:

LuaJIT 是 Lua 编译器,但不是 C 编译器。您必须首先将 C 代码编译到共享库中。例如与

gcc -shared -fPIC -o libtest.so test.c
luajit test.lua

文件test.ctest.lua 如下。

test.c

int return_one_two_four(){
    return 124;
}

test.lua

local ffi = require"ffi"

local ltest = ffi.load"./libtest.so"

ffi.cdef[[
int return_one_two_four();
]]

local function print124()
    print(ltest.return_one_two_four())
end

print124()

Live example on Wandbox

LuaJIT 中的 JIT

在问题下的 cmets 中,有人提到了一种解决方法,即用机器代码编写函数并让它们在 Windows 上的 LuaJIT 中执行。实际上,在 Linux 中同样可以通过在 LuaJIT 中实现 JIT 来实现。在 Windows 上,您可以将操作码插入到字符串中,将其转换为函数指针并调用它,但由于页面限制,在 Linux 上是不可能的。在 Linux 上,内存要么是可写的,要么是可执行的,但永远不会同时存在,所以我们必须以读写模式分配一个页面,插入程序集,然后将模式更改为读-执行。为此,只需使用 Linux 内核函数来获取页面大小和映射内存。但是,即使您犯了最微小的错误,例如其中一个操作码中的拼写错误,程序也会出现段错误。我使用的是 64 位程序集,因为我使用的是 64 位操作系统。

重要提示:在您的机器上执行此操作之前,请检查 <bits/mman-linux.h> 中的幻数。它们在每个系统上都不相同。

local ffi = require"ffi"

ffi.cdef[[
typedef unsigned char uint8_t;
typedef long int off_t;

// from <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,
           int fd, off_t offset);
int munmap(void *addr, size_t length);
int mprotect(void *addr, size_t len, int prot);

// from <unistd.h>
int getpagesize(void);
]]

-- magic numbers from <bits/mman-linux.h>
local PROT_READ     = 0x1  -- Page can be read.
local PROT_WRITE    = 0x2  -- Page can be written.
local PROT_EXEC     = 0x4  -- Page can be executed.
local MAP_PRIVATE   = 0x02 -- Changes are private.
local MAP_ANONYMOUS = 0x20 -- Don't use a file.

local page_size = ffi.C.getpagesize()
local prot = bit.bor(PROT_READ, PROT_WRITE)
local flags = bit.bor(MAP_ANONYMOUS, MAP_PRIVATE)
local code = ffi.new("uint8_t *", ffi.C.mmap(ffi.NULL, page_size, prot, flags, -1, 0))

local count = 0
local asmins = function(...)
    for _,v in ipairs{ ... } do
        assert(count < page_size)
        code[count] = v
        count = count + 1
    end
end

asmins(0xb8, 0x7c, 0x00, 0x00, 0x00) -- mov rax, 124
asmins(0xc3) -- ret

ffi.C.mprotect(code, page_size, bit.bor(PROT_READ, PROT_EXEC))

local fun = ffi.cast("int(*)(void)", code)
print(fun())

ffi.C.munmap(code, page_size)

Live example on Wandbox

如何找到操作码

我看到这个答案引起了一些兴趣,所以我想添加一些我一开始遇到困难的东西,即如何找到你想要执行的指令的操作码。网上有一些资源,最著名的是Intel® 64 and IA-32 Architectures Software Developer Manuals,但没有人愿意浏览数千个 PDF 页面来了解如何操作mov rax, 124。因此,有些人制作了列出指令和相应操作码的表格,例如http://ref.x86asm.net/,但是在表中查找操作码也很麻烦,因为即使mov 根据目标和源操作数的不同,也可以有许多不同的操作码。所以我要做的是写一个简短的汇编文件,例如

mov rax, 124
ret

您可能想知道,为什么我的汇编文件中没有函数,也没有像segment .text 这样的东西。好吧,因为我不想链接它,我可以把所有这些都省掉,省点打字。然后只需使用

组装它
$ nasm -felf64 -l test.lst test.s

-felf64 选项告诉汇编器我正在使用 64 位语法,-l test.lst 选项告诉我想要在文件test.lst 中列出生成的代码。清单看起来与此类似:

$ cat test.lst
     1 00000000 B87C000000              mov rax, 124
     2 00000005 C3                      ret

第三列包含我感兴趣的操作码。只需将它们拆分为 1 个字节的单元并将它们插入到您的程序中,即 B87C000000 变为 0xb8, 0x7c, 0x00, 0x00, 0x00(幸运的是,十六进制数字在 Lua 中不区分大小写,我喜欢小写更好)。

【讨论】:

    【解决方案2】:

    LuaJIT 包含一个 C 声明识别器,但它不是一个成熟的 C 编译器。其 FFI 系统的目的是能够定义特定 DLL 导出的 C 函数,以便它可以加载该 DLL(通过 ffi.load)并允许您从 Lua 调用这些函数。

    LuaJIT 可以通过基于 DLL C 的接口加载预编译的代码,但它不能编译 C 本身。

    【讨论】:

      【解决方案3】:

      从技术上讲,你可以做你想做的事情而不会太麻烦(只要代码足够简单)。 使用这样的东西: https://github.com/nucular/tcclua 使用 tcc(它非常小,您甚至可以轻松地使用它进行部署)它是一个很好的方式,可以将两全其美,都放在一个包中:)

      【讨论】:

        猜你喜欢
        • 2021-09-17
        • 1970-01-01
        • 1970-01-01
        • 2016-08-11
        • 1970-01-01
        • 2019-09-21
        • 2017-06-03
        • 2013-03-12
        • 2020-05-27
        相关资源
        最近更新 更多