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: invalid TLS access model used with -buildmode c-shared #53214
Comments
Thanks for the issue. Could you share how you invoke the build commands? Do you run |
Hi! Yes to both of those things (we use the Buck build system which breaks down the process into individual invocations of Go tools like I'm currently working on a minimalized repro case that only relies on pure Golang toolchain, but other things will keep me busy till about mid-July, so cannot provide more info at the moment. |
What flags do you pass to Note that not all compiler/assembler flags are compatible with the build mode. |
The original build is quite extensive and complicated, but here's the gist of what's being run to produce some of the key
The
I believe this part is a recreation of Golang's own distribution build within Buck. Is there perhaps some key arg to |
I think you need to pass the Note that this also applies to the Go standard library packages. If you use pre-built packages for the standard library, make sure they are built with those flags as well. |
Thanks for the suggestion! I'll try that and report back. It might need to wait till August, though, due to other things I need to work on first. |
@cherrymui I created a minimal repro script. go env, go, clang, ld.lld versions
Script
Output
|
@n-canter , as commented in #53214 (comment) , you'd need to use the |
Hey, @cherrymui, thanks for looking into this. We're using the lower level go tool compile/link as we've got to stay integrated with the cross-language build system deployed internally here at Meta. We have our reservations about rebuilding the world with |
In short I'd still suggest to use the go command if possible. Directly using the compiler and linker is not a supported mode and may change any time, especially with non-trivial flags such as c-shared build mode. If you really want to do that, maybe you could take a look at the compiler/assembler/linker invocations from the go command by doing |
Timed out in state WaitingForInfo. Closing. (I am just a bot, though. Please speak up if this is a mistake or you have the requested information.) |
My last 2 cents after closure of this task in the interest of future follow-up discussions that may happen. First of all, thanks @cherrymui for pointing out the necessity to rebuild all the dependencies with I can see that the world needs rebuilding with However, I believe that this discussion brought to light another interesting fact, which to the best of my knowledge (and Googling) is not very well documented. In our testing and repro work, we have noticed that the Namely, it spawns an implicit build job to perform temporary compilation of Golang standard library modules with the Here's an easy way to observe this behavior:
As one can see, without any explicit declarations, module sources like I'd like to argue that this is a huge and surprising departure from established practice with compiler and build toolchains in other compiled languages. Typically, when the language's standard library needs special mode of compilation for a flavor of its binaries (be it target CPU architecture or shared vs static), it's done in advance and the compiled binaries get shipped along with "normal" ones. E.g. regular C libraries which usually ship both static archives and This makes many beneficial things easier, e.g. having the trusted compiled libraries authenticated and cryptographically signed; saving time on compilation etc. At the very least, this has the benefit of adhering to the principle of least astonishment for the user. And Golang even has a facility for shipping different flavors of compiled standard libs, after all we have architecture-oriented dirs like It is obviously up to the Golang project to decide how the build toolchain works, but I'd like to suggest changing the behavior from on-demand recompilation of standard library modules to shipping them pre-compiled in shared mode (e.g. in directories named like |
@aadamowski the build of the packages are cached, so it shouldn't be rebuilt the next time you build it with the same flag. However, you used the |
I understand. However, performance is only a minor of all the concerns that I have around this design. It also applies only to local builds (on a specific machine/OS instance/filesystem), not any distributed build infrastructures (out of the box). My largest concern is the departure from the established practices around how build toolchains generally work and what features their individual tools provide (e.g. a compiler does not suddenly decide on its own to compile a piece of source it wasn't explicitly asked to compile). |
Sorry, I'm not sure I understand. You used the |
Oh, I see what you mean. Yes, I'd like to be able to drop the usage of |
IOW, the idea is to add another sublevel to module cache directory structure (https://go.dev/ref/mod#module-cache) to guarantee separation between compiled artifacts that cannot possibly work together (due to being cross-compiled for a different architecture, or mixing of |
It does already, as well as on other compile/linker flags. The use of |
I see. Thanks, I think we now have a better understanding of it now and how to adapt Buck's Golang support to handle Would you have some time to answer the last few questions? AFAIU the behavior you mentioned for keying on compile/linker flags only applies to the build cache, correct? E.g. in my case, after dropping Is there any similar mechanism that would apply to pre-compiled modules originating externally (installed on the system, not produced by the local build environment) and where the go tools search for them? Is there any classification mechanism that doesn't depend on all the possible inputs and build args and only the ones that could affect compiled binary compatibility (such as the target CPU architecture, or I am looking for the best way to preserve compiled modules dependencies and distribute/ship them in several supported flavors (basically what I described above - CPU arch, The goal is to save the build fleet time that would be spent on repeatedly recompiling popular modules from sources (most importantly the stdlib, but not limited to it). |
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
Yes, 1.18 is the latest release.
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
We have a CGo-based library that we build in c-shared mode to use as a dynamic shared object on Linux (x86_64).
The library is, unfortunately, an internal project and I cannot disclose the source code.
For the C compiler, we are using Clang.
The final linking of the shared object file is performed with the following invocation of the
go link
tool (some irrelevant flags removed):What did you expect to see?
All this setup has been working fine with Clang's previous version, 9.0.20190721 and the exact same (binary for binary) Golang toolchain version.
We would expect it to work the same after upgrading to a newer Clang version.
What did you see instead?
With a newer Clang version 12.0.20210610, we are observing
cmd/link
fail with the following error:The first suspicion would obviously be on Clang. However, we tracked it down to this change: https://reviews.llvm.org/D33100
As can be seen, Clang has introduced a stricter check on where this relocation is allowed due to correctness reasons.
Further reading of https://akkadia.org/drepper/tls.pdf indicates that this relocation type is intended for the TLS Local Exec model, and exec models are not suitable for usage in dynamic shared objects (only in final built executables).
We've analyzed all the intermediate/dependency
.a
archive files and the.o
object files they contain withreadelf -r
, and none of them contain theR_X86_64_TPOFF32
relocation anywhere.The temporary
go.o
file generated by thego link
command is the first that appears during the build process that contains it, indicating that it is this command inserting the relocation.We've tracked down the code in
cmd/link
that generates this relocation to this place:https://github.com/golang/go/blob/go1.18/src/cmd/link/internal/amd64/asm.go#L403
which further confirms that
cmd/link
is using an invalid TLS access model for the situation (-buildmode c-shared
was used).I do not yet have a good enough understanding of the Go linker and why is it choosing this access model, but its behavior seems to be clearly incorrect.
It's possible that, as the latest Clang releases get more prevalent, this issue will pop up in other places with other users who rely on
-buildmode c-shared
.This issue might also have some relation to issue #48596 and bears some similarity to the old (and fixed) issue #9652.
The text was updated successfully, but these errors were encountered: