18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2015 Josh Poimboeuf <jpoimboe@redhat.com> 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <stdio.h> 78c2ecf20Sopenharmony_ci#include <stdlib.h> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#define unlikely(cond) (cond) 108c2ecf20Sopenharmony_ci#include <asm/insn.h> 118c2ecf20Sopenharmony_ci#include "../../../arch/x86/lib/inat.c" 128c2ecf20Sopenharmony_ci#include "../../../arch/x86/lib/insn.c" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "../../check.h" 158c2ecf20Sopenharmony_ci#include "../../elf.h" 168c2ecf20Sopenharmony_ci#include "../../arch.h" 178c2ecf20Sopenharmony_ci#include "../../warn.h" 188c2ecf20Sopenharmony_ci#include <asm/orc_types.h> 198c2ecf20Sopenharmony_ci#include "arch_elf.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic unsigned char op_to_cfi_reg[][2] = { 228c2ecf20Sopenharmony_ci {CFI_AX, CFI_R8}, 238c2ecf20Sopenharmony_ci {CFI_CX, CFI_R9}, 248c2ecf20Sopenharmony_ci {CFI_DX, CFI_R10}, 258c2ecf20Sopenharmony_ci {CFI_BX, CFI_R11}, 268c2ecf20Sopenharmony_ci {CFI_SP, CFI_R12}, 278c2ecf20Sopenharmony_ci {CFI_BP, CFI_R13}, 288c2ecf20Sopenharmony_ci {CFI_SI, CFI_R14}, 298c2ecf20Sopenharmony_ci {CFI_DI, CFI_R15}, 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int is_x86_64(const struct elf *elf) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci switch (elf->ehdr.e_machine) { 358c2ecf20Sopenharmony_ci case EM_X86_64: 368c2ecf20Sopenharmony_ci return 1; 378c2ecf20Sopenharmony_ci case EM_386: 388c2ecf20Sopenharmony_ci return 0; 398c2ecf20Sopenharmony_ci default: 408c2ecf20Sopenharmony_ci WARN("unexpected ELF machine type %d", elf->ehdr.e_machine); 418c2ecf20Sopenharmony_ci return -1; 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cibool arch_callee_saved_reg(unsigned char reg) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci switch (reg) { 488c2ecf20Sopenharmony_ci case CFI_BP: 498c2ecf20Sopenharmony_ci case CFI_BX: 508c2ecf20Sopenharmony_ci case CFI_R12: 518c2ecf20Sopenharmony_ci case CFI_R13: 528c2ecf20Sopenharmony_ci case CFI_R14: 538c2ecf20Sopenharmony_ci case CFI_R15: 548c2ecf20Sopenharmony_ci return true; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci case CFI_AX: 578c2ecf20Sopenharmony_ci case CFI_CX: 588c2ecf20Sopenharmony_ci case CFI_DX: 598c2ecf20Sopenharmony_ci case CFI_SI: 608c2ecf20Sopenharmony_ci case CFI_DI: 618c2ecf20Sopenharmony_ci case CFI_SP: 628c2ecf20Sopenharmony_ci case CFI_R8: 638c2ecf20Sopenharmony_ci case CFI_R9: 648c2ecf20Sopenharmony_ci case CFI_R10: 658c2ecf20Sopenharmony_ci case CFI_R11: 668c2ecf20Sopenharmony_ci case CFI_RA: 678c2ecf20Sopenharmony_ci default: 688c2ecf20Sopenharmony_ci return false; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ciunsigned long arch_dest_reloc_offset(int addend) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci return addend + 4; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ciunsigned long arch_jump_destination(struct instruction *insn) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci return insn->offset + insn->len + insn->immediate; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci#define ADD_OP(op) \ 838c2ecf20Sopenharmony_ci if (!(op = calloc(1, sizeof(*op)))) \ 848c2ecf20Sopenharmony_ci return -1; \ 858c2ecf20Sopenharmony_ci else for (list_add_tail(&op->list, ops_list); op; op = NULL) 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ciint arch_decode_instruction(const struct elf *elf, const struct section *sec, 888c2ecf20Sopenharmony_ci unsigned long offset, unsigned int maxlen, 898c2ecf20Sopenharmony_ci unsigned int *len, enum insn_type *type, 908c2ecf20Sopenharmony_ci unsigned long *immediate, 918c2ecf20Sopenharmony_ci struct list_head *ops_list) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct insn insn; 948c2ecf20Sopenharmony_ci int x86_64, sign; 958c2ecf20Sopenharmony_ci unsigned char op1, op2, rex = 0, rex_b = 0, rex_r = 0, rex_w = 0, 968c2ecf20Sopenharmony_ci rex_x = 0, modrm = 0, modrm_mod = 0, modrm_rm = 0, 978c2ecf20Sopenharmony_ci modrm_reg = 0, sib = 0; 988c2ecf20Sopenharmony_ci struct stack_op *op = NULL; 998c2ecf20Sopenharmony_ci struct symbol *sym; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci x86_64 = is_x86_64(elf); 1028c2ecf20Sopenharmony_ci if (x86_64 == -1) 1038c2ecf20Sopenharmony_ci return -1; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci insn_init(&insn, sec->data->d_buf + offset, maxlen, x86_64); 1068c2ecf20Sopenharmony_ci insn_get_length(&insn); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (!insn_complete(&insn)) { 1098c2ecf20Sopenharmony_ci WARN("can't decode instruction at %s:0x%lx", sec->name, offset); 1108c2ecf20Sopenharmony_ci return -1; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci *len = insn.length; 1148c2ecf20Sopenharmony_ci *type = INSN_OTHER; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (insn.vex_prefix.nbytes) 1178c2ecf20Sopenharmony_ci return 0; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci op1 = insn.opcode.bytes[0]; 1208c2ecf20Sopenharmony_ci op2 = insn.opcode.bytes[1]; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (insn.rex_prefix.nbytes) { 1238c2ecf20Sopenharmony_ci rex = insn.rex_prefix.bytes[0]; 1248c2ecf20Sopenharmony_ci rex_w = X86_REX_W(rex) >> 3; 1258c2ecf20Sopenharmony_ci rex_r = X86_REX_R(rex) >> 2; 1268c2ecf20Sopenharmony_ci rex_x = X86_REX_X(rex) >> 1; 1278c2ecf20Sopenharmony_ci rex_b = X86_REX_B(rex); 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (insn.modrm.nbytes) { 1318c2ecf20Sopenharmony_ci modrm = insn.modrm.bytes[0]; 1328c2ecf20Sopenharmony_ci modrm_mod = X86_MODRM_MOD(modrm); 1338c2ecf20Sopenharmony_ci modrm_reg = X86_MODRM_REG(modrm); 1348c2ecf20Sopenharmony_ci modrm_rm = X86_MODRM_RM(modrm); 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (insn.sib.nbytes) 1388c2ecf20Sopenharmony_ci sib = insn.sib.bytes[0]; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci switch (op1) { 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci case 0x1: 1438c2ecf20Sopenharmony_ci case 0x29: 1448c2ecf20Sopenharmony_ci if (rex_w && !rex_b && modrm_mod == 3 && modrm_rm == 4) { 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci /* add/sub reg, %rsp */ 1478c2ecf20Sopenharmony_ci ADD_OP(op) { 1488c2ecf20Sopenharmony_ci op->src.type = OP_SRC_ADD; 1498c2ecf20Sopenharmony_ci op->src.reg = op_to_cfi_reg[modrm_reg][rex_r]; 1508c2ecf20Sopenharmony_ci op->dest.type = OP_DEST_REG; 1518c2ecf20Sopenharmony_ci op->dest.reg = CFI_SP; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci break; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci case 0x50 ... 0x57: 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* push reg */ 1598c2ecf20Sopenharmony_ci ADD_OP(op) { 1608c2ecf20Sopenharmony_ci op->src.type = OP_SRC_REG; 1618c2ecf20Sopenharmony_ci op->src.reg = op_to_cfi_reg[op1 & 0x7][rex_b]; 1628c2ecf20Sopenharmony_ci op->dest.type = OP_DEST_PUSH; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci break; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci case 0x58 ... 0x5f: 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* pop reg */ 1708c2ecf20Sopenharmony_ci ADD_OP(op) { 1718c2ecf20Sopenharmony_ci op->src.type = OP_SRC_POP; 1728c2ecf20Sopenharmony_ci op->dest.type = OP_DEST_REG; 1738c2ecf20Sopenharmony_ci op->dest.reg = op_to_cfi_reg[op1 & 0x7][rex_b]; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci break; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci case 0x68: 1798c2ecf20Sopenharmony_ci case 0x6a: 1808c2ecf20Sopenharmony_ci /* push immediate */ 1818c2ecf20Sopenharmony_ci ADD_OP(op) { 1828c2ecf20Sopenharmony_ci op->src.type = OP_SRC_CONST; 1838c2ecf20Sopenharmony_ci op->dest.type = OP_DEST_PUSH; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci break; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci case 0x70 ... 0x7f: 1888c2ecf20Sopenharmony_ci *type = INSN_JUMP_CONDITIONAL; 1898c2ecf20Sopenharmony_ci break; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci case 0x81: 1928c2ecf20Sopenharmony_ci case 0x83: 1938c2ecf20Sopenharmony_ci if (rex != 0x48) 1948c2ecf20Sopenharmony_ci break; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci if (modrm == 0xe4) { 1978c2ecf20Sopenharmony_ci /* and imm, %rsp */ 1988c2ecf20Sopenharmony_ci ADD_OP(op) { 1998c2ecf20Sopenharmony_ci op->src.type = OP_SRC_AND; 2008c2ecf20Sopenharmony_ci op->src.reg = CFI_SP; 2018c2ecf20Sopenharmony_ci op->src.offset = insn.immediate.value; 2028c2ecf20Sopenharmony_ci op->dest.type = OP_DEST_REG; 2038c2ecf20Sopenharmony_ci op->dest.reg = CFI_SP; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci break; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (modrm == 0xc4) 2098c2ecf20Sopenharmony_ci sign = 1; 2108c2ecf20Sopenharmony_ci else if (modrm == 0xec) 2118c2ecf20Sopenharmony_ci sign = -1; 2128c2ecf20Sopenharmony_ci else 2138c2ecf20Sopenharmony_ci break; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci /* add/sub imm, %rsp */ 2168c2ecf20Sopenharmony_ci ADD_OP(op) { 2178c2ecf20Sopenharmony_ci op->src.type = OP_SRC_ADD; 2188c2ecf20Sopenharmony_ci op->src.reg = CFI_SP; 2198c2ecf20Sopenharmony_ci op->src.offset = insn.immediate.value * sign; 2208c2ecf20Sopenharmony_ci op->dest.type = OP_DEST_REG; 2218c2ecf20Sopenharmony_ci op->dest.reg = CFI_SP; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci break; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci case 0x89: 2268c2ecf20Sopenharmony_ci if (rex_w && !rex_r && modrm_mod == 3 && modrm_reg == 4) { 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* mov %rsp, reg */ 2298c2ecf20Sopenharmony_ci ADD_OP(op) { 2308c2ecf20Sopenharmony_ci op->src.type = OP_SRC_REG; 2318c2ecf20Sopenharmony_ci op->src.reg = CFI_SP; 2328c2ecf20Sopenharmony_ci op->dest.type = OP_DEST_REG; 2338c2ecf20Sopenharmony_ci op->dest.reg = op_to_cfi_reg[modrm_rm][rex_b]; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci break; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (rex_w && !rex_b && modrm_mod == 3 && modrm_rm == 4) { 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* mov reg, %rsp */ 2418c2ecf20Sopenharmony_ci ADD_OP(op) { 2428c2ecf20Sopenharmony_ci op->src.type = OP_SRC_REG; 2438c2ecf20Sopenharmony_ci op->src.reg = op_to_cfi_reg[modrm_reg][rex_r]; 2448c2ecf20Sopenharmony_ci op->dest.type = OP_DEST_REG; 2458c2ecf20Sopenharmony_ci op->dest.reg = CFI_SP; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci break; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci /* fallthrough */ 2518c2ecf20Sopenharmony_ci case 0x88: 2528c2ecf20Sopenharmony_ci if (!rex_b && 2538c2ecf20Sopenharmony_ci (modrm_mod == 1 || modrm_mod == 2) && modrm_rm == 5) { 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* mov reg, disp(%rbp) */ 2568c2ecf20Sopenharmony_ci ADD_OP(op) { 2578c2ecf20Sopenharmony_ci op->src.type = OP_SRC_REG; 2588c2ecf20Sopenharmony_ci op->src.reg = op_to_cfi_reg[modrm_reg][rex_r]; 2598c2ecf20Sopenharmony_ci op->dest.type = OP_DEST_REG_INDIRECT; 2608c2ecf20Sopenharmony_ci op->dest.reg = CFI_BP; 2618c2ecf20Sopenharmony_ci op->dest.offset = insn.displacement.value; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci } else if (rex_w && !rex_b && modrm_rm == 4 && sib == 0x24) { 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* mov reg, disp(%rsp) */ 2678c2ecf20Sopenharmony_ci ADD_OP(op) { 2688c2ecf20Sopenharmony_ci op->src.type = OP_SRC_REG; 2698c2ecf20Sopenharmony_ci op->src.reg = op_to_cfi_reg[modrm_reg][rex_r]; 2708c2ecf20Sopenharmony_ci op->dest.type = OP_DEST_REG_INDIRECT; 2718c2ecf20Sopenharmony_ci op->dest.reg = CFI_SP; 2728c2ecf20Sopenharmony_ci op->dest.offset = insn.displacement.value; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci break; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci case 0x8b: 2798c2ecf20Sopenharmony_ci if (rex_w && !rex_b && modrm_mod == 1 && modrm_rm == 5) { 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* mov disp(%rbp), reg */ 2828c2ecf20Sopenharmony_ci ADD_OP(op) { 2838c2ecf20Sopenharmony_ci op->src.type = OP_SRC_REG_INDIRECT; 2848c2ecf20Sopenharmony_ci op->src.reg = CFI_BP; 2858c2ecf20Sopenharmony_ci op->src.offset = insn.displacement.value; 2868c2ecf20Sopenharmony_ci op->dest.type = OP_DEST_REG; 2878c2ecf20Sopenharmony_ci op->dest.reg = op_to_cfi_reg[modrm_reg][rex_r]; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci } else if (rex_w && !rex_b && sib == 0x24 && 2918c2ecf20Sopenharmony_ci modrm_mod != 3 && modrm_rm == 4) { 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci /* mov disp(%rsp), reg */ 2948c2ecf20Sopenharmony_ci ADD_OP(op) { 2958c2ecf20Sopenharmony_ci op->src.type = OP_SRC_REG_INDIRECT; 2968c2ecf20Sopenharmony_ci op->src.reg = CFI_SP; 2978c2ecf20Sopenharmony_ci op->src.offset = insn.displacement.value; 2988c2ecf20Sopenharmony_ci op->dest.type = OP_DEST_REG; 2998c2ecf20Sopenharmony_ci op->dest.reg = op_to_cfi_reg[modrm_reg][rex_r]; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci break; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci case 0x8d: 3068c2ecf20Sopenharmony_ci if (sib == 0x24 && rex_w && !rex_b && !rex_x) { 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci ADD_OP(op) { 3098c2ecf20Sopenharmony_ci if (!insn.displacement.value) { 3108c2ecf20Sopenharmony_ci /* lea (%rsp), reg */ 3118c2ecf20Sopenharmony_ci op->src.type = OP_SRC_REG; 3128c2ecf20Sopenharmony_ci } else { 3138c2ecf20Sopenharmony_ci /* lea disp(%rsp), reg */ 3148c2ecf20Sopenharmony_ci op->src.type = OP_SRC_ADD; 3158c2ecf20Sopenharmony_ci op->src.offset = insn.displacement.value; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci op->src.reg = CFI_SP; 3188c2ecf20Sopenharmony_ci op->dest.type = OP_DEST_REG; 3198c2ecf20Sopenharmony_ci op->dest.reg = op_to_cfi_reg[modrm_reg][rex_r]; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci } else if (rex == 0x48 && modrm == 0x65) { 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* lea disp(%rbp), %rsp */ 3258c2ecf20Sopenharmony_ci ADD_OP(op) { 3268c2ecf20Sopenharmony_ci op->src.type = OP_SRC_ADD; 3278c2ecf20Sopenharmony_ci op->src.reg = CFI_BP; 3288c2ecf20Sopenharmony_ci op->src.offset = insn.displacement.value; 3298c2ecf20Sopenharmony_ci op->dest.type = OP_DEST_REG; 3308c2ecf20Sopenharmony_ci op->dest.reg = CFI_SP; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci } else if (rex == 0x49 && modrm == 0x62 && 3348c2ecf20Sopenharmony_ci insn.displacement.value == -8) { 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* 3378c2ecf20Sopenharmony_ci * lea -0x8(%r10), %rsp 3388c2ecf20Sopenharmony_ci * 3398c2ecf20Sopenharmony_ci * Restoring rsp back to its original value after a 3408c2ecf20Sopenharmony_ci * stack realignment. 3418c2ecf20Sopenharmony_ci */ 3428c2ecf20Sopenharmony_ci ADD_OP(op) { 3438c2ecf20Sopenharmony_ci op->src.type = OP_SRC_ADD; 3448c2ecf20Sopenharmony_ci op->src.reg = CFI_R10; 3458c2ecf20Sopenharmony_ci op->src.offset = -8; 3468c2ecf20Sopenharmony_ci op->dest.type = OP_DEST_REG; 3478c2ecf20Sopenharmony_ci op->dest.reg = CFI_SP; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci } else if (rex == 0x49 && modrm == 0x65 && 3518c2ecf20Sopenharmony_ci insn.displacement.value == -16) { 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* 3548c2ecf20Sopenharmony_ci * lea -0x10(%r13), %rsp 3558c2ecf20Sopenharmony_ci * 3568c2ecf20Sopenharmony_ci * Restoring rsp back to its original value after a 3578c2ecf20Sopenharmony_ci * stack realignment. 3588c2ecf20Sopenharmony_ci */ 3598c2ecf20Sopenharmony_ci ADD_OP(op) { 3608c2ecf20Sopenharmony_ci op->src.type = OP_SRC_ADD; 3618c2ecf20Sopenharmony_ci op->src.reg = CFI_R13; 3628c2ecf20Sopenharmony_ci op->src.offset = -16; 3638c2ecf20Sopenharmony_ci op->dest.type = OP_DEST_REG; 3648c2ecf20Sopenharmony_ci op->dest.reg = CFI_SP; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci break; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci case 0x8f: 3718c2ecf20Sopenharmony_ci /* pop to mem */ 3728c2ecf20Sopenharmony_ci ADD_OP(op) { 3738c2ecf20Sopenharmony_ci op->src.type = OP_SRC_POP; 3748c2ecf20Sopenharmony_ci op->dest.type = OP_DEST_MEM; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci break; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci case 0x90: 3798c2ecf20Sopenharmony_ci *type = INSN_NOP; 3808c2ecf20Sopenharmony_ci break; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci case 0x9c: 3838c2ecf20Sopenharmony_ci /* pushf */ 3848c2ecf20Sopenharmony_ci ADD_OP(op) { 3858c2ecf20Sopenharmony_ci op->src.type = OP_SRC_CONST; 3868c2ecf20Sopenharmony_ci op->dest.type = OP_DEST_PUSHF; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci break; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci case 0x9d: 3918c2ecf20Sopenharmony_ci /* popf */ 3928c2ecf20Sopenharmony_ci ADD_OP(op) { 3938c2ecf20Sopenharmony_ci op->src.type = OP_SRC_POPF; 3948c2ecf20Sopenharmony_ci op->dest.type = OP_DEST_MEM; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci break; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci case 0x0f: 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (op2 == 0x01) { 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (modrm == 0xca) 4038c2ecf20Sopenharmony_ci *type = INSN_CLAC; 4048c2ecf20Sopenharmony_ci else if (modrm == 0xcb) 4058c2ecf20Sopenharmony_ci *type = INSN_STAC; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci } else if (op2 >= 0x80 && op2 <= 0x8f) { 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci *type = INSN_JUMP_CONDITIONAL; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci } else if (op2 == 0x05 || op2 == 0x07 || op2 == 0x34 || 4128c2ecf20Sopenharmony_ci op2 == 0x35) { 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* sysenter, sysret */ 4158c2ecf20Sopenharmony_ci *type = INSN_CONTEXT_SWITCH; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci } else if (op2 == 0x0b || op2 == 0xb9) { 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci /* ud2 */ 4208c2ecf20Sopenharmony_ci *type = INSN_BUG; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci } else if (op2 == 0x0d || op2 == 0x1f) { 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /* nopl/nopw */ 4258c2ecf20Sopenharmony_ci *type = INSN_NOP; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci } else if (op2 == 0xa0 || op2 == 0xa8) { 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* push fs/gs */ 4308c2ecf20Sopenharmony_ci ADD_OP(op) { 4318c2ecf20Sopenharmony_ci op->src.type = OP_SRC_CONST; 4328c2ecf20Sopenharmony_ci op->dest.type = OP_DEST_PUSH; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci } else if (op2 == 0xa1 || op2 == 0xa9) { 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* pop fs/gs */ 4388c2ecf20Sopenharmony_ci ADD_OP(op) { 4398c2ecf20Sopenharmony_ci op->src.type = OP_SRC_POP; 4408c2ecf20Sopenharmony_ci op->dest.type = OP_DEST_MEM; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci break; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci case 0xc9: 4478c2ecf20Sopenharmony_ci /* 4488c2ecf20Sopenharmony_ci * leave 4498c2ecf20Sopenharmony_ci * 4508c2ecf20Sopenharmony_ci * equivalent to: 4518c2ecf20Sopenharmony_ci * mov bp, sp 4528c2ecf20Sopenharmony_ci * pop bp 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_ci ADD_OP(op) 4558c2ecf20Sopenharmony_ci op->dest.type = OP_DEST_LEAVE; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci break; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci case 0xcc: 4608c2ecf20Sopenharmony_ci /* int3 */ 4618c2ecf20Sopenharmony_ci *type = INSN_TRAP; 4628c2ecf20Sopenharmony_ci break; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci case 0xe3: 4658c2ecf20Sopenharmony_ci /* jecxz/jrcxz */ 4668c2ecf20Sopenharmony_ci *type = INSN_JUMP_CONDITIONAL; 4678c2ecf20Sopenharmony_ci break; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci case 0xe9: 4708c2ecf20Sopenharmony_ci case 0xeb: 4718c2ecf20Sopenharmony_ci *type = INSN_JUMP_UNCONDITIONAL; 4728c2ecf20Sopenharmony_ci break; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci case 0xc2: 4758c2ecf20Sopenharmony_ci case 0xc3: 4768c2ecf20Sopenharmony_ci *type = INSN_RETURN; 4778c2ecf20Sopenharmony_ci break; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci case 0xcf: /* iret */ 4808c2ecf20Sopenharmony_ci /* 4818c2ecf20Sopenharmony_ci * Handle sync_core(), which has an IRET to self. 4828c2ecf20Sopenharmony_ci * All other IRET are in STT_NONE entry code. 4838c2ecf20Sopenharmony_ci */ 4848c2ecf20Sopenharmony_ci sym = find_symbol_containing(sec, offset); 4858c2ecf20Sopenharmony_ci if (sym && sym->type == STT_FUNC) { 4868c2ecf20Sopenharmony_ci ADD_OP(op) { 4878c2ecf20Sopenharmony_ci /* add $40, %rsp */ 4888c2ecf20Sopenharmony_ci op->src.type = OP_SRC_ADD; 4898c2ecf20Sopenharmony_ci op->src.reg = CFI_SP; 4908c2ecf20Sopenharmony_ci op->src.offset = 5*8; 4918c2ecf20Sopenharmony_ci op->dest.type = OP_DEST_REG; 4928c2ecf20Sopenharmony_ci op->dest.reg = CFI_SP; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci break; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci /* fallthrough */ 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci case 0xca: /* retf */ 5008c2ecf20Sopenharmony_ci case 0xcb: /* retf */ 5018c2ecf20Sopenharmony_ci *type = INSN_CONTEXT_SWITCH; 5028c2ecf20Sopenharmony_ci break; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci case 0xe8: 5058c2ecf20Sopenharmony_ci *type = INSN_CALL; 5068c2ecf20Sopenharmony_ci /* 5078c2ecf20Sopenharmony_ci * For the impact on the stack, a CALL behaves like 5088c2ecf20Sopenharmony_ci * a PUSH of an immediate value (the return address). 5098c2ecf20Sopenharmony_ci */ 5108c2ecf20Sopenharmony_ci ADD_OP(op) { 5118c2ecf20Sopenharmony_ci op->src.type = OP_SRC_CONST; 5128c2ecf20Sopenharmony_ci op->dest.type = OP_DEST_PUSH; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci break; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci case 0xfc: 5178c2ecf20Sopenharmony_ci *type = INSN_CLD; 5188c2ecf20Sopenharmony_ci break; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci case 0xfd: 5218c2ecf20Sopenharmony_ci *type = INSN_STD; 5228c2ecf20Sopenharmony_ci break; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci case 0xff: 5258c2ecf20Sopenharmony_ci if (modrm_reg == 2 || modrm_reg == 3) 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci *type = INSN_CALL_DYNAMIC; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci else if (modrm_reg == 4) 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci *type = INSN_JUMP_DYNAMIC; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci else if (modrm_reg == 5) 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci /* jmpf */ 5368c2ecf20Sopenharmony_ci *type = INSN_CONTEXT_SWITCH; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci else if (modrm_reg == 6) { 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci /* push from mem */ 5418c2ecf20Sopenharmony_ci ADD_OP(op) { 5428c2ecf20Sopenharmony_ci op->src.type = OP_SRC_CONST; 5438c2ecf20Sopenharmony_ci op->dest.type = OP_DEST_PUSH; 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci break; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci default: 5508c2ecf20Sopenharmony_ci break; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci *immediate = insn.immediate.nbytes ? insn.immediate.value : 0; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci return 0; 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_civoid arch_initial_func_cfi_state(struct cfi_init_state *state) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci int i; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci for (i = 0; i < CFI_NUM_REGS; i++) { 5638c2ecf20Sopenharmony_ci state->regs[i].base = CFI_UNDEFINED; 5648c2ecf20Sopenharmony_ci state->regs[i].offset = 0; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci /* initial CFA (call frame address) */ 5688c2ecf20Sopenharmony_ci state->cfa.base = CFI_SP; 5698c2ecf20Sopenharmony_ci state->cfa.offset = 8; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* initial RA (return address) */ 5728c2ecf20Sopenharmony_ci state->regs[CFI_RA].base = CFI_CFA; 5738c2ecf20Sopenharmony_ci state->regs[CFI_RA].offset = -8; 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ciconst char *arch_nop_insn(int len) 5778c2ecf20Sopenharmony_ci{ 5788c2ecf20Sopenharmony_ci static const char nops[5][5] = { 5798c2ecf20Sopenharmony_ci /* 1 */ { 0x90 }, 5808c2ecf20Sopenharmony_ci /* 2 */ { 0x66, 0x90 }, 5818c2ecf20Sopenharmony_ci /* 3 */ { 0x0f, 0x1f, 0x00 }, 5828c2ecf20Sopenharmony_ci /* 4 */ { 0x0f, 0x1f, 0x40, 0x00 }, 5838c2ecf20Sopenharmony_ci /* 5 */ { 0x0f, 0x1f, 0x44, 0x00, 0x00 }, 5848c2ecf20Sopenharmony_ci }; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci if (len < 1 || len > 5) { 5878c2ecf20Sopenharmony_ci WARN("invalid NOP size: %d\n", len); 5888c2ecf20Sopenharmony_ci return NULL; 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci return nops[len-1]; 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci#define BYTE_RET 0xC3 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ciconst char *arch_ret_insn(int len) 5978c2ecf20Sopenharmony_ci{ 5988c2ecf20Sopenharmony_ci static const char ret[5][5] = { 5998c2ecf20Sopenharmony_ci { BYTE_RET }, 6008c2ecf20Sopenharmony_ci { BYTE_RET, 0xcc }, 6018c2ecf20Sopenharmony_ci { BYTE_RET, 0xcc, 0x90 }, 6028c2ecf20Sopenharmony_ci { BYTE_RET, 0xcc, 0x66, 0x90 }, 6038c2ecf20Sopenharmony_ci { BYTE_RET, 0xcc, 0x0f, 0x1f, 0x00 }, 6048c2ecf20Sopenharmony_ci }; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci if (len < 1 || len > 5) { 6078c2ecf20Sopenharmony_ci WARN("invalid RET size: %d\n", len); 6088c2ecf20Sopenharmony_ci return NULL; 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci return ret[len-1]; 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ciint arch_decode_hint_reg(u8 sp_reg, int *base) 6158c2ecf20Sopenharmony_ci{ 6168c2ecf20Sopenharmony_ci switch (sp_reg) { 6178c2ecf20Sopenharmony_ci case ORC_REG_UNDEFINED: 6188c2ecf20Sopenharmony_ci *base = CFI_UNDEFINED; 6198c2ecf20Sopenharmony_ci break; 6208c2ecf20Sopenharmony_ci case ORC_REG_SP: 6218c2ecf20Sopenharmony_ci *base = CFI_SP; 6228c2ecf20Sopenharmony_ci break; 6238c2ecf20Sopenharmony_ci case ORC_REG_BP: 6248c2ecf20Sopenharmony_ci *base = CFI_BP; 6258c2ecf20Sopenharmony_ci break; 6268c2ecf20Sopenharmony_ci case ORC_REG_SP_INDIRECT: 6278c2ecf20Sopenharmony_ci *base = CFI_SP_INDIRECT; 6288c2ecf20Sopenharmony_ci break; 6298c2ecf20Sopenharmony_ci case ORC_REG_R10: 6308c2ecf20Sopenharmony_ci *base = CFI_R10; 6318c2ecf20Sopenharmony_ci break; 6328c2ecf20Sopenharmony_ci case ORC_REG_R13: 6338c2ecf20Sopenharmony_ci *base = CFI_R13; 6348c2ecf20Sopenharmony_ci break; 6358c2ecf20Sopenharmony_ci case ORC_REG_DI: 6368c2ecf20Sopenharmony_ci *base = CFI_DI; 6378c2ecf20Sopenharmony_ci break; 6388c2ecf20Sopenharmony_ci case ORC_REG_DX: 6398c2ecf20Sopenharmony_ci *base = CFI_DX; 6408c2ecf20Sopenharmony_ci break; 6418c2ecf20Sopenharmony_ci default: 6428c2ecf20Sopenharmony_ci return -1; 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci return 0; 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_cibool arch_is_retpoline(struct symbol *sym) 6498c2ecf20Sopenharmony_ci{ 6508c2ecf20Sopenharmony_ci return !strncmp(sym->name, "__x86_indirect_", 15); 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cibool arch_is_rethunk(struct symbol *sym) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci return !strcmp(sym->name, "__x86_return_thunk"); 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cibool arch_is_embedded_insn(struct symbol *sym) 6598c2ecf20Sopenharmony_ci{ 6608c2ecf20Sopenharmony_ci return !strcmp(sym->name, "retbleed_return_thunk") || 6618c2ecf20Sopenharmony_ci !strcmp(sym->name, "srso_safe_ret"); 6628c2ecf20Sopenharmony_ci} 663