Compare commits

...

33 Commits

Author SHA1 Message Date
Andrew Gerrand
2a3daa8bdd go1.3rc2
LGTM=minux, rsc
R=golang-codereviews, minux, rsc
CC=golang-codereviews
https://golang.org/cl/105170044
2014-06-13 13:24:50 +10:00
Russ Cox
64de40a551 [release-branch.go1.3] runtime: revise CL 105140044 (defer nil) to work on Windows
««« CL 105120044 / 824ea5943ba8
runtime: revise CL 105140044 (defer nil) to work on Windows

It appears that something about Go on Windows
cannot handle the fault cause by a jump to address 0.
The way Go represents and calls functions, this
never happened at all, until CL 105140044.

This CL changes the code added in CL 105140044
to make jump to 0 impossible once again.

Fixes #8047. (again, on Windows)

TBR=bradfitz
R=golang-codereviews, dave
CC=adg, golang-codereviews, iant, r
https://golang.org/cl/105120044
»»»

LGTM=bradfitz
R=golang-codereviews, bradfitz, alex.brainman
CC=adg, golang-codereviews
https://golang.org/cl/108890045
2014-06-12 21:52:52 -04:00
Andrew Gerrand
69616e4e5b revert go1.3rc2
The Windows build is still bad. The previous CL is not go1.3rc2.

TBR=bradfitz
R=golang-codereviews
CC=golang-codereviews
https://golang.org/cl/107050043
2014-06-13 10:12:29 +10:00
Andrew Gerrand
4dc991cd3f go1.3rc2
LGTM=iant
R=golang-codereviews, iant
CC=golang-codereviews
https://golang.org/cl/103420043
2014-06-13 09:41:58 +10:00
Russ Cox
1305c4ce9d [release-branch.go1.3] runtime: do not trace past jmpdefer during pprof traceback on arm
««« CL 107970043 / b336da131a84
runtime: do not trace past jmpdefer during pprof traceback on arm

jmpdefer modifies PC, SP, and LR, and not atomically,
so walking past jmpdefer will often end up in a state
where the three are not a consistent execution snapshot.
This was causing warning messages a few frames later
when the traceback realized it was confused, but given
the right memory it could easily crash instead.

Update #8153

LGTM=minux, iant
R=golang-codereviews, minux, iant
CC=golang-codereviews, r
https://golang.org/cl/107970043
»»»

LGTM=r
R=golang-codereviews, r
CC=adg, golang-codereviews, iant
https://golang.org/cl/101260043
2014-06-12 16:55:36 -04:00
Russ Cox
5c196b842a [release-branch.go1.3] runtime: fix defer of nil func
««« CL 105140044 / c2832405e9b9
runtime: fix defer of nil func

Fixes #8047.

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

LGTM=r
R=golang-codereviews, r
CC=adg, golang-codereviews, iant
https://golang.org/cl/103370044
2014-06-12 16:55:24 -04:00
Russ Cox
ad02e9ade5 [release-branch.go1.3] doc: link to release history from /doc/
««« CL 107950043 / 593f58ee96da
doc: link to release history from /doc/

Fixes #8168.

TBR=bradfitz
R=golang-codereviews
CC=golang-codereviews
https://golang.org/cl/107950043
»»»

LGTM=r
R=golang-codereviews, r
CC=adg, golang-codereviews, iant
https://golang.org/cl/105910043
2014-06-12 16:55:11 -04:00
Russ Cox
e0d5179635 [release-branch.go1.3] runtime: add test for issue 8047.
««« CL 108840043 / 3a2306461574
runtime: add test for issue 8047.

Make sure stack copier doesn't barf on a nil defer.
Bug was fixed in https://golang.org/cl/101800043
This change just adds a test.

Fixes #8047

LGTM=dvyukov, rsc
R=dvyukov, rsc
CC=golang-codereviews
https://golang.org/cl/108840043

»»»

TBR=adg
CC=golang-codereviews
https://golang.org/cl/102320043
2014-06-11 20:42:13 -04:00
Russ Cox
23f6bc5ed8 [release-branch.go1.3] net/http: fix double Content-Length in response
««« CL 105040043 / ef8878dbed3b
net/http: fix double Content-Length in response

Fixes #8180

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

TBR=bradfitz
R=golang-codereviews
CC=bradfitz, golang-codereviews, iant
https://golang.org/cl/102300046
2014-06-11 17:02:43 -04:00
Russ Cox
74183c5b06 [release-branch.go1.3] doc/install.html: fix erroneous HTML annotation
««« CL 106910044 / ac907318915d
doc/install.html: fix erroneous HTML annotation
align=middle is invalid; use align=center

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

LGTM=r
R=golang-codereviews, r
CC=bradfitz, golang-codereviews, iant
https://golang.org/cl/108860047
2014-06-11 17:00:27 -04:00
Russ Cox
8ef4135731 [release-branch.go1.3] cmd/gc: two escape analysis fixes
««« CL 108860043 / f153208c0a0e
cmd/gc: fix escape analysis for &x inside switch x := v.(type)

The analysis for &x was using the loop depth on x set
during x's declaration. A type switch creates a list of
implicit declarations that were not getting initialized
with loop depths.

Fixes #8176.

LGTM=iant
R=iant
CC=golang-codereviews
https://golang.org/cl/108860043
»»»

««« CL 108870044 / 331dbd4a6334
cmd/gc: fix &result escaping into result

There is a hierarchy of location defined by loop depth:

        -1 = the heap
        0 = function results
        1 = local variables (and parameters)
        2 = local variable declared inside a loop
        3 = local variable declared inside a loop inside a loop
        etc

In general if an address from loopdepth n is assigned to
something in loop depth m < n, that indicates an extended
lifetime of some form that requires a heap allocation.

Function results can be local variables too, though, and so
they don't actually fit into the hierarchy very well.
Treat the address of a function result as level 1 so that
if it is written back into a result, the address is treated
as escaping.

Fixes  issue 8185 .

LGTM=iant
R=iant
CC=golang-codereviews
https://golang.org/cl/108870044
»»»

LGTM=r
R=golang-codereviews, r
CC=bradfitz, golang-codereviews, iant
https://golang.org/cl/107930044
2014-06-11 17:00:17 -04:00
Russ Cox
16367614fb [release-branch.go1.3] cmd/ld: fix PC deltas in DWARF line number table
««« CL 104950045 / 87daa424d96a
cmd/ld: fix PC deltas in DWARF line number table

The putpclcdelta function set the DWARF line number PC to
s->value + pcline->pc, which is correct, but the code then set
the local variable pc to epc, which can be a different value.
This caused the next delta in the DWARF table to be wrong.

Fixes #8098.

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

LGTM=r
R=golang-codereviews, r
CC=bradfitz, golang-codereviews, iant
https://golang.org/cl/107900045
2014-06-11 16:55:38 -04:00
Russ Cox
cbffba160c [release-branch.go1.3] nacltest.bash, misc/nacl/README: update NaCl docs.
««« CL 105030043 / 6146799f32ed
nacltest.bash, misc/nacl/README: update NaCl docs.

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

LGTM=r
R=golang-codereviews, r
CC=bradfitz, golang-codereviews, iant
https://golang.org/cl/105020044
2014-06-11 16:55:24 -04:00
Russ Cox
37a84af3d3 [release-branch.go1.3] docs: link to the assembler document from the Documents tab
««« CL 108840045 / 087e446f2c41
docs: link to the assembler document from the Documents tab

Fixes #8156.

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

LGTM=r
R=golang-codereviews, r
CC=bradfitz, golang-codereviews, iant
https://golang.org/cl/101170045
2014-06-11 16:55:04 -04:00
Russ Cox
c70654f7e1 [release-branch.go1.3] runtime: fix panic stack during runtime.Goexit during panic
««« CL 102220043 / 00224712f89e
runtime: fix panic stack during runtime.Goexit during panic

A runtime.Goexit during a panic-invoked deferred call
left the panic stack intact even though all the stack frames
are gone when the goroutine is torn down.
The next goroutine to reuse that struct will have a
bogus panic stack and can cause the traceback routines
to walk into garbage.

Most likely to happen during tests, because t.Fatal might
be called during a deferred func and uses runtime.Goexit.

This "not enough cleared in Goexit" failure mode has
happened to us multiple times now. Clear all the pointers
that don't make sense to keep, not just gp->panic.

Fixes #8158.

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

LGTM=iant
R=golang-codereviews, iant
CC=golang-codereviews, r
https://golang.org/cl/108780044
2014-06-06 22:07:32 -04:00
Russ Cox
e16c88a5e7 [release-branch.go1.3] cmd/6g: fix stack zeroing on native client
««« CL 108740047 / c8e9255aed3f
cmd/6g: fix stack zeroing on native client

I am not sure what the rounding here was
trying to do, but it was skipping the first
pointer on native client.

The code above the rounding already checks
that xoffset is widthptr-aligned, so the rnd
was a no-op everywhere but on Native Client.
And on Native Client it was wrong.

Perhaps it was supposed to be rounding down,
not up, but zerorange handles the extra 32 bits
correctly, so the rnd does not seem to be necessary
at all.

This wouldn't be worth doing for Go 1.3 except
that it can affect code on the playground.

Fixes #8155.

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

LGTM=iant
R=golang-codereviews, iant
CC=golang-codereviews, r
https://golang.org/cl/107830044
2014-06-06 22:07:21 -04:00
Russ Cox
2dbe352b0f [release-branch.go1.3] doc: fix happens-before rules for buffered channels
««« CL 101980047 / 12c9a9ff50d8
doc: fix happens-before rules for buffered channels
The current wording is reversed in 2 places.
Not sure how it got 4 LGTMs (mine was there as well).
Update #6242.

LGTM=dan.kortschak, r, rsc
R=golang-codereviews, 0xjnml, dan.kortschak, r, rsc
CC=golang-codereviews
https://golang.org/cl/101980047
»»»

LGTM=iant
R=golang-codereviews, iant
CC=golang-codereviews, r
https://golang.org/cl/106830047
2014-06-06 22:07:11 -04:00
Russ Cox
dd58096ae6 [release-branch.go1.3] cmd/cgo: for typedef of untagged struct, use typedef name in C code
««« CL 103080043 / 5e058e21b67d
cmd/cgo: for typedef of untagged struct, use typedef name in C code

Fixes #8148.

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

LGTM=iant
R=golang-codereviews, iant
CC=golang-codereviews, r
https://golang.org/cl/103900046
2014-06-06 22:06:57 -04:00
Shenghou Ma
e1e3e705a6 [release-branch.go1.3] doc/install-source.html: document that GO386 will be auto-detected when building on both 386 and amd64.
««« CL 102150046 / ccf7893cc2f0
doc/install-source.html: document that GO386 will be auto-detected when building on both 386 and amd64.
Fixes #8152.

LGTM=iant
R=iant
CC=golang-codereviews
https://golang.org/cl/102150046
»»»

LGTM=iant
R=golang-codereviews, iant
CC=golang-codereviews
https://golang.org/cl/105830044
2014-06-04 21:19:54 -04:00
David Symonds
d7f399919e [release-branch.go1.3] debug/elf: support DWARF that needs relocs for 386
««« CL 96680045 / 5439c77d4acb
debug/elf: support DWARF that needs relocs for 386

It's not clear how widespread this issue is, but we do have a
test case generated by a development version of clang.

I don't know whether this should go into 1.3 or not; happy to
hear arguments either way.

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

LGTM=rsc
R=rsc
CC=golang-codereviews
https://golang.org/cl/102140043
2014-06-04 11:23:24 +10:00
David Symonds
6e3d786dbf [release-branch.go1.3] compress/gzip: allow Reset on Reader without NewReader
««« CL 103020044 / 318b56ffe04b
compress/gzip: allow Reset on Reader without NewReader

Fixes #8126.

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

TBR=rsc
R=rsc
CC=golang-codereviews
https://golang.org/cl/105820043
2014-06-04 11:21:08 +10:00
Russ Cox
764cb069db [release-branch.go1.3] crypto/tls: fix typo referencing the required Config field
««« CL 107740043 / d86ec79a5f30
crypto/tls: fix typo referencing the required Config field

Thanks to Frithjof Schulze for noticing.

LGTM=adg
R=adg
CC=agl, golang-codereviews, r
https://golang.org/cl/107740043

»»»

LGTM=r
R=golang-codereviews, r
CC=bradfitz, golang-codereviews, iant
https://golang.org/cl/103020043
2014-06-03 14:44:25 -04:00
Russ Cox
9381fe2d1d [release-branch.go1.3] cmd/gc: fix escape analysis of func returning indirect of parameter
««« CL 102040046 / a078b2056ebc
cmd/gc: fix escape analysis of func returning indirect of parameter

I introduced this bug when I changed the escape
analysis to run in phases based on call graph
dependency order, in order to be more precise about
inputs escaping back to outputs (functions returning
their arguments).

Given

        func f(z **int) *int { return *z }

we were tagging the function as 'z does not escape
and is not returned', which is all true, but not
enough information.

If used as:

        var x int
        p := &x
        q := &p
        leak(f(q))

then the compiler might try to keep x, p, and q all
on the stack, since (according to the recorded
information) nothing interesting ends up being
passed to leak.

In fact since f returns *q = p, &x is passed to leak
and x needs to be heap allocated.

To trigger the bug, you need a chain that the
compiler wants to keep on the stack (like x, p, q
above), and you need a function that returns an
indirect of its argument, and you need to pass the
head of the chain to that function. This doesn't
come up very often: this bug has been present since
June 2012 (between Go 1 and Go 1.1) and we haven't
seen it until now. It helps that most functions that
return indirects are getters that are simple enough
to be inlined, avoiding the bug.

Earlier versions of Go also had the benefit that if
&x really wasn't used beyond x's lifetime, nothing
broke if you put &x in a heap-allocated structure
accidentally. With the new stack copying, though,
heap-allocated structures containing &x are not
updated when the stack is copied and x moves,
leading to crashes in Go 1.3 that were not crashes
in Go 1.2 or Go 1.1.

The fix is in two parts.

First, in the analysis of a function, recognize when
a value obtained via indirect of a parameter ends up
being returned. Mark those parameters as having
content escape back to the return results (but we
don't bother to write down which result).

Second, when using the analysis to analyze, say,
f(q), mark parameters with content escaping as
having any indirections escape to the heap. (We
don't bother trying to match the content to the
return value.)

The fix could be less precise (simpler).
In the first part we might mark all content-escaping
parameters as plain escaping, and then the second
part could be dropped. Or we might assume that when
calling f(q) all the things pointed at by q escape
always (for any f and q).

The fix could also be more precise (more complex).
We might record the specific mapping from parameter
to result along with the number of indirects from the
parameter to the thing being returned as the result,
and then at the call sites we could set up exactly the
right graph for the called function. That would make
notleaks(f(q)) be able to keep x on the stack, because
the reuslt of f(q) isn't passed to anything that leaks it.

The less precise the fix, the more stack allocations
become heap allocations.

This fix is exactly as precise as it needs to be so that
none of the current stack allocations in the standard
library turn into heap allocations.

Fixes #8120.

LGTM=iant
R=golang-codereviews, iant
CC=golang-codereviews, khr, r
https://golang.org/cl/102040046
»»»

LGTM=iant
R=golang-codereviews, iant
CC=golang-codereviews
https://golang.org/cl/103870043
2014-06-03 14:42:14 -04:00
David Symonds
53479b823c [release-branch.go1.3] time: support version 3 zone records
««« CL 100930044 / fde405c62fca
time: support version 3 zone records

Fixes #8134

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

LGTM=rsc
R=adg, rsc
CC=golang-codereviews
https://golang.org/cl/96690043
2014-06-03 16:13:38 +10:00
David Symonds
a1fa208f79 [release-branch.go1.3] cmd/gc: fix liveness for address-taken variables in inlined functions
««« CL 96670046 / 1bec455e95f1
cmd/gc: fix liveness for address-taken variables in inlined functions

The 'address taken' bit in a function variable was not
propagating into the inlined copies, causing incorrect
liveness information.

LGTM=dsymonds, bradfitz
R=golang-codereviews, bradfitz
CC=dsymonds, golang-codereviews, iant, khr, r
https://golang.org/cl/96670046
»»»

TBR=adg
R=adg
CC=golang-codereviews
https://golang.org/cl/103810046
2014-06-03 11:30:20 +10:00
David Symonds
f096cad531 [release-branch.go1.3] runtime: fix 1-byte return during x.(T) for 0-byte T
««« CL 100940043 / 93baf7bea171
runtime: fix 1-byte return during x.(T) for 0-byte T

The 1-byte write was silently clearing a byte on the stack.
If there was another function call with more arguments
in the same stack frame, no harm done.
Otherwise, if the variable at that location was already zero,
no harm done.
Otherwise, problems.

Fixes #8139.

LGTM=dsymonds
R=golang-codereviews, dsymonds
CC=golang-codereviews, iant, r
https://golang.org/cl/100940043
»»»

TBR=adg
CC=golang-codereviews
https://golang.org/cl/105760045
2014-06-03 11:20:32 +10:00
David Symonds
f34a051afc [release-branch.go1.3] cmd/gc: don't generate zillions of linehists for wrapper functions
««« CL 104840043 / 876107512a67
cmd/gc: don't generate zillions of linehists for wrapper functions
This is a workaround - the code should be better than this - but the
fix avoids generating large numbers of linehist entries for the wrapper
functions that enable interface conversions. There can be many of
them, they all happen at the end of compilation, and they can all
share a linehist entry.
Avoids bad n^2 behavior in liblink.
Test case in issue 8135 goes from 64 seconds to 2.5 seconds (still bad
but not intolerable).

Fixes #8135.

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

TBR=adg
CC=golang-codereviews
https://golang.org/cl/102070045
2014-06-03 11:19:11 +10:00
David Symonds
e82cde2d5d [release-branch.go1.3] cmd/cgo: use same Go type for typedef to anonymous struct
««« CL 102080043 / 256d975c53cb
cmd/cgo: use same Go type for typedef to anonymous struct

If we see a typedef to an anonymous struct more than once,
presumably in two different Go files that import "C", use the
same Go type name.

Fixes #8133.

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

TBR=adg
CC=golang-codereviews
https://golang.org/cl/102100043
2014-06-03 11:18:01 +10:00
David Symonds
4aea3f6fee [release-branch.go1.3] doc: mention WriteHeapDump in 1.3 release notes
««« CL 103810044 / 603f6c3b152c
doc: mention WriteHeapDump in 1.3 release notes

LGTM=r
R=khr, r
CC=golang-codereviews
https://golang.org/cl/103810044
»»»

TBR=adg
R=adg
CC=golang-codereviews
https://golang.org/cl/99700043
2014-06-03 11:16:35 +10:00
Andrew Gerrand
a5565ec7d9 go1.3rc1
LGTM=minux
R=golang-codereviews, minux
CC=golang-codereviews
https://golang.org/cl/102920050
2014-06-02 14:34:50 +10:00
Andrew Gerrand
f2277d7722 [release-branch.go1.3] misc/nacl: remove cmd/link from testzip.proto
There's no cmd/link in this branch.

LGTM=bradfitz
R=golang-codereviews, bradfitz
CC=golang-codereviews
https://golang.org/cl/102050043
2014-06-02 13:37:16 +10:00
Andrew Gerrand
d00442aba8 [release-branch.go1.3] remove package debug/goobj and copy functionality to cmd/nm
https://golang.org/cl/103760043/
2014-06-02 13:04:43 +10:00
Andrew Gerrand
f40040b093 [release-branch.go1.3] remove cmd/link
https://golang.org/cl/106740043
2014-06-02 13:04:00 +10:00
84 changed files with 840 additions and 5510 deletions

1
VERSION Normal file
View File

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

View File

@@ -97,6 +97,9 @@ one goroutine can be guaranteed to observe values produced by writes to the
same variable in a different goroutine.
</p>
<h3 id="release"><a href="/doc/devel/release.html">Release History</a></h3>
<p>A summary of the changes between Go releases.</p>
<h2 id="articles">Articles</h2>
@@ -145,6 +148,7 @@ Guided tours of Go programs.
<li><a href="/blog/profiling-go-programs">Profiling Go Programs</a></li>
<li><a href="/doc/articles/race_detector.html">Data Race Detector</a> - a manual for the data race detector.</li>
<li><a href="/blog/race-detector">Introducing the Go Race Detector</a> - an introduction to the race detector.
<li><a href="/doc/asm">A Quick Guide to Go's Assembler</a> - an introduction to the assembler used by Go.
</ul>
<h4 id="articles_more">More</h4>

View File

@@ -539,6 +539,12 @@ Earlier versions of Go handled this situation inconsistently: most instances
were reported as deadlocks, but some trivial cases exited cleanly instead.
</li>
<li>
The runtime/debug package now has a new function
<a href="/pkg/runtime/debug/#WriteHeapDump"><code>debug.WriteHeapDump</code></a>
that writes out a description of the heap.
</li>
<li>
The <a href="/pkg/strconv/#CanBackquote"><code>CanBackquote</code></a>
function in the <a href="/pkg/strconv/"><code>strconv</code></a> package

View File

@@ -1,6 +1,6 @@
<!--{
"Title": "The Go Memory Model",
"Subtitle": "Version of March 6, 2012",
"Subtitle": "Version of May 31, 2014",
"Path": "/ref/mem"
}-->
@@ -275,17 +275,17 @@ crash, or do something else.)
</p>
<p class="rule">
The <i>k</i>th send on a channel with capacity <i>C</i> happens before the <i>k</i>+<i>C</i>th receive from that channel completes.
The <i>k</i>th receive on a channel with capacity <i>C</i> happens before the <i>k</i>+<i>C</i>th send from that channel completes.
</p>
<p>
This rule generalizes the previous rule to buffered channels.
It allows a counting semaphore to be modeled by a buffered channel:
the number of items in the channel corresponds to the semaphore count,
the capacity of the channel corresponds to the semaphore maximum,
the number of items in the channel corresponds to the number of active uses,
the capacity of the channel corresponds to the maximum number of simultaneous uses,
sending an item acquires the semaphore, and receiving an item releases
the semaphore.
This is a common idiom for rate-limiting work.
This is a common idiom for limiting concurrency.
</p>
<p>

