Browse Source

More representable bpfasm command line tool

maze 6 months ago
parent
commit
5244f07ddf
3 changed files with 224 additions and 39 deletions
  1. 219
    39
      cmd/bpfasm/main.go
  2. 1
    0
      go.mod
  3. 4
    0
      go.sum

+ 219
- 39
cmd/bpfasm/main.go View File

@@ -19,73 +19,253 @@ package main
19 19
 
20 20
 import (
21 21
 	"encoding/binary"
22
-	"flag"
23 22
 	"fmt"
24 23
 	"io"
25 24
 	"io/ioutil"
26 25
 	"os"
26
+	"strings"
27
+	"text/tabwriter"
27 28
 
29
+	cli "gopkg.in/urfave/cli.v2"
28 30
 	"maze.io/x/bpf"
29 31
 )
30 32
 
31 33
 func main() {
32
-	outASM := flag.Bool("asm", false, "output assembly")
33
-	outCode := flag.Bool("opc", false, "output opcodes")
34
-	outFile := flag.String("o", "", "output file (default: stdout)")
35
-	flag.Parse()
34
+	app := cli.App{
35
+		Name:  "bpfasm",
36
+		Usage: "Berkeley Packet Filter assembler/disassembler",
37
+		Commands: []*cli.Command{
38
+			&cli.Command{
39
+				Name:      "assemble",
40
+				Aliases:   []string{"a"},
41
+				Usage:     "Assemble a BPF ASM file",
42
+				ArgsUsage: "<input>",
43
+				Action:    assemble,
44
+				Flags: []cli.Flag{
45
+					&cli.StringFlag{
46
+						Name:    "endian",
47
+						Aliases: []string{"e"},
48
+						Value:   "be",
49
+						Usage:   "Binary byte order (be, le)",
50
+					},
51
+					&cli.StringFlag{
52
+						Name:    "format",
53
+						Aliases: []string{"f"},
54
+						Value:   "bin",
55
+						Usage:   "Output format (bin, asm, c)",
56
+					},
57
+					&cli.StringFlag{
58
+						Name:        "output",
59
+						Aliases:     []string{"o"},
60
+						Usage:       "Output file",
61
+						DefaultText: "use stdout",
62
+					},
63
+				},
64
+			},
65
+			&cli.Command{
66
+				Name:      "disassemble",
67
+				Aliases:   []string{"d"},
68
+				Usage:     "Disassemble a BPF binary file",
69
+				ArgsUsage: "<input>",
70
+				Action:    disassemble,
71
+				Flags: []cli.Flag{
72
+					&cli.StringFlag{
73
+						Name:    "endian",
74
+						Aliases: []string{"e"},
75
+						Value:   "be",
76
+						Usage:   "Binary byte order (be, le)",
77
+					},
78
+					&cli.StringFlag{
79
+						Name:    "format",
80
+						Aliases: []string{"f"},
81
+						Value:   "asm",
82
+						Usage:   "Output format (asm, c)",
83
+					},
84
+					&cli.StringFlag{
85
+						Name:        "output",
86
+						Aliases:     []string{"o"},
87
+						Usage:       "Output file",
88
+						DefaultText: "use stdout",
89
+					},
90
+				},
91
+			},
92
+		},
93
+	}
94
+	app.Run(os.Args)
95
+}
36 96
 
