-
Notifications
You must be signed in to change notification settings - Fork 17.8k
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/go: buildmode=pie broken with -linkmode=internal and cgo #18968
Comments
@ianlancetaylor, @crawshaw, is this a Go 1.8 worry? |
I'm a little worried. Will prod at it a bit tonight. Any chance that running one of these tests under gdb gives you a useful traceback? |
This is the backtrace I see for package "context": $ go test -buildmode=pie -c context |
I did a bisection for package "context". This change somehow broke -buildmode=pie for this package. Maybe that's a hint (a local type implementing an interface it didn't before). Did another bisect for package "encoding/gob". |
From the stack trace it looks like the dynamic loader cannot even get the Go binary started. That is, it's not the generated Go code or the assembly that is the problem but something in the binary format itself, probably a bad relocation of some kind. /cc @ianlancetaylor in case this is obvious. |
It works in 1.7 because it uses external linking. It fails in 1.8 because it uses internal linking. |
Should -buildmode=pie imply external linking? |
Looks like it was re-enabled again. Now trying to figure out whether it was always broken or whether it broke sometime after that. The specific problem is that the offset field of the jump slot relocations is always zero. |
Internal linking of PIE should be off by default. If it got turned back on by one of my other changes, that was a mistake. |
It was turned on by https://golang.org/cl/28971. |
I think for 1.8 we should have it off unless -linkmode is set explicitly. (I'll send a CL in a moment.) |
CL https://golang.org/cl/36417 mentions this issue. |
OK, CL 36417 does the obvious conservative thing for 1.8 and defaults to external linking. But let's keep this bug opened for 1.9. |
Now `go test -buildmode=pie std -short` passes on linux/amd64. Updates #18968 Change-Id: Ide21877713e00edc64c1700c950016d6bff8de0e Reviewed-on: https://go-review.googlesource.com/36417 Reviewed-by: Ian Lance Taylor <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
CL https://golang.org/cl/36421 mentions this issue. |
Now `go test -buildmode=pie std -short` passes on linux/amd64. Updates #18968 Change-Id: Ide21877713e00edc64c1700c950016d6bff8de0e Reviewed-on: https://go-review.googlesource.com/36417 Reviewed-by: Ian Lance Taylor <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-on: https://go-review.googlesource.com/36421 Run-TryBot: Russ Cox <[email protected]> Reviewed-by: David Crawshaw <[email protected]> Reviewed-by: Minux Ma <[email protected]>
@crawshaw I didn't try to analyze it, beyond observing that the offset for the JMP_SLOT relocations is zero. The bug occurs when using internal linking with cgo. That is, the internal linker does not correctly handle some ELF relocations. |
This is not an ongoing concern until we try to push PIE builds back to internal linking. Perhaps we can add a test that go test net -linkmode=pie works on linux/amd64 and close this issue. |
CL https://golang.org/cl/46426 mentions this issue. |
ld.addpltsym adds an R_X86_64_JMP_SLOT dynamic relocation to .rela.plt and uses Addaddrplus to reference the GOT in Elf64_Rela.r_offset. Addaddrplus results in an R_ADDR relocation, which here we transform into an R_X86_64_64 dynamic relocation. This is wrong for several reasons: 1. .rela.plt is not a writable, relro section. It is mapped read-only, causing the dynamic linker to segfault when it tried to handle the relocation. This was the immediate cause of internal PIE cgo crashes. 2. Relocations targetting other reloc sections are, as far as I can tell, undefined behavior in the ELF spec and are unlikely to be a good idea. 3. Even if the relocation did work, it isn't what we want. The relocation, if successfully handled, would have put an absolute address as the JMP_SLOT offset, but it should be the offset from the beginning of the binary, just like any other relocation. What we want is a statically resolved R_ADDR relocation, just as is used below for the R_X86_64_64 relocation. Skipping the .rela.plt allows reloc() to handle these R_ADDR relocations. With this CL, internal PIE cgo binaries work. Fixes golang#18968 Change-Id: Ie74e6fe249e88150baa0e340b1cb128cf7f28673
CL https://golang.org/cl/47837 mentions this issue. |
ld.addpltsym adds an R_X86_64_JMP_SLOT dynamic relocation to .rela.plt and uses Addaddrplus to reference the GOT in Elf64_Rela.r_offset. Addaddrplus results in an R_ADDR relocation, which here we transform into an R_X86_64_64 dynamic relocation. This is wrong for several reasons: 1. .rela.plt is not a writable, relro section. It is mapped read-only, causing the dynamic linker to segfault when it tried to handle the relocation. This was the immediate cause of internal PIE cgo crashes. 2. Relocations targetting other reloc sections are, as far as I can tell, undefined behavior in the ELF spec and are unlikely to be a good idea. 3. Even if the relocation did work, it isn't what we want. The relocation, if successfully handled, would have put an absolute address as the JMP_SLOT offset, but it should be the offset from the beginning of the binary, just like any other relocation. What we want is a statically resolved R_ADDR relocation, just as is used below for the R_X86_64_64 relocation. Skipping the .rela.plt allows reloc() to handle these R_ADDR relocations. With this CL, internal PIE cgo binaries work. Updates #18968 Change-Id: Ie74e6fe249e88150baa0e340b1cb128cf7f28673 Reviewed-on: https://go-review.googlesource.com/47837 Reviewed-by: Ian Lance Taylor <[email protected]>
Change https://golang.org/cl/54790 mentions this issue: |
Change some configurations to enable the feature. Also add the test. This CL doesn't include internal linking support which is tentatively disabled due to #18968. We could do that another day. Fixes #21220 Change-Id: I601d2d78446d36332acc70be0d5b9461ac635208 Reviewed-on: https://go-review.googlesource.com/54790 Reviewed-by: Ian Lance Taylor <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
Change https://golang.org/cl/57231 mentions this issue: |
The go tool assumed that -buildmode=pie implied internal linking on linux-amd64. However, that was changed by CL 36417 for issue #18968. Fixes #21452 Change-Id: I8ed13aea52959cc5c53223f4c41ba35329445545 Reviewed-on: https://go-review.googlesource.com/57231 Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Avelino <[email protected]> Reviewed-by: Rob Pike <[email protected]>
buildmode=pie now forces external linking on all systems, so this sounds fixed. |
What version of Go are you using (
go version
)?go version go1.8rc3 linux/amd64
What did you do?
$ go test std -buildmode=pie -short
30 packages in the standard library fail with segfault. The segfault happens very early on (appears to be in the dynamic linker/loader). In some cases, simply trying to load a test binary in gdb crashes gdb.
It worked fine with go1.7, so this is a regression.
Tried this on Gentoo, Ubuntu, and Debian. Same failures on all three systems.
Also tested linux/386, linux/arm, android/arm, and android/arm64. No issues there. This is specific to amd64 (perhaps related to #18820).
Here's what the output looks like (stripping out the packages that passed):
signal: segmentation fault (core dumped)
FAIL context 0.165s
signal: segmentation fault (core dumped)
FAIL crypto/tls 0.258s
signal: segmentation fault (core dumped)
FAIL crypto/x509 0.134s
signal: segmentation fault (core dumped)
FAIL debug/elf 0.134s
signal: segmentation fault (core dumped)
FAIL encoding/gob 0.151s
signal: segmentation fault (core dumped)
FAIL encoding/json 0.212s
signal: segmentation fault (core dumped)
FAIL encoding/pem 0.172s
signal: segmentation fault (core dumped)
FAIL expvar 0.211s
signal: segmentation fault (core dumped)
FAIL log/syslog 0.152s
signal: segmentation fault (core dumped)
FAIL mime/multipart 0.145s
signal: segmentation fault (core dumped)
FAIL net 0.169s
signal: segmentation fault (core dumped)
FAIL net/http 0.272s
signal: segmentation fault (core dumped)
FAIL net/http/cgi 0.211s
signal: segmentation fault (core dumped)
FAIL net/http/cookiejar 0.221s
signal: segmentation fault (core dumped)
FAIL net/http/fcgi 0.182s
signal: segmentation fault (core dumped)
FAIL net/http/httptest 0.198s
signal: segmentation fault (core dumped)
FAIL net/http/httptrace 0.219s
signal: segmentation fault (core dumped)
FAIL net/http/httputil 0.198s
signal: segmentation fault (core dumped)
FAIL net/mail 0.146s
signal: segmentation fault (core dumped)
FAIL net/rpc 0.310s
signal: segmentation fault (core dumped)
FAIL net/rpc/jsonrpc 0.306s
signal: segmentation fault (core dumped)
FAIL net/smtp 0.170s
signal: segmentation fault (core dumped)
FAIL net/textproto 0.142s
signal: segmentation fault (core dumped)
FAIL net/url 0.201s
signal: segmentation fault (core dumped)
FAIL os/exec 0.205s
signal: segmentation fault (core dumped)
FAIL os/user 0.188s
signal: segmentation fault (core dumped)
FAIL runtime 0.206s
signal: segmentation fault (core dumped)
FAIL runtime/trace 0.147s
signal: segmentation fault (core dumped)
FAIL syscall 0.157s
signal: segmentation fault (core dumped)
FAIL vendor/golang_org/x/net/lex/httplex 0.149s
The text was updated successfully, but these errors were encountered: