mirror of
https://github.com/golang/go.git
synced 2026-01-29 07:02:05 +03:00
Compare commits
72 Commits
3604782621
...
release-br
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3dbc53ae6a | ||
|
|
59c83485df | ||
|
|
32fb19371b | ||
|
|
c09c8bb325 | ||
|
|
fcfb7aaf2e | ||
|
|
ca70c91892 | ||
|
|
3fa6b988e0 | ||
|
|
370a9c9c0e | ||
|
|
f3c81ed821 | ||
|
|
ef34616d6b | ||
|
|
247820ff6b | ||
|
|
7935b51b8b | ||
|
|
a3bfff1fbd | ||
|
|
881f0d1e9e | ||
|
|
4a05139f6f | ||
|
|
7769be7d2f | ||
|
|
df7a37efd7 | ||
|
|
1657de2d6d | ||
|
|
f36546bc10 | ||
|
|
f466851b77 | ||
|
|
69dc3a910f | ||
|
|
31f2f8d624 | ||
|
|
c6f639cdf6 | ||
|
|
3fa4a7849c | ||
|
|
fa02130477 | ||
|
|
6b0ac0f972 | ||
|
|
78a4cf7f39 | ||
|
|
b0454f5d2b | ||
|
|
b48cd4b9dc | ||
|
|
1798bb298f | ||
|
|
0752bc8f26 | ||
|
|
329d3ce984 | ||
|
|
57435625e5 | ||
|
|
1cdd48c8a2 | ||
|
|
89a95c92a3 | ||
|
|
0a164d2333 | ||
|
|
9d7a83a04b | ||
|
|
60b7cd2c85 | ||
|
|
2ae4c996a1 | ||
|
|
2a3daa8bdd | ||
|
|
64de40a551 | ||
|
|
69616e4e5b | ||
|
|
4dc991cd3f | ||
|
|
1305c4ce9d | ||
|
|
5c196b842a | ||
|
|
ad02e9ade5 | ||
|
|
e0d5179635 | ||
|
|
23f6bc5ed8 | ||
|
|
74183c5b06 | ||
|
|
8ef4135731 | ||
|
|
16367614fb | ||
|
|
cbffba160c | ||
|
|
37a84af3d3 | ||
|
|
c70654f7e1 | ||
|
|
e16c88a5e7 | ||
|
|
2dbe352b0f | ||
|
|
dd58096ae6 | ||
|
|
e1e3e705a6 | ||
|
|
d7f399919e | ||
|
|
6e3d786dbf | ||
|
|
764cb069db | ||
|
|
9381fe2d1d | ||
|
|
53479b823c | ||
|
|
a1fa208f79 | ||
|
|
f096cad531 | ||
|
|
f34a051afc | ||
|
|
e82cde2d5d | ||
|
|
4aea3f6fee | ||
|
|
a5565ec7d9 | ||
|
|
f2277d7722 | ||
|
|
d00442aba8 | ||
|
|
f40040b093 |
1
.hgtags
1
.hgtags
@@ -126,6 +126,5 @@ b3d5a20b070a92da2458c5788694d1359b353f4a go1.2rc5
|
||||
87dea3f5ebe7510998c84dbeeec89382b7d42f9c go1.2
|
||||
0ddbdc3c7ce27e66508fe58ab81ff29324786026 go1.2.1
|
||||
9c4fdd8369ca4483fbed1cb8e67f02643ca10f79 go1.2.2
|
||||
9c4fdd8369ca4483fbed1cb8e67f02643ca10f79 release
|
||||
f8b50ad4cac4d4c4ecf48324b4f512f65e82cc1c go1.3beta1
|
||||
9e1652c32289c164126b6171f024afad5665fc9e go1.3beta2
|
||||
|
||||
@@ -62,7 +62,7 @@ details.
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><a href="http://godoc.org/code.google.com/p/go.tools/cmd/cover/">cover</a></td>
|
||||
<td><a href="//godoc.org/code.google.com/p/go.tools/cmd/cover/">cover</a></td>
|
||||
<td> </td>
|
||||
<td>Cover is a program for creating and analyzing the coverage profiles
|
||||
generated by <code>"go test -coverprofile"</code>.
|
||||
@@ -83,13 +83,13 @@ gofmt</a> command with more general options.</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><a href="http://godoc.org/code.google.com/p/go.tools/cmd/godoc/">godoc</a></td>
|
||||
<td><a href="//godoc.org/code.google.com/p/go.tools/cmd/godoc/">godoc</a></td>
|
||||
<td> </td>
|
||||
<td>Godoc extracts and generates documentation for Go packages.</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><a href="http://godoc.org/code.google.com/p/go.tools/cmd/vet/">vet</a></td>
|
||||
<td><a href="//godoc.org/code.google.com/p/go.tools/cmd/vet/">vet</a></td>
|
||||
<td> </td>
|
||||
<td>Vet examines Go source code and reports suspicious constructs, such as Printf
|
||||
calls whose arguments do not align with the format string.</td>
|
||||
|
||||
@@ -18,7 +18,7 @@ It explains the simplest way to get up and running with your Go installation.
|
||||
|
||||
<p>
|
||||
A similar explanation is available as a
|
||||
<a href="http://www.youtube.com/watch?v=XCsL89YtqCs">screencast</a>.
|
||||
<a href="//www.youtube.com/watch?v=XCsL89YtqCs">screencast</a>.
|
||||
</p>
|
||||
|
||||
|
||||
@@ -508,7 +508,7 @@ revision control system such as Git or Mercurial. The <code>go</code> tool uses
|
||||
this property to automatically fetch packages from remote repositories.
|
||||
For instance, the examples described in this document are also kept in a
|
||||
Mercurial repository hosted at Google Code,
|
||||
<code><a href="http://code.google.com/p/go.example">code.google.com/p/go.example</a></code>.
|
||||
<code><a href="//code.google.com/p/go.example">code.google.com/p/go.example</a></code>.
|
||||
If you include the repository URL in the package's import path,
|
||||
<code>go get</code> will fetch, build, and install it automatically:
|
||||
</p>
|
||||
@@ -569,8 +569,8 @@ import "code.google.com/p/go.example/newmath"
|
||||
<p>
|
||||
This convention is the easiest way to make your Go packages available for
|
||||
others to use.
|
||||
The <a href="http://code.google.com/p/go-wiki/wiki/Projects">Go Wiki</a>
|
||||
and <a href="http://godoc.org/">godoc.org</a>
|
||||
The <a href="//code.google.com/p/go-wiki/wiki/Projects">Go Wiki</a>
|
||||
and <a href="//godoc.org/">godoc.org</a>
|
||||
provide lists of external Go projects.
|
||||
</p>
|
||||
|
||||
@@ -584,7 +584,7 @@ For more information on using remote repositories with the <code>go</code> tool,
|
||||
|
||||
<p>
|
||||
Subscribe to the
|
||||
<a href="http://groups.google.com/group/golang-announce">golang-announce</a>
|
||||
<a href="//groups.google.com/group/golang-announce">golang-announce</a>
|
||||
mailing list to be notified when a new stable version of Go is released.
|
||||
</p>
|
||||
|
||||
@@ -594,7 +594,7 @@ clear, idiomatic Go code.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Take <a href="http://tour.golang.org/">A Tour of Go</a> to learn the language
|
||||
Take <a href="//tour.golang.org/">A Tour of Go</a> to learn the language
|
||||
proper.
|
||||
</p>
|
||||
|
||||
@@ -613,10 +613,10 @@ For real-time help, ask the helpful gophers in <code>#go-nuts</code> on the
|
||||
|
||||
<p>
|
||||
The official mailing list for discussion of the Go language is
|
||||
<a href="http://groups.google.com/group/golang-nuts">Go Nuts</a>.
|
||||
<a href="//groups.google.com/group/golang-nuts">Go Nuts</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Report bugs using the
|
||||
<a href="http://code.google.com/p/go/issues/list">Go issue tracker</a>.
|
||||
<a href="//code.google.com/p/go/issues/list">Go issue tracker</a>.
|
||||
</p>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<p>
|
||||
Go is an open source project developed by a team at
|
||||
<a href="http://google.com/">Google</a> and many
|
||||
<a href="//google.com/">Google</a> and many
|
||||
<a href="/CONTRIBUTORS">contributors</a> from the open source community.
|
||||
</p>
|
||||
|
||||
@@ -17,13 +17,13 @@ Go is an open source project developed by a team at
|
||||
Go is distributed under a <a href="/LICENSE">BSD-style license</a>.
|
||||
</p>
|
||||
|
||||
<h3 id="announce"><a href="http://groups.google.com/group/golang-announce">Announcements Mailing List</a></h3>
|
||||
<h3 id="announce"><a href="//groups.google.com/group/golang-announce">Announcements Mailing List</a></h3>
|
||||
<p>
|
||||
A low traffic mailing list for important announcements, such as new releases.
|
||||
</p>
|
||||
<p>
|
||||
We encourage all Go users to subscribe to
|
||||
<a href="http://groups.google.com/group/golang-announce">golang-announce</a>.
|
||||
<a href="//groups.google.com/group/golang-announce">golang-announce</a>.
|
||||
</p>
|
||||
|
||||
|
||||
@@ -72,9 +72,9 @@ href="https://groups.google.com/group/golang-nuts">golang-nuts</a>.</p>
|
||||
<p>A mailing list that receives a message summarizing each checkin to the Go repository.</p>
|
||||
|
||||
<h3 id="golang-bugs"><a href="https://groups.google.com/group/golang-bugs">Bugs Mailing List</a></h3>
|
||||
<p>A mailing list that receives each update to the Go <a href="http://golang.org/issue">issue tracker</a>.</p>
|
||||
<p>A mailing list that receives each update to the Go <a href="//golang.org/issue">issue tracker</a>.</p>
|
||||
|
||||
<h3 id="build_status"><a href="http://build.golang.org/">Build Status</a></h3>
|
||||
<h3 id="build_status"><a href="//build.golang.org/">Build Status</a></h3>
|
||||
<p>View the status of Go builds across the supported operating
|
||||
systems and architectures.</p>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
}-->
|
||||
|
||||
<p>This page summarizes the changes between official stable releases of Go.
|
||||
The <a href="http://code.google.com/p/go/source/list">Mercurial change log</a>
|
||||
The <a href="//code.google.com/p/go/source/list">Mercurial change log</a>
|
||||
has the full details.</p>
|
||||
|
||||
<p>To update to a specific release, use:</p>
|
||||
@@ -13,6 +13,30 @@ hg pull
|
||||
hg update <i>tag</i>
|
||||
</pre>
|
||||
|
||||
<h2 id="go1.3">go1.3 (released 2014/06/18)</h2>
|
||||
|
||||
<p>
|
||||
Go 1.3 is a major release of Go.
|
||||
Read the <a href="/doc/go1.3">Go 1.3 Release Notes</a> for more information.
|
||||
</p>
|
||||
|
||||
<h3 id="go1.3.minor">Minor revisions</h3>
|
||||
|
||||
<p>
|
||||
go1.3.1 (released 2014/08/13) includes bug fixes to the compiler and the <code>runtime</code>, <code>net</code>, and <code>crypto/rsa</code> packages.
|
||||
See the <a href="//code.google.com/p/go/source/list?name=release-branch.go1.3&r=073fc578434bf3e1e22749b559d273c8da728ebb">change history</a> for details.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
go1.3.2 (released 2014/09/25) includes bug fixes to cgo and the crypto/tls packages.
|
||||
See the <a href="//code.google.com/p/go/source/list?name=release-branch.go1.3&r=go1.3.2">change history</a> for details.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
go1.3.3 (released 2014/09/30) includes further bug fixes to cgo, the runtime package, and the nacl port.
|
||||
See the <a href="//code.google.com/p/go/source/list?name=release-branch.go1.3&r=go1.3.3">change history</a> for details.
|
||||
</p>
|
||||
|
||||
<h2 id="go1.2">go1.2 (released 2013/12/01)</h2>
|
||||
|
||||
<p>
|
||||
@@ -24,12 +48,12 @@ Read the <a href="/doc/go1.2">Go 1.2 Release Notes</a> for more information.
|
||||
|
||||
<p>
|
||||
go1.2.1 (released 2014/03/02) includes bug fixes to the <code>runtime</code>, <code>net</code>, and <code>database/sql</code> packages.
|
||||
See the <a href="https://code.google.com/p/go/source/list?name=release-branch.go1.2&r=7ada9e760ce34e78aee5b476c9621556d0fa5d31">change history</a> for details.
|
||||
See the <a href="//code.google.com/p/go/source/list?name=release-branch.go1.2&r=7ada9e760ce34e78aee5b476c9621556d0fa5d31">change history</a> for details.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
go1.2.2 (released 2014/05/05) includes a
|
||||
<a href="https://code.google.com/p/go/source/detail?r=bda3619e7a2c&repo=tools">security fix</a>
|
||||
<a href="//code.google.com/p/go/source/detail?r=bda3619e7a2c&repo=tools">security fix</a>
|
||||
that affects the tour binary included in the binary distributions (thanks to Guillaume T).
|
||||
</p>
|
||||
|
||||
@@ -44,18 +68,18 @@ Read the <a href="/doc/go1.1">Go 1.1 Release Notes</a> for more information.
|
||||
|
||||
<p>
|
||||
go1.1.1 (released 2013/06/13) includes several compiler and runtime bug fixes.
|
||||
See the <a href="https://code.google.com/p/go/source/list?name=release-branch.go1.1&r=43c4a41d24382a56a90e924800c681e435d9e399">change history</a> for details.
|
||||
See the <a href="//code.google.com/p/go/source/list?name=release-branch.go1.1&r=43c4a41d24382a56a90e924800c681e435d9e399">change history</a> for details.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
go1.1.2 (released 2013/08/13) includes fixes to the <code>gc</code> compiler
|
||||
and <code>cgo</code>, and the <code>bufio</code>, <code>runtime</code>,
|
||||
<code>syscall</code>, and <code>time</code> packages.
|
||||
See the <a href="https://code.google.com/p/go/source/list?name=release-branch.go1.1&r=a6a9792f94acd4ff686b2bc57383d163608b91cf">change history</a> for details.
|
||||
See the <a href="//code.google.com/p/go/source/list?name=release-branch.go1.1&r=a6a9792f94acd4ff686b2bc57383d163608b91cf">change history</a> for details.
|
||||
If you use package syscall's <code>Getrlimit</code> and <code>Setrlimit</code>
|
||||
functions under Linux on the ARM or 386 architectures, please note change
|
||||
<a href="http://golang.org/change/55ac276af5a7">55ac276af5a7</a>
|
||||
that fixes <a href="http://golang.org/issue/5949">issue 5949</a>.
|
||||
<a href="//golang.org/change/55ac276af5a7">55ac276af5a7</a>
|
||||
that fixes <a href="//golang.org/issue/5949">issue 5949</a>.
|
||||
</p>
|
||||
|
||||
<h2 id="go1">go1 (released 2012/03/28)</h2>
|
||||
@@ -81,17 +105,17 @@ The go1 release corresponds to
|
||||
|
||||
<p>
|
||||
go1.0.1 (released 2012/04/25) was issued to
|
||||
<a href="https://code.google.com/p/go/source/detail?r=a890477d3dfb">fix</a> an
|
||||
<a href="https://code.google.com/p/go/issues/detail?id=3545">escape analysis
|
||||
bug</a> that can lead to memory corruption.
|
||||
<a href="//golang.org/change/a890477d3dfb">fix</a> an
|
||||
<a href="//golang.org/issue/3545">escape analysis bug</a>
|
||||
that can lead to memory corruption.
|
||||
It also includes several minor code and documentation fixes.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
go1.0.2 (released 2012/06/13) was issued to fix two bugs in the implementation
|
||||
of maps using struct or array keys:
|
||||
<a href="http://code.google.com/p/go/issues/detail?id=3695">issue 3695</a> and
|
||||
<a href="http://code.google.com/p/go/issues/detail?id=3573">issue 3573</a>.
|
||||
<a href="//golang.org/issue/3695">issue 3695</a> and
|
||||
<a href="//golang.org/issue/3573">issue 3573</a>.
|
||||
It also includes many minor code and documentation fixes.
|
||||
</p>
|
||||
|
||||
@@ -100,7 +124,7 @@ go1.0.3 (released 2012/09/21) includes minor code and documentation fixes.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
See the <a href="http://code.google.com/p/go/source/list?name=release-branch.go1">go1 release branch history</a> for the complete list of changes.
|
||||
See the <a href="//code.google.com/p/go/source/list?name=release-branch.go1">go1 release branch history</a> for the complete list of changes.
|
||||
</p>
|
||||
|
||||
<h2 id="r60">r60 (released 2011/09/07)</h2>
|
||||
@@ -112,7 +136,7 @@ This section highlights the most significant changes in this release.
|
||||
For a more detailed summary, see the
|
||||
<a href="weekly.html#2011-08-17">weekly release notes</a>.
|
||||
For complete information, see the
|
||||
<a href="http://code.google.com/p/go/source/list?r=release-branch.r60">Mercurial change list</a>.
|
||||
<a href="//code.google.com/p/go/source/list?r=release-branch.r60">Mercurial change list</a>.
|
||||
</p>
|
||||
|
||||
<h3 id="r60.lang">Language</h3>
|
||||
@@ -182,26 +206,26 @@ more accessible.
|
||||
|
||||
<p>
|
||||
r60.1 includes a
|
||||
<a href="http://code.google.com/p/go/source/detail?r=1824581bf62d">linker
|
||||
<a href="//golang.org/change/1824581bf62d">linker
|
||||
fix</a>, a pair of
|
||||
<a href="http://code.google.com/p/go/source/detail?r=9ef4429c2c64">goplay</a>
|
||||
<a href="http://code.google.com/p/go/source/detail?r=d42ed8c3098e">fixes</a>,
|
||||
<a href="//golang.org/change/9ef4429c2c64">goplay</a>
|
||||
<a href="//golang.org/change/d42ed8c3098e">fixes</a>,
|
||||
and a <code>json</code> package
|
||||
<a href="http://code.google.com/p/go/source/detail?r=d5e97874fe84">fix</a> and
|
||||
<a href="//golang.org/change/d5e97874fe84">fix</a> and
|
||||
a new
|
||||
<a href="http://code.google.com/p/go/source/detail?r=4f0e6269213f">struct tag
|
||||
<a href="//golang.org/change/4f0e6269213f">struct tag
|
||||
option</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
r60.2
|
||||
<a href="http://code.google.com/p/go/source/detail?r=ff19536042ac">fixes</a>
|
||||
<a href="//golang.org/change/ff19536042ac">fixes</a>
|
||||
a memory leak involving maps.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
r60.3 fixes a
|
||||
<a href="http://code.google.com/p/go/source/detail?r=01fa62f5e4e5">reflect bug</a>.
|
||||
<a href="//golang.org/change/01fa62f5e4e5">reflect bug</a>.
|
||||
</p>
|
||||
|
||||
<h2 id="r59">r59 (released 2011/08/01)</h2>
|
||||
@@ -213,7 +237,7 @@ This section highlights the most significant changes in this release.
|
||||
For a more detailed summary, see the
|
||||
<a href="weekly.html#2011-07-07">weekly release notes</a>.
|
||||
For complete information, see the
|
||||
<a href="http://code.google.com/p/go/source/list?r=release-branch.r59">Mercurial change list</a>.
|
||||
<a href="//code.google.com/p/go/source/list?r=release-branch.r59">Mercurial change list</a>.
|
||||
</p>
|
||||
|
||||
<h3 id="r59.lang">Language</h3>
|
||||
@@ -223,7 +247,7 @@ This release includes a language change that restricts the use of
|
||||
<code>goto</code>. In essence, a <code>goto</code> statement outside a block
|
||||
cannot jump to a label inside that block. Your code may require changes if it
|
||||
uses <code>goto</code>.
|
||||
See <a href="http://code.google.com/p/go/source/detail?r=dc6d3cf9279d">this
|
||||
See <a href="//golang.org/change/dc6d3cf9279d">this
|
||||
changeset</a> for how the new rule affected the Go tree.
|
||||
</p>
|
||||
|
||||
@@ -321,13 +345,13 @@ This section highlights the most significant changes in this release.
|
||||
For a more detailed summary, see the
|
||||
<a href="weekly.html#2011-06-09">weekly release notes</a>.
|
||||
For complete information, see the
|
||||
<a href="http://code.google.com/p/go/source/list?r=release-branch.r58">Mercurial change list</a>.
|
||||
<a href="//code.google.com/p/go/source/list?r=release-branch.r58">Mercurial change list</a>.
|
||||
</p>
|
||||
|
||||
<h3 id="r58.lang">Language</h3>
|
||||
|
||||
<p>
|
||||
This release fixes a <a href="http://code.google.com/p/go/source/detail?r=b720749486e1">use of uninitialized memory in programs that misuse <code>goto</code></a>.
|
||||
This release fixes a <a href="//golang.org/change/b720749486e1">use of uninitialized memory in programs that misuse <code>goto</code></a>.
|
||||
</p>
|
||||
|
||||
<h3 id="r58.pkg">Packages</h3>
|
||||
@@ -386,8 +410,8 @@ the Go tree (and avoid writing Makefiles).
|
||||
<h3 id="r58.minor">Minor revisions</h3>
|
||||
|
||||
<p>r58.1 adds
|
||||
<a href="http://code.google.com/p/go/source/detail?r=293c25943586">build</a> and
|
||||
<a href="http://code.google.com/p/go/source/detail?r=bf17e96b6582">runtime</a>
|
||||
<a href="//golang.org/change/293c25943586">build</a> and
|
||||
<a href="//golang.org/change/bf17e96b6582">runtime</a>
|
||||
changes to make Go run on OS X 10.7 Lion.
|
||||
</p>
|
||||
|
||||
@@ -401,7 +425,7 @@ This section highlights the most significant changes in this release.
|
||||
For a more detailed summary, see the
|
||||
<a href="weekly.html#2011-04-27">weekly release notes</a>.
|
||||
For complete information, see the
|
||||
<a href="http://code.google.com/p/go/source/list?r=release-branch.r57">Mercurial change list</a>.
|
||||
<a href="//code.google.com/p/go/source/list?r=release-branch.r57">Mercurial change list</a>.
|
||||
</p>
|
||||
|
||||
<p>The new <a href="/cmd/gofix">gofix</a> tool finds Go programs that use old APIs and rewrites them to use
|
||||
@@ -412,7 +436,7 @@ future changes to the libraries.
|
||||
Gofix can’t
|
||||
handle all situations perfectly, so read and test the changes it makes before
|
||||
committing them.
|
||||
See <a href="http://blog.golang.org/2011/04/introducing-gofix.html">the gofix blog post</a> for more
|
||||
See <a href="//blog.golang.org/2011/04/introducing-gofix.html">the gofix blog post</a> for more
|
||||
information.</p>
|
||||
|
||||
<h3 id="r57.lang">Language</h3>
|
||||
@@ -458,7 +482,7 @@ For clients, there are new
|
||||
<a href="/pkg/http/#Client">Client</a> and <a href="/pkg/http/#Transport">Transport</a>
|
||||
abstractions that give more control over HTTP details such as headers sent
|
||||
and redirections followed. These abstractions make it easy to implement
|
||||
custom clients that add functionality such as <a href="http://code.google.com/p/goauth2/source/browse/oauth/oauth.go">OAuth2</a>.
|
||||
custom clients that add functionality such as <a href="//code.google.com/p/goauth2/source/browse/oauth/oauth.go">OAuth2</a>.
|
||||
For servers, <a href="/pkg/http/#ResponseWriter">ResponseWriter</a>
|
||||
has dropped its non-essential methods.
|
||||
The Hijack and Flush methods are no longer required;
|
||||
@@ -502,7 +526,7 @@ implements all the possible value methods.
|
||||
Instead of a type switch on a Value <code>v</code>, switch on <code>v.Kind()</code>.
|
||||
Typeof and NewValue are now called <a href="/pkg/reflect/#Type.TypeOf">TypeOf</a> and <a href="/pkg/reflect/#Value.ValueOf">ValueOf</a>
|
||||
To create a writable Value, use <code>New(t).Elem()</code> instead of <code>Zero(t)</code>.
|
||||
See <a href="http://code.google.com/p/go/source/detail?r=843855f3c026">the change description</a>
|
||||
See <a href="//golang.org/change/843855f3c026">the change description</a>
|
||||
for the full details.
|
||||
The new API allows a more efficient implementation of Value
|
||||
that avoids many of the allocations required by the previous API.
|
||||
@@ -538,8 +562,8 @@ For other uses, see the <a href="/pkg/runtime/pprof/">runtime/pprof</a> document
|
||||
|
||||
<h3 id="r57.minor">Minor revisions</h3>
|
||||
|
||||
<p>r57.1 fixes a <a href="http://code.google.com/p/go/source/detail?r=ff2bc62726e7145eb2ecc1e0f076998e4a8f86f0">nil pointer dereference in http.FormFile</a>.</p>
|
||||
<p>r57.2 fixes a <a href="http://code.google.com/p/go/source/detail?r=063b0ff67d8277df03c956208abc068076818dae">use of uninitialized memory in programs that misuse <code>goto</code></a>.</p>
|
||||
<p>r57.1 fixes a <a href="//golang.org/change/ff2bc62726e7145eb2ecc1e0f076998e4a8f86f0">nil pointer dereference in http.FormFile</a>.</p>
|
||||
<p>r57.2 fixes a <a href="//golang.org/change/063b0ff67d8277df03c956208abc068076818dae">use of uninitialized memory in programs that misuse <code>goto</code></a>.</p>
|
||||
|
||||
<h2 id="r56">r56 (released 2011/03/16)</h2>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<p>This page summarizes the changes between tagged weekly snapshots of Go.
|
||||
Such snapshots are no longer created. This page remains as a historical reference only.</p>
|
||||
|
||||
<p>For recent information, see the <a href="http://code.google.com/p/go/source/list">Mercurial change log</a> and <a href="http://groups.google.com/group/golang-dev/">development mailing list</a>.</p>
|
||||
<p>For recent information, see the <a href="//code.google.com/p/go/source/list">Mercurial change log</a> and <a href="//groups.google.com/group/golang-dev/">development mailing list</a>.</p>
|
||||
|
||||
<h2 id="2012-03-27">2012-03-27 (<a href="release.html#go1">Go 1</a>)</h2>
|
||||
|
||||
|
||||
@@ -33,20 +33,20 @@ libraries.
|
||||
|
||||
<img class="gopher" src="/doc/gopher/doc.png"/>
|
||||
|
||||
<h3 id="go_tour"><a href="http://tour.golang.org/">A Tour of Go</a></h3>
|
||||
<h3 id="go_tour"><a href="//tour.golang.org/">A Tour of Go</a></h3>
|
||||
<p>
|
||||
An interactive introduction to Go in three sections.
|
||||
The first section covers basic syntax and data structures; the second discusses
|
||||
methods and interfaces; and the third introduces Go's concurrency primitives.
|
||||
Each section concludes with a few exercises so you can practice what you've
|
||||
learned. You can <a href="http://tour.golang.org/">take the tour online</a> or
|
||||
<a href="http://code.google.com/p/go-tour/">install it locally</a>.
|
||||
learned. You can <a href="//tour.golang.org/">take the tour online</a> or
|
||||
<a href="//code.google.com/p/go-tour/">install it locally</a>.
|
||||
</p>
|
||||
|
||||
<h3 id="code"><a href="code.html">How to write Go code</a></h3>
|
||||
<p>
|
||||
Also available as a
|
||||
<a href="http://www.youtube.com/watch?v=XCsL89YtqCs">screencast</a>, this doc
|
||||
<a href="//www.youtube.com/watch?v=XCsL89YtqCs">screencast</a>, this doc
|
||||
explains how to use the <a href="/cmd/go/">go command</a> to fetch, build, and
|
||||
install packages, commands, and run tests.
|
||||
</p>
|
||||
@@ -97,10 +97,13 @@ 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>
|
||||
|
||||
<h3 id="blog"><a href="http://blog.golang.org/">The Go Blog</a></h3>
|
||||
<h3 id="blog"><a href="//blog.golang.org/">The Go Blog</a></h3>
|
||||
<p>The official blog of the Go project, featuring news and in-depth articles by
|
||||
the Go team and guests.</p>
|
||||
|
||||
@@ -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>
|
||||
@@ -165,17 +169,17 @@ interfaces, reflection, and concurrency. Builds a toy web crawler to
|
||||
demonstrate these.
|
||||
</p>
|
||||
|
||||
<h3 id="go_code_that_grows"><a href="http://vimeo.com/53221560">Code that grows with grace</a></h3>
|
||||
<h3 id="go_code_that_grows"><a href="//vimeo.com/53221560">Code that grows with grace</a></h3>
|
||||
<p>
|
||||
One of Go's key design goals is code adaptability; that it should be easy to take a simple design and build upon it in a clean and natural way. In this talk Andrew Gerrand describes a simple "chat roulette" server that matches pairs of incoming TCP connections, and then use Go's concurrency mechanisms, interfaces, and standard library to extend it with a web interface and other features. While the function of the program changes dramatically, Go's flexibility preserves the original design as it grows.
|
||||
</p>
|
||||
|
||||
<h3 id="go_concurrency_patterns"><a href="http://www.youtube.com/watch?v=f6kdp27TYZs">Go Concurrency Patterns</a></h3>
|
||||
<h3 id="go_concurrency_patterns"><a href="//www.youtube.com/watch?v=f6kdp27TYZs">Go Concurrency Patterns</a></h3>
|
||||
<p>
|
||||
Concurrency is the key to designing high performance network services. Go's concurrency primitives (goroutines and channels) provide a simple and efficient means of expressing concurrent execution. In this talk we see how tricky concurrency problems can be solved gracefully with simple Go code.
|
||||
</p>
|
||||
|
||||
<h3 id="advanced_go_concurrency_patterns"><a href="http://www.youtube.com/watch?v=QDDwwePbDtw">Advanced Go Concurrency Patterns</a></h3>
|
||||
<h3 id="advanced_go_concurrency_patterns"><a href="//www.youtube.com/watch?v=QDDwwePbDtw">Advanced Go Concurrency Patterns</a></h3>
|
||||
<p>
|
||||
This talk expands on the <i>Go Concurrency Patterns</i> talk to dive deeper into Go's concurrency primitives.
|
||||
</p>
|
||||
|
||||
@@ -28,7 +28,7 @@ will be easy for other Go programmers to understand.
|
||||
<p>
|
||||
This document gives tips for writing clear, idiomatic Go code.
|
||||
It augments the <a href="/ref/spec">language specification</a>,
|
||||
the <a href="http://tour.golang.org/">Tour of Go</a>,
|
||||
the <a href="//tour.golang.org/">Tour of Go</a>,
|
||||
and <a href="/doc/code.html">How to Write Go Code</a>,
|
||||
all of which you
|
||||
should read first.
|
||||
@@ -43,8 +43,8 @@ only as the core library but also as examples of how to
|
||||
use the language.
|
||||
Moreover, many of the packages contain working, self-contained
|
||||
executable examples you can run directly from the
|
||||
<a href="http://golang.org">golang.org</a> web site, such as
|
||||
<a href="http://golang.org/pkg/strings/#example_Map">this one</a> (if
|
||||
<a href="//golang.org">golang.org</a> web site, such as
|
||||
<a href="//golang.org/pkg/strings/#example_Map">this one</a> (if
|
||||
necessary, click on the word "Example" to open it up).
|
||||
If you have a question about how to approach a problem or how something
|
||||
might be implemented, the documentation, code and examples in the
|
||||
@@ -3214,7 +3214,7 @@ Although the concurrency features of Go can make some problems easy
|
||||
to structure as parallel computations, Go is a concurrent language,
|
||||
not a parallel one, and not all parallelization problems fit Go's model.
|
||||
For a discussion of the distinction, see the talk cited in
|
||||
<a href="http://blog.golang.org/2013/01/concurrency-is-not-parallelism.html">this
|
||||
<a href="//blog.golang.org/2013/01/concurrency-is-not-parallelism.html">this
|
||||
blog post</a>.
|
||||
|
||||
<h3 id="leaky_buffer">A leaky buffer</h3>
|
||||
|
||||
@@ -30,7 +30,7 @@ contribution rules</a>.
|
||||
|
||||
<p>
|
||||
The master sources for the gccgo frontend may be found at
|
||||
<a href="http://code.google.com/p/gofrontend">http://code.google.com/p/gofrontend</a>.
|
||||
<a href="//code.google.com/p/gofrontend">http://code.google.com/p/gofrontend</a>.
|
||||
The master sources are not buildable by themselves, but only in
|
||||
conjunction with GCC (in the future, other compilers may be
|
||||
supported). Changes made to the gccgo frontend are also applied to
|
||||
@@ -40,7 +40,7 @@ is mirrored to the <code>gcc/go/gofrontend</code> directory in the GCC
|
||||
repository, and the <code>gofrontend</code> <code>libgo</code>
|
||||
directory is mirrored to the GCC <code>libgo</code> directory. In
|
||||
addition, the <code>test</code> directory
|
||||
from <a href="http://code.google.com/p/go">the main Go repository</a>
|
||||
from <a href="//code.google.com/p/go">the main Go repository</a>
|
||||
is mirrored to the <code>gcc/testsuite/go.test/test</code> directory
|
||||
in the GCC repository.
|
||||
</p>
|
||||
@@ -65,7 +65,7 @@ from <code>gcc/go/gofrontend</code> to <code>gcc/go</code>.
|
||||
|
||||
<p>
|
||||
The run-time library for gccgo is mostly the same as the library
|
||||
in <a href="http://code.google.com/p/go">the main Go repository</a>.
|
||||
in <a href="//code.google.com/p/go">the main Go repository</a>.
|
||||
The library code in the Go repository is periodically merged into
|
||||
the <code>libgo/go</code> directory of the <code>gofrontend</code> and
|
||||
then the GCC repositories, using the shell
|
||||
|
||||
@@ -304,7 +304,7 @@ The race detector is documented in <a href="/doc/articles/race_detector.html">a
|
||||
|
||||
<p>
|
||||
Due to the change of the <a href="#int"><code>int</code></a> to 64 bits and
|
||||
a new internal <a href="http://golang.org/s/go11func">representation of functions</a>,
|
||||
a new internal <a href="//golang.org/s/go11func">representation of functions</a>,
|
||||
the arrangement of function arguments on the stack has changed in the gc tool chain.
|
||||
Functions written in assembly will need to be revised at least
|
||||
to adjust frame pointer offsets.
|
||||
|
||||
@@ -63,7 +63,7 @@ The implementation may inject extra tests into the compiled program to enforce t
|
||||
|
||||
<p>
|
||||
Further details are in the
|
||||
<a href="http://golang.org/s/go12nil">design document</a>.
|
||||
<a href="//golang.org/s/go12nil">design document</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -115,7 +115,7 @@ It is possible that future releases of Go may introduce default values for these
|
||||
|
||||
<p>
|
||||
Further details are in the
|
||||
<a href="http://golang.org/s/go12slice">design document</a>.
|
||||
<a href="//golang.org/s/go12slice">design document</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -199,7 +199,7 @@ compiler to build any pieces of the linked-to library that are written in C++;
|
||||
<p>
|
||||
Both binaries are still included with the distribution, but the source code for the
|
||||
godoc and vet commands has moved to the
|
||||
<a href="http://code.google.com/p/go.tools">go.tools</a> subrepository.
|
||||
<a href="//code.google.com/p/go.tools">go.tools</a> subrepository.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -218,7 +218,7 @@ no client Go code depends on the their source and no updating is required.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The binary distributions available from <a href="http://golang.org">golang.org</a>
|
||||
The binary distributions available from <a href="//golang.org">golang.org</a>
|
||||
include these binaries, so users of these distributions are unaffected.
|
||||
</p>
|
||||
|
||||
@@ -448,7 +448,7 @@ The new interfaces are called
|
||||
and
|
||||
<a href="/pkg/encoding/#TextUnmarshaler"><code>TextUnmarshaler</code></a>.
|
||||
Full details are in the <a href="/pkg/encoding/">documentation</a> for the package
|
||||
and a separate <a href="http://golang.org/s/go12encoding">design document</a>.
|
||||
and a separate <a href="//golang.org/s/go12encoding">design document</a>.
|
||||
</p>
|
||||
|
||||
<h3 id="fmt_indexed_arguments">The fmt package</h3>
|
||||
|
||||
@@ -66,7 +66,7 @@ It runs on the 32-bit Intel architectures (<code>GOARCH=386</code>) and also on
|
||||
There is not yet support for Native Client on ARM.
|
||||
Note that this is Native Client (NaCl), not Portable Native Client (PNaCl).
|
||||
Details about Native Client are <a href="https://developers.google.com/native-client/dev/">here</a>;
|
||||
how to set up the Go version is described <a href="http://golang.org/wiki/NativeClient">here</a>.
|
||||
how to set up the Go version is described <a href="//golang.org/wiki/NativeClient">here</a>.
|
||||
</p>
|
||||
|
||||
<h3 id="netbsd">Support for NetBSD</h3>
|
||||
@@ -118,7 +118,7 @@ than is available, its stack is transferred to a larger single block of memory.
|
||||
The overhead of this transfer operation amortizes well and eliminates the old "hot spot"
|
||||
problem when a calculation repeatedly steps across a segment boundary.
|
||||
Details including performance numbers are in this
|
||||
<a href="http://golang.org/s/contigstacks">design document</a>.
|
||||
<a href="//golang.org/s/contigstacks">design document</a>.
|
||||
</p>
|
||||
|
||||
<h3 id="garbage_collector">Changes to the garbage collector</h3>
|
||||
@@ -141,7 +141,7 @@ Programs that use <a href="/pkg/unsafe/">package unsafe</a> to store pointers
|
||||
in integer-typed values are also illegal but more difficult to diagnose during execution.
|
||||
Because the pointers are hidden from the runtime, a stack expansion or garbage collection
|
||||
may reclaim the memory they point at, creating
|
||||
<a href="http://en.wikipedia.org/wiki/Dangling_pointer">dangling pointers</a>.
|
||||
<a href="//en.wikipedia.org/wiki/Dangling_pointer">dangling pointers</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -154,7 +154,7 @@ Such code can be identified by <code>go vet</code>.
|
||||
|
||||
<p>
|
||||
Iterations over small maps no longer happen in a consistent order.
|
||||
Go 1 defines that “<a href="http://golang.org/ref/spec#For_statements">The iteration order over maps
|
||||
Go 1 defines that “<a href="//golang.org/ref/spec#For_statements">The iteration order over maps
|
||||
is not specified and is not guaranteed to be the same from one iteration to the next.</a>”
|
||||
To keep code from depending on map iteration order,
|
||||
Go 1.0 started each map iteration at a random index in the map.
|
||||
@@ -175,7 +175,7 @@ Because only small maps are affected, the problem arises most often in tests.
|
||||
<h3 id="liblink">The linker</h3>
|
||||
|
||||
<p>
|
||||
As part of the general <a href="http://golang.org/s/go13linker">overhaul</a> to
|
||||
As part of the general <a href="//golang.org/s/go13linker">overhaul</a> to
|
||||
the Go linker, the compilers and linkers have been refactored.
|
||||
The linker is still a C program, but now the instruction selection phase that
|
||||
was part of the linker has been moved to the compiler through the creation of a new
|
||||
@@ -295,13 +295,30 @@ For example,
|
||||
(The same change was made to the compilers and linkers in <a href="/doc/go1.1#gc_flag">Go 1.1</a>.)
|
||||
</p>
|
||||
|
||||
<h3 id="godoc">Changes to godoc</h3>
|
||||
<p>
|
||||
When invoked with the <code>-analysis</code> flag,
|
||||
<a href="//godoc.org/code.google.com/p/go.tools/cmd/godoc">godoc</a>
|
||||
now performs sophisticated <a href="/lib/godoc/analysis/help.html">static
|
||||
analysis</a> of the code it indexes.
|
||||
The results of analysis are presented in both the source view and the
|
||||
package documentation view, and include the call graph of each package
|
||||
and the relationships between
|
||||
definitions and references,
|
||||
types and their methods,
|
||||
interfaces and their implementations,
|
||||
send and receive operations on channels,
|
||||
functions and their callers, and
|
||||
call sites and their callees.
|
||||
</p>
|
||||
|
||||
<h3 id="misc">Miscellany</h3>
|
||||
|
||||
<p>
|
||||
The program <code>misc/benchcmp</code> that compares
|
||||
performance across benchmarking runs has been rewritten.
|
||||
Once a shell and awk script in the main repository, it is now a Go program in the <code>go.tools</code> repo.
|
||||
Documentation is <a href="http://godoc.org/code.google.com/p/go.tools/cmd/benchcmp">here</a>.
|
||||
Documentation is <a href="//godoc.org/code.google.com/p/go.tools/cmd/benchcmp">here</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -309,7 +326,6 @@ For the few of us that build Go distributions, the tool <code>misc/dist</code> h
|
||||
moved and renamed; it now lives in <code>misc/makerelease</code>, still in the main repository.
|
||||
</p>
|
||||
|
||||
|
||||
<h2 id="performance">Performance</h2>
|
||||
|
||||
<p>
|
||||
@@ -505,6 +521,15 @@ field to specify an end-to-end timeout on requests made using the
|
||||
client.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
The <a href="/pkg/net/http/"><code>net/http</code></a> package's
|
||||
<a href="/pkg/net/http/#Request.ParseMultipartForm"><code>Request.ParseMultipartForm</code></a>
|
||||
method will now return an error if the body's <code>Content-Type</code>
|
||||
is not <code>mutipart/form-data</code>.
|
||||
Prior to Go 1.3 it would silently fail and return <code>nil</code>.
|
||||
Code that relies on the previous behavior should be updated.
|
||||
</li>
|
||||
|
||||
<li> In the <a href="/pkg/net/"><code>net</code></a> package,
|
||||
the <a href="/pkg/net/#Dialer"><code>Dialer</code></a> struct now
|
||||
has a <code>KeepAlive</code> option to specify a keep-alive period for the connection.
|
||||
@@ -539,6 +564,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
|
||||
|
||||
14
doc/go1.html
14
doc/go1.html
@@ -486,7 +486,7 @@ into subdirectories. For instance, <code>utf8</code> and
|
||||
<code>utf16</code> now occupy subdirectories of <code>unicode</code>.
|
||||
Also, <a href="#subrepo">some packages</a> have moved into
|
||||
subrepositories of
|
||||
<a href="http://code.google.com/p/go"><code>code.google.com/p/go</code></a>
|
||||
<a href="//code.google.com/p/go"><code>code.google.com/p/go</code></a>
|
||||
while <a href="#deleted">others</a> have been deleted outright.
|
||||
</p>
|
||||
|
||||
@@ -565,7 +565,7 @@ by hand.
|
||||
<p>
|
||||
Because they are not standardized, the packages under the <code>exp</code> directory will not be available in the
|
||||
standard Go 1 release distributions, although they will be available in source code form
|
||||
in <a href="http://code.google.com/p/go/">the repository</a> for
|
||||
in <a href="//code.google.com/p/go/">the repository</a> for
|
||||
developers who wish to use them.
|
||||
</p>
|
||||
|
||||
@@ -651,7 +651,7 @@ and also the command <code>gotry</code>.
|
||||
<em>Updating</em>:
|
||||
Code that uses <code>container/vector</code> should be updated to use
|
||||
slices directly. See
|
||||
<a href="http://code.google.com/p/go-wiki/wiki/SliceTricks">the Go
|
||||
<a href="//code.google.com/p/go-wiki/wiki/SliceTricks">the Go
|
||||
Language Community Wiki</a> for some suggestions.
|
||||
Code that uses the other packages (there should be almost zero) will need to be rethought.
|
||||
</p>
|
||||
@@ -660,7 +660,7 @@ Code that uses the other packages (there should be almost zero) will need to be
|
||||
|
||||
<p>
|
||||
Go 1 has moved a number of packages into other repositories, usually sub-repositories of
|
||||
<a href="http://code.google.com/p/go/">the main Go repository</a>.
|
||||
<a href="//code.google.com/p/go/">the main Go repository</a>.
|
||||
This table lists the old and new import paths:
|
||||
|
||||
<table class="codetable" frame="border" summary="Sub-repositories">
|
||||
@@ -1695,7 +1695,7 @@ The compiler will catch code using the old interface.
|
||||
The <a href="/pkg/regexp/"><code>regexp</code></a> package has been rewritten.
|
||||
It has the same interface but the specification of the regular expressions
|
||||
it supports has changed from the old "egrep" form to that of
|
||||
<a href="http://code.google.com/p/re2/">RE2</a>.
|
||||
<a href="//code.google.com/p/re2/">RE2</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -1912,7 +1912,7 @@ package <a href="/pkg/reflect/"><code>reflect</code></a>.
|
||||
<em>Updating</em>:
|
||||
Code using these functions must be rewritten to use
|
||||
package <a href="/pkg/reflect/"><code>reflect</code></a>.
|
||||
The changes to <a href="http://code.google.com/p/go/source/detail?r=2646dc956207">encoding/gob</a> and the <a href="http://code.google.com/p/goprotobuf/source/detail?r=5340ad310031">protocol buffer library</a>
|
||||
The changes to <a href="//golang.org/change/2646dc956207">encoding/gob</a> and the <a href="//code.google.com/p/goprotobuf/source/detail?r=5340ad310031">protocol buffer library</a>
|
||||
may be helpful as examples.
|
||||
</p>
|
||||
|
||||
@@ -2035,4 +2035,4 @@ They are available for many combinations of architecture and operating system
|
||||
Installation details are described on the
|
||||
<a href="/doc/install">Getting Started</a> page, while
|
||||
the distributions themselves are listed on the
|
||||
<a href="http://code.google.com/p/go/downloads/list">downloads page</a>.
|
||||
<a href="/dl/">downloads page</a>.
|
||||
|
||||
@@ -145,7 +145,7 @@ developed software based on Go 1.
|
||||
|
||||
<p>
|
||||
Code in sub-repositories of the main go tree, such as
|
||||
<a href="http://code.google.com/p/go.net">code.google.com/p/go.net</a>,
|
||||
<a href="//code.google.com/p/go.net">code.google.com/p/go.net</a>,
|
||||
may be developed under
|
||||
looser compatibility requirements. However, the sub-repositories
|
||||
will be tagged as appropriate to identify versions that are compatible
|
||||
|
||||
@@ -57,7 +57,7 @@ software on multicore machines.
|
||||
|
||||
<p>
|
||||
A much more expansive answer to this question is available in the article,
|
||||
<a href="http://talks.golang.org/2012/splash.article">Go at Google:
|
||||
<a href="//talks.golang.org/2012/splash.article">Go at Google:
|
||||
Language Design in the Service of Software Engineering</a>.
|
||||
|
||||
<h3 id="What_is_the_status_of_the_project">
|
||||
@@ -66,7 +66,7 @@ What is the status of the project?</h3>
|
||||
<p>
|
||||
Go became a public open source project on November 10, 2009.
|
||||
After a couple of years of very active design and development, stability was called for and
|
||||
Go 1 was <a href="http://blog.golang.org/2012/03/go-version-1-is-released.html">released</a>
|
||||
Go 1 was <a href="//blog.golang.org/2012/03/go-version-1-is-released.html">released</a>
|
||||
on March 28, 2012.
|
||||
Go 1, which includes a <a href="/ref/spec">language specification</a>,
|
||||
<a href="/pkg/">standard libraries</a>,
|
||||
@@ -163,7 +163,7 @@ language was called for.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The article <a href="http://talks.golang.org/2012/splash.article">Go at Google</a>
|
||||
The article <a href="//talks.golang.org/2012/splash.article">Go at Google</a>
|
||||
discusses the background and motivation behind the design of the Go language,
|
||||
as well as providing more detail about many of the answers presented in this FAQ.
|
||||
</p>
|
||||
@@ -221,7 +221,7 @@ easier to understand what happens when things combine.
|
||||
<p>
|
||||
Yes. There are now several Go programs deployed in
|
||||
production inside Google. A public example is the server behind
|
||||
<a href="http://golang.org">http://golang.org</a>.
|
||||
<a href="//golang.org">golang.org</a>.
|
||||
It's just the <a href="/cmd/godoc"><code>godoc</code></a>
|
||||
document server running in a production configuration on
|
||||
<a href="https://developers.google.com/appengine/">Google App Engine</a>.
|
||||
@@ -260,7 +260,7 @@ Does Go support Google's protocol buffers?</h3>
|
||||
<p>
|
||||
A separate open source project provides the necessary compiler plugin and library.
|
||||
It is available at
|
||||
<a href="http://code.google.com/p/goprotobuf/">http://code.google.com/p/goprotobuf/</a>
|
||||
<a href="//code.google.com/p/goprotobuf/">code.google.com/p/goprotobuf/</a>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -270,9 +270,9 @@ Can I translate the Go home page into another language?</h3>
|
||||
<p>
|
||||
Absolutely. We encourage developers to make Go Language sites in their own languages.
|
||||
However, if you choose to add the Google logo or branding to your site
|
||||
(it does not appear on <a href="http://golang.org/">golang.org</a>),
|
||||
(it does not appear on <a href="//golang.org/">golang.org</a>),
|
||||
you will need to abide by the guidelines at
|
||||
<a href="http://www.google.com/permissions/guidelines.html">http://www.google.com/permissions/guidelines.html</a>
|
||||
<a href="//www.google.com/permissions/guidelines.html">www.google.com/permissions/guidelines.html</a>
|
||||
</p>
|
||||
|
||||
<h2 id="Design">Design</h2>
|
||||
@@ -466,7 +466,7 @@ Will you accept my language change?</h3>
|
||||
|
||||
<p>
|
||||
People often suggest improvements to the language—the
|
||||
<a href="http://groups.google.com/group/golang-nuts">mailing list</a>
|
||||
<a href="//groups.google.com/group/golang-nuts">mailing list</a>
|
||||
contains a rich history of such discussions—but very few of these changes have
|
||||
been accepted.
|
||||
</p>
|
||||
@@ -484,7 +484,7 @@ to start talking about what that might be.
|
||||
<p>
|
||||
Even if your proposal is compatible with the Go 1 spec, it might
|
||||
not be in the spirit of Go's design goals.
|
||||
The article <i><a href="http://talks.golang.org/2012/splash.article">Go
|
||||
The article <i><a href="//talks.golang.org/2012/splash.article">Go
|
||||
at Google: Language Design in the Service of Software Engineering</a></i>
|
||||
explains Go's origins and the motivation behind its design.
|
||||
</p>
|
||||
@@ -940,9 +940,9 @@ How are libraries documented?</h3>
|
||||
There is a program, <code>godoc</code>, written in Go, that extracts
|
||||
package documentation from the source code. It can be used on the
|
||||
command line or on the web. An instance is running at
|
||||
<a href="/pkg/">http://golang.org/pkg/</a>.
|
||||
<a href="/pkg/">golang.org/pkg/</a>.
|
||||
In fact, <code>godoc</code> implements the full site at
|
||||
<a href="/">http://golang.org/</a>.
|
||||
<a href="/">golang.org/</a>.
|
||||
</p>
|
||||
|
||||
<h3 id="Is_there_a_Go_programming_style_guide">
|
||||
@@ -961,7 +961,7 @@ All the Go code in the repository has been run through <code>gofmt</code>.
|
||||
|
||||
<p>
|
||||
The document titled
|
||||
<a href="http://golang.org/s/comments">Go Code Review Comments</a>
|
||||
<a href="//golang.org/s/comments">Go Code Review Comments</a>
|
||||
is a collection of very short essays about details of Go idiom that are often
|
||||
missed by programmers.
|
||||
It is a handy reference for people doing code reviews for Go projects.
|
||||
@@ -986,14 +986,14 @@ Why does the project use Mercurial and not git?</h3>
|
||||
|
||||
<p>
|
||||
The Go project, hosted by Google Code at
|
||||
<a href="http://code.google.com/p/go">code.google.com/p/go</a>,
|
||||
<a href="//code.google.com/p/go">code.google.com/p/go</a>,
|
||||
uses Mercurial as its version control system.
|
||||
When the project launched,
|
||||
Google Code supported only Subversion and Mercurial.
|
||||
Mercurial was a better choice because of its plugin mechanism
|
||||
that allowed us to create the "codereview" plugin to connect
|
||||
the project to the excellent code review tools at
|
||||
<a href="http://codereview.appspot.com">codereview.appspot.com</a>.
|
||||
<a href="//codereview.appspot.com">codereview.appspot.com</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -1304,7 +1304,7 @@ Do not communicate by sharing memory. Instead, share memory by communicating.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
See the <a href="/doc/codewalk/sharemem/">Share Memory By Communicating</a> code walk and its <a href="http://blog.golang.org/2010/07/share-memory-by-communicating.html">associated article</a> for a detailed discussion of this concept.
|
||||
See the <a href="/doc/codewalk/sharemem/">Share Memory By Communicating</a> code walk and its <a href="//blog.golang.org/2010/07/share-memory-by-communicating.html">associated article</a> for a detailed discussion of this concept.
|
||||
</p>
|
||||
|
||||
<h3 id="Why_no_multi_CPU">
|
||||
@@ -1321,7 +1321,7 @@ run-time support to utilize more than one OS thread.
|
||||
Programs that perform parallel computation should benefit from an increase in
|
||||
<code>GOMAXPROCS</code>.
|
||||
However, be aware that
|
||||
<a href="http://blog.golang.org/2013/01/concurrency-is-not-parallelism.html">concurrency
|
||||
<a href="//blog.golang.org/2013/01/concurrency-is-not-parallelism.html">concurrency
|
||||
is not parallelism</a>.
|
||||
</p>
|
||||
|
||||
@@ -1358,7 +1358,7 @@ should recognize such cases and optimize its use of OS threads. For now,
|
||||
|
||||
<p>
|
||||
For more detail on this topic see the talk entitled,
|
||||
<a href="http://blog.golang.org/2013/01/concurrency-is-not-parallelism.html">Concurrency
|
||||
<a href="//blog.golang.org/2013/01/concurrency-is-not-parallelism.html">Concurrency
|
||||
is not Parallelism</a>.
|
||||
|
||||
<h2 id="Functions_methods">Functions and Methods</h2>
|
||||
@@ -1736,7 +1736,7 @@ In any case, Go can often be very competitive.
|
||||
There has been significant improvement in the performance of many programs
|
||||
as the language and tools have developed.
|
||||
See the blog post about
|
||||
<a href="http://blog.golang.org/2011/06/profiling-go-programs.html">profiling
|
||||
<a href="//blog.golang.org/2011/06/profiling-go-programs.html">profiling
|
||||
Go programs</a> for an informative example.
|
||||
|
||||
<h2 id="change_from_c">Changes from C</h2>
|
||||
@@ -1895,7 +1895,7 @@ considerable control over memory layout and allocation, much more than
|
||||
is typical in garbage-collected languages. A careful programmer can reduce
|
||||
the garbage collection overhead dramatically by using the language well;
|
||||
see the article about
|
||||
<a href="http://blog.golang.org/2011/06/profiling-go-programs.html">profiling
|
||||
<a href="//blog.golang.org/2011/06/profiling-go-programs.html">profiling
|
||||
Go programs</a> for a worked example, including a demonstration of Go's
|
||||
profiling tools.
|
||||
</p>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -23,7 +23,7 @@ TODO
|
||||
|
||||
<p>
|
||||
This is a reference manual for the Go programming language. For
|
||||
more information and other documents, see <a href="/">http://golang.org</a>.
|
||||
more information and other documents, see <a href="/">golang.org</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -20,11 +20,11 @@ Need help with Go? Try these resources.
|
||||
<h3 id="wiki"><a href="/wiki">The Go Wiki</a></h3>
|
||||
<p>A wiki maintained by the Go community.</p>
|
||||
|
||||
<h3 id="mailinglist"><a href="http://groups.google.com/group/golang-nuts">Go Nuts Mailing List</a></h3>
|
||||
<h3 id="mailinglist"><a href="//groups.google.com/group/golang-nuts">Go Nuts Mailing List</a></h3>
|
||||
<p>
|
||||
Search the <a href="http://groups.google.com/group/golang-nuts">golang-nuts</a>
|
||||
Search the <a href="//groups.google.com/group/golang-nuts">golang-nuts</a>
|
||||
archives and consult the <a href="/doc/go_faq.html">FAQ</a> and
|
||||
<a href="http://code.google.com/p/go-wiki/wiki">wiki</a> before posting.
|
||||
<a href="//code.google.com/p/go-wiki/wiki">wiki</a> before posting.
|
||||
</p>
|
||||
|
||||
<h3 id="irc"><a href="irc:irc.freenode.net/go-nuts">Go IRC Channel</a></h3>
|
||||
@@ -37,7 +37,7 @@ Go IRC channel.</p>
|
||||
<h3 id="plus"><a href="https://plus.google.com/101406623878176903605/posts">The Go Programming Language at Google+</a></h3>
|
||||
<p>The Go project's Google+ page.</p>
|
||||
|
||||
<h3 id="twitter"><a href="http://twitter.com/golang">@golang at Twitter</a></h3>
|
||||
<h3 id="twitter"><a href="//twitter.com/golang">@golang at Twitter</a></h3>
|
||||
<p>The Go project's official Twitter account.</p>
|
||||
<p>Tweeting about your problem with the <code>#golang</code> hashtag usually
|
||||
generates some helpful responses.</p>
|
||||
|
||||
@@ -81,7 +81,7 @@ The full set of supported combinations is listed in the discussion of
|
||||
|
||||
<p>
|
||||
The Go tool chain is written in C. To build it, you need a C compiler installed.
|
||||
Please refer to the <a href="http://code.google.com/p/go-wiki/wiki/InstallFromSource#Install_C_tools">InstallFromSource</a>
|
||||
Please refer to the <a href="//golang.org/wiki/InstallFromSource#Install_C_tools">InstallFromSource</a>
|
||||
page on the Go community Wiki for operating system specific instructions.
|
||||
</p>
|
||||
|
||||
@@ -283,16 +283,16 @@ You can access the latter commands with
|
||||
The usual community resources such as
|
||||
<code>#go-nuts</code> on the <a href="http://freenode.net/">Freenode</a> IRC server
|
||||
and the
|
||||
<a href="http://groups.google.com/group/golang-nuts">Go Nuts</a>
|
||||
<a href="//groups.google.com/group/golang-nuts">Go Nuts</a>
|
||||
mailing list have active developers that can help you with problems
|
||||
with your installation or your development work.
|
||||
For those who wish to keep up to date,
|
||||
there is another mailing list, <a href="http://groups.google.com/group/golang-checkins">golang-checkins</a>,
|
||||
there is another mailing list, <a href="//groups.google.com/group/golang-checkins">golang-checkins</a>,
|
||||
that receives a message summarizing each checkin to the Go repository.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Bugs can be reported using the <a href="http://code.google.com/p/go/issues/list">Go issue tracker</a>.
|
||||
Bugs can be reported using the <a href="//code.google.com/p/go/issues/list">Go issue tracker</a>.
|
||||
</p>
|
||||
|
||||
|
||||
@@ -306,7 +306,7 @@ The Go project maintains a stable tag in its Mercurial repository:
|
||||
<p>
|
||||
The <code>release</code> tag refers to the current stable release of Go.
|
||||
Most Go users should use this version. New releases are announced on the
|
||||
<a href="http://groups.google.com/group/golang-announce">golang-announce</a>
|
||||
<a href="//groups.google.com/group/golang-announce">golang-announce</a>
|
||||
mailing list.
|
||||
</p>
|
||||
|
||||
@@ -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
|
||||
@@ -482,8 +482,8 @@ should target. If you are compiling on the target system, its value will be auto
|
||||
<p>
|
||||
If in doubt, leave this variable unset, and adjust it if required
|
||||
when you first run the Go executable.
|
||||
The <a href="http://code.google.com/p/go-wiki/wiki/GoArm">GoARM</a> page
|
||||
on the <a href="http://code.google.com/p/go-wiki/w/list">Go community wiki</a>
|
||||
The <a href="//golang.org/wiki/GoArm">GoARM</a> page
|
||||
on the <a href="//golang.org/wiki">Go community wiki</a>
|
||||
contains further details regarding Go's ARM support.
|
||||
</p>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<h2 id="download">Download the Go distribution</h2>
|
||||
|
||||
<p>
|
||||
<a href="https://code.google.com/p/go/wiki/Downloads?tm=2" id="start" class="download" target="_blank">
|
||||
<a href="/dl/" id="start" class="download" target="_blank">
|
||||
<span class="big">Download Go</span>
|
||||
<span class="desc">Click here to visit the downloads page</span>
|
||||
</a>
|
||||
@@ -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>
|
||||
@@ -270,12 +270,12 @@ For real-time help, ask the helpful gophers in <code>#go-nuts</code> on the
|
||||
|
||||
<p>
|
||||
The official mailing list for discussion of the Go language is
|
||||
<a href="http://groups.google.com/group/golang-nuts">Go Nuts</a>.
|
||||
<a href="//groups.google.com/group/golang-nuts">Go Nuts</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Report bugs using the
|
||||
<a href="http://code.google.com/p/go/issues/list">Go issue tracker</a>.
|
||||
<a href="//golang.org/issue">Go issue tracker</a>.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ Hello, 世界
|
||||
<div class="buttons">
|
||||
<a class="run" href="#" title="Run this code [shift-enter]">Run</a>
|
||||
<a class="share" href="#" title="Share this code">Share</a>
|
||||
<a class="tour" href="http://tour.golang.org/" title="Learn Go from your browser">Tour</a>
|
||||
<a class="tour" href="//tour.golang.org/" title="Learn Go from your browser">Tour</a>
|
||||
</div>
|
||||
<div class="toys">
|
||||
<select>
|
||||
@@ -78,7 +78,7 @@ Linux, Mac OS X, Windows, and more.
|
||||
|
||||
<div id="blog">
|
||||
<div class="rootHeading">Featured articles</div>
|
||||
<div class="read"><a href="http://blog.golang.org/">Read more</a></div>
|
||||
<div class="read"><a href="//blog.golang.org/">Read more</a></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -124,7 +124,7 @@ window.initFuncs.push(function() {
|
||||
"outputEl": "#learn .output",
|
||||
"runEl": "#learn .run",
|
||||
"shareEl": "#learn .share",
|
||||
"shareRedirect": "http://play.golang.org/p/",
|
||||
"shareRedirect": "//play.golang.org/p/",
|
||||
"toysEl": "#learn .toys select"
|
||||
});
|
||||
} else {
|
||||
@@ -133,7 +133,7 @@ window.initFuncs.push(function() {
|
||||
|
||||
// Load blog feed.
|
||||
$('<script/>').attr('text', 'text/javascript')
|
||||
.attr('src', 'http://blog.golang.org/.json?jsonp=feedLoaded')
|
||||
.attr('src', '//blog.golang.org/.json?jsonp=feedLoaded')
|
||||
.appendTo('body');
|
||||
|
||||
// Set the video at random.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<p>
|
||||
The Go website (the "Website") is hosted by Google.
|
||||
By using and/or visiting the Website, you consent to be bound by Google's general
|
||||
<a href="http://www.google.com/intl/en/policies/terms/">Terms of Service</a>
|
||||
<a href="//www.google.com/intl/en/policies/terms/">Terms of Service</a>
|
||||
and Google's general
|
||||
<a href="http://www.google.com/intl/en/privacy/privacy-policy.html">Privacy Policy</a>.
|
||||
<a href="//www.google.com/intl/en/privacy/privacy-policy.html">Privacy Policy</a>.
|
||||
</p>
|
||||
|
||||
@@ -40,6 +40,7 @@ extern "C" {
|
||||
#endif
|
||||
#define _BSD_SOURCE 1
|
||||
#define _NETBSD_SOURCE 1 /* NetBSD */
|
||||
#define _DEFAULT_SOURCE 1 /* glibc > 2.19 */
|
||||
#define _SVID_SOURCE 1
|
||||
#if !defined(__APPLE__) && !defined(__OpenBSD__)
|
||||
# define _XOPEN_SOURCE 1000
|
||||
|
||||
@@ -53,5 +53,6 @@ func Test5986(t *testing.T) { test5986(t) }
|
||||
func Test7665(t *testing.T) { test7665(t) }
|
||||
func TestNaming(t *testing.T) { testNaming(t) }
|
||||
func Test7560(t *testing.T) { test7560(t) }
|
||||
func Test7978(t *testing.T) { test7978(t) }
|
||||
|
||||
func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) }
|
||||
|
||||
103
misc/cgo/test/issue7978.go
Normal file
103
misc/cgo/test/issue7978.go
Normal file
@@ -0,0 +1,103 @@
|
||||
// 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 7978. Stack tracing didn't work during cgo code after calling a Go
|
||||
// callback. Make sure GC works and the stack trace is correct.
|
||||
|
||||
package cgotest
|
||||
|
||||
/*
|
||||
#include <stdint.h>
|
||||
|
||||
void issue7978cb(void);
|
||||
|
||||
// use ugly atomic variable sync since that doesn't require calling back into
|
||||
// Go code or OS dependencies
|
||||
static void issue7978c(uint32_t *sync) {
|
||||
while(__sync_fetch_and_add(sync, 0) != 0)
|
||||
;
|
||||
__sync_fetch_and_add(sync, 1);
|
||||
while(__sync_fetch_and_add(sync, 0) != 2)
|
||||
;
|
||||
issue7978cb();
|
||||
__sync_fetch_and_add(sync, 1);
|
||||
while(__sync_fetch_and_add(sync, 0) != 6)
|
||||
;
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var issue7978sync uint32
|
||||
|
||||
func issue7978check(t *testing.T, wantFunc string, badFunc string, depth int) {
|
||||
runtime.GC()
|
||||
buf := make([]byte, 65536)
|
||||
trace := string(buf[:runtime.Stack(buf, true)])
|
||||
for _, goroutine := range strings.Split(trace, "\n\n") {
|
||||
if strings.Contains(goroutine, "test.issue7978go") {
|
||||
trace := strings.Split(goroutine, "\n")
|
||||
// look for the expected function in the stack
|
||||
for i := 0; i < depth; i++ {
|
||||
if badFunc != "" && strings.Contains(trace[1+2*i], badFunc) {
|
||||
t.Errorf("bad stack: found %s in the stack:\n%s", badFunc, goroutine)
|
||||
return
|
||||
}
|
||||
if strings.Contains(trace[1+2*i], wantFunc) {
|
||||
return
|
||||
}
|
||||
}
|
||||
t.Errorf("bad stack: didn't find %s in the stack:\n%s", wantFunc, goroutine)
|
||||
return
|
||||
}
|
||||
}
|
||||
t.Errorf("bad stack: goroutine not found. Full stack dump:\n%s", trace)
|
||||
}
|
||||
|
||||
func issue7978wait(store uint32, wait uint32) {
|
||||
if store != 0 {
|
||||
atomic.StoreUint32(&issue7978sync, store)
|
||||
}
|
||||
for atomic.LoadUint32(&issue7978sync) != wait {
|
||||
runtime.Gosched()
|
||||
}
|
||||
}
|
||||
|
||||
//export issue7978cb
|
||||
func issue7978cb() {
|
||||
issue7978wait(3, 4)
|
||||
}
|
||||
|
||||
func issue7978go() {
|
||||
C.issue7978c((*C.uint32_t)(&issue7978sync))
|
||||
issue7978wait(7, 8)
|
||||
}
|
||||
|
||||
func test7978(t *testing.T) {
|
||||
if os.Getenv("GOTRACEBACK") != "2" {
|
||||
t.Fatal("GOTRACEBACK must be 2")
|
||||
}
|
||||
issue7978sync = 0
|
||||
go issue7978go()
|
||||
// test in c code, before callback
|
||||
issue7978wait(0, 1)
|
||||
issue7978check(t, "runtime.cgocall(", "", 1)
|
||||
// test in go code, during callback
|
||||
issue7978wait(2, 3)
|
||||
issue7978check(t, "test.issue7978cb(", "test.issue7978go", 4)
|
||||
// test in c code, after callback
|
||||
issue7978wait(4, 5)
|
||||
issue7978check(t, "runtime.cgocall(", "runtime.cgocallback", 1)
|
||||
// test in go code, after return from cgo
|
||||
issue7978wait(6, 7)
|
||||
issue7978check(t, "test.issue7978go(", "", 4)
|
||||
atomic.StoreUint32(&issue7978sync, 8)
|
||||
}
|
||||
31
misc/cgo/test/issue8148.go
Normal file
31
misc/cgo/test/issue8148.go
Normal 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())
|
||||
}
|
||||
@@ -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;
|
||||
15
misc/cgo/test/issue8331a.go
Normal file
15
misc/cgo/test/issue8331a.go
Normal 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
|
||||
}
|
||||
13
misc/cgo/test/issue8331b.go
Normal file
13
misc/cgo/test/issue8331b.go
Normal 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
|
||||
27
misc/cgo/test/issue8441.go
Normal file
27
misc/cgo/test/issue8441.go
Normal file
@@ -0,0 +1,27 @@
|
||||
// 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 8368 and 8441. Recursive struct definitions didn't work.
|
||||
// No runtime test; just make sure it compiles.
|
||||
|
||||
package cgotest
|
||||
|
||||
/*
|
||||
typedef struct one one;
|
||||
typedef struct two two;
|
||||
struct one {
|
||||
two *x;
|
||||
};
|
||||
struct two {
|
||||
one *x;
|
||||
};
|
||||
*/
|
||||
import "C"
|
||||
|
||||
func issue8368(one *C.struct_one, two *C.struct_two) {
|
||||
}
|
||||
|
||||
func issue8441(one *C.one, two *C.two) {
|
||||
issue8441(two.x, one.x)
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -13,9 +13,6 @@ go src=..
|
||||
gofmt
|
||||
testdata
|
||||
+
|
||||
link
|
||||
testdata
|
||||
+
|
||||
pkg
|
||||
archive
|
||||
tar
|
||||
|
||||
@@ -599,10 +599,10 @@ asmb(void)
|
||||
if(iself)
|
||||
goto ElfSym;
|
||||
case Hplan9:
|
||||
symo = HEADR+segtext.len+segdata.filelen;
|
||||
symo = segdata.fileoff+segdata.filelen;
|
||||
break;
|
||||
ElfSym:
|
||||
symo = rnd(HEADR+segtext.filelen, INITRND)+rnd(HEADR+segrodata.filelen, INITRND)+segdata.filelen;
|
||||
symo = segdata.fileoff+segdata.filelen;
|
||||
symo = rnd(symo, INITRND);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -943,7 +943,7 @@ cgen_hmul(Node *nl, Node *nr, Node *res)
|
||||
if(t->width == 1) {
|
||||
// byte multiply behaves differently.
|
||||
nodreg(&ax, t, D_AH);
|
||||
nodreg(&dx, t, D_DL);
|
||||
nodreg(&dx, t, D_DX);
|
||||
gmove(&ax, &dx);
|
||||
}
|
||||
nodreg(&dx, t, D_DX);
|
||||
|
||||
@@ -838,6 +838,11 @@ copyu(Prog *p, Adr *v, Adr *s)
|
||||
static int
|
||||
copyas(Adr *a, Adr *v)
|
||||
{
|
||||
if(D_AL <= a->type && a->type <= D_R15B)
|
||||
fatal("use of byte register");
|
||||
if(D_AL <= v->type && v->type <= D_R15B)
|
||||
fatal("use of byte register");
|
||||
|
||||
if(a->type != v->type)
|
||||
return 0;
|
||||
if(regtyp(v))
|
||||
|
||||
@@ -689,10 +689,10 @@ asmb(void)
|
||||
case Hplan9:
|
||||
case Helf:
|
||||
debug['s'] = 1;
|
||||
symo = HEADR+segtext.len+segdata.filelen;
|
||||
symo = segdata.fileoff+segdata.filelen;
|
||||
break;
|
||||
case Hdarwin:
|
||||
symo = rnd(HEADR+segtext.len, INITRND)+rnd(segdata.filelen, INITRND)+machlink;
|
||||
symo = segdata.fileoff+rnd(segdata.filelen, INITRND)+machlink;
|
||||
break;
|
||||
case Hlinux:
|
||||
case Hfreebsd:
|
||||
@@ -701,11 +701,11 @@ asmb(void)
|
||||
case Hdragonfly:
|
||||
case Hsolaris:
|
||||
case Hnacl:
|
||||
symo = rnd(HEADR+segtext.len, INITRND)+rnd(segrodata.len, INITRND)+segdata.filelen;
|
||||
symo = segdata.fileoff+segdata.filelen;
|
||||
symo = rnd(symo, INITRND);
|
||||
break;
|
||||
case Hwindows:
|
||||
symo = rnd(HEADR+segtext.filelen, PEFILEALIGN)+segdata.filelen;
|
||||
symo = segdata.fileoff+segdata.filelen;
|
||||
symo = rnd(symo, PEFILEALIGN);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -991,7 +991,7 @@ cgen_hmul(Node *nl, Node *nr, Node *res)
|
||||
if(t->width == 1) {
|
||||
// byte multiply behaves differently.
|
||||
nodreg(&ax, t, D_AH);
|
||||
nodreg(&dx, t, D_DL);
|
||||
nodreg(&dx, t, D_DX);
|
||||
gmove(&ax, &dx);
|
||||
}
|
||||
nodreg(&dx, t, D_DX);
|
||||
|
||||
@@ -634,6 +634,11 @@ copyu(Prog *p, Adr *v, Adr *s)
|
||||
static int
|
||||
copyas(Adr *a, Adr *v)
|
||||
{
|
||||
if(D_AL <= a->type && a->type <= D_BL)
|
||||
fatal("use of byte register");
|
||||
if(D_AL <= v->type && v->type <= D_BL)
|
||||
fatal("use of byte register");
|
||||
|
||||
if(a->type != v->type)
|
||||
return 0;
|
||||
if(regtyp(v))
|
||||
|
||||
@@ -619,17 +619,17 @@ asmb(void)
|
||||
if(iself)
|
||||
goto Elfsym;
|
||||
case Hplan9:
|
||||
symo = HEADR+segtext.filelen+segdata.filelen;
|
||||
symo = segdata.fileoff+segdata.filelen;
|
||||
break;
|
||||
case Hdarwin:
|
||||
symo = rnd(HEADR+segtext.filelen, INITRND)+rnd(segdata.filelen, INITRND)+machlink;
|
||||
symo = segdata.fileoff+rnd(segdata.filelen, INITRND)+machlink;
|
||||
break;
|
||||
Elfsym:
|
||||
symo = rnd(HEADR+segtext.filelen, INITRND)+rnd(HEADR+segrodata.filelen, INITRND)+segdata.filelen;
|
||||
symo = segdata.fileoff+segdata.filelen;
|
||||
symo = rnd(symo, INITRND);
|
||||
break;
|
||||
case Hwindows:
|
||||
symo = rnd(HEADR+segtext.filelen, PEFILEALIGN)+segdata.filelen;
|
||||
symo = segdata.fileoff+segdata.filelen;
|
||||
symo = rnd(symo, PEFILEALIGN);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -552,8 +552,8 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
|
||||
n.Const = fmt.Sprintf("%#x", enumVal[i])
|
||||
}
|
||||
}
|
||||
conv.FinishType(pos)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// mangleName does name mangling to translate names
|
||||
@@ -926,6 +926,12 @@ type typeConv struct {
|
||||
m map[dwarf.Type]*Type
|
||||
typedef map[string]ast.Expr
|
||||
|
||||
// Map from types to incomplete pointers to those types.
|
||||
ptrs map[dwarf.Type][]*Type
|
||||
|
||||
// Fields to be processed by godefsField after completing pointers.
|
||||
todoFlds [][]*ast.Field
|
||||
|
||||
// Predeclared types.
|
||||
bool ast.Expr
|
||||
byte ast.Expr // denotes padding
|
||||
@@ -950,6 +956,7 @@ func (c *typeConv) Init(ptrSize, intSize int64) {
|
||||
c.ptrSize = ptrSize
|
||||
c.intSize = intSize
|
||||
c.m = make(map[dwarf.Type]*Type)
|
||||
c.ptrs = make(map[dwarf.Type][]*Type)
|
||||
c.bool = c.Ident("bool")
|
||||
c.byte = c.Ident("byte")
|
||||
c.int8 = c.Ident("int8")
|
||||
@@ -1029,6 +1036,32 @@ func (tr *TypeRepr) Set(repr string, fargs ...interface{}) {
|
||||
tr.FormatArgs = fargs
|
||||
}
|
||||
|
||||
// FinishType completes any outstanding type mapping work.
|
||||
// In particular, it resolves incomplete pointer types and also runs
|
||||
// godefsFields on any new struct types.
|
||||
func (c *typeConv) FinishType(pos token.Pos) {
|
||||
// Completing one pointer type might produce more to complete.
|
||||
// Keep looping until they're all done.
|
||||
for len(c.ptrs) > 0 {
|
||||
for dtype := range c.ptrs {
|
||||
// Note Type might invalidate c.ptrs[dtype].
|
||||
t := c.Type(dtype, pos)
|
||||
for _, ptr := range c.ptrs[dtype] {
|
||||
ptr.Go.(*ast.StarExpr).X = t.Go
|
||||
ptr.C.Set("%s*", t.C)
|
||||
}
|
||||
delete(c.ptrs, dtype)
|
||||
}
|
||||
}
|
||||
|
||||
// Now that pointer types are completed, we can invoke godefsFields
|
||||
// to rewrite struct definitions.
|
||||
for _, fld := range c.todoFlds {
|
||||
godefsFields(fld)
|
||||
}
|
||||
c.todoFlds = nil
|
||||
}
|
||||
|
||||
// Type returns a *Type with the same memory layout as
|
||||
// dtype when used as the type of a variable or a struct field.
|
||||
func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
|
||||
@@ -1068,13 +1101,12 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
|
||||
t.Go = c.Opaque(t.Size)
|
||||
break
|
||||
}
|
||||
gt := &ast.ArrayType{
|
||||
Len: c.intExpr(dt.Count),
|
||||
}
|
||||
t.Go = gt // publish before recursive call
|
||||
sub := c.Type(dt.Type, pos)
|
||||
t.Align = sub.Align
|
||||
gt.Elt = sub.Go
|
||||
t.Go = &ast.ArrayType{
|
||||
Len: c.intExpr(dt.Count),
|
||||
Elt: sub.Go,
|
||||
}
|
||||
t.C.Set("__typeof__(%s[%d])", sub.C, dt.Count)
|
||||
|
||||
case *dwarf.BoolType:
|
||||
@@ -1184,11 +1216,10 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
|
||||
break
|
||||
}
|
||||
|
||||
gt := &ast.StarExpr{}
|
||||
t.Go = gt // publish before recursive call
|
||||
sub := c.Type(dt.Type, pos)
|
||||
gt.X = sub.Go
|
||||
t.C.Set("%s*", sub.C)
|
||||
// Placeholder initialization; completed in FinishType.
|
||||
t.Go = &ast.StarExpr{}
|
||||
t.C.Set("<incomplete>*")
|
||||
c.ptrs[dt.Type] = append(c.ptrs[dt.Type], t)
|
||||
|
||||
case *dwarf.QualType:
|
||||
// Ignore qualifier.
|
||||
@@ -1265,11 +1296,12 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
|
||||
}
|
||||
name := c.Ident("_Ctype_" + dt.Name)
|
||||
goIdent[name.Name] = name
|
||||
t.Go = name // publish before recursive call
|
||||
sub := c.Type(dt.Type, pos)
|
||||
t.Go = name
|
||||
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 +1313,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:
|
||||
@@ -1589,7 +1635,7 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct
|
||||
csyntax = buf.String()
|
||||
|
||||
if *godefs || *cdefs {
|
||||
godefsFields(fld)
|
||||
c.todoFlds = append(c.todoFlds, fld)
|
||||
}
|
||||
expr = &ast.StructType{Fields: &ast.FieldList{List: fld}}
|
||||
return
|
||||
|
||||
@@ -108,20 +108,6 @@ arrayadd(Array *array, void *element)
|
||||
arrayset(array, array->length - 1, element);
|
||||
}
|
||||
|
||||
int32
|
||||
arrayindexof(Array *array, void *element)
|
||||
{
|
||||
void *p;
|
||||
int32 i;
|
||||
|
||||
for(i = 0; i < array->length; i++) {
|
||||
p = arrayget(array, i);
|
||||
if(memcmp(p, &element, array->size) == 0)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
arraysort(Array *array, int (*cmp)(const void*, const void*))
|
||||
{
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
enum {
|
||||
WORDSIZE = sizeof(uint32),
|
||||
WORDBITS = 32,
|
||||
WORDMASK = WORDBITS - 1,
|
||||
WORDSHIFT = 5,
|
||||
};
|
||||
|
||||
static uintptr
|
||||
@@ -94,13 +96,35 @@ bvconcat(Bvec *src1, Bvec *src2)
|
||||
int
|
||||
bvget(Bvec *bv, int32 i)
|
||||
{
|
||||
uint32 mask, word;
|
||||
|
||||
if(i < 0 || i >= bv->n)
|
||||
fatal("bvget: index %d is out of bounds with length %d\n", i, bv->n);
|
||||
mask = 1U << (i % WORDBITS);
|
||||
word = bv->b[i / WORDBITS] & mask;
|
||||
return word ? 1 : 0;
|
||||
return (bv->b[i>>WORDSHIFT] >> (i&WORDMASK)) & 1;
|
||||
}
|
||||
|
||||
// bvnext returns the smallest index >= i for which bvget(bv, i) == 1.
|
||||
// If there is no such index, bvnext returns -1.
|
||||
int
|
||||
bvnext(Bvec *bv, int32 i)
|
||||
{
|
||||
uint32 w;
|
||||
|
||||
// Jump i ahead to next word with bits.
|
||||
if((bv->b[i>>WORDSHIFT]>>(i&WORDMASK)) == 0) {
|
||||
i &= ~WORDMASK;
|
||||
i += WORDBITS;
|
||||
while(i < bv->n && bv->b[i>>WORDSHIFT] == 0)
|
||||
i += WORDBITS;
|
||||
}
|
||||
if(i >= bv->n)
|
||||
return -1;
|
||||
|
||||
// Find 1 bit.
|
||||
w = bv->b[i>>WORDSHIFT]>>(i&WORDMASK);
|
||||
while((w&1) == 0) {
|
||||
w>>=1;
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int
|
||||
@@ -109,7 +133,7 @@ bvisempty(Bvec *bv)
|
||||
int32 i;
|
||||
|
||||
for(i = 0; i < bv->n; i += WORDBITS)
|
||||
if(bv->b[i / WORDBITS] != 0)
|
||||
if(bv->b[i>>WORDSHIFT] != 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
@@ -1015,7 +1017,6 @@ int32 arraylength(Array *array);
|
||||
void* arrayget(Array *array, int32 index);
|
||||
void arrayset(Array *array, int32 index, void *element);
|
||||
void arrayadd(Array *array, void *element);
|
||||
int32 arrayindexof(Array* array, void *element);
|
||||
void arraysort(Array* array, int (*cmp)(const void*, const void*));
|
||||
|
||||
/*
|
||||
@@ -1041,6 +1042,7 @@ int bvcmp(Bvec *bv1, Bvec *bv2);
|
||||
void bvcopy(Bvec *dst, Bvec *src);
|
||||
Bvec* bvconcat(Bvec *src1, Bvec *src2);
|
||||
int bvget(Bvec *bv, int32 i);
|
||||
int32 bvnext(Bvec *bv, int32 i);
|
||||
int bvisempty(Bvec *bv);
|
||||
void bvnot(Bvec *bv);
|
||||
void bvor(Bvec *dst, Bvec *src1, Bvec *src2);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -283,13 +283,30 @@ getvariables(Node *fn)
|
||||
// For arguments and results, the bitmap covers all variables,
|
||||
// so we must include all the variables, even the ones without
|
||||
// pointers.
|
||||
//
|
||||
// The Node.opt field is available for use by optimization passes.
|
||||
// We use it to hold the index of the node in the variables array, plus 1
|
||||
// (so that 0 means the Node is not in the variables array).
|
||||
// Each pass should clear opt when done, but you never know,
|
||||
// so clear them all ourselves too.
|
||||
// The Node.curfn field is supposed to be set to the current function
|
||||
// already, but for some compiler-introduced names it seems not to be,
|
||||
// so fix that here.
|
||||
// Later, when we want to find the index of a node in the variables list,
|
||||
// we will check that n->curfn == curfn and n->opt > 0. Then n->opt - 1
|
||||
// is the index in the variables list.
|
||||
ll->n->opt = nil;
|
||||
ll->n->curfn = curfn;
|
||||
switch(ll->n->class) {
|
||||
case PAUTO:
|
||||
if(haspointers(ll->n->type))
|
||||
if(haspointers(ll->n->type)) {
|
||||
ll->n->opt = (void*)(uintptr)(arraylength(result)+1);
|
||||
arrayadd(result, &ll->n);
|
||||
}
|
||||
break;
|
||||
case PPARAM:
|
||||
case PPARAMOUT:
|
||||
ll->n->opt = (void*)(uintptr)(arraylength(result)+1);
|
||||
arrayadd(result, &ll->n);
|
||||
break;
|
||||
}
|
||||
@@ -718,14 +735,16 @@ progeffects(Prog *prog, Array *vars, Bvec *uevar, Bvec *varkill, Bvec *avarinit)
|
||||
}
|
||||
if(info.flags & (LeftRead | LeftWrite | LeftAddr)) {
|
||||
from = &prog->from;
|
||||
if (from->node != nil && from->sym != nil) {
|
||||
if (from->node != nil && from->sym != nil && from->node->curfn == curfn) {
|
||||
switch(from->node->class & ~PHEAP) {
|
||||
case PAUTO:
|
||||
case PPARAM:
|
||||
case PPARAMOUT:
|
||||
pos = arrayindexof(vars, from->node);
|
||||
pos = (int)(uintptr)from->node->opt - 1; // index in vars
|
||||
if(pos == -1)
|
||||
goto Next;
|
||||
if(pos >= arraylength(vars) || *(Node**)arrayget(vars, pos) != from->node)
|
||||
fatal("bad bookkeeping in liveness %N %d", from->node, pos);
|
||||
if(from->node->addrtaken) {
|
||||
bvset(avarinit, pos);
|
||||
} else {
|
||||
@@ -741,14 +760,16 @@ progeffects(Prog *prog, Array *vars, Bvec *uevar, Bvec *varkill, Bvec *avarinit)
|
||||
Next:
|
||||
if(info.flags & (RightRead | RightWrite | RightAddr)) {
|
||||
to = &prog->to;
|
||||
if (to->node != nil && to->sym != nil) {
|
||||
if (to->node != nil && to->sym != nil && to->node->curfn == curfn) {
|
||||
switch(to->node->class & ~PHEAP) {
|
||||
case PAUTO:
|
||||
case PPARAM:
|
||||
case PPARAMOUT:
|
||||
pos = arrayindexof(vars, to->node);
|
||||
pos = (int)(uintptr)to->node->opt - 1; // index in vars
|
||||
if(pos == -1)
|
||||
goto Next1;
|
||||
if(pos >= arraylength(vars) || *(Node**)arrayget(vars, pos) != to->node)
|
||||
fatal("bad bookkeeping in liveness %N %d", to->node, pos);
|
||||
if(to->node->addrtaken) {
|
||||
if(prog->as != AVARKILL)
|
||||
bvset(avarinit, pos);
|
||||
@@ -1020,6 +1041,9 @@ checkptxt(Node *fn, Prog *firstp)
|
||||
{
|
||||
Prog *p;
|
||||
|
||||
if(debuglive == 0)
|
||||
return;
|
||||
|
||||
for(p = firstp; p != P; p = p->link) {
|
||||
if(0)
|
||||
print("analyzing '%P'\n", p);
|
||||
@@ -1172,21 +1196,17 @@ twobitlivepointermap(Liveness *lv, Bvec *liveout, Array *vars, Bvec *args, Bvec
|
||||
vlong xoffset;
|
||||
int32 i;
|
||||
|
||||
for(i = 0; i < arraylength(vars); i++) {
|
||||
for(i = 0; (i = bvnext(liveout, i)) >= 0; i++) {
|
||||
node = *(Node**)arrayget(vars, i);
|
||||
switch(node->class) {
|
||||
case PAUTO:
|
||||
if(bvget(liveout, i)) {
|
||||
xoffset = node->xoffset + stkptrsize;
|
||||
twobitwalktype1(node->type, &xoffset, locals);
|
||||
}
|
||||
xoffset = node->xoffset + stkptrsize;
|
||||
twobitwalktype1(node->type, &xoffset, locals);
|
||||
break;
|
||||
case PPARAM:
|
||||
case PPARAMOUT:
|
||||
if(bvget(liveout, i)) {
|
||||
xoffset = node->xoffset;
|
||||
twobitwalktype1(node->type, &xoffset, args);
|
||||
}
|
||||
xoffset = node->xoffset;
|
||||
twobitwalktype1(node->type, &xoffset, args);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1937,6 +1957,7 @@ liveness(Node *fn, Prog *firstp, Sym *argssym, Sym *livesym)
|
||||
Array *cfg, *vars;
|
||||
Liveness *lv;
|
||||
int debugdelta;
|
||||
NodeList *l;
|
||||
|
||||
// Change name to dump debugging information only for a specific function.
|
||||
debugdelta = 0;
|
||||
@@ -1977,6 +1998,9 @@ liveness(Node *fn, Prog *firstp, Sym *argssym, Sym *livesym)
|
||||
twobitwritesymbol(lv->argslivepointers, argssym);
|
||||
|
||||
// Free everything.
|
||||
for(l=fn->dcl; l != nil; l = l->next)
|
||||
if(l->n != N)
|
||||
l->n->opt = nil;
|
||||
freeliveness(lv);
|
||||
arrayfree(vars);
|
||||
freecfg(cfg);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Generation of debug data structures (in the executable but not mapped at run time).
|
||||
// See also runtime.go.
|
||||
|
||||
package main
|
||||
|
||||
func (p *Prog) debug() {
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// mustParseHexdumpFile returns a block of data generated by
|
||||
// parsing the hex dump in the named file.
|
||||
// If the file cannot be read or does not contain a valid hex dump,
|
||||
// mustParseHexdumpFile calls t.Fatal.
|
||||
func mustParseHexdumpFile(t *testing.T, file string) []byte {
|
||||
hex, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data, err := parseHexdump(string(hex))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// parseHexdump parses the hex dump in text, which should be the
|
||||
// output of "hexdump -C" or Plan 9's "xd -b",
|
||||
// and returns the original data used to produce the dump.
|
||||
// It is meant to enable storing golden binary files as text, so that
|
||||
// changes to the golden files can be seen during code reviews.
|
||||
func parseHexdump(text string) ([]byte, error) {
|
||||
var out []byte
|
||||
for _, line := range strings.Split(text, "\n") {
|
||||
if i := strings.Index(line, "|"); i >= 0 { // remove text dump
|
||||
line = line[:i]
|
||||
}
|
||||
f := strings.Fields(line)
|
||||
if len(f) > 1+16 {
|
||||
return nil, fmt.Errorf("parsing hex dump: too many fields on line %q", line)
|
||||
}
|
||||
if len(f) == 0 || len(f) == 1 && f[0] == "*" { // all zeros block omitted
|
||||
continue
|
||||
}
|
||||
addr64, err := strconv.ParseUint(f[0], 16, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing hex dump: invalid address %q", f[0])
|
||||
}
|
||||
addr := int(addr64)
|
||||
if len(out) < addr {
|
||||
out = append(out, make([]byte, addr-len(out))...)
|
||||
}
|
||||
for _, x := range f[1:] {
|
||||
val, err := strconv.ParseUint(x, 16, 8)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing hexdump: invalid hex byte %q", x)
|
||||
}
|
||||
out = append(out, byte(val))
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func hexdump(data []byte) string {
|
||||
text := hex.Dump(data) + fmt.Sprintf("%08x\n", len(data))
|
||||
text = regexp.MustCompile(`\n([0-9a-f]+(\s+00){16}.*\n)+`).ReplaceAllString(text, "\n*\n")
|
||||
return text
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Executable image layout - address assignment.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"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)
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLayout(t *testing.T) {
|
||||
p := Prog{GOOS: "darwin", GOARCH: "amd64", StartSym: "text_start"}
|
||||
p.omitRuntime = true
|
||||
p.Error = func(s string) { t.Error(s) }
|
||||
var buf bytes.Buffer
|
||||
const obj = "testdata/layout.6"
|
||||
p.link(&buf, obj)
|
||||
if p.NumError > 0 {
|
||||
return // already reported
|
||||
}
|
||||
if len(p.Dead) > 0 {
|
||||
t.Errorf("%s: unexpected dead symbols %v", obj, p.Dead)
|
||||
return
|
||||
}
|
||||
|
||||
for _, sym := range p.SymOrder {
|
||||
if p.isAuto(sym.SymID) {
|
||||
continue
|
||||
}
|
||||
if sym.Section == nil {
|
||||
t.Errorf("%s: symbol %s is missing section", obj, sym)
|
||||
continue
|
||||
}
|
||||
i := strings.Index(sym.Name, "_")
|
||||
if i < 0 {
|
||||
t.Errorf("%s: unexpected symbol %s", obj, sym)
|
||||
continue
|
||||
}
|
||||
if sym.Section.Name != sym.Name[:i] {
|
||||
t.Errorf("%s: symbol %s in section %s, want %s", obj, sym, sym.Section.Name, sym.Name[:i])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"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")
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,380 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Mach-O (Darwin) object file writing.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"debug/macho"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// machoFormat is the implementation of formatter.
|
||||
type machoFormat struct{}
|
||||
|
||||
// machoHeader and friends are data structures
|
||||
// corresponding to the Mach-O file header
|
||||
// to be written to disk.
|
||||
|
||||
const (
|
||||
macho64Bit = 1 << 24
|
||||
machoSubCPU386 = 3
|
||||
)
|
||||
|
||||
// machoArch describes a Mach-O target architecture.
|
||||
type machoArch struct {
|
||||
CPU uint32
|
||||
SubCPU uint32
|
||||
}
|
||||
|
||||
// machoHeader is the Mach-O file header.
|
||||
type machoHeader struct {
|
||||
machoArch
|
||||
FileType uint32
|
||||
Loads []*machoLoad
|
||||
Segments []*machoSegment
|
||||
p *Prog // for reporting errors
|
||||
}
|
||||
|
||||
// machoLoad is a Mach-O load command.
|
||||
type machoLoad struct {
|
||||
Type uint32
|
||||
Data []uint32
|
||||
}
|
||||
|
||||
// machoSegment is a Mach-O segment.
|
||||
type machoSegment struct {
|
||||
Name string
|
||||
VirtAddr Addr
|
||||
VirtSize Addr
|
||||
FileOffset Addr
|
||||
FileSize Addr
|
||||
Prot1 uint32
|
||||
Prot2 uint32
|
||||
Flags uint32
|
||||
Sections []*machoSection
|
||||
}
|
||||
|
||||
// machoSection is a Mach-O section, inside a segment.
|
||||
type machoSection struct {
|
||||
Name string
|
||||
Segment string
|
||||
Addr Addr
|
||||
Size Addr
|
||||
Offset uint32
|
||||
Align uint32
|
||||
Reloc uint32
|
||||
Nreloc uint32
|
||||
Flags uint32
|
||||
Res1 uint32
|
||||
Res2 uint32
|
||||
}
|
||||
|
||||
// layout positions the segments and sections in p
|
||||
// to make room for the Mach-O file header.
|
||||
// That is, it edits their VirtAddr fields to adjust for the presence
|
||||
// of the Mach-O header at the beginning of the address space.
|
||||
func (machoFormat) headerSize(p *Prog) (virt, file Addr) {
|
||||
var h machoHeader
|
||||
h.init(p)
|
||||
size := Addr(h.size())
|
||||
size = round(size, 4096)
|
||||
p.HeaderSize = size
|
||||
return size, size
|
||||
}
|
||||
|
||||
// write writes p to w as a Mach-O executable.
|
||||
// layout(p) must have already been called,
|
||||
// and the number, sizes, and addresses of the segments
|
||||
// and sections must not have been modified since the call.
|
||||
func (machoFormat) write(w io.Writer, p *Prog) {
|
||||
var h machoHeader
|
||||
h.init(p)
|
||||
off := Addr(0)
|
||||
enc := h.encode()
|
||||
w.Write(enc)
|
||||
off += Addr(len(enc))
|
||||
for _, seg := range p.Segments {
|
||||
if seg.FileOffset < off {
|
||||
h.p.errorf("mach-o error: invalid file offset")
|
||||
}
|
||||
w.Write(make([]byte, int(seg.FileOffset-off)))
|
||||
if seg.FileSize != Addr(len(seg.Data)) {
|
||||
h.p.errorf("mach-o error: invalid file size")
|
||||
}
|
||||
w.Write(seg.Data)
|
||||
off = seg.FileOffset + Addr(len(seg.Data))
|
||||
}
|
||||
}
|
||||
|
||||
// Conversion of Prog to macho data structures.
|
||||
|
||||
// machoArches maps from GOARCH to machoArch.
|
||||
var machoArches = map[string]machoArch{
|
||||
"amd64": {
|
||||
CPU: uint32(macho.CpuAmd64),
|
||||
SubCPU: uint32(machoSubCPU386),
|
||||
},
|
||||
}
|
||||
|
||||
// init initializes the header h to describe p.
|
||||
func (h *machoHeader) init(p *Prog) {
|
||||
h.p = p
|
||||
h.Segments = nil
|
||||
h.Loads = nil
|
||||
var ok bool
|
||||
h.machoArch, ok = machoArches[p.GOARCH]
|
||||
if !ok {
|
||||
p.errorf("mach-o: unknown target GOARCH %q", p.GOARCH)
|
||||
return
|
||||
}
|
||||
h.FileType = uint32(macho.TypeExec)
|
||||
|
||||
mseg := h.addSegment(p, "__PAGEZERO", nil)
|
||||
mseg.VirtSize = p.UnmappedSize
|
||||
|
||||
for _, seg := range p.Segments {
|
||||
h.addSegment(p, "__"+strings.ToUpper(seg.Name), seg)
|
||||
}
|
||||
|
||||
var data []uint32
|
||||
switch h.CPU {
|
||||
default:
|
||||
p.errorf("mach-o: unknown cpu %#x for GOARCH %q", h.CPU, p.GOARCH)
|
||||
case uint32(macho.CpuAmd64):
|
||||
data = make([]uint32, 2+42)
|
||||
data[0] = 4 // thread type
|
||||
data[1] = 42 // word count
|
||||
data[2+32] = uint32(p.Entry) // RIP register, in two parts
|
||||
data[2+32+1] = uint32(p.Entry >> 32)
|
||||
}
|
||||
|
||||
h.Loads = append(h.Loads, &machoLoad{
|
||||
Type: uint32(macho.LoadCmdUnixThread),
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
|
||||
// addSegment adds to h a Mach-O segment like seg with the given name.
|
||||
func (h *machoHeader) addSegment(p *Prog, name string, seg *Segment) *machoSegment {
|
||||
mseg := &machoSegment{
|
||||
Name: name,
|
||||
}
|
||||
h.Segments = append(h.Segments, mseg)
|
||||
if seg == nil {
|
||||
return mseg
|
||||
}
|
||||
|
||||
mseg.VirtAddr = seg.VirtAddr
|
||||
mseg.VirtSize = seg.VirtSize
|
||||
mseg.FileOffset = round(seg.FileOffset, 4096)
|
||||
mseg.FileSize = seg.FileSize
|
||||
|
||||
if name == "__TEXT" {
|
||||
// Initially RWX, then just RX
|
||||
mseg.Prot1 = 7
|
||||
mseg.Prot2 = 5
|
||||
|
||||
// Text segment maps Mach-O header, needed by dynamic linker.
|
||||
mseg.VirtAddr -= p.HeaderSize
|
||||
mseg.VirtSize += p.HeaderSize
|
||||
mseg.FileOffset -= p.HeaderSize
|
||||
mseg.FileSize += p.HeaderSize
|
||||
} else {
|
||||
// RW
|
||||
mseg.Prot1 = 3
|
||||
mseg.Prot2 = 3
|
||||
}
|
||||
|
||||
for _, sect := range seg.Sections {
|
||||
h.addSection(mseg, seg, sect)
|
||||
}
|
||||
return mseg
|
||||
}
|
||||
|
||||
// addSection adds to mseg a Mach-O section like sect, inside seg, with the given name.
|
||||
func (h *machoHeader) addSection(mseg *machoSegment, seg *Segment, sect *Section) {
|
||||
msect := &machoSection{
|
||||
Name: "__" + sect.Name,
|
||||
Segment: mseg.Name,
|
||||
// Reloc: sect.RelocOffset,
|
||||
// NumReloc: sect.RelocLen / 8,
|
||||
Addr: sect.VirtAddr,
|
||||
Size: sect.Size,
|
||||
}
|
||||
mseg.Sections = append(mseg.Sections, msect)
|
||||
|
||||
for 1<<msect.Align < sect.Align {
|
||||
msect.Align++
|
||||
}
|
||||
|
||||
if off := sect.VirtAddr - seg.VirtAddr; off < seg.FileSize {
|
||||
// Data in file.
|
||||
if sect.Size > seg.FileSize-off {
|
||||
h.p.errorf("mach-o error: section crosses file boundary")
|
||||
}
|
||||
msect.Offset = uint32(seg.FileOffset + off)
|
||||
} else {
|
||||
// Zero filled.
|
||||
msect.Flags |= 1
|
||||
}
|
||||
|
||||
if sect.Name == "text" {
|
||||
msect.Flags |= 0x400 // contains executable instructions
|
||||
}
|
||||
}
|
||||
|
||||
// A machoWriter helps write Mach-O headers.
|
||||
// It is basically a buffer with some helper routines for writing integers.
|
||||
type machoWriter struct {
|
||||
dst []byte
|
||||
tmp [8]byte
|
||||
order binary.ByteOrder
|
||||
is64 bool
|
||||
p *Prog
|
||||
}
|
||||
|
||||
// if64 returns x if w is writing a 64-bit object file; otherwise it returns y.
|
||||
func (w *machoWriter) if64(x, y interface{}) interface{} {
|
||||
if w.is64 {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
||||
|
||||
// encode encodes each of the given arguments into the writer.
|
||||
// It encodes uint32, []uint32, uint64, and []uint64 by writing each value
|
||||
// in turn in the correct byte order for the output file.
|
||||
// It encodes an Addr as a uint64 if writing a 64-bit output file, or else as a uint32.
|
||||
// It encodes []byte and string by writing the raw bytes (no length prefix).
|
||||
// It skips nil values in the args list.
|
||||
func (w *machoWriter) encode(args ...interface{}) {
|
||||
for _, arg := range args {
|
||||
switch arg := arg.(type) {
|
||||
default:
|
||||
w.p.errorf("mach-o error: cannot encode %T", arg)
|
||||
case nil:
|
||||
// skip
|
||||
case []byte:
|
||||
w.dst = append(w.dst, arg...)
|
||||
case string:
|
||||
w.dst = append(w.dst, arg...)
|
||||
case uint32:
|
||||
w.order.PutUint32(w.tmp[:], arg)
|
||||
w.dst = append(w.dst, w.tmp[:4]...)
|
||||
case []uint32:
|
||||
for _, x := range arg {
|
||||
w.order.PutUint32(w.tmp[:], x)
|
||||
w.dst = append(w.dst, w.tmp[:4]...)
|
||||
}
|
||||
case uint64:
|
||||
w.order.PutUint64(w.tmp[:], arg)
|
||||
w.dst = append(w.dst, w.tmp[:8]...)
|
||||
case Addr:
|
||||
if w.is64 {
|
||||
w.order.PutUint64(w.tmp[:], uint64(arg))
|
||||
w.dst = append(w.dst, w.tmp[:8]...)
|
||||
} else {
|
||||
if Addr(uint32(arg)) != arg {
|
||||
w.p.errorf("mach-o error: truncating address %#x to uint32", arg)
|
||||
}
|
||||
w.order.PutUint32(w.tmp[:], uint32(arg))
|
||||
w.dst = append(w.dst, w.tmp[:4]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// segmentSize returns the size of the encoding of seg in bytes.
|
||||
func (w *machoWriter) segmentSize(seg *machoSegment) int {
|
||||
if w.is64 {
|
||||
return 18*4 + 20*4*len(seg.Sections)
|
||||
}
|
||||
return 14*4 + 22*4*len(seg.Sections)
|
||||
}
|
||||
|
||||
// zeroPad returns the string s truncated or padded with NULs to n bytes.
|
||||
func zeroPad(s string, n int) string {
|
||||
if len(s) >= n {
|
||||
return s[:n]
|
||||
}
|
||||
return s + strings.Repeat("\x00", n-len(s))
|
||||
}
|
||||
|
||||
// size returns the encoded size of the header.
|
||||
func (h *machoHeader) size() int {
|
||||
// Could write separate code, but encoding is cheap; encode and throw it away.
|
||||
return len(h.encode())
|
||||
}
|
||||
|
||||
// encode returns the Mach-O encoding of the header.
|
||||
func (h *machoHeader) encode() []byte {
|
||||
w := &machoWriter{p: h.p}
|
||||
w.is64 = h.CPU&macho64Bit != 0
|
||||
w.order = w.p.byteorder
|
||||
|
||||
loadSize := 0
|
||||
for _, seg := range h.Segments {
|
||||
loadSize += w.segmentSize(seg)
|
||||
}
|
||||
for _, l := range h.Loads {
|
||||
loadSize += 4 * (2 + len(l.Data))
|
||||
}
|
||||
|
||||
w.encode(
|
||||
w.if64(macho.Magic64, macho.Magic32),
|
||||
uint32(h.CPU),
|
||||
uint32(h.SubCPU),
|
||||
uint32(h.FileType),
|
||||
uint32(len(h.Loads)+len(h.Segments)),
|
||||
uint32(loadSize),
|
||||
uint32(1),
|
||||
w.if64(uint32(0), nil),
|
||||
)
|
||||
|
||||
for _, seg := range h.Segments {
|
||||
w.encode(
|
||||
w.if64(uint32(macho.LoadCmdSegment64), uint32(macho.LoadCmdSegment)),
|
||||
uint32(w.segmentSize(seg)),
|
||||
zeroPad(seg.Name, 16),
|
||||
seg.VirtAddr,
|
||||
seg.VirtSize,
|
||||
seg.FileOffset,
|
||||
seg.FileSize,
|
||||
seg.Prot1,
|
||||
seg.Prot2,
|
||||
uint32(len(seg.Sections)),
|
||||
seg.Flags,
|
||||
)
|
||||
for _, sect := range seg.Sections {
|
||||
w.encode(
|
||||
zeroPad(sect.Name, 16),
|
||||
zeroPad(seg.Name, 16),
|
||||
sect.Addr,
|
||||
sect.Size,
|
||||
sect.Offset,
|
||||
sect.Align,
|
||||
sect.Reloc,
|
||||
sect.Nreloc,
|
||||
sect.Flags,
|
||||
sect.Res1,
|
||||
sect.Res2,
|
||||
w.if64(uint32(0), nil),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for _, load := range h.Loads {
|
||||
w.encode(
|
||||
load.Type,
|
||||
uint32(4*(2+len(load.Data))),
|
||||
load.Data,
|
||||
)
|
||||
}
|
||||
|
||||
return w.dst
|
||||
}
|
||||
@@ -1,407 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/macho"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Test macho writing by checking that each generated prog can be written
|
||||
// and then read back using debug/macho to get the same prog.
|
||||
// Also check against golden testdata file.
|
||||
var machoWriteTests = []struct {
|
||||
name string
|
||||
golden bool
|
||||
prog *Prog
|
||||
}{
|
||||
// amd64 exit 9
|
||||
{
|
||||
name: "exit9",
|
||||
golden: true,
|
||||
prog: &Prog{
|
||||
GOARCH: "amd64",
|
||||
GOOS: "darwin",
|
||||
UnmappedSize: 0x1000,
|
||||
Entry: 0x1000,
|
||||
Segments: []*Segment{
|
||||
{
|
||||
Name: "text",
|
||||
VirtAddr: 0x1000,
|
||||
VirtSize: 13,
|
||||
FileOffset: 0,
|
||||
FileSize: 13,
|
||||
Data: []byte{
|
||||
0xb8, 0x01, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX
|
||||
0xbf, 0x09, 0x00, 0x00, 0x00, // MOVL $9, DI
|
||||
0x0f, 0x05, // SYSCALL
|
||||
0xf4, // HLT
|
||||
},
|
||||
Sections: []*Section{
|
||||
{
|
||||
Name: "text",
|
||||
VirtAddr: 0x1000,
|
||||
Size: 13,
|
||||
Align: 64,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// amd64 write hello world & exit 9
|
||||
{
|
||||
name: "hello",
|
||||
golden: true,
|
||||
prog: &Prog{
|
||||
GOARCH: "amd64",
|
||||
GOOS: "darwin",
|
||||
UnmappedSize: 0x1000,
|
||||
Entry: 0x1000,
|
||||
Segments: []*Segment{
|
||||
{
|
||||
Name: "text",
|
||||
VirtAddr: 0x1000,
|
||||
VirtSize: 35,
|
||||
FileOffset: 0,
|
||||
FileSize: 35,
|
||||
Data: []byte{
|
||||
0xb8, 0x04, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX
|
||||
0xbf, 0x01, 0x00, 0x00, 0x00, // MOVL $1, DI
|
||||
0xbe, 0x00, 0x30, 0x00, 0x00, // MOVL $0x3000, SI
|
||||
0xba, 0x0c, 0x00, 0x00, 0x00, // MOVL $12, DX
|
||||
0x0f, 0x05, // SYSCALL
|
||||
0xb8, 0x01, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX
|
||||
0xbf, 0x09, 0x00, 0x00, 0x00, // MOVL $9, DI
|
||||
0x0f, 0x05, // SYSCALL
|
||||
0xf4, // HLT
|
||||
},
|
||||
Sections: []*Section{
|
||||
{
|
||||
Name: "text",
|
||||
VirtAddr: 0x1000,
|
||||
Size: 35,
|
||||
Align: 64,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "data",
|
||||
VirtAddr: 0x2000,
|
||||
VirtSize: 12,
|
||||
FileOffset: 0x1000,
|
||||
FileSize: 12,
|
||||
Data: []byte("hello world\n"),
|
||||
Sections: []*Section{
|
||||
{
|
||||
Name: "data",
|
||||
VirtAddr: 0x2000,
|
||||
Size: 12,
|
||||
Align: 64,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// amd64 write hello world from rodata & exit 0
|
||||
{
|
||||
name: "helloro",
|
||||
golden: true,
|
||||
prog: &Prog{
|
||||
GOARCH: "amd64",
|
||||
GOOS: "darwin",
|
||||
UnmappedSize: 0x1000,
|
||||
Entry: 0x1000,
|
||||
Segments: []*Segment{
|
||||
{
|
||||
Name: "text",
|
||||
VirtAddr: 0x1000,
|
||||
VirtSize: 0x100c,
|
||||
FileOffset: 0,
|
||||
FileSize: 0x100c,
|
||||
Data: concat(
|
||||
[]byte{
|
||||
0xb8, 0x04, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX
|
||||
0xbf, 0x01, 0x00, 0x00, 0x00, // MOVL $1, DI
|
||||
0xbe, 0x00, 0x30, 0x00, 0x00, // MOVL $0x3000, SI
|
||||
0xba, 0x0c, 0x00, 0x00, 0x00, // MOVL $12, DX
|
||||
0x0f, 0x05, // SYSCALL
|
||||
0xb8, 0x01, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX
|
||||
0xbf, 0x00, 0x00, 0x00, 0x00, // MOVL $0, DI
|
||||
0x0f, 0x05, // SYSCALL
|
||||
0xf4, // HLT
|
||||
},
|
||||
make([]byte, 0x1000-35),
|
||||
[]byte("hello world\n"),
|
||||
),
|
||||
Sections: []*Section{
|
||||
{
|
||||
Name: "text",
|
||||
VirtAddr: 0x1000,
|
||||
Size: 35,
|
||||
Align: 64,
|
||||
},
|
||||
{
|
||||
Name: "rodata",
|
||||
VirtAddr: 0x2000,
|
||||
Size: 12,
|
||||
Align: 64,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func concat(xs ...[]byte) []byte {
|
||||
var out []byte
|
||||
for _, x := range xs {
|
||||
out = append(out, x...)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func TestMachoWrite(t *testing.T) {
|
||||
for _, tt := range machoWriteTests {
|
||||
name := tt.prog.GOARCH + "." + tt.name
|
||||
prog := cloneProg(tt.prog)
|
||||
prog.init()
|
||||
var f machoFormat
|
||||
vsize, fsize := f.headerSize(prog)
|
||||
shiftProg(prog, vsize, fsize)
|
||||
var buf bytes.Buffer
|
||||
f.write(&buf, prog)
|
||||
if false { // enable to debug
|
||||
ioutil.WriteFile("a.out", buf.Bytes(), 0777)
|
||||
}
|
||||
read, err := machoRead(machoArches[tt.prog.GOARCH], buf.Bytes())
|
||||
if err != nil {
|
||||
t.Errorf("%s: reading mach-o output:\n\t%v", name, err)
|
||||
continue
|
||||
}
|
||||
diffs := diffProg(read, prog)
|
||||
if diffs != nil {
|
||||
t.Errorf("%s: mismatched prog:\n\t%s", name, strings.Join(diffs, "\n\t"))
|
||||
continue
|
||||
}
|
||||
if !tt.golden {
|
||||
continue
|
||||
}
|
||||
checkGolden(t, buf.Bytes(), "testdata/macho."+name)
|
||||
}
|
||||
}
|
||||
|
||||
// machoRead reads the mach-o file in data and returns a corresponding prog.
|
||||
func machoRead(arch machoArch, data []byte) (*Prog, error) {
|
||||
f, err := macho.NewFile(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var errors []string
|
||||
errorf := func(format string, args ...interface{}) {
|
||||
errors = append(errors, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
magic := uint32(0xFEEDFACE)
|
||||
if arch.CPU&macho64Bit != 0 {
|
||||
magic |= 1
|
||||
}
|
||||
if f.Magic != magic {
|
||||
errorf("header: Magic = %#x, want %#x", f.Magic, magic)
|
||||
}
|
||||
if f.Cpu != macho.CpuAmd64 {
|
||||
errorf("header: CPU = %#x, want %#x", f.Cpu, macho.CpuAmd64)
|
||||
}
|
||||
if f.SubCpu != 3 {
|
||||
errorf("header: SubCPU = %#x, want %#x", f.SubCpu, 3)
|
||||
}
|
||||
if f.Type != 2 {
|
||||
errorf("header: FileType = %d, want %d", f.Type, 2)
|
||||
}
|
||||
if f.Flags != 1 {
|
||||
errorf("header: Flags = %d, want %d", f.Flags, 1)
|
||||
}
|
||||
|
||||
msects := f.Sections
|
||||
var limit uint64
|
||||
prog := new(Prog)
|
||||
for _, load := range f.Loads {
|
||||
switch load := load.(type) {
|
||||
default:
|
||||
errorf("unexpected macho load %T %x", load, load.Raw())
|
||||
|
||||
case macho.LoadBytes:
|
||||
if len(load) < 8 || len(load)%4 != 0 {
|
||||
errorf("unexpected load length %d", len(load))
|
||||
continue
|
||||
}
|
||||
cmd := f.ByteOrder.Uint32(load)
|
||||
switch macho.LoadCmd(cmd) {
|
||||
default:
|
||||
errorf("unexpected macho load cmd %s", macho.LoadCmd(cmd))
|
||||
case macho.LoadCmdUnixThread:
|
||||
data := make([]uint32, len(load[8:])/4)
|
||||
binary.Read(bytes.NewReader(load[8:]), f.ByteOrder, data)
|
||||
if len(data) != 44 {
|
||||
errorf("macho thread len(data) = %d, want 42", len(data))
|
||||
continue
|
||||
}
|
||||
if data[0] != 4 {
|
||||
errorf("macho thread type = %d, want 4", data[0])
|
||||
}
|
||||
if data[1] != uint32(len(data))-2 {
|
||||
errorf("macho thread desc len = %d, want %d", data[1], uint32(len(data))-2)
|
||||
continue
|
||||
}
|
||||
for i, val := range data[2:] {
|
||||
switch i {
|
||||
default:
|
||||
if val != 0 {
|
||||
errorf("macho thread data[%d] = %#x, want 0", i, val)
|
||||
}
|
||||
case 32:
|
||||
prog.Entry = Addr(val)
|
||||
case 33:
|
||||
prog.Entry |= Addr(val) << 32
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case *macho.Segment:
|
||||
if load.Addr < limit {
|
||||
errorf("segments out of order: %q at %#x after %#x", load.Name, load.Addr, limit)
|
||||
}
|
||||
limit = load.Addr + load.Memsz
|
||||
if load.Name == "__PAGEZERO" || load.Addr == 0 && load.Filesz == 0 {
|
||||
if load.Name != "__PAGEZERO" {
|
||||
errorf("segment with Addr=0, Filesz=0 is named %q, want %q", load.Name, "__PAGEZERO")
|
||||
} else if load.Addr != 0 || load.Filesz != 0 {
|
||||
errorf("segment %q has Addr=%#x, Filesz=%d, want Addr=%#x, Filesz=%d", load.Name, load.Addr, load.Filesz, 0, 0)
|
||||
}
|
||||
prog.UnmappedSize = Addr(load.Memsz)
|
||||
continue
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(load.Name, "__") {
|
||||
errorf("segment name %q does not begin with %q", load.Name, "__")
|
||||
}
|
||||
if strings.ToUpper(load.Name) != load.Name {
|
||||
errorf("segment name %q is not all upper case", load.Name)
|
||||
}
|
||||
|
||||
seg := &Segment{
|
||||
Name: strings.ToLower(strings.TrimPrefix(load.Name, "__")),
|
||||
VirtAddr: Addr(load.Addr),
|
||||
VirtSize: Addr(load.Memsz),
|
||||
FileOffset: Addr(load.Offset),
|
||||
FileSize: Addr(load.Filesz),
|
||||
}
|
||||
prog.Segments = append(prog.Segments, seg)
|
||||
|
||||
data, err := load.Data()
|
||||
if err != nil {
|
||||
errorf("loading data from %q: %v", load.Name, err)
|
||||
}
|
||||
seg.Data = data
|
||||
|
||||
var maxprot, prot uint32
|
||||
if load.Name == "__TEXT" {
|
||||
maxprot, prot = 7, 5
|
||||
} else {
|
||||
maxprot, prot = 3, 3
|
||||
}
|
||||
if load.Maxprot != maxprot || load.Prot != prot {
|
||||
errorf("segment %q protection is %d, %d, want %d, %d",
|
||||
load.Name, load.Maxprot, load.Prot, maxprot, prot)
|
||||
}
|
||||
|
||||
for len(msects) > 0 && msects[0].Addr < load.Addr+load.Memsz {
|
||||
msect := msects[0]
|
||||
msects = msects[1:]
|
||||
|
||||
if msect.Offset > 0 && prog.HeaderSize == 0 {
|
||||
prog.HeaderSize = Addr(msect.Offset)
|
||||
if seg.FileOffset != 0 {
|
||||
errorf("initial segment %q does not map header", load.Name)
|
||||
}
|
||||
seg.VirtAddr += prog.HeaderSize
|
||||
seg.VirtSize -= prog.HeaderSize
|
||||
seg.FileOffset += prog.HeaderSize
|
||||
seg.FileSize -= prog.HeaderSize
|
||||
seg.Data = seg.Data[prog.HeaderSize:]
|
||||
}
|
||||
|
||||
if msect.Addr < load.Addr {
|
||||
errorf("section %q at address %#x is missing segment", msect.Name, msect.Addr)
|
||||
continue
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(msect.Name, "__") {
|
||||
errorf("section name %q does not begin with %q", msect.Name, "__")
|
||||
}
|
||||
if strings.ToLower(msect.Name) != msect.Name {
|
||||
errorf("section name %q is not all lower case", msect.Name)
|
||||
}
|
||||
if msect.Seg != load.Name {
|
||||
errorf("section %q is lists segment name %q, want %q",
|
||||
msect.Name, msect.Seg, load.Name)
|
||||
}
|
||||
if uint64(msect.Offset) != uint64(load.Offset)+msect.Addr-load.Addr {
|
||||
errorf("section %q file offset is %#x, want %#x",
|
||||
msect.Name, msect.Offset, load.Offset+msect.Addr-load.Addr)
|
||||
}
|
||||
if msect.Reloff != 0 || msect.Nreloc != 0 {
|
||||
errorf("section %q has reloff %d,%d, want %d,%d",
|
||||
msect.Name, msect.Reloff, msect.Nreloc, 0, 0)
|
||||
}
|
||||
flags := uint32(0)
|
||||
if msect.Name == "__text" {
|
||||
flags = 0x400
|
||||
}
|
||||
if msect.Offset == 0 {
|
||||
flags = 1
|
||||
}
|
||||
if msect.Flags != flags {
|
||||
errorf("section %q flags = %#x, want %#x", msect.Name, msect.Flags, flags)
|
||||
}
|
||||
sect := &Section{
|
||||
Name: strings.ToLower(strings.TrimPrefix(msect.Name, "__")),
|
||||
VirtAddr: Addr(msect.Addr),
|
||||
Size: Addr(msect.Size),
|
||||
Align: 1 << msect.Align,
|
||||
}
|
||||
seg.Sections = append(seg.Sections, sect)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, msect := range msects {
|
||||
errorf("section %q has no segment", msect.Name)
|
||||
}
|
||||
|
||||
limit = 0
|
||||
for _, msect := range f.Sections {
|
||||
if msect.Addr < limit {
|
||||
errorf("sections out of order: %q at %#x after %#x", msect.Name, msect.Addr, limit)
|
||||
}
|
||||
limit = msect.Addr + msect.Size
|
||||
}
|
||||
|
||||
err = nil
|
||||
if errors != nil {
|
||||
err = fmt.Errorf("%s", strings.Join(errors, "\n\t"))
|
||||
}
|
||||
return prog, err
|
||||
}
|
||||
@@ -1,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
|
||||
}
|
||||
@@ -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])
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// shiftProg adjusts the addresses in p.
|
||||
// It adds vdelta to all virtual addresses and fdelta to all file offsets.
|
||||
func shiftProg(p *Prog, vdelta Addr, fdelta Addr) {
|
||||
p.Entry += vdelta
|
||||
for _, seg := range p.Segments {
|
||||
seg.FileOffset += fdelta
|
||||
seg.VirtAddr += vdelta
|
||||
for _, sect := range seg.Sections {
|
||||
sect.VirtAddr += vdelta
|
||||
for _, sym := range sect.Syms {
|
||||
sym.Addr += vdelta
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// diffProg returns a list of differences between p and q,
|
||||
// assuming p is being checked and q is the correct answer.
|
||||
func diffProg(p, q *Prog) []string {
|
||||
var errors []string
|
||||
if p.UnmappedSize != q.UnmappedSize {
|
||||
errors = append(errors, fmt.Sprintf("p.UnmappedSize = %#x, want %#x", p.UnmappedSize, q.UnmappedSize))
|
||||
}
|
||||
if p.HeaderSize != q.HeaderSize {
|
||||
errors = append(errors, fmt.Sprintf("p.HeaderSize = %#x, want %#x", p.HeaderSize, q.HeaderSize))
|
||||
}
|
||||
if p.Entry != q.Entry {
|
||||
errors = append(errors, fmt.Sprintf("p.Entry = %#x, want %#x", p.Entry, q.Entry))
|
||||
}
|
||||
for i := 0; i < len(p.Segments) || i < len(q.Segments); i++ {
|
||||
if i >= len(p.Segments) {
|
||||
errors = append(errors, fmt.Sprintf("p missing segment %q", q.Segments[i].Name))
|
||||
continue
|
||||
}
|
||||
if i >= len(q.Segments) {
|
||||
errors = append(errors, fmt.Sprintf("p has extra segment %q", p.Segments[i].Name))
|
||||
continue
|
||||
}
|
||||
pseg := p.Segments[i]
|
||||
qseg := q.Segments[i]
|
||||
if pseg.Name != qseg.Name {
|
||||
errors = append(errors, fmt.Sprintf("segment %d Name = %q, want %q", i, pseg.Name, qseg.Name))
|
||||
continue // probably out of sync
|
||||
}
|
||||
if pseg.VirtAddr != qseg.VirtAddr {
|
||||
errors = append(errors, fmt.Sprintf("segment %q VirtAddr = %#x, want %#x", pseg.Name, pseg.VirtAddr, qseg.VirtAddr))
|
||||
}
|
||||
if pseg.VirtSize != qseg.VirtSize {
|
||||
errors = append(errors, fmt.Sprintf("segment %q VirtSize = %#x, want %#x", pseg.Name, pseg.VirtSize, qseg.VirtSize))
|
||||
}
|
||||
if pseg.FileOffset != qseg.FileOffset {
|
||||
errors = append(errors, fmt.Sprintf("segment %q FileOffset = %#x, want %#x", pseg.Name, pseg.FileOffset, qseg.FileOffset))
|
||||
}
|
||||
if pseg.FileSize != qseg.FileSize {
|
||||
errors = append(errors, fmt.Sprintf("segment %q FileSize = %#x, want %#x", pseg.Name, pseg.FileSize, qseg.FileSize))
|
||||
}
|
||||
if len(pseg.Data) != len(qseg.Data) {
|
||||
errors = append(errors, fmt.Sprintf("segment %q len(Data) = %d, want %d", pseg.Name, len(pseg.Data), len(qseg.Data)))
|
||||
} else if !bytes.Equal(pseg.Data, qseg.Data) {
|
||||
errors = append(errors, fmt.Sprintf("segment %q Data mismatch:\n\thave %x\n\twant %x", pseg.Name, pseg.Data, qseg.Data))
|
||||
}
|
||||
|
||||
for j := 0; j < len(pseg.Sections) || j < len(qseg.Sections); j++ {
|
||||
if j >= len(pseg.Sections) {
|
||||
errors = append(errors, fmt.Sprintf("segment %q missing section %q", pseg.Name, qseg.Sections[i].Name))
|
||||
continue
|
||||
}
|
||||
if j >= len(qseg.Sections) {
|
||||
errors = append(errors, fmt.Sprintf("segment %q has extra section %q", pseg.Name, pseg.Sections[i].Name))
|
||||
continue
|
||||
}
|
||||
psect := pseg.Sections[j]
|
||||
qsect := qseg.Sections[j]
|
||||
if psect.Name != qsect.Name {
|
||||
errors = append(errors, fmt.Sprintf("segment %q, section %d Name = %q, want %q", pseg.Name, j, psect.Name, qsect.Name))
|
||||
continue // probably out of sync
|
||||
}
|
||||
|
||||
if psect.VirtAddr != qsect.VirtAddr {
|
||||
errors = append(errors, fmt.Sprintf("segment %q section %q VirtAddr = %#x, want %#x", pseg.Name, psect.Name, psect.VirtAddr, qsect.VirtAddr))
|
||||
}
|
||||
if psect.Size != qsect.Size {
|
||||
errors = append(errors, fmt.Sprintf("segment %q section %q Size = %#x, want %#x", pseg.Name, psect.Name, psect.Size, qsect.Size))
|
||||
}
|
||||
if psect.Align != qsect.Align {
|
||||
errors = append(errors, fmt.Sprintf("segment %q section %q Align = %#x, want %#x", pseg.Name, psect.Name, psect.Align, qsect.Align))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
// cloneProg returns a deep copy of p.
|
||||
func cloneProg(p *Prog) *Prog {
|
||||
q := new(Prog)
|
||||
*q = *p
|
||||
q.Segments = make([]*Segment, len(p.Segments))
|
||||
for i, seg := range p.Segments {
|
||||
q.Segments[i] = cloneSegment(seg)
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
// cloneSegment returns a deep copy of seg.
|
||||
func cloneSegment(seg *Segment) *Segment {
|
||||
t := new(Segment)
|
||||
*t = *seg
|
||||
t.Sections = make([]*Section, len(seg.Sections))
|
||||
for i, sect := range seg.Sections {
|
||||
t.Sections[i] = cloneSection(sect)
|
||||
}
|
||||
t.Data = make([]byte, len(seg.Data))
|
||||
copy(t.Data, seg.Data)
|
||||
return t
|
||||
}
|
||||
|
||||
// cloneSection returns a deep copy of section.
|
||||
func cloneSection(sect *Section) *Section {
|
||||
// At the moment, there's nothing we need to make a deep copy of.
|
||||
t := new(Section)
|
||||
*t = *sect
|
||||
return t
|
||||
}
|
||||
|
||||
const saveMismatch = true
|
||||
|
||||
// checkGolden checks that data matches the named file.
|
||||
// If not, it reports the error to the test.
|
||||
func checkGolden(t *testing.T, data []byte, name string) {
|
||||
golden := mustParseHexdumpFile(t, name)
|
||||
if !bytes.Equal(data, golden) {
|
||||
if saveMismatch {
|
||||
ioutil.WriteFile(name+".raw", data, 0666)
|
||||
ioutil.WriteFile(name+".hex", []byte(hexdump(data)), 0666)
|
||||
}
|
||||
// TODO(rsc): A better diff would be nice, as needed.
|
||||
i := 0
|
||||
for i < len(data) && i < len(golden) && data[i] == golden[i] {
|
||||
i++
|
||||
}
|
||||
if i >= len(data) {
|
||||
t.Errorf("%s: output file shorter than expected: have %d bytes, want %d", name, len(data), len(golden))
|
||||
} else if i >= len(golden) {
|
||||
t.Errorf("%s: output file larger than expected: have %d bytes, want %d", name, len(data), len(golden))
|
||||
} else {
|
||||
t.Errorf("%s: output file differs at byte %d: have %#02x, want %#02x", name, i, data[i], golden[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Generation of runtime-accessible data structures.
|
||||
// See also debug.go.
|
||||
|
||||
package main
|
||||
|
||||
import "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,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
15
src/cmd/link/testdata/Makefile
vendored
15
src/cmd/link/testdata/Makefile
vendored
@@ -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
|
||||
BIN
src/cmd/link/testdata/autosection.6
vendored
BIN
src/cmd/link/testdata/autosection.6
vendored
Binary file not shown.
60
src/cmd/link/testdata/autosection.s
vendored
60
src/cmd/link/testdata/autosection.s
vendored
@@ -1,60 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Test of section-named symbols.
|
||||
|
||||
#include "../../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
|
||||
BIN
src/cmd/link/testdata/autoweak.6
vendored
BIN
src/cmd/link/testdata/autoweak.6
vendored
Binary file not shown.
30
src/cmd/link/testdata/autoweak.s
vendored
30
src/cmd/link/testdata/autoweak.s
vendored
@@ -1,30 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Test of go.weak symbols.
|
||||
|
||||
TEXT start(SB),7,$0
|
||||
MOVQ $autotab(SB),AX
|
||||
MOVQ $autoptr(SB),AX
|
||||
RET
|
||||
|
||||
// go.weak.sym should resolve to sym, because sym is in the binary.
|
||||
DATA autotab+0(SB)/8, $go·weak·sym(SB)
|
||||
DATA autotab+8(SB)/8, $sym(SB)
|
||||
|
||||
// go.weak.missingsym should resolve to 0, because missingsym is not in the binary.
|
||||
DATA autotab+16(SB)/8, $go·weak·missingsym(SB)
|
||||
DATA autotab+24(SB)/8, $0
|
||||
|
||||
// go.weak.deadsym should resolve to 0, because deadsym is discarded during dead code removal
|
||||
DATA autotab+32(SB)/8, $go·weak·deadsym(SB)
|
||||
DATA autotab+40(SB)/8, $0
|
||||
|
||||
GLOBL autotab(SB), $48
|
||||
|
||||
GLOBL sym(SB), $1
|
||||
|
||||
GLOBL deadsym(SB), $1
|
||||
|
||||
GLOBL autoptr(SB), $0
|
||||
BIN
src/cmd/link/testdata/dead.6
vendored
BIN
src/cmd/link/testdata/dead.6
vendored
Binary file not shown.
49
src/cmd/link/testdata/dead.s
vendored
49
src/cmd/link/testdata/dead.s
vendored
@@ -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
|
||||
|
||||
112
src/cmd/link/testdata/genpcln.go
vendored
112
src/cmd/link/testdata/genpcln.go
vendored
@@ -1,112 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This program generates a .s file using a pseudorandom
|
||||
// value stream for the runtime function data.
|
||||
// The pclntab test checks that the linked copy
|
||||
// still has the same pseudorandom value stream.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Printf("// generated by genpcln.go; do not edit\n\n")
|
||||
for f := 0; f < 3; f++ {
|
||||
r := rand.New(rand.NewSource(int64(f)))
|
||||
file := "input"
|
||||
line := 1
|
||||
args := r.Intn(100) * 8
|
||||
frame := 32 + r.Intn(32)/8*8
|
||||
fmt.Printf("#line %d %q\n", line, file)
|
||||
fmt.Printf("TEXT func%d(SB),7,$%d-%d\n", f, frame, args)
|
||||
fmt.Printf("\tFUNCDATA $1, funcdata%d(SB)\n", f)
|
||||
fmt.Printf("#line %d %q\n", line, file)
|
||||
size := 200 + r.Intn(100)*8
|
||||
spadj := 0
|
||||
flushed := 0
|
||||
firstpc := 4
|
||||
flush := func(i int) {
|
||||
for i-flushed >= 10 {
|
||||
fmt.Printf("#line %d %q\n", line, file)
|
||||
fmt.Printf("/*%#04x*/\tMOVQ $0x123456789, AX\n", firstpc+flushed)
|
||||
flushed += 10
|
||||
}
|
||||
for i-flushed >= 5 {
|
||||
fmt.Printf("#line %d %q\n", line, file)
|
||||
fmt.Printf("/*%#04x*/\tMOVL $0x1234567, AX\n", firstpc+flushed)
|
||||
flushed += 5
|
||||
}
|
||||
for i-flushed > 0 {
|
||||
fmt.Printf("#line %d %q\n", line, file)
|
||||
fmt.Printf("/*%#04x*/\tBYTE $0\n", firstpc+flushed)
|
||||
flushed++
|
||||
}
|
||||
}
|
||||
for i := 0; i < size; i++ {
|
||||
// Possible SP adjustment.
|
||||
if r.Intn(100) == 0 {
|
||||
flush(i)
|
||||
fmt.Printf("#line %d %q\n", line, file)
|
||||
if spadj <= -32 || spadj < 32 && r.Intn(2) == 0 {
|
||||
spadj += 8
|
||||
fmt.Printf("/*%#04x*/\tPUSHQ AX\n", firstpc+i)
|
||||
} else {
|
||||
spadj -= 8
|
||||
fmt.Printf("/*%#04x*/\tPOPQ AX\n", firstpc+i)
|
||||
}
|
||||
i += 1
|
||||
flushed = i
|
||||
}
|
||||
|
||||
// Possible PCFile change.
|
||||
if r.Intn(100) == 0 {
|
||||
flush(i)
|
||||
file = fmt.Sprintf("file%d.s", r.Intn(10))
|
||||
line = r.Intn(100) + 1
|
||||
}
|
||||
|
||||
// Possible PCLine change.
|
||||
if r.Intn(10) == 0 {
|
||||
flush(i)
|
||||
line = r.Intn(1000) + 1
|
||||
}
|
||||
|
||||
// Possible PCData $1 change.
|
||||
if r.Intn(100) == 0 {
|
||||
flush(i)
|
||||
fmt.Printf("/*%6s*/\tPCDATA $1, $%d\n", "", r.Intn(1000))
|
||||
}
|
||||
|
||||
// Possible PCData $2 change.
|
||||
if r.Intn(100) == 0 {
|
||||
flush(i)
|
||||
fmt.Printf("/*%6s*/\tPCDATA $2, $%d\n", "", r.Intn(1000))
|
||||
}
|
||||
}
|
||||
flush(size)
|
||||
for spadj < 0 {
|
||||
fmt.Printf("\tPUSHQ AX\n")
|
||||
spadj += 8
|
||||
}
|
||||
for spadj > 0 {
|
||||
fmt.Printf("\tPOPQ AX\n")
|
||||
spadj -= 8
|
||||
}
|
||||
fmt.Printf("\tRET\n")
|
||||
|
||||
fmt.Printf("\n")
|
||||
fmt.Printf("GLOBL funcdata%d(SB), $16\n", f)
|
||||
}
|
||||
|
||||
fmt.Printf("\nTEXT start(SB),7,$0\n")
|
||||
for f := 0; f < 3; f++ {
|
||||
fmt.Printf("\tCALL func%d(SB)\n", f)
|
||||
}
|
||||
fmt.Printf("\tMOVQ $pclntab(SB), AX\n")
|
||||
fmt.Printf("\n\tRET\n")
|
||||
}
|
||||
BIN
src/cmd/link/testdata/hello.6
vendored
BIN
src/cmd/link/testdata/hello.6
vendored
Binary file not shown.
15
src/cmd/link/testdata/hello.s
vendored
15
src/cmd/link/testdata/hello.s
vendored
@@ -1,15 +0,0 @@
|
||||
TEXT _rt0_go(SB),7,$0
|
||||
MOVL $1, DI
|
||||
MOVL $hello<>(SB), SI
|
||||
MOVL $12, DX
|
||||
MOVL $0x2000004, AX
|
||||
SYSCALL
|
||||
MOVL $0, DI
|
||||
MOVL $0x2000001, AX
|
||||
SYSCALL
|
||||
RET
|
||||
|
||||
DATA hello<>+0(SB)/4, $"hell"
|
||||
DATA hello<>+4(SB)/4, $"o wo"
|
||||
DATA hello<>+8(SB)/4, $"rld\n"
|
||||
GLOBL hello<>(SB), $12
|
||||
BIN
src/cmd/link/testdata/layout.6
vendored
BIN
src/cmd/link/testdata/layout.6
vendored
Binary file not shown.
29
src/cmd/link/testdata/layout.s
vendored
29
src/cmd/link/testdata/layout.s
vendored
@@ -1,29 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Test of section assignment in layout.go.
|
||||
// Each symbol should end up in the section named by the symbol name prefix (up to the underscore).
|
||||
|
||||
#include "../../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
|
||||
57
src/cmd/link/testdata/link.hello.darwin.amd64
vendored
57
src/cmd/link/testdata/link.hello.darwin.amd64
vendored
@@ -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
|
||||
24
src/cmd/link/testdata/macho.amd64.exit9
vendored
24
src/cmd/link/testdata/macho.amd64.exit9
vendored
@@ -1,24 +0,0 @@
|
||||
00000000 cf fa ed fe 07 00 00 01 03 00 00 00 02 00 00 00 |................|
|
||||
00000010 03 00 00 00 98 01 00 00 01 00 00 00 00 00 00 00 |................|
|
||||
00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE|
|
||||
00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............|
|
||||
00000040 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000060 00 00 00 00 00 00 00 00 19 00 00 00 98 00 00 00 |................|
|
||||
00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
00000080 00 10 00 00 00 00 00 00 0d 10 00 00 00 00 00 00 |................|
|
||||
00000090 00 00 00 00 00 00 00 00 0d 10 00 00 00 00 00 00 |................|
|
||||
000000a0 07 00 00 00 05 00 00 00 01 00 00 00 00 00 00 00 |................|
|
||||
000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........|
|
||||
000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
000000d0 00 20 00 00 00 00 00 00 0d 00 00 00 00 00 00 00 |. ..............|
|
||||
000000e0 00 10 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
000000f0 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000100 05 00 00 00 b8 00 00 00 04 00 00 00 2a 00 00 00 |............*...|
|
||||
00000110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00000190 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |. ..............|
|
||||
000001a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00001000 b8 01 00 00 02 bf 09 00 00 00 0f 05 f4 |.............|
|
||||
0000100d
|
||||
39
src/cmd/link/testdata/macho.amd64.hello
vendored
39
src/cmd/link/testdata/macho.amd64.hello
vendored
@@ -1,39 +0,0 @@
|
||||
00000000 cf fa ed fe 07 00 00 01 03 00 00 00 02 00 00 00 |................|
|
||||
00000010 04 00 00 00 30 02 00 00 01 00 00 00 00 00 00 00 |....0...........|
|
||||
00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE|
|
||||
00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............|
|
||||
00000040 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000060 00 00 00 00 00 00 00 00 19 00 00 00 98 00 00 00 |................|
|
||||
00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
00000080 00 10 00 00 00 00 00 00 23 10 00 00 00 00 00 00 |........#.......|
|
||||
00000090 00 00 00 00 00 00 00 00 23 10 00 00 00 00 00 00 |........#.......|
|
||||
000000a0 07 00 00 00 05 00 00 00 01 00 00 00 00 00 00 00 |................|
|
||||
000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........|
|
||||
000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
000000d0 00 20 00 00 00 00 00 00 23 00 00 00 00 00 00 00 |. ......#.......|
|
||||
000000e0 00 10 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
000000f0 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000100 19 00 00 00 98 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..|
|
||||
00000110 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 |.........0......|
|
||||
00000120 0c 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......|
|
||||
00000130 0c 00 00 00 00 00 00 00 03 00 00 00 03 00 00 00 |................|
|
||||
00000140 01 00 00 00 00 00 00 00 5f 5f 64 61 74 61 00 00 |........__data..|
|
||||
00000150 00 00 00 00 00 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..|
|
||||
00000160 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 |.........0......|
|
||||
00000170 0c 00 00 00 00 00 00 00 00 20 00 00 06 00 00 00 |......... ......|
|
||||
00000180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000190 00 00 00 00 00 00 00 00 05 00 00 00 b8 00 00 00 |................|
|
||||
000001a0 04 00 00 00 2a 00 00 00 00 00 00 00 00 00 00 00 |....*...........|
|
||||
000001b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00000220 00 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......|
|
||||
00000230 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00001000 b8 04 00 00 02 bf 01 00 00 00 be 00 30 00 00 ba |............0...|
|
||||
00001010 0c 00 00 00 0f 05 b8 01 00 00 02 bf 09 00 00 00 |................|
|
||||
00001020 0f 05 f4 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00001030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00002000 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a |hello world.|
|
||||
0000200c
|
||||
34
src/cmd/link/testdata/macho.amd64.helloro
vendored
34
src/cmd/link/testdata/macho.amd64.helloro
vendored
@@ -1,34 +0,0 @@
|
||||
00000000 cf fa ed fe 07 00 00 01 03 00 00 00 02 00 00 00 |................|
|
||||
00000010 03 00 00 00 e8 01 00 00 01 00 00 00 00 00 00 00 |................|
|
||||
00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE|
|
||||
00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............|
|
||||
00000040 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000060 00 00 00 00 00 00 00 00 19 00 00 00 e8 00 00 00 |................|
|
||||
00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
00000080 00 10 00 00 00 00 00 00 0c 20 00 00 00 00 00 00 |......... ......|
|
||||
00000090 00 00 00 00 00 00 00 00 0c 20 00 00 00 00 00 00 |......... ......|
|
||||
000000a0 07 00 00 00 05 00 00 00 02 00 00 00 00 00 00 00 |................|
|
||||
000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........|
|
||||
000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
000000d0 00 20 00 00 00 00 00 00 23 00 00 00 00 00 00 00 |. ......#.......|
|
||||
000000e0 00 10 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
000000f0 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000100 5f 5f 72 6f 64 61 74 61 00 00 00 00 00 00 00 00 |__rodata........|
|
||||
00000110 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
00000120 00 30 00 00 00 00 00 00 0c 00 00 00 00 00 00 00 |.0..............|
|
||||
00000130 00 20 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |. ..............|
|
||||
00000140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000150 05 00 00 00 b8 00 00 00 04 00 00 00 2a 00 00 00 |............*...|
|
||||
00000160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
000001e0 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |. ..............|
|
||||
000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00001000 b8 04 00 00 02 bf 01 00 00 00 be 00 30 00 00 ba |............0...|
|
||||
00001010 0c 00 00 00 0f 05 b8 01 00 00 02 bf 00 00 00 00 |................|
|
||||
00001020 0f 05 f4 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00001030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00002000 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a |hello world.|
|
||||
0000200c
|
||||
BIN
src/cmd/link/testdata/pclntab.6
vendored
BIN
src/cmd/link/testdata/pclntab.6
vendored
Binary file not shown.
1751
src/cmd/link/testdata/pclntab.s
vendored
1751
src/cmd/link/testdata/pclntab.s
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,11 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
// round returns size rounded up to the next multiple of align;
|
||||
// align must be a power of two.
|
||||
func round(size, align Addr) Addr {
|
||||
return (size + align - 1) &^ (align - 1)
|
||||
}
|
||||
@@ -1,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
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
@@ -39,6 +45,7 @@ if [ ! -f make.bash ]; then
|
||||
exit 1
|
||||
fi
|
||||
GOOS=$GOHOSTOS GOARCH=$GOHOSTARCH ./make.bash
|
||||
unset GOROOT
|
||||
|
||||
# Build zip file embedded in package syscall.
|
||||
gobin=${GOBIN:-$(pwd)/../bin}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,7 +245,8 @@ func (z *Writer) Flush() error {
|
||||
return z.err
|
||||
}
|
||||
|
||||
// Close closes the Writer. It does not close the underlying io.Writer.
|
||||
// Close closes the Writer, flushing any unwritten data to the underlying
|
||||
// io.Writer, but does not close the underlying io.Writer.
|
||||
func (z *Writer) Close() error {
|
||||
if z.err != nil {
|
||||
return z.err
|
||||
|
||||
@@ -174,7 +174,8 @@ func (z *Writer) Flush() error {
|
||||
return z.err
|
||||
}
|
||||
|
||||
// Calling Close does not close the wrapped io.Writer originally passed to NewWriter.
|
||||
// Close closes the Writer, flushing any unwritten data to the underlying
|
||||
// io.Writer, but does not close the underlying io.Writer.
|
||||
func (z *Writer) Close() error {
|
||||
if !z.wroteHeader {
|
||||
z.err = z.writeHeader()
|
||||
|
||||
@@ -53,11 +53,14 @@ func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (out [
|
||||
if err := checkPub(&priv.PublicKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
valid, out, err := decryptPKCS1v15(rand, priv, ciphertext)
|
||||
if err == nil && valid == 0 {
|
||||
err = ErrDecryption
|
||||
valid, out, index, err := decryptPKCS1v15(rand, priv, ciphertext)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if valid == 0 {
|
||||
return nil, ErrDecryption
|
||||
}
|
||||
out = out[index:]
|
||||
return
|
||||
}
|
||||
|
||||
@@ -80,21 +83,32 @@ func DecryptPKCS1v15SessionKey(rand io.Reader, priv *PrivateKey, ciphertext []by
|
||||
}
|
||||
k := (priv.N.BitLen() + 7) / 8
|
||||
if k-(len(key)+3+8) < 0 {
|
||||
err = ErrDecryption
|
||||
return
|
||||
return ErrDecryption
|
||||
}
|
||||
|
||||
valid, msg, err := decryptPKCS1v15(rand, priv, ciphertext)
|
||||
valid, em, index, err := decryptPKCS1v15(rand, priv, ciphertext)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
valid &= subtle.ConstantTimeEq(int32(len(msg)), int32(len(key)))
|
||||
subtle.ConstantTimeCopy(valid, key, msg)
|
||||
if len(em) != k {
|
||||
// This should be impossible because decryptPKCS1v15 always
|
||||
// returns the full slice.
|
||||
return ErrDecryption
|
||||
}
|
||||
|
||||
valid &= subtle.ConstantTimeEq(int32(len(em)-index), int32(len(key)))
|
||||
subtle.ConstantTimeCopy(valid, key, em[len(em)-len(key):])
|
||||
return
|
||||
}
|
||||
|
||||
func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid int, msg []byte, err error) {
|
||||
// decryptPKCS1v15 decrypts ciphertext using priv and blinds the operation if
|
||||
// rand is not nil. It returns one or zero in valid that indicates whether the
|
||||
// plaintext was correctly structured. In either case, the plaintext is
|
||||
// returned in em so that it may be read independently of whether it was valid
|
||||
// in order to maintain constant memory access patterns. If the plaintext was
|
||||
// valid then index contains the index of the original message in em.
|
||||
func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid int, em []byte, index int, err error) {
|
||||
k := (priv.N.BitLen() + 7) / 8
|
||||
if k < 11 {
|
||||
err = ErrDecryption
|
||||
@@ -107,7 +121,7 @@ func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid
|
||||
return
|
||||
}
|
||||
|
||||
em := leftPad(m.Bytes(), k)
|
||||
em = leftPad(m.Bytes(), k)
|
||||
firstByteIsZero := subtle.ConstantTimeByteEq(em[0], 0)
|
||||
secondByteIsTwo := subtle.ConstantTimeByteEq(em[1], 2)
|
||||
|
||||
@@ -115,8 +129,7 @@ func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid
|
||||
// octets, followed by a 0, followed by the message.
|
||||
// lookingForIndex: 1 iff we are still looking for the zero.
|
||||
// index: the offset of the first zero byte.
|
||||
var lookingForIndex, index int
|
||||
lookingForIndex = 1
|
||||
lookingForIndex := 1
|
||||
|
||||
for i := 2; i < len(em); i++ {
|
||||
equals0 := subtle.ConstantTimeByteEq(em[i], 0)
|
||||
@@ -129,8 +142,8 @@ func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid
|
||||
validPS := subtle.ConstantTimeLessOrEq(2+8, index)
|
||||
|
||||
valid = firstByteIsZero & secondByteIsTwo & (^lookingForIndex & 1) & validPS
|
||||
msg = em[index+1:]
|
||||
return
|
||||
index = subtle.ConstantTimeSelect(valid, index+1, 0)
|
||||
return valid, em, index, nil
|
||||
}
|
||||
|
||||
// nonZeroRandomBytes fills the given slice with non-zero random octets.
|
||||
|
||||
@@ -227,6 +227,26 @@ func TestUnpaddedSignature(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestShortSessionKey(t *testing.T) {
|
||||
// This tests that attempting to decrypt a session key where the
|
||||
// ciphertext is too small doesn't run outside the array bounds.
|
||||
ciphertext, err := EncryptPKCS1v15(rand.Reader, &rsaPrivateKey.PublicKey, []byte{1})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to encrypt short message: %s", err)
|
||||
}
|
||||
|
||||
var key [32]byte
|
||||
if err := DecryptPKCS1v15SessionKey(nil, rsaPrivateKey, ciphertext, key[:]); err != nil {
|
||||
t.Fatalf("Failed to decrypt short message: %s", err)
|
||||
}
|
||||
|
||||
for _, v := range key {
|
||||
if v != 0 {
|
||||
t.Fatal("key was modified when ciphertext was invalid")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// In order to generate new test vectors you'll need the PEM form of this key:
|
||||
// -----BEGIN RSA PRIVATE KEY-----
|
||||
// MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0
|
||||
|
||||
@@ -49,9 +49,14 @@ func ConstantTimeEq(x, y int32) int {
|
||||
return int(z & 1)
|
||||
}
|
||||
|
||||
// ConstantTimeCopy copies the contents of y into x iff v == 1. If v == 0, x is left unchanged.
|
||||
// Its behavior is undefined if v takes any other value.
|
||||
// ConstantTimeCopy copies the contents of y into x (a slice of equal length)
|
||||
// if v == 1. If v == 0, x is left unchanged. Its behavior is undefined if v
|
||||
// takes any other value.
|
||||
func ConstantTimeCopy(v int, x, y []byte) {
|
||||
if len(x) != len(y) {
|
||||
panic("subtle: slices have different lengths")
|
||||
}
|
||||
|
||||
xmask := byte(v - 1)
|
||||
ymask := byte(^(v - 1))
|
||||
for i := 0; i < len(x); i++ {
|
||||
|
||||
@@ -214,6 +214,10 @@ Curves:
|
||||
func (hs *serverHandshakeState) checkForResumption() bool {
|
||||
c := hs.c
|
||||
|
||||
if c.config.SessionTicketsDisabled {
|
||||
return false
|
||||
}
|
||||
|
||||
var ok bool
|
||||
if hs.sessionState, ok = c.decryptTicket(hs.clientHello.sessionTicket); !ok {
|
||||
return false
|
||||
|
||||
@@ -557,6 +557,32 @@ func TestResumption(t *testing.T) {
|
||||
runServerTestTLS12(t, test)
|
||||
}
|
||||
|
||||
func TestResumptionDisabled(t *testing.T) {
|
||||
sessionFilePath := tempFile("")
|
||||
defer os.Remove(sessionFilePath)
|
||||
|
||||
config := *testConfig
|
||||
|
||||
test := &serverTest{
|
||||
name: "IssueTicketPreDisable",
|
||||
command: []string{"openssl", "s_client", "-cipher", "RC4-SHA", "-sess_out", sessionFilePath},
|
||||
config: &config,
|
||||
}
|
||||
runServerTestTLS12(t, test)
|
||||
|
||||
config.SessionTicketsDisabled = true
|
||||
|
||||
test = &serverTest{
|
||||
name: "ResumeDisabled",
|
||||
command: []string{"openssl", "s_client", "-cipher", "RC4-SHA", "-sess_in", sessionFilePath},
|
||||
config: &config,
|
||||
}
|
||||
runServerTestTLS12(t, test)
|
||||
|
||||
// One needs to manually confirm that the handshake in the golden data
|
||||
// file for ResumeDisabled does not include a resumption handshake.
|
||||
}
|
||||
|
||||
// cert.pem and key.pem were generated with generate_cert.go
|
||||
// Thus, they have no ExtKeyUsage fields and trigger an error
|
||||
// when verification is turned on.
|
||||
|
||||
87
src/pkg/crypto/tls/testdata/Server-TLSv12-IssueTicketPreDisable
vendored
Normal file
87
src/pkg/crypto/tls/testdata/Server-TLSv12-IssueTicketPreDisable
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
>>> Flow 1 (client to server)
|
||||
00000000 16 03 01 00 60 01 00 00 5c 03 03 54 23 54 02 17 |....`...\..T#T..|
|
||||
00000010 f3 53 13 3d 48 88 c3 19 b9 d1 3d 33 7f f5 99 56 |.S.=H.....=3...V|
|
||||
00000020 04 71 1b d9 d5 64 8a 0d 4a 54 00 00 00 04 00 05 |.q...d..JT......|
|
||||
00000030 00 ff 01 00 00 2f 00 23 00 00 00 0d 00 22 00 20 |...../.#.....". |
|
||||
00000040 06 01 06 02 06 03 05 01 05 02 05 03 04 01 04 02 |................|
|
||||
00000050 04 03 03 01 03 02 03 03 02 01 02 02 02 03 01 01 |................|
|
||||
00000060 00 0f 00 01 01 |.....|
|
||||
>>> Flow 2 (server to client)
|
||||
00000000 16 03 03 00 35 02 00 00 31 03 03 00 00 00 00 00 |....5...1.......|
|
||||
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 05 00 00 |................|
|
||||
00000030 09 00 23 00 00 ff 01 00 01 00 16 03 03 02 be 0b |..#.............|
|
||||
00000040 00 02 ba 00 02 b7 00 02 b4 30 82 02 b0 30 82 02 |.........0...0..|
|
||||
00000050 19 a0 03 02 01 02 02 09 00 85 b0 bb a4 8a 7f b8 |................|
|
||||
00000060 ca 30 0d 06 09 2a 86 48 86 f7 0d 01 01 05 05 00 |.0...*.H........|
|
||||
00000070 30 45 31 0b 30 09 06 03 55 04 06 13 02 41 55 31 |0E1.0...U....AU1|
|
||||
00000080 13 30 11 06 03 55 04 08 13 0a 53 6f 6d 65 2d 53 |.0...U....Some-S|
|
||||
00000090 74 61 74 65 31 21 30 1f 06 03 55 04 0a 13 18 49 |tate1!0...U....I|
|
||||
000000a0 6e 74 65 72 6e 65 74 20 57 69 64 67 69 74 73 20 |nternet Widgits |
|
||||
000000b0 50 74 79 20 4c 74 64 30 1e 17 0d 31 30 30 34 32 |Pty Ltd0...10042|
|
||||
000000c0 34 30 39 30 39 33 38 5a 17 0d 31 31 30 34 32 34 |4090938Z..110424|
|
||||
000000d0 30 39 30 39 33 38 5a 30 45 31 0b 30 09 06 03 55 |090938Z0E1.0...U|
|
||||
000000e0 04 06 13 02 41 55 31 13 30 11 06 03 55 04 08 13 |....AU1.0...U...|
|
||||
000000f0 0a 53 6f 6d 65 2d 53 74 61 74 65 31 21 30 1f 06 |.Some-State1!0..|
|
||||
00000100 03 55 04 0a 13 18 49 6e 74 65 72 6e 65 74 20 57 |.U....Internet W|
|
||||
00000110 69 64 67 69 74 73 20 50 74 79 20 4c 74 64 30 81 |idgits Pty Ltd0.|
|
||||
00000120 9f 30 0d 06 09 2a 86 48 86 f7 0d 01 01 01 05 00 |.0...*.H........|
|
||||
00000130 03 81 8d 00 30 81 89 02 81 81 00 bb 79 d6 f5 17 |....0.......y...|
|
||||
00000140 b5 e5 bf 46 10 d0 dc 69 be e6 2b 07 43 5a d0 03 |...F...i..+.CZ..|
|
||||
00000150 2d 8a 7a 43 85 b7 14 52 e7 a5 65 4c 2c 78 b8 23 |-.zC...R..eL,x.#|
|
||||
00000160 8c b5 b4 82 e5 de 1f 95 3b 7e 62 a5 2c a5 33 d6 |........;~b.,.3.|
|
||||
00000170 fe 12 5c 7a 56 fc f5 06 bf fa 58 7b 26 3f b5 cd |..\zV.....X{&?..|
|
||||
00000180 04 d3 d0 c9 21 96 4a c7 f4 54 9f 5a bf ef 42 71 |....!.J..T.Z..Bq|
|
||||
00000190 00 fe 18 99 07 7f 7e 88 7d 7d f1 04 39 c4 a2 2e |......~.}}..9...|
|
||||
000001a0 db 51 c9 7c e3 c0 4c 3b 32 66 01 cf af b1 1d b8 |.Q.|..L;2f......|
|
||||
000001b0 71 9a 1d db db 89 6b ae da 2d 79 02 03 01 00 01 |q.....k..-y.....|
|
||||
000001c0 a3 81 a7 30 81 a4 30 1d 06 03 55 1d 0e 04 16 04 |...0..0...U.....|
|
||||
000001d0 14 b1 ad e2 85 5a cf cb 28 db 69 ce 23 69 de d3 |.....Z..(.i.#i..|
|
||||
000001e0 26 8e 18 88 39 30 75 06 03 55 1d 23 04 6e 30 6c |&...90u..U.#.n0l|
|
||||
000001f0 80 14 b1 ad e2 85 5a cf cb 28 db 69 ce 23 69 de |......Z..(.i.#i.|
|
||||
00000200 d3 26 8e 18 88 39 a1 49 a4 47 30 45 31 0b 30 09 |.&...9.I.G0E1.0.|
|
||||
00000210 06 03 55 04 06 13 02 41 55 31 13 30 11 06 03 55 |..U....AU1.0...U|
|
||||
00000220 04 08 13 0a 53 6f 6d 65 2d 53 74 61 74 65 31 21 |....Some-State1!|
|
||||
00000230 30 1f 06 03 55 04 0a 13 18 49 6e 74 65 72 6e 65 |0...U....Interne|
|
||||
00000240 74 20 57 69 64 67 69 74 73 20 50 74 79 20 4c 74 |t Widgits Pty Lt|
|
||||
00000250 64 82 09 00 85 b0 bb a4 8a 7f b8 ca 30 0c 06 03 |d...........0...|
|
||||
00000260 55 1d 13 04 05 30 03 01 01 ff 30 0d 06 09 2a 86 |U....0....0...*.|
|
||||
00000270 48 86 f7 0d 01 01 05 05 00 03 81 81 00 08 6c 45 |H.............lE|
|
||||
00000280 24 c7 6b b1 59 ab 0c 52 cc f2 b0 14 d7 87 9d 7a |$.k.Y..R.......z|
|
||||
00000290 64 75 b5 5a 95 66 e4 c5 2b 8e ae 12 66 1f eb 4f |du.Z.f..+...f..O|
|
||||
000002a0 38 b3 6e 60 d3 92 fd f7 41 08 b5 25 13 b1 18 7a |8.n`....A..%...z|
|
||||
000002b0 24 fb 30 1d ba ed 98 b9 17 ec e7 d7 31 59 db 95 |$.0.........1Y..|
|
||||
000002c0 d3 1d 78 ea 50 56 5c d5 82 5a 2d 5a 5f 33 c4 b6 |..x.PV\..Z-Z_3..|
|
||||
000002d0 d8 c9 75 90 96 8c 0f 52 98 b5 cd 98 1f 89 20 5f |..u....R...... _|
|
||||
000002e0 f2 a0 1c a3 1b 96 94 dd a9 fd 57 e9 70 e8 26 6d |..........W.p.&m|
|
||||
000002f0 71 99 9b 26 6e 38 50 29 6c 90 a7 bd d9 16 03 03 |q..&n8P)l.......|
|
||||
00000300 00 04 0e 00 00 00 |......|
|
||||
>>> Flow 3 (client to server)
|
||||
00000000 16 03 03 00 86 10 00 00 82 00 80 27 e9 a4 f7 e7 |...........'....|
|
||||
00000010 df 25 de 84 8c 1f d6 e6 c3 11 28 55 9a c1 91 37 |.%........(U...7|
|
||||
00000020 84 f5 ba f8 80 0d ca 50 cb 1e 72 f7 97 6f c2 b2 |.......P..r..o..|
|
||||
00000030 04 4d 13 7c e0 6e a0 1f 91 e1 38 1b a2 c0 55 16 |.M.|.n....8...U.|
|
||||
00000040 7f 29 fc ed 1c 1a cf 72 14 c3 00 c1 dd 36 36 af |.).....r.....66.|
|
||||
00000050 a6 e4 a8 be ba ec 13 d0 1e d0 1d fd e1 5b 27 fd |.............['.|
|
||||
00000060 9a da 2e 12 c8 b0 b9 c2 b9 76 ec 7f 3c 98 b6 63 |.........v..<..c|
|
||||
00000070 bc da f0 07 7a 3d e7 61 f4 2f 12 80 3b f9 3b cc |....z=.a./..;.;.|
|
||||
00000080 05 c8 2f 7e 28 b2 73 bf 97 61 29 14 03 03 00 01 |../~(.s..a).....|
|
||||
00000090 01 16 03 03 00 24 17 59 a9 45 53 46 33 96 50 dd |.....$.Y.ESF3.P.|
|
||||
000000a0 3e 23 aa 91 38 f8 56 4a 2f 1a f2 b1 44 9b ce 17 |>#..8.VJ/...D...|
|
||||
000000b0 6b 8a 89 76 bc 67 b8 8b ba 90 |k..v.g....|
|
||||
>>> Flow 4 (server to client)
|
||||
00000000 16 03 03 00 72 04 00 00 6e 00 00 00 00 00 68 00 |....r...n.....h.|
|
||||
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 65 |...............e|
|
||||
00000020 ea 4b d1 ef ba 2d db 0c ba 9a d4 20 76 57 c8 ec |.K...-..... vW..|
|
||||
00000030 dc 2d 77 fb fb 3b 93 5f 53 e0 14 4f 90 fb d6 55 |.-w..;._S..O...U|
|
||||
00000040 57 8c 8d 0d 25 ea 5d 0d f2 91 e5 12 22 12 ec 7b |W...%.]....."..{|
|
||||
00000050 5f b6 6e fd 07 59 23 24 fc b1 97 ca ea 56 a5 c2 |_.n..Y#$.....V..|
|
||||
00000060 a0 e4 9e 99 64 f2 64 d0 75 7a 46 63 e3 dc 21 ed |....d.d.uzFc..!.|
|
||||
00000070 78 56 e9 e1 ab 66 80 14 03 03 00 01 01 16 03 03 |xV...f..........|
|
||||
00000080 00 24 fc 14 68 07 17 1f df b7 84 cb fd c1 e0 e4 |.$..h...........|
|
||||
00000090 f2 1a ea 34 b5 00 7f 70 be c8 1c 0a d6 55 e3 57 |...4...p.....U.W|
|
||||
000000a0 50 4e 6d 7d 8a 5d 17 03 03 00 21 24 27 50 40 c1 |PNm}.]....!$'P@.|
|
||||
000000b0 c5 bd c7 9f 95 d9 ba 2e 7b 0e db ea a7 31 81 05 |........{....1..|
|
||||
000000c0 75 43 b1 63 cf b8 55 92 ef 76 98 a9 15 03 03 00 |uC.c..U..v......|
|
||||
000000d0 16 d7 ea 3c 79 e7 a6 2f 61 39 ec 4e 95 86 48 5e |...<y../a9.N..H^|
|
||||
000000e0 75 a0 9e 41 42 89 67 |u..AB.g|
|
||||
87
src/pkg/crypto/tls/testdata/Server-TLSv12-ResumeDisabled
vendored
Normal file
87
src/pkg/crypto/tls/testdata/Server-TLSv12-ResumeDisabled
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
>>> Flow 1 (client to server)
|
||||
00000000 16 03 01 00 e8 01 00 00 e4 03 03 54 23 54 02 a5 |...........T#T..|
|
||||
00000010 10 11 0f 6d e5 2d 2f e8 bb 52 b1 38 3f 65 01 43 |...m.-/..R.8?e.C|
|
||||
00000020 36 cc 48 f6 09 22 a1 85 20 28 3c 20 35 8b fe 7a |6.H..".. (< 5..z|
|
||||
00000030 41 3b 59 3a 5d b9 b3 21 f0 62 e9 0d 7b af f5 5d |A;Y:]..!.b..{..]|
|
||||
00000040 fa 65 1a 40 c8 ca cd 74 8c ef d2 fb 00 04 00 05 |.e.@...t........|
|
||||
00000050 00 ff 01 00 00 97 00 23 00 68 00 00 00 00 00 00 |.......#.h......|
|
||||
00000060 00 00 00 00 00 00 00 00 00 00 65 ea 4b d1 ef ba |..........e.K...|
|
||||
00000070 2d db 0c ba 9a d4 20 76 57 c8 ec dc 2d 77 fb fb |-..... vW...-w..|
|
||||
00000080 3b 93 5f 53 e0 14 4f 90 fb d6 55 57 8c 8d 0d 25 |;._S..O...UW...%|
|
||||
00000090 ea 5d 0d f2 91 e5 12 22 12 ec 7b 5f b6 6e fd 07 |.]....."..{_.n..|
|
||||
000000a0 59 23 24 fc b1 97 ca ea 56 a5 c2 a0 e4 9e 99 64 |Y#$.....V......d|
|
||||
000000b0 f2 64 d0 75 7a 46 63 e3 dc 21 ed 78 56 e9 e1 ab |.d.uzFc..!.xV...|
|
||||
000000c0 66 80 00 0d 00 22 00 20 06 01 06 02 06 03 05 01 |f....". ........|
|
||||
000000d0 05 02 05 03 04 01 04 02 04 03 03 01 03 02 03 03 |................|
|
||||
000000e0 02 01 02 02 02 03 01 01 00 0f 00 01 01 |.............|
|
||||
>>> Flow 2 (server to client)
|
||||
00000000 16 03 03 00 31 02 00 00 2d 03 03 00 00 00 00 00 |....1...-.......|
|
||||
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 05 00 00 |................|
|
||||
00000030 05 ff 01 00 01 00 16 03 03 02 be 0b 00 02 ba 00 |................|
|
||||
00000040 02 b7 00 02 b4 30 82 02 b0 30 82 02 19 a0 03 02 |.....0...0......|
|
||||
00000050 01 02 02 09 00 85 b0 bb a4 8a 7f b8 ca 30 0d 06 |.............0..|
|
||||
00000060 09 2a 86 48 86 f7 0d 01 01 05 05 00 30 45 31 0b |.*.H........0E1.|
|
||||
00000070 30 09 06 03 55 04 06 13 02 41 55 31 13 30 11 06 |0...U....AU1.0..|
|
||||
00000080 03 55 04 08 13 0a 53 6f 6d 65 2d 53 74 61 74 65 |.U....Some-State|
|
||||
00000090 31 21 30 1f 06 03 55 04 0a 13 18 49 6e 74 65 72 |1!0...U....Inter|
|
||||
000000a0 6e 65 74 20 57 69 64 67 69 74 73 20 50 74 79 20 |net Widgits Pty |
|
||||
000000b0 4c 74 64 30 1e 17 0d 31 30 30 34 32 34 30 39 30 |Ltd0...100424090|
|
||||
000000c0 39 33 38 5a 17 0d 31 31 30 34 32 34 30 39 30 39 |938Z..1104240909|
|
||||
000000d0 33 38 5a 30 45 31 0b 30 09 06 03 55 04 06 13 02 |38Z0E1.0...U....|
|
||||
000000e0 41 55 31 13 30 11 06 03 55 04 08 13 0a 53 6f 6d |AU1.0...U....Som|
|
||||
000000f0 65 2d 53 74 61 74 65 31 21 30 1f 06 03 55 04 0a |e-State1!0...U..|
|
||||
00000100 13 18 49 6e 74 65 72 6e 65 74 20 57 69 64 67 69 |..Internet Widgi|
|
||||
00000110 74 73 20 50 74 79 20 4c 74 64 30 81 9f 30 0d 06 |ts Pty Ltd0..0..|
|
||||
00000120 09 2a 86 48 86 f7 0d 01 01 01 05 00 03 81 8d 00 |.*.H............|
|
||||
00000130 30 81 89 02 81 81 00 bb 79 d6 f5 17 b5 e5 bf 46 |0.......y......F|
|
||||
00000140 10 d0 dc 69 be e6 2b 07 43 5a d0 03 2d 8a 7a 43 |...i..+.CZ..-.zC|
|
||||
00000150 85 b7 14 52 e7 a5 65 4c 2c 78 b8 23 8c b5 b4 82 |...R..eL,x.#....|
|
||||
00000160 e5 de 1f 95 3b 7e 62 a5 2c a5 33 d6 fe 12 5c 7a |....;~b.,.3...\z|
|
||||
00000170 56 fc f5 06 bf fa 58 7b 26 3f b5 cd 04 d3 d0 c9 |V.....X{&?......|
|
||||
00000180 21 96 4a c7 f4 54 9f 5a bf ef 42 71 00 fe 18 99 |!.J..T.Z..Bq....|
|
||||
00000190 07 7f 7e 88 7d 7d f1 04 39 c4 a2 2e db 51 c9 7c |..~.}}..9....Q.||
|
||||
000001a0 e3 c0 4c 3b 32 66 01 cf af b1 1d b8 71 9a 1d db |..L;2f......q...|
|
||||
000001b0 db 89 6b ae da 2d 79 02 03 01 00 01 a3 81 a7 30 |..k..-y........0|
|
||||
000001c0 81 a4 30 1d 06 03 55 1d 0e 04 16 04 14 b1 ad e2 |..0...U.........|
|
||||
000001d0 85 5a cf cb 28 db 69 ce 23 69 de d3 26 8e 18 88 |.Z..(.i.#i..&...|
|
||||
000001e0 39 30 75 06 03 55 1d 23 04 6e 30 6c 80 14 b1 ad |90u..U.#.n0l....|
|
||||
000001f0 e2 85 5a cf cb 28 db 69 ce 23 69 de d3 26 8e 18 |..Z..(.i.#i..&..|
|
||||
00000200 88 39 a1 49 a4 47 30 45 31 0b 30 09 06 03 55 04 |.9.I.G0E1.0...U.|
|
||||
00000210 06 13 02 41 55 31 13 30 11 06 03 55 04 08 13 0a |...AU1.0...U....|
|
||||
00000220 53 6f 6d 65 2d 53 74 61 74 65 31 21 30 1f 06 03 |Some-State1!0...|
|
||||
00000230 55 04 0a 13 18 49 6e 74 65 72 6e 65 74 20 57 69 |U....Internet Wi|
|
||||
00000240 64 67 69 74 73 20 50 74 79 20 4c 74 64 82 09 00 |dgits Pty Ltd...|
|
||||
00000250 85 b0 bb a4 8a 7f b8 ca 30 0c 06 03 55 1d 13 04 |........0...U...|
|
||||
00000260 05 30 03 01 01 ff 30 0d 06 09 2a 86 48 86 f7 0d |.0....0...*.H...|
|
||||
00000270 01 01 05 05 00 03 81 81 00 08 6c 45 24 c7 6b b1 |..........lE$.k.|
|
||||
00000280 59 ab 0c 52 cc f2 b0 14 d7 87 9d 7a 64 75 b5 5a |Y..R.......zdu.Z|
|
||||
00000290 95 66 e4 c5 2b 8e ae 12 66 1f eb 4f 38 b3 6e 60 |.f..+...f..O8.n`|
|
||||
000002a0 d3 92 fd f7 41 08 b5 25 13 b1 18 7a 24 fb 30 1d |....A..%...z$.0.|
|
||||
000002b0 ba ed 98 b9 17 ec e7 d7 31 59 db 95 d3 1d 78 ea |........1Y....x.|
|
||||
000002c0 50 56 5c d5 82 5a 2d 5a 5f 33 c4 b6 d8 c9 75 90 |PV\..Z-Z_3....u.|
|
||||
000002d0 96 8c 0f 52 98 b5 cd 98 1f 89 20 5f f2 a0 1c a3 |...R...... _....|
|
||||
000002e0 1b 96 94 dd a9 fd 57 e9 70 e8 26 6d 71 99 9b 26 |......W.p.&mq..&|
|
||||
000002f0 6e 38 50 29 6c 90 a7 bd d9 16 03 03 00 04 0e 00 |n8P)l...........|
|
||||
00000300 00 00 |..|
|
||||
>>> Flow 3 (client to server)
|
||||
00000000 16 03 03 00 86 10 00 00 82 00 80 ae 02 dd 1f 1a |................|
|
||||
00000010 86 83 f5 2f 82 46 4b 29 58 aa a1 b3 56 8b 4e 40 |.../.FK)X...V.N@|
|
||||
00000020 ef 23 65 67 ad 48 e5 e1 fd ae dd bf 68 fd bd a6 |.#eg.H......h...|
|
||||
00000030 13 a0 7e 05 ab f7 20 e1 6a 4e d1 37 93 08 1d c9 |..~... .jN.7....|
|
||||
00000040 37 e0 b5 34 28 bf 20 45 45 da 0f 7e 51 a7 c6 ae |7..4(. EE..~Q...|
|
||||
00000050 61 6c 07 1b 73 ef da 6e 25 c4 ed be e3 3f da ae |al..s..n%....?..|
|
||||
00000060 cd 3c 17 9c 2e ee fb 47 9d b3 a1 b2 c3 5d e0 83 |.<.....G.....]..|
|
||||
00000070 74 20 37 2d 72 d6 d0 4d 58 0e 26 1c 50 22 95 08 |t 7-r..MX.&.P"..|
|
||||
00000080 7d e0 5f 86 99 9e 2c 2e a7 a0 7f 14 03 03 00 01 |}._...,.........|
|
||||
00000090 01 16 03 03 00 24 a2 ab 41 25 a5 cf 04 18 1d 98 |.....$..A%......|
|
||||
000000a0 88 6c 59 21 86 33 54 f4 35 b4 21 6e a5 29 d5 6e |.lY!.3T.5.!n.).n|
|
||||
000000b0 3d 08 72 b0 af 46 b5 8f 6b 86 |=.r..F..k.|
|
||||
>>> Flow 4 (server to client)
|
||||
00000000 14 03 03 00 01 01 16 03 03 00 24 59 20 4d c2 17 |..........$Y M..|
|
||||
00000010 8b 3c 9b 33 d9 f9 ef fb 80 18 1f 67 a7 58 12 89 |.<.3.......g.X..|
|
||||
00000020 4e 73 0f 2d 7b e6 c4 a6 79 73 01 da 22 e8 54 17 |Ns.-{...ys..".T.|
|
||||
00000030 03 03 00 21 36 ca 64 0f 4a 12 a5 50 3d 97 bb 39 |...!6.d.J..P=..9|
|
||||
00000040 02 fc ed d1 82 6a 9a 2e 21 79 f6 e1 b3 cc 32 db |.....j..!y....2.|
|
||||
00000050 0f 5d b3 fb a5 15 03 03 00 16 51 f4 be 57 7a df |.]........Q..Wz.|
|
||||
00000060 f1 f2 bd b5 51 5e 45 80 be 0b 9a 0c d1 19 3c 79 |....Q^E.......<y|
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user