github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/osutil/buildid.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2017 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package osutil 21 22 import ( 23 "debug/elf" 24 "encoding/binary" 25 "encoding/hex" 26 "errors" 27 "os" 28 ) 29 30 // ErrNoBuildID is returned when an executable does not contain a Build-ID 31 var ErrNoBuildID = errors.New("executable does not contain a build ID") 32 33 type elfNoteHeader struct { 34 Namesz uint32 35 Descsz uint32 36 Type uint32 37 } 38 39 const ( 40 gnuElfNote = "GNU\x00" 41 gnuHdrType = 3 42 goElfNote = "Go\x00\x00" 43 goHdrType = 4 44 ) 45 46 // ReadBuildID returns the build ID of a given binary. GNU BuildID is is 47 // preferred over Go BuildID. Returns an error when neither is found. 48 func ReadBuildID(fname string) (string, error) { 49 if buildId, err := readGenericBuildID(fname, gnuElfNote, gnuHdrType); err == nil { 50 return buildId, nil 51 } 52 return readGenericBuildID(fname, goElfNote, goHdrType) 53 } 54 55 func readGenericBuildID(fname, elfNote string, hdrType uint32) (string, error) { 56 // Open the designated ELF file 57 f, err := elf.Open(fname) 58 if err != nil { 59 return "", err 60 } 61 defer f.Close() 62 63 for _, section := range f.Sections { 64 65 // We are looking for note sections 66 if section.Type != elf.SHT_NOTE { 67 continue 68 } 69 70 // NOTE: this is a ReadSeeker so no need to close it 71 sr := section.Open() 72 sr.Seek(0, os.SEEK_SET) 73 74 // Read the ELF Note header 75 nHdr := new(elfNoteHeader) 76 if err := binary.Read(sr, f.ByteOrder, nHdr); err != nil { 77 return "", err 78 } 79 80 // We are looking for a specific type of note 81 if nHdr.Type != hdrType { 82 continue 83 } 84 85 // Read note name 86 noteName := make([]byte, nHdr.Namesz) 87 if err := binary.Read(sr, f.ByteOrder, noteName); err != nil { 88 return "", err 89 } 90 91 // We are only interested in GNU build IDs 92 if string(noteName) != elfNote { 93 continue 94 } 95 96 // Read note data 97 noteData := make([]byte, nHdr.Descsz) 98 if err := binary.Read(sr, f.ByteOrder, noteData); err != nil { 99 return "", err 100 } 101 102 // Return the first build-id we manage to find 103 return hex.EncodeToString(noteData), nil 104 } 105 return "", ErrNoBuildID 106 } 107 108 // MyBuildID return the build-id of the currently running executable 109 func MyBuildID() (string, error) { 110 exe, err := osReadlink("/proc/self/exe") 111 if err != nil { 112 return "", err 113 } 114 115 return ReadBuildID(exe) 116 }