Variable Number of Parameters in Assembly

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Fear
Member
Member
Posts: 39
Joined: Wed May 24, 2006 11:00 pm

Variable Number of Parameters in Assembly

Post by Fear »

In assembly (nasm), is it possible to set up a procedure that can take any number of argument? I know that I could just push more parameters onto the stack, but how would I make the procedure identify how many parameters have been passed. Either way, the point is to make a printf type function. Thanks in advance.
ntfs
Posts: 19
Joined: Tue Dec 12, 2006 9:55 am
Location: Czech Republic Prague

Post by ntfs »

Function has to identify how many paramters have been passed. For example printf read this from format string. Or you can pass an int parameter at any time to specify the count. I thing it works like this.
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Post by Solar »

Correct, printf() uses the format string to determine how many parameters (and of which type) are to be popped from the stack - which is why it crashes when you don't supply enough parameters.
Every good solution is obvious once you've found it.
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Post by Candy »

Printf has no idea how many parameters you pushed. It really doesn't know. You can call it with 0 with a printf list of 3 items and it'll just take the bytes that were on the stack and print them instead. You can give it too many items and it doesn't care. An unportable way of printing a 64-bit number as two halves is to push just the 64-bit number and to print "%i %i".
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Variable Number of Parameters in Assembly

Post by Brendan »

Hi,

Is a "printf()" function actually necessary?

I've always used several functions - printString, printHexByte, printHexDword, printDec, etc. This isn't as convenient to use, but removes the need to worry about supporting a variable number of arguments and produces better code (i.e. there isn't any run-time parsing of the format string).


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
User avatar
Colonel Kernel
Member
Member
Posts: 1437
Joined: Tue Oct 17, 2006 6:06 pm
Location: Vancouver, BC, Canada
Contact:

Re: Variable Number of Parameters in Assembly

Post by Colonel Kernel »

Brendan wrote:Is a "printf()" function actually necessary?
This is like asking if C is necessary. Technically no, we could all write our programs in assembler or even brainfuck, but many people find C more convenient. The same goes for printf()... at least in C. Other languages have much better implementations of variadic functions.
Top three reasons why my OS project died:
  1. Too much overtime at work
  2. Got married
  3. My brain got stuck in an infinite loop while trying to design the memory manager
Don't let this happen to you!
Mike
Member
Member
Posts: 25
Joined: Tue Oct 17, 2006 7:57 pm

Post by Mike »

Technically no, we could all write our programs in assembler or even brainfuck, but many people find C more convenient.
:shock: :lol: Now there's the understatement of the year![/quote]
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Variable Number of Parameters in Assembly

Post by Brendan »

Hi,
Colonel Kernel wrote:
Brendan wrote:Is a "printf()" function actually necessary?
This is like asking if C is necessary. Technically no, we could all write our programs in assembler or even brainfuck, but many people find C more convenient. The same goes for printf()... at least in C. Other languages have much better implementations of variadic functions.
Hehee - what I said does seem a little silly if it's taken as a general question. As a response to the original posters questions (e.g. "In assembly (nasm), is it possible to set up a procedure that can take any number of argument?") it does make more sense....

Given that I've never implemented a "printf" like function or used variadic functions in any of my (assembly) OSs, my question remains - i.e. is a "printf()" function (and variadic functions) actually necessary for Fear's code?


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Ready4Dis
Member
Member
Posts: 571
Joined: Sat Nov 18, 2006 9:11 am

Post by Ready4Dis »

How printf works (I have written my own before) is this:

You push a variable number of arguments onto the stack, while parsing the string for variables to display, you increment a pointer into the stack to read the next variable... the calling location is responsible for popping/fixing the stack after the return call (this is normal for a C call), so just make sure you pop (increment) the SP after your call... example:

Code: Select all

Printf:
push bp       ;So we can restore it later
mov bp, sp  ;Grab our current SP
pusha         ; Push everything else onto the stack...
mov eax, bp
add eax, 4    ;if 32-bit, bp is 4 bytes, so increment past the push of BP
;now eax is a pointer to our first variable...
;we can parse the string until we find a %c, %d, %s, %i, %x, whatever
;and increment eax by the correct amount for the next variable
;of course, we would have to traverse the string held in ESI for
;the keywords and display everything properly :).
popa
pop bp
ret

DisplayStr db 'This is a test %d %c",0

Main:
push [dword] 10
push [byte] 'c'
mov esi, DisplayStr
call Printf
add sp, 5   ;removes the dword (4 bytes) and byte (1 byte)
Fear
Member
Member
Posts: 39
Joined: Wed May 24, 2006 11:00 pm

Post by Fear »

Ok, thanks. But I have one question: Lets say that I keep a running track of how many additional parameters I pop off the stack. Could I increment ESP at the end of the function to prevent having clean-up code all over the place? I mean its so much easier to call things when you don't have to ask yourself "What do I have to do after the call to make sure I didn't break everything"
TheQuux
Member
Member
Posts: 73
Joined: Sun Oct 22, 2006 6:49 pm

Post by TheQuux »

Fear wrote:Could I increment ESP at the end of the function to prevent having clean-up code all over the place?
Yes and no. Yes, in that you could parse the format string and pop the requisite number of arguments (like the Pascal convention), but keep in mind that you could have passed the wrong number of args, in which case that will crash your kernel.

For variadic functions, the C calling convention is used, in which the caller pops the args (only the caller knows how many args are passed)

For example, suppose you want to print the address of the poweroff function, which takes no args. So, you accidentally type (in C, because it's more clear

Code: Select all

push poweroff
push .msg
call printf
ret
.msg: "Poweroff: $x", 0x0d, 0x0a, 0x00
Printf prints out "Poweroff: $x", pops ONE ARGUMENT.
Then, it returns, and your function returns to the address which is on the stack, which is the poweroff function. So your computer powers off, and you have no idea what happened.

Now, consider if the caller is responsible for popping the args. Printf returns succressfully, the caller pops two args (it knows how many it pushed) and then returns... to the right address.
My project: Xenon
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Post by Brendan »

Hi,
Fear wrote:Ok, thanks. But I have one question: Lets say that I keep a running track of how many additional parameters I pop off the stack. Could I increment ESP at the end of the function to prevent having clean-up code all over the place? I mean its so much easier to call things when you don't have to ask yourself "What do I have to do after the call to make sure I didn't break everything"
The problem here is that during processing, the stack could look something like:
  • local variables (if any)
    saved registers (if any)
    return EIP
    arguments
    BOTTOM OF STACK
What you'd want is for the stack to look like this:
  • arguments
    local variables (if any)
    saved registers (if any)
    return EIP
    BOTTOM OF STACK
That way you could "pop" the arguments off the stack as you use them. Unfortunately you can't re-arrange the stack at the start of your code to suit, because you don't know how many arguments were passed until you find them in the format string.

You could do something like:

Code: Select all

my_printf:
    pop edx        ;edx = return address

    ;Process everything
    ;Clean everything off of the stack
         
    push edx       ;Put the return address back on the stack
    ret
The problem here is that it'd be messy and at least one register must remain trashed after the call (the one used to store the return address).

I'd be tempted to do things a different way - use macros.

Code: Select all

%macro  MY_PRINTF 1-* 
    %rep %0 
    %rotate -1 
        push %1 
    %endrep 
    call my_printf
    add esp,%0 * 4
%endmacro

    MY_PRINTF address_of_string1, 1, 2
    MY_PRINTF address_of_string2, [some_variable], [esp+12]
    MY_PRINTF address_of_string3, eax, ebx, ecx

Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
m
Member
Member
Posts: 67
Joined: Sat Nov 25, 2006 6:33 am
Location: PRC

Re: Variable Number of Parameters in Assembly

Post by m »

Hi.
Fear wrote:In assembly (nasm), is it possible to set up a procedure that can take any number of argument? I know that I could just push more parameters onto the stack, but how would I make the procedure identify how many parameters have been passed. Either way, the point is to make a printf type function. Thanks in advance.
I have a dull method but it may work.

You can pass the total number of the passed parameters as the last parameter to push onto the stack.In this way,the called procedure can first pop the parameter containing the total number(which is on the top of the stack) and identify the total number of the rest passed parameters according to it(the total number excludes the last one).

To make this method robust,you may want to write a specialised stack-operating procedure for passing parameters.

I hope I'm not screwing your idea and I hope that may help. :wink:
m
Member
Member
Posts: 67
Joined: Sat Nov 25, 2006 6:33 am
Location: PRC

Post by m »

Hi.
In previous method the calling procedure has the responsibility to give the correct number of parameters.I've just come up with a better version.You can push a sign first(e.g. it can be a NUL in ASCII) and other parameters follow.The called procedure pop and process each parameter in a loop until it meets the sign indicating the end of all the parameters.
Ready4Dis
Member
Member
Posts: 571
Joined: Sat Nov 18, 2006 9:11 am

Post by Ready4Dis »

m wrote:Hi.
In previous method the calling procedure has the responsibility to give the correct number of parameters.I've just come up with a better version.You can push a sign first(e.g. it can be a NUL in ASCII) and other parameters follow.The called procedure pop and process each parameter in a loop until it meets the sign indicating the end of all the parameters.
It's not very uncommon for a value of zero to appear in an argument list though, how would you determine the difference? In C it's the compilers job that after each call, it restores the SP depending on how many arguments where sent, which is simple because it knows how many and what size where passed, in assembly it feels redundant, but it's the simplest way really. What's the point of passing the total # of arguments so you can count and calculate how much to increment SP, when it's just as easy, if not easier, to just press +1+2+4, whatever :). Just sucks when you have a ton of variables passed, but hopefully you won't be trying to display that many in one line! One last thing, make sure you push them last to first, so that the first one you come upon is the first in your list ;).

One last note, if you are really feeling froggy, you *could* pass the argument count or size as the first parameter, and write some code to read the pushed address from the stack, pop all variables from the stack, push the return address back onto the stack, then call ret. This would work ok, but is a bit more complex and still requires passing the # of variables or size of total variables (in which case you could easily just do the add sp, XX).
Post Reply