知识导图 知识导图
首页
iOS知识
计算机软件
  • 即时通讯网 (opens new window)
  • 开发常用网站 (opens new window)
首页
iOS知识
计算机软件
  • 即时通讯网 (opens new window)
  • 开发常用网站 (opens new window)
  • MD

  • Vue

  • C语法

  • C++语法

  • 汇编语言

    • Day1-前言
    • Day2-语言发展
    • intel-32基础语法(上)
      • 前言
      • 基本结构
        • Hello World
        • 语法结构
        • intel32程序三个段结构
        • 1、data段
        • 2、bss 段
        • 3、text段
      • 寄存器
        • 常见寄存器
      • 汇编命令
      • 变量
        • define:变量内存分配、创建(alloc)
        • 示例:输出字符串 xyz
        • reserve:变量内存释放(release)
        • 示例:申请空间并打印
      • 常量
        • 常量定义
        • 示例
        • 汇编EQU示例
        • 汇编宏示例
    • intel-32基础语法(中)
    • intel-32基础语法(下)
  • 软件编程及算法
  • 汇编语言
2023-08-16
目录

intel-32基础语法(上)

# 前言

汇编语言多种多样,不同于其他语言,汇编指令对接的是硬件,所以不同厂家,甚至相同的厂家的不同芯片都有不同的汇编指令集。

因此花时间学完所有的汇编指令显得不必要,只要对主流的汇编语法学习然后融会贯通即可。

常见学习的汇编:

1、8086汇编,intel-16,早期较为基础的汇编指令,也是入门常用的汇编学习版本,大学教材《王爽汇编xx》

2、intel-32位 ,intel的32位芯片

3、intel-64,现在主流的intel芯片版本

4、ARM-64,ARM当前主流的版本

⚠️ 注意:

本篇以C++语法对比介绍intel-32汇编指令集(无特殊说明的,下面介绍的汇编指令均为 intel-32 指令集)

# 基本结构

# Hello World

Hello World 示例对比

1、C++ 显示字符串“ Hello World”

#include <iostream> // 引入支持输入输出的库
using namespace std; // 命名空间

int main(){ // 程序开始执行入口函数
    cout << "Hello World"; // cout 输出
    return 0; 
} 

C++在线编译(菜鸟工具) (opens new window)

2、intel32汇编语言显示字符串“ Hello World”

section .text
   global _start   

_start:          
   mov  edx,len   
   mov  ecx,msg    
   mov  ebx,1      
   mov  eax,4       
   int  0x80       

   mov  eax,1      
   int  0x80     

section .data
msg db 'Hello, world!', 0xa  
len equ $ - msg    

链接:intel32汇编在线编译 (opens new window)

上面结果都是打印一个 'Hello, world!' 但是,

显然汇编语法较为复杂,下面将先从结构上介绍他们的区别。

(具体 ax、bx、cx、dx、int 等指令后面再介绍)

# 语法结构

改造C++ Hello World示例语法:

#include <iostream> // 引入支持输入输出的库
using namespace std; // 命名空间

// 1、data段: 被定义不可修改的数据常量
const string msg = "Hello World";

// 2、text段:程序代码
int main();

int main(){ // 3、程序入口函数
	
    cout << msg; // cout 输出
		// 4、程序返回
    return 0; // ret
} 

汇编代码: Hello World

; 1、data段: 被定义不可修改的数据常量
section	.data
msg db 'Hello, world!', 0xa  ;要打印的字符串
len equ $ - msg     ;字符串的长度,尾地址($)- 起始地址(msg[0])= 字符串长度 

; 2、text段:程序代码
section	.text
   global _start     ;必须为链接器(ld)声明

_start:	            ; 3、程序入口函数:告诉链接器入口点
   mov	edx,len     ;消息长度
   mov	ecx,msg     ;写消息
   mov	ebx,1       ;文件描述符 (stdout)
   mov	eax,4       ;系统调用号 (sys_write)
   int	0x80        ;调用内核

   mov	eax,1       ;系统调用号 (sys_exit)
   ; 4、程序中断 
   int	0x80        ;调用内核

对比两段代码(大概类比,方便记忆):

1、data段:

被定义不可修改的数据常量,其中数据 **msg db ***** 等同于C++ 常量 const string msg

2、text段:声明

程序代码,比如入口函数声明 global _start 等同于C++ main函数声明 int main();

3、text段:程序实现

程序入口函数

代码实现 _start: 等同于 C++ main函数 实现 int main(){ *** }

4、text段:执行中断或退出

一个程序的函数通常会在使用完后中断退出

mov eax,1 ;系统调用sys_exit,相当main函数的退出,也即是进程退出

返回:

mov	eax,4
int	0x80 ;调用内核中断

上面两句相当于函数(过程)的返回吧,
return :C++中当前函数执行完毕

5、屏幕输出

cout : c++屏幕输出

mov ebx,1 ;文件描述符 (stdout)

6、注释

intel32汇编语言 行注释以分号(;)开头

; 注释信息

C++中行注释

// 注释信息

(还有其他多种注释方法)

# intel32程序三个段结构

# 1、data段

数据(data)段被用于声明初始化的数据或常数。此数据在运行时不会更改。您可以在段中声明各种常量值,文件名或缓冲区大小等。

声明数据段的语法是-

section.data

# 2、bss 段

在bss段用于声明变量。声明bss段的语法是-

section .bss           ;未初始化的数据
   num resb 8

# 3、text段

代码段被用于保持实际的代码。该段必须以全局声明**_start**开头,该声明告诉内核程序从何处开始执行。

声明代码段的语法是-

section.text
   global _start
_start:

# 寄存器

# 常见寄存器

寄存器 16位 32位 64位
累加寄存器 accumulator AX EAX RAX
基址寄存器 base BX EBX RBX
计数寄存器 count CX ECX RCX
数据寄存器 data DX EDX RDX
堆栈基指针 Base Pointer BP EBP RBP
变址寄存器 Source Index SI ESI RSI
堆栈顶指针 Stack Pointer SP ESP RSP
指令寄存器 Instruction Pointer IP EIP RIP
AH&AL=AX(accumulator):累加寄存器 
BH&BL=BX(base):基址寄存器 
CH&CL=CX(count):计数寄存器 
DH&DL=DX(data):数据寄存器 
SP(Stack Pointer):堆栈指针寄存器 
BP(Base Pointer):基址指针寄存器 
SI(Source Index):源变址寄存器 
DI(Destination Index):目的变址寄存器 
IP(Instruction Pointer):指令指针寄存器 
CS(Code Segment)代码段寄存器 
DS(Data Segment):数据段寄存器 
SS(Stack Segment):堆栈段寄存器 
ES(Extra Segment):附加段寄存器 

⚠️

1、你会发现寄存器指令名称基本是在16位基础上衍生而来,比如32位 EAX是在16 位AX前加E,64 位 RAX是在16 位AX前加R

2、H是高位缩写,L是低位缩写。比如 AX 由AH 高位和AL低位组合,这是为兼容8位寄存器而来

3、篇头demo中下面代码什么意思?

_start:	            ; 3、程序入口函数:告诉链接器入口点
   mov	edx,len     ;消息长度
   mov	ecx,msg     ;写消息
   mov	ebx,1       ;文件描述符 (stdout)
   mov	eax,4       ;系统调用号 (sys_write)
   int	0x80        ;调用内核

拆分解释一下:

; 程序入口函数:告诉链接器入口点,加这个好理解,就是main函数
_start:	            
   
   ;比如C语法中 int a = 1; 就默认处理好了变量a的数据长度,通常是4字节,代码底层帮你申请了对应大小的空间,通常使用 alloc 分配。
   ;而在汇编中可以通过 dx 告诉编译器当前对象的需要分配的内存大小,
   mov	edx,len     ;消息长度
   
   ; int a = 1 中,赋值1给变量a的过程在汇编中可以使用 cx 这个指令
   ; mov 赋值,msg 数据, cx 指向真实的物理地址,将内容写入的地方
   mov	ecx,msg     ;写消息
   
   ;这个就是打印指令,也即是输出指令,cout、print、等等类似
   ;在bx寄存器中写入1就是输出信息
   mov	ebx,1       ;文件描述符 (stdout)
   
   ;ax中写入100(4)即是相当于函数的返回指令,告诉CPU下面函数过程要做的动作是执行后返回
   mov	eax,4       ;系统调用号 (sys_write)
   
   ;int 不是c中的int,而是 Interrupt,中断的意思,而0x80是intel-32中调用linux中系统命令,可以理解为system_call,也就是系统函数调用
   int	0x80        ;调用内核

# 汇编命令

列举几个汇编命令:

指令 作用
ORG 在程序执行的时候,会告诉编译器将这些指令转载到内存的哪个位置
JMP (jump) 跳转。类似于C中的 go to
entry() JMP 指令的跳转目的地
DB(data byte) 向文件中直接写入一个字节的指令
DW(data word) 写入一个字,两个字节
DD(data double-word) 写入两个字,4个字节
RESB(reserve byte) 与 DB相反,指定的地址清空,保留备用
RESW(reserve data double-word) 与 DW相反
MOV(move) 移动、赋值,等等动作
ADD(add) 加法指令
CMP(compare) 比较指令。
INT(Interrupt) 中断指令,用来调用BIOS中函数的指令
HLT(halt) 停止。让CPU进入待机状态,只要外部发生变化,CPU就会醒过来。
JC(jump if carry) 标志,INF调用函数后,没有错,进位标志为0,有错,进位标志为1
JNC(jump if not carry) 判断指令:进位为0就跳转
JAE(jump if above or equal) 判断指令:大于等于时跳转
JBE(jump if below or equal) 判断指令:小于等于则跳转
RET(return) 返回
XCHG (exchange) 交换指令
push 栈:进栈,或叫压栈指令
pop 栈:出栈指令
sub、sbb、dec、neg、cmp 减法指令
mul、imul 乘法指令
div、idiv 除法指令
not、and、or、xor、test 逻辑运算指令
ROL、ROR、RCL、RCR 循环移位指令
LOOP、LOOPE/LOOPZ、LOOPNE/LOOPNZ、JCXZ 循环指令
其他详见汇编指令梳理篇章

# 变量

# define:变量内存分配、创建(alloc)

NASM提供了各种定义指令来为变量保留存储空间。

define assembler指令用于分配存储空间。

在C++中:

int a = 3;  
double b = 1.345;
byte z = 22;  
char x = 'x'; 

而汇编中,变量示例:

str          DB      'xyz'
num          DW      354534
neg_num      DW      -323
big_num      DQ      119287337644
real_num     DD      1.23456
real_num2    DQ      123.456

其中内存分配的define指令:

指令 目的 储存空间
DB 定义字节 分配1个字节
DW 定义字 分配2个字节
DD 定义双字 分配4个字节
DQ 定义四字 分配8个字节
DT 定义十个字节 分配10个字节

# 示例:输出字符串 xyz

section .text
   global _start          ;链接器入口声明
        
_start:                   ;链接器执行入口
   mov  edx,3             ;定义长度
   mov  ecx,str1          ;写入数据
   mov  ebx,1             ;基地址(base)寄存器, 在内存寻址时存放基地址
   mov  eax,4             ;输出信息
   int  0x80              ;中断

   mov  eax,1             ;退出exit
   int  0x80              ;中断

section .data
str1 DB 'xyz'

结果:

xyz

1、字符的每个字节以十六进制形式存储为其ASCII值

2、数据类型转换为其等效的16位二进制数,并以十六进制数形式存储

3、使用小尾存储顺序(高字节存放内存高位,低字节在低位)

4、负数以补码形式表示

# reserve:变量内存释放(release)

相应的,上面define给对应内存分配空间,而reserve相反,是给对应内存地址清理空间,可以理解为 release

