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

Rust逆向学习 (4)

Reverse for Struct

Rust中的结构体是一个重要的内容,由于Rust中没有类的概念,因此其他编程语言中的封装、继承、多态与Rust中的表现都有较大差异。

我们使用参考书中的一个示例开始进行分析。

Struct 初始化

struct User {username: String,email: String,sign_in_count: u64,active: bool,
}pub fn main() {let mut user1 = User {email: String::from("someone@example.com"),username: String::from("someusername123"),active: true,sign_in_count: 1};println!("{}, {}", user1.email, user1.active);
}

上面这段在汇编层是如何处理的呢?

第一段

example::main:sub     rsp, 296lea     rsi, [rip + .L__unnamed_5]lea     rdi, [rsp + 120]mov     edx, 19call    <alloc::string::String as core::convert::From<&str>>::fromlea     rsi, [rip + .L__unnamed_6]lea     rdi, [rsp + 144]mov     edx, 15call    <alloc::string::String as core::convert::From<&str>>::fromjmp     .LBB17_3....L__unnamed_5:.ascii  "someone@example.com".L__unnamed_6:.ascii  "someusername123"

上面是第一段汇编内容,在源码中,我们是首先对email进行了初始化,在汇编中也是如此。这里分别将两个字符串实例保存到了[rsp+120][rsp+144]处。我们之前分析过,String实例在栈中的大小应该为0x18,可见这两个String实例是完全相邻的,中间没有其他的数据。

第二段

.LBB17_3:mov     rax, qword ptr [rsp + 160]mov     qword ptr [rsp + 64], raxmovups  xmm0, xmmword ptr [rsp + 144]movaps  xmmword ptr [rsp + 48], xmm0lea     rax, [rsp + 72]mov     rcx, qword ptr [rsp + 136]mov     qword ptr [rsp + 88], rcxmovups  xmm0, xmmword ptr [rsp + 120]movups  xmmword ptr [rsp + 72], xmm0mov     qword ptr [rsp + 96], 1mov     byte ptr [rsp + 104], 1mov     qword ptr [rsp + 280], raxlea     rax, [rip + <alloc::string::String as core::fmt::Display>::fmt]mov     qword ptr [rsp + 288], raxmov     rax, qword ptr [rsp + 280]mov     qword ptr [rsp + 32], raxmov     rax, qword ptr [rsp + 288]mov     qword ptr [rsp + 40], raxjmp     .LBB17_6

随后是第二段,这里有一个Rust 1.73与Rust 1.69的不同之处,在老版本中,对于宏将会调用core::fmt::ArgumentV1::new_display将中括号对应的内容转为字符串,而在新版本中,则只会将core::fmt::Display函数地址保存到栈而并不调用。并且结构体中各个元素的内存排列顺序也不相同,通过IDA分析可见在1.73版本中,元素排列与元素定义的顺序相同,但老版本中则不是。这里是因为String实例实现了Display这个Trait,所以能够直接输出。输出时调用的实际上也是DisplayTrait

需要注意的是,第一段中的字符串初始化并不是对结构体的字符串直接进行初始化,而是在栈中另外开辟了0x30大小的空间用于初始化这两个字符串,随后将这段内存的内容复制到结构体中。真正的结构体应该位于[rsp+48]。四个元素的保存地址分别为:[rsp+48][rsp+72][rsp+96][rsp+104],因此,中间的两条指令mov qword ptr [rsp + 96], 1mov byte ptr [rsp + 104], 1就是在对sign_in_countactive进行初始化,因为二者一个是整数类型,一个是布尔值,都是不需要通过new进行初始化的,因此可以直接赋值。

00000000 revlab::User struc ; (sizeof=0x40, align=0x8, copyof_91)
00000000                                         ; XREF: _ZN6revlab4main17h1e5ad0972ab6a820E/r
00000000                                         ; _ZN6revlab4main17h1e5ad0972ab6a820E/r
00000000 username alloc::string::String ?        ; XREF: revlab::main::h1e5ad0972ab6a820+65/w
00000000                                         ; revlab::main::h1e5ad0972ab6a820+72/w
00000018 email alloc::string::String ?           ; XREF: revlab::main::h1e5ad0972ab6a820+77/o
00000018                                         ; revlab::main::h1e5ad0972ab6a820+84/w ...
00000030 sign_in_count dq ?                      ; XREF: revlab::main::h1e5ad0972ab6a820+93/w
00000038 active db ?                             ; XREF: revlab::main::h1e5ad0972ab6a820+9C/w
00000038                                         ; revlab::main::h1e5ad0972ab6a820+11C/o
00000039 db ? ; undefined
0000003A db ? ; undefined
0000003B db ? ; undefined
0000003C db ? ; undefined
0000003D db ? ; undefined
0000003E db ? ; undefined
0000003F db ? ; undefined

