Sign in to follow this  
Krisc

Getting Parameters in IA32 Assembly...

Recommended Posts

Krisc    494
Hey, I have a question regarding getting parameters in IA32 Assembly... How do I do it? I though this was how...
main:
	pushl	%ebp
	movl	%esp, %ebp

	xor	%eax,%eax
	xor	%ecx,%ecx

	movl	8(%ebp),%eax	# eax is x
	movl	12(%ebp),%ecx	# ecx is y
where 8(%ebp) is the first and 12(..) is the second...?

Share this post


Link to post
Share on other sites
doynax    850
That looks correct for most stack based calling conventions, the base offset of 8 corresponds to the pushed base pointer and function return address.
Unfortunately x86 has numerous calling conventions with different ideas about parameter passing. Fastcall transfers to two first parameters in ECX and EDX for instance, some push the data in reverse order and the return address may contain a segment too.
The two most common conventions, cdecl and stdcall, use the stack passing method you describe however. But a significant difference is that stdcall requires the callee to remove the parameters from the stack (ending the function with "RET 8" in this case).
IIRC all common conventions require you to preserve the ESI,EDI,EBX and EBP registers.

Share this post


Link to post
Share on other sites
Krisc    494
I think I may need to call atoi actually as I need to transfer the parameters into integers, but I am unsure how to do this... if I just push and call it results in a seg fault.

Share this post


Link to post
Share on other sites
doynax    850
Quote:
Original post by Krisc
I think I may need to call atoi actually as I need to transfer the parameters into integers, but I am unsure how to do this... if I just push and call it results in a seg fault.
Atoi is probably a cdecl function is which case the caller is responsible for removing the argument afterwards.
pushl %eax
call atoi
addl 4,%esp

Share this post


Link to post
Share on other sites
hplus0603    11356
The first argument to "main" is argc, which is an integer.

The second argument to "main" is argv, which is a pointer to a number of pointers to char strings.

Thus, you can't just push either of these and call atoi -- you can't call atoi(int) and you can't call atoi(char**); both will crash. However, because it's assembly, you don't have type checking to actually tell you that when you're compiling.

I have two suggestions for you, that will both help you a lot when learning these things:

1) run your program in gdb, and use "disas" to disassemble your function. Use "stepi" to step by single instruction, and look at what's in the registers. Use "x" to look at what's in memory at different places.

2) write the function you want to write in C first, compile it, and disassemble it using "objdump --disassemble"; that will give you something that works to start from. You may also wish to use "stepi" and "dias" in gdb on the C version of the function to see how it behaves.

Good luck!

Share this post


Link to post
Share on other sites
Krisc    494
Duh, that makes sense. I completely forgot what the parameters were in actual C code.

okay, so the second one is an array of pointers. so what i need to do is add 4 to the memory spot of the second parameter each time i want to increment to the next spot in the array. correct?

Share this post


Link to post
Share on other sites
doynax    850
Quote:
Original post by Krisc
okay, so the second one is an array of pointers. so what i need to do is add 4 to the memory spot of the second parameter each time i want to increment to the next spot in the array. correct?
Correct.

Share this post


Link to post
Share on other sites
Krisc    494
Quote:
Original post by doynax
Quote:
Original post by Krisc
okay, so the second one is an array of pointers. so what i need to do is add 4 to the memory spot of the second parameter each time i want to increment to the next spot in the array. correct?
Correct.


I have this much... it is so confusing! arrg! ;)


main:
pushl %ebp
movl %esp, %ebp

#... put argv[1] into ebx
#... put argv[2] into ecx

pushl (%ebx)
call atoi

pushl (%ecx)
call atoi

Share this post


Link to post
Share on other sites
doynax    850
Try this:
     	movl 12(%ebp),%eax  # load argv into eax
movl 4(%eax),%ebx # put argv[1] into ebx
movl 8(%eax),%ecx # put argv[2] into ecx

Share this post


Link to post
Share on other sites
doynax    850
Quote:
Original post by Krisc
Shouldn't it be 12(%ebp),
Oops.. Guess I've gotten a little too used to direct esp addressing..
Quote:
Original post by Krisc
It is still throwing a seg fault, time to load up gdp.
But you're still not removing your arguments after the atoi calls. You need to pop them (or add 4 to the stack pointer).

Share this post


Link to post
Share on other sites
doynax    850
Quote:
Original post by Krisc
I don't understand what you mean by removing my arguments... Could you go a bit in depth about this?
Well, the strings you're pushing for the atoi calls needs to be removed from the stack (popped) by someone. In stdcall this is the callee's responsibility but in cdecl (the "standard" convention) it's actually up to the caller to do it.

