Files

425 lines
12 KiB
ArmAsm
Raw Normal View History

2021-02-20 19:25:30 -06:00
.file "functions.c"
.version "01.01"
gcc2_compiled.:
.section .rodata
.LC0:
.string "%d, %d, %d\n"
.text
.align 4
.globl function3args
.type function3args,@function
function3args:
/* This push saves the ebp, and in combination with the move is called
* the function prolog. */
pushl %ebp /* at (%ebp) on the stack */
movl %esp,%ebp
/* This subl is used to allocate space for any local variables. In
* this case we have none, and we can see the fact that this
* instruction is useless because no stack references are negative
* offsets from the %ebp (visualize or draw the stack to see this).
* I'm not sure why GCC does this. */
subl $8,%esp
/* (%esp) == -8(%ebp) */
/* remember our comments. This instruction copies the last argument of
* the function to %eax*/
movl 16(%ebp),%eax
/* push this value as the last argument to the printf call.
* Note: This is why we have an %ebp register, because this push will
* affect the %esp, not the %ebp, and our references to local
* variables all remain the same still. */
pushl %eax
/* (%esp) == -12(%ebp) */
/* Now access the second argument of the function, and push it */
movl 12(%ebp),%eax
pushl %eax
/* (%esp) == -16(%ebp) */
/* Access the first argument of the function. Remember that the
* remaining two things below 8(%ebp) are the return address at
* 4(%ebp) and the old value of %ebp, which is at (%ebp) */
movl 8(%ebp),%eax
pushl %eax
/* (%esp) == -20(%ebp) */
/* Push the string onto the stack */
pushl $.LC0
/* (%esp) == -24(%ebp) */
call printf
/* (%esp) == -24(%ebp) because the stack is reset fixed after a call */
/* Again, "pop" all 16 bytes of arguments off the stack */
addl $16,%esp
/* (%esp) == -8(%ebp) */
.L2:
/* Leave copies the value of %ebp into %esp, effectively popping all
* extra local variables and junk off the stack. It then pops the top
* value off the stack (which is the saved %ebp) and stores it in %ebp
*
* So it is basically the reverse of the function
* prolog, and implicityly removes any local variables and junk that
* GCC may have thrown on the stack. This is key, because GCC loves to
* throw junk on the stack for no reason. It is all taken care of at
* function exit because of this instruction */
leave
/* (%esp) == (%ebp) == (old %ebp) just after call */
/* pops the return address saved on the stack into %eip, and thus
* execution transfers to just after the call */
ret
.Lfe1:
.size function3args,.Lfe1-function3args
.align 4
.globl function3argsRet
.type function3argsRet,@function
function3argsRet:
pushl %ebp
movl %esp,%ebp
/* Move the first argument to %edx */
/* The first argument is at 8 above the ebp. Ie it as at the lowest
* address of all arguments. The rest are at higher address */
movl 8(%ebp),%edx
/* multiply the second argument with %edx, store in %edx */
imull 12(%ebp),%edx
/* multiply the third argument with %edx, store in %edx */
imull 16(%ebp),%edx
/* Move %edx to %eax. %eax is the return value */
movl %edx,%eax
/* Alignment junk */
jmp .L3
.p2align 4,,7
.L3:
leave
ret
.Lfe2:
.size function3argsRet,.Lfe2-function3argsRet
.align 4
.globl functionPtrArg
.type functionPtrArg,@function
functionPtrArg:
pushl %ebp
movl %esp,%ebp
subl $8,%esp
/* move the third argument (the pointer) into eax */
movl 16(%ebp),%eax
/* derefrence it. Remember how I said that leal does not deref, but
* mov does? */
movl (%eax),%edx
/* push the rest of the args, and call printf */
pushl %edx
movl 12(%ebp),%eax
pushl %eax
movl 8(%ebp),%eax
pushl %eax
pushl $.LC0
call printf
addl $16,%esp
.L4:
leave
ret
.Lfe3:
.size functionPtrArg,.Lfe3-functionPtrArg
.align 4
.globl functionPtrRet
.type functionPtrRet,@function
functionPtrRet:
pushl %ebp
movl %esp,%ebp
/* Put the first argument of our function */
movl 8(%ebp),%eax
movl %eax,%edx
/* put the address made by 0 + %edx*4 into register %eax */
leal 0(,%edx,4),%eax
movl %eax,%edx
/* Add the third argument of our function (the pointer) to the result */
addl 16(%ebp),%edx
/* Put the second arg into eax */
movl 12(%ebp),%eax
movl %eax,%ecx
/* put the address 0 + %ecx*4 into %eax. */
leal 0(,%ecx,4),%eax
/* add %eax to %edx, store in %edx.
* If you were keeping track of the registers like you should have been,
* you should now realize that %edx contains pointer + second_arg*4 +
* third_ard*4. In other words, we know pointer is an integer pointer
* because the scale was 4 during all the pointer arithmetic */
addl %eax,%edx
/* Put the result into the return value register %eax */
movl %edx,%eax
jmp .L5
.p2align 4,,7
.L5:
leave
ret
.Lfe4:
.size functionPtrRet,.Lfe4-functionPtrRet
.align 4
.globl functionLocalVars
.type functionLocalVars,@function
functionLocalVars:
pushl %ebp
movl %esp, %ebp
/* so this is enough space for 4 integer variables, but sometimes GCC
* allocates more space than it needs, especially in recent versions.
* Note in this case, we have only THREE variables in our function.
* But we will actually get to see GCC use this magic local variable
* in a bit. Most times we aren't so lucky. */
subl $16, %esp
/* recall 12 from ebp is the second 4-byte function argument (note
* that if this function had non-integer arguments, 12(%ebp) might be
* like the 3rd or 5th argument. Just something to keep in mind) */
movl 12(%ebp), %eax
/* XOR the second function arg with the first function arg */
xorl 8(%ebp), %eax
/* Store it in the first local variable. So the first local variable
* now contains arg1 ^ arg2. This update of a local variable should
* clue you into the completetion of a C statement.
* In this case, we have determined that the statement was
* local1 = arg1 ^ arg2;
*/
movl %eax, -4(%ebp)
/* put the first arg into %edx */
movl 8(%ebp), %edx
/* Take the address of the second function arg.. */
leal 12(%ebp), %eax
/* put it into what appears to be the fourth local variable (again,
* it could be the the 9th, 17th, etc)
*
* HOWEVER, NOTE: We do NOT have 4 local variables in the
* corresponding C code. GCC has created a temporary here to do the
* calculation. This is further evidence of non-optimized code. */
movl %eax, -16(%ebp)
/* check your sheet for %edx */
movl %edx, %eax
/* Move the fourth local variable into %ecx. So, following your sheet,
* %ecx now contains the address of the second function arg. */
movl -16(%ebp), %ecx
/* FIXME: BUH? */
cltd
/* So here's an odd intruction. Basically, if you check the Intel
* Instruction set reference, you see that idiv takes a single
* argument of either a register %reg or an indirected register (ie a
* register containing a memory location, (%reg)) and then divides
* %eax by the value in %reg or at memory location (%reg). The result is
* stored in %eax, and the remainder is in %edx.
*/
/* Do: %eax = %eax/(%ecx); %edx = %eax MOD (%ecx);
* so from your sheet, %eax = arg1/arg2; %edx = arg1 MOD arg2 */
idivl (%ecx)
/* Move result to second local variable. So local2 = arg1 / arg2; */
movl %eax, -8(%ebp)
/* Move first arg to %edx */
movl 8(%ebp), %edx
/* Put the address of the second arg into %eax */
leal 12(%ebp), %eax
/* Use that temporary variable again */
movl %eax, -16(%ebp)
movl %edx, %eax
movl -16(%ebp), %ecx
cltd
/* %eax = %eax/(%ecx); %edx = %eax MOD (%ecx);
* So, %eax = arg1/arg2; %edx = arg1 MOD arg2;
*/
idivl (%ecx)
/* Store %edx into third local variable. So local3 = arg1 MOD arg2; */
movl %edx, -12(%ebp)
/* Put the local2 into %eax */
movl -8(%ebp), %eax
/* %eax = local1 | %eax */
orl -4(%ebp), %eax
/* local3 = local1 | local2 */
movl %eax, -12(%ebp)
/* Put local2 into eax */
movl -12(%ebp), %eax
/* %eax = local1 & local2 */
andl 8(%ebp), %eax
/* Junk instruction that says return %eax */
movl %eax, %eax
leave
ret
.Lfe5:
.size functionLocalVars,.Lfe5-functionLocalVars
.align 4
.globl main
.type main,@function
main:
/* save ebp */
pushl %ebp
/* move esp to ebp so we can access vars from ebp */
movl %esp,%ebp
/* allocate stack space.. Notice that gcc likes to allocate WAY more
* space than it needs in some cases.. why this is, I don't know.
* We really only need 4 bytes of space here for our int a, and a
* quick scroll through the function shows that -4(%ebp) is the only
* local variable we use */
subl $24,%esp
#APP
nop
#NO_APP
/* So here we see that GCC pushes some mystery arg onto the stack,
* and then the three arguments in reverse order, followed by the call
* to function3args. Remember that the call instruction places the
* address of the next instruction onto the stack. So at the entrance
* to function3args, esp points to the return address, and we have 20
* bytes above the esp, including ret and the mystery argument.
*
* However, since we are working on source generated without
* -fomit-frame-pointer, there will be a push of the ebp, and then the
* esp will be copied to ebp, and variables will be referenced from the
* ebp.
*/
addl $-4,%esp /* 20(%ebp) after prolog */
pushl $3 /* 16(ebp) */
pushl $2 /* 12(%ebp) */
pushl $1 /* 8(%ebp) */
call function3args /* 4(%ebp) */
/* Go to function3args and see the comments there to see these
* variables in action */
/* This stack ajustment is the same as popping all 4 arguments off the
* stack, ie the 3 integers and the mystery arg. */
addl $16,%esp
#APP
nop
#NO_APP
/* So this function is the same exact deal as the previous, except we
* have a return value. GCC uses the eax register to store the return
* value of a function.
* A good excercise would be to follow the stack along yourself with
* a sheet of paper for this example. */
addl $-4,%esp
pushl $3
pushl $2
pushl $1
call function3argsRet
addl $16,%esp
/* Junk instruction, unoptimized code */
movl %eax,%eax
/* Notice now that %eax is copied into the first local variable */
movl %eax,-4(%ebp)
#APP
nop
#NO_APP
/* This function exists as an example of what happens when you have a
* pointer as an argument. */
addl $-4,%esp
/* the lea instruction loads the effective address of its first
* argument and places it in the second. In other words, it simply
* adds the offset to the register being indexed, and then moves that
* into the destination.
*
* It is easy to become confused with this instruction, because it
* actually does NOT derefrence the first arg, where as a mov does.
*/
/* Load the address of the first local variable into %eax */
leal -4(%ebp),%eax
/* push it. Thus the pointer is the third argument */
pushl %eax
pushl $3
pushl $1
call functionPtrArg
addl $16,%esp
#APP
nop
#NO_APP
/* The example is the same as the previous, except we return a
* pointer */
addl $-4,%esp
leal -4(%ebp),%eax
pushl %eax
pushl $3
pushl $1
call functionPtrRet
addl $16,%esp
movl %eax,%eax
/* Put the value in %eax into the second local variable. So the second
* var must be an int pointer from out conclusions in functionPtrRet */
movl %eax,-8(%ebp)
#APP
nop
#NO_APP
/* This example is intended to show how a function handles local
* variables as always being negative offsets from the %ebp */
/* Here we see another mystery stack allocation.. */
subl $8, %esp
pushl $2
pushl $1
call functionLocalVars
addl $16, %esp
movl %eax, %eax
movl %eax, -4(%ebp)
#APP
nop
#NO_APP
.L6:
leave
ret
.Lfe6:
.size main,.Lfe6-main
.ident "GCC: (GNU) 2.95.4 (Debian prerelease)"
// vim:noet