Skip to content

cmd/link: corrupted pclntab created by binutils strip for PIE binary #67261

Closed
@thanm

Description

@thanm

Go version:

go version devel go1.23-5f5e9f4ff1 Tue May 7 18:48:48 2024 +0000 linux/amd64

This problem happens for Go 1.23 (tip), but is not applicable for previous versions.

It appears that a recent change to the linker to enable full RELRO triggers some unpleasant behavior when Go programs are processed with "strip" (the binutils tool) for ELF targets. It should be noted that the problem only crops up for -buildmode=pie binaries.

The change in question is CL 473495, which moves the .got and .dynamic sections from writable data into .data.rel.ro when PIE buildmode is enabled (note that this change was initially rolled back and then rolled forward again in CL 571417). The resulting Go binaries execute properly, but something in the way they are constructed triggers a bug in the binutils version of strip. Here is an example to demonstrate:

$ git clone https://go.googlesource.com/tools
...
$ cd tools/gopls
$ go build -o gopls.exe -trimpath -buildmode=pie
$ strip --version
GNU strip (GNU Binutils for Debian) 2.41.90.20240122
...
$ strip -o bad.exe gopls
strip: bad.exe: section .got lma 0x165c728 adjusted to 0x165c880
strip: bad.exe: section .dynamic lma 0x165c740 adjusted to 0x165c888
strip: bad.exe: section .data.rel.ro.typelink lma 0x165c880 adjusted to 0x165c9c8
strip: bad.exe: section .data.rel.ro.itablink lma 0x1663aa0 adjusted to 0x1663bdc
strip: bad.exe: section .data.rel.ro.gosymtab lma 0x1666230 adjusted to 0x166636c
strip: bad.exe: section .data.rel.ro.gopclntab lma 0x1666240 adjusted to 0x166636c
$ ./bad.exe version
runtime: pcHeader: magic= 0x0 pad1= 112 pad2= 211 minLC= 92 ptrSize= 1 pcHeader.textStart= 0x55b1423f3000 text= 0x55b1423f3000 pluginpath=
fatal error: invalid function symbol table
runtime: panic before malloc heap initialized

$ llvm-strip-16 -o good.exe gopls
$ ./good.exe version
[golang.org/x/tools/gopls](https://www.google.com/url?q=http://golang.org/x/tools/gopls&sa=D&source=buganizer&usg=AOvVaw11zqc7TmfhOipIIJbPeAWg) (devel)
$

The failure mode here ("invalid function symbol table") is due to the fact that the pclntab has been corrupted; when the Go runtime tries to read the pclntab section header it finds that the magic string is corrupted.

Unclear as to exactly what it is that is causing binutils strip to do the wrong thing. Regardless, it would be great if we could come up with some sort of workaround (e.g. tweak our ELF generation in some way that will bypass the bug). Even if we track down the problem in strip (assuming it is indeed a bug there), Go users who run strip are likely to run into this problem.

Also worth noting that this is not an issue with external linking; if we can figure out what it is that the external linker is doing to make strip happy and do the same thing in the Go linker, that seems like it would be the best option.

Metadata

Metadata

Assignees

Labels

FrozenDueToAgeNeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.compiler/runtimeIssues related to the Go compiler and/or runtime.release-blocker

Type

No type

Projects

Status

Done

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions