汇编程序伪操作指令

汇编程序伪操作指令


汇编程序的 . 伪操作

汇编程序中以.开头的名称并不是指令的助记符,不会被翻译成机器指令,而是给汇编器一些特殊指示,称为汇编指示(Assembler Directive)或伪操作(Pseudo-operation)

.section指示把代码划分成若干个段(Section),程序被操作系统加载执行时,每个段被加载到不同的地址,操作系统对不同的页面设置不同的读、写、执行权限。

 .section .text

.text段保存代码,是只读和可执行的,后面那些指令都属于.text段。

 .section .data

.data段保存已经初始化的全局静态变量和局部静态变量,C程序普通局部变量在运行时被保存在堆栈中,既不出现在.data段中,也不出现在.bss段中。此外,如果变量被初始化值为0,也可能会放到bss段。

 .section .rodata

rodata段存放的是只读数据,一般是程序的只读变量和字符串常量。如C语言中的const修饰的变量。单独设置rodata段有很多好处,,不光是在语义上支持C/C++的const关键字,而是操作系统在加载的时候可以将rodata段的属性映射为只读,对于这个短的任何操作都会作为非法操作处理,提高了程序的安全性。另外,在嵌入式平台下一般也会存在只读存储器,如ROM,这样rodata段放在该存储区域中就可以保证程序访问存储器的正确性。

 .section .bss

bss段保存未初始化的全局变量和局部静态变量。

 .section .rodata1

rodata1段存放只读数据,比如字符串常量、全局const变量,跟rodata一样。

.section .comment

comment存放的是编译器版本信息,如字符串“GCC:(GNU)4.2.0”。

.section debug

debug段保存调试信息。

.section dynamic

dynamic段保存动态连接信息。

.section hash

hash段保存符号哈希表

.section line

line段保存调试时的行号表,即源代码行号与编译后指令的对应表。

.section note

note段保存额外的编译器信息,例如程序的发布者、版本号等信息。

.section strtab

strtab段(string table)保存字符串表,用于存储ELF文件中用到的各种字符串。

.section symtab

symtab段保存符号表

.section shstrtab

shstrtab段(section string table)保存段名表。

.init
.finit

init段和finit段保存初始化与终结代码段。

.globl _start
.global _start

_start是一个符号(Symbol),符号在汇编程序中代表一个地址,可以用在指令中,汇编程序经过汇编器的处理之后,所有的符号都被替换成它所代表的地址值。在C语言中我们通过变量名访问一个变量,其实就是读写某个地址的内存单元,我们通过函数名调用一个函数,其实就是跳转到该函数第一条指令所在的地址,所以变量名和函数名都是符号,本质上是代表内存地址的。

.globlglobal指示告诉汇编器,_start这个符号要被链接器用到,所以要在目标文件的符号表中标记它是一个全局符号。_start就像C程序的main函数一样特殊,是整个程序的入口,链接器在链接时会查找目标文件中的_start符号代表的地址,把它设置为整个程序的入口地址,所以每个汇编程序都要提供一个_start符号并且用.globl声明。如果一个符号没有用.globl声明,就表示这个符号不会被链接器用到。

这里定义了_start符号,汇编器在翻译汇编程序时会计算每个数据对象和每条指令的地址,当看到这样一个符号定义时,就把它后面一条指令的地址作为这个符号所代表的地址。而_start这个符号又比较特殊,它所代表的地址是整个程序的入口地址。

.weak symbol_name

.weak伪操作用于设置符号的属性为,在汇编程序中,符号的默认属性为强(strong),如果此符号之前从未定义过,则同时创建此符号,并定义其属性为weak。

如果符号的属性为weak,那么它无需定义具体的内容。在链接的过程中,另外一个属性为strong的同名符号可以将此weak符号的内容强制覆盖。利用此特性,.weak伪操作常用于预先预留一个空符号,使得其能够通过汇编器语法检查,但是在后续的程序中定义符号的真正实体,并且在链接阶段将空符号覆盖并链接。

.string "hello world!\n"

.string伪操作将从当前PC地址处开始分配若干个字节空间用于存放“string”字符串。字节的个数取决于字符串的长度。

.asciz "string"

asciz伪操作用于将从当前PC地址处开始分配若干个字节空间用于存放“string”字符串。相当于string伪操作的别名。

.file filename

.file伪操作指示汇编器该汇编程序的逻辑文件名。

.local symbol_name

.local伪操作用于定义局部符号,使此符号不能够被其他程序文件可见。

.type type_name, %function
.type type_name, %object

.type伪操作用于定义符号的类。即.type type_name, %function表示将名为type_name的符号定义为一个函数(function).type type_name, %object表示将名为type_name的符号定义为数据对象。

.zero integer

zero伪操作将从当前PC处分配integer个字节空间,并用0填充。

.byte expression [, expression]
.2byte expression [, expression]
.4byte expression [, expression]
.8byte expression [, expression]
.half expression [, expression]
.word expression [, expression]
.dword expression [, expression]
.float expression [, expression]
.double expression [, expression]
.quad expression [, expression]

byte伪操作将当前PC地址开始分配若干个字节(byte)的空间,每个字节的填充值由逗号分隔开的expression指定,[]之间的值可省略。

2byte伪操作将当前PC地址开始分配若干个双字节(2byte)的空间,每个双字节的填充值由逗号分隔开的expression指定,[]之间的值可省略。分配的空间地址可以与双字节非对齐。

4byte伪操作将当前PC地址开始分配若干个4字节(4byte)的空间,每个4字节的填充值由逗号分隔开的expression指定,[]之间的值可省略。分配的空间地址可以与4字节非对齐。

8byte伪操作将当前PC地址开始分配若干个8字节(8byte)的空间,每个8字节的填充值由逗号分隔开的expression指定,[]之间的值可省略。分配的空间地址可以与8字节非对齐。

half伪操作将当前PC地址开始分配若干个半字(half-word)的空间,每个半字的填充值由逗号分隔开的expression指定,[]之间的值可省略。分配的空间地址一定与半字对齐。

word伪操作用于将当前PC地址开始分配若干个字(word)的空间,每个字的填充值由逗号分隔开的expression指定。这里的空间分配一定是注意是按字对齐。

dword伪操作将从当前PC处开始分配若干个双字(double word)的空间,每个双字填充的值由逗号分隔开的expression指定。这里的空间分配一定是双子对齐的。

float伪操作将从当前PC处开始分配若干个单精度浮点数(32位)的空间,每个单精度浮点数填充的值由逗号分隔开的expression指定。空间分配的地址一定与32位对齐。

double伪操作将从当前PC处开始分配若干个双精度浮点数(64位)的空间,每个单精度浮点数填充的值由逗号分隔开的expression指定。空间分配的地址一定与64位对齐。

quad伪操作将从当前PC处开始分配8字节(64位)的空间,每个8字节的填充值由逗号分隔开的expression指定,[]之间的值可省略。

.align integer

align伪操作用于将当前PC值推进至2的integer次方字节对齐的位置。例:align 2即表示将当前PC值推进至4字节对齐的位置。

.balign integer

balign伪操作用于将当前PC值推进至integer字节对齐的位置。

.size main_size, .-main

size伪操作用于指定符号的大小。.表示当前位置,减去main符号的地址即为整个main函数的大小。相当于将将main函数的大小赋值给符号main_size。

.equ name,value

equ伪操作用于进行常数的定义。

.set name,value

set伪操作用于给一个全局变量或局部变量赋值,和.equ的功能一样。

.option {rvc,norvc,push,pop,relax,norelax,pic,nopic}

option伪操作用于设定某些架构特定的选项,使得汇编器能够识别并按照选项的定义采取相应的行为:

rvcnorvc是RSIC-V特有的选项,用于控制是否生成16位宽的压缩指令:

  • rvc表示接下来的汇编程序可以被汇编生成16位宽的压缩指令。
  • norvc表示接下来的汇编程序可以被不可以被汇编生成16位宽的压缩指令。

pushpop用于临时性的保存或者恢复.option伪操作指定的选项。

relaxnorelax允许链接器松弛(linker relaxation,链接时多次扫描代码,尽可能将跳转两条指令替换为一条)和不允许链接器松弛1

picnopic表示位置无关的代码和表示与位置有关的代码。

.comm name, length
.common name, length

commcommon伪操作用于声明一个名为name的未初始化存储区间,区间大小为length字节。由于是未初始化存储区间,所以在链接时会链接至.bss段。

.include "xxx_core.h"

include伪指令用于包含指定的头文件。

.space size, fill

space伪操作用于分配一段内存单元,用fill填充,若省略, file则默认填充0

定义宏

.macro mac, a, b, c    //定义一个宏,宏名为mac,参数为a,b,c
mul t0, \b,  \c           //mul指令将b和c相乘得到乘积写入to寄存器
add a,  \t0, \a           //累加结果存储写入a
.endm

类似于C语言的宏

macroendm伪操作可以将一段代码定义为一个整体,称为宏指令。然后就可以在程序中通过宏指令多次调用该段代码。**注意:**先定义后使用。

参考《GNU手册》第7章《Assembler Directives》


🤔

🎡

🤷‍♀️


  1. 链接器松弛(linker relaxation):
      跳转并链接指令(jump and link)中有20位的相对地址域,因此一条指令就足够跳到很远的位置。尽管编译器为每个外部函数的跳转都生成了两条指令,很多时候其实一条就已经足够了。从两条指令到一条的优化同时节省了时间和空间开销,因此链接器会扫描几遍代码,尽可能地把两条指令替换为一条。每次替换会导致 函数和调用它的位置之间的距离缩短,所以链接器会多次扫描替换,直到代码不再改变。这个过程称为链接器松弛,名字来源于求解方程组的松弛技术。除了过程调用之外,对于 gp指针 ±2KiB范围内的数据访问, RISC-V链接器也会 使用一个全局指针替换掉 lui和 auipc两条指令。对 tp指针 ±2KiB范围内的线程局部变量访问也有类似的处理。 ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值