github.com/vchain-us/vcn@v0.9.11-0.20210921212052-a2484d23c0b3/pkg/bom/golang/exe.go (about)

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