mirror of https://github.com/golang/go.git
221 lines
6.1 KiB
Go
221 lines
6.1 KiB
Go
// 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"
|
|
"runtime"
|
|
)
|
|
|
|
// 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
|
|
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()
|
|
p.autoData()
|
|
p.layout()
|
|
p.autoConst()
|
|
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,
|
|
pcquantum: 1,
|
|
},
|
|
}
|