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/link: corrupted pclntab created by binutils strip for PIE binary #67261

Closed
thanm opened this issue May 8, 2024 · 2 comments
Closed

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

thanm opened this issue May 8, 2024 · 2 comments
Assignees
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. release-blocker
Milestone

Comments

@thanm
Copy link
Contributor

thanm commented May 8, 2024

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.

@thanm thanm added NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. release-blocker labels May 8, 2024
@thanm thanm added this to the Go1.23 milestone May 8, 2024
@thanm thanm self-assigned this May 8, 2024
@gopherbot gopherbot added the compiler/runtime Issues related to the Go compiler and/or runtime. label May 8, 2024
@ianlancetaylor
Copy link
Contributor

Nice job finding that.

Just a note that the messages

strip: bad.exe: section .got lma 0x165c728 adjusted to 0x165c880

are actually error messages. It's a long-standing bug in GNU strip and objcopy that certain kinds of error messages don't cause a non-zero exit status.

Looking at the section headers of the unstripped file, they are kind of weird. .data.rel.ro starts at address 0x13bf000 and has size 0x29c890, meaning that it ends at address 0x165b890. But .got starts at 0x165b748 and .dynamic starts at 0x165b760. That puts the .got and .dynamic sections inside the .data.rel.ro section. That is probably what is confusing GNU strip.

@gopherbot
Copy link

Change https://go.dev/cl/584595 mentions this issue: cmd/link/internal/ld: fix overlapping sections in ELF relro links

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. release-blocker
Projects
Development

No branches or pull requests

3 participants