Back to Top

Linux下C函数参数和返回值的反汇编分析

在Linux环境下,常用的反汇编命令是objdump -Sd file。该命令采用的是AT&T格式,与Intel格式有一些不同之处, 主要的区别是源操作数和目标操作数的顺序是相反的。

测试环境: GCC4.8.5 -O3

基础知识

常见的汇编子程序实现方式是利用栈帧的概念。SP指向栈顶,入栈操作会使SP减少,而出栈操作则会使SP增加。

call MySub              ; 入栈函数返回地址(下一条指令的地址), 跳转到MySub执行
; ...

MySub PROC
    push %ebp           ; 将BP入栈
    mov %esp, %ebp      ; 备份SP(栈顶指针)到BP
    sub $8, %esp        ; 为局部变量保留空间
    ; ...
    mov %ebp, %esp      ; 从堆栈中删除局部变量
    pop %ebp            ; 出栈BP
    ret                 ; 调用call时入栈的函数返回地址
MySub ENDP

int/float 传参

float test_p(int i1, float f1, int i2, float f2)
{
	float k = i1 + i2 + f1 + f2;
	return k;
}

test_p(1, 2, 3, 4);
0000000000000770 <test_p>:
 770:   01 f7                   add    %esi,%edi
 772:   f3 0f 2a d7             cvtsi2ss %edi,%xmm2
 776:   f3 0f 58 d0             addss  %xmm0,%xmm2
 77a:   f3 0f 58 d1             addss  %xmm1,%xmm2
 77e:   0f 28 c2                movaps %xmm2,%xmm0
 781:   c3                      retq   
 782:   0f 1f 40 00             nopl   0x0(%rax)
 786:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
 78d:   00 00 00 

400584:       be 03 00 00 00          mov    $0x3,%esi
400589:       bf 01 00 00 00          mov    $0x1,%edi
40058e:       f3 0f 10 0d ae 01 00    movss  0x1ae(%rip),%xmm1        # 400744 <__dso_handle+0xc>
400595:       00 
400596:       f3 0f 10 05 aa 01 00    movss  0x1aa(%rip),%xmm0        # 400748 <__dso_handle+0x10>
40059d:       00 
40059e:       e8 cd ff ff ff          callq  400570 <test_p@plt>

根据System V AMD64 ABI,前六个整数或指针参数是通过寄存器RDI、RSI、RDX、RCX、R8和R9传递,浮点参数是通过XMM0、XMM1、XMM2、XMM3、XMM4、XMM5和XMM6来传递的。

结构体参数

如果结构体不大于16个字节, 则通过寄存器传值, 否则通过堆栈传值或返回.

小结构体参数

struct small {
	char c1;
	char c2;
	char sz1[14];
};

struct small test_small(int i, struct small in) {
	struct small out;
	memset(&out, i, sizeof(out));
	out.c1 = in.c2;
	return out;
}

struct small in;
struct small out = test_small(5, in);
0000000000000750 <test_small>:
 750:   40 0f b6 d7             movzbl %dil,%edx
 754:   48 b8 01 01 01 01 01    movabs $0x101010101010101,%rax
 75b:   01 01 01 
 75e:   66 c1 ee 08             shr    $0x8,%si
 762:   48 0f af d0             imul   %rax,%rdx
 766:   48 89 54 24 d8          mov    %rdx,-0x28(%rsp)
 76b:   40 88 74 24 d8          mov    %sil,-0x28(%rsp)
 770:   48 8b 44 24 d8          mov    -0x28(%rsp),%rax
 775:   c3                      retq   
 776:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
 77d:   00 00 00 

400584:       bf 05 00 00 00          mov    $0x5,%edi
400589:       48 8b 54 24 08          mov    0x8(%rsp),%rdx
40058e:       48 8b 34 24             mov    (%rsp),%rsi
400592:       e8 a9 ff ff ff          callq  400540 <test_small@plt>
400597:       48 8d 74 24 10          lea    0x10(%rsp),%rsi
40059c:       48 89 44 24 10          mov    %rax,0x10(%rsp)
4005a1:       48 89 54 24 18          mov    %rdx,0x18(%rsp)

in参数通过%rsi、%rdx传入, 返回值通过%rax和%rdx传出

大结构体参数

struct large
{
	char c1;
	char c2;
	char sz1[15];
};

struct large test_large(int i, struct large in)
{
	struct large out;
	memset(&out, i, sizeof(out));
	out.c1 = in.c2;
	return out;
}

struct large in;
struct large out = test_large(5, in);
0000000000000780 <test_large>:
 780:   40 0f b6 d6             movzbl %sil,%edx
 784:   48 b9 01 01 01 01 01    movabs $0x101010101010101,%rcx
 78b:   01 01 01 
 78e:   48 89 f8                mov    %rdi,%rax
 791:   48 0f af d1             imul   %rcx,%rdx
 795:   0f b6 4c 24 09          movzbl 0x9(%rsp),%ecx               ; 0x9(%rsp) 是 in.c2
 79a:   40 88 77 10             mov    %sil,0x10(%rdi)
 79e:   48 89 54 24 d8          mov    %rdx,-0x28(%rsp)
 7a3:   88 4c 24 d8             mov    %cl,-0x28(%rsp)
 7a7:   48 8b 4c 24 d8          mov    -0x28(%rsp),%rcx
 7ac:   48 89 57 08             mov    %rdx,0x8(%rdi)
 7b0:   48 89 0f                mov    %rcx,(%rdi)
 7b3:   c3                      retq  

400580:       48 83 ec 68             sub    $0x68,%rsp
400584:       be 05 00 00 00          mov    $0x5,%esi              ; 参数 int i
400589:       48 8b 44 24 20          mov    0x20(%rsp),%rax
40058e:       48 8d 7c 24 40          lea    0x40(%rsp),%rdi        ; 返回值
400593:       48 89 04 24             mov    %rax,(%rsp)            ; 参数 struct large in
400597:       48 8b 44 24 28          mov    0x28(%rsp),%rax
40059c:       48 89 44 24 08          mov    %rax,0x8(%rsp)         ; 参数 struct large in
4005a1:       0f b6 44 24 30          movzbl 0x30(%rsp),%eax
4005a6:       88 44 24 10             mov    %al,0x10(%rsp)         ; 参数 struct large in
4005aa:       e8 91 ff ff ff          callq  400540 <test_large@plt>

在主调函数中,in变量的地址是0x20(%rsp),out变量的地址是0x40(%rsp), 将out变量作为第一个参数,将函数声明的第一个参数int i作为第二个参数,将函数声明的第二个参数struct large in放入堆栈

本文链接, 未经许可,禁止转载