ReadBuildID reads the build ID from the currently running executable.
()
| 13 | |
| 14 | // ReadBuildID reads the build ID from the currently running executable. |
| 15 | func ReadBuildID() ([]byte, error) { |
| 16 | executable, err := os.Executable() |
| 17 | if err != nil { |
| 18 | return nil, err |
| 19 | } |
| 20 | f, err := os.Open(executable) |
| 21 | if err != nil { |
| 22 | return nil, err |
| 23 | } |
| 24 | defer f.Close() |
| 25 | |
| 26 | switch runtime.GOOS { |
| 27 | case "linux", "freebsd", "android": |
| 28 | // Read the GNU build id section. (Not sure about FreeBSD though...) |
| 29 | file, err := elf.NewFile(f) |
| 30 | if err != nil { |
| 31 | return nil, err |
| 32 | } |
| 33 | var gnuID, goID []byte |
| 34 | for _, section := range file.Sections { |
| 35 | if section.Type != elf.SHT_NOTE || |
| 36 | (section.Name != ".note.gnu.build-id" && section.Name != ".note.go.buildid") { |
| 37 | continue |
| 38 | } |
| 39 | buf := make([]byte, section.Size) |
| 40 | n, err := section.ReadAt(buf, 0) |
| 41 | if uint64(n) != section.Size || err != nil { |
| 42 | return nil, fmt.Errorf("could not read build id: %w", err) |
| 43 | } |
| 44 | if section.Name == ".note.gnu.build-id" { |
| 45 | gnuID = buf |
| 46 | } else { |
| 47 | goID = buf |
| 48 | } |
| 49 | } |
| 50 | if gnuID != nil { |
| 51 | return gnuID, nil |
| 52 | } else if goID != nil { |
| 53 | return goID, nil |
| 54 | } |
| 55 | case "darwin": |
| 56 | // Read the LC_UUID load command, which contains the equivalent of a |
| 57 | // build ID. |
| 58 | file, err := macho.NewFile(f) |
| 59 | if err != nil { |
| 60 | return nil, err |
| 61 | } |
| 62 | for _, load := range file.Loads { |
| 63 | // Unfortunately, the debug/macho package doesn't support the |
| 64 | // LC_UUID command directly. So we have to read it from |
| 65 | // macho.LoadBytes. |
| 66 | load, ok := load.(macho.LoadBytes) |
| 67 | if !ok { |
| 68 | continue |
| 69 | } |
| 70 | raw := load.Raw() |
| 71 | command := binary.LittleEndian.Uint32(raw) |
| 72 | if command != 0x1b { |