将一个无操作二进制文件与`net`和`cgo`关闭进行静态链接会失败。

huangapple go评论75阅读模式
英文:

static linking a no-op binary with `net` and cgo turned off fails

问题

我尝试提交了一个错误报告(https://github.com/golang/go/issues/48081),但被发送到了这里。

我正在尝试静态链接一个二进制文件,即使是一个空操作的二进制文件,也会遇到陷阱问题。

我使用的是docker golang:1.17-buster 镜像,但在1.16版本中也可以重现这个问题。

我尝试静态编译一个简单的主文件:

$ cat go.mod
module naphatkrit/go-bug-repro

go 1.17
$ cat main.go
package main

func main() {
}

然后我使用需要静态链接二进制文件的标志运行go build,结果出现了一个关于找不到cgo的警告。

$ go build -a -v -o /tmp/my-binary -ldflags "-linkmode external -extldflags -static" -trimpath .
runtime/internal/sys
internal/goexperiment
internal/cpu
internal/abi
runtime/internal/atomic
runtime/internal/math
internal/bytealg
runtime
naphatkrit/go-bug-repro
# naphatkrit/go-bug-repro
loadinternal: cannot find runtime/cgo

执行二进制文件会导致陷阱错误。

$ /tmp/my-binary
Trace/breakpoint trap

此外,以下是strace的输出:

root@3f441e3ed41c:/code# strace /tmp/my-binary
execve("/tmp/my-binary", ["/tmp/my-binary"], 0x7fff5db80d00 /* 10 vars */) = 0
brk(NULL)                               = 0xf03000
brk(0xf041c0)                           = 0xf041c0
arch_prctl(ARCH_SET_FS, 0xf03880)       = 0
uname({sysname="Linux", nodename="3f441e3ed41c", ...}) = 0
readlink("/proc/self/exe", "/tmp/my-binary", 4096) = 14
brk(0xf251c0)                           = 0xf251c0
brk(0xf26000)                           = 0xf26000
arch_prctl(ARCH_SET_FS, 0x55fd90)       = 0
--- SIGTRAP {si_signo=SIGTRAP, si_code=SI_KERNEL} ---
+++ killed by SIGTRAP +++
Trace/breakpoint trap

如果我强制包含runtime/cgo,构建警告就会消失,编译后的二进制文件可以正常运行。

$ cat main.go
package main

import _ "runtime/cgo"

func main() {
}
$ go build -a -v -o /tmp/my-binary -ldflags "-linkmode external -extldflags -static" -trimpath .
internal/race
runtime/internal/sys
internal/abi
internal/cpu
internal/goexperiment
runtime/internal/atomic
sync/atomic
runtime/internal/math
internal/bytealg
runtime
sync
runtime/cgo
naphatkrit/go-bug-repro
$ /tmp/my-binary
$

我是不是静态链接的方法有问题?

我已经在https://github.com/naphatkrit/go-bug-repro 上放置了我的复现代码,以方便查看。

编辑:我意识到我简化了我的复现代码,这使得问题变得不明显。我想要做的是静态链接一个使用net包并关闭其cgo使用的二进制文件。

特别是,考虑以下代码,看看它与上面的问题相同:

package main

import _ "net"

func main() {
}
$ go build -a -v -o /tmp/gobuild -ldflags "-linkmode external -extldflags -static" -trimpath -tags 'netgo' .
...
loadinternal: cannot find runtime/cgo
$ /tmp/gobuild
Trace/breakpoint trap
英文:

I tried to file a bug report (https://github.com/golang/go/issues/48081) but was sent here instead.

I am trying to statically link a binary, and am running into trap issues even with a no op binary.

$ go version
go version go1.17 linux/amd64

Note I was using the docker golang:1.17-buster image, but this repros with 1.16 too.

$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/root/.cache/go-build"
GOENV="/root/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.17"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/dev/null"
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-build838287027=/tmp/go-build -gno-record-gcc-switches"

I tried to statically compile a simple main file:

$ cat go.mod
module naphatkrit/go-bug-repro

go 1.17
$ cat main.go
package main

func main() {
}

I then ran go build with the flags needed to statically link my binary, this results in a warning about not being able to find cgo.

$ go build -a -v -o /tmp/my-binary -ldflags "-linkmode external -extldflags -static" -trimpath .
runtime/internal/sys
internal/goexperiment
internal/cpu
internal/abi
runtime/internal/atomic
runtime/internal/math
internal/bytealg
runtime
naphatkrit/go-bug-repro
# naphatkrit/go-bug-repro
loadinternal: cannot find runtime/cgo

Executing the binary results in a trap error

$ /tmp/my-binary
Trace/breakpoint trap

Additionally, here is what strace shows:

root@3f441e3ed41c:/code# strace /tmp/my-binary
execve("/tmp/my-binary", ["/tmp/my-binary"], 0x7fff5db80d00 /* 10 vars */) = 0
brk(NULL)                               = 0xf03000
brk(0xf041c0)                           = 0xf041c0
arch_prctl(ARCH_SET_FS, 0xf03880)       = 0
uname({sysname="Linux", nodename="3f441e3ed41c", ...}) = 0
readlink("/proc/self/exe", "/tmp/my-binary", 4096) = 14
brk(0xf251c0)                           = 0xf251c0
brk(0xf26000)                           = 0xf26000
arch_prctl(ARCH_SET_FS, 0x55fd90)       = 0
--- SIGTRAP {si_signo=SIGTRAP, si_code=SI_KERNEL} ---
+++ killed by SIGTRAP +++
Trace/breakpoint trap

If I forcibly include runtime/cgo, the build warning goes away and the compiled binary runs fine.

$ cat main.go
package main

import _ "runtime/cgo"

func main() {
}
$ go build -a -v -o /tmp/my-binary -ldflags "-linkmode external -extldflags -static" -trimpath .
internal/race
runtime/internal/sys
internal/abi
internal/cpu
internal/goexperiment
runtime/internal/atomic
sync/atomic
runtime/internal/math
internal/bytealg
runtime
sync
runtime/cgo
naphatkrit/go-bug-repro
$ /tmp/my-binary
$

Am I doing static linking wrong?

I have put up my repro at https://github.com/naphatkrit/go-bug-repro for convenience


Edit: I realized that I simplified my repro too far and it detracted from the issue I was facing. What I am trying to do is statically link a binary that uses net and turn off its cgo usage.

In particular, consider this instead and see how it has the same issue as above:

package main

import _ "net"

func main() {
}
$ go build -a -v -o /tmp/gobuild -ldflags "-linkmode external -extldflags -static" -trimpath -tags 'netgo' .
...
loadinternal: cannot find runtime/cgo
$ /tmp/gobuild
Trace/breakpoint trap

答案1

得分: 1

Go默认情况下构建静态二进制文件(除非你实际上使用cgo导入C代码)。标准库中有两个例外。netos/user包默认使用本地代码。

编辑:

要在不使用本地代码的情况下使用net包,可以执行以下操作:

CGO_ENABLED=0 go build ...

或者

go build -tags netgo ...

但是你的方法也应该可以正常工作,尽管我会省略-a标志。

英文:

Go builds static binaries by default (unless you are actually using cgo to import C code). There are two exceptions with the std library. The net and os/user packages do use native code by default.

EDIT:

To use the net package without native code, you can do

CGO_ENABLED=0 go build ...

or

go build -tags netgo ...

But what you have should also work as well, although I'd omit the -a flag.

huangapple
  • 本文由 发表于 2021年8月31日 12:54:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/68993070.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定