convert to go buildpack
This commit is contained in:
parent
3b4b6cede9
commit
3a21ef1926
|
@ -0,0 +1 @@
|
|||
https://github.com/heroku/heroku-buildpack-go
|
|
@ -0,0 +1,99 @@
|
|||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/GeertJohan/go.rice"
|
||||
packages = [".","embedded"]
|
||||
revision = "c02ca9a983da5807ddf7d796784928f5be4afd09"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/Xe/gopreload"
|
||||
packages = ["."]
|
||||
revision = "a00a8beb369cafd88bb7b32f31fc4ff3219c3565"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/Xe/jsonfeed"
|
||||
packages = ["."]
|
||||
revision = "e21591505612b9064436351ffc9acddfa3b583e8"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/Xe/ln"
|
||||
packages = ["."]
|
||||
revision = "466e05b2ef3e48ce08a367b6aaac09ee29a124e5"
|
||||
version = "v0.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/daaku/go.zipexe"
|
||||
packages = ["."]
|
||||
revision = "a5fe2436ffcb3236e175e5149162b41cd28bd27d"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/google/gops"
|
||||
packages = ["agent","internal","signal"]
|
||||
revision = "57e77c5c37da1f4e1af49f9d1fe760f146c1579e"
|
||||
version = "v0.3.2"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/gorilla/feeds"
|
||||
packages = ["."]
|
||||
revision = "4b936b5221c53c99fcd4b15ac0b8c38ff490ab89"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/jtolds/qod"
|
||||
packages = ["."]
|
||||
revision = "3abb44dfc7ba8b5cdfdb634786f57e78c7004e1c"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/kardianos/osext"
|
||||
packages = ["."]
|
||||
revision = "ae77be60afb1dcacde03767a8c37337fad28ac14"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/magefile/mage"
|
||||
packages = ["mg","types"]
|
||||
revision = "ab3ca2f6f85577d7ec82e0a6df721147a2e737f9"
|
||||
version = "v2.0.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pkg/errors"
|
||||
packages = ["."]
|
||||
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
|
||||
version = "v0.8.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/russross/blackfriday"
|
||||
packages = ["."]
|
||||
revision = "cadec560ec52d93835bf2f15bd794700d3a2473b"
|
||||
version = "v2.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/shurcooL/sanitized_anchor_name"
|
||||
packages = ["."]
|
||||
revision = "86672fcb3f950f35f2e675df2240550f2a50762f"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/tj/front"
|
||||
packages = ["."]
|
||||
revision = "739be213b0a1c496dccaf9e5df1514150c9548e4"
|
||||
|
||||
[[projects]]
|
||||
branch = "v1"
|
||||
name = "gopkg.in/yaml.v1"
|
||||
packages = ["."]
|
||||
revision = "9f9df34309c04878acc86042b16630b0f696e1de"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "0689be1827bc408bc22bad70a56fe65f0d7986f4b7a01376fd4c9420f54aac18"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
|
@ -0,0 +1,71 @@
|
|||
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/GeertJohan/go.rice"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/Xe/gopreload"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/Xe/jsonfeed"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/Xe/ln"
|
||||
version = "0.1.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/google/gops"
|
||||
version = "0.3.2"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/gorilla/feeds"
|
||||
version = "1.0.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/jtolds/qod"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/magefile/mage"
|
||||
version = "2.0.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/pkg/errors"
|
||||
version = "0.8.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/russross/blackfriday"
|
||||
version = "2.0.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/tj/front"
|
||||
|
||||
[metadata.heroku]
|
||||
root-package = "github.com/Xe/site"
|
||||
install = [ "." ]
|
||||
ensure = "false"
|
15
vendor-log
15
vendor-log
|
@ -1,15 +0,0 @@
|
|||
c02ca9a983da5807ddf7d796784928f5be4afd09 github.com/GeertJohan/go.rice
|
||||
c02ca9a983da5807ddf7d796784928f5be4afd09 github.com/GeertJohan/go.rice/embedded
|
||||
a00a8beb369cafd88bb7b32f31fc4ff3219c3565 github.com/Xe/gopreload
|
||||
b685d4edebe855f8edbb4e605c0bf74e1e60b0e9 github.com/Xe/jsonfeed
|
||||
f759b797c0ff6b2c514202198fe5e8ba90094c14 github.com/Xe/ln
|
||||
a5fe2436ffcb3236e175e5149162b41cd28bd27d github.com/daaku/go.zipexe
|
||||
62f833fc9f6c4d3223bdb37bd0c2f8951bed8596 github.com/google/gops/agent
|
||||
62f833fc9f6c4d3223bdb37bd0c2f8951bed8596 github.com/google/gops/internal
|
||||
62f833fc9f6c4d3223bdb37bd0c2f8951bed8596 github.com/google/gops/signal
|
||||
441264de03a8117ed530ae8e049d8f601a33a099 github.com/gorilla/feeds
|
||||
c2c54e542fb797ad986b31721e1baedf214ca413 github.com/kardianos/osext
|
||||
ff09b135c25aae272398c51a07235b90a75aa4f0 github.com/pkg/errors
|
||||
0ba0f2b6ed7c475a92e4df8641825cb7a11d1fa3 github.com/russross/blackfriday
|
||||
739be213b0a1c496dccaf9e5df1514150c9548e4 github.com/tj/front
|
||||
9f9df34309c04878acc86042b16630b0f696e1de gopkg.in/yaml.v1
|
|
@ -0,0 +1,8 @@
|
|||
/example/example
|
||||
/example/example.exe
|
||||
/rice/rice
|
||||
/rice/rice.exe
|
||||
|
||||
*.rice-box.go
|
||||
*.rice-box.syso
|
||||
.wercker
|
|
@ -0,0 +1,19 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- master
|
||||
- 1.x.x
|
||||
- 1.8.x
|
||||
- 1.7.x
|
||||
- 1.6.x
|
||||
- 1.5.x
|
||||
|
||||
install:
|
||||
- go get -t ./...
|
||||
- env
|
||||
- if [ "${TRAVIS_GO_VERSION%.*}" != "1.5" ]; then go get github.com/golang/lint/golint; fi
|
||||
script:
|
||||
- go build -x ./...
|
||||
- go test -cover ./...
|
||||
- go vet ./...
|
||||
- if [ "${TRAVIS_GO_VERSION%.*}" != "1.5" ]; then golint .; fi
|
|
@ -0,0 +1,4 @@
|
|||
Geert-Johan Riemer <geertjohan@geertjohan.net>
|
||||
Paul Maddox <paul.maddox@gmail.com>
|
||||
Vincent Petithory <vincent.petithory@gmail.com>
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
Copyright (c) 2013, Geert-Johan Riemer
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,151 @@
|
|||
## go.rice
|
||||
|
||||
[![Build Status](https://travis-ci.org/GeertJohan/go.rice.png)](https://travis-ci.org/GeertJohan/go.rice)
|
||||
[![Godoc](https://img.shields.io/badge/godoc-go.rice-blue.svg?style=flat-square)](https://godoc.org/github.com/GeertJohan/go.rice)
|
||||
|
||||
go.rice is a [Go](http://golang.org) package that makes working with resources such as html,js,css,images and templates very easy. During development `go.rice` will load required files directly from disk. Upon deployment it is easy to add all resource files to a executable using the `rice` tool, without changing the source code for your package. go.rice provides several methods to add resources to a binary.
|
||||
|
||||
### What does it do?
|
||||
The first thing go.rice does is finding the correct absolute path for your resource files. Say you are executing go binary in your home directory, but your `html-files` are located in `$GOPATH/src/yourApplication/html-files`. `go.rice` will lookup the correct path for that directory (relative to the location of yourApplication). The only thing you have to do is include the resources using `rice.FindBox("html-files")`.
|
||||
|
||||
This only works when the source is available to the machine executing the binary. Which is always the case when the binary was installed with `go get` or `go install`. It might occur that you wish to simply provide a binary, without source. The `rice` tool analyses source code and finds call's to `rice.FindBox(..)` and adds the required directories to the executable binary. There are several methods to add these resources. You can 'embed' by generating go source code, or append the resource to the executable as zip file. In both cases `go.rice` will detect the embedded or appended resources and load those, instead of looking up files from disk.
|
||||
|
||||
### Installation
|
||||
|
||||
Use `go get` to install the package the `rice` tool.
|
||||
```
|
||||
go get github.com/GeertJohan/go.rice
|
||||
go get github.com/GeertJohan/go.rice/rice
|
||||
```
|
||||
|
||||
### Package usage
|
||||
|
||||
Import the package: `import "github.com/GeertJohan/go.rice"`
|
||||
|
||||
**Serving a static content folder over HTTP with a rice Box**
|
||||
```go
|
||||
http.Handle("/", http.FileServer(rice.MustFindBox("http-files").HTTPBox()))
|
||||
http.ListenAndServe(":8080", nil)
|
||||
```
|
||||
|
||||
**Service a static content folder over HTTP at a non-root location**
|
||||
```go
|
||||
box := rice.MustFindBox("cssfiles")
|
||||
cssFileServer := http.StripPrefix("/css/", http.FileServer(box.HTTPBox()))
|
||||
http.Handle("/css/", cssFileServer)
|
||||
http.ListenAndServe(":8080", nil)
|
||||
```
|
||||
|
||||
Note the *trailing slash* in `/css/` in both the call to
|
||||
`http.StripPrefix` and `http.Handle`.
|
||||
|
||||
**Loading a template**
|
||||
```go
|
||||
// find a rice.Box
|
||||
templateBox, err := rice.FindBox("example-templates")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// get file contents as string
|
||||
templateString, err := templateBox.String("message.tmpl")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// parse and execute the template
|
||||
tmplMessage, err := template.New("message").Parse(templateString)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
tmplMessage.Execute(os.Stdout, map[string]string{"Message": "Hello, world!"})
|
||||
|
||||
```
|
||||
|
||||
Never call `FindBox()` or `MustFindBox()` from an `init()` function, as the boxes might have not been loaded at that time.
|
||||
|
||||
### Tool usage
|
||||
The `rice` tool lets you add the resources to a binary executable so the files are not loaded from the filesystem anymore. This creates a 'standalone' executable. There are several ways to add the resources to a binary, each has pro's and con's but all will work without requiring changes to the way you load the resources.
|
||||
|
||||
#### embed-go
|
||||
**Embed resources by generating Go source code**
|
||||
|
||||
This method must be executed before building. It generates a single Go source file called *rice-box.go* for each package, that is compiled by the go compiler into the binary.
|
||||
|
||||
The downside with this option is that the generated go source files can become very large, which will slow down compilation and require lots of memory to compile.
|
||||
|
||||
Execute the following commands:
|
||||
```
|
||||
rice embed-go
|
||||
go build
|
||||
```
|
||||
|
||||
*A Note on Symbolic Links*: `embed-go` uses the `os.Walk` function
|
||||
from the standard library. The `os.Walk` function does **not** follow
|
||||
symbolic links. So, when creating a box, be aware that any symbolic
|
||||
links inside your box's directory will not be followed. **However**,
|
||||
if the box itself is a symbolic link, its actual location will be
|
||||
resolved first and then walked. In summary, if your box location is a
|
||||
symbolic link, it will be followed but none of the symbolic links in
|
||||
the box will be followed.
|
||||
|
||||
#### embed-syso
|
||||
**Embed resources by generating a coff .syso file and some .go source code**
|
||||
|
||||
** This method is experimental and should not be used for production systems just yet **
|
||||
|
||||
This method must be executed before building. It generates a COFF .syso file and Go source file that are compiled by the go compiler into the binary.
|
||||
|
||||
Execute the following commands:
|
||||
```
|
||||
rice embed-syso
|
||||
go build
|
||||
```
|
||||
|
||||
#### append
|
||||
**Append resources to executable as zip file**
|
||||
|
||||
This method changes an already built executable. It appends the resources as zip file to the binary. It makes compilation a lot faster and can be used with large resource files.
|
||||
|
||||
Downsides for appending are that it requires `zip` to be installed and does not provide a working Seek method.
|
||||
|
||||
Run the following commands to create a standalone executable.
|
||||
```
|
||||
go build -o example
|
||||
rice append --exec example
|
||||
```
|
||||
|
||||
**Note: requires zip command to be installed**
|
||||
|
||||
On windows, install zip from http://gnuwin32.sourceforge.net/packages/zip.htm or cygwin/msys toolsets.
|
||||
|
||||
#### Help information
|
||||
Run `rice -h` for information about all options.
|
||||
|
||||
You can run the -h option for each sub-command, e.g. `rice append -h`.
|
||||
|
||||
### Order of precedence
|
||||
When opening a new box, the rice package tries to locate the resources in the following order:
|
||||
|
||||
- embedded in generated go source
|
||||
- appended as zip
|
||||
- 'live' from filesystem
|
||||
|
||||
|
||||
### License
|
||||
This project is licensed under a Simplified BSD license. Please read the [LICENSE file][license].
|
||||
|
||||
### TODO & Development
|
||||
This package is not completed yet. Though it already provides working embedding, some important featuers are still missing.
|
||||
- implement Readdir() correctly on virtualDir
|
||||
- in-code TODO's
|
||||
- find boxes in imported packages
|
||||
|
||||
Less important stuff:
|
||||
- idea, os/arch dependent embeds. rice checks if embedding file has _os_arch or build flags. If box is not requested by file without buildflags, then the buildflags are applied to the embed file.
|
||||
|
||||
### Package documentation
|
||||
|
||||
You will find package documentation at [godoc.org/github.com/GeertJohan/go.rice][godoc].
|
||||
|
||||
|
||||
[license]: https://github.com/GeertJohan/go.rice/blob/master/LICENSE
|
||||
[godoc]: http://godoc.org/github.com/GeertJohan/go.rice
|
|
@ -0,0 +1,136 @@
|
|||
package rice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/GeertJohan/go.rice/embedded"
|
||||
)
|
||||
|
||||
// For all test code in this package, define a set of test boxes.
|
||||
var eb1 *embedded.EmbeddedBox
|
||||
var ab1, ab2 *appendedBox
|
||||
var fsb1, fsb2, fsb3 string // paths to filesystem boxes
|
||||
func init() {
|
||||
var err error
|
||||
|
||||
// Box1 exists in all three locations.
|
||||
eb1 = &embedded.EmbeddedBox{Name: "box1"}
|
||||
embedded.RegisterEmbeddedBox(eb1.Name, eb1)
|
||||
ab1 = &appendedBox{Name: "box1"}
|
||||
appendedBoxes["box1"] = ab1
|
||||
fsb1, err = ioutil.TempDir("", "box1")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Box2 exists in only appended and FS.
|
||||
ab2 = &appendedBox{Name: "box2"}
|
||||
appendedBoxes["box2"] = ab2
|
||||
fsb2, err = ioutil.TempDir("", "box2")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Box3 exists only on disk.
|
||||
fsb3, err = ioutil.TempDir("", "box3")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Also, replace the default filesystem lookup path to directly support the
|
||||
// on-disk temp directories.
|
||||
resolveAbsolutePathFromCaller = func(name string, n int) (string, error) {
|
||||
if name == "box1" {
|
||||
return fsb1, nil
|
||||
} else if name == "box2" {
|
||||
return fsb2, nil
|
||||
} else if name == "box3" {
|
||||
return fsb3, nil
|
||||
}
|
||||
return "", fmt.Errorf("Unknown box name: %q", name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultLookupOrder(t *testing.T) {
|
||||
// Box1 exists in all three, so the default order should find the embedded.
|
||||
b, err := FindBox("box1")
|
||||
if err != nil {
|
||||
t.Fatalf("Expected to find box1, got error: %v", err)
|
||||
}
|
||||
if b.embed != eb1 {
|
||||
t.Fatalf("Expected to find embedded box, but got %#v", b)
|
||||
}
|
||||
|
||||
// Box2 exists in appended and FS, so find the appended.
|
||||
b2, err := FindBox("box2")
|
||||
if err != nil {
|
||||
t.Fatalf("Expected to find box2, got error: %v", err)
|
||||
}
|
||||
if b2.appendd != ab2 {
|
||||
t.Fatalf("Expected to find appended box, but got %#v", b2)
|
||||
}
|
||||
|
||||
// Box3 exists only on FS, so find it there.
|
||||
b3, err := FindBox("box3")
|
||||
if err != nil {
|
||||
t.Fatalf("Expected to find box3, got error: %v", err)
|
||||
}
|
||||
if b3.absolutePath != fsb3 {
|
||||
t.Fatalf("Expected to find FS box, but got %#v", b3)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigLocateOrder(t *testing.T) {
|
||||
cfg := Config{LocateOrder: []LocateMethod{LocateFS, LocateAppended, LocateEmbedded}}
|
||||
fsb := []string{fsb1, fsb2, fsb3}
|
||||
// All 3 boxes have a FS backend, so we should always find that.
|
||||
for i, boxName := range []string{"box1", "box2", "box3"} {
|
||||
b, err := cfg.FindBox(boxName)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected to find %q, got error: %v", boxName, err)
|
||||
}
|
||||
if b.absolutePath != fsb[i] {
|
||||
t.Fatalf("Expected to find FS box, but got %#v", b)
|
||||
}
|
||||
}
|
||||
|
||||
cfg.LocateOrder = []LocateMethod{LocateAppended, LocateFS, LocateEmbedded}
|
||||
{
|
||||
b, err := cfg.FindBox("box3")
|
||||
if err != nil {
|
||||
t.Fatalf("Expected to find box3, got error: %v", err)
|
||||
}
|
||||
if b.absolutePath != fsb3 {
|
||||
t.Fatalf("Expected to find FS box, but got %#v", b)
|
||||
}
|
||||
}
|
||||
{
|
||||
b, err := cfg.FindBox("box2")
|
||||
if err != nil {
|
||||
t.Fatalf("Expected to find box2, got error: %v", err)
|
||||
}
|
||||
if b.appendd != ab2 {
|
||||
t.Fatalf("Expected to find appended box, but got %#v", b)
|
||||
}
|
||||
}
|
||||
|
||||
// What if we don't list all the locate methods?
|
||||
cfg.LocateOrder = []LocateMethod{LocateEmbedded}
|
||||
{
|
||||
b, err := cfg.FindBox("box2")
|
||||
if err == nil {
|
||||
t.Fatalf("Expected not to find box2, but something was found: %#v", b)
|
||||
}
|
||||
}
|
||||
{
|
||||
b, err := cfg.FindBox("box1")
|
||||
if err != nil {
|
||||
t.Fatalf("Expected to find box2, got error: %v", err)
|
||||
}
|
||||
if b.embed != eb1 {
|
||||
t.Fatalf("Expected to find embedded box, but got %#v", b)
|
||||
}
|
||||
}
|
||||
}
|
2
vendor/github.com/GeertJohan/go.rice/example/example-files/file.txt
generated
vendored
Normal file
2
vendor/github.com/GeertJohan/go.rice/example/example-files/file.txt
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
test content
|
||||
break
|
BIN
vendor/github.com/GeertJohan/go.rice/example/example-files/img/doge.jpg
generated
vendored
Normal file
BIN
vendor/github.com/GeertJohan/go.rice/example/example-files/img/doge.jpg
generated
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 52 KiB |
1
vendor/github.com/GeertJohan/go.rice/example/example-templates/message.tmpl
generated
vendored
Normal file
1
vendor/github.com/GeertJohan/go.rice/example/example-templates/message.tmpl
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
I have a message for you: {{.Message}}
|
|
@ -0,0 +1,69 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"text/template"
|
||||
|
||||
"github.com/GeertJohan/go.rice"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
func main() {
|
||||
conf := rice.Config{
|
||||
LocateOrder: []rice.LocateMethod{rice.LocateEmbedded, rice.LocateAppended, rice.LocateFS},
|
||||
}
|
||||
box, err := conf.FindBox("example-files")
|
||||
if err != nil {
|
||||
log.Fatalf("error opening rice.Box: %s\n", err)
|
||||
}
|
||||
// spew.Dump(box)
|
||||
|
||||
contentString, err := box.String("file.txt")
|
||||
if err != nil {
|
||||
log.Fatalf("could not read file contents as string: %s\n", err)
|
||||
}
|
||||
log.Printf("Read some file contents as string:\n%s\n", contentString)
|
||||
|
||||
contentBytes, err := box.Bytes("file.txt")
|
||||
if err != nil {
|
||||
log.Fatalf("could not read file contents as byteSlice: %s\n", err)
|
||||
}
|
||||
log.Printf("Read some file contents as byteSlice:\n%s\n", hex.Dump(contentBytes))
|
||||
|
||||
file, err := box.Open("file.txt")
|
||||
if err != nil {
|
||||
log.Fatalf("could not open file: %s\n", err)
|
||||
}
|
||||
spew.Dump(file)
|
||||
|
||||
// find/create a rice.Box
|
||||
templateBox, err := rice.FindBox("example-templates")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// get file contents as string
|
||||
templateString, err := templateBox.String("message.tmpl")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// parse and execute the template
|
||||
tmplMessage, err := template.New("message").Parse(templateString)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
tmplMessage.Execute(os.Stdout, map[string]string{"Message": "Hello, world!"})
|
||||
|
||||
http.Handle("/", http.FileServer(box.HTTPBox()))
|
||||
go func() {
|
||||
fmt.Println("Serving files on :8080, press ctrl-C to exit")
|
||||
err := http.ListenAndServe(":8080", nil)
|
||||
if err != nil {
|
||||
log.Fatalf("error serving files: %v", err)
|
||||
}
|
||||
}()
|
||||
select {}
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
zipexe "github.com/daaku/go.zipexe"
|
||||
)
|
||||
|
||||
func operationAppend(pkgs []*build.Package) {
|
||||
// create tmp zipfile
|
||||
tmpZipfileName := filepath.Join(os.TempDir(), fmt.Sprintf("ricebox-%d-%s.zip", time.Now().Unix(), randomString(10)))
|
||||
verbosef("Will create tmp zipfile: %s\n", tmpZipfileName)
|
||||
tmpZipfile, err := os.Create(tmpZipfileName)
|
||||
if err != nil {
|
||||
fmt.Printf("Error creating tmp zipfile: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer func() {
|
||||
tmpZipfile.Close()
|
||||
os.Remove(tmpZipfileName)
|
||||
}()
|
||||
|
||||
// find abs path for binary file
|
||||
binfileName, err := filepath.Abs(flags.Append.Executable)
|
||||
if err != nil {
|
||||
fmt.Printf("Error finding absolute path for executable to append: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
verbosef("Will append to file: %s\n", binfileName)
|
||||
|
||||
// check that command doesn't already have zip appended
|
||||
if rd, _ := zipexe.Open(binfileName); rd != nil {
|
||||
fmt.Printf("Cannot append to already appended executable. Please remove %s and build a fresh one.\n", binfileName)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// open binfile
|
||||
binfile, err := os.OpenFile(binfileName, os.O_WRONLY, os.ModeAppend)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: unable to open executable file: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer binfile.Close()
|
||||
|
||||
binfileInfo, err := binfile.Stat()
|
||||
if err != nil {
|
||||
fmt.Printf("Error: unable to stat executable file: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// create zip.Writer
|
||||
zipWriter := zip.NewWriter(tmpZipfile)
|
||||
|
||||
// write the zip offset into the zip data
|
||||
zipWriter.SetOffset(binfileInfo.Size())
|
||||
|
||||
for _, pkg := range pkgs {
|
||||
// find boxes for this command
|
||||
boxMap := findBoxes(pkg)
|
||||
|
||||
// notify user when no calls to rice.FindBox are made (is this an error and therefore os.Exit(1) ?
|
||||
if len(boxMap) == 0 {
|
||||
fmt.Printf("no calls to rice.FindBox() or rice.MustFindBox() found in import path `%s`\n", pkg.ImportPath)
|
||||
continue
|
||||
}
|
||||
|
||||
verbosef("\n")
|
||||
|
||||
for boxname := range boxMap {
|
||||
appendedBoxName := strings.Replace(boxname, `/`, `-`, -1)
|
||||
|
||||
// walk box path's and insert files
|
||||
boxPath := filepath.Clean(filepath.Join(pkg.Dir, boxname))
|
||||
filepath.Walk(boxPath, func(path string, info os.FileInfo, err error) error {
|
||||
if info == nil {
|
||||
fmt.Printf("Error: box \"%s\" not found on disk\n", path)
|
||||
os.Exit(1)
|
||||
}
|
||||
// create zipFilename
|
||||
zipFileName := filepath.Join(appendedBoxName, strings.TrimPrefix(path, boxPath))
|
||||
// write directories as empty file with comment "dir"
|
||||
if info.IsDir() {
|
||||
_, err := zipWriter.CreateHeader(&zip.FileHeader{
|
||||
Name: zipFileName,
|
||||
Comment: "dir",
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf("Error creating dir in tmp zip: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// create zipFileWriter
|
||||
zipFileHeader, err := zip.FileInfoHeader(info)
|
||||
if err != nil {
|
||||
fmt.Printf("Error creating zip FileHeader: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
zipFileHeader.Name = zipFileName
|
||||
zipFileWriter, err := zipWriter.CreateHeader(zipFileHeader)
|
||||
if err != nil {
|
||||
fmt.Printf("Error creating file in tmp zip: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
srcFile, err := os.Open(path)
|
||||
if err != nil {
|
||||
fmt.Printf("Error opening file to append: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
_, err = io.Copy(zipFileWriter, srcFile)
|
||||
if err != nil {
|
||||
fmt.Printf("Error copying file contents to zip: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
srcFile.Close()
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
err = zipWriter.Close()
|
||||
if err != nil {
|
||||
fmt.Printf("Error closing tmp zipfile: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = tmpZipfile.Sync()
|
||||
if err != nil {
|
||||
fmt.Printf("Error syncing tmp zipfile: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
_, err = tmpZipfile.Seek(0, 0)
|
||||
if err != nil {
|
||||
fmt.Printf("Error seeking tmp zipfile: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
_, err = binfile.Seek(0, 2)
|
||||
if err != nil {
|
||||
fmt.Printf("Error seeking bin file: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
_, err = io.Copy(binfile, tmpZipfile)
|
||||
if err != nil {
|
||||
fmt.Printf("Error appending zipfile to executable: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/build"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func operationClean(pkg *build.Package) {
|
||||
filepath.Walk(pkg.Dir, func(filename string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
fmt.Printf("error walking pkg dir to clean files: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
verbosef("checking file '%s'\n", filename)
|
||||
if filepath.Base(filename) == "rice-box.go" ||
|
||||
strings.HasSuffix(filename, ".rice-box.go") ||
|
||||
strings.HasSuffix(filename, ".rice-box.syso") {
|
||||
err := os.Remove(filename)
|
||||
if err != nil {
|
||||
fmt.Printf("error removing file (%s): %s\n", filename, err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
verbosef("removed file '%s'\n", filename)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"go/format"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const boxFilename = "rice-box.go"
|
||||
|
||||
func writeBoxesGo(pkg *build.Package, out io.Writer) error {
|
||||
boxMap := findBoxes(pkg)
|
||||
|
||||
// notify user when no calls to rice.FindBox are made (is this an error and therefore os.Exit(1) ?
|
||||
if len(boxMap) == 0 {
|
||||
fmt.Println("no calls to rice.FindBox() found")
|
||||
return nil
|
||||
}
|
||||
|
||||
verbosef("\n")
|
||||
|
||||
var boxes []*boxDataType
|
||||
|
||||
for boxname := range boxMap {
|
||||
// find path and filename for this box
|
||||
boxPath := filepath.Join(pkg.Dir, boxname)
|
||||
|
||||
// Check to see if the path for the box is a symbolic link. If so, simply
|
||||
// box what the symbolic link points to. Note: the filepath.Walk function
|
||||
// will NOT follow any nested symbolic links. This only handles the case
|
||||
// where the root of the box is a symbolic link.
|
||||
symPath, serr := os.Readlink(boxPath)
|
||||
if serr == nil {
|
||||
boxPath = symPath
|
||||
}
|
||||
|
||||
// verbose info
|
||||
verbosef("embedding box '%s' to '%s'\n", boxname, boxFilename)
|
||||
|
||||
// read box metadata
|
||||
boxInfo, ierr := os.Stat(boxPath)
|
||||
if ierr != nil {
|
||||
return fmt.Errorf("Error: unable to access box at %s\n", boxPath)
|
||||
}
|
||||
|
||||
// create box datastructure (used by template)
|
||||
box := &boxDataType{
|
||||
BoxName: boxname,
|
||||
UnixNow: boxInfo.ModTime().Unix(),
|
||||
Files: make([]*fileDataType, 0),
|
||||
Dirs: make(map[string]*dirDataType),
|
||||
}
|
||||
|
||||
if !boxInfo.IsDir() {
|
||||
return fmt.Errorf("Error: Box %s must point to a directory but points to %s instead\n",
|
||||
boxname, boxPath)
|
||||
}
|
||||
|
||||
// fill box datastructure with file data
|
||||
err := filepath.Walk(boxPath, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("error walking box: %s\n", err)
|
||||
}
|
||||
|
||||
filename := strings.TrimPrefix(path, boxPath)
|
||||
filename = strings.Replace(filename, "\\", "/", -1)
|
||||
filename = strings.TrimPrefix(filename, "/")
|
||||
if info.IsDir() {
|
||||
dirData := &dirDataType{
|
||||
Identifier: "dir" + nextIdentifier(),
|
||||
FileName: filename,
|
||||
ModTime: info.ModTime().Unix(),
|
||||
ChildFiles: make([]*fileDataType, 0),
|
||||
ChildDirs: make([]*dirDataType, 0),
|
||||
}
|
||||
verbosef("\tincludes dir: '%s'\n", dirData.FileName)
|
||||
box.Dirs[dirData.FileName] = dirData
|
||||
|
||||
// add tree entry (skip for root, it'll create a recursion)
|
||||
if dirData.FileName != "" {
|
||||
pathParts := strings.Split(dirData.FileName, "/")
|
||||
parentDir := box.Dirs[strings.Join(pathParts[:len(pathParts)-1], "/")]
|
||||
parentDir.ChildDirs = append(parentDir.ChildDirs, dirData)
|
||||
}
|
||||
} else {
|
||||
fileData := &fileDataType{
|
||||
Identifier: "file" + nextIdentifier(),
|
||||
FileName: filename,
|
||||
ModTime: info.ModTime().Unix(),
|
||||
}
|
||||
verbosef("\tincludes file: '%s'\n", fileData.FileName)
|
||||
fileData.Content, err = ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading file content while walking box: %s\n", err)
|
||||
}
|
||||
box.Files = append(box.Files, fileData)
|
||||
|
||||
// add tree entry
|
||||
pathParts := strings.Split(fileData.FileName, "/")
|
||||
parentDir := box.Dirs[strings.Join(pathParts[:len(pathParts)-1], "/")]
|
||||
if parentDir == nil {
|
||||
return fmt.Errorf("Error: parent of %s is not within the box\n", path)
|
||||
}
|
||||
parentDir.ChildFiles = append(parentDir.ChildFiles, fileData)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
boxes = append(boxes, box)
|
||||
|
||||
}
|
||||
|
||||
embedSourceUnformated := bytes.NewBuffer(make([]byte, 0))
|
||||
|
||||
// execute template to buffer
|
||||
err := tmplEmbeddedBox.Execute(
|
||||
embedSourceUnformated,
|
||||
embedFileDataType{pkg.Name, boxes},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error writing embedded box to file (template execute): %s\n", err)
|
||||
}
|
||||
|
||||
// format the source code
|
||||
embedSource, err := format.Source(embedSourceUnformated.Bytes())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error formatting embedSource: %s\n", err)
|
||||
}
|
||||
|
||||
// write source to file
|
||||
_, err = io.Copy(out, bytes.NewBuffer(embedSource))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error writing embedSource to file: %s\n", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func operationEmbedGo(pkg *build.Package) {
|
||||
// create go file for box
|
||||
boxFile, err := os.Create(filepath.Join(pkg.Dir, boxFilename))
|
||||
if err != nil {
|
||||
log.Printf("error creating embedded box file: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer boxFile.Close()
|
||||
|
||||
err = writeBoxesGo(pkg, boxFile)
|
||||
if err != nil {
|
||||
log.Printf("error creating embedded box file: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,680 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type registeredDir struct {
|
||||
Filename string
|
||||
ModTime int
|
||||
ChildFiles []*registeredFile
|
||||
ChildDirs []*registeredDir
|
||||
}
|
||||
|
||||
type registeredFile struct {
|
||||
Filename string
|
||||
ModTime int
|
||||
Content string
|
||||
}
|
||||
|
||||
type registeredBox struct {
|
||||
Name string
|
||||
Time int
|
||||
// key is path
|
||||
Dirs map[string]*registeredDir
|
||||
// key is path
|
||||
Files map[string]*registeredFile
|
||||
}
|
||||
|
||||
// isSimpleSelector returns true if expr is pkgName.ident
|
||||
func isSimpleSelector(pkgName, ident string, expr ast.Expr) bool {
|
||||
if sel, ok := expr.(*ast.SelectorExpr); ok {
|
||||
if pkgIdent, ok := sel.X.(*ast.Ident); ok && pkgIdent.Name == pkgName && sel.Sel != nil && sel.Sel.Name == ident {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isIdent(ident string, expr ast.Expr) bool {
|
||||
if expr, ok := expr.(*ast.Ident); ok && expr.Name == ident {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getIdentName(expr ast.Expr) (string, bool) {
|
||||
if expr, ok := expr.(*ast.Ident); ok {
|
||||
return expr.Name, true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func getKey(expr *ast.KeyValueExpr) string {
|
||||
if ident, ok := expr.Key.(*ast.Ident); ok {
|
||||
return ident.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// parseModTime parses a time.Unix call, and returns the unix time.
|
||||
func parseModTime(expr ast.Expr) (int, error) {
|
||||
if expr, ok := expr.(*ast.CallExpr); ok {
|
||||
if !isSimpleSelector("time", "Unix", expr.Fun) {
|
||||
return 0, fmt.Errorf("ModTime is not time.Unix: %#v", expr.Fun)
|
||||
}
|
||||
if len(expr.Args) == 0 {
|
||||
return 0, fmt.Errorf("not enough args to time.Unix")
|
||||
}
|
||||
arg0 := expr.Args[0]
|
||||
if lit, ok := arg0.(*ast.BasicLit); ok && lit.Kind == token.INT {
|
||||
return strconv.Atoi(lit.Value)
|
||||
}
|
||||
}
|
||||
return 0, fmt.Errorf("not time.Unix: %#v", expr)
|
||||
}
|
||||
|
||||
func parseString(expr ast.Expr) (string, error) {
|
||||
if expr, ok := expr.(*ast.CallExpr); ok && isIdent("string", expr.Fun) && len(expr.Args) == 1 {
|
||||
return parseString(expr.Args[0])
|
||||
}
|
||||
if lit, ok := expr.(*ast.BasicLit); ok && lit.Kind == token.STRING {
|
||||
return strconv.Unquote(lit.Value)
|
||||
}
|
||||
return "", fmt.Errorf("not string: %#v", expr)
|
||||
}
|
||||
|
||||
// parseDir parses an embedded.EmbeddedDir literal.
|
||||
// It can be either a variable name or a composite literal.
|
||||
// Returns nil if the literal is not embedded.EmbeddedDir.
|
||||
func parseDir(expr ast.Expr, dirs map[string]*registeredDir, files map[string]*registeredFile) (*registeredDir, []error) {
|
||||
|
||||
if varName, ok := getIdentName(expr); ok {
|
||||
dir, ok := dirs[varName]
|
||||
if !ok {
|
||||
return nil, []error{fmt.Errorf("unknown variable %v", varName)}
|
||||
}
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
lit, ok := expr.(*ast.CompositeLit)
|
||||
if !ok {
|
||||
return nil, []error{fmt.Errorf("dir is not a composite literal: %#v", expr)}
|
||||
}
|
||||
|
||||
var errors []error
|
||||
if !isSimpleSelector("embedded", "EmbeddedDir", lit.Type) {
|
||||
return nil, nil
|
||||
}
|
||||
ret := ®isteredDir{}
|
||||
for _, el := range lit.Elts {
|
||||
if el, ok := el.(*ast.KeyValueExpr); ok {
|
||||
key := getKey(el)
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
switch key {
|
||||
case "DirModTime":
|
||||
var err error
|
||||
ret.ModTime, err = parseModTime(el.Value)
|
||||
if err != nil {
|
||||
errors = append(errors, fmt.Errorf("DirModTime %s", err))
|
||||
}
|
||||
case "Filename":
|
||||
var err error
|
||||
ret.Filename, err = parseString(el.Value)
|
||||
if err != nil {
|
||||
errors = append(errors, fmt.Errorf("Filename %s", err))
|
||||
}
|
||||
case "ChildDirs":
|
||||
var errors2 []error
|
||||
ret.ChildDirs, errors2 = parseDirsSlice(el.Value, dirs, files)
|
||||
errors = append(errors, errors2...)
|
||||
case "ChildFiles":
|
||||
var errors2 []error
|
||||
ret.ChildFiles, errors2 = parseFilesSlice(el.Value, files)
|
||||
errors = append(errors, errors2...)
|
||||
default:
|
||||
errors = append(errors, fmt.Errorf("Unknown field: %v: %#v", key, el.Value))
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret, errors
|
||||
}
|
||||
|
||||
// parseFile parses an embedded.EmbeddedFile literal.
|
||||
// It can be either a variable name or a composite literal.
|
||||
// Returns nil if the literal is not embedded.EmbeddedFile.
|
||||
func parseFile(expr ast.Expr, files map[string]*registeredFile) (*registeredFile, []error) {
|
||||
if varName, ok := getIdentName(expr); ok {
|
||||
file, ok := files[varName]
|
||||
if !ok {
|
||||
return nil, []error{fmt.Errorf("unknown variable %v", varName)}
|
||||
}
|
||||
return file, nil
|
||||
}
|
||||
|
||||
lit, ok := expr.(*ast.CompositeLit)
|
||||
if !ok {
|
||||
return nil, []error{fmt.Errorf("file is not a composite literal: %#v", expr)}
|
||||
}
|
||||
|
||||
var errors []error
|
||||
if !isSimpleSelector("embedded", "EmbeddedFile", lit.Type) {
|
||||
return nil, nil
|
||||
}
|
||||
ret := ®isteredFile{}
|
||||
for _, el := range lit.Elts {
|
||||
if el, ok := el.(*ast.KeyValueExpr); ok {
|
||||
key := getKey(el)
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
switch key {
|
||||
case "FileModTime":
|
||||
var err error
|
||||
ret.ModTime, err = parseModTime(el.Value)
|
||||
if err != nil {
|
||||
errors = append(errors, fmt.Errorf("DirModTime %s", err))
|
||||
}
|
||||
case "Filename":
|
||||
var err error
|
||||
ret.Filename, err = parseString(el.Value)
|
||||
if err != nil {
|
||||
errors = append(errors, fmt.Errorf("Filename %s", err))
|
||||
}
|
||||
case "Content":
|
||||
var err error
|
||||
ret.Content, err = parseString(el.Value)
|
||||
if err != nil {
|
||||
errors = append(errors, fmt.Errorf("Content %s", err))
|
||||
}
|
||||
default:
|
||||
errors = append(errors, fmt.Errorf("Unknown field: %v: %#v", key, el.Value))
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret, errors
|
||||
}
|
||||
|
||||
func parseRegistration(lit *ast.CompositeLit, dirs map[string]*registeredDir, files map[string]*registeredFile) (*registeredBox, []error) {
|
||||
var errors []error
|
||||
if !isSimpleSelector("embedded", "EmbeddedBox", lit.Type) {
|
||||
return nil, nil
|
||||
}
|
||||
ret := ®isteredBox{
|
||||
Dirs: make(map[string]*registeredDir),
|
||||
Files: make(map[string]*registeredFile),
|
||||
}
|
||||
for _, el := range lit.Elts {
|
||||
if el, ok := el.(*ast.KeyValueExpr); ok {
|
||||
key := getKey(el)
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
switch key {
|
||||
case "Time":
|
||||
var err error
|
||||
ret.Time, err = parseModTime(el.Value)
|
||||
if err != nil {
|
||||
errors = append(errors, fmt.Errorf("Time %s", err))
|
||||
}
|
||||
case "Name":
|
||||
var err error
|
||||
ret.Name, err = parseString(el.Value)
|
||||
if err != nil {
|
||||
errors = append(errors, fmt.Errorf("Name %s", err))
|
||||
}
|
||||
case "Dirs":
|
||||
var errors2 []error
|
||||
ret.Dirs, errors2 = parseDirsMap(el.Value, dirs, files)
|
||||
errors = append(errors, errors2...)
|
||||
case "Files":
|
||||
var errors2 []error
|
||||
ret.Files, errors2 = parseFilesMap(el.Value, files)
|
||||
errors = append(errors, errors2...)
|
||||
default:
|
||||
errors = append(errors, fmt.Errorf("Unknown field: %v: %#v", key, el.Value))
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret, errors
|
||||
}
|
||||
|
||||
func parseDirsSlice(expr ast.Expr, dirs map[string]*registeredDir, files map[string]*registeredFile) (childDirs []*registeredDir, errors []error) {
|
||||
valid := false
|
||||
lit, ok := expr.(*ast.CompositeLit)
|
||||
if ok {
|
||||
if arrType, ok := lit.Type.(*ast.ArrayType); ok {
|
||||
if star, ok := arrType.Elt.(*ast.StarExpr); ok {
|
||||
if isSimpleSelector("embedded", "EmbeddedDir", star.X) {
|
||||
valid = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !valid {
|
||||
return nil, []error{fmt.Errorf("not a []*embedded.EmbeddedDir: %#v", expr)}
|
||||
}
|
||||
for _, el := range lit.Elts {
|
||||
child, childErrors := parseDir(el, dirs, files)
|
||||
errors = append(errors, childErrors...)
|
||||
childDirs = append(childDirs, child)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func parseFilesSlice(expr ast.Expr, files map[string]*registeredFile) (childFiles []*registeredFile, errors []error) {
|
||||
valid := false
|
||||
lit, ok := expr.(*ast.CompositeLit)
|
||||
if ok {
|
||||
if arrType, ok := lit.Type.(*ast.ArrayType); ok {
|
||||
if star, ok := arrType.Elt.(*ast.StarExpr); ok {
|
||||
if isSimpleSelector("embedded", "EmbeddedFile", star.X) {
|
||||
valid = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !valid {
|
||||
return nil, []error{fmt.Errorf("not a []*embedded.EmbeddedFile: %#v", expr)}
|
||||
}
|
||||
for _, el := range lit.Elts {
|
||||
child, childErrors := parseFile(el, files)
|
||||
errors = append(errors, childErrors...)
|
||||
childFiles = append(childFiles, child)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func parseDirsMap(expr ast.Expr, dirs map[string]*registeredDir, files map[string]*registeredFile) (childDirs map[string]*registeredDir, errors []error) {
|
||||
valid := false
|
||||
lit, ok := expr.(*ast.CompositeLit)
|
||||
if ok {
|
||||
if mapType, ok := lit.Type.(*ast.MapType); ok {
|
||||
if star, ok := mapType.Value.(*ast.StarExpr); ok {
|
||||
if isSimpleSelector("embedded", "EmbeddedDir", star.X) && isIdent("string", mapType.Key) {
|
||||
valid = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !valid {
|
||||
return nil, []error{fmt.Errorf("not a map[string]*embedded.EmbeddedDir: %#v", expr)}
|
||||
}
|
||||
childDirs = make(map[string]*registeredDir)
|
||||
for _, el := range lit.Elts {
|
||||
kv, ok := el.(*ast.KeyValueExpr)
|
||||
if !ok {
|
||||
errors = append(errors, fmt.Errorf("not a KeyValueExpr: %#v", el))
|
||||
continue
|
||||
}
|
||||
key, err := parseString(kv.Key)
|
||||
if err != nil {
|
||||
errors = append(errors, fmt.Errorf("key %s", err))
|
||||
continue
|
||||
}
|
||||
|
||||
child, childErrors := parseDir(kv.Value, dirs, files)
|
||||
errors = append(errors, childErrors...)
|
||||
childDirs[key] = child
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func parseFilesMap(expr ast.Expr, files map[string]*registeredFile) (childFiles map[string]*registeredFile, errors []error) {
|
||||
valid := false
|
||||
lit, ok := expr.(*ast.CompositeLit)
|
||||
if ok {
|
||||
if mapType, ok := lit.Type.(*ast.MapType); ok {
|
||||
if star, ok := mapType.Value.(*ast.StarExpr); ok {
|
||||
if isSimpleSelector("embedded", "EmbeddedFile", star.X) && isIdent("string", mapType.Key) {
|
||||
valid = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !valid {
|
||||
return nil, []error{fmt.Errorf("not a map[string]*embedded.EmbeddedFile: %#v", expr)}
|
||||
}
|
||||
childFiles = make(map[string]*registeredFile)
|
||||
for _, el := range lit.Elts {
|
||||
kv, ok := el.(*ast.KeyValueExpr)
|
||||
if !ok {
|
||||
errors = append(errors, fmt.Errorf("not a KeyValueExpr: %#v", el))
|
||||
continue
|
||||
}
|
||||
key, err := parseString(kv.Key)
|
||||
if err != nil {
|
||||
errors = append(errors, fmt.Errorf("key %s", err))
|
||||
continue
|
||||
}
|
||||
|
||||
child, childErrors := parseFile(kv.Value, files)
|
||||
errors = append(errors, childErrors...)
|
||||
childFiles[key] = child
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// unpoint returns the expression expr points to
|
||||
// if expr is a & unary expression.
|
||||
func unpoint(expr ast.Expr) ast.Expr {
|
||||
if expr, ok := expr.(*ast.UnaryExpr); ok {
|
||||
if expr.Op == token.AND {
|
||||
return expr.X
|
||||
}
|
||||
}
|
||||
return expr
|
||||
}
|
||||
|
||||
func validateBox(t *testing.T, box *registeredBox, files []sourceFile) {
|
||||
dirsToBeChecked := make(map[string]struct{})
|
||||
filesToBeChecked := make(map[string]string)
|
||||
for _, file := range files {
|
||||
if !strings.HasPrefix(file.Name, box.Name) {
|
||||
continue
|
||||
}
|
||||
pathParts := strings.Split(file.Name, "/")
|
||||
dirs := pathParts[:len(pathParts)-1]
|
||||
dirPath := ""
|
||||
for _, dir := range dirs {
|
||||
if dir != box.Name {
|
||||
dirPath = path.Join(dirPath, dir)
|
||||
}
|
||||
dirsToBeChecked[dirPath] = struct{}{}
|
||||
}
|
||||
filesToBeChecked[path.Join(dirPath, pathParts[len(pathParts)-1])] = string(file.Contents)
|
||||
}
|
||||
|
||||
if len(box.Files) != len(filesToBeChecked) {
|
||||
t.Errorf("box %v has incorrect number of files; expected %v, got %v", box.Name, len(filesToBeChecked), len(box.Files))
|
||||
}
|
||||
|
||||
if len(box.Dirs) != len(dirsToBeChecked) {
|
||||
t.Errorf("box %v has incorrect number of dirs; expected %v, got %v", box.Name, len(dirsToBeChecked), len(box.Dirs))
|
||||
}
|
||||
|
||||
for name, content := range filesToBeChecked {
|
||||
f, ok := box.Files[name]
|
||||
if !ok {
|
||||
t.Errorf("file %v not present in box %v", name, box.Name)
|
||||
continue
|
||||
}
|
||||
if f.Filename != name {
|
||||
t.Errorf("box %v: filename mismatch: key: %v; Filename: %v", box.Name, name, f.Filename)
|
||||
}
|
||||
if f.Content != content {
|
||||
t.Errorf("box %v: file %v content does not match: got %v, expected %v", box.Name, name, f.Content, content)
|
||||
}
|
||||
dirPath, _ := path.Split(name)
|
||||
dirPath = strings.TrimSuffix(dirPath, "/")
|
||||
dir, ok := box.Dirs[dirPath]
|
||||
if !ok {
|
||||
t.Errorf("directory %v not present in box %v", dirPath, box.Name)
|
||||
continue
|
||||
}
|
||||
found := false
|
||||
for _, file := range dir.ChildFiles {
|
||||
if file == f {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("file %v not found in directory %v in box %v", name, dirPath, box.Name)
|
||||
continue
|
||||
}
|
||||
}
|
||||
for name := range dirsToBeChecked {
|
||||
d, ok := box.Dirs[name]
|
||||
if !ok {
|
||||
t.Errorf("directory %v not present in box %v", name, box.Name)
|
||||
continue
|
||||
}
|
||||
if d.Filename != name {
|
||||
t.Errorf("box %v: filename mismatch: key: %v; Filename: %v", box.Name, name, d.Filename)
|
||||
}
|
||||
if name != "" {
|
||||
dirPath, _ := path.Split(name)
|
||||
dirPath = strings.TrimSuffix(dirPath, "/")
|
||||
dir, ok := box.Dirs[dirPath]
|
||||
if !ok {
|
||||
t.Errorf("directory %v not present in box %v", dirPath, box.Name)
|
||||
continue
|
||||
}
|
||||
found := false
|
||||
for _, dir := range dir.ChildDirs {
|
||||
if dir == d {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("directory %v not found in directory %v in box %v", name, dirPath, box.Name)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmbedGo(t *testing.T) {
|
||||
sourceFiles := []sourceFile{
|
||||
{
|
||||
"boxes.go",
|
||||
[]byte(`package main
|
||||
|
||||
import (
|
||||
"github.com/GeertJohan/go.rice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
rice.MustFindBox("foo")
|
||||
}
|
||||
`),
|
||||
},
|
||||
{
|
||||
"foo/test1.txt",
|
||||
[]byte(`This is test 1`),
|
||||
},
|
||||
{
|
||||
"foo/test2.txt",
|
||||
[]byte(`This is test 2`),
|
||||
},
|
||||
{
|
||||
"foo/bar/test1.txt",
|
||||
[]byte(`This is test 1 in bar`),
|
||||
},
|
||||
{
|
||||
"foo/bar/baz/test1.txt",
|
||||
[]byte(`This is test 1 in bar/baz`),
|
||||
},
|
||||
{
|
||||
"foo/bar/baz/backtick`.txt",
|
||||
[]byte(`Backtick filename`),
|
||||
},
|
||||
{
|
||||
"foo/bar/baz/\"quote\".txt",
|
||||
[]byte(`double quoted filename`),
|
||||
},
|
||||
{
|
||||
"foo/bar/baz/'quote'.txt",
|
||||
[]byte(`single quoted filename`),
|
||||
},
|
||||
{
|
||||
"foo/`/`/`.txt",
|
||||
[]byte(`Backticks everywhere!`),
|
||||
},
|
||||
{
|
||||
"foo/new\nline",
|
||||
[]byte("File with newline in name. Yes, this is possible."),
|
||||
},
|
||||
}
|
||||
pkg, cleanup, err := setUpTestPkg("foobar", sourceFiles)
|
||||
defer cleanup()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
var buffer bytes.Buffer
|
||||
|
||||
err = writeBoxesGo(pkg, &buffer)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("Generated file: \n%s", buffer.String())
|
||||
fset := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset, filepath.Join(pkg.Dir, "rice-box.go"), &buffer, 0)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
var initFunc *ast.FuncDecl
|
||||
for _, decl := range f.Decls {
|
||||
if decl, ok := decl.(*ast.FuncDecl); ok && decl.Name != nil && decl.Name.Name == "init" {
|
||||
initFunc = decl
|
||||
break
|
||||
}
|
||||
}
|
||||
if initFunc == nil {
|
||||
t.Fatal("init function not found in generated file")
|
||||
}
|
||||
if initFunc.Body == nil {
|
||||
t.Fatal("init function has no body in generated file")
|
||||
}
|
||||
var registrations []*ast.CallExpr
|
||||
directories := make(map[string]*registeredDir)
|
||||
files := make(map[string]*registeredFile)
|
||||
_ = directories
|
||||
_ = files
|
||||
for _, stmt := range initFunc.Body.List {
|
||||
if stmt, ok := stmt.(*ast.ExprStmt); ok {
|
||||
if call, ok := stmt.X.(*ast.CallExpr); ok {
|
||||
registrations = append(registrations, call)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if stmt, ok := stmt.(*ast.AssignStmt); ok {
|
||||
for i, rhs := range stmt.Rhs {
|
||||
// Rhs can be EmbeddedDir or EmbeddedFile.
|
||||
var literal *ast.CompositeLit
|
||||
literal, ok := unpoint(rhs).(*ast.CompositeLit)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if lhs, ok := stmt.Lhs[i].(*ast.Ident); ok {
|
||||
// variable
|
||||
edir, direrrs := parseDir(literal, directories, files)
|
||||
efile, fileerrs := parseFile(literal, files)
|
||||
abort := false
|
||||
for _, err := range direrrs {
|
||||
t.Error("error while parsing dir: ", err)
|
||||
abort = true
|
||||
}
|
||||
for _, err := range fileerrs {
|
||||
t.Error("error while parsing file: ", err)
|
||||
abort = true
|
||||
}
|
||||
if abort {
|
||||
return
|
||||
}
|
||||
|
||||
if edir == nil && efile == nil {
|
||||
continue
|
||||
}
|
||||
if edir != nil {
|
||||
directories[lhs.Name] = edir
|
||||
} else {
|
||||
files[lhs.Name] = efile
|
||||
}
|
||||
} else if lhs, ok := stmt.Lhs[i].(*ast.SelectorExpr); ok {
|
||||
selName, ok := getIdentName(lhs.Sel)
|
||||
if !ok || selName != "ChildDirs" {
|
||||
continue
|
||||
}
|
||||
varName, ok := getIdentName(lhs.X)
|
||||
if !ok {
|
||||
t.Fatalf("cannot parse ChildDirs assignment: %#v", lhs)
|
||||
}
|
||||
dir, ok := directories[varName]
|
||||
if !ok {
|
||||
t.Fatalf("variable %v not found", varName)
|
||||
}
|
||||
|
||||
var errors []error
|
||||
dir.ChildDirs, errors = parseDirsSlice(rhs, directories, files)
|
||||
|
||||
abort := false
|
||||
for _, err := range errors {
|
||||
t.Errorf("error parsing child dirs: %s", err)
|
||||
abort = true
|
||||
}
|
||||
if abort {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(registrations) == 0 {
|
||||
t.Fatal("could not find registration of embedded box")
|
||||
}
|
||||
|
||||
boxes := make(map[string]*registeredBox)
|
||||
|
||||
for _, call := range registrations {
|
||||
if isSimpleSelector("embedded", "RegisterEmbeddedBox", call.Fun) {
|
||||
if len(call.Args) != 2 {
|
||||
t.Fatalf("incorrect arguments to embedded.RegisterEmbeddedBox: %#v", call.Args)
|
||||
}
|
||||
boxArg := unpoint(call.Args[1])
|
||||
name, err := parseString(call.Args[0])
|
||||
if err != nil {
|
||||
t.Fatalf("first argument to embedded.RegisterEmbeddedBox incorrect: %s", err)
|
||||
}
|
||||
boxLit, ok := boxArg.(*ast.CompositeLit)
|
||||
if !ok {
|
||||
t.Fatalf("second argument to embedded.RegisterEmbeddedBox is not a composite literal: %#v", boxArg)
|
||||
}
|
||||
abort := false
|
||||
box, errors := parseRegistration(boxLit, directories, files)
|
||||
for _, err := range errors {
|
||||
t.Error("error while parsing box: ", err)
|
||||
abort = true
|
||||
}
|
||||
if abort {
|
||||
return
|
||||
}
|
||||
if box == nil {
|
||||
t.Fatalf("second argument to embedded.RegisterEmbeddedBox is not an embedded.EmbeddedBox: %#v", boxArg)
|
||||
}
|
||||
if box.Name != name {
|
||||
t.Fatalf("first argument to embedded.RegisterEmbeddedBox is not the same as the name in the second argument: %v, %#v", name, boxArg)
|
||||
}
|
||||
boxes[name] = box
|
||||
}
|
||||
}
|
||||
|
||||
// Validate that all boxes are present.
|
||||
if _, ok := boxes["foo"]; !ok {
|
||||
t.Error("box \"foo\" not found")
|
||||
}
|
||||
for _, box := range boxes {
|
||||
validateBox(t, box, sourceFiles)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,204 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/GeertJohan/go.rice/embedded"
|
||||
"github.com/akavel/rsrc/coff"
|
||||
)
|
||||
|
||||
type sizedReader struct {
|
||||
*bytes.Reader
|
||||
}
|
||||
|
||||
func (s sizedReader) Size() int64 {
|
||||
return int64(s.Len())
|
||||
}
|
||||
|
||||
var tmplEmbeddedSysoHelper *template.Template
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
tmplEmbeddedSysoHelper, err = template.New("embeddedSysoHelper").Parse(`package {{.Package}}
|
||||
// ############# GENERATED CODE #####################
|
||||
// ## This file was generated by the rice tool.
|
||||
// ## Do not edit unless you know what you're doing.
|
||||
// ##################################################
|
||||
|
||||
// extern char _bricebox_{{.Symname}}[], _ericebox_{{.Symname}};
|
||||
// int get_{{.Symname}}_length() {
|
||||
// return &_ericebox_{{.Symname}} - _bricebox_{{.Symname}};
|
||||
// }
|
||||
import "C"
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"github.com/GeertJohan/go.rice/embedded"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func init() {
|
||||
ptr := unsafe.Pointer(&C._bricebox_{{.Symname}})
|
||||
bts := C.GoBytes(ptr, C.get_{{.Symname}}_length())
|
||||
embeddedBox := &embedded.EmbeddedBox{}
|
||||
err := gob.NewDecoder(bytes.NewReader(bts)).Decode(embeddedBox)
|
||||
if err != nil {
|
||||
panic("error decoding embedded box: "+err.Error())
|
||||
}
|
||||
embeddedBox.Link()
|
||||
embedded.RegisterEmbeddedBox(embeddedBox.Name, embeddedBox)
|
||||
}`)
|
||||
if err != nil {
|
||||
panic("could not parse template embeddedSysoHelper: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
type embeddedSysoHelperData struct {
|
||||
Package string
|
||||
Symname string
|
||||
}
|
||||
|
||||
func operationEmbedSyso(pkg *build.Package) {
|
||||
|
||||
regexpSynameReplacer := regexp.MustCompile(`[^a-z0-9_]`)
|
||||
|
||||
boxMap := findBoxes(pkg)
|
||||
|
||||
// notify user when no calls to rice.FindBox are made (is this an error and therefore os.Exit(1) ?
|
||||
if len(boxMap) == 0 {
|
||||
fmt.Println("no calls to rice.FindBox() found")
|
||||
return
|
||||
}
|
||||
|
||||
verbosef("\n")
|
||||
|
||||
for boxname := range boxMap {
|
||||
// find path and filename for this box
|
||||
boxPath := filepath.Join(pkg.Dir, boxname)
|
||||
boxFilename := strings.Replace(boxname, "/", "-", -1)
|
||||
boxFilename = strings.Replace(boxFilename, "..", "back", -1)
|
||||
boxFilename = strings.Replace(boxFilename, ".", "-", -1)
|
||||
|
||||
// verbose info
|
||||
verbosef("embedding box '%s'\n", boxname)
|
||||
verbosef("\tto file %s\n", boxFilename)
|
||||
|
||||
// read box metadata
|
||||
boxInfo, ierr := os.Stat(boxPath)
|
||||
if ierr != nil {
|
||||
fmt.Printf("Error: unable to access box at %s\n", boxPath)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// create box datastructure (used by template)
|
||||
box := &embedded.EmbeddedBox{
|
||||
Name: boxname,
|
||||
Time: boxInfo.ModTime(),
|
||||
EmbedType: embedded.EmbedTypeSyso,
|
||||
Files: make(map[string]*embedded.EmbeddedFile),
|
||||
Dirs: make(map[string]*embedded.EmbeddedDir),
|
||||
}
|
||||
|
||||
// fill box datastructure with file data
|
||||
filepath.Walk(boxPath, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
fmt.Printf("error walking box: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
filename := strings.TrimPrefix(path, boxPath)
|
||||
filename = strings.Replace(filename, "\\", "/", -1)
|
||||
filename = strings.TrimPrefix(filename, "/")
|
||||
if info.IsDir() {
|
||||
embeddedDir := &embedded.EmbeddedDir{
|
||||
Filename: filename,
|
||||
DirModTime: info.ModTime(),
|
||||
}
|
||||
verbosef("\tincludes dir: '%s'\n", embeddedDir.Filename)
|
||||
box.Dirs[embeddedDir.Filename] = embeddedDir
|
||||
|
||||
// add tree entry (skip for root, it'll create a recursion)
|
||||
if embeddedDir.Filename != "" {
|
||||
pathParts := strings.Split(embeddedDir.Filename, "/")
|
||||
parentDir := box.Dirs[strings.Join(pathParts[:len(pathParts)-1], "/")]
|
||||
parentDir.ChildDirs = append(parentDir.ChildDirs, embeddedDir)
|
||||
}
|
||||
} else {
|
||||
embeddedFile := &embedded.EmbeddedFile{
|
||||
Filename: filename,
|
||||
FileModTime: info.ModTime(),
|
||||
Content: "",
|
||||
}
|
||||
verbosef("\tincludes file: '%s'\n", embeddedFile.Filename)
|
||||
contentBytes, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
fmt.Printf("error reading file content while walking box: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
embeddedFile.Content = string(contentBytes)
|
||||
box.Files[embeddedFile.Filename] = embeddedFile
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// encode embedded box to gob file
|
||||
boxGobBuf := &bytes.Buffer{}
|
||||
err := gob.NewEncoder(boxGobBuf).Encode(box)
|
||||
if err != nil {
|
||||
fmt.Printf("error encoding box to gob: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
verbosef("gob-encoded embeddedBox is %d bytes large\n", boxGobBuf.Len())
|
||||
|
||||
// write coff
|
||||
symname := regexpSynameReplacer.ReplaceAllString(boxname, "_")
|
||||
createCoffSyso(boxname, symname, "386", boxGobBuf.Bytes())
|
||||
createCoffSyso(boxname, symname, "amd64", boxGobBuf.Bytes())
|
||||
|
||||
// write go
|
||||
sysoHelperData := embeddedSysoHelperData{
|
||||
Package: pkg.Name,
|
||||
Symname: symname,
|
||||
}
|
||||
fileSysoHelper, err := os.Create(boxFilename + ".rice-box.go")
|
||||
if err != nil {
|
||||
fmt.Printf("error creating syso helper: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
err = tmplEmbeddedSysoHelper.Execute(fileSysoHelper, sysoHelperData)
|
||||
if err != nil {
|
||||
fmt.Printf("error executing tmplEmbeddedSysoHelper: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createCoffSyso(boxFilename string, symname string, arch string, data []byte) {
|
||||
boxCoff := coff.NewRDATA()
|
||||
switch arch {
|
||||
case "386":
|
||||
case "amd64":
|
||||
boxCoff.FileHeader.Machine = 0x8664
|
||||
default:
|
||||
panic("invalid arch")
|
||||
}
|
||||
boxCoff.AddData("_bricebox_"+symname, sizedReader{bytes.NewReader(data)})
|
||||
boxCoff.AddData("_ericebox_"+symname, io.NewSectionReader(strings.NewReader("\000\000"), 0, 2)) // TODO: why? copied from rsrc, which copied it from as-generated
|
||||
boxCoff.Freeze()
|
||||
err := writeCoff(boxCoff, boxFilename+"_"+arch+".rice-box.syso")
|
||||
if err != nil {
|
||||
fmt.Printf("error writing %s coff/.syso: %v\n", arch, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func badArgument(fileset *token.FileSet, p token.Pos) {
|
||||
pos := fileset.Position(p)
|
||||
filename := pos.Filename
|
||||
base, err := os.Getwd()
|
||||
if err == nil {
|
||||
rpath, perr := filepath.Rel(base, pos.Filename)
|
||||
if perr == nil {
|
||||
filename = rpath
|
||||
}
|
||||
}
|
||||
msg := fmt.Sprintf("%s:%d: Error: found call to rice.FindBox, "+
|
||||
"but argument must be a string literal.\n", filename, pos.Line)
|
||||
fmt.Println(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func findBoxes(pkg *build.Package) map[string]bool {
|
||||
// create map of boxes to embed
|
||||
var boxMap = make(map[string]bool)
|
||||
|
||||
// create one list of files for this package
|
||||
filenames := make([]string, 0, len(pkg.GoFiles)+len(pkg.CgoFiles))
|
||||
filenames = append(filenames, pkg.GoFiles...)
|
||||
filenames = append(filenames, pkg.CgoFiles...)
|
||||
|
||||
// loop over files, search for rice.FindBox(..) calls
|
||||
for _, filename := range filenames {
|
||||
// find full filepath
|
||||
fullpath := filepath.Join(pkg.Dir, filename)
|
||||
if strings.HasSuffix(filename, "rice-box.go") {
|
||||
// Ignore *.rice-box.go files
|
||||
verbosef("skipping file %q\n", fullpath)
|
||||
continue
|
||||
}
|
||||
verbosef("scanning file %q\n", fullpath)
|
||||
|
||||
fset := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset, fullpath, nil, 0)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var riceIsImported bool
|
||||
ricePkgName := "rice"
|
||||
for _, imp := range f.Imports {
|
||||
if strings.HasSuffix(imp.Path.Value, "go.rice\"") {
|
||||
if imp.Name != nil {
|
||||
ricePkgName = imp.Name.Name
|
||||
}
|
||||
riceIsImported = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !riceIsImported {
|
||||
// Rice wasn't imported, so we won't find a box.
|
||||
continue
|
||||
}
|
||||
if ricePkgName == "_" {
|
||||
// Rice pkg is unnamed, so we won't find a box.
|
||||
continue
|
||||
}
|
||||
|
||||
// Inspect AST, looking for calls to (Must)?FindBox.
|
||||
// First parameter of the func must be a basic literal.
|
||||
// Identifiers won't be resolved.
|
||||
var nextIdentIsBoxFunc bool
|
||||
var nextBasicLitParamIsBoxName bool
|
||||
var boxCall token.Pos
|
||||
var variableToRemember string
|
||||
var validVariablesForBoxes map[string]bool = make(map[string]bool)
|
||||
|
||||
ast.Inspect(f, func(node ast.Node) bool {
|
||||
if node == nil {
|
||||
return false
|
||||
}
|
||||
switch x := node.(type) {
|
||||
// this case fixes the var := func() style assignments, not assignments to vars declared separately from the assignment.
|
||||
case *ast.AssignStmt:
|
||||
var assign = node.(*ast.AssignStmt)
|
||||
name, found := assign.Lhs[0].(*ast.Ident)
|
||||
if found {
|
||||
variableToRemember = name.Name
|
||||
composite, first := assign.Rhs[0].(*ast.CompositeLit)
|
||||
if first {
|
||||
riceSelector, second := composite.Type.(*ast.SelectorExpr)
|
||||
|
||||
if second {
|
||||
callCorrect := riceSelector.Sel.Name == "Config"
|
||||
packageName, third := riceSelector.X.(*ast.Ident)
|
||||
|
||||
if third && callCorrect && packageName.Name == ricePkgName {
|
||||
validVariablesForBoxes[name.Name] = true
|
||||
verbosef("\tfound variable, saving to scan for boxes: %q\n", name.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case *ast.Ident:
|
||||
if nextIdentIsBoxFunc || ricePkgName == "." {
|
||||
nextIdentIsBoxFunc = false
|
||||
if x.Name == "FindBox" || x.Name == "MustFindBox" {
|
||||
nextBasicLitParamIsBoxName = true
|
||||
boxCall = x.Pos()
|
||||
}
|
||||
} else {
|
||||
if x.Name == ricePkgName || validVariablesForBoxes[x.Name] {
|
||||
nextIdentIsBoxFunc = true
|
||||
}
|
||||
}
|
||||
case *ast.BasicLit:
|
||||
if nextBasicLitParamIsBoxName {
|
||||
if x.Kind == token.STRING {
|
||||
nextBasicLitParamIsBoxName = false
|
||||
// trim "" or ``
|
||||
name := x.Value[1 : len(x.Value)-1]
|
||||
boxMap[name] = true
|
||||
verbosef("\tfound box %q\n", name)
|
||||
} else {
|
||||
badArgument(fset, boxCall)
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
if nextIdentIsBoxFunc {
|
||||
nextIdentIsBoxFunc = false
|
||||
}
|
||||
if nextBasicLitParamIsBoxName {
|
||||
badArgument(fset, boxCall)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
return boxMap
|
||||
}
|
|
@ -0,0 +1,302 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type sourceFile struct {
|
||||
Name string
|
||||
Contents []byte
|
||||
}
|
||||
|
||||
func expectBoxes(expected []string, actual map[string]bool) error {
|
||||
if len(expected) != len(actual) {
|
||||
return fmt.Errorf("expected %v, got %v", expected, actual)
|
||||
}
|
||||
for _, box := range expected {
|
||||
if _, ok := actual[box]; !ok {
|
||||
return fmt.Errorf("expected %v, got %v", expected, actual)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setUpTestPkg(pkgName string, files []sourceFile) (*build.Package, func(), error) {
|
||||
temp, err := ioutil.TempDir("", "go.rice-test")
|
||||
if err != nil {
|
||||
return nil, func() {}, err
|
||||
}
|
||||
cleanup := func() {
|
||||
os.RemoveAll(temp)
|
||||
}
|
||||
dir := filepath.Join(temp, pkgName)
|
||||
if err := os.Mkdir(dir, 0770); err != nil {
|
||||
return nil, cleanup, err
|
||||
}
|
||||
for _, f := range files {
|
||||
fullPath := filepath.Join(dir, f.Name)
|
||||
if err := os.MkdirAll(filepath.Dir(fullPath), 0770); err != nil {
|
||||
return nil, cleanup, err
|
||||
}
|
||||
if err := ioutil.WriteFile(fullPath, f.Contents, 0660); err != nil {
|
||||
return nil, cleanup, err
|
||||
}
|
||||
}
|
||||
pkg, err := build.ImportDir(dir, 0)
|
||||
return pkg, cleanup, err
|
||||
}
|
||||
|
||||
func TestFindOneBox(t *testing.T) {
|
||||
pkg, cleanup, err := setUpTestPkg("foobar", []sourceFile{
|
||||
{
|
||||
"boxes.go",
|
||||
[]byte(`package main
|
||||
|
||||
import (
|
||||
"github.com/GeertJohan/go.rice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
rice.MustFindBox("foo")
|
||||
}
|
||||
`),
|
||||
},
|
||||
})
|
||||
defer cleanup()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
expectedBoxes := []string{"foo"}
|
||||
boxMap := findBoxes(pkg)
|
||||
if err := expectBoxes(expectedBoxes, boxMap); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindOneBoxViaVariable(t *testing.T) {
|
||||
|
||||
pkg, cleanup, err := setUpTestPkg("foobar", []sourceFile{
|
||||
{
|
||||
"boxes.go",
|
||||
[]byte(`package main
|
||||
|
||||
import (
|
||||
"github.com/GeertJohan/go.rice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
conf := rice.Config{
|
||||
LocateOrder: []rice.LocateMethod{rice.LocateEmbedded, rice.LocateAppended, rice.LocateFS},
|
||||
}
|
||||
conf.MustFindBox("foo")
|
||||
}
|
||||
`),
|
||||
},
|
||||
})
|
||||
defer cleanup()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
expectedBoxes := []string{"foo"}
|
||||
boxMap := findBoxes(pkg)
|
||||
if err := expectBoxes(expectedBoxes, boxMap); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindMultipleBoxes(t *testing.T) {
|
||||
pkg, cleanup, err := setUpTestPkg("foobar", []sourceFile{
|
||||
{
|
||||
"boxes.go",
|
||||
[]byte(`package main
|
||||
|
||||
import (
|
||||
"github.com/GeertJohan/go.rice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
rice.MustFindBox("foo")
|
||||
rice.MustFindBox("bar")
|
||||
}
|
||||
`),
|
||||
},
|
||||
})
|
||||
defer cleanup()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
expectedBoxes := []string{"foo", "bar"}
|
||||
boxMap := findBoxes(pkg)
|
||||
if err := expectBoxes(expectedBoxes, boxMap); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoBoxFoundIfRiceNotImported(t *testing.T) {
|
||||
pkg, cleanup, err := setUpTestPkg("foobar", []sourceFile{
|
||||
{
|
||||
"boxes.go",
|
||||
[]byte(`package main
|
||||
type fakerice struct {}
|
||||
|
||||
func (fr fakerice) FindBox(s string) {
|
||||
}
|
||||
|
||||
func main() {
|
||||
rice := fakerice{}
|
||||
rice.FindBox("foo")
|
||||
}
|
||||
`),
|
||||
},
|
||||
})
|
||||
defer cleanup()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
boxMap := findBoxes(pkg)
|
||||
if _, ok := boxMap["foo"]; ok {
|
||||
t.Errorf("Unexpected box %q was found", "foo")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnrelatedBoxesAreNotFound(t *testing.T) {
|
||||
pkg, cleanup, err := setUpTestPkg("foobar", []sourceFile{
|
||||
{
|
||||
"boxes.go",
|
||||
[]byte(`package foobar
|
||||
|
||||
import (
|
||||
_ "github.com/GeertJohan/go.rice"
|
||||
)
|
||||
|
||||
type fakerice struct {}
|
||||
|
||||
func (fr fakerice) FindBox(s string) {
|
||||
}
|
||||
|
||||
func FindBox(s string) {
|
||||
|
||||
}
|
||||
|
||||
func LoadBoxes() {
|
||||
rice := fakerice{}
|
||||
rice.FindBox("foo")
|
||||
|
||||
FindBox("bar")
|
||||
}
|
||||
`),
|
||||
},
|
||||
})
|
||||
defer cleanup()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
boxMap := findBoxes(pkg)
|
||||
for _, box := range []string{"foo", "bar"} {
|
||||
if _, ok := boxMap[box]; ok {
|
||||
t.Errorf("Unexpected box %q was found", box)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMixGoodAndBadBoxes(t *testing.T) {
|
||||
pkg, cleanup, err := setUpTestPkg("foobar", []sourceFile{
|
||||
{
|
||||
"boxes1.go",
|
||||
[]byte(`package foobar
|
||||
|
||||
import (
|
||||
_ "github.com/GeertJohan/go.rice"
|
||||
)
|
||||
|
||||
type fakerice struct {}
|
||||
|
||||
func (fr fakerice) FindBox(s string) {
|
||||
}
|
||||
|
||||
func FindBox(s string) {
|
||||
|
||||
}
|
||||
|
||||
func LoadBoxes1() {
|
||||
rice := fakerice{}
|
||||
rice.FindBox("foo")
|
||||
|
||||
FindBox("bar")
|
||||
}
|
||||
`),
|
||||
},
|
||||
{
|
||||
"boxes2.go",
|
||||
[]byte(`package foobar
|
||||
|
||||
import (
|
||||
noodles "github.com/GeertJohan/go.rice"
|
||||
)
|
||||
|
||||
func LoadBoxes2() {
|
||||
FindBox("baz")
|
||||
noodles.FindBox("veggies")
|
||||
}
|
||||
`),
|
||||
},
|
||||
{
|
||||
"boxes3.go",
|
||||
[]byte(`package foobar
|
||||
|
||||
import (
|
||||
"github.com/GeertJohan/go.rice"
|
||||
)
|
||||
|
||||
func LoadBoxes3() {
|
||||
rice.FindBox("fish")
|
||||
}
|
||||
`),
|
||||
},
|
||||
{
|
||||
"boxes4.go",
|
||||
[]byte(`package foobar
|
||||
|
||||
import (
|
||||
. "github.com/GeertJohan/go.rice"
|
||||
)
|
||||
|
||||
func LoadBoxes3() {
|
||||
MustFindBox("chicken")
|
||||
}
|
||||
`),
|
||||
},
|
||||
})
|
||||
defer cleanup()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
boxMap := findBoxes(pkg)
|
||||
for _, box := range []string{"foo", "bar", "baz"} {
|
||||
if _, ok := boxMap[box]; ok {
|
||||
t.Errorf("Unexpected box %q was found", box)
|
||||
}
|
||||
}
|
||||
for _, box := range []string{"veggies", "fish", "chicken"} {
|
||||
if _, ok := boxMap[box]; !ok {
|
||||
t.Errorf("Expected box %q not found", box)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/build"
|
||||
"os"
|
||||
|
||||
goflags "github.com/jessevdk/go-flags" // rename import to `goflags` (file scope) so we can use `var flags` (package scope)
|
||||
)
|
||||
|
||||
// flags
|
||||
var flags struct {
|
||||
Verbose bool `long:"verbose" short:"v" description:"Show verbose debug information"`
|
||||
ImportPaths []string `long:"import-path" short:"i" description:"Import path(s) to use. Using PWD when left empty. Specify multiple times for more import paths to append"`
|
||||
|
||||
Append struct {
|
||||
Executable string `long:"exec" description:"Executable to append" required:"true"`
|
||||
} `command:"append"`
|
||||
|
||||
EmbedGo struct{} `command:"embed-go" alias:"embed"`
|
||||
EmbedSyso struct{} `command:"embed-syso"`
|
||||
Clean struct{} `command:"clean"`
|
||||
}
|
||||
|
||||
// flags parser
|
||||
var flagsParser *goflags.Parser
|
||||
|
||||
// initFlags parses the given flags.
|
||||
// when the user asks for help (-h or --help): the application exists with status 0
|
||||
// when unexpected flags is given: the application exits with status 1
|
||||
func parseArguments() {
|
||||
// create flags parser in global var, for flagsParser.Active.Name (operation)
|
||||
flagsParser = goflags.NewParser(&flags, goflags.Default)
|
||||
|
||||
// parse flags
|
||||
args, err := flagsParser.Parse()
|
||||
if err != nil {
|
||||
// assert the err to be a flags.Error
|
||||
flagError := err.(*goflags.Error)
|
||||
if flagError.Type == goflags.ErrHelp {
|
||||
// user asked for help on flags.
|
||||
// program can exit successfully
|
||||
os.Exit(0)
|
||||
}
|
||||
if flagError.Type == goflags.ErrUnknownFlag {
|
||||
fmt.Println("Use --help to view available options.")
|
||||
os.Exit(1)
|
||||
}
|
||||
if flagError.Type == goflags.ErrRequired {
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Error parsing flags: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// error on left-over arguments
|
||||
if len(args) > 0 {
|
||||
fmt.Printf("Unexpected arguments: %s\nUse --help to view available options.", args)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// default ImportPath to pwd when not set
|
||||
if len(flags.ImportPaths) == 0 {
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
fmt.Printf("error getting pwd: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
verbosef("using pwd as import path\n")
|
||||
// find non-absolute path for this pwd
|
||||
pkg, err := build.ImportDir(pwd, build.FindOnly)
|
||||
if err != nil {
|
||||
fmt.Printf("error using current directory as import path: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
flags.ImportPaths = append(flags.ImportPaths, pkg.ImportPath)
|
||||
verbosef("using import paths: %s\n", flags.ImportPaths)
|
||||
return
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/GeertJohan/go.incremental"
|
||||
)
|
||||
|
||||
var identifierCount incremental.Uint64
|
||||
|
||||
func nextIdentifier() string {
|
||||
num := identifierCount.Next()
|
||||
return strconv.FormatUint(num, 36) // 0123456789abcdefghijklmnopqrstuvwxyz
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/build"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// parser arguments
|
||||
parseArguments()
|
||||
|
||||
// find package for path
|
||||
var pkgs []*build.Package
|
||||
for _, importPath := range flags.ImportPaths {
|
||||
pkg := pkgForPath(importPath)
|
||||
pkgs = append(pkgs, pkg)
|
||||
}
|
||||
|
||||
// switch on the operation to perform
|
||||
switch flagsParser.Active.Name {
|
||||
case "embed", "embed-go":
|
||||
for _, pkg := range pkgs {
|
||||
operationEmbedGo(pkg)
|
||||
}
|
||||
case "embed-syso":
|
||||
log.Println("WARNING: embedding .syso is experimental..")
|
||||
for _, pkg := range pkgs {
|
||||
operationEmbedSyso(pkg)
|
||||
}
|
||||
case "append":
|
||||
operationAppend(pkgs)
|
||||
case "clean":
|
||||
for _, pkg := range pkgs {
|
||||
operationClean(pkg)
|
||||
}
|
||||
}
|
||||
|
||||
// all done
|
||||
verbosef("\n")
|
||||
verbosef("rice finished successfully\n")
|
||||
}
|
||||
|
||||
// helper function to get *build.Package for given path
|
||||
func pkgForPath(path string) *build.Package {
|
||||
// get pwd for relative imports
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
fmt.Printf("error getting pwd (required for relative imports): %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// read full package information
|
||||
pkg, err := build.Import(path, pwd, 0)
|
||||
if err != nil {
|
||||
fmt.Printf("error reading package: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return pkg
|
||||
}
|
||||
|
||||
func verbosef(format string, stuff ...interface{}) {
|
||||
if flags.Verbose {
|
||||
log.Printf(format, stuff...)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
var tmplEmbeddedBox *template.Template
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
|
||||
// parse embedded box template
|
||||
tmplEmbeddedBox, err = template.New("embeddedBox").Parse(`package {{.Package}}
|
||||
|
||||
import (
|
||||
"github.com/GeertJohan/go.rice/embedded"
|
||||
"time"
|
||||
)
|
||||
|
||||
{{range .Boxes}}
|
||||
func init() {
|
||||
|
||||
// define files
|
||||
{{range .Files}}{{.Identifier}} := &embedded.EmbeddedFile{
|
||||
Filename: {{.FileName | printf "%q"}},
|
||||
FileModTime: time.Unix({{.ModTime}}, 0),
|
||||
Content: string({{.Content | printf "%q"}}),
|
||||
}
|
||||
{{end}}
|
||||
|
||||
// define dirs
|
||||
{{range .Dirs}}{{.Identifier}} := &embedded.EmbeddedDir{
|
||||
Filename: {{.FileName | printf "%q"}},
|
||||
DirModTime: time.Unix({{.ModTime}}, 0),
|
||||
ChildFiles: []*embedded.EmbeddedFile{
|
||||
{{range .ChildFiles}}{{.Identifier}}, // {{.FileName | printf "%q"}}
|
||||
{{end}}
|
||||
},
|
||||
}
|
||||
{{end}}
|
||||
|
||||
// link ChildDirs
|
||||
{{range .Dirs}}{{.Identifier}}.ChildDirs = []*embedded.EmbeddedDir{
|
||||
{{range .ChildDirs}}{{.Identifier}}, // {{.FileName | printf "%q"}}
|
||||
{{end}}
|
||||
}
|
||||
{{end}}
|
||||
|
||||
// register embeddedBox
|
||||
embedded.RegisterEmbeddedBox(` + "`" + `{{.BoxName}}` + "`" + `, &embedded.EmbeddedBox{
|
||||
Name: ` + "`" + `{{.BoxName}}` + "`" + `,
|
||||
Time: time.Unix({{.UnixNow}}, 0),
|
||||
Dirs: map[string]*embedded.EmbeddedDir{
|
||||
{{range .Dirs}}{{.FileName | printf "%q"}}: {{.Identifier}},
|
||||
{{end}}
|
||||
},
|
||||
Files: map[string]*embedded.EmbeddedFile{
|
||||
{{range .Files}}{{.FileName | printf "%q"}}: {{.Identifier}},
|
||||
{{end}}
|
||||
},
|
||||
})
|
||||
}
|
||||
{{end}}`)
|
||||
if err != nil {
|
||||
fmt.Printf("error parsing embedded box template: %s\n", err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
}
|
||||
|
||||
type embedFileDataType struct {
|
||||
Package string
|
||||
Boxes []*boxDataType
|
||||
}
|
||||
|
||||
type boxDataType struct {
|
||||
BoxName string
|
||||
UnixNow int64
|
||||
Files []*fileDataType
|
||||
Dirs map[string]*dirDataType
|
||||
}
|
||||
|
||||
type fileDataType struct {
|
||||
Identifier string
|
||||
FileName string
|
||||
Content []byte
|
||||
ModTime int64
|
||||
}
|
||||
|
||||
type dirDataType struct {
|
||||
Identifier string
|
||||
FileName string
|
||||
Content []byte
|
||||
ModTime int64
|
||||
ChildDirs []*dirDataType
|
||||
ChildFiles []*fileDataType
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
// randomString generates a pseudo-random alpha-numeric string with given length.
|
||||
func randomString(length int) string {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
k := make([]rune, length)
|
||||
for i := 0; i < length; i++ {
|
||||
c := rand.Intn(35)
|
||||
if c < 10 {
|
||||
c += 48 // numbers (0-9) (0+48 == 48 == '0', 9+48 == 57 == '9')
|
||||
} else {
|
||||
c += 87 // lower case alphabets (a-z) (10+87 == 97 == 'a', 35+87 == 122 = 'z')
|
||||
}
|
||||
k[i] = rune(c)
|
||||
}
|
||||
return string(k)
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
|
||||
"github.com/akavel/rsrc/binutil"
|
||||
"github.com/akavel/rsrc/coff"
|
||||
)
|
||||
|
||||
// copied from github.com/akavel/rsrc
|
||||
// LICENSE: MIT
|
||||
// Copyright 2013-2014 The rsrc Authors. (https://github.com/akavel/rsrc/blob/master/AUTHORS)
|
||||
func writeCoff(coff *coff.Coff, fnameout string) error {
|
||||
out, err := os.Create(fnameout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
w := binutil.Writer{W: out}
|
||||
|
||||
// write the resulting file to disk
|
||||
binutil.Walk(coff, func(v reflect.Value, path string) error {
|
||||
if binutil.Plain(v.Kind()) {
|
||||
w.WriteLE(v.Interface())
|
||||
return nil
|
||||
}
|
||||
vv, ok := v.Interface().(binutil.SizedReader)
|
||||
if ok {
|
||||
w.WriteFromSized(vv)
|
||||
return binutil.WALK_SKIP
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if w.Err != nil {
|
||||
return fmt.Errorf("Error writing output file: %s", w.Err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
*.so
|
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2017 Christine Dodrill <me@christine.website>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -0,0 +1,51 @@
|
|||
gopreload
|
||||
=========
|
||||
|
||||
An emulation of the linux libc `LD_PRELOAD` except for use with Go plugins for
|
||||
the addition of instrumentation and debugging utilities.
|
||||
|
||||
## Pluginizer
|
||||
|
||||
`pluginizer` is a bit of glue that makes it easier to turn underscore imports
|
||||
into plugins:
|
||||
|
||||
```console
|
||||
$ go get github.com/Xe/gopreload/cmd/pluginizer
|
||||
$ pluginizer -help
|
||||
Usage of pluginizer:
|
||||
-dest string
|
||||
destination package to generate
|
||||
-pkg string
|
||||
package to underscore import
|
||||
$ pluginizer -pkg github.com/lib/pq -dest github.com/Xe/gopreload/database/postgres
|
||||
To build this plugin:
|
||||
$ go build -buildmode plugin -o /path/to/output.so github.com/Xe/gopreload/database/postgres
|
||||
```
|
||||
|
||||
### Database drivers
|
||||
|
||||
I have included plugin boilerplate autogenned versions of the sqlite, postgres
|
||||
and mysql database drivers.
|
||||
|
||||
## Manhole
|
||||
|
||||
[`manhole`][manhole] is an example of debugging and introspection tooling that has
|
||||
been useful when debugging past issues with long-running server processes.
|
||||
|
||||
## Security Implications
|
||||
|
||||
This package assumes that programs run using it are never started with environment
|
||||
variables that are set by unauthenticated users. Any errors in loading the plugins
|
||||
will be logged using the standard library logger `log` and ignored.
|
||||
|
||||
This has about the same security implications as [`LD_PRELOAD`][ld-preload] does in most
|
||||
Linux distributions, but the risk is minimal compared to the massive benefit for
|
||||
being able to have arbitrary background services all be able to be dug into using
|
||||
the same tooling or being able to have metric submission be completely separated
|
||||
from the backend metric creation. Common logging setup processes can be _always_
|
||||
loaded, making the default logger settings into the correct settings.
|
||||
|
||||
---
|
||||
|
||||
[manhole]: https://github.com/Xe/gopreload/tree/master/manhole
|
||||
[ld-preload]: https://rafalcieslak.wordpress.com/2013/04/02/dynamic-linker-tricks-using-ld_preload-to-cheat-inject-features-and-investigate-programs/
|
|
@ -0,0 +1,56 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
_ "github.com/Xe/gopreload"
|
||||
)
|
||||
|
||||
var (
|
||||
pkgName = flag.String("pkg", "", "package to underscore import")
|
||||
destPkgName = flag.String("dest", "", "destination package to generate")
|
||||
)
|
||||
|
||||
const codeTemplate = `//+build go1.8
|
||||
|
||||
package main
|
||||
|
||||
import _ "$PACKAGE_PATH"`
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
if *pkgName == "" || *destPkgName == "" {
|
||||
log.Fatal("must set -pkg and -dest")
|
||||
}
|
||||
|
||||
srcDir := filepath.Join(os.Getenv("GOPATH"), "src", *destPkgName)
|
||||
|
||||
err := os.MkdirAll(srcDir, os.ModePerm)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fout, err := os.Create(srcDir + "/main.go")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer fout.Close()
|
||||
|
||||
codeBody := os.Expand(codeTemplate, func(s string) string {
|
||||
if s == "PACKAGE_PATH" {
|
||||
return *pkgName
|
||||
}
|
||||
|
||||
return "no idea man"
|
||||
})
|
||||
|
||||
fmt.Fprintln(fout, codeBody)
|
||||
|
||||
fmt.Println("To build this plugin: ")
|
||||
fmt.Println(" $ go build -buildmode plugin -o /path/to/output.so " + *destPkgName)
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
//+build go1.8
|
||||
|
||||
package main
|
||||
|
||||
import _ "github.com/go-sql-driver/mysql"
|
|
@ -0,0 +1,5 @@
|
|||
//+build go1.8
|
||||
|
||||
package main
|
||||
|
||||
import _ "github.com/lib/pq"
|
|
@ -0,0 +1,5 @@
|
|||
//+build go1.8
|
||||
|
||||
package main
|
||||
|
||||
import _ "github.com/mattn/go-sqlite3"
|
|
@ -0,0 +1,56 @@
|
|||
# manhole
|
||||
|
||||
An opinionated HTTP manhole into Go processes.
|
||||
|
||||
## Assumptions This Package Makes
|
||||
|
||||
- Make each server instance have a unique HTTP port that is randomized by default.
|
||||
This makes it very hard to accidentally route this manhole to the outside world.
|
||||
If more assurance is required I personally suggest using [yubikey totp][yktotp],
|
||||
but do research.
|
||||
- Application code does not touch [`http.DefaultServeMux`][default-servemux]'. This is so that
|
||||
administative control rods can be dynamically flipped in the case they are
|
||||
needed.
|
||||
- [pprof][pprof] endpoints added to `http.DefaultServeMux`. This allows easy
|
||||
access to [pprof runtime tracing][pprof-tracing] to debug issues on long-running
|
||||
applications like HTTP services.
|
||||
- Make the manhole slightly inconvenient to put into place in production. This
|
||||
helps make sure that this tool remains a debugging tool and not a part of a
|
||||
long-term production rollout.
|
||||
|
||||
## Usage
|
||||
|
||||
Compile this as a plugin:
|
||||
|
||||
```console
|
||||
$ go get -d github.com/Xe/gopreload/manhole
|
||||
$ go build -buildmode plugin -o manhole.so github.com/Xe/gopreload/manhole
|
||||
```
|
||||
|
||||
Then add [`gopreload`][gopreload] to your application:
|
||||
|
||||
```go
|
||||
// gopreload.go
|
||||
package main
|
||||
|
||||
/*
|
||||
This file is separate to make it very easy to both add into an application, but
|
||||
also very easy to remove.
|
||||
*/
|
||||
|
||||
import _ "github.com/Xe/gopreload"
|
||||
```
|
||||
|
||||
And at runtime add the `manhole.so` file you created earlier to the target system
|
||||
somehow and add the following environment variable to its run configuration:
|
||||
|
||||
```sh
|
||||
GO_PRELOAD=/path/to/manhole.so
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
[pprof]: https://godoc.org/net/http/pprof
|
||||
[default-servemux]: https://godoc.org/net/http#pkg-variables
|
||||
[yktotp]: https://github.com/GeertJohan/yubigo
|
||||
[gopreload]: https://github.com/Xe/gopreload
|
|
@ -0,0 +1,22 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"net/rpc"
|
||||
)
|
||||
|
||||
func init() {
|
||||
l, err := net.Listen("tcp", "127.0.0.2:0")
|
||||
if err != nil {
|
||||
log.Printf("manhole: cannot bind to 127.0.0.2:0: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("manhole: Now listening on http://%s", l.Addr())
|
||||
|
||||
rpc.HandleHTTP()
|
||||
go http.Serve(l, nil)
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
_ "github.com/Xe/gopreload"
|
||||
"github.com/Xe/ln"
|
||||
)
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
spew()
|
||||
|
||||
ln.Log(ln.F{"action": "gc_spew", "who": r.RemoteAddr})
|
||||
|
||||
fmt.Fprintln(w, "done")
|
||||
})
|
||||
|
||||
http.ListenAndServe(":9184", nil)
|
||||
}
|
||||
|
||||
func makeBuffer() []byte {
|
||||
return make([]byte, rand.Intn(5000000)+5000000)
|
||||
}
|
||||
|
||||
func spew() {
|
||||
pool := make([][]byte, 20)
|
||||
|
||||
var m runtime.MemStats
|
||||
makes := 0
|
||||
for _ = range make([]struct{}, 50) {
|
||||
b := makeBuffer()
|
||||
makes += 1
|
||||
i := rand.Intn(len(pool))
|
||||
pool[i] = b
|
||||
|
||||
time.Sleep(time.Millisecond * 250)
|
||||
|
||||
bytes := 0
|
||||
|
||||
for i := 0; i < len(pool); i++ {
|
||||
if pool[i] != nil {
|
||||
bytes += len(pool[i])
|
||||
}
|
||||
}
|
||||
|
||||
runtime.ReadMemStats(&m)
|
||||
fmt.Printf("%d,%d,%d,%d,%d,%d\n", m.HeapSys, bytes, m.HeapAlloc,
|
||||
m.HeapIdle, m.HeapReleased, makes)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/Xe/ln"
|
||||
)
|
||||
|
||||
func init() {
|
||||
ln.Log(ln.F{
|
||||
"action": "started_up",
|
||||
"every": "20_seconds",
|
||||
"what": "gc_metrics",
|
||||
})
|
||||
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(20 * time.Second)
|
||||
gatherMetrics()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func gatherMetrics() {
|
||||
stats := &runtime.MemStats{}
|
||||
runtime.ReadMemStats(stats)
|
||||
|
||||
ln.Log(ln.F{
|
||||
"gc-collections": stats.NumGC,
|
||||
"gc-stw-pause-total": stats.PauseTotalNs,
|
||||
"live-object-count": stats.Mallocs - stats.Frees,
|
||||
"heap-bytes": stats.Alloc,
|
||||
"stack-bytes": stats.StackInuse,
|
||||
})
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.8.1
|
||||
|
||||
before_install:
|
||||
- go get -t -v ./...
|
||||
|
||||
script:
|
||||
- go test -race -coverprofile=coverage.txt -covermode=atomic
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
|
@ -0,0 +1,363 @@
|
|||
Mozilla Public License, version 2.0
|
||||
|
||||
1. Definitions
|
||||
|
||||
1.1. "Contributor"
|
||||
|
||||
means each individual or legal entity that creates, contributes to the
|
||||
creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
|
||||
means the combination of the Contributions of others (if any) used by a
|
||||
Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
|
||||
means Source Code Form to which the initial Contributor has attached the
|
||||
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||
Modifications of such Source Code Form, in each case including portions
|
||||
thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
a. that the initial Contributor has attached the notice described in
|
||||
Exhibit B to the Covered Software; or
|
||||
|
||||
b. that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the terms of
|
||||
a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
|
||||
means a work that combines Covered Software with other material, in a
|
||||
separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
|
||||
means having the right to grant, to the maximum extent possible, whether
|
||||
at the time of the initial grant or subsequently, any and all of the
|
||||
rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
|
||||
means any of the following:
|
||||
|
||||
a. any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered Software; or
|
||||
|
||||
b. any new file in Source Code Form that contains any Covered Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the License,
|
||||
by the making, using, selling, offering for sale, having made, import,
|
||||
or transfer of either its Contributions or its Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
|
||||
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||
General Public License, Version 2.1, the GNU Affero General Public
|
||||
License, Version 3.0, or any later versions of those licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that controls, is
|
||||
controlled by, or is under common control with You. For purposes of this
|
||||
definition, "control" means (a) the power, direct or indirect, to cause
|
||||
the direction or management of such entity, whether by contract or
|
||||
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||
outstanding shares or beneficial ownership of such entity.
|
||||
|
||||
|
||||
2. License Grants and Conditions
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
a. under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||
sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
a. for any code that a Contributor has removed from Covered Software; or
|
||||
|
||||
b. for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
c. under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights to
|
||||
grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||
Section 2.1.
|
||||
|
||||
|
||||
3. Responsibilities
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
a. such Covered Software must also be made available in Source Code Form,
|
||||
as described in Section 3.1, and You must inform recipients of the
|
||||
Executable Form how they can obtain a copy of such Source Code Form by
|
||||
reasonable means in a timely manner, at a charge no more than the cost
|
||||
of distribution to the recipient; and
|
||||
|
||||
b. You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter the
|
||||
recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty, or
|
||||
limitations of liability) contained within the Source Code Form of the
|
||||
Covered Software, except that You may alter any license notices to the
|
||||
extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this License
|
||||
with respect to some or all of the Covered Software due to statute,
|
||||
judicial order, or regulation then You must: (a) comply with the terms of
|
||||
this License to the maximum extent possible; and (b) describe the
|
||||
limitations and the code they affect. Such description must be placed in a
|
||||
text file included with all distributions of the Covered Software under
|
||||
this License. Except to the extent prohibited by statute or regulation,
|
||||
such description must be sufficiently detailed for a recipient of ordinary
|
||||
skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically if You
|
||||
fail to comply with any of its terms. However, if You become compliant,
|
||||
then the rights granted under this License from a particular Contributor
|
||||
are reinstated (a) provisionally, unless and until such Contributor
|
||||
explicitly and finally terminates Your grants, and (b) on an ongoing
|
||||
basis, if such Contributor fails to notify You of the non-compliance by
|
||||
some reasonable means prior to 60 days after You have come back into
|
||||
compliance. Moreover, Your grants from a particular Contributor are
|
||||
reinstated on an ongoing basis if such Contributor notifies You of the
|
||||
non-compliance by some reasonable means, this is the first time You have
|
||||
received notice of non-compliance with this License from such
|
||||
Contributor, and You become compliant prior to 30 days after Your receipt
|
||||
of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||
license agreements (excluding distributors and resellers) which have been
|
||||
validly granted by You or Your distributors under this License prior to
|
||||
termination shall survive termination.
|
||||
|
||||
6. Disclaimer of Warranty
|
||||
|
||||
Covered Software is provided under this License on an "as is" basis,
|
||||
without warranty of any kind, either expressed, implied, or statutory,
|
||||
including, without limitation, warranties that the Covered Software is free
|
||||
of defects, merchantable, fit for a particular purpose or non-infringing.
|
||||
The entire risk as to the quality and performance of the Covered Software
|
||||
is with You. Should any Covered Software prove defective in any respect,
|
||||
You (not any Contributor) assume the cost of any necessary servicing,
|
||||
repair, or correction. This disclaimer of warranty constitutes an essential
|
||||
part of this License. No use of any Covered Software is authorized under
|
||||
this License except under this disclaimer.
|
||||
|
||||
7. Limitation of Liability
|
||||
|
||||
Under no circumstances and under no legal theory, whether tort (including
|
||||
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||
distributes Covered Software as permitted above, be liable to You for any
|
||||
direct, indirect, special, incidental, or consequential damages of any
|
||||
character including, without limitation, damages for lost profits, loss of
|
||||
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses, even if such party shall have been
|
||||
informed of the possibility of such damages. This limitation of liability
|
||||
shall not apply to liability for death or personal injury resulting from
|
||||
such party's negligence to the extent applicable law prohibits such
|
||||
limitation. Some jurisdictions do not allow the exclusion or limitation of
|
||||
incidental or consequential damages, so this exclusion and limitation may
|
||||
not apply to You.
|
||||
|
||||
8. Litigation
|
||||
|
||||
Any litigation relating to this License may be brought only in the courts
|
||||
of a jurisdiction where the defendant maintains its principal place of
|
||||
business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions. Nothing
|
||||
in this Section shall prevent a party's ability to bring cross-claims or
|
||||
counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides that
|
||||
the language of a contract shall be construed against the drafter shall not
|
||||
be used to construe this License against a Contributor.
|
||||
|
||||
|
||||
10. Versions of the License
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses If You choose to distribute Source Code Form that is
|
||||
Incompatible With Secondary Licenses under the terms of this version of
|
||||
the License, the notice described in Exhibit B of this License must be
|
||||
attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular file,
|
||||
then You may include the notice in a location (such as a LICENSE file in a
|
||||
relevant directory) where a recipient would be likely to look for such a
|
||||
notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
|
||||
This Source Code Form is "Incompatible
|
||||
With Secondary Licenses", as defined by
|
||||
the Mozilla Public License, v. 2.0.
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# JSONFeed - Go Package to parse JSON Feed streams
|
||||
|
||||
[![Build Status](https://travis-ci.org/st3fan/jsonfeed.svg?branch=master)](https://travis-ci.org/st3fan/jsonfeed) [![Go Report Card](https://goreportcard.com/badge/github.com/st3fan/jsonfeed)](https://goreportcard.com/report/github.com/st3fan/jsonfeed) [![codecov](https://codecov.io/gh/st3fan/jsonfeed/branch/master/graph/badge.svg)](https://codecov.io/gh/st3fan/jsonfeed)
|
||||
|
||||
|
||||
*Stefan Arentz, May 2017*
|
||||
|
||||
Work in progress. Minimal package to parse JSON Feed streams. Please file feature requests.
|
|
@ -2,10 +2,6 @@
|
|||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/
|
||||
|
||||
/*
|
||||
Package jsonfeed is a set of types and convenience functions for reading and
|
||||
parsing JSON Feed version 1 as defined here: https://jsonfeed.org/version/1
|
||||
*/
|
||||
package jsonfeed
|
||||
|
||||
import (
|
||||
|
@ -14,224 +10,59 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// CurrentVersion will point to the current specification of JSON feed
|
||||
// that this package implements.
|
||||
const CurrentVersion = "https://jsonfeed.org/version/1"
|
||||
|
||||
// Item is a single article or link in a JSON Feed.
|
||||
type Item struct {
|
||||
// ID is unique for that item for that feed over time. If an item
|
||||
// is ever updated, the id should be unchanged. New items should
|
||||
// never use a previously-used id. If an id is presented as a number
|
||||
// or other type, a JSON Feed reader must coerce it to a string.
|
||||
// Ideally, the id is the full URL of the resource described by the
|
||||
// item, since URLs make great unique identifiers.
|
||||
ID string `json:"id"`
|
||||
|
||||
// URL is the URL of the resource described by the item. It’s the
|
||||
// permalink. This may be the same as the id — but should be present
|
||||
// regardless.
|
||||
URL string `json:"url,omitempty"`
|
||||
|
||||
// ExternalURL is the URL of a page elsewhere. This is especially
|
||||
// useful for linkblogs. If url links to where you’re talking about
|
||||
// a thing, then this links to the thing you’re talking about.
|
||||
ExternalURL string `json:"external_url,omitempty"`
|
||||
|
||||
// Title (optional, string) is plain text. Microblog items in
|
||||
// particular may omit titles.
|
||||
Title string `json:"title,omitempty"`
|
||||
|
||||
// ContentHTML and ContentText are each optional strings — but one
|
||||
// or both must be present. This is the HTML or plain text of the
|
||||
// item. Important: the only place HTML is allowed in this format
|
||||
// is in content_html. A Twitter-like service might use content_text,
|
||||
// while a blog might use content_html. Use whichever makes sense
|
||||
// for your resource. (It doesn’t even have to be the same for each
|
||||
// item in a feed.)
|
||||
ContentHTML string `json:"content_html,omitempty"`
|
||||
ContentText string `json:"content_text,omitempty"`
|
||||
|
||||
// Summary is a plain text sentence or two describing the item.
|
||||
// This might be presented in a timeline, for instance, where a
|
||||
// detail view would display all of ContentHTML or ContentText.
|
||||
Summary string `json:"summary,omitempty"`
|
||||
|
||||
// Image is the URL of the main image for the item. This image
|
||||
// may also appear in the content_html — if so, it’s a hint to
|
||||
// the feed reader that this is the main, featured image. Feed
|
||||
// readers may use the image as a preview (probably resized as
|
||||
// a thumbnail and placed in a timeline).
|
||||
Image string `json:"image,omitempty"`
|
||||
|
||||
// BannerImage is the URL of an image to use as a banner. Some
|
||||
// blogging systems (such as Medium) display a different banner
|
||||
// image chosen to go with each post, but that image wouldn’t
|
||||
// otherwise appear in the content_html. A feed reader with a
|
||||
// detail view may choose to show this banner image at the top
|
||||
// of the detail view, possibly with the title overlaid.
|
||||
BannerImage string `json:"banner_image,omitempty"`
|
||||
|
||||
// DatePublished specifies the date of this Item's publication.
|
||||
DatePublished time.Time `json:"date_published,omitempty"`
|
||||
|
||||
// DateModified specifies the date of this Item's last modification
|
||||
// (if applicable)
|
||||
DateModified time.Time `json:"date_modified,omitempty"`
|
||||
|
||||
// Author has the same structure as the top-level author. If not
|
||||
// specified in an item, then the top-level author, if present,
|
||||
// is the author of the item.
|
||||
Author *Author `json:"author,omitempty"`
|
||||
|
||||
// Tags can have any plain text values you want. Tags tend to be
|
||||
// just one word, but they may be anything. Note: they are not
|
||||
// the equivalent of Twitter hashtags. Some blogging systems and
|
||||
// other feed formats call these categories.
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
|
||||
// Attachments (optional, array) lists related resources. Podcasts,
|
||||
// for instance, would include an attachment that’s an audio or
|
||||
// video file.
|
||||
Attachments []Attachment `json:"attachments,omitempty"`
|
||||
ID string `json:"id"`
|
||||
URL string `json:"url"`
|
||||
ExternalURL string `json:"external_url"`
|
||||
Title string `json:"title"`
|
||||
ContentHTML string `json:"content_html"`
|
||||
ContentText string `json:"content_text"`
|
||||
Summary string `json:"summary"`
|
||||
Image string `json:"image"`
|
||||
BannerImage string `json:"banner_image"`
|
||||
DatePublished time.Time `json:"date_published"`
|
||||
DateModified time.Time `json:"date_modified"`
|
||||
Author Author `json:"author"`
|
||||
Tags []string `json:"tags"`
|
||||
}
|
||||
|
||||
// Author specifies the feed author. The author object has several members.
|
||||
// These are all optional, but if you provide an author object, then at
|
||||
// least one is required.
|
||||
type Author struct {
|
||||
// Name is the author's name.
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// URL is the URL of a site owned by the author. It could be a
|
||||
// blog, micro-blog, Twitter account, and so on. Ideally the linked-to
|
||||
// page provides a way to contact the author, but that’s not
|
||||
// required. The URL could be a mailto: link, though we suspect
|
||||
// that will be rare.
|
||||
URL string `json:"url,omitempty"`
|
||||
|
||||
// Avatar is the URL for an image for the author. As with icon,
|
||||
// it should be square and relatively large — such as 512 x 512 —
|
||||
// and should use transparency where appropriate, since it may
|
||||
// be rendered on a non-white background.
|
||||
Avatar string `json:"avatar,omitempty"`
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
Avatar string `json:"avatar"`
|
||||
}
|
||||
|
||||
// Hub describes endpoints that can be used to subscribe to real-time
|
||||
// notifications from the publisher of this feed. Each object has a type
|
||||
// and url, both of which are required.
|
||||
type Hub struct {
|
||||
Type string `json:"type"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// Attachment is a related resource to an Item. If the Feed describes a
|
||||
// podcast, this would refer to the episodes of said podcast.
|
||||
type Attachment struct {
|
||||
// URL specifies the location of the attachment.
|
||||
URL string `json:"url"`
|
||||
|
||||
// MIMEType specifies the type of the attachment, such as "audio/mpeg".
|
||||
MIMEType string `json:"mime_type"`
|
||||
|
||||
// Title is a name for the attachment. Important: if there are multiple
|
||||
// attachments, and two or more have the exact same title (when title
|
||||
// is present), then they are considered as alternate representations
|
||||
// of the same thing. In this way a podcaster, for instance, might
|
||||
// provide an audio recording in different formats.
|
||||
Title string `json:"title,omitifempty"`
|
||||
|
||||
// SizeInBytes specifies the attachment filesize in bytes.
|
||||
SizeInBytes int64 `json:"size_in_bytes,omitempty"`
|
||||
|
||||
// DurationInSeconds specifies how long the attachment takes to listen
|
||||
// to or watch.
|
||||
DurationInSeconds int64 `json:"duration_in_seconds,omitempty"`
|
||||
URL string `json:"url"`
|
||||
MIMEType string `json:"mime_type"`
|
||||
Title string `json:"title"`
|
||||
SizeInBytes int64 `json:"size_in_bytes"`
|
||||
DurationInSeconds int64 `json:"duration_in_seconds"`
|
||||
}
|
||||
|
||||
// Feed is a list that may change over time, and the individual items in the
|
||||
// list may change.
|
||||
//
|
||||
// Think of a blog or microblog, Twitter or Facebook timeline, set of commits
|
||||
// to a repository, or even a server log. These are all lists, and each could
|
||||
// be described by a Feed.
|
||||
//
|
||||
// A JSON Feed starts with some info at the top: it says where the Feed comes
|
||||
// from, and may say who created it and so on.
|
||||
type Feed struct {
|
||||
// Version is the URL of the version of the format the Feed uses.
|
||||
Version string `json:"version"`
|
||||
|
||||
// Title is the name of the Feed, which will often correspond to the
|
||||
// name of the website (blog, for instance), though not necessarily.
|
||||
Title string `json:"title"`
|
||||
|
||||
// HomePageURL is the URL of the resource that the Feed describes.
|
||||
// This resource may or may not actually be a “home” page, but it
|
||||
// should be an HTML page. If a Feed is published on the public web,
|
||||
// this should be considered as required. But it may not make sense
|
||||
// in the case of a file created on a desktop computer, when that
|
||||
// file is not shared or is shared only privately.
|
||||
//
|
||||
// This field is strongly reccomended, but not required.
|
||||
HomePageURL string `json:"home_page_url,omitempty"`
|
||||
|
||||
// FeedURL is the URL of the Feed, and serves as the unique identifier
|
||||
// for the Feed. As with home_page_url, this should be considered
|
||||
// required for Feeds on the public web.
|
||||
//
|
||||
// This field is strongly reccomended, but not required.
|
||||
FeedURL string `json:"feed_url,omitempty"`
|
||||
|
||||
// Description provides more detail, beyond the title, on what the Feed
|
||||
// is about. A Feed reader may display this text.
|
||||
Description string `json:"description,omitempty"`
|
||||
|
||||
// UserComment is a description of the purpose of the Feed. This is for
|
||||
// the use of people looking at the raw JSON, and should be ignored by
|
||||
// Feed readers.
|
||||
UserComment string `json:"user_comment,omitempty"`
|
||||
|
||||
// NextURL is the URL of a Feed that provides the next n items, where
|
||||
// n is determined by the publisher. This allows for pagination, but
|
||||
// with the expectation that reader software is not required to use it
|
||||
// and probably won’t use it very often. next_url must not be the same
|
||||
// as Feed_url, and it must not be the same as a previous next_url
|
||||
// (to avoid infinite loops).
|
||||
NextURL string `json:"next_url,omitempty"`
|
||||
|
||||
// Icon is the URL of an image for the Feed suitable to be used in a
|
||||
// timeline, much the way an avatar might be used. It should be square
|
||||
// and relatively large — such as 512 x 512 — so that it can be scaled-down
|
||||
// and so that it can look good on retina displays. It should use transparency
|
||||
// where appropriate, since it may be rendered on a non-white background.
|
||||
Icon string `json:"icon,omitempty"`
|
||||
|
||||
// Favicon is the URL of an image for the Feed suitable to be used in a
|
||||
// source list. It should be square and relatively small, but not smaller
|
||||
// than 64 x 64 (so that it can look good on retina displays). As with icon,
|
||||
// this image should use transparency where appropriate, since it may be
|
||||
// rendered on a non-white background.
|
||||
Favicon string `json:"favicon,omitempty"`
|
||||
|
||||
// Author specifies the Feed author.
|
||||
Author Author `json:"author,omitempty"`
|
||||
|
||||
// Expired specifies if the Feed will never update again. A Feed for a
|
||||
// temporary event, such as an instance of the Olympics, could expire.
|
||||
// If the value is true, then it’s expired. Any other value, or the
|
||||
// absence of expired, means the Feed may continue to update.
|
||||
Expired bool `json:"expired,omitempty"`
|
||||
|
||||
// Hubs describes endpoints that can be used to subscribe to real-time
|
||||
// notifications from the publisher of this Feed.
|
||||
Hubs []Hub `json:"hubs,omitempty"`
|
||||
|
||||
// Items is the list of Items in this Feed.
|
||||
Items []Item `json:"items"`
|
||||
Version string `json:"version"`
|
||||
Title string `json:"title"`
|
||||
HomePageURL string `json:"home_page_url"`
|
||||
FeedURL string `json:"feed_url"`
|
||||
Description string `json:"description"`
|
||||
UserComment string `json:"user_comment"`
|
||||
NextURL string `json:"next_url"`
|
||||
Icon string `json:"icon"`
|
||||
Favicon string `json:"favicon"`
|
||||
Author Author `json:"author"`
|
||||
Expired bool `json:"expired"`
|
||||
Hubs []Hub `json:"hubs"`
|
||||
Items []Item `json:"items"`
|
||||
}
|
||||
|
||||
// Parse reads a JSON feed object out of a reader.
|
||||
func Parse(r io.Reader) (Feed, error) {
|
||||
var feed Feed
|
||||
decoder := json.NewDecoder(r)
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/
|
||||
|
||||
package jsonfeed_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/st3fan/jsonfeed"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_ParseSimple(t *testing.T) {
|
||||
r, err := os.Open("testdata/feed.json")
|
||||
assert.NoError(t, err, "Could not open testdata/feed.json")
|
||||
|
||||
feed, err := jsonfeed.Parse(r)
|
||||
assert.NoError(t, err, "Could not parse testdata/feed.json")
|
||||
|
||||
assert.Equal(t, "https://jsonfeed.org/version/1", feed.Version)
|
||||
assert.Equal(t, "JSON Feed", feed.Title)
|
||||
assert.Equal(t, "JSON Feed is a ...", feed.Description)
|
||||
assert.Equal(t, "https://jsonfeed.org/", feed.HomePageURL)
|
||||
assert.Equal(t, "https://jsonfeed.org/feed.json", feed.FeedURL)
|
||||
assert.Equal(t, "This feed allows ...", feed.UserComment)
|
||||
assert.Equal(t, "https://jsonfeed.org/graphics/icon.png", feed.Favicon)
|
||||
assert.Equal(t, "Brent Simmons and Manton Reece", feed.Author.Name)
|
||||
|
||||
assert.Equal(t, 1, len(feed.Items))
|
||||
|
||||
assert.Equal(t, "https://jsonfeed.org/2017/05/17/announcing_json_feed", feed.Items[0].ID)
|
||||
assert.Equal(t, "https://jsonfeed.org/2017/05/17/announcing_json_feed", feed.Items[0].URL)
|
||||
assert.Equal(t, "Announcing JSON Feed", feed.Items[0].Title)
|
||||
assert.Equal(t, "<p>We ...", feed.Items[0].ContentHTML)
|
||||
|
||||
datePublished, err := time.Parse("2006-01-02T15:04:05-07:00", "2017-05-17T08:02:12-07:00")
|
||||
assert.NoError(t, err, "Could not parse timestamp")
|
||||
|
||||
assert.Equal(t, datePublished, feed.Items[0].DatePublished)
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"version": "https://jsonfeed.org/version/1",
|
||||
"title": "JSON Feed",
|
||||
"description": "JSON Feed is a ...",
|
||||
"home_page_url": "https://jsonfeed.org/",
|
||||
"feed_url": "https://jsonfeed.org/feed.json",
|
||||
"user_comment": "This feed allows ...",
|
||||
"favicon": "https://jsonfeed.org/graphics/icon.png",
|
||||
"author": {
|
||||
"name": "Brent Simmons and Manton Reece"
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"id": "https://jsonfeed.org/2017/05/17/announcing_json_feed",
|
||||
"url": "https://jsonfeed.org/2017/05/17/announcing_json_feed",
|
||||
"title": "Announcing JSON Feed",
|
||||
"content_html": "<p>We ...",
|
||||
"date_published": "2017-05-17T08:02:12-07:00"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
Copyright (c) 2015, Andrew Gwozdziewycz
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,29 @@
|
|||
# ln: The Natural Logger for Go
|
||||
|
||||
`ln` provides a simple interface to logging, and metrics, and
|
||||
obviates the need to utilize purpose built metrics packages, like
|
||||
`go-metrics` for simple use cases.
|
||||
|
||||
The design of `ln` centers around the idea of key-value pairs, which
|
||||
can be interpreted on the fly, but "Filters" to do things such as
|
||||
aggregated metrics, and report said metrics to, say Librato, or
|
||||
statsd.
|
||||
|
||||
"Filters" are like WSGI, or Rack Middleware. They are run "top down"
|
||||
and can abort an emitted log's output at any time, or continue to let
|
||||
it through the chain. However, the interface is slightly different
|
||||
than that. Rather than encapsulating the chain with partial function
|
||||
application, we utilize a simpler method, namely, each plugin defines
|
||||
an `Apply` function, which takes as an argument the log event, and
|
||||
performs the work of the plugin, only if the Plugin "Applies" to this
|
||||
log event.
|
||||
|
||||
If `Apply` returns `false`, the iteration through the rest of the
|
||||
filters is aborted, and the log is dropped from further processing.
|
||||
|
||||
## Current Status: Initial Development / Concept
|
||||
|
||||
## Copyright
|
||||
|
||||
(c) 2015, Andrew Gwozdziewycz, BSD Licensed. See LICENSE for more
|
||||
info.
|
|
@ -0,0 +1,11 @@
|
|||
package ln
|
||||
|
||||
// Action is a convenience helper for logging the "action" being performed as
|
||||
// part of a log line.
|
||||
//
|
||||
// It is a convenience wrapper for the following:
|
||||
//
|
||||
// ln.Log(ctx, fer, f, ln.Action("writing frozberry sales reports to database"))
|
||||
func Action(act string) Fer {
|
||||
return F{"action": act}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package ln
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type ctxKey int
|
||||
|
||||
const (
|
||||
fKey = iota
|
||||
)
|
||||
|
||||
// WithF stores or appends a given F instance into a context.
|
||||
func WithF(ctx context.Context, f F) context.Context {
|
||||
pf, ok := FFromContext(ctx)
|
||||
if !ok {
|
||||
return context.WithValue(ctx, fKey, f)
|
||||
}
|
||||
|
||||
pf.Extend(f)
|
||||
|
||||
return context.WithValue(ctx, fKey, pf)
|
||||
}
|
||||
|
||||
// FFromContext fetches the `F` out of the context if it exists.
|
||||
func FFromContext(ctx context.Context) (F, bool) {
|
||||
fvp := ctx.Value(fKey)
|
||||
if fvp == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
f, ok := fvp.(F)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return f, true
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
Package ln is the Natural Logger for Go
|
||||
|
||||
`ln` provides a simple interface to logging, and metrics, and
|
||||
obviates the need to utilize purpose built metrics packages, like
|
||||
`go-metrics` for simple use cases.
|
||||
|
||||
The design of `ln` centers around the idea of key-value pairs, which
|
||||
can be interpreted on the fly, but "Filters" to do things such as
|
||||
aggregated metrics, and report said metrics to, say Librato, or
|
||||
statsd.
|
||||
|
||||
"Filters" are like WSGI, or Rack Middleware. They are run "top down"
|
||||
and can abort an emitted log's output at any time, or continue to let
|
||||
it through the chain. However, the interface is slightly different
|
||||
than that. Rather than encapsulating the chain with partial function
|
||||
application, we utilize a simpler method, namely, each plugin defines
|
||||
an `Apply` function, which takes as an argument the log event, and
|
||||
performs the work of the plugin, only if the Plugin "Applies" to this
|
||||
log event.
|
||||
|
||||
If `Apply` returns `false`, the iteration through the rest of the
|
||||
filters is aborted, and the log is dropped from further processing.
|
||||
*/
|
||||
package ln
|
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
Package ex is a set of extensions and middleware for ln.
|
||||
|
||||
This package will (inevitably) have a lot of third-party dependencies and
|
||||
as such might be broken apart into other packages in the future.
|
||||
*/
|
||||
package ex
|
|
@ -0,0 +1,68 @@
|
|||
package ex
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"github.com/Xe/ln"
|
||||
"golang.org/x/net/trace"
|
||||
)
|
||||
|
||||
type goEventLogger struct {
|
||||
ev trace.EventLog
|
||||
}
|
||||
|
||||
// NewGoEventLogger will log ln information to a given trace.EventLog instance.
|
||||
func NewGoEventLogger(ev trace.EventLog) ln.Filter {
|
||||
return &goEventLogger{ev: ev}
|
||||
}
|
||||
|
||||
func (gel *goEventLogger) Apply(ctx context.Context, e ln.Event) bool {
|
||||
data, err := ln.DefaultFormatter.Format(ctx, e)
|
||||
if err != nil {
|
||||
log.Printf("wtf: error in log formatting: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if everr := e.Data["err"]; everr != nil {
|
||||
gel.ev.Errorf("%s", string(data))
|
||||
return true
|
||||
}
|
||||
|
||||
gel.ev.Printf("%s", string(data))
|
||||
return true
|
||||
}
|
||||
|
||||
func (gel *goEventLogger) Close() { gel.ev.Finish() }
|
||||
func (gel *goEventLogger) Run() {}
|
||||
|
||||
type sst string
|
||||
|
||||
func (s sst) String() string { return string(s) }
|
||||
|
||||
func goTraceLogger(ctx context.Context, e ln.Event) bool {
|
||||
sp, ok := trace.FromContext(ctx)
|
||||
if !ok {
|
||||
return true // no trace in context
|
||||
}
|
||||
|
||||
data, err := ln.DefaultFormatter.Format(ctx, e)
|
||||
if err != nil {
|
||||
log.Printf("wtf: error in log formatting: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if everr := e.Data["err"]; everr != nil {
|
||||
sp.SetError()
|
||||
}
|
||||
|
||||
sp.LazyLog(sst(string(data)), false)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// NewGoTraceLogger will log ln information to a golang.org/x/net/trace.Trace
|
||||
// if it is present in the context of ln calls.
|
||||
func NewGoTraceLogger() ln.Filter {
|
||||
return ln.FilterFunc(goTraceLogger)
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package ex
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/Xe/ln"
|
||||
)
|
||||
|
||||
func HTTPLog(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
host, _, _ := net.SplitHostPort(r.RemoteAddr)
|
||||
f := ln.F{
|
||||
"remote_ip": host,
|
||||
"x_forwarded_for": r.Header.Get("X-Forwarded-For"),
|
||||
"path": r.URL.Path,
|
||||
}
|
||||
ctx := ln.WithF(r.Context(), f)
|
||||
st := time.Now()
|
||||
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
|
||||
af := time.Now()
|
||||
f["request_duration"] = af.Sub(st)
|
||||
|
||||
ws, ok := w.(interface {
|
||||
Status() int
|
||||
})
|
||||
if ok {
|
||||
f["status"] = ws.Status()
|
||||
}
|
||||
|
||||
ln.Log(r.Context(), f)
|
||||
})
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package ex
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/Xe/ln"
|
||||
)
|
||||
|
||||
// This file deals with formatting of [l2met] style metrics.
|
||||
// [l2met]: https://r.32k.io/l2met-introduction
|
||||
|
||||
// Counter formats a value as a metrics counter.
|
||||
func Counter(name string, value int) ln.Fer {
|
||||
return ln.F{"count#" + name: value}
|
||||
}
|
||||
|
||||
// Gauge formats a value as a metrics gauge.
|
||||
func Gauge(name string, value int) ln.Fer {
|
||||
return ln.F{"gauge#" + name: value}
|
||||
}
|
||||
|
||||
// Measure formats a value as a metrics measure.
|
||||
func Measure(name string, ts time.Time) ln.Fer {
|
||||
return ln.F{"measure#" + name: time.Since(ts)}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/Xe/ln"
|
||||
"github.com/Xe/ln/ex"
|
||||
"github.com/facebookgo/flagenv"
|
||||
"golang.org/x/net/trace"
|
||||
)
|
||||
|
||||
var (
|
||||
port = flag.String("port", "2145", "http port to listen on")
|
||||
tracingFamily = flag.String("trace-family", "ln example", "tracing family to use for x/net/trace")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flagenv.Parse()
|
||||
flag.Parse()
|
||||
|
||||
ln.DefaultLogger.Filters = append(ln.DefaultLogger.Filters, ex.NewGoTraceLogger())
|
||||
|
||||
http.HandleFunc("/", handleIndex)
|
||||
http.ListenAndServe(":"+*port, middlewareSpan(ex.HTTPLog(http.DefaultServeMux)))
|
||||
}
|
||||
|
||||
func middlewareSpan(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
sp := trace.New(*tracingFamily, "HTTP request")
|
||||
defer sp.Finish()
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
ctx = trace.NewContext(ctx, sp)
|
||||
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
|
||||
func handleIndex(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
ln.Log(ctx, ln.Action("index"), ln.F{"there_is": "no_danger"})
|
||||
|
||||
http.Error(w, "There is no danger citizen", http.StatusOK)
|
||||
}
|
|
@ -1,23 +1,24 @@
|
|||
package ln
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Filter interface for defining chain filters
|
||||
type Filter interface {
|
||||
Apply(Event) bool
|
||||
Apply(ctx context.Context, e Event) bool
|
||||
Run()
|
||||
Close()
|
||||
}
|
||||
|
||||
// FilterFunc allows simple functions to implement the Filter interface
|
||||
type FilterFunc func(e Event) bool
|
||||
type FilterFunc func(ctx context.Context, e Event) bool
|
||||
|
||||
// Apply implements the Filter interface
|
||||
func (ff FilterFunc) Apply(e Event) bool {
|
||||
return ff(e)
|
||||
func (ff FilterFunc) Apply(ctx context.Context, e Event) bool {
|
||||
return ff(ctx, e)
|
||||
}
|
||||
|
||||
// Run implements the Filter interface
|
||||
|
@ -45,8 +46,8 @@ func NewWriterFilter(out io.Writer, format Formatter) *WriterFilter {
|
|||
}
|
||||
|
||||
// Apply implements the Filter interface
|
||||
func (w *WriterFilter) Apply(e Event) bool {
|
||||
output, err := w.Formatter.Format(e)
|
||||
func (w *WriterFilter) Apply(ctx context.Context, e Event) bool {
|
||||
output, err := w.Formatter.Format(ctx, e)
|
||||
if err == nil {
|
||||
w.Lock()
|
||||
w.Out.Write(output)
|
||||
|
@ -63,4 +64,4 @@ func (w *WriterFilter) Run() {}
|
|||
func (w *WriterFilter) Close() {}
|
||||
|
||||
// NilFilter is safe to return as a Filter, but does nothing
|
||||
var NilFilter = FilterFunc(func(e Event) bool { return true })
|
||||
var NilFilter = FilterFunc(func(_ context.Context, e Event) bool { return true })
|
||||
|
|
|
@ -2,6 +2,7 @@ package ln
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
@ -13,7 +14,7 @@ var (
|
|||
|
||||
// Formatter defines the formatting of events
|
||||
type Formatter interface {
|
||||
Format(Event) ([]byte, error)
|
||||
Format(ctx context.Context, e Event) ([]byte, error)
|
||||
}
|
||||
|
||||
// DefaultFormatter is the default way in which to format events
|
||||
|
@ -36,7 +37,7 @@ func NewTextFormatter() Formatter {
|
|||
}
|
||||
|
||||
// Format implements the Formatter interface
|
||||
func (t *TextFormatter) Format(e Event) ([]byte, error) {
|
||||
func (t *TextFormatter) Format(_ context.Context, e Event) ([]byte, error) {
|
||||
var writer bytes.Buffer
|
||||
|
||||
writer.WriteString("time=\"")
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package ln
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"context"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
|
@ -30,14 +30,28 @@ func init() {
|
|||
DefaultLogger = &Logger{
|
||||
Filters: defaultFilters,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// F is a key-value mapping for structured data.
|
||||
type F map[string]interface{}
|
||||
|
||||
// Extend concatentates one F with one or many Fer instances.
|
||||
func (f F) Extend(other ...Fer) {
|
||||
for _, ff := range other {
|
||||
for k, v := range ff.F() {
|
||||
f[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// F makes F an Fer
|
||||
func (f F) F() F {
|
||||
return f
|
||||
}
|
||||
|
||||
// Fer allows any type to add fields to the structured logging key->value pairs.
|
||||
type Fer interface {
|
||||
F() map[string]interface{}
|
||||
F() F
|
||||
}
|
||||
|
||||
// Event represents an event
|
||||
|
@ -48,8 +62,7 @@ type Event struct {
|
|||
}
|
||||
|
||||
// Log is the generic logging method.
|
||||
func (l *Logger) Log(xs ...interface{}) {
|
||||
var bits []interface{}
|
||||
func (l *Logger) Log(ctx context.Context, xs ...Fer) {
|
||||
event := Event{Time: time.Now()}
|
||||
|
||||
addF := func(bf F) {
|
||||
|
@ -62,18 +75,14 @@ func (l *Logger) Log(xs ...interface{}) {
|
|||
}
|
||||
}
|
||||
|
||||
// Assemble the event
|
||||
for _, b := range xs {
|
||||
if bf, ok := b.(F); ok {
|
||||
addF(bf)
|
||||
} else if fer, ok := b.(Fer); ok {
|
||||
addF(F(fer.F()))
|
||||
} else {
|
||||
bits = append(bits, b)
|
||||
}
|
||||
for _, f := range xs {
|
||||
addF(f.F())
|
||||
}
|
||||
|
||||
event.Message = fmt.Sprint(bits...)
|
||||
ctxf, ok := FFromContext(ctx)
|
||||
if ok {
|
||||
addF(ctxf)
|
||||
}
|
||||
|
||||
if os.Getenv("LN_DEBUG_ALL_EVENTS") == "1" {
|
||||
frame := callersFrame()
|
||||
|
@ -85,19 +94,19 @@ func (l *Logger) Log(xs ...interface{}) {
|
|||
event.Data["_filename"] = frame.filename
|
||||
}
|
||||
|
||||
l.filter(event)
|
||||
l.filter(ctx, event)
|
||||
}
|
||||
|
||||
func (l *Logger) filter(e Event) {
|
||||
func (l *Logger) filter(ctx context.Context, e Event) {
|
||||
for _, f := range l.Filters {
|
||||
if !f.Apply(e) {
|
||||
if !f.Apply(ctx, e) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Error logs an error and information about the context of said error.
|
||||
func (l *Logger) Error(err error, xs ...interface{}) {
|
||||
func (l *Logger) Error(ctx context.Context, err error, xs ...Fer) {
|
||||
data := F{}
|
||||
frame := callersFrame()
|
||||
|
||||
|
@ -113,12 +122,37 @@ func (l *Logger) Error(err error, xs ...interface{}) {
|
|||
|
||||
xs = append(xs, data)
|
||||
|
||||
l.Log(xs...)
|
||||
l.Log(ctx, xs...)
|
||||
}
|
||||
|
||||
// Fatal logs this set of values, then exits with status code 1.
|
||||
func (l *Logger) Fatal(xs ...interface{}) {
|
||||
l.Log(xs...)
|
||||
func (l *Logger) Fatal(ctx context.Context, xs ...Fer) {
|
||||
xs = append(xs, F{"fatal": true})
|
||||
|
||||
l.Log(ctx, xs...)
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// FatalErr combines Fatal and Error.
|
||||
func (l *Logger) FatalErr(ctx context.Context, err error, xs ...Fer) {
|
||||
xs = append(xs, F{"fatal": true})
|
||||
|
||||
data := F{}
|
||||
frame := callersFrame()
|
||||
|
||||
data["_lineno"] = frame.lineno
|
||||
data["_function"] = frame.function
|
||||
data["_filename"] = frame.filename
|
||||
data["err"] = err
|
||||
|
||||
cause := errors.Cause(err)
|
||||
if cause != nil {
|
||||
data["cause"] = cause.Error()
|
||||
}
|
||||
|
||||
xs = append(xs, data)
|
||||
l.Log(ctx, xs...)
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
|
@ -126,16 +160,21 @@ func (l *Logger) Fatal(xs ...interface{}) {
|
|||
// Default Implementation
|
||||
|
||||
// Log is the generic logging method.
|
||||
func Log(xs ...interface{}) {
|
||||
DefaultLogger.Log(xs...)
|
||||
func Log(ctx context.Context, xs ...Fer) {
|
||||
DefaultLogger.Log(ctx, xs...)
|
||||
}
|
||||
|
||||
// Error logs an error and information about the context of said error.
|
||||
func Error(err error, xs ...interface{}) {
|
||||
DefaultLogger.Error(err, xs...)
|
||||
func Error(ctx context.Context, err error, xs ...Fer) {
|
||||
DefaultLogger.Error(ctx, err, xs...)
|
||||
}
|
||||
|
||||
// Fatal logs this set of values, then exits with status code 1.
|
||||
func Fatal(xs ...interface{}) {
|
||||
DefaultLogger.Fatal(xs...)
|
||||
func Fatal(ctx context.Context, xs ...Fer) {
|
||||
DefaultLogger.Fatal(ctx, xs...)
|
||||
}
|
||||
|
||||
// FatalErr combines Fatal and Error.
|
||||
func FatalErr(ctx context.Context, err error, xs ...Fer) {
|
||||
DefaultLogger.FatalErr(ctx, err, xs...)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
package ln
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var ctx context.Context
|
||||
|
||||
func setup(t *testing.T) (*bytes.Buffer, func()) {
|
||||
ctx = context.Background()
|
||||
|
||||
out := bytes.Buffer{}
|
||||
oldFilters := DefaultLogger.Filters
|
||||
DefaultLogger.Filters = []Filter{NewWriterFilter(&out, nil)}
|
||||
return &out, func() {
|
||||
DefaultLogger.Filters = oldFilters
|
||||
}
|
||||
}
|
||||
|
||||
func TestSimpleError(t *testing.T) {
|
||||
out, teardown := setup(t)
|
||||
defer teardown()
|
||||
|
||||
Log(ctx, F{"err": fmt.Errorf("This is an Error!!!")}, F{"msg": "fooey", "bar": "foo"})
|
||||
data := []string{
|
||||
`err="This is an Error!!!"`,
|
||||
`fooey`,
|
||||
`bar=foo`,
|
||||
}
|
||||
|
||||
for _, line := range data {
|
||||
if !bytes.Contains(out.Bytes(), []byte(line)) {
|
||||
t.Fatalf("Bytes: %s not in %s", line, out.Bytes())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimeConversion(t *testing.T) {
|
||||
out, teardown := setup(t)
|
||||
defer teardown()
|
||||
|
||||
var zeroTime time.Time
|
||||
|
||||
Log(ctx, F{"zero": zeroTime})
|
||||
data := []string{
|
||||
`zero=0001-01-01T00:00:00Z`,
|
||||
}
|
||||
|
||||
for _, line := range data {
|
||||
if !bytes.Contains(out.Bytes(), []byte(line)) {
|
||||
t.Fatalf("Bytes: %s not in %s", line, out.Bytes())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDebug(t *testing.T) {
|
||||
out, teardown := setup(t)
|
||||
defer teardown()
|
||||
|
||||
// set priority to Debug
|
||||
Error(ctx, fmt.Errorf("This is an Error!!!"), F{})
|
||||
|
||||
data := []string{
|
||||
`err="This is an Error!!!"`,
|
||||
`_lineno=`,
|
||||
`_function=ln.TestDebug`,
|
||||
`_filename=github.com/Xe/ln/logger_test.go`,
|
||||
`cause="This is an Error!!!"`,
|
||||
}
|
||||
|
||||
for _, line := range data {
|
||||
if !bytes.Contains(out.Bytes(), []byte(line)) {
|
||||
t.Fatalf("Bytes: %s not in %s", line, out.Bytes())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFer(t *testing.T) {
|
||||
out, teardown := setup(t)
|
||||
defer teardown()
|
||||
|
||||
underTest := foobar{Foo: 1, Bar: "quux"}
|
||||
|
||||
Log(ctx, underTest)
|
||||
data := []string{
|
||||
`foo=1`,
|
||||
`bar=quux`,
|
||||
}
|
||||
|
||||
for _, line := range data {
|
||||
if !bytes.Contains(out.Bytes(), []byte(line)) {
|
||||
t.Fatalf("Bytes: %s not in %s", line, out.Bytes())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type foobar struct {
|
||||
Foo int
|
||||
Bar string
|
||||
}
|
||||
|
||||
func (f foobar) F() F {
|
||||
return F{
|
||||
"foo": f.Foo,
|
||||
"bar": f.Bar,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright © 2012-2015 Carlos Castillo
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the “Software”), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,5 @@
|
|||
go.zipexe
|
||||
=========
|
||||
|
||||
This module was taken as-is from https://github.com/cookieo9/resources-go.
|
||||
Documentation: https://godoc.org/github.com/daaku/go.zipexe
|
|
@ -0,0 +1,15 @@
|
|||
# Golang CircleCI 2.0 configuration file
|
||||
#
|
||||
# Check https://circleci.com/docs/2.0/language-go/ for more details
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
- image: circleci/golang:1.9
|
||||
|
||||
working_directory: /go/src/github.com/google/gops
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
# specify any bash command here prefixed with `run: `
|
||||
- run: go test -v ./...
|
|
@ -0,0 +1,33 @@
|
|||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/kardianos/osext"
|
||||
packages = ["."]
|
||||
revision = "ae77be60afb1dcacde03767a8c37337fad28ac14"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/keybase/go-ps"
|
||||
packages = [".","darwincgo"]
|
||||
revision = "668c8856d9992f97248b3177d45743d2cc1068db"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/arch"
|
||||
packages = ["arm/armasm","ppc64/ppc64asm","x86/x86asm"]
|
||||
revision = "f185940480d2c897e1ca8cf2f1be122e1258341b"
|
||||
|
||||
[[projects]]
|
||||
name = "rsc.io/goversion"
|
||||
packages = ["version"]
|
||||
revision = "84cd9d26d7efce780a7aaafd0944961371da741f"
|
||||
version = "v1.0.0"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "4b494828d559e89fc1fe70b9bfdd0f5c8ad62ee16258a3b2bcf1028c140faa32"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
|
@ -0,0 +1,34 @@
|
|||
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/kardianos/osext"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/keybase/go-ps"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/arch"
|
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2016 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,150 @@
|
|||
# gops
|
||||
|
||||
[![Build status](https://circleci.com/gh/google/gops/tree/master.svg?style=shield&circle-token=2637dc1e57d5407ae250480a86a2e553a7d20482)](https://circleci.com/gh/google/gops)
|
||||
[![GoDoc](https://godoc.org/github.com/google/gops/agent?status.svg)](https://godoc.org/github.com/google/gops/agent)
|
||||
|
||||
gops is a command to list and diagnose Go processes currently running on your system.
|
||||
|
||||
```
|
||||
$ gops
|
||||
983 uplink-soecks (/usr/local/bin/uplink-soecks)
|
||||
52697 gops (/Users/jbd/bin/gops)
|
||||
4132* foops (/Users/jbd/bin/foops)
|
||||
51130 gocode (/Users/jbd/bin/gocode)
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
$ go get -u github.com/google/gops
|
||||
```
|
||||
|
||||
## Diagnostics
|
||||
|
||||
For processes that starts the diagnostics agent, gops can report
|
||||
additional information such as the current stack trace, Go version, memory
|
||||
stats, etc.
|
||||
|
||||
In order to start the diagnostics agent, see the [hello example](https://github.com/google/gops/blob/master/examples/hello/main.go).
|
||||
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/google/gops/agent"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := agent.Listen(nil); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
time.Sleep(time.Hour)
|
||||
}
|
||||
```
|
||||
|
||||
### Manual
|
||||
|
||||
It is possible to use gops tool both in local and remote mode.
|
||||
|
||||
Local mode requires that you start the target binary as the same user that runs gops binary.
|
||||
To use gops in a remote mode you need to know target's agent address.
|
||||
|
||||
In Local mode use process's PID as a target; in Remote mode target is a `host:port` combination.
|
||||
|
||||
#### 0. Listing all processes running locally
|
||||
|
||||
To print all go processes, run `gops` without arguments:
|
||||
|
||||
```sh
|
||||
$ gops
|
||||
983 uplink-soecks (/usr/local/bin/uplink-soecks)
|
||||
52697 gops (/Users/jbd/bin/gops)
|
||||
4132* foops (/Users/jbd/bin/foops)
|
||||
51130 gocode (/Users/jbd/bin/gocode)
|
||||
```
|
||||
|
||||
Note that processes running the agent are marked with `*` next to the PID (e.g. `4132*`).
|
||||
|
||||
#### $ gops stack (\<pid\>|\<addr\>)
|
||||
|
||||
In order to print the current stack trace from a target program, run the following command:
|
||||
|
||||
|
||||
```sh
|
||||
$ gops stack (<pid>|<addr>)
|
||||
gops stack 85709
|
||||
goroutine 8 [running]:
|
||||
runtime/pprof.writeGoroutineStacks(0x13c7bc0, 0xc42000e008, 0xc420ec8520, 0xc420ec8520)
|
||||
/Users/jbd/go/src/runtime/pprof/pprof.go:603 +0x79
|
||||
runtime/pprof.writeGoroutine(0x13c7bc0, 0xc42000e008, 0x2, 0xc428f1c048, 0xc420ec8608)
|
||||
/Users/jbd/go/src/runtime/pprof/pprof.go:592 +0x44
|
||||
runtime/pprof.(*Profile).WriteTo(0x13eeda0, 0x13c7bc0, 0xc42000e008, 0x2, 0xc42000e008, 0x0)
|
||||
/Users/jbd/go/src/runtime/pprof/pprof.go:302 +0x3b5
|
||||
github.com/google/gops/agent.handle(0x13cd560, 0xc42000e008, 0xc420186000, 0x1, 0x1, 0x0, 0x0)
|
||||
/Users/jbd/src/github.com/google/gops/agent/agent.go:150 +0x1b3
|
||||
github.com/google/gops/agent.listen()
|
||||
/Users/jbd/src/github.com/google/gops/agent/agent.go:113 +0x2b2
|
||||
created by github.com/google/gops/agent.Listen
|
||||
/Users/jbd/src/github.com/google/gops/agent/agent.go:94 +0x480
|
||||
# ...
|
||||
```
|
||||
|
||||
#### $ gops memstats (\<pid\>|\<addr\>)
|
||||
|
||||
To print the current memory stats, run the following command:
|
||||
|
||||
```sh
|
||||
$ gops memstats (<pid>|<addr>)
|
||||
```
|
||||
|
||||
|
||||
#### $ gops gc (\<pid\>|\<addr\>)
|
||||
|
||||
If you want to force run garbage collection on the target program, run `gc`.
|
||||
It will block until the GC is completed.
|
||||
|
||||
|
||||
#### $ gops version (\<pid\>|\<addr\>)
|
||||
|
||||
gops reports the Go version the target program is built with, if you run the following:
|
||||
|
||||
```sh
|
||||
$ gops version (<pid>|<addr>)
|
||||
devel +6a3c6c0 Sat Jan 14 05:57:07 2017 +0000
|
||||
```
|
||||
|
||||
#### $ gops stats (\<pid\>|\<addr\>)
|
||||
|
||||
To print the runtime statistics such as number of goroutines and `GOMAXPROCS`.
|
||||
|
||||
#### Profiling
|
||||
|
||||
|
||||
##### Pprof
|
||||
|
||||
gops supports CPU and heap pprof profiles. After reading either heap or CPU profile,
|
||||
it shells out to the `go tool pprof` and let you interatively examine the profiles.
|
||||
|
||||
To enter the CPU profile, run:
|
||||
|
||||
```sh
|
||||
$ gops pprof-cpu (<pid>|<addr>)
|
||||
```
|
||||
|
||||
To enter the heap profile, run:
|
||||
|
||||
```sh
|
||||
$ gops pprof-heap (<pid>|<addr>)
|
||||
```
|
||||
|
||||
##### Execution trace
|
||||
|
||||
gops allows you to start the runtime tracer for 5 seconds and examine the results.
|
||||
|
||||
```sh
|
||||
$ gops trace (<pid>|<addr>)
|
||||
```
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package agent
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestListen(t *testing.T) {
|
||||
err := Listen(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
Close()
|
||||
}
|
||||
|
||||
func TestAgentClose(t *testing.T) {
|
||||
err := Listen(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
Close()
|
||||
_, err = os.Stat(portfile)
|
||||
if !os.IsNotExist(err) {
|
||||
t.Fatalf("portfile = %q doesn't exist; err = %v", portfile, err)
|
||||
}
|
||||
if portfile != "" {
|
||||
t.Fatalf("got = %q; want empty portfile", portfile)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentListenMultipleClose(t *testing.T) {
|
||||
err := Listen(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
Close()
|
||||
Close()
|
||||
Close()
|
||||
Close()
|
||||
}
|
||||
|
||||
func TestFormatBytes(t *testing.T) {
|
||||
tests := []struct {
|
||||
val uint64
|
||||
want string
|
||||
}{
|
||||
{1023, "1023 bytes"},
|
||||
{1024, "1.00KB (1024 bytes)"},
|
||||
{1024*1024 - 100, "1023.90KB (1048476 bytes)"},
|
||||
{1024 * 1024, "1.00MB (1048576 bytes)"},
|
||||
{1024 * 1025, "1.00MB (1049600 bytes)"},
|
||||
{1024 * 1024 * 1024, "1.00GB (1073741824 bytes)"},
|
||||
{1024*1024*1024 + 430*1024*1024, "1.42GB (1524629504 bytes)"},
|
||||
{1024 * 1024 * 1024 * 1024 * 1024, "1.00PB (1125899906842624 bytes)"},
|
||||
{1024 * 1024 * 1024 * 1024 * 1024 * 1024, "1024.00PB (1152921504606846976 bytes)"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
result := formatBytes(tt.val)
|
||||
if result != tt.want {
|
||||
t.Errorf("formatBytes(%v) = %q; want %q", tt.val, result, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,185 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/google/gops/internal"
|
||||
"github.com/google/gops/signal"
|
||||
)
|
||||
|
||||
var cmds = map[string](func(addr net.TCPAddr) error){
|
||||
"stack": stackTrace,
|
||||
"gc": gc,
|
||||
"memstats": memStats,
|
||||
"version": version,
|
||||
"pprof-heap": pprofHeap,
|
||||
"pprof-cpu": pprofCPU,
|
||||
"stats": stats,
|
||||
"trace": trace,
|
||||
}
|
||||
|
||||
func stackTrace(addr net.TCPAddr) error {
|
||||
return cmdWithPrint(addr, signal.StackTrace)
|
||||
}
|
||||
|
||||
func gc(addr net.TCPAddr) error {
|
||||
_, err := cmd(addr, signal.GC)
|
||||
return err
|
||||
}
|
||||
|
||||
func memStats(addr net.TCPAddr) error {
|
||||
return cmdWithPrint(addr, signal.MemStats)
|
||||
}
|
||||
|
||||
func version(addr net.TCPAddr) error {
|
||||
return cmdWithPrint(addr, signal.Version)
|
||||
}
|
||||
|
||||
func pprofHeap(addr net.TCPAddr) error {
|
||||
return pprof(addr, signal.HeapProfile)
|
||||
}
|
||||
|
||||
func pprofCPU(addr net.TCPAddr) error {
|
||||
fmt.Println("Profiling CPU now, will take 30 secs...")
|
||||
return pprof(addr, signal.CPUProfile)
|
||||
}
|
||||
|
||||
func trace(addr net.TCPAddr) error {
|
||||
fmt.Println("Tracing now, will take 5 secs...")
|
||||
out, err := cmd(addr, signal.Trace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(out) == 0 {
|
||||
return errors.New("nothing has traced")
|
||||
}
|
||||
tmpfile, err := ioutil.TempFile("", "trace")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove(tmpfile.Name())
|
||||
if err := ioutil.WriteFile(tmpfile.Name(), out, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Trace dump saved to: %s\n", tmpfile.Name())
|
||||
cmd := exec.Command("go", "tool", "trace", tmpfile.Name())
|
||||
cmd.Env = os.Environ()
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func pprof(addr net.TCPAddr, p byte) error {
|
||||
|
||||
tmpDumpFile, err := ioutil.TempFile("", "profile")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
{
|
||||
out, err := cmd(addr, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(out) == 0 {
|
||||
return errors.New("failed to read the profile")
|
||||
}
|
||||
defer os.Remove(tmpDumpFile.Name())
|
||||
if err := ioutil.WriteFile(tmpDumpFile.Name(), out, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Download running binary
|
||||
tmpBinFile, err := ioutil.TempFile("", "binary")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
{
|
||||
out, err := cmd(addr, signal.BinaryDump)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read the binary: %v", err)
|
||||
}
|
||||
if len(out) == 0 {
|
||||
return errors.New("failed to read the binary")
|
||||
}
|
||||
defer os.Remove(tmpBinFile.Name())
|
||||
if err := ioutil.WriteFile(tmpBinFile.Name(), out, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
fmt.Printf("Profiling dump saved to: %s\n", tmpDumpFile.Name())
|
||||
fmt.Printf("Binary file saved to: %s\n", tmpBinFile.Name())
|
||||
cmd := exec.Command("go", "tool", "pprof", tmpBinFile.Name(), tmpDumpFile.Name())
|
||||
cmd.Env = os.Environ()
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func stats(addr net.TCPAddr) error {
|
||||
return cmdWithPrint(addr, signal.Stats)
|
||||
}
|
||||
|
||||
func cmdWithPrint(addr net.TCPAddr, c byte) error {
|
||||
out, err := cmd(addr, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s", out)
|
||||
return nil
|
||||
}
|
||||
|
||||
// targetToAddr tries to parse the target string, be it remote host:port
|
||||
// or local process's PID.
|
||||
func targetToAddr(target string) (*net.TCPAddr, error) {
|
||||
if strings.Index(target, ":") != -1 {
|
||||
// addr host:port passed
|
||||
var err error
|
||||
addr, err := net.ResolveTCPAddr("tcp", target)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't parse dst address: %v", err)
|
||||
}
|
||||
return addr, nil
|
||||
}
|
||||
// try to find port by pid then, connect to local
|
||||
pid, err := strconv.Atoi(target)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't parse PID: %v", err)
|
||||
}
|
||||
port, err := internal.GetPort(pid)
|
||||
addr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:"+port)
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
func cmd(addr net.TCPAddr, c byte) ([]byte, error) {
|
||||
conn, err := cmdLazy(addr, c)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't get port by PID: %v", err)
|
||||
}
|
||||
|
||||
all, err := ioutil.ReadAll(conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return all, nil
|
||||
}
|
||||
|
||||
func cmdLazy(addr net.TCPAddr, c byte) (io.Reader, error) {
|
||||
conn, err := net.DialTCP("tcp", nil, &addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := conn.Write([]byte{c}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/google/gops/agent"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := agent.Listen(nil); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
time.Sleep(time.Hour)
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
// Copyright 2017 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 goprocess reports the Go processes running on a host.
|
||||
package goprocess
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
goversion "rsc.io/goversion/version"
|
||||
|
||||
"github.com/google/gops/internal"
|
||||
ps "github.com/keybase/go-ps"
|
||||
)
|
||||
|
||||
// P represents a Go process.
|
||||
type P struct {
|
||||
PID int
|
||||
PPID int
|
||||
Exec string
|
||||
Path string
|
||||
BuildVersion string
|
||||
Agent bool
|
||||
}
|
||||
|
||||
// FindAll returns all the Go processes currently running on this host.
|
||||
func FindAll() []P {
|
||||
var results []P
|
||||
|
||||
pss, err := ps.Processes()
|
||||
if err != nil {
|
||||
return results
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(pss))
|
||||
|
||||
for _, pr := range pss {
|
||||
pr := pr
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
path, version, agent, ok, err := isGo(pr)
|
||||
if err != nil {
|
||||
// TODO(jbd): Return a list of errors.
|
||||
}
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
results = append(results, P{
|
||||
PID: pr.Pid(),
|
||||
PPID: pr.PPid(),
|
||||
Exec: pr.Executable(),
|
||||
Path: path,
|
||||
BuildVersion: version,
|
||||
Agent: agent,
|
||||
})
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
return results
|
||||
}
|
||||
|
||||
// Find finds info about the process identified with the given PID.
|
||||
func Find(pid int) (p P, ok bool, err error) {
|
||||
pr, err := ps.FindProcess(pid)
|
||||
if err != nil {
|
||||
return P{}, false, err
|
||||
}
|
||||
path, version, agent, ok, err := isGo(pr)
|
||||
if !ok {
|
||||
return P{}, false, nil
|
||||
}
|
||||
return P{
|
||||
PID: pr.Pid(),
|
||||
PPID: pr.PPid(),
|
||||
Exec: pr.Executable(),
|
||||
Path: path,
|
||||
BuildVersion: version,
|
||||
Agent: agent,
|
||||
}, true, nil
|
||||
}
|
||||
|
||||
// isGo looks up the runtime.buildVersion symbol
|
||||
// in the process' binary and determines if the process
|
||||
// if a Go process or not. If the process is a Go process,
|
||||
// it reports PID, binary name and full path of the binary.
|
||||
func isGo(pr ps.Process) (path, version string, agent, ok bool, err error) {
|
||||
if pr.Pid() == 0 {
|
||||
// ignore system process
|
||||
return
|
||||
}
|
||||
path, err = pr.Path()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var versionInfo goversion.Version
|
||||
versionInfo, err = goversion.ReadExe(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ok = true
|
||||
version = versionInfo.Release
|
||||
pidfile, err := internal.PIDFile(pr.Pid())
|
||||
if err == nil {
|
||||
_, err := os.Stat(pidfile)
|
||||
agent = err == nil
|
||||
}
|
||||
return path, version, agent, ok, nil
|
||||
}
|
|
@ -0,0 +1,600 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package dwarf generates DWARF debugging information.
|
||||
// DWARF generation is split between the compiler and the linker,
|
||||
// this package contains the shared code.
|
||||
package dwarf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// InfoPrefix is the prefix for all the symbols containing DWARF info entries.
|
||||
const InfoPrefix = "go.info."
|
||||
|
||||
// Sym represents a symbol.
|
||||
type Sym interface {
|
||||
}
|
||||
|
||||
// A Var represents a local variable or a function parameter.
|
||||
type Var struct {
|
||||
Name string
|
||||
Abbrev int // Either DW_ABRV_AUTO or DW_ABRV_PARAM
|
||||
Offset int32
|
||||
Type Sym
|
||||
Link *Var
|
||||
}
|
||||
|
||||
// A Context specifies how to add data to a Sym.
|
||||
type Context interface {
|
||||
PtrSize() int
|
||||
AddInt(s Sym, size int, i int64)
|
||||
AddBytes(s Sym, b []byte)
|
||||
AddAddress(s Sym, t interface{}, ofs int64)
|
||||
AddSectionOffset(s Sym, size int, t interface{}, ofs int64)
|
||||
AddString(s Sym, v string)
|
||||
SymValue(s Sym) int64
|
||||
}
|
||||
|
||||
// AppendUleb128 appends v to b using DWARF's unsigned LEB128 encoding.
|
||||
func AppendUleb128(b []byte, v uint64) []byte {
|
||||
for {
|
||||
c := uint8(v & 0x7f)
|
||||
v >>= 7
|
||||
if v != 0 {
|
||||
c |= 0x80
|
||||
}
|
||||
b = append(b, c)
|
||||
if c&0x80 == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// AppendSleb128 appends v to b using DWARF's signed LEB128 encoding.
|
||||
func AppendSleb128(b []byte, v int64) []byte {
|
||||
for {
|
||||
c := uint8(v & 0x7f)
|
||||
s := uint8(v & 0x40)
|
||||
v >>= 7
|
||||
if (v != -1 || s == 0) && (v != 0 || s != 0) {
|
||||
c |= 0x80
|
||||
}
|
||||
b = append(b, c)
|
||||
if c&0x80 == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
var encbuf [20]byte
|
||||
|
||||
// AppendUleb128 appends v to s using DWARF's unsigned LEB128 encoding.
|
||||
func Uleb128put(ctxt Context, s Sym, v int64) {
|
||||
b := AppendUleb128(encbuf[:0], uint64(v))
|
||||
ctxt.AddBytes(s, b)
|
||||
}
|
||||
|
||||
// AppendUleb128 appends v to s using DWARF's signed LEB128 encoding.
|
||||
func Sleb128put(ctxt Context, s Sym, v int64) {
|
||||
b := AppendSleb128(encbuf[:0], v)
|
||||
ctxt.AddBytes(s, b)
|
||||
}
|
||||
|
||||
/*
|
||||
* Defining Abbrevs. This is hardcoded, and there will be
|
||||
* only a handful of them. The DWARF spec places no restriction on
|
||||
* the ordering of attributes in the Abbrevs and DIEs, and we will
|
||||
* always write them out in the order of declaration in the abbrev.
|
||||
*/
|
||||
type dwAttrForm struct {
|
||||
attr uint16
|
||||
form uint8
|
||||
}
|
||||
|
||||
// Go-specific type attributes.
|
||||
const (
|
||||
DW_AT_go_kind = 0x2900
|
||||
DW_AT_go_key = 0x2901
|
||||
DW_AT_go_elem = 0x2902
|
||||
|
||||
DW_AT_internal_location = 253 // params and locals; not emitted
|
||||
)
|
||||
|
||||
// Index into the abbrevs table below.
|
||||
// Keep in sync with ispubname() and ispubtype() below.
|
||||
// ispubtype considers >= NULLTYPE public
|
||||
const (
|
||||
DW_ABRV_NULL = iota
|
||||
DW_ABRV_COMPUNIT
|
||||
DW_ABRV_FUNCTION
|
||||
DW_ABRV_VARIABLE
|
||||
DW_ABRV_AUTO
|
||||
DW_ABRV_PARAM
|
||||
DW_ABRV_STRUCTFIELD
|
||||
DW_ABRV_FUNCTYPEPARAM
|
||||
DW_ABRV_DOTDOTDOT
|
||||
DW_ABRV_ARRAYRANGE
|
||||
DW_ABRV_NULLTYPE
|
||||
DW_ABRV_BASETYPE
|
||||
DW_ABRV_ARRAYTYPE
|
||||
DW_ABRV_CHANTYPE
|
||||
DW_ABRV_FUNCTYPE
|
||||
DW_ABRV_IFACETYPE
|
||||
DW_ABRV_MAPTYPE
|
||||
DW_ABRV_PTRTYPE
|
||||
DW_ABRV_BARE_PTRTYPE // only for void*, no DW_AT_type attr to please gdb 6.
|
||||
DW_ABRV_SLICETYPE
|
||||
DW_ABRV_STRINGTYPE
|
||||
DW_ABRV_STRUCTTYPE
|
||||
DW_ABRV_TYPEDECL
|
||||
DW_NABRV
|
||||
)
|
||||
|
||||
type dwAbbrev struct {
|
||||
tag uint8
|
||||
children uint8
|
||||
attr []dwAttrForm
|
||||
}
|
||||
|
||||
var abbrevs = [DW_NABRV]dwAbbrev{
|
||||
/* The mandatory DW_ABRV_NULL entry. */
|
||||
{0, 0, []dwAttrForm{}},
|
||||
|
||||
/* COMPUNIT */
|
||||
{
|
||||
DW_TAG_compile_unit,
|
||||
DW_CHILDREN_yes,
|
||||
[]dwAttrForm{
|
||||
{DW_AT_name, DW_FORM_string},
|
||||
{DW_AT_language, DW_FORM_data1},
|
||||
{DW_AT_low_pc, DW_FORM_addr},
|
||||
{DW_AT_high_pc, DW_FORM_addr},
|
||||
{DW_AT_stmt_list, DW_FORM_data4},
|
||||
{DW_AT_comp_dir, DW_FORM_string},
|
||||
},
|
||||
},
|
||||
|
||||
/* FUNCTION */
|
||||
{
|
||||
DW_TAG_subprogram,
|
||||
DW_CHILDREN_yes,
|
||||
[]dwAttrForm{
|
||||
{DW_AT_name, DW_FORM_string},
|
||||
{DW_AT_low_pc, DW_FORM_addr},
|
||||
{DW_AT_high_pc, DW_FORM_addr},
|
||||
{DW_AT_external, DW_FORM_flag},
|
||||
},
|
||||
},
|
||||
|
||||
/* VARIABLE */
|
||||
{
|
||||
DW_TAG_variable,
|
||||
DW_CHILDREN_no,
|
||||
[]dwAttrForm{
|
||||
{DW_AT_name, DW_FORM_string},
|
||||
{DW_AT_location, DW_FORM_block1},
|
||||
{DW_AT_type, DW_FORM_ref_addr},
|
||||
{DW_AT_external, DW_FORM_flag},
|
||||
},
|
||||
},
|
||||
|
||||
/* AUTO */
|
||||
{
|
||||
DW_TAG_variable,
|
||||
DW_CHILDREN_no,
|
||||
[]dwAttrForm{
|
||||
{DW_AT_name, DW_FORM_string},
|
||||
{DW_AT_location, DW_FORM_block1},
|
||||
{DW_AT_type, DW_FORM_ref_addr},
|
||||
},
|
||||
},
|
||||
|
||||
/* PARAM */
|
||||
{
|
||||
DW_TAG_formal_parameter,
|
||||
DW_CHILDREN_no,
|
||||
[]dwAttrForm{
|
||||
{DW_AT_name, DW_FORM_string},
|
||||
{DW_AT_location, DW_FORM_block1},
|
||||
{DW_AT_type, DW_FORM_ref_addr},
|
||||
},
|
||||
},
|
||||
|
||||
/* STRUCTFIELD */
|
||||
{
|
||||
DW_TAG_member,
|
||||
DW_CHILDREN_no,
|
||||
[]dwAttrForm{
|
||||
{DW_AT_name, DW_FORM_string},
|
||||
{DW_AT_data_member_location, DW_FORM_block1},
|
||||
{DW_AT_type, DW_FORM_ref_addr},
|
||||
},
|
||||
},
|
||||
|
||||
/* FUNCTYPEPARAM */
|
||||
{
|
||||
DW_TAG_formal_parameter,
|
||||
DW_CHILDREN_no,
|
||||
|
||||
// No name!
|
||||
[]dwAttrForm{
|
||||
{DW_AT_type, DW_FORM_ref_addr},
|
||||
},
|
||||
},
|
||||
|
||||
/* DOTDOTDOT */
|
||||
{
|
||||
DW_TAG_unspecified_parameters,
|
||||
DW_CHILDREN_no,
|
||||
[]dwAttrForm{},
|
||||
},
|
||||
|
||||
/* ARRAYRANGE */
|
||||
{
|
||||
DW_TAG_subrange_type,
|
||||
DW_CHILDREN_no,
|
||||
|
||||
// No name!
|
||||
[]dwAttrForm{
|
||||
{DW_AT_type, DW_FORM_ref_addr},
|
||||
{DW_AT_count, DW_FORM_udata},
|
||||
},
|
||||
},
|
||||
|
||||
// Below here are the types considered public by ispubtype
|
||||
/* NULLTYPE */
|
||||
{
|
||||
DW_TAG_unspecified_type,
|
||||
DW_CHILDREN_no,
|
||||
[]dwAttrForm{
|
||||
{DW_AT_name, DW_FORM_string},
|
||||
},
|
||||
},
|
||||
|
||||
/* BASETYPE */
|
||||
{
|
||||
DW_TAG_base_type,
|
||||
DW_CHILDREN_no,
|
||||
[]dwAttrForm{
|
||||
{DW_AT_name, DW_FORM_string},
|
||||
{DW_AT_encoding, DW_FORM_data1},
|
||||
{DW_AT_byte_size, DW_FORM_data1},
|
||||
{DW_AT_go_kind, DW_FORM_data1},
|
||||
},
|
||||
},
|
||||
|
||||
/* ARRAYTYPE */
|
||||
// child is subrange with upper bound
|
||||
{
|
||||
DW_TAG_array_type,
|
||||
DW_CHILDREN_yes,
|
||||
[]dwAttrForm{
|
||||
{DW_AT_name, DW_FORM_string},
|
||||
{DW_AT_type, DW_FORM_ref_addr},
|
||||
{DW_AT_byte_size, DW_FORM_udata},
|
||||
{DW_AT_go_kind, DW_FORM_data1},
|
||||
},
|
||||
},
|
||||
|
||||
/* CHANTYPE */
|
||||
{
|
||||
DW_TAG_typedef,
|
||||
DW_CHILDREN_no,
|
||||
[]dwAttrForm{
|
||||
{DW_AT_name, DW_FORM_string},
|
||||
{DW_AT_type, DW_FORM_ref_addr},
|
||||
{DW_AT_go_kind, DW_FORM_data1},
|
||||
{DW_AT_go_elem, DW_FORM_ref_addr},
|
||||
},
|
||||
},
|
||||
|
||||
/* FUNCTYPE */
|
||||
{
|
||||
DW_TAG_subroutine_type,
|
||||
DW_CHILDREN_yes,
|
||||
[]dwAttrForm{
|
||||
{DW_AT_name, DW_FORM_string},
|
||||
// {DW_AT_type, DW_FORM_ref_addr},
|
||||
{DW_AT_go_kind, DW_FORM_data1},
|
||||
},
|
||||
},
|
||||
|
||||
/* IFACETYPE */
|
||||
{
|
||||
DW_TAG_typedef,
|
||||
DW_CHILDREN_yes,
|
||||
[]dwAttrForm{
|
||||
{DW_AT_name, DW_FORM_string},
|
||||
{DW_AT_type, DW_FORM_ref_addr},
|
||||
{DW_AT_go_kind, DW_FORM_data1},
|
||||
},
|
||||
},
|
||||
|
||||
/* MAPTYPE */
|
||||
{
|
||||
DW_TAG_typedef,
|
||||
DW_CHILDREN_no,
|
||||
[]dwAttrForm{
|
||||
{DW_AT_name, DW_FORM_string},
|
||||
{DW_AT_type, DW_FORM_ref_addr},
|
||||
{DW_AT_go_kind, DW_FORM_data1},
|
||||
{DW_AT_go_key, DW_FORM_ref_addr},
|
||||
{DW_AT_go_elem, DW_FORM_ref_addr},
|
||||
},
|
||||
},
|
||||
|
||||
/* PTRTYPE */
|
||||
{
|
||||
DW_TAG_pointer_type,
|
||||
DW_CHILDREN_no,
|
||||
[]dwAttrForm{
|
||||
{DW_AT_name, DW_FORM_string},
|
||||
{DW_AT_type, DW_FORM_ref_addr},
|
||||
{DW_AT_go_kind, DW_FORM_data1},
|
||||
},
|
||||
},
|
||||
|
||||
/* BARE_PTRTYPE */
|
||||
{
|
||||
DW_TAG_pointer_type,
|
||||
DW_CHILDREN_no,
|
||||
[]dwAttrForm{
|
||||
{DW_AT_name, DW_FORM_string},
|
||||
},
|
||||
},
|
||||
|
||||
/* SLICETYPE */
|
||||
{
|
||||
DW_TAG_structure_type,
|
||||
DW_CHILDREN_yes,
|
||||
[]dwAttrForm{
|
||||
{DW_AT_name, DW_FORM_string},
|
||||
{DW_AT_byte_size, DW_FORM_udata},
|
||||
{DW_AT_go_kind, DW_FORM_data1},
|
||||
{DW_AT_go_elem, DW_FORM_ref_addr},
|
||||
},
|
||||
},
|
||||
|
||||
/* STRINGTYPE */
|
||||
{
|
||||
DW_TAG_structure_type,
|
||||
DW_CHILDREN_yes,
|
||||
[]dwAttrForm{
|
||||
{DW_AT_name, DW_FORM_string},
|
||||
{DW_AT_byte_size, DW_FORM_udata},
|
||||
{DW_AT_go_kind, DW_FORM_data1},
|
||||
},
|
||||
},
|
||||
|
||||
/* STRUCTTYPE */
|
||||
{
|
||||
DW_TAG_structure_type,
|
||||
DW_CHILDREN_yes,
|
||||
[]dwAttrForm{
|
||||
{DW_AT_name, DW_FORM_string},
|
||||
{DW_AT_byte_size, DW_FORM_udata},
|
||||
{DW_AT_go_kind, DW_FORM_data1},
|
||||
},
|
||||
},
|
||||
|
||||
/* TYPEDECL */
|
||||
{
|
||||
DW_TAG_typedef,
|
||||
DW_CHILDREN_no,
|
||||
[]dwAttrForm{
|
||||
{DW_AT_name, DW_FORM_string},
|
||||
{DW_AT_type, DW_FORM_ref_addr},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// GetAbbrev returns the contents of the .debug_abbrev section.
|
||||
func GetAbbrev() []byte {
|
||||
var buf []byte
|
||||
for i := 1; i < DW_NABRV; i++ {
|
||||
// See section 7.5.3
|
||||
buf = AppendUleb128(buf, uint64(i))
|
||||
|
||||
buf = AppendUleb128(buf, uint64(abbrevs[i].tag))
|
||||
buf = append(buf, byte(abbrevs[i].children))
|
||||
for _, f := range abbrevs[i].attr {
|
||||
buf = AppendUleb128(buf, uint64(f.attr))
|
||||
buf = AppendUleb128(buf, uint64(f.form))
|
||||
}
|
||||
buf = append(buf, 0, 0)
|
||||
}
|
||||
return append(buf, 0)
|
||||
}
|
||||
|
||||
/*
|
||||
* Debugging Information Entries and their attributes.
|
||||
*/
|
||||
|
||||
// DWAttr represents an attribute of a DWDie.
|
||||
//
|
||||
// For DW_CLS_string and _block, value should contain the length, and
|
||||
// data the data, for _reference, value is 0 and data is a DWDie* to
|
||||
// the referenced instance, for all others, value is the whole thing
|
||||
// and data is null.
|
||||
type DWAttr struct {
|
||||
Link *DWAttr
|
||||
Atr uint16 // DW_AT_
|
||||
Cls uint8 // DW_CLS_
|
||||
Value int64
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
// DWDie represents a DWARF debug info entry.
|
||||
type DWDie struct {
|
||||
Abbrev int
|
||||
Link *DWDie
|
||||
Child *DWDie
|
||||
Attr *DWAttr
|
||||
Sym Sym
|
||||
}
|
||||
|
||||
func putattr(ctxt Context, s Sym, abbrev int, form int, cls int, value int64, data interface{}) error {
|
||||
switch form {
|
||||
case DW_FORM_addr: // address
|
||||
ctxt.AddAddress(s, data, value)
|
||||
|
||||
case DW_FORM_block1: // block
|
||||
if cls == DW_CLS_ADDRESS {
|
||||
ctxt.AddInt(s, 1, int64(1+ctxt.PtrSize()))
|
||||
ctxt.AddInt(s, 1, DW_OP_addr)
|
||||
ctxt.AddAddress(s, data, 0)
|
||||
break
|
||||
}
|
||||
|
||||
value &= 0xff
|
||||
ctxt.AddInt(s, 1, value)
|
||||
p := data.([]byte)[:value]
|
||||
ctxt.AddBytes(s, p)
|
||||
|
||||
case DW_FORM_block2: // block
|
||||
value &= 0xffff
|
||||
|
||||
ctxt.AddInt(s, 2, value)
|
||||
p := data.([]byte)[:value]
|
||||
ctxt.AddBytes(s, p)
|
||||
|
||||
case DW_FORM_block4: // block
|
||||
value &= 0xffffffff
|
||||
|
||||
ctxt.AddInt(s, 4, value)
|
||||
p := data.([]byte)[:value]
|
||||
ctxt.AddBytes(s, p)
|
||||
|
||||
case DW_FORM_block: // block
|
||||
Uleb128put(ctxt, s, value)
|
||||
|
||||
p := data.([]byte)[:value]
|
||||
ctxt.AddBytes(s, p)
|
||||
|
||||
case DW_FORM_data1: // constant
|
||||
ctxt.AddInt(s, 1, value)
|
||||
|
||||
case DW_FORM_data2: // constant
|
||||
ctxt.AddInt(s, 2, value)
|
||||
|
||||
case DW_FORM_data4: // constant, {line,loclist,mac,rangelist}ptr
|
||||
if cls == DW_CLS_PTR { // DW_AT_stmt_list
|
||||
ctxt.AddSectionOffset(s, 4, data, 0)
|
||||
break
|
||||
}
|
||||
ctxt.AddInt(s, 4, value)
|
||||
|
||||
case DW_FORM_data8: // constant, {line,loclist,mac,rangelist}ptr
|
||||
ctxt.AddInt(s, 8, value)
|
||||
|
||||
case DW_FORM_sdata: // constant
|
||||
Sleb128put(ctxt, s, value)
|
||||
|
||||
case DW_FORM_udata: // constant
|
||||
Uleb128put(ctxt, s, value)
|
||||
|
||||
case DW_FORM_string: // string
|
||||
str := data.(string)
|
||||
ctxt.AddString(s, str)
|
||||
// TODO(ribrdb): verify padded strings are never used and remove this
|
||||
for i := int64(len(str)); i < value; i++ {
|
||||
ctxt.AddInt(s, 1, 0)
|
||||
}
|
||||
|
||||
case DW_FORM_flag: // flag
|
||||
if value != 0 {
|
||||
ctxt.AddInt(s, 1, 1)
|
||||
} else {
|
||||
ctxt.AddInt(s, 1, 0)
|
||||
}
|
||||
|
||||
// In DWARF 2 (which is what we claim to generate),
|
||||
// the ref_addr is the same size as a normal address.
|
||||
// In DWARF 3 it is always 32 bits, unless emitting a large
|
||||
// (> 4 GB of debug info aka "64-bit") unit, which we don't implement.
|
||||
case DW_FORM_ref_addr: // reference to a DIE in the .info section
|
||||
if data == nil {
|
||||
return fmt.Errorf("dwarf: null reference in %d", abbrev)
|
||||
} else {
|
||||
ctxt.AddSectionOffset(s, ctxt.PtrSize(), data, 0)
|
||||
}
|
||||
|
||||
case DW_FORM_ref1, // reference within the compilation unit
|
||||
DW_FORM_ref2, // reference
|
||||
DW_FORM_ref4, // reference
|
||||
DW_FORM_ref8, // reference
|
||||
DW_FORM_ref_udata, // reference
|
||||
|
||||
DW_FORM_strp, // string
|
||||
DW_FORM_indirect: // (see Section 7.5.3)
|
||||
fallthrough
|
||||
default:
|
||||
return fmt.Errorf("dwarf: unsupported attribute form %d / class %d", form, cls)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PutAttrs writes the attributes for a DIE to symbol 's'.
|
||||
//
|
||||
// Note that we can (and do) add arbitrary attributes to a DIE, but
|
||||
// only the ones actually listed in the Abbrev will be written out.
|
||||
func PutAttrs(ctxt Context, s Sym, abbrev int, attr *DWAttr) {
|
||||
Outer:
|
||||
for _, f := range abbrevs[abbrev].attr {
|
||||
for ap := attr; ap != nil; ap = ap.Link {
|
||||
if ap.Atr == f.attr {
|
||||
putattr(ctxt, s, abbrev, int(f.form), int(ap.Cls), ap.Value, ap.Data)
|
||||
continue Outer
|
||||
}
|
||||
}
|
||||
|
||||
putattr(ctxt, s, abbrev, int(f.form), 0, 0, nil)
|
||||
}
|
||||
}
|
||||
|
||||
// HasChildren returns true if 'die' uses an abbrev that supports children.
|
||||
func HasChildren(die *DWDie) bool {
|
||||
return abbrevs[die.Abbrev].children != 0
|
||||
}
|
||||
|
||||
// PutFunc writes a DIE for a function to s.
|
||||
// It also writes child DIEs for each variable in vars.
|
||||
func PutFunc(ctxt Context, s Sym, name string, external bool, startPC Sym, size int64, vars *Var) {
|
||||
Uleb128put(ctxt, s, DW_ABRV_FUNCTION)
|
||||
putattr(ctxt, s, DW_ABRV_FUNCTION, DW_FORM_string, DW_CLS_STRING, int64(len(name)), name)
|
||||
putattr(ctxt, s, DW_ABRV_FUNCTION, DW_FORM_addr, DW_CLS_ADDRESS, 0, startPC)
|
||||
putattr(ctxt, s, DW_ABRV_FUNCTION, DW_FORM_addr, DW_CLS_ADDRESS, size+ctxt.SymValue(startPC), startPC)
|
||||
var ev int64
|
||||
if external {
|
||||
ev = 1
|
||||
}
|
||||
putattr(ctxt, s, DW_ABRV_FUNCTION, DW_FORM_flag, DW_CLS_FLAG, ev, 0)
|
||||
names := make(map[string]bool)
|
||||
for v := vars; v != nil; v = v.Link {
|
||||
var n string
|
||||
if names[v.Name] {
|
||||
n = fmt.Sprintf("%s#%d", v.Name, len(names))
|
||||
} else {
|
||||
n = v.Name
|
||||
}
|
||||
names[n] = true
|
||||
|
||||
Uleb128put(ctxt, s, int64(v.Abbrev))
|
||||
putattr(ctxt, s, v.Abbrev, DW_FORM_string, DW_CLS_STRING, int64(len(n)), n)
|
||||
loc := append(encbuf[:0], DW_OP_call_frame_cfa)
|
||||
if v.Offset != 0 {
|
||||
loc = append(loc, DW_OP_consts)
|
||||
loc = AppendSleb128(loc, int64(v.Offset))
|
||||
loc = append(loc, DW_OP_plus)
|
||||
}
|
||||
putattr(ctxt, s, v.Abbrev, DW_FORM_block1, DW_CLS_BLOCK, int64(len(loc)), loc)
|
||||
putattr(ctxt, s, v.Abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type)
|
||||
|
||||
}
|
||||
Uleb128put(ctxt, s, 0)
|
||||
}
|
|
@ -0,0 +1,483 @@
|
|||
// Copyright 2010 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 dwarf
|
||||
|
||||
// Cut, pasted, tr-and-awk'ed from tables in
|
||||
// http://dwarfstd.org/doc/Dwarf3.pdf
|
||||
|
||||
// Table 18
|
||||
const (
|
||||
DW_TAG_array_type = 0x01
|
||||
DW_TAG_class_type = 0x02
|
||||
DW_TAG_entry_point = 0x03
|
||||
DW_TAG_enumeration_type = 0x04
|
||||
DW_TAG_formal_parameter = 0x05
|
||||
DW_TAG_imported_declaration = 0x08
|
||||
DW_TAG_label = 0x0a
|
||||
DW_TAG_lexical_block = 0x0b
|
||||
DW_TAG_member = 0x0d
|
||||
DW_TAG_pointer_type = 0x0f
|
||||
DW_TAG_reference_type = 0x10
|
||||
DW_TAG_compile_unit = 0x11
|
||||
DW_TAG_string_type = 0x12
|
||||
DW_TAG_structure_type = 0x13
|
||||
DW_TAG_subroutine_type = 0x15
|
||||
DW_TAG_typedef = 0x16
|
||||
DW_TAG_union_type = 0x17
|
||||
DW_TAG_unspecified_parameters = 0x18
|
||||
DW_TAG_variant = 0x19
|
||||
DW_TAG_common_block = 0x1a
|
||||
DW_TAG_common_inclusion = 0x1b
|
||||
DW_TAG_inheritance = 0x1c
|
||||
DW_TAG_inlined_subroutine = 0x1d
|
||||
DW_TAG_module = 0x1e
|
||||
DW_TAG_ptr_to_member_type = 0x1f
|
||||
DW_TAG_set_type = 0x20
|
||||
DW_TAG_subrange_type = 0x21
|
||||
DW_TAG_with_stmt = 0x22
|
||||
DW_TAG_access_declaration = 0x23
|
||||
DW_TAG_base_type = 0x24
|
||||
DW_TAG_catch_block = 0x25
|
||||
DW_TAG_const_type = 0x26
|
||||
DW_TAG_constant = 0x27
|
||||
DW_TAG_enumerator = 0x28
|
||||
DW_TAG_file_type = 0x29
|
||||
DW_TAG_friend = 0x2a
|
||||
DW_TAG_namelist = 0x2b
|
||||
DW_TAG_namelist_item = 0x2c
|
||||
DW_TAG_packed_type = 0x2d
|
||||
DW_TAG_subprogram = 0x2e
|
||||
DW_TAG_template_type_parameter = 0x2f
|
||||
DW_TAG_template_value_parameter = 0x30
|
||||
DW_TAG_thrown_type = 0x31
|
||||
DW_TAG_try_block = 0x32
|
||||
DW_TAG_variant_part = 0x33
|
||||
DW_TAG_variable = 0x34
|
||||
DW_TAG_volatile_type = 0x35
|
||||
// Dwarf3
|
||||
DW_TAG_dwarf_procedure = 0x36
|
||||
DW_TAG_restrict_type = 0x37
|
||||
DW_TAG_interface_type = 0x38
|
||||
DW_TAG_namespace = 0x39
|
||||
DW_TAG_imported_module = 0x3a
|
||||
DW_TAG_unspecified_type = 0x3b
|
||||
DW_TAG_partial_unit = 0x3c
|
||||
DW_TAG_imported_unit = 0x3d
|
||||
DW_TAG_condition = 0x3f
|
||||
DW_TAG_shared_type = 0x40
|
||||
// Dwarf4
|
||||
DW_TAG_type_unit = 0x41
|
||||
DW_TAG_rvalue_reference_type = 0x42
|
||||
DW_TAG_template_alias = 0x43
|
||||
|
||||
// User defined
|
||||
DW_TAG_lo_user = 0x4080
|
||||
DW_TAG_hi_user = 0xffff
|
||||
)
|
||||
|
||||
// Table 19
|
||||
const (
|
||||
DW_CHILDREN_no = 0x00
|
||||
DW_CHILDREN_yes = 0x01
|
||||
)
|
||||
|
||||
// Not from the spec, but logically belongs here
|
||||
const (
|
||||
DW_CLS_ADDRESS = 0x01 + iota
|
||||
DW_CLS_BLOCK
|
||||
DW_CLS_CONSTANT
|
||||
DW_CLS_FLAG
|
||||
DW_CLS_PTR // lineptr, loclistptr, macptr, rangelistptr
|
||||
DW_CLS_REFERENCE
|
||||
DW_CLS_ADDRLOC
|
||||
DW_CLS_STRING
|
||||
)
|
||||
|
||||
// Table 20
|
||||
const (
|
||||
DW_AT_sibling = 0x01 // reference
|
||||
DW_AT_location = 0x02 // block, loclistptr
|
||||
DW_AT_name = 0x03 // string
|
||||
DW_AT_ordering = 0x09 // constant
|
||||
DW_AT_byte_size = 0x0b // block, constant, reference
|
||||
DW_AT_bit_offset = 0x0c // block, constant, reference
|
||||
DW_AT_bit_size = 0x0d // block, constant, reference
|
||||
DW_AT_stmt_list = 0x10 // lineptr
|
||||
DW_AT_low_pc = 0x11 // address
|
||||
DW_AT_high_pc = 0x12 // address
|
||||
DW_AT_language = 0x13 // constant
|
||||
DW_AT_discr = 0x15 // reference
|
||||
DW_AT_discr_value = 0x16 // constant
|
||||
DW_AT_visibility = 0x17 // constant
|
||||
DW_AT_import = 0x18 // reference
|
||||
DW_AT_string_length = 0x19 // block, loclistptr
|
||||
DW_AT_common_reference = 0x1a // reference
|
||||
DW_AT_comp_dir = 0x1b // string
|
||||
DW_AT_const_value = 0x1c // block, constant, string
|
||||
DW_AT_containing_type = 0x1d // reference
|
||||
DW_AT_default_value = 0x1e // reference
|
||||
DW_AT_inline = 0x20 // constant
|
||||
DW_AT_is_optional = 0x21 // flag
|
||||
DW_AT_lower_bound = 0x22 // block, constant, reference
|
||||
DW_AT_producer = 0x25 // string
|
||||
DW_AT_prototyped = 0x27 // flag
|
||||
DW_AT_return_addr = 0x2a // block, loclistptr
|
||||
DW_AT_start_scope = 0x2c // constant
|
||||
DW_AT_bit_stride = 0x2e // constant
|
||||
DW_AT_upper_bound = 0x2f // block, constant, reference
|
||||
DW_AT_abstract_origin = 0x31 // reference
|
||||
DW_AT_accessibility = 0x32 // constant
|
||||
DW_AT_address_class = 0x33 // constant
|
||||
DW_AT_artificial = 0x34 // flag
|
||||
DW_AT_base_types = 0x35 // reference
|
||||
DW_AT_calling_convention = 0x36 // constant
|
||||
DW_AT_count = 0x37 // block, constant, reference
|
||||
DW_AT_data_member_location = 0x38 // block, constant, loclistptr
|
||||
DW_AT_decl_column = 0x39 // constant
|
||||
DW_AT_decl_file = 0x3a // constant
|
||||
DW_AT_decl_line = 0x3b // constant
|
||||
DW_AT_declaration = 0x3c // flag
|
||||
DW_AT_discr_list = 0x3d // block
|
||||
DW_AT_encoding = 0x3e // constant
|
||||
DW_AT_external = 0x3f // flag
|
||||
DW_AT_frame_base = 0x40 // block, loclistptr
|
||||
DW_AT_friend = 0x41 // reference
|
||||
DW_AT_identifier_case = 0x42 // constant
|
||||
DW_AT_macro_info = 0x43 // macptr
|
||||
DW_AT_namelist_item = 0x44 // block
|
||||
DW_AT_priority = 0x45 // reference
|
||||
DW_AT_segment = 0x46 // block, loclistptr
|
||||
DW_AT_specification = 0x47 // reference
|
||||
DW_AT_static_link = 0x48 // block, loclistptr
|
||||
DW_AT_type = 0x49 // reference
|
||||
DW_AT_use_location = 0x4a // block, loclistptr
|
||||
DW_AT_variable_parameter = 0x4b // flag
|
||||
DW_AT_virtuality = 0x4c // constant
|
||||
DW_AT_vtable_elem_location = 0x4d // block, loclistptr
|
||||
// Dwarf3
|
||||
DW_AT_allocated = 0x4e // block, constant, reference
|
||||
DW_AT_associated = 0x4f // block, constant, reference
|
||||
DW_AT_data_location = 0x50 // block
|
||||
DW_AT_byte_stride = 0x51 // block, constant, reference
|
||||
DW_AT_entry_pc = 0x52 // address
|
||||
DW_AT_use_UTF8 = 0x53 // flag
|
||||
DW_AT_extension = 0x54 // reference
|
||||
DW_AT_ranges = 0x55 // rangelistptr
|
||||
DW_AT_trampoline = 0x56 // address, flag, reference, string
|
||||
DW_AT_call_column = 0x57 // constant
|
||||
DW_AT_call_file = 0x58 // constant
|
||||
DW_AT_call_line = 0x59 // constant
|
||||
DW_AT_description = 0x5a // string
|
||||
DW_AT_binary_scale = 0x5b // constant
|
||||
DW_AT_decimal_scale = 0x5c // constant
|
||||
DW_AT_small = 0x5d // reference
|
||||
DW_AT_decimal_sign = 0x5e // constant
|
||||
DW_AT_digit_count = 0x5f // constant
|
||||
DW_AT_picture_string = 0x60 // string
|
||||
DW_AT_mutable = 0x61 // flag
|
||||
DW_AT_threads_scaled = 0x62 // flag
|
||||
DW_AT_explicit = 0x63 // flag
|
||||
DW_AT_object_pointer = 0x64 // reference
|
||||
DW_AT_endianity = 0x65 // constant
|
||||
DW_AT_elemental = 0x66 // flag
|
||||
DW_AT_pure = 0x67 // flag
|
||||
DW_AT_recursive = 0x68 // flag
|
||||
|
||||
DW_AT_lo_user = 0x2000 // ---
|
||||
DW_AT_hi_user = 0x3fff // ---
|
||||
)
|
||||
|
||||
// Table 21
|
||||
const (
|
||||
DW_FORM_addr = 0x01 // address
|
||||
DW_FORM_block2 = 0x03 // block
|
||||
DW_FORM_block4 = 0x04 // block
|
||||
DW_FORM_data2 = 0x05 // constant
|
||||
DW_FORM_data4 = 0x06 // constant, lineptr, loclistptr, macptr, rangelistptr
|
||||
DW_FORM_data8 = 0x07 // constant, lineptr, loclistptr, macptr, rangelistptr
|
||||
DW_FORM_string = 0x08 // string
|
||||
DW_FORM_block = 0x09 // block
|
||||
DW_FORM_block1 = 0x0a // block
|
||||
DW_FORM_data1 = 0x0b // constant
|
||||
DW_FORM_flag = 0x0c // flag
|
||||
DW_FORM_sdata = 0x0d // constant
|
||||
DW_FORM_strp = 0x0e // string
|
||||
DW_FORM_udata = 0x0f // constant
|
||||
DW_FORM_ref_addr = 0x10 // reference
|
||||
DW_FORM_ref1 = 0x11 // reference
|
||||
DW_FORM_ref2 = 0x12 // reference
|
||||
DW_FORM_ref4 = 0x13 // reference
|
||||
DW_FORM_ref8 = 0x14 // reference
|
||||
DW_FORM_ref_udata = 0x15 // reference
|
||||
DW_FORM_indirect = 0x16 // (see Section 7.5.3)
|
||||
)
|
||||
|
||||
// Table 24 (#operands, notes)
|
||||
const (
|
||||
DW_OP_addr = 0x03 // 1 constant address (size target specific)
|
||||
DW_OP_deref = 0x06 // 0
|
||||
DW_OP_const1u = 0x08 // 1 1-byte constant
|
||||
DW_OP_const1s = 0x09 // 1 1-byte constant
|
||||
DW_OP_const2u = 0x0a // 1 2-byte constant
|
||||
DW_OP_const2s = 0x0b // 1 2-byte constant
|
||||
DW_OP_const4u = 0x0c // 1 4-byte constant
|
||||
DW_OP_const4s = 0x0d // 1 4-byte constant
|
||||
DW_OP_const8u = 0x0e // 1 8-byte constant
|
||||
DW_OP_const8s = 0x0f // 1 8-byte constant
|
||||
DW_OP_constu = 0x10 // 1 ULEB128 constant
|
||||
DW_OP_consts = 0x11 // 1 SLEB128 constant
|
||||
DW_OP_dup = 0x12 // 0
|
||||
DW_OP_drop = 0x13 // 0
|
||||
DW_OP_over = 0x14 // 0
|
||||
DW_OP_pick = 0x15 // 1 1-byte stack index
|
||||
DW_OP_swap = 0x16 // 0
|
||||
DW_OP_rot = 0x17 // 0
|
||||
DW_OP_xderef = 0x18 // 0
|
||||
DW_OP_abs = 0x19 // 0
|
||||
DW_OP_and = 0x1a // 0
|
||||
DW_OP_div = 0x1b // 0
|
||||
DW_OP_minus = 0x1c // 0
|
||||
DW_OP_mod = 0x1d // 0
|
||||
DW_OP_mul = 0x1e // 0
|
||||
DW_OP_neg = 0x1f // 0
|
||||
DW_OP_not = 0x20 // 0
|
||||
DW_OP_or = 0x21 // 0
|
||||
DW_OP_plus = 0x22 // 0
|
||||
DW_OP_plus_uconst = 0x23 // 1 ULEB128 addend
|
||||
DW_OP_shl = 0x24 // 0
|
||||
DW_OP_shr = 0x25 // 0
|
||||
DW_OP_shra = 0x26 // 0
|
||||
DW_OP_xor = 0x27 // 0
|
||||
DW_OP_skip = 0x2f // 1 signed 2-byte constant
|
||||
DW_OP_bra = 0x28 // 1 signed 2-byte constant
|
||||
DW_OP_eq = 0x29 // 0
|
||||
DW_OP_ge = 0x2a // 0
|
||||
DW_OP_gt = 0x2b // 0
|
||||
DW_OP_le = 0x2c // 0
|
||||
DW_OP_lt = 0x2d // 0
|
||||
DW_OP_ne = 0x2e // 0
|
||||
DW_OP_lit0 = 0x30 // 0 ...
|
||||
DW_OP_lit31 = 0x4f // 0 literals 0..31 = (DW_OP_lit0 + literal)
|
||||
DW_OP_reg0 = 0x50 // 0 ..
|
||||
DW_OP_reg31 = 0x6f // 0 reg 0..31 = (DW_OP_reg0 + regnum)
|
||||
DW_OP_breg0 = 0x70 // 1 ...
|
||||
DW_OP_breg31 = 0x8f // 1 SLEB128 offset base register 0..31 = (DW_OP_breg0 + regnum)
|
||||
DW_OP_regx = 0x90 // 1 ULEB128 register
|
||||
DW_OP_fbreg = 0x91 // 1 SLEB128 offset
|
||||
DW_OP_bregx = 0x92 // 2 ULEB128 register followed by SLEB128 offset
|
||||
DW_OP_piece = 0x93 // 1 ULEB128 size of piece addressed
|
||||
DW_OP_deref_size = 0x94 // 1 1-byte size of data retrieved
|
||||
DW_OP_xderef_size = 0x95 // 1 1-byte size of data retrieved
|
||||
DW_OP_nop = 0x96 // 0
|
||||
DW_OP_push_object_address = 0x97 // 0
|
||||
DW_OP_call2 = 0x98 // 1 2-byte offset of DIE
|
||||
DW_OP_call4 = 0x99 // 1 4-byte offset of DIE
|
||||
DW_OP_call_ref = 0x9a // 1 4- or 8-byte offset of DIE
|
||||
DW_OP_form_tls_address = 0x9b // 0
|
||||
DW_OP_call_frame_cfa = 0x9c // 0
|
||||
DW_OP_bit_piece = 0x9d // 2
|
||||
DW_OP_lo_user = 0xe0
|
||||
DW_OP_hi_user = 0xff
|
||||
)
|
||||
|
||||
// Table 25
|
||||
const (
|
||||
DW_ATE_address = 0x01
|
||||
DW_ATE_boolean = 0x02
|
||||
DW_ATE_complex_float = 0x03
|
||||
DW_ATE_float = 0x04
|
||||
DW_ATE_signed = 0x05
|
||||
DW_ATE_signed_char = 0x06
|
||||
DW_ATE_unsigned = 0x07
|
||||
DW_ATE_unsigned_char = 0x08
|
||||
DW_ATE_imaginary_float = 0x09
|
||||
DW_ATE_packed_decimal = 0x0a
|
||||
DW_ATE_numeric_string = 0x0b
|
||||
DW_ATE_edited = 0x0c
|
||||
DW_ATE_signed_fixed = 0x0d
|
||||
DW_ATE_unsigned_fixed = 0x0e
|
||||
DW_ATE_decimal_float = 0x0f
|
||||
DW_ATE_lo_user = 0x80
|
||||
DW_ATE_hi_user = 0xff
|
||||
)
|
||||
|
||||
// Table 26
|
||||
const (
|
||||
DW_DS_unsigned = 0x01
|
||||
DW_DS_leading_overpunch = 0x02
|
||||
DW_DS_trailing_overpunch = 0x03
|
||||
DW_DS_leading_separate = 0x04
|
||||
DW_DS_trailing_separate = 0x05
|
||||
)
|
||||
|
||||
// Table 27
|
||||
const (
|
||||
DW_END_default = 0x00
|
||||
DW_END_big = 0x01
|
||||
DW_END_little = 0x02
|
||||
DW_END_lo_user = 0x40
|
||||
DW_END_hi_user = 0xff
|
||||
)
|
||||
|
||||
// Table 28
|
||||
const (
|
||||
DW_ACCESS_public = 0x01
|
||||
DW_ACCESS_protected = 0x02
|
||||
DW_ACCESS_private = 0x03
|
||||
)
|
||||
|
||||
// Table 29
|
||||
const (
|
||||
DW_VIS_local = 0x01
|
||||
DW_VIS_exported = 0x02
|
||||
DW_VIS_qualified = 0x03
|
||||
)
|
||||
|
||||
// Table 30
|
||||
const (
|
||||
DW_VIRTUALITY_none = 0x00
|
||||
DW_VIRTUALITY_virtual = 0x01
|
||||
DW_VIRTUALITY_pure_virtual = 0x02
|
||||
)
|
||||
|
||||
// Table 31
|
||||
const (
|
||||
DW_LANG_C89 = 0x0001
|
||||
DW_LANG_C = 0x0002
|
||||
DW_LANG_Ada83 = 0x0003
|
||||
DW_LANG_C_plus_plus = 0x0004
|
||||
DW_LANG_Cobol74 = 0x0005
|
||||
DW_LANG_Cobol85 = 0x0006
|
||||
DW_LANG_Fortran77 = 0x0007
|
||||
DW_LANG_Fortran90 = 0x0008
|
||||
DW_LANG_Pascal83 = 0x0009
|
||||
DW_LANG_Modula2 = 0x000a
|
||||
// Dwarf3
|
||||
DW_LANG_Java = 0x000b
|
||||
DW_LANG_C99 = 0x000c
|
||||
DW_LANG_Ada95 = 0x000d
|
||||
DW_LANG_Fortran95 = 0x000e
|
||||
DW_LANG_PLI = 0x000f
|
||||
DW_LANG_ObjC = 0x0010
|
||||
DW_LANG_ObjC_plus_plus = 0x0011
|
||||
DW_LANG_UPC = 0x0012
|
||||
DW_LANG_D = 0x0013
|
||||
// Dwarf4
|
||||
DW_LANG_Python = 0x0014
|
||||
// Dwarf5
|
||||
DW_LANG_Go = 0x0016
|
||||
|
||||
DW_LANG_lo_user = 0x8000
|
||||
DW_LANG_hi_user = 0xffff
|
||||
)
|
||||
|
||||
// Table 32
|
||||
const (
|
||||
DW_ID_case_sensitive = 0x00
|
||||
DW_ID_up_case = 0x01
|
||||
DW_ID_down_case = 0x02
|
||||
DW_ID_case_insensitive = 0x03
|
||||
)
|
||||
|
||||
// Table 33
|
||||
const (
|
||||
DW_CC_normal = 0x01
|
||||
DW_CC_program = 0x02
|
||||
DW_CC_nocall = 0x03
|
||||
DW_CC_lo_user = 0x40
|
||||
DW_CC_hi_user = 0xff
|
||||
)
|
||||
|
||||
// Table 34
|
||||
const (
|
||||
DW_INL_not_inlined = 0x00
|
||||
DW_INL_inlined = 0x01
|
||||
DW_INL_declared_not_inlined = 0x02
|
||||
DW_INL_declared_inlined = 0x03
|
||||
)
|
||||
|
||||
// Table 35
|
||||
const (
|
||||
DW_ORD_row_major = 0x00
|
||||
DW_ORD_col_major = 0x01
|
||||
)
|
||||
|
||||
// Table 36
|
||||
const (
|
||||
DW_DSC_label = 0x00
|
||||
DW_DSC_range = 0x01
|
||||
)
|
||||
|
||||
// Table 37
|
||||
const (
|
||||
DW_LNS_copy = 0x01
|
||||
DW_LNS_advance_pc = 0x02
|
||||
DW_LNS_advance_line = 0x03
|
||||
DW_LNS_set_file = 0x04
|
||||
DW_LNS_set_column = 0x05
|
||||
DW_LNS_negate_stmt = 0x06
|
||||
DW_LNS_set_basic_block = 0x07
|
||||
DW_LNS_const_add_pc = 0x08
|
||||
DW_LNS_fixed_advance_pc = 0x09
|
||||
// Dwarf3
|
||||
DW_LNS_set_prologue_end = 0x0a
|
||||
DW_LNS_set_epilogue_begin = 0x0b
|
||||
DW_LNS_set_isa = 0x0c
|
||||
)
|
||||
|
||||
// Table 38
|
||||
const (
|
||||
DW_LNE_end_sequence = 0x01
|
||||
DW_LNE_set_address = 0x02
|
||||
DW_LNE_define_file = 0x03
|
||||
DW_LNE_lo_user = 0x80
|
||||
DW_LNE_hi_user = 0xff
|
||||
)
|
||||
|
||||
// Table 39
|
||||
const (
|
||||
DW_MACINFO_define = 0x01
|
||||
DW_MACINFO_undef = 0x02
|
||||
DW_MACINFO_start_file = 0x03
|
||||
DW_MACINFO_end_file = 0x04
|
||||
DW_MACINFO_vendor_ext = 0xff
|
||||
)
|
||||
|
||||
// Table 40.
|
||||
const (
|
||||
// operand,...
|
||||
DW_CFA_nop = 0x00
|
||||
DW_CFA_set_loc = 0x01 // address
|
||||
DW_CFA_advance_loc1 = 0x02 // 1-byte delta
|
||||
DW_CFA_advance_loc2 = 0x03 // 2-byte delta
|
||||
DW_CFA_advance_loc4 = 0x04 // 4-byte delta
|
||||
DW_CFA_offset_extended = 0x05 // ULEB128 register, ULEB128 offset
|
||||
DW_CFA_restore_extended = 0x06 // ULEB128 register
|
||||
DW_CFA_undefined = 0x07 // ULEB128 register
|
||||
DW_CFA_same_value = 0x08 // ULEB128 register
|
||||
DW_CFA_register = 0x09 // ULEB128 register, ULEB128 register
|
||||
DW_CFA_remember_state = 0x0a
|
||||
DW_CFA_restore_state = 0x0b
|
||||
|
||||
DW_CFA_def_cfa = 0x0c // ULEB128 register, ULEB128 offset
|
||||
DW_CFA_def_cfa_register = 0x0d // ULEB128 register
|
||||
DW_CFA_def_cfa_offset = 0x0e // ULEB128 offset
|
||||
DW_CFA_def_cfa_expression = 0x0f // BLOCK
|
||||
DW_CFA_expression = 0x10 // ULEB128 register, BLOCK
|
||||
DW_CFA_offset_extended_sf = 0x11 // ULEB128 register, SLEB128 offset
|
||||
DW_CFA_def_cfa_sf = 0x12 // ULEB128 register, SLEB128 offset
|
||||
DW_CFA_def_cfa_offset_sf = 0x13 // SLEB128 offset
|
||||
DW_CFA_val_offset = 0x14 // ULEB128, ULEB128
|
||||
DW_CFA_val_offset_sf = 0x15 // ULEB128, SLEB128
|
||||
DW_CFA_val_expression = 0x16 // ULEB128, BLOCK
|
||||
|
||||
DW_CFA_lo_user = 0x1c
|
||||
DW_CFA_hi_user = 0x3f
|
||||
|
||||
// Opcodes that take an addend operand.
|
||||
DW_CFA_advance_loc = 0x1 << 6 // +delta
|
||||
DW_CFA_offset = 0x2 << 6 // +register (ULEB128 offset)
|
||||
DW_CFA_restore = 0x3 << 6 // +register
|
||||
)
|
|
@ -0,0 +1,714 @@
|
|||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package goobj implements reading of Go object files and archives.
|
||||
//
|
||||
// TODO(rsc): Decide where this package should live. (golang.org/issue/6932)
|
||||
// TODO(rsc): Decide the appropriate integer types for various fields.
|
||||
// TODO(rsc): Write tests. (File format still up in the air a little.)
|
||||
package goobj
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/google/gops/internal/obj"
|
||||
)
|
||||
|
||||
// A SymKind describes the kind of memory represented by a symbol.
|
||||
type SymKind int
|
||||
|
||||
// This list is taken from include/link.h.
|
||||
|
||||
// Defined SymKind values.
|
||||
// TODO(rsc): Give idiomatic Go names.
|
||||
// TODO(rsc): Reduce the number of symbol types in the object files.
|
||||
const (
|
||||
// readonly, executable
|
||||
STEXT = SymKind(obj.STEXT)
|
||||
SELFRXSECT = SymKind(obj.SELFRXSECT)
|
||||
|
||||
// readonly, non-executable
|
||||
STYPE = SymKind(obj.STYPE)
|
||||
SSTRING = SymKind(obj.SSTRING)
|
||||
SGOSTRING = SymKind(obj.SGOSTRING)
|
||||
SGOFUNC = SymKind(obj.SGOFUNC)
|
||||
SRODATA = SymKind(obj.SRODATA)
|
||||
SFUNCTAB = SymKind(obj.SFUNCTAB)
|
||||
STYPELINK = SymKind(obj.STYPELINK)
|
||||
SITABLINK = SymKind(obj.SITABLINK)
|
||||
SSYMTAB = SymKind(obj.SSYMTAB) // TODO: move to unmapped section
|
||||
SPCLNTAB = SymKind(obj.SPCLNTAB)
|
||||
SELFROSECT = SymKind(obj.SELFROSECT)
|
||||
|
||||
// writable, non-executable
|
||||
SMACHOPLT = SymKind(obj.SMACHOPLT)
|
||||
SELFSECT = SymKind(obj.SELFSECT)
|
||||
SMACHO = SymKind(obj.SMACHO) // Mach-O __nl_symbol_ptr
|
||||
SMACHOGOT = SymKind(obj.SMACHOGOT)
|
||||
SWINDOWS = SymKind(obj.SWINDOWS)
|
||||
SELFGOT = SymKind(obj.SELFGOT)
|
||||
SNOPTRDATA = SymKind(obj.SNOPTRDATA)
|
||||
SINITARR = SymKind(obj.SINITARR)
|
||||
SDATA = SymKind(obj.SDATA)
|
||||
SBSS = SymKind(obj.SBSS)
|
||||
SNOPTRBSS = SymKind(obj.SNOPTRBSS)
|
||||
STLSBSS = SymKind(obj.STLSBSS)
|
||||
|
||||
// not mapped
|
||||
SXREF = SymKind(obj.SXREF)
|
||||
SMACHOSYMSTR = SymKind(obj.SMACHOSYMSTR)
|
||||
SMACHOSYMTAB = SymKind(obj.SMACHOSYMTAB)
|
||||
SMACHOINDIRECTPLT = SymKind(obj.SMACHOINDIRECTPLT)
|
||||
SMACHOINDIRECTGOT = SymKind(obj.SMACHOINDIRECTGOT)
|
||||
SFILE = SymKind(obj.SFILE)
|
||||
SFILEPATH = SymKind(obj.SFILEPATH)
|
||||
SCONST = SymKind(obj.SCONST)
|
||||
SDYNIMPORT = SymKind(obj.SDYNIMPORT)
|
||||
SHOSTOBJ = SymKind(obj.SHOSTOBJ)
|
||||
)
|
||||
|
||||
var symKindStrings = []string{
|
||||
SBSS: "SBSS",
|
||||
SCONST: "SCONST",
|
||||
SDATA: "SDATA",
|
||||
SDYNIMPORT: "SDYNIMPORT",
|
||||
SELFROSECT: "SELFROSECT",
|
||||
SELFRXSECT: "SELFRXSECT",
|
||||
SELFSECT: "SELFSECT",
|
||||
SFILE: "SFILE",
|
||||
SFILEPATH: "SFILEPATH",
|
||||
SFUNCTAB: "SFUNCTAB",
|
||||
SGOFUNC: "SGOFUNC",
|
||||
SGOSTRING: "SGOSTRING",
|
||||
SHOSTOBJ: "SHOSTOBJ",
|
||||
SINITARR: "SINITARR",
|
||||
SMACHO: "SMACHO",
|
||||
SMACHOGOT: "SMACHOGOT",
|
||||
SMACHOINDIRECTGOT: "SMACHOINDIRECTGOT",
|
||||
SMACHOINDIRECTPLT: "SMACHOINDIRECTPLT",
|
||||
SMACHOPLT: "SMACHOPLT",
|
||||
SMACHOSYMSTR: "SMACHOSYMSTR",
|
||||
SMACHOSYMTAB: "SMACHOSYMTAB",
|
||||
SNOPTRBSS: "SNOPTRBSS",
|
||||
SNOPTRDATA: "SNOPTRDATA",
|
||||
SPCLNTAB: "SPCLNTAB",
|
||||
SRODATA: "SRODATA",
|
||||
SSTRING: "SSTRING",
|
||||
SSYMTAB: "SSYMTAB",
|
||||
STEXT: "STEXT",
|
||||
STLSBSS: "STLSBSS",
|
||||
STYPE: "STYPE",
|
||||
STYPELINK: "STYPELINK",
|
||||
SITABLINK: "SITABLINK",
|
||||
SWINDOWS: "SWINDOWS",
|
||||
SXREF: "SXREF",
|
||||
}
|
||||
|
||||
func (k SymKind) String() string {
|
||||
if k < 0 || int(k) >= len(symKindStrings) {
|
||||
return fmt.Sprintf("SymKind(%d)", k)
|
||||
}
|
||||
return symKindStrings[k]
|
||||
}
|
||||
|
||||
// A Sym is a named symbol in an object file.
|
||||
type Sym struct {
|
||||
SymID // symbol identifier (name and version)
|
||||
Kind SymKind // kind of symbol
|
||||
DupOK bool // are duplicate definitions okay?
|
||||
Size int // size of corresponding data
|
||||
Type SymID // symbol for Go type information
|
||||
Data Data // memory image of symbol
|
||||
Reloc []Reloc // relocations to apply to Data
|
||||
Func *Func // additional data for functions
|
||||
}
|
||||
|
||||
// A SymID - the combination of Name and Version - uniquely identifies
|
||||
// a symbol within a package.
|
||||
type SymID struct {
|
||||
// Name is the name of a symbol.
|
||||
Name string
|
||||
|
||||
// Version is zero for symbols with global visibility.
|
||||
// Symbols with only file visibility (such as file-level static
|
||||
// declarations in C) have a non-zero version distinguishing
|
||||
// a symbol in one file from a symbol of the same name
|
||||
// in another file
|
||||
Version int
|
||||
}
|
||||
|
||||
func (s SymID) String() string {
|
||||
if s.Version == 0 {
|
||||
return s.Name
|
||||
}
|
||||
return fmt.Sprintf("%s<%d>", s.Name, s.Version)
|
||||
}
|
||||
|
||||
// A Data is a reference to data stored in an object file.
|
||||
// It records the offset and size of the data, so that a client can
|
||||
// read the data only if necessary.
|
||||
type Data struct {
|
||||
Offset int64
|
||||
Size int64
|
||||
}
|
||||
|
||||
// A Reloc describes a relocation applied to a memory image to refer
|
||||
// to an address within a particular symbol.
|
||||
type Reloc struct {
|
||||
// The bytes at [Offset, Offset+Size) within the containing Sym
|
||||
// should be updated to refer to the address Add bytes after the start
|
||||
// of the symbol Sym.
|
||||
Offset int
|
||||
Size int
|
||||
Sym SymID
|
||||
Add int
|
||||
|
||||
// The Type records the form of address expected in the bytes
|
||||
// described by the previous fields: absolute, PC-relative, and so on.
|
||||
// TODO(rsc): The interpretation of Type is not exposed by this package.
|
||||
Type obj.RelocType
|
||||
}
|
||||
|
||||
// A Var describes a variable in a function stack frame: a declared
|
||||
// local variable, an input argument, or an output result.
|
||||
type Var struct {
|
||||
// The combination of Name, Kind, and Offset uniquely
|
||||
// identifies a variable in a function stack frame.
|
||||
// Using fewer of these - in particular, using only Name - does not.
|
||||
Name string // Name of variable.
|
||||
Kind int // TODO(rsc): Define meaning.
|
||||
Offset int // Frame offset. TODO(rsc): Define meaning.
|
||||
|
||||
Type SymID // Go type for variable.
|
||||
}
|
||||
|
||||
// Func contains additional per-symbol information specific to functions.
|
||||
type Func struct {
|
||||
Args int // size in bytes of argument frame: inputs and outputs
|
||||
Frame int // size in bytes of local variable frame
|
||||
Leaf bool // function omits save of link register (ARM)
|
||||
NoSplit bool // function omits stack split prologue
|
||||
Var []Var // detail about local variables
|
||||
PCSP Data // PC → SP offset map
|
||||
PCFile Data // PC → file number map (index into File)
|
||||
PCLine Data // PC → line number map
|
||||
PCData []Data // PC → runtime support data map
|
||||
FuncData []FuncData // non-PC-specific runtime support data
|
||||
File []string // paths indexed by PCFile
|
||||
}
|
||||
|
||||
// TODO: Add PCData []byte and PCDataIter (similar to liblink).
|
||||
|
||||
// A FuncData is a single function-specific data value.
|
||||
type FuncData struct {
|
||||
Sym SymID // symbol holding data
|
||||
Offset int64 // offset into symbol for funcdata pointer
|
||||
}
|
||||
|
||||
// A Package is a parsed Go object file or archive defining a Go package.
|
||||
type Package struct {
|
||||
ImportPath string // import path denoting this package
|
||||
Imports []string // packages imported by this package
|
||||
SymRefs []SymID // list of symbol names and versions referred to by this pack
|
||||
Syms []*Sym // symbols defined by this package
|
||||
MaxVersion int // maximum Version in any SymID in Syms
|
||||
Arch string // architecture
|
||||
}
|
||||
|
||||
var (
|
||||
archiveHeader = []byte("!<arch>\n")
|
||||
archiveMagic = []byte("`\n")
|
||||
goobjHeader = []byte("go objec") // truncated to size of archiveHeader
|
||||
|
||||
errCorruptArchive = errors.New("corrupt archive")
|
||||
errTruncatedArchive = errors.New("truncated archive")
|
||||
errCorruptObject = errors.New("corrupt object file")
|
||||
errNotObject = errors.New("unrecognized object file format")
|
||||
)
|
||||
|
||||
// An objReader is an object file reader.
|
||||
type objReader struct {
|
||||
p *Package
|
||||
b *bufio.Reader
|
||||
f io.ReadSeeker
|
||||
err error
|
||||
offset int64
|
||||
dataOffset int64
|
||||
limit int64
|
||||
tmp [256]byte
|
||||
pkgprefix string
|
||||
}
|
||||
|
||||
// importPathToPrefix returns the prefix that will be used in the
|
||||
// final symbol table for the given import path.
|
||||
// We escape '%', '"', all control characters and non-ASCII bytes,
|
||||
// and any '.' after the final slash.
|
||||
//
|
||||
// See ../../../cmd/ld/lib.c:/^pathtoprefix and
|
||||
// ../../../cmd/gc/subr.c:/^pathtoprefix.
|
||||
func importPathToPrefix(s string) string {
|
||||
// find index of last slash, if any, or else -1.
|
||||
// used for determining whether an index is after the last slash.
|
||||
slash := strings.LastIndex(s, "/")
|
||||
|
||||
// check for chars that need escaping
|
||||
n := 0
|
||||
for r := 0; r < len(s); r++ {
|
||||
if c := s[r]; c <= ' ' || (c == '.' && r > slash) || c == '%' || c == '"' || c >= 0x7F {
|
||||
n++
|
||||
}
|
||||
}
|
||||
|
||||
// quick exit
|
||||
if n == 0 {
|
||||
return s
|
||||
}
|
||||
|
||||
// escape
|
||||
const hex = "0123456789abcdef"
|
||||
p := make([]byte, 0, len(s)+2*n)
|
||||
for r := 0; r < len(s); r++ {
|
||||
if c := s[r]; c <= ' ' || (c == '.' && r > slash) || c == '%' || c == '"' || c >= 0x7F {
|
||||
p = append(p, '%', hex[c>>4], hex[c&0xF])
|
||||
} else {
|
||||
p = append(p, c)
|
||||
}
|
||||
}
|
||||
|
||||
return string(p)
|
||||
}
|
||||
|
||||
// init initializes r to read package p from f.
|
||||
func (r *objReader) init(f io.ReadSeeker, p *Package) {
|
||||
r.f = f
|
||||
r.p = p
|
||||
r.offset, _ = f.Seek(0, io.SeekCurrent)
|
||||
r.limit, _ = f.Seek(0, io.SeekEnd)
|
||||
f.Seek(r.offset, io.SeekStart)
|
||||
r.b = bufio.NewReader(f)
|
||||
r.pkgprefix = importPathToPrefix(p.ImportPath) + "."
|
||||
}
|
||||
|
||||
// error records that an error occurred.
|
||||
// It returns only the first error, so that an error
|
||||
// caused by an earlier error does not discard information
|
||||
// about the earlier error.
|
||||
func (r *objReader) error(err error) error {
|
||||
if r.err == nil {
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
r.err = err
|
||||
}
|
||||
// panic("corrupt") // useful for debugging
|
||||
return r.err
|
||||
}
|
||||
|
||||
// readByte reads and returns a byte from the input file.
|
||||
// On I/O error or EOF, it records the error but returns byte 0.
|
||||
// A sequence of 0 bytes will eventually terminate any
|
||||
// parsing state in the object file. In particular, it ends the
|
||||
// reading of a varint.
|
||||
func (r *objReader) readByte() byte {
|
||||
if r.err != nil {
|
||||
return 0
|
||||
}
|
||||
if r.offset >= r.limit {
|
||||
r.error(io.ErrUnexpectedEOF)
|
||||
return 0
|
||||
}
|
||||
b, err := r.b.ReadByte()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
r.error(err)
|
||||
b = 0
|
||||
} else {
|
||||
r.offset++
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// read reads exactly len(b) bytes from the input file.
|
||||
// If an error occurs, read returns the error but also
|
||||
// records it, so it is safe for callers to ignore the result
|
||||
// as long as delaying the report is not a problem.
|
||||
func (r *objReader) readFull(b []byte) error {
|
||||
if r.err != nil {
|
||||
return r.err
|
||||
}
|
||||
if r.offset+int64(len(b)) > r.limit {
|
||||
return r.error(io.ErrUnexpectedEOF)
|
||||
}
|
||||
n, err := io.ReadFull(r.b, b)
|
||||
r.offset += int64(n)
|
||||
if err != nil {
|
||||
return r.error(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// readInt reads a zigzag varint from the input file.
|
||||
func (r *objReader) readInt() int {
|
||||
var u uint64
|
||||
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
r.error(errCorruptObject)
|
||||
return 0
|
||||
}
|
||||
c := r.readByte()
|
||||
u |= uint64(c&0x7F) << shift
|
||||
if c&0x80 == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
v := int64(u>>1) ^ (int64(u) << 63 >> 63)
|
||||
if int64(int(v)) != v {
|
||||
r.error(errCorruptObject) // TODO
|
||||
return 0
|
||||
}
|
||||
return int(v)
|
||||
}
|
||||
|
||||
// readString reads a length-delimited string from the input file.
|
||||
func (r *objReader) readString() string {
|
||||
n := r.readInt()
|
||||
buf := make([]byte, n)
|
||||
r.readFull(buf)
|
||||
return string(buf)
|
||||
}
|
||||
|
||||
// readSymID reads a SymID from the input file.
|
||||
func (r *objReader) readSymID() SymID {
|
||||
i := r.readInt()
|
||||
return r.p.SymRefs[i]
|
||||
}
|
||||
|
||||
func (r *objReader) readRef() {
|
||||
name, vers := r.readString(), r.readInt()
|
||||
|
||||
// In a symbol name in an object file, "". denotes the
|
||||
// prefix for the package in which the object file has been found.
|
||||
// Expand it.
|
||||
name = strings.Replace(name, `"".`, r.pkgprefix, -1)
|
||||
|
||||
// An individual object file only records version 0 (extern) or 1 (static).
|
||||
// To make static symbols unique across all files being read, we
|
||||
// replace version 1 with the version corresponding to the current
|
||||
// file number. The number is incremented on each call to parseObject.
|
||||
if vers != 0 {
|
||||
vers = r.p.MaxVersion
|
||||
}
|
||||
r.p.SymRefs = append(r.p.SymRefs, SymID{name, vers})
|
||||
}
|
||||
|
||||
// readData reads a data reference from the input file.
|
||||
func (r *objReader) readData() Data {
|
||||
n := r.readInt()
|
||||
d := Data{Offset: r.dataOffset, Size: int64(n)}
|
||||
r.dataOffset += int64(n)
|
||||
return d
|
||||
}
|
||||
|
||||
// skip skips n bytes in the input.
|
||||
func (r *objReader) skip(n int64) {
|
||||
if n < 0 {
|
||||
r.error(fmt.Errorf("debug/goobj: internal error: misuse of skip"))
|
||||
}
|
||||
if n < int64(len(r.tmp)) {
|
||||
// Since the data is so small, a just reading from the buffered
|
||||
// reader is better than flushing the buffer and seeking.
|
||||
r.readFull(r.tmp[:n])
|
||||
} else if n <= int64(r.b.Buffered()) {
|
||||
// Even though the data is not small, it has already been read.
|
||||
// Advance the buffer instead of seeking.
|
||||
for n > int64(len(r.tmp)) {
|
||||
r.readFull(r.tmp[:])
|
||||
n -= int64(len(r.tmp))
|
||||
}
|
||||
r.readFull(r.tmp[:n])
|
||||
} else {
|
||||
// Seek, giving up buffered data.
|
||||
_, err := r.f.Seek(r.offset+n, io.SeekStart)
|
||||
if err != nil {
|
||||
r.error(err)
|
||||
}
|
||||
r.offset += n
|
||||
r.b.Reset(r.f)
|
||||
}
|
||||
}
|
||||
|
||||
// Parse parses an object file or archive from r,
|
||||
// assuming that its import path is pkgpath.
|
||||
func Parse(r io.ReadSeeker, pkgpath string) (*Package, error) {
|
||||
if pkgpath == "" {
|
||||
pkgpath = `""`
|
||||
}
|
||||
p := new(Package)
|
||||
p.ImportPath = pkgpath
|
||||
|
||||
var rd objReader
|
||||
rd.init(r, p)
|
||||
err := rd.readFull(rd.tmp[:8])
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch {
|
||||
default:
|
||||
return nil, errNotObject
|
||||
|
||||
case bytes.Equal(rd.tmp[:8], archiveHeader):
|
||||
if err := rd.parseArchive(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case bytes.Equal(rd.tmp[:8], goobjHeader):
|
||||
if err := rd.parseObject(goobjHeader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// trimSpace removes trailing spaces from b and returns the corresponding string.
|
||||
// This effectively parses the form used in archive headers.
|
||||
func trimSpace(b []byte) string {
|
||||
return string(bytes.TrimRight(b, " "))
|
||||
}
|
||||
|
||||
// parseArchive parses a Unix archive of Go object files.
|
||||
// TODO(rsc): Need to skip non-Go object files.
|
||||
// TODO(rsc): Maybe record table of contents in r.p so that
|
||||
// linker can avoid having code to parse archives too.
|
||||
func (r *objReader) parseArchive() error {
|
||||
for r.offset < r.limit {
|
||||
if err := r.readFull(r.tmp[:60]); err != nil {
|
||||
return err
|
||||
}
|
||||
data := r.tmp[:60]
|
||||
|
||||
// Each file is preceded by this text header (slice indices in first column):
|
||||
// 0:16 name
|
||||
// 16:28 date
|
||||
// 28:34 uid
|
||||
// 34:40 gid
|
||||
// 40:48 mode
|
||||
// 48:58 size
|
||||
// 58:60 magic - `\n
|
||||
// We only care about name, size, and magic.
|
||||
// The fields are space-padded on the right.
|
||||
// The size is in decimal.
|
||||
// The file data - size bytes - follows the header.
|
||||
// Headers are 2-byte aligned, so if size is odd, an extra padding
|
||||
// byte sits between the file data and the next header.
|
||||
// The file data that follows is padded to an even number of bytes:
|
||||
// if size is odd, an extra padding byte is inserted betw the next header.
|
||||
if len(data) < 60 {
|
||||
return errTruncatedArchive
|
||||
}
|
||||
if !bytes.Equal(data[58:60], archiveMagic) {
|
||||
return errCorruptArchive
|
||||
}
|
||||
name := trimSpace(data[0:16])
|
||||
size, err := strconv.ParseInt(trimSpace(data[48:58]), 10, 64)
|
||||
if err != nil {
|
||||
return errCorruptArchive
|
||||
}
|
||||
data = data[60:]
|
||||
fsize := size + size&1
|
||||
if fsize < 0 || fsize < size {
|
||||
return errCorruptArchive
|
||||
}
|
||||
switch name {
|
||||
case "__.PKGDEF":
|
||||
r.skip(size)
|
||||
default:
|
||||
oldLimit := r.limit
|
||||
r.limit = r.offset + size
|
||||
if err := r.parseObject(nil); err != nil {
|
||||
return fmt.Errorf("parsing archive member %q: %v", name, err)
|
||||
}
|
||||
r.skip(r.limit - r.offset)
|
||||
r.limit = oldLimit
|
||||
}
|
||||
if size&1 != 0 {
|
||||
r.skip(1)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseObject parses a single Go object file.
|
||||
// The prefix is the bytes already read from the file,
|
||||
// typically in order to detect that this is an object file.
|
||||
// The object file consists of a textual header ending in "\n!\n"
|
||||
// and then the part we want to parse begins.
|
||||
// The format of that part is defined in a comment at the top
|
||||
// of src/liblink/objfile.c.
|
||||
func (r *objReader) parseObject(prefix []byte) error {
|
||||
r.p.MaxVersion++
|
||||
h := make([]byte, 0, 256)
|
||||
h = append(h, prefix...)
|
||||
var c1, c2, c3 byte
|
||||
for {
|
||||
c1, c2, c3 = c2, c3, r.readByte()
|
||||
h = append(h, c3)
|
||||
// The new export format can contain 0 bytes.
|
||||
// Don't consider them errors, only look for r.err != nil.
|
||||
if r.err != nil {
|
||||
return errCorruptObject
|
||||
}
|
||||
if c1 == '\n' && c2 == '!' && c3 == '\n' {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
hs := strings.Fields(string(h))
|
||||
if len(hs) >= 4 {
|
||||
r.p.Arch = hs[3]
|
||||
}
|
||||
// TODO: extract OS + build ID if/when we need it
|
||||
|
||||
r.readFull(r.tmp[:8])
|
||||
if !bytes.Equal(r.tmp[:8], []byte("\x00\x00go17ld")) {
|
||||
return r.error(errCorruptObject)
|
||||
}
|
||||
|
||||
b := r.readByte()
|
||||
if b != 1 {
|
||||
return r.error(errCorruptObject)
|
||||
}
|
||||
|
||||
// Direct package dependencies.
|
||||
for {
|
||||
s := r.readString()
|
||||
if s == "" {
|
||||
break
|
||||
}
|
||||
r.p.Imports = append(r.p.Imports, s)
|
||||
}
|
||||
|
||||
r.p.SymRefs = []SymID{{"", 0}}
|
||||
for {
|
||||
if b := r.readByte(); b != 0xfe {
|
||||
if b != 0xff {
|
||||
return r.error(errCorruptObject)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
r.readRef()
|
||||
}
|
||||
|
||||
dataLength := r.readInt()
|
||||
r.readInt() // n relocations - ignore
|
||||
r.readInt() // n pcdata - ignore
|
||||
r.readInt() // n autom - ignore
|
||||
r.readInt() // n funcdata - ignore
|
||||
r.readInt() // n files - ignore
|
||||
|
||||
r.dataOffset = r.offset
|
||||
r.skip(int64(dataLength))
|
||||
|
||||
// Symbols.
|
||||
for {
|
||||
if b := r.readByte(); b != 0xfe {
|
||||
if b != 0xff {
|
||||
return r.error(errCorruptObject)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
typ := r.readInt()
|
||||
s := &Sym{SymID: r.readSymID()}
|
||||
r.p.Syms = append(r.p.Syms, s)
|
||||
s.Kind = SymKind(typ)
|
||||
flags := r.readInt()
|
||||
s.DupOK = flags&1 != 0
|
||||
s.Size = r.readInt()
|
||||
s.Type = r.readSymID()
|
||||
s.Data = r.readData()
|
||||
s.Reloc = make([]Reloc, r.readInt())
|
||||
for i := range s.Reloc {
|
||||
rel := &s.Reloc[i]
|
||||
rel.Offset = r.readInt()
|
||||
rel.Size = r.readInt()
|
||||
rel.Type = obj.RelocType(r.readInt())
|
||||
rel.Add = r.readInt()
|
||||
rel.Sym = r.readSymID()
|
||||
}
|
||||
|
||||
if s.Kind == STEXT {
|
||||
f := new(Func)
|
||||
s.Func = f
|
||||
f.Args = r.readInt()
|
||||
f.Frame = r.readInt()
|
||||
flags := r.readInt()
|
||||
f.Leaf = flags&1 != 0
|
||||
f.NoSplit = r.readInt() != 0
|
||||
f.Var = make([]Var, r.readInt())
|
||||
for i := range f.Var {
|
||||
v := &f.Var[i]
|
||||
v.Name = r.readSymID().Name
|
||||
v.Offset = r.readInt()
|
||||
v.Kind = r.readInt()
|
||||
v.Type = r.readSymID()
|
||||
}
|
||||
|
||||
f.PCSP = r.readData()
|
||||
f.PCFile = r.readData()
|
||||
f.PCLine = r.readData()
|
||||
f.PCData = make([]Data, r.readInt())
|
||||
for i := range f.PCData {
|
||||
f.PCData[i] = r.readData()
|
||||
}
|
||||
f.FuncData = make([]FuncData, r.readInt())
|
||||
for i := range f.FuncData {
|
||||
f.FuncData[i].Sym = r.readSymID()
|
||||
}
|
||||
for i := range f.FuncData {
|
||||
f.FuncData[i].Offset = int64(r.readInt()) // TODO
|
||||
}
|
||||
f.File = make([]string, r.readInt())
|
||||
for i := range f.File {
|
||||
f.File[i] = r.readSymID().Name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r.readFull(r.tmp[:7])
|
||||
if !bytes.Equal(r.tmp[:7], []byte("\xffgo17ld")) {
|
||||
return r.error(errCorruptObject)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Reloc) String(insnOffset uint64) string {
|
||||
delta := r.Offset - int(insnOffset)
|
||||
s := fmt.Sprintf("[%d:%d]%s", delta, delta+r.Size, r.Type)
|
||||
if r.Sym.Name != "" {
|
||||
if r.Add != 0 {
|
||||
return fmt.Sprintf("%s:%s+%d", s, r.Sym.Name, r.Add)
|
||||
}
|
||||
return fmt.Sprintf("%s:%s", s, r.Sym.Name)
|
||||
}
|
||||
if r.Add != 0 {
|
||||
return fmt.Sprintf("%s:%d", s, r.Add)
|
||||
}
|
||||
return s
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package goobj
|
||||
|
||||
import "testing"
|
||||
|
||||
var importPathToPrefixTests = []struct {
|
||||
in string
|
||||
out string
|
||||
}{
|
||||
{"runtime", "runtime"},
|
||||
{"sync/atomic", "sync/atomic"},
|
||||
{"golang.org/x/tools/godoc", "golang.org/x/tools/godoc"},
|
||||
{"foo.bar/baz.quux", "foo.bar/baz%2equux"},
|
||||
{"", ""},
|
||||
{"%foo%bar", "%25foo%25bar"},
|
||||
{"\x01\x00\x7F☺", "%01%00%7f%e2%98%ba"},
|
||||
}
|
||||
|
||||
func TestImportPathToPrefix(t *testing.T) {
|
||||
for _, tt := range importPathToPrefixTests {
|
||||
if out := importPathToPrefix(tt.in); out != tt.out {
|
||||
t.Errorf("importPathToPrefix(%q) = %q, want %q", tt.in, out, tt.out)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// Code generated by "stringer -type AddrType cmd/internal/obj"; DO NOT EDIT
|
||||
|
||||
package obj
|
||||
|
||||
import "fmt"
|
||||
|
||||
const (
|
||||
_AddrType_name_0 = "TYPE_NONE"
|
||||
_AddrType_name_1 = "TYPE_BRANCHTYPE_TEXTSIZETYPE_MEMTYPE_CONSTTYPE_FCONSTTYPE_SCONSTTYPE_REGTYPE_ADDRTYPE_SHIFTTYPE_REGREGTYPE_REGREG2TYPE_INDIRTYPE_REGLIST"
|
||||
)
|
||||
|
||||
var (
|
||||
_AddrType_index_0 = [...]uint8{0, 9}
|
||||
_AddrType_index_1 = [...]uint8{0, 11, 24, 32, 42, 53, 64, 72, 81, 91, 102, 114, 124, 136}
|
||||
)
|
||||
|
||||
func (i AddrType) String() string {
|
||||
switch {
|
||||
case i == 0:
|
||||
return _AddrType_name_0
|
||||
case 6 <= i && i <= 18:
|
||||
i -= 6
|
||||
return _AddrType_name_1[_AddrType_index_1[i]:_AddrType_index_1[i+1]]
|
||||
default:
|
||||
return fmt.Sprintf("AddrType(%d)", i)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,338 @@
|
|||
// Inferno utils/5c/5.out.h
|
||||
// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5c/5.out.h
|
||||
//
|
||||
// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
|
||||
// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
|
||||
// Portions Copyright © 1997-1999 Vita Nuova Limited
|
||||
// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
|
||||
// Portions Copyright © 2004,2006 Bruce Ellis
|
||||
// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
|
||||
// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
|
||||
// Portions Copyright © 2009 The Go Authors. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package arm
|
||||
|
||||
import "github.com/google/gops/internal/obj"
|
||||
|
||||
//go:generate go run ../stringer.go -i $GOFILE -o anames.go -p arm
|
||||
|
||||
const (
|
||||
NSNAME = 8
|
||||
NSYM = 50
|
||||
NREG = 16
|
||||
)
|
||||
|
||||
/* -1 disables use of REGARG */
|
||||
const (
|
||||
REGARG = -1
|
||||
)
|
||||
|
||||
const (
|
||||
REG_R0 = obj.RBaseARM + iota // must be 16-aligned
|
||||
REG_R1
|
||||
REG_R2
|
||||
REG_R3
|
||||
REG_R4
|
||||
REG_R5
|
||||
REG_R6
|
||||
REG_R7
|
||||
REG_R8
|
||||
REG_R9
|
||||
REG_R10
|
||||
REG_R11
|
||||
REG_R12
|
||||
REG_R13
|
||||
REG_R14
|
||||
REG_R15
|
||||
|
||||
REG_F0 // must be 16-aligned
|
||||
REG_F1
|
||||
REG_F2
|
||||
REG_F3
|
||||
REG_F4
|
||||
REG_F5
|
||||
REG_F6
|
||||
REG_F7
|
||||
REG_F8
|
||||
REG_F9
|
||||
REG_F10
|
||||
REG_F11
|
||||
REG_F12
|
||||
REG_F13
|
||||
REG_F14
|
||||
REG_F15
|
||||
|
||||
REG_FPSR // must be 2-aligned
|
||||
REG_FPCR
|
||||
|
||||
REG_CPSR // must be 2-aligned
|
||||
REG_SPSR
|
||||
|
||||
MAXREG
|
||||
REGRET = REG_R0
|
||||
/* compiler allocates R1 up as temps */
|
||||
/* compiler allocates register variables R3 up */
|
||||
/* compiler allocates external registers R10 down */
|
||||
REGEXT = REG_R10
|
||||
/* these two registers are declared in runtime.h */
|
||||
REGG = REGEXT - 0
|
||||
REGM = REGEXT - 1
|
||||
|
||||
REGCTXT = REG_R7
|
||||
REGTMP = REG_R11
|
||||
REGSP = REG_R13
|
||||
REGLINK = REG_R14
|
||||
REGPC = REG_R15
|
||||
|
||||
NFREG = 16
|
||||
/* compiler allocates register variables F0 up */
|
||||
/* compiler allocates external registers F7 down */
|
||||
FREGRET = REG_F0
|
||||
FREGEXT = REG_F7
|
||||
FREGTMP = REG_F15
|
||||
)
|
||||
|
||||
const (
|
||||
C_NONE = iota
|
||||
C_REG
|
||||
C_REGREG
|
||||
C_REGREG2
|
||||
C_REGLIST
|
||||
C_SHIFT
|
||||
C_FREG
|
||||
C_PSR
|
||||
C_FCR
|
||||
|
||||
C_RCON /* 0xff rotated */
|
||||
C_NCON /* ~RCON */
|
||||
C_SCON /* 0xffff */
|
||||
C_LCON
|
||||
C_LCONADDR
|
||||
C_ZFCON
|
||||
C_SFCON
|
||||
C_LFCON
|
||||
|
||||
C_RACON
|
||||
C_LACON
|
||||
|
||||
C_SBRA
|
||||
C_LBRA
|
||||
|
||||
C_HAUTO /* halfword insn offset (-0xff to 0xff) */
|
||||
C_FAUTO /* float insn offset (0 to 0x3fc, word aligned) */
|
||||
C_HFAUTO /* both H and F */
|
||||
C_SAUTO /* -0xfff to 0xfff */
|
||||
C_LAUTO
|
||||
|
||||
C_HOREG
|
||||
C_FOREG
|
||||
C_HFOREG
|
||||
C_SOREG
|
||||
C_ROREG
|
||||
C_SROREG /* both nil and R */
|
||||
C_LOREG
|
||||
|
||||
C_PC
|
||||
C_SP
|
||||
C_HREG
|
||||
|
||||
C_ADDR /* reference to relocatable address */
|
||||
|
||||
// TLS "var" in local exec mode: will become a constant offset from
|
||||
// thread local base that is ultimately chosen by the program linker.
|
||||
C_TLS_LE
|
||||
|
||||
// TLS "var" in initial exec mode: will become a memory address (chosen
|
||||
// by the program linker) that the dynamic linker will fill with the
|
||||
// offset from the thread local base.
|
||||
C_TLS_IE
|
||||
|
||||
C_TEXTSIZE
|
||||
|
||||
C_GOK
|
||||
|
||||
C_NCLASS /* must be the last */
|
||||
)
|
||||
|
||||
const (
|
||||
AAND = obj.ABaseARM + obj.A_ARCHSPECIFIC + iota
|
||||
AEOR
|
||||
ASUB
|
||||
ARSB
|
||||
AADD
|
||||
AADC
|
||||
ASBC
|
||||
ARSC
|
||||
ATST
|
||||
ATEQ
|
||||
ACMP
|
||||
ACMN
|
||||
AORR
|
||||
ABIC
|
||||
|
||||
AMVN
|
||||
|
||||
/*
|
||||
* Do not reorder or fragment the conditional branch
|
||||
* opcodes, or the predication code will break
|
||||
*/
|
||||
ABEQ
|
||||
ABNE
|
||||
ABCS
|
||||
ABHS
|
||||
ABCC
|
||||
ABLO
|
||||
ABMI
|
||||
ABPL
|
||||
ABVS
|
||||
ABVC
|
||||
ABHI
|
||||
ABLS
|
||||
ABGE
|
||||
ABLT
|
||||
ABGT
|
||||
ABLE
|
||||
|
||||
AMOVWD
|
||||
AMOVWF
|
||||
AMOVDW
|
||||
AMOVFW
|
||||
AMOVFD
|
||||
AMOVDF
|
||||
AMOVF
|
||||
AMOVD
|
||||
|
||||
ACMPF
|
||||
ACMPD
|
||||
AADDF
|
||||
AADDD
|
||||
ASUBF
|
||||
ASUBD
|
||||
AMULF
|
||||
AMULD
|
||||
ADIVF
|
||||
ADIVD
|
||||
ASQRTF
|
||||
ASQRTD
|
||||
AABSF
|
||||
AABSD
|
||||
ANEGF
|
||||
ANEGD
|
||||
|
||||
ASRL
|
||||
ASRA
|
||||
ASLL
|
||||
AMULU
|
||||
ADIVU
|
||||
AMUL
|
||||
ADIV
|
||||
AMOD
|
||||
AMODU
|
||||
|
||||
AMOVB
|
||||
AMOVBS
|
||||
AMOVBU
|
||||
AMOVH
|
||||
AMOVHS
|
||||
AMOVHU
|
||||
AMOVW
|
||||
AMOVM
|
||||
ASWPBU
|
||||
ASWPW
|
||||
|
||||
ARFE
|
||||
ASWI
|
||||
AMULA
|
||||
|
||||
AWORD
|
||||
|
||||
AMULL
|
||||
AMULAL
|
||||
AMULLU
|
||||
AMULALU
|
||||
|
||||
ABX
|
||||
ABXRET
|
||||
ADWORD
|
||||
|
||||
ALDREX
|
||||
ASTREX
|
||||
ALDREXD
|
||||
ASTREXD
|
||||
|
||||
APLD
|
||||
|
||||
ACLZ
|
||||
|
||||
AMULWT
|
||||
AMULWB
|
||||
AMULAWT
|
||||
AMULAWB
|
||||
|
||||
ADATABUNDLE
|
||||
ADATABUNDLEEND
|
||||
|
||||
AMRC // MRC/MCR
|
||||
|
||||
ALAST
|
||||
|
||||
// aliases
|
||||
AB = obj.AJMP
|
||||
ABL = obj.ACALL
|
||||
)
|
||||
|
||||
/* scond byte */
|
||||
const (
|
||||
C_SCOND = (1 << 4) - 1
|
||||
C_SBIT = 1 << 4
|
||||
C_PBIT = 1 << 5
|
||||
C_WBIT = 1 << 6
|
||||
C_FBIT = 1 << 7 /* psr flags-only */
|
||||
C_UBIT = 1 << 7 /* up bit, unsigned bit */
|
||||
|
||||
// These constants are the ARM condition codes encodings,
|
||||
// XORed with 14 so that C_SCOND_NONE has value 0,
|
||||
// so that a zeroed Prog.scond means "always execute".
|
||||
C_SCOND_XOR = 14
|
||||
|
||||
C_SCOND_EQ = 0 ^ C_SCOND_XOR
|
||||
C_SCOND_NE = 1 ^ C_SCOND_XOR
|
||||
C_SCOND_HS = 2 ^ C_SCOND_XOR
|
||||
C_SCOND_LO = 3 ^ C_SCOND_XOR
|
||||
C_SCOND_MI = 4 ^ C_SCOND_XOR
|
||||
C_SCOND_PL = 5 ^ C_SCOND_XOR
|
||||
C_SCOND_VS = 6 ^ C_SCOND_XOR
|
||||
C_SCOND_VC = 7 ^ C_SCOND_XOR
|
||||
C_SCOND_HI = 8 ^ C_SCOND_XOR
|
||||
C_SCOND_LS = 9 ^ C_SCOND_XOR
|
||||
C_SCOND_GE = 10 ^ C_SCOND_XOR
|
||||
C_SCOND_LT = 11 ^ C_SCOND_XOR
|
||||
C_SCOND_GT = 12 ^ C_SCOND_XOR
|
||||
C_SCOND_LE = 13 ^ C_SCOND_XOR
|
||||
C_SCOND_NONE = 14 ^ C_SCOND_XOR
|
||||
C_SCOND_NV = 15 ^ C_SCOND_XOR
|
||||
|
||||
/* D_SHIFT type */
|
||||
SHIFT_LL = 0 << 5
|
||||
SHIFT_LR = 1 << 5
|
||||
SHIFT_AR = 2 << 5
|
||||
SHIFT_RR = 3 << 5
|
||||
)
|
|
@ -0,0 +1,108 @@
|
|||
// Generated by stringer -i a.out.go -o anames.go -p arm
|
||||
// Do not edit.
|
||||
|
||||
package arm
|
||||
|
||||
import "github.com/google/gops/internal/obj"
|
||||
|
||||
var Anames = []string{
|
||||
obj.A_ARCHSPECIFIC: "AND",
|
||||
"EOR",
|
||||
"SUB",
|
||||
"RSB",
|
||||
"ADD",
|
||||
"ADC",
|
||||
"SBC",
|
||||
"RSC",
|
||||
"TST",
|
||||
"TEQ",
|
||||
"CMP",
|
||||
"CMN",
|
||||
"ORR",
|
||||
"BIC",
|
||||
"MVN",
|
||||
"BEQ",
|
||||
"BNE",
|
||||
"BCS",
|
||||
"BHS",
|
||||
"BCC",
|
||||
"BLO",
|
||||
"BMI",
|
||||
"BPL",
|
||||
"BVS",
|
||||
"BVC",
|
||||
"BHI",
|
||||
"BLS",
|
||||
"BGE",
|
||||
"BLT",
|
||||
"BGT",
|
||||
"BLE",
|
||||
"MOVWD",
|
||||
"MOVWF",
|
||||
"MOVDW",
|
||||
"MOVFW",
|
||||
"MOVFD",
|
||||
"MOVDF",
|
||||
"MOVF",
|
||||
"MOVD",
|
||||
"CMPF",
|
||||
"CMPD",
|
||||
"ADDF",
|
||||
"ADDD",
|
||||
"SUBF",
|
||||
"SUBD",
|
||||
"MULF",
|
||||
"MULD",
|
||||
"DIVF",
|
||||
"DIVD",
|
||||
"SQRTF",
|
||||
"SQRTD",
|
||||
"ABSF",
|
||||
"ABSD",
|
||||
"NEGF",
|
||||
"NEGD",
|
||||
"SRL",
|
||||
"SRA",
|
||||
"SLL",
|
||||
"MULU",
|
||||
"DIVU",
|
||||
"MUL",
|
||||
"DIV",
|
||||
"MOD",
|
||||
"MODU",
|
||||
"MOVB",
|
||||
"MOVBS",
|
||||
"MOVBU",
|
||||
"MOVH",
|
||||
"MOVHS",
|
||||
"MOVHU",
|
||||
"MOVW",
|
||||
"MOVM",
|
||||
"SWPBU",
|
||||
"SWPW",
|
||||
"RFE",
|
||||
"SWI",
|
||||
"MULA",
|
||||
"WORD",
|
||||
"MULL",
|
||||
"MULAL",
|
||||
"MULLU",
|
||||
"MULALU",
|
||||
"BX",
|
||||
"BXRET",
|
||||
"DWORD",
|
||||
"LDREX",
|
||||
"STREX",
|
||||
"LDREXD",
|
||||
"STREXD",
|
||||
"PLD",
|
||||
"CLZ",
|
||||
"MULWT",
|
||||
"MULWB",
|
||||
"MULAWT",
|
||||
"MULAWB",
|
||||
"DATABUNDLE",
|
||||
"DATABUNDLEEND",
|
||||
"MRC",
|
||||
"LAST",
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
// 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.
|
||||
|
||||
package arm
|
||||
|
||||
var cnames5 = []string{
|
||||
"NONE",
|
||||
"REG",
|
||||
"REGREG",
|
||||
"REGREG2",
|
||||
"REGLIST",
|
||||
"SHIFT",
|
||||
"FREG",
|
||||
"PSR",
|
||||
"FCR",
|
||||
"RCON",
|
||||
"NCON",
|
||||
"SCON",
|
||||
"LCON",
|
||||
"LCONADDR",
|
||||
"ZFCON",
|
||||
"SFCON",
|
||||
"LFCON",
|
||||
"RACON",
|
||||
"LACON",
|
||||
"SBRA",
|
||||
"LBRA",
|
||||
"HAUTO",
|
||||
"FAUTO",
|
||||
"HFAUTO",
|
||||
"SAUTO",
|
||||
"LAUTO",
|
||||
"HOREG",
|
||||
"FOREG",
|
||||
"HFOREG",
|
||||
"SOREG",
|
||||
"ROREG",
|
||||
"SROREG",
|
||||
"LOREG",
|
||||
"PC",
|
||||
"SP",
|
||||
"HREG",
|
||||
"ADDR",
|
||||
"C_TLS_LE",
|
||||
"C_TLS_IE",
|
||||
"TEXTSIZE",
|
||||
"GOK",
|
||||
"NCLASS",
|
||||
"SCOND = (1<<4)-1",
|
||||
"SBIT = 1<<4",
|
||||
"PBIT = 1<<5",
|
||||
"WBIT = 1<<6",
|
||||
"FBIT = 1<<7",
|
||||
"UBIT = 1<<7",
|
||||
"SCOND_XOR = 14",
|
||||
"SCOND_EQ = 0 ^ C_SCOND_XOR",
|
||||
"SCOND_NE = 1 ^ C_SCOND_XOR",
|
||||
"SCOND_HS = 2 ^ C_SCOND_XOR",
|
||||
"SCOND_LO = 3 ^ C_SCOND_XOR",
|
||||
"SCOND_MI = 4 ^ C_SCOND_XOR",
|
||||
"SCOND_PL = 5 ^ C_SCOND_XOR",
|
||||
"SCOND_VS = 6 ^ C_SCOND_XOR",
|
||||
"SCOND_VC = 7 ^ C_SCOND_XOR",
|
||||
"SCOND_HI = 8 ^ C_SCOND_XOR",
|
||||
"SCOND_LS = 9 ^ C_SCOND_XOR",
|
||||
"SCOND_GE = 10 ^ C_SCOND_XOR",
|
||||
"SCOND_LT = 11 ^ C_SCOND_XOR",
|
||||
"SCOND_GT = 12 ^ C_SCOND_XOR",
|
||||
"SCOND_LE = 13 ^ C_SCOND_XOR",
|
||||
"SCOND_NONE = 14 ^ C_SCOND_XOR",
|
||||
"SCOND_NV = 15 ^ C_SCOND_XOR",
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,84 @@
|
|||
// Inferno utils/5c/list.c
|
||||
// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5c/list.c
|
||||
//
|
||||
// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
|
||||
// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
|
||||
// Portions Copyright © 1997-1999 Vita Nuova Limited
|
||||
// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
|
||||
// Portions Copyright © 2004,2006 Bruce Ellis
|
||||
// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
|
||||
// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
|
||||
// Portions Copyright © 2009 The Go Authors. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package arm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/google/gops/internal/obj"
|
||||
)
|
||||
|
||||
func init() {
|
||||
obj.RegisterRegister(obj.RBaseARM, MAXREG, Rconv)
|
||||
obj.RegisterOpcode(obj.ABaseARM, Anames)
|
||||
}
|
||||
|
||||
func Rconv(r int) string {
|
||||
if r == 0 {
|
||||
return "NONE"
|
||||
}
|
||||
if r == REGG {
|
||||
// Special case.
|
||||
return "g"
|
||||
}
|
||||
if REG_R0 <= r && r <= REG_R15 {
|
||||
return fmt.Sprintf("R%d", r-REG_R0)
|
||||
}
|
||||
if REG_F0 <= r && r <= REG_F15 {
|
||||
return fmt.Sprintf("F%d", r-REG_F0)
|
||||
}
|
||||
|
||||
switch r {
|
||||
case REG_FPSR:
|
||||
return "FPSR"
|
||||
|
||||
case REG_FPCR:
|
||||
return "FPCR"
|
||||
|
||||
case REG_CPSR:
|
||||
return "CPSR"
|
||||
|
||||
case REG_SPSR:
|
||||
return "SPSR"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Rgok(%d)", r-obj.RBaseARM)
|
||||
}
|
||||
|
||||
func DRconv(a int) string {
|
||||
s := "C_??"
|
||||
if a >= C_NONE && a <= C_NCLASS {
|
||||
s = cnames5[a]
|
||||
}
|
||||
var fp string
|
||||
fp += s
|
||||
return fp
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,719 @@
|
|||
// cmd/7c/7.out.h from Vita Nuova.
|
||||
// https://code.google.com/p/ken-cc/source/browse/src/cmd/7c/7.out.h
|
||||
//
|
||||
// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
|
||||
// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
|
||||
// Portions Copyright © 1997-1999 Vita Nuova Limited
|
||||
// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
|
||||
// Portions Copyright © 2004,2006 Bruce Ellis
|
||||
// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
|
||||
// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
|
||||
// Portions Copyright © 2009 The Go Authors. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package arm64
|
||||
|
||||
import "github.com/google/gops/internal/obj"
|
||||
|
||||
const (
|
||||
NSNAME = 8
|
||||
NSYM = 50
|
||||
NREG = 32 /* number of general registers */
|
||||
NFREG = 32 /* number of floating point registers */
|
||||
)
|
||||
|
||||
// General purpose registers, kept in the low bits of Prog.Reg.
|
||||
const (
|
||||
// integer
|
||||
REG_R0 = obj.RBaseARM64 + iota
|
||||
REG_R1
|
||||
REG_R2
|
||||
REG_R3
|
||||
REG_R4
|
||||
REG_R5
|
||||
REG_R6
|
||||
REG_R7
|
||||
REG_R8
|
||||
REG_R9
|
||||
REG_R10
|
||||
REG_R11
|
||||
REG_R12
|
||||
REG_R13
|
||||
REG_R14
|
||||
REG_R15
|
||||
REG_R16
|
||||
REG_R17
|
||||
REG_R18
|
||||
REG_R19
|
||||
REG_R20
|
||||
REG_R21
|
||||
REG_R22
|
||||
REG_R23
|
||||
REG_R24
|
||||
REG_R25
|
||||
REG_R26
|
||||
REG_R27
|
||||
REG_R28
|
||||
REG_R29
|
||||
REG_R30
|
||||
REG_R31
|
||||
|
||||
// scalar floating point
|
||||
REG_F0
|
||||
REG_F1
|
||||
REG_F2
|
||||
REG_F3
|
||||
REG_F4
|
||||
REG_F5
|
||||
REG_F6
|
||||
REG_F7
|
||||
REG_F8
|
||||
REG_F9
|
||||
REG_F10
|
||||
REG_F11
|
||||
REG_F12
|
||||
REG_F13
|
||||
REG_F14
|
||||
REG_F15
|
||||
REG_F16
|
||||
REG_F17
|
||||
REG_F18
|
||||
REG_F19
|
||||
REG_F20
|
||||
REG_F21
|
||||
REG_F22
|
||||
REG_F23
|
||||
REG_F24
|
||||
REG_F25
|
||||
REG_F26
|
||||
REG_F27
|
||||
REG_F28
|
||||
REG_F29
|
||||
REG_F30
|
||||
REG_F31
|
||||
|
||||
// SIMD
|
||||
REG_V0
|
||||
REG_V1
|
||||
REG_V2
|
||||
REG_V3
|
||||
REG_V4
|
||||
REG_V5
|
||||
REG_V6
|
||||
REG_V7
|
||||
REG_V8
|
||||
REG_V9
|
||||
REG_V10
|
||||
REG_V11
|
||||
REG_V12
|
||||
REG_V13
|
||||
REG_V14
|
||||
REG_V15
|
||||
REG_V16
|
||||
REG_V17
|
||||
REG_V18
|
||||
REG_V19
|
||||
REG_V20
|
||||
REG_V21
|
||||
REG_V22
|
||||
REG_V23
|
||||
REG_V24
|
||||
REG_V25
|
||||
REG_V26
|
||||
REG_V27
|
||||
REG_V28
|
||||
REG_V29
|
||||
REG_V30
|
||||
REG_V31
|
||||
|
||||
// The EQ in
|
||||
// CSET EQ, R0
|
||||
// is encoded as TYPE_REG, even though it's not really a register.
|
||||
COND_EQ
|
||||
COND_NE
|
||||
COND_HS
|
||||
COND_LO
|
||||
COND_MI
|
||||
COND_PL
|
||||
COND_VS
|
||||
COND_VC
|
||||
COND_HI
|
||||
COND_LS
|
||||
COND_GE
|
||||
COND_LT
|
||||
COND_GT
|
||||
COND_LE
|
||||
COND_AL
|
||||
COND_NV
|
||||
|
||||
REG_RSP = REG_V31 + 32 // to differentiate ZR/SP, REG_RSP&0x1f = 31
|
||||
)
|
||||
|
||||
// Not registers, but flags that can be combined with regular register
|
||||
// constants to indicate extended register conversion. When checking,
|
||||
// you should subtract obj.RBaseARM64 first. From this difference, bit 11
|
||||
// indicates extended register, bits 8-10 select the conversion mode.
|
||||
const REG_EXT = obj.RBaseARM64 + 1<<11
|
||||
|
||||
const (
|
||||
REG_UXTB = REG_EXT + iota<<8
|
||||
REG_UXTH
|
||||
REG_UXTW
|
||||
REG_UXTX
|
||||
REG_SXTB
|
||||
REG_SXTH
|
||||
REG_SXTW
|
||||
REG_SXTX
|
||||
)
|
||||
|
||||
// Special registers, after subtracting obj.RBaseARM64, bit 12 indicates
|
||||
// a special register and the low bits select the register.
|
||||
const (
|
||||
REG_SPECIAL = obj.RBaseARM64 + 1<<12 + iota
|
||||
REG_DAIF
|
||||
REG_NZCV
|
||||
REG_FPSR
|
||||
REG_FPCR
|
||||
REG_SPSR_EL1
|
||||
REG_ELR_EL1
|
||||
REG_SPSR_EL2
|
||||
REG_ELR_EL2
|
||||
REG_CurrentEL
|
||||
REG_SP_EL0
|
||||
REG_SPSel
|
||||
REG_DAIFSet
|
||||
REG_DAIFClr
|
||||
)
|
||||
|
||||
// Register assignments:
|
||||
//
|
||||
// compiler allocates R0 up as temps
|
||||
// compiler allocates register variables R7-R25
|
||||
// compiler allocates external registers R26 down
|
||||
//
|
||||
// compiler allocates register variables F7-F26
|
||||
// compiler allocates external registers F26 down
|
||||
const (
|
||||
REGMIN = REG_R7 // register variables allocated from here to REGMAX
|
||||
REGRT1 = REG_R16 // ARM64 IP0, for external linker, runtime, duffzero and duffcopy
|
||||
REGRT2 = REG_R17 // ARM64 IP1, for external linker, runtime, duffcopy
|
||||
REGPR = REG_R18 // ARM64 platform register, unused in the Go toolchain
|
||||
REGMAX = REG_R25
|
||||
|
||||
REGCTXT = REG_R26 // environment for closures
|
||||
REGTMP = REG_R27 // reserved for liblink
|
||||
REGG = REG_R28 // G
|
||||
REGFP = REG_R29 // frame pointer, unused in the Go toolchain
|
||||
REGLINK = REG_R30
|
||||
|
||||
// ARM64 uses R31 as both stack pointer and zero register,
|
||||
// depending on the instruction. To differentiate RSP from ZR,
|
||||
// we use a different numeric value for REGZERO and REGSP.
|
||||
REGZERO = REG_R31
|
||||
REGSP = REG_RSP
|
||||
|
||||
FREGRET = REG_F0
|
||||
FREGMIN = REG_F7 // first register variable
|
||||
FREGMAX = REG_F26 // last register variable for 7g only
|
||||
FREGEXT = REG_F26 // first external register
|
||||
)
|
||||
|
||||
const (
|
||||
BIG = 2048 - 8
|
||||
)
|
||||
|
||||
const (
|
||||
/* mark flags */
|
||||
LABEL = 1 << iota
|
||||
LEAF
|
||||
FLOAT
|
||||
BRANCH
|
||||
LOAD
|
||||
FCMP
|
||||
SYNC
|
||||
LIST
|
||||
FOLL
|
||||
NOSCHED
|
||||
)
|
||||
|
||||
const (
|
||||
C_NONE = iota
|
||||
C_REG // R0..R30
|
||||
C_RSP // R0..R30, RSP
|
||||
C_FREG // F0..F31
|
||||
C_VREG // V0..V31
|
||||
C_PAIR // (Rn, Rm)
|
||||
C_SHIFT // Rn<<2
|
||||
C_EXTREG // Rn.UXTB<<3
|
||||
C_SPR // REG_NZCV
|
||||
C_COND // EQ, NE, etc
|
||||
|
||||
C_ZCON // $0 or ZR
|
||||
C_ADDCON0 // 12-bit unsigned, unshifted
|
||||
C_ADDCON // 12-bit unsigned, shifted left by 0 or 12
|
||||
C_MOVCON // generated by a 16-bit constant, optionally inverted and/or shifted by multiple of 16
|
||||
C_BITCON // bitfield and logical immediate masks
|
||||
C_ABCON0 // could be C_ADDCON0 or C_BITCON
|
||||
C_ABCON // could be C_ADDCON or C_BITCON
|
||||
C_MBCON // could be C_MOVCON or C_BITCON
|
||||
C_LCON // 32-bit constant
|
||||
C_VCON // 64-bit constant
|
||||
C_FCON // floating-point constant
|
||||
C_VCONADDR // 64-bit memory address
|
||||
|
||||
C_AACON // ADDCON offset in auto constant $a(FP)
|
||||
C_LACON // 32-bit offset in auto constant $a(FP)
|
||||
C_AECON // ADDCON offset in extern constant $e(SB)
|
||||
|
||||
// TODO(aram): only one branch class should be enough
|
||||
C_SBRA // for TYPE_BRANCH
|
||||
C_LBRA
|
||||
|
||||
C_NPAUTO // -512 <= x < 0, 0 mod 8
|
||||
C_NSAUTO // -256 <= x < 0
|
||||
C_PSAUTO // 0 to 255
|
||||
C_PPAUTO // 0 to 504, 0 mod 8
|
||||
C_UAUTO4K // 0 to 4095
|
||||
C_UAUTO8K // 0 to 8190, 0 mod 2
|
||||
C_UAUTO16K // 0 to 16380, 0 mod 4
|
||||
C_UAUTO32K // 0 to 32760, 0 mod 8
|
||||
C_UAUTO64K // 0 to 65520, 0 mod 16
|
||||
C_LAUTO // any other 32-bit constant
|
||||
|
||||
C_SEXT1 // 0 to 4095, direct
|
||||
C_SEXT2 // 0 to 8190
|
||||
C_SEXT4 // 0 to 16380
|
||||
C_SEXT8 // 0 to 32760
|
||||
C_SEXT16 // 0 to 65520
|
||||
C_LEXT
|
||||
|
||||
// TODO(aram): s/AUTO/INDIR/
|
||||
C_ZOREG // 0(R)
|
||||
C_NPOREG // mirror NPAUTO, etc
|
||||
C_NSOREG
|
||||
C_PSOREG
|
||||
C_PPOREG
|
||||
C_UOREG4K
|
||||
C_UOREG8K
|
||||
C_UOREG16K
|
||||
C_UOREG32K
|
||||
C_UOREG64K
|
||||
C_LOREG
|
||||
|
||||
C_ADDR // TODO(aram): explain difference from C_VCONADDR
|
||||
|
||||
// The GOT slot for a symbol in -dynlink mode.
|
||||
C_GOTADDR
|
||||
|
||||
// TLS "var" in local exec mode: will become a constant offset from
|
||||
// thread local base that is ultimately chosen by the program linker.
|
||||
C_TLS_LE
|
||||
|
||||
// TLS "var" in initial exec mode: will become a memory address (chosen
|
||||
// by the program linker) that the dynamic linker will fill with the
|
||||
// offset from the thread local base.
|
||||
C_TLS_IE
|
||||
|
||||
C_ROFF // register offset (including register extended)
|
||||
|
||||
C_GOK
|
||||
C_TEXTSIZE
|
||||
C_NCLASS // must be last
|
||||
)
|
||||
|
||||
const (
|
||||
C_XPRE = 1 << 6 // match arm.C_WBIT, so Prog.String know how to print it
|
||||
C_XPOST = 1 << 5 // match arm.C_PBIT, so Prog.String know how to print it
|
||||
)
|
||||
|
||||
//go:generate go run ../stringer.go -i $GOFILE -o anames.go -p arm64
|
||||
|
||||
const (
|
||||
AADC = obj.ABaseARM64 + obj.A_ARCHSPECIFIC + iota
|
||||
AADCS
|
||||
AADCSW
|
||||
AADCW
|
||||
AADD
|
||||
AADDS
|
||||
AADDSW
|
||||
AADDW
|
||||
AADR
|
||||
AADRP
|
||||
AAND
|
||||
AANDS
|
||||
AANDSW
|
||||
AANDW
|
||||
AASR
|
||||
AASRW
|
||||
AAT
|
||||
ABFI
|
||||
ABFIW
|
||||
ABFM
|
||||
ABFMW
|
||||
ABFXIL
|
||||
ABFXILW
|
||||
ABIC
|
||||
ABICS
|
||||
ABICSW
|
||||
ABICW
|
||||
ABRK
|
||||
ACBNZ
|
||||
ACBNZW
|
||||
ACBZ
|
||||
ACBZW
|
||||
ACCMN
|
||||
ACCMNW
|
||||
ACCMP
|
||||
ACCMPW
|
||||
ACINC
|
||||
ACINCW
|
||||
ACINV
|
||||
ACINVW
|
||||
ACLREX
|
||||
ACLS
|
||||
ACLSW
|
||||
ACLZ
|
||||
ACLZW
|
||||
ACMN
|
||||
ACMNW
|
||||
ACMP
|
||||
ACMPW
|
||||
ACNEG
|
||||
ACNEGW
|
||||
ACRC32B
|
||||
ACRC32CB
|
||||
ACRC32CH
|
||||
ACRC32CW
|
||||
ACRC32CX
|
||||
ACRC32H
|
||||
ACRC32W
|
||||
ACRC32X
|
||||
ACSEL
|
||||
ACSELW
|
||||
ACSET
|
||||
ACSETM
|
||||
ACSETMW
|
||||
ACSETW
|
||||
ACSINC
|
||||
ACSINCW
|
||||
ACSINV
|
||||
ACSINVW
|
||||
ACSNEG
|
||||
ACSNEGW
|
||||
ADC
|
||||
ADCPS1
|
||||
ADCPS2
|
||||
ADCPS3
|
||||
ADMB
|
||||
ADRPS
|
||||
ADSB
|
||||
AEON
|
||||
AEONW
|
||||
AEOR
|
||||
AEORW
|
||||
AERET
|
||||
AEXTR
|
||||
AEXTRW
|
||||
AHINT
|
||||
AHLT
|
||||
AHVC
|
||||
AIC
|
||||
AISB
|
||||
ALDAR
|
||||
ALDARB
|
||||
ALDARH
|
||||
ALDARW
|
||||
ALDAXP
|
||||
ALDAXPW
|
||||
ALDAXR
|
||||
ALDAXRB
|
||||
ALDAXRH
|
||||
ALDAXRW
|
||||
ALDP
|
||||
ALDXR
|
||||
ALDXRB
|
||||
ALDXRH
|
||||
ALDXRW
|
||||
ALDXP
|
||||
ALDXPW
|
||||
ALSL
|
||||
ALSLW
|
||||
ALSR
|
||||
ALSRW
|
||||
AMADD
|
||||
AMADDW
|
||||
AMNEG
|
||||
AMNEGW
|
||||
AMOVK
|
||||
AMOVKW
|
||||
AMOVN
|
||||
AMOVNW
|
||||
AMOVZ
|
||||
AMOVZW
|
||||
AMRS
|
||||
AMSR
|
||||
AMSUB
|
||||
AMSUBW
|
||||
AMUL
|
||||
AMULW
|
||||
AMVN
|
||||
AMVNW
|
||||
ANEG
|
||||
ANEGS
|
||||
ANEGSW
|
||||
ANEGW
|
||||
ANGC
|
||||
ANGCS
|
||||
ANGCSW
|
||||
ANGCW
|
||||
AORN
|
||||
AORNW
|
||||
AORR
|
||||
AORRW
|
||||
APRFM
|
||||
APRFUM
|
||||
ARBIT
|
||||
ARBITW
|
||||
AREM
|
||||
AREMW
|
||||
AREV
|
||||
AREV16
|
||||
AREV16W
|
||||
AREV32
|
||||
AREVW
|
||||
AROR
|
||||
ARORW
|
||||
ASBC
|
||||
ASBCS
|
||||
ASBCSW
|
||||
ASBCW
|
||||
ASBFIZ
|
||||
ASBFIZW
|
||||
ASBFM
|
||||
ASBFMW
|
||||
ASBFX
|
||||
ASBFXW
|
||||
ASDIV
|
||||
ASDIVW
|
||||
ASEV
|
||||
ASEVL
|
||||
ASMADDL
|
||||
ASMC
|
||||
ASMNEGL
|
||||
ASMSUBL
|
||||
ASMULH
|
||||
ASMULL
|
||||
ASTXR
|
||||
ASTXRB
|
||||
ASTXRH
|
||||
ASTXP
|
||||
ASTXPW
|
||||
ASTXRW
|
||||
ASTLP
|
||||
ASTLPW
|
||||
ASTLR
|
||||
ASTLRB
|
||||
ASTLRH
|
||||
ASTLRW
|
||||
ASTLXP
|
||||
ASTLXPW
|
||||
ASTLXR
|
||||
ASTLXRB
|
||||
ASTLXRH
|
||||
ASTLXRW
|
||||
ASTP
|
||||
ASUB
|
||||
ASUBS
|
||||
ASUBSW
|
||||
ASUBW
|
||||
ASVC
|
||||
ASXTB
|
||||
ASXTBW
|
||||
ASXTH
|
||||
ASXTHW
|
||||
ASXTW
|
||||
ASYS
|
||||
ASYSL
|
||||
ATBNZ
|
||||
ATBZ
|
||||
ATLBI
|
||||
ATST
|
||||
ATSTW
|
||||
AUBFIZ
|
||||
AUBFIZW
|
||||
AUBFM
|
||||
AUBFMW
|
||||
AUBFX
|
||||
AUBFXW
|
||||
AUDIV
|
||||
AUDIVW
|
||||
AUMADDL
|
||||
AUMNEGL
|
||||
AUMSUBL
|
||||
AUMULH
|
||||
AUMULL
|
||||
AUREM
|
||||
AUREMW
|
||||
AUXTB
|
||||
AUXTH
|
||||
AUXTW
|
||||
AUXTBW
|
||||
AUXTHW
|
||||
AWFE
|
||||
AWFI
|
||||
AYIELD
|
||||
AMOVB
|
||||
AMOVBU
|
||||
AMOVH
|
||||
AMOVHU
|
||||
AMOVW
|
||||
AMOVWU
|
||||
AMOVD
|
||||
AMOVNP
|
||||
AMOVNPW
|
||||
AMOVP
|
||||
AMOVPD
|
||||
AMOVPQ
|
||||
AMOVPS
|
||||
AMOVPSW
|
||||
AMOVPW
|
||||
ABEQ
|
||||
ABNE
|
||||
ABCS
|
||||
ABHS
|
||||
ABCC
|
||||
ABLO
|
||||
ABMI
|
||||
ABPL
|
||||
ABVS
|
||||
ABVC
|
||||
ABHI
|
||||
ABLS
|
||||
ABGE
|
||||
ABLT
|
||||
ABGT
|
||||
ABLE
|
||||
AFABSD
|
||||
AFABSS
|
||||
AFADDD
|
||||
AFADDS
|
||||
AFCCMPD
|
||||
AFCCMPED
|
||||
AFCCMPS
|
||||
AFCCMPES
|
||||
AFCMPD
|
||||
AFCMPED
|
||||
AFCMPES
|
||||
AFCMPS
|
||||
AFCVTSD
|
||||
AFCVTDS
|
||||
AFCVTZSD
|
||||
AFCVTZSDW
|
||||
AFCVTZSS
|
||||
AFCVTZSSW
|
||||
AFCVTZUD
|
||||
AFCVTZUDW
|
||||
AFCVTZUS
|
||||
AFCVTZUSW
|
||||
AFDIVD
|
||||
AFDIVS
|
||||
AFMOVD
|
||||
AFMOVS
|
||||
AFMULD
|
||||
AFMULS
|
||||
AFNEGD
|
||||
AFNEGS
|
||||
AFSQRTD
|
||||
AFSQRTS
|
||||
AFSUBD
|
||||
AFSUBS
|
||||
ASCVTFD
|
||||
ASCVTFS
|
||||
ASCVTFWD
|
||||
ASCVTFWS
|
||||
AUCVTFD
|
||||
AUCVTFS
|
||||
AUCVTFWD
|
||||
AUCVTFWS
|
||||
AWORD
|
||||
ADWORD
|
||||
AFCSELS
|
||||
AFCSELD
|
||||
AFMAXS
|
||||
AFMINS
|
||||
AFMAXD
|
||||
AFMIND
|
||||
AFMAXNMS
|
||||
AFMAXNMD
|
||||
AFNMULS
|
||||
AFNMULD
|
||||
AFRINTNS
|
||||
AFRINTND
|
||||
AFRINTPS
|
||||
AFRINTPD
|
||||
AFRINTMS
|
||||
AFRINTMD
|
||||
AFRINTZS
|
||||
AFRINTZD
|
||||
AFRINTAS
|
||||
AFRINTAD
|
||||
AFRINTXS
|
||||
AFRINTXD
|
||||
AFRINTIS
|
||||
AFRINTID
|
||||
AFMADDS
|
||||
AFMADDD
|
||||
AFMSUBS
|
||||
AFMSUBD
|
||||
AFNMADDS
|
||||
AFNMADDD
|
||||
AFNMSUBS
|
||||
AFNMSUBD
|
||||
AFMINNMS
|
||||
AFMINNMD
|
||||
AFCVTDH
|
||||
AFCVTHS
|
||||
AFCVTHD
|
||||
AFCVTSH
|
||||
AAESD
|
||||
AAESE
|
||||
AAESIMC
|
||||
AAESMC
|
||||
ASHA1C
|
||||
ASHA1H
|
||||
ASHA1M
|
||||
ASHA1P
|
||||
ASHA1SU0
|
||||
ASHA1SU1
|
||||
ASHA256H
|
||||
ASHA256H2
|
||||
ASHA256SU0
|
||||
ASHA256SU1
|
||||
ALAST
|
||||
AB = obj.AJMP
|
||||
ABL = obj.ACALL
|
||||
)
|
||||
|
||||
const (
|
||||
// shift types
|
||||
SHIFT_LL = 0 << 22
|
||||
SHIFT_LR = 1 << 22
|
||||
SHIFT_AR = 2 << 22
|
||||
)
|
|
@ -0,0 +1,370 @@
|
|||
// Generated by stringer -i a.out.go -o anames.go -p arm64
|
||||
// Do not edit.
|
||||
|
||||
package arm64
|
||||
|
||||
import "github.com/google/gops/internal/obj"
|
||||
|
||||
var Anames = []string{
|
||||
obj.A_ARCHSPECIFIC: "ADC",
|
||||
"ADCS",
|
||||
"ADCSW",
|
||||
"ADCW",
|
||||
"ADD",
|
||||
"ADDS",
|
||||
"ADDSW",
|
||||
"ADDW",
|
||||
"ADR",
|
||||
"ADRP",
|
||||
"AND",
|
||||
"ANDS",
|
||||
"ANDSW",
|
||||
"ANDW",
|
||||
"ASR",
|
||||
"ASRW",
|
||||
"AT",
|
||||
"BFI",
|
||||
"BFIW",
|
||||
"BFM",
|
||||
"BFMW",
|
||||
"BFXIL",
|
||||
"BFXILW",
|
||||
"BIC",
|
||||
"BICS",
|
||||
"BICSW",
|
||||
"BICW",
|
||||
"BRK",
|
||||
"CBNZ",
|
||||
"CBNZW",
|
||||
"CBZ",
|
||||
"CBZW",
|
||||
"CCMN",
|
||||
"CCMNW",
|
||||
"CCMP",
|
||||
"CCMPW",
|
||||
"CINC",
|
||||
"CINCW",
|
||||
"CINV",
|
||||
"CINVW",
|
||||
"CLREX",
|
||||
"CLS",
|
||||
"CLSW",
|
||||
"CLZ",
|
||||
"CLZW",
|
||||
"CMN",
|
||||
"CMNW",
|
||||
"CMP",
|
||||
"CMPW",
|
||||
"CNEG",
|
||||
"CNEGW",
|
||||
"CRC32B",
|
||||
"CRC32CB",
|
||||
"CRC32CH",
|
||||
"CRC32CW",
|
||||
"CRC32CX",
|
||||
"CRC32H",
|
||||
"CRC32W",
|
||||
"CRC32X",
|
||||
"CSEL",
|
||||
"CSELW",
|
||||
"CSET",
|
||||
"CSETM",
|
||||
"CSETMW",
|
||||
"CSETW",
|
||||
"CSINC",
|
||||
"CSINCW",
|
||||
"CSINV",
|
||||
"CSINVW",
|
||||
"CSNEG",
|
||||
"CSNEGW",
|
||||
"DC",
|
||||
"DCPS1",
|
||||
"DCPS2",
|
||||
"DCPS3",
|
||||
"DMB",
|
||||
"DRPS",
|
||||
"DSB",
|
||||
"EON",
|
||||
"EONW",
|
||||
"EOR",
|
||||
"EORW",
|
||||
"ERET",
|
||||
"EXTR",
|
||||
"EXTRW",
|
||||
"HINT",
|
||||
"HLT",
|
||||
"HVC",
|
||||
"IC",
|
||||
"ISB",
|
||||
"LDAR",
|
||||
"LDARB",
|
||||
"LDARH",
|
||||
"LDARW",
|
||||
"LDAXP",
|
||||
"LDAXPW",
|
||||
"LDAXR",
|
||||
"LDAXRB",
|
||||
"LDAXRH",
|
||||
"LDAXRW",
|
||||
"LDP",
|
||||
"LDXR",
|
||||
"LDXRB",
|
||||
"LDXRH",
|
||||
"LDXRW",
|
||||
"LDXP",
|
||||
"LDXPW",
|
||||
"LSL",
|
||||
"LSLW",
|
||||
"LSR",
|
||||
"LSRW",
|
||||
"MADD",
|
||||
"MADDW",
|
||||
"MNEG",
|
||||
"MNEGW",
|
||||
"MOVK",
|
||||
"MOVKW",
|
||||
"MOVN",
|
||||
"MOVNW",
|
||||
"MOVZ",
|
||||
"MOVZW",
|
||||
"MRS",
|
||||
"MSR",
|
||||
"MSUB",
|
||||
"MSUBW",
|
||||
"MUL",
|
||||
"MULW",
|
||||
"MVN",
|
||||
"MVNW",
|
||||
"NEG",
|
||||
"NEGS",
|
||||
"NEGSW",
|
||||
"NEGW",
|
||||
"NGC",
|
||||
"NGCS",
|
||||
"NGCSW",
|
||||
"NGCW",
|
||||
"ORN",
|
||||
"ORNW",
|
||||
"ORR",
|
||||
"ORRW",
|
||||
"PRFM",
|
||||
"PRFUM",
|
||||
"RBIT",
|
||||
"RBITW",
|
||||
"REM",
|
||||
"REMW",
|
||||
"REV",
|
||||
"REV16",
|
||||
"REV16W",
|
||||
"REV32",
|
||||
"REVW",
|
||||
"ROR",
|
||||
"RORW",
|
||||
"SBC",
|
||||
"SBCS",
|
||||
"SBCSW",
|
||||
"SBCW",
|
||||
"SBFIZ",
|
||||
"SBFIZW",
|
||||
"SBFM",
|
||||
"SBFMW",
|
||||
"SBFX",
|
||||
"SBFXW",
|
||||
"SDIV",
|
||||
"SDIVW",
|
||||
"SEV",
|
||||
"SEVL",
|
||||
"SMADDL",
|
||||
"SMC",
|
||||
"SMNEGL",
|
||||
"SMSUBL",
|
||||
"SMULH",
|
||||
"SMULL",
|
||||
"STXR",
|
||||
"STXRB",
|
||||
"STXRH",
|
||||
"STXP",
|
||||
"STXPW",
|
||||
"STXRW",
|
||||
"STLP",
|
||||
"STLPW",
|
||||
"STLR",
|
||||
"STLRB",
|
||||
"STLRH",
|
||||
"STLRW",
|
||||
"STLXP",
|
||||
"STLXPW",
|
||||
"STLXR",
|
||||
"STLXRB",
|
||||
"STLXRH",
|
||||
"STLXRW",
|
||||
"STP",
|
||||
"SUB",
|
||||
"SUBS",
|
||||
"SUBSW",
|
||||
"SUBW",
|
||||
"SVC",
|
||||
"SXTB",
|
||||
"SXTBW",
|
||||
"SXTH",
|
||||
"SXTHW",
|
||||
"SXTW",
|
||||
"SYS",
|
||||
"SYSL",
|
||||
"TBNZ",
|
||||
"TBZ",
|
||||
"TLBI",
|
||||
"TST",
|
||||
"TSTW",
|
||||
"UBFIZ",
|
||||
"UBFIZW",
|
||||
"UBFM",
|
||||
"UBFMW",
|
||||
"UBFX",
|
||||
"UBFXW",
|
||||
"UDIV",
|
||||
"UDIVW",
|
||||
"UMADDL",
|
||||
"UMNEGL",
|
||||
"UMSUBL",
|
||||
"UMULH",
|
||||
"UMULL",
|
||||
"UREM",
|
||||
"UREMW",
|
||||
"UXTB",
|
||||
"UXTH",
|
||||
"UXTW",
|
||||
"UXTBW",
|
||||
"UXTHW",
|
||||
"WFE",
|
||||
"WFI",
|
||||
"YIELD",
|
||||
"MOVB",
|
||||
"MOVBU",
|
||||
"MOVH",
|
||||
"MOVHU",
|
||||
"MOVW",
|
||||
"MOVWU",
|
||||
"MOVD",
|
||||
"MOVNP",
|
||||
"MOVNPW",
|
||||
"MOVP",
|
||||
"MOVPD",
|
||||
"MOVPQ",
|
||||
"MOVPS",
|
||||
"MOVPSW",
|
||||
"MOVPW",
|
||||
"BEQ",
|
||||
"BNE",
|
||||
"BCS",
|
||||
"BHS",
|
||||
"BCC",
|
||||
"BLO",
|
||||
"BMI",
|
||||
"BPL",
|
||||
"BVS",
|
||||
"BVC",
|
||||
"BHI",
|
||||
"BLS",
|
||||
"BGE",
|
||||
"BLT",
|
||||
"BGT",
|
||||
"BLE",
|
||||
"FABSD",
|
||||
"FABSS",
|
||||
"FADDD",
|
||||
"FADDS",
|
||||
"FCCMPD",
|
||||
"FCCMPED",
|
||||
"FCCMPS",
|
||||
"FCCMPES",
|
||||
"FCMPD",
|
||||
"FCMPED",
|
||||
"FCMPES",
|
||||
"FCMPS",
|
||||
"FCVTSD",
|
||||
"FCVTDS",
|
||||
"FCVTZSD",
|
||||
"FCVTZSDW",
|
||||
"FCVTZSS",
|
||||
"FCVTZSSW",
|
||||
"FCVTZUD",
|
||||
"FCVTZUDW",
|
||||
"FCVTZUS",
|
||||
"FCVTZUSW",
|
||||
"FDIVD",
|
||||
"FDIVS",
|
||||
"FMOVD",
|
||||
"FMOVS",
|
||||
"FMULD",
|
||||
"FMULS",
|
||||
"FNEGD",
|
||||
"FNEGS",
|
||||
"FSQRTD",
|
||||
"FSQRTS",
|
||||
"FSUBD",
|
||||
"FSUBS",
|
||||
"SCVTFD",
|
||||
"SCVTFS",
|
||||
"SCVTFWD",
|
||||
"SCVTFWS",
|
||||
"UCVTFD",
|
||||
"UCVTFS",
|
||||
"UCVTFWD",
|
||||
"UCVTFWS",
|
||||
"WORD",
|
||||
"DWORD",
|
||||
"FCSELS",
|
||||
"FCSELD",
|
||||
"FMAXS",
|
||||
"FMINS",
|
||||
"FMAXD",
|
||||
"FMIND",
|
||||
"FMAXNMS",
|
||||
"FMAXNMD",
|
||||
"FNMULS",
|
||||
"FNMULD",
|
||||
"FRINTNS",
|
||||
"FRINTND",
|
||||
"FRINTPS",
|
||||
"FRINTPD",
|
||||
"FRINTMS",
|
||||
"FRINTMD",
|
||||
"FRINTZS",
|
||||
"FRINTZD",
|
||||
"FRINTAS",
|
||||
"FRINTAD",
|
||||
"FRINTXS",
|
||||
"FRINTXD",
|
||||
"FRINTIS",
|
||||
"FRINTID",
|
||||
"FMADDS",
|
||||
"FMADDD",
|
||||
"FMSUBS",
|
||||
"FMSUBD",
|
||||
"FNMADDS",
|
||||
"FNMADDD",
|
||||
"FNMSUBS",
|
||||
"FNMSUBD",
|
||||
"FMINNMS",
|
||||
"FMINNMD",
|
||||
"FCVTDH",
|
||||
"FCVTHS",
|
||||
"FCVTHD",
|
||||
"FCVTSH",
|
||||
"AESD",
|
||||
"AESE",
|
||||
"AESIMC",
|
||||
"AESMC",
|
||||
"SHA1C",
|
||||
"SHA1H",
|
||||
"SHA1M",
|
||||
"SHA1P",
|
||||
"SHA1SU0",
|
||||
"SHA1SU1",
|
||||
"SHA256H",
|
||||
"SHA256H2",
|
||||
"SHA256SU0",
|
||||
"SHA256SU1",
|
||||
"LAST",
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
// 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.
|
||||
|
||||
package arm64
|
||||
|
||||
var cnames7 = []string{
|
||||
"NONE",
|
||||
"REG",
|
||||
"RSP",
|
||||
"FREG",
|
||||
"VREG",
|
||||
"PAIR",
|
||||
"SHIFT",
|
||||
"EXTREG",
|
||||
"SPR",
|
||||
"COND",
|
||||
"ZCON",
|
||||
"ADDCON0",
|
||||
"ADDCON",
|
||||
"MOVCON",
|
||||
"BITCON",
|
||||
"ABCON0",
|
||||
"ABCON",
|
||||
"MBCON",
|
||||
"LCON",
|
||||
"VCON",
|
||||
"FCON",
|
||||
"VCONADDR",
|
||||
"AACON",
|
||||
"LACON",
|
||||
"AECON",
|
||||
"SBRA",
|
||||
"LBRA",
|
||||
"NPAUTO",
|
||||
"NSAUTO",
|
||||
"PSAUTO",
|
||||
"PPAUTO",
|
||||
"UAUTO4K",
|
||||
"UAUTO8K",
|
||||
"UAUTO16K",
|
||||
"UAUTO32K",
|
||||
"UAUTO64K",
|
||||
"LAUTO",
|
||||
"SEXT1",
|
||||
"SEXT2",
|
||||
"SEXT4",
|
||||
"SEXT8",
|
||||
"SEXT16",
|
||||
"LEXT",
|
||||
"ZOREG",
|
||||
"NPOREG",
|
||||
"NSOREG",
|
||||
"PSOREG",
|
||||
"PPOREG",
|
||||
"UOREG4K",
|
||||
"UOREG8K",
|
||||
"UOREG16K",
|
||||
"UOREG32K",
|
||||
"UOREG64K",
|
||||
"LOREG",
|
||||
"ADDR",
|
||||
"GOTADDR",
|
||||
"TLS_LE",
|
||||
"TLS_IE",
|
||||
"ROFF",
|
||||
"GOK",
|
||||
"TEXTSIZE",
|
||||
"NCLASS",
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,115 @@
|
|||
// cmd/7l/list.c and cmd/7l/sub.c from Vita Nuova.
|
||||
// https://code.google.com/p/ken-cc/source/browse/
|
||||
//
|
||||
// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
|
||||
// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
|
||||
// Portions Copyright © 1997-1999 Vita Nuova Limited
|
||||
// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
|
||||
// Portions Copyright © 2004,2006 Bruce Ellis
|
||||
// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
|
||||
// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
|
||||
// Portions Copyright © 2009 The Go Authors. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package arm64
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/google/gops/internal/obj"
|
||||
)
|
||||
|
||||
var strcond = [16]string{
|
||||
"EQ",
|
||||
"NE",
|
||||
"HS",
|
||||
"LO",
|
||||
"MI",
|
||||
"PL",
|
||||
"VS",
|
||||
"VC",
|
||||
"HI",
|
||||
"LS",
|
||||
"GE",
|
||||
"LT",
|
||||
"GT",
|
||||
"LE",
|
||||
"AL",
|
||||
"NV",
|
||||
}
|
||||
|
||||
func init() {
|
||||
obj.RegisterRegister(obj.RBaseARM64, REG_SPECIAL+1024, Rconv)
|
||||
obj.RegisterOpcode(obj.ABaseARM64, Anames)
|
||||
}
|
||||
|
||||
func Rconv(r int) string {
|
||||
if r == REGG {
|
||||
return "g"
|
||||
}
|
||||
switch {
|
||||
case REG_R0 <= r && r <= REG_R30:
|
||||
return fmt.Sprintf("R%d", r-REG_R0)
|
||||
case r == REG_R31:
|
||||
return "ZR"
|
||||
case REG_F0 <= r && r <= REG_F31:
|
||||
return fmt.Sprintf("F%d", r-REG_F0)
|
||||
case REG_V0 <= r && r <= REG_V31:
|
||||
return fmt.Sprintf("V%d", r-REG_V0)
|
||||
case COND_EQ <= r && r <= COND_NV:
|
||||
return strcond[r-COND_EQ]
|
||||
case r == REGSP:
|
||||
return "RSP"
|
||||
case r == REG_DAIF:
|
||||
return "DAIF"
|
||||
case r == REG_NZCV:
|
||||
return "NZCV"
|
||||
case r == REG_FPSR:
|
||||
return "FPSR"
|
||||
case r == REG_FPCR:
|
||||
return "FPCR"
|
||||
case r == REG_SPSR_EL1:
|
||||
return "SPSR_EL1"
|
||||
case r == REG_ELR_EL1:
|
||||
return "ELR_EL1"
|
||||
case r == REG_SPSR_EL2:
|
||||
return "SPSR_EL2"
|
||||
case r == REG_ELR_EL2:
|
||||
return "ELR_EL2"
|
||||
case r == REG_CurrentEL:
|
||||
return "CurrentEL"
|
||||
case r == REG_SP_EL0:
|
||||
return "SP_EL0"
|
||||
case r == REG_SPSel:
|
||||
return "SPSel"
|
||||
case r == REG_DAIFSet:
|
||||
return "DAIFSet"
|
||||
case r == REG_DAIFClr:
|
||||
return "DAIFClr"
|
||||
}
|
||||
return fmt.Sprintf("badreg(%d)", r)
|
||||
}
|
||||
|
||||
func DRconv(a int) string {
|
||||
if a >= C_NONE && a <= C_NCLASS {
|
||||
return cnames7[a]
|
||||
}
|
||||
return "C_??"
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,190 @@
|
|||
// Derived from Inferno utils/6l/obj.c and utils/6l/span.c
|
||||
// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/obj.c
|
||||
// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/span.c
|
||||
//
|
||||
// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
|
||||
// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
|
||||
// Portions Copyright © 1997-1999 Vita Nuova Limited
|
||||
// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
|
||||
// Portions Copyright © 2004,2006 Bruce Ellis
|
||||
// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
|
||||
// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
|
||||
// Portions Copyright © 2009 The Go Authors. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package obj
|
||||
|
||||
import (
|
||||
"log"
|
||||
"math"
|
||||
)
|
||||
|
||||
// Grow increases the length of s.P to lsiz.
|
||||
func (s *LSym) Grow(lsiz int64) {
|
||||
siz := int(lsiz)
|
||||
if int64(siz) != lsiz {
|
||||
log.Fatalf("LSym.Grow size %d too long", lsiz)
|
||||
}
|
||||
if len(s.P) >= siz {
|
||||
return
|
||||
}
|
||||
// TODO(dfc) append cap-len at once, rather than
|
||||
// one byte at a time.
|
||||
for cap(s.P) < siz {
|
||||
s.P = append(s.P[:cap(s.P)], 0)
|
||||
}
|
||||
s.P = s.P[:siz]
|
||||
}
|
||||
|
||||
// GrowCap increases the capacity of s.P to c.
|
||||
func (s *LSym) GrowCap(c int64) {
|
||||
if int64(cap(s.P)) >= c {
|
||||
return
|
||||
}
|
||||
if s.P == nil {
|
||||
s.P = make([]byte, 0, c)
|
||||
return
|
||||
}
|
||||
b := make([]byte, len(s.P), c)
|
||||
copy(b, s.P)
|
||||
s.P = b
|
||||
}
|
||||
|
||||
// prepwrite prepares to write data of size siz into s at offset off.
|
||||
func (s *LSym) prepwrite(ctxt *Link, off int64, siz int) {
|
||||
if off < 0 || siz < 0 || off >= 1<<30 {
|
||||
log.Fatalf("prepwrite: bad off=%d siz=%d", off, siz)
|
||||
}
|
||||
if s.Type == SBSS || s.Type == STLSBSS {
|
||||
ctxt.Diag("cannot supply data for BSS var")
|
||||
}
|
||||
l := off + int64(siz)
|
||||
s.Grow(l)
|
||||
if l > s.Size {
|
||||
s.Size = l
|
||||
}
|
||||
}
|
||||
|
||||
// WriteFloat32 writes f into s at offset off.
|
||||
func (s *LSym) WriteFloat32(ctxt *Link, off int64, f float32) {
|
||||
s.prepwrite(ctxt, off, 4)
|
||||
ctxt.Arch.ByteOrder.PutUint32(s.P[off:], math.Float32bits(f))
|
||||
}
|
||||
|
||||
// WriteFloat64 writes f into s at offset off.
|
||||
func (s *LSym) WriteFloat64(ctxt *Link, off int64, f float64) {
|
||||
s.prepwrite(ctxt, off, 8)
|
||||
ctxt.Arch.ByteOrder.PutUint64(s.P[off:], math.Float64bits(f))
|
||||
}
|
||||
|
||||
// WriteInt writes an integer i of size siz into s at offset off.
|
||||
func (s *LSym) WriteInt(ctxt *Link, off int64, siz int, i int64) {
|
||||
s.prepwrite(ctxt, off, siz)
|
||||
switch siz {
|
||||
default:
|
||||
ctxt.Diag("WriteInt: bad integer size: %d", siz)
|
||||
case 1:
|
||||
s.P[off] = byte(i)
|
||||
case 2:
|
||||
ctxt.Arch.ByteOrder.PutUint16(s.P[off:], uint16(i))
|
||||
case 4:
|
||||
ctxt.Arch.ByteOrder.PutUint32(s.P[off:], uint32(i))
|
||||
case 8:
|
||||
ctxt.Arch.ByteOrder.PutUint64(s.P[off:], uint64(i))
|
||||
}
|
||||
}
|
||||
|
||||
// WriteAddr writes an address of size siz into s at offset off.
|
||||
// rsym and roff specify the relocation for the address.
|
||||
func (s *LSym) WriteAddr(ctxt *Link, off int64, siz int, rsym *LSym, roff int64) {
|
||||
if siz != ctxt.Arch.PtrSize {
|
||||
ctxt.Diag("WriteAddr: bad address size %d in %s", siz, s.Name)
|
||||
}
|
||||
s.prepwrite(ctxt, off, siz)
|
||||
r := Addrel(s)
|
||||
r.Off = int32(off)
|
||||
if int64(r.Off) != off {
|
||||
ctxt.Diag("WriteAddr: off overflow %d in %s", off, s.Name)
|
||||
}
|
||||
r.Siz = uint8(siz)
|
||||
r.Sym = rsym
|
||||
r.Type = R_ADDR
|
||||
r.Add = roff
|
||||
}
|
||||
|
||||
// WriteOff writes a 4 byte offset to rsym+roff into s at offset off.
|
||||
// After linking the 4 bytes stored at s+off will be
|
||||
// rsym+roff-(start of section that s is in).
|
||||
func (s *LSym) WriteOff(ctxt *Link, off int64, rsym *LSym, roff int64) {
|
||||
s.prepwrite(ctxt, off, 4)
|
||||
r := Addrel(s)
|
||||
r.Off = int32(off)
|
||||
if int64(r.Off) != off {
|
||||
ctxt.Diag("WriteOff: off overflow %d in %s", off, s.Name)
|
||||
}
|
||||
r.Siz = 4
|
||||
r.Sym = rsym
|
||||
r.Type = R_ADDROFF
|
||||
r.Add = roff
|
||||
}
|
||||
|
||||
// WriteString writes a string of size siz into s at offset off.
|
||||
func (s *LSym) WriteString(ctxt *Link, off int64, siz int, str string) {
|
||||
if siz < len(str) {
|
||||
ctxt.Diag("WriteString: bad string size: %d < %d", siz, len(str))
|
||||
}
|
||||
s.prepwrite(ctxt, off, siz)
|
||||
copy(s.P[off:off+int64(siz)], str)
|
||||
}
|
||||
|
||||
// WriteBytes writes a slice of bytes into s at offset off.
|
||||
func (s *LSym) WriteBytes(ctxt *Link, off int64, b []byte) int64 {
|
||||
s.prepwrite(ctxt, off, len(b))
|
||||
copy(s.P[off:], b)
|
||||
return off + int64(len(b))
|
||||
}
|
||||
|
||||
func Addrel(s *LSym) *Reloc {
|
||||
s.R = append(s.R, Reloc{})
|
||||
return &s.R[len(s.R)-1]
|
||||
}
|
||||
|
||||
func Setuintxx(ctxt *Link, s *LSym, off int64, v uint64, wid int64) int64 {
|
||||
if s.Type == 0 {
|
||||
s.Type = SDATA
|
||||
}
|
||||
if s.Size < off+wid {
|
||||
s.Size = off + wid
|
||||
s.Grow(s.Size)
|
||||
}
|
||||
|
||||
switch wid {
|
||||
case 1:
|
||||
s.P[off] = uint8(v)
|
||||
case 2:
|
||||
ctxt.Arch.ByteOrder.PutUint16(s.P[off:], uint16(v))
|
||||
case 4:
|
||||
ctxt.Arch.ByteOrder.PutUint32(s.P[off:], uint32(v))
|
||||
case 8:
|
||||
ctxt.Arch.ByteOrder.PutUint64(s.P[off:], v)
|
||||
}
|
||||
|
||||
return off + wid
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
// 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.
|
||||
|
||||
package obj
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func Flagfn2(string, string, func(string, string)) { panic("flag") }
|
||||
|
||||
func Flagcount(name, usage string, val *int) {
|
||||
flag.Var((*count)(val), name, usage)
|
||||
}
|
||||
|
||||
func Flagint32(name, usage string, val *int32) {
|
||||
flag.Var((*int32Value)(val), name, usage)
|
||||
}
|
||||
|
||||
func Flagint64(name, usage string, val *int64) {
|
||||
flag.Int64Var(val, name, *val, usage)
|
||||
}
|
||||
|
||||
func Flagstr(name, usage string, val *string) {
|
||||
flag.StringVar(val, name, *val, usage)
|
||||
}
|
||||
|
||||
func Flagfn0(name, usage string, f func()) {
|
||||
flag.Var(fn0(f), name, usage)
|
||||
}
|
||||
|
||||
func Flagfn1(name, usage string, f func(string)) {
|
||||
flag.Var(fn1(f), name, usage)
|
||||
}
|
||||
|
||||
func Flagprint(fd int) {
|
||||
if fd == 1 {
|
||||
flag.CommandLine.SetOutput(os.Stdout)
|
||||
}
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
func Flagparse(usage func()) {
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
// count is a flag.Value that is like a flag.Bool and a flag.Int.
|
||||
// If used as -name, it increments the count, but -name=x sets the count.
|
||||
// Used for verbose flag -v.
|
||||
type count int
|
||||
|
||||
func (c *count) String() string {
|
||||
return fmt.Sprint(int(*c))
|
||||
}
|
||||
|
||||
func (c *count) Set(s string) error {
|
||||
switch s {
|
||||
case "true":
|
||||
*c++
|
||||
case "false":
|
||||
*c = 0
|
||||
default:
|
||||
n, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid count %q", s)
|
||||
}
|
||||
*c = count(n)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *count) IsBoolFlag() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type int32Value int32
|
||||
|
||||
func (i *int32Value) Set(s string) error {
|
||||
v, err := strconv.ParseInt(s, 0, 64)
|
||||
*i = int32Value(v)
|
||||
return err
|
||||
}
|
||||
|
||||
func (i *int32Value) Get() interface{} { return int32(*i) }
|
||||
|
||||
func (i *int32Value) String() string { return fmt.Sprint(*i) }
|
||||
|
||||
type fn0 func()
|
||||
|
||||
func (f fn0) Set(s string) error {
|
||||
f()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f fn0) Get() interface{} { return nil }
|
||||
|
||||
func (f fn0) String() string { return "" }
|
||||
|
||||
func (f fn0) IsBoolFlag() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type fn1 func(string)
|
||||
|
||||
func (f fn1) Set(s string) error {
|
||||
f(s)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f fn1) String() string { return "" }
|
|
@ -0,0 +1,48 @@
|
|||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package obj
|
||||
|
||||
// This file defines the IDs for PCDATA and FUNCDATA instructions
|
||||
// in Go binaries. It is included by assembly sources, so it must
|
||||
// be written using #defines.
|
||||
//
|
||||
// The Go compiler also #includes this file, for now.
|
||||
//
|
||||
// symtab.go also contains a copy of these constants.
|
||||
|
||||
// Pseudo-assembly statements.
|
||||
|
||||
// GO_ARGS, GO_RESULTS_INITIALIZED, and NO_LOCAL_POINTERS are macros
|
||||
// that communicate to the runtime information about the location and liveness
|
||||
// of pointers in an assembly function's arguments, results, and stack frame.
|
||||
// This communication is only required in assembly functions that make calls
|
||||
// to other functions that might be preempted or grow the stack.
|
||||
// NOSPLIT functions that make no calls do not need to use these macros.
|
||||
|
||||
// GO_ARGS indicates that the Go prototype for this assembly function
|
||||
// defines the pointer map for the function's arguments.
|
||||
// GO_ARGS should be the first instruction in a function that uses it.
|
||||
// It can be omitted if there are no arguments at all.
|
||||
// GO_ARGS is inserted implicitly by the linker for any function
|
||||
// that also has a Go prototype and therefore is usually not necessary
|
||||
// to write explicitly.
|
||||
|
||||
// GO_RESULTS_INITIALIZED indicates that the assembly function
|
||||
// has initialized the stack space for its results and that those results
|
||||
// should be considered live for the remainder of the function.
|
||||
|
||||
// NO_LOCAL_POINTERS indicates that the assembly function stores
|
||||
// no pointers to heap objects in its local stack variables.
|
||||
|
||||
// ArgsSizeUnknown is set in Func.argsize to mark all functions
|
||||
// whose argument size is unknown (C vararg functions, and
|
||||
// assembly code without an explicit specification).
|
||||
// This value is generated by the compiler, assembler, or linker.
|
||||
const (
|
||||
PCDATA_StackMapIndex = 0
|
||||
FUNCDATA_ArgsPointerMaps = 0
|
||||
FUNCDATA_LocalsPointerMaps = 1
|
||||
ArgsSizeUnknown = -0x80000000
|
||||
)
|
|
@ -0,0 +1,86 @@
|
|||
// Copyright 2009 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 obj
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// go-specific code shared across loaders (5l, 6l, 8l).
|
||||
|
||||
var (
|
||||
framepointer_enabled int
|
||||
Fieldtrack_enabled int
|
||||
)
|
||||
|
||||
// Toolchain experiments.
|
||||
// These are controlled by the GOEXPERIMENT environment
|
||||
// variable recorded when the toolchain is built.
|
||||
// This list is also known to cmd/gc.
|
||||
var exper = []struct {
|
||||
name string
|
||||
val *int
|
||||
}{
|
||||
{"fieldtrack", &Fieldtrack_enabled},
|
||||
{"framepointer", &framepointer_enabled},
|
||||
}
|
||||
|
||||
func addexp(s string) {
|
||||
// Could do general integer parsing here, but the runtime copy doesn't yet.
|
||||
v := 1
|
||||
name := s
|
||||
if len(name) > 2 && name[:2] == "no" {
|
||||
v = 0
|
||||
name = name[2:]
|
||||
}
|
||||
for i := 0; i < len(exper); i++ {
|
||||
if exper[i].name == name {
|
||||
if exper[i].val != nil {
|
||||
*exper[i].val = v
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("unknown experiment %s\n", s)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
func init() {
|
||||
framepointer_enabled = 1 // default
|
||||
for _, f := range strings.Split(goexperiment, ",") {
|
||||
if f != "" {
|
||||
addexp(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Framepointer_enabled(goos, goarch string) bool {
|
||||
return framepointer_enabled != 0 && goarch == "amd64" && goos != "nacl"
|
||||
}
|
||||
|
||||
func Nopout(p *Prog) {
|
||||
p.As = ANOP
|
||||
p.Scond = 0
|
||||
p.From = Addr{}
|
||||
p.From3 = nil
|
||||
p.Reg = 0
|
||||
p.To = Addr{}
|
||||
}
|
||||
|
||||
func Expstring() string {
|
||||
buf := "X"
|
||||
for i := range exper {
|
||||
if *exper[i].val != 0 {
|
||||
buf += "," + exper[i].name
|
||||
}
|
||||
}
|
||||
if buf == "X" {
|
||||
buf += ",none"
|
||||
}
|
||||
return "X:" + buf[2:]
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
// Derived from Inferno utils/6l/obj.c and utils/6l/span.c
|
||||
// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/obj.c
|
||||
// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/span.c
|
||||
//
|
||||
// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
|
||||
// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
|
||||
// Portions Copyright © 1997-1999 Vita Nuova Limited
|
||||
// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
|
||||
// Portions Copyright © 2004,2006 Bruce Ellis
|
||||
// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
|
||||
// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
|
||||
// Portions Copyright © 2009 The Go Authors. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package obj
|
||||
|
||||
/*
|
||||
* add library to library list.
|
||||
* srcref: src file referring to package
|
||||
* objref: object file referring to package
|
||||
* file: object file, e.g., /home/rsc/go/pkg/container/vector.a
|
||||
* pkg: package import path, e.g. container/vector
|
||||
*/
|
||||
|
||||
const (
|
||||
LOG = 5
|
||||
)
|
||||
|
||||
func mkfwd(sym *LSym) {
|
||||
var dwn [LOG]int32
|
||||
var cnt [LOG]int32
|
||||
var lst [LOG]*Prog
|
||||
|
||||
for i := 0; i < LOG; i++ {
|
||||
if i == 0 {
|
||||
cnt[i] = 1
|
||||
} else {
|
||||
cnt[i] = LOG * cnt[i-1]
|
||||
}
|
||||
dwn[i] = 1
|
||||
lst[i] = nil
|
||||
}
|
||||
|
||||
i := 0
|
||||
for p := sym.Text; p != nil && p.Link != nil; p = p.Link {
|
||||
i--
|
||||
if i < 0 {
|
||||
i = LOG - 1
|
||||
}
|
||||
p.Forwd = nil
|
||||
dwn[i]--
|
||||
if dwn[i] <= 0 {
|
||||
dwn[i] = cnt[i]
|
||||
if lst[i] != nil {
|
||||
lst[i].Forwd = p
|
||||
}
|
||||
lst[i] = p
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Copyp(ctxt *Link, q *Prog) *Prog {
|
||||
p := ctxt.NewProg()
|
||||
*p = *q
|
||||
return p
|
||||
}
|
||||
|
||||
func Appendp(ctxt *Link, q *Prog) *Prog {
|
||||
p := ctxt.NewProg()
|
||||
p.Link = q.Link
|
||||
q.Link = p
|
||||
p.Lineno = q.Lineno
|
||||
p.Mode = q.Mode
|
||||
return p
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
// 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.
|
||||
|
||||
package obj
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLineHist(t *testing.T) {
|
||||
ctxt := new(Link)
|
||||
ctxt.Hash = make(map[SymVer]*LSym)
|
||||
|
||||
ctxt.LineHist.Push(1, "a.c")
|
||||
ctxt.LineHist.Push(3, "a.h")
|
||||
ctxt.LineHist.Pop(5)
|
||||
ctxt.LineHist.Update(7, "linedir", 2)
|
||||
ctxt.LineHist.Pop(9)
|
||||
ctxt.LineHist.Push(11, "b.c")
|
||||
ctxt.LineHist.Pop(13)
|
||||
|
||||
var expect = []string{
|
||||
0: "??:0",
|
||||
1: "a.c:1",
|
||||
2: "a.c:2",
|
||||
3: "a.h:1",
|
||||
4: "a.h:2",
|
||||
5: "a.c:3",
|
||||
6: "a.c:4",
|
||||
7: "linedir:2",
|
||||
8: "linedir:3",
|
||||
9: "??:0",
|
||||
10: "??:0",
|
||||
11: "b.c:1",
|
||||
12: "b.c:2",
|
||||
13: "??:0",
|
||||
14: "??:0",
|
||||
}
|
||||
|
||||
for i, want := range expect {
|
||||
f, l := linkgetline(ctxt, int32(i))
|
||||
have := fmt.Sprintf("%s:%d", f.Name, l)
|
||||
if have != want {
|
||||
t.Errorf("linkgetline(%d) = %q, want %q", i, have, want)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,974 @@
|
|||
// Derived from Inferno utils/6l/l.h and related files.
|
||||
// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/l.h
|
||||
//
|
||||
// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
|
||||
// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
|
||||
// Portions Copyright © 1997-1999 Vita Nuova Limited
|
||||
// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
|
||||
// Portions Copyright © 2004,2006 Bruce Ellis
|
||||
// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
|
||||
// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
|
||||
// Portions Copyright © 2009 The Go Authors. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package obj
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/gops/internal/sys"
|
||||
)
|
||||
|
||||
// An Addr is an argument to an instruction.
|
||||
// The general forms and their encodings are:
|
||||
//
|
||||
// sym±offset(symkind)(reg)(index*scale)
|
||||
// Memory reference at address &sym(symkind) + offset + reg + index*scale.
|
||||
// Any of sym(symkind), ±offset, (reg), (index*scale), and *scale can be omitted.
|
||||
// If (reg) and *scale are both omitted, the resulting expression (index) is parsed as (reg).
|
||||
// To force a parsing as index*scale, write (index*1).
|
||||
// Encoding:
|
||||
// type = TYPE_MEM
|
||||
// name = symkind (NAME_AUTO, ...) or 0 (NAME_NONE)
|
||||
// sym = sym
|
||||
// offset = ±offset
|
||||
// reg = reg (REG_*)
|
||||
// index = index (REG_*)
|
||||
// scale = scale (1, 2, 4, 8)
|
||||
//
|
||||
// $<mem>
|
||||
// Effective address of memory reference <mem>, defined above.
|
||||
// Encoding: same as memory reference, but type = TYPE_ADDR.
|
||||
//
|
||||
// $<±integer value>
|
||||
// This is a special case of $<mem>, in which only ±offset is present.
|
||||
// It has a separate type for easy recognition.
|
||||
// Encoding:
|
||||
// type = TYPE_CONST
|
||||
// offset = ±integer value
|
||||
//
|
||||
// *<mem>
|
||||
// Indirect reference through memory reference <mem>, defined above.
|
||||
// Only used on x86 for CALL/JMP *sym(SB), which calls/jumps to a function
|
||||
// pointer stored in the data word sym(SB), not a function named sym(SB).
|
||||
// Encoding: same as above, but type = TYPE_INDIR.
|
||||
//
|
||||
// $*$<mem>
|
||||
// No longer used.
|
||||
// On machines with actual SB registers, $*$<mem> forced the
|
||||
// instruction encoding to use a full 32-bit constant, never a
|
||||
// reference relative to SB.
|
||||
//
|
||||
// $<floating point literal>
|
||||
// Floating point constant value.
|
||||
// Encoding:
|
||||
// type = TYPE_FCONST
|
||||
// val = floating point value
|
||||
//
|
||||
// $<string literal, up to 8 chars>
|
||||
// String literal value (raw bytes used for DATA instruction).
|
||||
// Encoding:
|
||||
// type = TYPE_SCONST
|
||||
// val = string
|
||||
//
|
||||
// <register name>
|
||||
// Any register: integer, floating point, control, segment, and so on.
|
||||
// If looking for specific register kind, must check type and reg value range.
|
||||
// Encoding:
|
||||
// type = TYPE_REG
|
||||
// reg = reg (REG_*)
|
||||
//
|
||||
// x(PC)
|
||||
// Encoding:
|
||||
// type = TYPE_BRANCH
|
||||
// val = Prog* reference OR ELSE offset = target pc (branch takes priority)
|
||||
//
|
||||
// $±x-±y
|
||||
// Final argument to TEXT, specifying local frame size x and argument size y.
|
||||
// In this form, x and y are integer literals only, not arbitrary expressions.
|
||||
// This avoids parsing ambiguities due to the use of - as a separator.
|
||||
// The ± are optional.
|
||||
// If the final argument to TEXT omits the -±y, the encoding should still
|
||||
// use TYPE_TEXTSIZE (not TYPE_CONST), with u.argsize = ArgsSizeUnknown.
|
||||
// Encoding:
|
||||
// type = TYPE_TEXTSIZE
|
||||
// offset = x
|
||||
// val = int32(y)
|
||||
//
|
||||
// reg<<shift, reg>>shift, reg->shift, reg@>shift
|
||||
// Shifted register value, for ARM and ARM64.
|
||||
// In this form, reg must be a register and shift can be a register or an integer constant.
|
||||
// Encoding:
|
||||
// type = TYPE_SHIFT
|
||||
// On ARM:
|
||||
// offset = (reg&15) | shifttype<<5 | count
|
||||
// shifttype = 0, 1, 2, 3 for <<, >>, ->, @>
|
||||
// count = (reg&15)<<8 | 1<<4 for a register shift count, (n&31)<<7 for an integer constant.
|
||||
// On ARM64:
|
||||
// offset = (reg&31)<<16 | shifttype<<22 | (count&63)<<10
|
||||
// shifttype = 0, 1, 2 for <<, >>, ->
|
||||
//
|
||||
// (reg, reg)
|
||||
// A destination register pair. When used as the last argument of an instruction,
|
||||
// this form makes clear that both registers are destinations.
|
||||
// Encoding:
|
||||
// type = TYPE_REGREG
|
||||
// reg = first register
|
||||
// offset = second register
|
||||
//
|
||||
// [reg, reg, reg-reg]
|
||||
// Register list for ARM.
|
||||
// Encoding:
|
||||
// type = TYPE_REGLIST
|
||||
// offset = bit mask of registers in list; R0 is low bit.
|
||||
//
|
||||
// reg, reg
|
||||
// Register pair for ARM.
|
||||
// TYPE_REGREG2
|
||||
//
|
||||
// (reg+reg)
|
||||
// Register pair for PPC64.
|
||||
// Encoding:
|
||||
// type = TYPE_MEM
|
||||
// reg = first register
|
||||
// index = second register
|
||||
// scale = 1
|
||||
//
|
||||
type Addr struct {
|
||||
Reg int16
|
||||
Index int16
|
||||
Scale int16 // Sometimes holds a register.
|
||||
Type AddrType
|
||||
Name int8
|
||||
Class int8
|
||||
Offset int64
|
||||
Sym *LSym
|
||||
|
||||
// argument value:
|
||||
// for TYPE_SCONST, a string
|
||||
// for TYPE_FCONST, a float64
|
||||
// for TYPE_BRANCH, a *Prog (optional)
|
||||
// for TYPE_TEXTSIZE, an int32 (optional)
|
||||
Val interface{}
|
||||
|
||||
Node interface{} // for use by compiler
|
||||
}
|
||||
|
||||
type AddrType uint8
|
||||
|
||||
const (
|
||||
NAME_NONE = 0 + iota
|
||||
NAME_EXTERN
|
||||
NAME_STATIC
|
||||
NAME_AUTO
|
||||
NAME_PARAM
|
||||
// A reference to name@GOT(SB) is a reference to the entry in the global offset
|
||||
// table for 'name'.
|
||||
NAME_GOTREF
|
||||
)
|
||||
|
||||
const (
|
||||
TYPE_NONE AddrType = 0
|
||||
|
||||
TYPE_BRANCH AddrType = 5 + iota
|
||||
TYPE_TEXTSIZE
|
||||
TYPE_MEM
|
||||
TYPE_CONST
|
||||
TYPE_FCONST
|
||||
TYPE_SCONST
|
||||
TYPE_REG
|
||||
TYPE_ADDR
|
||||
TYPE_SHIFT
|
||||
TYPE_REGREG
|
||||
TYPE_REGREG2
|
||||
TYPE_INDIR
|
||||
TYPE_REGLIST
|
||||
)
|
||||
|
||||
// Prog describes a single machine instruction.
|
||||
//
|
||||
// The general instruction form is:
|
||||
//
|
||||
// As.Scond From, Reg, From3, To, RegTo2
|
||||
//
|
||||
// where As is an opcode and the others are arguments:
|
||||
// From, Reg, From3 are sources, and To, RegTo2 are destinations.
|
||||
// Usually, not all arguments are present.
|
||||
// For example, MOVL R1, R2 encodes using only As=MOVL, From=R1, To=R2.
|
||||
// The Scond field holds additional condition bits for systems (like arm)
|
||||
// that have generalized conditional execution.
|
||||
//
|
||||
// Jump instructions use the Pcond field to point to the target instruction,
|
||||
// which must be in the same linked list as the jump instruction.
|
||||
//
|
||||
// The Progs for a given function are arranged in a list linked through the Link field.
|
||||
//
|
||||
// Each Prog is charged to a specific source line in the debug information,
|
||||
// specified by Lineno, an index into the line history (see LineHist).
|
||||
// Every Prog has a Ctxt field that defines various context, including the current LineHist.
|
||||
// Progs should be allocated using ctxt.NewProg(), not new(Prog).
|
||||
//
|
||||
// The other fields not yet mentioned are for use by the back ends and should
|
||||
// be left zeroed by creators of Prog lists.
|
||||
type Prog struct {
|
||||
Ctxt *Link // linker context
|
||||
Link *Prog // next Prog in linked list
|
||||
From Addr // first source operand
|
||||
From3 *Addr // third source operand (second is Reg below)
|
||||
To Addr // destination operand (second is RegTo2 below)
|
||||
Pcond *Prog // target of conditional jump
|
||||
Opt interface{} // available to optimization passes to hold per-Prog state
|
||||
Forwd *Prog // for x86 back end
|
||||
Rel *Prog // for x86, arm back ends
|
||||
Pc int64 // for back ends or assembler: virtual or actual program counter, depending on phase
|
||||
Lineno int32 // line number of this instruction
|
||||
Spadj int32 // effect of instruction on stack pointer (increment or decrement amount)
|
||||
As As // assembler opcode
|
||||
Reg int16 // 2nd source operand
|
||||
RegTo2 int16 // 2nd destination operand
|
||||
Mark uint16 // bitmask of arch-specific items
|
||||
Optab uint16 // arch-specific opcode index
|
||||
Scond uint8 // condition bits for conditional instruction (e.g., on ARM)
|
||||
Back uint8 // for x86 back end: backwards branch state
|
||||
Ft uint8 // for x86 back end: type index of Prog.From
|
||||
Tt uint8 // for x86 back end: type index of Prog.To
|
||||
Isize uint8 // for x86 back end: size of the instruction in bytes
|
||||
Mode int8 // for x86 back end: 32- or 64-bit mode
|
||||
}
|
||||
|
||||
// From3Type returns From3.Type, or TYPE_NONE when From3 is nil.
|
||||
func (p *Prog) From3Type() AddrType {
|
||||
if p.From3 == nil {
|
||||
return TYPE_NONE
|
||||
}
|
||||
return p.From3.Type
|
||||
}
|
||||
|
||||
// From3Offset returns From3.Offset, or 0 when From3 is nil.
|
||||
func (p *Prog) From3Offset() int64 {
|
||||
if p.From3 == nil {
|
||||
return 0
|
||||
}
|
||||
return p.From3.Offset
|
||||
}
|
||||
|
||||
// An As denotes an assembler opcode.
|
||||
// There are some portable opcodes, declared here in package obj,
|
||||
// that are common to all architectures.
|
||||
// However, the majority of opcodes are arch-specific
|
||||
// and are declared in their respective architecture's subpackage.
|
||||
type As int16
|
||||
|
||||
// These are the portable opcodes.
|
||||
const (
|
||||
AXXX As = iota
|
||||
ACALL
|
||||
ADUFFCOPY
|
||||
ADUFFZERO
|
||||
AEND
|
||||
AFUNCDATA
|
||||
AJMP
|
||||
ANOP
|
||||
APCDATA
|
||||
ARET
|
||||
ATEXT
|
||||
ATYPE
|
||||
AUNDEF
|
||||
AUSEFIELD
|
||||
AVARDEF
|
||||
AVARKILL
|
||||
AVARLIVE
|
||||
A_ARCHSPECIFIC
|
||||
)
|
||||
|
||||
// Each architecture is allotted a distinct subspace of opcode values
|
||||
// for declaring its arch-specific opcodes.
|
||||
// Within this subspace, the first arch-specific opcode should be
|
||||
// at offset A_ARCHSPECIFIC.
|
||||
//
|
||||
// Subspaces are aligned to a power of two so opcodes can be masked
|
||||
// with AMask and used as compact array indices.
|
||||
const (
|
||||
ABase386 = (1 + iota) << 10
|
||||
ABaseARM
|
||||
ABaseAMD64
|
||||
ABasePPC64
|
||||
ABaseARM64
|
||||
ABaseMIPS64
|
||||
ABaseS390X
|
||||
|
||||
AllowedOpCodes = 1 << 10 // The number of opcodes available for any given architecture.
|
||||
AMask = AllowedOpCodes - 1 // AND with this to use the opcode as an array index.
|
||||
)
|
||||
|
||||
// An LSym is the sort of symbol that is written to an object file.
|
||||
type LSym struct {
|
||||
Name string
|
||||
Type SymKind
|
||||
Version int16
|
||||
Attribute
|
||||
|
||||
RefIdx int // Index of this symbol in the symbol reference list.
|
||||
Args int32
|
||||
Locals int32
|
||||
Size int64
|
||||
Gotype *LSym
|
||||
Autom *Auto
|
||||
Text *Prog
|
||||
Pcln *Pcln
|
||||
P []byte
|
||||
R []Reloc
|
||||
}
|
||||
|
||||
// Attribute is a set of symbol attributes.
|
||||
type Attribute int16
|
||||
|
||||
const (
|
||||
AttrDuplicateOK Attribute = 1 << iota
|
||||
AttrCFunc
|
||||
AttrNoSplit
|
||||
AttrLeaf
|
||||
AttrSeenGlobl
|
||||
AttrOnList
|
||||
|
||||
// MakeTypelink means that the type should have an entry in the typelink table.
|
||||
AttrMakeTypelink
|
||||
|
||||
// ReflectMethod means the function may call reflect.Type.Method or
|
||||
// reflect.Type.MethodByName. Matching is imprecise (as reflect.Type
|
||||
// can be used through a custom interface), so ReflectMethod may be
|
||||
// set in some cases when the reflect package is not called.
|
||||
//
|
||||
// Used by the linker to determine what methods can be pruned.
|
||||
AttrReflectMethod
|
||||
|
||||
// Local means make the symbol local even when compiling Go code to reference Go
|
||||
// symbols in other shared libraries, as in this mode symbols are global by
|
||||
// default. "local" here means in the sense of the dynamic linker, i.e. not
|
||||
// visible outside of the module (shared library or executable) that contains its
|
||||
// definition. (When not compiling to support Go shared libraries, all symbols are
|
||||
// local in this sense unless there is a cgo_export_* directive).
|
||||
AttrLocal
|
||||
)
|
||||
|
||||
func (a Attribute) DuplicateOK() bool { return a&AttrDuplicateOK != 0 }
|
||||
func (a Attribute) MakeTypelink() bool { return a&AttrMakeTypelink != 0 }
|
||||
func (a Attribute) CFunc() bool { return a&AttrCFunc != 0 }
|
||||
func (a Attribute) NoSplit() bool { return a&AttrNoSplit != 0 }
|
||||
func (a Attribute) Leaf() bool { return a&AttrLeaf != 0 }
|
||||
func (a Attribute) SeenGlobl() bool { return a&AttrSeenGlobl != 0 }
|
||||
func (a Attribute) OnList() bool { return a&AttrOnList != 0 }
|
||||
func (a Attribute) ReflectMethod() bool { return a&AttrReflectMethod != 0 }
|
||||
func (a Attribute) Local() bool { return a&AttrLocal != 0 }
|
||||
|
||||
func (a *Attribute) Set(flag Attribute, value bool) {
|
||||
if value {
|
||||
*a |= flag
|
||||
} else {
|
||||
*a &^= flag
|
||||
}
|
||||
}
|
||||
|
||||
// The compiler needs LSym to satisfy fmt.Stringer, because it stores
|
||||
// an LSym in ssa.ExternSymbol.
|
||||
func (s *LSym) String() string {
|
||||
return s.Name
|
||||
}
|
||||
|
||||
type Pcln struct {
|
||||
Pcsp Pcdata
|
||||
Pcfile Pcdata
|
||||
Pcline Pcdata
|
||||
Pcdata []Pcdata
|
||||
Funcdata []*LSym
|
||||
Funcdataoff []int64
|
||||
File []*LSym
|
||||
Lastfile *LSym
|
||||
Lastindex int
|
||||
}
|
||||
|
||||
// A SymKind describes the kind of memory represented by a symbol.
|
||||
type SymKind int16
|
||||
|
||||
// Defined SymKind values.
|
||||
//
|
||||
// TODO(rsc): Give idiomatic Go names.
|
||||
// TODO(rsc): Reduce the number of symbol types in the object files.
|
||||
//go:generate stringer -type=SymKind
|
||||
const (
|
||||
Sxxx SymKind = iota
|
||||
STEXT
|
||||
SELFRXSECT
|
||||
|
||||
// Read-only sections.
|
||||
STYPE
|
||||
SSTRING
|
||||
SGOSTRING
|
||||
SGOFUNC
|
||||
SGCBITS
|
||||
SRODATA
|
||||
SFUNCTAB
|
||||
|
||||
SELFROSECT
|
||||
SMACHOPLT
|
||||
|
||||
// Read-only sections with relocations.
|
||||
//
|
||||
// Types STYPE-SFUNCTAB above are written to the .rodata section by default.
|
||||
// When linking a shared object, some conceptually "read only" types need to
|
||||
// be written to by relocations and putting them in a section called
|
||||
// ".rodata" interacts poorly with the system linkers. The GNU linkers
|
||||
// support this situation by arranging for sections of the name
|
||||
// ".data.rel.ro.XXX" to be mprotected read only by the dynamic linker after
|
||||
// relocations have applied, so when the Go linker is creating a shared
|
||||
// object it checks all objects of the above types and bumps any object that
|
||||
// has a relocation to it to the corresponding type below, which are then
|
||||
// written to sections with appropriate magic names.
|
||||
STYPERELRO
|
||||
SSTRINGRELRO
|
||||
SGOSTRINGRELRO
|
||||
SGOFUNCRELRO
|
||||
SGCBITSRELRO
|
||||
SRODATARELRO
|
||||
SFUNCTABRELRO
|
||||
|
||||
// Part of .data.rel.ro if it exists, otherwise part of .rodata.
|
||||
STYPELINK
|
||||
SITABLINK
|
||||
SSYMTAB
|
||||
SPCLNTAB
|
||||
|
||||
// Writable sections.
|
||||
SELFSECT
|
||||
SMACHO
|
||||
SMACHOGOT
|
||||
SWINDOWS
|
||||
SELFGOT
|
||||
SNOPTRDATA
|
||||
SINITARR
|
||||
SDATA
|
||||
SBSS
|
||||
SNOPTRBSS
|
||||
STLSBSS
|
||||
SXREF
|
||||
SMACHOSYMSTR
|
||||
SMACHOSYMTAB
|
||||
SMACHOINDIRECTPLT
|
||||
SMACHOINDIRECTGOT
|
||||
SFILE
|
||||
SFILEPATH
|
||||
SCONST
|
||||
SDYNIMPORT
|
||||
SHOSTOBJ
|
||||
SDWARFSECT
|
||||
SDWARFINFO
|
||||
SSUB = SymKind(1 << 8)
|
||||
SMASK = SymKind(SSUB - 1)
|
||||
SHIDDEN = SymKind(1 << 9)
|
||||
SCONTAINER = SymKind(1 << 10) // has a sub-symbol
|
||||
)
|
||||
|
||||
// ReadOnly are the symbol kinds that form read-only sections. In some
|
||||
// cases, if they will require relocations, they are transformed into
|
||||
// rel-ro sections using RelROMap.
|
||||
var ReadOnly = []SymKind{
|
||||
STYPE,
|
||||
SSTRING,
|
||||
SGOSTRING,
|
||||
SGOFUNC,
|
||||
SGCBITS,
|
||||
SRODATA,
|
||||
SFUNCTAB,
|
||||
}
|
||||
|
||||
// RelROMap describes the transformation of read-only symbols to rel-ro
|
||||
// symbols.
|
||||
var RelROMap = map[SymKind]SymKind{
|
||||
STYPE: STYPERELRO,
|
||||
SSTRING: SSTRINGRELRO,
|
||||
SGOSTRING: SGOSTRINGRELRO,
|
||||
SGOFUNC: SGOFUNCRELRO,
|
||||
SGCBITS: SGCBITSRELRO,
|
||||
SRODATA: SRODATARELRO,
|
||||
SFUNCTAB: SFUNCTABRELRO,
|
||||
}
|
||||
|
||||
type Reloc struct {
|
||||
Off int32
|
||||
Siz uint8
|
||||
Type RelocType
|
||||
Add int64
|
||||
Sym *LSym
|
||||
}
|
||||
|
||||
type RelocType int32
|
||||
|
||||
//go:generate stringer -type=RelocType
|
||||
const (
|
||||
R_ADDR RelocType = 1 + iota
|
||||
// R_ADDRPOWER relocates a pair of "D-form" instructions (instructions with 16-bit
|
||||
// immediates in the low half of the instruction word), usually addis followed by
|
||||
// another add or a load, inserting the "high adjusted" 16 bits of the address of
|
||||
// the referenced symbol into the immediate field of the first instruction and the
|
||||
// low 16 bits into that of the second instruction.
|
||||
R_ADDRPOWER
|
||||
// R_ADDRARM64 relocates an adrp, add pair to compute the address of the
|
||||
// referenced symbol.
|
||||
R_ADDRARM64
|
||||
// R_ADDRMIPS (only used on mips64) resolves to the low 16 bits of an external
|
||||
// address, by encoding it into the instruction.
|
||||
R_ADDRMIPS
|
||||
// R_ADDROFF resolves to a 32-bit offset from the beginning of the section
|
||||
// holding the data being relocated to the referenced symbol.
|
||||
R_ADDROFF
|
||||
R_SIZE
|
||||
R_CALL
|
||||
R_CALLARM
|
||||
R_CALLARM64
|
||||
R_CALLIND
|
||||
R_CALLPOWER
|
||||
// R_CALLMIPS (only used on mips64) resolves to non-PC-relative target address
|
||||
// of a CALL (JAL) instruction, by encoding the address into the instruction.
|
||||
R_CALLMIPS
|
||||
R_CONST
|
||||
R_PCREL
|
||||
// R_TLS_LE, used on 386, amd64, and ARM, resolves to the offset of the
|
||||
// thread-local symbol from the thread local base and is used to implement the
|
||||
// "local exec" model for tls access (r.Sym is not set on intel platforms but is
|
||||
// set to a TLS symbol -- runtime.tlsg -- in the linker when externally linking).
|
||||
R_TLS_LE
|
||||
// R_TLS_IE, used 386, amd64, and ARM resolves to the PC-relative offset to a GOT
|
||||
// slot containing the offset from the thread-local symbol from the thread local
|
||||
// base and is used to implemented the "initial exec" model for tls access (r.Sym
|
||||
// is not set on intel platforms but is set to a TLS symbol -- runtime.tlsg -- in
|
||||
// the linker when externally linking).
|
||||
R_TLS_IE
|
||||
R_GOTOFF
|
||||
R_PLT0
|
||||
R_PLT1
|
||||
R_PLT2
|
||||
R_USEFIELD
|
||||
// R_USETYPE resolves to an *rtype, but no relocation is created. The
|
||||
// linker uses this as a signal that the pointed-to type information
|
||||
// should be linked into the final binary, even if there are no other
|
||||
// direct references. (This is used for types reachable by reflection.)
|
||||
R_USETYPE
|
||||
// R_METHODOFF resolves to a 32-bit offset from the beginning of the section
|
||||
// holding the data being relocated to the referenced symbol.
|
||||
// It is a variant of R_ADDROFF used when linking from the uncommonType of a
|
||||
// *rtype, and may be set to zero by the linker if it determines the method
|
||||
// text is unreachable by the linked program.
|
||||
R_METHODOFF
|
||||
R_POWER_TOC
|
||||
R_GOTPCREL
|
||||
// R_JMPMIPS (only used on mips64) resolves to non-PC-relative target address
|
||||
// of a JMP instruction, by encoding the address into the instruction.
|
||||
// The stack nosplit check ignores this since it is not a function call.
|
||||
R_JMPMIPS
|
||||
// R_DWARFREF resolves to the offset of the symbol from its section.
|
||||
R_DWARFREF
|
||||
|
||||
// Platform dependent relocations. Architectures with fixed width instructions
|
||||
// have the inherent issue that a 32-bit (or 64-bit!) displacement cannot be
|
||||
// stuffed into a 32-bit instruction, so an address needs to be spread across
|
||||
// several instructions, and in turn this requires a sequence of relocations, each
|
||||
// updating a part of an instruction. This leads to relocation codes that are
|
||||
// inherently processor specific.
|
||||
|
||||
// Arm64.
|
||||
|
||||
// Set a MOV[NZ] immediate field to bits [15:0] of the offset from the thread
|
||||
// local base to the thread local variable defined by the referenced (thread
|
||||
// local) symbol. Error if the offset does not fit into 16 bits.
|
||||
R_ARM64_TLS_LE
|
||||
|
||||
// Relocates an ADRP; LD64 instruction sequence to load the offset between
|
||||
// the thread local base and the thread local variable defined by the
|
||||
// referenced (thread local) symbol from the GOT.
|
||||
R_ARM64_TLS_IE
|
||||
|
||||
// R_ARM64_GOTPCREL relocates an adrp, ld64 pair to compute the address of the GOT
|
||||
// slot of the referenced symbol.
|
||||
R_ARM64_GOTPCREL
|
||||
|
||||
// PPC64.
|
||||
|
||||
// R_POWER_TLS_LE is used to implement the "local exec" model for tls
|
||||
// access. It resolves to the offset of the thread-local symbol from the
|
||||
// thread pointer (R13) and inserts this value into the low 16 bits of an
|
||||
// instruction word.
|
||||
R_POWER_TLS_LE
|
||||
|
||||
// R_POWER_TLS_IE is used to implement the "initial exec" model for tls access. It
|
||||
// relocates a D-form, DS-form instruction sequence like R_ADDRPOWER_DS. It
|
||||
// inserts to the offset of GOT slot for the thread-local symbol from the TOC (the
|
||||
// GOT slot is filled by the dynamic linker with the offset of the thread-local
|
||||
// symbol from the thread pointer (R13)).
|
||||
R_POWER_TLS_IE
|
||||
|
||||
// R_POWER_TLS marks an X-form instruction such as "MOVD 0(R13)(R31*1), g" as
|
||||
// accessing a particular thread-local symbol. It does not affect code generation
|
||||
// but is used by the system linker when relaxing "initial exec" model code to
|
||||
// "local exec" model code.
|
||||
R_POWER_TLS
|
||||
|
||||
// R_ADDRPOWER_DS is similar to R_ADDRPOWER above, but assumes the second
|
||||
// instruction is a "DS-form" instruction, which has an immediate field occupying
|
||||
// bits [15:2] of the instruction word. Bits [15:2] of the address of the
|
||||
// relocated symbol are inserted into this field; it is an error if the last two
|
||||
// bits of the address are not 0.
|
||||
R_ADDRPOWER_DS
|
||||
|
||||
// R_ADDRPOWER_PCREL relocates a D-form, DS-form instruction sequence like
|
||||
// R_ADDRPOWER_DS but inserts the offset of the GOT slot for the referenced symbol
|
||||
// from the TOC rather than the symbol's address.
|
||||
R_ADDRPOWER_GOT
|
||||
|
||||
// R_ADDRPOWER_PCREL relocates two D-form instructions like R_ADDRPOWER, but
|
||||
// inserts the displacement from the place being relocated to the address of the
|
||||
// the relocated symbol instead of just its address.
|
||||
R_ADDRPOWER_PCREL
|
||||
|
||||
// R_ADDRPOWER_TOCREL relocates two D-form instructions like R_ADDRPOWER, but
|
||||
// inserts the offset from the TOC to the address of the the relocated symbol
|
||||
// rather than the symbol's address.
|
||||
R_ADDRPOWER_TOCREL
|
||||
|
||||
// R_ADDRPOWER_TOCREL relocates a D-form, DS-form instruction sequence like
|
||||
// R_ADDRPOWER_DS but inserts the offset from the TOC to the address of the the
|
||||
// relocated symbol rather than the symbol's address.
|
||||
R_ADDRPOWER_TOCREL_DS
|
||||
|
||||
// R_PCRELDBL relocates s390x 2-byte aligned PC-relative addresses.
|
||||
// TODO(mundaym): remove once variants can be serialized - see issue 14218.
|
||||
R_PCRELDBL
|
||||
|
||||
// R_ADDRMIPSU (only used on mips64) resolves to the sign-adjusted "upper" 16
|
||||
// bits (bit 16-31) of an external address, by encoding it into the instruction.
|
||||
R_ADDRMIPSU
|
||||
// R_ADDRMIPSTLS (only used on mips64) resolves to the low 16 bits of a TLS
|
||||
// address (offset from thread pointer), by encoding it into the instruction.
|
||||
R_ADDRMIPSTLS
|
||||
)
|
||||
|
||||
// IsDirectJump returns whether r is a relocation for a direct jump.
|
||||
// A direct jump is a CALL or JMP instruction that takes the target address
|
||||
// as immediate. The address is embedded into the instruction, possibly
|
||||
// with limited width.
|
||||
// An indirect jump is a CALL or JMP instruction that takes the target address
|
||||
// in register or memory.
|
||||
func (r RelocType) IsDirectJump() bool {
|
||||
switch r {
|
||||
case R_CALL, R_CALLARM, R_CALLARM64, R_CALLPOWER, R_CALLMIPS, R_JMPMIPS:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type Auto struct {
|
||||
Asym *LSym
|
||||
Link *Auto
|
||||
Aoffset int32
|
||||
Name int16
|
||||
Gotype *LSym
|
||||
}
|
||||
|
||||
// Auto.name
|
||||
const (
|
||||
A_AUTO = 1 + iota
|
||||
A_PARAM
|
||||
)
|
||||
|
||||
type Pcdata struct {
|
||||
P []byte
|
||||
}
|
||||
|
||||
// symbol version, incremented each time a file is loaded.
|
||||
// version==1 is reserved for savehist.
|
||||
const (
|
||||
HistVersion = 1
|
||||
)
|
||||
|
||||
// Link holds the context for writing object code from a compiler
|
||||
// to be linker input or for reading that input into the linker.
|
||||
type Link struct {
|
||||
Headtype HeadType
|
||||
Arch *LinkArch
|
||||
Debugasm int32
|
||||
Debugvlog int32
|
||||
Debugdivmod int32
|
||||
Debugpcln int32
|
||||
Flag_shared bool
|
||||
Flag_dynlink bool
|
||||
Flag_optimize bool
|
||||
Bso *bufio.Writer
|
||||
Pathname string
|
||||
Hash map[SymVer]*LSym
|
||||
LineHist LineHist
|
||||
Imports []string
|
||||
Plists []*Plist
|
||||
Sym_div *LSym
|
||||
Sym_divu *LSym
|
||||
Sym_mod *LSym
|
||||
Sym_modu *LSym
|
||||
Plan9privates *LSym
|
||||
Curp *Prog
|
||||
Printp *Prog
|
||||
Blitrl *Prog
|
||||
Elitrl *Prog
|
||||
Rexflag int
|
||||
Vexflag int
|
||||
Rep int
|
||||
Repn int
|
||||
Lock int
|
||||
Asmode int
|
||||
AsmBuf AsmBuf // instruction buffer for x86
|
||||
Instoffset int64
|
||||
Autosize int32
|
||||
Armsize int32
|
||||
Pc int64
|
||||
DiagFunc func(string, ...interface{})
|
||||
Mode int
|
||||
Cursym *LSym
|
||||
Version int
|
||||
Errors int
|
||||
|
||||
Framepointer_enabled bool
|
||||
|
||||
// state for writing objects
|
||||
Text []*LSym
|
||||
Data []*LSym
|
||||
|
||||
// Cache of Progs
|
||||
allocIdx int
|
||||
progs [10000]Prog
|
||||
}
|
||||
|
||||
func (ctxt *Link) Diag(format string, args ...interface{}) {
|
||||
ctxt.Errors++
|
||||
ctxt.DiagFunc(format, args...)
|
||||
}
|
||||
|
||||
func (ctxt *Link) Logf(format string, args ...interface{}) {
|
||||
fmt.Fprintf(ctxt.Bso, format, args...)
|
||||
ctxt.Bso.Flush()
|
||||
}
|
||||
|
||||
// The smallest possible offset from the hardware stack pointer to a local
|
||||
// variable on the stack. Architectures that use a link register save its value
|
||||
// on the stack in the function prologue and so always have a pointer between
|
||||
// the hardware stack pointer and the local variable area.
|
||||
func (ctxt *Link) FixedFrameSize() int64 {
|
||||
switch ctxt.Arch.Family {
|
||||
case sys.AMD64, sys.I386:
|
||||
return 0
|
||||
case sys.PPC64:
|
||||
// PIC code on ppc64le requires 32 bytes of stack, and it's easier to
|
||||
// just use that much stack always on ppc64x.
|
||||
return int64(4 * ctxt.Arch.PtrSize)
|
||||
default:
|
||||
return int64(ctxt.Arch.PtrSize)
|
||||
}
|
||||
}
|
||||
|
||||
type SymVer struct {
|
||||
Name string
|
||||
Version int // TODO: make int16 to match LSym.Version?
|
||||
}
|
||||
|
||||
// LinkArch is the definition of a single architecture.
|
||||
type LinkArch struct {
|
||||
*sys.Arch
|
||||
Preprocess func(*Link, *LSym)
|
||||
Assemble func(*Link, *LSym)
|
||||
Follow func(*Link, *LSym)
|
||||
Progedit func(*Link, *Prog)
|
||||
UnaryDst map[As]bool // Instruction takes one operand, a destination.
|
||||
}
|
||||
|
||||
// HeadType is the executable header type.
|
||||
type HeadType uint8
|
||||
|
||||
const (
|
||||
Hunknown HeadType = iota
|
||||
Hdarwin
|
||||
Hdragonfly
|
||||
Hfreebsd
|
||||
Hlinux
|
||||
Hnacl
|
||||
Hnetbsd
|
||||
Hopenbsd
|
||||
Hplan9
|
||||
Hsolaris
|
||||
Hwindows
|
||||
Hwindowsgui
|
||||
)
|
||||
|
||||
func (h *HeadType) Set(s string) error {
|
||||
switch s {
|
||||
case "darwin":
|
||||
*h = Hdarwin
|
||||
case "dragonfly":
|
||||
*h = Hdragonfly
|
||||
case "freebsd":
|
||||
*h = Hfreebsd
|
||||
case "linux", "android":
|
||||
*h = Hlinux
|
||||
case "nacl":
|
||||
*h = Hnacl
|
||||
case "netbsd":
|
||||
*h = Hnetbsd
|
||||
case "openbsd":
|
||||
*h = Hopenbsd
|
||||
case "plan9":
|
||||
*h = Hplan9
|
||||
case "solaris":
|
||||
*h = Hsolaris
|
||||
case "windows":
|
||||
*h = Hwindows
|
||||
case "windowsgui":
|
||||
*h = Hwindowsgui
|
||||
default:
|
||||
return fmt.Errorf("invalid headtype: %q", s)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *HeadType) String() string {
|
||||
switch *h {
|
||||
case Hdarwin:
|
||||
return "darwin"
|
||||
case Hdragonfly:
|
||||
return "dragonfly"
|
||||
case Hfreebsd:
|
||||
return "freebsd"
|
||||
case Hlinux:
|
||||
return "linux"
|
||||
case Hnacl:
|
||||
return "nacl"
|
||||
case Hnetbsd:
|
||||
return "netbsd"
|
||||
case Hopenbsd:
|
||||
return "openbsd"
|
||||
case Hplan9:
|
||||
return "plan9"
|
||||
case Hsolaris:
|
||||
return "solaris"
|
||||
case Hwindows:
|
||||
return "windows"
|
||||
case Hwindowsgui:
|
||||
return "windowsgui"
|
||||
}
|
||||
return fmt.Sprintf("HeadType(%d)", *h)
|
||||
}
|
||||
|
||||
// AsmBuf is a simple buffer to assemble variable-length x86 instructions into.
|
||||
type AsmBuf struct {
|
||||
buf [100]byte
|
||||
off int
|
||||
}
|
||||
|
||||
// Put1 appends one byte to the end of the buffer.
|
||||
func (a *AsmBuf) Put1(x byte) {
|
||||
a.buf[a.off] = x
|
||||
a.off++
|
||||
}
|
||||
|
||||
// Put2 appends two bytes to the end of the buffer.
|
||||
func (a *AsmBuf) Put2(x, y byte) {
|
||||
a.buf[a.off+0] = x
|
||||
a.buf[a.off+1] = y
|
||||
a.off += 2
|
||||
}
|
||||
|
||||
// Put3 appends three bytes to the end of the buffer.
|
||||
func (a *AsmBuf) Put3(x, y, z byte) {
|
||||
a.buf[a.off+0] = x
|
||||
a.buf[a.off+1] = y
|
||||
a.buf[a.off+2] = z
|
||||
a.off += 3
|
||||
}
|
||||
|
||||
// Put4 appends four bytes to the end of the buffer.
|
||||
func (a *AsmBuf) Put4(x, y, z, w byte) {
|
||||
a.buf[a.off+0] = x
|
||||
a.buf[a.off+1] = y
|
||||
a.buf[a.off+2] = z
|
||||
a.buf[a.off+3] = w
|
||||
a.off += 4
|
||||
}
|
||||
|
||||
// PutInt16 writes v into the buffer using little-endian encoding.
|
||||
func (a *AsmBuf) PutInt16(v int16) {
|
||||
a.buf[a.off+0] = byte(v)
|
||||
a.buf[a.off+1] = byte(v >> 8)
|
||||
a.off += 2
|
||||
}
|
||||
|
||||
// PutInt32 writes v into the buffer using little-endian encoding.
|
||||
func (a *AsmBuf) PutInt32(v int32) {
|
||||
a.buf[a.off+0] = byte(v)
|
||||
a.buf[a.off+1] = byte(v >> 8)
|
||||
a.buf[a.off+2] = byte(v >> 16)
|
||||
a.buf[a.off+3] = byte(v >> 24)
|
||||
a.off += 4
|
||||
}
|
||||
|
||||
// PutInt64 writes v into the buffer using little-endian encoding.
|
||||
func (a *AsmBuf) PutInt64(v int64) {
|
||||
a.buf[a.off+0] = byte(v)
|
||||
a.buf[a.off+1] = byte(v >> 8)
|
||||
a.buf[a.off+2] = byte(v >> 16)
|
||||
a.buf[a.off+3] = byte(v >> 24)
|
||||
a.buf[a.off+4] = byte(v >> 32)
|
||||
a.buf[a.off+5] = byte(v >> 40)
|
||||
a.buf[a.off+6] = byte(v >> 48)
|
||||
a.buf[a.off+7] = byte(v >> 56)
|
||||
a.off += 8
|
||||
}
|
||||
|
||||
// Put copies b into the buffer.
|
||||
func (a *AsmBuf) Put(b []byte) {
|
||||
copy(a.buf[a.off:], b)
|
||||
a.off += len(b)
|
||||
}
|
||||
|
||||
// Insert inserts b at offset i.
|
||||
func (a *AsmBuf) Insert(i int, b byte) {
|
||||
a.off++
|
||||
copy(a.buf[i+1:a.off], a.buf[i:a.off-1])
|
||||
a.buf[i] = b
|
||||
}
|
||||
|
||||
// Last returns the byte at the end of the buffer.
|
||||
func (a *AsmBuf) Last() byte { return a.buf[a.off-1] }
|
||||
|
||||
// Len returns the length of the buffer.
|
||||
func (a *AsmBuf) Len() int { return a.off }
|
||||
|
||||
// Bytes returns the contents of the buffer.
|
||||
func (a *AsmBuf) Bytes() []byte { return a.buf[:a.off] }
|
||||
|
||||
// Reset empties the buffer.
|
||||
func (a *AsmBuf) Reset() { a.off = 0 }
|
||||
|
||||
// Peek returns the byte at offset i.
|
||||
func (a *AsmBuf) Peek(i int) byte { return a.buf[i] }
|
|
@ -0,0 +1,375 @@
|
|||
// cmd/9c/9.out.h from Vita Nuova.
|
||||
//
|
||||
// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
|
||||
// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
|
||||
// Portions Copyright © 1997-1999 Vita Nuova Limited
|
||||
// Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com)
|
||||
// Portions Copyright © 2004,2006 Bruce Ellis
|
||||
// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
|
||||
// Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others
|
||||
// Portions Copyright © 2009 The Go Authors. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package mips
|
||||
|
||||
import "github.com/google/gops/internal/obj"
|
||||
|
||||
//go:generate go run ../stringer.go -i $GOFILE -o anames.go -p mips
|
||||
|
||||
/*
|
||||
* mips 64
|
||||
*/
|
||||
const (
|
||||
NSNAME = 8
|
||||
NSYM = 50
|
||||
NREG = 32 /* number of general registers */
|
||||
NFREG = 32 /* number of floating point registers */
|
||||
)
|
||||
|
||||
const (
|
||||
REG_R0 = obj.RBaseMIPS64 + iota
|
||||
REG_R1
|
||||
REG_R2
|
||||
REG_R3
|
||||
REG_R4
|
||||
REG_R5
|
||||
REG_R6
|
||||
REG_R7
|
||||
REG_R8
|
||||
REG_R9
|
||||
REG_R10
|
||||
REG_R11
|
||||
REG_R12
|
||||
REG_R13
|
||||
REG_R14
|
||||
REG_R15
|
||||
REG_R16
|
||||
REG_R17
|
||||
REG_R18
|
||||
REG_R19
|
||||
REG_R20
|
||||
REG_R21
|
||||
REG_R22
|
||||
REG_R23
|
||||
REG_R24
|
||||
REG_R25
|
||||
REG_R26
|
||||
REG_R27
|
||||
REG_R28
|
||||
REG_R29
|
||||
REG_R30
|
||||
REG_R31
|
||||
|
||||
REG_F0
|
||||
REG_F1
|
||||
REG_F2
|
||||
REG_F3
|
||||
REG_F4
|
||||
REG_F5
|
||||
REG_F6
|
||||
REG_F7
|
||||
REG_F8
|
||||
REG_F9
|
||||
REG_F10
|
||||
REG_F11
|
||||
REG_F12
|
||||
REG_F13
|
||||
REG_F14
|
||||
REG_F15
|
||||
REG_F16
|
||||
REG_F17
|
||||
REG_F18
|
||||
REG_F19
|
||||
REG_F20
|
||||
REG_F21
|
||||
REG_F22
|
||||
REG_F23
|
||||
REG_F24
|
||||
REG_F25
|
||||
REG_F26
|
||||
REG_F27
|
||||
REG_F28
|
||||
REG_F29
|
||||
REG_F30
|
||||
REG_F31
|
||||
|
||||
REG_HI
|
||||
REG_LO
|
||||
|
||||
// co-processor 0 control registers
|
||||
REG_M0
|
||||
REG_M1
|
||||
REG_M2
|
||||
REG_M3
|
||||
REG_M4
|
||||
REG_M5
|
||||
REG_M6
|
||||
REG_M7
|
||||
REG_M8
|
||||
REG_M9
|
||||
REG_M10
|
||||
REG_M11
|
||||
REG_M12
|
||||
REG_M13
|
||||
REG_M14
|
||||
REG_M15
|
||||
REG_M16
|
||||
REG_M17
|
||||
REG_M18
|
||||
REG_M19
|
||||
REG_M20
|
||||
REG_M21
|
||||
REG_M22
|
||||
REG_M23
|
||||
REG_M24
|
||||
REG_M25
|
||||
REG_M26
|
||||
REG_M27
|
||||
REG_M28
|
||||
REG_M29
|
||||
REG_M30
|
||||
REG_M31
|
||||
|
||||
// FPU control registers
|
||||
REG_FCR0
|
||||
REG_FCR1
|
||||
REG_FCR2
|
||||
REG_FCR3
|
||||
REG_FCR4
|
||||
REG_FCR5
|
||||
REG_FCR6
|
||||
REG_FCR7
|
||||
REG_FCR8
|
||||
REG_FCR9
|
||||
REG_FCR10
|
||||
REG_FCR11
|
||||
REG_FCR12
|
||||
REG_FCR13
|
||||
REG_FCR14
|
||||
REG_FCR15
|
||||
REG_FCR16
|
||||
REG_FCR17
|
||||
REG_FCR18
|
||||
REG_FCR19
|
||||
REG_FCR20
|
||||
REG_FCR21
|
||||
REG_FCR22
|
||||
REG_FCR23
|
||||
REG_FCR24
|
||||
REG_FCR25
|
||||
REG_FCR26
|
||||
REG_FCR27
|
||||
REG_FCR28
|
||||
REG_FCR29
|
||||
REG_FCR30
|
||||
REG_FCR31
|
||||
|
||||
REG_LAST = REG_FCR31 // the last defined register
|
||||
|
||||
REG_SPECIAL = REG_M0
|
||||
|
||||
REGZERO = REG_R0 /* set to zero */
|
||||
REGSP = REG_R29
|
||||
REGSB = REG_R28
|
||||
REGLINK = REG_R31
|
||||
REGRET = REG_R1
|
||||
REGARG = -1 /* -1 disables passing the first argument in register */
|
||||
REGRT1 = REG_R1 /* reserved for runtime, duffzero and duffcopy */
|
||||
REGRT2 = REG_R2 /* reserved for runtime, duffcopy */
|
||||
REGCTXT = REG_R22 /* context for closures */
|
||||
REGG = REG_R30 /* G */
|
||||
REGTMP = REG_R23 /* used by the linker */
|
||||
FREGRET = REG_F0
|
||||
)
|
||||
|
||||
const (
|
||||
BIG = 32766
|
||||
)
|
||||
|
||||
const (
|
||||
/* mark flags */
|
||||
FOLL = 1 << 0
|
||||
LABEL = 1 << 1
|
||||
LEAF = 1 << 2
|
||||
SYNC = 1 << 3
|
||||
BRANCH = 1 << 4
|
||||
LOAD = 1 << 5
|
||||
FCMP = 1 << 6
|
||||
NOSCHED = 1 << 7
|
||||
|
||||
NSCHED = 20
|
||||
)
|
||||
|
||||
const (
|
||||
C_NONE = iota
|
||||
C_REG
|
||||
C_FREG
|
||||
C_FCREG
|
||||
C_MREG /* special processor register */
|
||||
C_HI
|
||||
C_LO
|
||||
C_ZCON
|
||||
C_SCON /* 16 bit signed */
|
||||
C_UCON /* 32 bit signed, low 16 bits 0 */
|
||||
C_ADD0CON
|
||||
C_AND0CON
|
||||
C_ADDCON /* -0x8000 <= v < 0 */
|
||||
C_ANDCON /* 0 < v <= 0xFFFF */
|
||||
C_LCON /* other 32 */
|
||||
C_DCON /* other 64 (could subdivide further) */
|
||||
C_SACON /* $n(REG) where n <= int16 */
|
||||
C_SECON
|
||||
C_LACON /* $n(REG) where int16 < n <= int32 */
|
||||
C_LECON
|
||||
C_DACON /* $n(REG) where int32 < n */
|
||||
C_STCON /* $tlsvar */
|
||||
C_SBRA
|
||||
C_LBRA
|
||||
C_SAUTO
|
||||
C_LAUTO
|
||||
C_SEXT
|
||||
C_LEXT
|
||||
C_ZOREG
|
||||
C_SOREG
|
||||
C_LOREG
|
||||
C_GOK
|
||||
C_ADDR
|
||||
C_TLS
|
||||
C_TEXTSIZE
|
||||
|
||||
C_NCLASS /* must be the last */
|
||||
)
|
||||
|
||||
const (
|
||||
AABSD = obj.ABaseMIPS64 + obj.A_ARCHSPECIFIC + iota
|
||||
AABSF
|
||||
AABSW
|
||||
AADD
|
||||
AADDD
|
||||
AADDF
|
||||
AADDU
|
||||
AADDW
|
||||
AAND
|
||||
ABEQ
|
||||
ABFPF
|
||||
ABFPT
|
||||
ABGEZ
|
||||
ABGEZAL
|
||||
ABGTZ
|
||||
ABLEZ
|
||||
ABLTZ
|
||||
ABLTZAL
|
||||
ABNE
|
||||
ABREAK
|
||||
ACMPEQD
|
||||
ACMPEQF
|
||||
ACMPGED
|
||||
ACMPGEF
|
||||
ACMPGTD
|
||||
ACMPGTF
|
||||
ADIV
|
||||
ADIVD
|
||||
ADIVF
|
||||
ADIVU
|
||||
ADIVW
|
||||
AGOK
|
||||
ALUI
|
||||
AMOVB
|
||||
AMOVBU
|
||||
AMOVD
|
||||
AMOVDF
|
||||
AMOVDW
|
||||
AMOVF
|
||||
AMOVFD
|
||||
AMOVFW
|
||||
AMOVH
|
||||
AMOVHU
|
||||
AMOVW
|
||||
AMOVWD
|
||||
AMOVWF
|
||||
AMOVWL
|
||||
AMOVWR
|
||||
AMUL
|
||||
AMULD
|
||||
AMULF
|
||||
AMULU
|
||||
AMULW
|
||||
ANEGD
|
||||
ANEGF
|
||||
ANEGW
|
||||
ANOR
|
||||
AOR
|
||||
AREM
|
||||
AREMU
|
||||
ARFE
|
||||
ASGT
|
||||
ASGTU
|
||||
ASLL
|
||||
ASRA
|
||||
ASRL
|
||||
ASUB
|
||||
ASUBD
|
||||
ASUBF
|
||||
ASUBU
|
||||
ASUBW
|
||||
ASYSCALL
|
||||
ATLBP
|
||||
ATLBR
|
||||
ATLBWI
|
||||
ATLBWR
|
||||
AWORD
|
||||
AXOR
|
||||
|
||||
/* 64-bit */
|
||||
AMOVV
|
||||
AMOVVL
|
||||
AMOVVR
|
||||
ASLLV
|
||||
ASRAV
|
||||
ASRLV
|
||||
ADIVV
|
||||
ADIVVU
|
||||
AREMV
|
||||
AREMVU
|
||||
AMULV
|
||||
AMULVU
|
||||
AADDV
|
||||
AADDVU
|
||||
ASUBV
|
||||
ASUBVU
|
||||
|
||||
/* 64-bit FP */
|
||||
ATRUNCFV
|
||||
ATRUNCDV
|
||||
ATRUNCFW
|
||||
ATRUNCDW
|
||||
AMOVWU
|
||||
AMOVFV
|
||||
AMOVDV
|
||||
AMOVVF
|
||||
AMOVVD
|
||||
|
||||
ALAST
|
||||
|
||||
// aliases
|
||||
AJMP = obj.AJMP
|
||||
AJAL = obj.ACALL
|
||||
ARET = obj.ARET
|
||||
)
|
|
@ -0,0 +1,113 @@
|
|||
// Generated by stringer -i a.out.go -o anames.go -p mips
|
||||
// Do not edit.
|
||||
|
||||
package mips
|
||||
|
||||
import "github.com/google/gops/internal/obj"
|
||||
|
||||
var Anames = []string{
|
||||
obj.A_ARCHSPECIFIC: "ABSD",
|
||||
"ABSF",
|
||||
"ABSW",
|
||||
"ADD",
|
||||
"ADDD",
|
||||
"ADDF",
|
||||
"ADDU",
|
||||
"ADDW",
|
||||
"AND",
|
||||
"BEQ",
|
||||
"BFPF",
|
||||
"BFPT",
|
||||
"BGEZ",
|
||||
"BGEZAL",
|
||||
"BGTZ",
|
||||
"BLEZ",
|
||||
"BLTZ",
|
||||
"BLTZAL",
|
||||
"BNE",
|
||||
"BREAK",
|
||||
"CMPEQD",
|
||||
"CMPEQF",
|
||||
"CMPGED",
|
||||
"CMPGEF",
|
||||
"CMPGTD",
|
||||
"CMPGTF",
|
||||
"DIV",
|
||||
"DIVD",
|
||||
"DIVF",
|
||||
"DIVU",
|
||||
"DIVW",
|
||||
"GOK",
|
||||
"LUI",
|
||||
"MOVB",
|
||||
"MOVBU",
|
||||
"MOVD",
|
||||
"MOVDF",
|
||||
"MOVDW",
|
||||
"MOVF",
|
||||
"MOVFD",
|
||||
"MOVFW",
|
||||
"MOVH",
|
||||
"MOVHU",
|
||||
"MOVW",
|
||||
"MOVWD",
|
||||
"MOVWF",
|
||||
"MOVWL",
|
||||
"MOVWR",
|
||||
"MUL",
|
||||
"MULD",
|
||||
"MULF",
|
||||
"MULU",
|
||||
"MULW",
|
||||
"NEGD",
|
||||
"NEGF",
|
||||
"NEGW",
|
||||
"NOR",
|
||||
"OR",
|
||||
"REM",
|
||||
"REMU",
|
||||
"RFE",
|
||||
"SGT",
|
||||
"SGTU",
|
||||
"SLL",
|
||||
"SRA",
|
||||
"SRL",
|
||||
"SUB",
|
||||
"SUBD",
|
||||
"SUBF",
|
||||
"SUBU",
|
||||
"SUBW",
|
||||
"SYSCALL",
|
||||
"TLBP",
|
||||
"TLBR",
|
||||
"TLBWI",
|
||||
"TLBWR",
|
||||
"WORD",
|
||||
"XOR",
|
||||
"MOVV",
|
||||
"MOVVL",
|
||||
"MOVVR",
|
||||
"SLLV",
|
||||
"SRAV",
|
||||
"SRLV",
|
||||
"DIVV",
|
||||
"DIVVU",
|
||||
"REMV",
|
||||
"REMVU",
|
||||
"MULV",
|
||||
"MULVU",
|
||||
"ADDV",
|
||||
"ADDVU",
|
||||
"SUBV",
|
||||
"SUBVU",
|
||||
"TRUNCFV",
|
||||
"TRUNCDV",
|
||||
"TRUNCFW",
|
||||
"TRUNCDW",
|
||||
"MOVWU",
|
||||
"MOVFV",
|
||||
"MOVDV",
|
||||
"MOVVF",
|
||||
"MOVVD",
|
||||
"LAST",
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
// 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.
|
||||
|
||||
package mips
|
||||
|
||||
var cnames0 = []string{
|
||||
"NONE",
|
||||
"REG",
|
||||
"FREG",
|
||||
"FCREG",
|
||||
"MREG",
|
||||
"HI",
|
||||
"LO",
|
||||
"ZCON",
|
||||
"SCON",
|
||||
"UCON",
|
||||
"ADD0CON",
|
||||
"AND0CON",
|
||||
"ADDCON",
|
||||
"ANDCON",
|
||||
"LCON",
|
||||
"DCON",
|
||||
"SACON",
|
||||
"SECON",
|
||||
"LACON",
|
||||
"LECON",
|
||||
"DACON",
|
||||
"STCON",
|
||||
"SBRA",
|
||||
"LBRA",
|
||||
"SAUTO",
|
||||
"LAUTO",
|
||||
"SEXT",
|
||||
"LEXT",
|
||||
"ZOREG",
|
||||
"SOREG",
|
||||
"LOREG",
|
||||
"GOK",
|
||||
"ADDR",
|
||||
"TLS",
|
||||
"TEXTSIZE",
|
||||
"NCLASS",
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,85 @@
|
|||
// cmd/9l/list.c from Vita Nuova.
|
||||
//
|
||||
// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
|
||||
// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
|
||||
// Portions Copyright © 1997-1999 Vita Nuova Limited
|
||||
// Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com)
|
||||
// Portions Copyright © 2004,2006 Bruce Ellis
|
||||
// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
|
||||
// Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others
|
||||
// Portions Copyright © 2009 The Go Authors. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
package mips
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/google/gops/internal/obj"
|
||||
)
|
||||
|
||||
func init() {
|
||||
obj.RegisterRegister(obj.RBaseMIPS64, REG_LAST&^1023+1024, Rconv)
|
||||
obj.RegisterOpcode(obj.ABaseMIPS64, Anames)
|
||||
}
|
||||
|
||||
func Rconv(r int) string {
|
||||
if r == 0 {
|
||||
return "NONE"
|
||||
}
|
||||
if r == REGG {
|
||||
// Special case.
|
||||
return "g"
|
||||
}
|
||||
if r == REGSB {
|
||||
// Special case.
|
||||
return "RSB"
|
||||
}
|
||||
if REG_R0 <= r && r <= REG_R31 {
|
||||
return fmt.Sprintf("R%d", r-REG_R0)
|
||||
}
|
||||
if REG_F0 <= r && r <= REG_F31 {
|
||||
return fmt.Sprintf("F%d", r-REG_F0)
|
||||
}
|
||||
if REG_M0 <= r && r <= REG_M31 {
|
||||
return fmt.Sprintf("M%d", r-REG_M0)
|
||||
}
|
||||
if REG_FCR0 <= r && r <= REG_FCR31 {
|
||||
return fmt.Sprintf("FCR%d", r-REG_FCR0)
|
||||
}
|
||||
if r == REG_HI {
|
||||
return "HI"
|
||||
}
|
||||
if r == REG_LO {
|
||||
return "LO"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Rgok(%d)", r-obj.RBaseMIPS64)
|
||||
}
|
||||
|
||||
func DRconv(a int) string {
|
||||
s := "C_??"
|
||||
if a >= C_NONE && a <= C_NCLASS {
|
||||
s = cnames0[a]
|
||||
}
|
||||
var fp string
|
||||
fp += s
|
||||
return fp
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue