AT&T汇编语言的相关知识

在Linux源代码中,以.S为扩展名的文件是“纯”汇编语言的文件。这里,我们结合具体的例子再介绍一些AT&T汇编语言的相关知识。
   1.GNU汇编程序GAS(GNU Assembly和连接程序
当你编写了一个程序后,就需要对其进行汇编(assembly)和连接。在Linux下有两种方式,一种是使用汇编程序GAS和连接程序ld,一种是使用gcc。我们先来看一下GAS和ld:
GAS把汇编语言源文件(.o)转换为目标文件(.o),其基本语法如下:
as filename.s -o filename.o
一旦创建了一个目标文件,就需要把它连接并执行,连接一个目标文件的基本语法为:
ld filename.o -o filename
 
这里 filename.o是目标文件名,而filename 是输出(可执行) 文件。
GAS使用的是AT&T的语法而不是Intel的语法,这就再次说明了AT&T语法是Unix世界的标准,你必须熟悉它。
 
如果要使用GNC的C编译器gcc,就可以一步完成汇编和连接,例如:
 
gcc -o example example.S
   
   这里,example.S是你的汇编程序,输出文件(可执行文件)名为example。其中,扩展名必须为大写的S,这是因为,大写的S可以使gcc自动识别汇编程序中的C预处理命令,像#include、#define、#ifdef、 #endif等,也就是说,使用gcc进行编译,你可以在汇编程序中使用C的预处理命令。
2. AT&T中的节(Section)
     在AT&T的语法中,一个节由.section关键词来标识,当你编写汇编语言程序时,至少需要有以下三种节:
.section .data: 这种节包含程序已初始化的数据,也就是说,包含具有初值的那些变量,例如:
              hello     : .string "Hello world!/n"
              hello_len : .long 13
 .section .bss:这个节包含程序还未初始化的数据,也就是说,包含没有初值的那些变量。当操作
 系统装入这个程序时将把这些变量都置为0,例如:
 
      name      : .fill 30   # 用来请求用户输入名字
             name_len : .long 0   # 名字的长度 (尚未定义)
       当这个程序被装入时,name 和 name_len都被置为0。如果你在.bss节不小心给一个变量赋了初值,这个值也会丢失,并且变量的值仍为0。
使用.bss比使用.data的优势在于,.bss节不占用磁盘的空间。在磁盘上,一个长整数就足以存放.bss节。当程序被装入到内存时,操作系统也只分配给这个节4个字节的内存大小。 
注意:编译程序把.data和.bss在4字节上对齐(align),例如,.data总共有34字节,那么编译程序把它对其在36字节上,也就是说,实际给它36字节的空间。
.section .text :这个节包含程序的代码,它是只读节,而.data 和.bss是读/写节。
 
3.汇编程序指令(Assembler Directive)
     上面介绍的.section就是汇编程序指令的一种,GNU汇编程序提供了很多这样的指令(directiv),这种指令都是以句点(.)为开头,后跟指令名(小写字母),在此,我们只介绍在内核源代码中出现的几个指令(以arch/i386/kernel/head.S中的代码为例)。
(1)ascii "string"...
.ascii 表示零个或多个(用逗号隔开)字符串,并把每个字符串(结尾 不自动加“0“字节)中的字符放在连续的地址单元。
还有一个与.ascii类似的.asciz,z代表 “0“,即每个字符串结尾自动加一个”0“字节,例如:
int_msg:
         .asciz "Unknown interrupt/n"
(2).byte 表达式
     .byte表示零或多个表达式(用逗号隔开),每个表达式被放在下一个字节单元。
(3).fill 表达式
     形式:.fill repeat , size , value
     其中,repeat、size 和value都是常量表达式。Fill的含义是反复拷贝size 个字节。Repeat可以大于等于0。size也可以大于等于0,但不能超过8,如果超过8,也只取8。把repeat 个字节以8个为一组,每组的最高4个字节内容为0,最低4字节内容置为value。
     Size和 value为可选项。如果第二个逗号和value值不存在,则假定value为0。如果第一个逗号和size不存在,则假定size为1。
     例如,在Linux初始化的过程中,对全局描述符表GDT进行设置的最后一句为:
     .fill NR_CPUS*4,8,0             /* space for TSS's and LDT's */
     因为每个描述符正好占8个字节,因此,.fill给每个CPU留有存放4个描述符的位置。
(4). globl symbol
     .globl使得连接程序(ld)能够看到symbl。如果你的局部程序中定义了symbl,那么,与这个局部程序连接的其他局部程序也能存取symbl,例如:
     .globl SYMBOL_NAME(idt)
     .globl SYMBOL_NAME(gdt)
     定义idt和gdt为全局符号。
 
(5)quad bignums
.quad表示零个或多个bignums(用逗号分隔),对于每个bignum,其缺省值是8字节整数。如果bignum超过8字节,则打印一个警告信息;并只取bignum最低8字节。
例如,对全局描述符表的填充就用到这个指令:
.quad 0x00cf9a000000ffff         /* 0x10 kernel 4GB code at 0x00000000 */
.quad 0x00cf92000000ffff         /* 0x18 kernel 4GB data at 0x00000000 */
.quad 0x00cffa000000ffff         /* 0x23 user   4GB code at 0x00000000 */
.quad 0x00cff2000000ffff         /* 0x2b user   4GB data at 0x00000000 */
 
(6)rept count
     把.rept指令与.endr指令之间的行重复count次,例如
        .rept   3
        .long   0
        .endr
    相当于
        .long   0
         .long   0
        .long   0
 (7)space size , fill
 这个指令保留size 个字节的空间,每个字节的值为fill。size 和fill都是常量表达式。如果逗号和fill被省略,则假定fill为0,例如在arch/i386/bootl/setup.S中有一句:
 . space 1024
 表示保留1024字节的空间,并且每个字节的值为0。
 (8). word expressions
   这个表达式表示任意一节中的一个或多个表达式(用逗号分开),表达式的值占两个字节,例如:
 gdt_descr:
        .word GDT_ENTRIES*8-1
   表示变量gdt_descr的置为GDT_ENTRIES*8-1
 (9). long expressions
    这与.word类似
 (10). org new-lc , fill
    把当前节的位置计数器提前到new-lc(new location counter)。new-lc或者是一个常量表达式,或者是一个与当前子 节处于同一节的表达式。也就是说,你不能用.org横跨节:如果new-lc是个错误的值,则.org被忽略。.org只能增加位置计数器的值,或者让其保持不变;但绝不能用.org来让位置计数器倒退。
   注意,位置计数器的 起始值是相对于一个节的开始的,而不是子节的开始。当位置计数器被提升后,中间位置的字节被填充值fill(这也是一个常量表达式)。如果逗号和fill都省略,则fill的缺省值为0。
 例如:.org 0x2000
        ENTRY(pg0)
 表示把位置计数器置为0x2000,这个位置存放的就是临时页表pg0。
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值