当前位置: 首页 > news >正文

RISC-V 汇编语言

安装RISCV工具链

riscv-gnu-toolchain工具链和模拟器安装记录 - 知乎 (zhihu.com)

riscv-gnu-toolchain工具链分elf-gcc、linux-gnu-gcc两个版本,以及对应的32位和64位版本。两个版本的主要区别是:

  • riscv32-unknown-elf-gcc、riscv64-unknown-elf-gcc使用的是riscv-newlib库(面向嵌入式的C库),只支持静态链接,不支持动态链接。
  • riscv32-unknown-linux-gnu-gcc、riscv64-unknown-linux-gnu-gcc使用的是glibc标准库,支持动态链接。

最终选用的是riscv64-unknown-linux-gnu-gcc,支持动态链接

函数调用约定

x86

  • 调用约定
  • 常见的调用约定包括 cdeclstdcallfastcall
  • 不同的调用约定通常是为了满足特定编程语言或平台的需求。
  • cdecl(C Declaration):调用者清理堆栈,支持可变参数,C语言标准推荐使用
  • stdcall:被调用者清理堆栈,不支持可变参数,通常用于 Windows API
  • fastcall:使用寄存器传递前两个参数(ECX 和 EDX),其余参数通过堆栈传递,适用于函数频繁调用的场景。

平台与工具链
不同的编译器和平台可能会选择不同的调用约定。

例如,Microsoft 的 Visual C++ 编译器通常支持 stdcall 和 fastcall,

而 GCC 支持 cdecl 和其他约定。

  • 参数传递:参数从右到左入栈。
  • 返回值:返回值通常通过 EAX 寄存器返回。
  • 通用寄存器:8 个(EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP)
  • 指令指针寄存器:1 个(EIP)
  • 段寄存器:6 个(CS, DS, SS, ES, FS, GS)
  • 总数:大约 15 个(包含所有类型)

x64

  • 调用约定
  • 常用的调用约定为 Microsoft 和 System V(Linux 和 macOS)。
  • Microsoft x64(Window操作系统,常与WindowsAPI 交互)
    • 前四个整型或指针参数通过寄存器(RCX、RDX、R8、R9)传递,其余通过堆栈。
    • 返回值通过 RAX 寄存器返回。
  • System V x64( Linux /Unix操作系统 , 常与 POSIX 标准库和开源项目兼容。 )
    • 前六个整型或指针参数通过寄存器(RDI、RSI、RDX、RCX、R8、R9)传递,其余通过堆栈。
    • 返回值通过 RAX 寄存器返回。
  • 参数传递:同样是从右到左入栈。
  • 通用寄存器:16 个(RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, R8, R9, R10, R11, R12, R13, R14, R15)
  • 指令指针寄存器:1 个(RIP)
  • 总数:17 个(包含所有类型)

RV32(RV64类似,但支持更大的寄存器范围)

  • 调用约定
  • 使用 RISC-V 的 ABI,函数参数通过寄存器和堆栈传递。
  • 参数传递
  • 前八个参数通过寄存器(a0 到 a7)传递。
  • 如果参数超过八个,其余参数通过堆栈传递。
  • 返回值
  • 返回值通过 a0 寄存器(即 x10)返回。

调用约定
函数调用过程通常分为 6 个阶段 [Patterson and Hennessy 2021]:

  1. 将参数存放到函数可访问的位置;
  2. 跳转到函数入口(使用 RV32I 的 jal 指令);
  3. 获取函数所需局部存储资源,按需保存寄存器;
  4. 执行函数功能;
  5. 将返回值存放到调用者可访问的位置,恢复寄存器,释放局部存储资源;
  6. 由于程序可从多处调用函数,故需将控制权返回到调用点(使用 ret 指令)

为提升性能,应尽量将变量存放在寄存器而不是内存中,但同时也要避免因保存

和恢复寄存器(函数调用和上下文切换)而频繁访问内存。

RISCV架构优势在于

1.足量的寄存器

2.可以减少保存和恢复寄存器的次数

寄存器的分类

1.临时寄存器:不保证其值在函数调用前后的一致性

2.保存寄存器:保证其值在函数调用前后的一致性

叶子函数

不再调用其他函数的函数称为叶子函数。

当一个叶子函数只有少量参数和局部变量时,可将其分配到寄存器, 无需分配到内存。大部分函数调用均如此,此时程序无需将寄存器保存到内存

RISCV寄存器

比较特殊的几个寄存器

  1. 裸机环境
    在裸机(bare-metal)编程中,如果程序没有操作系统,tp 通常不会被使用。
    如果程序需要自己实现多线程,则可以定义一个线程控制块,并通过软件设置 tp 指向相应的结构。

在函数入口处和出口处标准的RV32I代码


entry_label:
addi sp,sp,-framesize # 调整栈指针(sp 寄存器)来分配栈帧
sw ra,framesize-4(sp) # 保存返回地址(ra 寄存器)
# 按需保存其他寄存器
... # 函数体

# 按需恢复其他寄存器
lw ra,framesize-4(sp) # 恢复返回地址寄存器
addi sp,sp, framesize # 释放栈帧空间
ret # 返回调用点

汇编器

汇编器的作用

1.面向处理器:生成的目标代码(处理器可以理解的指令)

2.面向程序员和编译器开发者:伪指令(常规指令的巧妙特例)

RISCV中的伪指令分为两类

1.依赖恒为0的x0寄存器(硬连线为0)——————>极大简化了RISCV指令集

电气连接:在电路设计中,硬连线意味着某个信号线直接连接到地(0V),从而使得该信号始终保持在低电平状态(即逻辑 0)。

2.其他

伪指令

1.依赖恒为0的x0寄存器的伪指令(32条)

set not equal to zero

set less than zero

set greater than zero

将rs与x0寄存器的值(0)进行条件比较,满足比较条件将rd置为1,否则为0

看大于0时置位,实则是用slt指令进行比较的,无sgt指令

branch equal to zero

以第一条为例,比较rs寄存器的值是否为0,为0则跳转到当前指令相对偏移offset地址处

jump and link 无条件跳转

1.jal x0, offset 会直接跳转到目标地址

适用于不需要保存返回地址的跳转,offset:是一个相对地址偏移量,指向要跳转到的目标指令地址

(正常情况下在进行函数调用时,需要将调用函数处的下一条指令的地址保存在寄存器中,此处寄存器为x0,硬连线为0, 这种跳转适用于不需要返回的场景,如错误处理或实现循环)

2.jalr x0, rs, 0 会直接跳转到 rs 中存储的地址,

3. 同上,唯一区别是返回地址寄存器x1

在 RISC-V 架构中,x1 寄存器通常被称为 ra(return address)寄存器,专门用于存储返回地址。 此处代表从子过程返回

什么是尾调用?

以C源代码为例,将return func();成为尾调用

用jmp 跳转指令替换掉call+ret组合的指令成为尾调用优化

尾调用优化,用jmp 跳转指令替换掉call+ret组合的指令成为尾调用优化,减少栈帧的维护

尾调用优化可以减少函数中不必要的 调用流程

时间上:更少更快的指令

空间上:更少的栈帧,不再容易产生overflow的问题,减少内存中需要维护的栈帧数量

