Linux x86_64平台指令替换函数 text_poke_smp/bp

文章目录

  • 前言
  • 一、text_poke_early
    • 1.1 text_poke_early简介
    • 1.2 用途
  • 二、text_poke_smp
    • 2.1 简介
      • 2.1.1 text_poke_smp函数
      • 2.2.2 stop_machine_text_poke简介
      • 2.2.3 text_poke函数
    • 2.2 用途
  • 三、text_poke_smp 内核hook

前言

Linux x86_64平台指令替换函数有两种类型:
(1)在内核启动阶段指令替换函数:text_poke_early
(2)在内核运行阶段指令替换函数:text_poke_smp/bp

在3.12.0版本以后,内核运行阶段指令替换函数由text_poke_smp改为text_poke_bp。

这里我以centos7 3.10.0为例主要介绍text_poke_smp函数。

一、text_poke_early

1.1 text_poke_early简介

// linux-3.10/arch/x86/kernel/alternative.c

/**
 * text_poke_early - Update instructions on a live kernel at boot time
 * @addr: address to modify
 * @opcode: source of the copy
 * @len: length to copy
 *
 * When you use this code to patch more than one byte of an instruction
 * you need to make sure that other CPUs cannot execute this code in parallel.
 * Also no thread must be currently preempted in the middle of these
 * instructions. And on the local CPU you need to be protected again NMI or MCE
 * handlers seeing an inconsistent instruction while you patch.
 */
void *__init_or_module text_poke_early(void *addr, const void *opcode,
					      size_t len)
{
	unsigned long flags;
	local_irq_save(flags);
	memcpy(addr, opcode, len);
	sync_core();
	local_irq_restore(flags);
	/* Could also do a CLFLUSH here to speed up CPU recovery; but
	   that causes hangs on some VIA CPUs. */
	return addr;
}

这段代码实现了在系统引导时(boot time)修改内核指令的函数text_poke_early()。它用于在引导过程中更新内核指令,以便在早期阶段进行修补。

函数的参数包括:

addr:要修改的指令的地址。
opcode:要复制的指令的源地址。
len:要复制的指令长度。

函数的实现步骤如下:
(1)使用local_irq_save()保存当前CPU的中断状态,并禁用中断。
(2)使用memcpy()将源指令(opcode)的内容复制到目标地址(addr)。
(3)调用sync_core()进行同步,以确保指令修改生效。
(4)使用local_irq_restore()恢复之前保存的中断状态,重新启用中断。

1.2 用途

主要用于以下几个地方:
(1)alternatives 指令替换:具体可以参考:Linux x86_64架构 动态替换 altinstructions

内核启动时进行alternatives 指令替换。

/* Replace instructions with better alternatives for this CPU type.
   This runs before SMP is initialized to avoid SMP problems with
   self modifying code. This implies that asymmetric systems where
   APs have less capabilities than the boot processor are not handled.
   Tough. Make sure you disable such features by hand. */

void __init_or_module apply_alternatives(struct alt_instr *start,
					 struct alt_instr *end)
{
	struct alt_instr *a;
	u8 *instr, *replacement;
	u8 insnbuf[MAX_PATCH_LEN];

	DPRINTK("%s: alt table %p -> %p\n", __func__, start, end);
	/*
	 * The scan order should be from start to end. A later scanned
	 * alternative code can overwrite a previous scanned alternative code.
	 * Some kernel functions (e.g. memcpy, memset, etc) use this order to
	 * patch code.
	 *
	 * So be careful if you want to change the scan order to any other
	 * order.
	 */
	for (a = start; a < end; a++) {
		instr = (u8 *)&a->instr_offset + a->instr_offset;
		replacement = (u8 *)&a->repl_offset + a->repl_offset;
		BUG_ON(a->replacementlen > a->instrlen);
		BUG_ON(a->instrlen > sizeof(insnbuf));
		BUG_ON(a->cpuid >= (NCAPINTS + NBUGINTS) * 32);
		if (!boot_cpu_has(a->cpuid))
			continue;

		memcpy(insnbuf, replacement, a->replacementlen);

		/* 0xe8 is a relative jump; fix the offset. */
		if (*insnbuf == 0xe8 && a->replacementlen == 5)
		    *(s32 *)(insnbuf + 1) += replacement - instr;

		add_nops(insnbuf + a->replacementlen,
			 a->instrlen - a->replacementlen);

		text_poke_early(instr, insnbuf, a->instrlen);
	}
}

(2)Linux jump label机制:具体可参考:Linux Static Keys和jump label机制

内核启动时修改jump label。

// linux-3.10/init/main.c
start_kernel()
	-->jump_label_init()
		-->arch_jump_label_transform_static()
__init_or_module void arch_jump_label_transform_static(struct jump_entry *entry,
				      enum jump_label_type type)
{
	__jump_label_transform(entry, type, text_poke_early);
}
static void __jump_label_transform(struct jump_entry *entry,
				   enum jump_label_type type,
				   void *(*poker)(void *, const void *, size_t))
{
	union jump_code_union code;

	if (type == JUMP_LABEL_ENABLE) {
		code.jump = 0xe9;
		code.offset = entry->target -
				(entry->code + JUMP_LABEL_NOP_SIZE);
	} else
		memcpy(&code, ideal_nops[NOP_ATOMIC5], JUMP_LABEL_NOP_SIZE);

	(*poker)((void *)entry->code, &code, JUMP_LABEL_NOP_SIZE);
}

(3)Linux Static calls机制:Linux Static calls机制

// linux-5.15/arch/x86/kernel/static_call.c

static void __ref __static_call_transform(void *insn, enum insn_type type, void *func)
{
	const void *emulate = NULL;
	int size = CALL_INSN_SIZE;
	const void *code;

	switch (type) {
	case CALL:
		code = text_gen_insn(CALL_INSN_OPCODE, insn, func);
		if (func == &__static_call_return0) {
			emulate = code;
			code = &xor5rax;
		}

		break;

	case NOP:
		code = x86_nops[5];
		break;

	case JMP:
		code = text_gen_insn(JMP32_INSN_OPCODE, insn, func);
		break;

	case RET:
		code = text_gen_insn(RET_INSN_OPCODE, insn, func);
		size = RET_INSN_SIZE;
		break;
	}

	if (memcmp(insn, code, size) == 0)
		return;

	if (unlikely(system_state == SYSTEM_BOOTING))
		return text_poke_early(insn, code, size);

	text_poke_bp(insn, code, size, emulate);
}

(4)ftrace

二、text_poke_smp

2.1 简介

2.1.1 text_poke_smp函数

/**
 * text_poke_smp - Update instructions on a live kernel on SMP
 * @addr: address to modify
 * @opcode: source of the copy
 * @len: length to copy
 *
 * Modify multi-byte instruction by using stop_machine() on SMP. This allows
 * user to poke/set multi-byte text on SMP. Only non-NMI/MCE code modifying
 * should be allowed, since stop_machine() does _not_ protect code against
 * NMI and MCE.
 *
 * Note: Must be called under get_online_cpus() and text_mutex.
 */
void *__kprobes text_poke_smp(void *addr, const void *opcode, size_t len)
{
	struct text_poke_params tpp;
	struct text_poke_param p;

	p.addr = addr;
	p.opcode = opcode;
	p.len = len;
	tpp.params = &p;
	tpp.nparams = 1;
	atomic_set(&stop_machine_first, 1);
	wrote_text = 0;
	/* Use __stop_machine() because the caller already got online_cpus. */
	__stop_machine(stop_machine_text_poke, (void *)&tpp, cpu_online_mask);
	return addr;
}

这段代码实现了在SMP(对称多处理器)系统上更新内核中的指令的函数text_poke_smp()。它使用stop_machine()函数在SMP系统上修改多字节指令。这允许用户在SMP系统上修改多字节的内核指令。但需要注意的是,由于stop_machine()函数无法保护代码免受NMI(非屏蔽中断)和MCE(机器检查异常)的影响,因此只应允许非NMI和MCE的代码进行修改。

函数的参数包括:

addr:要修改的指令的地址。
opcode:要复制的指令的源地址。
len:要复制的指令长度。

主要调用stop_machine_text_poke函数。

2.2.2 stop_machine_text_poke简介

/*
 * Cross-modifying kernel text with stop_machine().
 * This code originally comes from immediate value.
 */
static atomic_t stop_machine_first;
static int wrote_text;

struct text_poke_params {
	struct text_poke_param *params;
	int nparams;
};

