Decrementing stack by 24 when only 8 bytes are needed? - c

I have the C code:
long fib(long n) {
if (n < 2) return 1;
return fib(n-1) + fib(n-2);
}
int main(int argc, char** argv) {
return 0;
}
which I compiled by running gcc -O0 -fno-optimize-sibling-calls -S file.c yielding assembly code that has not been optimized:
.file "long.c"
.text
.globl fib
.type fib, #function
fib:
.LFB5:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
pushq %rbx
subq $24, %rsp
.cfi_offset 3, -24
movq %rdi, -24(%rbp)
cmpq $1, -24(%rbp)
jg .L2
movl $1, %eax
jmp .L3
.L2:
movq -24(%rbp), %rax
subq $1, %rax
movq %rax, %rdi
call fib
movq %rax, %rbx
movq -24(%rbp), %rax
subq $2, %rax
movq %rax, %rdi
call fib
addq %rbx, %rax
.L3:
addq $24, %rsp
popq %rbx
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE5:
.size fib, .-fib
.globl main
.type main, #function
main:
.LFB6:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movq %rsi, -16(%rbp)
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE6:
.size main, .-main
.ident "GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0"
.section .note.GNU-stack,"",#progbits
My question is:
Why do we decrement the stack pointer by 24, subq $24, %rsp? As I see it, we store one element only, first argument n in %rdi, on the stack after the initial two pushes. So why don't we just decrement the stack pointer by 8 and then move n to -8(%rbp)? So
subq $8, %rsp
movq %rdi, -8(%rbp)

GCC does not fully optimize with -O0, not even its stack use. (This may aid in debugging by making some of its use of the stack more transparent to humans. For example, objects a, b, and c may share a single stack location if their active lifetimes (defined by uses in the program, not by the model of lifetime in the C standard) with -O3, but may have separately reserved places in the stack with -O0, and that makes it easier for a human to see where a, b, and c are used in the assembly code. The wasted 16 bytes may be a side effect of this, as those spaces may be reserved for some purpose that this small function did not happen to use, such as space to save certain registers if needed.)
Changing optimization to -O3 results in GCC subtracting only eight from the stack pointer.

Related

Putting a local array changes assembly output for X86-64

When we have just an int variable in main:
int main() {
int d;
return 0;
}
Following code is generated for x86-64 on Linux by gcc -S test.c.
.file "test.c"
.text
.globl main
.type main, #function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (GNU) 6.3.1 20170109"
.section .note.GNU-stack,"",#progbits
Putting an array as local variable
int main() {
int d[2];
return 0;
}
generates a lot of extraneous code at the beginning which I am not able to comprehend.
.file "test.c"
.text
.globl main
.type main, #function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
leaq -4144(%rsp), %rsp
orq $0, (%rsp)
leaq 4128(%rsp), %rsp
movq %fs:40, %rax
movq %rax, -8(%rbp)
xorl %eax, %eax
movl $0, %eax
movq -8(%rbp), %rdx
xorq %fs:40, %rdx
je .L3
call __stack_chk_fail#PLT
.L3:
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (GNU) 6.3.1 20170109"
.section .note.GNU-stack,"",#progbits
Specifically, what are these instructions doing?
leaq -4144(%rsp), %rsp
orq $0, (%rsp)
leaq 4128(%rsp), %rsp
movq %fs:40, %rax
movq %rax, -8(%rbp)
xorl %eax, %eax
movl $0, %eax

Understanding pointer assignment in x86-64 Assembly Code

