-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
stage2: code signing in self-hosted MachO linker (arm64) #7103
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
Comments
I thought I'm going to post an update. Yesterday I have finally managed to get the snippet mentioned in the issue description to generate a valid MachO executable that in turn was successfully code signed using Apple's From what I see, there are two critical bits that need to be satisfied for MachO to be code signed and accepted by the kernel on Apple Silicon:
The cleaned-up changes required to run the snippet above can be found in my local branch kubkon/zig.git#stage2-arm-macos. I will not be pushing these to master branch just yet until I work out how to go about the PIE requirement given our goal of incremental linking in stage2 (they don't really go hand-in-hand). |
"I was able to apply an ad-hoc signature to a binary on my Intel mac and transfer it to my M1 mac and it ran just fine. " - golang/go#42684 (comment) |
That’s some really good news @komuw! |
Is #4503 a blocker? |
@kubkon
|
There's also a tool used in nixOS to inject adhoc signatures; https://github.com/thefloweringash/sigtool |
You’re the best @komuw, thanks! If this indeed generates a valid signature, we’re one step closer to home! |
It’s got to be done, yep, but my thinking was that initially we’ll support it only on macOS, since there’s no running from it now. |
OK y'all, got some really good news. The first draft of in-house, adhoc codesigning mechanism which makes up for the very last stage of flushing the output binary from the self-hosted linker, is alive. I repeat, is alive. 😎 Many thanks to @komuw for finding the Lastly, if you feel like prodding what's inside the code signature data section, I've added a flag for dissecting that section to ZachO, and it goes like this:
|
@Mouvedia no, it doesn’t. For the PIE, which I’m working on right now, we need to tweak the GOT in stage2. Currently, we use absolute addresses to indirect via GOT which obviously will not work with PIE. |
Quick update: I’ve got the PoC of PIE on |
FYI, #7231 brings this one home! |
This is a meta issue for tracking progress on code signing of binaries generated with the self-hosted MachO linker targeting arm64 Macs. I also hope to disseminate knowledge about the signing process used and enforced by Apple on the latest arm64 platform.
Background
With the release of Apple Silicon and macOS 11 Big Sur, Apple is now enforcing code signed binaries even at debugging stage. Essentially speaking, if the user wants to build an app/binary for local use/testing, they are expected to do the strict validation of the MachO binary and apply adhoc code signing. There is a pretty good stackExchange answer on that topic as well.
My understanding here is that adhoc signed binary can only be used on the machine it was originally built on; however, this requires further investigation.
What does all of this mean for Zig?
Cross-compilation to arm64 Macs will become tricky due to the additional code signing requirement. Distribution of Zig binaries as well; however for the latter I assume the standard code signing process required by Apple on other platforms (iOS, watchOS, tvOS) will be used here --- that is, obtaining a developer identity and certificate, and signing the binary with it. @jedisct1 pointed me to some nice, existing OSS solutions that do this and were not developed by Apple (see for instance gon) which hints on the possibility of having a similar solution written in Zig but hosted as a separate, preferably community-driven project. (Any takers? 😁)
What about local debug builds? @andrewrk suggested we investigate if perhaps there is an exception in the kernel which at least permits non-signed binaries to run fine via Apple debugger, however, this is not the case. No code signature means immediate
SIGKILL -9
even for binaries build from source by the user on the very Mac.With this in mind, my idea, and what I've been trying hard to understand and explore, is how should we tweak the self-hosted MachO linker to be able to generate a valid adhoc code signature. My idea was for this research effort to proceed in the following 3 steps:
codesign -s - binary
--- this will generate an adhoc signature and allow running of thebinary
locally.codesign -s -
in the self-hosted linker.Depending on progress, we'd either go immediately from 1 -> 3, or 1 -> 2 and optionally -> 3.
The story so far...
Easy stuff first. The code signature is stored inside a
__LINKEDIT
segment at an offset pointed to byLC_CODE_SIGNATURE
load command. The load command itself is alinked_data_command
.The structure that's embedded into the binary is a little bit more cryptic and complicated. I couldn't find much info about it, however, from browsing Apple's sources, I managed to come up with a partial parser and included it in the draft helper project Zacho#64f0a0d. This is based largely on SecurityTool/codesign.c. In fact, this tool pointed out that a
LC_VERSION_MIN_MACOSX
load command might not be optional after all since its presence or absence has direct effect on the code signature version generated and embedded within the binary bycodesign
utility.Now onto the more tricky stuff. It turns out that
codesign
will perform strict validation of the MachO binary before it is signed, and if the binary doesn't conform to, it will either refuse to sign and generate some garbage. I'm still to work out what that is, however, I already know that the sections within__LINKEDIT
segment cannot have "holes" between them; i.e., the offset of one should be the offset + size of the preceding section. This complicated things for us since we specifically want to leave some gaps for easier management of the incremental linking process in the self-hosted. With that out of the way, I also think arm64 macOS requires the binary to be position-independent executables (or PIEs) which is a big setback wrt to the self-hosted linker since we store and rely on absolute addressing stored within__DATA,__got
section. We can work this out, however, since this is somewhat tangential to the code signature issue, I believe it is of interest to get a simple "exit syscall" binary working first. Such a binary will not contain any function call (or debugging info), thus not requiring any GOT indirection:And this is what I'm currently working on. You can track the progress in kubkon/zig.git#stage2-arm-macos.
The text was updated successfully, but these errors were encountered: