]> Cypherpunks repositories - gostls13.git/commitdiff
debug/elf: avoid using binary.Read() in NewFile()
authorPeter Collingbourne <pcc@google.com>
Wed, 13 Mar 2024 03:01:40 +0000 (20:01 -0700)
committerGopher Robot <gobot@golang.org>
Fri, 15 Mar 2024 05:04:31 +0000 (05:04 +0000)
With this change my test program that reads a tree of ELF files runs
1.71 ± 0.12 times faster without parallelism or 1.39 ± 0.06 times
faster using 8 goroutines.

Change-Id: I443d1a02736f16f5532ef28e1447c97aa87c7126
Reviewed-on: https://go-review.googlesource.com/c/go/+/571436
Auto-Submit: Ian Lance Taylor <iant@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
src/debug/elf/file.go

index 7228447c2128fec48dbd9b8aaf23c6bdb51b0377..6b4de0c56ef3b9c26c44033e1d3276bf4b5a8d31 100644 (file)
@@ -27,6 +27,7 @@ import (
        "io"
        "os"
        "strings"
+       "unsafe"
 )
 
 // TODO: error reporting detail
@@ -296,14 +297,16 @@ func NewFile(r io.ReaderAt) (*File, error) {
        }
 
        f.Data = Data(ident[EI_DATA])
+       var bo binary.ByteOrder
        switch f.Data {
        case ELFDATA2LSB:
-               f.ByteOrder = binary.LittleEndian
+               bo = binary.LittleEndian
        case ELFDATA2MSB:
-               f.ByteOrder = binary.BigEndian
+               bo = binary.BigEndian
        default:
                return nil, &FormatError{0, "unknown ELF data encoding", f.Data}
        }
+       f.ByteOrder = bo
 
        f.Version = Version(ident[EI_VERSION])
        if f.Version != EV_CURRENT {
@@ -320,43 +323,43 @@ func NewFile(r io.ReaderAt) (*File, error) {
        var shentsize, shnum, shstrndx int
        switch f.Class {
        case ELFCLASS32:
-               hdr := new(Header32)
-               sr.Seek(0, io.SeekStart)
-               if err := binary.Read(sr, f.ByteOrder, hdr); err != nil {
+               var hdr Header32
+               data := make([]byte, unsafe.Sizeof(hdr))
+               if _, err := sr.ReadAt(data, 0); err != nil {
                        return nil, err
                }
-               f.Type = Type(hdr.Type)
-               f.Machine = Machine(hdr.Machine)
-               f.Entry = uint64(hdr.Entry)
-               if v := Version(hdr.Version); v != f.Version {
+               f.Type = Type(bo.Uint16(data[unsafe.Offsetof(hdr.Type):]))
+               f.Machine = Machine(bo.Uint16(data[unsafe.Offsetof(hdr.Machine):]))
+               f.Entry = uint64(bo.Uint32(data[unsafe.Offsetof(hdr.Entry):]))
+               if v := Version(bo.Uint32(data[unsafe.Offsetof(hdr.Version):])); v != f.Version {
                        return nil, &FormatError{0, "mismatched ELF version", v}
                }
-               phoff = int64(hdr.Phoff)
-               phentsize = int(hdr.Phentsize)
-               phnum = int(hdr.Phnum)
-               shoff = int64(hdr.Shoff)
-               shentsize = int(hdr.Shentsize)
-               shnum = int(hdr.Shnum)
-               shstrndx = int(hdr.Shstrndx)
+               phoff = int64(bo.Uint32(data[unsafe.Offsetof(hdr.Phoff):]))
+               phentsize = int(bo.Uint16(data[unsafe.Offsetof(hdr.Phentsize):]))
+               phnum = int(bo.Uint16(data[unsafe.Offsetof(hdr.Phnum):]))
+               shoff = int64(bo.Uint32(data[unsafe.Offsetof(hdr.Shoff):]))
+               shentsize = int(bo.Uint16(data[unsafe.Offsetof(hdr.Shentsize):]))
+               shnum = int(bo.Uint16(data[unsafe.Offsetof(hdr.Shnum):]))
+               shstrndx = int(bo.Uint16(data[unsafe.Offsetof(hdr.Shstrndx):]))
        case ELFCLASS64:
-               hdr := new(Header64)
-               sr.Seek(0, io.SeekStart)
-               if err := binary.Read(sr, f.ByteOrder, hdr); err != nil {
+               var hdr Header64
+               data := make([]byte, unsafe.Sizeof(hdr))
+               if _, err := sr.ReadAt(data, 0); err != nil {
                        return nil, err
                }
-               f.Type = Type(hdr.Type)
-               f.Machine = Machine(hdr.Machine)
-               f.Entry = hdr.Entry
-               if v := Version(hdr.Version); v != f.Version {
+               f.Type = Type(bo.Uint16(data[unsafe.Offsetof(hdr.Type):]))
+               f.Machine = Machine(bo.Uint16(data[unsafe.Offsetof(hdr.Machine):]))
+               f.Entry = bo.Uint64(data[unsafe.Offsetof(hdr.Entry):])
+               if v := Version(bo.Uint32(data[unsafe.Offsetof(hdr.Version):])); v != f.Version {
                        return nil, &FormatError{0, "mismatched ELF version", v}
                }
-               phoff = int64(hdr.Phoff)
-               phentsize = int(hdr.Phentsize)
-               phnum = int(hdr.Phnum)
-               shoff = int64(hdr.Shoff)
-               shentsize = int(hdr.Shentsize)
-               shnum = int(hdr.Shnum)
-               shstrndx = int(hdr.Shstrndx)
+               phoff = int64(bo.Uint64(data[unsafe.Offsetof(hdr.Phoff):]))
+               phentsize = int(bo.Uint16(data[unsafe.Offsetof(hdr.Phentsize):]))
+               phnum = int(bo.Uint16(data[unsafe.Offsetof(hdr.Phnum):]))
+               shoff = int64(bo.Uint64(data[unsafe.Offsetof(hdr.Shoff):]))
+               shentsize = int(bo.Uint16(data[unsafe.Offsetof(hdr.Shentsize):]))
+               shnum = int(bo.Uint16(data[unsafe.Offsetof(hdr.Shnum):]))
+               shstrndx = int(bo.Uint16(data[unsafe.Offsetof(hdr.Shstrndx):]))
        }
 
        if shoff < 0 {
@@ -389,47 +392,44 @@ func NewFile(r io.ReaderAt) (*File, error) {
 
        // Read program headers
        f.Progs = make([]*Prog, phnum)
+       phdata, err := saferio.ReadDataAt(sr, uint64(phnum)*uint64(phentsize), phoff)
+       if err != nil {
+               return nil, err
+       }
        for i := 0; i < phnum; i++ {
-               off := phoff + int64(i)*int64(phentsize)
-               sr.Seek(off, io.SeekStart)
+               off := uintptr(i) * uintptr(phentsize)
                p := new(Prog)
                switch f.Class {
                case ELFCLASS32:
-                       ph := new(Prog32)
-                       if err := binary.Read(sr, f.ByteOrder, ph); err != nil {
-                               return nil, err
-                       }
+                       var ph Prog32
                        p.ProgHeader = ProgHeader{
-                               Type:   ProgType(ph.Type),
-                               Flags:  ProgFlag(ph.Flags),
-                               Off:    uint64(ph.Off),
-                               Vaddr:  uint64(ph.Vaddr),
-                               Paddr:  uint64(ph.Paddr),
-                               Filesz: uint64(ph.Filesz),
-                               Memsz:  uint64(ph.Memsz),
-                               Align:  uint64(ph.Align),
+                               Type:   ProgType(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Type):])),
+                               Flags:  ProgFlag(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Flags):])),
+                               Off:    uint64(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Off):])),
+                               Vaddr:  uint64(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Vaddr):])),
+                               Paddr:  uint64(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Paddr):])),
+                               Filesz: uint64(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Filesz):])),
+                               Memsz:  uint64(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Memsz):])),
+                               Align:  uint64(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Align):])),
                        }
                case ELFCLASS64:
