...
Run Format

Source file src/runtime/pprof/elf.go

Documentation: runtime/pprof

  // Copyright 2017 The Go Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style
  // license that can be found in the LICENSE file.
  
  package pprof
  
  import (
  	"encoding/binary"
  	"errors"
  	"fmt"
  	"os"
  )
  
  var (
  	errBadELF    = errors.New("malformed ELF binary")
  	errNoBuildID = errors.New("no NT_GNU_BUILD_ID found in ELF binary")
  )
  
  // elfBuildID returns the GNU build ID of the named ELF binary,
  // without introducing a dependency on debug/elf and its dependencies.
  func elfBuildID(file string) (string, error) {
  	buf := make([]byte, 256)
  	f, err := os.Open(file)
  	if err != nil {
  		return "", err
  	}
  	defer f.Close()
  
  	if _, err := f.ReadAt(buf[:64], 0); err != nil {
  		return "", err
  	}
  
  	// ELF file begins with \x7F E L F.
  	if buf[0] != 0x7F || buf[1] != 'E' || buf[2] != 'L' || buf[3] != 'F' {
  		return "", errBadELF
  	}
  
  	var byteOrder binary.ByteOrder
  	switch buf[5] {
  	default:
  		return "", errBadELF
  	case 1: // little-endian
  		byteOrder = binary.LittleEndian
  	case 2: // big-endian
  		byteOrder = binary.BigEndian
  	}
  
  	var shnum int
  	var shoff, shentsize int64
  	switch buf[4] {
  	default:
  		return "", errBadELF
  	case 1: // 32-bit file header
  		shoff = int64(byteOrder.Uint32(buf[32:]))
  		shentsize = int64(byteOrder.Uint16(buf[46:]))
  		if shentsize != 40 {
  			return "", errBadELF
  		}
  		shnum = int(byteOrder.Uint16(buf[48:]))
  	case 2: // 64-bit file header
  		shoff = int64(byteOrder.Uint64(buf[40:]))
  		shentsize = int64(byteOrder.Uint16(buf[58:]))
  		if shentsize != 64 {
  			return "", errBadELF
  		}
  		shnum = int(byteOrder.Uint16(buf[60:]))
  	}
  
  	for i := 0; i < shnum; i++ {
  		if _, err := f.ReadAt(buf[:shentsize], shoff+int64(i)*shentsize); err != nil {
  			return "", err
  		}
  		if typ := byteOrder.Uint32(buf[4:]); typ != 7 { // SHT_NOTE
  			continue
  		}
  		var off, size int64
  		if shentsize == 40 {
  			// 32-bit section header
  			off = int64(byteOrder.Uint32(buf[16:]))
  			size = int64(byteOrder.Uint32(buf[20:]))
  		} else {
  			// 64-bit section header
  			off = int64(byteOrder.Uint64(buf[24:]))
  			size = int64(byteOrder.Uint64(buf[32:]))
  		}
  		size += off
  		for off < size {
  			if _, err := f.ReadAt(buf[:16], off); err != nil { // room for header + name GNU\x00
  				return "", err
  			}
  			nameSize := int(byteOrder.Uint32(buf[0:]))
  			descSize := int(byteOrder.Uint32(buf[4:]))
  			noteType := int(byteOrder.Uint32(buf[8:]))
  			descOff := off + int64(12+(nameSize+3)&^3)
  			off = descOff + int64((descSize+3)&^3)
  			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)
  				continue
  			}
  			if descSize > len(buf) {
  				return "", errBadELF
  			}
  			if _, err := f.ReadAt(buf[:descSize], descOff); err != nil {
  				return "", err
  			}
  			return fmt.Sprintf("%x", buf[:descSize]), nil
  		}
  	}
  	return "", errNoBuildID
  }
  

View as plain text