Skip to content

Commit

Permalink
delve: add linux-riscv64 support
Browse files Browse the repository at this point in the history
  • Loading branch information
lrzlin committed Jul 29, 2024
1 parent b9f50fe commit b32dcc0
Show file tree
Hide file tree
Showing 36 changed files with 5,456 additions and 53 deletions.
7 changes: 7 additions & 0 deletions Documentation/backend_test_health.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,19 @@ Tests skipped by each supported backend:
* 1 broken in linux ppc64le
* linux/ppc64le/native/pie skipped = 3
* 3 broken - pie mode
* linux/riscv64 skipped = 2
* 1 broken - cgo stacktraces
* 1 not working on linux/riscv64
* pie skipped = 2
* 2 upstream issue - https://github.com/golang/go/issues/29322
* ppc64le skipped = 12
* 6 broken
* 1 broken - global variable symbolication
* 5 not implemented
* riscv64 skipped = 6
* 2 broken
* 1 broken - global variable symbolication
* 3 not implemented
* windows skipped = 7
* 1 broken
* 2 not working on windows
Expand Down
7 changes: 7 additions & 0 deletions _fixtures/asmnilptr/main_riscv64.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include "textflag.h"

TEXT ·asmFunc(SB),0,$0-16
MOV arg+0(FP), R5
MOV (R5), R5
MOV R5, ret+8(FP)
RET
2 changes: 2 additions & 0 deletions _fixtures/cgostacktest/hello.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#else
#define BREAKPOINT asm("brk 0;")
#endif
#elif __riscv
#define BREAKPOINT asm("ebreak;")
#endif

