Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5cf567359 | ||
|
|
caffc2831c | ||
|
|
6f643ce13f | ||
|
|
16c42047ef | ||
|
|
b8af3fd1bd | ||
|
|
4afe4c803e | ||
|
|
2d8ecac3d0 | ||
|
|
e805bf3945 | ||
|
|
343cd02d55 | ||
|
|
bfccf7b3e1 | ||
|
|
6b42ef7093 | ||
|
|
7bc40ffb05 | ||
|
|
6e3041b927 | ||
|
|
5d343bdfb1 | ||
|
|
ce8d5ba53e | ||
|
|
62e5355fdd | ||
|
|
9a80b5e1f3 | ||
|
|
036b8fd40b | ||
|
|
6c6aabe0bc |
@@ -34,6 +34,7 @@ We encourage all Go users to subscribe to
|
||||
<p>A <a href="/doc/devel/release.html">summary</a> of the changes between Go releases. Notes for the major releases:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="/doc/go1.6">Go 1.6</a> <small>(February 2016)</small></li>
|
||||
<li><a href="/doc/go1.5">Go 1.5</a> <small>(August 2015)</small></li>
|
||||
<li><a href="/doc/go1.4">Go 1.4</a> <small>(December 2014)</small></li>
|
||||
<li><a href="/doc/go1.3">Go 1.3</a> <small>(June 2014)</small></li>
|
||||
|
||||
@@ -30,6 +30,21 @@ to fix critical security problems in both Go 1.4 and Go 1.5 as they arise.
|
||||
See the <a href="/security">security policy</a> for more details.
|
||||
</p>
|
||||
|
||||
<h2 id="go1.6">go1.6 (released 2016/02/17)</h2>
|
||||
|
||||
<p>
|
||||
Go 1.6 is a major release of Go.
|
||||
Read the <a href="/doc/go1.6">Go 1.6 Release Notes</a> for more information.
|
||||
</p>
|
||||
|
||||
<h3 id="go1.6.minor">Minor revisions</h3>
|
||||
|
||||
<p>
|
||||
go1.6.1 (released 2016/04/12) includes two security fixes.
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.6.1">Go
|
||||
1.6.1 milestone</a> on our issue tracker for details.
|
||||
</p>
|
||||
|
||||
<h2 id="go1.5">go1.5 (released 2015/08/19)</h2>
|
||||
|
||||
<p>
|
||||
@@ -62,6 +77,13 @@ See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.5.3">Go 1
|
||||
and the <a href="https://golang.org/s/go153announce">release announcement</a> for details.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
go1.5.4 (released 2016/04/12) includes two security fixes.
|
||||
It contains the same fixes as Go 1.6.1 and was released at the same time.
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.6.1">Go
|
||||
1.6.1 milestone</a> on our issue tracker for details.
|
||||
</p>
|
||||
|
||||
<h2 id="go1.4">go1.4 (released 2014/12/10)</h2>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -199,7 +199,7 @@ Then clone the repository and check out the latest release tag:</p>
|
||||
<pre>
|
||||
$ git clone https://go.googlesource.com/go
|
||||
$ cd go
|
||||
$ git checkout go1.5.3
|
||||
$ git checkout go1.6
|
||||
</pre>
|
||||
|
||||
<h2 id="head">(Optional) Switch to the master branch</h2>
|
||||
@@ -378,7 +378,7 @@ New releases are announced on the
|
||||
<a href="//groups.google.com/group/golang-announce">golang-announce</a>
|
||||
mailing list.
|
||||
Each announcement mentions the latest release tag, for instance,
|
||||
<code>go1.5.3</code>.
|
||||
<code>go1.6</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -44,9 +44,6 @@ go src=..
|
||||
gofmt_test.go
|
||||
testdata
|
||||
+
|
||||
newlink
|
||||
testdata
|
||||
+
|
||||
archive
|
||||
tar
|
||||
testdata
|
||||
|
||||
57
src/cmd/dist/deps.go
vendored
57
src/cmd/dist/deps.go
vendored
@@ -5,43 +5,44 @@ package main
|
||||
var builddeps = map[string][]string{
|
||||
"bufio": {"bytes", "errors", "internal/race", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "unicode", "unicode/utf8"},
|
||||
"bytes": {"errors", "internal/race", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "unicode", "unicode/utf8"},
|
||||
"compress/flate": {"bufio", "bytes", "errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"compress/zlib": {"bufio", "bytes", "compress/flate", "errors", "fmt", "hash", "hash/adler32", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"compress/flate": {"bufio", "bytes", "errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"compress/zlib": {"bufio", "bytes", "compress/flate", "errors", "fmt", "hash", "hash/adler32", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"container/heap": {"runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort"},
|
||||
"crypto": {"errors", "hash", "internal/race", "io", "math", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "unicode/utf8"},
|
||||
"crypto/sha1": {"crypto", "errors", "hash", "internal/race", "io", "math", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "unicode/utf8"},
|
||||
"debug/dwarf": {"encoding/binary", "errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "io", "math", "os", "path", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"debug/elf": {"bufio", "bytes", "compress/flate", "compress/zlib", "debug/dwarf", "encoding/binary", "errors", "fmt", "hash", "hash/adler32", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "io", "math", "os", "path", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"debug/macho": {"bytes", "debug/dwarf", "encoding/binary", "errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "io", "math", "os", "path", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"debug/dwarf": {"encoding/binary", "errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "path", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"debug/elf": {"bufio", "bytes", "compress/flate", "compress/zlib", "debug/dwarf", "encoding/binary", "errors", "fmt", "hash", "hash/adler32", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "path", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"debug/macho": {"bytes", "debug/dwarf", "encoding/binary", "errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "path", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"encoding": {"runtime", "runtime/internal/atomic", "runtime/internal/sys"},
|
||||
"encoding/base64": {"errors", "internal/race", "io", "math", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "unicode/utf8"},
|
||||
"encoding/binary": {"errors", "internal/race", "io", "math", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "unicode/utf8"},
|
||||
"encoding/json": {"bytes", "encoding", "encoding/base64", "errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"encoding/json": {"bytes", "encoding", "encoding/base64", "errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"errors": {"runtime", "runtime/internal/atomic", "runtime/internal/sys"},
|
||||
"flag": {"errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"},
|
||||
"fmt": {"errors", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"},
|
||||
"go/ast": {"bytes", "errors", "fmt", "go/scanner", "go/token", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "io", "math", "os", "path/filepath", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"go/build": {"bufio", "bytes", "errors", "fmt", "go/ast", "go/doc", "go/parser", "go/scanner", "go/token", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "io", "io/ioutil", "log", "math", "net/url", "os", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"go/doc": {"bytes", "errors", "fmt", "go/ast", "go/scanner", "go/token", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "io", "io/ioutil", "math", "net/url", "os", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"go/parser": {"bytes", "errors", "fmt", "go/ast", "go/scanner", "go/token", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "io", "io/ioutil", "math", "os", "path/filepath", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"go/scanner": {"bytes", "errors", "fmt", "go/token", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "io", "math", "os", "path/filepath", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"go/token": {"errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"},
|
||||
"flag": {"errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"},
|
||||
"fmt": {"errors", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"},
|
||||
"go/ast": {"bytes", "errors", "fmt", "go/scanner", "go/token", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "path/filepath", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"go/build": {"bufio", "bytes", "errors", "fmt", "go/ast", "go/doc", "go/parser", "go/scanner", "go/token", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"go/doc": {"bytes", "errors", "fmt", "go/ast", "go/scanner", "go/token", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "math", "net/url", "os", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"go/parser": {"bytes", "errors", "fmt", "go/ast", "go/scanner", "go/token", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "math", "os", "path/filepath", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"go/scanner": {"bytes", "errors", "fmt", "go/token", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "path/filepath", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"go/token": {"errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"},
|
||||
"hash": {"errors", "internal/race", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic"},
|
||||
"hash/adler32": {"errors", "hash", "internal/race", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic"},
|
||||
"internal/race": {"runtime", "runtime/internal/atomic", "runtime/internal/sys"},
|
||||
"internal/singleflight": {"internal/race", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic"},
|
||||
"internal/syscall/windows": {"errors", "internal/race", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "syscall", "unicode/utf16"},
|
||||
"internal/syscall/windows/registry": {"errors", "internal/race", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "syscall", "unicode/utf16"},
|
||||
"internal/syscall/windows": {"errors", "internal/race", "internal/syscall/windows/sysdll", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "syscall", "unicode/utf16"},
|
||||
"internal/syscall/windows/registry": {"errors", "internal/race", "internal/syscall/windows/sysdll", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "syscall", "unicode/utf16"},
|
||||
"internal/syscall/windows/sysdll": {"runtime", "runtime/internal/atomic", "runtime/internal/sys"},
|
||||
"io": {"errors", "internal/race", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic"},
|
||||
"io/ioutil": {"bytes", "errors", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "io", "math", "os", "path/filepath", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"log": {"errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"},
|
||||
"io/ioutil": {"bytes", "errors", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "path/filepath", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"log": {"errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"},
|
||||
"math": {"runtime", "runtime/internal/atomic", "runtime/internal/sys"},
|
||||
"net/url": {"bytes", "errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"os": {"errors", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"},
|
||||
"os/exec": {"bytes", "errors", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "io", "math", "os", "path/filepath", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"os/signal": {"errors", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "io", "os", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"},
|
||||
"net/url": {"bytes", "errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"os": {"errors", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"},
|
||||
"os/exec": {"bytes", "errors", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "path/filepath", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"os/signal": {"errors", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "os", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "syscall", "time", "unicode/utf16", "unicode/utf8"},
|
||||
"path": {"errors", "internal/race", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strings", "sync", "sync/atomic", "unicode", "unicode/utf8"},
|
||||
"path/filepath": {"errors", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "io", "os", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"path/filepath": {"errors", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "os", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"reflect": {"errors", "internal/race", "math", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "sync", "sync/atomic", "unicode/utf8"},
|
||||
"regexp": {"bytes", "errors", "internal/race", "io", "math", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "unicode", "unicode/utf8"},
|
||||
"regexp/syntax": {"bytes", "errors", "internal/race", "io", "math", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "unicode", "unicode/utf8"},
|
||||
@@ -53,12 +54,12 @@ var builddeps = map[string][]string{
|
||||
"strings": {"errors", "internal/race", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "unicode", "unicode/utf8"},
|
||||
"sync": {"internal/race", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync/atomic"},
|
||||
"sync/atomic": {"runtime", "runtime/internal/atomic", "runtime/internal/sys"},
|
||||
"syscall": {"errors", "internal/race", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "unicode/utf16"},
|
||||
"text/template": {"bytes", "errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "io", "io/ioutil", "math", "net/url", "os", "path/filepath", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"text/template/parse": {"bytes", "errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"time": {"errors", "internal/race", "internal/syscall/windows/registry", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "syscall", "unicode/utf16"},
|
||||
"syscall": {"errors", "internal/race", "internal/syscall/windows/sysdll", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "unicode/utf16"},
|
||||
"text/template": {"bytes", "errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "math", "net/url", "os", "path/filepath", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"text/template/parse": {"bytes", "errors", "fmt", "internal/race", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "math", "os", "reflect", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "strconv", "strings", "sync", "sync/atomic", "syscall", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"time": {"errors", "internal/race", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sync", "sync/atomic", "syscall", "unicode/utf16"},
|
||||
"unicode": {"runtime", "runtime/internal/atomic", "runtime/internal/sys"},
|
||||
"unicode/utf16": {"runtime", "runtime/internal/atomic", "runtime/internal/sys"},
|
||||
"unicode/utf8": {"runtime", "runtime/internal/atomic", "runtime/internal/sys"},
|
||||
"cmd/go": {"bufio", "bytes", "compress/flate", "compress/zlib", "container/heap", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding", "encoding/base64", "encoding/binary", "encoding/json", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/race", "internal/singleflight", "internal/syscall/windows", "internal/syscall/windows/registry", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
"cmd/go": {"bufio", "bytes", "compress/flate", "compress/zlib", "container/heap", "crypto", "crypto/sha1", "debug/dwarf", "debug/elf", "debug/macho", "encoding", "encoding/base64", "encoding/binary", "encoding/json", "errors", "flag", "fmt", "go/ast", "go/build", "go/doc", "go/parser", "go/scanner", "go/token", "hash", "hash/adler32", "internal/race", "internal/singleflight", "internal/syscall/windows", "internal/syscall/windows/registry", "internal/syscall/windows/sysdll", "io", "io/ioutil", "log", "math", "net/url", "os", "os/exec", "os/signal", "path", "path/filepath", "reflect", "regexp", "regexp/syntax", "runtime", "runtime/internal/atomic", "runtime/internal/sys", "sort", "strconv", "strings", "sync", "sync/atomic", "syscall", "text/template", "text/template/parse", "time", "unicode", "unicode/utf16", "unicode/utf8"},
|
||||
}
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Automatic symbol generation.
|
||||
|
||||
// TODO(rsc): Handle go.typelink, go.track symbols.
|
||||
// TODO(rsc): Do not handle $f64. and $f32. symbols. Instead, generate those
|
||||
// from the compiler and assemblers as dupok data, and then remove autoData below.
|
||||
package main
|
||||
|
||||
import (
|
||||
"cmd/internal/goobj"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// linkerDefined lists the symbols supplied by other parts of the linker
|
||||
// (runtime.go and layout.go).
|
||||
var linkerDefined = map[string]bool{
|
||||
"runtime.bss": true,
|
||||
"runtime.data": true,
|
||||
"runtime.ebss": true,
|
||||
"runtime.edata": true,
|
||||
"runtime.efunctab": true,
|
||||
"runtime.end": true,
|
||||
"runtime.enoptrbss": true,
|
||||
"runtime.enoptrdata": true,
|
||||
"runtime.erodata": true,
|
||||
"runtime.etext": true,
|
||||
"runtime.etypelink": true,
|
||||
"runtime.functab": true,
|
||||
"runtime.gcbss": true,
|
||||
"runtime.gcdata": true,
|
||||
"runtime.noptrbss": true,
|
||||
"runtime.noptrdata": true,
|
||||
"runtime.pclntab": true,
|
||||
"runtime.rodata": true,
|
||||
"runtime.text": true,
|
||||
"runtime.typelink": true,
|
||||
}
|
||||
|
||||
// isAuto reports whether sym is an automatically-generated data or constant symbol.
|
||||
func (p *Prog) isAuto(sym goobj.SymID) bool {
|
||||
return strings.HasPrefix(sym.Name, "go.weak.") ||
|
||||
strings.HasPrefix(sym.Name, "$f64.") ||
|
||||
strings.HasPrefix(sym.Name, "$f32.") ||
|
||||
linkerDefined[sym.Name]
|
||||
}
|
||||
|
||||
// autoData defines the automatically generated data symbols needed by p.
|
||||
func (p *Prog) autoData() {
|
||||
for sym := range p.Missing {
|
||||
switch {
|
||||
// Floating-point constants that need to be loaded from memory are
|
||||
// written as $f64.{16 hex digits} or $f32.{8 hex digits}; the hex digits
|
||||
// give the IEEE bit pattern of the constant. As far as the layout into
|
||||
// memory is concerned, we interpret these as uint64 or uint32 constants.
|
||||
case strings.HasPrefix(sym.Name, "$f64."), strings.HasPrefix(sym.Name, "$f32."):
|
||||
size := 64
|
||||
if sym.Name[2:4] == "32" {
|
||||
size = 32
|
||||
}
|
||||
delete(p.Missing, sym)
|
||||
fbits, err := strconv.ParseUint(sym.Name[len("$f64."):], 16, size)
|
||||
if err != nil {
|
||||
p.errorf("unexpected floating point symbol %s", sym)
|
||||
continue
|
||||
}
|
||||
data := make([]byte, size/8)
|
||||
if size == 64 {
|
||||
p.byteorder.PutUint64(data, fbits)
|
||||
} else {
|
||||
p.byteorder.PutUint32(data, uint32(fbits))
|
||||
}
|
||||
p.addSym(&Sym{
|
||||
Sym: &goobj.Sym{
|
||||
SymID: sym,
|
||||
Kind: goobj.SRODATA,
|
||||
Size: size / 8,
|
||||
},
|
||||
Bytes: data,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// autoConst defines the automatically generated constant symbols needed by p.
|
||||
func (p *Prog) autoConst() {
|
||||
for sym := range p.Missing {
|
||||
switch {
|
||||
case strings.HasPrefix(sym.Name, "go.weak."):
|
||||
// weak symbol resolves to actual symbol if present, or else nil.
|
||||
delete(p.Missing, sym)
|
||||
targ := sym
|
||||
targ.Name = sym.Name[len("go.weak."):]
|
||||
var addr Addr
|
||||
if s := p.Syms[targ]; s != nil {
|
||||
addr = s.Addr
|
||||
}
|
||||
p.defineConst(sym.Name, addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// defineConst defines a new symbol with the given name and constant address.
|
||||
func (p *Prog) defineConst(name string, addr Addr) {
|
||||
sym := goobj.SymID{Name: name}
|
||||
p.addSym(&Sym{
|
||||
Sym: &goobj.Sym{
|
||||
SymID: sym,
|
||||
Kind: goobj.SCONST,
|
||||
},
|
||||
Package: nil,
|
||||
Addr: addr,
|
||||
})
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Test for auto-generated symbols.
|
||||
|
||||
// There is no test for $f64. and $f32. symbols, because those are
|
||||
// not possible to write in the assembler syntax. Instead of changing
|
||||
// the assembler to allow that, we plan to change the compilers
|
||||
// not to generate such symbols (plain dupok data is sufficient).
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"cmd/internal/goobj"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Each test case is an object file, generated from a corresponding .s file.
|
||||
// The image of the autotab symbol should be a sequence of pairs of
|
||||
// identical 8-byte sequences.
|
||||
var autoTests = []string{
|
||||
"testdata/autosection.6",
|
||||
"testdata/autoweak.6",
|
||||
}
|
||||
|
||||
func TestAuto(t *testing.T) {
|
||||
for _, obj := range autoTests {
|
||||
p := Prog{GOOS: "darwin", GOARCH: "amd64", StartSym: "start"}
|
||||
p.omitRuntime = true
|
||||
p.Error = func(s string) { t.Error(s) }
|
||||
var buf bytes.Buffer
|
||||
p.link(&buf, obj)
|
||||
if p.NumError > 0 {
|
||||
continue // already reported
|
||||
}
|
||||
|
||||
const name = "autotab"
|
||||
sym := p.Syms[goobj.SymID{Name: name}]
|
||||
if sym == nil {
|
||||
t.Errorf("%s is missing %s symbol", obj, name)
|
||||
return
|
||||
}
|
||||
if sym.Size == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
seg := sym.Section.Segment
|
||||
off := sym.Addr - seg.VirtAddr
|
||||
data := seg.Data[off : off+Addr(sym.Size)]
|
||||
if len(data)%16 != 0 {
|
||||
t.Errorf("%s: %s.Size = %d, want multiple of 16", obj, name, len(data))
|
||||
return
|
||||
}
|
||||
Data:
|
||||
for i := 0; i < len(data); i += 16 {
|
||||
have := p.byteorder.Uint64(data[i : i+8])
|
||||
want := p.byteorder.Uint64(data[i+8 : i+16])
|
||||
if have != want {
|
||||
// Look for relocation so we can explain what went wrong.
|
||||
for _, r := range sym.Reloc {
|
||||
if r.Offset == i {
|
||||
t.Errorf("%s: %s+%#x: %s: have %#x want %#x", obj, name, i, r.Sym, have, want)
|
||||
continue Data
|
||||
}
|
||||
}
|
||||
t.Errorf("%s: %s+%#x: have %#x want %#x", obj, name, i, have, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Removal of dead code and data.
|
||||
|
||||
package main
|
||||
|
||||
import "cmd/internal/goobj"
|
||||
|
||||
// dead removes unreachable code and data from the program.
|
||||
// It is basically a mark-sweep garbage collection: traverse all the
|
||||
// symbols reachable from the entry (startSymID) and then delete
|
||||
// the rest.
|
||||
func (p *Prog) dead() {
|
||||
p.Dead = make(map[goobj.SymID]bool)
|
||||
reachable := make(map[goobj.SymID]bool)
|
||||
p.walkDead(p.startSym, reachable)
|
||||
|
||||
for sym := range p.Syms {
|
||||
if !reachable[sym] {
|
||||
delete(p.Syms, sym)
|
||||
p.Dead[sym] = true
|
||||
}
|
||||
}
|
||||
|
||||
for sym := range p.Missing {
|
||||
if !reachable[sym] {
|
||||
delete(p.Missing, sym)
|
||||
p.Dead[sym] = true
|
||||
}
|
||||
}
|
||||
|
||||
p.SymOrder = removeDead(p.SymOrder, reachable)
|
||||
|
||||
for _, pkg := range p.Packages {
|
||||
pkg.Syms = removeDead(pkg.Syms, reachable)
|
||||
}
|
||||
}
|
||||
|
||||
// walkDead traverses the symbols reachable from sym, adding them to reachable.
|
||||
// The caller has verified that reachable[sym] = false.
|
||||
func (p *Prog) walkDead(sym goobj.SymID, reachable map[goobj.SymID]bool) {
|
||||
reachable[sym] = true
|
||||
s := p.Syms[sym]
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
for i := range s.Reloc {
|
||||
r := &s.Reloc[i]
|
||||
if !reachable[r.Sym] {
|
||||
p.walkDead(r.Sym, reachable)
|
||||
}
|
||||
}
|
||||
if s.Func != nil {
|
||||
for _, fdata := range s.Func.FuncData {
|
||||
if fdata.Sym.Name != "" && !reachable[fdata.Sym] {
|
||||
p.walkDead(fdata.Sym, reachable)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// removeDead removes unreachable (dead) symbols from syms,
|
||||
// returning a shortened slice using the same underlying array.
|
||||
func removeDead(syms []*Sym, reachable map[goobj.SymID]bool) []*Sym {
|
||||
keep := syms[:0]
|
||||
for _, sym := range syms {
|
||||
if reachable[sym.SymID] {
|
||||
keep = append(keep, sym)
|
||||
}
|
||||
}
|
||||
return keep
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"cmd/internal/goobj"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Each test case is an object file, generated from a corresponding .s file.
|
||||
// The symbols in the object file with a dead_ prefix are the ones that
|
||||
// should be removed from the program.
|
||||
var deadTests = []string{
|
||||
"testdata/dead.6",
|
||||
}
|
||||
|
||||
func TestDead(t *testing.T) {
|
||||
for _, obj := range deadTests {
|
||||
p := Prog{GOOS: "darwin", GOARCH: "amd64", StartSym: "start"}
|
||||
p.omitRuntime = true
|
||||
p.Error = func(s string) { t.Error(s) }
|
||||
p.init()
|
||||
p.scan(obj)
|
||||
if p.NumError > 0 {
|
||||
continue // already reported
|
||||
}
|
||||
origSyms := copyMap(p.Syms)
|
||||
origMissing := copyMap(p.Missing)
|
||||
origSymOrder := copySlice(p.SymOrder)
|
||||
origPkgSyms := copySlice(p.Packages["main"].Syms)
|
||||
p.dead()
|
||||
checkDeadMap(t, obj, "p.Syms", origSyms, p.Syms)
|
||||
checkDeadMap(t, obj, "p.Missing", origMissing, p.Missing)
|
||||
checkDeadSlice(t, obj, "p.SymOrder", origSymOrder, p.SymOrder)
|
||||
checkDeadSlice(t, obj, `p.Packages["main"].Syms`, origPkgSyms, p.Packages["main"].Syms)
|
||||
}
|
||||
}
|
||||
|
||||
func copyMap(m interface{}) interface{} {
|
||||
v := reflect.ValueOf(m)
|
||||
out := reflect.MakeMap(v.Type())
|
||||
for _, key := range v.MapKeys() {
|
||||
out.SetMapIndex(key, v.MapIndex(key))
|
||||
}
|
||||
return out.Interface()
|
||||
}
|
||||
|
||||
func checkDeadMap(t *testing.T, obj, name string, old, new interface{}) {
|
||||
vold := reflect.ValueOf(old)
|
||||
vnew := reflect.ValueOf(new)
|
||||
for _, vid := range vold.MapKeys() {
|
||||
id := vid.Interface().(goobj.SymID)
|
||||
if strings.HasPrefix(id.Name, "dead_") {
|
||||
if vnew.MapIndex(vid).IsValid() {
|
||||
t.Errorf("%s: %s contains unnecessary symbol %s", obj, name, id)
|
||||
}
|
||||
} else {
|
||||
if !vnew.MapIndex(vid).IsValid() {
|
||||
t.Errorf("%s: %s is missing symbol %s", obj, name, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, vid := range vnew.MapKeys() {
|
||||
id := vid.Interface().(goobj.SymID)
|
||||
if !vold.MapIndex(vid).IsValid() {
|
||||
t.Errorf("%s: %s contains unexpected symbol %s", obj, name, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func copySlice(x []*Sym) (out []*Sym) {
|
||||
return append(out, x...)
|
||||
}
|
||||
|
||||
func checkDeadSlice(t *testing.T, obj, name string, old, new []*Sym) {
|
||||
for i, s := range old {
|
||||
if strings.HasPrefix(s.Name, "dead_") {
|
||||
continue
|
||||
}
|
||||
if len(new) == 0 {
|
||||
t.Errorf("%s: %s is missing symbol %s\nhave%v\nwant%v", obj, name, s, new, old[i:])
|
||||
return
|
||||
}
|
||||
if new[0].SymID != s.SymID {
|
||||
t.Errorf("%s: %s is incorrect: have %s, want %s\nhave%v\nwant%v", obj, name, new[0].SymID, s.SymID, new, old[i:])
|
||||
return
|
||||
}
|
||||
new = new[1:]
|
||||
}
|
||||
if len(new) > 0 {
|
||||
t.Errorf("%s: %s has unexpected symbols: %v", obj, name, new)
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Generation of debug data structures (in the executable but not mapped at run time).
|
||||
// See also runtime.go.
|
||||
|
||||
package main
|
||||
|
||||
func (p *Prog) debug() {
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// mustParseHexdumpFile returns a block of data generated by
|
||||
// parsing the hex dump in the named file.
|
||||
// If the file cannot be read or does not contain a valid hex dump,
|
||||
// mustParseHexdumpFile calls t.Fatal.
|
||||
func mustParseHexdumpFile(t *testing.T, file string) []byte {
|
||||
hex, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data, err := parseHexdump(string(hex))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// parseHexdump parses the hex dump in text, which should be the
|
||||
// output of "hexdump -C" or Plan 9's "xd -b",
|
||||
// and returns the original data used to produce the dump.
|
||||
// It is meant to enable storing golden binary files as text, so that
|
||||
// changes to the golden files can be seen during code reviews.
|
||||
func parseHexdump(text string) ([]byte, error) {
|
||||
var out []byte
|
||||
for _, line := range strings.Split(text, "\n") {
|
||||
if i := strings.Index(line, "|"); i >= 0 { // remove text dump
|
||||
line = line[:i]
|
||||
}
|
||||
f := strings.Fields(line)
|
||||
if len(f) > 1+16 {
|
||||
return nil, fmt.Errorf("parsing hex dump: too many fields on line %q", line)
|
||||
}
|
||||
if len(f) == 0 || len(f) == 1 && f[0] == "*" { // all zeros block omitted
|
||||
continue
|
||||
}
|
||||
addr64, err := strconv.ParseUint(f[0], 16, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing hex dump: invalid address %q", f[0])
|
||||
}
|
||||
addr := int(addr64)
|
||||
if len(out) < addr {
|
||||
out = append(out, make([]byte, addr-len(out))...)
|
||||
}
|
||||
for _, x := range f[1:] {
|
||||
val, err := strconv.ParseUint(x, 16, 8)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing hexdump: invalid hex byte %q", x)
|
||||
}
|
||||
out = append(out, byte(val))
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func hexdump(data []byte) string {
|
||||
text := hex.Dump(data) + fmt.Sprintf("%08x\n", len(data))
|
||||
text = regexp.MustCompile(`\n([0-9a-f]+(\s+00){16}.*\n)+`).ReplaceAllString(text, "\n*\n")
|
||||
return text
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Executable image layout - address assignment.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"cmd/internal/goobj"
|
||||
)
|
||||
|
||||
// A layoutSection describes a single section to add to the
|
||||
// final executable. Go binaries only have a fixed set of possible
|
||||
// sections, and the symbol kind determines the section.
|
||||
type layoutSection struct {
|
||||
Segment string
|
||||
Section string
|
||||
Kind goobj.SymKind
|
||||
Index int
|
||||
}
|
||||
|
||||
// layout defines the layout of the generated Go executable.
|
||||
// The order of entries here is the order in the executable.
|
||||
// Entries with the same Segment name must be contiguous.
|
||||
var layout = []layoutSection{
|
||||
{Segment: "text", Section: "text", Kind: goobj.STEXT},
|
||||
{Segment: "rodata", Section: "rodata", Kind: goobj.SRODATA},
|
||||
{Segment: "rodata", Section: "functab", Kind: goobj.SPCLNTAB},
|
||||
{Segment: "rodata", Section: "typelink", Kind: goobj.STYPELINK},
|
||||
{Segment: "data", Section: "noptrdata", Kind: goobj.SNOPTRDATA},
|
||||
{Segment: "data", Section: "data", Kind: goobj.SDATA},
|
||||
{Segment: "data", Section: "bss", Kind: goobj.SBSS},
|
||||
{Segment: "data", Section: "noptrbss", Kind: goobj.SNOPTRBSS},
|
||||
|
||||
// Later:
|
||||
// {"rodata", "type", goobj.STYPE},
|
||||
// {"rodata", "string", goobj.SSTRING},
|
||||
// {"rodata", "gostring", goobj.SGOSTRING},
|
||||
// {"rodata", "gofunc", goobj.SGOFUNC},
|
||||
}
|
||||
|
||||
// layoutByKind maps from SymKind to an entry in layout.
|
||||
var layoutByKind []*layoutSection
|
||||
|
||||
func init() {
|
||||
// Build index from symbol type to layout entry.
|
||||
max := 0
|
||||
for _, sect := range layout {
|
||||
if max <= int(sect.Kind) {
|
||||
max = int(sect.Kind) + 1
|
||||
}
|
||||
}
|
||||
layoutByKind = make([]*layoutSection, max)
|
||||
for i := range layout {
|
||||
sect := &layout[i]
|
||||
layoutByKind[sect.Kind] = sect
|
||||
sect.Index = i
|
||||
}
|
||||
}
|
||||
|
||||
// layout arranges symbols into sections and sections into segments,
|
||||
// and then it assigns addresses to segments, sections, and symbols.
|
||||
func (p *Prog) layout() {
|
||||
sections := make([]*Section, len(layout))
|
||||
|
||||
// Assign symbols to sections using index, creating sections as needed.
|
||||
// Could keep sections separated by type during input instead.
|
||||
for _, sym := range p.SymOrder {
|
||||
kind := sym.Kind
|
||||
if kind < 0 || int(kind) >= len(layoutByKind) || layoutByKind[kind] == nil {
|
||||
p.errorf("%s: unexpected symbol kind %v", sym.SymID, kind)
|
||||
continue
|
||||
}
|
||||
lsect := layoutByKind[kind]
|
||||
sect := sections[lsect.Index]
|
||||
if sect == nil {
|
||||
sect = &Section{
|
||||
Name: lsect.Section,
|
||||
Align: 1,
|
||||
}
|
||||
sections[lsect.Index] = sect
|
||||
}
|
||||
if sym.Data.Size > 0 || len(sym.Bytes) > 0 {
|
||||
sect.InFile = true
|
||||
}
|
||||
sym.Section = sect
|
||||
sect.Syms = append(sect.Syms, sym)
|
||||
|
||||
// TODO(rsc): Incorporate alignment information.
|
||||
// First that information needs to be added to the object files.
|
||||
//
|
||||
// if sect.Align < Addr(sym.Align) {
|
||||
// sect.Align = Addr(sym.Align)
|
||||
// }
|
||||
}
|
||||
|
||||
// Assign sections to segments, creating segments as needed.
|
||||
var seg *Segment
|
||||
for i, sect := range sections {
|
||||
if sect == nil {
|
||||
continue
|
||||
}
|
||||
segName := layout[i].Segment
|
||||
|
||||
// Special case: Mach-O does not support "rodata" segment,
|
||||
// so store read-only data in text segment.
|
||||
if p.GOOS == "darwin" && segName == "rodata" {
|
||||
segName = "text"
|
||||
}
|
||||
|
||||
if seg == nil || seg.Name != segName {
|
||||
seg = &Segment{
|
||||
Name: segName,
|
||||
}
|
||||
p.Segments = append(p.Segments, seg)
|
||||
}
|
||||
sect.Segment = seg
|
||||
seg.Sections = append(seg.Sections, sect)
|
||||
}
|
||||
|
||||
// Assign addresses.
|
||||
|
||||
// TODO(rsc): This choice needs to be informed by both
|
||||
// the formatter and the target architecture.
|
||||
// And maybe eventually a command line flag (sigh).
|
||||
const segAlign = 4096
|
||||
|
||||
// TODO(rsc): Use a larger amount on most systems, which will let the
|
||||
// compiler eliminate more nil checks.
|
||||
if p.UnmappedSize == 0 {
|
||||
p.UnmappedSize = segAlign
|
||||
}
|
||||
|
||||
// TODO(rsc): addr := Addr(0) when generating a shared library or PIE.
|
||||
addr := p.UnmappedSize
|
||||
|
||||
// Account for initial file header.
|
||||
hdrVirt, hdrFile := p.formatter.headerSize(p)
|
||||
addr += hdrVirt
|
||||
|
||||
// Assign addresses to segments, sections, symbols.
|
||||
// Assign sizes to segments, sections.
|
||||
startVirt := addr
|
||||
startFile := hdrFile
|
||||
for _, seg := range p.Segments {
|
||||
addr = round(addr, segAlign)
|
||||
seg.VirtAddr = addr
|
||||
seg.FileOffset = startFile + seg.VirtAddr - startVirt
|
||||
for _, sect := range seg.Sections {
|
||||
addr = round(addr, sect.Align)
|
||||
sect.VirtAddr = addr
|
||||
for _, sym := range sect.Syms {
|
||||
// TODO(rsc): Respect alignment once we have that information.
|
||||
sym.Addr = addr
|
||||
addr += Addr(sym.Size)
|
||||
}
|
||||
sect.Size = addr - sect.VirtAddr
|
||||
if sect.InFile {
|
||||
seg.FileSize = addr - seg.VirtAddr
|
||||
}
|
||||
}
|
||||
seg.VirtSize = addr - seg.VirtAddr
|
||||
}
|
||||
|
||||
// Define symbols for section names.
|
||||
var progEnd Addr
|
||||
for i, sect := range sections {
|
||||
name := layout[i].Section
|
||||
var start, end Addr
|
||||
if sect != nil {
|
||||
start = sect.VirtAddr
|
||||
end = sect.VirtAddr + sect.Size
|
||||
}
|
||||
p.defineConst("runtime."+name, start)
|
||||
p.defineConst("runtime.e"+name, end)
|
||||
progEnd = end
|
||||
}
|
||||
p.defineConst("runtime.end", progEnd)
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLayout(t *testing.T) {
|
||||
p := Prog{GOOS: "darwin", GOARCH: "amd64", StartSym: "text_start"}
|
||||
p.omitRuntime = true
|
||||
p.Error = func(s string) { t.Error(s) }
|
||||
var buf bytes.Buffer
|
||||
const obj = "testdata/layout.6"
|
||||
p.link(&buf, obj)
|
||||
if p.NumError > 0 {
|
||||
return // already reported
|
||||
}
|
||||
if len(p.Dead) > 0 {
|
||||
t.Errorf("%s: unexpected dead symbols %v", obj, p.Dead)
|
||||
return
|
||||
}
|
||||
|
||||
for _, sym := range p.SymOrder {
|
||||
if p.isAuto(sym.SymID) {
|
||||
continue
|
||||
}
|
||||
if sym.Section == nil {
|
||||
t.Errorf("%s: symbol %s is missing section", obj, sym)
|
||||
continue
|
||||
}
|
||||
i := strings.Index(sym.Name, "_")
|
||||
if i < 0 {
|
||||
t.Errorf("%s: unexpected symbol %s", obj, sym)
|
||||
continue
|
||||
}
|
||||
if sym.Section.Name != sym.Name[:i] {
|
||||
t.Errorf("%s: symbol %s in section %s, want %s", obj, sym, sym.Section.Name, sym.Name[:i])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"cmd/internal/goobj"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLinkHello(t *testing.T) {
|
||||
p := &Prog{
|
||||
GOOS: "darwin",
|
||||
GOARCH: "amd64",
|
||||
Error: func(s string) { t.Error(s) },
|
||||
StartSym: "_rt0_go",
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
p.link(&buf, "testdata/hello.6")
|
||||
if p.NumError > 0 {
|
||||
return
|
||||
}
|
||||
if p.Syms[goobj.SymID{"_rt0_go", 0}] == nil || p.Syms[goobj.SymID{"hello", 1}] == nil {
|
||||
t.Errorf("Syms = %v, want at least [_rt0_go hello<1>]", p.Syms)
|
||||
}
|
||||
|
||||
// uncomment to leave file behind for execution:
|
||||
if false {
|
||||
ioutil.WriteFile("a.out", buf.Bytes(), 0777)
|
||||
}
|
||||
checkGolden(t, buf.Bytes(), "testdata/link.hello.darwin.amd64")
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Loading of code and data fragments from package files into final image.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"cmd/internal/obj"
|
||||
"os"
|
||||
)
|
||||
|
||||
// load allocates segment images, populates them with data
|
||||
// read from package files, and applies relocations to the data.
|
||||
func (p *Prog) load() {
|
||||
// TODO(rsc): mmap the output file and store the data directly.
|
||||
// That will make writing the output file more efficient.
|
||||
for _, seg := range p.Segments {
|
||||
seg.Data = make([]byte, seg.FileSize)
|
||||
}
|
||||
for _, pkg := range p.Packages {
|
||||
p.loadPackage(pkg)
|
||||
}
|
||||
}
|
||||
|
||||
// loadPackage loads and relocates data for all the
|
||||
// symbols needed in the given package.
|
||||
func (p *Prog) loadPackage(pkg *Package) {
|
||||
if pkg.File == "" {
|
||||
// This "package" contains internally generated symbols only.
|
||||
// All such symbols have a sym.Bytes field holding the actual data
|
||||
// (if any), plus relocations.
|
||||
for _, sym := range pkg.Syms {
|
||||
if sym.Bytes == nil {
|
||||
continue
|
||||
}
|
||||
seg := sym.Section.Segment
|
||||
off := sym.Addr - seg.VirtAddr
|
||||
data := seg.Data[off : off+Addr(sym.Size)]
|
||||
copy(data, sym.Bytes)
|
||||
p.relocateSym(sym, data)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Package stored in file.
|
||||
f, err := os.Open(pkg.File)
|
||||
if err != nil {
|
||||
p.errorf("%v", err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// TODO(rsc): Mmap file into memory.
|
||||
|
||||
for _, sym := range pkg.Syms {
|
||||
if sym.Data.Size == 0 {
|
||||
continue
|
||||
}
|
||||
// TODO(rsc): If not using mmap, at least coalesce nearby reads.
|
||||
if sym.Section == nil {
|
||||
p.errorf("internal error: missing section for %s", sym.Name)
|
||||
}
|
||||
seg := sym.Section.Segment
|
||||
off := sym.Addr - seg.VirtAddr
|
||||
if off >= Addr(len(seg.Data)) || off+Addr(sym.Data.Size) > Addr(len(seg.Data)) {
|
||||
p.errorf("internal error: allocated space for %s too small: %d bytes for %d+%d (%d)", sym, len(seg.Data), off, sym.Data.Size, sym.Size)
|
||||
}
|
||||
data := seg.Data[off : off+Addr(sym.Data.Size)]
|
||||
_, err := f.ReadAt(data, sym.Data.Offset)
|
||||
if err != nil {
|
||||
p.errorf("reading %v: %v", sym.SymID, err)
|
||||
}
|
||||
p.relocateSym(sym, data)
|
||||
}
|
||||
}
|
||||
|
||||
// relocateSym applies relocations to sym's data.
|
||||
func (p *Prog) relocateSym(sym *Sym, data []byte) {
|
||||
for i := range sym.Reloc {
|
||||
r := &sym.Reloc[i]
|
||||
targ := p.Syms[r.Sym]
|
||||
if targ == nil {
|
||||
p.errorf("%v: reference to undefined symbol %v", sym, r.Sym)
|
||||
continue
|
||||
}
|
||||
val := targ.Addr + Addr(r.Add)
|
||||
switch r.Type {
|
||||
default:
|
||||
p.errorf("%v: unknown relocation type %d", sym, r.Type)
|
||||
case obj.R_ADDR, obj.R_CALLIND:
|
||||
// ok
|
||||
case obj.R_PCREL, obj.R_CALL:
|
||||
val -= sym.Addr + Addr(r.Offset+r.Size)
|
||||
}
|
||||
frag := data[r.Offset : r.Offset+r.Size]
|
||||
switch r.Size {
|
||||
default:
|
||||
p.errorf("%v: unknown relocation size %d", sym, r.Size)
|
||||
case 4:
|
||||
// TODO(rsc): Check for overflow?
|
||||
p.byteorder.PutUint32(frag, uint32(val))
|
||||
case 8:
|
||||
p.byteorder.PutUint64(frag, uint64(val))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,380 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Mach-O (Darwin) object file writing.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"debug/macho"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// machoFormat is the implementation of formatter.
|
||||
type machoFormat struct{}
|
||||
|
||||
// machoHeader and friends are data structures
|
||||
// corresponding to the Mach-O file header
|
||||
// to be written to disk.
|
||||
|
||||
const (
|
||||
macho64Bit = 1 << 24
|
||||
machoSubCPU386 = 3
|
||||
)
|
||||
|
||||
// machoArch describes a Mach-O target architecture.
|
||||
type machoArch struct {
|
||||
CPU uint32
|
||||
SubCPU uint32
|
||||
}
|
||||
|
||||
// machoHeader is the Mach-O file header.
|
||||
type machoHeader struct {
|
||||
machoArch
|
||||
FileType uint32
|
||||
Loads []*machoLoad
|
||||
Segments []*machoSegment
|
||||
p *Prog // for reporting errors
|
||||
}
|
||||
|
||||
// machoLoad is a Mach-O load command.
|
||||
type machoLoad struct {
|
||||
Type uint32
|
||||
Data []uint32
|
||||
}
|
||||
|
||||
// machoSegment is a Mach-O segment.
|
||||
type machoSegment struct {
|
||||
Name string
|
||||
VirtAddr Addr
|
||||
VirtSize Addr
|
||||
FileOffset Addr
|
||||
FileSize Addr
|
||||
Prot1 uint32
|
||||
Prot2 uint32
|
||||
Flags uint32
|
||||
Sections []*machoSection
|
||||
}
|
||||
|
||||
// machoSection is a Mach-O section, inside a segment.
|
||||
type machoSection struct {
|
||||
Name string
|
||||
Segment string
|
||||
Addr Addr
|
||||
Size Addr
|
||||
Offset uint32
|
||||
Align uint32
|
||||
Reloc uint32
|
||||
Nreloc uint32
|
||||
Flags uint32
|
||||
Res1 uint32
|
||||
Res2 uint32
|
||||
}
|
||||
|
||||
// layout positions the segments and sections in p
|
||||
// to make room for the Mach-O file header.
|
||||
// That is, it edits their VirtAddr fields to adjust for the presence
|
||||
// of the Mach-O header at the beginning of the address space.
|
||||
func (machoFormat) headerSize(p *Prog) (virt, file Addr) {
|
||||
var h machoHeader
|
||||
h.init(p)
|
||||
size := Addr(h.size())
|
||||
size = round(size, 4096)
|
||||
p.HeaderSize = size
|
||||
return size, size
|
||||
}
|
||||
|
||||
// write writes p to w as a Mach-O executable.
|
||||
// layout(p) must have already been called,
|
||||
// and the number, sizes, and addresses of the segments
|
||||
// and sections must not have been modified since the call.
|
||||
func (machoFormat) write(w io.Writer, p *Prog) {
|
||||
var h machoHeader
|
||||
h.init(p)
|
||||
off := Addr(0)
|
||||
enc := h.encode()
|
||||
w.Write(enc)
|
||||
off += Addr(len(enc))
|
||||
for _, seg := range p.Segments {
|
||||
if seg.FileOffset < off {
|
||||
h.p.errorf("mach-o error: invalid file offset")
|
||||
}
|
||||
w.Write(make([]byte, int(seg.FileOffset-off)))
|
||||
if seg.FileSize != Addr(len(seg.Data)) {
|
||||
h.p.errorf("mach-o error: invalid file size")
|
||||
}
|
||||
w.Write(seg.Data)
|
||||
off = seg.FileOffset + Addr(len(seg.Data))
|
||||
}
|
||||
}
|
||||
|
||||
// Conversion of Prog to macho data structures.
|
||||
|
||||
// machoArches maps from GOARCH to machoArch.
|
||||
var machoArches = map[string]machoArch{
|
||||
"amd64": {
|
||||
CPU: uint32(macho.CpuAmd64),
|
||||
SubCPU: uint32(machoSubCPU386),
|
||||
},
|
||||
}
|
||||
|
||||
// init initializes the header h to describe p.
|
||||
func (h *machoHeader) init(p *Prog) {
|
||||
h.p = p
|
||||
h.Segments = nil
|
||||
h.Loads = nil
|
||||
var ok bool
|
||||
h.machoArch, ok = machoArches[p.GOARCH]
|
||||
if !ok {
|
||||
p.errorf("mach-o: unknown target GOARCH %q", p.GOARCH)
|
||||
return
|
||||
}
|
||||
h.FileType = uint32(macho.TypeExec)
|
||||
|
||||
mseg := h.addSegment(p, "__PAGEZERO", nil)
|
||||
mseg.VirtSize = p.UnmappedSize
|
||||
|
||||
for _, seg := range p.Segments {
|
||||
h.addSegment(p, "__"+strings.ToUpper(seg.Name), seg)
|
||||
}
|
||||
|
||||
var data []uint32
|
||||
switch h.CPU {
|
||||
default:
|
||||
p.errorf("mach-o: unknown cpu %#x for GOARCH %q", h.CPU, p.GOARCH)
|
||||
case uint32(macho.CpuAmd64):
|
||||
data = make([]uint32, 2+42)
|
||||
data[0] = 4 // thread type
|
||||
data[1] = 42 // word count
|
||||
data[2+32] = uint32(p.Entry) // RIP register, in two parts
|
||||
data[2+32+1] = uint32(p.Entry >> 32)
|
||||
}
|
||||
|
||||
h.Loads = append(h.Loads, &machoLoad{
|
||||
Type: uint32(macho.LoadCmdUnixThread),
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
|
||||
// addSegment adds to h a Mach-O segment like seg with the given name.
|
||||
func (h *machoHeader) addSegment(p *Prog, name string, seg *Segment) *machoSegment {
|
||||
mseg := &machoSegment{
|
||||
Name: name,
|
||||
}
|
||||
h.Segments = append(h.Segments, mseg)
|
||||
if seg == nil {
|
||||
return mseg
|
||||
}
|
||||
|
||||
mseg.VirtAddr = seg.VirtAddr
|
||||
mseg.VirtSize = seg.VirtSize
|
||||
mseg.FileOffset = round(seg.FileOffset, 4096)
|
||||
mseg.FileSize = seg.FileSize
|
||||
|
||||
if name == "__TEXT" {
|
||||
// Initially RWX, then just RX
|
||||
mseg.Prot1 = 7
|
||||
mseg.Prot2 = 5
|
||||
|
||||
// Text segment maps Mach-O header, needed by dynamic linker.
|
||||
mseg.VirtAddr -= p.HeaderSize
|
||||
mseg.VirtSize += p.HeaderSize
|
||||
mseg.FileOffset -= p.HeaderSize
|
||||
mseg.FileSize += p.HeaderSize
|
||||
} else {
|
||||
// RW
|
||||
mseg.Prot1 = 3
|
||||
mseg.Prot2 = 3
|
||||
}
|
||||
|
||||
for _, sect := range seg.Sections {
|
||||
h.addSection(mseg, seg, sect)
|
||||
}
|
||||
return mseg
|
||||
}
|
||||
|
||||
// addSection adds to mseg a Mach-O section like sect, inside seg, with the given name.
|
||||
func (h *machoHeader) addSection(mseg *machoSegment, seg *Segment, sect *Section) {
|
||||
msect := &machoSection{
|
||||
Name: "__" + sect.Name,
|
||||
Segment: mseg.Name,
|
||||
// Reloc: sect.RelocOffset,
|
||||
// NumReloc: sect.RelocLen / 8,
|
||||
Addr: sect.VirtAddr,
|
||||
Size: sect.Size,
|
||||
}
|
||||
mseg.Sections = append(mseg.Sections, msect)
|
||||
|
||||
for 1<<msect.Align < sect.Align {
|
||||
msect.Align++
|
||||
}
|
||||
|
||||
if off := sect.VirtAddr - seg.VirtAddr; off < seg.FileSize {
|
||||
// Data in file.
|
||||
if sect.Size > seg.FileSize-off {
|
||||
h.p.errorf("mach-o error: section crosses file boundary")
|
||||
}
|
||||
msect.Offset = uint32(seg.FileOffset + off)
|
||||
} else {
|
||||
// Zero filled.
|
||||
msect.Flags |= 1
|
||||
}
|
||||
|
||||
if sect.Name == "text" {
|
||||
msect.Flags |= 0x400 // contains executable instructions
|
||||
}
|
||||
}
|
||||
|
||||
// A machoWriter helps write Mach-O headers.
|
||||
// It is basically a buffer with some helper routines for writing integers.
|
||||
type machoWriter struct {
|
||||
dst []byte
|
||||
tmp [8]byte
|
||||
order binary.ByteOrder
|
||||
is64 bool
|
||||
p *Prog
|
||||
}
|
||||
|
||||
// if64 returns x if w is writing a 64-bit object file; otherwise it returns y.
|
||||
func (w *machoWriter) if64(x, y interface{}) interface{} {
|
||||
if w.is64 {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
||||
|
||||
// encode encodes each of the given arguments into the writer.
|
||||
// It encodes uint32, []uint32, uint64, and []uint64 by writing each value
|
||||
// in turn in the correct byte order for the output file.
|
||||
// It encodes an Addr as a uint64 if writing a 64-bit output file, or else as a uint32.
|
||||
// It encodes []byte and string by writing the raw bytes (no length prefix).
|
||||
// It skips nil values in the args list.
|
||||
func (w *machoWriter) encode(args ...interface{}) {
|
||||
for _, arg := range args {
|
||||
switch arg := arg.(type) {
|
||||
default:
|
||||
w.p.errorf("mach-o error: cannot encode %T", arg)
|
||||
case nil:
|
||||
// skip
|
||||
case []byte:
|
||||
w.dst = append(w.dst, arg...)
|
||||
case string:
|
||||
w.dst = append(w.dst, arg...)
|
||||
case uint32:
|
||||
w.order.PutUint32(w.tmp[:], arg)
|
||||
w.dst = append(w.dst, w.tmp[:4]...)
|
||||
case []uint32:
|
||||
for _, x := range arg {
|
||||
w.order.PutUint32(w.tmp[:], x)
|
||||
w.dst = append(w.dst, w.tmp[:4]...)
|
||||
}
|
||||
case uint64:
|
||||
w.order.PutUint64(w.tmp[:], arg)
|
||||
w.dst = append(w.dst, w.tmp[:8]...)
|
||||
case Addr:
|
||||
if w.is64 {
|
||||
w.order.PutUint64(w.tmp[:], uint64(arg))
|
||||
w.dst = append(w.dst, w.tmp[:8]...)
|
||||
} else {
|
||||
if Addr(uint32(arg)) != arg {
|
||||
w.p.errorf("mach-o error: truncating address %#x to uint32", arg)
|
||||
}
|
||||
w.order.PutUint32(w.tmp[:], uint32(arg))
|
||||
w.dst = append(w.dst, w.tmp[:4]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// segmentSize returns the size of the encoding of seg in bytes.
|
||||
func (w *machoWriter) segmentSize(seg *machoSegment) int {
|
||||
if w.is64 {
|
||||
return 18*4 + 20*4*len(seg.Sections)
|
||||
}
|
||||
return 14*4 + 22*4*len(seg.Sections)
|
||||
}
|
||||
|
||||
// zeroPad returns the string s truncated or padded with NULs to n bytes.
|
||||
func zeroPad(s string, n int) string {
|
||||
if len(s) >= n {
|
||||
return s[:n]
|
||||
}
|
||||
return s + strings.Repeat("\x00", n-len(s))
|
||||
}
|
||||
|
||||
// size returns the encoded size of the header.
|
||||
func (h *machoHeader) size() int {
|
||||
// Could write separate code, but encoding is cheap; encode and throw it away.
|
||||
return len(h.encode())
|
||||
}
|
||||
|
||||
// encode returns the Mach-O encoding of the header.
|
||||
func (h *machoHeader) encode() []byte {
|
||||
w := &machoWriter{p: h.p}
|
||||
w.is64 = h.CPU&macho64Bit != 0
|
||||
w.order = w.p.byteorder
|
||||
|
||||
loadSize := 0
|
||||
for _, seg := range h.Segments {
|
||||
loadSize += w.segmentSize(seg)
|
||||
}
|
||||
for _, l := range h.Loads {
|
||||
loadSize += 4 * (2 + len(l.Data))
|
||||
}
|
||||
|
||||
w.encode(
|
||||
w.if64(macho.Magic64, macho.Magic32),
|
||||
uint32(h.CPU),
|
||||
uint32(h.SubCPU),
|
||||
uint32(h.FileType),
|
||||
uint32(len(h.Loads)+len(h.Segments)),
|
||||
uint32(loadSize),
|
||||
uint32(1),
|
||||
w.if64(uint32(0), nil),
|
||||
)
|
||||
|
||||
for _, seg := range h.Segments {
|
||||
w.encode(
|
||||
w.if64(uint32(macho.LoadCmdSegment64), uint32(macho.LoadCmdSegment)),
|
||||
uint32(w.segmentSize(seg)),
|
||||
zeroPad(seg.Name, 16),
|
||||
seg.VirtAddr,
|
||||
seg.VirtSize,
|
||||
seg.FileOffset,
|
||||
seg.FileSize,
|
||||
seg.Prot1,
|
||||
seg.Prot2,
|
||||
uint32(len(seg.Sections)),
|
||||
seg.Flags,
|
||||
)
|
||||
for _, sect := range seg.Sections {
|
||||
w.encode(
|
||||
zeroPad(sect.Name, 16),
|
||||
zeroPad(seg.Name, 16),
|
||||
sect.Addr,
|
||||
sect.Size,
|
||||
sect.Offset,
|
||||
sect.Align,
|
||||
sect.Reloc,
|
||||
sect.Nreloc,
|
||||
sect.Flags,
|
||||
sect.Res1,
|
||||
sect.Res2,
|
||||
w.if64(uint32(0), nil),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for _, load := range h.Loads {
|
||||
w.encode(
|
||||
load.Type,
|
||||
uint32(4*(2+len(load.Data))),
|
||||
load.Data,
|
||||
)
|
||||
}
|
||||
|
||||
return w.dst
|
||||
}
|
||||
@@ -1,407 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/macho"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Test macho writing by checking that each generated prog can be written
|
||||
// and then read back using debug/macho to get the same prog.
|
||||
// Also check against golden testdata file.
|
||||
var machoWriteTests = []struct {
|
||||
name string
|
||||
golden bool
|
||||
prog *Prog
|
||||
}{
|
||||
// amd64 exit 9
|
||||
{
|
||||
name: "exit9",
|
||||
golden: true,
|
||||
prog: &Prog{
|
||||
GOARCH: "amd64",
|
||||
GOOS: "darwin",
|
||||
UnmappedSize: 0x1000,
|
||||
Entry: 0x1000,
|
||||
Segments: []*Segment{
|
||||
{
|
||||
Name: "text",
|
||||
VirtAddr: 0x1000,
|
||||
VirtSize: 13,
|
||||
FileOffset: 0,
|
||||
FileSize: 13,
|
||||
Data: []byte{
|
||||
0xb8, 0x01, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX
|
||||
0xbf, 0x09, 0x00, 0x00, 0x00, // MOVL $9, DI
|
||||
0x0f, 0x05, // SYSCALL
|
||||
0xf4, // HLT
|
||||
},
|
||||
Sections: []*Section{
|
||||
{
|
||||
Name: "text",
|
||||
VirtAddr: 0x1000,
|
||||
Size: 13,
|
||||
Align: 64,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// amd64 write hello world & exit 9
|
||||
{
|
||||
name: "hello",
|
||||
golden: true,
|
||||
prog: &Prog{
|
||||
GOARCH: "amd64",
|
||||
GOOS: "darwin",
|
||||
UnmappedSize: 0x1000,
|
||||
Entry: 0x1000,
|
||||
Segments: []*Segment{
|
||||
{
|
||||
Name: "text",
|
||||
VirtAddr: 0x1000,
|
||||
VirtSize: 35,
|
||||
FileOffset: 0,
|
||||
FileSize: 35,
|
||||
Data: []byte{
|
||||
0xb8, 0x04, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX
|
||||
0xbf, 0x01, 0x00, 0x00, 0x00, // MOVL $1, DI
|
||||
0xbe, 0x00, 0x30, 0x00, 0x00, // MOVL $0x3000, SI
|
||||
0xba, 0x0c, 0x00, 0x00, 0x00, // MOVL $12, DX
|
||||
0x0f, 0x05, // SYSCALL
|
||||
0xb8, 0x01, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX
|
||||
0xbf, 0x09, 0x00, 0x00, 0x00, // MOVL $9, DI
|
||||
0x0f, 0x05, // SYSCALL
|
||||
0xf4, // HLT
|
||||
},
|
||||
Sections: []*Section{
|
||||
{
|
||||
Name: "text",
|
||||
VirtAddr: 0x1000,
|
||||
Size: 35,
|
||||
Align: 64,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "data",
|
||||
VirtAddr: 0x2000,
|
||||
VirtSize: 12,
|
||||
FileOffset: 0x1000,
|
||||
FileSize: 12,
|
||||
Data: []byte("hello world\n"),
|
||||
Sections: []*Section{
|
||||
{
|
||||
Name: "data",
|
||||
VirtAddr: 0x2000,
|
||||
Size: 12,
|
||||
Align: 64,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// amd64 write hello world from rodata & exit 0
|
||||
{
|
||||
name: "helloro",
|
||||
golden: true,
|
||||
prog: &Prog{
|
||||
GOARCH: "amd64",
|
||||
GOOS: "darwin",
|
||||
UnmappedSize: 0x1000,
|
||||
Entry: 0x1000,
|
||||
Segments: []*Segment{
|
||||
{
|
||||
Name: "text",
|
||||
VirtAddr: 0x1000,
|
||||
VirtSize: 0x100c,
|
||||
FileOffset: 0,
|
||||
FileSize: 0x100c,
|
||||
Data: concat(
|
||||
[]byte{
|
||||
0xb8, 0x04, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX
|
||||
0xbf, 0x01, 0x00, 0x00, 0x00, // MOVL $1, DI
|
||||
0xbe, 0x00, 0x30, 0x00, 0x00, // MOVL $0x3000, SI
|
||||
0xba, 0x0c, 0x00, 0x00, 0x00, // MOVL $12, DX
|
||||
0x0f, 0x05, // SYSCALL
|
||||
0xb8, 0x01, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX
|
||||
0xbf, 0x00, 0x00, 0x00, 0x00, // MOVL $0, DI
|
||||
0x0f, 0x05, // SYSCALL
|
||||
0xf4, // HLT
|
||||
},
|
||||
make([]byte, 0x1000-35),
|
||||
[]byte("hello world\n"),
|
||||
),
|
||||
Sections: []*Section{
|
||||
{
|
||||
Name: "text",
|
||||
VirtAddr: 0x1000,
|
||||
Size: 35,
|
||||
Align: 64,
|
||||
},
|
||||
{
|
||||
Name: "rodata",
|
||||
VirtAddr: 0x2000,
|
||||
Size: 12,
|
||||
Align: 64,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func concat(xs ...[]byte) []byte {
|
||||
var out []byte
|
||||
for _, x := range xs {
|
||||
out = append(out, x...)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func TestMachoWrite(t *testing.T) {
|
||||
for _, tt := range machoWriteTests {
|
||||
name := tt.prog.GOARCH + "." + tt.name
|
||||
prog := cloneProg(tt.prog)
|
||||
prog.init()
|
||||
var f machoFormat
|
||||
vsize, fsize := f.headerSize(prog)
|
||||
shiftProg(prog, vsize, fsize)
|
||||
var buf bytes.Buffer
|
||||
f.write(&buf, prog)
|
||||
if false { // enable to debug
|
||||
ioutil.WriteFile("a.out", buf.Bytes(), 0777)
|
||||
}
|
||||
read, err := machoRead(machoArches[tt.prog.GOARCH], buf.Bytes())
|
||||
if err != nil {
|
||||
t.Errorf("%s: reading mach-o output:\n\t%v", name, err)
|
||||
continue
|
||||
}
|
||||
diffs := diffProg(read, prog)
|
||||
if diffs != nil {
|
||||
t.Errorf("%s: mismatched prog:\n\t%s", name, strings.Join(diffs, "\n\t"))
|
||||
continue
|
||||
}
|
||||
if !tt.golden {
|
||||
continue
|
||||
}
|
||||
checkGolden(t, buf.Bytes(), "testdata/macho."+name)
|
||||
}
|
||||
}
|
||||
|
||||
// machoRead reads the mach-o file in data and returns a corresponding prog.
|
||||
func machoRead(arch machoArch, data []byte) (*Prog, error) {
|
||||
f, err := macho.NewFile(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var errors []string
|
||||
errorf := func(format string, args ...interface{}) {
|
||||
errors = append(errors, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
magic := uint32(0xFEEDFACE)
|
||||
if arch.CPU&macho64Bit != 0 {
|
||||
magic |= 1
|
||||
}
|
||||
if f.Magic != magic {
|
||||
errorf("header: Magic = %#x, want %#x", f.Magic, magic)
|
||||
}
|
||||
if f.Cpu != macho.CpuAmd64 {
|
||||
errorf("header: CPU = %#x, want %#x", f.Cpu, macho.CpuAmd64)
|
||||
}
|
||||
if f.SubCpu != 3 {
|
||||
errorf("header: SubCPU = %#x, want %#x", f.SubCpu, 3)
|
||||
}
|
||||
if f.Type != 2 {
|
||||
errorf("header: FileType = %d, want %d", f.Type, 2)
|
||||
}
|
||||
if f.Flags != 1 {
|
||||
errorf("header: Flags = %d, want %d", f.Flags, 1)
|
||||
}
|
||||
|
||||
msects := f.Sections
|
||||
var limit uint64
|
||||
prog := new(Prog)
|
||||
for _, load := range f.Loads {
|
||||
switch load := load.(type) {
|
||||
default:
|
||||
errorf("unexpected macho load %T %x", load, load.Raw())
|
||||
|
||||
case macho.LoadBytes:
|
||||
if len(load) < 8 || len(load)%4 != 0 {
|
||||
errorf("unexpected load length %d", len(load))
|
||||
continue
|
||||
}
|
||||
cmd := f.ByteOrder.Uint32(load)
|
||||
switch macho.LoadCmd(cmd) {
|
||||
default:
|
||||
errorf("unexpected macho load cmd %s", macho.LoadCmd(cmd))
|
||||
case macho.LoadCmdUnixThread:
|
||||
data := make([]uint32, len(load[8:])/4)
|
||||
binary.Read(bytes.NewReader(load[8:]), f.ByteOrder, data)
|
||||
if len(data) != 44 {
|
||||
errorf("macho thread len(data) = %d, want 42", len(data))
|
||||
continue
|
||||
}
|
||||
if data[0] != 4 {
|
||||
errorf("macho thread type = %d, want 4", data[0])
|
||||
}
|
||||
if data[1] != uint32(len(data))-2 {
|
||||
errorf("macho thread desc len = %d, want %d", data[1], uint32(len(data))-2)
|
||||
continue
|
||||
}
|
||||
for i, val := range data[2:] {
|
||||
switch i {
|
||||
default:
|
||||
if val != 0 {
|
||||
errorf("macho thread data[%d] = %#x, want 0", i, val)
|
||||
}
|
||||
case 32:
|
||||
prog.Entry = Addr(val)
|
||||
case 33:
|
||||
prog.Entry |= Addr(val) << 32
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case *macho.Segment:
|
||||
if load.Addr < limit {
|
||||
errorf("segments out of order: %q at %#x after %#x", load.Name, load.Addr, limit)
|
||||
}
|
||||
limit = load.Addr + load.Memsz
|
||||
if load.Name == "__PAGEZERO" || load.Addr == 0 && load.Filesz == 0 {
|
||||
if load.Name != "__PAGEZERO" {
|
||||
errorf("segment with Addr=0, Filesz=0 is named %q, want %q", load.Name, "__PAGEZERO")
|
||||
} else if load.Addr != 0 || load.Filesz != 0 {
|
||||
errorf("segment %q has Addr=%#x, Filesz=%d, want Addr=%#x, Filesz=%d", load.Name, load.Addr, load.Filesz, 0, 0)
|
||||
}
|
||||
prog.UnmappedSize = Addr(load.Memsz)
|
||||
continue
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(load.Name, "__") {
|
||||
errorf("segment name %q does not begin with %q", load.Name, "__")
|
||||
}
|
||||
if strings.ToUpper(load.Name) != load.Name {
|
||||
errorf("segment name %q is not all upper case", load.Name)
|
||||
}
|
||||
|
||||
seg := &Segment{
|
||||
Name: strings.ToLower(strings.TrimPrefix(load.Name, "__")),
|
||||
VirtAddr: Addr(load.Addr),
|
||||
VirtSize: Addr(load.Memsz),
|
||||
FileOffset: Addr(load.Offset),
|
||||
FileSize: Addr(load.Filesz),
|
||||
}
|
||||
prog.Segments = append(prog.Segments, seg)
|
||||
|
||||
data, err := load.Data()
|
||||
if err != nil {
|
||||
errorf("loading data from %q: %v", load.Name, err)
|
||||
}
|
||||
seg.Data = data
|
||||
|
||||
var maxprot, prot uint32
|
||||
if load.Name == "__TEXT" {
|
||||
maxprot, prot = 7, 5
|
||||
} else {
|
||||
maxprot, prot = 3, 3
|
||||
}
|
||||
if load.Maxprot != maxprot || load.Prot != prot {
|
||||
errorf("segment %q protection is %d, %d, want %d, %d",
|
||||
load.Name, load.Maxprot, load.Prot, maxprot, prot)
|
||||
}
|
||||
|
||||
for len(msects) > 0 && msects[0].Addr < load.Addr+load.Memsz {
|
||||
msect := msects[0]
|
||||
msects = msects[1:]
|
||||
|
||||
if msect.Offset > 0 && prog.HeaderSize == 0 {
|
||||
prog.HeaderSize = Addr(msect.Offset)
|
||||
if seg.FileOffset != 0 {
|
||||
errorf("initial segment %q does not map header", load.Name)
|
||||
}
|
||||
seg.VirtAddr += prog.HeaderSize
|
||||
seg.VirtSize -= prog.HeaderSize
|
||||
seg.FileOffset += prog.HeaderSize
|
||||
seg.FileSize -= prog.HeaderSize
|
||||
seg.Data = seg.Data[prog.HeaderSize:]
|
||||
}
|
||||
|
||||
if msect.Addr < load.Addr {
|
||||
errorf("section %q at address %#x is missing segment", msect.Name, msect.Addr)
|
||||
continue
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(msect.Name, "__") {
|
||||
errorf("section name %q does not begin with %q", msect.Name, "__")
|
||||
}
|
||||
if strings.ToLower(msect.Name) != msect.Name {
|
||||
errorf("section name %q is not all lower case", msect.Name)
|
||||
}
|
||||
if msect.Seg != load.Name {
|
||||
errorf("section %q is lists segment name %q, want %q",
|
||||
msect.Name, msect.Seg, load.Name)
|
||||
}
|
||||
if uint64(msect.Offset) != uint64(load.Offset)+msect.Addr-load.Addr {
|
||||
errorf("section %q file offset is %#x, want %#x",
|
||||
msect.Name, msect.Offset, load.Offset+msect.Addr-load.Addr)
|
||||
}
|
||||
if msect.Reloff != 0 || msect.Nreloc != 0 {
|
||||
errorf("section %q has reloff %d,%d, want %d,%d",
|
||||
msect.Name, msect.Reloff, msect.Nreloc, 0, 0)
|
||||
}
|
||||
flags := uint32(0)
|
||||
if msect.Name == "__text" {
|
||||
flags = 0x400
|
||||
}
|
||||
if msect.Offset == 0 {
|
||||
flags = 1
|
||||
}
|
||||
if msect.Flags != flags {
|
||||
errorf("section %q flags = %#x, want %#x", msect.Name, msect.Flags, flags)
|
||||
}
|
||||
sect := &Section{
|
||||
Name: strings.ToLower(strings.TrimPrefix(msect.Name, "__")),
|
||||
VirtAddr: Addr(msect.Addr),
|
||||
Size: Addr(msect.Size),
|
||||
Align: 1 << msect.Align,
|
||||
}
|
||||
seg.Sections = append(seg.Sections, sect)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, msect := range msects {
|
||||
errorf("section %q has no segment", msect.Name)
|
||||
}
|
||||
|
||||
limit = 0
|
||||
for _, msect := range f.Sections {
|
||||
if msect.Addr < limit {
|
||||
errorf("sections out of order: %q at %#x after %#x", msect.Name, msect.Addr, limit)
|
||||
}
|
||||
limit = msect.Addr + msect.Size
|
||||
}
|
||||
|
||||
err = nil
|
||||
if errors != nil {
|
||||
err = fmt.Errorf("%s", strings.Join(errors, "\n\t"))
|
||||
}
|
||||
return prog, err
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Placeholder to keep build building.
|
||||
|
||||
package main
|
||||
|
||||
func main() {}
|
||||
@@ -1,480 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Generation of runtime function information (pclntab).
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"cmd/internal/goobj"
|
||||
"cmd/internal/obj"
|
||||
"encoding/binary"
|
||||
"os"
|
||||
"sort"
|
||||
)
|
||||
|
||||
var zerofunc goobj.Func
|
||||
|
||||
// pclntab collects the runtime function data for each function that will
|
||||
// be listed in the binary and builds a single table describing all functions.
|
||||
// This table is used at run time for stack traces and to look up PC-specific
|
||||
// information during garbage collection. The symbol created is named
|
||||
// "pclntab" for historical reasons; the scope of the table has grown to
|
||||
// include more than just PC/line number correspondences.
|
||||
// The table format is documented at https://golang.org/s/go12symtab.
|
||||
func (p *Prog) pclntab() {
|
||||
// Count number of functions going into the binary,
|
||||
// so that we can size the initial index correctly.
|
||||
nfunc := 0
|
||||
for _, sym := range p.SymOrder {
|
||||
if sym.Kind != goobj.STEXT {
|
||||
continue
|
||||
}
|
||||
nfunc++
|
||||
}
|
||||
|
||||
// Table header.
|
||||
buf := new(SymBuffer)
|
||||
buf.Init(p)
|
||||
buf.SetSize(8 + p.ptrsize)
|
||||
off := 0
|
||||
off = buf.Uint32(off, 0xfffffffb)
|
||||
off = buf.Uint8(off, 0)
|
||||
off = buf.Uint8(off, 0)
|
||||
off = buf.Uint8(off, uint8(p.pcquantum))
|
||||
off = buf.Uint8(off, uint8(p.ptrsize))
|
||||
off = buf.Uint(off, uint64(nfunc), p.ptrsize)
|
||||
indexOff := off
|
||||
off += (nfunc*2 + 1) * p.ptrsize // function index, to be filled in
|
||||
off += 4 // file table start offset, to be filled in
|
||||
buf.SetSize(off)
|
||||
|
||||
// One-file cache for reading PCData tables from package files.
|
||||
// TODO(rsc): Better I/O strategy.
|
||||
var (
|
||||
file *os.File
|
||||
fname string
|
||||
)
|
||||
|
||||
// Files gives the file numbering for source file names recorded
|
||||
// in the binary.
|
||||
files := make(map[string]int)
|
||||
|
||||
// Build the table, build the index, and build the file name numbering.
|
||||
// The loop here must visit functions in the same order that they will
|
||||
// be stored in the binary, or else binary search over the index will fail.
|
||||
// The runtime checks that the index is sorted properly at program start time.
|
||||
var lastSym *Sym
|
||||
for _, sym := range p.SymOrder {
|
||||
if sym.Kind != goobj.STEXT {
|
||||
continue
|
||||
}
|
||||
lastSym = sym
|
||||
|
||||
// Treat no recorded function information same as all zeros.
|
||||
f := sym.Func
|
||||
if f == nil {
|
||||
f = &zerofunc
|
||||
}
|
||||
|
||||
// Open package file if needed, for reading PC data.
|
||||
if fname != sym.Package.File {
|
||||
if file != nil {
|
||||
file.Close()
|
||||
}
|
||||
var err error
|
||||
file, err = os.Open(sym.Package.File)
|
||||
if err != nil {
|
||||
p.errorf("%v: %v", sym, err)
|
||||
return
|
||||
}
|
||||
fname = sym.Package.File
|
||||
}
|
||||
|
||||
// off is the offset of the table entry where we're going to write
|
||||
// the encoded form of Func.
|
||||
// indexOff is the current position in the table index;
|
||||
// we add an entry in the index pointing at off.
|
||||
off = (buf.Size() + p.ptrsize - 1) &^ (p.ptrsize - 1)
|
||||
indexOff = buf.Addr(indexOff, sym.SymID, 0)
|
||||
indexOff = buf.Uint(indexOff, uint64(off), p.ptrsize)
|
||||
|
||||
// The Func encoding starts with a header giving offsets
|
||||
// to data blobs, and then the data blobs themselves.
|
||||
// end gives the current write position for the data blobs.
|
||||
end := off + p.ptrsize + 3*4 + 5*4 + len(f.PCData)*4 + len(f.FuncData)*p.ptrsize
|
||||
if len(f.FuncData) > 0 {
|
||||
end += -end & (p.ptrsize - 1)
|
||||
}
|
||||
buf.SetSize(end)
|
||||
|
||||
// entry uintptr
|
||||
// name int32
|
||||
// args int32
|
||||
// frame int32
|
||||
//
|
||||
// The frame recorded in the object file is
|
||||
// the frame size used in an assembly listing, which does
|
||||
// not include the caller PC on the stack.
|
||||
// The frame size we want to list here is the delta from
|
||||
// this function's SP to its caller's SP, which does include
|
||||
// the caller PC. Add p.ptrsize to f.Frame to adjust.
|
||||
// TODO(rsc): Record the same frame size in the object file.
|
||||
off = buf.Addr(off, sym.SymID, 0)
|
||||
off = buf.Uint32(off, uint32(addString(buf, sym.Name)))
|
||||
off = buf.Uint32(off, uint32(f.Args))
|
||||
off = buf.Uint32(off, uint32(f.Frame+p.ptrsize))
|
||||
|
||||
// pcdata
|
||||
off = buf.Uint32(off, uint32(addPCTable(p, buf, file, f.PCSP)))
|
||||
off = buf.Uint32(off, uint32(addPCFileTable(p, buf, file, f.PCFile, sym, files)))
|
||||
off = buf.Uint32(off, uint32(addPCTable(p, buf, file, f.PCLine)))
|
||||
off = buf.Uint32(off, uint32(len(f.PCData)))
|
||||
off = buf.Uint32(off, uint32(len(f.FuncData)))
|
||||
for _, pcdata := range f.PCData {
|
||||
off = buf.Uint32(off, uint32(addPCTable(p, buf, file, pcdata)))
|
||||
}
|
||||
|
||||
// funcdata
|
||||
if len(f.FuncData) > 0 {
|
||||
off += -off & (p.ptrsize - 1) // must be pointer-aligned
|
||||
for _, funcdata := range f.FuncData {
|
||||
if funcdata.Sym.Name == "" {
|
||||
off = buf.Uint(off, uint64(funcdata.Offset), p.ptrsize)
|
||||
} else {
|
||||
off = buf.Addr(off, funcdata.Sym, funcdata.Offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if off != end {
|
||||
p.errorf("internal error: invalid math in pclntab: off=%#x end=%#x", off, end)
|
||||
break
|
||||
}
|
||||
}
|
||||
if file != nil {
|
||||
file.Close()
|
||||
}
|
||||
|
||||
// Final entry of index is end PC of last function.
|
||||
indexOff = buf.Addr(indexOff, lastSym.SymID, int64(lastSym.Size))
|
||||
|
||||
// Start file table.
|
||||
// Function index is immediately followed by offset to file table.
|
||||
off = (buf.Size() + p.ptrsize - 1) &^ (p.ptrsize - 1)
|
||||
buf.Uint32(indexOff, uint32(off))
|
||||
|
||||
// File table is an array of uint32s.
|
||||
// The first entry gives 1+n, the size of the array.
|
||||
// The following n entries hold offsets to string data.
|
||||
// File number n uses the string pointed at by entry n.
|
||||
// File number 0 is invalid.
|
||||
buf.SetSize(off + (1+len(files))*4)
|
||||
buf.Uint32(off, uint32(1+len(files)))
|
||||
var filestr []string
|
||||
for file := range files {
|
||||
filestr = append(filestr, file)
|
||||
}
|
||||
sort.Strings(filestr)
|
||||
for _, file := range filestr {
|
||||
id := files[file]
|
||||
buf.Uint32(off+4*id, uint32(addString(buf, file)))
|
||||
}
|
||||
|
||||
pclntab := &Sym{
|
||||
Sym: &goobj.Sym{
|
||||
SymID: goobj.SymID{Name: "runtime.pclntab"},
|
||||
Kind: goobj.SPCLNTAB,
|
||||
Size: buf.Size(),
|
||||
Reloc: buf.Reloc(),
|
||||
},
|
||||
Bytes: buf.Bytes(),
|
||||
}
|
||||
p.addSym(pclntab)
|
||||
}
|
||||
|
||||
// addString appends the string s to the buffer b.
|
||||
// It returns the offset of the beginning of the string in the buffer.
|
||||
func addString(b *SymBuffer, s string) int {
|
||||
off := b.Size()
|
||||
b.SetSize(off + len(s) + 1)
|
||||
copy(b.data[off:], s)
|
||||
return off
|
||||
}
|
||||
|
||||
// addPCTable appends the PC-data table stored in the file f at the location loc
|
||||
// to the symbol buffer b. It returns the offset of the beginning of the table
|
||||
// in the buffer.
|
||||
func addPCTable(p *Prog, b *SymBuffer, f *os.File, loc goobj.Data) int {
|
||||
if loc.Size == 0 {
|
||||
return 0
|
||||
}
|
||||
off := b.Size()
|
||||
b.SetSize(off + int(loc.Size))
|
||||
_, err := f.ReadAt(b.data[off:off+int(loc.Size)], loc.Offset)
|
||||
if err != nil {
|
||||
p.errorf("%v", err)
|
||||
}
|
||||
return off
|
||||
}
|
||||
|
||||
// addPCFileTable is like addPCTable, but it renumbers the file names referred to by the table
|
||||
// to use the global numbering maintained in the files map. It adds new files to the
|
||||
// map as necessary.
|
||||
func addPCFileTable(p *Prog, b *SymBuffer, f *os.File, loc goobj.Data, sym *Sym, files map[string]int) int {
|
||||
if loc.Size == 0 {
|
||||
return 0
|
||||
}
|
||||
off := b.Size()
|
||||
|
||||
src := make([]byte, loc.Size)
|
||||
_, err := f.ReadAt(src, loc.Offset)
|
||||
if err != nil {
|
||||
p.errorf("%v", err)
|
||||
return 0
|
||||
}
|
||||
|
||||
filenum := make([]int, len(sym.Func.File))
|
||||
for i, name := range sym.Func.File {
|
||||
num := files[name]
|
||||
if num == 0 {
|
||||
num = len(files) + 1
|
||||
files[name] = num
|
||||
}
|
||||
filenum[i] = num
|
||||
}
|
||||
|
||||
var dst []byte
|
||||
newval := int32(-1)
|
||||
var it PCIter
|
||||
for it.Init(p, src); !it.Done; it.Next() {
|
||||
// value delta
|
||||
oldval := it.Value
|
||||
val := oldval
|
||||
if oldval != -1 {
|
||||
if oldval < 0 || int(oldval) >= len(filenum) {
|
||||
p.errorf("%s: corrupt pc-file table", sym)
|
||||
break
|
||||
}
|
||||
val = int32(filenum[oldval])
|
||||
}
|
||||
dv := val - newval
|
||||
newval = val
|
||||
uv := uint32(dv<<1) ^ uint32(dv>>31)
|
||||
dst = appendVarint(dst, uv)
|
||||
|
||||
// pc delta
|
||||
dst = appendVarint(dst, it.NextPC-it.PC)
|
||||
}
|
||||
if it.Corrupt {
|
||||
p.errorf("%s: corrupt pc-file table", sym)
|
||||
}
|
||||
|
||||
// terminating value delta
|
||||
dst = appendVarint(dst, 0)
|
||||
|
||||
b.SetSize(off + len(dst))
|
||||
copy(b.data[off:], dst)
|
||||
return off
|
||||
}
|
||||
|
||||
// A SymBuffer is a buffer for preparing the data image of a
|
||||
// linker-generated symbol.
|
||||
type SymBuffer struct {
|
||||
data []byte
|
||||
reloc []goobj.Reloc
|
||||
order binary.ByteOrder
|
||||
ptrsize int
|
||||
}
|
||||
|
||||
// Init initializes the buffer for writing.
|
||||
func (b *SymBuffer) Init(p *Prog) {
|
||||
b.data = nil
|
||||
b.reloc = nil
|
||||
b.order = p.byteorder
|
||||
b.ptrsize = p.ptrsize
|
||||
}
|
||||
|
||||
// Bytes returns the buffer data.
|
||||
func (b *SymBuffer) Bytes() []byte {
|
||||
return b.data
|
||||
}
|
||||
|
||||
// SetSize sets the buffer's data size to n bytes.
|
||||
func (b *SymBuffer) SetSize(n int) {
|
||||
for cap(b.data) < n {
|
||||
b.data = append(b.data[:cap(b.data)], 0)
|
||||
}
|
||||
b.data = b.data[:n]
|
||||
}
|
||||
|
||||
// Size returns the buffer's data size.
|
||||
func (b *SymBuffer) Size() int {
|
||||
return len(b.data)
|
||||
}
|
||||
|
||||
// Reloc returns the buffered relocations.
|
||||
func (b *SymBuffer) Reloc() []goobj.Reloc {
|
||||
return b.reloc
|
||||
}
|
||||
|
||||
// Uint8 sets the uint8 at offset off to v.
|
||||
// It returns the offset just beyond v.
|
||||
func (b *SymBuffer) Uint8(off int, v uint8) int {
|
||||
b.data[off] = v
|
||||
return off + 1
|
||||
}
|
||||
|
||||
// Uint16 sets the uint16 at offset off to v.
|
||||
// It returns the offset just beyond v.
|
||||
func (b *SymBuffer) Uint16(off int, v uint16) int {
|
||||
b.order.PutUint16(b.data[off:], v)
|
||||
return off + 2
|
||||
}
|
||||
|
||||
// Uint32 sets the uint32 at offset off to v.
|
||||
// It returns the offset just beyond v.
|
||||
func (b *SymBuffer) Uint32(off int, v uint32) int {
|
||||
b.order.PutUint32(b.data[off:], v)
|
||||
return off + 4
|
||||
}
|
||||
|
||||
// Uint64 sets the uint64 at offset off to v.
|
||||
// It returns the offset just beyond v.
|
||||
func (b *SymBuffer) Uint64(off int, v uint64) int {
|
||||
b.order.PutUint64(b.data[off:], v)
|
||||
return off + 8
|
||||
}
|
||||
|
||||
// Uint sets the size-byte unsigned integer at offset off to v.
|
||||
// It returns the offset just beyond v.
|
||||
func (b *SymBuffer) Uint(off int, v uint64, size int) int {
|
||||
switch size {
|
||||
case 1:
|
||||
return b.Uint8(off, uint8(v))
|
||||
case 2:
|
||||
return b.Uint16(off, uint16(v))
|
||||
case 4:
|
||||
return b.Uint32(off, uint32(v))
|
||||
case 8:
|
||||
return b.Uint64(off, v)
|
||||
}
|
||||
panic("invalid use of SymBuffer.SetUint")
|
||||
}
|
||||
|
||||
// Addr sets the pointer-sized address at offset off to refer
|
||||
// to symoff bytes past the start of sym. It returns the offset
|
||||
// just beyond the address.
|
||||
func (b *SymBuffer) Addr(off int, sym goobj.SymID, symoff int64) int {
|
||||
b.reloc = append(b.reloc, goobj.Reloc{
|
||||
Offset: off,
|
||||
Size: b.ptrsize,
|
||||
Sym: sym,
|
||||
Add: int(symoff),
|
||||
Type: obj.R_ADDR,
|
||||
})
|
||||
return off + b.ptrsize
|
||||
}
|
||||
|
||||
// A PCIter implements iteration over PC-data tables.
|
||||
//
|
||||
// var it PCIter
|
||||
// for it.Init(p, data); !it.Done; it.Next() {
|
||||
// it.Value holds from it.PC up to (but not including) it.NextPC
|
||||
// }
|
||||
// if it.Corrupt {
|
||||
// data was malformed
|
||||
// }
|
||||
//
|
||||
type PCIter struct {
|
||||
PC uint32
|
||||
NextPC uint32
|
||||
Value int32
|
||||
Done bool
|
||||
Corrupt bool
|
||||
p []byte
|
||||
start bool
|
||||
pcquantum uint32
|
||||
}
|
||||
|
||||
// Init initializes the iteration.
|
||||
// On return, if it.Done is true, the iteration is over.
|
||||
// Otherwise it.Value applies in the pc range [it.PC, it.NextPC).
|
||||
func (it *PCIter) Init(p *Prog, buf []byte) {
|
||||
it.p = buf
|
||||
it.PC = 0
|
||||
it.NextPC = 0
|
||||
it.Value = -1
|
||||
it.start = true
|
||||
it.pcquantum = uint32(p.pcquantum)
|
||||
it.Done = false
|
||||
it.Next()
|
||||
}
|
||||
|
||||
// Next steps forward one entry in the table.
|
||||
// On return, if it.Done is true, the iteration is over.
|
||||
// Otherwise it.Value applies in the pc range [it.PC, it.NextPC).
|
||||
func (it *PCIter) Next() {
|
||||
it.PC = it.NextPC
|
||||
if it.Done {
|
||||
return
|
||||
}
|
||||
if len(it.p) == 0 {
|
||||
it.Done = true
|
||||
return
|
||||
}
|
||||
|
||||
// value delta
|
||||
uv, p, ok := decodeVarint(it.p)
|
||||
if !ok {
|
||||
it.Done = true
|
||||
it.Corrupt = true
|
||||
return
|
||||
}
|
||||
it.p = p
|
||||
if uv == 0 && !it.start {
|
||||
it.Done = true
|
||||
return
|
||||
}
|
||||
it.start = false
|
||||
sv := int32(uv>>1) ^ int32(uv<<31)>>31
|
||||
it.Value += sv
|
||||
|
||||
// pc delta
|
||||
uv, it.p, ok = decodeVarint(it.p)
|
||||
if !ok {
|
||||
it.Done = true
|
||||
it.Corrupt = true
|
||||
return
|
||||
}
|
||||
it.NextPC = it.PC + uv*it.pcquantum
|
||||
}
|
||||
|
||||
// decodeVarint decodes an unsigned varint from p,
|
||||
// reporting the value, the remainder of the data, and
|
||||
// whether the decoding was successful.
|
||||
func decodeVarint(p []byte) (v uint32, rest []byte, ok bool) {
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if len(p) == 0 {
|
||||
return
|
||||
}
|
||||
c := uint32(p[0])
|
||||
p = p[1:]
|
||||
v |= (c & 0x7F) << shift
|
||||
if c&0x80 == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return v, p, true
|
||||
}
|
||||
|
||||
// appendVarint appends an unsigned varint encoding of v to p
|
||||
// and returns the resulting slice.
|
||||
func appendVarint(p []byte, v uint32) []byte {
|
||||
for ; v >= 0x80; v >>= 7 {
|
||||
p = append(p, byte(v)|0x80)
|
||||
}
|
||||
p = append(p, byte(v))
|
||||
return p
|
||||
}
|
||||
@@ -1,340 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"cmd/internal/goobj"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Test of pcln table encoding.
|
||||
// testdata/genpcln.go generates an assembly file with
|
||||
// pseudorandom values for the data that pclntab stores.
|
||||
// This test recomputes the same pseudorandom stream
|
||||
// and checks that the final linked binary uses those values
|
||||
// as well.
|
||||
func TestPclntab(t *testing.T) {
|
||||
p := &Prog{
|
||||
GOOS: "darwin",
|
||||
GOARCH: "amd64",
|
||||
Error: func(s string) { t.Error(s) },
|
||||
StartSym: "start",
|
||||
omitRuntime: true,
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
p.link(&buf, "testdata/pclntab.6")
|
||||
if p.NumError > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// The algorithm for computing values here must match
|
||||
// the one in testdata/genpcln.go.
|
||||
for f := 0; f < 3; f++ {
|
||||
file := "input"
|
||||
line := 1
|
||||
rnd := rand.New(rand.NewSource(int64(f)))
|
||||
args := rnd.Intn(100) * 8
|
||||
frame := 32 + rnd.Intn(32)/8*8
|
||||
size := 200 + rnd.Intn(100)*8
|
||||
|
||||
name := fmt.Sprintf("func%d", f)
|
||||
r, off, fargs, fframe, ok := findFunc(t, p, name)
|
||||
if !ok {
|
||||
continue // error already printed
|
||||
}
|
||||
if fargs != args {
|
||||
t.Errorf("%s: args=%d, want %d", name, fargs, args)
|
||||
}
|
||||
if fframe != frame+8 {
|
||||
t.Errorf("%s: frame=%d, want %d", name, fframe, frame+8)
|
||||
}
|
||||
|
||||
// Check FUNCDATA 1.
|
||||
fdata, ok := loadFuncdata(t, r, name, off, 1)
|
||||
if ok {
|
||||
fsym := p.Syms[goobj.SymID{Name: fmt.Sprintf("funcdata%d", f)}]
|
||||
if fsym == nil {
|
||||
t.Errorf("funcdata%d is missing in binary", f)
|
||||
} else if fdata != fsym.Addr {
|
||||
t.Errorf("%s: funcdata 1 = %#x, want %#x", name, fdata, fsym.Addr)
|
||||
}
|
||||
}
|
||||
|
||||
// Walk code checking pcdata values.
|
||||
spadj := 0
|
||||
pcdata1 := -1
|
||||
pcdata2 := -1
|
||||
|
||||
checkPCSP(t, r, name, off, 0, 0)
|
||||
checkPCData(t, r, name, off, 0, 0, -1)
|
||||
checkPCData(t, r, name, off, 0, 1, -1)
|
||||
checkPCData(t, r, name, off, 0, 2, -1)
|
||||
|
||||
firstpc := 4
|
||||
for i := 0; i < size; i++ {
|
||||
pc := firstpc + i // skip SP adjustment to allocate frame
|
||||
if i >= 0x100 && t.Failed() {
|
||||
break
|
||||
}
|
||||
// Possible SP adjustment.
|
||||
checkPCSP(t, r, name, off, pc, frame+spadj)
|
||||
if rnd.Intn(100) == 0 {
|
||||
checkPCFileLine(t, r, name, off, pc, file, line)
|
||||
checkPCData(t, r, name, off, pc, 1, pcdata1)
|
||||
checkPCData(t, r, name, off, pc, 2, pcdata2)
|
||||
i += 1
|
||||
pc = firstpc + i
|
||||
checkPCFileLine(t, r, name, off, pc-1, file, line)
|
||||
checkPCData(t, r, name, off, pc-1, 1, pcdata1)
|
||||
checkPCData(t, r, name, off, pc-1, 2, pcdata2)
|
||||
checkPCSP(t, r, name, off, pc-1, frame+spadj)
|
||||
|
||||
if spadj <= -32 || spadj < 32 && rnd.Intn(2) == 0 {
|
||||
spadj += 8
|
||||
} else {
|
||||
spadj -= 8
|
||||
}
|
||||
checkPCSP(t, r, name, off, pc, frame+spadj)
|
||||
}
|
||||
|
||||
// Possible PCFile change.
|
||||
if rnd.Intn(100) == 0 {
|
||||
file = fmt.Sprintf("file%d.s", rnd.Intn(10))
|
||||
line = rnd.Intn(100) + 1
|
||||
}
|
||||
|
||||
// Possible PCLine change.
|
||||
if rnd.Intn(10) == 0 {
|
||||
line = rnd.Intn(1000) + 1
|
||||
}
|
||||
|
||||
// Possible PCData $1 change.
|
||||
if rnd.Intn(100) == 0 {
|
||||
pcdata1 = rnd.Intn(1000)
|
||||
}
|
||||
|
||||
// Possible PCData $2 change.
|
||||
if rnd.Intn(100) == 0 {
|
||||
pcdata2 = rnd.Intn(1000)
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
checkPCFileLine(t, r, name, off, 0, file, line)
|
||||
checkPCFileLine(t, r, name, off, pc-1, file, line)
|
||||
}
|
||||
checkPCFileLine(t, r, name, off, pc, file, line)
|
||||
checkPCData(t, r, name, off, pc, 1, pcdata1)
|
||||
checkPCData(t, r, name, off, pc, 2, pcdata2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// findFunc finds the function information in the pclntab of p
|
||||
// for the function with the given name.
|
||||
// It returns a symbol reader for pclntab, the offset of the function information
|
||||
// within that symbol, and the args and frame values read out of the information.
|
||||
func findFunc(t *testing.T, p *Prog, name string) (r *SymReader, off, args, frame int, ok bool) {
|
||||
tabsym := p.Syms[goobj.SymID{Name: "runtime.pclntab"}]
|
||||
if tabsym == nil {
|
||||
t.Errorf("pclntab is missing in binary")
|
||||
return
|
||||
}
|
||||
|
||||
r = new(SymReader)
|
||||
r.Init(p, tabsym)
|
||||
|
||||
// pclntab must with 8-byte header
|
||||
if r.Uint32(0) != 0xfffffffb || r.Uint8(4) != 0 || r.Uint8(5) != 0 || r.Uint8(6) != uint8(p.pcquantum) || r.Uint8(7) != uint8(p.ptrsize) {
|
||||
t.Errorf("pclntab has incorrect header %.8x", r.data[:8])
|
||||
return
|
||||
}
|
||||
|
||||
sym := p.Syms[goobj.SymID{Name: name}]
|
||||
if sym == nil {
|
||||
t.Errorf("%s is missing in the binary", name)
|
||||
return
|
||||
}
|
||||
|
||||
// index is nfunc addr0 off0 addr1 off1 ... addr_nfunc (sentinel)
|
||||
nfunc := int(r.Addr(8))
|
||||
i := sort.Search(nfunc, func(i int) bool {
|
||||
return r.Addr(8+p.ptrsize*(1+2*i)) >= sym.Addr
|
||||
})
|
||||
if entry := r.Addr(8 + p.ptrsize*(1+2*i)); entry != sym.Addr {
|
||||
indexTab := make([]Addr, 2*nfunc+1)
|
||||
for j := range indexTab {
|
||||
indexTab[j] = r.Addr(8 + p.ptrsize*(1+j))
|
||||
}
|
||||
t.Errorf("pclntab is missing entry for %s (%#x): %#x", name, sym.Addr, indexTab)
|
||||
return
|
||||
}
|
||||
|
||||
off = int(r.Addr(8 + p.ptrsize*(1+2*i+1)))
|
||||
|
||||
// func description at off is
|
||||
// entry addr
|
||||
// nameoff uint32
|
||||
// args uint32
|
||||
// frame uint32
|
||||
// pcspoff uint32
|
||||
// pcfileoff uint32
|
||||
// pclineoff uint32
|
||||
// npcdata uint32
|
||||
// nfuncdata uint32
|
||||
// pcdata npcdata*uint32
|
||||
// funcdata nfuncdata*addr
|
||||
//
|
||||
if entry := r.Addr(off); entry != sym.Addr {
|
||||
t.Errorf("pclntab inconsistent: entry for %s addr=%#x has entry=%#x", name, sym.Addr, entry)
|
||||
return
|
||||
}
|
||||
nameoff := int(r.Uint32(off + p.ptrsize))
|
||||
args = int(r.Uint32(off + p.ptrsize + 1*4))
|
||||
frame = int(r.Uint32(off + p.ptrsize + 2*4))
|
||||
|
||||
fname := r.String(nameoff)
|
||||
if fname != name {
|
||||
t.Errorf("pclntab inconsistent: entry for %s addr=%#x has name %q", name, sym.Addr, fname)
|
||||
}
|
||||
|
||||
ok = true // off, args, frame are usable
|
||||
return
|
||||
}
|
||||
|
||||
// loadFuncdata returns the funcdata #fnum value
|
||||
// loaded from the function information for name.
|
||||
func loadFuncdata(t *testing.T, r *SymReader, name string, off int, fnum int) (Addr, bool) {
|
||||
npcdata := int(r.Uint32(off + r.p.ptrsize + 6*4))
|
||||
nfuncdata := int(r.Uint32(off + r.p.ptrsize + 7*4))
|
||||
if fnum >= nfuncdata {
|
||||
t.Errorf("pclntab(%s): no funcdata %d (only < %d)", name, fnum, nfuncdata)
|
||||
return 0, false
|
||||
}
|
||||
fdataoff := off + r.p.ptrsize + (8+npcdata)*4 + fnum*r.p.ptrsize
|
||||
fdataoff += fdataoff & 4
|
||||
return r.Addr(fdataoff), true
|
||||
}
|
||||
|
||||
// checkPCSP checks that the PCSP table in the function information at off
|
||||
// lists spadj as the sp delta for pc.
|
||||
func checkPCSP(t *testing.T, r *SymReader, name string, off, pc, spadj int) {
|
||||
pcoff := r.Uint32(off + r.p.ptrsize + 3*4)
|
||||
pcval, ok := readPCData(t, r, name, "PCSP", pcoff, pc)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if pcval != spadj {
|
||||
t.Errorf("pclntab(%s): at pc=+%#x, pcsp=%d, want %d", name, pc, pcval, spadj)
|
||||
}
|
||||
}
|
||||
|
||||
// checkPCSP checks that the PCFile and PCLine tables in the function information at off
|
||||
// list file, line as the file name and line number for pc.
|
||||
func checkPCFileLine(t *testing.T, r *SymReader, name string, off, pc int, file string, line int) {
|
||||
pcfileoff := r.Uint32(off + r.p.ptrsize + 4*4)
|
||||
pclineoff := r.Uint32(off + r.p.ptrsize + 5*4)
|
||||
pcfilenum, ok1 := readPCData(t, r, name, "PCFile", pcfileoff, pc)
|
||||
pcline, ok2 := readPCData(t, r, name, "PCLine", pclineoff, pc)
|
||||
if !ok1 || !ok2 {
|
||||
return
|
||||
}
|
||||
nfunc := int(r.Addr(8))
|
||||
filetaboff := r.Uint32(8 + r.p.ptrsize*2*(nfunc+1))
|
||||
nfile := int(r.Uint32(int(filetaboff)))
|
||||
if pcfilenum <= 0 || pcfilenum >= nfile {
|
||||
t.Errorf("pclntab(%s): at pc=+%#x, filenum=%d (invalid; nfile=%d)", name, pc, pcfilenum, nfile)
|
||||
}
|
||||
pcfile := r.String(int(r.Uint32(int(filetaboff) + pcfilenum*4)))
|
||||
if !strings.HasSuffix(pcfile, file) {
|
||||
t.Errorf("pclntab(%s): at pc=+%#x, file=%q, want %q", name, pc, pcfile, file)
|
||||
}
|
||||
if pcline != line {
|
||||
t.Errorf("pclntab(%s): at pc=+%#x, line=%d, want %d", name, pc, pcline, line)
|
||||
}
|
||||
}
|
||||
|
||||
// checkPCData checks that the PCData#pnum table in the function information at off
|
||||
// list val as the value for pc.
|
||||
func checkPCData(t *testing.T, r *SymReader, name string, off, pc, pnum, val int) {
|
||||
pcoff := r.Uint32(off + r.p.ptrsize + (8+pnum)*4)
|
||||
pcval, ok := readPCData(t, r, name, fmt.Sprintf("PCData#%d", pnum), pcoff, pc)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if pcval != val {
|
||||
t.Errorf("pclntab(%s): at pc=+%#x, pcdata#%d=%d, want %d", name, pc, pnum, pcval, val)
|
||||
}
|
||||
}
|
||||
|
||||
// readPCData reads the PCData table offset off
|
||||
// to obtain and return the value associated with pc.
|
||||
func readPCData(t *testing.T, r *SymReader, name, pcdataname string, pcoff uint32, pc int) (int, bool) {
|
||||
// "If pcsp, pcfile, pcln, or any of the pcdata offsets is zero,
|
||||
// that table is considered missing, and all PCs take value -1."
|
||||
if pcoff == 0 {
|
||||
return -1, true
|
||||
}
|
||||
|
||||
var it PCIter
|
||||
for it.Init(r.p, r.data[pcoff:]); !it.Done; it.Next() {
|
||||
if it.PC <= uint32(pc) && uint32(pc) < it.NextPC {
|
||||
return int(it.Value), true
|
||||
}
|
||||
}
|
||||
if it.Corrupt {
|
||||
t.Errorf("pclntab(%s): %s: corrupt pcdata table", name, pcdataname)
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// A SymReader provides typed access to the data for a symbol.
|
||||
type SymReader struct {
|
||||
p *Prog
|
||||
data []byte
|
||||
}
|
||||
|
||||
func (r *SymReader) Init(p *Prog, sym *Sym) {
|
||||
seg := sym.Section.Segment
|
||||
off := sym.Addr - seg.VirtAddr
|
||||
data := seg.Data[off : off+Addr(sym.Size)]
|
||||
r.p = p
|
||||
r.data = data
|
||||
}
|
||||
|
||||
func (r *SymReader) Uint8(off int) uint8 {
|
||||
return r.data[off]
|
||||
}
|
||||
|
||||
func (r *SymReader) Uint16(off int) uint16 {
|
||||
return r.p.byteorder.Uint16(r.data[off:])
|
||||
}
|
||||
|
||||
func (r *SymReader) Uint32(off int) uint32 {
|
||||
return r.p.byteorder.Uint32(r.data[off:])
|
||||
}
|
||||
|
||||
func (r *SymReader) Uint64(off int) uint64 {
|
||||
return r.p.byteorder.Uint64(r.data[off:])
|
||||
}
|
||||
|
||||
func (r *SymReader) Addr(off int) Addr {
|
||||
if r.p.ptrsize == 4 {
|
||||
return Addr(r.Uint32(off))
|
||||
}
|
||||
return Addr(r.Uint64(off))
|
||||
}
|
||||
|
||||
func (r *SymReader) String(off int) string {
|
||||
end := off
|
||||
for r.data[end] != '\x00' {
|
||||
end++
|
||||
}
|
||||
return string(r.data[off:end])
|
||||
}
|
||||
@@ -1,220 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"cmd/internal/goobj"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// A Prog holds state for constructing an executable (program) image.
|
||||
//
|
||||
// The usual sequence of operations on a Prog is:
|
||||
//
|
||||
// p.init()
|
||||
// p.scan(file)
|
||||
// p.dead()
|
||||
// p.runtime()
|
||||
// p.layout()
|
||||
// p.load()
|
||||
// p.debug()
|
||||
// p.write(w)
|
||||
//
|
||||
// p.init is in this file. The rest of the methods are in files
|
||||
// named for the method. The convenience method p.link runs
|
||||
// this sequence.
|
||||
//
|
||||
type Prog struct {
|
||||
// Context
|
||||
GOOS string // target operating system
|
||||
GOARCH string // target architecture
|
||||
Format string // desired file format ("elf", "macho", ...)
|
||||
Error func(string) // called to report an error (if set)
|
||||
NumError int // number of errors printed
|
||||
StartSym string
|
||||
|
||||
// Derived context
|
||||
arch
|
||||
formatter formatter
|
||||
startSym goobj.SymID
|
||||
pkgdir string
|
||||
omitRuntime bool // do not load runtime package
|
||||
|
||||
// Input
|
||||
Packages map[string]*Package // loaded packages, by import path
|
||||
Syms map[goobj.SymID]*Sym // defined symbols, by symbol ID
|
||||
Missing map[goobj.SymID]bool // missing symbols
|
||||
Dead map[goobj.SymID]bool // symbols removed as dead
|
||||
SymOrder []*Sym // order syms were scanned
|
||||
MaxVersion int // max SymID.Version, for generating fresh symbol IDs
|
||||
|
||||
// Output
|
||||
UnmappedSize Addr // size of unmapped region at address 0
|
||||
HeaderSize Addr // size of object file header
|
||||
Entry Addr // virtual address where execution begins
|
||||
Segments []*Segment // loaded memory segments
|
||||
}
|
||||
|
||||
// An arch describes architecture-dependent settings.
|
||||
type arch struct {
|
||||
byteorder binary.ByteOrder
|
||||
ptrsize int
|
||||
pcquantum int
|
||||
}
|
||||
|
||||
// A formatter takes care of the details of generating a particular
|
||||
// kind of executable file.
|
||||
type formatter interface {
|
||||
// headerSize returns the footprint of the header for p
|
||||
// in both virtual address space and file bytes.
|
||||
// The footprint does not include any bytes stored at the
|
||||
// end of the file.
|
||||
headerSize(p *Prog) (virt, file Addr)
|
||||
|
||||
// write writes the executable file for p to w.
|
||||
write(w io.Writer, p *Prog)
|
||||
}
|
||||
|
||||
// An Addr represents a virtual memory address, a file address, or a size.
|
||||
// It must be a uint64, not a uintptr, so that a 32-bit linker can still generate a 64-bit binary.
|
||||
// It must be unsigned in order to link programs placed at very large start addresses.
|
||||
// Math involving Addrs must be checked carefully not to require negative numbers.
|
||||
type Addr uint64
|
||||
|
||||
// A Package is a Go package loaded from a file.
|
||||
type Package struct {
|
||||
*goobj.Package // table of contents
|
||||
File string // file name for reopening
|
||||
Syms []*Sym // symbols defined by this package
|
||||
}
|
||||
|
||||
// A Sym is a symbol defined in a loaded package.
|
||||
type Sym struct {
|
||||
*goobj.Sym // symbol metadata from package file
|
||||
Package *Package // package defining symbol
|
||||
Section *Section // section where symbol is placed in output program
|
||||
Addr Addr // virtual address of symbol in output program
|
||||
Bytes []byte // symbol data, for internally defined symbols
|
||||
}
|
||||
|
||||
// A Segment is a loaded memory segment.
|
||||
// A Prog is expected to have segments named "text" and optionally "data",
|
||||
// in that order, before any other segments.
|
||||
type Segment struct {
|
||||
Name string // name of segment: "text", "data", ...
|
||||
VirtAddr Addr // virtual memory address of segment base
|
||||
VirtSize Addr // size of segment in memory
|
||||
FileOffset Addr // file offset of segment base
|
||||
FileSize Addr // size of segment in file; can be less than VirtSize
|
||||
Sections []*Section // sections inside segment
|
||||
Data []byte // raw data of segment image
|
||||
}
|
||||
|
||||
// A Section is part of a loaded memory segment.
|
||||
type Section struct {
|
||||
Name string // name of section: "text", "rodata", "noptrbss", and so on
|
||||
VirtAddr Addr // virtual memory address of section base
|
||||
Size Addr // size of section in memory
|
||||
Align Addr // required alignment
|
||||
InFile bool // section has image data in file (like data, unlike bss)
|
||||
Syms []*Sym // symbols stored in section
|
||||
Segment *Segment // segment containing section
|
||||
}
|
||||
|
||||
func (p *Prog) errorf(format string, args ...interface{}) {
|
||||
if p.Error != nil {
|
||||
p.Error(fmt.Sprintf(format, args...))
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, format+"\n", args...)
|
||||
}
|
||||
p.NumError++
|
||||
}
|
||||
|
||||
// link is the one-stop convenience method for running a link.
|
||||
// It writes to w the object file generated from using mainFile as the main package.
|
||||
func (p *Prog) link(w io.Writer, mainFile string) {
|
||||
p.init()
|
||||
p.scan(mainFile)
|
||||
if p.NumError > 0 {
|
||||
return
|
||||
}
|
||||
p.dead()
|
||||
p.runtime()
|
||||
p.autoData()
|
||||
p.layout()
|
||||
p.autoConst()
|
||||
if p.NumError > 0 {
|
||||
return
|
||||
}
|
||||
p.load()
|
||||
if p.NumError > 0 {
|
||||
return
|
||||
}
|
||||
p.debug()
|
||||
if p.NumError > 0 {
|
||||
return
|
||||
}
|
||||
p.write(w)
|
||||
}
|
||||
|
||||
// init initializes p for use by the other methods.
|
||||
func (p *Prog) init() {
|
||||
// Set default context if not overridden.
|
||||
if p.GOOS == "" {
|
||||
p.GOOS = build.Default.GOOS
|
||||
}
|
||||
if p.GOARCH == "" {
|
||||
p.GOARCH = build.Default.GOARCH
|
||||
}
|
||||
if p.Format == "" {
|
||||
p.Format = goosFormat[p.GOOS]
|
||||
if p.Format == "" {
|
||||
p.errorf("no default file format for GOOS %q", p.GOOS)
|
||||
return
|
||||
}
|
||||
}
|
||||
if p.StartSym == "" {
|
||||
p.StartSym = fmt.Sprintf("_rt0_%s_%s", p.GOARCH, p.GOOS)
|
||||
}
|
||||
|
||||
// Derive internal context.
|
||||
p.formatter = formatters[p.Format]
|
||||
if p.formatter == nil {
|
||||
p.errorf("unknown output file format %q", p.Format)
|
||||
return
|
||||
}
|
||||
p.startSym = goobj.SymID{Name: p.StartSym}
|
||||
arch, ok := arches[p.GOARCH]
|
||||
if !ok {
|
||||
p.errorf("unknown GOOS %q", p.GOOS)
|
||||
return
|
||||
}
|
||||
p.arch = arch
|
||||
|
||||
p.pkgdir = fmt.Sprintf("%s/pkg/%s_%s", runtime.GOROOT(), p.GOOS, p.GOARCH)
|
||||
}
|
||||
|
||||
// goosFormat records the default format for each known GOOS value.
|
||||
var goosFormat = map[string]string{
|
||||
"darwin": "darwin",
|
||||
}
|
||||
|
||||
// formatters records the format implementation for each known format value.
|
||||
var formatters = map[string]formatter{
|
||||
"darwin": machoFormat{},
|
||||
}
|
||||
|
||||
var arches = map[string]arch{
|
||||
"amd64": {
|
||||
byteorder: binary.LittleEndian,
|
||||
ptrsize: 8,
|
||||
pcquantum: 1,
|
||||
},
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// shiftProg adjusts the addresses in p.
|
||||
// It adds vdelta to all virtual addresses and fdelta to all file offsets.
|
||||
func shiftProg(p *Prog, vdelta Addr, fdelta Addr) {
|
||||
p.Entry += vdelta
|
||||
for _, seg := range p.Segments {
|
||||
seg.FileOffset += fdelta
|
||||
seg.VirtAddr += vdelta
|
||||
for _, sect := range seg.Sections {
|
||||
sect.VirtAddr += vdelta
|
||||
for _, sym := range sect.Syms {
|
||||
sym.Addr += vdelta
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// diffProg returns a list of differences between p and q,
|
||||
// assuming p is being checked and q is the correct answer.
|
||||
func diffProg(p, q *Prog) []string {
|
||||
var errors []string
|
||||
if p.UnmappedSize != q.UnmappedSize {
|
||||
errors = append(errors, fmt.Sprintf("p.UnmappedSize = %#x, want %#x", p.UnmappedSize, q.UnmappedSize))
|
||||
}
|
||||
if p.HeaderSize != q.HeaderSize {
|
||||
errors = append(errors, fmt.Sprintf("p.HeaderSize = %#x, want %#x", p.HeaderSize, q.HeaderSize))
|
||||
}
|
||||
if p.Entry != q.Entry {
|
||||
errors = append(errors, fmt.Sprintf("p.Entry = %#x, want %#x", p.Entry, q.Entry))
|
||||
}
|
||||
for i := 0; i < len(p.Segments) || i < len(q.Segments); i++ {
|
||||
if i >= len(p.Segments) {
|
||||
errors = append(errors, fmt.Sprintf("p missing segment %q", q.Segments[i].Name))
|
||||
continue
|
||||
}
|
||||
if i >= len(q.Segments) {
|
||||
errors = append(errors, fmt.Sprintf("p has extra segment %q", p.Segments[i].Name))
|
||||
continue
|
||||
}
|
||||
pseg := p.Segments[i]
|
||||
qseg := q.Segments[i]
|
||||
if pseg.Name != qseg.Name {
|
||||
errors = append(errors, fmt.Sprintf("segment %d Name = %q, want %q", i, pseg.Name, qseg.Name))
|
||||
continue // probably out of sync
|
||||
}
|
||||
if pseg.VirtAddr != qseg.VirtAddr {
|
||||
errors = append(errors, fmt.Sprintf("segment %q VirtAddr = %#x, want %#x", pseg.Name, pseg.VirtAddr, qseg.VirtAddr))
|
||||
}
|
||||
if pseg.VirtSize != qseg.VirtSize {
|
||||
errors = append(errors, fmt.Sprintf("segment %q VirtSize = %#x, want %#x", pseg.Name, pseg.VirtSize, qseg.VirtSize))
|
||||
}
|
||||
if pseg.FileOffset != qseg.FileOffset {
|
||||
errors = append(errors, fmt.Sprintf("segment %q FileOffset = %#x, want %#x", pseg.Name, pseg.FileOffset, qseg.FileOffset))
|
||||
}
|
||||
if pseg.FileSize != qseg.FileSize {
|
||||
errors = append(errors, fmt.Sprintf("segment %q FileSize = %#x, want %#x", pseg.Name, pseg.FileSize, qseg.FileSize))
|
||||
}
|
||||
if len(pseg.Data) != len(qseg.Data) {
|
||||
errors = append(errors, fmt.Sprintf("segment %q len(Data) = %d, want %d", pseg.Name, len(pseg.Data), len(qseg.Data)))
|
||||
} else if !bytes.Equal(pseg.Data, qseg.Data) {
|
||||
errors = append(errors, fmt.Sprintf("segment %q Data mismatch:\n\thave %x\n\twant %x", pseg.Name, pseg.Data, qseg.Data))
|
||||
}
|
||||
|
||||
for j := 0; j < len(pseg.Sections) || j < len(qseg.Sections); j++ {
|
||||
if j >= len(pseg.Sections) {
|
||||
errors = append(errors, fmt.Sprintf("segment %q missing section %q", pseg.Name, qseg.Sections[i].Name))
|
||||
continue
|
||||
}
|
||||
if j >= len(qseg.Sections) {
|
||||
errors = append(errors, fmt.Sprintf("segment %q has extra section %q", pseg.Name, pseg.Sections[i].Name))
|
||||
continue
|
||||
}
|
||||
psect := pseg.Sections[j]
|
||||
qsect := qseg.Sections[j]
|
||||
if psect.Name != qsect.Name {
|
||||
errors = append(errors, fmt.Sprintf("segment %q, section %d Name = %q, want %q", pseg.Name, j, psect.Name, qsect.Name))
|
||||
continue // probably out of sync
|
||||
}
|
||||
|
||||
if psect.VirtAddr != qsect.VirtAddr {
|
||||
errors = append(errors, fmt.Sprintf("segment %q section %q VirtAddr = %#x, want %#x", pseg.Name, psect.Name, psect.VirtAddr, qsect.VirtAddr))
|
||||
}
|
||||
if psect.Size != qsect.Size {
|
||||
errors = append(errors, fmt.Sprintf("segment %q section %q Size = %#x, want %#x", pseg.Name, psect.Name, psect.Size, qsect.Size))
|
||||
}
|
||||
if psect.Align != qsect.Align {
|
||||
errors = append(errors, fmt.Sprintf("segment %q section %q Align = %#x, want %#x", pseg.Name, psect.Name, psect.Align, qsect.Align))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
// cloneProg returns a deep copy of p.
|
||||
func cloneProg(p *Prog) *Prog {
|
||||
q := new(Prog)
|
||||
*q = *p
|
||||
q.Segments = make([]*Segment, len(p.Segments))
|
||||
for i, seg := range p.Segments {
|
||||
q.Segments[i] = cloneSegment(seg)
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
// cloneSegment returns a deep copy of seg.
|
||||
func cloneSegment(seg *Segment) *Segment {
|
||||
t := new(Segment)
|
||||
*t = *seg
|
||||
t.Sections = make([]*Section, len(seg.Sections))
|
||||
for i, sect := range seg.Sections {
|
||||
t.Sections[i] = cloneSection(sect)
|
||||
}
|
||||
t.Data = make([]byte, len(seg.Data))
|
||||
copy(t.Data, seg.Data)
|
||||
return t
|
||||
}
|
||||
|
||||
// cloneSection returns a deep copy of section.
|
||||
func cloneSection(sect *Section) *Section {
|
||||
// At the moment, there's nothing we need to make a deep copy of.
|
||||
t := new(Section)
|
||||
*t = *sect
|
||||
return t
|
||||
}
|
||||
|
||||
const saveMismatch = true
|
||||
|
||||
// checkGolden checks that data matches the named file.
|
||||
// If not, it reports the error to the test.
|
||||
func checkGolden(t *testing.T, data []byte, name string) {
|
||||
golden := mustParseHexdumpFile(t, name)
|
||||
if !bytes.Equal(data, golden) {
|
||||
if saveMismatch {
|
||||
ioutil.WriteFile(name+".raw", data, 0666)
|
||||
ioutil.WriteFile(name+".hex", []byte(hexdump(data)), 0666)
|
||||
}
|
||||
// TODO(rsc): A better diff would be nice, as needed.
|
||||
i := 0
|
||||
for i < len(data) && i < len(golden) && data[i] == golden[i] {
|
||||
i++
|
||||
}
|
||||
if i >= len(data) {
|
||||
t.Errorf("%s: output file shorter than expected: have %d bytes, want %d", name, len(data), len(golden))
|
||||
} else if i >= len(golden) {
|
||||
t.Errorf("%s: output file larger than expected: have %d bytes, want %d", name, len(data), len(golden))
|
||||
} else {
|
||||
t.Errorf("%s: output file differs at byte %d: have %#02x, want %#02x", name, i, data[i], golden[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Generation of runtime-accessible data structures.
|
||||
// See also debug.go.
|
||||
|
||||
package main
|
||||
|
||||
import "cmd/internal/goobj"
|
||||
|
||||
func (p *Prog) runtime() {
|
||||
p.pclntab()
|
||||
|
||||
// TODO: Implement garbage collection data.
|
||||
p.addSym(&Sym{
|
||||
Sym: &goobj.Sym{
|
||||
SymID: goobj.SymID{Name: "runtime.gcdata"},
|
||||
Kind: goobj.SRODATA,
|
||||
},
|
||||
})
|
||||
p.addSym(&Sym{
|
||||
Sym: &goobj.Sym{
|
||||
SymID: goobj.SymID{Name: "runtime.gcbss"},
|
||||
Kind: goobj.SRODATA,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Initial scan of packages making up a program.
|
||||
|
||||
// TODO(rsc): Rename goobj.SymID.Version to StaticID to avoid confusion with the ELF meaning of version.
|
||||
// TODO(rsc): Fix file format so that SBSS/SNOPTRBSS with data is listed as SDATA/SNOPTRDATA.
|
||||
// TODO(rsc): Parallelize scan to overlap file i/o where possible.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"cmd/internal/goobj"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// scan scans all packages making up the program, starting with package main defined in mainfile.
|
||||
func (p *Prog) scan(mainfile string) {
|
||||
p.initScan()
|
||||
p.scanFile("main", mainfile)
|
||||
if len(p.Missing) > 0 && !p.omitRuntime {
|
||||
p.scanImport("runtime")
|
||||
}
|
||||
|
||||
var missing []string
|
||||
for sym := range p.Missing {
|
||||
if !p.isAuto(sym) {
|
||||
missing = append(missing, sym.String())
|
||||
}
|
||||
}
|
||||
|
||||
if missing != nil {
|
||||
sort.Strings(missing)
|
||||
for _, sym := range missing {
|
||||
p.errorf("undefined: %s", sym)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(rsc): Walk import graph to diagnose cycles.
|
||||
}
|
||||
|
||||
// initScan initializes the Prog fields needed by scan.
|
||||
func (p *Prog) initScan() {
|
||||
p.Packages = make(map[string]*Package)
|
||||
p.Syms = make(map[goobj.SymID]*Sym)
|
||||
p.Missing = make(map[goobj.SymID]bool)
|
||||
p.Missing[p.startSym] = true
|
||||
}
|
||||
|
||||
// scanFile reads file to learn about the package with the given import path.
|
||||
func (p *Prog) scanFile(pkgpath string, file string) {
|
||||
pkg := &Package{
|
||||
File: file,
|
||||
}
|
||||
p.Packages[pkgpath] = pkg
|
||||
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
p.errorf("%v", err)
|
||||
return
|
||||
}
|
||||
gp, err := goobj.Parse(f, pkgpath)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
p.errorf("reading %s: %v", file, err)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(rsc): Change cmd/internal/goobj to record package name as gp.Name.
|
||||
// TODO(rsc): If pkgpath == "main", check that gp.Name == "main".
|
||||
|
||||
pkg.Package = gp
|
||||
|
||||
for _, gs := range gp.Syms {
|
||||
// TODO(rsc): Fix file format instead of this workaround.
|
||||
if gs.Data.Size > 0 {
|
||||
switch gs.Kind {
|
||||
case goobj.SBSS:
|
||||
gs.Kind = goobj.SDATA
|
||||
case goobj.SNOPTRBSS:
|
||||
gs.Kind = goobj.SNOPTRDATA
|
||||
}
|
||||
}
|
||||
|
||||
if gs.Version != 0 {
|
||||
gs.Version += p.MaxVersion
|
||||
}
|
||||
for i := range gs.Reloc {
|
||||
r := &gs.Reloc[i]
|
||||
if r.Sym.Version != 0 {
|
||||
r.Sym.Version += p.MaxVersion
|
||||
}
|
||||
if p.Syms[r.Sym] == nil {
|
||||
p.Missing[r.Sym] = true
|
||||
}
|
||||
}
|
||||
if gs.Func != nil {
|
||||
for i := range gs.Func.FuncData {
|
||||
fdata := &gs.Func.FuncData[i]
|
||||
if fdata.Sym.Name != "" {
|
||||
if fdata.Sym.Version != 0 {
|
||||
fdata.Sym.Version += p.MaxVersion
|
||||
}
|
||||
if p.Syms[fdata.Sym] == nil {
|
||||
p.Missing[fdata.Sym] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if old := p.Syms[gs.SymID]; old != nil {
|
||||
// Duplicate definition of symbol. Is it okay?
|
||||
// TODO(rsc): Write test for this code.
|
||||
switch {
|
||||
// If both symbols are BSS (no data), take max of sizes
|
||||
// but otherwise ignore second symbol.
|
||||
case old.Data.Size == 0 && gs.Data.Size == 0:
|
||||
if old.Size < gs.Size {
|
||||
old.Size = gs.Size
|
||||
}
|
||||
continue
|
||||
|
||||
// If one is in BSS and one is not, use the one that is not.
|
||||
case old.Data.Size > 0 && gs.Data.Size == 0:
|
||||
continue
|
||||
case gs.Data.Size > 0 && old.Data.Size == 0:
|
||||
break // install gs as new symbol below
|
||||
|
||||
// If either is marked as DupOK, we can keep either one.
|
||||
// Keep the one that we saw first.
|
||||
case old.DupOK || gs.DupOK:
|
||||
continue
|
||||
|
||||
// Otherwise, there's an actual conflict:
|
||||
default:
|
||||
p.errorf("symbol %s defined in both %s and %s %v %v", gs.SymID, old.Package.File, file, old.Data, gs.Data)
|
||||
continue
|
||||
}
|
||||
}
|
||||
s := &Sym{
|
||||
Sym: gs,
|
||||
Package: pkg,
|
||||
}
|
||||
p.addSym(s)
|
||||
delete(p.Missing, gs.SymID)
|
||||
|
||||
if s.Data.Size > int64(s.Size) {
|
||||
p.errorf("%s: initialized data larger than symbol (%d > %d)", s, s.Data.Size, s.Size)
|
||||
}
|
||||
}
|
||||
p.MaxVersion += pkg.MaxVersion
|
||||
|
||||
for i, pkgpath := range pkg.Imports {
|
||||
// TODO(rsc): Fix file format to drop .a from recorded import path.
|
||||
pkgpath = strings.TrimSuffix(pkgpath, ".a")
|
||||
pkg.Imports[i] = pkgpath
|
||||
|
||||
p.scanImport(pkgpath)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Prog) addSym(s *Sym) {
|
||||
pkg := s.Package
|
||||
if pkg == nil {
|
||||
pkg = p.Packages[""]
|
||||
if pkg == nil {
|
||||
pkg = &Package{}
|
||||
p.Packages[""] = pkg
|
||||
}
|
||||
s.Package = pkg
|
||||
}
|
||||
pkg.Syms = append(pkg.Syms, s)
|
||||
p.Syms[s.SymID] = s
|
||||
p.SymOrder = append(p.SymOrder, s)
|
||||
}
|
||||
|
||||
// scanImport finds the object file for the given import path and then scans it.
|
||||
func (p *Prog) scanImport(pkgpath string) {
|
||||
if p.Packages[pkgpath] != nil {
|
||||
return // already loaded
|
||||
}
|
||||
|
||||
// TODO(rsc): Implement correct search to find file.
|
||||
p.scanFile(pkgpath, p.pkgdir+"/"+pkgpath+".a")
|
||||
}
|
||||
15
src/cmd/newlink/testdata/Makefile
vendored
15
src/cmd/newlink/testdata/Makefile
vendored
@@ -1,15 +0,0 @@
|
||||
ALL=\
|
||||
autosection.6\
|
||||
autoweak.6\
|
||||
dead.6\
|
||||
hello.6\
|
||||
layout.6\
|
||||
pclntab.6\
|
||||
|
||||
all: $(ALL)
|
||||
|
||||
%.6: %.s
|
||||
GOARCH=amd64 GOOS=darwin go tool asm -o $*.6 -I $(shell go env GOROOT)/pkg/include -trimpath=$(shell pwd) $*.s
|
||||
|
||||
pclntab.s: genpcln.go
|
||||
go run genpcln.go >pclntab.s
|
||||
BIN
src/cmd/newlink/testdata/autosection.6
vendored
BIN
src/cmd/newlink/testdata/autosection.6
vendored
Binary file not shown.
60
src/cmd/newlink/testdata/autosection.s
vendored
60
src/cmd/newlink/testdata/autosection.s
vendored
@@ -1,60 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Test of section-named symbols.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
TEXT start(SB),7,$0
|
||||
MOVQ $autotab(SB),AX
|
||||
MOVQ $autoptr(SB),AX
|
||||
RET
|
||||
|
||||
GLOBL zero(SB), $8
|
||||
|
||||
GLOBL zeronoptr(SB), NOPTR, $16
|
||||
|
||||
// text
|
||||
DATA autotab+0x00(SB)/8, $runtime·text(SB)
|
||||
DATA autotab+0x08(SB)/8, $start(SB)
|
||||
DATA autotab+0x10(SB)/8, $runtime·etext(SB)
|
||||
DATA autotab+0x18(SB)/8, $start+16(SB)
|
||||
|
||||
// data
|
||||
DATA autotab+0x20(SB)/8, $runtime·data(SB)
|
||||
DATA autotab+0x28(SB)/8, $autotab(SB)
|
||||
DATA autotab+0x30(SB)/8, $runtime·edata(SB)
|
||||
DATA autotab+0x38(SB)/8, $nonzero+4(SB)
|
||||
|
||||
// bss
|
||||
DATA autotab+0x40(SB)/8, $runtime·bss(SB)
|
||||
DATA autotab+0x48(SB)/8, $zero(SB)
|
||||
DATA autotab+0x50(SB)/8, $runtime·ebss(SB)
|
||||
DATA autotab+0x58(SB)/8, $zero+8(SB)
|
||||
|
||||
// noptrdata
|
||||
DATA autotab+0x60(SB)/8, $runtime·noptrdata(SB)
|
||||
DATA autotab+0x68(SB)/8, $nonzeronoptr(SB)
|
||||
DATA autotab+0x70(SB)/8, $runtime·enoptrdata(SB)
|
||||
DATA autotab+0x78(SB)/8, $nonzeronoptr+8(SB)
|
||||
|
||||
// noptrbss
|
||||
DATA autotab+0x80(SB)/8, $runtime·noptrbss(SB)
|
||||
DATA autotab+0x88(SB)/8, $zeronoptr(SB)
|
||||
DATA autotab+0x90(SB)/8, $runtime·enoptrbss(SB)
|
||||
DATA autotab+0x98(SB)/8, $zeronoptr+16(SB)
|
||||
|
||||
// end
|
||||
DATA autotab+0xa0(SB)/8, $runtime·end(SB)
|
||||
DATA autotab+0xa8(SB)/8, $zeronoptr+16(SB)
|
||||
|
||||
GLOBL autotab(SB), $0xb0
|
||||
|
||||
DATA nonzero(SB)/4, $1
|
||||
GLOBL nonzero(SB), $4
|
||||
|
||||
DATA nonzeronoptr(SB)/8, $2
|
||||
GLOBL nonzeronoptr(SB), NOPTR, $8
|
||||
|
||||
GLOBL autoptr(SB), $0
|
||||
BIN
src/cmd/newlink/testdata/autoweak.6
vendored
BIN
src/cmd/newlink/testdata/autoweak.6
vendored
Binary file not shown.
30
src/cmd/newlink/testdata/autoweak.s
vendored
30
src/cmd/newlink/testdata/autoweak.s
vendored
@@ -1,30 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Test of go.weak symbols.
|
||||
|
||||
TEXT start(SB),7,$0
|
||||
MOVQ $autotab(SB),AX
|
||||
MOVQ $autoptr(SB),AX
|
||||
RET
|
||||
|
||||
// go.weak.sym should resolve to sym, because sym is in the binary.
|
||||
DATA autotab+0(SB)/8, $go·weak·sym(SB)
|
||||
DATA autotab+8(SB)/8, $sym(SB)
|
||||
|
||||
// go.weak.missingsym should resolve to 0, because missingsym is not in the binary.
|
||||
DATA autotab+16(SB)/8, $go·weak·missingsym(SB)
|
||||
DATA autotab+24(SB)/8, $0
|
||||
|
||||
// go.weak.deadsym should resolve to 0, because deadsym is discarded during dead code removal
|
||||
DATA autotab+32(SB)/8, $go·weak·deadsym(SB)
|
||||
DATA autotab+40(SB)/8, $0
|
||||
|
||||
GLOBL autotab(SB), $48
|
||||
|
||||
GLOBL sym(SB), $1
|
||||
|
||||
GLOBL deadsym(SB), $1
|
||||
|
||||
GLOBL autoptr(SB), $0
|
||||
BIN
src/cmd/newlink/testdata/dead.6
vendored
BIN
src/cmd/newlink/testdata/dead.6
vendored
Binary file not shown.
48
src/cmd/newlink/testdata/dead.s
vendored
48
src/cmd/newlink/testdata/dead.s
vendored
@@ -1,48 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Test of dead code removal.
|
||||
// Symbols with names beginning with dead_ should be discarded.
|
||||
// Others should be kept.
|
||||
|
||||
TEXT start(SB),7,$0 // start symbol
|
||||
MOVQ $data1<>(SB), AX
|
||||
CALL text1(SB)
|
||||
MOVQ $text2(SB), BX
|
||||
RET
|
||||
|
||||
TEXT text1(SB),7,$0
|
||||
FUNCDATA $1, funcdata+4(SB)
|
||||
RET
|
||||
|
||||
TEXT text2(SB),7,$0
|
||||
MOVQ $runtime·edata(SB),BX
|
||||
RET
|
||||
|
||||
DATA data1<>+0(SB)/8, $data2(SB)
|
||||
DATA data1<>+8(SB)/8, $data3(SB)
|
||||
GLOBL data1<>(SB), $16
|
||||
GLOBL data2(SB), $1
|
||||
GLOBL data3(SB), $1
|
||||
GLOBL funcdata(SB), $8
|
||||
|
||||
TEXT dead_start(SB),7,$0
|
||||
MOVQ $dead_data1(SB), AX
|
||||
CALL dead_text1(SB)
|
||||
MOVQ $dead_text2(SB), BX
|
||||
RET
|
||||
|
||||
TEXT dead_text1(SB),7,$0
|
||||
FUNCDATA $1, dead_funcdata+4(SB)
|
||||
RET
|
||||
|
||||
TEXT dead_text2(SB),7,$0
|
||||
RET
|
||||
|
||||
DATA dead_data1+0(SB)/8, $dead_data2(SB)
|
||||
DATA dead_data1+8(SB)/8, $dead_data3(SB)
|
||||
GLOBL dead_data1(SB), $16
|
||||
GLOBL dead_data2(SB), $1
|
||||
GLOBL dead_data3(SB), $1
|
||||
GLOBL dead_funcdata(SB), $8
|
||||
112
src/cmd/newlink/testdata/genpcln.go
vendored
112
src/cmd/newlink/testdata/genpcln.go
vendored
@@ -1,112 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This program generates a .s file using a pseudorandom
|
||||
// value stream for the runtime function data.
|
||||
// The pclntab test checks that the linked copy
|
||||
// still has the same pseudorandom value stream.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Printf("// generated by genpcln.go; do not edit\n\n")
|
||||
for f := 0; f < 3; f++ {
|
||||
r := rand.New(rand.NewSource(int64(f)))
|
||||
file := "input"
|
||||
line := 1
|
||||
args := r.Intn(100) * 8
|
||||
frame := 32 + r.Intn(32)/8*8
|
||||
fmt.Printf("#line %d %q\n", line, file)
|
||||
fmt.Printf("TEXT func%d(SB),7,$%d-%d\n", f, frame, args)
|
||||
fmt.Printf("\tFUNCDATA $1, funcdata%d(SB)\n", f)
|
||||
fmt.Printf("#line %d %q\n", line, file)
|
||||
size := 200 + r.Intn(100)*8
|
||||
spadj := 0
|
||||
flushed := 0
|
||||
firstpc := 4
|
||||
flush := func(i int) {
|
||||
for i-flushed >= 10 {
|
||||
fmt.Printf("#line %d %q\n", line, file)
|
||||
fmt.Printf("/*%#04x*/\tMOVQ $0x123456789, AX\n", firstpc+flushed)
|
||||
flushed += 10
|
||||
}
|
||||
for i-flushed >= 5 {
|
||||
fmt.Printf("#line %d %q\n", line, file)
|
||||
fmt.Printf("/*%#04x*/\tMOVL $0x1234567, AX\n", firstpc+flushed)
|
||||
flushed += 5
|
||||
}
|
||||
for i-flushed > 0 {
|
||||
fmt.Printf("#line %d %q\n", line, file)
|
||||
fmt.Printf("/*%#04x*/\tBYTE $0\n", firstpc+flushed)
|
||||
flushed++
|
||||
}
|
||||
}
|
||||
for i := 0; i < size; i++ {
|
||||
// Possible SP adjustment.
|
||||
if r.Intn(100) == 0 {
|
||||
flush(i)
|
||||
fmt.Printf("#line %d %q\n", line, file)
|
||||
if spadj <= -32 || spadj < 32 && r.Intn(2) == 0 {
|
||||
spadj += 8
|
||||
fmt.Printf("/*%#04x*/\tPUSHQ AX\n", firstpc+i)
|
||||
} else {
|
||||
spadj -= 8
|
||||
fmt.Printf("/*%#04x*/\tPOPQ AX\n", firstpc+i)
|
||||
}
|
||||
i += 1
|
||||
flushed = i
|
||||
}
|
||||
|
||||
// Possible PCFile change.
|
||||
if r.Intn(100) == 0 {
|
||||
flush(i)
|
||||
file = fmt.Sprintf("file%d.s", r.Intn(10))
|
||||
line = r.Intn(100) + 1
|
||||
}
|
||||
|
||||
// Possible PCLine change.
|
||||
if r.Intn(10) == 0 {
|
||||
flush(i)
|
||||
line = r.Intn(1000) + 1
|
||||
}
|
||||
|
||||
// Possible PCData $1 change.
|
||||
if r.Intn(100) == 0 {
|
||||
flush(i)
|
||||
fmt.Printf("/*%6s*/\tPCDATA $1, $%d\n", "", r.Intn(1000))
|
||||
}
|
||||
|
||||
// Possible PCData $2 change.
|
||||
if r.Intn(100) == 0 {
|
||||
flush(i)
|
||||
fmt.Printf("/*%6s*/\tPCDATA $2, $%d\n", "", r.Intn(1000))
|
||||
}
|
||||
}
|
||||
flush(size)
|
||||
for spadj < 0 {
|
||||
fmt.Printf("\tPUSHQ AX\n")
|
||||
spadj += 8
|
||||
}
|
||||
for spadj > 0 {
|
||||
fmt.Printf("\tPOPQ AX\n")
|
||||
spadj -= 8
|
||||
}
|
||||
fmt.Printf("\tRET\n")
|
||||
|
||||
fmt.Printf("\n")
|
||||
fmt.Printf("GLOBL funcdata%d(SB), $16\n", f)
|
||||
}
|
||||
|
||||
fmt.Printf("\nTEXT start(SB),7,$0\n")
|
||||
for f := 0; f < 3; f++ {
|
||||
fmt.Printf("\tCALL func%d(SB)\n", f)
|
||||
}
|
||||
fmt.Printf("\tMOVQ $runtime·pclntab(SB), AX\n")
|
||||
fmt.Printf("\n\tRET\n")
|
||||
}
|
||||
BIN
src/cmd/newlink/testdata/hello.6
vendored
BIN
src/cmd/newlink/testdata/hello.6
vendored
Binary file not shown.
15
src/cmd/newlink/testdata/hello.s
vendored
15
src/cmd/newlink/testdata/hello.s
vendored
@@ -1,15 +0,0 @@
|
||||
TEXT _rt0_go(SB),7,$0
|
||||
MOVL $1, DI
|
||||
MOVL $hello<>(SB), SI
|
||||
MOVL $12, DX
|
||||
MOVL $0x2000004, AX
|
||||
SYSCALL
|
||||
MOVL $0, DI
|
||||
MOVL $0x2000001, AX
|
||||
SYSCALL
|
||||
RET
|
||||
|
||||
DATA hello<>+0(SB)/4, $"hell"
|
||||
DATA hello<>+4(SB)/4, $"o wo"
|
||||
DATA hello<>+8(SB)/4, $"rld\n"
|
||||
GLOBL hello<>(SB), $12
|
||||
BIN
src/cmd/newlink/testdata/layout.6
vendored
BIN
src/cmd/newlink/testdata/layout.6
vendored
Binary file not shown.
29
src/cmd/newlink/testdata/layout.s
vendored
29
src/cmd/newlink/testdata/layout.s
vendored
@@ -1,29 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Test of section assignment in layout.go.
|
||||
// Each symbol should end up in the section named by the symbol name prefix (up to the underscore).
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
TEXT text_start(SB),7,$0
|
||||
MOVQ $rodata_sym(SB), AX
|
||||
MOVQ $noptrdata_sym(SB), AX
|
||||
MOVQ $data_sym(SB), AX
|
||||
MOVQ $bss_sym(SB), AX
|
||||
MOVQ $noptrbss_sym(SB), AX
|
||||
RET
|
||||
|
||||
DATA rodata_sym(SB)/4, $1
|
||||
GLOBL rodata_sym(SB), RODATA, $4
|
||||
|
||||
DATA noptrdata_sym(SB)/4, $1
|
||||
GLOBL noptrdata_sym(SB), NOPTR, $4
|
||||
|
||||
DATA data_sym(SB)/4, $1
|
||||
GLOBL data_sym(SB), $4
|
||||
|
||||
GLOBL bss_sym(SB), $4
|
||||
|
||||
GLOBL noptrbss_sym(SB), NOPTR, $4
|
||||
55
src/cmd/newlink/testdata/link.hello.darwin.amd64
vendored
55
src/cmd/newlink/testdata/link.hello.darwin.amd64
vendored
@@ -1,55 +0,0 @@
|
||||
00000000 cf fa ed fe 07 00 00 01 03 00 00 00 02 00 00 00 |................|
|
||||
00000010 04 00 00 00 d0 02 00 00 01 00 00 00 00 00 00 00 |................|
|
||||
00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE|
|
||||
00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............|
|
||||
00000040 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00000060 00 00 00 00 00 00 00 00 19 00 00 00 38 01 00 00 |............8...|
|
||||
00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
00000080 00 10 00 00 00 00 00 00 c0 10 00 00 00 00 00 00 |................|
|
||||
00000090 00 00 00 00 00 00 00 00 c0 10 00 00 00 00 00 00 |................|
|
||||
000000a0 07 00 00 00 05 00 00 00 03 00 00 00 00 00 00 00 |................|
|
||||
000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........|
|
||||
000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
000000d0 00 20 00 00 00 00 00 00 30 00 00 00 00 00 00 00 |. ......0.......|
|
||||
000000e0 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
000000f0 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000100 5f 5f 72 6f 64 61 74 61 00 00 00 00 00 00 00 00 |__rodata........|
|
||||
00000110 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
00000120 30 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |0 ..............|
|
||||
00000130 30 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |0...............|
|
||||
*
|
||||
00000150 5f 5f 66 75 6e 63 74 61 62 00 00 00 00 00 00 00 |__functab.......|
|
||||
00000160 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
00000170 30 20 00 00 00 00 00 00 90 00 00 00 00 00 00 00 |0 ..............|
|
||||
00000180 30 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |0...............|
|
||||
*
|
||||
000001a0 19 00 00 00 98 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..|
|
||||
000001b0 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 |.........0......|
|
||||
000001c0 0c 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......|
|
||||
000001d0 0c 00 00 00 00 00 00 00 03 00 00 00 03 00 00 00 |................|
|
||||
000001e0 01 00 00 00 00 00 00 00 5f 5f 64 61 74 61 00 00 |........__data..|
|
||||
000001f0 00 00 00 00 00 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..|
|
||||
00000200 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 |.........0......|
|
||||
00000210 0c 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......|
|
||||
*
|
||||
00000230 00 00 00 00 00 00 00 00 05 00 00 00 b8 00 00 00 |................|
|
||||
00000240 04 00 00 00 2a 00 00 00 00 00 00 00 00 00 00 00 |....*...........|
|
||||
*
|
||||
000002c0 00 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......|
|
||||
*
|
||||
00001000 bf 01 00 00 00 8d 35 f5 0f 00 00 ba 0c 00 00 00 |......5.........|
|
||||
00001010 b8 04 00 00 02 0f 05 31 ff b8 01 00 00 02 0f 05 |.......1........|
|
||||
00001020 c3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00001030 fb ff ff ff 00 00 01 08 01 00 00 00 00 00 00 00 |................|
|
||||
00001040 00 20 00 00 00 00 00 00 30 00 00 00 00 00 00 00 |. ......0.......|
|
||||
00001050 30 20 00 00 00 00 00 00 80 00 00 00 00 00 00 00 |0 ..............|
|
||||
00001060 00 20 00 00 00 00 00 00 58 00 00 00 00 00 00 80 |. ......X.......|
|
||||
00001070 08 00 00 00 60 00 00 00 63 00 00 00 66 00 00 00 |....`...c...f...|
|
||||
00001080 00 00 00 00 00 00 00 00 5f 72 74 30 5f 67 6f 00 |........_rt0_go.|
|
||||
00001090 02 30 00 04 30 00 06 05 02 06 02 05 02 05 02 02 |.0..0...........|
|
||||
000010a0 02 02 02 05 02 02 02 10 00 00 00 00 00 00 00 00 |................|
|
||||
000010b0 02 00 00 00 88 00 00 00 68 65 6c 6c 6f 2e 73 00 |........hello.s.|
|
||||
*
|
||||
00002000 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a |hello world.|
|
||||
0000200c
|
||||
24
src/cmd/newlink/testdata/macho.amd64.exit9
vendored
24
src/cmd/newlink/testdata/macho.amd64.exit9
vendored
@@ -1,24 +0,0 @@
|
||||
00000000 cf fa ed fe 07 00 00 01 03 00 00 00 02 00 00 00 |................|
|
||||
00000010 03 00 00 00 98 01 00 00 01 00 00 00 00 00 00 00 |................|
|
||||
00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE|
|
||||
00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............|
|
||||
00000040 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000060 00 00 00 00 00 00 00 00 19 00 00 00 98 00 00 00 |................|
|
||||
00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
00000080 00 10 00 00 00 00 00 00 0d 10 00 00 00 00 00 00 |................|
|
||||
00000090 00 00 00 00 00 00 00 00 0d 10 00 00 00 00 00 00 |................|
|
||||
000000a0 07 00 00 00 05 00 00 00 01 00 00 00 00 00 00 00 |................|
|
||||
000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........|
|
||||
000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
000000d0 00 20 00 00 00 00 00 00 0d 00 00 00 00 00 00 00 |. ..............|
|
||||
000000e0 00 10 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
000000f0 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000100 05 00 00 00 b8 00 00 00 04 00 00 00 2a 00 00 00 |............*...|
|
||||
00000110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00000190 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |. ..............|
|
||||
000001a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00001000 b8 01 00 00 02 bf 09 00 00 00 0f 05 f4 |.............|
|
||||
0000100d
|
||||
39
src/cmd/newlink/testdata/macho.amd64.hello
vendored
39
src/cmd/newlink/testdata/macho.amd64.hello
vendored
@@ -1,39 +0,0 @@
|
||||
00000000 cf fa ed fe 07 00 00 01 03 00 00 00 02 00 00 00 |................|
|
||||
00000010 04 00 00 00 30 02 00 00 01 00 00 00 00 00 00 00 |....0...........|
|
||||
00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE|
|
||||
00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............|
|
||||
00000040 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000060 00 00 00 00 00 00 00 00 19 00 00 00 98 00 00 00 |................|
|
||||
00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
00000080 00 10 00 00 00 00 00 00 23 10 00 00 00 00 00 00 |........#.......|
|
||||
00000090 00 00 00 00 00 00 00 00 23 10 00 00 00 00 00 00 |........#.......|
|
||||
000000a0 07 00 00 00 05 00 00 00 01 00 00 00 00 00 00 00 |................|
|
||||
000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........|
|
||||
000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
000000d0 00 20 00 00 00 00 00 00 23 00 00 00 00 00 00 00 |. ......#.......|
|
||||
000000e0 00 10 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
000000f0 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000100 19 00 00 00 98 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..|
|
||||
00000110 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 |.........0......|
|
||||
00000120 0c 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......|
|
||||
00000130 0c 00 00 00 00 00 00 00 03 00 00 00 03 00 00 00 |................|
|
||||
00000140 01 00 00 00 00 00 00 00 5f 5f 64 61 74 61 00 00 |........__data..|
|
||||
00000150 00 00 00 00 00 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..|
|
||||
00000160 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 |.........0......|
|
||||
00000170 0c 00 00 00 00 00 00 00 00 20 00 00 06 00 00 00 |......... ......|
|
||||
00000180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000190 00 00 00 00 00 00 00 00 05 00 00 00 b8 00 00 00 |................|
|
||||
000001a0 04 00 00 00 2a 00 00 00 00 00 00 00 00 00 00 00 |....*...........|
|
||||
000001b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00000220 00 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......|
|
||||
00000230 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00001000 b8 04 00 00 02 bf 01 00 00 00 be 00 30 00 00 ba |............0...|
|
||||
00001010 0c 00 00 00 0f 05 b8 01 00 00 02 bf 09 00 00 00 |................|
|
||||
00001020 0f 05 f4 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00001030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00002000 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a |hello world.|
|
||||
0000200c
|
||||
34
src/cmd/newlink/testdata/macho.amd64.helloro
vendored
34
src/cmd/newlink/testdata/macho.amd64.helloro
vendored
@@ -1,34 +0,0 @@
|
||||
00000000 cf fa ed fe 07 00 00 01 03 00 00 00 02 00 00 00 |................|
|
||||
00000010 03 00 00 00 e8 01 00 00 01 00 00 00 00 00 00 00 |................|
|
||||
00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE|
|
||||
00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............|
|
||||
00000040 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000060 00 00 00 00 00 00 00 00 19 00 00 00 e8 00 00 00 |................|
|
||||
00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
00000080 00 10 00 00 00 00 00 00 0c 20 00 00 00 00 00 00 |......... ......|
|
||||
00000090 00 00 00 00 00 00 00 00 0c 20 00 00 00 00 00 00 |......... ......|
|
||||
000000a0 07 00 00 00 05 00 00 00 02 00 00 00 00 00 00 00 |................|
|
||||
000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........|
|
||||
000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
000000d0 00 20 00 00 00 00 00 00 23 00 00 00 00 00 00 00 |. ......#.......|
|
||||
000000e0 00 10 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
000000f0 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000100 5f 5f 72 6f 64 61 74 61 00 00 00 00 00 00 00 00 |__rodata........|
|
||||
00000110 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
00000120 00 30 00 00 00 00 00 00 0c 00 00 00 00 00 00 00 |.0..............|
|
||||
00000130 00 20 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |. ..............|
|
||||
00000140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000150 05 00 00 00 b8 00 00 00 04 00 00 00 2a 00 00 00 |............*...|
|
||||
00000160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
000001e0 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |. ..............|
|
||||
000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00001000 b8 04 00 00 02 bf 01 00 00 00 be 00 30 00 00 ba |............0...|
|
||||
00001010 0c 00 00 00 0f 05 b8 01 00 00 02 bf 00 00 00 00 |................|
|
||||
00001020 0f 05 f4 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00001030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00002000 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a |hello world.|
|
||||
0000200c
|
||||
BIN
src/cmd/newlink/testdata/pclntab.6
vendored
BIN
src/cmd/newlink/testdata/pclntab.6
vendored
Binary file not shown.
1751
src/cmd/newlink/testdata/pclntab.s
vendored
1751
src/cmd/newlink/testdata/pclntab.s
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,11 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
// round returns size rounded up to the next multiple of align;
|
||||
// align must be a power of two.
|
||||
func round(size, align Addr) Addr {
|
||||
return (size + align - 1) &^ (align - 1)
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Writing of executable and (for hostlink mode) object files.
|
||||
|
||||
package main
|
||||
|
||||
import "io"
|
||||
|
||||
func (p *Prog) write(w io.Writer) {
|
||||
p.Entry = p.Syms[p.startSym].Addr
|
||||
p.formatter.write(w, p)
|
||||
}
|
||||
@@ -249,6 +249,10 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
|
||||
func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool {
|
||||
// FIPS 186-3, section 4.7
|
||||
|
||||
if pub.P.Sign() == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if r.Sign() < 1 || r.Cmp(pub.Q) >= 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"crypto/elliptic"
|
||||
"crypto/sha512"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
"io"
|
||||
"math/big"
|
||||
)
|
||||
@@ -140,6 +141,8 @@ func fermatInverse(k, N *big.Int) *big.Int {
|
||||
return new(big.Int).Exp(k, nMinus2, N)
|
||||
}
|
||||
|
||||
var errZeroParam = errors.New("zero parameter")
|
||||
|
||||
// Sign signs an arbitrary length hash (which should be the result of hashing a
|
||||
// larger message) using the private key, priv. It returns the signature as a
|
||||
// pair of integers. The security of the private key depends on the entropy of
|
||||
@@ -180,7 +183,9 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
|
||||
// See [NSA] 3.4.1
|
||||
c := priv.PublicKey.Curve
|
||||
N := c.Params().N
|
||||
|
||||
if N.Sign() == 0 {
|
||||
return nil, nil, errZeroParam
|
||||
}
|
||||
var k, kInv *big.Int
|
||||
for {
|
||||
for {
|
||||
@@ -193,7 +198,7 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
|
||||
if in, ok := priv.Curve.(invertible); ok {
|
||||
kInv = in.Inverse(k)
|
||||
} else {
|
||||
kInv = fermatInverse(k, N)
|
||||
kInv = fermatInverse(k, N) // N != 0
|
||||
}
|
||||
|
||||
r, _ = priv.Curve.ScalarBaseMult(k.Bytes())
|
||||
@@ -207,7 +212,7 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
|
||||
s = new(big.Int).Mul(priv.D, r)
|
||||
s.Add(s, e)
|
||||
s.Mul(s, kInv)
|
||||
s.Mod(s, N)
|
||||
s.Mod(s, N) // N != 0
|
||||
if s.Sign() != 0 {
|
||||
break
|
||||
}
|
||||
|
||||
@@ -465,6 +465,9 @@ func decrypt(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err er
|
||||
err = ErrDecryption
|
||||
return
|
||||
}
|
||||
if priv.N.Sign() == 0 {
|
||||
return nil, ErrDecryption
|
||||
}
|
||||
|
||||
var ir *big.Int
|
||||
if random != nil {
|
||||
@@ -490,7 +493,7 @@ func decrypt(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err er
|
||||
}
|
||||
}
|
||||
bigE := big.NewInt(int64(priv.E))
|
||||
rpowe := new(big.Int).Exp(r, bigE, priv.N)
|
||||
rpowe := new(big.Int).Exp(r, bigE, priv.N) // N != 0
|
||||
cCopy := new(big.Int).Set(c)
|
||||
cCopy.Mul(cCopy, rpowe)
|
||||
cCopy.Mod(cCopy, priv.N)
|
||||
|
||||
@@ -132,10 +132,10 @@ var pkgDeps = map[string][]string{
|
||||
// End of linear dependency definitions.
|
||||
|
||||
// Operating system access.
|
||||
"syscall": {"L0", "internal/race", "unicode/utf16"},
|
||||
"syscall": {"L0", "internal/race", "internal/syscall/windows/sysdll", "unicode/utf16"},
|
||||
"internal/syscall/unix": {"L0", "syscall"},
|
||||
"internal/syscall/windows": {"L0", "syscall"},
|
||||
"internal/syscall/windows/registry": {"L0", "syscall", "unicode/utf16"},
|
||||
"internal/syscall/windows": {"L0", "syscall", "internal/syscall/windows/sysdll"},
|
||||
"internal/syscall/windows/registry": {"L0", "syscall", "internal/syscall/windows/sysdll", "unicode/utf16"},
|
||||
"time": {"L0", "syscall", "internal/syscall/windows/registry"},
|
||||
"os": {"L1", "os", "syscall", "time", "internal/syscall/windows"},
|
||||
"path/filepath": {"L2", "os", "syscall"},
|
||||
|
||||
@@ -8,7 +8,7 @@ package registry
|
||||
|
||||
import "syscall"
|
||||
|
||||
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall.go
|
||||
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go -systemdll syscall.go
|
||||
|
||||
const (
|
||||
_REG_OPTION_NON_VOLATILE = 0
|
||||
|
||||
@@ -4,12 +4,13 @@ package registry
|
||||
|
||||
import "unsafe"
|
||||
import "syscall"
|
||||
import "internal/syscall/windows/sysdll"
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
var (
|
||||
modadvapi32 = syscall.NewLazyDLL("advapi32.dll")
|
||||
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
modadvapi32 = syscall.NewLazyDLL(sysdll.Add("advapi32.dll"))
|
||||
modkernel32 = syscall.NewLazyDLL(sysdll.Add("kernel32.dll"))
|
||||
|
||||
procRegCreateKeyExW = modadvapi32.NewProc("RegCreateKeyExW")
|
||||
procRegDeleteKeyW = modadvapi32.NewProc("RegDeleteKeyW")
|
||||
|
||||
@@ -6,7 +6,7 @@ package windows
|
||||
|
||||
import "syscall"
|
||||
|
||||
//go:generate go run ../../../syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go
|
||||
//go:generate go run ../../../syscall/mksyscall_windows.go -output zsyscall_windows.go -systemdll syscall_windows.go
|
||||
|
||||
const GAA_FLAG_INCLUDE_PREFIX = 0x00000010
|
||||
|
||||
|
||||
28
src/internal/syscall/windows/sysdll/sysdll.go
Normal file
28
src/internal/syscall/windows/sysdll/sysdll.go
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright 2016 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 sysdll is an internal leaf package that records and reports
|
||||
// which Windows DLL names are used by Go itself. These DLLs are then
|
||||
// only loaded from the System32 directory. See Issue 14959.
|
||||
package sysdll
|
||||
|
||||
// IsSystemDLL reports whether the named dll key (a base name, like
|
||||
// "foo.dll") is a system DLL which should only be loaded from the
|
||||
// Windows SYSTEM32 directory.
|
||||
//
|
||||
// Filenames are case sensitive, but that doesn't matter because
|
||||
// the case registered with Add is also the same case used with
|
||||
// LoadDLL later.
|
||||
//
|
||||
// It has no associated mutex and should only be mutated serially
|
||||
// (currently: during init), and not concurrent with DLL loading.
|
||||
var IsSystemDLL = map[string]bool{}
|
||||
|
||||
// Add notes that dll is a system32 DLL which should only be loaded
|
||||
// from the Windows SYSTEM32 directory. It returns its argument back,
|
||||
// for ease of use in generated code.
|
||||
func Add(dll string) string {
|
||||
IsSystemDLL[dll] = true
|
||||
return dll
|
||||
}
|
||||
@@ -4,12 +4,13 @@ package windows
|
||||
|
||||
import "unsafe"
|
||||
import "syscall"
|
||||
import "internal/syscall/windows/sysdll"
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
var (
|
||||
modiphlpapi = syscall.NewLazyDLL("iphlpapi.dll")
|
||||
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
modiphlpapi = syscall.NewLazyDLL(sysdll.Add("iphlpapi.dll"))
|
||||
modkernel32 = syscall.NewLazyDLL(sysdll.Add("kernel32.dll"))
|
||||
|
||||
procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses")
|
||||
procGetComputerNameExW = modkernel32.NewProc("GetComputerNameExW")
|
||||
|
||||
@@ -15,3 +15,7 @@ func NumberOfProcessors() int32 {
|
||||
stdcall1(_GetSystemInfo, uintptr(unsafe.Pointer(&info)))
|
||||
return int32(info.dwnumberofprocessors)
|
||||
}
|
||||
|
||||
func LoadLibraryExStatus() (useEx, haveEx, haveFlags bool) {
|
||||
return useLoadLibraryEx, _LoadLibraryExW != nil, _AddDllDirectory != nil
|
||||
}
|
||||
|
||||
@@ -93,8 +93,11 @@ var (
|
||||
|
||||
// Following syscalls are only available on some Windows PCs.
|
||||
// We will load syscalls, if available, before using them.
|
||||
_AddDllDirectory,
|
||||
_AddVectoredContinueHandler,
|
||||
_GetQueuedCompletionStatusEx stdFunction
|
||||
_GetQueuedCompletionStatusEx,
|
||||
_LoadLibraryExW,
|
||||
_ stdFunction
|
||||
)
|
||||
|
||||
type sigset struct{}
|
||||
@@ -117,8 +120,10 @@ func loadOptionalSyscalls() {
|
||||
return stdFunction(unsafe.Pointer(f))
|
||||
}
|
||||
if l != 0 {
|
||||
_AddDllDirectory = findfunc("AddDllDirectory")
|
||||
_AddVectoredContinueHandler = findfunc("AddVectoredContinueHandler")
|
||||
_GetQueuedCompletionStatusEx = findfunc("GetQueuedCompletionStatusEx")
|
||||
_LoadLibraryExW = findfunc("LoadLibraryExW")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,6 +132,11 @@ func getLoadLibrary() uintptr {
|
||||
return uintptr(unsafe.Pointer(_LoadLibraryW))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func getLoadLibraryEx() uintptr {
|
||||
return uintptr(unsafe.Pointer(_LoadLibraryExW))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func getGetProcAddress() uintptr {
|
||||
return uintptr(unsafe.Pointer(_GetProcAddress))
|
||||
@@ -161,6 +171,22 @@ const (
|
||||
// in sys_windows_386.s and sys_windows_amd64.s
|
||||
func externalthreadhandler()
|
||||
|
||||
// When loading DLLs, we prefer to use LoadLibraryEx with
|
||||
// LOAD_LIBRARY_SEARCH_* flags, if available. LoadLibraryEx is not
|
||||
// available on old Windows, though, and the LOAD_LIBRARY_SEARCH_*
|
||||
// flags are not available on some versions of Windows without a
|
||||
// security patch.
|
||||
//
|
||||
// https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says:
|
||||
// "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows
|
||||
// Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on
|
||||
// systems that have KB2533623 installed. To determine whether the
|
||||
// flags are available, use GetProcAddress to get the address of the
|
||||
// AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories
|
||||
// function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_*
|
||||
// flags can be used with LoadLibraryEx."
|
||||
var useLoadLibraryEx bool
|
||||
|
||||
func osinit() {
|
||||
asmstdcallAddr = unsafe.Pointer(funcPC(asmstdcall))
|
||||
|
||||
@@ -168,6 +194,8 @@ func osinit() {
|
||||
|
||||
loadOptionalSyscalls()
|
||||
|
||||
useLoadLibraryEx = (_LoadLibraryExW != nil && _AddDllDirectory != nil)
|
||||
|
||||
disableWER()
|
||||
|
||||
externalthreadhandlerp = funcPC(externalthreadhandler)
|
||||
|
||||
@@ -88,6 +88,41 @@ func compileCallback(fn eface, cleanstack bool) (code uintptr) {
|
||||
return callbackasmAddr(n)
|
||||
}
|
||||
|
||||
const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800
|
||||
|
||||
//go:linkname syscall_loadsystemlibrary syscall.loadsystemlibrary
|
||||
//go:nosplit
|
||||
func syscall_loadsystemlibrary(filename *uint16) (handle, err uintptr) {
|
||||
c := &getg().m.syscall
|
||||
|
||||
if useLoadLibraryEx {
|
||||
c.fn = getLoadLibraryEx()
|
||||
c.n = 3
|
||||
args := struct {
|
||||
lpFileName *uint16
|
||||
hFile uintptr // always 0
|
||||
flags uint32
|
||||
}{filename, 0, _LOAD_LIBRARY_SEARCH_SYSTEM32}
|
||||
c.args = uintptr(noescape(unsafe.Pointer(&args)))
|
||||
} else {
|
||||
// User is on Windows XP or something ancient.
|
||||
// The caller wanted to only load the filename DLL
|
||||
// from the System32 directory but that facility
|
||||
// doesn't exist, so just load it the normal way. This
|
||||
// is a potential security risk, but so is Windows XP.
|
||||
c.fn = getLoadLibrary()
|
||||
c.n = 1
|
||||
c.args = uintptr(noescape(unsafe.Pointer(&filename)))
|
||||
}
|
||||
|
||||
cgocall(asmstdcallAddr, unsafe.Pointer(c))
|
||||
handle = c.r1
|
||||
if handle == 0 {
|
||||
err = c.err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//go:linkname syscall_loadlibrary syscall.loadlibrary
|
||||
//go:nosplit
|
||||
func syscall_loadlibrary(filename *uint16) (handle, err uintptr) {
|
||||
|
||||
@@ -7,6 +7,8 @@ package runtime_test
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"internal/syscall/windows/sysdll"
|
||||
"internal/testenv"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -771,3 +773,99 @@ func TestNumCPU(t *testing.T) {
|
||||
t.Fatalf("SetProcessAffinityMask didn't set newmask of 0x%x. Current mask is 0x%x.", newmask, mask)
|
||||
}
|
||||
}
|
||||
|
||||
// See Issue 14959
|
||||
func TestDLLPreloadMitigation(t *testing.T) {
|
||||
if _, err := exec.LookPath("gcc"); err != nil {
|
||||
t.Skip("skipping test: gcc is missing")
|
||||
}
|
||||
|
||||
tmpdir, err := ioutil.TempDir("", "TestDLLPreloadMitigation")
|
||||
if err != nil {
|
||||
t.Fatal("TempDir failed: ", err)
|
||||
}
|
||||
defer func() {
|
||||
err := os.RemoveAll(tmpdir)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}()
|
||||
|
||||
dir0, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Chdir(dir0)
|
||||
|
||||
const src = `
|
||||
#include <stdint.h>
|
||||
#include <windows.h>
|
||||
|
||||
uintptr_t cfunc() {
|
||||
SetLastError(123);
|
||||
}
|
||||
`
|
||||
srcname := "nojack.c"
|
||||
err = ioutil.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
name := "nojack.dll"
|
||||
cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", name, srcname)
|
||||
cmd.Dir = tmpdir
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to build dll: %v - %v", err, string(out))
|
||||
}
|
||||
dllpath := filepath.Join(tmpdir, name)
|
||||
|
||||
dll := syscall.MustLoadDLL(dllpath)
|
||||
dll.MustFindProc("cfunc")
|
||||
dll.Release()
|
||||
|
||||
// Get into the directory with the DLL we'll load by base name
|
||||
// ("nojack.dll") Think of this as the user double-clicking an
|
||||
// installer from their Downloads directory where a browser
|
||||
// silently downloaded some malicious DLLs.
|
||||
os.Chdir(tmpdir)
|
||||
|
||||
// First before we can load a DLL from the current directory,
|
||||
// loading it only as "nojack.dll", without an absolute path.
|
||||
delete(sysdll.IsSystemDLL, name) // in case test was run repeatedly
|
||||
dll, err = syscall.LoadDLL(name)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load %s by base name before sysdll registration: %v", name, err)
|
||||
}
|
||||
dll.Release()
|
||||
|
||||
// And now verify that if we register it as a system32-only
|
||||
// DLL, the implicit loading from the current directory no
|
||||
// longer works.
|
||||
sysdll.IsSystemDLL[name] = true
|
||||
dll, err = syscall.LoadDLL(name)
|
||||
if err == nil {
|
||||
dll.Release()
|
||||
if wantLoadLibraryEx() {
|
||||
t.Fatalf("Bad: insecure load of DLL by base name %q before sysdll registration: %v", name, err)
|
||||
}
|
||||
t.Skip("insecure load of DLL, but expected")
|
||||
}
|
||||
}
|
||||
|
||||
// wantLoadLibraryEx reports whether we expect LoadLibraryEx to work for tests.
|
||||
func wantLoadLibraryEx() bool {
|
||||
return testenv.Builder() == "windows-amd64-gce" || testenv.Builder() == "windows-386-gce"
|
||||
}
|
||||
|
||||
func TestLoadLibraryEx(t *testing.T) {
|
||||
use, have, flags := runtime.LoadLibraryExStatus()
|
||||
if use {
|
||||
return // success.
|
||||
}
|
||||
if wantLoadLibraryEx() {
|
||||
t.Fatalf("Expected LoadLibraryEx+flags to be available. (LoadLibraryEx=%v; flags=%v)",
|
||||
have, flags)
|
||||
}
|
||||
t.Skipf("LoadLibraryEx not usable, but not expected. (LoadLibraryEx=%v; flags=%v)",
|
||||
have, flags)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package syscall
|
||||
|
||||
import (
|
||||
"internal/syscall/windows/sysdll"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
@@ -26,6 +27,7 @@ func Syscall9(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 u
|
||||
func Syscall12(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 uintptr) (r1, r2 uintptr, err Errno)
|
||||
func Syscall15(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2 uintptr, err Errno)
|
||||
func loadlibrary(filename *uint16) (handle uintptr, err Errno)
|
||||
func loadsystemlibrary(filename *uint16) (handle uintptr, err Errno)
|
||||
func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno)
|
||||
|
||||
// A DLL implements access to a single DLL.
|
||||
@@ -34,13 +36,26 @@ type DLL struct {
|
||||
Handle Handle
|
||||
}
|
||||
|
||||
// LoadDLL loads DLL file into memory.
|
||||
func LoadDLL(name string) (dll *DLL, err error) {
|
||||
// LoadDLL loads the named DLL file into memory.
|
||||
//
|
||||
// If name is not an absolute path and is not a known system DLL used by
|
||||
// Go, Windows will search for the named DLL in many locations, causing
|
||||
// potential DLL preloading attacks.
|
||||
//
|
||||
// Use LazyDLL in golang.org/x/sys/windows for a secure way to
|
||||
// load system DLLs.
|
||||
func LoadDLL(name string) (*DLL, error) {
|
||||
namep, err := UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h, e := loadlibrary(namep)
|
||||
var h uintptr
|
||||
var e Errno
|
||||
if sysdll.IsSystemDLL[name] {
|
||||
h, e = loadsystemlibrary(namep)
|
||||
} else {
|
||||
h, e = loadlibrary(namep)
|
||||
}
|
||||
if e != 0 {
|
||||
return nil, &DLLError{
|
||||
Err: e,
|
||||
@@ -166,6 +181,12 @@ func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
|
||||
// It will delay the load of the DLL until the first
|
||||
// call to its Handle method or to one of its
|
||||
// LazyProc's Addr method.
|
||||
//
|
||||
// LazyDLL is subject to the same DLL preloading attacks as documented
|
||||
// on LoadDLL.
|
||||
//
|
||||
// Use LazyDLL in golang.org/x/sys/windows for a secure way to
|
||||
// load system DLLs.
|
||||
type LazyDLL struct {
|
||||
mu sync.Mutex
|
||||
dll *DLL // non nil once DLL is loaded
|
||||
|
||||
@@ -57,6 +57,7 @@ import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
@@ -65,6 +66,8 @@ import (
|
||||
var (
|
||||
filename = flag.String("output", "", "output file name (standard output if omitted)")
|
||||
printTraceFlag = flag.Bool("trace", false, "generate print statement after every syscall")
|
||||
systemDLL = flag.Bool("systemdll", false, "whether all DLLs should be loaded from the Windows system directory")
|
||||
sysRepo = flag.Bool("xsys", false, "whether this code is for the x/sys subrepo")
|
||||
)
|
||||
|
||||
func trim(s string) string {
|
||||
@@ -593,8 +596,14 @@ func (f *Fn) HelperName() string {
|
||||
|
||||
// Source files and functions.
|
||||
type Source struct {
|
||||
Funcs []*Fn
|
||||
Files []string
|
||||
Funcs []*Fn
|
||||
Files []string
|
||||
Imports []string
|
||||
}
|
||||
|
||||
func (src *Source) Import(pkg string) {
|
||||
src.Imports = append(src.Imports, pkg)
|
||||
sort.Strings(src.Imports)
|
||||
}
|
||||
|
||||
// ParseFiles parses files listed in fs and extracts all syscall
|
||||
@@ -604,6 +613,12 @@ func ParseFiles(fs []string) (*Source, error) {
|
||||
src := &Source{
|
||||
Funcs: make([]*Fn, 0),
|
||||
Files: make([]string, 0),
|
||||
Imports: []string{
|
||||
"unsafe",
|
||||
},
|
||||
}
|
||||
if *systemDLL {
|
||||
src.Import("internal/syscall/windows/sysdll")
|
||||
}
|
||||
for _, file := range fs {
|
||||
if err := src.ParseFile(file); err != nil {
|
||||
@@ -676,9 +691,30 @@ func (src *Source) ParseFile(path string) error {
|
||||
|
||||
// Generate output source file from a source set src.
|
||||
func (src *Source) Generate(w io.Writer) error {
|
||||
if *sysRepo && packageName != "windows" {
|
||||
src.Import("golang.org/x/sys/windows")
|
||||
}
|
||||
if packageName != "syscall" {
|
||||
src.Import("syscall")
|
||||
}
|
||||
funcMap := template.FuncMap{
|
||||
"packagename": packagename,
|
||||
"syscalldot": syscalldot,
|
||||
"newlazydll": func(dll string) string {
|
||||
arg := "\"" + dll + ".dll\""
|
||||
if *systemDLL {
|
||||
arg = "sysdll.Add(" + arg + ")"
|
||||
}
|
||||
if *sysRepo {
|
||||
if packageName == "windows" {
|
||||
return "&LazyDLL{Name: " + arg + ", System: true}"
|
||||
} else {
|
||||
return "&windows.LazyDLL{Name: " + arg + ", System: true}"
|
||||
}
|
||||
} else {
|
||||
return syscalldot() + "NewLazyDLL(" + arg + ")"
|
||||
}
|
||||
},
|
||||
}
|
||||
t := template.Must(template.New("main").Funcs(funcMap).Parse(srcTemplate))
|
||||
err := t.Execute(w, src)
|
||||
@@ -733,8 +769,10 @@ const srcTemplate = `
|
||||
|
||||
package {{packagename}}
|
||||
|
||||
import "unsafe"{{if syscalldot}}
|
||||
import "syscall"{{end}}
|
||||
import (
|
||||
{{range .Imports}}"{{.}}"
|
||||
{{end}}
|
||||
)
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
@@ -746,7 +784,7 @@ var (
|
||||
|
||||
{{/* help functions */}}
|
||||
|
||||
{{define "dlls"}}{{range .DLLs}} mod{{.}} = {{syscalldot}}NewLazyDLL("{{.}}.dll")
|
||||
{{define "dlls"}}{{range .DLLs}} mod{{.}} = {{newlazydll .}}
|
||||
{{end}}{{end}}
|
||||
|
||||
{{define "funcnames"}}{{range .Funcs}} proc{{.DLLFuncName}} = mod{{.DLLName}}.NewProc("{{.DLLFuncName}}")
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//go:generate go run mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go security_windows.go
|
||||
//go:generate go run mksyscall_windows.go -systemdll -output zsyscall_windows.go syscall_windows.go security_windows.go
|
||||
|
||||
type Handle uintptr
|
||||
|
||||
|
||||
@@ -2,22 +2,25 @@
|
||||
|
||||
package syscall
|
||||
|
||||
import "unsafe"
|
||||
import (
|
||||
"internal/syscall/windows/sysdll"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
var (
|
||||
modkernel32 = NewLazyDLL("kernel32.dll")
|
||||
modadvapi32 = NewLazyDLL("advapi32.dll")
|
||||
modshell32 = NewLazyDLL("shell32.dll")
|
||||
modmswsock = NewLazyDLL("mswsock.dll")
|
||||
modcrypt32 = NewLazyDLL("crypt32.dll")
|
||||
modws2_32 = NewLazyDLL("ws2_32.dll")
|
||||
moddnsapi = NewLazyDLL("dnsapi.dll")
|
||||
modiphlpapi = NewLazyDLL("iphlpapi.dll")
|
||||
modsecur32 = NewLazyDLL("secur32.dll")
|
||||
modnetapi32 = NewLazyDLL("netapi32.dll")
|
||||
moduserenv = NewLazyDLL("userenv.dll")
|
||||
modkernel32 = NewLazyDLL(sysdll.Add("kernel32.dll"))
|
||||
modadvapi32 = NewLazyDLL(sysdll.Add("advapi32.dll"))
|
||||
modshell32 = NewLazyDLL(sysdll.Add("shell32.dll"))
|
||||
modmswsock = NewLazyDLL(sysdll.Add("mswsock.dll"))
|
||||
modcrypt32 = NewLazyDLL(sysdll.Add("crypt32.dll"))
|
||||
modws2_32 = NewLazyDLL(sysdll.Add("ws2_32.dll"))
|
||||
moddnsapi = NewLazyDLL(sysdll.Add("dnsapi.dll"))
|
||||
modiphlpapi = NewLazyDLL(sysdll.Add("iphlpapi.dll"))
|
||||
modsecur32 = NewLazyDLL(sysdll.Add("secur32.dll"))
|
||||
modnetapi32 = NewLazyDLL(sysdll.Add("netapi32.dll"))
|
||||
moduserenv = NewLazyDLL(sysdll.Add("userenv.dll"))
|
||||
|
||||
procGetLastError = modkernel32.NewProc("GetLastError")
|
||||
procLoadLibraryW = modkernel32.NewProc("LoadLibraryW")
|
||||
|
||||
Reference in New Issue
Block a user