编译器/解释器支持尾调用优化

clang 、gcc对尾调用优化都有很好的支持

javaScript在Webkit引擎上可以进行适当的尾调用优化

不支持尾调用优化

两条指令如何理解呢?在 RISC-V 中,auipcjalr 指令可以用来实现远距离的尾调用(tail call)操作。让我们逐条分析这两条指令的功能以及它们在尾调用中的角色。

AUIPCJALR 里的 12 位立即数的组合,能够转移控制到任意的 32 位 PC 相对地址

AUIPC 先把 32位立即数的高 20 位,补上低 12 位全 0跟 PC 相加,把结果存到某个寄存器里

JALR 再把这个寄存器和 32 位立即数的低 12 位相加,跳转到相加得到的目标地址。

也就是实现了 32 位立即数加上 PC的目标地址

三个相关的CSR寄存器

1.instret:这是一个 CSR,表示已提交指令的数量。它是一个 64 位的寄存器(在 RV64 中),可以分为高 32 位和低 32 位,分别是 instret[63:32]instret[31:0],通常通过 instret[h] 访问高 32 位,通过 instret 访问低 32 位。

2.cycle:这是一个 CSR,表示 CPU 时钟周期数。它也是一个 64 位寄存器,可以分为高 32 位和低 32 位,分别是 cycle[63:32]cycle[31:0]。通过 cycle[h] 访问高 32 位。

3.time:这是一个 CSR,表示当前实时时间。与前两者一样,它是一个 64 位寄存器,可以分为高 32 位和低 32 位,分别是 time[63:32]time[31:0]。通过 time[h] 访问高 32 位。

这三条指令的主要作用是读取 RISC-V 处理器的性能计数器,便于进行性能分析和监控。通过这些寄存器,开发者可以获取有关 CPU 执行的指令数量、时钟周期和当前时间的信息。这对于优化代码和评估系统性能非常重要

CSR置位详细解释:

  • 操作csr = csr | rs
  • 目的
    • 通过将 rs 中为 1 的位与 csr 中对应的位进行按位操作,来设置 csr 中的某些位。
    • csrrs 指令用于启用某些功能或状态,例如启用特定的中断或设置状态位

CSR清位详细解释:

  • 操作csr = csr & ~rs
  • 目标寄存器x0 是目标寄存器,但由于它是硬连线为 0,所以指令执行后不会保存 csr 的原始值。
  • 功能:该指令会从 csr 中清除与 rs 中为 1 的位对应的位。这意味着 csr 中被清零的位是根据 rs 中的位设置决定的

浮点的舍入模式

FRM 寄存器值与舍入模式对应关系

FRM 值

舍入模式

说明

0

Round to Nearest (RN)

最近舍入:向最接近的可表示值舍入。如果恰好在两个值之间,则向偶数舍入(即取最接近的偶数)。

1

Round Toward Zero (RZ)

向零舍入:直接截断小数部分,向零方向舍入。

2

Round Toward Positive Infinity (RP)

向上舍入:总是向正无穷舍入,适用于希望结果偏大时的场景。

3

Round Toward Negative Infinity (RN)

向下舍入:总是向负无穷舍入,适用于希望结果偏小时的场景。

浮点异常标志

浮点异常标志(FFLAGS)

标志位

名称

说明

0

Invalid Operation (IV)

无效操作:指示执行了无效的浮点操作,例如除以零或无效的浮点数。

1

Division by Zero (DZ)

除以零:指示发生了除以零的操作。

2

Overflow (OV)

溢出:指示浮点运算结果超出了可以表示的范围。

3

Underflow (UF)

下溢:指示浮点运算结果过小,低于可表示的最小正数(非零)。

4

Inexact (NX)

不精确:指示运算结果不是精确的结果(例如舍入发生)。

RV32I 中,那些读取 64 位计数器的指令默认读取低 32 位,可通过 “h” 结尾的指令

读取高 32 位。

在 RV32I 架构中,64 位计数器通常指的是用来记录处理器执行周期、已执行指令数量或时间的特定寄存器。例如:

  1. Cycle Counter:用于跟踪 CPU 时钟周期的数量,帮助进行性能分析。
  2. Instruction Retired Counter:记录已经执行的指令数量。

这些计数器通常被实现为 64 位,以便能够记录长时间运行程序所产生的计数值。但在 RV32I 的 32 位环境中,默认读取操作只涉及到这类寄存器的低 32 位部分。要读取高 32 位部分,则需要使用带有 “h” 后缀的特殊指令。这种设计允许处理器在性能监控方面提供更大的灵活性和更精确的计数。

2.其他伪指令(28条)

fence iorw, iorw 的作用是确保在执行此指令之前的所有 I/O 操作和内存读写操作都已经完成,且在此指令之后的 I/O 和内存操作在屏障之前的操作完成后才会执行。这种屏障对于多处理器或多线程环境尤为重要,可以防止因操作顺序不当而导致的竞态条件或数据不一致性。

程序实例

1.源代码
#include <stdio.h>
int main()
{printf("Hello, %s\n", "world");return 0;
}
2.编译生成汇编程序Hello.s
riscv64-unknown-linux-gnu-gcc -S Hello.c -o Hello.s
  • -S 选项告诉编译器将 C 程序编译为汇编代码,输出文件为 Hello.s
  • -o Hello.s 指定输出文件名。
	.file	"Hello.c".option nopic.attribute arch, "rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0".attribute unaligned_access, 0.attribute stack_align, 16.text.section	.rodata.align	3
.LC0:.string	"world".align	3
.LC1:.string	"Hello, %s\n".text.align	1.globl	main.type	main, @function
main:
.LFB0:.cfi_startprocaddi	sp,sp,-16.cfi_def_cfa_offset 16sd	ra,8(sp)sd	s0,0(sp).cfi_offset 1, -8.cfi_offset 8, -16addi	s0,sp,16.cfi_def_cfa 8, 0lui	a5,%hi(.LC0)addi	a1,a5,%lo(.LC0)lui	a5,%hi(.LC1)addi	a0,a5,%lo(.LC1)call	printfli	a5,0mv	a0,a5ld	ra,8(sp).cfi_restore 1ld	s0,0(sp).cfi_restore 8.cfi_def_cfa 2, 16addi	sp,sp,16.cfi_def_cfa_offset 0jr	ra.cfi_endproc
.LFE0:.size	main, .-main.ident	"GCC: () 13.2.0".section	.note.GNU-stack,"",@progbits

汇编指示符:
形式:以英文句号开头

作用:通知汇编器在何处放置代码和数据,指定程序中使用的代码和数据常量

在以上程序中可能出现的汇编指示符,如下所示:

常见的汇编指示符

3.汇编生成目标程序Hello.o
riscv64-unknown-linux-gnu-gcc -c Hello.s -o Hello.o

-c 选项告诉编译器只进行编译而不进行链接,输出一个目标文件 Hello.o

riscv64-unknown-linux-gnu-objdump -d Hello.o

汇编器生成如下所示的 ELF(Executable and Linkable Format,可执行可链接格式) 标准格式目标文件

Hello.o:     file format elf64-littleriscvDisassembly of section .text:0000000000000000 <main>:0:	1141                	addi	sp,sp,-162:	e406                	sd	ra,8(sp)4:	e022                	sd	s0,0(sp)6:	0800                	addi	s0,sp,168:	000007b7          	lui	a5,0x0c:	00078593          	mv	a1,a510:	000007b7          	lui	a5,0x014:	00078513          	mv	a0,a518:	00000097          	auipc	ra,0x01c:	000080e7          	jalr	ra # 18 <main+0x18>#如上六条指令的地址字段为0,后续由链接器填充20:	4781                	li	a5,022:	853e                	mv	a0,a524:	60a2                	ld	ra,8(sp)26:	6402                	ld	s0,0(sp)28:	0141                	addi	sp,sp,162a:	8082                	ret

除指令外,每个目标文件还包含一张符号表,用于记录程序中所有需要在链接过程中确定地址的符号

其中包含数据符号和代码符号。

riscv64-unknown-linux-gnu-objdump -t Hello.o
Hello.o:     file format elf64-littleriscvSYMBOL TABLE:
0000000000000000 l    df *ABS*	0000000000000000 Hello.c
0000000000000000 l    d  .text	0000000000000000 .text
0000000000000000 l    d  .data	0000000000000000 .data
0000000000000000 l    d  .bss	0000000000000000 .bss
0000000000000000 l    d  .rodata	0000000000000000 .rodata
0000000000000000 l    d  .note.GNU-stack	0000000000000000 .note.GNU-stack
0000000000000000 l    d  .comment	0000000000000000 .comment
0000000000000000 l    d  .eh_frame	0000000000000000 .eh_frame
0000000000000000 l    d  .riscv.attributes	0000000000000000 .riscv.attributes
#代码符号
0000000000000000 g     F .text	000000000000002c main 
0000000000000000         *UND*	0000000000000000 printf

数据符号(如字符串常量):常量字符串(如 "Hello, %s\n""world")被存储在 .rodata 段中,通常没有单独的符号名,符号表只会标记这些段存在,而不会为每个常量字符串分配单独的符号

oslab@oslab-virtual-machine:/opt/riscv/bin$ riscv64-unknown-linux-gnu-objdump -s -j .rodata Hello.oHello.o:     file format elf64-littleriscvContents of section .rodata:0000 776f726c 64000000 48656c6c 6f2c2025  world...Hello, %0010 730a00  

生成PIC位置无关的目标文件

使用-fPIC编译选项

riscv64-unknown-linux-gnu-gcc -fPIC -c Hello.c -o Hello_pic.o

riscv64-unknown-linux-gnu-objdump -d Hello_pic.o

oslab@oslab-virtual-machine:/opt/riscv/bin$ riscv64-unknown-linux-gnu-objdump -d Hello_pic.oHello_pic.o:     file format elf64-littleriscvDisassembly of section .text:0000000000000000 <main>:0:	1141                	addi	sp,sp,-162:	e406                	sd	ra,8(sp)4:	e022                	sd	s0,0(sp)6:	0800                	addi	s0,sp,168:	00000597          	auipc	a1,0x0c:	00058593          	mv	a1,a110:	00000517          	auipc	a0,0x014:	00050513          	mv	a0,a018:	00000097          	auipc	ra,0x01c:	000080e7          	jalr	ra # 18 <main+0x18>20:	4781                	li	a5,022:	853e                	mv	a0,a524:	60a2                	ld	ra,8(sp)26:	6402                	ld	s0,0(sp)28:	0141                	addi	sp,sp,162a:	8082                	ret

riscv64-unknown-linux-gnu-gcc -pie -o Hello_pic Hello_pic.o

4.链接生成可执行文件Hello.out
riscv64-unknown-linux-gnu-gcc Hello.o -o Hello.out
riscv64-unknown-linux-gnu-objdump -d Hello.out
Hello.out:     file format elf64-littleriscvDisassembly of section .plt:0000000000010430 <_PROCEDURE_LINKAGE_TABLE_>:10430:	97 23 00 00 33 03 c3 41 03 be 03 bc 13 03 43 fd     .#..3..A......C.10440:	93 82 03 bc 13 53 13 00 83 b2 82 00 67 00 0e 00     .....S......g...0000000000010450 <__libc_start_main@plt>:10450:	00002e17          	auipc	t3,0x210454:	bb0e3e03          	ld	t3,-1104(t3) # 12000 <__libc_start_main@GLIBC_2.34>10458:	000e0367          	jalr	t1,t31045c:	00000013          	nop0000000000010460 <printf@plt>:10460:	00002e17          	auipc	t3,0x210464:	ba8e3e03          	ld	t3,-1112(t3) # 12008 <printf@GLIBC_2.27>10468:	000e0367          	jalr	t1,t31046c:	00000013          	nopDisassembly of section .text:0000000000010470 <_start>:10470:	024000ef          	jal	10494 <load_gp>10474:	87aa                	mv	a5,a010476:	00000517          	auipc	a0,0x01047a:	01c50513          	addi	a0,a0,28 # 10492 <__wrap_main>1047e:	6582                	ld	a1,0(sp)10480:	0030                	addi	a2,sp,810482:	ff017113          	andi	sp,sp,-1610486:	4681                	li	a3,010488:	4701                	li	a4,01048a:	880a                	mv	a6,sp1048c:	fc5ff0ef          	jal	10450 <__libc_start_main@plt>10490:	9002                	ebreak0000000000010492 <__wrap_main>:10492:	a89d                	j	10508 <main>0000000000010494 <load_gp>:10494:	00002197          	auipc	gp,0x210498:	37c18193          	addi	gp,gp,892 # 12810 <__global_pointer$>1049c:	8082                	ret...00000000000104a0 <deregister_tm_clones>:104a0:	6549                	lui	a0,0x12104a2:	6749                	lui	a4,0x12104a4:	01050793          	addi	a5,a0,16 # 12010 <__TMC_END__>104a8:	01070713          	addi	a4,a4,16 # 12010 <__TMC_END__>104ac:	00f70863          	beq	a4,a5,104bc <deregister_tm_clones+0x1c>104b0:	00000793          	li	a5,0104b4:	c781                	beqz	a5,104bc <deregister_tm_clones+0x1c>104b6:	01050513          	addi	a0,a0,16104ba:	8782                	jr	a5104bc:	8082                	ret00000000000104be <register_tm_clones>:104be:	6549                	lui	a0,0x12104c0:	01050793          	addi	a5,a0,16 # 12010 <__TMC_END__>104c4:	6749                	lui	a4,0x12104c6:	01070593          	addi	a1,a4,16 # 12010 <__TMC_END__>104ca:	8d9d                	sub	a1,a1,a5104cc:	4035d793          	srai	a5,a1,0x3104d0:	91fd                	srli	a1,a1,0x3f104d2:	95be                	add	a1,a1,a5104d4:	8585                	srai	a1,a1,0x1104d6:	c599                	beqz	a1,104e4 <register_tm_clones+0x26>104d8:	00000793          	li	a5,0104dc:	c781                	beqz	a5,104e4 <register_tm_clones+0x26>104de:	01050513          	addi	a0,a0,16104e2:	8782                	jr	a5104e4:	8082                	ret00000000000104e6 <__do_global_dtors_aux>:104e6:	1141                	addi	sp,sp,-16104e8:	e022                	sd	s0,0(sp)104ea:	6449                	lui	s0,0x12104ec:	01844783          	lbu	a5,24(s0) # 12018 <completed.0>104f0:	e406                	sd	ra,8(sp)104f2:	e791                	bnez	a5,104fe <__do_global_dtors_aux+0x18>104f4:	fadff0ef          	jal	104a0 <deregister_tm_clones>104f8:	4785                	li	a5,1104fa:	00f40c23          	sb	a5,24(s0)104fe:	60a2                	ld	ra,8(sp)10500:	6402                	ld	s0,0(sp)10502:	0141                	addi	sp,sp,1610504:	8082                	ret0000000000010506 <frame_dummy>:10506:	bf65                	j	104be <register_tm_clones>0000000000010508 <main>:10508:	1141                	addi	sp,sp,-161050a:	e406                	sd	ra,8(sp)1050c:	e022                	sd	s0,0(sp)1050e:	0800                	addi	s0,sp,16在第一段代码中,a1 和 a0 寄存器的值是通过 lui 和 addi 指令计算的,
偏移量是常量(1336 和 1344),计算的是某个绝对地址附近的值。10510:	67c1                	lui	a5,0x1010512:	53878593          	addi	a1,a5,1336 # 10538 <_IO_stdin_used+0x8>10516:	67c1                	lui	a5,0x1010518:	54078513          	addi	a0,a5,1344 # 10540 <_IO_stdin_used+0x10>1051c:	f45ff0ef          	jal	10460 <printf@plt>在第二段位置无关(PIC)代码中,a1 和 a0 寄存器的值是通过 auipc 和 addi 来构造的,
使用的是相对地址(相对于当前代码位置的偏移)
这意味着在生成的位置无关代码中,程序的地址依赖于当前指令的位置,
而不是固定的常量地址。690:	00000597          	auipc	a1,0x0694:	02858593          	addi	a1,a1,40 # 6b8 <_IO_stdin_used+0x8>698:	00000517          	auipc	a0,0x069c:	02850513          	addi	a0,a0,40 # 6c0 <_IO_stdin_used+0x10>6a0:	f21ff0ef          	jal	5c0 <printf@plt>10520:	4781                	li	a5,010522:	853e                	mv	a0,a510524:	60a2                	ld	ra,8(sp)10526:	6402                	ld	s0,0(sp)10528:	0141                	addi	sp,sp,161052a:	8082                	ret

链接器

动态链接

1.plt表和got表

2.延迟绑定

在RISCV中Linux动态链接和延迟绑定

#include <stdio.h>
void print_banner()
{printf("Welcome to World of PLT and GOT\n");
}
int main(void)
{print_banner();return 0;
}

依次用以下的编译命令,现在有原有的 test.c 还有个 test.o 以及可执行文件 test

riscv64-unknown-linux-gnu-gcc -Wall -g -o test.o -c test.c
riscv64-unknown-linux-gnu-gcc -o test test.o

执行RISCV程序test

oslab@oslab-virtual-machine:/opt/riscv/bin/Linker/dynamic$ qemu-riscv64 test
Welcome to World of PLT and GOT

查看反汇编

riscv64-unknown-linux-gnu-objdump -d test.o
test.o:     file format elf64-littleriscvDisassembly of section .text:0000000000000000 <print_banner>:0:	1141                	addi	sp,sp,-162:	e406                	sd	ra,8(sp)4:	e022                	sd	s0,0(sp)6:	0800                	addi	s0,sp,168:	000007b7          	lui	a5,0x0c:	00078513          	mv	a0,a510:	00000097          	auipc	ra,0x014:	000080e7          	jalr	ra # 10 <print_banner+0x10>18:	0001                	nop1a:	60a2                	ld	ra,8(sp)1c:	6402                	ld	s0,0(sp)1e:	0141                	addi	sp,sp,1620:	8082                	ret0000000000000022 <main>:22:	1141                	addi	sp,sp,-1624:	e406                	sd	ra,8(sp)26:	e022                	sd	s0,0(sp)28:	0800                	addi	s0,sp,162a:	00000097          	auipc	ra,0x02e:	000080e7          	jalr	ra # 2a <main+0x8>32:	4781                	li	a5,034:	853e                	mv	a0,a536:	60a2                	ld	ra,8(sp)38:	6402                	ld	s0,0(sp)3a:	0141                	addi	sp,sp,163c:	8082                	ret

riscv64-unknown-linux-gnu-objdump -d test > test.asm

Disassembly of section .plt:0000000000010430 <_PROCEDURE_LINKAGE_TABLE_>:10430:	97 23 00 00 33 03 c3 41 03 be 03 bc 13 03 43 fd     .#..3..A......C.10440:	93 82 03 bc 13 53 13 00 83 b2 82 00 67 00 0e 00     .....S......g...0000000000010450 <__libc_start_main@plt>:10450:	00002e17          	auipc	t3,0x210454:	bb0e3e03          	ld	t3,-1104(t3) # 12000 <__libc_start_main@GLIBC_2.34>10458:	000e0367          	jalr	t1,t31045c:	00000013          	nop0000000000010460 <puts@plt>:10460:	00002e17          	auipc	t3,0x210464:	ba8e3e03          	ld	t3,-1112(t3) # 12008 <puts@GLIBC_2.27>10468:	000e0367          	jalr	t1,t31046c:	00000013          	nop

可以看到,除了第一个plt表项外,每个plt 表都是跳转到对应的 got 表项

对test使用qemu-riscv64和riscv64-unknown-linux-gnu-gdb进行调试

qemu-riscv64 -singlestep -g 1234 test
oslab@oslab-virtual-machine:/opt/riscv/bin/Linker/dynamic$ 
riscv64-unknown-linux-gnu-gdb ./test
GNU gdb (GDB) 14.2
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "--host=x86_64-pc-linux-gnu --target=riscv64-unknown-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:<http://www.gnu.org/software/gdb/documentation/>.For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./test...
(gdb) target remote localhost:1234
Remote debugging using localhost:1234
Reading symbols from /opt/riscv/sysroot/lib/ld-linux-riscv64-lp64d.so.1...
(No debugging symbols found in /opt/riscv/sysroot/lib/ld-linux-riscv64-lp64d.so.1)
0x00000040008112e2 in _start () from /opt/riscv/sysroot/lib/ld-linux-riscv64-lp64d.so.1
(gdb) b main
Breakpoint 1 at 0x1052c: file test.c, line 8.
(gdb) c
Continuing.

之后再test.asm中

得到got表项的地址,在gdb中查看

(gdb) x/x 0x12008
0x12008 <puts@got.plt>:	0x00010430

可以看到此处的puts的got表项的地址就是plt表的0x10430

接着调试执行

(gdb) disas
Dump of assembler code for function puts@plt:0x0000000000010460 <+0>:	auipc	t3,0x20x0000000000010464 <+4>:	ld	t3,-1112(t3) # 0x12008 <puts@got.plt>
=> 0x0000000000010468 <+8>:	jalr	t1,t30x000000000001046c <+12>:	nop
End of assembler dump.
(gdb) info r t3
t3             0x10430	66608
(gdb) si
0x0000000000010430 in _PROCEDURE_LINKAGE_TABLE_ ()
(gdb) info r t3
t3             0x10430	66608
(gdb) disas
Dump of assembler code for function _PROCEDURE_LINKAGE_TABLE_:
=> 0x0000000000010430 <+0>:	auipc	t2,0x20x0000000000010434 <+4>:	sub	t1,t1,t30x0000000000010438 <+8>:	ld	t3,-1088(t2) # 0x11ff00x000000000001043c <+12>:	addi	t1,t1,-440x0000000000010440 <+16>:	addi	t0,t2,-10880x0000000000010444 <+20>:	srli	t1,t1,0x10x0000000000010448 <+24>:	ld	t0,8(t0)0x000000000001044c <+28>:	jr	t3
End of assembler dump. 

最终要跳转到t3处的值,也就是_dl_runtime_resolve函数

0x000000400080d7b4 in _dl_runtime_resolve ()from /opt/riscv/sysroot/lib/ld-linux-riscv64-lp64d.so.1
(gdb) disas
Dump of assembler code for function _dl_runtime_resolve:
=> 0x000000400080d7b4 <+0>:	addi	sp,sp,-1440x000000400080d7b6 <+2>:	sd	ra,72(sp)0x000000400080d7b8 <+4>:	sd	a0,8(sp)0x000000400080d7ba <+6>:	sd	a1,16(sp)0x000000400080d7bc <+8>:	sd	a2,24(sp)0x000000400080d7be <+10>:	sd	a3,32(sp)0x000000400080d7c0 <+12>:	sd	a4,40(sp)0x000000400080d7c2 <+14>:	sd	a5,48(sp)0x000000400080d7c4 <+16>:	sd	a6,56(sp)0x000000400080d7c6 <+18>:	sd	a7,64(sp)0x000000400080d7c8 <+20>:	fsd	fa0,80(sp)0x000000400080d7ca <+22>:	fsd	fa1,88(sp)0x000000400080d7cc <+24>:	fsd	fa2,96(sp)0x000000400080d7ce <+26>:	fsd	fa3,104(sp)0x000000400080d7d0 <+28>:	fsd	fa4,112(sp)0x000000400080d7d2 <+30>:	fsd	fa5,120(sp)0x000000400080d7d4 <+32>:	fsd	fa6,128(sp)0x000000400080d7d6 <+34>:	fsd	fa7,136(sp)0x000000400080d7d8 <+36>:	slli	a1,t1,0x10x000000400080d7dc <+40>:	mv	a0,t00x000000400080d7de <+42>:	add	a1,a1,t10x000000400080d7e0 <+44>:	auipc	a2,0x160x000000400080d7e4 <+48>:	ld	a2,-2040(a2) # 0x4000822fe80x000000400080d7e8 <+52>:	jalr	a20x000000400080d7ea <+54>:	mv	t1,a00x000000400080d7ec <+56>:	ld	ra,72(sp)0x000000400080d7ee <+58>:	ld	a0,8(sp)0x000000400080d7f0 <+60>:	ld	a1,16(sp)0x000000400080d7f2 <+62>:	ld	a2,24(sp)0x000000400080d7f4 <+64>:	ld	a3,32(sp)0x000000400080d7f6 <+66>:	ld	a4,40(sp)0x000000400080d7f8 <+68>:	ld	a5,48(sp)
--Type <RET> for more, q to quit, c to continue without paging--0x000000400080d7fa <+70>:	ld	a6,56(sp)0x000000400080d7fc <+72>:	ld	a7,64(sp)0x000000400080d7fe <+74>:	fld	fa0,80(sp)0x000000400080d800 <+76>:	fld	fa1,88(sp)0x000000400080d802 <+78>:	fld	fa2,96(sp)0x000000400080d804 <+80>:	fld	fa3,104(sp)0x000000400080d806 <+82>:	fld	fa4,112(sp)0x000000400080d808 <+84>:	fld	fa5,120(sp)0x000000400080d80a <+86>:	fld	fa6,128(sp)0x000000400080d80c <+88>:	fld	fa7,136(sp)0x000000400080d80e <+90>:	addi	sp,sp,1440x000000400080d810 <+92>:	jr	t1
End of assembler dump.

基本调用流程

1.XX@plt

2.xx@got

3.plt表段起始地址

4._dl_runtime_resolve ()

到这里我们还需要知道

  1. _dl_runtime_resolve 是怎么知道要查找 printf 函数的
  2. _dl_runtime_resolve 找到 printf 函数地址之后,它怎么知道回填到哪个 GOT 表项

查看重定位信息

oslab@oslab-virtual-machine:/opt/riscv/bin/Linker/dynamic$ riscv64-unknown-linux-gnu-readelf -r testRelocation section '.rela.plt' at offset 0x3f8 contains 2 entries:Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000012000  000300000005 R_RISCV_JUMP_SLOT 0000000000010450 __libc_start_main@GLIBC_2.34 + 0
000000012008  000100000005 R_RISCV_JUMP_SLOT 0000000000010460 puts@GLIBC_2.27 + 0
3.快速链接

修改源码,二次调用printf函数

#include <stdio.h>
void print_banner()
{printf("Welcome to World of PLT and GOT\n");
}
int main(void)
{print_banner();print_banner();return 0;
}

其他步骤如上

比较两次的got表项的值

//一开始没有重定位的时候将 printf@got 填成 lookup_printf 的地址
void printf@plt()
{
address_good:jmp *printf@got   
lookup_printf:调用重定位函数查找 printf 地址,并写到 printf@gotgoto address_good;//再返回去执行address_good
}
gdb) disas
Dump of assembler code for function puts@plt:0x0000000000010460 <+0>:	auipc	t3,0x20x0000000000010464 <+4>:	ld	t3,-1112(t3) # 0x12008 <puts@got.plt>
=> 0x0000000000010468 <+8>:	jalr	t1,t30x000000000001046c <+12>:	nop
End of assembler dump.
(gdb) info r t3
t3             0x10430	66608
(gdb) c
Continuing.Breakpoint 2, 0x0000000000010516 in print_banner () at test.c:4
4	    printf("Welcome to World of PLT and GOT\n");
(gdb) c
Continuing.Breakpoint 3, 0x0000000000010468 in puts@plt ()
(gdb) disas
Dump of assembler code for function puts@plt:0x0000000000010460 <+0>:	auipc	t3,0x20x0000000000010464 <+4>:	ld	t3,-1112(t3) # 0x12008 <puts@got.plt>
=> 0x0000000000010468 <+8>:	jalr	t1,t30x000000000001046c <+12>:	nop
End of assembler dump.
(gdb) info r t3
t3             0x40008961a8	274886910376
(gdb) x 0x40008961a8
0x40008961a8 <puts>:	0xe44e7179
(gdb) disas 0x40008961a8
Dump of assembler code for function puts:0x00000040008961a8 <+0>:	addi	sp,sp,-480x00000040008961aa <+2>:	sd	s3,8(sp)0x00000040008961ac <+4>:	auipc	s3,0xee0x00000040008961b0 <+8>:	ld	s3,-916(s3) # 0x4000983e180x00000040008961b4 <+12>:	sd	s0,32(sp)0x00000040008961b6 <+14>:	sd	s1,24(sp)0x00000040008961b8 <+16>:	sd	s2,16(sp)0x00000040008961ba <+18>:	sd	ra,40(sp)0x00000040008961bc <+20>:	sd	s4,0(sp)0x00000040008961be <+22>:	mv	s2,a00x00000040008961c0 <+24>:	jal	0x40008b47da <strlen>0x00000040008961c4 <+28>:	ld	s1,0(s3)0x00000040008961c8 <+32>:	mv	s0,a00x00000040008961ca <+34>:	lw	a5,0(s1)0x00000040008961cc <+36>:	slli	a4,a5,0x300x00000040008961d0 <+40>:	bgez	a4,0x400089626c <puts+196>0x00000040008961d4 <+44>:	mv	a0,s10x00000040008961d6 <+46>:	lw	a5,192(a0)0x00000040008961da <+50>:	bnez	a5,0x4000896262 <puts+186>0x00000040008961dc <+52>:	li	a5,-10x00000040008961de <+54>:	sw	a5,192(a0)0x00000040008961e2 <+58>:	ld	s4,216(a0)0x00000040008961e6 <+62>:	lui	a5,0x10x00000040008961e8 <+64>:	auipc	a4,0xed0x00000040008961ec <+68>:	addi	a4,a4,-80 # 0x4000983198 <__io_vtables>0x00000040008961f0 <+72>:	sub	a4,s4,a40x00000040008961f4 <+76>:	addi	a5,a5,-1745 # 0x92f0x00000040008961f8 <+80>:	bltu	a5,a4,0x40008962da <puts+306>0x00000040008961fc <+84>:	ld	a5,56(s4)0x0000004000896200 <+88>:	mv	a2,s00x0000004000896202 <+90>:	mv	a1,s20x0000004000896204 <+92>:	jalr	a50x0000004000896206 <+94>:	bne	s0,a0,0x4000896268 <puts+192>

