github.com/q45/go@v0.0.0-20151101211701-a4fb8c13db3f/src/cmd/go/note.go (about) 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. 4 5 package main 6 7 import ( 8 "bytes" 9 "debug/elf" 10 "encoding/binary" 11 "fmt" 12 "io" 13 "os" 14 ) 15 16 func readAligned4(r io.Reader, sz int32) ([]byte, error) { 17 full := (sz + 3) &^ 3 18 data := make([]byte, full) 19 _, err := io.ReadFull(r, data) 20 if err != nil { 21 return nil, err 22 } 23 data = data[:sz] 24 return data, nil 25 } 26 27 func readELFNote(filename, name string, typ int32) ([]byte, error) { 28 f, err := elf.Open(filename) 29 if err != nil { 30 return nil, err 31 } 32 for _, sect := range f.Sections { 33 if sect.Type != elf.SHT_NOTE { 34 continue 35 } 36 r := sect.Open() 37 for { 38 var namesize, descsize, noteType int32 39 err = binary.Read(r, f.ByteOrder, &namesize) 40 if err != nil { 41 if err == io.EOF { 42 break 43 } 44 return nil, fmt.Errorf("read namesize failed: %v", err) 45 } 46 err = binary.Read(r, f.ByteOrder, &descsize) 47 if err != nil { 48 return nil, fmt.Errorf("read descsize failed: %v", err) 49 } 50 err = binary.Read(r, f.ByteOrder, ¬eType) 51 if err != nil { 52 return nil, fmt.Errorf("read type failed: %v", err) 53 } 54 noteName, err := readAligned4(r, namesize) 55 if err != nil { 56 return nil, fmt.Errorf("read name failed: %v", err) 57 } 58 desc, err := readAligned4(r, descsize) 59 if err != nil { 60 return nil, fmt.Errorf("read desc failed: %v", err) 61 } 62 if name == string(noteName) && typ == noteType { 63 return desc, nil 64 } 65 } 66 } 67 return nil, nil 68 } 69 70 var elfGoNote = []byte("Go\x00\x00") 71 72 // The Go build ID is stored in a note described by an ELF PT_NOTE prog 73 // header. The caller has already opened filename, to get f, and read 74 // at least 4 kB out, in data. 75 func readELFGoBuildID(filename string, f *os.File, data []byte) (buildid string, err error) { 76 // Assume the note content is in the data, already read. 77 // Rewrite the ELF header to set shnum to 0, so that we can pass 78 // the data to elf.NewFile and it will decode the Prog list but not 79 // try to read the section headers and the string table from disk. 80 // That's a waste of I/O when all we care about is the Prog list 81 // and the one ELF note. 82 switch elf.Class(data[elf.EI_CLASS]) { 83 case elf.ELFCLASS32: 84 data[48] = 0 85 data[49] = 0 86 case elf.ELFCLASS64: 87 data[60] = 0 88 data[61] = 0 89 } 90 91 const elfGoBuildIDTag = 4 92 93 ef, err := elf.NewFile(bytes.NewReader(data)) 94 if err != nil { 95 return "", &os.PathError{Path: filename, Op: "parse", Err: err} 96 } 97 for _, p := range ef.Progs { 98 if p.Type != elf.PT_NOTE || p.Filesz < 16 { 99 continue 100 } 101 102 var note []byte 103 if p.Off+p.Filesz < uint64(len(data)) { 104 note = data[p.Off : p.Off+p.Filesz] 105 } else { 106 // For some linkers, such as the Solaris linker, 107 // the buildid may not be found in data (which 108 // likely contains the first 16kB of the file) 109 // or even the first few megabytes of the file 110 // due to differences in note segment placement; 111 // in that case, extract the note data manually. 112 _, err = f.Seek(int64(p.Off), 0) 113 if err != nil { 114 return "", err 115 } 116 117 note = make([]byte, p.Filesz) 118 _, err = io.ReadFull(f, note) 119 if err != nil { 120 return "", err 121 } 122 } 123 nameSize := ef.ByteOrder.Uint32(note) 124 valSize := ef.ByteOrder.Uint32(note[4:]) 125 tag := ef.ByteOrder.Uint32(note[8:]) 126 name := note[12:16] 127 if nameSize != 4 || 16+valSize > uint32(len(note)) || tag != elfGoBuildIDTag || !bytes.Equal(name, elfGoNote) { 128 continue 129 } 130 131 return string(note[16 : 16+valSize]), nil 132 } 133 134 // No note. Treat as successful but build ID empty. 135 return "", nil 136 }