Test enhancement (#133)

* test-enhancement

* test-enhancement

* test-enhancement

test-enhancement

test-enhancement

test-enhancement

test-enhancement

test-enhancement

test-enhancement

test-enhancement

test-enhancement

test-enhancement

test-enhancement

test-enhancement

test-enhancement

test-enhancement

test-enhancement

modify get_started

modify get started

modify get started

modify

* modify base_test to resolve conflicts

* test-tmp

* modify config

* modify config

* imeplement nulllogger to bypass go vet check

* add test for du

* add test for count

* use make to generate docs

* replace hook with jmp to enable closure

go fmt

* verify-bug

* unhook exec after tests to facilitate following tests

* wrap unhook to pass golint check
This commit is contained in:
biubiu-biub 2020-09-14 14:34:08 +08:00 committed by GitHub
parent 0c379995f0
commit 81006179dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
78 changed files with 36446 additions and 56 deletions

View File

@ -6,18 +6,26 @@ matrix:
os:
- linux
go_import_path: github.com/fluid-cloudnative/fluid
sudo: false
#for the convenience of gohook to set gcflag here
env:
- TEST_FLAGS='-race -coverprofile=coverage.txt -covermode=atomic -gcflags=-l'
sudo: true
before_script:
- curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin latest
- arch=$(go env GOARCH)
- curl -L https://go.kubebuilder.io/dl/2.3.1/linux/${arch} | tar -xz -C /tmp/
- sudo mv /tmp/kubebuilder_2.3.1_linux_${arch} /usr/local/kubebuilder
- export PATH=$PATH:/usr/local/kubebuilder/bin
script:
- CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=off go build -o bin/manager cmd/controller/main.go
- CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=off go build -o bin/csi cmd/csi/main.go
- CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=off go vet ./...
- golangci-lint run --timeout=10m ./...
- test -z "$(go fmt ./... 2>/dev/null | tee /dev/stderr)" || (echo "please format Go code with 'gofmt'")
- TEST_FLAGS='-race -coverprofile=coverage.txt -covermode=atomic' make unit-test
- go test ./... ${TEST_FLAGS}
- bash <(curl -s https://codecov.io/bash)
- language: ruby
rvm:
- 2.6

View File

@ -28,6 +28,7 @@ all: manager
test: generate fmt vet manifests
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=off go list ./... | grep -v controller | xargs go test -coverprofile cover.out
# used in CI and simply ignore controller tests which need k8s now.
# maybe incompatible if more end to end tests are added.
unit-test: generate fmt vet manifests
@ -72,7 +73,7 @@ fmt:
# Run go vet against code
vet:
GO111MODULE=off go vet ./...
GO111MODULE=off go list ./... | grep -v "vendor" | xargs go vet
# Generate code
generate: controller-gen

18
docs/Makefile Normal file
View File

@ -0,0 +1,18 @@
api:
cp zh/dev/api_doc.md zh/dev/api_doc.html;
merge:
python3 scripts/mergeByTOC.py zh/
python3 scripts/mergeByTOC.py en/
md2pdf:merge
./scripts/genDoc.sh
api2pdf:api
xvfb-run wkhtmltopdf zh/dev/api_doc.html api.pdf
build:md2pdf api2pdf
python3 scripts/mergePDF.py zh;
python3 scripts/mergePDF.py en
clean:
rm api.pdf;
rm output*.pdf;
rm en/doc.md;rm zh/doc.md;
rm zh/dev/api_doc.html;

View File

@ -20,8 +20,12 @@ Before generating,we suppose you have installed [Docker](https://www.docker.com/
don't have to install required tools one by one.
1. Download Required Docker Image
`docker pull registry.cn-hangzhou.aliyuncs.com/docs-fluid/doc-build `
`docker pull registry.cn-hangzhou.aliyuncs.com/docs-fluid/doc-build `
2. Start a Container
`docker run -it -v <your fluid/docs path>:/data/ fluid/doc-build:0.2.0`
3. Run Build script
`data/bluid.sh`
`docker run -it -v <your fluid/docs path>:/data/ fluid/doc-build:0.2.0`
3. Run Makefile
```shell
cd data
make build
make clean
```

View File

@ -18,8 +18,12 @@
目前我们提供了脚本以便用户自行生成PDF格式的文档。为了避免你配置生成环境我们提供了Docker镜像所以生成文档前请确认你安装
了[Docker](https://www.docker.com/)。
1. 获取Docker镜像
`docker pull registry.cn-hangzhou.aliyuncs.com/docs-fluid/doc-build `
2. 创建容器
`docker run -it -v <your fluid/docs path>:/data/ fluid/doc-build:0.2.0`
3. 执行脚本
`data/bluid.sh`
`docker pull registry.cn-hangzhou.aliyuncs.com/docs-fluid/doc-build `
2. 创建容器
`docker run -it -v <your fluid/docs path>:/data/ fluid/doc-build:0.2.0`
3. 执行Makefile
```shell
cd data
make build
make clean
```

View File

@ -53,7 +53,8 @@ This document mainly describes how to deploy Fluid with Helm, and use Fluid to c
Fluid provides cloud-native data acceleration and management capabilities, and use *dataset* as a high-level abstraction to facilitate user management. Here we will show you how to create a dataset with Fluid.
1. Create a Dataset object through the CRD file, which describes the source of the dataset.
```yaml
```shell
$ cat<<EOF >dataset.yaml
apiVersion: data.fluid.io/v1alpha1
kind: Dataset
metadata:
@ -62,15 +63,16 @@ Fluid provides cloud-native data acceleration and management capabilities, and u
mounts:
- mountPoint: https://mirror.bit.edu.cn/apache/spark/spark-3.0.0/
name: spark
EOF
```
Create dataset with kubectl
```shell
kubectl create -f dataset.yaml
```
2. Create an `AlluxioRuntime` CRD object to support the dataset we created. We use [Alluxio](https://www.alluxio.io/) as its runtime here.
```yaml
```shell
$ cat<<EOF >runtime.yaml
apiVersion: data.fluid.io/v1alpha1
kind: AlluxioRuntime
metadata:
@ -106,6 +108,7 @@ Fluid provides cloud-native data acceleration and management capabilities, and u
args:
- fuse
- --fuse-opts=direct_io,ro,max_read=131072
EOF
```
Create *Alluxio* Runtime with `kubectl`
@ -116,7 +119,8 @@ Fluid provides cloud-native data acceleration and management capabilities, and u
3. Next, we create an application to access this dataset. Here we will access the same data multiple times and compare the time consumed by each access.
```yaml
```shell
$ cat<<EOF >app.yaml
apiVersion: v1
kind: Pod
metadata:
@ -132,20 +136,21 @@ Fluid provides cloud-native data acceleration and management capabilities, and u
- name: demo
persistentVolumeClaim:
claimName: demo
EOF
```
Create Pod with `kubectl`
```shell
kubectl create -f app.yaml
$ kubectl create -f app.yaml
```
4. Dive into the container to access data, the first access will take longer.
```
kubectl exec -it demo-app -- bash
# du -sh /data/spark/spark-3.0.0-bin-without-hadoop.tgz
$ kubectl exec -it demo-app -- bash
$ du -sh /data/spark/spark-3.0.0-bin-without-hadoop.tgz
150M /data/spark/spark-3.0.0-bin-without-hadoop.tgz
# time cp /data/spark/spark-3.0.0-bin-without-hadoop.tgz /dev/null
$ time cp /data/spark/spark-3.0.0-bin-without-hadoop.tgz /dev/null
real 0m13.171s
user 0m0.002s
sys 0m0.028s
@ -153,9 +158,9 @@ Fluid provides cloud-native data acceleration and management capabilities, and u
5. In order to avoid the influence of other factors like page cache, we will delete the previous container, create the same application, and try to access the same file. Since the file has been cached by alluxio at this time, you can see that it takes significantly less time now.
```
kubectl delete -f app.yaml && kubectl create -f app.yaml
...
# time cp /data/spark/spark-3.0.0-bin-without-hadoop.tgz /dev/null
$ kubectl delete -f app.yaml && kubectl create -f app.yaml
$ kubectl exec -it demo-app -- bash
$ time cp /data/spark/spark-3.0.0-bin-without-hadoop.tgz /dev/null
real 0m0.344s
user 0m0.002s
sys 0m0.020s

View File

@ -53,7 +53,8 @@
Fluid提供了云原生的数据加速和管理能力并抽象出了`数据集(Dataset)`概念方便用户管理,接下来将演示如何用 Fluid 创建一个数据集。
1. 创建一个Dataset CRD对象其中描述了数据集的来源。
```yaml
```shell
$ cat<<EOF >dataset.yaml
apiVersion: data.fluid.io/v1alpha1
kind: Dataset
metadata:
@ -62,15 +63,17 @@ Fluid提供了云原生的数据加速和管理能力并抽象出了`数据
mounts:
- mountPoint: https://mirror.bit.edu.cn/apache/spark/spark-3.0.0/
name: spark
EOF
```
执行安装
```
kubectl create -f dataset.yaml
$ kubectl create -f dataset.yaml
```
2. 创建 `AlluxioRuntime` CRD对象用来描述支持这个数据集的 Runtime, 在这里我们使用[Alluxio](https://www.alluxio.io/)作为其Runtime
```yaml
```shell
$ cat<<EOF >runtime.yaml
apiVersion: data.fluid.io/v1alpha1
kind: AlluxioRuntime
metadata:
@ -106,15 +109,17 @@ Fluid提供了云原生的数据加速和管理能力并抽象出了`数据
args:
- fuse
- --fuse-opts=direct_io,ro,max_read=131072
EOF
```
使用`kubectl`完成创建
```shell
kubectl create -f runtime.yaml
$ kubectl create -f runtime.yaml
```
3. 接下来,我们创建一个应用容器来使用该数据集,我们将多次访问同一数据,并比较访问时间来展示 Fluid 的加速效果。
```yaml
```shell
$ cat<<EOF >app.yaml
apiVersion: v1
kind: Pod
metadata:
@ -130,14 +135,20 @@ Fluid提供了云原生的数据加速和管理能力并抽象出了`数据
- name: demo
persistentVolumeClaim:
claimName: demo
EOF
```
使用`kubectl`完成创建
```shell
$ kubectl create -f app.yaml
```
4. 登录到应用容器中访问数据,初次访问会花费更长时间。
```shell
kubectl exec -it demo-app -- bash
# du -sh /data/spark/spark-3.0.0-bin-without-hadoop.tgz
$ kubectl exec -it demo-app -- bash
$ du -sh /data/spark/spark-3.0.0-bin-without-hadoop.tgz
150M /data/spark/spark-3.0.0-bin-without-hadoop.tgz
# time cp /data/spark/spark-3.0.0-bin-without-hadoop.tgz /dev/null
$ time cp /data/spark/spark-3.0.0-bin-without-hadoop.tgz /dev/null
real 0m13.171s
user 0m0.002s
sys 0m0.028s
@ -145,9 +156,9 @@ Fluid提供了云原生的数据加速和管理能力并抽象出了`数据
5. 为了避免其他因素(比如 page cache )对结果造成影响,我们将删除之前的容器,新建相同的应用,尝试访问同样的文件。由于此时文件已经被 `Alluxio` 缓存,可以看到第二次访问所需时间远小于第一次。
```shell
kubectl delete -f app.yaml && kubectl create -f app.yaml
...
# time cp /data/spark/spark-3.0.0-bin-without-hadoop.tgz /dev/null
$ kubectl delete -f app.yaml && kubectl create -f app.yaml
$ kubectl exec -it demo-app -- bash
$ time cp /data/spark/spark-3.0.0-bin-without-hadoop.tgz /dev/null
real 0m0.344s
user 0m0.002s
sys 0m0.020s

1
go.mod
View File

@ -3,6 +3,7 @@ module github.com/fluid-cloudnative/fluid
go 1.13
require (
github.com/brahma-adshonor/gohook v1.1.9
github.com/container-storage-interface/spec v1.2.0
github.com/docker/go-units v0.4.0
github.com/go-logr/logr v0.1.0

4
go.sum
View File

@ -57,6 +57,8 @@ github.com/bifurcation/mint v0.0.0-20180715133206-93c51c6ce115/go.mod h1:zVt7zX3
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/brahma-adshonor/gohook v1.1.9 h1:YuQFj8rhAj1kvtGHUc5BlLvAELw98M/ydpBz/MvBXNU=
github.com/brahma-adshonor/gohook v1.1.9/go.mod h1:3B9f7Lwh7z5fW6MvUvGxCFaVdloVI+1tOsl4BMJdWr0=
github.com/caddyserver/caddy v1.0.3/go.mod h1:G+ouvOY32gENkJC+jhgl62TyhvqEsFaDiZ4uw0RzP1E=
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@ -530,6 +532,8 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
golang.org/x/arch v0.0.0-20190312162104-788fe5ffcd8c h1:Rx/HTKi09myZ25t1SOlDHmHOy/mKxNAcu0hP1oPX9qM=
golang.org/x/arch v0.0.0-20190312162104-788fe5ffcd8c/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
golang.org/x/build v0.0.0-20190927031335-2835ba2e683f/go.mod h1:fYw7AShPAhGMdXqA9gRadk/CcMsvLlClpE5oBwnS3dM=
golang.org/x/crypto v0.0.0-20180426230345-b49d69b5da94/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=

View File

@ -16,6 +16,7 @@ limitations under the License.
package alluxio
import (
"os"
"path/filepath"
"testing"
@ -39,6 +40,7 @@ import (
var cfg *rest.Config
var k8sClient client.Client
var testEnv *envtest.Environment
var useExistingCluster = false
func TestAPIs(t *testing.T) {
RegisterFailHandler(Fail)
@ -50,6 +52,9 @@ func TestAPIs(t *testing.T) {
var _ = BeforeSuite(func(done Done) {
logf.SetLogger(zap.LoggerTo(GinkgoWriter, true))
if env := os.Getenv("USE_EXISTING_CLUSTER"); env == "true" {
useExistingCluster = true
}
By("bootstrapping test environment")
testEnv = &envtest.Environment{

View File

@ -17,6 +17,7 @@ package dataset
import (
"context"
"os"
"path/filepath"
"testing"
@ -44,6 +45,7 @@ var cfg *rest.Config
var k8sClient client.Client
var testEnv *envtest.Environment
var testCtx = context.Background()
var useExistingCluster = false
func TestAPIs(t *testing.T) {
RegisterFailHandler(Fail)
@ -55,10 +57,13 @@ func TestAPIs(t *testing.T) {
var _ = BeforeSuite(func(done Done) {
logf.SetLogger(zap.LoggerTo(GinkgoWriter, true))
if env := os.Getenv("USE_EXISTING_CLUSTER"); env != "" {
useExistingCluster = true
}
By("bootstrapping test environment")
testEnv = &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "config", "crd", "bases")},
UseExistingCluster: &useExistingCluster,
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "config", "crd", "bases")},
}
var err error

View File

@ -56,7 +56,7 @@ func (a AlluxioFileUtils) IsExist(alluxioPath string) (found bool, err error) {
if strings.Contains(stdout, "does not exist") {
err = nil
} else {
err = fmt.Errorf("execute command %v with err: %v stdout %s and stderr %s", command, err, stdout, stderr)
err = fmt.Errorf("execute command %v with expectedErr: %v stdout %s and stderr %s", command, err, stdout, stderr)
return
}
} else {
@ -87,7 +87,7 @@ func (a AlluxioFileUtils) LoadMetaData(alluxioPath string, sync bool) (err error
duration := time.Since(start)
a.log.Info("Load MetaData took times to run", "period", duration)
if err != nil {
err = fmt.Errorf("execute command %v with err: %v stdout %s and stderr %s", command, err, stdout, stderr)
err = fmt.Errorf("execute command %v with expectedErr: %v stdout %s and stderr %s", command, err, stdout, stderr)
return
}
@ -103,7 +103,7 @@ func (a AlluxioFileUtils) Mkdir(alluxioPath string) (err error) {
stdout, stderr, err = a.exec(command, false)
if err != nil {
err = fmt.Errorf("execute command %v with err: %v stdout %s and stderr %s", command, err, stdout, stderr)
err = fmt.Errorf("execute command %v with expectedErr: %v stdout %s and stderr %s", command, err, stdout, stderr)
return
}
@ -116,15 +116,15 @@ func (a AlluxioFileUtils) Mount(alluxioPath string,
readOnly bool,
shared bool) (err error) {
// exist, err := a.IsExist(alluxioPath)
// if err != nil {
// return err
// exist, expectedErr := a.IsExist(alluxioPath)
// if expectedErr != nil {
// return expectedErr
// }
// if !exist {
// err = a.Mkdir(alluxioPath)
// if err != nil {
// return err
// expectedErr = a.Mkdir(alluxioPath)
// if expectedErr != nil {
// return expectedErr
// }
// }
@ -150,7 +150,7 @@ func (a AlluxioFileUtils) Mount(alluxioPath string,
stdout, stderr, err = a.exec(command, false)
if err != nil {
err = fmt.Errorf("execute command %v with err: %v stdout %s and stderr %s", command, err, stdout, stderr)
err = fmt.Errorf("execute command %v with expectedErr: %v stdout %s and stderr %s", command, err, stdout, stderr)
return
}
@ -166,7 +166,7 @@ func (a AlluxioFileUtils) IsMounted(alluxioPath string) (mounted bool, err error
stdout, stderr, err = a.exec(command, true)
if err != nil {
return mounted, fmt.Errorf("execute command %v with err: %v stdout %s and stderr %s", command, err, stdout, stderr)
return mounted, fmt.Errorf("execute command %v with expectedErr: %v stdout %s and stderr %s", command, err, stdout, stderr)
}
results := strings.Split(stdout, "\n")
@ -211,7 +211,7 @@ func (a AlluxioFileUtils) Du(alluxioPath string) (ufs int64, cached int64, cache
stdout, stderr, err = a.exec(command, false)
if err != nil {
err = fmt.Errorf("execute command %v with err: %v stdout %s and stderr %s", command, err, stdout, stderr)
err = fmt.Errorf("execute command %v with expectedErr: %v stdout %s and stderr %s", command, err, stdout, stderr)
return
}
str := strings.Split(stdout, "\n")
@ -254,7 +254,7 @@ func (a AlluxioFileUtils) Count(alluxioPath string) (fileCount int64, folderCoun
stdout, stderr, err = a.exec(command, false)
if err != nil {
err = fmt.Errorf("execute command %v with err: %v stdout %s and stderr %s", command, err, stdout, stderr)
err = fmt.Errorf("execute command %v with expectedErr: %v stdout %s and stderr %s", command, err, stdout, stderr)
return
}

View File

@ -16,12 +16,34 @@ limitations under the License.
package operations
import (
"testing"
"errors"
"fmt"
"github.com/brahma-adshonor/gohook"
"github.com/fluid-cloudnative/fluid/pkg/utils/kubeclient"
"github.com/go-logr/logr"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
"strings"
"testing"
)
const (
NOT_EXIST = "not-exist"
OTHER_ERR = "other-err"
FINE = "fine"
EXEC_ERR = "exec-err"
TOO_MANY_LINES = "too many lines"
DATA_NUM = "data nums not match"
PARSE_ERR = "parse err"
)
// a empty logger just for testing ...
type NullLogger struct{}
func (_ NullLogger) Info(_ string, _ ...interface{}) {
// Do nothing.
}
func TestLoadMetaData(t *testing.T) {
var tests = []struct {
path string
@ -37,9 +59,186 @@ func TestLoadMetaData(t *testing.T) {
for _, test := range tests {
tools := NewAlluxioFileUtils("", "", "", ctrl.Log)
err := tools.LoadMetaData(test.path, test.sync)
// fmt.Println(err)
// fmt.Println(expectedErr)
if err == nil {
t.Errorf("expected %v, got %v", test.path, tools)
}
}
}
func (_ NullLogger) Enabled() bool {
return false
}
func (_ NullLogger) Error(_ error, _ string, _ ...interface{}) {
// Do nothing.
}
func (log NullLogger) V(_ int) logr.InfoLogger {
return log
}
func (log NullLogger) WithName(_ string) logr.Logger {
return log
}
func (log NullLogger) WithValues(_ ...interface{}) logr.Logger {
return log
}
//imeplement nulllogger to bypass go vet check
func TestAlluxioFileUtils_IsExist(t *testing.T) {
mockExec := func(p1, p2, p3 string, p4 []string) (stdout string, stderr string, e error) {
if strings.Contains(p4[3], NOT_EXIST) {
return "does not exist", "", errors.New("does not exist")
} else if strings.Contains(p4[3], OTHER_ERR) {
return "", "", errors.New("other error")
} else {
return "", "", nil
}
}
err := gohook.Hook(kubeclient.ExecCommandInContainer, mockExec, nil)
if err != nil {
t.Fatal(err.Error())
}
wrappedUnhook:=func(){
err:=gohook.UnHook(kubeclient.ExecCommandInContainer)
if err!=nil{
t.Fatal(err.Error())
}
}
defer wrappedUnhook()
var tests = []struct {
in string
out bool
noErr bool
}{
{NOT_EXIST, false, true},
{OTHER_ERR, false, false},
{FINE, true, true},
}
for _, test := range tests {
found, err := AlluxioFileUtils{log: NullLogger{}}.IsExist(test.in)
if found != test.out {
t.Errorf("input parameter is %s,expected %t, got %t", test.in, test.out, found)
}
var noErr bool = (err == nil)
if test.noErr != noErr {
t.Errorf("input parameter is %s,expected noerr is %t", test.in, test.noErr)
}
}
}
func TestAlluxioFileUtils_Du(t *testing.T) {
out1, out2, out3 := 111, 222, "%233"
mockExec := func(p1, p2, p3 string, p4 []string) (stdout string, stderr string, e error) {
if strings.Contains(p4[4], EXEC_ERR) {
return "does not exist", "", errors.New("exec-error")
} else if strings.Contains(p4[4], TOO_MANY_LINES) {
return "1\n2\n3\n4\n", "1\n2\n3\n4\n", nil
} else if strings.Contains(p4[4], DATA_NUM) {
return "1\n2\t3", "1\n2\t3", nil
} else if strings.Contains(p4[4], PARSE_ERR) {
return "1\n1\tdududu\tbbb\t", "1\n1\t2\tbbb\t", nil
} else {
return fmt.Sprintf("first line!\n%d\t%d\t(%s)\t2333", out1, out2, out3), "", nil
}
}
err := gohook.HookByIndirectJmp(kubeclient.ExecCommandInContainer, mockExec, nil)
if err != nil {
t.Fatal(err.Error())
}
wrappedUnhook:=func(){
err:=gohook.UnHook(kubeclient.ExecCommandInContainer)
if err!=nil{
t.Fatal(err.Error())
}
}
defer wrappedUnhook()
var tests = []struct {
in string
out1, out2 int64
out3 string
noErr bool
}{
{EXEC_ERR, 0, 0, "", false},
{TOO_MANY_LINES, 0, 0, "", false},
{DATA_NUM, 0, 0, "", false},
{PARSE_ERR, 0, 0, "", false},
{FINE, int64(out1), int64(out2), out3, true},
}
for _, test := range tests {
o1, o2, o3, err := AlluxioFileUtils{log: NullLogger{}}.Du(test.in)
var noErr bool = (err == nil)
if test.noErr != noErr {
t.Errorf("input parameter is %s,expected noerr is %t", test.in, test.noErr)
}
if test.noErr {
if o1 != test.out1 || o2 != test.out2 || o3 != test.out3 {
t.Fatalf("input parameter is %s,output is %d,%d, %s", test.in, o1, o2, o3)
}
}
}
}
func TestAlluxioFileUtils_Count(t *testing.T) {
out1, out2, out3 := 111, 222, 333
mockExec := func(p1, p2, p3 string, p4 []string) (stdout string, stderr string, e error) {
if strings.Contains(p4[3], EXEC_ERR) {
return "does not exist", "", errors.New("exec-error")
} else if strings.Contains(p4[3], TOO_MANY_LINES) {
return "1\n2\n3\n4\n", "1\n2\n3\n4\n", nil
} else if strings.Contains(p4[3], DATA_NUM) {
return "1\n2\t3", "1\n2\t3", nil
} else if strings.Contains(p4[3], PARSE_ERR) {
return "1\n1\tdududu\tbbb\t", "1\n1\t2\tbbb\t", nil
} else {
return fmt.Sprintf("first line!\n%d\t%d\t%d", out1, out2, out3), "", nil
}
}
err := gohook.HookByIndirectJmp(kubeclient.ExecCommandInContainer, mockExec, nil)
wrappedUnhook:=func(){
err:=gohook.UnHook(kubeclient.ExecCommandInContainer)
if err!=nil{
t.Fatal(err.Error())
}
}
defer wrappedUnhook()
if err != nil {
t.Fatal(err.Error())
}
var tests = []struct {
in string
out1, out2, out3 int64
noErr bool
}{
{EXEC_ERR, 0, 0, 0, false},
{TOO_MANY_LINES, 0, 0, 0, false},
{DATA_NUM, 0, 0, 0, false},
{PARSE_ERR, 0, 0, 0, false},
{FINE, int64(out1), int64(out2), int64(out3), true},
}
for _, test := range tests {
o1, o2, o3, err := AlluxioFileUtils{log: NullLogger{}}.Count(test.in)
var noErr bool = (err == nil)
if test.noErr != noErr {
t.Errorf("input parameter is %s,expected noerr is %t", test.in, test.noErr)
}
if test.noErr {
if o1 != test.out1 || o2 != test.out2 || o3 != test.out3 {
t.Fatalf("input parameter is %s,output is %d,%d, %d", test.in, o1, o2, o3)
}
}
}
}

View File

@ -32,7 +32,7 @@ func (a AlluxioFileUtils) CachedState() (cached int64, err error) {
found := false
stdout, stderr, err = a.exec(command, false)
if err != nil {
err = fmt.Errorf("execute command %v with err: %v stdout %s and stderr %s", command, err, stdout, stderr)
err = fmt.Errorf("execute command %v with expectedErr: %v stdout %s and stderr %s", command, err, stdout, stderr)
return
}
str := strings.Split(stdout, "\n")
@ -67,7 +67,7 @@ func (a AlluxioFileUtils) CleanCache(path string) (err error) {
stdout, stderr, err = a.exec(command, false)
if err != nil {
err = fmt.Errorf("execute command %v with err: %v stdout %s and stderr %s", command, err, stdout, stderr)
err = fmt.Errorf("execute command %v with expectedErr: %v stdout %s and stderr %s", command, err, stdout, stderr)
return
}

View File

@ -34,7 +34,7 @@ func (a AlluxioFileUtils) SyncLocalDir(path string) (err error) {
duration := time.Since(start)
a.log.Info("du -sh", "path", path, "period", duration)
if err != nil {
err = fmt.Errorf("execute command %v with err: %v stdout %s and stderr %s", command, err, stdout, stderr)
err = fmt.Errorf("execute command %v with expectedErr: %v stdout %s and stderr %s", command, err, stdout, stderr)
return
}

View File

@ -36,7 +36,7 @@ func TestSyncLocalDir(t *testing.T) {
for _, test := range tests {
tools := NewAlluxioFileUtils("", "", "", ctrl.Log)
err := tools.SyncLocalDir(test.path)
// fmt.Println(err)
// fmt.Println(expectedErr)
if err == nil {
t.Errorf("expected %v, got %v", test.path, tools)
}

View File

@ -0,0 +1,108 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package kubeclient
import (
datav1alpha1 "github.com/fluid-cloudnative/fluid/api/v1alpha1"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"path/filepath"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
"testing"
"time"
// +kubebuilder:scaffold:imports
)
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
var cfg *rest.Config
var k8sClient client.Client
var testEnv *envtest.Environment
func TestAPIs(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecsWithDefaultAndCustomReporters(t,
"Controller Suite",
[]Reporter{printer.NewlineReporter{}})
}
var _ = BeforeSuite(func(done Done) {
logf.SetLogger(zap.LoggerTo(GinkgoWriter, true))
By("bootstrapping test environment")
testEnv = &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")},
}
var err error
cfg, err = testEnv.Start()
Expect(err).ToNot(HaveOccurred())
Expect(cfg).ToNot(BeNil())
err = datav1alpha1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
// +kubebuilder:scaffold:scheme
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
Expect(err).ToNot(HaveOccurred())
Expect(k8sClient).ToNot(BeNil())
close(done)
}, 60)
var _ = AfterSuite(func() {
By("tearing down the test environment")
err := testEnv.Stop()
Expect(err).ToNot(HaveOccurred())
})
var _ = Describe("Test namespace", func() {
Context("check if given ns exist", func() {
It("this ns exists,and err should be nil", func() {
err := EnsureNamespace(k8sClient, "default")
Expect(err).NotTo(HaveOccurred())
})
It("try to get a non-existed ns,should fail", func() {
err := EnsureNamespace(k8sClient, time.Now().String())
Expect(err).Should(HaveOccurred())
})
})
Context("test createnamespace", func() {
namespace := "woohoo"
It("create a new ns,err should be nil", func() {
err := createNamespace(k8sClient, namespace)
Expect(err).NotTo(HaveOccurred())
})
It("check if create ns successfully,err should nil", func() {
err := EnsureNamespace(k8sClient, namespace)
Expect(err).NotTo(HaveOccurred())
})
It("try to create ns with a conflicted name ,should fail", func() {
err := createNamespace(k8sClient, namespace)
Expect(err).Should(HaveOccurred())
})
})
})

21
vendor/github.com/brahma-adshonor/gohook/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 brahma-adshonor
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.

94
vendor/github.com/brahma-adshonor/gohook/README.md generated vendored Normal file
View File

@ -0,0 +1,94 @@
[![Build Status](https://kmalloc.visualstudio.com/ink/_apis/build/status/kmalloc.gohook?branchName=master)](https://kmalloc.visualstudio.com/ink/_build/latest?definitionId=1&branchName=master)
## Gohook
A funny library to hook golang function dynamically at runtime, enabling functionality like patching in dynamic language.
The most significant feature this library provided that makes it distinguished from others is that it supports calling back to the original function.
Read following blogpost for further explanation of the implementation detail: [1](https://www.cnblogs.com/catch/p/10973611.html),[2](https://onedrive.live.com/View.aspx?resid=7804A3BDAEB13A9F!58083&authkey=!AKVlLS9s9KYh07s)
## How it works
The general idea of this library is that gohook will find out the address of a go function and then insert a few jump instructions to redirect execution flow to the new function.
there are a few steps to perform a hook:
1. find out the address of a function, this can be accomplished by standard reflect library.
2. inject jump code into target function, with carefully crafted binary instruction.
3. implement trampoline function to enable calling back to the original function.
It may seem risky and dangerous to perform operations like these at first glance, but this is actually common practice in c/c++ though, you can google it, search for "hot patching" something like that for more information.
## Using gohook
5 api are exported from this library, the signatures are simple as illustrated following:
1. `func Hook(target, replace, trampoline interface{}) error;`
2. `func UnHook(target interface{}) error;`
3. `func HookByIndirectJmp(target, replace, trampoline interface{});`
4. `func HookMethod(instance interface{}, method string, replace, trampoline interface{}) error;`
5. `func UnHookMethod(instance interface{}, method string) error;`
The first 3 functions are used to hook/unhook regular functions, the rest are for instance method, as the naming implies(essentially, HookMethod(obj,x,y,z) is the same as Hook(ObjType.x,y,z)).
Basically, you can just call `gohook.Hook(fmt.Printf, myPrintf, myPrintfTramp)` to hook the fmt.Printf in the standard library.
Trampolines here serves as a shadow function after the target function is hooked, think of it as a copy of the original target function.
In situation where calling back to the original function is not needed, trampoline can be passed a nil value.
HookByIndirectJmp() differs from Hook() in that it uses rdx to perform an indirect jump from a funcval, and:
1. `rdx is the context register used by compiler to access funcval.`
2. `funcval contains extra information for a closure, which is used by compiler and runtime.`
this makes it possible to hook closure function and function created by reflect.MakeFunc(), **in a less compatible way**, since the implementaion of this hook has to guess the memory layout of a reflect.Value object, which may vary from different version of runtime.
```go
package main
import (
"fmt"
"os"
"github.com/kmalloc/gohook"
)
func myPrintln(a ...interface{}) (n int, err error) {
fmt.Fprintln(os.Stdout, "before real Printfln")
return myPrintlnTramp(a...)
}
func myPrintlnTramp(a ...interface{}) (n int, err error) {
// a dummy function to make room for a shadow copy of the original function.
// it doesn't matter what we do here, just to create an addressable function with adequate size.
myPrintlnTramp(a...)
myPrintlnTramp(a...)
myPrintlnTramp(a...)
for {
fmt.Printf("hello")
}
return 0, nil
}
func main() {
gohook.Hook(fmt.Println, myPrintln, myPrintlnTramp)
fmt.Println("hello world!")
}
```
For more usage example, please refer to the example folder.
## Notes
1. 32 bit mode may not work, far jump is not handled.
2. trampoline is used to make room for the original function, it will be overwrited.
3. in case of small function which may be inlined, gohook may fail:
- disable inlining by passig -gcflags=-l to build cmd.
4. this library is created for integrated testing, and not fully tested in production(yet), user discretion is advised.
5. escape analysis may be influenced:
- deep copy arguments if you need to copy argument from replacement function(see func_stack_test.go).
- escape those arguments from trampoline(by passing it to a goroutine or to other function that can escape it)
if that argument is allocated from the replacement function.

861
vendor/github.com/brahma-adshonor/gohook/arch_util.go generated vendored Normal file
View File

@ -0,0 +1,861 @@
package gohook
import (
"bytes"
"errors"
"fmt"
"math"
"unsafe"
"golang.org/x/arch/x86/x86asm"
)
type CodeFix struct {
Code []byte
Addr uintptr
Foreign bool
}
var (
minJmpCodeSize = 0
elfInfo, _ = NewElfInfo()
errInplaceFixSizeNotEnough = fmt.Errorf("func size exceed during inplace fix")
funcPrologue32 = defaultFuncPrologue32
funcPrologue64 = defaultFuncPrologue64
// ======================condition jump instruction========================
// JA JAE JB JBE JCXZ JE JECXZ JG JGE JL JLE JMP JNE JNO JNP JNS JO JP JRCXZ JS
// one byte opcode, one byte relative offset
twoByteCondJmp = []byte{0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xe3}
// two byte opcode, four byte relative offset
sixByteCondJmp = []uint16{0x0f80, 0x0f81, 0x0f82, 0x0f83, 0x0f84, 0x0f85, 0x0f86, 0x0f87, 0x0f88, 0x0f89, 0x0f8a, 0x0f8b, 0x0f8c, 0x0f8d, 0x0f8e, 0x0f8f}
// ====================== jump instruction========================
// one byte opcode, one byte relative offset
twoByteJmp = []byte{0xeb}
// one byte opcode, four byte relative offset
fiveByteJmp = []byte{0xe9}
// ====================== call instruction========================
// one byte opcode, 4 byte relative offset
fiveByteCall = []byte{0xe8}
// ====================== ret instruction========================
// return instruction, no operand
oneByteRet = []byte{0xc3, 0xcb}
// return instruction, one byte opcode, 2 byte operand
threeByteRet = []byte{0xc2, 0xca}
)
const (
FT_CondJmp = 1
FT_JMP = 2
FT_CALL = 3
FT_RET = 4
FT_OTHER = 5
FT_INVALID = 6
FT_SKIP = 7
FT_OVERFLOW = 8
)
func SetMinJmpCodeSize(sz int) {
minJmpCodeSize = sz
}
func ResetFuncPrologue() {
funcPrologue32 = defaultFuncPrologue32
funcPrologue64 = defaultFuncPrologue64
}
func SetFuncPrologue(mode int, data []byte) {
if mode == 32 {
funcPrologue32 = make([]byte, len(data))
copy(funcPrologue32, data)
} else {
funcPrologue64 = make([]byte, len(data))
copy(funcPrologue64, data)
}
}
func GetInsLenGreaterThan(mode int, data []byte, least int) int {
if len(data) < least {
return 0
}
curLen := 0
d := data[curLen:]
for {
if len(d) <= 0 {
break
}
if curLen >= least {
break
}
inst, err := x86asm.Decode(d, mode)
if err != nil || (inst.Opcode == 0 && inst.Len == 1 && inst.Prefix[0] == x86asm.Prefix(d[0])) {
break
}
if inst.Len == 1 && d[0] == 0xcc {
// 0xcc -> int3, trap to debugger, padding to function end
break
}
curLen = curLen + inst.Len
d = data[curLen:]
}
return curLen
}
func isByteOverflow(v int32) bool {
if v > 0 {
if v > math.MaxInt8 {
return true
}
} else {
if v < math.MinInt8 {
return true
}
}
return false
}
func isIntOverflow(v int64) bool {
if v > 0 {
if v > math.MaxInt32 {
return true
}
} else {
if v < math.MinInt32 {
return true
}
}
return false
}
func calcOffset(insSz int, startAddr, curAddr, to uintptr, to_sz int, offset int32) int64 {
newAddr := curAddr
absAddr := curAddr + uintptr(insSz) + uintptr(offset)
if curAddr < startAddr+uintptr(to_sz) {
newAddr = to + (curAddr - startAddr)
}
if absAddr >= startAddr && absAddr < startAddr+uintptr(to_sz) {
absAddr = to + (absAddr - startAddr)
}
return int64(uint64(absAddr) - uint64(newAddr) - uint64(insSz))
}
func translateJump(off int64, code []byte) ([]byte, error) {
if code[0] == 0xe3 {
return nil, errors.New("not supported JCXZ instruction(0xe3)")
}
if code[0] == 0xeb {
ret := make([]byte, 5)
ret[0] = 0xe9
ret[1] = byte(off)
ret[2] = byte(off >> 8)
ret[3] = byte(off >> 16)
ret[4] = byte(off >> 24)
return ret, nil
} else if code[0] >= 0x70 && code[0] <= 0x7f {
ret := make([]byte, 6)
ret[0] = 0x0f
ret[1] = 0x80 + code[0] - 0x70
ret[2] = byte(off)
ret[3] = byte(off >> 8)
ret[4] = byte(off >> 16)
ret[5] = byte(off >> 24)
return ret, nil
} else {
return nil, errors.New("cannot fix unsupported jump instruction inplace")
}
}
func adjustInstructionOffset(code []byte, off int64) ([]byte, error) {
if code[0] == 0xe3 || code[0] == 0xeb || (code[0] >= 0x70 && code[0] <= 0x7f) {
offset := int(int8(code[1]))
if offset == int(off) {
return code, nil
}
if isByteOverflow(int32(off)) {
return nil, fmt.Errorf("byte overflow in adjusting offset")
}
code[1] = byte(int8(off))
} else if code[0] == 0x0f && (code[1] >= 0x80 && code[1] <= 0x8f) {
offset := int(int32(uint32(code[2]) | (uint32(code[3]) << 8) | (uint32(code[4]) << 16) | (uint32(code[5]) << 24)))
if offset == int(off) {
return code, nil
}
if isIntOverflow(off) {
return nil, fmt.Errorf("int overflow in adjusting 6-bytes inst offset")
}
code[2] = byte(off)
code[3] = byte(off >> 8)
code[4] = byte(off >> 16)
code[5] = byte(off >> 24)
} else if code[0] == 0xe9 || code[0] == 0xe8 {
offset := int(int32(uint32(code[1]) | (uint32(code[2]) << 8) | (uint32(code[3]) << 16) | (uint32(code[4]) << 24)))
if offset == int(off) {
return code, nil
}
if isIntOverflow(off) {
return nil, fmt.Errorf("int overflow in adjusting 5-bytes inst offset")
}
code[1] = byte(off)
code[2] = byte(off >> 8)
code[3] = byte(off >> 16)
code[4] = byte(off >> 24)
} else if code[0] == 0x48 && (code[1] == 0x8b || code[1] == 0x8d) && (code[2]&0x05) == 0x05 { // mod == 00 r/m == 101
// rip relative addressing: mov/lea
// intel software development manual: `Addressing-Mode Encoding of ModR/M and SIB Bytes` && `RIP-Relative Addressing`
// or https://www.cs.uaf.edu/2016/fall/cs301/lecture/09_28_machinecode.html
offset := int(int32(uint32(code[3]) | (uint32(code[4]) << 8) | (uint32(code[5]) << 16) | (uint32(code[6]) << 24)))
if offset == int(off) {
return code, nil
}
code[3] = byte(off)
code[4] = byte(off >> 8)
code[5] = byte(off >> 16)
code[6] = byte(off >> 24)
} else {
return nil, fmt.Errorf("not jump instruction")
}
return code, nil
}
func calcJumpToAbsAddr(mode int, addr uintptr, code []byte) uintptr {
sz := 0
offset := 0
if code[0] == 0xe3 || code[0] == 0xeb || (code[0] >= 0x70 && code[0] <= 0x7f) {
sz = 2
offset = int(int8(code[1]))
}
if code[0] == 0x0f && (code[1] >= 0x80 && code[1] <= 0x8f) {
sz = 6
offset = int(int32(uint32(code[2]) | (uint32(code[3]) << 8) | (uint32(code[4]) << 16) | (uint32(code[5]) << 24)))
}
if code[0] == 0xe9 || code[0] == 0xe8 {
sz = 5
offset = int(int32(uint32(code[1]) | (uint32(code[2]) << 8) | (uint32(code[3]) << 16) | (uint32(code[4]) << 24)))
}
if code[0] == 0x48 && (code[1] == 0x8b || code[1] == 0x8d) && (code[2]&0x05) == 0x05 { // mod == 00 r/m == 101
// rip relative addressing: mov/lea
// intel software development manual: `Addressing-Mode Encoding of ModR/M and SIB Bytes` && `RIP-Relative Addressing`
// or https://www.cs.uaf.edu/2016/fall/cs301/lecture/09_28_machinecode.html
sz = 7
offset = int(int32(uint32(code[3]) | (uint32(code[4]) << 8) | (uint32(code[5]) << 16) | (uint32(code[6]) << 24)))
}
if sz == 0 {
return uintptr(0)
}
return addr + uintptr(sz) + uintptr(offset)
}
func FixOneInstruction(mode int, fix_recursive_call bool, startAddr, curAddr uintptr, code []byte, to uintptr, to_sz int) (int, int, []byte) {
nc := make([]byte, len(code))
copy(nc, code)
if code[0] == 0xe3 || code[0] == 0xeb || (code[0] >= 0x70 && code[0] <= 0x7f) {
// two byte condition jump, two byte jmp
nc = nc[:2]
off := calcOffset(2, startAddr, curAddr, to, to_sz, int32(int8(code[1])))
if off != int64(int8(nc[1])) {
if isByteOverflow(int32(off)) {
// overfloat, cannot fix this with one byte operand
return 2, FT_OVERFLOW, nc
}
nc[1] = byte(off)
return 2, FT_CondJmp, nc
}
return 2, FT_SKIP, nc
}
if code[0] == 0x0f && (code[1] >= 0x80 && code[1] <= 0x8f) {
// six byte condition jump
nc = nc[:6]
off1 := (uint32(code[2]) | (uint32(code[3]) << 8) | (uint32(code[4]) << 16) | (uint32(code[5]) << 24))
off2 := uint64(calcOffset(6, startAddr, curAddr, to, to_sz, int32(off1)))
if uint64(int32(off1)) != off2 {
if isIntOverflow(int64(off2)) {
// overfloat, cannot fix this with four byte operand
return 6, FT_OVERFLOW, nc
}
nc[2] = byte(off2)
nc[3] = byte(off2 >> 8)
nc[4] = byte(off2 >> 16)
nc[5] = byte(off2 >> 24)
return 6, FT_CondJmp, nc
}
return 6, FT_SKIP, nc
}
if code[0] == 0xe9 || code[0] == 0xe8 {
// five byte jmp, five byte call
nc = nc[:5]
off1 := (uint32(code[1]) | (uint32(code[2]) << 8) | (uint32(code[3]) << 16) | (uint32(code[4]) << 24))
off2 := uint64(0)
if !fix_recursive_call && code[0] == 0xe8 && startAddr == (curAddr+uintptr(5)+uintptr(int32(off1))) {
// don't fix recursive call
off2 = uint64(int32(off1))
} else {
off2 = uint64(calcOffset(5, startAddr, curAddr, to, to_sz, int32(off1)))
}
if uint64(int32(off1)) != off2 {
if isIntOverflow(int64(off2)) {
// overfloat, cannot fix this with four byte operand
return 5, FT_OVERFLOW, nc
}
nc[1] = byte(off2)
nc[2] = byte(off2 >> 8)
nc[3] = byte(off2 >> 16)
nc[4] = byte(off2 >> 24)
return 5, FT_JMP, nc
}
return 5, FT_SKIP, nc
}
// ret instruction just return, no fix is needed.
if code[0] == 0xc3 || code[0] == 0xcb {
// one byte ret
nc = nc[:1]
return 1, FT_RET, nc
}
if code[0] == 0xc2 || code[0] == 0xca {
// three byte ret
nc = nc[:3]
return 3, FT_RET, nc
}
inst, err := x86asm.Decode(code, mode)
if err != nil || (inst.Opcode == 0 && inst.Len == 1 && inst.Prefix[0] == x86asm.Prefix(code[0])) {
return 0, FT_INVALID, nc
}
if inst.Len == 1 && code[0] == 0xcc {
return 0, FT_INVALID, nc
}
sz := inst.Len
nc = nc[:sz]
return sz, FT_OTHER, nc
}
func doFixTargetFuncCode(all bool, mode int, start uintptr, funcSz int, to uintptr, move_sz int, inst []CodeFix) ([]CodeFix, error) {
fix := make([]CodeFix, 0, 64)
curSz := 0
curAddr := start
i := 0
for i = 0; i < len(inst); i++ {
if curSz >= move_sz {
break
}
code := inst[i].Code
sz, ft, nc := FixOneInstruction(mode, false, start, curAddr, code, to, move_sz)
if sz == 0 && ft == FT_INVALID {
// the end or unrecognized instruction
return nil, errors.New(fmt.Sprintf("invalid instruction scanned, addr:0x%x", curAddr))
} else if sz == 5 && nc[0] == 0xe8 {
// call instruction is not allowed to move.
// this will mess up with golang stack reallocation.
return nil, fmt.Errorf("call instruction is not allowed to move")
}
if ft == FT_RET {
return nil, errors.New(fmt.Sprintf("ret instruction in patching erea is not allowed, addr:0x%x", curAddr))
}
if ft == FT_OVERFLOW {
return nil, errors.New(fmt.Sprintf("jmp instruction in patching erea overflow, addr:0x%x", curAddr))
}
if ft != FT_OTHER && ft != FT_SKIP {
fix = append(fix, CodeFix{Code: nc, Addr: curAddr, Foreign: true})
} else if all {
fix = append(fix, CodeFix{Code: nc, Addr: curAddr, Foreign: true})
}
curSz += sz
curAddr = start + uintptr(curSz)
}
for ; i < len(inst); i++ {
if funcSz > 0 && int(curAddr-start) >= funcSz {
break
}
code := inst[i].Code
sz, ft, nc := FixOneInstruction(mode, false, start, curAddr, code, to, move_sz)
if sz == 0 && ft == FT_INVALID {
// the end or unrecognized instruction
break
}
if ft == FT_OVERFLOW {
return nil, errors.New(fmt.Sprintf("jmp instruction in body overflow, addr:0x%x", curAddr))
}
if ft != FT_OTHER && ft != FT_RET && ft != FT_SKIP {
fix = append(fix, CodeFix{Code: nc, Addr: curAddr, Foreign: false})
} else if all {
fix = append(fix, CodeFix{Code: nc, Addr: curAddr, Foreign: false})
}
curSz += sz
curAddr = start + uintptr(curSz)
}
return fix, nil
}
// FixTargetFuncCode fix function code starting at address [start]
// parameter 'funcSz' may not specify, in which case, we need to find out the end by scanning next prologue or finding invalid instruction.
// 'to' specifys a new location, to which 'move_sz' bytes instruction will be copied
// since move_sz byte instructions will be copied, those relative jump instruction need to be fixed.
func FixTargetFuncCode(mode int, start uintptr, funcSz uint32, to uintptr, move_sz int) ([]CodeFix, error) {
inst, _ := parseInstruction(mode, start, int(funcSz), false)
return doFixTargetFuncCode(false, mode, start, int(funcSz), to, move_sz, inst)
}
func GetFuncSizeByGuess(mode int, start uintptr, minimal bool) (uint32, error) {
funcPrologue := funcPrologue64
if mode == 32 {
funcPrologue = funcPrologue32
}
prologueLen := len(funcPrologue)
code := makeSliceFromPointer(start, 16) // instruction takes at most 16 bytes
/* prologue is not required
if !bytes.Equal(funcPrologue, code[:prologueLen]) { // not valid function start or invalid prologue
return 0, errors.New(fmt.Sprintf("no func prologue, addr:0x%x", start))
}
*/
int3_found := false
curLen := uint32(0)
for {
inst, err := x86asm.Decode(code, mode)
if err != nil || (inst.Opcode == 0 && inst.Len == 1 && inst.Prefix[0] == x86asm.Prefix(code[0])) {
break
}
if inst.Len == 1 && code[0] == 0xcc {
// 0xcc -> int3, trap to debugger, padding to function end
if minimal {
break
}
int3_found = true
} else if int3_found {
break
}
curLen = curLen + uint32(inst.Len)
code = makeSliceFromPointer(start+uintptr(curLen), 16) // instruction takes at most 16 bytes
if bytes.Equal(funcPrologue, code[:prologueLen]) {
break
}
}
return curLen, nil
}
// sz size of source function
// WARNING: copy function won't work in copystack(since go 1.3).
// runtime will copy stack to new area and fix those weird stuff(pointer/rbp etc), this will crash trampoline function.
// since copying function makes trampoline a completely different function, with completely different stack layout which is
// not known to runtime.
// solution to this is, we should just copy those non-call instructions to trampoline. in this way we don't mess up with runtime.
// TODO/FIXME
func copyFuncInstruction(mode int, from, to uintptr, sz int, allowCall bool) ([]CodeFix, error) {
curSz := 0
curAddr := from
fix := make([]CodeFix, 0, 256)
for {
if curSz >= sz {
break
}
code := makeSliceFromPointer(curAddr, 16) // instruction takes at most 16 bytes
sz, ft, nc := FixOneInstruction(mode, true, from, curAddr, code, to, sz)
if sz == 0 && ft == FT_INVALID {
// the end or unrecognized instruction
break
} else if !allowCall && sz == 5 && nc[0] == 0xe8 {
// call instruction is not allowed to move.
// this will mess up with golang stack reallocation.
return nil, fmt.Errorf("call instruction is not allowed to copy")
}
if ft == FT_OVERFLOW {
return nil, fmt.Errorf("overflow instruction in copying function, addr:0x%x", curAddr)
}
to_addr := (to + (curAddr - from))
fix = append(fix, CodeFix{Code: nc, Addr: to_addr})
curSz += sz
curAddr = from + uintptr(curSz)
}
to_addr := (to + (curAddr - from))
fix = append(fix, CodeFix{Code: []byte{0xcc}, Addr: to_addr})
return fix, nil
}
func adjustJmpOffset(mode int, start, delem uintptr, funcSize, moveSize int, inst []CodeFix) error {
funcEnd := start + uintptr(funcSize)
for i := range inst {
code := inst[i].Code
curAddr := inst[i].Addr
absAddr := calcJumpToAbsAddr(mode, curAddr, code)
if curAddr > delem && curAddr < funcEnd {
inst[i].Addr = curAddr + uintptr(moveSize)
}
if absAddr != uintptr(0) {
delta := absAddr - curAddr - uintptr(len(code))
off := int64(delta)
if unsafe.Sizeof(uintptr(0)) == unsafe.Sizeof(int32(0)) {
off = int64(int32(delta))
}
// fmt.Printf("adjust inst at:%x, sz:%d, delem:%x, target:%x, funcEnd:%x, off:%x\n", curAddr, len(code), delem, absAddr, funcEnd, uintptr(off))
if (curAddr < delem || curAddr >= funcEnd) && absAddr > delem && absAddr < funcEnd {
off += int64(moveSize)
} else if (curAddr >= delem && curAddr < funcEnd) && (absAddr <= delem || absAddr >= funcEnd) {
off -= int64(moveSize)
} else {
// do nothing
}
c, err := adjustInstructionOffset(code, off)
if err != nil {
return fmt.Errorf("err occurs adjusting inst, addr:%x,off:%x,err:%s", curAddr, off, err.Error())
}
inst[i].Code = c
// absAddr = calcJumpToAbsAddr(mode, inst[i].Addr, code)
// fmt.Printf("after adjust inst, old addr:%x, new addr:%x, target:%x\n", curAddr, inst[i].Addr, absAddr)
}
}
return nil
}
func translateShortJump(mode int, addr, to uintptr, inst []CodeFix, funcSz, move_sz, jumpSize int) (int, []CodeFix, error) {
newSz := 0
fix := make([]CodeFix, 0, 256)
for i := range inst {
code := inst[i].Code
curAddr := inst[i].Addr
sz, ft, _ := FixOneInstruction(mode, false, addr, curAddr, code, to, move_sz)
if sz == 0 && ft == FT_INVALID {
// the end or unrecognized instruction
break
}
foreign := false
if curAddr < addr+uintptr(move_sz) {
foreign = true
}
if ft == FT_OVERFLOW {
if sz != 2 {
return 0, nil, fmt.Errorf("inst overflow with size != 2")
}
nc, err := translateJump(int64(int8(code[1])), code)
if err != nil {
return 0, nil, err
}
delta := len(nc) - len(code)
if curAddr < addr+uintptr(move_sz) {
move_sz += delta
}
inst[i].Code = nc
// fmt.Printf("extent overflow inst at:%x, sz:%d, move sz:%d\n", curAddr, len(nc), move_sz)
err = adjustJmpOffset(mode, addr, curAddr, funcSz, delta, inst[i:])
if err != nil {
return 0, nil, err
}
err = adjustJmpOffset(mode, addr, curAddr, funcSz, delta, fix)
if err != nil {
return 0, nil, err
}
}
newSz += len(inst[i].Code)
fix = append(fix, CodeFix{Code: inst[i].Code, Addr: inst[i].Addr, Foreign: foreign})
}
if newSz-move_sz > funcSz-jumpSize {
return move_sz, fix, errInplaceFixSizeNotEnough
}
return move_sz, fix, nil
}
func parseInstruction(mode int, addr uintptr, funcSz int, minimal bool) ([]CodeFix, error) {
funcPrologue := funcPrologue64
if mode == 32 {
funcPrologue = funcPrologue32
}
prologueLen := len(funcPrologue)
code := makeSliceFromPointer(addr, 16) // instruction takes at most 16 bytes
curLen := 0
int3_found := false
ret := make([]CodeFix, 0, 258)
for {
if funcSz > 0 && curLen >= funcSz {
break
}
inst, err := x86asm.Decode(code, mode)
if err != nil || (inst.Opcode == 0 && inst.Len == 1 && inst.Prefix[0] == x86asm.Prefix(code[0])) {
break
}
if inst.Len == 1 && code[0] == 0xcc {
// 0xcc -> int3, trap to debugger, padding to function end
if minimal {
break
}
int3_found = true
} else if int3_found {
break
}
c := make([]byte, inst.Len)
copy(c, code)
cf := CodeFix{Addr: addr + uintptr(curLen), Code: c}
ret = append(ret, cf)
curLen = curLen + inst.Len
code = makeSliceFromPointer(addr+uintptr(curLen), 16)
if bytes.Equal(funcPrologue, code[:prologueLen]) {
break
}
}
return ret, nil
}
func fixFuncInstructionInplace(mode int, addr, to uintptr, funcSz int, move_sz int, jumpSize int) ([]CodeFix, error) {
/*
trail := makeSliceFromPointer(addr+uintptr(funcSz), 1024)
for i := 0; i < len(trail); i++ {
if trail[i] != 0xcc {
break
}
funcSz++
}
*/
code, _ := parseInstruction(mode, addr, funcSz, false)
move_sz, fix, err := translateShortJump(mode, addr, to, code, funcSz, move_sz, jumpSize)
if err != nil {
return nil, err
}
fix, err1 := doFixTargetFuncCode(true, mode, addr, funcSz, to, move_sz, fix)
if err1 != nil {
return fix, err1
}
curAddr := to
firstBody := addr
for i := range fix {
if !fix[i].Foreign {
firstBody = fix[i].Addr
break
}
// fmt.Printf("foreign addr:%x, sz:%d\n", curAddr, len(fix[i].Code))
fix[i].Addr = curAddr
curAddr += uintptr(len(fix[i].Code))
}
mvAddr := addr + uintptr(jumpSize)
msz := -int(firstBody - mvAddr)
if msz != 0 {
// fmt.Printf("now move to the front, msz:%d\n", msz)
err2 := adjustJmpOffset(mode, addr, mvAddr, funcSz, msz, fix)
if err2 != nil {
// fmt.Printf("error in fixing inplace\n")
return nil, err2
}
}
// fmt.Printf("done fixing inplace\n")
return fix, nil
}
func genJumpCode(mode int, rdxIndirect bool, to, from uintptr) []byte {
// 1. use relaive jump if |from-to| < 2G
// 2. otherwise, push target, then ret
var code []byte
if rdxIndirect {
// rdx indirect jump.
// 'to' :data pointer from reflect.Value, pointed to a funcValue, and the first field of funcval is a pointer to the real func.
// 'from': this is the instruction code addr of the target function.
// by convention, rdx is the context register pointed to a funcval.
// funcval of a closure function contains extra information used by compiler and runtime.
// so using indirect jmp by rdx makes it possible to hook closure func and func created by reflect.MakeFunc
// caution: 'to' funcval must stay alive after hook is installed.
if mode == 32 {
code = []byte{
0xBA,
byte(to),
byte(to >> 8),
byte(to >> 16),
byte(to >> 24), // mov edx,to
0xFF, 0x22, // jmp DWORD PTR [edx]
}
} else {
code = []byte{
0x48, 0xBA,
byte(to),
byte(to >> 8),
byte(to >> 16),
byte(to >> 24),
byte(to >> 32),
byte(to >> 40),
byte(to >> 48),
byte(to >> 56), // movabs rdx,to
0xFF, 0x22, // jmp QWORD PTR [rdx]
}
}
} else {
delta := int64(from - to)
if unsafe.Sizeof(uintptr(0)) == unsafe.Sizeof(int32(0)) {
delta = int64(int32(from - to))
}
relative := (delta <= 0x7fffffff)
if delta < 0 {
delta = -delta
relative = (delta <= 0x80000000)
}
// relative = false
if relative {
var dis uint32
if to > from {
dis = uint32(int32(to-from) - 5)
} else {
dis = uint32(-int32(from-to) - 5)
}
code = []byte{
0xe9,
byte(dis),
byte(dis >> 8),
byte(dis >> 16),
byte(dis >> 24),
}
} else if mode == 32 {
code = []byte{
0x68, // push
byte(to),
byte(to >> 8),
byte(to >> 16),
byte(to >> 24),
0xc3, // retn
}
} else if mode == 64 {
// push does not operate on 64bit imm, workarounds are:
// 1. move to register(eg, %rdx), then push %rdx, however, overwriting register may cause problem if not handled carefully.
// 2. push twice, preferred.
/*
code = []byte{
0x48, // prefix
0xba, // mov to %rdx
byte(to), byte(to >> 8), byte(to >> 16), byte(to >> 24),
byte(to >> 32), byte(to >> 40), byte(to >> 48), byte(to >> 56),
0x52, // push %rdx
0xc3, // retn
}
*/
code = []byte{
0x68, //push
byte(to), byte(to >> 8), byte(to >> 16), byte(to >> 24),
0xc7, 0x44, 0x24, // mov $value, 4%rsp
0x04, // rsp + 4
byte(to >> 32), byte(to >> 40), byte(to >> 48), byte(to >> 56),
0xc3, // retn
}
} else {
panic("invalid mode")
}
}
sz := len(code)
if minJmpCodeSize > 0 && sz < minJmpCodeSize {
nop := make([]byte, 0, minJmpCodeSize-sz)
for {
if len(nop) >= minJmpCodeSize-sz {
break
}
nop = append(nop, 0x90)
}
code = append(code, nop...)
}
return code
}

View File

@ -0,0 +1,54 @@
# Go
# Build your Go project.
# Add steps that test, save build artifacts, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/go
trigger:
- master
pool:
vmImage: 'ubuntu-latest'
variables:
GOBIN: '$(GOPATH)/bin' # Go binaries path
GOROOT: '/usr/local/go1.11' # Go installation path
GOPATH: '$(system.defaultWorkingDirectory)/gopath' # Go workspace path
modulePath: '$(GOPATH)/src/github.com/$(build.repository.name)' # Path to the module's code
steps:
- script: |
mkdir -p '$(GOBIN)'
mkdir -p '$(GOPATH)/pkg'
mkdir -p '$(modulePath)'
shopt -s extglob
shopt -s dotglob
mv !(gopath) '$(modulePath)'
echo '##vso[task.prependpath]$(GOBIN)'
echo '##vso[task.prependpath]$(GOROOT)/bin'
displayName: 'Set up the Go workspace'
- script: |
go version
go get -v -t -d ./...
if [ -f Gopkg.toml ]; then
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure
fi
go build -v .
workingDirectory: '$(modulePath)'
displayName: 'Get dependencies, then build'
- script: |
go test -v .
GOARCH=386 go test -v .
workingDirectory: '$(modulePath)'
displayName: 'run unittest'
- script: |
GO111MODULE=on go run example/main.go &&
GO111MODULE=on go run example/unwind.go &&
GO111MODULE=on go run example/example.go &&
GO111MODULE=on go run example/example2.go &&
GO111MODULE=on go run example/example3.go
workingDirectory: '$(modulePath)'
displayName: 'run example'

9
vendor/github.com/brahma-adshonor/gohook/bug.sh generated vendored Executable file
View File

@ -0,0 +1,9 @@
#! /bin/bash
for i in {1..10000};do
go test -gcflags=all='-l' -cover -o t
if [[ "$?" != "0" ]]; then
exit 233
fi
echo "@@@@@@@@@@@@@ ${i}th run done @@@@@@@@@@@@@@@"
done

75
vendor/github.com/brahma-adshonor/gohook/elf_helper.go generated vendored Normal file
View File

@ -0,0 +1,75 @@
package gohook
import (
"debug/elf"
"errors"
"os"
"path/filepath"
"sort"
)
var (
curExecutable, _ = filepath.Abs(os.Args[0])
)
type SymbolSlice []elf.Symbol
func (a SymbolSlice) Len() int { return len(a) }
func (a SymbolSlice) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a SymbolSlice) Less(i, j int) bool { return a[i].Value < a[j].Value }
type ElfInfo struct {
CurFile string
Symbol SymbolSlice
}
func NewElfInfo() (*ElfInfo, error) {
ei := &ElfInfo{CurFile: curExecutable}
err := ei.init()
if err != nil {
return nil, err
}
return ei, nil
}
func (ei *ElfInfo) init() error {
f, err := elf.Open(ei.CurFile)
if err != nil {
return err
}
defer f.Close()
var sym []elf.Symbol
sym, err = f.Symbols()
ei.Symbol = make(SymbolSlice, 0, len(sym))
for _, v := range sym {
if v.Size > 0 {
ei.Symbol = append(ei.Symbol, v)
}
}
if err != nil {
return err
}
sort.Sort(ei.Symbol)
return nil
}
func (ei *ElfInfo) GetFuncSize(addr uintptr) (uint32, error) {
if ei.Symbol == nil {
return 0, errors.New("no symbol")
}
i := sort.Search(len(ei.Symbol), func(i int) bool { return ei.Symbol[i].Value >= uint64(addr) })
if i < len(ei.Symbol) && ei.Symbol[i].Value == uint64(addr) {
//fmt.Printf("addr:0x%x,value:0x%x, sz:%d\n", addr, ei.Symbol[i].Value, ei.Symbol[i].Size)
return uint32(ei.Symbol[i].Size), nil
}
//fmt.Printf("not find elf\n")
return 0, errors.New("can not find func")
}

View File

@ -0,0 +1,299 @@
package gohook
import (
"bytes"
"fmt"
"runtime"
"testing"
"github.com/stretchr/testify/assert"
)
func victim(a, b, c int, e, f, g string) int {
if a > 100 {
return 42
}
var someBigStackArray [4096]byte // to occupy stack, don't let it escape
for i := 0; i < len(someBigStackArray); i++ {
someBigStackArray[i] = byte((a ^ b) & (i ^ c))
}
ch := make(chan int, 2)
if (a % 2) != 0 {
someBigStackArray[200] = 0xe9
}
ch <- 2
fmt.Printf("calling real victim() (%s,%s,%s,%x):%dth\n", e, f, g, someBigStackArray[200], a)
runtime.GC()
return victim(a+1, b-1, c-1, e, f, g)
}
func victimTrampoline(a, b, c int, e, f, g string) int {
fmt.Printf("calling victim()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling victim()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling victim()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling victim()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling victim()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling victim()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling victim()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling victim()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling victim()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling victim()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling victim()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling victim()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
for {
if (a % 2) != 0 {
fmt.Printf("calling victim()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
} else {
a++
}
if a+b > 100 {
break
}
buff := bytes.NewBufferString("something weird")
fmt.Printf("len:%d\n", buff.Len())
}
return 1
}
func victimReplace(a, b, c int, e, f, g string) int {
fmt.Printf("victimReplace sends its regard\n")
ret := 0
if a > 100 {
ret = 100000
}
return ret + victimTrampoline(a, b, c, e, f, g)
}
func TestStackGrowth(t *testing.T) {
SetMinJmpCodeSize(64)
defer SetMinJmpCodeSize(0)
ResetFuncPrologue()
err := Hook(victim, victimReplace, victimTrampoline)
assert.Nil(t, err)
ret := victim(0, 1000, 100000, "ab", "miliao", "see")
runtime.GC()
runtime.GC()
runtime.GC()
assert.Equal(t, 100042, ret)
UnHook(victim)
fmt.Printf("after unHook\n")
victimReplace(98, 2, 3, "ab", "ef", "g")
}
func TestFuncSize(t *testing.T) {
ResetFuncPrologue()
addr1 := GetFuncAddr(victim)
addr2 := GetFuncAddr(victimReplace)
addr3 := GetFuncAddr(victimTrampoline)
elf, err := NewElfInfo()
hasElf := (err == nil)
sz11, err11 := GetFuncSizeByGuess(GetArchMode(), addr1, true)
assert.Nil(t, err11)
if hasElf {
sz1, err1 := elf.GetFuncSize(addr1)
assert.Nil(t, err1)
assert.Equal(t, sz1, sz11)
} else {
assert.True(t, sz11 > 0)
}
sz21, err21 := GetFuncSizeByGuess(GetArchMode(), addr2, true)
assert.Nil(t, err21)
if hasElf {
sz2, err2 := elf.GetFuncSize(addr2)
assert.Nil(t, err2)
assert.Equal(t, sz2, sz21)
}
sz31, err31 := GetFuncSizeByGuess(GetArchMode(), addr3, true)
assert.Nil(t, err31)
if hasElf {
sz3, err3 := elf.GetFuncSize(addr3)
assert.Nil(t, err3)
assert.Equal(t, sz3, sz31)
}
}
func mySprintf(format string, a ...interface{}) string {
addr1 := GetFuncAddr(victim)
addr2 := GetFuncAddr(victimReplace)
addr3 := GetFuncAddr(victimTrampoline)
elf, err := NewElfInfo()
fmt.Println("show:", elf, err)
sz1, err1 := elf.GetFuncSize(addr1)
fmt.Println("show:", sz1, err1)
sz11, err11 := GetFuncSizeByGuess(GetArchMode(), addr1, false)
fmt.Println("show:", sz11, err11)
sz2, err2 := elf.GetFuncSize(addr2)
fmt.Println("show:", sz2, err2)
sz21, err21 := GetFuncSizeByGuess(GetArchMode(), addr2, false)
fmt.Println("show:", sz21, err21)
sz3, err3 := elf.GetFuncSize(addr3)
fmt.Println("show:", sz3, err3)
sz31, err31 := GetFuncSizeByGuess(GetArchMode(), addr3, false)
fmt.Println("show:", sz31, err31)
return ""
}
func TestCopyFunc(t *testing.T) {
ResetFuncPrologue()
addr := GetFuncAddr(mySprintf)
sz := GetFuncInstSize(mySprintf)
tp := makeSliceFromPointer(addr, int(sz))
txt := make([]byte, int(sz))
copy(txt, tp)
fs := "some random text, from %d,%S,%T"
s1 := fmt.Sprintf(fs, 233, "miliao test sprintf", addr)
info := &CodeInfo{}
origin, err := CopyFunction(true, fmt.Sprintf, mySprintf, info)
assert.Nil(t, err)
assert.Equal(t, len(txt), len(origin))
assert.Equal(t, txt, origin)
s2 := mySprintf(fs, 233, "miliao test sprintf", addr)
assert.Equal(t, s1, s2)
addr2 := GetFuncAddr(fmt.Sprintf)
sz2, _ := GetFuncSizeByGuess(GetArchMode(), addr2, true)
sz3, _ := GetFuncSizeByGuess(GetArchMode(), addr, true)
assert.Equal(t, sz2, sz3)
}
type DataHolder struct {
size int
addr []string
close chan int
channel chan *DataHolder
}
func foo_return_orig(from *DataHolder, addr []string) *DataHolder {
dh := &DataHolder{
size: from.size + 8,
close: make(chan int, from.size+2),
channel: make(chan *DataHolder, from.size+2),
}
// origin func doesn't store argument 'addr'
runtime.GC()
runtime.GC()
from.close = make(chan int, from.size+3)
fmt.Printf("done foo_return_orig\n")
return dh
}
func foo_return_replace(from *DataHolder, addr []string) *DataHolder {
dummy := make([]int, 10*1024*1024)
fmt.Printf("dummy data, size:%d\n", len(dummy))
// replace func DOES store argument 'addr'
/// this will trick golang escape analysis to make wrong decisions regarding whether to heap allocate 'addr'
dh := &DataHolder{
addr: addr,
size: from.size,
close: make(chan int, from.size),
channel: make(chan *DataHolder, from.size+1),
}
// fixing escape analysis by always deep copy slice.
dh.addr = make([]string, len(addr))
copy(dh.addr, addr)
origin := foo_return_trampoline(from, addr)
from.close = make(chan int, origin.size)
go func() {
select {
case <-dh.close:
return
}
}()
runtime.GC()
from.channel = make(chan *DataHolder, 22)
runtime.GC()
runtime.GC()
runtime.GC()
runtime.GC()
fmt.Printf("done foo_return_replace\n")
return dh
}
func foo_return_trampoline(from *DataHolder, addr []string) *DataHolder {
for {
if (from.size % 2) != 0 {
fmt.Printf("even from size:%d\n", from.size)
} else {
from.size++
}
if from.size > 100 {
break
}
buff := bytes.NewBufferString("something weird")
fmt.Printf("len:%d\n", buff.Len())
}
from.close = make(chan int, 11)
return from
}
func TestGarbageCollection(t *testing.T) {
err := Hook(foo_return_orig, foo_return_replace, foo_return_trampoline)
assert.Nil(t, err)
addr := []string{"mm11111111111vvvvvvvv", "=ggslsllllllllllllllll"}
dh := &DataHolder{size: 32}
dh2 := foo_return_orig(dh, addr)
for i := 0; i < 10; i++ {
runtime.GC()
runtime.GC()
assert.NotNil(t, dh2)
runtime.GC()
runtime.GC()
}
dh.close <- 1
}

8
vendor/github.com/brahma-adshonor/gohook/go.mod generated vendored Normal file
View File

@ -0,0 +1,8 @@
module github.com/brahma-adshonor/gohook
go 1.11
require (
github.com/stretchr/testify v1.3.0
golang.org/x/arch v0.0.0-20190312162104-788fe5ffcd8c
)

10
vendor/github.com/brahma-adshonor/gohook/go.sum generated vendored Normal file
View File

@ -0,0 +1,10 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/arch v0.0.0-20190312162104-788fe5ffcd8c h1:Rx/HTKi09myZ25t1SOlDHmHOy/mKxNAcu0hP1oPX9qM=
golang.org/x/arch v0.0.0-20190312162104-788fe5ffcd8c/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

172
vendor/github.com/brahma-adshonor/gohook/hook.go generated vendored Normal file
View File

@ -0,0 +1,172 @@
package gohook
import (
"bytes"
"errors"
"fmt"
"reflect"
"unsafe"
)
type HookInfo struct {
Mode int
Info *CodeInfo
Target reflect.Value
Replacement reflect.Value
Trampoline reflect.Value
}
var (
archMode = 64
g_all = make(map[uintptr]HookInfo)
)
func init() {
sz := unsafe.Sizeof(uintptr(0))
if sz == 4 {
archMode = 32
}
}
func GetArchMode() int {
return archMode
}
// valueStruct is taken from runtime source code.
// it may be changed in later release
type valueStruct struct {
typ uintptr
ptr uintptr
}
func getDataPtrFromValue(v reflect.Value) uintptr {
return (uintptr)((*valueStruct)(unsafe.Pointer(&v)).ptr)
}
func ShowDebugInfo() string {
buff := bytes.NewBuffer(make([]byte, 0, 256))
for k, v := range g_all {
s := fmt.Sprintf("hook function at addr:%x, how:%s, num of instruction fixed:%d\n", k, v.Info.How, len(v.Info.Fix))
buff.WriteString(s)
for _, f := range v.Info.Fix {
s = fmt.Sprintf("==@%08x new inst:", f.Addr)
buff.WriteString(s)
for _, c := range f.Code {
s = fmt.Sprintf("%02x ", c)
buff.WriteString(s)
}
s = fmt.Sprintf("\n")
buff.WriteString(s)
}
}
return string(buff.Bytes())
}
func Hook(target, replacement, trampoline interface{}) error {
t := reflect.ValueOf(target)
r := reflect.ValueOf(replacement)
t2 := reflect.ValueOf(trampoline)
return doHook(archMode, false, t, r, t2)
}
func HookByIndirectJmp(target, replacement, trampoline interface{}) error {
t := reflect.ValueOf(target)
r := reflect.ValueOf(replacement)
t2 := reflect.ValueOf(trampoline)
return doHook(archMode, true, t, r, t2)
}
func UnHook(target interface{}) error {
t := reflect.ValueOf(target)
return doUnHook(t.Pointer())
}
func HookMethod(instance interface{}, method string, replacement, trampoline interface{}) error {
target := reflect.TypeOf(instance)
m, ok := target.MethodByName(method)
if !ok {
return fmt.Errorf("unknown method %s.%s()", target.Name(), method)
}
r := reflect.ValueOf(replacement)
t := reflect.ValueOf(trampoline)
return doHook(archMode, false, m.Func, r, t)
}
func UnHookMethod(instance interface{}, methodName string) error {
target := reflect.TypeOf(instance)
m, ok := target.MethodByName(methodName)
if !ok {
return errors.New(fmt.Sprintf("unknown method %s", methodName))
}
return UnHook(m.Func.Interface())
}
func doUnHook(target uintptr) error {
info, ok := g_all[target]
if !ok {
return errors.New("target not exist")
}
CopyInstruction(target, info.Info.Origin)
if info.Info.How == "fix" {
for _, v := range info.Info.Fix {
CopyInstruction(v.Addr, v.Code)
}
}
if info.Trampoline.IsValid() {
CopyInstruction(info.Trampoline.Pointer(), info.Info.TrampolineOrig)
}
delete(g_all, target)
return nil
}
func doHook(mode int, rdxIndirect bool, target, replacement, trampoline reflect.Value) error {
if target.Kind() != reflect.Func {
return fmt.Errorf("target must be a Func")
}
if replacement.Kind() != reflect.Func {
return fmt.Errorf("replacement must be a Func")
}
if target.Type() != replacement.Type() {
return fmt.Errorf("target and replacement must have the same type %s != %s", target.Type().Name(), replacement.Type().Name())
}
tp := uintptr(0)
if trampoline.IsValid() {
if trampoline.Kind() != reflect.Func {
return fmt.Errorf("replacement must be a Func")
}
if target.Type() != trampoline.Type() {
return fmt.Errorf("target and trampoline must have the same type %s != %s", target.Type().Name(), trampoline.Type().Name())
}
tp = trampoline.Pointer()
}
doUnHook(target.Pointer())
replaceAddr := replacement.Pointer()
if rdxIndirect {
// get data ptr out of a reflect value.
replaceAddr = getDataPtrFromValue(replacement)
}
info, err := hookFunction(mode, rdxIndirect, target.Pointer(), replaceAddr, tp)
if err != nil {
return err
}
g_all[target.Pointer()] = HookInfo{Mode: mode, Info: info, Target: target, Replacement: replacement, Trampoline: trampoline}
return nil
}

273
vendor/github.com/brahma-adshonor/gohook/hook_test.go generated vendored Normal file
View File

@ -0,0 +1,273 @@
package gohook
import (
"bytes"
"fmt"
"reflect"
"sync"
"testing"
"github.com/stretchr/testify/assert"
)
func myPrintf(f string, a ...interface{}) (n int, err error) {
myPrintfTramp("prefixed by miliao -- ")
return myPrintfTramp(f, a...)
}
//go:noinline
func myPrintfTramp(f string, a ...interface{}) (n int, err error) {
fmt.Printf("hello")
fmt.Printf("hello")
fmt.Printf("hello")
fmt.Printf("hello")
fmt.Printf("hello")
return fmt.Printf("hello")
}
func init() {
fmt.Printf("test file init()\n")
err := Hook(fmt.Printf, myPrintf, myPrintfTramp)
if err != nil {
fmt.Printf("err:%s\n", err.Error())
} else {
fmt.Printf("hook fmt.Printf() done\n")
}
fmt.Printf("debug info for init():%s\n", ShowDebugInfo())
}
//go:noinline
func foo1(v1 int, v2 string) int {
fmt.Printf("foo1:%d(%s)\n", v1, v2)
return v1 + 42
}
func foo2(v1 int, v2 string) int {
fmt.Printf("foo2:%d(%s)\n", v1, v2)
v1 = foo3(100, "not calling foo3")
return v1 + 4200
}
//go:noinline
func foo3(v1 int, v2 string) int {
fmt.Printf("foo3:%d(%s)\n", v1, v2)
return v1 + 10000
}
func myByteContain(a, b []byte) bool {
fmt.Printf("calling fake bytes.Contain()\n")
return false
}
func TestHook(t *testing.T) {
ResetFuncPrologue()
fmt.Printf("start testing...\n")
ret1 := foo1(23, "sval for foo1")
assert.Equal(t, 65, ret1)
err := Hook(foo1, foo2, foo3)
assert.Nil(t, err)
ret2 := foo1(23, "sval for foo1")
assert.Equal(t, 4342, ret2)
ret4 := foo3(100, "vvv")
assert.Equal(t, 142, ret4)
UnHook(foo1)
ret3 := foo1(23, "sval for foo1")
assert.Equal(t, 65, ret3)
ret5 := foo3(100, "vvv")
assert.Equal(t, 10100, ret5)
ret6 := bytes.Contains([]byte{1, 2, 3}, []byte{2, 3})
assert.Equal(t, true, ret6)
err = Hook(bytes.Contains, myByteContain, nil)
assert.Nil(t, err)
fun := bytes.Contains // prevent inline
ret7 := fun([]byte{1, 2, 3}, []byte{2, 3})
assert.Equal(t, false, ret7)
UnHook(bytes.Contains)
ret8 := bytes.Contains([]byte{1, 2, 3}, []byte{2, 3})
assert.Equal(t, true, ret8)
}
func myBuffLen(b *bytes.Buffer) int {
fmt.Println("calling myBuffLen")
return 0 + myBuffLenTramp(b)
}
//go:noinline
func myBuffLenTramp(b *bytes.Buffer) int {
fmt.Println("calling myBuffLenTramp")
return 1000
}
func myBuffGrow(b *bytes.Buffer, n int) {
fmt.Println("fake buffer grow func")
}
func myBuffWriteString(b *bytes.Buffer, s string) (int, error) {
fmt.Printf("fake buffer WriteString func, s:%s\n", s)
l, _ := myBuffWriteStringTramp(b, s)
return 1000 + l, nil
}
func myBuffWriteStringTramp(b *bytes.Buffer, s string) (int, error) {
fmt.Printf("fake buffer WriteString tramp, s:%s\n", s)
fmt.Printf("fake buffer WriteString tramp, s:%s\n", s)
fmt.Printf("fake buffer WriteString tramp, s:%s\n", s)
fmt.Printf("fake buffer WriteString tramp, s:%s\n", s)
fmt.Printf("fake buffer WriteString tramp, s:%s\n", s)
fmt.Printf("fake buffer WriteString tramp, s:%s\n", s)
return 0, nil
}
func TestInstanceHook(t *testing.T) {
ResetFuncPrologue()
buff1 := bytes.NewBufferString("abcd")
assert.Equal(t, 4, buff1.Len())
err1 := HookMethod(buff1, "Grow", myBuffGrow, nil)
err2 := HookMethod(buff1, "Len", myBuffLen, myBuffLenTramp)
assert.Nil(t, err1)
assert.Nil(t, err2)
assert.Equal(t, 4, buff1.Len()) // Len() is inlined
buff1.Grow(233) // no grow
assert.Equal(t, 4, buff1.Len()) // Len() is inlined
err3 := HookMethod(buff1, "WriteString", myBuffWriteString, myBuffWriteStringTramp)
assert.Nil(t, err3)
sz1, _ := buff1.WriteString("miliao")
assert.Equal(t, 1006, sz1)
assert.Equal(t, 10, buff1.Len()) // Len() is inlined
err4 := UnHookMethod(buff1, "WriteString")
assert.Nil(t, err4)
flen := buff1.Len
sz2, _ := buff1.WriteString("miliao")
assert.Equal(t, 6, sz2)
assert.Equal(t, 16, flen()) // Len() is inlined
sz3, _ := myBuffWriteStringTramp(nil, "sssssss")
assert.Equal(t, 0, sz3)
}
//go:noinline
func foov(v int) int {
v2 := v*v + 2*v
return v + v2
}
func foor(v int) int {
if v%2 == 0 {
fmt.Printf("vvvvvvv:%d\n", v)
}
return v + 1 + foot(v)
}
//go:noinline
func foot(v int) int {
fmt.Printf("fake func hold:%+v\n", v)
fmt.Printf("fake func hold:%+v\n", v)
fmt.Printf("fake func hold:%+v\n", v)
if v%3 == 0 {
panic("vvvv")
} else {
v = v*v + 23
}
return v
}
func TestHookByIndirectJmp(t *testing.T) {
ResetFuncPrologue()
v := foov(3)
assert.Equal(t, 3*3+2*3+3, v)
err := HookByIndirectJmp(foov, foor, foot)
assert.Nil(t, err)
v2 := foov(3)
assert.Equal(t, 3+1+v, v2)
}
type cfunc func(int) int
func getClosureFunc(v int) (cfunc, cfunc) {
vv := v + 1
f1 := func(v2 int) int {
fmt.Printf("f111111, v:%d\n", v2) // prevent inline
v3 := v2*v2 + vv*v + v
return v3
}
vv2 := v + 10
f2 := func(v2 int) int {
return v2 + vv2 + v
}
return f1, f2
}
func TestClosure(t *testing.T) {
ResetFuncPrologue()
f1, f2 := getClosureFunc(200)
// use Hook() will fail
err := HookByIndirectJmp(f1, f2, nil)
assert.Nil(t, err)
var wg sync.WaitGroup
wg.Add(1)
go func() {
v1 := f1(2)
assert.Equal(t, 2+200+10+200, v1)
wg.Done()
}()
wg.Wait()
}
//go:noinline
func tfunc(v int) int {
fmt.Printf("vvvvv:%v\n", v)
return v*v*100 + 123 + v
}
func TestMakeFunc(t *testing.T) {
ResetFuncPrologue()
replace := func(in []reflect.Value) []reflect.Value {
v := int(in[0].Int())
ret := reflect.ValueOf(v + 1000)
return []reflect.Value{ret}
}
f := reflect.MakeFunc(reflect.ValueOf(tfunc).Type(), replace)
// use Hook() will panic
err := HookByIndirectJmp(tfunc, f.Interface(), nil)
assert.Nil(t, err)
v := tfunc(int(3))
assert.Equal(t, 3+1000, v)
}

View File

@ -0,0 +1,391 @@
package gohook
import (
"bytes"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func inplaceFix(a, b, c int, e, f, g string) int {
fmt.Printf("calling victim()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling victim()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling victim()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling victim()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling victim()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling victim()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling victim()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling victim()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling victim()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling victim()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
for {
if (a % 2) != 0 {
fmt.Printf("calling victim()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
} else {
a++
}
if a+b > 100 {
break
}
buff := bytes.NewBufferString("something weird")
fmt.Printf("len:%d\n", buff.Len())
}
return 1
}
func TestRipRelativeAddr(t *testing.T) {
toAddr := uintptr(0x10101042)
code := []byte{0x48, 0x8b, 0x05, 0x11, 0x22, 0x33, 0x44}
addr := calcJumpToAbsAddr(GetArchMode(), toAddr, code)
assert.Equal(t, toAddr+7+0x44332211, addr)
}
func TestFixInplace(t *testing.T) {
d0 := int32(0x01b1)
d1 := byte(0xb2) // byte(-78)
d2 := byte(0xa8) // byte(-88)
prefix1 := []byte{
0x64, 0x48, 0x8b, 0x0c, 0x25, 0xf8, 0xff, 0xff, 0xff, // 9
0x48, 0x3b, 0x61, 0x10, // 4
}
jc0 := []byte{
0x0f, 0x86, byte(d0), byte(d0 >> 8), 0x00, 0x00, // jmp
}
prefix2 := []byte{
0x48, 0x83, 0xec, 0x58, // 4
0x48, 0x89, 0x6c, 0x24, 0x50, // 5
0x48, 0x8d, 0x6c, 0x24, 0x50, // 5
0x90, // 1
}
prefix3rip := []byte{
0x48, 0x8b, 0x05, 0xc7, 0x5f, 0x16, 0x00, // 7 mov $rip offset
}
prefix4rip := []byte{
0x48, 0x8d, 0x0d, 0x50, 0x85, 0x07, 0x00, // 7 lea $rip offset
}
prefix5 := []byte{
0x48, 0x89, 0x0c, 0x24, // 4
0x48, 0x89, 0x44, 0x24, 0x08, // 5
}
prefix6rip := []byte{
0x48, 0x8d, 0x05, 0x9f, 0xe0, 0x04, 0x00, // 7 lea $rip offset
}
prefix7 := []byte{
0x48, 0x89, 0x44, 0x24, 0x10, // 5
0x48, 0xc7, 0x44, 0x24, 0x18, 0x07, 0x00, 0x00, 0x00, // 9
}
// totoal 78 bytes
// short jump
jc1 := []byte{0xeb, d1} // 2
mid := []byte{
0x0f, 0x57, 0xc0, // 3
0x0f, 0x11, 0x44, 0x24, 0x28, // 5
}
jc2 := []byte{
// condition jump
0x77, d2, // 2
}
posfix := []byte{
// trailing
0xcc, 0xcc, 0xcc, 0xcc,
0xcc, 0xcc, 0xcc, 0xcc,
0xcc, 0xcc, 0xcc, 0xcc,
0xcc, 0xcc, 0xcc, 0xcc,
}
var fc []byte
all := [][]byte{prefix1, jc0, prefix2, prefix3rip, prefix4rip, prefix5, prefix6rip, prefix7, jc1, mid, jc2, posfix}
for k := range all {
fc = append(fc, all[k]...)
}
info := &CodeInfo{}
addr := GetFuncAddr(inplaceFix)
size := len(fc)
mvSize := 0x09
toAddr := GetFuncAddr(inplaceFix2)
curAddr1 := addr + uintptr(78)
curAddr2 := addr + uintptr(78) + uintptr(10)
CopyInstruction(addr, fc)
fs := makeSliceFromPointer(addr, len(fc))
raw := make([]byte, len(fc))
copy(raw, fs)
fmt.Printf("src func:%x, target func:%x\n", addr, toAddr)
err := doFixFuncInplace(64, addr, toAddr, int(size), mvSize, info, 5)
assert.Nil(t, err)
assert.True(t, len(raw) >= len(info.Origin))
raw = raw[:len(info.Origin)]
assert.Equal(t, raw, info.Origin)
assert.Equal(t, 18, len(info.Fix))
assert.Equal(t, prefix1[:5], fs[:5])
off0 := d0 + 4
fix0, _ := adjustInstructionOffset(jc0, int64(off0))
fmt.Printf("inplace fix, off0:%x, sz:%d\n", off0, len(fix0))
rip3Addr := addr + uintptr(len(prefix1)+len(jc0)+len(prefix2))
ripOff3 := calcJumpToAbsAddr(GetArchMode(), rip3Addr, prefix3rip) - uintptr(len(prefix3rip)) - rip3Addr + uintptr(4)
prefix3ripFix, _ := adjustInstructionOffset(prefix3rip, int64(ripOff3))
rip4Addr := addr + uintptr(len(prefix1)+len(jc0)+len(prefix2)+len(prefix3rip))
ripOff4 := calcJumpToAbsAddr(GetArchMode(), rip4Addr, prefix4rip) - uintptr(len(prefix4rip)) - rip4Addr + uintptr(4)
prefix4ripFix, _ := adjustInstructionOffset(prefix4rip, int64(ripOff4))
rip6Addr := addr + uintptr(len(prefix1)+len(jc0)+len(prefix2)+len(prefix3rip)+len(prefix4rip)+len(prefix5))
ripOff6 := calcJumpToAbsAddr(GetArchMode(), rip6Addr, prefix6rip) - uintptr(len(prefix6rip)) - rip6Addr + uintptr(4)
prefix6ripFix, _ := adjustInstructionOffset(prefix6rip, int64(ripOff6))
to1 := curAddr1 + uintptr(2) + uintptr(int32(int8(d1)))
newTo1 := toAddr + to1 - addr
off1 := int64(newTo1 - (curAddr1 - uintptr(4)) - 5)
fix1, _ := translateJump(off1, jc1)
fmt.Printf("inplace fix, off1:%x, sz:%d\n", off1, len(fix1))
to2 := curAddr2 + uintptr(2) + uintptr(int32(int8(d2)))
newTo2 := toAddr + to2 - addr
off2 := int64(newTo2 - (curAddr2 + uintptr(3) - uintptr(4)) - 6)
fix2, _ := translateJump(off2, jc2)
fmt.Printf("inplace fix, off2:%x, sz:%d\n", off2, len(fix2))
all2 := [][]byte{prefix1[:5], prefix1[9:], fix0, prefix2, prefix3ripFix, prefix4ripFix, prefix5, prefix6ripFix, prefix7, fix1, mid, fix2, posfix}
var fc2 []byte
for k := range all2 {
fc2 = append(fc2, all2[k]...)
}
assert.Equal(t, len(fc)-4+3+4, len(fc2))
fs = makeSliceFromPointer(addr, len(fc2)-len(posfix))
assert.Equal(t, fc2[:len(fc2)-len(posfix)], fs)
}
func inplaceFix2(a, b, c int, e, f, g string) int {
fmt.Printf("calling inplacefix2()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling inplacefix2()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling inplacefix2()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling inplacefix2()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling inplacefix2()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling inplacefix2()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling inplacefix2()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling inplacefix2()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling inplacefix2()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
fmt.Printf("calling inplacefix2()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
for {
if (a % 2) != 0 {
fmt.Printf("calling victim()(%d,%s,%s,%x):%dth\n", a, e, f, c, 0x23)
} else {
a++
}
if a+b > 100 {
break
}
buff := bytes.NewBufferString("something weird")
fmt.Printf("len:%d\n", buff.Len())
}
return 1
}
func foo_for_inplace_fix(id string) string {
c := 0
for {
fmt.Printf("calling victim\n")
if id == "miliao" {
return "done"
}
c++
if c > len(id) {
break
}
}
fmt.Printf("len:%d\n", len(id))
return id + "xxx"
}
func foo_for_inplace_fix_delimiter(id string) string {
for {
fmt.Printf("calling victim trampoline")
if id == "miliao" {
return "done"
}
break
}
ret := "miliao"
ret += foo_for_inplace_fix("test")
ret += foo_for_inplace_fix("test")
ret += foo_for_inplace_fix("test")
ret += foo_for_inplace_fix("test")
fmt.Printf("len1:%d\n", len(id))
fmt.Printf("len2:%d\n", len(ret))
return id + ret
}
func foo_for_inplace_fix_replace(id string) string {
c := 0
for {
fmt.Printf("calling foo_for_inplace_fix_replace\n")
if id == "miliao" {
return "done"
}
c++
if c > len(id) {
break
}
}
// TODO uncomment following
foo_for_inplace_fix_trampoline("miliao")
fmt.Printf("len:%d\n", len(id))
return id + "xxx2"
}
func foo_for_inplace_fix_trampoline(id string) string {
c := 0
for {
fmt.Printf("calling foo_for_inplace_fix_trampoline\n")
if id == "miliao" {
return "done"
}
c++
if c > len(id) {
break
}
}
fmt.Printf("len:%d\n", len(id))
return id + "xxx3"
}
func TestInplaceFixAtMoveArea(t *testing.T) {
code := []byte{
/*
0x48, 0x8b, 0x48, 0x08, // mov 0x8(%rax),%rcx
0x74, 0x4, // jbe
0x48, 0x8b, 0x48, 0x18, // sub 0x18(%rax), %rcx
0x48, 0x89, 0x4c, 0x24, 0x10, // %rcx, 0x10(%rsp)
0xc3, // retq
0xcc, 0xcc,
*/
0x90, 0x90,
0xeb, 0x04, // jmp 4
0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90,
0xc3,
0x74, 0xf0, // jbe -16
0xcc, 0xcc, 0xcc, 0xcc,
0xcc, 0xcc, 0xcc, 0xcc,
}
target := GetFuncAddr(foo_for_inplace_fix)
replace := GetFuncAddr(foo_for_inplace_fix_replace)
trampoline := GetFuncAddr(foo_for_inplace_fix_trampoline)
assert.True(t, isByteOverflow((int32)(trampoline-target)))
CopyInstruction(target, code)
fmt.Printf("short call target:%x, replace:%x, trampoline:%x\n", target, replace, trampoline)
err1 := Hook(foo_for_inplace_fix, foo_for_inplace_fix_replace, foo_for_inplace_fix_trampoline)
assert.Nil(t, err1)
fmt.Printf("debug info:%s\n", ShowDebugInfo())
msg1 := foo_for_inplace_fix("txt")
fmt.Printf("calling foo inplace fix func\n")
assert.Equal(t, "txtxxx2", msg1)
sz1 := 5
na1 := trampoline + uintptr(2)
ta1 := target + uintptr(2+5+4-3)
off1 := ta1 - (na1 + uintptr(sz1))
sz2 := 6
na2 := target + uintptr(15+3-3)
ta2 := trampoline + uintptr(1)
off2 := ta2 - (na2 + uintptr(sz2))
fmt.Printf("off1:%x, off2:%x\n", off1, off2)
ret := []byte{
0x90, 0x90,
0xe9, 0x74, 0xfc, 0xff, 0xff,
0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90,
0xc3,
0x0f, 0x84, 0x80, 0x03, 0x00, 0x00,
0xcc, 0xcc, 0xcc,
}
ret[3] = byte(off1)
ret[4] = byte(off1 >> 8)
ret[5] = byte(off1 >> 16)
ret[6] = byte(off1 >> 24)
ret[20] = byte(off2)
ret[21] = byte(off2 >> 8)
ret[22] = byte(off2 >> 16)
ret[23] = byte(off2 >> 24)
fc1 := makeSliceFromPointer(target, len(ret))
fc2 := makeSliceFromPointer(trampoline, len(ret))
assert.Equal(t, ret[:8], fc2[:8])
assert.Equal(t, byte(0xe9), fc2[8])
assert.Equal(t, ret[8:], fc1[5:len(ret)-3])
code2 := []byte{
0x90, 0x90, 0x90, 0x90,
0x74, 0x04,
0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90,
0xc3, 0xcc, 0x90,
}
err2 := UnHook(foo_for_inplace_fix)
assert.Nil(t, err2)
msg2 := foo_for_inplace_fix_trampoline("txt")
assert.Equal(t, "txtxxx3", msg2)
msg3 := foo_for_inplace_fix_replace("txt2")
assert.Equal(t, "txt2xxx2", msg3)
CopyInstruction(target, code2)
fsz, _ := GetFuncSizeByGuess(GetArchMode(), target, false)
assert.Equal(t, len(code2)-1, int(fsz))
}

View File

@ -0,0 +1,329 @@
package gohook
import (
"reflect"
"testing"
"unsafe"
"github.com/stretchr/testify/assert"
)
func TestGetInsLenGreaterThan(t *testing.T) {
ResetFuncPrologue()
c1 := []byte{0x64, 0x48, 0x8b, 0x0c, 0x25, 0xf8}
c2 := []byte{0x64, 0x48, 0x8b, 0x0c, 0x25, 0xf8, 0xff, 0xff, 0xff}
r1 := GetInsLenGreaterThan(64, c1, len(c1)-2)
assert.Equal(t, 0, r1)
r2 := GetInsLenGreaterThan(64, c2, len(c2)-2)
assert.Equal(t, len(c2), r2)
r22 := GetInsLenGreaterThan(64, c2, len(c2))
assert.Equal(t, len(c2), r22)
r23 := GetInsLenGreaterThan(64, c2, len(c2)+2)
assert.Equal(t, 0, r23)
c3 := []byte{0x64, 0x48, 0x8b, 0x0c, 0x25, 0xf8, 0xff, 0xff, 0xff, 0x48, 0x3b, 0x41, 0x10}
r3 := GetInsLenGreaterThan(64, c3, len(c2)+2)
assert.Equal(t, len(c3), r3)
r32 := GetInsLenGreaterThan(64, c3, len(c2)-2)
assert.Equal(t, len(c2), r32)
}
func TestFixOneInstructionForTwoByteJmp(t *testing.T) {
ResetFuncPrologue()
// jump from within patching erea to outside, negative fix
c1 := []byte{0x75, 0x40} // jne 64
l1, t1, r1 := FixOneInstruction(64, false, 10, 12, c1, 100, 8)
assert.Equal(t, 2, l1)
assert.Equal(t, FT_CondJmp, t1)
assert.Equal(t, c1[0], r1[0])
assert.Equal(t, int8(-26), int8(r1[1]))
// jump from within patching erea to outside, positive fix
l2, t2, r2 := FixOneInstruction(64, false, 10, 12, c1, 26, 8)
assert.Equal(t, 2, l2)
assert.Equal(t, FT_CondJmp, t2)
assert.Equal(t, c1[0], r2[0])
assert.Equal(t, int8(48), int8(r2[1]))
//overflow test
l3, t3, r3 := FixOneInstruction(64, false, 10, 12, c1, 1000, 8)
assert.Equal(t, 2, l3)
assert.Equal(t, FT_OVERFLOW, t3)
assert.Equal(t, c1[0], r3[0])
assert.Equal(t, c1[1], r3[1])
// overflow test2
c32 := []byte{0x75, 0x7e} // jne 0x7e
l32, t32, r32 := FixOneInstruction(64, false, 30, 32, c32, 10, 8)
assert.Equal(t, 2, l32)
assert.Equal(t, FT_OVERFLOW, t32)
assert.Equal(t, c32[0], r32[0])
assert.Equal(t, c32[1], r32[1])
// jump from outside patching erea to outside of patching erea
l4, t4, r4 := FixOneInstruction(64, false, 10, 18, c1, 100, 4)
assert.Equal(t, 2, l4)
assert.Equal(t, FT_SKIP, t4)
assert.Equal(t, c1[0], r4[0])
assert.Equal(t, c1[1], r4[1])
// jump from outside patching erea to within patching erea
c2 := []byte{0x75, 0xe6} // jne -26
l5, t5, r5 := FixOneInstruction(64, false, 10, 38, c2, 100, 8)
assert.Equal(t, 2, l5)
assert.Equal(t, FT_CondJmp, t5)
assert.Equal(t, c2[0], r5[0])
assert.Equal(t, 64, int(r5[1]))
// jump within patching erea
c3 := []byte{0x75, 0x06} // jne 6
l6, t6, r6 := FixOneInstruction(64, false, 10, 12, c3, 100, 11)
assert.Equal(t, 2, l6)
assert.Equal(t, FT_SKIP, t6)
assert.Equal(t, c3[0], r6[0])
assert.Equal(t, c3[1], r6[1])
// sign test, from outside to outside
c4 := []byte{0x7c, 0xcd} // jne -51
l7, t7, r7 := FixOneInstruction(64, false, 10, 83, c4, 1000, 10)
assert.Equal(t, 2, l7)
assert.Equal(t, FT_SKIP, t7)
assert.Equal(t, c4[0], r7[0])
assert.Equal(t, c4[1], r7[1])
}
func byteToInt32(d []byte) int32 {
v := int32(uint32(d[0]) | (uint32(d[1]) << 8) | (uint32(d[2]) << 16) | (uint32(d[3]) << 24))
return v
}
func TestFixOneInstructionForSixByteJmp(t *testing.T) {
ResetFuncPrologue()
// jump from within patching erea to outside, negative fix
c1 := []byte{0x0f, 0x8d, 0x10, 0x00, 0x00, 0x00} // jge 16
l1, t1, r1 := FixOneInstruction(64, false, 20, 22, c1, 100, 8)
assert.Equal(t, 6, l1)
assert.Equal(t, FT_CondJmp, t1)
assert.Equal(t, c1[0], r1[0])
assert.Equal(t, c1[1], r1[1])
assert.Equal(t, int32(-64), byteToInt32(r1[2:]))
// jump from within patching erea to outside, positive fix
c2 := []byte{0x0f, 0x8d, 0x40, 0x00, 0x00, 0x00} // jge 64
l2, t2, r2 := FixOneInstruction(64, false, 2, 4, c2, 32, 9)
assert.Equal(t, 6, l2)
assert.Equal(t, FT_CondJmp, t2)
assert.Equal(t, c2[0], r2[0])
assert.Equal(t, c2[1], r2[1])
assert.Equal(t, int32(34), byteToInt32(r2[2:]))
// overflow test
c3 := []byte{0x0f, 0x8d, 0xfe, 0xff, 0xff, 0x7f} // jge 64
l3, t3, r3 := FixOneInstruction(64, false, 10000, 10004, c3, 100, 16)
assert.Equal(t, 6, l3)
assert.Equal(t, FT_OVERFLOW, t3)
assert.Equal(t, c3[0], r3[0])
assert.Equal(t, c3[1], r3[1])
assert.Equal(t, c3[2], r3[2])
assert.Equal(t, c3[3], r3[3])
assert.Equal(t, c3[4], r3[4])
assert.Equal(t, c3[5], r3[5])
// jump from outside patching erea to outside of patching erea
c4 := []byte{0x0f, 0x8d, 0x40, 0x00, 0x00, 0x00} // jge 64
l4, t4, r4 := FixOneInstruction(64, false, 10, 33, c4, 22, 9)
assert.Equal(t, 6, l4)
assert.Equal(t, FT_SKIP, t4)
assert.Equal(t, c4[0], r4[0])
assert.Equal(t, c4[1], r4[1])
assert.Equal(t, c4[2], r4[2])
assert.Equal(t, c4[3], r4[3])
assert.Equal(t, c4[4], r4[4])
assert.Equal(t, c4[5], r4[5])
// jump from outside patching erea to within patching erea
c5 := []byte{0x0f, 0x85, 0xce, 0xff, 0xff, 0xff} // jne -50
l5, t5, r5 := FixOneInstruction(64, false, 10, 60, c5, 1000, 9)
assert.Equal(t, 6, l5)
assert.Equal(t, FT_CondJmp, t5)
assert.Equal(t, c5[0], r5[0])
assert.Equal(t, c5[1], r5[1])
assert.Equal(t, int32(940), byteToInt32(r5[2:]))
// jump within patching erea
c6 := []byte{0x0f, 0x85, 0x10, 0x00, 0x00, 0x00} // jne 16
l6, t6, r6 := FixOneInstruction(64, false, 10, 12, c6, 1000, 30)
assert.Equal(t, 6, l6)
assert.Equal(t, FT_SKIP, t6)
assert.Equal(t, c6[0], r6[0])
assert.Equal(t, c6[1], r6[1])
assert.Equal(t, c6[2], r6[2])
assert.Equal(t, c6[3], r6[3])
assert.Equal(t, c6[4], r6[4])
assert.Equal(t, c6[5], r6[5])
}
func TestFixOneInstructionForFixByteJmp(t *testing.T) {
// jump from within patching erea to outside, negative fix
c1 := []byte{0xe9, 0x10, 0x00, 0x00, 0x00} // jmp 16
l1, t1, r1 := FixOneInstruction(64, false, 20, 22, c1, 100, 8)
assert.Equal(t, 5, l1)
assert.Equal(t, FT_JMP, t1)
assert.Equal(t, c1[0], r1[0])
assert.Equal(t, int32(-64), byteToInt32(r1[1:]))
// jump from within patching erea to outside, positive fix
c2 := []byte{0xe9, 0x40, 0x00, 0x00, 0x00} // jmp 64
l2, t2, r2 := FixOneInstruction(64, false, 2, 4, c2, 32, 9)
assert.Equal(t, 5, l2)
assert.Equal(t, FT_JMP, t2)
assert.Equal(t, c2[0], r2[0])
assert.Equal(t, int32(34), byteToInt32(r2[1:]))
// overflow test
c3 := []byte{0xe9, 0xfe, 0xff, 0xff, 0x7f} // jmp 64
l3, t3, r3 := FixOneInstruction(64, false, 10000, 10004, c3, 100, 16)
assert.Equal(t, 5, l3)
assert.Equal(t, FT_OVERFLOW, t3)
assert.Equal(t, c3[0], r3[0])
assert.Equal(t, c3[1], r3[1])
assert.Equal(t, c3[2], r3[2])
assert.Equal(t, c3[3], r3[3])
assert.Equal(t, c3[4], r3[4])
// jump from outside patching erea to outside of patching erea
c4 := []byte{0xe9, 0x40, 0x00, 0x00, 0x00} // jmp 64
l4, t4, r4 := FixOneInstruction(64, false, 10, 33, c4, 22, 9)
assert.Equal(t, 5, l4)
assert.Equal(t, FT_SKIP, t4)
assert.Equal(t, c4[0], r4[0])
assert.Equal(t, c4[1], r4[1])
assert.Equal(t, c4[2], r4[2])
assert.Equal(t, c4[3], r4[3])
assert.Equal(t, c4[4], r4[4])
// jump from outside patching erea to within patching erea
c5 := []byte{0xe9, 0xce, 0xff, 0xff, 0xff} // jmp -50
l5, t5, r5 := FixOneInstruction(64, false, 10, 60, c5, 1000, 9)
assert.Equal(t, 5, l5)
assert.Equal(t, FT_JMP, t5)
assert.Equal(t, c5[0], r5[0])
assert.Equal(t, int32(940), byteToInt32(r5[1:]))
// jump within patching erea
c6 := []byte{0xe9, 0x10, 0x00, 0x00, 0x00} // jmp 16
l6, t6, r6 := FixOneInstruction(64, false, 10, 12, c6, 1000, 30)
assert.Equal(t, 5, l6)
assert.Equal(t, FT_SKIP, t6)
assert.Equal(t, c6[0], r6[0])
assert.Equal(t, c6[1], r6[1])
assert.Equal(t, c6[2], r6[2])
assert.Equal(t, c6[3], r6[3])
assert.Equal(t, c6[4], r6[4])
// jump from outside to outside, sign test
c7 := []byte{0xe8, 0xdc, 0xfb, 0xff, 0xff} // jmp -1060
l7, t7, r7 := FixOneInstruction(64, false, 2000, 4100, c7, 10000, 30)
assert.Equal(t, 5, l7)
assert.Equal(t, FT_SKIP, t7)
assert.Equal(t, c7[0], r7[0])
assert.Equal(t, c7[1], r7[1])
assert.Equal(t, c7[2], r7[2])
assert.Equal(t, c7[3], r7[3])
assert.Equal(t, c7[4], r7[4])
}
func TestFixFuncCode(t *testing.T) {
p := []byte{0x64, 0x48, 0x8b, 0x0c, 0x25, 0xf8, 0xff, 0xff, 0xff} // move %fs:0xfffffffffffffff8, %rcx
c1 := []byte{
/*0:*/ 0x64, 0x48, 0x8b, 0x0c, 0x25, 0xf8, 0xff, 0xff, 0xff, // move %fs:0xfffffffffffffff8, %rcx sz:9
/*9:*/ 0x48, 0x8d, 0x44, 0x24, 0xe0, // lea -0x20(%rsp),%rax sz:5
/*14:*/ 0x48, 0x3b, 0x41, 0x10, // cmp 0x10(%rcx),%rax sz:4
/*18:*/ 0x0f, 0x86, 0xc3, 0x01, 0x00, 0x00, // jbe 451 sz:6
/*24:*/ 0x48, 0x81, 0xec, 0xa0, 0x00, 0x00, 0x00, // sub $0xa0,%rsp sz:7
/*31:*/ 0x48, 0x8b, 0x9c, 0x24, 0xa8, 0x00, 0x00, 0x00, // mov 0xa8(%rsp),%rbx sz:8
/*39:*/ 0xe3, 0x02, // jmp 02 sz:2
/*41:*/ 0x90, // nop sz:1
/*42:*/ 0x90, // nop sz:1
/*43:*/ 0x90, // nop sz:1
/*44:*/ 0x90, // nop sz:1
//////////patching erea end: 45 bytes/////////////////////////////////////////
/*45:*/ 0x48, 0x89, 0x5c, 0x24, 0x40, // mov %rbx,0x40(%rsp) sz:5
/*50:*/ 0xe9, 0xd2, 0xff, 0xff, 0xff, // jmp -46 sz:5
/*55:*/ 0x90, // nop sz:1
/*56:*/ 0x90, // nop sz:1
/*57:*/ 0x90, // nop sz:1
/*58:*/ 0x90, // nop sz:1
}
SetFuncPrologue(64, []byte{0x64, 0x48, 0x8b, 0x0c, 0x25, 0xf8, 0xff, 0xff, 0xff, 0x48})
sh1 := (*reflect.SliceHeader)((unsafe.Pointer(&c1)))
move_sz := 45
startAddr := sh1.Data
toAddr := startAddr + 100000
fix1, err1 := FixTargetFuncCode(64, startAddr, uint32(len(c1)), toAddr, move_sz)
assert.Nil(t, err1)
assert.Equal(t, 2, len(fix1))
assert.Equal(t, startAddr+uintptr(18), fix1[0].Addr)
assert.Equal(t, startAddr+uintptr(50), fix1[1].Addr)
assert.Equal(t, 6, len(fix1[0].Code))
assert.Equal(t, byte(0x0f), fix1[0].Code[0])
assert.Equal(t, byte(0x86), fix1[0].Code[1])
assert.Equal(t, int32(startAddr+451-toAddr), byteToInt32(fix1[0].Code[2:]))
assert.Equal(t, 5, len(fix1[1].Code))
assert.Equal(t, byte(0xe9), fix1[1].Code[0])
assert.Equal(t, int32(toAddr+9-startAddr-50-5), byteToInt32(fix1[1].Code[1:]))
c2 := append(c1, p...)
sh2 := (*reflect.SliceHeader)((unsafe.Pointer(&c2)))
startAddr = sh2.Data
toAddr = startAddr + 100000
fix2, err2 := FixTargetFuncCode(64, startAddr, 0, toAddr, move_sz)
assert.Nil(t, err2)
assert.Equal(t, 2, len(fix2))
assert.Equal(t, startAddr+uintptr(18), fix2[0].Addr)
assert.Equal(t, startAddr+uintptr(50), fix2[1].Addr)
assert.Equal(t, 6, len(fix2[0].Code))
assert.Equal(t, byte(0x0f), fix2[0].Code[0])
assert.Equal(t, byte(0x86), fix2[0].Code[1])
assert.Equal(t, int32(startAddr+451-toAddr), byteToInt32(fix2[0].Code[2:]))
assert.Equal(t, 5, len(fix2[1].Code))
assert.Equal(t, byte(0xe9), fix2[1].Code[0])
assert.Equal(t, int32(toAddr+9-startAddr-50-5), byteToInt32(fix2[1].Code[1:]))
}

View File

@ -0,0 +1,141 @@
package gohook
import (
"fmt"
"runtime"
"testing"
"github.com/stretchr/testify/assert"
)
//go:noinline
func foo_short_call(a int) (int, error) {
//fmt.Printf("calling short call origin func\n")
return 3 + foo_short_call2(a), nil
}
//go:noinline
func foo_short_call2(a int) int {
fmt.Printf("in short call2\n")
return 3 + a
}
//go:noinline
func foo_short_call_replace(a int) (int, error) {
fmt.Printf("calling short call replace func\n")
r, _ := foo_short_call_trampoline(a)
return a + 1000 + r, nil
}
func dummy_delimiter(id string) string {
for {
fmt.Printf("calling victim trampoline")
if id == "miliao" {
return "done"
}
break
}
ret := "miliao"
ret += foo_for_inplace_fix("test")
ret += foo_for_inplace_fix("test")
ret += foo_for_inplace_fix("test")
ret += foo_for_inplace_fix("test")
fmt.Printf("len1:%d\n", len(id))
fmt.Printf("len2:%d\n", len(ret))
ret += foo_for_inplace_fix_delimiter(id)
return id + ret
}
//go:noinline
func foo_short_call_trampoline(a int) (int, error) {
for {
fmt.Printf("printing a:%d\n", a)
a++
if a > 233 {
fmt.Printf("done printing a:%d\n", a)
break
}
}
dummy_delimiter("miliao")
return a + 233, nil
}
func TestShortCall(t *testing.T) {
r, _ := foo_short_call(32)
assert.Equal(t, 38, r)
addr := GetFuncAddr(foo_short_call)
sz1 := GetFuncInstSize(foo_short_call)
addr2 := addr + uintptr(sz1)
fmt.Printf("start hook real short call func, start:%x, end:%x\n", addr, addr2)
err := Hook(foo_short_call, foo_short_call_replace, foo_short_call_trampoline)
assert.Nil(t, err)
r1, _ := foo_short_call(22)
assert.Equal(t, 1050, r1)
UnHook(foo_short_call)
r2, _ := foo_short_call(32)
assert.Equal(t, 38, r2)
code := make([]byte, 0, sz1)
for i := 0; i < int(sz1); i++ {
code = append(code, 0x90)
}
code1 := []byte{0xeb, 0x4}
code2 := []byte{0xeb, 0x5}
copy(code, code1)
copy(code[2:], code2)
ret := sz1 - 5
jmp1 := sz1 - 4
jmp2 := sz1 - 2
if sz1 > 0x7f {
ret = 0x70 - 5
jmp1 = 0x70 - 4
jmp2 = 0x70 - 2
}
code[ret] = byte(0xc3)
code3 := []byte{0xeb, byte(-jmp1 - 2)}
code4 := []byte{0xeb, byte(-jmp2 - 2)}
copy(code[jmp1:], code3)
copy(code[jmp2:], code4)
assert.Equal(t, code[:4], append(code1, code2...))
CopyInstruction(addr, code)
err = Hook(foo_short_call, foo_short_call_replace, foo_short_call_trampoline)
assert.Nil(t, err)
fmt.Printf("fix code for foo_short_call:\n%s\n", ShowDebugInfo())
foo_short_call(22)
addr3 := addr2 + uintptr(2)
fc := runtime.FuncForPC(addr3)
assert.NotNil(t, fc)
fmt.Printf("func name get from addr beyond scope:%s\n", fc.Name())
assert.Equal(t, addr, fc.Entry())
f, l := fc.FileLine(addr2 + uintptr(3))
assert.Equal(t, 0, l)
assert.Equal(t, "?", f)
fmt.Printf("file:%s, line:%d\n", f, l)
}

237
vendor/github.com/brahma-adshonor/gohook/utility.go generated vendored Normal file
View File

@ -0,0 +1,237 @@
package gohook
import (
"fmt"
"reflect"
"unsafe"
)
func dummy(v int) string {
return fmt.Sprintf("some text:%d", v)
}
type CodeInfo struct {
How string
Origin []byte
Fix []CodeFix
TrampolineOrig []byte
}
func makeSliceFromPointer(p uintptr, length int) []byte {
return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
Data: p,
Len: length,
Cap: length,
}))
}
func GetFuncInstSize(f interface{}) uint32 {
sz := uint32(0)
ptr := reflect.ValueOf(f).Pointer()
if elfInfo != nil {
sz, _ = elfInfo.GetFuncSize(ptr)
}
if sz == 0 {
sz, _ = GetFuncSizeByGuess(GetArchMode(), ptr, true)
}
return sz
}
func CopyFunction(allowCall bool, from, to interface{}, info *CodeInfo) ([]byte, error) {
s := reflect.ValueOf(from).Pointer()
d := reflect.ValueOf(to).Pointer()
mode := GetArchMode()
sz1 := getFuncSize(mode, s, true)
sz2 := getFuncSize(mode, d, true)
return doCopyFunction(mode, allowCall, s, d, sz1, sz2, info)
}
func getFuncSize(mode int, addr uintptr, minimal bool) uint32 {
sz := uint32(0)
if elfInfo != nil {
sz, _ = elfInfo.GetFuncSize(addr)
}
var err error
if sz == 0 {
sz, err = GetFuncSizeByGuess(mode, addr, minimal)
if err != nil {
return 0
}
}
return sz
}
func doFixFuncInplace(mode int, addr, to uintptr, funcSz, to_sz int, info *CodeInfo, jumpSize int) error {
fix, err := fixFuncInstructionInplace(mode, addr, to, funcSz, to_sz, jumpSize)
for retry := 0; err != nil && retry < 8 && to_sz < funcSz/2; retry++ {
if err != errInplaceFixSizeNotEnough {
break
}
to_sz += 16
f := makeSliceFromPointer(addr, to_sz+16)
to_sz = GetInsLenGreaterThan(mode, f, to_sz)
// fmt.Printf("retry fix inplace by extending move size, move sz:%d\n", to_sz)
fix, err = fixFuncInstructionInplace(mode, addr, to, funcSz, to_sz, jumpSize)
}
if err != nil {
return err
}
size2 := 0
total_len := 0
for _, f := range fix {
total_len += len(f.Code)
if f.Foreign {
// fmt.Printf("foreign code:%x, sz:%d\n", f.Addr, len(f.Code))
size2 += len(f.Code)
}
}
//jump from trampoline back to origin func
jumpcode2 := genJumpCode(mode, false, addr+uintptr(jumpSize), to+uintptr(size2))
origin := makeSliceFromPointer(addr, int(total_len))
sf := make([]byte, total_len)
copy(sf, origin)
tramp_origin := makeSliceFromPointer(to, size2+64)
tf := make([]byte, len(tramp_origin))
copy(tf, tramp_origin)
for _, f := range fix {
CopyInstruction(f.Addr, f.Code)
}
CopyInstruction(to+uintptr(size2), jumpcode2)
info.Fix = fix
info.Origin = sf
info.TrampolineOrig = tf
return nil
}
func doCopyFunction(mode int, allowCall bool, from, to uintptr, sz1, sz2 uint32, info *CodeInfo) ([]byte, error) {
if sz1 > sz2+1 { // add trailing int3 to the end
return nil, fmt.Errorf("source addr:%x, target addr:%x, sizeof source func(%d) > sizeof of target func(%d)", from, to, sz1, sz2)
}
fix, err2 := copyFuncInstruction(mode, from, to, int(sz1), allowCall)
if err2 != nil {
return nil, err2
}
origin := makeSliceFromPointer(to, int(sz2))
sf := make([]byte, sz2)
copy(sf, origin)
curAddr := to
for _, f := range fix {
CopyInstruction(curAddr, f.Code)
f.Addr = curAddr
curAddr += uintptr(len(f.Code))
}
info.Fix = fix
return sf, nil
}
func hookFunction(mode int, rdxIndirect bool, target, replace, trampoline uintptr) (*CodeInfo, error) {
info := &CodeInfo{}
jumpcode := genJumpCode(mode, rdxIndirect, replace, target)
insLen := len(jumpcode)
if trampoline != uintptr(0) {
f := makeSliceFromPointer(target, len(jumpcode)+65)
insLen = GetInsLenGreaterThan(mode, f, len(jumpcode))
}
// target slice
ts := makeSliceFromPointer(target, insLen)
info.Origin = make([]byte, len(ts))
copy(info.Origin, ts)
info.How = "jump"
target_body_off := insLen
if trampoline != uintptr(0) {
sz := uint32(0)
if elfInfo != nil {
sz, _ = elfInfo.GetFuncSize(target)
}
fix, err := FixTargetFuncCode(mode, target, sz, trampoline, insLen)
if err != nil {
sz1 := getFuncSize(mode, target, false)
sz2 := getFuncSize(mode, trampoline, false)
if sz1 <= 0 || sz2 <= 0 {
return nil, fmt.Errorf("failed calc func size")
}
err1 := doFixFuncInplace(mode, target, trampoline, int(sz1), insLen, info, len(jumpcode))
if err1 != nil {
info.How = "copy"
origin, err2 := doCopyFunction(mode, false, target, trampoline, sz1, sz2, info)
if err2 != nil {
return nil, fmt.Errorf("both fix/fix2/copy failed, fix:%s, fix2:%s, copy:%s", err.Error(), err1.Error(), err2.Error())
}
info.TrampolineOrig = origin
} else {
info.How = "adjust"
/*
ts = makeSliceFromPointer(target, len(jumpcode)+65)
insLen = GetInsLenGreaterThan(mode, ts, len(jumpcode))
ts = makeSliceFromPointer(target, insLen)
*/
}
} else {
info.How = "fix"
for _, v := range fix {
origin := makeSliceFromPointer(v.Addr, len(v.Code))
f := make([]byte, len(v.Code))
copy(f, origin)
CopyInstruction(v.Addr, v.Code)
v.Code = f
info.Fix = append(info.Fix, v)
}
jumpcode2 := genJumpCode(mode, false, target+uintptr(target_body_off), trampoline+uintptr(insLen))
f2 := makeSliceFromPointer(trampoline, insLen+len(jumpcode2)*2)
insLen2 := GetInsLenGreaterThan(mode, f2, insLen+len(jumpcode2))
info.TrampolineOrig = make([]byte, insLen2)
ts2 := makeSliceFromPointer(trampoline, insLen2)
copy(info.TrampolineOrig, ts2)
CopyInstruction(trampoline, ts)
CopyInstruction(trampoline+uintptr(insLen), jumpcode2)
}
}
CopyInstruction(target, jumpcode)
return info, nil
}
func printInstructionFix(v CodeFix, origin []byte) {
for _, c := range v.Code {
fmt.Printf(" %x", c)
}
fmt.Printf(", origin:")
for _, c := range origin {
fmt.Printf(" %x", c)
}
fmt.Printf("\n")
}
func GetFuncAddr(f interface{}) uintptr {
fv := reflect.ValueOf(f)
return fv.Pointer()
}

View File

@ -0,0 +1,7 @@
// +build darwin
package gohook
var (
defaultFuncPrologue64 = []byte{0x65, 0x48, 0x8b, 0x0c, 0x25, 0x30, 0x00, 0x00, 0x00, 0x48}
defaultFuncPrologue32 = []byte{0x65, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x89, 0xfc, 0xff, 0xff, 0xff}
)

View File

@ -0,0 +1,8 @@
//+build linux
package gohook
var (
defaultFuncPrologue64 = []byte{0x64, 0x48, 0x8b, 0x0c, 0x25, 0xf8, 0xff, 0xff, 0xff, 0x48}
defaultFuncPrologue32 = []byte{0x65, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x89, 0xfc, 0xff, 0xff, 0xff}
)

View File

@ -0,0 +1,32 @@
// +build !windows
package gohook
import (
"syscall"
)
func getPageAddr(ptr uintptr) uintptr {
return ptr & ^(uintptr(syscall.Getpagesize() - 1))
}
func setPageWritable(addr uintptr, length int, prot int) {
pageSize := syscall.Getpagesize()
for p := getPageAddr(addr); p < addr+uintptr(length); p += uintptr(pageSize) {
page := makeSliceFromPointer(p, pageSize)
err := syscall.Mprotect(page, prot)
if err != nil {
panic(err)
}
}
}
func CopyInstruction(location uintptr, data []byte) {
f := makeSliceFromPointer(location, len(data))
setPageWritable(location, len(data), syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC)
sz := copy(f, data[:])
setPageWritable(location, len(data), syscall.PROT_READ|syscall.PROT_EXEC)
if sz != len(data) {
panic("copy instruction to target failed")
}
}

View File

@ -0,0 +1,45 @@
package gohook
import (
"syscall"
"unsafe"
)
var (
defaultFuncPrologue32 = []byte{0x65, 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x89, 0xfc, 0xff, 0xff, 0xff}
defaultFuncPrologue64 = []byte{0x65, 0x48, 0x8B, 0x0C, 0x25, 0x28, 0x00, 0x00, 0x00}
)
const PAGE_EXECUTE_READWRITE = 0x40
var procVirtualProtect = syscall.NewLazyDLL("kernel32.dll").NewProc("VirtualProtect")
func virtualProtect(lpAddress uintptr, dwSize int, flNewProtect uint32, lpflOldProtect unsafe.Pointer) error {
ret, _, _ := procVirtualProtect.Call(
lpAddress,
uintptr(dwSize),
uintptr(flNewProtect),
uintptr(lpflOldProtect))
if ret == 0 {
return syscall.GetLastError()
}
return nil
}
func CopyInstruction(location uintptr, data []byte) {
f := makeSliceFromPointer(location, len(data))
var oldPerms uint32
err := virtualProtect(location, len(data), PAGE_EXECUTE_READWRITE, unsafe.Pointer(&oldPerms))
if err != nil {
panic(err)
}
copy(f, data[:])
var tmp uint32
err = virtualProtect(location, len(data), oldPerms, unsafe.Pointer(&tmp))
if err != nil {
panic(err)
}
}

48
vendor/github.com/go-logr/logr/testing/null.go generated vendored Normal file
View File

@ -0,0 +1,48 @@
/*
Copyright 2019 The logr Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package testing
import "github.com/go-logr/logr"
// NullLogger is a logr.Logger that does nothing.
type NullLogger struct{}
var _ logr.Logger = NullLogger{}
func (_ NullLogger) Info(_ string, _ ...interface{}) {
// Do nothing.
}
func (_ NullLogger) Enabled() bool {
return false
}
func (_ NullLogger) Error(_ error, _ string, _ ...interface{}) {
// Do nothing.
}
func (log NullLogger) V(_ int) logr.Logger {
return log
}
func (log NullLogger) WithName(_ string) logr.Logger {
return log
}
func (log NullLogger) WithValues(_ ...interface{}) logr.Logger {
return log
}

55
vendor/github.com/go-logr/logr/testing/test.go generated vendored Normal file
View File

@ -0,0 +1,55 @@
/*
Copyright 2019 The logr Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package testing
import (
"testing"
"github.com/go-logr/logr"
)
// TestLogger is a logr.Logger that prints through a testing.T object.
// Only error logs will have any effect.
type TestLogger struct {
T *testing.T
}
var _ logr.Logger = TestLogger{}
func (_ TestLogger) Info(_ string, _ ...interface{}) {
// Do nothing.
}
func (_ TestLogger) Enabled() bool {
return false
}
func (log TestLogger) Error(err error, msg string, args ...interface{}) {
log.T.Logf("%s: %v -- %v", msg, err, args)
}
func (log TestLogger) V(v int) logr.Logger {
return log
}
func (log TestLogger) WithName(_ string) logr.Logger {
return log
}
func (log TestLogger) WithValues(_ ...interface{}) logr.Logger {
return log
}

27
vendor/github.com/pmezard/go-difflib/LICENSE generated vendored Normal file
View File

@ -0,0 +1,27 @@
Copyright (c) 2013, Patrick Mezard
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.
The names of its contributors may not 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
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.

772
vendor/github.com/pmezard/go-difflib/difflib/difflib.go generated vendored Normal file
View File

@ -0,0 +1,772 @@
// Package difflib is a partial port of Python difflib module.
//
// It provides tools to compare sequences of strings and generate textual diffs.
//
// The following class and functions have been ported:
//
// - SequenceMatcher
//
// - unified_diff
//
// - context_diff
//
// Getting unified diffs was the main goal of the port. Keep in mind this code
// is mostly suitable to output text differences in a human friendly way, there
// are no guarantees generated diffs are consumable by patch(1).
package difflib
import (
"bufio"
"bytes"
"fmt"
"io"
"strings"
)
func min(a, b int) int {
if a < b {
return a
}
return b
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func calculateRatio(matches, length int) float64 {
if length > 0 {
return 2.0 * float64(matches) / float64(length)
}
return 1.0
}
type Match struct {
A int
B int
Size int
}
type OpCode struct {
Tag byte
I1 int
I2 int
J1 int
J2 int
}
// SequenceMatcher compares sequence of strings. The basic
// algorithm predates, and is a little fancier than, an algorithm
// published in the late 1980's by Ratcliff and Obershelp under the
// hyperbolic name "gestalt pattern matching". The basic idea is to find
// the longest contiguous matching subsequence that contains no "junk"
// elements (R-O doesn't address junk). The same idea is then applied
// recursively to the pieces of the sequences to the left and to the right
// of the matching subsequence. This does not yield minimal edit
// sequences, but does tend to yield matches that "look right" to people.
//
// SequenceMatcher tries to compute a "human-friendly diff" between two
// sequences. Unlike e.g. UNIX(tm) diff, the fundamental notion is the
// longest *contiguous* & junk-free matching subsequence. That's what
// catches peoples' eyes. The Windows(tm) windiff has another interesting
// notion, pairing up elements that appear uniquely in each sequence.
// That, and the method here, appear to yield more intuitive difference
// reports than does diff. This method appears to be the least vulnerable
// to synching up on blocks of "junk lines", though (like blank lines in
// ordinary text files, or maybe "<P>" lines in HTML files). That may be
// because this is the only method of the 3 that has a *concept* of
// "junk" <wink>.
//
// Timing: Basic R-O is cubic time worst case and quadratic time expected
// case. SequenceMatcher is quadratic time for the worst case and has
// expected-case behavior dependent in a complicated way on how many
// elements the sequences have in common; best case time is linear.
type SequenceMatcher struct {
a []string
b []string
b2j map[string][]int
IsJunk func(string) bool
autoJunk bool
bJunk map[string]struct{}
matchingBlocks []Match
fullBCount map[string]int
bPopular map[string]struct{}
opCodes []OpCode
}
func NewMatcher(a, b []string) *SequenceMatcher {
m := SequenceMatcher{autoJunk: true}
m.SetSeqs(a, b)
return &m
}
func NewMatcherWithJunk(a, b []string, autoJunk bool,
isJunk func(string) bool) *SequenceMatcher {
m := SequenceMatcher{IsJunk: isJunk, autoJunk: autoJunk}
m.SetSeqs(a, b)
return &m
}
// Set two sequences to be compared.
func (m *SequenceMatcher) SetSeqs(a, b []string) {
m.SetSeq1(a)
m.SetSeq2(b)
}
// Set the first sequence to be compared. The second sequence to be compared is
// not changed.
//
// SequenceMatcher computes and caches detailed information about the second
// sequence, so if you want to compare one sequence S against many sequences,
// use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other
// sequences.
//
// See also SetSeqs() and SetSeq2().
func (m *SequenceMatcher) SetSeq1(a []string) {
if &a == &m.a {
return
}
m.a = a
m.matchingBlocks = nil
m.opCodes = nil
}
// Set the second sequence to be compared. The first sequence to be compared is
// not changed.
func (m *SequenceMatcher) SetSeq2(b []string) {
if &b == &m.b {
return
}
m.b = b
m.matchingBlocks = nil
m.opCodes = nil
m.fullBCount = nil
m.chainB()
}
func (m *SequenceMatcher) chainB() {
// Populate line -> index mapping
b2j := map[string][]int{}
for i, s := range m.b {
indices := b2j[s]
indices = append(indices, i)
b2j[s] = indices
}
// Purge junk elements
m.bJunk = map[string]struct{}{}
if m.IsJunk != nil {
junk := m.bJunk
for s, _ := range b2j {
if m.IsJunk(s) {
junk[s] = struct{}{}
}
}
for s, _ := range junk {
delete(b2j, s)
}
}
// Purge remaining popular elements
popular := map[string]struct{}{}
n := len(m.b)
if m.autoJunk && n >= 200 {
ntest := n/100 + 1
for s, indices := range b2j {
if len(indices) > ntest {
popular[s] = struct{}{}
}
}
for s, _ := range popular {
delete(b2j, s)
}
}
m.bPopular = popular
m.b2j = b2j
}
func (m *SequenceMatcher) isBJunk(s string) bool {
_, ok := m.bJunk[s]
return ok
}
// Find longest matching block in a[alo:ahi] and b[blo:bhi].
//
// If IsJunk is not defined:
//
// Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where
// alo <= i <= i+k <= ahi
// blo <= j <= j+k <= bhi
// and for all (i',j',k') meeting those conditions,
// k >= k'
// i <= i'
// and if i == i', j <= j'
//
// In other words, of all maximal matching blocks, return one that
// starts earliest in a, and of all those maximal matching blocks that
// start earliest in a, return the one that starts earliest in b.
//
// If IsJunk is defined, first the longest matching block is
// determined as above, but with the additional restriction that no
// junk element appears in the block. Then that block is extended as
// far as possible by matching (only) junk elements on both sides. So
// the resulting block never matches on junk except as identical junk
// happens to be adjacent to an "interesting" match.
//
// If no blocks match, return (alo, blo, 0).
func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match {
// CAUTION: stripping common prefix or suffix would be incorrect.
// E.g.,
// ab
// acab
// Longest matching block is "ab", but if common prefix is
// stripped, it's "a" (tied with "b"). UNIX(tm) diff does so
// strip, so ends up claiming that ab is changed to acab by
// inserting "ca" in the middle. That's minimal but unintuitive:
// "it's obvious" that someone inserted "ac" at the front.
// Windiff ends up at the same place as diff, but by pairing up
// the unique 'b's and then matching the first two 'a's.
besti, bestj, bestsize := alo, blo, 0
// find longest junk-free match
// during an iteration of the loop, j2len[j] = length of longest
// junk-free match ending with a[i-1] and b[j]
j2len := map[int]int{}
for i := alo; i != ahi; i++ {
// look at all instances of a[i] in b; note that because
// b2j has no junk keys, the loop is skipped if a[i] is junk
newj2len := map[int]int{}
for _, j := range m.b2j[m.a[i]] {
// a[i] matches b[j]
if j < blo {
continue
}
if j >= bhi {
break
}
k := j2len[j-1] + 1
newj2len[j] = k
if k > bestsize {
besti, bestj, bestsize = i-k+1, j-k+1, k
}
}
j2len = newj2len
}
// Extend the best by non-junk elements on each end. In particular,
// "popular" non-junk elements aren't in b2j, which greatly speeds
// the inner loop above, but also means "the best" match so far
// doesn't contain any junk *or* popular non-junk elements.
for besti > alo && bestj > blo && !m.isBJunk(m.b[bestj-1]) &&
m.a[besti-1] == m.b[bestj-1] {
besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
}
for besti+bestsize < ahi && bestj+bestsize < bhi &&
!m.isBJunk(m.b[bestj+bestsize]) &&
m.a[besti+bestsize] == m.b[bestj+bestsize] {
bestsize += 1
}
// Now that we have a wholly interesting match (albeit possibly
// empty!), we may as well suck up the matching junk on each
// side of it too. Can't think of a good reason not to, and it
// saves post-processing the (possibly considerable) expense of
// figuring out what to do with it. In the case of an empty
// interesting match, this is clearly the right thing to do,
// because no other kind of match is possible in the regions.
for besti > alo && bestj > blo && m.isBJunk(m.b[bestj-1]) &&
m.a[besti-1] == m.b[bestj-1] {
besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
}
for besti+bestsize < ahi && bestj+bestsize < bhi &&
m.isBJunk(m.b[bestj+bestsize]) &&
m.a[besti+bestsize] == m.b[bestj+bestsize] {
bestsize += 1
}
return Match{A: besti, B: bestj, Size: bestsize}
}
// Return list of triples describing matching subsequences.
//
// Each triple is of the form (i, j, n), and means that
// a[i:i+n] == b[j:j+n]. The triples are monotonically increasing in
// i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are
// adjacent triples in the list, and the second is not the last triple in the
// list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe
// adjacent equal blocks.
//
// The last triple is a dummy, (len(a), len(b), 0), and is the only
// triple with n==0.
func (m *SequenceMatcher) GetMatchingBlocks() []Match {
if m.matchingBlocks != nil {
return m.matchingBlocks
}
var matchBlocks func(alo, ahi, blo, bhi int, matched []Match) []Match
matchBlocks = func(alo, ahi, blo, bhi int, matched []Match) []Match {
match := m.findLongestMatch(alo, ahi, blo, bhi)
i, j, k := match.A, match.B, match.Size
if match.Size > 0 {
if alo < i && blo < j {
matched = matchBlocks(alo, i, blo, j, matched)
}
matched = append(matched, match)
if i+k < ahi && j+k < bhi {
matched = matchBlocks(i+k, ahi, j+k, bhi, matched)
}
}
return matched
}
matched := matchBlocks(0, len(m.a), 0, len(m.b), nil)
// It's possible that we have adjacent equal blocks in the
// matching_blocks list now.
nonAdjacent := []Match{}
i1, j1, k1 := 0, 0, 0
for _, b := range matched {
// Is this block adjacent to i1, j1, k1?
i2, j2, k2 := b.A, b.B, b.Size
if i1+k1 == i2 && j1+k1 == j2 {
// Yes, so collapse them -- this just increases the length of
// the first block by the length of the second, and the first
// block so lengthened remains the block to compare against.
k1 += k2
} else {
// Not adjacent. Remember the first block (k1==0 means it's
// the dummy we started with), and make the second block the
// new block to compare against.
if k1 > 0 {
nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
}
i1, j1, k1 = i2, j2, k2
}
}
if k1 > 0 {
nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
}
nonAdjacent = append(nonAdjacent, Match{len(m.a), len(m.b), 0})
m.matchingBlocks = nonAdjacent
return m.matchingBlocks
}
// Return list of 5-tuples describing how to turn a into b.
//
// Each tuple is of the form (tag, i1, i2, j1, j2). The first tuple
// has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the
// tuple preceding it, and likewise for j1 == the previous j2.
//
// The tags are characters, with these meanings:
//
// 'r' (replace): a[i1:i2] should be replaced by b[j1:j2]
//
// 'd' (delete): a[i1:i2] should be deleted, j1==j2 in this case.
//
// 'i' (insert): b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case.
//
// 'e' (equal): a[i1:i2] == b[j1:j2]
func (m *SequenceMatcher) GetOpCodes() []OpCode {
if m.opCodes != nil {
return m.opCodes
}
i, j := 0, 0
matching := m.GetMatchingBlocks()
opCodes := make([]OpCode, 0, len(matching))
for _, m := range matching {
// invariant: we've pumped out correct diffs to change
// a[:i] into b[:j], and the next matching block is
// a[ai:ai+size] == b[bj:bj+size]. So we need to pump
// out a diff to change a[i:ai] into b[j:bj], pump out
// the matching block, and move (i,j) beyond the match
ai, bj, size := m.A, m.B, m.Size
tag := byte(0)
if i < ai && j < bj {
tag = 'r'
} else if i < ai {
tag = 'd'
} else if j < bj {
tag = 'i'
}
if tag > 0 {
opCodes = append(opCodes, OpCode{tag, i, ai, j, bj})
}
i, j = ai+size, bj+size
// the list of matching blocks is terminated by a
// sentinel with size 0
if size > 0 {
opCodes = append(opCodes, OpCode{'e', ai, i, bj, j})
}
}
m.opCodes = opCodes
return m.opCodes
}
// Isolate change clusters by eliminating ranges with no changes.
//
// Return a generator of groups with up to n lines of context.
// Each group is in the same format as returned by GetOpCodes().
func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode {
if n < 0 {
n = 3
}
codes := m.GetOpCodes()
if len(codes) == 0 {
codes = []OpCode{OpCode{'e', 0, 1, 0, 1}}
}
// Fixup leading and trailing groups if they show no changes.
if codes[0].Tag == 'e' {
c := codes[0]
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
codes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2}
}
if codes[len(codes)-1].Tag == 'e' {
c := codes[len(codes)-1]
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
codes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)}
}
nn := n + n
groups := [][]OpCode{}
group := []OpCode{}
for _, c := range codes {
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
// End the current group and start a new one whenever
// there is a large range with no changes.
if c.Tag == 'e' && i2-i1 > nn {
group = append(group, OpCode{c.Tag, i1, min(i2, i1+n),
j1, min(j2, j1+n)})
groups = append(groups, group)
group = []OpCode{}
i1, j1 = max(i1, i2-n), max(j1, j2-n)
}
group = append(group, OpCode{c.Tag, i1, i2, j1, j2})
}
if len(group) > 0 && !(len(group) == 1 && group[0].Tag == 'e') {
groups = append(groups, group)
}
return groups
}
// Return a measure of the sequences' similarity (float in [0,1]).
//
// Where T is the total number of elements in both sequences, and
// M is the number of matches, this is 2.0*M / T.
// Note that this is 1 if the sequences are identical, and 0 if
// they have nothing in common.
//
// .Ratio() is expensive to compute if you haven't already computed
// .GetMatchingBlocks() or .GetOpCodes(), in which case you may
// want to try .QuickRatio() or .RealQuickRation() first to get an
// upper bound.
func (m *SequenceMatcher) Ratio() float64 {
matches := 0
for _, m := range m.GetMatchingBlocks() {
matches += m.Size
}
return calculateRatio(matches, len(m.a)+len(m.b))
}
// Return an upper bound on ratio() relatively quickly.
//
// This isn't defined beyond that it is an upper bound on .Ratio(), and
// is faster to compute.
func (m *SequenceMatcher) QuickRatio() float64 {
// viewing a and b as multisets, set matches to the cardinality
// of their intersection; this counts the number of matches
// without regard to order, so is clearly an upper bound
if m.fullBCount == nil {
m.fullBCount = map[string]int{}
for _, s := range m.b {
m.fullBCount[s] = m.fullBCount[s] + 1
}
}
// avail[x] is the number of times x appears in 'b' less the
// number of times we've seen it in 'a' so far ... kinda
avail := map[string]int{}
matches := 0
for _, s := range m.a {
n, ok := avail[s]
if !ok {
n = m.fullBCount[s]
}
avail[s] = n - 1
if n > 0 {
matches += 1
}
}
return calculateRatio(matches, len(m.a)+len(m.b))
}
// Return an upper bound on ratio() very quickly.
//
// This isn't defined beyond that it is an upper bound on .Ratio(), and
// is faster to compute than either .Ratio() or .QuickRatio().
func (m *SequenceMatcher) RealQuickRatio() float64 {
la, lb := len(m.a), len(m.b)
return calculateRatio(min(la, lb), la+lb)
}
// Convert range to the "ed" format
func formatRangeUnified(start, stop int) string {
// Per the diff spec at http://www.unix.org/single_unix_specification/
beginning := start + 1 // lines start numbering with one
length := stop - start
if length == 1 {
return fmt.Sprintf("%d", beginning)
}
if length == 0 {
beginning -= 1 // empty ranges begin at line just before the range
}
return fmt.Sprintf("%d,%d", beginning, length)
}
// Unified diff parameters
type UnifiedDiff struct {
A []string // First sequence lines
FromFile string // First file name
FromDate string // First file time
B []string // Second sequence lines
ToFile string // Second file name
ToDate string // Second file time
Eol string // Headers end of line, defaults to LF
Context int // Number of context lines
}
// Compare two sequences of lines; generate the delta as a unified diff.
//
// Unified diffs are a compact way of showing line changes and a few
// lines of context. The number of context lines is set by 'n' which
// defaults to three.
//
// By default, the diff control lines (those with ---, +++, or @@) are
// created with a trailing newline. This is helpful so that inputs
// created from file.readlines() result in diffs that are suitable for
// file.writelines() since both the inputs and outputs have trailing
// newlines.
//
// For inputs that do not have trailing newlines, set the lineterm
// argument to "" so that the output will be uniformly newline free.
//
// The unidiff format normally has a header for filenames and modification
// times. Any or all of these may be specified using strings for
// 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'.
// The modification times are normally expressed in the ISO 8601 format.
func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error {
buf := bufio.NewWriter(writer)
defer buf.Flush()
wf := func(format string, args ...interface{}) error {
_, err := buf.WriteString(fmt.Sprintf(format, args...))
return err
}
ws := func(s string) error {
_, err := buf.WriteString(s)
return err
}
if len(diff.Eol) == 0 {
diff.Eol = "\n"
}
started := false
m := NewMatcher(diff.A, diff.B)
for _, g := range m.GetGroupedOpCodes(diff.Context) {
if !started {
started = true
fromDate := ""
if len(diff.FromDate) > 0 {
fromDate = "\t" + diff.FromDate
}
toDate := ""
if len(diff.ToDate) > 0 {
toDate = "\t" + diff.ToDate
}
if diff.FromFile != "" || diff.ToFile != "" {
err := wf("--- %s%s%s", diff.FromFile, fromDate, diff.Eol)
if err != nil {
return err
}
err = wf("+++ %s%s%s", diff.ToFile, toDate, diff.Eol)
if err != nil {
return err
}
}
}
first, last := g[0], g[len(g)-1]
range1 := formatRangeUnified(first.I1, last.I2)
range2 := formatRangeUnified(first.J1, last.J2)
if err := wf("@@ -%s +%s @@%s", range1, range2, diff.Eol); err != nil {
return err
}
for _, c := range g {
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
if c.Tag == 'e' {
for _, line := range diff.A[i1:i2] {
if err := ws(" " + line); err != nil {
return err
}
}
continue
}
if c.Tag == 'r' || c.Tag == 'd' {
for _, line := range diff.A[i1:i2] {
if err := ws("-" + line); err != nil {
return err
}
}
}
if c.Tag == 'r' || c.Tag == 'i' {
for _, line := range diff.B[j1:j2] {
if err := ws("+" + line); err != nil {
return err
}
}
}
}
}
return nil
}
// Like WriteUnifiedDiff but returns the diff a string.
func GetUnifiedDiffString(diff UnifiedDiff) (string, error) {
w := &bytes.Buffer{}
err := WriteUnifiedDiff(w, diff)
return string(w.Bytes()), err
}
// Convert range to the "ed" format.
func formatRangeContext(start, stop int) string {
// Per the diff spec at http://www.unix.org/single_unix_specification/
beginning := start + 1 // lines start numbering with one
length := stop - start
if length == 0 {
beginning -= 1 // empty ranges begin at line just before the range
}
if length <= 1 {
return fmt.Sprintf("%d", beginning)
}
return fmt.Sprintf("%d,%d", beginning, beginning+length-1)
}
type ContextDiff UnifiedDiff
// Compare two sequences of lines; generate the delta as a context diff.
//
// Context diffs are a compact way of showing line changes and a few
// lines of context. The number of context lines is set by diff.Context
// which defaults to three.
//
// By default, the diff control lines (those with *** or ---) are
// created with a trailing newline.
//
// For inputs that do not have trailing newlines, set the diff.Eol
// argument to "" so that the output will be uniformly newline free.
//
// The context diff format normally has a header for filenames and
// modification times. Any or all of these may be specified using
// strings for diff.FromFile, diff.ToFile, diff.FromDate, diff.ToDate.
// The modification times are normally expressed in the ISO 8601 format.
// If not specified, the strings default to blanks.
func WriteContextDiff(writer io.Writer, diff ContextDiff) error {
buf := bufio.NewWriter(writer)
defer buf.Flush()
var diffErr error
wf := func(format string, args ...interface{}) {
_, err := buf.WriteString(fmt.Sprintf(format, args...))
if diffErr == nil && err != nil {
diffErr = err
}
}
ws := func(s string) {
_, err := buf.WriteString(s)
if diffErr == nil && err != nil {
diffErr = err
}
}
if len(diff.Eol) == 0 {
diff.Eol = "\n"
}
prefix := map[byte]string{
'i': "+ ",
'd': "- ",
'r': "! ",
'e': " ",
}
started := false
m := NewMatcher(diff.A, diff.B)
for _, g := range m.GetGroupedOpCodes(diff.Context) {
if !started {
started = true
fromDate := ""
if len(diff.FromDate) > 0 {
fromDate = "\t" + diff.FromDate
}
toDate := ""
if len(diff.ToDate) > 0 {
toDate = "\t" + diff.ToDate
}
if diff.FromFile != "" || diff.ToFile != "" {
wf("*** %s%s%s", diff.FromFile, fromDate, diff.Eol)
wf("--- %s%s%s", diff.ToFile, toDate, diff.Eol)
}
}
first, last := g[0], g[len(g)-1]
ws("***************" + diff.Eol)
range1 := formatRangeContext(first.I1, last.I2)
wf("*** %s ****%s", range1, diff.Eol)
for _, c := range g {
if c.Tag == 'r' || c.Tag == 'd' {
for _, cc := range g {
if cc.Tag == 'i' {
continue
}
for _, line := range diff.A[cc.I1:cc.I2] {
ws(prefix[cc.Tag] + line)
}
}
break
}
}
range2 := formatRangeContext(first.J1, last.J2)
wf("--- %s ----%s", range2, diff.Eol)
for _, c := range g {
if c.Tag == 'r' || c.Tag == 'i' {
for _, cc := range g {
if cc.Tag == 'd' {
continue
}
for _, line := range diff.B[cc.J1:cc.J2] {
ws(prefix[cc.Tag] + line)
}
}
break
}
}
}
return diffErr
}
// Like WriteContextDiff but returns the diff a string.
func GetContextDiffString(diff ContextDiff) (string, error) {
w := &bytes.Buffer{}
err := WriteContextDiff(w, diff)
return string(w.Bytes()), err
}
// Split a string on "\n" while preserving them. The output can be used
// as input for UnifiedDiff and ContextDiff structures.
func SplitLines(s string) []string {
lines := strings.SplitAfter(s, "\n")
lines[len(lines)-1] += "\n"
return lines
}

21
vendor/github.com/stretchr/testify/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2012-2018 Mat Ryer and Tyler Bunnell
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.

View File

@ -0,0 +1,566 @@
/*
* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen
* THIS FILE MUST NOT BE EDITED BY HAND
*/
package assert
import (
http "net/http"
url "net/url"
time "time"
)
// Conditionf uses a Comparison to assert a complex condition.
func Conditionf(t TestingT, comp Comparison, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Condition(t, comp, append([]interface{}{msg}, args...)...)
}
// Containsf asserts that the specified string, list(array, slice...) or map contains the
// specified substring or element.
//
// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted")
// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted")
// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted")
func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Contains(t, s, contains, append([]interface{}{msg}, args...)...)
}
// DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.
func DirExistsf(t TestingT, path string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return DirExists(t, path, append([]interface{}{msg}, args...)...)
}
// ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified
// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
// the number of appearances of each of them in both lists should match.
//
// assert.ElementsMatchf(t, [1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted")
func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return ElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...)
}
// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either
// a slice or a channel with len == 0.
//
// assert.Emptyf(t, obj, "error message %s", "formatted")
func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Empty(t, object, append([]interface{}{msg}, args...)...)
}
// Equalf asserts that two objects are equal.
//
// assert.Equalf(t, 123, 123, "error message %s", "formatted")
//
// Pointer variable equality is determined based on the equality of the
// referenced values (as opposed to the memory addresses). Function equality
// cannot be determined and will always fail.
func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Equal(t, expected, actual, append([]interface{}{msg}, args...)...)
}
// EqualErrorf asserts that a function returned an error (i.e. not `nil`)
// and that it is equal to the provided error.
//
// actualObj, err := SomeFunction()
// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted")
func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return EqualError(t, theError, errString, append([]interface{}{msg}, args...)...)
}
// EqualValuesf asserts that two objects are equal or convertable to the same types
// and equal.
//
// assert.EqualValuesf(t, uint32(123, "error message %s", "formatted"), int32(123))
func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return EqualValues(t, expected, actual, append([]interface{}{msg}, args...)...)
}
// Errorf asserts that a function returned an error (i.e. not `nil`).
//
// actualObj, err := SomeFunction()
// if assert.Errorf(t, err, "error message %s", "formatted") {
// assert.Equal(t, expectedErrorf, err)
// }
func Errorf(t TestingT, err error, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Error(t, err, append([]interface{}{msg}, args...)...)
}
// Eventuallyf asserts that given condition will be met in waitFor time,
// periodically checking target function each tick.
//
// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted")
func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Eventually(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...)
}
// Exactlyf asserts that two objects are equal in value and type.
//
// assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123))
func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Exactly(t, expected, actual, append([]interface{}{msg}, args...)...)
}
// Failf reports a failure through
func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Fail(t, failureMessage, append([]interface{}{msg}, args...)...)
}
// FailNowf fails test
func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return FailNow(t, failureMessage, append([]interface{}{msg}, args...)...)
}
// Falsef asserts that the specified value is false.
//
// assert.Falsef(t, myBool, "error message %s", "formatted")
func Falsef(t TestingT, value bool, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return False(t, value, append([]interface{}{msg}, args...)...)
}
// FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.
func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return FileExists(t, path, append([]interface{}{msg}, args...)...)
}
// Greaterf asserts that the first element is greater than the second
//
// assert.Greaterf(t, 2, 1, "error message %s", "formatted")
// assert.Greaterf(t, float64(2, "error message %s", "formatted"), float64(1))
// assert.Greaterf(t, "b", "a", "error message %s", "formatted")
func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Greater(t, e1, e2, append([]interface{}{msg}, args...)...)
}
// GreaterOrEqualf asserts that the first element is greater than or equal to the second
//
// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted")
// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted")
// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted")
// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted")
func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return GreaterOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...)
}
// HTTPBodyContainsf asserts that a specified handler returns a
// body that contains a string.
//
// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return HTTPBodyContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...)
}
// HTTPBodyNotContainsf asserts that a specified handler returns a
// body that does not contain a string.
//
// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return HTTPBodyNotContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...)
}
// HTTPErrorf asserts that a specified handler returns an error status code.
//
// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return HTTPError(t, handler, method, url, values, append([]interface{}{msg}, args...)...)
}
// HTTPRedirectf asserts that a specified handler returns a redirect status code.
//
// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return HTTPRedirect(t, handler, method, url, values, append([]interface{}{msg}, args...)...)
}
// HTTPSuccessf asserts that a specified handler returns a success status code.
//
// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted")
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return HTTPSuccess(t, handler, method, url, values, append([]interface{}{msg}, args...)...)
}
// Implementsf asserts that an object is implemented by the specified interface.
//
// assert.Implementsf(t, (*MyInterface, "error message %s", "formatted")(nil), new(MyObject))
func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Implements(t, interfaceObject, object, append([]interface{}{msg}, args...)...)
}
// InDeltaf asserts that the two numerals are within delta of each other.
//
// assert.InDeltaf(t, math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01)
func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return InDelta(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
}
// InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys.
func InDeltaMapValuesf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return InDeltaMapValues(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
}
// InDeltaSlicef is the same as InDelta, except it compares two slices.
func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return InDeltaSlice(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
}
// InEpsilonf asserts that expected and actual have a relative error less than epsilon
func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return InEpsilon(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...)
}
// InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices.
func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return InEpsilonSlice(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...)
}
// IsTypef asserts that the specified objects are of the same type.
func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return IsType(t, expectedType, object, append([]interface{}{msg}, args...)...)
}
// JSONEqf asserts that two JSON strings are equivalent.
//
// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted")
func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return JSONEq(t, expected, actual, append([]interface{}{msg}, args...)...)
}
// YAMLEqf asserts that two YAML strings are equivalent.
func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return YAMLEq(t, expected, actual, append([]interface{}{msg}, args...)...)
}
// Lenf asserts that the specified object has specific length.
// Lenf also fails if the object has a type that len() not accept.
//
// assert.Lenf(t, mySlice, 3, "error message %s", "formatted")
func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Len(t, object, length, append([]interface{}{msg}, args...)...)
}
// Lessf asserts that the first element is less than the second
//
// assert.Lessf(t, 1, 2, "error message %s", "formatted")
// assert.Lessf(t, float64(1, "error message %s", "formatted"), float64(2))
// assert.Lessf(t, "a", "b", "error message %s", "formatted")
func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Less(t, e1, e2, append([]interface{}{msg}, args...)...)
}
// LessOrEqualf asserts that the first element is less than or equal to the second
//
// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted")
// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted")
// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted")
// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted")
func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return LessOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...)
}
// Nilf asserts that the specified object is nil.
//
// assert.Nilf(t, err, "error message %s", "formatted")
func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Nil(t, object, append([]interface{}{msg}, args...)...)
}
// NoErrorf asserts that a function returned no error (i.e. `nil`).
//
// actualObj, err := SomeFunction()
// if assert.NoErrorf(t, err, "error message %s", "formatted") {
// assert.Equal(t, expectedObj, actualObj)
// }
func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NoError(t, err, append([]interface{}{msg}, args...)...)
}
// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the
// specified substring or element.
//
// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted")
// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted")
// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted")
func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotContains(t, s, contains, append([]interface{}{msg}, args...)...)
}
// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
// a slice or a channel with len == 0.
//
// if assert.NotEmptyf(t, obj, "error message %s", "formatted") {
// assert.Equal(t, "two", obj[1])
// }
func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotEmpty(t, object, append([]interface{}{msg}, args...)...)
}
// NotEqualf asserts that the specified values are NOT equal.
//
// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted")
//
// Pointer variable equality is determined based on the equality of the
// referenced values (as opposed to the memory addresses).
func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotEqual(t, expected, actual, append([]interface{}{msg}, args...)...)
}
// NotNilf asserts that the specified object is not nil.
//
// assert.NotNilf(t, err, "error message %s", "formatted")
func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotNil(t, object, append([]interface{}{msg}, args...)...)
}
// NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic.
//
// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted")
func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotPanics(t, f, append([]interface{}{msg}, args...)...)
}
// NotRegexpf asserts that a specified regexp does not match a string.
//
// assert.NotRegexpf(t, regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting")
// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted")
func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotRegexp(t, rx, str, append([]interface{}{msg}, args...)...)
}
// NotSubsetf asserts that the specified list(array, slice...) contains not all
// elements given in the specified subset(array, slice...).
//
// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted")
func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotSubset(t, list, subset, append([]interface{}{msg}, args...)...)
}
// NotZerof asserts that i is not the zero value for its type.
func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotZero(t, i, append([]interface{}{msg}, args...)...)
}
// Panicsf asserts that the code inside the specified PanicTestFunc panics.
//
// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted")
func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Panics(t, f, append([]interface{}{msg}, args...)...)
}
// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that
// the recovered panic value equals the expected panic value.
//
// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...)
}
// Regexpf asserts that a specified regexp matches a string.
//
// assert.Regexpf(t, regexp.MustCompile("start", "error message %s", "formatted"), "it's starting")
// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted")
func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Regexp(t, rx, str, append([]interface{}{msg}, args...)...)
}
// Samef asserts that two pointers reference the same object.
//
// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted")
//
// Both arguments must be pointer variables. Pointer variable sameness is
// determined based on the equality of both type and value.
func Samef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Same(t, expected, actual, append([]interface{}{msg}, args...)...)
}
// Subsetf asserts that the specified list(array, slice...) contains all
// elements given in the specified subset(array, slice...).
//
// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted")
func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Subset(t, list, subset, append([]interface{}{msg}, args...)...)
}
// Truef asserts that the specified value is true.
//
// assert.Truef(t, myBool, "error message %s", "formatted")
func Truef(t TestingT, value bool, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return True(t, value, append([]interface{}{msg}, args...)...)
}
// WithinDurationf asserts that the two times are within duration delta of each other.
//
// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted")
func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
}
// Zerof asserts that i is the zero value for its type.
func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Zero(t, i, append([]interface{}{msg}, args...)...)
}

View File

@ -0,0 +1,5 @@
{{.CommentFormat}}
func {{.DocInfo.Name}}f(t TestingT, {{.ParamsFormat}}) bool {
if h, ok := t.(tHelper); ok { h.Helper() }
return {{.DocInfo.Name}}(t, {{.ForwardedParamsFormat}})
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,5 @@
{{.CommentWithoutT "a"}}
func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) bool {
if h, ok := a.t.(tHelper); ok { h.Helper() }
return {{.DocInfo.Name}}(a.t, {{.ForwardedParams}})
}

View File

@ -0,0 +1,309 @@
package assert
import (
"fmt"
"reflect"
)
func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
switch kind {
case reflect.Int:
{
intobj1 := obj1.(int)
intobj2 := obj2.(int)
if intobj1 > intobj2 {
return -1, true
}
if intobj1 == intobj2 {
return 0, true
}
if intobj1 < intobj2 {
return 1, true
}
}
case reflect.Int8:
{
int8obj1 := obj1.(int8)
int8obj2 := obj2.(int8)
if int8obj1 > int8obj2 {
return -1, true
}
if int8obj1 == int8obj2 {
return 0, true
}
if int8obj1 < int8obj2 {
return 1, true
}
}
case reflect.Int16:
{
int16obj1 := obj1.(int16)
int16obj2 := obj2.(int16)
if int16obj1 > int16obj2 {
return -1, true
}
if int16obj1 == int16obj2 {
return 0, true
}
if int16obj1 < int16obj2 {
return 1, true
}
}
case reflect.Int32:
{
int32obj1 := obj1.(int32)
int32obj2 := obj2.(int32)
if int32obj1 > int32obj2 {
return -1, true
}
if int32obj1 == int32obj2 {
return 0, true
}
if int32obj1 < int32obj2 {
return 1, true
}
}
case reflect.Int64:
{
int64obj1 := obj1.(int64)
int64obj2 := obj2.(int64)
if int64obj1 > int64obj2 {
return -1, true
}
if int64obj1 == int64obj2 {
return 0, true
}
if int64obj1 < int64obj2 {
return 1, true
}
}
case reflect.Uint:
{
uintobj1 := obj1.(uint)
uintobj2 := obj2.(uint)
if uintobj1 > uintobj2 {
return -1, true
}
if uintobj1 == uintobj2 {
return 0, true
}
if uintobj1 < uintobj2 {
return 1, true
}
}
case reflect.Uint8:
{
uint8obj1 := obj1.(uint8)
uint8obj2 := obj2.(uint8)
if uint8obj1 > uint8obj2 {
return -1, true
}
if uint8obj1 == uint8obj2 {
return 0, true
}
if uint8obj1 < uint8obj2 {
return 1, true
}
}
case reflect.Uint16:
{
uint16obj1 := obj1.(uint16)
uint16obj2 := obj2.(uint16)
if uint16obj1 > uint16obj2 {
return -1, true
}
if uint16obj1 == uint16obj2 {
return 0, true
}
if uint16obj1 < uint16obj2 {
return 1, true
}
}
case reflect.Uint32:
{
uint32obj1 := obj1.(uint32)
uint32obj2 := obj2.(uint32)
if uint32obj1 > uint32obj2 {
return -1, true
}
if uint32obj1 == uint32obj2 {
return 0, true
}
if uint32obj1 < uint32obj2 {
return 1, true
}
}
case reflect.Uint64:
{
uint64obj1 := obj1.(uint64)
uint64obj2 := obj2.(uint64)
if uint64obj1 > uint64obj2 {
return -1, true
}
if uint64obj1 == uint64obj2 {
return 0, true
}
if uint64obj1 < uint64obj2 {
return 1, true
}
}
case reflect.Float32:
{
float32obj1 := obj1.(float32)
float32obj2 := obj2.(float32)
if float32obj1 > float32obj2 {
return -1, true
}
if float32obj1 == float32obj2 {
return 0, true
}
if float32obj1 < float32obj2 {
return 1, true
}
}
case reflect.Float64:
{
float64obj1 := obj1.(float64)
float64obj2 := obj2.(float64)
if float64obj1 > float64obj2 {
return -1, true
}
if float64obj1 == float64obj2 {
return 0, true
}
if float64obj1 < float64obj2 {
return 1, true
}
}
case reflect.String:
{
stringobj1 := obj1.(string)
stringobj2 := obj2.(string)
if stringobj1 > stringobj2 {
return -1, true
}
if stringobj1 == stringobj2 {
return 0, true
}
if stringobj1 < stringobj2 {
return 1, true
}
}
}
return 0, false
}
// Greater asserts that the first element is greater than the second
//
// assert.Greater(t, 2, 1)
// assert.Greater(t, float64(2), float64(1))
// assert.Greater(t, "b", "a")
func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
e1Kind := reflect.ValueOf(e1).Kind()
e2Kind := reflect.ValueOf(e2).Kind()
if e1Kind != e2Kind {
return Fail(t, "Elements should be the same type", msgAndArgs...)
}
res, isComparable := compare(e1, e2, e1Kind)
if !isComparable {
return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...)
}
if res != -1 {
return Fail(t, fmt.Sprintf("\"%v\" is not greater than \"%v\"", e1, e2), msgAndArgs...)
}
return true
}
// GreaterOrEqual asserts that the first element is greater than or equal to the second
//
// assert.GreaterOrEqual(t, 2, 1)
// assert.GreaterOrEqual(t, 2, 2)
// assert.GreaterOrEqual(t, "b", "a")
// assert.GreaterOrEqual(t, "b", "b")
func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
e1Kind := reflect.ValueOf(e1).Kind()
e2Kind := reflect.ValueOf(e2).Kind()
if e1Kind != e2Kind {
return Fail(t, "Elements should be the same type", msgAndArgs...)
}
res, isComparable := compare(e1, e2, e1Kind)
if !isComparable {
return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...)
}
if res != -1 && res != 0 {
return Fail(t, fmt.Sprintf("\"%v\" is not greater than or equal to \"%v\"", e1, e2), msgAndArgs...)
}
return true
}
// Less asserts that the first element is less than the second
//
// assert.Less(t, 1, 2)
// assert.Less(t, float64(1), float64(2))
// assert.Less(t, "a", "b")
func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
e1Kind := reflect.ValueOf(e1).Kind()
e2Kind := reflect.ValueOf(e2).Kind()
if e1Kind != e2Kind {
return Fail(t, "Elements should be the same type", msgAndArgs...)
}
res, isComparable := compare(e1, e2, e1Kind)
if !isComparable {
return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...)
}
if res != 1 {
return Fail(t, fmt.Sprintf("\"%v\" is not less than \"%v\"", e1, e2), msgAndArgs...)
}
return true
}
// LessOrEqual asserts that the first element is less than or equal to the second
//
// assert.LessOrEqual(t, 1, 2)
// assert.LessOrEqual(t, 2, 2)
// assert.LessOrEqual(t, "a", "b")
// assert.LessOrEqual(t, "b", "b")
func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
e1Kind := reflect.ValueOf(e1).Kind()
e2Kind := reflect.ValueOf(e2).Kind()
if e1Kind != e2Kind {
return Fail(t, "Elements should be the same type", msgAndArgs...)
}
res, isComparable := compare(e1, e2, e1Kind)
if !isComparable {
return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...)
}
if res != 1 && res != 0 {
return Fail(t, fmt.Sprintf("\"%v\" is not less than or equal to \"%v\"", e1, e2), msgAndArgs...)
}
return true
}

View File

@ -0,0 +1,118 @@
package assert
import (
"reflect"
"testing"
)
func TestCompare(t *testing.T) {
for _, currCase := range []struct {
less interface{}
greater interface{}
cType string
}{
{less: "a", greater: "b", cType: "string"},
{less: int(1), greater: int(2), cType: "int"},
{less: int8(1), greater: int8(2), cType: "int8"},
{less: int16(1), greater: int16(2), cType: "int16"},
{less: int32(1), greater: int32(2), cType: "int32"},
{less: int64(1), greater: int64(2), cType: "int64"},
{less: uint8(1), greater: uint8(2), cType: "uint8"},
{less: uint16(1), greater: uint16(2), cType: "uint16"},
{less: uint32(1), greater: uint32(2), cType: "uint32"},
{less: uint64(1), greater: uint64(2), cType: "uint64"},
{less: float32(1), greater: float32(2), cType: "float32"},
{less: float64(1), greater: float64(2), cType: "float64"},
} {
resLess, isComparable := compare(currCase.less, currCase.greater, reflect.ValueOf(currCase.less).Kind())
if !isComparable {
t.Error("object should be comparable for type " + currCase.cType)
}
if resLess != 1 {
t.Errorf("object less should be less than greater for type " + currCase.cType)
}
resGreater, isComparable := compare(currCase.greater, currCase.less, reflect.ValueOf(currCase.less).Kind())
if !isComparable {
t.Error("object are comparable for type " + currCase.cType)
}
if resGreater != -1 {
t.Errorf("object greater should be greater than less for type " + currCase.cType)
}
resEqual, isComparable := compare(currCase.less, currCase.less, reflect.ValueOf(currCase.less).Kind())
if !isComparable {
t.Error("object are comparable for type " + currCase.cType)
}
if resEqual != 0 {
t.Errorf("objects should be equal for type " + currCase.cType)
}
}
}
func TestGreater(t *testing.T) {
mockT := new(testing.T)
if !Greater(mockT, 2, 1) {
t.Error("Greater should return true")
}
if Greater(mockT, 1, 1) {
t.Error("Greater should return false")
}
if Greater(mockT, 1, 2) {
t.Error("Greater should return false")
}
}
func TestGreaterOrEqual(t *testing.T) {
mockT := new(testing.T)
if !GreaterOrEqual(mockT, 2, 1) {
t.Error("Greater should return true")
}
if !GreaterOrEqual(mockT, 1, 1) {
t.Error("Greater should return true")
}
if GreaterOrEqual(mockT, 1, 2) {
t.Error("Greater should return false")
}
}
func TestLess(t *testing.T) {
mockT := new(testing.T)
if !Less(mockT, 1, 2) {
t.Error("Less should return true")
}
if Less(mockT, 1, 1) {
t.Error("Less should return false")
}
if Less(mockT, 2, 1) {
t.Error("Less should return false")
}
}
func TestLessOrEqual(t *testing.T) {
mockT := new(testing.T)
if !LessOrEqual(mockT, 1, 2) {
t.Error("Greater should return true")
}
if !LessOrEqual(mockT, 1, 1) {
t.Error("Greater should return true")
}
if LessOrEqual(mockT, 2, 1) {
t.Error("Greater should return false")
}
}

1498
vendor/github.com/stretchr/testify/assert/assertions.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

45
vendor/github.com/stretchr/testify/assert/doc.go generated vendored Normal file
View File

@ -0,0 +1,45 @@
// Package assert provides a set of comprehensive testing tools for use with the normal Go testing system.
//
// Example Usage
//
// The following is a complete example using assert in a standard test function:
// import (
// "testing"
// "github.com/stretchr/testify/assert"
// )
//
// func TestSomething(t *testing.T) {
//
// var a string = "Hello"
// var b string = "Hello"
//
// assert.Equal(t, a, b, "The two words should be the same.")
//
// }
//
// if you assert many times, use the format below:
//
// import (
// "testing"
// "github.com/stretchr/testify/assert"
// )
//
// func TestSomething(t *testing.T) {
// assert := assert.New(t)
//
// var a string = "Hello"
// var b string = "Hello"
//
// assert.Equal(a, b, "The two words should be the same.")
// }
//
// Assertions
//
// Assertions allow you to easily write test code, and are global funcs in the `assert` package.
// All assertion functions take, as the first argument, the `*testing.T` object provided by the
// testing framework. This allows the assertion funcs to write the failings and other details to
// the correct place.
//
// Every assertion function also takes an optional string message as the final argument,
// allowing custom error messages to be appended to the message the assertion method outputs.
package assert

10
vendor/github.com/stretchr/testify/assert/errors.go generated vendored Normal file
View File

@ -0,0 +1,10 @@
package assert
import (
"errors"
)
// AnError is an error instance useful for testing. If the code does not care
// about error specifics, and only needs to return the error for example, this
// error should be used to make the test code more readable.
var AnError = errors.New("assert.AnError general error for testing")

View File

@ -0,0 +1,16 @@
package assert
// Assertions provides assertion methods around the
// TestingT interface.
type Assertions struct {
t TestingT
}
// New makes a new Assertions object for the specified TestingT.
func New(t TestingT) *Assertions {
return &Assertions{
t: t,
}
}
//go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_forward.go.tmpl -include-format-funcs

View File

@ -0,0 +1,709 @@
package assert
import (
"errors"
"regexp"
"testing"
"time"
)
func TestImplementsWrapper(t *testing.T) {
assert := New(new(testing.T))
if !assert.Implements((*AssertionTesterInterface)(nil), new(AssertionTesterConformingObject)) {
t.Error("Implements method should return true: AssertionTesterConformingObject implements AssertionTesterInterface")
}
if assert.Implements((*AssertionTesterInterface)(nil), new(AssertionTesterNonConformingObject)) {
t.Error("Implements method should return false: AssertionTesterNonConformingObject does not implements AssertionTesterInterface")
}
}
func TestIsTypeWrapper(t *testing.T) {
assert := New(new(testing.T))
if !assert.IsType(new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) {
t.Error("IsType should return true: AssertionTesterConformingObject is the same type as AssertionTesterConformingObject")
}
if assert.IsType(new(AssertionTesterConformingObject), new(AssertionTesterNonConformingObject)) {
t.Error("IsType should return false: AssertionTesterConformingObject is not the same type as AssertionTesterNonConformingObject")
}
}
func TestEqualWrapper(t *testing.T) {
assert := New(new(testing.T))
if !assert.Equal("Hello World", "Hello World") {
t.Error("Equal should return true")
}
if !assert.Equal(123, 123) {
t.Error("Equal should return true")
}
if !assert.Equal(123.5, 123.5) {
t.Error("Equal should return true")
}
if !assert.Equal([]byte("Hello World"), []byte("Hello World")) {
t.Error("Equal should return true")
}
if !assert.Equal(nil, nil) {
t.Error("Equal should return true")
}
}
func TestEqualValuesWrapper(t *testing.T) {
assert := New(new(testing.T))
if !assert.EqualValues(uint32(10), int32(10)) {
t.Error("EqualValues should return true")
}
}
func TestNotNilWrapper(t *testing.T) {
assert := New(new(testing.T))
if !assert.NotNil(new(AssertionTesterConformingObject)) {
t.Error("NotNil should return true: object is not nil")
}
if assert.NotNil(nil) {
t.Error("NotNil should return false: object is nil")
}
}
func TestNilWrapper(t *testing.T) {
assert := New(new(testing.T))
if !assert.Nil(nil) {
t.Error("Nil should return true: object is nil")
}
if assert.Nil(new(AssertionTesterConformingObject)) {
t.Error("Nil should return false: object is not nil")
}
}
func TestTrueWrapper(t *testing.T) {
assert := New(new(testing.T))
if !assert.True(true) {
t.Error("True should return true")
}
if assert.True(false) {
t.Error("True should return false")
}
}
func TestFalseWrapper(t *testing.T) {
assert := New(new(testing.T))
if !assert.False(false) {
t.Error("False should return true")
}
if assert.False(true) {
t.Error("False should return false")
}
}
func TestExactlyWrapper(t *testing.T) {
assert := New(new(testing.T))
a := float32(1)
b := float64(1)
c := float32(1)
d := float32(2)
if assert.Exactly(a, b) {
t.Error("Exactly should return false")
}
if assert.Exactly(a, d) {
t.Error("Exactly should return false")
}
if !assert.Exactly(a, c) {
t.Error("Exactly should return true")
}
if assert.Exactly(nil, a) {
t.Error("Exactly should return false")
}
if assert.Exactly(a, nil) {
t.Error("Exactly should return false")
}
}
func TestNotEqualWrapper(t *testing.T) {
assert := New(new(testing.T))
if !assert.NotEqual("Hello World", "Hello World!") {
t.Error("NotEqual should return true")
}
if !assert.NotEqual(123, 1234) {
t.Error("NotEqual should return true")
}
if !assert.NotEqual(123.5, 123.55) {
t.Error("NotEqual should return true")
}
if !assert.NotEqual([]byte("Hello World"), []byte("Hello World!")) {
t.Error("NotEqual should return true")
}
if !assert.NotEqual(nil, new(AssertionTesterConformingObject)) {
t.Error("NotEqual should return true")
}
}
func TestContainsWrapper(t *testing.T) {
assert := New(new(testing.T))
list := []string{"Foo", "Bar"}
if !assert.Contains("Hello World", "Hello") {
t.Error("Contains should return true: \"Hello World\" contains \"Hello\"")
}
if assert.Contains("Hello World", "Salut") {
t.Error("Contains should return false: \"Hello World\" does not contain \"Salut\"")
}
if !assert.Contains(list, "Foo") {
t.Error("Contains should return true: \"[\"Foo\", \"Bar\"]\" contains \"Foo\"")
}
if assert.Contains(list, "Salut") {
t.Error("Contains should return false: \"[\"Foo\", \"Bar\"]\" does not contain \"Salut\"")
}
}
func TestNotContainsWrapper(t *testing.T) {
assert := New(new(testing.T))
list := []string{"Foo", "Bar"}
if !assert.NotContains("Hello World", "Hello!") {
t.Error("NotContains should return true: \"Hello World\" does not contain \"Hello!\"")
}
if assert.NotContains("Hello World", "Hello") {
t.Error("NotContains should return false: \"Hello World\" contains \"Hello\"")
}
if !assert.NotContains(list, "Foo!") {
t.Error("NotContains should return true: \"[\"Foo\", \"Bar\"]\" does not contain \"Foo!\"")
}
if assert.NotContains(list, "Foo") {
t.Error("NotContains should return false: \"[\"Foo\", \"Bar\"]\" contains \"Foo\"")
}
}
func TestConditionWrapper(t *testing.T) {
assert := New(new(testing.T))
if !assert.Condition(func() bool { return true }, "Truth") {
t.Error("Condition should return true")
}
if assert.Condition(func() bool { return false }, "Lie") {
t.Error("Condition should return false")
}
}
func TestDidPanicWrapper(t *testing.T) {
if funcDidPanic, _ := didPanic(func() {
panic("Panic!")
}); !funcDidPanic {
t.Error("didPanic should return true")
}
if funcDidPanic, _ := didPanic(func() {
}); funcDidPanic {
t.Error("didPanic should return false")
}
}
func TestPanicsWrapper(t *testing.T) {
assert := New(new(testing.T))
if !assert.Panics(func() {
panic("Panic!")
}) {
t.Error("Panics should return true")
}
if assert.Panics(func() {
}) {
t.Error("Panics should return false")
}
}
func TestNotPanicsWrapper(t *testing.T) {
assert := New(new(testing.T))
if !assert.NotPanics(func() {
}) {
t.Error("NotPanics should return true")
}
if assert.NotPanics(func() {
panic("Panic!")
}) {
t.Error("NotPanics should return false")
}
}
func TestNoErrorWrapper(t *testing.T) {
assert := New(t)
mockAssert := New(new(testing.T))
// start with a nil error
var err error
assert.True(mockAssert.NoError(err), "NoError should return True for nil arg")
// now set an error
err = errors.New("Some error")
assert.False(mockAssert.NoError(err), "NoError with error should return False")
}
func TestErrorWrapper(t *testing.T) {
assert := New(t)
mockAssert := New(new(testing.T))
// start with a nil error
var err error
assert.False(mockAssert.Error(err), "Error should return False for nil arg")
// now set an error
err = errors.New("Some error")
assert.True(mockAssert.Error(err), "Error with error should return True")
}
func TestEqualErrorWrapper(t *testing.T) {
assert := New(t)
mockAssert := New(new(testing.T))
// start with a nil error
var err error
assert.False(mockAssert.EqualError(err, ""),
"EqualError should return false for nil arg")
// now set an error
err = errors.New("some error")
assert.False(mockAssert.EqualError(err, "Not some error"),
"EqualError should return false for different error string")
assert.True(mockAssert.EqualError(err, "some error"),
"EqualError should return true")
}
func TestEmptyWrapper(t *testing.T) {
assert := New(t)
mockAssert := New(new(testing.T))
assert.True(mockAssert.Empty(""), "Empty string is empty")
assert.True(mockAssert.Empty(nil), "Nil is empty")
assert.True(mockAssert.Empty([]string{}), "Empty string array is empty")
assert.True(mockAssert.Empty(0), "Zero int value is empty")
assert.True(mockAssert.Empty(false), "False value is empty")
assert.False(mockAssert.Empty("something"), "Non Empty string is not empty")
assert.False(mockAssert.Empty(errors.New("something")), "Non nil object is not empty")
assert.False(mockAssert.Empty([]string{"something"}), "Non empty string array is not empty")
assert.False(mockAssert.Empty(1), "Non-zero int value is not empty")
assert.False(mockAssert.Empty(true), "True value is not empty")
}
func TestNotEmptyWrapper(t *testing.T) {
assert := New(t)
mockAssert := New(new(testing.T))
assert.False(mockAssert.NotEmpty(""), "Empty string is empty")
assert.False(mockAssert.NotEmpty(nil), "Nil is empty")
assert.False(mockAssert.NotEmpty([]string{}), "Empty string array is empty")
assert.False(mockAssert.NotEmpty(0), "Zero int value is empty")
assert.False(mockAssert.NotEmpty(false), "False value is empty")
assert.True(mockAssert.NotEmpty("something"), "Non Empty string is not empty")
assert.True(mockAssert.NotEmpty(errors.New("something")), "Non nil object is not empty")
assert.True(mockAssert.NotEmpty([]string{"something"}), "Non empty string array is not empty")
assert.True(mockAssert.NotEmpty(1), "Non-zero int value is not empty")
assert.True(mockAssert.NotEmpty(true), "True value is not empty")
}
func TestLenWrapper(t *testing.T) {
assert := New(t)
mockAssert := New(new(testing.T))
assert.False(mockAssert.Len(nil, 0), "nil does not have length")
assert.False(mockAssert.Len(0, 0), "int does not have length")
assert.False(mockAssert.Len(true, 0), "true does not have length")
assert.False(mockAssert.Len(false, 0), "false does not have length")
assert.False(mockAssert.Len('A', 0), "Rune does not have length")
assert.False(mockAssert.Len(struct{}{}, 0), "Struct does not have length")
ch := make(chan int, 5)
ch <- 1
ch <- 2
ch <- 3
cases := []struct {
v interface{}
l int
}{
{[]int{1, 2, 3}, 3},
{[...]int{1, 2, 3}, 3},
{"ABC", 3},
{map[int]int{1: 2, 2: 4, 3: 6}, 3},
{ch, 3},
{[]int{}, 0},
{map[int]int{}, 0},
{make(chan int), 0},
{[]int(nil), 0},
{map[int]int(nil), 0},
{(chan int)(nil), 0},
}
for _, c := range cases {
assert.True(mockAssert.Len(c.v, c.l), "%#v have %d items", c.v, c.l)
}
}
func TestWithinDurationWrapper(t *testing.T) {
assert := New(t)
mockAssert := New(new(testing.T))
a := time.Now()
b := a.Add(10 * time.Second)
assert.True(mockAssert.WithinDuration(a, b, 10*time.Second), "A 10s difference is within a 10s time difference")
assert.True(mockAssert.WithinDuration(b, a, 10*time.Second), "A 10s difference is within a 10s time difference")
assert.False(mockAssert.WithinDuration(a, b, 9*time.Second), "A 10s difference is not within a 9s time difference")
assert.False(mockAssert.WithinDuration(b, a, 9*time.Second), "A 10s difference is not within a 9s time difference")
assert.False(mockAssert.WithinDuration(a, b, -9*time.Second), "A 10s difference is not within a 9s time difference")
assert.False(mockAssert.WithinDuration(b, a, -9*time.Second), "A 10s difference is not within a 9s time difference")
assert.False(mockAssert.WithinDuration(a, b, -11*time.Second), "A 10s difference is not within a 9s time difference")
assert.False(mockAssert.WithinDuration(b, a, -11*time.Second), "A 10s difference is not within a 9s time difference")
}
func TestInDeltaWrapper(t *testing.T) {
assert := New(new(testing.T))
True(t, assert.InDelta(1.001, 1, 0.01), "|1.001 - 1| <= 0.01")
True(t, assert.InDelta(1, 1.001, 0.01), "|1 - 1.001| <= 0.01")
True(t, assert.InDelta(1, 2, 1), "|1 - 2| <= 1")
False(t, assert.InDelta(1, 2, 0.5), "Expected |1 - 2| <= 0.5 to fail")
False(t, assert.InDelta(2, 1, 0.5), "Expected |2 - 1| <= 0.5 to fail")
False(t, assert.InDelta("", nil, 1), "Expected non numerals to fail")
cases := []struct {
a, b interface{}
delta float64
}{
{uint8(2), uint8(1), 1},
{uint16(2), uint16(1), 1},
{uint32(2), uint32(1), 1},
{uint64(2), uint64(1), 1},
{int(2), int(1), 1},
{int8(2), int8(1), 1},
{int16(2), int16(1), 1},
{int32(2), int32(1), 1},
{int64(2), int64(1), 1},
{float32(2), float32(1), 1},
{float64(2), float64(1), 1},
}
for _, tc := range cases {
True(t, assert.InDelta(tc.a, tc.b, tc.delta), "Expected |%V - %V| <= %v", tc.a, tc.b, tc.delta)
}
}
func TestInEpsilonWrapper(t *testing.T) {
assert := New(new(testing.T))
cases := []struct {
a, b interface{}
epsilon float64
}{
{uint8(2), uint16(2), .001},
{2.1, 2.2, 0.1},
{2.2, 2.1, 0.1},
{-2.1, -2.2, 0.1},
{-2.2, -2.1, 0.1},
{uint64(100), uint8(101), 0.01},
{0.1, -0.1, 2},
}
for _, tc := range cases {
True(t, assert.InEpsilon(tc.a, tc.b, tc.epsilon, "Expected %V and %V to have a relative difference of %v", tc.a, tc.b, tc.epsilon))
}
cases = []struct {
a, b interface{}
epsilon float64
}{
{uint8(2), int16(-2), .001},
{uint64(100), uint8(102), 0.01},
{2.1, 2.2, 0.001},
{2.2, 2.1, 0.001},
{2.1, -2.2, 1},
{2.1, "bla-bla", 0},
{0.1, -0.1, 1.99},
}
for _, tc := range cases {
False(t, assert.InEpsilon(tc.a, tc.b, tc.epsilon, "Expected %V and %V to have a relative difference of %v", tc.a, tc.b, tc.epsilon))
}
}
func TestRegexpWrapper(t *testing.T) {
assert := New(new(testing.T))
cases := []struct {
rx, str string
}{
{"^start", "start of the line"},
{"end$", "in the end"},
{"[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12.34"},
}
for _, tc := range cases {
True(t, assert.Regexp(tc.rx, tc.str))
True(t, assert.Regexp(regexp.MustCompile(tc.rx), tc.str))
False(t, assert.NotRegexp(tc.rx, tc.str))
False(t, assert.NotRegexp(regexp.MustCompile(tc.rx), tc.str))
}
cases = []struct {
rx, str string
}{
{"^asdfastart", "Not the start of the line"},
{"end$", "in the end."},
{"[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12a.34"},
}
for _, tc := range cases {
False(t, assert.Regexp(tc.rx, tc.str), "Expected \"%s\" to not match \"%s\"", tc.rx, tc.str)
False(t, assert.Regexp(regexp.MustCompile(tc.rx), tc.str))
True(t, assert.NotRegexp(tc.rx, tc.str))
True(t, assert.NotRegexp(regexp.MustCompile(tc.rx), tc.str))
}
}
func TestZeroWrapper(t *testing.T) {
assert := New(t)
mockAssert := New(new(testing.T))
for _, test := range zeros {
assert.True(mockAssert.Zero(test), "Zero should return true for %v", test)
}
for _, test := range nonZeros {
assert.False(mockAssert.Zero(test), "Zero should return false for %v", test)
}
}
func TestNotZeroWrapper(t *testing.T) {
assert := New(t)
mockAssert := New(new(testing.T))
for _, test := range zeros {
assert.False(mockAssert.NotZero(test), "Zero should return true for %v", test)
}
for _, test := range nonZeros {
assert.True(mockAssert.NotZero(test), "Zero should return false for %v", test)
}
}
func TestJSONEqWrapper_EqualSONString(t *testing.T) {
assert := New(new(testing.T))
if !assert.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) {
t.Error("JSONEq should return true")
}
}
func TestJSONEqWrapper_EquivalentButNotEqual(t *testing.T) {
assert := New(new(testing.T))
if !assert.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) {
t.Error("JSONEq should return true")
}
}
func TestJSONEqWrapper_HashOfArraysAndHashes(t *testing.T) {
assert := New(new(testing.T))
if !assert.JSONEq("{\r\n\t\"numeric\": 1.5,\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]],\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\"\r\n}",
"{\r\n\t\"numeric\": 1.5,\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\",\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]]\r\n}") {
t.Error("JSONEq should return true")
}
}
func TestJSONEqWrapper_Array(t *testing.T) {
assert := New(new(testing.T))
if !assert.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) {
t.Error("JSONEq should return true")
}
}
func TestJSONEqWrapper_HashAndArrayNotEquivalent(t *testing.T) {
assert := New(new(testing.T))
if assert.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) {
t.Error("JSONEq should return false")
}
}
func TestJSONEqWrapper_HashesNotEquivalent(t *testing.T) {
assert := New(new(testing.T))
if assert.JSONEq(`{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) {
t.Error("JSONEq should return false")
}
}
func TestJSONEqWrapper_ActualIsNotJSON(t *testing.T) {
assert := New(new(testing.T))
if assert.JSONEq(`{"foo": "bar"}`, "Not JSON") {
t.Error("JSONEq should return false")
}
}
func TestJSONEqWrapper_ExpectedIsNotJSON(t *testing.T) {
assert := New(new(testing.T))
if assert.JSONEq("Not JSON", `{"foo": "bar", "hello": "world"}`) {
t.Error("JSONEq should return false")
}
}
func TestJSONEqWrapper_ExpectedAndActualNotJSON(t *testing.T) {
assert := New(new(testing.T))
if assert.JSONEq("Not JSON", "Not JSON") {
t.Error("JSONEq should return false")
}
}
func TestJSONEqWrapper_ArraysOfDifferentOrder(t *testing.T) {
assert := New(new(testing.T))
if assert.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) {
t.Error("JSONEq should return false")
}
}
func TestYAMLEqWrapper_EqualYAMLString(t *testing.T) {
assert := New(new(testing.T))
if !assert.YAMLEq(`{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) {
t.Error("YAMLEq should return true")
}
}
func TestYAMLEqWrapper_EquivalentButNotEqual(t *testing.T) {
assert := New(new(testing.T))
if !assert.YAMLEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) {
t.Error("YAMLEq should return true")
}
}
func TestYAMLEqWrapper_HashOfArraysAndHashes(t *testing.T) {
assert := New(new(testing.T))
expected := `
numeric: 1.5
array:
- foo: bar
- 1
- "string"
- ["nested", "array", 5.5]
hash:
nested: hash
nested_slice: [this, is, nested]
string: "foo"
`
actual := `
numeric: 1.5
hash:
nested: hash
nested_slice: [this, is, nested]
string: "foo"
array:
- foo: bar
- 1
- "string"
- ["nested", "array", 5.5]
`
if !assert.YAMLEq(expected, actual) {
t.Error("YAMLEq should return true")
}
}
func TestYAMLEqWrapper_Array(t *testing.T) {
assert := New(new(testing.T))
if !assert.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) {
t.Error("YAMLEq should return true")
}
}
func TestYAMLEqWrapper_HashAndArrayNotEquivalent(t *testing.T) {
assert := New(new(testing.T))
if assert.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) {
t.Error("YAMLEq should return false")
}
}
func TestYAMLEqWrapper_HashesNotEquivalent(t *testing.T) {
assert := New(new(testing.T))
if assert.YAMLEq(`{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) {
t.Error("YAMLEq should return false")
}
}
func TestYAMLEqWrapper_ActualIsSimpleString(t *testing.T) {
assert := New(new(testing.T))
if assert.YAMLEq(`{"foo": "bar"}`, "Simple String") {
t.Error("YAMLEq should return false")
}
}
func TestYAMLEqWrapper_ExpectedIsSimpleString(t *testing.T) {
assert := New(new(testing.T))
if assert.YAMLEq("Simple String", `{"foo": "bar", "hello": "world"}`) {
t.Error("YAMLEq should return false")
}
}
func TestYAMLEqWrapper_ExpectedAndActualSimpleString(t *testing.T) {
assert := New(new(testing.T))
if !assert.YAMLEq("Simple String", "Simple String") {
t.Error("YAMLEq should return true")
}
}
func TestYAMLEqWrapper_ArraysOfDifferentOrder(t *testing.T) {
assert := New(new(testing.T))
if assert.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) {
t.Error("YAMLEq should return false")
}
}

View File

@ -0,0 +1,143 @@
package assert
import (
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"strings"
)
// httpCode is a helper that returns HTTP code of the response. It returns -1 and
// an error if building a new request fails.
func httpCode(handler http.HandlerFunc, method, url string, values url.Values) (int, error) {
w := httptest.NewRecorder()
req, err := http.NewRequest(method, url, nil)
if err != nil {
return -1, err
}
req.URL.RawQuery = values.Encode()
handler(w, req)
return w.Code, nil
}
// HTTPSuccess asserts that a specified handler returns a success status code.
//
// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil)
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
code, err := httpCode(handler, method, url, values)
if err != nil {
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
return false
}
isSuccessCode := code >= http.StatusOK && code <= http.StatusPartialContent
if !isSuccessCode {
Fail(t, fmt.Sprintf("Expected HTTP success status code for %q but received %d", url+"?"+values.Encode(), code))
}
return isSuccessCode
}
// HTTPRedirect asserts that a specified handler returns a redirect status code.
//
// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
code, err := httpCode(handler, method, url, values)
if err != nil {
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
return false
}
isRedirectCode := code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect
if !isRedirectCode {
Fail(t, fmt.Sprintf("Expected HTTP redirect status code for %q but received %d", url+"?"+values.Encode(), code))
}
return isRedirectCode
}
// HTTPError asserts that a specified handler returns an error status code.
//
// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
code, err := httpCode(handler, method, url, values)
if err != nil {
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
return false
}
isErrorCode := code >= http.StatusBadRequest
if !isErrorCode {
Fail(t, fmt.Sprintf("Expected HTTP error status code for %q but received %d", url+"?"+values.Encode(), code))
}
return isErrorCode
}
// HTTPBody is a helper that returns HTTP body of the response. It returns
// empty string if building a new request fails.
func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string {
w := httptest.NewRecorder()
req, err := http.NewRequest(method, url+"?"+values.Encode(), nil)
if err != nil {
return ""
}
handler(w, req)
return w.Body.String()
}
// HTTPBodyContains asserts that a specified handler returns a
// body that contains a string.
//
// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
body := HTTPBody(handler, method, url, values)
contains := strings.Contains(body, fmt.Sprint(str))
if !contains {
Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body))
}
return contains
}
// HTTPBodyNotContains asserts that a specified handler returns a
// body that does not contain a string.
//
// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
body := HTTPBody(handler, method, url, values)
contains := strings.Contains(body, fmt.Sprint(str))
if contains {
Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body))
}
return !contains
}

View File

@ -0,0 +1,146 @@
package assert
import (
"fmt"
"net/http"
"net/url"
"testing"
)
func httpOK(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}
func httpRedirect(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusTemporaryRedirect)
}
func httpError(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}
func TestHTTPSuccess(t *testing.T) {
assert := New(t)
mockT1 := new(testing.T)
assert.Equal(HTTPSuccess(mockT1, httpOK, "GET", "/", nil), true)
assert.False(mockT1.Failed())
mockT2 := new(testing.T)
assert.Equal(HTTPSuccess(mockT2, httpRedirect, "GET", "/", nil), false)
assert.True(mockT2.Failed())
mockT3 := new(testing.T)
assert.Equal(HTTPSuccess(mockT3, httpError, "GET", "/", nil), false)
assert.True(mockT3.Failed())
}
func TestHTTPRedirect(t *testing.T) {
assert := New(t)
mockT1 := new(testing.T)
assert.Equal(HTTPRedirect(mockT1, httpOK, "GET", "/", nil), false)
assert.True(mockT1.Failed())
mockT2 := new(testing.T)
assert.Equal(HTTPRedirect(mockT2, httpRedirect, "GET", "/", nil), true)
assert.False(mockT2.Failed())
mockT3 := new(testing.T)
assert.Equal(HTTPRedirect(mockT3, httpError, "GET", "/", nil), false)
assert.True(mockT3.Failed())
}
func TestHTTPError(t *testing.T) {
assert := New(t)
mockT1 := new(testing.T)
assert.Equal(HTTPError(mockT1, httpOK, "GET", "/", nil), false)
assert.True(mockT1.Failed())
mockT2 := new(testing.T)
assert.Equal(HTTPError(mockT2, httpRedirect, "GET", "/", nil), false)
assert.True(mockT2.Failed())
mockT3 := new(testing.T)
assert.Equal(HTTPError(mockT3, httpError, "GET", "/", nil), true)
assert.False(mockT3.Failed())
}
func TestHTTPStatusesWrapper(t *testing.T) {
assert := New(t)
mockAssert := New(new(testing.T))
assert.Equal(mockAssert.HTTPSuccess(httpOK, "GET", "/", nil), true)
assert.Equal(mockAssert.HTTPSuccess(httpRedirect, "GET", "/", nil), false)
assert.Equal(mockAssert.HTTPSuccess(httpError, "GET", "/", nil), false)
assert.Equal(mockAssert.HTTPRedirect(httpOK, "GET", "/", nil), false)
assert.Equal(mockAssert.HTTPRedirect(httpRedirect, "GET", "/", nil), true)
assert.Equal(mockAssert.HTTPRedirect(httpError, "GET", "/", nil), false)
assert.Equal(mockAssert.HTTPError(httpOK, "GET", "/", nil), false)
assert.Equal(mockAssert.HTTPError(httpRedirect, "GET", "/", nil), false)
assert.Equal(mockAssert.HTTPError(httpError, "GET", "/", nil), true)
}
func httpHelloName(w http.ResponseWriter, r *http.Request) {
name := r.FormValue("name")
w.Write([]byte(fmt.Sprintf("Hello, %s!", name)))
}
func TestHTTPRequestWithNoParams(t *testing.T) {
var got *http.Request
handler := func(w http.ResponseWriter, r *http.Request) {
got = r
w.WriteHeader(http.StatusOK)
}
True(t, HTTPSuccess(t, handler, "GET", "/url", nil))
Empty(t, got.URL.Query())
Equal(t, "/url", got.URL.RequestURI())
}
func TestHTTPRequestWithParams(t *testing.T) {
var got *http.Request
handler := func(w http.ResponseWriter, r *http.Request) {
got = r
w.WriteHeader(http.StatusOK)
}
params := url.Values{}
params.Add("id", "12345")
True(t, HTTPSuccess(t, handler, "GET", "/url", params))
Equal(t, url.Values{"id": []string{"12345"}}, got.URL.Query())
Equal(t, "/url?id=12345", got.URL.String())
Equal(t, "/url?id=12345", got.URL.RequestURI())
}
func TestHttpBody(t *testing.T) {
assert := New(t)
mockT := new(testing.T)
assert.True(HTTPBodyContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!"))
assert.True(HTTPBodyContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World"))
assert.False(HTTPBodyContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world"))
assert.False(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!"))
assert.False(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World"))
assert.True(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world"))
}
func TestHttpBodyWrappers(t *testing.T) {
assert := New(t)
mockAssert := New(new(testing.T))
assert.True(mockAssert.HTTPBodyContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!"))
assert.True(mockAssert.HTTPBodyContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World"))
assert.False(mockAssert.HTTPBodyContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world"))
assert.False(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!"))
assert.False(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World"))
assert.True(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world"))
}

27
vendor/golang.org/x/arch/LICENSE generated vendored Normal file
View File

@ -0,0 +1,27 @@
Copyright (c) 2015 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.

22
vendor/golang.org/x/arch/PATENTS generated vendored Normal file
View File

@ -0,0 +1,22 @@
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.

3
vendor/golang.org/x/arch/x86/x86asm/Makefile generated vendored Normal file
View File

@ -0,0 +1,3 @@
tables.go: ../x86map/map.go ../x86.csv
go run ../x86map/map.go -fmt=decoder ../x86.csv >_tables.go && gofmt _tables.go >tables.go && rm _tables.go

1724
vendor/golang.org/x/arch/x86/x86asm/decode.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

85
vendor/golang.org/x/arch/x86/x86asm/decode_test.go generated vendored Normal file
View File

@ -0,0 +1,85 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"encoding/hex"
"io/ioutil"
"strconv"
"strings"
"testing"
)
func TestDecode(t *testing.T) {
data, err := ioutil.ReadFile("testdata/decode.txt")
if err != nil {
t.Fatal(err)
}
all := string(data)
for strings.Contains(all, "\t\t") {
all = strings.Replace(all, "\t\t", "\t", -1)
}
for _, line := range strings.Split(all, "\n") {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
f := strings.SplitN(line, "\t", 4)
i := strings.Index(f[0], "|")
if i < 0 {
t.Errorf("parsing %q: missing | separator", f[0])
continue
}
if i%2 != 0 {
t.Errorf("parsing %q: misaligned | separator", f[0])
}
size := i / 2
code, err := hex.DecodeString(f[0][:i] + f[0][i+1:])
if err != nil {
t.Errorf("parsing %q: %v", f[0], err)
continue
}
mode, err := strconv.Atoi(f[1])
if err != nil {
t.Errorf("invalid mode %q in: %s", f[1], line)
continue
}
syntax, asm := f[2], f[3]
inst, err := Decode(code, mode)
var out string
if err != nil {
out = "error: " + err.Error()
} else {
switch syntax {
case "gnu":
out = GNUSyntax(inst, 0, nil)
case "intel":
out = IntelSyntax(inst, 0, nil)
case "plan9": // [sic]
out = GoSyntax(inst, 0, nil)
default:
t.Errorf("unknown syntax %q", syntax)
continue
}
}
if out != asm || inst.Len != size {
t.Errorf("Decode(%s) [%s] = %s, %d, want %s, %d", f[0], syntax, out, inst.Len, asm, size)
}
}
}
func TestDecodeDoesNotCrash(t *testing.T) {
cases := [...][]byte{
[]byte{},
[]byte{0xc5},
[]byte{0xc4},
}
for _, test := range cases {
_, err := Decode([]byte(test), 64) // the only goal is that this line does not panic
if err == nil {
t.Errorf("expected error on invalid instruction %x", test)
}
}
}

816
vendor/golang.org/x/arch/x86/x86asm/ext_test.go generated vendored Normal file
View File

@ -0,0 +1,816 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Support for testing against external disassembler program.
package x86asm
import (
"bufio"
"bytes"
"encoding/hex"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"math/rand"
"os"
"os/exec"
"regexp"
"runtime"
"strings"
"testing"
"time"
)
var (
printTests = flag.Bool("printtests", false, "print test cases that exercise new code paths")
dumpTest = flag.Bool("dump", false, "dump all encodings")
mismatch = flag.Bool("mismatch", false, "log allowed mismatches")
longTest = flag.Bool("long", false, "long test")
keep = flag.Bool("keep", false, "keep object files around")
debug = false
)
// An ExtInst represents a single decoded instruction parsed
// from an external disassembler's output.
type ExtInst struct {
addr uint32
enc [32]byte
nenc int
text string
}
func (r ExtInst) String() string {
return fmt.Sprintf("%#x: % x: %s", r.addr, r.enc, r.text)
}
// An ExtDis is a connection between an external disassembler and a test.
type ExtDis struct {
Arch int
Dec chan ExtInst
File *os.File
Size int
KeepFile bool
Cmd *exec.Cmd
}
// Run runs the given command - the external disassembler - and returns
// a buffered reader of its standard output.
func (ext *ExtDis) Run(cmd ...string) (*bufio.Reader, error) {
if *keep {
log.Printf("%s\n", strings.Join(cmd, " "))
}
ext.Cmd = exec.Command(cmd[0], cmd[1:]...)
out, err := ext.Cmd.StdoutPipe()
if err != nil {
return nil, fmt.Errorf("stdoutpipe: %v", err)
}
if err := ext.Cmd.Start(); err != nil {
return nil, fmt.Errorf("exec: %v", err)
}
b := bufio.NewReaderSize(out, 1<<20)
return b, nil
}
// Wait waits for the command started with Run to exit.
func (ext *ExtDis) Wait() error {
return ext.Cmd.Wait()
}
// testExtDis tests a set of byte sequences against an external disassembler.
// The disassembler is expected to produce the given syntax and be run
// in the given architecture mode (16, 32, or 64-bit).
// The extdis function must start the external disassembler
// and then parse its output, sending the parsed instructions on ext.Dec.
// The generate function calls its argument f once for each byte sequence
// to be tested. The generate function itself will be called twice, and it must
// make the same sequence of calls to f each time.
// When a disassembly does not match the internal decoding,
// allowedMismatch determines whether this mismatch should be
// allowed, or else considered an error.
func testExtDis(
t *testing.T,
syntax string,
arch int,
extdis func(ext *ExtDis) error,
generate func(f func([]byte)),
allowedMismatch func(text string, size int, inst *Inst, dec ExtInst) bool,
) {
decoderCover = make([]bool, len(decoder))
defer func() {
decoderCover = nil
}()
start := time.Now()
ext := &ExtDis{
Dec: make(chan ExtInst),
Arch: arch,
}
errc := make(chan error)
// First pass: write instructions to input file for external disassembler.
file, f, size, err := writeInst(generate)
if err != nil {
t.Fatal(err)
}
ext.Size = size
ext.File = f
defer func() {
f.Close()
if !*keep {
os.Remove(file)
}
}()
// Second pass: compare disassembly against our decodings.
var (
totalTests = 0
totalSkips = 0
totalErrors = 0
errors = make([]string, 0, 100) // sampled errors, at most cap
)
go func() {
errc <- extdis(ext)
}()
generate(func(enc []byte) {
dec, ok := <-ext.Dec
if !ok {
t.Errorf("decoding stream ended early")
return
}
inst, text := disasm(syntax, arch, pad(enc))
totalTests++
if *dumpTest {
fmt.Printf("%x -> %s [%d]\n", enc[:len(enc)], dec.text, dec.nenc)
}
if text != dec.text || inst.Len != dec.nenc {
suffix := ""
if allowedMismatch(text, size, &inst, dec) {
totalSkips++
if !*mismatch {
return
}
suffix += " (allowed mismatch)"
}
totalErrors++
if len(errors) >= cap(errors) {
j := rand.Intn(totalErrors)
if j >= cap(errors) {
return
}
errors = append(errors[:j], errors[j+1:]...)
}
errors = append(errors, fmt.Sprintf("decode(%x) = %q, %d, want %q, %d%s", enc, text, inst.Len, dec.text, dec.nenc, suffix))
}
})
if *mismatch {
totalErrors -= totalSkips
}
for _, b := range errors {
t.Log(b)
}
if totalErrors > 0 {
t.Fail()
}
t.Logf("%d test cases, %d expected mismatches, %d failures; %.0f cases/second", totalTests, totalSkips, totalErrors, float64(totalTests)/time.Since(start).Seconds())
if err := <-errc; err != nil {
t.Fatalf("external disassembler: %v", err)
}
}
const start = 0x8000 // start address of text
// writeInst writes the generated byte sequences to a new file
// starting at offset start. That file is intended to be the input to
// the external disassembler.
func writeInst(generate func(func([]byte))) (file string, f *os.File, size int, err error) {
f, err = ioutil.TempFile("", "x86map")
if err != nil {
return
}
file = f.Name()
f.Seek(start, io.SeekStart)
w := bufio.NewWriter(f)
defer w.Flush()
size = 0
generate(func(x []byte) {
if len(x) > 16 {
x = x[:16]
}
if debug {
fmt.Printf("%#x: %x%x\n", start+size, x, pops[len(x):])
}
w.Write(x)
w.Write(pops[len(x):])
size += len(pops)
})
return file, f, size, nil
}
// 0x5F is a single-byte pop instruction.
// We pad the bytes we want decoded with enough 0x5Fs
// that no matter what state the instruction stream is in
// after reading our bytes, the pops will get us back to
// a forced instruction boundary.
var pops = []byte{
0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
}
// pad pads the code sequence with pops.
func pad(enc []byte) []byte {
return append(enc[:len(enc):len(enc)], pops...)
}
// disasm returns the decoded instruction and text
// for the given source bytes, using the given syntax and mode.
func disasm(syntax string, mode int, src []byte) (inst Inst, text string) {
// If printTests is set, we record the coverage value
// before and after, and we write out the inputs for which
// coverage went up, in the format expected in testdata/decode.text.
// This produces a fairly small set of test cases that exercise nearly
// all the code.
var cover float64
if *printTests {
cover -= coverage()
}
inst, err := decode1(src, mode, syntax == "gnu")
if err != nil {
text = "error: " + err.Error()
} else {
switch syntax {
case "gnu":
text = GNUSyntax(inst, 0, nil)
case "intel":
text = IntelSyntax(inst, 0, nil)
case "plan9": // [sic]
text = GoSyntax(inst, 0, nil)
default:
text = "error: unknown syntax " + syntax
}
}
if *printTests {
cover += coverage()
if cover > 0 {
max := len(src)
if max > 16 && inst.Len <= 16 {
max = 16
}
fmt.Printf("%x|%x\t%d\t%s\t%s\n", src[:inst.Len], src[inst.Len:max], mode, syntax, text)
}
}
return
}
// coverage returns a floating point number denoting the
// test coverage until now. The number increases when new code paths are exercised,
// both in the Go program and in the decoder byte code.
func coverage() float64 {
/*
testing.Coverage is not in the main distribution.
The implementation, which must go in package testing, is:
// Coverage reports the current code coverage as a fraction in the range [0, 1].
func Coverage() float64 {
var n, d int64
for _, counters := range cover.Counters {
for _, c := range counters {
if c > 0 {
n++
}
d++
}
}
if d == 0 {
return 0
}
return float64(n) / float64(d)
}
*/
var f float64
// f += testing.Coverage()
f += decodeCoverage()
return f
}
func decodeCoverage() float64 {
n := 0
for _, t := range decoderCover {
if t {
n++
}
}
return float64(1+n) / float64(1+len(decoderCover))
}
// Helpers for writing disassembler output parsers.
// isPrefix reports whether text is the name of an instruction prefix.
func isPrefix(text string) bool {
return prefixByte[text] > 0
}
// prefixByte maps instruction prefix text to actual prefix byte values.
var prefixByte = map[string]byte{
"es": 0x26,
"cs": 0x2e,
"ss": 0x36,
"ds": 0x3e,
"fs": 0x64,
"gs": 0x65,
"data16": 0x66,
"addr16": 0x67,
"lock": 0xf0,
"repn": 0xf2,
"repne": 0xf2,
"rep": 0xf3,
"repe": 0xf3,
"xacquire": 0xf2,
"xrelease": 0xf3,
"bnd": 0xf2,
"addr32": 0x66,
"data32": 0x67,
}
// hasPrefix reports whether any of the space-separated words in the text s
// begins with any of the given prefixes.
func hasPrefix(s string, prefixes ...string) bool {
for _, prefix := range prefixes {
for s := s; s != ""; {
if strings.HasPrefix(s, prefix) {
return true
}
i := strings.Index(s, " ")
if i < 0 {
break
}
s = s[i+1:]
}
}
return false
}
// contains reports whether the text s contains any of the given substrings.
func contains(s string, substrings ...string) bool {
for _, sub := range substrings {
if strings.Contains(s, sub) {
return true
}
}
return false
}
// isHex reports whether b is a hexadecimal character (0-9A-Fa-f).
func isHex(b byte) bool { return b == '0' || unhex[b] > 0 }
// parseHex parses the hexadecimal byte dump in hex,
// appending the parsed bytes to raw and returning the updated slice.
// The returned bool signals whether any invalid hex was found.
// Spaces and tabs between bytes are okay but any other non-hex is not.
func parseHex(hex []byte, raw []byte) ([]byte, bool) {
hex = trimSpace(hex)
for j := 0; j < len(hex); {
for hex[j] == ' ' || hex[j] == '\t' {
j++
}
if j >= len(hex) {
break
}
if j+2 > len(hex) || !isHex(hex[j]) || !isHex(hex[j+1]) {
return nil, false
}
raw = append(raw, unhex[hex[j]]<<4|unhex[hex[j+1]])
j += 2
}
return raw, true
}
var unhex = [256]byte{
'0': 0,
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
'6': 6,
'7': 7,
'8': 8,
'9': 9,
'A': 10,
'B': 11,
'C': 12,
'D': 13,
'E': 14,
'F': 15,
'a': 10,
'b': 11,
'c': 12,
'd': 13,
'e': 14,
'f': 15,
}
// index is like bytes.Index(s, []byte(t)) but avoids the allocation.
func index(s []byte, t string) int {
i := 0
for {
j := bytes.IndexByte(s[i:], t[0])
if j < 0 {
return -1
}
i = i + j
if i+len(t) > len(s) {
return -1
}
for k := 1; k < len(t); k++ {
if s[i+k] != t[k] {
goto nomatch
}
}
return i
nomatch:
i++
}
}
// fixSpace rewrites runs of spaces, tabs, and newline characters into single spaces in s.
// If s must be rewritten, it is rewritten in place.
func fixSpace(s []byte) []byte {
s = trimSpace(s)
for i := 0; i < len(s); i++ {
if s[i] == '\t' || s[i] == '\n' || i > 0 && s[i] == ' ' && s[i-1] == ' ' {
goto Fix
}
}
return s
Fix:
b := s
w := 0
for i := 0; i < len(s); i++ {
c := s[i]
if c == '\t' || c == '\n' {
c = ' '
}
if c == ' ' && w > 0 && b[w-1] == ' ' {
continue
}
b[w] = c
w++
}
if w > 0 && b[w-1] == ' ' {
w--
}
return b[:w]
}
// trimSpace trims leading and trailing space from s, returning a subslice of s.
func trimSpace(s []byte) []byte {
j := len(s)
for j > 0 && (s[j-1] == ' ' || s[j-1] == '\t' || s[j-1] == '\n') {
j--
}
i := 0
for i < j && (s[i] == ' ' || s[i] == '\t') {
i++
}
return s[i:j]
}
// pcrel and pcrelw match instructions using relative addressing mode.
var (
pcrel = regexp.MustCompile(`^((?:.* )?(?:j[a-z]+|call|ljmp|loopn?e?w?|xbegin)q?(?:,p[nt])?) 0x([0-9a-f]+)$`)
pcrelw = regexp.MustCompile(`^((?:.* )?(?:callw|jmpw|xbeginw|ljmpw)(?:,p[nt])?) 0x([0-9a-f]+)$`)
)
// Generators.
//
// The test cases are described as functions that invoke a callback repeatedly,
// with a new input sequence each time. These helpers make writing those
// a little easier.
// hexCases generates the cases written in hexadecimal in the encoded string.
// Spaces in 'encoded' separate entire test cases, not individual bytes.
func hexCases(t *testing.T, encoded string) func(func([]byte)) {
return func(try func([]byte)) {
for _, x := range strings.Fields(encoded) {
src, err := hex.DecodeString(x)
if err != nil {
t.Errorf("parsing %q: %v", x, err)
}
try(src)
}
}
}
// testdataCases generates the test cases recorded in testdata/decode.txt.
// It only uses the inputs; it ignores the answers recorded in that file.
func testdataCases(t *testing.T) func(func([]byte)) {
var codes [][]byte
data, err := ioutil.ReadFile("testdata/decode.txt")
if err != nil {
t.Fatal(err)
}
for _, line := range strings.Split(string(data), "\n") {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
f := strings.Fields(line)[0]
i := strings.Index(f, "|")
if i < 0 {
t.Errorf("parsing %q: missing | separator", f)
continue
}
if i%2 != 0 {
t.Errorf("parsing %q: misaligned | separator", f)
}
code, err := hex.DecodeString(f[:i] + f[i+1:])
if err != nil {
t.Errorf("parsing %q: %v", f, err)
continue
}
codes = append(codes, code)
}
return func(try func([]byte)) {
for _, code := range codes {
try(code)
}
}
}
// manyPrefixes generates all possible 2⁹ combinations of nine chosen prefixes.
// The relative ordering of the prefixes within the combinations varies deterministically.
func manyPrefixes(try func([]byte)) {
var prefixBytes = []byte{0x66, 0x67, 0xF0, 0xF2, 0xF3, 0x3E, 0x36, 0x66, 0x67}
var enc []byte
for i := 0; i < 1<<uint(len(prefixBytes)); i++ {
enc = enc[:0]
for j, p := range prefixBytes {
if i&(1<<uint(j)) != 0 {
enc = append(enc, p)
}
}
if len(enc) > 0 {
k := i % len(enc)
enc[0], enc[k] = enc[k], enc[0]
}
try(enc)
}
}
// basicPrefixes geneartes 8 different possible prefix cases: no prefix
// and then one each of seven different prefix bytes.
func basicPrefixes(try func([]byte)) {
try(nil)
for _, b := range []byte{0x66, 0x67, 0xF0, 0xF2, 0xF3, 0x3E, 0x36} {
try([]byte{b})
}
}
func rexPrefixes(try func([]byte)) {
try(nil)
for _, b := range []byte{0x40, 0x48, 0x43, 0x4C} {
try([]byte{b})
}
}
// concat takes two generators and returns a generator for the
// cross product of the two, concatenating the results from each.
func concat(gen1, gen2 func(func([]byte))) func(func([]byte)) {
return func(try func([]byte)) {
gen1(func(enc1 []byte) {
gen2(func(enc2 []byte) {
try(append(enc1[:len(enc1):len(enc1)], enc2...))
})
})
}
}
// concat3 takes three generators and returns a generator for the
// cross product of the three, concatenating the results from each.
func concat3(gen1, gen2, gen3 func(func([]byte))) func(func([]byte)) {
return func(try func([]byte)) {
gen1(func(enc1 []byte) {
gen2(func(enc2 []byte) {
gen3(func(enc3 []byte) {
try(append(append(enc1[:len(enc1):len(enc1)], enc2...), enc3...))
})
})
})
}
}
// concat4 takes four generators and returns a generator for the
// cross product of the four, concatenating the results from each.
func concat4(gen1, gen2, gen3, gen4 func(func([]byte))) func(func([]byte)) {
return func(try func([]byte)) {
gen1(func(enc1 []byte) {
gen2(func(enc2 []byte) {
gen3(func(enc3 []byte) {
gen4(func(enc4 []byte) {
try(append(append(append(enc1[:len(enc1):len(enc1)], enc2...), enc3...), enc4...))
})
})
})
})
}
}
// filter generates the sequences from gen that satisfy ok.
func filter(gen func(func([]byte)), ok func([]byte) bool) func(func([]byte)) {
return func(try func([]byte)) {
gen(func(enc []byte) {
if ok(enc) {
try(enc)
}
})
}
}
// enum8bit generates all possible 1-byte sequences, followed by distinctive padding.
func enum8bit(try func([]byte)) {
for i := 0; i < 1<<8; i++ {
try([]byte{byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})
}
}
// enum8bit generates all possible 2-byte sequences, followed by distinctive padding.
func enum16bit(try func([]byte)) {
for i := 0; i < 1<<16; i++ {
try([]byte{byte(i), byte(i >> 8), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})
}
}
// enum24bit generates all possible 3-byte sequences, followed by distinctive padding.
func enum24bit(try func([]byte)) {
for i := 0; i < 1<<24; i++ {
try([]byte{byte(i), byte(i >> 8), byte(i >> 16), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88})
}
}
// enumModRM generates all possible modrm bytes and, for modrm values that indicate
// a following sib byte, all possible modrm, sib combinations.
func enumModRM(try func([]byte)) {
for i := 0; i < 256; i++ {
if (i>>3)&07 == 04 && i>>6 != 3 { // has sib
for j := 0; j < 256; j++ {
try([]byte{0, byte(i), byte(j), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // byte encodings
try([]byte{1, byte(i), byte(j), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // word encodings
}
} else {
try([]byte{0, byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // byte encodings
try([]byte{1, byte(i), 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}) // word encodings
}
}
}
// fixed generates the single case b.
// It's mainly useful to prepare an argument for concat or concat3.
func fixed(b ...byte) func(func([]byte)) {
return func(try func([]byte)) {
try(b)
}
}
// testBasic runs the given test function with cases all using opcode as the initial opcode bytes.
// It runs three phases:
//
// First, zero-or-one prefixes followed by opcode followed by all possible 1-byte values.
// If in -short mode, that's all.
//
// Second, zero-or-one prefixes followed by opcode followed by all possible 2-byte values.
// If not in -long mode, that's all. This phase and the next run in parallel with other tests
// (using t.Parallel).
//
// Finally, opcode followed by all possible 3-byte values. The test can take a very long time
// and prints progress messages to package log.
func testBasic(t *testing.T, testfn func(*testing.T, func(func([]byte))), opcode ...byte) {
testfn(t, concat3(basicPrefixes, fixed(opcode...), enum8bit))
if testing.Short() {
return
}
t.Parallel()
testfn(t, concat3(basicPrefixes, fixed(opcode...), enum16bit))
if !*longTest {
return
}
name := caller(2)
op1 := make([]byte, len(opcode)+1)
copy(op1, opcode)
for i := 0; i < 256; i++ {
log.Printf("%s 24-bit: %d/256\n", name, i)
op1[len(opcode)] = byte(i)
testfn(t, concat(fixed(op1...), enum16bit))
}
}
func testBasicREX(t *testing.T, testfn func(*testing.T, func(func([]byte))), opcode ...byte) {
testfn(t, filter(concat4(basicPrefixes, rexPrefixes, fixed(opcode...), enum8bit), isValidREX))
if testing.Short() {
return
}
t.Parallel()
testfn(t, filter(concat4(basicPrefixes, rexPrefixes, fixed(opcode...), enum16bit), isValidREX))
if !*longTest {
return
}
name := caller(2)
op1 := make([]byte, len(opcode)+1)
copy(op1, opcode)
for i := 0; i < 256; i++ {
log.Printf("%s 24-bit: %d/256\n", name, i)
op1[len(opcode)] = byte(i)
testfn(t, filter(concat3(rexPrefixes, fixed(op1...), enum16bit), isValidREX))
}
}
// testPrefix runs the given test function for all many prefix possibilities
// followed by all possible 1-byte sequences.
//
// If in -long mode, it then runs a test of all the prefix possibilities followed
// by all possible 2-byte sequences.
func testPrefix(t *testing.T, testfn func(*testing.T, func(func([]byte)))) {
t.Parallel()
testfn(t, concat(manyPrefixes, enum8bit))
if testing.Short() || !*longTest {
return
}
name := caller(2)
for i := 0; i < 256; i++ {
log.Printf("%s 16-bit: %d/256\n", name, i)
testfn(t, concat3(manyPrefixes, fixed(byte(i)), enum8bit))
}
}
func testPrefixREX(t *testing.T, testfn func(*testing.T, func(func([]byte)))) {
t.Parallel()
testfn(t, filter(concat3(manyPrefixes, rexPrefixes, enum8bit), isValidREX))
if testing.Short() || !*longTest {
return
}
name := caller(2)
for i := 0; i < 256; i++ {
log.Printf("%s 16-bit: %d/256\n", name, i)
testfn(t, filter(concat4(manyPrefixes, rexPrefixes, fixed(byte(i)), enum8bit), isValidREX))
}
}
func caller(skip int) string {
pc, _, _, _ := runtime.Caller(skip)
f := runtime.FuncForPC(pc)
name := "?"
if f != nil {
name = f.Name()
if i := strings.LastIndex(name, "."); i >= 0 {
name = name[i+1:]
}
}
return name
}
func isValidREX(x []byte) bool {
i := 0
for i < len(x) && isPrefixByte(x[i]) {
i++
}
if i < len(x) && Prefix(x[i]).IsREX() {
i++
if i < len(x) {
return !isPrefixByte(x[i]) && !Prefix(x[i]).IsREX()
}
}
return true
}
func isPrefixByte(b byte) bool {
switch b {
case 0x26, 0x2E, 0x36, 0x3E, 0x64, 0x65, 0x66, 0x67, 0xF0, 0xF2, 0xF3:
return true
}
return false
}

68
vendor/golang.org/x/arch/x86/x86asm/format_test.go generated vendored Normal file
View File

@ -0,0 +1,68 @@
// 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 x86asm
import (
"encoding/hex"
"testing"
)
func testFormattingSymname(addr uint64) (string, uint64) {
switch addr {
case 0x424080:
return "runtime.printint", 0x424080
case 0x4c8068:
return "main.A", 0x4c8068
}
return "", 0
}
func TestFormatting(t *testing.T) {
testCases := []struct {
PC uint64
bytes string
goSyntax, intelSyntax, gnuSyntax string
}{
{0x4816b2, "0f8677010000",
"JBE 0x48182f",
"jbe 0x48182f",
"jbe 0x48182f"},
{0x45065b, "488b442408",
"MOVQ 0x8(SP), AX",
"mov rax, qword ptr [rsp+0x8]",
"mov 0x8(%rsp),%rax"},
{0x450678, "488b05e9790700",
"MOVQ main.A(SB), AX",
"mov rax, qword ptr [main.A]",
"mov main.A,%rax"},
{0x450664, "e8173afdff",
"CALL runtime.printint(SB)",
"call runtime.printint",
"callq runtime.printint"},
{0x45069b, "488d0575d90100",
"LEAQ 0x1d975(IP), AX",
"lea rax, ptr [rip+0x1d975]",
"lea 0x1d975(%rip),%rax"},
}
for _, testCase := range testCases {
t.Logf("%#x %s %s", testCase.PC, testCase.bytes, testCase.goSyntax)
bs, _ := hex.DecodeString(testCase.bytes)
inst, err := Decode(bs, 64)
if err != nil {
t.Errorf("decode error %v", err)
}
if out := GoSyntax(inst, testCase.PC, testFormattingSymname); out != testCase.goSyntax {
t.Errorf("GoSyntax: %q", out)
}
if out := IntelSyntax(inst, testCase.PC, testFormattingSymname); out != testCase.intelSyntax {
t.Errorf("IntelSyntax: %q expected: %q", out, testCase.intelSyntax)
}
if out := GNUSyntax(inst, testCase.PC, testFormattingSymname); out != testCase.gnuSyntax {
t.Errorf("GNUSyntax: %q expected: %q", out, testCase.gnuSyntax)
}
}
}

956
vendor/golang.org/x/arch/x86/x86asm/gnu.go generated vendored Normal file
View File

@ -0,0 +1,956 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"fmt"
"strings"
)
// GNUSyntax returns the GNU assembler syntax for the instruction, as defined by GNU binutils.
// This general form is often called ``AT&T syntax'' as a reference to AT&T System V Unix.
func GNUSyntax(inst Inst, pc uint64, symname SymLookup) string {
// Rewrite instruction to mimic GNU peculiarities.
// Note that inst has been passed by value and contains
// no pointers, so any changes we make here are local
// and will not propagate back out to the caller.
if symname == nil {
symname = func(uint64) (string, uint64) { return "", 0 }
}
// Adjust opcode [sic].
switch inst.Op {
case FDIV, FDIVR, FSUB, FSUBR, FDIVP, FDIVRP, FSUBP, FSUBRP:
// DC E0, DC F0: libopcodes swaps FSUBR/FSUB and FDIVR/FDIV, at least
// if you believe the Intel manual is correct (the encoding is irregular as given;
// libopcodes uses the more regular expected encoding).
// TODO(rsc): Test to ensure Intel manuals are correct and report to libopcodes maintainers?
// NOTE: iant thinks this is deliberate, but we can't find the history.
_, reg1 := inst.Args[0].(Reg)
_, reg2 := inst.Args[1].(Reg)
if reg1 && reg2 && (inst.Opcode>>24 == 0xDC || inst.Opcode>>24 == 0xDE) {
switch inst.Op {
case FDIV:
inst.Op = FDIVR
case FDIVR:
inst.Op = FDIV
case FSUB:
inst.Op = FSUBR
case FSUBR:
inst.Op = FSUB
case FDIVP:
inst.Op = FDIVRP
case FDIVRP:
inst.Op = FDIVP
case FSUBP:
inst.Op = FSUBRP
case FSUBRP:
inst.Op = FSUBP
}
}
case MOVNTSD:
// MOVNTSD is F2 0F 2B /r.
// MOVNTSS is F3 0F 2B /r (supposedly; not in manuals).
// Usually inner prefixes win for display,
// so that F3 F2 0F 2B 11 is REP MOVNTSD
// and F2 F3 0F 2B 11 is REPN MOVNTSS.
// Libopcodes always prefers MOVNTSS regardless of prefix order.
if countPrefix(&inst, 0xF3) > 0 {
found := false
for i := len(inst.Prefix) - 1; i >= 0; i-- {
switch inst.Prefix[i] & 0xFF {
case 0xF3:
if !found {
found = true
inst.Prefix[i] |= PrefixImplicit
}
case 0xF2:
inst.Prefix[i] &^= PrefixImplicit
}
}
inst.Op = MOVNTSS
}
}
// Add implicit arguments.
switch inst.Op {
case MONITOR:
inst.Args[0] = EDX
inst.Args[1] = ECX
inst.Args[2] = EAX
if inst.AddrSize == 16 {
inst.Args[2] = AX
}
case MWAIT:
if inst.Mode == 64 {
inst.Args[0] = RCX
inst.Args[1] = RAX
} else {
inst.Args[0] = ECX
inst.Args[1] = EAX
}
}
// Adjust which prefixes will be displayed.
// The rule is to display all the prefixes not implied by
// the usual instruction display, that is, all the prefixes
// except the ones with PrefixImplicit set.
// However, of course, there are exceptions to the rule.
switch inst.Op {
case CRC32:
// CRC32 has a mandatory F2 prefix.
// If there are multiple F2s and no F3s, the extra F2s do not print.
// (And Decode has already marked them implicit.)
// However, if there is an F3 anywhere, then the extra F2s do print.
// If there are multiple F2 prefixes *and* an (ignored) F3,
// then libopcodes prints the extra F2s as REPNs.
if countPrefix(&inst, 0xF2) > 1 {
unmarkImplicit(&inst, 0xF2)
markLastImplicit(&inst, 0xF2)
}
// An unused data size override should probably be shown,
// to distinguish DATA16 CRC32B from plain CRC32B,
// but libopcodes always treats the final override as implicit
// and the others as explicit.
unmarkImplicit(&inst, PrefixDataSize)
markLastImplicit(&inst, PrefixDataSize)
case CVTSI2SD, CVTSI2SS:
if !isMem(inst.Args[1]) {
markLastImplicit(&inst, PrefixDataSize)
}
case CVTSD2SI, CVTSS2SI, CVTTSD2SI, CVTTSS2SI,
ENTER, FLDENV, FNSAVE, FNSTENV, FRSTOR, LGDT, LIDT, LRET,
POP, PUSH, RET, SGDT, SIDT, SYSRET, XBEGIN:
markLastImplicit(&inst, PrefixDataSize)
case LOOP, LOOPE, LOOPNE, MONITOR:
markLastImplicit(&inst, PrefixAddrSize)
case MOV:
// The 16-bit and 32-bit forms of MOV Sreg, dst and MOV src, Sreg
// cannot be distinguished when src or dst refers to memory, because
// Sreg is always a 16-bit value, even when we're doing a 32-bit
// instruction. Because the instruction tables distinguished these two,
// any operand size prefix has been marked as used (to decide which
// branch to take). Unmark it, so that it will show up in disassembly,
// so that the reader can tell the size of memory operand.
// up with the same arguments
dst, _ := inst.Args[0].(Reg)
src, _ := inst.Args[1].(Reg)
if ES <= src && src <= GS && isMem(inst.Args[0]) || ES <= dst && dst <= GS && isMem(inst.Args[1]) {
unmarkImplicit(&inst, PrefixDataSize)
}
case MOVDQU:
if countPrefix(&inst, 0xF3) > 1 {
unmarkImplicit(&inst, 0xF3)
markLastImplicit(&inst, 0xF3)
}
case MOVQ2DQ:
markLastImplicit(&inst, PrefixDataSize)
case SLDT, SMSW, STR, FXRSTOR, XRSTOR, XSAVE, XSAVEOPT, CMPXCHG8B:
if isMem(inst.Args[0]) {
unmarkImplicit(&inst, PrefixDataSize)
}
case SYSEXIT:
unmarkImplicit(&inst, PrefixDataSize)
}
if isCondJmp[inst.Op] || isLoop[inst.Op] || inst.Op == JCXZ || inst.Op == JECXZ || inst.Op == JRCXZ {
if countPrefix(&inst, PrefixCS) > 0 && countPrefix(&inst, PrefixDS) > 0 {
for i, p := range inst.Prefix {
switch p & 0xFFF {
case PrefixPN, PrefixPT:
inst.Prefix[i] &= 0xF0FF // cut interpretation bits, producing original segment prefix
}
}
}
}
// XACQUIRE/XRELEASE adjustment.
if inst.Op == MOV {
// MOV into memory is a candidate for turning REP into XRELEASE.
// However, if the REP is followed by a REPN, that REPN blocks the
// conversion.
haveREPN := false
for i := len(inst.Prefix) - 1; i >= 0; i-- {
switch inst.Prefix[i] &^ PrefixIgnored {
case PrefixREPN:
haveREPN = true
case PrefixXRELEASE:
if haveREPN {
inst.Prefix[i] = PrefixREP
}
}
}
}
// We only format the final F2/F3 as XRELEASE/XACQUIRE.
haveXA := false
haveXR := false
for i := len(inst.Prefix) - 1; i >= 0; i-- {
switch inst.Prefix[i] &^ PrefixIgnored {
case PrefixXRELEASE:
if !haveXR {
haveXR = true
} else {
inst.Prefix[i] = PrefixREP
}
case PrefixXACQUIRE:
if !haveXA {
haveXA = true
} else {
inst.Prefix[i] = PrefixREPN
}
}
}
// Determine opcode.
op := strings.ToLower(inst.Op.String())
if alt := gnuOp[inst.Op]; alt != "" {
op = alt
}
// Determine opcode suffix.
// Libopcodes omits the suffix if the width of the operation
// can be inferred from a register arguments. For example,
// add $1, %ebx has no suffix because you can tell from the
// 32-bit register destination that it is a 32-bit add,
// but in addl $1, (%ebx), the destination is memory, so the
// size is not evident without the l suffix.
needSuffix := true
SuffixLoop:
for i, a := range inst.Args {
if a == nil {
break
}
switch a := a.(type) {
case Reg:
switch inst.Op {
case MOVSX, MOVZX:
continue
case SHL, SHR, RCL, RCR, ROL, ROR, SAR:
if i == 1 {
// shift count does not tell us operand size
continue
}
case CRC32:
// The source argument does tell us operand size,
// but libopcodes still always puts a suffix on crc32.
continue
case PUSH, POP:
// Even though segment registers are 16-bit, push and pop
// can save/restore them from 32-bit slots, so they
// do not imply operand size.
if ES <= a && a <= GS {
continue
}
case CVTSI2SD, CVTSI2SS:
// The integer register argument takes priority.
if X0 <= a && a <= X15 {
continue
}
}
if AL <= a && a <= R15 || ES <= a && a <= GS || X0 <= a && a <= X15 || M0 <= a && a <= M7 {
needSuffix = false
break SuffixLoop
}
}
}
if needSuffix {
switch inst.Op {
case CMPXCHG8B, FLDCW, FNSTCW, FNSTSW, LDMXCSR, LLDT, LMSW, LTR, PCLMULQDQ,
SETA, SETAE, SETB, SETBE, SETE, SETG, SETGE, SETL, SETLE, SETNE, SETNO, SETNP, SETNS, SETO, SETP, SETS,
SLDT, SMSW, STMXCSR, STR, VERR, VERW:
// For various reasons, libopcodes emits no suffix for these instructions.
case CRC32:
op += byteSizeSuffix(argBytes(&inst, inst.Args[1]))
case LGDT, LIDT, SGDT, SIDT:
op += byteSizeSuffix(inst.DataSize / 8)
case MOVZX, MOVSX:
// Integer size conversions get two suffixes.
op = op[:4] + byteSizeSuffix(argBytes(&inst, inst.Args[1])) + byteSizeSuffix(argBytes(&inst, inst.Args[0]))
case LOOP, LOOPE, LOOPNE:
// Add w suffix to indicate use of CX register instead of ECX.
if inst.AddrSize == 16 {
op += "w"
}
case CALL, ENTER, JMP, LCALL, LEAVE, LJMP, LRET, RET, SYSRET, XBEGIN:
// Add w suffix to indicate use of 16-bit target.
// Exclude JMP rel8.
if inst.Opcode>>24 == 0xEB {
break
}
if inst.DataSize == 16 && inst.Mode != 16 {
markLastImplicit(&inst, PrefixDataSize)
op += "w"
} else if inst.Mode == 64 {
op += "q"
}
case FRSTOR, FNSAVE, FNSTENV, FLDENV:
// Add s suffix to indicate shortened FPU state (I guess).
if inst.DataSize == 16 {
op += "s"
}
case PUSH, POP:
if markLastImplicit(&inst, PrefixDataSize) {
op += byteSizeSuffix(inst.DataSize / 8)
} else if inst.Mode == 64 {
op += "q"
} else {
op += byteSizeSuffix(inst.MemBytes)
}
default:
if isFloat(inst.Op) {
// I can't explain any of this, but it's what libopcodes does.
switch inst.MemBytes {
default:
if (inst.Op == FLD || inst.Op == FSTP) && isMem(inst.Args[0]) {
op += "t"
}
case 4:
if isFloatInt(inst.Op) {
op += "l"
} else {
op += "s"
}
case 8:
if isFloatInt(inst.Op) {
op += "ll"
} else {
op += "l"
}
}
break
}
op += byteSizeSuffix(inst.MemBytes)
}
}
// Adjust special case opcodes.
switch inst.Op {
case 0:
if inst.Prefix[0] != 0 {
return strings.ToLower(inst.Prefix[0].String())
}
case INT:
if inst.Opcode>>24 == 0xCC {
inst.Args[0] = nil
op = "int3"
}
case CMPPS, CMPPD, CMPSD_XMM, CMPSS:
imm, ok := inst.Args[2].(Imm)
if ok && 0 <= imm && imm < 8 {
inst.Args[2] = nil
op = cmppsOps[imm] + op[3:]
}
case PCLMULQDQ:
imm, ok := inst.Args[2].(Imm)
if ok && imm&^0x11 == 0 {
inst.Args[2] = nil
op = pclmulqOps[(imm&0x10)>>3|(imm&1)]
}
case XLATB:
if markLastImplicit(&inst, PrefixAddrSize) {
op = "xlat" // not xlatb
}
}
// Build list of argument strings.
var (
usedPrefixes bool // segment prefixes consumed by Mem formatting
args []string // formatted arguments
)
for i, a := range inst.Args {
if a == nil {
break
}
switch inst.Op {
case MOVSB, MOVSW, MOVSD, MOVSQ, OUTSB, OUTSW, OUTSD:
if i == 0 {
usedPrefixes = true // disable use of prefixes for first argument
} else {
usedPrefixes = false
}
}
if a == Imm(1) && (inst.Opcode>>24)&^1 == 0xD0 {
continue
}
args = append(args, gnuArg(&inst, pc, symname, a, &usedPrefixes))
}
// The default is to print the arguments in reverse Intel order.
// A few instructions inhibit this behavior.
switch inst.Op {
case BOUND, LCALL, ENTER, LJMP:
// no reverse
default:
// reverse args
for i, j := 0, len(args)-1; i < j; i, j = i+1, j-1 {
args[i], args[j] = args[j], args[i]
}
}
// Build prefix string.
// Must be after argument formatting, which can turn off segment prefixes.
var (
prefix = "" // output string
numAddr = 0
numData = 0
implicitData = false
)
for _, p := range inst.Prefix {
if p&0xFF == PrefixDataSize && p&PrefixImplicit != 0 {
implicitData = true
}
}
for _, p := range inst.Prefix {
if p == 0 || p.IsVEX() {
break
}
if p&PrefixImplicit != 0 {
continue
}
switch p &^ (PrefixIgnored | PrefixInvalid) {
default:
if p.IsREX() {
if p&0xFF == PrefixREX {
prefix += "rex "
} else {
prefix += "rex." + p.String()[4:] + " "
}
break
}
prefix += strings.ToLower(p.String()) + " "
case PrefixPN:
op += ",pn"
continue
case PrefixPT:
op += ",pt"
continue
case PrefixAddrSize, PrefixAddr16, PrefixAddr32:
// For unknown reasons, if the addr16 prefix is repeated,
// libopcodes displays all but the last as addr32, even though
// the addressing form used in a memory reference is clearly
// still 16-bit.
n := 32
if inst.Mode == 32 {
n = 16
}
numAddr++
if countPrefix(&inst, PrefixAddrSize) > numAddr {
n = inst.Mode
}
prefix += fmt.Sprintf("addr%d ", n)
continue
case PrefixData16, PrefixData32:
if implicitData && countPrefix(&inst, PrefixDataSize) > 1 {
// Similar to the addr32 logic above, but it only kicks in
// when something used the data size prefix (one is implicit).
n := 16
if inst.Mode == 16 {
n = 32
}
numData++
if countPrefix(&inst, PrefixDataSize) > numData {
if inst.Mode == 16 {
n = 16
} else {
n = 32
}
}
prefix += fmt.Sprintf("data%d ", n)
continue
}
prefix += strings.ToLower(p.String()) + " "
}
}
// Finally! Put it all together.
text := prefix + op
if args != nil {
text += " "
// Indirect call/jmp gets a star to distinguish from direct jump address.
if (inst.Op == CALL || inst.Op == JMP || inst.Op == LJMP || inst.Op == LCALL) && (isMem(inst.Args[0]) || isReg(inst.Args[0])) {
text += "*"
}
text += strings.Join(args, ",")
}
return text
}
// gnuArg returns the GNU syntax for the argument x from the instruction inst.
// If *usedPrefixes is false and x is a Mem, then the formatting
// includes any segment prefixes and sets *usedPrefixes to true.
func gnuArg(inst *Inst, pc uint64, symname SymLookup, x Arg, usedPrefixes *bool) string {
if x == nil {
return "<nil>"
}
switch x := x.(type) {
case Reg:
switch inst.Op {
case CVTSI2SS, CVTSI2SD, CVTSS2SI, CVTSD2SI, CVTTSD2SI, CVTTSS2SI:
if inst.DataSize == 16 && EAX <= x && x <= R15L {
x -= EAX - AX
}
case IN, INSB, INSW, INSD, OUT, OUTSB, OUTSW, OUTSD:
// DX is the port, but libopcodes prints it as if it were a memory reference.
if x == DX {
return "(%dx)"
}
case VMOVDQA, VMOVDQU, VMOVNTDQA, VMOVNTDQ:
return strings.Replace(gccRegName[x], "xmm", "ymm", -1)
}
return gccRegName[x]
case Mem:
if s, disp := memArgToSymbol(x, pc, inst.Len, symname); s != "" {
suffix := ""
if disp != 0 {
suffix = fmt.Sprintf("%+d", disp)
}
return fmt.Sprintf("%s%s", s, suffix)
}
seg := ""
var haveCS, haveDS, haveES, haveFS, haveGS, haveSS bool
switch x.Segment {
case CS:
haveCS = true
case DS:
haveDS = true
case ES:
haveES = true
case FS:
haveFS = true
case GS:
haveGS = true
case SS:
haveSS = true
}
switch inst.Op {
case INSB, INSW, INSD, STOSB, STOSW, STOSD, STOSQ, SCASB, SCASW, SCASD, SCASQ:
// These do not accept segment prefixes, at least in the GNU rendering.
default:
if *usedPrefixes {
break
}
for i := len(inst.Prefix) - 1; i >= 0; i-- {
p := inst.Prefix[i] &^ PrefixIgnored
if p == 0 {
continue
}
switch p {
case PrefixCS:
if !haveCS {
haveCS = true
inst.Prefix[i] |= PrefixImplicit
}
case PrefixDS:
if !haveDS {
haveDS = true
inst.Prefix[i] |= PrefixImplicit
}
case PrefixES:
if !haveES {
haveES = true
inst.Prefix[i] |= PrefixImplicit
}
case PrefixFS:
if !haveFS {
haveFS = true
inst.Prefix[i] |= PrefixImplicit
}
case PrefixGS:
if !haveGS {
haveGS = true
inst.Prefix[i] |= PrefixImplicit
}
case PrefixSS:
if !haveSS {
haveSS = true
inst.Prefix[i] |= PrefixImplicit
}
}
}
*usedPrefixes = true
}
if haveCS {
seg += "%cs:"
}
if haveDS {
seg += "%ds:"
}
if haveSS {
seg += "%ss:"
}
if haveES {
seg += "%es:"
}
if haveFS {
seg += "%fs:"
}
if haveGS {
seg += "%gs:"
}
disp := ""
if x.Disp != 0 {
disp = fmt.Sprintf("%#x", x.Disp)
}
if x.Scale == 0 || x.Index == 0 && x.Scale == 1 && (x.Base == ESP || x.Base == RSP || x.Base == 0 && inst.Mode == 64) {
if x.Base == 0 {
return seg + disp
}
return fmt.Sprintf("%s%s(%s)", seg, disp, gccRegName[x.Base])
}
base := gccRegName[x.Base]
if x.Base == 0 {
base = ""
}
index := gccRegName[x.Index]
if x.Index == 0 {
if inst.AddrSize == 64 {
index = "%riz"
} else {
index = "%eiz"
}
}
if AX <= x.Base && x.Base <= DI {
// 16-bit addressing - no scale
return fmt.Sprintf("%s%s(%s,%s)", seg, disp, base, index)
}
return fmt.Sprintf("%s%s(%s,%s,%d)", seg, disp, base, index, x.Scale)
case Rel:
if pc == 0 {
return fmt.Sprintf(".%+#x", int64(x))
} else {
addr := pc + uint64(inst.Len) + uint64(x)
if s, base := symname(addr); s != "" && addr == base {
return fmt.Sprintf("%s", s)
} else {
addr := pc + uint64(inst.Len) + uint64(x)
return fmt.Sprintf("%#x", addr)
}
}
case Imm:
if s, base := symname(uint64(x)); s != "" {
suffix := ""
if uint64(x) != base {
suffix = fmt.Sprintf("%+d", uint64(x)-base)
}
return fmt.Sprintf("$%s%s", s, suffix)
}
if inst.Mode == 32 {
return fmt.Sprintf("$%#x", uint32(x))
}
return fmt.Sprintf("$%#x", int64(x))
}
return x.String()
}
var gccRegName = [...]string{
0: "REG0",
AL: "%al",
CL: "%cl",
BL: "%bl",
DL: "%dl",
AH: "%ah",
CH: "%ch",
BH: "%bh",
DH: "%dh",
SPB: "%spl",
BPB: "%bpl",
SIB: "%sil",
DIB: "%dil",
R8B: "%r8b",
R9B: "%r9b",
R10B: "%r10b",
R11B: "%r11b",
R12B: "%r12b",
R13B: "%r13b",
R14B: "%r14b",
R15B: "%r15b",
AX: "%ax",
CX: "%cx",
BX: "%bx",
DX: "%dx",
SP: "%sp",
BP: "%bp",
SI: "%si",
DI: "%di",
R8W: "%r8w",
R9W: "%r9w",
R10W: "%r10w",
R11W: "%r11w",
R12W: "%r12w",
R13W: "%r13w",
R14W: "%r14w",
R15W: "%r15w",
EAX: "%eax",
ECX: "%ecx",
EDX: "%edx",
EBX: "%ebx",
ESP: "%esp",
EBP: "%ebp",
ESI: "%esi",
EDI: "%edi",
R8L: "%r8d",
R9L: "%r9d",
R10L: "%r10d",
R11L: "%r11d",
R12L: "%r12d",
R13L: "%r13d",
R14L: "%r14d",
R15L: "%r15d",
RAX: "%rax",
RCX: "%rcx",
RDX: "%rdx",
RBX: "%rbx",
RSP: "%rsp",
RBP: "%rbp",
RSI: "%rsi",
RDI: "%rdi",
R8: "%r8",
R9: "%r9",
R10: "%r10",
R11: "%r11",
R12: "%r12",
R13: "%r13",
R14: "%r14",
R15: "%r15",
IP: "%ip",
EIP: "%eip",
RIP: "%rip",
F0: "%st",
F1: "%st(1)",
F2: "%st(2)",
F3: "%st(3)",
F4: "%st(4)",
F5: "%st(5)",
F6: "%st(6)",
F7: "%st(7)",
M0: "%mm0",
M1: "%mm1",
M2: "%mm2",
M3: "%mm3",
M4: "%mm4",
M5: "%mm5",
M6: "%mm6",
M7: "%mm7",
X0: "%xmm0",
X1: "%xmm1",
X2: "%xmm2",
X3: "%xmm3",
X4: "%xmm4",
X5: "%xmm5",
X6: "%xmm6",
X7: "%xmm7",
X8: "%xmm8",
X9: "%xmm9",
X10: "%xmm10",
X11: "%xmm11",
X12: "%xmm12",
X13: "%xmm13",
X14: "%xmm14",
X15: "%xmm15",
CS: "%cs",
SS: "%ss",
DS: "%ds",
ES: "%es",
FS: "%fs",
GS: "%gs",
GDTR: "%gdtr",
IDTR: "%idtr",
LDTR: "%ldtr",
MSW: "%msw",
TASK: "%task",
CR0: "%cr0",
CR1: "%cr1",
CR2: "%cr2",
CR3: "%cr3",
CR4: "%cr4",
CR5: "%cr5",
CR6: "%cr6",
CR7: "%cr7",
CR8: "%cr8",
CR9: "%cr9",
CR10: "%cr10",
CR11: "%cr11",
CR12: "%cr12",
CR13: "%cr13",
CR14: "%cr14",
CR15: "%cr15",
DR0: "%db0",
DR1: "%db1",
DR2: "%db2",
DR3: "%db3",
DR4: "%db4",
DR5: "%db5",
DR6: "%db6",
DR7: "%db7",
TR0: "%tr0",
TR1: "%tr1",
TR2: "%tr2",
TR3: "%tr3",
TR4: "%tr4",
TR5: "%tr5",
TR6: "%tr6",
TR7: "%tr7",
}
var gnuOp = map[Op]string{
CBW: "cbtw",
CDQ: "cltd",
CMPSD: "cmpsl",
CMPSD_XMM: "cmpsd",
CWD: "cwtd",
CWDE: "cwtl",
CQO: "cqto",
INSD: "insl",
IRET: "iretw",
IRETD: "iret",
IRETQ: "iretq",
LODSB: "lods",
LODSD: "lods",
LODSQ: "lods",
LODSW: "lods",
MOVSD: "movsl",
MOVSD_XMM: "movsd",
OUTSD: "outsl",
POPA: "popaw",
POPAD: "popa",
POPF: "popfw",
POPFD: "popf",
PUSHA: "pushaw",
PUSHAD: "pusha",
PUSHF: "pushfw",
PUSHFD: "pushf",
SCASB: "scas",
SCASD: "scas",
SCASQ: "scas",
SCASW: "scas",
STOSB: "stos",
STOSD: "stos",
STOSQ: "stos",
STOSW: "stos",
XLATB: "xlat",
}
var cmppsOps = []string{
"cmpeq",
"cmplt",
"cmple",
"cmpunord",
"cmpneq",
"cmpnlt",
"cmpnle",
"cmpord",
}
var pclmulqOps = []string{
"pclmullqlqdq",
"pclmulhqlqdq",
"pclmullqhqdq",
"pclmulhqhqdq",
}
func countPrefix(inst *Inst, target Prefix) int {
n := 0
for _, p := range inst.Prefix {
if p&0xFF == target&0xFF {
n++
}
}
return n
}
func markLastImplicit(inst *Inst, prefix Prefix) bool {
for i := len(inst.Prefix) - 1; i >= 0; i-- {
p := inst.Prefix[i]
if p&0xFF == prefix {
inst.Prefix[i] |= PrefixImplicit
return true
}
}
return false
}
func unmarkImplicit(inst *Inst, prefix Prefix) {
for i := len(inst.Prefix) - 1; i >= 0; i-- {
p := inst.Prefix[i]
if p&0xFF == prefix {
inst.Prefix[i] &^= PrefixImplicit
}
}
}
func byteSizeSuffix(b int) string {
switch b {
case 1:
return "b"
case 2:
return "w"
case 4:
return "l"
case 8:
return "q"
}
return ""
}
func argBytes(inst *Inst, arg Arg) int {
if isMem(arg) {
return inst.MemBytes
}
return regBytes(arg)
}
func isFloat(op Op) bool {
switch op {
case FADD, FCOM, FCOMP, FDIV, FDIVR, FIADD, FICOM, FICOMP, FIDIV, FIDIVR, FILD, FIMUL, FIST, FISTP, FISTTP, FISUB, FISUBR, FLD, FMUL, FST, FSTP, FSUB, FSUBR:
return true
}
return false
}
func isFloatInt(op Op) bool {
switch op {
case FIADD, FICOM, FICOMP, FIDIV, FIDIVR, FILD, FIMUL, FIST, FISTP, FISTTP, FISUB, FISUBR:
return true
}
return false
}

649
vendor/golang.org/x/arch/x86/x86asm/inst.go generated vendored Normal file
View File

@ -0,0 +1,649 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package x86asm implements decoding of x86 machine code.
package x86asm
import (
"bytes"
"fmt"
)
// An Inst is a single instruction.
type Inst struct {
Prefix Prefixes // Prefixes applied to the instruction.
Op Op // Opcode mnemonic
Opcode uint32 // Encoded opcode bits, left aligned (first byte is Opcode>>24, etc)
Args Args // Instruction arguments, in Intel order
Mode int // processor mode in bits: 16, 32, or 64
AddrSize int // address size in bits: 16, 32, or 64
DataSize int // operand size in bits: 16, 32, or 64
MemBytes int // size of memory argument in bytes: 1, 2, 4, 8, 16, and so on.
Len int // length of encoded instruction in bytes
PCRel int // length of PC-relative address in instruction encoding
PCRelOff int // index of start of PC-relative address in instruction encoding
}
// Prefixes is an array of prefixes associated with a single instruction.
// The prefixes are listed in the same order as found in the instruction:
// each prefix byte corresponds to one slot in the array. The first zero
// in the array marks the end of the prefixes.
type Prefixes [14]Prefix
// A Prefix represents an Intel instruction prefix.
// The low 8 bits are the actual prefix byte encoding,
// and the top 8 bits contain distinguishing bits and metadata.
type Prefix uint16
const (
// Metadata about the role of a prefix in an instruction.
PrefixImplicit Prefix = 0x8000 // prefix is implied by instruction text
PrefixIgnored Prefix = 0x4000 // prefix is ignored: either irrelevant or overridden by a later prefix
PrefixInvalid Prefix = 0x2000 // prefix makes entire instruction invalid (bad LOCK)
// Memory segment overrides.
PrefixES Prefix = 0x26 // ES segment override
PrefixCS Prefix = 0x2E // CS segment override
PrefixSS Prefix = 0x36 // SS segment override
PrefixDS Prefix = 0x3E // DS segment override
PrefixFS Prefix = 0x64 // FS segment override
PrefixGS Prefix = 0x65 // GS segment override
// Branch prediction.
PrefixPN Prefix = 0x12E // predict not taken (conditional branch only)
PrefixPT Prefix = 0x13E // predict taken (conditional branch only)
// Size attributes.
PrefixDataSize Prefix = 0x66 // operand size override
PrefixData16 Prefix = 0x166
PrefixData32 Prefix = 0x266
PrefixAddrSize Prefix = 0x67 // address size override
PrefixAddr16 Prefix = 0x167
PrefixAddr32 Prefix = 0x267
// One of a kind.
PrefixLOCK Prefix = 0xF0 // lock
PrefixREPN Prefix = 0xF2 // repeat not zero
PrefixXACQUIRE Prefix = 0x1F2
PrefixBND Prefix = 0x2F2
PrefixREP Prefix = 0xF3 // repeat
PrefixXRELEASE Prefix = 0x1F3
// The REX prefixes must be in the range [PrefixREX, PrefixREX+0x10).
// the other bits are set or not according to the intended use.
PrefixREX Prefix = 0x40 // REX 64-bit extension prefix
PrefixREXW Prefix = 0x08 // extension bit W (64-bit instruction width)
PrefixREXR Prefix = 0x04 // extension bit R (r field in modrm)
PrefixREXX Prefix = 0x02 // extension bit X (index field in sib)
PrefixREXB Prefix = 0x01 // extension bit B (r/m field in modrm or base field in sib)
PrefixVEX2Bytes Prefix = 0xC5 // Short form of vex prefix
PrefixVEX3Bytes Prefix = 0xC4 // Long form of vex prefix
)
// IsREX reports whether p is a REX prefix byte.
func (p Prefix) IsREX() bool {
return p&0xF0 == PrefixREX
}
func (p Prefix) IsVEX() bool {
return p&0xFF == PrefixVEX2Bytes || p&0xFF == PrefixVEX3Bytes
}
func (p Prefix) String() string {
p &^= PrefixImplicit | PrefixIgnored | PrefixInvalid
if s := prefixNames[p]; s != "" {
return s
}
if p.IsREX() {
s := "REX."
if p&PrefixREXW != 0 {
s += "W"
}
if p&PrefixREXR != 0 {
s += "R"
}
if p&PrefixREXX != 0 {
s += "X"
}
if p&PrefixREXB != 0 {
s += "B"
}
return s
}
return fmt.Sprintf("Prefix(%#x)", int(p))
}
// An Op is an x86 opcode.
type Op uint32
func (op Op) String() string {
i := int(op)
if i < 0 || i >= len(opNames) || opNames[i] == "" {
return fmt.Sprintf("Op(%d)", i)
}
return opNames[i]
}
// An Args holds the instruction arguments.
// If an instruction has fewer than 4 arguments,
// the final elements in the array are nil.
type Args [4]Arg
// An Arg is a single instruction argument,
// one of these types: Reg, Mem, Imm, Rel.
type Arg interface {
String() string
isArg()
}
// Note that the implements of Arg that follow are all sized
// so that on a 64-bit machine the data can be inlined in
// the interface value instead of requiring an allocation.
// A Reg is a single register.
// The zero Reg value has no name but indicates ``no register.''
type Reg uint8
const (
_ Reg = iota
// 8-bit
AL
CL
DL
BL
AH
CH
DH
BH
SPB
BPB
SIB
DIB
R8B
R9B
R10B
R11B
R12B
R13B
R14B
R15B
// 16-bit
AX
CX
DX
BX
SP
BP
SI
DI
R8W
R9W
R10W
R11W
R12W
R13W
R14W
R15W
// 32-bit
EAX
ECX
EDX
EBX
ESP
EBP
ESI
EDI
R8L
R9L
R10L
R11L
R12L
R13L
R14L
R15L
// 64-bit
RAX
RCX
RDX
RBX
RSP
RBP
RSI
RDI
R8
R9
R10
R11
R12
R13
R14
R15
// Instruction pointer.
IP // 16-bit
EIP // 32-bit
RIP // 64-bit
// 387 floating point registers.
F0
F1
F2
F3
F4
F5
F6
F7
// MMX registers.
M0
M1
M2
M3
M4
M5
M6
M7
// XMM registers.
X0
X1
X2
X3
X4
X5
X6
X7
X8
X9
X10
X11
X12
X13
X14
X15
// Segment registers.
ES
CS
SS
DS
FS
GS
// System registers.
GDTR
IDTR
LDTR
MSW
TASK
// Control registers.
CR0
CR1
CR2
CR3
CR4
CR5
CR6
CR7
CR8
CR9
CR10
CR11
CR12
CR13
CR14
CR15
// Debug registers.
DR0
DR1
DR2
DR3
DR4
DR5
DR6
DR7
DR8
DR9
DR10
DR11
DR12
DR13
DR14
DR15
// Task registers.
TR0
TR1
TR2
TR3
TR4
TR5
TR6
TR7
)
const regMax = TR7
func (Reg) isArg() {}
func (r Reg) String() string {
i := int(r)
if i < 0 || i >= len(regNames) || regNames[i] == "" {
return fmt.Sprintf("Reg(%d)", i)
}
return regNames[i]
}
// A Mem is a memory reference.
// The general form is Segment:[Base+Scale*Index+Disp].
type Mem struct {
Segment Reg
Base Reg
Scale uint8
Index Reg
Disp int64
}
func (Mem) isArg() {}
func (m Mem) String() string {
var base, plus, scale, index, disp string
if m.Base != 0 {
base = m.Base.String()
}
if m.Scale != 0 {
if m.Base != 0 {
plus = "+"
}
if m.Scale > 1 {
scale = fmt.Sprintf("%d*", m.Scale)
}
index = m.Index.String()
}
if m.Disp != 0 || m.Base == 0 && m.Scale == 0 {
disp = fmt.Sprintf("%+#x", m.Disp)
}
return "[" + base + plus + scale + index + disp + "]"
}
// A Rel is an offset relative to the current instruction pointer.
type Rel int32
func (Rel) isArg() {}
func (r Rel) String() string {
return fmt.Sprintf(".%+d", r)
}
// An Imm is an integer constant.
type Imm int64
func (Imm) isArg() {}
func (i Imm) String() string {
return fmt.Sprintf("%#x", int64(i))
}
func (i Inst) String() string {
var buf bytes.Buffer
for _, p := range i.Prefix {
if p == 0 {
break
}
if p&PrefixImplicit != 0 {
continue
}
fmt.Fprintf(&buf, "%v ", p)
}
fmt.Fprintf(&buf, "%v", i.Op)
sep := " "
for _, v := range i.Args {
if v == nil {
break
}
fmt.Fprintf(&buf, "%s%v", sep, v)
sep = ", "
}
return buf.String()
}
func isReg(a Arg) bool {
_, ok := a.(Reg)
return ok
}
func isSegReg(a Arg) bool {
r, ok := a.(Reg)
return ok && ES <= r && r <= GS
}
func isMem(a Arg) bool {
_, ok := a.(Mem)
return ok
}
func isImm(a Arg) bool {
_, ok := a.(Imm)
return ok
}
func regBytes(a Arg) int {
r, ok := a.(Reg)
if !ok {
return 0
}
if AL <= r && r <= R15B {
return 1
}
if AX <= r && r <= R15W {
return 2
}
if EAX <= r && r <= R15L {
return 4
}
if RAX <= r && r <= R15 {
return 8
}
return 0
}
func isSegment(p Prefix) bool {
switch p {
case PrefixCS, PrefixDS, PrefixES, PrefixFS, PrefixGS, PrefixSS:
return true
}
return false
}
// The Op definitions and string list are in tables.go.
var prefixNames = map[Prefix]string{
PrefixCS: "CS",
PrefixDS: "DS",
PrefixES: "ES",
PrefixFS: "FS",
PrefixGS: "GS",
PrefixSS: "SS",
PrefixLOCK: "LOCK",
PrefixREP: "REP",
PrefixREPN: "REPN",
PrefixAddrSize: "ADDRSIZE",
PrefixDataSize: "DATASIZE",
PrefixAddr16: "ADDR16",
PrefixData16: "DATA16",
PrefixAddr32: "ADDR32",
PrefixData32: "DATA32",
PrefixBND: "BND",
PrefixXACQUIRE: "XACQUIRE",
PrefixXRELEASE: "XRELEASE",
PrefixREX: "REX",
PrefixPT: "PT",
PrefixPN: "PN",
}
var regNames = [...]string{
AL: "AL",
CL: "CL",
BL: "BL",
DL: "DL",
AH: "AH",
CH: "CH",
BH: "BH",
DH: "DH",
SPB: "SPB",
BPB: "BPB",
SIB: "SIB",
DIB: "DIB",
R8B: "R8B",
R9B: "R9B",
R10B: "R10B",
R11B: "R11B",
R12B: "R12B",
R13B: "R13B",
R14B: "R14B",
R15B: "R15B",
AX: "AX",
CX: "CX",
BX: "BX",
DX: "DX",
SP: "SP",
BP: "BP",
SI: "SI",
DI: "DI",
R8W: "R8W",
R9W: "R9W",
R10W: "R10W",
R11W: "R11W",
R12W: "R12W",
R13W: "R13W",
R14W: "R14W",
R15W: "R15W",
EAX: "EAX",
ECX: "ECX",
EDX: "EDX",
EBX: "EBX",
ESP: "ESP",
EBP: "EBP",
ESI: "ESI",
EDI: "EDI",
R8L: "R8L",
R9L: "R9L",
R10L: "R10L",
R11L: "R11L",
R12L: "R12L",
R13L: "R13L",
R14L: "R14L",
R15L: "R15L",
RAX: "RAX",
RCX: "RCX",
RDX: "RDX",
RBX: "RBX",
RSP: "RSP",
RBP: "RBP",
RSI: "RSI",
RDI: "RDI",
R8: "R8",
R9: "R9",
R10: "R10",
R11: "R11",
R12: "R12",
R13: "R13",
R14: "R14",
R15: "R15",
IP: "IP",
EIP: "EIP",
RIP: "RIP",
F0: "F0",
F1: "F1",
F2: "F2",
F3: "F3",
F4: "F4",
F5: "F5",
F6: "F6",
F7: "F7",
M0: "M0",
M1: "M1",
M2: "M2",
M3: "M3",
M4: "M4",
M5: "M5",
M6: "M6",
M7: "M7",
X0: "X0",
X1: "X1",
X2: "X2",
X3: "X3",
X4: "X4",
X5: "X5",
X6: "X6",
X7: "X7",
X8: "X8",
X9: "X9",
X10: "X10",
X11: "X11",
X12: "X12",
X13: "X13",
X14: "X14",
X15: "X15",
CS: "CS",
SS: "SS",
DS: "DS",
ES: "ES",
FS: "FS",
GS: "GS",
GDTR: "GDTR",
IDTR: "IDTR",
LDTR: "LDTR",
MSW: "MSW",
TASK: "TASK",
CR0: "CR0",
CR1: "CR1",
CR2: "CR2",
CR3: "CR3",
CR4: "CR4",
CR5: "CR5",
CR6: "CR6",
CR7: "CR7",
CR8: "CR8",
CR9: "CR9",
CR10: "CR10",
CR11: "CR11",
CR12: "CR12",
CR13: "CR13",
CR14: "CR14",
CR15: "CR15",
DR0: "DR0",
DR1: "DR1",
DR2: "DR2",
DR3: "DR3",
DR4: "DR4",
DR5: "DR5",
DR6: "DR6",
DR7: "DR7",
DR8: "DR8",
DR9: "DR9",
DR10: "DR10",
DR11: "DR11",
DR12: "DR12",
DR13: "DR13",
DR14: "DR14",
DR15: "DR15",
TR0: "TR0",
TR1: "TR1",
TR2: "TR2",
TR3: "TR3",
TR4: "TR4",
TR5: "TR5",
TR6: "TR6",
TR7: "TR7",
}

20
vendor/golang.org/x/arch/x86/x86asm/inst_test.go generated vendored Normal file
View File

@ -0,0 +1,20 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"strings"
"testing"
)
func TestRegString(t *testing.T) {
for r := Reg(1); r <= regMax; r++ {
if regNames[r] == "" {
t.Errorf("regNames[%d] is missing", int(r))
} else if s := r.String(); strings.Contains(s, "Reg(") {
t.Errorf("Reg(%d).String() = %s, want proper name", int(r), s)
}
}
}

560
vendor/golang.org/x/arch/x86/x86asm/intel.go generated vendored Normal file
View File

@ -0,0 +1,560 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"fmt"
"strings"
)
// IntelSyntax returns the Intel assembler syntax for the instruction, as defined by Intel's XED tool.
func IntelSyntax(inst Inst, pc uint64, symname SymLookup) string {
if symname == nil {
symname = func(uint64) (string, uint64) { return "", 0 }
}
var iargs []Arg
for _, a := range inst.Args {
if a == nil {
break
}
iargs = append(iargs, a)
}
switch inst.Op {
case INSB, INSD, INSW, OUTSB, OUTSD, OUTSW, LOOPNE, JCXZ, JECXZ, JRCXZ, LOOP, LOOPE, MOV, XLATB:
if inst.Op == MOV && (inst.Opcode>>16)&0xFFFC != 0x0F20 {
break
}
for i, p := range inst.Prefix {
if p&0xFF == PrefixAddrSize {
inst.Prefix[i] &^= PrefixImplicit
}
}
}
switch inst.Op {
case MOV:
dst, _ := inst.Args[0].(Reg)
src, _ := inst.Args[1].(Reg)
if ES <= dst && dst <= GS && EAX <= src && src <= R15L {
src -= EAX - AX
iargs[1] = src
}
if ES <= dst && dst <= GS && RAX <= src && src <= R15 {
src -= RAX - AX
iargs[1] = src
}
if inst.Opcode>>24&^3 == 0xA0 {
for i, p := range inst.Prefix {
if p&0xFF == PrefixAddrSize {
inst.Prefix[i] |= PrefixImplicit
}
}
}
}
switch inst.Op {
case AAM, AAD:
if imm, ok := iargs[0].(Imm); ok {
if inst.DataSize == 32 {
iargs[0] = Imm(uint32(int8(imm)))
} else if inst.DataSize == 16 {
iargs[0] = Imm(uint16(int8(imm)))
}
}
case PUSH:
if imm, ok := iargs[0].(Imm); ok {
iargs[0] = Imm(uint32(imm))
}
}
for _, p := range inst.Prefix {
if p&PrefixImplicit != 0 {
for j, pj := range inst.Prefix {
if pj&0xFF == p&0xFF {
inst.Prefix[j] |= PrefixImplicit
}
}
}
}
if inst.Op != 0 {
for i, p := range inst.Prefix {
switch p &^ PrefixIgnored {
case PrefixData16, PrefixData32, PrefixCS, PrefixDS, PrefixES, PrefixSS:
inst.Prefix[i] |= PrefixImplicit
}
if p.IsREX() {
inst.Prefix[i] |= PrefixImplicit
}
if p.IsVEX() {
if p == PrefixVEX3Bytes {
inst.Prefix[i+2] |= PrefixImplicit
}
inst.Prefix[i] |= PrefixImplicit
inst.Prefix[i+1] |= PrefixImplicit
}
}
}
if isLoop[inst.Op] || inst.Op == JCXZ || inst.Op == JECXZ || inst.Op == JRCXZ {
for i, p := range inst.Prefix {
if p == PrefixPT || p == PrefixPN {
inst.Prefix[i] |= PrefixImplicit
}
}
}
switch inst.Op {
case AAA, AAS, CBW, CDQE, CLC, CLD, CLI, CLTS, CMC, CPUID, CQO, CWD, DAA, DAS,
FDECSTP, FINCSTP, FNCLEX, FNINIT, FNOP, FWAIT, HLT,
ICEBP, INSB, INSD, INSW, INT, INTO, INVD, IRET, IRETQ,
LAHF, LEAVE, LRET, MONITOR, MWAIT, NOP, OUTSB, OUTSD, OUTSW,
PAUSE, POPA, POPF, POPFQ, PUSHA, PUSHF, PUSHFQ,
RDMSR, RDPMC, RDTSC, RDTSCP, RET, RSM,
SAHF, STC, STD, STI, SYSENTER, SYSEXIT, SYSRET,
UD2, WBINVD, WRMSR, XEND, XLATB, XTEST:
if inst.Op == NOP && inst.Opcode>>24 != 0x90 {
break
}
if inst.Op == RET && inst.Opcode>>24 != 0xC3 {
break
}
if inst.Op == INT && inst.Opcode>>24 != 0xCC {
break
}
if inst.Op == LRET && inst.Opcode>>24 != 0xcb {
break
}
for i, p := range inst.Prefix {
if p&0xFF == PrefixDataSize {
inst.Prefix[i] &^= PrefixImplicit | PrefixIgnored
}
}
case 0:
// ok
}
switch inst.Op {
case INSB, INSD, INSW, OUTSB, OUTSD, OUTSW, MONITOR, MWAIT, XLATB:
iargs = nil
case STOSB, STOSW, STOSD, STOSQ:
iargs = iargs[:1]
case LODSB, LODSW, LODSD, LODSQ, SCASB, SCASW, SCASD, SCASQ:
iargs = iargs[1:]
}
const (
haveData16 = 1 << iota
haveData32
haveAddr16
haveAddr32
haveXacquire
haveXrelease
haveLock
haveHintTaken
haveHintNotTaken
haveBnd
)
var prefixBits uint32
prefix := ""
for _, p := range inst.Prefix {
if p == 0 {
break
}
if p&0xFF == 0xF3 {
prefixBits &^= haveBnd
}
if p&(PrefixImplicit|PrefixIgnored) != 0 {
continue
}
switch p {
default:
prefix += strings.ToLower(p.String()) + " "
case PrefixCS, PrefixDS, PrefixES, PrefixFS, PrefixGS, PrefixSS:
if inst.Op == 0 {
prefix += strings.ToLower(p.String()) + " "
}
case PrefixREPN:
prefix += "repne "
case PrefixLOCK:
prefixBits |= haveLock
case PrefixData16, PrefixDataSize:
prefixBits |= haveData16
case PrefixData32:
prefixBits |= haveData32
case PrefixAddrSize, PrefixAddr16:
prefixBits |= haveAddr16
case PrefixAddr32:
prefixBits |= haveAddr32
case PrefixXACQUIRE:
prefixBits |= haveXacquire
case PrefixXRELEASE:
prefixBits |= haveXrelease
case PrefixPT:
prefixBits |= haveHintTaken
case PrefixPN:
prefixBits |= haveHintNotTaken
case PrefixBND:
prefixBits |= haveBnd
}
}
switch inst.Op {
case JMP:
if inst.Opcode>>24 == 0xEB {
prefixBits &^= haveBnd
}
case RET, LRET:
prefixBits &^= haveData16 | haveData32
}
if prefixBits&haveXacquire != 0 {
prefix += "xacquire "
}
if prefixBits&haveXrelease != 0 {
prefix += "xrelease "
}
if prefixBits&haveLock != 0 {
prefix += "lock "
}
if prefixBits&haveBnd != 0 {
prefix += "bnd "
}
if prefixBits&haveHintTaken != 0 {
prefix += "hint-taken "
}
if prefixBits&haveHintNotTaken != 0 {
prefix += "hint-not-taken "
}
if prefixBits&haveAddr16 != 0 {
prefix += "addr16 "
}
if prefixBits&haveAddr32 != 0 {
prefix += "addr32 "
}
if prefixBits&haveData16 != 0 {
prefix += "data16 "
}
if prefixBits&haveData32 != 0 {
prefix += "data32 "
}
if inst.Op == 0 {
if prefix == "" {
return "<no instruction>"
}
return prefix[:len(prefix)-1]
}
var args []string
for _, a := range iargs {
if a == nil {
break
}
args = append(args, intelArg(&inst, pc, symname, a))
}
var op string
switch inst.Op {
case NOP:
if inst.Opcode>>24 == 0x0F {
if inst.DataSize == 16 {
args = append(args, "ax")
} else {
args = append(args, "eax")
}
}
case BLENDVPD, BLENDVPS, PBLENDVB:
args = args[:2]
case INT:
if inst.Opcode>>24 == 0xCC {
args = nil
op = "int3"
}
case LCALL, LJMP:
if len(args) == 2 {
args[0], args[1] = args[1], args[0]
}
case FCHS, FABS, FTST, FLDPI, FLDL2E, FLDLG2, F2XM1, FXAM, FLD1, FLDL2T, FSQRT, FRNDINT, FCOS, FSIN:
if len(args) == 0 {
args = append(args, "st0")
}
case FPTAN, FSINCOS, FUCOMPP, FCOMPP, FYL2X, FPATAN, FXTRACT, FPREM1, FPREM, FYL2XP1, FSCALE:
if len(args) == 0 {
args = []string{"st0", "st1"}
}
case FST, FSTP, FISTTP, FIST, FISTP, FBSTP:
if len(args) == 1 {
args = append(args, "st0")
}
case FLD, FXCH, FCOM, FCOMP, FIADD, FIMUL, FICOM, FICOMP, FISUBR, FIDIV, FUCOM, FUCOMP, FILD, FBLD, FADD, FMUL, FSUB, FSUBR, FISUB, FDIV, FDIVR, FIDIVR:
if len(args) == 1 {
args = []string{"st0", args[0]}
}
case MASKMOVDQU, MASKMOVQ, XLATB, OUTSB, OUTSW, OUTSD:
FixSegment:
for i := len(inst.Prefix) - 1; i >= 0; i-- {
p := inst.Prefix[i] & 0xFF
switch p {
case PrefixCS, PrefixES, PrefixFS, PrefixGS, PrefixSS:
if inst.Mode != 64 || p == PrefixFS || p == PrefixGS {
args = append(args, strings.ToLower((inst.Prefix[i] & 0xFF).String()))
break FixSegment
}
case PrefixDS:
if inst.Mode != 64 {
break FixSegment
}
}
}
}
if op == "" {
op = intelOp[inst.Op]
}
if op == "" {
op = strings.ToLower(inst.Op.String())
}
if args != nil {
op += " " + strings.Join(args, ", ")
}
return prefix + op
}
func intelArg(inst *Inst, pc uint64, symname SymLookup, arg Arg) string {
switch a := arg.(type) {
case Imm:
if s, base := symname(uint64(a)); s != "" {
suffix := ""
if uint64(a) != base {
suffix = fmt.Sprintf("%+d", uint64(a)-base)
}
return fmt.Sprintf("$%s%s", s, suffix)
}
if inst.Mode == 32 {
return fmt.Sprintf("%#x", uint32(a))
}
if Imm(int32(a)) == a {
return fmt.Sprintf("%#x", int64(a))
}
return fmt.Sprintf("%#x", uint64(a))
case Mem:
if a.Base == EIP {
a.Base = RIP
}
prefix := ""
switch inst.MemBytes {
case 1:
prefix = "byte "
case 2:
prefix = "word "
case 4:
prefix = "dword "
case 8:
prefix = "qword "
case 16:
prefix = "xmmword "
case 32:
prefix = "ymmword "
}
switch inst.Op {
case INVLPG:
prefix = "byte "
case STOSB, MOVSB, CMPSB, LODSB, SCASB:
prefix = "byte "
case STOSW, MOVSW, CMPSW, LODSW, SCASW:
prefix = "word "
case STOSD, MOVSD, CMPSD, LODSD, SCASD:
prefix = "dword "
case STOSQ, MOVSQ, CMPSQ, LODSQ, SCASQ:
prefix = "qword "
case LAR:
prefix = "word "
case BOUND:
if inst.Mode == 32 {
prefix = "qword "
} else {
prefix = "dword "
}
case PREFETCHW, PREFETCHNTA, PREFETCHT0, PREFETCHT1, PREFETCHT2, CLFLUSH:
prefix = "zmmword "
}
switch inst.Op {
case MOVSB, MOVSW, MOVSD, MOVSQ, CMPSB, CMPSW, CMPSD, CMPSQ, STOSB, STOSW, STOSD, STOSQ, SCASB, SCASW, SCASD, SCASQ, LODSB, LODSW, LODSD, LODSQ:
switch a.Base {
case DI, EDI, RDI:
if a.Segment == ES {
a.Segment = 0
}
case SI, ESI, RSI:
if a.Segment == DS {
a.Segment = 0
}
}
case LEA:
a.Segment = 0
default:
switch a.Base {
case SP, ESP, RSP, BP, EBP, RBP:
if a.Segment == SS {
a.Segment = 0
}
default:
if a.Segment == DS {
a.Segment = 0
}
}
}
if inst.Mode == 64 && a.Segment != FS && a.Segment != GS {
a.Segment = 0
}
prefix += "ptr "
if s, disp := memArgToSymbol(a, pc, inst.Len, symname); s != "" {
suffix := ""
if disp != 0 {
suffix = fmt.Sprintf("%+d", disp)
}
return prefix + fmt.Sprintf("[%s%s]", s, suffix)
}
if a.Segment != 0 {
prefix += strings.ToLower(a.Segment.String()) + ":"
}
prefix += "["
if a.Base != 0 {
prefix += intelArg(inst, pc, symname, a.Base)
}
if a.Scale != 0 && a.Index != 0 {
if a.Base != 0 {
prefix += "+"
}
prefix += fmt.Sprintf("%s*%d", intelArg(inst, pc, symname, a.Index), a.Scale)
}
if a.Disp != 0 {
if prefix[len(prefix)-1] == '[' && (a.Disp >= 0 || int64(int32(a.Disp)) != a.Disp) {
prefix += fmt.Sprintf("%#x", uint64(a.Disp))
} else {
prefix += fmt.Sprintf("%+#x", a.Disp)
}
}
prefix += "]"
return prefix
case Rel:
if pc == 0 {
return fmt.Sprintf(".%+#x", int64(a))
} else {
addr := pc + uint64(inst.Len) + uint64(a)
if s, base := symname(addr); s != "" && addr == base {
return fmt.Sprintf("%s", s)
} else {
addr := pc + uint64(inst.Len) + uint64(a)
return fmt.Sprintf("%#x", addr)
}
}
case Reg:
if int(a) < len(intelReg) && intelReg[a] != "" {
switch inst.Op {
case VMOVDQA, VMOVDQU, VMOVNTDQA, VMOVNTDQ:
return strings.Replace(intelReg[a], "xmm", "ymm", -1)
default:
return intelReg[a]
}
}
}
return strings.ToLower(arg.String())
}
var intelOp = map[Op]string{
JAE: "jnb",
JA: "jnbe",
JGE: "jnl",
JNE: "jnz",
JG: "jnle",
JE: "jz",
SETAE: "setnb",
SETA: "setnbe",
SETGE: "setnl",
SETNE: "setnz",
SETG: "setnle",
SETE: "setz",
CMOVAE: "cmovnb",
CMOVA: "cmovnbe",
CMOVGE: "cmovnl",
CMOVNE: "cmovnz",
CMOVG: "cmovnle",
CMOVE: "cmovz",
LCALL: "call far",
LJMP: "jmp far",
LRET: "ret far",
ICEBP: "int1",
MOVSD_XMM: "movsd",
XLATB: "xlat",
}
var intelReg = [...]string{
F0: "st0",
F1: "st1",
F2: "st2",
F3: "st3",
F4: "st4",
F5: "st5",
F6: "st6",
F7: "st7",
M0: "mmx0",
M1: "mmx1",
M2: "mmx2",
M3: "mmx3",
M4: "mmx4",
M5: "mmx5",
M6: "mmx6",
M7: "mmx7",
X0: "xmm0",
X1: "xmm1",
X2: "xmm2",
X3: "xmm3",
X4: "xmm4",
X5: "xmm5",
X6: "xmm6",
X7: "xmm7",
X8: "xmm8",
X9: "xmm9",
X10: "xmm10",
X11: "xmm11",
X12: "xmm12",
X13: "xmm13",
X14: "xmm14",
X15: "xmm15",
// TODO: Maybe the constants are named wrong.
SPB: "spl",
BPB: "bpl",
SIB: "sil",
DIB: "dil",
R8L: "r8d",
R9L: "r9d",
R10L: "r10d",
R11L: "r11d",
R12L: "r12d",
R13L: "r13d",
R14L: "r14d",
R15L: "r15d",
}

384
vendor/golang.org/x/arch/x86/x86asm/objdump_test.go generated vendored Normal file
View File

@ -0,0 +1,384 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"bytes"
"strings"
"testing"
)
func TestObjdump32Manual(t *testing.T) { testObjdump32(t, hexCases(t, objdumpManualTests)) }
func TestObjdump32Testdata(t *testing.T) { testObjdump32(t, concat(basicPrefixes, testdataCases(t))) }
func TestObjdump32ModRM(t *testing.T) { testObjdump32(t, concat(basicPrefixes, enumModRM)) }
func TestObjdump32OneByte(t *testing.T) { testBasic(t, testObjdump32) }
func TestObjdump320F(t *testing.T) { testBasic(t, testObjdump32, 0x0F) }
func TestObjdump320F38(t *testing.T) { testBasic(t, testObjdump32, 0x0F, 0x38) }
func TestObjdump320F3A(t *testing.T) { testBasic(t, testObjdump32, 0x0F, 0x3A) }
func TestObjdump32Prefix(t *testing.T) { testPrefix(t, testObjdump32) }
func TestObjdump64Manual(t *testing.T) { testObjdump64(t, hexCases(t, objdumpManualTests)) }
func TestObjdump64Testdata(t *testing.T) { testObjdump64(t, concat(basicPrefixes, testdataCases(t))) }
func TestObjdump64ModRM(t *testing.T) { testObjdump64(t, concat(basicPrefixes, enumModRM)) }
func TestObjdump64OneByte(t *testing.T) { testBasic(t, testObjdump64) }
func TestObjdump640F(t *testing.T) { testBasic(t, testObjdump64, 0x0F) }
func TestObjdump640F38(t *testing.T) { testBasic(t, testObjdump64, 0x0F, 0x38) }
func TestObjdump640F3A(t *testing.T) { testBasic(t, testObjdump64, 0x0F, 0x3A) }
func TestObjdump64Prefix(t *testing.T) { testPrefix(t, testObjdump64) }
func TestObjdump64REXTestdata(t *testing.T) {
testObjdump64(t, filter(concat3(basicPrefixes, rexPrefixes, testdataCases(t)), isValidREX))
}
func TestObjdump64REXModRM(t *testing.T) {
testObjdump64(t, concat3(basicPrefixes, rexPrefixes, enumModRM))
}
func TestObjdump64REXOneByte(t *testing.T) { testBasicREX(t, testObjdump64) }
func TestObjdump64REX0F(t *testing.T) { testBasicREX(t, testObjdump64, 0x0F) }
func TestObjdump64REX0F38(t *testing.T) { testBasicREX(t, testObjdump64, 0x0F, 0x38) }
func TestObjdump64REX0F3A(t *testing.T) { testBasicREX(t, testObjdump64, 0x0F, 0x3A) }
func TestObjdump64REXPrefix(t *testing.T) { testPrefixREX(t, testObjdump64) }
// objdumpManualTests holds test cases that will be run by TestObjdumpManual.
// If you are debugging a few cases that turned up in a longer run, it can be useful
// to list them here and then use -run=ObjdumpManual, particularly with tracing enabled.
var objdumpManualTests = `
4883FE017413
488DFC2500000000
488D3D00000000
`
// allowedMismatchObjdump reports whether the mismatch between text and dec
// should be allowed by the test.
func allowedMismatchObjdump(text string, size int, inst *Inst, dec ExtInst) bool {
if size == 15 && dec.nenc == 15 && contains(text, "truncated") && contains(dec.text, "(bad)") {
return true
}
if i := strings.LastIndex(dec.text, " "); isPrefix(dec.text[i+1:]) && size == 1 && isPrefix(text) {
return true
}
if size == dec.nenc && contains(dec.text, "movupd") && contains(dec.text, "data32") {
s := strings.Replace(dec.text, "data32 ", "", -1)
if text == s {
return true
}
}
// Simplify our invalid instruction text.
if text == "error: unrecognized instruction" {
text = "BAD"
}
// Invalid instructions for which libopcodes prints %? register.
// FF E8 11 22 33 44:
// Invalid instructions for which libopcodes prints "internal disassembler error".
// Invalid instructions for which libopcodes prints 8087 only (e.g., DB E0)
// or prints 287 only (e.g., DB E4).
if contains(dec.text, "%?", "<internal disassembler error>", "(8087 only)", "(287 only)") {
dec.text = "(bad)"
}
// 0F 19 11, 0F 1C 11, 0F 1D 11, 0F 1E 11, 0F 1F 11: libopcodes says nop,
// but the Intel manuals say that the only NOP there is 0F 1F /0.
// Perhaps libopcodes is reporting an older encoding.
i := bytes.IndexByte(dec.enc[:], 0x0F)
if contains(dec.text, "nop") && i >= 0 && i+2 < len(dec.enc) && dec.enc[i+1]&^7 == 0x18 && (dec.enc[i+1] != 0x1F || (dec.enc[i+2]>>3)&7 != 0) {
dec.text = "(bad)"
}
// Any invalid instruction.
if text == "BAD" && contains(dec.text, "(bad)") {
return true
}
// Instructions libopcodes knows but we do not (e.g., 0F 19 11).
if (text == "BAD" || size == 1 && isPrefix(text)) && hasPrefix(dec.text, unsupported...) {
return true
}
// Instructions we know but libopcodes does not (e.g., 0F D0 11).
if (contains(dec.text, "(bad)") || dec.nenc == 1 && isPrefix(dec.text)) && hasPrefix(text, libopcodesUnsupported...) {
return true
}
// Libopcodes rejects F2 90 as NOP. Not sure why.
if (contains(dec.text, "(bad)") || dec.nenc == 1 && isPrefix(dec.text)) && inst.Opcode>>24 == 0x90 && countPrefix(inst, 0xF2) > 0 {
return true
}
// 0F 20 11, 0F 21 11, 0F 22 11, 0F 23 11, 0F 24 11:
// Moves into and out of some control registers seem to be unsupported by libopcodes.
// TODO(rsc): Are they invalid somehow?
if (contains(dec.text, "(bad)") || dec.nenc == 1 && isPrefix(dec.text)) && contains(text, "%cr", "%db", "%tr") {
return true
}
if contains(dec.text, "fwait") && dec.nenc == 1 && dec.enc[0] != 0x9B {
return true
}
// 9B D9 11: libopcodes reports FSTSW instead of FWAIT + FNSTSW.
// This is correct in that FSTSW is a pseudo-op for the pair, but it really
// is a pair of instructions: execution can stop between them.
// Our decoder chooses to separate them.
if (text == "fwait" || strings.HasSuffix(text, " fwait")) && dec.nenc >= len(strings.Fields(text)) && dec.enc[len(strings.Fields(text))-1] == 0x9B {
return true
}
// 0F 18 77 11:
// Invalid instructions for which libopcodes prints "nop/reserved".
// Perhaps libopcodes is reporting an older encoding.
if text == "BAD" && contains(dec.text, "nop/reserved") {
return true
}
// 0F C7 B0 11 22 33 44: libopcodes says vmptrld 0x44332211(%eax); we say rdrand %eax.
// TODO(rsc): Fix, since we are probably wrong, but we don't have vmptrld in the manual.
if contains(text, "rdrand") && contains(dec.text, "vmptrld", "vmxon", "vmclear") {
return true
}
// DD C8: libopcodes says FNOP but the Intel manual is clear FNOP is only D9 D0.
// Perhaps libopcodes is reporting an older encoding.
if text == "BAD" && contains(dec.text, "fnop") && (dec.enc[0] != 0xD9 || dec.enc[1] != 0xD0) {
return true
}
// 66 90: libopcodes says xchg %ax,%ax; we say 'data16 nop'.
// The 16-bit swap will preserve the high bits of the register,
// so they are the same.
if contains(text, "nop") && contains(dec.text, "xchg %ax,%ax") {
return true
}
// If there are multiple prefixes, allow libopcodes to use an alternate name.
if size == 1 && dec.nenc == 1 && prefixByte[text] > 0 && prefixByte[text] == prefixByte[dec.text] {
return true
}
// 26 9B: libopcodes reports "fwait"/1, ignoring segment prefix.
// https://sourceware.org/bugzilla/show_bug.cgi?id=16891
// F0 82: Decode="lock"/1 but libopcodes="lock (bad)"/2.
if size == 1 && dec.nenc >= 1 && prefixByte[text] == dec.enc[0] && contains(dec.text, "(bad)", "fwait", "fnop") {
return true
}
// libopcodes interprets 660f801122 as taking a rel16 but
// truncating the address at 16 bits. Not sure what is correct.
if contains(text, ".+0x2211", ".+0x11") && contains(dec.text, " .-") {
return true
}
// 66 F3 0F D6 C5, 66 F2 0F D6 C0: libopcodes reports use of XMM register instead of MMX register,
// but only when the instruction has a 66 prefix. Maybe they know something we don't.
if countPrefix(inst, 0x66) > 0 && contains(dec.text, "movdq2q", "movq2dq") && !contains(dec.text, "%mm") {
return true
}
// 0F 01 F8, 0F 05, 0F 07: these are 64-bit instructions but libopcodes accepts them.
if (text == "BAD" || size == 1 && isPrefix(text)) && contains(dec.text, "swapgs", "syscall", "sysret", "rdfsbase", "rdgsbase", "wrfsbase", "wrgsbase") {
return true
}
return false
}
// Instructions known to libopcodes (or xed) but not to us.
// Most of these come from supplementary manuals of one form or another.
var unsupported = strings.Fields(`
bndc
bndl
bndm
bnds
clac
clgi
femms
fldln
getsec
invlpga
kmov
montmul
pavg
pf2i
pfacc
pfadd
pfcmp
pfmax
pfmin
pfmul
pfna
pfpnac
pfrc
pfrs
pfsub
phadd
phsub
pi2f
pmulhr
prefetch
pswap
ptest
rdseed
sha1
sha256
skinit
stac
stgi
vadd
vand
vcmp
vcomis
vcvt
vcvt
vdiv
vhadd
vhsub
vld
vmax
vmcall
vmfunc
vmin
vmlaunch
vmload
vmmcall
vmov
vmov
vmov
vmptrld
vmptrst
vmread
vmresume
vmrun
vmsave
vmul
vmwrite
vmxoff
vor
vpack
vpadd
vpand
vpavg
vpcmp
vpcmp
vpins
vpmadd
vpmax
vpmin
vpmul
vpmul
vpor
vpsad
vpshuf
vpsll
vpsra
vpsrad
vpsrl
vpsub
vpunp
vpxor
vrcp
vrsqrt
vshuf
vsqrt
vsub
vucomis
vunp
vxor
vzero
xcrypt
xsha1
xsha256
xstore-rng
insertq
extrq
vmclear
invvpid
adox
vmxon
invept
adcx
vmclear
prefetchwt1
enclu
encls
salc
fstpnce
fdisi8087_nop
fsetpm287_nop
feni8087_nop
syscall
sysret
`)
// Instructions known to us but not to libopcodes (at least in binutils 2.24).
var libopcodesUnsupported = strings.Fields(`
addsubps
aes
blend
cvttpd2dq
dpp
extract
haddps
hsubps
insert
invpcid
lddqu
movmsk
movnt
movq2dq
mps
pack
pblend
pclmul
pcmp
pext
phmin
pins
pmax
pmin
pmov
pmovmsk
pmul
popcnt
pslld
psllq
psllw
psrad
psraw
psrl
ptest
punpck
round
xrstor
xsavec
xsaves
comis
ucomis
movhps
movntps
rsqrt
rcpp
puncpck
bsf
movq2dq
cvttpd2dq
movq
hsubpd
movdqa
movhpd
addsubpd
movd
haddpd
cvtps2dq
bsr
cvtdq2ps
rdrand
maskmov
movq2dq
movlhps
movbe
movlpd
`)

313
vendor/golang.org/x/arch/x86/x86asm/objdumpext_test.go generated vendored Normal file
View File

@ -0,0 +1,313 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"bytes"
"debug/elf"
"encoding/binary"
"fmt"
"io"
"log"
"os"
"strconv"
"strings"
"testing"
)
// Apologies for the proprietary path, but we need objdump 2.24 + some committed patches that will land in 2.25.
const objdumpPath = "/Users/rsc/bin/objdump2"
func testObjdump32(t *testing.T, generate func(func([]byte))) {
testObjdumpArch(t, generate, 32)
}
func testObjdump64(t *testing.T, generate func(func([]byte))) {
testObjdumpArch(t, generate, 64)
}
func testObjdumpArch(t *testing.T, generate func(func([]byte)), arch int) {
if testing.Short() {
t.Skip("skipping objdump test in short mode")
}
if _, err := os.Stat(objdumpPath); err != nil {
t.Skip(err)
}
testExtDis(t, "gnu", arch, objdump, generate, allowedMismatchObjdump)
}
func objdump(ext *ExtDis) error {
// File already written with instructions; add ELF header.
if ext.Arch == 32 {
if err := writeELF32(ext.File, ext.Size); err != nil {
return err
}
} else {
if err := writeELF64(ext.File, ext.Size); err != nil {
return err
}
}
b, err := ext.Run(objdumpPath, "-d", "-z", ext.File.Name())
if err != nil {
return err
}
var (
nmatch int
reading bool
next uint32 = start
addr uint32
encbuf [32]byte
enc []byte
text string
)
flush := func() {
if addr == next {
switch text {
case "repz":
text = "rep"
case "repnz":
text = "repn"
default:
text = strings.Replace(text, "repz ", "rep ", -1)
text = strings.Replace(text, "repnz ", "repn ", -1)
}
if m := pcrelw.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], int16(uint32(targ)-uint32(uint16(addr))-uint32(len(enc))))
}
if m := pcrel.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], int32(uint32(targ)-addr-uint32(len(enc))))
}
text = strings.Replace(text, "0x0(", "(", -1)
text = strings.Replace(text, "%st(0)", "%st", -1)
ext.Dec <- ExtInst{addr, encbuf, len(enc), text}
encbuf = [32]byte{}
enc = nil
next += 32
}
}
var textangle = []byte("<.text>:")
for {
line, err := b.ReadSlice('\n')
if err != nil {
if err == io.EOF {
break
}
return fmt.Errorf("reading objdump output: %v", err)
}
if bytes.Contains(line, textangle) {
reading = true
continue
}
if !reading {
continue
}
if debug {
os.Stdout.Write(line)
}
if enc1 := parseContinuation(line, encbuf[:len(enc)]); enc1 != nil {
enc = enc1
continue
}
flush()
nmatch++
addr, enc, text = parseLine(line, encbuf[:0])
if addr > next {
return fmt.Errorf("address out of sync expected <= %#x at %q in:\n%s", next, line, line)
}
}
flush()
if next != start+uint32(ext.Size) {
return fmt.Errorf("not enough results found [%d %d]", next, start+ext.Size)
}
if err := ext.Wait(); err != nil {
return fmt.Errorf("exec: %v", err)
}
return nil
}
func parseLine(line []byte, encstart []byte) (addr uint32, enc []byte, text string) {
oline := line
i := index(line, ":\t")
if i < 0 {
log.Fatalf("cannot parse disassembly: %q", oline)
}
x, err := strconv.ParseUint(string(trimSpace(line[:i])), 16, 32)
if err != nil {
log.Fatalf("cannot parse disassembly: %q", oline)
}
addr = uint32(x)
line = line[i+2:]
i = bytes.IndexByte(line, '\t')
if i < 0 {
log.Fatalf("cannot parse disassembly: %q", oline)
}
enc, ok := parseHex(line[:i], encstart)
if !ok {
log.Fatalf("cannot parse disassembly: %q", oline)
}
line = trimSpace(line[i:])
if i := bytes.IndexByte(line, '#'); i >= 0 {
line = trimSpace(line[:i])
}
text = string(fixSpace(line))
return
}
func parseContinuation(line []byte, enc []byte) []byte {
i := index(line, ":\t")
if i < 0 {
return nil
}
line = line[i+1:]
enc, _ = parseHex(line, enc)
return enc
}
// writeELF32 writes an ELF32 header to the file,
// describing a text segment that starts at start
// and extends for size bytes.
func writeELF32(f *os.File, size int) error {
f.Seek(0, io.SeekStart)
var hdr elf.Header32
var prog elf.Prog32
var sect elf.Section32
var buf bytes.Buffer
binary.Write(&buf, binary.LittleEndian, &hdr)
off1 := buf.Len()
binary.Write(&buf, binary.LittleEndian, &prog)
off2 := buf.Len()
binary.Write(&buf, binary.LittleEndian, &sect)
off3 := buf.Len()
buf.Reset()
data := byte(elf.ELFDATA2LSB)
hdr = elf.Header32{
Ident: [16]byte{0x7F, 'E', 'L', 'F', 1, data, 1},
Type: 2,
Machine: uint16(elf.EM_386),
Version: 1,
Entry: start,
Phoff: uint32(off1),
Shoff: uint32(off2),
Flags: 0x05000002,
Ehsize: uint16(off1),
Phentsize: uint16(off2 - off1),
Phnum: 1,
Shentsize: uint16(off3 - off2),
Shnum: 3,
Shstrndx: 2,
}
binary.Write(&buf, binary.LittleEndian, &hdr)
prog = elf.Prog32{
Type: 1,
Off: start,
Vaddr: start,
Paddr: start,
Filesz: uint32(size),
Memsz: uint32(size),
Flags: 5,
Align: start,
}
binary.Write(&buf, binary.LittleEndian, &prog)
binary.Write(&buf, binary.LittleEndian, &sect) // NULL section
sect = elf.Section32{
Name: 1,
Type: uint32(elf.SHT_PROGBITS),
Addr: start,
Off: start,
Size: uint32(size),
Flags: uint32(elf.SHF_ALLOC | elf.SHF_EXECINSTR),
Addralign: 4,
}
binary.Write(&buf, binary.LittleEndian, &sect) // .text
sect = elf.Section32{
Name: uint32(len("\x00.text\x00")),
Type: uint32(elf.SHT_STRTAB),
Addr: 0,
Off: uint32(off2 + (off3-off2)*3),
Size: uint32(len("\x00.text\x00.shstrtab\x00")),
Addralign: 1,
}
binary.Write(&buf, binary.LittleEndian, &sect)
buf.WriteString("\x00.text\x00.shstrtab\x00")
f.Write(buf.Bytes())
return nil
}
// writeELF64 writes an ELF64 header to the file,
// describing a text segment that starts at start
// and extends for size bytes.
func writeELF64(f *os.File, size int) error {
f.Seek(0, io.SeekStart)
var hdr elf.Header64
var prog elf.Prog64
var sect elf.Section64
var buf bytes.Buffer
binary.Write(&buf, binary.LittleEndian, &hdr)
off1 := buf.Len()
binary.Write(&buf, binary.LittleEndian, &prog)
off2 := buf.Len()
binary.Write(&buf, binary.LittleEndian, &sect)
off3 := buf.Len()
buf.Reset()
data := byte(elf.ELFDATA2LSB)
hdr = elf.Header64{
Ident: [16]byte{0x7F, 'E', 'L', 'F', 2, data, 1},
Type: 2,
Machine: uint16(elf.EM_X86_64),
Version: 1,
Entry: start,
Phoff: uint64(off1),
Shoff: uint64(off2),
Flags: 0x05000002,
Ehsize: uint16(off1),
Phentsize: uint16(off2 - off1),
Phnum: 1,
Shentsize: uint16(off3 - off2),
Shnum: 3,
Shstrndx: 2,
}
binary.Write(&buf, binary.LittleEndian, &hdr)
prog = elf.Prog64{
Type: 1,
Off: start,
Vaddr: start,
Paddr: start,
Filesz: uint64(size),
Memsz: uint64(size),
Flags: 5,
Align: start,
}
binary.Write(&buf, binary.LittleEndian, &prog)
binary.Write(&buf, binary.LittleEndian, &sect) // NULL section
sect = elf.Section64{
Name: 1,
Type: uint32(elf.SHT_PROGBITS),
Addr: start,
Off: start,
Size: uint64(size),
Flags: uint64(elf.SHF_ALLOC | elf.SHF_EXECINSTR),
Addralign: 4,
}
binary.Write(&buf, binary.LittleEndian, &sect) // .text
sect = elf.Section64{
Name: uint32(len("\x00.text\x00")),
Type: uint32(elf.SHT_STRTAB),
Addr: 0,
Off: uint64(off2 + (off3-off2)*3),
Size: uint64(len("\x00.text\x00.shstrtab\x00")),
Addralign: 1,
}
binary.Write(&buf, binary.LittleEndian, &sect)
buf.WriteString("\x00.text\x00.shstrtab\x00")
f.Write(buf.Bytes())
return nil
}

119
vendor/golang.org/x/arch/x86/x86asm/plan9ext_test.go generated vendored Normal file
View File

@ -0,0 +1,119 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"bytes"
"fmt"
"io"
"log"
"os"
"strconv"
"testing"
)
const plan9Path = "testdata/libmach8db"
func testPlan9Arch(t *testing.T, arch int, generate func(func([]byte))) {
if testing.Short() {
t.Skip("skipping libmach test in short mode")
}
if _, err := os.Stat(plan9Path); err != nil {
t.Skip(err)
}
testExtDis(t, "plan9", arch, plan9, generate, allowedMismatchPlan9)
}
func testPlan932(t *testing.T, generate func(func([]byte))) {
testPlan9Arch(t, 32, generate)
}
func testPlan964(t *testing.T, generate func(func([]byte))) {
testPlan9Arch(t, 64, generate)
}
func plan9(ext *ExtDis) error {
flag := "-8"
if ext.Arch == 64 {
flag = "-6"
}
b, err := ext.Run(plan9Path, flag, ext.File.Name())
if err != nil {
return err
}
nmatch := 0
next := uint32(start)
var (
addr uint32
encbuf [32]byte
enc []byte
text string
)
for {
line, err := b.ReadSlice('\n')
if err != nil {
if err == io.EOF {
break
}
return fmt.Errorf("reading libmach8db output: %v", err)
}
if debug {
os.Stdout.Write(line)
}
nmatch++
addr, enc, text = parseLinePlan9(line, encbuf[:0])
if addr > next {
return fmt.Errorf("address out of sync expected <= %#x at %q in:\n%s", next, line, line)
}
if addr < next {
continue
}
if m := pcrelw.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], int16(uint32(targ)-uint32(uint16(addr))-uint32(len(enc))))
}
if m := pcrel.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], int32(uint32(targ)-addr-uint32(len(enc))))
}
ext.Dec <- ExtInst{addr, encbuf, len(enc), text}
encbuf = [32]byte{}
enc = nil
next += 32
}
if next != start+uint32(ext.Size) {
return fmt.Errorf("not enough results found [%d %d]", next, start+ext.Size)
}
if err := ext.Wait(); err != nil {
return fmt.Errorf("exec: %v", err)
}
return nil
}
func parseLinePlan9(line []byte, encstart []byte) (addr uint32, enc []byte, text string) {
i := bytes.IndexByte(line, ' ')
if i < 0 || line[0] != '0' || line[1] != 'x' {
log.Fatalf("cannot parse disassembly: %q", line)
}
j := bytes.IndexByte(line[i+1:], ' ')
if j < 0 {
log.Fatalf("cannot parse disassembly: %q", line)
}
j += i + 1
x, err := strconv.ParseUint(string(trimSpace(line[2:i])), 16, 32)
if err != nil {
log.Fatalf("cannot parse disassembly: %q", line)
}
addr = uint32(x)
enc, ok := parseHex(line[i+1:j], encstart)
if !ok {
log.Fatalf("cannot parse disassembly: %q", line)
}
return addr, enc, string(fixSpace(line[j+1:]))
}

381
vendor/golang.org/x/arch/x86/x86asm/plan9x.go generated vendored Normal file
View File

@ -0,0 +1,381 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"fmt"
"strings"
)
type SymLookup func(uint64) (string, uint64)
// GoSyntax returns the Go assembler syntax for the instruction.
// The syntax was originally defined by Plan 9.
// The pc is the program counter of the instruction, used for expanding
// PC-relative addresses into absolute ones.
// The symname function queries the symbol table for the program
// being disassembled. Given a target address it returns the name and base
// address of the symbol containing the target, if any; otherwise it returns "", 0.
func GoSyntax(inst Inst, pc uint64, symname SymLookup) string {
if symname == nil {
symname = func(uint64) (string, uint64) { return "", 0 }
}
var args []string
for i := len(inst.Args) - 1; i >= 0; i-- {
a := inst.Args[i]
if a == nil {
continue
}
args = append(args, plan9Arg(&inst, pc, symname, a))
}
var rep string
var last Prefix
for _, p := range inst.Prefix {
if p == 0 || p.IsREX() || p.IsVEX() {
break
}
switch {
// Don't show prefixes implied by the instruction text.
case p&0xFF00 == PrefixImplicit:
continue
// Only REP and REPN are recognized repeaters. Plan 9 syntax
// treats them as separate opcodes.
case p&0xFF == PrefixREP:
rep = "REP; "
case p&0xFF == PrefixREPN:
rep = "REPNE; "
default:
last = p
}
}
prefix := ""
switch last & 0xFF {
case 0, 0x66, 0x67:
// ignore
default:
prefix += last.String() + " "
}
op := inst.Op.String()
if plan9Suffix[inst.Op] {
s := inst.DataSize
if inst.MemBytes != 0 {
s = inst.MemBytes * 8
}
switch s {
case 8:
op += "B"
case 16:
op += "W"
case 32:
op += "L"
case 64:
op += "Q"
}
}
if args != nil {
op += " " + strings.Join(args, ", ")
}
return rep + prefix + op
}
func plan9Arg(inst *Inst, pc uint64, symname func(uint64) (string, uint64), arg Arg) string {
switch a := arg.(type) {
case Reg:
return plan9Reg[a]
case Rel:
if pc == 0 {
break
}
// If the absolute address is the start of a symbol, use the name.
// Otherwise use the raw address, so that things like relative
// jumps show up as JMP 0x123 instead of JMP f+10(SB).
// It is usually easier to search for 0x123 than to do the mental
// arithmetic to find f+10.
addr := pc + uint64(inst.Len) + uint64(a)
if s, base := symname(addr); s != "" && addr == base {
return fmt.Sprintf("%s(SB)", s)
}
return fmt.Sprintf("%#x", addr)
case Imm:
if s, base := symname(uint64(a)); s != "" {
suffix := ""
if uint64(a) != base {
suffix = fmt.Sprintf("%+d", uint64(a)-base)
}
return fmt.Sprintf("$%s%s(SB)", s, suffix)
}
if inst.Mode == 32 {
return fmt.Sprintf("$%#x", uint32(a))
}
if Imm(int32(a)) == a {
return fmt.Sprintf("$%#x", int64(a))
}
return fmt.Sprintf("$%#x", uint64(a))
case Mem:
if s, disp := memArgToSymbol(a, pc, inst.Len, symname); s != "" {
suffix := ""
if disp != 0 {
suffix = fmt.Sprintf("%+d", disp)
}
return fmt.Sprintf("%s%s(SB)", s, suffix)
}
s := ""
if a.Segment != 0 {
s += fmt.Sprintf("%s:", plan9Reg[a.Segment])
}
if a.Disp != 0 {
s += fmt.Sprintf("%#x", a.Disp)
} else {
s += "0"
}
if a.Base != 0 {
s += fmt.Sprintf("(%s)", plan9Reg[a.Base])
}
if a.Index != 0 && a.Scale != 0 {
s += fmt.Sprintf("(%s*%d)", plan9Reg[a.Index], a.Scale)
}
return s
}
return arg.String()
}
func memArgToSymbol(a Mem, pc uint64, instrLen int, symname SymLookup) (string, int64) {
if a.Segment != 0 || a.Disp == 0 || a.Index != 0 || a.Scale != 0 {
return "", 0
}
var disp uint64
switch a.Base {
case IP, EIP, RIP:
disp = uint64(a.Disp + int64(pc) + int64(instrLen))
case 0:
disp = uint64(a.Disp)
default:
return "", 0
}
s, base := symname(disp)
return s, int64(disp) - int64(base)
}
var plan9Suffix = [maxOp + 1]bool{
ADC: true,
ADD: true,
AND: true,
BSF: true,
BSR: true,
BT: true,
BTC: true,
BTR: true,
BTS: true,
CMP: true,
CMPXCHG: true,
CVTSI2SD: true,
CVTSI2SS: true,
CVTSD2SI: true,
CVTSS2SI: true,
CVTTSD2SI: true,
CVTTSS2SI: true,
DEC: true,
DIV: true,
FLDENV: true,
FRSTOR: true,
IDIV: true,
IMUL: true,
IN: true,
INC: true,
LEA: true,
MOV: true,
MOVNTI: true,
MUL: true,
NEG: true,
NOP: true,
NOT: true,
OR: true,
OUT: true,
POP: true,
POPA: true,
PUSH: true,
PUSHA: true,
RCL: true,
RCR: true,
ROL: true,
ROR: true,
SAR: true,
SBB: true,
SHL: true,
SHLD: true,
SHR: true,
SHRD: true,
SUB: true,
TEST: true,
XADD: true,
XCHG: true,
XOR: true,
}
var plan9Reg = [...]string{
AL: "AL",
CL: "CL",
BL: "BL",
DL: "DL",
AH: "AH",
CH: "CH",
BH: "BH",
DH: "DH",
SPB: "SP",
BPB: "BP",
SIB: "SI",
DIB: "DI",
R8B: "R8",
R9B: "R9",
R10B: "R10",
R11B: "R11",
R12B: "R12",
R13B: "R13",
R14B: "R14",
R15B: "R15",
AX: "AX",
CX: "CX",
BX: "BX",
DX: "DX",
SP: "SP",
BP: "BP",
SI: "SI",
DI: "DI",
R8W: "R8",
R9W: "R9",
R10W: "R10",
R11W: "R11",
R12W: "R12",
R13W: "R13",
R14W: "R14",
R15W: "R15",
EAX: "AX",
ECX: "CX",
EDX: "DX",
EBX: "BX",
ESP: "SP",
EBP: "BP",
ESI: "SI",
EDI: "DI",
R8L: "R8",
R9L: "R9",
R10L: "R10",
R11L: "R11",
R12L: "R12",
R13L: "R13",
R14L: "R14",
R15L: "R15",
RAX: "AX",
RCX: "CX",
RDX: "DX",
RBX: "BX",
RSP: "SP",
RBP: "BP",
RSI: "SI",
RDI: "DI",
R8: "R8",
R9: "R9",
R10: "R10",
R11: "R11",
R12: "R12",
R13: "R13",
R14: "R14",
R15: "R15",
IP: "IP",
EIP: "IP",
RIP: "IP",
F0: "F0",
F1: "F1",
F2: "F2",
F3: "F3",
F4: "F4",
F5: "F5",
F6: "F6",
F7: "F7",
M0: "M0",
M1: "M1",
M2: "M2",
M3: "M3",
M4: "M4",
M5: "M5",
M6: "M6",
M7: "M7",
X0: "X0",
X1: "X1",
X2: "X2",
X3: "X3",
X4: "X4",
X5: "X5",
X6: "X6",
X7: "X7",
X8: "X8",
X9: "X9",
X10: "X10",
X11: "X11",
X12: "X12",
X13: "X13",
X14: "X14",
X15: "X15",
CS: "CS",
SS: "SS",
DS: "DS",
ES: "ES",
FS: "FS",
GS: "GS",
GDTR: "GDTR",
IDTR: "IDTR",
LDTR: "LDTR",
MSW: "MSW",
TASK: "TASK",
CR0: "CR0",
CR1: "CR1",
CR2: "CR2",
CR3: "CR3",
CR4: "CR4",
CR5: "CR5",
CR6: "CR6",
CR7: "CR7",
CR8: "CR8",
CR9: "CR9",
CR10: "CR10",
CR11: "CR11",
CR12: "CR12",
CR13: "CR13",
CR14: "CR14",
CR15: "CR15",
DR0: "DR0",
DR1: "DR1",
DR2: "DR2",
DR3: "DR3",
DR4: "DR4",
DR5: "DR5",
DR6: "DR6",
DR7: "DR7",
DR8: "DR8",
DR9: "DR9",
DR10: "DR10",
DR11: "DR11",
DR12: "DR12",
DR13: "DR13",
DR14: "DR14",
DR15: "DR15",
TR0: "TR0",
TR1: "TR1",
TR2: "TR2",
TR3: "TR3",
TR4: "TR4",
TR5: "TR5",
TR6: "TR6",
TR7: "TR7",
}

54
vendor/golang.org/x/arch/x86/x86asm/plan9x_test.go generated vendored Normal file
View File

@ -0,0 +1,54 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"strings"
"testing"
)
func TestPlan932Manual(t *testing.T) { testPlan932(t, hexCases(t, plan9ManualTests)) }
func TestPlan932Testdata(t *testing.T) { testPlan932(t, concat(basicPrefixes, testdataCases(t))) }
func TestPlan932ModRM(t *testing.T) { testPlan932(t, concat(basicPrefixes, enumModRM)) }
func TestPlan932OneByte(t *testing.T) { testBasic(t, testPlan932) }
func TestPlan9320F(t *testing.T) { testBasic(t, testPlan932, 0x0F) }
func TestPlan9320F38(t *testing.T) { testBasic(t, testPlan932, 0x0F, 0x38) }
func TestPlan9320F3A(t *testing.T) { testBasic(t, testPlan932, 0x0F, 0x3A) }
func TestPlan932Prefix(t *testing.T) { testPrefix(t, testPlan932) }
func TestPlan964Manual(t *testing.T) { testPlan964(t, hexCases(t, plan9ManualTests)) }
func TestPlan964Testdata(t *testing.T) { testPlan964(t, concat(basicPrefixes, testdataCases(t))) }
func TestPlan964ModRM(t *testing.T) { testPlan964(t, concat(basicPrefixes, enumModRM)) }
func TestPlan964OneByte(t *testing.T) { testBasic(t, testPlan964) }
func TestPlan9640F(t *testing.T) { testBasic(t, testPlan964, 0x0F) }
func TestPlan9640F38(t *testing.T) { testBasic(t, testPlan964, 0x0F, 0x38) }
func TestPlan9640F3A(t *testing.T) { testBasic(t, testPlan964, 0x0F, 0x3A) }
func TestPlan964Prefix(t *testing.T) { testPrefix(t, testPlan964) }
func TestPlan964REXTestdata(t *testing.T) {
testPlan964(t, filter(concat3(basicPrefixes, rexPrefixes, testdataCases(t)), isValidREX))
}
func TestPlan964REXModRM(t *testing.T) { testPlan964(t, concat3(basicPrefixes, rexPrefixes, enumModRM)) }
func TestPlan964REXOneByte(t *testing.T) { testBasicREX(t, testPlan964) }
func TestPlan964REX0F(t *testing.T) { testBasicREX(t, testPlan964, 0x0F) }
func TestPlan964REX0F38(t *testing.T) { testBasicREX(t, testPlan964, 0x0F, 0x38) }
func TestPlan964REX0F3A(t *testing.T) { testBasicREX(t, testPlan964, 0x0F, 0x3A) }
func TestPlan964REXPrefix(t *testing.T) { testPrefixREX(t, testPlan964) }
// plan9ManualTests holds test cases that will be run by TestPlan9Manual32 and TestPlan9Manual64.
// If you are debugging a few cases that turned up in a longer run, it can be useful
// to list them here and then use -run=Plan9Manual, particularly with tracing enabled.
var plan9ManualTests = `
`
// allowedMismatchPlan9 reports whether the mismatch between text and dec
// should be allowed by the test.
func allowedMismatchPlan9(text string, size int, inst *Inst, dec ExtInst) bool {
return false
}
// Instructions known to us but not to plan9.
var plan9Unsupported = strings.Fields(`
`)

9912
vendor/golang.org/x/arch/x86/x86asm/tables.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

12
vendor/golang.org/x/arch/x86/x86asm/testdata/Makefile generated vendored Normal file
View File

@ -0,0 +1,12 @@
libmach8db: libmach8db.c
9c libmach8db.c && 9l -o libmach8db libmach8db.o; rm libmach8db.o
newdecode.txt:
cd ..; go test -cover -run 'Objdump.*32' -v -timeout 10h -printtests 2>&1 | tee log
cd ..; go test -cover -run 'Objdump.*64' -v -timeout 10h -printtests 2>&1 | tee -a log
cd ..; go test -cover -run 'Xed.*32' -v -timeout 10h -printtests 2>&1 | tee -a log
cd ..; go test -cover -run 'Xed.*64' -v -timeout 10h -printtests 2>&1 | tee -a log
cd ..; go test -cover -run 'Plan9.*32' -v -timeout 10h -printtests 2>&1 | tee -a log
cd ..; go test -cover -run 'Plan9.*64' -v -timeout 10h -printtests 2>&1 | tee -a log
egrep ' (gnu|intel|plan9) ' ../log |sort >newdecode.txt

6781
vendor/golang.org/x/arch/x86/x86asm/testdata/decode.txt generated vendored Normal file

File diff suppressed because it is too large Load Diff

2075
vendor/golang.org/x/arch/x86/x86asm/testdata/libmach8db.c generated vendored Normal file

File diff suppressed because it is too large Load Diff

211
vendor/golang.org/x/arch/x86/x86asm/xed_test.go generated vendored Normal file
View File

@ -0,0 +1,211 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x86asm
import (
"bytes"
"strings"
"testing"
)
func TestXed32Manual(t *testing.T) { testXed32(t, hexCases(t, xedManualTests)) }
func TestXed32Testdata(t *testing.T) { testXed32(t, concat(basicPrefixes, testdataCases(t))) }
func TestXed32ModRM(t *testing.T) { testXed32(t, concat(basicPrefixes, enumModRM)) }
func TestXed32OneByte(t *testing.T) { testBasic(t, testXed32) }
func TestXed320F(t *testing.T) { testBasic(t, testXed32, 0x0F) }
func TestXed320F38(t *testing.T) { testBasic(t, testXed32, 0x0F, 0x38) }
func TestXed320F3A(t *testing.T) { testBasic(t, testXed32, 0x0F, 0x3A) }
func TestXed32Prefix(t *testing.T) { testPrefix(t, testXed32) }
func TestXed64Manual(t *testing.T) { testXed64(t, hexCases(t, xedManualTests)) }
func TestXed64Testdata(t *testing.T) { testXed64(t, concat(basicPrefixes, testdataCases(t))) }
func TestXed64ModRM(t *testing.T) { testXed64(t, concat(basicPrefixes, enumModRM)) }
func TestXed64OneByte(t *testing.T) { testBasic(t, testXed64) }
func TestXed640F(t *testing.T) { testBasic(t, testXed64, 0x0F) }
func TestXed640F38(t *testing.T) { testBasic(t, testXed64, 0x0F, 0x38) }
func TestXed640F3A(t *testing.T) { testBasic(t, testXed64, 0x0F, 0x3A) }
func TestXed64Prefix(t *testing.T) { testPrefix(t, testXed64) }
func TestXed64REXTestdata(t *testing.T) {
testXed64(t, filter(concat3(basicPrefixes, rexPrefixes, testdataCases(t)), isValidREX))
}
func TestXed64REXModRM(t *testing.T) { testXed64(t, concat3(basicPrefixes, rexPrefixes, enumModRM)) }
func TestXed64REXOneByte(t *testing.T) { testBasicREX(t, testXed64) }
func TestXed64REX0F(t *testing.T) { testBasicREX(t, testXed64, 0x0F) }
func TestXed64REX0F38(t *testing.T) { testBasicREX(t, testXed64, 0x0F, 0x38) }
func TestXed64REX0F3A(t *testing.T) { testBasicREX(t, testXed64, 0x0F, 0x3A) }
func TestXed64REXPrefix(t *testing.T) { testPrefixREX(t, testXed64) }
// xedManualTests holds test cases that will be run by TestXedManual32 and TestXedManual64.
// If you are debugging a few cases that turned up in a longer run, it can be useful
// to list them here and then use -run=XedManual, particularly with tracing enabled.
var xedManualTests = `
6690
`
// allowedMismatchXed reports whether the mismatch between text and dec
// should be allowed by the test.
func allowedMismatchXed(text string, size int, inst *Inst, dec ExtInst) bool {
if (contains(text, "error:") || isPrefix(text) && size == 1) && contains(dec.text, "GENERAL_ERROR", "INSTR_TOO_LONG", "BAD_LOCK_PREFIX") {
return true
}
if contains(dec.text, "BAD_LOCK_PREFIX") && countExactPrefix(inst, PrefixLOCK|PrefixInvalid) > 0 {
return true
}
if contains(dec.text, "BAD_LOCK_PREFIX", "GENERAL_ERROR") && countExactPrefix(inst, PrefixLOCK|PrefixImplicit) > 0 {
return true
}
if text == "lock" && size == 1 && contains(dec.text, "BAD_LOCK_PREFIX") {
return true
}
// Instructions not known to us.
if (contains(text, "error:") || isPrefix(text) && size == 1) && contains(dec.text, unsupported...) {
return true
}
// Instructions not known to xed.
if contains(text, xedUnsupported...) && contains(dec.text, "ERROR") {
return true
}
if (contains(text, "error:") || isPrefix(text) && size == 1) && contains(dec.text, "shl ") && (inst.Opcode>>16)&0xEC38 == 0xC030 {
return true
}
// 82 11 22: xed says 'adc byte ptr [ecx], 0x22' but there is no justification in the manuals for that.
// C0 30 11: xed says 'shl byte ptr [eax], 0x11' but there is no justification in the manuals for that.
// F6 08 11: xed says 'test byte ptr [eax], 0x11' but there is no justification in the manuals for that.
if (contains(text, "error:") || isPrefix(text) && size == 1) && hasByte(dec.enc[:dec.nenc], 0x82, 0xC0, 0xC1, 0xD0, 0xD1, 0xD2, 0xD3, 0xF6, 0xF7) {
return true
}
// F3 11 22 and many others: xed allows and drops misused rep/repn prefix.
if (text == "rep" && dec.enc[0] == 0xF3 || (text == "repn" || text == "repne") && dec.enc[0] == 0xF2) && (!contains(dec.text, "ins", "outs", "movs", "lods", "cmps", "scas") || contains(dec.text, "xmm")) {
return true
}
// 0F C7 30: xed says vmptrld qword ptr [eax]; we say rdrand eax.
// TODO(rsc): Fix, since we are probably wrong, but we don't have vmptrld in the manual.
if contains(text, "rdrand") && contains(dec.text, "vmptrld", "vmxon", "vmclear") {
return true
}
// F3 0F AE 00: we say 'rdfsbase dword ptr [eax]' but RDFSBASE needs a register.
// Also, this is a 64-bit only instruction.
// TODO(rsc): Fix to reject this encoding.
if contains(text, "rdfsbase", "rdgsbase", "wrfsbase", "wrgsbase") && contains(dec.text, "ERROR") {
return true
}
// 0F 01 F8: we say swapgs but that's only valid in 64-bit mode.
// TODO(rsc): Fix.
if contains(text, "swapgs") {
return true
}
// 0F 24 11: 'mov ecx, tr2' except there is no TR2.
// Or maybe the MOV to TR registers doesn't use RMF.
if contains(text, "cr1", "cr5", "cr6", "cr7", "tr0", "tr1", "tr2", "tr3", "tr4", "tr5", "tr6", "tr7") && contains(dec.text, "ERROR") {
return true
}
// 0F 19 11, 0F 1C 11, 0F 1D 11, 0F 1E 11, 0F 1F 11: xed says nop,
// but the Intel manuals say that the only NOP there is 0F 1F /0.
// Perhaps xed is reporting an older encoding.
if (contains(text, "error:") || isPrefix(text) && size == 1) && contains(dec.text, "nop ") && (inst.Opcode>>8)&0xFFFF38 != 0x0F1F00 {
return true
}
// 66 0F AE 38: clflushopt but we only know clflush
if contains(text, "clflush") && contains(dec.text, "clflushopt") {
return true
}
// 0F 20 04 11: MOV SP, CR0 but has mod!=3 despite register argument.
// (This encoding ignores the mod bits.) The decoder sees the non-register
// mod and reads farther ahead to decode the memory reference that
// isn't really there, causing the size to be too large.
// TODO(rsc): Fix.
if text == dec.text && size > dec.nenc && contains(text, " cr", " dr", " tr") {
return true
}
// 0F AE E9: xed says lfence, which is wrong (only 0F AE E8 is lfence). And so on.
if contains(dec.text, "fence") && hasByte(dec.enc[:dec.nenc], 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF) {
return true
}
// DD C9, DF C9: xed says 'fxch st0, st1' but that instruction is D9 C9.
if (contains(text, "error:") || isPrefix(text) && size == 1) && contains(dec.text, "fxch ") && hasByte(dec.enc[:dec.nenc], 0xDD, 0xDF) {
return true
}
// DC D4: xed says 'fcom st0, st4' but that instruction is D8 D4.
if (contains(text, "error:") || isPrefix(text) && size == 1) && contains(dec.text, "fcom ") && hasByte(dec.enc[:dec.nenc], 0xD8, 0xDC) {
return true
}
// DE D4: xed says 'fcomp st0, st4' but that instruction is D8 D4.
if (contains(text, "error:") || isPrefix(text) && size == 1) && contains(dec.text, "fcomp ") && hasByte(dec.enc[:dec.nenc], 0xDC, 0xDE) {
return true
}
// DF D4: xed says 'fstp st4, st0' but that instruction is DD D4.
if (contains(text, "error:") || isPrefix(text) && size == 1) && contains(dec.text, "fstp ") && hasByte(dec.enc[:dec.nenc], 0xDF) {
return true
}
return false
}
func countExactPrefix(inst *Inst, target Prefix) int {
n := 0
for _, p := range inst.Prefix {
if p == target {
n++
}
}
return n
}
func hasByte(src []byte, target ...byte) bool {
for _, b := range target {
if bytes.IndexByte(src, b) >= 0 {
return true
}
}
return false
}
// Instructions known to us but not to xed.
var xedUnsupported = strings.Fields(`
xrstor
xsave
xsave
ud1
xgetbv
xsetbv
fxsave
fxrstor
clflush
lfence
mfence
sfence
rsqrtps
rcpps
emms
ldmxcsr
stmxcsr
movhpd
movnti
rdrand
movbe
movlpd
sysret
`)

205
vendor/golang.org/x/arch/x86/x86asm/xedext_test.go generated vendored Normal file
View File

@ -0,0 +1,205 @@
package x86asm
import (
"bytes"
"fmt"
"io"
"log"
"os"
"strconv"
"strings"
"testing"
)
// xed binary from Intel sde-external-6.22.0-2014-03-06.
const xedPath = "/Users/rsc/bin/xed"
func testXedArch(t *testing.T, arch int, generate func(func([]byte))) {
if testing.Short() {
t.Skip("skipping xed test in short mode")
}
if _, err := os.Stat(xedPath); err != nil {
t.Skip(err)
}
testExtDis(t, "intel", arch, xed, generate, allowedMismatchXed)
}
func testXed32(t *testing.T, generate func(func([]byte))) {
testXedArch(t, 32, generate)
}
func testXed64(t *testing.T, generate func(func([]byte))) {
testXedArch(t, 64, generate)
}
func xed(ext *ExtDis) error {
b, err := ext.Run(xedPath, fmt.Sprintf("-%d", ext.Arch), "-n", "1G", "-ir", ext.File.Name())
if err != nil {
return err
}
nmatch := 0
next := uint32(start)
var (
addr uint32
encbuf [32]byte
enc []byte
text string
)
var xedEnd = []byte("# end of text section")
var xedEnd1 = []byte("# Errors")
eof := false
for {
line, err := b.ReadSlice('\n')
if err != nil {
if err == io.EOF {
break
}
return fmt.Errorf("reading objdump output: %v", err)
}
if debug {
os.Stdout.Write(line)
}
if bytes.HasPrefix(line, xedEnd) || bytes.HasPrefix(line, xedEnd1) {
eof = true
}
if eof {
continue
}
nmatch++
addr, enc, text = parseLineXed(line, encbuf[:0])
if addr > next {
return fmt.Errorf("address out of sync expected <= %#x at %q in:\n%s", next, line, line)
}
if addr < next {
continue
}
switch text {
case "repz":
text = "rep"
case "repnz":
text = "repn"
default:
text = strings.Replace(text, "repz ", "rep ", -1)
text = strings.Replace(text, "repnz ", "repn ", -1)
}
if m := pcrelw.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], int16(uint32(targ)-uint32(uint16(addr))-uint32(len(enc))))
}
if m := pcrel.FindStringSubmatch(text); m != nil {
targ, _ := strconv.ParseUint(m[2], 16, 64)
text = fmt.Sprintf("%s .%+#x", m[1], int32(uint32(targ)-addr-uint32(len(enc))))
}
ext.Dec <- ExtInst{addr, encbuf, len(enc), text}
encbuf = [32]byte{}
enc = nil
next += 32
}
if next != start+uint32(ext.Size) {
return fmt.Errorf("not enough results found [%d %d]", next, start+ext.Size)
}
if err := ext.Wait(); err != nil {
return fmt.Errorf("exec: %v", err)
}
return nil
}
var (
xedInRaw = []byte("In raw...")
xedDots = []byte("...")
xdis = []byte("XDIS ")
xedError = []byte("ERROR: ")
xedNoDecode = []byte("Could not decode at offset: 0x")
)
func parseLineXed(line []byte, encstart []byte) (addr uint32, enc []byte, text string) {
oline := line
if bytes.HasPrefix(line, xedInRaw) || bytes.HasPrefix(line, xedDots) {
return 0, nil, ""
}
if bytes.HasPrefix(line, xedError) {
i := bytes.IndexByte(line[len(xedError):], ' ')
if i < 0 {
log.Fatalf("cannot parse error: %q", oline)
}
errstr := string(line[len(xedError):])
i = bytes.Index(line, xedNoDecode)
if i < 0 {
log.Fatalf("cannot parse error: %q", oline)
}
i += len(xedNoDecode)
j := bytes.IndexByte(line[i:], ' ')
if j < 0 {
log.Fatalf("cannot parse error: %q", oline)
}
x, err := strconv.ParseUint(string(trimSpace(line[i:i+j])), 16, 32)
if err != nil {
log.Fatalf("cannot parse disassembly: %q", oline)
}
addr = uint32(x)
return addr, nil, errstr
}
if !bytes.HasPrefix(line, xdis) {
log.Fatalf("cannot parse disassembly: %q", oline)
}
i := bytes.IndexByte(line, ':')
if i < 0 {
log.Fatalf("cannot parse disassembly: %q", oline)
}
x, err := strconv.ParseUint(string(trimSpace(line[len(xdis):i])), 16, 32)
if err != nil {
log.Fatalf("cannot parse disassembly: %q", oline)
}
addr = uint32(x)
// spaces
i++
for i < len(line) && line[i] == ' ' {
i++
}
// instruction class, spaces
for i < len(line) && line[i] != ' ' {
i++
}
for i < len(line) && line[i] == ' ' {
i++
}
// instruction set, spaces
for i < len(line) && line[i] != ' ' {
i++
}
for i < len(line) && line[i] == ' ' {
i++
}
// hex
hexStart := i
for i < len(line) && line[i] != ' ' {
i++
}
hexEnd := i
for i < len(line) && line[i] == ' ' {
i++
}
// text
textStart := i
for i < len(line) && line[i] != '\n' {
i++
}
textEnd := i
enc, ok := parseHex(line[hexStart:hexEnd], encstart)
if !ok {
log.Fatalf("cannot parse disassembly: %q", oline)
}
return addr, enc, string(fixSpace(line[textStart:textEnd]))
}

38
vendor/vendor.json vendored Normal file
View File

@ -0,0 +1,38 @@
{
"comment": "",
"ignore": "",
"package": [
{
"checksumSHA1": "S5YH6+pAjKFAFidprYjhl9OpWWE=",
"path": "github.com/brahma-adshonor/gohook",
"revision": "cfeffdca9b9b432fd22853f9dea6bc0957c64bcc",
"revisionTime": "2020-05-11T02:27:46Z"
},
{
"checksumSHA1": "9+Rs09zfc0ajMWczTSADvj5oUXg=",
"path": "github.com/go-logr/logr/testing",
"revision": "ff9374eda70c55592ad54d87839c59db58257d2f",
"revisionTime": "2020-09-04T21:25:47Z"
},
{
"checksumSHA1": "LuFv4/jlrmFNnDb/5SCSEPAM9vU=",
"origin": "github.com/stretchr/testify/vendor/github.com/pmezard/go-difflib/difflib",
"path": "github.com/pmezard/go-difflib/difflib",
"revision": "221dbe5ed46703ee255b1da0dec05086f5035f62",
"revisionTime": "2019-05-17T17:51:56Z"
},
{
"checksumSHA1": "OHK0GAgeySXMMiqH/84b80WoPNU=",
"path": "github.com/stretchr/testify/assert",
"revision": "221dbe5ed46703ee255b1da0dec05086f5035f62",
"revisionTime": "2019-05-17T17:51:56Z"
},
{
"checksumSHA1": "AuAGph7pQZKYtiuSB6ZVroWxlY4=",
"path": "golang.org/x/arch/x86/x86asm",
"revision": "b19915210f009e139b20abfd6a6052c7acc1f445",
"revisionTime": "2020-08-18T21:29:11Z"
}
],
"rootPath": "github.com/fluid-cloudnative/fluid"
}