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 шестнадцатеричные числа нечувствительны к регистру, и мне больше нравятся строчные).