1
0
forked from GitHub/lad

Uploading Lua Assembler/Disassembler for Lua 5.2

This commit is contained in:
Andre Murbach Maidl
2013-08-04 10:41:28 -03:00
parent 6c9a9f0cd6
commit 5d2eafb879
13 changed files with 2735 additions and 0 deletions
+685
View File
@@ -0,0 +1,685 @@
local re = require("re")
local ladconf = require("ladconf")
local OPCODE = { MOVE = 0, LOADK = 1, LOADKX = 2, LOADBOOL = 3, LOADNIL = 4,
GETUPVAL = 5, GETTABUP = 6, GETTABLE = 7, SETTABUP = 8,
SETUPVAL = 9, SETTABLE = 10, NEWTABLE = 11, SELF = 12,
ADD = 13, SUB = 14, MUL = 15, DIV = 16, MOD = 17, POW = 18,
UNM = 19, NOT = 20, LEN = 21, CONCAT = 22, JMP = 23,
EQ = 24, LT = 25, LE = 26, TEST = 27, TESTSET = 28,
CALL = 29, TAILCALL = 30, RETURN = 31, FORLOOP = 32,
FORPREP = 33, TFORCALL = 34, TFORLOOP = 35, SETLIST = 36,
CLOSURE = 37, VARARG = 38, EXTRAARG = 39 }
local grammar = [[
prog <- s ( {:tag: '' -> 'prog':} function )* -> {} !.
function <- ( {:tag: '' -> 'func':} {:header: header:} {:codelist: codelist:}) -> {}
header <- ("function" s {:name: name:} s "(" s {:numparams: n:} s ")" s ":" s) -> {}
codelist <- code+ -> {}
code <- autocode / manualcode
autocode <- ( {:tag: '' -> 'code':} n s ln s op s param s ("," s param s)* ) -> {}
manualcode <- ( {:tag: '' -> 'code':} ({:label: label:} s )? op s param s ("," s param s)* ) -> {}
n <- %d+
label <- name ":"
name <- ( !reserved {[a-zA-Z_][a-zA-Z0-9_]*} )
ln <- "[" %d+ "]"
op <- {:op: !reserved %a+ -> to_upper:}
param <- register / number / string
register <- ( {:tag: '' -> 'reg':} ("$" {n}) -> to_number ) -> {}
number <- ( {:tag: '' -> 'num':} ( hex / float / int ) ) -> {}
string <- ( {:tag: '' -> 'str':} ( name / shortstr ) ) -> {}
shortstr <- ( '"' {('\\' / '\"' / !'"' .)*} '"' / "'" {("\\" / "\'" / !"'" .)*} "'" ) -> to_string
hex <- ( {:tag: '' -> 'int':} {"-"? "0" [xX] %x+} -> to_number ) -> {}
float <- ( {:tag: '' -> 'float':} {"-"? ( (%d+ "." %d* / "." %d+) e? / %d+ e )} -> to_number ) -> {}
e <- [eE] [+-]? n
int <- ( {:tag: '' -> 'int':} {"-"? n} -> to_number ) -> {}
s <- (space / comment)*
space <- %s+
comment <- ";" (!%nl .)*
reserved <- "function"
]]
local function fixed_string(s)
s = string.gsub(s, "\\\"", '\"')
s = string.gsub(s, "\\\\", '\\')
s = string.gsub(s, "\\a", '\a')
s = string.gsub(s, "\\b", '\b')
s = string.gsub(s, "\\f", '\f')
s = string.gsub(s, "\\n", '\n')
s = string.gsub(s, "\\r", '\r')
s = string.gsub(s, "\\t", '\t')
s = string.gsub(s, "\\v", '\v')
s = string.gsub(s, "\\(%d+)", function (s) return string.char (tonumber(s)) end )
return s
end
local defs = {
to_number = function (n) return tonumber(n) end,
to_string = function (s) return fixed_string(s) end,
to_upper = function (s) return string.upper(s) end,
}
local parser = re.compile(grammar, defs)
local function parse(contents)
return parser:match(contents)
end
local function print_header(t)
io.write(string.format("\nfunction %s(%s):\n", t.name, t.numparams))
end
local function print_param(t)
if t.tag == 'reg' then
io.write(string.format("$%d", t[1]))
elseif t.tag == 'num' then
io.write(string.format("%s", tostring(t[1])))
elseif t.tag == 'str' then
io.write(string.format("%s", t[1]))
end
end
local function print_sep()
io.write(string.format(", "))
end
local function print_code(t, i)
if t.tag == 'code' then
if t.label then io.write(string.format("%s:", t.label)) end
io.write(string.format("\t%d", i))
io.write(string.format("\t%-9s\t", t.op))
for i=1,#t-1 do
print_param(t[i])
print_sep()
end
print_param(t[#t])
io.write(string.format("\n"))
end
end
local function print_codelist(t)
for i=1,#t do
print_code(t[i], i)
end
end
local function print_func(t)
if t.tag == 'func' then
print_header(t.header)
print_codelist(t.codelist)
end
end
local function print_ast(t)
if t.tag == 'prog' then
for k,v in ipairs(t) do
print_func(v)
end
end
end
local function converge(ast)
if ast.tag ~= 'prog' then return nil end
local t = {}
for k,v in ipairs(ast) do
if v.tag ~= 'func' then return nil end
local name = v.header.name
t[name] = {}
t[name].id = k
end
return t
end
local function R(t, f)
if t.tag ~= 'reg' then error("Register expected") end
local r = t[1]
if not f.register[r] then
f.register[r] = r
f.maxstacksize = f.maxstacksize + 1
end
return r
end
local function Int(t)
if t.tag ~= 'num' or t[1].tag ~= 'int' then
error("Integer expected")
end
local i = t[1][1]
if i < 0 then error("Positive integer expected") end
return i
end
local function Label(t, f)
local l = t[1]
if not f.label[l] then error(string.format("Label %s not defined", l)) end
return f.label[l]
end
local function Kst(t, f)
local k, k_type
if t.tag == 'num' then
k = t[1][1]
k_type = ladconf.LUA_TNUMBER
elseif t.tag == 'str' then
k = t[1]
k_type = ladconf.LUA_TSTRING
else
error ("Constant not implemented")
end
if not f.const[k] then
local n = f.sizek + 1
f.sizek = n
f.const[k] = n
f.k[n] = {}
f.k[n].k_type = k_type
f.k[n].value = k
end
return f.const[k]
end
local function KPROTO(t, f, a)
if t.tag ~= 'str' then error("Function name expected") end
local name = t[1]
if not a[name] then
error(string.format("Function %s not defined", name))
end
if not f.func[name] then
local n = f.sizep + 1
f.sizep = n
f.func[name] = n
f.p[n] = {}
f.p[n] = name
end
return f.func[name]
end
local function UpValue(t, f)
if t.tag ~= 'str' then error("String/Name expected") end
local name = t[1]
if not f.upval[name] then
local n = f.sizeupvalues + 1
f.sizeupvalues = n
f.upval[name] = n
f.upvalues[n] = {}
if name == "_ENV" then
f.upvalues[n].instack = 0
else
f.upvalues[n].instack = 1
end
f.upvalues[n].idx = 0
f.upvalues[n].name = name
end
return f.upval[name]
end
local function RK(t, f)
if t.tag == 'reg' then
return R(t, f)
end
-- TODO: fix it (does not work if sizek > 2^8)
local k = Kst(t, f)
if k < ladconf.MAXINDEXRK then
return k + ladconf.MAXINDEXRK
end
return k
end
local function SBX(t, f, n)
if t.tag == 'num' then
return Int(t) - n
elseif t.tag == 'str' then
return Label(t, f) - n
end
end
local function sew_label(i, t, n)
if i.label then
local l = i.label
if not t[l] then t[l] = n
else error (string.format("Label %s already defined\n", l))
end
end
end
local function sew_code(t, ast, all)
local codelist = ast[t.id].codelist
t.sizecode = #codelist
-- first check for labels
t.label = {}
for k,v in ipairs(codelist) do
sew_label(v, t.label, k)
end
-- after that check code
t.code = {}
for k,v in ipairs(codelist) do
if v.tag ~= 'code' then return nil end
local f,i = t,{}
local op = v.op
t.code[k] = i
i.O = OPCODE[op]
if op == "MOVE" or
op == "UNM" or
op == "NOT" or
op == "LEN" then
-- R(A) R(B)
i.A = R(v[1], f)
i.B = R(v[2], f)
i.C = 0
elseif op == "LOADK" then
-- R(A) Kst(Bx)
i.A = R(v[1], f)
i.Bx = Kst(v[2], f)
elseif op == "LOADKX" then
-- R(A)
if codelist[k+1].op ~= "EXTRAARG" then
error("For LOADKX next instruction is always EXTRAARG")
end
i.A = R(v[1], f)
i.Bx = 0
elseif op == "LOADBOOL" or
op == "NEWTABLE" or
op == "CALL" or
op == "TAILCALL" or
op == "SETLIST" then
-- R(A) B C
i.A = R(v[1], f)
i.B = Int(v[2])
i.C = Int(v[3])
elseif op == "LOADNIL" or
op == "RETURN" then
-- R(A) B
i.A = R(v[1], f)
i.B = Int(v[2])
i.C = 0
elseif op == "GETUPVAL" or
op == "SETUPVAL" then
-- R(A) UpValue(B)
i.A = R(v[1], f)
i.B = UpValue(v[2], f) - 1
i.C = 0
elseif op == "GETTABUP" then
-- R(A) UpValue(B) RK(C)
i.A = R(v[1], f)
i.B = UpValue(v[2], f) - 1
i.C = RK(v[3], f)
elseif op == "GETTABLE" or
op == "SELF" then
-- R(A) R(B) RK(C)
i.A = R(v[1], f)
i.B = R(v[2], f)
i.C = RK(v[3], f)
elseif op == "SETTABUP" then
-- UpValue(A) RK(B) RK(C)
i.A = UpValue(v[1], f) - 1
i.B = RK(v[2], f)
i.C = RK(v[3], f)
elseif op == "SETTABLE" or
op == "ADD" or
op == "SUB" or
op == "MUL" or
op == "DIV" or
op == "MOD" or
op == "POW" then
-- R(A) RK(B) RK(C)
i.A = R(v[1], f)
i.B = RK(v[2], f)
i.C = RK(v[3], f)
elseif op == "CONCAT" then
-- R(A) R(B) R(C)
i.A = R(v[1], f)
i.B = R(v[2], f)
i.C = R(v[3], f)
elseif op == "JMP" then
-- A sBx
i.A = Int(v[1])
i.sBx = SBX(v[2], f, k)
elseif op == "EQ" or
op == "LT" or
op == "LE" then
-- A RK(B) RK(C)
i.A = Int(v[1])
i.B = RK(v[2], f)
i.C = RK(v[3], f)
elseif op == "TEST" or
op == "TFORCALL" then
-- R(A) C
i.A = R(v[1], f)
i.B = 0
i.C = Int(v[2])
elseif op == "TESTSET" then
-- R(A) R(B) C
i.A = R(v[1], f)
i.B = R(v[2], f)
i.C = Int(v[3])
elseif op == "FORLOOP" or
op == "FORPREP" or
op == "TFORLOOP" then
-- R(A) sBx
i.A = R(v[1], f)
i.sBx = SBX(v[2], f, k)
elseif op == "CLOSURE" then
-- R(A) KPROTO(Bx)
i.A = R(v[1], f)
i.Bx = KPROTO(v[2], f, all)
elseif op == "VARARG" then
-- R(A) B
i.A = R(v[1], f)
i.B = Int(v[2])
i.C = 0
f.is_vararg = 1
f.numparams = i.B
elseif op == "EXTRAARG" then
-- Ax
local pop = codelist[k-1].op
if pop == "LOADKX" then
i.Ax = Kst(v[1], f) - 1
elseif pop == "SETLIST" then
i.Ax = Int(v[1])
else
error ("EXTRAARG not expected")
end
else
local str = string.format("%s not implemented\n", op)
error (str)
end
end
end
local function sew_function(t, ast, all)
local name = ast[t.id].header.name
t.linedefined = 0
t.lastlinedefined = 0
t.numparams = ast[t.id].header.numparams
if name ~= "main" then
t.is_vararg = 0
end
-- registers 0/1 are always valid
t.register = {}
t.register[0] = 0
t.register[1] = 1
t.maxstacksize = 2
-- constants
t.sizek = 0
t.k = {}
t.const = {}
-- functions
t.sizep = 0
t.p = {}
t.func = {}
-- upvalues
if name ~= "main" then
t.sizeupvalues = 0
t.upvalues = {}
t.upval = {}
end
t.sizelineinfo = 0
t.sizelocvars = 0
sew_code(t, ast, all)
end
local function sew(t, ast)
if not t.main then error ("main not defined") end
-- main function always have upvalue _ENV defined
t.main.sizeupvalues = 1
t.main.upval = {}
t.main.upval[ladconf.LUA_ENV] = 1
t.main.upvalues = {}
t.main.upvalues[1] = {}
t.main.upvalues[1].instack = 1
t.main.upvalues[1].idx = 0
t.main.upvalues[1].name = ladconf.LUA_ENV
-- main is always vararg
t.main.is_vararg = 1
for k,v in pairs(t) do
sew_function(v, ast, t)
end
end
local function traverse(ast)
local t = converge(ast)
if not t then error("Empty File") end
sew(t, ast)
return t
end
local function write_byte(output, byte)
if byte ~= 0 then
output:write(string.format("%c", byte))
else
output:write('\0')
end
end
local function get_hex(n)
return string.format("0x%x", n)
end
local function get_int(i, s)
local x = get_hex(i)
local a,b = 0,8
local t = {}
for k=1,s do
t[k] = ladconf.get_bit(x, a, b)
a = a + 8
b = b + 8
end
return t
end
local function write_int(output, n, s)
local i = get_int(n, s)
for k=1,s do
write_byte(output, i[k])
end
end
local function write_string(output, str)
local len = string.len(str)
write_int(output, len + 1, ladconf.SIZE_T)
for i=1,len do
write_byte(output, string.byte(str, i))
end
write_byte(output, 0)
end
local function write_source_name(output, name)
write_string(output, name)
end
local function get_byte(v)
return math.floor(v / 256), string.char(math.floor(v) % 256)
end
local function convert_number(x)
local sign = 0
if x < 0 then sign = 1; x = -x end
local mantissa, exponent = math.frexp(x)
if x == 0 then -- zero
mantissa, exponent = 0, 0
else
mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, 53)
exponent = exponent + 1022
end
local v, byte = {}, "" -- convert to bytes
x = mantissa
for i = 1,6 do
x, byte = get_byte(x); v[i] = string.byte(byte)
end
x, byte = get_byte(exponent * 16 + x); v[7] = string.byte(byte)
x, byte = get_byte(sign * 128 + x); v[8] = string.byte(byte)
return v
end
local function write_number(output, n)
local t = convert_number(n)
for k=1,ladconf.LUA_NUMBER do write_byte(output, t[k]) end
end
local function write_header(output)
for i=1,string.len(ladconf.LUA_SIGNATURE) do
write_byte(output, string.byte(ladconf.LUA_SIGNATURE, i))
end
write_byte(output, ladconf.LUA_VERSION)
write_byte(output, ladconf.LUA_FORMAT)
write_byte(output, ladconf.ENDIANNESS)
write_byte(output, ladconf.INT)
write_byte(output, ladconf.SIZE_T)
write_byte(output, ladconf.INSTRUCTION)
write_byte(output, ladconf.LUA_NUMBER)
write_byte(output, ladconf.INTEGRAL)
for i=1,string.len(ladconf.LUAC_TAIL) do
write_byte(output, string.byte(ladconf.LUAC_TAIL, i))
end
end
local function write_function_values(output, f)
write_byte(output, f.numparams)
write_byte(output, f.is_vararg)
write_byte(output, f.maxstacksize)
end
local function gen_iABC(O, A, B, C)
local field = {O, A, C, B}
local v, i = {}, 0
local cValue, cBits, cPos = 0, 0, 1
-- encode an instruction
while i < ladconf.INSTRUCTION do
-- if need more bits, suck in a field at a time
while cBits < 8 do
cValue = field[cPos] * math.ldexp(1, cBits) + cValue
cBits = cBits + ladconf.ABC[cPos]; cPos = cPos + 1
end
-- extract bytes to instruction string
while cBits >= 8 do
v[i+1] = (cValue % 256)
cValue = math.floor(cValue / 256)
cBits = cBits - 8; i = i + 1
end
end
return v
end
local function gen_iABx(O, A, Bx)
return gen_iABC(O, A, math.floor(Bx / ladconf.MASK_C), (Bx % ladconf.MASK_C))
end
local function gen_iAsBx(O, A, sBx)
return gen_iABx(O, A, (sBx + ladconf.MAXARG_sBx))
end
local function gen_iAx(O, Ax)
return gen_iABC(O, Ax, 0, 0)
end
local function write_instruction(output, i)
local m = ladconf.get_op_mode(i.O + 1)
local t = {}
if m == ladconf.iABC then
t = gen_iABC(i.O, i.A, i.B, i.C)
elseif m == ladconf.iABx then
t = gen_iABx(i.O, i.A, i.Bx - 1)
elseif m == ladconf.iAsBx then
t = gen_iAsBx(i.O, i.A, i.sBx - 1)
elseif m == ladconf.iAx then
t = gen_iAx(i.O, i.Ax)
end
for k=1,ladconf.INSTRUCTION do write_byte(output, t[k]) end
end
local function write_code(output, f)
local n = f.sizecode
write_int(output, n, ladconf.INT)
for i=1,n do
write_instruction(output, f.code[i])
end
end
local function write_constant(output, k)
local t = k.k_type
write_byte(output, t)
if t == ladconf.LUA_TNIL then
-- do not need to write anything
elseif t == ladconf.LUA_TBOOLEAN then
elseif t == ladconf.LUA_TNUMBER then
write_number(output, k.value)
elseif t == ladconf.LUA_TSTRING then
write_string(output, k.value)
end
end
local function write_constants(output, f)
local n
n = f.sizek
write_int(output, n, ladconf.INT)
for i=1,n do
write_constant(output, f.k[i])
end
end
local function write_upvalues(output, f)
local n = f.sizeupvalues
write_int(output, n, ladconf.INT)
for i=1,n do
write_byte(output, f.upvalues[i].instack)
write_byte(output, f.upvalues[i].idx)
end
end
local function write_debug(output, f)
write_string(output, ladconf.LUA_SOURCE)
write_int(output, f.sizelineinfo, ladconf.INT)
write_int(output, f.sizelocvars, ladconf.INT)
local n = f.sizeupvalues
write_int(output, n, ladconf.INT)
for k=1,n do
write_string(output, f.upvalues[k].name)
end
end
local function write_function(output, current, parsed)
write_int(output, current.linedefined, ladconf.INT) -- line defined
write_int(output, current.lastlinedefined, ladconf.INT) -- last line defined
write_function_values(output, current)
write_code(output, current)
write_constants(output, current, parsed)
n = current.sizep
write_int(output, n, ladconf.INT)
for i=1,n do
local name = current.p[i]
write_function(output, parsed[name], parsed)
end
write_upvalues(output, current)
write_debug(output, current)
end
local function write_bytecode(output, parsed)
write_header(output)
write_function(output, parsed.main, parsed)
end
local function write(filename, parsed)
local output = assert(io.open(filename, "wb"))
write_bytecode(output, parsed)
output:close()
end
local assembler = {
parse = parse,
traverse = traverse,
write = write,
print_ast = print_ast,
}
return assembler