第九周x86-64指令系统

在线阅读PDF文档

第1讲 x86-64指令系统概述

第2讲 x86-64的基本指令

1.x86-64传送指令(16分钟)

2.x86-64算术、逻辑运算指令(20分钟)

3.x86-64逆向工程举例(17分钟)

第3讲 x86-64的过程调用

1.x86-64过程调用的参数传递方式(16分钟)

long int sample(long int *xp, long int y)
{
        long int t = *xp + y;
        *xp = t;
        return t;
}

執行gcc -O1 -S -m32 sample.c

        .file   "sample.c"
        .text
        .globl  sample
        .type   sample, @function
sample:
.LFB0:
        .cfi_startproc
        movl    4(%esp), %edx
        movl    (%edx), %eax
        addl    8(%esp), %eax
        movl    %eax, (%edx)
        ret
        .cfi_endproc
.LFE0:
        .size   sample, .-sample
        .ident  "GCC: (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0"
        .section        .note.GNU-stack,"",@progbits

執行gcc -O1 -S -m64 sample.c

        .file   "sample.c"
        .text
        .globl  sample
        .type   sample, @function
sample:
.LFB0:
        .cfi_startproc
        endbr64
        movq    %rsi, %rax
        addq    (%rdi), %rax
        movq    %rax, (%rdi)
        ret
        .cfi_endproc
.LFE0:
        .size   sample, .-sample
        .ident  "GCC: (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0"
        .section        .note.GNU-stack,"",@progbits
        .section        .note.gnu.property,"a"
        .align 8
        .long   1f - 0f
        .long   4f - 1f
        .long   5
0:
        .string "GNU"
1:
        .align 8
        .long   0xc0000002
        .long   3f - 2f
2:
        .long   0x3
3:
        .align 8
4:

2.x86-64过程调用举例(24分钟)

void test(char a,char *ap,short b,short *bp,int c,int *cp,long d,long *dp)
{
        *ap += a;
        *bp += b;
        *cp += c;
        *dp += d;
}
long caller()
{
        char a =1 ;
        short b = 2;
        int c = 3;
        long d = 4;
        test(a,&a,b,&b,c,&c,d,&d);
        return a*b+c*d;
}
        .file   "sample.c"
        .text
        .globl  test
        .type   test, @function
test:
.LFB0:
        .cfi_startproc
        endbr64
        movq    16(%rsp), %rax
        addb    %dil, (%rsi)
        addw    %dx, (%rcx)
        addl    %r8d, (%r9)
        movq    8(%rsp), %rdx
        addq    %rdx, (%rax)
        ret
        .cfi_endproc
.LFE0:
        .size   test, .-test
        .globl  caller
        .type   caller, @function
caller:
.LFB1:
        .cfi_startproc
        endbr64
        movl    $56, %eax
        ret
        .cfi_endproc
.LFE1:
        .size   caller, .-caller
                .ident  "GCC: (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0"
        .section        .note.GNU-stack,"",@progbits
        .section        .note.gnu.property,"a"
        .align 8
        .long   1f - 0f
        .long   4f - 1f
        .long   5
0:
        .string "GNU"
1:
        .align 8
        .long   0xc0000002
        .long   3f - 2f
2:
        .long   0x3
3:
        .align 8
4:

3.IA-32和x86-64的比较举例(15分钟)

#include <stdio.h>
void test(char a,char *ap,short b,short *bp,int c,int *cp,long d,long *dp)
{
        *ap += a;
        *bp += b;
        *cp += c;
        *dp += d;
}
long caller()
{
        char a =1 ;
        short b = 2;
        int c = 3;
        long d = 4;
        test(a,&a,b,&b,c,&c,d,&d);
        return a*b+c*d;
}
int main()
{
        double a = 10;
        printf("a = %d\n",a);
}

在WSL2Ubuntu22.04LTS系统上输出结果a = -310744488是个随机值

        .file   "sample.c"
        .text
        .globl  test
        .type   test, @function
test:
.LFB23:
        .cfi_startproc
        endbr64
        movq    16(%rsp), %rax
        addb    %dil, (%rsi)
        addw    %dx, (%rcx)
        addl    %r8d, (%r9)
        movq    8(%rsp), %rdx
        addq    %rdx, (%rax)
        ret
        .cfi_endproc
.LFE23:
        .size   test, .-test
        .globl  caller
        .type   caller, @function
caller:
.LFB24:
        .cfi_startproc
        endbr64
        movl    $56, %eax
        ret
        .cfi_endproc
.LFE24:
         .size   caller, .-caller
        .section        .rodata.str1.1,"aMS",@progbits,1
.LC1:
        .string "a = %d\n"
        .text
        .globl  main
        .type   main, @function
main:
.LFB25:
        .cfi_startproc
        endbr64
        subq    $8, %rsp
        .cfi_def_cfa_offset 16
        movsd   .LC0(%rip), %xmm0
        leaq    .LC1(%rip), %rsi
        movl    $1, %edi
        movl    $1, %eax
        call    __printf_chk@PLT
        movl    $0, %eax
        addq    $8, %rsp
        .cfi_def_cfa_offset 8
        ret
        .cfi_endproc
.LFE25:
        .size   main, .-main
        .section        .rodata.cst8,"aM",@progbits,8
        .align 8
.LC0:
        .long   0
        .long   1076101120
        .ident  "GCC: (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0"
        .section        .note.GNU-stack,"",@progbits
        .section        .note.gnu.property,"a"
        .align 8
        .long   1f - 0f
        .long   4f - 1f
        .long   5
0:
        .string "GNU"
1:
        .align 8
        .long   0xc0000002
        .long   3f - 2f
2:
        .long   0x3
3:
        .align 8
4:

第九周小测验

1以下有关IA-32和x86-64之间比较的叙述中,错误的是(D )。

A.IA-32的通用寄存器有8个,而x86-64的通用寄存器有16个

B.IA-32的字长为32位,x86-64的字长为64位并兼容IA-32

C.IA-32的通用寄存器为8/16/32位,而x86-64的通用寄存器为8/16/32/64位

D.(unsigned) long型变量在IA-32和x86-64中的长度都是64位(四字)

解释:long型变量在IA-32中的长度是32位

2以下有关x86-64寄存器的叙述中,错误的是( D )。

A.128位的XMM寄存器从原来IA-32中的8个增加到16个

B.用来存放将要执行的指令的地址的指令指针寄存器为64位的RIP

C.基址寄存器和变址寄存器都可以是任意一个64位的通用寄存器

D.任何浮点操作数都被分配在浮点寄存器栈(ST(0)~ST(7))中

3以下有关x86-64对齐方式的叙述中,错误的是(B )。

A.int、float型数据必须按4字节边界对齐

B.long double型数据在内存占12字节空间(96位)

C.long、double、指针型数据必须按8字节边界对齐

D.short型数据必须按2字节边界对齐

解释:long double型数据在内存占16字节空间(128位),实际只用80位

4以下有关x86-64传送指令的叙述中,错误的是(B )。

A.movl相当于movzlq,能将目的寄存器高32位清0

B.pushq和popq分别对ESP寄存器减8和加8

C.相比IA-32,增加了movq指令,可传送64位数据

D.movzbq的功能是将8位寄存器内容零扩展为64位

解释:pushq和popq分别对RSP寄存器减8和加8

5假定变量x的类型为int,对于变量y的初始化声明“long y=(long) x;”,其对应的汇编指令是(A )。

A.movslq %edx, %rax

B.movq %rdx, %rax

C.movzlq %edx, %rax

D.movl %edx, %eax

解释:这里进行的是符号扩展,不是零扩展

6假定变量x的类型为long,对于变量y的初始化声明“int y=(int) x;”,其对应的汇编指令不可能是( C)。

A.movl %edx, %eax

B.movzlq %edx, %rax

C.movsql %rdx, %eax

D.movslq %edx, %rax

7以下是C语言赋值语句“x=a*b+c;”对应的x86-64汇编代码:

movslq  %edx, %rdx
movsbl  %sil, %esi
imull  %edi, %esi
movslq  %esi, %rsi
leaq  (%rdx, %rsi), %rax

已知x、a、b和c分别在RAX、RDI、RSI和RDX对应宽度的寄存器中,根据上述汇编指令序列,推测x、a、b和c的数据类型分别为(D )。

A.x—long, a—long, b—char, c—int

B.x—long, a—long, b—char, c—long

C.x—long, a—int, b—char, c—long

D.x—long, a—int, b—char, c—int

8假定long型变量t、int型变量x和short型变量y分别在RAX、RDI和RSI对应宽度寄存器中,C语言赋值语句“t=(long)(x+y);”对应的x86-64汇编指令序列不可能是( A )。

A.

movswq %si, %rdx

leaq (%rdx, %rdi), %rax

B.

movswq %si, %rax

movslq %edi, %rdx

addq %rdx, %rax

C.

movswq %si, %rsi

movslq %edi, %rdi

leaq (%rsi, %rdi), %rax

D.

movswl %si, %edx

addl %edi, %edx

movslq %edx, %rax

9以下关于x86-64过程调用的叙述中,错误的是( B )。

A.前6个参数采用通用寄存器传递,其余参数通过栈传递

B.在通用寄存器中传递的参数,都存放在64位寄存器中

C.在栈中的参数若是基本类型,则被分配8个字节空间

D.返回参数存放在RAX相应宽度的寄存器中

10以下关于IA-32和x86-64指令系统比较的叙述中,错误的是( D )。

A.对于64位数据,x86-64可用一条指令处理,而IA-32需多条指令处理

B.对于入口参数,x86-64可用通用寄存器传递,而IA-32需用栈来传递

C.对于浮点操作数,x86-64存于128位的XMM中,而IA-32存于80位的ST(i)中

D.对于返回地址,x86-64使用通用寄存器保存,而IA-32使用栈来保存

解释:对于返回地址,x86-64和IA-32都使用栈来保存