Skip to content

Fix GH-16168: php 8.1 and earlier crash immediately when compiled with Xcode 16 clang on macOS 15 #16348

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from

Conversation

nielsdos
Copy link
Member

The inline assembly uses labels with the prefix .L. On Linux systems this is the local label prefix. It appears that macOS uses L as a local prefix, which means that the prefix used in the inline assembly is not local for macOS systems [1].
This causes the compiler to get confused and move part of the inline assembly into another function. This is avoided on PHP 8.2 and up by the fact that it uses zend_never_inline NOIPA, but nothing guarantees that compiler changes won't affect this as well.

To solve this issue, we instead use local labels. These will make the compiler pick the correct prefix, preventing the issue.

Additionally, while here, we also change the computation of delta. It is undefined behaviour to compute the pointer difference between two different objects. To circumvent this, we cast first to uintptr_t.

This change is cleanly backportable to 8.1 for vendors to pick up.

[1] #16168 (comment)

This was tested by @ryandesign on their macOS machine with their setup and this patch works for them on old versions too.

…with Xcode 16 clang on macOS 15

The inline assembly uses labels with the prefix `.L`. On Linux systems
this is the local label prefix. It appears that macOS uses `L` as a
local prefix, which means that the prefix used in the inline assembly is not
local for macOS systems [1].
This causes the compiler to get confused and move part of the inline assembly
into another function. This is avoided on PHP 8.2 and up by the fact that it
uses `zend_never_inline NOIPA`, but nothing guarantees that compiler
changes won't affect this as well.

To solve this issue, we instead use local labels. These will make the
compiler pick the correct prefix, preventing the issue.

Additionally, while here, we also change the computation of `delta`.
It is undefined behaviour to compute the pointer difference between
two different objects. To circumvent this, we cast first to `uintptr_t`.

This change is cleanly backportable to 8.1 for vendors to pick up.

[1] php#16168 (comment)
@ryandesign
Copy link
Contributor

This causes the compiler to get confused and move part of the inline assembly into another function.

I mischaracterized this in my report. It doesn't get moved. The assembly code ends up in five different places due to inlining. The fifth occurrence is the entire correct code. The four earlier instances are missing one part; the optimizer apparently decided that it could merge the five instances of that part of the function into one and adjust the jump instructions to match. This of course means that control passes incorrectly from those first four functions into the middle of the fifth function.

Copy link
Member

@dstogov dstogov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ASM part looks OK.

size_t delta = (const char*)s2 - (const char*)s1;
uintptr_t delta = (uintptr_t) s2 - (uintptr_t) s1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a question: what was wrong with size_t.
Probably it would be better to use ssize_t (or offset_t).
Now, may be intptr_t is better (I'm not sure).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think size_t is good too. On the x86 platforms they are the same size anyway. I just changed it to match the types of the right hand side without thinking too much about it. I can change it back to size_t.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem. uintptr_t is good as well. (size_t and pointers have the same size, according to some standard).

@nielsdos
Copy link
Member Author

@ryandesign right, I'll update the description, thanks for checking

@nielsdos nielsdos closed this in e2e2b3a Oct 11, 2024
nielsdos added a commit that referenced this pull request Dec 9, 2024
Agreed by RM: #16168 (comment)

The inline assembly uses labels with the prefix `.L`. On Linux systems
this is the local label prefix. It appears that macOS uses `L` as a
local prefix, which means that the prefix used in the inline assembly is not
local for macOS systems [1].
When combined with inlining, this causes the compiler to get confused
and merge a part of the inline assembly between different functions,
causing control flow to jump from one function to another function.
This is avoided on PHP 8.2 and up by the fact that it
uses `zend_never_inline NOIPA`, but nothing guarantees that compiler
changes won't affect this as well.

To solve this issue, we instead use local labels. These will make the
compiler pick the correct prefix, preventing the issue.

Additionally, while here, we also change the computation of `delta`.
It is undefined behaviour to compute the pointer difference between
two different objects. To circumvent this, we cast first to `uintptr_t`.

This change is cleanly backportable to 8.1 for vendors to pick up.

[1] #16168 (comment)

With the help of investigation and testing of @ryandesign.

Closes GH-16348.
nielsdos added a commit that referenced this pull request Dec 9, 2024
nielsdos added a commit that referenced this pull request Dec 9, 2024
nielsdos added a commit that referenced this pull request Dec 9, 2024
nielsdos added a commit that referenced this pull request Dec 9, 2024
* PHP-8.4:
  Backport GH-16348
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

php 8.1 and earlier crash immediately when compiled with Xcode 16 clang on macOS 15
3 participants