reserve指令用于为未初始化的数据保留空间。reserve指令采用单个操作数,该操作数指定要保留的空间单位数。每个define指令都有一个相关的reserve指令。

保留指令有五种基本形式-

指令 目的
RESB 保留一个字节
RESW 保留字
RESD 保留双字
RESQ 保留四字
REST 保留十个字节

# 示例:申请空间并打印

section	.text
   global _start     ;必须为链接器(ld)声明
   
_start:	            ;告诉链接器入口点
	  
	  ;1、打印固定字符串 “我的年龄:”
    mov	edx,len     ;设置数据长度
    mov	ecx,msg     ;赋值数据
    mov	ebx,1       ;打印信息
    mov	eax,4       ;执行函数标记4
    int	0x80        ;调用内核call函数执行

	  ;2、打印存储后的数据 “9”
    mov  eax,'9'
    mov  [sum], eax ;将数据9存储到申请的内存中
    
    mov	edx,1     
    mov	ecx,sum  ;从 sum中取数据存入
    mov	ebx,1       
    mov	eax,4      
    int	0x80        
		
		;3、退出
    mov	eax,1       ;退出进程sys_exit
    int	0x80        ;调用内核call函数执行退出

section	.data
msg db '我的年龄:',0xA,0xD   ;换行 0xA,0xD 。要打印的字符串
len equ $ - msg     ;字符串的长度

segment .bss
sum resb 1  ;申请1字节空间

结果

我的年龄:
9

# 常量

在C++中我们使用过 const、#define 等 定义一个常量,汇编中类似

# 常量定义

intel 32汇编指令 C++ 含义
EQU (equ) const (比如:const int a = 1;) 不可变常量赋值
%assign static 静态变量,可修改
%define #define 单行宏,代码替换
%macro - %endmacro 多行宏,代码替换

多行宏:

%macro  宏名称 参数数量                      
        push    ebp                
        mov     ebp,esp                
        sub     esp,%1               
%endmacro

指令区分大小写

# 示例

1、C++示例:

#include <iostream>
using namespace std;

// 常量
const int i_x = 3;
// 宏
#define s_y "hello"
// 静态变量
static double d_z = 1.20;

int main()
{
    cout << "不可修改 i_x = " << i_x << endl;
    cout << "不可修改 s_y = " << s_y << endl;
  
    d_z = d_z + 3.14;
    cout << "可修改 d_z = " << d_z << endl;
}

结果

不可修改 i_x = 3
不可修改 s_y = hello
可修改 d_z = 4.34

# 汇编EQU示例

将通用指令固定数据定义为常量,方便使用

sys_exit  	equ 1 ;退出
sys_write 	equ 4 ;函数过程写
sdt_in      equ 0 ;输入
sdt_out    	equ 1 ;输出
sys_call    equ 0x80 ;调用

section  .text
   global _start    
        
_start:            
   mov eax, sys_write         
   mov ebx, sdt_out         
   mov ecx, msg1         
   mov edx, len1 
   int sys_call                
          
   mov eax,sys_exit   
   int sys_call     

section  .data
msg1 db '我是第一行信息!',0xA,0xD   ;换行 0xA,0xD
len1 equ $ - msg1                        

# 汇编宏示例

将上面示例使用宏修改:

;使用字符串变量
%macro  set_string 1                      
        mov ecx, %1          
        mov edx, len1                 
%endmacro

;调用并输出
%macro  run_out 0                      
        mov eax, 4         
         mov ebx, 1 
         int 0x80               
%endmacro

;退出
%macro  return 0                      
        mov eax,1   
         int 0x80              
%endmacro

section  .text
   global _start    
        
_start:      

   set_string msg1 
   run_out
                  
          
   return     

section  .data
msg1 db '使用宏修改!'
len1 equ $ - msg1 

退出的汇编代码直接使用 return 替换,这样更容易记忆。

Day2-语言发展
intel-32基础语法(中)

← Day2-语言发展 intel-32基础语法(中)→

Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式