-                       ph := new(Prog64)
-                       if err := binary.Read(sr, f.ByteOrder, ph); err != nil {
-                               return nil, err
-                       }
+                       var ph Prog64
                        p.ProgHeader = ProgHeader{
-                               Type:   ProgType(ph.Type),
-                               Flags:  ProgFlag(ph.Flags),
-                               Off:    ph.Off,
-                               Vaddr:  ph.Vaddr,
-                               Paddr:  ph.Paddr,
-                               Filesz: ph.Filesz,
-                               Memsz:  ph.Memsz,
-                               Align:  ph.Align,
+                               Type:   ProgType(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Type):])),
+                               Flags:  ProgFlag(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Flags):])),
+                               Off:    bo.Uint64(phdata[off+unsafe.Offsetof(ph.Off):]),
+                               Vaddr:  bo.Uint64(phdata[off+unsafe.Offsetof(ph.Vaddr):]),
+                               Paddr:  bo.Uint64(phdata[off+unsafe.Offsetof(ph.Paddr):]),
+                               Filesz: bo.Uint64(phdata[off+unsafe.Offsetof(ph.Filesz):]),
+                               Memsz:  bo.Uint64(phdata[off+unsafe.Offsetof(ph.Memsz):]),
+                               Align:  bo.Uint64(phdata[off+unsafe.Offsetof(ph.Align):]),
                        }
                }
                if int64(p.Off) < 0 {
-                       return nil, &FormatError{off, "invalid program header offset", p.Off}
+                       return nil, &FormatError{phoff + int64(off), "invalid program header offset", p.Off}
                }
                if int64(p.Filesz) < 0 {
-                       return nil, &FormatError{off, "invalid program header file size", p.Filesz}
+                       return nil, &FormatError{phoff + int64(off), "invalid program header file size", p.Filesz}
                }
                p.sr = io.NewSectionReader(r, int64(p.Off), int64(p.Filesz))
                p.ReaderAt = p.sr
@@ -446,7 +446,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
                switch f.Class {
                case ELFCLASS32:
                        sh := new(Section32)
-                       if err := binary.Read(sr, f.ByteOrder, sh); err != nil {
+                       if err := binary.Read(sr, bo, sh); err != nil {
                                return nil, err
                        }
                        shnum = int(sh.Size)
@@ -454,7 +454,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
                        link = sh.Link
                case ELFCLASS64:
                        sh := new(Section64)
-                       if err := binary.Read(sr, f.ByteOrder, sh); err != nil {
+                       if err := binary.Read(sr, bo, sh); err != nil {
                                return nil, err
                        }
                        shnum = int(sh.Size)
@@ -493,51 +493,48 @@ func NewFile(r io.ReaderAt) (*File, error) {
        }
        f.Sections = make([]*Section, 0, c)
        names := make([]uint32, 0, c)
+       shdata, err := saferio.ReadDataAt(sr, uint64(shnum)*uint64(shentsize), shoff)
+       if err != nil {
+               return nil, err
+       }
        for i := 0; i < shnum; i++ {
-               off := shoff + int64(i)*int64(shentsize)
-               sr.Seek(off, io.SeekStart)
+               off := uintptr(i) * uintptr(shentsize)
                s := new(Section)
                switch f.Class {
                case ELFCLASS32:
-                       sh := new(Section32)
-                       if err := binary.Read(sr, f.ByteOrder, sh); err != nil {
-                               return nil, err
-                       }
-                       names = append(names, sh.Name)
+                       var sh Section32
+                       names = append(names, bo.Uint32(shdata[off+unsafe.Offsetof(sh.Name):]))
                        s.SectionHeader = SectionHeader{
-                               Type:      SectionType(sh.Type),
-                               Flags:     SectionFlag(sh.Flags),
-                               Addr:      uint64(sh.Addr),
-                               Offset:    uint64(sh.Off),
-                               FileSize:  uint64(sh.Size),
-                               Link:      sh.Link,
-                               Info:      sh.Info,
-                               Addralign: uint64(sh.Addralign),
-                               Entsize:   uint64(sh.Entsize),
+                               Type:      SectionType(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Type):])),
+                               Flags:     SectionFlag(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Flags):])),
+                               Addr:      uint64(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Addr):])),
+                               Offset:    uint64(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Off):])),
+                               FileSize:  uint64(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Size):])),
+                               Link:      bo.Uint32(shdata[off+unsafe.Offsetof(sh.Link):]),
+                               Info:      bo.Uint32(shdata[off+unsafe.Offsetof(sh.Info):]),
+                               Addralign: uint64(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Addralign):])),
+                               Entsize:   uint64(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Entsize):])),
                        }
                case ELFCLASS64:
-                       sh := new(Section64)
-                       if err := binary.Read(sr, f.ByteOrder, sh); err != nil {
-                               return nil, err
-                       }
-                       names = append(names, sh.Name)
+                       var sh Section64
+                       names = append(names, bo.Uint32(shdata[off+unsafe.Offsetof(sh.Name):]))
                        s.SectionHeader = SectionHeader{
-                               Type:      SectionType(sh.Type),
-                               Flags:     SectionFlag(sh.Flags),
-                               Offset:    sh.Off,
-                               FileSize:  sh.Size,
-                               Addr:      sh.Addr,
-                               Link:      sh.Link,
-                               Info:      sh.Info,
-                               Addralign: sh.Addralign,
-                               Entsize:   sh.Entsize,
+                               Type:      SectionType(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Type):])),
+                               Flags:     SectionFlag(bo.Uint64(shdata[off+unsafe.Offsetof(sh.Flags):])),
+                               Offset:    bo.Uint64(shdata[off+unsafe.Offsetof(sh.Off):]),
+                               FileSize:  bo.Uint64(shdata[off+unsafe.Offsetof(sh.Size):]),
+                               Addr:      bo.Uint64(shdata[off+unsafe.Offsetof(sh.Addr):]),
+                               Link:      bo.Uint32(shdata[off+unsafe.Offsetof(sh.Link):]),
+                               Info:      bo.Uint32(shdata[off+unsafe.Offsetof(sh.Info):]),
+                               Addralign: bo.Uint64(shdata[off+unsafe.Offsetof(sh.Addralign):]),
+                               Entsize:   bo.Uint64(shdata[off+unsafe.Offsetof(sh.Entsize):]),
                        }
                }
                if int64(s.Offset) < 0 {
-                       return nil, &FormatError{off, "invalid section offset", int64(s.Offset)}
+                       return nil, &FormatError{shoff + int64(off), "invalid section offset", int64(s.Offset)}
                }
                if int64(s.FileSize) < 0 {
-                       return nil, &FormatError{off, "invalid section size", int64(s.FileSize)}
+                       return nil, &FormatError{shoff + int64(off), "invalid section size", int64(s.FileSize)}
                }
                s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.FileSize))
 
@@ -548,23 +545,25 @@ func NewFile(r io.ReaderAt) (*File, error) {
                        // Read the compression header.
                        switch f.Class {
                        case ELFCLASS32:
-                               ch := new(Chdr32)
-                               if err := binary.Read(s.sr, f.ByteOrder, ch); err != nil {
+                               var ch Chdr32
+                               chdata := make([]byte, unsafe.Sizeof(ch))
+                               if _, err := s.sr.ReadAt(chdata, 0); err != nil {
                                        return nil, err
                                }
-                               s.compressionType = CompressionType(ch.Type)
-                               s.Size = uint64(ch.Size)
-                               s.Addralign = uint64(ch.Addralign)
-                               s.compressionOffset = int64(binary.Size(ch))
+                               s.compressionType = CompressionType(bo.Uint32(chdata[unsafe.Offsetof(ch.Type):]))
+                               s.Size = uint64(bo.Uint32(chdata[unsafe.Offsetof(ch.Size):]))
+                               s.Addralign = uint64(bo.Uint32(chdata[unsafe.Offsetof(ch.Addralign):]))
+                               s.compressionOffset = int64(unsafe.Sizeof(ch))
                        case ELFCLASS64:
-                               ch := new(Chdr64)
-                               if err := binary.Read(s.sr, f.ByteOrder, ch); err != nil {
+                               var ch Chdr64
+                               chdata := make([]byte, unsafe.Sizeof(ch))
+                               if _, err := s.sr.ReadAt(chdata, 0); err != nil {
                                        return nil, err
                                }
-                               s.compressionType = CompressionType(ch.Type)
-                               s.Size = ch.Size
-                               s.Addralign = ch.Addralign
-                               s.compressionOffset = int64(binary.Size(ch))
+                               s.compressionType = CompressionType(bo.Uint32(chdata[unsafe.Offsetof(ch.Type):]))
+                               s.Size = bo.Uint64(chdata[unsafe.Offsetof(ch.Size):])
+                               s.Addralign = bo.Uint64(chdata[unsafe.Offsetof(ch.Addralign):])
+                               s.compressionOffset = int64(unsafe.Sizeof(ch))
                        }
                }