Compare commits

...

16 Commits

Author SHA1 Message Date
Andrew Gerrand
3d34461177 go1.4rc2
LGTM=rsc
R=rsc
CC=golang-codereviews
https://golang.org/cl/179700043
2014-12-02 13:43:43 +11:00
Russ Cox
28208eb8e3 [release-branch.go1.4] runtime: fix hang in GC due to shrinkstack vs netpoll race
««« CL 179680043 / 752cd9199639
runtime: fix hang in GC due to shrinkstack vs netpoll race

During garbage collection, after scanning a stack, we think about
shrinking it to reclaim some memory. The shrinking code (called
while the world is stopped) checked that the status was Gwaiting
or Grunnable and then changed the state to Gcopystack, to essentially
lock the stack so that no other GC thread is scanning it.
The same locking happens for stack growth (and is more necessary there).

        oldstatus = runtime·readgstatus(gp);
        oldstatus &= ~Gscan;
        if(oldstatus == Gwaiting || oldstatus == Grunnable)
                runtime·casgstatus(gp, oldstatus, Gcopystack); // oldstatus is Gwaiting or Grunnable
        else
                runtime·throw("copystack: bad status, not Gwaiting or Grunnable");

Unfortunately, "stop the world" doesn't stop everything. It stops all
normal goroutine execution, but the network polling thread is still
blocked in epoll and may wake up. If it does, and it chooses a goroutine
to mark runnable, and that goroutine is the one whose stack is shrinking,
then it can happen that between readgstatus and casgstatus, the status
changes from Gwaiting to Grunnable.

casgstatus assumes that if the status is not what is expected, it is a
transient change (like from Gwaiting to Gscanwaiting and back, or like
from Gwaiting to Gcopystack and back), and it loops until the status
has been restored to the expected value. In this case, the status has
changed semi-permanently from Gwaiting to Grunnable - it won't
change again until the GC is done and the world can continue, but the
GC is waiting for the status to change back. This wedges the program.

To fix, call a special variant of casgstatus that accepts either Gwaiting
or Grunnable as valid statuses.

Without the fix bug with the extra check+throw in casgstatus, the
program below dies in a few seconds (2-10) with GOMAXPROCS=8
on a 2012 Retina MacBook Pro. With the fix, it runs for minutes
and minutes.

package main

import (
        "io"
        "log"
        "net"
        "runtime"
)

func main() {
        const N = 100
        for i := 0; i < N; i++ {
                l, err := net.Listen("tcp", "127.0.0.1:0")
                if err != nil {
                        log.Fatal(err)
                }
                ch := make(chan net.Conn, 1)
                go func() {
                        var err error
                        c1, err := net.Dial("tcp", l.Addr().String())
                        if err != nil {
                                log.Fatal(err)
                        }
                        ch <- c1
                }()
                c2, err := l.Accept()
                if err != nil {
                        log.Fatal(err)
                }
                c1 := <-ch
                l.Close()
                go netguy(c1, c2)
                go netguy(c2, c1)
                c1.Write(make([]byte, 100))
        }
        for {
                runtime.GC()
        }
}

func netguy(r, w net.Conn) {
        buf := make([]byte, 100)
        for {
                bigstack(1000)
                _, err := io.ReadFull(r, buf)
                if err != nil {
                        log.Fatal(err)
                }
                w.Write(buf)
        }
}

var g int

func bigstack(n int) {
        var buf [100]byte
        if n > 0 {
                bigstack(n - 1)
        }
        g = int(buf[0]) + int(buf[99])
}

Fixes #9186.

LGTM=rlh
R=austin, rlh
CC=dvyukov, golang-codereviews, iant, khr, r
https://golang.org/cl/179680043
»»»

TBR=rlh
CC=golang-codereviews
https://golang.org/cl/184030043
2014-12-01 16:42:41 -05:00
Russ Cox
95e92ac420 [release-branch.go1.4] reflect: Fix reflect.funcLayout. The GC bitmap has two bits per
««« CL 182160043 / 321d04dea9d6
reflect: Fix reflect.funcLayout.  The GC bitmap has two bits per
pointer, not one.

Fixes #9179

LGTM=iant, rsc
R=golang-codereviews, iant, rsc
CC=golang-codereviews
https://golang.org/cl/182160043
»»»

TBR=khr
CC=golang-codereviews
https://golang.org/cl/180440044
2014-12-01 11:18:47 -05:00
Andrew Gerrand
783ad67982 [release-branch.go1.4] doc: tidy up "Projects" page; add Go 1.4
««« CL 182750043 / ffe33f1f1f17
doc: tidy up "Projects" page; add Go 1.4

LGTM=r
R=r
CC=golang-codereviews
https://golang.org/cl/182750043
»»»

TBR=r
CC=golang-codereviews
https://golang.org/cl/176350043
2014-11-26 07:57:03 +11:00
Russ Cox
d3ae115c41 [release-branch.go1.4] go/build: build $GOOS_test.go always
««« CL 176290043 / 8025b7d1e6c9
go/build: build $GOOS_test.go always

We decided to build $GOOS.go always
but forgot to test $GOOS_test.go.

Fixes #9159.

LGTM=r
R=r
CC=golang-codereviews
https://golang.org/cl/176290043
»»»

LGTM=r
R=r
CC=golang-codereviews
https://golang.org/cl/182740043
2014-11-24 22:00:01 -05:00
Russ Cox
738ccf32d9 [release-branch.go1.4] image/jpeg: handle Read returning n > 0, err != nil in d.fill
««« CL 178120043 / 95f5614b4648
image/jpeg: handle Read returning n > 0, err != nil in d.fill

Fixes #9127.

LGTM=r
R=bradfitz, r
CC=golang-codereviews, nigeltao
https://golang.org/cl/178120043
»»»

TBR=r
CC=golang-codereviews
https://golang.org/cl/181870043
2014-11-23 11:15:26 -05:00
Russ Cox
f6818121ed [release-branch.go1.4] cmd/go: fix running pprof on windows.
««« CL 176170043 / 61bbf19823d5
cmd/go: fix running pprof on windows.

Fixes #9149.

LGTM=alex.brainman, rsc
R=rsc, dave, alex.brainman
CC=golang-codereviews
https://golang.org/cl/176170043

»»»

TBR=minux
CC=golang-codereviews
https://golang.org/cl/175550043
2014-11-22 13:38:29 -05:00
Russ Cox
791fec05e4 [release-branch.go1.4] runtime: fix atomic operations on non-heap addresses
««« CL 179030043 / e4ab8f908aac
runtime: fix atomic operations on non-heap addresses
Race detector runtime does not tolerate operations on addresses
that was not previously declared with __tsan_map_shadow
(namely, data, bss and heap). The corresponding address
checks for atomic operations were removed in
https://golang.org/cl/111310044
Restore these checks.
It's tricker than just not calling into race runtime,
because it is the race runtime that makes the atomic
operations themselves (if we do not call into race runtime
we skip the atomic operation itself as well). So instead we call
__tsan_go_ignore_sync_start/end around the atomic operation.
This forces race runtime to skip all other processing
except than doing the atomic operation itself.
Fixes #9136.

LGTM=rsc
R=rsc
CC=golang-codereviews
https://golang.org/cl/179030043

»»»

TBR=dvyukov
CC=golang-codereviews
https://golang.org/cl/180030043
2014-11-20 10:14:49 -05:00
Russ Cox
a791780bfd [release-branch.go1.4] build: disable race external linking test on OS X 10.6 and earlier
««« CL 176070043 / 500cb52e08e6
build: disable race external linking test on OS X 10.6 and earlier

External linking doesn't work there at all.

LGTM=bradfitz
R=adg, bradfitz
CC=golang-codereviews
https://golang.org/cl/176070043
»»»

LGTM=bradfitz, adg
R=adg, bradfitz
CC=golang-codereviews
https://golang.org/cl/175400043
2014-11-19 21:25:07 -05:00
Russ Cox
427ee80413 [release-branch.go1.4] runtime: remove assumption that noptrdata data bss noptrbss are ordered and contiguous
««« CL 179980043 / d71cc7e8a0e0
runtime: remove assumption that noptrdata data bss noptrbss are ordered and contiguous

The assumption can be violated by external linkers reordering them or
inserting non-Go sections in between them. I looked briefly at trying
to write out the _go_.o in external linking mode in a way that forced
the ordering, but no matter what there's no way to force Go's data
and Go's bss to be next to each other. If there is any data or bss from
non-Go objects, it's very likely to get stuck in between them.

Instead, rewrite the two places we know about that make the assumption.
I grepped for noptrdata to look for more and didn't find any.

The added race test (os/exec in external linking mode) fails without
the changes in the runtime. It crashes with an invalid pointer dereference.

Fixes #9133.

LGTM=dneil
R=dneil
CC=dvyukov, golang-codereviews, iant
https://golang.org/cl/179980043
»»»

LGTM=dneil
R=dneil
CC=golang-codereviews
https://golang.org/cl/173510043
2014-11-19 15:31:31 -05:00
Russ Cox
b4df0154c2 [release-branch.go1.4] undo CL 131750044 / 2d6d44ceb80e
««« CL 174450043 / 699cc091a16d
undo CL 131750044 / 2d6d44ceb80e

Breaks reading from stdin in parent after exec with SysProcAttr{Setpgid: true}.

package main

import (
        "fmt"
        "os"
        "os/exec"
        "syscall"
)

func main() {
        cmd := exec.Command("true")
        cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
        cmd.Run()

        fmt.Printf("Hit enter:")
        os.Stdin.Read(make([]byte, 100))
        fmt.Printf("Bye\n")
}

In go1.3, I type enter at the prompt and the program exits.
With the CL being rolled back, the program wedges at the
prompt.

««« original CL description
syscall: SysProcAttr job control changes

Making the child's process group the foreground process group and
placing the child in a specific process group involves co-ordination
between the parent and child that must be done post-fork but pre-exec.

LGTM=iant
R=golang-codereviews, gobot, iant, mikioh.mikioh
CC=golang-codereviews
https://golang.org/cl/131750044

»»»

LGTM=minux, dneil
R=dneil, minux
CC=golang-codereviews, iant, michael.p.macinnis
https://golang.org/cl/174450043
»»»

LGTM=minux
R=dneil, minux
CC=golang-codereviews
https://golang.org/cl/179970043
2014-11-19 14:38:22 -05:00
Andrew Gerrand
c9e183e781 [release-branch.go1.4] doc/go1.4.html: rewrite first sentence to make it clearer
««« CL 178910043 / 3916b070c5f3
doc/go1.4.html: rewrite first sentence to make it clearer
The grammar was atrocious, probably the victim of an editing error.

LGTM=bradfitz
R=bradfitz
CC=golang-codereviews
https://golang.org/cl/178910043
»»»

LGTM=r
R=r
CC=golang-codereviews
https://golang.org/cl/175310043
2014-11-19 09:47:56 +11:00
Andrew Gerrand
30ef146819 [release-branch.go1.4] remove cmd/link from nacl test zip
LGTM=dsymonds
R=rsc, dsymonds
CC=golang-codereviews
https://golang.org/cl/179830043
2014-11-17 13:55:59 +11:00
Andrew Gerrand
daf5d41471 [release-branch.go1.4] remove cmd/link
LGTM=dsymonds, minux
R=rsc, dsymonds, minux
CC=golang-codereviews
https://golang.org/cl/176910043
2014-11-17 13:46:45 +11:00
Andrew Gerrand
c1fc059b08 [release-branch.go1.4] debug/goobj: move to cmd/internal/goobj
««« CL 174250043 / c16349455e05
debug/goobj: move to cmd/internal/goobj

debug/goobj is not ready to be published but it is
needed for the various binary-reading commands.
Move to cmd/internal/goobj.

(The Go 1.3 release branch deleted it, but that's not
an option anymore due to the command dependencies.
The API is still not vetted nor terribly well designed.)

LGTM=adg, dsymonds
R=adg, dsymonds
CC=golang-codereviews
https://golang.org/cl/174250043
»»»

LGTM=rsc
R=rsc
CC=golang-codereviews
https://golang.org/cl/177890043
2014-11-17 12:56:35 +11:00
Andrew Gerrand
335ad3db99 go1.4rc1 2014-11-17 09:37:04 +11:00
64 changed files with 369 additions and 5412 deletions

1
VERSION Normal file
View File

@@ -0,0 +1 @@
go1.4rc2

View File

@@ -30,21 +30,16 @@ We encourage all Go users to subscribe to
<h2 id="go1">Version history</h2>
<h3 id="release"><a href="/doc/devel/release.html">Release History</a></h3>
<p>A summary of the changes between Go releases.</p>
<h4 id="go1notes"><a href="/doc/go1">Go 1 Release Notes</a></h4>
<p>
A guide for updating your code to work with Go 1.
</p>
<p>A <a href="/doc/devel/release.html">summary</a> of the changes between Go releases. Notes for the major releases:</p>
<h4 id="release notes"><a href="/doc/go1.1">Go 1.1 Release Notes</a></h4>
<p>
A list of significant changes in Go 1.1, with instructions for updating
your code where necessary.
Each point release includes a similar document appropriate for that
release: <a href="/doc/go1.2">Go 1.2</a>, <a href="/doc/go1.3">Go 1.3</a>,
and so on.
</p>
<ul>
<li><a href="/doc/go1.4">Go 1.4</a> <small>(December 2014)</small></li>
<li><a href="/doc/go1.3">Go 1.3</a> <small>(June 2014)</small></li>
<li><a href="/doc/go1.2">Go 1.2</a> <small>(December 2013)</small></li>
<li><a href="/doc/go1.1">Go 1.1</a> <small>(May 2013)</small></li>
<li><a href="/doc/go1">Go 1</a> <small>(March 2012)</small></li>
</ul>
<h3 id="go1compat"><a href="/doc/go1compat">Go 1 and the Future of Go Programs</a></h3>
<p>

View File

@@ -7,10 +7,10 @@
<h2 id="introduction">Introduction to Go 1.4</h2>
<p>
The latest Go release, version 1.4, arrives as scheduled six months after 1.3
and contains only one tiny language change,
a possibly breaking change to the compiler,
a backwards-compatible simple form of <code>for</code>-<code>range</code> loop.
The latest Go release, version 1.4, arrives as scheduled six months after 1.3.
It contains only one tiny language change,
in the form of a backwards-compatible simple variant of <code>for</code>-<code>range</code> loop,
and a possibly breaking change to the compiler involving methods on pointers-to-pointers.
The release focuses primarily on implementation work, improving the garbage collector
and preparing the ground for a fully concurrent collector to be rolled out in the
next few releases.
@@ -20,7 +20,7 @@ this release therefore eliminates the notorious "hot stack split" problem.
There are some new tools available including support in the <code>go</code> command
for build-time source code generation.
The release also adds support for ARM processors on Android and Native Client (NaCl)
and AMD64 on Plan 9.
and for AMD64 on Plan 9.
As always, Go 1.4 keeps the <a href="/doc/go1compat.html">promise
of compatibility</a>,
and almost everything

View File

@@ -18,9 +18,6 @@ go src=..
gofmt_test.go
testdata
+
link
testdata
+
archive
tar
testdata

View File

@@ -47,7 +47,7 @@ const toolWindowsExtension = ".exe"
func tool(toolName string) string {
toolPath := filepath.Join(toolDir, toolName)
if toolIsWindows && toolName != "pprof" {
if toolIsWindows {
toolPath += toolWindowsExtension
}
// Give a nice message if there is no tool with that name.
@@ -91,16 +91,6 @@ func runTool(cmd *Command, args []string) {
if toolPath == "" {
return
}
if toolIsWindows && toolName == "pprof" {
args = append([]string{"perl", toolPath}, args[1:]...)
var err error
toolPath, err = exec.LookPath("perl")
if err != nil {
fmt.Fprintf(os.Stderr, "go tool: perl not found\n")
setExitStatus(3)
return
}
}
if toolN {
fmt.Printf("%s %s\n", toolPath, strings.Join(args[1:], " "))
return

View File

@@ -7,7 +7,7 @@
package objfile
import (
"debug/goobj"
"cmd/internal/goobj"
"fmt"
"os"
)

View File

@@ -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 (
"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{
"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,
})
}

View File

@@ -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"
"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)
}
}
}
}

View File

@@ -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 "debug/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
}

View File

@@ -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 (
"debug/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)
}
}

View File

@@ -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() {
}

View File

@@ -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
}

View File

@@ -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 (
"debug/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)
}

View File

@@ -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])
}
}
}

View File

@@ -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"
"debug/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")
}

View File

@@ -1,116 +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 "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)
}
}
// TODO(rsc): Define full enumeration for relocation types.
const (
R_ADDR = 1
R_SIZE = 2
R_CALL = 3
R_CALLARM = 4
R_CALLIND = 5
R_CONST = 6
R_PCREL = 7
)
// 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 R_ADDR, R_CALLIND:
// ok
case R_PCREL, 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))
}
}
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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() {}

View File

@@ -1,479 +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 (
"debug/goobj"
"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 http://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: 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
}

View File

@@ -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"
"debug/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])
}

View File

@@ -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 (
"debug/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,
},
}

View File

@@ -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])
}
}
}

View File

@@ -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 "debug/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,
},
})
}

View File

@@ -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 (
"debug/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 debug/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")
}

View File

@@ -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 6a -trimpath=$(shell pwd) $*.s
pclntab.s: genpcln.go
go run genpcln.go >pclntab.s

