Skip to content

Commit c9ed561

Browse files
pccgopherbot
authored andcommitted
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 <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent a29c30f commit c9ed561

File tree

1 file changed

+103
-104
lines changed

1 file changed

+103
-104
lines changed

src/debug/elf/file.go

Lines changed: 103 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"io"
2828
"os"
2929
"strings"
30+
"unsafe"
3031
)
3132

3233
// TODO: error reporting detail
@@ -296,14 +297,16 @@ func NewFile(r io.ReaderAt) (*File, error) {
296297
}
297298

298299
f.Data = Data(ident[EI_DATA])
300+
var bo binary.ByteOrder
299301
switch f.Data {
300302
case ELFDATA2LSB:
301-
f.ByteOrder = binary.LittleEndian
303+
bo = binary.LittleEndian
302304
case ELFDATA2MSB:
303-
f.ByteOrder = binary.BigEndian
305+
bo = binary.BigEndian
304306
default:
305307
return nil, &FormatError{0, "unknown ELF data encoding", f.Data}
306308
}
309+
f.ByteOrder = bo
307310

308311
f.Version = Version(ident[EI_VERSION])
309312
if f.Version != EV_CURRENT {
@@ -320,43 +323,43 @@ func NewFile(r io.ReaderAt) (*File, error) {
320323
var shentsize, shnum, shstrndx int
321324
switch f.Class {
322325
case ELFCLASS32:
323-
hdr := new(Header32)
324-
sr.Seek(0, io.SeekStart)
325-
if err := binary.Read(sr, f.ByteOrder, hdr); err != nil {
326+
var hdr Header32
327+
data := make([]byte, unsafe.Sizeof(hdr))
328+
if _, err := sr.ReadAt(data, 0); err != nil {
326329
return nil, err
327330
}
328-
f.Type = Type(hdr.Type)
329-
f.Machine = Machine(hdr.Machine)
330-
f.Entry = uint64(hdr.Entry)
331-
if v := Version(hdr.Version); v != f.Version {
331+
f.Type = Type(bo.Uint16(data[unsafe.Offsetof(hdr.Type):]))
332+
f.Machine = Machine(bo.Uint16(data[unsafe.Offsetof(hdr.Machine):]))
333+
f.Entry = uint64(bo.Uint32(data[unsafe.Offsetof(hdr.Entry):]))
334+
if v := Version(bo.Uint32(data[unsafe.Offsetof(hdr.Version):])); v != f.Version {
332335
return nil, &FormatError{0, "mismatched ELF version", v}
333336
}
334-
phoff = int64(hdr.Phoff)
335-
phentsize = int(hdr.Phentsize)
336-
phnum = int(hdr.Phnum)
337-
shoff = int64(hdr.Shoff)
338-
shentsize = int(hdr.Shentsize)
339-
shnum = int(hdr.Shnum)
340-
shstrndx = int(hdr.Shstrndx)
337+
phoff = int64(bo.Uint32(data[unsafe.Offsetof(hdr.Phoff):]))
338+
phentsize = int(bo.Uint16(data[unsafe.Offsetof(hdr.Phentsize):]))
339+
phnum = int(bo.Uint16(data[unsafe.Offsetof(hdr.Phnum):]))
340+
shoff = int64(bo.Uint32(data[unsafe.Offsetof(hdr.Shoff):]))
341+
shentsize = int(bo.Uint16(data[unsafe.Offsetof(hdr.Shentsize):]))
342+
shnum = int(bo.Uint16(data[unsafe.Offsetof(hdr.Shnum):]))
343+
shstrndx = int(bo.Uint16(data[unsafe.Offsetof(hdr.Shstrndx):]))
341344
case ELFCLASS64:
342-
hdr := new(Header64)
343-
sr.Seek(0, io.SeekStart)
344-
if err := binary.Read(sr, f.ByteOrder, hdr); err != nil {
345+
var hdr Header64
346+
data := make([]byte, unsafe.Sizeof(hdr))
347+
if _, err := sr.ReadAt(data, 0); err != nil {
345348
return nil, err
346349
}
347-
f.Type = Type(hdr.Type)
348-
f.Machine = Machine(hdr.Machine)
349-
f.Entry = hdr.Entry
350-
if v := Version(hdr.Version); v != f.Version {
350+
f.Type = Type(bo.Uint16(data[unsafe.Offsetof(hdr.Type):]))
351+
f.Machine = Machine(bo.Uint16(data[unsafe.Offsetof(hdr.Machine):]))
352+
f.Entry = bo.Uint64(data[unsafe.Offsetof(hdr.Entry):])
353+
if v := Version(bo.Uint32(data[unsafe.Offsetof(hdr.Version):])); v != f.Version {
351354
return nil, &FormatError{0, "mismatched ELF version", v}
352355
}
353-
phoff = int64(hdr.Phoff)
354-
phentsize = int(hdr.Phentsize)
355-
phnum = int(hdr.Phnum)
356-
shoff = int64(hdr.Shoff)
357-
shentsize = int(hdr.Shentsize)
358-
shnum = int(hdr.Shnum)
359-
shstrndx = int(hdr.Shstrndx)
356+
phoff = int64(bo.Uint64(data[unsafe.Offsetof(hdr.Phoff):]))
357+
phentsize = int(bo.Uint16(data[unsafe.Offsetof(hdr.Phentsize):]))
358+
phnum = int(bo.Uint16(data[unsafe.Offsetof(hdr.Phnum):]))
359+
shoff = int64(bo.Uint64(data[unsafe.Offsetof(hdr.Shoff):]))
360+
shentsize = int(bo.Uint16(data[unsafe.Offsetof(hdr.Shentsize):]))
361+
shnum = int(bo.Uint16(data[unsafe.Offsetof(hdr.Shnum):]))
362+
shstrndx = int(bo.Uint16(data[unsafe.Offsetof(hdr.Shstrndx):]))
360363
}
361364

362365
if shoff < 0 {
@@ -389,47 +392,44 @@ func NewFile(r io.ReaderAt) (*File, error) {
389392

390393
// Read program headers
391394
f.Progs = make([]*Prog, phnum)
395+
phdata, err := saferio.ReadDataAt(sr, uint64(phnum)*uint64(phentsize), phoff)
396+
if err != nil {
397+
return nil, err
398+
}
392399
for i := 0; i < phnum; i++ {
393-
off := phoff + int64(i)*int64(phentsize)
394-
sr.Seek(off, io.SeekStart)
400+
off := uintptr(i) * uintptr(phentsize)
395401
p := new(Prog)
396402
switch f.Class {
397403
case ELFCLASS32:
398-
ph := new(Prog32)
399-
if err := binary.Read(sr, f.ByteOrder, ph); err != nil {
400-
return nil, err
401-
}
404+
var ph Prog32
402405
p.ProgHeader = ProgHeader{
403-
Type: ProgType(ph.Type),
404-
Flags: ProgFlag(ph.Flags),
405-
Off: uint64(ph.Off),
406-
Vaddr: uint64(ph.Vaddr),
407-
Paddr: uint64(ph.Paddr),
408-
Filesz: uint64(ph.Filesz),
409-
Memsz: uint64(ph.Memsz),
410-
Align: uint64(ph.Align),
406+
Type: ProgType(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Type):])),
407+
Flags: ProgFlag(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Flags):])),
408+
Off: uint64(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Off):])),
409+
Vaddr: uint64(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Vaddr):])),
410+
Paddr: uint64(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Paddr):])),
411+
Filesz: uint64(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Filesz):])),
412+
Memsz: uint64(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Memsz):])),
413+
Align: uint64(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Align):])),
411414
}
412415
case ELFCLASS64:
413-
ph := new(Prog64)
414-
if err := binary.Read(sr, f.ByteOrder, ph); err != nil {
415-
return nil, err
416-
}
416+
var ph Prog64
417417
p.ProgHeader = ProgHeader{
418-
Type: ProgType(ph.Type),
419-
Flags: ProgFlag(ph.Flags),
420-
Off: ph.Off,
421-
Vaddr: ph.Vaddr,
422-
Paddr: ph.Paddr,
423-
Filesz: ph.Filesz,
424-
Memsz: ph.Memsz,
425-
Align: ph.Align,
418+
Type: ProgType(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Type):])),
419+
Flags: ProgFlag(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Flags):])),
420+
Off: bo.Uint64(phdata[off+unsafe.Offsetof(ph.Off):]),
421+
Vaddr: bo.Uint64(phdata[off+unsafe.Offsetof(ph.Vaddr):]),
422+
Paddr: bo.Uint64(phdata[off+unsafe.Offsetof(ph.Paddr):]),
423+
Filesz: bo.Uint64(phdata[off+unsafe.Offsetof(ph.Filesz):]),
424+
Memsz: bo.Uint64(phdata[off+unsafe.Offsetof(ph.Memsz):]),
425+
Align: bo.Uint64(phdata[off+unsafe.Offsetof(ph.Align):]),
426426
}
427427
}
428428
if int64(p.Off) < 0 {
429-
return nil, &FormatError{off, "invalid program header offset", p.Off}
429+
return nil, &FormatError{phoff + int64(off), "invalid program header offset", p.Off}
430430
}
431431
if int64(p.Filesz) < 0 {
432-
return nil, &FormatError{off, "invalid program header file size", p.Filesz}
432+
return nil, &FormatError{phoff + int64(off), "invalid program header file size", p.Filesz}
433433
}
434434
p.sr = io.NewSectionReader(r, int64(p.Off), int64(p.Filesz))
435435
p.ReaderAt = p.sr
@@ -446,15 +446,15 @@ func NewFile(r io.ReaderAt) (*File, error) {
446446
switch f.Class {
447447
case ELFCLASS32:
448448
sh := new(Section32)
449-
if err := binary.Read(sr, f.ByteOrder, sh); err != nil {
449+
if err := binary.Read(sr, bo, sh); err != nil {
450450
return nil, err
451451
}
452452
shnum = int(sh.Size)
453453
typ = sh.Type
454454
link = sh.Link
455455
case ELFCLASS64:
456456
sh := new(Section64)
457-
if err := binary.Read(sr, f.ByteOrder, sh); err != nil {
457+
if err := binary.Read(sr, bo, sh); err != nil {
458458
return nil, err
459459
}
460460
shnum = int(sh.Size)
@@ -493,51 +493,48 @@ func NewFile(r io.ReaderAt) (*File, error) {
493493
}
494494
f.Sections = make([]*Section, 0, c)
495495
names := make([]uint32, 0, c)
496+
shdata, err := saferio.ReadDataAt(sr, uint64(shnum)*uint64(shentsize), shoff)
497+
if err != nil {
498+
return nil, err
499+
}
496500
for i := 0; i < shnum; i++ {
497-
off := shoff + int64(i)*int64(shentsize)
498-
sr.Seek(off, io.SeekStart)
501+
off := uintptr(i) * uintptr(shentsize)
499502
s := new(Section)
500503
switch f.Class {
501504
case ELFCLASS32:
502-
sh := new(Section32)
503-
if err := binary.Read(sr, f.ByteOrder, sh); err != nil {
504-
return nil, err
505-
}
506-
names = append(names, sh.Name)
505+
var sh Section32
506+
names = append(names, bo.Uint32(shdata[off+unsafe.Offsetof(sh.Name):]))
507507
s.SectionHeader = SectionHeader{
508-
Type: SectionType(sh.Type),
509-
Flags: SectionFlag(sh.Flags),
510-
Addr: uint64(sh.Addr),
511-
Offset: uint64(sh.Off),
512-
FileSize: uint64(sh.Size),
513-
Link: sh.Link,
514-
Info: sh.Info,
515-
Addralign: uint64(sh.Addralign),
516-
Entsize: uint64(sh.Entsize),
508+
Type: SectionType(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Type):])),
509+
Flags: SectionFlag(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Flags):])),
510+
Addr: uint64(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Addr):])),
511+
Offset: uint64(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Off):])),
512+
FileSize: uint64(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Size):])),
513+
Link: bo.Uint32(shdata[off+unsafe.Offsetof(sh.Link):]),
514+
Info: bo.Uint32(shdata[off+unsafe.Offsetof(sh.Info):]),
515+
Addralign: uint64(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Addralign):])),
516+
Entsize: uint64(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Entsize):])),
517517
}
518518
case ELFCLASS64:
519-
sh := new(Section64)
520-
if err := binary.Read(sr, f.ByteOrder, sh); err != nil {
521-
return nil, err
522-
}
523-
names = append(names, sh.Name)
519+
var sh Section64
520+
names = append(names, bo.Uint32(shdata[off+unsafe.Offsetof(sh.Name):]))
524521
s.SectionHeader = SectionHeader{
525-
Type: SectionType(sh.Type),
526-
Flags: SectionFlag(sh.Flags),
527-
Offset: sh.Off,
528-
FileSize: sh.Size,
529-
Addr: sh.Addr,
530-
Link: sh.Link,
531-
Info: sh.Info,
532-
Addralign: sh.Addralign,
533-
Entsize: sh.Entsize,
522+
Type: SectionType(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Type):])),
523+
Flags: SectionFlag(bo.Uint64(shdata[off+unsafe.Offsetof(sh.Flags):])),
524+
Offset: bo.Uint64(shdata[off+unsafe.Offsetof(sh.Off):]),
525+
FileSize: bo.Uint64(shdata[off+unsafe.Offsetof(sh.Size):]),
526+
Addr: bo.Uint64(shdata[off+unsafe.Offsetof(sh.Addr):]),
527+
Link: bo.Uint32(shdata[off+unsafe.Offsetof(sh.Link):]),
528+
Info: bo.Uint32(shdata[off+unsafe.Offsetof(sh.Info):]),
529+
Addralign: bo.Uint64(shdata[off+unsafe.Offsetof(sh.Addralign):]),
530+
Entsize: bo.Uint64(shdata[off+unsafe.Offsetof(sh.Entsize):]),
534531
}
535532
}
536533
if int64(s.Offset) < 0 {
537-
return nil, &FormatError{off, "invalid section offset", int64(s.Offset)}
534+
return nil, &FormatError{shoff + int64(off), "invalid section offset", int64(s.Offset)}
538535
}
539536
if int64(s.FileSize) < 0 {
540-
return nil, &FormatError{off, "invalid section size", int64(s.FileSize)}
537+
return nil, &FormatError{shoff + int64(off), "invalid section size", int64(s.FileSize)}
541538
}
542539
s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.FileSize))
543540

@@ -548,23 +545,25 @@ func NewFile(r io.ReaderAt) (*File, error) {
548545
// Read the compression header.
549546
switch f.Class {
550547
case ELFCLASS32:
551-
ch := new(Chdr32)
552-
if err := binary.Read(s.sr, f.ByteOrder, ch); err != nil {
548+
var ch Chdr32
549+
chdata := make([]byte, unsafe.Sizeof(ch))
550+
if _, err := s.sr.ReadAt(chdata, 0); err != nil {
553551
return nil, err
554552
}
555-
s.compressionType = CompressionType(ch.Type)
556-
s.Size = uint64(ch.Size)
557-
s.Addralign = uint64(ch.Addralign)
558-
s.compressionOffset = int64(binary.Size(ch))
553+
s.compressionType = CompressionType(bo.Uint32(chdata[unsafe.Offsetof(ch.Type):]))
554+
s.Size = uint64(bo.Uint32(chdata[unsafe.Offsetof(ch.Size):]))
555+
s.Addralign = uint64(bo.Uint32(chdata[unsafe.Offsetof(ch.Addralign):]))
556+
s.compressionOffset = int64(unsafe.Sizeof(ch))
559557
case ELFCLASS64:
560-
ch := new(Chdr64)
561-
if err := binary.Read(s.sr, f.ByteOrder, ch); err != nil {
558+
var ch Chdr64
559+
chdata := make([]byte, unsafe.Sizeof(ch))
560+
if _, err := s.sr.ReadAt(chdata, 0); err != nil {
562561
return nil, err
563562
}
564-
s.compressionType = CompressionType(ch.Type)
565-
s.Size = ch.Size
566-
s.Addralign = ch.Addralign
567-
s.compressionOffset = int64(binary.Size(ch))
563+
s.compressionType = CompressionType(bo.Uint32(chdata[unsafe.Offsetof(ch.Type):]))
564+
s.Size = bo.Uint64(chdata[unsafe.Offsetof(ch.Size):])
565+
s.Addralign = bo.Uint64(chdata[unsafe.Offsetof(ch.Addralign):])
566+
s.compressionOffset = int64(unsafe.Sizeof(ch))
568567
}
569568
}
570569

0 commit comments

Comments
 (0)