support cgroup2
* only shim v2 runc v2 ("io.containerd.runc.v2") is supported
* only PID metrics is implemented. Others should be implemented in separate PRs.
* lots of code duplication in v1 metrics and v2 metrics. Dedupe should be separate PR.
Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
			
			
This commit is contained in:
		
							
								
								
									
										392
									
								
								vendor/github.com/cilium/ebpf/elf_reader.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										392
									
								
								vendor/github.com/cilium/ebpf/elf_reader.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,392 @@ | ||||
| package ebpf | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"debug/elf" | ||||
| 	"encoding/binary" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/cilium/ebpf/asm" | ||||
|  | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
|  | ||||
| type elfCode struct { | ||||
| 	*elf.File | ||||
| 	symbols           []elf.Symbol | ||||
| 	symbolsPerSection map[elf.SectionIndex]map[uint64]string | ||||
| } | ||||
|  | ||||
| // LoadCollectionSpec parses an ELF file into a CollectionSpec. | ||||
| func LoadCollectionSpec(file string) (*CollectionSpec, error) { | ||||
| 	f, err := os.Open(file) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer f.Close() | ||||
|  | ||||
| 	spec, err := LoadCollectionSpecFromReader(f) | ||||
| 	return spec, errors.Wrapf(err, "file %s", file) | ||||
| } | ||||
|  | ||||
| // LoadCollectionSpecFromReader parses an ELF file into a CollectionSpec. | ||||
| func LoadCollectionSpecFromReader(code io.ReaderAt) (*CollectionSpec, error) { | ||||
| 	f, err := elf.NewFile(code) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer f.Close() | ||||
|  | ||||
| 	symbols, err := f.Symbols() | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, "load symbols") | ||||
| 	} | ||||
|  | ||||
| 	ec := &elfCode{f, symbols, symbolsPerSection(symbols)} | ||||
|  | ||||
| 	var licenseSection, versionSection *elf.Section | ||||
| 	progSections := make(map[elf.SectionIndex]*elf.Section) | ||||
| 	relSections := make(map[elf.SectionIndex]*elf.Section) | ||||
| 	mapSections := make(map[elf.SectionIndex]*elf.Section) | ||||
| 	for i, sec := range ec.Sections { | ||||
| 		switch { | ||||
| 		case strings.HasPrefix(sec.Name, "license"): | ||||
| 			licenseSection = sec | ||||
| 		case strings.HasPrefix(sec.Name, "version"): | ||||
| 			versionSection = sec | ||||
| 		case strings.HasPrefix(sec.Name, "maps"): | ||||
| 			mapSections[elf.SectionIndex(i)] = sec | ||||
| 		case sec.Type == elf.SHT_REL: | ||||
| 			if int(sec.Info) >= len(ec.Sections) { | ||||
| 				return nil, errors.Errorf("found relocation section %v for missing section %v", i, sec.Info) | ||||
| 			} | ||||
|  | ||||
| 			// Store relocations under the section index of the target | ||||
| 			idx := elf.SectionIndex(sec.Info) | ||||
| 			if relSections[idx] != nil { | ||||
| 				return nil, errors.Errorf("section %d has multiple relocation sections", idx) | ||||
| 			} | ||||
| 			relSections[idx] = sec | ||||
| 		case sec.Type == elf.SHT_PROGBITS && (sec.Flags&elf.SHF_EXECINSTR) != 0 && sec.Size > 0: | ||||
| 			progSections[elf.SectionIndex(i)] = sec | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	license, err := loadLicense(licenseSection) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, "load license") | ||||
| 	} | ||||
|  | ||||
| 	version, err := loadVersion(versionSection, ec.ByteOrder) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, "load version") | ||||
| 	} | ||||
|  | ||||
| 	maps, err := ec.loadMaps(mapSections) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, "load maps") | ||||
| 	} | ||||
|  | ||||
| 	progs, libs, err := ec.loadPrograms(progSections, relSections, license, version) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.Wrap(err, "load programs") | ||||
| 	} | ||||
|  | ||||
| 	if len(libs) > 0 { | ||||
| 		for name, prog := range progs { | ||||
| 			prog.Instructions, err = link(prog.Instructions, libs...) | ||||
| 			if err != nil { | ||||
| 				return nil, errors.Wrapf(err, "program %s", name) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return &CollectionSpec{maps, progs}, nil | ||||
| } | ||||
|  | ||||
| func loadLicense(sec *elf.Section) (string, error) { | ||||
| 	if sec == nil { | ||||
| 		return "", errors.Errorf("missing license section") | ||||
| 	} | ||||
| 	data, err := sec.Data() | ||||
| 	if err != nil { | ||||
| 		return "", errors.Wrapf(err, "section %s", sec.Name) | ||||
| 	} | ||||
| 	return string(bytes.TrimRight(data, "\000")), nil | ||||
| } | ||||
|  | ||||
| func loadVersion(sec *elf.Section, bo binary.ByteOrder) (uint32, error) { | ||||
| 	if sec == nil { | ||||
| 		return 0, nil | ||||
| 	} | ||||
|  | ||||
| 	var version uint32 | ||||
| 	err := binary.Read(sec.Open(), bo, &version) | ||||
| 	return version, errors.Wrapf(err, "section %s", sec.Name) | ||||
| } | ||||
|  | ||||
| func (ec *elfCode) loadPrograms(progSections, relSections map[elf.SectionIndex]*elf.Section, license string, version uint32) (map[string]*ProgramSpec, []asm.Instructions, error) { | ||||
| 	var ( | ||||
| 		progs = make(map[string]*ProgramSpec) | ||||
| 		libs  []asm.Instructions | ||||
| 	) | ||||
| 	for idx, prog := range progSections { | ||||
| 		syms := ec.symbolsPerSection[idx] | ||||
| 		if len(syms) == 0 { | ||||
| 			return nil, nil, errors.Errorf("section %v: missing symbols", prog.Name) | ||||
| 		} | ||||
|  | ||||
| 		funcSym := syms[0] | ||||
| 		if funcSym == "" { | ||||
| 			return nil, nil, errors.Errorf("section %v: no label at start", prog.Name) | ||||
| 		} | ||||
|  | ||||
| 		rels, err := ec.loadRelocations(relSections[idx]) | ||||
| 		if err != nil { | ||||
| 			return nil, nil, errors.Wrapf(err, "program %s: can't load relocations", funcSym) | ||||
| 		} | ||||
|  | ||||
| 		insns, err := ec.loadInstructions(prog, syms, rels) | ||||
| 		if err != nil { | ||||
| 			return nil, nil, errors.Wrapf(err, "program %s: can't unmarshal instructions", funcSym) | ||||
| 		} | ||||
|  | ||||
| 		if progType, attachType := getProgType(prog.Name); progType == UnspecifiedProgram { | ||||
| 			// There is no single name we can use for "library" sections, | ||||
| 			// since they may contain multiple functions. We'll decode the | ||||
| 			// labels they contain later on, and then link sections that way. | ||||
| 			libs = append(libs, insns) | ||||
| 		} else { | ||||
| 			progs[funcSym] = &ProgramSpec{ | ||||
| 				Name:          funcSym, | ||||
| 				Type:          progType, | ||||
| 				AttachType:    attachType, | ||||
| 				License:       license, | ||||
| 				KernelVersion: version, | ||||
| 				Instructions:  insns, | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return progs, libs, nil | ||||
| } | ||||
|  | ||||
| func (ec *elfCode) loadInstructions(section *elf.Section, symbols, relocations map[uint64]string) (asm.Instructions, error) { | ||||
| 	var ( | ||||
| 		r      = section.Open() | ||||
| 		insns  asm.Instructions | ||||
| 		ins    asm.Instruction | ||||
| 		offset uint64 | ||||
| 	) | ||||
| 	for { | ||||
| 		n, err := ins.Unmarshal(r, ec.ByteOrder) | ||||
| 		if err == io.EOF { | ||||
| 			return insns, nil | ||||
| 		} | ||||
| 		if err != nil { | ||||
| 			return nil, errors.Wrapf(err, "offset %d", offset) | ||||
| 		} | ||||
|  | ||||
| 		ins.Symbol = symbols[offset] | ||||
| 		ins.Reference = relocations[offset] | ||||
|  | ||||
| 		insns = append(insns, ins) | ||||
| 		offset += n | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (ec *elfCode) loadMaps(mapSections map[elf.SectionIndex]*elf.Section) (map[string]*MapSpec, error) { | ||||
| 	var ( | ||||
| 		maps = make(map[string]*MapSpec) | ||||
| 		b    = make([]byte, 1) | ||||
| 	) | ||||
| 	for idx, sec := range mapSections { | ||||
| 		syms := ec.symbolsPerSection[idx] | ||||
| 		if len(syms) == 0 { | ||||
| 			return nil, errors.Errorf("section %v: no symbols", sec.Name) | ||||
| 		} | ||||
|  | ||||
| 		if sec.Size%uint64(len(syms)) != 0 { | ||||
| 			return nil, errors.Errorf("section %v: map descriptors are not of equal size", sec.Name) | ||||
| 		} | ||||
|  | ||||
| 		var ( | ||||
| 			r    = sec.Open() | ||||
| 			size = sec.Size / uint64(len(syms)) | ||||
| 		) | ||||
| 		for i, offset := 0, uint64(0); i < len(syms); i, offset = i+1, offset+size { | ||||
| 			mapSym := syms[offset] | ||||
| 			if mapSym == "" { | ||||
| 				fmt.Println(syms) | ||||
| 				return nil, errors.Errorf("section %s: missing symbol for map at offset %d", sec.Name, offset) | ||||
| 			} | ||||
|  | ||||
| 			if maps[mapSym] != nil { | ||||
| 				return nil, errors.Errorf("section %v: map %v already exists", sec.Name, mapSym) | ||||
| 			} | ||||
|  | ||||
| 			lr := io.LimitReader(r, int64(size)) | ||||
|  | ||||
| 			var spec MapSpec | ||||
| 			switch { | ||||
| 			case binary.Read(lr, ec.ByteOrder, &spec.Type) != nil: | ||||
| 				return nil, errors.Errorf("map %v: missing type", mapSym) | ||||
| 			case binary.Read(lr, ec.ByteOrder, &spec.KeySize) != nil: | ||||
| 				return nil, errors.Errorf("map %v: missing key size", mapSym) | ||||
| 			case binary.Read(lr, ec.ByteOrder, &spec.ValueSize) != nil: | ||||
| 				return nil, errors.Errorf("map %v: missing value size", mapSym) | ||||
| 			case binary.Read(lr, ec.ByteOrder, &spec.MaxEntries) != nil: | ||||
| 				return nil, errors.Errorf("map %v: missing max entries", mapSym) | ||||
| 			case binary.Read(lr, ec.ByteOrder, &spec.Flags) != nil: | ||||
| 				return nil, errors.Errorf("map %v: missing flags", mapSym) | ||||
| 			} | ||||
|  | ||||
| 			for { | ||||
| 				_, err := lr.Read(b) | ||||
| 				if err == io.EOF { | ||||
| 					break | ||||
| 				} | ||||
| 				if err != nil { | ||||
| 					return nil, err | ||||
| 				} | ||||
| 				if b[0] != 0 { | ||||
| 					return nil, errors.Errorf("map %v: unknown and non-zero fields in definition", mapSym) | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			maps[mapSym] = &spec | ||||
| 		} | ||||
| 	} | ||||
| 	return maps, nil | ||||
| } | ||||
|  | ||||
| func getProgType(v string) (ProgramType, AttachType) { | ||||
| 	types := map[string]ProgramType{ | ||||
| 		// From https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/lib/bpf/libbpf.c#n3568 | ||||
| 		"socket":         SocketFilter, | ||||
| 		"seccomp":        SocketFilter, | ||||
| 		"kprobe/":        Kprobe, | ||||
| 		"kretprobe/":     Kprobe, | ||||
| 		"tracepoint/":    TracePoint, | ||||
| 		"xdp":            XDP, | ||||
| 		"perf_event":     PerfEvent, | ||||
| 		"sockops":        SockOps, | ||||
| 		"sk_skb":         SkSKB, | ||||
| 		"sk_msg":         SkMsg, | ||||
| 		"lirc_mode2":     LircMode2, | ||||
| 		"flow_dissector": FlowDissector, | ||||
|  | ||||
| 		"cgroup_skb/":       CGroupSKB, | ||||
| 		"cgroup/dev":        CGroupDevice, | ||||
| 		"cgroup/skb":        CGroupSKB, | ||||
| 		"cgroup/sock":       CGroupSock, | ||||
| 		"cgroup/post_bind":  CGroupSock, | ||||
| 		"cgroup/bind":       CGroupSockAddr, | ||||
| 		"cgroup/connect":    CGroupSockAddr, | ||||
| 		"cgroup/sendmsg":    CGroupSockAddr, | ||||
| 		"cgroup/recvmsg":    CGroupSockAddr, | ||||
| 		"cgroup/sysctl":     CGroupSysctl, | ||||
| 		"cgroup/getsockopt": CGroupSockopt, | ||||
| 		"cgroup/setsockopt": CGroupSockopt, | ||||
| 		"classifier":        SchedCLS, | ||||
| 		"action":            SchedACT, | ||||
| 	} | ||||
| 	attachTypes := map[string]AttachType{ | ||||
| 		"cgroup_skb/ingress":    AttachCGroupInetIngress, | ||||
| 		"cgroup_skb/egress":     AttachCGroupInetEgress, | ||||
| 		"cgroup/sock":           AttachCGroupInetSockCreate, | ||||
| 		"cgroup/post_bind4":     AttachCGroupInet4PostBind, | ||||
| 		"cgroup/post_bind6":     AttachCGroupInet6PostBind, | ||||
| 		"cgroup/dev":            AttachCGroupDevice, | ||||
| 		"sockops":               AttachCGroupSockOps, | ||||
| 		"sk_skb/stream_parser":  AttachSkSKBStreamParser, | ||||
| 		"sk_skb/stream_verdict": AttachSkSKBStreamVerdict, | ||||
| 		"sk_msg":                AttachSkSKBStreamVerdict, | ||||
| 		"lirc_mode2":            AttachLircMode2, | ||||
| 		"flow_dissector":        AttachFlowDissector, | ||||
| 		"cgroup/bind4":          AttachCGroupInet4Bind, | ||||
| 		"cgroup/bind6":          AttachCGroupInet6Bind, | ||||
| 		"cgroup/connect4":       AttachCGroupInet4Connect, | ||||
| 		"cgroup/connect6":       AttachCGroupInet6Connect, | ||||
| 		"cgroup/sendmsg4":       AttachCGroupUDP4Sendmsg, | ||||
| 		"cgroup/sendmsg6":       AttachCGroupUDP6Sendmsg, | ||||
| 		"cgroup/recvmsg4":       AttachCGroupUDP4Recvmsg, | ||||
| 		"cgroup/recvmsg6":       AttachCGroupUDP6Recvmsg, | ||||
| 		"cgroup/sysctl":         AttachCGroupSysctl, | ||||
| 		"cgroup/getsockopt":     AttachCGroupGetsockopt, | ||||
| 		"cgroup/setsockopt":     AttachCGroupSetsockopt, | ||||
| 	} | ||||
| 	attachType := AttachNone | ||||
| 	for k, t := range attachTypes { | ||||
| 		if strings.HasPrefix(v, k) { | ||||
| 			attachType = t | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for k, t := range types { | ||||
| 		if strings.HasPrefix(v, k) { | ||||
| 			return t, attachType | ||||
| 		} | ||||
| 	} | ||||
| 	return UnspecifiedProgram, AttachNone | ||||
| } | ||||
|  | ||||
| func (ec *elfCode) loadRelocations(sec *elf.Section) (map[uint64]string, error) { | ||||
| 	rels := make(map[uint64]string) | ||||
| 	if sec == nil { | ||||
| 		return rels, nil | ||||
| 	} | ||||
|  | ||||
| 	if sec.Entsize < 16 { | ||||
| 		return nil, errors.New("rels are less than 16 bytes") | ||||
| 	} | ||||
|  | ||||
| 	r := sec.Open() | ||||
| 	for off := uint64(0); off < sec.Size; off += sec.Entsize { | ||||
| 		ent := io.LimitReader(r, int64(sec.Entsize)) | ||||
|  | ||||
| 		var rel elf.Rel64 | ||||
| 		if binary.Read(ent, ec.ByteOrder, &rel) != nil { | ||||
| 			return nil, errors.Errorf("can't parse relocation at offset %v", off) | ||||
| 		} | ||||
|  | ||||
| 		symNo := int(elf.R_SYM64(rel.Info) - 1) | ||||
| 		if symNo >= len(ec.symbols) { | ||||
| 			return nil, errors.Errorf("relocation at offset %d: symbol %v doesnt exist", off, symNo) | ||||
| 		} | ||||
|  | ||||
| 		rels[rel.Off] = ec.symbols[symNo].Name | ||||
| 	} | ||||
| 	return rels, nil | ||||
| } | ||||
|  | ||||
| func symbolsPerSection(symbols []elf.Symbol) map[elf.SectionIndex]map[uint64]string { | ||||
| 	result := make(map[elf.SectionIndex]map[uint64]string) | ||||
| 	for i, sym := range symbols { | ||||
| 		switch elf.ST_TYPE(sym.Info) { | ||||
| 		case elf.STT_NOTYPE: | ||||
| 			// Older versions of LLVM doesn't tag | ||||
| 			// symbols correctly. | ||||
| 			break | ||||
| 		case elf.STT_OBJECT: | ||||
| 			break | ||||
| 		case elf.STT_FUNC: | ||||
| 			break | ||||
| 		default: | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		if sym.Name == "" { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		idx := sym.Section | ||||
| 		if _, ok := result[idx]; !ok { | ||||
| 			result[idx] = make(map[uint64]string) | ||||
| 		} | ||||
| 		result[idx][sym.Value] = symbols[i].Name | ||||
| 	} | ||||
| 	return result | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Akihiro Suda
					Akihiro Suda