@ -0,0 +1,19 @@ | |||
package ebpf | |||
// swapUint32 converts a uint16 to network byte order and back. | |||
func swapUint32(n uint32) uint32 { | |||
return (n&0x000000FF)<<24 | (n&0x0000FF00)<<8 | | |||
(n&0x00FF0000)>>8 | (n&0xFF000000)>>24 | |||
} | |||
// swapUint64 converts a uint16 to network byte order and back. | |||
func swapUint64(n uint64) uint64 { | |||
return ((n & 0x00000000000000FF) << 56) | | |||
((n & 0x000000000000FF00) << 40) | | |||
((n & 0x0000000000FF0000) << 24) | | |||
((n & 0x00000000FF000000) << 8) | | |||
((n & 0x000000FF00000000) >> 8) | | |||
((n & 0x0000FF0000000000) >> 24) | | |||
((n & 0x00FF000000000000) >> 40) | | |||
((n & 0xFF00000000000000) >> 56) | |||
} |
@ -0,0 +1,257 @@ | |||
package main | |||
import ( | |||
"encoding/binary" | |||
"fmt" | |||
"io" | |||
"io/ioutil" | |||
"os" | |||
"strings" | |||
"text/tabwriter" | |||
"maze.io/ebpf" | |||
cli "github.com/urfave/cli/v2" | |||
) | |||
func main() { | |||
app := cli.App{ | |||
Name: "bpfasm", | |||
Usage: "Berkeley Packet Filter assembler/disassembler", | |||
Commands: []*cli.Command{ | |||
&cli.Command{ | |||
Name: "assemble", | |||
Aliases: []string{"a"}, | |||
Usage: "Assemble a BPF ASM file", | |||
ArgsUsage: "<input>", | |||
Action: assemble, | |||
Flags: []cli.Flag{ | |||
&cli.StringFlag{ | |||
Name: "endian", | |||
Aliases: []string{"e"}, | |||
Value: "be", | |||
Usage: "Binary byte order (be, le)", | |||
}, | |||
&cli.StringFlag{ | |||
Name: "format", | |||
Aliases: []string{"f"}, | |||
Value: "bin", | |||
Usage: "Output format (bin, asm, c)", | |||
}, | |||
&cli.StringFlag{ | |||
Name: "output", | |||
Aliases: []string{"o"}, | |||
Usage: "Output file", | |||
DefaultText: "use stdout", | |||
}, | |||
}, | |||
}, | |||
&cli.Command{ | |||
Name: "disassemble", | |||
Aliases: []string{"d"}, | |||
Usage: "Disassemble a BPF binary file", | |||
ArgsUsage: "<input>", | |||
Action: disassemble, | |||
Flags: []cli.Flag{ | |||
&cli.StringFlag{ | |||
Name: "endian", | |||
Aliases: []string{"e"}, | |||
Value: "le", | |||
Usage: "Binary byte order (be, le)", | |||
}, | |||
&cli.StringFlag{ | |||
Name: "format", | |||
Aliases: []string{"f"}, | |||
Value: "asm", | |||
Usage: "Output format (asm, c)", | |||
}, | |||
&cli.StringFlag{ | |||
Name: "output", | |||
Aliases: []string{"o"}, | |||
Usage: "Output file", | |||
DefaultText: "use stdout", | |||
}, | |||
}, | |||
}, | |||
}, | |||
} | |||
app.Run(os.Args) | |||
} | |||
func assemble(ctx *cli.Context) error { | |||
if ctx.NArg() != 1 { | |||
return cli.ShowCommandHelp(ctx, "assemble") | |||
} | |||
var asm assembler | |||
switch ctx.String("format") { | |||
case "bin", "binary": | |||
switch ctx.String("endian") { | |||
case "be": | |||
asm = formatBinary(binary.BigEndian) | |||
case "le": | |||
asm = formatBinary(binary.LittleEndian) | |||
default: | |||
fmt.Fprintf(os.Stderr, "invalid byte order %q\n", ctx.String("endian")) | |||
os.Exit(1) | |||
} | |||
case "asm": | |||
asm = formatAssembly | |||
case "c", "C": | |||
asm = formatC | |||
default: | |||
fmt.Fprintf(os.Stderr, "invalid output format %q\n", ctx.String("format")) | |||
os.Exit(1) | |||
} | |||
sourcePath := ctx.Args().First() | |||
sourceBytes, err := ioutil.ReadFile(sourcePath) | |||
if err != nil { | |||
fmt.Fprintf(os.Stderr, "error reading %s: %v\n", sourcePath, err) | |||
os.Exit(1) | |||
} | |||
program, err := ebpf.Assemble(string(sourceBytes)) | |||
if err != nil { | |||
fmt.Fprintf(os.Stderr, "error assembling %s: %v\n", sourcePath, err) | |||
os.Exit(1) | |||
} | |||
/* | |||
else if err = program.Verify(); err != nil { | |||
fmt.Fprintf(os.Stderr, "error verifying program: %v\n", err) | |||
os.Exit(1) | |||
} | |||
*/ | |||
var output io.WriteCloser | |||
switch outputFile := ctx.String("output"); outputFile { | |||
case "", "-": | |||
if f := ctx.String("format"); f == "bin" || f == "binary" { | |||
fmt.Fprintf(os.Stderr, "writing to %s.bin\n", sourcePath) | |||
if output, err = os.Create(sourcePath + ".bin"); err != nil { | |||
fmt.Fprintf(os.Stderr, "error opening output file: %v\n", err) | |||
os.Exit(1) | |||
} | |||
} else { | |||
output = os.Stdout | |||
} | |||
default: | |||
fmt.Fprintf(os.Stderr, "writing to %s\n", outputFile) | |||
if output, err = os.Create(outputFile); err != nil { | |||
fmt.Fprintf(os.Stderr, "error opening output file: %v\n", err) | |||
os.Exit(1) | |||
} | |||
} | |||
defer output.Close() | |||
return asm(output, program) | |||
} | |||
func disassemble(ctx *cli.Context) error { | |||
if ctx.NArg() != 1 { | |||
return cli.ShowCommandHelp(ctx, "disassemble") | |||
} | |||
var ( | |||
sourcePath = ctx.Args().First() | |||
byteOrder binary.ByteOrder | |||
asm assembler | |||
) | |||
switch ctx.String("endian") { | |||
case "be": | |||
byteOrder = binary.BigEndian | |||
case "le": | |||
byteOrder = binary.LittleEndian | |||
default: | |||
fmt.Fprintf(os.Stderr, "invalid byte order %q\n", ctx.String("endian")) | |||
os.Exit(1) | |||
} | |||
switch ctx.String("format") { | |||
case "asm": | |||
asm = formatAssembly | |||
case "c", "C": | |||
asm = formatC | |||
default: | |||
fmt.Fprintf(os.Stderr, "invalid output format %q\n", ctx.String("format")) | |||
os.Exit(1) | |||
} | |||
sourceFile, err := os.Open(sourcePath) | |||
if err != nil { | |||
fmt.Fprintf(os.Stderr, "error opening %s: %v\n", sourcePath, err) | |||
os.Exit(1) | |||
} | |||
defer sourceFile.Close() | |||
program, err := ebpf.DisassembleReaderOrder(sourceFile, byteOrder) | |||
if err != nil { | |||
fmt.Fprintf(os.Stderr, "error disassembling: %v\n", err) | |||
os.Exit(2) | |||
} | |||
var output io.WriteCloser | |||
switch outputFile := ctx.String("output"); outputFile { | |||
case "", "-": | |||
output = os.Stdout | |||
default: | |||
fmt.Fprintf(os.Stderr, "writing to %s\n", outputFile) | |||
if output, err = os.Create(outputFile); err != nil { | |||
fmt.Fprintf(os.Stderr, "error opening output file: %v\n", err) | |||
os.Exit(1) | |||
} | |||
} | |||
defer output.Close() | |||
return asm(output, program) | |||
} | |||
type assembler func(io.Writer, ebpf.Program) error | |||
func formatBinary(byteOrder binary.ByteOrder) assembler { | |||
return func(w io.Writer, program ebpf.Program) error { | |||
raw, err := program.Assemble() | |||
if err != nil { | |||
return err | |||
} | |||
for _, ins := range raw { | |||
if err := binary.Write(w, byteOrder, ins); err != nil { | |||
return err | |||
} | |||
} | |||
return nil | |||
} | |||
} | |||
func formatAssembly(w io.Writer, program ebpf.Program) error { | |||
t := tabwriter.NewWriter(w, 0, 0, 4, ' ', tabwriter.TabIndent) | |||
for _, ins := range program { | |||
if s, ok := ins.(fmt.Stringer); ok { | |||
fmt.Fprintln(t, strings.Replace(s.String(), " ", "\t", -1)) | |||
} | |||
} | |||
return t.Flush() | |||
} | |||
func formatC(w io.Writer, program ebpf.Program) error { | |||
raw, err := program.Assemble() | |||
if err != nil { | |||
return err | |||
} | |||
l := len(raw) - 1 | |||
for i, ri := range raw { | |||
var comma string | |||
if i < l { | |||
comma = "," | |||
} | |||
if _, err := fmt.Fprintf(w, "{ %#02x, %s, %s, %#04x, %#08x }%s // %s\n", | |||
ri.Op, ri.Dst, ri.Src, ri.Offset, ri.Immediate, comma, ri.Disassemble()); err != nil { | |||
return err | |||
} | |||
} | |||
return nil | |||
} |
@ -0,0 +1,80 @@ | |||
package ebpf | |||
const ( | |||
opMaskClass = 0x07 | |||
opMaskSize = 0x18 | |||
opMaskMode = 0xe0 | |||
opMaskSource = 0x08 | |||
opMaskOperand = 0xf0 | |||
) | |||
// Operand classes. | |||
const ( | |||
opClassLoad Opcode = iota // 0x00 | |||
opClassLoadX // 0x01 | |||
opClassStore // 0x02 | |||
opClassStoreX // 0x03 | |||
opClassALU // 0x04 | |||
opClassJump // 0x05 | |||
_ // 0x06 (reserved for future use) | |||
opClassALU64 // 0x07 | |||
) | |||
// For load and store instructions: | |||
// +------------+--------+------------+ | |||
// | 3 bits | 2 bits | 3 bits | | |||
// | mode | size | insn class | | |||
// +------------+--------+------------+ | |||
// (MSB) (LSB) | |||
// Size modifiers. | |||
const ( | |||
sizeWord Opcode = iota << 3 // 0x00 | |||
sizeHalfWord // 0x08 | |||
sizeByte // 0x10 | |||
sizeDoubleWord // 0x18 | |||
) | |||
// Mode modifiers. | |||
const ( | |||
modeImmediate Opcode = iota << 5 // 0x00 | |||
modeAbsolute // 0x20 | |||
modeIndirect // 0x40 | |||
modeMemory // 0x60 | |||
_ // 0x80 | |||
_ // 0xa0 | |||
modeExclusiveAdd // 0xc0 | |||
) | |||
// For arithmetic (BPF_ALU/BPF_ALU64) and jump (BPF_JMP) instructions: | |||
// +----------------+----+------------+ | |||
// | 4 bits |1 b.| 3 bits | | |||
// | operation code | src| insn class | | |||
// +----------------+----+------------+ | |||
// (MSB) (LSB) | |||
// Source modifiers. | |||
const ( | |||
aluSourceImmediate Opcode = iota << 3 | |||
aluSourceX | |||
jumpSourceImmediate = aluSourceImmediate | |||
jumpSourceX = aluSourceX | |||
) | |||
// Jumps | |||
const ( | |||
jumpOp Opcode = iota << 4 // 0x00 | |||
jumpOpEqual // 0x10 | |||
jumpOpGreater // 0x20 | |||
jumpOpGreaterOrEqual // 0x30 | |||
jumpOpSet // 0x40 | |||
jumpOpNotEqual // 0x50 | |||
jumpOpSignedGreater // 0x60 | |||
jumpOpSignedGreaterOrEqual // 0x70 | |||
jumpOpCall // 0x80 | |||
jumpOpExit // 0x90 | |||
jumpOpLess // 0xa0 | |||
jumpOpLessOrEqual // 0xb0 | |||
jumpOpSignedLess // 0xc0 | |||
jumpOpSignedLessOrEqual // 0xd0 | |||
) |
@ -0,0 +1,190 @@ | |||
package ebpf | |||
import ( | |||
"bytes" | |||
"debug/elf" | |||
"encoding/binary" | |||
"errors" | |||
"fmt" | |||
"log" | |||
) | |||
const ( | |||
maxELFSections = 32 | |||
) | |||
var ( | |||
errELFClass = errors.New("ebpf: invalid ELF class") | |||
errELFByteOrder = errors.New("ebpf: invalid ELF byte order") | |||
errELFVersion = errors.New("ebpf: invalid ELF version") | |||
errELFOSABI = errors.New("ebpf: invalid ELF OS ABI") | |||
errELFRelocatable = errors.New("ebpf: invalid ELF not relocatable") | |||
errTextSection = errors.New("ebpf: text section not found") | |||
) | |||
func loadELF(name string) error { | |||
file, err := elf.Open(name) | |||
if err != nil { | |||
return err | |||
} | |||
if file.Class != elf.ELFCLASS64 { | |||
return errELFClass | |||
} | |||
if file.Data != elf.ELFDATA2LSB { | |||
return errELFByteOrder | |||
} | |||
if file.Version != 1 { | |||
return errELFVersion | |||
} | |||
if file.OSABI != 0 { | |||
return errELFOSABI | |||
} | |||
if file.Type != elf.ET_REL { | |||
return errELFRelocatable | |||
} | |||
if file.Machine != elf.EM_NONE && file.Machine != elf.EM_BPF { | |||
return fmt.Errorf("ebpf: invalid ELF machine, expected BPF, got %d", file.Machine) | |||
} | |||
if l := len(file.Sections); l > maxELFSections { | |||
return fmt.Errorf("ebpf: %d ELF sections exceeds maximum of %d", l, maxELFSections) | |||
} | |||
var textSection *elf.Section | |||
for _, section := range file.Sections { | |||
if section.Type == elf.SHT_PROGBITS && section.Flags == (elf.SHF_ALLOC|elf.SHF_EXECINSTR) { | |||
textSection = section | |||
break | |||
} | |||
} | |||
if textSection == nil { | |||
return errTextSection | |||
} | |||
var textData = make([]byte, textSection.Size) | |||
data, err := textSection.Data() | |||
if err != nil { | |||
return err | |||
} | |||
copy(textData, data) | |||
/* Process each relocation rel */ | |||
for _, rel := range file.Sections { | |||
if rel.Type != elf.SHT_REL { | |||
continue | |||
} | |||
log.Printf("rel: %#+v", rel) | |||
if rel.SectionHeader.Link >= uint32(len(file.Sections)) { | |||
return errors.New("ebpf: bad symbol table section index") | |||
} | |||
symbols, _, err := getSymbols(file, file.Sections[rel.SectionHeader.Link]) | |||
log.Printf("symbols: %#+v", symbols) | |||
rs, err := parseRelocations(file, rel) | |||
if err != nil { | |||
return err | |||
} | |||
log.Printf("relocations: %#+v", rs) | |||
for _, r := range rs { | |||
var ( | |||
_ = r.Info & 0xffffffff // relType | |||
symIndex = r.Info >> 32 | |||
) | |||
if symIndex >= uint64(len(symbols)) { | |||
return errors.New("ebpf: bad symbol index") | |||
} | |||
log.Println("relocate to", symbols[symIndex]) | |||
} | |||
} | |||
return nil | |||
} | |||
func parseRelocations(file *elf.File, rel *elf.Section) (rs []elf.Rel64, err error) { | |||
var b []byte | |||
if b, err = rel.Data(); err != nil { | |||
return | |||
} | |||
if l := len(b); l%16 != 0 { | |||
return nil, fmt.Errorf("length of relocation section size %d is not a multiple of 16", l) | |||
} | |||
buf := bytes.NewBuffer(b) | |||
for buf.Len() > 0 { | |||
var r elf.Rel64 | |||
if err = binary.Read(buf, file.ByteOrder, &r); err != nil { | |||
return | |||
} | |||
rs = append(rs, r) | |||
} | |||
return | |||
} | |||
func getSymbols(file *elf.File, section *elf.Section) ([]elf.Symbol, []byte, error) { | |||
data, err := section.Data() | |||
if err != nil { | |||
return nil, nil, errors.New("cannot load symbol section") | |||
} | |||
symtab := bytes.NewReader(data) | |||
if symtab.Len()%elf.Sym64Size != 0 { | |||
return nil, nil, errors.New("length of symbol section is not a multiple of Sym64Size") | |||
} | |||
strdata, err := stringTable(file, section.Link) | |||
if err != nil { | |||
return nil, nil, errors.New("cannot load string table section") | |||
} | |||
// The first entry is all zeros. | |||
var skip [elf.Sym64Size]byte | |||
symtab.Read(skip[:]) | |||
symbols := make([]elf.Symbol, symtab.Len()/elf.Sym64Size) | |||
i := 0 | |||
var sym elf.Sym64 | |||
for symtab.Len() > 0 { | |||
binary.Read(symtab, file.ByteOrder, &sym) | |||
str, _ := getString(strdata, int(sym.Name)) | |||
symbols[i].Name = str | |||
symbols[i].Info = sym.Info | |||
symbols[i].Other = sym.Other | |||
symbols[i].Section = elf.SectionIndex(sym.Shndx) | |||
symbols[i].Value = sym.Value | |||
symbols[i].Size = sym.Size | |||
i++ | |||
} | |||
return symbols, strdata, nil | |||
} | |||
// stringTable reads and returns the string table given by the | |||
// specified link value. | |||
func stringTable(f *elf.File, link uint32) ([]byte, error) { | |||
if link <= 0 || link >= uint32(len(f.Sections)) { | |||
return nil, errors.New("section has invalid string table link") | |||
} | |||
return f.Sections[link].Data() | |||
} | |||
// getString extracts a string from an ELF string table. | |||
func getString(section []byte, start int) (string, bool) { | |||
if start < 0 || start >= len(section) { | |||
return "", false | |||
} | |||
for end := start; end < len(section); end++ { | |||
if section[end] == 0 { | |||
return string(section[start:end]), true | |||
} | |||
} | |||
return "", false | |||
} | |||
func applyRelocations(dst, relocations []byte) error { | |||
return nil | |||
} |
@ -0,0 +1,12 @@ | |||
package ebpf | |||
import ( | |||
"path/filepath" | |||
"testing" | |||
) | |||
func TestLoadELF(t *testing.T) { | |||
if err := loadELF(filepath.Join("testdata", "vfs-4.19.o")); err != nil { | |||
t.Fatal(err) | |||
} | |||
} |
@ -0,0 +1,233 @@ | |||
package ebpf | |||
import ( | |||
"bytes" | |||
"encoding/binary" | |||
"fmt" | |||
"io" | |||
) | |||
type Instruction interface { | |||
// Assemble assembles the Instruction into a RawInstruction. | |||
Assemble() (RawInstruction, error) | |||
// String returns the assembly. | |||
String() string | |||
} | |||
// A RawInstruction is a raw BPF virtual machine instruction. | |||
type RawInstruction struct { | |||
// Operation to execute. | |||
Op Opcode | |||
Dst, Src Register | |||
Offset uint16 | |||
Immediate uint32 | |||
} | |||
// Assemble implements the Instruction Assemble method. | |||
func (ri RawInstruction) Assemble() (RawInstruction, error) { return ri, nil } | |||
// Disassemble parses ri into an Instruction and returns it. If ri is | |||
// not recognized by this package, ri itself is returned. | |||
func (ri RawInstruction) Disassemble() Instruction { | |||
// log.Printf("op class %#02x", ri.Op&opMaskClass) | |||
switch class := ri.Op & opMaskClass; class { | |||
case opClassLoad, opClassLoadX: | |||
var size uint8 | |||
switch ri.Op & opMaskSize { | |||
case sizeWord: | |||
size = 4 | |||
case sizeDoubleWord: | |||
size = 8 | |||
case sizeHalfWord: | |||
size = 2 | |||
case sizeByte: | |||
size = 1 | |||
default: | |||
return ri | |||
} | |||
switch ri.Op & opMaskMode { | |||
case modeImmediate: // 0x00 | |||
if class == opClassLoad { | |||
if size != 4 { | |||
return ri | |||
} | |||
return LoadConstant{Dst: ri.Dst, Value: ri.Immediate} | |||
} | |||
case modeAbsolute: // 0x20 | |||
if class == opClassLoad { | |||
return LoadAbsolute{ | |||
Dst: ri.Dst, | |||
Src: ri.Src, | |||
Value: ri.Immediate, | |||
Size: size, | |||
} | |||
} | |||
case modeIndirect: // 0x40 | |||
if class == opClassLoad { | |||
return LoadIndirect{ | |||
Dst: ri.Dst, | |||
Src: ri.Src, | |||
Value: ri.Immediate, | |||
Size: size, | |||
} | |||
} | |||
case modeMemory: // 0x60 | |||
if class == opClassLoadX { | |||
return LoadMemory{ | |||
Dst: ri.Dst, | |||
Src: ri.Src, | |||
Offset: ri.Offset, | |||
Size: size, | |||
} | |||
} | |||
case modeExclusiveAdd: // 0xc0 | |||
} | |||
case opClassStore, opClassStoreX: | |||
var size uint8 | |||
switch ri.Op & opMaskSize { | |||
case sizeWord: | |||
size = 4 | |||
case sizeDoubleWord: | |||
size = 8 | |||
case sizeHalfWord: | |||
size = 2 | |||
case sizeByte: | |||
size = 1 | |||
default: | |||
return ri | |||
} | |||
switch ri.Op & opMaskMode { | |||
case modeMemory: // 0x60 | |||
if class == opClassStore { | |||
return Store{ | |||
Dst: ri.Dst, | |||
Offset: ri.Offset, | |||
Value: ri.Immediate, | |||
Size: size, | |||
} | |||
} | |||
return StoreMemory{ | |||
Dst: ri.Dst, | |||
Src: ri.Src, | |||
Offset: ri.Offset, | |||
Size: size, | |||
} | |||
} | |||
case opClassALU: | |||
switch op := ALUOp(ri.Op & opMaskOperand); op { | |||
case ALUOpAdd, ALUOpSub, ALUOpMul, ALUOpDiv, ALUOpOr, ALUOpAnd, ALUOpShiftLeft, ALUOpShiftRight, ALUOpMod, ALUOpXor, ALUOpMove, ALUOpArithmicShiftRight: | |||
switch ri.Op & opMaskSource { | |||
case aluSourceImmediate: | |||
return ALUOpConstant{ | |||
Op: op, | |||
Dst: ri.Dst, | |||
Value: ri.Immediate, | |||
} | |||
case aluSourceX: | |||
return ALUOpRegister{ | |||
Op: op, | |||
Dst: ri.Dst, | |||
Src: ri.Src, | |||
} | |||
} | |||
case aluOpNegate: | |||
return Negate{ri.Dst} | |||
case aluOpByteSwap: | |||
var byteOrder binary.ByteOrder = binary.LittleEndian | |||
if ri.Op&opMaskSource == aluSourceX { | |||
byteOrder = binary.BigEndian | |||
} | |||
return ByteSwap{ | |||
Dst: ri.Dst, | |||
ByteOrder: byteOrder, | |||
Size: uint8(ri.Immediate), | |||
} | |||
} | |||
case opClassALU64: | |||
switch op := ALUOp(ri.Op & opMaskOperand); op { | |||
case ALUOpAdd, ALUOpSub, ALUOpMul, ALUOpDiv, ALUOpOr, ALUOpAnd, ALUOpShiftLeft, ALUOpShiftRight, ALUOpMod, ALUOpXor, ALUOpMove, ALUOpArithmicShiftRight: | |||
switch ri.Op & opMaskSource { | |||
case aluSourceImmediate: | |||
return ALU64OpConstant{ | |||
Op: op, | |||
Dst: ri.Dst, | |||
Value: ri.Immediate, | |||
} | |||
case aluSourceX: | |||
return ALU64OpRegister{ | |||
Op: op, | |||
Dst: ri.Dst, | |||
Src: ri.Src, | |||
} | |||
} | |||
case aluOpNegate: | |||
return Negate64{ri.Dst} | |||
} | |||
case opClassJump: | |||
switch op := ri.Op & 0xf0; op { | |||
case jumpOp: | |||
return Jump{ri.Offset} | |||
case jumpOpEqual, jumpOpGreater, jumpOpGreaterOrEqual, jumpOpSet, jumpOpNotEqual, | |||
jumpOpSignedGreater, jumpOpSignedGreaterOrEqual, jumpOpLess, jumpOpLessOrEqual, | |||
jumpOpSignedLess, jumpOpSignedLessOrEqual: | |||
cond := jumpOpToTest(op) | |||
if ri.Op&0x08 == jumpSourceImmediate { | |||
return JumpIf{cond, ri.Dst, ri.Immediate, ri.Offset} | |||
} | |||
return JumpIfX{cond, ri.Dst, ri.Src, ri.Offset} | |||
case jumpOpExit: | |||
return Exit{} | |||
case jumpOpCall: | |||
return Builtin(ri.Immediate) | |||
} | |||
} | |||
return ri | |||
} | |||
func (ri RawInstruction) String() string { | |||
return fmt.Sprintf("# invalid opcode = %#02x, class = %#02x", ri.Op, ri.Op&opMaskClass) | |||
} | |||
// Disassemble a slice of bytes containing an eBPF program. | |||
func Disassemble(data []byte) (Program, error) { | |||
if l := len(data); l%8 != 0 { | |||
return nil, fmt.Errorf("ebpf: raw data size %d does not align with 64-bit boundary", l) | |||
} | |||
return DisassembleReader(bytes.NewBuffer(data)) | |||
} | |||
// DisassembleReader disassembles an eBPF program from reader using the default byte order. | |||
func DisassembleReader(r io.Reader) (Program, error) { | |||
return DisassembleReaderOrder(r, binary.LittleEndian) | |||
} | |||
// DisassembleReaderOrder is like DisassembleReader with a custom byte order. | |||
func DisassembleReaderOrder(r io.Reader, byteOrder binary.ByteOrder) (Program, error) { | |||
var out Program | |||
for { | |||
var load struct { | |||
Op Opcode | |||
DstSrc uint8 | |||
Offset uint16 | |||
Immediate uint32 | |||
} | |||
if err := binary.Read(r, byteOrder, &load); err == io.EOF { | |||
break | |||
} else if err != nil { | |||
return nil, err | |||
} | |||
out = append(out, RawInstruction{ | |||
Op: load.Op, | |||
Dst: Register(load.DstSrc & 0x0f), | |||
Src: Register(load.DstSrc >> 4), | |||
Offset: load.Offset, | |||
Immediate: load.Immediate, | |||
}.Disassemble()) | |||
} | |||
return out, nil | |||
} |
@ -0,0 +1,150 @@ | |||
package ebpf | |||
import ( | |||
"encoding/hex" | |||
"reflect" | |||
"testing" | |||
) | |||
func TestDisassemble(t *testing.T) { | |||
tests := []struct { | |||
Name string | |||
Data []byte | |||
Want []string | |||
}{ | |||
{ | |||
Name: "alu", | |||
Data: mustUnhex("040100000200000004090000ffffffff0c210000000000001c210000000000002c210000000000003c210000000000004c210000000000005c210000000000006c210000000000007c2100000000000084010000000000009c21000000000000ac21000000000000bc21000000000000cc21000000000000d401000010000000d401000020000000d401000040000000dc01000010000000dc01000020000000dc01000040000000"), | |||
Want: []string{ | |||
"add32 r1, 2", | |||
"add32 r9, 4294967295", | |||
"add32 r1, r2", | |||
"sub32 r1, r2", | |||
"mul32 r1, r2", | |||
"div32 r1, r2", | |||
"or32 r1, r2", | |||
"and32 r1, r2", | |||
"lsh32 r1, r2", | |||
"rsh32 r1, r2", | |||
"neg32 r1", | |||
"mod32 r1, r2", | |||
"xor32 r1, r2", | |||
"mov32 r1, r2", | |||
"arsh32 r1, r2", | |||
"le16 r1", | |||
"le32 r1", | |||
"le64 r1", | |||
"be16 r1", | |||
"be32 r1", | |||
"be64 r1", | |||
}, | |||
}, | |||
{ | |||
Name: "alu-bit", | |||
Want: []string{ | |||
"mov32 r0, 0", | |||
"mov32 r1, 1", | |||
"mov32 r2, 2", | |||
"mov32 r3, 3", | |||
"mov32 r4, 4", | |||
"mov32 r5, 5", | |||
"mov32 r6, 6", | |||
"mov32 r7, 7", | |||
"mov32 r8, 8", | |||
"or32 r0, r5", | |||
"or32 r0, 0xa0", | |||
"and32 r0, 0xa3", | |||
"mov32 r9, 0x91", | |||
"and32 r0, r9", | |||
"lsh32 r0, 22", | |||
"lsh32 r0, r8", | |||
"rsh32 r0, 19", | |||
"rsh32 r0, r7", | |||
"xor32 r0, 3", | |||
"xor32 r0, r2", | |||
"exit", | |||
}, | |||
}, | |||
{ | |||
Name: "ja", | |||
Data: mustUnhex("b7000000010000000500010000000000b7000000020000009500000000000000"), | |||
Want: []string{ | |||
"mov r0, 1", | |||
"ja +1", | |||
"mov r0, 2", | |||
"exit", | |||
}, | |||
}, | |||
} | |||
for _, test := range tests { | |||
t.Run(test.Name, func(t *testing.T) { | |||
ins, err := Disassemble(test.Data) | |||
if err != nil { | |||
t.Fatal(err) | |||
} | |||
if l := len(ins); l != len(test.Want) { | |||
t.Errorf("expected %d instructions, got %d", len(test.Want), l) | |||
} | |||
for i, a := range ins { | |||
if i < len(test.Want) { | |||
if v := a.String(); v != test.Want[i] { | |||
t.Errorf("unexpected assembly in line %d:\ntest: %q\nwant: %q", i+1, test.Want[i], v) | |||
} | |||
} else { | |||
t.Errorf("unexpected %q", a) | |||
} | |||
} | |||
}) | |||
} | |||
} | |||
func mustUnhex(s string) []byte { | |||
b, err := hex.DecodeString(s) | |||
if err != nil { | |||
panic(err) | |||
} | |||
return b | |||
} | |||
type testInstruction struct { | |||
Test Instruction | |||
WantRaw RawInstruction | |||
WantError string | |||
WantString string | |||
} | |||
func (test testInstruction) Run(t *testing.T) { | |||
t.Helper() | |||
r, err := test.Test.Assemble() | |||
if err != nil { | |||
if test.WantError == "" { | |||
t.Fatal(err) | |||
return | |||
} else if v := err.Error(); v != test.WantError { | |||
t.Errorf("unexpected error:\nwant: %v\ntest: %v", test.WantError, err) | |||
} | |||
t.Log(err) | |||
return | |||
} | |||
if !reflect.DeepEqual(r, test.WantRaw) { | |||
t.Errorf("unexpected result:\nwant: %#+v\ntest: %#+v", test.WantRaw, r) | |||
} | |||
if v := test.Test.String(); v != test.WantString { | |||
t.Errorf("unexpected result:\nwant: %q\ntest: %q", test.WantString, v) | |||
} else { | |||
t.Log(v) | |||
} | |||
} | |||
type testInstructionSuite []testInstruction | |||
func (suite testInstructionSuite) Run(t *testing.T) { | |||
for _, test := range suite { | |||
t.Run(test.WantString, func(it *testing.T) { | |||
test.Run(it) | |||
}) | |||
} | |||
} |
@ -0,0 +1,304 @@ | |||
grammar Assembler; | |||
program | |||
: (labelDefinition | labelDefinition? instruction)+ EOF | |||
; | |||
labelDefinition | |||
: IDENTIFIER ':' | |||
; | |||
label : IDENTIFIER ; | |||
comment | |||
: COMMENT | |||
; | |||
instruction | |||
: aluOperation | |||
| alu64Operation | |||
| byteSwapOperation | |||
| jumpOperation | |||
| jumpConditionalOperation | |||
| callOperation | |||
| exitOperation | |||
| loadOperation | |||
| storeOperation | |||
; | |||
aluOperation | |||
: aluOperator register ',' (register | immediate) | |||
| aluOperator register | |||
; | |||
aluOperator | |||
: ADD32 | |||
| SUB32 | |||
| MUL32 | |||
| DIV32 | |||
| MOD32 | |||
| AND32 | |||
| OR32 | |||
| NEG32 | |||
| XOR32 | |||
| MOV32 | |||
| LSH32 | |||
| RSH32 | |||
| ARSH32 | |||
; | |||
ADD32 : A D D '32' ; | |||
SUB32 : S U B '32' ; | |||
MUL32 : M U L '32' ; | |||
DIV32 : D I V '32' ; | |||
MOD32 : M O D '32' ; | |||
AND32 : A N D '32' ; | |||
OR32 : O R '32' ; | |||
NEG32 : N E G '32' ; | |||
XOR32 : X O R '32' ; | |||
MOV32 : M O V '32' ; | |||
LSH32 : L S H '32' ; | |||
RSH32 : R S H '32' ; | |||
ARSH32 : A R S H '32' ; | |||
alu64Operation | |||
: alu64Operator register ',' (register | immediate) | |||
| alu64Operator register | |||
; | |||
alu64Operator | |||
: ADD | |||
| SUB | |||
| MUL | |||
| DIV | |||
| MOD | |||
| AND | |||
| OR | |||
| NEG | |||
| XOR | |||
| MOV | |||
| LSH | |||
| RSH | |||
| ARSH | |||
| LE | |||
| BE | |||
; | |||
ADD : A D D ; | |||
SUB : S U B ; | |||
MUL : M U L ; | |||
DIV : D I V ; | |||
MOD : M O D ; | |||
AND : A N D ; | |||
OR : O R ; | |||
NEG : N E G ; | |||
XOR : X O R ; | |||
MOV : M O V ; | |||
LSH : L S H ; | |||
RSH : R S H ; | |||
ARSH : A R S H ; | |||
byteSwapOperation | |||
: byteSwapOperator register | |||
; | |||
byteSwapOperator | |||
: LE | |||
| BE | |||
; | |||
LE : L E '16' | |||
| L E '32' | |||
| L E '64' | |||
; | |||
BE : B E '16' | |||
| B E '32' | |||
| B E '64' | |||
; | |||
jumpOperation | |||
: jumpOperator '+' number | |||
; | |||
jumpOperator | |||
: JA | |||
; | |||
jumpConditionalOperation | |||
: jumpConditionalOperator register ',' ( register | immediate ) ',' '+' offset | |||
; | |||
jumpConditionalOperator | |||
: JEQ | |||
| JGT | |||
| JGE | |||
| JLT | |||
| JLE | |||
| JSET | |||
| JNE | |||
| JSGT | |||
| JSGE | |||
| JSLT | |||
| JSLE | |||
; | |||
callOperation | |||
: CALL literalNumber | |||
; | |||
exitOperation | |||
: EXIT | |||
; | |||
JA : J A ; | |||
JEQ : J E Q ; | |||
JGT : J G T ; | |||
JGE : J G E ; | |||
JLT : J L T ; | |||
JLE : J L E ; | |||
JSET : J S E T ; | |||
JNE : J N E ; | |||
JSGT : J S G T ; | |||
JSGE : J S G E ; | |||
JSLT : J S L T ; | |||
JSLE : J S L E ; | |||
CALL : C A L L ; | |||
EXIT : E X I T ; | |||
loadOperation | |||
: LDDW register ',' literalNumber | |||
| loadAbsoluteOperator register ',' register ',' immediate | |||
| loadOperator register ',' '[' register '+' offset ']' | |||
; | |||
loadAbsoluteOperator | |||
: LDABSW | |||
| LDABSH | |||
| LDABSB | |||
| LDABSDW | |||
; | |||
loadOperator | |||
: LDXW | |||
| LDXH | |||
| LDXB | |||
| LDXDW | |||
; | |||
LDDW : L D D W ; | |||
LDABSW : L D A B S W ; | |||
LDABSH : L D A B S H ; | |||
LDABSB : L D A B S B ; | |||
LDABSDW : L D A B S D W ; | |||
LDXW : L D X W ; | |||
LDXH : L D X H ; | |||
LDXB : L D X B ; | |||
LDXDW : L D X D W ; | |||
storeOperation | |||
: storeOperator '[' register '+' offset ']' ',' immediate | |||
| storeXOperator '[' register '+' offset ']' ',' register | |||
; | |||
storeOperator | |||
: STW | |||
| STH | |||
| STB | |||
| STDW | |||
; | |||
storeXOperator | |||
: STXW | |||
| STXH | |||
| STXB | |||
| STXDW | |||
; | |||
STW : S T W; | |||
STH : S T H; | |||
STB : S T B; | |||
STDW : S T D W; | |||
STXW : S T X W; | |||
STXH : S T X H; | |||
STXB : S T X B; | |||
STXDW : S T X D W; | |||
number | |||
: NUMBER | |||
; | |||
literalNumber | |||
: ('#' number) | |||
; | |||
offset | |||
: NUMBER | |||
; | |||
immediate | |||
: NUMBER | |||
; | |||
register | |||
: REGISTER | |||
| RAX | |||
| RFP | |||
; | |||
extension | |||
: IDENTIFIER | |||
; | |||
NUMBER | |||
: '0b' [01]+ | |||
| '0x' [0-9a-fA-F]+ | |||
| '-'? [0-9]+ | |||
; | |||
COMMENT | |||
: ';' ~ [\r\n]* -> skip | |||
; | |||
IDENTIFIER | |||
: [._a-zA-Z]+ | |||
; | |||
WHITESPACE | |||
: [ \t\r\n]+ -> skip | |||
; | |||
REGISTER | |||
: R [0-9] | |||
| R '10' | |||
; | |||
RAX : R A X ; | |||
RFP : R F P ; | |||
fragment A : ( 'a' | 'A' ); | |||
fragment B : ( 'b' | 'B' ); | |||
fragment C : ( 'c' | 'C' ); | |||
fragment D : ( 'd' | 'D' ); | |||
fragment E : ( 'e' | 'E' ); | |||
fragment F : ( 'f' | 'F' ); | |||
fragment G : ( 'g' | 'G' ); | |||
fragment H : ( 'h' | 'H' ); | |||
fragment I : ( 'i' | 'I' ); | |||
fragment J : ( 'j' | 'J' ); | |||
fragment K : ( 'k' | 'K' ); | |||
fragment L : ( 'l' | 'L' ); | |||
fragment M : ( 'm' | 'M' ); | |||
fragment N : ( 'n' | 'N' ); | |||
fragment O : ( 'o' | 'O' ); | |||
fragment P : ( 'p' | 'P' ); | |||
fragment Q : ( 'q' | 'Q' ); | |||
fragment R : ( 'r' | 'R' ); | |||
fragment S : ( 's' | 'S' ); | |||
fragment T : ( 't' | 'T' ); | |||
fragment U : ( 'u' | 'U' ); | |||
fragment V : ( 'v' | 'V' ); | |||
fragment W : ( 'w' | 'W' ); | |||
fragment X : ( 'x' | 'X' ); | |||
fragment Y : ( 'y' | 'Y' ); | |||
fragment Z : ( 'z' | 'Z' ); |
@ -0,0 +1,195 @@ | |||
// Code generated from internal/parser/Assembler.g4 by ANTLR 4.7.2. DO NOT EDIT. | |||
package parser // Assembler | |||
import "github.com/antlr/antlr4/runtime/Go/antlr" | |||
// BaseAssemblerListener is a complete listener for a parse tree produced by AssemblerParser. | |||
type BaseAssemblerListener struct{} | |||
var _ AssemblerListener = &BaseAssemblerListener{} | |||
// VisitTerminal is called when a terminal node is visited. | |||
func (s *BaseAssemblerListener) VisitTerminal(node antlr.TerminalNode) {} | |||
// VisitErrorNode is called when an error node is visited. | |||
func (s *BaseAssemblerListener) VisitErrorNode(node antlr.ErrorNode) {} | |||
// EnterEveryRule is called when any rule is entered. | |||
func (s *BaseAssemblerListener) EnterEveryRule(ctx antlr.ParserRuleContext) {} | |||
// ExitEveryRule is called when any rule is exited. | |||
func (s *BaseAssemblerListener) ExitEveryRule(ctx antlr.ParserRuleContext) {} | |||
// EnterProgram is called when production program is entered. | |||
func (s *BaseAssemblerListener) EnterProgram(ctx *ProgramContext) {} | |||
// ExitProgram is called when production program is exited. | |||
func (s *BaseAssemblerListener) ExitProgram(ctx *ProgramContext) {} | |||
// EnterLabelDefinition is called when production labelDefinition is entered. | |||
func (s *BaseAssemblerListener) EnterLabelDefinition(ctx *LabelDefinitionContext) {} | |||
// ExitLabelDefinition is called when production labelDefinition is exited. | |||
func (s *BaseAssemblerListener) ExitLabelDefinition(ctx *LabelDefinitionContext) {} | |||
// EnterLabel is called when production label is entered. | |||
func (s *BaseAssemblerListener) EnterLabel(ctx *LabelContext) {} | |||
// ExitLabel is called when production label is exited. | |||
func (s *BaseAssemblerListener) ExitLabel(ctx *LabelContext) {} | |||
// EnterComment is called when production comment is entered. | |||
func (s *BaseAssemblerListener) EnterComment(ctx *CommentContext) {} | |||
// ExitComment is called when production comment is exited. | |||
func (s *BaseAssemblerListener) ExitComment(ctx *CommentContext) {} | |||
// EnterInstruction is called when production instruction is entered. | |||
func (s *BaseAssemblerListener) EnterInstruction(ctx *InstructionContext) {} | |||
// ExitInstruction is called when production instruction is exited. | |||
func (s *BaseAssemblerListener) ExitInstruction(ctx *InstructionContext) {} | |||
// EnterAlu64Operation is called when production alu64Operation is entered. | |||
func (s *BaseAssemblerListener) EnterAlu64Operation(ctx *Alu64OperationContext) {} | |||
// ExitAlu64Operation is called when production alu64Operation is exited. | |||
func (s *BaseAssemblerListener) ExitAlu64Operation(ctx *Alu64OperationContext) {} | |||
// EnterAlu64Operator is called when production alu64Operator is entered. | |||
func (s *BaseAssemblerListener) EnterAlu64Operator(ctx *Alu64OperatorContext) {} | |||
// ExitAlu64Operator is called when production alu64Operator is exited. | |||
func (s *BaseAssemblerListener) ExitAlu64Operator(ctx *Alu64OperatorContext) {} | |||
// EnterAluOperation is called when production aluOperation is entered. | |||
func (s *BaseAssemblerListener) EnterAluOperation(ctx *AluOperationContext) {} | |||
// ExitAluOperation is called when production aluOperation is exited. | |||
func (s *BaseAssemblerListener) ExitAluOperation(ctx *AluOperationContext) {} | |||
// EnterAluOperator is called when production aluOperator is entered. | |||
func (s *BaseAssemblerListener) EnterAluOperator(ctx *AluOperatorContext) {} | |||
// ExitAluOperator is called when production aluOperator is exited. | |||
func (s *BaseAssemblerListener) ExitAluOperator(ctx *AluOperatorContext) {} | |||
// EnterByteSwapOperation is called when production byteSwapOperation is entered. | |||
func (s *BaseAssemblerListener) EnterByteSwapOperation(ctx *ByteSwapOperationContext) {} | |||
// ExitByteSwapOperation is called when production byteSwapOperation is exited. | |||
func (s *BaseAssemblerListener) ExitByteSwapOperation(ctx *ByteSwapOperationContext) {} | |||
// EnterByteSwapOperator is called when production byteSwapOperator is entered. | |||
func (s *BaseAssemblerListener) EnterByteSwapOperator(ctx *ByteSwapOperatorContext) {} | |||
// ExitByteSwapOperator is called when production byteSwapOperator is exited. | |||
func (s *BaseAssemblerListener) ExitByteSwapOperator(ctx *ByteSwapOperatorContext) {} | |||
// EnterJumpOperation is called when production jumpOperation is entered. | |||
func (s *BaseAssemblerListener) EnterJumpOperation(ctx *JumpOperationContext) {} | |||
// ExitJumpOperation is called when production jumpOperation is exited. | |||
func (s *BaseAssemblerListener) ExitJumpOperation(ctx *JumpOperationContext) {} | |||
// EnterJumpOperator is called when production jumpOperator is entered. | |||
func (s *BaseAssemblerListener) EnterJumpOperator(ctx *JumpOperatorContext) {} | |||
// ExitJumpOperator is called when production jumpOperator is exited. | |||
func (s *BaseAssemblerListener) ExitJumpOperator(ctx *JumpOperatorContext) {} | |||
// EnterJumpConditionalOperation is called when production jumpConditionalOperation is entered. | |||
func (s *BaseAssemblerListener) EnterJumpConditionalOperation(ctx *JumpConditionalOperationContext) {} | |||
// ExitJumpConditionalOperation is called when production jumpConditionalOperation is exited. | |||
func (s *BaseAssemblerListener) ExitJumpConditionalOperation(ctx *JumpConditionalOperationContext) {} | |||
// EnterJumpConditionalOperator is called when production jumpConditionalOperator is entered. | |||
func (s *BaseAssemblerListener) EnterJumpConditionalOperator(ctx *JumpConditionalOperatorContext) {} | |||
// ExitJumpConditionalOperator is called when production jumpConditionalOperator is exited. | |||
func (s *BaseAssemblerListener) ExitJumpConditionalOperator(ctx *JumpConditionalOperatorContext) {} | |||
// EnterCallOperation is called when production callOperation is entered. | |||
func (s *BaseAssemblerListener) EnterCallOperation(ctx *CallOperationContext) {} | |||
// ExitCallOperation is called when production callOperation is exited. | |||
func (s *BaseAssemblerListener) ExitCallOperation(ctx *CallOperationContext) {} | |||
// EnterExitOperation is called when production exitOperation is entered. | |||
func (s *BaseAssemblerListener) EnterExitOperation(ctx *ExitOperationContext) {} | |||
// ExitExitOperation is called when production exitOperation is exited. | |||
func (s *BaseAssemblerListener) ExitExitOperation(ctx *ExitOperationContext) {} | |||
// EnterLoadOperation is called when production loadOperation is entered. | |||
func (s *BaseAssemblerListener) EnterLoadOperation(ctx *LoadOperationContext) {} | |||
// ExitLoadOperation is called when production loadOperation is exited. | |||
func (s *BaseAssemblerListener) ExitLoadOperation(ctx *LoadOperationContext) {} | |||
// EnterLoadAbsoluteOperator is called when production loadAbsoluteOperator is entered. | |||
func (s *BaseAssemblerListener) EnterLoadAbsoluteOperator(ctx *LoadAbsoluteOperatorContext) {} | |||
// ExitLoadAbsoluteOperator is called when production loadAbsoluteOperator is exited. | |||
func (s *BaseAssemblerListener) ExitLoadAbsoluteOperator(ctx *LoadAbsoluteOperatorContext) {} | |||
// EnterLoadOperator is called when production loadOperator is entered. | |||
func (s *BaseAssemblerListener) EnterLoadOperator(ctx *LoadOperatorContext) {} | |||
// ExitLoadOperator is called when production loadOperator is exited. | |||
func (s *BaseAssemblerListener) ExitLoadOperator(ctx *LoadOperatorContext) {} | |||
// EnterStoreOperation is called when production storeOperation is entered. | |||
func (s *BaseAssemblerListener) EnterStoreOperation(ctx *StoreOperationContext) {} | |||
// ExitStoreOperation is called when production storeOperation is exited. | |||
func (s *BaseAssemblerListener) ExitStoreOperation(ctx *StoreOperationContext) {} | |||
// EnterStoreOperator is called when production storeOperator is entered. | |||
func (s *BaseAssemblerListener) EnterStoreOperator(ctx *StoreOperatorContext) {} | |||
// ExitStoreOperator is called when production storeOperator is exited. | |||
func (s *BaseAssemblerListener) ExitStoreOperator(ctx *StoreOperatorContext) {} | |||
// EnterStoreXOperator is called when production storeXOperator is entered. | |||
func (s *BaseAssemblerListener) EnterStoreXOperator(ctx *StoreXOperatorContext) {} | |||
// ExitStoreXOperator is called when production storeXOperator is exited. | |||
func (s *BaseAssemblerListener) ExitStoreXOperator(ctx *StoreXOperatorContext) {} | |||
// EnterNumber is called when production number is entered. | |||
func (s *BaseAssemblerListener) EnterNumber(ctx *NumberContext) {} | |||
// ExitNumber is called when production number is exited. | |||
func (s *BaseAssemblerListener) ExitNumber(ctx *NumberContext) {} | |||
// EnterLiteralNumber is called when production literalNumber is entered. | |||
func (s *BaseAssemblerListener) EnterLiteralNumber(ctx *LiteralNumberContext) {} | |||
// ExitLiteralNumber is called when production literalNumber is exited. | |||
func (s *BaseAssemblerListener) ExitLiteralNumber(ctx *LiteralNumberContext) {} | |||
// EnterOffset is called when production offset is entered. | |||
func (s *BaseAssemblerListener) EnterOffset(ctx *OffsetContext) {} | |||
// ExitOffset is called when production offset is exited. | |||
func (s *BaseAssemblerListener) ExitOffset(ctx *OffsetContext) {} | |||
// EnterImmediate is called when production immediate is entered. | |||
func (s *BaseAssemblerListener) EnterImmediate(ctx *ImmediateContext) {} | |||
// ExitImmediate is called when production immediate is exited. | |||
func (s *BaseAssemblerListener) ExitImmediate(ctx *ImmediateContext) {} | |||
// EnterRegister is called when production register is entered. | |||
func (s *BaseAssemblerListener) EnterRegister(ctx *RegisterContext) {} | |||
// ExitRegister is called when production register is exited. | |||
func (s *BaseAssemblerListener) ExitRegister(ctx *RegisterContext) {} | |||
// EnterExtension is called when production extension is entered. | |||
func (s *BaseAssemblerListener) EnterExtension(ctx *ExtensionContext) {} | |||
// ExitExtension is called when production extension is exited. | |||
func (s *BaseAssemblerListener) ExitExtension(ctx *ExtensionContext) {} |
@ -0,0 +1,124 @@ | |||
// Code generated from internal/parser/Assembler.g4 by ANTLR 4.7.2. DO NOT EDIT. | |||
package parser // Assembler | |||
import "github.com/antlr/antlr4/runtime/Go/antlr" | |||
type BaseAssemblerVisitor struct { | |||
*antlr.BaseParseTreeVisitor | |||
} | |||
func (v *BaseAssemblerVisitor) VisitProgram(ctx *ProgramContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitLabelDefinition(ctx *LabelDefinitionContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitLabel(ctx *LabelContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitComment(ctx *CommentContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitInstruction(ctx *InstructionContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitAlu64Operation(ctx *Alu64OperationContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitAlu64Operator(ctx *Alu64OperatorContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitAluOperation(ctx *AluOperationContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitAluOperator(ctx *AluOperatorContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitByteSwapOperation(ctx *ByteSwapOperationContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitByteSwapOperator(ctx *ByteSwapOperatorContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitJumpOperation(ctx *JumpOperationContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitJumpOperator(ctx *JumpOperatorContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitJumpConditionalOperation(ctx *JumpConditionalOperationContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitJumpConditionalOperator(ctx *JumpConditionalOperatorContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitCallOperation(ctx *CallOperationContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitExitOperation(ctx *ExitOperationContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitLoadOperation(ctx *LoadOperationContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitLoadAbsoluteOperator(ctx *LoadAbsoluteOperatorContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitLoadOperator(ctx *LoadOperatorContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitStoreOperation(ctx *StoreOperationContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitStoreOperator(ctx *StoreOperatorContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitStoreXOperator(ctx *StoreXOperatorContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitNumber(ctx *NumberContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitLiteralNumber(ctx *LiteralNumberContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitOffset(ctx *OffsetContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitImmediate(ctx *ImmediateContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitRegister(ctx *RegisterContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} | |||
func (v *BaseAssemblerVisitor) VisitExtension(ctx *ExtensionContext) interface{} { | |||
return v.VisitChildren(ctx) | |||
} |
@ -0,0 +1,450 @@ | |||
// Code generated from internal/parser/Assembler.g4 by ANTLR 4.7.2. DO NOT EDIT. | |||
package parser | |||
import ( | |||
"fmt" | |||
"unicode" | |||
"github.com/antlr/antlr4/runtime/Go/antlr" | |||
) | |||
// Suppress unused import error | |||
var _ = fmt.Printf | |||
var _ = unicode.IsLetter | |||
var serializedLexerAtn = []uint16{ | |||
3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 74, 646, | |||
8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, | |||
9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, | |||
4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, | |||
18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, | |||
9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9, | |||
28, 4, 29, 9, 29, 4, 30, 9, 30, 4, 31, 9, 31, 4, 32, 9, 32, 4, 33, 9, 33, | |||
4, 34, 9, 34, 4, 35, 9, 35, 4, 36, 9, 36, 4, 37, 9, 37, 4, 38, 9, 38, 4, | |||
39, 9, 39, 4, 40, 9, 40, 4, 41, 9, 41, 4, 42, 9, 42, 4, 43, 9, 43, 4, 44, | |||
9, 44, 4, 45, 9, 45, 4, 46, 9, 46, 4, 47, 9, 47, 4, 48, 9, 48, 4, 49, 9, | |||
49, 4, 50, 9, 50, 4, 51, 9, 51, 4, 52, 9, 52, 4, 53, 9, 53, 4, 54, 9, 54, | |||
4, 55, 9, 55, 4, 56, 9, 56, 4, 57, 9, 57, 4, 58, 9, 58, 4, 59, 9, 59, 4, | |||
60, 9, 60, 4, 61, 9, 61, 4, 62, 9, 62, 4, 63, 9, 63, 4, 64, 9, 64, 4, 65, | |||
9, 65, 4, 66, 9, 66, 4, 67, 9, 67, 4, 68, 9, 68, 4, 69, 9, 69, 4, 70, 9, | |||
70, 4, 71, 9, 71, 4, 72, 9, 72, 4, 73, 9, 73, 4, 74, 9, 74, 4, 75, 9, 75, | |||
4, 76, 9, 76, 4, 77, 9, 77, 4, 78, 9, 78, 4, 79, 9, 79, 4, 80, 9, 80, 4, | |||
81, 9, 81, 4, 82, 9, 82, 4, 83, 9, 83, 4, 84, 9, 84, 4, 85, 9, 85, 4, 86, | |||
9, 86, 4, 87, 9, 87, 4, 88, 9, 88, 4, 89, 9, 89, 4, 90, 9, 90, 4, 91, 9, | |||
91, 4, 92, 9, 92, 4, 93, 9, 93, 4, 94, 9, 94, 4, 95, 9, 95, 4, 96, 9, 96, | |||
4, 97, 9, 97, 4, 98, 9, 98, 4, 99, 9, 99, 3, 2, 3, 2, 3, 3, 3, 3, 3, 4, | |||
3, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 7, 3, 7, 3, 8, 3, 8, 3, 8, 3, 8, 3, 9, | |||
3, 9, 3, 9, 3, 9, 3, 10, 3, 10, 3, 10, 3, 10, 3, 11, 3, 11, 3, 11, 3, 11, | |||
3, 12, 3, 12, 3, 12, 3, 12, 3, 13, 3, 13, 3, 13, 3, 13, 3, 14, 3, 14, 3, | |||
14, 3, 15, 3, 15, 3, 15, 3, 15, 3, 16, 3, 16, 3, 16, 3, 16, 3, 17, 3, 17, | |||
3, 17, 3, 17, 3, 18, 3, 18, 3, 18, 3, 18, 3, 19, 3, 19, 3, 19, 3, 19, 3, | |||
20, 3, 20, 3, 20, 3, 20, 3, 20, 3, 21, 3, 21, 3, 21, 3, 21, 3, 21, 3, 21, | |||
3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 23, 3, 23, 3, 23, 3, 23, 3, | |||
23, 3, 23, 3, 24, 3, 24, 3, 24, 3, 24, 3, 24, 3, 24, 3, 25, 3, 25, 3, 25, | |||
3, 25, 3, 25, 3, 25, 3, 26, 3, 26, 3, 26, 3, 26, 3, 26, 3, 26, 3, 27, 3, | |||
27, 3, 27, 3, 27, 3, 27, 3, 28, 3, 28, 3, 28, 3, 28, 3, 28, 3, 28, 3, 29, | |||
3, 29, 3, 29, 3, 29, 3, 29, 3, 29, 3, 30, 3, 30, 3, 30, 3, 30, 3, 30, 3, | |||
30, 3, 31, 3, 31, 3, 31, 3, 31, 3, 31, 3, 31, 3, 32, 3, 32, 3, 32, 3, 32, | |||
3, 32, 3, 32, 3, 33, 3, 33, 3, 33, 3, 33, 3, 33, 3, 33, 3, 33, 3, 34, 3, | |||
34, 3, 34, 3, 34, 3, 34, 3, 34, 3, 34, 3, 34, 3, 34, 3, 34, 3, 34, 3, 34, | |||
3, 34, 3, 34, 3, 34, 5, 34, 357, 10, 34, 3, 35, 3, 35, 3, 35, 3, 35, 3, | |||
35, 3, 35, 3, 35, 3, 35, 3, 35, 3, 35, 3, 35, 3, 35, 3, 35, 3, 35, 3, 35, | |||
5, 35, 374, 10, 35, 3, 36, 3, 36, 3, 36, 3, 37, 3, 37, 3, 37, 3, 37, 3, | |||
38, 3, 38, 3, 38, 3, 38, 3, 39, 3, 39, 3, 39, 3, 39, 3, 40, 3, 40, 3, 40, | |||
3, 40, 3, 41, 3, 41, 3, 41, 3, 41, 3, 42, 3, 42, 3, 42, 3, 42, 3, 42, 3, | |||
43, 3, 43, 3, 43, 3, 43, 3, 44, 3, 44, 3, 44, 3, 44, 3, 44, 3, 45, 3, 45, | |||
3, 45, 3, 45, 3, 45, 3, 46, 3, 46, 3, 46, 3, 46, 3, 46, 3, 47, 3, 47, 3, | |||
47, 3, 47, 3, 47, 3, 48, 3, 48, 3, 48, 3, 48, 3, 48, 3, 49, 3, 49, 3, 49, | |||
3, 49, 3, 49, 3, 50, 3, 50, 3, 50, 3, 50, 3, 50, 3, 51, 3, 51, 3, 51, 3, | |||
51, 3, 51, 3, 51, 3, 51, 3, 52, 3, 52, 3, 52, 3, 52, 3, 52, 3, 52, 3, 52, | |||
3, 53, 3, 53, 3, 53, 3, 53, 3, 53, 3, 53, 3, 53, 3, 54, 3, 54, 3, 54, 3, | |||
54, 3, 54, 3, 54, 3, 54, 3, 54, 3, 55, 3, 55, 3, 55, 3, 55, 3, 55, 3, 56, | |||
3, 56, 3, 56, 3, 56, 3, 56, 3, 57, 3, 57, 3, 57, 3, 57, 3, 57, 3, 58, 3, | |||
58, 3, 58, 3, 58, 3, 58, 3, 58, 3, 59, 3, 59, 3, 59, 3, 59, 3, 60, 3, 60, | |||
3, 60, 3, 60, 3, 61, 3, 61, 3, 61, 3, 61, 3, 62, 3, 62, 3, 62, 3, 62, 3, | |||
62, 3, 63, 3, 63, 3, 63, 3, 63, 3, 63, 3, 64, 3, 64, 3, 64, 3, 64, 3, 64, | |||
3, 65, 3, 65, 3, 65, 3, 65, 3, 65, 3, 66, 3, 66, 3, 66, 3, 66, 3, 66, 3, | |||
66, 3, 67, 3, 67, 3, 67, 3, 67, 6, 67, 535, 10, 67, 13, 67, 14, 67, 536, | |||
3, 67, 3, 67, 3, 67, 3, 67, 6, 67, 543, 10, 67, 13, 67, 14, 67, 544, 3, | |||
67, 5, 67, 548, 10, 67, 3, 67, 6, 67, 551, 10, 67, 13, 67, 14, 67, 552, | |||
5, 67, 555, 10, 67, 3, 68, 3, 68, 7, 68, 559, 10, 68, 12, 68, 14, 68, 562, | |||
11, 68, 3, 68, 3, 68, 3, 69, 6, 69, 567, 10, 69, 13, 69, 14, 69, 568, 3, | |||
70, 6, 70, 572, 10, 70, 13, 70, 14, 70, 573, 3, 70, 3, 70, 3, 71, 3, 71, | |||
3, 71, 3, 71, 3, 71, 3, 71, 3, 71, 5, 71, 585, 10, 71, 3, 72, 3, 72, 3, | |||
72, 3, 72, 3, 73, 3, 73, 3, 73, 3, 73, 3, 74, 3, 74, 3, 75, 3, 75, 3, 76, | |||
3, 76, 3, 77, 3, 77, 3, 78, 3, 78, 3, 79, 3, 79, 3, 80, 3, 80, 3, 81, 3, |