Repository navigation
fix(tls): read a stripped binary's function table through the open file - #362
mayankpande88 wants to merge 2 commits into
Conversation
A stripped Go binary's symbols come only from .gopclntab, and openGoFuncTable read it by reopening the path, a /proc/<pid>/exe link. When the process exited between the ELF open and that reopen, its crypto/tls functions were "not found". LookupSymbols caches that per binary and AttachGoTlsUprobes caches no_symbols, so every later process of the binary went unprobed until the entries were evicted. Short-lived processes of a stripped binary, the first of which is often gone before the lookup finishes, hit this. In a local run where attaching was slower, 450 attaches of the short-lived test client returned no_symbols. ELFFile now keeps the opened file and the table is mapped from it. A symbol is also no longer recorded as missing when the table failed to load for another reason.
There was a problem hiding this comment.
Code Review
This pull request modifies the ELF tracer to keep the binary file descriptor open in the ELFFile struct instead of reopening the file path when reading the Go function table. This ensures that symbols can still be resolved even if the target process exits and its path becomes unavailable. Feedback suggests adding GOOS=linux to the test build environment in TestGetSymbol_StrippedGoBinaryAfterPathIsGone to prevent test failures on non-Linux development platforms.
| bin := filepath.Join(t.TempDir(), "stripped") | ||
| cmd := exec.Command(goBin, "build", "-ldflags=-s -w", "-o", bin, ".") | ||
| cmd.Dir = src | ||
| cmd.Env = append(os.Environ(), "CGO_ENABLED=0") |
There was a problem hiding this comment.
On non-Linux development platforms (such as macOS or Windows), running this test will fail because go build without GOOS=linux produces a native binary (Mach-O or PE) which OpenELFFile cannot parse as an ELF file.
Adding GOOS=linux to the environment ensures that an ELF binary is always built, allowing the test to run successfully on all platforms.
| cmd.Env = append(os.Environ(), "CGO_ENABLED=0") | |
| cmd.Env = append(os.Environ(), "GOOS=linux", "CGO_ENABLED=0") |
There was a problem hiding this comment.
Applied in 89979b4: the test binary is built with GOOS=linux.
|
Folded into #364 with the rest of this stack, so it can be reviewed and merged as one unit (squash-only merges made each stacked merge conflict). The commit is unchanged there; its description and review thread here stay as the detailed write-up. |
Summary
A stripped Go binary (
-ldflags="-s -w", the usual production build) has no ELF symbol table. Its functions are found through.gopclntab, whichopenGoFuncTableread by reopening the binary's path, a/proc/<pid>/exelink.When the process exited between the ELF open and that reopen, its
crypto/tlsfunctions came back "not found".LookupSymbolscaches that per binary, andAttachGoTlsUprobesthen cachesno_symbols, so every later process of the binary went unprobed until the cache evicted it. The first process of a short-lived stripped binary is often gone before the lookup finishes.ELFFilekeeps the file it opened, and the function table is mapped from that descriptor. The path is never reopened.Stacked on #359.
Engineering detail
TestGetSymbol_StrippedGoBinaryAfterPathIsGonebuilds a stripped binary, opens it, removes the file, then looks up the TLS functions. It fails on the parent commit (no symbols found) and passes here.no_symbols, and the binary stayed unprobed after that.Local e2e: built agent images with and without this change and ran them in a local Docker VM (kernel 6.10). The local short-lived-client scenario (391 TLS clients of one Go binary) captured 390 of 390 with this branch, which includes #359's probe fix. The
no_symbolsresults seen while attaching was slow are gone. The unit test above fails without the change. golangci-lint passes.