第一次

第二次快速链接

调试程序的步骤

没有配置好riscv64-unknown-linux-gnu-gdb和qemu相结合的调试环境

已完成

qemu-riscv64 -singlestep -g 1234 Hello
riscv64-unknown-elf-gdb Hello#进入gdb调试模式后,需要执行target remote localhost:端口号 来连接qemu模拟器:
(gdb)target remote localhost:1234#之后再gdb就可以正常调试RISCV程序了

链接器通常需要为每个符号调整两条 RV32I 指令。如

数据地址需要调整 lui 和 addi

代码地址需要调整 auipc 和 jalr。

链接器松弛(没太看懂)

Linker Relaxation in LLD - Chih-Mao Chen, Andes Technology - YouTube

Linker Relaxation - 知乎 (zhihu.com)

加载器

由操作系统实现

相关文章:

RISC-V 汇编语言

安装RISCV工具链 riscv-gnu-toolchain工具链和模拟器安装记录 - 知乎 (zhihu.com) riscv-gnu-toolchain工具链分elf-gcc、linux-gnu-gcc两个版本&#xff0c;以及对应的32位和64位版本。两个版本的主要区别是&#xff1a; riscv32-unknown-elf-gcc、riscv64-unknown-elf-gcc…...

MySQL Explain 指南

MySQL Explain 指南 idselect_typetablepartitionstypepossible_keyskeykeylenrefrowsfilteredExtra 使用 explain 执行 DML 语句时&#xff0c;数据不会发生变化。explain 的结果可能包含多行数据&#xff0c;每行对应一个表。若涉及 union 操作&#xff0c;MySQL 会创建临时表…...

keil报错---connection refused due to device mismatch

解决办法如下&#xff1a; 记得改成1 把Enable取消...

ubuntu下的chattts 学习4:Advanced Usage

源码 import ChatTTS import torch import torchaudiochat ChatTTS.Chat() chat.load(compileFalse) # Set to True for better performance ################################### # Sample a speaker from Gaussian.rand_spk chat.sample_random_speaker() print(rand_spk)…...

Ubuntu桌面突然卡住,图形界面无反应

1.可能等待几分钟&#xff0c;系统会自动反应过来。你可以选择等待几分钟。 2.绝大多数情况系统是不会反应过来的&#xff0c;这时候可以进入tty终端直接注销用户。 (1)Ubuntu有6个tty终端&#xff0c;按住CtrlAltF1可以进入tty1终端&#xff0c;&#xff08;同理CtrlAltF2&a…...

毕设记录_论文阅读(动磁式音圈电机的开发与应用)_20241207

前言 提醒&#xff1a; 文章内容为方便作者自己后日复习与查阅而进行的书写与发布&#xff0c;其中引用内容都会使用链接表明出处&#xff08;如有侵权问题&#xff0c;请及时联系&#xff09;。 其中内容多为一次书写&#xff0c;缺少检查与订正&#xff0c;如有问题或其他拓展…...

我有一个Python项目,已经用docker打包镜像也push了,k8s怎么部署呢?

要在Kubernetes (k8s) 部署你的Python项目&#xff0c;你需要创建一系列的Kubernetes资源定义文件&#xff08;通常是以.yaml为扩展名&#xff09;&#xff0c;这些文件描述了你希望在集群中运行的应用程序。以下是部署的基本步骤&#xff1a; 1. **准备Docker镜像**&#xff1…...

GAN(生成对抗网络)原理与目标函数

GAN&#xff08;生成对抗网络&#xff09;原理与目标函数 什么是 GAN&#xff1f; GAN 是一种生成模型&#xff0c;全名是 生成对抗网络 (Generative Adversarial Network)。它由两个部分组成&#xff1a; 生成器 (Generator, G)&#xff1a;负责生成“假数据”。判别器 (Di…...

[Java]项目入门

这篇简单介绍一些入门的有关项目和行业的知识&#xff0c;并带着实现一个小项目。便于已经编程入门的各位准备进阶到下一个阶段。 先大致地介绍&#xff0c;一个完整的项目(不看客户端、服务端的分类)基本可以划分为三部分&#xff1a; 1.前端。比如你现在看到的CSDN页面就是一…...

自定义指令,全局,局部,注册

让输入框自动获取焦点(每次刷新自动获取焦点&#xff09; <template><div><h3>自定义指令</h3><input ref"inp" type"text"></div> </template><script> export default {mounted(){this.$refs.inp.focus…...

存储类内存,非易失性内存)的升级换代,将有利于促进【PCIe交换芯片】市场的发展

摘要 根据 HengCe&#xff08;恒策咨询&#xff09;的统计及预测&#xff0c;2023年全球PCIe交换芯片市场销售额达到了10.05亿美元&#xff0c;预计2030年将达到23.81亿美元&#xff0c;年复合增长率&#xff08;CAGR&#xff09;为12.5%&#xff08;2024-2030&#xff09;。地…...

泷羽sec-burp(7)

免责声明 学习视频来自 B 站up主泷羽sec&#xff0c;如涉及侵权马上删除文章。 笔记的只是方便各位师傅学习知识&#xff0c;以下代码、网站只涉及学习内容&#xff0c;其他的都与本人无关&#xff0c;切莫逾越法律红线&#xff0c;否则后果自负。 泷羽sec官网&#xff1a;http…...