Binary file not shown.

View File

@@ -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

Binary file not shown.

View File

@@ -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

Binary file not shown.

View File

@@ -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

View File

@@ -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")
}

Binary file not shown.

View File

@@ -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

Binary file not shown.

View File

@@ -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

View File

@@ -1,54 +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 b0 10 00 00 00 00 00 00 |................|
00000090 00 00 00 00 00 00 00 00 b0 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 20 00 00 00 00 00 00 00 |. ...... .......|
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 20 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ..............|
00000130 20 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ...............|
*
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 20 20 00 00 00 00 00 00 90 00 00 00 00 00 00 00 | ..............|
00000180 20 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ...............|
*
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 be 00 30 00 00 ba 0c 00 00 00 b8 |.......0........|
00001010 04 00 00 02 0f 05 31 ff b8 01 00 00 02 0f 05 c3 |......1.........|
00001020 fb ff ff ff 00 00 01 08 01 00 00 00 00 00 00 00 |................|
00001030 00 20 00 00 00 00 00 00 30 00 00 00 00 00 00 00 |. ......0.......|
00001040 20 20 00 00 00 00 00 00 80 00 00 00 00 00 00 00 | ..............|
00001050 00 20 00 00 00 00 00 00 58 00 00 00 00 00 00 80 |. ......X.......|
00001060 08 00 00 00 60 00 00 00 63 00 00 00 66 00 00 00 |....`...c...f...|
00001070 00 00 00 00 00 00 00 00 5f 72 74 30 5f 67 6f 00 |........_rt0_go.|
00001080 02 20 00 04 20 00 06 05 02 05 02 05 02 05 02 02 |. .. ...........|
00001090 02 02 02 05 02 02 02 01 00 00 00 00 00 00 00 00 |................|
000010a0 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -1310,11 +1310,13 @@ func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool {
// auto-tagging to apply only to files with a non-empty prefix, so
// "foo_linux.go" is tagged but "linux.go" is not. This allows new operating
// sytems, such as android, to arrive without breaking existing code with
// innocuous source code in "android.go". The easiest fix: files without
// underscores are always included.
if !strings.ContainsRune(name, '_') {
// innocuous source code in "android.go". The easiest fix: cut everything
// in the name before the initial _.
i := strings.Index(name, "_")
if i < 0 {
return true
}
name = name[i:] // ignore everything before first _
l := strings.Split(name, "_")
if n := len(l); n > 0 && l[n-1] == "test" {

View File

@@ -189,6 +189,7 @@ var matchFileTests = []struct {
{ctxtAndroid, "foo_plan9.go", "", false},
{ctxtAndroid, "android.go", "", true},
{ctxtAndroid, "plan9.go", "", true},
{ctxtAndroid, "plan9_test.go", "", true},
{ctxtAndroid, "arm.s", "", true},
{ctxtAndroid, "amd64.s", "", true},
}

View File

@@ -143,6 +143,9 @@ func (d *decoder) fill() error {
// Fill in the rest of the buffer.
n, err := d.r.Read(d.bytes.buf[d.bytes.j:])
d.bytes.j += n
if n > 0 {
err = nil
}
return err
}

View File

@@ -9,6 +9,7 @@ import (
"fmt"
"image"
"image/color"
"io"
"io/ioutil"
"math/rand"
"os"
@@ -88,6 +89,51 @@ func decodeFile(filename string) (image.Image, error) {
return Decode(f)
}
type eofReader struct {
data []byte // deliver from Read without EOF
dataEOF []byte // then deliver from Read with EOF on last chunk
lenAtEOF int
}
func (r *eofReader) Read(b []byte) (n int, err error) {
if len(r.data) > 0 {
n = copy(b, r.data)
r.data = r.data[n:]
} else {
n = copy(b, r.dataEOF)
r.dataEOF = r.dataEOF[n:]
if len(r.dataEOF) == 0 {
err = io.EOF
if r.lenAtEOF == -1 {
r.lenAtEOF = n
}
}
}
return
}
func TestDecodeEOF(t *testing.T) {
// Check that if reader returns final data and EOF at same time, jpeg handles it.
data, err := ioutil.ReadFile("../testdata/video-001.jpeg")
if err != nil {
t.Fatal(err)
}
n := len(data)
for i := 0; i < n; {
r := &eofReader{data[:n-i], data[n-i:], -1}
_, err := Decode(r)
if err != nil {
t.Errorf("Decode with Read() = %d, EOF: %v", r.lenAtEOF, err)
}
if i == 0 {
i = 1
} else {
i *= 2
}
}
}
// check checks that the two pix data are equal, within the given bounds.
func check(bounds image.Rectangle, pix0, pix1 []byte, stride0, stride1 int) error {
if stride0 <= 0 || stride0%8 != 0 {

View File

@@ -4055,3 +4055,104 @@ func TestLargeGCProg(t *testing.T) {
fv := ValueOf(func([256]*byte) {})
fv.Call([]Value{ValueOf([256]*byte{})})
}
// Issue 9179.
func TestCallGC(t *testing.T) {
f := func(a, b, c, d, e string) {
}
g := func(in []Value) []Value {
runtime.GC()
return nil
}
typ := ValueOf(f).Type()
f2 := MakeFunc(typ, g).Interface().(func(string, string, string, string, string))
f2("four", "five5", "six666", "seven77", "eight888")
}
type funcLayoutTest struct {
rcvr, t Type
argsize, retOffset uintptr
stack []byte
}
var funcLayoutTests []funcLayoutTest
func init() {
var argAlign = PtrSize
if runtime.GOARCH == "amd64p32" {
argAlign = 2 * PtrSize
}
roundup := func(x uintptr, a uintptr) uintptr {
return (x + a - 1) / a * a
}
funcLayoutTests = append(funcLayoutTests,
funcLayoutTest{
nil,
ValueOf(func(a, b string) string { return "" }).Type(),
4 * PtrSize,
4 * PtrSize,
[]byte{BitsPointer, BitsScalar, BitsPointer},
})
var r []byte
if PtrSize == 4 {
r = []byte{BitsScalar, BitsScalar, BitsScalar, BitsPointer}
} else {
r = []byte{BitsScalar, BitsScalar, BitsPointer}
}
funcLayoutTests = append(funcLayoutTests,
funcLayoutTest{
nil,
ValueOf(func(a, b, c uint32, p *byte, d uint16) {}).Type(),
roundup(3*4, PtrSize) + PtrSize + 2,
roundup(roundup(3*4, PtrSize)+PtrSize+2, argAlign),
r,
})
funcLayoutTests = append(funcLayoutTests,
funcLayoutTest{
nil,
ValueOf(func(a map[int]int, b uintptr, c interface{}) {}).Type(),
4 * PtrSize,
4 * PtrSize,
[]byte{BitsPointer, BitsScalar, BitsPointer, BitsPointer},
})
type S struct {
a, b uintptr
c, d *byte
}
funcLayoutTests = append(funcLayoutTests,
funcLayoutTest{
nil,
ValueOf(func(a S) {}).Type(),
4 * PtrSize,
4 * PtrSize,
[]byte{BitsScalar, BitsScalar, BitsPointer, BitsPointer},
})
funcLayoutTests = append(funcLayoutTests,
funcLayoutTest{
ValueOf((*byte)(nil)).Type(),
ValueOf(func(a uintptr, b *int) {}).Type(),
3 * PtrSize,
roundup(3*PtrSize, argAlign),
[]byte{BitsPointer, BitsScalar, BitsPointer},
})
}
func TestFuncLayout(t *testing.T) {
for _, lt := range funcLayoutTests {
_, argsize, retOffset, stack := FuncLayout(lt.t, lt.rcvr)
if argsize != lt.argsize {
t.Errorf("funcLayout(%v, %v).argsize=%d, want %d", lt.t, lt.rcvr, argsize, lt.argsize)
}
if retOffset != lt.retOffset {
t.Errorf("funcLayout(%v, %v).retOffset=%d, want %d", lt.t, lt.rcvr, retOffset, lt.retOffset)
}
if !bytes.Equal(stack, lt.stack) {
t.Errorf("funcLayout(%v, %v).stack=%v, want %v", lt.t, lt.rcvr, stack, lt.stack)
}
}
}

View File

@@ -17,3 +17,22 @@ func IsRO(v Value) bool {
var ArrayOf = arrayOf
var CallGC = &callGC
const PtrSize = ptrSize
const BitsPointer = bitsPointer
const BitsScalar = bitsScalar
func FuncLayout(t Type, rcvr Type) (frametype Type, argSize, retOffset uintptr, stack []byte) {
var ft *rtype
var s *bitVector
if rcvr != nil {
ft, argSize, retOffset, s = funcLayout(t.(*rtype), rcvr.(*rtype))
} else {
ft, argSize, retOffset, s = funcLayout(t.(*rtype), nil)
}
frametype = ft
for i := uint32(0); i < s.n; i += 2 {
stack = append(stack, s.data[i/8]>>(i%8)&3)
}
return
}

View File

@@ -1894,14 +1894,14 @@ func addTypeBits(bv *bitVector, offset *uintptr, t *rtype) {
switch Kind(t.kind & kindMask) {
case Chan, Func, Map, Ptr, Slice, String, UnsafePointer:
// 1 pointer at start of representation
for bv.n < uint32(*offset/uintptr(ptrSize)) {
for bv.n < 2*uint32(*offset/uintptr(ptrSize)) {
bv.append2(bitsScalar)
}
bv.append2(bitsPointer)
case Interface:
// 2 pointers
for bv.n < uint32(*offset/uintptr(ptrSize)) {
for bv.n < 2*uint32(*offset/uintptr(ptrSize)) {
bv.append2(bitsScalar)
}
bv.append2(bitsPointer)

View File

@@ -64,17 +64,6 @@ echo
echo '# sync -cpu=10'
go test sync -short -timeout=$(expr 120 \* $timeout_scale)s -cpu=10
# Race detector only supported on Linux, FreeBSD and OS X,
# and only on amd64, and only when cgo is enabled.
case "$GOHOSTOS-$GOOS-$GOARCH-$CGO_ENABLED" in
linux-linux-amd64-1 | freebsd-freebsd-amd64-1 | darwin-darwin-amd64-1)
echo
echo '# Testing race detector.'
go test -race -i runtime/race flag
go test -race -run=Output runtime/race
go test -race -short flag
esac
xcd() {
echo
echo '#' $1
@@ -120,6 +109,7 @@ go run $GOROOT/test/run.go - . || exit 1
[ "$CGO_ENABLED" != 1 ] ||
(xcd ../misc/cgo/test
# cgo tests inspect the traceback for runtime functions
extlink=0
export GOTRACEBACK=2
go test -ldflags '-linkmode=auto' || exit 1
# linkmode=internal fails on dragonfly since errno is a TLS relocation.
@@ -128,19 +118,24 @@ case "$GOHOSTOS-$GOARCH" in
openbsd-386 | openbsd-amd64)
# test linkmode=external, but __thread not supported, so skip testtls.
go test -ldflags '-linkmode=external' || exit 1
extlink=1
;;
darwin-386 | darwin-amd64)
# linkmode=external fails on OS X 10.6 and earlier == Darwin
# 10.8 and earlier.
case $(uname -r) in
[0-9].* | 10.*) ;;
*) go test -ldflags '-linkmode=external' || exit 1;;
*)
go test -ldflags '-linkmode=external' || exit 1
extlink=1
;;
esac
;;
android-arm | dragonfly-386 | dragonfly-amd64 | freebsd-386 | freebsd-amd64 | freebsd-arm | linux-386 | linux-amd64 | linux-arm | netbsd-386 | netbsd-amd64)
go test -ldflags '-linkmode=external' || exit 1
go test -ldflags '-linkmode=auto' ../testtls || exit 1
go test -ldflags '-linkmode=external' ../testtls || exit 1
extlink=1
case "$GOHOSTOS-$GOARCH" in
netbsd-386 | netbsd-amd64) ;; # no static linking
@@ -164,6 +159,23 @@ android-arm | dragonfly-386 | dragonfly-amd64 | freebsd-386 | freebsd-amd64 | fr
esac
) || exit $?
# Race detector only supported on Linux, FreeBSD and OS X,
# and only on amd64, and only when cgo is enabled.
# Delayed until here so we know whether to try external linking.
case "$GOHOSTOS-$GOOS-$GOARCH-$CGO_ENABLED" in
linux-linux-amd64-1 | freebsd-freebsd-amd64-1 | darwin-darwin-amd64-1)
echo
echo '# Testing race detector.'
go test -race -i runtime/race flag os/exec
go test -race -run=Output runtime/race
go test -race -short flag os/exec
# Test with external linking; see issue 9133.
if [ "$extlink" = 1 ]; then
go test -race -short -ldflags=-linkmode=external flag os/exec
fi
esac
# This tests cgo -cdefs. That mode is not supported,
# so it's okay if it doesn't work on some systems.
# In particular, it works badly with clang on OS X.

View File

@@ -490,6 +490,8 @@ func GC() {
// linker-provided
var noptrdata struct{}
var enoptrdata struct{}
var noptrbss struct{}
var enoptrbss struct{}
// SetFinalizer sets the finalizer associated with x to f.
@@ -566,8 +568,13 @@ func SetFinalizer(obj interface{}, finalizer interface{}) {
// func main() {
// runtime.SetFinalizer(Foo, nil)
// }
// The segments are, in order: text, rodata, noptrdata, data, bss, noptrbss.
if uintptr(unsafe.Pointer(&noptrdata)) <= uintptr(e.data) && uintptr(e.data) < uintptr(unsafe.Pointer(&enoptrbss)) {
// The relevant segments are: noptrdata, data, bss, noptrbss.
// We cannot assume they are in any order or even contiguous,
// due to external linking.
if uintptr(unsafe.Pointer(&noptrdata)) <= uintptr(e.data) && uintptr(e.data) < uintptr(unsafe.Pointer(&enoptrdata)) ||
uintptr(unsafe.Pointer(&data)) <= uintptr(e.data) && uintptr(e.data) < uintptr(unsafe.Pointer(&edata)) ||
uintptr(unsafe.Pointer(&bss)) <= uintptr(e.data) && uintptr(e.data) < uintptr(unsafe.Pointer(&ebss)) ||
uintptr(unsafe.Pointer(&noptrbss)) <= uintptr(e.data) && uintptr(e.data) < uintptr(unsafe.Pointer(&enoptrbss)) {
return
}
gothrow("runtime.SetFinalizer: pointer not in allocated block")

View File

@@ -402,6 +402,7 @@ runtime·castogscanstatus(G *gp, uint32 oldval, uint32 newval)
static void badcasgstatus(void);
static void helpcasgstatus(void);
static void badgstatusrunnable(void);
// If asked to move to or from a Gscanstatus this will throw. Use the castogscanstatus
// and casfromgscanstatus instead.
@@ -423,6 +424,10 @@ runtime·casgstatus(G *gp, uint32 oldval, uint32 newval)
// loop if gp->atomicstatus is in a scan state giving
// GC time to finish and change the state to oldval.
while(!runtime·cas(&gp->atomicstatus, oldval, newval)) {
if(oldval == Gwaiting && gp->atomicstatus == Grunnable) {
fn = badgstatusrunnable;
runtime·onM(&fn);
}
// Help GC if needed.
if(gp->preemptscan && !gp->gcworkdone && (oldval == Grunning || oldval == Gsyscall)) {
gp->preemptscan = false;
@@ -433,6 +438,33 @@ runtime·casgstatus(G *gp, uint32 oldval, uint32 newval)
}
}
static void
badgstatusrunnable(void)
{
runtime·throw("casgstatus: waiting for Gwaiting but is Grunnable");
}
// casgstatus(gp, oldstatus, Gcopystack), assuming oldstatus is Gwaiting or Grunnable.
// Returns old status. Cannot call casgstatus directly, because we are racing with an
// async wakeup that might come in from netpoll. If we see Gwaiting from the readgstatus,
// it might have become Grunnable by the time we get to the cas. If we called casgstatus,
// it would loop waiting for the status to go back to Gwaiting, which it never will.
#pragma textflag NOSPLIT
uint32
runtime·casgcopystack(G *gp)
{
uint32 oldstatus;
for(;;) {
oldstatus = runtime·readgstatus(gp) & ~Gscan;
if(oldstatus != Gwaiting && oldstatus != Grunnable)
runtime·throw("copystack: bad status, not Gwaiting or Grunnable");
if(runtime·cas(&gp->atomicstatus, oldstatus, Gcopystack))
break;
}
return oldstatus;
}
static void
badcasgstatus(void)
{

View File

@@ -63,8 +63,17 @@ void __tsan_go_ignore_sync_end(void);
#pragma cgo_import_static __tsan_go_atomic64_compare_exchange
extern byte runtime·noptrdata[];
extern byte runtime·enoptrdata[];
extern byte runtime·data[];
extern byte runtime·edata[];
extern byte runtime·bss[];
extern byte runtime·ebss[];
extern byte runtime·noptrbss[];
extern byte runtime·enoptrbss[];
// start/end of global data (data+bss).
uintptr runtime·racedatastart;
uintptr runtime·racedataend;
// start/end of heap for race_amd64.s
uintptr runtime·racearenastart;
uintptr runtime·racearenaend;
@@ -86,7 +95,7 @@ isvalidaddr(uintptr addr)
{
if(addr >= runtime·racearenastart && addr < runtime·racearenaend)
return true;
if(addr >= (uintptr)runtime·noptrdata && addr < (uintptr)runtime·enoptrbss)
if(addr >= runtime·racedatastart && addr < runtime·racedataend)
return true;
return false;
}
@@ -95,16 +104,40 @@ isvalidaddr(uintptr addr)
uintptr
runtime·raceinit(void)
{
uintptr racectx, start, size;
uintptr racectx, start, end, size;
// cgo is required to initialize libc, which is used by race runtime
if(!runtime·iscgo)
runtime·throw("raceinit: race build must use cgo");
runtime·racecall(__tsan_init, &racectx, runtime·racesymbolizethunk);
// Round data segment to page boundaries, because it's used in mmap().
start = (uintptr)runtime·noptrdata & ~(PageSize-1);
size = ROUND((uintptr)runtime·enoptrbss - start, PageSize);
// The relevant sections are noptrdata, data, bss, noptrbss.
// In external linking mode, there may be other non-Go data mixed in,
// and the sections may even occur out of order.
// Work out a conservative range of addresses.
start = ~(uintptr)0;
end = 0;
if(start > (uintptr)runtime·noptrdata)
start = (uintptr)runtime·noptrdata;
if(start > (uintptr)runtime·data)
start = (uintptr)runtime·data;
if(start > (uintptr)runtime·noptrbss)
start = (uintptr)runtime·noptrbss;
if(start > (uintptr)runtime·bss)
start = (uintptr)runtime·bss;
if(end < (uintptr)runtime·enoptrdata)
end = (uintptr)runtime·enoptrdata;
if(end < (uintptr)runtime·edata)
end = (uintptr)runtime·edata;
if(end < (uintptr)runtime·enoptrbss)
end = (uintptr)runtime·enoptrbss;
if(end < (uintptr)runtime·ebss)
end = (uintptr)runtime·ebss;
start = start & ~(PageSize-1);
size = ROUND(end - start, PageSize);
runtime·racecall(__tsan_map_shadow, start, size);
runtime·racedatastart = start;
runtime·racedataend = start + size;
return racectx;
}

View File

@@ -0,0 +1,30 @@
// 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.
// +build race
// +build darwin freebsd linux
package race_test
import (
"sync/atomic"
"syscall"
"testing"
"unsafe"
)
// Test that race detector does not crash when accessing non-Go allocated memory (issue 9136).
func TestNonGoMemory(t *testing.T) {
data, err := syscall.Mmap(-1, 0, 4096, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_ANON|syscall.MAP_PRIVATE)
if err != nil {
t.Fatalf("failed to mmap memory: %v", err)
}
p := (*uint32)(unsafe.Pointer(&data[0]))
atomic.AddUint32(p, 1)
(*p)++
if *p != 2 {
t.Fatalf("data[0] = %v, expect 2", *p)
}
syscall.Munmap(data)
}

View File

@@ -138,17 +138,15 @@ TEXT racecalladdr<>(SB), NOSPLIT, $0-0
get_tls(R12)
MOVQ g(R12), R14
MOVQ g_racectx(R14), RARG0 // goroutine context
// Check that addr is within [arenastart, arenaend) or within [noptrdata, enoptrbss).
// Check that addr is within [arenastart, arenaend) or within [racedatastart, racedataend).
CMPQ RARG1, runtime·racearenastart(SB)
JB racecalladdr_data
CMPQ RARG1, runtime·racearenaend(SB)
JB racecalladdr_call
racecalladdr_data:
MOVQ $runtime·noptrdata(SB), R13
CMPQ RARG1, R13
CMPQ RARG1, runtime·racedatastart(SB)
JB racecalladdr_ret
MOVQ $runtime·enoptrbss(SB), R13
CMPQ RARG1, R13
CMPQ RARG1, runtime·racedataend(SB)
JAE racecalladdr_ret
racecalladdr_call:
MOVQ AX, AX // w/o this 6a miscompiles this function
@@ -166,6 +164,7 @@ TEXT runtime·racefuncenter(SB), NOSPLIT, $0-8
MOVQ callpc+0(FP), RARG1
// void __tsan_func_enter(ThreadState *thr, void *pc);
MOVQ $__tsan_func_enter(SB), AX
// racecall<> preserves R15
CALL racecall<>(SB)
MOVQ R15, DX // restore function entry context
RET
@@ -306,13 +305,45 @@ TEXT syncatomic·CompareAndSwapPointer(SB), NOSPLIT, $0-0
TEXT racecallatomic<>(SB), NOSPLIT, $0-0
// Trigger SIGSEGV early.
MOVQ 16(SP), R12
MOVL (R12), R12
MOVL (R12), R13
// Check that addr is within [arenastart, arenaend) or within [racedatastart, racedataend).
CMPQ R12, runtime·racearenastart(SB)
JB racecallatomic_data
CMPQ R12, runtime·racearenaend(SB)
JB racecallatomic_ok
racecallatomic_data:
CMPQ R12, runtime·racedatastart(SB)
JB racecallatomic_ignore
CMPQ R12, runtime·racedataend(SB)
JAE racecallatomic_ignore
racecallatomic_ok:
// Addr is within the good range, call the atomic function.
get_tls(R12)
MOVQ g(R12), R14
MOVQ g_racectx(R14), RARG0 // goroutine context
MOVQ 8(SP), RARG1 // caller pc
MOVQ (SP), RARG2 // pc
LEAQ 16(SP), RARG3 // arguments
JMP racecall<>(SB) // does not return
racecallatomic_ignore:
// Addr is outside the good range.
// Call __tsan_go_ignore_sync_begin to ignore synchronization during the atomic op.
// An attempt to synchronize on the address would cause crash.
MOVQ AX, R15 // remember the original function
MOVQ $__tsan_go_ignore_sync_begin(SB), AX
MOVQ g(R12), R14
MOVQ g_racectx(R14), RARG0 // goroutine context
CALL racecall<>(SB)
MOVQ R15, AX // restore the original function
// Call the atomic function.
MOVQ g_racectx(R14), RARG0 // goroutine context
MOVQ 8(SP), RARG1 // caller pc
MOVQ (SP), RARG2 // pc
LEAQ 16(SP), RARG3 // arguments
CALL racecall<>(SB)
// Call __tsan_go_ignore_sync_end.
MOVQ $__tsan_go_ignore_sync_end(SB), AX
MOVQ g_racectx(R14), RARG0 // goroutine context
JMP racecall<>(SB)
// void runtime·racecall(void(*f)(...), ...)

View File

@@ -666,6 +666,8 @@ enum {
uint32 runtime·readgstatus(G*);
void runtime·casgstatus(G*, uint32, uint32);
void runtime·casgstatus(G*, uint32, uint32);
uint32 runtime·casgcopystack(G*);
void runtime·quiesce(G*);
bool runtime·stopg(G*);
void runtime·restartg(G*);

View File

@@ -637,12 +637,7 @@ copystack(G *gp, uintptr newsize)
}
runtime·memmove((byte*)new.hi - used, (byte*)old.hi - used, used);
oldstatus = runtime·readgstatus(gp);
oldstatus &= ~Gscan;
if(oldstatus == Gwaiting || oldstatus == Grunnable)
runtime·casgstatus(gp, oldstatus, Gcopystack); // oldstatus is Gwaiting or Grunnable
else
runtime·throw("copystack: bad status, not Gwaiting or Grunnable");
oldstatus = runtime·casgcopystack(gp); // cas from Gwaiting or Grunnable to Gcopystack, return old status
// Swap out old stack for new one
gp->stack = new;

View File

@@ -19,8 +19,6 @@ type SysProcAttr struct {
Setpgid bool // Set process group ID to new pid (SYSV setpgrp)
Setctty bool // Set controlling terminal to fd 0
Noctty bool // Detach fd 0 from controlling terminal
Foreground bool // Set foreground process group to child's pid. (Implies Setpgid. Stdin should be a TTY)
Joinpgrp int // If != 0, child's process group ID. (Setpgid must not be set)
}
// Implemented in runtime package.
@@ -81,22 +79,7 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
if r1 != 0 {
// parent; return PID
runtime_AfterFork()
pid = int(r1)
if sys.Joinpgrp != 0 {
// Place the child in the specified process group.
RawSyscall(SYS_SETPGID, r1, uintptr(sys.Joinpgrp), 0)
} else if sys.Foreground || sys.Setpgid {
// Place the child in a new process group.
RawSyscall(SYS_SETPGID, 0, 0, 0)
if sys.Foreground {
// Set new foreground process group.
RawSyscall(SYS_IOCTL, uintptr(Stdin), TIOCSPGRP, uintptr(unsafe.Pointer(&pid)))
}
}
return pid, 0
return int(r1), 0
}
// Fork succeeded, now in child.
@@ -118,30 +101,11 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
}
// Set process group
if sys.Joinpgrp != 0 {
// Place the child in the specified process group.
_, _, err1 = RawSyscall(SYS_SETPGID, r1, uintptr(sys.Joinpgrp), 0)
if err1 != 0 {
goto childerror
}
} else if sys.Foreground || sys.Setpgid {
// Place the child in a new process group.
if sys.Setpgid {
_, _, err1 = RawSyscall(SYS_SETPGID, 0, 0, 0)
if err1 != 0 {
goto childerror
}
if sys.Foreground {
r1, _, _ = RawSyscall(SYS_GETPID, 0, 0, 0)
pid := int(r1)
// Set new foreground process group.
_, _, err1 = RawSyscall(SYS_IOCTL, uintptr(Stdin), TIOCSPGRP, uintptr(unsafe.Pointer(&pid)))
if err1 != 0 {
goto childerror
}
}
}
// Chroot

View File

@@ -29,8 +29,6 @@ type SysProcAttr struct {
Ctty int // Controlling TTY fd (Linux only)
Pdeathsig Signal // Signal that the process will get when its parent dies (Linux only)
Cloneflags uintptr // Flags for clone calls (Linux only)
Foreground bool // Set foreground process group to child's pid. (Implies Setpgid. Stdin should be a TTY)
Joinpgrp int // If != 0, child's process group ID. (Setpgid must not be set)
UidMappings []SysProcIDMap // User ID mappings for user namespaces.
GidMappings []SysProcIDMap // Group ID mappings for user namespaces.
}
@@ -105,19 +103,6 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
Close(p[1])
}
if sys.Joinpgrp != 0 {
// Place the child in the specified process group.
RawSyscall(SYS_SETPGID, r1, uintptr(sys.Joinpgrp), 0)
} else if sys.Foreground || sys.Setpgid {
// Place the child in a new process group.
RawSyscall(SYS_SETPGID, 0, 0, 0)
if sys.Foreground {
// Set new foreground process group.
RawSyscall(SYS_IOCTL, uintptr(Stdin), TIOCSPGRP, uintptr(unsafe.Pointer(&pid)))
}
}
return pid, 0
}
@@ -179,30 +164,11 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
}
// Set process group
if sys.Joinpgrp != 0 {
// Place the child in the specified process group.
_, _, err1 = RawSyscall(SYS_SETPGID, r1, uintptr(sys.Joinpgrp), 0)
if err1 != 0 {
goto childerror
}
} else if sys.Foreground || sys.Setpgid {
// Place the child in a new process group.
if sys.Setpgid {
_, _, err1 = RawSyscall(SYS_SETPGID, 0, 0, 0)
if err1 != 0 {
goto childerror
}
if sys.Foreground {
r1, _, _ = RawSyscall(SYS_GETPID, 0, 0, 0)
pid := int(r1)
// Set new foreground process group.
_, _, err1 = RawSyscall(SYS_IOCTL, uintptr(Stdin), TIOCSPGRP, uintptr(unsafe.Pointer(&pid)))
if err1 != 0 {
goto childerror
}
}
}
// Chroot