class Metasm::WebAsm

Attributes

wasm_file[RW]

Public Class Methods

new(*args) click to toggle source
Calls superclass method
# File metasm/cpu/webasm/main.rb, line 12
def initialize(*args)
        super()
        @size = args.grep(Integer).first || 64
        @wasm_file = args.grep(ExeFormat).first
        @endianness = args.delete(:little) || args.delete(:big) || (@wasm_file ? @wasm_file.endianness : :little)
end

Public Instance Methods

abi_funcall() click to toggle source
# File metasm/cpu/webasm/decompile.rb, line 118
def abi_funcall
        @abi_funcall ||= { :changed => [] }
end
addop(name, bin, *args) click to toggle source
# File metasm/cpu/webasm/opcodes.rb, line 10
def addop(name, bin, *args)
        o = Opcode.new name, bin

        args.each { |a|
                if a == :mem
                        o.args << :uleb << :memoff
                elsif @valid_props[a]
                        o.props[a] = true
                else
                        o.args << a
                end
        }

        @opcode_list << o
end
backtrace_is_function_return(expr, di=nil) click to toggle source
# File metasm/cpu/webasm/decode.rb, line 297
def backtrace_is_function_return(expr, di=nil)
        expr and Expression[expr] == Expression[Indirection[:callstack, 8]]
end
backtrace_is_stack_address(expr) click to toggle source
# File metasm/cpu/webasm/decode.rb, line 313
def backtrace_is_stack_address(expr)
        ([:local_base, :opstack] & Expression[expr].expr_externals).first
end
backtrace_update_function_binding(dasm, faddr, f, retaddrlist, *wantregs) click to toggle source
# File metasm/cpu/webasm/decode.rb, line 309
def backtrace_update_function_binding(dasm, faddr, f, retaddrlist, *wantregs)
        f.backtrace_binding = { :callstack => Expression[:callstack, :-, 8] }
end
build_bin_lookaside() click to toggle source
# File metasm/cpu/webasm/decode.rb, line 11
def build_bin_lookaside
        lookaside = (0..0xff).inject({}) { |h, i| h.update i => [] }
        opcode_list.each { |op|
                lookaside[op.bin] << op
        }
        lookaside
end
dbg_end_stepout(dbg, addr, di) click to toggle source
# File metasm/cpu/webasm/debug.rb, line 27
def dbg_end_stepout(dbg, addr, di)
        di and di.opcode.props[:stopexec] and (di.opcode.name == 'return' or di.opcode.name == 'end')
end
dbg_register_list() click to toggle source
# File metasm/cpu/webasm/debug.rb, line 11
def dbg_register_list
        @dbg_register_list ||= [:pc, :opstack, :mem, :local_base]
end
dbg_resolve_pc(di, fbd, pc_reg, dbg_ctx) click to toggle source
Calls superclass method
# File metasm/cpu/webasm/debug.rb, line 15
def dbg_resolve_pc(di, fbd, pc_reg, dbg_ctx)
        case di.opcode.name
        when 'br_if', 'if'
                if dbg_ctx.resolve(Indirection[:opstack, 8]) != 0
                        fbd[pc_reg] = (di.opcode.name == 'if' ? di.next_addr : di.misc[:x])
                else
                        fbd[pc_reg] = (di.opcode.name == 'if' ? di.misc[:x] : di.next_addr)
                end
        else return super(di, fbd, pc_reg, dbg_ctx)
        end
end
decode_br_table(edata) click to toggle source
# File metasm/cpu/webasm/decode.rb, line 150
def decode_br_table(edata)
        count = decode_uleb(edata)
        ary = []
        count.times { ary << decode_uleb(edata) }
        default = decode_uleb(edata)
        BrTable.new(ary, default)
end
decode_c_function_prototype(cp, sym, orig=nil) click to toggle source
# File metasm/cpu/webasm/decode.rb, line 317
def decode_c_function_prototype(cp, sym, orig=nil)
        disassembler_default_func
end
decode_findopcode(edata) click to toggle source
# File metasm/cpu/webasm/decode.rb, line 102
def decode_findopcode(edata)
        di = DecodedInstruction.new(self)
        val = edata.decode_imm(:u8, @endianness)
        di if di.opcode = bin_lookaside[val].first
end
decode_instr_interpret(di, addr) click to toggle source
# File metasm/cpu/webasm/decode.rb, line 130
def decode_instr_interpret(di, addr)
        case di.opcode.name
        when 'call'
                fnr = di.instruction.args.first.reduce
                di.misc ||= {}
                di.misc[:tg_func_nr] = fnr
                if f = @wasm_file.get_function_nr(fnr)
                        tg = f[:init_offset] ? f[:init_offset] : "#{f[:module]}_#{f[:field]}"
                        di.instruction.args[0] = Expression[tg]
                        di.misc[:x] = [tg]
                else
                        di.misc[:x] = [:default]
                end
        when 'call_indirect'
                di.misc ||= {}
                di.misc[:x] = [:default]
        end
        di
end
decode_instr_op(edata, di) click to toggle source
# File metasm/cpu/webasm/decode.rb, line 108
def decode_instr_op(edata, di)
        before_ptr = edata.ptr
        op = di.opcode
        di.instruction.opname = op.name

        op.args.each { |a|
                di.instruction.args << case a
                when :f32; Expression[edata.decode_imm(:u32, @endianness)]
                when :f64; Expression[edata.decode_imm(:u64, @endianness)]
                when :memoff; Memref.new(decode_uleb(edata))
                when :uleb; Expression[decode_uleb(edata)]
                when :sleb; Expression[decode_uleb(edata, true)]
                when :blocksig; BlockSignature.new(decode_uleb(edata, true))
                when :br_table; decode_br_table(edata)
                else raise SyntaxError, "Internal error: invalid argument #{a} in #{op.name}"
                end
        }

        di.bin_length = 1 + edata.ptr - before_ptr
        di
end
decode_instruction_context(dasm, edata, di_addr, ctx) click to toggle source

reuse the instructions from the cache

# File metasm/cpu/webasm/decode.rb, line 93
def decode_instruction_context(dasm, edata, di_addr, ctx)
        ctx ||= disassemble_init_context(dasm, di_addr)
        if not ctx[:di_cache][di_addr]
                di_addr = dasm.normalize(di_addr)
                disassemble_init_context(dasm, di_addr)
        end
        ctx[:di_cache][di_addr]
end
decode_uleb(ed, signed=false) click to toggle source
# File metasm/cpu/webasm/decode.rb, line 19
def decode_uleb(ed, signed=false)
        v = s = 0
        while s < 10*7
                b = ed.read(1).unpack('C').first.to_i
                v |= (b & 0x7f) << s
                s += 7
                break if (b&0x80) == 0
        end
        v = Expression.make_signed(v, s) if signed
        v
end
decompile_blocks(dcmp, myblocks, deps, func, nextaddr = nil) click to toggle source
# File metasm/cpu/webasm/decompile.rb, line 150
def decompile_blocks(dcmp, myblocks, deps, func, nextaddr = nil)
        func_entry = myblocks.first[0]
        if w_func = @wasm_file.function_body.find { |fb| fb[:init_offset] == func_entry }
        elsif g = @wasm_file.global.find { |gg| gg[:init_offset] == func_entry }
                w_func = { :local_var => [], :type => { :params => [], :ret => [g[:type]] } }
        elsif (@wasm_file.element.to_a + @wasm_file.data.to_a).find { |gg| gg[:init_offset] == func_entry }
                w_func = { :local_var => [], :type => { :params => [], :ret => ['i32'] } }
        end
        scope = func.initializer
        func.type.args.each { |a| scope.symbol[a.name] = a }
        stmts = scope.statements

        local = []
        w_func[:type][:params].each { |t|
                local << C::Variable.new("arg_#{local.length}", wasm_type_to_type(t))
                scope.symbol[local.last.name] = local.last
                func.type.args << local.last
        }
        w_func[:local_var].each { |t|
                local << C::Variable.new("var_#{local.length}", wasm_type_to_type(t))
                scope.symbol[local.last.name] = local.last
                local.last.initializer = C::CExpression[0]
                stmts << C::Declaration.new(local.last)
        }

        opstack = {}

        # *(_int32*)(local_base+16) => 16
        ce_ptr_offset = lambda { |ee, base|
                if ee.kind_of?(C::CExpression) and ee.op == :* and not ee.lexpr and ee.rexpr.kind_of?(C::CExpression) and
                                not ee.rexpr.op and ee.rexpr.rexpr.kind_of?(C::CExpression)
                        if not ee.rexpr.rexpr.op and ee.rexpr.rexpr.rexpr.kind_of?(C::Variable) and ee.rexpr.rexpr.rexpr.name == base
                                0
                        elsif ee.rexpr.rexpr.lexpr.kind_of?(C::Variable) and ee.rexpr.rexpr.lexpr.name == base and
                                        ee.rexpr.rexpr.rexpr.kind_of?(C::CExpression) and not ee.rexpr.rexpr.rexpr.op and ee.rexpr.rexpr.rexpr.rexpr.kind_of?(::Integer)
                                if ee.rexpr.rexpr.op == :+
                                        ee.rexpr.rexpr.rexpr.rexpr
                                elsif ee.rexpr.rexpr.op == :-
                                        -ee.rexpr.rexpr.rexpr.rexpr
                                end
                        end
                end
        }
        opstack_idx = -1
        ce_local_offset = lambda { |ee| ce_ptr_offset[ee, 'local_base'] }
        ce_opstack_offset = lambda { |ee| ce_ptr_offset[ee, 'frameptr'] }

        di_addr = nil

        # Expr => CExpr
        ce = lambda { |*e|
                c_expr = dcmp.decompile_cexpr(Expression[Expression[*e].reduce], scope)
                dcmp.walk_ce(c_expr, true) { |ee|
                        if ee.rexpr.kind_of?(::Array)
                                # funcall arglist
                                ee.rexpr.map! { |eee|
                                        if loff = ce_local_offset[eee]
                                                C::CExpression[local[loff/8]]
                                        elsif soff = ce_opstack_offset[eee]
                                                C::CExpression[opstack[-soff/8]]
                                        else
                                                eee
                                        end
                                }
                        end
                        if loff = ce_local_offset[ee.lexpr]
                                ee.lexpr = local[loff/8]
                        end
                        if loff = ce_local_offset[ee.rexpr]
                                ee.rexpr = local[loff/8]
                                ee.rexpr = C::CExpression[ee.rexpr] if not ee.op and ee.type.pointer?
                        end
                        if soff = ce_opstack_offset[ee.rexpr]
                                # must do soff.rexpr before lexpr in case of reaffectation !
                                ee.rexpr = opstack[-soff/8]
                                ee.rexpr = C::CExpression[ee.rexpr] if not ee.op and ee.type.pointer?
                        end
                        if soff = ce_opstack_offset[ee.lexpr]
                                if ee.op == :'='
                                        # affectation: create a new variable
                                        varname = "loc_#{opstack_idx += 1}"
                                        ne = C::Variable.new(varname, wasm_type_to_type("i#{8*dcmp.sizeof(ee.lexpr)}"))
                                        scope.symbol[varname] = ne
                                        stmts << C::Declaration.new(ne)
                                        opstack[-soff/8] = ne
                                end
                                ee.lexpr = opstack[-soff/8]
                        end
                }
                ret = if loff = ce_local_offset[c_expr]
                        C::CExpression[local[loff/8]]
                elsif soff = ce_opstack_offset[c_expr]
                        C::CExpression[opstack[-soff/8]]
                else
                        c_expr
                end
                dcmp.walk_ce(ret) { |ee| ee.with_misc :di_addr => di_addr if di_addr }
                ret
        }


        blocks_toclean = myblocks.dup
        until myblocks.empty?
                b, to = myblocks.shift
                if l = dcmp.dasm.get_label_at(b)
                        stmts << C::Label.new(l)
                end

                # go !
                di_list = dcmp.dasm.decoded[b].block.list.dup
                di_list.each { |di|
                        di_addr = di.address
                        if di.opcode.name == 'if' or di.opcode.name == 'br_if'
                                n = dcmp.backtrace_target(get_xrefs_x(dcmp.dasm, di).first, di.address)
                                bd = get_fwdemu_binding(di)
                                if di.opcode.name == 'if'
                                        cc = ce[:!, bd[:flag]]
                                else
                                        cc = ce[bd[:flag]]
                                end
                                stmts << C::If.new(C::CExpression[cc], C::Goto.new(n).with_misc(:di_addr => di.address)).with_misc(:di_addr => di.address)
                                to.delete dcmp.dasm.normalize(n)
                        elsif (di.opcode.name == 'end' or di.opcode.name == 'return') and di.opcode.props[:stopexec]
                                fsig = w_func[:type]
                                rettype = wasm_type_to_type(fsig[:ret].first) if fsig[:ret] and fsig[:ret].first
                                if not fsig[:ret].empty?
                                        off = di.misc[:dcmp_stackoff] || -8
                                        ret = C::CExpression[ce[Indirection[[:frameptr, :+, off], dcmp.sizeof(rettype)]]]
                                end
                                stmts << C::Return.new(ret).with_misc(:di_addr => di.address)
                        elsif (di.opcode.name == 'end' or di.opcode.name == 'else') and di.misc[:dcmp_stackoff] and di.misc[:end_of]
                                # end of block returning a value: store the value in a real variable instead of the autogenerated local
                                # so that if { } else {} both update the same var
                                start = di.misc[:end_of]
                                start_rettype = start.instruction.args.first.to_s
                                if start_rettype != 'none'
                                        retsz = dcmp.sizeof(wasm_type_to_type(start_rettype))
                                        off = di.misc[:dcmp_stackoff]
                                        if not start.misc[:dcmp_retval] or not scope.symbol[start.misc[:dcmp_retval]]
                                                stmts << C::CExpression[ce[Indirection[[:frameptr, :+, off], retsz], :'=', Indirection[[:frameptr, :+, off], retsz]]]
                                                start.misc[:dcmp_retval] = stmts.last.lexpr.name
                                        else
                                                stmts << C::CExpression[ce[scope.symbol[start.misc[:dcmp_retval]], :'=', Indirection[[:frameptr, :+, off], retsz]]]
                                        end
                                end
                        elsif di.opcode.name == 'call'
                                tg = di.misc[:x].first
                                raise "no call target for #{di}" if not tg
                                tg = dcmp.dasm.auto_label_at(tg, 'sub') if dcmp.dasm.get_section_at(tg)
                                f = dcmp.c_parser.toplevel.symbol[tg]
                                raise "no global function #{tg} for #{di}" if not f

                                args = []
                                bd = get_fwdemu_binding(di)
                                i = 0
                                while bd_arg = bd["param_#{i}"]
                                        args << ce[bd_arg]
                                        i += 1
                                end
                                e = C::CExpression[f, :funcall, args].with_misc(:di_addr => di.address)
                                if bd_ret = bd.index(Expression["ret_0"])
                                        e = ce[bd_ret, :'=', e]
                                end
                                stmts << e
                        elsif di.opcode.name == 'call_indirect'
                                args = []
                                bd = get_fwdemu_binding(di)
                                wt = @wasm_file.type[di.instruction.args.first.reduce]
                                fptr = C::CExpression[[dcmp.c_parser.toplevel.symbol['indirect_calltable'], :[], ce[bd['func_idx']]], wasm_type_to_type(wt)]
                                i = 0
                                while bd_arg = bd["param_#{i}"]
                                        args << ce[bd_arg]
                                        i += 1
                                end
                                e = C::CExpression[fptr, :funcall, args].with_misc(:di_addr => di.address)
                                if bd_ret = bd.index(Expression["ret_0"])
                                        e = ce[bd_ret, :'=', e]
                                end
                                stmts << e
                        else
                                bd = get_fwdemu_binding(di)
                                if di.backtrace_binding[:incomplete_binding]
                                        stmts << C::Asm.new(di.instruction.to_s, nil, nil, nil, nil, nil).with_misc(:di_addr => di.address)
                                else
                                        bd.each { |k, v|
                                                next if k == :opstack
                                                e = ce[k, :'=', v]
                                                stmts << e if not e.kind_of?(C::Variable)        # [:eflag_s, :=, :unknown].reduce
                                        }
                                end
                        end
                        di_addr = nil
                }

                case to.length
                when 0
                        if not myblocks.empty? and not stmts.last.kind_of?(C::Return)
                                puts "  block #{Expression[b]} has no to and don't end in ret"
                        end
                when 1
                        if (myblocks.empty? ? nextaddr != to[0] : myblocks.first.first != to[0])
                                stmts << C::Goto.new(dcmp.dasm.auto_label_at(to[0], 'unknown_goto'))
                        end
                else
                        puts "  block #{Expression[b]} with multiple to"
                end
        end

        # cleanup di.bt_binding (we set :frameptr etc in those, this may confuse the dasm)
        blocks_toclean.each { |b_, to_|
                dcmp.dasm.decoded[b_].block.list.each { |di|
                        di.backtrace_binding = nil
                }
        }
end
decompile_check_abi(dcmp, entry, func) click to toggle source
# File metasm/cpu/webasm/decompile.rb, line 366
def decompile_check_abi(dcmp, entry, func)
        scope = func.initializer
        @wasm_file.function_body.to_a.each { |fb|
                next if fb[:init_offset] != entry
                w_type = wasm_type_to_type(fb[:type])
                func.type.type = w_type.type
                if func.type.args.length > w_type.args.length
                        # detected an argument that is actually a local variable, move into func scope
                        while a = func.type.args.delete_at(w_type.args.length)
                                if a.has_attribute('unused')
                                        scope.symbol.delete a.name
                                else
                                        a.initializer = C::CExpression[0]
                                        scope.statements[0, 0] = [C::Declaration.new(a)]
                                end
                        end
                end
        }
end
decompile_func_finddeps(dcmp, blocks, func) click to toggle source
# File metasm/cpu/webasm/decompile.rb, line 146
def decompile_func_finddeps(dcmp, blocks, func)
        {}
end
decompile_func_finddeps_di(dcmp, func, di, a, w) click to toggle source
# File metasm/cpu/webasm/decompile.rb, line 143
def decompile_func_finddeps_di(dcmp, func, di, a, w)
end
decompile_init(dcmp) click to toggle source
# File metasm/cpu/webasm/decompile.rb, line 25
def decompile_init(dcmp)
        mem = dcmp.c_parser.toplevel.symbol['mem'] = C::Variable.new('mem', C::Pointer.new(C::BaseType.new(:char)))
        mem.storage = :static
        dcmp.c_parser.toplevel.statements << C::Declaration.new(mem)

        global_idx = 0
        @wasm_file.import.to_a.each { |i|
                case i[:kind]
                when 'global'
                        global_idx += 1
                        var = C::Variable.new
                        var.name = '%s_%s' % [i[:module], i[:field]]
                        var.type = C::Array.new(wasm_type_to_type(i[:type]), 1)
                        var.storage = :extern
                        dcmp.c_parser.toplevel.symbol[var.name] = var
                        dcmp.c_parser.toplevel.statements << C::Declaration.new(var)
                when 'function'
                        var = C::Variable.new
                        var.name = '%s_%s' % [i[:module], i[:field]]
                        var.type = wasm_type_to_type(i[:type])
                        var.storage = :extern
                        dcmp.c_parser.toplevel.symbol[var.name] = var
                        dcmp.c_parser.toplevel.statements << C::Declaration.new(var)
                end
        }

        @wasm_file.global.to_a.each_with_index { |g, idx|
                g_name = 'global_%d' % global_idx
                global_idx += 1
                var = C::Variable.new
                var.name = g_name
                var.type = C::Array.new(wasm_type_to_type(g[:type]), 1)
                var.storage = :static
                dcmp.c_parser.toplevel.symbol[var.name] = var
                dcmp.c_parser.toplevel.statements << C::Declaration.new(var)

                # decompile initializers
                g_init_name = g_name + '_init'
                dcmp.dasm.disassemble(g_init_name)
                dcmp.decompile_func(g_init_name)
                if init = dcmp.c_parser.toplevel.symbol[g_init_name] and init.initializer.kind_of?(C::Block) and
                                init.initializer.statements.first.kind_of?(C::Return)
                        dcmp.c_parser.toplevel.symbol[g_name].initializer = [ init.initializer.statements.first.value ]
                        dcmp.c_parser.toplevel.symbol.delete(g_init_name)
                        dcmp.c_parser.toplevel.statements.delete_if { |st| st.kind_of?(C::Declaration) and st.var.name == g_init_name }
                end
        }

        @wasm_file.table.to_a.each_with_index { |t, idx|
                break if idx > 0
                t_name = 'indirect_calltable'
                var = C::Variable.new
                var.name = t_name
                sz = t[:limits][:initial_size]
                var.type = C::Array.new(C::Pointer.new(wasm_type_to_type(t[:type])), sz)
                var.storage = :static
                dcmp.c_parser.toplevel.symbol[var.name] = var
                dcmp.c_parser.toplevel.statements << C::Declaration.new(var)
                var.initializer = [C::CExpression[0]] * sz

                # initializer
                @wasm_file.element.to_a.each_with_index { |e, eidx|
                        next if e[:table_index] != idx
                        # address of the code that evals the index at which to place the elements inside the table
                        e_init_name = "element_#{eidx}_init_addr"
                        dcmp.dasm.disassemble(e_init_name)
                        dcmp.decompile_func(e_init_name)
                        if init = dcmp.c_parser.toplevel.symbol[e_init_name] and init.initializer.kind_of?(C::Block) and
                                        init.initializer.statements.first.kind_of?(C::Return)
                                eoff = init.initializer.statements.first.value.reduce(dcmp.c_parser)
                                dcmp.c_parser.toplevel.symbol.delete(e_init_name)
                                dcmp.c_parser.toplevel.statements.delete_if { |st| st.kind_of?(C::Declaration) and st.var.name == e_init_name }
                                e[:elems].each_with_index { |ev, vidx|
                                        # table 0 is the only table in a wasm file and contains a list of function indexes used with the call_indirect asm instruction
                                        # e_init_name gives the index at which we should put e[:elems], and we convert the func indexes into C names
                                        vidx += eoff
                                        if vidx >= sz or vidx < 0
                                                puts "W: initializing indirect_calltable, would put #{ev} beyond end of table (#{vidx} > #{sz})"
                                                next
                                        end
                                        if not tg_func = @wasm_file.get_function_nr(ev)
                                                puts "W: initializing indirect_calltable, bad func index #{ev}"
                                                next
                                        end
                                        funcname = dcmp.dasm.get_label_at(tg_func[:init_offset]) || "func_at_#{'%x' % tg_func[:init_offset]}"
                                        # XXX should decompile funcname now ?
                                        var.initializer[vidx] = C::CExpression[:&, C::Variable.new(funcname)]
                                }
                        end
                }
        }
end
decompile_makestackvars(dasm, funcstart, blocks) { |block| ... } click to toggle source
# File metasm/cpu/webasm/decompile.rb, line 122
def decompile_makestackvars(dasm, funcstart, blocks)
        @decomp_mkstackvars_terminals = [:frameptr, :local_base, :mem]
        oldbd = {}
        oldbd[funcstart] = dasm.address_binding[funcstart]
        dasm.address_binding[funcstart] = { :opstack => Expression[:frameptr] }
        blocks.each { |block|
                oldbd[block.address] = dasm.address_binding[block.address]
                stkoff = dasm.backtrace(:opstack, block.address, :snapshot_addr => funcstart)
                dasm.address_binding[block.address] = { :opstack => Expression[:frameptr, :+, stkoff[0]-:frameptr] }
                yield block
                # store frameptr offset at each 'end' 'return' or 'else' instruction
                if di = block.list.last and %w[end return else].include?(di.opcode.name)
                        stkoff = dasm.backtrace(:opstack, di.address, :snapshot_addr => funcstart)
                        if stkoff.length == 1 and (stkoff[0] - :frameptr).kind_of?(::Integer)
                                di.misc[:dcmp_stackoff] = stkoff[0] - :frameptr
                        end
                end
        }
        oldbd.each { |a, b| b ? dasm.address_binding[a] = b : dasm.address_binding.delete(a) }
end
disassemble_init_context(dasm, addr) click to toggle source

when starting disassembly, pre-decode all instructions until the final 'end' and fixup the xrefs (if/block/loop…)

# File metasm/cpu/webasm/decode.rb, line 32
def disassemble_init_context(dasm, addr)
        dasm.misc ||= {}
        dasm.misc[:cpu_context] ||= {}
        cache = dasm.misc[:cpu_context][:di_cache] ||= {}
        addr = dasm.normalize(addr)
        return dasm.misc[:cpu_context] if cache[addr]

        code_start = addr
        stack = [[]]
        set_misc_x = lambda { |di, tg| di.misc[:x] ||= [] ; di.misc[:x] |= [tg] }
        while di = dasm.disassemble_instruction(addr)
                cache[addr] = di
                di.misc ||= {}
                di.misc[:code_start] = code_start
                case di.opcode.name
                when 'if', 'loop', 'block'
                        stack << [di]
                when 'else'
                        raise "bad #{di} #{stack.last.inspect}" if stack.last.empty? or stack.last.last.opcode.name != 'if'
                        stack.last.each { |ddi| set_misc_x[ddi, di.next_addr] }     # 'if' points past here
                        di.misc[:end_of] = stack.last[0]    # store matching 'if'
                        stack.last[0] = di  # 'else' replace 'if'
                when 'br', 'br_if', 'br_table'
                        if di.opcode.name == 'br_table'
                                depths = di.instruction.args.first.ary.uniq | [di.instruction.args.first.default]
                        else
                                depths = [di.instruction.args.first.reduce]
                        end
                        depths.each { |depth|
                                tg = stack[-depth-1] # XXX skip if/else in the stack ?
                                raise "bad #{di} (#{stack.length})" if not tg
                                if tg.first and tg.first.opcode.name == 'loop'
                                        set_misc_x[di, tg.first.address]
                                else
                                        tg << di
                                end
                        }
                when 'end'
                        dis = stack.pop
                        dis.each { |ddi| set_misc_x[ddi, di.next_addr] if ddi.opcode.name != 'loop' and ddi.opcode.name != 'block' }
                        if stack.empty?
                                # stack empty: end of func
                                di.opcode = @opcode_list.find { |op| op.name == 'end' and op.props[:stopexec] }
                                break
                        else
                                if dis.first
                                        di.misc[:end_of] = dis.first      # store matching loop/block/if
                                        if dis.first.opcode.name == 'else'
                                                di.misc[:end_of] = dis.first.misc[:end_of]       # else patched stack.last, recover original 'if'
                                        end
                                end
                                di.opcode = @opcode_list.find { |op| op.name == 'end' and not op.props[:stopexec] }
                        end
                end
                addr = di.next_addr
        end

        dasm.misc[:cpu_context]
