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)
34 for _, sect := range f.Sections {
35 if sect.Type != elf.SHT_NOTE {
40 var namesize, descsize, noteType int32
41 err = binary.Read(r, f.ByteOrder, &namesize)
46 return nil, fmt.Errorf("read namesize failed: %v", err)
48 err = binary.Read(r, f.ByteOrder, &descsize)
50 return nil, fmt.Errorf("read descsize failed: %v", err)
52 err = binary.Read(r, f.ByteOrder, ¬eType)
54 return nil, fmt.Errorf("read type failed: %v", err)
56 noteName, err := readAligned4(r, namesize)
58 return nil, fmt.Errorf("read name failed: %v", err)
60 desc, err := readAligned4(r, descsize)
62 return nil, fmt.Errorf("read desc failed: %v", err)
64 if name == string(noteName) && typ == noteType {
72 var elfGoNote = []byte("Go\x00\x00")
73 var elfGNUNote = []byte("GNU\x00")
75 // The Go build ID is stored in a note described by an ELF PT_NOTE prog
76 // header. The caller has already opened filename, to get f, and read
77 // at least 4 kB out, in data.
78 func readELF(name string, f *os.File, data []byte) (buildid string, err error) {
79 // Assume the note content is in the data, already read.
80 // Rewrite the ELF header to set shnum to 0, so that we can pass
81 // the data to elf.NewFile and it will decode the Prog list but not
82 // try to read the section headers and the string table from disk.
83 // That's a waste of I/O when all we care about is the Prog list
84 // and the one ELF note.
85 switch elf.Class(data[elf.EI_CLASS]) {
94 const elfGoBuildIDTag = 4
95 const gnuBuildIDTag = 3
97 ef, err := elf.NewFile(bytes.NewReader(data))
99 return "", &os.PathError{Path: name, Op: "parse", Err: err}
102 for _, p := range ef.Progs {
103 if p.Type != elf.PT_NOTE || p.Filesz < 16 {
108 if p.Off+p.Filesz < uint64(len(data)) {
109 note = data[p.Off : p.Off+p.Filesz]
111 // For some linkers, such as the Solaris linker,
112 // the buildid may not be found in data (which
113 // likely contains the first 16kB of the file)
114 // or even the first few megabytes of the file
115 // due to differences in note segment placement;
116 // in that case, extract the note data manually.
117 _, err = f.Seek(int64(p.Off), io.SeekStart)
122 note = make([]byte, p.Filesz)
123 _, err = io.ReadFull(f, note)
132 nameSize := ef.ByteOrder.Uint32(note)
133 valSize := ef.ByteOrder.Uint32(note[4:])
134 tag := ef.ByteOrder.Uint32(note[8:])
136 if nameSize == 4 && 16+valSize <= uint32(len(note)) && tag == elfGoBuildIDTag && bytes.Equal(nname, elfGoNote) {
137 return string(note[16 : 16+valSize]), nil
140 if nameSize == 4 && 16+valSize <= uint32(len(note)) && tag == gnuBuildIDTag && bytes.Equal(nname, elfGNUNote) {
141 gnu = string(note[16 : 16+valSize])
144 nameSize = (nameSize + 3) &^ 3
145 valSize = (valSize + 3) &^ 3
146 notesz := uint64(12 + nameSize + valSize)
147 if filesz <= notesz {
152 alignedOff := (off + align - 1) &^ (align - 1)
153 notesz += alignedOff - off
160 // If we didn't find a Go note, use a GNU note if available.
161 // This is what gccgo uses.
166 // No note. Treat as successful but build ID empty.
170 // The Go build ID is stored at the beginning of the Mach-O __text segment.
171 // The caller has already opened filename, to get f, and read a few kB out, in data.
172 // Sadly, that's not guaranteed to hold the note, because there is an arbitrary amount
173 // of other junk placed in the file ahead of the main text.
174 func readMacho(name string, f *os.File, data []byte) (buildid string, err error) {
175 // If the data we want has already been read, don't worry about Mach-O parsing.
176 // This is both an optimization and a hedge against the Mach-O parsing failing
177 // in the future due to, for example, the name of the __text section changing.
178 if b, err := readRaw(name, data); b != "" && err == nil {
182 mf, err := macho.NewFile(f)
184 return "", &os.PathError{Path: name, Op: "parse", Err: err}
187 sect := mf.Section("__text")
189 // Every binary has a __text section. Something is wrong.
190 return "", &os.PathError{Path: name, Op: "parse", Err: fmt.Errorf("cannot find __text section")}
193 // It should be in the first few bytes, but read a lot just in case,
194 // especially given our past problems on OS X with the build ID moving.
195 // There shouldn't be much difference between reading 4kB and 32kB:
196 // the hard part is getting to the data, not transferring it.
198 if n > uint64(readSize) {
201 buf := make([]byte, n)
202 if _, err := f.ReadAt(buf, int64(sect.Offset)); err != nil {
206 return readRaw(name, buf)