 Good to see everyone, I miss you, hope you miss me, and I did, okay, I'm going for life. Cool, okay, so we, oh I thought that was feedback because of the chair. Okay, so we are going to start off, right where we left off last Monday. So we left off talking about the runtime climate and specifically we've been looking at and talking about the stack. So I believe we stopped right at this example. So just to refresh all of our memories, the stack is, because I know you guys have jettisoned all this information to load in and everything for midterm too. So now it's time to reload all this information back in. So the stack is essentially scratch memory for functions. So why do functions need some scratch memory? What was that? Local variables. Local variables, yeah, so we want to store local variables in a certain place. What else, is that the only reason? Recursive functions? So we need to be able to call recursive functions and those local variables will be stored in the stack that you need to be stored in their own distinct copy. How many registers does x86 have? x86 architecture, how many registers does the CPU have? Ten. Ten? Is that it? Oh. Sixteen. I am. I think it's sixteen. I actually don't know, I wish I should have noted that before I asked you all. It actually doesn't matter for my purposes right now exactly how many registers it has. Can it ever have more registers? No, let's say it's sixteen. And you need to compute some crazy math on fourteen, fifteen, twenty variables. Right? And that computation has to happen on the CPU, so where do those extra values go? On to the stack. Yes, I'm talking about the stack. Yeah. This is extra scratch memory. This is place that the function can store things while it's computing things. So any temporary variables that need storage can all be stored onto the stack. So it starts at high memory addresses, grows down, we're going to look at pushing and popping operations. So we have our example of the stack. We have super high memory at top, all f's, all ones. Right? At the bottom we have zero. So our stack is going to start somewhere. We'll say our stack pointer is here. And that's at memory address ten thousand. Now the important thing is because our stack is growing down. Right? So it's starting from higher memory and growing down. So as we push things on the stack pointer will move down. As we pop things off the stack pointer will move up. That means what do we know about all the memory that's below the stack pointer? It's what? It's not on the stack. Exactly. So can we access and use that memory? Yes, you can. Well we could but what would that be? What are those memory addresses? The non-site to function. What do we know about the memory above the stack pointer? It's needed for our program. Yes. It's on the stack and it's needed for our program. So it's memory that's been explicitly allocated to our program. And everything else below that is essentially garbage and it's been deallocated. Right? We don't know exactly what it is but we know that it is garbage. We don't know what it is but we have to assume it can be anything and it doesn't matter at all what it is. Cool. So if we have some instructions, let's say we have two instructions. We're going to push E-A-X and pop E-B-B. So here I'm going to show the CPU registers. So we have, let's say there's 10 or hex A in the register E-A-X. Then we have the register E-B-X which whatever has zero we don't really know yet. And then we have ESP. So what value is inside ESP right now? Which is what? X 10,000, right? The address there. Right? So this is something that's important to remember is this pointer or arrow that I'm drawing to the top of the stack is essentially arbitrary. The only reason it points to memory address 10,000 is because there is the value 10,000 in the stack pointer register. So when we push things onto the stack pointer register, sorry when we push things on that stack pointer register it's going to be decremented and it's going to move down. When we pop things off it's going to be incremented and it's going to move up. Cool? So now we're going to walk through exactly what happens when we see these two memory instructions. So we have push E-A-X. So at a high level what should happen here? Too high. Should have already had one. It's one o'clock. Two o'clock. Whatever time it is, it's later. Add something. Add what? Yeah. So push E-A-X means take the value inside E-A-X and put it on the top of the stack, right? And so specifically what this is going to do is when this executes we first move the stack down. So we're going to move the stack pointer, decrement it by four, right? So in from 1-0-0-0 to f-f-f-c. So now that's going to move the arrow, right? Because the arrow just represents wherever the stack pointer register is pointing to. Then we copy the value a onto there. So the value inside the E-A-X register we're going to put on there. And then we're done. So that was pushing a value onto the stack. Now we need to execute the next instruction. So we need to pop E-B-X. So at a high level what does this mean? Yeah. So we're going to take the thing that's on top of the stack, put it into E-B-X. And because we've removed it, right? We're going to move the stack pointer to the top of the stack. So when we step through this we now will copy what's inside where ESP is pointing to. So this is cases A. We're going to copy that into E-B-X. And then we're going to increment the stack up by four. So now the stack pointer is at 10,000. So do we change or alter this memory when we do pushes and pops? It's not part of the semantics, right? The CPU just leaves that memory there. But to our program what's at this memory address? Our base, right? We don't, anything can happen to those values, right? We can't use those values. They've been explicitly deallocated. Cool. So where this comes into play and why the stack is so important is as we talked about each function needs to have copies of its local variables and parameters. And those are stored in what we call function frames. So these are states of how functions can use the stack. And so what we want to think about is, okay, let's go. So we have some function in Y, some local variable, right? So when the, so we are going to discuss, right, we can't put Z as a global variable, right? When we're trying to compile this down into some code, we can't just put Z at some fixed memory location like 10 or 20. Because we don't know, we won't be able to have multiple instances of that. Multiple invocations of this function foo. We also need somewhere to put the parameters. So what we're thinking about this is we need some way, so we want to, as we said, we want to store, we're going to want to use the stack, right? That makes the most sense. That's what we've been talking about. We have the stack. We want to use it to store the local variables. So now this is, that's at the top, and then here at the bottom we have all zeros, right? So let's say the stack corner is here. So now how does one probably know where to put, let's say Z? So function foo is called. When it writes out, when I say Z is equal to X plus Y, where does it put these values of Z, X and Y? Actually I'm going to simplify this because I just realized this is a bit complicated. No parameters. Z equals 10. So where do we want to store Z? On the stack. That's a super broad answer. How do we actually translate this Z is equal to 10 into code? Where do we move 10 to a register and then where? Then a memory address. What memory address? Memory of Z. Where is the memory of Z? Exactly. Okay, so let's keep it kind of simple. I think this would give us enough. Let's say we're at 100 when we start executing. How much local space are we going to need for local variables in function foo? Four bytes. Four bytes. And we can know that just by looking at this and what do you know based on this? How do you know it's four bytes? The declaration. We know that one declaration of an int and we know that an int is of size four bytes. So we know we have to have four bytes. So why don't we then reserve four bytes here and say this is going to be the address of Z. And because we want to actually save that memory, we have to move the stack over that. But then here I say Z is equal to 10. How do I know where do I put 10? How do I actually compile this into code then load it to the stack? So how do you know that it's going to be on the stack? Scope. So you can see Z is here. So we just said, hey, move 10 onto the stack. We get something like this here. Cool. But does that work in every single case? What if there's a bunch of other operations here? And then now I say Z is equal to 100. And maybe there's other operations after us. So what if these operations, as we said, they have a bunch of temporary variables? What if they now change the stack? And they move the stack down, let's say, we'll call this delta C, which is 12. Move it down 12 bytes. Now we see Z is equal to 100. Should the code that we compile for this Z equals 10 be different from the Z equals 100? Well, it should be different than 100 versus 10. But in the general case, no, it shouldn't be different. So why can't we just say, hey, put 100 on the top of the stack here? Because Z, the address of Z is up here. But because the stack can be moved around when our function executes, remember, it's just temporary storage. The only thing that our function must ensure is that when foo returns, the stack better be pointing here. That's it. That's the only guarantees we have. As this function executes, the stack can change however much it wants to. So before we were basically saying, hey, that offset zero from the stack pointer is where Z is located. But the problem is, is that may not be true throughout the execution of this function. The stack itself can change. So what do we do? Check whose symbol table. For what? So we're compiling it, right? So when we're compiling it, we can have a symbol table. But how do we actually translate into something that works like that in a runtime? So what was the nice thing about global addresses? They never change. They're fixed locations, right? But here, we can't have fixed locations here for Z. The function just push a flag on the offsets relative to that flag. Yeah, so... Don't worry about the stack pointer and how it changes because that flag won't go anywhere until you pop up. Right. So we have one pointer that points to the bottom of the stack that's going to completely change throughout the execution. But what we really like to have is some other pointer that points to where the top of the stack was. And then we can use fixed offsets for all of our local variables. We can say Z is always four off of that and any other local variables are eight off of that and twelve off of that. So that when we first start executing this function, right? When we first start executing this function, we say, okay, let's allocate space for Z, move the stack pointer down. But now we need some kind of pointer, so we'll call this, this is the stack pointer. We'll call this... Okay, annoyingly, this will have two names. On X86, it's called the base pointer, so that's mostly what I'll be using. It'll also be called the function frame pointer. Very similar things. So that now if I compile each of these Z equals to a hundred to say, hey, move ten at EVP plus four, EVP plus four will be the address of Z. And here if I say, oh, actually that's sorry, it's like, it'll be EVP minus four. It's a negative offset. And then here this would say move EVP minus, move a hundred into EVP minus four. And now I don't care at all what happens to the stack. So now I've given myself this fixed... So I'm really doing this similar thing. I'm assigning each local variable a fixed offset from the base pointer. And so this whole, well, and there could be parameters we'll get into exactly why, but there'll be some other junk on here, and then there'll be parameters. So now if I have an 8X and an 8Y. So here this would be, like, this is, say, this pointer, say, this would be X, and this would be Y. So now I see Z is equal to X plus Y. I know this X and Y are fixed offsets from the base pointer. So these are positive offsets, so they'll be four, eight. So this would be EVP plus eight, and EVP plus 12. Sorry, yeah, EVP plus 12, yes. Where's the address of the base pointer being stored? There's a register happily named EVP. Okay, so my question is that if you're going to save the variables, if you're going to assign them a value of offset, what is the difference between assigning them an offset and just assigning them, like, a physical place of memory? Because we don't know until this program executes what location that memory address will be. But we know where EVP will be. We will set it up properly as we'll see. When we get called, we'll set up our EVP so it's in the same place every time. Relative to all the parameters that are passed in and our local variables. Thank you. We'll see that. Where do we use store-owning offsets? Ah, in the code. So when it gets compiled, we no longer see variables. And this is what's really interesting. And actually it's a, I was just, this conference I was at again, there's people talking about when you have X86 or another language, how do you lift it back up and decompile it into C code. One of the big problems is you no longer see variables into X and Y. You see move EVP plus something or EVP minus something. So you have to lift that. You lose semantics about variables when you compile things. So it's a really interesting research challenge about how to go backwards. Let's look at this in more detail. Cool. So, we just said we can't use the stack pointer, right? Because the stack pointer is going to change. We can't refer to our local variables based on a stack pointer that can change. We want to refer to them based off of fixed location. So the frame pointer, the base pointer is going to point to the start of the function's frame on the stack. And then each local variable will be different offsets. So in X86, it's called the base pointer. It's stored in the registered PVP as we just saw. So we can go back to our original main function. So before we had variables A, B, and C declared globally, now there are going to be local variables of method made. So we have an A and B float C. A equals 10, B equals 100, C is equal to 10.4.5. A is equal to A plus B returns it. So just like before, when the compiler is trying to compile this, it generates a parse tree, it checks the semantics, it checks the type system. Now instead, it generates some code and it says, so I need to say that A is going to be at some location, EVP, well, plus A. In this case, it will be a negative offset from EVP. And B is at some location, EVP plus B. C is at a location, EVP plus C. And then the instructions are basically the same. Every time we see A, we see get memory location EVP plus A. So set 10 equal to there. Set 100 of EVP plus B. Set 10.4.5 to EVP plus C. And finally calculate local variable A is equal to local variable A plus local variable B. So unlike before, we had fixed offsets. Now what exactly this code does will change based on, and which, sorry, not what it does, but which memory locations will be changed will depend based on what this base pointer is. Cool. So how, actually, these are from a compilation that I did. So it shows EVP minus hex C, EVP minus 8, EVP minus 4. And so it's going to do some little bit of code. We're going to step through this, so don't worry. So the first thing that it does when the function gets called, so some other function calls us, the stack pointer is going to be a certain place. What's going to be inside that base pointer address, EVP? So we're going to put, essentially, the current stack pointer in our base pointer. So let's think about it from a different perspective. Then we call some other function. So they get called very first thing, what's inside that EVP function? EVP, sorry, EVP register. Almost. We'll see. That's something else. We'll see that in a second. The previous function's base pointer, right? The function that called us has a base pointer. It points to somewhere in the stack, and then it calls us. Now we have to, well, okay, we'll see. Now we have to replace that base pointer to our base pointer. So this is what we're going to do. So the stack is fairly pointed somewhere. We're going to move, remember we're moving right to left here, so we're going to move, no, we're moving left to right here. That's why I got a check. I checked with the constants. You can't move right to left if the constants are on the left, so you have to move left to right. Okay, so, wait, does that make sense? No. Stack pointer can date to the top of the stack, don't you want that to be the new function's base? Yes. Yes, yes, yes, yes. Okay, fine. I'm just trying to read too much into it. Okay, yes. We're copying left to right, huh? Okay. It's good we're not doing something else, but that's okay. We'll look at it later. Okay, so, great. So the current stack is here, and then we're saying move the current stack pointer where that points to, move it into the base pointer, so the base pointer also points there. And now for the entire duration of the execution of this function, the stack pointer can change, but the base pointer's going to get a fixed offset. It's going to remain fixed. And so all local variable axes will be variants of that. Okay, now we have to make space for our local variables. So here we're subtracting 16 from the stack pointer. Y16, how many bytes is two ends in a float? Float's only four. Oh, okay. Double is eight. Oh, okay. I believe. I'm 32 bits. We do, I think I removed that from this example, because I wanted to focus just on this thing, but we'll see that in future examples. So Y16, even though we're only using 12, doesn't that super inefficient? Doesn't that kill you a little bit? He's getting up close to four bytes. What? He's going to just give you some runway to work with in case you don't come on. Could be. Maybe we can return address. Return address? Sorry, I got a mistake. We'll see later. Yeah. Can I keep a library for certain sizes? Yes. So X86, I believe you can access any memory location, but some architectures, you can only access four byte aligned memory locations. And so in this way, we're moving it down 16. I believe it'll be 16, even if you only use four bytes. Once again, this is another one of those because the compiler does it. There's not, there's probably logic somewhere, somebody somewhere implemented this, and this is how it is. I'm sure there's a compiler five that you can say to remove this extra padding. It would still work on most architectures, but not guaranteed to work on all. Okay. Then we're moving A into, so A is 10, constant value 10, into EVP minus C, which is which variable? A. A. Right? And even if we didn't know this, we could look at the code and see, well the first thing that this function does is move 10 into A. And this line is moved 10 into EVP minus C. That means that EVP minus C must be A. And we'll see similar things happen. We move the value 100 onto the, into EVP minus 8. And then we move this, remember what we talked about, this is the IEEE floating point representation of 10.45. Move that first into EAX, and then from EAX into EVP minus 4 which is the location of C. Then finally we move B into EAX. We then add EAX to A. Yes, to A. I can't see the C here, I don't want to say C. To A. And we store that result in A. So just like here, there's subtraction. We're subtracting 10 from ESP and we're returning the result into ESP. That's what that syntax means. So we can step through this and see exactly what's going on. So we have our code on the right. We have our handy-dandy stack. Let's say we're at 10,000 hex. And do we know it's in EAX? What a function is called? No. Do we know it's in EVP? Previous, whoever called us must have EVP. Yeah. So there is something in there but we don't know what it is beforehand. But based on this diagram, what's going to be in ESP? Hex 10,000, right? Because I'm telling you, the stack pointer points to this location, you know exactly what's in that register when this function is called. Cool. So we're at the first instruction. Now this is where we're setting up our base pointer, right? We need to set up our EVP address. So we're moving the value 10,000, which is in ESP, into EVP. We will... I didn't include that from here, but we'll get to it. Now we just clobber to these people's EVP, right? So if we return, we destroy their core function. Now we're going to make space for our variables on the stacks. We're going to subtract 16 from ESP. So that's going to put us down here. And remember, we still have our base pointer pointing up to where the stack originally was. So our base pointer will move the stack down to essentially allocate local memory. Cool. Then we move 10 to EVP minus C. So we can see all of that. So now I gave each of these memory locations. So now I'm going to move 10 into layer. I have Fc here. I have F4. So each of these are 4 by offsets. The 4, 8, 12. C is 12. So we're going to move down 12. So we'll copy A here. Or hex A, which is 10, which is the variable A in our regional program. Then we're going to move the value 100 into EVP minus 8, which is going to be right above that. And then we will move this floating point value of 10.45 into EAX. Then we move that into EVP minus 4, which is FFFC. So the compiler basically laid it out such that it's ABC in reverse order, CBA. Didn't have to do it that way. It can do it whichever way it wants. It can do ABC, BCA, whatever. It can completely rearrange these, as long as these compiled instructions are correct. So somebody asked, well, where are these offsets stored? They're stored in the code itself, in these instructions. These assembly instructions are moved 10 into EVP minus C. Because we're not really pushing, that would be an optimization that maybe you could do for very specific functions. I believe it would be cheaper to allocate all the memory first and then put things back right, and then push things onto the stack. Because you may not have values for here yet, and you may not ever access them till later. But you want to make sure you've allocated all your memory for local variables first. Like this. At compile time. So it looks at... Well, it's going to be hard to get back. It's going to look and analyze this function, because it knows... C, we've staggered and looked at this whole function, and we know exactly how many variables are declared. We know there are three variables declared here. So we know that with our padding and rounding, getting to 4-byte boundary, we want 16 bytes of local variables. So in our example, we have A, B, C, one after another. So if it's like integer A, integer B, then I do something with A and B, and then I declare float C. So will it be the same if they will reserve first on the stack and then... Yes, so it will reserve... Yeah, it will analyze the whole function and look at... I believe this is like early versions of C who hadn't declared everything first and then used them, so you couldn't just declare a variable lower down. I think that's to make the compiling process easier, because everything's going to be here. You can still do like one run through the program, right? Figure out all the declarations, and then you know exactly how much you need, and then go back and compile everything. So I must know all the local variables before I reach this time. Exactly. Another question. Let's say in my main, I have condition. Based on that condition, I will call the function. Let's say if this is the case, then call this function or this function. But that will be decided on the runtime. So when I'm doing this procedure, should I allocate two frames? So we will see. But this is the function frame for the function main. That's it. So when you call a function, it's responsible for creating its own function frame on the stack, and any function it calls will create their own function frame on the stack, and so on and so forth, all the way down, so you can run out of memory, or they start returning. But the function call will be decided on the runtime. Yes. Right, so that's why the stack changes during runtime. So if I call that, if I end up calling a function, it will then allocate space on the stack to create its own function frame. It'll execute, it'll return, and remember the rule is when it returns, the stack has to be in exactly the same location. And we'll see there's tons of rules. They have to put your EEP back in the right place and all that stuff, so you can continue executing. So that way, from your perspective, you don't really know or care that their function got called, except that you got a return value from it. Yeah. And this is why it's called stack-executing, not for dynamic scope. Because we don't know... Yes. Yes. In dynamic scoping, you'd have to look up... Instead of this EDP-C, you'd probably have to call some other function to look up the exact variable. But you could... If you remember, in dynamic scoping, there were some cases where you knew it was a local variable. There's no other possibilities. So in those cases, you could compile it like this as an optimization stuff. So this is why you can think of... Why are most languages statically scoped and statically typed? Or lexically scoped and statically typed? Because it's a lot easier to compile. Or in why are fast languages also that way? Because it's easier to compile. And you get really close... The more you think it's seen, the more you realize it's like a glorified assembly language. It's kind of like assembly languages of functions. Okay. So we've added everything onto the stack, into the local variables. Now we're going to move Eax. We're going to move... Oh, yes. Because we're moving this onto the stack page. So we're moving this value, the floating point value, onto variable C. And then... So we know here we have ADC. And so now we're going to move into Eax, EBP minus 8, which is B. So we're going to move 64 into Eax. And then we're going to add Eax to EBP minus C, which is A. So the result should be 112, which everyone knows is 6e. And then we're going to continue executing. So there's some stuff I left out, which we'll talk about in a second, but any questions on brief overview of function frames and stacks? So why are we using these? We're talking about how functions are implemented. So we have decorations, which have a function name. We have formal parameters. So we have names and types of that function. And we have a return type. So we know there's a difference between when declaring a function, we're saying exactly, hey, there exists a function, it has this name, it takes these parameters, here are the types of all the parameters, and here's what it returns. Then once we have that declaration, we can invoke the function. When we invoke the function, what are we providing? So we're providing the parameters of the function. What else? Address of the function. The address of the function, maybe the amount we'll see. Yeah, address of the function. Or name of the function, right? We're telling the compiler what function we want to call. Yeah. I guess we're providing the stack pointer where we're at, right? Yes. So we'll see the stack pointer where we'll add the effect. So when we invoke the function, we have something like this, fx1, x2, all the way up to xk. And remember, x1, x2, xk are all expressions, right? These can be a plus b plus c times d, right? It's not just that we're passing variables into functions, but expressions that could be complicated things. Okay, so when we talk about functions, we're going to talk about what we pass in. x1, x2, xk are called, we're going to call actual parameters. So these are the actual parameters that we're supplying to the function. And what the function declares, and in the function body, those are going to be called formal parameters. So this is just to separate the function definition from the runtime execution of a function and invocation of a function. So it kind of seems obvious, right? But when we want to invoke a function, we need to be able to, the code that is calling, invoking this function must create enough space to hold all of these parameters. Otherwise, where would they come from? So this also ties into function frames. So this gives us a little bit of terminology to talk about functions, because this gets into a really big part of function frames, right? Function frames do not exist in isolation, right? We must have been called from someone unless we are literally the 31st function invocation. And we will probably call other functions, right? So we need to know how we invoke other functions. And so function frames, as we saw, they allow us to allocate memory for the function's local variables. So we know that information. We can know that at compile time for every function. But what other information do we actually need to call a function? So you want to invoke some function. What information do you need? You need, maybe, actually. I think it's the proper answer. We'll see in which case that you definitely need it. So the next function doesn't need the previous functions. So you can think of a current function. We have a previous function. We're going to call some next function. So we've already stored, as we'll see, we have to somehow store the next, because we're using a base pointer. So we need to somehow store the previous function's base pointer. But the next one, we may not need that. So we need the return value. How do we get the return value from a function? And this has to be specified and established. We also need to know how many parameters are there. We need to know our frame pointer. And we need to know the return address. So we need to tell this function how to get back to us. Where should this function return to? When this function is finished executing, where does it go to? Remember, CPUs are incredibly stupid. We need that in the best way possible. They are also incredibly complicated, beautiful pieces of engineering. At the same time, when you're programming them, they're incredibly stupid. There is an instruction pointer that points to the current bit of code that's executing. And then when it's done, it goes to the next one, and the next one, and the next one, and the next one. So when you invoke a function, very similarly to not using the little variables, it's executing, executing, executing. At some point, it has to go back to where you called it, but where does it go? How does it know how to actually go back and to start executing right after that call instruction? So we also need local variables, maybe space for temporary variables. So what all of this comes in, what I'm trying to allude to is if I write some function, and let's say I put the return address on the stack, so you call my function, where should you get the return address from? The return value. So I'm returning, you're calling my function, let's say I've written it in hand assembly, because I'm awesome. I say the return value is going to be on the stack. Somewhere near base pointer, R base pointer. Somewhere near return address. Yeah, so when my function returns, what I'm saying is the stack will currently point one down from where you called me, and that thing on there will be the return value. So now you can pop that off into a register, right, and use that value that you got from me. But then let's say somebody else is writing a program, and they don't like that style because it's ugly. They're going to put the return value in EAS. So now you're writing some code. How do you deal with the fact that both of us have different ways of giving you our return value? Does that sound like a good situation to be in? No, it sounds horrible, right, because you'd have to keep track of different functions and how exactly they wanted you to get the return value. And this is where we come to calling convention. This is one of the super important things if you ever get into, especially in security, like binary vulnerability analysis and binary exploitation and reverse engineering and all this stuff, the calling convention is key to that because it describes a standard interface where we all agree. So instead of us each coming up with our own decisions, we're going to say, okay, the calling convention for x86, the return value, we'll say is always in the register EAS. What's the problem with that? EAS can be overwritten? EAS can be overwritten, maybe. How big is EAS? 32 bits. What if I want to return a double, which is 8 bytes? Yeah, so things get more complicated, but people have figured this out and they've established standards to do this, so you won't actually have to worry about this. But it's important that we have the convention and that what this allows us to do, right, if we're compiling all the code ourselves, then it doesn't matter as long as it's standard. As long as our compiler is standard across all the code we compile, it's fine. But what if you compile a library and you give it to me and say, hey, I wrote these awesome functions, here's the binary and here's the header file. Just link these in and you'll be good to go. Now, you better give me code that's the same calling convention that I expect, otherwise we're going to have problems. So, all that information we just talked about has to be stored on a stack in order to call a function. We need the parameters to the function. We need the my function's base pointer because you're going to overwrite it. We need the return address of where we go after the function executes. And then we also need the return value and the local variables. So, we know we need all these things. The calling convention says who's responsible for what. This answers the question, who should store this information, who's responsible and end in what order. Also, as part of this, we talked about it with registers. Some registers are what they call are considered saved registers. So, if you call a function and it wants to use one of those registers, it has to save whatever that value is on the stack, use it, and then return it to the stack before it returns to the function. Or, sorry, it returns it into that register so it's the same. Other variables can be clobbered by the function and they don't have to apologize at all. But this is just a standard. You have to all agree on it, otherwise you're going to write software that's incompatible. So, we need to find a convention of who pushes and stores what values on the stack in order to call a function and end in what order. And so, to make this even more confusing, not only does this vary based on architecture, so x86 is different from the 64A, it's not x64, it's called AB64, whatever the 64 bit version is. And it's different from R and it's different on MIPS. It's different on architectures, which makes sense to different languages. It also varies based on operating system. So, the calling convention for Windows is different than the calling convention for Linux, even on x86. And even more annoying, the calling convention can vary on an operating system. So, on Linux, the standard calling convention we're going to look at, Cdepple, is standard among most software except when you want to make system calls. There's a different system call calling convention to make syscalls into the operating system. So, not only does it vary on processor, operating system, compiler type of call that you're making. And the nice thing is, do you remember when we were doing, those of you who wrote C++ for project 2 and wanted to use our C code, the lecture code, you had to do in your header file, you did a pounding clue, you added a little special curly thing around it. That was saying what calling convention that code expected, so that it could know. So, you can actually, as long as you tell your compiler, hey, these guys are using Windows calling convention for whatever reason and these people are using Cdepple, you can actually use them both as long as you know. In fact, we're going to revisit this on Wednesday.