golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
124.08k stars 17.68k forks source link

cmd/go: go tool rejects #cgo LDFLAGS: -Wl,-rpath,@executable_path/../Frameworks #40559

Open abemedia opened 4 years ago

abemedia commented 4 years ago

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

$ go version
go version go1.14.6 darwin/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="/Users/adam/Library/Caches/go-build"
GOENV="/Users/adam/Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/adam/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/Cellar/go/1.14.6/libexec"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/Cellar/go/1.14.6/libexec/pkg/tool/darwin_amd64"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/Users/adam/Work/go-test/go.mod"
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/rp/mzkkm6297lv16qf_qgszt3wm0000gn/T/go-build776046366=/tmp/go-build -gno-record-gcc-switches -fno-common"

What did you do?

I tried to build a MacOS bundle using dynamically linked libraries. As per convention these are stored in MyApp.app/Contents/Frameworks which requires LC_RPATH to point there.

I added the following flag:

#cgo LDFLAGS: -Wl,-rpath,@executable_path/../Frameworks

What did you expect to see?

go build builds binary with LC_RPATH set to @executable_path/../Frameworks

What did you see instead?

I got an invalid flag error upon running go build.

I also tried replacing @executable_path for $ORIGIN as I would in Linux but then I got an image not found error when running the resulting binary (I assume this isn't supported on MacOS).

ianlancetaylor commented 4 years ago

What does @executable_path mean?

Any fix for this will have to be very careful. For the GNU tools, among others, starting a parameter with @ causes it to be replaced with the contents of a file with the string following the @. So if we do need to handle @executable_path, we need to be careful to only do it in a context in which we know for sure that it won't be replaced by a file. Otherwise all of this flag vetting no longer works.

abemedia commented 4 years ago

@exexutable_path is like the MacOS version of $ORIGIN on Linux i.e. it is the path that the binary is in at runtime to be able to dynamically link to a library using a relative path.

abemedia commented 4 years ago

Here's some more info: https://wincent.com/wiki/@executable_path,_@load_path_and_@rpath While we're at it we should probably add @loader_path as well as @executable_path.

jayconrod commented 4 years ago

It looks like @ is specifically forbidden as a leading character since we started validating cgo flags in 1dcb5836.

A wrinkle in all this is that GNU binutils uniformly accept
@foo on the command line to mean "if the file foo exists,
then substitute its contents for @foo in the command line".
So we must also reject @x.go, flags and flag arguments
beginning with @, and so on.

That would still be an issue here. Perhaps we could support specifically @loader_path and @executable_path (nothing else that starts with @), and only allow those when GOOS=darwin?

abemedia commented 4 years ago

Yes, that sounds like the ideal solution!

abemedia commented 4 years ago

If someone can point me to where I need to look I'm happy to have a bash at a PR.

jayconrod commented 4 years ago

security.go contains regular expressions for allowed flags.

cfg.GOOS may be used to check the target operating system. I don't think we have any regular expressions that are allowed on one platform but not others, but that would be necessary here.

cgo_bad_directives.txt tests this filtering, so that would need to be expanded. See README.txt in that directory for information on the test format.

snmed commented 4 years ago

Actually I'm evaluating go for a project on mac and i've a poc that needs to load an external *.dylib from a directory relative to the binary. So I stumbled over @executable_path and this issue with go, so is there any known workaround to get a go binary working that can load a *.dylib from a directory relative to the binary?

Any suggestions would be great, I really would like to use go for the project, but we need to load external C libraries that are not installed in any of the OS search paths.

Update: Sorry for the post but I've found at least a workaround for that:

  1. Compile my go file with cgo flags
    
    package native

/*

cgo LDFLAGS: -L../path/to/libs -Wl,-rpath,./libs -lgreeter

include "path/to/libs/include/greeter.h"

include

*/ import "C"

// Rest of code omitted for brevity

2. Run `go build -o mybinary`
3. Run the `install_name_tool`
```bash
# You can check the correct name for change with otool -L mybinary
install_name_tool -change @rpath/libgreeter.dylib @executable_path/libs/x64/libgreeter.dylib mybinary

I'm not sure if this would break anything else, but so far everything is working as expected. I still hope that the go tooling will allow the @executable_path and @loader_path flags in the future.

ianlancetaylor commented 4 years ago

@snmed I'm glad you found a way. You can also set set the CGO_LDFLAGS_ALLOW environment variable. https://golang.org/cmd/go/#hdr-Environment_variables

snmed commented 4 years ago

@ianlancetaylor Thanks for your advice, that worked very well with CGO_LDFLAGS_ALLOW="-Wl,-rpath,@executable_path/.*" go build, now only the vscode extensions still shows the invalid flag in #cgo LDFLAGS:-Wl,-rpath,@executable_path/libs maybe there is some setting for that. A workaround for that is to set the variable for the whole development environment.