OpenCV图像处理——二值化原理与代码实现(C++/Python)

概述 在 OpenCV 中&#xff0c;二值化&#xff08;Binarization&#xff09;是一种图像处理操作&#xff0c;它的目的是将一幅灰度图像转换为仅包含两种像素值&#xff08;通常为 0 和 255&#xff0c;分别代表黑色和白色&#xff09;的二值图像。通过设定一个合适的阈值&…...

Scala 高阶模式案例解析:从入门到实战

引言 Scala 作为一种功能强大的多范式编程语言&#xff0c;因其函数式编程特性而广受欢迎。其中&#xff0c;高阶模式&#xff08;High-Order Patterns&#xff09;是 Scala 函数式编程的核心概念之一&#xff0c;为开发者提供了解决复杂问题的优雅方式。本篇文章将全面解析 S…...

30天学会Go--第8天 GO语言 Gin Web框架学习与实践

30天学会Go–第8天 GO语言 Gin Web框架学习与实践 文章目录 30天学会Go--第8天 GO语言 Gin Web框架学习与实践前言一、Gin 的简介与安装1.1 Gin 的特点1.2 安装 Gin 二、Gin 的基础用法2.1 路由2.1.1 基本路由2.1.2 路由参数2.1.3 查询参数2.1.4 路由分组 2.2 中间件2.2.1 使用…...

用R(语言)学R-Learning R,In R

一、安装swirl包 在R语言控制面板&#xff0c;对话框输入以下命令&#xff1a; swirl 是一个非常有用的 R 包&#xff0c;它允许你通过交互式教程来学习 R 语言。以下是使用 swirl 包的基本步骤&#xff1a; 安装 swirl 包&#xff1a;首先&#xff0c;你需要在 R 中安装 swi…...

【银河麒麟操作系统运维】某平台多台虚拟机异常重启分析及处理

了解更多银河麒麟操作系统全新产品&#xff0c;请点击访问 麒麟软件产品专区&#xff1a;https://product.kylinos.cn 开发者专区&#xff1a;https://developer.kylinos.cn 文档中心&#xff1a;https://documentkylinos.cn 现象描述 某虚拟化平台多台虚拟机于凌晨触发异常…...

线性代数中的谱分解

一、谱分解的基本原理 谱分解&#xff08;Spectral Decomposition&#xff09;是线性代数中的一个重要概念&#xff0c;特别是在研究矩阵的特征值和特征向量时。它指的是将一个矩阵分解为其特征值和特征向量的组合&#xff0c;从而简化矩阵的运算和分析。谱分解通常适用于对称…...

synchronized(juc)

目录 线程一&#xff1a;interrupt设计模式两阶段终止模式interrupt打断park线程 二:守护线程三:线程状态五个状态(从操作系统角度来说)六种状态(从java API的角度) 共享模型之管程一&#xff1a;上下文切换的安全问题临界区和竟态条件 二&#xff1a;synchronized解决安全问题…...

HTML Input 文件上传功能全解析:从基础到优化

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…...

深入理解Spring事务

目录 什么是Spring事务为什么需要Spring事务Spring事务的实现 Spring事务的传播机制Spring事务的底层原理 EnableTransactionManagement --开启Spring管理事务Import(TransactionManagementConfigurationSelector.class) --提供两个beanAutoProxyRegistrar --启用AOP的功能&am…...

React学习笔记2-初识React

这篇七个点&#xff1a;1 环境搭建&#xff0c; 2 JSX&#xff0c; 3 组件&#xff0c;4 数据流&#xff0c;5 生命周期&#xff0c;6 React与DOM&#xff0c;7 实例 1 环境搭建 1.1 引用React CDN <!DOCTYPE html> <html lang"en"> <head><met…...

【5G】Spectrum 频谱

频谱是移动运营商的关键资产&#xff0c;可用的频谱是定义移动网络容量和覆盖范围的重要因素。本章讨论了5G的不同频谱选项、它们的特性以及5G早期部署阶段的预期频谱。5G是首个旨在利用大约400 MHz到90 GHz之间所有频段的移动无线系统。5G还设计用于在许可、共享和非许可频谱带…...

解决流网络中不存在s~u~t路径的节点的最大流问题

解决流网络中不存在s~u~t路径的节点的最大流问题 问题分析伪代码C代码示例解释在流网络问题中,我们通常会假设对于所有的节点v ∈ V,都存在一条从源点s到汇点t经过v的路径。然而,当这一假设不成立时,即存在某些节点u,使得不存在路径sut,我们需要证明在这种情况下,网络中…...

springboot的 nacos 配置获取不到导致启动失败及日志不输出问题

前言 问题 1. 本地启动应用时&#xff0c;一切正常&#xff0c;但是部署 docker 后&#xff0c;会因为获取不到 nacos 中的配置导致服务启动失败。 2.当 docker 中的服务一直重启&#xff0c;可能会突然某一次启动成功&#xff0c;之后只要不重新构建 docker 镜像&am…...

word poi-tl 图表功能增强,插入图表折线图、柱状图、饼状图

目录 问题解决问题poi-tl介绍 功能实现引入依赖功能介绍 功能实例饼图模版代码效果图 雷达图&#xff08;模版同饼图&#xff09;代码效果图 柱状图&#xff08;模版同饼图&#xff09;代码效果图 附加CustomCharts 工具类CustomChartSingleSeriesRenderData 数据对象CustomCha…...

SpringBoot 项目如何集成 JWT

SpringBoot 项目如何集成 JWT JWT JSON Web Token (JWT) 是一个开放标准(RFC 7519)&#xff0c;它定义了一种紧凑的、自包含的方式&#xff0c;用于作为 JSON 对象在各方之间安全地传输信息。 在 Oauth2 中&#xff0c;其实就是返回访问令牌 &#xff08;access_token&#…...

如何查看电脑刷新率

Windows 系统 通过显示设置查看&#xff1a; 右键点击桌面空白处&#xff0c;选择 “显示设置”。在打开的窗口中&#xff0c;找到 “高级显示设置”。点击 “显示适配器属性”。在弹出的窗口中&#xff0c;选择 “监视器” 选项卡&#xff0c;即可看到当前的屏幕刷新率。使用 …...

MVC基础——市场管理系统(一)

文章目录 项目地址一、创建项目结构1.1 创建程序以及Controller1.2 创建View1.3 创建Models层,并且在Edit页面显示1.4 创建Layou模板页面1.5 创建静态文件css中间件二、Categories的CRUD2.1 使用静态仓库存储数据2.2 将Categorie的列表显示在页面中(List)2.3 创建_ViewImport.…...

面向对象中多态的含义

多态性的定义 多态&#xff08;Polymorphism&#xff09;是面向对象编程中的一个重要概念。它是指同一个函数名或操作符在不同的对象或情境下具有不同的行为。简单来说&#xff0c;就是 “多种形态”。例如&#xff0c;在一个图形处理程序中&#xff0c;有 “计算面积” 这个操…...

L2G3000-LMDeploy 量化部署实践

文章目录 LMDeploy 量化部署实践闯关任务环境配置W4A16 量化 KV cacheKV cache 量化Function call LMDeploy 量化部署实践闯关任务 环境配置 conda create -n lmdeploy python3.10 -y conda activate lmdeploy conda install pytorch2.1.2 torchvision0.16.2 torchaudio2.1.…...

