Commit 7cecac3c authored by Russ Cox's avatar Russ Cox

cmd/link: implement and test automatic symbols

Related changes included in this CL:

 - Add explicit start symbol to Prog.
 - Add omitRuntime bool to Prog.
 - Introduce p.Packages[""] to hold automatic symbols
 - Add SymOrder to Prog to preserve symbol order.
 - Add layout test (and fix bug that was putting everything in text section).

R=iant
CC=golang-codereviews
https://golang.org/cl/51260045
parent 9c1aa658
// 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 (
"debug/goobj"
"strconv"
"strings"
)
// linkerDefined lists the symbols supplied by other parts of the linker
// (runtime.go and layout.go).
var linkerDefined = map[string]bool{
"bss": true,
"data": true,
"ebss": true,
"edata": true,
"efunctab": true,
"end": true,
"enoptrbss": true,
"enoptrdata": true,
"erodata": true,
"etext": true,
"etypelink": true,
"functab": true,
"gcbss": true,
"gcdata": true,
"noptrbss": true,
"noptrdata": true,
"pclntab": true,
"rodata": true,
"text": true,
"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,
})
}
// 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"
"debug/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)
}
}
}
}
...@@ -25,21 +25,19 @@ type layoutSection struct { ...@@ -25,21 +25,19 @@ type layoutSection struct {
// Entries with the same Segment name must be contiguous. // Entries with the same Segment name must be contiguous.
var layout = []layoutSection{ var layout = []layoutSection{
{Segment: "text", Section: "text", Kind: goobj.STEXT}, {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: "data", Kind: goobj.SDATA},
{Segment: "data", Section: "bss", Kind: goobj.SBSS},
{Segment: "data", Section: "noptrbss", Kind: goobj.SNOPTRBSS},
// Later: // Later:
// {"rodata", "type", goobj.STYPE}, // {"rodata", "type", goobj.STYPE},
// {"rodata", "string", goobj.SSTRING}, // {"rodata", "string", goobj.SSTRING},
// {"rodata", "gostring", goobj.SGOSTRING}, // {"rodata", "gostring", goobj.SGOSTRING},
// {"rodata", "gofunc", goobj.SGOFUNC}, // {"rodata", "gofunc", goobj.SGOFUNC},
// {"rodata", "rodata", goobj.SRODATA},
// {"rodata", "functab", goobj.SFUNCTAB},
// {"rodata", "typelink", goobj.STYPELINK},
// {"rodata", "symtab", goobj.SSYMTAB},
// {"rodata", "pclntab", goobj.SPCLNTAB},
// {"data", "noptrdata", goobj.SNOPTRDATA},
// {"data", "bss", goobj.SBSS},
// {"data", "noptrbss", goobj.SNOPTRBSS},
} }
// layoutByKind maps from SymKind to an entry in layout. // layoutByKind maps from SymKind to an entry in layout.
...@@ -54,8 +52,9 @@ func init() { ...@@ -54,8 +52,9 @@ func init() {
} }
} }
layoutByKind = make([]*layoutSection, max) layoutByKind = make([]*layoutSection, max)
for i, sect := range layout { for i := range layout {
layoutByKind[sect.Kind] = &layout[i] sect := &layout[i]
layoutByKind[sect.Kind] = sect
sect.Index = i sect.Index = i
} }
} }
...@@ -67,7 +66,7 @@ func (p *Prog) layout() { ...@@ -67,7 +66,7 @@ func (p *Prog) layout() {
// Assign symbols to sections using index, creating sections as needed. // Assign symbols to sections using index, creating sections as needed.
// Could keep sections separated by type during input instead. // Could keep sections separated by type during input instead.
for _, sym := range p.Syms { for _, sym := range p.SymOrder {
kind := sym.Kind kind := sym.Kind
if kind < 0 || int(kind) >= len(layoutByKind) || layoutByKind[kind] == nil { if kind < 0 || int(kind) >= len(layoutByKind) || layoutByKind[kind] == nil {
p.errorf("%s: unexpected symbol kind %v", sym.SymID, kind) p.errorf("%s: unexpected symbol kind %v", sym.SymID, kind)
...@@ -82,7 +81,7 @@ func (p *Prog) layout() { ...@@ -82,7 +81,7 @@ func (p *Prog) layout() {
} }
sections[lsect.Index] = sect sections[lsect.Index] = sect
} }
if sym.Data.Size > 0 { if sym.Data.Size > 0 || len(sym.Bytes) > 0 {
sect.InFile = true sect.InFile = true
} }
sym.Section = sect sym.Section = sect
...@@ -102,9 +101,17 @@ func (p *Prog) layout() { ...@@ -102,9 +101,17 @@ func (p *Prog) layout() {
if sect == nil { if sect == nil {
continue continue
} }
if seg == nil || seg.Name != layout[i].Segment { 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{ seg = &Segment{
Name: layout[i].Segment, Name: segName,
} }
p.Segments = append(p.Segments, seg) p.Segments = append(p.Segments, seg)
} }
...@@ -153,6 +160,21 @@ func (p *Prog) layout() { ...@@ -153,6 +160,21 @@ func (p *Prog) layout() {
seg.FileSize = addr - seg.VirtAddr seg.FileSize = addr - seg.VirtAddr
} }
} }
seg.VirtSize = addr 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(name, start)
p.defineConst("e"+name, end)
progEnd = end
} }
p.defineConst("end", progEnd)
} }
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"strings"
"testing"
)
func TestLayout(t *testing.T) {
p := Prog{GOOS: "darwin", GOARCH: "amd64", StartSym: "text_start"}
p.omitRuntime = true
p.Error = func(s string) { t.Error(s) }
var buf bytes.Buffer
const obj = "testdata/layout.6"
p.link(&buf, obj)
if p.NumError > 0 {
return // already reported
}
if len(p.Dead) > 0 {
t.Errorf("%s: unexpected dead symbols %v", obj, p.Dead)
return
}
for _, sym := range p.SymOrder {
if p.isAuto(sym.SymID) {
continue
}
if sym.Section == nil {
t.Errorf("%s: symbol %s is missing section", obj, sym)
continue
}
i := strings.Index(sym.Name, "_")
if i < 0 {
t.Errorf("%s: unexpected symbol %s", obj, sym)
continue
}
if sym.Section.Name != sym.Name[:i] {
t.Errorf("%s: symbol %s in section %s, want %s", obj, sym, sym.Section.Name, sym.Name[:i])
}
}
}
...@@ -7,26 +7,29 @@ package main ...@@ -7,26 +7,29 @@ package main
import ( import (
"bytes" "bytes"
"debug/goobj" "debug/goobj"
"io/ioutil"
"testing" "testing"
) )
func TestLinkHello(t *testing.T) { func TestLinkHello(t *testing.T) {
p := &Prog{ p := &Prog{
GOOS: "darwin", GOOS: "darwin",
GOARCH: "amd64", GOARCH: "amd64",
Error: func(s string) { t.Error(s) }, Error: func(s string) { t.Error(s) },
StartSym: "_rt0_go",
} }
var buf bytes.Buffer var buf bytes.Buffer
p.link(&buf, "testdata/hello.6") p.link(&buf, "testdata/hello.6")
if p.NumError > 0 { if p.NumError > 0 {
return return
} }
if len(p.Syms) != 2 || p.Syms[goobj.SymID{"_rt0_go", 0}] == nil || p.Syms[goobj.SymID{"hello", 1}] == nil { if p.Syms[goobj.SymID{"_rt0_go", 0}] == nil || p.Syms[goobj.SymID{"hello", 1}] == nil {
t.Errorf("Syms = %v, want [_rt0_go hello<1>]", p.Syms) t.Errorf("Syms = %v, want at least [_rt0_go hello<1>]", p.Syms)
} }
checkGolden(t, buf.Bytes(), "testdata/link.hello.darwin.amd64")
// uncomment to leave file behind for execution: // uncomment to leave file behind for execution:
// ioutil.WriteFile("a.out", buf.Bytes(), 0777) if false {
ioutil.WriteFile("a.out", buf.Bytes(), 0777)
}
checkGolden(t, buf.Bytes(), "testdata/link.hello.darwin.amd64")
} }
...@@ -6,10 +6,7 @@ ...@@ -6,10 +6,7 @@
package main package main
import ( import "os"
"encoding/binary"
"os"
)
// load allocates segment images, populates them with data // load allocates segment images, populates them with data
// read from package files, and applies relocations to the data. // read from package files, and applies relocations to the data.
...@@ -27,6 +24,24 @@ func (p *Prog) load() { ...@@ -27,6 +24,24 @@ func (p *Prog) load() {
// loadPackage loads and relocates data for all the // loadPackage loads and relocates data for all the
// symbols needed in the given package. // symbols needed in the given package.
func (p *Prog) loadPackage(pkg *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) f, err := os.Open(pkg.File)
if err != nil { if err != nil {
p.errorf("%v", err) p.errorf("%v", err)
...@@ -41,8 +56,14 @@ func (p *Prog) loadPackage(pkg *Package) { ...@@ -41,8 +56,14 @@ func (p *Prog) loadPackage(pkg *Package) {
continue continue
} }
// TODO(rsc): If not using mmap, at least coalesce nearby reads. // 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 seg := sym.Section.Segment
off := sym.Addr - seg.VirtAddr 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)] data := seg.Data[off : off+Addr(sym.Data.Size)]
_, err := f.ReadAt(data, sym.Data.Offset) _, err := f.ReadAt(data, sym.Data.Offset)
if err != nil { if err != nil {
...@@ -89,10 +110,9 @@ func (p *Prog) relocateSym(sym *Sym, data []byte) { ...@@ -89,10 +110,9 @@ func (p *Prog) relocateSym(sym *Sym, data []byte) {
p.errorf("%v: unknown relocation size %d", sym, r.Size) p.errorf("%v: unknown relocation size %d", sym, r.Size)
case 4: case 4:
// TODO(rsc): Check for overflow? // TODO(rsc): Check for overflow?
// TODO(rsc): Handle big-endian systems. p.byteorder.PutUint32(frag, uint32(val))
binary.LittleEndian.PutUint32(frag, uint32(val))
case 8: case 8:
binary.LittleEndian.PutUint64(frag, uint64(val)) p.byteorder.PutUint64(frag, uint64(val))
} }
} }
} }
...@@ -315,12 +315,7 @@ func (h *machoHeader) size() int { ...@@ -315,12 +315,7 @@ func (h *machoHeader) size() int {
func (h *machoHeader) encode() []byte { func (h *machoHeader) encode() []byte {
w := &machoWriter{p: h.p} w := &machoWriter{p: h.p}
w.is64 = h.CPU&macho64Bit != 0 w.is64 = h.CPU&macho64Bit != 0
switch h.SubCPU { w.order = w.p.byteorder
default:
h.p.errorf("mach-o error: unknown CPU")
case machoSubCPU386:
w.order = binary.LittleEndian
}
loadSize := 0 loadSize := 0
for _, seg := range h.Segments { for _, seg := range h.Segments {
......
...@@ -28,6 +28,7 @@ var machoWriteTests = []struct { ...@@ -28,6 +28,7 @@ var machoWriteTests = []struct {
golden: true, golden: true,
prog: &Prog{ prog: &Prog{
GOARCH: "amd64", GOARCH: "amd64",
GOOS: "darwin",
UnmappedSize: 0x1000, UnmappedSize: 0x1000,
Entry: 0x1000, Entry: 0x1000,
Segments: []*Segment{ Segments: []*Segment{
...@@ -62,6 +63,7 @@ var machoWriteTests = []struct { ...@@ -62,6 +63,7 @@ var machoWriteTests = []struct {
golden: true, golden: true,
prog: &Prog{ prog: &Prog{
GOARCH: "amd64", GOARCH: "amd64",
GOOS: "darwin",
UnmappedSize: 0x1000, UnmappedSize: 0x1000,
Entry: 0x1000, Entry: 0x1000,
Segments: []*Segment{ Segments: []*Segment{
...@@ -117,6 +119,7 @@ var machoWriteTests = []struct { ...@@ -117,6 +119,7 @@ var machoWriteTests = []struct {
golden: true, golden: true,
prog: &Prog{ prog: &Prog{
GOARCH: "amd64", GOARCH: "amd64",
GOOS: "darwin",
UnmappedSize: 0x1000, UnmappedSize: 0x1000,
Entry: 0x1000, Entry: 0x1000,
Segments: []*Segment{ Segments: []*Segment{
...@@ -173,6 +176,7 @@ func TestMachoWrite(t *testing.T) { ...@@ -173,6 +176,7 @@ func TestMachoWrite(t *testing.T) {
for _, tt := range machoWriteTests { for _, tt := range machoWriteTests {
name := tt.prog.GOARCH + "." + tt.name name := tt.prog.GOARCH + "." + tt.name
prog := cloneProg(tt.prog) prog := cloneProg(tt.prog)
prog.init()
var f machoFormat var f machoFormat
vsize, fsize := f.headerSize(prog) vsize, fsize := f.headerSize(prog)
shiftProg(prog, vsize, fsize) shiftProg(prog, vsize, fsize)
......
...@@ -6,10 +6,12 @@ package main ...@@ -6,10 +6,12 @@ package main
import ( import (
"debug/goobj" "debug/goobj"
"encoding/binary"
"fmt" "fmt"
"go/build" "go/build"
"io" "io"
"os" "os"
"runtime"
) )
// A Prog holds state for constructing an executable (program) image. // A Prog holds state for constructing an executable (program) image.
...@@ -31,17 +33,26 @@ import ( ...@@ -31,17 +33,26 @@ import (
// //
type Prog struct { type Prog struct {
// Context // Context
GOOS string // target operating system GOOS string // target operating system
GOARCH string // target architecture GOARCH string // target architecture
Format string // desired file format ("elf", "macho", ...) Format string // desired file format ("elf", "macho", ...)
formatter formatter Error func(string) // called to report an error (if set)
Error func(string) // called to report an error (if set) NumError int // number of errors printed
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 // Input
Packages map[string]*Package // loaded packages, by import path Packages map[string]*Package // loaded packages, by import path
Syms map[goobj.SymID]*Sym // defined symbols, by symbol ID Syms map[goobj.SymID]*Sym // defined symbols, by symbol ID
Missing map[goobj.SymID]bool // missing 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 MaxVersion int // max SymID.Version, for generating fresh symbol IDs
// Output // Output
...@@ -51,8 +62,11 @@ type Prog struct { ...@@ -51,8 +62,11 @@ type Prog struct {
Segments []*Segment // loaded memory segments Segments []*Segment // loaded memory segments
} }
// startSymID is the symbol where program execution begins. // An arch describes architecture-dependent settings.
var startSymID = goobj.SymID{Name: "_rt0_go"} type arch struct {
byteorder binary.ByteOrder
ptrsize int
}
// A formatter takes care of the details of generating a particular // A formatter takes care of the details of generating a particular
// kind of executable file. // kind of executable file.
...@@ -86,6 +100,7 @@ type Sym struct { ...@@ -86,6 +100,7 @@ type Sym struct {
Package *Package // package defining symbol Package *Package // package defining symbol
Section *Section // section where symbol is placed in output program Section *Section // section where symbol is placed in output program
Addr Addr // virtual address of symbol 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 Segment is a loaded memory segment.
...@@ -131,7 +146,9 @@ func (p *Prog) link(w io.Writer, mainFile string) { ...@@ -131,7 +146,9 @@ func (p *Prog) link(w io.Writer, mainFile string) {
} }
p.dead() p.dead()
p.runtime() p.runtime()
p.autoData()
p.layout() p.layout()
p.autoConst()
if p.NumError > 0 { if p.NumError > 0 {
return return
} }
...@@ -162,6 +179,9 @@ func (p *Prog) init() { ...@@ -162,6 +179,9 @@ func (p *Prog) init() {
return return
} }
} }
if p.StartSym == "" {
p.StartSym = fmt.Sprintf("_rt0_%s_%s", p.GOARCH, p.GOOS)
}
// Derive internal context. // Derive internal context.
p.formatter = formatters[p.Format] p.formatter = formatters[p.Format]
...@@ -169,6 +189,15 @@ func (p *Prog) init() { ...@@ -169,6 +189,15 @@ func (p *Prog) init() {
p.errorf("unknown output file format %q", p.Format) p.errorf("unknown output file format %q", p.Format)
return 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. // goosFormat records the default format for each known GOOS value.
...@@ -180,3 +209,10 @@ var goosFormat = map[string]string{ ...@@ -180,3 +209,10 @@ var goosFormat = map[string]string{
var formatters = map[string]formatter{ var formatters = map[string]formatter{
"darwin": machoFormat{}, "darwin": machoFormat{},
} }
var arches = map[string]arch{
"amd64": {
byteorder: binary.LittleEndian,
ptrsize: 8,
},
}
...@@ -13,6 +13,7 @@ package main ...@@ -13,6 +13,7 @@ package main
import ( import (
"debug/goobj" "debug/goobj"
"os" "os"
"sort"
"strings" "strings"
) )
...@@ -20,9 +21,20 @@ import ( ...@@ -20,9 +21,20 @@ import (
func (p *Prog) scan(mainfile string) { func (p *Prog) scan(mainfile string) {
p.initScan() p.initScan()
p.scanFile("main", mainfile) p.scanFile("main", mainfile)
if len(p.Missing) != 0 { if len(p.Missing) > 0 && !p.omitRuntime {
// TODO(rsc): iterate in deterministic order p.scanImport("runtime")
for sym := range p.Missing { }
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) p.errorf("undefined: %s", sym)
} }
} }
...@@ -35,7 +47,7 @@ func (p *Prog) initScan() { ...@@ -35,7 +47,7 @@ func (p *Prog) initScan() {
p.Packages = make(map[string]*Package) p.Packages = make(map[string]*Package)
p.Syms = make(map[goobj.SymID]*Sym) p.Syms = make(map[goobj.SymID]*Sym)
p.Missing = make(map[goobj.SymID]bool) p.Missing = make(map[goobj.SymID]bool)
p.Missing[startSymID] = true p.Missing[p.startSym] = true
} }
// scanFile reads file to learn about the package with the given import path. // scanFile reads file to learn about the package with the given import path.
...@@ -81,21 +93,62 @@ func (p *Prog) scanFile(pkgpath string, file string) { ...@@ -81,21 +93,62 @@ func (p *Prog) scanFile(pkgpath string, file string) {
if r.Sym.Version != 0 { if r.Sym.Version != 0 {
r.Sym.Version += p.MaxVersion r.Sym.Version += p.MaxVersion
} }
if p.Syms[r.Sym] != nil { if p.Syms[r.Sym] == nil {
p.Missing[r.Sym] = true 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 { if old := p.Syms[gs.SymID]; old != nil {
p.errorf("symbol %s defined in both %s and %s", old.Package.File, file) // Duplicate definition of symbol. Is it okay?
continue // 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{ s := &Sym{
Sym: gs, Sym: gs,
Package: pkg, Package: pkg,
} }
pkg.Syms = append(pkg.Syms, s) p.addSym(s)
p.Syms[gs.SymID] = s
delete(p.Missing, gs.SymID) 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 p.MaxVersion += pkg.MaxVersion
...@@ -108,6 +161,21 @@ func (p *Prog) scanFile(pkgpath string, file string) { ...@@ -108,6 +161,21 @@ func (p *Prog) scanFile(pkgpath string, file string) {
} }
} }
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. // scanImport finds the object file for the given import path and then scans it.
func (p *Prog) scanImport(pkgpath string) { func (p *Prog) scanImport(pkgpath string) {
if p.Packages[pkgpath] != nil { if p.Packages[pkgpath] != nil {
...@@ -115,5 +183,5 @@ func (p *Prog) scanImport(pkgpath string) { ...@@ -115,5 +183,5 @@ func (p *Prog) scanImport(pkgpath string) {
} }
// TODO(rsc): Implement correct search to find file. // TODO(rsc): Implement correct search to find file.
p.scanFile(pkgpath, "/Users/rsc/rscgo/pkg/darwin_amd64/"+pkgpath+".a") p.scanFile(pkgpath, p.pkgdir+"/"+pkgpath+".a")
} }
// 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 "../../ld/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, $text(SB)
DATA autotab+0x08(SB)/8, $start(SB)
DATA autotab+0x10(SB)/8, $etext(SB)
DATA autotab+0x18(SB)/8, $start+16(SB)
// data
DATA autotab+0x20(SB)/8, $data(SB)
DATA autotab+0x28(SB)/8, $autotab(SB)
DATA autotab+0x30(SB)/8, $edata(SB)
DATA autotab+0x38(SB)/8, $nonzero+4(SB)
// bss
DATA autotab+0x40(SB)/8, $bss(SB)
DATA autotab+0x48(SB)/8, $zero(SB)
DATA autotab+0x50(SB)/8, $ebss(SB)
DATA autotab+0x58(SB)/8, $zero+8(SB)
// noptrdata
DATA autotab+0x60(SB)/8, $noptrdata(SB)
DATA autotab+0x68(SB)/8, $nonzeronoptr(SB)
DATA autotab+0x70(SB)/8, $enoptrdata(SB)
DATA autotab+0x78(SB)/8, $nonzeronoptr+8(SB)
// noptrbss
DATA autotab+0x80(SB)/8, $noptrbss(SB)
DATA autotab+0x88(SB)/8, $zeronoptr(SB)
DATA autotab+0x90(SB)/8, $enoptrbss(SB)
DATA autotab+0x98(SB)/8, $zeronoptr+16(SB)
// end
DATA autotab+0xa0(SB)/8, $end(SB)
DATA autotab+0xa8(SB)/8, $zeronoptr+16(SB)
GLOBL autotab(SB), $0xb0
DATA nonzero(SB)/4, $1
GLOBL nonzero(SB), $4
DATA nonzeronoptr(SB)/8, $2
GLOBL nonzeronoptr(SB), NOPTR, $8
GLOBL autoptr(SB), $0
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Test of 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
// 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 "../../ld/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
...@@ -9,6 +9,6 @@ package main ...@@ -9,6 +9,6 @@ package main
import "io" import "io"
func (p *Prog) write(w io.Writer) { func (p *Prog) write(w io.Writer) {
p.Entry = p.Syms[startSymID].Addr p.Entry = p.Syms[p.startSym].Addr
p.formatter.write(w, p) p.formatter.write(w, p)
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment