Commit fb880b8a authored by Russ Cox's avatar Russ Cox

cmd/newlink: delete

cmd/link is clearly the way forward.
The original rationale for cmd/newlink was that it would be a clean Go reimplementation.
But when push came to shove, cmd/link got converted from C instead,
and all the work on build modes and the like is in cmd/link now.
Cleaning up cmd/link is likely a much better plan.

This directory is something to delete from releases and the
testdata is something that breaks every time the .6 format changes.
Fix both problems by just deleting it outright.

Change-Id: Ib00fecda258ba685f1752725971182af9d4459eb
Reviewed-on: https://go-review.googlesource.com/20380
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: default avatarIan Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent c4012b6b
...@@ -44,9 +44,6 @@ go src=.. ...@@ -44,9 +44,6 @@ go src=..
gofmt_test.go gofmt_test.go
testdata testdata
+ +
newlink
testdata
+
archive archive
tar tar
testdata testdata
......
// 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.
// Automatic symbol generation.
// TODO(rsc): Handle go.typelink, go.track symbols.
// TODO(rsc): Do not handle $f64. and $f32. symbols. Instead, generate those
// from the compiler and assemblers as dupok data, and then remove autoData below.
package main
import (
"cmd/internal/goobj"
"strconv"
"strings"
)
// linkerDefined lists the symbols supplied by other parts of the linker
// (runtime.go and layout.go).
var linkerDefined = map[string]bool{
"runtime.bss": true,
"runtime.data": true,
"runtime.ebss": true,
"runtime.edata": true,
"runtime.efunctab": true,
"runtime.end": true,
"runtime.enoptrbss": true,
"runtime.enoptrdata": true,
"runtime.erodata": true,
"runtime.etext": true,
"runtime.etypelink": true,
"runtime.functab": true,
"runtime.gcbss": true,
"runtime.gcdata": true,
"runtime.noptrbss": true,
"runtime.noptrdata": true,
"runtime.pclntab": true,
"runtime.rodata": true,
"runtime.text": true,
"runtime.typelink": true,
}
// isAuto reports whether sym is an automatically-generated data or constant symbol.
func (p *Prog) isAuto(sym goobj.SymID) bool {
return strings.HasPrefix(sym.Name, "$f64.") ||
strings.HasPrefix(sym.Name, "$f32.") ||
linkerDefined[sym.Name]
}
// autoData defines the automatically generated data symbols needed by p.
func (p *Prog) autoData() {
for sym := range p.Missing {
switch {
// Floating-point constants that need to be loaded from memory are
// written as $f64.{16 hex digits} or $f32.{8 hex digits}; the hex digits
// give the IEEE bit pattern of the constant. As far as the layout into
// memory is concerned, we interpret these as uint64 or uint32 constants.
case strings.HasPrefix(sym.Name, "$f64."), strings.HasPrefix(sym.Name, "$f32."):
size := 64
if sym.Name[2:4] == "32" {
size = 32
}
delete(p.Missing, sym)
fbits, err := strconv.ParseUint(sym.Name[len("$f64."):], 16, size)
if err != nil {
p.errorf("unexpected floating point symbol %s", sym)
continue
}
data := make([]byte, size/8)
if size == 64 {
p.byteorder.PutUint64(data, fbits)
} else {
p.byteorder.PutUint32(data, uint32(fbits))
}
p.addSym(&Sym{
Sym: &goobj.Sym{
SymID: sym,
Kind: goobj.SRODATA,
Size: size / 8,
},
Bytes: data,
})
}
}
}
// defineConst defines a new symbol with the given name and constant address.
func (p *Prog) defineConst(name string, addr Addr) {
sym := goobj.SymID{Name: name}
p.addSym(&Sym{
Sym: &goobj.Sym{
SymID: sym,
Kind: goobj.SCONST,
},
Package: nil,
Addr: addr,
})
}
// 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.
// Test for auto-generated symbols.
// There is no test for $f64. and $f32. symbols, because those are
// not possible to write in the assembler syntax. Instead of changing
// the assembler to allow that, we plan to change the compilers
// not to generate such symbols (plain dupok data is sufficient).
package main
import (
"bytes"
"cmd/internal/goobj"
"testing"
)
// Each test case is an object file, generated from a corresponding .s file.
// The image of the autotab symbol should be a sequence of pairs of
// identical 8-byte sequences.
var autoTests = []string{
"testdata/autosection.6",
}
func TestAuto(t *testing.T) {
for _, obj := range autoTests {
p := Prog{GOOS: "darwin", GOARCH: "amd64", StartSym: "start"}
p.omitRuntime = true
p.Error = func(s string) { t.Error(s) }
var buf bytes.Buffer
p.link(&buf, obj)
if p.NumError > 0 {
continue // already reported
}
const name = "autotab"
sym := p.Syms[goobj.SymID{Name: name}]
if sym == nil {
t.Errorf("%s is missing %s symbol", obj, name)
return
}
if sym.Size == 0 {
return
}
seg := sym.Section.Segment
off := sym.Addr - seg.VirtAddr
data := seg.Data[off : off+Addr(sym.Size)]
if len(data)%16 != 0 {
t.Errorf("%s: %s.Size = %d, want multiple of 16", obj, name, len(data))
return
}
Data:
for i := 0; i < len(data); i += 16 {
have := p.byteorder.Uint64(data[i : i+8])
want := p.byteorder.Uint64(data[i+8 : i+16])
if have != want {
// Look for relocation so we can explain what went wrong.
for _, r := range sym.Reloc {
if r.Offset == i {
t.Errorf("%s: %s+%#x: %s: have %#x want %#x", obj, name, i, r.Sym, have, want)
continue Data
}
}
t.Errorf("%s: %s+%#x: have %#x want %#x", obj, name, i, have, want)
}
}
}
}
// 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.
// Removal of dead code and data.
package main
import "cmd/internal/goobj"
// dead removes unreachable code and data from the program.
// It is basically a mark-sweep garbage collection: traverse all the
// symbols reachable from the entry (startSymID) and then delete
// the rest.
func (p *Prog) dead() {
p.Dead = make(map[goobj.SymID]bool)
reachable := make(map[goobj.SymID]bool)
p.walkDead(p.startSym, reachable)
for sym := range p.Syms {
if !reachable[sym] {
delete(p.Syms, sym)
p.Dead[sym] = true
}
}
for sym := range p.Missing {
if !reachable[sym] {
delete(p.Missing, sym)
p.Dead[sym] = true
}
}
p.SymOrder = removeDead(p.SymOrder, reachable)
for _, pkg := range p.Packages {
pkg.Syms = removeDead(pkg.Syms, reachable)
}
}
// walkDead traverses the symbols reachable from sym, adding them to reachable.
// The caller has verified that reachable[sym] = false.
func (p *Prog) walkDead(sym goobj.SymID, reachable map[goobj.SymID]bool) {
reachable[sym] = true
s := p.Syms[sym]
if s == nil {
return
}
for i := range s.Reloc {
r := &s.Reloc[i]
if !reachable[r.Sym] {
p.walkDead(r.Sym, reachable)
}
}
if s.Func != nil {
for _, fdata := range s.Func.FuncData {
if fdata.Sym.Name != "" && !reachable[fdata.Sym] {
p.walkDead(fdata.Sym, reachable)
}
}
}
}
// removeDead removes unreachable (dead) symbols from syms,
// returning a shortened slice using the same underlying array.
func removeDead(syms []*Sym, reachable map[goobj.SymID]bool) []*Sym {
keep := syms[:0]
for _, sym := range syms {
if reachable[sym.SymID] {
keep = append(keep, sym)
}
}
return keep
}
// 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"
"reflect"
"strings"
"testing"
)
// Each test case is an object file, generated from a corresponding .s file.
// The symbols in the object file with a dead_ prefix are the ones that
// should be removed from the program.
var deadTests = []string{
"testdata/dead.6",
}
func TestDead(t *testing.T) {
for _, obj := range deadTests {
p := Prog{GOOS: "darwin", GOARCH: "amd64", StartSym: "start"}
p.omitRuntime = true
p.Error = func(s string) { t.Error(s) }
p.init()
p.scan(obj)
if p.NumError > 0 {
continue // already reported
}
origSyms := copyMap(p.Syms)
origMissing := copyMap(p.Missing)
origSymOrder := copySlice(p.SymOrder)
origPkgSyms := copySlice(p.Packages["main"].Syms)
p.dead()
checkDeadMap(t, obj, "p.Syms", origSyms, p.Syms)
checkDeadMap(t, obj, "p.Missing", origMissing, p.Missing)
checkDeadSlice(t, obj, "p.SymOrder", origSymOrder, p.SymOrder)
checkDeadSlice(t, obj, `p.Packages["main"].Syms`, origPkgSyms, p.Packages["main"].Syms)
}
}
func copyMap(m interface{}) interface{} {
v := reflect.ValueOf(m)
out := reflect.MakeMap(v.Type())
for _, key := range v.MapKeys() {
out.SetMapIndex(key, v.MapIndex(key))
}
return out.Interface()
}
func checkDeadMap(t *testing.T, obj, name string, old, new interface{}) {
vold := reflect.ValueOf(old)
vnew := reflect.ValueOf(new)
for _, vid := range vold.MapKeys() {
id := vid.Interface().(goobj.SymID)
if strings.HasPrefix(id.Name, "dead_") {
if vnew.MapIndex(vid).IsValid() {
t.Errorf("%s: %s contains unnecessary symbol %s", obj, name, id)
}
} else {
if !vnew.MapIndex(vid).IsValid() {
t.Errorf("%s: %s is missing symbol %s", obj, name, id)
}
}
}
for _, vid := range vnew.MapKeys() {
id := vid.Interface().(goobj.SymID)
if !vold.MapIndex(vid).IsValid() {
t.Errorf("%s: %s contains unexpected symbol %s", obj, name, id)
}
}
}
func copySlice(x []*Sym) (out []*Sym) {
return append(out, x...)
}
func checkDeadSlice(t *testing.T, obj, name string, old, new []*Sym) {
for i, s := range old {
if strings.HasPrefix(s.Name, "dead_") {
continue
}
if len(new) == 0 {
t.Errorf("%s: %s is missing symbol %s\nhave%v\nwant%v", obj, name, s, new, old[i:])
return
}
if new[0].SymID != s.SymID {
t.Errorf("%s: %s is incorrect: have %s, want %s\nhave%v\nwant%v", obj, name, new[0].SymID, s.SymID, new, old[i:])
return
}
new = new[1:]
}
if len(new) > 0 {
t.Errorf("%s: %s has unexpected symbols: %v", obj, name, new)
}
}
// 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.
// Generation of debug data structures (in the executable but not mapped at run time).
// See also runtime.go.
package main
func (p *Prog) debug() {
}
// 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 (
"encoding/hex"
"fmt"
"io/ioutil"
"regexp"
"strconv"
"strings"
"testing"
)
// mustParseHexdumpFile returns a block of data generated by
// parsing the hex dump in the named file.
// If the file cannot be read or does not contain a valid hex dump,
// mustParseHexdumpFile calls t.Fatal.
func mustParseHexdumpFile(t *testing.T, file string) []byte {
hex, err := ioutil.ReadFile(file)
if err != nil {
t.Fatal(err)
}
data, err := parseHexdump(string(hex))
if err != nil {
t.Fatal(err)
}
return data
}
// parseHexdump parses the hex dump in text, which should be the
// output of "hexdump -C" or Plan 9's "xd -b",
// and returns the original data used to produce the dump.
// It is meant to enable storing golden binary files as text, so that
// changes to the golden files can be seen during code reviews.
func parseHexdump(text string) ([]byte, error) {
var out []byte
for _, line := range strings.Split(text, "\n") {
if i := strings.Index(line, "|"); i >= 0 { // remove text dump
line = line[:i]
}
f := strings.Fields(line)
if len(f) > 1+16 {
return nil, fmt.Errorf("parsing hex dump: too many fields on line %q", line)
}
if len(f) == 0 || len(f) == 1 && f[0] == "*" { // all zeros block omitted
continue
}
addr64, err := strconv.ParseUint(f[0], 16, 0)
if err != nil {
return nil, fmt.Errorf("parsing hex dump: invalid address %q", f[0])
}
addr := int(addr64)
if len(out) < addr {
out = append(out, make([]byte, addr-len(out))...)
}
for _, x := range f[1:] {
val, err := strconv.ParseUint(x, 16, 8)
if err != nil {
return nil, fmt.Errorf("parsing hexdump: invalid hex byte %q", x)
}
out = append(out, byte(val))
}
}
return out, nil
}
func hexdump(data []byte) string {
text := hex.Dump(data) + fmt.Sprintf("%08x\n", len(data))
text = regexp.MustCompile(`\n([0-9a-f]+(\s+00){16}.*\n)+`).ReplaceAllString(text, "\n*\n")
return text
}
// 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.
// Executable image layout - address assignment.
package main
import (
"cmd/internal/goobj"
)
// A layoutSection describes a single section to add to the
// final executable. Go binaries only have a fixed set of possible
// sections, and the symbol kind determines the section.
type layoutSection struct {
Segment string
Section string
Kind goobj.SymKind
Index int
}
// layout defines the layout of the generated Go executable.
// The order of entries here is the order in the executable.
// Entries with the same Segment name must be contiguous.
var layout = []layoutSection{
{Segment: "text", Section: "text", Kind: goobj.STEXT},
{Segment: "rodata", Section: "rodata", Kind: goobj.SRODATA},
{Segment: "rodata", Section: "functab", Kind: goobj.SPCLNTAB},
{Segment: "rodata", Section: "typelink", Kind: goobj.STYPELINK},
{Segment: "data", Section: "noptrdata", Kind: goobj.SNOPTRDATA},
{Segment: "data", Section: "data", Kind: goobj.SDATA},
{Segment: "data", Section: "bss", Kind: goobj.SBSS},
{Segment: "data", Section: "noptrbss", Kind: goobj.SNOPTRBSS},
// Later:
// {"rodata", "type", goobj.STYPE},
// {"rodata", "string", goobj.SSTRING},
// {"rodata", "gostring", goobj.SGOSTRING},
// {"rodata", "gofunc", goobj.SGOFUNC},
}
// layoutByKind maps from SymKind to an entry in layout.
var layoutByKind []*layoutSection
func init() {
// Build index from symbol type to layout entry.
max := 0
for _, sect := range layout {
if max <= int(sect.Kind) {
max = int(sect.Kind) + 1
}
}
layoutByKind = make([]*layoutSection, max)
for i := range layout {
sect := &layout[i]
layoutByKind[sect.Kind] = sect
sect.Index = i
}
}
// layout arranges symbols into sections and sections into segments,
// and then it assigns addresses to segments, sections, and symbols.
func (p *Prog) layout() {
sections := make([]*Section, len(layout))
// Assign symbols to sections using index, creating sections as needed.
// Could keep sections separated by type during input instead.
for _, sym := range p.SymOrder {
kind := sym.Kind
if kind < 0 || int(kind) >= len(layoutByKind) || layoutByKind[kind] == nil {
p.errorf("%s: unexpected symbol kind %v", sym.SymID, kind)
continue
}
lsect := layoutByKind[kind]
sect := sections[lsect.Index]
if sect == nil {
sect = &Section{
Name: lsect.Section,
Align: 1,
}
sections[lsect.Index] = sect
}
if sym.Data.Size > 0 || len(sym.Bytes) > 0 {
sect.InFile = true
}
sym.Section = sect
sect.Syms = append(sect.Syms, sym)
// TODO(rsc): Incorporate alignment information.
// First that information needs to be added to the object files.
//
// if sect.Align < Addr(sym.Align) {
// sect.Align = Addr(sym.Align)
// }
}
// Assign sections to segments, creating segments as needed.
var seg *Segment
for i, sect := range sections {
if sect == nil {
continue
}
segName := layout[i].Segment
// Special case: Mach-O does not support "rodata" segment,
// so store read-only data in text segment.
if p.GOOS == "darwin" && segName == "rodata" {
segName = "text"
}
if seg == nil || seg.Name != segName {
seg = &Segment{
Name: segName,
}
p.Segments = append(p.Segments, seg)
}
sect.Segment = seg
seg.Sections = append(seg.Sections, sect)
}
// Assign addresses.
// TODO(rsc): This choice needs to be informed by both
// the formatter and the target architecture.
// And maybe eventually a command line flag (sigh).
const segAlign = 4096
// TODO(rsc): Use a larger amount on most systems, which will let the
// compiler eliminate more nil checks.
if p.UnmappedSize == 0 {
p.UnmappedSize = segAlign
}
// TODO(rsc): addr := Addr(0) when generating a shared library or PIE.
addr := p.UnmappedSize
// Account for initial file header.
hdrVirt, hdrFile := p.formatter.headerSize(p)
addr += hdrVirt
// Assign addresses to segments, sections, symbols.
// Assign sizes to segments, sections.
startVirt := addr
startFile := hdrFile
for _, seg := range p.Segments {
addr = round(addr, segAlign)
seg.VirtAddr = addr
seg.FileOffset = startFile + seg.VirtAddr - startVirt
for _, sect := range seg.Sections {
addr = round(addr, sect.Align)
sect.VirtAddr = addr
for _, sym := range sect.Syms {
// TODO(rsc): Respect alignment once we have that information.
sym.Addr = addr
addr += Addr(sym.Size)
}
sect.Size = addr - sect.VirtAddr
if sect.InFile {
seg.FileSize = addr - seg.VirtAddr
}
}
seg.VirtSize = addr - seg.VirtAddr
}
// Define symbols for section names.
var progEnd Addr
for i, sect := range sections {
name := layout[i].Section
var start, end Addr
if sect != nil {
start = sect.VirtAddr
end = sect.VirtAddr + sect.Size
}
p.defineConst("runtime."+name, start)
p.defineConst("runtime.e"+name, end)
progEnd = end
}
p.defineConst("runtime.end", progEnd)
}
// 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 (
"bytes"
"strings"
"testing"
)
func TestLayout(t *testing.T) {
p := Prog{GOOS: "darwin", GOARCH: "amd64", StartSym: "text_start"}
p.omitRuntime = true
p.Error = func(s string) { t.Error(s) }
var buf bytes.Buffer
const obj = "testdata/layout.6"
p.link(&buf, obj)
if p.NumError > 0 {
return // already reported
}
if len(p.Dead) > 0 {
t.Errorf("%s: unexpected dead symbols %v", obj, p.Dead)
return
}
for _, sym := range p.SymOrder {
if p.isAuto(sym.SymID) {
continue
}
if sym.Section == nil {
t.Errorf("%s: symbol %s is missing section", obj, sym)
continue
}
i := strings.Index(sym.Name, "_")
if i < 0 {
t.Errorf("%s: unexpected symbol %s", obj, sym)
continue
}
if sym.Section.Name != sym.Name[:i] {
t.Errorf("%s: symbol %s in section %s, want %s", obj, sym, sym.Section.Name, sym.Name[:i])
}
}
}
// 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 (
"bytes"
"cmd/internal/goobj"
"io/ioutil"
"testing"
)
func TestLinkHello(t *testing.T) {
p := &Prog{
GOOS: "darwin",
GOARCH: "amd64",
Error: func(s string) { t.Error(s) },
StartSym: "_rt0_go",
}
var buf bytes.Buffer
p.link(&buf, "testdata/hello.6")
if p.NumError > 0 {
return
}
if p.Syms[goobj.SymID{"_rt0_go", 0}] == nil || p.Syms[goobj.SymID{"hello", 1}] == nil {
t.Errorf("Syms = %v, want at least [_rt0_go hello<1>]", p.Syms)
}
// uncomment to leave file behind for execution:
if false {
ioutil.WriteFile("a.out", buf.Bytes(), 0777)
}
checkGolden(t, buf.Bytes(), "testdata/link.hello.darwin.amd64")
}
// 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.
// Loading of code and data fragments from package files into final image.
package main
import (
"cmd/internal/obj"
"os"
)
// load allocates segment images, populates them with data
// read from package files, and applies relocations to the data.
func (p *Prog) load() {
// TODO(rsc): mmap the output file and store the data directly.
// That will make writing the output file more efficient.
for _, seg := range p.Segments {
seg.Data = make([]byte, seg.FileSize)
}
for _, pkg := range p.Packages {
p.loadPackage(pkg)
}
}
// loadPackage loads and relocates data for all the
// symbols needed in the given package.
func (p *Prog) loadPackage(pkg *Package) {
if pkg.File == "" {
// This "package" contains internally generated symbols only.
// All such symbols have a sym.Bytes field holding the actual data
// (if any), plus relocations.
for _, sym := range pkg.Syms {
if sym.Bytes == nil {
continue
}
seg := sym.Section.Segment
off := sym.Addr - seg.VirtAddr
data := seg.Data[off : off+Addr(sym.Size)]
copy(data, sym.Bytes)
p.relocateSym(sym, data)
}
return
}
// Package stored in file.
f, err := os.Open(pkg.File)
if err != nil {
p.errorf("%v", err)
return
}
defer f.Close()
// TODO(rsc): Mmap file into memory.
for _, sym := range pkg.Syms {
if sym.Data.Size == 0 {
continue
}
// TODO(rsc): If not using mmap, at least coalesce nearby reads.
if sym.Section == nil {
p.errorf("internal error: missing section for %s", sym.Name)
}
seg := sym.Section.Segment
off := sym.Addr - seg.VirtAddr
if off >= Addr(len(seg.Data)) || off+Addr(sym.Data.Size) > Addr(len(seg.Data)) {
p.errorf("internal error: allocated space for %s too small: %d bytes for %d+%d (%d)", sym, len(seg.Data), off, sym.Data.Size, sym.Size)
}
data := seg.Data[off : off+Addr(sym.Data.Size)]
_, err := f.ReadAt(data, sym.Data.Offset)
if err != nil {
p.errorf("reading %v: %v", sym.SymID, err)
}
p.relocateSym(sym, data)
}
}
// relocateSym applies relocations to sym's data.
func (p *Prog) relocateSym(sym *Sym, data []byte) {
for i := range sym.Reloc {
r := &sym.Reloc[i]
targ := p.Syms[r.Sym]
if targ == nil {
p.errorf("%v: reference to undefined symbol %v", sym, r.Sym)
continue
}
val := targ.Addr + Addr(r.Add)
switch r.Type {
default:
p.errorf("%v: unknown relocation type %d", sym, r.Type)
case obj.R_ADDR, obj.R_CALLIND:
// ok
case obj.R_PCREL, obj.R_CALL:
val -= sym.Addr + Addr(r.Offset+r.Size)
}
frag := data[r.Offset : r.Offset+r.Size]
switch r.Size {
default:
p.errorf("%v: unknown relocation size %d", sym, r.Size)
case 4:
// TODO(rsc): Check for overflow?
p.byteorder.PutUint32(frag, uint32(val))
case 8:
p.byteorder.PutUint64(frag, uint64(val))
}
}
}
// 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.
// Mach-O (Darwin) object file writing.
package main
import (
"debug/macho"
"encoding/binary"
"io"
"strings"
)
// machoFormat is the implementation of formatter.
type machoFormat struct{}
// machoHeader and friends are data structures
// corresponding to the Mach-O file header
// to be written to disk.
const (
macho64Bit = 1 << 24
machoSubCPU386 = 3
)
// machoArch describes a Mach-O target architecture.
type machoArch struct {
CPU uint32
SubCPU uint32
}
// machoHeader is the Mach-O file header.
type machoHeader struct {
machoArch
FileType uint32
Loads []*machoLoad
Segments []*machoSegment
p *Prog // for reporting errors
}
// machoLoad is a Mach-O load command.
type machoLoad struct {
Type uint32
Data []uint32
}
// machoSegment is a Mach-O segment.
type machoSegment struct {
Name string
VirtAddr Addr
VirtSize Addr
FileOffset Addr
FileSize Addr
Prot1 uint32
Prot2 uint32
Flags uint32
Sections []*machoSection
}
// machoSection is a Mach-O section, inside a segment.
type machoSection struct {
Name string
Segment string
Addr Addr
Size Addr
Offset uint32
Align uint32
Reloc uint32
Nreloc uint32
Flags uint32
Res1 uint32
Res2 uint32
}
// layout positions the segments and sections in p
// to make room for the Mach-O file header.
// That is, it edits their VirtAddr fields to adjust for the presence
// of the Mach-O header at the beginning of the address space.
func (machoFormat) headerSize(p *Prog) (virt, file Addr) {
var h machoHeader
h.init(p)
size := Addr(h.size())
size = round(size, 4096)
p.HeaderSize = size
return size, size
}
// write writes p to w as a Mach-O executable.
// layout(p) must have already been called,
// and the number, sizes, and addresses of the segments
// and sections must not have been modified since the call.
func (machoFormat) write(w io.Writer, p *Prog) {
var h machoHeader
h.init(p)
off := Addr(0)
enc := h.encode()
w.Write(enc)
off += Addr(len(enc))
for _, seg := range p.Segments {
if seg.FileOffset < off {
h.p.errorf("mach-o error: invalid file offset")
}
w.Write(make([]byte, int(seg.FileOffset-off)))
if seg.FileSize != Addr(len(seg.Data)) {
h.p.errorf("mach-o error: invalid file size")
}
w.Write(seg.Data)
off = seg.FileOffset + Addr(len(seg.Data))
}
}
// Conversion of Prog to macho data structures.
// machoArches maps from GOARCH to machoArch.
var machoArches = map[string]machoArch{
"amd64": {
CPU: uint32(macho.CpuAmd64),
SubCPU: uint32(machoSubCPU386),
},
}
// init initializes the header h to describe p.
func (h *machoHeader) init(p *Prog) {
h.p = p
h.Segments = nil
h.Loads = nil
var ok bool
h.machoArch, ok = machoArches[p.GOARCH]
if !ok {
p.errorf("mach-o: unknown target GOARCH %q", p.GOARCH)
return
}
h.FileType = uint32(macho.TypeExec)
mseg := h.addSegment(p, "__PAGEZERO", nil)
mseg.VirtSize = p.UnmappedSize
for _, seg := range p.Segments {
h.addSegment(p, "__"+strings.ToUpper(seg.Name), seg)
}
var data []uint32
switch h.CPU {
default:
p.errorf("mach-o: unknown cpu %#x for GOARCH %q", h.CPU, p.GOARCH)
case uint32(macho.CpuAmd64):
data = make([]uint32, 2+42)
data[0] = 4 // thread type
data[1] = 42 // word count
data[2+32] = uint32(p.Entry) // RIP register, in two parts
data[2+32+1] = uint32(p.Entry >> 32)
}
h.Loads = append(h.Loads, &machoLoad{
Type: uint32(macho.LoadCmdUnixThread),
Data: data,
})
}
// addSegment adds to h a Mach-O segment like seg with the given name.
func (h *machoHeader) addSegment(p *Prog, name string, seg *Segment) *machoSegment {
mseg := &machoSegment{
Name: name,
}
h.Segments = append(h.Segments, mseg)
if seg == nil {
return mseg
}
mseg.VirtAddr = seg.VirtAddr
mseg.VirtSize = seg.VirtSize
mseg.FileOffset = round(seg.FileOffset, 4096)
mseg.FileSize = seg.FileSize
if name == "__TEXT" {
// Initially RWX, then just RX
mseg.Prot1 = 7
mseg.Prot2 = 5
// Text segment maps Mach-O header, needed by dynamic linker.
mseg.VirtAddr -= p.HeaderSize
mseg.VirtSize += p.HeaderSize
mseg.FileOffset -= p.HeaderSize
mseg.FileSize += p.HeaderSize
} else {
// RW
mseg.Prot1 = 3
mseg.Prot2 = 3
}
for _, sect := range seg.Sections {
h.addSection(mseg, seg, sect)
}
return mseg
}
// addSection adds to mseg a Mach-O section like sect, inside seg, with the given name.
func (h *machoHeader) addSection(mseg *machoSegment, seg *Segment, sect *Section) {
msect := &machoSection{
Name: "__" + sect.Name,
Segment: mseg.Name,
// Reloc: sect.RelocOffset,
// NumReloc: sect.RelocLen / 8,
Addr: sect.VirtAddr,
Size: sect.Size,
}
mseg.Sections = append(mseg.Sections, msect)
for 1<<msect.Align < sect.Align {
msect.Align++
}
if off := sect.VirtAddr - seg.VirtAddr; off < seg.FileSize {
// Data in file.
if sect.Size > seg.FileSize-off {
h.p.errorf("mach-o error: section crosses file boundary")
}
msect.Offset = uint32(seg.FileOffset + off)
} else {
// Zero filled.
msect.Flags |= 1
}
if sect.Name == "text" {
msect.Flags |= 0x400 // contains executable instructions
}
}
// A machoWriter helps write Mach-O headers.
// It is basically a buffer with some helper routines for writing integers.
type machoWriter struct {
dst []byte
tmp [8]byte
order binary.ByteOrder
is64 bool
p *Prog
}
// if64 returns x if w is writing a 64-bit object file; otherwise it returns y.
func (w *machoWriter) if64(x, y interface{}) interface{} {
if w.is64 {
return x
}
return y
}
// encode encodes each of the given arguments into the writer.
// It encodes uint32, []uint32, uint64, and []uint64 by writing each value
// in turn in the correct byte order for the output file.
// It encodes an Addr as a uint64 if writing a 64-bit output file, or else as a uint32.
// It encodes []byte and string by writing the raw bytes (no length prefix).
// It skips nil values in the args list.
func (w *machoWriter) encode(args ...interface{}) {
for _, arg := range args {
switch arg := arg.(type) {
default:
w.p.errorf("mach-o error: cannot encode %T", arg)
case nil:
// skip
case []byte:
w.dst = append(w.dst, arg...)
case string:
w.dst = append(w.dst, arg...)
case uint32:
w.order.PutUint32(w.tmp[:], arg)
w.dst = append(w.dst, w.tmp[:4]...)
case []uint32:
for _, x := range arg {
w.order.PutUint32(w.tmp[:], x)
w.dst = append(w.dst, w.tmp[:4]...)
}
case uint64:
w.order.PutUint64(w.tmp[:], arg)
w.dst = append(w.dst, w.tmp[:8]...)
case Addr:
if w.is64 {
w.order.PutUint64(w.tmp[:], uint64(arg))
w.dst = append(w.dst, w.tmp[:8]...)
} else {
if Addr(uint32(arg)) != arg {
w.p.errorf("mach-o error: truncating address %#x to uint32", arg)
}
w.order.PutUint32(w.tmp[:], uint32(arg))
w.dst = append(w.dst, w.tmp[:4]...)
}
}
}
}
// segmentSize returns the size of the encoding of seg in bytes.
func (w *machoWriter) segmentSize(seg *machoSegment) int {
if w.is64 {
return 18*4 + 20*4*len(seg.Sections)
}
return 14*4 + 22*4*len(seg.Sections)
}
// zeroPad returns the string s truncated or padded with NULs to n bytes.
func zeroPad(s string, n int) string {
if len(s) >= n {
return s[:n]
}
return s + strings.Repeat("\x00", n-len(s))
}
// size returns the encoded size of the header.
func (h *machoHeader) size() int {
// Could write separate code, but encoding is cheap; encode and throw it away.
return len(h.encode())
}
// encode returns the Mach-O encoding of the header.
func (h *machoHeader) encode() []byte {
w := &machoWriter{p: h.p}
w.is64 = h.CPU&macho64Bit != 0
w.order = w.p.byteorder
loadSize := 0
for _, seg := range h.Segments {
loadSize += w.segmentSize(seg)
}
for _, l := range h.Loads {
loadSize += 4 * (2 + len(l.Data))
}
w.encode(
w.if64(macho.Magic64, macho.Magic32),
uint32(h.CPU),
uint32(h.SubCPU),
uint32(h.FileType),
uint32(len(h.Loads)+len(h.Segments)),
uint32(loadSize),
uint32(1),
w.if64(uint32(0), nil),
)
for _, seg := range h.Segments {
w.encode(
w.if64(uint32(macho.LoadCmdSegment64), uint32(macho.LoadCmdSegment)),
uint32(w.segmentSize(seg)),
zeroPad(seg.Name, 16),
seg.VirtAddr,
seg.VirtSize,
seg.FileOffset,
seg.FileSize,
seg.Prot1,
seg.Prot2,
uint32(len(seg.Sections)),
seg.Flags,
)
for _, sect := range seg.Sections {
w.encode(
zeroPad(sect.Name, 16),
zeroPad(seg.Name, 16),
sect.Addr,
sect.Size,
sect.Offset,
sect.Align,
sect.Reloc,
sect.Nreloc,
sect.Flags,
sect.Res1,
sect.Res2,
w.if64(uint32(0), nil),
)
}
}
for _, load := range h.Loads {
w.encode(
load.Type,
uint32(4*(2+len(load.Data))),
load.Data,
)
}
return w.dst
}
This diff is collapsed.
// 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.
// Placeholder to keep build building.
package main
func main() {}
This diff is collapsed.
// 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 (
"bytes"
"cmd/internal/goobj"
"fmt"
"math/rand"
"sort"
"strings"
"testing"
)
// Test of pcln table encoding.
// testdata/genpcln.go generates an assembly file with
// pseudorandom values for the data that pclntab stores.
// This test recomputes the same pseudorandom stream
// and checks that the final linked binary uses those values
// as well.
func TestPclntab(t *testing.T) {
p := &Prog{
GOOS: "darwin",
GOARCH: "amd64",
Error: func(s string) { t.Error(s) },
StartSym: "start",
omitRuntime: true,
}
var buf bytes.Buffer
p.link(&buf, "testdata/pclntab.6")
if p.NumError > 0 {
return
}
// The algorithm for computing values here must match
// the one in testdata/genpcln.go.
for f := 0; f < 3; f++ {
file := "input"
line := 1
rnd := rand.New(rand.NewSource(int64(f)))
args := rnd.Intn(100) * 8
frame := 32 + rnd.Intn(32)/8*8
size := 200 + rnd.Intn(100)*8
name := fmt.Sprintf("func%d", f)
r, off, fargs, fframe, ok := findFunc(t, p, name)
if !ok {
continue // error already printed
}
if fargs != args {
t.Errorf("%s: args=%d, want %d", name, fargs, args)
}
if fframe != frame+8 {
t.Errorf("%s: frame=%d, want %d", name, fframe, frame+8)
}
// Check FUNCDATA 1.
fdata, ok := loadFuncdata(t, r, name, off, 1)
if ok {
fsym := p.Syms[goobj.SymID{Name: fmt.Sprintf("funcdata%d", f)}]
if fsym == nil {
t.Errorf("funcdata%d is missing in binary", f)
} else if fdata != fsym.Addr {
t.Errorf("%s: funcdata 1 = %#x, want %#x", name, fdata, fsym.Addr)
}
}
// Walk code checking pcdata values.
spadj := 0
pcdata1 := -1
pcdata2 := -1
checkPCSP(t, r, name, off, 0, 0)
checkPCData(t, r, name, off, 0, 0, -1)
checkPCData(t, r, name, off, 0, 1, -1)
checkPCData(t, r, name, off, 0, 2, -1)
firstpc := 4
for i := 0; i < size; i++ {
pc := firstpc + i // skip SP adjustment to allocate frame
if i >= 0x100 && t.Failed() {
break
}
// Possible SP adjustment.
checkPCSP(t, r, name, off, pc, frame+spadj)
if rnd.Intn(100) == 0 {
checkPCFileLine(t, r, name, off, pc, file, line)
checkPCData(t, r, name, off, pc, 1, pcdata1)
checkPCData(t, r, name, off, pc, 2, pcdata2)
i += 1
pc = firstpc + i
checkPCFileLine(t, r, name, off, pc-1, file, line)
checkPCData(t, r, name, off, pc-1, 1, pcdata1)
checkPCData(t, r, name, off, pc-1, 2, pcdata2)
checkPCSP(t, r, name, off, pc-1, frame+spadj)
if spadj <= -32 || spadj < 32 && rnd.Intn(2) == 0 {
spadj += 8
} else {
spadj -= 8
}
checkPCSP(t, r, name, off, pc, frame+spadj)
}
// Possible PCFile change.
if rnd.Intn(100) == 0 {
file = fmt.Sprintf("file%d.s", rnd.Intn(10))
line = rnd.Intn(100) + 1
}
// Possible PCLine change.
if rnd.Intn(10) == 0 {
line = rnd.Intn(1000) + 1
}
// Possible PCData $1 change.
if rnd.Intn(100) == 0 {
pcdata1 = rnd.Intn(1000)
}
// Possible PCData $2 change.
if rnd.Intn(100) == 0 {
pcdata2 = rnd.Intn(1000)
}
if i == 0 {
checkPCFileLine(t, r, name, off, 0, file, line)
checkPCFileLine(t, r, name, off, pc-1, file, line)
}
checkPCFileLine(t, r, name, off, pc, file, line)
checkPCData(t, r, name, off, pc, 1, pcdata1)
checkPCData(t, r, name, off, pc, 2, pcdata2)
}
}
}
// findFunc finds the function information in the pclntab of p
// for the function with the given name.
// It returns a symbol reader for pclntab, the offset of the function information
// within that symbol, and the args and frame values read out of the information.
func findFunc(t *testing.T, p *Prog, name string) (r *SymReader, off, args, frame int, ok bool) {
tabsym := p.Syms[goobj.SymID{Name: "runtime.pclntab"}]
if tabsym == nil {
t.Errorf("pclntab is missing in binary")
return
}
r = new(SymReader)
r.Init(p, tabsym)
// pclntab must with 8-byte header
if r.Uint32(0) != 0xfffffffb || r.Uint8(4) != 0 || r.Uint8(5) != 0 || r.Uint8(6) != uint8(p.pcquantum) || r.Uint8(7) != uint8(p.ptrsize) {
t.Errorf("pclntab has incorrect header %.8x", r.data[:8])
return
}
sym := p.Syms[goobj.SymID{Name: name}]
if sym == nil {
t.Errorf("%s is missing in the binary", name)
return
}
// index is nfunc addr0 off0 addr1 off1 ... addr_nfunc (sentinel)
nfunc := int(r.Addr(8))
i := sort.Search(nfunc, func(i int) bool {
return r.Addr(8+p.ptrsize*(1+2*i)) >= sym.Addr
})
if entry := r.Addr(8 + p.ptrsize*(1+2*i)); entry != sym.Addr {
indexTab := make([]Addr, 2*nfunc+1)
for j := range indexTab {
indexTab[j] = r.Addr(8 + p.ptrsize*(1+j))
}
t.Errorf("pclntab is missing entry for %s (%#x): %#x", name, sym.Addr, indexTab)
return
}
off = int(r.Addr(8 + p.ptrsize*(1+2*i+1)))
// func description at off is
// entry addr
// nameoff uint32
// args uint32
// frame uint32
// pcspoff uint32
// pcfileoff uint32
// pclineoff uint32
// npcdata uint32
// nfuncdata uint32
// pcdata npcdata*uint32
// funcdata nfuncdata*addr
//
if entry := r.Addr(off); entry != sym.Addr {
t.Errorf("pclntab inconsistent: entry for %s addr=%#x has entry=%#x", name, sym.Addr, entry)
return
}
nameoff := int(r.Uint32(off + p.ptrsize))
args = int(r.Uint32(off + p.ptrsize + 1*4))
frame = int(r.Uint32(off + p.ptrsize + 2*4))
fname := r.String(nameoff)
if fname != name {
t.Errorf("pclntab inconsistent: entry for %s addr=%#x has name %q", name, sym.Addr, fname)
}
ok = true // off, args, frame are usable
return
}
// loadFuncdata returns the funcdata #fnum value
// loaded from the function information for name.
func loadFuncdata(t *testing.T, r *SymReader, name string, off int, fnum int) (Addr, bool) {
npcdata := int(r.Uint32(off + r.p.ptrsize + 6*4))
nfuncdata := int(r.Uint32(off + r.p.ptrsize + 7*4))
if fnum >= nfuncdata {
t.Errorf("pclntab(%s): no funcdata %d (only < %d)", name, fnum, nfuncdata)
return 0, false
}
fdataoff := off + r.p.ptrsize + (8+npcdata)*4 + fnum*r.p.ptrsize
fdataoff += fdataoff & 4
return r.Addr(fdataoff), true
}
// checkPCSP checks that the PCSP table in the function information at off
// lists spadj as the sp delta for pc.
func checkPCSP(t *testing.T, r *SymReader, name string, off, pc, spadj int) {
pcoff := r.Uint32(off + r.p.ptrsize + 3*4)
pcval, ok := readPCData(t, r, name, "PCSP", pcoff, pc)
if !ok {
return
}
if pcval != spadj {
t.Errorf("pclntab(%s): at pc=+%#x, pcsp=%d, want %d", name, pc, pcval, spadj)
}
}
// checkPCSP checks that the PCFile and PCLine tables in the function information at off
// list file, line as the file name and line number for pc.
func checkPCFileLine(t *testing.T, r *SymReader, name string, off, pc int, file string, line int) {
pcfileoff := r.Uint32(off + r.p.ptrsize + 4*4)
pclineoff := r.Uint32(off + r.p.ptrsize + 5*4)
pcfilenum, ok1 := readPCData(t, r, name, "PCFile", pcfileoff, pc)
pcline, ok2 := readPCData(t, r, name, "PCLine", pclineoff, pc)
if !ok1 || !ok2 {
return
}
nfunc := int(r.Addr(8))
filetaboff := r.Uint32(8 + r.p.ptrsize*2*(nfunc+1))
nfile := int(r.Uint32(int(filetaboff)))
if pcfilenum <= 0 || pcfilenum >= nfile {
t.Errorf("pclntab(%s): at pc=+%#x, filenum=%d (invalid; nfile=%d)", name, pc, pcfilenum, nfile)
}
pcfile := r.String(int(r.Uint32(int(filetaboff) + pcfilenum*4)))
if !strings.HasSuffix(pcfile, file) {
t.Errorf("pclntab(%s): at pc=+%#x, file=%q, want %q", name, pc, pcfile, file)
}
if pcline != line {
t.Errorf("pclntab(%s): at pc=+%#x, line=%d, want %d", name, pc, pcline, line)
}
}
// checkPCData checks that the PCData#pnum table in the function information at off
// list val as the value for pc.
func checkPCData(t *testing.T, r *SymReader, name string, off, pc, pnum, val int) {
pcoff := r.Uint32(off + r.p.ptrsize + (8+pnum)*4)
pcval, ok := readPCData(t, r, name, fmt.Sprintf("PCData#%d", pnum), pcoff, pc)
if !ok {
return
}
if pcval != val {
t.Errorf("pclntab(%s): at pc=+%#x, pcdata#%d=%d, want %d", name, pc, pnum, pcval, val)
}
}
// readPCData reads the PCData table offset off
// to obtain and return the value associated with pc.
func readPCData(t *testing.T, r *SymReader, name, pcdataname string, pcoff uint32, pc int) (int, bool) {
// "If pcsp, pcfile, pcln, or any of the pcdata offsets is zero,
// that table is considered missing, and all PCs take value -1."
if pcoff == 0 {
return -1, true
}
var it PCIter
for it.Init(r.p, r.data[pcoff:]); !it.Done; it.Next() {
if it.PC <= uint32(pc) && uint32(pc) < it.NextPC {
return int(it.Value), true
}
}
if it.Corrupt {
t.Errorf("pclntab(%s): %s: corrupt pcdata table", name, pcdataname)
}
return 0, false
}
// A SymReader provides typed access to the data for a symbol.
type SymReader struct {
p *Prog
data []byte
}
func (r *SymReader) Init(p *Prog, sym *Sym) {
seg := sym.Section.Segment
off := sym.Addr - seg.VirtAddr
data := seg.Data[off : off+Addr(sym.Size)]
r.p = p
r.data = data
}
func (r *SymReader) Uint8(off int) uint8 {
return r.data[off]
}
func (r *SymReader) Uint16(off int) uint16 {
return r.p.byteorder.Uint16(r.data[off:])
}
func (r *SymReader) Uint32(off int) uint32 {
return r.p.byteorder.Uint32(r.data[off:])
}
func (r *SymReader) Uint64(off int) uint64 {
return r.p.byteorder.Uint64(r.data[off:])
}
func (r *SymReader) Addr(off int) Addr {
if r.p.ptrsize == 4 {
return Addr(r.Uint32(off))
}
return Addr(r.Uint64(off))
}
func (r *SymReader) String(off int) string {
end := off
for r.data[end] != '\x00' {
end++
}
return string(r.data[off:end])
}
// 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()
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,
},
}
// 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 (
"bytes"
"fmt"
"io/ioutil"
"testing"
)
// shiftProg adjusts the addresses in p.
// It adds vdelta to all virtual addresses and fdelta to all file offsets.
func shiftProg(p *Prog, vdelta Addr, fdelta Addr) {
p.Entry += vdelta
for _, seg := range p.Segments {
seg.FileOffset += fdelta
seg.VirtAddr += vdelta
for _, sect := range seg.Sections {
sect.VirtAddr += vdelta
for _, sym := range sect.Syms {
sym.Addr += vdelta
}
}
}
}
// diffProg returns a list of differences between p and q,
// assuming p is being checked and q is the correct answer.
func diffProg(p, q *Prog) []string {
var errors []string
if p.UnmappedSize != q.UnmappedSize {
errors = append(errors, fmt.Sprintf("p.UnmappedSize = %#x, want %#x", p.UnmappedSize, q.UnmappedSize))
}
if p.HeaderSize != q.HeaderSize {
errors = append(errors, fmt.Sprintf("p.HeaderSize = %#x, want %#x", p.HeaderSize, q.HeaderSize))
}
if p.Entry != q.Entry {
errors = append(errors, fmt.Sprintf("p.Entry = %#x, want %#x", p.Entry, q.Entry))
}
for i := 0; i < len(p.Segments) || i < len(q.Segments); i++ {
if i >= len(p.Segments) {
errors = append(errors, fmt.Sprintf("p missing segment %q", q.Segments[i].Name))
continue
}
if i >= len(q.Segments) {
errors = append(errors, fmt.Sprintf("p has extra segment %q", p.Segments[i].Name))
continue
}
pseg := p.Segments[i]
qseg := q.Segments[i]
if pseg.Name != qseg.Name {
errors = append(errors, fmt.Sprintf("segment %d Name = %q, want %q", i, pseg.Name, qseg.Name))
continue // probably out of sync
}
if pseg.VirtAddr != qseg.VirtAddr {
errors = append(errors, fmt.Sprintf("segment %q VirtAddr = %#x, want %#x", pseg.Name, pseg.VirtAddr, qseg.VirtAddr))
}
if pseg.VirtSize != qseg.VirtSize {
errors = append(errors, fmt.Sprintf("segment %q VirtSize = %#x, want %#x", pseg.Name, pseg.VirtSize, qseg.VirtSize))
}
if pseg.FileOffset != qseg.FileOffset {
errors = append(errors, fmt.Sprintf("segment %q FileOffset = %#x, want %#x", pseg.Name, pseg.FileOffset, qseg.FileOffset))
}
if pseg.FileSize != qseg.FileSize {
errors = append(errors, fmt.Sprintf("segment %q FileSize = %#x, want %#x", pseg.Name, pseg.FileSize, qseg.FileSize))
}
if len(pseg.Data) != len(qseg.Data) {
errors = append(errors, fmt.Sprintf("segment %q len(Data) = %d, want %d", pseg.Name, len(pseg.Data), len(qseg.Data)))
} else if !bytes.Equal(pseg.Data, qseg.Data) {
errors = append(errors, fmt.Sprintf("segment %q Data mismatch:\n\thave %x\n\twant %x", pseg.Name, pseg.Data, qseg.Data))
}
for j := 0; j < len(pseg.Sections) || j < len(qseg.Sections); j++ {
if j >= len(pseg.Sections) {
errors = append(errors, fmt.Sprintf("segment %q missing section %q", pseg.Name, qseg.Sections[i].Name))
continue
}
if j >= len(qseg.Sections) {
errors = append(errors, fmt.Sprintf("segment %q has extra section %q", pseg.Name, pseg.Sections[i].Name))
continue
}
psect := pseg.Sections[j]
qsect := qseg.Sections[j]
if psect.Name != qsect.Name {
errors = append(errors, fmt.Sprintf("segment %q, section %d Name = %q, want %q", pseg.Name, j, psect.Name, qsect.Name))
continue // probably out of sync
}
if psect.VirtAddr != qsect.VirtAddr {
errors = append(errors, fmt.Sprintf("segment %q section %q VirtAddr = %#x, want %#x", pseg.Name, psect.Name, psect.VirtAddr, qsect.VirtAddr))
}
if psect.Size != qsect.Size {
errors = append(errors, fmt.Sprintf("segment %q section %q Size = %#x, want %#x", pseg.Name, psect.Name, psect.Size, qsect.Size))
}
if psect.Align != qsect.Align {
errors = append(errors, fmt.Sprintf("segment %q section %q Align = %#x, want %#x", pseg.Name, psect.Name, psect.Align, qsect.Align))
}
}
}
return errors
}
// cloneProg returns a deep copy of p.
func cloneProg(p *Prog) *Prog {
q := new(Prog)
*q = *p
q.Segments = make([]*Segment, len(p.Segments))
for i, seg := range p.Segments {
q.Segments[i] = cloneSegment(seg)
}
return q
}
// cloneSegment returns a deep copy of seg.
func cloneSegment(seg *Segment) *Segment {
t := new(Segment)
*t = *seg
t.Sections = make([]*Section, len(seg.Sections))
for i, sect := range seg.Sections {
t.Sections[i] = cloneSection(sect)
}
t.Data = make([]byte, len(seg.Data))
copy(t.Data, seg.Data)
return t
}
// cloneSection returns a deep copy of section.
func cloneSection(sect *Section) *Section {
// At the moment, there's nothing we need to make a deep copy of.
t := new(Section)
*t = *sect
return t
}
const saveMismatch = true
// checkGolden checks that data matches the named file.
// If not, it reports the error to the test.
func checkGolden(t *testing.T, data []byte, name string) {
golden := mustParseHexdumpFile(t, name)
if !bytes.Equal(data, golden) {
if saveMismatch {
ioutil.WriteFile(name+".raw", data, 0666)
ioutil.WriteFile(name+".hex", []byte(hexdump(data)), 0666)
}
// TODO(rsc): A better diff would be nice, as needed.
i := 0
for i < len(data) && i < len(golden) && data[i] == golden[i] {
i++
}
if i >= len(data) {
t.Errorf("%s: output file shorter than expected: have %d bytes, want %d", name, len(data), len(golden))
} else if i >= len(golden) {
t.Errorf("%s: output file larger than expected: have %d bytes, want %d", name, len(data), len(golden))
} else {
t.Errorf("%s: output file differs at byte %d: have %#02x, want %#02x", name, i, data[i], golden[i])
}
}
}
// 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.
// Generation of runtime-accessible data structures.
// See also debug.go.
package main
import "cmd/internal/goobj"
func (p *Prog) runtime() {
p.pclntab()
// TODO: Implement garbage collection data.
p.addSym(&Sym{
Sym: &goobj.Sym{
SymID: goobj.SymID{Name: "runtime.gcdata"},
Kind: goobj.SRODATA,
},
})
p.addSym(&Sym{
Sym: &goobj.Sym{
SymID: goobj.SymID{Name: "runtime.gcbss"},
Kind: goobj.SRODATA,
},
})
}
// 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.
// Initial scan of packages making up a program.
// TODO(rsc): Rename goobj.SymID.Version to StaticID to avoid confusion with the ELF meaning of version.
// TODO(rsc): Fix file format so that SBSS/SNOPTRBSS with data is listed as SDATA/SNOPTRDATA.
// TODO(rsc): Parallelize scan to overlap file i/o where possible.
package main
import (
"cmd/internal/goobj"
"os"
"sort"
"strings"
)
// scan scans all packages making up the program, starting with package main defined in mainfile.
func (p *Prog) scan(mainfile string) {
p.initScan()
p.scanFile("main", mainfile)
if len(p.Missing) > 0 && !p.omitRuntime {
p.scanImport("runtime")
}
var missing []string
for sym := range p.Missing {
if !p.isAuto(sym) {
missing = append(missing, sym.String())
}
}
if missing != nil {
sort.Strings(missing)
for _, sym := range missing {
p.errorf("undefined: %s", sym)
}
}
// TODO(rsc): Walk import graph to diagnose cycles.
}
// initScan initializes the Prog fields needed by scan.
func (p *Prog) initScan() {
p.Packages = make(map[string]*Package)
p.Syms = make(map[goobj.SymID]*Sym)
p.Missing = make(map[goobj.SymID]bool)
p.Missing[p.startSym] = true
}
// scanFile reads file to learn about the package with the given import path.
func (p *Prog) scanFile(pkgpath string, file string) {
pkg := &Package{
File: file,
}
p.Packages[pkgpath] = pkg
f, err := os.Open(file)
if err != nil {
p.errorf("%v", err)
return
}
gp, err := goobj.Parse(f, pkgpath)
f.Close()
if err != nil {
p.errorf("reading %s: %v", file, err)
return
}
// TODO(rsc): Change cmd/internal/goobj to record package name as gp.Name.
// TODO(rsc): If pkgpath == "main", check that gp.Name == "main".
pkg.Package = gp
for _, gs := range gp.Syms {
// TODO(rsc): Fix file format instead of this workaround.
if gs.Data.Size > 0 {
switch gs.Kind {
case goobj.SBSS:
gs.Kind = goobj.SDATA
case goobj.SNOPTRBSS:
gs.Kind = goobj.SNOPTRDATA
}
}
if gs.Version != 0 {
gs.Version += p.MaxVersion
}
for i := range gs.Reloc {
r := &gs.Reloc[i]
if r.Sym.Version != 0 {
r.Sym.Version += p.MaxVersion
}
if p.Syms[r.Sym] == nil {
p.Missing[r.Sym] = true
}
}
if gs.Func != nil {
for i := range gs.Func.FuncData {
fdata := &gs.Func.FuncData[i]
if fdata.Sym.Name != "" {
if fdata.Sym.Version != 0 {
fdata.Sym.Version += p.MaxVersion
}
if p.Syms[fdata.Sym] == nil {
p.Missing[fdata.Sym] = true
}
}
}
}
if old := p.Syms[gs.SymID]; old != nil {
// Duplicate definition of symbol. Is it okay?
// TODO(rsc): Write test for this code.
switch {
// If both symbols are BSS (no data), take max of sizes
// but otherwise ignore second symbol.
case old.Data.Size == 0 && gs.Data.Size == 0:
if old.Size < gs.Size {
old.Size = gs.Size
}
continue
// If one is in BSS and one is not, use the one that is not.
case old.Data.Size > 0 && gs.Data.Size == 0:
continue
case gs.Data.Size > 0 && old.Data.Size == 0:
break // install gs as new symbol below
// If either is marked as DupOK, we can keep either one.
// Keep the one that we saw first.
case old.DupOK || gs.DupOK:
continue
// Otherwise, there's an actual conflict:
default:
p.errorf("symbol %s defined in both %s and %s %v %v", gs.SymID, old.Package.File, file, old.Data, gs.Data)
continue
}
}
s := &Sym{
Sym: gs,
Package: pkg,
}
p.addSym(s)
delete(p.Missing, gs.SymID)
if s.Data.Size > int64(s.Size) {
p.errorf("%s: initialized data larger than symbol (%d > %d)", s, s.Data.Size, s.Size)
}
}
p.MaxVersion += pkg.MaxVersion
for i, pkgpath := range pkg.Imports {
// TODO(rsc): Fix file format to drop .a from recorded import path.
pkgpath = strings.TrimSuffix(pkgpath, ".a")
pkg.Imports[i] = pkgpath
p.scanImport(pkgpath)
}
}
func (p *Prog) addSym(s *Sym) {
pkg := s.Package
if pkg == nil {
pkg = p.Packages[""]
if pkg == nil {
pkg = &Package{}
p.Packages[""] = pkg
}
s.Package = pkg
}
pkg.Syms = append(pkg.Syms, s)
p.Syms[s.SymID] = s
p.SymOrder = append(p.SymOrder, s)
}
// scanImport finds the object file for the given import path and then scans it.
func (p *Prog) scanImport(pkgpath string) {
if p.Packages[pkgpath] != nil {
return // already loaded
}
// TODO(rsc): Implement correct search to find file.
p.scanFile(pkgpath, p.pkgdir+"/"+pkgpath+".a")
}
ALL=\
autosection.6\
autoweak.6\
dead.6\
hello.6\
layout.6\
pclntab.6\
all: $(ALL)
%.6: %.s
GOARCH=amd64 GOOS=darwin go tool asm -o $*.6 -I $(shell go env GOROOT)/pkg/include -trimpath=$(shell pwd) $*.s
pclntab.s: genpcln.go
go run genpcln.go >pclntab.s
// 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.
// Test of section-named symbols.
#include "textflag.h"
TEXT start(SB),7,$0
MOVQ $autotab(SB),AX
MOVQ $autoptr(SB),AX
RET
GLOBL zero(SB), $8
GLOBL zeronoptr(SB), NOPTR, $16
// text
DATA autotab+0x00(SB)/8, $runtime·text(SB)
DATA autotab+0x08(SB)/8, $start(SB)
DATA autotab+0x10(SB)/8, $runtime·etext(SB)
DATA autotab+0x18(SB)/8, $start+16(SB)
// data
DATA autotab+0x20(SB)/8, $runtime·data(SB)
DATA autotab+0x28(SB)/8, $autotab(SB)
DATA autotab+0x30(SB)/8, $runtime·edata(SB)
DATA autotab+0x38(SB)/8, $nonzero+4(SB)
// bss
DATA autotab+0x40(SB)/8, $runtime·bss(SB)
DATA autotab+0x48(SB)/8, $zero(SB)
DATA autotab+0x50(SB)/8, $runtime·ebss(SB)
DATA autotab+0x58(SB)/8, $zero+8(SB)
// noptrdata
DATA autotab+0x60(SB)/8, $runtime·noptrdata(SB)
DATA autotab+0x68(SB)/8, $nonzeronoptr(SB)
DATA autotab+0x70(SB)/8, $runtime·enoptrdata(SB)
DATA autotab+0x78(SB)/8, $nonzeronoptr+8(SB)
// noptrbss
DATA autotab+0x80(SB)/8, $runtime·noptrbss(SB)
DATA autotab+0x88(SB)/8, $zeronoptr(SB)
DATA autotab+0x90(SB)/8, $runtime·enoptrbss(SB)
DATA autotab+0x98(SB)/8, $zeronoptr+16(SB)
// end
DATA autotab+0xa0(SB)/8, $runtime·end(SB)
DATA autotab+0xa8(SB)/8, $zeronoptr+16(SB)
GLOBL autotab(SB), $0xb0
DATA nonzero(SB)/4, $1
GLOBL nonzero(SB), $4
DATA nonzeronoptr(SB)/8, $2
GLOBL nonzeronoptr(SB), NOPTR, $8
GLOBL autoptr(SB), $0
// 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.
// Test of dead code removal.
// Symbols with names beginning with dead_ should be discarded.
// Others should be kept.
TEXT start(SB),7,$0 // start symbol
MOVQ $data1<>(SB), AX
CALL text1(SB)
MOVQ $text2(SB), BX
RET
TEXT text1(SB),7,$0
FUNCDATA $1, funcdata+4(SB)
RET
TEXT text2(SB),7,$0
MOVQ $runtime·edata(SB),BX
RET
DATA data1<>+0(SB)/8, $data2(SB)
DATA data1<>+8(SB)/8, $data3(SB)
GLOBL data1<>(SB), $16
GLOBL data2(SB), $1
GLOBL data3(SB), $1
GLOBL funcdata(SB), $8
TEXT dead_start(SB),7,$0
MOVQ $dead_data1(SB), AX
CALL dead_text1(SB)
MOVQ $dead_text2(SB), BX
RET
TEXT dead_text1(SB),7,$0
FUNCDATA $1, dead_funcdata+4(SB)
RET
TEXT dead_text2(SB),7,$0
RET
DATA dead_data1+0(SB)/8, $dead_data2(SB)
DATA dead_data1+8(SB)/8, $dead_data3(SB)
GLOBL dead_data1(SB), $16
GLOBL dead_data2(SB), $1
GLOBL dead_data3(SB), $1
GLOBL dead_funcdata(SB), $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.
// This program generates a .s file using a pseudorandom
// value stream for the runtime function data.
// The pclntab test checks that the linked copy
// still has the same pseudorandom value stream.
package main
import (
"fmt"
"math/rand"
)
func main() {
fmt.Printf("// generated by genpcln.go; do not edit\n\n")
for f := 0; f < 3; f++ {
r := rand.New(rand.NewSource(int64(f)))
file := "input"
line := 1
args := r.Intn(100) * 8
frame := 32 + r.Intn(32)/8*8
fmt.Printf("#line %d %q\n", line, file)
fmt.Printf("TEXT func%d(SB),7,$%d-%d\n", f, frame, args)
fmt.Printf("\tFUNCDATA $1, funcdata%d(SB)\n", f)
fmt.Printf("#line %d %q\n", line, file)
size := 200 + r.Intn(100)*8
spadj := 0
flushed := 0
firstpc := 4
flush := func(i int) {
for i-flushed >= 10 {
fmt.Printf("#line %d %q\n", line, file)
fmt.Printf("/*%#04x*/\tMOVQ $0x123456789, AX\n", firstpc+flushed)
flushed += 10
}
for i-flushed >= 5 {
fmt.Printf("#line %d %q\n", line, file)
fmt.Printf("/*%#04x*/\tMOVL $0x1234567, AX\n", firstpc+flushed)
flushed += 5
}
for i-flushed > 0 {
fmt.Printf("#line %d %q\n", line, file)
fmt.Printf("/*%#04x*/\tBYTE $0\n", firstpc+flushed)
flushed++
}
}
for i := 0; i < size; i++ {
// Possible SP adjustment.
if r.Intn(100) == 0 {
flush(i)
fmt.Printf("#line %d %q\n", line, file)
if spadj <= -32 || spadj < 32 && r.Intn(2) == 0 {
spadj += 8
fmt.Printf("/*%#04x*/\tPUSHQ AX\n", firstpc+i)
} else {
spadj -= 8
fmt.Printf("/*%#04x*/\tPOPQ AX\n", firstpc+i)
}
i += 1
flushed = i
}
// Possible PCFile change.
if r.Intn(100) == 0 {
flush(i)
file = fmt.Sprintf("file%d.s", r.Intn(10))
line = r.Intn(100) + 1
}
// Possible PCLine change.
if r.Intn(10) == 0 {
flush(i)
line = r.Intn(1000) + 1
}
// Possible PCData $1 change.
if r.Intn(100) == 0 {
flush(i)
fmt.Printf("/*%6s*/\tPCDATA $1, $%d\n", "", r.Intn(1000))
}
// Possible PCData $2 change.
if r.Intn(100) == 0 {
flush(i)
fmt.Printf("/*%6s*/\tPCDATA $2, $%d\n", "", r.Intn(1000))
}
}
flush(size)
for spadj < 0 {
fmt.Printf("\tPUSHQ AX\n")
spadj += 8
}
for spadj > 0 {
fmt.Printf("\tPOPQ AX\n")
spadj -= 8
}
fmt.Printf("\tRET\n")
fmt.Printf("\n")
fmt.Printf("GLOBL funcdata%d(SB), $16\n", f)
}
fmt.Printf("\nTEXT start(SB),7,$0\n")
for f := 0; f < 3; f++ {
fmt.Printf("\tCALL func%d(SB)\n", f)
}
fmt.Printf("\tMOVQ $runtime·pclntab(SB), AX\n")
fmt.Printf("\n\tRET\n")
}
TEXT _rt0_go(SB),7,$0
MOVL $1, DI
MOVL $hello<>(SB), SI
MOVL $12, DX
MOVL $0x2000004, AX
SYSCALL
MOVL $0, DI
MOVL $0x2000001, AX
SYSCALL
RET
DATA hello<>+0(SB)/4, $"hell"
DATA hello<>+4(SB)/4, $"o wo"
DATA hello<>+8(SB)/4, $"rld\n"
GLOBL hello<>(SB), $12
// 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.
// Test of section assignment in layout.go.
// Each symbol should end up in the section named by the symbol name prefix (up to the underscore).
#include "textflag.h"
TEXT text_start(SB),7,$0
MOVQ $rodata_sym(SB), AX
MOVQ $noptrdata_sym(SB), AX
MOVQ $data_sym(SB), AX
MOVQ $bss_sym(SB), AX
MOVQ $noptrbss_sym(SB), AX
RET
DATA rodata_sym(SB)/4, $1
GLOBL rodata_sym(SB), RODATA, $4
DATA noptrdata_sym(SB)/4, $1
GLOBL noptrdata_sym(SB), NOPTR, $4
DATA data_sym(SB)/4, $1
GLOBL data_sym(SB), $4
GLOBL bss_sym(SB), $4
GLOBL noptrbss_sym(SB), NOPTR, $4
00000000 cf fa ed fe 07 00 00 01 03 00 00 00 02 00 00 00 |................|
00000010 04 00 00 00 d0 02 00 00 01 00 00 00 00 00 00 00 |................|
00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE|
00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............|
00000040 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000060 00 00 00 00 00 00 00 00 19 00 00 00 38 01 00 00 |............8...|
00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
00000080 00 10 00 00 00 00 00 00 c0 10 00 00 00 00 00 00 |................|
00000090 00 00 00 00 00 00 00 00 c0 10 00 00 00 00 00 00 |................|
000000a0 07 00 00 00 05 00 00 00 03 00 00 00 00 00 00 00 |................|
000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........|
000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
000000d0 00 20 00 00 00 00 00 00 30 00 00 00 00 00 00 00 |. ......0.......|
000000e0 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000f0 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000100 5f 5f 72 6f 64 61 74 61 00 00 00 00 00 00 00 00 |__rodata........|
00000110 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
00000120 30 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |0 ..............|
00000130 30 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |0...............|
*
00000150 5f 5f 66 75 6e 63 74 61 62 00 00 00 00 00 00 00 |__functab.......|
00000160 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
00000170 30 20 00 00 00 00 00 00 90 00 00 00 00 00 00 00 |0 ..............|
00000180 30 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |0...............|
*
000001a0 19 00 00 00 98 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..|
000001b0 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 |.........0......|
000001c0 0c 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......|
000001d0 0c 00 00 00 00 00 00 00 03 00 00 00 03 00 00 00 |................|
000001e0 01 00 00 00 00 00 00 00 5f 5f 64 61 74 61 00 00 |........__data..|
000001f0 00 00 00 00 00 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..|
00000200 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 |.........0......|
00000210 0c 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......|
*
00000230 00 00 00 00 00 00 00 00 05 00 00 00 b8 00 00 00 |................|
00000240 04 00 00 00 2a 00 00 00 00 00 00 00 00 00 00 00 |....*...........|
*
000002c0 00 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......|
*
00001000 bf 01 00 00 00 8d 35 f5 0f 00 00 ba 0c 00 00 00 |......5.........|
00001010 b8 04 00 00 02 0f 05 31 ff b8 01 00 00 02 0f 05 |.......1........|
00001020 c3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00001030 fb ff ff ff 00 00 01 08 01 00 00 00 00 00 00 00 |................|
00001040 00 20 00 00 00 00 00 00 30 00 00 00 00 00 00 00 |. ......0.......|
00001050 30 20 00 00 00 00 00 00 80 00 00 00 00 00 00 00 |0 ..............|
00001060 00 20 00 00 00 00 00 00 58 00 00 00 00 00 00 80 |. ......X.......|
00001070 08 00 00 00 60 00 00 00 63 00 00 00 66 00 00 00 |....`...c...f...|
00001080 00 00 00 00 00 00 00 00 5f 72 74 30 5f 67 6f 00 |........_rt0_go.|
00001090 02 30 00 04 30 00 06 05 02 06 02 05 02 05 02 02 |.0..0...........|
000010a0 02 02 02 05 02 02 02 10 00 00 00 00 00 00 00 00 |................|
000010b0 02 00 00 00 88 00 00 00 68 65 6c 6c 6f 2e 73 00 |........hello.s.|
*
00002000 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a |hello world.|
0000200c
00000000 cf fa ed fe 07 00 00 01 03 00 00 00 02 00 00 00 |................|
00000010 03 00 00 00 98 01 00 00 01 00 00 00 00 00 00 00 |................|
00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE|
00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............|
00000040 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000060 00 00 00 00 00 00 00 00 19 00 00 00 98 00 00 00 |................|
00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
00000080 00 10 00 00 00 00 00 00 0d 10 00 00 00 00 00 00 |................|
00000090 00 00 00 00 00 00 00 00 0d 10 00 00 00 00 00 00 |................|
000000a0 07 00 00 00 05 00 00 00 01 00 00 00 00 00 00 00 |................|
000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........|
000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
000000d0 00 20 00 00 00 00 00 00 0d 00 00 00 00 00 00 00 |. ..............|
000000e0 00 10 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |................|
000000f0 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000100 05 00 00 00 b8 00 00 00 04 00 00 00 2a 00 00 00 |............*...|
00000110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000190 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |. ..............|
000001a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00001000 b8 01 00 00 02 bf 09 00 00 00 0f 05 f4 |.............|
0000100d
00000000 cf fa ed fe 07 00 00 01 03 00 00 00 02 00 00 00 |................|
00000010 04 00 00 00 30 02 00 00 01 00 00 00 00 00 00 00 |....0...........|
00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE|
00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............|
00000040 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000060 00 00 00 00 00 00 00 00 19 00 00 00 98 00 00 00 |................|
00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
00000080 00 10 00 00 00 00 00 00 23 10 00 00 00 00 00 00 |........#.......|
00000090 00 00 00 00 00 00 00 00 23 10 00 00 00 00 00 00 |........#.......|
000000a0 07 00 00 00 05 00 00 00 01 00 00 00 00 00 00 00 |................|
000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........|
000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
000000d0 00 20 00 00 00 00 00 00 23 00 00 00 00 00 00 00 |. ......#.......|
000000e0 00 10 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |................|
000000f0 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000100 19 00 00 00 98 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..|
00000110 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 |.........0......|
00000120 0c 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......|
00000130 0c 00 00 00 00 00 00 00 03 00 00 00 03 00 00 00 |................|
00000140 01 00 00 00 00 00 00 00 5f 5f 64 61 74 61 00 00 |........__data..|
00000150 00 00 00 00 00 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..|
00000160 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 |.........0......|
00000170 0c 00 00 00 00 00 00 00 00 20 00 00 06 00 00 00 |......... ......|
00000180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000190 00 00 00 00 00 00 00 00 05 00 00 00 b8 00 00 00 |................|
000001a0 04 00 00 00 2a 00 00 00 00 00 00 00 00 00 00 00 |....*...........|
000001b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000220 00 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......|
00000230 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00001000 b8 04 00 00 02 bf 01 00 00 00 be 00 30 00 00 ba |............0...|
00001010 0c 00 00 00 0f 05 b8 01 00 00 02 bf 09 00 00 00 |................|
00001020 0f 05 f4 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00001030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00002000 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a |hello world.|
0000200c
00000000 cf fa ed fe 07 00 00 01 03 00 00 00 02 00 00 00 |................|
00000010 03 00 00 00 e8 01 00 00 01 00 00 00 00 00 00 00 |................|
00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE|
00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............|
00000040 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000060 00 00 00 00 00 00 00 00 19 00 00 00 e8 00 00 00 |................|
00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
00000080 00 10 00 00 00 00 00 00 0c 20 00 00 00 00 00 00 |......... ......|
00000090 00 00 00 00 00 00 00 00 0c 20 00 00 00 00 00 00 |......... ......|
000000a0 07 00 00 00 05 00 00 00 02 00 00 00 00 00 00 00 |................|
000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........|
000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
000000d0 00 20 00 00 00 00 00 00 23 00 00 00 00 00 00 00 |. ......#.......|
000000e0 00 10 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |................|
000000f0 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000100 5f 5f 72 6f 64 61 74 61 00 00 00 00 00 00 00 00 |__rodata........|
00000110 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
00000120 00 30 00 00 00 00 00 00 0c 00 00 00 00 00 00 00 |.0..............|
00000130 00 20 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |. ..............|
00000140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000150 05 00 00 00 b8 00 00 00 04 00 00 00 2a 00 00 00 |............*...|
00000160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
000001e0 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |. ..............|
000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00001000 b8 04 00 00 02 bf 01 00 00 00 be 00 30 00 00 ba |............0...|
00001010 0c 00 00 00 0f 05 b8 01 00 00 02 bf 00 00 00 00 |................|
00001020 0f 05 f4 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00001030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00002000 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a |hello world.|
0000200c
This diff is collapsed.
// 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
// round returns size rounded up to the next multiple of align;
// align must be a power of two.
func round(size, align Addr) Addr {
return (size + align - 1) &^ (align - 1)
}
// 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.
// Writing of executable and (for hostlink mode) object files.
package main
import "io"
func (p *Prog) write(w io.Writer) {
p.Entry = p.Syms[p.startSym].Addr
p.formatter.write(w, p)
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment