var elfGoNote = []byte("Go\x00\x00")
-// readELFGoBuildID the Go build ID string from an ELF binary.
-// The Go build ID is stored in a note described by an ELF PT_NOTE prog header.
-// The caller has already opened filename, to get f, and read the first 4 kB out, in data.
+// The Go build ID is stored in a note described by an ELF PT_NOTE prog
+// header. The caller has already opened filename, to get f, and read
+// at least 4 kB out, in data.
func readELFGoBuildID(filename string, f *os.File, data []byte) (buildid string, err error) {
- // Assume the note content is in the first 4 kB, already read.
+ // Assume the note content is in the data, already read.
// Rewrite the ELF header to set shnum to 0, so that we can pass
// the data to elf.NewFile and it will decode the Prog list but not
// try to read the section headers and the string table from disk.
return "", &os.PathError{Path: filename, Op: "parse", Err: err}
}
for _, p := range ef.Progs {
- if p.Type != elf.PT_NOTE || p.Off >= uint64(len(data)) || p.Off+p.Filesz >= uint64(len(data)) || p.Filesz < 16 {
+ if p.Type != elf.PT_NOTE || p.Filesz < 16 {
continue
}
- note := data[p.Off : p.Off+p.Filesz]
+ var note []byte
+ if p.Off+p.Filesz < uint64(len(data)) {
+ note = data[p.Off : p.Off+p.Filesz]
+ } else {
+ // For some linkers, such as the Solaris linker,
+ // the buildid may not be found in data (which
+ // likely contains the first 16kB of the file)
+ // or even the first few megabytes of the file
+ // due to differences in note segment placement;
+ // in that case, extract the note data manually.
+ _, err = f.Seek(int64(p.Off), 0)
+ if err != nil {
+ return "", err
+ }
+
+ note = make([]byte, p.Filesz)
+ _, err = io.ReadFull(f, note)
+ if err != nil {
+ return "", err
+ }
+ }
nameSize := ef.ByteOrder.Uint32(note)
valSize := ef.ByteOrder.Uint32(note[4:])
tag := ef.ByteOrder.Uint32(note[8:])