首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >手工装配与GCC之比

手工装配与GCC之比
EN

Stack Overflow用户
提问于 2015-07-21 12:59:48
回答 2查看 3.4K关注 0票数 37

免责声明:我刚开始使用x86程序集。我在大学里确实学到了一些SPIM,但这一点也不值得一提。

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

代码语言:javascript
复制
long myAbs(long j) {
    return j < 0 ? -j : j;
}

我在汇编中的版本:

代码语言:javascript
复制
    .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):

代码语言:javascript
复制
        .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提出了更多的指令。我无法想象这不会比我的代码慢。我是不是遗漏了什么?还是我在这里做了严重的错事?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-07-21 13:28:52

对于那些想知道生成的代码来自什么的人,首先要注意的是,当GCC用堆栈保护编译myAbs时,它会将它转换成这种形式。

代码语言:javascript
复制
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;的代码是

代码语言:javascript
复制
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

分析生成的代码

代码语言:javascript
复制
    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

所以所有额外的指令都是用来实现堆碎保护器的。

感谢FUZxxl指出序言后的第一个指令的用法。

票数 41
EN

Stack Overflow用户

发布于 2015-07-21 13:10:49

许多开始调用的目的是设置堆栈并保存返回地址(这是您没有做的事情)。似乎有一些堆栈保护正在进行。也许您可以调优编译器设置,以消除一些开销。

也许向编译器添加标记,如:-fno-stack-protector,可以将这种差异降到最低。

是的,这可能比您的手写程序集慢,但是提供了更多的保护,并且可能值得花费一些开销。

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

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31539725

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档