aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/vendor/github.com/google/pprof/internal/elfexec/elfexec.go
diff options
context:
space:
mode:
authorDmitri Shuralyov <dmitshur@golang.org>2021-09-08 10:51:06 -0400
committerDmitri Shuralyov <dmitshur@golang.org>2021-09-08 15:36:36 +0000
commit9581d891ab8c88bbf9e5b5142926fbca653551e6 (patch)
tree3f563b03dc6bdf9f7c8c478f682995b236ae415f /src/cmd/vendor/github.com/google/pprof/internal/elfexec/elfexec.go
parent8214257347b16a03464ace16bbcf6346fc784a3e (diff)
downloadgo-9581d891ab8c88bbf9e5b5142926fbca653551e6.tar.xz
cmd/pprof: update vendored github.com/google/pprof
Pull in the latest published version of github.com/google/pprof that is available at this time in the Go 1.18 development cycle. Done with: go get -d github.com/google/pprof@latest go mod tidy go mod vendor For #36905. Change-Id: Ib25aa38365ec70a0bed2a8a6527e5823ab9f9ded Reviewed-on: https://go-review.googlesource.com/c/go/+/348410 Trust: Dmitri Shuralyov <dmitshur@golang.org> Run-TryBot: Dmitri Shuralyov <dmitshur@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Michael Pratt <mpratt@google.com>
Diffstat (limited to 'src/cmd/vendor/github.com/google/pprof/internal/elfexec/elfexec.go')
-rw-r--r--src/cmd/vendor/github.com/google/pprof/internal/elfexec/elfexec.go96
1 files changed, 53 insertions, 43 deletions
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/elfexec/elfexec.go b/src/cmd/vendor/github.com/google/pprof/internal/elfexec/elfexec.go
index 2638b2db2d..4f11645185 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/elfexec/elfexec.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/elfexec/elfexec.go
@@ -284,10 +284,16 @@ func FindTextProgHeader(f *elf.File) *elf.ProgHeader {
return nil
}
-// ProgramHeadersForMapping returns the loadable program segment headers that
-// are fully contained in the runtime mapping with file offset pgoff and memory
-// size memsz, and if the binary includes any loadable segments.
-func ProgramHeadersForMapping(f *elf.File, pgoff, memsz uint64) ([]*elf.ProgHeader, bool) {
+// ProgramHeadersForMapping returns the program segment headers that overlap
+// the runtime mapping with file offset mapOff and memory size mapSz. We skip
+// over segments zero file size because their file offset values are unreliable.
+// Even if overlapping, a segment is not selected if its aligned file offset is
+// greater than the mapping file offset, or if the mapping includes the last
+// page of the segment, but not the full segment and the mapping includes
+// additional pages after the segment end.
+// The function returns a slice of pointers to the headers in the input
+// slice, which are valid only while phdrs is not modified or discarded.
+func ProgramHeadersForMapping(phdrs []elf.ProgHeader, mapOff, mapSz uint64) []*elf.ProgHeader {
const (
// pageSize defines the virtual memory page size used by the loader. This
// value is dependent on the memory management unit of the CPU. The page
@@ -298,57 +304,61 @@ func ProgramHeadersForMapping(f *elf.File, pgoff, memsz uint64) ([]*elf.ProgHead
// specified in the ELF file header.
pageSize = 4096
pageOffsetMask = pageSize - 1
- pageMask = ^uint64(pageOffsetMask)
)
+ mapLimit := mapOff + mapSz
var headers []*elf.ProgHeader
- hasLoadables := false
- for _, p := range f.Progs {
- // The segment must be fully included in the mapping.
- if p.Type == elf.PT_LOAD && pgoff <= p.Off && p.Off+p.Memsz <= pgoff+memsz {
- alignedOffset := uint64(0)
+ for i := range phdrs {
+ p := &phdrs[i]
+ // Skip over segments with zero file size. Their file offsets can have
+ // arbitrary values, see b/195427553.
+ if p.Filesz == 0 {
+ continue
+ }
+ segLimit := p.Off + p.Memsz
+ // The segment must overlap the mapping.
+ if p.Type == elf.PT_LOAD && mapOff < segLimit && p.Off < mapLimit {
+ // If the mapping offset is strictly less than the page aligned segment
+ // offset, then this mapping comes from a different segment, fixes
+ // b/179920361.
+ alignedSegOffset := uint64(0)
if p.Off > (p.Vaddr & pageOffsetMask) {
- alignedOffset = p.Off - (p.Vaddr & pageOffsetMask)
+ alignedSegOffset = p.Off - (p.Vaddr & pageOffsetMask)
}
- if alignedOffset <= pgoff {
- headers = append(headers, &p.ProgHeader)
+ if mapOff < alignedSegOffset {
+ continue
}
+ // If the mapping starts in the middle of the segment, it covers less than
+ // one page of the segment, and it extends at least one page past the
+ // segment, then this mapping comes from a different segment.
+ if mapOff > p.Off && (segLimit < mapOff+pageSize) && (mapLimit >= segLimit+pageSize) {
+ continue
+ }
+ headers = append(headers, p)
}
- if p.Type == elf.PT_LOAD {
- hasLoadables = true
- }
- }
- if len(headers) < 2 {
- return headers, hasLoadables
- }
-
- // If we have more than one matching segments, try a strict check on the
- // segment memory size. We use a heuristic to compute the minimum mapping size
- // required for a segment, assuming mappings are page aligned.
- // The memory size based heuristic makes sense only if the mapping size is a
- // multiple of page size.
- if memsz%pageSize != 0 {
- return headers, hasLoadables
}
+ return headers
+}
- // Return all found headers if we cannot narrow the selection to a single
- // program segment.
+// HeaderForFileOffset attempts to identify a unique program header that
+// includes the given file offset. It returns an error if it cannot identify a
+// unique header.
+func HeaderForFileOffset(headers []*elf.ProgHeader, fileOffset uint64) (*elf.ProgHeader, error) {
var ph *elf.ProgHeader
for _, h := range headers {
- wantSize := (h.Vaddr+h.Memsz+pageSize-1)&pageMask - (h.Vaddr & pageMask)
- if wantSize != memsz {
- continue
- }
- if ph != nil {
- // Found a second program header matching, so return all previously
- // identified headers.
- return headers, hasLoadables
+ if fileOffset >= h.Off && fileOffset < h.Off+h.Memsz {
+ if ph != nil {
+ // Assuming no other bugs, this can only happen if we have two or
+ // more small program segments that fit on the same page, and a
+ // segment other than the last one includes uninitialized data, or
+ // if the debug binary used for symbolization is stripped of some
+ // sections, so segment file sizes are smaller than memory sizes.
+ return nil, fmt.Errorf("found second program header (%#v) that matches file offset %x, first program header is %#v. Is this a stripped binary, or does the first program segment contain uninitialized data?", *h, fileOffset, *ph)
+ }
+ ph = h
}
- ph = h
}
if ph == nil {
- // No matching header for the strict check. Return all previously identified
- // headers.
- return headers, hasLoadables
+ return nil, fmt.Errorf("no program header matches file offset %x", fileOffset)
}
- return []*elf.ProgHeader{ph}, hasLoadables
+ return ph, nil
}