• intel lea指令中SIB模式的极限在哪里


    一、问题的由来

    在汇编代码中,经常可以看到lea这个指令,它的出现频率比它“应该出现”的频率高得多,因为很多时候,它甚至可以用来做简单的乘法运算。
    tsecer@harry: cat mult.cpp
    int foo(int x)
    {
    return x * 9;
    }
    tsecer@harry: gcc -O2 -c mult.cpp
    tsecer@harry: objdump -d mult.o

    mult.o: 文件格式 elf64-x86-64


    Disassembly of section .text:

    0000000000000000 <_Z3fooi>:
    0: 8d 04 ff lea (%rdi,%rdi,8),%eax
    3: c3 retq
    tsecer@harry:

    这个指令初看起来比较复杂,尽管它最原始的设计意图是为了对应高级语言中对结构数组中特定成员取值的指令。例如下面的情况
    tsecer@harry: cat typicallea.cpp
    struct S
    {
    int x;
    int y;
    };

    void *foo(S *ps, int i)
    {
    return &ps[i].y;
    }
    tsecer@harry: gcc -O2 -c typicallea.cpp
    tsecer@harry: objdump -d typicallea.o

    typicallea.o: 文件格式 elf64-x86-64


    Disassembly of section .text:

    0000000000000000 <_Z3fooP1Si>:
    0: 48 63 f6 movslq %esi,%rsi
    3: 48 8d 44 f7 04 lea 0x4(%rdi,%rsi,8),%rax
    8: c3 retq
    tsecer@harry:
    现在的问题是,这个复杂指令的极限在哪里?比方说,其中的8可以修改为100、7这种数值吗?外面的0x4可以修改为任意的32位绝对地址吗?

    二、intel手册对指令的说明

    ModR/M 1 byte (if required) 其中 Mod为7-6bit,Reg/OpCode为5-3bit,R/M为2-0bit
    SIB 1 byte (if required) 其中Scale为7-6bit、Index为5-3bit,Base为2-0bit。

    2.1.3 ModR/M and SIB Bytes
    Many instructions that refer to an operand in memory have an addressing-form specifier byte (called the ModR/M byte) following the primary opcode. The ModR/M byte contains three fields of information:
    • The mod field combines with the r/m field to form 32 possible values: eight registers and 24 addressing modes.
    • The reg/opcode field specifies either a register number or three more bits of opcode information. The purpose
    of the reg/opcode field is specified in the primary opcode.
    • The r/m field can specify a register as an operand or it can be combined with the mod field to encode an
    addressing mode. Sometimes, certain combinations of the mod field and the r/m field is used to express
    opcode information for some instructions.
    Certain encodings of the ModR/M byte require a second addressing byte (the SIB byte). The base-plus-index and
    scale-plus-index forms of 32-bit addressing require the SIB byte. The SIB byte includes the following fields:
    • The scale field specifies the scale factor.
    • The index field specifies the register number of the index register.
    • The base field specifies the register number of the base register.
    See Section 2.1.5 for the encodings of the ModR/M and SIB bytes

    综合这些信息,我们比较关心的问题是:
    scale的范围:scale为7-6两个bit,所以它的范围为0-3。但是,这个factor事实上是表示2^factor中的factor(尽管我在手册搜索“scale”没有找到明确说明),也就是说在汇编中看到的disp(scale, index, base)中的scale的取值只能为1、2、4、8。

    Table 2-2. 32-Bit Addressing Forms with the ModR/M Byte
    [--][--]1、[--][--]+disp8、[--][--]+disp32
    并且在注释中说明
    NOTES:
    1. The [--][--] nomenclature means a SIB follows the ModR/M byte.
    2. The disp32 nomenclature denotes a 32-bit displacement that follows the ModR/M byte (or the SIB byte if one is present) and that is added to the index.
    3. The disp8 nomenclature denotes an 8-bit displacement that follows the ModR/M byte (or the SIB byte if one is present) and that is sign-extended and added to the index.
    从这里可以看到,当使用SIB模式的时候,偏移量可以为8bit的常量,也可以是32bit的常量。

    这样算下来,其实lea中的偏移量可以表达的形式既丰富又受限,可以认为是带着脚镣跳舞。

    三、验证一些例子

    1、关于scale大小的例子


    可以看到,当结构大于8的时候(例如struct B),一条lea指令已经无法满足,而需要转换为多条lea指令。以struct B为例,结构大小为12=4*3,所以首先使用lea(esi, esi, 2)获得esi * 3,然后在lea(edi,esi,4)获得esi*4。
    tsecer@harry: cat lea.cpp
    struct S
    {
    int x;
    int y;
    };
    struct B
    {
    S s;
    int y;
    };

    #pragma pack(1)
    struct A
    {
    int x;
    int y;
    short s;
    }
    ;

    void* fooS(S *ps, int i)
    {
    return &ps[i].y;
    }

    void* fooB(B *pb, int i)
    {
    return &pb[i].y;
    }

    void* fooA(A *pa, int i)
    {
    return &pa[i].y;
    }


    tsecer@harry: gcc -O2 -c lea.cpp
    tsecer@harry: objdump -d lea.o

    lea.o: 文件格式 elf64-x86-64


    Disassembly of section .text:

    0000000000000000 <_Z4fooSP1Si>:
    0: 48 63 f6 movslq %esi,%rsi
    3: 48 8d 44 f7 04 lea 0x4(%rdi,%rsi,8),%rax
    8: c3 retq
    9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)

    0000000000000010 <_Z4fooBP1Bi>:
    10: 48 63 f6 movslq %esi,%rsi
    13: 48 8d 04 76 lea (%rsi,%rsi,2),%rax
    17: 48 8d 44 87 08 lea 0x8(%rdi,%rax,4),%rax
    1c: c3 retq
    1d: 0f 1f 00 nopl (%rax)

    0000000000000020 <_Z4fooAP1Ai>:
    20: 48 63 f6 movslq %esi,%rsi
    23: 48 8d 04 b6 lea (%rsi,%rsi,4),%rax
    27: 48 8d 44 47 04 lea 0x4(%rdi,%rax,2),%rax
    2c: c3 retq
    tsecer@harry:

    2、关于disp大小的例子

    下面的例子看到,虽然127和128只差了1,但是由于它的指令长度相差了3个bytes,导致最后的dips从8个bit直接提升为32bit,也就是
    3: 48 8d 44 f7 7f lea 0x7f(%rdi,%rsi,8),%rax

    13: 48 8d 84 f7 80 00 00 lea 0x80(%rdi,%rsi,8),%rax
    1a: 00
    的差别。

    tsecer@harry: cat lea.disp.cpp
    struct S
    {
    int x;
    int y;
    };


    void* foo127(S *ps, int i)
    {
    return (void*)&ps[i].x + 127;
    }

    void* foo128(S *ps, int i)
    {
    return (void*)&ps[i].x + 128;
    }

    tsecer@harry: gcc -O2 -c lea.disp.cpp
    lea.disp.cpp: 在函数‘void* foo127(S*, int)’中:
    lea.disp.cpp:10:27: 警告:‘void *’型指针用在了算术表达式中 [-Wpointer-arith]
    return (void*)&ps[i].x + 127;
    ^
    lea.disp.cpp: 在函数‘void* foo128(S*, int)’中:
    lea.disp.cpp:15:27: 警告:‘void *’型指针用在了算术表达式中 [-Wpointer-arith]
    return (void*)&ps[i].x + 128;
    ^
    tsecer@harry: objdump -d lea.disp.o

    lea.disp.o: 文件格式 elf64-x86-64


    Disassembly of section .text:

    0000000000000000 <_Z6foo127P1Si>:
    0: 48 63 f6 movslq %esi,%rsi
    3: 48 8d 44 f7 7f lea 0x7f(%rdi,%rsi,8),%rax
    8: c3 retq
    9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)

    0000000000000010 <_Z6foo128P1Si>:
    10: 48 63 f6 movslq %esi,%rsi
    13: 48 8d 84 f7 80 00 00 lea 0x80(%rdi,%rsi,8),%rax
    1a: 00
    1b: c3 retq
    tsecer@harry:

  • 相关阅读:
    EasyUI+bootsrtap混合前端框架
    软件的极简主义的三个大敌:配置文件,冗余的参数,和大量复杂的接口。
    PowerDesigner逆向操作(从mysql5.0生成数据库的物理模型),把Comment写到name中,pdm文件导出为word
    修改apache配置文件去除thinkphp url中的index.php
    javascript闭包(Module模式)的用途和高级使用方式
    seo标题关键字描述字数限制Title,keywords,description长度最长多长 ?
    css去掉a标签点击后的虚线框,outline,this.blur()
    让360双核浏览器默认极速模式,避免采用IE模式无法正常访问html5网页的解决办法
    zendstudio中加入对tpl文件的支持,用HTML Editor编辑器编辑
    nginx的权限问题(Permission denied)解决办法
  • 原文地址:https://www.cnblogs.com/tsecer/p/12600930.html
Copyright © 2020-2023  润新知