static int __kprobes stop_machine_text_poke(void *data)
{
	struct text_poke_params *tpp = data;
	struct text_poke_param *p;
	int i;

	if (atomic_xchg(&stop_machine_first, 0)) {
		for (i = 0; i < tpp->nparams; i++) {
			p = &tpp->params[i];
			text_poke(p->addr, p->opcode, p->len);
		}
		smp_wmb();	/* Make sure other cpus see that this has run */
		wrote_text = 1;
	} else {
		while (!wrote_text)
			cpu_relax();
		smp_mb();	/* Load wrote_text before following execution */
	}

	for (i = 0; i < tpp->nparams; i++) {
		p = &tpp->params[i];
		flush_icache_range((unsigned long)p->addr,
				   (unsigned long)p->addr + p->len);
	}
	/*
	 * Intel Archiecture Software Developer's Manual section 7.1.3 specifies
	 * that a core serializing instruction such as "cpuid" should be
	 * executed on _each_ core before the new instruction is made visible.
	 */
	sync_core();
	return 0;
}

这段代码实现了使用stop_machine()函数进行内核文本交叉修改的功能。它通过stop_machine_text_poke()函数实现了在停机状态下修改内核文本的操作。

代码中使用了以下全局变量:

atomic_t类型的stop_machine_first:用于标记是否为第一次执行stop_machine_text_poke()函数。
int类型的wrote_text:表示是否已经修改了文本。

结构体text_poke_params定义了传递给stop_machine_text_poke()函数的参数:

params:指向text_poke_param结构体数组的指针,用于存储要修改的参数。
nparams:参数数组的长度。

stop_machine_text_poke()函数的实现步骤如下:
(1)通过传递的参数data获取text_poke_params结构体指针tpp。
(2)如果atomic_xchg()函数将stop_machine_first的值交换为0,表示当前是第一次执行stop_machine_text_poke()函数,则执行以下操作:
a:遍历tpp->params数组,依次获取text_poke_param结构体指针p。
b:调用text_poke()函数,将p->addr地址处的指令替换为p->opcode指令,长度为p->len。
c: 使用smp_wmb()确保其他CPU能够看到此次修改。
d:将wrote_text设置为1,表示已经修改了文本。
(3)否则,即不是第一次执行stop_machine_text_poke()函数,则进入循环,直到wrote_text变为非0。
(4)使用smp_mb()在执行后加载wrote_text之前进行内存屏障操作。
(5)遍历tpp->params数组,依次获取text_poke_param结构体指针p。
(6)调用flush_icache_range()函数,刷新从p->addr到(p->addr + p->len)范围内的指令缓存。
(7)使用sync_core()函数进行核心同步,确保新指令在每个核心上都可见。

2.2.3 text_poke函数

/**
 * text_poke - Update instructions on a live kernel
 * @addr: address to modify
 * @opcode: source of the copy
 * @len: length to copy
 *
 * Only atomic text poke/set should be allowed when not doing early patching.
 * It means the size must be writable atomically and the address must be aligned
 * in a way that permits an atomic write. It also makes sure we fit on a single
 * page.
 *
 * Note: Must be called under text_mutex.
 */
void *__kprobes text_poke(void *addr, const void *opcode, size_t len)
{
	unsigned long flags;
	char *vaddr;
	struct page *pages[2];
	int i;

	if (!core_kernel_text((unsigned long)addr)) {
		pages[0] = vmalloc_to_page(addr);
		pages[1] = vmalloc_to_page(addr + PAGE_SIZE);
	} else {
		pages[0] = virt_to_page(addr);
		WARN_ON(!PageReserved(pages[0]));
		pages[1] = virt_to_page(addr + PAGE_SIZE);
	}
	BUG_ON(!pages[0]);
	local_irq_save(flags);
	set_fixmap(FIX_TEXT_POKE0, page_to_phys(pages[0]));
	if (pages[1])
		set_fixmap(FIX_TEXT_POKE1, page_to_phys(pages[1]));
	vaddr = (char *)fix_to_virt(FIX_TEXT_POKE0);
	memcpy(&vaddr[(unsigned long)addr & ~PAGE_MASK], opcode, len);
	clear_fixmap(FIX_TEXT_POKE0);
	if (pages[1])
		clear_fixmap(FIX_TEXT_POKE1);
	local_flush_tlb();
	sync_core();
	/* Could also do a CLFLUSH here to speed up CPU recovery; but
	   that causes hangs on some VIA CPUs. */
	for (i = 0; i < len; i++)
		BUG_ON(((char *)addr)[i] != ((char *)opcode)[i]);
	local_irq_restore(flags);
	return addr;
}

