Skip to content
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

chore: added Apple M1 platform detection when compiling with Rosetta 2 #40315

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

kyr0
Copy link

@kyr0 kyr0 commented Oct 4, 2021

This PR handles the special case when a developer is trying to compile on an Apple Silicon M1 machine with a toolchain that is dual-arch (arm64e and x86_64) which is a valid case when the build toolchain is x86_64 based and Rosetta 2 is active. In this case, the current configure.py is unable to figure out the correct host platform as the toolchain will just return x86_64 even if the true host arch is arm64.

The impl. in the commit is trying to keep the current logic as untouched as possible to prevent any regressions.

Fixes: #40302

@nodejs-github-bot nodejs-github-bot added build Issues and PRs related to build files or the CI. needs-ci PRs that need a full CI run. labels Oct 4, 2021
configure.py Outdated
@@ -1053,6 +1053,7 @@ def host_arch_cc():
k = cc_macros(os.environ.get('CC_host'))

matchup = {
'_LP64' : 'arm64',
Copy link
Member

Choose a reason for hiding this comment

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

_LP64 is not arm64 specific so this is going to break non-arm 64-bit platforms.

Copy link
Author

Choose a reason for hiding this comment

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

True, I just came to the same understanding while you were commenting. I wrote a sum-up of the issue in my updated PR desc.

Copy link
Author

@kyr0 kyr0 Oct 4, 2021

Choose a reason for hiding this comment

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

The true cause of the issue is that when we assume that the parsed output of e.g. ${CC_host} -dM -E - will contain the host arch at a specific index like we do here:

node/configure.py

Line 1072 in d23af0d

if i in k and k[i] != '0':

Just isn't there because the output of cc (in my case) is not standardized (as it seems) or other circumstances make the toolchain write stuff like _LP64 there instead of the host arch identifier... well, then we're going to assume the wrong host_arch here, trigger the cross-compilation, and v8 compilation will fail because it tries to compile the wrong inline assembly impl.

Any ideas? :)

Copy link
Author

Choose a reason for hiding this comment

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

I could just put a special override if-condition to check for M1 as a platform instead of relying on the cc output but it is such a nice abstract impl, that it seems a bit hacky to do that. However, Node.js not being able to compile on M1 is quite a blocker... so maybe...

Copy link
Author

@kyr0 kyr0 Oct 4, 2021

Choose a reason for hiding this comment

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

Unfortunately, in the case of building with a fat binary, even running uname -p or uname -m gives me i386 and x86_64. This is probably because Rosetta2 emulates "I'm running on an x86_64 machine". There is only one way to escape this rabbit hole (as far as I can see atm): Running uname -v which gives us the kernel version string as Darwin Kernel Version 20.6.0: Mon Aug 30 06:12:20 PDT 2021; root:xnu-7195.141.6~3/RELEASE_ARM64_T8101.

This off behaviour can not reproduced by running uname -p or uname -m on the command line of an M1 machine. It can only reproduced when the parent process that executes these shell commands is emulated via Rosetta.

As we can see, the uname -v kernel version string includes the ARM64 release tag which indicates that the kernel is actually running on an ARM platform and the toolchain is running as a universal binary, translated via Rosetta2.

@mscdex mscdex added the wip Issues and PRs that are still a work in progress. label Oct 4, 2021
@kyr0 kyr0 force-pushed the chore_m1_host_arch_fix branch from d23af0d to b2c1f89 Compare October 4, 2021 17:29
@kyr0
Copy link
Author

kyr0 commented Oct 4, 2021

I now implemented this extra check using the uname -v approach; it is so specific that it def. shouldn't break other platforms: The probability that uname -v includes "Darwin" and "ARM64" should be 0 for any other platform than Apple / M1, right? Only in this case, the condition will trigger and override the wrong assumption that is just happening because the whole toolchain is in emulation mode (Rosetta)

stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
except OSError:
Copy link
Author

Choose a reason for hiding this comment

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

Could someone check if other types of error/exception could happen in other environments when uname is not available? I tried with different command names that are def. not available and the catch/forget code works well

@kyr0 kyr0 force-pushed the chore_m1_host_arch_fix branch from b2c1f89 to 3ea3a9b Compare October 4, 2021 18:11
@kyr0 kyr0 changed the title chore: added Apple Silicon M1 host arch _LP64 to the arch mapping added Apple M1 platform detection when compiling with Rosetta 2 Oct 4, 2021
@kyr0 kyr0 changed the title added Apple M1 platform detection when compiling with Rosetta 2 chore: added Apple M1 platform detection when compiling with Rosetta 2 Oct 4, 2021
@mscdex
Copy link
Contributor

mscdex commented Oct 4, 2021

Instead of relying on uname, wouldn't a better solution to check the value of the sysctl sysctl.proc_translated if the platform is Darwin? If it gives a value of 1 then it's executing under Rosetta.

Apparently you can also query the macOS I/O registry to get the real underlying hardware, such as with ioreg -c IOPlatformDevice.

However should we really be supporting this? I don't own/use Apple hardware so I'm not familiar with Rosetta, but it seems like with a change like this we'd be implicitly cross compiling (with no way to create an x86 binary), which might be surprising?

@kyr0
Copy link
Author

kyr0 commented Oct 4, 2021

@mscdex Nice suggestions! Actually, it is a valid point that you raised; the default build target could also be x86_64 for Rosetta; but it would be nice (from my point of view) that if you're running Rosetta, it wouldn't crash at least, when you specify --dest-cpu=arm64; of course, to make this happen, I'd need to change my PR further to additionally check for the --dest-cpu flag as well. Or, as mentioned by me in the issue, we could also define this as an edge case and leave it as it is; but then, you cannot build arm64 on M1 when your build toolchain is "universal" which is also kinda counterintuitive that you can't build arm64 on an arm64 machine :)) But yeah, it's an edge case somehow; it doesn't affect most M1 users as most of them seem to not have a universal build toolchain installed. In case we close this, it would be at least nice to document the behavior I think.. other users could bump into this as well.

Copy link
Member

@AshCripps AshCripps left a comment

Choose a reason for hiding this comment

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

Blocking as I believe this will break our release builds and will need verifying.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
build Issues and PRs related to build files or the CI. needs-ci PRs that need a full CI run. wip Issues and PRs that are still a work in progress.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Cannot build arm64 arch on macOS / M1 host
5 participants