diff --git a/agent/uprobe/dwarf_reader/buf.go b/agent/uprobe/dwarf_reader/buf.go new file mode 100644 index 0000000..7ca30bd --- /dev/null +++ b/agent/uprobe/dwarf_reader/buf.go @@ -0,0 +1,109 @@ +package dwarfreader + +import ( + "debug/dwarf" + "fmt" +) + +// Data buffer being decoded. +type buf struct { + dwarf *dwarf.Data + format dataFormat + name string + off dwarf.Offset + data []byte + Err error +} + +// Data format, other than byte order. This affects the handling of +// certain field formats. +type dataFormat interface { + // DWARF version number. Zero means unknown. + version() int + + // 64-bit DWARF format? + dwarf64() (dwarf64 bool, isKnown bool) + + // Size of an address, in bytes. Zero means unknown. + addrsize() int +} + +// Some parts of DWARF have no data format, e.g., abbrevs. +type UnknownFormat struct{} + +func (u UnknownFormat) version() int { + return 0 +} + +func (u UnknownFormat) dwarf64() (bool, bool) { + return false, false +} + +func (u UnknownFormat) addrsize() int { + return 0 +} + +func MakeBuf(d *dwarf.Data, format dataFormat, name string, off dwarf.Offset, data []byte) buf { + return buf{d, format, name, off, data, nil} +} + +func (b *buf) Uint8() uint8 { + if len(b.data) < 1 { + b.error("underflow") + return 0 + } + val := b.data[0] + b.data = b.data[1:] + b.off++ + return val +} + +// Read a varint, which is 7 bits per byte, little endian. +// the 0x80 bit means read another byte. +func (b *buf) Varint() (c uint64, bits uint) { + for i := 0; i < len(b.data); i++ { + byte := b.data[i] + c |= uint64(byte&0x7F) << bits + bits += 7 + if byte&0x80 == 0 { + b.off += dwarf.Offset(i + 1) + b.data = b.data[i+1:] + return c, bits + } + } + return 0, 0 +} + +// Unsigned int is just a varint. +func (b *buf) Uint() uint64 { + x, _ := b.Varint() + return x +} + +// Signed int is a sign-extended varint. +func (b *buf) Int() int64 { + ux, bits := b.Varint() + x := int64(ux) + if x&(1<<(bits-1)) != 0 { + x |= -1 << bits + } + return x +} + +// AssertEmpty checks that everything has been read from b. +func (b *buf) AssertEmpty() { + if len(b.data) == 0 { + return + } + if len(b.data) > 5 { + b.error(fmt.Sprintf("unexpected extra data: %x...", b.data[0:5])) + } + b.error(fmt.Sprintf("unexpected extra data: %x", b.data)) +} + +func (b *buf) error(s string) { + if b.Err == nil { + b.data = nil + b.Err = dwarf.DecodeError{Name: b.name, Offset: b.off, Err: s} + } +} diff --git a/agent/uprobe/dwarf_reader/reader.go b/agent/uprobe/dwarf_reader/reader.go index 67d55c8..a4e2569 100644 --- a/agent/uprobe/dwarf_reader/reader.go +++ b/agent/uprobe/dwarf_reader/reader.go @@ -190,18 +190,45 @@ func GetStructMemberInfo(structName string, tag dwarf.Tag, memberName string, me } return &memberInfo, nil } - return nil, errors.New(fmt.Sprintf("Could not find member %s in struct %s.", memberName, structName)) + return nil, fmt.Errorf("could not find member %s in struct %s", memberName, structName) } func GetMemberOffset(die *dwarf.Entry) (int64, error) { + const DW_OP_plus_uconst = 0x23 + const DW_OP_consts = 0x11 + const DW_OP_plus = 0x22 + if die.Tag != dwarf.TagMember { panic("not a member") } attr := die.AttrField(dwarf.AttrDataMemberLoc) if attr == nil { - return 0, errors.New(fmt.Sprintf("can't find DW_AT_data_member_location")) + return 0, fmt.Errorf("can't find DW_AT_data_member_location") } - return attr.Val.(int64), nil + var offset int64 + switch val := attr.Val.(type) { + case int64: + return attr.Val.(int64), nil + case []byte: + if len(val) == 0 { + return 0, nil + } + b := MakeBuf(nil, UnknownFormat{}, "location", 0, val) + op := b.Uint8() + switch op { + case DW_OP_plus_uconst: // DW_OP_plus_uconst, Handle opcode sequence [DW_OP_plus_uconst ] + offset = int64(b.Uint()) + case DW_OP_consts: // DW_OP_consts, Handle opcode sequence [DW_OP_consts DW_OP_plus] + offset = b.Int() + op = b.Uint8() + if op != DW_OP_plus { + return -1, fmt.Errorf("unexpected opcode 0x%x exptect: 0x%x", op, DW_OP_plus) + } + default: + return -1, fmt.Errorf("unknown opcode: %x", op) + } + } + return offset, nil } func GetNumPrimitives(die *dwarf.Entry, reader *dwarf.Reader) (int, error) { diff --git a/agent/uprobe/gotls.go b/agent/uprobe/gotls.go index e41e7a5..30337d5 100644 --- a/agent/uprobe/gotls.go +++ b/agent/uprobe/gotls.go @@ -62,7 +62,14 @@ func LoadGoTlsUprobe() error { goTlsObjs = objs return nil } -func AttachGoTlsProbes(pid int) ([]link.Link, error) { +func AttachGoTlsProbes(pid int) (_links []link.Link, e error) { + defer func() { + if r := recover(); r != nil { + common.UprobeLog.Errorf("Recovered in AttachGoTlsProbes: %v", r) + _links = nil + e = fmt.Errorf("attachBpfProgs panic: %v", r) + } + }() if goTlsObjs == nil { return nil, errors.New("GoTlsObjs not loaded") } diff --git a/bpf/loader/loader.go b/bpf/loader/loader.go index dbfc175..5b31a02 100644 --- a/bpf/loader/loader.go +++ b/bpf/loader/loader.go @@ -158,7 +158,7 @@ func (bf *BPF) AttachProgs(options *ac.AgentOptions) error { } bpf.PullProcessExecEvents(options.Ctx, &execEventChannels) - attachOpenSslUprobes(links, options, options.Kv, bf.Objs) + attachUprobes(links, options, options.Kv, bf.Objs) options.LoadPorgressChannel <- "🍕 Attached ssl eBPF programs." } attachSchedProgs(links) @@ -683,7 +683,7 @@ func attachBpfProgs(ifName string, kernelVersion *compatible.KernelVersion, opti return linkList, nil } -func attachOpenSslUprobes(links *list.List, options *ac.AgentOptions, kernelVersion *compatible.KernelVersion, objs any) { +func attachUprobes(links *list.List, options *ac.AgentOptions, kernelVersion *compatible.KernelVersion, objs any) { pids, err := common.GetAllPids() loadGoTlsErr := uprobe.LoadGoTlsUprobe() if loadGoTlsErr != nil {