| // 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, |
| }, |
| } |