how are structs passed as parameters in assembly
Asked Answered
B

3

7

How are structs passed as parameters in assembly?

Since structs have sizes large than normal are the individual fields passed sequentially?

If so are they in reverse order like normal parameters?

Are their any differences between cdecl and stdcall?

Backfire answered 6/5, 2015 at 4:1 Comment(6)
Is this x86 or x86_64 and what assembler are you using?Shakta
it just standard x86 although i am curious about x86_64?Backfire
The reason it makes a difference is that the calling convention differs between the two with x86_64 being passed by register with the first six integer arguments (from the left) are passed in RDI, RSI, RDX, RCX, R8, and R9, in that order while x86 is passed on the stack in reverse order. I don't have the answer to the non-standard type size struct offhand. I'll poke around a bit.Shakta
Apparently structs are passed by reference (by pointer) and it is compiler dependent whether it is passed in EAX/AX or on the stack. Apparently most compilers don't follow the calling convention with regard to classes and structs due to the non-thread-safe condition created. Here is a link that discusses the differences between gcc and visual c++ X86 Assembly/High-Level Languages It contains further links to wikipedia, etc.Shakta
You should perhaps add an example code snippet you are thinking about... Are you asking about C structs and arrays, or something else?Evered
You could also clarify, if you indeed mean, "pass a struct as value, like passing it in C", or if you mean "pass a struct in any way, including like explicitly passing a C pointer to struct".Evered
P
8

In assembly, all bets are off and you can pass parameters any way you like, provided that the caller and the callee agree on how it's done.

Put arguments on the stack, put a pointer to them on the stack, put them into registers, stash them in a fixed memory location, it's all up to you. I've seen cases where some arguments were passed in registers, while others were passed on the stack or passed by reference.

How you transfer control is also up to you. Execute a "call" instruction, or a software interrupt. The old PDP-10 architecture has like five different ways of calling a subroutine, and you had to know which one to use. The IBM-360 architecture also has a number of ways.

(You want to see crazy? Read the famous Interrupt List, which was a collection of all the known software interrupt calls available for the 286 architecture. Practically every piece of software you installed under MS-DOS added some new software interrupts to the pot, and they each had their own calling convention and many of them collided with each other.)

In general, your best approach is to find out what other programmers are doing and do the same thing. Either that, or document your function very well so that users know how to call it.


Now, if your assembly is going to call or be called by another language, such as C, C++, Fortran, etc., then you need to research the standard calling conventions that were established by the language designers and which usually also depend on the architecture. For example, for C on 32-bit x86, the arguments would be passed on the stack, while for Sparc, up to five arguments would be passed in registers and anything beyond that went on the stack.

As for structures, the C standard calls for them to be unpacked and the individual elements to be passed as separate arguments, to be re-assembled into a structure by the callee. If the structure is very large, this can be insanely wasteful, so it's better to pass a pointer to the structure instead.

If the function returns a structure, the caller allocates space to receive it, and passes a pointer to that space as a "secret" argument to the function.

Arrays are always passed as pointers.

For Fortran, everything is passed by reference, meaning that values can be returned to any argument. Even constants are stashed in memory somewhere and a pointer to them is passed to the called subroutine. (Thus making it possible to accidentally change the value of a constant.)

Packing answered 7/7, 2016 at 1:53 Comment(0)
P
2

In most cases, the structs are passed as a pointer to the start of the struct.

The function then loads this pointer in some register and addresses the fields of the structure by their offsets.

Protestation answered 6/5, 2015 at 7:15 Comment(3)
Just to clarify: this is one way you might do it in assembly (and the way I would recommend). This isn't how C does it; C would shove the whole structure down the pipe. In C, you'd be better off passing a pointer to the struct rather than the struct itself.Packing
@EdwardFalk: Interesting note: C ABIs typically return large structs by reference (passing a pointer as a hidden first argument).Orcus
Well, we are talking about assembly, aren't we? In assembly, in general you can do whatever you want, but I described the most natural way.Protestation
T
0

Structures, like arrays, are passed by reference, so as a parameter: they are just a 32-bit parameter (the pointer to the first structure member), and that pointer is pushed on the stack in the cases of cdecl and stdcall.

If you passed a data structure by value, that means you have to push every member of the structure on the stack to the callee, which has a huge effect on performance – specially with large structures.

myarray dword 300 dup(?)
push offset myarray

Now this array has been pushed by reference (the pointer of the first member).

Toad answered 6/5, 2015 at 7:19 Comment(3)
"if you passed a data structure by values...", note that C indeed passes structs as values (actually C passes everything by value, as even arrays are considered to decay to pointers, which are then passed by value), that is the function can't modify the original.Evered
Wrong. Structures are never passed by reference; doing so would completely defeat the purpose of using explicit pointers to structs (or C++ references). Like hyde said, you never actually "pass" an array, only a pointer to its first member. This is a quirk of arrays only, and doesn't apply to anything else. If you're seeing otherwise it may be an optimization introduced by your compiler, which noticed it could do it without breaking the semantics of the language.Accumulative
@Martin: Windows x64 does pass structs by hidden-reference when the C passes them by value. godbolt.org/z/hnxdTa. (Note that it makes a copy of the named local for the arg to point to, because the caller "owns" that memory. It's a missed optimization because the caller's copy is dead after the call, it could let the callee destroy it, and no other threads have a reference to it.) So anyway, it's not a reference to the caller's original object, which I think is your point. Other calling conventions like x86-64 System V copy the whole struct to the stack.Orcus

© 2022 - 2024 — McMap. All rights reserved.