LuaJIT — это компилятор Lua, но не компилятор C. Сначала вы должны скомпилировать свой код 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, чтобы получить размер страницы и отображенную память. Однако, если вы сделаете даже малейшую ошибку, например, опечатку в одном из опкодов, программа выдаст segfault. Я использую 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 нечувствительны к регистру, и мне больше нравятся строчные).
person
Henri Menke
schedule
16.12.2018
ffi.load
), вы не можете написать их напрямую на Lua. - person UnholySheep   schedule 16.12.2018