From: Brendan Hansen Date: Sun, 12 Apr 2020 04:40:12 +0000 (-0500) Subject: Finallized text conversion X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=9e76f74d9dcc34755cf62951f6bc12ff4c2d1280;p=wasm-analyzer.git Finallized text conversion --- diff --git a/src/utils.lua b/src/utils.lua index db8880f..0889f6f 100644 --- a/src/utils.lua +++ b/src/utils.lua @@ -120,6 +120,14 @@ class "Stack" { self.data = {} end; + size = function(self) + return #self.data + end; + + empty = function(self) + return #self.data == 0 + end; + at = function(self, x) return self.data[#self.data - x] end diff --git a/src/wasm/exprs.lua b/src/wasm/exprs.lua index eb10f91..5f957ae 100644 --- a/src/wasm/exprs.lua +++ b/src/wasm/exprs.lua @@ -1,5 +1,6 @@ import { Stack = "src.utils:Stack"; + pprint = "lualib.pprint"; } @@ -43,7 +44,7 @@ local stack_opts = { --call_indirect is dynamic ["drop"] = { 1, 0 }; - ["select"] = { 3, 0 }; + ["select"] = { 3, 1 }; ["local.get"] = { 0, 1 }; ["local.set"] = { 1, 0 }; @@ -209,63 +210,372 @@ local stack_opts = { ["f64.reinterpret_i64"] = { 1, 1 }; } +local build_instr_list + +function binop(op) + return function(stack, instr) + local right = stack:pop() + local left = stack:pop() + stack:push("(" .. left .. " " .. op .. " " .. right .. ")") + end +end + +function cast(to) + return function(stack, instr) + local thing = stack:pop() + stack:push("<" .. to .. ">" .. thing) + end +end + +function func_call(name, nparams) + nparams = nparams or 1 + return function(stack, instr) + local str = name .. "(" + local args = "" + for i=1,nparams do + local val = stack:pop() + args = ", " .. val .. args + end + str = str .. args:sub(3) .. ")" + stack:push(str) + end +end + +function const(t) + return function(stack, instr) + stack:push(tostring(instr.x) .. t) + end +end + +function expr_return(stack, instr) + if stack:size() >= 1 then + return "return " .. stack:pop() + else + return "return" + end +end + +function expr_br(stack, instr) + return "br <" .. instr.x.block.label .. ">" +end + +function expr_br_if(stack, instr) + return "br_if <" .. instr.x.block.label .. "> (" .. stack:pop() .. ")" +end + +function expr_br_table(stack, instr) + error "Cannot handle br_table" +end + +function expr_drop(stack, instr) + return "drop (" .. stack:pop() .. ")" +end + +function expr_select(stack, instr) + stack:push("select (" .. stack:pop() .. [[) { + 0: ]] .. stack:pop() .. [[ + 1: ]] .. stack:pop() .. [[ +}]]) +end + +function expr_local_get(stack, instr, func) + stack:push(func.locals[instr.x + 1].name) +end + +function expr_local_set(stack, instr, func) + local varname = func.locals[instr.x + 1].name + return varname .. " = " .. stack:pop() +end + +function expr_local_tee(stack, instr, func) + local varname = func.locals[instr.x + 1].name + stack:push(varname .. " = " .. stack:pop()) +end + +function expr_global_get(stack, instr) + stack:push("global" .. instr.x) +end + +function expr_global_set(stack, instr) + return "global" .. instr.x .. " = " .. stack:pop() +end + +function expr_load(stack, instr) + stack:push("[" .. stack:pop() .. "]") +end + +function expr_store(stack, instr) + local value = stack:pop() + local location = stack:pop() + return "[" .. location .. "] <- " .. value +end + +function indent(lines, prefix) + local newlines = {} + for _, line in ipairs(lines) do + table.insert(newlines, prefix .. line) + end + return newlines +end + +function expr_block(stack, instr, func, mod) + local lines = indent( + build_instr_list(instr.instrs, func, mod), + " " + ) + table.insert(lines, 1, "block <" .. instr.label .. "> {") + table.insert(lines, "}") + return lines +end + +function expr_loop(stack, instr, func, mod) + local lines = indent( + build_instr_list(instr.instrs, func, mod), + " " + ) + table.insert(lines, 1, "loop <" .. instr.label .. "> {") + table.insert(lines, "}") + return lines +end + +function expr_if(stack, instr, func, mod) + local lines = indent( + build_instr_list(instr.instrs, func, mod), + " " + ) + table.insert(lines, 1, "if <" .. instr.label .. "> (" .. stack:pop() .. ") {") + if #instr.else_instrs > 0 then + table.insert(lines, "} else {") + local else_lines = indent( + build_instr_list(instr.else_instrs, func, mod), + " " + ) + for _, line in ipairs(else_lines) do + table.insert(lines, line) + end + end + table.insert(lines, "}") + return lines +end + +function expr_call(stack, instr, func, mod) + local nparams = #mod.funcs[instr.x].type_.param_types + local nreturns = #mod.funcs[instr.x].type_.result_types + local name = mod.funcs[instr.x].name + + local res = func_call(name, nparams)(stack, instr, func, mod) + if nreturns >= 1 then + stack:push(res) + else + return res + end +end + +function call_indirect(stack, instr) + error "Cannot handle call indirect" +end + +local expr_generators = { +-- Name Inputs, Outputs + ["unreachable"] = { 0, 0 }; + ["nop"] = { 0, 0 }; + + ["block"] = expr_block; + ["loop"] = expr_loop; + ["if"] = expr_if; + + ["br"] = expr_br; + ["br_if"] = expr_br_if; + ["br_table"] = expr_br_table; + ["return"] = expr_return; + ["call"] = expr_call; + --call_indirect is dynamic + + ["drop"] = expr_drop; + ["select"] = expr_select; + + ["local.get"] = expr_local_get; + ["local.set"] = expr_local_set; + ["local.tee"] = expr_local_tee; + ["global.get"] = expr_global_get; + ["global.set"] = expr_global_set; + + ["i32.load"] = expr_load; + ["i64.load"] = expr_load; + ["f32.load"] = expr_load; + ["f64.load"] = expr_load; + ["i32.load8_s"] = expr_load; + ["i32.load8_u"] = expr_load; + ["i32.load16_s"] = expr_load; + ["i32.load16_u"] = expr_load; + ["i64.load8_s"] = expr_load; + ["i64.load8_u"] = expr_load; + ["i64.load16_s"] = expr_load; + ["i64.load16_u"] = expr_load; + ["i64.load32_s"] = expr_load; + ["i64.load32_u"] = expr_load; + ["i32.store"] = expr_store; + ["i64.store"] = expr_store; + ["f32.store"] = expr_store; + ["f64.store"] = expr_store; + ["i32.store8"] = expr_store; + ["i32.store16"] = expr_store; + ["i64.store8"] = expr_store; + ["i64.store16"] = expr_store; + ["i64.store32"] = expr_store; + + ["memory.size"] = func_call("memory.size", 0); + ["memory.grow"] = func_call "memory.grow"; + + ["i32.const"] = const "i32"; + ["i64.const"] = const "i64"; + ["f32.const"] = const "f32"; + ["f64.const"] = const "f64"; + + ["i32.eqz"] = func_call "eqz"; + ["i32.eq"] = binop "=="; + ["i32.ne"] = binop "!="; + ["i32.lt_s"] = binop "<"; + ["i32.lt_u"] = binop "<"; + ["i32.gt_s"] = binop ">"; + ["i32.gt_u"] = binop ">"; + ["i32.le_s"] = binop "<="; + ["i32.le_u"] = binop "<="; + ["i32.ge_s"] = binop ">="; + ["i32.ge_u"] = binop ">="; + ["i64.eqz"] = func_call "eqz"; + ["i64.eq"] = binop "=="; + ["i64.ne"] = binop "!="; + ["i64.lt_s"] = binop "<"; + ["i64.lt_u"] = binop "<"; + ["i64.gt_s"] = binop ">"; + ["i64.gt_u"] = binop ">"; + ["i64.le_s"] = binop "<="; + ["i64.le_u"] = binop "<="; + ["i64.ge_s"] = binop ">="; + ["i64.ge_u"] = binop ">="; + ["f32.eq"] = binop "=="; + ["f32.ne"] = binop "!="; + ["f32.lt"] = binop "<"; + ["f32.gt"] = binop ">"; + ["f32.le"] = binop "<="; + ["f32.ge"] = binop ">="; + ["f64.eq"] = binop "=="; + ["f64.ne"] = binop "!="; + ["f64.lt"] = binop "<"; + ["f64.gt"] = binop ">"; + ["f64.le"] = binop "<="; + ["f64.ge"] = binop ">="; + ["i32.clz"] = func_call "clz"; + ["i32.ctz"] = func_call "ctz"; + ["i32.popcnt"] = func_call "popcnt"; + ["i32.add"] = binop "+"; + ["i32.sub"] = binop "-"; + ["i32.mul"] = binop "*"; + ["i32.div_s"] = binop "/"; + ["i32.div_u"] = binop "/"; + ["i32.rem_s"] = binop "%"; + ["i32.rem_u"] = binop "%"; + ["i32.and"] = binop "&"; + ["i32.or"] = binop "|"; + ["i32.xor"] = binop "^"; + ["i32.shl"] = binop "<<"; + ["i32.shr_s"] = binop ">>"; + ["i32.shr_u"] = binop ">>>"; + ["i32.rotl"] = binop "rotl"; + ["i32.rotr"] = binop "rotr"; + ["i64.clz"] = func_call "clz"; + ["i64.ctz"] = func_call "ctz"; + ["i64.popcnt"] = func_call "popcnt"; + ["i64.add"] = binop "+"; + ["i64.sub"] = binop "-"; + ["i64.mul"] = binop "*"; + ["i64.div_s"] = binop "/"; + ["i64.div_u"] = binop "/"; + ["i64.rem_s"] = binop "%"; + ["i64.rem_u"] = binop "%"; + ["i64.and"] = binop "&"; + ["i64.or"] = binop "|"; + ["i64.xor"] = binop "^"; + ["i64.shl"] = binop "<<"; + ["i64.shr_s"] = binop ">>"; + ["i64.shr_u"] = binop ">>>"; + ["i64.rotl"] = binop "rotl"; + ["i64.rotr"] = binop "rotr"; + ["f32.abs"] = func_call "abs"; + ["f32.neg"] = func_call "neg"; -- TODO: make this better + ["f32.ceil"] = func_call "ceil"; + ["f32.floor"] = func_call "floor"; + ["f32.trunc"] = func_call "trunc"; + ["f32.nearest"] = func_call "nearest"; + ["f32.sqrt"] = func_call "sqrt"; + ["f32.add"] = binop "+"; + ["f32.sub"] = binop "-"; + ["f32.mul"] = binop "*"; + ["f32.div"] = binop "/"; + ["f32.min"] = func_call("min", 2); + ["f32.max"] = func_call("max", 2); + ["f32.copysign"] = func_call("copysign", 2); + ["f64.abs"] = func_call "abs"; + ["f64.neg"] = func_call "neg"; + ["f64.ceil"] = func_call "ceil"; + ["f64.floor"] = func_call "floor"; + ["f64.trunc"] = func_call "trunc"; + ["f64.nearest"] = func_call "nearest"; + ["f64.sqrt"] = func_call "sqrt"; + ["f64.add"] = binop "+"; + ["f64.sub"] = binop "-"; + ["f64.mul"] = binop "*"; + ["f64.div"] = binop "/"; + ["f64.min"] = func_call("min", 2); + ["f64.max"] = func_call("max", 2); + ["f64.copysign"] = func_call("copysign", 2); + + ["i32.wrap_i64"] = cast "i32"; + ["i32.trunc_f32_s"] = cast "i32"; + ["i32.trunc_f32_u"] = cast "i32"; + ["i32.trunc_f64_s"] = cast "i32"; + ["i32.trunc_f64_u"] = cast "i32"; + ["i64.extend_i32_s"] = cast "i64"; + ["i64.extend_i32_u"] = cast "i64"; + ["i64.trunc_f32_s"] = cast "i64"; + ["i64.trunc_f32_u"] = cast "i64"; + ["i64.trunc_f64_s"] = cast "i64"; + ["i64.trunc_f64_u"] = cast "i64"; + ["f32.convert_i32_s"] = cast "f32"; + ["f32.convert_i32_u"] = cast "f32"; + ["f32.convert_i64_s"] = cast "f32"; + ["f32.convert_i64_u"] = cast "f32"; + ["f32.demote_f64"] = cast "f32"; + ["f64.convert_i32_s"] = cast "f64"; + ["f64.convert_i32_u"] = cast "f64"; + ["f64.convert_i64_s"] = cast "f64"; + ["f64.convert_i64_u"] = cast "f64"; + ["f64.promote_f32"] = cast "f64"; + ["i32.reinterpret_f32"] = cast "i32"; + ["i64.reinterpret_f64"] = cast "i64"; + ["f32.reinterpret_i32"] = cast "f32"; + ["f64.reinterpret_i64"] = cast "f64"; +} + -- This works... ish but we should use proper symbols (+-*/) and other things -function build_instr_list(instrs) +function build_instr_list(instrs, func, mod) local lines = {} local stack = Stack() for _, instr in ipairs(instrs) do - local instr_name = instr[1] - if stack_opts[instr_name] then - -- For now, everything will be built in a function calling syntax - local str = "" - for i=1, stack_opts[instr_name][1] do - str = str .. ", " - str = str .. stack:pop() - end - - str = instr_name .. "(" .. str:sub(3) .. ")" - - if stack_opts[instr_name][2] == 1 then - stack:push(str) - else - table.insert(lines, str) - end - end - - if instr_name == "block" or instr_name == "loop" then - -- have a .instrs which is the instruction list - table.insert(lines, instr_name .. " { [" .. instr.label .. "]") - local instr_lines = build_instr_list(instr.instrs) - for _, line in ipairs(instr_lines) do - table.insert(lines, " " .. line) - end - table.insert(lines, "}") - end - if instr_name == "if" then - local str = instr_name .. " (" - str = str .. stack:pop() - str = str .. " ) { [" .. instr.label .. "]" - table.insert(lines, str) - - local instr_lines = build_instr_list(instr.instrs) - for _, line in ipairs(instr_lines) do - table.insert(lines, " " .. line) - end - - if instr_name == "if" and #instr.else_instrs > 0 then - table.insert(lines, "} else {") - instr_lines = build_instr_list(instr.else_instrs) - for _, line in ipairs(instr_lines) do - table.insert(lines, " " .. line) - end - end - - table.insert(lines, "}") + local res = expr_generators[instr[1]](stack, instr, func, mod) + if type(res) == "string" then + table.insert(lines, res) + elseif type(res) == "table" then + for _, line in ipairs(res) do table.insert(lines, line) end end + end - if instr_name == "call" then + if stack:size() ~= 0 then + while not stack:empty() do + table.insert(lines, stack:pop()) end end @@ -273,7 +583,7 @@ function build_instr_list(instrs) end function build_expr(wasm_func, wasm_mod) - local lines = build_instr_list(wasm_func.body) + local lines = build_instr_list(wasm_func.body, wasm_func, wasm_mod) return lines end diff --git a/testing.lua b/testing.lua index 033ef79..10362d8 100644 --- a/testing.lua +++ b/testing.lua @@ -10,10 +10,14 @@ import { local mod = decompile(arg[1]) mod = analyze(mod) +-- pprint(mod) for i, func in ipairs(mod.funcs) do if func.body then print(func.name .. " -----------------------------------") - pprint(build_expr(func, mod)) + local res = build_expr(func, mod) + for _, r in ipairs(res) do + print(r) + end end end