debug/elf: avoid using binary.Read() in NewFile()

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>
This commit is contained in:
Peter Collingbourne 2024-03-12 20:01:40 -07:00 committed by Gopher Robot
parent a29c30f620
commit c9ed561db4

View File

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