1
0

Added symbol tests (#15)

This commit is contained in:
Patrick Gaskin
2020-05-07 17:37:13 -04:00
parent f860801a38
commit 4be17f085f
4 changed files with 318 additions and 0 deletions

15
.github/workflows/test.yaml vendored Normal file
View File

@@ -0,0 +1,15 @@
name: Test
on: [push, pull_request]
jobs:
build:
name: NickelMenu / Symbols
runs-on: ubuntu-latest
container: docker.io/golang:1.14
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Build
run: cd test/syms && go build -o ../../test.syms .
- name: Run
run: cd src && ../test.syms

8
test/syms/go.mod Normal file
View File

@@ -0,0 +1,8 @@
module github.com/geek1011/NickelMenu/test/syms
go 1.14
require (
github.com/geek1011/kobopatch v0.15.0
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8
)

19
test/syms/go.sum Normal file
View File

@@ -0,0 +1,19 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/geek1011/czlib v0.0.3 h1:Cp9hWIbzdvyj/QmpciCghWLbRpRaMpJS7wL7bTxwgUM=
github.com/geek1011/czlib v0.0.3/go.mod h1:iw913x/pjDqhfoak/AU3XWgycIcGL9H6kjgPP2YqzQM=
github.com/geek1011/kobopatch v0.15.0 h1:xIxJPHNoiwbD7VkwBmZRzQAVHfjPqxuEvFNtDfKGPiE=
github.com/geek1011/kobopatch v0.15.0/go.mod h1:4/PuLBLcPPNO20/gBRJh9VhdVyNZmuwCvo8wffcVWiQ=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6 h1:UDMh68UUwekSh5iP2OMhRRZJiiBccgV7axzUG8vi56c=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
rsc.io/arm v0.0.0-20150420010332-9c32f2193064 h1:bBbas3KhLwE6f59Z9lUipY23xUX9qrvyLBdQzzV2Tko=
rsc.io/arm v0.0.0-20150420010332-9c32f2193064/go.mod h1:MVYPdlFruujBlzEY3x2Q3XBk7XLdYRNZ7zDbrzYFO7w=

276
test/syms/main.go Normal file
View File

@@ -0,0 +1,276 @@
package main
import (
"archive/tar"
"bufio"
"bytes"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"github.com/geek1011/kobopatch/patchlib"
"github.com/xi2/xz"
)
func main() {
sc, err := FindSymChecks(".")
if err != nil {
fmt.Fprintf(os.Stderr, "[FTL] find symbol checks: %v\n", err)
os.Exit(1)
return
}
versions := []string{
"4.6.9960", "4.6.9995", "4.7.10075", "4.7.10364", "4.7.10413",
"4.8.10956", "4.8.11073", "4.8.11090", "4.9.11311", "4.9.11314",
"4.10.11591", "4.10.11655", "4.11.11911", "4.11.11976", "4.11.11980",
"4.11.11982", "4.11.12019", "4.12.12111", "4.13.12638", "4.14.12777",
"4.15.12920", "4.16.13162", "4.17.13651", "4.17.13694", "4.18.13737",
"4.19.14123", "4.20.14601", "4.20.14617", "4.20.14622",
}
checks := map[string]map[string][]SymCheck{}
for _, c := range sc {
var sm, em int
for _, version := range versions {
if c.StartVersion == "*" || strings.HasPrefix(version+".", c.StartVersion+".") {
sm++
}
if c.EndVersion == "*" || strings.HasPrefix(version+".", c.EndVersion+".") {
em++
}
if versioncmp(c.StartVersion, version) <= 0 && versioncmp(version, c.EndVersion) >= 0 {
if _, ok := checks[version]; !ok {
checks[version] = map[string][]SymCheck{}
}
checks[version][c.Library] = append(checks[version][c.Library], c)
}
}
if sm == 0 {
fmt.Printf("[WRN] %s: no exact match for the base version in specifier %#v\n", c.File, c.StartVersion)
}
if em == 0 {
fmt.Printf("[WRN] %s: no exact match for the base version in specifier %#v\n", c.File, c.EndVersion)
}
}
var checkVersions []string
for version := range checks {
checkVersions = append(checkVersions, version)
}
sort.Slice(checkVersions, func(i, j int) bool {
return versioncmp(checkVersions[i], checkVersions[j]) == -1
})
var errs []error
gherrs := map[string][]string{}
for _, version := range checkVersions {
var checkLibs []string
for lib := range checks[version] {
checkLibs = append(checkLibs, lib)
}
sort.Strings(checkLibs)
for _, lib := range checkLibs {
fmt.Printf("[INF] checking %s@%s\n", lib, version)
pt, err := GetPatcher(version, lib)
if err != nil {
fmt.Fprintf(os.Stderr, "[FTL] get patcher: %v\n", err)
os.Exit(1)
return
} else if pt == nil {
fmt.Printf("[WRN] no data available, skipping\n")
continue
}
_, err = pt.ExtractDynsyms(true)
if err != nil {
fmt.Fprintf(os.Stderr, "[FTL] extract symbols: %v\n", err)
os.Exit(1)
return
}
for _, check := range checks[version][lib] {
fmt.Printf("[INF] %s:\n checking for one of %+s\n", check.File, check.Symbols)
var f bool
for _, sym := range check.Symbols {
off, err := pt.ResolveSym(sym)
if err != nil {
fmt.Printf(" %s not found\n", sym)
} else {
fmt.Printf(" %s found at %#x\n", sym, off)
f = true
}
}
if !f {
err := fmt.Errorf("%s: one of %+s not found in %s@%s", check.File, check.Symbols, lib, version)
fmt.Printf("[ERR] %v\n", err)
errs = append(errs, err)
spl := strings.Split(check.File, ":")
gherrf := fmt.Sprintf("file=%s,line=%s,col=%s", spl[0], spl[1], spl[2])
gherrs[gherrf] = append(gherrs[gherrf], fmt.Sprintf("one of symbols %+s not found in %s@%s", check.Symbols, lib, version))
}
}
}
}
if len(errs) == 0 {
os.Exit(0)
}
fmt.Printf("[FTL] check failed\n")
for _, err := range errs {
fmt.Printf(" %v\n", err)
}
if os.Getenv("GITHUB_ACTIONS") == "true" {
var ghfs []string
for ghf := range gherrs {
ghfs = append(ghfs, ghf)
}
sort.Strings(ghfs)
for _, ghf := range ghfs {
fmt.Printf("::error %s::%s\n", ghf, strings.Join(gherrs[ghf], "%0A"))
}
}
os.Exit(1)
}
func GetPatcher(version, lib string) (*patchlib.Patcher, error) {
resp, err := http.Get("https://github.com/geek1011/kobopatch-patches/raw/master/testdata/" + version + ".tar.xz")
if err != nil {
return nil, fmt.Errorf("get kobopatch testdata for %#v: %w", version, err)
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusNotFound {
return nil, nil
} else if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("get kobopatch testdata for %#v: response status %s", version, resp.Status)
}
zr, err := xz.NewReader(resp.Body, 0)
if err != nil {
return nil, fmt.Errorf("read kobopatch testdata: %w", err)
}
tr := tar.NewReader(zr)
for {
th, err := tr.Next()
if err != nil {
if err == io.EOF {
return nil, fmt.Errorf("read kobopatch testdata: file %#v not found", lib)
}
return nil, fmt.Errorf("read kobopatch testdata: %w", err)
}
if filepath.Clean(th.Name) == filepath.Clean(lib) {
break
}
}
buf, err := ioutil.ReadAll(tr)
if err != nil {
return nil, fmt.Errorf("read kobopatch testdata: %w", err)
}
return patchlib.NewPatcher(buf), nil
}
type SymCheck struct {
File string
Library string
StartVersion string
EndVersion string
Symbols []string // or
}
func FindSymChecks(dir string) ([]SymCheck, error) {
var checks []SymCheck
if err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
var m bool
for _, ext := range []string{".c", ".cc", ".cpp", ".h"} {
if filepath.Ext(path) == ext {
m = true
break
}
}
if !m {
return nil
}
f, err := os.OpenFile(path, os.O_RDONLY, 0)
if err != nil {
return fmt.Errorf("open %#v: %w", path, err)
}
defer f.Close()
sc := bufio.NewScanner(f)
var line int
for sc.Scan() {
line++
col := bytes.Index(sc.Bytes(), []byte("//libnickel"))
if col == -1 {
continue
}
args := strings.Fields(string(bytes.TrimSpace(sc.Bytes()[col+len("//libnickel"):])))
if len(args) < 3 || args[0] == "*" {
return fmt.Errorf("parse %#v: line %d, col %d: expected comment to be in the format '//libnickel <start_version> <end_version|*> <sym>...'", path, line, col+1)
}
checks = append(checks, SymCheck{
File: fmt.Sprintf("%s:%d:%d", path, line, col+1),
Library: "libnickel.so.1.0.0",
StartVersion: args[0],
EndVersion: args[1],
Symbols: args[2:],
})
}
if err := sc.Err(); err != nil {
return fmt.Errorf("read %#v: %w", path, err)
}
return nil
}); err != nil {
return nil, err
}
return checks, nil
}
func versioncmp(a, b string) int {
if a == "*" || b == "*" {
return 0
}
aspl, bspl := splint(a), splint(b)
mlen := len(aspl)
if len(bspl) > mlen {
mlen = len(bspl)
}
for i := 0; i < mlen; i++ {
switch {
case i == len(bspl):
return 1
case i == len(aspl):
return -1
case aspl[i] > bspl[i]:
return 1
case bspl[i] > aspl[i]:
return -1
}
}
return 0
}
func splint(str string) []int64 {
spl := strings.Split(str, ".")
ints := make([]int64, len(spl))
for i, p := range spl {
ints[i], _ = strconv.ParseInt(p, 10, 64)
}
return ints
}