Hello and welcome.
In this lecture, we're going to discuss what scope offsets are.
It's about time that we discuss scope offsets because we're soon to start implementing offsets in our
variables.
So firstly, what a scope offsets.
Scope offsets are offsets relative to the base pointer and the stack point are registers in the intel
processor.
So scope offsets can offset upwards and downwards access in local variables causes us to access offset
in a negative region.
For example, EVP minus for ABP, minus eight axis and function arguments causes us to access an offset
in a positive region plus eight plus 12.
Here's an example of a test program that just takes two function arguments.
We set X to 50 Y to 20.
To the right is the assembly code that's generated so we can see if x equals 50 that sets EBP plus 8
to 50.
If we set Y, it sets AB plus 12 to 20.
If you're a little lost, don't worry because we're going to explain this later on in this lecture.
So here our local variable scope access.
So here we have instead equals 20, integer equals 30.
So the right is the appropriate assembly language.
Notice we subtract 16 from the stack pointer.
Why do we do that?
Because we want to make room for our scope variables.
Why is it 16 bytes when we only have eight bytes on the stack?
You know, we have two integer variables that's eight bytes y 16 because the C programming language
standard requires us to align our stack to 16 bytes.
So even if we have four integer variables here, it's still only going to subtract ESP by 16.
But if we then have five, it's going to subtract ESP by 32 because the stack requires 16 byte alignment.
So here we see EPP minus four is equal to 20, EPP minus eight is equal to 30.
So this is the stack growing downwards from the base pointer.
So let's now explain the scope memory.
So that makes a little more sense.
So here you see, instead equals 20 inch equals 30.
Here we go, EEP -420, -830.
So we're now going to explain how these offsets work with this stack frame.
So I have zero x 1000 here as a base address.
I pulled this out of a hat.
It's just a number to make things simple for us to work with, and we'll work with all of our offsets
based on this number.
So firstly, once one calls a function in C, essentially it will use a call instruction.
And the call instruction obviously has a return address.
It pushes a return address to the stack so that when we call a RET, we know which instruction to go
back to.
You should know assembly language before you did this course.
Of course.
So you probably understand what I'm talking about.
Zero x 1000 is that address where the return address is stored.
Then we have our old base pointer which is in the test function.
By here you c push EVP.
So after we run this instruction, our base pointer is there.
Okay, so that's an address, zero zero FFC and we then set the base pointer to the current stack pointer
and the current stack pointer is 0x0 FFC.
Okay.
Because we've just pushed the base pointer and now for variable D to access it, we do EVP minus four.
So that basically decrements four bytes from EVP and remember EVP is set to zero X's FFC.
So that gives us 0x0f8.
And then obviously we write our four bytes which writes all the way up to 0x0 FFP.
Okay, now remember at zero zero FFC, that is our old base pointer.
Okay.
So if we were to just do F -0, we would overwrite our base point so that we just saved.
That is why we have to decrement.
So we do V minus four, EVP minus eight.
That's how it works because the stack grows downwards in intel systems, so we subtract our way down
the stack.
And obviously here when we subtract the stat point by 16, that ensures there's enough room for when
we do this decrement from the base pointer.
So essentially we decrement and then we write upwards.
So we decrement for bytes to write a four byte integer, we write upwards.
So then those four bytes are written upwards.
That's how it's working.
So again, for variable E, when we set it to 30, we do.
Minus eight, so zero zero FFC minus eight gives us 004.
We write four bytes up to 007.
So that also includes the byte written at 004.
And that's how we write variable E.
So that is how the stat frame works and why we have to decrement like this.
Okay.
So hope that makes sense.
Let's now explain the function argument, variable scope, memory.
So here we have a program on the left in it test accepts two arguments and it X and y and it sets them
to 50 and 20.
We get this assembly generated underneath by here to the right we have a function main and we call test
in this function and we pass 50 and 30.
So 50 for the X argument, 30 for the Y argument.
And look at the assembly generated.
It pushes 30 and then 50.
It actually pushes them backwards.
You have to be sure to remember that because if you push it forwards, then the code won't line up properly
and people will be reading the wrong variables.
So when we push arguments to a function, we have to push them in backwards.
That's why you see a push of 30 and then a push of 50 rather than a push of 50 and then a push of 30.
So once we've pushed them, we call function test, which obviously sets those variables here you see
add ESP eight.
Now that just restores the stack pointer because when we've pushed to two words by here, we've essentially
adjusted the stack by eight bytes.
So we need to restore the stack pointer back to what it was when we're done with the function.
Very important.
If you don't do that, your whole program will crash when something else happens.
So let's explain the stack frame.
We can see at C 1000, we have the 30 that we pushed.
That's four bytes because every push and pop to the stack is four bytes here on 0x0 FFC.
We have our 50 here on 0x0f8.
We have our return address that's generated when we call test.
Obviously it pushes the address of the add instruction to the stack so that when we're done and we call
a read, we can get back and run from the add instruction.
And finally on zero four is the old base pointer that is here.
So you can see in the test function we have PUSH EVP, that's where that would be.
So when we've called Test and we've pushed EVP, that is where our old base pointer would be.
And obviously the the new base pointer is now equal to the current stack pointer, which is the address
of the old base point of 0x04.
Okay.
So let us now explain EVP plus eight.
You can see that the EVP is is equal to 0x04.
So what we do is we plus a bytes to that which gives us zero zero FFC which is value 50 where the 50
was stored.
So you see.
So hopefully that makes sense.
So we actually have to jump over the base pointer and return address.
That's why we start with plus eight.
And then again, you can see plus 12.
So 0x04 plus 12 gives us the Xbox 1000, which is the 30 OC.
So I hope that makes sense on how we're accessing the function variables there.
So let's now explain global structure, variable access.
So we have a structure of dog in the global scope.
How are we accessing it?
So D-Day is simply equal.
The address of DX because DX plus zero is where variable A is.
So we just let you have d, d dot b is plus four, dx plus four because we have an integer here that's
four bytes, d, c is D plus five because we have an integer of A which is four bytes and a job which
is one byte, which means that we need a D plus five to access variable C.
So obviously we're setting variable A to 50 B to 30 C to 60.
So that is how you access global structure variables.
Now one, the structure variable is in the scope.
Things are a little different and you're going to be a bit confused.
But bear with me because it's not as bad as you think.
So here the structure, scope, variables, access.
So we have a structured dog in the scope.
Again, we have D-Day equals 50, D, B equals 30, D dot C equals 60.
Look at the code generated.
So we subtract stack pointer to make room for that structure, which is perfectly valid.
And then we do P minus eight equals 50.
So that's where our A is stored.
FP minus four equals 30.
And finally.
Minus three equals 60.
So that might be a bit confusing.
Right.
But let me show you the math for this.
The math is very simple.
So what we do is to calculate the offsets for a scope variable.
We take the size of the structure.
So in our case, the dog structure is eight bytes.
Because remember what I said, we align and pad.
So although this is only six bytes, we pad it to eight.
Therefore, the structure is eight bytes in size.
So calculating the scope variable offset variable A we take the position in the structure which is zero
for variable A minus set by eight.
So that gives us EPE minus eight and there's variable eight.
And then again for variable B, we take the position in the structure, which is a plus four and we
minus it by eight and we get P minus four and then we take variable C, which is an offset of five,
we minus it by eight, which gives us a P minus three.
So that's how you literally calculate the the offsets of the structure once it's put in a scope.
So it's very simple math, as you can see.
And, and yeah, that ensures that everything works correctly.
So I hope that explains offsets for you scope offsets.
In the next couple of lectures, we are going to start implementing actual offsets for our variables.
And that's why this lecture was important.
I hope I've broken down why we access the base pointer in the way we do.
If you do have any questions though, please let me know and I'll try to explain that a little further
for you.
Take care.