Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations IamaSherpa on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

push/pop in a function?

Status
Not open for further replies.

cpjust

Programmer
Sep 23, 2003
2,132
US
Hi, I'm using the GAS assembler on Linux (x86) and for some strange reason I can't get a pointer to a number to print out right when I pass it to a function, but if I push & pop it in the main function it prints the right value. What am I doing wrong?
Code:
# - bubble.asm - Bubble sort...
.section .data
output:
	.asciz "Values: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n"

endline:
	.asciz "XXXXX\n"

val:
	.asciz "Val - %d\n"

values:
	.int 105, 235, 61, 315, 134, 221, 53, 145, 117, 5

.section .text
.globl main
main:
	nop
	movl	$values,	%esi
	pushl	%esi
	call	print
	addl	$4,		%esp	# Pop the stack.

	push	$0
	call	exit			# exit( 0 );

# Function:  void print( int* )
print:
	popl	%esi
	pushl	(%esi)		# Push the value at address in esi on the stack.
	pushl	$val
	call	printf		# printf( val, *esi );
	addl	$8,	%esp	# Pop the stack.
	ret
Instead of printing out "Val - 105" it prints something like "Val - 11305948" and then gets a Segmentation Fault.
 
In the subfunction Print, you pop the return addres to
the main function from the stack, not the string you pushed in the main.

Tessa
 
OK, in that case, what do I do to get it to work properly?
Should I pop twice? If so, do I need to push the main address back on before I ret?

Thanks.
 
Nevermind. I found the answer.
Code:
# Function:  void print( int* )
print:
    pushl   %ebp           # Save the stack.
    movl    %ebp,    %esp

    movl    8(%esp), %esi  # Get int* parameter.
    pushl   (%esi)         # Push the value at address in esi on the stack.
    pushl   $val
    call    printf         # printf( val, *esi );
    addl    $8,      %esp  # Pop the stack.

    movl    %ebp,    %esp  # Restore the stack.
    popl    %ebp
    ret
 
Now you are making a real mess of the stack.
Please try to explain what you wanted to do in the
above example.

Tessa
 
There has been some dispute about whether stack variables should be removed by the calling or the called code. For safety, it's probably better that the caller (which knows what was pushed) removes whatever it pushed, but for code compactness, it's better for the called subroutine to remove what it expected. Some HLLs put an extra variable on the stack indicating how many bytes were pushed, which means that a common 'return' routine can then get rid of them all and return to the caller. Anyway, there should be no need to pop data from the stack within the subroutine - just access it at the expected offset from the stack pointer.
 
It looks like he's trying to do what an HLL compiler does: that code reminds me of the subroutine entry sequence in 16 bit FORTRAN 77 code.

On entry, the BP reg is pushed, then BP = SP to give you a means of accessing the data on the stack using BP as a pointer.

He's using 32 bit regs, but it's much the same sort of idea.

I'd cheat personally & see what the compiler produces itself, then hack it to suit.

But then I'm an old hacker anyway & have been for 25 years or so.
 
What that version of the print() function does is print 1 int that is passed as a pointer to an int. (Once I got it working I made it print an array of ints).
I skipped ahead in "Professional Assembly Language" by Richard Blum to Chapter 11 and found this method in the "Passing Data Values in C Style" section:
Code:
    pushl   %ebp
    movl    %ebp,    %esp
...
    movl    %ebp,    %esp
    popl    %ebp
    ret
When I tried popping the return address into a register, then popping the values passed to the function and pushing the return address back on the stack, I just got a segmentation fault.
Accessing the parameters by their offsets works fine though.
 
When a program is running under protected mode it is posible that a segment is protected in such a way that you
even can't pop its segment indicator (that is what's there, not the real segment address).
By using the offset relative to the stack (may also be done by directly using esp)you will illeminate the checking for segment indicators.

Tessa

 
Just have a look at what high-level languages do. Dissassemble a few "hello world" function calls made in your favourite compilers. Or get hold of an old-fashioned 86 assembler tutor and look at the inevitable chapter called something like "interfacting with high level languages".

It doesn't matter in the faintest whether removing local variables and parameters is dealt with by the caller or the called; you just need to make your mind up and stick with it, and if you are interfacing with a high level language, follow the convention of that language. They differ.

Typical pascal approach, for instance:

calling routine:
push local_variable
call function

called routine:
push bp (save pointer to area of stack used by caller )
mov bp, sp (set up pointer for called)
sub sp, something (make some local space for local vars)
... do everything....
mov sp, bp (undo all the local vars and prepare to ret)
pop bp (retrieve pointer to old area of stack for caller)
ret xxx (return clearing xxx bytes of parameters sent)
 
... forgot to add something. If you write assembly that looks just like a high-level language, and handles functions in exactly that way, it will probably run just about as well as code compiled from a high-level language. Not faster. That might not be what you want. You can also pass parameters in registers and take results in registers.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top