So your function call might look like this:
pushl (%ecx)
call atoi
popl %eax
or this:
pushl (%ecx)
call atoi
addl 4,%esp

Share this post


Link to post
Share on other sites
Krisc    494
okay, this is the entire program in IA32 asm... I got it working except y is set to whatever x is somehow...or somehow x is passed in twice to pow.

running:
./p1 2 4
results in
x^y = 4

running:
./p1 4 2
results in
x^y = 256

here is the code, thanks for the help guys i am beginning to understand it all

.file "p1.c"
.section .rodata
.LC0:
.string "x^y = %d\n"

.text
.globl pow
.type pow, @function
pow:
pushl %ebp
movl %esp, %ebp
pushl %ebx # save ebx
pushl %esi # save esi
pushl %edi # save edi

movl 8(%ebp),%eax # eax now holds x
movl 12(%ebp),%edx # edx now holds y

xor %ebx,%ebx
addl %eax,%ebx

.L1:
decl %edx
imull %ebx,%eax
cmpl $1,%edx
jg .L1

popl %edi # restore edi
popl %esi # restore esi
popl %ebx # restore ebx

leave
ret
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp

pushl %ebx # save ebx

movl 12(%ebp),%eax # load argv into eax (temp)
movl 4(%eax),%ebx # x = argv[1]
movl 8(%eax),%ecx # y = argv[2]

pushl %ecx # push y on the stack, as it gets
# clobbered by atoi

pushl %ebx # push x into atoi via stack
call atoi # call atoi
movl %eax,%ebx # move result into (x) ebx

call atoi # call atoi with y
movl %eax,%ecx # put result into (y) ecx

pushl %ecx # push y as second param
pushl %ebx # push x as first parameter
call pow # call power function

push %eax # push returned value
push $.LC0 # push fmt string
call printf # call printf

popl %ebx # restore ebx
xor %eax, %eax
leave
ret
.size main, .-main
.section .note.GNU-stack,"",@progbits
.ident "GCC: (GNU) 3.3.5 20050117 (prerelease) (SUSE Linux)"

Share this post


Link to post
Share on other sites
LessBread    1415
The next step with your pow function ...


.text
.globl pow
.type pow, @function
pow:
pushl %ebp
movl %esp, %ebp
pushl %ebx # save ebx
pushl %esi # save esi
pushl %edi # save edi

movl 8(%ebp),%eax # eax now holds x
movl 12(%ebp),%edx # edx now holds y

xor %ebx,%ebx
addl %eax,%ebx

.L1:
decl %edx
imull %ebx,%eax
cmpl $1,%edx
jg .L1

popl %edi # restore edi
popl %esi # restore esi
popl %ebx # restore ebx

leave
ret




... is to notice that since you don't use %edi or %esi you don't need to preserve them. And also that if you use %ecx instead of %ebx you won't have to preserve %ebx either. This reduces the number of instructions used by the function, thus making it faster which is good for a function like pow. The %ecx register is usually used as a counter, such as the loop countdown that you used %edx for. So you might consider using %ecx where you use %edx and %edx where you use %ebx.

Another thing to consider, but which I don't want to add to any confusion, is that the parameters can be accessed relative to %esp, further reducing the number of instructions used by the function. YMMV.


.text
.globl pow
.type pow, @function
pow:
movl 4(%esp),%eax # eax now holds x
movl 8(%esp),%ecx # ecx now holds y
xorl %edx,%edx
addl %eax,%edx
.L1:
decl %ecx
imull %edx,%eax
cmpl $1,%ecx
jg .L1
ret


Share this post


Link to post
Share on other sites
LessBread    1415
It looks like more than just once. In my earlier post I didn't examine main at all, but you've left the arguments to the functions on the stack.


pushl %ecx # push y as second param
pushl %ebx # push x as first parameter
call pow # call power function
addl $8,%esp # clean pow arguments from stack


Some functions clean the arguments from the stack as part of the return statement. For example, by including the number of bytes of arguments following the ret instruction, "ret $4" for one argument, "ret $8" for two arguments and so on.

Since I wrote about the pow function last time, I'll keep to it. The pow function doesn't clean the arguments from the stack, the function that calls it needs to do it - as the above code shows. A stack is like an array of unsigned longs, but stacks grow from higher addresses to lower addresses which is kind of strange thing to get your head around at first. What this means is that you subtract from a stack to expand it and add to it to retract it. Thus to remove the arguments add the number of bytes to esp. This goes for atoi and printf as well. Since atoi takes two arguments "addl $8,%esp" is needed following the call. The call for printf also takes two arguments so "addl $8,%esp" will work there as well, but other calls to printf will be different because printf can accept a variable number of arguments.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this