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/go: dylib on macos with rpath fails to run with go run/test #36572

Open
edaniels opened this issue Jan 15, 2020 · 17 comments
Open

cmd/go: dylib on macos with rpath fails to run with go run/test #36572

edaniels opened this issue Jan 15, 2020 · 17 comments

Comments

@edaniels
Copy link
Contributor

@edaniels edaniels commented Jan 15, 2020

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

$ go version
go version go1.13.6 darwin/amd64

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

$ sw_vers -productVersion
10.15.2

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="auto"
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/eric/Library/Caches/go-build"
GOENV="/Users/eric/Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/eric/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD=""
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 -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/v4/m6f6w8h54j58bp8q4h_138180000gn/T/go-build216040480=/tmp/go-build -gno-record-gcc-switches -fno-common"

What did you do?

When trying to run a simple go source file that links against an rpath based dylib, it fails to run with either go run or go test on 1.13.6. However, doing go build followed by running the executable works fine. It also works fine when I build go and test it from the go1.13.6 tag. It also works in go 1.13.5 but from looking at the commits between the two, I cannot find an issue. This leads me to believe that however the 1.13.6 was built on google servers introduced some kind of issue.

A repro with a makefile is located at https://github.com/edaniels/go1136dylibissue.

What did you expect to see?

I expected to see a log output of 2 from:

LD_LIBRARY_PATH=`pwd`/dylib/lib go run github.com/edaniels/go1136dylibissue/cmd

What did you see instead?

dyld: Library not loaded: @rpath/libfoo.dylib
  Referenced from: /var/folders/v4/m6f6w8h54j58bp8q4h_138180000gn/T/go-build330381377/b001/exe/cmd
  Reason: image not found
signal: abort trap
@adamchel
Copy link

@adamchel adamchel commented Jan 15, 2020

I have encountered the same exact problem. Downgrading to 1.13.5 fixed it for me.

@cagedmantis cagedmantis changed the title dylib on macos with rpath fails to run with go run/test cmd/go: dylib on macos with rpath fails to run with go run/test Jan 15, 2020
@randall77
Copy link
Contributor

@randall77 randall77 commented Jan 15, 2020

I can't reproduce this problem.
I downloaded your test case, and edited the makefile to remove the GOROOT assignment (not needed) and changed the instance of /usr/local/gogit/bin/go to go.
When I run make with Go 1.13.6 on OSX 10.15.2, the makefile runs to completion with no errors.

A possible theory: your GOROOT, go, and/or /usr/local/gogit/bin/go point to more than one installation of Go.

@randall77
Copy link
Contributor

@randall77 randall77 commented Jan 15, 2020

I take that back, I can reproduce. It only reproduces with the downloaded Go pkg, not the source.

@randall77
Copy link
Contributor

@randall77 randall77 commented Jan 15, 2020

@cagedmantis Anything different about the release process for 1.13.6 from 1.13.5?
None of the patches in 1.13.6 look relevant, and building 1.13.6 on my machine from source does not demonstrate the problem.
Different temp directory? Different environment variables? Not sure what might matter...

@cagedmantis
Copy link
Contributor

@cagedmantis cagedmantis commented Jan 15, 2020

@randall77 The only difference with the release process between 1.13.6 and 1.13.5 was the enabling of the Hardened Runtime. See #34986 for details.

@cagedmantis cagedmantis added this to the Backlog milestone Jan 15, 2020
@randall77
Copy link
Contributor

@randall77 randall77 commented Jan 15, 2020

That would make a lot of sense then.

From an article about the hardened runtime:

This involves a series of restrictions being placed on that app, which are intended to protect that app’s runtime integrity from certain types of exploits used by malware. These include code injection, the hijacking of dynamically-linked libraries (DLLs), and tampering with the app’s memory space.

So hard to be sure, but I suspect that part of hardening is squashing any LD_LIBRARY_PATH settings (the second part of that second sentence). So LD_LIBRARY_PATH won't propagate through the go binary to the binary to be run.
Note that we don't generate hardened binaries. It is only the Go tool itself that is hardened. That's why it works if you build and run separately.

I think that although this is unfortunate, it's intentional and there's nothing we can do.

Just wait until Apple starts forcing us to generate hardened binaries...

@cagedmantis
Copy link
Contributor

@cagedmantis cagedmantis commented Jan 15, 2020

@randall77 A member of the release team has suggested that we investigate some of the available entitlements (at some point) such as:

Do you think these are entitlements that we should enable?

@randall77
Copy link
Contributor

@randall77 randall77 commented Jan 15, 2020

That first one might possibly fix this issue.
I don't think we would want the second one. We don't want to actually allow unvalidated libraries to be run by the go tool. This entitlement is one that Go-generated binaries would need (assuming they were hardened, which they aren't at the moment) to use LD_LIBRARY_PATH successfully.

I don't think that's really the right way to go though. It's kind of an accident that the go tool passes environment variables unmolested through to temporary executables. It's asking for issues related to the same environment variable meaning different things in the go binary and the user's binary. (e.g. what if you wanted a different CC value for the go tool and the user's binary? Can't do that with CC=gcc go run.)

I think it is reasonable to ask that if you want to pass environment variables to a program written in Go, you have to build and run as separate steps.

You might be able to use the -exec flag to go run to introduce an environment variable at the right time. Something like

go run -exec "env LD_LIBRARY_PATH=/my/library/path" myprogram.go
@edaniels
Copy link
Contributor Author

@edaniels edaniels commented Jan 16, 2020

I think the bigger issue in terms of development is not wanting to have to build test binaries and run them instead of just running go test. It seems like it makes sense to have the go tooling pass through a flag such as this one. It was before so why not make it continue to do so. I’m not sure I see a practical benefit to limiting it for the sake of hardening a build tool.

@randall77
Copy link
Contributor

@randall77 randall77 commented Jan 16, 2020

I'm not worried about hardening the build tool - that's just something that is being forced on us by Apple.

But this issue demonstrates that it just isn't reliable to depend on the go tool faithfully transmitting all the environment variables. Through no fault of our own. What other environment variables is Apple silently squashing? What new ones are they going to start squashing tomorrow?

I suspect setting the first entitlement described above would fix this particular issue. And maybe we should do that, for backwards compatibility if nothing else. But I don't think the underlying problem is solved by doing that. The underlying problem is only solved by build/run separately, or using the -exec flag as I mentioned.

@edaniels
Copy link
Contributor Author

@edaniels edaniels commented Jan 16, 2020

Yep -exec does work. I think in this case, it makes sense to build up a whitelist of environment variables and have LD_LIBRARY_PATH be in it via entitlement.

@bcmills bcmills added the OS-Darwin label Jan 22, 2020
@bcmills bcmills modified the milestones: Backlog, Unplanned Jan 22, 2020
@bcmills
Copy link
Member

@bcmills bcmills commented Jan 22, 2020

@cagedmantis
Copy link
Contributor

@cagedmantis cagedmantis commented Jan 30, 2020

@bcmills I'm just double checking that this should not be a release blocker for go1.14rc1.

@bcmills
Copy link
Member

@bcmills bcmills commented Jan 30, 2020

Given that this issue appears to only affect dynamic libraries on macOS, has been present since Go 1.13.6, and has two workarounds (splitting the build step from the execution step, or using -exec as described in #36572 (comment)), I don't think this needs to block 1.14 at all.

@cagedmantis
Copy link
Contributor

@cagedmantis cagedmantis commented Jan 30, 2020

Thanks for the response @bcmills. I will update #34986 accordingly.

@adamchel
Copy link

@adamchel adamchel commented Apr 14, 2020

The -exec "env LD_LIBRARY_PATH=/my/library/path" workaround works for go run and go test, it does not appear to work for go tool.

I just upgraded to go 1.14.2 from 1.13.5, and it broke my ability to run tests in GoLand. GoLand uses go tool test2json -t to run tests. I've tried giving go tool test2json the -exec flag but it doesn't appear to work.

This is the test setup invoked by GoLand:

/usr/local/go/bin/go test -c -exec "env LD_LIBRARY_PATH=REDACTED" -o /private/var/folders/REDACTED_go github.com/REDACTED #gosetup
/usr/local/go/bin/go tool test2json -t /private/var/folders/REDACTED_go -test.v -test.run "^REDACTED$" -exec "env LD_LIBRARY_PATH=REDACTED" #gosetup
dyld: Library not loaded: @rpath/REDACTED.dylib
  Referenced from: /private/var/folders/REDACTED_go
  Reason: image not found

@randall77 @cagedmantis Is there a workaround you are aware of for passing an LD_LIBRARY_PATH to an invocation of go tool test2json? Or will this require adding -exec as a supported flag to go tool?

@bcmills
Copy link
Member

@bcmills bcmills commented Apr 14, 2020

@adamchel, you can run go tool -n test2json to locate the path to the executable, then invoke that executable yourself with a suitable LD_LIBRARY_PATH.

lispc added a commit to iost-official/go-iost that referenced this issue May 20, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
5 participants
You can’t perform that action at this time.