zoukankan      html  css  js  c++  java
  • AT&T 汇编学习

    套用网上看到的一句话开头吧(这也是我有过的一个过程,基本一样吧):今天开始学习linux内核编程了,从没有内核编程基础开始学起。所以很多相关的知识都要了解。首先就是AT&T汇编语言。因为在linux内核源代码中,好像除了开始的bootsect.shead.s是用intel的汇编外,别的汇编代码都是用的AT&T汇编语言,所以有必要把AT&T汇编语言了解一下。

    自己补充一点:我开始没想过什么linux内核编程哦,只觉得能把内核代码开懂就是最大的心愿了,不过现在还好,可以开始深入内核编写一些简单程序(主要是驱动吧,而且是针对的是嵌入式linux哈)!

    1.学习AT&T汇编语言的理由

    只有一点就是看懂linux启动代码。当然以后可能也会用到。

    2.学习的难度

    以为本人intel的汇编学的还算过得去,所以在这基础之上应该不是太难,最多发半天时间久可以熟悉了吧,对比intel汇编学习。

    3.intel的差异

     

     以下的内容都是AT&T汇编的特点:

    1)、寄存器前面要加“%”,如  mov %eax,%ebx

       这里要注意的一点是,AT&T汇编中,源寄存器和目的寄存器的顺序和intel汇编刚好相反,AT&T汇编中,左边的是源寄存器,右边的是目的寄存器,在上边那个例子中,%eax是源寄存器,%ebx是目的寄存器。

     

    2)、立即数/常数前面要加$,如  mov $4,%ebx 4这个数装入ebx这个寄存器。

       符号常数直接用, 如  mov  value,%eax 即把value代表的那个值装入eax寄存器。

                         mov $value,%eax 即把value的值作为地址,而把对应那个地址中的值装入eax

     

    3)、b(byte):8位, wword):16位, llong):32

       如:  movb %ax,%bx     movw %eax,%ebx

     

    4)、jum/call的操作数前要加上“*"作为前缀, 远跳转ljmp,远调用lcall

       如  ljmp $section,$offset

           lcall $section,$offset

       这里$sectionoffset表示的就是,以section为段地址,offset为段内偏移地址。因此,ljmp $section,$offset即跳转到section:offset地址。

     

    5)、远返回lret

       如  lret $stack_adjust

     

    6)、寻址方式

       表示方式 section:disp(base,index,scale)

       计算方法 base+index*scale+disp

            即 section:[base+index*scale+disp]

       其中disp是表示偏移地址。

       如  movl -4(%ebp),%eax  [%ebp-4]的内容装入eax

     

    7)、C语言中嵌入汇编

       格式: _asm_("asm statements":outputs:inputs:registers-modified)

       其中,"asm statements"是汇编语句表达式,outputs,inputs,register-modified都是可选参数,以冒号隔开,且一次以09编号,如outputs的寄存器是0号,inputs寄存器是1号,往后依次类推。outputs是汇编语句执行完后输出到的寄存器,inputs是输入到某个寄存器。

       例1_asm_("pushl %%eax/n/t" "movl $0,%%eax/n/t" "popl %%eax");

       在嵌入汇编中,寄存器前面要加两个%,因为gcc在编译是,会先去掉一个%再输出成汇编格式。

       例2{ register char _res;/

             asm("push %%fs/n/t"

             "movw %%ax,%%fs/n/t"

             "movb %%fs:%2,%%al/n/t"

             "pop %%fs"

             :"=a"(_res):"0"(seg),"m"(*(addr)));/

             _res;}

        movb %%fs:%2,%%al/n/t一句中是把以fs为段地址,以后面的第二号寄存器即后面的seg中的值为偏移地址所对应的值装入al"=a"(_res):"0"(seg),"m"(*(addr)))一句中,"=a"(_res)表示把a寄存器中的内容给_res"0"(seg)表示把seg中的内容给0所对应的寄存器,而0即表示使用和前一个寄存器相同的寄存器,这里即使用a寄存器,也就是说把seg中的内容个a寄存器。

       需要解释以下的是,a,b,c,d分别表示寄存器eaxebxecxedx

                      SD分别表示寄存器esiedi

                      r表示任意寄存器

                      0(数字0,不是o!)表示使用上一个寄存器

    4.下面是找到别人翻译的文档:

    一 基本语法  

        语法上主要有以下几个不同.  

    ★ 寄存器命名原则  

    AT&T: %eax Intel: eax  

    ★ 源/目的操作数顺序  

    AT&T: movl %eax,%ebx Intel: mov ebx,eax  

    ★ 常数/立即数的格式  

    AT&T: movl $_value,%ebx Intel: mov eax,_value  

    _value的地址放入eax寄存器  

    AT&T: movl $0xd00d,%ebx Intel: mov ebx,0xd00d  

    ★ 操作数长度标识  

    AT&T: movw %ax,%bx Intel: mov bx,ax  

    ★寻址方式  

    AT&T: immed32(basepointer,indexpointer,indexscale)  

    Intel: [basepointer + indexpointer*indexscale + imm32)  

    Linux工作于保护模式下,用的是32位线性地址,所以在计算地址时  

    不用考虑segment:offset的问题.上式中的地址应为:  

    imm32 + basepointer + indexpointer*indexscale  

    下面是一些例子:  

    ★直接寻址  

    AT&T: _booga ; _booga是一个全局的C变量  

    注意加上$是表示地址引用,不加是表示值引用.  

    注:对于局部变量,可以通过堆栈指针引用.  

    Intel: [_booga]  

    ★寄存器间接寻址  

    AT&T: (%eax)  

    Intel: [eax]  

    ★变址寻址  

    AT&T: _variable(%eax)  

    Intel: [eax + _variable]  

    AT&T: _array(,%eax,4)  

    Intel: [eax*4 + _array]  

    AT&T: _array(%ebx,%eax,8)  

    Intel: [ebx + eax*8 + _array]  

    二 基本的行内汇编  

        基本的行内汇编很简单,一般是按照下面的格式  

    asm("statements");  

    例如:asm("nop"); asm("cli");  

    asm 和 __asm__是完全一样的.  

    如果有多行汇编,则每一行都要加上 "/n/t"  

    例如:  

    asm( "pushl %eax/n/t"  

    "movl $0,%eax/n/t"  

    "popl %eax");  

    实际上gcc在处理汇编时,是要把asm(...)的内容"打印"到汇编  

    文件中,所以格式控制字符是必要的.  

    再例如:  

    asm("movl %eax,%ebx");  

    asm("xorl %ebx,%edx");  

    asm("movl $0,_booga);  

    在上面的例子中,由于我们在行内汇编中改变了edxebx的值,但是由于gcc的特殊的处理方法,即先形成汇编文件,再交给GAS去汇编,所以GAS并不知道我们已经改变了edxebx的值,如果程序的上下文需要edxebx作暂存,这样就会引起严重的后果.对于变量_booga也存在一样的问题.为了解决这个问题,就要用到扩展的行内汇编语法.  

    三 扩展的行内汇编  

        扩展的行内汇编类似于Watcom.  

    基本的格式是:  

    asm ( "statements" : output_regs : input_regs : clobbered_regs);  

    clobbered_regs指的是被改变的寄存器.  

    下面是一个例子(为方便起见,我使用全局变量):  

    int count=1;  

    int value=1;  

    int buf[10];  

    void main()  

    {  

    asm(  

    "cld /n/t"  

    "rep /n/t"  

    "stosl"  

    :  

    :  "c" (count), "a" (value) , "D" (buf[0])  

    :  "%ecx","%edi" );  

    }  

    得到的主要汇编代码为:  

    movl count,%ecx  

    movl value,%eax  

    movl buf,%edi  

    #APP  

    cld  

    rep  

    stosl  

    #NO_APP  

    cld,rep,stos就不用多解释了.  

    这几条语句的功能是向buf中写上countvalue值.  

    冒号后的语句指明输入,输出和被改变的寄存器.  

    通过冒号以后的语句,编译器就知道你的指令需要和改变哪些寄存器,  

    从而可以优化寄存器的分配.  

    其中符号"c"(count)指示要把count的值放入ecx寄存器  

    类似的还有:  

    a eax  

    b ebx  

    c ecx  

    d edx  

    S esi  

    D edi  

    常数值,(0 - 31)  

    q,r 动态分配的寄存器  

    g eax,ebx,ecx,edx或内存变量  

    eaxedx合成一个64位的寄存器(use long longs)  

    我们也可以让gcc自己选择合适的寄存器.  

    如下面的例子:  

    asm("leal (%1,%1,4),%0"  

    :  "=r" (x)  

  • 相关阅读:
    CentOS虚拟机和物理机共享文件夹实现
    集训第六周 数学概念与方法 概率 数论 最大公约数 G题
    集训第六周 数学概念与方法 概率 F题
    集训第六周 E题
    集训第六周 古典概型 期望 D题 Discovering Gold 期望
    集训第六周 古典概型 期望 C题
    集训第六周 数学概念与方法 UVA 11181 条件概率
    集训第六周 数学概念与方法 UVA 11722 几何概型
    DAG模型(矩形嵌套)
    集训第五周 动态规划 K题 背包
  • 原文地址:https://www.cnblogs.com/brucewoo/p/2252032.html
Copyright © 2011-2022 走看看