Skip to content
Snippets Groups Projects
prog.go 6.14 KiB
Newer Older
  • Learn to ignore specific revisions
  • // Copyright 2014 The Go Authors.  All rights reserved.
    // Use of this source code is governed by a BSD-style
    // license that can be found in the LICENSE file.
    
    package main
    
    import (
    
    	"cmd/internal/goobj"
    
    	"encoding/binary"
    
    	"fmt"
    	"go/build"
    	"io"
    	"os"
    
    )
    
    // A Prog holds state for constructing an executable (program) image.
    //
    // The usual sequence of operations on a Prog is:
    //
    //	p.init()
    //	p.scan(file)
    //	p.dead()
    //	p.runtime()
    //	p.layout()
    //	p.load()
    //	p.debug()
    //	p.write(w)
    //
    // p.init is in this file. The rest of the methods are in files
    // named for the method. The convenience method p.link runs
    // this sequence.
    //
    type Prog struct {
    	// Context
    
    	GOOS     string       // target operating system
    	GOARCH   string       // target architecture
    	Format   string       // desired file format ("elf", "macho", ...)
    	Error    func(string) // called to report an error (if set)
    	NumError int          // number of errors printed
    	StartSym string
    
    	// Derived context
    	arch
    	formatter   formatter
    	startSym    goobj.SymID
    	pkgdir      string
    	omitRuntime bool // do not load runtime package
    
    
    	// Input
    	Packages   map[string]*Package  // loaded packages, by import path
    	Syms       map[goobj.SymID]*Sym // defined symbols, by symbol ID
    
    	Missing    map[goobj.SymID]bool // missing symbols
    	Dead       map[goobj.SymID]bool // symbols removed as dead
    	SymOrder   []*Sym               // order syms were scanned
    
    	MaxVersion int                  // max SymID.Version, for generating fresh symbol IDs
    
    	// Output
    	UnmappedSize Addr       // size of unmapped region at address 0
    	HeaderSize   Addr       // size of object file header
    	Entry        Addr       // virtual address where execution begins
    	Segments     []*Segment // loaded memory segments
    }
    
    
    // An arch describes architecture-dependent settings.
    type arch struct {
    	byteorder binary.ByteOrder
    	ptrsize   int
    
    Russ Cox's avatar
    Russ Cox committed
    	pcquantum int
    
    
    // A formatter takes care of the details of generating a particular
    // kind of executable file.
    type formatter interface {
    	// headerSize returns the footprint of the header for p
    	// in both virtual address space and file bytes.
    	// The footprint does not include any bytes stored at the
    	// end of the file.
    	headerSize(p *Prog) (virt, file Addr)
    
    	// write writes the executable file for p to w.
    	write(w io.Writer, p *Prog)
    }
    
    // An Addr represents a virtual memory address, a file address, or a size.
    // It must be a uint64, not a uintptr, so that a 32-bit linker can still generate a 64-bit binary.
    // It must be unsigned in order to link programs placed at very large start addresses.
    // Math involving Addrs must be checked carefully not to require negative numbers.
    type Addr uint64
    
    // A Package is a Go package loaded from a file.
    type Package struct {
    	*goobj.Package        // table of contents
    	File           string // file name for reopening
    	Syms           []*Sym // symbols defined by this package
    }
    
    // A Sym is a symbol defined in a loaded package.
    type Sym struct {
    	*goobj.Sym          // symbol metadata from package file
    	Package    *Package // package defining symbol
    	Section    *Section // section where symbol is placed in output program
    	Addr       Addr     // virtual address of symbol in output program
    
    	Bytes      []byte   // symbol data, for internally defined symbols
    
    }
    
    // A Segment is a loaded memory segment.
    // A Prog is expected to have segments named "text" and optionally "data",
    // in that order, before any other segments.
    type Segment struct {
    	Name       string     // name of segment: "text", "data", ...
    	VirtAddr   Addr       // virtual memory address of segment base
    	VirtSize   Addr       // size of segment in memory
    	FileOffset Addr       // file offset of segment base
    	FileSize   Addr       // size of segment in file; can be less than VirtSize
    	Sections   []*Section // sections inside segment
    	Data       []byte     // raw data of segment image
    }
    
    // A Section is part of a loaded memory segment.
    type Section struct {
    	Name     string   // name of section: "text", "rodata", "noptrbss", and so on
    	VirtAddr Addr     // virtual memory address of section base
    	Size     Addr     // size of section in memory
    	Align    Addr     // required alignment
    	InFile   bool     // section has image data in file (like data, unlike bss)
    	Syms     []*Sym   // symbols stored in section
    	Segment  *Segment // segment containing section
    }
    
    func (p *Prog) errorf(format string, args ...interface{}) {
    	if p.Error != nil {
    		p.Error(fmt.Sprintf(format, args...))
    	} else {
    		fmt.Fprintf(os.Stderr, format+"\n", args...)
    	}
    	p.NumError++
    }
    
    // link is the one-stop convenience method for running a link.
    // It writes to w the object file generated from using mainFile as the main package.
    func (p *Prog) link(w io.Writer, mainFile string) {
    	p.init()
    	p.scan(mainFile)
    	if p.NumError > 0 {
    		return
    	}
    	p.dead()
    	p.runtime()
    
    	if p.NumError > 0 {
    		return
    	}
    	p.load()
    	if p.NumError > 0 {
    		return
    	}
    	p.debug()
    	if p.NumError > 0 {
    		return
    	}
    	p.write(w)
    }
    
    // init initializes p for use by the other methods.
    func (p *Prog) init() {
    	// Set default context if not overridden.
    	if p.GOOS == "" {
    		p.GOOS = build.Default.GOOS
    	}
    	if p.GOARCH == "" {
    		p.GOARCH = build.Default.GOARCH
    	}
    	if p.Format == "" {
    		p.Format = goosFormat[p.GOOS]
    		if p.Format == "" {
    			p.errorf("no default file format for GOOS %q", p.GOOS)
    			return
    		}
    	}
    
    	if p.StartSym == "" {
    		p.StartSym = fmt.Sprintf("_rt0_%s_%s", p.GOARCH, p.GOOS)
    	}
    
    
    	// Derive internal context.
    	p.formatter = formatters[p.Format]
    	if p.formatter == nil {
    		p.errorf("unknown output file format %q", p.Format)
    		return
    	}
    
    	p.startSym = goobj.SymID{Name: p.StartSym}
    	arch, ok := arches[p.GOARCH]
    	if !ok {
    		p.errorf("unknown GOOS %q", p.GOOS)
    		return
    	}
    	p.arch = arch
    
    	p.pkgdir = fmt.Sprintf("%s/pkg/%s_%s", runtime.GOROOT(), p.GOOS, p.GOARCH)
    
    }
    
    // goosFormat records the default format for each known GOOS value.
    var goosFormat = map[string]string{
    	"darwin": "darwin",
    }
    
    // formatters records the format implementation for each known format value.
    var formatters = map[string]formatter{
    	"darwin": machoFormat{},
    }
    
    
    var arches = map[string]arch{
    	"amd64": {
    		byteorder: binary.LittleEndian,
    		ptrsize:   8,
    
    Russ Cox's avatar
    Russ Cox committed
    		pcquantum: 1,