问题起因
编译chaos-mesh v1.0.3 报错
[root@localhost chaos-mesh]# make chaosdaemon
GO15VENDOREXPERIMENT="1" CGO_ENABLED=1 GOOS="" GOARCH="" go build -ldflags '-s -w -X 'github.com/chaos-mesh/chaos-mesh/pkg/version.buildDate=2021-02-20T03:20:41Z' -X 'github.com/chaos-mesh/chaos-mesh/pkg/version.gitCommit=28de66e58ef60056b1f1966527fe85cc2396d670' -X 'github.com/chaos-mesh/chaos-mesh/pkg/version.gitVersion=chart-0.3.3'' -o bin/chaos-daemon ./cmd/chaos-daemon/main.go
# github.com/chaos-mesh/chaos-mesh/pkg/ptrace
pkg/ptrace/ptrace_linux.go:210:61: p.backupRegs.Rip undefined (type *syscall.PtraceRegs has no field or method Rip)
pkg/ptrace/ptrace_linux.go:225:61: p.backupRegs.Rip undefined (type *syscall.PtraceRegs has no field or method Rip)
pkg/ptrace/ptrace_linux.go:261:6: regs.Rax undefined (type syscall.PtraceRegs has no field or method Rax)
pkg/ptrace/ptrace_linux.go:265:8: regs.Rdi undefined (type syscall.PtraceRegs has no field or method Rdi)
pkg/ptrace/ptrace_linux.go:267:8: regs.Rsi undefined (type syscall.PtraceRegs has no field or method Rsi)
pkg/ptrace/ptrace_linux.go:269:8: regs.Rdx undefined (type syscall.PtraceRegs has no field or method Rdx)
pkg/ptrace/ptrace_linux.go:271:8: regs.R10 undefined (type syscall.PtraceRegs has no field or method R10)
pkg/ptrace/ptrace_linux.go:273:8: regs.R8 undefined (type syscall.PtraceRegs has no field or method R8)
pkg/ptrace/ptrace_linux.go:275:8: regs.R9 undefined (type syscall.PtraceRegs has no field or method R9)
pkg/ptrace/ptrace_linux.go:289:61: p.backupRegs.Rip undefined (type *syscall.PtraceRegs has no field or method Rip)
pkg/ptrace/ptrace_linux.go:289:61: too many errors
查看对应的目录
root@sjt-pc:/wk/github.com/chaos-mesh/chaos-mesh/pkg/ptrace# tree
.
├── cwrapper_linux.go
├── ptrace_linux.go
└── ptrace_linux_test.go
参照 golang 的编译约束,这些文件仅在 linux 下生效,但是很明显在 mips 下报错了,说明该文件并不仅仅只有操作系统约束,还应该是架构约束,查看最新版本的 chaos-mesh 对应目录,果然已经加上了架构约束。
root@sjt-pc:/wk/github.com/chaos-mesh/chaos-mesh/pkg/ptrace# tree
.
├── cwrapper_linux.go
├── ptrace_linux_amd64.go
└── ptrace_linux_test.go
那么我们的问题就是写出 ptrace_linux_mips64le.go。
定位
以下分析仍然针对v1.0.3 版本
我们找到两段代码,可以看到 backupRegs 实际上是 *syscall.PtraceRegs
。并且使用了其中的几个变量例如 Rip,Rax
等等。我们需要比对一下 amd64 和 mips64le 对这二者的实现有什么区别。
type TracedProgram struct {
pid int
tids []int
Entries []mapreader.Entry
backupRegs *syscall.PtraceRegs
backupCode []byte
}
func (p *TracedProgram) Protect() error {
err := syscall.PtraceGetRegs(p.pid, p.backupRegs)
if err != nil {
return errors.WithStack(err)
}
_, err = syscall.PtracePeekData(p.pid, uintptr(p.backupRegs.Rip), p.backupCode)
if err != nil {
return errors.WithStack(err)
}
return nil
}
在 golang 源码中分别找到 amd64 和 mips64le 对 syscall.PtraceRegs
的定义,结合 Ptrace 的知识,这里是用户空间中可以访问的寄存器信息。
ztypes_linux_amd64.go
type PtraceRegs struct {
R15 uint64
R14 uint64
R13 uint64
R12 uint64
Rbp uint64
Rbx uint64
R11 uint64
R10 uint64
R9 uint64
R8 uint64
Rax uint64
Rcx uint64
Rdx uint64
Rsi uint64
Rdi uint64
Orig_rax uint64
Rip uint64
Cs uint64
Eflags uint64
Rsp uint64
Ss uint64
Fs_base uint64
Gs_base uint64
Ds uint64
Es uint64
Fs uint64
Gs uint64
}
ztypes_linux_mips64le.go
type PtraceRegs struct {
Regs [102]uint64
U_tsize uint64
U_dsize uint64
U_ssize uint64
Start_code uint64
Start_data uint64
Start_stack uint64
Signal int64
U_ar0 uint64
Magic uint64
U_comm [32]int8
}
回到报错点,在 amd64 下,主要使用了 Rip 寄存器,还有在系统调用时使用的 Rax,Rdi,Rsi,Rdx,R10,R8,R9。这是系统调用时参数传递的固定寄存器。先解决一下系统调用涉及的几个寄存器。对比一下 chaos-mesh 中这段系统调用代码和 golang 系统调用的源码。
func (p *TracedProgram) Syscall(number uint64, args ...uint64) (uint64, error) {
var regs syscall.PtraceRegs
err = syscall.PtraceGetRegs(p.pid, ®s)
if err != nil {
return 0, err
}
regs.Rax = number
for index, arg := range args {
// All these registers are hard coded for x86 platform
if index == 0 {
regs.Rdi = arg
} else if index == 1 {
regs.Rsi = arg
} else if index == 2 {
regs.Rdx = arg
} else if index == 3 {
regs.R10 = arg
} else if index == 4 {
regs.R8 = arg
} else if index == 5 {
regs.R9 = arg
} else {
return 0, fmt.Errorf("too many arguments for a syscall")
}
}
...
src/syscall/asm_linux_amd64.s
TEXT ·Syscall6(SB),NOSPLIT,$0-80
CALL runtime·entersyscall(SB)
MOVQ a1+8(FP), DI
MOVQ a2+16(FP), SI
MOVQ a3+24(FP), DX
MOVQ a4+32(FP), R10
MOVQ a5+40(FP), R8
MOVQ a6+48(FP), R9
MOVQ trap+0(FP), AX // syscall entry
SYSCALL
CMPQ AX, $0xfffffffffffff001
JLS ok6
MOVQ $-1, r1+56(FP)
MOVQ $0, r2+64(FP)
NEGQ AX
MOVQ AX, err+72(FP)
CALL runtime·exitsyscall(SB)
RET
ok6:
MOVQ AX, r1+56(FP)
MOVQ DX, r2+64(FP)
MOVQ $0, err+72(FP)
CALL runtime·exitsyscall(SB)
RET
同理,我们可以找到mips64对于 syscall 的实现
src/syscall/asm_linux_mips64.s
TEXT ·Syscall6(SB),NOSPLIT,$0-80
JAL runtime·entersyscall(SB)
MOVV a1+8(FP), R4
MOVV a2+16(FP), R5
MOVV a3+24(FP), R6
MOVV a4+32(FP), R7
MOVV a5+40(FP), R8
MOVV a6+48(FP), R9
MOVV trap+0(FP), R2 // syscall entry
SYSCALL
BEQ R7, ok6
MOVV $-1, R1
MOVV R1, r1+56(FP) // r1
MOVV R0, r2+64(FP) // r2
MOVV R2, err+72(FP) // errno
JAL runtime·exitsyscall(SB)
RET
ok6:
MOVV R2, r1+56(FP) // r1
MOVV R3, r2+64(FP) // r2
MOVV R0, err+72(FP) // errno
JAL runtime·exitsyscall(SB)
RET
此时,我们便找到了在mips64下 syscall 使用的几个寄存器了,就是四号到九号寄存器 R4-R9。
下一个问题,解决 rip 寄存器。
有疑问加站长微信联系(非本文作者)