end
disassembler_default_func() click to toggle source
# File metasm/cpu/webasm/decode.rb, line 301
def disassembler_default_func
        df = DecodedFunction.new
        ra = Indirection[:callstack, 8]
        df.backtracked_for << BacktraceTrace.new(ra, :default, ra, :x, nil)
        df.backtrace_binding = { :callstack => Expression[:callstack, :-, 8] }
        df
end
encode_instr_op(program, i, op) click to toggle source
# File metasm/cpu/webasm/encode.rb, line 83
def encode_instr_op(program, i, op)
        ed = EncodedData.new([op.bin].pack('C*'))
        op.args.zip(i.args).each { |oa, ia|
                case oa
                when :f32; ed << ia.encode(:u32, @endianness)
                when :f64; ed << ia.encode(:u64, @endianness)
                when :memoff; ed << encode_uleb(ia.off)
                when :uleb; ed << encode_uleb(ia)
                when :sleb; ed << encode_uleb(ia, true)
                when :blocksig
                        ia = ia.id if ia.kind_of?(BlockSignature)
                        ed << encode_uleb(ia, true)
                when :br_table
                        ed << encode_uleb(ia.ary.length)
                        ia.ary.each { |a| ed << encode_uleb(a) }
                        ed << encode_uleb(ia.default)
                end
        }
        ed
end
encode_uleb(val, signed=false) click to toggle source
# File metasm/cpu/webasm/encode.rb, line 71
def encode_uleb(val, signed=false)
        # TODO labels ?
        v = Expression[val].reduce
        raise "need numeric value for #{val}" if not v.kind_of?(::Integer)
        out = EncodedData.new
        while v > 0x7f or v < -0x40 or (signed and v > 0x3f)
                out << Expression[0x80 | (v&0x7f)].encode(:u8, @endianness)
                v >>= 7
        end
        out << Expression[v & 0x7f].encode(:u8, @endianness)
end
fix_fwdemu_binding(di, fbd) click to toggle source
# File metasm/cpu/webasm/decode.rb, line 271
def fix_fwdemu_binding(di, fbd)
        ori = fbd
        fbd = {}
        ori.each { |k, v|
                if k.kind_of?(Indirection) and not k.target.lexpr.kind_of?(Indirection)
                        # dont fixup store8 etc
                        fbd[k.bind(:opstack => ori[:opstack]).reduce_rec] = v
                else
                        fbd[k] = v
                end
        }
        fbd
end
get_backtrace_binding(di) click to toggle source
# File metasm/cpu/webasm/decode.rb, line 262
def get_backtrace_binding(di)
        if binding = backtrace_binding[di.opcode.name]
                binding[di] || {}
        else
                puts "unhandled instruction to backtrace: #{di}" if $VERBOSE
                {:incomplete_binding => Expression[1]}
        end
end
get_xrefs_x(dasm, di) click to toggle source
# File metasm/cpu/webasm/decode.rb, line 285
def get_xrefs_x(dasm, di)
        if di.opcode.props[:stopexec]
                case di.opcode.name
                when 'return', 'end'
                        return [Indirection[:callstack, 8]]
                end
        end
        return [] if not di.opcode.props[:setip]

        di.misc ? [di.misc[:x]].flatten : []
end
init() click to toggle source
# File metasm/cpu/webasm/opcodes.rb, line 26
def init
        @opcode_list = []
        @valid_props = { :setip => true, :stopexec => true, :saveip => true }

        addop 'unreachable',   0x00, :stopexec
        addop 'nop',           0x01
        addop 'block',         0x02, :blocksig                # arg = signature (block_type)
        addop 'loop',          0x03, :blocksig                # ^
        addop 'if',            0x04, :blocksig, :setip        # ^
        addop 'else',          0x05, :setip, :stopexec
        addop 'end',           0x0b, :stopexec                # end of function (default with no function context)
        addop 'end',           0x0b                   # end of if/else/block/loop
        addop 'br',            0x0c, :uleb, :setip, :stopexec # arg = depth to break up to
        addop 'br_if',         0x0d, :uleb, :setip
        addop 'br_table',      0x0e, :br_table, :setip, :stopexec
        addop 'return',        0x0f, :stopexec
        addop 'call',          0x10, :uleb, :setip, :saveip, :stopexec        # function index
        addop 'call_indirect', 0x11, :uleb, :uleb, :setip, :saveip, :stopexec # type index for target function signature ; table index where the function indexes come from (fixed 0)

        addop 'drop',   0x1a
        addop 'select', 0x1b

        addop 'get_local',  0x20, :uleb
        addop 'set_local',  0x21, :uleb
        addop 'tee_local',  0x22, :uleb
        addop 'get_global', 0x23, :uleb
        addop 'set_global', 0x24, :uleb

        addop 'i32.load', 0x28, :mem
        addop 'i64.load', 0x29, :mem
        addop 'f32.load', 0x2a, :mem
        addop 'f64.load', 0x2b, :mem
        addop 'i32.load8_s',  0x2c, :mem
        addop 'i32.load8_u',  0x2d, :mem
        addop 'i32.load16_s', 0x2e, :mem
        addop 'i32.load16_u', 0x2f, :mem
        addop 'i64.load8_s',  0x30, :mem
        addop 'i64.load8_u',  0x31, :mem
        addop 'i64.load16_s', 0x32, :mem
        addop 'i64.load16_u', 0x33, :mem
        addop 'i64.load32_s', 0x34, :mem
        addop 'i64.load32_u', 0x35, :mem
        addop 'i32.store',   0x36, :mem
        addop 'i64.store',   0x37, :mem
        addop 'f32.store',   0x38, :mem
        addop 'f64.store',   0x39, :mem
        addop 'i32.store8',  0x3a, :mem
        addop 'i32.store16', 0x3b, :mem
        addop 'i64.store8',  0x3c, :mem
        addop 'i64.store16', 0x3d, :mem
        addop 'i64.store32', 0x3e, :mem
        addop 'current_memory', 0x3f, :uleb   # resv1
        addop 'grow_memory', 0x40, :uleb      # resv1

        addop 'i32.const', 0x41, :sleb
        addop 'i64.const', 0x42, :sleb
        addop 'f32.const', 0x43, :f32
        addop 'f64.const', 0x44, :f64

        addop 'i32.eqz', 0x45          
        addop 'i32.eq', 0x46            
        addop 'i32.ne', 0x47            
        addop 'i32.lt_s', 0x48                
        addop 'i32.lt_u', 0x49                
        addop 'i32.gt_s', 0x4a                
        addop 'i32.gt_u', 0x4b                
        addop 'i32.le_s', 0x4c                
        addop 'i32.le_u', 0x4d                
        addop 'i32.ge_s', 0x4e                
        addop 'i32.ge_u', 0x4f                
        addop 'i64.eqz', 0x50          
        addop 'i64.eq', 0x51            
        addop 'i64.ne', 0x52            
        addop 'i64.lt_s', 0x53                
        addop 'i64.lt_u', 0x54                
        addop 'i64.gt_s', 0x55                
        addop 'i64.gt_u', 0x56                
        addop 'i64.le_s', 0x57                
        addop 'i64.le_u', 0x58                
        addop 'i64.ge_s', 0x59                
        addop 'i64.ge_u', 0x5a                
        addop 'f32.eq', 0x5b            
        addop 'f32.ne', 0x5c            
        addop 'f32.lt', 0x5d            
        addop 'f32.gt', 0x5e            
        addop 'f32.le', 0x5f            
        addop 'f32.ge', 0x60            
        addop 'f64.eq', 0x61            
        addop 'f64.ne', 0x62            
        addop 'f64.lt', 0x63            
        addop 'f64.gt', 0x64            
        addop 'f64.le', 0x65            
        addop 'f64.ge', 0x66            

        addop 'i32.clz', 0x67          
        addop 'i32.ctz', 0x68          
        addop 'i32.popcnt', 0x69            
        addop 'i32.add', 0x6a          
        addop 'i32.sub', 0x6b          
        addop 'i32.mul', 0x6c          
        addop 'i32.div_s', 0x6d              
        addop 'i32.div_u', 0x6e              
        addop 'i32.rem_s', 0x6f              
        addop 'i32.rem_u', 0x70              
        addop 'i32.and', 0x71          
        addop 'i32.or', 0x72            
        addop 'i32.xor', 0x73          
        addop 'i32.shl', 0x74          
        addop 'i32.shr_s', 0x75              
        addop 'i32.shr_u', 0x76              
        addop 'i32.rotl', 0x77                
        addop 'i32.rotr', 0x78                
        addop 'i64.clz', 0x79          
        addop 'i64.ctz', 0x7a          
        addop 'i64.popcnt', 0x7b            
        addop 'i64.add', 0x7c          
        addop 'i64.sub', 0x7d          
        addop 'i64.mul', 0x7e          
        addop 'i64.div_s', 0x7f              
        addop 'i64.div_u', 0x80              
        addop 'i64.rem_s', 0x81              
        addop 'i64.rem_u', 0x82              
        addop 'i64.and', 0x83          
        addop 'i64.or', 0x84            
        addop 'i64.xor', 0x85          
        addop 'i64.shl', 0x86          
        addop 'i64.shr_s', 0x87              
        addop 'i64.shr_u', 0x88              
        addop 'i64.rotl', 0x89                
        addop 'i64.rotr', 0x8a                
        addop 'f32.abs', 0x8b          
        addop 'f32.neg', 0x8c          
        addop 'f32.ceil', 0x8d                
        addop 'f32.floor', 0x8e              
        addop 'f32.trunc', 0x8f              
        addop 'f32.nearest', 0x90          
        addop 'f32.sqrt', 0x91                
        addop 'f32.add', 0x92          
        addop 'f32.sub', 0x93          
        addop 'f32.mul', 0x94          
        addop 'f32.div', 0x95          
        addop 'f32.min', 0x96          
        addop 'f32.max', 0x97          
        addop 'f32.copysign', 0x98        
        addop 'f64.abs', 0x99          
        addop 'f64.neg', 0x9a          
        addop 'f64.ceil', 0x9b                
        addop 'f64.floor', 0x9c              
        addop 'f64.trunc', 0x9d              
        addop 'f64.nearest', 0x9e          
        addop 'f64.sqrt', 0x9f                
        addop 'f64.add', 0xa0          
        addop 'f64.sub', 0xa1          
        addop 'f64.mul', 0xa2          
        addop 'f64.div', 0xa3          
        addop 'f64.min', 0xa4          
        addop 'f64.max', 0xa5          
        addop 'f64.copysign', 0xa6        

        addop 'i32.wrap/i64', 0xa7        
        addop 'i32.trunc_s/f32', 0xa8          
        addop 'i32.trunc_u/f32', 0xa9          
        addop 'i32.trunc_s/f64', 0xaa          
        addop 'i32.trunc_u/f64', 0xab          
        addop 'i64.extend_s/i32', 0xac                
        addop 'i64.extend_u/i32', 0xad                
        addop 'i64.trunc_s/f32', 0xae          
        addop 'i64.trunc_u/f32', 0xaf          
        addop 'i64.trunc_s/f64', 0xb0          
        addop 'i64.trunc_u/f64', 0xb1          
        addop 'f32.convert_s/i32', 0xb2              
        addop 'f32.convert_u/i32', 0xb3              
        addop 'f32.convert_s/i64', 0xb4              
        addop 'f32.convert_u/i64', 0xb5              
        addop 'f32.demote/f64', 0xb6            
        addop 'f64.convert_s/i32', 0xb7              
        addop 'f64.convert_u/i32', 0xb8              
        addop 'f64.convert_s/i64', 0xb9              
        addop 'f64.convert_u/i64', 0xba              
        addop 'f64.promote/f32', 0xbb          

        addop 'i32.reinterpret/f32', 0xbc          
        addop 'i64.reinterpret/f64', 0xbd          
        addop 'f32.reinterpret/i32', 0xbe          
        addop 'f64.reinterpret/i64', 0xbf
