手动汇编vs GCC

免责声明:我刚刚开始与x86程序集。 我在大学学到了一点SPIM,但是这并不值得一提。

我以为我从libc,abs()中最简单的函数开始。 在C中非常简单:

long myAbs(long j) { return j < 0 ? -j : j; } 

我的汇编版本:

  .global myAbs .type myAbs, @function .text myAbs: test %rdi, %rdi jns end negq %rdi end: movq %rdi, %rax ret 

(这对32位整数不起作用,可能是因为RAX是一个64位寄存器,标志可能位于错误的位置 – 我必须调查)。

现在这里是gcc所做的(gcc -O2 -S myAbs.c):

  .file "myAbs.c" .section .text.unlikely,"ax",@progbits .LCOLDB0: .text .LHOTB0: .p2align 4,,15 .globl myAbs .type myAbs, @function myAbs: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $4144, %rsp orq $0, (%rsp) addq $4128, %rsp movq %rdi, %rdx sarq $63, %rdx movq %fs:40, %rax movq %rax, -8(%rbp) xorl %eax, %eax movq %rdi, %rax xorq %rdx, %rax subq %rdx, %rax movq -8(%rbp), %rcx xorq %fs:40, %rcx jne .L5 leave .cfi_remember_state .cfi_def_cfa 7, 8 ret .L5: .cfi_restore_state call __stack_chk_fail@PLT .cfi_endproc .LFE0: .size myAbs, .-myAbs .section .text.unlikely .LCOLDE0: .text .LHOTE0: .ident "GCC: (Gentoo Hardened 5.1.0 p1.2, pie-0.6.3) 5.1.0" .section .note.GNU-stack,"",@progbits 

为什么有这么大的差别 海湾合作委员会提供更多的指示 我无法想象这不会比我的代码慢。 我错过了什么吗? 还是我在这里做了一些严重的错误?

对于那些想知道生成的代码来自哪里的人,首先要注意的是,当GCC编译myAbs使用堆栈保护,将其转换成这种forms

 long myAbs(long j) { uintptr_t canary = __stack_chk_guard; register long result = j < 0 ? -j : j; if ( (canary = canary ^ __stack_chk_guard) != 0 ) __stack_chk_fail(); } 

简单地执行j < 0 ? -j : j;的代码 j < 0 ? -j : j;

 movq %rdi, %rdx ;RDX = j movq %rdi, %rax ;RAX = j sarq $63, %rdx ;RDX = 0 if j >=0, 0fff...ffh if j < 0 xorq %rdx, %rax ;Note: x xor 0ff...ffh = Not X, x xor 0 = x ;RAX = j if j >=0, ~j if j < 0 subq %rdx, %rax ;Note: 0fff...ffh = -1 ;RAX = j+0 = j if j >= 0, ~j+1 = -j if j < 0 ;~j+1 = -j in two complement 

分析我们得到的生成的代码

  pushq %rbp movq %rsp, %rbp ;Standard prologue subq $4144, %rsp ;Allocate slight more than 4 KiB orq $0, (%rsp) ;Perform a useless RW operation to test if there is enough stack space for __stack_chk_fail addq $4128, %rsp ;This leave 16 byte allocated for local vars movq %rdi, %rdx ;See above sarq $63, %rdx ;See above movq %fs:40, %rax ;Get the canary movq %rax, -8(%rbp) ;Save it as a local var xorl %eax, %eax ;Clear it movq %rdi, %rax ;See above xorq %rdx, %rax ;See above subq %rdx, %rax ;See above movq -8(%rbp), %rcx ;RCX = Canary xorq %fs:40, %rcx ;Check if equal to the original value jne .L5 ;If not fail leave ret .L5: call __stack_chk_fail@PLT ;__stack_chk_fail is noreturn 

所以所有额外的指令都是为了实现Stack Smashing Protector 。

感谢FUZxxl在序幕之后指出使用第一条指令。

许多开始的调用是设置堆栈并保存返回地址(你没有做的事情)。 似乎有一些堆栈保护正在进行。 也许你可以调整你的编译器设置来摆脱一些开销。

也许向你的编译器添加标志,例如: -fno-stack-protector可以最小化这种差异。

是的,这可能比你的手写汇编慢,但提供了更多的保护,可能是值得的轻微开销。

至于为什么堆栈保护仍然存在,即使它是一个叶function看到这里 。