这段代码实现了在实时内核上更新指令的函数text_poke()。它用于在实时内核中修改指定地址的指令。

函数的参数包括:

addr:要修改的指令的地址。
opcode:要复制的指令的源地址。
len:要复制的指令的长度。

函数的实现步骤如下:
(1)根据地址addr的特性,判断是否位于核心内核文本区域。如果不是核心内核文本区域,使用vmalloc_to_page()将地址转换为对应的内存页,并存储在pages数组中的第一个位置和第二个位置。
(2)如果地址位于核心内核文本区域,使用virt_to_page()将地址转换为对应的内存页,并存储在pages数组中的第一个位置。同时,使用WARN_ON()检查该页是否为保留页。
(3)使用BUG_ON()检查pages[0]是否为空。
(4)使用local_irq_save()保存当前中断状态,并禁用中断。
(5)使用set_fixmap()将pages[0]与FIX_TEXT_POKE0进行映射,将其物理地址设置为页表项的值。
(6)如果pages[1]不为空,使用set_fixmap()将pages[1]与FIX_TEXT_POKE1进行映射,将其物理地址设置为页表项的值。
(7)将fix_to_virt()的结果赋给vaddr,即获取FIX_TEXT_POKE0映射的虚拟地址。
(8)使用memcpy()将opcode的内容复制到vaddr和addr在页内的相对偏移位置上,长度为len。
(9)使用clear_fixmap()清除FIX_TEXT_POKE0的映射。
(10)如果pages[1]不为空,使用clear_fixmap()清除FIX_TEXT_POKE1的映射。
(11)使用local_flush_tlb()刷新TLB(转换后备缓冲)以确保新的页表项生效。
(12)使用sync_core()进行核心同步,确保新指令在每个核心上都可见。
(13)使用一个循环遍历检查修改后的指令是否和原始指令一致。如果不一致,使用BUG_ON()触发错误。
(14)使用local_irq_restore()恢复之前保存的中断状态。
(15)返回被修改的地址。

该函数用于在实时内核中修改指令。它将指定地址的指令替换为新的指令,并进行了一系列的同步操作和错误检查,以确保修改的指令能够正确执行。需要注意的是,在调用text_poke()函数之前,必须在text_mutex的保护下进行,以确保互斥访问。

2.2 用途

(1)Linux jump label机制:具体可参考:Linux Static Keys和jump label机制

内核运行时修改jump label。

static_key_slow_inc/dec
	-->jump_label_update()
		-->__jump_label_update()
			-->arch_jump_label_transform()
void arch_jump_label_transform(struct jump_entry *entry,
			       enum jump_label_type type)
{
	get_online_cpus();
	mutex_lock(&text_mutex);
	__jump_label_transform(entry, type, text_poke_smp);
	mutex_unlock(&text_mutex);
	put_online_cpus();
}

(2)kprobe机制:优化kprobe,使用jmp指令替换int3指令。

/*
 * Replace breakpoints (int3) with relative jumps.
 * Caller must call with locking kprobe_mutex and text_mutex.
 */
void __kprobes arch_optimize_kprobes(struct list_head *oplist)
{
	struct optimized_kprobe *op, *tmp;
	int c = 0;

	list_for_each_entry_safe(op, tmp, oplist, list) {
		WARN_ON(kprobe_disabled(&op->kp));
		/* Setup param */
		setup_optimize_kprobe(&jump_poke_params[c],
				      jump_poke_bufs[c].buf, op);
		list_del_init(&op->list);
		if (++c >= MAX_OPTIMIZE_PROBES)
			break;
	}

	/*
	 * text_poke_smp doesn't support NMI/MCE code modifying.
	 * However, since kprobes itself also doesn't support NMI/MCE
	 * code probing, it's not a problem.
	 */
	text_poke_smp_batch(jump_poke_params, c);
}

(3)Linux Static calls机制:Linux Static calls机制

// linux-5.15/arch/x86/kernel/static_call.c

