#include "../internal.h"

////////////////////////////////////////////////////////////////////////////////
// IN-kernel length disassembler engine (x86 only, 2.6.33+)
////////////////////////////////////////////////////////////////////////////////

#include <asm/insn.h>

/*
 * typeof 是 GCC的一个扩展关键字
 * 它用于在预处理阶段确定一个变量或表达式的类型
 * 而不需要实际创建该变量或表达式的实例
 */
static struct {
	typeof(insn_init) *init;
	typeof(insn_get_length) *get_length;
} khook_arch_lde;

/*
 *初始化函数，用于查找并初始化 insn_init 和 insn_get_length 函数的地址
 */
static inline int khook_arch_lde_init(void) {
	khook_arch_lde.init = khook_lookup_name("insn_init");
	if (!khook_arch_lde.init) return -EINVAL;
	khook_arch_lde.get_length = khook_lookup_name("insn_get_length");
	if (!khook_arch_lde.get_length) return -EINVAL;
	return 0;
}

/*
 * 获取地址p的指令的长度
 * 先调用insn_init获得insn结构
 * 然后调用get_length得到指令长度，结果存放在insn的length字段
 */
static inline int khook_arch_lde_get_length(const void *p) {
	struct insn insn;
	int x86_64 = 0;
#ifdef CONFIG_X86_64
	x86_64 = 1;
#endif
#if defined MAX_INSN_SIZE && (MAX_INSN_SIZE == 15) /* 3.19.7+ */
	khook_arch_lde.init(&insn, p, MAX_INSN_SIZE, x86_64);
#else
	khook_arch_lde.init(&insn, p, x86_64);
#endif
	khook_arch_lde.get_length(&insn);
	return insn.length;
}

////////////////////////////////////////////////////////////////////////////////

// place a jump at addr @a from addr @f to addr @t
static inline void x86_put_jmp(void *a, void *f, void *t)
{
	*((char *)(a + 0)) = 0xE9; //跳转指令的Opcode
	*(( int *)(a + 1)) = (long)(t - (f + 5)); //偏移计算
}

static const char khook_stub_template[] = {
# include KHOOK_STUB_FILE_NAME
};

static inline void stub_fixup(void *stub, const void *value) {
	while (*(int *)stub != 0xcacacaca) stub++;
	*(long *)stub = (long)value;
}

/*
 * 初始化一个钩子
 * 使其能够在特定条件下跳转到新的函数地址
 * 同时保留原始指令以便在需要时恢复
 */
static inline void khook_arch_sm_init_one(khook_t *hook) {
	khook_stub_t *stub = KHOOK_STUB(hook);
	// 跳转指令的第一个字节是0xE9或0xCC，表示已经被hook过了
	if (hook->target.addr[0] == (char)0xE9 ||
	    hook->target.addr[0] == (char)0xCC) return;

	BUILD_BUG_ON(sizeof(khook_stub_template) > offsetof(khook_stub_t, nbytes));
	memcpy(stub, khook_stub_template, sizeof(khook_stub_template)); // 拷贝模板到stub中
	stub_fixup(stub->hook, hook->fn);

	while (stub->nbytes < 5)
		stub->nbytes += khook_arch_lde_get_length(hook->target.addr + stub->nbytes);

	memcpy(stub->orig, hook->target.addr, stub->nbytes); // 拷贝hook原函数到stub的orig中
	x86_put_jmp(stub->orig + stub->nbytes, stub->orig + stub->nbytes, hook->target.addr + stub->nbytes); // 设置跳转
	if (hook->flags & KHOOK_F_NOREF) {
		x86_put_jmp(hook->target.addr_map, hook->target.addr, hook->fn);
	} else {
		x86_put_jmp(hook->target.addr_map, hook->target.addr, stub->hook);
	}
	hook->orig = stub->orig; // the only link from hook to stub
}

/*
 * 清理一个钩子
 * 将目标地址恢复为原始指令
 */
static inline void khook_arch_sm_cleanup_one(khook_t *hook) {
	khook_stub_t *stub = KHOOK_STUB(hook);
	memcpy(hook->target.addr_map, stub->orig, stub->nbytes);
}

#define KHOOK_ARCH_INIT(...)					\
	(khook_arch_lde_init())
