⬆️ Update dependencies
This commit is contained in:
+537
@@ -0,0 +1,537 @@
|
||||
// Copyright 2026 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build zos
|
||||
|
||||
package unix
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// z/OS ptrace implementation notes:
|
||||
//
|
||||
// This file provides ptrace wrappers for z/OS that match the Linux API as closely
|
||||
// as possible. However, there are fundamental differences in how z/OS implements
|
||||
// process tracing compared to Linux:
|
||||
//
|
||||
// 1. SYSTEM CALL INTERFACE:
|
||||
// - z/OS uses BPX4PTR (5 parameters) vs Linux ptrace syscall (4 parameters)
|
||||
// - The 5th parameter (buffer) is used for block operations
|
||||
//
|
||||
// 2. MEMORY OPERATIONS:
|
||||
// - z/OS: PT_READ_BLOCK/PT_WRITE_BLOCK for efficient block transfers
|
||||
// - Linux: PTRACE_PEEKTEXT/POKETEXT for word-by-word access
|
||||
//
|
||||
// 3. REGISTER OPERATIONS:
|
||||
// - z/OS: PT_READ_GPR/PT_WRITE_GPR with register number for 64-bit values
|
||||
// - Linux: PTRACE_GETREGSET/SETREGSET with iovec structure
|
||||
//
|
||||
// 4. SINGLE-STEPPING:
|
||||
// - z/OS: No native single-step; requires temporary breakpoints or PER
|
||||
// - Linux: PTRACE_SINGLESTEP natively supported
|
||||
// a) Read instruction at current PSW
|
||||
// b) Calculate next instruction address (handle branches)
|
||||
// c) Set temporary breakpoint (SVC 144 = 0x0A90) at next address
|
||||
// d) Continue execution until breakpoint hit
|
||||
// e) Restore original instruction and adjust PSW
|
||||
//
|
||||
// 5. SYSCALL TRACING:
|
||||
// - z/OS: No PTRACE_SYSCALL equivalent; requires breakpoint-based approach
|
||||
// - Linux: PTRACE_SYSCALL stops at syscall entry/exit
|
||||
// a) Identify BPX syscall vector location
|
||||
// b) Set breakpoints at syscall entry points
|
||||
// c) Detect BASR instructions calling into syscall vector
|
||||
// d) Parse arguments from registers before/after syscall
|
||||
//
|
||||
// 6. LIMITATIONS:
|
||||
// - PtraceSetOptions: No equivalent (returns ENOSYS)
|
||||
// - PtraceGetEventMsg: No equivalent (returns ENOSYS)
|
||||
// - PtraceSingleStep: Requires complex implementation (returns ENOSYS)
|
||||
// - PtraceInterrupt: No equivalent (returns ENOSYS)
|
||||
// - PtracePokeUser: No PT_WRITE_U (returns ENOSYS)
|
||||
//
|
||||
// For reference implementations of single-stepping and syscall tracing on z/OS,
|
||||
|
||||
// ptrace is the basic wrapper for BPX4PTR on z/OS.
|
||||
// Note: z/OS ptrace (BPX4PTR) takes 5 parameters vs 4 on Linux/Darwin.
|
||||
// The 5th parameter (buffer) is used for operations like PT_READ_BLOCK/PT_WRITE_BLOCK.
|
||||
//
|
||||
// data is an integer-valued argument (signal number, register index, byte count,
|
||||
// or a target-process address). It is not a Go-managed pointer. We must not pass
|
||||
// it to Bpx4ptr as unsafe.Pointer(data) because the garbage collector would treat
|
||||
// that integer as a pointer and may crash trying to follow it.
|
||||
//
|
||||
// Instead we inline the parms array that Bpx4ptr would construct, placing &data
|
||||
// at parms[3]. BPX4PTR dereferences parms[3] to obtain the data value, so it
|
||||
// receives data unchanged while the GC only ever sees a valid stack pointer.
|
||||
func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) {
|
||||
req32 := int32(request)
|
||||
pid32 := int32(pid)
|
||||
var rv, rc, rn int32
|
||||
var buffer unsafe.Pointer
|
||||
parms := [8]unsafe.Pointer{
|
||||
unsafe.Pointer(&req32),
|
||||
unsafe.Pointer(&pid32),
|
||||
unsafe.Pointer(&addr),
|
||||
unsafe.Pointer(&data), // &data, not unsafe.Pointer(data)
|
||||
unsafe.Pointer(&buffer),
|
||||
unsafe.Pointer(&rv),
|
||||
unsafe.Pointer(&rc),
|
||||
unsafe.Pointer(&rn),
|
||||
}
|
||||
bpxcall(parms[:], BPX4PTR)
|
||||
if rv != 0 {
|
||||
err = errnoErr2(Errno(rc), uintptr(rn))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ptracePtr is a variant that accepts unsafe.Pointer for the data parameter.
|
||||
// addr is a target-process address (a raw integer), not a Go-managed pointer.
|
||||
// We must not pass it to Bpx4ptr as unsafe.Pointer(addr); instead we inline the
|
||||
// parms array and place &addr at parms[2], exactly as ptrace() does for data.
|
||||
func ptracePtr(request int, pid int, addr uintptr, data unsafe.Pointer) (err error) {
|
||||
req32 := int32(request)
|
||||
pid32 := int32(pid)
|
||||
var rv, rc, rn int32
|
||||
var buffer unsafe.Pointer
|
||||
parms := [8]unsafe.Pointer{
|
||||
unsafe.Pointer(&req32),
|
||||
unsafe.Pointer(&pid32),
|
||||
unsafe.Pointer(&addr), // &addr, not unsafe.Pointer(addr)
|
||||
unsafe.Pointer(&data),
|
||||
unsafe.Pointer(&buffer),
|
||||
unsafe.Pointer(&rv),
|
||||
unsafe.Pointer(&rc),
|
||||
unsafe.Pointer(&rn),
|
||||
}
|
||||
bpxcall(parms[:], BPX4PTR)
|
||||
if rv != 0 {
|
||||
err = errnoErr2(Errno(rc), uintptr(rn))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ptracePtrWithBuffer is used for operations that require the buffer parameter.
|
||||
// addr is a target-process address; same &addr treatment as ptracePtr.
|
||||
func ptracePtrWithBuffer(request int, pid int, addr uintptr, data unsafe.Pointer, buffer unsafe.Pointer) (err error) {
|
||||
req32 := int32(request)
|
||||
pid32 := int32(pid)
|
||||
var rv, rc, rn int32
|
||||
parms := [8]unsafe.Pointer{
|
||||
unsafe.Pointer(&req32),
|
||||
unsafe.Pointer(&pid32),
|
||||
unsafe.Pointer(&addr), // &addr, not unsafe.Pointer(addr)
|
||||
unsafe.Pointer(&data),
|
||||
unsafe.Pointer(&buffer),
|
||||
unsafe.Pointer(&rv),
|
||||
unsafe.Pointer(&rc),
|
||||
unsafe.Pointer(&rn),
|
||||
}
|
||||
bpxcall(parms[:], BPX4PTR)
|
||||
if rv != 0 {
|
||||
err = errnoErr2(Errno(rc), uintptr(rn))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// High-level convenience functions matching the Linux/Darwin API
|
||||
|
||||
func PtraceAttach(pid int) (err error) {
|
||||
return ptrace(PT_ATTACH, pid, 0, 0)
|
||||
}
|
||||
|
||||
func PtraceDetach(pid int) (err error) {
|
||||
return ptrace(PT_DETACH, pid, 0, 0)
|
||||
}
|
||||
|
||||
func PtraceCont(pid int, signal int) (err error) {
|
||||
// PT_CONTINUE can be interrupted by signals (EINTR), retry if needed
|
||||
for {
|
||||
err = ptrace(PT_CONTINUE, pid, 0, uintptr(signal))
|
||||
if err != EINTR {
|
||||
break
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func PtraceKill(pid int) (err error) {
|
||||
return ptrace(PT_KILL, pid, 0, 0)
|
||||
}
|
||||
|
||||
// ptracePeek implements the peek operations using PT_READ_BLOCK.
|
||||
// On z/OS, PT_READ_BLOCK can read arbitrary-length data efficiently.
|
||||
// Unlike Linux which reads word-by-word, z/OS can read blocks directly.
|
||||
func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, err error) {
|
||||
if len(out) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// z/OS PT_READ_BLOCK can read blocks directly without word-by-word alignment
|
||||
// The buffer parameter points to the output buffer
|
||||
buffer := unsafe.Pointer(&out[0])
|
||||
err = ptracePtrWithBuffer(req, pid, addr, unsafe.Pointer(uintptr(len(out))), buffer)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(out), nil
|
||||
}
|
||||
|
||||
// PtracePeekText reads from the traced process's text segment.
|
||||
func PtracePeekText(pid int, addr uintptr, out []byte) (count int, err error) {
|
||||
return ptracePeek(PT_READ_BLOCK, pid, addr, out)
|
||||
}
|
||||
|
||||
// PtracePeekData reads from the traced process's data segment.
|
||||
func PtracePeekData(pid int, addr uintptr, out []byte) (count int, err error) {
|
||||
return ptracePeek(PT_READ_BLOCK, pid, addr, out)
|
||||
}
|
||||
|
||||
// PtracePeekUser reads from the traced process's user area.
|
||||
// On z/OS, PT_READ_U reads a 32-bit word from the user area at the specified offset.
|
||||
// To match Linux behavior, we implement word-by-word reading for arbitrary lengths.
|
||||
func PtracePeekUser(pid int, addr uintptr, out []byte) (count int, err error) {
|
||||
if len(out) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// PT_READ_U reads 32-bit words from user area
|
||||
// We need to read word-by-word to handle arbitrary lengths
|
||||
n := 0
|
||||
for n < len(out) {
|
||||
var data uint32
|
||||
err = ptracePtr(PT_READ_U, pid, addr+uintptr(n), unsafe.Pointer(&data))
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Copy up to 4 bytes to output
|
||||
remaining := len(out) - n
|
||||
if remaining >= 4 {
|
||||
out[n] = byte(data >> 24)
|
||||
out[n+1] = byte(data >> 16)
|
||||
out[n+2] = byte(data >> 8)
|
||||
out[n+3] = byte(data)
|
||||
n += 4
|
||||
} else {
|
||||
// Handle partial word at the end
|
||||
for i := 0; i < remaining; i++ {
|
||||
out[n+i] = byte(data >> (24 - i*8))
|
||||
}
|
||||
n += remaining
|
||||
}
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// ptracePoke implements the poke operations using PT_WRITE_BLOCK.
|
||||
func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (count int, err error) {
|
||||
if len(data) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// z/OS PT_WRITE_BLOCK can write blocks directly
|
||||
buffer := unsafe.Pointer(&data[0])
|
||||
err = ptracePtrWithBuffer(pokeReq, pid, addr, unsafe.Pointer(uintptr(len(data))), buffer)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
// PtracePokeText writes to the traced process's text segment.
|
||||
func PtracePokeText(pid int, addr uintptr, data []byte) (count int, err error) {
|
||||
return ptracePoke(PT_WRITE_BLOCK, PT_READ_BLOCK, pid, addr, data)
|
||||
}
|
||||
|
||||
// PtracePokeData writes to the traced process's data segment.
|
||||
func PtracePokeData(pid int, addr uintptr, data []byte) (count int, err error) {
|
||||
return ptracePoke(PT_WRITE_BLOCK, PT_READ_BLOCK, pid, addr, data)
|
||||
}
|
||||
|
||||
// PtracePokeUser writes to the traced process's user area.
|
||||
// Note: z/OS does not have a direct PT_WRITE_U equivalent.
|
||||
// This function returns ENOSYS to indicate it's not supported.
|
||||
// DIFFERENCE FROM LINUX: Linux supports PTRACE_POKEUSR, z/OS does not.
|
||||
func PtracePokeUser(pid int, addr uintptr, data []byte) (count int, err error) {
|
||||
// z/OS doesn't have PT_WRITE_U
|
||||
return 0, ENOSYS
|
||||
}
|
||||
|
||||
// PtraceGetRegs retrieves the general purpose registers from the traced process.
|
||||
// On z/OS, PT_READ_GPR can read individual registers as 64-bit values when called
|
||||
// with a register number, or read all registers when called with a buffer.
|
||||
// This function reads each register individually to ensure full 64-bit values.
|
||||
// DIFFERENCE FROM LINUX: Linux uses PTRACE_GETREGSET with iovec, z/OS uses PT_READ_GPR.
|
||||
func PtraceGetRegs(pid int, regsout *PtraceRegs) (err error) {
|
||||
// Use PT_REGHSET to enable reading high GPRs for AMODE 64 programs
|
||||
rv, rc, rn := Bpx4ptr(int32(PT_REGHSET), int32(pid), nil, nil, nil)
|
||||
_ = rv
|
||||
_ = rc
|
||||
_ = rn
|
||||
|
||||
// Use PT_BLOCKREQ to read GPRs and PSW in one call
|
||||
// dbx uses 3 requests: PT_READ_GPR, PT_READ_U, PT_READ_GPRH
|
||||
// PT_READ_U is required for ptrace to populate PSW in GPR block
|
||||
//
|
||||
// IMPORTANT: z/OS ptrace always returns 64-bit ESA/390 format PSW in the Psw field.
|
||||
// The 128-bit Pswg field is never populated, even with PSWG_Req bit set.
|
||||
// We convert ESA/390 to z/Architecture format in software (matching DBX behavior).
|
||||
const (
|
||||
numRequests = 3
|
||||
reqSize = int(unsafe.Sizeof(PtraceBlkReqReq{}))
|
||||
gprSize = int(unsafe.Sizeof(PtraceBlkGpr{}))
|
||||
uareaSize = int(unsafe.Sizeof(PtraceBlkUar{})) + 6*int(unsafe.Sizeof(PtraceBlkUarOcw{}))
|
||||
totalSize = int(unsafe.Sizeof(PtraceBlkReq{})) + numRequests*reqSize + gprSize*2 + uareaSize
|
||||
)
|
||||
|
||||
// Allocate buffer for block request
|
||||
buf := make([]byte, totalSize)
|
||||
|
||||
// Setup block request header
|
||||
blkReq := (*PtraceBlkReq)(unsafe.Pointer(&buf[0]))
|
||||
blkReq.Numreq = numRequests
|
||||
|
||||
// Setup request array
|
||||
reqOffset := int(unsafe.Sizeof(PtraceBlkReq{}))
|
||||
reqs := (*[3]PtraceBlkReqReq)(unsafe.Pointer(&buf[reqOffset]))
|
||||
|
||||
// Setup GPR block (lower 32 bits)
|
||||
gprOffset := reqOffset + numRequests*reqSize
|
||||
gprBlock := (*PtraceBlkGpr)(unsafe.Pointer(&buf[gprOffset]))
|
||||
|
||||
// Setup user area block (required for PSW)
|
||||
uareaOffset := gprOffset + gprSize
|
||||
uareaBlock := (*PtraceBlkUar)(unsafe.Pointer(&buf[uareaOffset]))
|
||||
uareaBlock.Num = 6 // Number of control info entries
|
||||
|
||||
// Setup user area offset/control words
|
||||
uareaOcw := (*[6]PtraceBlkUarOcw)(unsafe.Pointer(&buf[uareaOffset+int(unsafe.Sizeof(PtraceBlkUar{}))]))
|
||||
uareaOcw[0].Ofs = 1025 // program interrupt code
|
||||
uareaOcw[1].Ofs = 1026 // abend completion code
|
||||
uareaOcw[2].Ofs = 1027 // abend reason code
|
||||
uareaOcw[3].Ofs = 1028 // signal code
|
||||
uareaOcw[4].Ofs = 1029 // instruction length code
|
||||
uareaOcw[5].Ofs = 1030 // process flags
|
||||
|
||||
// Setup high GPR block (upper 32 bits)
|
||||
gprHighOffset := uareaOffset + uareaSize
|
||||
gprHighBlock := (*PtraceBlkGpr)(unsafe.Pointer(&buf[gprHighOffset]))
|
||||
|
||||
// Configure requests (order matters: GPR, U, GPRH)
|
||||
// reqdata contains OFFSET from buffer start, not absolute address
|
||||
// dbx: brr[0].reqdata=(unsigned long)brg; where brg is the offset
|
||||
reqs[0].Reqtype = PT_READ_GPR
|
||||
reqs[0].Reqdata = uint32(gprOffset)
|
||||
reqs[1].Reqtype = PT_READ_U
|
||||
reqs[1].Reqdata = uint32(uareaOffset)
|
||||
reqs[2].Reqtype = PT_READ_GPRH
|
||||
reqs[2].Reqdata = uint32(gprHighOffset)
|
||||
|
||||
// Execute block request
|
||||
rvBlk, rcBlk, rnBlk := Bpx4ptr(int32(PT_BLOCKREQ), int32(pid),
|
||||
unsafe.Pointer(&buf[0]), unsafe.Pointer(uintptr(totalSize)), nil)
|
||||
if rvBlk == -1 {
|
||||
return errnoErr2(Errno(rcBlk), uintptr(rnBlk))
|
||||
}
|
||||
|
||||
// Extract GPRs (combine low and high 32 bits)
|
||||
for i := 0; i < 16; i++ {
|
||||
low := uint64(gprBlock.Gpr[i])
|
||||
high := uint64(gprHighBlock.Gpr[i]) << 32
|
||||
regsout.Gprs[i] = high | low
|
||||
}
|
||||
|
||||
// Extract PSW from GPR block structure
|
||||
// z/OS ptrace ALWAYS returns 8-byte ESA/390 format PSW at offset 144.
|
||||
// The 16-byte PSWG field at offset 152 is NEVER populated (always zero).
|
||||
// We must convert ESA/390 format to z/Architecture format in software (like DBX does).
|
||||
//
|
||||
// ESA/390 PSW format (8 bytes):
|
||||
// Word 1 (bytes 0-3): Mask with bit 12 = 1 (ECMODE31BIT)
|
||||
// Word 2 (bytes 4-7): Address with bit 0 = AMODE bit (1=31-bit, 0=64-bit)
|
||||
//
|
||||
// z/Architecture PSW format (16 bytes):
|
||||
// Mask (bytes 0-7): Extended mask with bit 12 = 0, bits 31-32 = EA+BA (AMODE)
|
||||
// Addr (bytes 8-15): Full 64-bit instruction address
|
||||
|
||||
// Read ESA/390 PSW as two 32-bit words
|
||||
pswWord1 := uint32(gprBlock.Psw[0])<<24 | uint32(gprBlock.Psw[1])<<16 |
|
||||
uint32(gprBlock.Psw[2])<<8 | uint32(gprBlock.Psw[3])
|
||||
pswWord2 := uint32(gprBlock.Psw[4])<<24 | uint32(gprBlock.Psw[5])<<16 |
|
||||
uint32(gprBlock.Psw[6])<<8 | uint32(gprBlock.Psw[7])
|
||||
|
||||
// Convert ESA/390 to z/Architecture format (matching DBX psw_ESA390_to_zArchitecture)
|
||||
const ECMODE31BIT = uint64(0x0000000000080000)
|
||||
const AMODE31BIT = uint64(0x0000000080000000)
|
||||
const ADDR_MASK_31 = uint64(0x000000007FFFFFFF)
|
||||
|
||||
// Extract AMODE bit from address word (bit 0 of word 2)
|
||||
amode31 := uint64(pswWord2) & AMODE31BIT
|
||||
|
||||
// Mask off AMODE bit from address
|
||||
addr := uint64(pswWord2) & ADDR_MASK_31
|
||||
|
||||
// Convert mask: clear ECMODE31BIT, shift left 32 bits, add AMODE bit
|
||||
mask := uint64(pswWord1) & ^ECMODE31BIT
|
||||
mask = (mask << 32) | amode31
|
||||
|
||||
regsout.Psw.Mask = mask
|
||||
regsout.Psw.Addr = addr
|
||||
|
||||
// Note: Access registers (Acrs), floating point registers (Fp_regs),
|
||||
// PER info (Per_info), and other fields would require additional
|
||||
// PT_READ_* calls. For now, we focus on GPRs and PSW which are
|
||||
// the most commonly used registers for debugging.
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ptraceWritePSW writes only the PSW address register.
|
||||
// This is a lower-level function used internally for single-stepping.
|
||||
// Based on ztrace's write_psw function.
|
||||
func ptraceWritePSW(pid int, pswAddr uint32) error {
|
||||
const PTRACE_REG_PSWA = 41
|
||||
rv, rc, rn := Bpx4ptr(int32(PT_WRITE_GPR), int32(pid),
|
||||
unsafe.Pointer(uintptr(PTRACE_REG_PSWA)),
|
||||
unsafe.Pointer(uintptr(pswAddr)),
|
||||
nil)
|
||||
if rv == -1 {
|
||||
return errnoErr2(Errno(rc), uintptr(rn))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PtraceSetRegs sets the general purpose registers in the traced process.
|
||||
// Uses PT_BLOCKREQ to write GPRs and PSW in a single call, matching dbx implementation.
|
||||
// DIFFERENCE FROM LINUX: Linux uses PTRACE_SETREGSET with iovec, z/OS uses PT_BLOCKREQ.
|
||||
// NOTE: The process must be in a stopped state for register writes to succeed.
|
||||
func PtraceSetRegs(pid int, regs *PtraceRegs) (err error) {
|
||||
// Use PT_REGHSET to enable writing high GPRs for AMODE 64 programs
|
||||
rv, rc, rn := Bpx4ptr(int32(PT_REGHSET), int32(pid), nil, nil, nil)
|
||||
_ = rv
|
||||
_ = rc
|
||||
_ = rn
|
||||
|
||||
// Use PT_BLOCKREQ to write GPRs and PSW in one call
|
||||
const (
|
||||
numRequests = 2
|
||||
reqSize = int(unsafe.Sizeof(PtraceBlkReqReq{}))
|
||||
gprSize = int(unsafe.Sizeof(PtraceBlkGpr{}))
|
||||
totalSize = int(unsafe.Sizeof(PtraceBlkReq{})) + numRequests*reqSize + gprSize*2
|
||||
)
|
||||
|
||||
// Allocate buffer for block request
|
||||
buf := make([]byte, totalSize)
|
||||
|
||||
// Setup block request header
|
||||
blkReq := (*PtraceBlkReq)(unsafe.Pointer(&buf[0]))
|
||||
blkReq.Numreq = numRequests
|
||||
|
||||
// Setup request array
|
||||
reqOffset := int(unsafe.Sizeof(PtraceBlkReq{}))
|
||||
reqs := (*[2]PtraceBlkReqReq)(unsafe.Pointer(&buf[reqOffset]))
|
||||
|
||||
// Setup GPR block (lower 32 bits)
|
||||
gprOffset := reqOffset + numRequests*reqSize
|
||||
gprBlock := (*PtraceBlkGpr)(unsafe.Pointer(&buf[gprOffset]))
|
||||
|
||||
// Setup high GPR block (upper 32 bits)
|
||||
gprHighOffset := gprOffset + gprSize
|
||||
gprHighBlock := (*PtraceBlkGpr)(unsafe.Pointer(&buf[gprHighOffset]))
|
||||
|
||||
// Mark all GPRs as modified (set all 16 bits)
|
||||
gprBlock.Writebitflags = 0xFFFF
|
||||
gprHighBlock.Writebitflags = 0xFFFF
|
||||
|
||||
// Split 64-bit GPRs into low and high 32 bits
|
||||
for i := 0; i < 16; i++ {
|
||||
gprBlock.Gpr[i] = uint32(regs.Gprs[i] & 0xFFFFFFFF)
|
||||
gprHighBlock.Gpr[i] = uint32(regs.Gprs[i] >> 32)
|
||||
}
|
||||
|
||||
// Pack PSW for 31-bit targets (current z/OS)
|
||||
// Upper 32 bits = mask, lower 32 bits = address
|
||||
gprBlock.Wpsw = 1 // Mark PSW as modified
|
||||
// Write PSW in old format (8 bytes at offset 144)
|
||||
// Word 1 (mask) at bytes 0-3, Word 2 (addr) at bytes 4-7
|
||||
pswMask := uint32(regs.Psw.Mask)
|
||||
pswAddr := uint32(regs.Psw.Addr)
|
||||
gprBlock.Psw[0] = byte(pswMask >> 24)
|
||||
gprBlock.Psw[1] = byte(pswMask >> 16)
|
||||
gprBlock.Psw[2] = byte(pswMask >> 8)
|
||||
gprBlock.Psw[3] = byte(pswMask)
|
||||
gprBlock.Psw[4] = byte(pswAddr >> 24)
|
||||
gprBlock.Psw[5] = byte(pswAddr >> 16)
|
||||
gprBlock.Psw[6] = byte(pswAddr >> 8)
|
||||
gprBlock.Psw[7] = byte(pswAddr)
|
||||
|
||||
// Clear extended PSWG (not used for 31-bit targets)
|
||||
for i := 0; i < 16; i++ {
|
||||
gprBlock.Pswg[i] = 0
|
||||
}
|
||||
|
||||
// Configure requests
|
||||
// reqdata contains OFFSET from buffer start, not absolute address
|
||||
reqs[0].Reqtype = PT_WRITE_GPR
|
||||
reqs[0].Reqdata = uint32(gprOffset)
|
||||
reqs[1].Reqtype = PT_WRITE_GPRH
|
||||
reqs[1].Reqdata = uint32(gprHighOffset)
|
||||
|
||||
// Execute block request
|
||||
rvBlk, rcBlk, rnBlk := Bpx4ptr(int32(PT_BLOCKREQ), int32(pid),
|
||||
unsafe.Pointer(&buf[0]), unsafe.Pointer(uintptr(totalSize)), nil)
|
||||
if rvBlk == -1 {
|
||||
return errnoErr2(Errno(rcBlk), uintptr(rnBlk))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PtraceSetOptions sets ptrace options.
|
||||
// DIFFERENCE FROM LINUX: z/OS doesn't have PTRACE_SETOPTIONS equivalent.
|
||||
// Returns ENOSYS to indicate this is not supported on z/OS.
|
||||
func PtraceSetOptions(pid int, options int) (err error) {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
// PtraceGetEventMsg retrieves a message about the ptrace event.
|
||||
// DIFFERENCE FROM LINUX: z/OS doesn't have PTRACE_GETEVENTMSG equivalent.
|
||||
// Returns ENOSYS to indicate this is not supported on z/OS.
|
||||
func PtraceGetEventMsg(pid int) (msg uint, err error) {
|
||||
return 0, ENOSYS
|
||||
}
|
||||
|
||||
// PtraceSyscall continues execution and stops at the next syscall entry/exit.
|
||||
// DIFFERENCE FROM LINUX: z/OS doesn't have PTRACE_SYSCALL equivalent.
|
||||
// This function falls back to PT_CONTINUE and will NOT stop at syscalls.
|
||||
//
|
||||
// To trace syscalls on z/OS, applications must:
|
||||
// 1. Set breakpoints at BPX syscall entry points (requires knowledge of syscall vector)
|
||||
// 2. Detect BASR instructions that call into the BPX syscall vector
|
||||
// 3. Use PT_LDINFO to identify loaded modules and their entry points
|
||||
func PtraceSyscall(pid int, signal int) (err error) {
|
||||
// z/OS doesn't have a direct PTRACE_SYSCALL equivalent
|
||||
// Use PT_CONTINUE as a fallback (won't stop at syscalls)
|
||||
return ptrace(PT_CONTINUE, pid, 0, uintptr(signal))
|
||||
}
|
||||
|
||||
// Note: PtraceSingleStep is implemented in ptrace_zos_singlestep.go
|
||||
// with full two-breakpoint emulation for branch instructions.
|
||||
|
||||
// PtraceInterrupt interrupts the traced process.
|
||||
// DIFFERENCE FROM LINUX: z/OS doesn't have PTRACE_INTERRUPT equivalent.
|
||||
// Returns ENOSYS to indicate this is not supported on z/OS.
|
||||
func PtraceInterrupt(pid int) (err error) {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
// PtraceSeize attaches to a process without stopping it.
|
||||
// DIFFERENCE FROM LINUX: z/OS doesn't have PTRACE_SEIZE equivalent.
|
||||
// Uses PT_ATTACH as a fallback, which will stop the process.
|
||||
func PtraceSeize(pid int) (err error) {
|
||||
// z/OS doesn't have PTRACE_SEIZE, use PT_ATTACH
|
||||
// Note: This WILL stop the process, unlike Linux PTRACE_SEIZE
|
||||
return ptrace(PT_ATTACH, pid, 0, 0)
|
||||
}
|
||||
Reference in New Issue
Block a user