...

Source file src/github.com/chaos-mesh/chaos-mesh/pkg/ptrace/ptrace_linux_arm64.go

Documentation: github.com/chaos-mesh/chaos-mesh/pkg/ptrace

     1  // Copyright 2021 Chaos Mesh Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  // http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  //go:build cgo
    16  
    17  package ptrace
    18  
    19  import (
    20  	"encoding/binary"
    21  	"syscall"
    22  
    23  	"github.com/pkg/errors"
    24  	"golang.org/x/sys/unix"
    25  )
    26  
    27  var endian = binary.LittleEndian
    28  
    29  const syscallInstrSize = 4
    30  
    31  const nrProcessVMReadv = 270
    32  const nrProcessVMWritev = 271
    33  
    34  // see kernel source /include/uapi/linux/elf.h
    35  const nrPRStatus = 1
    36  
    37  func getIp(regs *syscall.PtraceRegs) uintptr {
    38  	return uintptr(regs.Pc)
    39  }
    40  
    41  func getRegs(pid int, regsout *syscall.PtraceRegs) error {
    42  	err := unix.PtraceGetRegSetArm64(pid, nrPRStatus, (*unix.PtraceRegsArm64)(regsout))
    43  	if err != nil {
    44  		return errors.Wrapf(err, "get registers of process %d", pid)
    45  	}
    46  	return nil
    47  }
    48  
    49  func setRegs(pid int, regs *syscall.PtraceRegs) error {
    50  	err := unix.PtraceSetRegSetArm64(pid, nrPRStatus, (*unix.PtraceRegsArm64)(regs))
    51  	if err != nil {
    52  		return errors.Wrapf(err, "set registers of process %d", pid)
    53  	}
    54  	return nil
    55  }
    56  
    57  // Syscall runs a syscall at main thread of process
    58  func (p *TracedProgram) Syscall(number uint64, args ...uint64) (uint64, error) {
    59  	// save the original registers and the current instructions
    60  	err := p.Protect()
    61  	if err != nil {
    62  		return 0, err
    63  	}
    64  
    65  	var regs syscall.PtraceRegs
    66  
    67  	err = getRegs(p.pid, &regs)
    68  	if err != nil {
    69  		return 0, err
    70  	}
    71  	// set the registers according to the syscall convention. Learn more about
    72  	// it in `man 2 syscall`. In aarch64 the syscall nr is stored in w8, and the
    73  	// arguments are stored in x0, x1, x2, x3, x4, x5 in order
    74  	regs.Regs[8] = number
    75  	for index, arg := range args {
    76  		// All these registers are hard coded for x86 platform
    77  		if index > 6 {
    78  			return 0, errors.New("too many arguments for a syscall")
    79  		} else {
    80  			regs.Regs[index] = arg
    81  		}
    82  	}
    83  	err = setRegs(p.pid, &regs)
    84  	if err != nil {
    85  		return 0, err
    86  	}
    87  
    88  	instruction := make([]byte, syscallInstrSize)
    89  	ip := getIp(p.backupRegs)
    90  
    91  	// most aarch64 devices are little endian
    92  	// 0xd4000001 is `svc #0` to call the system call
    93  	endian.PutUint32(instruction, 0xd4000001)
    94  	_, err = syscall.PtracePokeData(p.pid, ip, instruction)
    95  	if err != nil {
    96  		return 0, errors.Wrapf(err, "writing data %v to %x", instruction, ip)
    97  	}
    98  
    99  	// run one instruction, and stop
   100  	err = p.Step()
   101  	if err != nil {
   102  		return 0, err
   103  	}
   104  
   105  	// read registers, the return value of syscall is stored inside x0 register
   106  	err = getRegs(p.pid, &regs)
   107  	if err != nil {
   108  		return 0, err
   109  	}
   110  
   111  	return regs.Regs[0], p.Restore()
   112  }
   113  
   114  // JumpToFakeFunc writes jmp instruction to jump to fake function
   115  func (p *TracedProgram) JumpToFakeFunc(originAddr uint64, targetAddr uint64) error {
   116  	instructions := make([]byte, 16)
   117  
   118  	// LDR x9, #8
   119  	// BR x9
   120  	// targetAddr
   121  	endian.PutUint32(instructions[0:], 0x58000049)
   122  	endian.PutUint32(instructions[4:], 0xD61F0120)
   123  
   124  	endian.PutUint64(instructions[8:], targetAddr)
   125  
   126  	return p.PtraceWriteSlice(originAddr, instructions)
   127  }
   128