Commits

Liam Devine committed 74e560f

First upload of a Lua 5.2 byte code inspector with interactive mode

Comments (0)

Files changed (2)

inspect_helper.lua

+local inspect_helper = {}
+
+local small_endian_host = string.dump(function()end):byte(7) == 1 
+
+function inspect_helper.host_is_small_endian()
+	return small_endian_host
+end
+
+function inspect_helper.byte_str_to_big_endian_number(str)
+	local num = 0
+	local len = #str
+	for i = 1,str do
+	num = num + string.byte(str,i) * 256^(str-i)
+	end
+	return num
+end
+
+function inspect_helper.byte_str_to_small_endian_number(str)
+	local num = 0
+	local len = #str
+	for i = 1,len do
+		num = num + string.byte(str,len - i +1) * 256^(len-i)
+	end
+	return num
+end
+
+function inspect_helper.four_byte_str_to_big_endian_number(str)
+	local num = 0
+	for i = 1,4 do
+	num = num + string.byte(str,i) * 256^(4-i)
+	end
+	return num
+end
+
+function inspect_helper.four_byte_str_to_small_endian_number(str)
+	local num = 0
+	for i = 1,4 do
+		num = num + string.byte(str,4 - i +1) * 256^(4-i)
+	end
+	return num
+end
+
+function inspect_helper.two_byte_small_to_host(str)
+	if small_endian_host then
+		return inspect_helper.byte_str_to_small_endian_number(str)
+	else
+		return inspect_helper.byte_str_to_big_endian_number(str)
+	end
+end
+
+
+
+function inspect_helper.two_byte_big_to_host(str)
+	if small_endian_host then
+		return inspect_helper.byte_str_to_big_endian_number(str)
+	else
+		return inspect_helper.byte_str_to_small_endian_number(str)
+	end
+end
+
+inspect_helper.small_to_host = inspect_helper.two_byte_small_to_host
+inspect_helper.big_to_host = inspect_helper.two_byte_big_to_host
+
+function inspect_helper.four_byte_small_to_host(str)
+	if small_endian_host then
+		return inspect_helper.four_byte_str_to_small_endian_number(str)
+	else
+		return inspect_helper.four_byte_str_to_big_endian_number(str)
+	end
+end
+
+function inspect_helper.four_byte_big_to_host(str)
+	if small_endian_host then
+		return inspect_helper.four_byte_str_to_big_endian_number(str)
+	else		
+		return inspect_helper.four_byte_str_to_small_endian_number(str)
+	end
+end
+
+
+function inspect_helper.to_double(x)
+	local bit = require 'bit32'
+	local sign = 1
+	local exp7,exp8 = x:byte(7,8)
+	if bit.band(exp8,0x80) ~= 0 then sign = -1 end
+	local expo = bit.bor( bit.lshift(bit.band(exp8,0x7f),4) , bit.rshift(bit.band(exp7,0xf0),4) )
+	if expo == 0 then return 0 end
+	local mantissa = bit.band(exp7,0x0f)
+	for i = 6, 1, -1 do --can not shift the mantissa as bit result is uint32
+		mantissa = mantissa * 256 + string.byte(x, i) 
+	end
+	return math.ldexp( (math.ldexp(mantissa, -52) + 1) * sign, expo - 1023) 		
+end
+
+
+return inspect_helper
+--[[
+Copyright (c) 2012 Liam Devine
+
+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.
+--]]
+
+
+local helper = require 'inspect_helper'
+local bit = require'bit32'
+
+--RK is a register < 256 or constant > 255.
+local lua_ops =
+{
+	 [ 0] = {name = 'OP_MOVE'		,regs = {'A','B' 	} ,desc = 'R(A) = R(B)'}
+	,[ 1] = {name = 'OP_LOADK'		,regs = {'A','Bx'	} ,desc = 'R(A) = Kst(Bx)'}
+	,[ 2] = {name = 'OP_LOADKX'		,regs = {'A'		} ,desc = 'R(A) = Kst(extra arg)'} --Ax (extra arg)taken from next instruction
+	,[ 3] = {name = 'OP_LOADBOOL'	,regs = {'A','B','C'} ,desc = 'R(A) = (bool)B; if (C) pc++'}
+	,[ 4] = {name = 'OP_LOADNIL' 	,regs = {'A','B' 	} ,desc = 'R(A), R(A+1), ..., R(A+B) = nil'}
+	,[ 5] = {name = 'OP_GETUPVAL'	,regs = {'A','B' 	} ,desc = 'R(A) = upvalue[B]'}
+	
+	,[ 6] = {name = 'OP_GETTABUP'	,regs = {'A','B','C'} ,desc = 'R(A) = upvalue[B][RK(C)]'}
+	,[ 7] = {name = 'OP_GETTABLE'	,regs = {'A','B','C'}, desc = 'R(A) = R(B)[RK(C)]'}
+	
+	,[ 8] = {name = 'OP_SETTABUP'	,regs = {'A','B','C'}, desc = 'upvalue[A][RK(B)] = RK(C)'}
+	,[ 9] = {name = 'OP_SETUPVAL'	,regs = {'A','B'	}, desc = 'upvalue[B] = R(A)'}
+	,[10] = {name = 'OP_SETTABLE'	,regs = {'A','B','C'}, desc = 'R(A)[RK(B)] = RK(C)'}
+	,[11] = {name = 'OP_NEWTABLE'	,regs = {'A','B','C'}, desc = 'R(A) = {} (arraysize = B, hashsize = C)'}
+	,[12] = {name = 'OP_SELF'		,regs = {'A','B','C'}, desc = 'R(A+1) = R(B); R(A) = R(B)[RK(C)]'}
+	
+	,[13] = {name = 'OP_ADD'		,regs = {'A','B','C'} ,desc = 'R(A) = RK(B) + RK(C)'}
+	,[14] = {name = 'OP_SUB'		,regs = {'A','B','C'} ,desc = 'R(A) = RK(B) - RK(C)'}
+	,[15] = {name = 'OP_MUL'		,regs = {'A','B','C'} ,desc = 'R(A) = RK(B) * RK(C)'}
+	,[16] = {name = 'OP_DIV'		,regs = {'A','B','C'} ,desc = 'R(A) = RK(B) / RK(C)'}
+	,[17] = {name = 'OP_MOD'		,regs = {'A','B','C'} ,desc = 'R(A) = RK(B) % RK(C)'}
+	,[18] = {name = 'OP_POW'		,regs = {'A','B','C'} ,desc = 'R(A) = RK(B) ^ RK(C)'}
+	,[19] = {name = 'OP_UNM'		,regs = {'A','B' 	}, desc = 'R(A) = -R(B)'}
+	,[20] = {name = 'OP_NOT'		,regs = {'A','B' 	}, desc = 'R(A) = !R(B)'}
+	,[21] = {name = 'OP_LEN'		,regs = {'A','B' 	}, desc = 'R(A) = #R(B)'}
+	,[22] = {name = 'OP_CONCAT'		,regs = {'A','B','C'} ,desc = 'R(A) = R(B) .. R(C)'}
+	,[23] = {name = 'OP_JMP'		,regs = {'A','sBx'	}, desc = 'pc+=sBx; if (A) close all upvalues >= A + 1'}
+	,[24] = {name = 'OP_EQ'			,regs = {'A','B','C'} ,desc = 'if ((RK(B) == RK(C)) ~= A) then pc++'}
+	,[25] = {name = 'OP_LT'			,regs = {'A','B','C'} ,desc = 'if ((RK(B) <  RK(C)) ~= A) then pc++'}
+	,[26] = {name = 'OP_LE'			,regs = {'A','B','C'} ,desc = 'if ((RK(B) <= RK(C)) ~= A) then pc++'}
+	
+	,[27] = {name = 'OP_TEST'		,regs = {'A','B'	}, desc = 'if not (R(A) <=> C) then pc++'}
+	,[28] = {name = 'OP_TESTSET'	,regs = {'A','B','C'} ,desc = 'if (R(B) <=> C) then R(A) := R(B) else pc++'}
+	
+	,[29] = {name = 'OP_CALL'		,regs = {'A','B','C'}, desc = 'R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)'}
+	,[30] = {name = 'OP_TAILCALL'	,regs = {'A','B','C'}, desc = 'return R(A)(R(A+1), ... ,R(A+B-1))'}
+	,[31] = {name = 'OP_RETURN'		,regs = {'A','B'	}, desc = 'return R(A), ... ,R(A+B-2)'}
+	
+	,[32] = {name = 'OP_FORLOOP'	,regs = {'A','sBx'	}, desc = 'R(A)+=R(A+2) \nif R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }'}
+	,[33] = {name = 'OP_FORPREP'	,regs = {'A','sBx'	}, desc = 'R(A)-=R(A+2); pc+=sBx'}
+	
+	,[34] = {name = 'OP_TFORCALL'	,regs = {'A','C'	}, desc = 'R(A+3), ... ,R(A+2+C) = R(A)(R(A+1), R(A+2));'}
+	,[35] = {name = 'OP_TFORLOOP'	,regs = {'A','sBx'	}, desc = 'if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx }'}
+	
+	,[36] = {name = 'OP_SETLIST'	,regs = {'A','B','C'} ,desc = 'R(A)[(C-1)*FPF+i] = R(A+i), 1 <= i <= B'}
+	,[37] = {name = 'OP_CLOSURE'	,regs = {'A','Bx' 	} ,desc = 'R(A) = closure(KPROTO[Bx])'}
+	,[38] = {name = 'OP_VARARG'		,regs = {'A','B'	} ,desc = 'R(A), R(A+1), ..., R(A+B-2) = vararg'}
+	,[39] = {name = 'OP_EXTRAARG'	,regs = {'Ax'		} ,desc = 'extra (larger) argument for previous opcode'}
+}
+
+--[[
+Instruction format
+1111 1111 1111 1111 1111 1111 1111 1111
+
+31	      23         14         6     0
+---------------------------------------
+|   B      |     C    |     A   |  OP |
+
+--------------------------------
+|                Ax             |
+
+-----------------------
+|          Bx         |
+
+----------------------
+|         sBx         |
+
+'sBx' is treated specially
+value & 2^18 -  2^18-1 >>2
+--]]
+
+
+--[[
+local masks_not_used =
+{
+	 ['op'] = 0x3f
+	,['A' ] = 0x00003fc0
+	,['C' ] = 0x007fc000
+	,['B' ] = 0xff800000
+	,['Ax'] = 0xffffffc0
+	,['Bx'] = 0xff803fc0
+}
+
+instructions are shifted by the position they occupy and then anded
+otherwise we loose the upper bit
+--]]
+local instruction =
+{
+	 ['op'] = {['pos']= 0,['size']= 6,['band']=0x000003f}
+	,['A' ] = {['pos']= 6,['size']= 8,['band']=0x00000ff}
+	,['C' ] = {['pos']=14,['size']= 9,['band']=0x00001ff}
+	,['B' ] = {['pos']=23,['size']= 9,['band']=0x00001ff}
+	
+	,['Ax'] = {['pos']= 6,['size']=26,['band']=0x3ffffff}
+	,['Bx'] = {['pos']=14,['size']=18,['band']=0x003ffff}
+--	,['sBx'] = {['pos']=14,['size']=18,['band']=0x003ffff}
+
+}
+
+local const_types = 
+{
+	[0x00] = 'nil',
+	[0x01] = 'bool',
+	[0x03] = 'number',
+	[0x04] = 'string' 
+}
+	
+local band = bit.band
+local shift = bit.arshift
+local rshift = bit.rshift
+local lshift = bit.lshift
+
+
+
+local file_position = function(num)
+	return '['.. string.rep('0',4-#num) .. num ..']'
+end
+
+local hex_pos = function(num)
+	return file_position( string.format('%X',num) )
+end
+
+local dec_pos = function(num)
+	return file_position( tostring(num) )	
+end
+
+--which format are byte numbers in
+--local pos = dec_pos
+local options =
+{
+	 ['pos'					] 	= hex_pos
+	,['replace_reg_indexes'	] 	= false
+	,['byte_code_width'		] 	= 8
+	,['debug_should_write'	]	= true 
+	,['is_verbose'			]	= true
+}
+
+local pos = hex_pos
+local replace_reg_indexes = false
+local byte_code_width = 8
+local debug_should_write = false --off by default true 
+local is_verbose = false --off by default true
+
+local verbose_print = function(...)
+	if is_verbose then
+		print(...)
+	end
+end
+local dummy_print = function(...)end
+	
+	
+local instruction_bits = function(num,instruction_part)
+	if instruction_part == 'sBx' then
+		local i = instruction['Bx']
+		return band(shift(num,i.pos),i.band) - shift(i.band,1)
+	end
+	local i = instruction[instruction_part]
+	return band(shift(num,i.pos),i.band)
+end
+
+local set_number_func = function(head)
+	if head.little_endian == 1 then
+		head.get_number = helper.byte_str_to_small_endian_number
+	else
+		head.get_number = helper.byte_str_to_big_endian_number
+	end
+end
+
+
+
+local write_bytes = function(str,index,len,new_line_tab)
+	new_line_tab = new_line_tab or ''
+	local ss = ''
+	local format = string.format
+	local t = { str:byte(index,index + len -1) }
+	local byte_address = index
+	for i = 1, #t do
+		if (i~=1) and (i-1) % byte_code_width == 0 then 
+			byte_address = byte_address + byte_code_width
+			ss = ss..'\n' .. new_line_tab .. pos(byte_address-1) ..'\t'
+		end
+		if t[i] < 16 then
+			ss = ss .. format('0%X ',t[i])
+		else
+			ss = ss .. format('%X ',t[i])
+		end
+	end
+	local pad=(#t)% byte_code_width
+	
+	if pad ~= 0 then ss = ss..string.rep('   ',byte_code_width - pad) end 
+	return ss
+end
+
+local get_bytes = function(stream,index,size,inc)
+	inc = inc or false
+	local b = ''
+	if tonumber(size,10) ~= 0 then 
+		b = stream:sub(index,index + size - 1)
+	else
+		error('size is 0')
+	end
+	if inc == true then 
+		index = index + size
+		return b,index
+	end
+	return b
+end
+
+local get_string = function(stream,index,size_t)
+	local str_size = tonumber(helper.byte_str_to_small_endian_number( stream:sub(index,index + size_t - 1) ),10)
+	index = index + size_t
+	local str , index = get_bytes(stream,index,str_size,true)
+	return index ,str
+end
+
+
+local grab_header = function(bs)
+	local h=
+	{
+		magic = bs:sub(1,4)
+		,version = bs:sub(5)
+		,offical = bs:byte(6)
+		,endian = bs:byte(7)
+		,sizeof_size_t = bs:byte(8)
+		,sizeof_int = bs:byte(9)
+		,sizeof_Instruction = bs:byte(10)
+		,sizeof_lua_Number = bs:byte(11)
+		,floating_point= bs:byte(12)
+		,padding = bs:sub(12,18)
+	}
+
+	return h, 19 --[[next byte index--]]
+end
+
+	
+local dump_header = function(byte_stream)--,write_header)
+	local h = grab_header(byte_stream)
+	local head =
+	{	
+		little_endian = byte_stream:byte(7)
+		,sizeof =
+		{
+			int = byte_stream:byte(8)
+			,size_t = byte_stream:byte(9)
+			,Instruction = byte_stream:byte(10)
+			,lua_Number = byte_stream:byte(11)
+		}
+	}
+
+ 	local p = verbose_print
+	local bs = byte_stream
+ 	local wb = write_bytes
+	--header 5.2 18 bytes
+	p('.header')
+	p(pos(0),wb(bs, 1,4) ,';magic header ["\\033Lua"]')
+	p(pos(4),wb(bs, 5,1) ,';version')
+	p(pos(5),wb(bs, 6,1) ,';offical [offical = 00]')
+	p(pos(6),wb(bs, 7,1) ,';endian [little = 01]')
+	p(pos(7),wb(bs, 8,1) ,';sizeof(int)')
+	p(pos(8),wb(bs, 9,1) ,';sizeof(size_t)')
+	p(pos(9),wb(bs,10,1) ,';sizeof(Instruction)')
+	p(pos(10),wb(bs,11,1),';sizeof(lua_Number)')
+	p(pos(11),wb(bs,12,1),';floating point [floating = 00, integral = 01]')
+	p(pos(12),wb(bs,13,4))
+	p(pos(16),wb(bs,17,2),';end of header check ["\\x19\\x93\\r\\n\\x1a\\n"]')
+	
+	set_number_func(head)
+	return 19--[[string_index = buffer_index+1--]], head
+end
+
+local dump_function = nil --forward declare
+
+local dump_const_funcs = function(byte_stream,index,head,tabstr)
+
+	local print = print
+	local vector_size = head.get_number( byte_stream:sub(index,index + head.sizeof.int -1) )
+	local bb = write_bytes(byte_stream,index,head.sizeof.int)
+	print(tabstr ..'.nested_proto')
+	verbose_print(tabstr .. pos(index-1),bb .. '\t;vector size',vector_size)
+	index = index + head.sizeof.int
+	
+	local new_tab = #tabstr+1
+	for i = 1, vector_size do
+		index = dump_function(byte_stream,index,head,new_tab)
+	end
+	return index
+end
+
+
+
+	
+local dump_constants = function(byte_stream,index,head,tabstr)
+	local print = print
+
+	local vector_size = head.get_number( byte_stream:sub(index,index +  head.sizeof.int -1) )
+	local bb = write_bytes(byte_stream,index,head.sizeof.int)
+
+	print(tabstr .. '.const')
+	verbose_print(tabstr .. pos(index-1),bb .. '\t;vector size',vector_size)
+	index = index + head.sizeof.int
+	
+	for i = 1, vector_size do
+	
+		local i_str =''
+		if i-1<10 then i_str=''end ---what is this for?
+		i_str = i_str .. tostring(i-1)
+		local const_str = ';const[' .. i_str .. '] = '
+		
+		local type_t = byte_stream:byte(index,index+1)
+		local bb = write_bytes(byte_stream,index,1--[[type--]],tabstr)
+		verbose_print(tabstr .. pos(index-1),bb, ';const[' .. i_str .. '].type = ' .. const_types[type_t]  )	
+		index = index +1 --increment passed type
+		
+		if type_t == 0x00 then
+			--repeat index-1 byte string as 0x00 is the type and the value is 'nil', ie there is no written value in the format
+			--so only in verbose mode will you see the byte index repeated
+			print(tabstr .. pos(index-2--[[we have already moved index--]]),bb,const_str .. 'nil' )	
+			--print(tabstr  ..const_str .. 'nil' )	
+		elseif type_t == 0x01 then
+			local bool = byte_stream:byte(index) == 0x01
+			local bb = write_bytes(byte_stream,index,1--[[head.sizeof.bool--]],tabstr)
+			print(tabstr .. pos(index-1),bb,const_str .. tostring(bool)  )	
+			index = index +1--[[head.sizeof.bool--]]
+		elseif type_t == 0x03 then
+			local num = helper.to_double(byte_stream:sub(index,index + head.sizeof.lua_Number - 1) )
+			local bb = write_bytes(byte_stream,index,head.sizeof.lua_Number,tabstr)
+			print(tabstr .. pos(index-1),bb,const_str ..  num)
+			index = index + head.sizeof.lua_Number
+		elseif type_t == 0x04 then
+			local str_size = head.get_number( byte_stream:sub(index,index + head.sizeof.size_t-1) )
+			local bb = write_bytes(byte_stream,index,head.sizeof.size_t,tabstr)
+			verbose_print(tabstr .. pos(index-1),bb,';str size ' .. str_size)
+			index = index + head.sizeof.size_t
+								
+			local str = get_bytes(byte_stream,index,str_size)
+			bb = write_bytes(byte_stream,index,str_size,tabstr)
+			print(tabstr  .. pos(index-1),bb,const_str .. '"' ..  str .. '"' )
+			index = index + str_size
+		else
+			print(tabstr .. pos(index-1),const_str .. 'unknown type',type_t)
+		end
+	end
+	
+	index = dump_const_funcs(byte_stream,index,head,tabstr)
+		
+	return index
+end
+
+local dump_code = function(byte_stream,index,head,tabstr)
+	local print = print
+	local code_size = helper.byte_str_to_small_endian_number( byte_stream:sub(index,index + head.sizeof.int - 1) )
+	local bb = write_bytes(byte_stream,index,4)--head.sizeof.int
+
+
+	--local _inst = reader.int(byte_stream:sub(index,index + head.sizeof.int - 1) )
+	--print(_inst)
+		
+	print(tabstr ..'.text')
+	print(tabstr .. pos(index-1),bb .. '\t;instruction count '..code_size)
+	index = index + head.sizeof.int
+	
+	for i = 1, code_size do
+		local instr = helper.byte_str_to_small_endian_number( 
+							byte_stream:sub(index,index + head.sizeof.Instruction - 1) 
+							)
+		local instruction_values = 
+		{
+			['op' ] = instruction_bits(instr,'op')
+			,['A' ] = instruction_bits(instr,'A')
+			,['C' ] = instruction_bits(instr,'C')
+			,['B' ] = instruction_bits(instr,'B')			
+			,['Ax'] = instruction_bits(instr,'Ax')
+			,['Bx'] = instruction_bits(instr,'Bx')
+			,['sBx'] = instruction_bits(instr,'sBx')
+		}
+		
+		local reg_str = ''
+		local op = lua_ops[instruction_values.op]
+		local description = op.desc
+		local jmp = ''
+
+		local idx_end
+		local idx_start, idx_end = description:find('RK%(%u%)')
+		local maybe_const = {['A']=nil,['A']=nil,['A']=nil}
+		while idx_start do
+			local maybe_const_reg = description:sub(idx_start,idx_end):match('RK%((%u)%)')
+			maybe_const[maybe_const_reg]=1
+			idx_start, idx_end = description:find('RK%(%u%)',idx_end +1)
+			
+		end
+				
+		for _,v in ipairs( op.regs ) do
+			reg_str = reg_str .. ' '.. v .. '=' .. instruction_values[v]
+			if maybe_const[v] and instruction_values[v] > 255 then
+				reg_str = reg_str .. '(const[' ..tostring(instruction_values[v] - 256) .. '])'
+			end
+		end
+		
+		if op.name =='OP_JMP' then
+			jmp = i+1 + instruction_values['sBx']
+			jmp = 'jmp pc[' .. tostring(jmp) ..']'
+			description = description:gsub('pc%+=sBx',jmp)	
+		elseif op.name == 'OP_CALL' or op.name == 'OP_TAILCALL' then
+			local regC = instruction_values.C 
+			if regC == 0 then
+				reg_str = reg_str .. ' (C=0 multiple returns)'
+			elseif regC == 1 then
+				reg_str = reg_str .. ' (C=1 no returns)'
+			elseif 1 < regC then
+				reg_str = reg_str .. ' (C>1 '.. tostring(regC-1) ..' returns)'				
+			end
+			local regB = instruction_values.B 
+			if regB == 0 then
+				reg_str = reg_str .. ' (B=0 multiple parameters from A+1 to top of stack)'
+			elseif regB == 1 then
+				reg_str = reg_str .. ' (B=1 no parameters)'
+			elseif 1 < regB then
+				reg_str = reg_str .. ' (B>1 '.. tostring(regB-1)..' parameters)'
+			end
+		elseif op.name == 'OP_VARARG' then
+			local regB = instruction_values.B 
+			if 1 < regB then
+				reg_str = reg_str .. ' (B>1 '.. tostring(regB-1)..' values)'
+			end	
+		elseif op.name == 'OP_RETURN' then
+			local regB = instruction_values.B 
+			if regB == 0 then
+				reg_str = reg_str .. ' (B=0 multiple returns from A to top of stack)'
+			elseif regB == 1 then
+				reg_str = reg_str .. ' (B=1 no returns)'
+			elseif 1 < regB then
+				reg_str = reg_str .. ' (B>1 '.. tostring(regB-1)..' returns)'
+			end
+		elseif op.name == 'OP_NEWTABLE' then
+			local fb2int = function(value)
+				local e = band(shift(value,3),0x1f)
+				if e == 0 then 
+					return value
+				else 
+					return lshift( band(value,7) +8 ,e -1)
+				end
+			end
+			reg_str = 'A='.. instruction_values.A .. ' B=' .. fb2int(instruction_values.B) ..' C=' .. fb2int(instruction_values.C)
+		end
+		
+		if replace_reg_indexes then
+			for _,reg in ipairs(op.regs) do
+				--replace instances of the register name with the register number
+				description = description:gsub(reg,instruction_values[reg])	
+			end
+		end
+		
+		local pretty_name = op.name .. string.rep(' ', 14 -#op.name)
+	
+		print(tabstr .. pos(index-1),write_bytes(byte_stream,index,4), ';pc['.. i ..'] '.. 
+		pretty_name 
+		--,lua_ops[instruction_values.op].name
+		,description
+		--,op.desc
+		,reg_str 
+		--.. ' ' ..jmp
+		)
+		index = index + head.sizeof.Instruction
+	end
+	return index
+end
+
+
+local dump_upvalues = function(byte_stream,index,head,tabstr)
+	local upvalue_sz = head.get_number( byte_stream:sub(index,index + head.sizeof.int - 1) )
+	local bb = write_bytes(byte_stream,index,head.sizeof.int)
+	
+	print(tabstr ..'.upvalue')
+	verbose_print(tabstr .. pos(index-1),bb .. '\t;upvalue count '..upvalue_sz)
+	index = index + head.sizeof.int
+	if upvalue_sz ~= 0 then
+		for i = 1, upvalue_sz do
+			local instack = byte_stream:byte(index) --TODO: if not in stack does it have an index? check this and comment
+			local idx = byte_stream:byte(index+1)
+			local bb = write_bytes(byte_stream,index,2)
+			print(tabstr .. pos(index-1), bb,';upvalue['.. tostring(i-1) ..'] = ','instack='..instack,'idx='..idx)
+			index = index + 2 --2 bytes  
+		end
+	end
+	return index
+end
+
+local dump_debug = function(byte_stream,index,head,tabstr)
+	local get_number = function(count)
+	 	return head.get_number( byte_stream:sub(index,index + count - 1) )
+	end
+	local format_bytes= function(count)
+		return write_bytes(byte_stream,index,count,tabstr)
+	end
+	local get_number_and_byte_string = function(size)
+		return get_number(size), format_bytes(size)
+	end
+	
+	local advance_index = function(advance)
+		index = index + advance
+	end
+	
+	local debug_print = function(...)
+		if debug_should_write then
+			print(...)
+		end
+	end
+	local debug_verbose_print
+	if debug_should_write then
+		debug_verbose_print = verbose_print
+	else
+		debug_verbose_print = dummy_print	
+	end
+	
+	
+	--only supports 32 bit ints :(
+	local src_count = 0
+	if head.sizeof.size_t > head.sizeof.int  then
+		local low = get_number(head.sizeof.int)
+		--index = index + head.sizeof.int
+		advance_index(head.sizeof.int)
+		if low == 0 then --HACK
+			src_count = get_number(head.sizeof.int)
+			index = index + head.sizeof.int
+			
+		else
+			src_count = low
+			index = index + head.sizeof.int --HACK just skip
+		end
+	else
+		src_count = get_number(head.sizeof.size_t)
+		index = index + head.sizeof.size_t
+	end
+	
+	local str_index = index
+	local src =''
+	if src_count > 0 then
+		src = get_bytes(byte_stream,index,src_count)
+		index = index + src_count
+	end
+
+	debug_print(tabstr ..'.debug_source')
+	local bb_ = write_bytes(byte_stream,str_index - head.sizeof.size_t,head.sizeof.size_t,tabstr)
+	debug_verbose_print(tabstr .. pos((str_index - head.sizeof.size_t) -1 ),bb_,';strlen( debug_source ) = ' ..  src_count)
+	
+	local bb = write_bytes(byte_stream,str_index,src_count,tabstr)
+	--this would be better to left fixed or prefix lines with tabs
+	--for now left fixed
+	debug_print(tabstr .. pos(str_index-1),bb,';\n"' .. src .. '"')
+	
+	debug_print(tabstr ..'.debug_line_numbers')
+	local linecount, bb = get_number_and_byte_string(head.sizeof.int)
+	debug_verbose_print(tabstr ..pos(index-1),bb,';vector size ' .. linecount)
+	index = index + head.sizeof.int
+	
+	for i = 1, linecount do
+		local line, bb = get_number_and_byte_string(head.sizeof.int)
+		debug_print(tabstr .. pos(index-1),bb, ';pc['.. i ..'] = ' .. line )
+		index = index + head.sizeof.int
+	end
+	
+	local locals, bb = get_number_and_byte_string(head.sizeof.int)
+	debug_print(tabstr ..'.debug_locals')
+	debug_verbose_print(tabstr ..pos(index-1),bb,';vector size ' .. locals)
+	index = index + head.sizeof.int
+	
+
+	for i = 1, locals do
+		local str_sz, bb = get_number_and_byte_string(head.sizeof.size_t)
+		debug_verbose_print(tabstr .. pos(index-1),bb, ';strlen( localvars['.. tostring(i-1) ..'].name ) = ' ..str_sz)
+		index = index + head.sizeof.size_t
+
+		local str =	get_bytes(byte_stream,index,str_sz)
+		local bb = write_bytes(byte_stream,index,str_sz,tabstr)
+		debug_print(tabstr .. pos(index-1),bb, ';localvars['.. tostring(i-1) ..'].name = "' .. str ..'"' )
+		index = index + str_sz
+		
+		local startpc, bb = get_number_and_byte_string(head.sizeof.int)
+		debug_verbose_print(tabstr .. pos(index-1),bb, ';localvars['.. tostring(i-1) ..'].startpc = ' .. startpc )
+		index = index + head.sizeof.int
+		
+		local endpc, bb = get_number_and_byte_string(head.sizeof.int)
+		debug_verbose_print(tabstr .. pos(index-1),bb, ';localvars['.. tostring(i-1) ..'].endpc = ' .. endpc )
+		index = index + head.sizeof.int	
+	end
+	
+	debug_print(tabstr ..'.debug_upvalue_str')
+	local up_str, bb = get_number_and_byte_string(head.sizeof.int)
+	debug_verbose_print(tabstr ..pos(index-1),bb,';vector size ' .. up_str)
+	index = index + head.sizeof.int
+	
+	for i = 1, up_str do
+		local str_sz, bb = get_number_and_byte_string(head.sizeof.size_t)
+		debug_verbose_print(tabstr .. pos(index-1),bb, ';strlen( upvalue['.. tostring(i-1) ..']) = ' ..str_sz)
+		index = index + head.sizeof.size_t
+
+		local str =	get_bytes(byte_stream,index,str_sz)
+		local bb = write_bytes(byte_stream,index,str_sz,tabstr)
+		debug_print(tabstr .. pos(index-1),bb, ';upvalue['.. tostring(i-1) ..'] = "' .. str ..'"' )
+		index = index + str_sz
+	end
+	
+	return index
+end
+
+dump_function = function(byte_stream,index,head,tab_count)
+	
+	local tabstr = string.rep('  ',tab_count)
+ 	
+ 	local get_bytes = function(count)
+ 		return write_bytes(byte_stream,index,count)
+ 	end
+	
+	local get_number = function(count)
+	 	return head.get_number( byte_stream:sub(index,index + count - 1) )
+	end
+	
+	local p = print
+	
+	local printf_one_byte = function(str)
+		p(tabstr .. pos(index-1),get_bytes(1), str)
+		index = index + 1
+	end
+	
+	
+ 	p('\n'.. tabstr ..'.proto')
+ 	
+ 	local line_defined = get_number(head.sizeof.int)
+ 	local description = 'Lua' if line_defined == 0 then description = 'Main' end
+
+ 	
+ 	p(tabstr .. pos(index-1),get_bytes(head.sizeof.int),';line defined', line_defined ,description)
+ 	index = index + head.sizeof.int
+ 	p(tabstr .. pos(index-1),get_bytes(head.sizeof.int),';last line', get_number(head.sizeof.int) )
+ 	index = index + head.sizeof.int
+ 	printf_one_byte(';num params')
+ 	printf_one_byte(';is vaargs')
+ 	printf_one_byte(';max stack size')
+ 	
+ 	index = dump_code(byte_stream,index,head,tabstr)
+ 	index = dump_constants(byte_stream,index,head,tabstr)
+ 	index = dump_upvalues(byte_stream,index,head,tabstr)
+ 	index = dump_debug(byte_stream,index,head,tabstr)
+ 	p('\n')
+ 	return index
+end
+
+local dump = function(func)--,write_header)
+	local bs
+
+	if type(func) == 'string' then
+		bs = func
+	elseif type(func) == 'function' then
+		bs = string.dump(func)
+	else
+		assert(nil,'incorrect input ' .. tostring(type(func)) )
+	end
+	
+	local index, head = dump_header(bs)
+	index = dump_function(bs,index,head,0)
+end
+
+
+
+
+
+
+local usage = function()
+
+	print[[
+Program args:
+arg[1] filename
+-d debug information
+-v verbose (use in conjunction with '-d' for all output)
+if arg[1] ~= nil and arg[2] == 'raw' open a binary compiled Lua file and inspect it
+if arg[1] ~= nil  and arg[2] ~= 'raw' open the source file, string.dump and then inspect
+-i interactive mode
+]]
+end
+
+--usage()
+
+local handle_options = function ()
+	local f 
+	local err
+	
+	for _,v in ipairs(arg) do
+		if v == '-i' then
+			return nil, true
+		end
+	end
+	
+	if arg[1] then 
+		if arg[2] == 'raw' then
+			local fi = io.open(arg[1])
+			f = fi:read('*a')
+			fi:close()
+			local idx = 2
+			local len = #arg
+			while(idx <= len) do
+				local a = arg[idx]
+				if a == '-v' then is_verbose = true
+				elseif a == '-d' then debug_should_write = true 
+				end
+				idx =  idx + 1
+			end	
+		else
+			local idx = 2
+			local len = #arg
+			while(idx <= len) do
+				local a = arg[idx]
+				if a == '-v' then is_verbose = true
+				elseif a == '-d' then debug_should_write = true 
+				end
+				idx =  idx + 1
+			end	
+			f , err = loadfile(arg[1])
+			if not f then error(err) end
+		end
+	else
+
+		usage()
+		error 'program requires one or more arguments'
+	end
+
+	return f
+end
+local interactive_options = function()
+	local on_off= function(bool) if bool == true then return 'on' else return 'off' end end
+	print'-option [option] [value]'
+	print('option			:','possible values','current value')
+	print('verbose			:','on / off       ',on_off(is_verbose))
+	print('debug			:','on / off       ',on_off(debug_should_write))
+	print('reg replace		:','on / off       ',on_off(replace_reg_indexes))
+	print('hexpos			:','on / off       ',on_off(pos==hex_pos))
+	print('byte code width		:','4 / 8	       ',byte_code_width)
+	print('')
+end
+
+local run_code_and_debug = function(input)
+	local data, err = load(input)		
+	if not data then
+		print(err)
+		return false
+	else
+		dump(data)
+		return true
+	end
+end
+	
+local interactive_mode = function()
+	print 'interactive mode'
+	print"The first line which does not end in '>>' is the end of buffer"
+	print'"-iend" ends interactive mode'
+	local buffered_input = ''
+	local last_complete_buffer = ''
+	local input = io.read()
+	
+	while input ~= '-iend' do
+	
+		if input:match('^-option*') then
+			if input:match('^-options.*$') then
+				interactive_options()
+
+			elseif input:match('^-option.?verbose.?off.*$') then
+				is_verbose = false
+			elseif input:match('^-option.?verbose.?on.*$') then
+				is_verbose = true
+			elseif input:match('^-option.?debug.?off.*$') then	
+				debug_should_write = false
+			elseif input:match('^-option.?debug.?on.*$') then	
+				debug_should_write = true
+			elseif input:match('^-option.?all.?on.*$') then	
+				debug_should_write = true
+				is_verbose = true
+			elseif input:match('^-option.?all.?off.*$') then	
+				debug_should_write = false
+				is_verbose = false
+			elseif input:match('^-option.?reg.?replace.on.*$') then
+				replace_reg_indexes = true
+			elseif input:match('^-option.?reg.?replace.off.*$') then
+				replace_reg_indexes = false
+			elseif input:match('^-option.?hexpos.?on.*$') then
+				pos = hex_pos
+			elseif input:match('^-option.?hexpos.?off*$') then
+				pos = dec_pos
+			elseif input:match('^-option.?byte.?code.?width.?8.*$')then
+				byte_code_width = 8
+			elseif input:match('^-option.?byte.?code.?width.?4.*$')then
+				byte_code_width = 4
+			else 
+				print('\nunknown option or value')
+				print(input)
+				interactive_options()
+			end
+		elseif input:match('^-rerun*') then
+			run_code_and_debug(last_complete_buffer)
+		elseif input:match('.*>>$') then
+			buffered_input = buffered_input .. input:sub(1,#input-2) ..'\n'
+		else
+			local result = run_code_and_debug(buffered_input .. input)
+			if result == true then last_complete_buffer = buffered_input .. input end
+			buffered_input = ''		
+		end
+		input = io.read()
+	end
+	
+	os.exit()
+end
+
+
+
+
+local file_data, interactive = handle_options()
+if file_data then
+	dump(file_data)
+elseif interactive then
+	interactive_mode()
+else
+	error 'WTF!'
+end