第三段

.LBB17_6:mov     rax, qword ptr [rsp + 40]mov     rcx, qword ptr [rsp + 32]mov     qword ptr [rsp], rcxmov     qword ptr [rsp + 8], raxlea     rax, [rsp + 104]mov     qword ptr [rsp + 264], raxmov     rax, qword ptr [rip + <bool as core::fmt::Display>::fmt@GOTPCREL]mov     qword ptr [rsp + 272], raxmov     rax, qword ptr [rsp + 264]mov     qword ptr [rsp + 16], raxmov     rax, qword ptr [rsp + 272]mov     qword ptr [rsp + 24], raxmov     rax, qword ptr [rsp + 24]mov     rcx, qword ptr [rsp + 16]mov     rdx, qword ptr [rsp + 8]mov     rsi, qword ptr [rsp]mov     qword ptr [rsp + 216], rsimov     qword ptr [rsp + 224], rdxmov     qword ptr [rsp + 232], rcxmov     qword ptr [rsp + 240], raxlea     rsi, [rip + .L__unnamed_7]lea     rdi, [rsp + 168]mov     edx, 3lea     rcx, [rsp + 216]mov     r8d, 2call    core::fmt::Arguments::new_v1jmp     .LBB17_8.L__unnamed_7:.quad   .L__unnamed_2.zero   8.quad   .L__unnamed_11.asciz  "\002\000\000\000\000\000\000".quad   .L__unnamed_12.asciz  "\001\000\000\000\000\000\000".L__unnamed_11:.ascii  ", ".L__unnamed_12:.ascii  "\n"

这一段的工作主要就是输出,通过调试发现,新版rustc在使用println!宏时将不再将临时字符串切片参数保存在栈中,但通过IDA依然可以较为容易地辨别。

Struct 作为返回值

下面书中给出一个通过函数初始化结构体的实例:

struct User {username: String,email: String,sign_in_count: u64,active: bool,
}fn build_user(email: String, username: String) -> User {User {email,username,active: true,sign_in_count: 1}
}pub fn main() {let mut user1 = build_user(String::from("someone@example.com"), String::from("someusername123"));println!("{}, {}", user1.email, user1.active);
}
example::build_user:mov     rax, rdimov     rcx, qword ptr [rdx]mov     qword ptr [rdi], rcxmov     rcx, qword ptr [rdx + 8]mov     qword ptr [rdi + 8], rcxmov     rcx, qword ptr [rdx + 16]mov     qword ptr [rdi + 16], rcxmov     rcx, qword ptr [rsi]mov     qword ptr [rdi + 24], rcxmov     rcx, qword ptr [rsi + 8]mov     qword ptr [rdi + 32], rcxmov     rcx, qword ptr [rsi + 16]mov     qword ptr [rdi + 40], rcxmov     qword ptr [rdi + 48], 1mov     byte ptr [rdi + 56], 1ret

从函数的汇编可以看到,这个函数实际上是将第一个参数作为指针完成初始化的,可以将第一个指针理解为this,这与C++类方法的函数调用规则类似。

实现 Debug Trait

一个结构体可以通过#[derive(Debug)]完成对Debug Trait的默认实现:

#[derive(Debug)]
struct Rect {width: u32,height: u32,
}pub fn main() {let rect1 = Rect {width: 30, height: 50};println!("rect1 = {:?}", rect1);
}
example::main:sub     rsp, 88mov     dword ptr [rsp], 30mov     dword ptr [rsp + 4], 50mov     rax, rspmov     qword ptr [rsp + 72], raxmov     rax, qword ptr [rip + <example::Rect as core::fmt::Debug>::fmt@GOTPCREL]mov     qword ptr [rsp + 80], raxmov     rcx, qword ptr [rsp + 72]mov     rax, qword ptr [rsp + 80]mov     qword ptr [rsp + 56], rcxmov     qword ptr [rsp + 64], raxlea     rdi, [rsp + 8]lea     rsi, [rip + .L__unnamed_4]mov     edx, 2lea     rcx, [rsp + 56]mov     r8d, 1call    core::fmt::Arguments::new_v1lea     rdi, [rsp + 8]call    qword ptr [rip + std::io::stdio::_print@GOTPCREL]add     rsp, 88ret

可以看到,汇编代码中获取的就是Debug这个Trait的函数指针,说明不同的宏实际上调用的函数也不同。如果将{:?}修改为{:#?},则原先调用的core::fmt::Arguments::new_v1将会改为调用core::fmt::Arguments::new_v1_formatted。考虑到Rust的格式化字符串非常强大与灵活,有多种输出形式,后面将通过专门的分析对宏展开进行分析,这里不深入探讨。

Reverse for Methods

在Rust中,结构体充当了其他语言中类的功能,可以在结构体下定义方法,使这个方法专属于该结构体。

struct Rect {width: u32,height: u32,
}impl Rect {fn area(&self) -> u32 {self.width * self.height}
}pub fn main() {let rect1 = Rect {width: 30, height: 50};println!("area = {}", rect1.area());
}
example::Rect::area:push    raxmov     eax, dword ptr [rdi]mul     dword ptr [rdi + 4]mov     dword ptr [rsp + 4], eaxseto    altest    al, 1jne     .LBB1_2mov     eax, dword ptr [rsp + 4]pop     rcxret
.LBB1_2:lea     rdi, [rip + str.0]lea     rdx, [rip + .L__unnamed_4]mov     rax, qword ptr [rip + core::panicking::panic@GOTPCREL]mov     esi, 33call    raxud2example::main:sub     rsp, 104mov     dword ptr [rsp + 8], 30mov     dword ptr [rsp + 12], 50lea     rdi, [rsp + 8]call    example::Rect::areamov     dword ptr [rsp + 84], eaxlea     rax, [rsp + 84]mov     qword ptr [rsp + 88], raxmov     rax, qword ptr [rip + core::fmt::num::imp::<impl core::fmt::Display for u32>::fmt@GOTPCREL]mov     qword ptr [rsp + 96], raxmov     rcx, qword ptr [rsp + 88]mov     rax, qword ptr [rsp + 96]mov     qword ptr [rsp + 64], rcxmov     qword ptr [rsp + 72], raxlea     rdi, [rsp + 16]lea     rsi, [rip + .L__unnamed_5]mov     edx, 2lea     rcx, [rsp + 64]mov     r8d, 1call    core::fmt::Arguments::new_v1lea     rdi, [rsp + 16]call    qword ptr [rip + std::io::stdio::_print@GOTPCREL]add     rsp, 104ret

由上述汇编可知,这里还是将rdi作为self使用。

#[derive(Debug)]
struct Rect {width: u32,height: u32,
}impl Rect {fn area(&self) -> u32 {self.width * self.height}fn can_hold(&self, other: &Rect) -> bool {self.width > other.width && self.height > other.height}
}pub fn main() {let rect1 = Rect {width: 30, height: 50};let rect2 = Rect {width: 10, height: 40};println!("{}", rect1.can_hold(&rect2));
}

对于上面的代码,can_hold方法的参数有两个,都是指针,如果将第二个参数的&去掉,则参数有三个。经过试验发现,当一个结构体中的元素数量较少时,不加&可能会将结构体的每个元素分别作为参数传递,当元素数量较多时,则是首先复制然后传递指针。

对于关联函数,由于其第一个参数并不是self,类似于C++中的类静态函数,不需要首先获取结构体实例即可调用,参数传递与一般的函数相同。

Reverse for enum (Part 2)

对于枚举类型,我们在第二篇文章中已经进行了较为详细的解释,对于枚举类型的内存排布有了一定的了解。

下面对枚举类型中定义的方法进行测试。

use std::any::Any;pub enum Student {Freshman(String),Sophomore(String),Junior(String),Senior(String),
}pub fn get_student(grade: i32, name: String) -> Option<Student> {match grade {1 => Some(Student::Freshman(name)),2 => Some(Student::Sophomore(name)),3 => Some(Student::Junior(name)),4 => Some(Student::Senior(name)),_ => None}
}impl Student {fn test(&self) -> String {match self {Student::Freshman(name) => format!("{}", "Calculus").to_string(),Student::Sophomore(name) => format!("{}", "Data Structure").to_string(),Student::Junior(name) => format!("{}", "Computer Network").to_string(),Student::Senior(name) => format!("{}", "Graduation Design").to_string()}}
}pub fn main() {let x = get_student(4, "CoLin".to_string()).unwrap();println!("{}", x.test());
}

上面代码中对于test方法的调用如下:

        mov     rax, qword ptr [rip + core::option::Option<T>::unwrap@GOTPCREL]lea     rdi, [rsp + 40]mov     qword ptr [rsp + 32], rdicall    raxmov     rsi, qword ptr [rsp + 32]lea     rdi, [rsp + 192]call    example::Student::testjmp     .LBB26_3

可以看到方法的第一个参数依然是self,第二个参数则是等待初始化的String实例地址。在代码中是返回String实例,实际上是传入未初始化的指针。

Option<T>

针对Option<T>,Rust在汇编层有自己的处理方式。如果将Option<T>看做一个普通的枚举类型,且Some后面带的是另一个枚举类型,那么这样的话就会产生两层枚举对象,不太优雅。对于get_student函数,下面是部分反编译结果:

.text:0000000000009702 48 89 4C 24 18                mov     [rsp+108h+var_F0], rcx
.text:0000000000009707 83 E8 03                      sub     eax, 3
.text:000000000000970A 77 15                         ja      short def_971F                  ; jumptable 000000000000971F default case
.text:000000000000970A
.text:000000000000970C 48 8B 44 24 18                mov     rax, [rsp+108h+var_F0]
.text:0000000000009711 48 8D 0D B4 09 04 00          lea     rcx, jpt_971F
.text:0000000000009718 48 63 04 81                   movsxd  rax, ds:(jpt_971F - 4A0CCh)[rcx+rax*4]
.text:000000000000971C 48 01 C8                      add     rax, rcx
.text:000000000000971F FF E0                         jmp     rax                             ; switch jump
.text:000000000000971F
.text:0000000000009721                               ; ---------------------------------------------------------------------------
.text:0000000000009721
.text:0000000000009721                               def_971F:                               ; CODE XREF: revlab::get_student::h5c77d454e35cea03+3A↑j
.text:0000000000009721 48 8B 44 24 08                mov     rax, [rsp+108h+var_100]         ; jumptable 000000000000971F default case
.text:0000000000009726 48 C7 00 04 00 00 00          mov     qword ptr [rax], 4
.text:000000000000972D E9 43 02 00 00                jmp     loc_9975

下面的def_971F为默认分支,可以看到这里是将枚举类型的索引值赋值为4,但上面定义的枚举类型一共只有4个值,最大的索引值只能为3。将索引值设置为4实际上也就表示这个枚举类型是一个无效值,这样在内存中实际上并不存在二重枚举类型,而是只有一个Student枚举类型。由此可见,对泛型参数为枚举类型的Option,Rust进行了优化。

Reverse for if-let

if let语句是针对只有一个处理条件和一个默认条件的match语句的平替。由于只有一个特殊条件和默认条件,因此在实际实现中只需要使用类似于if的逻辑即可完成。

pub fn main() {let x = get_student(4, "CoLin".to_string());if let Some(Student::Senior(y)) = x {println!("{}", y);}
}
example::main:sub     rsp, 216mov     byte ptr [rsp + 183], 0lea     rdi, [rsp + 56]lea     rsi, [rip + .L__unnamed_5]mov     edx, 5call    <str as alloc::string::ToString>::to_stringlea     rdi, [rsp + 24]mov     esi, 4lea     rdx, [rsp + 56]call    qword ptr [rip + example::get_student@GOTPCREL]mov     byte ptr [rsp + 183], 1mov     eax, 1xor     ecx, ecxcmp     qword ptr [rsp + 24], 4cmove   rax, rcxcmp     rax, 1jne     .LBB18_2cmp     qword ptr [rsp + 24], 3je      .LBB18_3

可以发现,这里的判断逻辑和match是类似的,都是对枚举索引值进行比较。

总结

本文学习了:

  1. Rust 结构体的内存排布以及结构体方法的参数传递,结构体方法参数传递遵照this参数传递法
  2. Rust 枚举类型方法的参数传递与结构体方法的参数传递类似
  3. Rust if-let语句的判断逻辑,Option<T>的内存结构

相关文章:

Rust逆向学习 (4)

Reverse for Struct Rust中的结构体是一个重要的内容&#xff0c;由于Rust中没有类的概念&#xff0c;因此其他编程语言中的封装、继承、多态与Rust中的表现都有较大差异。 我们使用参考书中的一个示例开始进行分析。 Struct 初始化 struct User {username: String,email: …...

uniapp vue2 vuex 持久化

1.vuex的使用 一、uniapp中有自带vuex插件&#xff0c;直接引用即可 二、在项目中新建文件夹store,在main.js中导入 在根目录下新建文件夹store,在此目录下新建index.js文件 index.js import Vue from vueimport Vuex from vuexVue.use(Vuex)const store new Vuex.Store(…...

【媒体邀约】媒体宣传——企业成长的催化剂

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 媒体宣传是企业成长的催化剂&#xff0c;它在各种方面对企业的成功和发展起到了关键作用。 1. 曝光和知名度&#xff1a; 媒体宣传可以将企业和其产品或服务推向广泛的受众&#xff0c;…...

ansible问题排查

拷贝模板时报错&#xff1a;AnsibleError: template error while templating string: Missing end of comment tag. 原因 shell脚本中地{#和jinja中的语法comment tag相同&#xff0c;而且只有一半&#xff0c;导致无法渲染导致。 解决 在有语法冲突的地方使用组合标签{% r…...

7天入门python系列之第四天python数据结构

第3天主要是学习Python的函数和模块 编者打算开一个python 初学主题的系列文章&#xff0c;用于指导想要学习python的同学。关于文章有任何疑问都可以私信作者。对于初学者想在7天内入门Python&#xff0c;这是一个紧凑的学习计划。但并不是不可完成的。第四天开始python 数据…...

远程电脑未连接显示器时分辨率太小的问题处理

背景&#xff1a;单位电脑显示器坏了&#xff0c;使用笔记本通过向日葵远程连接&#xff0c;发现分辨率只有800*600并且不能修改&#xff0c;网上找了好久找到了处理方法这里记录一下&#xff0c;主要用到的是一个虚拟显示器软件usbmmidd_v2 1)下载usbmmidd_v2 2&#xff09;…...

Java 设计模式——解释器模式

目录 1.概述2.结构3.案例实现3.1.抽象表达式类3.2.终结表达式3.3.非终结表达式3.4.环境类3.5.测试 4.优缺点5.使用场景 1.概述 &#xff08;1&#xff09;如下图&#xff0c;设计一个软件用来进行加减计算。我们第一想法可能就是使用工具类&#xff0c;提供对应的加法和减法的…...

面试经典150题——Day37

文章目录 一、题目二、题解 一、题目 73. Set Matrix Zeroes Given an m x n integer matrix matrix, if an element is 0, set its entire row and column to 0’s. You must do it in place. Example 1: Input: matrix [[1,1,1],[1,0,1],[1,1,1]] Output: [[1,0,1],[0,…...

在 Arduino IDE 2.0 中安装 ESP32 板(Windows、Mac OS X、Linux)

有一个新的 Arduino IDE——Arduino IDE 2.0&#xff08;测试版&#xff09;。在本教程中&#xff0c;您将学习如何在 Arduino IDE 2.0 中安装 ESP32 板并将代码上传到板。本教程与 Windows、Mac OS X 和 Linux 操作系统兼容。 据 Arduino 网站称&#xff1a;“ Arduino IDE 2.…...

西门子S7-1200PLC混合通信编程(ModbusTcp和UDP通信)

S7-1200PLC的MODBUS-TCP通信 西门子PLC ModbusTcp通信访问网关后从站(SCL语言轮询状态机)-CSDN博客文章浏览阅读305次。西门子PLC的ModbusTcp通信在专栏已有很多文章介绍,所不同的是每个项目的通信需求都略有不同,今天我们以访问网关后的三个从站数据来举例,给出轮询的推荐…...

Hbase 迁移小结:从实践中总结出的最佳迁移策略

在数据存储和处理领域&#xff0c;HBase作为一种分布式、可扩展的NoSQL数据库&#xff0c;被广泛应用于大规模数据的存储和分析。然而&#xff0c;随着业务需求的变化和技术发展的进步&#xff0c;有时候我们需要将现有的HBase数据迁移到其他环境或存储系统。HBase数据迁移是一…...

键盘win键无法使用,win+r不生效、win键没反应、Windows键失灵解决方案(亲测可以解决)

最近几天发现自己笔记本的win键无法使用&#xff0c;win失灵了&#xff0c;但是外接键盘后则正常:。 这个问题困扰了我一周&#xff0c;我都以为自己的枪神坏了。 寻找了几个解决方法&#xff0c;网上看了好多好多稀里糊涂的办法&#xff0c;都是不管用的&#xff0c;这里给大…...

1. 深度学习——激活函数

机器学习面试题汇总与解析——激活函数 本章讲解知识点 什么是激活函数&#xff1f; 为什么要使用激活函数&#xff1f; 详细讲解激活函数 本专栏适合于Python已经入门的学生或人士&#xff0c;有一定的编程基础。本专栏适合于算法工程师、机器学习、图像处理求职的学生或人…...

chatglm3-6b部署及微调

chatglm3-6b部署及微调 modelscope: https://modelscope.cn/models/ZhipuAI/chatglm3-6b/filesgithub: https://github.com/THUDM/ChatGLM3镜像: ubuntu20.04-cuda11.7.1-py38-torch2.0.1-tf1.15.5-1.8.1v100 16G现存 单卡 安装 软件依赖 # 非必要无需执行 # pip install -…...

Hive 知识点八股文记录 ——(二)优化

函数 UDF&#xff1a;用户定义函数 UDAF&#xff1a;用户定义聚集函数 UDTF&#xff1a;用户定义表生成函数 建表优化 分区建桶 创建表时指定分区字段 PARTITIONED BY (date string)指定分桶字段和数量 CLUSTERED BY (id) INTO 10 BUCKETS插入数据按分区、分桶字段插入 …...

计算机技术专业CSIT883系统分析与项目管理介绍

文章目录 前言一、学科学习成果二、使用步骤三、最低出勤要求四、讲座时间表五、项目管理 前言 本课程介绍了信息系统开发中的技术和技术&#xff0c;以及与管理信息技术项目的任务相关的方法和过程。 它研究了系统分析师、客户和用户在系统开发生命周期中的互补角色。 它涵盖…...

gitlab安装地址

镜像地址&#xff1a; Index of /gitlab-ce/yum/el7/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror wget Index of /gitlab-ce/yum/el7/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror rpm -i gitlab-ce-15.9.1-ce.0.el7.x86_64.rpm 一直提示 &#x…...

Spark处理方法_提取文件名中的时间

需求描述 通过读取目录下的类似文件的datapath路径的文件名及文件内容&#xff0c;需要将读取的每一个文件的文件名日期解析出来&#xff0c;并作为读取当前文件内容递归读取当前文件一个df列&#xff0c;列名为“时间”&#xff1b;后面就是读一个文件&#xff0c;解析一下时间…...

技术分享 | 测试平台开发-前端开发之数据展示与分析

测试平台的数据展示与分析&#xff0c;我们主要使用开源工具ECharts来进行数据的展示与分析。 ECharts简介与安装 ECharts是一款基于JavaScript的数据可视化图表库&#xff0c;提供直观&#xff0c;生动&#xff0c;可交互&#xff0c;可个性化定制的数据可视化图表&#xff…...

NZ系列工具NZ06:VBA创建PDF文件说明

我的教程一共九套及VBA汉英手册一部&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到数据库&#xff0c;到字典&#xff0c;到高级的网抓及类的应用。大家在学习的过程中可能会存在困惑&#xff0c;这么多知识点该如何组织…...

rknn优化教程(二)

文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

基于服务器使用 apt 安装、配置 Nginx

&#x1f9fe; 一、查看可安装的 Nginx 版本 首先&#xff0c;你可以运行以下命令查看可用版本&#xff1a; apt-cache madison nginx-core输出示例&#xff1a; nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

JVM垃圾回收机制全解析

Java虚拟机&#xff08;JVM&#xff09;中的垃圾收集器&#xff08;Garbage Collector&#xff0c;简称GC&#xff09;是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象&#xff0c;从而释放内存空间&#xff0c;避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

基于Docker Compose部署Java微服务项目

一. 创建根项目 根项目&#xff08;父项目&#xff09;主要用于依赖管理 一些需要注意的点&#xff1a; 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件&#xff0c;否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包&#xff08;Closure&#xff09;&#xff1f;闭包有什么应用场景和潜在问题&#xff1f;2.解释 JavaScript 的作用域链&#xff08;Scope Chain&#xff09; 二、原型与继承3.原型链是什么&#xff1f;如何实现继承&a…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

佰力博科技与您探讨热释电测量的几种方法

热释电的测量主要涉及热释电系数的测定&#xff0c;这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中&#xff0c;积分电荷法最为常用&#xff0c;其原理是通过测量在电容器上积累的热释电电荷&#xff0c;从而确定热释电系数…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析

Linux 内存管理实战精讲&#xff1a;核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用&#xff0c;还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...

JavaScript基础-API 和 Web API

在学习JavaScript的过程中&#xff0c;理解API&#xff08;应用程序接口&#xff09;和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能&#xff0c;使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...