end
init_backtrace_binding() click to toggle source
# File metasm/cpu/webasm/decode.rb, line 158
def init_backtrace_binding
        @backtrace_binding ||= {}

        typesz = Hash.new(8).update 'i32' => 4, 'f32' => 4
        opstack = lambda { |off, sz| Indirection[Expression[:opstack, :+, off].reduce, sz] }
        add_opstack = lambda { |delta, hash| { :opstack => Expression[:opstack, :+, delta].reduce }.update hash }
        globsz = lambda { |di|
                glob_nr = Expression[di.instruction.args.first].reduce
                g = @wasm_file.get_global_nr(glob_nr)
                g ? typesz[g[:type]] : 8
        }
        global = lambda { |di|
                glob_nr = Expression[di.instruction.args.first].reduce
                g = @wasm_file.get_global_nr(glob_nr)
                n = g && g[:module] ? "#{g[:module]}_#{g[:field]}" : "global_#{glob_nr}"
                Indirection[n, globsz[di]]
        }
        locsz = lambda { |di|
                loc_nr = Expression[di.instruction.args.first].reduce
                ci = @wasm_file.code_info[di.misc[:code_start]]
                next typesz[ci[:params][loc_nr]] if loc_nr < ci[:params].length
                loc_nr -= ci[:params].length
                next typesz[ci[:local_var][loc_nr]] if ci[:local_var][loc_nr]
                8
        }
        local = lambda { |di|
                loc_nr = Expression[di.instruction.args.first].reduce
                Indirection[[:local_base, :+, loc_nr*8], locsz[di]]
        }

        opcode_list.map { |ol| ol.name }.uniq.each { |opname|
                sz = (opname[1, 2] == '32' ? 4 : 8)
                @backtrace_binding[opname] ||= case opname
                when 'call', 'call_indirect'
                        lambda { |di|
                                stack_off = 0
                                if opname == 'call'
                                        f = @wasm_file.get_function_nr(di.misc[:tg_func_nr])
                                        proto = f ? f[:type] : {}
                                        # TODO use local_base
                                        h = { :callstack => Expression[:callstack, :+, 8], Indirection[:callstack, 8] => Expression[di.next_addr] }
                                        proto_params_offset = 0
                                else
                                        proto = @wasm_file.type[di.instruction.args.first.reduce]
                                        h = { :callstack => Expression[:callstack, :+, 8], Indirection[:callstack, 8] => Expression[di.next_addr], 'func_idx' => Expression[opstack[0, 4]] }
                                        stack_off += 8
                                        proto_params_offset = 1
                                end
                                stack_off -= 8*proto[:ret].to_a.length
                                stack_off += 8*proto[:params].to_a.length
                                h.update :opstack => Expression[:opstack, :+, stack_off]
                                proto[:ret].to_a.each_with_index { |rt, i| h.update opstack[8*i, typesz[rt]] => Expression["ret_#{i}"] }
                                proto[:params].to_a.each_with_index { |pt, i| h.update "param_#{i}" => Expression[opstack[8*(proto[:params].length-i-1+proto_params_offset), typesz[pt]]] }
                                h
                        }
                when 'if', 'br_if'; lambda { |di| add_opstack[ 8, :flag => Expression[opstack[0, 8]]] }
                when 'block', 'loop', 'br', 'nop', 'else'; lambda { |di| {} }
                when 'end', 'return'; lambda { |di| di.opcode.props[:stopexec] ? { :callstack => Expression[:callstack, :-, 8] } : {} }
                when 'drop'; lambda { |di| add_opstack[8, {}] }
                when 'select'; lambda { |di| add_opstack[16, opstack[0, 8] => Expression[[opstack[8, 8], :*, [1, :-, opstack[0, 8]]], :|, [opstack[16, 8], :*, opstack[0, 8]]]] }
                when 'get_local'; lambda { |di| add_opstack[-8, opstack[0, locsz[di]] => Expression[local[di]]] }
                when 'set_local'; lambda { |di| add_opstack[ 8, local[di] => Expression[opstack[0, locsz[di]]]] }
                when 'tee_local'; lambda { |di| add_opstack[ 0, local[di] => Expression[opstack[0, locsz[di]]]] }
                when 'get_global'; lambda { |di| add_opstack[-8, opstack[0, globsz[di]] => Expression[global[di]]] }
                when 'set_global'; lambda { |di| add_opstack[ 8, global[di] => Expression[opstack[0, globsz[di]]]] }
                when /\.load(.*)/
                        mode = $1; memsz = (mode.include?('32') ? 4 : mode.include?('16') ? 2 : mode.include?('8') ? 1 : sz)
                        lambda { |di| add_opstack[ 0, opstack[0, sz] => Expression[Indirection[[opstack[0, 4], :+, [:mem, :+, di.instruction.args[1].off]], memsz]]] }
                when /\.store(.*)/
                        mode = $1; memsz = (mode.include?('32') ? 4 : mode.include?('16') ? 2 : mode.include?('8') ? 1 : sz)
                        lambda { |di| add_opstack[ 16, Indirection[[opstack[8, 4], :+, [:mem, :+, di.instruction.args[1].off]], memsz] => Expression[opstack[0, sz], :&, (1 << (8*memsz)) - 1]] }
                when /\.const/; lambda { |di| add_opstack[-8, opstack[0, sz] => Expression[di.instruction.args.first.reduce]] }
                when /\.eqz/; lambda { |di| add_opstack[ 0, opstack[0, 8] => Expression[opstack[0, sz], :==, 0]] }
                when /\.eq/;  lambda { |di| add_opstack[ 8, opstack[0, 8] => Expression[opstack[8, sz], :==, opstack[0, sz]]] }
                when /\.ne/;  lambda { |di| add_opstack[ 8, opstack[0, 8] => Expression[opstack[8, sz], :!=, opstack[0, sz]]] }
                when /\.lt/;  lambda { |di| add_opstack[ 8, opstack[0, 8] => Expression[opstack[8, sz], :<,  opstack[0, sz]]] }
                when /\.gt/;  lambda { |di| add_opstack[ 8, opstack[0, 8] => Expression[opstack[8, sz], :>,  opstack[0, sz]]] }
                when /\.le/;  lambda { |di| add_opstack[ 8, opstack[0, 8] => Expression[opstack[8, sz], :<=, opstack[0, sz]]] }
                when /\.ge/;  lambda { |di| add_opstack[ 8, opstack[0, 8] => Expression[opstack[8, sz], :>=, opstack[0, sz]]] }

                when /\.(clz|ctz|popcnt)/; lambda { |di| add_opstack[ 0, :bits => Expression[opstack[0, sz]]] }
                when /\.add/; lambda { |di| add_opstack[ 8, opstack[0, sz] => Expression[opstack[8, sz], :+, opstack[0, sz]]] }
                when /\.sub/; lambda { |di| add_opstack[ 8, opstack[0, sz] => Expression[opstack[8, sz], :-, opstack[0, sz]]] }
                when /\.mul/; lambda { |di| add_opstack[ 8, opstack[0, sz] => Expression[opstack[8, sz], :*, opstack[0, sz]]] }
                when /\.div/; lambda { |di| add_opstack[ 8, opstack[0, sz] => Expression[opstack[8, sz], :/, opstack[0, sz]]] }
                when /\.rem/; lambda { |di| add_opstack[ 8, opstack[0, sz] => Expression[opstack[8, sz], :%, opstack[0, sz]]] }
                when /\.and/; lambda { |di| add_opstack[ 8, opstack[0, sz] => Expression[opstack[8, sz], :&, opstack[0, sz]]] }
                when /\.or/;  lambda { |di| add_opstack[ 8, opstack[0, sz] => Expression[opstack[8, sz], :|, opstack[0, sz]]] }
                when /\.xor/; lambda { |di| add_opstack[ 8, opstack[0, sz] => Expression[opstack[8, sz], :^, opstack[0, sz]]] }
                when /\.shl/; lambda { |di| add_opstack[ 8, opstack[0, sz] => Expression[opstack[8, sz], :<<, opstack[0, sz]]] }
                when /\.shr/; lambda { |di| add_opstack[ 8, opstack[0, sz] => Expression[opstack[8, sz], :>>, opstack[0, sz]]] }
                when /\.rotl/; lambda { |di| add_opstack[ 8, opstack[0, sz] => Expression[[opstack[8, sz], :<<, opstack[0, sz]], :|, [opstack[8, sz], :>>, [8*sz, :-, opstack[0, sz]]]]] }
                when /\.rotr/; lambda { |di| add_opstack[ 8, opstack[0, sz] => Expression[[opstack[8, sz], :>>, opstack[0, sz]], :|, [opstack[8, sz], :<<, [8*sz, :-, opstack[0, sz]]]]] }
                when /f.*\.(abs|neg|ceil|floor|trunc|nearest|sqrt|copysign)/; lambda { |di| add_opstack[0, :incomplete_binding => 1] }
                when /f.*\.(min|max)/; lambda { |di| add_opstack[8, :incomplete_binding => 1] }
                when /i32.wrap/; lambda { |di| add_opstack[ 0, opstack[0, 4] => Expression[opstack[0, 8]]] }
                when /i64.extend/; lambda { |di| add_opstack[ 0, opstack[0, 8] => Expression[opstack[0, 4]]] }
                when /trunc|convert|promote|demote|reinterpret/; lambda { |di| add_opstack[0, :incomplete_binding => 1] }
                end
        }

        @backtrace_binding
end
init_opcode_list() click to toggle source
# File metasm/cpu/webasm/main.rb, line 77
def init_opcode_list
        init
end
parse_arg_valid?(o, spec, arg) click to toggle source
# File metasm/cpu/webasm/encode.rb, line 53
def parse_arg_valid?(o, spec, arg)
        spec and arg
end
parse_argument(lexer) click to toggle source
# File metasm/cpu/webasm/encode.rb, line 12
def parse_argument(lexer)
        lexer = AsmPreprocessor.new(lexer) if lexer.kind_of? String
        lexer.skip_space
        return if not tok = lexer.readtok

        if tok.type == :punct and tok.raw == '['
                # Memref or BrTable
                ary = []
                loop do
                        # XXX empty array for BrTable ?
                        ary << parse_argument(lexer)
                        raise tok, 'bad ptr' if not ary.last.kind_of?(Expression)
                        lexer.skip_space
                        tok2 = lexer.readtok
                        if tok2 and tok2.type == :punct and tok2.raw == ']'
                                break
                        elsif not tok2 or tok2.type != :punct or tok2.raw != ','
                                raise tok, "unexpected #{tok2 ? 'eof' : tok2.raw}"
                        end
                end
                lexer.skip_space
                tok2 = lexer.readtok
                if tok2 and tok2.type == :string and tok2.raw == 'or'
                        # BrTable
                        df = parse_argument(lexer)
                        BrTable.new(ary, df)
                else
                        raise tok, 'bad Memref/BrTable' if ary.length != 1
                        lexer.unreadtok(tok2) if tok2
                        Memref.new(ary[0])
                end
        elsif WasmFile::TYPE.index(tok.raw)
                BlockSignature.new(WasmFile::TYPE.index(tok.raw))
        else
                lexer.unreadtok tok
                expr = Expression.parse(lexer)
                lexer.skip_space
                expr
        end
end
parse_instruction_mnemonic(lexer) click to toggle source
# File metasm/cpu/webasm/encode.rb, line 57
def parse_instruction_mnemonic(lexer)
        return if not tok = lexer.readtok
        tok = tok.dup
        while ntok = lexer.nexttok and ntok.type == :punct and (ntok.raw == '.' or ntok.raw == '/')
                tok.raw << lexer.readtok.raw
                ntok = lexer.readtok
                raise tok, 'invalid opcode name' if not ntok or ntok.type != :string
                tok.raw << ntok.raw
        end

        raise tok, 'invalid opcode' if not opcode_list_byname[tok.raw]
        tok
end
wasm_type_to_type(t) click to toggle source
# File metasm/cpu/webasm/decompile.rb, line 11
def wasm_type_to_type(t)
        case t
        when 'i32'; C::BaseType.new(:int)
        when 'i64'; C::BaseType.new(:longlong)
        when 'f32'; C::BaseType.new(:float)
        when 'f64'; C::BaseType.new(:double)
        when 'anyfunc'; C::Function.new(C::BaseType.new(:void))
        when Hash
                ret = t[:ret].first ? wasm_type_to_type(t[:ret].first) : C::BaseType.new(:void)
                args = t[:params].map { |p| C::Variable.new(nil, wasm_type_to_type(p)) }
                C::Function.new(ret, args)
        end
end