bc998d034f
Reviewed-on: https://go-review.googlesource.com/63753 From-SVN: r252767
110 lines
2.8 KiB
Go
110 lines
2.8 KiB
Go
// 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
|
|
}
|