Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 196 Vote(s) - 3.54 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Does each PUSH instruction push a multiple of 8 bytes on x64?

#1
On x64, does each PUSH instruction push a multiple of 8 bytes? If not, how much does it push?

Also, how much stack space does each function parameter consume?
Reply

#2
### PUSH Operand Size in 64-bit mode

The size of the value pushed on the stack and the amount that the stack pointer is adjusted by depends on the operand size of the PUSH instruction. In 64-bit mode the operand size can only be 16-bit or 64-bit. It's not possible to encode a 32-bit PUSH instruction in 64-bit mode and it's not possible to encode an 8-bit PUSH instruction in any mode.

For example, these are all 64-bit PUSH instructions:

push rax
push 1 ; 8-bit immediate sign-extended to 64 bits
push 65536 ; 32-bit immediate sign-extended to 64 bits
push QWORD PTR[0]
push fs ; 16-bit segment register zero-extended to 64 bits

The above instructions all subtract 8 from RSP and then write a 64-bit value to the location pointed to by RSP.

These are all 16-bit PUSH instructions:

push ax
push WORD PTR[0]

These instructions subtract 2 from RSP and then write a 16-bit value to the location pointed by RSP. Because they badly misalign the stack, using a 16-bit PUSH in 64-bit mode is pretty much always a mistake. Instead you should load the 16-bit value into a register (if not already there), extend it as necessary, and then use a 64-bit PUSH.

The following instructions are illegal and can't be encoded in 64-bit mode:

push al
push eax
push BYTE PTR[0]
push DWORD PTR[0]
push 0100000000h ; 64-bit immediate value isn't supported

Pushing an 8-bit or 32-bit value on the stack requires loading the value into a register, extending it and then using a 64-bit PUSH, just like you should do with 16-bit values.

### Parameter Passing in 64-bit mode

Generally speaking, in 64-bit mode function arguments aren't passed on stack. Both the Microsoft and Linux 64-bit x86 calling conventions pass most arguments in registers. The stack is only used when there's not enough room in registers to pass the arguments to a function. In that case each argument takes up one or more 8 byte stack slots. Note that compilers won't necessarily use PUSH instructions to place these arguments onto the stack. A common strategy is to allocate enough space on the stack for all of a function's outgoing arguments in the function prologue and then use MOV instructions to put arguments on the stack as necessary.
Reply

#3
No, but in practice, one always pushes an 8 byte value onto the stack.

Function parameters consuming varying amounts of stack space depending on the size of the function parameter and whether it is passed in the stack, in the registers, or passed by reference.

*If* one passes a function parameter in the stack by *pushing*, then the fact that there are convenient push instructions that pushes 8 bytes strongly suggests that you pass the parameter as an 8 byte value. For pointers, int64 and plain doubles, this is obviously easy. For char, bool, short, and other types whose memory size is smaller, what most compilers do is push the value in an 8 byte chunk. Types that take 16 or 32 bytes might be pushed by the compiler with several push instructions. Bigger values tend not to get passed by pushing; often a compiler tries to pass a pointer to a bigger value rather than pass the value itself. {I've built a compiler that can pass arbitrarily big values, but it does so by making space in the stack, and then executing a block move instruction]. Details vary from compiler to compiler, and according to the language semantics of the program being compiled.

A really clever compiler might notice that several arguments are small and can be packed into an 8 byte quantity that only requires a single push. I've not seen one actually do that, probably because it takes work to pack such values together into a register, and push instructions are already pretty fast by design and by cache.

It is possible to push smaller values onto the stack. This is legal according to the architecture, but is likely to cause a mis-aligned access performance hit if the set of small values pushed isn't a multiple of 8 bytes. And then one must be careful to pop off the non-multiple correctly to restore stack alignment. Not useful in my experience (see code golf comment by Peter Cordes).

If you pass the value in a register, nothing gets pushed :-}

One might arrange to store parameter values in a well known locations in the stack. Then there isn't any push :-}
Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through