static void __ref __static_call_transform(void *insn, enum insn_type type, void *func)
{
	const void *emulate = NULL;
	int size = CALL_INSN_SIZE;
	const void *code;

	switch (type) {
	case CALL:
		code = text_gen_insn(CALL_INSN_OPCODE, insn, func);
		if (func == &__static_call_return0) {
			emulate = code;
			code = &xor5rax;
		}

		break;

	case NOP:
		code = x86_nops[5];
		break;

	case JMP:
		code = text_gen_insn(JMP32_INSN_OPCODE, insn, func);
		break;

	case RET:
		code = text_gen_insn(RET_INSN_OPCODE, insn, func);
		size = RET_INSN_SIZE;
		break;
	}

	if (memcmp(insn, code, size) == 0)
		return;

	if (unlikely(system_state == SYSTEM_BOOTING))
		return text_poke_early(insn, code, size);

	text_poke_bp(insn, code, size, emulate);
}

(4)ftrace

三、text_poke_smp 内核hook

text_poke_smp 可以在内核运行时修改内存指令,因此可以用来进行内核hook。

例子请参考:
https://blog.csdn.net/bin_linux96/article/details/105776231
https://blog.csdn.net/dog250/article/details/105254739
https://blog.csdn.net/dog250/article/details/105787199
https://blog.csdn.net/dog250/article/details/84201114
https://blog.csdn.net/qq_21792169/article/details/84583275
https://richardweiyang-2.gitbook.io/kernel-exploring/00-index/04-ftrace_internal

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/774457.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【浦语大模型开源探索】InternLM实战营第二期:技术笔记与全链路解析

本次课程链接在GitHub上&#xff1a; InternLM/Tutorial at camp2 (github.com) 第一次课程录播链接&#xff1a; 书生浦语大模型全链路开源体系_哔哩哔哩_bilibili InternLM2技术报告&#xff1a; arxiv.org/pdf/2403.17297.pdf 一、书生浦语大模型全链路开源体系笔记 Int…

v-html 空格/换行不生效

接口返回的内容如下&#xff1a;有空格有换行&#xff0c;但 使用v-html无效 需加css样式 white-space: pre-wrap; <div class"pretty-html" v-html"Value"></div>.pretty-html {white-space: pre-wrap; /* 保留空格和换行&#xff0c;并允许…

震撼发布!4M-21:苹果多模态AI巨擘,一键解锁21种模态

前沿科技速递&#x1f680; 来自洛桑联邦理工学院&#xff08;EPFL&#xff09;与苹果科研巨擘的强强联手&#xff0c;震撼发布全新跨时代成果——4M-21模型&#xff01;这一革命性单一模型&#xff0c;突破性地覆盖了数十种高度多样化的模态&#xff0c;通过大规模多模态数据集…

数据洞察:从零到一的数据仓库与Navicat连接全攻略【实训Day04】[完结篇]

一、数据分析 1 实现数据仓库(在hadoop101上) 1) 创建jobdata数据库 # cd $HIVE_HOME # bin/hive hive>create database jobdata; hive>use jobdata; 2) 创建原始职位数据事实表ods_jobdata_orgin(在hadoop101上) create table ods_jobdata_origin( city string CO…

【测试开发】【postman】按顺序循环执行接口

