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

Native Apple Silicon (M1) Support #5

Closed
voldyman opened this issue Jan 9, 2021 · 24 comments
Closed

Native Apple Silicon (M1) Support #5

voldyman opened this issue Jan 9, 2021 · 24 comments
Labels
enhancement New feature or request help wanted Extra attention is needed

Comments

@voldyman
Copy link

voldyman commented Jan 9, 2021

The project doesn't build on apple silicone yet, i think it's mainly because of the lack of assembly files.

@progrium
Copy link
Owner

progrium commented Jan 10, 2021 via email

@voldyman
Copy link
Author

I tried doing it but am not familiar with the internal information or arm/x86 assembly but looks fun to learn.

I might give it another go, no guarantees

@progrium progrium changed the title Apple Silicone (M1) Mac Support Apple Silicon (M1) Mac Support Feb 3, 2021
@stephanwesten
Copy link

Hi,

I m running on Mac m1 and get the following error when I make the pomodoro example (main, 8d2ca95):

make pomodoro
go run ./examples/pomodoro
# github.com/progrium/macdriver/objc
objc/typeinfo.go:64:15: undefined: typeInfoForType
objc/typeinfo.go:73:14: undefined: typeInfoForType
objc/typeinfo.go:77:15: undefined: typeInfoForType
objc/class.go:114:36: undefined: methodCallTarget

I do not understand how this compile error is m1 related...

@dmitshur
Copy link

dmitshur commented Feb 5, 2021

@stephanwesten It has to do with build constraints and the fact that GOARCH is arm64 instead of amd64 on M1. typeInfoForType is defined in a file named typeinfo_amd64.go, which is only selected when GOARCH is amd64. There isn't a a typeinfo_arm64.go file, which this issue is about.

@progrium
Copy link
Owner

progrium commented Feb 5, 2021

missing files from build constraints for diff platforms is part of it, but afaik the differences are minimal. the real missing piece is the implementation of the workaround in variadic for arm and then the rest should be pretty straightforward. really its all straightforward but somebody that knows or is willing to learn arm architecture will have to port variadic.

@dolmen
Copy link

dolmen commented Feb 5, 2021

@progrium
Copy link
Owner

progrium commented Feb 5, 2021

@dolmen awesome, thank you for making it easy to find from here

@progrium progrium added the enhancement New feature or request label Mar 8, 2021
@helperShang
Copy link

@stephanwesten

GOOS=darwin GOARCH=amd64 CGO_CFLAGS="-arch x86_64" CGO_ENABLED=1 go run -x  github.com/progrium/macdriver/examples/largetype

@progrium
Copy link
Owner

see also https://gist.github.com/progrium/b286cd8c82ce0825b2eb3b0b3a0720a0

but these are workarounds.

@progrium progrium added the help wanted Extra attention is needed label Mar 29, 2021
@progrium progrium added this to the 1.0.0 milestone Mar 30, 2021
@progrium progrium changed the title Apple Silicon (M1) Mac Support Native Apple Silicon (M1) Support Apr 6, 2021
@mgood
Copy link
Sponsor Collaborator

mgood commented May 11, 2021

I'm currently working on setting things up with Rosetta as described, but maybe I'll get a chance to come back to this later.

Some Apple docs that are likely to be useful:
https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms
https://developer.apple.com/documentation/apple-silicon/addressing-architectural-differences-in-your-macos-code

@progrium
Copy link
Owner

yep yep. remember go has no problem compiling to arm64 already, but the variadic package that every call in this library depends on needs to have an arm64 implementation since it drops into assembly. and it doesn't do much! all it's doing is making an alternative way to do a function call.

@mgood
Copy link
Sponsor Collaborator

mgood commented May 13, 2021

(making note of a few more findings)

It looks like it's not just variadic, but some of the necessary code is in objc/call_amd64.* and objc/msg_amd64.go for initializing the procedures calls for AMD64. We'll see if I have time to fully dig into the implementation, but ideally I would like to see if there's a way to more cleanly delineate between the stuff that's specific to ObjcC, and the portion that's setting up the architecture-specific procedure calls.

For ARM, this describes the process for initializing the registers & memory to pass parameters to the procedure:
https://github.com/ARM-software/abi-aa/blob/2bcab1e3b22d55170c563c3c7940134089176746/aapcs64/aapcs64.rst#parameter-passing

The Apple docs here about calling objc_msgSend were also enlightening:
https://developer.apple.com/documentation/apple-silicon/addressing-architectural-differences-in-your-macos-code#Enable-Strict-Type-Enforcement-for-Dynamic-Method-Dispatching

So, the way objc_msgSend is implemented, it expects args to be passed like "normal" positional parameters. They give this example for creating a type-safe pointer to call objc_msgSend with the expected parameter types for the underlying function definition:

// Declare a type-safe function pointer.
void (* didSaveDispatcher)(id,SEL,NSDocument *,BOOL,void *) = 
       (void(*)(id,SEL,NSDocument *,BOOL,void *))objc_msgSend;

So, this does NOT use the conventional C-style "variadic" parameter passing where you use va_list and va_start as described here:
https://github.com/ARM-software/abi-aa/blob/2bcab1e3b22d55170c563c3c7940134089176746/aapcs64/aapcs64.rst#appendix-variable-argument-lists

Per Apple:

Because objc_msgSend declares your method as variadic, the compiler places the method’s parameters on the stack, in accordance with the calling conventions for the arm64 architecture. However, the original method declaration contains fixed parameters, not variable parameters. As a result, the method’s implementation looks for its parameters in registers, which is where the compiler places fixed parameters for arm64. This mismatch causes the method call to generate undefined results.

@mgood
Copy link
Sponsor Collaborator

mgood commented May 30, 2021

In #52 I've added code that could be used to reimplement Send based on NSInvocation in a cross-platform way (just based on CGo code and the Obj-C runtime). This would address the objc/msg_<arch> and variadic implementations.

The other part is objc/call_<arch> which is used by AddMethod to send Obj-C method calls back to the Go implementation. Looking into things a bit more, it looks like we should also be able address those with NSInvocation by implementing forwardInvocation instead:
https://developer.apple.com/documentation/objectivec/nsobject/1571955-forwardinvocation

IIUC we can use that hook to create objects that instead of registering each method directly, call a common CGo implementation for forwardInvocation. In there we can call back into Go to look up the Go method implementation and pass along the arguments.

A couple of the files (e.g. objc/typeinfo_<arch>) are more generally for any 32- vs 64-bit platform (rather than a specific CPU instruction set) so we can make those use build tags to target the appropriate platforms instead.

@vtolstov
Copy link

Can somebody write current status of this problem?

@progrium
Copy link
Owner

progrium commented Sep 17, 2021 via email

@vtolstov
Copy link

very nice, last question - when ? =)
and last another question - what about support File Provider stuff ? ( https://developer.apple.com/documentation/fileprovider/macos_support )

@mgood
Copy link
Sponsor Collaborator

mgood commented Sep 20, 2021

There's no set timeline for this. For now your best bet is still to install an amd64 Go build and run with Rosetta.

I'm working on the generator, which will expand the breadth of APIs included, and reduces the dependencies on the dynamic Send calls which are currently X86-only. Though after the generator is merged there will still be some more work to support M1. We could disable compiling Send on M1, though macdriver/cocoa also depends on AddMethod which would also need ported to work on M1.

I'm not aware of anyone looking into File Provider support for macdriver yet, but it would be interesting.

@vtolstov
Copy link

thank you

marcuswu pushed a commit to marcuswu/deej that referenced this issue Jul 11, 2022
Build scripts can go back to normal once macdriver adds M1 support:
progrium/darwinkit#5
@sparky4pro
Copy link

Pull request #100 by mkrautz adds basic arm64 support.

Thank you mkrautz, this is wonderful.

@mkrautz
Copy link
Contributor

mkrautz commented Aug 11, 2022

@sparkylein You're welcome. Have you tried it? Does it work to your liking?
As the PR says, right now it only implements parameter passing and returns in registers, not on the stack. I don't know if any of the APIs that macdriver calls require enough arguments as to require the implementation to do more than it does now or not.

@sparky4pro
Copy link

@mkrautz I only tried to create a minimalistic window which worked.

However, it's strange that it works, because here https://github.com/below/HelloSilicon#listing-9-1 it is said that for variadic functions Apple silicon diverges from the ARM64 standard ABI. Where Linux will accept arguments passed in the registers, for Darwin the arguments must be passed on the stack.

I'm not an assembly coder myself, so I don't know why what you did works...
It could also very well be that I'm mixing-up stuff, and the gentleman from HelloSilicon is talking about something different.
In that case, I apologize for my ignorance :P

@mkrautz
Copy link
Contributor

mkrautz commented Aug 13, 2022

It works because on darwin/arm64, Apple redefined objc_msgSend to use the regular calling convention.

objc_msgSend is defined as (void)objc_msgSend(void) in the headers, and if you must call it directly, you cast it to have the signature you require. That way, the compiler generates a non-variadic call, which objc_msgSend on arm64 requires.

That's also why the package name 'variadic' is a bit misleading with the new PR. Perhaps a better name would be 'objcffi'.

@sparky4pro
Copy link

Interesting. Thanks for the info.
Maybe this is the reason why this https://github.com/hsiafan/cocoa works without any assembly magic :)
(uses only some Objective-C glue...)

@mgood
Copy link
Sponsor Collaborator

mgood commented Aug 17, 2023

I guess just to clarify now that this is based on the https://github.com/hsiafan/cocoa package mentioned, it's using libffi to handle the function calls instead. It's still doing something equivalent to the prior assembly implementation, but now that's abstracted to use a common library for that architecture-specific ABI translation instead of reimplementing it here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

10 participants