prog.go 6.13 KB
Newer Older
1 2 3 4 5 6 7 8
// 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 (
	"debug/goobj"
9
	"encoding/binary"
10 11 12 13
	"fmt"
	"go/build"
	"io"
	"os"
14
	"runtime"
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
)

// 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
36 37 38 39 40 41 42 43 44 45 46 47 48
	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
49 50 51 52

	// Input
	Packages   map[string]*Package  // loaded packages, by import path
	Syms       map[goobj.SymID]*Sym // defined symbols, by symbol ID
53 54 55
	Missing    map[goobj.SymID]bool // missing symbols
	Dead       map[goobj.SymID]bool // symbols removed as dead
	SymOrder   []*Sym               // order syms were scanned
56 57 58 59 60 61 62 63 64
	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
}

65 66 67 68
// An arch describes architecture-dependent settings.
type arch struct {
	byteorder binary.ByteOrder
	ptrsize   int
Russ Cox's avatar
Russ Cox committed
69
	pcquantum int
70
}
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103

// 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
104
	Bytes      []byte   // symbol data, for internally defined symbols
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
}

// 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()
150
	p.autoData()
151
	p.layout()
152
	p.autoConst()
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
	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
		}
	}
183 184 185
	if p.StartSym == "" {
		p.StartSym = fmt.Sprintf("_rt0_%s_%s", p.GOARCH, p.GOOS)
	}
186 187 188 189 190 191 192

	// Derive internal context.
	p.formatter = formatters[p.Format]
	if p.formatter == nil {
		p.errorf("unknown output file format %q", p.Format)
		return
	}
193 194 195 196 197 198 199 200 201
	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)
202 203 204 205 206 207 208 209 210 211 212
}

// 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{},
}
213 214 215 216 217

var arches = map[string]arch{
	"amd64": {
		byteorder: binary.LittleEndian,
		ptrsize:   8,
Russ Cox's avatar
Russ Cox committed
218
		pcquantum: 1,
219 220
	},
}