ptrace_linux_amd64.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. // Copyright The OpenTelemetry Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package ptrace
  15. import (
  16. "encoding/binary"
  17. "syscall"
  18. "github.com/pkg/errors"
  19. )
  20. const syscallInstrSize = 2
  21. func getIP(regs *syscall.PtraceRegs) uintptr {
  22. return uintptr(regs.Rip)
  23. }
  24. func getRegs(pid int, regsout *syscall.PtraceRegs) error {
  25. err := syscall.PtraceGetRegs(pid, regsout)
  26. if err != nil {
  27. return errors.Wrapf(err, "get registers of process %d", pid)
  28. }
  29. return nil
  30. }
  31. func setRegs(pid int, regs *syscall.PtraceRegs) error {
  32. err := syscall.PtraceSetRegs(pid, regs)
  33. if err != nil {
  34. return errors.Wrapf(err, "set registers of process %d", pid)
  35. }
  36. return nil
  37. }
  38. // Syscall runs a syscall at main thread of process.
  39. func (p *TracedProgram) Syscall(number uint64, args ...uint64) (uint64, error) {
  40. // save the original registers and the current instructions
  41. err := p.Protect()
  42. if err != nil {
  43. return 0, err
  44. }
  45. var regs syscall.PtraceRegs
  46. err = getRegs(p.pid, &regs)
  47. if err != nil {
  48. return 0, err
  49. }
  50. // set the registers according to the syscall convention. Learn more about
  51. // it in `man 2 syscall`. In x86_64 the syscall nr is stored in rax
  52. // register, and the arguments are stored in rdi, rsi, rdx, r10, r8, r9 in
  53. // order
  54. regs.Rax = number
  55. for index, arg := range args {
  56. // All these registers are hard coded for x86 platform
  57. if index == 0 {
  58. regs.Rdi = arg
  59. } else if index == 1 {
  60. regs.Rsi = arg
  61. } else if index == 2 {
  62. regs.Rdx = arg
  63. } else if index == 3 {
  64. regs.R10 = arg
  65. } else if index == 4 {
  66. regs.R8 = arg
  67. } else if index == 5 {
  68. regs.R9 = arg
  69. } else {
  70. return 0, errors.New("too many arguments for a syscall")
  71. }
  72. }
  73. err = setRegs(p.pid, &regs)
  74. if err != nil {
  75. return 0, err
  76. }
  77. instruction := make([]byte, syscallInstrSize)
  78. ip := getIP(p.backupRegs)
  79. // set the current instruction (the ip register points to) to the `syscall`
  80. // instruction. In x86_64, the `syscall` instruction is 0x050f.
  81. binary.LittleEndian.PutUint16(instruction, 0x050f)
  82. _, err = syscall.PtracePokeData(p.pid, ip, instruction)
  83. if err != nil {
  84. return 0, errors.Wrapf(err, "writing data %v to %x", instruction, ip)
  85. }
  86. // run one instruction, and stop
  87. err = p.Step()
  88. if err != nil {
  89. return 0, err
  90. }
  91. // read registers, the return value of syscall is stored inside rax register
  92. err = getRegs(p.pid, &regs)
  93. if err != nil {
  94. return 0, err
  95. }
  96. // restore the state saved at beginning.
  97. return regs.Rax, p.Restore()
  98. }