gnustep / libobjc2

Objective-C runtime library intended for use with Clang.
http://www.gnustep.org/
MIT License
426 stars 116 forks source link

AArch64 objc_msgSend does not adhere to variadic ABI #264

Closed hmelder closed 6 months ago

hmelder commented 6 months ago

According to the AAPCS64 (AArch64 Procedure Call Standard):

[...] in C, unprototyped and variadic functions require single-precision values to be converted to double-precision and char and short > values to be converted to int.

See: https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#:~:text=The%20rules%20here,converted%20to%20int.

Assume we have an IMP that takes a float as an argument. As IMP is a non-variadic function, the argument is passed in s0 (the lower 32-bit of v0), if it were to be called directly.

When calling objc_msgSend for the Selector that leads to IMP, we pass the float in d0 (64-vit of v0). The objc_msgSend implementation now tail-calls IMP with arguments in d0.

hmelder commented 6 months ago

Same problem with RISC-V:

Additionally, floating-point arguments to variadic functions (except those that are explicitly named in the parameter list) are passed in integer registers.

18.2 RVG Calling Convention: https://riscv.org/wp-content/uploads/2015/01/riscv-calling.pdf

hmelder commented 6 months ago

This is only the case, when we manually call objc_msgSend. The generated code by clang treats objc_msgSend as a non-variadic function.

davidchisnall commented 6 months ago

It should always be called as non-variadic, you are required to cast it to the right type before calling.

The only time it should be called with the variadic calling convention is when the underlying method is variadic (e.g. arrayWithObjects:. We might need to add a test for that case.

hmelder commented 6 months ago

It should always be called as non-variadic, you are required to cast it to the right type before calling.

That makes sense. Clang emits this too, when Objective-C code is rewritten to C++. I have not heard about this before.

davidchisnall commented 6 months ago

It used to be in the language spec, not sure if Apple has kept it up to date. It was really important on platforms like SPARC, where getting the calling convention wrong would land you on a trap instruction. It's also important with Apple's version of the AArch64 ABI (where there's even more divergence between variadic and non-variadic calling conventions).