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, &noteType)
    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  }