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

cmd/link: use libsystem_kernel.dylib or libSystem.dylib for syscalls on macOS #17490

Closed
copumpkin opened this issue Oct 17, 2016 · 37 comments
Closed
Labels
FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done. OS-Darwin
Milestone

Comments

@copumpkin
Copy link

As I understand it, Go currently has its own syscall wrappers for Darwin. This is explicitly against what Apple recommends, precisely because they're not willing to commit to a particular syscall ABI. This leads to issues like #16570, and although we've been lucky in that things have generally been backward-compatible so far, there's no guarantee that it'll continue to happen. It doesn't seem inconceivable to me that we'd at some point end up having to specify "build for macOS 10.13+" vs. "build for 10.12 and below", for example.

Linking against libsystem_kernel.dylib (or the broader libSystem.dylib) would put Go back in line with Apple's recommendations for the platform, and the library is guaranteed to exist on all macOS boxes.

Apologies if this has been suggested before and people have already gone over good reasons not to do it, but I'm currently struggling to figure out what appears to be another Sierra incompatibility related to #16570 above (still haven't figured it out enough to post a reproducible bug) and am wishing we didn't have to deal with these issues.

@ianlancetaylor ianlancetaylor changed the title Consider linking against libsystem_kernel.dylib or libSystem.dylib on macOS? cmd/link: link against libsystem_kernel.dylib or libSystem.dylib on macOS Oct 18, 2016
@ianlancetaylor
Copy link
Member

I agree that we have to do this. I'm not sure who is actually going to do it, though.

@quentinmit
Copy link
Contributor

Note that the trouble with #16570 comes from fetching the current time, and that is something that is so performance critical that we might want to continue to use our own implementation even if we use libSystem for other syscall.

@crawshaw
Copy link
Member

We got into a discussion about how to do this over on #17200. I believe this can be done without forcing external linking. But I also don't know who is going to do it, my plate is full for the foreseeable future.

@quentinmit quentinmit added the NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. label Oct 20, 2016
@quentinmit quentinmit added this to the Go1.9Maybe milestone Oct 20, 2016
@rsc
Copy link
Contributor

rsc commented Jan 4, 2017

Agree we should do this at some point. It's not terribly high priority though.

@rsc rsc added NeedsFix The path to resolution is known, but the work has not been done. and removed NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. labels Jan 4, 2017
@rsc rsc changed the title cmd/link: link against libsystem_kernel.dylib or libSystem.dylib on macOS cmd/link: use libsystem_kernel.dylib or libSystem.dylib for syscalls on macOS Mar 8, 2017
@rsc
Copy link
Contributor

rsc commented Mar 8, 2017

This is about using the system calls. We already link against libSystem.dylib when cgo is in use.

@hirochachacha
Copy link
Contributor

Hi, I made a CL for x86. https://go-review.googlesource.com/c/50290/
Could someone review it (after development cycle open) ?
If the overall design is OK, I'd like do something in go 1.10. Thank you.

@gopherbot
Copy link
Contributor

CL https://golang.org/cl/50290 mentions this issue.

@gopherbot
Copy link
Contributor

Change https://golang.org/cl/54871 mentions this issue: cmd/internal/obj/x86: don't apply workaround for solaris to darwin

gopherbot pushed a commit that referenced this issue Aug 17, 2017
Currently, we have a workaround for solaris that enforce aboslute
addressing for external symbols. However, We don't want to use the
workaround for darwin.
This CL also refactors code a little bit, because the original function
name is not appropriate now.

Updates #17490

Change-Id: Id21f9cdf33dca6a40647226be49010c2c324ee24
Reviewed-on: https://go-review.googlesource.com/54871
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
@rsc
Copy link
Contributor

rsc commented Nov 22, 2017

Despite the release-blocker label I don't see this happening for Go 1.10. Moving to Go 1.11. If we really want to do this we need to make sure work starts at the beginning of the cycle.

@rsc rsc modified the milestones: Go1.10, Go1.11 Nov 22, 2017
@ianlancetaylor
Copy link
Member

Nick Kledzik suggested in e-mail:

There is a middle ground that some JIT creators use, which is to wrap their JITed code in a “boilerplate” mach-o.

For the Go case, since you already generate the wrapper mach-o structure for executables, you expand that a bit, and standard content for linking to libSystem.dylib. This would include the stubs (PLT) section and lazy pointer section (GOT), and the LINKEDIT stuff to bind them for all the POSIX calls Go supports. The Go compiler then calls the stub (PLT) instead of directly inlining the syscall.

The new mach-o content would be the same for all Go executables, so you don’t need a linker. You write a C program that calls all POSIX functions Go supports and compile/link that once, and copy the stubs/lazy pointer sections and LINKEDIT content for use as your boilerplate.

This won’t solve the thread local issue, but will make Go executables true dynamic executables and free Apple to evolve the private libSystem/kernel syscall interface.

gopherbot pushed a commit to golang/sys that referenced this issue Dec 20, 2018
Update golang/go#17490

Change-Id: I29feed5ddea976b39bd4c43bd1ff5942f47df083
Reviewed-on: https://go-review.googlesource.com/c/154661
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
gopherbot pushed a commit to golang/sys that referenced this issue Dec 20, 2018
Update golang/go#17490

Change-Id: I55ea10ce2eb5fb1c0518a57900e78e5f0a29b893
Reviewed-on: https://go-review.googlesource.com/c/154662
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
gopherbot pushed a commit to golang/sys that referenced this issue Dec 20, 2018
Update golang/go#17490

Change-Id: Iaec54b8ffda1a24d4c8b5671185d570fb8683155
Reviewed-on: https://go-review.googlesource.com/c/154663
Reviewed-by: Ian Lance Taylor <iant@golang.org>
@gopherbot
Copy link
Contributor

Change https://golang.org/cl/155737 mentions this issue: cmd: vendor x/sys/unix into the stdlib

@gopherbot
Copy link
Contributor

Change https://golang.org/cl/156346 mentions this issue: unix: make Fcntl* routines use libSystem on Darwin

gopherbot pushed a commit to golang/sys that referenced this issue Jan 6, 2019
Missed this one.

Update golang/go#17490.

Change-Id: I5ab2197f9981b712b37d3060f72209bebcadf291
Reviewed-on: https://go-review.googlesource.com/c/156346
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
@gopherbot
Copy link
Contributor

Change https://golang.org/cl/156365 mentions this issue: cmd: vendor x/sys/unix into the stdlib

gopherbot pushed a commit that referenced this issue Jan 7, 2019
upstream git hash: 1775db3

Update #17490

Change-Id: I95e3c57137756c5c7a9b7334075caef66f205231
Reviewed-on: https://go-review.googlesource.com/c/156365
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
aykevl added a commit to tinygo-org/tinygo that referenced this issue Mar 24, 2019
Go 1.12 switched to using libSystem.dylib for system calls, because
Apple recommends against doing direct system calls that Go 1.11 and
earlier did. For more information, see:
  golang/go#17490
  https://developer.apple.com/library/archive/qa/qa1118/_index.html

While the old syscall package was relatively easy to support in TinyGo
(just implement syscall.Syscall*), this got a whole lot harder with Go
1.12 as all syscalls now go through CGo magic to call the underlying
libSystem functions. Therefore, this commit overrides the stdlib syscall
package with a custom package that performs calls with libc (libSystem).
This may be useful not just for darwin but for other platforms as well
that do not place the stable ABI at the syscall boundary like Linux but
at the libc boundary.

Only a very minimal part of the syscall package has been implemented, to
get the tests to pass. More calls can easily be added in the future.
aykevl added a commit to tinygo-org/tinygo that referenced this issue Mar 24, 2019
Go 1.12 switched to using libSystem.dylib for system calls, because
Apple recommends against doing direct system calls that Go 1.11 and
earlier did. For more information, see:
  golang/go#17490
  https://developer.apple.com/library/archive/qa/qa1118/_index.html

While the old syscall package was relatively easy to support in TinyGo
(just implement syscall.Syscall*), this got a whole lot harder with Go
1.12 as all syscalls now go through CGo magic to call the underlying
libSystem functions. Therefore, this commit overrides the stdlib syscall
package with a custom package that performs calls with libc (libSystem).
This may be useful not just for darwin but for other platforms as well
that do not place the stable ABI at the syscall boundary like Linux but
at the libc boundary.

Only a very minimal part of the syscall package has been implemented, to
get the tests to pass. More calls can easily be added in the future.
aykevl added a commit to tinygo-org/tinygo that referenced this issue Apr 4, 2019
Go 1.12 switched to using libSystem.dylib for system calls, because
Apple recommends against doing direct system calls that Go 1.11 and
earlier did. For more information, see:
  golang/go#17490
  https://developer.apple.com/library/archive/qa/qa1118/_index.html

