diff --git a/5.2/ChangeLog b/5.2/ChangeLog new file mode 100644 index 0000000..577c7f5 --- /dev/null +++ b/5.2/ChangeLog @@ -0,0 +1,6 @@ +Version 0.2 - 28/03/2012 +* Ported code from Lua 5.1.4 to Lua 5.2.0 ++ Support to Lua 5.2.0 +- Support to Lua 5.1.4 +Version 0.1 - 19/07/2011 +* Initial version for Lua 5.1.4 diff --git a/5.2/README b/5.2/README new file mode 100644 index 0000000..5f0add8 --- /dev/null +++ b/5.2/README @@ -0,0 +1,5 @@ +Lua Assembler/Disassembler 0.2, relased on March 28 2012. + +For further information about Lua Assembler/Disassembler, +as well as installion instructions and license details, +please, see doc/manual.html. diff --git a/5.2/ToDo b/5.2/ToDo new file mode 100644 index 0000000..9543e01 --- /dev/null +++ b/5.2/ToDo @@ -0,0 +1,22 @@ + +Assembler + +- Fix RK to work with constants larger than 2^8. +- Add following command line options: + - endianness; + - size of string constants; + - size of ints; + - type of lua numbers. +- Insert line counter to improve error messages. +- Add operator '...' to allow the definition of vararg functions. +- Add following instructions: JNE, JG, JL, JGE, JLE and JE. +- Add below constants/parameters if necessary: + - TRUE; + - FALSE; + - NIL. + +Disassembler + +- Make it work with big endian code. +- Write '(...)' instead of '(0)' when a function is vararg. + diff --git a/5.2/assembler.lua b/5.2/assembler.lua new file mode 100644 index 0000000..14232ea --- /dev/null +++ b/5.2/assembler.lua @@ -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 diff --git a/5.2/disassembler.lua b/5.2/disassembler.lua new file mode 100644 index 0000000..f3a19f3 --- /dev/null +++ b/5.2/disassembler.lua @@ -0,0 +1,754 @@ +local ladconf = require("ladconf") + +local function check_signature(bytecode) + local sig = string.format(string.format("%o", string.byte(bytecode, 1))) + sig = sig .. string.char(string.byte(bytecode, 2)) + sig = sig .. string.char(string.byte(bytecode, 3)) + sig = sig .. string.char(string.byte(bytecode, 4)) + sig = string.gsub(sig, "33", "\033") + if sig ~= ladconf.LUA_SIGNATURE then + error ("Not a Lua bytecode!") + end +end + +local function check_version(bytecode) + if string.byte(bytecode, 5) ~= ladconf.LUA_VERSION then + error ("Disassembler works only with Lua 5.2!") + end +end + +local function check_header(bytecode) + check_signature(bytecode) + check_version(bytecode) +end + +local function set_auto_flags(bytecode) + ladconf.ENDIANNESS = string.byte(bytecode, 7) + ladconf.INT = string.byte(bytecode, 8) + ladconf.SIZE_T = string.byte(bytecode, 9) + ladconf.INSTRUCTION = string.byte(bytecode, 10) + ladconf.LUA_NUMBER = string.byte(bytecode, 11) + ladconf.INTEGRAL = string.byte(bytecode, 12) +end + +local function parse_int(bytecode, k, n) + local i = "0x" + local s,e = k + (n-1), k + 1 + for p=s,e,-1 do + i = i .. string.format("%02x", string.byte(bytecode, p)) + end + i = i .. string.format("%02x", string.byte(bytecode, k)) + return tonumber(i),k + (n-1) +end + +local function parse_instruction(bytecode, k) + local code = "0x" + local n = k + (ladconf.INSTRUCTION - 1) + for i=n,k,-1 do + code = code .. string.format("%02x", string.byte(bytecode, i)) + end + return code,k + (ladconf.INSTRUCTION - 1) +end + +local function parse_code(bytecode, k, parsed) + parsed.sizecode,k = parse_int(bytecode, k, ladconf.INT) + parsed.code = {} + for i=1,parsed.sizecode do + parsed.code[i],k = parse_instruction(bytecode, k + 1) + end + return k +end + +local function setnilvalue(parsed) + parsed.type = ladconf.LUA_TNIL + parsed.value = 'nil' +end + +local function setbvalue(parsed, boolean) + parsed.type = ladconf.LUA_TBOOLEAN + if boolean == 0 then + parsed.value = 'false' + end + parsed.value = 'true' +end + +local function setnvalue(parsed, number) + parsed.type = ladconf.LUA_TNUMBER + parsed.value = number +end + +local function iscntrl(x) + if (x >= 0 and x <= 31) or (x == 127) then return true end + return false +end + +local function isprint(x) + return not iscntrl(x) +end + +local function fixed_string(str) + local new_str = "" + for i=1,string.len(str) do + char = string.byte(str, i) + if char == 34 then new_str = new_str .. string.format("\\\"") + elseif char == 92 then new_str = new_str .. string.format("\\\\") + elseif char == 7 then new_str = new_str .. string.format("\\a") + elseif char == 8 then new_str = new_str .. string.format("\\b") + elseif char == 12 then new_str = new_str .. string.format("\\f") + elseif char == 10 then new_str = new_str .. string.format("\\n") + elseif char == 13 then new_str = new_str .. string.format("\\r") + elseif char == 9 then new_str = new_str .. string.format("\\t") + elseif char == 11 then new_str = new_str .. string.format("\\v") + else + if isprint(char) then + new_str = new_str .. string.format("%c", char) + else + new_str = new_str .. string.format("\\%03d", char) + end + end + end + return new_str +end + +local function setsvalue(parsed, string) + parsed.type = ladconf.LUA_TSTRING + parsed.value = fixed_string(string) +end + +local function parse_number(bytecode, k) + sign = 1 + mantissa = string.byte(bytecode, k+6) % 16 + for i=k+5,k,-1 do mantissa = mantissa * 256 + string.byte(bytecode, i) end + if string.byte(bytecode,k+7) > 127 then sign = -1 end + exponent = (string.byte(bytecode, k+7) % 128) * 16 + math.floor(string.byte(bytecode, k+6) / 16) + if exponent == 0 then return 0 end + mantissa = (math.ldexp(mantissa, -52) + 1) * sign + return math.ldexp(mantissa, exponent - 1023) +end + +local function parse_string(bytecode, k, len) + local str = "" + for i=k+1,k+(len-1) do + local byte = string.byte(bytecode, i) + if byte ~= 0 then + str = str .. string.char(byte) + else + str = str .. '\0' + end + end + return str +end + +local function parse_constants(bytecode, k, parsed) + parsed.sizek,k = parse_int(bytecode, k, ladconf.INT) + parsed.k = {} + for i=1,parsed.sizek do + k = k + 1 + local t = string.byte(bytecode, k) + parsed.k[i] = {} + if t == ladconf.LUA_TNIL then + setnilvalue(parsed.k[i]) + elseif t == ladconf.LUA_TBOOLEAN then + k = k + 1 + setbvalue(parsed.k[i], string.byte(bytecode, k)) + elseif t == ladconf.LUA_TNUMBER then + setnvalue(parsed.k[i], parse_number(bytecode, k + 1)) + k = k + ladconf.LUA_NUMBER + elseif t == ladconf.LUA_TSTRING then + local len + len,k = parse_int(bytecode, k + 1, ladconf.SIZE_T) + setsvalue(parsed.k[i], parse_string(bytecode, k, len)) + k = k + len + end + end + return k +end + +local function parse_upvalues(bytecode, k, parsed) + parsed.sizeupvalues,k = parse_int(bytecode, k, ladconf.INT) + parsed.upvalues = {} + for i=1,parsed.sizeupvalues do + parsed.upvalues[i] = {} + parsed.upvalues[i].instack = string.byte(bytecode, k + 1) + parsed.upvalues[i].idx = string.byte(bytecode, k + 2) + k = k + 2 + end + return k +end + +local function parse_debug(bytecode, k, parsed) + local len,n + len,k = parse_int(bytecode, k, ladconf.SIZE_T) + parsed.source = parse_string(bytecode, k + 1, len) + k = k + len + parsed.sizelineinfo,k = parse_int(bytecode, k + 1, ladconf.INT) + parsed.lineinfo = {} + for i=1,parsed.sizelineinfo do + parsed.lineinfo[i],k = parse_int(bytecode, k + 1, ladconf.INT) + end + parsed.sizelocvars,k = parse_int(bytecode, k + 1, ladconf.INT) + parsed.locvars = {} + for i=1,parsed.sizelocvars do + parsed.locvars[i] = {} + len,k = parse_int(bytecode, k + 1, ladconf.SIZE_T) + parsed.locvars[i].varname = parse_string(bytecode, k, len) + k = k + len + parsed.locvars[i].startpc,k = parse_int(bytecode, k + 1, ladconf.INT) + parsed.locvars[i].endpc,k = parse_int(bytecode, k + 1, ladconf.INT) + end + n,k = parse_int(bytecode, k + 1, ladconf.INT) + for i=1,n do + len,k = parse_int(bytecode, k + 1, ladconf.SIZE_T) + parsed.upvalues[i].name = parse_string(bytecode, k, len) + k = k + len + end + return k +end + +local function parse_function(bytecode, k, parsed) + parsed.linedefined,k = parse_int(bytecode, k + 1, ladconf.INT) + parsed.lastlinedefined,k = parse_int(bytecode, k + 1, ladconf.INT) + parsed.numparams,k = string.byte(bytecode, k + 1), k + 1 + parsed.is_vararg,k = string.byte(bytecode, k + 1), k + 1 + parsed.maxstacksize,k = string.byte(bytecode, k + 1), k + 1 + k = parse_code(bytecode, k + 1, parsed) + k = parse_constants(bytecode, k + 1, parsed) + parsed.sizep,k = parse_int(bytecode, k + 1, ladconf.INT) + parsed.p = {} + for i=1,parsed.sizep do + parsed.p[i] = {} + parsed.p[i].parent = parsed.id + parsed.p[i].id = i + k = parse_function(bytecode, k, parsed.p[i]) + end + k = parse_upvalues(bytecode, k + 1, parsed) + k = parse_debug(bytecode, k + 1, parsed) + return k +end + +local function parse(bytecode) + local parsed = {} + set_auto_flags(bytecode) + parsed.id = 0 + parse_function(bytecode, ladconf.LUAC_HEADERSIZE, parsed) + return parsed +end + +local function get_opcode(i) + return ladconf.get_bit(i, ladconf.POS_OP, ladconf.SIZE_OP) + 1 +end + +local function getarg_A(i) + return ladconf.get_bit(i, ladconf.POS_A, ladconf.SIZE_A) +end + +local function getarg_B(i) + return ladconf.get_bit(i, ladconf.POS_B, ladconf.SIZE_B) +end + +local function getarg_C(i) + return ladconf.get_bit(i, ladconf.POS_C, ladconf.SIZE_C) +end + +local function getarg_Bx(i) + return ladconf.get_bit(i, ladconf.POS_Bx, ladconf.SIZE_Bx) +end + +local function getarg_Ax(i) + return ladconf.get_bit(i, ladconf.POS_Ax, ladconf.SIZE_Ax) +end + +local function getarg_sBx(i) + return getarg_Bx(i) - ladconf.MAXARG_sBx +end + +local function get_b_mode(o) + return ladconf.opmodes[o].B +end + +local function get_c_mode(o) + return ladconf.opmodes[o].C +end + +local function ISK(x) + return ladconf.ISK(x) +end + +local function INDEXK(x) + return ladconf.INDEXK(x) +end + +local function MYK(x) + return (-1-(x)) +end + +local function UPVALNAME(parsed, x) + if parsed.upvalues[x].name then + return parsed.upvalues[x].name + end + return "-" +end + +local function get_funcline(parsed, pc) + if parsed.sizelineinfo > 0 then + return parsed.lineinfo[pc] + end + return 0 +end + +local function print_header(parsed) + local function SS(x) if x == 1 then return "" else return "s" end end + local function S(x) return x,SS(x) end + + local func_name = "function" + local is_vararg = "" + + if parsed.linedefined == 0 and parsed.id == 0 then + func_name = "main" + end + if parsed.is_vararg ~= 0 then + is_vararg = "+" + end + + io.write(string.format("\n%s <%s:%d,%d> (%d instruction%s at %d)\n", + func_name, parsed.source, + parsed.linedefined, parsed.lastlinedefined, + parsed.sizecode, SS(parsed.sizecode), 0)) + io.write(string.format("%d%s param%s, %d slot%s, %d upvalue%s, ", + parsed.numparams, is_vararg, SS(parsed.numparams), + parsed.maxstacksize, SS(parsed.maxstacksize), + parsed.sizeupvalues, SS(parsed.sizeupvalues))) + io.write(string.format("%d local%s, %d constant%s, %d function%s\n", + parsed.sizelocvars, SS(parsed.sizelocvars), + parsed.sizek, SS(parsed.sizek), + parsed.sizep, SS(parsed.sizep))) +end + +local function print_string(str) + io.write(string.format('"%s"', str)) +end + +local function print_constant(parsed, i) + if not parsed.k[i] then + io.write("nil") + return + end + local t = parsed.k[i].type + local v = parsed.k[i].value + if t == ladconf.LUA_TNIL or + t == ladconf.LUA_TBOOLEAN or + t == ladconf.LUA_TNUMBER then + io.write(string.format("%s", v)) + elseif t == ladconf.LUA_TSTRING then + print_string(v) + end +end + +local function print_code(parsed) + local n = parsed.sizecode + local pc = 1 + while pc <= n do + local i = parsed.code[pc] + local o = get_opcode(i) + local a = getarg_A(i) + local b = getarg_B(i) + local c = getarg_C(i) + local ax = getarg_Ax(i) + local bx = getarg_Bx(i) + local sbx = getarg_sBx(i) + local line = get_funcline(parsed, pc) + local opcode = ladconf.OPCODE[o] + + io.write(string.format("\t%d\t", pc)) + if line > 0 then + io.write(string.format("[%d]\t", line)) + else + io.write(string.format("[-]\t")) + end + io.write(string.format("%-9s\t", opcode)) + + local opmode = ladconf.get_op_mode(o) + if opmode == ladconf.iABC then + io.write(string.format("%d", a)) + local bmode = get_b_mode(o) + local cmode = get_c_mode(o) + if bmode ~= ladconf.OpArgN then + if ISK(b) then + io.write(string.format(" %d", MYK(INDEXK(b)))) + else + io.write(string.format(" %d", b)) + end + end + if cmode ~= ladconf.OpArgN then + if ISK(c) then + io.write(string.format(" %d", MYK(INDEXK(c)))) + else + io.write(string.format(" %d", c)) + end + end + elseif opmode == ladconf.iABx then + io.write(string.format("%d", a)) + local bmode = get_b_mode(o) + if bmode == ladconf.OpArgK then + io.write(string.format(" %d", MYK(bx))) + end + if bmode == ladconf.OpArgU then + io.write(string.format(" %d", bx)) + end + elseif opmode == ladconf.iAsBx then + io.write(string.format("%d %d", a, sbx)) + elseif opmode == ladconf.iAx then + io.write(string.format("%d", MYK(ax))) + end + + if opcode == "LOADK" then + io.write(string.format("\t; ")) + print_constant(parsed, bx+1) + elseif opcode == "GETUPVAL" or + opcode == "SETUPVAL" then + io.write(string.format("\t; %s", UPVALNAME(parsed, b+1))) + elseif opcode == "GETTABUP" then + io.write(string.format("\t; %s", UPVALNAME(parsed, b+1))) + if ISK(c) then + io.write(string.format(" ")) + print_constant(parsed, INDEXK(c+1)) + end + elseif opcode == "SETTABUP" then + io.write(string.format("\t; %s", UPVALNAME(parsed, a+1))) + if ISK(b) then + io.write(string.format(" ")) + print_constant(parsed, INDEXK(b+1)) + end + if ISK(c) then + io.write(string.format(" ")) + print_constant(parsed, INDEXK(c+1)) + end + elseif opcode == "GETTABLE" or + opcode == "SELF" then + if ISK(c) then + io.write(string.format("\t; ")) + print_constant(parsed, INDEXK(c+1)) + end + elseif opcode == "SETTABLE" or + opcode == "ADD" or + opcode == "SUB" or + opcode == "MUL" or + opcode == "DIV" or + opcode == "POW" or + opcode == "EQ" or + opcode == "LT" or + opcode == "LE" then + if ISK(b) or ISK(c) then + io.write(string.format("\t; ")) + if ISK(b) then + print_constant(parsed, INDEXK(b+1)) + else + io.write(string.format("-")) + end + io.write(string.format(" ")) + if ISK(c) then + print_constant(parsed, INDEXK(c+1)) + else + io.write(string.format("-")) + end + end + elseif opcode == "JMP" or + opcode == "FORLOOP" or + opcode == "FORPREP" or + opcode == "TFORLOOP" then + io.write(string.format("\t; to %d", sbx+pc+1)) + elseif opcode == "CLOSURE" then + io.write(string.format("\t; 0x")) + elseif opcode == "SETLIST" then + if c == 0 then + pc = pc + 1 + io.write(string.format("\t; %d", parsed.code[pc])) + else + io.write(string.format("\t; %d", c)) + end + elseif opcode == "EXTRAARG" then + io.write(string.format("\t; ")) + print_constant(parsed, ax+1) + end + io.write(string.format("\n")) + pc = pc + 1 + end +end + +local function print_debug(parsed) + local n + n = parsed.sizek + io.write(string.format("constants (%d) for 0:\n", n)) + for i=1,n do + io.write(string.format("\t%d\t", i)) + print_constant(parsed, i) + io.write(string.format("\n")) + end + n = parsed.sizelocvars + io.write(string.format("locals (%d) for 0:\n", n)) + for i=1,n do + io.write(string.format("\t%d\t%s\t%d\t%d\n", i-1, + parsed.locvars[i].varname, + parsed.locvars[i].startpc+1, + parsed.locvars[i].endpc+1)) + end + n = parsed.sizeupvalues + io.write(string.format("upvalues (%d) for 0:\n", n)) + for i=1,n do + io.write(string.format("\t%d\t%s\t%d\t%d\n", i-1, + UPVALNAME(parsed, i), + parsed.upvalues[i].instack, + parsed.upvalues[i].idx)) + end +end + +local function print_function(parsed, full) + local n = parsed.sizep + print_header(parsed) + print_code(parsed) + if full then print_debug(parsed) end + for i=1,n do + print_function(parsed.p[i], full) + end +end + +local function write_header(output, parsed) + local fname + local np = parsed.numparams + if parsed.linedefined == 0 and parsed.id == 0 then + fname = "main" + else + fname = string.format("F_%s_%s", parsed.parent, parsed.id) + end + output:write(string.format("\nfunction %s(%s):\n", fname, np)) +end + +local function write_par(output, x) + output:write(string.format("%s", x)) +end + +local function write_sep(output) + output:write(string.format(", ")) +end + +local function R(x) + return "$" .. x +end + +local function UpValue(parsed, x) + return parsed.upvalues[x+1].name +end + +local function KST(parsed, x) + return parsed.k[x+1].value +end + +local function Kst(parsed, x) + local t = parsed.k[x+1].type + local v = parsed.k[x+1].value + if t == ladconf.LUA_TNIL or + t == ladconf.LUA_TBOOLEAN or + t == ladconf.LUA_TNUMBER then + return v + elseif t == ladconf.LUA_TSTRING then + return '"' .. v .. '"' + end +end + +local function RKST(parsed, x) + if ISK(x) then + return KST(parsed, INDEXK(x)) + else + return R(x) + end +end + +local function RK(parsed, x) + if ISK(x) then + return Kst(parsed, INDEXK(x)) + else + return R(x) + end +end + +local function KPROTO(parsed, x) + local parent = parsed.p[x+1].parent + local id = parsed.p[x+1].id + return string.format("F_%s_%s", parent, id) +end + +local function write_code(output, parsed) + local n = parsed.sizecode + for pc=1,n do + local i = parsed.code[pc] + local o = get_opcode(i) + local a = getarg_A(i) + local b = getarg_B(i) + local c = getarg_C(i) + local ax = getarg_Ax(i) + local bx = getarg_Bx(i) + local sbx = getarg_sBx(i) + local line = get_funcline(parsed, pc) + local opcode = ladconf.OPCODE[o] + + output:write(string.format("\t%d\t", pc)) + output:write(string.format("[%d]\t", line)) + output:write(string.format("%-9s\t", opcode)) + + if opcode == "MOVE" or + opcode == "UNM" or + opcode == "NOT" or + opcode == "LEN" then + -- R(A) R(B) + write_par(output, R(a)) + write_sep(output) + write_par(output, R(b)) + elseif opcode == "LOADK" then + -- R(A) Kst(Bx) + write_par(output, R(a)) + write_sep(output) + write_par(output, Kst(parsed, bx)) + elseif opcode == "LOADKX" then + -- R(A) + write_par(output, R(a)) + elseif opcode == "LOADBOOL" or + opcode == "NEWTABLE" or + opcode == "CALL" or + opcode == "TAILCALL" or + opcode == "SETLIST" then + -- R(A) B C + write_par(output, R(a)) + write_sep(output) + write_par(output, b) + write_sep(output) + write_par(output, c) + elseif opcode == "LOADNIL" or + opcode == "RETURN" or + opcode == "VARARG" then + -- R(A) B + write_par(output, R(a)) + write_sep(output) + write_par(output, b) + elseif opcode == "GETUPVAL" or + opcode == "SETUPVAL" then + -- R(A) UpValue(B) + write_par(output, R(a)) + write_sep(output) + write_par(output, UpValue(parsed, b)) + elseif opcode == "GETTABUP" then + -- R(A) UpValue(B) RK(C) + write_par(output, R(a)) + write_sep(output) + write_par(output, UpValue(parsed, b)) + write_sep(output) + write_par(output, RK(parsed, c)) + elseif opcode == "GETTABLE" or + opcode == "SELF" then + -- R(A) R(B) RK(C) + write_par(output, R(a)) + write_sep(output) + write_par(output, R(b)) + write_sep(output) + write_par(output, RK(parsed, c)) + elseif opcode == "SETTABUP" then + -- UpValue(A) RK(B) RK(C) + write_par(output, UpValue(parsed, a)) + write_sep(output) + write_par(output, RK(parsed, b)) + write_sep(output) + write_par(output, RK(parsed, c)) + elseif opcode == "SETTABLE" or + opcode == "ADD" or + opcode == "SUB" or + opcode == "MUL" or + opcode == "DIV" or + opcode == "MOD" or + opcode == "POW" then + -- R(A) RK(B) RK(C) + write_par(output, R(a)) + write_sep(output) + write_par(output, RK(parsed, b)) + write_sep(output) + write_par(output, RK(parsed, c)) + elseif opcode == "CONCAT" then + -- R(A) R(B) R(C) + write_par(output, R(a)) + write_sep(output) + write_par(output, R(b)) + write_sep(output) + write_par(output, R(c)) + elseif opcode == "JMP" then + -- A sBx + write_par(output, a) + write_sep(output) + write_par(output, sbx + pc + 1) + elseif opcode == "EQ" or + opcode == "LT" or + opcode == "LE" then + -- A RK(B) RK(C) + write_par(output, a) + write_sep(output) + write_par(output, RK(parsed, b)) + write_sep(output) + write_par(output, RK(parsed, c)) + elseif opcode == "TEST" or + opcode == "TFORCALL" then + -- R(A) C + write_par(output, R(a)) + write_sep(output) + write_par(output, c) + elseif opcode == "TESTSET" then + -- R(A) R(B) C + write_par(output, R(a)) + write_sep(output) + write_par(output, R(b)) + write_sep(output) + write_par(output, c) + elseif opcode == "FORLOOP" or + opcode == "FORPREP" or + opcode == "TFORLOOP" then + -- R(A) sBx + write_par(output, R(a)) + write_sep(output) + write_par(output, sbx + pc + 1) + elseif opcode == "CLOSURE" then + -- R(A) KPROTO(Bx) + write_par(output, R(a)) + write_sep(output) + write_par(output, KPROTO(parsed, bx)) + elseif opcode == "EXTRAARG" then + -- Ax + po = get_opcode(parsed.code[pc-1]) + popcode = ladconf.OPCODE[po] + if popcode == "LOADKX" then + write_par(output, Kst(parsed, ax)) + elseif popcode == "SETLIST" then + write_par(output, ax) + end + end + output:write(string.format("\n")) + end +end + +local function write_function(output, parsed) + local n = parsed.sizep + write_header(output, parsed) + write_code(output, parsed) + for i=1,n do + write_function(output, parsed.p[i]) + end +end + +local function write(filename, parsed) + local output = assert(io.open(filename, "w")) + write_function(output, parsed) + output:close() +end + +local disassembler = { + parse = parse, + write = write, + print_function = print_function, +} + +return disassembler diff --git a/5.2/doc/fat_rec.asm b/5.2/doc/fat_rec.asm new file mode 100644 index 0000000..ce93373 --- /dev/null +++ b/5.2/doc/fat_rec.asm @@ -0,0 +1,23 @@ + +function main(0): + CLOSURE $0, fat + SETTABUP _ENV, fat, $0 + GETTABUP $0, _ENV, print + GETTABUP $1, _ENV, fat + LOADK $2, 5 + CALL $1, 2, 0 + CALL $0, 0, 1 + RETURN $0, 1 + +function fat(1): + EQ 0, $0, 0 + JMP 0, label1 ; jump to label1 + LOADK $1, 1 + RETURN $1, 2 +label1: GETTABUP $1, _ENV, fat ; create label1 + SUB $2, $0, 1 + CALL $1, 2, 2 + MUL $1, $0, $1 + RETURN $1, 2 + RETURN $0, 1 + diff --git a/5.2/doc/hello.asm b/5.2/doc/hello.asm new file mode 100644 index 0000000..57c046c --- /dev/null +++ b/5.2/doc/hello.asm @@ -0,0 +1,6 @@ + +function main(0): + 1 [1] GETTABUP $0, _ENV, "print" + 2 [1] LOADK $1, "hello world!" + 3 [1] CALL $0, 2, 1 + 4 [1] RETURN $0, 1 diff --git a/5.2/doc/hello.lua b/5.2/doc/hello.lua new file mode 100644 index 0000000..a50a5fc --- /dev/null +++ b/5.2/doc/hello.lua @@ -0,0 +1 @@ +print ("hello world!") diff --git a/5.2/doc/logo1.png b/5.2/doc/logo1.png new file mode 100644 index 0000000..714be3d Binary files /dev/null and b/5.2/doc/logo1.png differ diff --git a/5.2/doc/manual.html b/5.2/doc/manual.html new file mode 100644 index 0000000..7a2f0f4 --- /dev/null +++ b/5.2/doc/manual.html @@ -0,0 +1,869 @@ + + + + +Lua Assembler/Disassembler + + + +
+ +
+

Description

+ +

Lua Assembler/Disassembler is a tool that can be used to generate Lua bytecode and Assembly code. +Lua bytecode is generated through the assembler provided, while Assembly code is generated by the disassembler, +which inspects Lua bytecode in order to reproduce it into an Assembly language. + +The assembler uses Lpeg in order to implement the parser +for our Assembly syntax, which is presented on this document. +

+ +
+ +

Requirements

+Lua Assembler/Disassembler is compatible with Lua version 5.2.0 and Lpeg version 0.10.2. +
+ +

Install

+

+To run Lua Assembler/Disassembler in your computer, first you need install the +Lua language and the +Lpeg library. +The Lua Assembler/Disassembler may be executed as an application/script and +can be used as a module in your Lua code too. +In order to install, please, download the source code, extract it and then +follow below instructions to have the applications/scripts and modules installed. +

+

Installing scripts

+

+Just copy the following files to a directory in your computer or +to your $PATH (which will depend on the operating system). +

+ +

+Since the applications/scripts use the modules to run assembler and disassembler +functions, please, do not forget to follow below steps in order to get them installed. +

+ +

Installing modules

+

+Just copy the following files to the first directory that is set in package.path. +

+ +

+You can also copy above files to other directory, if that's the case, please, do not +forget to change package.path, in your Lua code, in order to look for the modules +in the directory that you have installed them. +

+
+ +

Assembler

+

+The assembler might be used as a stand alone application/script or as a module that +you require in your code. +

+ +

Assembler script

+The stand alone application/script is called luaa.lua. + +The following synopsis should be used: + +
+lua luaa.lua [options] [filename]
+
+ +

Available options are listed below and must be separate.

+ +

-h

+

+Just prints a help message listing all available options and a brief explanation about them. +

+ +

-o file

+

+Output to file, instead of the default luaa.out. +Be careful to not overwrite precious files since you might specify the output file as an existent source file. +

+ +

Assembler module

+

+The module is called assembler and something like below line should be used in order to make +its functions available. +

+
+local assembler = require("assembler")
+
+

Available functions are listed below.

+ +

assembler.parse(contents)

+

+The function parse gets the contents of an Assembly source code as its only +argument and returns its Abstract Syntax Tree (AST). +

+

assembler.traverse(ast)

+

+The function traverse gets the AST, returned by parse function, and inspects +the AST to find errors. If no errors were found, than all information inside +the AST are combined into a table that will be used to write the correspondent +Lua bytecode. +

+

assembler.write(filename, parsed)

+

+The function write gets two parameters: the first one is the filename were +Lua bytecode should be written, while the second one is the table returned +by traverse function. +

+

assembler.print_ast(ast)

+

+The function print_ast might be used for debug purposes. It receives the AST +as its only parameters and prints it. +

+ +
+ +

Disassembler

+

+The disassembler might be used as a stand alone application/script or as a module that +you require in your code. +

+ +

Disassembler script

+The stand alone application/script is called luad.lua. + +The following synopsis should be used: + +
+lua luad.lua [options] [filename]
+
+ +

Available options are listed below and must be separate.

+ +

-h

+

+Just prints a help message listing all available options and a brief explanation about them. +

+

-l

+

+Produce a listing of the compiled bytecode for Lua's virtual machine on luac -l style. +Use -l -l for full listing. +

+

-o file

+

+Output to file, instead of the default luad.asm. +Be careful to not overwrite precious files since you might specify the output file as an existent source file. +

+ +

Disassembler module

+

+The module is called disassembler and something like below line should be used in order to make +its functions available. +

+
+local disassembler = require("disassembler")
+
+

Available functions are listed below.

+ +

disassembler.parse(bytecode)

+The function parse gets a chunk of Lua bytecode as its only +argument and returns a table containing all information that +are necessary to write its correspondent Assembly code. +

+

+

disassembler.write(filename, parsed)

+The function write gets two parameters: the first one is the filename were +the Assembly code should be written, while the second one is the table returned +by parse function. +

+

+

disassembler.print_function(parsed, full)

+

+The function print_function might be used for printing Lua bytecode in luac -l style. +It has two parameters: the first one is the table returned by parse function and +the second one is a boolean value indication if full listing is expected or not. +

+ +
+ +

How To Use

+ +

The following command line shows how to use the disassembler in order to generate the assembly code +for a Lua source file: +

+$ lua luad.lua -o hello.asm hello.lua
+
+ +The above command line outputs hello.asm which contains the assembly code for hello.lua. +

+It is important to remember that the disassembler can be used with a Lua bytecode too, as we shown below: +
+$ lua luad.lua luac.out
+
+ +Notice that in this case we haven't specified the output file, in this way the output will be the default luadd.asm. +

+

The following command line shows how to use the assembler in order to generate Lua bytecode from +our assembly code: +

+$ lua luaa.lua -o hello.out hello.asm
+
+ +The above command line outputs hello.out which contains Lua bytecode for hello.asm and can be executed using lua. +

+It is important to remember that if you do not specify the output file, then Lua bytecode will be generated as luaa.out by default. + +
+ +

Examples

+ +In this section we show some examples of our assembly language. The complete syntax can be checked +at Assembly Syntax as well as opcodes can be checked at Instructions. + +Here we show the assembly code that were generated, on above section, for hello.lua which is a simple +hello world program. + +
+function main(0):
+        1       [1]     GETTABUP        $0, _ENV, "print"
+        2       [1]     LOADK           $1, "hello world!"
+        3       [1]     CALL            $0, 2, 1
+        4       [1]     RETURN          $0, 1
+
+ +Once we use the assembler to output the Lua bytecode we can execute it using lua, as follows: + +
+$ lua hello.out
+hello world!
+$
+
+ +Now we define a recursive factorial in our Assembly language. Notice that line number and +source line number are not needed when we are writting the Assembly code. However, they are +generated when the disassembler process is performed. + +
+function main(0):
+        CLOSURE         $0, fat
+        SETTABUP        _ENV, fat, $0
+        GETTABUP        $0, _ENV, print
+        GETTABUP        $1, _ENV, fat
+        LOADK           $2, 5
+        CALL            $1, 2, 0
+        CALL            $0, 0, 1
+        RETURN          $0, 1
+
+function fat(1):
+        EQ              0, $0, 0
+        JMP             0, label1 ; jump to label1
+        LOADK           $1, 1
+        RETURN          $1, 2
+label1: GETTABUP        $1, _ENV, fat ; create label1
+        SUB             $2, $0, 1
+        CALL            $1, 2, 2
+        MUL             $1, $0, $1
+        RETURN          $1, 2
+        RETURN          $0, 1
+
+ +Notice that we have used a label in fat function since it is easier to check where to jump +when we are writing manual assembly code. +Also notice that a comment starts with a semicolon (;) and finishes at the end of the line. +A function main is necessary for all Assembly codes and the number of +parameters for the function that is being defined should be specified between +parenthesis. + +
+ +

Assembly Syntax

+ +Here is the complete syntax of Assembly in lpeg re module. + +
+  prog <- s function* !.
+  function <- header codelist
+  header <- "function" s name s "(" s n s ")" s ":" s
+  codelist <- code+
+  code <- autocode / manualcode
+  autocode <- n s ln s op s param s ("," s param s)*
+  manualcode <- (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 <- !reserved %a+
+  param <- register / number / string
+  register <- "$" n
+  number <- hex / float / int
+  string <- name / shortstr
+  shortstr <- '"' ('\\' / '\"' / !'"' .)* '"' / "'" ("\\" / "\'" / !"'" .)* "'"
+  hex <- "-"? "0" [xX] %x+
+  float <- "-"? ( (%d+ "." %d* / "." %d+) e? / %d+ e )
+  e <- [eE] [+-]? n
+  int <- "-"? n
+  s <- (space / comment)*
+  space <- %s+
+  comment <- ";" (!%nl .)*
+  reserved <- "function"
+
+
+ +

Instructions

+ +

+We have four main types of instructions, they are so called iABC, iABx , iAsBx +and iAx. The instructions should be used as follows: +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
Instruction typePattern
iABCOPCODE A, B, C
iABxOPCODE A, Bx
iAsBxOPCODE A, sBx
iAxOPCODE Ax
+
+ +

+Some iABC instructions do not use argument B or C and below we specify when that is the case. +Nonetheless, most of the iABC instructions follow the pattern described in above table. +

+ +

+It is also important to keep in mind that parameters should comply with the Assembly Syntax. +Here a some examples: +

+ +
+ + + + + + + + + + + + + + + + + + + + + + +
Parameter typeExample
Register$0, $1, $2, ...
Number0, 1, 2, ... / -1, -2, ... / 0.1, 0.2, ..., 1.0, 1.1, ... / -0.1, -0.2, ..., -1.0, -1,1, ...
NameF_0_1, blah, foo, ...
String"oi, tudo bem?", "hello world", "%s, string", ...
+
+ +

+We will use a instruction notation as follows to show how to use each opcode. +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NotationMeaning
R(A), R(B), R(C)Register specified in field A, B or C.
PCProgram Counter.
Kst(k)A constant k that will be translated to a number.
Upvalue(name)An upvalue name that will be translated to a number.
RK(B), RK(C)A field that could be a register or a constant.
sBxSignal displacement for all kinds of jumps. It can be a number or a label.
KPROTO(name)A function name that will be used to create its closure.
+
+ +

+Below we show each opcode that is available in our assembly language as well as their respective arguments. +

+ +

MOVE R(A), R(B)

+R(A) := R(B) +

+Copies the value of register R(B) into register R(A). +

+

LOADK R(A), Kst(Bx)

+R(A) := Kst(Bx) +

+Loads constant Kst(Bx) into register R(A). Constants can be numbers or strings. +

+

LOADKX R(A)

+R(A) := Kst(extra arg) +

+Loads extra arg into register R(A). +The next instruction is always EXTRAARG. +

+

LOADBOOL R(A), B, C

+R(A) := (Bool)B; if (C) pc++ +

+Loads a boolean value B (1 for TRUE or 0 for FALSE should be used as B) into register R(A). +If C is not zero then next instruction is skipped. +

+

LOADNIL R(A), B

+R(A), R(A+1), ..., R(A+B) := nil +

+Sets to nil a range of registers from register R(A) up to B. +

+

GETUPVAL R(A), UpValue(B)

+R(A) := UpValue[B] +

+Copies the value in UpValue[B] into register R(A). +

+

GETTABUP R(A), UpValue(B), RK(C)

+R(A) := UpValue[B][RK(C)] +

+Copies the value of field RK(C), from table UpValue[B], into register R(A). +Note that if you are getting globals, then UpValue(B) should be _ENV. +

+

GETTABLE R(A), R(B), RK(C)

+R(A) := R(B)[RK(C)] +

+Copies the value from a table element into register R(A). +The table is referenced by register R(B), while the index to the table is given by +RK(C), which may be register R(C) or a constant Kst(C). +

+

SETTABUP UpValue(A), RK(B), RK(C)

+UpValue[A][RK(B)] := RK(C) +

+Copies the value of RK(C) to the field RK(B) of table UpValue[A]. +Note that if you are setting globals, then UpValue(A) should be _ENV. +

+

SETUPVAL R(A), UpValue(B)

+UpValue[B] := R(A) +

+Copies the value from register R(A) into UpValue[B]. +

+

SETTABLE R(A), RK(B), RK(C)

+R(A)[RK(B)] := RK(C) +

+Copies the value from register R(C) or constant Kst(C) into a table element. +The table is referenced by register R(A), while the index to the table is given by +RK(B), which may be register R(B) or a constant Kst(B). +

+

NEWTABLE R(A), B, C

+R(A) := {} (size = B,C) +

+Creates a new empty table at register R(A). +Argument B is the size of the array part, while C is the size of the hash part. +

+

SELF R(A), R(B), RK(C)

+R(A+1) := R(B); R(A) := R(B)[RK(C)] +

+It is used for object-oriented programming using tables. +Retrieves a function reference from a table element and places it in register R(A), +then a reference to the table itself is placed in the next register R(A+1). +R(B) is the register holding the reference to the table with the method, +while the method function is found using the table index RK(C), that can be +a register R(C) or a constant Kst(C). +

+

ADD R(A), RK(B), RK(C)

+R(A) := RK(B) + RK(C) +

+Adds RK(B) and RK(C) and holds the result into register R(A). +Both RK(B) and RK(C) may be either registers or constants. +

+

SUB R(A), RK(B), RK(C)

+R(A) := RK(B) - RK(C) +

+Subtracts RK(B) and RK(C) and holds the result into register R(A). +Both RK(B) and RK(C) may be either registers or constants. +

+

MUL R(A), RK(B), RK(C)

+R(A) := RK(B) * RK(C) +

+Multiplies RK(B) and RK(C) and holds the result into register R(A). +Both RK(B) and RK(C) may be either registers or constants. +

+

DIV R(A), RK(B), RK(C)

+R(A) := RK(B) / RK(C) +

+Divides RK(B) and RK(C) and holds the result into register R(A). +Both RK(B) and RK(C) may be either registers or constants. +

+

MOD R(A), RK(B), RK(C)

+R(A) := RK(B) % RK(C) +

+Performs modulus between RK(B) and RK(C) and holds the result into register R(A). +Both RK(B) and RK(C) may be either registers or constants. +

+

POW R(A), RK(B), RK(C)

+R(A) := RK(B) ^ RK(C) +

+Performs exponentiation between RK(B) and RK(C) and holds the result into register R(A). +Both RK(B) and RK(C) may be either registers or constants. +

+

UNM R(A), R(B)

+R(A) := -R(B) +

+Performs unary minus where register R(B) is negated and the value is placed in register R(A). +

+

NOT R(A), R(B)

+R(A) := not R(B) +

+Applies a boolean not to the value in register R(B) and holds the result in register R(A). +

+

LEN R(A), R(B)

+R(A) := length of R(B) +

+Returns the length of object in register R(B) and holds the result in register R(A). +

+

CONCAT R(A), R(B), R(C)

+R(A) := R(B).. ... ..R(C) +

+Performs the concatenation among two or more strings. The start register is R(B) and the +final register is R(C), meaning that R(C) should always be greater then R(B). +The result is stored in register R(A). +

+

JMP A, sBx

+pc+=sBx; if (A) close all upvalues >= R(A) + 1 +

+Performs a jump to sBx, which should be an instruction number or a label that should jump to. +If A is true then all upvalues up to R(A) + 1 are closed. +

+

EQ A, RK(B), RK(C)

+if ((RK(B) == RK(C)) ~= A) then pc++ +

+Performs a equality test between RK(B) and RK(C), wich may be registers or constants. +If the boolean is not A then next instruction is skipped. +

+

LT A, RK(B), RK(C)

+if ((RK(B) < RK(C)) ~= A) then pc++ +

+Performs a less than test between RK(B) and RK(C), wich may be registers or constants. +If the boolean is not A then next instruction is skipped. +

+

LE A, RK(B), RK(C)

+if ((RK(B) <= RK(C)) ~= A) then pc++ +

+Performs a less than or equal to test between RK(B) and RK(C), wich may be registers or constants. +If the boolean is not A then next instruction is skipped. +

+

TEST R(A), C

+if not (R(A) <=> C) then pc++ +

+Can be used to implement and/or logical operators, or for testing +a single register in a conditional statement. +TEST should be used when an assignment operation is not needed and works same way as TESTSET. +For more details, please, look at TESTSET. +

+

TESTSET R(A), R(B), C

+if (R(B) <=> C) then R(A) := R(B) else pc++ +

+Also can be used to implement and/or logical operators, or for testing +a single register in a conditional statement. +Register R(B) is coerced into a boolean and compared to the boolean field C. +If R(B) matches C then next instruction is skipped, otherwise R(B) is assigned to R(A). +

+

CALL R(A), B, C

+R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) +

+Performs a function call. R(A) holds the reference to the function object to be called. +Parameters to the function should be placed in the registers following R(A). +

+If B is 1 the function has no parameters. If B is 2 or more there are B-1 parameters. +If B is 0 the function parameters range from R(A+1) to the top of the stack. +

+Results returned by the function call are placed in a range of registers starting from R(A). +If C is 1 no return results. If C is 2 or more there will be C-1 results saved. +If C is 0 then multiple return results are saved. +

+

TAILCALL R(A), B, C

+return R(A)(R(A+1), ... ,R(A+B-1)) +

+Performs a tail call which happens when a return statement has a single function call as the expression. +Exactly like CALL, register R(A) is the reference to the function object to be called, +while B encodes the number of paramenters. However, even tough C is not used by TAILCALL, 0 should be +used to denote multiple return results. +

+

RETURN R(A), B

+return R(A), ... ,R(A+B-2) +

+Returns to the calling function with options return values. +

+If B is 1 there are no return values. If B is 2 or more, there are B-1 return values. +If B is 0 the set of values from R(A) to the top of the stack is returned. +

+

FORLOOP R(A) sBx

+R(A)+=R(A+2) ; if R(A) <?= R(A+1) then pc+=sBx; R(A+3)=R(A) +

+Should be used to perform an iteration of a numeric for loop. +A numeric for loop requires 4 registers on the stack where R(A) hold the initial value, +R(A+1) is the limit, R(A+2) is the stepping value and R(A+3) is the actual loop variable +that is local to the for block. +The argument sBx should be an instruction number or a label that should jump unconditionally to. +

+

FORPREP R(A), sBx

+R(A)-=R(A+2); pc+=sBx +

+Should be used to initialize a numeric for loop. +The argument sBx should be an instruction number or a label that should jump back to the loop body. +

+

TFORCALL R(A), C

+R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)) +

+Should be used to initialize a generic for loop, where +R(A) is the iterator function, R(A+1) is the state and R(A+2) is the enumeration index. +The loop variables are specified at locations R(A+3) and their count is defined by operand C, +which should be at least 1. +

+

TFORLOOP R(A), sBx

+if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx } +

+Should be used to perform an iteration of a generic for loop. +If register R(A+1) is not nil then it is stored in R(A) and jump back to sBx. +The argument sBx should be an instruction number or a label that should jump to. +

+

SETLIST R(A), B, C

+R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B +

+Sets the values for a range of arrays elements in a table referenced by R(A), +argument B is the number of elements to set and argument C is the number of blocks +to be initialized. +If B is zero then B is the top. If C is zero then next instruction should be +EXTRAARG(real C). +

+

CLOSURE R(A), KPROTO(Bx)

+R(A) := closure(KPROTO[Bx]) +

+Should be used to create an instance of a closure of a function where KPROTO[Bx] is the +function name and R(A) is the register that assigns the reference to the instantiated +function object. +

+

VARARG R(A), B

+R(A), R(A+1), ..., R(A+B-2) = vararg +

+Copies B-2 parameters into a number of registers starting from R(A). +If B is 0, VARARG copies as many values as it can based on the number of parameters passed. +If a fixed number of values is required, B is a value greater than 1. +If any number of values is required then B is 0. +

+

EXTRAARG Ax

+

+Sets an extra larger argument for previous opcode. +If previous opcode is LOADKX then Ax might be any constant, but +if previous opcode is SETLIST then Ax must be an integer. +

+ +
+ +

Notes

+

+The current version of Lua Assembler/Disassembler is 0.2. +

+
+ +

Licence

+

+Copyright © 2012 Andre Murbach Maidl. +

+

+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +

+

+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +

+

+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +

+ +
+ +
+ + diff --git a/5.2/ladconf.lua b/5.2/ladconf.lua new file mode 100644 index 0000000..0a923a8 --- /dev/null +++ b/5.2/ladconf.lua @@ -0,0 +1,230 @@ +local OPCODE = { "MOVE", "LOADK", "LOADKX", "LOADBOOL", "LOADNIL", + "GETUPVAL", "GETTABUP", "GETTABLE", "SETTABUP", + "SETUPVAL", "SETTABLE", "NEWTABLE", "SELF", + "ADD", "SUB", "MUL", "DIV", "MOD", "POW", + "UNM", "NOT", "LEN", "CONCAT", "JMP", + "EQ", "LT", "LE", "TEST", "TESTSET", + "CALL", "TAILCALL", "RETURN", "FORLOOP", + "FORPREP", "TFORCALL", "TFORLOOP", "SETLIST", + "CLOSURE", "VARARG", "EXTRAARG" } + +local LUA_ENV = "_ENV" +local LUA_SOURCE = "@Lua Assembler/Disassembler" + +local LUA_SIGNATURE = "\27Lua" +local LUAC_TAIL = "\x19\x93\r\n\x1a\n" +local LUAC_HEADERSIZE = string.len(LUA_SIGNATURE) + 2 + 6 + string.len(LUAC_TAIL) +local LUA_VERSION = 82 +local LUA_FORMAT = 0 + +local ENDIANNESS = 1 +local INT = 4 +local SIZE_T = 8 +local INSTRUCTION = 4 +local LUA_NUMBER = 8 +local INTEGRAL = 0 + +local LUA_TNIL = 0 +local LUA_TBOOLEAN = 1 +local LUA_TNUMBER = 3 +local LUA_TSTRING = 4 + +local iABC = 0 +local iABx = 1 +local iAsBx = 2 +local iAx = 3 + +local SIZE_C = 9 +local SIZE_B = 9 +local SIZE_Bx = (SIZE_C + SIZE_B) +local SIZE_A = 8 +local SIZE_Ax = (SIZE_C + SIZE_B + SIZE_A) + +local SIZE_OP = 6 + +local POS_OP = 0 +local POS_A = (POS_OP + SIZE_OP) +local POS_C = (POS_A + SIZE_A) +local POS_B = (POS_C + SIZE_C) +local POS_Bx = POS_C +local POS_Ax = POS_A + +local MASK_OP = math.ldexp(1, SIZE_OP) +local MASK_A = math.ldexp(1, SIZE_A) +local MASK_B = math.ldexp(1, SIZE_B) +local MASK_C = math.ldexp(1, SIZE_C) +local MASK_Bx = math.ldexp(1, SIZE_Bx) +local MASK_Ax = math.ldexp(1, SIZE_Ax) + +local ABC = { SIZE_OP, SIZE_A, SIZE_C, SIZE_B } + +local INT_MAX = (2 ^ ((SIZE_T * INT) - 1)) - 1 +local LUAI_BITSINT +if INT_MAX-20 < 32760 then + LUAI_BITSINT = 16 +elseif INT_MAX > 2147483640 then + LUAI_BITSINT = 32 +else + error ("you must define LUAI_BITSINT with number of bits in an integer") +end + +local MAX_INT = INT_MAX - 2 + +local MAXARG_Bx, MAXARG_sBx, MAXARG_Ax + +if SIZE_Bx < LUAI_BITSINT-1 then + MAXARG_Bx = bit32.lshift(1,SIZE_Bx)-1 + MAXARG_sBx = bit32.rshift(MAXARG_Bx,1) +else + MAXARG_Bx = MAX_INT + MAXARG_sBx = MAX_INT +end + +if SIZE_Ax < LUAI_BITSINT-1 then + MAXARG_Ax = bit32.lshift(1,SIZE_Ax)-1 +else + MAXARG_Ax = MAX_INT +end + +local MAXARG_A = bit32.lshift(1,SIZE_A)-1 +local MAXARG_B = bit32.lshift(1,SIZE_B)-1 +local MAXARG_C = bit32.lshift(1,SIZE_C)-1 + +local BITRK = bit32.lshift(1, (SIZE_B - 1)) + +local function ISK(x) + return bit32.btest(bit32.band(x, BITRK)) +end + +local function INDEXK(r) + return bit32.band(r, bit32.bnot(BITRK)) +end + +local MAXINDEXRK = BITRK - 1 + +local function RKASK(x) + return bit32.bor(x, BITRK) +end + +local function get_bit(x,a,b) + return (math.floor((x / 2^a) % 2^b)) +end + +local OpArgN = 0 +local OpArgU = 1 +local OpArgR = 2 +local OpArgK = 3 + +local opmodes = { + { T = 0, A = 1, B = OpArgR, C = OpArgN, mode = iABC }, -- MOVE + { T = 0, A = 1, B = OpArgK, C = OpArgN, mode = iABx }, -- OP_LOADK + { T = 0, A = 1, B = OpArgN, C = OpArgN, mode = iABx }, -- OP_LOADKX + { T = 0, A = 1, B = OpArgU, C = OpArgU, mode = iABC }, -- OP_LOADBOOL + { T = 0, A = 1, B = OpArgU, C = OpArgN, mode = iABC }, -- OP_LOADNIL + { T = 0, A = 1, B = OpArgU, C = OpArgN, mode = iABC }, -- OP_GETUPVAL + { T = 0, A = 1, B = OpArgU, C = OpArgK, mode = iABC }, -- OP_GETTABUP + { T = 0, A = 1, B = OpArgR, C = OpArgK, mode = iABC }, -- OP_GETTABLE + { T = 0, A = 0, B = OpArgK, C = OpArgK, mode = iABC }, -- OP_SETTABUP + { T = 0, A = 0, B = OpArgU, C = OpArgN, mode = iABC }, -- OP_SETUPVAL + { T = 0, A = 0, B = OpArgK, C = OpArgK, mode = iABC }, -- OP_SETTABLE + { T = 0, A = 1, B = OpArgU, C = OpArgU, mode = iABC }, -- OP_NEWTABLE + { T = 0, A = 1, B = OpArgR, C = OpArgK, mode = iABC }, -- OP_SELF + { T = 0, A = 1, B = OpArgK, C = OpArgK, mode = iABC }, -- OP_ADD + { T = 0, A = 1, B = OpArgK, C = OpArgK, mode = iABC }, -- OP_SUB + { T = 0, A = 1, B = OpArgK, C = OpArgK, mode = iABC }, -- OP_MUL + { T = 0, A = 1, B = OpArgK, C = OpArgK, mode = iABC }, -- OP_DIV + { T = 0, A = 1, B = OpArgK, C = OpArgK, mode = iABC }, -- OP_MOD + { T = 0, A = 1, B = OpArgK, C = OpArgK, mode = iABC }, -- OP_POW + { T = 0, A = 1, B = OpArgR, C = OpArgN, mode = iABC }, -- OP_UNM + { T = 0, A = 1, B = OpArgR, C = OpArgN, mode = iABC }, -- OP_NOT + { T = 0, A = 1, B = OpArgR, C = OpArgN, mode = iABC }, -- OP_LEN + { T = 0, A = 1, B = OpArgR, C = OpArgR, mode = iABC }, -- OP_CONCAT + { T = 0, A = 0, B = OpArgR, C = OpArgN, mode = iAsBx }, -- OP_JMP + { T = 1, A = 0, B = OpArgK, C = OpArgK, mode = iABC }, -- OP_EQ + { T = 1, A = 0, B = OpArgK, C = OpArgK, mode = iABC }, -- OP_LT + { T = 1, A = 0, B = OpArgK, C = OpArgK, mode = iABC }, -- OP_LE + { T = 1, A = 0, B = OpArgN, C = OpArgU, mode = iABC }, -- OP_TEST + { T = 1, A = 1, B = OpArgR, C = OpArgU, mode = iABC }, -- OP_TESTSET + { T = 0, A = 1, B = OpArgU, C = OpArgU, mode = iABC }, -- OP_CALL + { T = 0, A = 1, B = OpArgU, C = OpArgU, mode = iABC }, -- OP_TAILCALL + { T = 0, A = 0, B = OpArgU, C = OpArgN, mode = iABC }, -- OP_RETURN + { T = 0, A = 1, B = OpArgR, C = OpArgN, mode = iAsBx }, -- OP_FORLOOP + { T = 0, A = 1, B = OpArgR, C = OpArgN, mode = iAsBx }, -- OP_FORPREP + { T = 0, A = 0, B = OpArgN, C = OpArgU, mode = iABC }, -- OP_TFORCALL + { T = 0, A = 1, B = OpArgR, C = OpArgN, mode = iAsBx }, -- OP_TFORLOOP + { T = 0, A = 0, B = OpArgU, C = OpArgU, mode = iABC }, -- OP_SETLIST + { T = 0, A = 1, B = OpArgU, C = OpArgN, mode = iABx }, -- OP_CLOSURE + { T = 0, A = 1, B = OpArgU, C = OpArgN, mode = iABC }, -- OP_VARARG + { T = 0, A = 0, B = OpArgU, C = OpArgU, mode = iAx }, -- OP_EXTRAARG +} + +local function get_op_mode(o) + return opmodes[o].mode +end + +local ladconf = { + OPCODE = OPCODE, + LUA_ENV = LUA_ENV, + LUA_SOURCE = LUA_SOURCE, + LUA_SIGNATURE = LUA_SIGNATURE, + LUAC_TAIL = LUAC_TAIL, + LUAC_HEADERSIZE = LUAC_HEADERSIZE, + LUA_VERSION = LUA_VERSION, + LUA_FORMAT = LUA_FORMAT, + ENDIANNESS = ENDIANNESS, + INT = INT, + SIZE_T = SIZE_T, + INSTRUCTION = INSTRUCTION, + LUA_NUMBER = LUA_NUMBER, + INTEGRAL = INTEGRAL, + LUA_TNIL = LUA_TNIL, + LUA_TBOOLEAN = LUA_TBOOLEAN, + LUA_TNUMBER = LUA_TNUMBER, + LUA_TSTRING = LUA_TSTRING, + iABC = iABC, + iABx = iABx, + iAsBx = iAsBx, + iAx = iAx, + SIZE_C = SIZE_C, + SIZE_B = SIZE_B, + SIZE_Bx = SIZE_Bx, + SIZE_A = SIZE_A, + SIZE_Ax = SIZE_Ax, + SIZE_OP = SIZE_OP, + POS_OP = POS_OP, + POS_A = POS_A, + POS_C = POS_C, + POS_B = POS_B, + POS_Bx = POS_Bx, + POS_Ax = POS_Ax, + MASK_OP = MASK_OP, + MASK_A = MASK_A, + MASK_B = MASK_B, + MASK_C = MASK_C, + MASK_Bx = MASK_Bx, + MASK_Ax = MASK_Ax, + ABC = ABC, + INT_MAX = INT_MAX, + LUAI_BITSINT = LUAI_BITSINT, + MAX_INT = MAX_INT, + MAXARG_Bx = MAXARG_Bx, + MAXARG_sBx = MAXARG_sBx, + MAXARG_Ax = MAXARG_Ax, + MAXARG_A = MAXARG_A, + MAXARG_B = MAXARG_B, + MAXARG_C = MAXARG_C, + BITRK = BITRK, + ISK = ISK, + INDEXK = INDEXK, + MAXINDEXRK = MAXINDEXRK, + RKASK = RKASK, + get_bit = get_bit, + OpArgN = OpArgN, + OpArgU = OpArgU, + OpArgR = OpArgR, + OpArgK = OpArgK, + opmodes = opmodes, + get_op_mode = get_op_mode, +} + +return ladconf diff --git a/5.2/luaa.lua b/5.2/luaa.lua new file mode 100755 index 0000000..37e20fb --- /dev/null +++ b/5.2/luaa.lua @@ -0,0 +1,72 @@ +#!/usr/bin/env lua + +--[[ +Lua Assembler for Lua 5.2.0 +Version 0.2 +Author: Andre Murbach Maidl +]] + +INPUT = "luad.asm" +OUTPUT = "luaa.out" +DEBUG = false + +USAGE = [[ +usage: %s [options] [filename] +Available options are: +-h print this help +-o name output to file name (default is %s) +]] + +function usage(msg) + if msg ~= nil then + io.write(string.format("%s: %s\n", arg[0], msg)) + end + io.write(string.format(USAGE, arg[0], OUTPUT)) + os.exit(1) +end + +function doargs() + local i = 1 + while i <= #arg do + if string.find(arg[i], "^-") == nil then + INPUT = arg[i] + break + elseif arg[i] == "-h" then usage() + elseif arg[i] == "-o" then i = i + 1 + if arg[i] == nil then usage("'-o' needs argument") else OUTPUT = arg[i] end + else usage(string.format("'%s' unkown option", arg[i])) + end + i = i + 1 + end +end + +function read_input(filename) + local input = assert(io.open(filename, "r")) + local contents = input:read("*a") + input:close() + return contents +end + +if #arg < 1 then + usage ("no input file given") +end + +doargs() + +local assembler = require("assembler") +local contents = read_input(INPUT) +local ast = assembler.parse(contents) + +if not ast then + print ("syntax error") + os.exit (1) +end + +if DEBUG then assembler.print_ast(ast) end +local parsed = assembler.traverse(ast) +if parsed == nil then + error ("could not generate bytecode") +end +assembler.write(OUTPUT, parsed) + +os.exit(0) diff --git a/5.2/luad.lua b/5.2/luad.lua new file mode 100755 index 0000000..91f40cd --- /dev/null +++ b/5.2/luad.lua @@ -0,0 +1,62 @@ +#!/usr/bin/env lua + +--[[ +Lua Disassembler for Lua 5.2.0 +Version 0.2 +Author: Andre Murbach Maidl +]] + +INPUT = "luac.out" +OUTPUT = "luad.asm" +LISTING = 0 +DUMPING = true + +USAGE = [[ +usage: %s [options] [filename] +Available options are: +-h print this help +-l list on luac style (use -l -l for full listing) +-o name output to file name (default is %s) +-p parse only +]] + +function usage(msg) + if msg ~= nil then + io.write(string.format("%s: %s\n", arg[0], msg)) + end + io.write(string.format(USAGE, arg[0], OUTPUT)) + os.exit(1) +end + +function doargs() + local i = 1 + while i <= #arg do + if string.find(arg[i], "^-") == nil then + INPUT = arg[i] + break + elseif arg[i] == "-h" then usage() + elseif arg[i] == "-l" then LISTING = LISTING + 1 + elseif arg[i] == "-o" then i = i + 1 + if arg[i] == nil then usage("'-o' needs argument") else OUTPUT = arg[i] end + elseif arg[i] == "-p" then DUMPING = false + else usage(string.format("'%s' unkown option", arg[i])) + end + i = i + 1 + end +end + +if #arg < 1 then + usage ("no input file given") +end + +doargs() + +local chunk = assert(loadfile(INPUT)) +local bytecode = string.dump(chunk) +local disassembler = require("disassembler") + +local parsed = disassembler.parse(bytecode) +if LISTING > 0 then disassembler.print_function(parsed, LISTING > 1) end +if DUMPING then disassembler.write(OUTPUT, parsed) end + +os.exit(0)