• X86_64函数调用汇编程序分析


    1 X86_64寄存器使用标准

    • %rdi, %rsi, %rdx, %rcx, %r8, %r9分别用于函数调用过程中的前6个参数,对于6的参数存放在栈中传递
    • %rsp用做栈指针寄存器,指向栈顶
    • %rbp用作栈框寄存器,指向栈底
    • %rax用做函数返回值的第一个寄存器

    在这里插入图片描述

    2 对应代码的分析

    2.1 main函数及其对应的汇编程序

    int main(void)
    {
            long a = 1;
            long b = 2;                                                                         
    
            printf("The current function is %s a:%ld b:%ld\r\n", __func__, a, b); 
    
            test_fun_a(a, b, 0, 1); 
    
            a = a + b;
            b = a + b;
    
            return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.1.1 main的C代码实现

    2.1.2 main函数对应汇编及其分析

    这段汇编代码实现了一个简单的程序,其功能如下:

    1. 首先,endbr64指令用于清除处理器的前瞻指令缓存,确保后面的指令正确执行。
    2. push %rbpmov %rsp,%rbp这两个指令用于保存和设置栈帧的基址指针(Base Pointer)。
    3. sub $0x10,%rsp指令用于为局部变量和保存的寄存器值分配栈空间。
    4. movq $0x1,-0x10(%rbp)movq $0x2,-0x8(%rbp)这两个指令用于将字面量1和2存储到栈中的特定位置。
    5. mov -0x8(%rbp),%rdxmov -0x10(%rbp),%rax这两个指令用于从栈中获取之前保存的值。
    6. mov %rdx,%rcxmov %rax,%rdx这两个指令用于将寄存器的值进行传递,为后面的函数调用做准备。
    7. lea 0xdf8(%rip),%rsilea 0xda6(%rip),%rdi这两个指令用于设置printf函数的参数,分别对应格式字符串和要打印的变量。
    8. mov $0x0,%eax指令用于设置系统调用的编号(这里为0,即系统调用号)。
    9. callq 1050 这个指令发起系统调用,执行printf函数,打印输出指定的字符串和数值。
    10. 接下来,mov -0x8(%rbp),%rsimov -0x10(%rbp),%raxmov $0x1,%ecxmov $0x0,%edxmov %rax,%rdi这些指令用于设置函数test_fun_a的参数。
    11. callq 11e8 这个指令调用函数test_fun_a,执行其代码。
    12. 在函数test_fun_a执行完成后,通过一系列的移动和加法操作,将返回值存储回原始的参数位置。
    13. 最后,mov $0x0,%eaxleaveqretq这些指令用于清理栈帧、结束当前函数并返回。

    这段汇编代码的功能是调用函数test_fun_a,并打印输出两个数值1和2,然后调用函数test_fun_a并将返回值存储回原始的参数位置。

    000000000000128a <main>:
        128a:	f3 0f 1e fa          	endbr64 
        128e:	55                   	push   %rbp
        128f:	48 89 e5             	mov    %rsp,%rbp
        1292:	48 83 ec 10          	sub    $0x10,%rsp
        1296:	48 c7 45 f0 01 00 00 	movq   $0x1,-0x10(%rbp)
        129d:	00 
        129e:	48 c7 45 f8 02 00 00 	movq   $0x2,-0x8(%rbp)
        12a5:	00 
        12a6:	48 8b 55 f8          	mov    -0x8(%rbp),%rdx
        12aa:	48 8b 45 f0          	mov    -0x10(%rbp),%rax
        12ae:	48 89 d1             	mov    %rdx,%rcx
        12b1:	48 89 c2             	mov    %rax,%rdx
        12b4:	48 8d 35 f8 0d 00 00 	lea    0xdf8(%rip),%rsi        # 20b3 <__func__.2519>
        12bb:	48 8d 3d a6 0d 00 00 	lea    0xda6(%rip),%rdi        # 2068 <_IO_stdin_used+0x68>
        12c2:	b8 00 00 00 00       	mov    $0x0,%eax
        12c7:	e8 84 fd ff ff       	callq  1050 <printf@plt>
        12cc:	48 8b 75 f8          	mov    -0x8(%rbp),%rsi
        12d0:	48 8b 45 f0          	mov    -0x10(%rbp),%rax
        12d4:	b9 01 00 00 00       	mov    $0x1,%ecx
        12d9:	ba 00 00 00 00       	mov    $0x0,%edx
        12de:	48 89 c7             	mov    %rax,%rdi
        12e1:	e8 02 ff ff ff       	callq  11e8 <test_fun_a>
        12e6:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
        12ea:	48 01 45 f0          	add    %rax,-0x10(%rbp)
        12ee:	48 8b 45 f0          	mov    -0x10(%rbp),%rax
        12f2:	48 01 45 f8          	add    %rax,-0x8(%rbp)
        12f6:	b8 00 00 00 00       	mov    $0x0,%eax
        12fb:	c9                   	leaveq 
        12fc:	c3                   	retq   
        12fd:	0f 1f 00             	nopl   (%rax)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    2.1.3 执行完成之后栈的存放情况

    2.1.3.1 main函数执行开栈以及局部变量压栈之后的栈结构

    在这里插入图片描述

    2.1.3.2 main函数调用test_fun_a时的栈结构
    • 执行callq 11e8 时会把调用test_fun_a的返回地址压到栈顶,会导致%rsp向下偏移8Byte

    在这里插入图片描述

    2.2 test_fun_a函数及其对应的汇编程序

    2.2.1 test_fun_a函数的C实现

    void test_fun_a(long m, long n, long x, long y)
    {
            long a = x;
            long b = 2;
            long c = 3;
    
            printf("The current function is %s b:%ld c:%ld\r\n", __func__, b, c); 
    
            test_fun_b(b, c, a, 2); 
    
            b = b + c + m;
            c = b + c + n;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2.2.2 test_fun_a函数对应汇编及其分析

    这段汇编代码是一个函数test_fun_a的实现,其功能大致如下:

    1. push %rbpmov %rsp,%rbp 用于保存和设置栈帧的基址指针(Base Pointer)。
    2. sub $0x40,%rsp 用于为局部变量和保存的寄存器值分配栈空间。
    3. mov %rdi,-0x28(%rbp)mov %rsi,-0x30(%rbp)mov %rdx,-0x38(%rbp)mov %rcx,-0x40(%rbp) 用于将参数传递到栈帧中的指定位置。
    4. mov %rax,-0x18(%rbp) 将某个值(可能是函数内的临时变量或计算结果)保存到栈帧的另一个位置。
    5. movq $0x2,-0x10(%rbp)movq $0x3,-0x8(%rbp) 用于将字面量值2和3存储到栈中的特定位置。
    6. mov -0x8(%rbp),%rdxmov -0x10(%rbp),%rax 用于从栈中获取之前保存的值。
    7. lea 0xe77(%rip),%rsilea 0xe00(%rip),%rdi 用于设置printf函数的参数,分别对应格式字符串和要打印的变量。
    8. mov $0x0,%eax 用于设置系统调用的编号(这里为0,即系统调用号)。
    9. callq 1050 发起系统调用,执行printf函数,打印输出指定的字符串和数值。
    10. mov -0x18(%rbp),%rdxmov -0x8(%rbp),%rsimov -0x10(%rbp),%raxmov $0x2,%ecxmov %rax,%rdi 用于设置函数test_fun_b的参数。
    11. callq 1149 调用函数test_fun_b,执行其代码。
    12. 在函数test_fun_b执行完成后,将返回值存储到栈帧的特定位置。
    13. 最后,通过一系列的移动和加法操作,将计算结果存储回原始的第一个参数的位置。
    14. 48 8b 45 d0 mov -0x30(%rbp),%rax: 将栈帧中偏移为-0x30的位置的值加载到寄存器rax中。
    15. 48 01 d0 add %rdx,%rax: 将rax和rdx寄存器的值相加,并将结果存储回rax寄存器中。
    16. 48 89 45 f8 mov %rax,-0x8(%rbp): 将rax寄存器的值存储回栈帧中偏移为-0x8的位置。
    17. 90: 无操作,用于填充指令。
    18. c9: leaveq指令用于撤销栈帧,恢复调用前的堆栈状态。
    19. c3: retq指令用于从当前函数返回,返回到调用者的代码位置。
    00000000000011e8 <test_fun_a>:
        11e8:	f3 0f 1e fa          	endbr64 
        11ec:	55                   	push   %rbp
        11ed:	48 89 e5             	mov    %rsp,%rbp
        11f0:	48 83 ec 40          	sub    $0x40,%rsp
        11f4:	48 89 7d d8          	mov    %rdi,-0x28(%rbp)
        11f8:	48 89 75 d0          	mov    %rsi,-0x30(%rbp)
        11fc:	48 89 55 c8          	mov    %rdx,-0x38(%rbp)
        1200:	48 89 4d c0          	mov    %rcx,-0x40(%rbp)
        1204:	48 8b 45 c8          	mov    -0x38(%rbp),%rax
        1208:	48 89 45 e8          	mov    %rax,-0x18(%rbp)
        120c:	48 c7 45 f0 02 00 00 	movq   $0x2,-0x10(%rbp)
        1213:	00 
        1214:	48 c7 45 f8 03 00 00 	movq   $0x3,-0x8(%rbp)
        121b:	00 
        121c:	48 8b 55 f8          	mov    -0x8(%rbp),%rdx
        1220:	48 8b 45 f0          	mov    -0x10(%rbp),%rax
        1224:	48 89 d1             	mov    %rdx,%rcx
        1227:	48 89 c2             	mov    %rax,%rdx
        122a:	48 8d 35 77 0e 00 00 	lea    0xe77(%rip),%rsi        # 20a8 <__func__.2513>
        1231:	48 8d 3d 00 0e 00 00 	lea    0xe00(%rip),%rdi        # 2038 <_IO_stdin_used+0x38>
        1238:	b8 00 00 00 00       	mov    $0x0,%eax
        123d:	e8 0e fe ff ff       	callq  1050 <printf@plt>
        1242:	48 8b 55 e8          	mov    -0x18(%rbp),%rdx
        1246:	48 8b 75 f8          	mov    -0x8(%rbp),%rsi
        124a:	48 8b 45 f0          	mov    -0x10(%rbp),%rax
        124e:	b9 02 00 00 00       	mov    $0x2,%ecx
        1253:	48 89 c7             	mov    %rax,%rdi
        1256:	e8 ee fe ff ff       	callq  1149 <test_fun_b>
        125b:	48 8b 55 f0          	mov    -0x10(%rbp),%rdx
        125f:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
        1263:	48 01 c2             	add    %rax,%rdx
        1266:	48 8b 45 d8          	mov    -0x28(%rbp),%rax
        126a:	48 01 d0             	add    %rdx,%rax
        126d:	48 89 45 f0          	mov    %rax,-0x10(%rbp)
        1271:	48 8b 55 f0          	mov    -0x10(%rbp),%rdx
        1275:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
        1279:	48 01 c2             	add    %rax,%rdx
        127c:	48 8b 45 d0          	mov    -0x30(%rbp),%rax
        1280:	48 01 d0             	add    %rdx,%rax
        1283:	48 89 45 f8          	mov    %rax,-0x8(%rbp)
        1287:	90                   	nop
        1288:	c9                   	leaveq 
        1289:	c3                   	retq
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    2.2.3 执行完成之后栈帧的使用情况

    2.2.3.1 test_fun_a函数执行开栈之后的栈结构示意图

    在这里插入图片描述

    2.2.3.2 test_fun_a函数执行参数压栈以及局部变量压栈之后的栈结构示意图

    在这里插入图片描述

    2.2.3.3 test_fun_a函数调用test_fun_b之后的栈结构示意图
    • callq 1149 该调指令会导致%rsp向下偏移8Byte以保存test_fun_a函数调用test_fun_b的返回地址

    在这里插入图片描述

    2.3 test_fun_b函数及其对应的汇编程序

    2.3.1 test_func_b函数的C实现

    void test_fun_b(long m, long n, long x, long y)
    {
            long a = y;
            long b = m;
            long c = 3;
            long d = 4;
    
            printf("The current function is %s c:%ld d:%ld\r\n", __func__, c, d);
    
            c = c + d + m + a;
            d = c + d + n + b;                                                                  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.3.2 test_fun_b函数对应汇编及其分析

    这段汇编代码是一个函数test_fun_b的实现。下面是对代码的逐行解释:

    1149: f3 0f 1e fa - endbr64指令用于结束64位BR(Branch Prediction)指令的预测。

    114d: 55 - push %rbp将当前栈帧的基址指针(Base Pointer,简称RBp)压入栈中,为新的栈帧做准备。

    114e: 48 89 e5 - mov %rsp,%rbp将当前栈帧的栈指针(Stack Pointer,简称SP)复制给基址指针(RBp),建立新的栈帧。

    1151: 48 83 ec 40 - sub $0x40,%rsp从SP中减去0x40个字节,扩展栈空间。

    1155: 48 89 7d d8 - mov %rdi,-0x28(%rbp)将函数参数rdi的值存储到当前栈帧的-0x28位置。

    1159: 48 89 75 d0 - mov %rsi,-0x30(%rbp)将函数参数rsi的值存储到当前栈帧的-0x30位置。

    115d: 48 89 55 c8 - mov %rdx,-0x38(%rbp)将函数参数rdx的值存储到当前栈帧的-0x38位置。

    1161: 48 89 4d c0 - mov %rcx,-0x40(%rbp)将函数参数rcx的值存储到当前栈帧的-0x40位置。

    1165: 48 8b 45 c0 - mov -0x40(%rbp),%rax将当前栈帧的-0x40位置的值加载到寄存器rax中。

    1169: 48 89 45 e0 - mov %rax,-0x20(%rbp)将寄存器rax的值存储到当前栈帧的-0x20位置。

    116d: 48 8b 45 d8 - mov -0x28(%rbp),%rax将当前栈帧的-0x28位置的值加载到寄存器rax中。

    1171: 48 89 45 e8 - mov %rax,-0x18(%rbp)将寄存器rax的值存储到当前栈帧的-0x18位置。

    1175: 48 c7 45 f0 03 00 00 00 - movq $0x3,-0x10(%rbp)将立即数0x3存储到当前栈帧的-0x10位置。
    117d: 48 c7 45 f8 04 00 00 00 - movq $0x4,-0x8(%rbp)将立即数0x4存储到当前栈帧的-0x8位置。

    1185: 48 8b 55 f8 - mov -0x8(%rbp),%rdx将当前栈帧的-0x8位置的值加载到寄存器rdx中。

    1189: 48 8b 45 f0 - mov -0x10(%rbp),%rax将当前栈帧的-0x10位置的值加载到寄存器rax中。

    118d: 48 89 d1 - mov %rdx,%rcx将寄存器rdx的值复制给rcx。

    1190: 48 89 c2 - mov %rax,%rdx将寄存器rax的值复制给rdx。

    1193: 48 8d 35 fe 0e 00 00 - lea 0xefe(%rip),%rsi将相对地址0xefe处的值加载到寄存器rsi。

    119a: 48 8d 3d 67 0e 00 00 - lea 0xe67(%rip),%rdi将相对地址0xe67处的值加载到寄存器rdi。

    11a1: b8 00 00 00 00 - mov $0x0,%eax将立即数0x0加载到寄存器eax。

    11a6: e8 a5 fe ff ff - callq 1050 调用函数plt的printf函数,跳转到地址1050处执行。

    接下来的指令继续处理计算结果,并执行一些算术操作。

    11ab: 48 8b 55 f0 - mov -0x10(%rbp),%rdx将当前栈帧的-0x10位置的值加载到寄存器rdx中。

    11af: 48 8b 45 f8 - mov -0x8(%rbp),%rax将当前栈帧的-0x8位置的值加载到寄存器rax中。

    11b3: 48 01 c2 - add %rax,%rdx将寄存器rax和rdx的值相加,结果存储在rdx中。

    11b6: 48 8b 45 d8 - mov -0x28(%rbp),%rax将当前栈帧的-0x28位置的值加载到寄存器rax中。

    11bd: 48 01 c2 - add %rax,%rdx将寄存器rax和rdx的值相加,结果存储在rdx中。

    11c1: 48 8b 45 e0 - mov -0x20(%rbp),%rax将当前栈帧的-0x20位置的值加载到寄存器rax中。

    11c4: 48 01 d0 - add %rdx,%rax将寄存器rdx和rax的值相加,结果存储在rax中。

    11c8: 48 89 45 f0 - mov %rax,-0x10(%rbp)将寄存器rax的值存储到当前栈帧的-0x10位置。

    11cc: 48 8b 45 f8 mov -0x8(%rbp),%rax将当前栈帧的-0x8位置的值加载到rax寄存器中

    11d0: 48 01 c2 add %rax,%rdx将寄存器rax和rdx的值相加,结果存储在rdx中。

    11d3: 48 8b 45 d0 mov -0x30(%rbp),%rax将当前栈帧的-0x30位置的值加载到rax寄存器中

    11d7: 48 01 c2 add %rax,%rdx 将寄存器rax和rdx的值相加,结果存储在rdx中。

    11da: 48 8b 45 e8 mov -0x18(%rbp),%rax将当前栈帧的-0x18位置的值加载到rax寄存器中

    11de: 48 01 d0 add %rdx,%rax 将寄存器rdx和rax的值相加,结果存储在rax中。

    11e1: 48 89 45 f8 mov %rax,-0x8(%rbp)将寄存器rax的值存储到当前栈帧的-0x8位置。

    11e5: 90 nop

    11e6: c9 leaveq 撤销栈帧,恢复调用前的堆栈状态。

    11e7: c3 retq从当前函数返回,返回到调用者的代码位置。

    0000000000001149 <test_fun_b>:
        1149:	f3 0f 1e fa          	endbr64 
        114d:	55                   	push   %rbp
        114e:	48 89 e5             	mov    %rsp,%rbp
        1151:	48 83 ec 40          	sub    $0x40,%rsp
        1155:	48 89 7d d8          	mov    %rdi,-0x28(%rbp)
        1159:	48 89 75 d0          	mov    %rsi,-0x30(%rbp)
        115d:	48 89 55 c8          	mov    %rdx,-0x38(%rbp)
        1161:	48 89 4d c0          	mov    %rcx,-0x40(%rbp)
        1165:	48 8b 45 c0          	mov    -0x40(%rbp),%rax
        1169:	48 89 45 e0          	mov    %rax,-0x20(%rbp)
        116d:	48 8b 45 d8          	mov    -0x28(%rbp),%rax
        1171:	48 89 45 e8          	mov    %rax,-0x18(%rbp)
        1175:	48 c7 45 f0 03 00 00 	movq   $0x3,-0x10(%rbp)
        117c:	00 
        117d:	48 c7 45 f8 04 00 00 	movq   $0x4,-0x8(%rbp)
        1184:	00 
        1185:	48 8b 55 f8          	mov    -0x8(%rbp),%rdx
        1189:	48 8b 45 f0          	mov    -0x10(%rbp),%rax
        118d:	48 89 d1             	mov    %rdx,%rcx
        1190:	48 89 c2             	mov    %rax,%rdx
        1193:	48 8d 35 fe 0e 00 00 	lea    0xefe(%rip),%rsi        # 2098 <__func__.2503>
        119a:	48 8d 3d 67 0e 00 00 	lea    0xe67(%rip),%rdi        # 2008 <_IO_stdin_used+0x8>
        11a1:	b8 00 00 00 00       	mov    $0x0,%eax
        11a6:	e8 a5 fe ff ff       	callq  1050 <printf@plt>
        11ab:	48 8b 55 f0          	mov    -0x10(%rbp),%rdx
        11af:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
        11b3:	48 01 c2             	add    %rax,%rdx
        11b6:	48 8b 45 d8          	mov    -0x28(%rbp),%rax
        11ba:	48 01 c2             	add    %rax,%rdx
        11bd:	48 8b 45 e0          	mov    -0x20(%rbp),%rax
        11c1:	48 01 d0             	add    %rdx,%rax
        11c4:	48 89 45 f0          	mov    %rax,-0x10(%rbp)
        11c8:	48 8b 55 f0          	mov    -0x10(%rbp),%rdx
        11cc:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
        11d0:	48 01 c2             	add    %rax,%rdx
        11d3:	48 8b 45 d0          	mov    -0x30(%rbp),%rax
        11d7:	48 01 c2             	add    %rax,%rdx
        11da:	48 8b 45 e8          	mov    -0x18(%rbp),%rax
        11de:	48 01 d0             	add    %rdx,%rax
        11e1:	48 89 45 f8          	mov    %rax,-0x8(%rbp)
        11e5:	90                   	nop
        11e6:	c9                   	leaveq 
        11e7:	c3                   	retq
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    2.3.3 执行完成之后栈帧的使用情况

    2.3.3.1 test_fun_b函数执行开栈之后的栈结构示意图

    在这里插入图片描述

    2.3.3.2 test_fun_a函数执行参数压栈以及局部变量压栈之后的栈结构示意图

    在这里插入图片描述

    3 X86_64 函数调用示例及其栈帧示意图

    在这里插入图片描述

    4 编译和反汇编的命令

    4.1 编译的命令

    x86_64-linux-gnu-gcc  -Wl,--no-as-needed main.c -o x86_test
    
    • 1

    4.2 反汇编的命令

    若是想把生成的反汇编程序保存的文件可以使用这个反汇编的命令:x86_64-linux-gnu-objdump -S -d x86_test > x86_64_test.S

    x86_64-linux-gnu-objdump -S -d x86_test
    
    • 1
  • 相关阅读:
    Java中restTemplate的使用
    一次性全讲透GaussDB(DWS)锁的问题
    进程调度的基本过程
    【软件设计师-从小白到大牛】上午题基础篇:第二章 操作系统
    深度解读UUID:结构、原理以及生成机制
    properties文件
    汇编语言设计
    一文概览NLP句法分析:从理论到PyTorch实战解读
    警惕.Elbie[thekeyishere@cock.li].Elbie勒索病毒,您需要知道的预防和恢复方法。
    啃完这35个Java技术栈,冲刺年薪百万
  • 原文地址:https://blog.csdn.net/u014100559/article/details/132746464