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