...

Source file src/pkg/runtime/pprof/elf.go

     1	// Copyright 2017 The Go Authors. All rights reserved.
     2	// Use of this source code is governed by a BSD-style
     3	// license that can be found in the LICENSE file.
     4	
     5	package pprof
     6	
     7	import (
     8		"encoding/binary"
     9		"errors"
    10		"fmt"
    11		"os"
    12	)
    13	
    14	var (
    15		errBadELF    = errors.New("malformed ELF binary")
    16		errNoBuildID = errors.New("no NT_GNU_BUILD_ID found in ELF binary")
    17	)
    18	
    19	// elfBuildID returns the GNU build ID of the named ELF binary,
    20	// without introducing a dependency on debug/elf and its dependencies.
    21	func elfBuildID(file string) (string, error) {
    22		buf := make([]byte, 256)
    23		f, err := os.Open(file)
    24		if err != nil {
    25			return "", err
    26		}
    27		defer f.Close()
    28	
    29		if _, err := f.ReadAt(buf[:64], 0); err != nil {
    30			return "", err
    31		}
    32	
    33		// ELF file begins with \x7F E L F.
    34		if buf[0] != 0x7F || buf[1] != 'E' || buf[2] != 'L' || buf[3] != 'F' {
    35			return "", errBadELF
    36		}
    37	
    38		var byteOrder binary.ByteOrder
    39		switch buf[5] {
    40		default:
    41			return "", errBadELF
    42		case 1: // little-endian
    43			byteOrder = binary.LittleEndian
    44		case 2: // big-endian
    45			byteOrder = binary.BigEndian
    46		}
    47	
    48		var shnum int
    49		var shoff, shentsize int64
    50		switch buf[4] {
    51		default:
    52			return "", errBadELF
    53		case 1: // 32-bit file header
    54			shoff = int64(byteOrder.Uint32(buf[32:]))
    55			shentsize = int64(byteOrder.Uint16(buf[46:]))
    56			if shentsize != 40 {
    57				return "", errBadELF
    58			}
    59			shnum = int(byteOrder.Uint16(buf[48:]))
    60		case 2: // 64-bit file header
    61			shoff = int64(byteOrder.Uint64(buf[40:]))
    62			shentsize = int64(byteOrder.Uint16(buf[58:]))
    63			if shentsize != 64 {
    64				return "", errBadELF
    65			}
    66			shnum = int(byteOrder.Uint16(buf[60:]))
    67		}
    68	
    69		for i := 0; i < shnum; i++ {
    70			if _, err := f.ReadAt(buf[:shentsize], shoff+int64(i)*shentsize); err != nil {
    71				return "", err
    72			}
    73			if typ := byteOrder.Uint32(buf[4:]); typ != 7 { // SHT_NOTE
    74				continue
    75			}
    76			var off, size int64
    77			if shentsize == 40 {
    78				// 32-bit section header
    79				off = int64(byteOrder.Uint32(buf[16:]))
    80				size = int64(byteOrder.Uint32(buf[20:]))
    81			} else {
    82				// 64-bit section header
    83				off = int64(byteOrder.Uint64(buf[24:]))
    84				size = int64(byteOrder.Uint64(buf[32:]))
    85			}
    86			size += off
    87			for off < size {
    88				if _, err := f.ReadAt(buf[:16], off); err != nil { // room for header + name GNU\x00
    89					return "", err
    90				}
    91				nameSize := int(byteOrder.Uint32(buf[0:]))
    92				descSize := int(byteOrder.Uint32(buf[4:]))
    93				noteType := int(byteOrder.Uint32(buf[8:]))
    94				descOff := off + int64(12+(nameSize+3)&^3)
    95				off = descOff + int64((descSize+3)&^3)
    96				if nameSize != 4 || noteType != 3 || buf[12] != 'G' || buf[13] != 'N' || buf[14] != 'U' || buf[15] != '\x00' { // want name GNU\x00 type 3 (NT_GNU_BUILD_ID)
    97					continue
    98				}
    99				if descSize > len(buf) {
   100					return "", errBadELF
   101				}
   102				if _, err := f.ReadAt(buf[:descSize], descOff); err != nil {
   103					return "", err
   104				}
   105				return fmt.Sprintf("%x", buf[:descSize]), nil
   106			}
   107		}
   108		return "", errNoBuildID
   109	}
   110	

View as plain text