1
0
forked from GitHub/lad
Files
lad/5.1/disassembler.lua
T
2013-08-04 10:40:15 -03:00

1017 lines
30 KiB
Lua

--[[
Lua Disassembler
Version 0.1
Author: Andre Murbach Maidl
]]
-- usage message
USAGE = [[
usage: %s [options] [filename]
Available options are:
-h print this help
-l list on luac style
-o name output to file name (default is %s)
]]
-- global settings
config = {}
config.SIGNATURE = "\27Lua"
config.VERSION = 81
config.FORMAT = 0
config.ENDIANNESS = 1
config.INT = 4
config.SIZE_T = 8
config.INSTRUCTION = 4
config.LUA_NUMBER = 8
config.INTEGRAL = 0
config.BLOCK = 4
config.HEADERBLOCK = 12
config.SIZE_C = 9
config.SIZE_B = 9
config.SIZE_Bx = config.SIZE_C + config.SIZE_B
config.SIZE_A = 8
config.SIZE_OP = 6
config.POS_OP = 0
config.POS_A = config.POS_OP + config.SIZE_OP
config.POS_C = config.POS_A + config.SIZE_A
config.POS_B = config.POS_C + config.SIZE_C
config.POS_Bx = config.POS_C
-- global types
types = {}
types.LUA_TNIL = 0
types.LUA_TBOOLEAN = 1
types.LUA_TNUMBER = 3
types.LUA_TSTRING = 4
-- opcodes
opcode = {}
opcode = { "MOVE", "LOADK", "LOADBOOL", "LOADNIL",
"GETUPVAL", "GETGLOBAL", "GETTABLE",
"SETGLOBAL", "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", "TFORLOOP", "SETLIST", "CLOSE",
"CLOSURE", "VARARG" }
-- opcode modes
opmode = {}
opmode.iABC = 0
opmode.iABx = 1
opmode.iAsBx = 2
-- opcode masks
opargmask = {}
opargmask.OpArgN = 0
opargmask.OpArgU = 1
opargmask.OpArgR = 2
opargmask.OpArgK = 3
-- global options
options = {}
options.INPUT = nil
options.OUTPUT = "d.asm"
options.LISTING = false
options.TMP = "luac_tmp.out"
-- table to store parsed data
parsed = {}
-- prints usage message
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], options.OUTPUT))
os.exit(1)
end
-- parse arguments
function doargs()
local i = 1
while i <= #arg do
if string.find(arg[i], "^-") == nil then
options.INPUT = arg[i]
break
elseif arg[i] == "-h" then usage()
elseif arg[i] == "-l" then
options.LISTING = true
elseif arg[i] == "-o" then
i = i + 1
if arg[i] == nil then usage("'-o' needs argument") else options.OUTPUT = arg[i] end
else usage(string.format("'%s' unkown option", arg[i]))
end
i = i + 1
end
end
-- print a table - debug only
function print_table(table)
for k,p in pairs(table) do print (k,p) end
end
-- print a chunk - debug only
function print_chunk(binary_chunk)
for b in string.gfind(binary_chunk, ".") do
io.write(string.format("%d ", string.byte(b)))
end
io.write(string.rep(" ", config.BLOCK - string.len(binary_chunk) + 1))
io.write(string.gsub(binary_chunk, "%c", "."), "\n")
end
-- print what still needs to be parsed - debug only
function print_rest(file)
while true do
binary_chunk = file:read(config.BLOCK)
if not binary_chunk then break else print_chunk(binary_chunk) end
end
end
-- get a bit from an hex string
function get_bit(x,a,b)
return (math.floor((x / 2^a) % 2^b))
end
-- generate an hex string
function get_hex_string(x)
str = "0x"
if config.ENDIANNESS == 1 then
for i=config.BLOCK,1,-1 do
str = str .. string.format("%02x", string.byte(x,i))
end
else
for i=1,config.BLOCK do
str = str .. string.format("%02x", string.byte(x,i))
end
end
return str
end
-- get an int from a chunk
function get_int(binary_chunk)
i = 0
if config.ENDIANNESS == 1 then
for p=config.INT,2,-1 do
i = i + (256 * string.byte(binary_chunk,p))
end
i = i + string.byte(binary_chunk,1)
else
for p=1,config.INT-1 do
i = i + (256 * string.byte(binary_chunk,p))
end
i = i + string.byte(binary_chunk,config.INT)
end
return i
end
-- revert chunk
function revert(x)
reverted = ""
for k=config.LUA_NUMBER,1,-1 do
reverted = reverted .. string.format("%c", string.byte(x,k))
end
return reverted
end
-- convert little endian number to float
function convert_little_endian(x)
sign = 1
mantissa = string.byte(x, 7) % 16
for i=6,1,-1 do mantissa = mantissa * 256 + string.byte(x,i) end
if string.byte(x,8) > 127 then sign = -1 end
exponent = (string.byte(x,8) % 128) * 16 + math.floor(string.byte(x,7) / 16)
if exponent == 0 then return 0 end
mantissa = (math.ldexp(mantissa, -52) + 1) * sign
return math.ldexp(mantissa, exponent - 1023)
end
-- convert big endian number to float
function convert_big_endian(x)
sign = 1
mantissa = string.byte(x, 2) % 16
for i=3,config.LUA_NUMBER do mantissa = mantissa * 256 + string.byte(x,i) end
if string.byte(x,1) > 127 then sign = -1 end
exponent = (string.byte(x,1) % 128) * 16 + math.floor(string.byte(x,2) / 16)
if exponent == 0 then return 0 end
mantissa = (math.ldexp(mantissa, -52) + 1) * sign
return math.ldexp(mantissa, exponent - 1023)
end
-- convert a number
function convert_number(x)
if config.ENDIANNESS == 1 then
return convert_little_endian(x)
end
return convert_big_endian(x)
end
-- get string length
function get_str_len(binary_chunk)
if config.ENDIANNESS == 1 then
i = string.byte(binary_chunk,1)
for p=2,config.LUA_NUMBER do
i = i + (256 * string.byte(binary_chunk,p))
end
else
i = string.byte(binary_chunk,config.INT)
for p=config.INT-1,1,-1 do
i = i + (256 * string.byte(binary_chunk,p))
end
end
return i
end
-- get boolean value
function get_boolean(binary_chunk)
if string.byte(binary_chunk,1) == 0 then return "false" end
return "true"
end
-- check if is a control char
function iscntrl(x)
if (x >= 0 and x <= 31) or (x == 127) then return true end
return false
end
-- check if is a printable char
function isprint(x)
return not iscntrl(x)
end
-- load header from file
function load_header(file)
return file:read(config.HEADERBLOCK)
end
-- check Lua signature
function check_signature(header_chunk)
if (string.sub(header_chunk, 1, string.len(config.SIGNATURE)) == config.SIGNATURE) then
return true
end
return false
end
-- check Lua version for 5.1
function check_version(header_chunk)
if (string.byte(header_chunk,5) == config.VERSION) then
return true
end
return false
end
-- set flags detected from file
function set_auto_flags(header_chunk)
config.ENDIANNESS = string.byte(header_chunk,7)
config.INT = string.byte(header_chunk,8)
config.SIZE_T = string.byte(header_chunk,9)
config.INSTRUCTION = string.byte(header_chunk,10)
config.LUA_NUMBER = string.byte(header_chunk,11)
config.INGETRAL = string.byte(header_chunk,12)
end
-- get a string based on its length
function get_string(file, str_len)
binary_chunk = file:read(str_len)
local str = ""
for i=1,str_len do
local byte = string.byte(binary_chunk, i)
if byte ~= 0 then
str = str .. string.format("%c", string.byte(binary_chunk,i))
else
str = str .. '\0'
end
end
return str
end
-- get source name from file
function get_source_name(file)
binary_chunk = file:read(config.SIZE_T)
str_len = get_str_len(binary_chunk)
return get_string(file, str_len)
end
-- get line defined from file
function get_line_defined(file, parsed)
binary_chunk = file:read(config.BLOCK)
parsed["line_defined"] = get_int(binary_chunk)
end
-- get last line defined from file
function get_last_line_defined(file, parsed)
binary_chunk = file:read(config.BLOCK)
parsed["last_line_defined"] = get_int(binary_chunk)
end
-- get function values from file
function get_function_values(file, parsed)
binary_chunk = file:read(config.BLOCK)
parsed["number_of_upvalues"] = string.byte(binary_chunk,1)
parsed["number_of_parameters"] = string.byte(binary_chunk,2)
parsed["is_vararg"] = string.byte(binary_chunk,3)
parsed["max_stack_size"] = string.byte(binary_chunk,4)
end
-- get instruction from file
function get_instruction(file, parsed, n)
if not parsed["instruction"] then parsed["instruction"] = {} end
binary_chunk = file:read(config.BLOCK)
parsed["instruction"][n] = get_hex_string(binary_chunk)
end
-- get all instructions from file
function get_instructions(file, parsed)
binary_chunk = file:read(config.BLOCK)
parsed["number_of_instructions"] = get_int(binary_chunk)
for n=1,parsed["number_of_instructions"] do
get_instruction(file, parsed, n)
end
end
-- get a constant from file
function get_constant(file, parsed, n)
if not parsed["constant"] then parsed["constant"] = {} end
if not parsed["constant"][n] then parsed["constant"][n] = {} end
binary_chunk = file:read(1)
c_type = string.byte(binary_chunk,1)
if c_type == types.LUA_TNIL then
parsed["constant"][n]["c_type"] = types.LUA_TNIL
parsed["constant"][n]["value"] = "nil"
elseif c_type == types.LUA_TBOOLEAN then
parsed["constant"][n]["c_type"] = types.LUA_TBOOLEAN
binary_chunk = file:read(1)
parsed["constant"][n]["value"] = get_boolean(binary_chunk)
elseif c_type == types.LUA_TNUMBER then
parsed["constant"][n]["c_type"] = types.LUA_TNUMBER
binary_chunk = file:read(config.LUA_NUMBER)
parsed["constant"][n]["value"] = convert_number(binary_chunk)
elseif c_type == types.LUA_TSTRING then
parsed["constant"][n]["c_type"] = types.LUA_TSTRING
binary_chunk = file:read(config.SIZE_T)
parsed["constant"][n]["str_len"] = get_str_len(binary_chunk)
parsed["constant"][n]["value"] = get_string(file, parsed["constant"][n]["str_len"])
end
end
-- get all constants from file
function get_constants(file, parsed)
binary_chunk = file:read(config.BLOCK)
parsed["number_of_constants"] = get_int(binary_chunk)
for n=1,parsed["number_of_constants"] do
get_constant(file, parsed, n)
end
end
-- get number of functions from file
function get_number_of_functions(file, parsed)
binary_chunk = file:read(config.BLOCK)
parsed["number_of_functions"] = get_int(binary_chunk)
end
-- get source line positions from file
function get_source_line_position(file, parsed, n)
if not parsed["instruction_line"] then parsed["instruction_line"] = {} end
binary_chunk = file:read(config.BLOCK)
parsed["instruction_line"][n] = get_int(binary_chunk)
end
-- get all source line positions from file
function get_source_line_positions(file, parsed)
binary_chunk = file:read(config.BLOCK)
parsed["number_of_instruction_lines"] = get_int(binary_chunk)
for n=1,parsed["number_of_instruction_lines"] do
get_source_line_position(file, parsed, n)
end
end
-- get a local variable from file
function get_local(file, parsed, n)
if not parsed["local"] then parsed["local"] = {} end
if not parsed["local"][n] then parsed["local"][n] = {} end
binary_chunk = file:read(config.SIZE_T)
parsed["local"][n]["str_len"] = get_str_len(binary_chunk)
parsed["local"][n]["varname"] = get_string(file, parsed["local"][n]["str_len"])
binary_chunk = file:read(config.BLOCK)
parsed["local"][n]["startpc"] = string.byte(binary_chunk,1)
binary_chunk = file:read(config.BLOCK)
parsed["local"][n]["endpc"] = string.byte(binary_chunk,1)
end
-- get all local variables from file
function get_locals(file, parsed)
binary_chunk = file:read(config.BLOCK)
parsed["number_of_locals"] = get_int(binary_chunk)
for n=1,parsed["number_of_locals"] do
get_local(file, parsed, n)
end
end
-- get an upvalue from file
function get_upvalue(file, parsed, n)
if not parsed["upvalue"] then parsed["upvalue"] = {} end
if not parsed["upvalue"][n] then parsed["upvalue"][n] = {} end
binary_chunk = file:read(config.SIZE_T)
parsed["upvalue"][n]["str_len"] = get_str_len(binary_chunk)
parsed["upvalue"][n]["value"] = get_string(file, parsed["upvalue"][n]["str_len"])
end
-- get all upvalues from file
function get_upvalues(file, parsed)
binary_chunk = file:read(config.BLOCK)
parsed["size_of_upvalues"] = get_int(binary_chunk)
for n=1,parsed["size_of_upvalues"] do
get_upvalue(file, parsed, n)
end
end
-- get function block from file
function get_function_block(file, parsed, source_name, parent)
parsed["source_name"] = source_name
get_line_defined(file, parsed)
get_last_line_defined(file, parsed)
get_function_values(file, parsed)
get_instructions(file, parsed)
get_constants(file, parsed)
get_number_of_functions(file, parsed)
for n=1,parsed["number_of_functions"] do
binary_chunk = file:read(config.SIZE_T)
if not parsed["function"] then parsed["function"] = {} end
if not parsed["function"][n] then parsed["function"][n] = {} end
parsed["function"][n]["parent"] = parent
parsed["function"][n]["id"] = n
get_function_block(file, parsed["function"][n], source_name, parent+1)
end
get_source_line_positions(file, parsed)
get_locals(file, parsed)
get_upvalues(file, parsed)
end
-- output header
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
func_name = "function"
is_vararg = ""
if parsed["line_defined"] == 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, %d bytes at %d)\n",
func_name, parsed["source_name"], parsed["line_defined"], parsed["last_line_defined"],
parsed["number_of_instructions"], SS(parsed["number_of_instructions"]),
parsed["number_of_instructions"] * config.BLOCK, 0))
io.write(string.format("%d%s param%s, %d slot%s, %d upvalue%s, ",
parsed["number_of_parameters"], is_vararg, SS(parsed["number_of_parameters"]),
parsed["max_stack_size"], SS(parsed["max_stack_size"]),
parsed["number_of_upvalues"], SS(parsed["number_of_upvalues"])))
io.write(string.format("%d local%s, %d constant%s, %d function%s\n",
parsed["number_of_locals"], SS(parsed["number_of_locals"]),
parsed["number_of_constants"], SS(parsed["number_of_constants"]),
parsed["number_of_functions"], SS(parsed["number_of_functions"])))
end
-- get an opcode
function get_opcode(inst)
return get_bit(inst,config.POS_OP,config.SIZE_OP)
end
-- get argument A
function getarg_a(inst)
return get_bit(inst,config.POS_A,config.SIZE_A)
end
-- get argument B
function getarg_b(inst)
return get_bit(inst,config.POS_B,config.SIZE_B)
end
-- get argument C
function getarg_c(inst)
return get_bit(inst,config.POS_C,config.SIZE_C)
end
-- get argument Bx
function getarg_bx(inst)
return get_bit(inst,config.POS_Bx,config.SIZE_Bx)
end
-- get max argument for Bx
function maxarg_bx()
return (2 ^ config.SIZE_Bx) - 1
end
-- get max argument for sBx
function maxarg_sbx()
return get_bit(maxarg_bx(),1,32)
end
-- get argument sBx
function getarg_sbx(inst)
return getarg_bx(inst)-maxarg_sbx()
end
-- get opcode mode
function get_op_mode(o)
if opcode[o] == "JMP" or opcode[o] == "FORLOOP" or opcode[o] == "FORPREP" then
return opmode.iAsBx
elseif opcode[o] == "LOADK" or opcode[o] == "GETGLOBAL" or
opcode[o] == "SETGLOBAL" or opcode[o] == "CLOSURE" then
return opmode.iABx
end
return opmode.iABC
end
-- get B mask
function get_b_mode(o)
if opcode[o] == "TFORLOOP" or opcode[o] == "CLOSE" then
m = opargmask.OpArgN
elseif opcode[o] == "LOADBOOL" or opcode[o] == "GETUPVAL" or
opcode[o] == "SETUPVAL" or opcode[o] == "NEWTABLE" or
opcode[o] == "CALL" or opcode[o] == "TAILCALL" or
opcode[o] == "RETURN" or opcode[o] == "SETLIST" or
opcode[o] == "CLOSURE" or opcode[o] == "VARARG" then
m = opargmask.OpArgU
elseif opcode[o] == "MOVE" or opcode[o] == "LOADNIL" or
opcode[o] == "GETTABLE" or opcode[o] == "SELF" or
opcode[o] == "UNM" or opcode[o] == "NOT" or
opcode[o] == "LEN" or opcode[o] == "CONCAT" or
opcode[o] == "JMP" or opcode[o] == "TEST" or
opcode[o] == "TESTSET" or opcode[o] == "FORLOOP" or opcode[o] == "FORPREP" then
m = opargmask.OpArgR
elseif opcode[o] == "LOADK" or opcode[o] == "GETGLOBAL" or
opcode[o] == "SETGLOBAL" or opcode[o] == "SETTABLE" or
opcode[o] == "ADD" or opcode[o] == "SUB" or opcode[o] == "MUL" or
opcode[o] == "DIV" or opcode[o] == "MOD" or opcode[o] == "POW" or
opcode[o] == "EQ" or opcode[o] == "LT" or opcode[o] == "LE" then
m = opargmask.OpArgK
end
return m
end
-- get C mask
function get_c_mode(o)
if opcode[o] == "MOVE" or opcode[o] == "LOADK" or
opcode[o] == "LOADNIL" or opcode[o] == "GETUPVAL" or
opcode[o] == "GETGLOBAL" or opcode[o] == "SETGLOBAL" or
opcode[o] == "SETUPVAL" or opcode[o] == "UNM" or
opcode[o] == "NOT" or opcode[o] == "LEN" or opcode[o] == "JMP" or
opcode[o] == "RETURN" or opcode[o] == "FORLOOP" or
opcode[o] == "FORPREP" or opcode[o] == "CLOSE" or
opcode[o] == "CLOSURE" or opcode[o] == "VARARG" then
m = opargmask.OpArgN
elseif opcode[o] == "LOADBOOL" or opcode[o] == "NEWTABLE" or
opcode[o] == "TEST" or opcode[o] == "TESTSET" or
opcode[o] == "CALL" or opcode[o] == "TAILCALL" or
opcode[o] == "TFORLOOP" or opcode[o] == "SETLIST" then
m = opargmask.OpArgU
elseif opcode[o] == "CONCAT" then
m = opargmask.OpArgR
elseif opcode[o] == "GETTABLE" or opcode[o] == "SETTABLE" or
opcode[o] == "SELF" or opcode[o] == "ADD" or opcode[o] == "SUB" or
opcode[o] == "MUL" or opcode[o] == "DIV" or opcode[o] == "MOD" or
opcode[o] == "POW" or opcode[o] == "EQ" or opcode[o] == "LT" or opcode[o] == "LE" then
m = opargmask.OpArgK
end
return m
end
-- get BITRK
function bitrk()
return (2 ^ (config.SIZE_B - 1))
end
-- get ~BITRK
function not_bitrk()
return (bitrk() + 1) * -1
end
-- get ISK
function isk(x)
if x >= bitrk() then return true end
return false
end
-- get INDEXK
function indexk(x)
if x >= bitrk() then return not_bitrk() + (x + 1) else return x end
end
-- output string
function print_string(parsed)
io.write(string.format("\""))
for i=1,parsed["str_len"]-1 do
char = string.byte(parsed["value"],i)
if char == 34 then io.write(string.format("\\\""))
elseif char == 92 then io.write(string.format("\\\\"))
elseif char == 7 then io.write(string.format("\\a"))
elseif char == 8 then io.write(string.format("\\b"))
elseif char == 12 then io.write(string.format("\\f"))
elseif char == 10 then io.write(string.format("\\n"))
elseif char == 13 then io.write(string.format("\\r"))
elseif char == 9 then io.write(string.format("\\t"))
elseif char == 11 then io.write(string.format("\\v"))
else
if isprint(char) then
io.write(string.format("%c", char))
else
io.write(string.format("\\%03d", char))
end
end
end
io.write(string.format("\""))
end
-- output a constant
function print_constant(parsed, n)
c_type = parsed["constant"][n]["c_type"]
value = parsed["constant"][n]["value"]
if c_type == types.LUA_TNIL then
io.write(string.format("%s", value))
elseif c_type == types.LUA_TBOOLEAN then
io.write(string.format("%s", value))
elseif c_type == types.LUA_TNUMBER then
io.write(string.format("%s", value))
elseif c_type == types.LUA_TSTRING then
print_string(parsed["constant"][n])
end
end
-- output code
function print_code(parsed)
for n=1,parsed["number_of_instructions"] do
i = parsed["instruction"][n]
o = get_opcode(i) + 1
a = getarg_a(i)
b = getarg_b(i)
c = getarg_c(i)
bx = getarg_bx(i)
sbx = getarg_sbx(i)
if parsed["number_of_instruction_lines"] > 0 then line = parsed["instruction_line"][n] else line = 0 end
io.write(string.format("\t%d\t", n))
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[o]))
if get_op_mode(o) == opmode.iABC then
io.write(string.format("%d", a))
if get_b_mode(o) ~= opargmask.OpArgN then
if isk(b) then
io.write(string.format(" %d", -1-indexk(b)))
else
io.write(string.format(" %d", b))
end
end
if get_c_mode(o) ~= opargmask.OpArgN then
if isk(c) then
io.write(string.format(" %d", -1-indexk(c)))
else
io.write(string.format(" %d", c))
end
end
elseif get_op_mode(o) == opmode.iABx then
if get_b_mode(o) == opargmask.OpArgK then
io.write(string.format("%d %d", a, -1-bx))
else
io.write(string.format("%d %d", a, bx))
end
elseif get_op_mode(o) == opmode.iAsBx then
if opcode[o] == "JMP" then
io.write(string.format("%d", sbx))
else
io.write(string.format("%d %d", a, sbx))
end
end
if opcode[o] == "LOADK" then
io.write(string.format("\t; "))
print_constant(parsed, bx+1)
elseif opcode[o] == "GETUPVAL" or opcode[o] == "SETUPVAL" then
if parsed["number_of_upvalues"] > 0 then myupvalue = parsed["upvalue"][b+1]["value"] else myupvalue = "-" end
io.write(string.format("\t; %s", myupvalue))
elseif opcode[o] == "GETGLOBAL" or opcode[o] == "SETGLOBAL" then
io.write(string.format("\t; %s", parsed["constant"][bx+1]["value"]))
elseif opcode[o] == "GETTABLE" or opcode[o] == "SELF" then
if isk(c) then
io.write(string.format("\t; "))
print_constant(parsed, indexk(c+1))
end
elseif opcode[o] == "SETTABLE" or opcode[o] == "ADD" or
opcode[o] == "SUB" or opcode[o] == "MUL" or
opcode[o] == "DIV" or opcode[o] == "POW" or
opcode[o] == "EQ" or opcode[o] == "LT" or opcode[o] == "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[o] == "JMP" or opcode[o] == "FORLOOP" or opcode[o] == "FORPREP" then
io.write(string.format("\t; to %d", sbx+n+1))
elseif opcode[o] == "CLOSURE" then
io.write(string.format("\t; 0x"))
elseif opcode[o] == "SETLIST" then
if c == 0 then
io.write(string.format("\t; %d", 0))
else
io.write(string.format("\t; %d", c))
end
end
io.write(string.format("\n"))
end
end
-- output a function
function print_function(parsed)
print_header(parsed)
print_code(parsed)
for i=1,parsed["number_of_functions"] do
print_function(parsed["function"][i])
end
end
-- output header to a file
function write_header(output, parsed)
if parsed["line_defined"] == 0 and parsed["id"] == 0 then
func_name = "main function:"
else
func_name = string.format("function F_%s_%s:", parsed["parent"], parsed["id"])
end
output:write(string.format("\n%s\n", func_name))
end
-- output a string to a file
function write_string(output, parsed)
output:write(string.format("\""))
for i=1,parsed["str_len"]-1 do
char = string.byte(parsed["value"],i)
if char == 34 then output:write(string.format("\\\""))
elseif char == 92 then output:write(string.format("\\\\"))
elseif char == 7 then output:write(string.format("\\a"))
elseif char == 8 then output:write(string.format("\\b"))
elseif char == 12 then output:write(string.format("\\f"))
elseif char == 10 then output:write(string.format("\\n"))
elseif char == 13 then output:write(string.format("\\r"))
elseif char == 9 then output:write(string.format("\\t"))
elseif char == 11 then output:write(string.format("\\v"))
else
if isprint(char) then
output:write(string.format("%c", char))
else
output:write(string.format("\\%d", char))
end
end
end
output:write(string.format("\""))
end
-- output a constant to a file
function write_constant(output, parsed, n)
c_type = parsed["constant"][n]["c_type"]
value = parsed["constant"][n]["value"]
if c_type == types.LUA_TNIL or c_type == types.LUA_TBOOLEAN or c_type == types.LUA_TNUMBER then
output:write(string.format("%s", value))
elseif c_type == types.LUA_TSTRING then
write_string(output, parsed["constant"][n])
end
end
-- output a boolean to a file
function write_boolean(output, b)
if b == 0 then
output:write(string.format("FALSE"))
else
output:write(string.format("TRUE"))
end
end
-- output a register to a file
function write_register(output, i)
output:write(string.format("R[%s]", i))
end
-- output a constant or register to a file
function write_rk(output, parsed, x)
if isk(x) then
write_constant(output, parsed, indexk(x+1))
else
write_register(output, x)
end
end
-- output an index to a file
function write_index(output, x)
if isk(x) then
output:write(string.format("%s", -1-indexk(x)))
else
output:write(string.format("%s", x))
end
end
-- output function id to a file
function write_function_id(output, parsed, i)
local parent = parsed["function"][i]["parent"]
local id = parsed["function"][i]["id"]
local np = parsed["function"][i]["number_of_parameters"]
output:write(string.format("F_%s_%s %d", parent, id, np))
end
-- output an space to a file
function write_space(output)
output:write(string.format(" "))
end
-- output instruction to a file
function write_code(output, parsed)
for n=1,parsed["number_of_instructions"] do
i = parsed["instruction"][n]
o = get_opcode(i) + 1
a = getarg_a(i)
b = getarg_b(i)
c = getarg_c(i)
bx = getarg_bx(i)
sbx = getarg_sbx(i)
if parsed["number_of_instruction_lines"] > 0 then line = parsed["instruction_line"][n] else line = 0 end
output:write(string.format("\t%d\t", n))
if line > 0 then
output:write(string.format("[%d]\t", line))
else
output:write(string.format("[0]\t"))
end
output:write(string.format("%-9s\t", opcode[o]))
if opcode[o] ~= "JMP" then
write_register(output, a)
else
output:write(string.format("%s", sbx+n+1))
end
if opcode[o] == "MOVE" or opcode[o] == "LOADNIL" or
opcode[o] == "UNM" or opcode[o] == "NOT" or opcode[o] == "LEN" then
write_space(output)
write_register(output, b)
elseif opcode[o] == "LOADK" then
write_space(output)
write_constant(output, parsed, bx+1)
elseif opcode[o] == "LOADBOOL" then
write_space(output)
write_boolean(output, b)
write_space(output)
write_index(output, c)
elseif opcode[o] == "GETUPVAL" or opcode[o] == "SETUPVAL" then
write_space(output)
output:write(string.format("%s", parsed["upvalue"][b+1]["value"]))
elseif opcode[o] == "GETGLOBAL" or opcode[o] == "SETGLOBAL" then
write_space(output)
output:write(string.format("%s", parsed["constant"][bx+1]["value"]))
elseif opcode[o] == "GETTABLE" or opcode[o] == "SELF" then
write_space(output)
write_register(output, b)
write_space(output)
write_rk(output, parsed, c)
elseif opcode[o] == "SETTABLE" or
opcode[o] == "ADD" or opcode[o] == "SUB" or opcode[o] == "MUL" or
opcode[o] == "DIV" or opcode[o] == "MOD" or opcode[o] == "POW" or
opcode[o] == "EQ" or opcode[o] == "LT" or opcode[o] == "LE" then
write_space(output)
write_rk(output, parsed, b)
write_space(output)
write_rk(output, parsed, c)
elseif opcode[o] == "NEWTABLE" or opcode[o] == "CALL" or opcode[o] == "TAILCALL" or
opcode[o] == "SETLIST" then
write_space(output)
write_index(output, b)
write_space(output)
write_index(output, c)
elseif opcode[o] == "CONCAT" then
write_space(output)
write_register(output, b)
write_space(output)
write_register(output, c)
elseif opcode[o] == "FORLOOP" or opcode[o] == "FORPREP" then
write_space(output)
output:write(string.format("%s", sbx+n+1))
elseif opcode[o] == "TEST" or opcode[o] == "TESTSET" then
write_space(output)
write_register(output, b)
write_space(output)
write_boolean(output, c)
elseif opcode[o] == "RETURN" or opcode[o] == "VARARG" then
write_space(output)
write_index(output, b)
elseif opcode[o] == "TFORLOOP" then
write_space(output)
write_index(output, c)
elseif opcode[o] == "CLOSE" then
elseif opcode[o] == "CLOSURE" then
write_space(output)
write_function_id(output, parsed, bx+1)
end
output:write(string.format("\n"))
end
end
-- output a function to a file
function write_function(output, parsed)
write_header(output, parsed)
write_code(output, parsed)
for i=1,parsed["number_of_functions"] do
write_function(output, parsed["function"][i])
end
end
if #arg < 1 then
usage("no input file given");
end
doargs()
if options.INPUT == nil then
usage("no input file given")
end
if not (loadfile(options.INPUT)) then
usage(options.INPUT .. " can not be loaded")
end
contents = string.dump(loadfile(options.INPUT))
tmp = assert(io.open(options.TMP, "wb"))
tmp:write(contents)
tmp:close()
input = assert(io.open(options.TMP, "rb"))
if not input then
io.write(string.format("Couldn't open %s!\n", options.TMP))
end
header_chunk = load_header(input)
-- check if Lua version is 5.1
if not (check_version(header_chunk)) then
io.write(string.format("Can't proceed. It works only with Lua 5.1.\n"))
input:close()
os.exit(1)
end
-- set flags got in the input file
set_auto_flags(header_chunk)
if not parsed["main"] then parsed["main"] = {} end
-- check source name
source_name = get_source_name(input, parsed["main"])
-- scan input file
parsed["main"]["id"] = 0
get_function_block(input, parsed["main"], source_name, parsed["main"]["id"])
input:close()
os.remove(options.TMP)
if options.LISTING then print_function(parsed["main"]) end
output = assert(io.open(options.OUTPUT, "w"))
if not output then
io.write(string.format("Couldn't open %s!\n", options.OUTPUT))
end
write_function(output, parsed["main"])
output:close()
os.exit(0)