blob: 77fb1ece5b1685c09358973870dadf7001830621 [file] [log] [blame]
// 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 (
// 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 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
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...)
// 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) {
if p.NumError > 0 {
if p.NumError > 0 {
if p.NumError > 0 {
if p.NumError > 0 {
// 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)
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)
p.startSym = goobj.SymID{Name: p.StartSym}
arch, ok := arches[p.GOARCH]
if !ok {
p.errorf("unknown GOOS %q", p.GOOS)
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,