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.
15 errBadELF
= errors
.New("malformed ELF binary")
16 errNoBuildID
= errors
.New("no NT_GNU_BUILD_ID found in ELF binary")
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
)
29 if _
, err
:= f
.ReadAt(buf
[:64], 0); err
!= nil {
33 // ELF file begins with \x7F E L F.
34 if buf
[0] != 0x7F || buf
[1] != 'E' || buf
[2] != 'L' || buf
[3] != 'F' {
38 var byteOrder binary
.ByteOrder
42 case 1: // little-endian
43 byteOrder
= binary
.LittleEndian
45 byteOrder
= binary
.BigEndian
49 var shoff
, shentsize
int64
53 case 1: // 32-bit file header
54 shoff
= int64(byteOrder
.Uint32(buf
[32:]))
55 shentsize
= int64(byteOrder
.Uint16(buf
[46:]))
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:]))
66 shnum
= int(byteOrder
.Uint16(buf
[60:]))
69 for i
:= 0; i
< shnum
; i
++ {
70 if _
, err
:= f
.ReadAt(buf
[:shentsize
], shoff
+int64(i
)*shentsize
); err
!= nil {
73 if typ
:= byteOrder
.Uint32(buf
[4:]); typ
!= 7 { // SHT_NOTE
78 // 32-bit section header
79 off
= int64(byteOrder
.Uint32(buf
[16:]))
80 size
= int64(byteOrder
.Uint32(buf
[20:]))
82 // 64-bit section header
83 off
= int64(byteOrder
.Uint64(buf
[24:]))
84 size
= int64(byteOrder
.Uint64(buf
[32:]))
88 if _
, err
:= f
.ReadAt(buf
[:16], off
); err
!= nil { // room for header + name GNU\x00
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)
99 if descSize
> len(buf
) {
102 if _
, err
:= f
.ReadAt(buf
[:descSize
], descOff
); err
!= nil {
105 return fmt
.Sprintf("%x", buf
[:descSize
]), nil
108 return "", errNoBuildID