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

plugin: "built with a different version of package" error with go 1.18 & CGO #51955

Closed
dtrudg opened this issue Mar 25, 2022 · 7 comments
Closed

Comments

@dtrudg
Copy link

dtrudg commented Mar 25, 2022

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

go version go1.18 linux/amd64

Does this issue reproduce with the latest release?

yes

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

go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/dtrudg/.cache/go-build"
GOENV="/home/dtrudg/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/dtrudg/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/dtrudg/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/opt/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/opt/go/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.18"
GCCGO="gccgo"
GOAMD64="v1"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/home/dtrudg/Git_sylabs/singularity/go.mod"
GOWORK=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build864842809=/tmp/go-build -gno-record-gcc-switches"

What did you do?

We have a plugin (https://github.com/sylabs/singularity/blob/master/e2e/plugin/testdata/runtime_singularity/callback.go) that indirectly imports a package containing some CGO code (https://github.com/sylabs/singularity/tree/master/internal/pkg/util/user).

I have tried to extract a simpler reproducer, but haven't managed to hit the same issue yet... so what follows is, unfortunately, a terse description of a rather complicated build...

Our main binary is compiled with the following go build and GOFLAGS contains -trimpath

go build
  -mod=readonly
  -buildmode=pie
  -tags "containers_image_openpgp exclude_graphdriver_btrfs exclude_graphdriver_devicemapper sylog singularity_engine fakeroot_engine apparmor selinux seccomp"  
  -gcflags=github.com/sylabs/singularity/...="-trimpath /home/dtrudg/Git_sylabs/singularity=>github.com/sylabs/singularity@v0.0.0" 
  -asmflags=github.com/sylabs/singularity/...="-trimpath /home/dtrudg/Git_sylabs/singularity=>github.com/sylabs/singularity@v0.0.0"
  -o ./singularity 
  /home/dtrudg/Git_sylabs/singularity/cmd/singularity

The plugin is built by copying the source into a temporary directory, and then using the following go build:

go build 
  -a 
  -o /tmp/plugin-1291001354/src/plugin.so 
  -mod=readonly 
  -ldflags -X main.PluginRootDir=/usr/local/libexec/singularity/plugin 
  -trimpath 
  -buildmode=plugin 
  -tags containers_image_openpgp exclude_graphdriver_btrfs exclude_graphdriver_devicemapper sylog singularity_engine fakeroot_engine apparmor selinux seccomp
  .

What did you expect to see?

With Go 1.17.8 or prior, the compiled plugin can be loaded by the main binary without issue.

What did you see instead?

With Go 1.18, the plugin is compiled without issue, but when the main binary attempts to load it, it fails with an error:

plugin.Open("/tmp/plugin-1836960432/src/plugin"): plugin was built with a different version of package github.com/sylabs/singularity/internal/pkg/util/user

Examining the pkghashbytes for the "github.com/sylabs/singularity/pkg/image" package, I can see a difference between the main binary and plugin:

main binary: 
go.link.pkghashbytes.github.com/sylabs/singularity/internal/pkg/util/user: 
[233 225 141 122 49 203 202 104]

plugin:
go.link.pkghashbytes.github.com/sylabs/singularity/internal/pkg/util/user:
[189 134 9 108 2 209 91 245]

I can see that there is a difference in the strings section exposed in cmd/compile/internal/typecheck/iexport.go:

main binary compilation
# github.com/sylabs/singularity/internal/pkg/util/user
export: hdr strings 533, data 950, index 1039

plugin compilation
# github.com/sylabs/singularity/internal/pkg/util/user
export: hdr strings 504, data 950, index 1039

Grabbing these and converting to text we have:

strings section in the main binary build…

Lgithub.com/sylabs/singularity@v0.0.0/internal/pkg/util/user/identity_unix.goNameUIDGIDGecosDirShelluidUser~r0~r1$autotmp
lookupUnixUidNgithub.com/sylabs/singularity@v0.0.0/internal/pkg/util/user/cgo_lookup_unix.goname
lookupUserusernamegidGroup
lookupUnixGid�lookupGroup	groupnamecurrentIgithub.com/sylabs/singularity@v0.0.0/internal/pkg/util/user/membership.golistesc:,/tmp/go-build2510098997/b050/_cgo_gotypes.gouser	.inittaskCurrentCurrentOriginalGetGrGIDGetGrNamGetPwNamGetPwUID
UIDInAnyGroup	UIDInList

strings section in the plugin build…

Lgithub.com/sylabs/singularity@v0.0.0/internal/pkg/util/user/identity_unix.goNameUIDGIDGecosDirShelluidUser~r0~r1$autotmp
lookupUnixUidNgithub.com/sylabs/singularity@v0.0.0/internal/pkg/util/user/cgo_lookup_unix.goname
lookupUserusernamegidGroup
lookupUnixGid�lookupGroup	groupnamecurrentIgithub.com/sylabs/singularity@v0.0.0/internal/pkg/util/user/membership.golistesc:_cgo_gotypes.gouser	.inittaskCurrentCurrentOriginalGetGrGIDGetGrNamGetPwNamGetPwUID
UIDInAnyGroup	UIDInList

The difference here being an extra ,/tmp/go-build2510098997/b050/ prior to _cgo_gotypes.gouser in the build of the main binary.

I'm not sure what might have changed from 1.17.x -> 1.18 to cause this?

A quick strings $(which singularity) (main binary) for different 1.18 and 1.17.8 builds shows at least some kind of difference without the context:

Go 1.18

strings $(which singularity) | grep "_cgo_gotypes.go"
_cgo_gotypes.go
/tmp/go-build2709203403/b271/_cgo_gotypes.go
/tmp/go-build2709203403/b666/_cgo_gotypes.go

Go 1.17.8

strings $(which singularity) | grep "_cgo_gotypes.go"
_cgo_gotypes.go
/tmp/go-build1536705585/b262/_cgo_gotypes.go

Any pointers on where to go here would be appreciated!

@dtrudg dtrudg changed the title plugin: "built with a different version of package error" with go 1.18 & CGO plugin: "built with a different version of package" error with go 1.18 & CGO Mar 25, 2022
@zhouguangyuan0718
Copy link
Contributor

I'm a little confused. When you build the plugin, you use -trimpath for go build. But when you build the main binary, you only use -trimpath for -gcflags. How does other pkghash
equal, such as runtime? As I know, the hash of every package is related to the path of it. The path is different when "-trimpath" is enabled.

@dtrudg
Copy link
Author

dtrudg commented Mar 28, 2022

I'm a little confused. When you build the plugin, you use -trimpath for go build. But when you build the main binary, you only use -trimpath for -gcflags. How does other pkghash equal, such as runtime? As I know, the hash of every package is related to the path of it. The path is different when "-trimpath" is enabled.

The general -trimpath is used for the main binary in addition to the asmflags and gcflags trimpath, but it is set via GOFLAGS env var

Our main binary is compiled with the following go build and GOFLAGS contains -trimpath

Am pretty confident this is working, as we don't see any issue with Go 1.17.8 or older - this problem only occurs with Go 1.18... and the error occurs only for a particular package that uses cgo. Other plugins that don't use our github.com/sylabs/singularity/internal/pkg/util/user package are fine with Go 1.18.

I can pull out the strings section for Go 1.17.8 (which works) tomorrow, with exactly the same build flags / GOFLAGS to clarify a bit further.

@dtrudg
Copy link
Author

dtrudg commented Mar 28, 2022

I moved -trimpath from GOFLAGS to an explicit flag on go build, so that's less confusing. Have also added below a direct comparison of the package strings between Go 1.17.8 and Go 1.18.

The main binary now builds with:

go build 
  -mod=readonly
  -buildmode=pie
  -trimpath
  -tags "containers_image_openpgp exclude_graphdriver_btrfs exclude_graphdriver_devicemapper sylog singularity_engine fakeroot_engine apparmor selinux seccomp"
  -ldflags="-B 0x`head -c20 /dev/urandom|od -An -tx1|tr -d ' \n'`"
  -gcflags=github.com/sylabs/singularity/...="-trimpath /home/dtrudg/Git_sylabs/singularity=>github.com/sylabs/singularity@v0.0.0"
  -asmflags=github.com/sylabs/singularity/...="-trimpath /home/dtrudg/Git_sylabs/singularity=>github.com/sylabs/singularity@v0.0.0" 
  -o cmd/starter/c/starter
  /home/dtrudg/Git_sylabs/singularity/cmd/starter/main_linux.go

The plugin now builds with:

go build 
  -a 
  -o /tmp/plugin-1227855955/src/plugin.so 
  -mod=readonly 
  -ldflags -X main.PluginRootDir=/usr/local/libexec/singularity/plugin 
  -trimpath
  -buildmode=plugin 
  -tags containers_image_openpgp exclude_graphdriver_btrfs exclude_graphdriver_devicemapper sylog singularity_engine fakeroot_engine apparmor selinux seccomp 
  .

As before, everything works in Go 1.17.8 (or prior), but not with Go 1.18...

With Go 1.17.8 plugin.Open succeeds

The main binary and plugin build correctly, plugin.Open succeeds, and the plugin can be used.

Strings for internal/pkg/util/user during main binary build with 1.17.8:

Lgithub.com/sylabs/singularity@v0.0.0/internal/pkg/util/user/identity_unix.go�Name�UID�GID�Gecos�Dir�Shell�uid�User�~r1�~r2
.autotmp_3
.autotmp_4
lookupUnixUidNgithub.com/sylabs/singularity@v0.0.0/internal/pkg/util/user/cgo_lookup_unix.go�name
lookupUser�username�gid�Group
lookupUnixGid�lookupGroup	groupname�~r0
.autotmp_2�currentIgithub.com/sylabs/singularity@v0.0.0/internal/pkg/util/user/membership.go�list�esc:�user	.inittask�Current�CurrentOriginal�GetGrGID�GetGrNam�GetPwNam�GetPwUID
UIDInAnyGroup	UIDInList

Strings for internal/pkg/util/user/ during plugin build with 1.17.8:

Lgithub.com/sylabs/singularity@v0.0.0/internal/pkg/util/user/identity_unix.go�Name�UID�GID�Gecos�Dir�Shell�uid�User�~r1�~r2
.autotmp_3
.autotmp_4
lookupUnixUidNgithub.com/sylabs/singularity@v0.0.0/internal/pkg/util/user/cgo_lookup_unix.go�name
lookupUser�username�gid�Group
lookupUnixGid�lookupGroup	groupname�~r0
.autotmp_2�currentIgithub.com/sylabs/singularity@v0.0.0/internal/pkg/util/user/membership.go�list�esc:�user	.inittask�Current�CurrentOriginal�GetGrGID�GetGrNam�GetPwNam�GetPwUID
UIDInAnyGroup	UIDInList

These are identical between main binary and plugin, so there is no pkghash mismatch / failure with plugin.Open

Note that there is no path information in either. There are references to autotmp_X which aren't present in the Go 1.18 strings below.

There is also no reference to _cgo_gotypes.go which is the string which has an extra prefixed path in the main binary build for Go 1.18, causing the mismatch with the Go 1.18 plugin.

With Go 1.18 plugin.Open fails

plugin.Open("/tmp/plugin-1227855955/src/plugin"): plugin was built with a different version of package github.com/sylabs/singularity/internal/pkg/util/user

Strings for internal/pkg/util/user during main binary build with 1.18:

Lgithub.com/sylabs/singularity@v0.0.0/internal/pkg/util/user/identity_unix.go�Name�UID�GID�Gecos�Dir�Shell�uid�User�~r0�~r1�$autotmp
lookupUnixUidNgithub.com/sylabs/singularity@v0.0.0/internal/pkg/util/user/cgo_lookup_unix.go�name
lookupUser�username�gid�Group
lookupUnixGid�lookupGroup	groupname�currentIgithub.com/sylabs/singularity@v0.0.0/internal/pkg/util/user/membership.go�list�esc:+/tmp/go-build552663777/b050/_cgo_gotypes.go�user	.inittask�Current�CurrentOriginal�GetGrGID�GetGrNam�GetPwNam�GetPwUID
UIDInAnyGroup	UIDInList

Strings for internal/pkg/util/user during plugin build with 1.18.

Lgithub.com/sylabs/singularity@v0.0.0/internal/pkg/util/user/identity_unix.go�Name�UID�GID�Gecos�Dir�Shell�uid�User�~r0�~r1�$autotmp
lookupUnixUidNgithub.com/sylabs/singularity@v0.0.0/internal/pkg/util/user/cgo_lookup_unix.go�name
lookupUser�username�gid�Group
lookupUnixGid�lookupGroup	groupname�currentIgithub.com/sylabs/singularity@v0.0.0/internal/pkg/util/user/membership.go�list�esc:�_cgo_gotypes.go�user	.inittask�Current�CurrentOriginal�GetGrGID�GetGrNam�GetPwNam�GetPwUID
UIDInAnyGroup	UIDInList

The strings for internal/pkg/util/user from compilation of the main binary differ by having a: /tmp/go-build552663777/b050/ path prior to _cgo_gotypes.go while the strings from compilation of the plugin does not have this path. Therefore the pkghash differs so the plugin fails to load.

I can build other plugins that don't use internal/pkg/util/user without issue. There is no mismatch of pkghash / strings for the other packages between the main binary and plugin build.

@dtrudg
Copy link
Author

dtrudg commented Mar 28, 2022

By looking into src/cmd/compile/internal/noder/noder.go trimFilename() which calls src/cmd/internal/objabi/line.go Absfile I think I've found this is related to building with a -trimpath inside -gcflags on Go 1.18.

When building with a -trimpath in -gcflags...

/home/dave/Git_sylabs/go/bin/go build 
-mod=readonly 
-buildmode=pie 
-trimpath 
-tags "containers_image_openpgp exclude_graphdriver_btrfs exclude_graphdriver_devicemapper sylog singularity_engine fakeroot_engine apparmor selinux seccomp" 
-ldflags="-B 0x`head -c20 /dev/urandom|od -An -tx1|tr -d ' \n'`" 
-gcflags=github.com/sylabs/singularity/...="-trimpath /home/dtrudg/Git_sylabs/singularity=>github.com/sylabs/singularity@v0.0.0" 
-asmflags=github.com/sylabs/singularity/...="-trimpath /home/dtrudg/Git_sylabs/singularity=>github.com/sylabs/singularity@v0.0.0" 
-o cmd/starter/c/starter-suid 
/home/dtrudg/Git_sylabs/singularity/cmd/starter/main_linux.go

... it's apparent that AbsFile doesn't receive the rewrite that will remove the /tmp/gobuildxxxxx path for the _cgo_gotypes.go. It only recieves the rewrite that was set via the -gcflags:

# github.com/sylabs/singularity/internal/pkg/util/user
AbsFile dir:/home/dtrudg/Git_sylabs/singularity file:/tmp/go-build2238438623/b050/_cgo_gotypes.go rewrites:/home/dtrudg/Git_sylabs/singularity=>github.com/sylabs/singularity@v0.0.0
trimFilename: b.Filename()=/tmp/go-build2238438623/b050/_cgo_gotypes.go b.Trimmed()=false b.IsFileBase()=true filename=/tmp/go-build2238438623/b050/_cgo_gotypes.go

If I drop the -gcflags from go build then AbsFile is getting a rewrite that removes the /tmp/go-buildxxxx from the _cgo_gotypes.go` path:

/home/dave/Git_sylabs/go/bin/go build
-mod=readonly
-buildmode=pie
-trimpath
-tags "containers_image_openpgp exclude_graphdriver_btrfs exclude_graphdriver_devicemapper sylog singularity_engine fakeroot_engine apparmor selinux seccomp"
-ldflags="-B 0x`head -c20 /dev/urandom|od -An -tx1|tr -d ' \n'`"
-asmflags=github.com/sylabs/singularity/...="-trimpath /home/dtrudg/Git_sylabs/singularity=>github.com/sylabs/singularity@v0.0.0"
-o cmd/starter/c/starter-suid
/home/dtrudg/Git_sylabs/singularity/cmd/starter/main_linux.go 

# github.com/sylabs/singularity/internal/pkg/util/user
AbsFile dir:/home/dtrudg/Git_sylabs/singularity file:/tmp/go-build1956690545/b050/_cgo_gotypes.go rewrites:/home/dtrudg/Git_sylabs/singularity/internal/pkg/util/user=>github.com/sylabs/singularity/internal/pkg/util/user;/tmp/go-build1956690545/b050=>
trimFilename: b.Filename()=/tmp/go-build1956690545/b050/_cgo_gotypes.go b.Trimmed()=false b.IsFileBase()=true filename=_cgo_gotypes.go

@seankhliao
Copy link
Member

So, similar to #47256 , use the high level -trimpath and not the individual tool flags.
In which case I don't think there's anything to do here?

@dtrudg
Copy link
Author

dtrudg commented Mar 28, 2022

So, similar to #47256 , use the high level -trimpath and not the individual tool flags.
In which case I don't think there's anything to do here?

Our main binary compilation uses the -trimpath in the tool flags as we have a somewhat wacky process of using runtime.Caller(0) and some substitution to identify our own source path so that our CLI can provide a command to compile plugins code that is not within our source tree.

I'll look into whether we can do this more sanely, but at the time it was the implementation that a developer came up with to allow people to build plugins via our own CLI.

I guess I'm interested in pursuing this a bit more, as it used to work up to and including 1.17 - and presumably the gcflag trimpath could just be appended to the rewrites that AbsFile sees, rather than replace them... which would maintain the 1.17 behavior?

Edit - understood that there may not be any appetite for pursuing this further, though, so I'll put most of my efforts on us coming up with something a bit more straightforward to avoid the issue :-)

Edit 2 - there may more to it, w.r.t. the reason for asmflags and gcflags... we're using CGO in our main binary and plugins, and I noticed mention of #27751, #36072 in the linked #47256 - I'll stop speculating and look into it properly.

@dtrudg
Copy link
Author

dtrudg commented Mar 29, 2022

Having come across a definitive message, while browsing through issues, that...

rsc commented on Apr 22, 2019
Direct use of -gcflags=-trimpath is unsupported.

... we'll drop back to a more restrictive approach for building plugins in our app, that doesn't require that.

Apologies for the noise!

@dtrudg dtrudg closed this as completed Mar 29, 2022
@golang golang locked and limited conversation to collaborators Mar 29, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants