0

I was studying one of my courses when I ran into a specific exercise that I cannot seem to resolve... It is pretty basic because I am VERY new to assembly. So lets begin.

I have a C function

unsigned int func(int *ptr, unsigned int j) {
    unsigned int res = j;   
    int i = ptr[j+1];

    for(; i<8; ++i) {
        res >>= 1;
    }
    return res;
}

I translated it with gcc to assembly

.file   "func.c"
.intel_syntax noprefix
.text
.globl  func
.type   func, @function
func:
.LFB0:
.cfi_startproc
push    rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    mov rbp, rsp
    .cfi_def_cfa_register 6
    mov QWORD PTR [rbp-24], rdi
    mov DWORD PTR [rbp-28], esi
    mov eax, DWORD PTR [rbp-28]
    mov DWORD PTR [rbp-8], eax
    mov eax, DWORD PTR [rbp-28]
    add eax, 1
    mov eax, eax
    lea rdx, [0+rax*4]
    mov rax, QWORD PTR [rbp-24]
    add rax, rdx
    mov eax, DWORD PTR [rax]
    mov DWORD PTR [rbp-4], eax
    jmp .L2
.L3:
    shr DWORD PTR [rbp-8]
    add DWORD PTR [rbp-4], 1
.L2:
    cmp DWORD PTR [rbp-4], 7
    jle .L3
    mov eax, DWORD PTR [rbp-8]
    pop rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   func, .-func
    .ident  "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4"
    .section    .note.GNU-stack,"",@progbits

The question is as follow. what is the command that place j (variable in the c function) on top of the stack?

I sincerely cannot find out please enlighten me XD.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • Look into `cfi_def_cfa_offset` .. I think this will give you big hint at what you're looking for. – Burstful Oct 18 '17 at 23:35
  • `rbp` is the base pointer, which points to the base of the current stack frame, and `rsp` is the stack pointer, which points to the top of the current stack frame. – Burstful Oct 18 '17 at 23:38
  • If you literally just look into each line one by one, you will figure this out @VladimirVonMartel – Burstful Oct 18 '17 at 23:38
  • "what is the command that place j on top of the stack" - that's easy, `j` is never on top of the stack. Are you sure that's the question? Did you just mean places it on the stack (not necessarily at the top)? You can find that, yeah. But don't bother with the `cfi...` stuff, instead, read about the [calling convention](https://en.wikipedia.org/wiki/X86_calling_conventions#System_V_AMD64_ABI) that tells you how `j` is passed into the function. – Jester Oct 18 '17 at 23:39
  • J is on the stack. – Burstful Oct 18 '17 at 23:40
  • I'm voting to close this question as off-topic because this seems to be a homework problem rather than a programming problem. – David Hoelzer Oct 18 '17 at 23:41
  • to an extent it is im learning basics of assembly for a job i'll close it later this evening – Vladimir Von Martel Oct 18 '17 at 23:42
  • I think he is mostly concerned that you asked a question without showing you tried to answer it first yourself. @VladimirVonMartel – Burstful Oct 18 '17 at 23:44
  • Related: https://stackoverflow.com/questions/38552116/how-to-remove-noise-from-gcc-clang-assembly-output. It's actually easier to read output from `gcc -Og` or `-O1`, because it's not spilling / reloading everything to memory around every C statement. So it's closer to what a human might write to implement the whole simple function. – Peter Cordes Oct 19 '17 at 03:03

3 Answers3

1

The variable j is the second parameter for func; it is stored in the register esi in the x86-64 System V ABI calling convention. This instruction mov DWORD PTR [rbp-28], esi put j into the stack.

You can see it very clearly by writing a simple function that calls "func" and compiling it with -O0 (or with -O2 and marking it as noinline, or only providing a prototype so there's nothing for the compiler to inline).

unsigned int func(int *ptr, unsigned int j) {
    unsigned int res = j;   
    int i = ptr[j+1];

    for(; i<8; ++i) {
        res >>= 1;
    }
    return res;
}

int main()
{
    int a = 1;
    int array[10];    
    func (array, a);
    return 0;
}

Using the Godbolt compiler explorer, we can easily get gcc -O0 -fverbose-asm assembly output. Please focus on the following instructions:

# in main:
...
mov DWORD PTR [rbp-4], 1
mov edx, DWORD PTR [rbp-4]
...
mov esi, edx
...


func(int*, unsigned int):
...
mov DWORD PTR [rbp-28], esi   # j, j
...

j, j is a comment added by gcc -fverbose-asm tell you that the source and destination operands are both the C variable j in that instruction.

The full assembly instructions:

func(int*, unsigned int):
  push rbp
  mov rbp, rsp
  mov QWORD PTR [rbp-24], rdi
  mov DWORD PTR [rbp-28], esi
  mov eax, DWORD PTR [rbp-28]
  mov DWORD PTR [rbp-4], eax
  mov eax, DWORD PTR [rbp-28]
  add eax, 1
  mov eax, eax
  lea rdx, [0+rax*4]
  mov rax, QWORD PTR [rbp-24]
  add rax, rdx
  mov eax, DWORD PTR [rax]
  mov DWORD PTR [rbp-8], eax
  jmp .L2
.L3:
  shr DWORD PTR [rbp-4]
  add DWORD PTR [rbp-8], 1
.L2:
  cmp DWORD PTR [rbp-8], 7
  jle .L3
  mov eax, DWORD PTR [rbp-4]
  pop rbp
  ret
main:
  push rbp
  mov rbp, rsp
  sub rsp, 48
  mov DWORD PTR [rbp-4], 1
  mov edx, DWORD PTR [rbp-4]
  lea rax, [rbp-48]
  mov esi, edx
  mov rdi, rax
  call func(int*, unsigned int)
  mov eax, 0
  leave
  ret
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Forward
  • 855
  • 7
  • 12
  • `gcc -O2` output is significantly easier to read. I included both `-O0` and `-O2` panes in the godbolt link I added to your question. (You can link to code on Godbolt with the "share" button, instead of linking just the front page.) – Peter Cordes Oct 19 '17 at 22:21
  • Thanks Peter for enriching my answer and fixing my problem in English grammar. BTW, my first question in stackoverflow is answered by you :) – Forward Oct 20 '17 at 01:03
0

Taking into account these instructions

mov eax, DWORD PTR [rbp-28]
add eax, 1

it seems that j is stored at address rbp-28 While ptr is stored at address rbp-24.

These are instructions where the values are stored in the stack

mov QWORD PTR [rbp-24], rdi
mov DWORD PTR [rbp-28], esi

It seems the arguments are passed to the function using registers rdi and esi.

Compilers can optimize their calls of functions and use registers instead of the stack to pass arguments of small sizes to functions. Within the functions they can use the stack to temporary store the arguments passed through registers.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 1
    The important thing is to know how `j` is passed into the function, then it's easy to see how it's copied to the stack. – Jester Oct 18 '17 at 23:46
0

Just a suggestion for further explorations on your own. Use gcc -O0 -g2 f.c -Wa,-adhln. It will turn off optimizations and generate assembly code intermixed with the source. It might give you better ideas about what it does.

As an alternative you can use the objdump -Sd f.o on the output '.o' or executable. Just make sure that you add debugging info and turn off optimizations at compilation.

Serge
  • 11,616
  • 3
  • 18
  • 28
  • I disagree that disabling optimization is a good idea. `-O1 -fverbose-asm` output won't spill/reload everything around every statement, so there are many fewer instructions. It's easier to keep track of what's going on with many fewer instructions. – Peter Cordes Oct 19 '17 at 03:05