I am trying to understand assembly code. I am stuck in the portion where the pointer is assigned and the code after leaq command
This is my C code:
#include <stdio.h>
#include<stdlib.h>
int main(){
int x=50;
int *y=&x;
return 0;
}
This is my corresponding ASSEMBLY code:
.file "AssemlyCode.c"
.def __main; .scl 2; .type 32; .endef
.text
.globl main
.def main; .scl 2; .type 32; .endef
.seh_proc main
main:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
subq $48, %rsp
.seh_stackalloc 48
.seh_endprologue
call __main
movl $50, -12(%rbp)
leaq -12(%rbp), %rax
movq %rax, -8(%rbp)
movl $0, %eax
addq $48, %rsp
popq %rbp
ret
.seh_endproc
.ident "GCC: (GNU) 5.4.0"
leaq -8(%rbp), %rax
movl %eax, -4(%rbp)
movl $0, %eax
addq $48, %rsp
popq %rbp
ret
leaq saves address of variable x on the stack to register rax. Variable x is automatic variable on the stack, hence it address is calculated as offset from register that holds stack frame pointer(rbp).
movl eax to stack saves argc argument to the stack.
next step is to put return value in eax register from main function(return 0)
two next opcodes are function epilogue - you are cleaning up used stack and restore previous frame pointer register.
and the last one instruction is simple return.

Manual Assembly vs GCC

Disclaimer: I'm just starting out with x86 assembly. I did learn a bit of SPIM at university, but that's hardly worth mentioning.
I thought I start with what's probably the most simple function in libc, abs(). Pretty straightforward in C:
long myAbs(long j) {
return j < 0 ? -j : j;
}
My version in assembly:
.global myAbs
.type myAbs, #function
.text
myAbs:
test %rdi, %rdi
jns end
negq %rdi
end:
movq %rdi, %rax
ret
(This doesn't work for 32bit integers, probably because RAX is a 64bit register and the sign is probably at the wrong position - I have to investigate that).
Now here's what gcc does (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
Why this big difference? GCC produces substantially more instructions. I can't imagine that this won't be slower than my code.
Am I missing something? Or am I doing something seriousely wrong here?
For those who wonder what the generated code comes from, first note that when GCC compile myAbs with stack protection it transform it into this form
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();
}
The code to simply perform j < 0 ? -j : j; is
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
Analyzing the generated code we get
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
So all the extra instructions are for implementing the Stack Smashing Protector.
Thanks to FUZxxl for pointing out the use of the first instructions after the prologue.
Many of the beginning calls are to setup the stack and save the return address (something which you are not doing). Seems like theres are some stack protection going on. Perhaps you could tune your compiler settings to get rid of some overhead.
Perhaps adding flags to you compiler such as: -fno-stack-protector could minimise this difference.
Yes this probably is slower than your handwritten assembly, but offers much more protection and is probably worth the slight overhead.
As for why the stack protection still exists even though it is a leaf function see here.

Atomic load and store functions produce same assembly code as non-atomic load and store

Why is the assembly output of store_idx_x86() the same as store_idx() and load_idx_x86() the same as load_idx()?
It was my understanding that __atomic_load_n() would flush the core's invalidation queue, and __atomic_store_n() would flush the core's store buffer.
Note -- I complied with: gcc (GCC) 4.8.2 20140120 (Red Hat 4.8.2-16)
Update: I understand that x86 will never reorder stores with other stores and loads with other loads -- so is gcc smart enough to implement sfence and lfence only when it is needed or should using __atomic_ result in a fence (assuming a memory model stricter than __ATOMIC_RELAXED)?
Code
#include <stdint.h>
inline void store_idx_x86(uint64_t* dest, uint64_t idx)
{
*dest = idx;
}
inline void store_idx(uint64_t* dest, uint64_t idx)
{
__atomic_store_n(dest, idx, __ATOMIC_RELEASE);
}
inline uint64_t load_idx_x86(uint64_t* source)
{
return *source;
}
inline uint64_t load_idx(uint64_t* source)
{
return __atomic_load_n(source, __ATOMIC_ACQUIRE);
}
Assembly:
.file "util.c"
.text
.globl store_idx_x86
.type store_idx_x86, #function
store_idx_x86:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp)
movq %rsi, -16(%rbp)
movq -8(%rbp), %rax
movq -16(%rbp), %rdx
movq %rdx, (%rax)
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size store_idx_x86, .-store_idx_x86
.globl store_idx
.type store_idx, #function
store_idx:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp)
movq %rsi, -16(%rbp)
movq -8(%rbp), %rax
movq -16(%rbp), %rdx
movq %rdx, (%rax)
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size store_idx, .-store_idx
.globl load_idx_x86
.type load_idx_x86, #function
load_idx_x86:
.LFB2:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp)
movq -8(%rbp), %rax
movq (%rax), %rax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2:
.size load_idx_x86, .-load_idx_x86
.globl load_idx
.type load_idx, #function
load_idx:
.LFB3:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp)
movq -8(%rbp), %rax
movq (%rax), %rax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE3:
.size load_idx, .-load_idx
.ident "GCC: (GNU) 4.8.2 20140120 (Red Hat 4.8.2-16)"
.section .note.GNU-stack,"",#progbits
Why is the assembly output of store_idx_x86() the same as store_idx() and load_idx_x86() the same as load_idx()?
On x86, assuming compiler-enforced alignment, they are the same operations. Loads and Stores to aligned addresses of the native size or smaller are guaranteed to be atomic. Reference Intel manual vol 3A, 8.1.1:
The Pentium processor (and newer processors since) guarantees that the following additional memory operations
will always be carried out atomically: Reading or writing a quadword aligned on a 64-bit boundary [...]
Furthermore, x86 enforces a strongly ordered memory model, meaning every store and load has implicit release and acquire semantics, respectively.
Lastly, the fencing instructions you mention are only required when using Intel's non-temporal SSE instructions (great reference here), or when needing to create a store-load fence (article here) (and that one is the mfence or lock instruction actually).
Aside: I was curious about that statement in Intel's manuals, so I devised a test program. Frustratingly, on my computer (2 core i3-4030U), I get this output from it:
unaligned
4265292 / 303932066 | 1.40337%
unaligned, but in same cache line
2373 / 246957659 | 0.000960893%
aligned (8 byte)
0 / 247097496 | 0%
Which seems to violate what Intel says. I will investigate. In the meantime, you should clone that demo program and see what it gives you. You just need -std=c++11 ... -pthread on linux.

Incrementing a variable through embedded assembly language

I am trying to understand how to embed assembly language in C (using gcc on x86_64 architecture). I wrote this program to increment the value of a single variable. But I am getting garbage value as output. And ideas why?
#include <stdio.h>
int main(void) {
int x;
x = 4;
asm("incl %0": "=r"(x): "r0"(x));
printf("%d", x);
return 0;
}
Thanks
Update The program is giving expected result on gcc 4.8.3 but not on gcc 4.6.3. I am pasting the assembly output of the non-working code:
.file "abc.c"
.section .rodata
.LC0:
.string "%d"
.text
.globl main
.type main, #function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
pushq %rbx
subq $24, %rsp
movl $4, -20(%rbp)
movl -20(%rbp), %eax
incl %edx
movl %edx, %ebx
.cfi_offset 3, -24
movl %ebx, -20(%rbp)
movl $.LC0, %eax
movl -20(%rbp), %edx
movl %edx, %esi
movq %rax, %rdi
movl $0, %eax
call printf
movl $0, %eax
addq $24, %rsp
popq %rbx
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
.section .note.GNU-stack,"",#progbits
You don't need to say x twice; once is sufficient:
asm("incl %0": "+r"(x));
The +r says that the value will be input and output.
Your way, with separate inputs and output registers, requires that you take the input from %1, add one, and write the output to %0, but you can't do that with incl.
The reason it works on some compilers is because GCC is free to allocate both %0 and %1 to the same register, and appears to have done so in those cases, but it does not have to. Incidentally, if you want to prevent GCC allocating an input and output to the same register (say, if you want to initialize the output before using the input to calculate a final output), you need to use the & modifier.
The documentation for the modifiers is here.

Resources