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

ARM汇编常见伪指令及其用法示例

伪指令不是指令,伪指令和指令的根本区别是经过编译后会不会生成机器码。 伪指令的意义在于指导编译过程。 伪指令是和具体的编译器相关的,我们使用gnu工具链,因此学习gnu环境下的汇编伪指令。

在 ARM 汇编中,伪指令(Pseudoinstruction)由汇编器解析,用于辅助程序编写(如定义数据、划分段、设置对齐等),不直接生成机器码。以下是常用伪指令及其用法示例:

一、数据定义伪指令

用于在内存中定义数据(如整数、字符串、数组等)。

1. .byte

定义8 位整数(1 字节),可跟多个值,用逗号分隔。

.data
byte1: .byte 0x10          ; 定义1字节数据0x10
byte2: .byte 0x20, 0x30    ; 连续定义两个字节:0x20、0x30

2. .half / .short

定义16 位整数(2 字节),两者功能相同。

.data
half1: .half 0x1234        ; 定义2字节数据0x1234
half2: .short 0x5678       ; 等价于.half,定义0x5678

3. .word / .int

定义32 位整数(4 字节),ARM 架构中最常用的数据定义伪指令。

.data
word1: .word 0x12345678    ; 定义4字节数据0x12345678
array: .word 1, 2, 3       ; 定义32位整数数组:1、2、3(占12字节)

4. .quad / .long

定义64 位整数(8 字节),适用于支持 64 位的 ARM 架构(如 ARMv8)。

.data
quad1: .quad 0x1122334455667788  ; 定义8字节数据

5. .asciz / .ascii

  • .asciz:定义以 null 结尾的字符串(C 风格字符串,自动添加\0)。
  • .ascii:定义字符串,不自动添加 null 结尾
.data
str1: .asciz "Hello"       ; 存储 'H','e','l','l','o','\0'(6字节)
str2: .ascii "ARM"         ; 存储 'A','R','M'(3字节,无结束符)

6. .space / .fill

  • .space n:分配n 字节的未初始化空间
  • .fill n, size, val:重复填充 n 次,每次填充 size 字节,值为 val(size 通常为 1、2、4)。
.data
buf: .space 100            ; 分配100字节缓冲区
arr: .fill 5, 4, 0x1234    ; 填充5个4字节数据,每个值为0x1234(共20字节)

更多待补充

数据只能在.data段下定义吗? 

在 ARM 汇编中,数据并非只能在.data段中定义,不同类型的数据适合放在不同的段中,常见的段包括.data.bss.rodata等,各自有不同的用途和特性。

1. .data段:初始化的读写数据

  • 用途:存放已初始化且需要修改的数据(如变量、数组等)。
  • 特点:数据会被存储在可执行文件中,加载到内存后可读写。
  • 示例
    .data
    count: .word 100         ; 初始化的整数(可修改)
    buffer: .byte 0x01, 0x02 ; 初始化的字节数组(可修改)
    

2. .bss段:未初始化或零初始化数据

  • 用途:存放未初始化初始值为 0的数据(如缓冲区、大数组等)。
  • 特点
    • 不占用可执行文件的空间(仅记录需要分配的大小)。
    • 程序加载时由系统自动初始化为 0。
    • 适合存储大容量数据(节省文件大小)。
  • 示例
    .bss
    big_buffer: .space 1024  ; 1024字节未初始化缓冲区(自动初始化为0)
    temp_var: .word 0        ; 初始值为0的变量(等效于未初始化)
    

3. .rodata段:只读数据

  • 用途:存放初始化后不需要修改的数据(如常量、字符串字面量等)。
  • 特点
    • 数据会被存储在可执行文件中。
    • 加载到内存后处于只读区域(修改会触发错误)。
    • 提高安全性,适合保护常量数据。
  • 示例
    .rodata
    PI: .word 0x4048F5C3     ; 浮点数π的32位表示(只读)
    msg: .asciz "Hello"      ; 只读字符串(不可修改)
    

4. 自定义段(.section)

通过.section伪指令可创建自定义段,用于特殊用途(如硬件相关数据、特定内存区域的数据等)。

示例:定义一个用于硬件寄存器映射的数据段

.section .hw_regs, "aw"  ; "a"=可分配, "w"=可写
uart_base: .word 0x12340000  ; UART寄存器基地址(自定义段数据)

总结:如何选择段?

数据类型推荐段原因
已初始化且需要修改.data可读写,数据存储在文件中
未初始化或初始值为 0.bss不占文件空间,自动初始化为 0
初始化后不需要修改.rodata只读保护,防止意外修改
特殊用途(如硬件相关)自定义段灵活控制段属性和内存布局

合理划分数据段可以优化程序的内存使用效率,并提高代码的安全性和可维护性。

如何访问数据

用伪指令定义数据后,怎么访问这个数据?

在 ARM 汇编中,访问用伪指令定义的数据通常需要通过标号(Label) 获取数据地址,再结合加载 / 存储指令(如LDRSTR)访问数据。具体方法取决于数据的类型和位置,以下是常见场景及示例:

一、访问数据段中的初始化数据

假设已用伪指令在.data段定义数据,通过标号获取地址后,用LDR(加载)指令读取数据到寄存器。

示例 1:访问 32 位整数(.word 定义)

.data
; 用伪指令定义数据并标记标号
num: .word 0x12345678    ; 32位数据,地址由标号num标识.text
.global main
main:; 步骤1:将数据地址加载到寄存器(用LDR =标号)LDR R0, =num         ; R0 = num的地址(0x...); 步骤2:从地址加载数据到寄存器(用LDR 寄存器, [地址寄存器])LDR R1, [R0]         ; R1 = 内存[R0] = 0x12345678; 后续操作(例如修改数据后存回)ADD R1, R1, #1       ; R1 = 0x12345679STR R1, [R0]         ; 内存[R0] = R1(更新数据); 程序结束MOV PC, LR

示例 2:访问字符串(.asciz 定义)

字符串本质是字节数组,可通过地址逐个访问字符:

.data
str: .asciz "Hello"      ; 字符串:'H','e','l','l','o','\0'.text
main:LDR R0, =str         ; R0 = 字符串首地址; 访问第一个字符(H的ASCII码为0x48)LDRB R1, [R0]        ; R1 = 0x48(LDRB:加载字节,自动零扩展); 访问第二个字符(e的ASCII码为0x65)LDRB R2, [R0, #1]    ; R2 = 0x65(地址偏移+1字节)MOV PC, LR

二、访问数组数据(多个连续元素)

对于.word.byte等定义的数组,通过地址 + 偏移量访问元素:

.data
arr: .word 10, 20, 30, 40  ; 32位整数数组(每个元素4字节).text
main:LDR R0, =arr           ; R0 = 数组首地址; 访问第1个元素(索引0):10LDR R1, [R0]           ; R1 = 10; 访问第2个元素(索引1):20(偏移4字节)LDR R2, [R0, #4]       ; R2 = 20; 访问第3个元素(索引2):30(用寄存器偏移)MOV R3, #8LDR R4, [R0, R3]       ; R4 = 30MOV PC, LR

三、访问.bss 段的未初始化数据

.bss段数据仅分配空间(初始值为 0),访问方式与.data段相同:

.bss
buf: .space 16            ; 16字节未初始化缓冲区.text
main:LDR R0, =buf          ; R0 = 缓冲区首地址; 向缓冲区第1个字节写入0x55MOV R1, #0x55STRB R1, [R0]         ; STRB:存储字节; 向缓冲区第5个字节(偏移4)写入0xAAMOV R2, #0xAASTRB R2, [R0, #4]MOV PC, LR

四、关键指令总结

指令用途示例
LDR Rn, =label将标号地址加载到寄存器LDR R0, =num
LDR Rd, [Rn]从 Rn 指向的地址加载 32 位数据LDR R1, [R0]
LDRB Rd, [Rn]从 Rn 指向的地址加载 8 位数据LDRB R1, [R0]
LDRH Rd, [Rn]从 Rn 指向的地址加载 16 位数据LDRH R1, [R0]
STR Rd, [Rn]将 32 位数据存储到 Rn 指向的地址STR R1, [R0]
STRB Rd, [Rn]将 8 位数据存储到 Rn 指向的地址STRB R1, [R0]
STRH Rd, [Rn]将 16 位数据存储到 Rn 指向的地址STRH R1, [R0]

核心逻辑

  1. 获取地址:用 LDR Rn, =label 将数据标号的地址加载到寄存器(Rn)。
  2. 访问数据:根据数据宽度(8/16/32 位),用 LDR/STR 及其变体(LDRB/STRB等),通过地址寄存器(Rn)读写内存。

这种方式依赖汇编器对地址的解析和链接器的地址分配,最终实现对伪指令定义数据的访问。

二、段定义伪指令

用于划分程序的不同段(代码段、数据段等),便于内存管理。

1. .text

定义代码段(存放可执行指令),是程序的默认段。

.text               ; 进入代码段
start:MOV R0, #0      ; 指令存放于此段

2. .data

定义数据段(存放初始化的数据)。

.data               ; 进入数据段
var: .word 0x1234   ; 初始化数据存放于此段

3. .bss

定义未初始化数据段(仅分配空间,初始值为 0),节省可执行文件大小。

.bss                ; 进入未初始化数据段
buf: .space 200     ; 200字节缓冲区(初始为0)

4. .section

自定义段(灵活划分特殊用途的段,如中断向量表)。

.section .vector, "a"  ; 定义名为.vector的段,"a"表示可分配
reset:B start            ; 中断向量表中的复位向量

更多待补充

三、符号与地址伪指令

用于声明符号可见性、加载地址等。

1. .global / .extern

  • .global sym:声明 sym 为全局符号(可被其他文件引用)。
  • .extern sym:声明 sym 为外部符号(在其他文件中定义)。
.global main         ; 声明main为全局符号(供链接器识别)
.extern printf       ; 声明printf为外部符号(来自C库)

2. .equ

定义符号常量(类似 C 中的#define),便于代码维护。

.equ MAX_LEN, 100    ; 定义常量MAX_LEN=100
.equ PI, 3.14        ; 也可定义浮点数(汇编器支持时).text
start:MOV R1, #MAX_LEN ; 使用常量

3. =label(地址加载伪指令)

配合LDR指令,将标号的绝对地址加载到寄存器(实际会被汇编器转换为合适的指令)。

.data
msg: .asciz "Hello".text
main:LDR R0, =msg     ; 将msg的地址加载到R0(等价于加载绝对地址)LDR R1, =0x12345678  ; 加载32位立即数(超出MOV指令范围时用)

更多待补充

四、对齐与定位伪指令 

用于控制数据或指令在内存中的对齐方式(提高访问效率)。

1. .align n

使当前地址对齐到2^n字节边界(n 通常为 0~3,对应 1、2、4、8 字节对齐)。

.data
.align 2            ; 对齐到4字节边界(2^2=4)
val: .word 0x1234   ; val的地址必为4的倍数

2. .org addr

强制将当前地址设置为addr(常用于固定地址初始化,如硬件寄存器)。

.org 0x40000000     ; 强制当前地址为0x40000000(假设为UART寄存器地址)
uart_tx: .word 0    ; uart_tx的地址固定为0x40000000

更多待补充

五、其他常用伪指令

1. .end

标记程序结束,汇编器遇到此指令后停止处理。

.text
start:MOV PC, LR
.end                ; 程序结束

2. .include

包含其他汇编文件(类似 C 的#include),便于代码复用。

.include "common.s"  ; 包含common.s文件中的代码

3. .thumb / .arm

切换指令集:.thumb进入 Thumb 模式(16/32 位指令),.arm进入 ARM 模式(32 位指令)。

.arm                ; 使用ARM指令集
MOV R0, #1.thumb              ; 切换到Thumb指令集
MOV R1, #2

更多待补充 

总结 

伪指令是 ARM 汇编的 “辅助工具”,核心作用是:

  • 定义数据(.word.asciz等);
  • 划分内存段(.text.data等);
  • 控制符号与地址(.global=label等);
  • 优化内存对齐(.align)。

灵活使用伪指令可使汇编代码更清晰、易维护,同时适配不同的硬件和内存布局需求。

http://www.dtcms.com/a/296970.html

相关文章:

  • IntelliJ IDEA中管理多版本Git子模块的完整指南
  • 智慧工厂网络升级:新型 SD-WAN 技术架构与应用解析
  • 商场导航软件:3D+AI 基于Deepseek 模型的意图识别技术解析
  • BacNet 是什么?跟 LoRaWAN 的关系是什么?
  • 将JS字节流转化为对象
  • 西安交通大学XJTU 通信/信息工程大三和部分大四 实验和课程答案
  • C++哪些运算符不能被重载?
  • kubernetes集群中部署CoreDNS服务
  • day46day47 通道注意力
  • 一种基于单片机控制的太阳能电池板系统设计
  • 集训Demo6
  • 挖掘录屏宝藏:Screenity 深度解析与使用指南
  • 《计算机网络》实验报告八 加密、数字签名与证书
  • pytest测试框架
  • AUTOSAR进阶图解==>AUTOSAR_SWS_BSWGeneral
  • 【Vue学习笔记】状态管理:Pinia 与 Vuex 的使用方法与对比【附有完整案例】
  • 网络安全入门第一课:信息收集实战手册(2)
  • C语言-指针[变量指针与指针变量]
  • Java 集合框架之----ArrayList
  • Effective Modern C++ 条款16:保证const成员函数的线程安全性
  • 网址收集总结
  • 【硬件-笔试面试题】硬件/电子工程师,笔试面试题-17,(知识点:PCB布线,传输线阻抗影响因素)
  • 第一二章笔记
  • [ComfyUI] --ComfyUI 是什么?比 Stable Diffusion WebUI 强在哪?
  • 【STM32项目】智能台灯
  • 无人机保养指南
  • 深入解析Hadoop NameNode的Full GC问题、堆外内存泄漏及元数据分治策略
  • 软件测试的分类
  • C++实现精确延时的方法
  • 季逸超:Manus的上下文工程启示