View File

@@ -457,7 +457,7 @@ installs all commands there.
</p>
<li><code>$GO386</code> (for <code>386</code> only, default is auto-detected
if built natively, <code>387</code> if not)
if built on either <code>386</code> or <code>amd64</code>, <code>387</code> otherwise)
<p>
This controls the code generated by 8g to use either the 387 floating-point unit
(set to <code>387</code>) or SSE2 instructions (set to <code>sse2</code>) for

View File

@@ -39,9 +39,9 @@ proceeding. If your OS or architecture is not on the list, it's possible that
<table class="codetable" frame="border" summary="requirements">
<tr>
<th align="middle">Operating system</th>
<th align="middle">Architectures</th>
<th align="middle">Notes</th>
<th align="center">Operating system</th>
<th align="center">Architectures</th>
<th align="center">Notes</th>
</tr>
<tr><td colspan="3"><hr></td></tr>
<tr><td>FreeBSD 8 or later</td> <td>amd64, 386, arm</td> <td>Debian GNU/kFreeBSD not supported; FreeBSD/ARM needs FreeBSD 10 or later</td></tr>

View File

@@ -0,0 +1,31 @@
// 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.
// Issue 8148. A typedef of an unnamed struct didn't work when used
// with an exported Go function. No runtime test; just make sure it
// compiles.
package cgotest
/*
typedef struct { int i; } T;
int issue8148Callback(T*);
static int get() {
T t;
t.i = 42;
return issue8148Callback(&t);
}
*/
import "C"
//export issue8148Callback
func issue8148Callback(t *C.T) C.int {
return t.i
}
func Issue8148() int {
return int(C.get())
}

View File

@@ -2,8 +2,6 @@
// 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() {}
typedef struct {
int i;
} issue8331;

View File

@@ -0,0 +1,15 @@
// 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.
// Issue 8331. A typedef of an unnamed struct is the same struct when
// #include'd twice. No runtime test; just make sure it compiles.
package cgotest
// #include "issue8331.h"
import "C"
func issue8331a() C.issue8331 {
return issue8331Var
}

View File

@@ -0,0 +1,13 @@
// 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.
// Issue 8331. A typedef of an unnamed struct is the same struct when
// #include'd twice. No runtime test; just make sure it compiles.
package cgotest
// #include "issue8331.h"
import "C"
var issue8331Var C.issue8331

View File

@@ -54,12 +54,10 @@ The script's name has a special format, go_$GOOS_$GOARCH_exec, so cmd/go can fin
In short, if the support scripts are in place, the cmd/go tool can be used as per normal.
# Build the Go toolchain.
# Build and test Go for NaCl
NaCl does not permit direct file system access. Instead, package syscall provides a simulated file system served by in-memory data. The script nacltest.bash is the NaCl equivalent of all.bash. It builds NaCl with an in-memory file system containing files needed for tests, and then it runs the tests.
% cd go/src
% env GOOS=nacl GOARCH=amd64p32 ./make.bash
% env GOARCH=amd64p32 ./nacltest.bash
# Test the Go toolchain.
% cd go/src
% env GOOS=nacl GOARCH=amd64p32 ./run.bash

View File

@@ -13,9 +13,6 @@ go src=..
gofmt
testdata
+
link
testdata
+
pkg
archive
tar

View File

@@ -47,7 +47,7 @@ defframe(Prog *ptxt)
if(lo != hi && n->xoffset + n->type->width >= lo - 2*widthreg) {
// merge with range we already have
lo = rnd(n->xoffset, widthreg);
lo = n->xoffset;
continue;
}
// zero old range

View File

@@ -1269,7 +1269,8 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
sub := c.Type(dt.Type, pos)
t.Size = sub.Size
t.Align = sub.Align
if _, ok := typedef[name.Name]; !ok {
oldType := typedef[name.Name]
if oldType == nil {
tt := *t
tt.Go = sub.Go
typedef[name.Name] = &tt
@@ -1281,6 +1282,20 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
// In -godefs and -cdefs mode, do this for all typedefs.
if isStructUnionClass(sub.Go) || *godefs || *cdefs {
t.Go = sub.Go
if isStructUnionClass(sub.Go) {
// Use the typedef name for C code.
typedef[sub.Go.(*ast.Ident).Name].C = t.C
}
// If we've seen this typedef before, and it
// was an anonymous struct/union/class before
// too, use the old definition.
// TODO: it would be safer to only do this if
// we verify that the types are the same.
if oldType != nil && isStructUnionClass(oldType.Go) {
t.Go = oldType.Go
}
}
case *dwarf.UcharType:

View File

@@ -204,6 +204,13 @@ struct EscState {
// flow to.
Node theSink;
// If an analyzed function is recorded to return
// pieces obtained via indirection from a parameter,
// and later there is a call f(x) to that function,
// we create a link funcParam <- x to record that fact.
// The funcParam node is handled specially in escflood.
Node funcParam;
NodeList* dsts; // all dst nodes
int loopdepth; // for detecting nested loop scopes
int pdepth; // for debug printing in recursions.
@@ -269,7 +276,13 @@ analyze(NodeList *all, int recursive)
e->theSink.sym = lookup(".sink");
e->theSink.escloopdepth = -1;
e->recursive = recursive;
e->funcParam.op = ONAME;
e->funcParam.orig = &e->funcParam;
e->funcParam.class = PAUTO;
e->funcParam.sym = lookup(".param");
e->funcParam.escloopdepth = 10000000;
for(l=all; l; l=l->next)
if(l->n->op == ODCLFUNC)
l->n->esc = EscFuncPlanned;
@@ -429,6 +442,18 @@ esc(EscState *e, Node *n, Node *up)
if(n->op == OFOR || n->op == ORANGE)
e->loopdepth++;
// type switch variables have no ODCL.
// process type switch as declaration.
// must happen before processing of switch body,
// so before recursion.
if(n->op == OSWITCH && n->ntest && n->ntest->op == OTYPESW) {
for(ll=n->list; ll; ll=ll->next) { // cases
// ll->n->nname is the variable per case
if(ll->n->nname)
ll->n->nname->escloopdepth = e->loopdepth;
}
}
esc(e, n->left, n);
esc(e, n->right, n);
esc(e, n->ntest, n);
@@ -645,13 +670,24 @@ esc(EscState *e, Node *n, Node *up)
// current loop depth is an upper bound on actual loop depth
// of addressed value.
n->escloopdepth = e->loopdepth;
// for &x, use loop depth of x.
// for &x, use loop depth of x if known.
// it should always be known, but if not, be conservative
// and keep the current loop depth.
if(n->left->op == ONAME) {
switch(n->left->class) {
case PAUTO:
if(n->left->escloopdepth != 0)
n->escloopdepth = n->left->escloopdepth;
break;
case PPARAM:
case PPARAMOUT:
n->escloopdepth = n->left->escloopdepth;
// PPARAM is loop depth 1 always.
// PPARAMOUT is loop depth 0 for writes
// but considered loop depth 1 for address-of,
// so that writing the address of one result
// to another (or the same) result makes the
// first result move to the heap.
n->escloopdepth = 1;
break;
}
}
@@ -822,12 +858,17 @@ escassignfromtag(EscState *e, Strlit *note, NodeList *dsts, Node *src)
escassign(e, &e->theSink, src);
return em;
}
if(em == EscNone)
return em;
// If content inside parameter (reached via indirection)
// escapes back to results, mark as such.
if(em & EscContentEscapes)
escassign(e, &e->funcParam, src);
em0 = em;
for(em >>= EscBits; em && dsts; em >>= 1, dsts=dsts->next)
for(em >>= EscReturnBits; em && dsts; em >>= 1, dsts=dsts->next)
if(em & 1)
escassign(e, dsts->n, src);
@@ -1090,19 +1131,30 @@ escwalk(EscState *e, int level, Node *dst, Node *src)
// Input parameter flowing to output parameter?
if(dst->op == ONAME && dst->class == PPARAMOUT && dst->vargen <= 20) {
if(src->op == ONAME && src->class == PPARAM && level == 0 && src->curfn == dst->curfn) {
if(src->esc != EscScope && src->esc != EscHeap) {
if(src->op == ONAME && src->class == PPARAM && src->curfn == dst->curfn && src->esc != EscScope && src->esc != EscHeap) {
if(level == 0) {
if(debug['m'])
warnl(src->lineno, "leaking param: %hN to result %S", src, dst->sym);
if((src->esc&EscMask) != EscReturn)
src->esc = EscReturn;
src->esc |= 1<<((dst->vargen-1) + EscBits);
src->esc |= 1<<((dst->vargen-1) + EscReturnBits);
goto recurse;
} else if(level > 0) {
if(debug['m'])
warnl(src->lineno, "%N leaking param %hN content to result %S", src->curfn->nname, src, dst->sym);
if((src->esc&EscMask) != EscReturn)
src->esc = EscReturn;
src->esc |= EscContentEscapes;
goto recurse;
}
}
}
leaks = (level <= 0) && (dst->escloopdepth < src->escloopdepth);
// The second clause is for values pointed at by an object passed to a call
// that returns something reached via indirect from the object.
// We don't know which result it is or how many indirects, so we treat it as leaking.
leaks = level <= 0 && dst->escloopdepth < src->escloopdepth ||
level < 0 && dst == &e->funcParam && haspointers(src->type);
switch(src->op) {
case ONAME:

View File

@@ -236,8 +236,10 @@ enum
EscNone,
EscReturn,
EscNever,
EscBits = 4,
EscBits = 3,
EscMask = (1<<EscBits) - 1,
EscContentEscapes = 1<<EscBits, // value obtained by indirect of parameter escapes to some returned result
EscReturnBits = EscBits+1,
};
struct Node

View File

@@ -802,6 +802,7 @@ inlvar(Node *var)
n->class = PAUTO;
n->used = 1;
n->curfn = curfn; // the calling function, not the called one
n->addrtaken = var->addrtaken;
// esc pass wont run if we're inlining into a iface wrapper
// luckily, we can steal the results from the target func

View File

@@ -2493,6 +2493,7 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface)
Type *tpad, *methodrcvr;
int isddd;
Val v;
static int linehistdone = 0;
if(0 && debug['r'])
print("genwrapper rcvrtype=%T method=%T newnam=%S\n",
@@ -2500,7 +2501,11 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface)
lexlineno++;
lineno = lexlineno;
linehist("<autogenerated>", 0, 0);
if (linehistdone == 0) {
// All the wrappers can share the same linehist entry.
linehist("<autogenerated>", 0, 0);
linehistdone = 1;
}
dclcontext = PEXTERN;
markdcl();

View File

@@ -1624,13 +1624,13 @@ writelines(void)
}
putpclcdelta(s->value + pcline.pc - pc, pcline.value - line);
pc = epc;
pc = s->value + pcline.pc;
line = pcline.value;
if(pcfile.nextpc < pcline.nextpc)
epc = pcfile.nextpc;
else
epc = pcline.nextpc;
epc += s->value;
line = pcline.value;
}
da = 0;

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{
"bss": true,
"data": true,
"ebss": true,
"edata": true,
"efunctab": true,
"end": true,
"enoptrbss": true,
"enoptrdata": true,
"erodata": true,
"etext": true,
"etypelink": true,
"functab": true,
"gcbss": true,
"gcdata": true,
"noptrbss": true,
"noptrdata": true,
"pclntab": true,
"rodata": true,
"text": true,
"typelink": true,
}
// isAuto reports whether sym is an automatically-generated data or constant symbol.
func (p *Prog) isAuto(sym goobj.SymID) bool {
return strings.HasPrefix(sym.Name, "go.weak.") ||
strings.HasPrefix(sym.Name, "$f64.") ||
strings.HasPrefix(sym.Name, "$f32.") ||
linkerDefined[sym.Name]
}
// autoData defines the automatically generated data symbols needed by p.
func (p *Prog) autoData() {
for sym := range p.Missing {
switch {
// Floating-point constants that need to be loaded from memory are
// written as $f64.{16 hex digits} or $f32.{8 hex digits}; the hex digits
// give the IEEE bit pattern of the constant. As far as the layout into
// memory is concerned, we interpret these as uint64 or uint32 constants.
case strings.HasPrefix(sym.Name, "$f64."), strings.HasPrefix(sym.Name, "$f32."):
size := 64
if sym.Name[2:4] == "32" {
size = 32
}
delete(p.Missing, sym)
fbits, err := strconv.ParseUint(sym.Name[len("$f64."):], 16, size)
if err != nil {
p.errorf("unexpected floating point symbol %s", sym)
continue
}
data := make([]byte, size/8)
if size == 64 {
p.byteorder.PutUint64(data, fbits)
} else {
p.byteorder.PutUint32(data, uint32(fbits))
}
p.addSym(&Sym{
Sym: &goobj.Sym{
SymID: sym,
Kind: goobj.SRODATA,
Size: size / 8,
},
Bytes: data,
})
}
}
}
// autoConst defines the automatically generated constant symbols needed by p.
func (p *Prog) autoConst() {
for sym := range p.Missing {
switch {
case strings.HasPrefix(sym.Name, "go.weak."):
// weak symbol resolves to actual symbol if present, or else nil.
delete(p.Missing, sym)
targ := sym
targ.Name = sym.Name[len("go.weak."):]
var addr Addr
if s := p.Syms[targ]; s != nil {
addr = s.Addr
}
p.defineConst(sym.Name, addr)
}
}
}
// defineConst defines a new symbol with the given name and constant address.
func (p *Prog) defineConst(name string, addr Addr) {
sym := goobj.SymID{Name: name}
p.addSym(&Sym{
Sym: &goobj.Sym{
SymID: sym,
Kind: goobj.SCONST,
},
Package: nil,
Addr: addr,
})
}

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(name, start)
p.defineConst("e"+name, end)
progEnd = end
}
p.defineConst("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,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: "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,334 +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: "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) {
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: "gcdata"},
Kind: goobj.SRODATA,
},
})
p.addSym(&Sym{
Sym: &goobj.Sym{
SymID: goobj.SymID{Name: "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
go tool 6a $*.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 "../../ld/textflag.h"
TEXT start(SB),7,$0
MOVQ $autotab(SB),AX
MOVQ $autoptr(SB),AX
RET
GLOBL zero(SB), $8
GLOBL zeronoptr(SB), NOPTR, $16
// text
DATA autotab+0x00(SB)/8, $text(SB)
DATA autotab+0x08(SB)/8, $start(SB)
DATA autotab+0x10(SB)/8, $etext(SB)
DATA autotab+0x18(SB)/8, $start+16(SB)
// data
DATA autotab+0x20(SB)/8, $data(SB)
DATA autotab+0x28(SB)/8, $autotab(SB)
DATA autotab+0x30(SB)/8, $edata(SB)
DATA autotab+0x38(SB)/8, $nonzero+4(SB)
// bss
DATA autotab+0x40(SB)/8, $bss(SB)
DATA autotab+0x48(SB)/8, $zero(SB)
DATA autotab+0x50(SB)/8, $ebss(SB)
DATA autotab+0x58(SB)/8, $zero+8(SB)
// noptrdata
DATA autotab+0x60(SB)/8, $noptrdata(SB)
DATA autotab+0x68(SB)/8, $nonzeronoptr(SB)
DATA autotab+0x70(SB)/8, $enoptrdata(SB)
DATA autotab+0x78(SB)/8, $nonzeronoptr+8(SB)
// noptrbss
DATA autotab+0x80(SB)/8, $noptrbss(SB)
DATA autotab+0x88(SB)/8, $zeronoptr(SB)
DATA autotab+0x90(SB)/8, $enoptrbss(SB)
DATA autotab+0x98(SB)/8, $zeronoptr+16(SB)
// end
DATA autotab+0xa0(SB)/8, $end(SB)
DATA autotab+0xa8(SB)/8, $zeronoptr+16(SB)
GLOBL autotab(SB), $0xb0
DATA nonzero(SB)/4, $1
GLOBL nonzero(SB), $4
DATA nonzeronoptr(SB)/8, $2
GLOBL nonzeronoptr(SB), NOPTR, $8
GLOBL autoptr(SB), $0

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,49 +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 $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 $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 "../../ld/textflag.h"
TEXT text_start(SB),7,$0
MOVQ $rodata_sym(SB), AX
MOVQ $noptrdata_sym(SB), AX
MOVQ $data_sym(SB), AX
MOVQ $bss_sym(SB), AX
MOVQ $noptrbss_sym(SB), AX
RET
DATA rodata_sym(SB)/4, $1
GLOBL rodata_sym(SB), RODATA, $4
DATA noptrdata_sym(SB)/4, $1
GLOBL noptrdata_sym(SB), NOPTR, $4
DATA data_sym(SB)/4, $1
GLOBL data_sym(SB), $4
GLOBL bss_sym(SB), $4
GLOBL noptrbss_sym(SB), NOPTR, $4

View File

@@ -1,57 +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 d6 10 00 00 00 00 00 00 |................|
00000090 00 00 00 00 00 00 00 00 d6 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 b6 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 2f 55 73 65 72 73 2f 72 |......../Users/r|
000010b0 73 63 2f 67 2f 67 6f 2f 73 72 63 2f 63 6d 64 2f |sc/g/go/src/cmd/|
000010c0 6c 69 6e 6b 2f 74 65 73 74 64 61 74 61 2f 68 65 |link/testdata/he|
000010d0 6c 6c 6f 2e 73 00 00 00 00 00 00 00 00 00 00 00 |llo.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,3 +1,8 @@
// DO NOT EDIT. Generated by code.google.com/p/rsc/cmd/bundle
// bundle -p main -x goobj_ debug/goobj
/* read.go */
// Copyright 2013 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.
@@ -7,7 +12,8 @@
// TODO(rsc): Decide where this package should live. (golang.org/issue/6932)
// TODO(rsc): Decide the appropriate integer types for various fields.
// TODO(rsc): Write tests. (File format still up in the air a little.)
package goobj
package main
import (
"bufio"
@@ -20,7 +26,7 @@ import (
)
// A SymKind describes the kind of memory represented by a symbol.
type SymKind int
type goobj_SymKind int
// This list is taken from include/link.h.
@@ -28,108 +34,108 @@ type SymKind int
// TODO(rsc): Give idiomatic Go names.
// TODO(rsc): Reduce the number of symbol types in the object files.
const (
_ SymKind = iota
_ goobj_SymKind = iota
// readonly, executable
STEXT
SELFRXSECT
goobj_STEXT
goobj_SELFRXSECT
// readonly, non-executable
STYPE
SSTRING
SGOSTRING
SGOFUNC
SRODATA
SFUNCTAB
STYPELINK
SSYMTAB // TODO: move to unmapped section
SPCLNTAB
SELFROSECT
goobj_STYPE
goobj_SSTRING
goobj_SGOSTRING
goobj_SGOFUNC
goobj_SRODATA
goobj_SFUNCTAB
goobj_STYPELINK
goobj_SSYMTAB // TODO: move to unmapped section
goobj_SPCLNTAB
goobj_SELFROSECT
// writable, non-executable
SMACHOPLT
SELFSECT
SMACHO // Mach-O __nl_symbol_ptr
SMACHOGOT
SNOPTRDATA
SINITARR
SDATA
SWINDOWS
SBSS
SNOPTRBSS
STLSBSS
goobj_SMACHOPLT
goobj_SELFSECT
goobj_SMACHO // Mach-O __nl_symbol_ptr
goobj_SMACHOGOT
goobj_SNOPTRDATA
goobj_SINITARR
goobj_SDATA
goobj_SWINDOWS
goobj_SBSS
goobj_SNOPTRBSS
goobj_STLSBSS
// not mapped
SXREF
SMACHOSYMSTR
SMACHOSYMTAB
SMACHOINDIRECTPLT
SMACHOINDIRECTGOT
SFILE
SFILEPATH
SCONST
SDYNIMPORT
SHOSTOBJ
goobj_SXREF
goobj_SMACHOSYMSTR
goobj_SMACHOSYMTAB
goobj_SMACHOINDIRECTPLT
goobj_SMACHOINDIRECTGOT
goobj_SFILE
goobj_SFILEPATH
goobj_SCONST
goobj_SDYNIMPORT
goobj_SHOSTOBJ
)
var symKindStrings = []string{
SBSS: "SBSS",
SCONST: "SCONST",
SDATA: "SDATA",
SDYNIMPORT: "SDYNIMPORT",
SELFROSECT: "SELFROSECT",
SELFRXSECT: "SELFRXSECT",
SELFSECT: "SELFSECT",
SFILE: "SFILE",
SFILEPATH: "SFILEPATH",
SFUNCTAB: "SFUNCTAB",
SGOFUNC: "SGOFUNC",
SGOSTRING: "SGOSTRING",
SHOSTOBJ: "SHOSTOBJ",
SINITARR: "SINITARR",
SMACHO: "SMACHO",
SMACHOGOT: "SMACHOGOT",
SMACHOINDIRECTGOT: "SMACHOINDIRECTGOT",
SMACHOINDIRECTPLT: "SMACHOINDIRECTPLT",
SMACHOPLT: "SMACHOPLT",
SMACHOSYMSTR: "SMACHOSYMSTR",
SMACHOSYMTAB: "SMACHOSYMTAB",
SNOPTRBSS: "SNOPTRBSS",
SNOPTRDATA: "SNOPTRDATA",
SPCLNTAB: "SPCLNTAB",
SRODATA: "SRODATA",
SSTRING: "SSTRING",
SSYMTAB: "SSYMTAB",
STEXT: "STEXT",
STLSBSS: "STLSBSS",
STYPE: "STYPE",
STYPELINK: "STYPELINK",
SWINDOWS: "SWINDOWS",
SXREF: "SXREF",
var goobj_symKindStrings = []string{
goobj_SBSS: "SBSS",
goobj_SCONST: "SCONST",
goobj_SDATA: "SDATA",
goobj_SDYNIMPORT: "SDYNIMPORT",
goobj_SELFROSECT: "SELFROSECT",
goobj_SELFRXSECT: "SELFRXSECT",
goobj_SELFSECT: "SELFSECT",
goobj_SFILE: "SFILE",
goobj_SFILEPATH: "SFILEPATH",
goobj_SFUNCTAB: "SFUNCTAB",
goobj_SGOFUNC: "SGOFUNC",
goobj_SGOSTRING: "SGOSTRING",
goobj_SHOSTOBJ: "SHOSTOBJ",
goobj_SINITARR: "SINITARR",
goobj_SMACHO: "SMACHO",
goobj_SMACHOGOT: "SMACHOGOT",
goobj_SMACHOINDIRECTGOT: "SMACHOINDIRECTGOT",
goobj_SMACHOINDIRECTPLT: "SMACHOINDIRECTPLT",
goobj_SMACHOPLT: "SMACHOPLT",
goobj_SMACHOSYMSTR: "SMACHOSYMSTR",
goobj_SMACHOSYMTAB: "SMACHOSYMTAB",
goobj_SNOPTRBSS: "SNOPTRBSS",
goobj_SNOPTRDATA: "SNOPTRDATA",
goobj_SPCLNTAB: "SPCLNTAB",
goobj_SRODATA: "SRODATA",
goobj_SSTRING: "SSTRING",
goobj_SSYMTAB: "SSYMTAB",
goobj_STEXT: "STEXT",
goobj_STLSBSS: "STLSBSS",
goobj_STYPE: "STYPE",
goobj_STYPELINK: "STYPELINK",
goobj_SWINDOWS: "SWINDOWS",
goobj_SXREF: "SXREF",
}
func (k SymKind) String() string {
if k < 0 || int(k) >= len(symKindStrings) {
func (k goobj_SymKind) String() string {
if k < 0 || int(k) >= len(goobj_symKindStrings) {
return fmt.Sprintf("SymKind(%d)", k)
}
return symKindStrings[k]
return goobj_symKindStrings[k]
}
// A Sym is a named symbol in an object file.
type Sym struct {
SymID // symbol identifier (name and version)
Kind SymKind // kind of symbol
DupOK bool // are duplicate definitions okay?
Size int // size of corresponding data
Type SymID // symbol for Go type information
Data Data // memory image of symbol
Reloc []Reloc // relocations to apply to Data
Func *Func // additional data for functions
type goobj_Sym struct {
goobj_SymID // symbol identifier (name and version)
Kind goobj_SymKind // kind of symbol
DupOK bool // are duplicate definitions okay?
Size int // size of corresponding data
Type goobj_SymID // symbol for Go type information
Data goobj_Data // memory image of symbol
Reloc []goobj_Reloc // relocations to apply to Data
Func *goobj_Func // additional data for functions
}
// A SymID - the combination of Name and Version - uniquely identifies
// a symbol within a package.
type SymID struct {
type goobj_SymID struct {
// Name is the name of a symbol.
Name string
@@ -141,7 +147,7 @@ type SymID struct {
Version int
}
func (s SymID) String() string {
func (s goobj_SymID) String() string {
if s.Version == 0 {
return s.Name
}
@@ -151,20 +157,20 @@ func (s SymID) String() string {
// A Data is a reference to data stored in an object file.
// It records the offset and size of the data, so that a client can
// read the data only if necessary.
type Data struct {
type goobj_Data struct {
Offset int64
Size int64
}
// A Reloc describes a relocation applied to a memory image to refer
// to an address within a particular symbol.
type Reloc struct {
type goobj_Reloc struct {
// The bytes at [Offset, Offset+Size) within the memory image
// should be updated to refer to the address Add bytes after the start
// of the symbol Sym.
Offset int
Size int
Sym SymID
Sym goobj_SymID
Add int
// The Type records the form of address expected in the bytes
@@ -175,7 +181,7 @@ type Reloc struct {
// A Var describes a variable in a function stack frame: a declared
// local variable, an input argument, or an output result.
type Var struct {
type goobj_Var struct {
// The combination of Name, Kind, and Offset uniquely
// identifies a variable in a function stack frame.
// Using fewer of these - in particular, using only Name - does not.
@@ -183,57 +189,57 @@ type Var struct {
Kind int // TODO(rsc): Define meaning.
Offset int // Frame offset. TODO(rsc): Define meaning.
Type SymID // Go type for variable.
Type goobj_SymID // Go type for variable.
}
// Func contains additional per-symbol information specific to functions.
type Func struct {
Args int // size in bytes of argument frame: inputs and outputs
Frame int // size in bytes of local variable frame
Leaf bool // function omits save of link register (ARM)
NoSplit bool // function omits stack split prologue
Var []Var // detail about local variables
PCSP Data // PC → SP offset map
PCFile Data // PC → file number map (index into File)
PCLine Data // PC → line number map
PCData []Data // PC → runtime support data map
FuncData []FuncData // non-PC-specific runtime support data
File []string // paths indexed by PCFile
type goobj_Func struct {
Args int // size in bytes of argument frame: inputs and outputs
Frame int // size in bytes of local variable frame
Leaf bool // function omits save of link register (ARM)
NoSplit bool // function omits stack split prologue
Var []goobj_Var // detail about local variables
PCSP goobj_Data // PC → SP offset map
PCFile goobj_Data // PC → file number map (index into File)
PCLine goobj_Data // PC → line number map
PCData []goobj_Data // PC → runtime support data map
FuncData []goobj_FuncData // non-PC-specific runtime support data
File []string // paths indexed by PCFile
}
// TODO: Add PCData []byte and PCDataIter (similar to liblink).
// A FuncData is a single function-specific data value.
type FuncData struct {
Sym SymID // symbol holding data
Offset int64 // offset into symbol for funcdata pointer
type goobj_FuncData struct {
Sym goobj_SymID // symbol holding data
Offset int64 // offset into symbol for funcdata pointer
}
// A Package is a parsed Go object file or archive defining a Go package.
type Package struct {
ImportPath string // import path denoting this package
Imports []string // packages imported by this package
Syms []*Sym // symbols defined by this package
MaxVersion int // maximum Version in any SymID in Syms
type goobj_Package struct {
ImportPath string // import path denoting this package
Imports []string // packages imported by this package
Syms []*goobj_Sym // symbols defined by this package
MaxVersion int // maximum Version in any SymID in Syms
}
var (
archiveHeader = []byte("!<arch>\n")
archiveMagic = []byte("`\n")
goobjHeader = []byte("go objec") // truncated to size of archiveHeader
goobj_archiveHeader = []byte("!<arch>\n")
goobj_archiveMagic = []byte("`\n")
goobj_goobjHeader = []byte("go objec") // truncated to size of archiveHeader
errCorruptArchive = errors.New("corrupt archive")
errTruncatedArchive = errors.New("truncated archive")
errNotArchive = errors.New("unrecognized archive format")
goobj_errCorruptArchive = errors.New("corrupt archive")
goobj_errTruncatedArchive = errors.New("truncated archive")
goobj_errNotArchive = errors.New("unrecognized archive format")
errCorruptObject = errors.New("corrupt object file")
errTruncatedObject = errors.New("truncated object file")
errNotObject = errors.New("unrecognized object file format")
goobj_errCorruptObject = errors.New("corrupt object file")
goobj_errTruncatedObject = errors.New("truncated object file")
goobj_errNotObject = errors.New("unrecognized object file format")
)
// An objReader is an object file reader.
type objReader struct {
p *Package
type goobj_objReader struct {
p *goobj_Package
b *bufio.Reader
f io.ReadSeeker
err error
@@ -251,7 +257,7 @@ type objReader struct {
//
// See ../../../cmd/ld/lib.c:/^pathtoprefix and
// ../../../cmd/gc/subr.c:/^pathtoprefix.
func importPathToPrefix(s string) string {
func goobj_importPathToPrefix(s string) string {
// find index of last slash, if any, or else -1.
// used for determining whether an index is after the last slash.
slash := strings.LastIndex(s, "/")
@@ -284,21 +290,21 @@ func importPathToPrefix(s string) string {
}
// init initializes r to read package p from f.
func (r *objReader) init(f io.ReadSeeker, p *Package) {
func (r *goobj_objReader) init(f io.ReadSeeker, p *goobj_Package) {
r.f = f
r.p = p
r.offset, _ = f.Seek(0, 1)
r.limit, _ = f.Seek(0, 2)
f.Seek(r.offset, 0)
r.b = bufio.NewReader(f)
r.pkgprefix = importPathToPrefix(p.ImportPath) + "."
r.pkgprefix = goobj_importPathToPrefix(p.ImportPath) + "."
}
// error records that an error occurred.
// It returns only the first error, so that an error
// caused by an earlier error does not discard information
// about the earlier error.
func (r *objReader) error(err error) error {
func (r *goobj_objReader) error(err error) error {
if r.err == nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
@@ -314,7 +320,7 @@ func (r *objReader) error(err error) error {
// A sequence of 0 bytes will eventually terminate any
// parsing state in the object file. In particular, it ends the
// reading of a varint.
func (r *objReader) readByte() byte {
func (r *goobj_objReader) readByte() byte {
if r.err != nil {
return 0
}
@@ -339,7 +345,7 @@ func (r *objReader) readByte() byte {
// If an error occurs, read returns the error but also
// records it, so it is safe for callers to ignore the result
// as long as delaying the report is not a problem.
func (r *objReader) readFull(b []byte) error {
func (r *goobj_objReader) readFull(b []byte) error {
if r.err != nil {
return r.err
}
@@ -355,12 +361,12 @@ func (r *objReader) readFull(b []byte) error {
}
// readInt reads a zigzag varint from the input file.
func (r *objReader) readInt() int {
func (r *goobj_objReader) readInt() int {
var u uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
r.error(errCorruptObject)
r.error(goobj_errCorruptObject)
return 0
}
c := r.readByte()
@@ -372,14 +378,14 @@ func (r *objReader) readInt() int {
v := int64(u>>1) ^ (int64(u) << 63 >> 63)
if int64(int(v)) != v {
r.error(errCorruptObject) // TODO
r.error(goobj_errCorruptObject) // TODO
return 0
}
return int(v)
}
// readString reads a length-delimited string from the input file.
func (r *objReader) readString() string {
func (r *goobj_objReader) readString() string {
n := r.readInt()
buf := make([]byte, n)
r.readFull(buf)
@@ -387,7 +393,7 @@ func (r *objReader) readString() string {
}
// readSymID reads a SymID from the input file.
func (r *objReader) readSymID() SymID {
func (r *goobj_objReader) readSymID() goobj_SymID {
name, vers := r.readString(), r.readInt()
// In a symbol name in an object file, "". denotes the
@@ -403,19 +409,19 @@ func (r *objReader) readSymID() SymID {
vers = r.p.MaxVersion
}
return SymID{name, vers}
return goobj_SymID{name, vers}
}
// readData reads a data reference from the input file.
func (r *objReader) readData() Data {
func (r *goobj_objReader) readData() goobj_Data {
n := r.readInt()
d := Data{Offset: r.offset, Size: int64(n)}
d := goobj_Data{Offset: r.offset, Size: int64(n)}
r.skip(int64(n))
return d
}
// skip skips n bytes in the input.
func (r *objReader) skip(n int64) {
func (r *goobj_objReader) skip(n int64) {
if n < 0 {
r.error(fmt.Errorf("debug/goobj: internal error: misuse of skip"))
}
@@ -444,14 +450,14 @@ func (r *objReader) skip(n int64) {
// Parse parses an object file or archive from r,
// assuming that its import path is pkgpath.
func Parse(r io.ReadSeeker, pkgpath string) (*Package, error) {
func goobj_Parse(r io.ReadSeeker, pkgpath string) (*goobj_Package, error) {
if pkgpath == "" {
pkgpath = `""`
}
p := new(Package)
p := new(goobj_Package)
p.ImportPath = pkgpath
var rd objReader
var rd goobj_objReader
rd.init(r, p)
err := rd.readFull(rd.tmp[:8])
if err != nil {
@@ -463,14 +469,14 @@ func Parse(r io.ReadSeeker, pkgpath string) (*Package, error) {
switch {
default:
return nil, errNotObject
return nil, goobj_errNotObject
case bytes.Equal(rd.tmp[:8], archiveHeader):
case bytes.Equal(rd.tmp[:8], goobj_archiveHeader):
if err := rd.parseArchive(); err != nil {
return nil, err
}
case bytes.Equal(rd.tmp[:8], goobjHeader):
if err := rd.parseObject(goobjHeader); err != nil {
case bytes.Equal(rd.tmp[:8], goobj_goobjHeader):
if err := rd.parseObject(goobj_goobjHeader); err != nil {
return nil, err
}
}
@@ -480,7 +486,7 @@ func Parse(r io.ReadSeeker, pkgpath string) (*Package, error) {
// trimSpace removes trailing spaces from b and returns the corresponding string.
// This effectively parses the form used in archive headers.
func trimSpace(b []byte) string {
func goobj_trimSpace(b []byte) string {
return string(bytes.TrimRight(b, " "))
}
@@ -488,7 +494,7 @@ func trimSpace(b []byte) string {
// TODO(rsc): Need to skip non-Go object files.
// TODO(rsc): Maybe record table of contents in r.p so that
// linker can avoid having code to parse archives too.
func (r *objReader) parseArchive() error {
func (r *goobj_objReader) parseArchive() error {
for r.offset < r.limit {
if err := r.readFull(r.tmp[:60]); err != nil {
return err
@@ -512,20 +518,20 @@ func (r *objReader) parseArchive() error {
// The file data that follows is padded to an even number of bytes:
// if size is odd, an extra padding byte is inserted betw the next header.
if len(data) < 60 {
return errTruncatedArchive
return goobj_errTruncatedArchive
}
if !bytes.Equal(data[58:60], archiveMagic) {
return errCorruptArchive
if !bytes.Equal(data[58:60], goobj_archiveMagic) {
return goobj_errCorruptArchive
}
name := trimSpace(data[0:16])
size, err := strconv.ParseInt(trimSpace(data[48:58]), 10, 64)
name := goobj_trimSpace(data[0:16])
size, err := strconv.ParseInt(goobj_trimSpace(data[48:58]), 10, 64)
if err != nil {
return errCorruptArchive
return goobj_errCorruptArchive
}
data = data[60:]
fsize := size + size&1
if fsize < 0 || fsize < size {
return errCorruptArchive
return goobj_errCorruptArchive
}
switch name {
case "__.SYMDEF", "__.GOSYMDEF", "__.PKGDEF":
@@ -553,7 +559,7 @@ func (r *objReader) parseArchive() error {
// and then the part we want to parse begins.
// The format of that part is defined in a comment at the top
// of src/liblink/objfile.c.
func (r *objReader) parseObject(prefix []byte) error {
func (r *goobj_objReader) parseObject(prefix []byte) error {
// TODO(rsc): Maybe use prefix and the initial input to
// record the header line from the file, which would
// give the architecture and other version information.
@@ -563,7 +569,7 @@ func (r *objReader) parseObject(prefix []byte) error {
for {
c1, c2, c3 = c2, c3, r.readByte()
if c3 == 0 { // NUL or EOF, either is bad
return errCorruptObject
return goobj_errCorruptObject
}
if c1 == '\n' && c2 == '!' && c3 == '\n' {
break
@@ -572,12 +578,12 @@ func (r *objReader) parseObject(prefix []byte) error {
r.readFull(r.tmp[:8])
if !bytes.Equal(r.tmp[:8], []byte("\x00\x00go13ld")) {
return r.error(errCorruptObject)
return r.error(goobj_errCorruptObject)
}
b := r.readByte()
if b != 1 {
return r.error(errCorruptObject)
return r.error(goobj_errCorruptObject)
}
// Direct package dependencies.
@@ -593,20 +599,20 @@ func (r *objReader) parseObject(prefix []byte) error {
for {
if b := r.readByte(); b != 0xfe {
if b != 0xff {
return r.error(errCorruptObject)
return r.error(goobj_errCorruptObject)
}
break
}
typ := r.readInt()
s := &Sym{SymID: r.readSymID()}
s := &goobj_Sym{goobj_SymID: r.readSymID()}
r.p.Syms = append(r.p.Syms, s)
s.Kind = SymKind(typ)
s.Kind = goobj_SymKind(typ)
s.DupOK = r.readInt() != 0
s.Size = r.readInt()
s.Type = r.readSymID()
s.Data = r.readData()
s.Reloc = make([]Reloc, r.readInt())
s.Reloc = make([]goobj_Reloc, r.readInt())
for i := range s.Reloc {
rel := &s.Reloc[i]
rel.Offset = r.readInt()
@@ -618,14 +624,14 @@ func (r *objReader) parseObject(prefix []byte) error {
r.readSymID() // Xsym - ignored
}
if s.Kind == STEXT {
f := new(Func)
if s.Kind == goobj_STEXT {
f := new(goobj_Func)
s.Func = f
f.Args = r.readInt()
f.Frame = r.readInt()
f.Leaf = r.readInt() != 0
f.NoSplit = r.readInt() != 0
f.Var = make([]Var, r.readInt())
f.Var = make([]goobj_Var, r.readInt())
for i := range f.Var {
v := &f.Var[i]
v.Name = r.readSymID().Name
@@ -637,11 +643,11 @@ func (r *objReader) parseObject(prefix []byte) error {
f.PCSP = r.readData()
f.PCFile = r.readData()
f.PCLine = r.readData()
f.PCData = make([]Data, r.readInt())
f.PCData = make([]goobj_Data, r.readInt())
for i := range f.PCData {
f.PCData[i] = r.readData()
}
f.FuncData = make([]FuncData, r.readInt())
f.FuncData = make([]goobj_FuncData, r.readInt())
for i := range f.FuncData {
f.FuncData[i].Sym = r.readSymID()
}
@@ -657,7 +663,7 @@ func (r *objReader) parseObject(prefix []byte) error {
r.readFull(r.tmp[:7])
if !bytes.Equal(r.tmp[:7], []byte("\xffgo13ld")) {
return r.error(errCorruptObject)
return r.error(goobj_errCorruptObject)
}
return nil

View File

@@ -7,12 +7,11 @@
package main
import (
"debug/goobj"
"fmt"
"os"
)
func goobjName(id goobj.SymID) string {
func goobjName(id goobj_SymID) string {
if id.Version == 0 {
return id.Name
}
@@ -20,28 +19,28 @@ func goobjName(id goobj.SymID) string {
}
func goobjSymbols(f *os.File) []Sym {
pkg, err := goobj.Parse(f, `""`)
pkg, err := goobj_Parse(f, `""`)
if err != nil {
errorf("parsing %s: %v", f.Name(), err)
return nil
}
seen := make(map[goobj.SymID]bool)
seen := make(map[goobj_SymID]bool)
var syms []Sym
for _, s := range pkg.Syms {
seen[s.SymID] = true
sym := Sym{Addr: uint64(s.Data.Offset), Name: goobjName(s.SymID), Size: int64(s.Size), Type: s.Type.Name, Code: '?'}
seen[s.goobj_SymID] = true
sym := Sym{Addr: uint64(s.Data.Offset), Name: goobjName(s.goobj_SymID), Size: int64(s.Size), Type: s.Type.Name, Code: '?'}
switch s.Kind {
case goobj.STEXT, goobj.SELFRXSECT:
case goobj_STEXT, goobj_SELFRXSECT:
sym.Code = 'T'
case goobj.STYPE, goobj.SSTRING, goobj.SGOSTRING, goobj.SGOFUNC, goobj.SRODATA, goobj.SFUNCTAB, goobj.STYPELINK, goobj.SSYMTAB, goobj.SPCLNTAB, goobj.SELFROSECT:
case goobj_STYPE, goobj_SSTRING, goobj_SGOSTRING, goobj_SGOFUNC, goobj_SRODATA, goobj_SFUNCTAB, goobj_STYPELINK, goobj_SSYMTAB, goobj_SPCLNTAB, goobj_SELFROSECT:
sym.Code = 'R'
case goobj.SMACHOPLT, goobj.SELFSECT, goobj.SMACHO, goobj.SMACHOGOT, goobj.SNOPTRDATA, goobj.SINITARR, goobj.SDATA, goobj.SWINDOWS:
case goobj_SMACHOPLT, goobj_SELFSECT, goobj_SMACHO, goobj_SMACHOGOT, goobj_SNOPTRDATA, goobj_SINITARR, goobj_SDATA, goobj_SWINDOWS:
sym.Code = 'D'
case goobj.SBSS, goobj.SNOPTRBSS, goobj.STLSBSS:
case goobj_SBSS, goobj_SNOPTRBSS, goobj_STLSBSS:
sym.Code = 'B'
case goobj.SXREF, goobj.SMACHOSYMSTR, goobj.SMACHOSYMTAB, goobj.SMACHOINDIRECTPLT, goobj.SMACHOINDIRECTGOT, goobj.SFILE, goobj.SFILEPATH, goobj.SCONST, goobj.SDYNIMPORT, goobj.SHOSTOBJ:
case goobj_SXREF, goobj_SMACHOSYMSTR, goobj_SMACHOSYMTAB, goobj_SMACHOINDIRECTPLT, goobj_SMACHOINDIRECTGOT, goobj_SFILE, goobj_SFILEPATH, goobj_SCONST, goobj_SDYNIMPORT, goobj_SHOSTOBJ:
sym.Code = 'X' // should not see
}
if s.Version != 0 {

View File

@@ -7,7 +7,8 @@
# Builds a test file system and embeds it into package syscall
# in every generated binary.
#
# Assumes that sel_ldr binaries are in $PATH; see ../misc/nacl/README.
# Assumes that sel_ldr binaries and go_nacl_$GOARCH_exec scripts are in $PATH;
# see ../misc/nacl/README.
set -e
ulimit -c 0
@@ -32,6 +33,11 @@ amd64p32)
exit 1
esac
if ! which go_nacl_${naclGOARCH}_exec >/dev/null; then
echo "cannot find go_nacl_${naclGOARCH}_exec, see ../misc/nacl/README." 1>&2
exit 1
fi
# Run host build to get toolchain for running zip generator.
unset GOOS GOARCH
if [ ! -f make.bash ]; then

View File

@@ -94,7 +94,11 @@ func NewReader(r io.Reader) (*Reader, error) {
// This permits reusing a Reader rather than allocating a new one.
func (z *Reader) Reset(r io.Reader) error {
z.r = makeReader(r)
z.digest.Reset()
if z.digest == nil {
z.digest = crc32.NewIEEE()
} else {
z.digest.Reset()
}
z.size = 0
z.err = nil
return z.readHeader(true)

View File

@@ -353,3 +353,17 @@ func TestIssue6550(t *testing.T) {
// ok
}
}
func TestInitialReset(t *testing.T) {
var r Reader
if err := r.Reset(bytes.NewReader(gunzipTests[1].gzip)); err != nil {
t.Error(err)
}
var buf bytes.Buffer
if _, err := io.Copy(&buf, &r); err != nil {
t.Error(err)
}
if s := buf.String(); s != gunzipTests[1].raw {
t.Errorf("got %q want %q", s, gunzipTests[1].raw)
}
}

View File

@@ -28,7 +28,7 @@ func Server(conn net.Conn, config *Config) *Conn {
// Client returns a new TLS client side connection
// using conn as the underlying transport.
// The config cannot be nil: users must set either ServerHostname or
// The config cannot be nil: users must set either ServerName or
// InsecureSkipVerify in the config.
func Client(conn net.Conn, config *Config) *Conn {
return &Conn{conn: conn, config: config, isClient: true}

View File

@@ -522,13 +522,17 @@ func (f *File) applyRelocations(dst []byte, rels []byte) error {
if f.Class == ELFCLASS64 && f.Machine == EM_X86_64 {
return f.applyRelocationsAMD64(dst, rels)
}
if f.Class == ELFCLASS32 && f.Machine == EM_386 {
return f.applyRelocations386(dst, rels)
}
return errors.New("not implemented")
}
func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) error {
if len(rels)%Sym64Size != 0 {
return errors.New("length of relocation section is not a multiple of Sym64Size")
// 24 is the size of Rela64.
if len(rels)%24 != 0 {
return errors.New("length of relocation section is not a multiple of 24")
}
symbols, _, err := f.getSymbols(SHT_SYMTAB)
@@ -570,6 +574,43 @@ func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) error {
return nil
}
func (f *File) applyRelocations386(dst []byte, rels []byte) error {
// 8 is the size of Rel32.
if len(rels)%8 != 0 {
return errors.New("length of relocation section is not a multiple of 8")
}
symbols, _, err := f.getSymbols(SHT_SYMTAB)
if err != nil {
return err
}
b := bytes.NewReader(rels)
var rel Rel32
for b.Len() > 0 {
binary.Read(b, f.ByteOrder, &rel)
symNo := rel.Info >> 8
t := R_386(rel.Info & 0xff)
if symNo == 0 || symNo > uint32(len(symbols)) {
continue
}
sym := &symbols[symNo-1]
if t == R_386_32 {
if rel.Off+4 >= uint32(len(dst)) {
continue
}
val := f.ByteOrder.Uint32(dst[rel.Off : rel.Off+4])
val += uint32(sym.Value)
f.ByteOrder.PutUint32(dst[rel.Off:rel.Off+4], val)
}
}
return nil
}
func (f *File) DWARF() (*dwarf.Data, error) {
// There are many other DWARF sections, but these
// are the required ones, and the debug/dwarf package
@@ -603,6 +644,19 @@ func (f *File) DWARF() (*dwarf.Data, error) {
}
}
// When using clang we need to process relocations even for 386.
rel := f.Section(".rel.debug_info")
if rel != nil && rel.Type == SHT_REL && f.Machine == EM_386 {
data, err := rel.Data()
if err != nil {
return nil, err
}
err = f.applyRelocations(dat[1], data)
if err != nil {
return nil, err
}
}
abbrev, info, str := dat[0], dat[1], dat[2]
d, err := dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str)
if err != nil {

View File

@@ -260,6 +260,12 @@ var relocationTests = []relocationTest{
{0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.2.4 (Ubuntu 4.2.4-1ubuntu4)"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc424.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}},
},
},
{
"testdata/go-relocation-test-clang-x86.obj",
[]relocationTestEntry{
{0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "clang version google3-trunk (trunk r209387)"}, {Attr: dwarf.AttrLanguage, Val: int64(12)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-clang.c"}, {Attr: dwarf.AttrStmtList, Val: int64(0)}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}}}},
},
},
{
"testdata/gcc-amd64-openbsd-debug-with-rela.obj",
[]relocationTestEntry{

Binary file not shown.

View File

@@ -1,28 +0,0 @@
// Copyright 2013 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 goobj
import "testing"
var importPathToPrefixTests = []struct {
in string
out string
}{
{"runtime", "runtime"},
{"sync/atomic", "sync/atomic"},
{"code.google.com/p/go.tools/godoc", "code.google.com/p/go.tools/godoc"},
{"foo.bar/baz.quux", "foo.bar/baz%2equux"},
{"", ""},
{"%foo%bar", "%25foo%25bar"},
{"\x01\x00\x7F☺", "%01%00%7f%e2%98%ba"},
}
func TestImportPathToPrefix(t *testing.T) {
for _, tt := range importPathToPrefixTests {
if out := importPathToPrefix(tt.in); out != tt.out {
t.Errorf("importPathToPrefix(%q) = %q, want %q", tt.in, out, tt.out)
}
}
}

View File

@@ -266,7 +266,10 @@ func (r *Response) Write(w io.Writer) error {
return err
}
if r1.ContentLength == 0 && !chunked(r1.TransferEncoding) {
// contentLengthAlreadySent may have been already sent for
// POST/PUT requests, even if zero length. See Issue 8180.
contentLengthAlreadySent := tw.shouldSendContentLength()
if r1.ContentLength == 0 && !chunked(r1.TransferEncoding) && !contentLengthAlreadySent {
if _, err := io.WriteString(w, "Content-Length: 0\r\n"); err != nil {
return err
}

View File

@@ -191,6 +191,22 @@ func TestResponseWrite(t *testing.T) {
"Foo: Bar Baz\r\n" +
"\r\n",
},
// Want a single Content-Length header. Fixing issue 8180 where
// there were two.
{
Response{
StatusCode: StatusOK,
ProtoMajor: 1,
ProtoMinor: 1,
Request: &Request{Method: "POST"},
Header: Header{},
ContentLength: 0,
TransferEncoding: nil,
Body: nil,
},
"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
},
}
for i := range respWriteTests {

View File

@@ -155,7 +155,9 @@ func (t *transferWriter) WriteHeader(w io.Writer) error {
// function of the sanitized field triple (Body, ContentLength,
// TransferEncoding)
if t.shouldSendContentLength() {
io.WriteString(w, "Content-Length: ")
if _, err := io.WriteString(w, "Content-Length: "); err != nil {
return err
}
if _, err := io.WriteString(w, strconv.FormatInt(t.ContentLength, 10)+"\r\n"); err != nil {
return err
}

View File

@@ -394,6 +394,10 @@ TEXT runtime·lessstack(SB), NOSPLIT, $-4-0
// 1. grab stored LR for caller
// 2. sub 4 bytes to get back to BL deferreturn
// 3. B to fn
// TODO(rsc): Push things on stack and then use pop
// to load all registers simultaneously, so that a profiling
// interrupt can never see mismatched SP/LR/PC.
// (And double-check that pop is atomic in that way.)
TEXT runtime·jmpdefer(SB), NOSPLIT, $0-8
MOVW 0(SP), LR
MOVW $-4(LR), LR // BL deferreturn

View File

@@ -209,9 +209,19 @@ func convT2E(t *Type, elem *byte) (ret Eface) {
static void assertI2Tret(Type *t, Iface i, byte *ret);
/*
* NOTE: Cannot use 'func' here, because we have to declare
* a return value, the only types we have are at least 1 byte large,
* goc2c will zero the return value, and the actual return value
* might have size 0 bytes, in which case the zeroing of the
* 1 or more bytes would be wrong.
* Using C lets us control (avoid) the initial zeroing.
*/
#pragma textflag NOSPLIT
func assertI2T(t *Type, i Iface) (ret byte, ...) {
assertI2Tret(t, i, &ret);
void
runtime·assertI2T(Type *t, Iface i, GoOutput retbase)
{
assertI2Tret(t, i, (byte*)&retbase);
}
static void
@@ -260,9 +270,14 @@ func assertI2TOK(t *Type, i Iface) (ok bool) {
static void assertE2Tret(Type *t, Eface e, byte *ret);
/*
* NOTE: Cannot use 'func' here. See assertI2T above.
*/
#pragma textflag NOSPLIT
func assertE2T(t *Type, e Eface) (ret byte, ...) {
assertE2Tret(t, e, &ret);
void
runtime·assertE2T(Type *t, Eface e, GoOutput retbase)
{
assertE2Tret(t, e, (byte*)&retbase);
}
static void

View File

@@ -1459,6 +1459,12 @@ goexit0(G *gp)
gp->m = nil;
gp->lockedm = nil;
gp->paniconfault = 0;
gp->defer = nil; // should be true already but just in case.
gp->panic = nil; // non-nil for Goexit during panic. points at stack-allocated data.
gp->writenbuf = 0;
gp->writebuf = nil;
gp->waitreason = nil;
gp->param = nil;
m->curg = nil;
m->lockedg = nil;
if(m->locked & ~LockExternal) {

View File

@@ -9,6 +9,7 @@
#include "funcdata.h"
#include "typekind.h"
#include "type.h"
#include "../../cmd/ld/textflag.h"
enum
{
@@ -851,12 +852,25 @@ runtime·newstack(void)
*(int32*)345 = 123; // never return
}
#pragma textflag NOSPLIT
void
runtime·nilfunc(void)
{
*(byte*)0 = 0;
}
// adjust Gobuf as if it executed a call to fn
// and then did an immediate gosave.
void
runtime·gostartcallfn(Gobuf *gobuf, FuncVal *fv)
{
runtime·gostartcall(gobuf, fv->fn, fv);
void *fn;
if(fv != nil)
fn = fv->fn;
else
fn = runtime·nilfunc;
runtime·gostartcall(gobuf, fn, fv);
}
// Maybe shrink the stack being used by gp.

View File

@@ -110,6 +110,19 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
if(runtime·topofstack(f)) {
frame.lr = 0;
flr = nil;
} else if(f->entry == (uintptr)runtime·jmpdefer) {
// jmpdefer modifies SP/LR/PC non-atomically.
// If a profiling interrupt arrives during jmpdefer,
// the stack unwind may see a mismatched register set
// and get confused. Stop if we see PC within jmpdefer
// to avoid that confusion.
// See golang.org/issue/8153.
// This check can be deleted if jmpdefer is changed
// to restore all three atomically using pop.
if(callback != nil)
runtime·throw("traceback_arm: found jmpdefer when tracing with callback");
frame.lr = 0;
flr = nil;
} else {
if((n == 0 && frame.sp < frame.fp) || frame.lr == 0)
frame.lr = *(uintptr*)frame.sp;

View File

@@ -68,7 +68,7 @@ func loadZoneData(bytes []byte) (l *Location, err error) {
// 1-byte version, then 15 bytes of padding
var p []byte
if p = d.read(16); len(p) != 16 || p[0] != 0 && p[0] != '2' {
if p = d.read(16); len(p) != 16 || p[0] != 0 && p[0] != '2' && p[0] != '3' {
return nil, badData
}
@@ -123,7 +123,7 @@ func loadZoneData(bytes []byte) (l *Location, err error) {
return nil, badData
}
// If version == 2, the entire file repeats, this time using
// If version == 2 or 3, the entire file repeats, this time using
// 8-byte ints for txtimes and leap seconds.
// We won't need those until 2106.

View File

@@ -9,6 +9,15 @@ import (
"time"
)
func TestVersion3(t *testing.T) {
time.ForceZipFileForTesting(true)
defer time.ForceZipFileForTesting(false)
_, err := time.LoadLocation("Asia/Jerusalem")
if err != nil {
t.Fatal(err)
}
}
// Test that we get the correct results for times before the first
// transition time. To do this we explicitly check early dates in a
// couple of specific timezones.

View File

@@ -135,7 +135,7 @@ func (b *Bar) Leak() *int { // ERROR "leaking param: b"
return &b.i // ERROR "&b.i escapes to heap"
}
func (b *Bar) AlsoNoLeak() *int { // ERROR "b does not escape"
func (b *Bar) AlsoNoLeak() *int { // ERROR "leaking param b content to result ~r0"
return b.ii
}
@@ -149,7 +149,7 @@ func (b Bar) LeaksToo() *int { // ERROR "leaking param: b"
return b.ii
}
func (b *Bar) LeaksABit() *int { // ERROR "b does not escape"
func (b *Bar) LeaksABit() *int { // ERROR "leaking param b content to result ~r0"
v := 0 // ERROR "moved to heap: v"
b.ii = &v // ERROR "&v escapes"
return b.ii
@@ -182,7 +182,7 @@ func (b *Bar2) Leak() []int { // ERROR "leaking param: b"
return b.i[:] // ERROR "b.i escapes to heap"
}
func (b *Bar2) AlsoNoLeak() []int { // ERROR "b does not escape"
func (b *Bar2) AlsoNoLeak() []int { // ERROR "leaking param b content to result ~r0"
return b.ii[0:1]
}
@@ -1443,3 +1443,50 @@ func bar151d() {
b := a[:] // ERROR "a escapes to heap"
foo151(&b[4:8:8][0]) // ERROR "&b\[4:8:8\]\[0\] escapes to heap"
}
// issue 8120
type U struct {
s *string
}
func (u *U) String() *string { // ERROR "leaking param u content to result ~r0"
return u.s
}
type V struct {
s *string
}
func NewV(u U) *V { // ERROR "leaking param: u"
return &V{u.String()} // ERROR "&V literal escapes to heap" "u does not escape"
}
func foo152() {
a := "a" // ERROR "moved to heap: a"
u := U{&a} // ERROR "&a escapes to heap"
v := NewV(u)
println(v)
}
// issue 8176 - &x in type switch body not marked as escaping
func foo153(v interface{}) *int { // ERROR "leaking param: v"
switch x := v.(type) {
case int: // ERROR "moved to heap: x"
return &x // ERROR "&x escapes to heap"
}
panic(0)
}
// issue 8185 - &result escaping into result
func f() (x int, y *int) { // ERROR "moved to heap: x"
y = &x // ERROR "&x escapes to heap"
return
}
func g() (x interface{}) { // ERROR "moved to heap: x"
x = &x // ERROR "&x escapes to heap"
return
}

View File

@@ -0,0 +1,29 @@
// run
// 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.
// Issue 8047. Stack copier shouldn't crash if there
// is a nil defer.
package main
func stackit(n int) {
if n == 0 {
return
}
stackit(n - 1)
}
func main() {
defer func() {
// catch & ignore panic from nil defer below
err := recover()
if err == nil {
panic("defer of nil func didn't panic")
}
}()
defer ((func())(nil))()
stackit(1000)
}

View File

@@ -1,14 +1,22 @@
// run
// 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.
// Issue 8047. Defer setup during panic shouldn't crash for nil defer.
package main
import "io"
func (p *Prog) write(w io.Writer) {
p.Entry = p.Syms[p.startSym].Addr
p.formatter.write(w, p)
func main() {
defer func() {
recover()
}()
f()
}
func f() {
var g func()
defer g()
panic(1)
}

View File

@@ -0,0 +1,50 @@
// run
// 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.
// Issue 8139. The x.(T) assertions used to write 1 (unexpected)
// return byte for the 0-byte return value T.
package main
import "fmt"
type T struct{}
func (T) M() {}
type M interface {
M()
}
var e interface{} = T{}
var i M = T{}
var b bool
func f1() int {
if b {
return f1() // convince inliner not to inline
}
z := 0x11223344
_ = e.(T)
return z
}
func f2() int {
if b {
return f1() // convince inliner not to inline
}
z := 0x11223344
_ = i.(T)
return z
}
func main() {
x := f1()
y := f2()
if x != 0x11223344 || y != 0x11223344 {
fmt.Printf("BUG: x=%#x y=%#x, want 0x11223344 for both\n", x, y)
}
}

View File

@@ -0,0 +1,48 @@
// run
// 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.
// Issue 8155.
// Alignment of stack prologue zeroing was wrong on 64-bit Native Client
// (because of 32-bit pointers).
package main
import "runtime"
func bad(b bool) uintptr {
var p **int
var x1 uintptr
x1 = 1
if b {
var x [11]*int
p = &x[0]
}
if b {
var x [1]*int
p = &x[0]
}
runtime.GC()
if p != nil {
x1 = uintptr(**p)
}
return x1
}
func poison() uintptr {
runtime.GC()
var x [20]uintptr
var s uintptr
for i := range x {
x[i] = uintptr(i+1)
s += x[i]
}
return s
}
func main() {
poison()
bad(false)
}

View File

@@ -0,0 +1,41 @@
// run
// 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 (
"runtime"
"time"
)
func main() {
c := make(chan bool, 1)
go f1(c)
<-c
time.Sleep(10 * time.Millisecond)
go f2(c)
<-c
}
func f1(done chan bool) {
defer func() {
recover()
done <- true
runtime.Goexit() // left stack-allocated Panic struct on gp->panic stack
}()
panic("p")
}
func f2(done chan bool) {
defer func() {
recover()
done <- true
runtime.Goexit()
}()
time.Sleep(10 * time.Millisecond) // overwrote Panic struct with Timer struct
runtime.GC() // walked gp->panic list, found mangled Panic struct, crashed
panic("p")
}

View File

@@ -4,6 +4,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// liveness tests with inlining disabled.
// see also live2.go.
package main
func f1() {
@@ -590,3 +593,32 @@ func f39c() (x [10]*int) {
println() // ERROR "live at call to printnl: x"
return
}
// issue 8142: lost 'addrtaken' bit on inlined variables.
// no inlining in this test, so just checking that non-inlined works.
type T40 struct {
m map[int]int
}
func newT40() *T40 {
ret := T40{ // ERROR "live at call to makemap: &ret"
make(map[int]int),
}
return &ret
}
func bad40() {
t := newT40()
println()
_ = t
}
func good40() {
ret := T40{ // ERROR "live at call to makemap: ret"
make(map[int]int),
}
t := &ret
println() // ERROR "live at call to printnl: ret"
_ = t
}

39
test/live2.go Normal file
View File

@@ -0,0 +1,39 @@
// errorcheck -0 -live
// 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.
// liveness tests with inlining ENABLED
// see also live.go.
package main
// issue 8142: lost 'addrtaken' bit on inlined variables.
// no inlining in this test, so just checking that non-inlined works.
type T40 struct {
m map[int]int
}
func newT40() *T40 {
ret := T40{ // ERROR "live at call to makemap: &ret"
make(map[int]int),
}
return &ret
}
func bad40() {
t := newT40() // ERROR "live at call to makemap: ret"
println() // ERROR "live at call to printnl: ret"
_ = t
}
func good40() {
ret := T40{ // ERROR "live at call to makemap: ret"
make(map[int]int),
}
t := &ret
println() // ERROR "live at call to printnl: ret"
_ = t
}