// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) // SPDX-License-Identifier: BSD-3-Clause //go:build linux package debug import ( "encoding/hex" "fmt" "os" "strconv" "strings" "syscall" "unsafe" ) // mapRWX maps code into a read-write-execute region. func mapRWX(code []byte) ([]byte, error) { const pageSize = 4096 size := (len(code) + pageSize - 1) &^ (pageSize - 1) mem, err := syscall.Mmap(-1, 0, size, syscall.PROT_READ|syscall.PROT_WRITE|syscall.PROT_EXEC, syscall.MAP_PRIVATE|syscall.MAP_ANON) if err != nil { return nil, err } copy(mem, code) return mem, nil } // setupBuffers allocates buffers in the debuggee's memory. func setupBuffers(spec string, args []byte, tmpDir string) ([]byte, error) { type bufSpec struct { name string size int pattern string } var specs []bufSpec for part := range strings.SplitSeq(spec, ",") { fields := strings.SplitN(part, ":", 3) if len(fields) != 3 { continue } size, err := strconv.Atoi(fields[1]) if err != nil || size <= 0 { continue } specs = append(specs, bufSpec{name: fields[0], size: size, pattern: fields[2]}) } if len(specs) == 0 { return args, nil } var bufAddrs []uint64 for _, s := range specs { buf, err := syscall.Mmap(-1, 0, s.size, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_PRIVATE|syscall.MAP_ANON) if err != nil { return nil, fmt.Errorf("mmap buffer %s: %w", s.name, err) } fillBuffer(buf, s.pattern) bufAddrs = append(bufAddrs, uint64(uintptr(unsafe.Pointer(&buf[0])))) } addrFile, err := os.Create(tmpDir + "/bufaddrs") if err != nil { return nil, err } for _, addr := range bufAddrs { fmt.Fprintf(addrFile, "%d\n", addr) } addrFile.Close() return args, nil } // fillBuffer fills a buffer with the specified pattern. func fillBuffer(buf []byte, pattern string) { switch pattern { case "zero": case "ones": for i := range buf { buf[i] = 0xFF } case "seq": for i := range buf { buf[i] = byte(i) } default: if data, err := hex.DecodeString(pattern); err == nil && len(data) > 0 { for i := range buf { buf[i] = data[i%len(data)] } } } }