From f34bdead608d06d353c7235c070707f3c7f68c79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= Date: Mon, 4 May 2026 16:48:44 +0300 Subject: [PATCH 1/2] pfelf: separate mmap reader from the underlying reader This prepares pfelf for the upcoming removal of mmap suppport. To start with, the 'elfReader' is always the underlying original reader it was handed. The mmap is now attempted at open time if possible, and used if it succeeded. This will start reducing the RSS once the Prog/Section.Data() is no longer used. PR #1393 adds a new Underlying() method to access the data without the mmap together with few users. This PR should remove the temporary RSS spikes during scanning the large segments in that PR. --- libpf/pfelf/file.go | 39 ++++++++++++++++++++++++------- libpf/pfelf/internal/mmap/mmap.go | 10 ++++++-- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/libpf/pfelf/file.go b/libpf/pfelf/file.go index 16ba04235..aaa3cf230 100644 --- a/libpf/pfelf/file.go +++ b/libpf/pfelf/file.go @@ -27,6 +27,7 @@ import ( "fmt" "hash/crc32" "io" + "os" "path/filepath" "runtime" "runtime/debug" @@ -67,6 +68,9 @@ type File struct { // elfReader is the ReadAt implementation used for this File elfReader io.ReaderAt + // mmapReader is the mmap reader for this File if available + mmapReader *mmap.ReaderAt + // ehFrame is a pointer to the PT_GNU_EH_FRAME segment of the ELF ehFrame *Prog @@ -170,7 +174,7 @@ type Section struct { // Open opens the named file using os.Open and prepares it for use as an ELF binary. func Open(name string) (*File, error) { - f, err := mmap.Open(name) + f, err := os.Open(name) if err != nil { return nil, err } @@ -185,6 +189,9 @@ func Open(name string) (*File, error) { // Close closes the File. func (f *File) Close() (err error) { + if f.mmapReader != nil { + f.mmapReader.Close() + } if f.closer != nil { err = f.closer.Close() f.closer = nil @@ -235,6 +242,11 @@ func newFile(r io.ReaderAt, closer io.Closer, return nil, err } + if osFile, ok := r.(*os.File); ok { + // Attempt to mmap the file if possible + f.mmapReader, _ = mmap.OpenFile(osFile) + } + f.Progs = make([]Prog, hdr.Phnum) virtualBase := ^uint64(0) numROData := 0 @@ -251,7 +263,7 @@ func newFile(r io.ReaderAt, closer io.Closer, Memsz: ph.Memsz, Align: ph.Align, } - p.elfReader = r + p.elfReader = f.getReader() if p.Type == elf.PT_LOAD { if p.Vaddr < virtualBase { @@ -362,12 +374,21 @@ func (_ NoMmapCloser) Close() error { // keep slices returned by Section.Data() and Prog.Data() after File has been // GCd. The returned Close() will release the reference on data. func (f *File) Take() io.Closer { - if mapping, ok := f.elfReader.(*mmap.ReaderAt); ok { - return mapping.Take() + if f.mmapReader != nil { + return f.mmapReader.Take() } return NoMmapCloser{} } +// getReader returns the mmap reader if available, or otherwise the underlying +// reader (typically os.File). +func (f *File) getReader() io.ReaderAt { + if f.mmapReader != nil { + return f.mmapReader + } + return f.elfReader +} + // LoadSections loads the ELF file sections func (f *File) LoadSections() error { if f.InsideCore { @@ -412,7 +433,7 @@ func (f *File) LoadSections() error { Entsize: sh.Entsize, FileSize: sh.Size, } - s.elfReader = f.elfReader + s.elfReader = f.getReader() } // Load the section name string table @@ -546,7 +567,7 @@ func (f *File) EHFrame() (*Prog, error) { Memsz: ph.Memsz - offs, Align: ph.Align, }, - elfReader: f.elfReader, + elfReader: f.getReader(), }, nil } return nil, errors.New("no PT_LOAD segment for PT_GNU_EH_FRAME found") @@ -597,7 +618,7 @@ func (f *File) GoVersion() (string, error) { if !f.IsGolang() { return "", nil } - bi, err := buildinfo.Read(f.elfReader) + bi, err := buildinfo.Read(f.getReader()) if err != nil { return "", err } @@ -954,8 +975,8 @@ func (sh *Section) Data(maxSize uint) ([]byte, error) { // SetDontNeed sets the flag MADV_DONTNEED on the mmapped data. func (f *File) SetDontNeed() { - if mapping, ok := f.elfReader.(*mmap.ReaderAt); ok { - if err := mapping.SetMadvDontNeed(); err != nil { + if f.mmapReader != nil { + if err := f.mmapReader.SetMadvDontNeed(); err != nil { log.Errorf("Failed to set MADV_DONTNEED: %v", err) } } diff --git a/libpf/pfelf/internal/mmap/mmap.go b/libpf/pfelf/internal/mmap/mmap.go index fb648078b..4edcb7790 100644 --- a/libpf/pfelf/internal/mmap/mmap.go +++ b/libpf/pfelf/internal/mmap/mmap.go @@ -90,6 +90,12 @@ func Open(filename string) (*ReaderAt, error) { return nil, err } defer f.Close() + + return OpenFile(f) +} + +// Open memory-maps the OS file for reading. +func OpenFile(f *os.File) (*ReaderAt, error) { fi, err := f.Stat() if err != nil { return nil, err @@ -107,10 +113,10 @@ func Open(filename string) (*ReaderAt, error) { }, nil } if size < 0 { - return nil, fmt.Errorf("mmap: file %q has negative size", filename) + return nil, fmt.Errorf("mmap: negative file size") } if size != int64(int(size)) { - return nil, fmt.Errorf("mmap: file %q is too large", filename) + return nil, fmt.Errorf("mmap: too large file size") } data, err := syscall.Mmap(int(f.Fd()), 0, int(size), syscall.PROT_READ, syscall.MAP_SHARED) From 08a7892ee951f623e1e1974a5957d53ec2f98f0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= Date: Tue, 5 May 2026 12:06:37 +0300 Subject: [PATCH 2/2] Update libpf/pfelf/internal/mmap/mmap.go Co-authored-by: Christos Kalkanis --- libpf/pfelf/internal/mmap/mmap.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libpf/pfelf/internal/mmap/mmap.go b/libpf/pfelf/internal/mmap/mmap.go index 4edcb7790..984fc8e4d 100644 --- a/libpf/pfelf/internal/mmap/mmap.go +++ b/libpf/pfelf/internal/mmap/mmap.go @@ -94,7 +94,7 @@ func Open(filename string) (*ReaderAt, error) { return OpenFile(f) } -// Open memory-maps the OS file for reading. +// OpenFile memory-maps the OS file for reading. func OpenFile(f *os.File) (*ReaderAt, error) { fi, err := f.Stat() if err != nil {