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

NASM汇编

1. 前置知识

1. 汇编语言两种风格

  • intel:我们学的NASM就属于Intel风格
  • AT&T:GCC后端工具默认使用这种风格,当然我们也可以加选项改成intel风格

2. 代码

1. 段分布

  • .text: 存放的是二进制机器码,只读
  • .data: 存放有初始化的全局变量。非默认值
  • .bss:存放未初始化的全局变量,或者默认初始化的全局变量。这一部分在二进制文件中不占硬盘空间,即不会真实存储这些为初始化的变量,而是在程序加载到内存时再分配。当然肯定需要有个标识,告诉该怎么分配内存
  • .rodata:存放只读数据,如常量数据
#include <stdio.h>int global_var1;                     // bss
int global_var2 = 0;                 // bss
int global_var3 = 42;                // data
const char* hello = "Hello, world!"; // rodataint main()                           // text
{printf("%s\n", hello);           // textreturn 0;                        // text
}

2. 标签

section .data
counter db 10   ; 声明并初始化计数器,counter表示计数器的地址section .text
global _start_start:nop           ; No operation, 用于占位loop_start:      ; 标签定义一个循环开始的位置dec byte [counter]   ; 减少计数器的值jnz loop_start       ; 如果计数器非零(ZF=0),跳回到 loop_start; ... 程序的其余部分
  • 标签可以被视为一个给定位置的名称或者是一个指向特定地址的指针。 可以把他们都当作地址
  • 上面的例子定义了两个标签:counterloop_start
  • 本地标签:本地标签通常以.开头,只在本地上下文有意义

3. 包含

section .data
%include "data.asm"section .text
global _start
_start:%include "code.asm"
  • include通常用于在一个源文件中插入另一个源文件的内容,等同于C中的#include
  • 上面的例子就是将 data.asm的内容插入到.data
section .dataimage: incbin "picture.bmp"
  • incbin:此伪指令可以用于将一些预先生成的、需要嵌入到程序的二进制数据(如图像、音频、编码的数据文件等)直接加载到汇编程序中。
  • 其格式为incbin “filename” [, skip, length]

3. 内存

1. 寄存器(在x64架构中)

通用寄存器

  1. RAX: 累加器。用于进行算数运算,同时也是一部分系统调用(例如sys_write或sys_read)中存储返回值的寄存器。
  2. RBX: 基址寄存器。一般用于在间接寻址中保存变量的内存地址。
  3. RCX: 计数器寄存器。在循环迭代中常常用作循环计数。
  4. RDX: 数据寄存器。通常与RAX配合,用于大数的乘法和除法运算。
  5. RSI: 源变址寄存器。在字符串和内存操作中常常用来存储源地址。
  6. RDI: 目的变址寄存器。在字符串和内存操作中常常用来存储目的地址。
  7. RBP: 基址指针。通常被用作帧指针,指示当前函数帧在堆栈中的位置。
  8. RSP: 堆栈指针。始终指向当前栈顶的位置。
  9. R8 - R15: 在x64架构中新增的8个通用寄存器,可以通用性地使用。

对于这些寄存器的使用,取决于具体的应用场景和编程约定。例如,某些系统调用可能会使用RDI,RSI,RDX,R10,R8和R9来传递前6个参数。(以上寄存器都是64位
指令寄存器:

  1. RIP:指令指针,指向下一条要执行的指令。64位

段寄存器:(都是16位)

  1. CS(Code Segment):代码段寄存器,包含当前正在执行的代码的段基址。
  2. DS(Data Segment):数据段寄存器,通常包含程序正在操作的数据的段基址。
  3. SS(Stack Segment):堆栈段寄存器,包含当前堆栈的段基址。
  4. ES(Extra Segment):附加段寄存器,用于存储其他数据段的基址。
  5. FS
  6. GS

标志寄存器:

  1. RFLAGS(64位):标志寄存器,保存了程序的运行状态
    在这里插入图片描述

一个简单的汇编例子如下:

section .data
msg db 'Hello, World!', 0section .text
global _start_start:; syscall: writemov eax, 1        ; sys_writemov edi, 1        ; file descriptor: stdoutlea rsi, [rel msg] ; buffer address: msgmov edx, 13       ; message lengthsyscall           ; perform syscall; syscall: exitxor edi, edi      ; exit status code:

2. 寻址

在 x64 架构中,使用 NASM 汇编,支持的寻址方式如下:

  1. 立即寻址(Immediate addressing):操作的数据直接包含在指令中。比如:

    mov rax, 123 ; rax = 123
    
  2. 寄存器寻址(Register addressing):操作数存储在寄存器中。例如:

    add rax, rbx ; rax = rax + rbx
    
  3. 直接寻址(Direct addressing):操作的数据在内存中的某个具体位置,其地址在指令中直接给出。例如:

    mov eax, [someVariable] ; eax = contents of memory at address someVariable
    
  4. 间接寻址(Indirect addressing):操作数存储在由另一个寄存器指定的内存地址中。例如:

    mov eax, [rbx] ; eax = contents of memory at address stored in rbx
    
  5. 基址寻址(Base addressing):操作数的地址是某个寄存器的值加上一个常量偏移量。例如:

    mov eax, [rbx+4] ; eax = contents of memory at address (rbx + 4)
    
  6. 索引寻址(Indexed addressing):操作数地址由基址加上索引值乘以元素大小给出。它通常被用来处理数组。例如:

    mov eax, [rbx+rcx*4] ; eax = contents of memory at address (rbx + rcx*4)
    
  7. 基址变址寻址(Base-indexed addressing):使用一个基址加上一个偏移量,再加上一个索引。例如:

    mov eax, [rbx+rcx+4] ; eax = contents of memory at address (rbx + rcx + 4)
    

以上就是在 NASM 中使用的基于 x64 架构的寻址方式的简单介绍。选择哪种寻址方式取决于你需要在哪里获取数据或者指令应该如何计算操作数的地址。

4. 指令

1. 操作数类型

  • 立即数(imm):以常量出现在指令中,只能是源操作数
  • 寄存器(reg):数据存放在寄存器中,指令中给出寄存器名
  • 内存(mem):数据存在于内存单元中,指令中给出内存地址

2. 操作

  1. 数据移动指令

    • mov:复制(移动)源操作数到目标操作数。
    • push:将数据压入堆栈。
    • pop:从堆栈中弹出数据。
  2. 算数指令

    • add:两个操作数相加,结果保存到目标操作数中。
    • sub:两个操作数相减,结果保存到目标操作数中。
    • inc:将操作数的值加一。
    • dec:将操作数的值减一。
    • imul:相乘操作。
    • idiv:相除操作。
  3. 逻辑运算指令

    • and:逻辑与操作。
    • or:逻辑或操作。
    • xor:逻辑异或操作。
    • not:逻辑非操作。
  4. 控制流指令

    • jmp:无条件跳转到指定的代码地址。
    • je, jne, jg, jge, jl, jle:基于某个条件跳转。
    • call:调用一个子程序/函数,通常会跳转到一个代码地址,并将返回地址压入堆栈以方便返回。
    • ret:从子程序/函数返回,通常会弹出一个值作为下一个要执行的代码地址。
    • loop: 循环指令
  5. 比较和测试指令

    • cmp:比较两个操作数。
    • test:逻辑与测试。
  6. 字符串操作指令

    • movs, cmps, scas, lods, stos:对字符串进行操作的一组指令。
  7. 转换指令

    • cwdecdqecwdcdqcqo:转换字宽度的一组指令。

5. 数据

High Addresses ---> .----------------------.|      Environment     ||----------------------||                      |   Functions and variable are declared|         STACK        |   on the stack.
base pointer ->     | - - - - - - - - - - -||           |          ||           v          |:                      :.                      .   The stack grows down into unused space.         Empty        .   while the heap grows up. .                      ..                      .   (other memory maps do occur here, such .                      .    as dynamic libraries, and different memory:                      :    allocate)|           ^          ||           |          |brk point ->       | - - - - - - - - - - -|   Dynamic memory is declared on the heap|          HEAP        ||                      ||----------------------||          BSS         |   Uninitialized data (BSS)|----------------------|   |          Data        |   Initialized data (DS)|----------------------||          Text        |   Binary code
Low Addresses ----> '----------------------'

1. 内存分段

在汇编语言层面,变量主要体现为内存的确定位置。

在 NASM 汇编中,你可以通过 SECTION 或者 SEGMENT 关键字把变量定义在不同的段区。以下是一些常见的段区:

  1. .data 段:用于存储程序中已初始化的全局变量和静态变量。
    SECTION .data
    var1 db 10             ; 定义一个字节变量var1并初始化值为10
    var2 dd 1000           ; 定义一个双字变量var2并初始化值为1000
    arry db 1, 2, 3, 4, 5  ; 定义了一个长度为5的字节数组,在访问时通过基址加上偏移的方式访问各元素。
    
  2. .bss 段:用于声明未初始化的全局变量和静态变量,这些变量在程序开始执行前自动初始化为0。声明变量使用 resb, resw, resd, resq 等伪指令。
    SECTION .bss
    var3 resb 1            ; 定义一个字节变量var3
    var4 resd 1            ; 定义一个双字变量var4
    b    resq 2.           ; 2个DQ空间
    blen equ $ - b.        ; resq*2=16
    
  3. .text 段:是程序代码段,会包含程序的可执行指令。
    SECTION .text
    global _start          ; 声明一个全局入口
    _start:; 你的汇编代码
    
  4. .rodata 段:用于存储只读数据,比如你的程序中的常量字符串。
    SECTION .rodata
    msg db "Hello, World", 0    ; 定义一个字符串常量msg。 每个字符一个字节,0在c语言表示字符串结尾
    screenWidth equ 1024        ; 定义一个符号,名字叫做screenWidth,代表数值为1024
    
  • x86/x64架构采用小端数据存储,var dd 0x12345678 这个四字节变量在内存中从低位地址到高位地址依次是0x78,0x56,0x34,0x12。在访问大于一个字节的内存时需要小心处理字节的顺序。
  • 变量定义在 .bss.data段中
  • 常量在.rodata段中

2. 声明内存分配的伪指令

  1. db:Define Byte,定义一个字节大内存空间
    myConst db 12 ; 声明一个名为 myConst 的字节常量,值为12
    
  2. dw:Define Word,定义一个两字节大的内存空间。
    myConst dw 1234 ; 声明一个名为 myConst 的字常量,值为1234
    
  3. dd:Define Double Word,定义一个四字节大的内存空间。
    var dd 12345678 ; 声明一个名为var 的双字变量,值为12345678
    
  4. dq:Define Quad Word,定义一个八字节大的内存空间。
    myConst dq 123456789012345678 ; 声明一个名为 myConst 的四字常量,值为123456789012345678
    
  5. dt:Define Ten Bytes,定义一个十字节大的内存空间。

3. equ伪指令

  • equ 在 NASM 汇编中用于声明一个符号。
  • 在汇编阶段,这些由 equ 定义的符号将被替换为它们代表的实际值。
  • 类似于在高级编程语言中,我们定义的预处理宏或者常量。
  • 它是伪指令,不占内存

例如,如果你写下以下代码:

PI equ 3.14
times 2*PI

在汇编阶段,这将被视为:

times 2*3.14

这样的特性使得 equ 成为定义常量值、内存大小或其他需要在编译阶段进行代替的符号的理想选择。

  • equ 定义的是符号以及其对应的值,不分配内存;
  • db, dw, dd, dq 定义的是内存中的数据,即变量或者说是内存分配的常量,会占用实际的内存空间。

4. times 伪指令

在 NASM 汇编中,times 是一种伪指令,它的功能是重复指定的汇编指令或数据定义指定的次数。

  1. 数据段中的使用times 可以用来定义重复的数据。

    section .data
    ones db 1      ; 定义一个字节的数据大小为1
    bigspace times 1000 db 0  ; 定义一个包含1000个字节的数据,每个字节初始化为0
    
  2. 代码段中的使用times 可以用来重复指定的汇编指令。

    section .text
    global _start
    _start:times 5 mov al, 'A'  ; 执行5次mov al, 'A' 指令
    

注意:上述代码段中的用法仅作为times概念理解,实际上这段代码并没有意义,因为连续执行相同的mov指令并不会有特别的效果,因为每次执行都会覆盖原有内容。在实际编程中,times通常用于数据定义,如初始化一个具有相同初始值的大数组。

总的来说,times 是一种在汇编期间进行循环展开的方法,它可以用来定义大块的重复数据,或者重复执行同一条指令。

5. $$$符号的含义

  • $$ 表示当前指令的地址。这在给向前或者向后跳转的指令填写距离的时候很有用,因为你可以用 $ 来代表当前的位置,然后和目标指令的标签计算距离。

    mov eax, $ ; 将当前指令的地址赋给 eax
    
  • $$$$ 表示当前段的开始地址。它通常用来计算相对于段开始的偏移。这在一些需要处理相对地址的操作中比较有用。

    mov eax, $$ ; 将当前段开始的地址赋给 eax
    

6. %assign%define宏与处理器命令

在 NASM 汇编中,%assign%define 是宏预处理器指令,用于定义和赋值符号。

  1. %define是用来定义一个宏常量或者宏。定义后,当汇编器在后续的代码中遇到这个宏时,就会用其定义的字符串替换。典型用法如下:

    %define BUFFER_SIZE 1024
    mov eax, BUFFER_SIZE     ; 预处理阶段会替换成 mov eax, 1024
    
  2. %assign是用来给一个符号赋值一个表达式,然后可以在后续中用这个符号来引用这个表达式。典型用法如下:

    %assign BUFFER_SIZE 1024
    mov eax, BUFFER_SIZE    ; 预处理阶段会替换成 mov eax, 1024
    
  • 两者的主要区别在于 %assign 通常用于数值或者可以计算的表达式,而 %define 更多的是用于字符串替换。比如你可以在 %assign 中写一个算术表达式,而 %define 则不解析它的内容,直接做文本替换。
%assign SIZE 1024 * 2      ;size=2048
%define SIZE_DEF 1024 * 2  ;size="1024 * 2"

7. 前缀指令rep

  • rep 是一种前缀指令,通常与串操作指令(如 movs, stos, cmps, scas, lods 等)一起使用,用于对字符串进行重复操作。

  • rep 指令的功能是:只要 cxecx(根据地址大小,16位环境下使用 cx,32位环境下使用 ecx)的值不为0,就重复执行后面的指令,并且每执行一次,cxecx 的值自减1。

下面是一个例子,使用 rep 前缀指令来赋值字符串前四个字节。假设我们要将字符串的前四个字节赋值为 'A'

section .data
buffer db '12345678'  ; 原始字符串section .text
global _start
_start:mov ecx, 4      ; 要复制的字节数量mov al, 'A'     ; 要复制的字节值lea edi, [buffer] ; 目标字符串的地址rep stosb       ; 执行赋值操作; 假设用 `_start` 函数作为程序的起点,; 你可以在这里添加你想要的代码,; 比如将更改后的字符串打印到控制台。; 程序退出mov eax, 0x60xor edi, edi  ; 参数syscall       ; 调用系统退出

rep stosb 指令的作用是将 AL 寄存器的内容存储到由 EDI 寄存器指向的内存位置,然后 EDI 将根据标志寄存器中的方向标志(Direction Flag)增加或减少。如果方向标志为 0(默认),EDI 加1,如果为 1,EDI 减1。然后 ECX 减 1。当 ECX 变为 0 时,rep 指令就会停止重复。因此,上述代码的效果就是将 'A' 复制4次到 buffer

此外,还有两个与 rep 类似的重复前缀:

  • reperepz:只要 zx 标志(零标志)为真,并且 cxecx 的值不为 0,就重复执行后面的指令。这种情况常与 cmpsb, cmpsw, 或 scas 等指令配合使用。

  • repnerepnz:只要 zx 标志(零标志)为假,并且 cxecx 的值不为 0,就重复执行后面的指令。这种情况常与 cmpsb, cmpsw, 或 scas 等指令配合使用。

8. 结构体如何实现

在汇编语言中,并没有直接定义结构体的语法,但是我们可以使用数据定义指令(如 db, dw, dd, dq)以及各种标签和偏移来创建类似结构体的复合数据类型。

下面是在 NASM 汇编语言中定义和操作结构体的一个例子:

例如,我们要定义一个类似 C 语言中的如下结构体:

struct MyStruct {int id;char name[10];
};

在汇编中,可以这样定义:

section .dataMyStruct: .id     dd  1     ; 4 bytes for integer value.name   db 'nasm' ; 10 bytes for char array.endStruct

然后我们可以使用标签定位每个元素,并对其进行操作。例如我们可以改变 id 的值:

mov [MyStruct.id], dword 2

或者改变 name 的值:

mov [MyStruct.name], byte "x"

在 NASM 汇编中,. 前缀被用于定义一个局部标签,这个局部标签只在距离它最近的上一个非局部标签(没有以.开头的标签)之后可见。

在上面例子中, .id.nameMyStruct 的局部标签。在 MyStruct 之后的代码中,你可以通过 MyStruct.id 访问 id 域,通过 MyStruct.name 访问 name 域。当然你还可以定义一个新的结构,并且使用相同的局部标签名。例如:

Section .dataMyStruct: .id   dd  1.name db 'nasm'YourStruct:.id   dd  2.name db 'assembly'

在这个例子中,.id.nameYourStruct中重新开始,且不会引起重定义错误。你可以通过YourStruct.idYourStruct.name来访问这个新结构的字段。这种方式使代码更加结构化,易于理解和维护。

6. 函数

1. 宏的定义

在汇编语言中,我们可以使用 %macro%endmacro 指令来定义一个宏。宏可以包含任意的代码片段,并用一个自定义的名字来标识它,然后可以在需要的地方通过这个名字来使用宏。

以下是一个在 NASM 汇编中定义和使用宏的例子:

; 定义宏
%macro print 1mov eax, 4       ; sys_write 的系统调用号为4mov ebx, 1       ; 文件描述符 1 —— stdoutmov ecx, %1      ; 要打印的字符串mov edx, %1len   ; 要打印的字符串长度int 0x80         ; 执行系统调用
%endmacrosection .data
Msg db 'Hello, World!',0xA ; 0xA 不包括在长度计数中
Len equ $-Msgsection .text
global _start
_start:print Msg         ; 使用宏mov eax, 1xor ebx, ebxint 0x80

在此示例中,我们定义了一个名为 print 的宏,该宏接收一个参数(用 %1 表示),将这个参数打印到 stdout。我们可以通过将字符串名传递给 print 宏来使用它,就像在 _start: 标签下那样。

注意,%macro 指令之后的 1 表示该宏需要一个参数。如果你的宏需要多个参数,你可以通过改变这个数字,而且可以用 %1%2 等来访问这些参数。

此外,%1len 是 NASM 的一个特性,它可以返回宏参数的长度,这在处理字符串时非常有用。当我们传递 Msg 作为参数到 print时,%1len 就被替换成了 Msg 的长度,也就是字符串 ‘Hello, World!’ 的字符数。这个长度在编译期就被确定下来,不会在运行期改变。

2. 规定宏的参数个数

; 定义一个接受两个参数的宏
%macro sum 2mov eax, %1add eax, %2
%endmacrosection .text
global _start
_start:sum 5, 3    ; 调用宏,参数为5和3

在此示例中,sum是一个宏,它接受两个参数(表示为%1%2)。此宏将第一个参数和第二个参数相加,然后将结果存入eax寄存器。

使用sum 5, 3%1是5

3. 宏的重载

在汇编语言中,特别是在 NASM 中,没有直接支持宏重载的功能。

有一些间接的方法可以模拟"宏重载"的效果。一种常见的方法是根据参数数量的不同来改变宏的行为。NASM 这样支持:

%macro say 0-2  ; 0-2 表示该宏可以接受0个,1个或2个参数%if %0 == 0     ; %0 用来获取宏参数的数量%define msg "Hello, World!"
%elif %0 == 1%define msg %1
%else%define msg "Too many arguments"
%endifmov eax, 4mov ebx, 1mov ecx, msgmov edx, msglenint 0x80%endmacro

如上的代码,say 宏可以接受0个、1个或2个参数,并根据参数数量的不同采取不同的行为,实际上就模拟了"宏重载"的效果。

不过要注意,这样的方法在复杂的程序中可能会导致代码难以理解和维护,所以应当谨慎使用。

在需要进行底层编程的时候,如果有“宏重载”的需求,可能需要考虑使用更高级的语言或者 assembly 的宏处理工具,例如使用 C/C++ 可以更便捷地处理此类需求。

4. 宏中使用本地标签%%

在 NASM 汇编中,本地标签(或称局部标签)常常在宏定义中使用,主要用来创建在宏内部唯一,但在宏的实例之间不唯一的标签名。

对于宏内部的代码流程控制,我们通常需要使用跳转指令,而跳转指令又需要标签。如果我们在不同的宏或者宏的多个实例中使用相同名称的标签,就会造成标签重复定义的问题。为了解决这个问题,NASM 提供了一个特性,允许我们在宏定义中使用 %% 前缀来创建本地标签。

以下是一个在 NASM 中创建和使用本地标签的例子:

%macro do_something 0mov eax, 1%%loop:            ; 一个本地标签add eax, eaxcmp eax, 1024jl %%loop         ; 使用本地标签
%endmacrosection .text
global _start
_start:do_something     ; 调用宏第一次do_something     ; 调用宏第二次

在这个例子中,我们定义了一个名为 do_something 的宏,该宏包含一个本地标签 %%loop 和一个跳转指令 jl,该跳转指令在 eax 小于1024时,跳转到 %%loop 标签处执行代码。我们可以多次调用这个宏,每次调用这个宏时,宏内的 %%loop 都为新生成的代码块创建一个新的,唯一的标签。

使用本地标签,我们可以在宏定义中包含更复杂的控制流程,而不需要担心标签重复定义的问题。同时,由于标签名在不同宏实例中可以相同,这也使得我们可以编写更通用,更有复用性的宏。

5. 函数运行栈

在这里插入图片描述

  • call: 将IP寄存器内容入栈,跳转到目标函数
  • ret:弹出值到IP寄存器,跳转回调用方,继续执行

6. 函数调用

  • 在Linux下,函数的参数主要通过RDI, RSI, RDX, RCX, R8, R9 这些寄存器传递,如果参数多于这些寄存器的数量,那么剩余的参数会通过堆栈传递。
  • 函数的返回值通常通过RAX寄存器返回。

以下是一个在 NASM 中的示例,包含定义和调用函数:

section .data
value1  dq 5
value2  dq 3
result  dq 0section .text
global _start
_start:; 把参数放在寄存器中mov rdi, [value1]mov rsi, [value2]; 调用函数call add_two_numbers; 函数返回后,结果在 rax 寄存器中mov [result], rax; 退出程序mov eax, 60xor edi, edisyscall; 函数: add_two_numbers
; 参数: rdi = num1, rsi = num2
; 返回: rax = num1 + num2
add_two_numbers:; 函数体add rdi, rsimov rax, rdiret ; 返回

在上述代码中,我们定义了一个函数add_two_numbers,它接受两个参数,返回它们的和。函数的参数通过rdirsi寄存器传递,返回值存储在rax寄存器中。call指令用于执行函数调用,ret指令用于从函数返回。

7. 函数的保护现场处理

在 NASM 风格的 x86-64(也叫 AMD64)汇编中,保存现场和恢复现场的代码示例如下:

section .text
global _start
_start:; 保存现场push raxpush rbx; 这里是函数调用等操作; ...; 恢复现场pop rbxpop rax; 退出程序mov eax, 60xor edi, edisyscall
  • 通过 push 指令将 raxrbx 寄存器的当前值保存到堆栈中,该过程被称为"保存现场"。
  • 然后在函数调用结束后,我们通过 pop 指令将 rbxrax 寄存器的值从堆栈中恢复,这就是"恢复现场"。
  • mov eax, 60 将系统调用号设置为 60(SYS_exit 是 Linux 下的系统调用,代表请求操作系统退出程序),
  • xor edi, edi 是把 edi 寄存器清零,代表退出状态码为 0,最后 syscall 指令执行系统调用。

8. 函数调用的相关命令

  • call: 此指令用于跳转到函数或子程序。它首先将下一条指令的地址(即返回地址)压入堆栈,然后跳转到目标函数的首地址开始执行。比如,call functionName

  • ret: 此指令用于从函数或子程序返回。它从堆栈中弹出一个值,然后将此值作为即将要执行的下一条指令的地址,即跳转回函数调用前的点。如果在 call 之前堆栈有被正确设定,ret 就能正常返回。

  • enterleave: 保护现场和恢复现场的命令

9. 调用libc函数规范

  • 参数从左到右,依次使用RDIRSIRDXRCXR8R9寄存器传递参数
  • 如果还不够,参数从右到左依次入栈
  • 返回值RAXRDX:RAX

相关文章:

NASM汇编

1. 前置知识 1. 汇编语言两种风格 intel&#xff1a;我们学的NASM就属于Intel风格AT&T&#xff1a;GCC后端工具默认使用这种风格&#xff0c;当然我们也可以加选项改成intel风格 2. 代码 1. 段分布 .text: 存放的是二进制机器码&#xff0c;只读.data: 存放有初始化的…...

第三章 HL7 架构和可用工具 - 使用 HL7 架构结构页面

文章目录 第三章 HL7 架构和可用工具 - 使用 HL7 架构结构页面使用 HL7 架构结构页面查看文档类型列表查看消息结构查看段结构 第三章 HL7 架构和可用工具 - 使用 HL7 架构结构页面 使用 HL7 架构结构页面 通过 HL7 架构页面&#xff0c;可以导入和查看 HL7 版本 2 架构规范。…...

spring注解驱动开发(一)

1、需要导入的spring框架的依赖 <dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>4.3.12.RELEASE</version></dependency>2、Configuration 设置类为配置类 3、Annota…...

Vue3搭建启动

Vue3搭建&启动 一、创建项目二、启动项目三、配置项目1、添加编辑器配置文件2、配置别名3、处理sass/scss4、处理tsx(不用的话可以不处理) 四、添加Eslint 一、创建项目 npm create vite 1.project-name 输入项目名vue3-vite 2.select a framework 选择框架 3.select a var…...

阻塞队列(模拟实现)

概念 阻塞队列是带有阻塞功能的队列 特性 当队列满的时候&#xff0c;继续入队列&#xff0c;就会出现阻塞&#xff0c;阻塞到其他线程从队列中取走元素为止 当队列空的时候&#xff0c;继续出队列&#xff0c;也会发生阻塞&#xff0c;阻塞到其他线程往队列中添加元素为止 特…...

VScode中python的相对路径与绝对路径 FileNotFoundError: [Errno 2] No such file or directory

VScode中&#xff0c;python里的相对路径是相对于当前工作目录来定位的&#xff0c;而当前的工作目录在VScode中下方的终端窗口会有提示&#xff1a; 说明此时的工作目录并非当前python文件所在的目录&#xff0c;而是C:\Users\xxxxx(你的用户名)。因此&#xff0c;使用VScode…...

Unity XML2——C#读写XML

一、XML 文件的存放位置 &#xff08;一&#xff09;只读不写的 XML ​ 放在 Resouces 或者 StreamingAssets 文件夹下&#xff0c;详见 Unity基础3——Resources资源动态加载_weixin_53163894的博客-CSDN博客。 &#xff08;二&#xff09;动态存储的 XML ​ 放在 Applica…...

带wiringPi库的交叉编译 ---宿主机x86Ubuntu,目标机ARMv8 aarch64(香橙派)

带wiringPi库的交叉编译如何进行 先交叉编译wiringPi库&#xff0c;编译出的库适合香橙派&#xff0c;这时候交叉编译可执行程序的平台和链接库的格式也是正确的&#xff0c;然后通过-I和-L来指定链接的wiringPi库的头文件和库的位置&#xff0c;但是现在还没有学习过&#xf…...

数据仓库基础知识

什么是数据仓库&#xff1f; 数仓&#xff0c;DataWarehouse&#xff0c;是一个 面向主题的、集成的、稳定的、与时间相关的 数据集合。 而这个数据集合的建立&#xff0c;是为了支持管理者的决策过程。 也就是说&#xff0c;我们通过建设数仓&#xff0c;为业务中的流程改进、…...

M 芯片的 macos 系统安装虚拟机 centos7 网络配置

centos 安装之前把网络配置配好或者是把网线插好 第一步找到这个 第二步打开网络适配器 选择图中所指位置 设置好之后 开机启动 centos 第三步 开机以后 编写网卡文件保存 重启网卡就可以了&#xff0c;如果重启网卡不管用&#xff0c;则重启虚拟机即可 “ ifcfg-ens160 ” 这…...

AcWing 3708. 求矩阵的鞍点

输入样例&#xff1a; 3 4 1 2 3 4 1 2 3 4 1 2 3 4输出样例&#xff1a; 1 4 4 2 4 4 3 4 4 #include<bits/stdc.h> using namespace std; const int N1010; int n,m,a[N][N],x[N],y[N],flag1; int main(){scanf("%d%d",&n,&m);for(int i1;i<n;i…...

web前端开发工程师的具体职责范本(合集)

web前端开发工程师的具体职责范本1 职责&#xff1a; 1.负责web前端架构的搭建&#xff0c;核心业务功能开发和核心代码编写。 2.配合产品经理&#xff0c;实现产品UI和交互方面的需求&#xff0c;持续界面优化&#xff0c;提升用户体验。 3.参与相关业务需求变更评审。 4.…...

从源程序到可执行文件的四个过程

从源程序到可执行文件的四个过程 预处理编译汇编链接 程序要运行起来&#xff0c;必须要经过四个步骤&#xff1a;预处理、编译、汇编和链接&#xff0c;如下图所示&#xff1a; -E选项&#xff1a;提示编译器执行完预处理就停下来&#xff0c;后边的编译、汇编、链接就先不执…...

C++部署学习

gcc -E src/main.c -o src/main.i gcc -S src/main.c -o src/main.s gcc -C src/main.c -o src/main.o gcc src/main.c -o exec ./exec...

linux下lazarus开发ide里 BGRAControls控件库comboBox示例

下载开发工具 ftp://ftp.freepascal.org/pub/lazarus/releases/Lazarus%20Linux%20amd64%20DEB/Lazarus%202.2.6/https://sourceforge.net/projects/lazarus/files/Lazarus%20Linux%20amd64%20DEB/Lazarus%202.2.6/ sourceforge下载可能比较慢&#xff0c;选择 下载有问题&…...

Redis学习路线(9)—— Redis的场景使用

默认做好了其它的前提&#xff0c;只专注于Redis使用 一、短信登录 在没有Redis数据库时&#xff0c;我们会基于Session实现登录&#xff08;利用令牌进行授权&#xff09;&#xff0c;是怎么实现的呢&#xff1f; &#xff08;一&#xff09;基于Session的短信登录功能 1、…...

糟了,数据库主从延迟了!

前言 在实际的生产环境中&#xff0c;由单台MySQL作为独立的数据库是完全不能满足实际需求的&#xff0c;无论是在安全性&#xff0c;高可用性以及高并发等各个方面 因此&#xff0c;一般来说都是通过集群主从复制&#xff08;Master-Slave&#xff09;的方式来同步数据&…...

VUE,子组件给父组件传递参数,props 自定义属性,ref

<template><div><!-- 子传父 --><!-- 通过父组件给子组件传递函数类型的props实现&#xff1a;子给父传递数据 --><AA :getAAname"getAAname"/><h1>AA&#xff1a;{{aaname}}</h1><!-- 通过父组件给子组件绑定一个自定…...

【Oracle系列】- Oracle数据迁移

【Oracle系列】- Oracle数据迁移 文章目录 【Oracle系列】- Oracle数据迁移一、概述二、数据迁移方案三、模拟迁移方案四、迁移步骤五、迁移方案及其实施细则5.1 exp/imp逻辑备份与恢复5.2 Storage存储迁移5.3 利用data guard迁移 一、概述 最近在做公司软件系统盘点时&#x…...

Linux环境安装MySQL(详细教程)

1、下载MySQL MySQL官网&#xff1a;MySQLhttps://www.mysql.com/ 下载社区版&#xff08;免费&#xff0c;但不提供技术支持&#xff09; 简单说明一下rpm和tar包的区别&#xff1a; tar 只是一种压缩文件格式&#xff0c;所以&#xff0c;它只是把文件压缩打包 rpm&#xf…...

GNSS终端授时方式-合集:PPS、B码、NTP、PTP、单站授时,共视授时

GNSS接收机具备授时功能&#xff0c;能够对外输出高精度的时间信息&#xff0c;并通过多种接口、多种形式进行时间信息的传递。 step by step介绍GNSS卫星导航定位基本原理&#xff0c;为什么定位需要至少4个卫星&#xff1f;这个文章的最后&#xff0c;我们介绍了为什么GNSS接…...

使用 OpenCV (C++) 进行人脸边缘提取

使用 OpenCV (C) 进行人脸边缘提取 本文将介绍如何使用 C 和 OpenCV 库来检测图像中的人脸&#xff0c;并提取这些区域的边缘。我们将首先使用 Haar级联分类器进行人脸检测&#xff0c;然后在检测到的人脸区域&#xff08;ROI - Region of Interest&#xff09;内应用 Canny 边…...

算法blog合集

https://zhuanlan.zhihu.com/p/600245782 https://zhuanlan.zhihu.com/p/696212679 https://zhuanlan.zhihu.com/p/291406172 【推荐系统】DSSM双塔召回2_pair-wise训练和推理-CSDN博客 精通推荐算法1&#xff1a;为什么需要推荐系统&#xff08;系列文章&#xff0c;建议收…...

算法篇 八大排序(冒泡 插入 选择 堆 希尔 快排 归并 计数)

目录 引言 1.冒泡排序 思路 代码实现 2.选择排序 思路 代码实现&#xff08;存在易错点&#xff09; 3.插入排序 思路 代码实现 4.希尔排序 思路 代码实现 5.堆排序 思路 代码实现 6.快速排序&#xff08;快排&#xff09; 一.三路划分 思路 代码实现 二.自…...

Java Lambda表达式深度解析:从入门到实战

简介 Lambda表达式是Java 8引入的最重要特性之一,它极大地简化了Java代码的编写方式,使函数式编程风格在Java中成为可能。本文将全面介绍Lambda表达式的概念、语法、应用场景以及与相关特性的配合使用,帮助开发者掌握这一强大的编程工具。 一、Lambda表达式基础 1.1 什么…...

网心云 OEC/OECT 笔记(1) 拆机刷入Armbian固件

目录 网心云 OEC/OECT 笔记(1) 拆机刷入Armbian固件网心云 OEC/OECT 笔记(2) 运行RKNN程序 外观 内部 PCB正面 PCB背面 PCB背面 RK3566 1Gbps PHY 配置 OEC 和 OECT(OEC-turbo) 都是基于瑞芯微 RK3566/RK3568 的网络盒子, 没有HDMI输入输出. 硬件上 OEC 和 OECT…...

Gemini开源项目DeepResearch:基于LangGraph的智能研究代理技术原理与实现

引言 在人工智能快速发展的今天&#xff0c;如何构建一个能够进行深度研究、自主学习和迭代优化的AI系统成为了技术前沿的重要课题。Gemini开源的DeepResearch一周收获7.9k Star&#xff0c;Google的开源项目Gemini DeepResearch技术通过结合LangGraph框架和Gemini大语言模型&…...

npm install 报错:npm error: ...node_modules\deasync npm error command failed

npm install 时报错如下&#xff1a; 首先尝试更换node版本&#xff0c;当前node版本20.15.0&#xff0c;更换node版本为16.17.0。再次执行npm install安装成功...

【项目实践】SMBMS(Javaweb版)(三)登出、注册、注销、修改

文章目录 登出、注册、注销、修改登出操作的实现逻辑及方式防止用户登出后可以继续访问修改密码功能实现导入jsp实现Dao层数据接口实现Service层业务接口注册Servlet 注册和注销 用户的方式导入jsp实现Dao层的数据逻辑实现Service逻辑注册Servlet 登出、注册、注销、修改 登出…...

基于nlohmann/json 实现 从C++对象转换成JSON数据格式

C对象的JSON序列化与反序列化 基于JsonCpp库实现C对象序列化与反序列化 JSON 介绍 JSON作为一种轻量级的数据交换格式&#xff0c;在Web服务和应用程序中广泛使用。 JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&#xff0c;易于人阅读…...