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: lock down future uses of linkname #67401

Closed
rsc opened this issue May 15, 2024 · 117 comments
Closed

cmd/link: lock down future uses of linkname #67401

rsc opened this issue May 15, 2024 · 117 comments
Assignees
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsFix The path to resolution is known, but the work has not been done. release-blocker
Milestone

Comments

@rsc
Copy link
Contributor

rsc commented May 15, 2024

Overuse of //go:linkname to reach into Go standard library internals (especially runtime internals) means that when we do change the standard library internals in ways that should not matter, we can end up breaking packages that are depended on by a large swath of the Go ecosystem. For example, https://go.dev/cl/583756 broke github.com/goccy/go-json because it turns out that package copied most of the runtime's internal type API. Now we can't change anything in that list, despite that being an ostensibly internal package, without breaking goccy/go-json. And goccy is used by many packages, including Kubernetes.

This situation is unsustainable. Internals are internal for a reason. We can't keep Go programs working when they create explicit dependencies on details that we have kept internal. But we also care a lot about compatibility: we don't want to break Go programs either. The obvious conclusion is that we have to stop Go programs from being able to create these dependencies on internal details in the first place.

This issue tracks work to prevent new //go:linkname-based dependencies and contain existing ones.

Right now, if package A has a symbol and package B wants to refer to it with //go:linkname, there are three patterns:

  • (Push) Package A uses a //go:linkname to rename one of its own symbols to B.foo, and then B declares func foo() without a body. In this form, A clearly intends for B to use foo, although the compiler cannot quite tell what's going on in B and warns about foo not having a body unless you create an empty dummy.s file.

  • (Pull) Package A defines foo without any annotation, and package B uses //go:linkname to access A.foo. In this form, A may not intend for B to use foo at all. That's a serious problem: when A renames foo and/or changes its type signature, B breaks, and A may never even have heard of B.

  • (Handshake) Package A defines foo with a //go:linkname and package B defines foo also with a //go:linkname, and the two agree on the name (either A.foo or B.foo). This is the ideal form, and it avoids the dummy.s workaround that is needed in the Push case.

The ideal goal state is a world where all //go:linkname usage must be in the Handshake form: both sides must agree to use linkname for a given symbol in order for it to succeed. This will mean that arbitrary packages cannot create new dependencies on runtime internals. At the same time, we realize that the current world is not this ideal world, and we don't want to break all existing uses.

Our plan is as follows.

  1. Introduce a new -checklinkname=1 flag to cmd/link that requires the Handshake form for symbols in the standard library. That flag is already landed in at tip, but it is not the default.

  2. Survey all existing open-source Go packages to find standard library symbols that are being //go:linkname'd (behind our backs!) using the Pull pattern. Add the necessary //go:linkname annotations to the standard library to keep those working, documenting why each exists. The explicit //go:linkname lines and documentation will help avoid accidental breakage in future refactoring. We have done a preliminary survey, but we haven't yet added all the necessary //go:linkname lines.

  3. Make -checklinkname=1 the default for Go 1.23. If this breaks anything, users can use -ldflags=-checklinkname=0 to get unbroken, and we hope they will also file reports letting us know what we missed.

  4. As we get reports of additional breakage we missed, add more //go:linkname annotations to the standard library.

At the completion of that plan, we won't be in the ideal world, but we will have accomplished two important things:

  • We won't have broken anything.

  • We will have stopped new damage from accumulating: there will be no more new references to runtime internals introduced. In particular, new internals we added during the Go 1.23 cycle, like coro and weak pointers, cannot be linknamed, now or ever. And anything that wasn't linknamed yet won't grow new linknames in the future.

Note that anyone who wants to experiment can always build with -ldflags=-checklinkname=0 and linkname whatever they like. That's fine. We like experimenting too. But the fact that the code won't build without special flags should help prevent code that digs into internal details from becoming a core dependency in the Go ecosystem that we end up having to maintain forever.

Note also that for now, //go:linkname can still be used in Pull mode to get at internals of non-standard library packages. We'd like to change that eventually too, insisting on Handshakes everywhere. For now, we are starting with the standard library. If all goes well, we'll circle back and try to devise a plan for the rest of the ecosystem.

@rsc rsc added this to the Go1.23 milestone May 15, 2024
@gopherbot gopherbot added the compiler/runtime Issues related to the Go compiler and/or runtime. label May 15, 2024
@gopherbot
Copy link

Change https://go.dev/cl/585820 mentions this issue: runtime/coverage: remove uses of //go:linkname

@Jorropo
Copy link
Member

Jorropo commented May 15, 2024

I think github.com/goccy/go-json pulled internal runtime APIs in a reckless manner and there are safe ways to pull internal packages from the std.
For example quic-go did an equally dangerous thing with crypto/tls by accessing private fields using unsafe.Pointer and maintaining forks of crypto/tls:

However key differences:

As a downstream of quic-go I clearly understood the situation, the worst was the need to wait a couple of days for quic-go to release a new release compatible with the new go release before updating my go toolchain and the inability to run on tip or RCs.


In the case of github.com/goccy/go-json the situation could have been even better than quic-go's as it claims to be compatible with encoder/json:

Fast JSON encoder/decoder compatible with encoding/json for Go

instead of creating a compile time error they could have stubbed their API by forwarding calls to encoding/json when the release of go were to be unknown. (I don't know the details, maybe due to some edge cases or behaviors only go-json implements this wouldn't have been possible)


What I actually propose:

Continue to allow the Pull kind of linkname from std packages when the file is locked with build tags:

//go:build go1.23.4 && !go1.23.5 && !go1.24

To know if a file is properly locked down, the toolchain can evaluate the build tags with the next future release and it MUST fail to be allowed. That means if a file pass the current version but not the next one, then Pull linknames from the std would be allowed.

There is a downside to this approach which is that if there is no change required between two releases you still need a different file per version with the same implementation, to satisfy this constraint. Maybe parsing the build tags would be better.

I am not sure if goX.Y or goX.Y.Z should be used. goX.Y might be more dangerous in case a fix require breaking some internal API, quic-go used that and it was fine and created this couple of days period where you can't use latest go only every 6 months (note that goX.Y.Z build tags didn't existed back then).

@rsc
Copy link
Contributor Author

rsc commented May 15, 2024

I think ... there are safe ways to pull internal packages from the std.

I completely disagree. Quic-go's use of linkname caused all manner of problems for us release after release too, because anyone using quic-go couldn't update to a new Go version until quic-go did.

@gopherbot
Copy link

Change https://go.dev/cl/585916 mentions this issue: internal/coverage/cfile: remove //go:linkname into testing

@gopherbot
Copy link

Change https://go.dev/cl/585915 mentions this issue: internal/coverage/cfile: remove more //go:linkname usage

@ericlagergren

This comment has been minimized.

@randall77
Copy link
Contributor

We can't fix what we don't know about.

  1. As we get reports of additional breakage we missed, add more //go:linkname annotations to the standard library.

We're open to any reports from closed-source packages. It would be particularly useful to hear about these when the release candidate comes out so they can make the .0 release.

@ericlagergren

This comment has been minimized.

@randall77

This comment was marked as outdated.

@ericlagergren

This comment was marked as outdated.

@ruyi789
Copy link

ruyi789 commented May 16, 2024

Misuse is undesirable, disabling is even worse, you want to change you maintain that package (go-json), that doesn't solve it?

@bjorndm
Copy link

bjorndm commented May 16, 2024

I would like to say that //go:linkname is very useful for certain types of low level programming such as Ebitengine , etc. While I agree the situation with goccy/go-json is bad, we should look at how it is being used in detail and think of alternatives for the legitimate uses.

@DmitriyMV
Copy link
Contributor

@bjorndm should't -ldflags=-checklinkname=0 which disables this check be enough for low level programming? And if you can always raise a proposal if something is missing and truly needed without ld flags.

As we get reports of additional breakage we missed, add more //go:linkname annotations to the standard library.

@cherrymui
Copy link
Member

In C, static symbols are not accessible outside of the compilation unit, full stop. There is no way to pull a static symbol from a C library. A number of other languages have similar strict visibility rules. They are very successful languages and are widely used. This suggests that a lot programs can be written and things can go very well without a mechanism to break into a library's internal details.

I don't think Go is fundamentally different. Ideally we could also have strict visibility rules. I would think Go unexported symbols are meant to be similar to C static symbols. In fact, that is what gccgo does. Unfortunately for the gc toolchain it is not the case today. But we can get closer to it. And as we care a lot about compatibility, we'll keep the existing code continue to build in Go 1.23 (Step 2 in the plan). And we have a linker flag to disable the restriction (e.g. for experiments; as far as I know, the C linker doesn't seem to have such an option).

Also, I think the authors of the code should have a way to decide which symbols are visible externally and which are not.

@TotallyGamerJet
Copy link

TotallyGamerJet commented May 16, 2024

Purego which is a dependency of Oto, Beep, and Ebitengine as well as others doesn't just pull symbols it also pushes since it reimplements runtime/cgo package when CGO_ENABLED=0 entirely in Go. Is there any way the symbols defined in the runtime that hook into that package also get comments to avoid breaking us?

Another potential solution for us is to prebuild runtime/cgo into a cgo_GOOS_GOARCH_GOVERSION.syso that ships with purego. That would save us from having to keep up with any changes that package has and allows the Go team the freedom to change it as they like. Is this actually possible? I tried with setting different -buildmode but none of them would link.

Of course, if the Go team wanted to port runtime/cgo to Go that would be optimal.

@cherrymui
Copy link
Member

Is there any way the symbols defined in the runtime that hook into that package also get comments to avoid breaking us?

Push linknames are still allowed. If they are currently pushed from runtime/cgo, I believe you can still push them from Purego. Does Purego push symbols more than runtime/cgo?

Of course, if the Go team wanted to port runtime/cgo to Go that would be optimal.

This might be a possible option, but I think we need to understand the rationales better. The runtime/cgo package is intended to work with cgo, that is, interacting with C code. I'm not sure I understand the use case of runtime/cgo with CGO_ENABLED=0. This is probably better to be a separate discussion. Thanks.

@TotallyGamerJet
Copy link

TotallyGamerJet commented May 16, 2024

Push linknames are still allowed. If they are currently pushed from runtime/cgo, I believe you can still push them from Purego. Does Purego push symbols more than runtime/cgo?

No, Purego pushes the same symbols that runtime/cgo does which means the "Handshake" is already satisfied for those. Step 2 of the suggested plan only mentions surveying for Pull linknames and marking them with comments to avoid future breakage. I'm wondering if there is any plans for Pushes as changes to those would break Purego?

This might be a possible option, but I think we need to understand the rationales better. The runtime/cgo package is intended to work with cgo, that is, interacting with C code. I'm not sure I understand the use case of runtime/cgo with CGO_ENABLED=0. This is probably better to be a separate discussion. Thanks.

Indeed, runtime/cgo is required to allow Go code and C code to play well with each other. Purego provides an entirely Go version of it so that you can call C code using purego.Dlopen and purego.Dlsym without the need of a C compiler so cross-compiling is again possible. We can discuss this further elsewhere.

@cherrymui
Copy link
Member

cherrymui commented May 16, 2024

Thanks.

I'm wondering if there is any plans for Pushes as changes to those would break Purego?

I don't think there is any plan to break the use case of Purego's pushes. If we do anything to restrict push-only ones, they will be equally applied to the ones runtime/cgo pushing to runtime. So we'll need to fix those first (I think many of them are already in handshake form, but it is possible we missed some). And that should make Purego work as well.

@dmitshur dmitshur added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label May 16, 2024
@gopherbot
Copy link

Change https://go.dev/cl/586259 mentions this issue: runtime: move exit hooks into internal/runtime/exithook

@gopherbot
Copy link

Change https://go.dev/cl/586137 mentions this issue: all: add push linknames to allow legacy pull linknames

@iDigitalFlame
Copy link

@bjorndm

While I agree that there is a need for low level access to the runtime, using undefined behavior to do this is a recipe for disaster. The Go developers are not bound to keep this working.

And I understand that, but if you recognize the need then why be ok with this change?

@iDigitalFlame
Copy link

Just to be clear, I think the A.foo B.foo situation with external packages makes sense. But anything in the stdlib should be allowed, as there are many useful functions that are sometimes needed but gated away by export-ability.

@DmitriyMV
Copy link
Contributor

DmitriyMV commented May 27, 2024

Never said this should be done on modules. It's on the module developer to do this, so a go vet from the module developer's standpoint makes sense.

If you are the only one who are using the code -ldflags=-checklinkname=0 should be a trivial change.

Sure, but that's a problem, I make modules that relay on it without breaking, so why should I have to deal with this because some other developer's code breaks?

Because someone decides to use your code as a dependency and someone else decides to use new code as their dependency and so on until we have big project relying on go:linkname usages somewhere down the line.

However removing the way I can get the access is not something many of us agreed to (looking at the comments).

Once again -ldflags=-checklinkname=0. Nothing changes from your perspective, Go team simply wants your confirmation about your intentions. And confirmation from people who are using your code.

I'm pretty sure if you needed a feature that wouldn't pass a proposal, you'd have not qualms using go:linkname if you have to.

Thats why I'm fine with -ldflags=-checklinkname=0.

@iDigitalFlame
Copy link

@DmitriyMV

If you are the only one who are using the code -ldflags=-checklinkname=0 should be a trivial change.

For vet sure, but that's for build. I have no issue changing my automated builds, scripts and workflows and adding build notes. However it still seems wildly ridiculous cause we are worried about "things breaking", when if they were broken, they already would anyway? If a user is using my package, they'll hopefully have to know and that's just how I would handle it, not the hundreds of other packages that may break.

Because someone decides to use your code as a dependency and someone else decides to use new code as their dependency and so on until we have big project relying on go:linkname usages somewhere down the line.

The same thing would happen if something else breaks. They downgrade or submit an issue. It's literally the same scenario that happens today without go:linkname

Once again -ldflags=-checklinkname=0.

See above

Thats why I'm fine with -ldflags=-checklinkname=0.

See above

I understand your reasoning and argument here, seriously I do. But this change honestly makes more mess than it fixes (and will break more than fix!). Can we do better, sure! But doesn't seem the best route.

IMO: To fix the spirit of this issue, disabling external linking using go:linkname makes total sense but leave the majority of uses that point internally alone.

@ianlancetaylor
Copy link
Contributor

ianlancetaylor commented May 27, 2024

This has already been said, but the problem is not that people write packages that use go:linkname. People can continue to write those packages and use the new linker command line option.

The problem is that people write packages that use go:linkname, and then publish those packages, and then other people start to use those packages, and they become popular. Now we have popular packages being used, directly or indirectly, by literally thousands of other packages, all relying on internal details of the Go standard library. It doesn't help for the original author to say "but I will fix my package if the Go standard library ever changes." Yes, the original author will fix their package. But they won't go and update the thousands of other packages that indirectly depend on their package. They can't. And we can't either.

When these cases happen, the Go team has two bad choices. 1) retain internal details indefinitely, slowing down or even stopping development of the Go standard library. 2) break thousands of Go packages that currently work just fine.

Preventing new uses of go:linkname is an attempt to get out of this trap in the future.

If you want to argue against this change, please argue against what I am writing here. If you don't publish your package, use the command line argument. If you do publish your package, either don't use go:linkname or explain how the Go team can avoid the trap that I describe.

Yes, you can make your package run faster in some cases by using go:linkname. But if you publish your package, you are speeding up your use case at the cost of slowing down or stopping Go standard library development for everyone. Reaching into internals is not free.

Thanks.

@iDigitalFlame
Copy link

@ianlancetaylor

I apoligize if I was off topic here.

It doesn't help for the original author to say "but I will fix my package if the Go standard library ever changes." Yes, the original author will fix their package. But they won't go and updates the thousands of other packages that indirectly depend on their package. They can't. And we can't either.

Fair point.

When these cases happen, the Go team has two bad choices. 1) retain internal details indefinitely, slowing down or even stopping development of the Go standard library. 2) break thousands of Go packages that currently work just fine.

I'd argue no one here wants 1 and 2 would happen even if the change wasn't made and a breaking internals change was made. (IMO: When I do use go:linkname I try to target "stable" functions that haven't changed over N+ versions, but that's not everyone)

If you want to argue against this change, please argue against what I am writing here. If you don't publish your package, use the command line argument. If you do publish your package, either don't use go:linkname or explain how the Go team can avoid the trap that I describe.