37
-	if flag.NArg() != 1 {
38
-		fmt.Fprintln(os.Stderr, "usage: bpfasm <source>")
39
-		os.Exit(1)
97
+type assembler func(io.Writer, bpf.Program) error
98
+
99
+func formatBinary(byteOrder binary.ByteOrder) assembler {
100
+	return func(w io.Writer, program bpf.Program) error {
101
+		raw, err := program.Assemble()
102
+		if err != nil {
103
+			return err
104
+		}
105
+		for _, ins := range raw {
106
+			if err := binary.Write(w, byteOrder, ins); err != nil {
107
+				return err
108
+			}
109
+		}
110
+		return nil
40 111
 	}
112
+}
41 113
 
42
-	b, err := ioutil.ReadFile(flag.Arg(0))
43
-	if err != nil {
44
-		fmt.Fprintln(os.Stderr, "error loading", flag.Arg(0)+":", err)
45
-		os.Exit(2)
114
+func formatAssembly(w io.Writer, program bpf.Program) error {
115
+	t := tabwriter.NewWriter(w, 0, 0, 4, ' ', tabwriter.TabIndent)
116
+	for _, ins := range program {
117
+		if s, ok := ins.(fmt.Stringer); ok {
118
+			fmt.Fprintln(t, strings.Replace(s.String(), " ", "\t", -1))
119
+		}
46 120
 	}
121
+	return t.Flush()
122
+}
47 123
 
48
-	program, err := bpf.Assemble(string(b))
124
+func formatC(w io.Writer, program bpf.Program) error {
125
+	raw, err := program.Assemble()
49 126
 	if err != nil {
50
-		fmt.Fprintln(os.Stderr, "error assembling", flag.Arg(0)+":", err)
51
-		os.Exit(2)
127
+		return err
52 128
 	}
53
-	raw, err := program.Assemble()
129
+	l := len(raw) - 1
130
+	for i, ins := range raw {
131
+		var comma string
132
+		if i < l {
133
+			comma = ","
134
+		}
135
+		if _, err := fmt.Fprintf(w, "{ %#04x, %d, %d, %#08x }%s\n",
136
+			ins.Op, ins.Jt, ins.Jf, ins.K, comma); err != nil {
137
+			return err
138
+		}
139
+	}
140
+	return nil
141
+}
142
+
143
+func assemble(ctx *cli.Context) error {
144
+	if ctx.NArg() != 1 {
145
+		return cli.ShowCommandHelp(ctx, "assemble")
146
+	}
147
+
148
+	var asm assembler
149
+	switch ctx.String("format") {
150
+	case "bin", "binary":
151
+		switch ctx.String("endian") {
152
+		case "be":
153
+			asm = formatBinary(binary.BigEndian)
154
+		case "le":
155
+			asm = formatBinary(binary.LittleEndian)
156
+		default:
157
+			fmt.Fprintf(os.Stderr, "invalid byte order %q\n", ctx.String("endian"))
158
+			os.Exit(1)
159
+		}
160
+
161
+	case "asm":
162
+		asm = formatAssembly
163
+	case "c", "C":
164
+		asm = formatC
165
+	default:
166
+		fmt.Fprintf(os.Stderr, "invalid output format %q\n", ctx.String("format"))
167
+		os.Exit(1)
168
+	}
169
+
170
+	sourcePath := ctx.Args().First()
171
+	sourceBytes, err := ioutil.ReadFile(sourcePath)
54 172
 	if err != nil {
55
-		fmt.Fprintln(os.Stderr, "error assembling instructions", flag.Arg(0)+":", err)
56
-		os.Exit(2)
173
+		fmt.Fprintf(os.Stderr, "error reading %s: %v\n", sourcePath, err)
174
+		os.Exit(1)
57 175
 	}
58 176
 
59
-	if !*outASM && !*outCode && *outFile == "" {
60
-		*outFile = flag.Arg(0) + ".bin"
61
-		fmt.Fprintln(os.Stderr, "writing to", *outFile)
177
+	program, err := bpf.Assemble(string(sourceBytes))
178
+	if err != nil {
179
+		fmt.Fprintf(os.Stderr, "error assembling %s: %v\n", sourcePath, err)
180
+		os.Exit(1)
62 181
 	}
63 182
 
64 183
 	var output io.WriteCloser
65
-	if *outFile != "" {
66
-		if output, err = os.Create(*outFile); err != nil {
67
-			fmt.Fprintln(os.Stderr, "error opening", *outFile+":", err)
68
-			os.Exit(2)
184
+	switch outputFile := ctx.String("output"); outputFile {
185
+	case "", "-":
186
+		if f := ctx.String("format"); f == "bin" || f == "binary" {
187
+			fmt.Fprintf(os.Stderr, "writing to %s.bin\n", sourcePath)
188
+			if output, err = os.Create(sourcePath + ".bin"); err != nil {
189
+				fmt.Fprintf(os.Stderr, "error opening output file: %v\n", err)
190
+				os.Exit(1)
191
+			}
192
+		} else {
193
+			output = os.Stdout
194
+		}
195
+
196
+	default:
197
+		fmt.Fprintf(os.Stderr, "writing to %s\n", outputFile)
198
+		if output, err = os.Create(outputFile); err != nil {
199
+			fmt.Fprintf(os.Stderr, "error opening output file: %v\n", err)
200
+			os.Exit(1)
69 201
 		}
70
-	} else {
71
-		output = os.Stdout
72 202
 	}
73 203
 	defer output.Close()
74 204
 
75
-	if *outCode {
76
-		for _, instruction := range raw {
77
-			fmt.Fprintf(output, "{ %#02x, %2d, %2d, %#08x },\n", instruction.Op, instruction.Jt, instruction.Jf, instruction.K)
78
-		}
79
-	} else if *outASM {
80
-		for _, instruction := range program {
81
-			fmt.Fprintln(output, instruction)
82
-		}
83
-	} else {
84
-		for _, instruction := range raw {
85
-			binary.Write(output, binary.BigEndian, instruction)
205
+	return asm(output, program)
206
+}
207
+
208
+func disassemble(ctx *cli.Context) error {
209
+	if ctx.NArg() != 1 {
210
+		return cli.ShowCommandHelp(ctx, "disassemble")
211
+	}
212
+
213
+	var (
214
+		sourcePath = ctx.Args().First()
215
+		program    bpf.Program
216
+		byteOrder  binary.ByteOrder
217
+		asm        assembler
218
+	)
219
+	switch ctx.String("endian") {
220
+	case "be":
221
+		byteOrder = binary.BigEndian
222
+	case "le":
223
+		byteOrder = binary.LittleEndian
224
+	default:
225
+		fmt.Fprintf(os.Stderr, "invalid byte order %q\n", ctx.String("endian"))
226
+		os.Exit(1)
227
+	}
228
+	switch ctx.String("format") {
229
+	case "asm":
230
+		asm = formatAssembly
231
+	case "c", "C":
232
+		asm = formatC
233
+	default:
234
+		fmt.Fprintf(os.Stderr, "invalid output format %q\n", ctx.String("format"))
235
+		os.Exit(1)
236
+	}
237
+
238
+	sourceFile, err := os.Open(sourcePath)
239
+	if err != nil {
240
+		fmt.Fprintf(os.Stderr, "error opening %s: %v\n", sourcePath, err)
241
+		os.Exit(1)
242
+	}
243
+	defer sourceFile.Close()
244
+
245
+	for {
246
+		var instruction bpf.RawInstruction
247
+		if err = binary.Read(sourceFile, byteOrder, &instruction); err != nil {
248
+			if err == io.EOF {
249
+				break
250
+			}
251
+			return err
86 252
 		}
253
+		program = append(program, instruction.Disassemble())
87 254
 	}
88 255
 
89
-	//assembled := unsafe.Pointer(&program[0])
256
+	var output io.WriteCloser
257
+	switch outputFile := ctx.String("output"); outputFile {
258
+	case "", "-":
259
+		output = os.Stdout
260
+
261
+	default:
262
+		fmt.Fprintf(os.Stderr, "writing to %s\n", outputFile)
263
+		if output, err = os.Create(outputFile); err != nil {
264
+			fmt.Fprintf(os.Stderr, "error opening output file: %v\n", err)
265
+			os.Exit(1)
266
+		}
267
+	}
268
+	defer output.Close()
90 269
 
270
+	return asm(output, program)
91 271
 }

+ 1
- 0
go.mod View File

@@ -3,4 +3,5 @@ module maze.io/x/bpf
3 3
 require (
4 4
 	github.com/antlr/antlr4 v0.0.0-20190223165740-dade65a895c2
5 5
 	golang.org/x/net v0.0.0-20190227160552-c95aed5357e7
6
+	gopkg.in/urfave/cli.v2 v2.0.0-20180128182452-d3ae77c26ac8 // indirect
6 7
 )

+ 4
- 0
go.sum View File

@@ -2,3 +2,7 @@ github.com/antlr/antlr4 v0.0.0-20190223165740-dade65a895c2 h1:Q1TGw0wvj6lqZQ4/CM
2 2
 github.com/antlr/antlr4 v0.0.0-20190223165740-dade65a895c2/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y=
3 3
 golang.org/x/net v0.0.0-20190227160552-c95aed5357e7 h1:C2F/nMkR/9sfUTpvR3QrjBuTdvMUC/cFajkphs1YLQo=
4 4
 golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
5
+gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=
6
+gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
7
+gopkg.in/urfave/cli.v2 v2.0.0-20180128182452-d3ae77c26ac8 h1:Ggy3mWN4l3PUFPfSG0YB3n5fVYggzysUmiUQ89SnX6Y=
8
+gopkg.in/urfave/cli.v2 v2.0.0-20180128182452-d3ae77c26ac8/go.mod h1:cKXr3E0k4aosgycml1b5z33BVV6hai1Kh7uDgFOkbcs=

Loading…
Cancel
Save