Source file src/debug/buildinfo/buildinfo.go

     1  // Copyright 2021 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 buildinfo provides access to information embedded in a Go binary
     6  // about how it was built. This includes the Go toolchain version, and the
     7  // set of modules used (for binaries built in module mode).
     8  //
     9  // Build information is available for the currently running binary in
    10  // runtime/debug.ReadBuildInfo.
    11  package buildinfo
    12  
    13  import (
    14  	"bytes"
    15  	"debug/elf"
    16  	"debug/macho"
    17  	"debug/pe"
    18  	"debug/plan9obj"
    19  	"encoding/binary"
    20  	"errors"
    21  	"fmt"
    22  	"internal/saferio"
    23  	"internal/xcoff"
    24  	"io"
    25  	"io/fs"
    26  	"os"
    27  	"runtime/debug"
    28  	_ "unsafe" // for linkname
    29  )
    30  
    31  // Type alias for build info. We cannot move the types here, since
    32  // runtime/debug would need to import this package, which would make it
    33  // a much larger dependency.
    34  type BuildInfo = debug.BuildInfo
    35  
    36  // errUnrecognizedFormat is returned when a given executable file doesn't
    37  // appear to be in a known format, or it breaks the rules of that format,
    38  // or when there are I/O errors reading the file.
    39  var errUnrecognizedFormat = errors.New("unrecognized file format")
    40  
    41  // errNotGoExe is returned when a given executable file is valid but does
    42  // not contain Go build information.
    43  //
    44  // errNotGoExe should be an internal detail,
    45  // but widely used packages access it using linkname.
    46  // Notable members of the hall of shame include:
    47  //   - github.com/quay/claircore
    48  //
    49  // Do not remove or change the type signature.
    50  // See go.dev/issue/67401.
    51  //
    52  //go:linkname errNotGoExe
    53  var errNotGoExe = errors.New("not a Go executable")
    54  
    55  // The build info blob left by the linker is identified by a 32-byte header,
    56  // consisting of buildInfoMagic (14 bytes), followed by version-dependent
    57  // fields.
    58  var buildInfoMagic = []byte("\xff Go buildinf:")
    59  
    60  const (
    61  	buildInfoAlign      = 16
    62  	buildInfoHeaderSize = 32
    63  )
    64  
    65  // ReadFile returns build information embedded in a Go binary
    66  // file at the given path. Most information is only available for binaries built
    67  // with module support.
    68  func ReadFile(name string) (info *BuildInfo, err error) {
    69  	defer func() {
    70  		if pathErr := (*fs.PathError)(nil); errors.As(err, &pathErr) {
    71  			err = fmt.Errorf("could not read Go build info: %w", err)
    72  		} else if err != nil {
    73  			err = fmt.Errorf("could not read Go build info from %s: %w", name, err)
    74  		}
    75  	}()
    76  
    77  	f, err := os.Open(name)
    78  	if err != nil {
    79  		return nil, err
    80  	}
    81  	defer f.Close()
    82  	return Read(f)
    83  }
    84  
    85  // Read returns build information embedded in a Go binary file
    86  // accessed through the given ReaderAt. Most information is only available for
    87  // binaries built with module support.
    88  func Read(r io.ReaderAt) (*BuildInfo, error) {
    89  	vers, mod, err := readRawBuildInfo(r)
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  	bi, err := debug.ParseBuildInfo(mod)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	bi.GoVersion = vers
    98  	return bi, nil
    99  }
   100  
   101  type exe interface {
   102  	// DataStart returns the virtual address and size of the segment or section that
   103  	// should contain build information. This is either a specially named section
   104  	// or the first writable non-zero data segment.
   105  	DataStart() (uint64, uint64)
   106  
   107  	// DataReader returns an io.ReaderAt that reads from addr until the end
   108  	// of segment or section that contains addr.
   109  	DataReader(addr uint64) (io.ReaderAt, error)
   110  }
   111  
   112  // readRawBuildInfo extracts the Go toolchain version and module information
   113  // strings from a Go binary. On success, vers should be non-empty. mod
   114  // is empty if the binary was not built with modules enabled.
   115  func readRawBuildInfo(r io.ReaderAt) (vers, mod string, err error) {
   116  	// Read the first bytes of the file to identify the format, then delegate to
   117  	// a format-specific function to load segment and section headers.
   118  	ident := make([]byte, 16)
   119  	if n, err := r.ReadAt(ident, 0); n < len(ident) || err != nil {
   120  		return "", "", errUnrecognizedFormat
   121  	}
   122  
   123  	var x exe
   124  	switch {
   125  	case bytes.HasPrefix(ident, []byte("\x7FELF")):
   126  		f, err := elf.NewFile(r)
   127  		if err != nil {
   128  			return "", "", errUnrecognizedFormat
   129  		}
   130  		x = &elfExe{f}
   131  	case bytes.HasPrefix(ident, []byte("MZ")):
   132  		f, err := pe.NewFile(r)
   133  		if err != nil {
   134  			return "", "", errUnrecognizedFormat
   135  		}
   136  		x = &peExe{f}
   137  	case bytes.HasPrefix(ident, []byte("\xFE\xED\xFA")) || bytes.HasPrefix(ident[1:], []byte("\xFA\xED\xFE")):
   138  		f, err := macho.NewFile(r)
   139  		if err != nil {
   140  			return "", "", errUnrecognizedFormat
   141  		}
   142  		x = &machoExe{f}
   143  	case bytes.HasPrefix(ident, []byte("\xCA\xFE\xBA\xBE")) || bytes.HasPrefix(ident, []byte("\xCA\xFE\xBA\xBF")):
   144  		f, err := macho.NewFatFile(r)
   145  		if err != nil || len(f.Arches) == 0 {
   146  			return "", "", errUnrecognizedFormat
   147  		}
   148  		x = &machoExe{f.Arches[0].File}
   149  	case bytes.HasPrefix(ident, []byte{0x01, 0xDF}) || bytes.HasPrefix(ident, []byte{0x01, 0xF7}):
   150  		f, err := xcoff.NewFile(r)
   151  		if err != nil {
   152  			return "", "", errUnrecognizedFormat
   153  		}
   154  		x = &xcoffExe{f}
   155  	case hasPlan9Magic(ident):
   156  		f, err := plan9obj.NewFile(r)
   157  		if err != nil {
   158  			return "", "", errUnrecognizedFormat
   159  		}
   160  		x = &plan9objExe{f}
   161  	default:
   162  		return "", "", errUnrecognizedFormat
   163  	}
   164  
   165  	// Read segment or section to find the build info blob.
   166  	// On some platforms, the blob will be in its own section, and DataStart
   167  	// returns the address of that section. On others, it's somewhere in the
   168  	// data segment; the linker puts it near the beginning.
   169  	// See cmd/link/internal/ld.Link.buildinfo.
   170  	dataAddr, dataSize := x.DataStart()
   171  	if dataSize == 0 {
   172  		return "", "", errNotGoExe
   173  	}
   174  
   175  	addr, err := searchMagic(x, dataAddr, dataSize)
   176  	if err != nil {
   177  		return "", "", err
   178  	}
   179  
   180  	// Read in the full header first.
   181  	header, err := readData(x, addr, buildInfoHeaderSize)
   182  	if err == io.EOF {
   183  		return "", "", errNotGoExe
   184  	} else if err != nil {
   185  		return "", "", err
   186  	}
   187  	if len(header) < buildInfoHeaderSize {
   188  		return "", "", errNotGoExe
   189  	}
   190  
   191  	const (
   192  		ptrSizeOffset = 14
   193  		flagsOffset   = 15
   194  		versPtrOffset = 16
   195  
   196  		flagsEndianMask   = 0x1
   197  		flagsEndianLittle = 0x0
   198  		flagsEndianBig    = 0x1
   199  
   200  		flagsVersionMask = 0x2
   201  		flagsVersionPtr  = 0x0
   202  		flagsVersionInl  = 0x2
   203  	)
   204  
   205  	// Decode the blob. The blob is a 32-byte header, optionally followed
   206  	// by 2 varint-prefixed string contents.
   207  	//
   208  	// type buildInfoHeader struct {
   209  	// 	magic       [14]byte
   210  	// 	ptrSize     uint8 // used if flagsVersionPtr
   211  	// 	flags       uint8
   212  	// 	versPtr     targetUintptr // used if flagsVersionPtr
   213  	// 	modPtr      targetUintptr // used if flagsVersionPtr
   214  	// }
   215  	//
   216  	// The version bit of the flags field determines the details of the format.
   217  	//
   218  	// Prior to 1.18, the flags version bit is flagsVersionPtr. In this
   219  	// case, the header includes pointers to the version and modinfo Go
   220  	// strings in the header. The ptrSize field indicates the size of the
   221  	// pointers and the endian bit of the flag indicates the pointer
   222  	// endianness.
   223  	//
   224  	// Since 1.18, the flags version bit is flagsVersionInl. In this case,
   225  	// the header is followed by the string contents inline as
   226  	// length-prefixed (as varint) string contents. First is the version
   227  	// string, followed immediately by the modinfo string.
   228  	flags := header[flagsOffset]
   229  	if flags&flagsVersionMask == flagsVersionInl {
   230  		vers, addr, err = decodeString(x, addr+buildInfoHeaderSize)
   231  		if err != nil {
   232  			return "", "", err
   233  		}
   234  		mod, _, err = decodeString(x, addr)
   235  		if err != nil {
   236  			return "", "", err
   237  		}
   238  	} else {
   239  		// flagsVersionPtr (<1.18)
   240  		ptrSize := int(header[ptrSizeOffset])
   241  		bigEndian := flags&flagsEndianMask == flagsEndianBig
   242  		var bo binary.ByteOrder
   243  		if bigEndian {
   244  			bo = binary.BigEndian
   245  		} else {
   246  			bo = binary.LittleEndian
   247  		}
   248  		var readPtr func([]byte) uint64
   249  		if ptrSize == 4 {
   250  			readPtr = func(b []byte) uint64 { return uint64(bo.Uint32(b)) }
   251  		} else if ptrSize == 8 {
   252  			readPtr = bo.Uint64
   253  		} else {
   254  			return "", "", errNotGoExe
   255  		}
   256  		vers = readString(x, ptrSize, readPtr, readPtr(header[versPtrOffset:]))
   257  		mod = readString(x, ptrSize, readPtr, readPtr(header[versPtrOffset+ptrSize:]))
   258  	}
   259  	if vers == "" {
   260  		return "", "", errNotGoExe
   261  	}
   262  	if len(mod) >= 33 && mod[len(mod)-17] == '\n' {
   263  		// Strip module framing: sentinel strings delimiting the module info.
   264  		// These are cmd/go/internal/modload.infoStart and infoEnd.
   265  		mod = mod[16 : len(mod)-16]
   266  	} else {
   267  		mod = ""
   268  	}
   269  
   270  	return vers, mod, nil
   271  }
   272  
   273  func hasPlan9Magic(magic []byte) bool {
   274  	if len(magic) >= 4 {
   275  		m := binary.BigEndian.Uint32(magic)
   276  		switch m {
   277  		case plan9obj.Magic386, plan9obj.MagicAMD64, plan9obj.MagicARM:
   278  			return true
   279  		}
   280  	}
   281  	return false
   282  }
   283  
   284  func decodeString(x exe, addr uint64) (string, uint64, error) {
   285  	// varint length followed by length bytes of data.
   286  
   287  	// N.B. ReadData reads _up to_ size bytes from the section containing
   288  	// addr. So we don't need to check that size doesn't overflow the
   289  	// section.
   290  	b, err := readData(x, addr, binary.MaxVarintLen64)
   291  	if err == io.EOF {
   292  		return "", 0, errNotGoExe
   293  	} else if err != nil {
   294  		return "", 0, err
   295  	}
   296  
   297  	length, n := binary.Uvarint(b)
   298  	if n <= 0 {
   299  		return "", 0, errNotGoExe
   300  	}
   301  	addr += uint64(n)
   302  
   303  	b, err = readData(x, addr, length)
   304  	if err == io.EOF {
   305  		return "", 0, errNotGoExe
   306  	} else if err == io.ErrUnexpectedEOF {
   307  		// Length too large to allocate. Clearly bogus value.
   308  		return "", 0, errNotGoExe
   309  	} else if err != nil {
   310  		return "", 0, err
   311  	}
   312  	if uint64(len(b)) < length {
   313  		// Section ended before we could read the full string.
   314  		return "", 0, errNotGoExe
   315  	}
   316  
   317  	return string(b), addr + length, nil
   318  }
   319  
   320  // readString returns the string at address addr in the executable x.
   321  func readString(x exe, ptrSize int, readPtr func([]byte) uint64, addr uint64) string {
   322  	hdr, err := readData(x, addr, uint64(2*ptrSize))
   323  	if err != nil || len(hdr) < 2*ptrSize {
   324  		return ""
   325  	}
   326  	dataAddr := readPtr(hdr)
   327  	dataLen := readPtr(hdr[ptrSize:])
   328  	data, err := readData(x, dataAddr, dataLen)
   329  	if err != nil || uint64(len(data)) < dataLen {
   330  		return ""
   331  	}
   332  	return string(data)
   333  }
   334  
   335  const searchChunkSize = 1 << 20 // 1 MB
   336  
   337  // searchMagic returns the aligned first instance of buildInfoMagic in the data
   338  // range [addr, addr+size). Returns false if not found.
   339  func searchMagic(x exe, start, size uint64) (uint64, error) {
   340  	end := start + size
   341  	if end < start {
   342  		// Overflow.
   343  		return 0, errUnrecognizedFormat
   344  	}
   345  
   346  	// Round up start; magic can't occur in the initial unaligned portion.
   347  	start = (start + buildInfoAlign - 1) &^ (buildInfoAlign - 1)
   348  	if start >= end {
   349  		return 0, errNotGoExe
   350  	}
   351  
   352  	var buf []byte
   353  	for start < end {
   354  		// Read in chunks to avoid consuming too much memory if data is large.
   355  		//
   356  		// Normally it would be somewhat painful to handle the magic crossing a
   357  		// chunk boundary, but since it must be 16-byte aligned we know it will
   358  		// fall within a single chunk.
   359  		remaining := end - start
   360  		chunkSize := uint64(searchChunkSize)
   361  		if chunkSize > remaining {
   362  			chunkSize = remaining
   363  		}
   364  
   365  		if buf == nil {
   366  			buf = make([]byte, chunkSize)
   367  		} else {
   368  			// N.B. chunkSize can only decrease, and only on the
   369  			// last chunk.
   370  			buf = buf[:chunkSize]
   371  			clear(buf)
   372  		}
   373  
   374  		n, err := readDataInto(x, start, buf)
   375  		if err == io.EOF {
   376  			// EOF before finding the magic; must not be a Go executable.
   377  			return 0, errNotGoExe
   378  		} else if err != nil {
   379  			return 0, err
   380  		}
   381  
   382  		data := buf[:n]
   383  		for len(data) > 0 {
   384  			i := bytes.Index(data, buildInfoMagic)
   385  			if i < 0 {
   386  				break
   387  			}
   388  			if remaining-uint64(i) < buildInfoHeaderSize {
   389  				// Found magic, but not enough space left for the full header.
   390  				return 0, errNotGoExe
   391  			}
   392  			if i%buildInfoAlign != 0 {
   393  				// Found magic, but misaligned. Keep searching.
   394  				next := (i + buildInfoAlign - 1) &^ (buildInfoAlign - 1)
   395  				if next > len(data) {
   396  					// Corrupt object file: the remaining
   397  					// count says there is more data,
   398  					// but we didn't read it.
   399  					return 0, errNotGoExe
   400  				}
   401  				data = data[next:]
   402  				continue
   403  			}
   404  			// Good match!
   405  			return start + uint64(i), nil
   406  		}
   407  
   408  		start += chunkSize
   409  	}
   410  
   411  	return 0, errNotGoExe
   412  }
   413  
   414  func readData(x exe, addr, size uint64) ([]byte, error) {
   415  	r, err := x.DataReader(addr)
   416  	if err != nil {
   417  		return nil, err
   418  	}
   419  
   420  	b, err := saferio.ReadDataAt(r, size, 0)
   421  	if len(b) > 0 && err == io.EOF {
   422  		err = nil
   423  	}
   424  	return b, err
   425  }
   426  
   427  func readDataInto(x exe, addr uint64, b []byte) (int, error) {
   428  	r, err := x.DataReader(addr)
   429  	if err != nil {
   430  		return 0, err
   431  	}
   432  
   433  	n, err := r.ReadAt(b, 0)
   434  	if n > 0 && err == io.EOF {
   435  		err = nil
   436  	}
   437  	return n, err
   438  }
   439  
   440  // elfExe is the ELF implementation of the exe interface.
   441  type elfExe struct {
   442  	f *elf.File
   443  }
   444  
   445  func (x *elfExe) DataReader(addr uint64) (io.ReaderAt, error) {
   446  	for _, prog := range x.f.Progs {
   447  		if prog.Vaddr <= addr && addr <= prog.Vaddr+prog.Filesz-1 {
   448  			remaining := prog.Vaddr + prog.Filesz - addr
   449  			return io.NewSectionReader(prog, int64(addr-prog.Vaddr), int64(remaining)), nil
   450  		}
   451  	}
   452  	return nil, errUnrecognizedFormat
   453  }
   454  
   455  func (x *elfExe) DataStart() (uint64, uint64) {
   456  	for _, s := range x.f.Sections {
   457  		if s.Name == ".go.buildinfo" {
   458  			return s.Addr, s.Size
   459  		}
   460  	}
   461  	for _, p := range x.f.Progs {
   462  		if p.Type == elf.PT_LOAD && p.Flags&(elf.PF_X|elf.PF_W) == elf.PF_W {
   463  			return p.Vaddr, p.Memsz
   464  		}
   465  	}
   466  	return 0, 0
   467  }
   468  
   469  // peExe is the PE (Windows Portable Executable) implementation of the exe interface.
   470  type peExe struct {
   471  	f *pe.File
   472  }
   473  
   474  func (x *peExe) imageBase() uint64 {
   475  	switch oh := x.f.OptionalHeader.(type) {
   476  	case *pe.OptionalHeader32:
   477  		return uint64(oh.ImageBase)
   478  	case *pe.OptionalHeader64:
   479  		return oh.ImageBase
   480  	}
   481  	return 0
   482  }
   483  
   484  func (x *peExe) DataReader(addr uint64) (io.ReaderAt, error) {
   485  	addr -= x.imageBase()
   486  	for _, sect := range x.f.Sections {
   487  		if uint64(sect.VirtualAddress) <= addr && addr <= uint64(sect.VirtualAddress+sect.Size-1) {
   488  			remaining := uint64(sect.VirtualAddress+sect.Size) - addr
   489  			return io.NewSectionReader(sect, int64(addr-uint64(sect.VirtualAddress)), int64(remaining)), nil
   490  		}
   491  	}
   492  	return nil, errUnrecognizedFormat
   493  }
   494  
   495  func (x *peExe) DataStart() (uint64, uint64) {
   496  	// Assume data is first writable section.
   497  	const (
   498  		IMAGE_SCN_CNT_CODE               = 0x00000020
   499  		IMAGE_SCN_CNT_INITIALIZED_DATA   = 0x00000040
   500  		IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080
   501  		IMAGE_SCN_MEM_EXECUTE            = 0x20000000
   502  		IMAGE_SCN_MEM_READ               = 0x40000000
   503  		IMAGE_SCN_MEM_WRITE              = 0x80000000
   504  		IMAGE_SCN_MEM_DISCARDABLE        = 0x2000000
   505  		IMAGE_SCN_LNK_NRELOC_OVFL        = 0x1000000
   506  		IMAGE_SCN_ALIGN_32BYTES          = 0x600000
   507  	)
   508  	for _, sect := range x.f.Sections {
   509  		if sect.VirtualAddress != 0 && sect.Size != 0 &&
   510  			sect.Characteristics&^IMAGE_SCN_ALIGN_32BYTES == IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE {
   511  			return uint64(sect.VirtualAddress) + x.imageBase(), uint64(sect.VirtualSize)
   512  		}
   513  	}
   514  	return 0, 0
   515  }
   516  
   517  // machoExe is the Mach-O (Apple macOS/iOS) implementation of the exe interface.
   518  type machoExe struct {
   519  	f *macho.File
   520  }
   521  
   522  func (x *machoExe) DataReader(addr uint64) (io.ReaderAt, error) {
   523  	for _, load := range x.f.Loads {
   524  		seg, ok := load.(*macho.Segment)
   525  		if !ok {
   526  			continue
   527  		}
   528  		if seg.Addr <= addr && addr <= seg.Addr+seg.Filesz-1 {
   529  			if seg.Name == "__PAGEZERO" {
   530  				continue
   531  			}
   532  			remaining := seg.Addr + seg.Filesz - addr
   533  			return io.NewSectionReader(seg, int64(addr-seg.Addr), int64(remaining)), nil
   534  		}
   535  	}
   536  	return nil, errUnrecognizedFormat
   537  }
   538  
   539  func (x *machoExe) DataStart() (uint64, uint64) {
   540  	// Look for section named "__go_buildinfo".
   541  	for _, sec := range x.f.Sections {
   542  		if sec.Name == "__go_buildinfo" {
   543  			return sec.Addr, sec.Size
   544  		}
   545  	}
   546  	// Try the first non-empty writable segment.
   547  	const RW = 3
   548  	for _, load := range x.f.Loads {
   549  		seg, ok := load.(*macho.Segment)
   550  		if ok && seg.Addr != 0 && seg.Filesz != 0 && seg.Prot == RW && seg.Maxprot == RW {
   551  			return seg.Addr, seg.Memsz
   552  		}
   553  	}
   554  	return 0, 0
   555  }
   556  
   557  // xcoffExe is the XCOFF (AIX eXtended COFF) implementation of the exe interface.
   558  type xcoffExe struct {
   559  	f *xcoff.File
   560  }
   561  
   562  func (x *xcoffExe) DataReader(addr uint64) (io.ReaderAt, error) {
   563  	for _, sect := range x.f.Sections {
   564  		if sect.VirtualAddress <= addr && addr <= sect.VirtualAddress+sect.Size-1 {
   565  			remaining := sect.VirtualAddress + sect.Size - addr
   566  			return io.NewSectionReader(sect, int64(addr-sect.VirtualAddress), int64(remaining)), nil
   567  		}
   568  	}
   569  	return nil, errors.New("address not mapped")
   570  }
   571  
   572  func (x *xcoffExe) DataStart() (uint64, uint64) {
   573  	if s := x.f.SectionByType(xcoff.STYP_DATA); s != nil {
   574  		return s.VirtualAddress, s.Size
   575  	}
   576  	return 0, 0
   577  }
   578  
   579  // plan9objExe is the Plan 9 a.out implementation of the exe interface.
   580  type plan9objExe struct {
   581  	f *plan9obj.File
   582  }
   583  
   584  func (x *plan9objExe) DataStart() (uint64, uint64) {
   585  	if s := x.f.Section("data"); s != nil {
   586  		return uint64(s.Offset), uint64(s.Size)
   587  	}
   588  	return 0, 0
   589  }
   590  
   591  func (x *plan9objExe) DataReader(addr uint64) (io.ReaderAt, error) {
   592  	for _, sect := range x.f.Sections {
   593  		if uint64(sect.Offset) <= addr && addr <= uint64(sect.Offset+sect.Size-1) {
   594  			remaining := uint64(sect.Offset+sect.Size) - addr
   595  			return io.NewSectionReader(sect, int64(addr-uint64(sect.Offset)), int64(remaining)), nil
   596  		}
   597  	}
   598  	return nil, errors.New("address not mapped")
   599  }
   600  

View as plain text