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

Put absolute paths into -extldflags #4217

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

smertnik3sh
Copy link
Contributor

@smertnik3sh smertnik3sh commented Jan 6, 2025

Bug fix

The go build system stores information about build flags (and environment variables) in the _go_.o file (go package manifest). When you use cgo, data from "CGO_LDFLAGS" is added to this file as is and used during final binary linking. If you add absolute path to "CGO_LDFLAGS" environment, it will be save inside manifest too. However, when using a distributed build system such as BuildBarn, different build agents not share all paths (such as "bazel sandboxing" paths). If the _go_.o file inside go library contains non-existent paths, linking with this module fails.

This patch puts all flags with absolute paths into -extldflags. In this case its not stored in the go package manifest.

Problem can be reproduced only on specific cases (we use custom sysroot and distributed build system) so I don't know how to write good "common" test.

@smertnik3sh smertnik3sh marked this pull request as draft January 6, 2025 19:07
@smertnik3sh smertnik3sh marked this pull request as ready for review January 7, 2025 13:47
@@ -33,7 +33,7 @@ import (

var (
// cgoEnvVars is the list of all cgo environment variable
cgoEnvVars = []string{"CGO_CFLAGS", "CGO_CXXFLAGS", "CGO_CPPFLAGS", "CGO_LDFLAGS"}
cgoAbsEnvVars = []string{"CGO_CFLAGS", "CGO_CXXFLAGS", "CGO_CPPFLAGS"}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update the comment

@@ -594,3 +594,40 @@ func useResponseFile(path string, argLen int) bool {
}
return false
}

// The go build system collects the CGO_LDFLAGS from manifests (_go_.o) and uses them in the user module linking.
// In some cases, such as when using the distributed Bazel executor, these paths may not exist (different build agents uses different sandboxing paths).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// In some cases, such as when using the distributed Bazel executor, these paths may not exist (different build agents uses different sandboxing paths).
// In some cases, such as when using remote execution, these paths may not exist (different build agents uses different sandboxing paths).

Does this also come up with sandboxed builds? The absolute path prefixes should already be different in that case.

var absolute []string
var nonAbsolute []string
absNext := false
splited, err := splitQuoted(originVar)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
splited, err := splitQuoted(originVar)
split, err := splitQuoted(originVar)

and below

// The go build system collects the CGO_LDFLAGS from manifests (_go_.o) and uses them in the user module linking.
// In some cases, such as when using the distributed Bazel executor, these paths may not exist (different build agents uses different sandboxing paths).
// It is better to use flags with absolute paths in the -extldflags option (in this case, they will not be saved inside the manifest).
func splitAbsEnvVar(originVar string, absArs []string) (string, string, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
func splitAbsEnvVar(originVar string, absArs []string) (string, string, error) {
func splitAbsEnvVar(originVar string, absArgs []string) (string, string, error) {

and below.


// The go build system collects the CGO_LDFLAGS from manifests (_go_.o) and uses them in the user module linking.
// In some cases, such as when using the distributed Bazel executor, these paths may not exist (different build agents uses different sandboxing paths).
// It is better to use flags with absolute paths in the -extldflags option (in this case, they will not be saved inside the manifest).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we just put all flags into -extldflags instead? What's the advantage of splitting them?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 reasons:

  1. I usually prefer to fix only broken things)
  2. I think it's a "go-way" for providing ldflags to dependencies (some kind of "transitive" flags). I'm afraid that changing this behavior car broke builds in some strange places. But it's a theory.

If you think that it's better to use -extldflags for all, I can implement it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems as if we somewhat lose the benefits of 2. as soon as we move any flag over, so I would be in favor of moving all of them. This aligns better with how Bazel works: We can track link flags of all transitive deps via providers, so there is no need to track them in executables.

The only potential breakage I can think of is for Go generated static archives that are considered shippable artifacts. That seems so rare that I don't think we should worry about it.

@linzhp @jayconrod Do you have opinions on this?

os.Setenv("CGO_LDFLAGS", nonAbsoluteLdFlags)
if len(absoluteLdFlags) > 0 {
ldflags = append(ldflags, "-extldflags")
splitFlags := strings.Split(absoluteLdFlags, " ")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are splitting the flags we just joined. Instead, could we have the helper function return string slices?

@fmeum
Copy link
Member

fmeum commented Jan 11, 2025

Could you post examples of the paths that cause cache misses? I would like to understand why this hasn't come up before.

@jayconrod
Copy link
Contributor

Before reviewing this, I think we need a clearer description of the problem being solved. Please open an issue explaining that and link here.

What are the absolute paths coming in through cgo? Why is that happening?

I suspect that the Go builder and the linker may be the wrong place to fix this. Maybe we can avoid absolute paths in the first place.

@smertnik3sh
Copy link
Contributor Author

smertnik3sh commented Jan 17, 2025

I was unable to quickly reproduce the issue using the actual opensource tools (in our repository we use self-built compiler and sysroot). The original issue in our build occurred on an older version of basel and a different configuration of the buildbarn server (few months ago). I was too lazy to creating pool request immediately, which I now regret)
Currently, when disabling the patch in our repository, the issue appears similar to #4091.
Also we are still using the old LLVM 16 infrastructure (Clang, LLD). Maybe that is the one of causes.
I will check if the issue persists on the latest version of rules_go. If it does, I will open the issue, send reproducer and create a new pull request. For now, this pull request can probably be closed. Thanks for your time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants