Как определить функции C с LuaJIT? - PullRequest
0 голосов
/ 16 декабря 2018

This:

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 и я хотел использовать некоторые из его хороших сторон для некоторых вещей, но я не смог найти много примеров в библиотеке FFI LuaJIT.Кажется, что cdef используется только для объявлений функций, а не для определений.Как я могу сделать функции в C, а затем использовать их в Lua?

Ответы [ 2 ]

0 голосов
/ 17 декабря 2018

LuaJIT - это компилятор Lua, но не компилятор Си.Сначала вы должны скомпилировать ваш код на C в общую библиотеку.Например, для

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

с файлами test.c и test.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()

Живой пример на Wandbox

JIT в LuaJIT

В комментариях к вопросу,кто-то упомянул обходной путь для написания функций в машинном коде и их выполнения в LuaJIT в Windows.На самом деле, то же самое возможно в Linux, по сути, реализуя JIT в LuaJIT.В то время как в 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)

Живой пример на Wandbox

Как найти коды операций

Я вижу, что этот ответ имеетвызвал некоторый интерес, поэтому я хочу добавить кое-что, с чем мне было трудно вначале, а именно, как найти коды операций для инструкций, которые вы хотите выполнить.В Интернете есть некоторые ресурсы, в частности, Руководства разработчика программного обеспечения Intel® 64 и IA-32 , но никто не хочет просматривать тысячи страниц 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 шестнадцатеричные числа нечувствительны к регистру, и мне больше нравятся строчные).

0 голосов
/ 17 декабря 2018

LuaJIT включает в себя распознаватель для объявлений C, но это не полноценный компилятор C .Цель его системы FFI - определить, какие функции C экспортирует конкретная DLL, чтобы она могла загрузить эту DLL (через ffi.load) и позволить вам вызывать эти функции из Lua.

LuaJIT можетзагрузить предварительно скомпилированный код через интерфейс на основе DLL C, но он не может скомпилировать сам C.

...