node/lib/metasm/metasm/cpu/ia32/decompile.rb
def decompile_func_finddeps(dcmp, blocks, func)
		deps_r = {} ; deps_w = {} ; deps_to = {}
		deps_subfunc = {}	# things read/written by subfuncs

		# find read/writes by each block
		blocks.each { |b, to|
			deps_r[b] = [] ; deps_w[b] = [] ; deps_to[b] = to
			deps_subfunc[b] = []

			blk = dcmp.dasm.decoded[b].block
			blk.list.each { |di|
				a = di.backtrace_binding.values
				w = []
				di.backtrace_binding.keys.each { |k|
					case k
					when ::Symbol; w |= [k]
					else a |= Expression[k].externals	# if dword [eax] <- 42, eax is read
					end
				}
				a << :eax if di.opcode.name == 'ret' and (not func.type.kind_of? C::BaseType or func.type.type.name != :void)	# standard ABI

				deps_r[b] |= a.map { |ee| Expression[ee].externals.grep(::Symbol) }.flatten - [:unknown] - deps_w[b]
				deps_w[b] |= w.map { |ee| Expression[ee].externals.grep(::Symbol) }.flatten - [:unknown]
			}
			stackoff = nil
			blk.each_to_normal { |t|
				t = dcmp.backtrace_target(t, blk.list.last.address)
				next if not t = dcmp.c_parser.toplevel.symbol[t]
				t.type = C::Function.new(C::BaseType.new(:int)) if not t.type.kind_of? C::Function	# XXX this may seem a bit extreme, and yes, it is.
				stackoff ||= Expression[dcmp.dasm.backtrace(:esp, blk.list.last.address, :snapshot_addr => blocks.first[0]).first, :-, :esp].reduce

				# things that are needed by the subfunction
				if t.has_attribute('fastcall')
					a = t.type.args.to_a
					dep = [:ecx, :edx]
					dep.shift if not a[0] or a[0].has_attribute('unused')
					dep.pop   if not a[1] or a[1].has_attribute('unused')
					deps_subfunc[b] |= dep
				end
				t.type.args.to_a.each { |arg|
					if reg = arg.has_attribute('register')
						deps_subfunc[b] |= [reg.to_sym]
					end
				}
			}
			if stackoff	# last block instr == subfunction call
				deps_r[b] |= deps_subfunc[b] - deps_w[b]
				deps_w[b] |= [:eax, :ecx, :edx]			# standard ABI
			end
		}


		bt = blocks.transpose
		roots = bt[0] - bt[1].flatten	# XXX jmp 1stblock ?

		# find regs read and never written (must have been set by caller and are part of the func ABI)
		uninitialized = lambda { |b, r, done|
			if not deps_r[b]
			elsif deps_r[b].include?(r)
				blk = dcmp.dasm.decoded[b].block
				bw = []
				rdi = blk.list.find { |di|
					a = di.backtrace_binding.values
					w = []
					di.backtrace_binding.keys.each { |k|
						case k
						when ::Symbol; w |= [k]
						else a |= Expression[k].externals	# if dword [eax] <- 42, eax is read
						end
					}
					a << :eax if di.opcode.name == 'ret' and (not func.type.kind_of? C::BaseType or func.type.type.name != :void)	# standard ABI

					next true if (a.map { |ee| Expression[ee].externals.grep(::Symbol) }.flatten - [:unknown] - bw).include? r
					bw |= w.map { |ee| Expression[ee].externals.grep(::Symbol) }.flatten - [:unknown]
					false
				}
				if r == :eax and (rdi || blk.list.last).opcode.name == 'ret'
					func.type.type = C::BaseType.new(:void)
					false
				elsif rdi and rdi.backtrace_binding[r]
					false	# mov al, 42 ; ret  -> don't regarg eax
				else
					true
				end
			elsif deps_w[b].include?(r)
			else
				done << b
				(deps_to[b] - done).find { |tb| uninitialized[tb, r, done] }
			end
		}

		regargs = []
		register_symbols.each { |r|
			if roots.find { |root| uninitialized[root, r, []] }
				regargs << r
			end
		}

		# TODO honor user-defined prototype if available (eg no, really, eax is not read in this function returning al)
		regargs.sort_by { |r| r.to_s }.each { |r|
			a = C::Variable.new(r.to_s, C::BaseType.new(:int, :unsigned))
			a.add_attribute("register(#{r})")
			func.type.args << a
		}

		# remove writes from a block if no following block read the value
		dw = {}
		deps_w.each { |b, deps|
			dw[b] = deps.reject { |dep|
				ret = true
				done = []
				todo = deps_to[b].dup
				while a = todo.pop
					next if done.include? a
					done << a
					if not deps_r[a] or deps_r[a].include? dep
						ret = false
						break
					elsif not deps_w[a].include? dep
						todo.concat deps_to[a]
					end
				end
				ret
			}
		}

		dw
	end
