2026-08-20 23:57:10 +02:00
|
|
|
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|
|
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
|
|
|
|
|
|
//go:build linux && loong64
|
|
|
|
|
|
|
|
|
|
package debug
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"os"
|
|
|
|
|
"runtime"
|
|
|
|
|
"syscall"
|
|
|
|
|
"unsafe"
|
|
|
|
|
|
|
|
|
|
"sourcedock.dev/petrbalvin/gasm-devkit/asm"
|
|
|
|
|
"sourcedock.dev/petrbalvin/gasm-devkit/parser"
|
2026-08-21 00:03:30 +02:00
|
|
|
"sourcedock.dev/petrbalvin/gasm-devkit/verify"
|
2026-08-20 23:57:10 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// RunTarget is the debuggee entry point (gasm debug --target).
|
|
|
|
|
func RunTarget(asmPath, funcName, argsFile, tmpDir string) error {
|
|
|
|
|
src, err := os.ReadFile(asmPath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("debug target: %w", err)
|
|
|
|
|
}
|
|
|
|
|
file, errs := parser.Parse(asmPath, string(src))
|
|
|
|
|
if len(errs) > 0 {
|
|
|
|
|
return fmt.Errorf("debug target: parse: %v", errs[0])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var img *asm.Image
|
|
|
|
|
switch "loong64" {
|
|
|
|
|
case "arm64":
|
|
|
|
|
img, err = asm.AssembleFileARM64(file)
|
|
|
|
|
case "riscv64":
|
|
|
|
|
img, err = asm.AssembleFileRISCV(file)
|
|
|
|
|
case "loong64":
|
|
|
|
|
img, err = asm.AssembleFileLOONG64(file)
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("debug target: assemble: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var fl *asm.FuncLayout
|
|
|
|
|
for i := range img.Funcs {
|
|
|
|
|
if img.Funcs[i].Name == funcName {
|
|
|
|
|
fl = &img.Funcs[i]
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if fl == nil {
|
|
|
|
|
return fmt.Errorf("debug target: function %q not found", funcName)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
code := img.Bytes()
|
|
|
|
|
exec, err := mapRWX(code)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("debug target: mmap: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
codeBase := uintptr(unsafe.Pointer(&exec[0]))
|
|
|
|
|
if err := os.WriteFile(tmpDir+"/codebase", []byte(fmt.Sprintf("%d", codeBase)), 0o644); err != nil {
|
|
|
|
|
return fmt.Errorf("debug target: write codebase: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
meta := fmt.Sprintf("%d %d %d", fl.Offset, fl.Size, fl.Args)
|
|
|
|
|
os.WriteFile(tmpDir+"/funcmeta", []byte(meta), 0o644)
|
|
|
|
|
|
|
|
|
|
labelsFile, _ := os.Create(tmpDir + "/labels")
|
|
|
|
|
if labelsFile != nil {
|
|
|
|
|
for label, off := range fl.Labels {
|
|
|
|
|
fmt.Fprintf(labelsFile, "%s %d\n", label, off)
|
|
|
|
|
}
|
|
|
|
|
labelsFile.Close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
args, err := os.ReadFile(argsFile)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("debug target: read args: %w", err)
|
|
|
|
|
}
|
|
|
|
|
if len(args) < fl.Args {
|
|
|
|
|
padded := make([]byte, fl.Args)
|
|
|
|
|
copy(padded, args)
|
|
|
|
|
args = padded
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bufSpecFile := tmpDir + "/bufspec"
|
|
|
|
|
if bufSpec, err := os.ReadFile(bufSpecFile); err == nil && len(bufSpec) > 0 {
|
2026-08-29 17:12:53 +02:00
|
|
|
args, err = setupBuffers(string(bufSpec), args, tmpDir)
|
2026-08-20 23:57:10 +02:00
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("debug target: setup buffers: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
runtime.LockOSThread()
|
|
|
|
|
|
|
|
|
|
if _, _, errno := syscall.Syscall(syscall.SYS_PTRACE, uintptr(syscall.PTRACE_TRACEME), 0, 0); errno != 0 {
|
|
|
|
|
return fmt.Errorf("debug target: PTRACE_TRACEME: %v", errno)
|
|
|
|
|
}
|
|
|
|
|
os.WriteFile(tmpDir+"/ready", []byte("ok"), 0o644)
|
|
|
|
|
syscall.Kill(syscall.Getpid(), syscall.SIGSTOP)
|
|
|
|
|
|
|
|
|
|
os.WriteFile(tmpDir+"/entry", []byte("ok"), 0o644)
|
|
|
|
|
syscall.Kill(syscall.Getpid(), syscall.SIGSTOP)
|
|
|
|
|
|
2026-08-21 00:03:30 +02:00
|
|
|
fnAddr := codeBase + uintptr(fl.Offset)
|
|
|
|
|
stackArgs := make([]byte, fl.Args)
|
|
|
|
|
copy(stackArgs, args)
|
|
|
|
|
|
2026-08-30 11:27:54 +02:00
|
|
|
_, callErr := verify.Call(fnAddr, stackArgs)
|
2026-08-21 00:03:30 +02:00
|
|
|
if callErr != nil {
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
os.Exit(0)
|
|
|
|
|
return nil
|
2026-08-20 23:57:10 +02:00
|
|
|
}
|