免责声明:我刚开始使用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提出了更多的指令。我无法想象这不会比我的代码慢。我是不是遗漏了什么?还是我在这里做了严重的错事?
发布于 2015-07-21 13:28:52
对于那些想知道生成的代码来自什么的人,首先要注意的是,当GCC用堆栈保护编译myAbs时,它会将它转换成这种形式。
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;的代码是
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所以所有额外的指令都是用来实现堆碎保护器的。
感谢FUZxxl指出序言后的第一个指令的用法。
发布于 2015-07-21 13:10:49
许多开始调用的目的是设置堆栈并保存返回地址(这是您没有做的事情)。似乎有一些堆栈保护正在进行。也许您可以调优编译器设置,以消除一些开销。
也许向编译器添加标记,如:-fno-stack-protector,可以将这种差异降到最低。
是的,这可能比您的手写程序集慢,但是提供了更多的保护,并且可能值得花费一些开销。
至于为什么堆栈保护仍然存在,即使它是一个叶函数请看这里。
https://stackoverflow.com/questions/31539725
复制相似问题