1 // Copyright 2015 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.
17 func readAligned4(r io
.Reader
, sz
int32) ([]byte, error
) {
19 data
:= make([]byte, full
)
20 _
, err
:= io
.ReadFull(r
, data
)
28 func ReadELFNote(filename
, name
string, typ
int32) ([]byte, error
) {
29 f
, err
:= elf
.Open(filename
)
33 for _
, sect
:= range f
.Sections
{
34 if sect
.Type
!= elf
.SHT_NOTE
{
39 var namesize
, descsize
, noteType
int32
40 err
= binary
.Read(r
, f
.ByteOrder
, &namesize
)
45 return nil, fmt
.Errorf("read namesize failed: %v", err
)
47 err
= binary
.Read(r
, f
.ByteOrder
, &descsize
)
49 return nil, fmt
.Errorf("read descsize failed: %v", err
)
51 err
= binary
.Read(r
, f
.ByteOrder
, ¬eType
)
53 return nil, fmt
.Errorf("read type failed: %v", err
)
55 noteName
, err
:= readAligned4(r
, namesize
)
57 return nil, fmt
.Errorf("read name failed: %v", err
)
59 desc
, err
:= readAligned4(r
, descsize
)
61 return nil, fmt
.Errorf("read desc failed: %v", err
)
63 if name
== string(noteName
) && typ
== noteType
{
71 var elfGoNote
= []byte("Go\x00\x00")
73 // The Go build ID is stored in a note described by an ELF PT_NOTE prog
74 // header. The caller has already opened filename, to get f, and read
75 // at least 4 kB out, in data.
76 func readELFGoBuildID(filename
string, f
*os
.File
, data
[]byte) (buildid
string, err error
) {
77 // Assume the note content is in the data, already read.
78 // Rewrite the ELF header to set shnum to 0, so that we can pass
79 // the data to elf.NewFile and it will decode the Prog list but not
80 // try to read the section headers and the string table from disk.
81 // That's a waste of I/O when all we care about is the Prog list
82 // and the one ELF note.
83 switch elf
.Class(data
[elf
.EI_CLASS
]) {
92 const elfGoBuildIDTag
= 4
94 ef
, err
:= elf
.NewFile(bytes
.NewReader(data
))
96 return "", &os
.PathError
{Path
: filename
, Op
: "parse", Err
: err
}
98 for _
, p
:= range ef
.Progs
{
99 if p
.Type
!= elf
.PT_NOTE || p
.Filesz
< 16 {
104 if p
.Off
+p
.Filesz
< uint64(len(data
)) {
105 note
= data
[p
.Off
: p
.Off
+p
.Filesz
]
107 // For some linkers, such as the Solaris linker,
108 // the buildid may not be found in data (which
109 // likely contains the first 16kB of the file)
110 // or even the first few megabytes of the file
111 // due to differences in note segment placement;
112 // in that case, extract the note data manually.
113 _
, err
= f
.Seek(int64(p
.Off
), io
.SeekStart
)
118 note
= make([]byte, p
.Filesz
)
119 _
, err
= io
.ReadFull(f
, note
)
127 nameSize
:= ef
.ByteOrder
.Uint32(note
)
128 valSize
:= ef
.ByteOrder
.Uint32(note
[4:])
129 tag
:= ef
.ByteOrder
.Uint32(note
[8:])
131 if nameSize
== 4 && 16+valSize
<= uint32(len(note
)) && tag
== elfGoBuildIDTag
&& bytes
.Equal(name
, elfGoNote
) {
132 return string(note
[16 : 16+valSize
]), nil
135 nameSize
= (nameSize
+ 3) &^ 3
136 valSize
= (valSize
+ 3) &^ 3
137 notesz
:= uint64(12 + nameSize
+ valSize
)
138 if filesz
<= notesz
{
146 // No note. Treat as successful but build ID empty.
150 // The Go build ID is stored at the beginning of the Mach-O __text segment.
151 // The caller has already opened filename, to get f, and read a few kB out, in data.
152 // Sadly, that's not guaranteed to hold the note, because there is an arbitrary amount
153 // of other junk placed in the file ahead of the main text.
154 func readMachoGoBuildID(filename
string, f
*os
.File
, data
[]byte) (buildid
string, err error
) {
155 // If the data we want has already been read, don't worry about Mach-O parsing.
156 // This is both an optimization and a hedge against the Mach-O parsing failing
157 // in the future due to, for example, the name of the __text section changing.
158 if b
, err
:= readRawGoBuildID(filename
, data
); b
!= "" && err
== nil {
162 mf
, err
:= macho
.NewFile(f
)
164 return "", &os
.PathError
{Path
: filename
, Op
: "parse", Err
: err
}
167 sect
:= mf
.Section("__text")
169 // Every binary has a __text section. Something is wrong.
170 return "", &os
.PathError
{Path
: filename
, Op
: "parse", Err
: fmt
.Errorf("cannot find __text section")}
173 // It should be in the first few bytes, but read a lot just in case,
174 // especially given our past problems on OS X with the build ID moving.
175 // There shouldn't be much difference between reading 4kB and 32kB:
176 // the hard part is getting to the data, not transferring it.
178 if n
> uint64(BuildIDReadSize
) {
179 n
= uint64(BuildIDReadSize
)
181 buf
:= make([]byte, n
)
182 if _
, err
:= f
.ReadAt(buf
, int64(sect
.Offset
)); err
!= nil {
186 return readRawGoBuildID(filename
, buf
)