While the old syscall package was relatively easy to support in TinyGo
(just implement syscall.Syscall*), this got a whole lot harder with Go
1.12 as all syscalls now go through CGo magic to call the underlying
libSystem functions. Therefore, this commit overrides the stdlib syscall
package with a custom package that performs calls with libc (libSystem).
This may be useful not just for darwin but for other platforms as well
that do not place the stable ABI at the syscall boundary like Linux but
at the libc boundary.

Only a very minimal part of the syscall package has been implemented, to
get the tests to pass. More calls can easily be added in the future.
deadprogram pushed a commit to tinygo-org/tinygo that referenced this issue Apr 5, 2019
Go 1.12 switched to using libSystem.dylib for system calls, because
Apple recommends against doing direct system calls that Go 1.11 and
earlier did. For more information, see:
  golang/go#17490
  https://developer.apple.com/library/archive/qa/qa1118/_index.html

While the old syscall package was relatively easy to support in TinyGo
(just implement syscall.Syscall*), this got a whole lot harder with Go
1.12 as all syscalls now go through CGo magic to call the underlying
libSystem functions. Therefore, this commit overrides the stdlib syscall
package with a custom package that performs calls with libc (libSystem).
This may be useful not just for darwin but for other platforms as well
that do not place the stable ABI at the syscall boundary like Linux but
at the libc boundary.

Only a very minimal part of the syscall package has been implemented, to
get the tests to pass. More calls can easily be added in the future.
@gopherbot
Copy link
Contributor

Change https://golang.org/cl/197938 mentions this issue: runtime: fix darwin syscall performance regression

