如何对网站ftp进行上传,分析竞争对手网站,做网站的方法,四川seo策略这显得很古怪#xff0c;不过在gcc知道程序员拿这些寄存器做些什么后#xff0c;这确实能够对gcc的优化操作有所帮助。表5-3中是一些可能会用到的寄存器加载代码及其具体的含义。表5-3 常用寄存器加载代码说明代 码说 明代 码说 明a使用寄存器eaxm使用内存地址b使用寄存器ebx…这显得很古怪不过在gcc知道程序员拿这些寄存器做些什么后这确实能够对gcc的优化操作有所帮助。表5-3中是一些可能会用到的寄存器加载代码及其具体的含义。表5-3 常用寄存器加载代码说明代 码说 明代 码说 明a使用寄存器eaxm使用内存地址b使用寄存器ebxo使用内存地址并可以加偏移值c使用寄存器ecxI使用常数0~31d使用寄存器edxJ使用常数0~62S使用esiK使用常数0~255D使用ediL使用常数0~65535q使用动态分配字节可寻址机寄存器(eax、ebx、ecx、或edx)M使用常数0~3r使用任意动态分配的寄存器N使用1字节常数g使用通用有效的地址即可(eax、ebx、ecx、edx或内存变量)O使用常数0~31A使用eax与edx联合(64位)下面的例子不是让程序员自己指定哪个变量使用哪个寄存器而是让gcc为程序员选择。01 asm(leal (%1, %1, 4),%002 : r(y)03 : 0(x));第一句汇编语句leal(r1r24)r3语句表示r1r2*4→r3,。这个例子可以非常快地将x乘5。其中“%0”“%1”是指gcc自动分配的寄存器。这里“%1”代表输入值x要放入的寄存器“%0”表示输出值寄存器。输出寄存器代码前一定要加入等于号。如果输入寄存器的代码时0或为空时则说明使用与相应输出一样的寄存器。所以。如果gcc将r指定为eax的话那么上面汇编语句的含义即为“leal(eaxeax4)eax”。注意在执行代码时如果不希望汇编语句被gcc优化而改变位置就需要在asm符号后面添加volatile关键词asmvolatile(……)或者更详细地说明为__asm_ _ _ _volatile_ _(……)一、ATT 格式Linux 汇编语法格式1.在 ATT汇编格式中寄存器名要加上 % 作为前缀而在 Intel 汇编格式中寄存器名不需要加前缀。 ATT格式Intel格式pushl xpush eax2.在 ATT 汇编格式中用 $ 前缀表示一个立即操作数而在 Intel汇编格式中立即数的表示不用带任何前缀。例如ATT格式Intel格式pushl $1push 1ATT 和 Intel格式中的源操作数和目标操作数的位置正好相反。在 Intel 汇编格式中目标操作数在源操作数的左边而在ATT 汇编格式中目标操作数在源操作数的右边。例如ATT格式Intel格式addl $1, xadd eax, 1在 ATT汇编格式中操作数的字长由操作符的最后一个字母决定后缀b、w、l分别表示操作数为字节(byte8比特)、字(word16 比特)和长字(long32比特)而在 Intel 汇编格式中操作数的字长是用 byte ptr和 word ptr 等前缀来表示的。例如ATT格式Intel格式movb val, %almov al, byte ptr val5.在 ATT汇编格式中绝对转移和调用指令(jump/call)的操作数前要加上*作为前缀而在 Intel 格式中则不需要。远程转移指令和远程子调用指令的操作码在ATT 汇编格式中为 ljump 和 lcall而在 Intel 汇编格式中则为 jmpfar 和 call far即ATT格式Intel格式ljump $section, $offsetjmp far section:offsetlcall $section, $offsetcall far section:offset与之相应的远程返回指令则为ATT格式Intel格式lret $stack_adjustret far stack_adjust在 ATT 汇编格式中内存操作数的寻址方式是section:disp(base, index, scale)而在 Intel 汇编格式中内存操作数的寻址方式为section:[base index*scale disp]由于 Linux 工作在保护模式下用的是 32位线性地址所以在计算地址时不用考虑段基址和偏移量而是采用如下的地址计算方法disp base index * scale下面是一些内存操作数的例子ATT格式Intel格式movl -4(p), xmov eax, [ebp - 4]movl array(, x, 4), xmov eax, [eax*4 array]movw array(x, x, 4), %cxmov cx, [ebx 4*eax array]movb $4, %fs:(x)mov fs:eax, 4linux内核嵌入式汇编总结(1)在linux内核中有很多的嵌入式汇编代码。嵌入汇编的基本格式为asm(汇编语句:输出寄存器:输入寄存器 :会被修改的寄存器);其中”汇编语句”是程序员写汇编指令的地方;”输出寄存器”表示当这段嵌入式汇编执行之后哪些寄存器用于存放输出数据。这些寄存器会分别对应一个C语言表达式或一个内存地址“输入寄存器”表示在开始执行汇编代码时这里指定的一些寄存器中应存放的输入值它们也分别对应着一个C变量或常数值。下面用例子来说明嵌入式汇编语句的使用方法。我们在下面列出了一段代码作为例子来详细解说01 #define get_seg_byte(seg,addr)\ 02 ({\ 03 register char __res;\ 04 __asm__(push %%fs;\ 05 mov %%ax, %%fs;\ 06 movb %%fs: %2, %%al;\ 07 pop %%fs\ 08 : a (__res)\ 09 : (seg),m (* (addr)));\ 10 __res; })这段10行代码定义了一个嵌入式汇编语言宏函数。通常使用汇编语句最方便的方式是把它们放在一个宏内。用圆括号括住的组合语句(花括号中的语句)可以作为表达式使用其中最后的变量__res(第10行)是该表达式的输出值。因为是宏语句需要在一行上定义因此这里使用反斜杠\将这些语句连成一行。这条宏定义将被替换到宏名称在程序中被引用的地方。第1行定义了宏的名称也就是宏函数名称get_seg_byte(seg,addr)。第3行定义了一个寄存器变量__res。第4行上的_ _asm__表示嵌入式语句的开始。从第4行到第7行的4条ATT格式的汇编语句。 第8行是输出寄存器这句的含义是在这段代码运行结束后将eax所代表的寄存器的值放入__res变量中作为本函数的输出值“a”中的“a”称为加载代码“”表示这是输出寄存器。第9行表示在这段代码开始运行时将seg放到eax寄存器中“”表示使用与上面同个位置的输出相同的寄存器。而(*(addr))表示一个内存偏移地址值。为了在上面汇编语句中使用该地址值嵌入式汇编程序规定把输入和输出寄存器统一按顺序编号顺序是从输出寄存器序列从左到右从上到下以“%0”开始分别记为%0、%1、……%9。因此输出寄存器的编号是%0(这里只有一个输出寄存器)输出寄存器前一部分(””(seg))的编号是%1而后部分的编号是%2。上面第6行上的%2即代表(*(addr))这个内存偏移量。现在我们来研究4~7行上的代码的作用。第一句将fs段寄存器的内容入栈第二句将eax中的段值赋给fs段寄存器第三句是fs(*(addr))所指定的字节放入al寄存器中。当执行完汇编语句后输出寄存器eax的值将被放入__res,作为该宏函数的返回值。01 asm(cld\n\t02 rep\n\t03 stol04 :05 :c(count-1), a(file_value),D(dest)06 :x, i);1~3行这三句是通常的汇编语句用以清方向位重复保存值。第4行说明这段嵌入汇编程序没有用到输出寄存器。第5行的含义是将count-1的值加载到ecx中dest放到edi中。为什么要让gcc编译程序去做这样的寄存器值的加载而不让我们自己做呢因为gcc在它进行寄存器分配时可以进行某些优化工作。例如fill_value值可能已经在eax中。如果是在一个循环语句中的话gcc就可能在整个循环操作中保留eax这样就可以在每次循环中少用一个movel语句。最后一行的作用是告诉gcc这些寄存器中的值已经改变了。通过上面分析我们知道宏名称中的seg代表一指定的内存段值而addr表示一内存偏移地址量。到现在为止我们应该很清楚这段程序的功能了吧该宏函数的功能是从指定段和偏移值的内存地址处取一个字节。再看下一个例子。