mirror of
https://github.com/LuaJIT/LuaJIT.git
synced 2025-02-10 00:14:09 +00:00
275 lines
7.1 KiB
Lua
275 lines
7.1 KiB
Lua
|
|
--Binding to the DynASM encoding engine.
|
|
--Written by Cosmin Apreutesei. Public Domain.
|
|
|
|
local ffi = require'ffi'
|
|
local bit = require'bit'
|
|
local arch = ffi.arch
|
|
if arch == 'x64' then arch = 'x86' end --same linker for x64
|
|
local C = ffi.load('dasm_'..arch)
|
|
local M = {C = C}
|
|
|
|
M._VERSION = 10400
|
|
|
|
ffi.cdef[[
|
|
enum {
|
|
DASM_S_OK = 0x00000000,
|
|
DASM_S_NOMEM = 0x01000000,
|
|
DASM_S_PHASE = 0x02000000,
|
|
DASM_S_MATCH_SEC = 0x03000000,
|
|
DASM_S_RANGE_I = 0x11000000,
|
|
DASM_S_RANGE_SEC = 0x12000000,
|
|
DASM_S_RANGE_LG = 0x13000000,
|
|
DASM_S_RANGE_PC = 0x14000000,
|
|
DASM_S_RANGE_VREG = 0x15000000,
|
|
DASM_S_UNDEF_L = 0x21000000,
|
|
DASM_S_UNDEF_PC = 0x22000000,
|
|
};
|
|
|
|
/* Internal DynASM encoder state. */
|
|
typedef struct dasm_State_Ref { struct dasm_State *p; } dasm_State_Ref;
|
|
typedef dasm_State_Ref *Dst_DECL;
|
|
|
|
/* Initialize and free DynASM state. */
|
|
void dasm_init(Dst_DECL, int maxsection);
|
|
void dasm_free(Dst_DECL);
|
|
|
|
/* Setup global array. Must be called before dasm_setup(). */
|
|
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl);
|
|
|
|
/* Grow PC label array. Can be called after dasm_setup(), too. */
|
|
void dasm_growpc(Dst_DECL, unsigned int maxpc);
|
|
|
|
/* Setup encoder. */
|
|
void dasm_setup(Dst_DECL, const void *actionlist);
|
|
|
|
/* Feed encoder with actions. Calls are generated by pre-processor. */
|
|
void dasm_put(Dst_DECL, int start, ...);
|
|
|
|
/* Link sections and return the resulting size. */
|
|
int dasm_link(Dst_DECL, size_t *szp);
|
|
|
|
/* Encode sections into buffer. */
|
|
int dasm_encode(Dst_DECL, void *buffer);
|
|
|
|
/* Get PC label offset. */
|
|
int dasm_getpclabel(Dst_DECL, unsigned int pc);
|
|
|
|
/* Optional sanity checker to call between isolated encoding steps. */
|
|
int dasm_checkstep(Dst_DECL, int secmatch);
|
|
|
|
typedef int (*DASM_EXTERN_TYPE) (void *ctx, unsigned char *addr, int idx, int type);
|
|
DASM_EXTERN_TYPE DASM_EXTERN_FUNC;
|
|
]]
|
|
|
|
local function err(...)
|
|
io.stderr:setvbuf'no'
|
|
io.stderr:write('dasm error: ', ...)
|
|
io.stderr:write'\n'
|
|
os.exit(1)
|
|
end
|
|
|
|
--status check helper
|
|
|
|
local status_map = {
|
|
[C.DASM_S_NOMEM] = 'out of memory',
|
|
[C.DASM_S_PHASE] = 'phase error',
|
|
[C.DASM_S_MATCH_SEC] = 'section not found',
|
|
[C.DASM_S_RANGE_I] = 'immediate value out of range',
|
|
[C.DASM_S_RANGE_SEC] = 'too many sections',
|
|
[C.DASM_S_RANGE_LG] = 'too many global labels',
|
|
[C.DASM_S_RANGE_PC] = 'too many pclabels',
|
|
[C.DASM_S_RANGE_VREG] = 'variable register out of range',
|
|
[C.DASM_S_UNDEF_L] = 'undefined global label',
|
|
[C.DASM_S_UNDEF_PC] = 'undefined pclabel',
|
|
}
|
|
|
|
local function checkst(st)
|
|
if st == C.DASM_S_OK then return end
|
|
local status, arg = status_map[bit.band(st, 0xff000000)], bit.band(st, 0x00ffffff)
|
|
if status then
|
|
err(status, '. :', arg)
|
|
else
|
|
err(string.format('0x%08X', st))
|
|
end
|
|
end
|
|
|
|
--low level API
|
|
|
|
M.init = C.dasm_init
|
|
M.free = C.dasm_free
|
|
M.setupglobal = C.dasm_setupglobal
|
|
M.growpc = C.dasm_growpc
|
|
M.setup = C.dasm_setup
|
|
|
|
local int_ct = ffi.typeof'int'
|
|
local function convert_arg(arg) --dasm_put() accepts only int32 varargs.
|
|
if type(arg) == "number" then --but we make it accept uint32 too by normalizing the arg.
|
|
arg = bit.tobit(arg) --non-number args are converted to int32 according to ffi rules.
|
|
end
|
|
return ffi.cast(int_ct, arg)
|
|
end
|
|
local function convert_args(...) --not a tailcall but at least it doesn't make any garbage
|
|
if select('#', ...) == 0 then return end
|
|
return convert_arg(...), convert_args(select(2, ...))
|
|
end
|
|
function M.put(state, start, ...)
|
|
C.dasm_put(state, start, convert_args(...))
|
|
end
|
|
|
|
function M.link(state, sz)
|
|
sz = sz or ffi.new'size_t[1]'
|
|
checkst(C.dasm_link(state, sz))
|
|
return tonumber(sz[0])
|
|
end
|
|
|
|
function M.encode(state, buf)
|
|
checkst(C.dasm_encode(state, buf))
|
|
end
|
|
jit.off(M.encode) --calls the DASM_EXTERN_FUNC callback
|
|
|
|
local voidptr_ct = ffi.typeof'void*'
|
|
local byteptr_ct = ffi.typeof'int8_t*'
|
|
function M.getpclabel(state, pc, buf)
|
|
local offset = C.dasm_getpclabel(state, pc)
|
|
if buf then
|
|
return ffi.cast(voidptr_ct, ffi.cast(byteptr_ct, buf) + offset)
|
|
end
|
|
return offset
|
|
end
|
|
|
|
function M.checkstep(state, section)
|
|
checkst(C.dasm_checkstep(state, section or -1))
|
|
end
|
|
|
|
--get the address of a standard symbol.
|
|
--TODO: ask Mike to expose clib_getsym() in ffi so we can get the address
|
|
--of symbols without having to declare them first.
|
|
local function getsym(name)
|
|
return ffi.C[name]
|
|
end
|
|
local getsym = function(name)
|
|
local ok, sym = pcall(getsym, name)
|
|
if not ok then --not found or not defined: define it and try again
|
|
ffi.cdef(string.format('void %s();', name))
|
|
return getsym(name)
|
|
else
|
|
return sym
|
|
end
|
|
end
|
|
|
|
--DASM_EXTERN callback plumbing
|
|
|
|
local extern_names --t[idx] -> name
|
|
local extern_get --f(name) -> ptr
|
|
|
|
local byteptr_ct = ffi.typeof'uint8_t*'
|
|
|
|
local function DASM_EXTERN_FUNC(ctx, addr, idx, type)
|
|
if not extern_names or not extern_get then
|
|
err'extern callback not initialized.'
|
|
end
|
|
local name = extern_names[idx]
|
|
local ptr = extern_get(name)
|
|
if ptr == nil then
|
|
err('extern not found: ', name, '.')
|
|
end
|
|
if type ~= 0 then
|
|
return ffi.cast(byteptr_ct, ptr) - addr - 4
|
|
else
|
|
return ptr
|
|
end
|
|
end
|
|
|
|
function M.setupextern(_, names, getter)
|
|
extern_names = names
|
|
extern_get = getter or getsym
|
|
if C.DASM_EXTERN_FUNC == nil then
|
|
C.DASM_EXTERN_FUNC = DASM_EXTERN_FUNC
|
|
end
|
|
end
|
|
|
|
--hi-level API
|
|
|
|
function M.new(actionlist, externnames, sectioncount, globalcount, externget, globals)
|
|
local state = ffi.new'dasm_State_Ref'
|
|
M.init(state, sectioncount or 1)
|
|
globalcount = globalcount or 256
|
|
globals = globals or ffi.new('void*[?]', globalcount)
|
|
ffi.gc(state, function(state)
|
|
local _ = actionlist, externnames, globals, externget --anchor those: don't rely on the user doing so
|
|
M.free(state)
|
|
end)
|
|
M.setupglobal(state, globals, globalcount)
|
|
M.setupextern(state, externnames, externget)
|
|
M.setup(state, actionlist)
|
|
return state, globals
|
|
end
|
|
|
|
function M.build(state)
|
|
state:checkstep(-1)
|
|
local sz = state:link()
|
|
if sz == 0 then err'no code?' end
|
|
local mm = require'dasm_mm' --runtime dependency
|
|
local buf = mm.new(sz)
|
|
state:encode(buf)
|
|
mm.protect(buf, sz)
|
|
return buf, sz
|
|
end
|
|
|
|
function M.dump(addr, size, out)
|
|
local disass = require('jit.dis_'..jit.arch).disass
|
|
disass(ffi.string(addr, size), tonumber(ffi.cast('uintptr_t', addr)), out)
|
|
end
|
|
|
|
--given the globals array from dasm.new() and the globalnames list
|
|
--from the `.globalnames` directive, return a map {global_name -> global_addr}.
|
|
function M.globals(globals, globalnames)
|
|
local t = {}
|
|
for i = 0, #globalnames do
|
|
if globals[i] ~= nil then
|
|
t[globalnames[i]] = globals[i]
|
|
end
|
|
end
|
|
return t
|
|
end
|
|
|
|
--object interface
|
|
|
|
ffi.metatype('dasm_State_Ref', {__index = {
|
|
--low-level API
|
|
init = M.init,
|
|
free = M.free,
|
|
setupglobal = M.setupglobal,
|
|
setupextern = M.setupextern,
|
|
growpc = M.growpc,
|
|
setup = M.setup,
|
|
put = M.put,
|
|
link = M.link,
|
|
encode = M.encode,
|
|
getpclabel = M.getpclabel,
|
|
checkstep = M.checkstep,
|
|
--hi-level API
|
|
build = M.build,
|
|
}})
|
|
|
|
if not ... then --demo
|
|
local dasm = M
|
|
local actions = ffi.new('const uint8_t[19]', {254,0,102,184,5,0,254,1,102,187,3,0,254,2,102,187,3,0,255})
|
|
local Dst, globals = dasm.new(actions, nil, 3)
|
|
--|.code
|
|
dasm.put(Dst, 0)
|
|
--| mov ax, 5
|
|
--|.sub1
|
|
dasm.put(Dst, 2)
|
|
--| mov bx, 3
|
|
--|.sub2
|
|
dasm.put(Dst, 8)
|
|
--| mov bx, 3
|
|
dasm.put(Dst, 14)
|
|
local addr, size = Dst:build()
|
|
dasm.dump(addr, size)
|
|
end
|
|
|
|
return M
|