stack.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. package ebpftracer
  2. import (
  3. "context"
  4. "debug/dwarf"
  5. "debug/elf"
  6. debugelf "debug/elf"
  7. "fmt"
  8. "io"
  9. "log"
  10. "os"
  11. "regexp"
  12. "sort"
  13. "strconv"
  14. "github.com/cilium/ebpf"
  15. "github.com/cilium/ebpf/link"
  16. "github.com/coroot/coroot-node-agent/ebpftracer/tracer"
  17. "github.com/coroot/coroot-node-agent/proc"
  18. "golang.org/x/arch/arm64/arm64asm"
  19. "golang.org/x/arch/x86/x86asm"
  20. "golang.org/x/sync/semaphore"
  21. )
  22. type uprobesDef struct {
  23. Name string
  24. Offset uint64
  25. EntAddress uint64
  26. RetAddress uint64
  27. }
  28. func (t *Tracer) stack() error {
  29. if t.disableL7Tracing {
  30. return nil
  31. }
  32. binType := "dotnet"
  33. MatchString := ".*HandleFunc|.*main.*|testfun.*|.*serverHandler.*|.*ServeHTTP.*"
  34. dbgpath := ""
  35. ENV_PID := os.Getenv("FILTER_PID")
  36. WHITE_LIST := os.Getenv("WHITE_LIST")
  37. BIN_TYPE := os.Getenv("BIN_TYPE")
  38. DBG_PATH := os.Getenv("DBG_PATH")
  39. if ENV_PID == "" {
  40. return nil
  41. }
  42. if WHITE_LIST != "" {
  43. MatchString = WHITE_LIST
  44. }
  45. if DBG_PATH != "" {
  46. dbgpath = DBG_PATH
  47. }
  48. if BIN_TYPE != "" {
  49. binType = BIN_TYPE
  50. }
  51. fmt.Println("UprobesMatchString:::init", MatchString)
  52. pid, _ := strconv.ParseInt(ENV_PID, 10, 32)
  53. path := proc.Path(uint32(pid), "exe")
  54. if dbgpath != "" {
  55. t.Uprobes, _ = t.getJavaAOTUprobes(binType, path, dbgpath, MatchString)
  56. } else {
  57. t.Uprobes, _ = t.getUprobes(path, MatchString)
  58. }
  59. t.UprobesMap = map[string]tracer.Uprobe{}
  60. fmt.Println("UprobesMap:::init")
  61. for _, up := range t.Uprobes {
  62. fmt.Println("UprobesMap:::", up.Funcname)
  63. t.UprobesMap[fmt.Sprintf("%s-%s", up.Funcname, up.Address)] = up
  64. }
  65. links := t.attachUprobes(path, t.Uprobes)
  66. t.links = append(t.links, links...)
  67. // defer t.detachUprobes(links)
  68. return nil
  69. }
  70. func (t *Tracer) getJavaAOTUprobes(binType, path string, dbgpath string, MatchString string) ([]tracer.Uprobe, error) {
  71. uprobes := []tracer.Uprobe{}
  72. elfFile, err := elf.Open(path)
  73. funSection := ".text"
  74. if binType == "dotnet" {
  75. funSection = "__managedcode"
  76. }
  77. textSection := elfFile.Section(funSection)
  78. if textSection == nil {
  79. fmt.Println("no text section", nil)
  80. return nil, nil
  81. }
  82. textSectionData, err := textSection.Data()
  83. if err != nil {
  84. fmt.Println("failed to read text section", err)
  85. return nil, nil
  86. }
  87. textSectionLen := uint64(len(textSectionData) - 1)
  88. dwarfFile, err := elf.Open(dbgpath)
  89. if err != nil {
  90. log.Fatal(err)
  91. }
  92. dwarfData, err := dwarfFile.DWARF()
  93. if err != nil {
  94. log.Fatal(err)
  95. }
  96. entryReader := dwarfData.Reader()
  97. // var targetAddress uint64
  98. listEntry := make(map[dwarf.Offset]uprobesDef)
  99. SpecListEntry := []dwarf.Entry{}
  100. for {
  101. entry, err := entryReader.Next()
  102. if err == io.EOF {
  103. // We've reached the end of DWARF entries
  104. break
  105. }
  106. if err != nil {
  107. log.Fatalf("Error reading entry: %v", err)
  108. }
  109. if entry == nil {
  110. log.Println("Warning: a nil entry was returned with no error")
  111. break
  112. }
  113. if entry.Tag == dwarf.TagSubprogram {
  114. // fmt.Printf("entry address: %x, %d\n", entry.Offset, entry.Children)
  115. funName, _ := entry.Val(dwarf.AttrName).(string)
  116. found, _ := regexp.MatchString(MatchString, funName)
  117. if found {
  118. entAddress, _ := entry.Val(dwarf.AttrLowpc).(uint64)
  119. retAddress, _ := entry.Val(dwarf.AttrHighpc).(uint64)
  120. // fmt.Printf("Function %s address: %x, %x\n", funName, address, entry.Offset)
  121. uprobes := uprobesDef{}
  122. uprobes.EntAddress = entAddress
  123. uprobes.RetAddress = retAddress
  124. uprobes.Offset = uint64(entry.Offset)
  125. uprobes.Name = funName
  126. listEntry[entry.Offset] = uprobes
  127. }
  128. specAddr, _ := entry.Val(dwarf.AttrSpecification).(dwarf.Offset)
  129. lowpc := entry.Val(dwarf.AttrLowpc)
  130. if lowpc != nil && specAddr > 0 && lowpc.(uint64) > 0 {
  131. // fmt.Printf("AttrSpecification address: %x, %x\n", specAddr, entry.Offset)
  132. SpecListEntry = append(SpecListEntry, *entry)
  133. }
  134. }
  135. }
  136. for _, v := range SpecListEntry {
  137. specAddr, _ := v.Val(dwarf.AttrSpecification).(dwarf.Offset)
  138. // fmt.Printf("SpecListEntrySpecListEntrySpecListEntry Attach Function: %x\n", specAddr)
  139. _, ok := listEntry[specAddr]
  140. if ok {
  141. vv := listEntry[specAddr]
  142. entAddr := v.Val(dwarf.AttrLowpc)
  143. if entAddr != nil {
  144. vv.EntAddress = entAddr.(uint64)
  145. }
  146. retAddr := v.Val(dwarf.AttrHighpc)
  147. if retAddr != nil {
  148. switch retAddr.(type) {
  149. case uint64:
  150. vv.RetAddress = uint64(retAddr.(uint64))
  151. case int64:
  152. vv.RetAddress = uint64(retAddr.(int64))
  153. default:
  154. fmt.Println("Unknown type")
  155. }
  156. }
  157. listEntry[specAddr] = vv
  158. }
  159. }
  160. for _, v := range listEntry {
  161. fmt.Printf("Need Attach Function %s address: %x, %x\n", v.Name, v.EntAddress, v.RetAddress)
  162. sStart := v.EntAddress - textSection.Addr
  163. sSize := v.RetAddress
  164. if v.RetAddress > v.EntAddress {
  165. sSize = v.RetAddress - v.EntAddress
  166. }
  167. sEnd := sStart + sSize
  168. if sEnd > textSectionLen {
  169. continue
  170. }
  171. sBytes := textSectionData[sStart:sEnd]
  172. rbpOffsets := getRbpEnterOffsets(elfFile.Machine, sBytes)
  173. returnOffsets := getReturnOffsets(elfFile.Machine, sBytes)
  174. if rbpOffsets != 0 {
  175. uprobes = append(uprobes, tracer.Uprobe{
  176. Funcname: v.Name, // 函数名
  177. Location: tracer.AtDotNetEntry, // 入口
  178. Address: v.EntAddress, // 函数地址
  179. AbsOffset: uint64(rbpOffsets), // 函数相对 ELF 偏移
  180. RelOffset: 0, // 函数真实偏移
  181. })
  182. } else {
  183. // 函数入口加入待 attach 列表
  184. uprobes = append(uprobes, tracer.Uprobe{
  185. Funcname: v.Name, // 函数名
  186. Location: tracer.AtEntry, // 入口
  187. Address: v.EntAddress, // 函数地址
  188. AbsOffset: 0, // 函数相对 ELF 偏移
  189. RelOffset: 0, // 函数真实偏移
  190. })
  191. }
  192. for _, offset := range returnOffsets {
  193. uprobes = append(uprobes, tracer.Uprobe{
  194. Funcname: v.Name,
  195. Location: tracer.AtRet,
  196. Address: v.EntAddress,
  197. AbsOffset: uint64(offset),
  198. RelOffset: 0,
  199. })
  200. }
  201. }
  202. return uprobes, nil
  203. }
  204. func (t *Tracer) getUprobes(path string, MatchString string) ([]tracer.Uprobe, error) {
  205. uprobes := []tracer.Uprobe{}
  206. binFile, err := os.Open(path)
  207. if err != nil {
  208. return nil, err
  209. }
  210. // cache := map[string]interface{}{}
  211. // 解析 elf 文件
  212. elfFile, _ := debugelf.NewFile(binFile)
  213. // 获取所有符号表
  214. symbols, _ := elfFile.Symbols()
  215. sort.Slice(symbols, func(i, j int) bool { return symbols[i].Value < symbols[j].Value })
  216. t.Symbols = symbols
  217. // 符号表组装成键值 map,方便使用
  218. symnames := map[string]debugelf.Symbol{}
  219. for _, symbol := range symbols {
  220. fmt.Println(symbol.Name, symbol)
  221. symnames[symbol.Name] = symbol
  222. }
  223. textSection := elfFile.Section(".text")
  224. if textSection == nil {
  225. fmt.Println("no text section", nil)
  226. return nil, nil
  227. }
  228. textSectionData, err := textSection.Data()
  229. if err != nil {
  230. fmt.Println("failed to read text section", err)
  231. return nil, nil
  232. }
  233. textSectionLen := uint64(len(textSectionData) - 1)
  234. // 遍历符号表
  235. for _, symbol := range symbols {
  236. if debugelf.ST_TYPE(symbol.Info) != debugelf.STT_FUNC {
  237. continue
  238. }
  239. // fmt.Println("Hello FunName: ", symbol.Name)
  240. // 使用正则表达式匹配函数白名单列表
  241. found, err := regexp.MatchString(MatchString, symbol.Name)
  242. // found, err := regexp.MatchString("main.*", symbol.Name)
  243. if err != nil {
  244. log.Fatal(err)
  245. }
  246. if found {
  247. // 匹配到了加入 attachFuncs 列表
  248. fmt.Printf("Fuck This: %s, %x\n", symbol.Name, symbol.Value)
  249. // attachFuncs = append(attachFuncs, symbol.Name)
  250. // 根据函数名拿到当前函数的符号结构体
  251. sym := symnames[symbol.Name]
  252. if err != nil {
  253. fmt.Printf("symnames[symbol.Name]", symbol.Name, err)
  254. return nil, err
  255. }
  256. address := sym.Value
  257. for _, p := range elfFile.Progs {
  258. if p.Type != elf.PT_LOAD || (p.Flags&elf.PF_X) == 0 {
  259. continue
  260. }
  261. if p.Vaddr <= sym.Value && sym.Value < (p.Vaddr+p.Memsz) {
  262. address = sym.Value - p.Vaddr + p.Off
  263. break
  264. }
  265. }
  266. // 函数入口加入待 attach 列表
  267. uprobes = append(uprobes, tracer.Uprobe{
  268. Funcname: symbol.Name, // 函数名
  269. Location: tracer.AtEntry, // 入口
  270. Address: address, // 函数地址
  271. AbsOffset: 0, // 函数相对 ELF 偏移
  272. RelOffset: 0, // 函数真实偏移
  273. Wanted: true,
  274. })
  275. sStart := sym.Value - textSection.Addr
  276. sEnd := sStart + sym.Size
  277. if sEnd > textSectionLen {
  278. continue
  279. }
  280. sBytes := textSectionData[sStart:sEnd]
  281. returnOffsets := getReturnOffsets(elfFile.Machine, sBytes)
  282. for _, offset := range returnOffsets {
  283. uprobes = append(uprobes, tracer.Uprobe{
  284. Funcname: symbol.Name,
  285. Location: tracer.AtRet,
  286. Address: address,
  287. AbsOffset: uint64(offset),
  288. RelOffset: 0,
  289. })
  290. }
  291. }
  292. }
  293. return uprobes, nil
  294. }
  295. func (t *Tracer) attachUprobes(path string, uprobes []tracer.Uprobe) []link.Link {
  296. var links []link.Link
  297. ex, err := link.OpenExecutable(path)
  298. if err != nil {
  299. return nil
  300. }
  301. fmt.Println("Attach Start", path)
  302. for i, up := range uprobes {
  303. fmt.Printf("attaching %d -> %d -> %s -> 0x%x -> 0x%x -> 0x%x\n", i, len(uprobes), up.Funcname, up.AbsOffset, up.Address, up.AbsOffset+up.Address)
  304. var prog *ebpf.Program
  305. switch up.Location {
  306. case tracer.AtEntry:
  307. prog = t.uprobes["ent"]
  308. case tracer.AtRet:
  309. prog = t.uprobes["ret"]
  310. case tracer.AtDotNetEntry:
  311. prog = t.uprobes["dotnetent"]
  312. }
  313. uplink, err := ex.Uprobe(up.Funcname, prog, &link.UprobeOptions{Address: up.Address, Offset: up.AbsOffset})
  314. if err != nil {
  315. fmt.Printf("attachingERROR:%v, %v, %v\n", err, up, uplink)
  316. // return nil
  317. } else {
  318. links = append(links, uplink)
  319. }
  320. }
  321. return links
  322. }
  323. func (t *Tracer) detachUprobes(links []link.Link) {
  324. sem := semaphore.NewWeighted(10)
  325. for i, closer := range links {
  326. fmt.Printf("detaching %d/%d\r", i+1, len(links))
  327. sem.Acquire(context.Background(), 1)
  328. go func(closer io.Closer) {
  329. defer sem.Release(1)
  330. closer.Close()
  331. }(closer)
  332. }
  333. fmt.Println()
  334. }
  335. func getRbpEnterOffsets(machine elf.Machine, instructions []byte) int {
  336. switch machine {
  337. case elf.EM_X86_64:
  338. for i := 0; i < len(instructions); {
  339. ins, err := x86asm.Decode(instructions[i:], 64)
  340. if err == nil && ins.Op == x86asm.LEA && ins.Args[0].String() == "RBP" {
  341. fmt.Printf("getRbpEnterOffsets: %v, %s, %s\n", ins, ins.Args[0].String(), ins.Args[1].String())
  342. return i
  343. }
  344. i += ins.Len
  345. }
  346. case elf.EM_AARCH64:
  347. for i := 0; i < len(instructions); {
  348. ins, err := arm64asm.Decode(instructions[i:])
  349. if err == nil && ins.Op == arm64asm.RET {
  350. return i
  351. }
  352. i += 4
  353. }
  354. }
  355. return 0
  356. }