load_elf.go 3.55 KB
Newer Older
1 2
package cmd

protolambda's avatar
protolambda committed
3 4 5 6
import (
	"debug/elf"
	"fmt"

7 8
	"github.com/ethereum-optimism/optimism/cannon/mipsevm"
	"github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded"
protolambda's avatar
protolambda committed
9 10
	"github.com/urfave/cli/v2"

11 12
	"github.com/ethereum-optimism/optimism/cannon/mipsevm/program"
	"github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded"
13
	"github.com/ethereum-optimism/optimism/op-service/jsonutil"
protolambda's avatar
protolambda committed
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
)

var (
	LoadELFPathFlag = &cli.PathFlag{
		Name:      "path",
		Usage:     "Path to 32-bit big-endian MIPS ELF file",
		TakesFile: true,
		Required:  true,
	}
	LoadELFPatchFlag = &cli.StringSliceFlag{
		Name:     "patch",
		Usage:    "Type of patching to do",
		Value:    cli.NewStringSlice("go", "stack"),
		Required: false,
	}
	LoadELFOutFlag = &cli.PathFlag{
		Name:     "out",
31
		Usage:    "Output path to write JSON state to. State is dumped to stdout if set to -. Not written if empty.",
protolambda's avatar
protolambda committed
32 33 34
		Value:    "state.json",
		Required: false,
	}
35 36 37 38 39 40
	LoadELFMetaFlag = &cli.PathFlag{
		Name:     "meta",
		Usage:    "Write metadata file, for symbol lookup during program execution. None if empty.",
		Value:    "meta.json",
		Required: false,
	}
protolambda's avatar
protolambda committed
41
)
42 43

func LoadELF(ctx *cli.Context) error {
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
	var createInitialState func(f *elf.File) (mipsevm.FPVMState, error)
	var writeState func(path string, state mipsevm.FPVMState) error

	if vmType, err := vmTypeFromString(ctx); err != nil {
		return err
	} else if vmType == cannonVMType {
		createInitialState = func(f *elf.File) (mipsevm.FPVMState, error) {
			return program.LoadELF(f, singlethreaded.CreateInitialState)
		}
		writeState = func(path string, state mipsevm.FPVMState) error {
			return jsonutil.WriteJSON[*singlethreaded.State](path, state.(*singlethreaded.State), OutFilePerm)
		}
	} else if vmType == mtVMType {
		createInitialState = func(f *elf.File) (mipsevm.FPVMState, error) {
			return program.LoadELF(f, multithreaded.CreateInitialState)
		}
		writeState = func(path string, state mipsevm.FPVMState) error {
			return jsonutil.WriteJSON[*multithreaded.State](path, state.(*multithreaded.State), OutFilePerm)
		}
	} else {
		return fmt.Errorf("invalid VM type: %q", vmType)
	}
protolambda's avatar
protolambda committed
66 67 68 69 70
	elfPath := ctx.Path(LoadELFPathFlag.Name)
	elfProgram, err := elf.Open(elfPath)
	if err != nil {
		return fmt.Errorf("failed to open ELF file %q: %w", elfPath, err)
	}
71 72 73
	if elfProgram.Machine != elf.EM_MIPS {
		return fmt.Errorf("ELF is not big-endian MIPS R3000, but got %q", elfProgram.Machine.String())
	}
74
	state, err := createInitialState(elfProgram)
protolambda's avatar
protolambda committed
75 76 77 78 79 80
	if err != nil {
		return fmt.Errorf("failed to load ELF data into VM state: %w", err)
	}
	for _, typ := range ctx.StringSlice(LoadELFPatchFlag.Name) {
		switch typ {
		case "stack":
81
			err = program.PatchStack(state)
protolambda's avatar
protolambda committed
82
		case "go":
83
			err = program.PatchGo(elfProgram, state)
protolambda's avatar
protolambda committed
84 85 86 87 88 89 90
		default:
			return fmt.Errorf("unrecognized form of patching: %q", typ)
		}
		if err != nil {
			return fmt.Errorf("failed to apply patch %s: %w", typ, err)
		}
	}
91
	meta, err := program.MakeMetadata(elfProgram)
92 93 94
	if err != nil {
		return fmt.Errorf("failed to compute program metadata: %w", err)
	}
95
	if err := jsonutil.WriteJSON[*program.Metadata](ctx.Path(LoadELFMetaFlag.Name), meta, OutFilePerm); err != nil {
96 97
		return fmt.Errorf("failed to output metadata: %w", err)
	}
98
	return writeState(ctx.Path(LoadELFOutFlag.Name), state)
99 100 101 102
}

var LoadELFCommand = &cli.Command{
	Name:        "load-elf",
protolambda's avatar
protolambda committed
103 104
	Usage:       "Load ELF file into Cannon JSON state",
	Description: "Load ELF file into Cannon JSON state, optionally patch out functions",
105
	Action:      LoadELF,
protolambda's avatar
protolambda committed
106
	Flags: []cli.Flag{
107
		VMTypeFlag,
protolambda's avatar
protolambda committed
108 109 110
		LoadELFPathFlag,
		LoadELFPatchFlag,
		LoadELFOutFlag,
111
		LoadELFMetaFlag,
protolambda's avatar
protolambda committed
112
	},
113
}