Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/go/internal/version/exe.go

Documentation: cmd/go/internal/version

     1  // Copyright 2019 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 version
     6  
     7  import (
     8  	"bytes"
     9  	"debug/elf"
    10  	"debug/macho"
    11  	"debug/pe"
    12  	"fmt"
    13  	"internal/xcoff"
    14  	"io"
    15  	"os"
    16  )
    17  
    18  // An exe is a generic interface to an OS executable (ELF, Mach-O, PE, XCOFF).
    19  type exe interface {
    20  	// Close closes the underlying file.
    21  	Close() error
    22  
    23  	// ReadData reads and returns up to size byte starting at virtual address addr.
    24  	ReadData(addr, size uint64) ([]byte, error)
    25  
    26  	// DataStart returns the writable data segment start address.
    27  	DataStart() uint64
    28  }
    29  
    30  // openExe opens file and returns it as an exe.
    31  func openExe(file string) (exe, error) {
    32  	f, err := os.Open(file)
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  	data := make([]byte, 16)
    37  	if _, err := io.ReadFull(f, data); err != nil {
    38  		return nil, err
    39  	}
    40  	f.Seek(0, 0)
    41  	if bytes.HasPrefix(data, []byte("\x7FELF")) {
    42  		e, err := elf.NewFile(f)
    43  		if err != nil {
    44  			f.Close()
    45  			return nil, err
    46  		}
    47  		return &elfExe{f, e}, nil
    48  	}
    49  	if bytes.HasPrefix(data, []byte("MZ")) {
    50  		e, err := pe.NewFile(f)
    51  		if err != nil {
    52  			f.Close()
    53  			return nil, err
    54  		}
    55  		return &peExe{f, e}, nil
    56  	}
    57  	if bytes.HasPrefix(data, []byte("\xFE\xED\xFA")) || bytes.HasPrefix(data[1:], []byte("\xFA\xED\xFE")) {
    58  		e, err := macho.NewFile(f)
    59  		if err != nil {
    60  			f.Close()
    61  			return nil, err
    62  		}
    63  		return &machoExe{f, e}, nil
    64  	}
    65  	if bytes.HasPrefix(data, []byte{0x01, 0xDF}) || bytes.HasPrefix(data, []byte{0x01, 0xF7}) {
    66  		e, err := xcoff.NewFile(f)
    67  		if err != nil {
    68  			f.Close()
    69  			return nil, err
    70  		}
    71  		return &xcoffExe{f, e}, nil
    72  
    73  	}
    74  	return nil, fmt.Errorf("unrecognized executable format")
    75  }
    76  
    77  // elfExe is the ELF implementation of the exe interface.
    78  type elfExe struct {
    79  	os *os.File
    80  	f  *elf.File
    81  }
    82  
    83  func (x *elfExe) Close() error {
    84  	return x.os.Close()
    85  }
    86  
    87  func (x *elfExe) ReadData(addr, size uint64) ([]byte, error) {
    88  	for _, prog := range x.f.Progs {
    89  		if prog.Vaddr <= addr && addr <= prog.Vaddr+prog.Filesz-1 {
    90  			n := prog.Vaddr + prog.Filesz - addr
    91  			if n > size {
    92  				n = size
    93  			}
    94  			data := make([]byte, n)
    95  			_, err := prog.ReadAt(data, int64(addr-prog.Vaddr))
    96  			if err != nil {
    97  				return nil, err
    98  			}
    99  			return data, nil
   100  		}
   101  	}
   102  	return nil, fmt.Errorf("address not mapped")
   103  }
   104  
   105  func (x *elfExe) DataStart() uint64 {
   106  	for _, s := range x.f.Sections {
   107  		if s.Name == ".go.buildinfo" {
   108  			return s.Addr
   109  		}
   110  	}
   111  	for _, p := range x.f.Progs {
   112  		if p.Type == elf.PT_LOAD && p.Flags&(elf.PF_X|elf.PF_W) == elf.PF_W {
   113  			return p.Vaddr
   114  		}
   115  	}
   116  	return 0
   117  }
   118  
   119  // peExe is the PE (Windows Portable Executable) implementation of the exe interface.
   120  type peExe struct {
   121  	os *os.File
   122  	f  *pe.File
   123  }
   124  
   125  func (x *peExe) Close() error {
   126  	return x.os.Close()
   127  }
   128  
   129  func (x *peExe) imageBase() uint64 {
   130  	switch oh := x.f.OptionalHeader.(type) {
   131  	case *pe.OptionalHeader32:
   132  		return uint64(oh.ImageBase)
   133  	case *pe.OptionalHeader64:
   134  		return oh.ImageBase
   135  	}
   136  	return 0
   137  }
   138  
   139  func (x *peExe) ReadData(addr, size uint64) ([]byte, error) {
   140  	addr -= x.imageBase()
   141  	for _, sect := range x.f.Sections {
   142  		if uint64(sect.VirtualAddress) <= addr && addr <= uint64(sect.VirtualAddress+sect.Size-1) {
   143  			n := uint64(sect.VirtualAddress+sect.Size) - addr
   144  			if n > size {
   145  				n = size
   146  			}
   147  			data := make([]byte, n)
   148  			_, err := sect.ReadAt(data, int64(addr-uint64(sect.VirtualAddress)))
   149  			if err != nil {
   150  				return nil, err
   151  			}
   152  			return data, nil
   153  		}
   154  	}
   155  	return nil, fmt.Errorf("address not mapped")
   156  }
   157  
   158  func (x *peExe) DataStart() uint64 {
   159  	// Assume data is first writable section.
   160  	const (
   161  		IMAGE_SCN_CNT_CODE               = 0x00000020
   162  		IMAGE_SCN_CNT_INITIALIZED_DATA   = 0x00000040
   163  		IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080
   164  		IMAGE_SCN_MEM_EXECUTE            = 0x20000000
   165  		IMAGE_SCN_MEM_READ               = 0x40000000
   166  		IMAGE_SCN_MEM_WRITE              = 0x80000000
   167  		IMAGE_SCN_MEM_DISCARDABLE        = 0x2000000
   168  		IMAGE_SCN_LNK_NRELOC_OVFL        = 0x1000000
   169  		IMAGE_SCN_ALIGN_32BYTES          = 0x600000
   170  	)
   171  	for _, sect := range x.f.Sections {
   172  		if sect.VirtualAddress != 0 && sect.Size != 0 &&
   173  			sect.Characteristics&^IMAGE_SCN_ALIGN_32BYTES == IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE {
   174  			return uint64(sect.VirtualAddress) + x.imageBase()
   175  		}
   176  	}
   177  	return 0
   178  }
   179  
   180  // machoExe is the Mach-O (Apple macOS/iOS) implementation of the exe interface.
   181  type machoExe struct {
   182  	os *os.File
   183  	f  *macho.File
   184  }
   185  
   186  func (x *machoExe) Close() error {
   187  	return x.os.Close()
   188  }
   189  
   190  func (x *machoExe) ReadData(addr, size uint64) ([]byte, error) {
   191  	for _, load := range x.f.Loads {
   192  		seg, ok := load.(*macho.Segment)
   193  		if !ok {
   194  			continue
   195  		}
   196  		if seg.Addr <= addr && addr <= seg.Addr+seg.Filesz-1 {
   197  			if seg.Name == "__PAGEZERO" {
   198  				continue
   199  			}
   200  			n := seg.Addr + seg.Filesz - addr
   201  			if n > size {
   202  				n = size
   203  			}
   204  			data := make([]byte, n)
   205  			_, err := seg.ReadAt(data, int64(addr-seg.Addr))
   206  			if err != nil {
   207  				return nil, err
   208  			}
   209  			return data, nil
   210  		}
   211  	}
   212  	return nil, fmt.Errorf("address not mapped")
   213  }
   214  
   215  func (x *machoExe) DataStart() uint64 {
   216  	// Look for section named "__go_buildinfo".
   217  	for _, sec := range x.f.Sections {
   218  		if sec.Name == "__go_buildinfo" {
   219  			return sec.Addr
   220  		}
   221  	}
   222  	// Try the first non-empty writable segment.
   223  	const RW = 3
   224  	for _, load := range x.f.Loads {
   225  		seg, ok := load.(*macho.Segment)
   226  		if ok && seg.Addr != 0 && seg.Filesz != 0 && seg.Prot == RW && seg.Maxprot == RW {
   227  			return seg.Addr
   228  		}
   229  	}
   230  	return 0
   231  }
   232  
   233  // xcoffExe is the XCOFF (AIX eXtended COFF) implementation of the exe interface.
   234  type xcoffExe struct {
   235  	os *os.File
   236  	f  *xcoff.File
   237  }
   238  
   239  func (x *xcoffExe) Close() error {
   240  	return x.os.Close()
   241  }
   242  
   243  func (x *xcoffExe) ReadData(addr, size uint64) ([]byte, error) {
   244  	for _, sect := range x.f.Sections {
   245  		if uint64(sect.VirtualAddress) <= addr && addr <= uint64(sect.VirtualAddress+sect.Size-1) {
   246  			n := uint64(sect.VirtualAddress+sect.Size) - addr
   247  			if n > size {
   248  				n = size
   249  			}
   250  			data := make([]byte, n)
   251  			_, err := sect.ReadAt(data, int64(addr-uint64(sect.VirtualAddress)))
   252  			if err != nil {
   253  				return nil, err
   254  			}
   255  			return data, nil
   256  		}
   257  	}
   258  	return nil, fmt.Errorf("address not mapped")
   259  }
   260  
   261  func (x *xcoffExe) DataStart() uint64 {
   262  	return x.f.SectionByType(xcoff.STYP_DATA).VirtualAddress
   263  }
   264  

View as plain text