# list variable dependency for each block, remove useless writes
# returns { blockaddr => [list of vars that are needed by a following block] }
list variable dependency for each block, remove useless writes
returns { blockaddr => [list of vars that are needed by a following block] }
list variable dependency for each block remove useless writes returns { blockaddr = > [ list of vars that are needed by a following block ] }
def decompile_func_finddeps(dcmp, blocks, func)
		deps_r = {} ; deps_w = {} ; deps_to = {}
		deps_subfunc = {}	
		blocks.each { |b, to|
			deps_r[b] = [] ; deps_w[b] = [] ; deps_to[b] = to
			deps_subfunc[b] = []
			blk = dcmp.dasm.decoded[b].block
			blk.list.each { |di|
				a = di.backtrace_binding.values
				w = []
				di.backtrace_binding.keys.each { |k|
					case k
					when ::Symbol; w |= [k]
					else a |= Expression[k].externals	
					end
				}
				a << :eax if di.opcode.name == 'ret' and (not func.type.kind_of? C::BaseType or func.type.type.name != :void)	
				deps_r[b] |= a.map { |ee| Expression[ee].externals.grep(::Symbol) }.flatten - [:unknown] - deps_w[b]
				deps_w[b] |= w.map { |ee| Expression[ee].externals.grep(::Symbol) }.flatten - [:unknown]
			}
			stackoff = nil
			blk.each_to_normal { |t|
				t = dcmp.backtrace_target(t, blk.list.last.address)
				next if not t = dcmp.c_parser.toplevel.symbol[t]
				t.type = C::Function.new(C::BaseType.new(:int)) if not t.type.kind_of? C::Function	
				stackoff ||= Expression[dcmp.dasm.backtrace(:esp, blk.list.last.address, :snapshot_addr => blocks.first[0]).first, :-, :esp].reduce
				if t.has_attribute('fastcall')
					a = t.type.args.to_a
					dep = [:ecx, :edx]
					dep.shift if not a[0] or a[0].has_attribute('unused')
					dep.pop   if not a[1] or a[1].has_attribute('unused')
					deps_subfunc[b] |= dep
				end
				t.type.args.to_a.each { |arg|
					if reg = arg.has_attribute('register')
						deps_subfunc[b] |= [reg.to_sym]
					end
				}
			}
			if stackoff	
				deps_r[b] |= deps_subfunc[b] - deps_w[b]
				deps_w[b] |= [:eax, :ecx, :edx]			
			end
		}
		bt = blocks.transpose
		roots = bt[0] - bt[1].flatten	
		uninitialized = lambda { |b, r, done|
			if not deps_r[b]
			elsif deps_r[b].include?(r)
				blk = dcmp.dasm.decoded[b].block
				bw = []
				rdi = blk.list.find { |di|
					a = di.backtrace_binding.values
					w = []
					di.backtrace_binding.keys.each { |k|
						case k
						when ::Symbol; w |= [k]
						else a |= Expression[k].externals	
						end
					}
					a << :eax if di.opcode.name == 'ret' and (not func.type.kind_of? C::BaseType or func.type.type.name != :void)	
					next true if (a.map { |ee| Expression[ee].externals.grep(::Symbol) }.flatten - [:unknown] - bw).include? r
					bw |= w.map { |ee| Expression[ee].externals.grep(::Symbol) }.flatten - [:unknown]
					false
				}
				if r == :eax and (rdi || blk.list.last).opcode.name == 'ret'
					func.type.type = C::BaseType.new(:void)
					false
				elsif rdi and rdi.backtrace_binding[r]
					false	
				else
					true
				end
			elsif deps_w[b].include?(r)
			else
				done << b
				(deps_to[b] - done).find { |tb| uninitialized[tb, r, done] }
			end
		}
		regargs = []
		register_symbols.each { |r|
			if roots.find { |root| uninitialized[root, r, []] }
				regargs << r
			end
		}
		regargs.sort_by { |r| r.to_s }.each { |r|
			a = C::Variable.new(r.to_s, C::BaseType.new(:int, :unsigned))
			a.add_attribute("register(#{r})")
			func.type.args << a
		}
		dw = {}
		deps_w.each { |b, deps|
			dw[b] = deps.reject { |dep|
				ret = true
				done = []
				todo = deps_to[b].dup
				while a = todo.pop
					next if done.include? a
					done << a
					if not deps_r[a] or deps_r[a].include? dep
						ret = false
						break
					elsif not deps_w[a].include? dep
						todo.concat deps_to[a]
					end
				end
				ret
			}
		}
		dw
	end
def decompile_func_finddeps ( dcmp , blocks , func ) deps_r = { } ; deps_w = { } ; deps_to = { } deps_subfunc = { } blocks . each { | b , to | deps_r [ b ] = [ ] ; deps_w [ b ] = [ ] ; deps_to [ b ] = to deps_subfunc [ b ] = [ ] blk = dcmp . dasm . decoded [ b ] . block blk . list . each { | di | a = di . backtrace_binding . values w = [ ] di . backtrace_binding . keys . each { | k | case k when :: Symbol ; w |= [ k ] else a |= Expression [ k ] . externals end } a << :eax if di . opcode . name == 'ret' and ( not func . type . kind_of? C :: BaseType or func . type . type . name != :void ) deps_r [ b ] |= a . map { | ee | Expression [ ee ] . externals . grep ( :: Symbol ) } . flatten - [ :unknown ] - deps_w [ b ] deps_w [ b ] |= w . map { | ee | Expression [ ee ] . externals . grep ( :: Symbol ) } . flatten - [ :unknown ] } stackoff = nil blk . each_to_normal { | t | t = dcmp . backtrace_target ( t , blk . list . last . address ) next if not t = dcmp . c_parser . toplevel . symbol [ t ] t . type = C :: Function . new ( C :: BaseType . new ( :int ) ) if not t . type . kind_of? C :: Function stackoff ||= Expression [ dcmp . dasm . backtrace ( :esp , blk . list . last . address , :snapshot_addr => blocks . first [ 0 ] ) . first , :- , :esp ] . reduce if t . has_attribute ( 'fastcall' ) a = t . type . args . to_a dep = [ :ecx , :edx ] dep . shift if not a [ 0 ] or a [ 0 ] . has_attribute ( 'unused' ) dep . pop if not a [ 1 ] or a [ 1 ] . has_attribute ( 'unused' ) deps_subfunc [ b ] |= dep end t . type . args . to_a . each { | arg | if reg = arg . has_attribute ( 'register' ) deps_subfunc [ b ] |= [ reg . to_sym ] end } } if stackoff deps_r [ b ] |= deps_subfunc [ b ] - deps_w [ b ] deps_w [ b ] |= [ :eax , :ecx , :edx ] end } bt = blocks . transpose roots = bt [ 0 ] - bt [ 1 ] . flatten uninitialized = lambda { | b , r , done | if not deps_r [ b ] elsif deps_r [ b ] . include? ( r ) blk = dcmp . dasm . decoded [ b ] . block bw = [ ] rdi = blk . list . find { | di | a = di . backtrace_binding . values w = [ ] di . backtrace_binding . keys . each { | k | case k when :: Symbol ; w |= [ k ] else a |= Expression [ k ] . externals end } a << :eax if di . opcode . name == 'ret' and ( not func . type . kind_of? C :: BaseType or func . type . type . name != :void ) next true if ( a . map { | ee | Expression [ ee ] . externals . grep ( :: Symbol ) } . flatten - [ :unknown ] - bw ) . include? r bw |= w . map { | ee | Expression [ ee ] . externals . grep ( :: Symbol ) } . flatten - [ :unknown ] false } if r == :eax and ( rdi || blk . list . last ) . opcode . name == 'ret' func . type . type = C :: BaseType . new ( :void ) false elsif rdi and rdi . backtrace_binding [ r ] false else true end elsif deps_w [ b ] . include? ( r ) else done << b ( deps_to [ b ] - done ) . find { | tb | uninitialized [ tb , r , done ] } end } regargs = [ ] register_symbols . each { | r | if roots . find { | root | uninitialized [ root , r , [ ] ] } regargs << r end } regargs . sort_by { | r | r . to_s } . each { | r | a = C :: Variable . new ( r . to_s , C :: BaseType . new ( :int , :unsigned ) ) a . add_attribute ( "register(#{r})" ) func . type . args << a } dw = { } deps_w . each { | b , deps | dw [ b ] = deps . reject { | dep | ret = true done = [ ] todo = deps_to [ b ] . dup while a = todo . pop next if done . include? a done << a if not deps_r [ a ] or deps_r [ a ] . include? dep ret = false break elsif not deps_w [ a ] . include? dep todo . concat deps_to [ a ] end end ret } } dw end
list variable dependency for each block, remove useless writes
returns { blockaddr => [list of vars that are needed by a following block] }
list variable dependency for each block remove useless writes returns { blockaddr = > [ list of vars that are needed by a following block ] }
# things read/written by subfuncs # find read/writes by each block # if dword [eax] <- 42, eax is read # standard ABI # XXX this may seem a bit extreme, and yes, it is. # things that are needed by the subfunction # last block instr == subfunction call # standard ABI # XXX jmp 1stblock ? # find regs read and never written (must have been set by caller and are part of the func ABI) # if dword [eax] <- 42, eax is read # standard ABI # mov al, 42 ; ret  -> don't regarg eax # TODO honor user-defined prototype if available (eg no, really, eax is not read in this function returning al) # remove writes from a block if no following block read the value
dcmp
blocks
func