...

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

     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