github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/ifuzz/arm64/arm64.go (about)

     1  // Copyright 2024 syzkaller project authors. All rights reserved.
     2  // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
     3  
     4  //go:generate bash -c "go run gen/gen.go gen/json/arm64.json | gofmt > generated/insns.go"
     5  
     6  // Package arm64 allows to generate and mutate arm64 machine code.
     7  package arm64
     8  
     9  import (
    10  	"encoding/binary"
    11  	"fmt"
    12  	"math/rand"
    13  
    14  	"github.com/google/syzkaller/pkg/ifuzz/iset"
    15  )
    16  
    17  type InsnField struct {
    18  	Name   string
    19  	Start  uint // Little endian bit order.
    20  	Length uint
    21  }
    22  
    23  type Insn struct {
    24  	Name       string
    25  	OpcodeMask uint32
    26  	Opcode     uint32
    27  	Fields     []InsnField
    28  	AsUInt32   uint32
    29  	Operands   []uint32
    30  	Pseudo     bool
    31  	Priv       bool
    32  	Generator  func(cfg *iset.Config, r *rand.Rand) []byte // for pseudo instructions
    33  }
    34  
    35  type InsnSet struct {
    36  	modeInsns iset.ModeInsns
    37  	Insns     []*Insn
    38  }
    39  
    40  func Register(insns []*Insn) {
    41  	if len(insns) == 0 {
    42  		panic("no instructions")
    43  	}
    44  	insnset := &InsnSet{
    45  		Insns: append(insns, pseudo...),
    46  	}
    47  	for _, insn := range insnset.Insns {
    48  		insnset.modeInsns.Add(insn)
    49  	}
    50  	iset.Arches[iset.ArchArm64] = insnset
    51  	templates = insns
    52  }
    53  
    54  func (insnset *InsnSet) GetInsns(mode iset.Mode, typ iset.Type) []iset.Insn {
    55  	return insnset.modeInsns[mode][typ]
    56  }
    57  
    58  func (insn *Insn) Info() (string, iset.Mode, bool, bool) {
    59  	return insn.Name, 1 << iset.ModeLong64, insn.Pseudo, insn.Priv
    60  }
    61  
    62  func (insn *Insn) Encode(cfg *iset.Config, r *rand.Rand) []byte {
    63  	if insn.Pseudo {
    64  		return insn.Generator(cfg, r)
    65  	}
    66  	ret := make([]byte, 4)
    67  	binary.LittleEndian.PutUint32(ret, insn.AsUInt32)
    68  	return ret
    69  }
    70  
    71  func (insnset *InsnSet) Decode(mode iset.Mode, text []byte) (int, error) {
    72  	if len(text) < 4 {
    73  		return 0, fmt.Errorf("must be at least 4 bytes")
    74  	}
    75  	opcode := binary.LittleEndian.Uint32(text[:4])
    76  	_, err := ParseInsn(opcode)
    77  	if err != nil {
    78  		return 0, fmt.Errorf("failed to decode %x", opcode)
    79  	}
    80  	return 4, nil
    81  }
    82  
    83  func (insnset *InsnSet) DecodeExt(mode iset.Mode, text []byte) (int, error) {
    84  	return 0, fmt.Errorf("no external decoder")
    85  }
    86  
    87  var templates []*Insn
    88  
    89  func (insn *Insn) initFromValue(val uint32) {
    90  	operands := []uint32{}
    91  	for _, field := range insn.Fields {
    92  		extracted := extractBits(val, field.Start, field.Length)
    93  		operands = append(operands, extracted)
    94  	}
    95  	insn.Operands = operands
    96  	insn.AsUInt32 = val
    97  }
    98  
    99  func (insn *Insn) matchesValue(val uint32) bool {
   100  	opcode := val & insn.OpcodeMask
   101  	return opcode == insn.Opcode
   102  }
   103  
   104  func ParseInsn(val uint32) (Insn, error) {
   105  	for _, tmpl := range templates {
   106  		if tmpl.matchesValue(val) {
   107  			newInsn := *tmpl
   108  			newInsn.initFromValue(val)
   109  			return newInsn, nil
   110  		}
   111  	}
   112  	unknown := Insn{
   113  		Name: "unknown",
   114  	}
   115  	return unknown, fmt.Errorf("unrecognized instruction: %08x", val)
   116  }