Alright. Well, Its tough, but I agree that it makes life harder for the Go team and on the other side I agree that removing this mechanic may stifle innovation or features in some packages.

Since we already change the behavior of Go due to the go.mod go 1.xx version string, maybe when compiling, the linker will look for any usage of go:linkname and if found would force the user to compile using the target go version in go.mod (IE: only compile with the version they built it on), otherwise it'd compile fine. So that way, you'd get the benefit of being able to change the internals freely and devs would be able to prevent breakage by having a "stable" version to target. Yes this can cause issues, such as security vulns and fixes, but this is a thing that happens today in langs like Java. I've also done this in Go to target specific version's feature sets for this exact reason.

I'm not 100% sure how hard it would be to implement the linker code, but this would be an idea to make everyone happy.

@ianlancetaylor
Copy link
Contributor

Thanks for the suggestion. As you say, security fixes are a problem with that kind of approach. We don't want people who indirectly depend on a package using go:linkname to be trapped into an old Go release. I suppose that is a way of forcing people to update their internal packages, but it is a heavy cost.

@DmitriyMV
Copy link
Contributor

Yes this can cause issues, such as security vulns and fixes, but this is a thing that happens today in langs like Java.

This is exactly why so many companies are still on Java 1.7 (Spring Framework is one of biggest culprits here but there are others). If this isn't explicit for the end user from the start, most of the corporate users will simply go "fine, whatever, we will just stick to the old Go version" after their project no longer compiles with latest Go. The price of upgrading will be simply too high. And this stucks.

@iDigitalFlame
Copy link

@DmitriyMV I don't see another method that would easily resolve this for both "sides".

This is exactly why so many companies are still on Java 1.7 (Spring Framework is one of biggest culprits here but there are others). If this isn't explicit for the end user from the start, most of the corporate users will simply go "fine, whatever, we will just stick to the old Go version"

As much as I understand your frustration with this, this is not always the case. Outliers? sure. If there isn't an available upgrade then maybe they'll have to stay at the old version or implement the import manually, not much you can do.

For example, I have a project that's stuck on go1.10 as it has to support WinXp, sometimes you have to make it work, so it's already happening anyway.

@wwqgtxx
Copy link

wwqgtxx commented May 28, 2024

@ianlancetaylor

I noticed that your focus on go:linkname may be limited to third-party libraries using it to improve performance, while ignoring another main use of this writing method: to implement functions that the standard library cannot provide for the time being but must rely on internal ones.

We all know that for projects like the Golang standard library, adding any public API requires long-term consideration, and it may take several years to be implemented after being marked as Accept. A large number of mechanisms in golang are only implemented internally in the runtime (such as netpoll). During this period, if there is no similar mechanism like go:linkname, users can only choose:

  1. Do not implement the feature and wait hard for the standard library to be updated (unfortunately, many functional requirements may never be implemented)
  2. Use a more hacky way to implement this function (for example, using more dangerous system calls and assembly code to bypass restrictions)
  3. Give up Golang and use other languages to implement it (this is the worst option, but it may also cause some new projects that need this feature to abandon Golang from the beginning)

I know you might say that simply passing -ldflags=-checklinkname=0 would make none of the above happen. So the question is, should these libraries be published?

The existence of third-party libraries is an organic supplement to the standard library. We cannot expect the standard library to realize all the functions that users need. Locking the standard library, or to be precise, locking the runtime, can indeed reduce the cost of maintaining compatibility, but it will also greatly reduce the discoverable functionality of a language. The progress of a language is jointly promoted by the official and third-party library developers. I don't think it is conducive to the future development of the language to rudely lock the third-party libraries out.

It is impossible to pursue absolute compatibility. For example, the new version of Golang will give up support for outdated operating systems (such as Windows7/Server2012), will give up outdated and insecure TLS cipher suites, and modify some original behaviors (for example, rlimit mentioned earlier). These seemingly minor changes make upgrading the Golang version without any testing just a good fantasy. So I don't think upgrading the version of third-party dependent libraries while upgrading the Golang version is a question of They can't. And we can't either. Under the current go module mechanism, upgrading a dependency package is not a costly matter that requires digging into each sub-dependency layer by layer. And not only the standard library has security issues, but third-party libraries also exist. Isn't it something that should be done to ensure that various dependencies are updated at all times? I don't understand why it is a can't case.


On the other hand, I still think that setting the default value of checklinkname to 1 is not a good choice. There is still a lot of code that relies on the linkname mechanism and there is no public API alternative available. Setting it to 1 and freezing whitelist-related internal implementations in the standard library source code will not help reduce maintenance costs and will become a burden for future improvements. In addition, this does not prevent more third-party libraries from continuing to rely on internal implementations in these whitelists. Perhaps the above behavior can be prevented through stricter one-to-one whitelisting restrictions, but this will also create new unfair problems: why do popular libraries get privileges, while other libraries are mistreated. Roughly prohibiting linkname from new internal implementations will also prevent many new features from being implemented by third parties.

I have a premature proposal here. We can require those who use go:linkname without Handshake MUST provide an "alternative implementation", so that libraries pursuing high performance can fallback to compatibility implementations that only need to expose APIs (the performance may be reduced, but it is still available). For libraries that provide additional functions, compilation errors will not be directly generated when the linkname symbol cannot be found (it can return errors.ErrUnsupported to give the caller more choices). Of course, this is just a draft, and specific implementation details still need to be discussed.

@korniltsev
Copy link
Contributor

A guilty pyroscope-go/godeltaprof is included in https://swtch.com/tmp/linkname100.html and relies on linking to the following functions

// runtime_FrameStartLine is defined in runtime/symtab.go.
//
//go:noescape
//go:linkname runtime_FrameStartLine runtime/pprof.runtime_FrameStartLine
func runtime_FrameStartLine(f *runtime.Frame) int

// runtime_FrameSymbolName is defined in runtime/symtab.go.
//
//go:noescape
//go:linkname runtime_FrameSymbolName runtime/pprof.runtime_FrameSymbolName
func runtime_FrameSymbolName(f *runtime.Frame) string

//go:linkname runtime_expandFinalInlineFrame runtime/pprof.runtime_expandFinalInlineFrame
func runtime_expandFinalInlineFrame(stk []uintptr) []uintptr

//go:linkname runtime_cyclesPerSecond runtime.pprof_cyclesPerSecond
func runtime_cyclesPerSecond() int64

And the last one was renamed in main branch already and differs from 1.22 (cc @felixge )

1.22: runtime/pprof.runtime_cyclesPerSecond
main: runtime.pprof_cyclesPerSecond

A question to go maintainers: can I submit a PR to "document" these functions, in a similar way as in https://go-review.googlesource.com/c/go/+/587576

@ianlancetaylor
Copy link
Contributor

@wwqgtxx Thanks for the note. These kinds of decisions always involve weighing costs and benefits. I think we view the costs and benefits differently.

You mention netpoll, so I'll note that I don't yet find that example convincing. The runtime netpoll code is designed to work closely with the os and net packages. We've changed it several times to make the os and net packages more flexible and more efficient. If linkname were used by widely used packages, future development would be seriously restricted.

On the other hand, anybody can call the Ppoll or Kevent functions in the golang.org/x/sys/unix package, and of course Windows functions are easy to call using syscall.LoadDLL and syscall.(*DLL).FindProc.

Alternatively, it's possible to use os.NewFile or net.FileConn to efficiently take an existing descriptor and use it with the runtime poller.

So I think we already have all the building blocks required for a third-party package to efficiently poll descriptors.

@database64128
Copy link
Contributor

Alternatively, it's possible to use os.NewFile or net.FileConn to efficiently take an existing descriptor and use it with the runtime poller.

So I think we already have all the building blocks required for a third-party package to efficiently poll descriptors.

@ianlancetaylor Please read my earlier comment. The building blocks are not there for Windows.

@korniltsev
Copy link
Contributor

Is it possible add a test, checking the documented/allowed link names are not changed?

@gopherbot
Copy link

Change https://go.dev/cl/588695 mentions this issue: runtime/pprof: document legacy //go:linkname

@thepudds
Copy link
Contributor

Hi @korniltsev

A guilty pyroscope-go/godeltaprof is included in https://swtch.com/tmp/linkname100.html and relies on linking to the following functions

[...]

A question to go maintainers: can I submit a PR to "document" these functions, in a similar way as in https://go-review.googlesource.com/c/go/+/587576

It looks like the 4 functions you asked about might already be covered in CL https://go.dev/cl/587598, with some possible follow-up in https://go.dev/cl/587756. Note that those are still active (not yet submitted), including it looks like there might be an open question related to funcdata in https://go.dev/cl/587598 (which seems unrelated to your pyroscope-go questions).

Regarding the pprof.runtime_cyclesPerSecond renaming that happened for other reasons during this development cycles, it looks like there was some discussion of reverting that name change here in order to be friendlier to anyone using it via linkname, though I'm not immediately sure of latest status of that.

This is just based on a quick look, so sorry if any of this is off base, but that might give you at least some starting points to dig further if needed.

@thepudds
Copy link
Contributor

Hi @database64128

@ianlancetaylor Please read my earlier comment. The building blocks are not there for Windows.

As far as I understand, it seems Ian was suggesting two options for Windows:

  • Option 1: via syscall.LoadDLL and syscall.(*DLL).FindProc
  • Option 2: via os.NewFile or net.FileConn.

Are you saying both options do not work for Windows for your use case?

Regardless of the linkname discussion here, it probably is worthwhile to raise an issue with the specifics in the hopes of identifying a future improvement (or perhaps workaround) that does not rely on a linkname.

@seebs
Copy link
Contributor

seebs commented May 29, 2024

I'm pretty strongly in favor of this. I love to write crazy stuff that pokes around in stdlib, but I know that it is definitionally unportable and can and will fail in the future, and I shouldn't be relying on it. This problem isn't unique to Go, it's been a thing for C programmers for about 50 years now. And I'm pretty solidly on the side of "actually, you should not do this, and if you do, you should not complain it it fails, and you should especially not do it in code you're publishing with intent that other people can rely on it", because the alternative is to wreck the implementation's ability to improve over time.

If it's not exported, it's not part of the interface, and if you rely on it anyway, that sounds like a you problem. I know it's frustrating, I know you can do really cool things against a specific implementation sometimes, but... if you're making this a problem for the implementors, they're gonna protect themselves, and they're right to do so.

@iDigitalFlame
Copy link

@seebs

If it's not exported, it's not part of the interface, and if you rely on it anyway, that sounds like a you problem. I know it's frustrating, I know you can do really cool things against a specific implementation sometimes, but... if you're making this a problem for the implementors, they're gonna protect themselves, and they're right to do so.

They may have a right to, and that's fine, but removing something that allows users to be flexible with the language is a terrible idea.

unportable and can and will fail in the future

Not always true, if you maintain the libs that use it.

If it's not exported, it's not part of the interface, and if you rely on it anyway, that sounds like a you problem.

That's a bad thought to have when you have to do things that are not directly exposed to you. There are many times I've had to write code well beyond what was in the standard library. But I've also never had go:linkname fail compiles on me.

How would you suggest fixes to those?

@Merovius
Copy link
Contributor

@iDigitalFlame My impression is, that the points you are raised are already well known and taken into account by @rsc and @ianlancetaylor. That is, I don't have the impression that there is a mismatch of available information, it's "just" that the tradeoffs are made differently.

You are fundamentally correct, that people sometimes feel the need to reach into unexported APIs. You are fundamentally correct, that they won't be able to after this change. And you are fundamentally correct that those people will then either have to solve their problem differently, choose to not solve the problem, or use a different language. As harsh as that may sound, I think that is fine. If the goals of Go as a language are incompatible with the goals of some developers, it is fine for those developers to use a different language.

Not always true, if you maintain the libs that use it.

I think it's become pretty clear recently, that it is unsustainable for the ecosystem and for the individuals involved in it, to create the expectation that open source software is professionally and promptly maintained. Open source dependency graphs are large and it's socially untenable, to "just do good engineering" on all of it.

Again, there is a tradeoff here. And part of the goals of Go as a language are to not depend on things being necessarily well-maintained. Go modules where, in part, explicitly designed to allow for situations where you just can't rely on people to apply fixes in a timely manner, while still keeping the ecosystem as a whole largely running.


Personally, as someone who mostly tries to just write boring Go code, I would very much love if what the Gophers slack calls #darkarts was not possible by default. I want as much Go code as possible to be boring. So that, if I only do boring things, I don't have to worry too much about stuff suddenly becoming interesting at the wrong time. I understand that sometimes things need to be interesting, but requiring a flag seems fine for that. I'd like the ecosystem as a whole to be boring by default.

gopherbot pushed a commit that referenced this issue May 29, 2024
For #67401.

Change-Id: I015408a3f437c1733d97160ef2fb5da6d4efcc5c
Reviewed-on: https://go-review.googlesource.com/c/go/+/587598
Reviewed-by: Cherry Mui <cherryyz@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Russ Cox <rsc@golang.org>
gopherbot pushed a commit that referenced this issue May 29, 2024
A bad merge syncing before the submit of CL 587220 dropped these.
(I forgot to write the file out.)

For #67401.

Change-Id: I6f2ba69f388907f3d24eeef55c80cbb2cf51f580
Reviewed-on: https://go-review.googlesource.com/c/go/+/587755
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Marten Seemann <martenseemann@gmail.com>
asubiotto added a commit to asubiotto/go that referenced this issue May 31, 2024
golang#67401 documents the removal of the ability
to perform "Pull" linknames of standard library internals starting in Go1.23.

This commit adds go:linkname to aeskeysched since
github.com/parquet-go/parquet-go relies on this and would not build on amd
systems with this new version of Go (see:
https://github.com/parquet-go/parquet-go/blob/d0d9efaa7ab89610ae7228a4105975176de2d0b8/hashprobe/aeshash/aeshash_amd64.s#L10)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsFix The path to resolution is known, but the work has not been done. release-blocker
Projects
Development

No branches or pull requests