mirror of
https://github.com/golang/go.git
synced 2026-01-29 15:12:08 +03:00
Compare commits
62 Commits
dev.inline
...
go1.4.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
883bc6ed0e | ||
|
|
5e9b6cac52 | ||
|
|
e4acac3dfb | ||
|
|
3e5977f99d | ||
|
|
02cf0526bf | ||
|
|
3124622303 | ||
|
|
a255645770 | ||
|
|
2b7d0b4c0d | ||
|
|
5caa9d15f2 | ||
|
|
b64f8f8764 | ||
|
|
15ce943f15 | ||
|
|
886b02d705 | ||
|
|
590548d7bd | ||
|
|
00d88f68bb | ||
|
|
cece1bd03f | ||
|
|
ac15ad8a38 | ||
|
|
7df87f5066 | ||
|
|
97b84fc4c8 | ||
|
|
add1ee0ed5 | ||
|
|
d9e0ca4055 | ||
|
|
ff2ab29914 | ||
|
|
6609baf2f7 | ||
|
|
957ed90d0e | ||
|
|
cc7bbb0ae9 | ||
|
|
4482c7b1a1 | ||
|
|
7cb53b8ca2 | ||
|
|
c303df658d | ||
|
|
75b53641f2 | ||
|
|
7412503a43 | ||
|
|
aec78b7a61 | ||
|
|
031850b689 | ||
|
|
f9ae81edca | ||
|
|
9820fbcf7b | ||
|
|
d88fe6146d | ||
|
|
c089afbbd7 | ||
|
|
05560adf62 | ||
|
|
c139772a39 | ||
|
|
c009bcdd8b | ||
|
|
75c8a78e61 | ||
|
|
f42f5263ad | ||
|
|
59730b3343 | ||
|
|
7aead4c6fd | ||
|
|
19bbff8a32 | ||
|
|
c29baa647e | ||
|
|
4d1f720b70 | ||
|
|
79a3df47aa | ||
|
|
3d34461177 | ||
|
|
28208eb8e3 | ||
|
|
95e92ac420 | ||
|
|
783ad67982 | ||
|
|
d3ae115c41 | ||
|
|
738ccf32d9 | ||
|
|
f6818121ed | ||
|
|
791fec05e4 | ||
|
|
a791780bfd | ||
|
|
427ee80413 | ||
|
|
b4df0154c2 | ||
|
|
c9e183e781 | ||
|
|
30ef146819 | ||
|
|
daf5d41471 | ||
|
|
c1fc059b08 | ||
|
|
335ad3db99 |
10
.gitattributes
vendored
Normal file
10
.gitattributes
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# Treat all files in the Go repo as binary, with no git magic updating
|
||||
# line endings. Windows users contributing to Go will need to use a
|
||||
# modern version of git and editors capable of LF line endings.
|
||||
#
|
||||
# We'll prevent accidental CRLF line endings from entering the repo
|
||||
# via the git-review gofmt checks.
|
||||
#
|
||||
# See golang.org/issue/9281
|
||||
|
||||
* -text
|
||||
53
.gitignore
vendored
Normal file
53
.gitignore
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
.DS_Store
|
||||
*.[5689ao]
|
||||
*.a[5689o]
|
||||
*.so
|
||||
*.pyc
|
||||
._*
|
||||
.nfs.*
|
||||
[5689a].out
|
||||
*~
|
||||
*.orig
|
||||
*.rej
|
||||
*.exe
|
||||
.*.swp
|
||||
core
|
||||
*.cgo*.go
|
||||
*.cgo*.c
|
||||
_cgo_*
|
||||
_obj
|
||||
_test
|
||||
_testmain.go
|
||||
build.out
|
||||
test.out
|
||||
doc/articles/wiki/*.bin
|
||||
include/plan9/libc_plan9.h
|
||||
misc/cgo/life/run.out
|
||||
misc/cgo/stdio/run.out
|
||||
misc/cgo/testso/main
|
||||
misc/dashboard/builder/builder
|
||||
src/cmd/?a/y.output
|
||||
src/liblink/anames?.c
|
||||
src/cmd/cc/y.output
|
||||
src/cmd/cgo/zdefaultcc.go
|
||||
src/cmd/dist/dist.dSYM
|
||||
src/cmd/gc/mkbuiltin1
|
||||
src/cmd/gc/opnames.h
|
||||
src/cmd/gc/y.output
|
||||
src/cmd/go/zdefaultcc.go
|
||||
src/go/doc/headscan
|
||||
src/runtime/mkversion
|
||||
src/runtime/zaexperiment.h
|
||||
src/runtime/zversion.go
|
||||
src/unicode/maketables
|
||||
src/*.*/
|
||||
test/pass.out
|
||||
test/run.out
|
||||
test/times.out
|
||||
test/garbage/*.out
|
||||
goinstall.log
|
||||
last-change
|
||||
VERSION.cache
|
||||
|
||||
bin/
|
||||
pkg/
|
||||
604
api/go1.4.txt
Normal file
604
api/go1.4.txt
Normal file
@@ -0,0 +1,604 @@
|
||||
# CL 134210043 archive/zip: add Writer.Flush, Brad Fitzpatrick <bradfitz@golang.org>
|
||||
pkg archive/zip, method (*Writer) Flush() error
|
||||
|
||||
# CL 97140043 compress/flate: add Reset() to allow reusing large buffers to compress multiple buffers, James Robinson <jamesr@google.com>
|
||||
pkg compress/flate, type Resetter interface { Reset }
|
||||
pkg compress/flate, type Resetter interface, Reset(io.Reader, []uint8) error
|
||||
pkg compress/zlib, type Resetter interface { Reset }
|
||||
pkg compress/zlib, type Resetter interface, Reset(io.Reader, []uint8) error
|
||||
|
||||
# CL 159120044 compress/gzip: allow stopping at end of first stream, Russ Cox <rsc@golang.org>
|
||||
pkg compress/gzip, method (*Reader) Multistream(bool)
|
||||
|
||||
# CL 138800043 crypto: Add SHA3 functions in go.crypto/sha3 to the Hash enum., David Leon Gil <coruus@gmail.com>
|
||||
pkg crypto, const SHA3_224 = 10
|
||||
pkg crypto, const SHA3_224 Hash
|
||||
pkg crypto, const SHA3_256 = 11
|
||||
pkg crypto, const SHA3_256 Hash
|
||||
pkg crypto, const SHA3_384 = 12
|
||||
pkg crypto, const SHA3_384 Hash
|
||||
pkg crypto, const SHA3_512 = 13
|
||||
pkg crypto, const SHA3_512 Hash
|
||||
|
||||
# CL 114680043 crypto: add Signer, Adam Langley <agl@golang.org>
|
||||
pkg crypto, method (Hash) HashFunc() Hash
|
||||
pkg crypto, type Signer interface { Public, Sign }
|
||||
pkg crypto, type Signer interface, Public() PublicKey
|
||||
pkg crypto, type Signer interface, Sign(io.Reader, []uint8, SignerOpts) ([]uint8, error)
|
||||
pkg crypto, type SignerOpts interface { HashFunc }
|
||||
pkg crypto, type SignerOpts interface, HashFunc() Hash
|
||||
pkg crypto/ecdsa, method (*PrivateKey) Public() crypto.PublicKey
|
||||
pkg crypto/ecdsa, method (*PrivateKey) Sign(io.Reader, []uint8, crypto.SignerOpts) ([]uint8, error)
|
||||
pkg crypto/rsa, method (*PSSOptions) HashFunc() crypto.Hash
|
||||
pkg crypto/rsa, method (*PrivateKey) Public() crypto.PublicKey
|
||||
pkg crypto/rsa, method (*PrivateKey) Sign(io.Reader, []uint8, crypto.SignerOpts) ([]uint8, error)
|
||||
pkg crypto/rsa, type PSSOptions struct, Hash crypto.Hash
|
||||
|
||||
# CL 157090043 crypto/tls: support TLS_FALLBACK_SCSV as a server., Adam Langley <agl@golang.org>
|
||||
pkg crypto/tls, const TLS_FALLBACK_SCSV = 22016
|
||||
pkg crypto/tls, const TLS_FALLBACK_SCSV uint16
|
||||
|
||||
# CL 107400043 crypto/tls: Added dynamic alternative to NameToCertificate map for SNI, Percy Wegmann <ox.to.a.cart@gmail.com>
|
||||
pkg crypto/tls, type ClientHelloInfo struct
|
||||
pkg crypto/tls, type ClientHelloInfo struct, CipherSuites []uint16
|
||||
pkg crypto/tls, type ClientHelloInfo struct, ServerName string
|
||||
pkg crypto/tls, type ClientHelloInfo struct, SupportedCurves []CurveID
|
||||
pkg crypto/tls, type ClientHelloInfo struct, SupportedPoints []uint8
|
||||
pkg crypto/tls, type Config struct, GetCertificate func(*ClientHelloInfo) (*Certificate, error)
|
||||
pkg crypto/tls, type ConnectionState struct, TLSUnique []uint8
|
||||
|
||||
# CL 153420045 crypto/x509: continue to recognise MaxPathLen of zero as "no value"., Adam Langley <agl@golang.org>
|
||||
pkg crypto/x509, type Certificate struct, MaxPathLenZero bool
|
||||
|
||||
# CL 158950043 database/sql: add Drivers, returning list of registered drivers, Russ Cox <rsc@golang.org>
|
||||
pkg database/sql, func Drivers() []string
|
||||
|
||||
# CL 117280043 debug/dwarf: fix Reader panic on DW_TAG_unspecified_type, Derek Parker <parkerderek86@gmail.com>
|
||||
pkg debug/dwarf, method (*UnspecifiedType) Basic() *BasicType
|
||||
pkg debug/dwarf, method (*UnspecifiedType) Common() *CommonType
|
||||
pkg debug/dwarf, method (*UnspecifiedType) Size() int64
|
||||
pkg debug/dwarf, method (*UnspecifiedType) String() string
|
||||
pkg debug/dwarf, type UnspecifiedType struct
|
||||
pkg debug/dwarf, type UnspecifiedType struct, embedded BasicType
|
||||
|
||||
# CL 132000043 debug/elf: support arm64 relocations, Michael Hudson-Doyle <michael.hudson@linaro.org>
|
||||
pkg debug/elf, const EM_AARCH64 = 183
|
||||
pkg debug/elf, const EM_AARCH64 Machine
|
||||
pkg debug/elf, const R_AARCH64_ABS16 = 259
|
||||
pkg debug/elf, const R_AARCH64_ABS16 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_ABS32 = 258
|
||||
pkg debug/elf, const R_AARCH64_ABS32 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_ABS64 = 257
|
||||
pkg debug/elf, const R_AARCH64_ABS64 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_ADD_ABS_LO12_NC = 277
|
||||
pkg debug/elf, const R_AARCH64_ADD_ABS_LO12_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_ADR_GOT_PAGE = 311
|
||||
pkg debug/elf, const R_AARCH64_ADR_GOT_PAGE R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_ADR_PREL_LO21 = 274
|
||||
pkg debug/elf, const R_AARCH64_ADR_PREL_LO21 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_ADR_PREL_PG_HI21 = 275
|
||||
pkg debug/elf, const R_AARCH64_ADR_PREL_PG_HI21 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_ADR_PREL_PG_HI21_NC = 276
|
||||
pkg debug/elf, const R_AARCH64_ADR_PREL_PG_HI21_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_CALL26 = 283
|
||||
pkg debug/elf, const R_AARCH64_CALL26 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_CONDBR19 = 280
|
||||
pkg debug/elf, const R_AARCH64_CONDBR19 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_COPY = 1024
|
||||
pkg debug/elf, const R_AARCH64_COPY R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_GLOB_DAT = 1025
|
||||
pkg debug/elf, const R_AARCH64_GLOB_DAT R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_GOT_LD_PREL19 = 309
|
||||
pkg debug/elf, const R_AARCH64_GOT_LD_PREL19 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_IRELATIVE = 1032
|
||||
pkg debug/elf, const R_AARCH64_IRELATIVE R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_JUMP26 = 282
|
||||
pkg debug/elf, const R_AARCH64_JUMP26 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_JUMP_SLOT = 1026
|
||||
pkg debug/elf, const R_AARCH64_JUMP_SLOT R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_LD64_GOT_LO12_NC = 312
|
||||
pkg debug/elf, const R_AARCH64_LD64_GOT_LO12_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_LDST128_ABS_LO12_NC = 299
|
||||
pkg debug/elf, const R_AARCH64_LDST128_ABS_LO12_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_LDST16_ABS_LO12_NC = 284
|
||||
pkg debug/elf, const R_AARCH64_LDST16_ABS_LO12_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_LDST32_ABS_LO12_NC = 285
|
||||
pkg debug/elf, const R_AARCH64_LDST32_ABS_LO12_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_LDST64_ABS_LO12_NC = 286
|
||||
pkg debug/elf, const R_AARCH64_LDST64_ABS_LO12_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_LDST8_ABS_LO12_NC = 278
|
||||
pkg debug/elf, const R_AARCH64_LDST8_ABS_LO12_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_LD_PREL_LO19 = 273
|
||||
pkg debug/elf, const R_AARCH64_LD_PREL_LO19 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_MOVW_SABS_G0 = 270
|
||||
pkg debug/elf, const R_AARCH64_MOVW_SABS_G0 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_MOVW_SABS_G1 = 271
|
||||
pkg debug/elf, const R_AARCH64_MOVW_SABS_G1 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_MOVW_SABS_G2 = 272
|
||||
pkg debug/elf, const R_AARCH64_MOVW_SABS_G2 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_MOVW_UABS_G0 = 263
|
||||
pkg debug/elf, const R_AARCH64_MOVW_UABS_G0 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_MOVW_UABS_G0_NC = 264
|
||||
pkg debug/elf, const R_AARCH64_MOVW_UABS_G0_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_MOVW_UABS_G1 = 265
|
||||
pkg debug/elf, const R_AARCH64_MOVW_UABS_G1 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_MOVW_UABS_G1_NC = 266
|
||||
pkg debug/elf, const R_AARCH64_MOVW_UABS_G1_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_MOVW_UABS_G2 = 267
|
||||
pkg debug/elf, const R_AARCH64_MOVW_UABS_G2 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_MOVW_UABS_G2_NC = 268
|
||||
pkg debug/elf, const R_AARCH64_MOVW_UABS_G2_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_MOVW_UABS_G3 = 269
|
||||
pkg debug/elf, const R_AARCH64_MOVW_UABS_G3 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_NONE = 0
|
||||
pkg debug/elf, const R_AARCH64_NONE R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_NULL = 256
|
||||
pkg debug/elf, const R_AARCH64_NULL R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_ABS16 = 2
|
||||
pkg debug/elf, const R_AARCH64_P32_ABS16 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_ABS32 = 1
|
||||
pkg debug/elf, const R_AARCH64_P32_ABS32 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_ADD_ABS_LO12_NC = 12
|
||||
pkg debug/elf, const R_AARCH64_P32_ADD_ABS_LO12_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_ADR_GOT_PAGE = 26
|
||||
pkg debug/elf, const R_AARCH64_P32_ADR_GOT_PAGE R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_ADR_PREL_LO21 = 10
|
||||
pkg debug/elf, const R_AARCH64_P32_ADR_PREL_LO21 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_ADR_PREL_PG_HI21 = 11
|
||||
pkg debug/elf, const R_AARCH64_P32_ADR_PREL_PG_HI21 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_CALL26 = 21
|
||||
pkg debug/elf, const R_AARCH64_P32_CALL26 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_CONDBR19 = 19
|
||||
pkg debug/elf, const R_AARCH64_P32_CONDBR19 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_COPY = 180
|
||||
pkg debug/elf, const R_AARCH64_P32_COPY R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_GLOB_DAT = 181
|
||||
pkg debug/elf, const R_AARCH64_P32_GLOB_DAT R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_GOT_LD_PREL19 = 25
|
||||
pkg debug/elf, const R_AARCH64_P32_GOT_LD_PREL19 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_IRELATIVE = 188
|
||||
pkg debug/elf, const R_AARCH64_P32_IRELATIVE R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_JUMP26 = 20
|
||||
pkg debug/elf, const R_AARCH64_P32_JUMP26 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_JUMP_SLOT = 182
|
||||
pkg debug/elf, const R_AARCH64_P32_JUMP_SLOT R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_LD32_GOT_LO12_NC = 27
|
||||
pkg debug/elf, const R_AARCH64_P32_LD32_GOT_LO12_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_LDST128_ABS_LO12_NC = 17
|
||||
pkg debug/elf, const R_AARCH64_P32_LDST128_ABS_LO12_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_LDST16_ABS_LO12_NC = 14
|
||||
pkg debug/elf, const R_AARCH64_P32_LDST16_ABS_LO12_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_LDST32_ABS_LO12_NC = 15
|
||||
pkg debug/elf, const R_AARCH64_P32_LDST32_ABS_LO12_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_LDST64_ABS_LO12_NC = 16
|
||||
pkg debug/elf, const R_AARCH64_P32_LDST64_ABS_LO12_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_LDST8_ABS_LO12_NC = 13
|
||||
pkg debug/elf, const R_AARCH64_P32_LDST8_ABS_LO12_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_LD_PREL_LO19 = 9
|
||||
pkg debug/elf, const R_AARCH64_P32_LD_PREL_LO19 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_MOVW_SABS_G0 = 8
|
||||
pkg debug/elf, const R_AARCH64_P32_MOVW_SABS_G0 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_MOVW_UABS_G0 = 5
|
||||
pkg debug/elf, const R_AARCH64_P32_MOVW_UABS_G0 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_MOVW_UABS_G0_NC = 6
|
||||
pkg debug/elf, const R_AARCH64_P32_MOVW_UABS_G0_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_MOVW_UABS_G1 = 7
|
||||
pkg debug/elf, const R_AARCH64_P32_MOVW_UABS_G1 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_PREL16 = 4
|
||||
pkg debug/elf, const R_AARCH64_P32_PREL16 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_PREL32 = 3
|
||||
pkg debug/elf, const R_AARCH64_P32_PREL32 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_RELATIVE = 183
|
||||
pkg debug/elf, const R_AARCH64_P32_RELATIVE R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSDESC = 187
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSDESC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSDESC_ADD_LO12_NC = 126
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSDESC_ADD_LO12_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSDESC_ADR_PAGE21 = 124
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSDESC_ADR_PAGE21 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSDESC_ADR_PREL21 = 123
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSDESC_ADR_PREL21 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSDESC_CALL = 127
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSDESC_CALL R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSDESC_LD32_LO12_NC = 125
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSDESC_LD32_LO12_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSDESC_LD_PREL19 = 122
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSDESC_LD_PREL19 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSGD_ADD_LO12_NC = 82
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSGD_ADD_LO12_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSGD_ADR_PAGE21 = 81
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSGD_ADR_PAGE21 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSIE_ADR_GOTTPREL_PAGE21 = 103
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSIE_ADR_GOTTPREL_PAGE21 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSIE_LD32_GOTTPREL_LO12_NC = 104
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSIE_LD32_GOTTPREL_LO12_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSIE_LD_GOTTPREL_PREL19 = 105
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSIE_LD_GOTTPREL_PREL19 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSLE_ADD_TPREL_HI12 = 109
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSLE_ADD_TPREL_HI12 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSLE_ADD_TPREL_LO12 = 110
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSLE_ADD_TPREL_LO12 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSLE_ADD_TPREL_LO12_NC = 111
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSLE_ADD_TPREL_LO12_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSLE_MOVW_TPREL_G0 = 107
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSLE_MOVW_TPREL_G0 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSLE_MOVW_TPREL_G0_NC = 108
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSLE_MOVW_TPREL_G0_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSLE_MOVW_TPREL_G1 = 106
|
||||
pkg debug/elf, const R_AARCH64_P32_TLSLE_MOVW_TPREL_G1 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_TLS_DTPMOD = 184
|
||||
pkg debug/elf, const R_AARCH64_P32_TLS_DTPMOD R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_TLS_DTPREL = 185
|
||||
pkg debug/elf, const R_AARCH64_P32_TLS_DTPREL R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_TLS_TPREL = 186
|
||||
pkg debug/elf, const R_AARCH64_P32_TLS_TPREL R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_P32_TSTBR14 = 18
|
||||
pkg debug/elf, const R_AARCH64_P32_TSTBR14 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_PREL16 = 262
|
||||
pkg debug/elf, const R_AARCH64_PREL16 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_PREL32 = 261
|
||||
pkg debug/elf, const R_AARCH64_PREL32 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_PREL64 = 260
|
||||
pkg debug/elf, const R_AARCH64_PREL64 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_RELATIVE = 1027
|
||||
pkg debug/elf, const R_AARCH64_RELATIVE R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLSDESC = 1031
|
||||
pkg debug/elf, const R_AARCH64_TLSDESC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLSDESC_ADD = 568
|
||||
pkg debug/elf, const R_AARCH64_TLSDESC_ADD R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLSDESC_ADD_LO12_NC = 564
|
||||
pkg debug/elf, const R_AARCH64_TLSDESC_ADD_LO12_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLSDESC_ADR_PAGE21 = 562
|
||||
pkg debug/elf, const R_AARCH64_TLSDESC_ADR_PAGE21 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLSDESC_ADR_PREL21 = 561
|
||||
pkg debug/elf, const R_AARCH64_TLSDESC_ADR_PREL21 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLSDESC_CALL = 569
|
||||
pkg debug/elf, const R_AARCH64_TLSDESC_CALL R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLSDESC_LD64_LO12_NC = 563
|
||||
pkg debug/elf, const R_AARCH64_TLSDESC_LD64_LO12_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLSDESC_LDR = 567
|
||||
pkg debug/elf, const R_AARCH64_TLSDESC_LDR R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLSDESC_LD_PREL19 = 560
|
||||
pkg debug/elf, const R_AARCH64_TLSDESC_LD_PREL19 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLSDESC_OFF_G0_NC = 566
|
||||
pkg debug/elf, const R_AARCH64_TLSDESC_OFF_G0_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLSDESC_OFF_G1 = 565
|
||||
pkg debug/elf, const R_AARCH64_TLSDESC_OFF_G1 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLSGD_ADD_LO12_NC = 514
|
||||
pkg debug/elf, const R_AARCH64_TLSGD_ADD_LO12_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLSGD_ADR_PAGE21 = 513
|
||||
pkg debug/elf, const R_AARCH64_TLSGD_ADR_PAGE21 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 = 541
|
||||
pkg debug/elf, const R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC = 542
|
||||
pkg debug/elf, const R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 = 543
|
||||
pkg debug/elf, const R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC = 540
|
||||
pkg debug/elf, const R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 = 539
|
||||
pkg debug/elf, const R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLSLE_ADD_TPREL_HI12 = 549
|
||||
pkg debug/elf, const R_AARCH64_TLSLE_ADD_TPREL_HI12 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLSLE_ADD_TPREL_LO12 = 550
|
||||
pkg debug/elf, const R_AARCH64_TLSLE_ADD_TPREL_LO12 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLSLE_ADD_TPREL_LO12_NC = 551
|
||||
pkg debug/elf, const R_AARCH64_TLSLE_ADD_TPREL_LO12_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLSLE_MOVW_TPREL_G0 = 547
|
||||
pkg debug/elf, const R_AARCH64_TLSLE_MOVW_TPREL_G0 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLSLE_MOVW_TPREL_G0_NC = 548
|
||||
pkg debug/elf, const R_AARCH64_TLSLE_MOVW_TPREL_G0_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLSLE_MOVW_TPREL_G1 = 545
|
||||
pkg debug/elf, const R_AARCH64_TLSLE_MOVW_TPREL_G1 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLSLE_MOVW_TPREL_G1_NC = 546
|
||||
pkg debug/elf, const R_AARCH64_TLSLE_MOVW_TPREL_G1_NC R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLSLE_MOVW_TPREL_G2 = 544
|
||||
pkg debug/elf, const R_AARCH64_TLSLE_MOVW_TPREL_G2 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLS_DTPMOD64 = 1028
|
||||
pkg debug/elf, const R_AARCH64_TLS_DTPMOD64 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLS_DTPREL64 = 1029
|
||||
pkg debug/elf, const R_AARCH64_TLS_DTPREL64 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TLS_TPREL64 = 1030
|
||||
pkg debug/elf, const R_AARCH64_TLS_TPREL64 R_AARCH64
|
||||
pkg debug/elf, const R_AARCH64_TSTBR14 = 279
|
||||
pkg debug/elf, const R_AARCH64_TSTBR14 R_AARCH64
|
||||
pkg debug/elf, method (R_AARCH64) GoString() string
|
||||
pkg debug/elf, method (R_AARCH64) String() string
|
||||
pkg debug/elf, type R_AARCH64 int
|
||||
|
||||
# CL 107530043 debug/elf: add (*File).DynamicSymbols, ErrNoSymbols, and tests for (*File).Symbols and (*File).DynamicSymbols, and formalize symbol order., Pietro Gagliardi <pietro10@mac.com>
|
||||
pkg debug/elf, method (*File) DynamicSymbols() ([]Symbol, error)
|
||||
pkg debug/elf, var ErrNoSymbols error
|
||||
|
||||
# CL 106460044 debug/plan9obj, cmd/addr2line: on Plan 9 use a.out header, Aram Hăvărneanu <aram@mgk.ro>
|
||||
pkg debug/plan9obj, type FileHeader struct, HdrSize uint64
|
||||
pkg debug/plan9obj, type FileHeader struct, LoadAddress uint64
|
||||
|
||||
# CL 122960043 encoding/xml: add InputOffset method to Decoder, Russ Cox <rsc@golang.org>
|
||||
pkg encoding/xml, method (*Decoder) InputOffset() int64
|
||||
|
||||
# CL 124940043 cmd/go, go/build: implement import comment checking, Russ Cox <rsc@golang.org>
|
||||
pkg go/build, const ImportComment = 4
|
||||
pkg go/build, const ImportComment ImportMode
|
||||
pkg go/build, type Package struct, ImportComment string
|
||||
|
||||
# CL 155050043 go/build: Return MultiplePackageError on importing a dir containing multiple packages, Jens Frederich <jfrederich@gmail.com>
|
||||
pkg go/build, method (*MultiplePackageError) Error() string
|
||||
pkg go/build, type MultiplePackageError struct
|
||||
pkg go/build, type MultiplePackageError struct, Dir string
|
||||
pkg go/build, type MultiplePackageError struct, Files []string
|
||||
pkg go/build, type MultiplePackageError struct, Packages []string
|
||||
|
||||
# CL 135110044 go/token: implement PositionFor accessors, Robert Griesemer <gri@golang.org>
|
||||
pkg go/token, method (*File) PositionFor(Pos, bool) Position
|
||||
pkg go/token, method (*FileSet) PositionFor(Pos, bool) Position
|
||||
|
||||
# CL 109000049 image: add RGBAAt, Gray16At, etc., ChaiShushan <chaishushan@gmail.com>
|
||||
pkg image, method (*Alpha) AlphaAt(int, int) color.Alpha
|
||||
pkg image, method (*Alpha16) Alpha16At(int, int) color.Alpha16
|
||||
pkg image, method (*Gray) GrayAt(int, int) color.Gray
|
||||
pkg image, method (*Gray16) Gray16At(int, int) color.Gray16
|
||||
pkg image, method (*NRGBA) NRGBAAt(int, int) color.NRGBA
|
||||
pkg image, method (*NRGBA64) NRGBA64At(int, int) color.NRGBA64
|
||||
pkg image, method (*RGBA) RGBAAt(int, int) color.RGBA
|
||||
pkg image, method (*RGBA64) RGBA64At(int, int) color.RGBA64
|
||||
pkg image, method (*YCbCr) YCbCrAt(int, int) color.YCbCr
|
||||
|
||||
# CL 129190043 png: make the encoder configurable, Jeff R. Allen <jra@nella.org>
|
||||
pkg image/png, const BestCompression = -3
|
||||
pkg image/png, const BestCompression CompressionLevel
|
||||
pkg image/png, const BestSpeed = -2
|
||||
pkg image/png, const BestSpeed CompressionLevel
|
||||
pkg image/png, const DefaultCompression = 0
|
||||
pkg image/png, const DefaultCompression CompressionLevel
|
||||
pkg image/png, const NoCompression = -1
|
||||
pkg image/png, const NoCompression CompressionLevel
|
||||
pkg image/png, method (*Encoder) Encode(io.Writer, image.Image) error
|
||||
pkg image/png, type CompressionLevel int
|
||||
pkg image/png, type Encoder struct
|
||||
pkg image/png, type Encoder struct, CompressionLevel CompressionLevel
|
||||
|
||||
# CL 101750048 math: implement Nextafter32, Robert Griesemer <gri@golang.org>
|
||||
pkg math, func Nextafter32(float32, float32) float32
|
||||
|
||||
# CL 93550043 math/big: implement Rat.Float32, Robert Griesemer <gri@golang.org>
|
||||
pkg math/big, method (*Rat) Float32() (float32, bool)
|
||||
|
||||
# CL 76540043 net/http: add BasicAuth method to *http.Request, Kelsey Hightower <kelsey.hightower@gmail.com>
|
||||
pkg net/http, method (*Request) BasicAuth() (string, string, bool)
|
||||
|
||||
# CL 137940043 net/http: add Transport.DialTLS hook, Brad Fitzpatrick <bradfitz@golang.org>
|
||||
pkg net/http, type Transport struct, DialTLS func(string, string) (net.Conn, error)
|
||||
|
||||
# CL 132750043 net/http/httputil: Pass a Logger to ReverseProxy, allowing the user to control logging., Mark Theunissen <mark.theunissen@gmail.com>
|
||||
pkg net/http/httputil, type ReverseProxy struct, ErrorLog *log.Logger
|
||||
|
||||
# CL 148370043 os, syscall: add Unsetenv, Brad Fitzpatrick <bradfitz@golang.org>
|
||||
pkg os, func Unsetenv(string) error
|
||||
pkg syscall, func Unsetenv(string) error
|
||||
|
||||
# CL 144020043 reflect: add Type.Comparable, Russ Cox <rsc@golang.org>
|
||||
pkg reflect, type Type interface, Comparable() bool
|
||||
|
||||
# CL 153670043 runtime: add PauseEnd array to MemStats and GCStats, Jens Frederich <jfrederich@gmail.com>
|
||||
pkg runtime, type MemStats struct, PauseEnd [256]uint64
|
||||
pkg runtime/debug, type GCStats struct, PauseEnd []time.Time
|
||||
|
||||
# CL 136710045 sync/atomic: add Value, Dmitriy Vyukov <dvyukov@google.com>
|
||||
pkg sync/atomic, method (*Value) Load() interface{}
|
||||
pkg sync/atomic, method (*Value) Store(interface{})
|
||||
pkg sync/atomic, type Value struct
|
||||
|
||||
# CL 126190043 syscall: support UID/GID map files for Linux user namespaces, Mrunal Patel <mrunalp@gmail.com>
|
||||
pkg syscall (linux-386), type SysProcAttr struct, GidMappings []SysProcIDMap
|
||||
pkg syscall (linux-386), type SysProcAttr struct, UidMappings []SysProcIDMap
|
||||
pkg syscall (linux-386), type SysProcIDMap struct
|
||||
pkg syscall (linux-386), type SysProcIDMap struct, ContainerID int
|
||||
pkg syscall (linux-386), type SysProcIDMap struct, HostID int
|
||||
pkg syscall (linux-386), type SysProcIDMap struct, Size int
|
||||
pkg syscall (linux-386-cgo), type SysProcAttr struct, GidMappings []SysProcIDMap
|
||||
pkg syscall (linux-386-cgo), type SysProcAttr struct, UidMappings []SysProcIDMap
|
||||
pkg syscall (linux-386-cgo), type SysProcIDMap struct
|
||||
pkg syscall (linux-386-cgo), type SysProcIDMap struct, ContainerID int
|
||||
pkg syscall (linux-386-cgo), type SysProcIDMap struct, HostID int
|
||||
pkg syscall (linux-386-cgo), type SysProcIDMap struct, Size int
|
||||
pkg syscall (linux-amd64), type SysProcAttr struct, GidMappings []SysProcIDMap
|
||||
pkg syscall (linux-amd64), type SysProcAttr struct, UidMappings []SysProcIDMap
|
||||
pkg syscall (linux-amd64), type SysProcIDMap struct
|
||||
pkg syscall (linux-amd64), type SysProcIDMap struct, ContainerID int
|
||||
pkg syscall (linux-amd64), type SysProcIDMap struct, HostID int
|
||||
pkg syscall (linux-amd64), type SysProcIDMap struct, Size int
|
||||
pkg syscall (linux-amd64-cgo), type SysProcAttr struct, GidMappings []SysProcIDMap
|
||||
pkg syscall (linux-amd64-cgo), type SysProcAttr struct, UidMappings []SysProcIDMap
|
||||
pkg syscall (linux-amd64-cgo), type SysProcIDMap struct
|
||||
pkg syscall (linux-amd64-cgo), type SysProcIDMap struct, ContainerID int
|
||||
pkg syscall (linux-amd64-cgo), type SysProcIDMap struct, HostID int
|
||||
pkg syscall (linux-amd64-cgo), type SysProcIDMap struct, Size int
|
||||
pkg syscall (linux-arm), type SysProcAttr struct, GidMappings []SysProcIDMap
|
||||
pkg syscall (linux-arm), type SysProcAttr struct, UidMappings []SysProcIDMap
|
||||
pkg syscall (linux-arm), type SysProcIDMap struct
|
||||
pkg syscall (linux-arm), type SysProcIDMap struct, ContainerID int
|
||||
pkg syscall (linux-arm), type SysProcIDMap struct, HostID int
|
||||
pkg syscall (linux-arm), type SysProcIDMap struct, Size int
|
||||
pkg syscall (linux-arm-cgo), type SysProcAttr struct, GidMappings []SysProcIDMap
|
||||
pkg syscall (linux-arm-cgo), type SysProcAttr struct, UidMappings []SysProcIDMap
|
||||
pkg syscall (linux-arm-cgo), type SysProcIDMap struct
|
||||
pkg syscall (linux-arm-cgo), type SysProcIDMap struct, ContainerID int
|
||||
pkg syscall (linux-arm-cgo), type SysProcIDMap struct, HostID int
|
||||
pkg syscall (linux-arm-cgo), type SysProcIDMap struct, Size int
|
||||
|
||||
# CL 122200043 net: fix CNAME resolving on Windows, Egon Elbre <egonelbre@gmail.com>
|
||||
pkg syscall (windows-386), const DNS_INFO_NO_RECORDS = 9501
|
||||
pkg syscall (windows-386), const DNS_INFO_NO_RECORDS ideal-int
|
||||
pkg syscall (windows-386), const DnsSectionAdditional = 3
|
||||
pkg syscall (windows-386), const DnsSectionAdditional ideal-int
|
||||
pkg syscall (windows-386), const DnsSectionAnswer = 1
|
||||
pkg syscall (windows-386), const DnsSectionAnswer ideal-int
|
||||
pkg syscall (windows-386), const DnsSectionAuthority = 2
|
||||
pkg syscall (windows-386), const DnsSectionAuthority ideal-int
|
||||
pkg syscall (windows-386), const DnsSectionQuestion = 0
|
||||
pkg syscall (windows-386), const DnsSectionQuestion ideal-int
|
||||
pkg syscall (windows-386), func DnsNameCompare(*uint16, *uint16) bool
|
||||
pkg syscall (windows-amd64), const DNS_INFO_NO_RECORDS = 9501
|
||||
pkg syscall (windows-amd64), const DNS_INFO_NO_RECORDS ideal-int
|
||||
pkg syscall (windows-amd64), const DnsSectionAdditional = 3
|
||||
pkg syscall (windows-amd64), const DnsSectionAdditional ideal-int
|
||||
pkg syscall (windows-amd64), const DnsSectionAnswer = 1
|
||||
pkg syscall (windows-amd64), const DnsSectionAnswer ideal-int
|
||||
pkg syscall (windows-amd64), const DnsSectionAuthority = 2
|
||||
pkg syscall (windows-amd64), const DnsSectionAuthority ideal-int
|
||||
pkg syscall (windows-amd64), const DnsSectionQuestion = 0
|
||||
pkg syscall (windows-amd64), const DnsSectionQuestion ideal-int
|
||||
pkg syscall (windows-amd64), func DnsNameCompare(*uint16, *uint16) bool
|
||||
|
||||
# CL 86160044 os: Implement symlink support for Windows, Michael Fraenkel <michael.fraenkel@gmail.com>
|
||||
pkg syscall (windows-386), const ERROR_PRIVILEGE_NOT_HELD = 1314
|
||||
pkg syscall (windows-386), const ERROR_PRIVILEGE_NOT_HELD Errno
|
||||
pkg syscall (windows-amd64), const ERROR_PRIVILEGE_NOT_HELD = 1314
|
||||
pkg syscall (windows-amd64), const ERROR_PRIVILEGE_NOT_HELD Errno
|
||||
|
||||
# CL 86160044 os: Implement symlink support for Windows, Michael Fraenkel <michael.fraenkel@gmail.com>
|
||||
pkg syscall (windows-386), const FILE_ATTRIBUTE_REPARSE_POINT = 1024
|
||||
pkg syscall (windows-386), const FILE_ATTRIBUTE_REPARSE_POINT ideal-int
|
||||
pkg syscall (windows-386), const FILE_FLAG_OPEN_REPARSE_POINT = 2097152
|
||||
pkg syscall (windows-386), const FILE_FLAG_OPEN_REPARSE_POINT ideal-int
|
||||
pkg syscall (windows-386), const FSCTL_GET_REPARSE_POINT = 589992
|
||||
pkg syscall (windows-386), const FSCTL_GET_REPARSE_POINT ideal-int
|
||||
pkg syscall (windows-386), const IO_REPARSE_TAG_SYMLINK = 2684354572
|
||||
pkg syscall (windows-386), const IO_REPARSE_TAG_SYMLINK ideal-int
|
||||
pkg syscall (windows-386), const MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16384
|
||||
pkg syscall (windows-386), const MAXIMUM_REPARSE_DATA_BUFFER_SIZE ideal-int
|
||||
pkg syscall (windows-386), const SYMBOLIC_LINK_FLAG_DIRECTORY = 1
|
||||
pkg syscall (windows-386), const SYMBOLIC_LINK_FLAG_DIRECTORY ideal-int
|
||||
pkg syscall (windows-386), func CreateHardLink(*uint16, *uint16, uintptr) error
|
||||
pkg syscall (windows-386), func CreateSymbolicLink(*uint16, *uint16, uint32) error
|
||||
pkg syscall (windows-386), func DeviceIoControl(Handle, uint32, *uint8, uint32, *uint8, uint32, *uint32, *Overlapped) error
|
||||
pkg syscall (windows-386), func LoadCreateSymbolicLink() error
|
||||
pkg syscall (windows-amd64), const FILE_ATTRIBUTE_REPARSE_POINT = 1024
|
||||
pkg syscall (windows-amd64), const FILE_ATTRIBUTE_REPARSE_POINT ideal-int
|
||||
pkg syscall (windows-amd64), const FILE_FLAG_OPEN_REPARSE_POINT = 2097152
|
||||
pkg syscall (windows-amd64), const FILE_FLAG_OPEN_REPARSE_POINT ideal-int
|
||||
pkg syscall (windows-amd64), const FSCTL_GET_REPARSE_POINT = 589992
|
||||
pkg syscall (windows-amd64), const FSCTL_GET_REPARSE_POINT ideal-int
|
||||
pkg syscall (windows-amd64), const IO_REPARSE_TAG_SYMLINK = 2684354572
|
||||
pkg syscall (windows-amd64), const IO_REPARSE_TAG_SYMLINK ideal-int
|
||||
pkg syscall (windows-amd64), const MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16384
|
||||
pkg syscall (windows-amd64), const MAXIMUM_REPARSE_DATA_BUFFER_SIZE ideal-int
|
||||
pkg syscall (windows-amd64), const SYMBOLIC_LINK_FLAG_DIRECTORY = 1
|
||||
pkg syscall (windows-amd64), const SYMBOLIC_LINK_FLAG_DIRECTORY ideal-int
|
||||
pkg syscall (windows-amd64), func CreateHardLink(*uint16, *uint16, uintptr) error
|
||||
pkg syscall (windows-amd64), func CreateSymbolicLink(*uint16, *uint16, uint32) error
|
||||
pkg syscall (windows-amd64), func DeviceIoControl(Handle, uint32, *uint8, uint32, *uint8, uint32, *uint32, *Overlapped) error
|
||||
pkg syscall (windows-amd64), func LoadCreateSymbolicLink() error
|
||||
|
||||
# CL 149510043 net: disable SIO_UDP_CONNRESET behavior on windows., Ron Hashimoto <mail@h2so5.net>
|
||||
pkg syscall (windows-386), const SIO_UDP_CONNRESET = 2550136844
|
||||
pkg syscall (windows-386), const SIO_UDP_CONNRESET ideal-int
|
||||
pkg syscall (windows-amd64), const SIO_UDP_CONNRESET = 2550136844
|
||||
pkg syscall (windows-amd64), const SIO_UDP_CONNRESET ideal-int
|
||||
|
||||
# CL 102320044 syscall: implement syscall.Getppid() on Windows, Alan Shreve <alan@inconshreveable.com>
|
||||
pkg syscall (windows-386), const TH32CS_INHERIT = 2147483648
|
||||
pkg syscall (windows-386), const TH32CS_INHERIT ideal-int
|
||||
pkg syscall (windows-386), const TH32CS_SNAPALL = 15
|
||||
pkg syscall (windows-386), const TH32CS_SNAPALL ideal-int
|
||||
pkg syscall (windows-386), const TH32CS_SNAPHEAPLIST = 1
|
||||
pkg syscall (windows-386), const TH32CS_SNAPHEAPLIST ideal-int
|
||||
pkg syscall (windows-386), const TH32CS_SNAPMODULE = 8
|
||||
pkg syscall (windows-386), const TH32CS_SNAPMODULE ideal-int
|
||||
pkg syscall (windows-386), const TH32CS_SNAPMODULE32 = 16
|
||||
pkg syscall (windows-386), const TH32CS_SNAPMODULE32 ideal-int
|
||||
pkg syscall (windows-386), const TH32CS_SNAPPROCESS = 2
|
||||
pkg syscall (windows-386), const TH32CS_SNAPPROCESS ideal-int
|
||||
pkg syscall (windows-386), const TH32CS_SNAPTHREAD = 4
|
||||
pkg syscall (windows-386), const TH32CS_SNAPTHREAD ideal-int
|
||||
pkg syscall (windows-386), func CreateToolhelp32Snapshot(uint32, uint32) (Handle, error)
|
||||
pkg syscall (windows-386), func Process32First(Handle, *ProcessEntry32) error
|
||||
pkg syscall (windows-386), func Process32Next(Handle, *ProcessEntry32) error
|
||||
pkg syscall (windows-386), type ProcessEntry32 struct
|
||||
pkg syscall (windows-386), type ProcessEntry32 struct, DefaultHeapID uintptr
|
||||
pkg syscall (windows-386), type ProcessEntry32 struct, ExeFile [260]uint16
|
||||
pkg syscall (windows-386), type ProcessEntry32 struct, Flags uint32
|
||||
pkg syscall (windows-386), type ProcessEntry32 struct, ModuleID uint32
|
||||
pkg syscall (windows-386), type ProcessEntry32 struct, ParentProcessID uint32
|
||||
pkg syscall (windows-386), type ProcessEntry32 struct, PriClassBase int32
|
||||
pkg syscall (windows-386), type ProcessEntry32 struct, ProcessID uint32
|
||||
pkg syscall (windows-386), type ProcessEntry32 struct, Size uint32
|
||||
pkg syscall (windows-386), type ProcessEntry32 struct, Threads uint32
|
||||
pkg syscall (windows-386), type ProcessEntry32 struct, Usage uint32
|
||||
pkg syscall (windows-amd64), const TH32CS_INHERIT = 2147483648
|
||||
pkg syscall (windows-amd64), const TH32CS_INHERIT ideal-int
|
||||
pkg syscall (windows-amd64), const TH32CS_SNAPALL = 15
|
||||
pkg syscall (windows-amd64), const TH32CS_SNAPALL ideal-int
|
||||
pkg syscall (windows-amd64), const TH32CS_SNAPHEAPLIST = 1
|
||||
pkg syscall (windows-amd64), const TH32CS_SNAPHEAPLIST ideal-int
|
||||
pkg syscall (windows-amd64), const TH32CS_SNAPMODULE = 8
|
||||
pkg syscall (windows-amd64), const TH32CS_SNAPMODULE ideal-int
|
||||
pkg syscall (windows-amd64), const TH32CS_SNAPMODULE32 = 16
|
||||
pkg syscall (windows-amd64), const TH32CS_SNAPMODULE32 ideal-int
|
||||
pkg syscall (windows-amd64), const TH32CS_SNAPPROCESS = 2
|
||||
pkg syscall (windows-amd64), const TH32CS_SNAPPROCESS ideal-int
|
||||
pkg syscall (windows-amd64), const TH32CS_SNAPTHREAD = 4
|
||||
pkg syscall (windows-amd64), const TH32CS_SNAPTHREAD ideal-int
|
||||
pkg syscall (windows-amd64), func CreateToolhelp32Snapshot(uint32, uint32) (Handle, error)
|
||||
pkg syscall (windows-amd64), func Process32First(Handle, *ProcessEntry32) error
|
||||
pkg syscall (windows-amd64), func Process32Next(Handle, *ProcessEntry32) error
|
||||
pkg syscall (windows-amd64), type ProcessEntry32 struct
|
||||
pkg syscall (windows-amd64), type ProcessEntry32 struct, DefaultHeapID uintptr
|
||||
pkg syscall (windows-amd64), type ProcessEntry32 struct, ExeFile [260]uint16
|
||||
pkg syscall (windows-amd64), type ProcessEntry32 struct, Flags uint32
|
||||
pkg syscall (windows-amd64), type ProcessEntry32 struct, ModuleID uint32
|
||||
pkg syscall (windows-amd64), type ProcessEntry32 struct, ParentProcessID uint32
|
||||
pkg syscall (windows-amd64), type ProcessEntry32 struct, PriClassBase int32
|
||||
pkg syscall (windows-amd64), type ProcessEntry32 struct, ProcessID uint32
|
||||
pkg syscall (windows-amd64), type ProcessEntry32 struct, Size uint32
|
||||
pkg syscall (windows-amd64), type ProcessEntry32 struct, Threads uint32
|
||||
pkg syscall (windows-amd64), type ProcessEntry32 struct, Usage uint32
|
||||
|
||||
# CL 127740043 os: make SameFile handle paths like c:a.txt properly, Alex Brainman <alex.brainman@gmail.com>
|
||||
pkg syscall (windows-386), func FullPath(string) (string, error)
|
||||
pkg syscall (windows-amd64), func FullPath(string) (string, error)
|
||||
|
||||
# CL 98150043 testing: add Coverage function, Russ Cox <rsc@golang.org>
|
||||
pkg testing, func Coverage() float64
|
||||
|
||||
# CL 148770043 cmd/go, testing: add TestMain support, Russ Cox <rsc@golang.org>
|
||||
pkg testing, func MainStart(func(string, string) (bool, error), []InternalTest, []InternalBenchmark, []InternalExample) *M
|
||||
pkg testing, method (*M) Run() int
|
||||
pkg testing, type M struct
|
||||
|
||||
# CL 108030044 text/scanner: provide facility for custom identifiers, Robert Griesemer <gri@golang.org>
|
||||
pkg text/scanner, type Scanner struct, IsIdentRune func(int32, int) bool
|
||||
|
||||
# CL 130620043 text/template: add back pointer to Nodes for better error generation, Rob Pike <r@golang.org>
|
||||
pkg text/template/parse, type DotNode struct, embedded NodeType
|
||||
pkg text/template/parse, type NilNode struct, embedded NodeType
|
||||
pkg text/template/parse, method (*BranchNode) Copy() Node
|
||||
pkg text/template/parse, method (*IdentifierNode) SetTree(*Tree) *IdentifierNode
|
||||
pkg html/template, type Error struct, Node parse.Node
|
||||
|
||||
# CL 127470043 unicode: strconv: regexp: Upgrade to Unicode 7.0.0., Marcel van Lohuizen <mpvl@golang.org>
|
||||
pkg unicode, const Version = "7.0.0"
|
||||
pkg unicode, var Bassa_Vah *RangeTable
|
||||
pkg unicode, var Caucasian_Albanian *RangeTable
|
||||
pkg unicode, var Duployan *RangeTable
|
||||
pkg unicode, var Elbasan *RangeTable
|
||||
pkg unicode, var Grantha *RangeTable
|
||||
pkg unicode, var Khojki *RangeTable
|
||||
pkg unicode, var Khudawadi *RangeTable
|
||||
pkg unicode, var Linear_A *RangeTable
|
||||
pkg unicode, var Mahajani *RangeTable
|
||||
pkg unicode, var Manichaean *RangeTable
|
||||
pkg unicode, var Mende_Kikakui *RangeTable
|
||||
pkg unicode, var Modi *RangeTable
|
||||
pkg unicode, var Mro *RangeTable
|
||||
pkg unicode, var Nabataean *RangeTable
|
||||
pkg unicode, var Old_North_Arabian *RangeTable
|
||||
pkg unicode, var Old_Permic *RangeTable
|
||||
pkg unicode, var Pahawh_Hmong *RangeTable
|
||||
pkg unicode, var Palmyrene *RangeTable
|
||||
pkg unicode, var Pau_Cin_Hau *RangeTable
|
||||
pkg unicode, var Psalter_Pahlavi *RangeTable
|
||||
pkg unicode, var Siddham *RangeTable
|
||||
pkg unicode, var Tirhuta *RangeTable
|
||||
pkg unicode, var Warang_Citi *RangeTable
|
||||
@@ -578,7 +578,7 @@ import "github.com/golang/example/stringutil"
|
||||
<p>
|
||||
This convention is the easiest way to make your Go packages available for
|
||||
others to use.
|
||||
The <a href="//code.google.com/p/go-wiki/wiki/Projects">Go Wiki</a>
|
||||
The <a href="//golang.org/wiki/Projects">Go Wiki</a>
|
||||
and <a href="//godoc.org/">godoc.org</a>
|
||||
provide lists of external Go projects.
|
||||
</p>
|
||||
@@ -627,5 +627,5 @@ The official mailing list for discussion of the Go language is
|
||||
|
||||
<p>
|
||||
Report bugs using the
|
||||
<a href="//code.google.com/p/go/issues/list">Go issue tracker</a>.
|
||||
<a href="//golang.org/issue">Go issue tracker</a>.
|
||||
</p>
|
||||
|
||||
@@ -30,21 +30,16 @@ We encourage all Go users to subscribe to
|
||||
<h2 id="go1">Version history</h2>
|
||||
|
||||
<h3 id="release"><a href="/doc/devel/release.html">Release History</a></h3>
|
||||
<p>A summary of the changes between Go releases.</p>
|
||||
|
||||
<h4 id="go1notes"><a href="/doc/go1">Go 1 Release Notes</a></h4>
|
||||
<p>
|
||||
A guide for updating your code to work with Go 1.
|
||||
</p>
|
||||
<p>A <a href="/doc/devel/release.html">summary</a> of the changes between Go releases. Notes for the major releases:</p>
|
||||
|
||||
<h4 id="release notes"><a href="/doc/go1.1">Go 1.1 Release Notes</a></h4>
|
||||
<p>
|
||||
A list of significant changes in Go 1.1, with instructions for updating
|
||||
your code where necessary.
|
||||
Each point release includes a similar document appropriate for that
|
||||
release: <a href="/doc/go1.2">Go 1.2</a>, <a href="/doc/go1.3">Go 1.3</a>,
|
||||
and so on.
|
||||
</p>
|
||||
<ul>
|
||||
<li><a href="/doc/go1.4">Go 1.4</a> <small>(December 2014)</small></li>
|
||||
<li><a href="/doc/go1.3">Go 1.3</a> <small>(June 2014)</small></li>
|
||||
<li><a href="/doc/go1.2">Go 1.2</a> <small>(December 2013)</small></li>
|
||||
<li><a href="/doc/go1.1">Go 1.1</a> <small>(May 2013)</small></li>
|
||||
<li><a href="/doc/go1">Go 1</a> <small>(March 2012)</small></li>
|
||||
</ul>
|
||||
|
||||
<h3 id="go1compat"><a href="/doc/go1compat">Go 1 and the Future of Go Programs</a></h3>
|
||||
<p>
|
||||
@@ -55,7 +50,7 @@ Go 1 matures.
|
||||
|
||||
<h2 id="resources">Developer Resources</h2>
|
||||
|
||||
<h3 id="source"><a href="https://code.google.com/p/go/source">Source Code</a></h3>
|
||||
<h3 id="source"><a href="https://golang.org/change">Source Code</a></h3>
|
||||
<p>Check out the Go source code.</p>
|
||||
|
||||
<h3 id="golang-dev"><a href="https://groups.google.com/group/golang-dev">Developer</a> and
|
||||
@@ -81,13 +76,13 @@ systems and architectures.</p>
|
||||
|
||||
<h2 id="howto">How you can help</h2>
|
||||
|
||||
<h3><a href="https://code.google.com/p/go/issues">Reporting issues</a></h3>
|
||||
<h3><a href="//golang.org/issue">Reporting issues</a></h3>
|
||||
|
||||
<p>
|
||||
If you spot bugs, mistakes, or inconsistencies in the Go project's code or
|
||||
documentation, please let us know by
|
||||
<a href="https://code.google.com/p/go/issues/entry">filing a ticket</a>
|
||||
on our <a href="https://code.google.com/p/go/issues">issue tracker</a>.
|
||||
<a href="//golang.org/issue/new">filing a ticket</a>
|
||||
on our <a href="//golang.org/issue">issue tracker</a>.
|
||||
(Of course, you should check it's not an existing issue before creating
|
||||
a new one.)
|
||||
</p>
|
||||
@@ -106,8 +101,8 @@ To get started, read these <a href="/doc/contribute.html">contribution
|
||||
guidelines</a> for information on design, testing, and our code review process.
|
||||
</p>
|
||||
<p>
|
||||
Check <a href="https://code.google.com/p/go/issues">the tracker</a> for
|
||||
Check <a href="//golang.org/issue">the tracker</a> for
|
||||
open issues that interest you. Those labeled
|
||||
<a href="https://code.google.com/p/go/issues/list?q=status=HelpWanted">HelpWanted</a>
|
||||
<a href="https://github.com/golang/go/issues?q=is%3Aopen+is%3Aissue+label%3Ahelpwanted">helpwanted</a>
|
||||
are particularly in need of outside help.
|
||||
</p>
|
||||
|
||||
@@ -6,9 +6,12 @@
|
||||
|
||||
<p>
|
||||
This document explains how to contribute changes to the Go project.
|
||||
It assumes you have installed Go using the
|
||||
It assumes you have followed the
|
||||
<a href="/doc/install/source">installation instructions</a> and
|
||||
have <a href="code.html">written and tested your code</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
(Note that the <code>gccgo</code> frontend lives elsewhere;
|
||||
see <a href="gccgo_contribute.html">Contributing to gccgo</a>.)
|
||||
</p>
|
||||
@@ -54,7 +57,8 @@ $ ./all.bash
|
||||
</p>
|
||||
|
||||
<p>
|
||||
After running for a while, the command should print "<code>ALL TESTS PASSED</code>".
|
||||
After running for a while, the command should print
|
||||
"<code>ALL</code> <code>TESTS</code> <code>PASSED</code>".
|
||||
</p>
|
||||
|
||||
<h2 id="Code_review">Code review</h2>
|
||||
@@ -64,208 +68,230 @@ Changes to Go must be reviewed before they are submitted,
|
||||
no matter who makes the change.
|
||||
(In exceptional cases, such as fixing a build, the review can
|
||||
follow shortly after submitting.)
|
||||
A Mercurial extension helps manage the code review process.
|
||||
The extension is included in the Go source tree but needs
|
||||
to be added to your Mercurial configuration.
|
||||
A custom git command called <code>git-codereview</code>,
|
||||
discussed below, helps manage the code review process through a Google-hosted
|
||||
<a href="https://go-review.googlesource.com/">instance</a> of the code review
|
||||
system called <a href="https://code.google.com/p/gerrit/">Gerrit</a>.
|
||||
</p>
|
||||
|
||||
<h3>Caveat for Mercurial aficionados</h3>
|
||||
<h3>Set up authentication for code review</h3>
|
||||
|
||||
<p>
|
||||
<i>Using Mercurial with the code review extension is not the same
|
||||
as using standard Mercurial.</i>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The Go repository is maintained as a single line of reviewed changes;
|
||||
we prefer to avoid the complexity of Mercurial's arbitrary change graph.
|
||||
The code review extension helps here: its <code>hg submit</code> command
|
||||
automatically checks for and warns about the local repository
|
||||
being out of date compared to the remote one.
|
||||
The <code>hg submit</code> command also verifies other
|
||||
properties about the Go repository.
|
||||
For example,
|
||||
it checks that Go code being checked in is formatted in the standard style,
|
||||
as defined by <a href="/cmd/gofmt">gofmt</a>,
|
||||
and it checks that the author of the code is properly recorded for
|
||||
<a href="#copyright">copyright purposes</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To help ensure changes are only created by <code>hg submit</code>,
|
||||
the code review extension disables the standard <code>hg commit</code>
|
||||
command.
|
||||
</p>
|
||||
|
||||
<h3>Configure the extension</h3>
|
||||
|
||||
<p>Edit <code>.hg/hgrc</code> in the root of your Go checkout to add:</p>
|
||||
|
||||
<pre>
|
||||
[extensions]
|
||||
codereview = /path/to/go/lib/codereview/codereview.py
|
||||
|
||||
[ui]
|
||||
username = Your Name <you@server.dom>
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
The <code>username</code> information will not be used unless
|
||||
you are a committer (see below), but Mercurial complains if it is missing.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
As the codereview extension is only enabled for your Go checkout, the remainder of this document assumes you
|
||||
are inside the go directory when issuing commands.
|
||||
</p>
|
||||
|
||||
<p>To contribute to subrepositories, edit the <code>.hg/hgrc</code> for each
|
||||
subrepository in the same way. For example, add the codereview extension to
|
||||
<code>golang.org/x/tools/.hg/hgrc</code>.
|
||||
</p>
|
||||
|
||||
<h3>Understanding the extension</h3>
|
||||
|
||||
<p>After adding the code review extension, you can run</p>
|
||||
|
||||
<pre>
|
||||
$ hg help codereview
|
||||
</pre>
|
||||
|
||||
<p>to learn more about its commands. To learn about a specific code-review-specific
|
||||
command such as <code>change</code>, run</p>
|
||||
|
||||
<pre>
|
||||
$ hg help change
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Windows users may need to perform extra steps to get the code review
|
||||
extension working. See the
|
||||
<a href="https://code.google.com/p/go-wiki/wiki/CodeReview">CodeReview page</a>
|
||||
on the <a href="https://code.google.com/p/go-wiki/wiki">Go Wiki</a> for details.
|
||||
</p>
|
||||
|
||||
<h3>Log in to the code review site.</h3>
|
||||
|
||||
<p>
|
||||
The code review server uses a Google Account to authenticate.
|
||||
The Git code hosting server and Gerrit code review server both use a Google
|
||||
Account to authenticate. You therefore need a Google Account to proceed.
|
||||
(If you can use the account to
|
||||
<a href="https://www.google.com/accounts/Login?hl=en&continue=http://www.google.com/">sign in at google.com</a>,
|
||||
<a href="https://www.google.com/accounts/Login">sign in at google.com</a>,
|
||||
you can use it to sign in to the code review server.)
|
||||
The email address you use on the Code Review site
|
||||
will be recorded in the <a href="https://code.google.com/p/go/source/list">Mercurial change log</a>
|
||||
The email address you use with the code review system
|
||||
will be recorded in the <a href="https://go.googlesource.com/go">change log</a>
|
||||
and in the <a href="/CONTRIBUTORS"><code>CONTRIBUTORS</code></a> file.
|
||||
You can <a href="https://www.google.com/accounts/NewAccount">create a Google Account</a>
|
||||
associated with any address where you receive email.
|
||||
If you've enabled the two-step verification feature, don't forget to generate an
|
||||
application-specific password and use that when prompted for a password.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Visit the site <a href="https://go.googlesource.com">go.googlesource.com</a>
|
||||
and log in using your Google Account.
|
||||
Click on the "Generate Password" link that appears at the top of the page.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Click the radio button that says "Only <code>go.googlesource.com</code>"
|
||||
to use this authentication token only for the Go project.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Further down the page is a box containing commands to install
|
||||
the authentication cookie in file called <code>.gitcookies</code> in your home
|
||||
directory.
|
||||
Copy the text for the commands into a Unix shell window to execute it.
|
||||
That will install the authentication token.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
(If you are on a Windows computer, you should instead follow the instructions
|
||||
in the yellow box to run the command.)
|
||||
</p>
|
||||
|
||||
<h3>Register with Gerrit</h3>
|
||||
|
||||
<p>
|
||||
Now that you have a Google account and the authentication token,
|
||||
you need to register your account with Gerrit, the code review system.
|
||||
To do this, visit <a href="https://golang.org/cl">golang.org/cl</a>
|
||||
and log in using the same Google Account you used above.
|
||||
That is all that is required.
|
||||
</p>
|
||||
|
||||
<h3>Install the git-codereview command</h3>
|
||||
|
||||
<p>
|
||||
Now install the <code>git-codereview</code> command by running,
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
$ hg code-login
|
||||
Email (login for uploading to codereview.appspot.com): rsc@golang.org
|
||||
Password for rsc@golang.org:
|
||||
|
||||
Saving authentication cookies to /Users/rsc/.codereview_upload_cookies_codereview.appspot.com
|
||||
go get -u golang.org/x/review/git-codereview
|
||||
</pre>
|
||||
|
||||
<h3>Configure your account settings.</h3>
|
||||
|
||||
<p>Edit your <a href="https://codereview.appspot.com/settings">code review settings</a>.
|
||||
Grab a nickname.
|
||||
Many people prefer to set the Context option to
|
||||
“Whole file” to see more context when reviewing changes.
|
||||
<p>
|
||||
Make sure <code>git-codereview</code> is installed in your shell path, so that the
|
||||
<code>git</code> command can find it. Check that
|
||||
</p>
|
||||
|
||||
<p>Once you have chosen a nickname in the settings page, others
|
||||
can use that nickname as a shorthand for naming reviewers and the CC list.
|
||||
For example, <code>rsc</code> is an alias for <code>rsc@golang.org</code>.
|
||||
<pre>
|
||||
$ git codereview help
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
prints help text, not an error.
|
||||
</p>
|
||||
|
||||
<h3>Switch to the default branch</h3>
|
||||
<p>
|
||||
Note to Git aficionados: The <code>git-codereview</code> command is not required to
|
||||
upload and manage Gerrit code reviews. For those who prefer plain Git, the text
|
||||
below gives the Git equivalent of each git-codereview command. If you do use plain
|
||||
Git, note that you still need the commit hooks that the git-codereview command
|
||||
configures; those hooks add a Gerrit <code>Change-Id</code> line to the commit
|
||||
message and check that all Go source files have been formatted with gofmt. Even
|
||||
if you intend to use plain Git for daily work, install the hooks in a new Git
|
||||
checkout by running <code>git-codereview</code> <code>hooks</code>.
|
||||
</p>
|
||||
|
||||
<h3>Set up git aliases</h3>
|
||||
|
||||
<p>
|
||||
The <code>git-codereview</code> command can be run directly from the shell
|
||||
by typing, for instance,
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
$ git codereview sync
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
but it is more convenient to set up aliases for <code>git-codereview</code>'s own
|
||||
subcommands, so that the above becomes,
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
$ git sync
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
The <code>git-codereview</code> subcommands have been chosen to be distinct from
|
||||
Git's own, so it's safe to do so.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The aliases are optional, but in the rest of this document we will assume
|
||||
they are installed.
|
||||
To install them, copy this text into your Git configuration file
|
||||
(usually <code>.gitconfig</code> in your home directory):
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
[alias]
|
||||
change = codereview change
|
||||
gofmt = codereview gofmt
|
||||
mail = codereview mail
|
||||
pending = codereview pending
|
||||
submit = codereview submit
|
||||
sync = codereview sync
|
||||
</pre>
|
||||
|
||||
<h3>Understanding the git-codereview command</h3>
|
||||
|
||||
<p>After installing the <code>git-codereview</code> command, you can run</p>
|
||||
|
||||
<pre>
|
||||
$ git codereview help
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
to learn more about its commands.
|
||||
You can also read the <a href="https://godoc.org/golang.org/x/review/git-codereview">command documentation</a>.
|
||||
</p>
|
||||
|
||||
<h3>Switch to the master branch</h3>
|
||||
|
||||
<p>
|
||||
Most Go installations use a release branch, but new changes should
|
||||
only be made to the default branch. (They may be applied later to a release
|
||||
branch as part of the release process.)
|
||||
Before making a change, make sure you use the default branch:
|
||||
only be made based on the master branch.
|
||||
(They may be applied later to a release branch as part of the release process,
|
||||
but most contributors won't do this themselves.)
|
||||
Before making a change, make sure you start on the master branch:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
$ hg update default
|
||||
$ git checkout master
|
||||
$ git sync
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
(In Git terms, <code>git</code> <code>sync</code> runs
|
||||
<code>git</code> <code>pull</code> <code>-r</code>.)
|
||||
</p>
|
||||
|
||||
<h3>Make a change</h3>
|
||||
|
||||
<p>
|
||||
The entire checked-out tree is writable.
|
||||
If you need to edit files, just edit them: Mercurial will figure out which ones changed.
|
||||
You do need to inform Mercurial of added, removed, copied, or renamed files,
|
||||
by running
|
||||
<code>hg add</code>,
|
||||
<code>hg rm</code>,
|
||||
<code>hg cp</code>,
|
||||
or
|
||||
<code>hg mv</code>.
|
||||
Once you have edited files, you must tell Git that they have been modified.
|
||||
You must also tell Git about any files that are added, removed, or renamed files.
|
||||
These operations are done with the usual Git commands,
|
||||
<code>git</code> <code>add</code>,
|
||||
<code>git</code> <code>rm</code>,
|
||||
and
|
||||
<code>git</code> <code>mv</code>.
|
||||
</p>
|
||||
|
||||
<p>When you are ready to send a change out for review, run</p>
|
||||
<p>
|
||||
If you wish to checkpoint your work, or are ready to send the code out for review, run</p>
|
||||
|
||||
<pre>
|
||||
$ hg change
|
||||
$ git change <i><branch></i>
|
||||
</pre>
|
||||
|
||||
<p>from any directory in your Go repository.
|
||||
Mercurial will open a change description file in your editor.
|
||||
(It uses the editor named by the <code>$EDITOR</code> environment variable, <code>vi</code> by default.)
|
||||
<p>
|
||||
from any directory in your Go repository to commit the changes so far.
|
||||
The name <i><branch></i> is an arbitrary one you choose to identify the
|
||||
local branch containing your changes.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
(In Git terms, <code>git</code> <code>change</code> <code><branch></code>
|
||||
runs <code>git</code> <code>checkout</code> <code>-b</code> <code>branch</code>,
|
||||
then <code>git</code> <code>branch</code> <code>--set-upstream-to</code> <code>origin/master</code>,
|
||||
then <code>git</code> <code>commit</code>.)
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Git will open a change description file in your editor.
|
||||
(It uses the editor named by the <code>$EDITOR</code> environment variable,
|
||||
<code>vi</code> by default.)
|
||||
The file will look like:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
# Change list.
|
||||
# Lines beginning with # are ignored.
|
||||
# Multi-line values should be indented.
|
||||
|
||||
Reviewer:
|
||||
CC:
|
||||
|
||||
Description:
|
||||
<enter description here>
|
||||
|
||||
Files:
|
||||
src/math/sin.go
|
||||
src/math/tan.go
|
||||
src/regexp/regexp.go
|
||||
# Please enter the commit message for your changes. Lines starting
|
||||
# with '#' will be ignored, and an empty message aborts the commit.
|
||||
# On branch foo
|
||||
# Changes not staged for commit:
|
||||
# modified: editedfile.go
|
||||
#
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
The <code>Reviewer</code> line lists the reviewers assigned
|
||||
to this change, and the <code>CC</code> line lists people to
|
||||
notify about the change.
|
||||
These can be code review nicknames or arbitrary email addresses.
|
||||
Unless explicitly told otherwise, such as in the discussion leading
|
||||
up to sending in the change list, leave the reviewer field blank.
|
||||
This means that the
|
||||
<a href="https://groups.google.com/group/golang-codereviews">golang-codereviews@googlegroups.com</a>
|
||||
mailing list will be used as the reviewer.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Replace “<code><enter description here></code>”
|
||||
with a description of your change.
|
||||
At the beginning of this file is a blank line; replace it
|
||||
with a thorough description of your change.
|
||||
The first line of the change description is conventionally a one-line
|
||||
summary of the change, prefixed by the primary affected package,
|
||||
and is used as the subject for code review mail; the rest of the
|
||||
description elaborates.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The <code>Files</code> section lists all the modified files
|
||||
in your client.
|
||||
It is best to keep unrelated changes in different change lists.
|
||||
In this example, we can include just the changes to package <code>math</code>
|
||||
by deleting the line mentioning <code>regexp.go</code>.
|
||||
and is used as the subject for code review mail.
|
||||
The rest of the
|
||||
description elaborates and should provide context for the
|
||||
change and explain what it does.
|
||||
If there is a helpful reference, mention it here.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -273,343 +299,314 @@ After editing, the template might now read:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
# Change list.
|
||||
# Lines beginning with # are ignored.
|
||||
# Multi-line values should be indented.
|
||||
math: improved Sin, Cos and Tan precision for very large arguments
|
||||
|
||||
Reviewer: golang-codereviews@googlegroups.com
|
||||
CC: math-nuts@swtch.com
|
||||
The existing implementation has poor numerical properties for
|
||||
large arguments, so use the McGillicutty algorithm to improve
|
||||
accuracy above 1e10.
|
||||
|
||||
Description:
|
||||
math: improved Sin, Cos and Tan precision for very large arguments.
|
||||
The algorithm is described at http://wikipedia.org/wiki/McGillicutty_Algorithm
|
||||
|
||||
See Bimmler and Shaney, ``Extreme sinusoids,'' J. Math 3(14).
|
||||
Fixes issue 159.
|
||||
Fixes #159
|
||||
|
||||
Files:
|
||||
src/math/sin.go
|
||||
src/math/tan.go
|
||||
# Please enter the commit message for your changes. Lines starting
|
||||
# with '#' will be ignored, and an empty message aborts the commit.
|
||||
# On branch foo
|
||||
# Changes not staged for commit:
|
||||
# modified: editedfile.go
|
||||
#
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
The special sentence “Fixes issue 159.” associates
|
||||
the change with issue 159 in the <a href="https://code.google.com/p/go/issues/list">Go issue tracker</a>.
|
||||
When this change is eventually submitted, the issue
|
||||
tracker will automatically mark the issue as fixed.
|
||||
(These conventions are described in detail by the
|
||||
<a href="https://code.google.com/p/support/wiki/IssueTracker#Integration_with_version_control">Google Project Hosting Issue Tracker documentation</a>.)
|
||||
The commented section of the file lists all the modified files in your client.
|
||||
It is best to keep unrelated changes in different change lists,
|
||||
so if you see a file listed that should not be included, abort
|
||||
the command and move that file to a different branch.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Save the file and exit the editor.</p>
|
||||
The special notation "Fixes #159" associates the change with issue 159 in the
|
||||
<a href="https://golang.org/issue/159">Go issue tracker</a>.
|
||||
When this change is eventually submitted, the issue
|
||||
tracker will automatically mark the issue as fixed.
|
||||
(There are several such conventions, described in detail in the
|
||||
<a href="https://help.github.com/articles/closing-issues-via-commit-messages/">GitHub Issue Tracker documentation</a>.)
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The code review server assigns your change an issue number and URL,
|
||||
which <code>hg change</code> will print, something like:
|
||||
Once you have finished writing the commit message,
|
||||
save the file and exit the editor.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you wish to do more editing, re-stage your changes using
|
||||
<code>git</code> <code>add</code>, and then run
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
CL created: https://codereview.appspot.com/99999
|
||||
$ git change
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
to update the change description and incorporate the staged changes. The
|
||||
change description contains a <code>Change-Id</code> line near the bottom,
|
||||
added by a Git commit hook during the initial
|
||||
<code>git</code> <code>change</code>.
|
||||
That line is used by Gerrit to match successive uploads of the same change.
|
||||
Do not edit or delete it.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
(In Git terms, <code>git</code> <code>change</code> with no branch name
|
||||
runs <code>git</code> <code>commit</code> <code>--amend</code>.)
|
||||
</p>
|
||||
|
||||
<h3>Mail the change for review</h3>
|
||||
|
||||
<p>
|
||||
Creating or uploading the change uploads a copy of the diff to the code review server,
|
||||
but it does not notify anyone about it. To do that, you need to run <code>hg mail</code>
|
||||
(see below).
|
||||
</p>
|
||||
|
||||
<p>To send out a change for review, run <code>hg mail</code> using the change list number
|
||||
assigned during <code>hg change</code>:</p>
|
||||
|
||||
<pre>
|
||||
$ hg mail 99999
|
||||
</pre>
|
||||
|
||||
<p>You can add to the <code>Reviewer:</code> and <code>CC:</code> lines
|
||||
using the <code>-r</code> or <code>--cc</code> options.
|
||||
In the above example, we could have left the <code>Reviewer</code> and <code>CC</code>
|
||||
lines blank and then run:
|
||||
Once the change is ready, mail it out for review:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
$ hg mail -r golang-codereviews@googlegroups.com --cc math-nuts@swtch.com 99999
|
||||
$ git mail
|
||||
</pre>
|
||||
|
||||
<p>to achieve the same effect.</p>
|
||||
<p>
|
||||
You can specify a reviewer or CC interested parties
|
||||
using the <code>-r</code> or <code>-cc</code> options.
|
||||
Both accept a comma-separated list of email addresses:
|
||||
</p>
|
||||
|
||||
<p>Note that <code>-r</code> and <code>--cc</code> cannot be spelled <code>--r</code> or <code>-cc</code>.</p>
|
||||
<pre>
|
||||
$ git mail -r joe@golang.org -cc mabel@example.com,math-nuts@swtch.com
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Unless explicitly told otherwise, such as in the discussion leading
|
||||
up to sending in the change list, it's better not to specify a reviewer.
|
||||
All changes are automatically CC'ed to the
|
||||
<a href="https://groups.google.com/group/golang-codereviews">golang-codereviews@googlegroups.com</a>
|
||||
mailing list.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
(In Git terms, <code>git</code> <code>mail</code> pushes the local committed
|
||||
changes to Gerrit using <code>git</code> <code>push</code> <code>origin</code>
|
||||
<code>HEAD:refs/for/master</code>.)
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If your change relates to an open issue, please add a comment to the issue
|
||||
announcing your proposed fix, including a link to your CL.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The code review server assigns your change an issue number and URL,
|
||||
which <code>git</code> <code>mail</code> will print, something like:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
remote: New Changes:
|
||||
remote: https://go-review.googlesource.com/99999 math: improved Sin, Cos and Tan precision for very large arguments
|
||||
</pre>
|
||||
|
||||
<h3>Reviewing code</h3>
|
||||
|
||||
<p>
|
||||
Running <code>hg mail</code> will send an email to you and the reviewers
|
||||
asking them to visit the issue's URL and make comments on the change.
|
||||
When done, the reviewer clicks “Publish and Mail comments”
|
||||
to send comments back.
|
||||
Running <code>git</code> <code>mail</code> will send an email to you and the
|
||||
reviewers asking them to visit the issue's URL and make comments on the change.
|
||||
When done, the reviewer adds comments through the Gerrit user interface
|
||||
and clicks "Reply" to send comments back.
|
||||
You will receive a mail notification when this happens.
|
||||
You must reply through the web interface.
|
||||
(Unlike with the old Rietveld review system, replying by mail has no effect.)
|
||||
</p>
|
||||
|
||||
|
||||
<h3>Revise and upload</h3>
|
||||
|
||||
<p>
|
||||
You must respond to review comments through the web interface.
|
||||
(Unlike with the old Rietveld review system, responding by mail has no effect.)
|
||||
</p>
|
||||
|
||||
<p>
|
||||
When you have revised the code and are ready for another round of review,
|
||||
you can upload your change and send mail asking the reviewers to
|
||||
please take another look (<code>PTAL</code>). Use the change list number
|
||||
assigned during <code>hg change</code>
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
$ hg mail 99999
|
||||
</pre>
|
||||
|
||||
|
||||
<p>
|
||||
Or to upload your change without sending a notification, run
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
$ hg upload 99999
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
You will probably revise your code in response to the reviewer comments.
|
||||
You might also visit the code review web page and reply to the comments,
|
||||
letting the reviewer know that you've addressed them or explain why you
|
||||
haven't. When you're done replying, click “Publish and Mail comments”
|
||||
to send the line-by-line replies and any other comments.
|
||||
stage those changes and use <code>git</code> <code>change</code> to update the
|
||||
commit.
|
||||
To send the update change list for another round of review,
|
||||
run <code>git</code> <code>mail</code> again.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The reviewer can comment on the new copy, and the process repeats.
|
||||
The reviewer approves the change by replying with a mail that says
|
||||
<code>LGTM</code>: looks good to me.
|
||||
The reviewer approves the change by giving it a positive score
|
||||
(+1 or +2) and replying <code>LGTM</code>: looks good to me.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You can see a list of your pending changes by running <code>hg pending</code> (<code>hg p</code> for short).
|
||||
</p>
|
||||
|
||||
<h3>Adding or removing files from an existing change</h3>
|
||||
|
||||
<p>
|
||||
If you need to re-edit the change description, or change the files included in the CL,
|
||||
run <code>hg change 99999</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Alternatively, you can use
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
$ hg file 99999 somefile
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
to add <code>somefile</code> to CL 99999, and
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
$ hg file -d 99999 somefile
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
to remove <code>somefile</code> from the CL.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
A file may only belong to a single active CL at a time. <code>hg file</code>
|
||||
will issue a warning if a file is moved between changes.
|
||||
You can see a list of your pending changes by running <code>git</code>
|
||||
<code>pending</code>, and switch between change branches with <code>git</code>
|
||||
<code>change</code> <code><i><branch></i></code>.
|
||||
</p>
|
||||
|
||||
<h3>Synchronize your client</h3>
|
||||
|
||||
<p>While you were working, others might have submitted changes
|
||||
to the repository. To update your client, run</p>
|
||||
<p>
|
||||
While you were working, others might have submitted changes to the repository.
|
||||
To update your local branch, run
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
$ hg sync
|
||||
$ git sync
|
||||
</pre>
|
||||
|
||||
<p>(For Mercurial fans, <code>hg sync</code> runs <code>hg pull -u</code>
|
||||
but then also synchronizes the local change list state against the new data.)</p>
|
||||
|
||||
<p>
|
||||
If files you were editing have changed, Mercurial does its best to merge the
|
||||
remote changes into your local changes. It may leave some files to merge by hand.
|
||||
(In git terms, <code>git</code> <code>sync</code> runs
|
||||
<code>git</code> <code>pull</code> <code>-r</code>.)
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For example, suppose you have edited <code>flag_test.go</code> but
|
||||
If files you were editing have changed, Git does its best to merge the
|
||||
remote changes into your local changes.
|
||||
It may leave some files to merge by hand.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For example, suppose you have edited <code>sin.go</code> but
|
||||
someone else has committed an independent change.
|
||||
When you run <code>hg sync</code>, you will get the (scary-looking) output
|
||||
(emphasis added):
|
||||
When you run <code>git</code> <code>sync</code>,
|
||||
you will get the (scary-looking) output:
|
||||
|
||||
<pre>
|
||||
$ hg sync
|
||||
adding changesets
|
||||
adding manifests
|
||||
adding file changes
|
||||
added 1 changeset with 2 changes to 2 files
|
||||
getting src/flag/flag.go
|
||||
couldn't find merge tool hgmerge
|
||||
merging src/flag/flag_test.go
|
||||
warning: conflicts during merge.
|
||||
<i>merging src/flag/flag_test.go failed!</i>
|
||||
1 file updated, 0 files merged, 0 files removed, 1 file unresolved
|
||||
use 'hg resolve' to retry unresolved file merges
|
||||
$
|
||||
$ git sync
|
||||
Failed to merge in the changes.
|
||||
Patch failed at 0023 math: improved Sin, Cos and Tan precision for very large arguments
|
||||
The copy of the patch that failed is found in:
|
||||
/home/you/repo/.git/rebase-apply/patch
|
||||
|
||||
When you have resolved this problem, run "git rebase --continue".
|
||||
If you prefer to skip this patch, run "git rebase --skip" instead.
|
||||
To check out the original branch and stop rebasing, run "git rebase --abort".
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
The only important part in that transcript is the italicized line:
|
||||
Mercurial failed to merge your changes with the independent change.
|
||||
When this happens, Mercurial leaves both edits in the file,
|
||||
marked by <code><<<<<<<</code> and
|
||||
If this happens, run
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
$ git status
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
to see which files failed to merge.
|
||||
The output will look something like this:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
rebase in progress; onto a24c3eb
|
||||
You are currently rebasing branch 'mcgillicutty' on 'a24c3eb'.
|
||||
(fix conflicts and then run "git rebase --continue")
|
||||
(use "git rebase --skip" to skip this patch)
|
||||
(use "git rebase --abort" to check out the original branch)
|
||||
|
||||
Unmerged paths:
|
||||
(use "git reset HEAD <file>..." to unstage)
|
||||
(use "git add <file>..." to mark resolution)
|
||||
|
||||
<i>both modified: sin.go</i>
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
The only important part in that transcript is the italicized "both modified"
|
||||
line: Git failed to merge your changes with the conflicting change.
|
||||
When this happens, Git leaves both sets of edits in the file,
|
||||
with conflicts marked by <code><<<<<<<</code> and
|
||||
<code>>>>>>>></code>.
|
||||
It is now your job to edit the file to combine them.
|
||||
Continuing the example, searching for those strings in <code>flag_test.go</code>
|
||||
Continuing the example, searching for those strings in <code>sin.go</code>
|
||||
might turn up:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
VisitAll(visitor);
|
||||
<<<<<<< local
|
||||
if len(m) != 7 {
|
||||
arg = scale(arg)
|
||||
<<<<<<< HEAD
|
||||
if arg > 1e9 {
|
||||
=======
|
||||
if len(m) != 8 {
|
||||
>>>>>>> other
|
||||
t.Error("VisitAll misses some flags");
|
||||
if arg > 1e10 {
|
||||
>>>>>>> mcgillicutty
|
||||
largeReduce(arg)
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Mercurial doesn't show it, but suppose the original text that both edits
|
||||
started with was 6; you added 1 and the other change added 2,
|
||||
so the correct answer might now be 9. First, edit the section
|
||||
Git doesn't show it, but suppose the original text that both edits
|
||||
started with was 1e8; you changed it to 1e10 and the other change to 1e9,
|
||||
so the correct answer might now be 1e10. First, edit the section
|
||||
to remove the markers and leave the correct code:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
VisitAll(visitor);
|
||||
if len(m) != 9 {
|
||||
t.Error("VisitAll misses some flags");
|
||||
arg = scale(arg)
|
||||
if arg > 1e10 {
|
||||
largeReduce(arg)
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Then ask Mercurial to mark the conflict as resolved:
|
||||
Then tell Git that the conflict is resolved by running
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
$ hg resolve -m flag_test.go
|
||||
$ git add sin.go
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
If you had been editing the file, say for debugging, but do not
|
||||
care to preserve your changes, you can run
|
||||
<code>hg revert flag_test.go</code> to abandon your
|
||||
changes, but you may still need to run
|
||||
<code>hg resolve -m</code> to mark the conflict resolved.
|
||||
<code>git</code> <code>reset</code> <code>HEAD</code> <code>sin.go</code>
|
||||
to abandon your changes.
|
||||
Then run <code>git</code> <code>rebase</code> <code>--continue</code> to
|
||||
restore the change commit.
|
||||
</p>
|
||||
|
||||
<h3>Reviewing code by others</h3>
|
||||
|
||||
<p>
|
||||
You can import a CL proposed by someone else into your local Mercurial client
|
||||
by using the <code>hg clpatch</code> command. Running
|
||||
You can import a change proposed by someone else into your local Git repository.
|
||||
On the Gerrit review page, click the "Download ▼" link in the upper right
|
||||
corner, copy the "Checkout" command and run it from your local Git repo.
|
||||
It should look something like this:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
$ hg clpatch 99999
|
||||
$ git fetch https://go.googlesource.com/review refs/changes/21/1221/1 && git checkout FETCH_HEAD
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
will apply the latest diff for CL 99999 to your working copy. If any of the
|
||||
files referenced in CL 99999 have local modifications, <code>clpatch</code>
|
||||
will refuse to apply the whole diff. Once applied, CL 99999 will show up in
|
||||
the output of <code>hg pending</code> and others.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To revert a CL you have applied locally, use the <code>hg revert</code>
|
||||
command. Running
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
$ hg revert @99999
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
will revert any files mentioned on CL 99999 to their original state. This can
|
||||
be an effective way of reverting one CL revision and applying another.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Once the CL has been submitted, the next time you run <code>hg sync</code>
|
||||
it will be removed from your local pending list. Occasionally the pending list
|
||||
can get out of sync leaving stale references to closed or abandoned CLs.
|
||||
You can use <code>hg change -D 99999</code> to remove the reference to CL 99999.
|
||||
To revert, change back to the branch you were working in.
|
||||
</p>
|
||||
|
||||
<h3>Submit the change after the review</h3>
|
||||
|
||||
<p>
|
||||
After the code has been <code>LGTM</code>'ed, it is time to submit
|
||||
it to the Mercurial repository.
|
||||
After the code has been <code>LGTM</code>'ed, an approver may
|
||||
submit it to the master branch using the Gerrit UI.
|
||||
There is a "Submit" button on the web page for the change
|
||||
that appears once the change is approved (marked +2).
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you are not a committer, you cannot submit the change directly.
|
||||
Instead a committer, usually the reviewer who said <code>LGTM</code>,
|
||||
will run:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
$ hg clpatch 99999
|
||||
$ hg submit 99999
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
The <code>submit</code> command submits the code. You will be listed as the
|
||||
author, but the change message will also indicate who the committer was.
|
||||
Your local client will notice that the change has been submitted
|
||||
when you next run <code>hg sync</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you are a committer, you can run:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
$ hg submit 99999
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
This checks the change into the repository.
|
||||
The change description will include a link to the code review,
|
||||
and the code review will be updated with a link to the change
|
||||
in the repository.
|
||||
Since the method used to integrate the changes is "Cherry Pick",
|
||||
the commit hashes in the repository will be changed by
|
||||
the submit operation.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If your local copy of the repository is out of date,
|
||||
<code>hg submit</code> will refuse the change:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
$ hg submit 99999
|
||||
local repository out of date; must sync before submit
|
||||
</pre>
|
||||
|
||||
<h3>More information</h3>
|
||||
|
||||
<p>
|
||||
In addition to the information here, the Go community maintains a <a href="https://code.google.com/p/go-wiki/wiki/CodeReview">CodeReview</a> wiki page.
|
||||
In addition to the information here, the Go community maintains a <a href="https://golang.org/wiki/CodeReview">CodeReview</a> wiki page.
|
||||
Feel free to contribute to this page as you learn the review process.
|
||||
</p>
|
||||
|
||||
@@ -617,7 +614,8 @@ Feel free to contribute to this page as you learn the review process.
|
||||
|
||||
<p>Files in the Go repository don't list author names,
|
||||
both to avoid clutter and to avoid having to keep the lists up to date.
|
||||
Instead, your name will appear in the <a href="https://code.google.com/p/go/source/list">Mercurial change log</a>
|
||||
Instead, your name will appear in the
|
||||
<a href="https://golang.org/change">change log</a>
|
||||
and in the <a href="/CONTRIBUTORS"><code>CONTRIBUTORS</code></a> file
|
||||
and perhaps the <a href="/AUTHORS"><code>AUTHORS</code></a> file.
|
||||
</p>
|
||||
@@ -654,7 +652,7 @@ This rigmarole needs to be done only for your first submission.
|
||||
<p>Code that you contribute should use the standard copyright header:</p>
|
||||
|
||||
<pre>
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Copyright 2015 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.
|
||||
</pre>
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
}-->
|
||||
|
||||
<p>This page summarizes the changes between official stable releases of Go.
|
||||
The <a href="//code.google.com/p/go/source/list">Mercurial change log</a>
|
||||
has the full details.</p>
|
||||
The <a href="//golang.org/change">change log</a> has the full details.</p>
|
||||
|
||||
<p>To update to a specific release, use:</p>
|
||||
|
||||
@@ -13,6 +12,25 @@ hg pull
|
||||
hg update <i>tag</i>
|
||||
</pre>
|
||||
|
||||
<h2 id="go1.4">go1.4 (released 2014/12/10)</h2>
|
||||
|
||||
<p>
|
||||
Go 1.4 is a major release of Go.
|
||||
Read the <a href="/doc/go1.4">Go 1.4 Release Notes</a> for more information.
|
||||
</p>
|
||||
|
||||
<h3 id="go1.4.minor">Minor revisions</h3>
|
||||
|
||||
<p>
|
||||
go1.4.1 (released 2015/01/15) includes bug fixes to the linker and the <code>log</code>, <code>syscall</code>, and <code>runtime</code> packages.
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.4.1">Go 1.4.1 milestone on our issue tracker</a> for details.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
go1.4.2 (released 2015/02/17) includes bug fixes to the <code>go</code> command, the compiler and linker, and the <code>runtime</code>, <code>syscall</code>, <code>reflect</code>, and <code>math/big</code> packages.
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.4.2">Go 1.4.2 milestone on our issue tracker</a> for details.
|
||||
</p>
|
||||
|
||||
<h2 id="go1.3">go1.3 (released 2014/06/18)</h2>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<p>This page summarizes the changes between tagged weekly snapshots of Go.
|
||||
Such snapshots are no longer created. This page remains as a historical reference only.</p>
|
||||
|
||||
<p>For recent information, see the <a href="//code.google.com/p/go/source/list">Mercurial change log</a> and <a href="//groups.google.com/group/golang-dev/">development mailing list</a>.</p>
|
||||
<p>For recent information, see the <a href="//golang.org/change">change log</a> and <a href="//groups.google.com/group/golang-dev/">development mailing list</a>.</p>
|
||||
|
||||
<h2 id="2012-03-27">2012-03-27 (<a href="release.html#go1">Go 1</a>)</h2>
|
||||
|
||||
|
||||
@@ -298,7 +298,7 @@ For example,
|
||||
<h3 id="godoc">Changes to godoc</h3>
|
||||
<p>
|
||||
When invoked with the <code>-analysis</code> flag,
|
||||
<a href="//godoc.org/code.google.com/p/go.tools/cmd/godoc">godoc</a>
|
||||
<a href="//godoc.org/golang.org/x/tools/cmd/godoc">godoc</a>
|
||||
now performs sophisticated <a href="/lib/godoc/analysis/help.html">static
|
||||
analysis</a> of the code it indexes.
|
||||
The results of analysis are presented in both the source view and the
|
||||
@@ -318,7 +318,7 @@ call sites and their callees.
|
||||
The program <code>misc/benchcmp</code> that compares
|
||||
performance across benchmarking runs has been rewritten.
|
||||
Once a shell and awk script in the main repository, it is now a Go program in the <code>go.tools</code> repo.
|
||||
Documentation is <a href="//godoc.org/code.google.com/p/go.tools/cmd/benchcmp">here</a>.
|
||||
Documentation is <a href="//godoc.org/golang.org/x/tools/cmd/benchcmp">here</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -7,10 +7,16 @@
|
||||
<h2 id="introduction">Introduction to Go 1.4</h2>
|
||||
|
||||
<p>
|
||||
The latest Go release, version 1.4, arrives as scheduled six months after 1.3
|
||||
and contains only one tiny language change,
|
||||
a possibly breaking change to the compiler,
|
||||
a backwards-compatible simple form of <code>for</code>-<code>range</code> loop.
|
||||
The latest Go release, version 1.4, arrives as scheduled six months after 1.3.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
It contains only one tiny language change,
|
||||
in the form of a backwards-compatible simple variant of <code>for</code>-<code>range</code> loop,
|
||||
and a possibly breaking change to the compiler involving methods on pointers-to-pointers.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The release focuses primarily on implementation work, improving the garbage collector
|
||||
and preparing the ground for a fully concurrent collector to be rolled out in the
|
||||
next few releases.
|
||||
@@ -20,7 +26,10 @@ this release therefore eliminates the notorious "hot stack split" problem.
|
||||
There are some new tools available including support in the <code>go</code> command
|
||||
for build-time source code generation.
|
||||
The release also adds support for ARM processors on Android and Native Client (NaCl)
|
||||
and AMD64 on Plan 9.
|
||||
and for AMD64 on Plan 9.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
As always, Go 1.4 keeps the <a href="/doc/go1compat.html">promise
|
||||
of compatibility</a>,
|
||||
and almost everything
|
||||
@@ -35,7 +44,7 @@ Up until Go 1.3, <code>for</code>-<code>range</code> loop had two forms
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
for k, v := range x {
|
||||
for i, v := range x {
|
||||
...
|
||||
}
|
||||
</pre>
|
||||
@@ -45,7 +54,7 @@ and
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
for k := range x {
|
||||
for i := range x {
|
||||
...
|
||||
}
|
||||
</pre>
|
||||
@@ -172,7 +181,7 @@ of the documentation.
|
||||
<h3 id="runtime">Changes to the runtime</h3>
|
||||
|
||||
<p>
|
||||
Up to Go 1.4, the runtime (garbage collector, concurrency support, interface management,
|
||||
Prior to Go 1.4, the runtime (garbage collector, concurrency support, interface management,
|
||||
maps, slices, strings, ...) was mostly written in C, with some assembler support.
|
||||
In 1.4, much of the code has been translated to Go so that the garbage collector can scan
|
||||
the stacks of programs in the runtime and get accurate information about what variables
|
||||
@@ -198,7 +207,7 @@ Details are available in <a href="https://golang.org/s/contigstacks">the design
|
||||
|
||||
<p>
|
||||
The use of contiguous stacks means that stacks can start smaller without triggering performance issues,
|
||||
so the default starting size for a goroutine's stack in 1.4 has been reduced to 2048 bytes from 8192 bytes.
|
||||
so the default starting size for a goroutine's stack in 1.4 has been reduced from 8192 bytes to 2048 bytes.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -513,10 +522,7 @@ have been updated.
|
||||
<h3 id="swig">SWIG</h3>
|
||||
|
||||
<p>
|
||||
Due to the runtime changes in this release, Go 1.4 will require SWIG 3.0.3.
|
||||
At time of writing that has not yet been released, but we expect it to be by
|
||||
Go 1.4's release date.
|
||||
TODO
|
||||
Due to runtime changes in this release, Go 1.4 requires SWIG 3.0.3.
|
||||
</p>
|
||||
|
||||
<h3 id="misc">Miscellany</h3>
|
||||
@@ -535,7 +541,7 @@ editor, even for editors we do not use.
|
||||
The Go community at large is much better suited to managing this information.
|
||||
In Go 1.4, therefore, this support has been removed from the repository.
|
||||
Instead, there is a curated, informative list of what's available on
|
||||
a <a href="https://code.google.com/p/go-wiki/wiki/IDEsAndTextEditorPlugins">wiki page</a>.
|
||||
a <a href="//golang.org/wiki/IDEsAndTextEditorPlugins">wiki page</a>.
|
||||
</p>
|
||||
|
||||
<h2 id="performance">Performance</h2>
|
||||
|
||||
@@ -228,7 +228,7 @@ document server running in a production configuration on
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Other examples include the <a href="https://code.google.com/p/vitess/">Vitess</a>
|
||||
Other examples include the <a href="//code.google.com/p/vitess/">Vitess</a>
|
||||
system for large-scale SQL installations and Google's download server, <code>dl.google.com</code>,
|
||||
which delivers Chrome binaries and other large installables such as <code>apt-get</code>
|
||||
packages.
|
||||
@@ -986,32 +986,6 @@ See the document
|
||||
for more information about how to proceed.
|
||||
</p>
|
||||
|
||||
<h3 id="Why_does_the_project_use_Mercurial_and_not_git">
|
||||
Why does the project use Mercurial and not git?</h3>
|
||||
|
||||
<p>
|
||||
The Go project, hosted by Google Code at
|
||||
<a href="//code.google.com/p/go">code.google.com/p/go</a>,
|
||||
uses Mercurial as its version control system.
|
||||
When the project launched,
|
||||
Google Code supported only Subversion and Mercurial.
|
||||
Mercurial was a better choice because of its plugin mechanism
|
||||
that allowed us to create the "codereview" plugin to connect
|
||||
the project to the excellent code review tools at
|
||||
<a href="//codereview.appspot.com">codereview.appspot.com</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Programmers who work
|
||||
with the Go project's source rather than release downloads sometimes
|
||||
ask for the project to switch to git.
|
||||
That would be possible, but it would be a lot of work and
|
||||
would also require reimplementing the codereview plugin.
|
||||
Given that Mercurial works today, with code review support,
|
||||
combined with the Go project's mostly linear, non-branching use of
|
||||
version control, a switch to git doesn't seem worthwhile.
|
||||
</p>
|
||||
|
||||
<h3 id="git_https">
|
||||
Why does "go get" use HTTPS when cloning a repository?</h3>
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ Need help with Go? Try these resources.
|
||||
<p>
|
||||
Search the <a href="//groups.google.com/group/golang-nuts">golang-nuts</a>
|
||||
archives and consult the <a href="/doc/go_faq.html">FAQ</a> and
|
||||
<a href="//code.google.com/p/go-wiki/wiki">wiki</a> before posting.
|
||||
<a href="//golang.org/wiki">wiki</a> before posting.
|
||||
</p>
|
||||
|
||||
<h3 id="irc"><a href="irc:irc.freenode.net/go-nuts">Go IRC Channel</a></h3>
|
||||
|
||||
@@ -81,38 +81,21 @@ The full set of supported combinations is listed in the discussion of
|
||||
|
||||
<p>
|
||||
The Go tool chain is written in C. To build it, you need a C compiler installed.
|
||||
Please refer to the <a href="//golang.org/wiki/InstallFromSource#Install_C_tools">InstallFromSource</a>
|
||||
Please refer to the <a href="//golang.org/wiki/InstallFromSource#install-c-tools">InstallFromSource</a>
|
||||
page on the Go community Wiki for operating system specific instructions.
|
||||
</p>
|
||||
|
||||
<h2 id="mercurial">Install Mercurial, if needed</h2>
|
||||
<h2 id="git">Install Git, if needed</h2>
|
||||
|
||||
<p>
|
||||
To perform the next step you must have Mercurial installed. (Check that you
|
||||
have an <code>hg</code> command.)
|
||||
To perform the next step you must have Git installed. (Check that you
|
||||
have a <code>git</code> command before proceeding.)
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you do not have a working Mercurial installation,
|
||||
If you do not have a working Git installation,
|
||||
follow the instructions on the
|
||||
<a href="http://mercurial.selenic.com/downloads">Mercurial downloads</a> page.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Mercurial versions 1.7.x and up require the configuration of
|
||||
<a href="http://mercurial.selenic.com/wiki/CACertificates">Certification Authorities</a>
|
||||
(CAs). Error messages of the form:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
warning: code.google.com certificate with fingerprint b1:af: ... bc not verified (check hostfingerprints or web.cacerts config setting)
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
when using Mercurial indicate that the CAs are missing.
|
||||
Check your Mercurial version (<code>hg --version</code>) and
|
||||
<a href="http://mercurial.selenic.com/wiki/CACertificates#Configuration_of_HTTPS_certificate_authorities">configure the CAs</a>
|
||||
if necessary.
|
||||
<a href="http://git-scm.com/downloads">Git downloads</a> page.
|
||||
</p>
|
||||
|
||||
|
||||
@@ -121,22 +104,24 @@ if necessary.
|
||||
<p>Go will install to a directory named <code>go</code>.
|
||||
Change to the directory that will be its parent
|
||||
and make sure the <code>go</code> directory does not exist.
|
||||
Then check out the repository:</p>
|
||||
Then clone the repository and check out the latest release tag:</p>
|
||||
|
||||
<pre>
|
||||
$ hg clone -u release https://code.google.com/p/go
|
||||
$ git clone https://go.googlesource.com/go
|
||||
$ cd go
|
||||
$ git checkout go1.4.1
|
||||
</pre>
|
||||
|
||||
<h2 id="head">(Optional) Switch to the default branch</h2>
|
||||
<h2 id="head">(Optional) Switch to the master branch</h2>
|
||||
|
||||
<p>If you intend to modify the go source code, and
|
||||
<a href="/doc/contribute.html">contribute your changes</a>
|
||||
to the project, then move your repository
|
||||
off the release branch, and onto the default (development) branch.
|
||||
off the release branch, and onto the master (development) branch.
|
||||
Otherwise, skip this step.</p>
|
||||
|
||||
<pre>
|
||||
$ hg update default
|
||||
$ git checkout master
|
||||
</pre>
|
||||
|
||||
<h2 id="install">Install Go</h2>
|
||||
@@ -259,7 +244,7 @@ $ go get golang.org/x/tools/cmd/godoc
|
||||
|
||||
<p>
|
||||
To install these tools, the <code>go</code> <code>get</code> command requires
|
||||
that <a href="#mercurial">Mercurial</a> be installed locally.
|
||||
that <a href="#git">Git</a> be installed locally.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -292,22 +277,18 @@ that receives a message summarizing each checkin to the Go repository.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Bugs can be reported using the <a href="//code.google.com/p/go/issues/list">Go issue tracker</a>.
|
||||
Bugs can be reported using the <a href="//golang.org/issue/new">Go issue tracker</a>.
|
||||
</p>
|
||||
|
||||
|
||||
<h2 id="releases">Keeping up with releases</h2>
|
||||
|
||||
<p>
|
||||
The Go project maintains a stable tag in its Mercurial repository:
|
||||
<code>release</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The <code>release</code> tag refers to the current stable release of Go.
|
||||
Most Go users should use this version. New releases are announced on the
|
||||
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.4</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -316,11 +297,13 @@ To update an existing tree to the latest release, you can run:
|
||||
|
||||
<pre>
|
||||
$ cd go/src
|
||||
$ hg pull
|
||||
$ hg update release
|
||||
$ git fetch
|
||||
$ git checkout <i><tag></i>
|
||||
$ ./all.bash
|
||||
</pre>
|
||||
|
||||
Where <code><tag></code> is the version string of the release.
|
||||
|
||||
|
||||
<h2 id="environment">Optional environment variables</h2>
|
||||
|
||||
|
||||
@@ -314,7 +314,11 @@ class CL(object):
|
||||
if self.name != "new":
|
||||
s = "code review %s: %s" % (self.name, s)
|
||||
typecheck(s, str)
|
||||
return branch_prefix(ui, repo) + s
|
||||
s = branch_prefix(ui, repo) + s
|
||||
# Rietveld does a hard reject on any subject > 100 chars. Be sure.
|
||||
if len(s) >= 100:
|
||||
s = s[0:95] + "..."
|
||||
return s
|
||||
|
||||
def Upload(self, ui, repo, send_mail=False, gofmt=True, gofmt_just_warn=False, creating=False, quiet=False):
|
||||
if not self.files and not creating:
|
||||
@@ -409,7 +413,7 @@ class CL(object):
|
||||
if not self.mailed:
|
||||
pmsg += "I'd like you to review this change to"
|
||||
branch = repo[None].branch()
|
||||
if branch.startswith("dev."):
|
||||
if workbranch(branch) and branch != "default":
|
||||
pmsg += " the " + branch + " branch of"
|
||||
pmsg += "\n" + repourl + "\n"
|
||||
else:
|
||||
@@ -1921,7 +1925,7 @@ def need_sync():
|
||||
def branch_prefix(ui, repo):
|
||||
prefix = ""
|
||||
branch = repo[None].branch()
|
||||
if branch.startswith("dev."):
|
||||
if workbranch(branch) and branch != "default":
|
||||
prefix = "[" + branch + "] "
|
||||
return prefix
|
||||
|
||||
@@ -2726,7 +2730,7 @@ def RietveldSetup(ui, repo):
|
||||
releaseBranch = t
|
||||
|
||||
def workbranch(name):
|
||||
return name == "default" or name.startswith('dev.')
|
||||
return name == "default" or name.startswith('dev.') or name == 'release-branch.go1.4'
|
||||
|
||||
#######################################################################
|
||||
# http://codereview.appspot.com/static/upload.py, heavily edited.
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
# downloaded from the ICANN/IANA distribution.
|
||||
|
||||
# Versions to use.
|
||||
CODE=2014d
|
||||
DATA=2014d
|
||||
CODE=2014j
|
||||
DATA=2014j
|
||||
|
||||
set -e
|
||||
rm -rf work
|
||||
|
||||
Binary file not shown.
@@ -12,9 +12,15 @@ complex double complexDoubleSquared(complex double a) { return a*a; }
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func test8694(t *testing.T) {
|
||||
if runtime.GOARCH == "arm" {
|
||||
t.Skip("test8694 is disabled on ARM because 5l cannot handle thumb library.")
|
||||
}
|
||||
// Really just testing that this compiles, but check answer anyway.
|
||||
x := complex64(2 + 3i)
|
||||
x2 := x * x
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"compress/gzip"
|
||||
"crypto/sha1"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -30,7 +31,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"code.google.com/p/goauth2/oauth"
|
||||
storage "code.google.com/p/google-api-go-client/storage/v1beta2"
|
||||
storage "code.google.com/p/google-api-go-client/storage/v1"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -56,8 +57,8 @@ const (
|
||||
blogPath = "golang.org/x/blog"
|
||||
toolPath = "golang.org/x/tools"
|
||||
tourPath = "code.google.com/p/go-tour"
|
||||
defaultToolTag = "release-branch.go1.3"
|
||||
defaultTourTag = "release-branch.go1.3"
|
||||
defaultToolTag = "release-branch.go1.4"
|
||||
defaultTourTag = "release-branch.go1.4"
|
||||
)
|
||||
|
||||
// Import paths for tool commands.
|
||||
@@ -504,16 +505,38 @@ func (b *Build) extras() error {
|
||||
}
|
||||
|
||||
func (b *Build) get(repoPath, revision string) error {
|
||||
// Fetch the packages (without building/installing).
|
||||
_, err := b.run(b.gopath, filepath.Join(b.root, "bin", "go"),
|
||||
"get", "-d", repoPath+"/...")
|
||||
if err != nil {
|
||||
return err
|
||||
dest := filepath.Join(b.gopath, "src", filepath.FromSlash(repoPath))
|
||||
|
||||
if strings.HasPrefix(repoPath, "golang.org/x/") {
|
||||
// For sub-repos, fetch the old Mercurial repo; bypass "go get".
|
||||
// DO NOT import this special case into the git tree.
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
repo := strings.Replace(repoPath, "golang.org/x/", "https://code.google.com/p/go.", 1)
|
||||
if _, err := b.run(b.gopath, "hg", "clone", repo, dest); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Fetch the packages (without building/installing).
|
||||
_, err := b.run(b.gopath, filepath.Join(b.root, "bin", "go"),
|
||||
"get", "-d", repoPath+"/...")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Update the repo to the specified revision.
|
||||
p := filepath.Join(b.gopath, "src", filepath.FromSlash(repoPath))
|
||||
_, err = b.run(p, "hg", "update", revision)
|
||||
var err error
|
||||
switch {
|
||||
case exists(filepath.Join(dest, ".git")):
|
||||
_, err = b.run(dest, "git", "checkout", revision)
|
||||
case exists(filepath.Join(dest, ".hg")):
|
||||
_, err = b.run(dest, "hg", "update", revision)
|
||||
default:
|
||||
err = errors.New("unknown version control system")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -18,9 +18,6 @@ go src=..
|
||||
gofmt_test.go
|
||||
testdata
|
||||
+
|
||||
link
|
||||
testdata
|
||||
+
|
||||
archive
|
||||
tar
|
||||
testdata
|
||||
|
||||
@@ -236,18 +236,14 @@ cgen(Node *n, Node *res)
|
||||
cgen(nl, &n1);
|
||||
nodconst(&n2, nl->type, -1);
|
||||
gins(a, &n2, &n1);
|
||||
gmove(&n1, res);
|
||||
regfree(&n1);
|
||||
goto ret;
|
||||
goto norm;
|
||||
|
||||
case OMINUS:
|
||||
regalloc(&n1, nl->type, N);
|
||||
cgen(nl, &n1);
|
||||
nodconst(&n2, nl->type, 0);
|
||||
gins(optoas(OMINUS, nl->type), &n2, &n1);
|
||||
gmove(&n1, res);
|
||||
regfree(&n1);
|
||||
goto ret;
|
||||
goto norm;
|
||||
|
||||
// symmetric binary
|
||||
case OAND:
|
||||
@@ -483,12 +479,15 @@ abop: // asymmetric binary
|
||||
cgen(nl, &n1);
|
||||
}
|
||||
gins(a, &n2, &n1);
|
||||
norm:
|
||||
// Normalize result for types smaller than word.
|
||||
if(n->type->width < widthptr) {
|
||||
switch(n->op) {
|
||||
case OADD:
|
||||
case OSUB:
|
||||
case OMUL:
|
||||
case OCOM:
|
||||
case OMINUS:
|
||||
gins(optoas(OAS, n->type), &n1, &n1);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -283,7 +283,7 @@ func compareAPI(w io.Writer, features, required, optional, exception []string) (
|
||||
delete(optionalSet, newFeature)
|
||||
} else {
|
||||
fmt.Fprintf(w, "+%s\n", newFeature)
|
||||
if !*allowNew {
|
||||
if !*allowNew || !strings.Contains(runtime.Version(), "devel") {
|
||||
ok = false // we're in lock-down mode for next release
|
||||
}
|
||||
}
|
||||
@@ -313,11 +313,15 @@ func fileFeatures(filename string) []string {
|
||||
if err != nil {
|
||||
log.Fatalf("Error reading file %s: %v", filename, err)
|
||||
}
|
||||
text := strings.TrimSpace(string(bs))
|
||||
if text == "" {
|
||||
return nil
|
||||
lines := strings.Split(string(bs), "\n")
|
||||
var nonblank []string
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if line != "" && !strings.HasPrefix(line, "#") {
|
||||
nonblank = append(nonblank, line)
|
||||
}
|
||||
}
|
||||
return strings.Split(text, "\n")
|
||||
return nonblank
|
||||
}
|
||||
|
||||
var fset = token.NewFileSet()
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -53,7 +54,7 @@ func main() {
|
||||
}
|
||||
|
||||
out, err = exec.Command("go", "tool", "api",
|
||||
"-c", file("go1", "go1.1", "go1.2", "go1.3"),
|
||||
"-c", file("go1", "go1.1", "go1.2", "go1.3", "go1.4"),
|
||||
"-next", file("next"),
|
||||
"-except", file("except")).CombinedOutput()
|
||||
if err != nil {
|
||||
@@ -105,7 +106,7 @@ func prepGoPath() string {
|
||||
}
|
||||
|
||||
// The GOPATH we'll return
|
||||
gopath := filepath.Join(os.TempDir(), "gopath-api-"+cleanUsername(username), goToolsVersion)
|
||||
gopath := filepath.Join(os.TempDir(), "gopath-api-"+cleanUsername(username)+"-"+cleanUsername(strings.Fields(runtime.Version())[0]), goToolsVersion)
|
||||
|
||||
// cloneDir is where we run "hg clone".
|
||||
cloneDir := filepath.Join(gopath, "src", "code.google.com", "p")
|
||||
|
||||
100
src/cmd/dist/build.c
vendored
100
src/cmd/dist/build.c
vendored
@@ -235,24 +235,65 @@ chomp(Buf *b)
|
||||
b->len--;
|
||||
}
|
||||
|
||||
static char*
|
||||
branchtag(char *branch, bool *precise)
|
||||
{
|
||||
char *tag, *p, *q;
|
||||
int i;
|
||||
Buf b, arg;
|
||||
Vec tags;
|
||||
|
||||
binit(&b);
|
||||
binit(&arg);
|
||||
vinit(&tags);
|
||||
|
||||
bprintf(&arg, "master..%s", branch);
|
||||
run(&b, goroot, CheckExit, "git", "log", "--decorate=full", "--format=format:%d", bstr(&arg), nil);
|
||||
|
||||
splitlines(&tags, bstr(&b));
|
||||
tag = branch;
|
||||
for(i=0; i < tags.len; i++) {
|
||||
// Each line is either blank, or looks like
|
||||
// (tag: refs/tags/go1.4rc2, refs/remotes/origin/release-branch.go1.4, refs/heads/release-branch.go1.4)
|
||||
// We need to find an element starting with refs/tags/.
|
||||
p = xstrstr(tags.p[i], " refs/tags/");
|
||||
if(p == nil)
|
||||
continue;
|
||||
p += xstrlen(" refs/tags/");
|
||||
// The tag name ends at a comma or paren (prefer the first).
|
||||
q = xstrstr(p, ",");
|
||||
if(q == nil)
|
||||
q = xstrstr(p, ")");
|
||||
if(q == nil)
|
||||
continue; // malformed line; ignore it
|
||||
*q = '\0';
|
||||
tag = xstrdup(p);
|
||||
if(i == 0)
|
||||
*precise = 1; // tag denotes HEAD
|
||||
break;
|
||||
}
|
||||
|
||||
bfree(&b);
|
||||
bfree(&arg);
|
||||
vfree(&tags);
|
||||
return tag;
|
||||
}
|
||||
|
||||
// findgoversion determines the Go version to use in the version string.
|
||||
static char*
|
||||
findgoversion(void)
|
||||
{
|
||||
char *tag, *rev, *p;
|
||||
int i, nrev;
|
||||
char *tag, *p;
|
||||
bool precise;
|
||||
Buf b, path, bmore, branch;
|
||||
Vec tags;
|
||||
|
||||
binit(&b);
|
||||
binit(&path);
|
||||
binit(&bmore);
|
||||
binit(&branch);
|
||||
vinit(&tags);
|
||||
|
||||
// The $GOROOT/VERSION file takes priority, for distributions
|
||||
// without the Mercurial repo.
|
||||
// without the source repo.
|
||||
bpathf(&path, "%s/VERSION", goroot);
|
||||
if(isfile(bstr(&path))) {
|
||||
readfile(&b, bstr(&path));
|
||||
@@ -266,7 +307,7 @@ findgoversion(void)
|
||||
}
|
||||
|
||||
// The $GOROOT/VERSION.cache file is a cache to avoid invoking
|
||||
// hg every time we run this command. Unlike VERSION, it gets
|
||||
// git every time we run this command. Unlike VERSION, it gets
|
||||
// deleted by the clean command.
|
||||
bpathf(&path, "%s/VERSION.cache", goroot);
|
||||
if(isfile(bstr(&path))) {
|
||||
@@ -275,49 +316,27 @@ findgoversion(void)
|
||||
goto done;
|
||||
}
|
||||
|
||||
// Otherwise, use Mercurial.
|
||||
// Otherwise, use Git.
|
||||
// What is the current branch?
|
||||
run(&branch, goroot, CheckExit, "hg", "identify", "-b", nil);
|
||||
run(&branch, goroot, CheckExit, "git", "rev-parse", "--abbrev-ref", "HEAD", nil);
|
||||
chomp(&branch);
|
||||
|
||||
// What are the tags along the current branch?
|
||||
tag = "devel";
|
||||
rev = ".";
|
||||
run(&b, goroot, CheckExit, "hg", "log", "-b", bstr(&branch), "-r", ".:0", "--template", "{tags} + ", nil);
|
||||
splitfields(&tags, bstr(&b));
|
||||
nrev = 0;
|
||||
for(i=0; i<tags.len; i++) {
|
||||
p = tags.p[i];
|
||||
if(streq(p, "+"))
|
||||
nrev++;
|
||||
// Only show the beta tag for the exact revision.
|
||||
if(hasprefix(p, "go") && (!contains(p, "beta") || nrev == 0)) {
|
||||
tag = xstrdup(p);
|
||||
// If this tag matches the current checkout
|
||||
// exactly (no "+" yet), don't show extra
|
||||
// revision information.
|
||||
if(nrev == 0)
|
||||
rev = "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
precise = 0;
|
||||
|
||||
if(tag[0] == '\0') {
|
||||
// Did not find a tag; use branch name.
|
||||
bprintf(&b, "branch.%s", bstr(&branch));
|
||||
tag = btake(&b);
|
||||
}
|
||||
|
||||
if(rev[0]) {
|
||||
// Tag is before the revision we're building.
|
||||
// Add extra information.
|
||||
run(&bmore, goroot, CheckExit, "hg", "log", "--template", " +{node|short} {date|date}", "-r", rev, nil);
|
||||
chomp(&bmore);
|
||||
}
|
||||
// If we're on a release branch, use the closest matching tag
|
||||
// that is on the release branch (and not on the master branch).
|
||||
if(hasprefix(bstr(&branch), "release-branch."))
|
||||
tag = branchtag(bstr(&branch), &precise);
|
||||
|
||||
bprintf(&b, "%s", tag);
|
||||
if(bmore.len > 0)
|
||||
if(!precise) {
|
||||
// Tag does not point at HEAD; add hash and date to version.
|
||||
run(&bmore, goroot, CheckExit, "git", "log", "-n", "1", "--format=format: +%h %cd", "HEAD", nil);
|
||||
chomp(&bmore);
|
||||
bwriteb(&b, &bmore);
|
||||
}
|
||||
|
||||
// Cache version.
|
||||
writefile(&b, bstr(&path), 0);
|
||||
@@ -330,7 +349,6 @@ done:
|
||||
bfree(&path);
|
||||
bfree(&bmore);
|
||||
bfree(&branch);
|
||||
vfree(&tags);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -128,6 +128,11 @@ dowidth(Type *t)
|
||||
return;
|
||||
}
|
||||
|
||||
// break infinite recursion if the broken recursive type
|
||||
// is referenced again
|
||||
if(t->broke && t->width == 0)
|
||||
return;
|
||||
|
||||
// defer checkwidth calls until after we're done
|
||||
defercalc++;
|
||||
|
||||
|
||||
@@ -974,6 +974,7 @@ EXTERN int funcdepth;
|
||||
EXTERN int typecheckok;
|
||||
EXTERN int compiling_runtime;
|
||||
EXTERN int compiling_wrappers;
|
||||
EXTERN int inl_nonlocal;
|
||||
EXTERN int use_writebarrier;
|
||||
EXTERN int pure_go;
|
||||
EXTERN char* flag_installsuffix;
|
||||
|
||||
@@ -804,9 +804,12 @@ inlvar(Node *var)
|
||||
n->curfn = curfn; // the calling function, not the called one
|
||||
n->addrtaken = var->addrtaken;
|
||||
|
||||
// esc pass wont run if we're inlining into a iface wrapper
|
||||
// luckily, we can steal the results from the target func
|
||||
if(var->esc == EscHeap)
|
||||
// Esc pass wont run if we're inlining into a iface wrapper.
|
||||
// Luckily, we can steal the results from the target func.
|
||||
// If inlining a function defined in another package after
|
||||
// escape analysis is done, treat all local vars as escaping.
|
||||
// See issue 9537.
|
||||
if(var->esc == EscHeap || (inl_nonlocal && var->op == ONAME))
|
||||
addrescapes(n);
|
||||
|
||||
curfn->dcl = list(curfn->dcl, n);
|
||||
|
||||
@@ -143,18 +143,6 @@ mapbucket(Type *t)
|
||||
// We don't need to encode it as GC doesn't care about it.
|
||||
offset = BUCKETSIZE * 1;
|
||||
|
||||
overflowfield = typ(TFIELD);
|
||||
overflowfield->type = ptrto(bucket);
|
||||
overflowfield->width = offset; // "width" is offset in structure
|
||||
overflowfield->sym = mal(sizeof(Sym)); // not important but needs to be set to give this type a name
|
||||
overflowfield->sym->name = "overflow";
|
||||
offset += widthptr;
|
||||
|
||||
// The keys are padded to the native integer alignment.
|
||||
// This is usually the same as widthptr; the exception (as usual) is nacl/amd64.
|
||||
if(widthreg > widthptr)
|
||||
offset += widthreg - widthptr;
|
||||
|
||||
keysfield = typ(TFIELD);
|
||||
keysfield->type = typ(TARRAY);
|
||||
keysfield->type->type = keytype;
|
||||
@@ -175,11 +163,23 @@ mapbucket(Type *t)
|
||||
valuesfield->sym->name = "values";
|
||||
offset += BUCKETSIZE * valtype->width;
|
||||
|
||||
overflowfield = typ(TFIELD);
|
||||
overflowfield->type = ptrto(bucket);
|
||||
overflowfield->width = offset; // "width" is offset in structure
|
||||
overflowfield->sym = mal(sizeof(Sym)); // not important but needs to be set to give this type a name
|
||||
overflowfield->sym->name = "overflow";
|
||||
offset += widthptr;
|
||||
|
||||
// Pad to the native integer alignment.
|
||||
// This is usually the same as widthptr; the exception (as usual) is nacl/amd64.
|
||||
if(widthreg > widthptr)
|
||||
offset += widthreg - widthptr;
|
||||
|
||||
// link up fields
|
||||
bucket->type = overflowfield;
|
||||
overflowfield->down = keysfield;
|
||||
bucket->type = keysfield;
|
||||
keysfield->down = valuesfield;
|
||||
valuesfield->down = T;
|
||||
valuesfield->down = overflowfield;
|
||||
overflowfield->down = T;
|
||||
|
||||
bucket->width = offset;
|
||||
bucket->local = t->local;
|
||||
|
||||
@@ -2614,7 +2614,16 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface)
|
||||
fn->dupok = 1;
|
||||
typecheck(&fn, Etop);
|
||||
typechecklist(fn->nbody, Etop);
|
||||
|
||||
// Set inl_nonlocal to whether we are calling a method on a
|
||||
// type defined in a different package. Checked in inlvar.
|
||||
if(!methodrcvr->local)
|
||||
inl_nonlocal = 1;
|
||||
|
||||
inlcalls(fn);
|
||||
|
||||
inl_nonlocal = 0;
|
||||
|
||||
curfn = nil;
|
||||
funccompile(fn, 0);
|
||||
}
|
||||
|
||||
@@ -1335,7 +1335,7 @@ reswitch:
|
||||
goto error;
|
||||
|
||||
// Unpack multiple-return result before type-checking.
|
||||
if(istype(t, TSTRUCT)) {
|
||||
if(istype(t, TSTRUCT) && t->funarg) {
|
||||
t = t->type;
|
||||
if(istype(t, TFIELD))
|
||||
t = t->type;
|
||||
|
||||
@@ -234,17 +234,24 @@ create or update Go source files, for instance by running yacc.
|
||||
Go generate is never run automatically by go build, go get, go test,
|
||||
and so on. It must be run explicitly.
|
||||
|
||||
Directives are written as a whole-line comment of the form
|
||||
Go generate scans the file for directives, which are lines of
|
||||
the form,
|
||||
|
||||
//go:generate command argument...
|
||||
|
||||
(note: no space in "//go") where command is the generator to be
|
||||
run, corresponding to an executable file that can be run locally.
|
||||
It must either be in the shell path (gofmt), a fully qualified path
|
||||
(/usr/you/bin/mytool), or a command alias, described below.
|
||||
(note: no leading spaces and no space in "//go") where command
|
||||
is the generator to be run, corresponding to an executable file
|
||||
that can be run locally. It must either be in the shell path
|
||||
(gofmt), a fully qualified path (/usr/you/bin/mytool), or a
|
||||
command alias, described below.
|
||||
|
||||
The arguments are space-separated tokens or double-quoted strings
|
||||
passed to the generator as individual arguments when it is run.
|
||||
Note that go generate does not parse the file, so lines that look
|
||||
like directives in comments or multiline strings will be treated
|
||||
as directives.
|
||||
|
||||
The arguments to the directive are space-separated tokens or
|
||||
double-quoted strings passed to the generator as individual
|
||||
arguments when it is run.
|
||||
|
||||
Quoted strings use Go syntax and are evaluated before execution; a
|
||||
quoted string appears as a single argument to the generator.
|
||||
@@ -301,6 +308,7 @@ The generator is run in the package's source directory.
|
||||
Go generate accepts one specific flag:
|
||||
|
||||
-run=""
|
||||
TODO: This flag is unimplemented.
|
||||
if non-empty, specifies a regular expression to
|
||||
select directives whose command matches the expression.
|
||||
|
||||
@@ -317,7 +325,7 @@ Download and install packages and dependencies
|
||||
|
||||
Usage:
|
||||
|
||||
go get [-d] [-fix] [-t] [-u] [build flags] [packages]
|
||||
go get [-d] [-f] [-fix] [-t] [-u] [build flags] [packages]
|
||||
|
||||
Get downloads and installs the packages named by the import paths,
|
||||
along with their dependencies.
|
||||
@@ -325,6 +333,11 @@ along with their dependencies.
|
||||
The -d flag instructs get to stop after downloading the packages; that is,
|
||||
it instructs get not to install the packages.
|
||||
|
||||
The -f flag, valid only when -u is set, forces get -u not to verify that
|
||||
each package has been checked out from the source control repository
|
||||
implied by its import path. This can be useful if the source is a local fork
|
||||
of the original.
|
||||
|
||||
The -fix flag instructs get to run the fix tool on the downloaded packages
|
||||
before resolving dependencies or building the code.
|
||||
|
||||
@@ -863,7 +876,26 @@ listed in the GOPATH environment variable (see 'go help gopath').
|
||||
|
||||
The go command attempts to download the version of the
|
||||
package appropriate for the Go release being used.
|
||||
Run 'go help install' for more.
|
||||
Run 'go help get' for more.
|
||||
|
||||
Import path checking
|
||||
|
||||
When the custom import path feature described above redirects to a
|
||||
known code hosting site, each of the resulting packages has two possible
|
||||
import paths, using the custom domain or the known hosting site.
|
||||
|
||||
A package statement is said to have an "import comment" if it is immediately
|
||||
followed (before the next newline) by a comment of one of these two forms:
|
||||
|
||||
package math // import "path"
|
||||
package math /* import "path" * /
|
||||
|
||||
The go command will refuse to install a package with an import comment
|
||||
unless it is being referred to by that import path. In this way, import comments
|
||||
let package authors make sure the custom import path is used and not a
|
||||
direct path to the underlying code hosting site.
|
||||
|
||||
See https://golang.org/s/go14customimport for details.
|
||||
|
||||
|
||||
Description of package lists
|
||||
|
||||
@@ -32,20 +32,27 @@ create or update Go source files, for instance by running yacc.
|
||||
Go generate is never run automatically by go build, go get, go test,
|
||||
and so on. It must be run explicitly.
|
||||
|
||||
Directives are written as a whole-line comment of the form
|
||||
Go generate scans the file for directives, which are lines of
|
||||
the form,
|
||||
|
||||
//go:generate command argument...
|
||||
|
||||
(note: no space in "//go") where command is the generator to be
|
||||
run, corresponding to an executable file that can be run locally.
|
||||
It must either be in the shell path (gofmt), a fully qualified path
|
||||
(/usr/you/bin/mytool), or a command alias, described below.
|
||||
(note: no leading spaces and no space in "//go") where command
|
||||
is the generator to be run, corresponding to an executable file
|
||||
that can be run locally. It must either be in the shell path
|
||||
(gofmt), a fully qualified path (/usr/you/bin/mytool), or a
|
||||
command alias, described below.
|
||||
|
||||
The arguments are space-separated tokens or double-quoted strings
|
||||
passed to the generator as individual arguments when it is run.
|
||||
Note that go generate does not parse the file, so lines that look
|
||||
like directives in comments or multiline strings will be treated
|
||||
as directives.
|
||||
|
||||
The arguments to the directive are space-separated tokens or
|
||||
double-quoted strings passed to the generator as individual
|
||||
arguments when it is run.
|
||||
|
||||
Quoted strings use Go syntax and are evaluated before execution; a
|
||||
quoted string appears a single argument to the generator.
|
||||
quoted string appears as a single argument to the generator.
|
||||
|
||||
Go generate sets several variables when it runs the generator:
|
||||
|
||||
@@ -99,6 +106,7 @@ The generator is run in the package's source directory.
|
||||
Go generate accepts one specific flag:
|
||||
|
||||
-run=""
|
||||
TODO: This flag is unimplemented.
|
||||
if non-empty, specifies a regular expression to
|
||||
select directives whose command matches the expression.
|
||||
|
||||
@@ -178,13 +186,43 @@ func (g *Generator) run() (ok bool) {
|
||||
fmt.Fprintf(os.Stderr, "%s\n", shortPath(g.path))
|
||||
}
|
||||
|
||||
s := bufio.NewScanner(g.r)
|
||||
for s.Scan() {
|
||||
g.lineNum++
|
||||
if !bytes.HasPrefix(s.Bytes(), []byte("//go:generate ")) && !bytes.HasPrefix(s.Bytes(), []byte("//go:generate\t")) {
|
||||
// Scan for lines that start "//go:generate".
|
||||
// Can't use bufio.Scanner because it can't handle long lines,
|
||||
// which are likely to appear when using generate.
|
||||
input := bufio.NewReader(g.r)
|
||||
var err error
|
||||
// One line per loop.
|
||||
for {
|
||||
g.lineNum++ // 1-indexed.
|
||||
var buf []byte
|
||||
buf, err = input.ReadSlice('\n')
|
||||
if err == bufio.ErrBufferFull {
|
||||
// Line too long - consume and ignore.
|
||||
if isGoGenerate(buf) {
|
||||
g.errorf("directive too long")
|
||||
}
|
||||
for err == bufio.ErrBufferFull {
|
||||
_, err = input.ReadSlice('\n')
|
||||
}
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
words := g.split(s.Text())
|
||||
|
||||
if err != nil {
|
||||
// Check for marker at EOF without final \n.
|
||||
if err == io.EOF && isGoGenerate(buf) {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if !isGoGenerate(buf) {
|
||||
continue
|
||||
}
|
||||
|
||||
words := g.split(string(buf))
|
||||
if len(words) == 0 {
|
||||
g.errorf("no arguments to directive")
|
||||
}
|
||||
@@ -201,19 +239,27 @@ func (g *Generator) run() (ok bool) {
|
||||
}
|
||||
g.exec(words)
|
||||
}
|
||||
if s.Err() != nil {
|
||||
g.errorf("error reading %s: %s", shortPath(g.path), s.Err())
|
||||
if err != nil && err != io.EOF {
|
||||
g.errorf("error reading %s: %s", shortPath(g.path), err)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isGoGenerate(buf []byte) bool {
|
||||
return bytes.HasPrefix(buf, []byte("//go:generate ")) || bytes.HasPrefix(buf, []byte("//go:generate\t"))
|
||||
}
|
||||
|
||||
// split breaks the line into words, evaluating quoted
|
||||
// strings and evaluating environment variables.
|
||||
// The initial //go:generate element is dropped.
|
||||
// The initial //go:generate element is present in line.
|
||||
func (g *Generator) split(line string) []string {
|
||||
// Parse line, obeying quoted strings.
|
||||
var words []string
|
||||
line = line[len("//go:generate "):]
|
||||
line = line[len("//go:generate ") : len(line)-1] // Drop preamble and final newline.
|
||||
// There may still be a carriage return.
|
||||
if len(line) > 0 && line[len(line)-1] == '\r' {
|
||||
line = line[:len(line)-1]
|
||||
}
|
||||
// One (possibly quoted) word per iteration.
|
||||
Words:
|
||||
for {
|
||||
|
||||
@@ -40,7 +40,13 @@ func TestGenerateCommandParse(t *testing.T) {
|
||||
}
|
||||
g.setShorthand([]string{"-command", "yacc", "go", "tool", "yacc"})
|
||||
for _, test := range splitTests {
|
||||
got := g.split("//go:generate " + test.in)
|
||||
// First with newlines.
|
||||
got := g.split("//go:generate " + test.in + "\n")
|
||||
if !reflect.DeepEqual(got, test.out) {
|
||||
t.Errorf("split(%q): got %q expected %q", test.in, got, test.out)
|
||||
}
|
||||
// Then with CRLFs, thank you Windows.
|
||||
got = g.split("//go:generate " + test.in + "\r\n")
|
||||
if !reflect.DeepEqual(got, test.out) {
|
||||
t.Errorf("split(%q): got %q expected %q", test.in, got, test.out)
|
||||
}
|
||||
|
||||
@@ -290,7 +290,7 @@ func downloadPackage(p *Package) error {
|
||||
}
|
||||
}
|
||||
if remote != repo {
|
||||
return fmt.Errorf("%s is from %s, should be from %s", dir, remote, repo)
|
||||
return fmt.Errorf("%s is a custom import path for %s, but %s is checked out from %s", rr.root, repo, dir, remote)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,7 +235,26 @@ listed in the GOPATH environment variable (see 'go help gopath').
|
||||
|
||||
The go command attempts to download the version of the
|
||||
package appropriate for the Go release being used.
|
||||
Run 'go help install' for more.
|
||||
Run 'go help get' for more.
|
||||
|
||||
Import path checking
|
||||
|
||||
When the custom import path feature described above redirects to a
|
||||
known code hosting site, each of the resulting packages has two possible
|
||||
import paths, using the custom domain or the known hosting site.
|
||||
|
||||
A package statement is said to have an "import comment" if it is immediately
|
||||
followed (before the next newline) by a comment of one of these two forms:
|
||||
|
||||
package math // import "path"
|
||||
package math /* import "path" */
|
||||
|
||||
The go command will refuse to install a package with an import comment
|
||||
unless it is being referred to by that import path. In this way, import comments
|
||||
let package authors make sure the custom import path is used and not a
|
||||
direct path to the underlying code hosting site.
|
||||
|
||||
See https://golang.org/s/go14customimport for details.
|
||||
`,
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
go install # So the next line will produce updated documentation.
|
||||
go help documentation > doc.go
|
||||
go help documentation | sed 's; \*/; * /;' >doc.go
|
||||
gofmt -w doc.go
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ const toolWindowsExtension = ".exe"
|
||||
|
||||
func tool(toolName string) string {
|
||||
toolPath := filepath.Join(toolDir, toolName)
|
||||
if toolIsWindows && toolName != "pprof" {
|
||||
if toolIsWindows {
|
||||
toolPath += toolWindowsExtension
|
||||
}
|
||||
// Give a nice message if there is no tool with that name.
|
||||
@@ -91,16 +91,6 @@ func runTool(cmd *Command, args []string) {
|
||||
if toolPath == "" {
|
||||
return
|
||||
}
|
||||
if toolIsWindows && toolName == "pprof" {
|
||||
args = append([]string{"perl", toolPath}, args[1:]...)
|
||||
var err error
|
||||
toolPath, err = exec.LookPath("perl")
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "go tool: perl not found\n")
|
||||
setExitStatus(3)
|
||||
return
|
||||
}
|
||||
}
|
||||
if toolN {
|
||||
fmt.Printf("%s %s\n", toolPath, strings.Join(args[1:], " "))
|
||||
return
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
package objfile
|
||||
|
||||
import (
|
||||
"debug/goobj"
|
||||
"cmd/internal/goobj"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
@@ -1281,12 +1281,19 @@ synthesizemaptypes(DWDie *die)
|
||||
|
||||
fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "keys");
|
||||
newrefattr(fld, DW_AT_type, dwhk);
|
||||
newmemberoffsetattr(fld, BucketSize + PtrSize);
|
||||
newmemberoffsetattr(fld, BucketSize);
|
||||
fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "values");
|
||||
newrefattr(fld, DW_AT_type, dwhv);
|
||||
newmemberoffsetattr(fld, BucketSize + PtrSize + BucketSize * keysize);
|
||||
newattr(dwhb, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize + PtrSize + BucketSize * keysize + BucketSize * valsize, 0);
|
||||
substitutetype(dwhb, "overflow", defptrto(dwhb));
|
||||
newmemberoffsetattr(fld, BucketSize + BucketSize * keysize);
|
||||
fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "overflow");
|
||||
newrefattr(fld, DW_AT_type, defptrto(dwhb));
|
||||
newmemberoffsetattr(fld, BucketSize + BucketSize * (keysize + valsize));
|
||||
if(RegSize > PtrSize) {
|
||||
fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "pad");
|
||||
newrefattr(fld, DW_AT_type, find_or_diag(&dwtypes, "uintptr"));
|
||||
newmemberoffsetattr(fld, BucketSize + BucketSize * (keysize + valsize) + PtrSize);
|
||||
}
|
||||
newattr(dwhb, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize + BucketSize * keysize + BucketSize * valsize + RegSize, 0);
|
||||
|
||||
// Construct hash<K,V>
|
||||
dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE,
|
||||
|
||||
@@ -539,7 +539,10 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn)
|
||||
s->type = SRODATA;
|
||||
break;
|
||||
case ElfSectFlagAlloc + ElfSectFlagWrite:
|
||||
s->type = SNOPTRDATA;
|
||||
if(sect->type == ElfSectNobits)
|
||||
s->type = SNOPTRBSS;
|
||||
else
|
||||
s->type = SNOPTRDATA;
|
||||
break;
|
||||
case ElfSectFlagAlloc + ElfSectFlagExec:
|
||||
s->type = STEXT;
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Automatic symbol generation.
|
||||
|
||||
// TODO(rsc): Handle go.typelink, go.track symbols.
|
||||
// TODO(rsc): Do not handle $f64. and $f32. symbols. Instead, generate those
|
||||
// from the compiler and assemblers as dupok data, and then remove autoData below.
|
||||
package main
|
||||
|
||||
import (
|
||||
"debug/goobj"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// linkerDefined lists the symbols supplied by other parts of the linker
|
||||
// (runtime.go and layout.go).
|
||||
var linkerDefined = map[string]bool{
|
||||
"runtime.bss": true,
|
||||
"runtime.data": true,
|
||||
"runtime.ebss": true,
|
||||
"runtime.edata": true,
|
||||
"runtime.efunctab": true,
|
||||
"runtime.end": true,
|
||||
"runtime.enoptrbss": true,
|
||||
"runtime.enoptrdata": true,
|
||||
"runtime.erodata": true,
|
||||
"runtime.etext": true,
|
||||
"runtime.etypelink": true,
|
||||
"runtime.functab": true,
|
||||
"runtime.gcbss": true,
|
||||
"runtime.gcdata": true,
|
||||
"runtime.noptrbss": true,
|
||||
"runtime.noptrdata": true,
|
||||
"runtime.pclntab": true,
|
||||
"runtime.rodata": true,
|
||||
"runtime.text": true,
|
||||
"runtime.typelink": true,
|
||||
}
|
||||
|
||||
// isAuto reports whether sym is an automatically-generated data or constant symbol.
|
||||
func (p *Prog) isAuto(sym goobj.SymID) bool {
|
||||
return strings.HasPrefix(sym.Name, "go.weak.") ||
|
||||
strings.HasPrefix(sym.Name, "$f64.") ||
|
||||
strings.HasPrefix(sym.Name, "$f32.") ||
|
||||
linkerDefined[sym.Name]
|
||||
}
|
||||
|
||||
// autoData defines the automatically generated data symbols needed by p.
|
||||
func (p *Prog) autoData() {
|
||||
for sym := range p.Missing {
|
||||
switch {
|
||||
// Floating-point constants that need to be loaded from memory are
|
||||
// written as $f64.{16 hex digits} or $f32.{8 hex digits}; the hex digits
|
||||
// give the IEEE bit pattern of the constant. As far as the layout into
|
||||
// memory is concerned, we interpret these as uint64 or uint32 constants.
|
||||
case strings.HasPrefix(sym.Name, "$f64."), strings.HasPrefix(sym.Name, "$f32."):
|
||||
size := 64
|
||||
if sym.Name[2:4] == "32" {
|
||||
size = 32
|
||||
}
|
||||
delete(p.Missing, sym)
|
||||
fbits, err := strconv.ParseUint(sym.Name[len("$f64."):], 16, size)
|
||||
if err != nil {
|
||||
p.errorf("unexpected floating point symbol %s", sym)
|
||||
continue
|
||||
}
|
||||
data := make([]byte, size/8)
|
||||
if size == 64 {
|
||||
p.byteorder.PutUint64(data, fbits)
|
||||
} else {
|
||||
p.byteorder.PutUint32(data, uint32(fbits))
|
||||
}
|
||||
p.addSym(&Sym{
|
||||
Sym: &goobj.Sym{
|
||||
SymID: sym,
|
||||
Kind: goobj.SRODATA,
|
||||
Size: size / 8,
|
||||
},
|
||||
Bytes: data,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// autoConst defines the automatically generated constant symbols needed by p.
|
||||
func (p *Prog) autoConst() {
|
||||
for sym := range p.Missing {
|
||||
switch {
|
||||
case strings.HasPrefix(sym.Name, "go.weak."):
|
||||
// weak symbol resolves to actual symbol if present, or else nil.
|
||||
delete(p.Missing, sym)
|
||||
targ := sym
|
||||
targ.Name = sym.Name[len("go.weak."):]
|
||||
var addr Addr
|
||||
if s := p.Syms[targ]; s != nil {
|
||||
addr = s.Addr
|
||||
}
|
||||
p.defineConst(sym.Name, addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// defineConst defines a new symbol with the given name and constant address.
|
||||
func (p *Prog) defineConst(name string, addr Addr) {
|
||||
sym := goobj.SymID{Name: name}
|
||||
p.addSym(&Sym{
|
||||
Sym: &goobj.Sym{
|
||||
SymID: sym,
|
||||
Kind: goobj.SCONST,
|
||||
},
|
||||
Package: nil,
|
||||
Addr: addr,
|
||||
})
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Test for auto-generated symbols.
|
||||
|
||||
// There is no test for $f64. and $f32. symbols, because those are
|
||||
// not possible to write in the assembler syntax. Instead of changing
|
||||
// the assembler to allow that, we plan to change the compilers
|
||||
// not to generate such symbols (plain dupok data is sufficient).
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/goobj"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Each test case is an object file, generated from a corresponding .s file.
|
||||
// The image of the autotab symbol should be a sequence of pairs of
|
||||
// identical 8-byte sequences.
|
||||
var autoTests = []string{
|
||||
"testdata/autosection.6",
|
||||
"testdata/autoweak.6",
|
||||
}
|
||||
|
||||
func TestAuto(t *testing.T) {
|
||||
for _, obj := range autoTests {
|
||||
p := Prog{GOOS: "darwin", GOARCH: "amd64", StartSym: "start"}
|
||||
p.omitRuntime = true
|
||||
p.Error = func(s string) { t.Error(s) }
|
||||
var buf bytes.Buffer
|
||||
p.link(&buf, obj)
|
||||
if p.NumError > 0 {
|
||||
continue // already reported
|
||||
}
|
||||
|
||||
const name = "autotab"
|
||||
sym := p.Syms[goobj.SymID{Name: name}]
|
||||
if sym == nil {
|
||||
t.Errorf("%s is missing %s symbol", obj, name)
|
||||
return
|
||||
}
|
||||
if sym.Size == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
seg := sym.Section.Segment
|
||||
off := sym.Addr - seg.VirtAddr
|
||||
data := seg.Data[off : off+Addr(sym.Size)]
|
||||
if len(data)%16 != 0 {
|
||||
t.Errorf("%s: %s.Size = %d, want multiple of 16", obj, name, len(data))
|
||||
return
|
||||
}
|
||||
Data:
|
||||
for i := 0; i < len(data); i += 16 {
|
||||
have := p.byteorder.Uint64(data[i : i+8])
|
||||
want := p.byteorder.Uint64(data[i+8 : i+16])
|
||||
if have != want {
|
||||
// Look for relocation so we can explain what went wrong.
|
||||
for _, r := range sym.Reloc {
|
||||
if r.Offset == i {
|
||||
t.Errorf("%s: %s+%#x: %s: have %#x want %#x", obj, name, i, r.Sym, have, want)
|
||||
continue Data
|
||||
}
|
||||
}
|
||||
t.Errorf("%s: %s+%#x: have %#x want %#x", obj, name, i, have, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Removal of dead code and data.
|
||||
|
||||
package main
|
||||
|
||||
import "debug/goobj"
|
||||
|
||||
// dead removes unreachable code and data from the program.
|
||||
// It is basically a mark-sweep garbage collection: traverse all the
|
||||
// symbols reachable from the entry (startSymID) and then delete
|
||||
// the rest.
|
||||
func (p *Prog) dead() {
|
||||
p.Dead = make(map[goobj.SymID]bool)
|
||||
reachable := make(map[goobj.SymID]bool)
|
||||
p.walkDead(p.startSym, reachable)
|
||||
|
||||
for sym := range p.Syms {
|
||||
if !reachable[sym] {
|
||||
delete(p.Syms, sym)
|
||||
p.Dead[sym] = true
|
||||
}
|
||||
}
|
||||
|
||||
for sym := range p.Missing {
|
||||
if !reachable[sym] {
|
||||
delete(p.Missing, sym)
|
||||
p.Dead[sym] = true
|
||||
}
|
||||
}
|
||||
|
||||
p.SymOrder = removeDead(p.SymOrder, reachable)
|
||||
|
||||
for _, pkg := range p.Packages {
|
||||
pkg.Syms = removeDead(pkg.Syms, reachable)
|
||||
}
|
||||
}
|
||||
|
||||
// walkDead traverses the symbols reachable from sym, adding them to reachable.
|
||||
// The caller has verified that reachable[sym] = false.
|
||||
func (p *Prog) walkDead(sym goobj.SymID, reachable map[goobj.SymID]bool) {
|
||||
reachable[sym] = true
|
||||
s := p.Syms[sym]
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
for i := range s.Reloc {
|
||||
r := &s.Reloc[i]
|
||||
if !reachable[r.Sym] {
|
||||
p.walkDead(r.Sym, reachable)
|
||||
}
|
||||
}
|
||||
if s.Func != nil {
|
||||
for _, fdata := range s.Func.FuncData {
|
||||
if fdata.Sym.Name != "" && !reachable[fdata.Sym] {
|
||||
p.walkDead(fdata.Sym, reachable)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// removeDead removes unreachable (dead) symbols from syms,
|
||||
// returning a shortened slice using the same underlying array.
|
||||
func removeDead(syms []*Sym, reachable map[goobj.SymID]bool) []*Sym {
|
||||
keep := syms[:0]
|
||||
for _, sym := range syms {
|
||||
if reachable[sym.SymID] {
|
||||
keep = append(keep, sym)
|
||||
}
|
||||
}
|
||||
return keep
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"debug/goobj"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Each test case is an object file, generated from a corresponding .s file.
|
||||
// The symbols in the object file with a dead_ prefix are the ones that
|
||||
// should be removed from the program.
|
||||
var deadTests = []string{
|
||||
"testdata/dead.6",
|
||||
}
|
||||
|
||||
func TestDead(t *testing.T) {
|
||||
for _, obj := range deadTests {
|
||||
p := Prog{GOOS: "darwin", GOARCH: "amd64", StartSym: "start"}
|
||||
p.omitRuntime = true
|
||||
p.Error = func(s string) { t.Error(s) }
|
||||
p.init()
|
||||
p.scan(obj)
|
||||
if p.NumError > 0 {
|
||||
continue // already reported
|
||||
}
|
||||
origSyms := copyMap(p.Syms)
|
||||
origMissing := copyMap(p.Missing)
|
||||
origSymOrder := copySlice(p.SymOrder)
|
||||
origPkgSyms := copySlice(p.Packages["main"].Syms)
|
||||
p.dead()
|
||||
checkDeadMap(t, obj, "p.Syms", origSyms, p.Syms)
|
||||
checkDeadMap(t, obj, "p.Missing", origMissing, p.Missing)
|
||||
checkDeadSlice(t, obj, "p.SymOrder", origSymOrder, p.SymOrder)
|
||||
checkDeadSlice(t, obj, `p.Packages["main"].Syms`, origPkgSyms, p.Packages["main"].Syms)
|
||||
}
|
||||
}
|
||||
|
||||
func copyMap(m interface{}) interface{} {
|
||||
v := reflect.ValueOf(m)
|
||||
out := reflect.MakeMap(v.Type())
|
||||
for _, key := range v.MapKeys() {
|
||||
out.SetMapIndex(key, v.MapIndex(key))
|
||||
}
|
||||
return out.Interface()
|
||||
}
|
||||
|
||||
func checkDeadMap(t *testing.T, obj, name string, old, new interface{}) {
|
||||
vold := reflect.ValueOf(old)
|
||||
vnew := reflect.ValueOf(new)
|
||||
for _, vid := range vold.MapKeys() {
|
||||
id := vid.Interface().(goobj.SymID)
|
||||
if strings.HasPrefix(id.Name, "dead_") {
|
||||
if vnew.MapIndex(vid).IsValid() {
|
||||
t.Errorf("%s: %s contains unnecessary symbol %s", obj, name, id)
|
||||
}
|
||||
} else {
|
||||
if !vnew.MapIndex(vid).IsValid() {
|
||||
t.Errorf("%s: %s is missing symbol %s", obj, name, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, vid := range vnew.MapKeys() {
|
||||
id := vid.Interface().(goobj.SymID)
|
||||
if !vold.MapIndex(vid).IsValid() {
|
||||
t.Errorf("%s: %s contains unexpected symbol %s", obj, name, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func copySlice(x []*Sym) (out []*Sym) {
|
||||
return append(out, x...)
|
||||
}
|
||||
|
||||
func checkDeadSlice(t *testing.T, obj, name string, old, new []*Sym) {
|
||||
for i, s := range old {
|
||||
if strings.HasPrefix(s.Name, "dead_") {
|
||||
continue
|
||||
}
|
||||
if len(new) == 0 {
|
||||
t.Errorf("%s: %s is missing symbol %s\nhave%v\nwant%v", obj, name, s, new, old[i:])
|
||||
return
|
||||
}
|
||||
if new[0].SymID != s.SymID {
|
||||
t.Errorf("%s: %s is incorrect: have %s, want %s\nhave%v\nwant%v", obj, name, new[0].SymID, s.SymID, new, old[i:])
|
||||
return
|
||||
}
|
||||
new = new[1:]
|
||||
}
|
||||
if len(new) > 0 {
|
||||
t.Errorf("%s: %s has unexpected symbols: %v", obj, name, new)
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Generation of debug data structures (in the executable but not mapped at run time).
|
||||
// See also runtime.go.
|
||||
|
||||
package main
|
||||
|
||||
func (p *Prog) debug() {
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// mustParseHexdumpFile returns a block of data generated by
|
||||
// parsing the hex dump in the named file.
|
||||
// If the file cannot be read or does not contain a valid hex dump,
|
||||
// mustParseHexdumpFile calls t.Fatal.
|
||||
func mustParseHexdumpFile(t *testing.T, file string) []byte {
|
||||
hex, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data, err := parseHexdump(string(hex))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// parseHexdump parses the hex dump in text, which should be the
|
||||
// output of "hexdump -C" or Plan 9's "xd -b",
|
||||
// and returns the original data used to produce the dump.
|
||||
// It is meant to enable storing golden binary files as text, so that
|
||||
// changes to the golden files can be seen during code reviews.
|
||||
func parseHexdump(text string) ([]byte, error) {
|
||||
var out []byte
|
||||
for _, line := range strings.Split(text, "\n") {
|
||||
if i := strings.Index(line, "|"); i >= 0 { // remove text dump
|
||||
line = line[:i]
|
||||
}
|
||||
f := strings.Fields(line)
|
||||
if len(f) > 1+16 {
|
||||
return nil, fmt.Errorf("parsing hex dump: too many fields on line %q", line)
|
||||
}
|
||||
if len(f) == 0 || len(f) == 1 && f[0] == "*" { // all zeros block omitted
|
||||
continue
|
||||
}
|
||||
addr64, err := strconv.ParseUint(f[0], 16, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing hex dump: invalid address %q", f[0])
|
||||
}
|
||||
addr := int(addr64)
|
||||
if len(out) < addr {
|
||||
out = append(out, make([]byte, addr-len(out))...)
|
||||
}
|
||||
for _, x := range f[1:] {
|
||||
val, err := strconv.ParseUint(x, 16, 8)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing hexdump: invalid hex byte %q", x)
|
||||
}
|
||||
out = append(out, byte(val))
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func hexdump(data []byte) string {
|
||||
text := hex.Dump(data) + fmt.Sprintf("%08x\n", len(data))
|
||||
text = regexp.MustCompile(`\n([0-9a-f]+(\s+00){16}.*\n)+`).ReplaceAllString(text, "\n*\n")
|
||||
return text
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Executable image layout - address assignment.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"debug/goobj"
|
||||
)
|
||||
|
||||
// A layoutSection describes a single section to add to the
|
||||
// final executable. Go binaries only have a fixed set of possible
|
||||
// sections, and the symbol kind determines the section.
|
||||
type layoutSection struct {
|
||||
Segment string
|
||||
Section string
|
||||
Kind goobj.SymKind
|
||||
Index int
|
||||
}
|
||||
|
||||
// layout defines the layout of the generated Go executable.
|
||||
// The order of entries here is the order in the executable.
|
||||
// Entries with the same Segment name must be contiguous.
|
||||
var layout = []layoutSection{
|
||||
{Segment: "text", Section: "text", Kind: goobj.STEXT},
|
||||
{Segment: "rodata", Section: "rodata", Kind: goobj.SRODATA},
|
||||
{Segment: "rodata", Section: "functab", Kind: goobj.SPCLNTAB},
|
||||
{Segment: "rodata", Section: "typelink", Kind: goobj.STYPELINK},
|
||||
{Segment: "data", Section: "noptrdata", Kind: goobj.SNOPTRDATA},
|
||||
{Segment: "data", Section: "data", Kind: goobj.SDATA},
|
||||
{Segment: "data", Section: "bss", Kind: goobj.SBSS},
|
||||
{Segment: "data", Section: "noptrbss", Kind: goobj.SNOPTRBSS},
|
||||
|
||||
// Later:
|
||||
// {"rodata", "type", goobj.STYPE},
|
||||
// {"rodata", "string", goobj.SSTRING},
|
||||
// {"rodata", "gostring", goobj.SGOSTRING},
|
||||
// {"rodata", "gofunc", goobj.SGOFUNC},
|
||||
}
|
||||
|
||||
// layoutByKind maps from SymKind to an entry in layout.
|
||||
var layoutByKind []*layoutSection
|
||||
|
||||
func init() {
|
||||
// Build index from symbol type to layout entry.
|
||||
max := 0
|
||||
for _, sect := range layout {
|
||||
if max <= int(sect.Kind) {
|
||||
max = int(sect.Kind) + 1
|
||||
}
|
||||
}
|
||||
layoutByKind = make([]*layoutSection, max)
|
||||
for i := range layout {
|
||||
sect := &layout[i]
|
||||
layoutByKind[sect.Kind] = sect
|
||||
sect.Index = i
|
||||
}
|
||||
}
|
||||
|
||||
// layout arranges symbols into sections and sections into segments,
|
||||
// and then it assigns addresses to segments, sections, and symbols.
|
||||
func (p *Prog) layout() {
|
||||
sections := make([]*Section, len(layout))
|
||||
|
||||
// Assign symbols to sections using index, creating sections as needed.
|
||||
// Could keep sections separated by type during input instead.
|
||||
for _, sym := range p.SymOrder {
|
||||
kind := sym.Kind
|
||||
if kind < 0 || int(kind) >= len(layoutByKind) || layoutByKind[kind] == nil {
|
||||
p.errorf("%s: unexpected symbol kind %v", sym.SymID, kind)
|
||||
continue
|
||||
}
|
||||
lsect := layoutByKind[kind]
|
||||
sect := sections[lsect.Index]
|
||||
if sect == nil {
|
||||
sect = &Section{
|
||||
Name: lsect.Section,
|
||||
Align: 1,
|
||||
}
|
||||
sections[lsect.Index] = sect
|
||||
}
|
||||
if sym.Data.Size > 0 || len(sym.Bytes) > 0 {
|
||||
sect.InFile = true
|
||||
}
|
||||
sym.Section = sect
|
||||
sect.Syms = append(sect.Syms, sym)
|
||||
|
||||
// TODO(rsc): Incorporate alignment information.
|
||||
// First that information needs to be added to the object files.
|
||||
//
|
||||
// if sect.Align < Addr(sym.Align) {
|
||||
// sect.Align = Addr(sym.Align)
|
||||
// }
|
||||
}
|
||||
|
||||
// Assign sections to segments, creating segments as needed.
|
||||
var seg *Segment
|
||||
for i, sect := range sections {
|
||||
if sect == nil {
|
||||
continue
|
||||
}
|
||||
segName := layout[i].Segment
|
||||
|
||||
// Special case: Mach-O does not support "rodata" segment,
|
||||
// so store read-only data in text segment.
|
||||
if p.GOOS == "darwin" && segName == "rodata" {
|
||||
segName = "text"
|
||||
}
|
||||
|
||||
if seg == nil || seg.Name != segName {
|
||||
seg = &Segment{
|
||||
Name: segName,
|
||||
}
|
||||
p.Segments = append(p.Segments, seg)
|
||||
}
|
||||
sect.Segment = seg
|
||||
seg.Sections = append(seg.Sections, sect)
|
||||
}
|
||||
|
||||
// Assign addresses.
|
||||
|
||||
// TODO(rsc): This choice needs to be informed by both
|
||||
// the formatter and the target architecture.
|
||||
// And maybe eventually a command line flag (sigh).
|
||||
const segAlign = 4096
|
||||
|
||||
// TODO(rsc): Use a larger amount on most systems, which will let the
|
||||
// compiler eliminate more nil checks.
|
||||
if p.UnmappedSize == 0 {
|
||||
p.UnmappedSize = segAlign
|
||||
}
|
||||
|
||||
// TODO(rsc): addr := Addr(0) when generating a shared library or PIE.
|
||||
addr := p.UnmappedSize
|
||||
|
||||
// Account for initial file header.
|
||||
hdrVirt, hdrFile := p.formatter.headerSize(p)
|
||||
addr += hdrVirt
|
||||
|
||||
// Assign addresses to segments, sections, symbols.
|
||||
// Assign sizes to segments, sections.
|
||||
startVirt := addr
|
||||
startFile := hdrFile
|
||||
for _, seg := range p.Segments {
|
||||
addr = round(addr, segAlign)
|
||||
seg.VirtAddr = addr
|
||||
seg.FileOffset = startFile + seg.VirtAddr - startVirt
|
||||
for _, sect := range seg.Sections {
|
||||
addr = round(addr, sect.Align)
|
||||
sect.VirtAddr = addr
|
||||
for _, sym := range sect.Syms {
|
||||
// TODO(rsc): Respect alignment once we have that information.
|
||||
sym.Addr = addr
|
||||
addr += Addr(sym.Size)
|
||||
}
|
||||
sect.Size = addr - sect.VirtAddr
|
||||
if sect.InFile {
|
||||
seg.FileSize = addr - seg.VirtAddr
|
||||
}
|
||||
}
|
||||
seg.VirtSize = addr - seg.VirtAddr
|
||||
}
|
||||
|
||||
// Define symbols for section names.
|
||||
var progEnd Addr
|
||||
for i, sect := range sections {
|
||||
name := layout[i].Section
|
||||
var start, end Addr
|
||||
if sect != nil {
|
||||
start = sect.VirtAddr
|
||||
end = sect.VirtAddr + sect.Size
|
||||
}
|
||||
p.defineConst("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"
|
||||
"debug/goobj"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLinkHello(t *testing.T) {
|
||||
p := &Prog{
|
||||
GOOS: "darwin",
|
||||
GOARCH: "amd64",
|
||||
Error: func(s string) { t.Error(s) },
|
||||
StartSym: "_rt0_go",
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
p.link(&buf, "testdata/hello.6")
|
||||
if p.NumError > 0 {
|
||||
return
|
||||
}
|
||||
if p.Syms[goobj.SymID{"_rt0_go", 0}] == nil || p.Syms[goobj.SymID{"hello", 1}] == nil {
|
||||
t.Errorf("Syms = %v, want at least [_rt0_go hello<1>]", p.Syms)
|
||||
}
|
||||
|
||||
// uncomment to leave file behind for execution:
|
||||
if false {
|
||||
ioutil.WriteFile("a.out", buf.Bytes(), 0777)
|
||||
}
|
||||
checkGolden(t, buf.Bytes(), "testdata/link.hello.darwin.amd64")
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Loading of code and data fragments from package files into final image.
|
||||
|
||||
package main
|
||||
|
||||
import "os"
|
||||
|
||||
// load allocates segment images, populates them with data
|
||||
// read from package files, and applies relocations to the data.
|
||||
func (p *Prog) load() {
|
||||
// TODO(rsc): mmap the output file and store the data directly.
|
||||
// That will make writing the output file more efficient.
|
||||
for _, seg := range p.Segments {
|
||||
seg.Data = make([]byte, seg.FileSize)
|
||||
}
|
||||
for _, pkg := range p.Packages {
|
||||
p.loadPackage(pkg)
|
||||
}
|
||||
}
|
||||
|
||||
// loadPackage loads and relocates data for all the
|
||||
// symbols needed in the given package.
|
||||
func (p *Prog) loadPackage(pkg *Package) {
|
||||
if pkg.File == "" {
|
||||
// This "package" contains internally generated symbols only.
|
||||
// All such symbols have a sym.Bytes field holding the actual data
|
||||
// (if any), plus relocations.
|
||||
for _, sym := range pkg.Syms {
|
||||
if sym.Bytes == nil {
|
||||
continue
|
||||
}
|
||||
seg := sym.Section.Segment
|
||||
off := sym.Addr - seg.VirtAddr
|
||||
data := seg.Data[off : off+Addr(sym.Size)]
|
||||
copy(data, sym.Bytes)
|
||||
p.relocateSym(sym, data)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Package stored in file.
|
||||
f, err := os.Open(pkg.File)
|
||||
if err != nil {
|
||||
p.errorf("%v", err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// TODO(rsc): Mmap file into memory.
|
||||
|
||||
for _, sym := range pkg.Syms {
|
||||
if sym.Data.Size == 0 {
|
||||
continue
|
||||
}
|
||||
// TODO(rsc): If not using mmap, at least coalesce nearby reads.
|
||||
if sym.Section == nil {
|
||||
p.errorf("internal error: missing section for %s", sym.Name)
|
||||
}
|
||||
seg := sym.Section.Segment
|
||||
off := sym.Addr - seg.VirtAddr
|
||||
if off >= Addr(len(seg.Data)) || off+Addr(sym.Data.Size) > Addr(len(seg.Data)) {
|
||||
p.errorf("internal error: allocated space for %s too small: %d bytes for %d+%d (%d)", sym, len(seg.Data), off, sym.Data.Size, sym.Size)
|
||||
}
|
||||
data := seg.Data[off : off+Addr(sym.Data.Size)]
|
||||
_, err := f.ReadAt(data, sym.Data.Offset)
|
||||
if err != nil {
|
||||
p.errorf("reading %v: %v", sym.SymID, err)
|
||||
}
|
||||
p.relocateSym(sym, data)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(rsc): Define full enumeration for relocation types.
|
||||
const (
|
||||
R_ADDR = 1
|
||||
R_SIZE = 2
|
||||
R_CALL = 3
|
||||
R_CALLARM = 4
|
||||
R_CALLIND = 5
|
||||
R_CONST = 6
|
||||
R_PCREL = 7
|
||||
)
|
||||
|
||||
// relocateSym applies relocations to sym's data.
|
||||
func (p *Prog) relocateSym(sym *Sym, data []byte) {
|
||||
for i := range sym.Reloc {
|
||||
r := &sym.Reloc[i]
|
||||
targ := p.Syms[r.Sym]
|
||||
if targ == nil {
|
||||
p.errorf("%v: reference to undefined symbol %v", sym, r.Sym)
|
||||
continue
|
||||
}
|
||||
val := targ.Addr + Addr(r.Add)
|
||||
switch r.Type {
|
||||
default:
|
||||
p.errorf("%v: unknown relocation type %d", sym, r.Type)
|
||||
case R_ADDR, R_CALLIND:
|
||||
// ok
|
||||
case R_PCREL, R_CALL:
|
||||
val -= sym.Addr + Addr(r.Offset+r.Size)
|
||||
}
|
||||
frag := data[r.Offset : r.Offset+r.Size]
|
||||
switch r.Size {
|
||||
default:
|
||||
p.errorf("%v: unknown relocation size %d", sym, r.Size)
|
||||
case 4:
|
||||
// TODO(rsc): Check for overflow?
|
||||
p.byteorder.PutUint32(frag, uint32(val))
|
||||
case 8:
|
||||
p.byteorder.PutUint64(frag, uint64(val))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,380 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Mach-O (Darwin) object file writing.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"debug/macho"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// machoFormat is the implementation of formatter.
|
||||
type machoFormat struct{}
|
||||
|
||||
// machoHeader and friends are data structures
|
||||
// corresponding to the Mach-O file header
|
||||
// to be written to disk.
|
||||
|
||||
const (
|
||||
macho64Bit = 1 << 24
|
||||
machoSubCPU386 = 3
|
||||
)
|
||||
|
||||
// machoArch describes a Mach-O target architecture.
|
||||
type machoArch struct {
|
||||
CPU uint32
|
||||
SubCPU uint32
|
||||
}
|
||||
|
||||
// machoHeader is the Mach-O file header.
|
||||
type machoHeader struct {
|
||||
machoArch
|
||||
FileType uint32
|
||||
Loads []*machoLoad
|
||||
Segments []*machoSegment
|
||||
p *Prog // for reporting errors
|
||||
}
|
||||
|
||||
// machoLoad is a Mach-O load command.
|
||||
type machoLoad struct {
|
||||
Type uint32
|
||||
Data []uint32
|
||||
}
|
||||
|
||||
// machoSegment is a Mach-O segment.
|
||||
type machoSegment struct {
|
||||
Name string
|
||||
VirtAddr Addr
|
||||
VirtSize Addr
|
||||
FileOffset Addr
|
||||
FileSize Addr
|
||||
Prot1 uint32
|
||||
Prot2 uint32
|
||||
Flags uint32
|
||||
Sections []*machoSection
|
||||
}
|
||||
|
||||
// machoSection is a Mach-O section, inside a segment.
|
||||
type machoSection struct {
|
||||
Name string
|
||||
Segment string
|
||||
Addr Addr
|
||||
Size Addr
|
||||
Offset uint32
|
||||
Align uint32
|
||||
Reloc uint32
|
||||
Nreloc uint32
|
||||
Flags uint32
|
||||
Res1 uint32
|
||||
Res2 uint32
|
||||
}
|
||||
|
||||
// layout positions the segments and sections in p
|
||||
// to make room for the Mach-O file header.
|
||||
// That is, it edits their VirtAddr fields to adjust for the presence
|
||||
// of the Mach-O header at the beginning of the address space.
|
||||
func (machoFormat) headerSize(p *Prog) (virt, file Addr) {
|
||||
var h machoHeader
|
||||
h.init(p)
|
||||
size := Addr(h.size())
|
||||
size = round(size, 4096)
|
||||
p.HeaderSize = size
|
||||
return size, size
|
||||
}
|
||||
|
||||
// write writes p to w as a Mach-O executable.
|
||||
// layout(p) must have already been called,
|
||||
// and the number, sizes, and addresses of the segments
|
||||
// and sections must not have been modified since the call.
|
||||
func (machoFormat) write(w io.Writer, p *Prog) {
|
||||
var h machoHeader
|
||||
h.init(p)
|
||||
off := Addr(0)
|
||||
enc := h.encode()
|
||||
w.Write(enc)
|
||||
off += Addr(len(enc))
|
||||
for _, seg := range p.Segments {
|
||||
if seg.FileOffset < off {
|
||||
h.p.errorf("mach-o error: invalid file offset")
|
||||
}
|
||||
w.Write(make([]byte, int(seg.FileOffset-off)))
|
||||
if seg.FileSize != Addr(len(seg.Data)) {
|
||||
h.p.errorf("mach-o error: invalid file size")
|
||||
}
|
||||
w.Write(seg.Data)
|
||||
off = seg.FileOffset + Addr(len(seg.Data))
|
||||
}
|
||||
}
|
||||
|
||||
// Conversion of Prog to macho data structures.
|
||||
|
||||
// machoArches maps from GOARCH to machoArch.
|
||||
var machoArches = map[string]machoArch{
|
||||
"amd64": {
|
||||
CPU: uint32(macho.CpuAmd64),
|
||||
SubCPU: uint32(machoSubCPU386),
|
||||
},
|
||||
}
|
||||
|
||||
// init initializes the header h to describe p.
|
||||
func (h *machoHeader) init(p *Prog) {
|
||||
h.p = p
|
||||
h.Segments = nil
|
||||
h.Loads = nil
|
||||
var ok bool
|
||||
h.machoArch, ok = machoArches[p.GOARCH]
|
||||
if !ok {
|
||||
p.errorf("mach-o: unknown target GOARCH %q", p.GOARCH)
|
||||
return
|
||||
}
|
||||
h.FileType = uint32(macho.TypeExec)
|
||||
|
||||
mseg := h.addSegment(p, "__PAGEZERO", nil)
|
||||
mseg.VirtSize = p.UnmappedSize
|
||||
|
||||
for _, seg := range p.Segments {
|
||||
h.addSegment(p, "__"+strings.ToUpper(seg.Name), seg)
|
||||
}
|
||||
|
||||
var data []uint32
|
||||
switch h.CPU {
|
||||
default:
|
||||
p.errorf("mach-o: unknown cpu %#x for GOARCH %q", h.CPU, p.GOARCH)
|
||||
case uint32(macho.CpuAmd64):
|
||||
data = make([]uint32, 2+42)
|
||||
data[0] = 4 // thread type
|
||||
data[1] = 42 // word count
|
||||
data[2+32] = uint32(p.Entry) // RIP register, in two parts
|
||||
data[2+32+1] = uint32(p.Entry >> 32)
|
||||
}
|
||||
|
||||
h.Loads = append(h.Loads, &machoLoad{
|
||||
Type: uint32(macho.LoadCmdUnixThread),
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
|
||||
// addSegment adds to h a Mach-O segment like seg with the given name.
|
||||
func (h *machoHeader) addSegment(p *Prog, name string, seg *Segment) *machoSegment {
|
||||
mseg := &machoSegment{
|
||||
Name: name,
|
||||
}
|
||||
h.Segments = append(h.Segments, mseg)
|
||||
if seg == nil {
|
||||
return mseg
|
||||
}
|
||||
|
||||
mseg.VirtAddr = seg.VirtAddr
|
||||
mseg.VirtSize = seg.VirtSize
|
||||
mseg.FileOffset = round(seg.FileOffset, 4096)
|
||||
mseg.FileSize = seg.FileSize
|
||||
|
||||
if name == "__TEXT" {
|
||||
// Initially RWX, then just RX
|
||||
mseg.Prot1 = 7
|
||||
mseg.Prot2 = 5
|
||||
|
||||
// Text segment maps Mach-O header, needed by dynamic linker.
|
||||
mseg.VirtAddr -= p.HeaderSize
|
||||
mseg.VirtSize += p.HeaderSize
|
||||
mseg.FileOffset -= p.HeaderSize
|
||||
mseg.FileSize += p.HeaderSize
|
||||
} else {
|
||||
// RW
|
||||
mseg.Prot1 = 3
|
||||
mseg.Prot2 = 3
|
||||
}
|
||||
|
||||
for _, sect := range seg.Sections {
|
||||
h.addSection(mseg, seg, sect)
|
||||
}
|
||||
return mseg
|
||||
}
|
||||
|
||||
// addSection adds to mseg a Mach-O section like sect, inside seg, with the given name.
|
||||
func (h *machoHeader) addSection(mseg *machoSegment, seg *Segment, sect *Section) {
|
||||
msect := &machoSection{
|
||||
Name: "__" + sect.Name,
|
||||
Segment: mseg.Name,
|
||||
// Reloc: sect.RelocOffset,
|
||||
// NumReloc: sect.RelocLen / 8,
|
||||
Addr: sect.VirtAddr,
|
||||
Size: sect.Size,
|
||||
}
|
||||
mseg.Sections = append(mseg.Sections, msect)
|
||||
|
||||
for 1<<msect.Align < sect.Align {
|
||||
msect.Align++
|
||||
}
|
||||
|
||||
if off := sect.VirtAddr - seg.VirtAddr; off < seg.FileSize {
|
||||
// Data in file.
|
||||
if sect.Size > seg.FileSize-off {
|
||||
h.p.errorf("mach-o error: section crosses file boundary")
|
||||
}
|
||||
msect.Offset = uint32(seg.FileOffset + off)
|
||||
} else {
|
||||
// Zero filled.
|
||||
msect.Flags |= 1
|
||||
}
|
||||
|
||||
if sect.Name == "text" {
|
||||
msect.Flags |= 0x400 // contains executable instructions
|
||||
}
|
||||
}
|
||||
|
||||
// A machoWriter helps write Mach-O headers.
|
||||
// It is basically a buffer with some helper routines for writing integers.
|
||||
type machoWriter struct {
|
||||
dst []byte
|
||||
tmp [8]byte
|
||||
order binary.ByteOrder
|
||||
is64 bool
|
||||
p *Prog
|
||||
}
|
||||
|
||||
// if64 returns x if w is writing a 64-bit object file; otherwise it returns y.
|
||||
func (w *machoWriter) if64(x, y interface{}) interface{} {
|
||||
if w.is64 {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
||||
|
||||
// encode encodes each of the given arguments into the writer.
|
||||
// It encodes uint32, []uint32, uint64, and []uint64 by writing each value
|
||||
// in turn in the correct byte order for the output file.
|
||||
// It encodes an Addr as a uint64 if writing a 64-bit output file, or else as a uint32.
|
||||
// It encodes []byte and string by writing the raw bytes (no length prefix).
|
||||
// It skips nil values in the args list.
|
||||
func (w *machoWriter) encode(args ...interface{}) {
|
||||
for _, arg := range args {
|
||||
switch arg := arg.(type) {
|
||||
default:
|
||||
w.p.errorf("mach-o error: cannot encode %T", arg)
|
||||
case nil:
|
||||
// skip
|
||||
case []byte:
|
||||
w.dst = append(w.dst, arg...)
|
||||
case string:
|
||||
w.dst = append(w.dst, arg...)
|
||||
case uint32:
|
||||
w.order.PutUint32(w.tmp[:], arg)
|
||||
w.dst = append(w.dst, w.tmp[:4]...)
|
||||
case []uint32:
|
||||
for _, x := range arg {
|
||||
w.order.PutUint32(w.tmp[:], x)
|
||||
w.dst = append(w.dst, w.tmp[:4]...)
|
||||
}
|
||||
case uint64:
|
||||
w.order.PutUint64(w.tmp[:], arg)
|
||||
w.dst = append(w.dst, w.tmp[:8]...)
|
||||
case Addr:
|
||||
if w.is64 {
|
||||
w.order.PutUint64(w.tmp[:], uint64(arg))
|
||||
w.dst = append(w.dst, w.tmp[:8]...)
|
||||
} else {
|
||||
if Addr(uint32(arg)) != arg {
|
||||
w.p.errorf("mach-o error: truncating address %#x to uint32", arg)
|
||||
}
|
||||
w.order.PutUint32(w.tmp[:], uint32(arg))
|
||||
w.dst = append(w.dst, w.tmp[:4]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// segmentSize returns the size of the encoding of seg in bytes.
|
||||
func (w *machoWriter) segmentSize(seg *machoSegment) int {
|
||||
if w.is64 {
|
||||
return 18*4 + 20*4*len(seg.Sections)
|
||||
}
|
||||
return 14*4 + 22*4*len(seg.Sections)
|
||||
}
|
||||
|
||||
// zeroPad returns the string s truncated or padded with NULs to n bytes.
|
||||
func zeroPad(s string, n int) string {
|
||||
if len(s) >= n {
|
||||
return s[:n]
|
||||
}
|
||||
return s + strings.Repeat("\x00", n-len(s))
|
||||
}
|
||||
|
||||
// size returns the encoded size of the header.
|
||||
func (h *machoHeader) size() int {
|
||||
// Could write separate code, but encoding is cheap; encode and throw it away.
|
||||
return len(h.encode())
|
||||
}
|
||||
|
||||
// encode returns the Mach-O encoding of the header.
|
||||
func (h *machoHeader) encode() []byte {
|
||||
w := &machoWriter{p: h.p}
|
||||
w.is64 = h.CPU&macho64Bit != 0
|
||||
w.order = w.p.byteorder
|
||||
|
||||
loadSize := 0
|
||||
for _, seg := range h.Segments {
|
||||
loadSize += w.segmentSize(seg)
|
||||
}
|
||||
for _, l := range h.Loads {
|
||||
loadSize += 4 * (2 + len(l.Data))
|
||||
}
|
||||
|
||||
w.encode(
|
||||
w.if64(macho.Magic64, macho.Magic32),
|
||||
uint32(h.CPU),
|
||||
uint32(h.SubCPU),
|
||||
uint32(h.FileType),
|
||||
uint32(len(h.Loads)+len(h.Segments)),
|
||||
uint32(loadSize),
|
||||
uint32(1),
|
||||
w.if64(uint32(0), nil),
|
||||
)
|
||||
|
||||
for _, seg := range h.Segments {
|
||||
w.encode(
|
||||
w.if64(uint32(macho.LoadCmdSegment64), uint32(macho.LoadCmdSegment)),
|
||||
uint32(w.segmentSize(seg)),
|
||||
zeroPad(seg.Name, 16),
|
||||
seg.VirtAddr,
|
||||
seg.VirtSize,
|
||||
seg.FileOffset,
|
||||
seg.FileSize,
|
||||
seg.Prot1,
|
||||
seg.Prot2,
|
||||
uint32(len(seg.Sections)),
|
||||
seg.Flags,
|
||||
)
|
||||
for _, sect := range seg.Sections {
|
||||
w.encode(
|
||||
zeroPad(sect.Name, 16),
|
||||
zeroPad(seg.Name, 16),
|
||||
sect.Addr,
|
||||
sect.Size,
|
||||
sect.Offset,
|
||||
sect.Align,
|
||||
sect.Reloc,
|
||||
sect.Nreloc,
|
||||
sect.Flags,
|
||||
sect.Res1,
|
||||
sect.Res2,
|
||||
w.if64(uint32(0), nil),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for _, load := range h.Loads {
|
||||
w.encode(
|
||||
load.Type,
|
||||
uint32(4*(2+len(load.Data))),
|
||||
load.Data,
|
||||
)
|
||||
}
|
||||
|
||||
return w.dst
|
||||
}
|
||||
@@ -1,407 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/macho"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Test macho writing by checking that each generated prog can be written
|
||||
// and then read back using debug/macho to get the same prog.
|
||||
// Also check against golden testdata file.
|
||||
var machoWriteTests = []struct {
|
||||
name string
|
||||
golden bool
|
||||
prog *Prog
|
||||
}{
|
||||
// amd64 exit 9
|
||||
{
|
||||
name: "exit9",
|
||||
golden: true,
|
||||
prog: &Prog{
|
||||
GOARCH: "amd64",
|
||||
GOOS: "darwin",
|
||||
UnmappedSize: 0x1000,
|
||||
Entry: 0x1000,
|
||||
Segments: []*Segment{
|
||||
{
|
||||
Name: "text",
|
||||
VirtAddr: 0x1000,
|
||||
VirtSize: 13,
|
||||
FileOffset: 0,
|
||||
FileSize: 13,
|
||||
Data: []byte{
|
||||
0xb8, 0x01, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX
|
||||
0xbf, 0x09, 0x00, 0x00, 0x00, // MOVL $9, DI
|
||||
0x0f, 0x05, // SYSCALL
|
||||
0xf4, // HLT
|
||||
},
|
||||
Sections: []*Section{
|
||||
{
|
||||
Name: "text",
|
||||
VirtAddr: 0x1000,
|
||||
Size: 13,
|
||||
Align: 64,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// amd64 write hello world & exit 9
|
||||
{
|
||||
name: "hello",
|
||||
golden: true,
|
||||
prog: &Prog{
|
||||
GOARCH: "amd64",
|
||||
GOOS: "darwin",
|
||||
UnmappedSize: 0x1000,
|
||||
Entry: 0x1000,
|
||||
Segments: []*Segment{
|
||||
{
|
||||
Name: "text",
|
||||
VirtAddr: 0x1000,
|
||||
VirtSize: 35,
|
||||
FileOffset: 0,
|
||||
FileSize: 35,
|
||||
Data: []byte{
|
||||
0xb8, 0x04, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX
|
||||
0xbf, 0x01, 0x00, 0x00, 0x00, // MOVL $1, DI
|
||||
0xbe, 0x00, 0x30, 0x00, 0x00, // MOVL $0x3000, SI
|
||||
0xba, 0x0c, 0x00, 0x00, 0x00, // MOVL $12, DX
|
||||
0x0f, 0x05, // SYSCALL
|
||||
0xb8, 0x01, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX
|
||||
0xbf, 0x09, 0x00, 0x00, 0x00, // MOVL $9, DI
|
||||
0x0f, 0x05, // SYSCALL
|
||||
0xf4, // HLT
|
||||
},
|
||||
Sections: []*Section{
|
||||
{
|
||||
Name: "text",
|
||||
VirtAddr: 0x1000,
|
||||
Size: 35,
|
||||
Align: 64,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "data",
|
||||
VirtAddr: 0x2000,
|
||||
VirtSize: 12,
|
||||
FileOffset: 0x1000,
|
||||
FileSize: 12,
|
||||
Data: []byte("hello world\n"),
|
||||
Sections: []*Section{
|
||||
{
|
||||
Name: "data",
|
||||
VirtAddr: 0x2000,
|
||||
Size: 12,
|
||||
Align: 64,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// amd64 write hello world from rodata & exit 0
|
||||
{
|
||||
name: "helloro",
|
||||
golden: true,
|
||||
prog: &Prog{
|
||||
GOARCH: "amd64",
|
||||
GOOS: "darwin",
|
||||
UnmappedSize: 0x1000,
|
||||
Entry: 0x1000,
|
||||
Segments: []*Segment{
|
||||
{
|
||||
Name: "text",
|
||||
VirtAddr: 0x1000,
|
||||
VirtSize: 0x100c,
|
||||
FileOffset: 0,
|
||||
FileSize: 0x100c,
|
||||
Data: concat(
|
||||
[]byte{
|
||||
0xb8, 0x04, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX
|
||||
0xbf, 0x01, 0x00, 0x00, 0x00, // MOVL $1, DI
|
||||
0xbe, 0x00, 0x30, 0x00, 0x00, // MOVL $0x3000, SI
|
||||
0xba, 0x0c, 0x00, 0x00, 0x00, // MOVL $12, DX
|
||||
0x0f, 0x05, // SYSCALL
|
||||
0xb8, 0x01, 0x00, 0x00, 0x02, // MOVL $0x2000001, AX
|
||||
0xbf, 0x00, 0x00, 0x00, 0x00, // MOVL $0, DI
|
||||
0x0f, 0x05, // SYSCALL
|
||||
0xf4, // HLT
|
||||
},
|
||||
make([]byte, 0x1000-35),
|
||||
[]byte("hello world\n"),
|
||||
),
|
||||
Sections: []*Section{
|
||||
{
|
||||
Name: "text",
|
||||
VirtAddr: 0x1000,
|
||||
Size: 35,
|
||||
Align: 64,
|
||||
},
|
||||
{
|
||||
Name: "rodata",
|
||||
VirtAddr: 0x2000,
|
||||
Size: 12,
|
||||
Align: 64,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func concat(xs ...[]byte) []byte {
|
||||
var out []byte
|
||||
for _, x := range xs {
|
||||
out = append(out, x...)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func TestMachoWrite(t *testing.T) {
|
||||
for _, tt := range machoWriteTests {
|
||||
name := tt.prog.GOARCH + "." + tt.name
|
||||
prog := cloneProg(tt.prog)
|
||||
prog.init()
|
||||
var f machoFormat
|
||||
vsize, fsize := f.headerSize(prog)
|
||||
shiftProg(prog, vsize, fsize)
|
||||
var buf bytes.Buffer
|
||||
f.write(&buf, prog)
|
||||
if false { // enable to debug
|
||||
ioutil.WriteFile("a.out", buf.Bytes(), 0777)
|
||||
}
|
||||
read, err := machoRead(machoArches[tt.prog.GOARCH], buf.Bytes())
|
||||
if err != nil {
|
||||
t.Errorf("%s: reading mach-o output:\n\t%v", name, err)
|
||||
continue
|
||||
}
|
||||
diffs := diffProg(read, prog)
|
||||
if diffs != nil {
|
||||
t.Errorf("%s: mismatched prog:\n\t%s", name, strings.Join(diffs, "\n\t"))
|
||||
continue
|
||||
}
|
||||
if !tt.golden {
|
||||
continue
|
||||
}
|
||||
checkGolden(t, buf.Bytes(), "testdata/macho."+name)
|
||||
}
|
||||
}
|
||||
|
||||
// machoRead reads the mach-o file in data and returns a corresponding prog.
|
||||
func machoRead(arch machoArch, data []byte) (*Prog, error) {
|
||||
f, err := macho.NewFile(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var errors []string
|
||||
errorf := func(format string, args ...interface{}) {
|
||||
errors = append(errors, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
magic := uint32(0xFEEDFACE)
|
||||
if arch.CPU&macho64Bit != 0 {
|
||||
magic |= 1
|
||||
}
|
||||
if f.Magic != magic {
|
||||
errorf("header: Magic = %#x, want %#x", f.Magic, magic)
|
||||
}
|
||||
if f.Cpu != macho.CpuAmd64 {
|
||||
errorf("header: CPU = %#x, want %#x", f.Cpu, macho.CpuAmd64)
|
||||
}
|
||||
if f.SubCpu != 3 {
|
||||
errorf("header: SubCPU = %#x, want %#x", f.SubCpu, 3)
|
||||
}
|
||||
if f.Type != 2 {
|
||||
errorf("header: FileType = %d, want %d", f.Type, 2)
|
||||
}
|
||||
if f.Flags != 1 {
|
||||
errorf("header: Flags = %d, want %d", f.Flags, 1)
|
||||
}
|
||||
|
||||
msects := f.Sections
|
||||
var limit uint64
|
||||
prog := new(Prog)
|
||||
for _, load := range f.Loads {
|
||||
switch load := load.(type) {
|
||||
default:
|
||||
errorf("unexpected macho load %T %x", load, load.Raw())
|
||||
|
||||
case macho.LoadBytes:
|
||||
if len(load) < 8 || len(load)%4 != 0 {
|
||||
errorf("unexpected load length %d", len(load))
|
||||
continue
|
||||
}
|
||||
cmd := f.ByteOrder.Uint32(load)
|
||||
switch macho.LoadCmd(cmd) {
|
||||
default:
|
||||
errorf("unexpected macho load cmd %s", macho.LoadCmd(cmd))
|
||||
case macho.LoadCmdUnixThread:
|
||||
data := make([]uint32, len(load[8:])/4)
|
||||
binary.Read(bytes.NewReader(load[8:]), f.ByteOrder, data)
|
||||
if len(data) != 44 {
|
||||
errorf("macho thread len(data) = %d, want 42", len(data))
|
||||
continue
|
||||
}
|
||||
if data[0] != 4 {
|
||||
errorf("macho thread type = %d, want 4", data[0])
|
||||
}
|
||||
if data[1] != uint32(len(data))-2 {
|
||||
errorf("macho thread desc len = %d, want %d", data[1], uint32(len(data))-2)
|
||||
continue
|
||||
}
|
||||
for i, val := range data[2:] {
|
||||
switch i {
|
||||
default:
|
||||
if val != 0 {
|
||||
errorf("macho thread data[%d] = %#x, want 0", i, val)
|
||||
}
|
||||
case 32:
|
||||
prog.Entry = Addr(val)
|
||||
case 33:
|
||||
prog.Entry |= Addr(val) << 32
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case *macho.Segment:
|
||||
if load.Addr < limit {
|
||||
errorf("segments out of order: %q at %#x after %#x", load.Name, load.Addr, limit)
|
||||
}
|
||||
limit = load.Addr + load.Memsz
|
||||
if load.Name == "__PAGEZERO" || load.Addr == 0 && load.Filesz == 0 {
|
||||
if load.Name != "__PAGEZERO" {
|
||||
errorf("segment with Addr=0, Filesz=0 is named %q, want %q", load.Name, "__PAGEZERO")
|
||||
} else if load.Addr != 0 || load.Filesz != 0 {
|
||||
errorf("segment %q has Addr=%#x, Filesz=%d, want Addr=%#x, Filesz=%d", load.Name, load.Addr, load.Filesz, 0, 0)
|
||||
}
|
||||
prog.UnmappedSize = Addr(load.Memsz)
|
||||
continue
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(load.Name, "__") {
|
||||
errorf("segment name %q does not begin with %q", load.Name, "__")
|
||||
}
|
||||
if strings.ToUpper(load.Name) != load.Name {
|
||||
errorf("segment name %q is not all upper case", load.Name)
|
||||
}
|
||||
|
||||
seg := &Segment{
|
||||
Name: strings.ToLower(strings.TrimPrefix(load.Name, "__")),
|
||||
VirtAddr: Addr(load.Addr),
|
||||
VirtSize: Addr(load.Memsz),
|
||||
FileOffset: Addr(load.Offset),
|
||||
FileSize: Addr(load.Filesz),
|
||||
}
|
||||
prog.Segments = append(prog.Segments, seg)
|
||||
|
||||
data, err := load.Data()
|
||||
if err != nil {
|
||||
errorf("loading data from %q: %v", load.Name, err)
|
||||
}
|
||||
seg.Data = data
|
||||
|
||||
var maxprot, prot uint32
|
||||
if load.Name == "__TEXT" {
|
||||
maxprot, prot = 7, 5
|
||||
} else {
|
||||
maxprot, prot = 3, 3
|
||||
}
|
||||
if load.Maxprot != maxprot || load.Prot != prot {
|
||||
errorf("segment %q protection is %d, %d, want %d, %d",
|
||||
load.Name, load.Maxprot, load.Prot, maxprot, prot)
|
||||
}
|
||||
|
||||
for len(msects) > 0 && msects[0].Addr < load.Addr+load.Memsz {
|
||||
msect := msects[0]
|
||||
msects = msects[1:]
|
||||
|
||||
if msect.Offset > 0 && prog.HeaderSize == 0 {
|
||||
prog.HeaderSize = Addr(msect.Offset)
|
||||
if seg.FileOffset != 0 {
|
||||
errorf("initial segment %q does not map header", load.Name)
|
||||
}
|
||||
seg.VirtAddr += prog.HeaderSize
|
||||
seg.VirtSize -= prog.HeaderSize
|
||||
seg.FileOffset += prog.HeaderSize
|
||||
seg.FileSize -= prog.HeaderSize
|
||||
seg.Data = seg.Data[prog.HeaderSize:]
|
||||
}
|
||||
|
||||
if msect.Addr < load.Addr {
|
||||
errorf("section %q at address %#x is missing segment", msect.Name, msect.Addr)
|
||||
continue
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(msect.Name, "__") {
|
||||
errorf("section name %q does not begin with %q", msect.Name, "__")
|
||||
}
|
||||
if strings.ToLower(msect.Name) != msect.Name {
|
||||
errorf("section name %q is not all lower case", msect.Name)
|
||||
}
|
||||
if msect.Seg != load.Name {
|
||||
errorf("section %q is lists segment name %q, want %q",
|
||||
msect.Name, msect.Seg, load.Name)
|
||||
}
|
||||
if uint64(msect.Offset) != uint64(load.Offset)+msect.Addr-load.Addr {
|
||||
errorf("section %q file offset is %#x, want %#x",
|
||||
msect.Name, msect.Offset, load.Offset+msect.Addr-load.Addr)
|
||||
}
|
||||
if msect.Reloff != 0 || msect.Nreloc != 0 {
|
||||
errorf("section %q has reloff %d,%d, want %d,%d",
|
||||
msect.Name, msect.Reloff, msect.Nreloc, 0, 0)
|
||||
}
|
||||
flags := uint32(0)
|
||||
if msect.Name == "__text" {
|
||||
flags = 0x400
|
||||
}
|
||||
if msect.Offset == 0 {
|
||||
flags = 1
|
||||
}
|
||||
if msect.Flags != flags {
|
||||
errorf("section %q flags = %#x, want %#x", msect.Name, msect.Flags, flags)
|
||||
}
|
||||
sect := &Section{
|
||||
Name: strings.ToLower(strings.TrimPrefix(msect.Name, "__")),
|
||||
VirtAddr: Addr(msect.Addr),
|
||||
Size: Addr(msect.Size),
|
||||
Align: 1 << msect.Align,
|
||||
}
|
||||
seg.Sections = append(seg.Sections, sect)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, msect := range msects {
|
||||
errorf("section %q has no segment", msect.Name)
|
||||
}
|
||||
|
||||
limit = 0
|
||||
for _, msect := range f.Sections {
|
||||
if msect.Addr < limit {
|
||||
errorf("sections out of order: %q at %#x after %#x", msect.Name, msect.Addr, limit)
|
||||
}
|
||||
limit = msect.Addr + msect.Size
|
||||
}
|
||||
|
||||
err = nil
|
||||
if errors != nil {
|
||||
err = fmt.Errorf("%s", strings.Join(errors, "\n\t"))
|
||||
}
|
||||
return prog, err
|
||||
}
|
||||
@@ -1,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,479 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Generation of runtime function information (pclntab).
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"debug/goobj"
|
||||
"encoding/binary"
|
||||
"os"
|
||||
"sort"
|
||||
)
|
||||
|
||||
var zerofunc goobj.Func
|
||||
|
||||
// pclntab collects the runtime function data for each function that will
|
||||
// be listed in the binary and builds a single table describing all functions.
|
||||
// This table is used at run time for stack traces and to look up PC-specific
|
||||
// information during garbage collection. The symbol created is named
|
||||
// "pclntab" for historical reasons; the scope of the table has grown to
|
||||
// include more than just PC/line number correspondences.
|
||||
// The table format is documented at http://golang.org/s/go12symtab.
|
||||
func (p *Prog) pclntab() {
|
||||
// Count number of functions going into the binary,
|
||||
// so that we can size the initial index correctly.
|
||||
nfunc := 0
|
||||
for _, sym := range p.SymOrder {
|
||||
if sym.Kind != goobj.STEXT {
|
||||
continue
|
||||
}
|
||||
nfunc++
|
||||
}
|
||||
|
||||
// Table header.
|
||||
buf := new(SymBuffer)
|
||||
buf.Init(p)
|
||||
buf.SetSize(8 + p.ptrsize)
|
||||
off := 0
|
||||
off = buf.Uint32(off, 0xfffffffb)
|
||||
off = buf.Uint8(off, 0)
|
||||
off = buf.Uint8(off, 0)
|
||||
off = buf.Uint8(off, uint8(p.pcquantum))
|
||||
off = buf.Uint8(off, uint8(p.ptrsize))
|
||||
off = buf.Uint(off, uint64(nfunc), p.ptrsize)
|
||||
indexOff := off
|
||||
off += (nfunc*2 + 1) * p.ptrsize // function index, to be filled in
|
||||
off += 4 // file table start offset, to be filled in
|
||||
buf.SetSize(off)
|
||||
|
||||
// One-file cache for reading PCData tables from package files.
|
||||
// TODO(rsc): Better I/O strategy.
|
||||
var (
|
||||
file *os.File
|
||||
fname string
|
||||
)
|
||||
|
||||
// Files gives the file numbering for source file names recorded
|
||||
// in the binary.
|
||||
files := make(map[string]int)
|
||||
|
||||
// Build the table, build the index, and build the file name numbering.
|
||||
// The loop here must visit functions in the same order that they will
|
||||
// be stored in the binary, or else binary search over the index will fail.
|
||||
// The runtime checks that the index is sorted properly at program start time.
|
||||
var lastSym *Sym
|
||||
for _, sym := range p.SymOrder {
|
||||
if sym.Kind != goobj.STEXT {
|
||||
continue
|
||||
}
|
||||
lastSym = sym
|
||||
|
||||
// Treat no recorded function information same as all zeros.
|
||||
f := sym.Func
|
||||
if f == nil {
|
||||
f = &zerofunc
|
||||
}
|
||||
|
||||
// Open package file if needed, for reading PC data.
|
||||
if fname != sym.Package.File {
|
||||
if file != nil {
|
||||
file.Close()
|
||||
}
|
||||
var err error
|
||||
file, err = os.Open(sym.Package.File)
|
||||
if err != nil {
|
||||
p.errorf("%v: %v", sym, err)
|
||||
return
|
||||
}
|
||||
fname = sym.Package.File
|
||||
}
|
||||
|
||||
// off is the offset of the table entry where we're going to write
|
||||
// the encoded form of Func.
|
||||
// indexOff is the current position in the table index;
|
||||
// we add an entry in the index pointing at off.
|
||||
off = (buf.Size() + p.ptrsize - 1) &^ (p.ptrsize - 1)
|
||||
indexOff = buf.Addr(indexOff, sym.SymID, 0)
|
||||
indexOff = buf.Uint(indexOff, uint64(off), p.ptrsize)
|
||||
|
||||
// The Func encoding starts with a header giving offsets
|
||||
// to data blobs, and then the data blobs themselves.
|
||||
// end gives the current write position for the data blobs.
|
||||
end := off + p.ptrsize + 3*4 + 5*4 + len(f.PCData)*4 + len(f.FuncData)*p.ptrsize
|
||||
if len(f.FuncData) > 0 {
|
||||
end += -end & (p.ptrsize - 1)
|
||||
}
|
||||
buf.SetSize(end)
|
||||
|
||||
// entry uintptr
|
||||
// name int32
|
||||
// args int32
|
||||
// frame int32
|
||||
//
|
||||
// The frame recorded in the object file is
|
||||
// the frame size used in an assembly listing, which does
|
||||
// not include the caller PC on the stack.
|
||||
// The frame size we want to list here is the delta from
|
||||
// this function's SP to its caller's SP, which does include
|
||||
// the caller PC. Add p.ptrsize to f.Frame to adjust.
|
||||
// TODO(rsc): Record the same frame size in the object file.
|
||||
off = buf.Addr(off, sym.SymID, 0)
|
||||
off = buf.Uint32(off, uint32(addString(buf, sym.Name)))
|
||||
off = buf.Uint32(off, uint32(f.Args))
|
||||
off = buf.Uint32(off, uint32(f.Frame+p.ptrsize))
|
||||
|
||||
// pcdata
|
||||
off = buf.Uint32(off, uint32(addPCTable(p, buf, file, f.PCSP)))
|
||||
off = buf.Uint32(off, uint32(addPCFileTable(p, buf, file, f.PCFile, sym, files)))
|
||||
off = buf.Uint32(off, uint32(addPCTable(p, buf, file, f.PCLine)))
|
||||
off = buf.Uint32(off, uint32(len(f.PCData)))
|
||||
off = buf.Uint32(off, uint32(len(f.FuncData)))
|
||||
for _, pcdata := range f.PCData {
|
||||
off = buf.Uint32(off, uint32(addPCTable(p, buf, file, pcdata)))
|
||||
}
|
||||
|
||||
// funcdata
|
||||
if len(f.FuncData) > 0 {
|
||||
off += -off & (p.ptrsize - 1) // must be pointer-aligned
|
||||
for _, funcdata := range f.FuncData {
|
||||
if funcdata.Sym.Name == "" {
|
||||
off = buf.Uint(off, uint64(funcdata.Offset), p.ptrsize)
|
||||
} else {
|
||||
off = buf.Addr(off, funcdata.Sym, funcdata.Offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if off != end {
|
||||
p.errorf("internal error: invalid math in pclntab: off=%#x end=%#x", off, end)
|
||||
break
|
||||
}
|
||||
}
|
||||
if file != nil {
|
||||
file.Close()
|
||||
}
|
||||
|
||||
// Final entry of index is end PC of last function.
|
||||
indexOff = buf.Addr(indexOff, lastSym.SymID, int64(lastSym.Size))
|
||||
|
||||
// Start file table.
|
||||
// Function index is immediately followed by offset to file table.
|
||||
off = (buf.Size() + p.ptrsize - 1) &^ (p.ptrsize - 1)
|
||||
buf.Uint32(indexOff, uint32(off))
|
||||
|
||||
// File table is an array of uint32s.
|
||||
// The first entry gives 1+n, the size of the array.
|
||||
// The following n entries hold offsets to string data.
|
||||
// File number n uses the string pointed at by entry n.
|
||||
// File number 0 is invalid.
|
||||
buf.SetSize(off + (1+len(files))*4)
|
||||
buf.Uint32(off, uint32(1+len(files)))
|
||||
var filestr []string
|
||||
for file := range files {
|
||||
filestr = append(filestr, file)
|
||||
}
|
||||
sort.Strings(filestr)
|
||||
for _, file := range filestr {
|
||||
id := files[file]
|
||||
buf.Uint32(off+4*id, uint32(addString(buf, file)))
|
||||
}
|
||||
|
||||
pclntab := &Sym{
|
||||
Sym: &goobj.Sym{
|
||||
SymID: goobj.SymID{Name: "runtime.pclntab"},
|
||||
Kind: goobj.SPCLNTAB,
|
||||
Size: buf.Size(),
|
||||
Reloc: buf.Reloc(),
|
||||
},
|
||||
Bytes: buf.Bytes(),
|
||||
}
|
||||
p.addSym(pclntab)
|
||||
}
|
||||
|
||||
// addString appends the string s to the buffer b.
|
||||
// It returns the offset of the beginning of the string in the buffer.
|
||||
func addString(b *SymBuffer, s string) int {
|
||||
off := b.Size()
|
||||
b.SetSize(off + len(s) + 1)
|
||||
copy(b.data[off:], s)
|
||||
return off
|
||||
}
|
||||
|
||||
// addPCTable appends the PC-data table stored in the file f at the location loc
|
||||
// to the symbol buffer b. It returns the offset of the beginning of the table
|
||||
// in the buffer.
|
||||
func addPCTable(p *Prog, b *SymBuffer, f *os.File, loc goobj.Data) int {
|
||||
if loc.Size == 0 {
|
||||
return 0
|
||||
}
|
||||
off := b.Size()
|
||||
b.SetSize(off + int(loc.Size))
|
||||
_, err := f.ReadAt(b.data[off:off+int(loc.Size)], loc.Offset)
|
||||
if err != nil {
|
||||
p.errorf("%v", err)
|
||||
}
|
||||
return off
|
||||
}
|
||||
|
||||
// addPCFileTable is like addPCTable, but it renumbers the file names referred to by the table
|
||||
// to use the global numbering maintained in the files map. It adds new files to the
|
||||
// map as necessary.
|
||||
func addPCFileTable(p *Prog, b *SymBuffer, f *os.File, loc goobj.Data, sym *Sym, files map[string]int) int {
|
||||
if loc.Size == 0 {
|
||||
return 0
|
||||
}
|
||||
off := b.Size()
|
||||
|
||||
src := make([]byte, loc.Size)
|
||||
_, err := f.ReadAt(src, loc.Offset)
|
||||
if err != nil {
|
||||
p.errorf("%v", err)
|
||||
return 0
|
||||
}
|
||||
|
||||
filenum := make([]int, len(sym.Func.File))
|
||||
for i, name := range sym.Func.File {
|
||||
num := files[name]
|
||||
if num == 0 {
|
||||
num = len(files) + 1
|
||||
files[name] = num
|
||||
}
|
||||
filenum[i] = num
|
||||
}
|
||||
|
||||
var dst []byte
|
||||
newval := int32(-1)
|
||||
var it PCIter
|
||||
for it.Init(p, src); !it.Done; it.Next() {
|
||||
// value delta
|
||||
oldval := it.Value
|
||||
val := oldval
|
||||
if oldval != -1 {
|
||||
if oldval < 0 || int(oldval) >= len(filenum) {
|
||||
p.errorf("%s: corrupt pc-file table", sym)
|
||||
break
|
||||
}
|
||||
val = int32(filenum[oldval])
|
||||
}
|
||||
dv := val - newval
|
||||
newval = val
|
||||
uv := uint32(dv<<1) ^ uint32(dv>>31)
|
||||
dst = appendVarint(dst, uv)
|
||||
|
||||
// pc delta
|
||||
dst = appendVarint(dst, it.NextPC-it.PC)
|
||||
}
|
||||
if it.Corrupt {
|
||||
p.errorf("%s: corrupt pc-file table", sym)
|
||||
}
|
||||
|
||||
// terminating value delta
|
||||
dst = appendVarint(dst, 0)
|
||||
|
||||
b.SetSize(off + len(dst))
|
||||
copy(b.data[off:], dst)
|
||||
return off
|
||||
}
|
||||
|
||||
// A SymBuffer is a buffer for preparing the data image of a
|
||||
// linker-generated symbol.
|
||||
type SymBuffer struct {
|
||||
data []byte
|
||||
reloc []goobj.Reloc
|
||||
order binary.ByteOrder
|
||||
ptrsize int
|
||||
}
|
||||
|
||||
// Init initializes the buffer for writing.
|
||||
func (b *SymBuffer) Init(p *Prog) {
|
||||
b.data = nil
|
||||
b.reloc = nil
|
||||
b.order = p.byteorder
|
||||
b.ptrsize = p.ptrsize
|
||||
}
|
||||
|
||||
// Bytes returns the buffer data.
|
||||
func (b *SymBuffer) Bytes() []byte {
|
||||
return b.data
|
||||
}
|
||||
|
||||
// SetSize sets the buffer's data size to n bytes.
|
||||
func (b *SymBuffer) SetSize(n int) {
|
||||
for cap(b.data) < n {
|
||||
b.data = append(b.data[:cap(b.data)], 0)
|
||||
}
|
||||
b.data = b.data[:n]
|
||||
}
|
||||
|
||||
// Size returns the buffer's data size.
|
||||
func (b *SymBuffer) Size() int {
|
||||
return len(b.data)
|
||||
}
|
||||
|
||||
// Reloc returns the buffered relocations.
|
||||
func (b *SymBuffer) Reloc() []goobj.Reloc {
|
||||
return b.reloc
|
||||
}
|
||||
|
||||
// Uint8 sets the uint8 at offset off to v.
|
||||
// It returns the offset just beyond v.
|
||||
func (b *SymBuffer) Uint8(off int, v uint8) int {
|
||||
b.data[off] = v
|
||||
return off + 1
|
||||
}
|
||||
|
||||
// Uint16 sets the uint16 at offset off to v.
|
||||
// It returns the offset just beyond v.
|
||||
func (b *SymBuffer) Uint16(off int, v uint16) int {
|
||||
b.order.PutUint16(b.data[off:], v)
|
||||
return off + 2
|
||||
}
|
||||
|
||||
// Uint32 sets the uint32 at offset off to v.
|
||||
// It returns the offset just beyond v.
|
||||
func (b *SymBuffer) Uint32(off int, v uint32) int {
|
||||
b.order.PutUint32(b.data[off:], v)
|
||||
return off + 4
|
||||
}
|
||||
|
||||
// Uint64 sets the uint64 at offset off to v.
|
||||
// It returns the offset just beyond v.
|
||||
func (b *SymBuffer) Uint64(off int, v uint64) int {
|
||||
b.order.PutUint64(b.data[off:], v)
|
||||
return off + 8
|
||||
}
|
||||
|
||||
// Uint sets the size-byte unsigned integer at offset off to v.
|
||||
// It returns the offset just beyond v.
|
||||
func (b *SymBuffer) Uint(off int, v uint64, size int) int {
|
||||
switch size {
|
||||
case 1:
|
||||
return b.Uint8(off, uint8(v))
|
||||
case 2:
|
||||
return b.Uint16(off, uint16(v))
|
||||
case 4:
|
||||
return b.Uint32(off, uint32(v))
|
||||
case 8:
|
||||
return b.Uint64(off, v)
|
||||
}
|
||||
panic("invalid use of SymBuffer.SetUint")
|
||||
}
|
||||
|
||||
// Addr sets the pointer-sized address at offset off to refer
|
||||
// to symoff bytes past the start of sym. It returns the offset
|
||||
// just beyond the address.
|
||||
func (b *SymBuffer) Addr(off int, sym goobj.SymID, symoff int64) int {
|
||||
b.reloc = append(b.reloc, goobj.Reloc{
|
||||
Offset: off,
|
||||
Size: b.ptrsize,
|
||||
Sym: sym,
|
||||
Add: int(symoff),
|
||||
Type: R_ADDR,
|
||||
})
|
||||
return off + b.ptrsize
|
||||
}
|
||||
|
||||
// A PCIter implements iteration over PC-data tables.
|
||||
//
|
||||
// var it PCIter
|
||||
// for it.Init(p, data); !it.Done; it.Next() {
|
||||
// it.Value holds from it.PC up to (but not including) it.NextPC
|
||||
// }
|
||||
// if it.Corrupt {
|
||||
// data was malformed
|
||||
// }
|
||||
//
|
||||
type PCIter struct {
|
||||
PC uint32
|
||||
NextPC uint32
|
||||
Value int32
|
||||
Done bool
|
||||
Corrupt bool
|
||||
p []byte
|
||||
start bool
|
||||
pcquantum uint32
|
||||
}
|
||||
|
||||
// Init initializes the iteration.
|
||||
// On return, if it.Done is true, the iteration is over.
|
||||
// Otherwise it.Value applies in the pc range [it.PC, it.NextPC).
|
||||
func (it *PCIter) Init(p *Prog, buf []byte) {
|
||||
it.p = buf
|
||||
it.PC = 0
|
||||
it.NextPC = 0
|
||||
it.Value = -1
|
||||
it.start = true
|
||||
it.pcquantum = uint32(p.pcquantum)
|
||||
it.Done = false
|
||||
it.Next()
|
||||
}
|
||||
|
||||
// Next steps forward one entry in the table.
|
||||
// On return, if it.Done is true, the iteration is over.
|
||||
// Otherwise it.Value applies in the pc range [it.PC, it.NextPC).
|
||||
func (it *PCIter) Next() {
|
||||
it.PC = it.NextPC
|
||||
if it.Done {
|
||||
return
|
||||
}
|
||||
if len(it.p) == 0 {
|
||||
it.Done = true
|
||||
return
|
||||
}
|
||||
|
||||
// value delta
|
||||
uv, p, ok := decodeVarint(it.p)
|
||||
if !ok {
|
||||
it.Done = true
|
||||
it.Corrupt = true
|
||||
return
|
||||
}
|
||||
it.p = p
|
||||
if uv == 0 && !it.start {
|
||||
it.Done = true
|
||||
return
|
||||
}
|
||||
it.start = false
|
||||
sv := int32(uv>>1) ^ int32(uv<<31)>>31
|
||||
it.Value += sv
|
||||
|
||||
// pc delta
|
||||
uv, it.p, ok = decodeVarint(it.p)
|
||||
if !ok {
|
||||
it.Done = true
|
||||
it.Corrupt = true
|
||||
return
|
||||
}
|
||||
it.NextPC = it.PC + uv*it.pcquantum
|
||||
}
|
||||
|
||||
// decodeVarint decodes an unsigned varint from p,
|
||||
// reporting the value, the remainder of the data, and
|
||||
// whether the decoding was successful.
|
||||
func decodeVarint(p []byte) (v uint32, rest []byte, ok bool) {
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if len(p) == 0 {
|
||||
return
|
||||
}
|
||||
c := uint32(p[0])
|
||||
p = p[1:]
|
||||
v |= (c & 0x7F) << shift
|
||||
if c&0x80 == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return v, p, true
|
||||
}
|
||||
|
||||
// appendVarint appends an unsigned varint encoding of v to p
|
||||
// and returns the resulting slice.
|
||||
func appendVarint(p []byte, v uint32) []byte {
|
||||
for ; v >= 0x80; v >>= 7 {
|
||||
p = append(p, byte(v)|0x80)
|
||||
}
|
||||
p = append(p, byte(v))
|
||||
return p
|
||||
}
|
||||
@@ -1,340 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/goobj"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Test of pcln table encoding.
|
||||
// testdata/genpcln.go generates an assembly file with
|
||||
// pseudorandom values for the data that pclntab stores.
|
||||
// This test recomputes the same pseudorandom stream
|
||||
// and checks that the final linked binary uses those values
|
||||
// as well.
|
||||
func TestPclntab(t *testing.T) {
|
||||
p := &Prog{
|
||||
GOOS: "darwin",
|
||||
GOARCH: "amd64",
|
||||
Error: func(s string) { t.Error(s) },
|
||||
StartSym: "start",
|
||||
omitRuntime: true,
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
p.link(&buf, "testdata/pclntab.6")
|
||||
if p.NumError > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// The algorithm for computing values here must match
|
||||
// the one in testdata/genpcln.go.
|
||||
for f := 0; f < 3; f++ {
|
||||
file := "input"
|
||||
line := 1
|
||||
rnd := rand.New(rand.NewSource(int64(f)))
|
||||
args := rnd.Intn(100) * 8
|
||||
frame := 32 + rnd.Intn(32)/8*8
|
||||
size := 200 + rnd.Intn(100)*8
|
||||
|
||||
name := fmt.Sprintf("func%d", f)
|
||||
r, off, fargs, fframe, ok := findFunc(t, p, name)
|
||||
if !ok {
|
||||
continue // error already printed
|
||||
}
|
||||
if fargs != args {
|
||||
t.Errorf("%s: args=%d, want %d", name, fargs, args)
|
||||
}
|
||||
if fframe != frame+8 {
|
||||
t.Errorf("%s: frame=%d, want %d", name, fframe, frame+8)
|
||||
}
|
||||
|
||||
// Check FUNCDATA 1.
|
||||
fdata, ok := loadFuncdata(t, r, name, off, 1)
|
||||
if ok {
|
||||
fsym := p.Syms[goobj.SymID{Name: fmt.Sprintf("funcdata%d", f)}]
|
||||
if fsym == nil {
|
||||
t.Errorf("funcdata%d is missing in binary", f)
|
||||
} else if fdata != fsym.Addr {
|
||||
t.Errorf("%s: funcdata 1 = %#x, want %#x", name, fdata, fsym.Addr)
|
||||
}
|
||||
}
|
||||
|
||||
// Walk code checking pcdata values.
|
||||
spadj := 0
|
||||
pcdata1 := -1
|
||||
pcdata2 := -1
|
||||
|
||||
checkPCSP(t, r, name, off, 0, 0)
|
||||
checkPCData(t, r, name, off, 0, 0, -1)
|
||||
checkPCData(t, r, name, off, 0, 1, -1)
|
||||
checkPCData(t, r, name, off, 0, 2, -1)
|
||||
|
||||
firstpc := 4
|
||||
for i := 0; i < size; i++ {
|
||||
pc := firstpc + i // skip SP adjustment to allocate frame
|
||||
if i >= 0x100 && t.Failed() {
|
||||
break
|
||||
}
|
||||
// Possible SP adjustment.
|
||||
checkPCSP(t, r, name, off, pc, frame+spadj)
|
||||
if rnd.Intn(100) == 0 {
|
||||
checkPCFileLine(t, r, name, off, pc, file, line)
|
||||
checkPCData(t, r, name, off, pc, 1, pcdata1)
|
||||
checkPCData(t, r, name, off, pc, 2, pcdata2)
|
||||
i += 1
|
||||
pc = firstpc + i
|
||||
checkPCFileLine(t, r, name, off, pc-1, file, line)
|
||||
checkPCData(t, r, name, off, pc-1, 1, pcdata1)
|
||||
checkPCData(t, r, name, off, pc-1, 2, pcdata2)
|
||||
checkPCSP(t, r, name, off, pc-1, frame+spadj)
|
||||
|
||||
if spadj <= -32 || spadj < 32 && rnd.Intn(2) == 0 {
|
||||
spadj += 8
|
||||
} else {
|
||||
spadj -= 8
|
||||
}
|
||||
checkPCSP(t, r, name, off, pc, frame+spadj)
|
||||
}
|
||||
|
||||
// Possible PCFile change.
|
||||
if rnd.Intn(100) == 0 {
|
||||
file = fmt.Sprintf("file%d.s", rnd.Intn(10))
|
||||
line = rnd.Intn(100) + 1
|
||||
}
|
||||
|
||||
// Possible PCLine change.
|
||||
if rnd.Intn(10) == 0 {
|
||||
line = rnd.Intn(1000) + 1
|
||||
}
|
||||
|
||||
// Possible PCData $1 change.
|
||||
if rnd.Intn(100) == 0 {
|
||||
pcdata1 = rnd.Intn(1000)
|
||||
}
|
||||
|
||||
// Possible PCData $2 change.
|
||||
if rnd.Intn(100) == 0 {
|
||||
pcdata2 = rnd.Intn(1000)
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
checkPCFileLine(t, r, name, off, 0, file, line)
|
||||
checkPCFileLine(t, r, name, off, pc-1, file, line)
|
||||
}
|
||||
checkPCFileLine(t, r, name, off, pc, file, line)
|
||||
checkPCData(t, r, name, off, pc, 1, pcdata1)
|
||||
checkPCData(t, r, name, off, pc, 2, pcdata2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// findFunc finds the function information in the pclntab of p
|
||||
// for the function with the given name.
|
||||
// It returns a symbol reader for pclntab, the offset of the function information
|
||||
// within that symbol, and the args and frame values read out of the information.
|
||||
func findFunc(t *testing.T, p *Prog, name string) (r *SymReader, off, args, frame int, ok bool) {
|
||||
tabsym := p.Syms[goobj.SymID{Name: "runtime.pclntab"}]
|
||||
if tabsym == nil {
|
||||
t.Errorf("pclntab is missing in binary")
|
||||
return
|
||||
}
|
||||
|
||||
r = new(SymReader)
|
||||
r.Init(p, tabsym)
|
||||
|
||||
// pclntab must with 8-byte header
|
||||
if r.Uint32(0) != 0xfffffffb || r.Uint8(4) != 0 || r.Uint8(5) != 0 || r.Uint8(6) != uint8(p.pcquantum) || r.Uint8(7) != uint8(p.ptrsize) {
|
||||
t.Errorf("pclntab has incorrect header %.8x", r.data[:8])
|
||||
return
|
||||
}
|
||||
|
||||
sym := p.Syms[goobj.SymID{Name: name}]
|
||||
if sym == nil {
|
||||
t.Errorf("%s is missing in the binary", name)
|
||||
return
|
||||
}
|
||||
|
||||
// index is nfunc addr0 off0 addr1 off1 ... addr_nfunc (sentinel)
|
||||
nfunc := int(r.Addr(8))
|
||||
i := sort.Search(nfunc, func(i int) bool {
|
||||
return r.Addr(8+p.ptrsize*(1+2*i)) >= sym.Addr
|
||||
})
|
||||
if entry := r.Addr(8 + p.ptrsize*(1+2*i)); entry != sym.Addr {
|
||||
indexTab := make([]Addr, 2*nfunc+1)
|
||||
for j := range indexTab {
|
||||
indexTab[j] = r.Addr(8 + p.ptrsize*(1+j))
|
||||
}
|
||||
t.Errorf("pclntab is missing entry for %s (%#x): %#x", name, sym.Addr, indexTab)
|
||||
return
|
||||
}
|
||||
|
||||
off = int(r.Addr(8 + p.ptrsize*(1+2*i+1)))
|
||||
|
||||
// func description at off is
|
||||
// entry addr
|
||||
// nameoff uint32
|
||||
// args uint32
|
||||
// frame uint32
|
||||
// pcspoff uint32
|
||||
// pcfileoff uint32
|
||||
// pclineoff uint32
|
||||
// npcdata uint32
|
||||
// nfuncdata uint32
|
||||
// pcdata npcdata*uint32
|
||||
// funcdata nfuncdata*addr
|
||||
//
|
||||
if entry := r.Addr(off); entry != sym.Addr {
|
||||
t.Errorf("pclntab inconsistent: entry for %s addr=%#x has entry=%#x", name, sym.Addr, entry)
|
||||
return
|
||||
}
|
||||
nameoff := int(r.Uint32(off + p.ptrsize))
|
||||
args = int(r.Uint32(off + p.ptrsize + 1*4))
|
||||
frame = int(r.Uint32(off + p.ptrsize + 2*4))
|
||||
|
||||
fname := r.String(nameoff)
|
||||
if fname != name {
|
||||
t.Errorf("pclntab inconsistent: entry for %s addr=%#x has name %q", name, sym.Addr, fname)
|
||||
}
|
||||
|
||||
ok = true // off, args, frame are usable
|
||||
return
|
||||
}
|
||||
|
||||
// loadFuncdata returns the funcdata #fnum value
|
||||
// loaded from the function information for name.
|
||||
func loadFuncdata(t *testing.T, r *SymReader, name string, off int, fnum int) (Addr, bool) {
|
||||
npcdata := int(r.Uint32(off + r.p.ptrsize + 6*4))
|
||||
nfuncdata := int(r.Uint32(off + r.p.ptrsize + 7*4))
|
||||
if fnum >= nfuncdata {
|
||||
t.Errorf("pclntab(%s): no funcdata %d (only < %d)", name, fnum, nfuncdata)
|
||||
return 0, false
|
||||
}
|
||||
fdataoff := off + r.p.ptrsize + (8+npcdata)*4 + fnum*r.p.ptrsize
|
||||
fdataoff += fdataoff & 4
|
||||
return r.Addr(fdataoff), true
|
||||
}
|
||||
|
||||
// checkPCSP checks that the PCSP table in the function information at off
|
||||
// lists spadj as the sp delta for pc.
|
||||
func checkPCSP(t *testing.T, r *SymReader, name string, off, pc, spadj int) {
|
||||
pcoff := r.Uint32(off + r.p.ptrsize + 3*4)
|
||||
pcval, ok := readPCData(t, r, name, "PCSP", pcoff, pc)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if pcval != spadj {
|
||||
t.Errorf("pclntab(%s): at pc=+%#x, pcsp=%d, want %d", name, pc, pcval, spadj)
|
||||
}
|
||||
}
|
||||
|
||||
// checkPCSP checks that the PCFile and PCLine tables in the function information at off
|
||||
// list file, line as the file name and line number for pc.
|
||||
func checkPCFileLine(t *testing.T, r *SymReader, name string, off, pc int, file string, line int) {
|
||||
pcfileoff := r.Uint32(off + r.p.ptrsize + 4*4)
|
||||
pclineoff := r.Uint32(off + r.p.ptrsize + 5*4)
|
||||
pcfilenum, ok1 := readPCData(t, r, name, "PCFile", pcfileoff, pc)
|
||||
pcline, ok2 := readPCData(t, r, name, "PCLine", pclineoff, pc)
|
||||
if !ok1 || !ok2 {
|
||||
return
|
||||
}
|
||||
nfunc := int(r.Addr(8))
|
||||
filetaboff := r.Uint32(8 + r.p.ptrsize*2*(nfunc+1))
|
||||
nfile := int(r.Uint32(int(filetaboff)))
|
||||
if pcfilenum <= 0 || pcfilenum >= nfile {
|
||||
t.Errorf("pclntab(%s): at pc=+%#x, filenum=%d (invalid; nfile=%d)", name, pc, pcfilenum, nfile)
|
||||
}
|
||||
pcfile := r.String(int(r.Uint32(int(filetaboff) + pcfilenum*4)))
|
||||
if !strings.HasSuffix(pcfile, file) {
|
||||
t.Errorf("pclntab(%s): at pc=+%#x, file=%q, want %q", name, pc, pcfile, file)
|
||||
}
|
||||
if pcline != line {
|
||||
t.Errorf("pclntab(%s): at pc=+%#x, line=%d, want %d", name, pc, pcline, line)
|
||||
}
|
||||
}
|
||||
|
||||
// checkPCData checks that the PCData#pnum table in the function information at off
|
||||
// list val as the value for pc.
|
||||
func checkPCData(t *testing.T, r *SymReader, name string, off, pc, pnum, val int) {
|
||||
pcoff := r.Uint32(off + r.p.ptrsize + (8+pnum)*4)
|
||||
pcval, ok := readPCData(t, r, name, fmt.Sprintf("PCData#%d", pnum), pcoff, pc)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if pcval != val {
|
||||
t.Errorf("pclntab(%s): at pc=+%#x, pcdata#%d=%d, want %d", name, pc, pnum, pcval, val)
|
||||
}
|
||||
}
|
||||
|
||||
// readPCData reads the PCData table offset off
|
||||
// to obtain and return the value associated with pc.
|
||||
func readPCData(t *testing.T, r *SymReader, name, pcdataname string, pcoff uint32, pc int) (int, bool) {
|
||||
// "If pcsp, pcfile, pcln, or any of the pcdata offsets is zero,
|
||||
// that table is considered missing, and all PCs take value -1."
|
||||
if pcoff == 0 {
|
||||
return -1, true
|
||||
}
|
||||
|
||||
var it PCIter
|
||||
for it.Init(r.p, r.data[pcoff:]); !it.Done; it.Next() {
|
||||
if it.PC <= uint32(pc) && uint32(pc) < it.NextPC {
|
||||
return int(it.Value), true
|
||||
}
|
||||
}
|
||||
if it.Corrupt {
|
||||
t.Errorf("pclntab(%s): %s: corrupt pcdata table", name, pcdataname)
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// A SymReader provides typed access to the data for a symbol.
|
||||
type SymReader struct {
|
||||
p *Prog
|
||||
data []byte
|
||||
}
|
||||
|
||||
func (r *SymReader) Init(p *Prog, sym *Sym) {
|
||||
seg := sym.Section.Segment
|
||||
off := sym.Addr - seg.VirtAddr
|
||||
data := seg.Data[off : off+Addr(sym.Size)]
|
||||
r.p = p
|
||||
r.data = data
|
||||
}
|
||||
|
||||
func (r *SymReader) Uint8(off int) uint8 {
|
||||
return r.data[off]
|
||||
}
|
||||
|
||||
func (r *SymReader) Uint16(off int) uint16 {
|
||||
return r.p.byteorder.Uint16(r.data[off:])
|
||||
}
|
||||
|
||||
func (r *SymReader) Uint32(off int) uint32 {
|
||||
return r.p.byteorder.Uint32(r.data[off:])
|
||||
}
|
||||
|
||||
func (r *SymReader) Uint64(off int) uint64 {
|
||||
return r.p.byteorder.Uint64(r.data[off:])
|
||||
}
|
||||
|
||||
func (r *SymReader) Addr(off int) Addr {
|
||||
if r.p.ptrsize == 4 {
|
||||
return Addr(r.Uint32(off))
|
||||
}
|
||||
return Addr(r.Uint64(off))
|
||||
}
|
||||
|
||||
func (r *SymReader) String(off int) string {
|
||||
end := off
|
||||
for r.data[end] != '\x00' {
|
||||
end++
|
||||
}
|
||||
return string(r.data[off:end])
|
||||
}
|
||||
@@ -1,220 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"debug/goobj"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// A Prog holds state for constructing an executable (program) image.
|
||||
//
|
||||
// The usual sequence of operations on a Prog is:
|
||||
//
|
||||
// p.init()
|
||||
// p.scan(file)
|
||||
// p.dead()
|
||||
// p.runtime()
|
||||
// p.layout()
|
||||
// p.load()
|
||||
// p.debug()
|
||||
// p.write(w)
|
||||
//
|
||||
// p.init is in this file. The rest of the methods are in files
|
||||
// named for the method. The convenience method p.link runs
|
||||
// this sequence.
|
||||
//
|
||||
type Prog struct {
|
||||
// Context
|
||||
GOOS string // target operating system
|
||||
GOARCH string // target architecture
|
||||
Format string // desired file format ("elf", "macho", ...)
|
||||
Error func(string) // called to report an error (if set)
|
||||
NumError int // number of errors printed
|
||||
StartSym string
|
||||
|
||||
// Derived context
|
||||
arch
|
||||
formatter formatter
|
||||
startSym goobj.SymID
|
||||
pkgdir string
|
||||
omitRuntime bool // do not load runtime package
|
||||
|
||||
// Input
|
||||
Packages map[string]*Package // loaded packages, by import path
|
||||
Syms map[goobj.SymID]*Sym // defined symbols, by symbol ID
|
||||
Missing map[goobj.SymID]bool // missing symbols
|
||||
Dead map[goobj.SymID]bool // symbols removed as dead
|
||||
SymOrder []*Sym // order syms were scanned
|
||||
MaxVersion int // max SymID.Version, for generating fresh symbol IDs
|
||||
|
||||
// Output
|
||||
UnmappedSize Addr // size of unmapped region at address 0
|
||||
HeaderSize Addr // size of object file header
|
||||
Entry Addr // virtual address where execution begins
|
||||
Segments []*Segment // loaded memory segments
|
||||
}
|
||||
|
||||
// An arch describes architecture-dependent settings.
|
||||
type arch struct {
|
||||
byteorder binary.ByteOrder
|
||||
ptrsize int
|
||||
pcquantum int
|
||||
}
|
||||
|
||||
// A formatter takes care of the details of generating a particular
|
||||
// kind of executable file.
|
||||
type formatter interface {
|
||||
// headerSize returns the footprint of the header for p
|
||||
// in both virtual address space and file bytes.
|
||||
// The footprint does not include any bytes stored at the
|
||||
// end of the file.
|
||||
headerSize(p *Prog) (virt, file Addr)
|
||||
|
||||
// write writes the executable file for p to w.
|
||||
write(w io.Writer, p *Prog)
|
||||
}
|
||||
|
||||
// An Addr represents a virtual memory address, a file address, or a size.
|
||||
// It must be a uint64, not a uintptr, so that a 32-bit linker can still generate a 64-bit binary.
|
||||
// It must be unsigned in order to link programs placed at very large start addresses.
|
||||
// Math involving Addrs must be checked carefully not to require negative numbers.
|
||||
type Addr uint64
|
||||
|
||||
// A Package is a Go package loaded from a file.
|
||||
type Package struct {
|
||||
*goobj.Package // table of contents
|
||||
File string // file name for reopening
|
||||
Syms []*Sym // symbols defined by this package
|
||||
}
|
||||
|
||||
// A Sym is a symbol defined in a loaded package.
|
||||
type Sym struct {
|
||||
*goobj.Sym // symbol metadata from package file
|
||||
Package *Package // package defining symbol
|
||||
Section *Section // section where symbol is placed in output program
|
||||
Addr Addr // virtual address of symbol in output program
|
||||
Bytes []byte // symbol data, for internally defined symbols
|
||||
}
|
||||
|
||||
// A Segment is a loaded memory segment.
|
||||
// A Prog is expected to have segments named "text" and optionally "data",
|
||||
// in that order, before any other segments.
|
||||
type Segment struct {
|
||||
Name string // name of segment: "text", "data", ...
|
||||
VirtAddr Addr // virtual memory address of segment base
|
||||
VirtSize Addr // size of segment in memory
|
||||
FileOffset Addr // file offset of segment base
|
||||
FileSize Addr // size of segment in file; can be less than VirtSize
|
||||
Sections []*Section // sections inside segment
|
||||
Data []byte // raw data of segment image
|
||||
}
|
||||
|
||||
// A Section is part of a loaded memory segment.
|
||||
type Section struct {
|
||||
Name string // name of section: "text", "rodata", "noptrbss", and so on
|
||||
VirtAddr Addr // virtual memory address of section base
|
||||
Size Addr // size of section in memory
|
||||
Align Addr // required alignment
|
||||
InFile bool // section has image data in file (like data, unlike bss)
|
||||
Syms []*Sym // symbols stored in section
|
||||
Segment *Segment // segment containing section
|
||||
}
|
||||
|
||||
func (p *Prog) errorf(format string, args ...interface{}) {
|
||||
if p.Error != nil {
|
||||
p.Error(fmt.Sprintf(format, args...))
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, format+"\n", args...)
|
||||
}
|
||||
p.NumError++
|
||||
}
|
||||
|
||||
// link is the one-stop convenience method for running a link.
|
||||
// It writes to w the object file generated from using mainFile as the main package.
|
||||
func (p *Prog) link(w io.Writer, mainFile string) {
|
||||
p.init()
|
||||
p.scan(mainFile)
|
||||
if p.NumError > 0 {
|
||||
return
|
||||
}
|
||||
p.dead()
|
||||
p.runtime()
|
||||
p.autoData()
|
||||
p.layout()
|
||||
p.autoConst()
|
||||
if p.NumError > 0 {
|
||||
return
|
||||
}
|
||||
p.load()
|
||||
if p.NumError > 0 {
|
||||
return
|
||||
}
|
||||
p.debug()
|
||||
if p.NumError > 0 {
|
||||
return
|
||||
}
|
||||
p.write(w)
|
||||
}
|
||||
|
||||
// init initializes p for use by the other methods.
|
||||
func (p *Prog) init() {
|
||||
// Set default context if not overridden.
|
||||
if p.GOOS == "" {
|
||||
p.GOOS = build.Default.GOOS
|
||||
}
|
||||
if p.GOARCH == "" {
|
||||
p.GOARCH = build.Default.GOARCH
|
||||
}
|
||||
if p.Format == "" {
|
||||
p.Format = goosFormat[p.GOOS]
|
||||
if p.Format == "" {
|
||||
p.errorf("no default file format for GOOS %q", p.GOOS)
|
||||
return
|
||||
}
|
||||
}
|
||||
if p.StartSym == "" {
|
||||
p.StartSym = fmt.Sprintf("_rt0_%s_%s", p.GOARCH, p.GOOS)
|
||||
}
|
||||
|
||||
// Derive internal context.
|
||||
p.formatter = formatters[p.Format]
|
||||
if p.formatter == nil {
|
||||
p.errorf("unknown output file format %q", p.Format)
|
||||
return
|
||||
}
|
||||
p.startSym = goobj.SymID{Name: p.StartSym}
|
||||
arch, ok := arches[p.GOARCH]
|
||||
if !ok {
|
||||
p.errorf("unknown GOOS %q", p.GOOS)
|
||||
return
|
||||
}
|
||||
p.arch = arch
|
||||
|
||||
p.pkgdir = fmt.Sprintf("%s/pkg/%s_%s", runtime.GOROOT(), p.GOOS, p.GOARCH)
|
||||
}
|
||||
|
||||
// goosFormat records the default format for each known GOOS value.
|
||||
var goosFormat = map[string]string{
|
||||
"darwin": "darwin",
|
||||
}
|
||||
|
||||
// formatters records the format implementation for each known format value.
|
||||
var formatters = map[string]formatter{
|
||||
"darwin": machoFormat{},
|
||||
}
|
||||
|
||||
var arches = map[string]arch{
|
||||
"amd64": {
|
||||
byteorder: binary.LittleEndian,
|
||||
ptrsize: 8,
|
||||
pcquantum: 1,
|
||||
},
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// shiftProg adjusts the addresses in p.
|
||||
// It adds vdelta to all virtual addresses and fdelta to all file offsets.
|
||||
func shiftProg(p *Prog, vdelta Addr, fdelta Addr) {
|
||||
p.Entry += vdelta
|
||||
for _, seg := range p.Segments {
|
||||
seg.FileOffset += fdelta
|
||||
seg.VirtAddr += vdelta
|
||||
for _, sect := range seg.Sections {
|
||||
sect.VirtAddr += vdelta
|
||||
for _, sym := range sect.Syms {
|
||||
sym.Addr += vdelta
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// diffProg returns a list of differences between p and q,
|
||||
// assuming p is being checked and q is the correct answer.
|
||||
func diffProg(p, q *Prog) []string {
|
||||
var errors []string
|
||||
if p.UnmappedSize != q.UnmappedSize {
|
||||
errors = append(errors, fmt.Sprintf("p.UnmappedSize = %#x, want %#x", p.UnmappedSize, q.UnmappedSize))
|
||||
}
|
||||
if p.HeaderSize != q.HeaderSize {
|
||||
errors = append(errors, fmt.Sprintf("p.HeaderSize = %#x, want %#x", p.HeaderSize, q.HeaderSize))
|
||||
}
|
||||
if p.Entry != q.Entry {
|
||||
errors = append(errors, fmt.Sprintf("p.Entry = %#x, want %#x", p.Entry, q.Entry))
|
||||
}
|
||||
for i := 0; i < len(p.Segments) || i < len(q.Segments); i++ {
|
||||
if i >= len(p.Segments) {
|
||||
errors = append(errors, fmt.Sprintf("p missing segment %q", q.Segments[i].Name))
|
||||
continue
|
||||
}
|
||||
if i >= len(q.Segments) {
|
||||
errors = append(errors, fmt.Sprintf("p has extra segment %q", p.Segments[i].Name))
|
||||
continue
|
||||
}
|
||||
pseg := p.Segments[i]
|
||||
qseg := q.Segments[i]
|
||||
if pseg.Name != qseg.Name {
|
||||
errors = append(errors, fmt.Sprintf("segment %d Name = %q, want %q", i, pseg.Name, qseg.Name))
|
||||
continue // probably out of sync
|
||||
}
|
||||
if pseg.VirtAddr != qseg.VirtAddr {
|
||||
errors = append(errors, fmt.Sprintf("segment %q VirtAddr = %#x, want %#x", pseg.Name, pseg.VirtAddr, qseg.VirtAddr))
|
||||
}
|
||||
if pseg.VirtSize != qseg.VirtSize {
|
||||
errors = append(errors, fmt.Sprintf("segment %q VirtSize = %#x, want %#x", pseg.Name, pseg.VirtSize, qseg.VirtSize))
|
||||
}
|
||||
if pseg.FileOffset != qseg.FileOffset {
|
||||
errors = append(errors, fmt.Sprintf("segment %q FileOffset = %#x, want %#x", pseg.Name, pseg.FileOffset, qseg.FileOffset))
|
||||
}
|
||||
if pseg.FileSize != qseg.FileSize {
|
||||
errors = append(errors, fmt.Sprintf("segment %q FileSize = %#x, want %#x", pseg.Name, pseg.FileSize, qseg.FileSize))
|
||||
}
|
||||
if len(pseg.Data) != len(qseg.Data) {
|
||||
errors = append(errors, fmt.Sprintf("segment %q len(Data) = %d, want %d", pseg.Name, len(pseg.Data), len(qseg.Data)))
|
||||
} else if !bytes.Equal(pseg.Data, qseg.Data) {
|
||||
errors = append(errors, fmt.Sprintf("segment %q Data mismatch:\n\thave %x\n\twant %x", pseg.Name, pseg.Data, qseg.Data))
|
||||
}
|
||||
|
||||
for j := 0; j < len(pseg.Sections) || j < len(qseg.Sections); j++ {
|
||||
if j >= len(pseg.Sections) {
|
||||
errors = append(errors, fmt.Sprintf("segment %q missing section %q", pseg.Name, qseg.Sections[i].Name))
|
||||
continue
|
||||
}
|
||||
if j >= len(qseg.Sections) {
|
||||
errors = append(errors, fmt.Sprintf("segment %q has extra section %q", pseg.Name, pseg.Sections[i].Name))
|
||||
continue
|
||||
}
|
||||
psect := pseg.Sections[j]
|
||||
qsect := qseg.Sections[j]
|
||||
if psect.Name != qsect.Name {
|
||||
errors = append(errors, fmt.Sprintf("segment %q, section %d Name = %q, want %q", pseg.Name, j, psect.Name, qsect.Name))
|
||||
continue // probably out of sync
|
||||
}
|
||||
|
||||
if psect.VirtAddr != qsect.VirtAddr {
|
||||
errors = append(errors, fmt.Sprintf("segment %q section %q VirtAddr = %#x, want %#x", pseg.Name, psect.Name, psect.VirtAddr, qsect.VirtAddr))
|
||||
}
|
||||
if psect.Size != qsect.Size {
|
||||
errors = append(errors, fmt.Sprintf("segment %q section %q Size = %#x, want %#x", pseg.Name, psect.Name, psect.Size, qsect.Size))
|
||||
}
|
||||
if psect.Align != qsect.Align {
|
||||
errors = append(errors, fmt.Sprintf("segment %q section %q Align = %#x, want %#x", pseg.Name, psect.Name, psect.Align, qsect.Align))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
// cloneProg returns a deep copy of p.
|
||||
func cloneProg(p *Prog) *Prog {
|
||||
q := new(Prog)
|
||||
*q = *p
|
||||
q.Segments = make([]*Segment, len(p.Segments))
|
||||
for i, seg := range p.Segments {
|
||||
q.Segments[i] = cloneSegment(seg)
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
// cloneSegment returns a deep copy of seg.
|
||||
func cloneSegment(seg *Segment) *Segment {
|
||||
t := new(Segment)
|
||||
*t = *seg
|
||||
t.Sections = make([]*Section, len(seg.Sections))
|
||||
for i, sect := range seg.Sections {
|
||||
t.Sections[i] = cloneSection(sect)
|
||||
}
|
||||
t.Data = make([]byte, len(seg.Data))
|
||||
copy(t.Data, seg.Data)
|
||||
return t
|
||||
}
|
||||
|
||||
// cloneSection returns a deep copy of section.
|
||||
func cloneSection(sect *Section) *Section {
|
||||
// At the moment, there's nothing we need to make a deep copy of.
|
||||
t := new(Section)
|
||||
*t = *sect
|
||||
return t
|
||||
}
|
||||
|
||||
const saveMismatch = true
|
||||
|
||||
// checkGolden checks that data matches the named file.
|
||||
// If not, it reports the error to the test.
|
||||
func checkGolden(t *testing.T, data []byte, name string) {
|
||||
golden := mustParseHexdumpFile(t, name)
|
||||
if !bytes.Equal(data, golden) {
|
||||
if saveMismatch {
|
||||
ioutil.WriteFile(name+".raw", data, 0666)
|
||||
ioutil.WriteFile(name+".hex", []byte(hexdump(data)), 0666)
|
||||
}
|
||||
// TODO(rsc): A better diff would be nice, as needed.
|
||||
i := 0
|
||||
for i < len(data) && i < len(golden) && data[i] == golden[i] {
|
||||
i++
|
||||
}
|
||||
if i >= len(data) {
|
||||
t.Errorf("%s: output file shorter than expected: have %d bytes, want %d", name, len(data), len(golden))
|
||||
} else if i >= len(golden) {
|
||||
t.Errorf("%s: output file larger than expected: have %d bytes, want %d", name, len(data), len(golden))
|
||||
} else {
|
||||
t.Errorf("%s: output file differs at byte %d: have %#02x, want %#02x", name, i, data[i], golden[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Generation of runtime-accessible data structures.
|
||||
// See also debug.go.
|
||||
|
||||
package main
|
||||
|
||||
import "debug/goobj"
|
||||
|
||||
func (p *Prog) runtime() {
|
||||
p.pclntab()
|
||||
|
||||
// TODO: Implement garbage collection data.
|
||||
p.addSym(&Sym{
|
||||
Sym: &goobj.Sym{
|
||||
SymID: goobj.SymID{Name: "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 (
|
||||
"debug/goobj"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// scan scans all packages making up the program, starting with package main defined in mainfile.
|
||||
func (p *Prog) scan(mainfile string) {
|
||||
p.initScan()
|
||||
p.scanFile("main", mainfile)
|
||||
if len(p.Missing) > 0 && !p.omitRuntime {
|
||||
p.scanImport("runtime")
|
||||
}
|
||||
|
||||
var missing []string
|
||||
for sym := range p.Missing {
|
||||
if !p.isAuto(sym) {
|
||||
missing = append(missing, sym.String())
|
||||
}
|
||||
}
|
||||
|
||||
if missing != nil {
|
||||
sort.Strings(missing)
|
||||
for _, sym := range missing {
|
||||
p.errorf("undefined: %s", sym)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(rsc): Walk import graph to diagnose cycles.
|
||||
}
|
||||
|
||||
// initScan initializes the Prog fields needed by scan.
|
||||
func (p *Prog) initScan() {
|
||||
p.Packages = make(map[string]*Package)
|
||||
p.Syms = make(map[goobj.SymID]*Sym)
|
||||
p.Missing = make(map[goobj.SymID]bool)
|
||||
p.Missing[p.startSym] = true
|
||||
}
|
||||
|
||||
// scanFile reads file to learn about the package with the given import path.
|
||||
func (p *Prog) scanFile(pkgpath string, file string) {
|
||||
pkg := &Package{
|
||||
File: file,
|
||||
}
|
||||
p.Packages[pkgpath] = pkg
|
||||
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
p.errorf("%v", err)
|
||||
return
|
||||
}
|
||||
gp, err := goobj.Parse(f, pkgpath)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
p.errorf("reading %s: %v", file, err)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(rsc): Change debug/goobj to record package name as gp.Name.
|
||||
// TODO(rsc): If pkgpath == "main", check that gp.Name == "main".
|
||||
|
||||
pkg.Package = gp
|
||||
|
||||
for _, gs := range gp.Syms {
|
||||
// TODO(rsc): Fix file format instead of this workaround.
|
||||
if gs.Data.Size > 0 {
|
||||
switch gs.Kind {
|
||||
case goobj.SBSS:
|
||||
gs.Kind = goobj.SDATA
|
||||
case goobj.SNOPTRBSS:
|
||||
gs.Kind = goobj.SNOPTRDATA
|
||||
}
|
||||
}
|
||||
|
||||
if gs.Version != 0 {
|
||||
gs.Version += p.MaxVersion
|
||||
}
|
||||
for i := range gs.Reloc {
|
||||
r := &gs.Reloc[i]
|
||||
if r.Sym.Version != 0 {
|
||||
r.Sym.Version += p.MaxVersion
|
||||
}
|
||||
if p.Syms[r.Sym] == nil {
|
||||
p.Missing[r.Sym] = true
|
||||
}
|
||||
}
|
||||
if gs.Func != nil {
|
||||
for i := range gs.Func.FuncData {
|
||||
fdata := &gs.Func.FuncData[i]
|
||||
if fdata.Sym.Name != "" {
|
||||
if fdata.Sym.Version != 0 {
|
||||
fdata.Sym.Version += p.MaxVersion
|
||||
}
|
||||
if p.Syms[fdata.Sym] == nil {
|
||||
p.Missing[fdata.Sym] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if old := p.Syms[gs.SymID]; old != nil {
|
||||
// Duplicate definition of symbol. Is it okay?
|
||||
// TODO(rsc): Write test for this code.
|
||||
switch {
|
||||
// If both symbols are BSS (no data), take max of sizes
|
||||
// but otherwise ignore second symbol.
|
||||
case old.Data.Size == 0 && gs.Data.Size == 0:
|
||||
if old.Size < gs.Size {
|
||||
old.Size = gs.Size
|
||||
}
|
||||
continue
|
||||
|
||||
// If one is in BSS and one is not, use the one that is not.
|
||||
case old.Data.Size > 0 && gs.Data.Size == 0:
|
||||
continue
|
||||
case gs.Data.Size > 0 && old.Data.Size == 0:
|
||||
break // install gs as new symbol below
|
||||
|
||||
// If either is marked as DupOK, we can keep either one.
|
||||
// Keep the one that we saw first.
|
||||
case old.DupOK || gs.DupOK:
|
||||
continue
|
||||
|
||||
// Otherwise, there's an actual conflict:
|
||||
default:
|
||||
p.errorf("symbol %s defined in both %s and %s %v %v", gs.SymID, old.Package.File, file, old.Data, gs.Data)
|
||||
continue
|
||||
}
|
||||
}
|
||||
s := &Sym{
|
||||
Sym: gs,
|
||||
Package: pkg,
|
||||
}
|
||||
p.addSym(s)
|
||||
delete(p.Missing, gs.SymID)
|
||||
|
||||
if s.Data.Size > int64(s.Size) {
|
||||
p.errorf("%s: initialized data larger than symbol (%d > %d)", s, s.Data.Size, s.Size)
|
||||
}
|
||||
}
|
||||
p.MaxVersion += pkg.MaxVersion
|
||||
|
||||
for i, pkgpath := range pkg.Imports {
|
||||
// TODO(rsc): Fix file format to drop .a from recorded import path.
|
||||
pkgpath = strings.TrimSuffix(pkgpath, ".a")
|
||||
pkg.Imports[i] = pkgpath
|
||||
|
||||
p.scanImport(pkgpath)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Prog) addSym(s *Sym) {
|
||||
pkg := s.Package
|
||||
if pkg == nil {
|
||||
pkg = p.Packages[""]
|
||||
if pkg == nil {
|
||||
pkg = &Package{}
|
||||
p.Packages[""] = pkg
|
||||
}
|
||||
s.Package = pkg
|
||||
}
|
||||
pkg.Syms = append(pkg.Syms, s)
|
||||
p.Syms[s.SymID] = s
|
||||
p.SymOrder = append(p.SymOrder, s)
|
||||
}
|
||||
|
||||
// scanImport finds the object file for the given import path and then scans it.
|
||||
func (p *Prog) scanImport(pkgpath string) {
|
||||
if p.Packages[pkgpath] != nil {
|
||||
return // already loaded
|
||||
}
|
||||
|
||||
// TODO(rsc): Implement correct search to find file.
|
||||
p.scanFile(pkgpath, p.pkgdir+"/"+pkgpath+".a")
|
||||
}
|
||||
15
src/cmd/link/testdata/Makefile
vendored
15
src/cmd/link/testdata/Makefile
vendored
@@ -1,15 +0,0 @@
|
||||
ALL=\
|
||||
autosection.6\
|
||||
autoweak.6\
|
||||
dead.6\
|
||||
hello.6\
|
||||
layout.6\
|
||||
pclntab.6\
|
||||
|
||||
all: $(ALL)
|
||||
|
||||
%.6: %.s
|
||||
GOARCH=amd64 GOOS=darwin go tool 6a -trimpath=$(shell pwd) $*.s
|
||||
|
||||
pclntab.s: genpcln.go
|
||||
go run genpcln.go >pclntab.s
|
||||
BIN
src/cmd/link/testdata/autosection.6
vendored
BIN
src/cmd/link/testdata/autosection.6
vendored
Binary file not shown.
60
src/cmd/link/testdata/autosection.s
vendored
60
src/cmd/link/testdata/autosection.s
vendored
@@ -1,60 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Test of section-named symbols.
|
||||
|
||||
#include "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/link/testdata/autoweak.6
vendored
BIN
src/cmd/link/testdata/autoweak.6
vendored
Binary file not shown.
30
src/cmd/link/testdata/autoweak.s
vendored
30
src/cmd/link/testdata/autoweak.s
vendored
@@ -1,30 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Test of go.weak symbols.
|
||||
|
||||
TEXT start(SB),7,$0
|
||||
MOVQ $autotab(SB),AX
|
||||
MOVQ $autoptr(SB),AX
|
||||
RET
|
||||
|
||||
// go.weak.sym should resolve to sym, because sym is in the binary.
|
||||
DATA autotab+0(SB)/8, $go·weak·sym(SB)
|
||||
DATA autotab+8(SB)/8, $sym(SB)
|
||||
|
||||
// go.weak.missingsym should resolve to 0, because missingsym is not in the binary.
|
||||
DATA autotab+16(SB)/8, $go·weak·missingsym(SB)
|
||||
DATA autotab+24(SB)/8, $0
|
||||
|
||||
// go.weak.deadsym should resolve to 0, because deadsym is discarded during dead code removal
|
||||
DATA autotab+32(SB)/8, $go·weak·deadsym(SB)
|
||||
DATA autotab+40(SB)/8, $0
|
||||
|
||||
GLOBL autotab(SB), $48
|
||||
|
||||
GLOBL sym(SB), $1
|
||||
|
||||
GLOBL deadsym(SB), $1
|
||||
|
||||
GLOBL autoptr(SB), $0
|
||||
BIN
src/cmd/link/testdata/dead.6
vendored
BIN
src/cmd/link/testdata/dead.6
vendored
Binary file not shown.
48
src/cmd/link/testdata/dead.s
vendored
48
src/cmd/link/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/link/testdata/genpcln.go
vendored
112
src/cmd/link/testdata/genpcln.go
vendored
@@ -1,112 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This program generates a .s file using a pseudorandom
|
||||
// value stream for the runtime function data.
|
||||
// The pclntab test checks that the linked copy
|
||||
// still has the same pseudorandom value stream.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Printf("// generated by genpcln.go; do not edit\n\n")
|
||||
for f := 0; f < 3; f++ {
|
||||
r := rand.New(rand.NewSource(int64(f)))
|
||||
file := "input"
|
||||
line := 1
|
||||
args := r.Intn(100) * 8
|
||||
frame := 32 + r.Intn(32)/8*8
|
||||
fmt.Printf("#line %d %q\n", line, file)
|
||||
fmt.Printf("TEXT func%d(SB),7,$%d-%d\n", f, frame, args)
|
||||
fmt.Printf("\tFUNCDATA $1, funcdata%d(SB)\n", f)
|
||||
fmt.Printf("#line %d %q\n", line, file)
|
||||
size := 200 + r.Intn(100)*8
|
||||
spadj := 0
|
||||
flushed := 0
|
||||
firstpc := 4
|
||||
flush := func(i int) {
|
||||
for i-flushed >= 10 {
|
||||
fmt.Printf("#line %d %q\n", line, file)
|
||||
fmt.Printf("/*%#04x*/\tMOVQ $0x123456789, AX\n", firstpc+flushed)
|
||||
flushed += 10
|
||||
}
|
||||
for i-flushed >= 5 {
|
||||
fmt.Printf("#line %d %q\n", line, file)
|
||||
fmt.Printf("/*%#04x*/\tMOVL $0x1234567, AX\n", firstpc+flushed)
|
||||
flushed += 5
|
||||
}
|
||||
for i-flushed > 0 {
|
||||
fmt.Printf("#line %d %q\n", line, file)
|
||||
fmt.Printf("/*%#04x*/\tBYTE $0\n", firstpc+flushed)
|
||||
flushed++
|
||||
}
|
||||
}
|
||||
for i := 0; i < size; i++ {
|
||||
// Possible SP adjustment.
|
||||
if r.Intn(100) == 0 {
|
||||
flush(i)
|
||||
fmt.Printf("#line %d %q\n", line, file)
|
||||
if spadj <= -32 || spadj < 32 && r.Intn(2) == 0 {
|
||||
spadj += 8
|
||||
fmt.Printf("/*%#04x*/\tPUSHQ AX\n", firstpc+i)
|
||||
} else {
|
||||
spadj -= 8
|
||||
fmt.Printf("/*%#04x*/\tPOPQ AX\n", firstpc+i)
|
||||
}
|
||||
i += 1
|
||||
flushed = i
|
||||
}
|
||||
|
||||
// Possible PCFile change.
|
||||
if r.Intn(100) == 0 {
|
||||
flush(i)
|
||||
file = fmt.Sprintf("file%d.s", r.Intn(10))
|
||||
line = r.Intn(100) + 1
|
||||
}
|
||||
|
||||
// Possible PCLine change.
|
||||
if r.Intn(10) == 0 {
|
||||
flush(i)
|
||||
line = r.Intn(1000) + 1
|
||||
}
|
||||
|
||||
// Possible PCData $1 change.
|
||||
if r.Intn(100) == 0 {
|
||||
flush(i)
|
||||
fmt.Printf("/*%6s*/\tPCDATA $1, $%d\n", "", r.Intn(1000))
|
||||
}
|
||||
|
||||
// Possible PCData $2 change.
|
||||
if r.Intn(100) == 0 {
|
||||
flush(i)
|
||||
fmt.Printf("/*%6s*/\tPCDATA $2, $%d\n", "", r.Intn(1000))
|
||||
}
|
||||
}
|
||||
flush(size)
|
||||
for spadj < 0 {
|
||||
fmt.Printf("\tPUSHQ AX\n")
|
||||
spadj += 8
|
||||
}
|
||||
for spadj > 0 {
|
||||
fmt.Printf("\tPOPQ AX\n")
|
||||
spadj -= 8
|
||||
}
|
||||
fmt.Printf("\tRET\n")
|
||||
|
||||
fmt.Printf("\n")
|
||||
fmt.Printf("GLOBL funcdata%d(SB), $16\n", f)
|
||||
}
|
||||
|
||||
fmt.Printf("\nTEXT start(SB),7,$0\n")
|
||||
for f := 0; f < 3; f++ {
|
||||
fmt.Printf("\tCALL func%d(SB)\n", f)
|
||||
}
|
||||
fmt.Printf("\tMOVQ $runtime·pclntab(SB), AX\n")
|
||||
fmt.Printf("\n\tRET\n")
|
||||
}
|
||||
BIN
src/cmd/link/testdata/hello.6
vendored
BIN
src/cmd/link/testdata/hello.6
vendored
Binary file not shown.
15
src/cmd/link/testdata/hello.s
vendored
15
src/cmd/link/testdata/hello.s
vendored
@@ -1,15 +0,0 @@
|
||||
TEXT _rt0_go(SB),7,$0
|
||||
MOVL $1, DI
|
||||
MOVL $hello<>(SB), SI
|
||||
MOVL $12, DX
|
||||
MOVL $0x2000004, AX
|
||||
SYSCALL
|
||||
MOVL $0, DI
|
||||
MOVL $0x2000001, AX
|
||||
SYSCALL
|
||||
RET
|
||||
|
||||
DATA hello<>+0(SB)/4, $"hell"
|
||||
DATA hello<>+4(SB)/4, $"o wo"
|
||||
DATA hello<>+8(SB)/4, $"rld\n"
|
||||
GLOBL hello<>(SB), $12
|
||||
BIN
src/cmd/link/testdata/layout.6
vendored
BIN
src/cmd/link/testdata/layout.6
vendored
Binary file not shown.
29
src/cmd/link/testdata/layout.s
vendored
29
src/cmd/link/testdata/layout.s
vendored
@@ -1,29 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Test of section assignment in layout.go.
|
||||
// Each symbol should end up in the section named by the symbol name prefix (up to the underscore).
|
||||
|
||||
#include "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
|
||||
54
src/cmd/link/testdata/link.hello.darwin.amd64
vendored
54
src/cmd/link/testdata/link.hello.darwin.amd64
vendored
@@ -1,54 +0,0 @@
|
||||
00000000 cf fa ed fe 07 00 00 01 03 00 00 00 02 00 00 00 |................|
|
||||
00000010 04 00 00 00 d0 02 00 00 01 00 00 00 00 00 00 00 |................|
|
||||
00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE|
|
||||
00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............|
|
||||
00000040 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00000060 00 00 00 00 00 00 00 00 19 00 00 00 38 01 00 00 |............8...|
|
||||
00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
00000080 00 10 00 00 00 00 00 00 b0 10 00 00 00 00 00 00 |................|
|
||||
00000090 00 00 00 00 00 00 00 00 b0 10 00 00 00 00 00 00 |................|
|
||||
000000a0 07 00 00 00 05 00 00 00 03 00 00 00 00 00 00 00 |................|
|
||||
000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........|
|
||||
000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
000000d0 00 20 00 00 00 00 00 00 20 00 00 00 00 00 00 00 |. ...... .......|
|
||||
000000e0 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
000000f0 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000100 5f 5f 72 6f 64 61 74 61 00 00 00 00 00 00 00 00 |__rodata........|
|
||||
00000110 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
00000120 20 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ..............|
|
||||
00000130 20 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ...............|
|
||||
*
|
||||
00000150 5f 5f 66 75 6e 63 74 61 62 00 00 00 00 00 00 00 |__functab.......|
|
||||
00000160 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
00000170 20 20 00 00 00 00 00 00 90 00 00 00 00 00 00 00 | ..............|
|
||||
00000180 20 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ...............|
|
||||
*
|
||||
000001a0 19 00 00 00 98 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..|
|
||||
000001b0 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 |.........0......|
|
||||
000001c0 0c 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......|
|
||||
000001d0 0c 00 00 00 00 00 00 00 03 00 00 00 03 00 00 00 |................|
|
||||
000001e0 01 00 00 00 00 00 00 00 5f 5f 64 61 74 61 00 00 |........__data..|
|
||||
000001f0 00 00 00 00 00 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..|
|
||||
00000200 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 |.........0......|
|
||||
00000210 0c 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......|
|
||||
*
|
||||
00000230 00 00 00 00 00 00 00 00 05 00 00 00 b8 00 00 00 |................|
|
||||
00000240 04 00 00 00 2a 00 00 00 00 00 00 00 00 00 00 00 |....*...........|
|
||||
*
|
||||
000002c0 00 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......|
|
||||
*
|
||||
00001000 bf 01 00 00 00 be 00 30 00 00 ba 0c 00 00 00 b8 |.......0........|
|
||||
00001010 04 00 00 02 0f 05 31 ff b8 01 00 00 02 0f 05 c3 |......1.........|
|
||||
00001020 fb ff ff ff 00 00 01 08 01 00 00 00 00 00 00 00 |................|
|
||||
00001030 00 20 00 00 00 00 00 00 30 00 00 00 00 00 00 00 |. ......0.......|
|
||||
00001040 20 20 00 00 00 00 00 00 80 00 00 00 00 00 00 00 | ..............|
|
||||
00001050 00 20 00 00 00 00 00 00 58 00 00 00 00 00 00 80 |. ......X.......|
|
||||
00001060 08 00 00 00 60 00 00 00 63 00 00 00 66 00 00 00 |....`...c...f...|
|
||||
00001070 00 00 00 00 00 00 00 00 5f 72 74 30 5f 67 6f 00 |........_rt0_go.|
|
||||
00001080 02 20 00 04 20 00 06 05 02 05 02 05 02 05 02 02 |. .. ...........|
|
||||
00001090 02 02 02 05 02 02 02 01 00 00 00 00 00 00 00 00 |................|
|
||||
000010a0 02 00 00 00 88 00 00 00 68 65 6c 6c 6f 2e 73 00 |........hello.s.|
|
||||
*
|
||||
00002000 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a |hello world.|
|
||||
0000200c
|
||||
24
src/cmd/link/testdata/macho.amd64.exit9
vendored
24
src/cmd/link/testdata/macho.amd64.exit9
vendored
@@ -1,24 +0,0 @@
|
||||
00000000 cf fa ed fe 07 00 00 01 03 00 00 00 02 00 00 00 |................|
|
||||
00000010 03 00 00 00 98 01 00 00 01 00 00 00 00 00 00 00 |................|
|
||||
00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE|
|
||||
00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............|
|
||||
00000040 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000060 00 00 00 00 00 00 00 00 19 00 00 00 98 00 00 00 |................|
|
||||
00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
00000080 00 10 00 00 00 00 00 00 0d 10 00 00 00 00 00 00 |................|
|
||||
00000090 00 00 00 00 00 00 00 00 0d 10 00 00 00 00 00 00 |................|
|
||||
000000a0 07 00 00 00 05 00 00 00 01 00 00 00 00 00 00 00 |................|
|
||||
000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........|
|
||||
000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
000000d0 00 20 00 00 00 00 00 00 0d 00 00 00 00 00 00 00 |. ..............|
|
||||
000000e0 00 10 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
000000f0 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000100 05 00 00 00 b8 00 00 00 04 00 00 00 2a 00 00 00 |............*...|
|
||||
00000110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00000190 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |. ..............|
|
||||
000001a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00001000 b8 01 00 00 02 bf 09 00 00 00 0f 05 f4 |.............|
|
||||
0000100d
|
||||
39
src/cmd/link/testdata/macho.amd64.hello
vendored
39
src/cmd/link/testdata/macho.amd64.hello
vendored
@@ -1,39 +0,0 @@
|
||||
00000000 cf fa ed fe 07 00 00 01 03 00 00 00 02 00 00 00 |................|
|
||||
00000010 04 00 00 00 30 02 00 00 01 00 00 00 00 00 00 00 |....0...........|
|
||||
00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE|
|
||||
00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............|
|
||||
00000040 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000060 00 00 00 00 00 00 00 00 19 00 00 00 98 00 00 00 |................|
|
||||
00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
00000080 00 10 00 00 00 00 00 00 23 10 00 00 00 00 00 00 |........#.......|
|
||||
00000090 00 00 00 00 00 00 00 00 23 10 00 00 00 00 00 00 |........#.......|
|
||||
000000a0 07 00 00 00 05 00 00 00 01 00 00 00 00 00 00 00 |................|
|
||||
000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........|
|
||||
000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
000000d0 00 20 00 00 00 00 00 00 23 00 00 00 00 00 00 00 |. ......#.......|
|
||||
000000e0 00 10 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
000000f0 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000100 19 00 00 00 98 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..|
|
||||
00000110 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 |.........0......|
|
||||
00000120 0c 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......|
|
||||
00000130 0c 00 00 00 00 00 00 00 03 00 00 00 03 00 00 00 |................|
|
||||
00000140 01 00 00 00 00 00 00 00 5f 5f 64 61 74 61 00 00 |........__data..|
|
||||
00000150 00 00 00 00 00 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..|
|
||||
00000160 00 00 00 00 00 00 00 00 00 30 00 00 00 00 00 00 |.........0......|
|
||||
00000170 0c 00 00 00 00 00 00 00 00 20 00 00 06 00 00 00 |......... ......|
|
||||
00000180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000190 00 00 00 00 00 00 00 00 05 00 00 00 b8 00 00 00 |................|
|
||||
000001a0 04 00 00 00 2a 00 00 00 00 00 00 00 00 00 00 00 |....*...........|
|
||||
000001b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00000220 00 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 |......... ......|
|
||||
00000230 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00001000 b8 04 00 00 02 bf 01 00 00 00 be 00 30 00 00 ba |............0...|
|
||||
00001010 0c 00 00 00 0f 05 b8 01 00 00 02 bf 09 00 00 00 |................|
|
||||
00001020 0f 05 f4 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00001030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00002000 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a |hello world.|
|
||||
0000200c
|
||||
34
src/cmd/link/testdata/macho.amd64.helloro
vendored
34
src/cmd/link/testdata/macho.amd64.helloro
vendored
@@ -1,34 +0,0 @@
|
||||
00000000 cf fa ed fe 07 00 00 01 03 00 00 00 02 00 00 00 |................|
|
||||
00000010 03 00 00 00 e8 01 00 00 01 00 00 00 00 00 00 00 |................|
|
||||
00000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 |....H...__PAGEZE|
|
||||
00000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |RO..............|
|
||||
00000040 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000060 00 00 00 00 00 00 00 00 19 00 00 00 e8 00 00 00 |................|
|
||||
00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
00000080 00 10 00 00 00 00 00 00 0c 20 00 00 00 00 00 00 |......... ......|
|
||||
00000090 00 00 00 00 00 00 00 00 0c 20 00 00 00 00 00 00 |......... ......|
|
||||
000000a0 07 00 00 00 05 00 00 00 02 00 00 00 00 00 00 00 |................|
|
||||
000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........|
|
||||
000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
000000d0 00 20 00 00 00 00 00 00 23 00 00 00 00 00 00 00 |. ......#.......|
|
||||
000000e0 00 10 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
000000f0 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000100 5f 5f 72 6f 64 61 74 61 00 00 00 00 00 00 00 00 |__rodata........|
|
||||
00000110 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........|
|
||||
00000120 00 30 00 00 00 00 00 00 0c 00 00 00 00 00 00 00 |.0..............|
|
||||
00000130 00 20 00 00 06 00 00 00 00 00 00 00 00 00 00 00 |. ..............|
|
||||
00000140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00000150 05 00 00 00 b8 00 00 00 04 00 00 00 2a 00 00 00 |............*...|
|
||||
00000160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
000001e0 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |. ..............|
|
||||
000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00001000 b8 04 00 00 02 bf 01 00 00 00 be 00 30 00 00 ba |............0...|
|
||||
00001010 0c 00 00 00 0f 05 b8 01 00 00 02 bf 00 00 00 00 |................|
|
||||
00001020 0f 05 f4 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
00001030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
||||
*
|
||||
00002000 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a |hello world.|
|
||||
0000200c
|
||||
BIN
src/cmd/link/testdata/pclntab.6
vendored
BIN
src/cmd/link/testdata/pclntab.6
vendored
Binary file not shown.
1751
src/cmd/link/testdata/pclntab.s
vendored
1751
src/cmd/link/testdata/pclntab.s
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,11 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
// round returns size rounded up to the next multiple of align;
|
||||
// align must be a power of two.
|
||||
func round(size, align Addr) Addr {
|
||||
return (size + align - 1) &^ (align - 1)
|
||||
}
|
||||
@@ -1,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)
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"cmd/pprof/internal/plugin"
|
||||
@@ -71,15 +72,27 @@ func PProf(c Completer, interactive **bool, svgpan **string) Commands {
|
||||
"eog": {c, report.Dot, invokeVisualizer(interactive, invokeDot("svg"), "svg", []string{"eog"}), false, "Visualize graph through eog"},
|
||||
"evince": {c, report.Dot, invokeVisualizer(interactive, invokeDot("pdf"), "pdf", []string{"evince"}), false, "Visualize graph through evince"},
|
||||
"gv": {c, report.Dot, invokeVisualizer(interactive, invokeDot("ps"), "ps", []string{"gv --noantialias"}), false, "Visualize graph through gv"},
|
||||
"web": {c, report.Dot, invokeVisualizer(interactive, saveSVGToFile(svgpan), "svg", browsers), false, "Visualize graph through web browser"},
|
||||
"web": {c, report.Dot, invokeVisualizer(interactive, saveSVGToFile(svgpan), "svg", browsers()), false, "Visualize graph through web browser"},
|
||||
|
||||
// Visualize HTML directly generated by report.
|
||||
"weblist": {c, report.WebList, invokeVisualizer(interactive, awayFromTTY("html"), "html", browsers), true, "Output annotated source in HTML for functions matching regexp or address"},
|
||||
"weblist": {c, report.WebList, invokeVisualizer(interactive, awayFromTTY("html"), "html", browsers()), true, "Output annotated source in HTML for functions matching regexp or address"},
|
||||
}
|
||||
}
|
||||
|
||||
// List of web browsers to attempt for web visualization
|
||||
var browsers = []string{"chrome", "google-chrome", "firefox", "/usr/bin/open"}
|
||||
// browsers returns a list of commands to attempt for web visualization
|
||||
// on the current platform
|
||||
func browsers() []string {
|
||||
cmds := []string{"chrome", "google-chrome", "firefox"}
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
cmds = append(cmds, "/usr/bin/open")
|
||||
case "windows":
|
||||
cmds = append(cmds, "cmd /c start")
|
||||
default:
|
||||
cmds = append(cmds, "xdg-open")
|
||||
}
|
||||
return cmds
|
||||
}
|
||||
|
||||
// NewCompleter creates an autocompletion function for a set of commands.
|
||||
func NewCompleter(cs Commands) Completer {
|
||||
@@ -142,6 +155,10 @@ func awayFromTTY(format string) PostProcessor {
|
||||
func invokeDot(format string) PostProcessor {
|
||||
divert := awayFromTTY(format)
|
||||
return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error {
|
||||
if _, err := exec.LookPath("dot"); err != nil {
|
||||
ui.PrintErr("Cannot find dot, have you installed Graphviz?")
|
||||
return err
|
||||
}
|
||||
cmd := exec.Command("dot", "-T"+format)
|
||||
var buf bytes.Buffer
|
||||
cmd.Stdin, cmd.Stdout, cmd.Stderr = input, &buf, os.Stderr
|
||||
@@ -174,6 +191,7 @@ func invokeVisualizer(interactive **bool, format PostProcessor, suffix string, v
|
||||
if err = format(input, tempFile, ui); err != nil {
|
||||
return err
|
||||
}
|
||||
tempFile.Close() // on windows, if the file is Open, start cannot access it.
|
||||
// Try visualizers until one is successful
|
||||
for _, v := range visualizers {
|
||||
// Separate command and arguments for exec.Command.
|
||||
|
||||
@@ -32,6 +32,10 @@ func Symbolize(mode string, prof *profile.Profile, obj plugin.ObjTool, ui plugin
|
||||
}
|
||||
}
|
||||
|
||||
if len(prof.Mapping) == 0 {
|
||||
return fmt.Errorf("no known mappings")
|
||||
}
|
||||
|
||||
mt, err := newMapping(prof, obj, ui, force)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -29,7 +29,6 @@ import (
|
||||
type SyntaxError struct {
|
||||
Msg string
|
||||
Line int
|
||||
Byte int64 // byte offset from start of stream
|
||||
}
|
||||
|
||||
func (e *SyntaxError) Error() string {
|
||||
|
||||
@@ -1310,11 +1310,13 @@ func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool {
|
||||
// auto-tagging to apply only to files with a non-empty prefix, so
|
||||
// "foo_linux.go" is tagged but "linux.go" is not. This allows new operating
|
||||
// sytems, such as android, to arrive without breaking existing code with
|
||||
// innocuous source code in "android.go". The easiest fix: files without
|
||||
// underscores are always included.
|
||||
if !strings.ContainsRune(name, '_') {
|
||||
// innocuous source code in "android.go". The easiest fix: cut everything
|
||||
// in the name before the initial _.
|
||||
i := strings.Index(name, "_")
|
||||
if i < 0 {
|
||||
return true
|
||||
}
|
||||
name = name[i:] // ignore everything before first _
|
||||
|
||||
l := strings.Split(name, "_")
|
||||
if n := len(l); n > 0 && l[n-1] == "test" {
|
||||
|
||||
@@ -189,6 +189,7 @@ var matchFileTests = []struct {
|
||||
{ctxtAndroid, "foo_plan9.go", "", false},
|
||||
{ctxtAndroid, "android.go", "", true},
|
||||
{ctxtAndroid, "plan9.go", "", true},
|
||||
{ctxtAndroid, "plan9_test.go", "", true},
|
||||
{ctxtAndroid, "arm.s", "", true},
|
||||
{ctxtAndroid, "amd64.s", "", true},
|
||||
}
|
||||
|
||||
@@ -143,6 +143,9 @@ func (d *decoder) fill() error {
|
||||
// Fill in the rest of the buffer.
|
||||
n, err := d.r.Read(d.bytes.buf[d.bytes.j:])
|
||||
d.bytes.j += n
|
||||
if n > 0 {
|
||||
err = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
@@ -88,6 +89,51 @@ func decodeFile(filename string) (image.Image, error) {
|
||||
return Decode(f)
|
||||
}
|
||||
|
||||
type eofReader struct {
|
||||
data []byte // deliver from Read without EOF
|
||||
dataEOF []byte // then deliver from Read with EOF on last chunk
|
||||
lenAtEOF int
|
||||
}
|
||||
|
||||
func (r *eofReader) Read(b []byte) (n int, err error) {
|
||||
if len(r.data) > 0 {
|
||||
n = copy(b, r.data)
|
||||
r.data = r.data[n:]
|
||||
} else {
|
||||
n = copy(b, r.dataEOF)
|
||||
r.dataEOF = r.dataEOF[n:]
|
||||
if len(r.dataEOF) == 0 {
|
||||
err = io.EOF
|
||||
if r.lenAtEOF == -1 {
|
||||
r.lenAtEOF = n
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func TestDecodeEOF(t *testing.T) {
|
||||
// Check that if reader returns final data and EOF at same time, jpeg handles it.
|
||||
data, err := ioutil.ReadFile("../testdata/video-001.jpeg")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
n := len(data)
|
||||
for i := 0; i < n; {
|
||||
r := &eofReader{data[:n-i], data[n-i:], -1}
|
||||
_, err := Decode(r)
|
||||
if err != nil {
|
||||
t.Errorf("Decode with Read() = %d, EOF: %v", r.lenAtEOF, err)
|
||||
}
|
||||
if i == 0 {
|
||||
i = 1
|
||||
} else {
|
||||
i *= 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check checks that the two pix data are equal, within the given bounds.
|
||||
func check(bounds image.Rectangle, pix0, pix1 []byte, stride0, stride1 int) error {
|
||||
if stride0 <= 0 || stride0%8 != 0 {
|
||||
|
||||
@@ -887,7 +887,7 @@ func (z *Int) AndNot(x, y *Int) *Int {
|
||||
}
|
||||
|
||||
// x &^ (-y) == x &^ ^(y-1) == x & (y-1)
|
||||
y1 := nat(nil).add(y.abs, natOne)
|
||||
y1 := nat(nil).sub(y.abs, natOne)
|
||||
z.abs = z.abs.and(x.abs, y1)
|
||||
z.neg = false
|
||||
return z
|
||||
|
||||
@@ -1201,6 +1201,7 @@ var bitwiseTests = []struct {
|
||||
{"-0x01", "-0x01", "-0x01", "-0x01", "0x00", "0x00"},
|
||||
{"0x07", "0x08", "0x00", "0x0f", "0x0f", "0x07"},
|
||||
{"0x05", "0x0f", "0x05", "0x0f", "0x0a", "0x00"},
|
||||
{"0xff", "-0x0a", "0xf6", "-0x01", "-0xf7", "0x09"},
|
||||
{"0x013ff6", "0x9a4e", "0x1a46", "0x01bffe", "0x01a5b8", "0x0125b0"},
|
||||
{"-0x013ff6", "0x9a4e", "0x800a", "-0x0125b2", "-0x01a5bc", "-0x01c000"},
|
||||
{"-0x013ff6", "-0x9a4e", "-0x01bffe", "-0x1a46", "0x01a5b8", "0x8008"},
|
||||
|
||||
@@ -4055,3 +4055,104 @@ func TestLargeGCProg(t *testing.T) {
|
||||
fv := ValueOf(func([256]*byte) {})
|
||||
fv.Call([]Value{ValueOf([256]*byte{})})
|
||||
}
|
||||
|
||||
// Issue 9179.
|
||||
func TestCallGC(t *testing.T) {
|
||||
f := func(a, b, c, d, e string) {
|
||||
}
|
||||
g := func(in []Value) []Value {
|
||||
runtime.GC()
|
||||
return nil
|
||||
}
|
||||
typ := ValueOf(f).Type()
|
||||
f2 := MakeFunc(typ, g).Interface().(func(string, string, string, string, string))
|
||||
f2("four", "five5", "six666", "seven77", "eight888")
|
||||
}
|
||||
|
||||
type funcLayoutTest struct {
|
||||
rcvr, t Type
|
||||
argsize, retOffset uintptr
|
||||
stack []byte
|
||||
}
|
||||
|
||||
var funcLayoutTests []funcLayoutTest
|
||||
|
||||
func init() {
|
||||
var argAlign = PtrSize
|
||||
if runtime.GOARCH == "amd64p32" {
|
||||
argAlign = 2 * PtrSize
|
||||
}
|
||||
roundup := func(x uintptr, a uintptr) uintptr {
|
||||
return (x + a - 1) / a * a
|
||||
}
|
||||
|
||||
funcLayoutTests = append(funcLayoutTests,
|
||||
funcLayoutTest{
|
||||
nil,
|
||||
ValueOf(func(a, b string) string { return "" }).Type(),
|
||||
4 * PtrSize,
|
||||
4 * PtrSize,
|
||||
[]byte{BitsPointer, BitsScalar, BitsPointer},
|
||||
})
|
||||
|
||||
var r []byte
|
||||
if PtrSize == 4 {
|
||||
r = []byte{BitsScalar, BitsScalar, BitsScalar, BitsPointer}
|
||||
} else {
|
||||
r = []byte{BitsScalar, BitsScalar, BitsPointer}
|
||||
}
|
||||
funcLayoutTests = append(funcLayoutTests,
|
||||
funcLayoutTest{
|
||||
nil,
|
||||
ValueOf(func(a, b, c uint32, p *byte, d uint16) {}).Type(),
|
||||
roundup(3*4, PtrSize) + PtrSize + 2,
|
||||
roundup(roundup(3*4, PtrSize)+PtrSize+2, argAlign),
|
||||
r,
|
||||
})
|
||||
|
||||
funcLayoutTests = append(funcLayoutTests,
|
||||
funcLayoutTest{
|
||||
nil,
|
||||
ValueOf(func(a map[int]int, b uintptr, c interface{}) {}).Type(),
|
||||
4 * PtrSize,
|
||||
4 * PtrSize,
|
||||
[]byte{BitsPointer, BitsScalar, BitsPointer, BitsPointer},
|
||||
})
|
||||
|
||||
type S struct {
|
||||
a, b uintptr
|
||||
c, d *byte
|
||||
}
|
||||
funcLayoutTests = append(funcLayoutTests,
|
||||
funcLayoutTest{
|
||||
nil,
|
||||
ValueOf(func(a S) {}).Type(),
|
||||
4 * PtrSize,
|
||||
4 * PtrSize,
|
||||
[]byte{BitsScalar, BitsScalar, BitsPointer, BitsPointer},
|
||||
})
|
||||
|
||||
funcLayoutTests = append(funcLayoutTests,
|
||||
funcLayoutTest{
|
||||
ValueOf((*byte)(nil)).Type(),
|
||||
ValueOf(func(a uintptr, b *int) {}).Type(),
|
||||
3 * PtrSize,
|
||||
roundup(3*PtrSize, argAlign),
|
||||
[]byte{BitsPointer, BitsScalar, BitsPointer},
|
||||
})
|
||||
}
|
||||
|
||||
func TestFuncLayout(t *testing.T) {
|
||||
for _, lt := range funcLayoutTests {
|
||||
_, argsize, retOffset, stack := FuncLayout(lt.t, lt.rcvr)
|
||||
if argsize != lt.argsize {
|
||||
t.Errorf("funcLayout(%v, %v).argsize=%d, want %d", lt.t, lt.rcvr, argsize, lt.argsize)
|
||||
}
|
||||
if retOffset != lt.retOffset {
|
||||
t.Errorf("funcLayout(%v, %v).retOffset=%d, want %d", lt.t, lt.rcvr, retOffset, lt.retOffset)
|
||||
}
|
||||
if !bytes.Equal(stack, lt.stack) {
|
||||
t.Errorf("funcLayout(%v, %v).stack=%v, want %v", lt.t, lt.rcvr, stack, lt.stack)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,3 +17,22 @@ func IsRO(v Value) bool {
|
||||
|
||||
var ArrayOf = arrayOf
|
||||
var CallGC = &callGC
|
||||
|
||||
const PtrSize = ptrSize
|
||||
const BitsPointer = bitsPointer
|
||||
const BitsScalar = bitsScalar
|
||||
|
||||
func FuncLayout(t Type, rcvr Type) (frametype Type, argSize, retOffset uintptr, stack []byte) {
|
||||
var ft *rtype
|
||||
var s *bitVector
|
||||
if rcvr != nil {
|
||||
ft, argSize, retOffset, s = funcLayout(t.(*rtype), rcvr.(*rtype))
|
||||
} else {
|
||||
ft, argSize, retOffset, s = funcLayout(t.(*rtype), nil)
|
||||
}
|
||||
frametype = ft
|
||||
for i := uint32(0); i < s.n; i += 2 {
|
||||
stack = append(stack, s.data[i/8]>>(i%8)&3)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1498,8 +1498,9 @@ func MapOf(key, elem Type) Type {
|
||||
|
||||
// gcProg is a helper type for generatation of GC pointer info.
|
||||
type gcProg struct {
|
||||
gc []byte
|
||||
size uintptr // size of type in bytes
|
||||
gc []byte
|
||||
size uintptr // size of type in bytes
|
||||
hasPtr bool
|
||||
}
|
||||
|
||||
func (gc *gcProg) append(v byte) {
|
||||
@@ -1560,11 +1561,14 @@ func (gc *gcProg) appendWord(v byte) {
|
||||
gc.gc[nptr/2] &= ^(3 << ((nptr%2)*4 + 2))
|
||||
gc.gc[nptr/2] |= v << ((nptr%2)*4 + 2)
|
||||
gc.size += ptrsize
|
||||
if v == bitsPointer {
|
||||
gc.hasPtr = true
|
||||
}
|
||||
}
|
||||
|
||||
func (gc *gcProg) finalize() unsafe.Pointer {
|
||||
func (gc *gcProg) finalize() (unsafe.Pointer, bool) {
|
||||
if gc.size == 0 {
|
||||
return nil
|
||||
return nil, false
|
||||
}
|
||||
ptrsize := unsafe.Sizeof(uintptr(0))
|
||||
gc.align(ptrsize)
|
||||
@@ -1579,7 +1583,7 @@ func (gc *gcProg) finalize() unsafe.Pointer {
|
||||
gc.appendWord(extractGCWord(gc.gc, i))
|
||||
}
|
||||
}
|
||||
return unsafe.Pointer(&gc.gc[0])
|
||||
return unsafe.Pointer(&gc.gc[0]), gc.hasPtr
|
||||
}
|
||||
|
||||
func extractGCWord(gc []byte, i uintptr) byte {
|
||||
@@ -1624,10 +1628,6 @@ func bucketOf(ktyp, etyp *rtype) *rtype {
|
||||
for i := 0; i < int(bucketSize*unsafe.Sizeof(uint8(0))/ptrsize); i++ {
|
||||
gc.append(bitsScalar)
|
||||
}
|
||||
gc.append(bitsPointer) // overflow
|
||||
if runtime.GOARCH == "amd64p32" {
|
||||
gc.append(bitsScalar)
|
||||
}
|
||||
// keys
|
||||
for i := 0; i < bucketSize; i++ {
|
||||
gc.appendProg(ktyp)
|
||||
@@ -1636,10 +1636,15 @@ func bucketOf(ktyp, etyp *rtype) *rtype {
|
||||
for i := 0; i < bucketSize; i++ {
|
||||
gc.appendProg(etyp)
|
||||
}
|
||||
// overflow
|
||||
gc.append(bitsPointer)
|
||||
if runtime.GOARCH == "amd64p32" {
|
||||
gc.append(bitsScalar)
|
||||
}
|
||||
|
||||
b := new(rtype)
|
||||
b.size = gc.size
|
||||
b.gc[0] = gc.finalize()
|
||||
b.gc[0], _ = gc.finalize()
|
||||
s := "bucket(" + *ktyp.string + "," + *etyp.string + ")"
|
||||
b.string = &s
|
||||
return b
|
||||
@@ -1840,7 +1845,11 @@ func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uin
|
||||
// build dummy rtype holding gc program
|
||||
x := new(rtype)
|
||||
x.size = gc.size
|
||||
x.gc[0] = gc.finalize()
|
||||
var hasPtr bool
|
||||
x.gc[0], hasPtr = gc.finalize()
|
||||
if !hasPtr {
|
||||
x.kind |= kindNoPointers
|
||||
}
|
||||
var s string
|
||||
if rcvr != nil {
|
||||
s = "methodargs(" + *rcvr.string + ")(" + *t.string + ")"
|
||||
@@ -1894,14 +1903,14 @@ func addTypeBits(bv *bitVector, offset *uintptr, t *rtype) {
|
||||
switch Kind(t.kind & kindMask) {
|
||||
case Chan, Func, Map, Ptr, Slice, String, UnsafePointer:
|
||||
// 1 pointer at start of representation
|
||||
for bv.n < uint32(*offset/uintptr(ptrSize)) {
|
||||
for bv.n < 2*uint32(*offset/uintptr(ptrSize)) {
|
||||
bv.append2(bitsScalar)
|
||||
}
|
||||
bv.append2(bitsPointer)
|
||||
|
||||
case Interface:
|
||||
// 2 pointers
|
||||
for bv.n < uint32(*offset/uintptr(ptrSize)) {
|
||||
for bv.n < 2*uint32(*offset/uintptr(ptrSize)) {
|
||||
bv.append2(bitsScalar)
|
||||
}
|
||||
bv.append2(bitsPointer)
|
||||
|
||||
36
src/run.bash
36
src/run.bash
@@ -64,17 +64,6 @@ echo
|
||||
echo '# sync -cpu=10'
|
||||
go test sync -short -timeout=$(expr 120 \* $timeout_scale)s -cpu=10
|
||||
|
||||
# Race detector only supported on Linux, FreeBSD and OS X,
|
||||
# and only on amd64, and only when cgo is enabled.
|
||||
case "$GOHOSTOS-$GOOS-$GOARCH-$CGO_ENABLED" in
|
||||
linux-linux-amd64-1 | freebsd-freebsd-amd64-1 | darwin-darwin-amd64-1)
|
||||
echo
|
||||
echo '# Testing race detector.'
|
||||
go test -race -i runtime/race flag
|
||||
go test -race -run=Output runtime/race
|
||||
go test -race -short flag
|
||||
esac
|
||||
|
||||
xcd() {
|
||||
echo
|
||||
echo '#' $1
|
||||
@@ -120,6 +109,7 @@ go run $GOROOT/test/run.go - . || exit 1
|
||||
[ "$CGO_ENABLED" != 1 ] ||
|
||||
(xcd ../misc/cgo/test
|
||||
# cgo tests inspect the traceback for runtime functions
|
||||
extlink=0
|
||||
export GOTRACEBACK=2
|
||||
go test -ldflags '-linkmode=auto' || exit 1
|
||||
# linkmode=internal fails on dragonfly since errno is a TLS relocation.
|
||||
@@ -128,19 +118,24 @@ case "$GOHOSTOS-$GOARCH" in
|
||||
openbsd-386 | openbsd-amd64)
|
||||
# test linkmode=external, but __thread not supported, so skip testtls.
|
||||
go test -ldflags '-linkmode=external' || exit 1
|
||||
extlink=1
|
||||
;;
|
||||
darwin-386 | darwin-amd64)
|
||||
# linkmode=external fails on OS X 10.6 and earlier == Darwin
|
||||
# 10.8 and earlier.
|
||||
case $(uname -r) in
|
||||
[0-9].* | 10.*) ;;
|
||||
*) go test -ldflags '-linkmode=external' || exit 1;;
|
||||
*)
|
||||
go test -ldflags '-linkmode=external' || exit 1
|
||||
extlink=1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
android-arm | dragonfly-386 | dragonfly-amd64 | freebsd-386 | freebsd-amd64 | freebsd-arm | linux-386 | linux-amd64 | linux-arm | netbsd-386 | netbsd-amd64)
|
||||
go test -ldflags '-linkmode=external' || exit 1
|
||||
go test -ldflags '-linkmode=auto' ../testtls || exit 1
|
||||
go test -ldflags '-linkmode=external' ../testtls || exit 1
|
||||
extlink=1
|
||||
|
||||
case "$GOHOSTOS-$GOARCH" in
|
||||
netbsd-386 | netbsd-amd64) ;; # no static linking
|
||||
@@ -164,6 +159,23 @@ android-arm | dragonfly-386 | dragonfly-amd64 | freebsd-386 | freebsd-amd64 | fr
|
||||
esac
|
||||
) || exit $?
|
||||
|
||||
# Race detector only supported on Linux, FreeBSD and OS X,
|
||||
# and only on amd64, and only when cgo is enabled.
|
||||
# Delayed until here so we know whether to try external linking.
|
||||
case "$GOHOSTOS-$GOOS-$GOARCH-$CGO_ENABLED" in
|
||||
linux-linux-amd64-1 | freebsd-freebsd-amd64-1 | darwin-darwin-amd64-1)
|
||||
echo
|
||||
echo '# Testing race detector.'
|
||||
go test -race -i runtime/race flag os/exec
|
||||
go test -race -run=Output runtime/race
|
||||
go test -race -short flag os/exec
|
||||
|
||||
# Test with external linking; see issue 9133.
|
||||
if [ "$extlink" = 1 ]; then
|
||||
go test -race -short -ldflags=-linkmode=external flag os/exec
|
||||
fi
|
||||
esac
|
||||
|
||||
# This tests cgo -cdefs. That mode is not supported,
|
||||
# so it's okay if it doesn't work on some systems.
|
||||
# In particular, it works badly with clang on OS X.
|
||||
|
||||
@@ -65,12 +65,39 @@ thread_start_wrapper(void *arg)
|
||||
return args.func(args.arg);
|
||||
}
|
||||
|
||||
static void init_pthread_wrapper(void) {
|
||||
void *handle;
|
||||
|
||||
// Locate symbol for the system pthread_create function.
|
||||
handle = dlopen("libpthread.so", RTLD_LAZY);
|
||||
if(handle == NULL) {
|
||||
fprintf(stderr, "runtime/cgo: dlopen failed to load libpthread: %s\n", dlerror());
|
||||
abort();
|
||||
}
|
||||
sys_pthread_create = dlsym(handle, "pthread_create");
|
||||
if(sys_pthread_create == NULL) {
|
||||
fprintf(stderr, "runtime/cgo: dlsym failed to find pthread_create: %s\n", dlerror());
|
||||
abort();
|
||||
}
|
||||
dlclose(handle);
|
||||
}
|
||||
|
||||
static pthread_once_t init_pthread_wrapper_once = PTHREAD_ONCE_INIT;
|
||||
|
||||
int
|
||||
pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
||||
void *(*start_routine)(void *), void *arg)
|
||||
{
|
||||
struct thread_args *p;
|
||||
|
||||
// we must initialize our wrapper in pthread_create, because it is valid to call
|
||||
// pthread_create in a static constructor, and in fact, our test for issue 9456
|
||||
// does just that.
|
||||
if(pthread_once(&init_pthread_wrapper_once, init_pthread_wrapper) != 0) {
|
||||
fprintf(stderr, "runtime/cgo: failed to initialize pthread_create wrapper\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
p = malloc(sizeof(*p));
|
||||
if(p == NULL) {
|
||||
errno = ENOMEM;
|
||||
@@ -87,7 +114,6 @@ x_cgo_init(G *g, void (*setg)(void*))
|
||||
{
|
||||
pthread_attr_t attr;
|
||||
size_t size;
|
||||
void *handle;
|
||||
|
||||
setg_gcc = setg;
|
||||
pthread_attr_init(&attr);
|
||||
@@ -95,18 +121,10 @@ x_cgo_init(G *g, void (*setg)(void*))
|
||||
g->stacklo = (uintptr)&attr - size + 4096;
|
||||
pthread_attr_destroy(&attr);
|
||||
|
||||
// Locate symbol for the system pthread_create function.
|
||||
handle = dlopen("libpthread.so", RTLD_LAZY);
|
||||
if(handle == NULL) {
|
||||
fprintf(stderr, "dlopen: failed to load libpthread: %s\n", dlerror());
|
||||
if(pthread_once(&init_pthread_wrapper_once, init_pthread_wrapper) != 0) {
|
||||
fprintf(stderr, "runtime/cgo: failed to initialize pthread_create wrapper\n");
|
||||
abort();
|
||||
}
|
||||
sys_pthread_create = dlsym(handle, "pthread_create");
|
||||
if(sys_pthread_create == NULL) {
|
||||
fprintf(stderr, "dlsym: failed to find pthread_create: %s\n", dlerror());
|
||||
abort();
|
||||
}
|
||||
dlclose(handle);
|
||||
|
||||
tcb_fixup(1);
|
||||
}
|
||||
|
||||
@@ -65,12 +65,39 @@ thread_start_wrapper(void *arg)
|
||||
return args.func(args.arg);
|
||||
}
|
||||
|
||||
static void init_pthread_wrapper(void) {
|
||||
void *handle;
|
||||
|
||||
// Locate symbol for the system pthread_create function.
|
||||
handle = dlopen("libpthread.so", RTLD_LAZY);
|
||||
if(handle == NULL) {
|
||||
fprintf(stderr, "runtime/cgo: dlopen failed to load libpthread: %s\n", dlerror());
|
||||
abort();
|
||||
}
|
||||
sys_pthread_create = dlsym(handle, "pthread_create");
|
||||
if(sys_pthread_create == NULL) {
|
||||
fprintf(stderr, "runtime/cgo: dlsym failed to find pthread_create: %s\n", dlerror());
|
||||
abort();
|
||||
}
|
||||
dlclose(handle);
|
||||
}
|
||||
|
||||
static pthread_once_t init_pthread_wrapper_once = PTHREAD_ONCE_INIT;
|
||||
|
||||
int
|
||||
pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
||||
void *(*start_routine)(void *), void *arg)
|
||||
{
|
||||
struct thread_args *p;
|
||||
|
||||
// we must initialize our wrapper in pthread_create, because it is valid to call
|
||||
// pthread_create in a static constructor, and in fact, our test for issue 9456
|
||||
// does just that.
|
||||
if(pthread_once(&init_pthread_wrapper_once, init_pthread_wrapper) != 0) {
|
||||
fprintf(stderr, "runtime/cgo: failed to initialize pthread_create wrapper\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
p = malloc(sizeof(*p));
|
||||
if(p == NULL) {
|
||||
errno = ENOMEM;
|
||||
@@ -87,7 +114,6 @@ x_cgo_init(G *g, void (*setg)(void*))
|
||||
{
|
||||
pthread_attr_t attr;
|
||||
size_t size;
|
||||
void *handle;
|
||||
|
||||
setg_gcc = setg;
|
||||
pthread_attr_init(&attr);
|
||||
@@ -95,18 +121,10 @@ x_cgo_init(G *g, void (*setg)(void*))
|
||||
g->stacklo = (uintptr)&attr - size + 4096;
|
||||
pthread_attr_destroy(&attr);
|
||||
|
||||
// Locate symbol for the system pthread_create function.
|
||||
handle = dlopen("libpthread.so", RTLD_LAZY);
|
||||
if(handle == NULL) {
|
||||
fprintf(stderr, "dlopen: failed to load libpthread: %s\n", dlerror());
|
||||
if(pthread_once(&init_pthread_wrapper_once, init_pthread_wrapper) != 0) {
|
||||
fprintf(stderr, "runtime/cgo: failed to initialize pthread_create wrapper\n");
|
||||
abort();
|
||||
}
|
||||
sys_pthread_create = dlsym(handle, "pthread_create");
|
||||
if(sys_pthread_create == NULL) {
|
||||
fprintf(stderr, "dlsym: failed to find pthread_create: %s\n", dlerror());
|
||||
abort();
|
||||
}
|
||||
dlclose(handle);
|
||||
|
||||
tcb_fixup(1);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
package runtime_test
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -50,6 +51,30 @@ func TestCgoExternalThreadPanic(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCgoExternalThreadSIGPROF(t *testing.T) {
|
||||
// issue 9456.
|
||||
switch runtime.GOOS {
|
||||
case "plan9", "windows":
|
||||
t.Skipf("no pthreads on %s", runtime.GOOS)
|
||||
case "darwin":
|
||||
// static constructor needs external linking, but we don't support
|
||||
// external linking on OS X 10.6.
|
||||
out, err := exec.Command("uname", "-r").Output()
|
||||
if err != nil {
|
||||
t.Fatalf("uname -r failed: %v", err)
|
||||
}
|
||||
// OS X 10.6 == Darwin 10.x
|
||||
if strings.HasPrefix(string(out), "10.") {
|
||||
t.Skipf("no external linking on OS X 10.6")
|
||||
}
|
||||
}
|
||||
got := executeTest(t, cgoExternalThreadSIGPROFSource, nil)
|
||||
want := "OK\n"
|
||||
if got != want {
|
||||
t.Fatalf("expected %q, but got %q", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
const cgoSignalDeadlockSource = `
|
||||
package main
|
||||
|
||||
@@ -194,3 +219,46 @@ start(void)
|
||||
printf("_beginthreadex failed\n");
|
||||
}
|
||||
`
|
||||
|
||||
const cgoExternalThreadSIGPROFSource = `
|
||||
package main
|
||||
|
||||
/*
|
||||
#include <stdint.h>
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
|
||||
volatile int32_t spinlock;
|
||||
|
||||
static void *thread1(void *p) {
|
||||
(void)p;
|
||||
while (spinlock == 0)
|
||||
;
|
||||
pthread_kill(pthread_self(), SIGPROF);
|
||||
spinlock = 0;
|
||||
return NULL;
|
||||
}
|
||||
__attribute__((constructor)) void issue9456() {
|
||||
pthread_t tid;
|
||||
pthread_create(&tid, 0, thread1, NULL);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// This test intends to test that sending SIGPROF to foreign threads
|
||||
// before we make any cgo call will not abort the whole process, so
|
||||
// we cannot make any cgo call here. See http://golang.org/issue/9456.
|
||||
atomic.StoreInt32((*int32)(unsafe.Pointer(&C.spinlock)), 1)
|
||||
for atomic.LoadInt32((*int32)(unsafe.Pointer(&C.spinlock))) == 1 {
|
||||
runtime.Gosched()
|
||||
}
|
||||
println("OK")
|
||||
}
|
||||
`
|
||||
|
||||
@@ -41,6 +41,7 @@ const (
|
||||
DUPLICATE_SAME_ACCESS = C.DUPLICATE_SAME_ACCESS
|
||||
THREAD_PRIORITY_HIGHEST = C.THREAD_PRIORITY_HIGHEST
|
||||
|
||||
SIGPROF = 0 // dummy value for badsignal
|
||||
SIGINT = C.SIGINT
|
||||
CTRL_C_EVENT = C.CTRL_C_EVENT
|
||||
CTRL_BREAK_EVENT = C.CTRL_BREAK_EVENT
|
||||
|
||||
@@ -117,12 +117,12 @@ type hmap struct {
|
||||
|
||||
// A bucket for a Go map.
|
||||
type bmap struct {
|
||||
tophash [bucketCnt]uint8
|
||||
overflow *bmap
|
||||
tophash [bucketCnt]uint8
|
||||
// Followed by bucketCnt keys and then bucketCnt values.
|
||||
// NOTE: packing all the keys together and then all the values together makes the
|
||||
// code a bit more complicated than alternating key/value/key/value/... but it allows
|
||||
// us to eliminate padding which would be needed for, e.g., map[int64]int8.
|
||||
// Followed by an overflow pointer.
|
||||
}
|
||||
|
||||
// A hash iteration structure.
|
||||
@@ -149,6 +149,13 @@ func evacuated(b *bmap) bool {
|
||||
return h > empty && h < minTopHash
|
||||
}
|
||||
|
||||
func (b *bmap) overflow(t *maptype) *bmap {
|
||||
return *(**bmap)(add(unsafe.Pointer(b), uintptr(t.bucketsize)-regSize))
|
||||
}
|
||||
func (b *bmap) setoverflow(t *maptype, ovf *bmap) {
|
||||
*(**bmap)(add(unsafe.Pointer(b), uintptr(t.bucketsize)-regSize)) = ovf
|
||||
}
|
||||
|
||||
func makemap(t *maptype, hint int64) *hmap {
|
||||
if sz := unsafe.Sizeof(hmap{}); sz > 48 || sz != uintptr(t.hmap.size) {
|
||||
gothrow("bad hmap size")
|
||||
@@ -275,7 +282,7 @@ func mapaccess1(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {
|
||||
return v
|
||||
}
|
||||
}
|
||||
b = b.overflow
|
||||
b = b.overflow(t)
|
||||
if b == nil {
|
||||
return unsafe.Pointer(t.elem.zero)
|
||||
}
|
||||
@@ -323,7 +330,7 @@ func mapaccess2(t *maptype, h *hmap, key unsafe.Pointer) (unsafe.Pointer, bool)
|
||||
return v, true
|
||||
}
|
||||
}
|
||||
b = b.overflow
|
||||
b = b.overflow(t)
|
||||
if b == nil {
|
||||
return unsafe.Pointer(t.elem.zero), false
|
||||
}
|
||||
@@ -366,7 +373,7 @@ func mapaccessK(t *maptype, h *hmap, key unsafe.Pointer) (unsafe.Pointer, unsafe
|
||||
return k, v
|
||||
}
|
||||
}
|
||||
b = b.overflow
|
||||
b = b.overflow(t)
|
||||
if b == nil {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -437,10 +444,11 @@ again:
|
||||
memmove(v2, val, uintptr(t.elem.size))
|
||||
return
|
||||
}
|
||||
if b.overflow == nil {
|
||||
ovf := b.overflow(t)
|
||||
if ovf == nil {
|
||||
break
|
||||
}
|
||||
b = b.overflow
|
||||
b = ovf
|
||||
}
|
||||
|
||||
// did not find mapping for key. Allocate new cell & add entry.
|
||||
@@ -455,7 +463,7 @@ again:
|
||||
memstats.next_gc = memstats.heap_alloc
|
||||
}
|
||||
newb := (*bmap)(newobject(t.bucket))
|
||||
b.overflow = newb
|
||||
b.setoverflow(t, newb)
|
||||
inserti = &newb.tophash[0]
|
||||
insertk = add(unsafe.Pointer(newb), dataOffset)
|
||||
insertv = add(insertk, bucketCnt*uintptr(t.keysize))
|
||||
@@ -525,7 +533,7 @@ func mapdelete(t *maptype, h *hmap, key unsafe.Pointer) {
|
||||
h.count--
|
||||
return
|
||||
}
|
||||
b = b.overflow
|
||||
b = b.overflow(t)
|
||||
if b == nil {
|
||||
return
|
||||
}
|
||||
@@ -720,7 +728,7 @@ next:
|
||||
return
|
||||
}
|
||||
}
|
||||
b = b.overflow
|
||||
b = b.overflow(t)
|
||||
i = 0
|
||||
goto next
|
||||
}
|
||||
@@ -778,7 +786,7 @@ func evacuate(t *maptype, h *hmap, oldbucket uintptr) {
|
||||
yk := add(unsafe.Pointer(y), dataOffset)
|
||||
xv := add(xk, bucketCnt*uintptr(t.keysize))
|
||||
yv := add(yk, bucketCnt*uintptr(t.keysize))
|
||||
for ; b != nil; b = b.overflow {
|
||||
for ; b != nil; b = b.overflow(t) {
|
||||
k := add(unsafe.Pointer(b), dataOffset)
|
||||
v := add(k, bucketCnt*uintptr(t.keysize))
|
||||
for i := 0; i < bucketCnt; i, k, v = i+1, add(k, uintptr(t.keysize)), add(v, uintptr(t.valuesize)) {
|
||||
@@ -828,7 +836,7 @@ func evacuate(t *maptype, h *hmap, oldbucket uintptr) {
|
||||
memstats.next_gc = memstats.heap_alloc
|
||||
}
|
||||
newx := (*bmap)(newobject(t.bucket))
|
||||
x.overflow = newx
|
||||
x.setoverflow(t, newx)
|
||||
x = newx
|
||||
xi = 0
|
||||
xk = add(unsafe.Pointer(x), dataOffset)
|
||||
@@ -855,7 +863,7 @@ func evacuate(t *maptype, h *hmap, oldbucket uintptr) {
|
||||
memstats.next_gc = memstats.heap_alloc
|
||||
}
|
||||
newy := (*bmap)(newobject(t.bucket))
|
||||
y.overflow = newy
|
||||
y.setoverflow(t, newy)
|
||||
y = newy
|
||||
yi = 0
|
||||
yk = add(unsafe.Pointer(y), dataOffset)
|
||||
@@ -881,7 +889,6 @@ func evacuate(t *maptype, h *hmap, oldbucket uintptr) {
|
||||
// Unlink the overflow buckets & clear key/value to help GC.
|
||||
if h.flags&oldIterator == 0 {
|
||||
b = (*bmap)(add(h.oldbuckets, oldbucket*uintptr(t.bucketsize)))
|
||||
b.overflow = nil
|
||||
memclr(add(unsafe.Pointer(b), dataOffset), uintptr(t.bucketsize)-dataOffset)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ func mapaccess1_fast32(t *maptype, h *hmap, key uint32) unsafe.Pointer {
|
||||
}
|
||||
return add(unsafe.Pointer(b), dataOffset+bucketCnt*4+i*uintptr(t.valuesize))
|
||||
}
|
||||
b = b.overflow
|
||||
b = b.overflow(t)
|
||||
if b == nil {
|
||||
return unsafe.Pointer(t.elem.zero)
|
||||
}
|
||||
@@ -85,7 +85,7 @@ func mapaccess2_fast32(t *maptype, h *hmap, key uint32) (unsafe.Pointer, bool) {
|
||||
}
|
||||
return add(unsafe.Pointer(b), dataOffset+bucketCnt*4+i*uintptr(t.valuesize)), true
|
||||
}
|
||||
b = b.overflow
|
||||
b = b.overflow(t)
|
||||
if b == nil {
|
||||
return unsafe.Pointer(t.elem.zero), false
|
||||
}
|
||||
@@ -127,7 +127,7 @@ func mapaccess1_fast64(t *maptype, h *hmap, key uint64) unsafe.Pointer {
|
||||
}
|
||||
return add(unsafe.Pointer(b), dataOffset+bucketCnt*8+i*uintptr(t.valuesize))
|
||||
}
|
||||
b = b.overflow
|
||||
b = b.overflow(t)
|
||||
if b == nil {
|
||||
return unsafe.Pointer(t.elem.zero)
|
||||
}
|
||||
@@ -169,7 +169,7 @@ func mapaccess2_fast64(t *maptype, h *hmap, key uint64) (unsafe.Pointer, bool) {
|
||||
}
|
||||
return add(unsafe.Pointer(b), dataOffset+bucketCnt*8+i*uintptr(t.valuesize)), true
|
||||
}
|
||||
b = b.overflow
|
||||
b = b.overflow(t)
|
||||
if b == nil {
|
||||
return unsafe.Pointer(t.elem.zero), false
|
||||
}
|
||||
@@ -271,7 +271,7 @@ dohash:
|
||||
return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*ptrSize+i*uintptr(t.valuesize))
|
||||
}
|
||||
}
|
||||
b = b.overflow
|
||||
b = b.overflow(t)
|
||||
if b == nil {
|
||||
return unsafe.Pointer(t.elem.zero)
|
||||
}
|
||||
@@ -371,7 +371,7 @@ dohash:
|
||||
return add(unsafe.Pointer(b), dataOffset+bucketCnt*2*ptrSize+i*uintptr(t.valuesize)), true
|
||||
}
|
||||
}
|
||||
b = b.overflow
|
||||
b = b.overflow(t)
|
||||
if b == nil {
|
||||
return unsafe.Pointer(t.elem.zero), false
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user