I have a question on a homework assignment for an Assembly class. I'm not looking for an answer by any means, just some guidance with how it works. Based on my understanding, I can't determine what exactly is happening.
Consider a function P, which generates local values a-c by simple local computation
and d-f by calling Q(), R(), and S().
long P(long x,long y,long z) {
long a = ...;
long b = ...;
long c = ...;
long d = ...;
long e = ...;
long f = ...;
return d + e + f;
}
0000000000000022 <P>:
22: 55 push %rbp
23: 53 push %rbx
24: 48 83 ec 20 sub $0x20,%rsp
28: 48 83 c7 01 add $0x1,%rdi
2c: 48 89 7c 24 18 mov %rdi,0x18(%rsp)
31: 48 83 c6 02 add $0x2,%rsi
35: 48 89 74 24 10 mov %rsi,0x10(%rsp)
3a: 48 83 c2 03 add $0x3,%rdx
3e: 48 89 54 24 08 mov %rdx,0x8(%rsp)
43: 48 8d 74 24 10 lea 0x10(%rsp),%rsi
48: 48 8d 7c 24 18 lea 0x18(%rsp),%rdi
4d: b8 00 00 00 00 mov $0x0,%eax
52: e8 00 00 00 00 callq 57
57: 48 89 c3 mov %rax,%rbx
5a: 48 8d 74 24 08 lea 0x8(%rsp),%rsi
5f: 48 8d 7c 24 10 lea 0x10(%rsp),%rdi
64: b8 00 00 00 00 mov $0x0,%eax
69: e8 00 00 00 00 callq 6e
6e: 48 89 c5 mov %rax,%rbp
71: 48 8d 74 24 18 lea 0x18(%rsp),%rsi
76: 48 8d 7c 24 08 lea 0x8(%rsp),%rdi
7b: b8 00 00 00 00 mov $0x0,%eax
80: e8 00 00 00 00 callq 85
85: 48 01 eb add %rbp,%rbx
88: 48 01 d8 add %rbx,%rax
8b: 48 83 c4 20 add $0x20,%rsp
8f: 5b pop %rbx
90: 5d pop %rbp
91: c3 retq
Find the size of the stack in bytes.
Identify the assembly statement(s) that allocate and free the local stack.
Identify which local values get stored in callee-saved registers.
Identify which local values get stored on the stack.
Explain why the program could not store all of the local values in callee-saved registers.
I understand the concept of pushing rbx and rbp on the stack to make room for other local variables later on. I understand how space on the stack is allocated on line 24. Then the arguments passed into P are altered and stored on the stack. My issue starts at line 43.
line 43 and and 48 create pointers to positions on the stack correct? Then line 4d sets eax(or rax) to 0. Then on line 57 we set rbx to rax(0), and the next 3 lines I'm completely confused about. We create more pointers of the stack and store the address into rsi and rdi. Wouldn't this override what we did in lines 43 and 48. And then it sets eax(rax) to 0 again on line 64, but eax was already 0 and nothing changed with it.
This repeats with the next call on line 69 as mentioned above. By the time you get to line 85 and 88 to me it seems like it would just be 0 + 0 + 0.
On a side note, shouldn't each 'callq' end with a 'ret'? For example, shoudn't there be a 'ret' after line 64 and 7b?
I've reached the point where it feels like something is missing from the code, but I wanted to check first because it seems more likely that I'm not understanding some core fundamental principle.
Thank you in advance for any friendly nudge in the right direction to figure this out!
The code you're looking at is unlinked code. The three call instructions do not simply jump to the next instruction,* they will be filled in by the linker with the offset to an actual function. So you cannot simply ignore their behavior as you have been.
The behavior of a function call is dependent on the ABI, as Anders mentioned. In particular, RSI and RDI should be assumed to be overwritten, and RAX contains the return value of the function.
* Call instructions in x86 are relative to the next instruction. So an offset of 0 in a call instruction causes the disassembler to display the next instruction as the target. This is typical for unlinked code.