NYU CSCI-UA 0201 - RET Instructions and the Stack

Unformatted text preview:

The CALL/RET Instructions and the StackIn the previous chapter, we explained most of the lines of the assembler file that isgenerated by the compiler for a C file, with one important exception which is the “ret”instruction at the end of the function. Let’s just review a simple example: unsigned a = 6; void f() {a = a + a;} void g() {f(); f();}The corresponding assembler file is:.file "x.c".intel_syntax.globl _a.data.align 4_a:.long 6.text.globl _f.def _f; .scl 2; .type 32; .endef_f:mov eax, DWORD PTR _aadd DWORD PTR _a, eaxret.globl _g.def _g; .scl 2; .type 32; .endef_g:call _fcall _fretMost of the lines in this file are familiar from the previous chapter. The .file directivegives the name of the original source file for informational purposes. The .intel_sytaxline tells the assembler we are using Intel rather than AT&T syntax. The .globl lines areused to tell the assembler that the corresponding symbols should be marked as globallyaccessible from other files. The .data directive marks the start of the data in the program,and .text marks the start of the program. The .align directive makes sure that the variableis on a 4-byte boundary, for efficiency purposes. The .long directive actually generatesfour bytes of data containing the specified initial value. The label lines, such as _a:associate assembler labels with specific addresses in memory. The mov instruction loadsa word into a register. The add instruction adds the contents of a register to a word inmemory.The previous paragraph identifies every line in the assembler output (see previous chapterif you need to consult the more detailed descriptions), with two notable exceptions, thecall instruction and the ret instruction. In general terms, we know what these do. The callinstruction clearly transfers control to another procedure, and the ret instruction returnsto the instruction following the call. For rough understanding of what the assemblermeans, this is an adequate level of explanation. That’s even a sufficient explanation forwriting assembler yourself. You can just code the call and the ret following the aboveexample and your code will work fine.However, that’s not good enough! We are aiming here at a complete understanding ofhow the machine works with no magic at all. It’s all very well to say that the retinstruction returns past the call, but the question remains of how this works. Rememberthat the machine executes instructions in isolation, it does not keep a memory of all theinstructions it has executed, so it can’t use some algorithm such as “check the list ofinstructions you have executed, find the most recent call instruction, and return to theinstruction that follows the call.” That’s fine as a description of what is supposed tohappen, but it is definitely not fine as a description of how things actually work at themachine level.When an instruction is executed, it has only the current machine environment available,consisting of the current register values (including the instruction pointer EIP) and thecurrent contents of memory.So the ret instruction must be able to do its job by consulting only the register andmemory state at the point when it is executed. From this we can conclude that somehowthe call instruction must figure out the address of the following instruction, and save thataddress in either a register or a memory location in such a way that it is accessible to theret instruction. So far, so good, the only issue that remains is how exactly does the callinstruction save this information, and how does the ret instruction retrieve theinformation stashed away by the call.Using a register to hold the return pointOne way this might be accomplished is to put the address in a register. Let’s redesign theia32 architecture to use that approach. Let’s pick a register, say EDI, which will containthe so called “return point”, that is the address of the instruction immediately followingthe call. Then all the ret instruction would have to do is to copy that value back into theEIP register, to cause the instruction following the call to be executed next.The ia32 does not in fact work that way, but it is a common approach. If you have a Mac,which uses the PowerPC, and you look at the design of that machine, you will find thatthis is exactly how the PowerPC works. The PowerPC has 32 registers, labeled %0 to%31, and register %31 is used to stash the return point on a call. The equivalent of a retinstruction simply has to copy this value into the program counter to achieve the return.To understand why the ia32 does not use this technique, consider one little problem withthis approach. Look again at the generated assembler language for function _g. Like allfunctions this also ends with a ret instruction. If call sets EDI to the return point, and retuses this return point, we have a problem. The problem is that the call within function gwill clobber the saved return point. Although this technique of using a fixed register forthe return works fine for one level of call, it does not work so nicely for the case ofmultiple calls. One way around this would be to temporarily save the return point in someother register in the higher level function. That’s quite reasonable on a machine like thePowerPC with lots of registers, but it’s bad news on the ia32 with its rather miserable setof 8 registers (only 7 of which can be freely used). That means that in practice we wouldhave to resort to having the high level function store the return point in memory, andretrieve it for the return.How the ia32 Really Does Call/RetThe designers of the architecture, faced with this dilemma, made the decision to avoidusing registers for saving the return point, and instead to use a memory location. Thatsounds good in general terms. All we have to do is to have the call instruction save thereturn point in memory, and then the ret instruction has to retrieve this value frommemory to know where to return to.Great! Problem solved! Oops, wait a moment, there is one pesky detail we did notaddress. Where shall we put this value in memory? If we choose a fixed location, saylocation zero, then we are stuck with the problem of multiple level calls again, since alow level call will destroy the return point required by the high level call. Let’s think for amoment about this problem. The way call and ret work is that control always returns tothe most recently executed call when the ret is executed, or in other words, we


View Full Document

NYU CSCI-UA 0201 - RET Instructions and the Stack

Documents in this Course
Load more
Download RET Instructions and the Stack
Our administrator received your request to download this document. We will send you the file to your email shortly.
Loading Unlocking...
Login

Join to view RET Instructions and the Stack and access 3M+ class-specific study document.

or
We will never post anything without your permission.
Don't have an account?
Sign Up

Join to view RET Instructions and the Stack 2 2 and access 3M+ class-specific study document.

or

By creating an account you agree to our Privacy Policy and Terms Of Use

Already a member?