postman按顺序循环执行接口 新建接口接口排序执行请求集合 新建接口 Request 001 Request 002 Request 003 接口排序 在Request 001的Tests中添加代码 postman.setNextRequest("Request 002");在Request 002的Tests中添加代码 postman.setNextRequest("Requ…

ASP.NET Core 使用Log4net

1. Nuget安装log4net&#xff0c;图里的两个 2.项目根目录下添加log4net.config.添加下面的代码: <?xml version"1.0" encoding"utf-8"?> <configuration><!-- This section contains the log4net configuration settings --><log…

“免费”的可视化大屏案例分享-智慧园区综合管理平台

一.智慧园区是什么&#xff1f; 智慧园区是一种融合了新一代信息与通信技术的先进园区发展理念。它通过迅捷信息采集、高速信息传输、高度集中计算、智能事务处理和无所不在的服务提供能力&#xff0c;实现了园区内及时、互动、整合的信息感知、传递和处理。这样的园区旨在提高…

k8s离线安装安装skywalking9.4

目录 概述资源下载Skywalking功能介绍成果速览实践rbacoapoap-svcuiui-svc 结束 概述 k8s 离线安装安装 skywalking9.4 版本&#xff0c;环境&#xff1a;k8s版本为&#xff1a;1.27.x 、spring boot 2.7.x spring cloud &#xff1a;2021.0.5 、spring.cloud.alibab&#xff1…

IDEA如何引入外部jar包

导了3次&#xff0c;记不住&#xff0c;写篇博客记一下&#xff1b; 1、File->Project Structure->项目名称->JARs or Dircetories... 2、选择所要导入的jar包【可多选】&#xff1b;此处图片略&#xff1b; 3、选中后点击确定&#xff0c;jar会显示在idea的目录中&…

零基础必看html5

文本格式化标签 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> </head&g…

6月30日功能测试Day10

3.4.4拼团购测试点 功能位置&#xff1a;营销-----拼团购 后台优惠促销列表管理可以添加拼团&#xff0c;查看拼团活动&#xff0c;启动活动&#xff0c;编辑活动&#xff0c;删除活动。 可以查看拼团活动中已下单的订单以状态 需求分析 功能和添加拼团 商品拼团活动页 3…

【python】Python中常用的数据结构——列表、元组和字典

python中的数据结构 列表、元组、字典的区别元组&#xff0c;字典&#xff0c;列表三者之间如何实现嵌套生成一个单一元素的元组、列表列表的地址列表、元组和字典的增删改查 列表、元组、字典的区别 列表、元组和字典是Python中常用的数据结构&#xff0c;它们各自有不同的特…

香橙派AIpro测评:yolo8+usb鱼眼摄像头的Camera图像获取及识别

一、前言 近期收到了一块受到业界人士关注的开发板"香橙派AIpro",因为这块板子具有极高的性价比&#xff0c;同时还可以兼容ubuntu、安卓等多种操作系统&#xff0c;今天博主便要在一块832g的香橙派AI香橙派AIpro进行YoloV8s算法的部署并使用一个外接的鱼眼USB摄像头…

14-28 剑和诗人2 - 高性能编程Bend和Mojo

介绍&#xff1a; 在不断发展的计算世界中&#xff0c;软件和硬件之间的界限变得越来越模糊。随着我们不断突破技术可能性的界限&#xff0c;对能够利用现代硬件功能的高效、可扩展的编程语言的需求从未如此迫切。 Bend和 Mojo是编程语言领域的两种新秀&#xff0c;它们有望弥…

RT-Thread Studio与CubeMX联合编程之rtthread启动

看到了好多文章&#xff0c;在rtthread studio中启用mx&#xff0c;后总是复制mx相关msp函数到rt的board.c文件下&#xff0c;实际使用过程中发现并不需要&#xff0c;这里我们看下rt启动流程&#xff0c;看下到底需要不。 1.打开startup_stm32h743xx.S文件&#xff0c;看下芯片…

法国工程师IMT联盟 密码学及其应用 2023年期末考试补考题

1 JAVA 安全 1.1 问题1 1.1.1 问题 用 2 或 3 句话解释 Java 执行模型&#xff08;Java 虚拟机machine virtuelle Java)&#xff09;中引入introduit沙箱bac sable机制 mcanisme d’excution par isolation的目的。 1.1.2 问题解释 在 Java 执行模型&#xff08;Java 虚拟机…

【车载开发系列】J-Link/JFlash 简介与驱动安装方法

【车载开发系列】J-Link/JFlash 简介与驱动安装方法 【车载开发系列】J-Link/JFlash 简介与驱动安装方法 【车载开发系列】J-Link/JFlash 简介与驱动安装方法一. 软件介绍二. 下载安装包二. 开始安装三. 确认安装四. J-Flash的使用 一. 软件介绍 J-Link是SEGGER公司为支持仿真…

昇思25天学习打卡营第07天 | 函数式自动微分

昇思25天学习打卡营第07天 | 函数式自动微分 文章目录 昇思25天学习打卡营第07天 | 函数式自动微分函数与计算图微分函数与梯度Stop GradientAuxiliary data 神经网络梯度计算总结打卡 神经网络的训练主要使用反向传播算法&#xff0c;首先计算模型预测值&#xff08;logits&am…

【IC】mismatch model

由于工艺和制造偏差的存在,相同设计参数的器件会存在参数间额差异,称为mismatch,通常用Monte Carlo去仿真多个mismatch叠加对设计的总影响。 器件偏差mismatch是工艺和制造偏差导致的,在Lot to Lot、Wafer to Wafer、Die to Die 以及in die的Device to Deview之间可见。 …