Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d20a61e68 | ||
|
|
e7f6a244a2 |
@@ -41,9 +41,6 @@ go src=..
|
|||||||
gofmt_test.go
|
gofmt_test.go
|
||||||
testdata
|
testdata
|
||||||
+
|
+
|
||||||
newlink
|
|
||||||
testdata
|
|
||||||
+
|
|
||||||
archive
|
archive
|
||||||
tar
|
tar
|
||||||
testdata
|
testdata
|
||||||
|
|||||||
@@ -1,117 +0,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.
|
|
||||||
|
|
||||||
// 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, "go.weak.") ||
|
|
||||||
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,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// autoConst defines the automatically generated constant symbols needed by p.
|
|
||||||
func (p *Prog) autoConst() {
|
|
||||||
for sym := range p.Missing {
|
|
||||||
switch {
|
|
||||||
case strings.HasPrefix(sym.Name, "go.weak."):
|
|
||||||
// weak symbol resolves to actual symbol if present, or else nil.
|
|
||||||
delete(p.Missing, sym)
|
|
||||||
targ := sym
|
|
||||||
targ.Name = sym.Name[len("go.weak."):]
|
|
||||||
var addr Addr
|
|
||||||
if s := p.Syms[targ]; s != nil {
|
|
||||||
addr = s.Addr
|
|
||||||
}
|
|
||||||
p.defineConst(sym.Name, addr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,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 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",
|
|
||||||
"testdata/autoweak.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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,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.
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,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.
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,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.
|
|
||||||
|
|
||||||
// Generation of debug data structures (in the executable but not mapped at run time).
|
|
||||||
// See also runtime.go.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
func (p *Prog) debug() {
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,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.
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,180 +0,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.
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,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.
|
|
||||||
|
|
||||||
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])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,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.
|
|
||||||
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
@@ -1,108 +0,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.
|
|
||||||
|
|
||||||
// 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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,380 +0,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.
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
@@ -1,407 +0,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.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"debug/macho"
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Test macho writing by checking that each generated prog can be written
|
|
||||||
// and then read back using debug/macho to get the same prog.
|
|
||||||
// Also check against golden testdata file.
|
|
||||||
var machoWriteTests = []struct {
|
|
||||||
name string
|
|
||||||
golden bool
|
|
||||||
prog *Prog
|
|
||||||
}{
|
|
||||||
// amd64 exit 9
|
|
||||||
{
|
|
||||||
name: "exit9",
|
|
||||||
golden: true,
|
|
||||||
prog: &Prog{
|
|
||||||
GOARCH: "amd64",
|
|
||||||
GOOS: "darwin",
|
|
||||||
UnmappedSize: 0x1000,
|
|
||||||
Entry: 0x1000,
|
|
||||||
Segments: []*Segment{
|
|
||||||
{
|
|
||||||
Name: "text",
|
|
||||||
VirtAddr: 0x1000,
|
|
||||||
VirtSize: 13,
|
|
||||||
FileOffset: 0,
|
|
||||||
FileSize: 13,
|
|
||||||
Data: []byte{
|
|
||||||
0xb8, 0x01, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX
|
|
||||||
0xbf, 0x09, 0x00, 0x00, 0x00, // MOVL $9, DI
|
|
||||||
0x0f, 0x05, // SYSCALL
|
|
||||||
0xf4, // HLT
|
|
||||||
},
|
|
||||||
Sections: []*Section{
|
|
||||||
{
|
|
||||||
Name: "text",
|
|
||||||
VirtAddr: 0x1000,
|
|
||||||
Size: 13,
|
|
||||||
Align: 64,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// amd64 write hello world & exit 9
|
|
||||||
{
|
|
||||||
name: "hello",
|
|
||||||
golden: true,
|
|
||||||
prog: &Prog{
|
|
||||||
GOARCH: "amd64",
|
|
||||||
GOOS: "darwin",
|
|
||||||
UnmappedSize: 0x1000,
|
|
||||||
Entry: 0x1000,
|
|
||||||
Segments: []*Segment{
|
|
||||||
{
|
|
||||||
Name: "text",
|
|
||||||
VirtAddr: 0x1000,
|
|
||||||
VirtSize: 35,
|
|
||||||
FileOffset: 0,
|
|
||||||
FileSize: 35,
|
|
||||||
Data: []byte{
|
|
||||||
0xb8, 0x04, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX
|
|
||||||
0xbf, 0x01, 0x00, 0x00, 0x00, // MOVL $1, DI
|
|
||||||
0xbe, 0x00, 0x30, 0x00, 0x00, // MOVL $0x3000, SI
|
|
||||||
0xba, 0x0c, 0x00, 0x00, 0x00, // MOVL $12, DX
|
|
||||||
0x0f, 0x05, // SYSCALL
|
|
||||||
0xb8, 0x01, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX
|
|
||||||
0xbf, 0x09, 0x00, 0x00, 0x00, // MOVL $9, DI
|
|
||||||
0x0f, 0x05, // SYSCALL
|
|
||||||
0xf4, // HLT
|
|
||||||
},
|
|
||||||
Sections: []*Section{
|
|
||||||
{
|
|
||||||
Name: "text",
|
|
||||||
VirtAddr: 0x1000,
|
|
||||||
Size: 35,
|
|
||||||
Align: 64,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "data",
|
|
||||||
VirtAddr: 0x2000,
|
|
||||||
VirtSize: 12,
|
|
||||||
FileOffset: 0x1000,
|
|
||||||
FileSize: 12,
|
|
||||||
Data: []byte("hello world\n"),
|
|
||||||
Sections: []*Section{
|
|
||||||
{
|
|
||||||
Name: "data",
|
|
||||||
VirtAddr: 0x2000,
|
|
||||||
Size: 12,
|
|
||||||
Align: 64,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// amd64 write hello world from rodata & exit 0
|
|
||||||
{
|
|
||||||
name: "helloro",
|
|
||||||
golden: true,
|
|
||||||
prog: &Prog{
|
|
||||||
GOARCH: "amd64",
|
|
||||||
GOOS: "darwin",
|
|
||||||
UnmappedSize: 0x1000,
|
|
||||||
Entry: 0x1000,
|
|
||||||
Segments: []*Segment{
|
|
||||||
{
|
|
||||||
Name: "text",
|
|
||||||
VirtAddr: 0x1000,
|
|
||||||
VirtSize: 0x100c,
|
|
||||||
FileOffset: 0,
|
|
||||||
FileSize: 0x100c,
|
|
||||||
Data: concat(
|
|
||||||
[]byte{
|
|
||||||
0xb8, 0x04, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX
|
|
||||||
0xbf, 0x01, 0x00, 0x00, 0x00, // MOVL $1, DI
|
|
||||||
0xbe, 0x00, 0x30, 0x00, 0x00, // MOVL $0x3000, SI
|
|
||||||
0xba, 0x0c, 0x00, 0x00, 0x00, // MOVL $12, DX
|
|
||||||
0x0f, 0x05, // SYSCALL
|
|
||||||
0xb8, 0x01, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX
|
|
||||||
0xbf, 0x00, 0x00, 0x00, 0x00, // MOVL $0, DI
|
|
||||||
0x0f, 0x05, // SYSCALL
|
|
||||||
0xf4, // HLT
|
|
||||||
},
|
|
||||||
make([]byte, 0x1000-35),
|
|
||||||
[]byte("hello world\n"),
|
|
||||||
),
|
|
||||||
Sections: []*Section{
|
|
||||||
{
|
|
||||||
Name: "text",
|
|
||||||
VirtAddr: 0x1000,
|
|
||||||
Size: 35,
|
|
||||||
Align: 64,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "rodata",
|
|
||||||
VirtAddr: 0x2000,
|
|
||||||
Size: 12,
|
|
||||||
Align: 64,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func concat(xs ...[]byte) []byte {
|
|
||||||
var out []byte
|
|
||||||
for _, x := range xs {
|
|
||||||
out = append(out, x...)
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMachoWrite(t *testing.T) {
|
|
||||||
for _, tt := range machoWriteTests {
|
|
||||||
name := tt.prog.GOARCH + "." + tt.name
|
|
||||||
prog := cloneProg(tt.prog)
|
|
||||||
prog.init()
|
|
||||||
var f machoFormat
|
|
||||||
vsize, fsize := f.headerSize(prog)
|
|
||||||
shiftProg(prog, vsize, fsize)
|
|
||||||
var buf bytes.Buffer
|
|
||||||
f.write(&buf, prog)
|
|
||||||
if false { // enable to debug
|
|
||||||
ioutil.WriteFile("a.out", buf.Bytes(), 0777)
|
|
||||||
}
|
|
||||||
read, err := machoRead(machoArches[tt.prog.GOARCH], buf.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%s: reading mach-o output:\n\t%v", name, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
diffs := diffProg(read, prog)
|
|
||||||
if diffs != nil {
|
|
||||||
t.Errorf("%s: mismatched prog:\n\t%s", name, strings.Join(diffs, "\n\t"))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !tt.golden {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
checkGolden(t, buf.Bytes(), "testdata/macho."+name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// machoRead reads the mach-o file in data and returns a corresponding prog.
|
|
||||||
func machoRead(arch machoArch, data []byte) (*Prog, error) {
|
|
||||||
f, err := macho.NewFile(bytes.NewReader(data))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var errors []string
|
|
||||||
errorf := func(format string, args ...interface{}) {
|
|
||||||
errors = append(errors, fmt.Sprintf(format, args...))
|
|
||||||
}
|
|
||||||
|
|
||||||
magic := uint32(0xFEEDFACE)
|
|
||||||
if arch.CPU&macho64Bit != 0 {
|
|
||||||
magic |= 1
|
|
||||||
}
|
|
||||||
if f.Magic != magic {
|
|
||||||
errorf("header: Magic = %#x, want %#x", f.Magic, magic)
|
|
||||||
}
|
|
||||||
if f.Cpu != macho.CpuAmd64 {
|
|
||||||
errorf("header: CPU = %#x, want %#x", f.Cpu, macho.CpuAmd64)
|
|
||||||
}
|
|
||||||
if f.SubCpu != 3 {
|
|
||||||
errorf("header: SubCPU = %#x, want %#x", f.SubCpu, 3)
|
|
||||||
}
|
|
||||||
if f.Type != 2 {
|
|
||||||
errorf("header: FileType = %d, want %d", f.Type, 2)
|
|
||||||
}
|
|
||||||
if f.Flags != 1 {
|
|
||||||
errorf("header: Flags = %d, want %d", f.Flags, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
msects := f.Sections
|
|
||||||
var limit uint64
|
|
||||||
prog := new(Prog)
|
|
||||||
for _, load := range f.Loads {
|
|
||||||
switch load := load.(type) {
|
|
||||||
default:
|
|
||||||
errorf("unexpected macho load %T %x", load, load.Raw())
|
|
||||||
|
|
||||||
case macho.LoadBytes:
|
|
||||||
if len(load) < 8 || len(load)%4 != 0 {
|
|
||||||
errorf("unexpected load length %d", len(load))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
cmd := f.ByteOrder.Uint32(load)
|
|
||||||
switch macho.LoadCmd(cmd) {
|
|
||||||
default:
|
|
||||||
errorf("unexpected macho load cmd %s", macho.LoadCmd(cmd))
|
|
||||||
case macho.LoadCmdUnixThread:
|
|
||||||
data := make([]uint32, len(load[8:])/4)
|
|
||||||
binary.Read(bytes.NewReader(load[8:]), f.ByteOrder, data)
|
|
||||||
if len(data) != 44 {
|
|
||||||
errorf("macho thread len(data) = %d, want 42", len(data))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if data[0] != 4 {
|
|
||||||
errorf("macho thread type = %d, want 4", data[0])
|
|
||||||
}
|
|
||||||
if data[1] != uint32(len(data))-2 {
|
|
||||||
errorf("macho thread desc len = %d, want %d", data[1], uint32(len(data))-2)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for i, val := range data[2:] {
|
|
||||||
switch i {
|
|
||||||
default:
|
|
||||||
if val != 0 {
|
|
||||||
errorf("macho thread data[%d] = %#x, want 0", i, val)
|
|
||||||
}
|
|
||||||
case 32:
|
|
||||||
prog.Entry = Addr(val)
|
|
||||||
case 33:
|
|
||||||
prog.Entry |= Addr(val) << 32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case *macho.Segment:
|
|
||||||
if load.Addr < limit {
|
|
||||||
errorf("segments out of order: %q at %#x after %#x", load.Name, load.Addr, limit)
|
|
||||||
}
|
|
||||||
limit = load.Addr + load.Memsz
|
|
||||||
if load.Name == "__PAGEZERO" || load.Addr == 0 && load.Filesz == 0 {
|
|
||||||
if load.Name != "__PAGEZERO" {
|
|
||||||
errorf("segment with Addr=0, Filesz=0 is named %q, want %q", load.Name, "__PAGEZERO")
|
|
||||||
} else if load.Addr != 0 || load.Filesz != 0 {
|
|
||||||
errorf("segment %q has Addr=%#x, Filesz=%d, want Addr=%#x, Filesz=%d", load.Name, load.Addr, load.Filesz, 0, 0)
|
|
||||||
}
|
|
||||||
prog.UnmappedSize = Addr(load.Memsz)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.HasPrefix(load.Name, "__") {
|
|
||||||
errorf("segment name %q does not begin with %q", load.Name, "__")
|
|
||||||
}
|
|
||||||
if strings.ToUpper(load.Name) != load.Name {
|
|
||||||
errorf("segment name %q is not all upper case", load.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
seg := &Segment{
|
|
||||||
Name: strings.ToLower(strings.TrimPrefix(load.Name, "__")),
|
|
||||||
VirtAddr: Addr(load.Addr),
|
|
||||||
VirtSize: Addr(load.Memsz),
|
|
||||||
FileOffset: Addr(load.Offset),
|
|
||||||
FileSize: Addr(load.Filesz),
|
|
||||||
}
|
|
||||||
prog.Segments = append(prog.Segments, seg)
|
|
||||||
|
|
||||||
data, err := load.Data()
|
|
||||||
if err != nil {
|
|
||||||
errorf("loading data from %q: %v", load.Name, err)
|
|
||||||
}
|
|
||||||
seg.Data = data
|
|
||||||
|
|
||||||
var maxprot, prot uint32
|
|
||||||
if load.Name == "__TEXT" {
|
|
||||||
maxprot, prot = 7, 5
|
|
||||||
} else {
|
|
||||||
maxprot, prot = 3, 3
|
|
||||||
}
|
|
||||||
if load.Maxprot != maxprot || load.Prot != prot {
|
|
||||||
errorf("segment %q protection is %d, %d, want %d, %d",
|
|
||||||
load.Name, load.Maxprot, load.Prot, maxprot, prot)
|
|
||||||
}
|
|
||||||
|
|
||||||
for len(msects) > 0 && msects[0].Addr < load.Addr+load.Memsz {
|
|
||||||
msect := msects[0]
|
|
||||||
msects = msects[1:]
|
|
||||||
|
|
||||||
if msect.Offset > 0 && prog.HeaderSize == 0 {
|
|
||||||
prog.HeaderSize = Addr(msect.Offset)
|
|
||||||
if seg.FileOffset != 0 {
|
|
||||||
errorf("initial segment %q does not map header", load.Name)
|
|
||||||
}
|
|
||||||
seg.VirtAddr += prog.HeaderSize
|
|
||||||
seg.VirtSize -= prog.HeaderSize
|
|
||||||
seg.FileOffset += prog.HeaderSize
|
|
||||||
seg.FileSize -= prog.HeaderSize
|
|
||||||
seg.Data = seg.Data[prog.HeaderSize:]
|
|
||||||
}
|
|
||||||
|
|
||||||
if msect.Addr < load.Addr {
|
|
||||||
errorf("section %q at address %#x is missing segment", msect.Name, msect.Addr)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.HasPrefix(msect.Name, "__") {
|
|
||||||
errorf("section name %q does not begin with %q", msect.Name, "__")
|
|
||||||
}
|
|
||||||
if strings.ToLower(msect.Name) != msect.Name {
|
|
||||||
errorf("section name %q is not all lower case", msect.Name)
|
|
||||||
}
|
|
||||||
if msect.Seg != load.Name {
|
|
||||||
errorf("section %q is lists segment name %q, want %q",
|
|
||||||
msect.Name, msect.Seg, load.Name)
|
|
||||||
}
|
|
||||||
if uint64(msect.Offset) != uint64(load.Offset)+msect.Addr-load.Addr {
|
|
||||||
errorf("section %q file offset is %#x, want %#x",
|
|
||||||
msect.Name, msect.Offset, load.Offset+msect.Addr-load.Addr)
|
|
||||||
}
|
|
||||||
if msect.Reloff != 0 || msect.Nreloc != 0 {
|
|
||||||
errorf("section %q has reloff %d,%d, want %d,%d",
|
|
||||||
msect.Name, msect.Reloff, msect.Nreloc, 0, 0)
|
|
||||||
}
|
|
||||||
flags := uint32(0)
|
|
||||||
if msect.Name == "__text" {
|
|
||||||
flags = 0x400
|
|
||||||
}
|
|
||||||
if msect.Offset == 0 {
|
|
||||||
flags = 1
|
|
||||||
}
|
|
||||||
if msect.Flags != flags {
|
|
||||||
errorf("section %q flags = %#x, want %#x", msect.Name, msect.Flags, flags)
|
|
||||||
}
|
|
||||||
sect := &Section{
|
|
||||||
Name: strings.ToLower(strings.TrimPrefix(msect.Name, "__")),
|
|
||||||
VirtAddr: Addr(msect.Addr),
|
|
||||||
Size: Addr(msect.Size),
|
|
||||||
Align: 1 << msect.Align,
|
|
||||||
}
|
|
||||||
seg.Sections = append(seg.Sections, sect)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, msect := range msects {
|
|
||||||
errorf("section %q has no segment", msect.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
limit = 0
|
|
||||||
for _, msect := range f.Sections {
|
|
||||||
if msect.Addr < limit {
|
|
||||||
errorf("sections out of order: %q at %#x after %#x", msect.Name, msect.Addr, limit)
|
|
||||||
}
|
|
||||||
limit = msect.Addr + msect.Size
|
|
||||||
}
|
|
||||||
|
|
||||||
err = nil
|
|
||||||
if errors != nil {
|
|
||||||
err = fmt.Errorf("%s", strings.Join(errors, "\n\t"))
|
|
||||||
}
|
|
||||||
return prog, err
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,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.
|
|
||||||
|
|
||||||
// Placeholder to keep build building.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
func main() {}
|
|
||||||
@@ -1,480 +0,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.
|
|
||||||
|
|
||||||
// Generation of runtime function information (pclntab).
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"cmd/internal/goobj"
|
|
||||||
"cmd/internal/obj"
|
|
||||||
"encoding/binary"
|
|
||||||
"os"
|
|
||||||
"sort"
|
|
||||||
)
|
|
||||||
|
|
||||||
var zerofunc goobj.Func
|
|
||||||
|
|
||||||
// pclntab collects the runtime function data for each function that will
|
|
||||||
// be listed in the binary and builds a single table describing all functions.
|
|
||||||
// This table is used at run time for stack traces and to look up PC-specific
|
|
||||||
// information during garbage collection. The symbol created is named
|
|
||||||
// "pclntab" for historical reasons; the scope of the table has grown to
|
|
||||||
// include more than just PC/line number correspondences.
|
|
||||||
// The table format is documented at https://golang.org/s/go12symtab.
|
|
||||||
func (p *Prog) pclntab() {
|
|
||||||
// Count number of functions going into the binary,
|
|
||||||
// so that we can size the initial index correctly.
|
|
||||||
nfunc := 0
|
|
||||||
for _, sym := range p.SymOrder {
|
|
||||||
if sym.Kind != goobj.STEXT {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
nfunc++
|
|
||||||
}
|
|
||||||
|
|
||||||
// Table header.
|
|
||||||
buf := new(SymBuffer)
|
|
||||||
buf.Init(p)
|
|
||||||
buf.SetSize(8 + p.ptrsize)
|
|
||||||
off := 0
|
|
||||||
off = buf.Uint32(off, 0xfffffffb)
|
|
||||||
off = buf.Uint8(off, 0)
|
|
||||||
off = buf.Uint8(off, 0)
|
|
||||||
off = buf.Uint8(off, uint8(p.pcquantum))
|
|
||||||
off = buf.Uint8(off, uint8(p.ptrsize))
|
|
||||||
off = buf.Uint(off, uint64(nfunc), p.ptrsize)
|
|
||||||
indexOff := off
|
|
||||||
off += (nfunc*2 + 1) * p.ptrsize // function index, to be filled in
|
|
||||||
off += 4 // file table start offset, to be filled in
|
|
||||||
buf.SetSize(off)
|
|
||||||
|
|
||||||
// One-file cache for reading PCData tables from package files.
|
|
||||||
// TODO(rsc): Better I/O strategy.
|
|
||||||
var (
|
|
||||||
file *os.File
|
|
||||||
fname string
|
|
||||||
)
|
|
||||||
|
|
||||||
// Files gives the file numbering for source file names recorded
|
|
||||||
// in the binary.
|
|
||||||
files := make(map[string]int)
|
|
||||||
|
|
||||||
// Build the table, build the index, and build the file name numbering.
|
|
||||||
// The loop here must visit functions in the same order that they will
|
|
||||||
// be stored in the binary, or else binary search over the index will fail.
|
|
||||||
// The runtime checks that the index is sorted properly at program start time.
|
|
||||||
var lastSym *Sym
|
|
||||||
for _, sym := range p.SymOrder {
|
|
||||||
if sym.Kind != goobj.STEXT {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
lastSym = sym
|
|
||||||
|
|
||||||
// Treat no recorded function information same as all zeros.
|
|
||||||
f := sym.Func
|
|
||||||
if f == nil {
|
|
||||||
f = &zerofunc
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open package file if needed, for reading PC data.
|
|
||||||
if fname != sym.Package.File {
|
|
||||||
if file != nil {
|
|
||||||
file.Close()
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
file, err = os.Open(sym.Package.File)
|
|
||||||
if err != nil {
|
|
||||||
p.errorf("%v: %v", sym, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fname = sym.Package.File
|
|
||||||
}
|
|
||||||
|
|
||||||
// off is the offset of the table entry where we're going to write
|
|
||||||
// the encoded form of Func.
|
|
||||||
// indexOff is the current position in the table index;
|
|
||||||
// we add an entry in the index pointing at off.
|
|
||||||
off = (buf.Size() + p.ptrsize - 1) &^ (p.ptrsize - 1)
|
|
||||||
indexOff = buf.Addr(indexOff, sym.SymID, 0)
|
|
||||||
indexOff = buf.Uint(indexOff, uint64(off), p.ptrsize)
|
|
||||||
|
|
||||||
// The Func encoding starts with a header giving offsets
|
|
||||||
// to data blobs, and then the data blobs themselves.
|
|
||||||
// end gives the current write position for the data blobs.
|
|
||||||
end := off + p.ptrsize + 3*4 + 5*4 + len(f.PCData)*4 + len(f.FuncData)*p.ptrsize
|
|
||||||
if len(f.FuncData) > 0 {
|
|
||||||
end += -end & (p.ptrsize - 1)
|
|
||||||
}
|
|
||||||
buf.SetSize(end)
|
|
||||||
|
|
||||||
// entry uintptr
|
|
||||||
// name int32
|
|
||||||
// args int32
|
|
||||||
// frame int32
|
|
||||||
//
|
|
||||||
// The frame recorded in the object file is
|
|
||||||
// the frame size used in an assembly listing, which does
|
|
||||||
// not include the caller PC on the stack.
|
|
||||||
// The frame size we want to list here is the delta from
|
|
||||||
// this function's SP to its caller's SP, which does include
|
|
||||||
// the caller PC. Add p.ptrsize to f.Frame to adjust.
|
|
||||||
// TODO(rsc): Record the same frame size in the object file.
|
|
||||||
off = buf.Addr(off, sym.SymID, 0)
|
|
||||||
off = buf.Uint32(off, uint32(addString(buf, sym.Name)))
|
|
||||||
off = buf.Uint32(off, uint32(f.Args))
|
|
||||||
off = buf.Uint32(off, uint32(f.Frame+p.ptrsize))
|
|
||||||
|
|
||||||
// pcdata
|
|
||||||
off = buf.Uint32(off, uint32(addPCTable(p, buf, file, f.PCSP)))
|
|
||||||
off = buf.Uint32(off, uint32(addPCFileTable(p, buf, file, f.PCFile, sym, files)))
|
|
||||||
off = buf.Uint32(off, uint32(addPCTable(p, buf, file, f.PCLine)))
|
|
||||||
off = buf.Uint32(off, uint32(len(f.PCData)))
|
|
||||||
off = buf.Uint32(off, uint32(len(f.FuncData)))
|
|
||||||
for _, pcdata := range f.PCData {
|
|
||||||
off = buf.Uint32(off, uint32(addPCTable(p, buf, file, pcdata)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// funcdata
|
|
||||||
if len(f.FuncData) > 0 {
|
|
||||||
off += -off & (p.ptrsize - 1) // must be pointer-aligned
|
|
||||||
for _, funcdata := range f.FuncData {
|
|
||||||
if funcdata.Sym.Name == "" {
|
|
||||||
off = buf.Uint(off, uint64(funcdata.Offset), p.ptrsize)
|
|
||||||
} else {
|
|
||||||
off = buf.Addr(off, funcdata.Sym, funcdata.Offset)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if off != end {
|
|
||||||
p.errorf("internal error: invalid math in pclntab: off=%#x end=%#x", off, end)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if file != nil {
|
|
||||||
file.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Final entry of index is end PC of last function.
|
|
||||||
indexOff = buf.Addr(indexOff, lastSym.SymID, int64(lastSym.Size))
|
|
||||||
|
|
||||||
// Start file table.
|
|
||||||
// Function index is immediately followed by offset to file table.
|
|
||||||
off = (buf.Size() + p.ptrsize - 1) &^ (p.ptrsize - 1)
|
|
||||||
buf.Uint32(indexOff, uint32(off))
|
|
||||||
|
|
||||||
// File table is an array of uint32s.
|
|
||||||
// The first entry gives 1+n, the size of the array.
|
|
||||||
// The following n entries hold offsets to string data.
|
|
||||||
// File number n uses the string pointed at by entry n.
|
|
||||||
// File number 0 is invalid.
|
|
||||||
buf.SetSize(off + (1+len(files))*4)
|
|
||||||
buf.Uint32(off, uint32(1+len(files)))
|
|
||||||
var filestr []string
|
|
||||||
for file := range files {
|
|
||||||
filestr = append(filestr, file)
|
|
||||||
}
|
|
||||||
sort.Strings(filestr)
|
|
||||||
for _, file := range filestr {
|
|
||||||
id := files[file]
|
|
||||||
buf.Uint32(off+4*id, uint32(addString(buf, file)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pclntab := &Sym{
|
|
||||||
Sym: &goobj.Sym{
|
|
||||||
SymID: goobj.SymID{Name: "runtime.pclntab"},
|
|
||||||
Kind: goobj.SPCLNTAB,
|
|
||||||
Size: buf.Size(),
|
|
||||||
Reloc: buf.Reloc(),
|
|
||||||
},
|
|
||||||
Bytes: buf.Bytes(),
|
|
||||||
}
|
|
||||||
p.addSym(pclntab)
|
|
||||||
}
|
|
||||||
|
|
||||||
// addString appends the string s to the buffer b.
|
|
||||||
// It returns the offset of the beginning of the string in the buffer.
|
|
||||||
func addString(b *SymBuffer, s string) int {
|
|
||||||
off := b.Size()
|
|
||||||
b.SetSize(off + len(s) + 1)
|
|
||||||
copy(b.data[off:], s)
|
|
||||||
return off
|
|
||||||
}
|
|
||||||
|
|
||||||
// addPCTable appends the PC-data table stored in the file f at the location loc
|
|
||||||
// to the symbol buffer b. It returns the offset of the beginning of the table
|
|
||||||
// in the buffer.
|
|
||||||
func addPCTable(p *Prog, b *SymBuffer, f *os.File, loc goobj.Data) int {
|
|
||||||
if loc.Size == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
off := b.Size()
|
|
||||||
b.SetSize(off + int(loc.Size))
|
|
||||||
_, err := f.ReadAt(b.data[off:off+int(loc.Size)], loc.Offset)
|
|
||||||
if err != nil {
|
|
||||||
p.errorf("%v", err)
|
|
||||||
}
|
|
||||||
return off
|
|
||||||
}
|
|
||||||
|
|
||||||
// addPCFileTable is like addPCTable, but it renumbers the file names referred to by the table
|
|
||||||
// to use the global numbering maintained in the files map. It adds new files to the
|
|
||||||
// map as necessary.
|
|
||||||
func addPCFileTable(p *Prog, b *SymBuffer, f *os.File, loc goobj.Data, sym *Sym, files map[string]int) int {
|
|
||||||
if loc.Size == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
off := b.Size()
|
|
||||||
|
|
||||||
src := make([]byte, loc.Size)
|
|
||||||
_, err := f.ReadAt(src, loc.Offset)
|
|
||||||
if err != nil {
|
|
||||||
p.errorf("%v", err)
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
filenum := make([]int, len(sym.Func.File))
|
|
||||||
for i, name := range sym.Func.File {
|
|
||||||
num := files[name]
|
|
||||||
if num == 0 {
|
|
||||||
num = len(files) + 1
|
|
||||||
files[name] = num
|
|
||||||
}
|
|
||||||
filenum[i] = num
|
|
||||||
}
|
|
||||||
|
|
||||||
var dst []byte
|
|
||||||
newval := int32(-1)
|
|
||||||
var it PCIter
|
|
||||||
for it.Init(p, src); !it.Done; it.Next() {
|
|
||||||
// value delta
|
|
||||||
oldval := it.Value
|
|
||||||
val := oldval
|
|
||||||
if oldval != -1 {
|
|
||||||
if oldval < 0 || int(oldval) >= len(filenum) {
|
|
||||||
p.errorf("%s: corrupt pc-file table", sym)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
val = int32(filenum[oldval])
|
|
||||||
}
|
|
||||||
dv := val - newval
|
|
||||||
newval = val
|
|
||||||
uv := uint32(dv<<1) ^ uint32(dv>>31)
|
|
||||||
dst = appendVarint(dst, uv)
|
|
||||||
|
|
||||||
// pc delta
|
|
||||||
dst = appendVarint(dst, it.NextPC-it.PC)
|
|
||||||
}
|
|
||||||
if it.Corrupt {
|
|
||||||
p.errorf("%s: corrupt pc-file table", sym)
|
|
||||||
}
|
|
||||||
|
|
||||||
// terminating value delta
|
|
||||||
dst = appendVarint(dst, 0)
|
|
||||||
|
|
||||||
b.SetSize(off + len(dst))
|
|
||||||
copy(b.data[off:], dst)
|
|
||||||
return off
|
|
||||||
}
|
|
||||||
|
|
||||||
// A SymBuffer is a buffer for preparing the data image of a
|
|
||||||
// linker-generated symbol.
|
|
||||||
type SymBuffer struct {
|
|
||||||
data []byte
|
|
||||||
reloc []goobj.Reloc
|
|
||||||
order binary.ByteOrder
|
|
||||||
ptrsize int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init initializes the buffer for writing.
|
|
||||||
func (b *SymBuffer) Init(p *Prog) {
|
|
||||||
b.data = nil
|
|
||||||
b.reloc = nil
|
|
||||||
b.order = p.byteorder
|
|
||||||
b.ptrsize = p.ptrsize
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes returns the buffer data.
|
|
||||||
func (b *SymBuffer) Bytes() []byte {
|
|
||||||
return b.data
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetSize sets the buffer's data size to n bytes.
|
|
||||||
func (b *SymBuffer) SetSize(n int) {
|
|
||||||
for cap(b.data) < n {
|
|
||||||
b.data = append(b.data[:cap(b.data)], 0)
|
|
||||||
}
|
|
||||||
b.data = b.data[:n]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size returns the buffer's data size.
|
|
||||||
func (b *SymBuffer) Size() int {
|
|
||||||
return len(b.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reloc returns the buffered relocations.
|
|
||||||
func (b *SymBuffer) Reloc() []goobj.Reloc {
|
|
||||||
return b.reloc
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uint8 sets the uint8 at offset off to v.
|
|
||||||
// It returns the offset just beyond v.
|
|
||||||
func (b *SymBuffer) Uint8(off int, v uint8) int {
|
|
||||||
b.data[off] = v
|
|
||||||
return off + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uint16 sets the uint16 at offset off to v.
|
|
||||||
// It returns the offset just beyond v.
|
|
||||||
func (b *SymBuffer) Uint16(off int, v uint16) int {
|
|
||||||
b.order.PutUint16(b.data[off:], v)
|
|
||||||
return off + 2
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uint32 sets the uint32 at offset off to v.
|
|
||||||
// It returns the offset just beyond v.
|
|
||||||
func (b *SymBuffer) Uint32(off int, v uint32) int {
|
|
||||||
b.order.PutUint32(b.data[off:], v)
|
|
||||||
return off + 4
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uint64 sets the uint64 at offset off to v.
|
|
||||||
// It returns the offset just beyond v.
|
|
||||||
func (b *SymBuffer) Uint64(off int, v uint64) int {
|
|
||||||
b.order.PutUint64(b.data[off:], v)
|
|
||||||
return off + 8
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uint sets the size-byte unsigned integer at offset off to v.
|
|
||||||
// It returns the offset just beyond v.
|
|
||||||
func (b *SymBuffer) Uint(off int, v uint64, size int) int {
|
|
||||||
switch size {
|
|
||||||
case 1:
|
|
||||||
return b.Uint8(off, uint8(v))
|
|
||||||
case 2:
|
|
||||||
return b.Uint16(off, uint16(v))
|
|
||||||
case 4:
|
|
||||||
return b.Uint32(off, uint32(v))
|
|
||||||
case 8:
|
|
||||||
return b.Uint64(off, v)
|
|
||||||
}
|
|
||||||
panic("invalid use of SymBuffer.SetUint")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Addr sets the pointer-sized address at offset off to refer
|
|
||||||
// to symoff bytes past the start of sym. It returns the offset
|
|
||||||
// just beyond the address.
|
|
||||||
func (b *SymBuffer) Addr(off int, sym goobj.SymID, symoff int64) int {
|
|
||||||
b.reloc = append(b.reloc, goobj.Reloc{
|
|
||||||
Offset: off,
|
|
||||||
Size: b.ptrsize,
|
|
||||||
Sym: sym,
|
|
||||||
Add: int(symoff),
|
|
||||||
Type: obj.R_ADDR,
|
|
||||||
})
|
|
||||||
return off + b.ptrsize
|
|
||||||
}
|
|
||||||
|
|
||||||
// A PCIter implements iteration over PC-data tables.
|
|
||||||
//
|
|
||||||
// var it PCIter
|
|
||||||
// for it.Init(p, data); !it.Done; it.Next() {
|
|
||||||
// it.Value holds from it.PC up to (but not including) it.NextPC
|
|
||||||
// }
|
|
||||||
// if it.Corrupt {
|
|
||||||
// data was malformed
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
type PCIter struct {
|
|
||||||
PC uint32
|
|
||||||
NextPC uint32
|
|
||||||
Value int32
|
|
||||||
Done bool
|
|
||||||
Corrupt bool
|
|
||||||
p []byte
|
|
||||||
start bool
|
|
||||||
pcquantum uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init initializes the iteration.
|
|
||||||
// On return, if it.Done is true, the iteration is over.
|
|
||||||
// Otherwise it.Value applies in the pc range [it.PC, it.NextPC).
|
|
||||||
func (it *PCIter) Init(p *Prog, buf []byte) {
|
|
||||||
it.p = buf
|
|
||||||
it.PC = 0
|
|
||||||
it.NextPC = 0
|
|
||||||
it.Value = -1
|
|
||||||
it.start = true
|
|
||||||
it.pcquantum = uint32(p.pcquantum)
|
|
||||||
it.Done = false
|
|
||||||
it.Next()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next steps forward one entry in the table.
|
|
||||||
// On return, if it.Done is true, the iteration is over.
|
|
||||||
// Otherwise it.Value applies in the pc range [it.PC, it.NextPC).
|
|
||||||
func (it *PCIter) Next() {
|
|
||||||
it.PC = it.NextPC
|
|
||||||
if it.Done {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(it.p) == 0 {
|
|
||||||
it.Done = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// value delta
|
|
||||||
uv, p, ok := decodeVarint(it.p)
|
|
||||||
if !ok {
|
|
||||||
it.Done = true
|
|
||||||
it.Corrupt = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
it.p = p
|
|
||||||
if uv == 0 && !it.start {
|
|
||||||
it.Done = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
it.start = false
|
|
||||||
sv := int32(uv>>1) ^ int32(uv<<31)>>31
|
|
||||||
it.Value += sv
|
|
||||||
|
|
||||||
// pc delta
|
|
||||||
uv, it.p, ok = decodeVarint(it.p)
|
|
||||||
if !ok {
|
|
||||||
it.Done = true
|
|
||||||
it.Corrupt = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
it.NextPC = it.PC + uv*it.pcquantum
|
|
||||||
}
|
|
||||||
|
|
||||||
// decodeVarint decodes an unsigned varint from p,
|
|
||||||
// reporting the value, the remainder of the data, and
|
|
||||||
// whether the decoding was successful.
|
|
||||||
func decodeVarint(p []byte) (v uint32, rest []byte, ok bool) {
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if len(p) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c := uint32(p[0])
|
|
||||||
p = p[1:]
|
|
||||||
v |= (c & 0x7F) << shift
|
|
||||||
if c&0x80 == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return v, p, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// appendVarint appends an unsigned varint encoding of v to p
|
|
||||||
// and returns the resulting slice.
|
|
||||||
func appendVarint(p []byte, v uint32) []byte {
|
|
||||||
for ; v >= 0x80; v >>= 7 {
|
|
||||||
p = append(p, byte(v)|0x80)
|
|
||||||
}
|
|
||||||
p = append(p, byte(v))
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
@@ -1,340 +0,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.
|
|
||||||
|
|
||||||
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])
|
|
||||||
}
|
|
||||||
@@ -1,220 +0,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.
|
|
||||||
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@@ -1,163 +0,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.
|
|
||||||
|
|
||||||
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])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,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.
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,187 +0,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.
|
|
||||||
|
|
||||||
// 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")
|
|
||||||
}
|
|
||||||
15
src/cmd/newlink/testdata/Makefile
vendored
15
src/cmd/newlink/testdata/Makefile
vendored
@@ -1,15 +0,0 @@
|
|||||||
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
|
|
||||||
BIN
src/cmd/newlink/testdata/autosection.6
vendored
BIN
src/cmd/newlink/testdata/autosection.6
vendored
Binary file not shown.
60
src/cmd/newlink/testdata/autosection.s
vendored
60
src/cmd/newlink/testdata/autosection.s
vendored
@@ -1,60 +0,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 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
|
|
||||||
BIN
src/cmd/newlink/testdata/autoweak.6
vendored
BIN
src/cmd/newlink/testdata/autoweak.6
vendored
Binary file not shown.
30
src/cmd/newlink/testdata/autoweak.s
vendored
30
src/cmd/newlink/testdata/autoweak.s
vendored
@@ -1,30 +0,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 go.weak symbols.
|
|
||||||
|
|
||||||
TEXT start(SB),7,$0
|
|
||||||
MOVQ $autotab(SB),AX
|
|
||||||
MOVQ $autoptr(SB),AX
|
|
||||||
RET
|
|
||||||
|
|
||||||
// go.weak.sym should resolve to sym, because sym is in the binary.
|
|
||||||
DATA autotab+0(SB)/8, $go·weak·sym(SB)
|
|
||||||
DATA autotab+8(SB)/8, $sym(SB)
|
|
||||||
|
|
||||||
// go.weak.missingsym should resolve to 0, because missingsym is not in the binary.
|
|
||||||
DATA autotab+16(SB)/8, $go·weak·missingsym(SB)
|
|
||||||
DATA autotab+24(SB)/8, $0
|
|
||||||
|
|
||||||
// go.weak.deadsym should resolve to 0, because deadsym is discarded during dead code removal
|
|
||||||
DATA autotab+32(SB)/8, $go·weak·deadsym(SB)
|
|
||||||
DATA autotab+40(SB)/8, $0
|
|
||||||
|
|
||||||
GLOBL autotab(SB), $48
|
|
||||||
|
|
||||||
GLOBL sym(SB), $1
|
|
||||||
|
|
||||||
GLOBL deadsym(SB), $1
|
|
||||||
|
|
||||||
GLOBL autoptr(SB), $0
|
|
||||||
BIN
src/cmd/newlink/testdata/dead.6
vendored
BIN
src/cmd/newlink/testdata/dead.6
vendored
Binary file not shown.
48
src/cmd/newlink/testdata/dead.s
vendored
48
src/cmd/newlink/testdata/dead.s
vendored
@@ -1,48 +0,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
|
|
||||||
112
src/cmd/newlink/testdata/genpcln.go
vendored
112
src/cmd/newlink/testdata/genpcln.go
vendored
@@ -1,112 +0,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.
|
|
||||||
|
|
||||||
// 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")
|
|
||||||
}
|
|
||||||
BIN
src/cmd/newlink/testdata/hello.6
vendored
BIN
src/cmd/newlink/testdata/hello.6
vendored
Binary file not shown.
15
src/cmd/newlink/testdata/hello.s
vendored
15
src/cmd/newlink/testdata/hello.s
vendored
@@ -1,15 +0,0 @@
|
|||||||
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
|
|
||||||
BIN
src/cmd/newlink/testdata/layout.6
vendored
BIN
src/cmd/newlink/testdata/layout.6
vendored
Binary file not shown.
29
src/cmd/newlink/testdata/layout.s
vendored
29
src/cmd/newlink/testdata/layout.s
vendored
@@ -1,29 +0,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 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
|
|
||||||
55
src/cmd/newlink/testdata/link.hello.darwin.amd64
vendored
55
src/cmd/newlink/testdata/link.hello.darwin.amd64
vendored
@@ -1,55 +0,0 @@
|
|||||||
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
|
|
||||||
24
src/cmd/newlink/testdata/macho.amd64.exit9
vendored
24
src/cmd/newlink/testdata/macho.amd64.exit9
vendored
@@ -1,24 +0,0 @@
|
|||||||
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
|
|
||||||
39
src/cmd/newlink/testdata/macho.amd64.hello
vendored
39
src/cmd/newlink/testdata/macho.amd64.hello
vendored
@@ -1,39 +0,0 @@
|
|||||||
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
|
|
||||||
34
src/cmd/newlink/testdata/macho.amd64.helloro
vendored
34
src/cmd/newlink/testdata/macho.amd64.helloro
vendored
@@ -1,34 +0,0 @@
|
|||||||
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
|
|
||||||
BIN
src/cmd/newlink/testdata/pclntab.6
vendored
BIN
src/cmd/newlink/testdata/pclntab.6
vendored
Binary file not shown.
1751
src/cmd/newlink/testdata/pclntab.s
vendored
1751
src/cmd/newlink/testdata/pclntab.s
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,11 +0,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.
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,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.
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user