package ebpftracer import ( "errors" "github.com/coroot/coroot-node-agent/common" "io/ioutil" "runtime" "strings" "debug/elf" "fmt" "path/filepath" "github.com/coroot/coroot-node-agent/proc" "github.com/coroot/coroot-node-agent/utils" "github.com/cilium/ebpf/link" "golang.org/x/arch/arm64/arm64asm" "golang.org/x/arch/x86/x86asm" ) const ( // goServeHTTP = "net/http.serverHandler.ServeHTTP" // binPath = "/root/code/jdk8u/build/linux-x86_64-normal-server-release/jdk/lib/amd64/libnio.so" symbolsocketRead0 = "Java_sun_nio_ch_FileDispatcherImpl_read0" ) func (t *Tracer) AttachJavaNioReadUprobes(pid uint32, insID utils.ID, codeType common.CodeType) []link.Link { if t.disableL7Tracing { return nil } var links []link.Link var bpath = "" // JavaAOT 逻辑 if codeType.IsJavaAotCode() { bpath = proc.Path(uint32(pid), "exe") } else { version := UsePIDToGetJDKVersion(pid) fmt.Println("java version is ", version) bpath = getSoPath(pid, "libnio.so") if bpath == "" { fmt.Println("can,t find the nio.so") return nil } } fmt.Println("find the nio.so path is ", bpath) ex, err := link.OpenExecutable(bpath) if err != nil { return nil } ef, err := elf.Open(bpath) if err != nil { return nil //PID: int(pid), } defer ef.Close() symbols, err := ef.DynamicSymbols() if err != nil { fmt.Println("can not get symbols") if errors.Is(err, elf.ErrNoSymbols) { return nil } return nil } textSection := ef.Section(".text") if textSection == nil { fmt.Println("can not find .text section") return nil } textSectionData, err := textSection.Data() if err != nil { fmt.Println("can not get .text section data", err) return nil } textSectionLen := uint64(len(textSectionData) - 1) // opt := link.UprobeOptions{ // Offset: 61, // } // upread02, err := ex.Uprobe(symbolsocketRead0, t.uprobes["uprobe_ret_Java_sun_nio_ch_FileDispatcherImpl_read0"], &opt) // if err != nil { // return nil // } // links = append(links, upread01) for _, s := range symbols { if elf.ST_TYPE(s.Info) != elf.STT_FUNC || s.Size == 0 { continue } switch s.Name { case symbolsocketRead0: default: continue } address := s.Value for _, p := range ef.Progs { if p.Type != elf.PT_LOAD || (p.Flags&elf.PF_X) == 0 { continue } if p.Vaddr <= s.Value && s.Value < (p.Vaddr+p.Memsz) { address = s.Value - p.Vaddr + p.Off break } } fmt.Println("s.Name-----:", s.Name) switch s.Name { case symbolsocketRead0: sStart := s.Value - textSection.Addr sEnd := sStart + s.Size if sEnd > textSectionLen { continue } sBytes := textSectionData[sStart:sEnd] returnOffsets := getCallNextMoveOffsets(ef.Machine, sBytes) if len(returnOffsets) == 0 { fmt.Println("failed to attach uprobe_ret_Java_sun_nio_ch_FileDispatcherImpl_read0 uprobe") return nil } for _, offset := range returnOffsets { l, err := ex.Uprobe(s.Name, t.uprobes["uprobe_ret_Java_sun_nio_ch_FileDispatcherImpl_read0"], &link.UprobeOptions{Address: address, Offset: uint64(offset), PID: int(pid)}) if err != nil { fmt.Println("failed to attach uprobe_ret_Java_sun_nio_ch_FileDispatcherImpl_read0 uprobe") return nil } links = append(links, l) } } } if len(links) == 0 { return nil } fmt.Println("[jvm] libnio attached, pid is ", pid) return links } func (t *Tracer) AttachJavaNetWriteUprobes(pid uint32, insID utils.ID) []link.Link { if t.disableL7Tracing { return nil } var libnetSo = "/data/roger/graalvm/lib/libnet.so" var sys = "Java_java_net_SocketOutputStream_socketWrite0" var offset = 53 if runtime.GOARCH == "arm64" { libnetSo = "/root/cwlibnet.so" sys = "CW_Java_java_net_SocketOutputStream_socketWrite0" offset = 8 } var links []link.Link ex, err := link.OpenExecutable(libnetSo) if err != nil { return nil } opt := link.UprobeOptions{ Offset: uint64(offset), PID: int(pid), } upread02, err := ex.Uprobe(sys, t.uprobes["uprobe_Java_java_net_SocketOutputStream_socketWrite0"], &opt) if err != nil { return nil } links = append(links, upread02) if len(links) == 0 { return nil } fmt.Println("[jvm] libnet attached", pid) return links } func getCallNextMoveOffsets(machine elf.Machine, instructions []byte) []int { var res []int firstCall := 0 switch machine { case elf.EM_X86_64: for i := 0; i < len(instructions); { ins, err := x86asm.Decode(instructions[i:], 64) if err == nil && ins.Op == x86asm.CALL { if firstCall == 0 { firstCall = 1 } else { i += ins.Len res = append(res, i) return res } } i += ins.Len } case elf.EM_AARCH64: for i := 0; i < len(instructions); { ins, err := arm64asm.Decode(instructions[i:]) if err == nil && ins.Op == arm64asm.BL { if firstCall == 0 { firstCall = 1 } else { i += 4 res = append(res, i) return res } } i += 4 } } return res } func getSoPath(pid uint32, soname string) string { // 获取进程的maps文件路径 mapsPath := fmt.Sprintf("/proc/%d/maps", pid) // 读取maps文件内容 mapsData, err := ioutil.ReadFile(mapsPath) if err != nil { fmt.Println("lookup so error.") return "" } lines := strings.Split(string(mapsData), "\n") for _, line := range lines { fields := strings.Fields(line) if len(fields) >= 6 { perms := fields[1] path := fields[len(fields)-1] if strings.Contains(perms, "x") && filepath.Ext(path) == ".so" { if strings.Contains(path, soname) { return path } } } } return "" }