Chinaunix首页 | 论坛 | 博客
  • 博客访问: 496831
  • 博文数量: 174
  • 博客积分: 2502
  • 博客等级: 少校
  • 技术积分: 1923
  • 用 户 组: 普通用户
  • 注册时间: 2008-09-28 09:47
文章分类

全部博文(174)

文章存档

2011年(8)

2010年(16)

2009年(68)

2008年(82)

我的朋友

分类: LINUX

2009-03-13 20:03:21

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 是读/写节。


汇编程序指令(Assembler Directive)
    上面介绍的.section就是汇编程序指令的一种,GNU汇编程序提供了很多这样的指令(directive),这种指令都是以点句(.)开头,后跟指令名(小写字母),在此,我们只介绍在内核源代码中出现的几个指令(以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)能够看到symbol。如果你的局部程序中定义了symbol,那么,与这个局部程序连接的其他局部程序也能存取symbol,例如:
        .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 code at 0x00000000 */
        .quad 0x00cffa000000ffff        /* 0x23 user   4GB code at 0x00000000 */
        .quad 0x00cff2000000ffff        /* 0x2b user   4GB code 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。

    附:
    嵌入式汇编
    1.嵌入式汇编的一般形式:
    __asm__ __volatile__ ("": output: input: modify);
    其中, __asm__表示汇编代码的开始,其后可以跟__volatile__ (可选项)
    ""为汇编指令部分。例如,"movl %%cr0, %0\n\t"。数字前加前缀"%",如%1,%2等表示使用寄存器的样板操作数。可以使用的操作数总数取决于具体CPU中通用寄存器的数量,如Intel可以有8个。由于这些样板操作数的前缀使用了"%",因此,在用到具体的寄存器时就在前面加两个"%",如%%cr0。
    输出部分(output): 用以规定对输出变量(目标操作数)如何与寄存器结合的约束(constraint),输出部分可以有多个约束,互相以逗号分开。每个约束以"="开头,接着用一个字母表示操作数的类型,然后是关于变量结合的约束。
    输入部分(input): 输入部分与输出部分相似,但是没有"="。如果输入部分一个操作数所要求使用的寄存器,与前面输出部分某个约束所要求的是同一个寄存器,那就把对应操作数的编号(如"1","2"等)放在约束条件中。
    修改部分(modify): 这部分常常以"memory"为约束条件,以表示操作完成后内存中的内容已有改变,如果原来某个寄存器的内容来自内存,那么现在内存中这个单元的内容已经改变。
    注意: 指令部分为必选项,而输入部分、输出部分及修改部分为可选项,当输入部分存在,而商场部分不存在,分号":"要保留,当"memory"存在时,三个分号都要保留,例如system.h中的宏定义__cli():
        #define __cli()    __asm__ __volatile__("cli": : :"memory")
    通常,在每个命令的结束加上两个符号: "\n"(换行符)、"\t"(tab符). 这是为了让gcc把嵌入式汇编代码翻译成一般的汇编代码时能够保证换行和留有一定的空格。
阅读(747) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~