From 5121164cf9098137ff2ee4227c35cefa9572d39b Mon Sep 17 00:00:00 2001 From: Michael McLoughlin Date: Tue, 7 Jan 2020 15:39:12 -0500 Subject: [PATCH] loader for asmdb Updates #99 --- internal/asmdb/doc.go | 2 + internal/asmdb/parse.go | 117 +++++++++++++++++++++++++++++++++++ internal/asmdb/parse_test.go | 13 ++++ internal/asmdb/testdata | 1 + 4 files changed, 133 insertions(+) create mode 100644 internal/asmdb/doc.go create mode 100644 internal/asmdb/parse.go create mode 100644 internal/asmdb/parse_test.go create mode 120000 internal/asmdb/testdata diff --git a/internal/asmdb/doc.go b/internal/asmdb/doc.go new file mode 100644 index 00000000..afdbd62a --- /dev/null +++ b/internal/asmdb/doc.go @@ -0,0 +1,2 @@ +// Package asmdb provides access to the asmjit opcodes database. +package asmdb diff --git a/internal/asmdb/parse.go b/internal/asmdb/parse.go new file mode 100644 index 00000000..6244c195 --- /dev/null +++ b/internal/asmdb/parse.go @@ -0,0 +1,117 @@ +package asmdb + +import ( + "bufio" + "encoding/json" + "io" + "os" + "strings" +) + +// Extension specifies an architecture extension. +type Extension struct { + Name string `json:"name"` +} + +// Attribute is specifies an instruction attribute. +type Attribute struct { + Name string `json:"name"` + Type string `json:"type"` + Doc string `json:"doc"` +} + +// SpecialReg specifies a special register or flag. +type SpecialReg struct { + Name string `json:"name"` + Group string `json:"group"` + Doc string `json:"doc"` +} + +// Shortcut specifies a shorthand for a collection of attributes. +type Shortcut struct { + Name string `json:"name"` + Expand string `json:"expand"` +} + +// Register defines a class of registers. +type Register struct { + Kind string `json:"kind"` + Any string `json:"any"` + Names []string `json:"names"` +} + +// Raw is an asmdb instruction database in its unprocessed form. +type Raw struct { + Architectures []string `json:"architectures"` + Extensions []Extension `json:"extensions"` + Attributes []Attribute `json:"attributes"` + SpecialRegs []SpecialReg `json:"specialRegs"` + Shortcuts []Shortcut `json:"shortcuts"` + Registers map[string]Register `json:"registers"` + Instructions [][]string `json:"instructions"` +} + +// ParseRaw parses the asmdb x86data.js file as a raw unprocessed data structure. +func ParseRaw(r io.Reader) (*Raw, error) { + // Extract the JSON blob from the javascript file. + b, err := extractjson(r) + if err != nil { + return nil, err + } + + // Parse JSON. + db := &Raw{} + if err := json.Unmarshal(b, db); err != nil { + return nil, err + } + + return db, nil +} + +// ParseRawFile parses an asmdb x86data.js file. +func ParseRawFile(filename string) (*Raw, error) { + f, err := os.Open(filename) + if err != nil { + return nil, err + } + defer f.Close() + return ParseRaw(f) +} + +// extractjson extracts the pure JSON part of the x86data.js file. +func extractjson(r io.Reader) ([]byte, error) { + dividers := []string{ + `// ${JSON:BEGIN}`, + `// ${JSON:END}`, + `/*`, + `*/`, + } + s := bufio.NewScanner(r) + var b []byte + take := false + for s.Scan() { + line := s.Text() + + if contains(strings.TrimSpace(line), dividers) { + take = !take + continue + } + + if take { + b = append(b, []byte(line)...) + } + } + if err := s.Err(); err != nil { + return nil, err + } + return b, nil +} + +func contains(needle string, haystack []string) bool { + for _, s := range haystack { + if s == needle { + return true + } + } + return false +} diff --git a/internal/asmdb/parse_test.go b/internal/asmdb/parse_test.go new file mode 100644 index 00000000..e860c000 --- /dev/null +++ b/internal/asmdb/parse_test.go @@ -0,0 +1,13 @@ +package asmdb + +import "testing" + +func TestParseRawFile(t *testing.T) { + db, err := ParseRawFile("testdata/x86data.js") + if err != nil { + t.Fatal(err) + } + if len(db.Instructions) == 0 { + t.FailNow() + } +} diff --git a/internal/asmdb/testdata b/internal/asmdb/testdata new file mode 120000 index 00000000..4909e06e --- /dev/null +++ b/internal/asmdb/testdata @@ -0,0 +1 @@ +../data \ No newline at end of file