gitlab.com/Raven-IO/raven-delve@v1.22.4/pkg/proc/internal/ebpf/helpers.go (about) 1 //go:build linux && amd64 && go1.16 2 3 package ebpf 4 5 import ( 6 "debug/elf" 7 "encoding/binary" 8 "errors" 9 "reflect" 10 "runtime" 11 "sync" 12 "unsafe" 13 14 "gitlab.com/Raven-IO/raven-delve/pkg/dwarf/godwarf" 15 "gitlab.com/Raven-IO/raven-delve/pkg/dwarf/op" 16 17 "github.com/cilium/ebpf" 18 "github.com/cilium/ebpf/link" 19 "github.com/cilium/ebpf/ringbuf" 20 "github.com/cilium/ebpf/rlimit" 21 ) 22 23 //lint:file-ignore U1000 some fields are used by the C program 24 25 // function_parameter_t tracks function_parameter_t from function_vals.bpf.h 26 type function_parameter_t struct { 27 kind uint32 28 size uint32 29 offset int32 30 in_reg bool 31 n_pieces int32 32 reg_nums [6]int32 33 daddr uint64 34 val [0x30]byte 35 deref_val [0x30]byte 36 } 37 38 // function_parameter_list_t tracks function_parameter_list_t from function_vals.bpf.h 39 type function_parameter_list_t struct { 40 goid_offset uint32 41 g_addr_offset uint64 42 goroutine_id uint32 43 fn_addr uint64 44 is_ret bool 45 46 n_parameters uint32 47 params [6]function_parameter_t 48 49 n_ret_parameters uint32 50 ret_params [6]function_parameter_t 51 } 52 53 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -tags "go1.16" -target amd64 trace bpf/trace.bpf.c -- -I./bpf/include 54 55 const FakeAddressBase = 0xbeed000000000000 56 57 type EBPFContext struct { 58 objs *traceObjects 59 bpfEvents chan []byte 60 bpfRingBuf *ringbuf.Reader 61 executable *link.Executable 62 bpfArgMap *ebpf.Map 63 links []link.Link 64 65 parsedBpfEvents []RawUProbeParams 66 m sync.Mutex 67 } 68 69 func (ctx *EBPFContext) Close() { 70 if ctx.objs != nil { 71 ctx.objs.Close() 72 } 73 for _, l := range ctx.links { 74 l.Close() 75 } 76 } 77 78 func (ctx *EBPFContext) AttachUprobe(pid int, name string, offset uint64) error { 79 if ctx.executable == nil { 80 return errors.New("no eBPF program loaded") 81 } 82 l, err := ctx.executable.Uprobe(name, ctx.objs.tracePrograms.UprobeDlvTrace, &link.UprobeOptions{PID: pid, Offset: offset}) 83 ctx.links = append(ctx.links, l) 84 return err 85 } 86 87 func (ctx *EBPFContext) UpdateArgMap(key uint64, goidOffset int64, args []UProbeArgMap, gAddrOffset uint64, isret bool) error { 88 if ctx.bpfArgMap == nil { 89 return errors.New("eBPF map not loaded") 90 } 91 params := createFunctionParameterList(key, goidOffset, args, isret) 92 params.g_addr_offset = gAddrOffset 93 return ctx.bpfArgMap.Update(unsafe.Pointer(&key), unsafe.Pointer(¶ms), ebpf.UpdateAny) 94 } 95 96 func (ctx *EBPFContext) GetBufferedTracepoints() []RawUProbeParams { 97 ctx.m.Lock() 98 defer ctx.m.Unlock() 99 100 if len(ctx.parsedBpfEvents) == 0 { 101 return make([]RawUProbeParams, 0) 102 } 103 104 events := make([]RawUProbeParams, len(ctx.parsedBpfEvents)) 105 copy(events, ctx.parsedBpfEvents) 106 ctx.parsedBpfEvents = ctx.parsedBpfEvents[:0] 107 return events 108 } 109 110 func LoadEBPFTracingProgram(path string) (*EBPFContext, error) { 111 var ( 112 ctx EBPFContext 113 err error 114 objs traceObjects 115 ) 116 117 if err = rlimit.RemoveMemlock(); err != nil { 118 return nil, err 119 } 120 ctx.executable, err = link.OpenExecutable(path) 121 if err != nil { 122 return nil, err 123 } 124 125 if err := loadTraceObjects(&objs, nil); err != nil { 126 return nil, err 127 } 128 ctx.objs = &objs 129 130 ctx.bpfRingBuf, err = ringbuf.NewReader(objs.Events) 131 if err != nil { 132 return nil, err 133 } 134 135 ctx.bpfArgMap = objs.ArgMap 136 137 // TODO(derekparker): This should eventually be moved to a more generalized place. 138 go func() { 139 for { 140 e, err := ctx.bpfRingBuf.Read() 141 if err != nil { 142 return 143 } 144 145 parsed := parseFunctionParameterList(e.RawSample) 146 147 ctx.m.Lock() 148 ctx.parsedBpfEvents = append(ctx.parsedBpfEvents, parsed) 149 ctx.m.Unlock() 150 } 151 }() 152 153 return &ctx, nil 154 } 155 156 func parseFunctionParameterList(rawParamBytes []byte) RawUProbeParams { 157 params := (*function_parameter_list_t)(unsafe.Pointer(&rawParamBytes[0])) 158 159 defer runtime.KeepAlive(params) // Ensure the param is not garbage collected. 160 161 var rawParams RawUProbeParams 162 rawParams.FnAddr = int(params.fn_addr) 163 rawParams.GoroutineID = int(params.goroutine_id) 164 rawParams.IsRet = params.is_ret 165 166 parseParam := func(param function_parameter_t) *RawUProbeParam { 167 iparam := &RawUProbeParam{} 168 data := make([]byte, 0x60) 169 ret := param 170 iparam.Kind = reflect.Kind(ret.kind) 171 172 val := ret.val[:ret.size] 173 rawDerefValue := ret.deref_val[:0x30] 174 copy(data, val) 175 copy(data[0x30:], rawDerefValue) 176 iparam.Data = data 177 178 pieces := make([]op.Piece, 0, 2) 179 pieces = append(pieces, op.Piece{Size: 0x30, Kind: op.AddrPiece, Val: FakeAddressBase}) 180 pieces = append(pieces, op.Piece{Size: 0x30, Kind: op.AddrPiece, Val: FakeAddressBase + 0x30}) 181 iparam.Pieces = pieces 182 183 iparam.Addr = FakeAddressBase 184 185 switch iparam.Kind { 186 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 187 iparam.RealType = &godwarf.UintType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: 8}}} 188 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Bool: 189 iparam.RealType = &godwarf.IntType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: 8}}} 190 case reflect.String: 191 strLen := binary.LittleEndian.Uint64(val[8:]) 192 iparam.Base = FakeAddressBase + 0x30 193 iparam.Len = int64(strLen) 194 } 195 return iparam 196 } 197 198 for i := 0; i < int(params.n_parameters); i++ { 199 rawParams.InputParams = append(rawParams.InputParams, parseParam(params.params[i])) 200 } 201 for i := 0; i < int(params.n_ret_parameters); i++ { 202 rawParams.ReturnParams = append(rawParams.ReturnParams, parseParam(params.ret_params[i])) 203 } 204 205 return rawParams 206 } 207 208 func createFunctionParameterList(entry uint64, goidOffset int64, args []UProbeArgMap, isret bool) function_parameter_list_t { 209 var params function_parameter_list_t 210 params.goid_offset = uint32(goidOffset) 211 params.fn_addr = entry 212 params.is_ret = isret 213 params.n_parameters = 0 214 params.n_ret_parameters = 0 215 for _, arg := range args { 216 var param function_parameter_t 217 param.size = uint32(arg.Size) 218 param.offset = int32(arg.Offset) 219 param.kind = uint32(arg.Kind) 220 if arg.InReg { 221 param.in_reg = true 222 param.n_pieces = int32(len(arg.Pieces)) 223 for i := range arg.Pieces { 224 if i > 5 { 225 break 226 } 227 param.reg_nums[i] = int32(arg.Pieces[i]) 228 } 229 } 230 if !arg.Ret { 231 params.params[params.n_parameters] = param 232 params.n_parameters++ 233 } else { 234 params.ret_params[params.n_ret_parameters] = param 235 params.n_ret_parameters++ 236 } 237 } 238 return params 239 } 240 241 func AddressToOffset(f *elf.File, addr uint64) (uint64, error) { 242 sectionsToSearchForSymbol := []*elf.Section{} 243 244 for i := range f.Sections { 245 if f.Sections[i].Flags == elf.SHF_ALLOC+elf.SHF_EXECINSTR { 246 sectionsToSearchForSymbol = append(sectionsToSearchForSymbol, f.Sections[i]) 247 } 248 } 249 250 var executableSection *elf.Section 251 252 // Find what section the symbol is in by checking the executable section's 253 // addr space. 254 for m := range sectionsToSearchForSymbol { 255 if addr > sectionsToSearchForSymbol[m].Addr && 256 addr < sectionsToSearchForSymbol[m].Addr+sectionsToSearchForSymbol[m].Size { 257 executableSection = sectionsToSearchForSymbol[m] 258 } 259 } 260 261 if executableSection == nil { 262 return 0, errors.New("could not find symbol in executable sections of binary") 263 } 264 265 return uint64(addr - executableSection.Addr + executableSection.Offset), nil 266 }