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: link failure caused by duplicated LDFLAGS passed to external linker #25930

Open
albertjin opened this issue Jun 17, 2018 · 9 comments

Comments

@albertjin
Copy link

@albertjin albertjin commented Jun 17, 2018

What version of Go are you using (go version)?

go1.10.3

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

macOS/amd64, doing cross compile for android/arm64

What did you do?

export GOOS=android
export GOARCH=arm64
export CC=/path/to/androd/gcc
export CXX=/path/to/androd/g++
export CGO_ENABLED=1
export CGO_LDFLAGS="-Wl,--version-script=/the/version-script/export.txt"
go build -buildmode=c-shared -ldflags="-s -w -v" -v -o libxxx.so foo/bar

What did you expect to see?

The shared library is generated without any error.

What did you see instead?

In the output line

0.19 host link: ...

"-Wl,--version-script=/the/version-script/export.txt" is repeated 4 time and there are 3 errors,

anonymous version tag cannot be combined with other version tags

Note that if the version script contains a version name, the error message is 'duplicate version tag xxx'.

@albertjin

This comment has been minimized.

Copy link
Author

@albertjin albertjin commented Jun 17, 2018

For a quick fix, change the line in src/cmd/link/internal/ld/lib.go

	argv = append(argv, ldflag...)

as the following,

	{
		added := map[string]bool{}
		for _, s := range ldflag {
			if added[s] {
				continue
			}
			added[s] = true
			argv = append(argv, s)
		}
	}
@albertjin albertjin changed the title Link failure caused by duplicated LDFLAGS passed to cgo linker cmd/go: Link failure caused by duplicated LDFLAGS passed to cgo linker Jun 17, 2018
@albertjin albertjin changed the title cmd/go: Link failure caused by duplicated LDFLAGS passed to cgo linker cmd/link: Link failure caused by duplicated LDFLAGS passed to cgo linker Jun 17, 2018
@tklauser

This comment has been minimized.

Copy link
Member

@tklauser tklauser commented Jun 18, 2018

@ianlancetaylor ianlancetaylor changed the title cmd/link: Link failure caused by duplicated LDFLAGS passed to cgo linker cmd/link: link failure caused by duplicated LDFLAGS passed to cgo linker Jun 18, 2018
@ianlancetaylor ianlancetaylor changed the title cmd/link: link failure caused by duplicated LDFLAGS passed to cgo linker cmd/link: link failure caused by duplicated LDFLAGS passed to external linker Jun 18, 2018
@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Jun 18, 2018

The suggested fix above is unfortunately too simple, as some options like -Bstatic may appear multiple times, and eliminating them would change the meaning of the link command. I don't know how to fix this without understanding the external linker options.

@ianlancetaylor ianlancetaylor added this to the Unplanned milestone Jun 18, 2018
@albertjin

This comment has been minimized.

Copy link
Author

@albertjin albertjin commented Jun 19, 2018

While analyzing the code for a safe fix, I realized that using #cgo directive in one .go file also solves the problem. This directive will not be replicated for each imported package, so there is just one after all auto generated //go:cgo_ldflag ... directives are merged back from each package.

It looks like this,

/*
#cgo LDFLAGS: "-Wl,--version-script=${SRCDIR}/export.txt"
//...
*/
#import "C"
// ...

Note that this flag is not in the white list and requires the following environment variable,

CGO_LDFLAGS_ALLOW="-Wl,--version-script=[a-zA-Z0-9+-_/.]+"

It seems that CGO_LDFLAGS is about a global configuration option of the Go toolchain. It should better not be used for each project.

@albertjin

This comment has been minimized.

Copy link
Author

@albertjin albertjin commented Sep 24, 2018

@ianlancetaylor, I got a new idea for a general fix.

  1. Add a mark tag at the moment getting CGO_LDFLAGS array from env
  2. Remove the tag and clean up all duplicated arrays at linking

Note that the tag format is (a const uuid + length of the whole CGO_LDFLAGS array with the tag prefix).

patch_cgo_ldflags.diff.txt

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Sep 24, 2018

@albertjin For copyright reasons I prefer to look at patches submitted using as a PR or via Gerrit, to ensure that the CLA is signed.

It sounds like you are suggesting handling LDFLAGS that come from an environment variable differently than LDFLAGS listed in the file, and removing duplicates from the environment variable. That seems reasonable.

@tmm1

This comment has been minimized.

Copy link
Contributor

@tmm1 tmm1 commented Jul 20, 2019

Is there any way to blacklist or remove certain ldflags from making it to the linker invocation?

For instance, src/runtime/cgo/cgo.go contains #cgo !android,linux LDFLAGS: -lpthread which is causing problems because I'm creating a static binary with some c++ code and it is very particular about where in the link line -lpthread will appear (and it also needs to be in the form of -Wl,--whole-archive -lpthread -Wl,--no-whole-archive). Is my only option to patch cgo.go?

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Jul 20, 2019

Can you simply add the -Wl,--whole-archive ... to #cgo LDFLAGS in your own Go file?

If that doesn't work, the next fallback would be to write a little script that shuffles the arguments as you need, and pass that script as -ldflags=-extld=SCRIPT.

@albertjin

This comment has been minimized.

Copy link
Author

@albertjin albertjin commented Jul 29, 2019

Can you simply add the -Wl,--whole-archive ... to #cgo LDFLAGS in your own Go file?

If that doesn't work, the next fallback would be to write a little script that shuffles the arguments as you need, and pass that script as -ldflags=-extld=SCRIPT.

For my situation, which was a generic build and deployment helper, I actually made a wrapper for the native compiler/linker without fixing the go toolchain. The method is the same as done in the previous patch, by adding a prefix mark in the environment variable CGO_LDFLAGS and getting rid of the duplicates later.

I tried to consolidate and submit the previous patch but hesitated. Maybe documenting the limitation of CGO_LDFLAGS is a final fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants
You can’t perform that action at this time.