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 }