你正在使用的Go版本是什么( go version
)?
$ go version
go version go1.21.1 windows/amd64
这个问题在最新版本的发布中是否重现?
1.21.1是MSYS2中可用的最新版本。我尝试了官方的Windows二进制版本1.21.2,但它不支持cgo,所以在链接步骤之前失败。值得注意的是,我不认为在1.21.1和1.21.2之间的提交中有任何相关的内容。
你正在使用什么操作系统和处理器架构( go env
)?
Windows 11, amd64
从MSYS2包中的go mingw-w64-ucrt-x86_64-go
MSYS2包中的GNU Binutils 2.41 mingw-w64-ucrt-x86_64-binutils
go env
输出
$ go env
set GO111MODULE=
set GOARCH=amd64
set GOBIN=
set GOCACHE=C:\Users\maia\AppData\Local\go-build
set GOENV=C:\Users\maia\AppData\Roaming\go\env
set GOEXE=.exe
set GOEXPERIMENT=
set GOFLAGS=
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOINSECURE=
set GOMODCACHE=C:\Users\maia\go\pkg\mod
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOPATH=C:\Users\maia\go
set GOPRIVATE=
set GOPROXY=https://proxy.golang.org,direct
set GOROOT=C:/msys64/ucrt64/lib/go
set GOSUMDB=sum.golang.org
set GOTMPDIR=
set GOTOOLCHAIN=auto
set GOTOOLDIR=C:\msys64\ucrt64\lib\go\pkg\tool\windows_amd64
set GOVCS=
set GOVERSION=go1.21.1
set GCCGO=gccgo
set GOAMD64=v1
set AR=ar
set CC=gcc
set CXX=g++
set CGO_ENABLED=1
set GOMOD=C:\Users\maia\code\snafu\go-hotplug\go.mod
set GOWORK=
set CGO_CFLAGS=-O2 -g
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-O2 -g
set CGO_FFLAGS=-O2 -g
set CGO_LDFLAGS=-O2 -g
set PKG_CONFIG=pkg-config
set GOGCCFLAGS=-m64 -mthreads -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=C:\msys64\tmp\go-build1188127562=/tmp/go-build -gno-record-gcc-switches
你做了什么?
尝试在 elemecca/go-hotplug 中构建示例程序
go build -x ./examples/announcehid
你期望看到什么?
它应该成功生成可执行文件,或者如果我的代码中有什么错误,应该立即失败。无论如何,它在进行操作时都应该消耗合理的内存。
你看到了什么?
在构建结束时的 cmd/link
步骤中, ld.exe
不断分配内存,从未完成。
在我机器上,有64 GB的RAM,它每秒大约分配2 GB,直到达到大约60 GB,然后当系统开始疯狂交换时速度会变慢。出于科学目的,我让它运行一次;Windows蓝屏时已经分配了大约215 GB。
由于问题出现在 ld.exe
中,这可能是GNU Binutils的上游错误,但我不知道关于如何工作的足够信息来确定这一点或为他们提供足够的信息以提交错误报告。
8条答案
按热度按时间bqujaahr1#
我尝试在MSYS2环境中从源代码构建go。
go1.21.2
和master
在f711892a8a
上表现出与打包的go 1.21.1相同的行为。此外,为了纪念,在这个问题发生时,elemecca/go-hotplug上的当前提交是9e9ddbf41d
。vcirk6k62#
I managed to reduce the test case down to just this.
nmpmafwu3#
CC @golang/compiler.
neekobn84#
在排障中,我们想知道如果你编写了一个等效的C程序(以类似的方式调用链接器(即类似的标志),
ld.exe
是否仍然会消耗大量内存?如果是这样的话,那似乎是ld.exe
中的一个bug。dhxwm5r45#
@mknyszek 这个值得一试。
看起来
go build
正在用命令行调用link.exe
,命令行内容为。
link.exe
反过来又用命令行调用
gcc.exe
。gcc.exe
用命令行调用
collect2.exe
。最后,
collect2.exe
用命令行调用
ld.exe
。等效的 C 程序代码如下:
编译成目标文件时,使用
gcc
成功。尝试使用类似于 go 使用的链接命令:
这个命令很快就失败了,输出如下:
这与我在尝试简化测试用例并删除一些必要的重复内存泄漏时从
go build
得到的结果相同,所以我怀疑如果不触发失控的内存泄漏,go 构建也会因为这些错误而失败。我从
gcc
命令中修改的唯一内容是删除了链接脚本-Wl,-T,C:\msys64\tmp\go-link-201239192\fix_debug_gdb_scripts.ld
并替换了对象文件列表。考虑到这一点,我认为引发问题的原因要么是在 cgo 生成的额外 C 代码中,要么是在 go 生成的对象文件中,要么是在链接脚本
cmd/link
中使用的。当然,这并不意味着这是 GNU ld 的上游 bug,只是说 go 在某些方面是必要的才能复现它。hfsqlsce6#
值得注意的是,我还尝试了MSYS2 MINGW64环境(包
mingw-w64-x86_64-go
和mingw-w64-x86_64-binutils
),它使用MSVCRT而不是UCRT。不幸的是,它也存在同样的问题。cidc1ykv7#
我将问题的触发因素仅限制在由go生成并包含在导致内存泄漏的最终链接命令中的
go.o
文件。我只需使用gcc go.o
就可以重现这个问题。由于无法附加对象文件到这个问题,所以我将其放入了一个gist:
https://gist.github.com/Elemecca/377451208c45b80226a39456e69f2eef
jogvjijk8#
看起来在某种程度上,对
DEVPKEY_*
的未定义引用与这个问题有关。我找到了如何解决这些问题的方法,即在
devpkey.h
上方添加一个包含initguid.h
的头文件。该头文件重新定义了DEFINE_GUID
宏,以便在后续的头文件中使用时,它们实际上会定义 GUID 符号,而不仅仅是声明它们extern
。将此添加到 go 测试用例中可以解决问题 - 它现在可以在导致失控内存泄漏的相同环境中成功链接。我的实际应用程序也是如此。
更新后的 go 测试用例,不会触发失控内存泄漏
然而,问题仍然存在:即使在存在无效输入的情况下,链接器也不应该通过消耗计算机的所有 RAM 而使计算机蓝屏。