mirror of
https://github.com/opencontainers/runc.git
synced 2026-07-11 06:03:57 +08:00
build(deps): bump github.com/cilium/ebpf from 0.9.1 to 0.9.3
Bumps [github.com/cilium/ebpf](https://github.com/cilium/ebpf) from 0.9.1 to 0.9.3. - [Release notes](https://github.com/cilium/ebpf/releases) - [Commits](https://github.com/cilium/ebpf/compare/v0.9.1...v0.9.3) --- updated-dependencies: - dependency-name: github.com/cilium/ebpf dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
+1
-1
@@ -79,7 +79,7 @@ generate: export BPF_CFLAGS := $(CFLAGS)
|
||||
generate:
|
||||
go generate ./cmd/bpf2go/test
|
||||
go generate ./internal/sys
|
||||
cd examples/ && go generate ./...
|
||||
go generate ./examples/...
|
||||
|
||||
testdata/loader-%-el.elf: testdata/loader.c
|
||||
$* $(CFLAGS) -target bpfel -c $< -o $@
|
||||
|
||||
+14
-10
@@ -4,14 +4,12 @@
|
||||
|
||||

|
||||
|
||||
eBPF is a pure Go library that provides utilities for loading, compiling, and
|
||||
ebpf-go is a pure Go library that provides utilities for loading, compiling, and
|
||||
debugging eBPF programs. It has minimal external dependencies and is intended to
|
||||
be used in long running processes.
|
||||
|
||||
The library is maintained by [Cloudflare](https://www.cloudflare.com) and
|
||||
[Cilium](https://www.cilium.io).
|
||||
|
||||
See [ebpf.io](https://ebpf.io) for other projects from the eBPF ecosystem.
|
||||
See [ebpf.io](https://ebpf.io) for complementary projects from the wider eBPF
|
||||
ecosystem.
|
||||
|
||||
## Getting Started
|
||||
|
||||
@@ -23,14 +21,20 @@ eBPF and the library, and help shape the future of the project.
|
||||
|
||||
## Getting Help
|
||||
|
||||
Please
|
||||
[join](https://ebpf.io/slack) the
|
||||
The community actively monitors our [GitHub Discussions](discussions/) page.
|
||||
Please search for existing threads before starting a new one. Refrain from
|
||||
opening issues on the bug tracker if you're just starting out or if you're not
|
||||
sure if something is a bug in the library code.
|
||||
|
||||
Alternatively, [join](https://ebpf.io/slack) the
|
||||
[#ebpf-go](https://cilium.slack.com/messages/ebpf-go) channel on Slack if you
|
||||
have questions regarding the library.
|
||||
have other questions regarding the project. Note that this channel is ephemeral
|
||||
and has its history erased past a certain point, which is less helpful for
|
||||
others running into the same problem later.
|
||||
|
||||
## Packages
|
||||
|
||||
This library includes the following packages:
|
||||
This library includes the following packages:
|
||||
|
||||
* [asm](https://pkg.go.dev/github.com/cilium/ebpf/asm) contains a basic
|
||||
assembler, allowing you to write eBPF assembly instructions directly
|
||||
@@ -38,7 +42,7 @@ This library includes the following packages:
|
||||
* [cmd/bpf2go](https://pkg.go.dev/github.com/cilium/ebpf/cmd/bpf2go) allows
|
||||
compiling and embedding eBPF programs written in C within Go code. As well as
|
||||
compiling the C code, it auto-generates Go code for loading and manipulating
|
||||
the eBPF program and map objects.
|
||||
the eBPF program and map objects.
|
||||
* [link](https://pkg.go.dev/github.com/cilium/ebpf/link) allows attaching eBPF
|
||||
to various hooks
|
||||
* [perf](https://pkg.go.dev/github.com/cilium/ebpf/perf) allows reading from a
|
||||
|
||||
+8
-8
@@ -4,10 +4,10 @@ package asm
|
||||
|
||||
// Source of ALU / ALU64 / Branch operations
|
||||
//
|
||||
// msb lsb
|
||||
// +----+-+---+
|
||||
// |op |S|cls|
|
||||
// +----+-+---+
|
||||
// msb lsb
|
||||
// +----+-+---+
|
||||
// |op |S|cls|
|
||||
// +----+-+---+
|
||||
type Source uint8
|
||||
|
||||
const sourceMask OpCode = 0x08
|
||||
@@ -39,10 +39,10 @@ const (
|
||||
|
||||
// ALUOp are ALU / ALU64 operations
|
||||
//
|
||||
// msb lsb
|
||||
// +----+-+---+
|
||||
// |OP |s|cls|
|
||||
// +----+-+---+
|
||||
// msb lsb
|
||||
// +----+-+---+
|
||||
// |OP |s|cls|
|
||||
// +----+-+---+
|
||||
type ALUOp uint8
|
||||
|
||||
const aluMask OpCode = 0xf0
|
||||
|
||||
+9
-9
@@ -13,15 +13,15 @@ func (_ BuiltinFunc) Max() BuiltinFunc {
|
||||
//
|
||||
// You can regenerate this list using the following gawk script:
|
||||
//
|
||||
// /FN\(.+\),/ {
|
||||
// match($1, /\((.+)\)/, r)
|
||||
// split(r[1], p, "_")
|
||||
// printf "Fn"
|
||||
// for (i in p) {
|
||||
// printf "%s%s", toupper(substr(p[i], 1, 1)), substr(p[i], 2)
|
||||
// }
|
||||
// print ""
|
||||
// }
|
||||
// /FN\(.+\),/ {
|
||||
// match($1, /\((.+)\)/, r)
|
||||
// split(r[1], p, "_")
|
||||
// printf "Fn"
|
||||
// for (i in p) {
|
||||
// printf "%s%s", toupper(substr(p[i], 1, 1)), substr(p[i], 2)
|
||||
// }
|
||||
// print ""
|
||||
// }
|
||||
//
|
||||
// The script expects include/uapi/linux/bpf.h as it's input.
|
||||
const (
|
||||
|
||||
+4
-4
@@ -4,10 +4,10 @@ package asm
|
||||
|
||||
// JumpOp affect control flow.
|
||||
//
|
||||
// msb lsb
|
||||
// +----+-+---+
|
||||
// |OP |s|cls|
|
||||
// +----+-+---+
|
||||
// msb lsb
|
||||
// +----+-+---+
|
||||
// |OP |s|cls|
|
||||
// +----+-+---+
|
||||
type JumpOp uint8
|
||||
|
||||
const jumpMask OpCode = aluMask
|
||||
|
||||
+8
-8
@@ -4,10 +4,10 @@ package asm
|
||||
|
||||
// Mode for load and store operations
|
||||
//
|
||||
// msb lsb
|
||||
// +---+--+---+
|
||||
// |MDE|sz|cls|
|
||||
// +---+--+---+
|
||||
// msb lsb
|
||||
// +---+--+---+
|
||||
// |MDE|sz|cls|
|
||||
// +---+--+---+
|
||||
type Mode uint8
|
||||
|
||||
const modeMask OpCode = 0xe0
|
||||
@@ -30,10 +30,10 @@ const (
|
||||
|
||||
// Size of load and store operations
|
||||
//
|
||||
// msb lsb
|
||||
// +---+--+---+
|
||||
// |mde|SZ|cls|
|
||||
// +---+--+---+
|
||||
// msb lsb
|
||||
// +---+--+---+
|
||||
// |mde|SZ|cls|
|
||||
// +---+--+---+
|
||||
type Size uint8
|
||||
|
||||
const sizeMask OpCode = 0x18
|
||||
|
||||
+8
-8
@@ -9,10 +9,10 @@ import (
|
||||
|
||||
// Class of operations
|
||||
//
|
||||
// msb lsb
|
||||
// +---+--+---+
|
||||
// | ?? |CLS|
|
||||
// +---+--+---+
|
||||
// msb lsb
|
||||
// +---+--+---+
|
||||
// | ?? |CLS|
|
||||
// +---+--+---+
|
||||
type Class uint8
|
||||
|
||||
const classMask OpCode = 0x07
|
||||
@@ -70,10 +70,10 @@ func (cls Class) isJumpOrALU() bool {
|
||||
//
|
||||
// Its encoding is defined by a Class value:
|
||||
//
|
||||
// msb lsb
|
||||
// +----+-+---+
|
||||
// | ???? |CLS|
|
||||
// +----+-+---+
|
||||
// msb lsb
|
||||
// +----+-+---+
|
||||
// | ???? |CLS|
|
||||
// +----+-+---+
|
||||
type OpCode uint8
|
||||
|
||||
// InvalidOpCode is returned by setters on OpCode
|
||||
|
||||
+3
-2
@@ -51,11 +51,12 @@ func _() {
|
||||
_ = x[AttachSkReuseportSelect-39]
|
||||
_ = x[AttachSkReuseportSelectOrMigrate-40]
|
||||
_ = x[AttachPerfEvent-41]
|
||||
_ = x[AttachTraceKprobeMulti-42]
|
||||
}
|
||||
|
||||
const _AttachType_name = "NoneCGroupInetEgressCGroupInetSockCreateCGroupSockOpsSkSKBStreamParserSkSKBStreamVerdictCGroupDeviceSkMsgVerdictCGroupInet4BindCGroupInet6BindCGroupInet4ConnectCGroupInet6ConnectCGroupInet4PostBindCGroupInet6PostBindCGroupUDP4SendmsgCGroupUDP6SendmsgLircMode2FlowDissectorCGroupSysctlCGroupUDP4RecvmsgCGroupUDP6RecvmsgCGroupGetsockoptCGroupSetsockoptTraceRawTpTraceFEntryTraceFExitModifyReturnLSMMacTraceIterCgroupInet4GetPeernameCgroupInet6GetPeernameCgroupInet4GetSocknameCgroupInet6GetSocknameXDPDevMapCgroupInetSockReleaseXDPCPUMapSkLookupXDPSkSKBVerdictSkReuseportSelectSkReuseportSelectOrMigratePerfEvent"
|
||||
const _AttachType_name = "NoneCGroupInetEgressCGroupInetSockCreateCGroupSockOpsSkSKBStreamParserSkSKBStreamVerdictCGroupDeviceSkMsgVerdictCGroupInet4BindCGroupInet6BindCGroupInet4ConnectCGroupInet6ConnectCGroupInet4PostBindCGroupInet6PostBindCGroupUDP4SendmsgCGroupUDP6SendmsgLircMode2FlowDissectorCGroupSysctlCGroupUDP4RecvmsgCGroupUDP6RecvmsgCGroupGetsockoptCGroupSetsockoptTraceRawTpTraceFEntryTraceFExitModifyReturnLSMMacTraceIterCgroupInet4GetPeernameCgroupInet6GetPeernameCgroupInet4GetSocknameCgroupInet6GetSocknameXDPDevMapCgroupInetSockReleaseXDPCPUMapSkLookupXDPSkSKBVerdictSkReuseportSelectSkReuseportSelectOrMigratePerfEventTraceKprobeMulti"
|
||||
|
||||
var _AttachType_index = [...]uint16{0, 4, 20, 40, 53, 70, 88, 100, 112, 127, 142, 160, 178, 197, 216, 233, 250, 259, 272, 284, 301, 318, 334, 350, 360, 371, 381, 393, 399, 408, 430, 452, 474, 496, 505, 526, 535, 543, 546, 558, 575, 601, 610}
|
||||
var _AttachType_index = [...]uint16{0, 4, 20, 40, 53, 70, 88, 100, 112, 127, 142, 160, 178, 197, 216, 233, 250, 259, 272, 284, 301, 318, 334, 350, 360, 371, 381, 393, 399, 408, 430, 452, 474, 496, 505, 526, 535, 543, 546, 558, 575, 601, 610, 626}
|
||||
|
||||
func (i AttachType) String() string {
|
||||
if i >= AttachType(len(_AttachType_index)-1) {
|
||||
|
||||
+28
-13
@@ -471,9 +471,10 @@ type marshalOpts struct {
|
||||
|
||||
func (s *Spec) marshal(opts marshalOpts) ([]byte, error) {
|
||||
var (
|
||||
buf bytes.Buffer
|
||||
header = new(btfHeader)
|
||||
headerLen = binary.Size(header)
|
||||
buf bytes.Buffer
|
||||
header = new(btfHeader)
|
||||
headerLen = binary.Size(header)
|
||||
stringsLen int
|
||||
)
|
||||
|
||||
// Reserve space for the header. We have to write it last since
|
||||
@@ -495,10 +496,12 @@ func (s *Spec) marshal(opts marshalOpts) ([]byte, error) {
|
||||
typeLen := uint32(buf.Len() - headerLen)
|
||||
|
||||
// Write string section after type section.
|
||||
stringsLen := s.strings.Length()
|
||||
buf.Grow(stringsLen)
|
||||
if err := s.strings.Marshal(&buf); err != nil {
|
||||
return nil, err
|
||||
if s.strings != nil {
|
||||
stringsLen = s.strings.Length()
|
||||
buf.Grow(stringsLen)
|
||||
if err := s.strings.Marshal(&buf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Fill out the header, and write it out.
|
||||
@@ -609,6 +612,9 @@ func (s *Spec) AnyTypeByName(name string) (Type, error) {
|
||||
// Type exists in the Spec. If multiple candidates are found,
|
||||
// an error is returned.
|
||||
func (s *Spec) TypeByName(name string, typ interface{}) error {
|
||||
typeInterface := reflect.TypeOf((*Type)(nil)).Elem()
|
||||
|
||||
// typ may be **T or *Type
|
||||
typValue := reflect.ValueOf(typ)
|
||||
if typValue.Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("%T is not a pointer", typ)
|
||||
@@ -620,7 +626,12 @@ func (s *Spec) TypeByName(name string, typ interface{}) error {
|
||||
}
|
||||
|
||||
wanted := typPtr.Type()
|
||||
if !wanted.AssignableTo(reflect.TypeOf((*Type)(nil)).Elem()) {
|
||||
if wanted == typeInterface {
|
||||
// This is *Type. Unwrap the value's type.
|
||||
wanted = typPtr.Elem().Type()
|
||||
}
|
||||
|
||||
if !wanted.AssignableTo(typeInterface) {
|
||||
return fmt.Errorf("%T does not satisfy Type interface", typ)
|
||||
}
|
||||
|
||||
@@ -643,7 +654,7 @@ func (s *Spec) TypeByName(name string, typ interface{}) error {
|
||||
}
|
||||
|
||||
if candidate == nil {
|
||||
return fmt.Errorf("type %s: %w", name, ErrNotFound)
|
||||
return fmt.Errorf("%s %s: %w", wanted, name, ErrNotFound)
|
||||
}
|
||||
|
||||
typPtr.Set(reflect.ValueOf(candidate))
|
||||
@@ -699,7 +710,7 @@ func NewHandle(spec *Spec) (*Handle, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if spec.byteOrder != internal.NativeEndian {
|
||||
if spec.byteOrder != nil && spec.byteOrder != internal.NativeEndian {
|
||||
return nil, fmt.Errorf("can't load %s BTF on %s", spec.byteOrder, internal.NativeEndian)
|
||||
}
|
||||
|
||||
@@ -726,9 +737,13 @@ func NewHandle(spec *Spec) (*Handle, error) {
|
||||
attr.BtfLogBuf = sys.NewSlicePointer(logBuf)
|
||||
attr.BtfLogSize = uint32(len(logBuf))
|
||||
attr.BtfLogLevel = 1
|
||||
// NB: The syscall will never return ENOSPC as of 5.18-rc4.
|
||||
_, _ = sys.BtfLoad(attr)
|
||||
return nil, internal.ErrorWithLog(err, logBuf)
|
||||
|
||||
// Up until at least kernel 6.0, the BTF verifier does not return ENOSPC
|
||||
// if there are other verification errors. ENOSPC is only returned when
|
||||
// the BTF blob is correct, a log was requested, and the provided buffer
|
||||
// is too small.
|
||||
_, ve := sys.BtfLoad(attr)
|
||||
return nil, internal.ErrorWithLog(err, logBuf, errors.Is(ve, unix.ENOSPC))
|
||||
}
|
||||
|
||||
return &Handle{fd, attr.BtfSize}, nil
|
||||
|
||||
+34
-65
@@ -6,33 +6,36 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
//go:generate stringer -linecomment -output=btf_types_string.go -type=FuncLinkage,VarLinkage
|
||||
//go:generate stringer -linecomment -output=btf_types_string.go -type=FuncLinkage,VarLinkage,btfKind
|
||||
|
||||
// btfKind describes a Type.
|
||||
type btfKind uint8
|
||||
|
||||
// Equivalents of the BTF_KIND_* constants.
|
||||
const (
|
||||
kindUnknown btfKind = iota
|
||||
kindInt
|
||||
kindPointer
|
||||
kindArray
|
||||
kindStruct
|
||||
kindUnion
|
||||
kindEnum
|
||||
kindForward
|
||||
kindTypedef
|
||||
kindVolatile
|
||||
kindConst
|
||||
kindRestrict
|
||||
kindUnknown btfKind = iota // Unknown
|
||||
kindInt // Int
|
||||
kindPointer // Pointer
|
||||
kindArray // Array
|
||||
kindStruct // Struct
|
||||
kindUnion // Union
|
||||
kindEnum // Enum
|
||||
kindForward // Forward
|
||||
kindTypedef // Typedef
|
||||
kindVolatile // Volatile
|
||||
kindConst // Const
|
||||
kindRestrict // Restrict
|
||||
// Added ~4.20
|
||||
kindFunc
|
||||
kindFuncProto
|
||||
kindFunc // Func
|
||||
kindFuncProto // FuncProto
|
||||
// Added ~5.1
|
||||
kindVar
|
||||
kindDatasec
|
||||
kindVar // Var
|
||||
kindDatasec // Datasec
|
||||
// Added ~5.13
|
||||
kindFloat
|
||||
kindFloat // Float
|
||||
// Added 5.16
|
||||
kindDeclTag // DeclTag
|
||||
kindTypeTag // TypeTag
|
||||
)
|
||||
|
||||
// FuncLinkage describes BTF function linkage metadata.
|
||||
@@ -85,47 +88,6 @@ type btfType struct {
|
||||
SizeType uint32
|
||||
}
|
||||
|
||||
func (k btfKind) String() string {
|
||||
switch k {
|
||||
case kindUnknown:
|
||||
return "Unknown"
|
||||
case kindInt:
|
||||
return "Integer"
|
||||
case kindPointer:
|
||||
return "Pointer"
|
||||
case kindArray:
|
||||
return "Array"
|
||||
case kindStruct:
|
||||
return "Struct"
|
||||
case kindUnion:
|
||||
return "Union"
|
||||
case kindEnum:
|
||||
return "Enumeration"
|
||||
case kindForward:
|
||||
return "Forward"
|
||||
case kindTypedef:
|
||||
return "Typedef"
|
||||
case kindVolatile:
|
||||
return "Volatile"
|
||||
case kindConst:
|
||||
return "Const"
|
||||
case kindRestrict:
|
||||
return "Restrict"
|
||||
case kindFunc:
|
||||
return "Function"
|
||||
case kindFuncProto:
|
||||
return "Function Proto"
|
||||
case kindVar:
|
||||
return "Variable"
|
||||
case kindDatasec:
|
||||
return "Section"
|
||||
case kindFloat:
|
||||
return "Float"
|
||||
default:
|
||||
return fmt.Sprintf("Unknown (%d)", k)
|
||||
}
|
||||
}
|
||||
|
||||
func mask(len uint32) uint32 {
|
||||
return (1 << len) - 1
|
||||
}
|
||||
@@ -209,11 +171,11 @@ func (rt *rawType) Marshal(w io.Writer, bo binary.ByteOrder) error {
|
||||
|
||||
// btfInt encodes additional data for integers.
|
||||
//
|
||||
// ? ? ? ? e e e e o o o o o o o o ? ? ? ? ? ? ? ? b b b b b b b b
|
||||
// ? = undefined
|
||||
// e = encoding
|
||||
// o = offset (bitfields?)
|
||||
// b = bits (bitfields)
|
||||
// ? ? ? ? e e e e o o o o o o o o ? ? ? ? ? ? ? ? b b b b b b b b
|
||||
// ? = undefined
|
||||
// e = encoding
|
||||
// o = offset (bitfields?)
|
||||
// b = bits (bitfields)
|
||||
type btfInt struct {
|
||||
Raw uint32
|
||||
}
|
||||
@@ -275,7 +237,7 @@ type btfVariable struct {
|
||||
|
||||
type btfEnum struct {
|
||||
NameOff uint32
|
||||
Val int32
|
||||
Val uint32
|
||||
}
|
||||
|
||||
type btfParam struct {
|
||||
@@ -283,6 +245,10 @@ type btfParam struct {
|
||||
Type TypeID
|
||||
}
|
||||
|
||||
type btfDeclTag struct {
|
||||
ComponentIdx uint32
|
||||
}
|
||||
|
||||
func readTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32) ([]rawType, error) {
|
||||
var header btfType
|
||||
// because of the interleaving between types and struct members it is difficult to
|
||||
@@ -325,6 +291,9 @@ func readTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32) ([]rawType, err
|
||||
case kindDatasec:
|
||||
data = make([]btfVarSecinfo, header.Vlen())
|
||||
case kindFloat:
|
||||
case kindDeclTag:
|
||||
data = new(btfDeclTag)
|
||||
case kindTypeTag:
|
||||
default:
|
||||
return nil, fmt.Errorf("type id %v: unknown kind: %v", id, header.Kind())
|
||||
}
|
||||
|
||||
+36
-1
@@ -1,4 +1,4 @@
|
||||
// Code generated by "stringer -linecomment -output=btf_types_string.go -type=FuncLinkage,VarLinkage"; DO NOT EDIT.
|
||||
// Code generated by "stringer -linecomment -output=btf_types_string.go -type=FuncLinkage,VarLinkage,btfKind"; DO NOT EDIT.
|
||||
|
||||
package btf
|
||||
|
||||
@@ -42,3 +42,38 @@ func (i VarLinkage) String() string {
|
||||
}
|
||||
return _VarLinkage_name[_VarLinkage_index[i]:_VarLinkage_index[i+1]]
|
||||
}
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[kindUnknown-0]
|
||||
_ = x[kindInt-1]
|
||||
_ = x[kindPointer-2]
|
||||
_ = x[kindArray-3]
|
||||
_ = x[kindStruct-4]
|
||||
_ = x[kindUnion-5]
|
||||
_ = x[kindEnum-6]
|
||||
_ = x[kindForward-7]
|
||||
_ = x[kindTypedef-8]
|
||||
_ = x[kindVolatile-9]
|
||||
_ = x[kindConst-10]
|
||||
_ = x[kindRestrict-11]
|
||||
_ = x[kindFunc-12]
|
||||
_ = x[kindFuncProto-13]
|
||||
_ = x[kindVar-14]
|
||||
_ = x[kindDatasec-15]
|
||||
_ = x[kindFloat-16]
|
||||
_ = x[kindDeclTag-17]
|
||||
_ = x[kindTypeTag-18]
|
||||
}
|
||||
|
||||
const _btfKind_name = "UnknownIntPointerArrayStructUnionEnumForwardTypedefVolatileConstRestrictFuncFuncProtoVarDatasecFloatDeclTagTypeTag"
|
||||
|
||||
var _btfKind_index = [...]uint8{0, 7, 10, 17, 22, 28, 33, 37, 44, 51, 59, 64, 72, 76, 85, 88, 95, 100, 107, 114}
|
||||
|
||||
func (i btfKind) String() string {
|
||||
if i >= btfKind(len(_btfKind_index)-1) {
|
||||
return "btfKind(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _btfKind_name[_btfKind_index[i]:_btfKind_index[i+1]]
|
||||
}
|
||||
|
||||
+31
-34
@@ -156,16 +156,17 @@ func (k coreKind) String() string {
|
||||
}
|
||||
}
|
||||
|
||||
// CORERelocate calculates the difference in types between local and target.
|
||||
// CORERelocate calculates changes needed to adjust eBPF instructions for differences
|
||||
// in types.
|
||||
//
|
||||
// Returns a list of fixups which can be applied to instructions to make them
|
||||
// match the target type(s).
|
||||
//
|
||||
// Fixups are returned in the order of relos, e.g. fixup[i] is the solution
|
||||
// for relos[i].
|
||||
func CORERelocate(local, target *Spec, relos []*CORERelocation) ([]COREFixup, error) {
|
||||
if local.byteOrder != target.byteOrder {
|
||||
return nil, fmt.Errorf("can't relocate %s against %s", local.byteOrder, target.byteOrder)
|
||||
func CORERelocate(relos []*CORERelocation, target *Spec, bo binary.ByteOrder) ([]COREFixup, error) {
|
||||
if bo != target.byteOrder {
|
||||
return nil, fmt.Errorf("can't relocate %s against %s", bo, target.byteOrder)
|
||||
}
|
||||
|
||||
type reloGroup struct {
|
||||
@@ -185,15 +186,14 @@ func CORERelocate(local, target *Spec, relos []*CORERelocation) ([]COREFixup, er
|
||||
return nil, fmt.Errorf("%s: unexpected accessor %v", relo.kind, relo.accessor)
|
||||
}
|
||||
|
||||
id, err := local.TypeID(relo.typ)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %w", relo.kind, err)
|
||||
}
|
||||
|
||||
result[i] = COREFixup{
|
||||
kind: relo.kind,
|
||||
local: uint32(id),
|
||||
target: uint32(id),
|
||||
kind: relo.kind,
|
||||
local: uint32(relo.id),
|
||||
// NB: Using relo.id as the target here is incorrect, since
|
||||
// it doesn't match the BTF we generate on the fly. This isn't
|
||||
// too bad for now since there are no uses of the local type ID
|
||||
// in the kernel, yet.
|
||||
target: uint32(relo.id),
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -214,7 +214,7 @@ func CORERelocate(local, target *Spec, relos []*CORERelocation) ([]COREFixup, er
|
||||
}
|
||||
|
||||
targets := target.namedTypes[newEssentialName(localTypeName)]
|
||||
fixups, err := coreCalculateFixups(local, target, localType, targets, group.relos)
|
||||
fixups, err := coreCalculateFixups(group.relos, target, targets, bo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("relocate %s: %w", localType, err)
|
||||
}
|
||||
@@ -230,18 +230,13 @@ func CORERelocate(local, target *Spec, relos []*CORERelocation) ([]COREFixup, er
|
||||
var errAmbiguousRelocation = errors.New("ambiguous relocation")
|
||||
var errImpossibleRelocation = errors.New("impossible relocation")
|
||||
|
||||
// coreCalculateFixups calculates the fixups for the given relocations using
|
||||
// the "best" target.
|
||||
// coreCalculateFixups finds the target type that best matches all relocations.
|
||||
//
|
||||
// All relos must target the same type.
|
||||
//
|
||||
// The best target is determined by scoring: the less poisoning we have to do
|
||||
// the better the target is.
|
||||
func coreCalculateFixups(localSpec, targetSpec *Spec, local Type, targets []Type, relos []*CORERelocation) ([]COREFixup, error) {
|
||||
localID, err := localSpec.TypeID(local)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("local type ID: %w", err)
|
||||
}
|
||||
local = Copy(local, UnderlyingType)
|
||||
|
||||
func coreCalculateFixups(relos []*CORERelocation, targetSpec *Spec, targets []Type, bo binary.ByteOrder) ([]COREFixup, error) {
|
||||
bestScore := len(relos)
|
||||
var bestFixups []COREFixup
|
||||
for i := range targets {
|
||||
@@ -254,7 +249,7 @@ func coreCalculateFixups(localSpec, targetSpec *Spec, local Type, targets []Type
|
||||
score := 0 // lower is better
|
||||
fixups := make([]COREFixup, 0, len(relos))
|
||||
for _, relo := range relos {
|
||||
fixup, err := coreCalculateFixup(localSpec.byteOrder, local, localID, target, targetID, relo)
|
||||
fixup, err := coreCalculateFixup(relo, target, targetID, bo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("target %s: %w", target, err)
|
||||
}
|
||||
@@ -305,7 +300,7 @@ func coreCalculateFixups(localSpec, targetSpec *Spec, local Type, targets []Type
|
||||
|
||||
// coreCalculateFixup calculates the fixup for a single local type, target type
|
||||
// and relocation.
|
||||
func coreCalculateFixup(byteOrder binary.ByteOrder, local Type, localID TypeID, target Type, targetID TypeID, relo *CORERelocation) (COREFixup, error) {
|
||||
func coreCalculateFixup(relo *CORERelocation, target Type, targetID TypeID, bo binary.ByteOrder) (COREFixup, error) {
|
||||
fixup := func(local, target uint32) (COREFixup, error) {
|
||||
return COREFixup{kind: relo.kind, local: local, target: target}, nil
|
||||
}
|
||||
@@ -320,6 +315,8 @@ func coreCalculateFixup(byteOrder binary.ByteOrder, local Type, localID TypeID,
|
||||
}
|
||||
zero := COREFixup{}
|
||||
|
||||
local := Copy(relo.typ, UnderlyingType)
|
||||
|
||||
switch relo.kind {
|
||||
case reloTypeIDTarget, reloTypeSize, reloTypeExists:
|
||||
if len(relo.accessor) > 1 || relo.accessor[0] != 0 {
|
||||
@@ -339,7 +336,7 @@ func coreCalculateFixup(byteOrder binary.ByteOrder, local Type, localID TypeID,
|
||||
return fixup(1, 1)
|
||||
|
||||
case reloTypeIDTarget:
|
||||
return fixup(uint32(localID), uint32(targetID))
|
||||
return fixup(uint32(relo.id), uint32(targetID))
|
||||
|
||||
case reloTypeSize:
|
||||
localSize, err := Sizeof(local)
|
||||
@@ -427,7 +424,7 @@ func coreCalculateFixup(byteOrder binary.ByteOrder, local Type, localID TypeID,
|
||||
|
||||
case reloFieldLShiftU64:
|
||||
var target uint32
|
||||
if byteOrder == binary.LittleEndian {
|
||||
if bo == binary.LittleEndian {
|
||||
targetSize, err := targetField.sizeBits()
|
||||
if err != nil {
|
||||
return zero, err
|
||||
@@ -536,9 +533,9 @@ func (ca coreAccessor) enumValue(t Type) (*EnumValue, error) {
|
||||
// coreField represents the position of a "child" of a composite type from the
|
||||
// start of that type.
|
||||
//
|
||||
// /- start of composite
|
||||
// | offset * 8 | bitfieldOffset | bitfieldSize | ... |
|
||||
// \- start of field end of field -/
|
||||
// /- start of composite
|
||||
// | offset * 8 | bitfieldOffset | bitfieldSize | ... |
|
||||
// \- start of field end of field -/
|
||||
type coreField struct {
|
||||
Type Type
|
||||
|
||||
@@ -858,7 +855,7 @@ func coreAreTypesCompatible(localType Type, targetType Type) error {
|
||||
depth = 0
|
||||
)
|
||||
|
||||
for ; l != nil && t != nil; l, t = localTs.shift(), targetTs.shift() {
|
||||
for ; l != nil && t != nil; l, t = localTs.Shift(), targetTs.Shift() {
|
||||
if depth >= maxTypeDepth {
|
||||
return errors.New("types are nested too deep")
|
||||
}
|
||||
@@ -876,8 +873,8 @@ func coreAreTypesCompatible(localType Type, targetType Type) error {
|
||||
|
||||
case *Pointer, *Array:
|
||||
depth++
|
||||
localType.walk(&localTs)
|
||||
targetType.walk(&targetTs)
|
||||
walkType(localType, localTs.Push)
|
||||
walkType(targetType, targetTs.Push)
|
||||
|
||||
case *FuncProto:
|
||||
tv := targetType.(*FuncProto)
|
||||
@@ -886,8 +883,8 @@ func coreAreTypesCompatible(localType Type, targetType Type) error {
|
||||
}
|
||||
|
||||
depth++
|
||||
localType.walk(&localTs)
|
||||
targetType.walk(&targetTs)
|
||||
walkType(localType, localTs.Push)
|
||||
walkType(targetType, targetTs.Push)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unsupported type %T", localType)
|
||||
|
||||
+4
@@ -605,9 +605,12 @@ type bpfCORERelo struct {
|
||||
}
|
||||
|
||||
type CORERelocation struct {
|
||||
// The local type of the relocation, stripped of typedefs and qualifiers.
|
||||
typ Type
|
||||
accessor coreAccessor
|
||||
kind coreKind
|
||||
// The ID of the local type in the source BTF.
|
||||
id TypeID
|
||||
}
|
||||
|
||||
func CORERelocationMetadata(ins *asm.Instruction) *CORERelocation {
|
||||
@@ -641,6 +644,7 @@ func newRelocationInfo(relo bpfCORERelo, ts types, strings *stringTable) (*coreR
|
||||
typ,
|
||||
accessor,
|
||||
relo.Kind,
|
||||
relo.TypeID,
|
||||
},
|
||||
asm.RawInstructionOffset(relo.InsnOff),
|
||||
}, nil
|
||||
|
||||
+74
-53
@@ -56,54 +56,40 @@ func (gf *GoFormatter) enumIdentifier(name, element string) string {
|
||||
//
|
||||
// It encodes https://golang.org/ref/spec#Type_declarations:
|
||||
//
|
||||
// type foo struct { bar uint32; }
|
||||
// type bar int32
|
||||
// type foo struct { bar uint32; }
|
||||
// type bar int32
|
||||
func (gf *GoFormatter) writeTypeDecl(name string, typ Type) error {
|
||||
if name == "" {
|
||||
return fmt.Errorf("need a name for type %s", typ)
|
||||
}
|
||||
|
||||
switch v := skipQualifiers(typ).(type) {
|
||||
case *Enum:
|
||||
fmt.Fprintf(&gf.w, "type %s ", name)
|
||||
switch v.Size {
|
||||
case 1:
|
||||
gf.w.WriteString("int8")
|
||||
case 2:
|
||||
gf.w.WriteString("int16")
|
||||
case 4:
|
||||
gf.w.WriteString("int32")
|
||||
case 8:
|
||||
gf.w.WriteString("int64")
|
||||
default:
|
||||
return fmt.Errorf("%s: invalid enum size %d", typ, v.Size)
|
||||
}
|
||||
|
||||
if len(v.Values) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
gf.w.WriteString("; const ( ")
|
||||
for _, ev := range v.Values {
|
||||
id := gf.enumIdentifier(name, ev.Name)
|
||||
fmt.Fprintf(&gf.w, "%s %s = %d; ", id, name, ev.Value)
|
||||
}
|
||||
gf.w.WriteString(")")
|
||||
|
||||
return nil
|
||||
|
||||
default:
|
||||
fmt.Fprintf(&gf.w, "type %s ", name)
|
||||
return gf.writeTypeLit(v, 0)
|
||||
typ = skipQualifiers(typ)
|
||||
fmt.Fprintf(&gf.w, "type %s ", name)
|
||||
if err := gf.writeTypeLit(typ, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e, ok := typ.(*Enum)
|
||||
if !ok || len(e.Values) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
gf.w.WriteString("; const ( ")
|
||||
for _, ev := range e.Values {
|
||||
id := gf.enumIdentifier(name, ev.Name)
|
||||
fmt.Fprintf(&gf.w, "%s %s = %d; ", id, name, ev.Value)
|
||||
}
|
||||
gf.w.WriteString(")")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeType outputs the name of a named type or a literal describing the type.
|
||||
//
|
||||
// It encodes https://golang.org/ref/spec#Types.
|
||||
//
|
||||
// foo (if foo is a named type)
|
||||
// uint32
|
||||
// foo (if foo is a named type)
|
||||
// uint32
|
||||
func (gf *GoFormatter) writeType(typ Type, depth int) error {
|
||||
typ = skipQualifiers(typ)
|
||||
|
||||
@@ -122,8 +108,8 @@ func (gf *GoFormatter) writeType(typ Type, depth int) error {
|
||||
//
|
||||
// It encodes https://golang.org/ref/spec#TypeLit.
|
||||
//
|
||||
// struct { bar uint32; }
|
||||
// uint32
|
||||
// struct { bar uint32; }
|
||||
// uint32
|
||||
func (gf *GoFormatter) writeTypeLit(typ Type, depth int) error {
|
||||
depth++
|
||||
if depth > maxTypeDepth {
|
||||
@@ -133,10 +119,24 @@ func (gf *GoFormatter) writeTypeLit(typ Type, depth int) error {
|
||||
var err error
|
||||
switch v := skipQualifiers(typ).(type) {
|
||||
case *Int:
|
||||
gf.writeIntLit(v)
|
||||
err = gf.writeIntLit(v)
|
||||
|
||||
case *Enum:
|
||||
gf.w.WriteString("int32")
|
||||
if !v.Signed {
|
||||
gf.w.WriteRune('u')
|
||||
}
|
||||
switch v.Size {
|
||||
case 1:
|
||||
gf.w.WriteString("int8")
|
||||
case 2:
|
||||
gf.w.WriteString("int16")
|
||||
case 4:
|
||||
gf.w.WriteString("int32")
|
||||
case 8:
|
||||
gf.w.WriteString("int64")
|
||||
default:
|
||||
err = fmt.Errorf("invalid enum size %d", v.Size)
|
||||
}
|
||||
|
||||
case *Typedef:
|
||||
err = gf.writeType(v.Type, depth)
|
||||
@@ -166,19 +166,36 @@ func (gf *GoFormatter) writeTypeLit(typ Type, depth int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gf *GoFormatter) writeIntLit(i *Int) {
|
||||
// NB: Encoding.IsChar is ignored.
|
||||
if i.Encoding.IsBool() && i.Size == 1 {
|
||||
gf.w.WriteString("bool")
|
||||
return
|
||||
}
|
||||
|
||||
func (gf *GoFormatter) writeIntLit(i *Int) error {
|
||||
bits := i.Size * 8
|
||||
if i.Encoding.IsSigned() {
|
||||
fmt.Fprintf(&gf.w, "int%d", bits)
|
||||
} else {
|
||||
fmt.Fprintf(&gf.w, "uint%d", bits)
|
||||
switch i.Encoding {
|
||||
case Bool:
|
||||
if i.Size != 1 {
|
||||
return fmt.Errorf("bool with size %d", i.Size)
|
||||
}
|
||||
gf.w.WriteString("bool")
|
||||
case Char:
|
||||
if i.Size != 1 {
|
||||
return fmt.Errorf("char with size %d", i.Size)
|
||||
}
|
||||
// BTF doesn't have a way to specify the signedness of a char. Assume
|
||||
// we are dealing with unsigned, since this works nicely with []byte
|
||||
// in Go code.
|
||||
fallthrough
|
||||
case Unsigned, Signed:
|
||||
stem := "uint"
|
||||
if i.Encoding == Signed {
|
||||
stem = "int"
|
||||
}
|
||||
if i.Size > 8 {
|
||||
fmt.Fprintf(&gf.w, "[%d]byte /* %s%d */", i.Size, stem, i.Size*8)
|
||||
} else {
|
||||
fmt.Fprintf(&gf.w, "%s%d", stem, bits)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("can't encode %s", i.Encoding)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gf *GoFormatter) writeStructLit(size uint32, members []Member, depth int) error {
|
||||
@@ -199,11 +216,15 @@ func (gf *GoFormatter) writeStructLit(size uint32, members []Member, depth int)
|
||||
gf.writePadding(n)
|
||||
}
|
||||
|
||||
size, err := Sizeof(m.Type)
|
||||
fieldSize, err := Sizeof(m.Type)
|
||||
if err != nil {
|
||||
return fmt.Errorf("field %d: %w", i, err)
|
||||
}
|
||||
prevOffset = offset + uint32(size)
|
||||
|
||||
prevOffset = offset + uint32(fieldSize)
|
||||
if prevOffset > size {
|
||||
return fmt.Errorf("field %d of size %d exceeds type size %d", i, fieldSize, size)
|
||||
}
|
||||
|
||||
if err := gf.writeStructField(m, depth); err != nil {
|
||||
return fmt.Errorf("field %d: %w", i, err)
|
||||
|
||||
+56
-18
@@ -71,51 +71,89 @@ func (i *HandleInfo) IsModule() bool {
|
||||
|
||||
// HandleIterator allows enumerating BTF blobs loaded into the kernel.
|
||||
type HandleIterator struct {
|
||||
// The ID of the last retrieved handle. Only valid after a call to Next.
|
||||
ID ID
|
||||
err error
|
||||
// The ID of the current handle. Only valid after a call to Next.
|
||||
ID ID
|
||||
// The current Handle. Only valid until a call to Next.
|
||||
// See Take if you want to retain the handle.
|
||||
Handle *Handle
|
||||
err error
|
||||
}
|
||||
|
||||
// Next retrieves a handle for the next BTF blob.
|
||||
// Next retrieves a handle for the next BTF object.
|
||||
//
|
||||
// [Handle.Close] is called if *handle is non-nil to avoid leaking fds.
|
||||
//
|
||||
// Returns true if another BTF blob was found. Call [HandleIterator.Err] after
|
||||
// Returns true if another BTF object was found. Call [HandleIterator.Err] after
|
||||
// the function returns false.
|
||||
func (it *HandleIterator) Next(handle **Handle) bool {
|
||||
if *handle != nil {
|
||||
(*handle).Close()
|
||||
*handle = nil
|
||||
}
|
||||
|
||||
func (it *HandleIterator) Next() bool {
|
||||
id := it.ID
|
||||
for {
|
||||
attr := &sys.BtfGetNextIdAttr{Id: id}
|
||||
err := sys.BtfGetNextId(attr)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
// There are no more BTF objects.
|
||||
return false
|
||||
break
|
||||
} else if err != nil {
|
||||
it.err = fmt.Errorf("get next BTF ID: %w", err)
|
||||
return false
|
||||
break
|
||||
}
|
||||
|
||||
id = attr.NextId
|
||||
*handle, err = NewHandleFromID(id)
|
||||
handle, err := NewHandleFromID(id)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
// Try again with the next ID.
|
||||
continue
|
||||
} else if err != nil {
|
||||
it.err = fmt.Errorf("retrieve handle for ID %d: %w", id, err)
|
||||
return false
|
||||
break
|
||||
}
|
||||
|
||||
it.ID = id
|
||||
it.Handle.Close()
|
||||
it.ID, it.Handle = id, handle
|
||||
return true
|
||||
}
|
||||
|
||||
// No more handles or we encountered an error.
|
||||
it.Handle.Close()
|
||||
it.Handle = nil
|
||||
return false
|
||||
}
|
||||
|
||||
// Take the ownership of the current handle.
|
||||
//
|
||||
// It's the callers responsibility to close the handle.
|
||||
func (it *HandleIterator) Take() *Handle {
|
||||
handle := it.Handle
|
||||
it.Handle = nil
|
||||
return handle
|
||||
}
|
||||
|
||||
// Err returns an error if iteration failed for some reason.
|
||||
func (it *HandleIterator) Err() error {
|
||||
return it.err
|
||||
}
|
||||
|
||||
// FindHandle returns the first handle for which predicate returns true.
|
||||
//
|
||||
// Requires CAP_SYS_ADMIN.
|
||||
//
|
||||
// Returns an error wrapping ErrNotFound if predicate never returns true or if
|
||||
// there is no BTF loaded into the kernel.
|
||||
func FindHandle(predicate func(info *HandleInfo) bool) (*Handle, error) {
|
||||
it := new(HandleIterator)
|
||||
defer it.Handle.Close()
|
||||
|
||||
for it.Next() {
|
||||
info, err := it.Handle.Info()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("info for ID %d: %w", it.ID, err)
|
||||
}
|
||||
|
||||
if predicate(info) {
|
||||
return it.Take(), nil
|
||||
}
|
||||
}
|
||||
if err := it.Err(); err != nil {
|
||||
return nil, fmt.Errorf("iterate handles: %w", err)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("find handle: %w", ErrNotFound)
|
||||
}
|
||||
|
||||
+4
@@ -89,6 +89,10 @@ func (st *stringTable) lookup(offset uint32) (string, error) {
|
||||
}
|
||||
|
||||
func (st *stringTable) Length() int {
|
||||
if len(st.offsets) == 0 || len(st.strings) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
last := len(st.offsets) - 1
|
||||
return int(st.offsets[last]) + len(st.strings[last]) + 1
|
||||
}
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package btf
|
||||
|
||||
import "fmt"
|
||||
|
||||
// walkType calls fn on each child of typ.
|
||||
func walkType(typ Type, fn func(*Type)) {
|
||||
// Explicitly type switch on the most common types to allow the inliner to
|
||||
// do its work. This avoids allocating intermediate slices from walk() on
|
||||
// the heap.
|
||||
switch v := typ.(type) {
|
||||
case *Void, *Int, *Enum, *Fwd, *Float:
|
||||
// No children to traverse.
|
||||
case *Pointer:
|
||||
fn(&v.Target)
|
||||
case *Array:
|
||||
fn(&v.Index)
|
||||
fn(&v.Type)
|
||||
case *Struct:
|
||||
for i := range v.Members {
|
||||
fn(&v.Members[i].Type)
|
||||
}
|
||||
case *Union:
|
||||
for i := range v.Members {
|
||||
fn(&v.Members[i].Type)
|
||||
}
|
||||
case *Typedef:
|
||||
fn(&v.Type)
|
||||
case *Volatile:
|
||||
fn(&v.Type)
|
||||
case *Const:
|
||||
fn(&v.Type)
|
||||
case *Restrict:
|
||||
fn(&v.Type)
|
||||
case *Func:
|
||||
fn(&v.Type)
|
||||
case *FuncProto:
|
||||
fn(&v.Return)
|
||||
for i := range v.Params {
|
||||
fn(&v.Params[i].Type)
|
||||
}
|
||||
case *Var:
|
||||
fn(&v.Type)
|
||||
case *Datasec:
|
||||
for i := range v.Vars {
|
||||
fn(&v.Vars[i].Type)
|
||||
}
|
||||
case *declTag:
|
||||
fn(&v.Type)
|
||||
case *typeTag:
|
||||
fn(&v.Type)
|
||||
case *cycle:
|
||||
// cycle has children, but we ignore them deliberately.
|
||||
default:
|
||||
panic(fmt.Sprintf("don't know how to walk Type %T", v))
|
||||
}
|
||||
}
|
||||
+124
-156
@@ -8,6 +8,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/cilium/ebpf/asm"
|
||||
"github.com/cilium/ebpf/internal"
|
||||
)
|
||||
|
||||
const maxTypeDepth = 32
|
||||
@@ -35,9 +36,7 @@ type Type interface {
|
||||
// Make a copy of the type, without copying Type members.
|
||||
copy() Type
|
||||
|
||||
// Enumerate all nested Types. Repeated calls must visit nested
|
||||
// types in the same order.
|
||||
walk(*typeDeque)
|
||||
// New implementations must update walkType.
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -51,6 +50,9 @@ var (
|
||||
_ Type = (*Var)(nil)
|
||||
_ Type = (*Datasec)(nil)
|
||||
_ Type = (*Float)(nil)
|
||||
_ Type = (*declTag)(nil)
|
||||
_ Type = (*typeTag)(nil)
|
||||
_ Type = (*cycle)(nil)
|
||||
)
|
||||
|
||||
// types is a list of Type.
|
||||
@@ -72,40 +74,32 @@ func (v *Void) Format(fs fmt.State, verb rune) { formatType(fs, verb, v) }
|
||||
func (v *Void) TypeName() string { return "" }
|
||||
func (v *Void) size() uint32 { return 0 }
|
||||
func (v *Void) copy() Type { return (*Void)(nil) }
|
||||
func (v *Void) walk(*typeDeque) {}
|
||||
|
||||
type IntEncoding byte
|
||||
|
||||
// Valid IntEncodings.
|
||||
//
|
||||
// These may look like they are flags, but they aren't.
|
||||
const (
|
||||
Signed IntEncoding = 1 << iota
|
||||
Char
|
||||
Bool
|
||||
Unsigned IntEncoding = 0
|
||||
Signed IntEncoding = 1
|
||||
Char IntEncoding = 2
|
||||
Bool IntEncoding = 4
|
||||
)
|
||||
|
||||
func (ie IntEncoding) IsSigned() bool {
|
||||
return ie&Signed != 0
|
||||
}
|
||||
|
||||
func (ie IntEncoding) IsChar() bool {
|
||||
return ie&Char != 0
|
||||
}
|
||||
|
||||
func (ie IntEncoding) IsBool() bool {
|
||||
return ie&Bool != 0
|
||||
}
|
||||
|
||||
func (ie IntEncoding) String() string {
|
||||
switch {
|
||||
case ie.IsChar() && ie.IsSigned():
|
||||
switch ie {
|
||||
case Char:
|
||||
// NB: There is no way to determine signedness for char.
|
||||
return "char"
|
||||
case ie.IsChar() && !ie.IsSigned():
|
||||
return "uchar"
|
||||
case ie.IsBool():
|
||||
case Bool:
|
||||
return "bool"
|
||||
case ie.IsSigned():
|
||||
case Signed:
|
||||
return "signed"
|
||||
default:
|
||||
case Unsigned:
|
||||
return "unsigned"
|
||||
default:
|
||||
return fmt.Sprintf("IntEncoding(%d)", byte(ie))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +120,6 @@ func (i *Int) Format(fs fmt.State, verb rune) {
|
||||
|
||||
func (i *Int) TypeName() string { return i.Name }
|
||||
func (i *Int) size() uint32 { return i.Size }
|
||||
func (i *Int) walk(*typeDeque) {}
|
||||
func (i *Int) copy() Type {
|
||||
cpy := *i
|
||||
return &cpy
|
||||
@@ -141,9 +134,8 @@ func (p *Pointer) Format(fs fmt.State, verb rune) {
|
||||
formatType(fs, verb, p, "target=", p.Target)
|
||||
}
|
||||
|
||||
func (p *Pointer) TypeName() string { return "" }
|
||||
func (p *Pointer) size() uint32 { return 8 }
|
||||
func (p *Pointer) walk(tdq *typeDeque) { tdq.push(&p.Target) }
|
||||
func (p *Pointer) TypeName() string { return "" }
|
||||
func (p *Pointer) size() uint32 { return 8 }
|
||||
func (p *Pointer) copy() Type {
|
||||
cpy := *p
|
||||
return &cpy
|
||||
@@ -162,11 +154,6 @@ func (arr *Array) Format(fs fmt.State, verb rune) {
|
||||
|
||||
func (arr *Array) TypeName() string { return "" }
|
||||
|
||||
func (arr *Array) walk(tdq *typeDeque) {
|
||||
tdq.push(&arr.Index)
|
||||
tdq.push(&arr.Type)
|
||||
}
|
||||
|
||||
func (arr *Array) copy() Type {
|
||||
cpy := *arr
|
||||
return &cpy
|
||||
@@ -188,12 +175,6 @@ func (s *Struct) TypeName() string { return s.Name }
|
||||
|
||||
func (s *Struct) size() uint32 { return s.Size }
|
||||
|
||||
func (s *Struct) walk(tdq *typeDeque) {
|
||||
for i := range s.Members {
|
||||
tdq.push(&s.Members[i].Type)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Struct) copy() Type {
|
||||
cpy := *s
|
||||
cpy.Members = copyMembers(s.Members)
|
||||
@@ -220,12 +201,6 @@ func (u *Union) TypeName() string { return u.Name }
|
||||
|
||||
func (u *Union) size() uint32 { return u.Size }
|
||||
|
||||
func (u *Union) walk(tdq *typeDeque) {
|
||||
for i := range u.Members {
|
||||
tdq.push(&u.Members[i].Type)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Union) copy() Type {
|
||||
cpy := *u
|
||||
cpy.Members = copyMembers(u.Members)
|
||||
@@ -273,7 +248,9 @@ type Member struct {
|
||||
type Enum struct {
|
||||
Name string
|
||||
// Size of the enum value in bytes.
|
||||
Size uint32
|
||||
Size uint32
|
||||
// True if the values should be interpreted as signed integers.
|
||||
Signed bool
|
||||
Values []EnumValue
|
||||
}
|
||||
|
||||
@@ -288,11 +265,10 @@ func (e *Enum) TypeName() string { return e.Name }
|
||||
// Is is not a valid Type
|
||||
type EnumValue struct {
|
||||
Name string
|
||||
Value int32
|
||||
Value uint64
|
||||
}
|
||||
|
||||
func (e *Enum) size() uint32 { return e.Size }
|
||||
func (e *Enum) walk(*typeDeque) {}
|
||||
func (e *Enum) size() uint32 { return e.Size }
|
||||
func (e *Enum) copy() Type {
|
||||
cpy := *e
|
||||
cpy.Values = make([]EnumValue, len(e.Values))
|
||||
@@ -332,7 +308,6 @@ func (f *Fwd) Format(fs fmt.State, verb rune) {
|
||||
|
||||
func (f *Fwd) TypeName() string { return f.Name }
|
||||
|
||||
func (f *Fwd) walk(*typeDeque) {}
|
||||
func (f *Fwd) copy() Type {
|
||||
cpy := *f
|
||||
return &cpy
|
||||
@@ -350,7 +325,6 @@ func (td *Typedef) Format(fs fmt.State, verb rune) {
|
||||
|
||||
func (td *Typedef) TypeName() string { return td.Name }
|
||||
|
||||
func (td *Typedef) walk(tdq *typeDeque) { tdq.push(&td.Type) }
|
||||
func (td *Typedef) copy() Type {
|
||||
cpy := *td
|
||||
return &cpy
|
||||
@@ -367,8 +341,7 @@ func (v *Volatile) Format(fs fmt.State, verb rune) {
|
||||
|
||||
func (v *Volatile) TypeName() string { return "" }
|
||||
|
||||
func (v *Volatile) qualify() Type { return v.Type }
|
||||
func (v *Volatile) walk(tdq *typeDeque) { tdq.push(&v.Type) }
|
||||
func (v *Volatile) qualify() Type { return v.Type }
|
||||
func (v *Volatile) copy() Type {
|
||||
cpy := *v
|
||||
return &cpy
|
||||
@@ -385,8 +358,7 @@ func (c *Const) Format(fs fmt.State, verb rune) {
|
||||
|
||||
func (c *Const) TypeName() string { return "" }
|
||||
|
||||
func (c *Const) qualify() Type { return c.Type }
|
||||
func (c *Const) walk(tdq *typeDeque) { tdq.push(&c.Type) }
|
||||
func (c *Const) qualify() Type { return c.Type }
|
||||
func (c *Const) copy() Type {
|
||||
cpy := *c
|
||||
return &cpy
|
||||
@@ -403,8 +375,7 @@ func (r *Restrict) Format(fs fmt.State, verb rune) {
|
||||
|
||||
func (r *Restrict) TypeName() string { return "" }
|
||||
|
||||
func (r *Restrict) qualify() Type { return r.Type }
|
||||
func (r *Restrict) walk(tdq *typeDeque) { tdq.push(&r.Type) }
|
||||
func (r *Restrict) qualify() Type { return r.Type }
|
||||
func (r *Restrict) copy() Type {
|
||||
cpy := *r
|
||||
return &cpy
|
||||
@@ -428,7 +399,6 @@ func (f *Func) Format(fs fmt.State, verb rune) {
|
||||
|
||||
func (f *Func) TypeName() string { return f.Name }
|
||||
|
||||
func (f *Func) walk(tdq *typeDeque) { tdq.push(&f.Type) }
|
||||
func (f *Func) copy() Type {
|
||||
cpy := *f
|
||||
return &cpy
|
||||
@@ -446,13 +416,6 @@ func (fp *FuncProto) Format(fs fmt.State, verb rune) {
|
||||
|
||||
func (fp *FuncProto) TypeName() string { return "" }
|
||||
|
||||
func (fp *FuncProto) walk(tdq *typeDeque) {
|
||||
tdq.push(&fp.Return)
|
||||
for i := range fp.Params {
|
||||
tdq.push(&fp.Params[i].Type)
|
||||
}
|
||||
}
|
||||
|
||||
func (fp *FuncProto) copy() Type {
|
||||
cpy := *fp
|
||||
cpy.Params = make([]FuncParam, len(fp.Params))
|
||||
@@ -478,7 +441,6 @@ func (v *Var) Format(fs fmt.State, verb rune) {
|
||||
|
||||
func (v *Var) TypeName() string { return v.Name }
|
||||
|
||||
func (v *Var) walk(tdq *typeDeque) { tdq.push(&v.Type) }
|
||||
func (v *Var) copy() Type {
|
||||
cpy := *v
|
||||
return &cpy
|
||||
@@ -499,12 +461,6 @@ func (ds *Datasec) TypeName() string { return ds.Name }
|
||||
|
||||
func (ds *Datasec) size() uint32 { return ds.Size }
|
||||
|
||||
func (ds *Datasec) walk(tdq *typeDeque) {
|
||||
for i := range ds.Vars {
|
||||
tdq.push(&ds.Vars[i].Type)
|
||||
}
|
||||
}
|
||||
|
||||
func (ds *Datasec) copy() Type {
|
||||
cpy := *ds
|
||||
cpy.Vars = make([]VarSecinfo, len(ds.Vars))
|
||||
@@ -535,12 +491,48 @@ func (f *Float) Format(fs fmt.State, verb rune) {
|
||||
|
||||
func (f *Float) TypeName() string { return f.Name }
|
||||
func (f *Float) size() uint32 { return f.Size }
|
||||
func (f *Float) walk(*typeDeque) {}
|
||||
func (f *Float) copy() Type {
|
||||
cpy := *f
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// declTag associates metadata with a declaration.
|
||||
type declTag struct {
|
||||
Type Type
|
||||
Value string
|
||||
// The index this tag refers to in the target type. For composite types,
|
||||
// a value of -1 indicates that the tag refers to the whole type. Otherwise
|
||||
// it indicates which member or argument the tag applies to.
|
||||
Index int
|
||||
}
|
||||
|
||||
func (dt *declTag) Format(fs fmt.State, verb rune) {
|
||||
formatType(fs, verb, dt, "type=", dt.Type, "value=", dt.Value, "index=", dt.Index)
|
||||
}
|
||||
|
||||
func (dt *declTag) TypeName() string { return "" }
|
||||
func (dt *declTag) copy() Type {
|
||||
cpy := *dt
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// typeTag associates metadata with a type.
|
||||
type typeTag struct {
|
||||
Type Type
|
||||
Value string
|
||||
}
|
||||
|
||||
func (tt *typeTag) Format(fs fmt.State, verb rune) {
|
||||
formatType(fs, verb, tt, "type=", tt.Type, "value=", tt.Value)
|
||||
}
|
||||
|
||||
func (tt *typeTag) TypeName() string { return "" }
|
||||
func (tt *typeTag) qualify() Type { return tt.Type }
|
||||
func (tt *typeTag) copy() Type {
|
||||
cpy := *tt
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// cycle is a type which had to be elided since it exceeded maxTypeDepth.
|
||||
type cycle struct {
|
||||
root Type
|
||||
@@ -549,7 +541,6 @@ type cycle struct {
|
||||
func (c *cycle) ID() TypeID { return math.MaxUint32 }
|
||||
func (c *cycle) Format(fs fmt.State, verb rune) { formatType(fs, verb, c, "root=", c.root) }
|
||||
func (c *cycle) TypeName() string { return "" }
|
||||
func (c *cycle) walk(*typeDeque) {}
|
||||
func (c *cycle) copy() Type {
|
||||
cpy := *c
|
||||
return &cpy
|
||||
@@ -576,6 +567,7 @@ var (
|
||||
_ qualifier = (*Const)(nil)
|
||||
_ qualifier = (*Restrict)(nil)
|
||||
_ qualifier = (*Volatile)(nil)
|
||||
_ qualifier = (*typeTag)(nil)
|
||||
)
|
||||
|
||||
// Sizeof returns the size of a type in bytes.
|
||||
@@ -679,7 +671,7 @@ type copier map[Type]Type
|
||||
|
||||
func (c copier) copy(typ *Type, transform Transformer) {
|
||||
var work typeDeque
|
||||
for t := typ; t != nil; t = work.pop() {
|
||||
for t := typ; t != nil; t = work.Pop() {
|
||||
// *t is the identity of the type.
|
||||
if cpy := c[*t]; cpy != nil {
|
||||
*t = cpy
|
||||
@@ -697,83 +689,11 @@ func (c copier) copy(typ *Type, transform Transformer) {
|
||||
*t = cpy
|
||||
|
||||
// Mark any nested types for copying.
|
||||
cpy.walk(&work)
|
||||
walkType(cpy, work.Push)
|
||||
}
|
||||
}
|
||||
|
||||
// typeDeque keeps track of pointers to types which still
|
||||
// need to be visited.
|
||||
type typeDeque struct {
|
||||
types []*Type
|
||||
read, write uint64
|
||||
mask uint64
|
||||
}
|
||||
|
||||
func (dq *typeDeque) empty() bool {
|
||||
return dq.read == dq.write
|
||||
}
|
||||
|
||||
// push adds a type to the stack.
|
||||
func (dq *typeDeque) push(t *Type) {
|
||||
if dq.write-dq.read < uint64(len(dq.types)) {
|
||||
dq.types[dq.write&dq.mask] = t
|
||||
dq.write++
|
||||
return
|
||||
}
|
||||
|
||||
new := len(dq.types) * 2
|
||||
if new == 0 {
|
||||
new = 8
|
||||
}
|
||||
|
||||
types := make([]*Type, new)
|
||||
pivot := dq.read & dq.mask
|
||||
n := copy(types, dq.types[pivot:])
|
||||
n += copy(types[n:], dq.types[:pivot])
|
||||
types[n] = t
|
||||
|
||||
dq.types = types
|
||||
dq.mask = uint64(new) - 1
|
||||
dq.read, dq.write = 0, uint64(n+1)
|
||||
}
|
||||
|
||||
// shift returns the first element or null.
|
||||
func (dq *typeDeque) shift() *Type {
|
||||
if dq.empty() {
|
||||
return nil
|
||||
}
|
||||
|
||||
index := dq.read & dq.mask
|
||||
t := dq.types[index]
|
||||
dq.types[index] = nil
|
||||
dq.read++
|
||||
return t
|
||||
}
|
||||
|
||||
// pop returns the last element or null.
|
||||
func (dq *typeDeque) pop() *Type {
|
||||
if dq.empty() {
|
||||
return nil
|
||||
}
|
||||
|
||||
dq.write--
|
||||
index := dq.write & dq.mask
|
||||
t := dq.types[index]
|
||||
dq.types[index] = nil
|
||||
return t
|
||||
}
|
||||
|
||||
// all returns all elements.
|
||||
//
|
||||
// The deque is empty after calling this method.
|
||||
func (dq *typeDeque) all() []*Type {
|
||||
length := dq.write - dq.read
|
||||
types := make([]*Type, 0, length)
|
||||
for t := dq.shift(); t != nil; t = dq.shift() {
|
||||
types = append(types, t)
|
||||
}
|
||||
return types
|
||||
}
|
||||
type typeDeque = internal.Deque[*Type]
|
||||
|
||||
// inflateRawTypes takes a list of raw btf types linked via type IDs, and turns
|
||||
// it into a graph of Types connected via pointers.
|
||||
@@ -903,6 +823,7 @@ func inflateRawTypes(rawTypes []rawType, baseTypes types, rawStrings *stringTabl
|
||||
return members, nil
|
||||
}
|
||||
|
||||
var declTags []*declTag
|
||||
for i, raw := range rawTypes {
|
||||
var (
|
||||
id = typeIDOffset + TypeID(i)
|
||||
@@ -952,17 +873,20 @@ func inflateRawTypes(rawTypes []rawType, baseTypes types, rawStrings *stringTabl
|
||||
case kindEnum:
|
||||
rawvals := raw.data.([]btfEnum)
|
||||
vals := make([]EnumValue, 0, len(rawvals))
|
||||
signed := raw.KindFlag()
|
||||
for i, btfVal := range rawvals {
|
||||
name, err := rawStrings.Lookup(btfVal.NameOff)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get name for enum value %d: %s", i, err)
|
||||
}
|
||||
vals = append(vals, EnumValue{
|
||||
Name: name,
|
||||
Value: btfVal.Val,
|
||||
})
|
||||
value := uint64(btfVal.Val)
|
||||
if signed {
|
||||
// Sign extend values to 64 bit.
|
||||
value = uint64(int32(btfVal.Val))
|
||||
}
|
||||
vals = append(vals, EnumValue{name, value})
|
||||
}
|
||||
typ = &Enum{name, raw.Size(), vals}
|
||||
typ = &Enum{name, raw.Size(), signed, vals}
|
||||
|
||||
case kindForward:
|
||||
if raw.KindFlag() {
|
||||
@@ -1045,6 +969,28 @@ func inflateRawTypes(rawTypes []rawType, baseTypes types, rawStrings *stringTabl
|
||||
case kindFloat:
|
||||
typ = &Float{name, raw.Size()}
|
||||
|
||||
case kindDeclTag:
|
||||
btfIndex := raw.data.(*btfDeclTag).ComponentIdx
|
||||
if uint64(btfIndex) > math.MaxInt {
|
||||
return nil, fmt.Errorf("type id %d: index exceeds int", id)
|
||||
}
|
||||
|
||||
index := int(btfIndex)
|
||||
if btfIndex == math.MaxUint32 {
|
||||
index = -1
|
||||
}
|
||||
|
||||
dt := &declTag{nil, name, index}
|
||||
fixup(raw.Type(), &dt.Type)
|
||||
typ = dt
|
||||
|
||||
declTags = append(declTags, dt)
|
||||
|
||||
case kindTypeTag:
|
||||
tt := &typeTag{nil, name}
|
||||
fixup(raw.Type(), &tt.Type)
|
||||
typ = tt
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("type id %d: unknown kind: %v", id, raw.Kind())
|
||||
}
|
||||
@@ -1083,6 +1029,28 @@ func inflateRawTypes(rawTypes []rawType, baseTypes types, rawStrings *stringTabl
|
||||
}
|
||||
}
|
||||
|
||||
for _, dt := range declTags {
|
||||
switch t := dt.Type.(type) {
|
||||
case *Var, *Typedef:
|
||||
if dt.Index != -1 {
|
||||
return nil, fmt.Errorf("type %s: index %d is not -1", dt, dt.Index)
|
||||
}
|
||||
|
||||
case composite:
|
||||
if dt.Index >= len(t.members()) {
|
||||
return nil, fmt.Errorf("type %s: index %d exceeds members of %s", dt, dt.Index, t)
|
||||
}
|
||||
|
||||
case *Func:
|
||||
if dt.Index >= len(t.Type.(*FuncProto).Params) {
|
||||
return nil, fmt.Errorf("type %s: index %d exceeds params of %s", dt, dt.Index, t)
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("type %s: decl tag for type %s is not supported", dt, t)
|
||||
}
|
||||
}
|
||||
|
||||
return types, nil
|
||||
}
|
||||
|
||||
|
||||
+12
-12
@@ -111,8 +111,8 @@ func (cs *CollectionSpec) RewriteMaps(maps map[string]*Map) error {
|
||||
//
|
||||
// The constant must be defined like so in the C program:
|
||||
//
|
||||
// volatile const type foobar;
|
||||
// volatile const type foobar = default;
|
||||
// volatile const type foobar;
|
||||
// volatile const type foobar = default;
|
||||
//
|
||||
// Replacement values must be of the same length as the C sizeof(type).
|
||||
// If necessary, they are marshalled according to the same rules as
|
||||
@@ -198,11 +198,11 @@ func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error
|
||||
// The tag's value specifies the name of the program or map as
|
||||
// found in the CollectionSpec.
|
||||
//
|
||||
// struct {
|
||||
// Foo *ebpf.ProgramSpec `ebpf:"xdp_foo"`
|
||||
// Bar *ebpf.MapSpec `ebpf:"bar_map"`
|
||||
// Ignored int
|
||||
// }
|
||||
// struct {
|
||||
// Foo *ebpf.ProgramSpec `ebpf:"xdp_foo"`
|
||||
// Bar *ebpf.MapSpec `ebpf:"bar_map"`
|
||||
// Ignored int
|
||||
// }
|
||||
//
|
||||
// Returns an error if any of the eBPF objects can't be found, or
|
||||
// if the same MapSpec or ProgramSpec is assigned multiple times.
|
||||
@@ -249,11 +249,11 @@ func (cs *CollectionSpec) Assign(to interface{}) error {
|
||||
// dependent resources are loaded into the kernel and populated with values if
|
||||
// specified.
|
||||
//
|
||||
// struct {
|
||||
// Foo *ebpf.Program `ebpf:"xdp_foo"`
|
||||
// Bar *ebpf.Map `ebpf:"bar_map"`
|
||||
// Ignored int
|
||||
// }
|
||||
// struct {
|
||||
// Foo *ebpf.Program `ebpf:"xdp_foo"`
|
||||
// Bar *ebpf.Map `ebpf:"bar_map"`
|
||||
// Ignored int
|
||||
// }
|
||||
//
|
||||
// opts may be nil.
|
||||
//
|
||||
|
||||
+26
-21
@@ -261,10 +261,6 @@ func (ec *elfCode) loadRelocations(relSections map[elf.SectionIndex]*elf.Section
|
||||
return fmt.Errorf("section %q: reference to %q in section %s: %w", section.Name, rel.Name, rel.Section, ErrNotSupported)
|
||||
}
|
||||
|
||||
if target.Flags&elf.SHF_STRINGS > 0 {
|
||||
return fmt.Errorf("section %q: string is not stack allocated: %w", section.Name, ErrNotSupported)
|
||||
}
|
||||
|
||||
target.references++
|
||||
}
|
||||
|
||||
@@ -1027,22 +1023,33 @@ func (ec *elfCode) loadDataSections(maps map[string]*MapSpec) error {
|
||||
continue
|
||||
}
|
||||
|
||||
data, err := sec.Data()
|
||||
if err != nil {
|
||||
return fmt.Errorf("data section %s: can't get contents: %w", sec.Name, err)
|
||||
}
|
||||
|
||||
if uint64(len(data)) > math.MaxUint32 {
|
||||
return fmt.Errorf("data section %s: contents exceed maximum size", sec.Name)
|
||||
}
|
||||
|
||||
mapSpec := &MapSpec{
|
||||
Name: SanitizeName(sec.Name, -1),
|
||||
Type: Array,
|
||||
KeySize: 4,
|
||||
ValueSize: uint32(len(data)),
|
||||
ValueSize: uint32(sec.Size),
|
||||
MaxEntries: 1,
|
||||
Contents: []MapKV{{uint32(0), data}},
|
||||
}
|
||||
|
||||
switch sec.Type {
|
||||
// Only open the section if we know there's actual data to be read.
|
||||
case elf.SHT_PROGBITS:
|
||||
data, err := sec.Data()
|
||||
if err != nil {
|
||||
return fmt.Errorf("data section %s: can't get contents: %w", sec.Name, err)
|
||||
}
|
||||
|
||||
if uint64(len(data)) > math.MaxUint32 {
|
||||
return fmt.Errorf("data section %s: contents exceed maximum size", sec.Name)
|
||||
}
|
||||
mapSpec.Contents = []MapKV{{uint32(0), data}}
|
||||
|
||||
case elf.SHT_NOBITS:
|
||||
// NOBITS sections like .bss contain only zeroes, and since data sections
|
||||
// are Arrays, the kernel already preallocates them. Skip reading zeroes
|
||||
// from the ELF.
|
||||
default:
|
||||
return fmt.Errorf("data section %s: unknown section type %s", sec.Name, sec.Type)
|
||||
}
|
||||
|
||||
// It is possible for a data section to exist without a corresponding BTF Datasec
|
||||
@@ -1057,13 +1064,9 @@ func (ec *elfCode) loadDataSections(maps map[string]*MapSpec) error {
|
||||
}
|
||||
}
|
||||
|
||||
switch n := sec.Name; {
|
||||
case strings.HasPrefix(n, ".rodata"):
|
||||
if strings.HasPrefix(sec.Name, ".rodata") {
|
||||
mapSpec.Flags = unix.BPF_F_RDONLY_PROG
|
||||
mapSpec.Freeze = true
|
||||
case n == ".bss":
|
||||
// The kernel already zero-initializes the map
|
||||
mapSpec.Contents = nil
|
||||
}
|
||||
|
||||
maps[sec.Name] = mapSpec
|
||||
@@ -1107,6 +1110,7 @@ func getProgType(sectionName string) (ProgramType, AttachType, uint32, string) {
|
||||
{"lsm/", LSM, AttachLSMMac, 0},
|
||||
{"lsm.s/", LSM, AttachLSMMac, unix.BPF_F_SLEEPABLE},
|
||||
{"iter/", Tracing, AttachTraceIter, 0},
|
||||
{"iter.s/", Tracing, AttachTraceIter, unix.BPF_F_SLEEPABLE},
|
||||
{"syscall", Syscall, AttachNone, 0},
|
||||
{"xdp_devmap/", XDP, AttachXDPDevMap, 0},
|
||||
{"xdp_cpumap/", XDP, AttachXDPCPUMap, 0},
|
||||
@@ -1149,8 +1153,9 @@ func getProgType(sectionName string) (ProgramType, AttachType, uint32, string) {
|
||||
{"cgroup/setsockopt", CGroupSockopt, AttachCGroupSetsockopt, 0},
|
||||
{"struct_ops+", StructOps, AttachNone, 0},
|
||||
{"sk_lookup/", SkLookup, AttachSkLookup, 0},
|
||||
|
||||
{"seccomp", SocketFilter, AttachNone, 0},
|
||||
{"kprobe.multi", Kprobe, AttachTraceKprobeMulti, 0},
|
||||
{"kretprobe.multi", Kprobe, AttachTraceKprobeMulti, 0},
|
||||
}
|
||||
|
||||
for _, t := range types {
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ func newMapInfoFromFd(fd *sys.FD) (*MapInfo, error) {
|
||||
info.KeySize,
|
||||
info.ValueSize,
|
||||
info.MaxEntries,
|
||||
info.MapFlags,
|
||||
uint32(info.MapFlags),
|
||||
unix.ByteSliceToString(info.Name[:]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
package internal
|
||||
|
||||
import "math/bits"
|
||||
|
||||
// Deque implements a double ended queue.
|
||||
type Deque[T any] struct {
|
||||
elems []T
|
||||
read, write uint64
|
||||
mask uint64
|
||||
}
|
||||
|
||||
func (dq *Deque[T]) Empty() bool {
|
||||
return dq.read == dq.write
|
||||
}
|
||||
|
||||
func (dq *Deque[T]) remainingCap() int {
|
||||
return len(dq.elems) - int(dq.write-dq.read)
|
||||
}
|
||||
|
||||
// Push adds an element to the end.
|
||||
func (dq *Deque[T]) Push(e T) {
|
||||
if dq.remainingCap() >= 1 {
|
||||
dq.elems[dq.write&dq.mask] = e
|
||||
dq.write++
|
||||
return
|
||||
}
|
||||
|
||||
elems := dq.linearise(1)
|
||||
elems = append(elems, e)
|
||||
|
||||
dq.elems = elems[:cap(elems)]
|
||||
dq.mask = uint64(cap(elems)) - 1
|
||||
dq.read, dq.write = 0, uint64(len(elems))
|
||||
}
|
||||
|
||||
// Shift returns the first element or the zero value.
|
||||
func (dq *Deque[T]) Shift() T {
|
||||
var zero T
|
||||
|
||||
if dq.Empty() {
|
||||
return zero
|
||||
}
|
||||
|
||||
index := dq.read & dq.mask
|
||||
t := dq.elems[index]
|
||||
dq.elems[index] = zero
|
||||
dq.read++
|
||||
return t
|
||||
}
|
||||
|
||||
// Pop returns the last element or the zero value.
|
||||
func (dq *Deque[T]) Pop() T {
|
||||
var zero T
|
||||
|
||||
if dq.Empty() {
|
||||
return zero
|
||||
}
|
||||
|
||||
dq.write--
|
||||
index := dq.write & dq.mask
|
||||
t := dq.elems[index]
|
||||
dq.elems[index] = zero
|
||||
return t
|
||||
}
|
||||
|
||||
// linearise the contents of the deque.
|
||||
//
|
||||
// The returned slice has space for at least n more elements and has power
|
||||
// of two capacity.
|
||||
func (dq *Deque[T]) linearise(n int) []T {
|
||||
length := dq.write - dq.read
|
||||
need := length + uint64(n)
|
||||
if need < length {
|
||||
panic("overflow")
|
||||
}
|
||||
|
||||
// Round up to the new power of two which is at least 8.
|
||||
// See https://jameshfisher.com/2018/03/30/round-up-power-2/
|
||||
capacity := 1 << (64 - bits.LeadingZeros64(need-1))
|
||||
if capacity < 8 {
|
||||
capacity = 8
|
||||
}
|
||||
|
||||
types := make([]T, length, capacity)
|
||||
pivot := dq.read & dq.mask
|
||||
copied := copy(types, dq.elems[pivot:])
|
||||
copy(types[copied:], dq.elems[:pivot])
|
||||
return types
|
||||
}
|
||||
+9
-16
@@ -7,32 +7,25 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ErrorWithLog returns an error which includes logs from the kernel verifier.
|
||||
// ErrorWithLog wraps err in a VerifierError that includes the parsed verifier
|
||||
// log buffer.
|
||||
//
|
||||
// The default error output is a summary of the full log. The latter can be
|
||||
// accessed via VerifierError.Log or by formatting the error, see Format.
|
||||
//
|
||||
// A set of heuristics is used to determine whether the log has been truncated.
|
||||
func ErrorWithLog(err error, log []byte) *VerifierError {
|
||||
func ErrorWithLog(err error, log []byte, truncated bool) *VerifierError {
|
||||
const whitespace = "\t\r\v\n "
|
||||
|
||||
// Convert verifier log C string by truncating it on the first 0 byte
|
||||
// and trimming trailing whitespace before interpreting as a Go string.
|
||||
truncated := false
|
||||
if i := bytes.IndexByte(log, 0); i != -1 {
|
||||
if i == len(log)-1 && !bytes.HasSuffix(log[:i], []byte{'\n'}) {
|
||||
// The null byte is at the end of the buffer and it's not preceded
|
||||
// by a newline character. Most likely the buffer was too short.
|
||||
truncated = true
|
||||
}
|
||||
|
||||
log = log[:i]
|
||||
} else if len(log) > 0 {
|
||||
// No null byte? Dodgy!
|
||||
truncated = true
|
||||
}
|
||||
|
||||
log = bytes.Trim(log, whitespace)
|
||||
if len(log) == 0 {
|
||||
return &VerifierError{err, nil, truncated}
|
||||
}
|
||||
|
||||
logLines := bytes.Split(log, []byte{'\n'})
|
||||
lines := make([]string, 0, len(logLines))
|
||||
for _, line := range logLines {
|
||||
@@ -143,8 +136,8 @@ func includePreviousLine(line string) bool {
|
||||
// Understood verbs are %s and %v, which are equivalent to calling Error(). %v
|
||||
// allows outputting additional information using the following flags:
|
||||
//
|
||||
// + Output the first <width> lines, or all lines if no width is given.
|
||||
// - Output the last <width> lines, or all lines if no width is given.
|
||||
// %+<width>v: Output the first <width> lines, or all lines if no width is given.
|
||||
// %-<width>v: Output the last <width> lines, or all lines if no width is given.
|
||||
//
|
||||
// Use width to specify how many lines to output. Use the '-' flag to output
|
||||
// lines from the end of the log instead of the beginning.
|
||||
|
||||
+3
-3
@@ -42,9 +42,9 @@ type featureTest struct {
|
||||
//
|
||||
// The return values have the following semantics:
|
||||
//
|
||||
// err == ErrNotSupported: the feature is not available
|
||||
// err == nil: the feature is available
|
||||
// err != nil: the test couldn't be executed
|
||||
// err == ErrNotSupported: the feature is not available
|
||||
// err == nil: the feature is available
|
||||
// err != nil: the test couldn't be executed
|
||||
type FeatureTestFn func() error
|
||||
|
||||
// FeatureTest wraps a function so that it is run at most once.
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package internal
|
||||
|
||||
// EmptyBPFContext is the smallest-possible BPF input context to be used for
|
||||
// invoking `Program.{Run,Benchmark,Test}`.
|
||||
//
|
||||
// Programs require a context input buffer of at least 15 bytes. Looking in
|
||||
// net/bpf/test_run.c, bpf_test_init() requires that the input is at least
|
||||
// ETH_HLEN (14) bytes. As of Linux commit fd18942 ("bpf: Don't redirect packets
|
||||
// with invalid pkt_len"), it also requires the skb to be non-empty after
|
||||
// removing the Layer 2 header.
|
||||
var EmptyBPFContext = make([]byte, 15)
|
||||
+14
@@ -36,3 +36,17 @@ func NewStringPointer(str string) Pointer {
|
||||
|
||||
return Pointer{ptr: unsafe.Pointer(p)}
|
||||
}
|
||||
|
||||
// NewStringSlicePointer allocates an array of Pointers to each string in the
|
||||
// given slice of strings and returns a 64-bit pointer to the start of the
|
||||
// resulting array.
|
||||
//
|
||||
// Use this function to pass arrays of strings as syscall arguments.
|
||||
func NewStringSlicePointer(strings []string) Pointer {
|
||||
sp := make([]Pointer, 0, len(strings))
|
||||
for _, s := range strings {
|
||||
sp = append(sp, NewStringPointer(s))
|
||||
}
|
||||
|
||||
return Pointer{ptr: unsafe.Pointer(&sp[0])}
|
||||
}
|
||||
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
package sys
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
"github.com/cilium/ebpf/internal/unix"
|
||||
)
|
||||
|
||||
var profSet unix.Sigset_t
|
||||
|
||||
func init() {
|
||||
if err := sigsetAdd(&profSet, unix.SIGPROF); err != nil {
|
||||
panic(fmt.Errorf("creating signal set: %w", err))
|
||||
}
|
||||
}
|
||||
|
||||
// maskProfilerSignal locks the calling goroutine to its underlying OS thread
|
||||
// and adds SIGPROF to the thread's signal mask. This prevents pprof from
|
||||
// interrupting expensive syscalls like e.g. BPF_PROG_LOAD.
|
||||
//
|
||||
// The caller must defer sys.UnmaskProfilerSignal() to reverse the operation.
|
||||
func maskProfilerSignal() {
|
||||
runtime.LockOSThread()
|
||||
|
||||
if err := unix.PthreadSigmask(unix.SIG_BLOCK, &profSet, nil); err != nil {
|
||||
runtime.UnlockOSThread()
|
||||
panic(fmt.Errorf("masking profiler signal: %w", err))
|
||||
}
|
||||
}
|
||||
|
||||
// unmaskProfilerSignal removes SIGPROF from the underlying thread's signal
|
||||
// mask, allowing it to be interrupted for profiling once again.
|
||||
//
|
||||
// It also unlocks the current goroutine from its underlying OS thread.
|
||||
func unmaskProfilerSignal() {
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
if err := unix.PthreadSigmask(unix.SIG_UNBLOCK, &profSet, nil); err != nil {
|
||||
panic(fmt.Errorf("unmasking profiler signal: %w", err))
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
wordBytes = int(unsafe.Sizeof(unix.Sigset_t{}.Val[0]))
|
||||
wordBits = wordBytes * 8
|
||||
|
||||
setBytes = int(unsafe.Sizeof(unix.Sigset_t{}))
|
||||
setBits = setBytes * 8
|
||||
)
|
||||
|
||||
// sigsetAdd adds signal to set.
|
||||
//
|
||||
// Note: Sigset_t.Val's value type is uint32 or uint64 depending on the arch.
|
||||
// This function must be able to deal with both and so must avoid any direct
|
||||
// references to u32 or u64 types.
|
||||
func sigsetAdd(set *unix.Sigset_t, signal unix.Signal) error {
|
||||
if signal < 1 {
|
||||
return fmt.Errorf("signal %d must be larger than 0", signal)
|
||||
}
|
||||
if int(signal) > setBits {
|
||||
return fmt.Errorf("signal %d does not fit within unix.Sigset_t", signal)
|
||||
}
|
||||
|
||||
// For amd64, runtime.sigaddset() performs the following operation:
|
||||
// set[(signal-1)/32] |= 1 << ((uint32(signal) - 1) & 31)
|
||||
//
|
||||
// This trick depends on sigset being two u32's, causing a signal in the the
|
||||
// bottom 31 bits to be written to the low word if bit 32 is low, or the high
|
||||
// word if bit 32 is high.
|
||||
|
||||
// Signal is the nth bit in the bitfield.
|
||||
bit := int(signal - 1)
|
||||
// Word within the sigset the bit needs to be written to.
|
||||
word := bit / wordBits
|
||||
|
||||
// Write the signal bit into its corresponding word at the corrected offset.
|
||||
set.Val[word] |= 1 << (bit % wordBits)
|
||||
|
||||
return nil
|
||||
}
|
||||
+35
-4
@@ -8,10 +8,22 @@ import (
|
||||
"github.com/cilium/ebpf/internal/unix"
|
||||
)
|
||||
|
||||
// ENOTSUPP is a Linux internal error code that has leaked into UAPI.
|
||||
//
|
||||
// It is not the same as ENOTSUP or EOPNOTSUPP.
|
||||
var ENOTSUPP = syscall.Errno(524)
|
||||
|
||||
// BPF wraps SYS_BPF.
|
||||
//
|
||||
// Any pointers contained in attr must use the Pointer type from this package.
|
||||
func BPF(cmd Cmd, attr unsafe.Pointer, size uintptr) (uintptr, error) {
|
||||
// Prevent the Go profiler from repeatedly interrupting the verifier,
|
||||
// which could otherwise lead to a livelock due to receiving EAGAIN.
|
||||
if cmd == BPF_PROG_LOAD {
|
||||
maskProfilerSignal()
|
||||
defer unmaskProfilerSignal()
|
||||
}
|
||||
|
||||
for {
|
||||
r1, _, errNo := unix.Syscall(unix.SYS_BPF, uintptr(cmd), uintptr(attr), size)
|
||||
runtime.KeepAlive(attr)
|
||||
@@ -33,10 +45,10 @@ func BPF(cmd Cmd, attr unsafe.Pointer, size uintptr) (uintptr, error) {
|
||||
|
||||
// Info is implemented by all structs that can be passed to the ObjInfo syscall.
|
||||
//
|
||||
// MapInfo
|
||||
// ProgInfo
|
||||
// LinkInfo
|
||||
// BtfInfo
|
||||
// MapInfo
|
||||
// ProgInfo
|
||||
// LinkInfo
|
||||
// BtfInfo
|
||||
type Info interface {
|
||||
info() (unsafe.Pointer, uint32)
|
||||
}
|
||||
@@ -90,12 +102,24 @@ func NewObjName(name string) ObjName {
|
||||
return result
|
||||
}
|
||||
|
||||
// LogLevel controls the verbosity of the kernel's eBPF program verifier.
|
||||
type LogLevel uint32
|
||||
|
||||
const (
|
||||
BPF_LOG_LEVEL1 LogLevel = 1 << iota
|
||||
BPF_LOG_LEVEL2
|
||||
BPF_LOG_STATS
|
||||
)
|
||||
|
||||
// LinkID uniquely identifies a bpf_link.
|
||||
type LinkID uint32
|
||||
|
||||
// BTFID uniquely identifies a BTF blob loaded into the kernel.
|
||||
type BTFID uint32
|
||||
|
||||
// MapFlags control map behaviour.
|
||||
type MapFlags uint32
|
||||
|
||||
// wrappedErrno wraps syscall.Errno to prevent direct comparisons with
|
||||
// syscall.E* or unix.E* constants.
|
||||
//
|
||||
@@ -108,6 +132,13 @@ func (we wrappedErrno) Unwrap() error {
|
||||
return we.Errno
|
||||
}
|
||||
|
||||
func (we wrappedErrno) Error() string {
|
||||
if we.Errno == ENOTSUPP {
|
||||
return "operation not supported"
|
||||
}
|
||||
return we.Errno.Error()
|
||||
}
|
||||
|
||||
type syscallError struct {
|
||||
error
|
||||
errno syscall.Errno
|
||||
|
||||
+37
-17
@@ -6,14 +6,14 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type AdjRoomMode int32
|
||||
type AdjRoomMode uint32
|
||||
|
||||
const (
|
||||
BPF_ADJ_ROOM_NET AdjRoomMode = 0
|
||||
BPF_ADJ_ROOM_MAC AdjRoomMode = 1
|
||||
)
|
||||
|
||||
type AttachType int32
|
||||
type AttachType uint32
|
||||
|
||||
const (
|
||||
BPF_CGROUP_INET_INGRESS AttachType = 0
|
||||
@@ -62,7 +62,7 @@ const (
|
||||
__MAX_BPF_ATTACH_TYPE AttachType = 43
|
||||
)
|
||||
|
||||
type Cmd int32
|
||||
type Cmd uint32
|
||||
|
||||
const (
|
||||
BPF_MAP_CREATE Cmd = 0
|
||||
@@ -104,7 +104,7 @@ const (
|
||||
BPF_PROG_BIND_MAP Cmd = 35
|
||||
)
|
||||
|
||||
type FunctionId int32
|
||||
type FunctionId uint32
|
||||
|
||||
const (
|
||||
BPF_FUNC_unspec FunctionId = 0
|
||||
@@ -304,14 +304,14 @@ const (
|
||||
__BPF_FUNC_MAX_ID FunctionId = 194
|
||||
)
|
||||
|
||||
type HdrStartOff int32
|
||||
type HdrStartOff uint32
|
||||
|
||||
const (
|
||||
BPF_HDR_START_MAC HdrStartOff = 0
|
||||
BPF_HDR_START_NET HdrStartOff = 1
|
||||
)
|
||||
|
||||
type LinkType int32
|
||||
type LinkType uint32
|
||||
|
||||
const (
|
||||
BPF_LINK_TYPE_UNSPEC LinkType = 0
|
||||
@@ -326,7 +326,7 @@ const (
|
||||
MAX_BPF_LINK_TYPE LinkType = 9
|
||||
)
|
||||
|
||||
type MapType int32
|
||||
type MapType uint32
|
||||
|
||||
const (
|
||||
BPF_MAP_TYPE_UNSPEC MapType = 0
|
||||
@@ -362,7 +362,7 @@ const (
|
||||
BPF_MAP_TYPE_BLOOM_FILTER MapType = 30
|
||||
)
|
||||
|
||||
type ProgType int32
|
||||
type ProgType uint32
|
||||
|
||||
const (
|
||||
BPF_PROG_TYPE_UNSPEC ProgType = 0
|
||||
@@ -399,7 +399,7 @@ const (
|
||||
BPF_PROG_TYPE_SYSCALL ProgType = 31
|
||||
)
|
||||
|
||||
type RetCode int32
|
||||
type RetCode uint32
|
||||
|
||||
const (
|
||||
BPF_OK RetCode = 0
|
||||
@@ -408,14 +408,14 @@ const (
|
||||
BPF_LWT_REROUTE RetCode = 128
|
||||
)
|
||||
|
||||
type SkAction int32
|
||||
type SkAction uint32
|
||||
|
||||
const (
|
||||
SK_DROP SkAction = 0
|
||||
SK_PASS SkAction = 1
|
||||
)
|
||||
|
||||
type StackBuildIdStatus int32
|
||||
type StackBuildIdStatus uint32
|
||||
|
||||
const (
|
||||
BPF_STACK_BUILD_ID_EMPTY StackBuildIdStatus = 0
|
||||
@@ -423,13 +423,13 @@ const (
|
||||
BPF_STACK_BUILD_ID_IP StackBuildIdStatus = 2
|
||||
)
|
||||
|
||||
type StatsType int32
|
||||
type StatsType uint32
|
||||
|
||||
const (
|
||||
BPF_STATS_RUN_TIME StatsType = 0
|
||||
)
|
||||
|
||||
type XdpAction int32
|
||||
type XdpAction uint32
|
||||
|
||||
const (
|
||||
XDP_ABORTED XdpAction = 0
|
||||
@@ -474,7 +474,7 @@ type MapInfo struct {
|
||||
KeySize uint32
|
||||
ValueSize uint32
|
||||
MaxEntries uint32
|
||||
MapFlags uint32
|
||||
MapFlags MapFlags
|
||||
Name ObjName
|
||||
Ifindex uint32
|
||||
BtfVmlinuxValueTypeId uint32
|
||||
@@ -646,6 +646,26 @@ func LinkCreateIter(attr *LinkCreateIterAttr) (*FD, error) {
|
||||
return NewFD(int(fd))
|
||||
}
|
||||
|
||||
type LinkCreateKprobeMultiAttr struct {
|
||||
ProgFd uint32
|
||||
TargetFd uint32
|
||||
AttachType AttachType
|
||||
Flags uint32
|
||||
KprobeMultiFlags uint32
|
||||
Count uint32
|
||||
Syms Pointer
|
||||
Addrs Pointer
|
||||
Cookies Pointer
|
||||
}
|
||||
|
||||
func LinkCreateKprobeMulti(attr *LinkCreateKprobeMultiAttr) (*FD, error) {
|
||||
fd, err := BPF(BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewFD(int(fd))
|
||||
}
|
||||
|
||||
type LinkCreatePerfEventAttr struct {
|
||||
ProgFd uint32
|
||||
TargetFd uint32
|
||||
@@ -680,7 +700,7 @@ type MapCreateAttr struct {
|
||||
KeySize uint32
|
||||
ValueSize uint32
|
||||
MaxEntries uint32
|
||||
MapFlags uint32
|
||||
MapFlags MapFlags
|
||||
InnerMapFd uint32
|
||||
NumaNode uint32
|
||||
MapName ObjName
|
||||
@@ -951,7 +971,7 @@ type ProgLoadAttr struct {
|
||||
InsnCnt uint32
|
||||
Insns Pointer
|
||||
License Pointer
|
||||
LogLevel uint32
|
||||
LogLevel LogLevel
|
||||
LogSize uint32
|
||||
LogBuf Pointer
|
||||
KernVersion uint32
|
||||
@@ -967,7 +987,7 @@ type ProgLoadAttr struct {
|
||||
LineInfo Pointer
|
||||
LineInfoCnt uint32
|
||||
AttachBtfId uint32
|
||||
AttachProgFd uint32
|
||||
AttachBtfObjFd uint32
|
||||
CoreReloCnt uint32
|
||||
FdArray Pointer
|
||||
CoreRelos Pointer
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// Package unix re-exports Linux specific parts of golang.org/x/sys/unix.
|
||||
//
|
||||
// It avoids breaking compilation on other OS by providing stubs as follows:
|
||||
// - Invoking a function always returns an error.
|
||||
// - Errnos have distinct, non-zero values.
|
||||
// - Constants have distinct but meaningless values.
|
||||
// - Types use the same names for members, but may or may not follow the
|
||||
// Linux layout.
|
||||
package unix
|
||||
|
||||
// Note: please don't add any custom API to this package. Use internal/sys instead.
|
||||
+61
-84
@@ -1,5 +1,4 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package unix
|
||||
|
||||
@@ -24,175 +23,153 @@ const (
|
||||
E2BIG = linux.E2BIG
|
||||
EFAULT = linux.EFAULT
|
||||
EACCES = linux.EACCES
|
||||
// ENOTSUPP is not the same as ENOTSUP or EOPNOTSUP
|
||||
ENOTSUPP = syscall.Errno(0x20c)
|
||||
|
||||
BPF_F_NO_PREALLOC = linux.BPF_F_NO_PREALLOC
|
||||
BPF_F_NUMA_NODE = linux.BPF_F_NUMA_NODE
|
||||
BPF_F_RDONLY = linux.BPF_F_RDONLY
|
||||
BPF_F_WRONLY = linux.BPF_F_WRONLY
|
||||
BPF_F_RDONLY_PROG = linux.BPF_F_RDONLY_PROG
|
||||
BPF_F_WRONLY_PROG = linux.BPF_F_WRONLY_PROG
|
||||
BPF_F_SLEEPABLE = linux.BPF_F_SLEEPABLE
|
||||
BPF_F_MMAPABLE = linux.BPF_F_MMAPABLE
|
||||
BPF_F_INNER_MAP = linux.BPF_F_INNER_MAP
|
||||
BPF_OBJ_NAME_LEN = linux.BPF_OBJ_NAME_LEN
|
||||
BPF_TAG_SIZE = linux.BPF_TAG_SIZE
|
||||
BPF_RINGBUF_BUSY_BIT = linux.BPF_RINGBUF_BUSY_BIT
|
||||
BPF_RINGBUF_DISCARD_BIT = linux.BPF_RINGBUF_DISCARD_BIT
|
||||
BPF_RINGBUF_HDR_SZ = linux.BPF_RINGBUF_HDR_SZ
|
||||
SYS_BPF = linux.SYS_BPF
|
||||
F_DUPFD_CLOEXEC = linux.F_DUPFD_CLOEXEC
|
||||
EPOLL_CTL_ADD = linux.EPOLL_CTL_ADD
|
||||
EPOLL_CLOEXEC = linux.EPOLL_CLOEXEC
|
||||
O_CLOEXEC = linux.O_CLOEXEC
|
||||
O_NONBLOCK = linux.O_NONBLOCK
|
||||
PROT_READ = linux.PROT_READ
|
||||
PROT_WRITE = linux.PROT_WRITE
|
||||
MAP_SHARED = linux.MAP_SHARED
|
||||
PERF_ATTR_SIZE_VER1 = linux.PERF_ATTR_SIZE_VER1
|
||||
PERF_TYPE_SOFTWARE = linux.PERF_TYPE_SOFTWARE
|
||||
PERF_TYPE_TRACEPOINT = linux.PERF_TYPE_TRACEPOINT
|
||||
PERF_COUNT_SW_BPF_OUTPUT = linux.PERF_COUNT_SW_BPF_OUTPUT
|
||||
PERF_EVENT_IOC_DISABLE = linux.PERF_EVENT_IOC_DISABLE
|
||||
PERF_EVENT_IOC_ENABLE = linux.PERF_EVENT_IOC_ENABLE
|
||||
PERF_EVENT_IOC_SET_BPF = linux.PERF_EVENT_IOC_SET_BPF
|
||||
PerfBitWatermark = linux.PerfBitWatermark
|
||||
PERF_SAMPLE_RAW = linux.PERF_SAMPLE_RAW
|
||||
PERF_FLAG_FD_CLOEXEC = linux.PERF_FLAG_FD_CLOEXEC
|
||||
RLIM_INFINITY = linux.RLIM_INFINITY
|
||||
RLIMIT_MEMLOCK = linux.RLIMIT_MEMLOCK
|
||||
BPF_STATS_RUN_TIME = linux.BPF_STATS_RUN_TIME
|
||||
PERF_RECORD_LOST = linux.PERF_RECORD_LOST
|
||||
PERF_RECORD_SAMPLE = linux.PERF_RECORD_SAMPLE
|
||||
AT_FDCWD = linux.AT_FDCWD
|
||||
RENAME_NOREPLACE = linux.RENAME_NOREPLACE
|
||||
SO_ATTACH_BPF = linux.SO_ATTACH_BPF
|
||||
SO_DETACH_BPF = linux.SO_DETACH_BPF
|
||||
SOL_SOCKET = linux.SOL_SOCKET
|
||||
EILSEQ = linux.EILSEQ
|
||||
)
|
||||
|
||||
const (
|
||||
BPF_F_NO_PREALLOC = linux.BPF_F_NO_PREALLOC
|
||||
BPF_F_NUMA_NODE = linux.BPF_F_NUMA_NODE
|
||||
BPF_F_RDONLY = linux.BPF_F_RDONLY
|
||||
BPF_F_WRONLY = linux.BPF_F_WRONLY
|
||||
BPF_F_RDONLY_PROG = linux.BPF_F_RDONLY_PROG
|
||||
BPF_F_WRONLY_PROG = linux.BPF_F_WRONLY_PROG
|
||||
BPF_F_SLEEPABLE = linux.BPF_F_SLEEPABLE
|
||||
BPF_F_MMAPABLE = linux.BPF_F_MMAPABLE
|
||||
BPF_F_INNER_MAP = linux.BPF_F_INNER_MAP
|
||||
BPF_F_KPROBE_MULTI_RETURN = linux.BPF_F_KPROBE_MULTI_RETURN
|
||||
BPF_OBJ_NAME_LEN = linux.BPF_OBJ_NAME_LEN
|
||||
BPF_TAG_SIZE = linux.BPF_TAG_SIZE
|
||||
BPF_RINGBUF_BUSY_BIT = linux.BPF_RINGBUF_BUSY_BIT
|
||||
BPF_RINGBUF_DISCARD_BIT = linux.BPF_RINGBUF_DISCARD_BIT
|
||||
BPF_RINGBUF_HDR_SZ = linux.BPF_RINGBUF_HDR_SZ
|
||||
SYS_BPF = linux.SYS_BPF
|
||||
F_DUPFD_CLOEXEC = linux.F_DUPFD_CLOEXEC
|
||||
EPOLL_CTL_ADD = linux.EPOLL_CTL_ADD
|
||||
EPOLL_CLOEXEC = linux.EPOLL_CLOEXEC
|
||||
O_CLOEXEC = linux.O_CLOEXEC
|
||||
O_NONBLOCK = linux.O_NONBLOCK
|
||||
PROT_READ = linux.PROT_READ
|
||||
PROT_WRITE = linux.PROT_WRITE
|
||||
MAP_SHARED = linux.MAP_SHARED
|
||||
PERF_ATTR_SIZE_VER1 = linux.PERF_ATTR_SIZE_VER1
|
||||
PERF_TYPE_SOFTWARE = linux.PERF_TYPE_SOFTWARE
|
||||
PERF_TYPE_TRACEPOINT = linux.PERF_TYPE_TRACEPOINT
|
||||
PERF_COUNT_SW_BPF_OUTPUT = linux.PERF_COUNT_SW_BPF_OUTPUT
|
||||
PERF_EVENT_IOC_DISABLE = linux.PERF_EVENT_IOC_DISABLE
|
||||
PERF_EVENT_IOC_ENABLE = linux.PERF_EVENT_IOC_ENABLE
|
||||
PERF_EVENT_IOC_SET_BPF = linux.PERF_EVENT_IOC_SET_BPF
|
||||
PerfBitWatermark = linux.PerfBitWatermark
|
||||
PERF_SAMPLE_RAW = linux.PERF_SAMPLE_RAW
|
||||
PERF_FLAG_FD_CLOEXEC = linux.PERF_FLAG_FD_CLOEXEC
|
||||
RLIM_INFINITY = linux.RLIM_INFINITY
|
||||
RLIMIT_MEMLOCK = linux.RLIMIT_MEMLOCK
|
||||
BPF_STATS_RUN_TIME = linux.BPF_STATS_RUN_TIME
|
||||
PERF_RECORD_LOST = linux.PERF_RECORD_LOST
|
||||
PERF_RECORD_SAMPLE = linux.PERF_RECORD_SAMPLE
|
||||
AT_FDCWD = linux.AT_FDCWD
|
||||
RENAME_NOREPLACE = linux.RENAME_NOREPLACE
|
||||
SO_ATTACH_BPF = linux.SO_ATTACH_BPF
|
||||
SO_DETACH_BPF = linux.SO_DETACH_BPF
|
||||
SOL_SOCKET = linux.SOL_SOCKET
|
||||
SIGPROF = linux.SIGPROF
|
||||
SIG_BLOCK = linux.SIG_BLOCK
|
||||
SIG_UNBLOCK = linux.SIG_UNBLOCK
|
||||
)
|
||||
|
||||
// Statfs_t is a wrapper
|
||||
type Statfs_t = linux.Statfs_t
|
||||
|
||||
type Stat_t = linux.Stat_t
|
||||
|
||||
// Rlimit is a wrapper
|
||||
type Rlimit = linux.Rlimit
|
||||
type Signal = linux.Signal
|
||||
type Sigset_t = linux.Sigset_t
|
||||
type PerfEventMmapPage = linux.PerfEventMmapPage
|
||||
type EpollEvent = linux.EpollEvent
|
||||
type PerfEventAttr = linux.PerfEventAttr
|
||||
type Utsname = linux.Utsname
|
||||
|
||||
// Syscall is a wrapper
|
||||
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
|
||||
return linux.Syscall(trap, a1, a2, a3)
|
||||
}
|
||||
|
||||
// FcntlInt is a wrapper
|
||||
func PthreadSigmask(how int, set, oldset *Sigset_t) error {
|
||||
return linux.PthreadSigmask(how, set, oldset)
|
||||
}
|
||||
|
||||
func FcntlInt(fd uintptr, cmd, arg int) (int, error) {
|
||||
return linux.FcntlInt(fd, cmd, arg)
|
||||
}
|
||||
|
||||
// IoctlSetInt is a wrapper
|
||||
func IoctlSetInt(fd int, req uint, value int) error {
|
||||
return linux.IoctlSetInt(fd, req, value)
|
||||
}
|
||||
|
||||
// Statfs is a wrapper
|
||||
func Statfs(path string, buf *Statfs_t) (err error) {
|
||||
return linux.Statfs(path, buf)
|
||||
}
|
||||
|
||||
// Close is a wrapper
|
||||
func Close(fd int) (err error) {
|
||||
return linux.Close(fd)
|
||||
}
|
||||
|
||||
// EpollEvent is a wrapper
|
||||
type EpollEvent = linux.EpollEvent
|
||||
|
||||
// EpollWait is a wrapper
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
return linux.EpollWait(epfd, events, msec)
|
||||
}
|
||||
|
||||
// EpollCtl is a wrapper
|
||||
func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) {
|
||||
return linux.EpollCtl(epfd, op, fd, event)
|
||||
}
|
||||
|
||||
// Eventfd is a wrapper
|
||||
func Eventfd(initval uint, flags int) (fd int, err error) {
|
||||
return linux.Eventfd(initval, flags)
|
||||
}
|
||||
|
||||
// Write is a wrapper
|
||||
func Write(fd int, p []byte) (n int, err error) {
|
||||
return linux.Write(fd, p)
|
||||
}
|
||||
|
||||
// EpollCreate1 is a wrapper
|
||||
func EpollCreate1(flag int) (fd int, err error) {
|
||||
return linux.EpollCreate1(flag)
|
||||
}
|
||||
|
||||
// PerfEventMmapPage is a wrapper
|
||||
type PerfEventMmapPage linux.PerfEventMmapPage
|
||||
|
||||
// SetNonblock is a wrapper
|
||||
func SetNonblock(fd int, nonblocking bool) (err error) {
|
||||
return linux.SetNonblock(fd, nonblocking)
|
||||
}
|
||||
|
||||
// Mmap is a wrapper
|
||||
func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) {
|
||||
return linux.Mmap(fd, offset, length, prot, flags)
|
||||
}
|
||||
|
||||
// Munmap is a wrapper
|
||||
func Munmap(b []byte) (err error) {
|
||||
return linux.Munmap(b)
|
||||
}
|
||||
|
||||
// PerfEventAttr is a wrapper
|
||||
type PerfEventAttr = linux.PerfEventAttr
|
||||
|
||||
// PerfEventOpen is a wrapper
|
||||
func PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) {
|
||||
return linux.PerfEventOpen(attr, pid, cpu, groupFd, flags)
|
||||
}
|
||||
|
||||
// Utsname is a wrapper
|
||||
type Utsname = linux.Utsname
|
||||
|
||||
// Uname is a wrapper
|
||||
func Uname(buf *Utsname) (err error) {
|
||||
return linux.Uname(buf)
|
||||
}
|
||||
|
||||
// Getpid is a wrapper
|
||||
func Getpid() int {
|
||||
return linux.Getpid()
|
||||
}
|
||||
|
||||
// Gettid is a wrapper
|
||||
func Gettid() int {
|
||||
return linux.Gettid()
|
||||
}
|
||||
|
||||
// Tgkill is a wrapper
|
||||
func Tgkill(tgid int, tid int, sig syscall.Signal) (err error) {
|
||||
return linux.Tgkill(tgid, tid, sig)
|
||||
}
|
||||
|
||||
// BytePtrFromString is a wrapper
|
||||
func BytePtrFromString(s string) (*byte, error) {
|
||||
return linux.BytePtrFromString(s)
|
||||
}
|
||||
|
||||
// ByteSliceToString is a wrapper
|
||||
func ByteSliceToString(s []byte) string {
|
||||
return linux.ByteSliceToString(s)
|
||||
}
|
||||
|
||||
// Renameat2 is a wrapper
|
||||
func Renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags uint) error {
|
||||
return linux.Renameat2(olddirfd, oldpath, newdirfd, newpath, flags)
|
||||
}
|
||||
|
||||
+79
-90
@@ -1,5 +1,4 @@
|
||||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
package unix
|
||||
|
||||
@@ -11,70 +10,76 @@ import (
|
||||
|
||||
var errNonLinux = fmt.Errorf("unsupported platform %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
|
||||
// Errnos are distinct and non-zero.
|
||||
const (
|
||||
ENOENT = syscall.ENOENT
|
||||
EEXIST = syscall.EEXIST
|
||||
EAGAIN = syscall.EAGAIN
|
||||
ENOSPC = syscall.ENOSPC
|
||||
EINVAL = syscall.EINVAL
|
||||
EINTR = syscall.EINTR
|
||||
EPERM = syscall.EPERM
|
||||
ESRCH = syscall.ESRCH
|
||||
ENODEV = syscall.ENODEV
|
||||
EBADF = syscall.Errno(0)
|
||||
E2BIG = syscall.Errno(0)
|
||||
EFAULT = syscall.EFAULT
|
||||
EACCES = syscall.Errno(0)
|
||||
// ENOTSUPP is not the same as ENOTSUP or EOPNOTSUP
|
||||
ENOTSUPP = syscall.Errno(0x20c)
|
||||
|
||||
BPF_F_NO_PREALLOC = 0
|
||||
BPF_F_NUMA_NODE = 0
|
||||
BPF_F_RDONLY = 0
|
||||
BPF_F_WRONLY = 0
|
||||
BPF_F_RDONLY_PROG = 0
|
||||
BPF_F_WRONLY_PROG = 0
|
||||
BPF_F_SLEEPABLE = 0
|
||||
BPF_F_MMAPABLE = 0
|
||||
BPF_F_INNER_MAP = 0
|
||||
BPF_OBJ_NAME_LEN = 0x10
|
||||
BPF_TAG_SIZE = 0x8
|
||||
BPF_RINGBUF_BUSY_BIT = 0
|
||||
BPF_RINGBUF_DISCARD_BIT = 0
|
||||
BPF_RINGBUF_HDR_SZ = 0
|
||||
SYS_BPF = 321
|
||||
F_DUPFD_CLOEXEC = 0x406
|
||||
EPOLLIN = 0x1
|
||||
EPOLL_CTL_ADD = 0x1
|
||||
EPOLL_CLOEXEC = 0x80000
|
||||
O_CLOEXEC = 0x80000
|
||||
O_NONBLOCK = 0x800
|
||||
PROT_READ = 0x1
|
||||
PROT_WRITE = 0x2
|
||||
MAP_SHARED = 0x1
|
||||
PERF_ATTR_SIZE_VER1 = 0
|
||||
PERF_TYPE_SOFTWARE = 0x1
|
||||
PERF_TYPE_TRACEPOINT = 0
|
||||
PERF_COUNT_SW_BPF_OUTPUT = 0xa
|
||||
PERF_EVENT_IOC_DISABLE = 0
|
||||
PERF_EVENT_IOC_ENABLE = 0
|
||||
PERF_EVENT_IOC_SET_BPF = 0
|
||||
PerfBitWatermark = 0x4000
|
||||
PERF_SAMPLE_RAW = 0x400
|
||||
PERF_FLAG_FD_CLOEXEC = 0x8
|
||||
RLIM_INFINITY = 0x7fffffffffffffff
|
||||
RLIMIT_MEMLOCK = 8
|
||||
BPF_STATS_RUN_TIME = 0
|
||||
PERF_RECORD_LOST = 2
|
||||
PERF_RECORD_SAMPLE = 9
|
||||
AT_FDCWD = -0x2
|
||||
RENAME_NOREPLACE = 0x1
|
||||
SO_ATTACH_BPF = 0x32
|
||||
SO_DETACH_BPF = 0x1b
|
||||
SOL_SOCKET = 0x1
|
||||
ENOENT syscall.Errno = iota + 1
|
||||
EEXIST
|
||||
EAGAIN
|
||||
ENOSPC
|
||||
EINVAL
|
||||
EINTR
|
||||
EPERM
|
||||
ESRCH
|
||||
ENODEV
|
||||
EBADF
|
||||
E2BIG
|
||||
EFAULT
|
||||
EACCES
|
||||
EILSEQ
|
||||
)
|
||||
|
||||
// Constants are distinct to avoid breaking switch statements.
|
||||
const (
|
||||
BPF_F_NO_PREALLOC = iota
|
||||
BPF_F_NUMA_NODE
|
||||
BPF_F_RDONLY
|
||||
BPF_F_WRONLY
|
||||
BPF_F_RDONLY_PROG
|
||||
BPF_F_WRONLY_PROG
|
||||
BPF_F_SLEEPABLE
|
||||
BPF_F_MMAPABLE
|
||||
BPF_F_INNER_MAP
|
||||
BPF_F_KPROBE_MULTI_RETURN
|
||||
BPF_OBJ_NAME_LEN
|
||||
BPF_TAG_SIZE
|
||||
BPF_RINGBUF_BUSY_BIT
|
||||
BPF_RINGBUF_DISCARD_BIT
|
||||
BPF_RINGBUF_HDR_SZ
|
||||
SYS_BPF
|
||||
F_DUPFD_CLOEXEC
|
||||
EPOLLIN
|
||||
EPOLL_CTL_ADD
|
||||
EPOLL_CLOEXEC
|
||||
O_CLOEXEC
|
||||
O_NONBLOCK
|
||||
PROT_READ
|
||||
PROT_WRITE
|
||||
MAP_SHARED
|
||||
PERF_ATTR_SIZE_VER1
|
||||
PERF_TYPE_SOFTWARE
|
||||
PERF_TYPE_TRACEPOINT
|
||||
PERF_COUNT_SW_BPF_OUTPUT
|
||||
PERF_EVENT_IOC_DISABLE
|
||||
PERF_EVENT_IOC_ENABLE
|
||||
PERF_EVENT_IOC_SET_BPF
|
||||
PerfBitWatermark
|
||||
PERF_SAMPLE_RAW
|
||||
PERF_FLAG_FD_CLOEXEC
|
||||
RLIM_INFINITY
|
||||
RLIMIT_MEMLOCK
|
||||
BPF_STATS_RUN_TIME
|
||||
PERF_RECORD_LOST
|
||||
PERF_RECORD_SAMPLE
|
||||
AT_FDCWD
|
||||
RENAME_NOREPLACE
|
||||
SO_ATTACH_BPF
|
||||
SO_DETACH_BPF
|
||||
SOL_SOCKET
|
||||
SIGPROF
|
||||
SIG_BLOCK
|
||||
SIG_UNBLOCK
|
||||
)
|
||||
|
||||
// Statfs_t is a wrapper
|
||||
type Statfs_t struct {
|
||||
Type int64
|
||||
Bsize int64
|
||||
@@ -92,70 +97,67 @@ type Statfs_t struct {
|
||||
|
||||
type Stat_t struct{}
|
||||
|
||||
// Rlimit is a wrapper
|
||||
type Rlimit struct {
|
||||
Cur uint64
|
||||
Max uint64
|
||||
}
|
||||
|
||||
// Syscall is a wrapper
|
||||
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
|
||||
return 0, 0, syscall.Errno(1)
|
||||
type Signal int
|
||||
|
||||
type Sigset_t struct {
|
||||
Val [4]uint64
|
||||
}
|
||||
|
||||
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
|
||||
return 0, 0, syscall.ENOTSUP
|
||||
}
|
||||
|
||||
func PthreadSigmask(how int, set, oldset *Sigset_t) error {
|
||||
return errNonLinux
|
||||
}
|
||||
|
||||
// FcntlInt is a wrapper
|
||||
func FcntlInt(fd uintptr, cmd, arg int) (int, error) {
|
||||
return -1, errNonLinux
|
||||
}
|
||||
|
||||
// IoctlSetInt is a wrapper
|
||||
func IoctlSetInt(fd int, req uint, value int) error {
|
||||
return errNonLinux
|
||||
}
|
||||
|
||||
// Statfs is a wrapper
|
||||
func Statfs(path string, buf *Statfs_t) error {
|
||||
return errNonLinux
|
||||
}
|
||||
|
||||
// Close is a wrapper
|
||||
func Close(fd int) (err error) {
|
||||
return errNonLinux
|
||||
}
|
||||
|
||||
// EpollEvent is a wrapper
|
||||
type EpollEvent struct {
|
||||
Events uint32
|
||||
Fd int32
|
||||
Pad int32
|
||||
}
|
||||
|
||||
// EpollWait is a wrapper
|
||||
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||
return 0, errNonLinux
|
||||
}
|
||||
|
||||
// EpollCtl is a wrapper
|
||||
func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) {
|
||||
return errNonLinux
|
||||
}
|
||||
|
||||
// Eventfd is a wrapper
|
||||
func Eventfd(initval uint, flags int) (fd int, err error) {
|
||||
return 0, errNonLinux
|
||||
}
|
||||
|
||||
// Write is a wrapper
|
||||
func Write(fd int, p []byte) (n int, err error) {
|
||||
return 0, errNonLinux
|
||||
}
|
||||
|
||||
// EpollCreate1 is a wrapper
|
||||
func EpollCreate1(flag int) (fd int, err error) {
|
||||
return 0, errNonLinux
|
||||
}
|
||||
|
||||
// PerfEventMmapPage is a wrapper
|
||||
type PerfEventMmapPage struct {
|
||||
Version uint32
|
||||
Compat_version uint32
|
||||
@@ -182,22 +184,18 @@ type PerfEventMmapPage struct {
|
||||
Aux_size uint64
|
||||
}
|
||||
|
||||
// SetNonblock is a wrapper
|
||||
func SetNonblock(fd int, nonblocking bool) (err error) {
|
||||
return errNonLinux
|
||||
}
|
||||
|
||||
// Mmap is a wrapper
|
||||
func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) {
|
||||
return []byte{}, errNonLinux
|
||||
}
|
||||
|
||||
// Munmap is a wrapper
|
||||
func Munmap(b []byte) (err error) {
|
||||
return errNonLinux
|
||||
}
|
||||
|
||||
// PerfEventAttr is a wrapper
|
||||
type PerfEventAttr struct {
|
||||
Type uint32
|
||||
Size uint32
|
||||
@@ -219,48 +217,39 @@ type PerfEventAttr struct {
|
||||
Sample_max_stack uint16
|
||||
}
|
||||
|
||||
// PerfEventOpen is a wrapper
|
||||
func PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) {
|
||||
return 0, errNonLinux
|
||||
}
|
||||
|
||||
// Utsname is a wrapper
|
||||
type Utsname struct {
|
||||
Release [65]byte
|
||||
Version [65]byte
|
||||
}
|
||||
|
||||
// Uname is a wrapper
|
||||
func Uname(buf *Utsname) (err error) {
|
||||
return errNonLinux
|
||||
}
|
||||
|
||||
// Getpid is a wrapper
|
||||
func Getpid() int {
|
||||
return -1
|
||||
}
|
||||
|
||||
// Gettid is a wrapper
|
||||
func Gettid() int {
|
||||
return -1
|
||||
}
|
||||
|
||||
// Tgkill is a wrapper
|
||||
func Tgkill(tgid int, tid int, sig syscall.Signal) (err error) {
|
||||
return errNonLinux
|
||||
}
|
||||
|
||||
// BytePtrFromString is a wrapper
|
||||
func BytePtrFromString(s string) (*byte, error) {
|
||||
return nil, errNonLinux
|
||||
}
|
||||
|
||||
// ByteSliceToString is a wrapper
|
||||
func ByteSliceToString(s []byte) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Renameat2 is a wrapper
|
||||
func Renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags uint) error {
|
||||
return errNonLinux
|
||||
}
|
||||
|
||||
+1
-1
@@ -138,7 +138,7 @@ func (cg *progAttachCgroup) Pin(string) error {
|
||||
}
|
||||
|
||||
func (cg *progAttachCgroup) Unpin() error {
|
||||
return fmt.Errorf("can't pin cgroup: %w", ErrNotSupported)
|
||||
return fmt.Errorf("can't unpin cgroup: %w", ErrNotSupported)
|
||||
}
|
||||
|
||||
func (cg *progAttachCgroup) Info() (*Info, error) {
|
||||
|
||||
+39
-71
@@ -1,7 +1,6 @@
|
||||
package link
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -9,7 +8,6 @@ import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
@@ -20,12 +18,6 @@ import (
|
||||
|
||||
var (
|
||||
kprobeEventsPath = filepath.Join(tracefsPath, "kprobe_events")
|
||||
|
||||
kprobeRetprobeBit = struct {
|
||||
once sync.Once
|
||||
value uint64
|
||||
err error
|
||||
}{}
|
||||
)
|
||||
|
||||
type probeType uint8
|
||||
@@ -83,13 +75,6 @@ func (pt probeType) PerfEventType(ret bool) perfEventType {
|
||||
return uprobeEvent
|
||||
}
|
||||
|
||||
func (pt probeType) RetprobeBit() (uint64, error) {
|
||||
if pt == kprobeType {
|
||||
return kretprobeBit()
|
||||
}
|
||||
return uretprobeBit()
|
||||
}
|
||||
|
||||
// Kprobe attaches the given eBPF program to a perf event that fires when the
|
||||
// given kernel symbol starts executing. See /proc/kallsyms for available
|
||||
// symbols. For example, printk():
|
||||
@@ -123,6 +108,9 @@ func Kprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions) (Link, error
|
||||
// Losing the reference to the resulting Link (kp) will close the Kretprobe
|
||||
// and prevent further execution of prog. The Link must be Closed during
|
||||
// program shutdown to avoid leaking system resources.
|
||||
//
|
||||
// On kernels 5.10 and earlier, setting a kretprobe on a nonexistent symbol
|
||||
// incorrectly returns unix.EINVAL instead of os.ErrNotExist.
|
||||
func Kretprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions) (Link, error) {
|
||||
k, err := kprobe(symbol, prog, opts, true)
|
||||
if err != nil {
|
||||
@@ -194,7 +182,7 @@ func kprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions, ret bool) (*
|
||||
|
||||
// Use kprobe PMU if the kernel has it available.
|
||||
tp, err := pmuKprobe(args)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL) {
|
||||
args.symbol = platformPrefix(symbol)
|
||||
tp, err = pmuKprobe(args)
|
||||
}
|
||||
@@ -208,7 +196,7 @@ func kprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions, ret bool) (*
|
||||
// Use tracefs if kprobe PMU is missing.
|
||||
args.symbol = symbol
|
||||
tp, err = tracefsKprobe(args)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL) {
|
||||
args.symbol = platformPrefix(symbol)
|
||||
tp, err = tracefsKprobe(args)
|
||||
}
|
||||
@@ -235,14 +223,17 @@ func pmuKprobe(args probeArgs) (*perfEvent, error) {
|
||||
func pmuProbe(typ probeType, args probeArgs) (*perfEvent, error) {
|
||||
// Getting the PMU type will fail if the kernel doesn't support
|
||||
// the perf_[k,u]probe PMU.
|
||||
et, err := getPMUEventType(typ)
|
||||
et, err := readUint64FromFileOnce("%d\n", "/sys/bus/event_source/devices", typ.String(), "type")
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return nil, fmt.Errorf("%s: %w", typ, ErrNotSupported)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var config uint64
|
||||
if args.ret {
|
||||
bit, err := typ.RetprobeBit()
|
||||
bit, err := readUint64FromFileOnce("config:%d\n", "/sys/bus/event_source/devices", typ.String(), "/format/retprobe")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -250,8 +241,9 @@ func pmuProbe(typ probeType, args probeArgs) (*perfEvent, error) {
|
||||
}
|
||||
|
||||
var (
|
||||
attr unix.PerfEventAttr
|
||||
sp unsafe.Pointer
|
||||
attr unix.PerfEventAttr
|
||||
sp unsafe.Pointer
|
||||
token string
|
||||
)
|
||||
switch typ {
|
||||
case kprobeType:
|
||||
@@ -261,6 +253,8 @@ func pmuProbe(typ probeType, args probeArgs) (*perfEvent, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
token = kprobeToken(args)
|
||||
|
||||
attr = unix.PerfEventAttr{
|
||||
// The minimum size required for PMU kprobes is PERF_ATTR_SIZE_VER1,
|
||||
// since it added the config2 (Ext2) field. Use Ext2 as probe_offset.
|
||||
@@ -280,6 +274,8 @@ func pmuProbe(typ probeType, args probeArgs) (*perfEvent, error) {
|
||||
config |= args.refCtrOffset << uprobeRefCtrOffsetShift
|
||||
}
|
||||
|
||||
token = uprobeToken(args)
|
||||
|
||||
attr = unix.PerfEventAttr{
|
||||
// The minimum size required for PMU uprobes is PERF_ATTR_SIZE_VER1,
|
||||
// since it added the config2 (Ext2) field. The Size field controls the
|
||||
@@ -299,26 +295,27 @@ func pmuProbe(typ probeType, args probeArgs) (*perfEvent, error) {
|
||||
// return -EINVAL. Return ErrNotSupported to allow falling back to tracefs.
|
||||
// https://github.com/torvalds/linux/blob/94710cac0ef4/kernel/trace/trace_kprobe.c#L340-L343
|
||||
if errors.Is(err, unix.EINVAL) && strings.Contains(args.symbol, ".") {
|
||||
return nil, fmt.Errorf("symbol '%s+%#x': older kernels don't accept dots: %w", args.symbol, args.offset, ErrNotSupported)
|
||||
return nil, fmt.Errorf("token %s: older kernels don't accept dots: %w", token, ErrNotSupported)
|
||||
}
|
||||
// Since commit 97c753e62e6c, ENOENT is correctly returned instead of EINVAL
|
||||
// when trying to create a kretprobe for a missing symbol. Make sure ENOENT
|
||||
// is returned to the caller.
|
||||
if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL) {
|
||||
return nil, fmt.Errorf("symbol '%s+%#x' not found: %w", args.symbol, args.offset, os.ErrNotExist)
|
||||
// when trying to create a retprobe for a missing symbol.
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return nil, fmt.Errorf("token %s: not found: %w", token, err)
|
||||
}
|
||||
// Since commit ab105a4fb894, -EILSEQ is returned when a kprobe sym+offset is resolved
|
||||
// to an invalid insn boundary.
|
||||
if errors.Is(err, syscall.EILSEQ) {
|
||||
return nil, fmt.Errorf("symbol '%s+%#x' not found (bad insn boundary): %w", args.symbol, args.offset, os.ErrNotExist)
|
||||
// Since commit ab105a4fb894, EILSEQ is returned when a kprobe sym+offset is resolved
|
||||
// to an invalid insn boundary. The exact conditions that trigger this error are
|
||||
// arch specific however.
|
||||
if errors.Is(err, unix.EILSEQ) {
|
||||
return nil, fmt.Errorf("token %s: bad insn boundary: %w", token, os.ErrNotExist)
|
||||
}
|
||||
// Since at least commit cb9a19fe4aa51, ENOTSUPP is returned
|
||||
// when attempting to set a uprobe on a trap instruction.
|
||||
if errors.Is(err, unix.ENOTSUPP) {
|
||||
return nil, fmt.Errorf("failed setting uprobe on offset %#x (possible trap insn): %w", args.offset, err)
|
||||
if errors.Is(err, sys.ENOTSUPP) {
|
||||
return nil, fmt.Errorf("token %s: failed setting uprobe on offset %#x (possible trap insn): %w", token, args.offset, err)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("opening perf event: %w", err)
|
||||
return nil, fmt.Errorf("token %s: opening perf event: %w", token, err)
|
||||
}
|
||||
|
||||
// Ensure the string pointer is not collected before PerfEventOpen returns.
|
||||
@@ -454,16 +451,15 @@ func createTraceFSProbeEvent(typ probeType, args probeArgs) error {
|
||||
pe = fmt.Sprintf("%s:%s/%s %s", probePrefix(args.ret), args.group, args.symbol, token)
|
||||
}
|
||||
_, err = f.WriteString(pe)
|
||||
|
||||
// Since commit 97c753e62e6c, ENOENT is correctly returned instead of EINVAL
|
||||
// when trying to create a kretprobe for a missing symbol. Make sure ENOENT
|
||||
// is returned to the caller.
|
||||
// EINVAL is also returned on pre-5.2 kernels when the `SYM[+offs]` token
|
||||
// is resolved to an invalid insn boundary.
|
||||
if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL) {
|
||||
return fmt.Errorf("token %s: %w", token, os.ErrNotExist)
|
||||
// when trying to create a retprobe for a missing symbol.
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return fmt.Errorf("token %s: not found: %w", token, err)
|
||||
}
|
||||
// Since commit ab105a4fb894, -EILSEQ is returned when a kprobe sym+offset is resolved
|
||||
// to an invalid insn boundary.
|
||||
// Since commit ab105a4fb894, EILSEQ is returned when a kprobe sym+offset is resolved
|
||||
// to an invalid insn boundary. The exact conditions that trigger this error are
|
||||
// arch specific however.
|
||||
if errors.Is(err, syscall.EILSEQ) {
|
||||
return fmt.Errorf("token %s: bad insn boundary: %w", token, os.ErrNotExist)
|
||||
}
|
||||
@@ -472,8 +468,9 @@ func createTraceFSProbeEvent(typ probeType, args probeArgs) error {
|
||||
if errors.Is(err, syscall.ERANGE) {
|
||||
return fmt.Errorf("token %s: offset too big: %w", token, os.ErrNotExist)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("writing '%s' to '%s': %w", pe, typ.EventsPath(), err)
|
||||
return fmt.Errorf("token %s: writing '%s': %w", token, pe, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -527,35 +524,6 @@ func probePrefix(ret bool) string {
|
||||
return "p"
|
||||
}
|
||||
|
||||
// determineRetprobeBit reads a Performance Monitoring Unit's retprobe bit
|
||||
// from /sys/bus/event_source/devices/<pmu>/format/retprobe.
|
||||
func determineRetprobeBit(typ probeType) (uint64, error) {
|
||||
p := filepath.Join("/sys/bus/event_source/devices/", typ.String(), "/format/retprobe")
|
||||
|
||||
data, err := os.ReadFile(p)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var rp uint64
|
||||
n, err := fmt.Sscanf(string(bytes.TrimSpace(data)), "config:%d", &rp)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("parse retprobe bit: %w", err)
|
||||
}
|
||||
if n != 1 {
|
||||
return 0, fmt.Errorf("parse retprobe bit: expected 1 item, got %d", n)
|
||||
}
|
||||
|
||||
return rp, nil
|
||||
}
|
||||
|
||||
func kretprobeBit() (uint64, error) {
|
||||
kprobeRetprobeBit.once.Do(func() {
|
||||
kprobeRetprobeBit.value, kprobeRetprobeBit.err = determineRetprobeBit(kprobeType)
|
||||
})
|
||||
return kprobeRetprobeBit.value, kprobeRetprobeBit.err
|
||||
}
|
||||
|
||||
// kprobeToken creates the SYM[+offs] token for the tracefs api.
|
||||
func kprobeToken(args probeArgs) string {
|
||||
po := args.symbol
|
||||
|
||||
+176
@@ -0,0 +1,176 @@
|
||||
package link
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"unsafe"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
"github.com/cilium/ebpf/asm"
|
||||
"github.com/cilium/ebpf/internal"
|
||||
"github.com/cilium/ebpf/internal/sys"
|
||||
"github.com/cilium/ebpf/internal/unix"
|
||||
)
|
||||
|
||||
// KprobeMultiOptions defines additional parameters that will be used
|
||||
// when opening a KprobeMulti Link.
|
||||
type KprobeMultiOptions struct {
|
||||
// Symbols takes a list of kernel symbol names to attach an ebpf program to.
|
||||
//
|
||||
// Mutually exclusive with Addresses.
|
||||
Symbols []string
|
||||
|
||||
// Addresses takes a list of kernel symbol addresses in case they can not
|
||||
// be referred to by name.
|
||||
//
|
||||
// Note that only start addresses can be specified, since the fprobe API
|
||||
// limits the attach point to the function entry or return.
|
||||
//
|
||||
// Mutually exclusive with Symbols.
|
||||
Addresses []uint64
|
||||
|
||||
// Cookies specifies arbitrary values that can be fetched from an eBPF
|
||||
// program via `bpf_get_attach_cookie()`.
|
||||
//
|
||||
// If set, its length should be equal to the length of Symbols or Addresses.
|
||||
// Each Cookie is assigned to the Symbol or Address specified at the
|
||||
// corresponding slice index.
|
||||
Cookies []uint64
|
||||
}
|
||||
|
||||
// KprobeMulti attaches the given eBPF program to the entry point of a given set
|
||||
// of kernel symbols.
|
||||
//
|
||||
// The difference with Kprobe() is that multi-kprobe accomplishes this in a
|
||||
// single system call, making it significantly faster than attaching many
|
||||
// probes one at a time.
|
||||
//
|
||||
// Requires at least Linux 5.18.
|
||||
func KprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions) (Link, error) {
|
||||
return kprobeMulti(prog, opts, 0)
|
||||
}
|
||||
|
||||
// KretprobeMulti attaches the given eBPF program to the return point of a given
|
||||
// set of kernel symbols.
|
||||
//
|
||||
// The difference with Kretprobe() is that multi-kprobe accomplishes this in a
|
||||
// single system call, making it significantly faster than attaching many
|
||||
// probes one at a time.
|
||||
//
|
||||
// Requires at least Linux 5.18.
|
||||
func KretprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions) (Link, error) {
|
||||
return kprobeMulti(prog, opts, unix.BPF_F_KPROBE_MULTI_RETURN)
|
||||
}
|
||||
|
||||
func kprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions, flags uint32) (Link, error) {
|
||||
if prog == nil {
|
||||
return nil, errors.New("cannot attach a nil program")
|
||||
}
|
||||
|
||||
syms := uint32(len(opts.Symbols))
|
||||
addrs := uint32(len(opts.Addresses))
|
||||
cookies := uint32(len(opts.Cookies))
|
||||
|
||||
if syms == 0 && addrs == 0 {
|
||||
return nil, fmt.Errorf("one of Symbols or Addresses is required: %w", errInvalidInput)
|
||||
}
|
||||
if syms != 0 && addrs != 0 {
|
||||
return nil, fmt.Errorf("Symbols and Addresses are mutually exclusive: %w", errInvalidInput)
|
||||
}
|
||||
if cookies > 0 && cookies != syms && cookies != addrs {
|
||||
return nil, fmt.Errorf("Cookies must be exactly Symbols or Addresses in length: %w", errInvalidInput)
|
||||
}
|
||||
|
||||
if err := haveBPFLinkKprobeMulti(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attr := &sys.LinkCreateKprobeMultiAttr{
|
||||
ProgFd: uint32(prog.FD()),
|
||||
AttachType: sys.BPF_TRACE_KPROBE_MULTI,
|
||||
KprobeMultiFlags: flags,
|
||||
}
|
||||
|
||||
switch {
|
||||
case syms != 0:
|
||||
attr.Count = syms
|
||||
attr.Syms = sys.NewStringSlicePointer(opts.Symbols)
|
||||
|
||||
case addrs != 0:
|
||||
attr.Count = addrs
|
||||
attr.Addrs = sys.NewPointer(unsafe.Pointer(&opts.Addresses[0]))
|
||||
}
|
||||
|
||||
if cookies != 0 {
|
||||
attr.Cookies = sys.NewPointer(unsafe.Pointer(&opts.Cookies[0]))
|
||||
}
|
||||
|
||||
fd, err := sys.LinkCreateKprobeMulti(attr)
|
||||
if errors.Is(err, unix.ESRCH) {
|
||||
return nil, fmt.Errorf("couldn't find one or more symbols: %w", os.ErrNotExist)
|
||||
}
|
||||
if errors.Is(err, unix.EINVAL) {
|
||||
return nil, fmt.Errorf("%w (missing kernel symbol or prog's AttachType not AttachTraceKprobeMulti?)", err)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &kprobeMultiLink{RawLink{fd, ""}}, nil
|
||||
}
|
||||
|
||||
type kprobeMultiLink struct {
|
||||
RawLink
|
||||
}
|
||||
|
||||
var _ Link = (*kprobeMultiLink)(nil)
|
||||
|
||||
func (kml *kprobeMultiLink) Update(prog *ebpf.Program) error {
|
||||
return fmt.Errorf("update kprobe_multi: %w", ErrNotSupported)
|
||||
}
|
||||
|
||||
func (kml *kprobeMultiLink) Pin(string) error {
|
||||
return fmt.Errorf("pin kprobe_multi: %w", ErrNotSupported)
|
||||
}
|
||||
|
||||
func (kml *kprobeMultiLink) Unpin() error {
|
||||
return fmt.Errorf("unpin kprobe_multi: %w", ErrNotSupported)
|
||||
}
|
||||
|
||||
var haveBPFLinkKprobeMulti = internal.FeatureTest("bpf_link_kprobe_multi", "5.18", func() error {
|
||||
prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
|
||||
Name: "probe_kpm_link",
|
||||
Type: ebpf.Kprobe,
|
||||
Instructions: asm.Instructions{
|
||||
asm.Mov.Imm(asm.R0, 0),
|
||||
asm.Return(),
|
||||
},
|
||||
AttachType: ebpf.AttachTraceKprobeMulti,
|
||||
License: "MIT",
|
||||
})
|
||||
if errors.Is(err, unix.E2BIG) {
|
||||
// Kernel doesn't support AttachType field.
|
||||
return internal.ErrNotSupported
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer prog.Close()
|
||||
|
||||
fd, err := sys.LinkCreateKprobeMulti(&sys.LinkCreateKprobeMultiAttr{
|
||||
ProgFd: uint32(prog.FD()),
|
||||
AttachType: sys.BPF_TRACE_KPROBE_MULTI,
|
||||
Count: 1,
|
||||
Syms: sys.NewStringSlicePointer([]string{"vprintk"}),
|
||||
})
|
||||
if errors.Is(err, unix.EINVAL) {
|
||||
return internal.ErrNotSupported
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fd.Close()
|
||||
|
||||
return nil
|
||||
})
|
||||
+6
-9
@@ -177,7 +177,7 @@ func AttachRawLink(opts RawLinkOptions) (*RawLink, error) {
|
||||
}
|
||||
fd, err := sys.LinkCreate(&attr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't create link: %s", err)
|
||||
return nil, fmt.Errorf("create link: %w", err)
|
||||
}
|
||||
|
||||
return &RawLink{fd, ""}, nil
|
||||
@@ -280,27 +280,24 @@ func (l *RawLink) Info() (*Info, error) {
|
||||
switch info.Type {
|
||||
case CgroupType:
|
||||
extra = &CgroupInfo{}
|
||||
case IterType:
|
||||
// not supported
|
||||
case NetNsType:
|
||||
extra = &NetNsInfo{}
|
||||
case RawTracepointType:
|
||||
// not supported
|
||||
case TracingType:
|
||||
extra = &TracingInfo{}
|
||||
case XDPType:
|
||||
extra = &XDPInfo{}
|
||||
case PerfEventType:
|
||||
// no extra
|
||||
case RawTracepointType, IterType,
|
||||
PerfEventType, KprobeMultiType:
|
||||
// Extra metadata not supported.
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown link info type: %d", info.Type)
|
||||
}
|
||||
|
||||
if info.Type != RawTracepointType && info.Type != IterType && info.Type != PerfEventType {
|
||||
if extra != nil {
|
||||
buf := bytes.NewReader(info.Extra[:])
|
||||
err := binary.Read(buf, internal.NativeEndian, extra)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can not read extra link info: %w", err)
|
||||
return nil, fmt.Errorf("cannot read extra link info: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+67
-27
@@ -7,8 +7,8 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
@@ -270,7 +270,11 @@ func unsafeStringPtr(str string) (unsafe.Pointer, error) {
|
||||
// can pass a raw symbol name, e.g. a kernel symbol containing dots.
|
||||
func getTraceEventID(group, name string) (uint64, error) {
|
||||
name = sanitizeSymbol(name)
|
||||
tid, err := uint64FromFile(tracefsPath, "events", group, name, "id")
|
||||
path, err := sanitizePath(tracefsPath, "events", group, name, "id")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
tid, err := readUint64FromFile("%d\n", path)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return 0, fmt.Errorf("trace event %s/%s: %w", group, name, os.ErrNotExist)
|
||||
}
|
||||
@@ -281,22 +285,6 @@ func getTraceEventID(group, name string) (uint64, error) {
|
||||
return tid, nil
|
||||
}
|
||||
|
||||
// getPMUEventType reads a Performance Monitoring Unit's type (numeric identifier)
|
||||
// from /sys/bus/event_source/devices/<pmu>/type.
|
||||
//
|
||||
// Returns ErrNotSupported if the pmu type is not supported.
|
||||
func getPMUEventType(typ probeType) (uint64, error) {
|
||||
et, err := uint64FromFile("/sys/bus/event_source/devices", typ.String(), "type")
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return 0, fmt.Errorf("pmu type %s: %w", typ, ErrNotSupported)
|
||||
}
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("reading pmu type %s: %w", typ, err)
|
||||
}
|
||||
|
||||
return et, nil
|
||||
}
|
||||
|
||||
// openTracepointPerfEvent opens a tracepoint-type perf event. System-wide
|
||||
// [k,u]probes created by writing to <tracefs>/[k,u]probe_events are tracepoints
|
||||
// behind the scenes, and can be attached to using these perf events.
|
||||
@@ -317,23 +305,75 @@ func openTracepointPerfEvent(tid uint64, pid int) (*sys.FD, error) {
|
||||
return sys.NewFD(fd)
|
||||
}
|
||||
|
||||
// uint64FromFile reads a uint64 from a file. All elements of path are sanitized
|
||||
// and joined onto base. Returns error if base no longer prefixes the path after
|
||||
// joining all components.
|
||||
func uint64FromFile(base string, path ...string) (uint64, error) {
|
||||
func sanitizePath(base string, path ...string) (string, error) {
|
||||
l := filepath.Join(path...)
|
||||
p := filepath.Join(base, l)
|
||||
if !strings.HasPrefix(p, base) {
|
||||
return 0, fmt.Errorf("path '%s' attempts to escape base path '%s': %w", l, base, errInvalidInput)
|
||||
return "", fmt.Errorf("path '%s' attempts to escape base path '%s': %w", l, base, errInvalidInput)
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(p)
|
||||
// readUint64FromFile reads a uint64 from a file.
|
||||
//
|
||||
// format specifies the contents of the file in fmt.Scanf syntax.
|
||||
func readUint64FromFile(format string, path ...string) (uint64, error) {
|
||||
filename := filepath.Join(path...)
|
||||
data, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("reading file %s: %w", p, err)
|
||||
return 0, fmt.Errorf("reading file %q: %w", filename, err)
|
||||
}
|
||||
|
||||
et := bytes.TrimSpace(data)
|
||||
return strconv.ParseUint(string(et), 10, 64)
|
||||
var value uint64
|
||||
n, err := fmt.Fscanf(bytes.NewReader(data), format, &value)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("parsing file %q: %w", filename, err)
|
||||
}
|
||||
if n != 1 {
|
||||
return 0, fmt.Errorf("parsing file %q: expected 1 item, got %d", filename, n)
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
type uint64FromFileKey struct {
|
||||
format, path string
|
||||
}
|
||||
|
||||
var uint64FromFileCache = struct {
|
||||
sync.RWMutex
|
||||
values map[uint64FromFileKey]uint64
|
||||
}{
|
||||
values: map[uint64FromFileKey]uint64{},
|
||||
}
|
||||
|
||||
// readUint64FromFileOnce is like readUint64FromFile but memoizes the result.
|
||||
func readUint64FromFileOnce(format string, path ...string) (uint64, error) {
|
||||
filename := filepath.Join(path...)
|
||||
key := uint64FromFileKey{format, filename}
|
||||
|
||||
uint64FromFileCache.RLock()
|
||||
if value, ok := uint64FromFileCache.values[key]; ok {
|
||||
uint64FromFileCache.RUnlock()
|
||||
return value, nil
|
||||
}
|
||||
uint64FromFileCache.RUnlock()
|
||||
|
||||
value, err := readUint64FromFile(format, filename)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
uint64FromFileCache.Lock()
|
||||
defer uint64FromFileCache.Unlock()
|
||||
|
||||
if value, ok := uint64FromFileCache.values[key]; ok {
|
||||
// Someone else got here before us, use what is cached.
|
||||
return value, nil
|
||||
}
|
||||
|
||||
uint64FromFileCache.values[key] = value
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// Probe BPF perf link.
|
||||
|
||||
+1
@@ -23,6 +23,7 @@ const (
|
||||
NetNsType = sys.BPF_LINK_TYPE_NETNS
|
||||
XDPType = sys.BPF_LINK_TYPE_XDP
|
||||
PerfEventType = sys.BPF_LINK_TYPE_PERF_EVENT
|
||||
KprobeMultiType = sys.BPF_LINK_TYPE_KPROBE_MULTI
|
||||
)
|
||||
|
||||
var haveProgAttach = internal.FeatureTest("BPF_PROG_ATTACH", "4.10", func() error {
|
||||
|
||||
+10
-1
@@ -1,6 +1,7 @@
|
||||
package link
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
@@ -70,6 +71,10 @@ func AttachFreplace(targetProg *ebpf.Program, name string, prog *ebpf.Program) (
|
||||
Attach: ebpf.AttachNone,
|
||||
BTF: typeID,
|
||||
})
|
||||
if errors.Is(err, sys.ENOTSUPP) {
|
||||
// This may be returned by bpf_tracing_prog_attach via bpf_arch_text_poke.
|
||||
return nil, fmt.Errorf("create raw tracepoint: %w", ErrNotSupported)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -99,8 +104,12 @@ func attachBTFID(program *ebpf.Program) (Link, error) {
|
||||
fd, err := sys.RawTracepointOpen(&sys.RawTracepointOpenAttr{
|
||||
ProgFd: uint32(program.FD()),
|
||||
})
|
||||
if errors.Is(err, sys.ENOTSUPP) {
|
||||
// This may be returned by bpf_tracing_prog_attach via bpf_arch_text_poke.
|
||||
return nil, fmt.Errorf("create raw tracepoint: %w", ErrNotSupported)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("create raw tracepoint: %w", err)
|
||||
}
|
||||
|
||||
raw := RawLink{fd: fd}
|
||||
|
||||
+7
-21
@@ -7,7 +7,6 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
"github.com/cilium/ebpf/internal"
|
||||
@@ -16,12 +15,6 @@ import (
|
||||
var (
|
||||
uprobeEventsPath = filepath.Join(tracefsPath, "uprobe_events")
|
||||
|
||||
uprobeRetprobeBit = struct {
|
||||
once sync.Once
|
||||
value uint64
|
||||
err error
|
||||
}{}
|
||||
|
||||
uprobeRefCtrOffsetPMUPath = "/sys/bus/event_source/devices/uprobe/format/ref_ctr_offset"
|
||||
// elixir.bootlin.com/linux/v5.15-rc7/source/kernel/events/core.c#L9799
|
||||
uprobeRefCtrOffsetShift = 32
|
||||
@@ -81,7 +74,7 @@ type UprobeOptions struct {
|
||||
|
||||
// To open a new Executable, use:
|
||||
//
|
||||
// OpenExecutable("/bin/bash")
|
||||
// OpenExecutable("/bin/bash")
|
||||
//
|
||||
// The returned value can then be used to open Uprobe(s).
|
||||
func OpenExecutable(path string) (*Executable, error) {
|
||||
@@ -194,13 +187,13 @@ func (ex *Executable) address(symbol string, opts *UprobeOptions) (uint64, error
|
||||
// given symbol starts executing in the given Executable.
|
||||
// For example, /bin/bash::main():
|
||||
//
|
||||
// ex, _ = OpenExecutable("/bin/bash")
|
||||
// ex.Uprobe("main", prog, nil)
|
||||
// ex, _ = OpenExecutable("/bin/bash")
|
||||
// ex.Uprobe("main", prog, nil)
|
||||
//
|
||||
// When using symbols which belongs to shared libraries,
|
||||
// an offset must be provided via options:
|
||||
//
|
||||
// up, err := ex.Uprobe("main", prog, &UprobeOptions{Offset: 0x123})
|
||||
// up, err := ex.Uprobe("main", prog, &UprobeOptions{Offset: 0x123})
|
||||
//
|
||||
// Note: Setting the Offset field in the options supersedes the symbol's offset.
|
||||
//
|
||||
@@ -228,13 +221,13 @@ func (ex *Executable) Uprobe(symbol string, prog *ebpf.Program, opts *UprobeOpti
|
||||
// Uretprobe attaches the given eBPF program to a perf event that fires right
|
||||
// before the given symbol exits. For example, /bin/bash::main():
|
||||
//
|
||||
// ex, _ = OpenExecutable("/bin/bash")
|
||||
// ex.Uretprobe("main", prog, nil)
|
||||
// ex, _ = OpenExecutable("/bin/bash")
|
||||
// ex.Uretprobe("main", prog, nil)
|
||||
//
|
||||
// When using symbols which belongs to shared libraries,
|
||||
// an offset must be provided via options:
|
||||
//
|
||||
// up, err := ex.Uretprobe("main", prog, &UprobeOptions{Offset: 0x123})
|
||||
// up, err := ex.Uretprobe("main", prog, &UprobeOptions{Offset: 0x123})
|
||||
//
|
||||
// Note: Setting the Offset field in the options supersedes the symbol's offset.
|
||||
//
|
||||
@@ -364,10 +357,3 @@ func uprobeToken(args probeArgs) string {
|
||||
|
||||
return po
|
||||
}
|
||||
|
||||
func uretprobeBit() (uint64, error) {
|
||||
uprobeRetprobeBit.once.Do(func() {
|
||||
uprobeRetprobeBit.value, uprobeRetprobeBit.err = determineRetprobeBit(uprobeType)
|
||||
})
|
||||
return uprobeRetprobeBit.value, uprobeRetprobeBit.err
|
||||
}
|
||||
|
||||
+8
-2
@@ -1,12 +1,14 @@
|
||||
package ebpf
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/cilium/ebpf/asm"
|
||||
"github.com/cilium/ebpf/btf"
|
||||
"github.com/cilium/ebpf/internal"
|
||||
)
|
||||
|
||||
// splitSymbols splits insns into subsections delimited by Symbol Instructions.
|
||||
@@ -67,7 +69,7 @@ func hasFunctionReferences(insns asm.Instructions) bool {
|
||||
//
|
||||
// Passing a nil target will relocate against the running kernel. insns are
|
||||
// modified in place.
|
||||
func applyRelocations(insns asm.Instructions, local, target *btf.Spec) error {
|
||||
func applyRelocations(insns asm.Instructions, target *btf.Spec, bo binary.ByteOrder) error {
|
||||
var relos []*btf.CORERelocation
|
||||
var reloInsns []*asm.Instruction
|
||||
iter := insns.Iterate()
|
||||
@@ -82,12 +84,16 @@ func applyRelocations(insns asm.Instructions, local, target *btf.Spec) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if bo == nil {
|
||||
bo = internal.NativeEndian
|
||||
}
|
||||
|
||||
target, err := maybeLoadKernelBTF(target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fixups, err := btf.CORERelocate(local, target, relos)
|
||||
fixups, err := btf.CORERelocate(relos, target, bo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+1
-1
@@ -413,7 +413,7 @@ func (spec *MapSpec) createMap(inner *sys.FD, opts MapOptions, handles *handleCa
|
||||
KeySize: spec.KeySize,
|
||||
ValueSize: spec.ValueSize,
|
||||
MaxEntries: spec.MaxEntries,
|
||||
MapFlags: spec.Flags,
|
||||
MapFlags: sys.MapFlags(spec.Flags),
|
||||
NumaNode: spec.NumaNode,
|
||||
}
|
||||
|
||||
|
||||
+156
-59
@@ -35,16 +35,44 @@ const (
|
||||
// verifier log.
|
||||
const DefaultVerifierLogSize = 64 * 1024
|
||||
|
||||
// maxVerifierLogSize is the maximum size of verifier log buffer the kernel
|
||||
// will accept before returning EINVAL.
|
||||
const maxVerifierLogSize = math.MaxUint32 >> 2
|
||||
|
||||
// ProgramOptions control loading a program into the kernel.
|
||||
type ProgramOptions struct {
|
||||
// Controls the detail emitted by the kernel verifier. Set to non-zero
|
||||
// to enable logging.
|
||||
LogLevel uint32
|
||||
// Controls the output buffer size for the verifier. Defaults to
|
||||
// DefaultVerifierLogSize.
|
||||
// Bitmap controlling the detail emitted by the kernel's eBPF verifier log.
|
||||
// LogLevel-type values can be ORed together to request specific kinds of
|
||||
// verifier output. See the documentation on [ebpf.LogLevel] for details.
|
||||
//
|
||||
// opts.LogLevel = (ebpf.LogLevelBranch | ebpf.LogLevelStats)
|
||||
//
|
||||
// If left to its default value, the program will first be loaded without
|
||||
// verifier output enabled. Upon error, the program load will be repeated
|
||||
// with LogLevelBranch and the given (or default) LogSize value.
|
||||
//
|
||||
// Setting this to a non-zero value will unconditionally enable the verifier
|
||||
// log, populating the [ebpf.Program.VerifierLog] field on successful loads
|
||||
// and including detailed verifier errors if the program is rejected. This
|
||||
// will always allocate an output buffer, but will result in only a single
|
||||
// attempt at loading the program.
|
||||
LogLevel LogLevel
|
||||
|
||||
// Controls the output buffer size for the verifier log, in bytes. See the
|
||||
// documentation on ProgramOptions.LogLevel for details about how this value
|
||||
// is used.
|
||||
//
|
||||
// If this value is set too low to fit the verifier log, the resulting
|
||||
// [ebpf.VerifierError]'s Truncated flag will be true, and the error string
|
||||
// will also contain a hint to that effect.
|
||||
//
|
||||
// Defaults to DefaultVerifierLogSize.
|
||||
LogSize int
|
||||
// Type information used for CO-RE relocations and when attaching to
|
||||
// kernel functions.
|
||||
|
||||
// Disables the verifier log completely, regardless of other options.
|
||||
LogDisabled bool
|
||||
|
||||
// Type information used for CO-RE relocations.
|
||||
//
|
||||
// This is useful in environments where the kernel BTF is not available
|
||||
// (containers) or where it is in a non-standard location. Defaults to
|
||||
@@ -74,7 +102,7 @@ type ProgramSpec struct {
|
||||
// The program to attach to. Must be provided manually.
|
||||
AttachTarget *Program
|
||||
|
||||
// The name of the ELF section this program orininated from.
|
||||
// The name of the ELF section this program originated from.
|
||||
SectionName string
|
||||
|
||||
Instructions asm.Instructions
|
||||
@@ -181,6 +209,10 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *hand
|
||||
return nil, fmt.Errorf("can't load %s program on %s", spec.ByteOrder, internal.NativeEndian)
|
||||
}
|
||||
|
||||
if opts.LogSize < 0 {
|
||||
return nil, errors.New("ProgramOptions.LogSize must be a positive value; disable verifier logs using ProgramOptions.LogDisabled")
|
||||
}
|
||||
|
||||
// Kernels before 5.0 (6c4fc209fcf9 "bpf: remove useless version check for prog load")
|
||||
// require the version field to be set to the value of the KERNEL_VERSION
|
||||
// macro for kprobe-type programs.
|
||||
@@ -206,17 +238,11 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *hand
|
||||
attr.ProgName = sys.NewObjName(spec.Name)
|
||||
}
|
||||
|
||||
kernelTypes := opts.KernelTypes
|
||||
|
||||
insns := make(asm.Instructions, len(spec.Instructions))
|
||||
copy(insns, spec.Instructions)
|
||||
|
||||
var btfDisabled bool
|
||||
if spec.BTF != nil {
|
||||
if err := applyRelocations(insns, spec.BTF, kernelTypes); err != nil {
|
||||
return nil, fmt.Errorf("apply CO-RE relocations: %w", err)
|
||||
}
|
||||
|
||||
handle, err := handles.btfHandle(spec.BTF)
|
||||
btfDisabled = errors.Is(err, btf.ErrNotSupported)
|
||||
if err != nil && !btfDisabled {
|
||||
@@ -241,6 +267,10 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *hand
|
||||
}
|
||||
}
|
||||
|
||||
if err := applyRelocations(insns, opts.KernelTypes, spec.ByteOrder); err != nil {
|
||||
return nil, fmt.Errorf("apply CO-RE relocations: %w", err)
|
||||
}
|
||||
|
||||
if err := fixupAndValidate(insns); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -262,10 +292,10 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *hand
|
||||
}
|
||||
|
||||
attr.AttachBtfId = uint32(targetID)
|
||||
attr.AttachProgFd = uint32(spec.AttachTarget.FD())
|
||||
attr.AttachBtfObjFd = uint32(spec.AttachTarget.FD())
|
||||
defer runtime.KeepAlive(spec.AttachTarget)
|
||||
} else if spec.AttachTo != "" {
|
||||
targetID, err := findTargetInKernel(kernelTypes, spec.AttachTo, spec.Type, spec.AttachType)
|
||||
module, targetID, err := findTargetInKernel(spec.AttachTo, spec.Type, spec.AttachType)
|
||||
if err != nil && !errors.Is(err, errUnrecognizedAttachType) {
|
||||
// We ignore errUnrecognizedAttachType since AttachTo may be non-empty
|
||||
// for programs that don't attach anywhere.
|
||||
@@ -273,16 +303,22 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *hand
|
||||
}
|
||||
|
||||
attr.AttachBtfId = uint32(targetID)
|
||||
if module != nil {
|
||||
attr.AttachBtfObjFd = uint32(module.FD())
|
||||
defer module.Close()
|
||||
}
|
||||
}
|
||||
|
||||
logSize := DefaultVerifierLogSize
|
||||
if opts.LogSize > 0 {
|
||||
logSize = opts.LogSize
|
||||
if opts.LogSize == 0 {
|
||||
opts.LogSize = DefaultVerifierLogSize
|
||||
}
|
||||
|
||||
// The caller provided a specific verifier log level. Immediately load
|
||||
// the program with the given log level and buffer size, and skip retrying
|
||||
// with a different level / size later.
|
||||
var logBuf []byte
|
||||
if opts.LogLevel > 0 {
|
||||
logBuf = make([]byte, logSize)
|
||||
if !opts.LogDisabled && opts.LogLevel != 0 {
|
||||
logBuf = make([]byte, opts.LogSize)
|
||||
attr.LogLevel = opts.LogLevel
|
||||
attr.LogSize = uint32(len(logBuf))
|
||||
attr.LogBuf = sys.NewSlicePointer(logBuf)
|
||||
@@ -293,13 +329,17 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *hand
|
||||
return &Program{unix.ByteSliceToString(logBuf), fd, spec.Name, "", spec.Type}, nil
|
||||
}
|
||||
|
||||
if opts.LogLevel == 0 && opts.LogSize >= 0 {
|
||||
// Re-run with the verifier enabled to get better error messages.
|
||||
logBuf = make([]byte, logSize)
|
||||
attr.LogLevel = 1
|
||||
// A verifier error occurred, but the caller did not specify a log level.
|
||||
// Re-run with branch-level verifier logs enabled to obtain more info.
|
||||
var truncated bool
|
||||
if !opts.LogDisabled && opts.LogLevel == 0 {
|
||||
logBuf = make([]byte, opts.LogSize)
|
||||
attr.LogLevel = LogLevelBranch
|
||||
attr.LogSize = uint32(len(logBuf))
|
||||
attr.LogBuf = sys.NewSlicePointer(logBuf)
|
||||
_, _ = sys.ProgLoad(attr)
|
||||
|
||||
_, ve := sys.ProgLoad(attr)
|
||||
truncated = errors.Is(ve, unix.ENOSPC)
|
||||
}
|
||||
|
||||
switch {
|
||||
@@ -318,11 +358,15 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *hand
|
||||
return nil, fmt.Errorf("load program: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if opts.LogSize > maxVerifierLogSize {
|
||||
return nil, fmt.Errorf("load program: %w (ProgramOptions.LogSize exceeds maximum value of %d)", err, maxVerifierLogSize)
|
||||
}
|
||||
}
|
||||
|
||||
err = internal.ErrorWithLog(err, logBuf)
|
||||
err = internal.ErrorWithLog(err, logBuf, truncated)
|
||||
if btfDisabled {
|
||||
return nil, fmt.Errorf("load program: %w (BTF disabled)", err)
|
||||
return nil, fmt.Errorf("load program: %w (kernel without BTF support)", err)
|
||||
}
|
||||
return nil, fmt.Errorf("load program: %w", err)
|
||||
}
|
||||
@@ -542,7 +586,7 @@ func (p *Program) Run(opts *RunOptions) (uint32, error) {
|
||||
// run or an error. reset is called whenever the benchmark syscall is
|
||||
// interrupted, and should be set to testing.B.ResetTimer or similar.
|
||||
//
|
||||
// Note: profiling a call to this function will skew it's results, see
|
||||
// Note: profiling a call to this function will skew its results, see
|
||||
// https://github.com/cilium/ebpf/issues/24
|
||||
//
|
||||
// This function requires at least Linux 4.12.
|
||||
@@ -580,8 +624,7 @@ var haveProgTestRun = internal.FeatureTest("BPF_PROG_TEST_RUN", "4.12", func() e
|
||||
}
|
||||
defer prog.Close()
|
||||
|
||||
// Programs require at least 14 bytes input
|
||||
in := make([]byte, 14)
|
||||
in := internal.EmptyBPFContext
|
||||
attr := sys.ProgRunAttr{
|
||||
ProgFd: uint32(prog.FD()),
|
||||
DataSizeIn: uint32(len(in)),
|
||||
@@ -599,7 +642,7 @@ var haveProgTestRun = internal.FeatureTest("BPF_PROG_TEST_RUN", "4.12", func() e
|
||||
// We know that PROG_TEST_RUN is supported if we get EINTR.
|
||||
return nil
|
||||
|
||||
case errors.Is(err, unix.ENOTSUPP):
|
||||
case errors.Is(err, sys.ENOTSUPP):
|
||||
// The first PROG_TEST_RUN patches shipped in 4.12 didn't include
|
||||
// a test runner for SocketFilter. ENOTSUPP means PROG_TEST_RUN is
|
||||
// supported, but not for the program type used in the probe.
|
||||
@@ -660,7 +703,7 @@ func (p *Program) testRun(opts *RunOptions) (uint32, time.Duration, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
if errors.Is(err, unix.ENOTSUPP) {
|
||||
if errors.Is(err, sys.ENOTSUPP) {
|
||||
return 0, 0, fmt.Errorf("kernel doesn't support testing program type %s: %w", p.Type(), ErrNotSupported)
|
||||
}
|
||||
|
||||
@@ -770,11 +813,15 @@ var errUnrecognizedAttachType = errors.New("unrecognized attach type")
|
||||
|
||||
// find an attach target type in the kernel.
|
||||
//
|
||||
// spec may be nil and defaults to the canonical kernel BTF. name together with
|
||||
// progType and attachType determine which type we need to attach to.
|
||||
// name, progType and attachType determine which type we need to attach to.
|
||||
//
|
||||
// Returns errUnrecognizedAttachType.
|
||||
func findTargetInKernel(spec *btf.Spec, name string, progType ProgramType, attachType AttachType) (btf.TypeID, error) {
|
||||
// The attach target may be in a loaded kernel module.
|
||||
// In that case the returned handle will be non-nil.
|
||||
// The caller is responsible for closing the handle.
|
||||
//
|
||||
// Returns errUnrecognizedAttachType if the combination of progType and attachType
|
||||
// is not recognised.
|
||||
func findTargetInKernel(name string, progType ProgramType, attachType AttachType) (*btf.Handle, btf.TypeID, error) {
|
||||
type match struct {
|
||||
p ProgramType
|
||||
a AttachType
|
||||
@@ -782,59 +829,109 @@ func findTargetInKernel(spec *btf.Spec, name string, progType ProgramType, attac
|
||||
|
||||
var (
|
||||
typeName, featureName string
|
||||
isBTFTypeFunc = true
|
||||
target btf.Type
|
||||
)
|
||||
|
||||
switch (match{progType, attachType}) {
|
||||
case match{LSM, AttachLSMMac}:
|
||||
typeName = "bpf_lsm_" + name
|
||||
featureName = name + " LSM hook"
|
||||
target = (*btf.Func)(nil)
|
||||
case match{Tracing, AttachTraceIter}:
|
||||
typeName = "bpf_iter_" + name
|
||||
featureName = name + " iterator"
|
||||
target = (*btf.Func)(nil)
|
||||
case match{Tracing, AttachTraceFEntry}:
|
||||
typeName = name
|
||||
featureName = fmt.Sprintf("fentry %s", name)
|
||||
target = (*btf.Func)(nil)
|
||||
case match{Tracing, AttachTraceFExit}:
|
||||
typeName = name
|
||||
featureName = fmt.Sprintf("fexit %s", name)
|
||||
target = (*btf.Func)(nil)
|
||||
case match{Tracing, AttachModifyReturn}:
|
||||
typeName = name
|
||||
featureName = fmt.Sprintf("fmod_ret %s", name)
|
||||
target = (*btf.Func)(nil)
|
||||
case match{Tracing, AttachTraceRawTp}:
|
||||
typeName = fmt.Sprintf("btf_trace_%s", name)
|
||||
featureName = fmt.Sprintf("raw_tp %s", name)
|
||||
isBTFTypeFunc = false
|
||||
target = (*btf.Typedef)(nil)
|
||||
default:
|
||||
return 0, errUnrecognizedAttachType
|
||||
return nil, 0, errUnrecognizedAttachType
|
||||
}
|
||||
|
||||
spec, err := maybeLoadKernelBTF(spec)
|
||||
// maybeLoadKernelBTF may return external BTF if /sys/... is not available.
|
||||
// Ideally we shouldn't use external BTF here, since we might try to use
|
||||
// it for parsing kmod split BTF later on. That seems unlikely to work.
|
||||
spec, err := maybeLoadKernelBTF(nil)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("load kernel spec: %w", err)
|
||||
return nil, 0, fmt.Errorf("load kernel spec: %w", err)
|
||||
}
|
||||
|
||||
var target btf.Type
|
||||
if isBTFTypeFunc {
|
||||
var targetFunc *btf.Func
|
||||
err = spec.TypeByName(typeName, &targetFunc)
|
||||
target = targetFunc
|
||||
} else {
|
||||
var targetTypedef *btf.Typedef
|
||||
err = spec.TypeByName(typeName, &targetTypedef)
|
||||
target = targetTypedef
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
err = spec.TypeByName(typeName, &target)
|
||||
if errors.Is(err, btf.ErrNotFound) {
|
||||
module, id, err := findTargetInModule(spec, typeName, target)
|
||||
if errors.Is(err, btf.ErrNotFound) {
|
||||
return 0, &internal.UnsupportedFeatureError{
|
||||
Name: featureName,
|
||||
}
|
||||
return nil, 0, &internal.UnsupportedFeatureError{Name: featureName}
|
||||
}
|
||||
return 0, fmt.Errorf("find target for %s: %w", featureName, err)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("find target for %s in modules: %w", featureName, err)
|
||||
}
|
||||
return module, id, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("find target for %s in vmlinux: %w", featureName, err)
|
||||
}
|
||||
|
||||
return spec.TypeID(target)
|
||||
id, err := spec.TypeID(target)
|
||||
return nil, id, err
|
||||
}
|
||||
|
||||
// find an attach target type in a kernel module.
|
||||
//
|
||||
// vmlinux must contain the kernel's types and is used to parse kmod BTF.
|
||||
//
|
||||
// Returns btf.ErrNotFound if the target can't be found in any module.
|
||||
func findTargetInModule(vmlinux *btf.Spec, typeName string, target btf.Type) (*btf.Handle, btf.TypeID, error) {
|
||||
it := new(btf.HandleIterator)
|
||||
defer it.Handle.Close()
|
||||
|
||||
for it.Next() {
|
||||
info, err := it.Handle.Info()
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("get info for BTF ID %d: %w", it.ID, err)
|
||||
}
|
||||
|
||||
if !info.IsModule() {
|
||||
continue
|
||||
}
|
||||
|
||||
spec, err := it.Handle.Spec(vmlinux)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("parse types for module %s: %w", info.Name, err)
|
||||
}
|
||||
|
||||
err = spec.TypeByName(typeName, &target)
|
||||
if errors.Is(err, btf.ErrNotFound) {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("lookup type in module %s: %w", info.Name, err)
|
||||
}
|
||||
|
||||
id, err := spec.TypeID(target)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("lookup type id in module %s: %w", info.Name, err)
|
||||
}
|
||||
|
||||
return it.Take(), id, nil
|
||||
}
|
||||
if err := it.Err(); err != nil {
|
||||
return nil, 0, fmt.Errorf("iterate modules: %w", err)
|
||||
}
|
||||
|
||||
return nil, 0, btf.ErrNotFound
|
||||
}
|
||||
|
||||
// find an attach target type in a program.
|
||||
|
||||
+1
-1
@@ -111,7 +111,7 @@ readonly branch="${BRANCH:-master}"
|
||||
fetch() {
|
||||
echo Fetching "${1}"
|
||||
pushd "${tmp_dir}" > /dev/null
|
||||
curl -s -L -O --fail --etag-compare "${1}.etag" --etag-save "${1}.etag" "https://github.com/cilium/ci-kernels/raw/${branch}/${1}"
|
||||
curl --no-progress-meter -L -O --fail --etag-compare "${1}.etag" --etag-save "${1}.etag" "https://github.com/cilium/ci-kernels/raw/${branch}/${1}"
|
||||
local ret=$?
|
||||
popd > /dev/null
|
||||
return $ret
|
||||
|
||||
+2
-2
@@ -143,8 +143,8 @@ func wrapMapError(err error) error {
|
||||
return sys.Error(ErrKeyExist, unix.EEXIST)
|
||||
}
|
||||
|
||||
if errors.Is(err, unix.ENOTSUPP) {
|
||||
return sys.Error(ErrNotSupported, unix.ENOTSUPP)
|
||||
if errors.Is(err, sys.ENOTSUPP) {
|
||||
return sys.Error(ErrNotSupported, sys.ENOTSUPP)
|
||||
}
|
||||
|
||||
if errors.Is(err, unix.E2BIG) {
|
||||
|
||||
+19
@@ -1,6 +1,7 @@
|
||||
package ebpf
|
||||
|
||||
import (
|
||||
"github.com/cilium/ebpf/internal/sys"
|
||||
"github.com/cilium/ebpf/internal/unix"
|
||||
)
|
||||
|
||||
@@ -229,6 +230,7 @@ const (
|
||||
AttachSkReuseportSelect
|
||||
AttachSkReuseportSelectOrMigrate
|
||||
AttachPerfEvent
|
||||
AttachTraceKprobeMulti
|
||||
)
|
||||
|
||||
// AttachFlags of the eBPF program used in BPF_PROG_ATTACH command
|
||||
@@ -282,3 +284,20 @@ type BatchOptions struct {
|
||||
ElemFlags uint64
|
||||
Flags uint64
|
||||
}
|
||||
|
||||
// LogLevel controls the verbosity of the kernel's eBPF program verifier.
|
||||
// These constants can be used for the ProgramOptions.LogLevel field.
|
||||
type LogLevel = sys.LogLevel
|
||||
|
||||
const (
|
||||
// Print verifier state at branch points.
|
||||
LogLevelBranch = sys.BPF_LOG_LEVEL1
|
||||
|
||||
// Print verifier state for every instruction.
|
||||
// Available since Linux v5.2.
|
||||
LogLevelInstruction = sys.BPF_LOG_LEVEL2
|
||||
|
||||
// Print verifier errors and stats at the end of the verification process.
|
||||
// Available since Linux v5.2.
|
||||
LogLevelStats = sys.BPF_LOG_STATS
|
||||
)
|
||||
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
# This source code refers to The Go Authors for copyright purposes.
|
||||
# The master list of authors is in the main Go distribution,
|
||||
# visible at http://tip.golang.org/AUTHORS.
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
# This source code was written by the Go contributors.
|
||||
# The master list of contributors is in the main Go distribution,
|
||||
# visible at http://tip.golang.org/CONTRIBUTORS.
|
||||
+17
-3
@@ -4,9 +4,7 @@
|
||||
|
||||
package unix
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
import "unsafe"
|
||||
|
||||
// IoctlRetInt performs an ioctl operation specified by req on a device
|
||||
// associated with opened file descriptor fd, and returns a non-negative
|
||||
@@ -217,3 +215,19 @@ func IoctlKCMAttach(fd int, info KCMAttach) error {
|
||||
func IoctlKCMUnattach(fd int, info KCMUnattach) error {
|
||||
return ioctlPtr(fd, SIOCKCMUNATTACH, unsafe.Pointer(&info))
|
||||
}
|
||||
|
||||
// IoctlLoopGetStatus64 gets the status of the loop device associated with the
|
||||
// file descriptor fd using the LOOP_GET_STATUS64 operation.
|
||||
func IoctlLoopGetStatus64(fd int) (*LoopInfo64, error) {
|
||||
var value LoopInfo64
|
||||
if err := ioctlPtr(fd, LOOP_GET_STATUS64, unsafe.Pointer(&value)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &value, nil
|
||||
}
|
||||
|
||||
// IoctlLoopSetStatus64 sets the status of the loop device associated with the
|
||||
// file descriptor fd using the LOOP_SET_STATUS64 operation.
|
||||
func IoctlLoopSetStatus64(fd int, value *LoopInfo64) error {
|
||||
return ioctlPtr(fd, LOOP_SET_STATUS64, unsafe.Pointer(value))
|
||||
}
|
||||
|
||||
+11
-16
@@ -73,12 +73,12 @@ aix_ppc64)
|
||||
darwin_amd64)
|
||||
mkerrors="$mkerrors -m64"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
mkasm="go run mkasm_darwin.go"
|
||||
mkasm="go run mkasm.go"
|
||||
;;
|
||||
darwin_arm64)
|
||||
mkerrors="$mkerrors -m64"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
mkasm="go run mkasm_darwin.go"
|
||||
mkasm="go run mkasm.go"
|
||||
;;
|
||||
dragonfly_amd64)
|
||||
mkerrors="$mkerrors -m64"
|
||||
@@ -142,33 +142,33 @@ netbsd_arm64)
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
;;
|
||||
openbsd_386)
|
||||
mkasm="go run mkasm.go"
|
||||
mkerrors="$mkerrors -m32"
|
||||
mksyscall="go run mksyscall.go -l32 -openbsd"
|
||||
mksyscall="go run mksyscall.go -l32 -openbsd -libc"
|
||||
mksysctl="go run mksysctl_openbsd.go"
|
||||
mksysnum="go run mksysnum.go 'https://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master'"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
;;
|
||||
openbsd_amd64)
|
||||
mkasm="go run mkasm.go"
|
||||
mkerrors="$mkerrors -m64"
|
||||
mksyscall="go run mksyscall.go -openbsd"
|
||||
mksyscall="go run mksyscall.go -openbsd -libc"
|
||||
mksysctl="go run mksysctl_openbsd.go"
|
||||
mksysnum="go run mksysnum.go 'https://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master'"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
;;
|
||||
openbsd_arm)
|
||||
mkasm="go run mkasm.go"
|
||||
mkerrors="$mkerrors"
|
||||
mksyscall="go run mksyscall.go -l32 -openbsd -arm"
|
||||
mksyscall="go run mksyscall.go -l32 -openbsd -arm -libc"
|
||||
mksysctl="go run mksysctl_openbsd.go"
|
||||
mksysnum="go run mksysnum.go 'https://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master'"
|
||||
# Let the type of C char be signed for making the bare syscall
|
||||
# API consistent across platforms.
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"
|
||||
;;
|
||||
openbsd_arm64)
|
||||
mkasm="go run mkasm.go"
|
||||
mkerrors="$mkerrors -m64"
|
||||
mksyscall="go run mksyscall.go -openbsd"
|
||||
mksyscall="go run mksyscall.go -openbsd -libc"
|
||||
mksysctl="go run mksysctl_openbsd.go"
|
||||
mksysnum="go run mksysnum.go 'https://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master'"
|
||||
# Let the type of C char be signed for making the bare syscall
|
||||
# API consistent across platforms.
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"
|
||||
@@ -214,11 +214,6 @@ esac
|
||||
if [ "$GOOSARCH" == "aix_ppc64" ]; then
|
||||
# aix/ppc64 script generates files instead of writing to stdin.
|
||||
echo "$mksyscall -tags $GOOS,$GOARCH $syscall_goos $GOOSARCH_in && gofmt -w zsyscall_$GOOSARCH.go && gofmt -w zsyscall_"$GOOSARCH"_gccgo.go && gofmt -w zsyscall_"$GOOSARCH"_gc.go " ;
|
||||
elif [ "$GOOS" == "darwin" ]; then
|
||||
# 1.12 and later, syscalls via libSystem
|
||||
echo "$mksyscall -tags $GOOS,$GOARCH,go1.12 $syscall_goos $GOOSARCH_in |gofmt >zsyscall_$GOOSARCH.go";
|
||||
# 1.13 and later, syscalls via libSystem (including syscallPtr)
|
||||
echo "$mksyscall -tags $GOOS,$GOARCH,go1.13 syscall_darwin.1_13.go |gofmt >zsyscall_$GOOSARCH.1_13.go";
|
||||
elif [ "$GOOS" == "illumos" ]; then
|
||||
# illumos code generation requires a --illumos switch
|
||||
echo "$mksyscall -illumos -tags illumos,$GOARCH syscall_illumos.go |gofmt > zsyscall_illumos_$GOARCH.go";
|
||||
@@ -232,5 +227,5 @@ esac
|
||||
if [ -n "$mksysctl" ]; then echo "$mksysctl |gofmt >$zsysctl"; fi
|
||||
if [ -n "$mksysnum" ]; then echo "$mksysnum |gofmt >zsysnum_$GOOSARCH.go"; fi
|
||||
if [ -n "$mktypes" ]; then echo "$mktypes types_$GOOS.go | go run mkpost.go > ztypes_$GOOSARCH.go"; fi
|
||||
if [ -n "$mkasm" ]; then echo "$mkasm $GOARCH"; fi
|
||||
if [ -n "$mkasm" ]; then echo "$mkasm $GOOS $GOARCH"; fi
|
||||
) | $run
|
||||
|
||||
+4
@@ -297,6 +297,10 @@ struct ltchars {
|
||||
#define SOL_NETLINK 270
|
||||
#endif
|
||||
|
||||
#ifndef SOL_SMC
|
||||
#define SOL_SMC 286
|
||||
#endif
|
||||
|
||||
#ifdef SOL_BLUETOOTH
|
||||
// SPARC includes this in /usr/include/sparc64-linux-gnu/bits/socket.h
|
||||
// but it is already in bluetooth_linux.go
|
||||
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
|
||||
package unix
|
||||
|
||||
func itoa(val int) string { // do it here rather than with fmt to avoid dependency
|
||||
if val < 0 {
|
||||
return "-" + uitoa(uint(-val))
|
||||
}
|
||||
return uitoa(uint(val))
|
||||
}
|
||||
|
||||
func uitoa(val uint) string {
|
||||
var buf [32]byte // big enough for int64
|
||||
i := len(buf) - 1
|
||||
for val >= 10 {
|
||||
buf[i] = byte(val%10 + '0')
|
||||
i--
|
||||
val /= 10
|
||||
}
|
||||
buf[i] = byte(val + '0')
|
||||
return string(buf[i:])
|
||||
}
|
||||
+1
-9
@@ -29,8 +29,6 @@ import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/internal/unsafeheader"
|
||||
)
|
||||
|
||||
// ByteSliceFromString returns a NUL-terminated slice of bytes
|
||||
@@ -82,13 +80,7 @@ func BytePtrToString(p *byte) string {
|
||||
ptr = unsafe.Pointer(uintptr(ptr) + 1)
|
||||
}
|
||||
|
||||
var s []byte
|
||||
h := (*unsafeheader.Slice)(unsafe.Pointer(&s))
|
||||
h.Data = unsafe.Pointer(p)
|
||||
h.Len = n
|
||||
h.Cap = n
|
||||
|
||||
return string(s)
|
||||
return string(unsafe.Slice(p, n))
|
||||
}
|
||||
|
||||
// Single-word zero for use when we need a valid pointer to 0 bytes.
|
||||
|
||||
+53
-4
@@ -218,13 +218,62 @@ func Accept(fd int) (nfd int, sa Sockaddr, err error) {
|
||||
}
|
||||
|
||||
func recvmsgRaw(fd int, iov []Iovec, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) {
|
||||
// Recvmsg not implemented on AIX
|
||||
return -1, -1, -1, ENOSYS
|
||||
var msg Msghdr
|
||||
msg.Name = (*byte)(unsafe.Pointer(rsa))
|
||||
msg.Namelen = uint32(SizeofSockaddrAny)
|
||||
var dummy byte
|
||||
if len(oob) > 0 {
|
||||
// receive at least one normal byte
|
||||
if emptyIovecs(iov) {
|
||||
var iova [1]Iovec
|
||||
iova[0].Base = &dummy
|
||||
iova[0].SetLen(1)
|
||||
iov = iova[:]
|
||||
}
|
||||
msg.Control = (*byte)(unsafe.Pointer(&oob[0]))
|
||||
msg.SetControllen(len(oob))
|
||||
}
|
||||
if len(iov) > 0 {
|
||||
msg.Iov = &iov[0]
|
||||
msg.SetIovlen(len(iov))
|
||||
}
|
||||
if n, err = recvmsg(fd, &msg, flags); n == -1 {
|
||||
return
|
||||
}
|
||||
oobn = int(msg.Controllen)
|
||||
recvflags = int(msg.Flags)
|
||||
return
|
||||
}
|
||||
|
||||
func sendmsgN(fd int, iov []Iovec, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) {
|
||||
// SendmsgN not implemented on AIX
|
||||
return -1, ENOSYS
|
||||
var msg Msghdr
|
||||
msg.Name = (*byte)(unsafe.Pointer(ptr))
|
||||
msg.Namelen = uint32(salen)
|
||||
var dummy byte
|
||||
var empty bool
|
||||
if len(oob) > 0 {
|
||||
// send at least one normal byte
|
||||
empty = emptyIovecs(iov)
|
||||
if empty {
|
||||
var iova [1]Iovec
|
||||
iova[0].Base = &dummy
|
||||
iova[0].SetLen(1)
|
||||
iov = iova[:]
|
||||
}
|
||||
msg.Control = (*byte)(unsafe.Pointer(&oob[0]))
|
||||
msg.SetControllen(len(oob))
|
||||
}
|
||||
if len(iov) > 0 {
|
||||
msg.Iov = &iov[0]
|
||||
msg.SetIovlen(len(iov))
|
||||
}
|
||||
if n, err = sendmsg(fd, &msg, flags); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(oob) > 0 && empty {
|
||||
n = 0
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
|
||||
|
||||
+1
-1
@@ -363,7 +363,7 @@ func sendmsgN(fd int, iov []Iovec, oob []byte, ptr unsafe.Pointer, salen _Sockle
|
||||
var empty bool
|
||||
if len(oob) > 0 {
|
||||
// send at least one normal byte
|
||||
empty := emptyIovecs(iov)
|
||||
empty = emptyIovecs(iov)
|
||||
if empty {
|
||||
var iova [1]Iovec
|
||||
iova[0].Base = &dummy
|
||||
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build darwin && go1.12 && !go1.13
|
||||
// +build darwin,go1.12,!go1.13
|
||||
|
||||
package unix
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const _SYS_GETDIRENTRIES64 = 344
|
||||
|
||||
func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
|
||||
// To implement this using libSystem we'd need syscall_syscallPtr for
|
||||
// fdopendir. However, syscallPtr was only added in Go 1.13, so we fall
|
||||
// back to raw syscalls for this func on Go 1.12.
|
||||
var p unsafe.Pointer
|
||||
if len(buf) > 0 {
|
||||
p = unsafe.Pointer(&buf[0])
|
||||
} else {
|
||||
p = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(_SYS_GETDIRENTRIES64, uintptr(fd), uintptr(p), uintptr(len(buf)), uintptr(unsafe.Pointer(basep)), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
return n, errnoErr(e1)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
-108
@@ -1,108 +0,0 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build darwin && go1.13
|
||||
// +build darwin,go1.13
|
||||
|
||||
package unix
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/internal/unsafeheader"
|
||||
)
|
||||
|
||||
//sys closedir(dir uintptr) (err error)
|
||||
//sys readdir_r(dir uintptr, entry *Dirent, result **Dirent) (res Errno)
|
||||
|
||||
func fdopendir(fd int) (dir uintptr, err error) {
|
||||
r0, _, e1 := syscall_syscallPtr(libc_fdopendir_trampoline_addr, uintptr(fd), 0, 0)
|
||||
dir = uintptr(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_fdopendir_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_fdopendir fdopendir "/usr/lib/libSystem.B.dylib"
|
||||
|
||||
func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
|
||||
// Simulate Getdirentries using fdopendir/readdir_r/closedir.
|
||||
// We store the number of entries to skip in the seek
|
||||
// offset of fd. See issue #31368.
|
||||
// It's not the full required semantics, but should handle the case
|
||||
// of calling Getdirentries or ReadDirent repeatedly.
|
||||
// It won't handle assigning the results of lseek to *basep, or handle
|
||||
// the directory being edited underfoot.
|
||||
skip, err := Seek(fd, 0, 1 /* SEEK_CUR */)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// We need to duplicate the incoming file descriptor
|
||||
// because the caller expects to retain control of it, but
|
||||
// fdopendir expects to take control of its argument.
|
||||
// Just Dup'ing the file descriptor is not enough, as the
|
||||
// result shares underlying state. Use Openat to make a really
|
||||
// new file descriptor referring to the same directory.
|
||||
fd2, err := Openat(fd, ".", O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
d, err := fdopendir(fd2)
|
||||
if err != nil {
|
||||
Close(fd2)
|
||||
return 0, err
|
||||
}
|
||||
defer closedir(d)
|
||||
|
||||
var cnt int64
|
||||
for {
|
||||
var entry Dirent
|
||||
var entryp *Dirent
|
||||
e := readdir_r(d, &entry, &entryp)
|
||||
if e != 0 {
|
||||
return n, errnoErr(e)
|
||||
}
|
||||
if entryp == nil {
|
||||
break
|
||||
}
|
||||
if skip > 0 {
|
||||
skip--
|
||||
cnt++
|
||||
continue
|
||||
}
|
||||
|
||||
reclen := int(entry.Reclen)
|
||||
if reclen > len(buf) {
|
||||
// Not enough room. Return for now.
|
||||
// The counter will let us know where we should start up again.
|
||||
// Note: this strategy for suspending in the middle and
|
||||
// restarting is O(n^2) in the length of the directory. Oh well.
|
||||
break
|
||||
}
|
||||
|
||||
// Copy entry into return buffer.
|
||||
var s []byte
|
||||
hdr := (*unsafeheader.Slice)(unsafe.Pointer(&s))
|
||||
hdr.Data = unsafe.Pointer(&entry)
|
||||
hdr.Cap = reclen
|
||||
hdr.Len = reclen
|
||||
copy(buf, s)
|
||||
|
||||
buf = buf[reclen:]
|
||||
n += reclen
|
||||
cnt++
|
||||
}
|
||||
// Set the seek offset of the input fd to record
|
||||
// how many files we've already returned.
|
||||
_, err = Seek(fd, cnt, 0 /* SEEK_SET */)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
+90
@@ -19,6 +19,96 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//sys closedir(dir uintptr) (err error)
|
||||
//sys readdir_r(dir uintptr, entry *Dirent, result **Dirent) (res Errno)
|
||||
|
||||
func fdopendir(fd int) (dir uintptr, err error) {
|
||||
r0, _, e1 := syscall_syscallPtr(libc_fdopendir_trampoline_addr, uintptr(fd), 0, 0)
|
||||
dir = uintptr(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_fdopendir_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_fdopendir fdopendir "/usr/lib/libSystem.B.dylib"
|
||||
|
||||
func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
|
||||
// Simulate Getdirentries using fdopendir/readdir_r/closedir.
|
||||
// We store the number of entries to skip in the seek
|
||||
// offset of fd. See issue #31368.
|
||||
// It's not the full required semantics, but should handle the case
|
||||
// of calling Getdirentries or ReadDirent repeatedly.
|
||||
// It won't handle assigning the results of lseek to *basep, or handle
|
||||
// the directory being edited underfoot.
|
||||
skip, err := Seek(fd, 0, 1 /* SEEK_CUR */)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// We need to duplicate the incoming file descriptor
|
||||
// because the caller expects to retain control of it, but
|
||||
// fdopendir expects to take control of its argument.
|
||||
// Just Dup'ing the file descriptor is not enough, as the
|
||||
// result shares underlying state. Use Openat to make a really
|
||||
// new file descriptor referring to the same directory.
|
||||
fd2, err := Openat(fd, ".", O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
d, err := fdopendir(fd2)
|
||||
if err != nil {
|
||||
Close(fd2)
|
||||
return 0, err
|
||||
}
|
||||
defer closedir(d)
|
||||
|
||||
var cnt int64
|
||||
for {
|
||||
var entry Dirent
|
||||
var entryp *Dirent
|
||||
e := readdir_r(d, &entry, &entryp)
|
||||
if e != 0 {
|
||||
return n, errnoErr(e)
|
||||
}
|
||||
if entryp == nil {
|
||||
break
|
||||
}
|
||||
if skip > 0 {
|
||||
skip--
|
||||
cnt++
|
||||
continue
|
||||
}
|
||||
|
||||
reclen := int(entry.Reclen)
|
||||
if reclen > len(buf) {
|
||||
// Not enough room. Return for now.
|
||||
// The counter will let us know where we should start up again.
|
||||
// Note: this strategy for suspending in the middle and
|
||||
// restarting is O(n^2) in the length of the directory. Oh well.
|
||||
break
|
||||
}
|
||||
|
||||
// Copy entry into return buffer.
|
||||
s := unsafe.Slice((*byte)(unsafe.Pointer(&entry)), reclen)
|
||||
copy(buf, s)
|
||||
|
||||
buf = buf[reclen:]
|
||||
n += reclen
|
||||
cnt++
|
||||
}
|
||||
// Set the seek offset of the input fd to record
|
||||
// how many files we've already returned.
|
||||
_, err = Seek(fd, cnt, 0 /* SEEK_SET */)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// SockaddrDatalink implements the Sockaddr interface for AF_LINK type sockets.
|
||||
type SockaddrDatalink struct {
|
||||
Len uint8
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@ func PtraceGetFsBase(pid int, fsbase *int64) (err error) {
|
||||
}
|
||||
|
||||
func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) {
|
||||
ioDesc := PtraceIoDesc{Op: int32(req), Offs: (*byte)(unsafe.Pointer(addr)), Addr: (*byte)(unsafe.Pointer(&out[0])), Len: uint32(countin)}
|
||||
ioDesc := PtraceIoDesc{Op: int32(req), Offs: uintptr(unsafe.Pointer(addr)), Addr: uintptr(unsafe.Pointer(&out[0])), Len: uint32(countin)}
|
||||
err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0)
|
||||
return int(ioDesc.Len), err
|
||||
}
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@ func PtraceGetFsBase(pid int, fsbase *int64) (err error) {
|
||||
}
|
||||
|
||||
func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) {
|
||||
ioDesc := PtraceIoDesc{Op: int32(req), Offs: (*byte)(unsafe.Pointer(addr)), Addr: (*byte)(unsafe.Pointer(&out[0])), Len: uint64(countin)}
|
||||
ioDesc := PtraceIoDesc{Op: int32(req), Offs: uintptr(unsafe.Pointer(addr)), Addr: uintptr(unsafe.Pointer(&out[0])), Len: uint64(countin)}
|
||||
err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0)
|
||||
return int(ioDesc.Len), err
|
||||
}
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
|
||||
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno)
|
||||
|
||||
func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) {
|
||||
ioDesc := PtraceIoDesc{Op: int32(req), Offs: (*byte)(unsafe.Pointer(addr)), Addr: (*byte)(unsafe.Pointer(&out[0])), Len: uint32(countin)}
|
||||
ioDesc := PtraceIoDesc{Op: int32(req), Offs: uintptr(unsafe.Pointer(addr)), Addr: uintptr(unsafe.Pointer(&out[0])), Len: uint32(countin)}
|
||||
err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0)
|
||||
return int(ioDesc.Len), err
|
||||
}
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
|
||||
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno)
|
||||
|
||||
func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) {
|
||||
ioDesc := PtraceIoDesc{Op: int32(req), Offs: (*byte)(unsafe.Pointer(addr)), Addr: (*byte)(unsafe.Pointer(&out[0])), Len: uint64(countin)}
|
||||
ioDesc := PtraceIoDesc{Op: int32(req), Offs: uintptr(unsafe.Pointer(addr)), Addr: uintptr(unsafe.Pointer(&out[0])), Len: uint64(countin)}
|
||||
err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0)
|
||||
return int(ioDesc.Len), err
|
||||
}
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
|
||||
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno)
|
||||
|
||||
func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) {
|
||||
ioDesc := PtraceIoDesc{Op: int32(req), Offs: (*byte)(unsafe.Pointer(addr)), Addr: (*byte)(unsafe.Pointer(&out[0])), Len: uint64(countin)}
|
||||
ioDesc := PtraceIoDesc{Op: int32(req), Offs: uintptr(unsafe.Pointer(addr)), Addr: uintptr(unsafe.Pointer(&out[0])), Len: uint64(countin)}
|
||||
err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0)
|
||||
return int(ioDesc.Len), err
|
||||
}
|
||||
|
||||
+33
-12
@@ -13,6 +13,7 @@ package unix
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
@@ -233,7 +234,7 @@ func Futimesat(dirfd int, path string, tv []Timeval) error {
|
||||
func Futimes(fd int, tv []Timeval) (err error) {
|
||||
// Believe it or not, this is the best we can do on Linux
|
||||
// (and is what glibc does).
|
||||
return Utimes("/proc/self/fd/"+itoa(fd), tv)
|
||||
return Utimes("/proc/self/fd/"+strconv.Itoa(fd), tv)
|
||||
}
|
||||
|
||||
const ImplementsGetwd = true
|
||||
@@ -1541,7 +1542,7 @@ func sendmsgN(fd int, iov []Iovec, oob []byte, ptr unsafe.Pointer, salen _Sockle
|
||||
var dummy byte
|
||||
var empty bool
|
||||
if len(oob) > 0 {
|
||||
empty := emptyIovecs(iov)
|
||||
empty = emptyIovecs(iov)
|
||||
if empty {
|
||||
var sockType int
|
||||
sockType, err = GetsockoptInt(fd, SOL_SOCKET, SO_TYPE)
|
||||
@@ -1891,17 +1892,28 @@ func PrctlRetInt(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uint
|
||||
return int(ret), nil
|
||||
}
|
||||
|
||||
// issue 1435.
|
||||
// On linux Setuid and Setgid only affects the current thread, not the process.
|
||||
// This does not match what most callers expect so we must return an error
|
||||
// here rather than letting the caller think that the call succeeded.
|
||||
|
||||
func Setuid(uid int) (err error) {
|
||||
return EOPNOTSUPP
|
||||
return syscall.Setuid(uid)
|
||||
}
|
||||
|
||||
func Setgid(uid int) (err error) {
|
||||
return EOPNOTSUPP
|
||||
func Setgid(gid int) (err error) {
|
||||
return syscall.Setgid(gid)
|
||||
}
|
||||
|
||||
func Setreuid(ruid, euid int) (err error) {
|
||||
return syscall.Setreuid(ruid, euid)
|
||||
}
|
||||
|
||||
func Setregid(rgid, egid int) (err error) {
|
||||
return syscall.Setregid(rgid, egid)
|
||||
}
|
||||
|
||||
func Setresuid(ruid, euid, suid int) (err error) {
|
||||
return syscall.Setresuid(ruid, euid, suid)
|
||||
}
|
||||
|
||||
func Setresgid(rgid, egid, sgid int) (err error) {
|
||||
return syscall.Setresgid(rgid, egid, sgid)
|
||||
}
|
||||
|
||||
// SetfsgidRetGid sets fsgid for current thread and returns previous fsgid set.
|
||||
@@ -2240,7 +2252,7 @@ func (fh *FileHandle) Bytes() []byte {
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
return (*[1 << 30]byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&fh.fileHandle.Type)) + 4))[:n:n]
|
||||
return unsafe.Slice((*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&fh.fileHandle.Type))+4)), n)
|
||||
}
|
||||
|
||||
// NameToHandleAt wraps the name_to_handle_at system call; it obtains
|
||||
@@ -2356,6 +2368,16 @@ func Setitimer(which ItimerWhich, it Itimerval) (Itimerval, error) {
|
||||
return prev, nil
|
||||
}
|
||||
|
||||
//sysnb rtSigprocmask(how int, set *Sigset_t, oldset *Sigset_t, sigsetsize uintptr) (err error) = SYS_RT_SIGPROCMASK
|
||||
|
||||
func PthreadSigmask(how int, set, oldset *Sigset_t) error {
|
||||
if oldset != nil {
|
||||
// Explicitly clear in case Sigset_t is larger than _C__NSIG.
|
||||
*oldset = Sigset_t{}
|
||||
}
|
||||
return rtSigprocmask(how, set, oldset, _C__NSIG/8)
|
||||
}
|
||||
|
||||
/*
|
||||
* Unimplemented
|
||||
*/
|
||||
@@ -2414,7 +2436,6 @@ func Setitimer(which ItimerWhich, it Itimerval) (Itimerval, error) {
|
||||
// RestartSyscall
|
||||
// RtSigaction
|
||||
// RtSigpending
|
||||
// RtSigprocmask
|
||||
// RtSigqueueinfo
|
||||
// RtSigreturn
|
||||
// RtSigsuspend
|
||||
|
||||
-4
@@ -41,10 +41,6 @@ func setTimeval(sec, usec int64) Timeval {
|
||||
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64
|
||||
//sys setfsgid(gid int) (prev int, err error) = SYS_SETFSGID32
|
||||
//sys setfsuid(uid int) (prev int, err error) = SYS_SETFSUID32
|
||||
//sysnb Setregid(rgid int, egid int) (err error) = SYS_SETREGID32
|
||||
//sysnb Setresgid(rgid int, egid int, sgid int) (err error) = SYS_SETRESGID32
|
||||
//sysnb Setresuid(ruid int, euid int, suid int) (err error) = SYS_SETRESUID32
|
||||
//sysnb Setreuid(ruid int, euid int) (err error) = SYS_SETREUID32
|
||||
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int, err error)
|
||||
//sys Stat(path string, stat *Stat_t) (err error) = SYS_STAT64
|
||||
//sys SyncFileRange(fd int, off int64, n int64, flags int) (err error)
|
||||
|
||||
-4
@@ -46,11 +46,7 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err
|
||||
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)
|
||||
//sys setfsgid(gid int) (prev int, err error)
|
||||
//sys setfsuid(uid int) (prev int, err error)
|
||||
//sysnb Setregid(rgid int, egid int) (err error)
|
||||
//sysnb Setresgid(rgid int, egid int, sgid int) (err error)
|
||||
//sysnb Setresuid(ruid int, euid int, suid int) (err error)
|
||||
//sysnb Setrlimit(resource int, rlim *Rlimit) (err error)
|
||||
//sysnb Setreuid(ruid int, euid int) (err error)
|
||||
//sys Shutdown(fd int, how int) (err error)
|
||||
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error)
|
||||
|
||||
|
||||
-4
@@ -62,10 +62,6 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) {
|
||||
//sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) = SYS__NEWSELECT
|
||||
//sys setfsgid(gid int) (prev int, err error) = SYS_SETFSGID32
|
||||
//sys setfsuid(uid int) (prev int, err error) = SYS_SETFSUID32
|
||||
//sysnb Setregid(rgid int, egid int) (err error) = SYS_SETREGID32
|
||||
//sysnb Setresgid(rgid int, egid int, sgid int) (err error) = SYS_SETRESGID32
|
||||
//sysnb Setresuid(ruid int, euid int, suid int) (err error) = SYS_SETRESUID32
|
||||
//sysnb Setreuid(ruid int, euid int) (err error) = SYS_SETREUID32
|
||||
//sys Shutdown(fd int, how int) (err error)
|
||||
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int, err error)
|
||||
//sys Stat(path string, stat *Stat_t) (err error) = SYS_STAT64
|
||||
|
||||
-4
@@ -39,11 +39,7 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err
|
||||
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)
|
||||
//sys setfsgid(gid int) (prev int, err error)
|
||||
//sys setfsuid(uid int) (prev int, err error)
|
||||
//sysnb Setregid(rgid int, egid int) (err error)
|
||||
//sysnb Setresgid(rgid int, egid int, sgid int) (err error)
|
||||
//sysnb Setresuid(ruid int, euid int, suid int) (err error)
|
||||
//sysnb setrlimit(resource int, rlim *Rlimit) (err error)
|
||||
//sysnb Setreuid(ruid int, euid int) (err error)
|
||||
//sys Shutdown(fd int, how int) (err error)
|
||||
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error)
|
||||
|
||||
|
||||
-4
@@ -34,10 +34,6 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err
|
||||
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)
|
||||
//sys setfsgid(gid int) (prev int, err error)
|
||||
//sys setfsuid(uid int) (prev int, err error)
|
||||
//sysnb Setregid(rgid int, egid int) (err error)
|
||||
//sysnb Setresgid(rgid int, egid int, sgid int) (err error)
|
||||
//sysnb Setresuid(ruid int, euid int, suid int) (err error)
|
||||
//sysnb Setreuid(ruid int, euid int) (err error)
|
||||
//sys Shutdown(fd int, how int) (err error)
|
||||
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error)
|
||||
|
||||
|
||||
-4
@@ -37,11 +37,7 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err
|
||||
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)
|
||||
//sys setfsgid(gid int) (prev int, err error)
|
||||
//sys setfsuid(uid int) (prev int, err error)
|
||||
//sysnb Setregid(rgid int, egid int) (err error)
|
||||
//sysnb Setresgid(rgid int, egid int, sgid int) (err error)
|
||||
//sysnb Setresuid(ruid int, euid int, suid int) (err error)
|
||||
//sysnb Setrlimit(resource int, rlim *Rlimit) (err error)
|
||||
//sysnb Setreuid(ruid int, euid int) (err error)
|
||||
//sys Shutdown(fd int, how int) (err error)
|
||||
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error)
|
||||
//sys Statfs(path string, buf *Statfs_t) (err error)
|
||||
|
||||
-4
@@ -32,10 +32,6 @@ func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr,
|
||||
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64
|
||||
//sys setfsgid(gid int) (prev int, err error)
|
||||
//sys setfsuid(uid int) (prev int, err error)
|
||||
//sysnb Setregid(rgid int, egid int) (err error)
|
||||
//sysnb Setresgid(rgid int, egid int, sgid int) (err error)
|
||||
//sysnb Setresuid(ruid int, euid int, suid int) (err error)
|
||||
//sysnb Setreuid(ruid int, euid int) (err error)
|
||||
//sys Shutdown(fd int, how int) (err error)
|
||||
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int, err error)
|
||||
//sys SyncFileRange(fd int, off int64, n int64, flags int) (err error)
|
||||
|
||||
-4
@@ -34,10 +34,6 @@ import (
|
||||
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64
|
||||
//sys setfsgid(gid int) (prev int, err error)
|
||||
//sys setfsuid(uid int) (prev int, err error)
|
||||
//sysnb Setregid(rgid int, egid int) (err error)
|
||||
//sysnb Setresgid(rgid int, egid int, sgid int) (err error)
|
||||
//sysnb Setresuid(ruid int, euid int, suid int) (err error)
|
||||
//sysnb Setreuid(ruid int, euid int) (err error)
|
||||
//sys Shutdown(fd int, how int) (err error)
|
||||
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int, err error)
|
||||
//sys Stat(path string, stat *Stat_t) (err error) = SYS_STAT64
|
||||
|
||||
-4
@@ -34,11 +34,7 @@ package unix
|
||||
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)
|
||||
//sys setfsgid(gid int) (prev int, err error)
|
||||
//sys setfsuid(uid int) (prev int, err error)
|
||||
//sysnb Setregid(rgid int, egid int) (err error)
|
||||
//sysnb Setresgid(rgid int, egid int, sgid int) (err error)
|
||||
//sysnb Setresuid(ruid int, euid int, suid int) (err error)
|
||||
//sysnb Setrlimit(resource int, rlim *Rlimit) (err error)
|
||||
//sysnb Setreuid(ruid int, euid int) (err error)
|
||||
//sys Shutdown(fd int, how int) (err error)
|
||||
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error)
|
||||
//sys Stat(path string, stat *Stat_t) (err error)
|
||||
|
||||
-4
@@ -38,11 +38,7 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err
|
||||
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)
|
||||
//sys setfsgid(gid int) (prev int, err error)
|
||||
//sys setfsuid(uid int) (prev int, err error)
|
||||
//sysnb Setregid(rgid int, egid int) (err error)
|
||||
//sysnb Setresgid(rgid int, egid int, sgid int) (err error)
|
||||
//sysnb Setresuid(ruid int, euid int, suid int) (err error)
|
||||
//sysnb Setrlimit(resource int, rlim *Rlimit) (err error)
|
||||
//sysnb Setreuid(ruid int, euid int) (err error)
|
||||
//sys Shutdown(fd int, how int) (err error)
|
||||
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error)
|
||||
|
||||
|
||||
-4
@@ -34,11 +34,7 @@ import (
|
||||
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)
|
||||
//sys setfsgid(gid int) (prev int, err error)
|
||||
//sys setfsuid(uid int) (prev int, err error)
|
||||
//sysnb Setregid(rgid int, egid int) (err error)
|
||||
//sysnb Setresgid(rgid int, egid int, sgid int) (err error)
|
||||
//sysnb Setresuid(ruid int, euid int, suid int) (err error)
|
||||
//sysnb Setrlimit(resource int, rlim *Rlimit) (err error)
|
||||
//sysnb Setreuid(ruid int, euid int) (err error)
|
||||
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error)
|
||||
//sys Stat(path string, stat *Stat_t) (err error)
|
||||
//sys Statfs(path string, buf *Statfs_t) (err error)
|
||||
|
||||
-4
@@ -31,11 +31,7 @@ package unix
|
||||
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)
|
||||
//sys setfsgid(gid int) (prev int, err error)
|
||||
//sys setfsuid(uid int) (prev int, err error)
|
||||
//sysnb Setregid(rgid int, egid int) (err error)
|
||||
//sysnb Setresgid(rgid int, egid int, sgid int) (err error)
|
||||
//sysnb Setresuid(ruid int, euid int, suid int) (err error)
|
||||
//sysnb Setrlimit(resource int, rlim *Rlimit) (err error)
|
||||
//sysnb Setreuid(ruid int, euid int) (err error)
|
||||
//sys Shutdown(fd int, how int) (err error)
|
||||
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error)
|
||||
//sys Stat(path string, stat *Stat_t) (err error)
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (openbsd && 386) || (openbsd && amd64) || (openbsd && arm) || (openbsd && arm64)
|
||||
// +build openbsd,386 openbsd,amd64 openbsd,arm openbsd,arm64
|
||||
|
||||
package unix
|
||||
|
||||
import _ "unsafe"
|
||||
|
||||
// Implemented in the runtime package (runtime/sys_openbsd3.go)
|
||||
func syscall_syscall(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
|
||||
func syscall_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
|
||||
func syscall_syscall10(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10 uintptr) (r1, r2 uintptr, err Errno)
|
||||
func syscall_rawSyscall(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
|
||||
func syscall_rawSyscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
|
||||
|
||||
//go:linkname syscall_syscall syscall.syscall
|
||||
//go:linkname syscall_syscall6 syscall.syscall6
|
||||
//go:linkname syscall_syscall10 syscall.syscall10
|
||||
//go:linkname syscall_rawSyscall syscall.rawSyscall
|
||||
//go:linkname syscall_rawSyscall6 syscall.rawSyscall6
|
||||
|
||||
func syscall_syscall9(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) {
|
||||
return syscall_syscall10(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, 0)
|
||||
}
|
||||
+67
-44
@@ -750,8 +750,8 @@ type EventPort struct {
|
||||
// we should handle things gracefully. To do so, we need to keep an extra
|
||||
// reference to the cookie around until the event is processed
|
||||
// thus the otherwise seemingly extraneous "cookies" map
|
||||
// The key of this map is a pointer to the corresponding &fCookie.cookie
|
||||
cookies map[*interface{}]*fileObjCookie
|
||||
// The key of this map is a pointer to the corresponding fCookie
|
||||
cookies map[*fileObjCookie]struct{}
|
||||
}
|
||||
|
||||
// PortEvent is an abstraction of the port_event C struct.
|
||||
@@ -778,7 +778,7 @@ func NewEventPort() (*EventPort, error) {
|
||||
port: port,
|
||||
fds: make(map[uintptr]*fileObjCookie),
|
||||
paths: make(map[string]*fileObjCookie),
|
||||
cookies: make(map[*interface{}]*fileObjCookie),
|
||||
cookies: make(map[*fileObjCookie]struct{}),
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
@@ -799,6 +799,7 @@ func (e *EventPort) Close() error {
|
||||
}
|
||||
e.fds = nil
|
||||
e.paths = nil
|
||||
e.cookies = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -826,17 +827,16 @@ func (e *EventPort) AssociatePath(path string, stat os.FileInfo, events int, coo
|
||||
if _, found := e.paths[path]; found {
|
||||
return fmt.Errorf("%v is already associated with this Event Port", path)
|
||||
}
|
||||
fobj, err := createFileObj(path, stat)
|
||||
fCookie, err := createFileObjCookie(path, stat, cookie)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fCookie := &fileObjCookie{fobj, cookie}
|
||||
_, err = port_associate(e.port, PORT_SOURCE_FILE, uintptr(unsafe.Pointer(fobj)), events, (*byte)(unsafe.Pointer(&fCookie.cookie)))
|
||||
_, err = port_associate(e.port, PORT_SOURCE_FILE, uintptr(unsafe.Pointer(fCookie.fobj)), events, (*byte)(unsafe.Pointer(fCookie)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.paths[path] = fCookie
|
||||
e.cookies[&fCookie.cookie] = fCookie
|
||||
e.cookies[fCookie] = struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -858,7 +858,7 @@ func (e *EventPort) DissociatePath(path string) error {
|
||||
if err == nil {
|
||||
// dissociate was successful, safe to delete the cookie
|
||||
fCookie := e.paths[path]
|
||||
delete(e.cookies, &fCookie.cookie)
|
||||
delete(e.cookies, fCookie)
|
||||
}
|
||||
delete(e.paths, path)
|
||||
return err
|
||||
@@ -871,13 +871,16 @@ func (e *EventPort) AssociateFd(fd uintptr, events int, cookie interface{}) erro
|
||||
if _, found := e.fds[fd]; found {
|
||||
return fmt.Errorf("%v is already associated with this Event Port", fd)
|
||||
}
|
||||
fCookie := &fileObjCookie{nil, cookie}
|
||||
_, err := port_associate(e.port, PORT_SOURCE_FD, fd, events, (*byte)(unsafe.Pointer(&fCookie.cookie)))
|
||||
fCookie, err := createFileObjCookie("", nil, cookie)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = port_associate(e.port, PORT_SOURCE_FD, fd, events, (*byte)(unsafe.Pointer(fCookie)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.fds[fd] = fCookie
|
||||
e.cookies[&fCookie.cookie] = fCookie
|
||||
e.cookies[fCookie] = struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -896,27 +899,31 @@ func (e *EventPort) DissociateFd(fd uintptr) error {
|
||||
if err == nil {
|
||||
// dissociate was successful, safe to delete the cookie
|
||||
fCookie := e.fds[fd]
|
||||
delete(e.cookies, &fCookie.cookie)
|
||||
delete(e.cookies, fCookie)
|
||||
}
|
||||
delete(e.fds, fd)
|
||||
return err
|
||||
}
|
||||
|
||||
func createFileObj(name string, stat os.FileInfo) (*fileObj, error) {
|
||||
fobj := new(fileObj)
|
||||
bs, err := ByteSliceFromString(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func createFileObjCookie(name string, stat os.FileInfo, cookie interface{}) (*fileObjCookie, error) {
|
||||
fCookie := new(fileObjCookie)
|
||||
fCookie.cookie = cookie
|
||||
if name != "" && stat != nil {
|
||||
fCookie.fobj = new(fileObj)
|
||||
bs, err := ByteSliceFromString(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fCookie.fobj.Name = (*int8)(unsafe.Pointer(&bs[0]))
|
||||
s := stat.Sys().(*syscall.Stat_t)
|
||||
fCookie.fobj.Atim.Sec = s.Atim.Sec
|
||||
fCookie.fobj.Atim.Nsec = s.Atim.Nsec
|
||||
fCookie.fobj.Mtim.Sec = s.Mtim.Sec
|
||||
fCookie.fobj.Mtim.Nsec = s.Mtim.Nsec
|
||||
fCookie.fobj.Ctim.Sec = s.Ctim.Sec
|
||||
fCookie.fobj.Ctim.Nsec = s.Ctim.Nsec
|
||||
}
|
||||
fobj.Name = (*int8)(unsafe.Pointer(&bs[0]))
|
||||
s := stat.Sys().(*syscall.Stat_t)
|
||||
fobj.Atim.Sec = s.Atim.Sec
|
||||
fobj.Atim.Nsec = s.Atim.Nsec
|
||||
fobj.Mtim.Sec = s.Mtim.Sec
|
||||
fobj.Mtim.Nsec = s.Mtim.Nsec
|
||||
fobj.Ctim.Sec = s.Ctim.Sec
|
||||
fobj.Ctim.Nsec = s.Ctim.Nsec
|
||||
return fobj, nil
|
||||
return fCookie, nil
|
||||
}
|
||||
|
||||
// GetOne wraps port_get(3c) and returns a single PortEvent.
|
||||
@@ -929,44 +936,50 @@ func (e *EventPort) GetOne(t *Timespec) (*PortEvent, error) {
|
||||
p := new(PortEvent)
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
e.peIntToExt(pe, p)
|
||||
err = e.peIntToExt(pe, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// peIntToExt converts a cgo portEvent struct into the friendlier PortEvent
|
||||
// NOTE: Always call this function while holding the e.mu mutex
|
||||
func (e *EventPort) peIntToExt(peInt *portEvent, peExt *PortEvent) {
|
||||
func (e *EventPort) peIntToExt(peInt *portEvent, peExt *PortEvent) error {
|
||||
if e.cookies == nil {
|
||||
return fmt.Errorf("this EventPort is already closed")
|
||||
}
|
||||
peExt.Events = peInt.Events
|
||||
peExt.Source = peInt.Source
|
||||
cookie := (*interface{})(unsafe.Pointer(peInt.User))
|
||||
peExt.Cookie = *cookie
|
||||
fCookie := (*fileObjCookie)(unsafe.Pointer(peInt.User))
|
||||
_, found := e.cookies[fCookie]
|
||||
|
||||
if !found {
|
||||
panic("unexpected event port address; may be due to kernel bug; see https://go.dev/issue/54254")
|
||||
}
|
||||
peExt.Cookie = fCookie.cookie
|
||||
delete(e.cookies, fCookie)
|
||||
|
||||
switch peInt.Source {
|
||||
case PORT_SOURCE_FD:
|
||||
delete(e.cookies, cookie)
|
||||
peExt.Fd = uintptr(peInt.Object)
|
||||
// Only remove the fds entry if it exists and this cookie matches
|
||||
if fobj, ok := e.fds[peExt.Fd]; ok {
|
||||
if &fobj.cookie == cookie {
|
||||
if fobj == fCookie {
|
||||
delete(e.fds, peExt.Fd)
|
||||
}
|
||||
}
|
||||
case PORT_SOURCE_FILE:
|
||||
if fCookie, ok := e.cookies[cookie]; ok && uintptr(unsafe.Pointer(fCookie.fobj)) == uintptr(peInt.Object) {
|
||||
// Use our stashed reference rather than using unsafe on what we got back
|
||||
// the unsafe version would be (*fileObj)(unsafe.Pointer(uintptr(peInt.Object)))
|
||||
peExt.fobj = fCookie.fobj
|
||||
} else {
|
||||
panic("mismanaged memory")
|
||||
}
|
||||
delete(e.cookies, cookie)
|
||||
peExt.fobj = fCookie.fobj
|
||||
peExt.Path = BytePtrToString((*byte)(unsafe.Pointer(peExt.fobj.Name)))
|
||||
// Only remove the paths entry if it exists and this cookie matches
|
||||
if fobj, ok := e.paths[peExt.Path]; ok {
|
||||
if &fobj.cookie == cookie {
|
||||
if fobj == fCookie {
|
||||
delete(e.paths, peExt.Path)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Pending wraps port_getn(3c) and returns how many events are pending.
|
||||
@@ -990,7 +1003,7 @@ func (e *EventPort) Get(s []PortEvent, min int, timeout *Timespec) (int, error)
|
||||
got := uint32(min)
|
||||
max := uint32(len(s))
|
||||
var err error
|
||||
ps := make([]portEvent, max, max)
|
||||
ps := make([]portEvent, max)
|
||||
_, err = port_getn(e.port, &ps[0], max, &got, timeout)
|
||||
// got will be trustworthy with ETIME, but not any other error.
|
||||
if err != nil && err != ETIME {
|
||||
@@ -998,8 +1011,18 @@ func (e *EventPort) Get(s []PortEvent, min int, timeout *Timespec) (int, error)
|
||||
}
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
valid := 0
|
||||
for i := 0; i < int(got); i++ {
|
||||
e.peIntToExt(&ps[i], &s[i])
|
||||
err2 := e.peIntToExt(&ps[i], &s[i])
|
||||
if err2 != nil {
|
||||
if valid == 0 && err == nil {
|
||||
// If err2 is the only error and there are no valid events
|
||||
// to return, return it to the caller.
|
||||
err = err2
|
||||
}
|
||||
break
|
||||
}
|
||||
valid = i + 1
|
||||
}
|
||||
return int(got), err
|
||||
return valid, err
|
||||
}
|
||||
|
||||
+9
-11
@@ -13,8 +13,6 @@ import (
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/internal/unsafeheader"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -117,11 +115,7 @@ func (m *mmapper) Mmap(fd int, offset int64, length int, prot int, flags int) (d
|
||||
}
|
||||
|
||||
// Use unsafe to convert addr into a []byte.
|
||||
var b []byte
|
||||
hdr := (*unsafeheader.Slice)(unsafe.Pointer(&b))
|
||||
hdr.Data = unsafe.Pointer(addr)
|
||||
hdr.Cap = length
|
||||
hdr.Len = length
|
||||
b := unsafe.Slice((*byte)(unsafe.Pointer(addr)), length)
|
||||
|
||||
// Register mapping in m and return it.
|
||||
p := &b[cap(b)-1]
|
||||
@@ -429,11 +423,15 @@ func Send(s int, buf []byte, flags int) (err error) {
|
||||
}
|
||||
|
||||
func Sendto(fd int, p []byte, flags int, to Sockaddr) (err error) {
|
||||
ptr, n, err := to.sockaddr()
|
||||
if err != nil {
|
||||
return err
|
||||
var ptr unsafe.Pointer
|
||||
var salen _Socklen
|
||||
if to != nil {
|
||||
ptr, salen, err = to.sockaddr()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return sendto(fd, p, flags, ptr, n)
|
||||
return sendto(fd, p, flags, ptr, salen)
|
||||
}
|
||||
|
||||
func SetsockoptByte(fd, level, opt int, value byte) (err error) {
|
||||
|
||||
+2
-11
@@ -7,11 +7,7 @@
|
||||
|
||||
package unix
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/internal/unsafeheader"
|
||||
)
|
||||
import "unsafe"
|
||||
|
||||
// SysvShmAttach attaches the Sysv shared memory segment associated with the
|
||||
// shared memory identifier id.
|
||||
@@ -34,12 +30,7 @@ func SysvShmAttach(id int, addr uintptr, flag int) ([]byte, error) {
|
||||
}
|
||||
|
||||
// Use unsafe to convert addr into a []byte.
|
||||
// TODO: convert to unsafe.Slice once we can assume Go 1.17
|
||||
var b []byte
|
||||
hdr := (*unsafeheader.Slice)(unsafe.Pointer(&b))
|
||||
hdr.Data = unsafe.Pointer(addr)
|
||||
hdr.Cap = int(info.Segsz)
|
||||
hdr.Len = int(info.Segsz)
|
||||
b := unsafe.Slice((*byte)(unsafe.Pointer(addr)), int(info.Segsz))
|
||||
return b, nil
|
||||
}
|
||||
|
||||
|
||||
+65
-30
@@ -160,13 +160,12 @@ func Lremovexattr(link string, attr string) (err error) {
|
||||
}
|
||||
|
||||
func Listxattr(file string, dest []byte) (sz int, err error) {
|
||||
d := initxattrdest(dest, 0)
|
||||
destsiz := len(dest)
|
||||
|
||||
// FreeBSD won't allow you to list xattrs from multiple namespaces
|
||||
s := 0
|
||||
s, pos := 0, 0
|
||||
for _, nsid := range [...]int{EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM} {
|
||||
stmp, e := ExtattrListFile(file, nsid, uintptr(d), destsiz)
|
||||
stmp, e := ListxattrNS(file, nsid, dest[pos:])
|
||||
|
||||
/* Errors accessing system attrs are ignored so that
|
||||
* we can implement the Linux-like behavior of omitting errors that
|
||||
@@ -175,66 +174,102 @@ func Listxattr(file string, dest []byte) (sz int, err error) {
|
||||
* Linux will still error if we ask for user attributes on a file that
|
||||
* we don't have read permissions on, so don't ignore those errors
|
||||
*/
|
||||
if e != nil && e == EPERM && nsid != EXTATTR_NAMESPACE_USER {
|
||||
continue
|
||||
} else if e != nil {
|
||||
if e != nil {
|
||||
if e == EPERM && nsid != EXTATTR_NAMESPACE_USER {
|
||||
continue
|
||||
}
|
||||
return s, e
|
||||
}
|
||||
|
||||
s += stmp
|
||||
destsiz -= s
|
||||
if destsiz < 0 {
|
||||
destsiz = 0
|
||||
pos = s
|
||||
if pos > destsiz {
|
||||
pos = destsiz
|
||||
}
|
||||
d = initxattrdest(dest, s)
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func ListxattrNS(file string, nsid int, dest []byte) (sz int, err error) {
|
||||
d := initxattrdest(dest, 0)
|
||||
destsiz := len(dest)
|
||||
|
||||
s, e := ExtattrListFile(file, nsid, uintptr(d), destsiz)
|
||||
if e != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func Flistxattr(fd int, dest []byte) (sz int, err error) {
|
||||
d := initxattrdest(dest, 0)
|
||||
destsiz := len(dest)
|
||||
|
||||
s := 0
|
||||
s, pos := 0, 0
|
||||
for _, nsid := range [...]int{EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM} {
|
||||
stmp, e := ExtattrListFd(fd, nsid, uintptr(d), destsiz)
|
||||
if e != nil && e == EPERM && nsid != EXTATTR_NAMESPACE_USER {
|
||||
continue
|
||||
} else if e != nil {
|
||||
stmp, e := FlistxattrNS(fd, nsid, dest[pos:])
|
||||
|
||||
if e != nil {
|
||||
if e == EPERM && nsid != EXTATTR_NAMESPACE_USER {
|
||||
continue
|
||||
}
|
||||
return s, e
|
||||
}
|
||||
|
||||
s += stmp
|
||||
destsiz -= s
|
||||
if destsiz < 0 {
|
||||
destsiz = 0
|
||||
pos = s
|
||||
if pos > destsiz {
|
||||
pos = destsiz
|
||||
}
|
||||
d = initxattrdest(dest, s)
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func FlistxattrNS(fd int, nsid int, dest []byte) (sz int, err error) {
|
||||
d := initxattrdest(dest, 0)
|
||||
destsiz := len(dest)
|
||||
|
||||
s, e := ExtattrListFd(fd, nsid, uintptr(d), destsiz)
|
||||
if e != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func Llistxattr(link string, dest []byte) (sz int, err error) {
|
||||
d := initxattrdest(dest, 0)
|
||||
destsiz := len(dest)
|
||||
|
||||
s := 0
|
||||
s, pos := 0, 0
|
||||
for _, nsid := range [...]int{EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM} {
|
||||
stmp, e := ExtattrListLink(link, nsid, uintptr(d), destsiz)
|
||||
if e != nil && e == EPERM && nsid != EXTATTR_NAMESPACE_USER {
|
||||
continue
|
||||
} else if e != nil {
|
||||
stmp, e := LlistxattrNS(link, nsid, dest[pos:])
|
||||
|
||||
if e != nil {
|
||||
if e == EPERM && nsid != EXTATTR_NAMESPACE_USER {
|
||||
continue
|
||||
}
|
||||
return s, e
|
||||
}
|
||||
|
||||
s += stmp
|
||||
destsiz -= s
|
||||
if destsiz < 0 {
|
||||
destsiz = 0
|
||||
pos = s
|
||||
if pos > destsiz {
|
||||
pos = destsiz
|
||||
}
|
||||
d = initxattrdest(dest, s)
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func LlistxattrNS(link string, nsid int, dest []byte) (sz int, err error) {
|
||||
d := initxattrdest(dest, 0)
|
||||
destsiz := len(dest)
|
||||
|
||||
s, e := ExtattrListLink(link, nsid, uintptr(d), destsiz)
|
||||
if e != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return s, nil
|
||||
|
||||
+1
@@ -2940,6 +2940,7 @@ const (
|
||||
SOL_RAW = 0xff
|
||||
SOL_RDS = 0x114
|
||||
SOL_RXRPC = 0x110
|
||||
SOL_SMC = 0x11e
|
||||
SOL_TCP = 0x6
|
||||
SOL_TIPC = 0x10f
|
||||
SOL_TLS = 0x11a
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/include -m32
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/386/include -m32
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build 386 && linux
|
||||
// +build 386,linux
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/include -m32 _const.go
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/386/include -m32 _const.go
|
||||
|
||||
package unix
|
||||
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/include -m64
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/amd64/include -m64
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build amd64 && linux
|
||||
// +build amd64,linux
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/include -m64 _const.go
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/amd64/include -m64 _const.go
|
||||
|
||||
package unix
|
||||
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/include
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/arm/include
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build arm && linux
|
||||
// +build arm,linux
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/arm/include _const.go
|
||||
|
||||
package unix
|
||||
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/include -fsigned-char
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/arm64/include -fsigned-char
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build arm64 && linux
|
||||
// +build arm64,linux
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/include -fsigned-char _const.go
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/arm64/include -fsigned-char _const.go
|
||||
|
||||
package unix
|
||||
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/include
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/loong64/include
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build loong64 && linux
|
||||
// +build loong64,linux
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/loong64/include _const.go
|
||||
|
||||
package unix
|
||||
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/include
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/mips/include
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build mips && linux
|
||||
// +build mips,linux
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/mips/include _const.go
|
||||
|
||||
package unix
|
||||
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/include
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/mips64/include
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build mips64 && linux
|
||||
// +build mips64,linux
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/mips64/include _const.go
|
||||
|
||||
package unix
|
||||
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/include
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/mips64le/include
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build mips64le && linux
|
||||
// +build mips64le,linux
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/mips64le/include _const.go
|
||||
|
||||
package unix
|
||||
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/include
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/mipsle/include
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build mipsle && linux
|
||||
// +build mipsle,linux
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/mipsle/include _const.go
|
||||
|
||||
package unix
|
||||
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/include
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/ppc/include
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build ppc && linux
|
||||
// +build ppc,linux
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/ppc/include _const.go
|
||||
|
||||
package unix
|
||||
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/include
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/ppc64/include
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build ppc64 && linux
|
||||
// +build ppc64,linux
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/ppc64/include _const.go
|
||||
|
||||
package unix
|
||||
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/include
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/ppc64le/include
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build ppc64le && linux
|
||||
// +build ppc64le,linux
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/ppc64le/include _const.go
|
||||
|
||||
package unix
|
||||
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/include
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/riscv64/include
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build riscv64 && linux
|
||||
// +build riscv64,linux
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/riscv64/include _const.go
|
||||
|
||||
package unix
|
||||
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/include -fsigned-char
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/s390x/include -fsigned-char
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build s390x && linux
|
||||
// +build s390x,linux
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/include -fsigned-char _const.go
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/s390x/include -fsigned-char _const.go
|
||||
|
||||
package unix
|
||||
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/include
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/sparc64/include
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build sparc64 && linux
|
||||
// +build sparc64,linux
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/sparc64/include _const.go
|
||||
|
||||
package unix
|
||||
|
||||
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
// go run mksyscall.go -tags darwin,amd64,go1.13 syscall_darwin.1_13.go
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build darwin && amd64 && go1.13
|
||||
// +build darwin,amd64,go1.13
|
||||
|
||||
package unix
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var _ syscall.Errno
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func closedir(dir uintptr) (err error) {
|
||||
_, _, e1 := syscall_syscall(libc_closedir_trampoline_addr, uintptr(dir), 0, 0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_closedir_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_closedir closedir "/usr/lib/libSystem.B.dylib"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func readdir_r(dir uintptr, entry *Dirent, result **Dirent) (res Errno) {
|
||||
r0, _, _ := syscall_syscall(libc_readdir_r_trampoline_addr, uintptr(dir), uintptr(unsafe.Pointer(entry)), uintptr(unsafe.Pointer(result)))
|
||||
res = Errno(r0)
|
||||
return
|
||||
}
|
||||
|
||||
var libc_readdir_r_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_readdir_r readdir_r "/usr/lib/libSystem.B.dylib"
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
// go run mkasm_darwin.go amd64
|
||||
// Code generated by the command above; DO NOT EDIT.
|
||||
|
||||
//go:build go1.13
|
||||
// +build go1.13
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
TEXT libc_fdopendir_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_fdopendir(SB)
|
||||
|
||||
GLOBL ·libc_fdopendir_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_fdopendir_trampoline_addr(SB)/8, $libc_fdopendir_trampoline<>(SB)
|
||||
|
||||
TEXT libc_closedir_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_closedir(SB)
|
||||
|
||||
GLOBL ·libc_closedir_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_closedir_trampoline_addr(SB)/8, $libc_closedir_trampoline<>(SB)
|
||||
|
||||
TEXT libc_readdir_r_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_readdir_r(SB)
|
||||
|
||||
GLOBL ·libc_readdir_r_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_readdir_r_trampoline_addr(SB)/8, $libc_readdir_r_trampoline<>(SB)
|
||||
+29
-3
@@ -1,8 +1,8 @@
|
||||
// go run mksyscall.go -tags darwin,amd64,go1.12 syscall_bsd.go syscall_darwin.go syscall_darwin_amd64.go
|
||||
// go run mksyscall.go -tags darwin,amd64 syscall_bsd.go syscall_darwin.go syscall_darwin_amd64.go
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build darwin && amd64 && go1.12
|
||||
// +build darwin,amd64,go1.12
|
||||
//go:build darwin && amd64
|
||||
// +build darwin,amd64
|
||||
|
||||
package unix
|
||||
|
||||
@@ -463,6 +463,32 @@ var libc_munlockall_trampoline_addr uintptr
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func closedir(dir uintptr) (err error) {
|
||||
_, _, e1 := syscall_syscall(libc_closedir_trampoline_addr, uintptr(dir), 0, 0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_closedir_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_closedir closedir "/usr/lib/libSystem.B.dylib"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func readdir_r(dir uintptr, entry *Dirent, result **Dirent) (res Errno) {
|
||||
r0, _, _ := syscall_syscall(libc_readdir_r_trampoline_addr, uintptr(dir), uintptr(unsafe.Pointer(entry)), uintptr(unsafe.Pointer(result)))
|
||||
res = Errno(r0)
|
||||
return
|
||||
}
|
||||
|
||||
var libc_readdir_r_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_readdir_r readdir_r "/usr/lib/libSystem.B.dylib"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func pipe(p *[2]int32) (err error) {
|
||||
_, _, e1 := syscall_rawSyscall(libc_pipe_trampoline_addr, uintptr(unsafe.Pointer(p)), 0, 0)
|
||||
if e1 != 0 {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user