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.pswp.cn/pingmian/90235.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/90235.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法调试技巧

引言算法调试常比编写更耗时,尤其是动态规划、递归等逻辑复杂的代码。本文分享一套系统化的调试方法,帮助快速定位问题。一、调试前的准备代码格式化使用统一缩进(4 空格)和命名规范,避免因格式混乱导致的逻辑误读。边…

每日功能分享|让观看者体验“无缝链接”观看的功能——视频自动续播功能

你是否遇到过这样的困扰——看到一半的视频,关闭后却忘记进度,再打开时需要手动拖拽寻找上次的观看位置?如今,“视频自动续播功能”完美解决了这一痛点!无论是在线教育课程、影视剧集还是企业内部员工培训,…

AWS: 云上侦探手册,七步排查ALB与EC2连接疑云

今天,咱们来聊一个对于许多刚接触AWS的运维同学来说,既常见又有点头疼的话题:如何优雅地排查和解决AWS上ALB(Application Load Balancer)暴露EC2服务时遇到的种种疑难杂症。 最近,我刚帮一个朋友解决了类似…

EIDE 创建基于STM32-HD的项目快速创建流程

EIDE 创建基于STM32-HD的项目流程芯片系列定义宏Flash 大小RAM 大小STM32F10x_HD#define STM32F10X_HD256KB~512KB48KB~64KBSTM32F10x_MD#define STM32F10X_MD64KB~128KB20KBSTM32F10x_LD#define STM32F10X_LD16KB~32KB4KB~10KB 新建项目远程仓库获取裸机开发程序STM(意法半导体…

使用 QLExpress 构建灵活可扩展的业务规则引擎

目录 一、什么是 QLExpress? 二、推荐系统中的规则脚本应用 1 场景描述 2 推荐规则脚本(QLExpress) 3 系统实现 4 执行结果 5 推荐系统应用建议 三、风控系统中的规则判定 1 场景描述 2 风控规则脚本(QLExpress&#xff…

【硬件-笔试面试题】硬件/电子工程师,笔试面试题-13,(知识点:DC-DC电源,相位裕度,增益裕度)

目录 1、题目 2、解答 相位裕度 增益裕度 3、相关知识点 一、波特图 二、相位裕度 三、增益裕度 四、在 DC - DC 电源中的应用 【硬件-笔试面试题】硬件/电子工程师,笔试面试题汇总版,持续更新学习,加油!!&a…

学生信息管理系统 - HTML实现增删改查

学生信息管理系统 - HTML实现增删改查 效果图 代码 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><titl…

Agile简介

Agile&#xff08;敏捷&#xff09;是一种软件开发方法论&#xff0c;核心是通过快速迭代、灵活响应变化&#xff0c;解决传统软件开发中周期长、需求变更困难等问题&#xff0c;最终高效交付符合用户实际需求的产品。 一、Agile 的起源&#xff1a;为什么需要敏捷&#xff1f;…

关于 URL 中 “+“ 号变成空格的问题

当你在 URL 中传递参数时&#xff0c;加号 () 会被自动转换为空格&#xff0c;这是 URL 编码的标准行为。问题原因在 URL 中&#xff1a;空格会被编码为 号当 URL 被解码时&#xff0c; 号又会被转换回空格这会导致原始数据中的 号丢失解决方案你需要对参数值进行正确的 URL …

综合实验(2)

文章目录 目录 文章目录 前言 OSPF运行在GRE隧道概述 典型应用场景 OSPF over GRE 配置 总结 前言 OSPF运行在GRE隧道概述 GRE&#xff08;Generic Routing Encapsulation&#xff09;隧道是一种通过封装原始数据包在IP网络中创建虚拟点对点连接的隧道技术。OSPF&#xff08;…

【应急响应工具教程】司稽(Whoamifuck):纯Shell打造的Linux应急响应利器

1、工具简介司稽&#xff08;Whoamifuck或Chief-Inspector,简称"who"&#xff09;&#xff0c;永恒之锋发布的第一款开源工具&#xff0c;这是一款由shell编写的Linux应急响应脚本&#xff0c;能对基本的检查项进行输出和分析&#xff0c;并支持一些扩展的特色功能。…

新手操作steam搬砖项目,应该如何快速起步

大家好哦&#xff0c;我是阿阳&#xff0c;今天继续给大家分享一些steam搬砖的知识。在我们操作过程中&#xff0c;问题问得最多的就是&#xff0c;新手应该怎么做&#xff1f;首先&#xff0c;那我们得先来了解-下,什么是steam搬砖,它的项目原理是什么&#xff0c;其次针对于这…

rt-thread加一个库

背景 官方软件包里没有的 可以以库或组件形式加入 本次仅为了验证&#xff0c;加到库 过程 下载源码 假设为 lib_demo 自己的板子目录为bsp/stm32 代码目录结构 bsp/stm32librarieslib_demo //新建文件夹src //把lib_demo里源码文件放进来inc //把lib_demo里头文件放进来SConsc…

c++深拷贝和浅拷贝

一、浅拷贝本质&#xff1a;简单地复制对象的成员值。如果成员里有指针&#xff0c;新对象和原对象的指针会指向同一块内存。比如你有对象 A&#xff0c;里面指针 p 指向堆内存 0x123&#xff1b;用 A 拷贝出对象 B&#xff0c;B 的指针 p 也指向 0x123。问题&#xff1a;若其中…

NineData新增SQL Server到MySQL复制链路,高效助力异构数据库迁移

在实际的数据库迁移工作中&#xff0c;异构库之间的迁移常常被视为一项“高风险、高工作量、高复杂度”的挑战任务。这不仅是一次数据库切换&#xff0c;更是对系统稳定性、数据一致性、业务连续性和技术团队耐力的全方位考验。为解决企业在异构数据库迁移中的痛点&#xff0c;…

字符串和对象的深拷贝和浅拷贝

字符串和对象的深拷贝和浅拷贝【一】基本介绍【1】浅拷贝【2】深拷贝【二】字符串的拷贝【1】字符串的 “浅拷贝”【2】字符串的 “深拷贝”【三】对象的拷贝【1】浅拷贝&#xff08;Shallow Copy&#xff09;【2】深拷贝&#xff08;Deep Copy&#xff09;【四】字符串和对象拷…

4.5 优化器中常见的梯度下降算法

梯度下降算法&#xff08;Gradient Descent&#xff09;的数学公式可以通过以下步骤严格表达&#xff1a;1. 基本梯度下降&#xff08;Batch Gradient Descent&#xff09; 目标&#xff1a;最小化损失函数L(θ)\mathcal{L}(\theta)L(θ)&#xff0c;其中 θ\thetaθ是模型参数…

AM1.5G AAA稳态太阳光模拟器特点

光谱匹配度AM1.5G AAA稳态太阳光模拟器的光谱分布严格匹配国际标准IEC 60904-9中的AM1.5G光谱&#xff08;波长范围300-4000nm&#xff09;&#xff0c;确保与自然太阳光的偏差在25%以内&#xff08;AAA级标准&#xff09;。光谱匹配度通过精密滤光片和氙灯或LED组合光源实现&a…

OSPF开放式最短路径优先

1OSPF简介&#xff08;1&#xff09;OSPF英文全称Open Shortest Path First (开放式最短路径优先)&#xff08;2&#xff09;OSPF是IETF 开发的一种链路状态路由协议&#xff0c;使用基于带宽的度量值。&#xff08;3&#xff09;OSPF采用SPF算法计算路由&#xff0c;从算法上保…

Lua(模块与包)

Lua 模块的基本概念Lua 中的模块是一个由函数、变量组成的代码库&#xff0c;通常保存在独立的 .lua 文件中。模块通过 return 语句导出其内容&#xff0c;供其他脚本调用。模块化设计可以提高代码复用性&#xff0c;便于管理。创建模块模块通常以 .lua 文件形式存在&#xff0…