ubuntu下的chattts 学习5:Example: self introduction

代码 import ChatTTS import torch import torchaudiochat ChatTTS.Chat() chat.load(compileFalse) # Set to True for better performance ################################### inputs_en """ chat T T S is a text to speech model designed for dialogu…...

IDEA的service窗口中启动类是灰色且容易消失

大家在学习Spring Cloud的过程中,随着项目的深入,会分出很多个微服务,当我们的服务数量大于等于三个的时候,IDEA会给我们的服务整理起来,类似于这样 但是当我们的微服务数量达到5个以上的时候,再启动服务的时候,服务的启动类就会变成灰色,而且还容易丢失 解决方法 我们按住…...

vue中pdf.js的使用,包括pdf显示,跳转指定页面,高亮关键词

目录 一、下载pdf.js 二、引入到本地的项目中 三、实现预览pdf 四、跳转到指定页面 五、利用pdf里面的find查找关键词 六、修改页面大小为实际大小 一、下载pdf.js https://github.com/mozilla/pdf.js 里面有很多的版本&#xff0c; 高版本的可能浏览器不兼容或者还要考…...

Cherno C++学习笔记 P32 字符串

这篇文章我们来讲字符串。字符串可以说是最重要的变量类型了&#xff0c;因为对字符串的读写极大地影响到我们的程序和用户之间的交互。甚至很多很庞大的程序就只是在处理字符串。 对于字符串&#xff0c;我们同时需要有关于数组和指针的关系&#xff0c;字符串的实现与数组是…...

【C++初阶】第7课—标准模版库STL(string_1)

文章目录 1. 什么是STL2. STL六大组件3. 标准库中string类3.1 auto关键字3.2 范围for3.3 string类的类型3.4 string类的常用接口(string类对象的常见构造)3.5 string的析构和赋值运算符重载3.6 string类对象的容量操作 1. 什么是STL STL(standard template library—标准模板库…...

GESP202306 一级【时间规划】题解(AC)

》》》点我查看「视频」详解》》》 AC_Code #include <bits/stdc.h> using namespace std;int main() {int h1, m1, h2, m2;cin >> h1 >> m1;cin >> h2 >> m2;h1 h1 * 60 m1;h2 h2 * 60 m2;cout << h2 - h1;return 0; }》》》点我查…...

React基础知识四 Hooks

什么是hooks&#xff1f; (coderwhy) hooks是react 16.8&#xff08;2019年&#xff09;出的新特性。 react有两种形式来创建组件——类式和函数式。在hooks之前类式组件就是react最主流的编程方式。 这个时候&#xff0c;函数式组件是非常鸡肋的&#xff0c;几乎没什么用。因…...

Linux服务器通用安全加固指南

1、保护引导过程&#xff08;以Grub引导为例&#xff09; 在 /etc/inittab 中添加 sp:S:respawn:/sbin/sulogin&#xff0c;以确保当切换到单用户模式时 运行级的配置要求输入 root 密码&#xff1a; cp /etc/inittab / etc/initab .bak vim /etc/inittab 退出&#xff1a;es…...

vsphere vcenter web 界面的介绍

这是主页的页面...

分类算法中的样本不平衡问题及其解决方案

一、样本不平衡问题概述 在机器学习的分类任务中&#xff0c;样本不平衡是指不同类别训练样本数量存在显著差异的现象。这一差异会给模型训练和性能评估带来挑战&#xff0c;尤其在处理少数类样本时&#xff0c;模型可能难以有效学习其特征。 以二分类为例&#xff0c;理想情况…...

[LitCTF 2023]破损的图片(初级)

[LitCTF 2023]破损的图片(初级) 我们下载附件得到一个没有后缀的文件&#xff0c;拖去010看一看&#xff0c;发现本来应该是文件头的那部分不大对劲&#xff0c;结合后面四个点以及IHDR&#xff0c;大致也应该知道是啥了 修改第一行为png 89 50 4E 47 0D 0A 1A 0A 00 00 00 …...

最新道客巴巴怎么免费下载文档方法

一、利用浏览器插件 插件自动识别下载&#xff1a;对于经常需要下载道客巴巴文档的人来说&#xff0c;安装浏览器插件是不错的选择。如Chrome浏览器上的一些插件&#xff08;如PDF在线转换器等&#xff09;&#xff0c;安装后&#xff0c;在浏览器中打开道客巴巴文档&#xff…...

Bert的Transformer原理

多义词如何应对&#xff1a; 答&#xff1a;通过Self attention&#xff0c;不同的上下文&#xff0c;对同一个"苹果"&#xff0c;得到截然不同的embedding激活值&#xff1b; Multi-head的作用&#xff1a; 有些类似CNN里用的多个卷积核得到多个Channel的特征图&…...

多人聊天室 NIO模型实现

NIO编程模型 Selector监听客户端不同的zhuangtai不同客户端触发不同的状态后&#xff0c;交由相应的handles处理Selector和对应的处理handles都是在同一线程上实现的 I/O多路复用 在Java中&#xff0c;I/O多路复用是一种技术&#xff0c;它允许单个线程处理多个输入/输出&…...

vue.js学习(day 20)

综合案例&#xff1a;购物车 数据渲染 构建cart购物车模块 准备后端接口服务环境 请求数据存入vuex cart.js // 新建购物车模块 import axios from axios export default {namespaced: true,state () {return {// 购物车数据 [{},{}]list: []}},mutations: {updateList (…...

蓝桥杯二分题

P1083 [NOIP2012 提高组] 借教室 题目描述 在大学期间&#xff0c;经常需要租借教室。大到院系举办活动&#xff0c;小到学习小组自习讨论&#xff0c;都需要向学校申请借教室。教室的大小功能不同&#xff0c;借教室人的身份不同&#xff0c;借教室的手续也不一样。 面对海量租…...

通过 CC-Link IEFB 转 Modbus RTU 网关达成三菱 FX5U PLC 与 RS485 温湿度传感器通信的配置实例展示

一. 案例背景 在某一化工厂现场&#xff0c;现场的设备是三菱FX5UPLC为了避免因为工作环境存在潮湿度较高性、较高温度等对员工造成不健康或危险的现象&#xff0c;现决定在现场装数多台温湿度传感器。通过捷米特JM-CCLKIE-RTU网关将三菱PLC及温湿度传感器连接起来并连接上位机…...

视频质量评价算法 DOVER 使用教程

DOVER 介绍 DOVER是为ICCV2023会议论文“Exploring Video Quality Assessment on User Generated Contents from Aesthetic and Technical Perspectives”提供的官方代码、演示和权重的项目。DOVER(Disentangled Objective Video Quality Evaluator)是一个用于评估用户生成内…...

视频编辑技术:一键生成混剪视频的AI技术应用

随着视频内容的爆炸式增长&#xff0c;视频编辑技术也在不断进步。本文将探讨如何利用AI技术&#xff0c;实现一键生成混剪视频&#xff0c;并自动添加配音和字幕&#xff0c;以提高视频编辑的效率和质量。 AI技术在视频编辑中的应用 AI技术在视频编辑领域的应用越来越广泛&am…...