gopherbot pushed a commit that referenced this issue Oct 1, 2019
While understanding why syscall.Read is 2x slower on darwin/amd64, I found
out that, contrary to popular belief, the slowdown is not due to the migration
to use libSystem.dylib instead of direct SYSCALLs, i.e., CL 141639 (and #17490),
but due to a subtle change introduced in CL 141639.

Previously, syscall.Read used syscall.Syscall(SYS_READ), whose preamble called
runtime.entersyscall, but after CL 141639, syscall.Read changes to call
runtime.syscall_syscall instead, which in turn calls runtime.entersyscallblock
instead of runtime.entersyscall. And the entire 2x slow down can be attributed
to this change.

I think this is unnecessary as even though syscalls like Read might block, it
does not always block, so there is no need to handoff P proactively for each
Read. Additionally, we have been fine with not handing off P for each Read
prior to Go 1.12, so we probably don't need to change it. This changes restores
the pre-Go 1.12 behavior, where syscall preamble uses runtime.entersyscall,
and we rely on sysmon to take P back from g blocked in syscalls.

Change-Id: If76e97b5a7040cf1c10380a567c4f5baec3121ba
Reviewed-on: https://go-review.googlesource.com/c/go/+/197938
Run-TryBot: Minux Ma <minux@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
@gopherbot
Copy link
Contributor

Change https://golang.org/cl/200103 mentions this issue: [release-branch.go1.13] runtime: fix darwin syscall performance regression

gopherbot pushed a commit that referenced this issue Oct 9, 2019
…ssion

While understanding why syscall.Read is 2x slower on darwin/amd64, I found
out that, contrary to popular belief, the slowdown is not due to the migration
to use libSystem.dylib instead of direct SYSCALLs, i.e., CL 141639 (and #17490),
but due to a subtle change introduced in CL 141639.

Previously, syscall.Read used syscall.Syscall(SYS_READ), whose preamble called
runtime.entersyscall, but after CL 141639, syscall.Read changes to call
runtime.syscall_syscall instead, which in turn calls runtime.entersyscallblock
instead of runtime.entersyscall. And the entire 2x slow down can be attributed
to this change.

I think this is unnecessary as even though syscalls like Read might block, it
does not always block, so there is no need to handoff P proactively for each
Read. Additionally, we have been fine with not handing off P for each Read
prior to Go 1.12, so we probably don't need to change it. This changes restores
the pre-Go 1.12 behavior, where syscall preamble uses runtime.entersyscall,
and we rely on sysmon to take P back from g blocked in syscalls.

Updates #34712

Change-Id: If76e97b5a7040cf1c10380a567c4f5baec3121ba
Reviewed-on: https://go-review.googlesource.com/c/go/+/197938
Run-TryBot: Minux Ma <minux@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
(cherry picked from commit c1635ad)
Reviewed-on: https://go-review.googlesource.com/c/go/+/200103
Run-TryBot: Alexander Rakoczy <alex@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
@gopherbot
Copy link
Contributor

Change https://golang.org/cl/227037 mentions this issue: crypto/x509: use Security.framework without cgo for roots on macOS

gopherbot pushed a commit that referenced this issue May 7, 2020
+----------------------------------------------------------------------+
| Hello, if you are reading this and run macOS, please test this code: |
|                                                                      |
| $ GO111MODULE=on go get golang.org/dl/gotip@latest                   |
| $ gotip download                                              |
| $ GODEBUG=x509roots=1 gotip test crypto/x509 -v -run TestSystemRoots |
+----------------------------------------------------------------------+

We currently have two code paths to extract system roots on macOS: one
uses cgo to invoke a maze of Security.framework APIs; the other is a
horrible fallback that runs "/usr/bin/security verify-cert" on every
root that has custom policies to check if it's trusted for SSL.

The fallback is not only terrifying because it shells out to a binary,
but also because it lets in certificates that are not trusted roots but
are signed by trusted roots, and because it applies some filters (EKUs
and expiration) only to roots with custom policies, as the others are
not passed to verify-cert. The other code path, of course, requires cgo,
so can't be used when cross-compiling and involves a large ball of C.

It's all a mess, and it broke oh-so-many times (#14514, #16532, #19436,
 #20990, #21416, #24437, #24652, #25649, #26073, #27958, #28025, #28092,
 #29497, #30471, #30672, #30763, #30889, #32891, #38215, #38365, ...).

Since macOS does not have a stable syscall ABI, we already dynamically
link and invoke libSystem.dylib regardless of cgo availability (#17490).

How that works is that functions in package syscall (like syscall.Open)
take the address of assembly trampolines (like libc_open_trampoline)
that jump to symbols imported with cgo_import_dynamic (like libc_open),
and pass them along with arguments to syscall.syscall (which is
implemented as runtime.syscall_syscall). syscall_syscall informs the
scheduler and profiler, and then uses asmcgocall to switch to a system
stack and invoke runtime.syscall. The latter is an assembly trampoline
that unpacks the Go ABI arguments passed to syscall.syscall, finally
calls the remote function, and puts the return value on the Go stack.
(This last bit is the part that cgo compiles from a C wrapper.)

We can do something similar to link and invoke Security.framework!

The one difference is that runtime.syscall and friends check errors
based on the errno convention, which Security doesn't follow, so I added
runtime.syscallNoErr which just skips interpreting the return value.
We only need a variant with six arguments because the calling convention
is register-based, and extra arguments simply zero out some registers.

That's plumbed through as crypto/x509/internal/macOS.syscall. The rest
of that package is a set of wrappers for Security.framework and Core
Foundation functions, like syscall is for libSystem. In theory, as long
as macOS respects ABI backwards compatibility (a.k.a. as long as
binaries built for a previous OS version keep running) this should be
stable, as the final result is not different from what a C compiler
would make. (One exception might be dictionary key strings, which we
make our own copy of instead of using the dynamic symbol. If they change
the value of those strings things might break. But why would they.)

Finally, I rewrote the crypto/x509 cgo logic in Go using those wrappers.
It works! I tried to make it match 1:1 the old logic, so that
root_darwin_amd64.go can be reviewed by comparing it to
root_cgo_darwin_amd64.go. The only difference is that we do proper error
handling now, and assume that if there is no error the return values are
there, while before we'd just check for nil pointers and move on.

I kept the cgo logic to help with review and testing, but we should
delete it once we are confident the new code works.

The nocgo logic is gone and we shall never speak of it again.

Fixes #32604
Fixes #19561
Fixes #38365
Awakens Cthulhu

Change-Id: Id850962bad667f71e3af594bdfebbbb1edfbcbb4
Reviewed-on: https://go-review.googlesource.com/c/go/+/227037
Reviewed-by: Katie Hockman <katie@golang.org>
@golang golang locked and limited conversation to collaborators Apr 14, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done. OS-Darwin
Projects
None yet
Development

No branches or pull requests

10 participants