• ARM官方汇编与ARM GNU汇编中的伪操作


    以下内容源于网络资源的学习与整理,如有侵权请告知删除。

    参考博客

    (1)嵌入式Linux ARM汇编

    (2)GNU ARM 汇编基础 - wanli1024 - 博客园

    (3)GNU ARM 汇编简介_niepangu的博客-CSDN博客

    (4)ARM编译器(一)ARM汇编与ARM GNU汇编 | 骏的世界

    (5)GNU ARM汇编伪操作(Directives) 命令集 - Leo Chin - 博客园

    (6)GNU ARM指令伪操作_Volatile_xian的博客-CSDN博客

    前言

    汇编器用于将汇编语言文件转化为二进制的目标文件,目前常用的ARM汇编器有以下两种:

    (1)ARM公司的汇编器,适合在Windows平台下使用。

    (2)GNU工具链中的汇编器,适合于Linux开发平台。

    不同的汇编器对汇编语言的语法要求不一样。GNU汇编器是GNU工具集的一部分,针对的是多种处理器架构,这意味着GNU汇编器的语法不同于ARM公司汇编器的语法。但是实际上两者的汇编指令基本相同(见博文ARM官方汇编指令),只是伪操作不同,因此文本主要说明ARM官方汇编器和GNU汇编器中的伪操作。

    一、ARM官方汇编器中的伪操作

    1、AREA

    (1)格式:AREA 段名 属性1,属性2,…,属性n

    (2)作用:定义一个代码段或数据段。

    (3)特别说明:

    • 段名若以数字开头,则该段名需用“|”括起来,比如“ |1_test| ”。
    • 属性字段表示这个段的相关属性,多个属性用逗号分隔。

    属性

    描述

    CODE

    用于定义代码段,默认为READONLY。

    DATA

    用于定义数据段,默认为READWRITE。

    READONLY

    指定本段为只读,代码段默认为READONLY。

    READWRITE

    指定本段为可读可写,数据段的默认属性为READWRITE。 

    ALIGN

    使用方式为ALIGN表达式。可执行文件的代码段和数据段默认是按字对齐的,表达式的取值范围为0~31,相应的对齐方式为2表达式次方。

    COMMON

    该属性定义一个通用的段,不包含任何的用户代码和数据。各源文件中同名的COMMON段共享同一段存储单元。

    (4)代码示例:

    AREA Init,CODE,READONLY ;定义了一个代码段,段名为Init,属性为只读。

    2、ALIGN

    (1)格式:ALIGN{表达式 { ,偏移量}}

    (2)作用:通过添加填充字节的方式,使当前位置满足一定的对其方式。

    (3)特别说明:表达式的值用于指定对齐方式,可能的取值为2的幂,如1、2、4、8、16等。若未指定表达式,则将当前位置对齐到下一个字的位置。偏移量也为一个数字表达式,若使用该字段,则当前位置的对齐方式为:2的表达式次幂+偏移量。另外注意,在AREA中使用和单独使用ALIGN时,格式与对齐的计算方式不一样(见代码示例)。

    (4)代码示例:

    1. @例子1
    2. AREA Init,CODE,READONLY,ALIGN=3  @指定后面的指令为8字节对齐
    3. ;.... 
    4. END
    5. @例子2
    6. ALIGN 4 表示4字节地址对齐

    3、CODE16、CODE32       

    (1)格式:CODE16(或CODE32 )

    (2)作用:CODE16(或CODE32 )伪指令通知编译器,其后的指令序列为16位的Thumb指令(或32位的ARM指令)。

    (3)特别说明:若在汇编源程序中同时包含ARM指令和Thumb指令时,可用CODE16伪指令通知编译器其后的指令序列为16位的Thumb指令,用CODE32伪指令通知编译器其后的指令序列为32位的ARM指令。因此,在使用ARM指令和Thumb指令混合编程的代码里,可用这两条伪指令进行切换,但注意它们只通知编译器其后指令的类型,并不能对处理器进行状态的切换。            

    (4)代码示例:

    1. AREA Init,CODE,READONLY            
    2. ;....      
    3. CODE32               @通知编译器其后的指令为32位的ARM指令
    4. LDR R0,=NEXT+1     @将跳转地址放入寄存器R0      
    5. BX R0                @程序跳转到新的位置执行,并将处理器切换到Thumb工作状态
    6. ;....     
    7. CODE16               @通知编译器其后的指令为16位的Thumb指令
    8. NEXT LDR R3,=0x3FF            
    9. ;....     
    10. END                  @程序结束

    4、ENTRY

    (1)格式:ENTRY

    (2)作用:用于指定汇编程序的入口点。

    (3)特别说明:在一个完整的汇编程序中至少要有一个ENTRY,也可以有多个(此时程序的真正入口点由链接器指定),但在一个源文件里最多只能有一个ENTRY(可以没有)。

    (4)代码示例:

    1. AREA Init,CODE,READONLY
    2. ENTRY ;指定应用程序的入口点
    3. ;.....

    5、END       

    (1)格式:END

    (2)作用:用于通知编译器已经到了源程序的结尾。

    (3)代码示例:

    1. AREA Init,CODE,READONLY
    2. ;......
    3. END @指定应用程序的结尾

    6、EXPORT

    (1)格式:EXPORT 标号 [,WEAK]

    (2)作用:声明一个全局标号,其他源文件可以使用这个标号。WEAK表示碰上其他同名标号时,其他标号优先。

    7、IMPORT

    (1)格式:IMPORT 标号,[,WEAK]

    (2)作用:表示当前文件中引用的标号在其他源文件中。WEAK表示找不到该标号时也不报错,一般将该标号置为0,如果是B 或BL指令用到该标号,该指令置为nop。

    (3)特别说明:该标号会加入到当前源文件的符号表中。

    8、EXTERN

    (1)格式:EXTERN 标号,[,WEAK]

    (2)作用:和IMPORT一样,但如果当前文件没有引用该标号,该标号不会加入到当前源文件的符号表中。

    9、GET(或INCLUDE)

    (1)格式:INCLUDE pathname

    (2)作用:将一个源文件包含到当前的源文件中。

    10、EQU

    (1)格式:name EQU expression

    (2)作用:对一个常量标号赋值,等同于C语言中用#define定义一个常量

    (3)特别说明:name是符号名, expression是寄存器相关或者程序相关的固定值。

    (4)代码示例:

    num EQU 2  ;为符号num赋值为2

    11、SPCAE

    (1)格式: {label} SPACE expr

    (2)作用:用于分配一片连续内存单元,并用0初始化。

    (3)特别说明:SPACE可用%代替;label是一个标号, 可选;expr是分配的内存字节数。

    (4)代码示例:

    stack SPACE 100 ;分配100个字节内存单元,并用0初始化。标号stack是这片空间的起始地址

    12、DCB

    (1)格式:{label} DCB expr {,expr}

    (2)作用:用于分配段字节内存单元,并用伪操作中的expr初始化。

    (3)特别说明:label是一个标号,可选;expr可以是-128~255的数值或者字符串。

    (4)代码示例:

    string DCB "HELLO"  ;为HELLO字符串分配空间,string是这块空间的起始地址

    13、DCD(或DCDU)

    (1)格式: {label} DCD expr, {,expr}

    (2)作用:用于分配段字内存单元(分配的内存都是字对齐,DCDU并不严格字对齐),并用伪操作中的expr初始化。

    (3)特别说明:label是一个标号,可选,表示这块内存单元的首地址。expr是数字表达式或程序中的标号。DCD 可用 & 代替。

    (4)代码示例:

    data DCD 1,2,3,4     ;分配字对齐的字单元空间,并初始化为1234

    ​14、更多伪操作

    伪操作

    语法格式

    作用

    GBLA

    GBLA  Varible

    声明一个全局的算术变量,并将其初始化为0

    GBLL

    GBLL  Varible

    声明一个全局的逻辑变量,并将其初始化成{FALSE}

    GBLS

    GBLS  Varible

    声明一个全局的字符串变量,并将其初始化成空串

    LCLA

    LCLA  Varible

    声明一个局部的算术变量,并将其初始化为0

    LCLL

    LCLL  Varible

    声明一个局部的逻辑变量,并将其初始化成{FALSE}

    LCLS

    LCLS  Varible

    声明一个局部的字符串变量,并将其初始化成空串

    SETA

    SETA  Varible  expr

    给一个全局或局部算术变量赋值

    SETL

    SETL  Varible  expr

    给一个全局或局部逻辑变量赋值

    SETS

    SETS  Varible  expr

    给一个全局或局部字符串变量赋值

    RLIST

    name LIST {list of registers}

    为一个通用寄存器列表定义名称

    CN

    name CN expr

    为一个协处理器的寄存器定义名称

    CP

    name CP expr

    为一个协处理器定义名称

    DN/SN

    name DN/SN expr

    DN/SN为一个双精度/单精度的VFP寄存器定义名称

    FN

    name FN  expr

    为一个FPA浮点寄存器定义名称

    LTORG

    LTONG

    声明一个数据缓冲池(文字池)的开始

    MAP

    MAP expr {, base-register}

    定义一个结构化的内存表(storage map)的首地址

    FIELD

    {label}  FIELF  expr

    定义一个结构化内存表中的数据域

    SPACE

    {label}  SPACE  expr

    分配一块连续内存单元,并用0初始化

    DCB

    {label}  DCB  expr {,expr}..

    分配一块字节内存单元,并用expr初始化

    DCD/ DCDU

    {label} DCD/DCDU expr {,expr}…

    分配一块字内存单元, 并用expr初始化

    DCDO

    {label}  DCDO  expr {,expr}…

    分配一块字对齐的字内存单元, 并用expr初始化

    DCFD/DCFDU

    {label}   DCFD{U}   fpliteral

    ,{,fpliteral}…

    为双精度的浮点数分配字对齐的内存单元

    DCFS/DCFSU

    {label}    DCFS{U}  fpliteral

    ,{,fpliteral}…

    为单精度的浮点数分配字对齐的内存单元

    DCI

    {label} DCI expr, {expr}…

    ARM代码分配一段字对齐的内存单元,填充expr(二进制指令码),THUMB代码中,分配一段半字对齐的半字内存单元。

    DCQ/ DCQU

    {label} DCQ{U}  {-} literal,

    {, {-} literal}…

    分配一段以双字(8个字节)为单位的内存

    DCW/DCWU

    {label} DCW{U}  {-} literal,

    {, {-} literal}…

    DCW用于分配一段半字对齐的半字内存单元

    二、ARM GNU汇编器中的伪操作

    (1)ARM GNU汇编中的伪操作,一般都以“.”开头。

    (2)ARM GNU汇编中的伪操作,可以分为以下几类。

    • 数据定义伪操作
    • 符号定义伪操作
    • 代码控制伪操作
    • 预定义控制伪操作
    • 其他伪操作

    1、数据定义伪操作

    常见的数据定义伪操作如表格所示。

    数据定义伪操作格式作用举例与备注

    .byte

    .byte expr {,expr}…

    分配一段字节内存单元,并用expr初始化。即定义单字节数据。

    .byte 1,2,0b01,0x34,072,'s'

    .hword/.short

    .hword expr {,expr}…

    分配一段半字内存单元,并用expr初始化。即定义双字节数据

    .short 0x1234

    .ascii

    .ascii expr {,expr}…

    定义字符串expr(不会自动增加/0结束符,需要自行添加结尾字符'\0'。)

    .ascii "welcome\0"

    .asciz/.string

    .asciz expr {,expr}…

    定义字符串expr(会自动增加/0为结束符)

    .string "abcd", "efgh", "hello!"

    .asciz "qwer", "sun", "world!"

    .floar/.single

    .float expr {,expr}…

    分配一段字内存单元,并用32位IEEE单精度浮点数expr初始化内存单元

    .double

    .doubel expr {,expr}…

    定义64bit IEEE浮点数expr

    .word/.long/.int

    .word expr {,expr}…

    分配一段字内存单元,并用expr初始化

    .long 0x12345678

    .fill

    .fill  repeat {,size} {,value}

    分配一段字节内存单元,用size个字节value填充repeat次(size默认为1,value默认为0)

    .zero

    .zero size

    分配一段size个字节的内存单元,并用0填充内存

    .space/.skip

    .space size, {,value}

    分配一段size个字节的内存单元,用value将内存初始化

    .quad.qua expr {,expr}…分配一段双字内存单元,并用expr初始化双字内存单元.quad 0x1234567890abcd
    .octa.octa expr{,expr}…分配一段四字内存单元,并用expr初始化四字内存单元
    .ltorg.ltorg声明一个数据缓冲池(literal pool),即在当前段的当前地址(字对齐)产生一个文字池
    .int.int expressions定义一个整型。

    2、符号定义伪操作

    常见的符号定义伪操作如表格所示。

    符号定义伪操作格式作用举例与备注
    .equ / .set.equ symbol,expr将symbol定义为expr.equ abc 3         @即abc=3
    .equiv.equiv symbol        将symbol定义为expr,若symbol已定义则出错
    .global/.globl.globl symbol将symbol定义为全局标号
    .extern.extern symbol声明symbol为一个外部变量

    3、代码控制伪操作

    常见的代码控制伪操作如表格所示。

    代码控制伪操作

    格式

    作用

    举例与备注

    .section

    .section expr

    定义一个段,expr可以是.text、.data、.bss

    .text

    .text {subsection}

    将定义符开始的代码编译到代码段或代码子段(subsection)

    .data

    .data{subsection}

    将定义符开始的代码编译到数据段或数据子段(subsection)

    .bss

    .bss{subsection}

    将变量存放到.bss段或.bss的子段(subsection)

    .cond 16/.thumb

    .code 16/.thumb

    表示之后的汇编指令使用THUMB指令集

    .code 32/.arm

    .code 32/.arm

    表示之后的汇编指令使用ARM指令集

    .end

    .end

    标记汇编文件的结束

    .include

    .include "filename"

    将一个源文件包含到当前源文件中

    .align/.balign

    .align {alignment} {,fill},{max}

    通过填充字节使当前位置满足一定的对齐格式

    比如“ .balignl 4 0xdeadbeef ”表示4字节对齐,l表示以四字节为单位填充,填充的内容是0xdeadbeef。
    .org.org offset{,expr}

     指定从当前地址加上offset开始存放代码,并且从当前地址到当前地址加上offset之间的内存单元,用零或指定的数据进行填充

     4、预定义控制伪操作

    常见的预定义控制伪操作如下所示。
    宏定义伪操作格式作用举例与备注

    .macro

    标识宏定义的开始用.macro及.endm定义一段代码,称为宏定义体。
    .exitm中途跳转出宏
    .endm标识宏定义的结束
    .if 条件判断语句
    .else条件判断语句
    .endif条件结束语句
    .include     .include "file_name"包含文件标识 
    .incbin.incbin "file"[,skip[,count]]

    将原封不动地一个二进制文件编译到当前文件中。

    skip表明是从文件开始处跳过skip个字节开始读取文件,count是读取的字数。

    5、其他伪操作         

    伪操作格式作用举例与备注
    .err.err使编译结果产生错误报告
    .eject.eject在汇编符号列表文件中插入一分页符
    .list .list 产生汇编列表(从.list到.nolist)
    .nolist.nolist汇编列表结束处。再次使用.list产生汇编列表
    .title.title "title_name"使用title_name作为标题
    .sbttl.sbttl "title_name" 使用title_name作为子标题
    .print .print string打印输出信息到标准输出
    .fail.fail expr编译汇编文件时产生警告
    .reqreq name, expr为一个特定的寄存器定义名称
    .unreq.unreq 寄存器别名取消一个寄存器的别名。注意被取消的别名必须事先定义过,否则编译器就会报错。这个伪操作也可以用来取消系统预制的别名。
    .rept

    .rept 重复次数

    数据定义

    .endr @结束重复定义

    重复定义伪操作。

    .rept 3

    .byte 0x23

    .endr

    相当于

    .byte 0x23

    .byte 0x23

    .byte 0x23

    三、两者的区别与联系

    ARM官方汇编、ARM GNU汇编,它们的伪操作的区别与联系如下。

    两种开发环境下的汇编代码,有较多不同的点,主要是符号及伪操作的不同。

    ARM官方汇编伪操作ARM GNU汇编伪操作

    备注

    ;

    @

    注释

    #0x

    #&

    十六进制立即数

    IFDEF、IF

    .ifdef、.if

    不完全相同

    ELSE

    .else

    ELSEIF

    .elseif

    ENDIF

    .endif

    LTORG

    .ltorg

    OR

    |

    OR

    AND

    &

    AND

    SHL

    <<

    Shift left

    SHR

    >>

    Shift right

    MACRO

    .macro

    开始宏

    END.end

    MEND

    .endm

    结束宏

    INCLUDE

    .include

    DCB

    .word

    定义一个字数据

    DCW

    .short

    DCD

    .long

    DCB

    .byte

    RN

    .req

    EXPORT.global

    GBLA.global
    IMPORT.extern
    ENTRYENTRY:
    CODE16.thumb
    CODE32.arm
    %.fill
    NUM SETA 16.equ  NUM,16
    NUM EQU 25.equ  NUM,25

    AREA WORD, CODE, READONLY

    .text

    AREA BLOCK, DATE, READWRITE

    .data

  • 相关阅读:
    dp线段树优化-最大子段和
    代理模式
    linux实战项目经验得到的常用linux命令(2) 磁盘分区和内容查找
    扬帆牧哲:商标转让和商标变更的概念
    Java中的线程
    2021 第四届 浙江省大学生网络与信息安全竞赛技能赛 决赛 Writeup,5题
    雪花算法改造: 兼容JS截短位数的53bit分布式ID生成器
    【状语从句练习题】主将从现原则
    Dockerfile
    解读JVM级别本地缓存Caffeine青出于蓝的要诀 —— 缘何会更强、如何去上手
  • 原文地址:https://blog.csdn.net/oqqHuTu12345678/article/details/128058743