void helloworld_pt2(int x) {
Expand Down
4 changes: 3 additions & 1 deletion _fixtures/testvariablescgo/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#else
#define BREAKPOINT asm("brk 0;")
#endif
#elif __riscv
#define BREAKPOINT asm("ebreak;")
#endif

#define N 100
Expand All @@ -37,6 +39,6 @@ void testfn(void) {
strcpy(s, s0);

BREAKPOINT;

printf("%s %s %p %p\n", s, longstring, v, v_align_check);
}
4 changes: 3 additions & 1 deletion _scripts/make.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ func NewMakeCommands() *cobra.Command {
envflags = append(envflags, "GOOS="+OS)
}
if len(envflags) > 0 {
// Add -mod=vendor to envflags due to riscv arch package has not upstream yet.
envflags = append(envflags, "-mod=vendor")
executeEnv(envflags, "go", "build", "-ldflags", "-extldflags -static", tagFlags(), buildFlags(), DelveMainPackagePath)
} else {
execute("go", "build", "-ldflags", "-extldflags -static", tagFlags(), buildFlags(), DelveMainPackagePath)
Expand Down Expand Up @@ -112,7 +114,7 @@ This option can only be specified if testset is basic or a single package.`)
test.PersistentFlags().StringVarP(&TestBuildMode, "test-build-mode", "m", "", `Runs tests compiling with the specified build mode, one of either:
normal normal buildmode (default)
pie PIE buildmode
This option can only be specified if testset is basic or a single package.`)
test.PersistentFlags().BoolVarP(&TestIncludePIE, "pie", "", true, "Standard testing should include PIE")

Expand Down
4 changes: 2 additions & 2 deletions cmd/dlv/dlv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ func getDlvBinEBPF(t *testing.T) string {

func getDlvBinInternal(t *testing.T, goflags ...string) string {
dlvbin := filepath.Join(t.TempDir(), "dlv.exe")
args := append([]string{"build", "-o", dlvbin}, goflags...)
args := append([]string{"build", "-mod=vendor", "-o", dlvbin}, goflags...)
args = append(args, "github.com/go-delve/delve/cmd/dlv")

out, err := exec.Command("go", args...).CombinedOutput()
Expand Down Expand Up @@ -411,7 +411,7 @@ func TestGeneratedDoc(t *testing.T) {

// Checks gen-usage-docs.go
tempDir := t.TempDir()
cmd := exec.Command("go", "run", "_scripts/gen-usage-docs.go", tempDir)
cmd := exec.Command("go", "run", "-mod=vendor", "_scripts/gen-usage-docs.go", tempDir)
cmd.Dir = projectRoot()
err := cmd.Run()
assertNoError(err, t, "go run _scripts/gen-usage-docs.go")
Expand Down
92 changes: 92 additions & 0 deletions pkg/dwarf/regnum/riscv64.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package regnum

import "fmt"

// The mapping between hardware registers and DWARF registers, See
// https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-dwarf.adoc

const (
// Integer Registers
RISCV64_X0 = 0
// Link Register
RISCV64_LR = 1
// Stack Pointer
RISCV64_SP = 2
RISCV64_GP = 3
RISCV64_TP = 4
RISCV64_T0 = 5
RISCV64_T1 = 6
RISCV64_T2 = 7
RISCV64_S0 = 8
// Frame Pointer
RISCV64_FP = RISCV64_S0
RISCV64_S1 = 9
RISCV64_A0 = 10
RISCV64_A1 = 11
RISCV64_A2 = 12
RISCV64_A3 = 13
RISCV64_A4 = 14
RISCV64_A5 = 15
RISCV64_A6 = 16
RISCV64_A7 = 17
RISCV64_S2 = 18
RISCV64_S3 = 19
RISCV64_S4 = 20
RISCV64_S5 = 21
RISCV64_S6 = 22
RISCV64_S7 = 23
RISCV64_S8 = 24
RISCV64_S9 = 25
RISCV64_S10 = 26
// G Register
RISCV64_S11 = 27
RISCV64_T3 = 28
RISCV64_T4 = 29
RISCV64_T5 = 30
RISCV64_T6 = 31

RISCV64_X31 = RISCV64_T6

// Floating-point Registers
RISCV64_F0 = 32
RISCV64_F31 = 63

// Not defined in DWARF specification
RISCV64_PC = 65

_RISC64_MaxRegNum = RISCV64_PC
)

func RISCV64ToName(num uint64) string {
switch {
case num <= RISCV64_X31:
return fmt.Sprintf("X%d", num)

case num >= RISCV64_F0 && num <= RISCV64_F31:
return fmt.Sprintf("F%d", num)

case num == RISCV64_PC:
return fmt.Sprintf("PC")

default:
return fmt.Sprintf("Unknown%d", num)
}
}

func RISCV64MaxRegNum() uint64 {
return _RISC64_MaxRegNum
}

var RISCV64NameToDwarf = func() map[string]int {
r := make(map[string]int)
for i := 0; i <= 31; i++ {
r[fmt.Sprintf("x%d", i)] = RISCV64_X0 + i
}
r[fmt.Sprintf("pc")] = RISCV64_PC

for i := 0; i <= 31; i++ {
r[fmt.Sprintf("f%d", i)] = RISCV64_F0 + i
}

return r
}()
2 changes: 1 addition & 1 deletion pkg/gobuild/gobuild.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func goBuildArgs(debugname string, pkgs []string, buildflags string, isTest bool

args = append(args, "-o", debugname)
if isTest {
args = append([]string{"-c"}, args...)
args = append([]string{"-c", "-mod=vendor"}, args...)
}
args = append(args, "-gcflags", "all=-N -l")
if buildflags != "" {
Expand Down
6 changes: 6 additions & 0 deletions pkg/proc/bininfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ var (
elf.EM_AARCH64: true,
elf.EM_386: true,
elf.EM_PPC64: true,
elf.EM_RISCV: true,
}

supportedWindowsArch = map[_PEMachine]bool{
Expand Down Expand Up @@ -802,6 +803,8 @@ func NewBinaryInfo(goos, goarch string) *BinaryInfo {
r.Arch = ARM64Arch(goos)
case "ppc64le":
r.Arch = PPC64LEArch(goos)
case "riscv64":
r.Arch = RISCV64Arch(goos)
}
return r
}
Expand Down Expand Up @@ -1803,6 +1806,9 @@ func (bi *BinaryInfo) setGStructOffsetElf(image *Image, exe *elf.File, wg *sync.
case elf.EM_PPC64:
_ = getSymbol(image, bi.logger, exe, "runtime.tls_g")

case elf.EM_RISCV:
_ = getSymbol(image, bi.logger, exe, "runtime.tls_g")

default:
// we should never get here
panic("architecture not supported")
Expand Down
56 changes: 56 additions & 0 deletions pkg/proc/core/linux_core.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const _NT_FPREGSET elf.NType = 0x2
const (
_EM_AARCH64 = 183
_EM_X86_64 = 62
_EM_RISCV = 243
_ARM_FP_HEADER_START = 512
)

Expand All @@ -49,6 +50,8 @@ func linuxThreadsFromNotes(p *process, notes []*note, machineType elf.Machine) p
var currentThread proc.Thread
var lastThreadAMD *linuxAMD64Thread
var lastThreadARM *linuxARM64Thread
var lastThreadRISCV *linuxRISCV64Thread

for _, note := range notes {
switch note.Type {
case elf.NT_PRSTATUS:
Expand All @@ -66,12 +69,23 @@ func linuxThreadsFromNotes(p *process, notes []*note, machineType elf.Machine) p
if currentThread == nil {
currentThread = p.Threads[int(t.Pid)]
}
} else if machineType == _EM_RISCV {
t := note.Desc.(*linuxPrStatusRISCV64)
lastThreadRISCV = &linuxRISCV64Thread{linutil.RISCV64Registers{Regs: &t.Reg}, t}
p.Threads[int(t.Pid)] = &thread{lastThreadRISCV, p, proc.CommonThread{}}
if currentThread == nil {
currentThread = p.Threads[int(t.Pid)]
}
}
case _NT_FPREGSET:
if machineType == _EM_AARCH64 {
if lastThreadARM != nil {
lastThreadARM.regs.Fpregs = note.Desc.(*linutil.ARM64PtraceFpRegs).Decode()
}
} else if machineType == _EM_RISCV {
if lastThreadRISCV != nil {
lastThreadRISCV.regs.Fpregs = note.Desc.(*linutil.RISCV64PtraceFpRegs).Decode()
}
}
case _NT_X86_XSTATE:
if machineType == _EM_X86_64 {
Expand Down Expand Up @@ -147,6 +161,8 @@ func readLinuxOrPlatformIndependentCore(corePath, exePath string) (*process, pro
bi = proc.NewBinaryInfo("linux", "amd64")
case _EM_AARCH64:
bi = proc.NewBinaryInfo("linux", "arm64")
case _EM_RISCV:
bi = proc.NewBinaryInfo("linux", "riscv64")
default:
return nil, nil, errors.New("unsupported machine type")
}
Expand Down Expand Up @@ -181,6 +197,11 @@ type linuxARM64Thread struct {
t *linuxPrStatusARM64
}

type linuxRISCV64Thread struct {
regs linutil.RISCV64Registers
t *linuxPrStatusRISCV64
}

func (t *linuxAMD64Thread) registers() (proc.Registers, error) {
var r linutil.AMD64Registers
r.Regs = t.regs.Regs
Expand All @@ -195,6 +216,13 @@ func (t *linuxARM64Thread) registers() (proc.Registers, error) {
return &r, nil
}

func (t *linuxRISCV64Thread) registers() (proc.Registers, error) {
var r linutil.RISCV64Registers
r.Regs = t.regs.Regs
r.Fpregs = t.regs.Fpregs
return &r, nil
}

func (t *linuxAMD64Thread) pid() int {
return int(t.t.Pid)
}
Expand All @@ -203,6 +231,10 @@ func (t *linuxARM64Thread) pid() int {
return int(t.t.Pid)
}

func (t *linuxRISCV64Thread) pid() int {
return int(t.t.Pid)
}

// Note is a note from the PT_NOTE prog.
// Relevant types:
// - NT_FILE: File mapping information, e.g. program text mappings. Desc is a LinuxNTFile.
Expand Down Expand Up @@ -286,6 +318,8 @@ func readNote(r io.ReadSeeker, machineType elf.Machine) (*note, error) {
note.Desc = &linuxPrStatusAMD64{}
case _EM_AARCH64:
note.Desc = &linuxPrStatusARM64{}
case _EM_RISCV:
note.Desc = &linuxPrStatusRISCV64{}
default:
return nil, errors.New("unsupported machine type")
}
Expand Down Expand Up @@ -333,6 +367,15 @@ func readNote(r io.ReadSeeker, machineType elf.Machine) (*note, error) {
}
note.Desc = fpregs
}

if machineType == _EM_RISCV {
fpregs := &linutil.RISCV64PtraceFpRegs{}
rdr := bytes.NewReader(desc)
if err := binary.Read(rdr, binary.LittleEndian, fpregs.Byte()); err != nil {
return nil, err
}
note.Desc = fpregs
}
}
if err := skipPadding(r, 4); err != nil {
return nil, fmt.Errorf("aligning after desc: %v", err)
Expand Down Expand Up @@ -446,6 +489,19 @@ type linuxPrStatusARM64 struct {
Fpvalid int32
}

// LinuxPrStatusRISCV64 is a copy of the prstatus kernel struct.
type linuxPrStatusRISCV64 struct {
Siginfo linuxSiginfo
Cursig uint16
_ [2]uint8
Sigpend uint64
Sighold uint64
Pid, Ppid, Pgrp, Sid int32
Utime, Stime, CUtime, CStime linuxCoreTimeval
Reg linutil.RISCV64PtraceRegs
Fpvalid int32
}

// LinuxSiginfo is a copy of the
// siginfo kernel struct.
type linuxSiginfo struct {
Expand Down
2 changes: 2 additions & 0 deletions pkg/proc/dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ func (t *Target) Dump(out elfwriter.WriteCloserSeeker, flags DumpFlags, state *D
fhdr.Machine = elf.EM_AARCH64
case "ppc64le":
fhdr.Machine = elf.EM_PPC64
case "riscv64":
fhdr.Machine = elf.EM_RISCV
default:
panic("not implemented")
}
Expand Down
Loading

0 comments on commit b32dcc0

Please sign in to comment.