github.com/decomp/exp@v0.0.0-20210624183419-6d058f5e1da6/cmd/bin2c/inst.go (about)

     1  // TODO: Handle flags for all instructions.
     2  
     3  package main
     4  
     5  import (
     6  	"fmt"
     7  	"go/ast"
     8  	"go/token"
     9  
    10  	"github.com/mewkiz/pkg/errutil"
    11  	"golang.org/x/arch/x86/x86asm"
    12  )
    13  
    14  // parseInst parses the given assembly instruction and returns a corresponding
    15  // Go statement.
    16  func parseInst(inst x86asm.Inst, offset int) (ast.Stmt, error) {
    17  	switch inst.Op {
    18  	case x86asm.ADD:
    19  		return parseBinaryInst(inst, token.ADD)
    20  	case x86asm.AND:
    21  		return parseBinaryInst(inst, token.AND)
    22  	case x86asm.CALL:
    23  		return parseCALL(inst, offset)
    24  	case x86asm.CMP:
    25  		return parseCMP(inst)
    26  	case x86asm.DEC:
    27  		return parseDEC(inst)
    28  	case x86asm.IMUL:
    29  		return parseIMUL(inst)
    30  	case x86asm.INC:
    31  		return parseINC(inst)
    32  	case x86asm.JE:
    33  		return parseJE(inst, offset)
    34  	case x86asm.JG:
    35  		return parseJG(inst, offset)
    36  	case x86asm.JGE:
    37  		return parseJGE(inst, offset)
    38  	case x86asm.JL:
    39  		return parseJL(inst, offset)
    40  	case x86asm.JLE:
    41  		return parseJLE(inst, offset)
    42  	case x86asm.JMP:
    43  		return parseJMP(inst, offset)
    44  	case x86asm.JNE:
    45  		return parseJNE(inst, offset)
    46  	case x86asm.LEA:
    47  		return parseLEA(inst)
    48  	case x86asm.MOV:
    49  		return parseMOV(inst)
    50  	case x86asm.MOVSX:
    51  		return parseMOVSX(inst)
    52  	case x86asm.MOVZX:
    53  		return parseMOVZX(inst)
    54  	case x86asm.OR:
    55  		return parseBinaryInst(inst, token.OR)
    56  	case x86asm.RET:
    57  		return parseRET(inst)
    58  	case x86asm.SUB:
    59  		return parseBinaryInst(inst, token.SUB)
    60  	case x86asm.TEST:
    61  		return parseTEST(inst)
    62  	case x86asm.XOR:
    63  		return parseBinaryInst(inst, token.XOR)
    64  	case x86asm.PUSH, x86asm.POP, x86asm.LEAVE:
    65  		// ignore for now.
    66  		return nil, nil
    67  	default:
    68  		fmt.Printf("%#v\n", inst)
    69  		return nil, errutil.Newf("support for opcode %v not yet implemented", inst.Op)
    70  	}
    71  }
    72  
    73  // parseCALL parses the given CALL instruction and returns a corresponding Go
    74  // statement.
    75  func parseCALL(inst x86asm.Inst, offset int) (ast.Stmt, error) {
    76  	// Parse arguments.
    77  	arg := inst.Args[0]
    78  	switch arg := arg.(type) {
    79  	case x86asm.Rel:
    80  		offset += inst.Len + int(arg)
    81  	default:
    82  		return nil, errutil.Newf("support for type %T not yet implemented", arg)
    83  	}
    84  	target := baseAddr + offset
    85  	lhs := getReg(x86asm.EAX)
    86  	// TODO: Figure out how to identify the calling convention.
    87  	rhs := &ast.CallExpr{
    88  		Fun: ast.NewIdent(fmt.Sprintf("sub_%08X", target)),
    89  	}
    90  	stmt := createAssign(lhs, rhs)
    91  	return stmt, nil
    92  }
    93  
    94  // parseCMP parses the given CMP instruction and returns a corresponding Go
    95  // statement.
    96  func parseCMP(inst x86asm.Inst) (ast.Stmt, error) {
    97  	// Parse arguments.
    98  	x := getArg(inst.Args[0])
    99  	y := getArg(inst.Args[1])
   100  
   101  	// Create statement.
   102  	//    zf = x == y
   103  	lhs := getFlag(ZF)
   104  	rhs := createBinaryExpr(x, y, token.EQL)
   105  	stmt1 := createAssign(lhs, rhs)
   106  
   107  	// Create statement.
   108  	//    cf = x < y
   109  	lhs = getFlag(CF)
   110  	rhs = createBinaryExpr(x, y, token.LSS)
   111  	stmt2 := createAssign(lhs, rhs)
   112  
   113  	// Create block statement.
   114  	stmt := &ast.BlockStmt{
   115  		List: []ast.Stmt{stmt1, stmt2},
   116  	}
   117  	return stmt, nil
   118  }
   119  
   120  // parseDEC parses the given DEC instruction and returns a corresponding Go
   121  // statement.
   122  func parseDEC(inst x86asm.Inst) (ast.Stmt, error) {
   123  	// Parse arguments.
   124  	x := getArg(inst.Args[0])
   125  
   126  	// Create statement.
   127  	//    x--
   128  	stmt1 := &ast.IncDecStmt{
   129  		X:   x,
   130  		Tok: token.DEC,
   131  	}
   132  
   133  	// Create statement.
   134  	//    zf = x == 0
   135  	lhs := getFlag(ZF)
   136  	rhs := createBinaryExpr(x, createExpr(0), token.EQL)
   137  	stmt2 := createAssign(lhs, rhs)
   138  
   139  	// TODO: Find a better solution for multiple statement than block statement.
   140  
   141  	// Create block statement.
   142  	stmt := &ast.BlockStmt{
   143  		List: []ast.Stmt{stmt1, stmt2},
   144  	}
   145  	return stmt, nil
   146  }
   147  
   148  // parseIMUL parses the given IMUL instruction and returns a corresponding Go
   149  // statement.
   150  func parseIMUL(inst x86asm.Inst) (ast.Stmt, error) {
   151  	// Parse arguments.
   152  	var x, y, z ast.Expr
   153  	x = getArg(inst.Args[0])
   154  	y = getArg(inst.Args[1])
   155  	if inst.Args[2] != nil {
   156  		z = getArg(inst.Args[2])
   157  	} else {
   158  		z = y
   159  	}
   160  
   161  	// Create statement.
   162  	//    x = y * z
   163  	lhs := x
   164  	rhs := createBinaryExpr(y, z, token.MUL)
   165  	return createAssign(lhs, rhs), nil
   166  }
   167  
   168  // parseINC parses the given INC instruction and returns a corresponding Go
   169  // statement.
   170  func parseINC(inst x86asm.Inst) (ast.Stmt, error) {
   171  	// Parse arguments.
   172  	x := getArg(inst.Args[0])
   173  
   174  	// Create statement.
   175  	//    x++
   176  	stmt := &ast.IncDecStmt{
   177  		X:   x,
   178  		Tok: token.INC,
   179  	}
   180  	return stmt, nil
   181  }
   182  
   183  // From http://faydoc.tripod.com/cpu/jge.htm
   184  //
   185  //    * [ ] JA       Jump if above (CF=0 and ZF=0)
   186  //    * [ ] JAE      Jump if above or equal (CF=0)
   187  //    * [ ] JB       Jump if below (CF=1)
   188  //    * [ ] JBE      Jump if below or equal (CF=1 or ZF=1)
   189  //    * [ ] JC       Jump if carry (CF=1)
   190  //    * [ ] JCXZ     Jump if CX register is 0
   191  //    * [x] JE       Jump if equal (ZF=1)
   192  //    * [ ] JECXZ    Jump if ECX register is 0
   193  //    * [x] JG       Jump if greater (ZF=0 and SF=OF)
   194  //    * [x] JGE      Jump if greater or equal (SF=OF)
   195  //    * [x] JL       Jump if less (SF!=OF)
   196  //    * [x] JLE      Jump if less or equal (ZF=1 or SF!=OF)
   197  //    * [ ] JNA      Jump if not above (CF=1 or ZF=1)
   198  //    * [ ] JNAE     Jump if not above or equal (CF=1)
   199  //    * [ ] JNB      Jump if not below (CF=0)
   200  //    * [ ] JNBE     Jump if not below or equal (CF=0 and ZF=0)
   201  //    * [ ] JNC      Jump if not carry (CF=0)
   202  //    * [x] JNE      Jump if not equal (ZF=0)
   203  //    * [ ] JNG      Jump if not greater (ZF=1 or SF!=OF)
   204  //    * [ ] JNGE     Jump if not greater or equal (SF!=OF)
   205  //    * [ ] JNL      Jump if not less (SF=OF)
   206  //    * [ ] JNLE     Jump if not less or equal (ZF=0 and SF=OF)
   207  //    * [ ] JNO      Jump if not overflow (OF=0)
   208  //    * [ ] JNP      Jump if not parity (PF=0)
   209  //    * [ ] JNS      Jump if not sign (SF=0)
   210  //    * [ ] JNZ      Jump if not zero (ZF=0)
   211  //    * [ ] JO       Jump if overflow (OF=1)
   212  //    * [ ] JP       Jump if parity (PF=1)
   213  //    * [ ] JPE      Jump if parity even (PF=1)
   214  //    * [ ] JPO      Jump if parity odd (PF=0)
   215  //    * [ ] JS       Jump if sign (SF=1)
   216  //    * [ ] JZ       Jump if zero (ZF=1)
   217  
   218  // parseJE parses the given JE instruction and returns a corresponding Go
   219  // statement.
   220  func parseJE(inst x86asm.Inst, offset int) (ast.Stmt, error) {
   221  	// Parse arguments.
   222  	arg := inst.Args[0]
   223  	switch arg := arg.(type) {
   224  	case x86asm.Rel:
   225  		offset += inst.Len + int(arg)
   226  	default:
   227  		return nil, errutil.Newf("support for type %T not yet implemented", arg)
   228  	}
   229  
   230  	// JE       Jump if equal (ZF=1)
   231  
   232  	// Create statement.
   233  	//    if zf {
   234  	//       goto x
   235  	//    }
   236  	cond := getFlag(ZF)
   237  	label := getLabel("loc", offset)
   238  	body := &ast.BranchStmt{
   239  		Tok:   token.GOTO,
   240  		Label: label,
   241  	}
   242  	stmt := &ast.IfStmt{
   243  		Cond: cond,
   244  		Body: &ast.BlockStmt{List: []ast.Stmt{body}},
   245  	}
   246  	return stmt, nil
   247  }
   248  
   249  // parseJG parses the given JG instruction and returns a corresponding Go
   250  // statement.
   251  func parseJG(inst x86asm.Inst, offset int) (ast.Stmt, error) {
   252  	// Parse arguments.
   253  	arg := inst.Args[0]
   254  	switch arg := arg.(type) {
   255  	case x86asm.Rel:
   256  		offset += inst.Len + int(arg)
   257  	default:
   258  		return nil, errutil.Newf("support for type %T not yet implemented", arg)
   259  	}
   260  
   261  	// JG      Jump if greater (ZF=0 and SF=OF)
   262  
   263  	// Create statement.
   264  	//    if !zf && sf == of
   265  	//       goto x
   266  	//    }
   267  	expr := &ast.BinaryExpr{
   268  		X:  getFlag(SF),
   269  		Op: token.EQL,
   270  		Y:  getFlag(OF),
   271  	}
   272  	cond := &ast.BinaryExpr{
   273  		X: &ast.UnaryExpr{
   274  			Op: token.NOT,
   275  			X:  getFlag(ZF),
   276  		},
   277  		Op: token.LOR,
   278  		Y:  expr,
   279  	}
   280  	label := getLabel("loc", offset)
   281  	body := &ast.BranchStmt{
   282  		Tok:   token.GOTO,
   283  		Label: label,
   284  	}
   285  	stmt := &ast.IfStmt{
   286  		Cond: cond,
   287  		Body: &ast.BlockStmt{List: []ast.Stmt{body}},
   288  	}
   289  	return stmt, nil
   290  }
   291  
   292  // parseJGE parses the given JGE instruction and returns a corresponding Go
   293  // statement.
   294  func parseJGE(inst x86asm.Inst, offset int) (ast.Stmt, error) {
   295  	// Parse arguments.
   296  	arg := inst.Args[0]
   297  	switch arg := arg.(type) {
   298  	case x86asm.Rel:
   299  		offset += inst.Len + int(arg)
   300  	default:
   301  		return nil, errutil.Newf("support for type %T not yet implemented", arg)
   302  	}
   303  
   304  	// JGE      Jump if greater or equal (SF=OF)
   305  
   306  	// Create statement.
   307  	//    if sf == of {
   308  	//       goto x
   309  	//    }
   310  	cond := &ast.BinaryExpr{
   311  		X:  getFlag(SF),
   312  		Op: token.EQL,
   313  		Y:  getFlag(OF),
   314  	}
   315  	label := getLabel("loc", offset)
   316  	body := &ast.BranchStmt{
   317  		Tok:   token.GOTO,
   318  		Label: label,
   319  	}
   320  	stmt := &ast.IfStmt{
   321  		Cond: cond,
   322  		Body: &ast.BlockStmt{List: []ast.Stmt{body}},
   323  	}
   324  	return stmt, nil
   325  }
   326  
   327  // parseJL parses the given JL instruction and returns a corresponding Go
   328  // statement.
   329  func parseJL(inst x86asm.Inst, offset int) (ast.Stmt, error) {
   330  	// Parse arguments.
   331  	arg := inst.Args[0]
   332  	switch arg := arg.(type) {
   333  	case x86asm.Rel:
   334  		offset += inst.Len + int(arg)
   335  	default:
   336  		return nil, errutil.Newf("support for type %T not yet implemented", arg)
   337  	}
   338  
   339  	// JL       Jump if less (SF!=OF)
   340  
   341  	// Create statement.
   342  	//    if sf != of {
   343  	//       goto x
   344  	//    }
   345  	cond := &ast.BinaryExpr{
   346  		X:  getFlag(SF),
   347  		Op: token.NEQ,
   348  		Y:  getFlag(OF),
   349  	}
   350  	label := getLabel("loc", offset)
   351  	body := &ast.BranchStmt{
   352  		Tok:   token.GOTO,
   353  		Label: label,
   354  	}
   355  	stmt := &ast.IfStmt{
   356  		Cond: cond,
   357  		Body: &ast.BlockStmt{List: []ast.Stmt{body}},
   358  	}
   359  	return stmt, nil
   360  }
   361  
   362  // parseJLE parses the given JLE instruction and returns a corresponding Go
   363  // statement.
   364  func parseJLE(inst x86asm.Inst, offset int) (ast.Stmt, error) {
   365  	// Parse arguments.
   366  	arg := inst.Args[0]
   367  	switch arg := arg.(type) {
   368  	case x86asm.Rel:
   369  		offset += inst.Len + int(arg)
   370  	default:
   371  		return nil, errutil.Newf("support for type %T not yet implemented", arg)
   372  	}
   373  
   374  	// JLE      Jump if less or equal (ZF=1 or SF!=OF)
   375  
   376  	// Create statement.
   377  	//    if zf || sf != of
   378  	//       goto x
   379  	//    }
   380  	expr := &ast.BinaryExpr{
   381  		X:  getFlag(SF),
   382  		Op: token.NEQ,
   383  		Y:  getFlag(OF),
   384  	}
   385  	cond := &ast.BinaryExpr{
   386  		X:  getFlag(ZF),
   387  		Op: token.LOR,
   388  		Y:  expr,
   389  	}
   390  	label := getLabel("loc", offset)
   391  	body := &ast.BranchStmt{
   392  		Tok:   token.GOTO,
   393  		Label: label,
   394  	}
   395  	stmt := &ast.IfStmt{
   396  		Cond: cond,
   397  		Body: &ast.BlockStmt{List: []ast.Stmt{body}},
   398  	}
   399  	return stmt, nil
   400  }
   401  
   402  // parseJMP parses the given JMP instruction and returns a corresponding Go
   403  // statement.
   404  func parseJMP(inst x86asm.Inst, offset int) (ast.Stmt, error) {
   405  	// Parse arguments.
   406  	arg := inst.Args[0]
   407  	switch arg := arg.(type) {
   408  	case x86asm.Rel:
   409  		offset += inst.Len + int(arg)
   410  	default:
   411  		return nil, errutil.Newf("support for type %T not yet implemented", arg)
   412  	}
   413  
   414  	// Create statement.
   415  	//    goto x
   416  	label := getLabel("loc", offset)
   417  	stmt := &ast.BranchStmt{
   418  		Tok:   token.GOTO,
   419  		Label: label,
   420  	}
   421  	return stmt, nil
   422  }
   423  
   424  // parseJNE parses the given JNE instruction and returns a corresponding Go
   425  // statement.
   426  func parseJNE(inst x86asm.Inst, offset int) (ast.Stmt, error) {
   427  	// Parse arguments.
   428  	arg := inst.Args[0]
   429  	switch arg := arg.(type) {
   430  	case x86asm.Rel:
   431  		offset += inst.Len + int(arg)
   432  	default:
   433  		return nil, errutil.Newf("support for type %T not yet implemented", arg)
   434  	}
   435  
   436  	// JNE      Jump if not equal (ZF=0)
   437  
   438  	// Create statement.
   439  	//    if !zf {
   440  	//       goto x
   441  	//    }
   442  	cond := &ast.UnaryExpr{
   443  		Op: token.NOT,
   444  		X:  getFlag(ZF),
   445  	}
   446  	label := getLabel("loc", offset)
   447  	body := &ast.BranchStmt{
   448  		Tok:   token.GOTO,
   449  		Label: label,
   450  	}
   451  	stmt := &ast.IfStmt{
   452  		Cond: cond,
   453  		Body: &ast.BlockStmt{List: []ast.Stmt{body}},
   454  	}
   455  	return stmt, nil
   456  }
   457  
   458  // parseLEA parses the given LEA instruction and returns a corresponding Go
   459  // statement.
   460  func parseLEA(inst x86asm.Inst) (ast.Stmt, error) {
   461  	// Parse arguments.
   462  	x := getArg(inst.Args[0])
   463  	y := getArg(inst.Args[1])
   464  
   465  	// Create statement.
   466  	//    x = &y
   467  	lhs := x
   468  	rhs, err := unstar(y)
   469  	if err != nil {
   470  		return nil, errutil.Err(err)
   471  	}
   472  	return createAssign(lhs, rhs), nil
   473  }
   474  
   475  // unstar returns the underlying expression from the given parenthesised star
   476  // expression.
   477  func unstar(expr ast.Expr) (ast.Expr, error) {
   478  	star, ok := expr.(*ast.StarExpr)
   479  	if !ok {
   480  		return nil, errutil.Newf("invalid argument type; expected *ast.StarExpr, got %T", expr)
   481  	}
   482  	paren, ok := star.X.(*ast.ParenExpr)
   483  	if !ok {
   484  		return nil, errutil.Newf("invalid argument type; expected *ast.ParenExpr, got %T", star.X)
   485  	}
   486  	return paren.X, nil
   487  }
   488  
   489  // parseMOV parses the given MOV instruction and returns a corresponding Go
   490  // statement.
   491  func parseMOV(inst x86asm.Inst) (ast.Stmt, error) {
   492  	// Parse arguments.
   493  	x := getArg(inst.Args[0])
   494  	y := getArg(inst.Args[1])
   495  
   496  	// Create statement.
   497  	//    x = y
   498  	lhs := x
   499  	rhs := y
   500  	return createAssign(lhs, rhs), nil
   501  }
   502  
   503  // parseMOVSX parses the given MOVSX instruction and returns a corresponding Go
   504  // statement.
   505  func parseMOVSX(inst x86asm.Inst) (ast.Stmt, error) {
   506  	// Parse arguments.
   507  	x := getArg(inst.Args[0])
   508  	y := getArg(inst.Args[1])
   509  
   510  	// Create statement.
   511  	//    x = y
   512  	lhs := x
   513  	rhs := y
   514  	return createAssign(lhs, rhs), nil
   515  }
   516  
   517  // parseMOVZX parses the given MOVZX instruction and returns a corresponding Go
   518  // statement.
   519  func parseMOVZX(inst x86asm.Inst) (ast.Stmt, error) {
   520  	// Parse arguments.
   521  	x := getArg(inst.Args[0])
   522  	y := getArg(inst.Args[1])
   523  
   524  	// Create statement.
   525  	//    x = y
   526  	lhs := x
   527  	rhs := y
   528  	return createAssign(lhs, rhs), nil
   529  }
   530  
   531  // parseRET parses the given RET instruction and returns a corresponding Go
   532  // statement.
   533  func parseRET(inst x86asm.Inst) (ast.Stmt, error) {
   534  	// TODO: Handle pops; e.g.
   535  	//    ret 0xC
   536  
   537  	// Create statement.
   538  	//    return
   539  	return &ast.ReturnStmt{}, nil
   540  }
   541  
   542  // parseTEST parses the given TEST instruction and returns a corresponding Go
   543  // statement.
   544  func parseTEST(inst x86asm.Inst) (ast.Stmt, error) {
   545  	// Parse arguments.
   546  	x := getArg(inst.Args[0])
   547  	y := getArg(inst.Args[1])
   548  
   549  	// Create statement.
   550  	//    zf = (x&y) == 0
   551  	lhs := getFlag(ZF)
   552  	expr := createBinaryExpr(x, y, token.AND)
   553  	zero := &ast.BasicLit{Kind: token.INT, Value: "0"}
   554  	rhs := createBinaryExpr(expr, zero, token.EQL)
   555  	stmt1 := createAssign(lhs, rhs)
   556  
   557  	// Create statement.
   558  	//    sf = (x&y)>>31 == 1
   559  	lhs = getFlag(SF)
   560  	expr = createBinaryExpr(x, y, token.AND)
   561  	thirtyone := &ast.BasicLit{Kind: token.INT, Value: "31"}
   562  	expr = createBinaryExpr(expr, thirtyone, token.SHR)
   563  	one := &ast.BasicLit{Kind: token.INT, Value: "1"}
   564  	rhs = createBinaryExpr(expr, one, token.EQL)
   565  	stmt2 := createAssign(lhs, rhs)
   566  
   567  	// Create statement.
   568  	//    of = 0
   569  	lhs = getFlag(OF)
   570  	rhs = zero
   571  	stmt3 := createAssign(lhs, rhs)
   572  
   573  	// Create statement.
   574  	//    cf = 0
   575  	lhs = getFlag(CF)
   576  	rhs = zero
   577  	stmt4 := createAssign(lhs, rhs)
   578  
   579  	// TODO: Set remaining flags.
   580  
   581  	// Create block statement.
   582  	stmt := &ast.BlockStmt{
   583  		List: []ast.Stmt{stmt1, stmt2, stmt3, stmt4},
   584  	}
   585  	return stmt, nil
   586  }
   587  
   588  // parseBinaryInst parses the given binary instruction and returns a
   589  // corresponding Go statement.
   590  func parseBinaryInst(inst x86asm.Inst, op token.Token) (ast.Stmt, error) {
   591  	// Parse arguments.
   592  	x := getArg(inst.Args[0])
   593  	y := getArg(inst.Args[1])
   594  
   595  	// Simplify instructions when x == y.
   596  	if inst.Args[0] == inst.Args[1] {
   597  		switch op {
   598  		case token.SUB, token.XOR:
   599  			// Before:
   600  			//    x = x - x
   601  			//    x = x ^ x
   602  			// After:
   603  			//    x = 0
   604  			return createAssign(x, createExpr(0)), nil
   605  		}
   606  	}
   607  
   608  	// Create statement.
   609  	//    x = x OP y
   610  	lhs := x
   611  	rhs := createBinaryExpr(x, y, op)
   612  	return createAssign(lhs, rhs), nil
   613  }
   614  
   615  // createBinaryExpr returns a binary expression with the given operands and
   616  // operation. Special consideration is taken with regards to sub-registers (e.g.
   617  // al, ah, ax).
   618  func createBinaryExpr(x, y ast.Expr, op token.Token) ast.Expr {
   619  	// Handle sub-registers (e.g. al, ah, ax).
   620  	x = fromSubReg(x)
   621  	y = fromSubReg(y)
   622  
   623  	return &ast.BinaryExpr{
   624  		X:  x,
   625  		Op: op,
   626  		Y:  y,
   627  	}
   628  }
   629  
   630  // createAssign returns an assignment statement with the given left- and right-
   631  // hand sides. Special consideration is taken with regards to sub-registers.
   632  // (e.g. al, ah, ax).
   633  func createAssign(lhs, rhs ast.Expr) ast.Stmt {
   634  	rhs = fromSubReg(rhs)
   635  	// TODO: Handle sub-registers (al, ah, ax)
   636  	return &ast.AssignStmt{
   637  		Lhs: []ast.Expr{lhs},
   638  		Tok: token.ASSIGN,
   639  		Rhs: []ast.Expr{rhs},
   640  	}
   641  }