Merge pull request #109029 from ehashman/runc-1.1.0

Dep bump to runc 1.1.0, cadvisor 0.44.0
This commit is contained in:
Kubernetes Prow Robot 2022-03-28 22:45:10 -07:00 committed by GitHub
commit fe8a663860
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
253 changed files with 6554 additions and 5345 deletions

View File

@ -1,31 +0,0 @@
= vendor/github.com/bits-and-blooms/bitset licensed under: =
Copyright (c) 2014 Will Fitzgerald. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
= vendor/github.com/bits-and-blooms/bitset/LICENSE 0d2b5d66dabaff0df8e1ffe191a21bd1

37
go.mod
View File

@ -31,7 +31,7 @@ require (
github.com/coreos/go-systemd/v22 v22.3.2 github.com/coreos/go-systemd/v22 v22.3.2
github.com/cpuguy83/go-md2man/v2 v2.0.1 github.com/cpuguy83/go-md2man/v2 v2.0.1
github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew v1.1.1
github.com/docker/distribution v2.7.1+incompatible github.com/docker/distribution v2.8.0+incompatible
github.com/docker/go-units v0.4.0 github.com/docker/go-units v0.4.0
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153
github.com/emicklei/go-restful v2.9.5+incompatible github.com/emicklei/go-restful v2.9.5+incompatible
@ -39,12 +39,12 @@ require (
github.com/fsnotify/fsnotify v1.4.9 github.com/fsnotify/fsnotify v1.4.9
github.com/go-logr/logr v1.2.0 github.com/go-logr/logr v1.2.0
github.com/go-ozzo/ozzo-validation v3.5.0+incompatible // indirect github.com/go-ozzo/ozzo-validation v3.5.0+incompatible // indirect
github.com/godbus/dbus/v5 v5.0.4 github.com/godbus/dbus/v5 v5.0.6
github.com/gogo/protobuf v1.3.2 github.com/gogo/protobuf v1.3.2
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da
github.com/golang/mock v1.5.0 github.com/golang/mock v1.5.0
github.com/golang/protobuf v1.5.2 github.com/golang/protobuf v1.5.2
github.com/google/cadvisor v0.43.0 github.com/google/cadvisor v0.44.0
github.com/google/gnostic v0.5.7-v3refs github.com/google/gnostic v0.5.7-v3refs
github.com/google/go-cmp v0.5.5 github.com/google/go-cmp v0.5.5
github.com/google/gofuzz v1.1.0 github.com/google/gofuzz v1.1.0
@ -63,8 +63,8 @@ require (
github.com/mvdan/xurls v1.1.0 github.com/mvdan/xurls v1.1.0
github.com/onsi/ginkgo v1.14.0 github.com/onsi/ginkgo v1.14.0
github.com/onsi/gomega v1.10.1 github.com/onsi/gomega v1.10.1
github.com/opencontainers/runc v1.0.3 github.com/opencontainers/runc v1.1.0
github.com/opencontainers/selinux v1.8.2 github.com/opencontainers/selinux v1.10.0
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/pmezard/go-difflib v1.0.0 github.com/pmezard/go-difflib v1.0.0
github.com/prometheus/client_golang v1.12.1 github.com/prometheus/client_golang v1.12.1
@ -178,7 +178,6 @@ replace (
github.com/aws/aws-sdk-go => github.com/aws/aws-sdk-go v1.38.49 github.com/aws/aws-sdk-go => github.com/aws/aws-sdk-go v1.38.49
github.com/benbjohnson/clock => github.com/benbjohnson/clock v1.1.0 github.com/benbjohnson/clock => github.com/benbjohnson/clock v1.1.0
github.com/beorn7/perks => github.com/beorn7/perks v1.0.1 github.com/beorn7/perks => github.com/beorn7/perks v1.0.1
github.com/bits-and-blooms/bitset => github.com/bits-and-blooms/bitset v1.2.0
github.com/blang/semver => github.com/blang/semver v3.5.1+incompatible github.com/blang/semver => github.com/blang/semver v3.5.1+incompatible
github.com/blang/semver/v4 => github.com/blang/semver/v4 v4.0.0 github.com/blang/semver/v4 => github.com/blang/semver/v4 v4.0.0
github.com/boltdb/bolt => github.com/boltdb/bolt v1.3.1 github.com/boltdb/bolt => github.com/boltdb/bolt v1.3.1
@ -187,11 +186,11 @@ replace (
github.com/cespare/xxhash => github.com/cespare/xxhash v1.1.0 github.com/cespare/xxhash => github.com/cespare/xxhash v1.1.0
github.com/cespare/xxhash/v2 => github.com/cespare/xxhash/v2 v2.1.2 github.com/cespare/xxhash/v2 => github.com/cespare/xxhash/v2 v2.1.2
github.com/chai2010/gettext-go => github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 github.com/chai2010/gettext-go => github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5
github.com/checkpoint-restore/go-criu/v5 => github.com/checkpoint-restore/go-criu/v5 v5.0.0 github.com/checkpoint-restore/go-criu/v5 => github.com/checkpoint-restore/go-criu/v5 v5.3.0
github.com/chzyer/logex => github.com/chzyer/logex v1.1.10 github.com/chzyer/logex => github.com/chzyer/logex v1.1.10
github.com/chzyer/readline => github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e github.com/chzyer/readline => github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
github.com/chzyer/test => github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 github.com/chzyer/test => github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1
github.com/cilium/ebpf => github.com/cilium/ebpf v0.6.2 github.com/cilium/ebpf => github.com/cilium/ebpf v0.7.0
github.com/clusterhq/flocker-go => github.com/clusterhq/flocker-go v0.0.0-20160920122132-2b8b7259d313 github.com/clusterhq/flocker-go => github.com/clusterhq/flocker-go v0.0.0-20160920122132-2b8b7259d313
github.com/cncf/udpa/go => github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 github.com/cncf/udpa/go => github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403
github.com/cncf/xds/go => github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed github.com/cncf/xds/go => github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed
@ -200,8 +199,8 @@ replace (
github.com/cockroachdb/logtags => github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f github.com/cockroachdb/logtags => github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f
github.com/container-storage-interface/spec => github.com/container-storage-interface/spec v1.5.0 github.com/container-storage-interface/spec => github.com/container-storage-interface/spec v1.5.0
github.com/containerd/cgroups => github.com/containerd/cgroups v1.0.1 github.com/containerd/cgroups => github.com/containerd/cgroups v1.0.1
github.com/containerd/console => github.com/containerd/console v1.0.2 github.com/containerd/console => github.com/containerd/console v1.0.3
github.com/containerd/containerd => github.com/containerd/containerd v1.4.11 github.com/containerd/containerd => github.com/containerd/containerd v1.4.12
github.com/containerd/continuity => github.com/containerd/continuity v0.1.0 github.com/containerd/continuity => github.com/containerd/continuity v0.1.0
github.com/containerd/fifo => github.com/containerd/fifo v1.0.0 github.com/containerd/fifo => github.com/containerd/fifo v1.0.0
github.com/containerd/go-runc => github.com/containerd/go-runc v1.0.0 github.com/containerd/go-runc => github.com/containerd/go-runc v1.0.0
@ -214,12 +213,12 @@ replace (
github.com/coreos/go-systemd/v22 => github.com/coreos/go-systemd/v22 v22.3.2 github.com/coreos/go-systemd/v22 => github.com/coreos/go-systemd/v22 v22.3.2
github.com/cpuguy83/go-md2man/v2 => github.com/cpuguy83/go-md2man/v2 v2.0.1 github.com/cpuguy83/go-md2man/v2 => github.com/cpuguy83/go-md2man/v2 v2.0.1
github.com/creack/pty => github.com/creack/pty v1.1.11 github.com/creack/pty => github.com/creack/pty v1.1.11
github.com/cyphar/filepath-securejoin => github.com/cyphar/filepath-securejoin v0.2.2 github.com/cyphar/filepath-securejoin => github.com/cyphar/filepath-securejoin v0.2.3
github.com/davecgh/go-spew => github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew => github.com/davecgh/go-spew v1.1.1
github.com/daviddengcn/go-colortext => github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd github.com/daviddengcn/go-colortext => github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd
github.com/dnaeon/go-vcr => github.com/dnaeon/go-vcr v1.0.1 github.com/dnaeon/go-vcr => github.com/dnaeon/go-vcr v1.0.1
github.com/docker/distribution => github.com/docker/distribution v2.7.1+incompatible github.com/docker/distribution => github.com/docker/distribution v2.8.0+incompatible
github.com/docker/docker => github.com/docker/docker v20.10.7+incompatible github.com/docker/docker => github.com/docker/docker v20.10.12+incompatible
github.com/docker/go-connections => github.com/docker/go-connections v0.4.0 github.com/docker/go-connections => github.com/docker/go-connections v0.4.0
github.com/docker/go-units => github.com/docker/go-units v0.4.0 github.com/docker/go-units => github.com/docker/go-units v0.4.0
github.com/docopt/docopt-go => github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 github.com/docopt/docopt-go => github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
@ -254,7 +253,7 @@ replace (
github.com/go-openapi/swag => github.com/go-openapi/swag v0.19.14 github.com/go-openapi/swag => github.com/go-openapi/swag v0.19.14
github.com/go-ozzo/ozzo-validation => github.com/go-ozzo/ozzo-validation v3.5.0+incompatible github.com/go-ozzo/ozzo-validation => github.com/go-ozzo/ozzo-validation v3.5.0+incompatible
github.com/go-stack/stack => github.com/go-stack/stack v1.8.0 github.com/go-stack/stack => github.com/go-stack/stack v1.8.0
github.com/godbus/dbus/v5 => github.com/godbus/dbus/v5 v5.0.4 github.com/godbus/dbus/v5 => github.com/godbus/dbus/v5 v5.0.6
github.com/gofrs/uuid => github.com/gofrs/uuid v4.0.0+incompatible github.com/gofrs/uuid => github.com/gofrs/uuid v4.0.0+incompatible
github.com/gogo/protobuf => github.com/gogo/protobuf v1.3.2 github.com/gogo/protobuf => github.com/gogo/protobuf v1.3.2
github.com/golang/freetype => github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 github.com/golang/freetype => github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
@ -264,7 +263,7 @@ replace (
github.com/golang/protobuf => github.com/golang/protobuf v1.5.2 github.com/golang/protobuf => github.com/golang/protobuf v1.5.2
github.com/golangplus/testing => github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e github.com/golangplus/testing => github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e
github.com/google/btree => github.com/google/btree v1.0.1 github.com/google/btree => github.com/google/btree v1.0.1
github.com/google/cadvisor => github.com/google/cadvisor v0.43.0 github.com/google/cadvisor => github.com/google/cadvisor v0.44.0
github.com/google/cel-go => github.com/google/cel-go v0.10.1 github.com/google/cel-go => github.com/google/cel-go v0.10.1
github.com/google/cel-spec => github.com/google/cel-spec v0.6.0 github.com/google/cel-spec => github.com/google/cel-spec v0.6.0
github.com/google/gnostic => github.com/google/gnostic v0.5.7-v3refs github.com/google/gnostic => github.com/google/gnostic v0.5.7-v3refs
@ -337,10 +336,10 @@ replace (
github.com/onsi/ginkgo => github.com/onsi/ginkgo v1.14.0 github.com/onsi/ginkgo => github.com/onsi/ginkgo v1.14.0
github.com/onsi/gomega => github.com/onsi/gomega v1.10.1 github.com/onsi/gomega => github.com/onsi/gomega v1.10.1
github.com/opencontainers/go-digest => github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/go-digest => github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec => github.com/opencontainers/image-spec v1.0.1 github.com/opencontainers/image-spec => github.com/opencontainers/image-spec v1.0.2
github.com/opencontainers/runc => github.com/opencontainers/runc v1.0.3 github.com/opencontainers/runc => github.com/opencontainers/runc v1.1.0
github.com/opencontainers/runtime-spec => github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 github.com/opencontainers/runtime-spec => github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417
github.com/opencontainers/selinux => github.com/opencontainers/selinux v1.8.2 github.com/opencontainers/selinux => github.com/opencontainers/selinux v1.10.0
github.com/opentracing/opentracing-go => github.com/opentracing/opentracing-go v1.1.0 github.com/opentracing/opentracing-go => github.com/opentracing/opentracing-go v1.1.0
github.com/peterbourgon/diskv => github.com/peterbourgon/diskv v2.0.1+incompatible github.com/peterbourgon/diskv => github.com/peterbourgon/diskv v2.0.1+incompatible
github.com/pkg/errors => github.com/pkg/errors v0.9.1 github.com/pkg/errors => github.com/pkg/errors v0.9.1
@ -359,7 +358,7 @@ replace (
github.com/rubiojr/go-vhd => github.com/rubiojr/go-vhd v0.0.0-20200706105327-02e210299021 github.com/rubiojr/go-vhd => github.com/rubiojr/go-vhd v0.0.0-20200706105327-02e210299021
github.com/russross/blackfriday => github.com/russross/blackfriday v1.5.2 github.com/russross/blackfriday => github.com/russross/blackfriday v1.5.2
github.com/russross/blackfriday/v2 => github.com/russross/blackfriday/v2 v2.1.0 github.com/russross/blackfriday/v2 => github.com/russross/blackfriday/v2 v2.1.0
github.com/seccomp/libseccomp-golang => github.com/seccomp/libseccomp-golang v0.9.1 github.com/seccomp/libseccomp-golang => github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921
github.com/sergi/go-diff => github.com/sergi/go-diff v1.1.0 github.com/sergi/go-diff => github.com/sergi/go-diff v1.1.0
github.com/sirupsen/logrus => github.com/sirupsen/logrus v1.8.1 github.com/sirupsen/logrus => github.com/sirupsen/logrus v1.8.1
github.com/smartystreets/assertions => github.com/smartystreets/assertions v1.1.0 github.com/smartystreets/assertions => github.com/smartystreets/assertions v1.1.0

54
go.sum
View File

@ -66,8 +66,6 @@ github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLj
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA=
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
@ -83,13 +81,13 @@ github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cb
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 h1:7aWHqerlJ41y6FOsEUvknqgXnGmJyJSbjhAWq5pO4F8= github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 h1:7aWHqerlJ41y6FOsEUvknqgXnGmJyJSbjhAWq5pO4F8=
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
github.com/checkpoint-restore/go-criu/v5 v5.0.0 h1:TW8f/UvntYoVDMN1K2HlT82qH1rb0sOjpGw3m6Ym+i4= github.com/checkpoint-restore/go-criu/v5 v5.3.0 h1:wpFFOoomK3389ue2lAb0Boag6XPht5QYpipxmSNL4d8=
github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cilium/ebpf v0.6.2 h1:iHsfF/t4aW4heW2YKfeHrVPGdtYTL4C4KocpM8KTSnI= github.com/cilium/ebpf v0.7.0 h1:1k/q3ATgxSXRdrmPfH8d7YK0GfqVsEKZAX9dQZvs56k=
github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
github.com/clusterhq/flocker-go v0.0.0-20160920122132-2b8b7259d313 h1:eIHD9GNM3Hp7kcRW5mvcz7WTR3ETeoYYKwpgA04kaXE= github.com/clusterhq/flocker-go v0.0.0-20160920122132-2b8b7259d313 h1:eIHD9GNM3Hp7kcRW5mvcz7WTR3ETeoYYKwpgA04kaXE=
github.com/clusterhq/flocker-go v0.0.0-20160920122132-2b8b7259d313/go.mod h1:P1wt9Z3DP8O6W3rvwCt0REIlshg1InHImaLW0t3ObY0= github.com/clusterhq/flocker-go v0.0.0-20160920122132-2b8b7259d313/go.mod h1:P1wt9Z3DP8O6W3rvwCt0REIlshg1InHImaLW0t3ObY0=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
@ -104,10 +102,10 @@ github.com/container-storage-interface/spec v1.5.0 h1:lvKxe3uLgqQeVQcrnL2CPQKISo
github.com/container-storage-interface/spec v1.5.0/go.mod h1:8K96oQNkJ7pFcC2R9Z1ynGGBB1I93kcS6PGg3SsOk8s= github.com/container-storage-interface/spec v1.5.0/go.mod h1:8K96oQNkJ7pFcC2R9Z1ynGGBB1I93kcS6PGg3SsOk8s=
github.com/containerd/cgroups v1.0.1 h1:iJnMvco9XGvKUvNQkv88bE4uJXxRQH18efbKo9w5vHQ= github.com/containerd/cgroups v1.0.1 h1:iJnMvco9XGvKUvNQkv88bE4uJXxRQH18efbKo9w5vHQ=
github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU=
github.com/containerd/console v1.0.2 h1:Pi6D+aZXM+oUw1czuKgH5IJ+y0jhYcwBJfx5/Ghn9dE= github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
github.com/containerd/containerd v1.4.11 h1:QCGOUN+i70jEEL/A6JVIbhy4f4fanzAzSR4kNG7SlcE= github.com/containerd/containerd v1.4.12 h1:V+SHzYmhng/iju6M5nFrpTTusrhidoxKTwdwLw+u4c4=
github.com/containerd/containerd v1.4.11/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.12/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM=
github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4=
github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
@ -129,18 +127,18 @@ github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKY
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg= github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI=
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd h1:uVsMphB1eRx7xB1njzL3fuMdWRN8HtVzoUOItHMwv5c= github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd h1:uVsMphB1eRx7xB1njzL3fuMdWRN8HtVzoUOItHMwv5c=
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY= github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.8.0+incompatible h1:l9EaZDICImO1ngI+uTifW+ZYvvz7fKISBAKpg+MbWbY=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v20.10.7+incompatible h1:Z6O9Nhsjv+ayUEeI1IojKbYcsGdgYSNqxe1s2MYzUhQ= github.com/docker/docker v20.10.12+incompatible h1:CEeNmFM0QZIsJCZKMkZx0ZcahTiewkrgiwfYD+dfl1U=
github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.12+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
@ -197,8 +195,8 @@ github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/
github.com/go-ozzo/ozzo-validation v3.5.0+incompatible h1:sUy/in/P6askYr16XJgTKq/0SZhiWsdg4WZGaLsGQkM= github.com/go-ozzo/ozzo-validation v3.5.0+incompatible h1:sUy/in/P6askYr16XJgTKq/0SZhiWsdg4WZGaLsGQkM=
github.com/go-ozzo/ozzo-validation v3.5.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU= github.com/go-ozzo/ozzo-validation v3.5.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
@ -215,8 +213,8 @@ github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e h1:KhcknUwkWHKZ
github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/cadvisor v0.43.0 h1:z0ULgYPKZ7L/c7Zjq+ZD6ltklWwYdCSvBMgSjNC/hGo= github.com/google/cadvisor v0.44.0 h1:523mBK94XHKAsU1TIQl/tYoPyYpWQdalf2CQ8Gv2Xek=
github.com/google/cadvisor v0.43.0/go.mod h1:+RdMSbc3FVr5NYCD2dOEJy/LI0jYJ/0xJXkzWXEyiFQ= github.com/google/cadvisor v0.44.0/go.mod h1:GQ9KQfz0iNHQk3D6ftzJWK4TXabfIgM10Oy3FkR+Gzg=
github.com/google/cel-go v0.10.1 h1:MQBGSZGnDwh7T/un+mzGKOMz3x+4E/GDPprWjDL+1Jg= github.com/google/cel-go v0.10.1 h1:MQBGSZGnDwh7T/un+mzGKOMz3x+4E/GDPprWjDL+1Jg=
github.com/google/cel-go v0.10.1/go.mod h1:U7ayypeSkw23szu4GaQTPJGx66c20mx8JklMSxrmI1w= github.com/google/cel-go v0.10.1/go.mod h1:U7ayypeSkw23szu4GaQTPJGx66c20mx8JklMSxrmI1w=
github.com/google/cel-spec v0.6.0/go.mod h1:Nwjgxy5CbjlPrtCWjeDjUyKMl8w41YBYGjsyDdqk0xA= github.com/google/cel-spec v0.6.0/go.mod h1:Nwjgxy5CbjlPrtCWjeDjUyKMl8w41YBYGjsyDdqk0xA=
@ -343,14 +341,14 @@ github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v1.0.3 h1:1hbqejyQWCJBvtKAfdO0b1FmaEf2z/bxnjqbARass5k= github.com/opencontainers/runc v1.1.0 h1:O9+X96OcDjkmmZyfaG996kV7yq8HsoU2h1XRRQcefG8=
github.com/opencontainers/runc v1.0.3/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= github.com/opencontainers/runc v1.1.0/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc=
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 h1:3snG66yBm59tKhhSPQrQ/0bCrv1LQbKt40LnUPiUxdc= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 h1:3snG66yBm59tKhhSPQrQ/0bCrv1LQbKt40LnUPiUxdc=
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.8.2 h1:c4ca10UMgRcvZ6h0K4HtS15UaVSBEaE+iln2LVpAuGc= github.com/opencontainers/selinux v1.10.0 h1:rAiKF8hTcgLI3w0DHm6i0ylVVcOrlgR1kK99DRLDhyU=
github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
@ -382,8 +380,8 @@ github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNue
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/seccomp/libseccomp-golang v0.9.1 h1:NJjM5DNFOs0s3kYE1WUOr6G8V97sdt46rlXTMfXGWBo= github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921 h1:58EBmR2dMNL2n/FnbQewK3D14nXr0V9CObDSvMJLq+Y=
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=

View File

@ -28,9 +28,8 @@ import (
"time" "time"
libcontainercgroups "github.com/opencontainers/runc/libcontainer/cgroups" libcontainercgroups "github.com/opencontainers/runc/libcontainer/cgroups"
cgroupfs "github.com/opencontainers/runc/libcontainer/cgroups/fs"
cgroupfs2 "github.com/opencontainers/runc/libcontainer/cgroups/fs2"
"github.com/opencontainers/runc/libcontainer/cgroups/fscommon" "github.com/opencontainers/runc/libcontainer/cgroups/fscommon"
"github.com/opencontainers/runc/libcontainer/cgroups/manager"
cgroupsystemd "github.com/opencontainers/runc/libcontainer/cgroups/systemd" cgroupsystemd "github.com/opencontainers/runc/libcontainer/cgroups/systemd"
libcontainerconfigs "github.com/opencontainers/runc/libcontainer/configs" libcontainerconfigs "github.com/opencontainers/runc/libcontainer/configs"
"k8s.io/klog/v2" "k8s.io/klog/v2"
@ -42,14 +41,7 @@ import (
"k8s.io/kubernetes/pkg/kubelet/metrics" "k8s.io/kubernetes/pkg/kubelet/metrics"
) )
// libcontainerCgroupManagerType defines how to interface with libcontainer
type libcontainerCgroupManagerType string
const ( const (
// libcontainerCgroupfs means use libcontainer with cgroupfs
libcontainerCgroupfs libcontainerCgroupManagerType = "cgroupfs"
// libcontainerSystemd means use libcontainer with systemd
libcontainerSystemd libcontainerCgroupManagerType = "systemd"
// systemdSuffix is the cgroup name suffix for systemd // systemdSuffix is the cgroup name suffix for systemd
systemdSuffix string = ".slice" systemdSuffix string = ".slice"
// MemoryMin is memory.min for cgroup v2 // MemoryMin is memory.min for cgroup v2
@ -133,39 +125,6 @@ func IsSystemdStyleName(name string) bool {
return strings.HasSuffix(name, systemdSuffix) return strings.HasSuffix(name, systemdSuffix)
} }
// libcontainerAdapter provides a simplified interface to libcontainer based on libcontainer type.
type libcontainerAdapter struct {
// cgroupManagerType defines how to interface with libcontainer
cgroupManagerType libcontainerCgroupManagerType
}
// newLibcontainerAdapter returns a configured libcontainerAdapter for specified manager.
// it does any initialization required by that manager to function.
func newLibcontainerAdapter(cgroupManagerType libcontainerCgroupManagerType) *libcontainerAdapter {
return &libcontainerAdapter{cgroupManagerType: cgroupManagerType}
}
// newManager returns an implementation of cgroups.Manager
func (l *libcontainerAdapter) newManager(cgroups *libcontainerconfigs.Cgroup, paths map[string]string) (libcontainercgroups.Manager, error) {
switch l.cgroupManagerType {
case libcontainerCgroupfs:
if libcontainercgroups.IsCgroup2UnifiedMode() {
return cgroupfs2.NewManager(cgroups, paths["memory"], false)
}
return cgroupfs.NewManager(cgroups, paths, false), nil
case libcontainerSystemd:
// this means you asked systemd to manage cgroups, but systemd was not on the host, so all you can do is panic...
if !cgroupsystemd.IsRunningSystemd() {
panic("systemd cgroup manager not available")
}
if libcontainercgroups.IsCgroup2UnifiedMode() {
return cgroupsystemd.NewUnifiedManager(cgroups, paths["memory"], false), nil
}
return cgroupsystemd.NewLegacyManager(cgroups, paths), nil
}
return nil, fmt.Errorf("invalid cgroup manager configuration")
}
// CgroupSubsystems holds information about the mounted cgroup subsystems // CgroupSubsystems holds information about the mounted cgroup subsystems
type CgroupSubsystems struct { type CgroupSubsystems struct {
// Cgroup subsystem mounts. // Cgroup subsystem mounts.
@ -180,13 +139,14 @@ type CgroupSubsystems struct {
// cgroupManagerImpl implements the CgroupManager interface. // cgroupManagerImpl implements the CgroupManager interface.
// Its a stateless object which can be used to // Its a stateless object which can be used to
// update,create or delete any number of cgroups // update,create or delete any number of cgroups
// It uses the Libcontainer raw fs cgroup manager for cgroup management. // It relies on runc/libcontainer cgroup managers.
type cgroupManagerImpl struct { type cgroupManagerImpl struct {
// subsystems holds information about all the // subsystems holds information about all the
// mounted cgroup subsystems on the node // mounted cgroup subsystems on the node
subsystems *CgroupSubsystems subsystems *CgroupSubsystems
// simplifies interaction with libcontainer and its cgroup managers
adapter *libcontainerAdapter // useSystemd tells if systemd cgroup manager should be used.
useSystemd bool
} }
// Make sure that cgroupManagerImpl implements the CgroupManager interface // Make sure that cgroupManagerImpl implements the CgroupManager interface
@ -194,20 +154,16 @@ var _ CgroupManager = &cgroupManagerImpl{}
// NewCgroupManager is a factory method that returns a CgroupManager // NewCgroupManager is a factory method that returns a CgroupManager
func NewCgroupManager(cs *CgroupSubsystems, cgroupDriver string) CgroupManager { func NewCgroupManager(cs *CgroupSubsystems, cgroupDriver string) CgroupManager {
managerType := libcontainerCgroupfs
if cgroupDriver == string(libcontainerSystemd) {
managerType = libcontainerSystemd
}
return &cgroupManagerImpl{ return &cgroupManagerImpl{
subsystems: cs, subsystems: cs,
adapter: newLibcontainerAdapter(managerType), useSystemd: cgroupDriver == "systemd",
} }
} }
// Name converts the cgroup to the driver specific value in cgroupfs form. // Name converts the cgroup to the driver specific value in cgroupfs form.
// This always returns a valid cgroupfs path even when systemd driver is in use! // This always returns a valid cgroupfs path even when systemd driver is in use!
func (m *cgroupManagerImpl) Name(name CgroupName) string { func (m *cgroupManagerImpl) Name(name CgroupName) string {
if m.adapter.cgroupManagerType == libcontainerSystemd { if m.useSystemd {
return name.ToSystemd() return name.ToSystemd()
} }
return name.ToCgroupfs() return name.ToCgroupfs()
@ -215,7 +171,7 @@ func (m *cgroupManagerImpl) Name(name CgroupName) string {
// CgroupName converts the literal cgroupfs name on the host to an internal identifier. // CgroupName converts the literal cgroupfs name on the host to an internal identifier.
func (m *cgroupManagerImpl) CgroupName(name string) CgroupName { func (m *cgroupManagerImpl) CgroupName(name string) CgroupName {
if m.adapter.cgroupManagerType == libcontainerSystemd { if m.useSystemd {
return ParseSystemdToCgroupName(name) return ParseSystemdToCgroupName(name)
} }
return ParseCgroupfsToCgroupName(name) return ParseCgroupfsToCgroupName(name)
@ -239,14 +195,16 @@ func (m *cgroupManagerImpl) buildCgroupUnifiedPath(name CgroupName) string {
// libctCgroupConfig converts CgroupConfig to libcontainer's Cgroup config. // libctCgroupConfig converts CgroupConfig to libcontainer's Cgroup config.
func (m *cgroupManagerImpl) libctCgroupConfig(in *CgroupConfig, needResources bool) *libcontainerconfigs.Cgroup { func (m *cgroupManagerImpl) libctCgroupConfig(in *CgroupConfig, needResources bool) *libcontainerconfigs.Cgroup {
config := &libcontainerconfigs.Cgroup{} config := &libcontainerconfigs.Cgroup{
Systemd: m.useSystemd,
}
if needResources { if needResources {
config.Resources = m.toResources(in.ResourceParameters) config.Resources = m.toResources(in.ResourceParameters)
} else { } else {
config.Resources = &libcontainerconfigs.Resources{} config.Resources = &libcontainerconfigs.Resources{}
} }
if m.adapter.cgroupManagerType == libcontainerCgroupfs { if !config.Systemd {
// For fs cgroup manager, we can either set Path or Name and Parent. // For fs cgroup manager, we can either set Path or Name and Parent.
// Setting Path is easier. // Setting Path is easier.
config.Path = in.Name.ToCgroupfs() config.Path = in.Name.ToCgroupfs()
@ -335,11 +293,8 @@ func (m *cgroupManagerImpl) Destroy(cgroupConfig *CgroupConfig) error {
metrics.CgroupManagerDuration.WithLabelValues("destroy").Observe(metrics.SinceInSeconds(start)) metrics.CgroupManagerDuration.WithLabelValues("destroy").Observe(metrics.SinceInSeconds(start))
}() }()
cgroupPaths := m.buildCgroupPaths(cgroupConfig.Name)
libcontainerCgroupConfig := m.libctCgroupConfig(cgroupConfig, false) libcontainerCgroupConfig := m.libctCgroupConfig(cgroupConfig, false)
manager, err := manager.New(libcontainerCgroupConfig)
manager, err := m.adapter.newManager(libcontainerCgroupConfig, cgroupPaths)
if err != nil { if err != nil {
return err return err
} }
@ -463,7 +418,7 @@ func (m *cgroupManagerImpl) maybeSetHugetlb(resourceConfig *ResourceConfig, reso
pageSizes.Insert(sizeString) pageSizes.Insert(sizeString)
} }
// for each page size omitted, limit to 0 // for each page size omitted, limit to 0
for _, pageSize := range cgroupfs.HugePageSizes { for _, pageSize := range libcontainercgroups.HugePageSizes() {
if pageSizes.Has(pageSize) { if pageSizes.Has(pageSize) {
continue continue
} }
@ -482,16 +437,7 @@ func (m *cgroupManagerImpl) Update(cgroupConfig *CgroupConfig) error {
}() }()
libcontainerCgroupConfig := m.libctCgroupConfig(cgroupConfig, true) libcontainerCgroupConfig := m.libctCgroupConfig(cgroupConfig, true)
manager, err := manager.New(libcontainerCgroupConfig)
unified := libcontainercgroups.IsCgroup2UnifiedMode()
var paths map[string]string
if unified {
libcontainerCgroupConfig.Path = m.Name(cgroupConfig.Name)
} else {
paths = m.buildCgroupPaths(cgroupConfig.Name)
}
manager, err := m.adapter.newManager(libcontainerCgroupConfig, paths)
if err != nil { if err != nil {
return fmt.Errorf("failed to create cgroup manager: %v", err) return fmt.Errorf("failed to create cgroup manager: %v", err)
} }
@ -506,9 +452,7 @@ func (m *cgroupManagerImpl) Create(cgroupConfig *CgroupConfig) error {
}() }()
libcontainerCgroupConfig := m.libctCgroupConfig(cgroupConfig, true) libcontainerCgroupConfig := m.libctCgroupConfig(cgroupConfig, true)
manager, err := manager.New(libcontainerCgroupConfig)
// get the manager with the specified cgroup configuration
manager, err := m.adapter.newManager(libcontainerCgroupConfig, nil)
if err != nil { if err != nil {
return err return err
} }

View File

@ -30,8 +30,7 @@ import (
"time" "time"
"github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/cgroups"
cgroupfs "github.com/opencontainers/runc/libcontainer/cgroups/fs" "github.com/opencontainers/runc/libcontainer/cgroups/manager"
cgroupfs2 "github.com/opencontainers/runc/libcontainer/cgroups/fs2"
"github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/configs"
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/mount-utils" "k8s.io/mount-utils"
@ -380,13 +379,10 @@ func createManager(containerName string) (cgroups.Manager, error) {
Resources: &configs.Resources{ Resources: &configs.Resources{
SkipDevices: true, SkipDevices: true,
}, },
Systemd: false,
} }
if cgroups.IsCgroup2UnifiedMode() { return manager.New(cg)
return cgroupfs2.NewManager(cg, "", false)
}
return cgroupfs.NewManager(cg, nil, false), nil
} }
type KernelTunableBehavior string type KernelTunableBehavior string

View File

@ -320,7 +320,7 @@ func NodeAllocatableRoot(cgroupRoot string, cgroupsPerQOS bool, cgroupDriver str
if cgroupsPerQOS { if cgroupsPerQOS {
nodeAllocatableRoot = NewCgroupName(nodeAllocatableRoot, defaultNodeAllocatableCgroupName) nodeAllocatableRoot = NewCgroupName(nodeAllocatableRoot, defaultNodeAllocatableCgroupName)
} }
if libcontainerCgroupManagerType(cgroupDriver) == libcontainerSystemd { if cgroupDriver == "systemd" {
return nodeAllocatableRoot.ToSystemd() return nodeAllocatableRoot.ToSystemd()
} }
return nodeAllocatableRoot.ToCgroupfs() return nodeAllocatableRoot.ToCgroupfs()

View File

@ -23,7 +23,7 @@ import (
"strings" "strings"
"testing" "testing"
"k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
) )
@ -107,13 +107,15 @@ func TestIsCgroupPod(t *testing.T) {
qosContainersInfo: qosContainersInfo, qosContainersInfo: qosContainersInfo,
} }
for _, testCase := range testCases { for _, testCase := range testCases {
// give the right cgroup structure based on driver // Give the right cgroup structure based on whether systemd is enabled.
cgroupfs := testCase.input.ToCgroupfs() var name string
if cgroupDriver == "systemd" { if cgroupDriver == "systemd" {
cgroupfs = testCase.input.ToSystemd() name = testCase.input.ToSystemd()
} else {
name = testCase.input.ToCgroupfs()
} }
// check if this is a pod or not with the literal cgroupfs input // check if this is a pod or not with the literal cgroupfs input
result, resultUID := pcm.IsPodCgroup(cgroupfs) result, resultUID := pcm.IsPodCgroup(name)
if result != testCase.expectedResult { if result != testCase.expectedResult {
t.Errorf("Unexpected result for driver: %v, input: %v, expected: %v, actual: %v", cgroupDriver, testCase.input, testCase.expectedResult, result) t.Errorf("Unexpected result for driver: %v, input: %v, expected: %v, actual: %v", cgroupDriver, testCase.input, testCase.expectedResult, result)
} }

View File

@ -23,14 +23,13 @@ import (
"sync" "sync"
"time" "time"
v1 "k8s.io/api/core/v1"
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
units "github.com/docker/go-units" units "github.com/docker/go-units"
libcontainercgroups "github.com/opencontainers/runc/libcontainer/cgroups" libcontainercgroups "github.com/opencontainers/runc/libcontainer/cgroups"
cgroupfs "github.com/opencontainers/runc/libcontainer/cgroups/fs"
v1 "k8s.io/api/core/v1"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/api/v1/resource" "k8s.io/kubernetes/pkg/api/v1/resource"
v1qos "k8s.io/kubernetes/pkg/apis/core/v1/helper/qos" v1qos "k8s.io/kubernetes/pkg/apis/core/v1/helper/qos"
@ -147,7 +146,7 @@ func (m *qosContainerManagerImpl) Start(getNodeAllocatable func() v1.ResourceLis
// setHugePagesUnbounded ensures hugetlb is effectively unbounded // setHugePagesUnbounded ensures hugetlb is effectively unbounded
func (m *qosContainerManagerImpl) setHugePagesUnbounded(cgroupConfig *CgroupConfig) error { func (m *qosContainerManagerImpl) setHugePagesUnbounded(cgroupConfig *CgroupConfig) error {
hugePageLimit := map[int64]int64{} hugePageLimit := map[int64]int64{}
for _, pageSize := range cgroupfs.HugePageSizes { for _, pageSize := range libcontainercgroups.HugePageSizes() {
pageSizeBytes, err := units.RAMInBytes(pageSize) pageSizeBytes, err := units.RAMInBytes(pageSize)
if err != nil { if err != nil {
return err return err

View File

@ -24,7 +24,6 @@ import (
"time" "time"
libcontainercgroups "github.com/opencontainers/runc/libcontainer/cgroups" libcontainercgroups "github.com/opencontainers/runc/libcontainer/cgroups"
cgroupfs "github.com/opencontainers/runc/libcontainer/cgroups/fs"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
@ -170,7 +169,7 @@ func GetHugepageLimitsFromResources(resources v1.ResourceRequirements) []*runtim
var hugepageLimits []*runtimeapi.HugepageLimit var hugepageLimits []*runtimeapi.HugepageLimit
// For each page size, limit to 0. // For each page size, limit to 0.
for _, pageSize := range cgroupfs.HugePageSizes { for _, pageSize := range libcontainercgroups.HugePageSizes() {
hugepageLimits = append(hugepageLimits, &runtimeapi.HugepageLimit{ hugepageLimits = append(hugepageLimits, &runtimeapi.HugepageLimit{
PageSize: pageSize, PageSize: pageSize,
Limit: uint64(0), Limit: uint64(0),

View File

@ -25,7 +25,7 @@ import (
"testing" "testing"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
cgroupfs "github.com/opencontainers/runc/libcontainer/cgroups/fs" libcontainercgroups "github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
@ -366,7 +366,7 @@ func TestGetHugepageLimitsFromResources(t *testing.T) {
var baseHugepage []*runtimeapi.HugepageLimit var baseHugepage []*runtimeapi.HugepageLimit
// For each page size, limit to 0. // For each page size, limit to 0.
for _, pageSize := range cgroupfs.HugePageSizes { for _, pageSize := range libcontainercgroups.HugePageSizes() {
baseHugepage = append(baseHugepage, &runtimeapi.HugepageLimit{ baseHugepage = append(baseHugepage, &runtimeapi.HugepageLimit{
PageSize: pageSize, PageSize: pageSize,
Limit: uint64(0), Limit: uint64(0),
@ -481,7 +481,7 @@ func TestGetHugepageLimitsFromResources(t *testing.T) {
machineHugepageSupport := true machineHugepageSupport := true
for _, hugepageLimit := range test.expected { for _, hugepageLimit := range test.expected {
hugepageSupport := false hugepageSupport := false
for _, pageSize := range cgroupfs.HugePageSizes { for _, pageSize := range libcontainercgroups.HugePageSizes() {
if pageSize == hugepageLimit.PageSize { if pageSize == hugepageLimit.PageSize {
hugepageSupport = true hugepageSupport = true
break break

View File

@ -9,7 +9,7 @@ require (
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5
github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew v1.1.1
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd
github.com/docker/distribution v2.7.1+incompatible github.com/docker/distribution v2.8.0+incompatible
github.com/evanphx/json-patch v4.12.0+incompatible github.com/evanphx/json-patch v4.12.0+incompatible
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d
github.com/fatih/camelcase v1.0.0 github.com/fatih/camelcase v1.0.0

View File

@ -104,8 +104,8 @@ github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd h1:uVsMph
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.8.0+incompatible h1:l9EaZDICImO1ngI+uTifW+ZYvvz7fKISBAKpg+MbWbY=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=

View File

@ -28,7 +28,7 @@ import (
"strings" "strings"
"time" "time"
"k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config" kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
@ -69,7 +69,6 @@ var _ = SIGDescribe("Node Container Manager [Serial]", func() {
framework.ExpectNoError(runTest(f)) framework.ExpectNoError(runTest(f))
}) })
}) })
}) })
func expectFileValToEqual(filePath string, expectedValue, delta int64) error { func expectFileValToEqual(filePath string, expectedValue, delta int64) error {

View File

@ -1,26 +0,0 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
target

View File

@ -1,37 +0,0 @@
language: go
sudo: false
branches:
except:
- release
branches:
only:
- master
- travis
go:
- "1.11.x"
- tip
matrix:
allow_failures:
- go: tip
before_install:
- if [ -n "$GH_USER" ]; then git config --global github.user ${GH_USER}; fi;
- if [ -n "$GH_TOKEN" ]; then git config --global github.token ${GH_TOKEN}; fi;
- go get github.com/mattn/goveralls
before_script:
- make deps
script:
- make qa
after_failure:
- cat ./target/test/report.xml
after_success:
- if [ "$TRAVIS_GO_VERSION" = "1.11.1" ]; then $HOME/gopath/bin/goveralls -covermode=count -coverprofile=target/report/coverage.out -service=travis-ci; fi;

View File

@ -1,27 +0,0 @@
Copyright (c) 2014 Will Fitzgerald. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,93 +0,0 @@
# bitset
*Go language library to map between non-negative integers and boolean values*
[![Test](https://github.com/bits-and-blooms/bitset/workflows/Test/badge.svg)](https://github.com/willf/bitset/actions?query=workflow%3ATest)
[![Go Report Card](https://goreportcard.com/badge/github.com/willf/bitset)](https://goreportcard.com/report/github.com/willf/bitset)
[![PkgGoDev](https://pkg.go.dev/badge/github.com/bits-and-blooms/bitset?tab=doc)](https://pkg.go.dev/github.com/bits-and-blooms/bitset?tab=doc)
## Description
Package bitset implements bitsets, a mapping between non-negative integers and boolean values.
It should be more efficient than map[uint] bool.
It provides methods for setting, clearing, flipping, and testing individual integers.
But it also provides set intersection, union, difference, complement, and symmetric operations, as well as tests to check whether any, all, or no bits are set, and querying a bitset's current length and number of positive bits.
BitSets are expanded to the size of the largest set bit; the memory allocation is approximately Max bits, where Max is the largest set bit. BitSets are never shrunk. On creation, a hint can be given for the number of bits that will be used.
Many of the methods, including Set, Clear, and Flip, return a BitSet pointer, which allows for chaining.
### Example use:
```go
package main
import (
"fmt"
"math/rand"
"github.com/bits-and-blooms/bitset"
)
func main() {
fmt.Printf("Hello from BitSet!\n")
var b bitset.BitSet
// play some Go Fish
for i := 0; i < 100; i++ {
card1 := uint(rand.Intn(52))
card2 := uint(rand.Intn(52))
b.Set(card1)
if b.Test(card2) {
fmt.Println("Go Fish!")
}
b.Clear(card1)
}
// Chaining
b.Set(10).Set(11)
for i, e := b.NextSet(0); e; i, e = b.NextSet(i + 1) {
fmt.Println("The following bit is set:", i)
}
if b.Intersection(bitset.New(100).Set(10)).Count() == 1 {
fmt.Println("Intersection works.")
} else {
fmt.Println("Intersection doesn't work???")
}
}
```
As an alternative to BitSets, one should check out the 'big' package, which provides a (less set-theoretical) view of bitsets.
Package documentation is at: https://pkg.go.dev/github.com/bits-and-blooms/bitset?tab=doc
## Memory Usage
The memory usage of a bitset using N bits is at least N/8 bytes. The number of bits in a bitset is at least as large as one plus the greatest bit index you have accessed. Thus it is possible to run out of memory while using a bitset. If you have lots of bits, you might prefer compressed bitsets, like the [Roaring bitmaps](http://roaringbitmap.org) and its [Go implementation](https://github.com/RoaringBitmap/roaring).
## Implementation Note
Go 1.9 introduced a native `math/bits` library. We provide backward compatibility to Go 1.7, which might be removed.
It is possible that a later version will match the `math/bits` return signature for counts (which is `int`, rather than our library's `unit64`). If so, the version will be bumped.
## Installation
```bash
go get github.com/bits-and-blooms/bitset
```
## Contributing
If you wish to contribute to this project, please branch and issue a pull request against master ("[GitHub Flow](https://guides.github.com/introduction/flow/)")
## Running all tests
Before committing the code, please check if it passes tests, has adequate coverage, etc.
```bash
go test
go test -cover
```

View File

@ -1,39 +0,0 @@
# Go
# Build your Go project.
# Add steps that test, save build artifacts, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/go
trigger:
- master
pool:
vmImage: 'Ubuntu-16.04'
variables:
GOBIN: '$(GOPATH)/bin' # Go binaries path
GOROOT: '/usr/local/go1.11' # Go installation path
GOPATH: '$(system.defaultWorkingDirectory)/gopath' # Go workspace path
modulePath: '$(GOPATH)/src/github.com/$(build.repository.name)' # Path to the module's code
steps:
- script: |
mkdir -p '$(GOBIN)'
mkdir -p '$(GOPATH)/pkg'
mkdir -p '$(modulePath)'
shopt -s extglob
shopt -s dotglob
mv !(gopath) '$(modulePath)'
echo '##vso[task.prependpath]$(GOBIN)'
echo '##vso[task.prependpath]$(GOROOT)/bin'
displayName: 'Set up the Go workspace'
- script: |
go version
go get -v -t -d ./...
if [ -f Gopkg.toml ]; then
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure
fi
go build -v .
workingDirectory: '$(modulePath)'
displayName: 'Get dependencies, then build'

View File

@ -1,952 +0,0 @@
/*
Package bitset implements bitsets, a mapping
between non-negative integers and boolean values. It should be more
efficient than map[uint] bool.
It provides methods for setting, clearing, flipping, and testing
individual integers.
But it also provides set intersection, union, difference,
complement, and symmetric operations, as well as tests to
check whether any, all, or no bits are set, and querying a
bitset's current length and number of positive bits.
BitSets are expanded to the size of the largest set bit; the
memory allocation is approximately Max bits, where Max is
the largest set bit. BitSets are never shrunk. On creation,
a hint can be given for the number of bits that will be used.
Many of the methods, including Set,Clear, and Flip, return
a BitSet pointer, which allows for chaining.
Example use:
import "bitset"
var b BitSet
b.Set(10).Set(11)
if b.Test(1000) {
b.Clear(1000)
}
if B.Intersection(bitset.New(100).Set(10)).Count() > 1 {
fmt.Println("Intersection works.")
}
As an alternative to BitSets, one should check out the 'big' package,
which provides a (less set-theoretical) view of bitsets.
*/
package bitset
import (
"bufio"
"bytes"
"encoding/base64"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"io"
"strconv"
)
// the wordSize of a bit set
const wordSize = uint(64)
// log2WordSize is lg(wordSize)
const log2WordSize = uint(6)
// allBits has every bit set
const allBits uint64 = 0xffffffffffffffff
// default binary BigEndian
var binaryOrder binary.ByteOrder = binary.BigEndian
// default json encoding base64.URLEncoding
var base64Encoding = base64.URLEncoding
// Base64StdEncoding Marshal/Unmarshal BitSet with base64.StdEncoding(Default: base64.URLEncoding)
func Base64StdEncoding() { base64Encoding = base64.StdEncoding }
// LittleEndian Marshal/Unmarshal Binary as Little Endian(Default: binary.BigEndian)
func LittleEndian() { binaryOrder = binary.LittleEndian }
// A BitSet is a set of bits. The zero value of a BitSet is an empty set of length 0.
type BitSet struct {
length uint
set []uint64
}
// Error is used to distinguish errors (panics) generated in this package.
type Error string
// safeSet will fixup b.set to be non-nil and return the field value
func (b *BitSet) safeSet() []uint64 {
if b.set == nil {
b.set = make([]uint64, wordsNeeded(0))
}
return b.set
}
// From is a constructor used to create a BitSet from an array of integers
func From(buf []uint64) *BitSet {
return &BitSet{uint(len(buf)) * 64, buf}
}
// Bytes returns the bitset as array of integers
func (b *BitSet) Bytes() []uint64 {
return b.set
}
// wordsNeeded calculates the number of words needed for i bits
func wordsNeeded(i uint) int {
if i > (Cap() - wordSize + 1) {
return int(Cap() >> log2WordSize)
}
return int((i + (wordSize - 1)) >> log2WordSize)
}
// New creates a new BitSet with a hint that length bits will be required
func New(length uint) (bset *BitSet) {
defer func() {
if r := recover(); r != nil {
bset = &BitSet{
0,
make([]uint64, 0),
}
}
}()
bset = &BitSet{
length,
make([]uint64, wordsNeeded(length)),
}
return bset
}
// Cap returns the total possible capacity, or number of bits
func Cap() uint {
return ^uint(0)
}
// Len returns the number of bits in the BitSet.
// Note the difference to method Count, see example.
func (b *BitSet) Len() uint {
return b.length
}
// extendSetMaybe adds additional words to incorporate new bits if needed
func (b *BitSet) extendSetMaybe(i uint) {
if i >= b.length { // if we need more bits, make 'em
if i >= Cap() {
panic("You are exceeding the capacity")
}
nsize := wordsNeeded(i + 1)
if b.set == nil {
b.set = make([]uint64, nsize)
} else if cap(b.set) >= nsize {
b.set = b.set[:nsize] // fast resize
} else if len(b.set) < nsize {
newset := make([]uint64, nsize, 2*nsize) // increase capacity 2x
copy(newset, b.set)
b.set = newset
}
b.length = i + 1
}
}
// Test whether bit i is set.
func (b *BitSet) Test(i uint) bool {
if i >= b.length {
return false
}
return b.set[i>>log2WordSize]&(1<<(i&(wordSize-1))) != 0
}
// Set bit i to 1, the capacity of the bitset is automatically
// increased accordingly.
// If i>= Cap(), this function will panic.
// Warning: using a very large value for 'i'
// may lead to a memory shortage and a panic: the caller is responsible
// for providing sensible parameters in line with their memory capacity.
func (b *BitSet) Set(i uint) *BitSet {
b.extendSetMaybe(i)
b.set[i>>log2WordSize] |= 1 << (i & (wordSize - 1))
return b
}
// Clear bit i to 0
func (b *BitSet) Clear(i uint) *BitSet {
if i >= b.length {
return b
}
b.set[i>>log2WordSize] &^= 1 << (i & (wordSize - 1))
return b
}
// SetTo sets bit i to value.
// If i>= Cap(), this function will panic.
// Warning: using a very large value for 'i'
// may lead to a memory shortage and a panic: the caller is responsible
// for providing sensible parameters in line with their memory capacity.
func (b *BitSet) SetTo(i uint, value bool) *BitSet {
if value {
return b.Set(i)
}
return b.Clear(i)
}
// Flip bit at i.
// If i>= Cap(), this function will panic.
// Warning: using a very large value for 'i'
// may lead to a memory shortage and a panic: the caller is responsible
// for providing sensible parameters in line with their memory capacity.
func (b *BitSet) Flip(i uint) *BitSet {
if i >= b.length {
return b.Set(i)
}
b.set[i>>log2WordSize] ^= 1 << (i & (wordSize - 1))
return b
}
// FlipRange bit in [start, end).
// If end>= Cap(), this function will panic.
// Warning: using a very large value for 'end'
// may lead to a memory shortage and a panic: the caller is responsible
// for providing sensible parameters in line with their memory capacity.
func (b *BitSet) FlipRange(start, end uint) *BitSet {
if start >= end {
return b
}
b.extendSetMaybe(end - 1)
var startWord uint = start >> log2WordSize
var endWord uint = end >> log2WordSize
b.set[startWord] ^= ^(^uint64(0) << (start & (wordSize - 1)))
for i := startWord; i < endWord; i++ {
b.set[i] = ^b.set[i]
}
b.set[endWord] ^= ^uint64(0) >> (-end & (wordSize - 1))
return b
}
// Shrink shrinks BitSet so that the provided value is the last possible
// set value. It clears all bits > the provided index and reduces the size
// and length of the set.
//
// Note that the parameter value is not the new length in bits: it is the
// maximal value that can be stored in the bitset after the function call.
// The new length in bits is the parameter value + 1. Thus it is not possible
// to use this function to set the length to 0, the minimal value of the length
// after this function call is 1.
//
// A new slice is allocated to store the new bits, so you may see an increase in
// memory usage until the GC runs. Normally this should not be a problem, but if you
// have an extremely large BitSet its important to understand that the old BitSet will
// remain in memory until the GC frees it.
func (b *BitSet) Shrink(lastbitindex uint) *BitSet {
length := lastbitindex + 1
idx := wordsNeeded(length)
if idx > len(b.set) {
return b
}
shrunk := make([]uint64, idx)
copy(shrunk, b.set[:idx])
b.set = shrunk
b.length = length
b.set[idx-1] &= (allBits >> (uint64(64) - uint64(length&(wordSize-1))))
return b
}
// Compact shrinks BitSet to so that we preserve all set bits, while minimizing
// memory usage. Compact calls Shrink.
func (b *BitSet) Compact() *BitSet {
idx := len(b.set) - 1
for ; idx >= 0 && b.set[idx] == 0; idx-- {
}
newlength := uint((idx + 1) << log2WordSize)
if newlength >= b.length {
return b // nothing to do
}
if newlength > 0 {
return b.Shrink(newlength - 1)
}
// We preserve one word
return b.Shrink(63)
}
// InsertAt takes an index which indicates where a bit should be
// inserted. Then it shifts all the bits in the set to the left by 1, starting
// from the given index position, and sets the index position to 0.
//
// Depending on the size of your BitSet, and where you are inserting the new entry,
// this method could be extremely slow and in some cases might cause the entire BitSet
// to be recopied.
func (b *BitSet) InsertAt(idx uint) *BitSet {
insertAtElement := (idx >> log2WordSize)
// if length of set is a multiple of wordSize we need to allocate more space first
if b.isLenExactMultiple() {
b.set = append(b.set, uint64(0))
}
var i uint
for i = uint(len(b.set) - 1); i > insertAtElement; i-- {
// all elements above the position where we want to insert can simply by shifted
b.set[i] <<= 1
// we take the most significant bit of the previous element and set it as
// the least significant bit of the current element
b.set[i] |= (b.set[i-1] & 0x8000000000000000) >> 63
}
// generate a mask to extract the data that we need to shift left
// within the element where we insert a bit
dataMask := ^(uint64(1)<<uint64(idx&(wordSize-1)) - 1)
// extract that data that we'll shift
data := b.set[i] & dataMask
// set the positions of the data mask to 0 in the element where we insert
b.set[i] &= ^dataMask
// shift data mask to the left and insert its data to the slice element
b.set[i] |= data << 1
// add 1 to length of BitSet
b.length++
return b
}
// String creates a string representation of the Bitmap
func (b *BitSet) String() string {
// follows code from https://github.com/RoaringBitmap/roaring
var buffer bytes.Buffer
start := []byte("{")
buffer.Write(start)
counter := 0
i, e := b.NextSet(0)
for e {
counter = counter + 1
// to avoid exhausting the memory
if counter > 0x40000 {
buffer.WriteString("...")
break
}
buffer.WriteString(strconv.FormatInt(int64(i), 10))
i, e = b.NextSet(i + 1)
if e {
buffer.WriteString(",")
}
}
buffer.WriteString("}")
return buffer.String()
}
// DeleteAt deletes the bit at the given index position from
// within the bitset
// All the bits residing on the left of the deleted bit get
// shifted right by 1
// The running time of this operation may potentially be
// relatively slow, O(length)
func (b *BitSet) DeleteAt(i uint) *BitSet {
// the index of the slice element where we'll delete a bit
deleteAtElement := i >> log2WordSize
// generate a mask for the data that needs to be shifted right
// within that slice element that gets modified
dataMask := ^((uint64(1) << (i & (wordSize - 1))) - 1)
// extract the data that we'll shift right from the slice element
data := b.set[deleteAtElement] & dataMask
// set the masked area to 0 while leaving the rest as it is
b.set[deleteAtElement] &= ^dataMask
// shift the previously extracted data to the right and then
// set it in the previously masked area
b.set[deleteAtElement] |= (data >> 1) & dataMask
// loop over all the consecutive slice elements to copy each
// lowest bit into the highest position of the previous element,
// then shift the entire content to the right by 1
for i := int(deleteAtElement) + 1; i < len(b.set); i++ {
b.set[i-1] |= (b.set[i] & 1) << 63
b.set[i] >>= 1
}
b.length = b.length - 1
return b
}
// NextSet returns the next bit set from the specified index,
// including possibly the current index
// along with an error code (true = valid, false = no set bit found)
// for i,e := v.NextSet(0); e; i,e = v.NextSet(i + 1) {...}
//
// Users concerned with performance may want to use NextSetMany to
// retrieve several values at once.
func (b *BitSet) NextSet(i uint) (uint, bool) {
x := int(i >> log2WordSize)
if x >= len(b.set) {
return 0, false
}
w := b.set[x]
w = w >> (i & (wordSize - 1))
if w != 0 {
return i + trailingZeroes64(w), true
}
x = x + 1
for x < len(b.set) {
if b.set[x] != 0 {
return uint(x)*wordSize + trailingZeroes64(b.set[x]), true
}
x = x + 1
}
return 0, false
}
// NextSetMany returns many next bit sets from the specified index,
// including possibly the current index and up to cap(buffer).
// If the returned slice has len zero, then no more set bits were found
//
// buffer := make([]uint, 256) // this should be reused
// j := uint(0)
// j, buffer = bitmap.NextSetMany(j, buffer)
// for ; len(buffer) > 0; j, buffer = bitmap.NextSetMany(j,buffer) {
// for k := range buffer {
// do something with buffer[k]
// }
// j += 1
// }
//
//
// It is possible to retrieve all set bits as follow:
//
// indices := make([]uint, bitmap.Count())
// bitmap.NextSetMany(0, indices)
//
// However if bitmap.Count() is large, it might be preferable to
// use several calls to NextSetMany, for performance reasons.
func (b *BitSet) NextSetMany(i uint, buffer []uint) (uint, []uint) {
myanswer := buffer
capacity := cap(buffer)
x := int(i >> log2WordSize)
if x >= len(b.set) || capacity == 0 {
return 0, myanswer[:0]
}
skip := i & (wordSize - 1)
word := b.set[x] >> skip
myanswer = myanswer[:capacity]
size := int(0)
for word != 0 {
r := trailingZeroes64(word)
t := word & ((^word) + 1)
myanswer[size] = r + i
size++
if size == capacity {
goto End
}
word = word ^ t
}
x++
for idx, word := range b.set[x:] {
for word != 0 {
r := trailingZeroes64(word)
t := word & ((^word) + 1)
myanswer[size] = r + (uint(x+idx) << 6)
size++
if size == capacity {
goto End
}
word = word ^ t
}
}
End:
if size > 0 {
return myanswer[size-1], myanswer[:size]
}
return 0, myanswer[:0]
}
// NextClear returns the next clear bit from the specified index,
// including possibly the current index
// along with an error code (true = valid, false = no bit found i.e. all bits are set)
func (b *BitSet) NextClear(i uint) (uint, bool) {
x := int(i >> log2WordSize)
if x >= len(b.set) {
return 0, false
}
w := b.set[x]
w = w >> (i & (wordSize - 1))
wA := allBits >> (i & (wordSize - 1))
index := i + trailingZeroes64(^w)
if w != wA && index < b.length {
return index, true
}
x++
for x < len(b.set) {
index = uint(x)*wordSize + trailingZeroes64(^b.set[x])
if b.set[x] != allBits && index < b.length {
return index, true
}
x++
}
return 0, false
}
// ClearAll clears the entire BitSet
func (b *BitSet) ClearAll() *BitSet {
if b != nil && b.set != nil {
for i := range b.set {
b.set[i] = 0
}
}
return b
}
// wordCount returns the number of words used in a bit set
func (b *BitSet) wordCount() int {
return len(b.set)
}
// Clone this BitSet
func (b *BitSet) Clone() *BitSet {
c := New(b.length)
if b.set != nil { // Clone should not modify current object
copy(c.set, b.set)
}
return c
}
// Copy into a destination BitSet
// Returning the size of the destination BitSet
// like array copy
func (b *BitSet) Copy(c *BitSet) (count uint) {
if c == nil {
return
}
if b.set != nil { // Copy should not modify current object
copy(c.set, b.set)
}
count = c.length
if b.length < c.length {
count = b.length
}
return
}
// Count (number of set bits).
// Also known as "popcount" or "population count".
func (b *BitSet) Count() uint {
if b != nil && b.set != nil {
return uint(popcntSlice(b.set))
}
return 0
}
// Equal tests the equivalence of two BitSets.
// False if they are of different sizes, otherwise true
// only if all the same bits are set
func (b *BitSet) Equal(c *BitSet) bool {
if c == nil || b == nil {
return c == b
}
if b.length != c.length {
return false
}
if b.length == 0 { // if they have both length == 0, then could have nil set
return true
}
// testing for equality shoud not transform the bitset (no call to safeSet)
for p, v := range b.set {
if c.set[p] != v {
return false
}
}
return true
}
func panicIfNull(b *BitSet) {
if b == nil {
panic(Error("BitSet must not be null"))
}
}
// Difference of base set and other set
// This is the BitSet equivalent of &^ (and not)
func (b *BitSet) Difference(compare *BitSet) (result *BitSet) {
panicIfNull(b)
panicIfNull(compare)
result = b.Clone() // clone b (in case b is bigger than compare)
l := int(compare.wordCount())
if l > int(b.wordCount()) {
l = int(b.wordCount())
}
for i := 0; i < l; i++ {
result.set[i] = b.set[i] &^ compare.set[i]
}
return
}
// DifferenceCardinality computes the cardinality of the differnce
func (b *BitSet) DifferenceCardinality(compare *BitSet) uint {
panicIfNull(b)
panicIfNull(compare)
l := int(compare.wordCount())
if l > int(b.wordCount()) {
l = int(b.wordCount())
}
cnt := uint64(0)
cnt += popcntMaskSlice(b.set[:l], compare.set[:l])
cnt += popcntSlice(b.set[l:])
return uint(cnt)
}
// InPlaceDifference computes the difference of base set and other set
// This is the BitSet equivalent of &^ (and not)
func (b *BitSet) InPlaceDifference(compare *BitSet) {
panicIfNull(b)
panicIfNull(compare)
l := int(compare.wordCount())
if l > int(b.wordCount()) {
l = int(b.wordCount())
}
for i := 0; i < l; i++ {
b.set[i] &^= compare.set[i]
}
}
// Convenience function: return two bitsets ordered by
// increasing length. Note: neither can be nil
func sortByLength(a *BitSet, b *BitSet) (ap *BitSet, bp *BitSet) {
if a.length <= b.length {
ap, bp = a, b
} else {
ap, bp = b, a
}
return
}
// Intersection of base set and other set
// This is the BitSet equivalent of & (and)
func (b *BitSet) Intersection(compare *BitSet) (result *BitSet) {
panicIfNull(b)
panicIfNull(compare)
b, compare = sortByLength(b, compare)
result = New(b.length)
for i, word := range b.set {
result.set[i] = word & compare.set[i]
}
return
}
// IntersectionCardinality computes the cardinality of the union
func (b *BitSet) IntersectionCardinality(compare *BitSet) uint {
panicIfNull(b)
panicIfNull(compare)
b, compare = sortByLength(b, compare)
cnt := popcntAndSlice(b.set, compare.set)
return uint(cnt)
}
// InPlaceIntersection destructively computes the intersection of
// base set and the compare set.
// This is the BitSet equivalent of & (and)
func (b *BitSet) InPlaceIntersection(compare *BitSet) {
panicIfNull(b)
panicIfNull(compare)
l := int(compare.wordCount())
if l > int(b.wordCount()) {
l = int(b.wordCount())
}
for i := 0; i < l; i++ {
b.set[i] &= compare.set[i]
}
for i := l; i < len(b.set); i++ {
b.set[i] = 0
}
if compare.length > 0 {
b.extendSetMaybe(compare.length - 1)
}
}
// Union of base set and other set
// This is the BitSet equivalent of | (or)
func (b *BitSet) Union(compare *BitSet) (result *BitSet) {
panicIfNull(b)
panicIfNull(compare)
b, compare = sortByLength(b, compare)
result = compare.Clone()
for i, word := range b.set {
result.set[i] = word | compare.set[i]
}
return
}
// UnionCardinality computes the cardinality of the uniton of the base set
// and the compare set.
func (b *BitSet) UnionCardinality(compare *BitSet) uint {
panicIfNull(b)
panicIfNull(compare)
b, compare = sortByLength(b, compare)
cnt := popcntOrSlice(b.set, compare.set)
if len(compare.set) > len(b.set) {
cnt += popcntSlice(compare.set[len(b.set):])
}
return uint(cnt)
}
// InPlaceUnion creates the destructive union of base set and compare set.
// This is the BitSet equivalent of | (or).
func (b *BitSet) InPlaceUnion(compare *BitSet) {
panicIfNull(b)
panicIfNull(compare)
l := int(compare.wordCount())
if l > int(b.wordCount()) {
l = int(b.wordCount())
}
if compare.length > 0 {
b.extendSetMaybe(compare.length - 1)
}
for i := 0; i < l; i++ {
b.set[i] |= compare.set[i]
}
if len(compare.set) > l {
for i := l; i < len(compare.set); i++ {
b.set[i] = compare.set[i]
}
}
}
// SymmetricDifference of base set and other set
// This is the BitSet equivalent of ^ (xor)
func (b *BitSet) SymmetricDifference(compare *BitSet) (result *BitSet) {
panicIfNull(b)
panicIfNull(compare)
b, compare = sortByLength(b, compare)
// compare is bigger, so clone it
result = compare.Clone()
for i, word := range b.set {
result.set[i] = word ^ compare.set[i]
}
return
}
// SymmetricDifferenceCardinality computes the cardinality of the symmetric difference
func (b *BitSet) SymmetricDifferenceCardinality(compare *BitSet) uint {
panicIfNull(b)
panicIfNull(compare)
b, compare = sortByLength(b, compare)
cnt := popcntXorSlice(b.set, compare.set)
if len(compare.set) > len(b.set) {
cnt += popcntSlice(compare.set[len(b.set):])
}
return uint(cnt)
}
// InPlaceSymmetricDifference creates the destructive SymmetricDifference of base set and other set
// This is the BitSet equivalent of ^ (xor)
func (b *BitSet) InPlaceSymmetricDifference(compare *BitSet) {
panicIfNull(b)
panicIfNull(compare)
l := int(compare.wordCount())
if l > int(b.wordCount()) {
l = int(b.wordCount())
}
if compare.length > 0 {
b.extendSetMaybe(compare.length - 1)
}
for i := 0; i < l; i++ {
b.set[i] ^= compare.set[i]
}
if len(compare.set) > l {
for i := l; i < len(compare.set); i++ {
b.set[i] = compare.set[i]
}
}
}
// Is the length an exact multiple of word sizes?
func (b *BitSet) isLenExactMultiple() bool {
return b.length%wordSize == 0
}
// Clean last word by setting unused bits to 0
func (b *BitSet) cleanLastWord() {
if !b.isLenExactMultiple() {
b.set[len(b.set)-1] &= allBits >> (wordSize - b.length%wordSize)
}
}
// Complement computes the (local) complement of a biset (up to length bits)
func (b *BitSet) Complement() (result *BitSet) {
panicIfNull(b)
result = New(b.length)
for i, word := range b.set {
result.set[i] = ^word
}
result.cleanLastWord()
return
}
// All returns true if all bits are set, false otherwise. Returns true for
// empty sets.
func (b *BitSet) All() bool {
panicIfNull(b)
return b.Count() == b.length
}
// None returns true if no bit is set, false otherwise. Returns true for
// empty sets.
func (b *BitSet) None() bool {
panicIfNull(b)
if b != nil && b.set != nil {
for _, word := range b.set {
if word > 0 {
return false
}
}
return true
}
return true
}
// Any returns true if any bit is set, false otherwise
func (b *BitSet) Any() bool {
panicIfNull(b)
return !b.None()
}
// IsSuperSet returns true if this is a superset of the other set
func (b *BitSet) IsSuperSet(other *BitSet) bool {
for i, e := other.NextSet(0); e; i, e = other.NextSet(i + 1) {
if !b.Test(i) {
return false
}
}
return true
}
// IsStrictSuperSet returns true if this is a strict superset of the other set
func (b *BitSet) IsStrictSuperSet(other *BitSet) bool {
return b.Count() > other.Count() && b.IsSuperSet(other)
}
// DumpAsBits dumps a bit set as a string of bits
func (b *BitSet) DumpAsBits() string {
if b.set == nil {
return "."
}
buffer := bytes.NewBufferString("")
i := len(b.set) - 1
for ; i >= 0; i-- {
fmt.Fprintf(buffer, "%064b.", b.set[i])
}
return buffer.String()
}
// BinaryStorageSize returns the binary storage requirements
func (b *BitSet) BinaryStorageSize() int {
return binary.Size(uint64(0)) + binary.Size(b.set)
}
// WriteTo writes a BitSet to a stream
func (b *BitSet) WriteTo(stream io.Writer) (int64, error) {
length := uint64(b.length)
// Write length
err := binary.Write(stream, binaryOrder, length)
if err != nil {
return 0, err
}
// Write set
err = binary.Write(stream, binaryOrder, b.set)
return int64(b.BinaryStorageSize()), err
}
// ReadFrom reads a BitSet from a stream written using WriteTo
func (b *BitSet) ReadFrom(stream io.Reader) (int64, error) {
var length uint64
// Read length first
err := binary.Read(stream, binaryOrder, &length)
if err != nil {
return 0, err
}
newset := New(uint(length))
if uint64(newset.length) != length {
return 0, errors.New("unmarshalling error: type mismatch")
}
// Read remaining bytes as set
err = binary.Read(stream, binaryOrder, newset.set)
if err != nil {
return 0, err
}
*b = *newset
return int64(b.BinaryStorageSize()), nil
}
// MarshalBinary encodes a BitSet into a binary form and returns the result.
func (b *BitSet) MarshalBinary() ([]byte, error) {
var buf bytes.Buffer
writer := bufio.NewWriter(&buf)
_, err := b.WriteTo(writer)
if err != nil {
return []byte{}, err
}
err = writer.Flush()
return buf.Bytes(), err
}
// UnmarshalBinary decodes the binary form generated by MarshalBinary.
func (b *BitSet) UnmarshalBinary(data []byte) error {
buf := bytes.NewReader(data)
reader := bufio.NewReader(buf)
_, err := b.ReadFrom(reader)
return err
}
// MarshalJSON marshals a BitSet as a JSON structure
func (b *BitSet) MarshalJSON() ([]byte, error) {
buffer := bytes.NewBuffer(make([]byte, 0, b.BinaryStorageSize()))
_, err := b.WriteTo(buffer)
if err != nil {
return nil, err
}
// URLEncode all bytes
return json.Marshal(base64Encoding.EncodeToString(buffer.Bytes()))
}
// UnmarshalJSON unmarshals a BitSet from JSON created using MarshalJSON
func (b *BitSet) UnmarshalJSON(data []byte) error {
// Unmarshal as string
var s string
err := json.Unmarshal(data, &s)
if err != nil {
return err
}
// URLDecode string
buf, err := base64Encoding.DecodeString(s)
if err != nil {
return err
}
_, err = b.ReadFrom(bytes.NewReader(buf))
return err
}

View File

@ -1,3 +0,0 @@
module github.com/bits-and-blooms/bitset
go 1.14

View File

View File

@ -1,53 +0,0 @@
package bitset
// bit population count, take from
// https://code.google.com/p/go/issues/detail?id=4988#c11
// credit: https://code.google.com/u/arnehormann/
func popcount(x uint64) (n uint64) {
x -= (x >> 1) & 0x5555555555555555
x = (x>>2)&0x3333333333333333 + x&0x3333333333333333
x += x >> 4
x &= 0x0f0f0f0f0f0f0f0f
x *= 0x0101010101010101
return x >> 56
}
func popcntSliceGo(s []uint64) uint64 {
cnt := uint64(0)
for _, x := range s {
cnt += popcount(x)
}
return cnt
}
func popcntMaskSliceGo(s, m []uint64) uint64 {
cnt := uint64(0)
for i := range s {
cnt += popcount(s[i] &^ m[i])
}
return cnt
}
func popcntAndSliceGo(s, m []uint64) uint64 {
cnt := uint64(0)
for i := range s {
cnt += popcount(s[i] & m[i])
}
return cnt
}
func popcntOrSliceGo(s, m []uint64) uint64 {
cnt := uint64(0)
for i := range s {
cnt += popcount(s[i] | m[i])
}
return cnt
}
func popcntXorSliceGo(s, m []uint64) uint64 {
cnt := uint64(0)
for i := range s {
cnt += popcount(s[i] ^ m[i])
}
return cnt
}

View File

@ -1,45 +0,0 @@
// +build go1.9
package bitset
import "math/bits"
func popcntSlice(s []uint64) uint64 {
var cnt int
for _, x := range s {
cnt += bits.OnesCount64(x)
}
return uint64(cnt)
}
func popcntMaskSlice(s, m []uint64) uint64 {
var cnt int
for i := range s {
cnt += bits.OnesCount64(s[i] &^ m[i])
}
return uint64(cnt)
}
func popcntAndSlice(s, m []uint64) uint64 {
var cnt int
for i := range s {
cnt += bits.OnesCount64(s[i] & m[i])
}
return uint64(cnt)
}
func popcntOrSlice(s, m []uint64) uint64 {
var cnt int
for i := range s {
cnt += bits.OnesCount64(s[i] | m[i])
}
return uint64(cnt)
}
func popcntXorSlice(s, m []uint64) uint64 {
var cnt int
for i := range s {
cnt += bits.OnesCount64(s[i] ^ m[i])
}
return uint64(cnt)
}

View File

@ -1,68 +0,0 @@
// +build !go1.9
// +build amd64,!appengine
package bitset
// *** the following functions are defined in popcnt_amd64.s
//go:noescape
func hasAsm() bool
// useAsm is a flag used to select the GO or ASM implementation of the popcnt function
var useAsm = hasAsm()
//go:noescape
func popcntSliceAsm(s []uint64) uint64
//go:noescape
func popcntMaskSliceAsm(s, m []uint64) uint64
//go:noescape
func popcntAndSliceAsm(s, m []uint64) uint64
//go:noescape
func popcntOrSliceAsm(s, m []uint64) uint64
//go:noescape
func popcntXorSliceAsm(s, m []uint64) uint64
func popcntSlice(s []uint64) uint64 {
if useAsm {
return popcntSliceAsm(s)
}
return popcntSliceGo(s)
}
func popcntMaskSlice(s, m []uint64) uint64 {
if useAsm {
return popcntMaskSliceAsm(s, m)
}
return popcntMaskSliceGo(s, m)
}
func popcntAndSlice(s, m []uint64) uint64 {
if useAsm {
return popcntAndSliceAsm(s, m)
}
return popcntAndSliceGo(s, m)
}
func popcntOrSlice(s, m []uint64) uint64 {
if useAsm {
return popcntOrSliceAsm(s, m)
}
return popcntOrSliceGo(s, m)
}
func popcntXorSlice(s, m []uint64) uint64 {
if useAsm {
return popcntXorSliceAsm(s, m)
}
return popcntXorSliceGo(s, m)
}

View File

@ -1,104 +0,0 @@
// +build !go1.9
// +build amd64,!appengine
TEXT ·hasAsm(SB),4,$0-1
MOVQ $1, AX
CPUID
SHRQ $23, CX
ANDQ $1, CX
MOVB CX, ret+0(FP)
RET
#define POPCNTQ_DX_DX BYTE $0xf3; BYTE $0x48; BYTE $0x0f; BYTE $0xb8; BYTE $0xd2
TEXT ·popcntSliceAsm(SB),4,$0-32
XORQ AX, AX
MOVQ s+0(FP), SI
MOVQ s_len+8(FP), CX
TESTQ CX, CX
JZ popcntSliceEnd
popcntSliceLoop:
BYTE $0xf3; BYTE $0x48; BYTE $0x0f; BYTE $0xb8; BYTE $0x16 // POPCNTQ (SI), DX
ADDQ DX, AX
ADDQ $8, SI
LOOP popcntSliceLoop
popcntSliceEnd:
MOVQ AX, ret+24(FP)
RET
TEXT ·popcntMaskSliceAsm(SB),4,$0-56
XORQ AX, AX
MOVQ s+0(FP), SI
MOVQ s_len+8(FP), CX
TESTQ CX, CX
JZ popcntMaskSliceEnd
MOVQ m+24(FP), DI
popcntMaskSliceLoop:
MOVQ (DI), DX
NOTQ DX
ANDQ (SI), DX
POPCNTQ_DX_DX
ADDQ DX, AX
ADDQ $8, SI
ADDQ $8, DI
LOOP popcntMaskSliceLoop
popcntMaskSliceEnd:
MOVQ AX, ret+48(FP)
RET
TEXT ·popcntAndSliceAsm(SB),4,$0-56
XORQ AX, AX
MOVQ s+0(FP), SI
MOVQ s_len+8(FP), CX
TESTQ CX, CX
JZ popcntAndSliceEnd
MOVQ m+24(FP), DI
popcntAndSliceLoop:
MOVQ (DI), DX
ANDQ (SI), DX
POPCNTQ_DX_DX
ADDQ DX, AX
ADDQ $8, SI
ADDQ $8, DI
LOOP popcntAndSliceLoop
popcntAndSliceEnd:
MOVQ AX, ret+48(FP)
RET
TEXT ·popcntOrSliceAsm(SB),4,$0-56
XORQ AX, AX
MOVQ s+0(FP), SI
MOVQ s_len+8(FP), CX
TESTQ CX, CX
JZ popcntOrSliceEnd
MOVQ m+24(FP), DI
popcntOrSliceLoop:
MOVQ (DI), DX
ORQ (SI), DX
POPCNTQ_DX_DX
ADDQ DX, AX
ADDQ $8, SI
ADDQ $8, DI
LOOP popcntOrSliceLoop
popcntOrSliceEnd:
MOVQ AX, ret+48(FP)
RET
TEXT ·popcntXorSliceAsm(SB),4,$0-56
XORQ AX, AX
MOVQ s+0(FP), SI
MOVQ s_len+8(FP), CX
TESTQ CX, CX
JZ popcntXorSliceEnd
MOVQ m+24(FP), DI
popcntXorSliceLoop:
MOVQ (DI), DX
XORQ (SI), DX
POPCNTQ_DX_DX
ADDQ DX, AX
ADDQ $8, SI
ADDQ $8, DI
LOOP popcntXorSliceLoop
popcntXorSliceEnd:
MOVQ AX, ret+48(FP)
RET

View File

@ -1,24 +0,0 @@
// +build !go1.9
// +build !amd64 appengine
package bitset
func popcntSlice(s []uint64) uint64 {
return popcntSliceGo(s)
}
func popcntMaskSlice(s, m []uint64) uint64 {
return popcntMaskSliceGo(s, m)
}
func popcntAndSlice(s, m []uint64) uint64 {
return popcntAndSliceGo(s, m)
}
func popcntOrSlice(s, m []uint64) uint64 {
return popcntOrSliceGo(s, m)
}
func popcntXorSlice(s, m []uint64) uint64 {
return popcntXorSliceGo(s, m)
}

View File

@ -1,14 +0,0 @@
// +build !go1.9
package bitset
var deBruijn = [...]byte{
0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4,
62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5,
63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11,
54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6,
}
func trailingZeroes64(v uint64) uint {
return uint(deBruijn[((v&-v)*0x03f79d71b4ca8b09)>>58])
}

View File

@ -1,9 +0,0 @@
// +build go1.9
package bitset
import "math/bits"
func trailingZeroes64(v uint64) uint {
return uint(bits.TrailingZeros64(v))
}

View File

@ -1,6 +1,6 @@
test/test test/test
test/test.coverage
test/piggie/piggie test/piggie/piggie
test/phaul test/phaul/phaul
test/phaul/phaul.coverage
image image
rpc/rpc.proto
stats/stats.proto

View File

@ -1,5 +1,12 @@
SHELL = /bin/bash
GO ?= go GO ?= go
CC ?= gcc CC ?= gcc
COVERAGE_PATH ?= $(shell pwd)/.coverage
CRIU_FEATURE_MEM_TRACK = $(shell if criu check --feature mem_dirty_track > /dev/null; then echo 1; else echo 0; fi)
CRIU_FEATURE_LAZY_PAGES = $(shell if criu check --feature uffd-noncoop > /dev/null; then echo 1; else echo 0; fi)
CRIU_FEATURE_PIDFD_STORE = $(shell if criu check --feature pidfd_store > /dev/null; then echo 1; else echo 0; fi)
export CRIU_FEATURE_MEM_TRACK CRIU_FEATURE_LAZY_PAGES CRIU_FEATURE_PIDFD_STORE
all: build test phaul-test all: build test phaul-test
@ -9,13 +16,15 @@ lint:
build: build:
$(GO) build -v ./... $(GO) build -v ./...
TEST_BINARIES := test/test test/piggie/piggie test/phaul/phaul TEST_PAYLOAD := test/piggie/piggie
TEST_BINARIES := test/test $(TEST_PAYLOAD) test/phaul/phaul
COVERAGE_BINARIES := test/test.coverage test/phaul/phaul.coverage
test-bin: $(TEST_BINARIES) test-bin: $(TEST_BINARIES)
test/piggie/piggie: test/piggie/piggie.c test/piggie/piggie: test/piggie/piggie.c
$(CC) $^ -o $@ $(CC) $^ -o $@
test/test: test/*.go test/test: test/main.go
$(GO) build -v -o $@ $^ $(GO) build -v -o $@ $^
test: $(TEST_BINARIES) test: $(TEST_BINARIES)
@ -27,7 +36,7 @@ test: $(TEST_BINARIES)
} }
rm -rf image rm -rf image
test/phaul/phaul: test/phaul/*.go test/phaul/phaul: test/phaul/main.go
$(GO) build -v -o $@ $^ $(GO) build -v -o $@ $^
phaul-test: $(TEST_BINARIES) phaul-test: $(TEST_BINARIES)
@ -37,10 +46,41 @@ phaul-test: $(TEST_BINARIES)
pkill -9 piggie; \ pkill -9 piggie; \
} }
test/test.coverage: test/*.go
$(GO) test \
-covermode=count \
-coverpkg=./... \
-mod=vendor \
-tags coverage \
-buildmode=pie -c -o $@ $^
test/phaul/phaul.coverage: test/phaul/*.go
$(GO) test \
-covermode=count \
-coverpkg=./... \
-mod=vendor \
-tags coverage \
-buildmode=pie -c -o $@ $^
coverage: $(COVERAGE_BINARIES) $(TEST_PAYLOAD)
mkdir -p $(COVERAGE_PATH)
mkdir -p image
PID=$$(test/piggie/piggie) && { \
test/test.coverage -test.coverprofile=coverprofile.integration.$$RANDOM -test.outputdir=${COVERAGE_PATH} COVERAGE dump $$PID image && \
test/test.coverage -test.coverprofile=coverprofile.integration.$$RANDOM -test.outputdir=${COVERAGE_PATH} COVERAGE restore image; \
pkill -9 piggie; \
}
rm -rf image
PID=$$(test/piggie/piggie) && { \
test/phaul/phaul.coverage -test.coverprofile=coverprofile.integration.$$RANDOM -test.outputdir=${COVERAGE_PATH} COVERAGE $$PID; \
pkill -9 piggie; \
}
echo "mode: set" > .coverage/coverage.out && cat .coverage/coverprofile* | \
grep -v mode: | sort -r | awk '{if($$1 != last) {print $$0;last=$$1}}' >> .coverage/coverage.out
clean: clean:
@rm -f $(TEST_BINARIES) @rm -f $(TEST_BINARIES) $(COVERAGE_BINARIES) codecov
@rm -rf image @rm -rf image $(COVERAGE_PATH)
@rm -f rpc/rpc.proto stats/stats.proto
rpc/rpc.proto: rpc/rpc.proto:
curl -sSL https://raw.githubusercontent.com/checkpoint-restore/criu/master/images/rpc.proto -o $@ curl -sSL https://raw.githubusercontent.com/checkpoint-restore/criu/master/images/rpc.proto -o $@
@ -49,9 +89,19 @@ stats/stats.proto:
curl -sSL https://raw.githubusercontent.com/checkpoint-restore/criu/master/images/stats.proto -o $@ curl -sSL https://raw.githubusercontent.com/checkpoint-restore/criu/master/images/stats.proto -o $@
rpc/rpc.pb.go: rpc/rpc.proto rpc/rpc.pb.go: rpc/rpc.proto
protoc --go_out=. $^ protoc --go_out=. --go_opt=M$^=rpc/ $^
stats/stats.pb.go: stats/stats.proto stats/stats.pb.go: stats/stats.proto
protoc --go_out=. $^ protoc --go_out=. --go_opt=M$^=stats/ $^
.PHONY: build test phaul-test test-bin clean lint vendor:
GO111MODULE=on $(GO) mod tidy
GO111MODULE=on $(GO) mod vendor
GO111MODULE=on $(GO) mod verify
codecov:
curl -Os https://uploader.codecov.io/latest/linux/codecov
chmod +x codecov
./codecov -f '.coverage/coverage.out'
.PHONY: build test phaul-test test-bin clean lint vendor coverage codecov

View File

@ -16,7 +16,7 @@ The following example would print the version of CRIU:
import ( import (
"log" "log"
"github.com/checkpoint/restore/go-criu/v5" "github.com/checkpoint-restore/go-criu/v5"
) )
func main() { func main() {
@ -50,6 +50,7 @@ The following table shows the relation between go-criu and criu versions:
| Major version | Latest release | CRIU version | | Major version | Latest release | CRIU version |
| -------------- | -------------- | ------------ | | -------------- | -------------- | ------------ |
| v5             | 5.2.0         | 3.16         |
| v5             | 5.0.0         | 3.15         | | v5             | 5.0.0         | 3.15         |
| v4             | 4.1.0         | 3.14         | | v4             | 4.1.0         | 3.14         |

View File

@ -0,0 +1,45 @@
package criu
import (
"fmt"
"github.com/checkpoint-restore/go-criu/v5/rpc"
)
// Feature checking in go-criu is based on the libcriu feature checking function.
// Feature checking allows the user to check if CRIU supports
// certain features. There are CRIU features which do not depend
// on the version of CRIU but on kernel features or architecture.
//
// One example is memory tracking. Memory tracking can be disabled
// in the kernel or there are architectures which do not support
// it (aarch64 for example). By using the feature check a libcriu
// user can easily query CRIU if a certain feature is available.
//
// The features which should be checked can be marked in the
// structure 'struct criu_feature_check'. Each structure member
// that is set to true will result in CRIU checking for the
// availability of that feature in the current combination of
// CRIU/kernel/architecture.
//
// Available features will be set to true when the function
// returns successfully. Missing features will be set to false.
func (c *Criu) FeatureCheck(features *rpc.CriuFeatures) (*rpc.CriuFeatures, error) {
resp, err := c.doSwrkWithResp(
rpc.CriuReqType_FEATURE_CHECK,
nil,
nil,
features,
)
if err != nil {
return nil, err
}
if resp.GetType() != rpc.CriuReqType_FEATURE_CHECK {
return nil, fmt.Errorf("Unexpected CRIU RPC response")
}
return features, nil
}

View File

@ -3,7 +3,6 @@ module github.com/checkpoint-restore/go-criu/v5
go 1.13 go 1.13
require ( require (
github.com/golang/protobuf v1.4.3
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c
google.golang.org/protobuf v1.23.0 google.golang.org/protobuf v1.27.1
) )

View File

@ -1,22 +1,10 @@
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=

View File

@ -87,19 +87,19 @@ func (c *Criu) sendAndRecv(reqB []byte) ([]byte, int, error) {
} }
func (c *Criu) doSwrk(reqType rpc.CriuReqType, opts *rpc.CriuOpts, nfy Notify) error { func (c *Criu) doSwrk(reqType rpc.CriuReqType, opts *rpc.CriuOpts, nfy Notify) error {
resp, err := c.doSwrkWithResp(reqType, opts, nfy) resp, err := c.doSwrkWithResp(reqType, opts, nfy, nil)
if err != nil { if err != nil {
return err return err
} }
respType := resp.GetType() respType := resp.GetType()
if respType != reqType { if respType != reqType {
return errors.New("unexpected responce") return errors.New("unexpected CRIU RPC response")
} }
return nil return nil
} }
func (c *Criu) doSwrkWithResp(reqType rpc.CriuReqType, opts *rpc.CriuOpts, nfy Notify) (*rpc.CriuResp, error) { func (c *Criu) doSwrkWithResp(reqType rpc.CriuReqType, opts *rpc.CriuOpts, nfy Notify, features *rpc.CriuFeatures) (*rpc.CriuResp, error) {
var resp *rpc.CriuResp var resp *rpc.CriuResp
req := rpc.CriuReq{ req := rpc.CriuReq{
@ -111,6 +111,10 @@ func (c *Criu) doSwrkWithResp(reqType rpc.CriuReqType, opts *rpc.CriuOpts, nfy N
opts.NotifyScripts = proto.Bool(true) opts.NotifyScripts = proto.Bool(true)
} }
if features != nil {
req.Features = features
}
if c.swrkCmd == nil { if c.swrkCmd == nil {
err := c.Prepare() err := c.Prepare()
if err != nil { if err != nil {
@ -209,7 +213,7 @@ func (c *Criu) StartPageServer(opts *rpc.CriuOpts) error {
// StartPageServerChld starts the page server and returns PID and port // StartPageServerChld starts the page server and returns PID and port
func (c *Criu) StartPageServerChld(opts *rpc.CriuOpts) (int, int, error) { func (c *Criu) StartPageServerChld(opts *rpc.CriuOpts) (int, int, error) {
resp, err := c.doSwrkWithResp(rpc.CriuReqType_PAGE_SERVER_CHLD, opts, nil) resp, err := c.doSwrkWithResp(rpc.CriuReqType_PAGE_SERVER_CHLD, opts, nil, nil)
if err != nil { if err != nil {
return 0, 0, err return 0, 0, err
} }
@ -220,7 +224,7 @@ func (c *Criu) StartPageServerChld(opts *rpc.CriuOpts) (int, int, error) {
// GetCriuVersion executes the VERSION RPC call and returns the version // GetCriuVersion executes the VERSION RPC call and returns the version
// as an integer. Major * 10000 + Minor * 100 + SubLevel // as an integer. Major * 10000 + Minor * 100 + SubLevel
func (c *Criu) GetCriuVersion() (int, error) { func (c *Criu) GetCriuVersion() (int, error) {
resp, err := c.doSwrkWithResp(rpc.CriuReqType_VERSION, nil, nil) resp, err := c.doSwrkWithResp(rpc.CriuReqType_VERSION, nil, nil, nil)
if err != nil { if err != nil {
return 0, err return 0, err
} }

View File

@ -1,13 +1,14 @@
// SPDX-License-Identifier: MIT
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.25.0 // protoc-gen-go v1.27.1
// protoc v3.12.4 // protoc v3.12.4
// source: rpc/rpc.proto // source: rpc/rpc.proto
package rpc package rpc
import ( import (
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect" reflect "reflect"
@ -21,10 +22,6 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
) )
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type CriuCgMode int32 type CriuCgMode int32
const ( const (
@ -706,7 +703,9 @@ type CriuOpts struct {
Tls *bool `protobuf:"varint,58,opt,name=tls" json:"tls,omitempty"` Tls *bool `protobuf:"varint,58,opt,name=tls" json:"tls,omitempty"`
TlsNoCnVerify *bool `protobuf:"varint,59,opt,name=tls_no_cn_verify,json=tlsNoCnVerify" json:"tls_no_cn_verify,omitempty"` TlsNoCnVerify *bool `protobuf:"varint,59,opt,name=tls_no_cn_verify,json=tlsNoCnVerify" json:"tls_no_cn_verify,omitempty"`
CgroupYard *string `protobuf:"bytes,60,opt,name=cgroup_yard,json=cgroupYard" json:"cgroup_yard,omitempty"` CgroupYard *string `protobuf:"bytes,60,opt,name=cgroup_yard,json=cgroupYard" json:"cgroup_yard,omitempty"`
PreDumpMode *CriuPreDumpMode `protobuf:"varint,61,opt,name=pre_dump_mode,json=preDumpMode,enum=CriuPreDumpMode,def=1" json:"pre_dump_mode,omitempty"` // optional bool check_mounts = 128; PreDumpMode *CriuPreDumpMode `protobuf:"varint,61,opt,name=pre_dump_mode,json=preDumpMode,enum=CriuPreDumpMode,def=1" json:"pre_dump_mode,omitempty"`
PidfdStoreSk *int32 `protobuf:"varint,62,opt,name=pidfd_store_sk,json=pidfdStoreSk" json:"pidfd_store_sk,omitempty"`
LsmMountContext *string `protobuf:"bytes,63,opt,name=lsm_mount_context,json=lsmMountContext" json:"lsm_mount_context,omitempty"` // optional bool check_mounts = 128;
} }
// Default values for CriuOpts fields. // Default values for CriuOpts fields.
@ -1169,6 +1168,20 @@ func (x *CriuOpts) GetPreDumpMode() CriuPreDumpMode {
return Default_CriuOpts_PreDumpMode return Default_CriuOpts_PreDumpMode
} }
func (x *CriuOpts) GetPidfdStoreSk() int32 {
if x != nil && x.PidfdStoreSk != nil {
return *x.PidfdStoreSk
}
return 0
}
func (x *CriuOpts) GetLsmMountContext() string {
if x != nil && x.LsmMountContext != nil {
return *x.LsmMountContext
}
return ""
}
type CriuDumpResp struct { type CriuDumpResp struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -1326,8 +1339,9 @@ type CriuFeatures struct {
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
MemTrack *bool `protobuf:"varint,1,opt,name=mem_track,json=memTrack" json:"mem_track,omitempty"` MemTrack *bool `protobuf:"varint,1,opt,name=mem_track,json=memTrack" json:"mem_track,omitempty"`
LazyPages *bool `protobuf:"varint,2,opt,name=lazy_pages,json=lazyPages" json:"lazy_pages,omitempty"` LazyPages *bool `protobuf:"varint,2,opt,name=lazy_pages,json=lazyPages" json:"lazy_pages,omitempty"`
PidfdStore *bool `protobuf:"varint,3,opt,name=pidfd_store,json=pidfdStore" json:"pidfd_store,omitempty"`
} }
func (x *CriuFeatures) Reset() { func (x *CriuFeatures) Reset() {
@ -1376,6 +1390,13 @@ func (x *CriuFeatures) GetLazyPages() bool {
return false return false
} }
func (x *CriuFeatures) GetPidfdStore() bool {
if x != nil && x.PidfdStore != nil {
return *x.PidfdStore
}
return false
}
type CriuReq struct { type CriuReq struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -1718,7 +1739,7 @@ var file_rpc_rpc_proto_rawDesc = []byte{
0x52, 0x04, 0x63, 0x74, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x52, 0x04, 0x63, 0x74, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02,
0x20, 0x02, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x1f, 0x0a, 0x07, 0x75, 0x6e, 0x20, 0x02, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x1f, 0x0a, 0x07, 0x75, 0x6e,
0x69, 0x78, 0x5f, 0x73, 0x6b, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x69, 0x78, 0x5f, 0x73, 0x6b, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01,
0x20, 0x02, 0x28, 0x0d, 0x52, 0x05, 0x69, 0x6e, 0x6f, 0x64, 0x65, 0x22, 0xba, 0x10, 0x0a, 0x09, 0x20, 0x02, 0x28, 0x0d, 0x52, 0x05, 0x69, 0x6e, 0x6f, 0x64, 0x65, 0x22, 0x8c, 0x11, 0x0a, 0x09,
0x63, 0x72, 0x69, 0x75, 0x5f, 0x6f, 0x70, 0x74, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x69, 0x6d, 0x61, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x6f, 0x70, 0x74, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x69, 0x6d, 0x61,
0x67, 0x65, 0x73, 0x5f, 0x64, 0x69, 0x72, 0x5f, 0x66, 0x64, 0x18, 0x01, 0x20, 0x02, 0x28, 0x05, 0x67, 0x65, 0x73, 0x5f, 0x64, 0x69, 0x72, 0x5f, 0x66, 0x64, 0x18, 0x01, 0x20, 0x02, 0x28, 0x05,
0x52, 0x0b, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x44, 0x69, 0x72, 0x46, 0x64, 0x12, 0x10, 0x0a, 0x52, 0x0b, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x44, 0x69, 0x72, 0x46, 0x64, 0x12, 0x10, 0x0a,
@ -1850,92 +1871,100 @@ var file_rpc_rpc_proto_rawDesc = []byte{
0x75, 0x6d, 0x70, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x3d, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x13, 0x75, 0x6d, 0x70, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x3d, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x13,
0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x70, 0x72, 0x65, 0x5f, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x6d, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x70, 0x72, 0x65, 0x5f, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x6d,
0x6f, 0x64, 0x65, 0x3a, 0x06, 0x53, 0x50, 0x4c, 0x49, 0x43, 0x45, 0x52, 0x0b, 0x70, 0x72, 0x65, 0x6f, 0x64, 0x65, 0x3a, 0x06, 0x53, 0x50, 0x4c, 0x49, 0x43, 0x45, 0x52, 0x0b, 0x70, 0x72, 0x65,
0x44, 0x75, 0x6d, 0x70, 0x4d, 0x6f, 0x64, 0x65, 0x22, 0x2c, 0x0a, 0x0e, 0x63, 0x72, 0x69, 0x75, 0x44, 0x75, 0x6d, 0x70, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x69, 0x64, 0x66,
0x5f, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x73, 0x6b, 0x18, 0x3e, 0x20, 0x01, 0x28, 0x05,
0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x52, 0x0c, 0x70, 0x69, 0x64, 0x66, 0x64, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x53, 0x6b, 0x12, 0x2a,
0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x22, 0x25, 0x0a, 0x11, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x72, 0x0a, 0x11, 0x6c, 0x73, 0x6d, 0x5f, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74,
0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x65, 0x78, 0x74, 0x18, 0x3f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6c, 0x73, 0x6d, 0x4d, 0x6f,
0x69, 0x64, 0x18, 0x01, 0x20, 0x02, 0x28, 0x05, 0x52, 0x03, 0x70, 0x69, 0x64, 0x22, 0x37, 0x0a, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x2c, 0x0a, 0x0e, 0x63, 0x72,
0x0b, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x75, 0x5f, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x12, 0x1a, 0x0a, 0x08,
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08,
0x72, 0x69, 0x70, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x22, 0x25, 0x0a, 0x11, 0x63, 0x72, 0x69, 0x75,
0x05, 0x52, 0x03, 0x70, 0x69, 0x64, 0x22, 0x4b, 0x0a, 0x0d, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x66, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x12, 0x10, 0x0a,
0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x6d, 0x5f, 0x74, 0x03, 0x70, 0x69, 0x64, 0x18, 0x01, 0x20, 0x02, 0x28, 0x05, 0x52, 0x03, 0x70, 0x69, 0x64, 0x22,
0x72, 0x61, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6d, 0x65, 0x6d, 0x54, 0x37, 0x0a, 0x0b, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x12, 0x16,
0x72, 0x61, 0x63, 0x6b, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x61, 0x7a, 0x79, 0x5f, 0x70, 0x61, 0x67, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6c, 0x61, 0x7a, 0x79, 0x50, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x02, 0x20,
0x67, 0x65, 0x73, 0x22, 0xd0, 0x01, 0x0a, 0x08, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x01, 0x28, 0x05, 0x52, 0x03, 0x70, 0x69, 0x64, 0x22, 0x6c, 0x0a, 0x0d, 0x63, 0x72, 0x69, 0x75,
0x12, 0x22, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x02, 0x28, 0x0e, 0x32, 0x0e, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x65, 0x6d,
0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x52, 0x04, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6d, 0x65,
0x74, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x04, 0x6f, 0x70, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x6d, 0x54, 0x72, 0x61, 0x63, 0x6b, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x61, 0x7a, 0x79, 0x5f, 0x70,
0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x6f, 0x70, 0x74, 0x73, 0x52, 0x04, 0x61, 0x67, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6c, 0x61, 0x7a, 0x79,
0x6f, 0x70, 0x74, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x73, 0x50, 0x61, 0x67, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x69, 0x64, 0x66, 0x64, 0x5f, 0x73,
0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x6e, 0x6f, 0x74, 0x6f, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x70, 0x69, 0x64, 0x66,
0x74, 0x69, 0x66, 0x79, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6b, 0x64, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x22, 0xd0, 0x01, 0x0a, 0x08, 0x63, 0x72, 0x69, 0x75, 0x5f,
0x65, 0x65, 0x70, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x71, 0x12, 0x22, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x02, 0x28,
0x6b, 0x65, 0x65, 0x70, 0x4f, 0x70, 0x65, 0x6e, 0x12, 0x2a, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x0e, 0x32, 0x0e, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x5f, 0x74, 0x79, 0x70,
0x75, 0x72, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x72, 0x69, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x04, 0x6f, 0x70, 0x74, 0x73, 0x18,
0x75, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x6f, 0x70, 0x74,
0x75, 0x72, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x73, 0x52, 0x04, 0x6f, 0x70, 0x74, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6e, 0x6f, 0x74, 0x69, 0x66,
0x0d, 0x52, 0x03, 0x70, 0x69, 0x64, 0x22, 0x8f, 0x03, 0x0a, 0x09, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x79, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52,
0x72, 0x65, 0x73, 0x70, 0x12, 0x22, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x02, 0x0d, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x1b,
0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x5f, 0x74, 0x79, 0x0a, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28,
0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x08, 0x52, 0x08, 0x6b, 0x65, 0x65, 0x70, 0x4f, 0x70, 0x65, 0x6e, 0x12, 0x2a, 0x0a, 0x08, 0x66,
0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x02, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e,
0x73, 0x73, 0x12, 0x23, 0x0a, 0x04, 0x64, 0x75, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x08, 0x66,
0x32, 0x0f, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x72, 0x65, 0x73, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x64, 0x18, 0x06,
0x70, 0x52, 0x04, 0x64, 0x75, 0x6d, 0x70, 0x12, 0x2c, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x70, 0x69, 0x64, 0x22, 0x8f, 0x03, 0x0a, 0x09, 0x63, 0x72,
0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x69, 0x75, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x12, 0x22, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18,
0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x52, 0x07, 0x72, 0x65, 0x01, 0x20, 0x02, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x72, 0x65, 0x71,
0x73, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x24, 0x0a, 0x06, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x18, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73,
0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x6e, 0x6f, 0x74, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x02, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75,
0x69, 0x66, 0x79, 0x52, 0x06, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x12, 0x26, 0x0a, 0x02, 0x70, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x23, 0x0a, 0x04, 0x64, 0x75, 0x6d, 0x70, 0x18, 0x03, 0x20,
0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x70, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x64, 0x75, 0x6d, 0x70, 0x5f,
0x61, 0x67, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x52, 0x72, 0x65, 0x73, 0x70, 0x52, 0x04, 0x64, 0x75, 0x6d, 0x70, 0x12, 0x2c, 0x0a, 0x07, 0x72, 0x65,
0x02, 0x70, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x72, 0x5f, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x18, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x72,
0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x63, 0x72, 0x45, 0x72, 0x72, 0x6e, 0x6f, 0x12, 0x2a, 0x69, 0x75, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x52,
0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x07, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x24, 0x0a, 0x06, 0x6e, 0x6f, 0x74, 0x69,
0x32, 0x0e, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x66, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f,
0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x72, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x06, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x12, 0x26,
0x5f, 0x65, 0x72, 0x72, 0x6d, 0x73, 0x67, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x0a, 0x02, 0x70, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x72, 0x69,
0x72, 0x45, 0x72, 0x72, 0x6d, 0x73, 0x67, 0x12, 0x27, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x75, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x6e,
0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x66, 0x6f, 0x52, 0x02, 0x70, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x72, 0x5f, 0x65, 0x72, 0x72,
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x6e, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x63, 0x72, 0x45, 0x72, 0x72, 0x6e,
0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x6f, 0x12, 0x2a, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x08, 0x20,
0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xb0, 0x01, 0x0a, 0x0c, 0x63, 0x72, 0x69, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75,
0x75, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x61, 0x6a, 0x72, 0x65, 0x73, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x1b, 0x0a,
0x6f, 0x72, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x02, 0x28, 0x05, 0x52, 0x09, 0x63, 0x72, 0x5f, 0x65, 0x72, 0x72, 0x6d, 0x73, 0x67, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09,
0x0b, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x52, 0x08, 0x63, 0x72, 0x45, 0x72, 0x72, 0x6d, 0x73, 0x67, 0x12, 0x27, 0x0a, 0x07, 0x76, 0x65,
0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x02, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x63, 0x72,
0x28, 0x05, 0x52, 0x0b, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x69, 0x75, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73,
0x14, 0x0a, 0x05, 0x67, 0x69, 0x74, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0b, 0x20,
0x67, 0x69, 0x74, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x75, 0x62, 0x6c, 0x65, 0x76, 0x65, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xb0, 0x01, 0x0a, 0x0c,
0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x73, 0x75, 0x62, 0x6c, 0x65, 0x76, 0x65, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c,
0x6c, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x78, 0x74, 0x72, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x02,
0x52, 0x05, 0x65, 0x78, 0x74, 0x72, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x28, 0x05, 0x52, 0x0b, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12,
0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x2a, 0x5f, 0x0a, 0x0c, 0x63, 0x21, 0x0a, 0x0c, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18,
0x72, 0x69, 0x75, 0x5f, 0x63, 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x49, 0x02, 0x20, 0x02, 0x28, 0x05, 0x52, 0x0b, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x4e, 0x75, 0x6d, 0x62,
0x47, 0x4e, 0x4f, 0x52, 0x45, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x47, 0x5f, 0x4e, 0x4f, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x69, 0x74, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28,
0x4e, 0x45, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x52, 0x4f, 0x50, 0x53, 0x10, 0x02, 0x12, 0x09, 0x52, 0x05, 0x67, 0x69, 0x74, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x75, 0x62, 0x6c,
0x08, 0x0a, 0x04, 0x53, 0x4f, 0x46, 0x54, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x55, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x73, 0x75, 0x62, 0x6c,
0x4c, 0x10, 0x04, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x52, 0x49, 0x43, 0x54, 0x10, 0x05, 0x12, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x78, 0x74, 0x72, 0x61, 0x18, 0x05, 0x20,
0x0b, 0x0a, 0x07, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x06, 0x2a, 0x2d, 0x0a, 0x12, 0x01, 0x28, 0x05, 0x52, 0x05, 0x65, 0x78, 0x74, 0x72, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
0x63, 0x72, 0x69, 0x75, 0x5f, 0x70, 0x72, 0x65, 0x5f, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x6d, 0x6f, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x2a, 0x5f,
0x64, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x50, 0x4c, 0x49, 0x43, 0x45, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x0c, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x63, 0x67, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x0a,
0x0a, 0x07, 0x56, 0x4d, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x10, 0x02, 0x2a, 0xd0, 0x01, 0x0a, 0x0d, 0x0a, 0x06, 0x49, 0x47, 0x4e, 0x4f, 0x52, 0x45, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x47,
0x63, 0x72, 0x69, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x12, 0x09, 0x0a, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x52, 0x4f, 0x50, 0x53,
0x05, 0x45, 0x4d, 0x50, 0x54, 0x59, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x55, 0x4d, 0x50, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x4f, 0x46, 0x54, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04,
0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x45, 0x53, 0x54, 0x4f, 0x52, 0x45, 0x10, 0x02, 0x12, 0x46, 0x55, 0x4c, 0x4c, 0x10, 0x04, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x52, 0x49, 0x43, 0x54,
0x09, 0x0a, 0x05, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x52, 0x10, 0x05, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x06, 0x2a,
0x45, 0x5f, 0x44, 0x55, 0x4d, 0x50, 0x10, 0x04, 0x12, 0x0f, 0x0a, 0x0b, 0x50, 0x41, 0x47, 0x45, 0x2d, 0x0a, 0x12, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x70, 0x72, 0x65, 0x5f, 0x64, 0x75, 0x6d, 0x70,
0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x10, 0x05, 0x12, 0x0a, 0x0a, 0x06, 0x4e, 0x4f, 0x54, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x50, 0x4c, 0x49, 0x43, 0x45, 0x10,
0x49, 0x46, 0x59, 0x10, 0x06, 0x12, 0x10, 0x0a, 0x0c, 0x43, 0x50, 0x55, 0x49, 0x4e, 0x46, 0x4f, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x56, 0x4d, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x10, 0x02, 0x2a, 0xd0,
0x5f, 0x44, 0x55, 0x4d, 0x50, 0x10, 0x07, 0x12, 0x11, 0x0a, 0x0d, 0x43, 0x50, 0x55, 0x49, 0x4e, 0x01, 0x0a, 0x0d, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x72, 0x65, 0x71, 0x5f, 0x74, 0x79, 0x70, 0x65,
0x46, 0x4f, 0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x08, 0x12, 0x11, 0x0a, 0x0d, 0x46, 0x45, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x4d, 0x50, 0x54, 0x59, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x44,
0x41, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x09, 0x12, 0x0b, 0x0a, 0x55, 0x4d, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x45, 0x53, 0x54, 0x4f, 0x52, 0x45,
0x07, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x0a, 0x12, 0x0c, 0x0a, 0x08, 0x57, 0x41, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x03, 0x12, 0x0c, 0x0a,
0x49, 0x54, 0x5f, 0x50, 0x49, 0x44, 0x10, 0x0b, 0x12, 0x14, 0x0a, 0x10, 0x50, 0x41, 0x47, 0x45, 0x08, 0x50, 0x52, 0x45, 0x5f, 0x44, 0x55, 0x4d, 0x50, 0x10, 0x04, 0x12, 0x0f, 0x0a, 0x0b, 0x50,
0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x43, 0x48, 0x4c, 0x44, 0x10, 0x0c, 0x41, 0x47, 0x45, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x10, 0x05, 0x12, 0x0a, 0x0a, 0x06,
0x4e, 0x4f, 0x54, 0x49, 0x46, 0x59, 0x10, 0x06, 0x12, 0x10, 0x0a, 0x0c, 0x43, 0x50, 0x55, 0x49,
0x4e, 0x46, 0x4f, 0x5f, 0x44, 0x55, 0x4d, 0x50, 0x10, 0x07, 0x12, 0x11, 0x0a, 0x0d, 0x43, 0x50,
0x55, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x08, 0x12, 0x11, 0x0a,
0x0d, 0x46, 0x45, 0x41, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x09,
0x12, 0x0b, 0x0a, 0x07, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x0a, 0x12, 0x0c, 0x0a,
0x08, 0x57, 0x41, 0x49, 0x54, 0x5f, 0x50, 0x49, 0x44, 0x10, 0x0b, 0x12, 0x14, 0x0a, 0x10, 0x50,
0x41, 0x47, 0x45, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x5f, 0x43, 0x48, 0x4c, 0x44, 0x10,
0x0c,
} }
var ( var (

View File

@ -0,0 +1,239 @@
// SPDX-License-Identifier: MIT
syntax = "proto2";
message criu_page_server_info {
optional string address = 1;
optional int32 port = 2;
optional int32 pid = 3;
optional int32 fd = 4;
}
message criu_veth_pair {
required string if_in = 1;
required string if_out = 2;
};
message ext_mount_map {
required string key = 1;
required string val = 2;
};
message join_namespace {
required string ns = 1;
required string ns_file = 2;
optional string extra_opt = 3;
}
message inherit_fd {
required string key = 1;
required int32 fd = 2;
};
message cgroup_root {
optional string ctrl = 1;
required string path = 2;
};
message unix_sk {
required uint32 inode = 1;
};
enum criu_cg_mode {
IGNORE = 0;
CG_NONE = 1;
PROPS = 2;
SOFT = 3;
FULL = 4;
STRICT = 5;
DEFAULT = 6;
};
enum criu_pre_dump_mode {
SPLICE = 1;
VM_READ = 2;
};
message criu_opts {
required int32 images_dir_fd = 1;
optional int32 pid = 2; /* if not set on dump, will dump requesting process */
optional bool leave_running = 3;
optional bool ext_unix_sk = 4;
optional bool tcp_established = 5;
optional bool evasive_devices = 6;
optional bool shell_job = 7;
optional bool file_locks = 8;
optional int32 log_level = 9 [default = 2];
optional string log_file = 10; /* No subdirs are allowed. Consider using work-dir */
optional criu_page_server_info ps = 11;
optional bool notify_scripts = 12;
optional string root = 13;
optional string parent_img = 14;
optional bool track_mem = 15;
optional bool auto_dedup = 16;
optional int32 work_dir_fd = 17;
optional bool link_remap = 18;
repeated criu_veth_pair veths = 19; /* DEPRECATED, use external instead */
optional uint32 cpu_cap = 20 [default = 0xffffffff];
optional bool force_irmap = 21;
repeated string exec_cmd = 22;
repeated ext_mount_map ext_mnt = 23; /* DEPRECATED, use external instead */
optional bool manage_cgroups = 24; /* backward compatibility */
repeated cgroup_root cg_root = 25;
optional bool rst_sibling = 26; /* swrk only */
repeated inherit_fd inherit_fd = 27; /* swrk only */
optional bool auto_ext_mnt = 28;
optional bool ext_sharing = 29;
optional bool ext_masters = 30;
repeated string skip_mnt = 31;
repeated string enable_fs = 32;
repeated unix_sk unix_sk_ino = 33; /* DEPRECATED, use external instead */
optional criu_cg_mode manage_cgroups_mode = 34;
optional uint32 ghost_limit = 35 [default = 0x100000];
repeated string irmap_scan_paths = 36;
repeated string external = 37;
optional uint32 empty_ns = 38;
repeated join_namespace join_ns = 39;
optional string cgroup_props = 41;
optional string cgroup_props_file = 42;
repeated string cgroup_dump_controller = 43;
optional string freeze_cgroup = 44;
optional uint32 timeout = 45;
optional bool tcp_skip_in_flight = 46;
optional bool weak_sysctls = 47;
optional bool lazy_pages = 48;
optional int32 status_fd = 49;
optional bool orphan_pts_master = 50;
optional string config_file = 51;
optional bool tcp_close = 52;
optional string lsm_profile = 53;
optional string tls_cacert = 54;
optional string tls_cacrl = 55;
optional string tls_cert = 56;
optional string tls_key = 57;
optional bool tls = 58;
optional bool tls_no_cn_verify = 59;
optional string cgroup_yard = 60;
optional criu_pre_dump_mode pre_dump_mode = 61 [default = SPLICE];
optional int32 pidfd_store_sk = 62;
optional string lsm_mount_context = 63;
/* optional bool check_mounts = 128; */
}
message criu_dump_resp {
optional bool restored = 1;
}
message criu_restore_resp {
required int32 pid = 1;
}
message criu_notify {
optional string script = 1;
optional int32 pid = 2;
}
enum criu_req_type {
EMPTY = 0;
DUMP = 1;
RESTORE = 2;
CHECK = 3;
PRE_DUMP = 4;
PAGE_SERVER = 5;
NOTIFY = 6;
CPUINFO_DUMP = 7;
CPUINFO_CHECK = 8;
FEATURE_CHECK = 9;
VERSION = 10;
WAIT_PID = 11;
PAGE_SERVER_CHLD = 12;
}
/*
* List of features which can queried via
* CRIU_REQ_TYPE__FEATURE_CHECK
*/
message criu_features {
optional bool mem_track = 1;
optional bool lazy_pages = 2;
optional bool pidfd_store = 3;
}
/*
* Request -- each type corresponds to must-be-there
* request arguments of respective type
*/
message criu_req {
required criu_req_type type = 1;
optional criu_opts opts = 2;
optional bool notify_success = 3;
/*
* When set service won't close the connection but
* will wait for more req-s to appear. Works not
* for all request types.
*/
optional bool keep_open = 4;
/*
* 'features' can be used to query which features
* are supported by the installed criu/kernel
* via RPC.
*/
optional criu_features features = 5;
/* 'pid' is used for WAIT_PID */
optional uint32 pid = 6;
}
/*
* Response -- it states whether the request was served
* and additional request-specific information
*/
message criu_resp {
required criu_req_type type = 1;
required bool success = 2;
optional criu_dump_resp dump = 3;
optional criu_restore_resp restore = 4;
optional criu_notify notify = 5;
optional criu_page_server_info ps = 6;
optional int32 cr_errno = 7;
optional criu_features features = 8;
optional string cr_errmsg = 9;
optional criu_version version = 10;
optional int32 status = 11;
}
/* Answer for criu_req_type.VERSION requests */
message criu_version {
required int32 major_number = 1;
required int32 minor_number = 2;
optional string gitid = 3;
optional int32 sublevel = 4;
optional int32 extra = 5;
optional string name = 6;
}

View File

@ -5,6 +5,7 @@
*.so *.so
*.dylib *.dylib
*.o *.o
!*_bpf*.o
# Test binary, build with `go test -c` # Test binary, build with `go test -c`
*.test *.test

View File

@ -24,6 +24,5 @@ linters:
# Could be enabled later: # Could be enabled later:
# - gocyclo # - gocyclo
# - prealloc
# - maligned # - maligned
# - gosec # - gosec

View File

@ -57,7 +57,7 @@ Objects
loading a spec will fail because the kernel is too old, or a feature is not loading a spec will fail because the kernel is too old, or a feature is not
enabled. There are multiple ways the library deals with that: enabled. There are multiple ways the library deals with that:
* Fallback: older kernels don't allowing naming programs and maps. The library * Fallback: older kernels don't allow naming programs and maps. The library
automatically detects support for names, and omits them during load if automatically detects support for names, and omits them during load if
necessary. This works since name is primarily a debug aid. necessary. This works since name is primarily a debug aid.
@ -68,7 +68,7 @@ enabled. There are multiple ways the library deals with that:
Once program and map objects are loaded they expose the kernel's low-level API, Once program and map objects are loaded they expose the kernel's low-level API,
e.g. `NextKey`. Often this API is awkward to use in Go, so there are safer e.g. `NextKey`. Often this API is awkward to use in Go, so there are safer
wrappers on top of the low-level API, like `MapIterator`. The low-level API is wrappers on top of the low-level API, like `MapIterator`. The low-level API is
useful as an out when our higher-level API doesn't support a particular use case. useful when our higher-level API doesn't support a particular use case.
Links Links
--- ---

View File

@ -6,8 +6,8 @@ are welcome. Please take a look at [the architecture](ARCHITECTURE.md) to get
a better understanding for the high-level goals. a better understanding for the high-level goals.
New features must be accompanied by tests. Before starting work on any large New features must be accompanied by tests. Before starting work on any large
feature, please [join](https://cilium.herokuapp.com/) the feature, please [join](https://ebpf.io/slack) the
[#libbpf-go](https://cilium.slack.com/messages/libbpf-go) channel on Slack to [#ebpf-go](https://cilium.slack.com/messages/ebpf-go) channel on Slack to
discuss the design first. discuss the design first.
When submitting pull requests, consider writing details about what problem you When submitting pull requests, consider writing details about what problem you

View File

@ -18,11 +18,14 @@ TARGETS := \
testdata/loader-clang-7 \ testdata/loader-clang-7 \
testdata/loader-clang-9 \ testdata/loader-clang-9 \
testdata/loader-$(CLANG) \ testdata/loader-$(CLANG) \
testdata/btf_map_init \
testdata/invalid_map \ testdata/invalid_map \
testdata/raw_tracepoint \ testdata/raw_tracepoint \
testdata/invalid_map_static \ testdata/invalid_map_static \
testdata/initialized_btf_map \ testdata/invalid_btf_map_init \
testdata/strings \ testdata/strings \
testdata/freplace \
testdata/iproute2_map_compat \
internal/btf/testdata/relocs internal/btf/testdata/relocs
.PHONY: all clean docker-all docker-shell .PHONY: all clean docker-all docker-shell

View File

@ -2,28 +2,16 @@
[![PkgGoDev](https://pkg.go.dev/badge/github.com/cilium/ebpf)](https://pkg.go.dev/github.com/cilium/ebpf) [![PkgGoDev](https://pkg.go.dev/badge/github.com/cilium/ebpf)](https://pkg.go.dev/github.com/cilium/ebpf)
![HoneyGopher](.github/images/cilium-ebpf.png)
eBPF is a pure Go library that provides utilities for loading, compiling, and eBPF is a pure Go library that provides utilities for loading, compiling, and
debugging eBPF programs. It has minimal external dependencies and is intended to debugging eBPF programs. It has minimal external dependencies and is intended to
be used in long running processes. be used in long running processes.
* [asm](https://pkg.go.dev/github.com/cilium/ebpf/asm) contains a basic
assembler
* [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
`PERF_EVENT_ARRAY`
* [cmd/bpf2go](https://pkg.go.dev/github.com/cilium/ebpf/cmd/bpf2go) allows
compiling and embedding eBPF programs in Go code
The library is maintained by [Cloudflare](https://www.cloudflare.com) and The library is maintained by [Cloudflare](https://www.cloudflare.com) and
[Cilium](https://www.cilium.io). Feel free to [Cilium](https://www.cilium.io).
[join](https://cilium.herokuapp.com/) the
[#libbpf-go](https://cilium.slack.com/messages/libbpf-go) channel on Slack.
## Current status See [ebpf.io](https://ebpf.io) for other projects from the eBPF ecosystem.
The package is production ready, but **the API is explicitly unstable right
now**. Expect to update your code if you want to follow along.
## Getting Started ## Getting Started
@ -33,21 +21,37 @@ your own tools can be found under [examples/](examples/).
Contributions are highly encouraged, as they highlight certain use cases of Contributions are highly encouraged, as they highlight certain use cases of
eBPF and the library, and help shape the future of the project. eBPF and the library, and help shape the future of the project.
## Getting Help
Please
[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.
## 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
within your Go code. (You don't need to use this if you prefer to write your eBPF program in C.)
* [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.
* [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
`PERF_EVENT_ARRAY`
* [ringbuf](https://pkg.go.dev/github.com/cilium/ebpf/ringbuf) allows reading from a
`BPF_MAP_TYPE_RINGBUF` map
## Requirements ## Requirements
* A version of Go that is [supported by * A version of Go that is [supported by
upstream](https://golang.org/doc/devel/release.html#policy) upstream](https://golang.org/doc/devel/release.html#policy)
* Linux 4.9, 4.19 or 5.4 (versions in-between should work, but are not tested) * Linux >= 4.9. CI is run against LTS releases.
## Useful resources
* [eBPF.io](https://ebpf.io) (recommended)
* [Cilium eBPF documentation](https://docs.cilium.io/en/latest/bpf/#bpf-guide)
(recommended)
* [Linux documentation on
BPF](https://www.kernel.org/doc/html/latest/networking/filter.html)
* [eBPF features by Linux
version](https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md)
## Regenerating Testdata ## Regenerating Testdata
@ -60,3 +64,7 @@ The toolchain image build files are kept in [testdata/docker/](testdata/docker/)
## License ## License
MIT MIT
### eBPF Gopher
The eBPF honeygopher is based on the Go gopher designed by Renee French.

View File

@ -184,6 +184,12 @@ const (
FnKtimeGetCoarseNs FnKtimeGetCoarseNs
FnImaInodeHash FnImaInodeHash
FnSockFromFile FnSockFromFile
FnCheckMtu
FnForEachMapElem
FnSnprintf
FnSysBpf
FnBtfFindByNameKind
FnSysClose
) )
// Call emits a function call. // Call emits a function call.

View File

@ -171,11 +171,17 @@ func _() {
_ = x[FnKtimeGetCoarseNs-160] _ = x[FnKtimeGetCoarseNs-160]
_ = x[FnImaInodeHash-161] _ = x[FnImaInodeHash-161]
_ = x[FnSockFromFile-162] _ = x[FnSockFromFile-162]
_ = x[FnCheckMtu-163]
_ = x[FnForEachMapElem-164]
_ = x[FnSnprintf-165]
_ = x[FnSysBpf-166]
_ = x[FnBtfFindByNameKind-167]
_ = x[FnSysClose-168]
} }
const _BuiltinFunc_name = "FnUnspecFnMapLookupElemFnMapUpdateElemFnMapDeleteElemFnProbeReadFnKtimeGetNsFnTracePrintkFnGetPrandomU32FnGetSmpProcessorIdFnSkbStoreBytesFnL3CsumReplaceFnL4CsumReplaceFnTailCallFnCloneRedirectFnGetCurrentPidTgidFnGetCurrentUidGidFnGetCurrentCommFnGetCgroupClassidFnSkbVlanPushFnSkbVlanPopFnSkbGetTunnelKeyFnSkbSetTunnelKeyFnPerfEventReadFnRedirectFnGetRouteRealmFnPerfEventOutputFnSkbLoadBytesFnGetStackidFnCsumDiffFnSkbGetTunnelOptFnSkbSetTunnelOptFnSkbChangeProtoFnSkbChangeTypeFnSkbUnderCgroupFnGetHashRecalcFnGetCurrentTaskFnProbeWriteUserFnCurrentTaskUnderCgroupFnSkbChangeTailFnSkbPullDataFnCsumUpdateFnSetHashInvalidFnGetNumaNodeIdFnSkbChangeHeadFnXdpAdjustHeadFnProbeReadStrFnGetSocketCookieFnGetSocketUidFnSetHashFnSetsockoptFnSkbAdjustRoomFnRedirectMapFnSkRedirectMapFnSockMapUpdateFnXdpAdjustMetaFnPerfEventReadValueFnPerfProgReadValueFnGetsockoptFnOverrideReturnFnSockOpsCbFlagsSetFnMsgRedirectMapFnMsgApplyBytesFnMsgCorkBytesFnMsgPullDataFnBindFnXdpAdjustTailFnSkbGetXfrmStateFnGetStackFnSkbLoadBytesRelativeFnFibLookupFnSockHashUpdateFnMsgRedirectHashFnSkRedirectHashFnLwtPushEncapFnLwtSeg6StoreBytesFnLwtSeg6AdjustSrhFnLwtSeg6ActionFnRcRepeatFnRcKeydownFnSkbCgroupIdFnGetCurrentCgroupIdFnGetLocalStorageFnSkSelectReuseportFnSkbAncestorCgroupIdFnSkLookupTcpFnSkLookupUdpFnSkReleaseFnMapPushElemFnMapPopElemFnMapPeekElemFnMsgPushDataFnMsgPopDataFnRcPointerRelFnSpinLockFnSpinUnlockFnSkFullsockFnTcpSockFnSkbEcnSetCeFnGetListenerSockFnSkcLookupTcpFnTcpCheckSyncookieFnSysctlGetNameFnSysctlGetCurrentValueFnSysctlGetNewValueFnSysctlSetNewValueFnStrtolFnStrtoulFnSkStorageGetFnSkStorageDeleteFnSendSignalFnTcpGenSyncookieFnSkbOutputFnProbeReadUserFnProbeReadKernelFnProbeReadUserStrFnProbeReadKernelStrFnTcpSendAckFnSendSignalThreadFnJiffies64FnReadBranchRecordsFnGetNsCurrentPidTgidFnXdpOutputFnGetNetnsCookieFnGetCurrentAncestorCgroupIdFnSkAssignFnKtimeGetBootNsFnSeqPrintfFnSeqWriteFnSkCgroupIdFnSkAncestorCgroupIdFnRingbufOutputFnRingbufReserveFnRingbufSubmitFnRingbufDiscardFnRingbufQueryFnCsumLevelFnSkcToTcp6SockFnSkcToTcpSockFnSkcToTcpTimewaitSockFnSkcToTcpRequestSockFnSkcToUdp6SockFnGetTaskStackFnLoadHdrOptFnStoreHdrOptFnReserveHdrOptFnInodeStorageGetFnInodeStorageDeleteFnDPathFnCopyFromUserFnSnprintfBtfFnSeqPrintfBtfFnSkbCgroupClassidFnRedirectNeighFnPerCpuPtrFnThisCpuPtrFnRedirectPeerFnTaskStorageGetFnTaskStorageDeleteFnGetCurrentTaskBtfFnBprmOptsSetFnKtimeGetCoarseNsFnImaInodeHashFnSockFromFile" const _BuiltinFunc_name = "FnUnspecFnMapLookupElemFnMapUpdateElemFnMapDeleteElemFnProbeReadFnKtimeGetNsFnTracePrintkFnGetPrandomU32FnGetSmpProcessorIdFnSkbStoreBytesFnL3CsumReplaceFnL4CsumReplaceFnTailCallFnCloneRedirectFnGetCurrentPidTgidFnGetCurrentUidGidFnGetCurrentCommFnGetCgroupClassidFnSkbVlanPushFnSkbVlanPopFnSkbGetTunnelKeyFnSkbSetTunnelKeyFnPerfEventReadFnRedirectFnGetRouteRealmFnPerfEventOutputFnSkbLoadBytesFnGetStackidFnCsumDiffFnSkbGetTunnelOptFnSkbSetTunnelOptFnSkbChangeProtoFnSkbChangeTypeFnSkbUnderCgroupFnGetHashRecalcFnGetCurrentTaskFnProbeWriteUserFnCurrentTaskUnderCgroupFnSkbChangeTailFnSkbPullDataFnCsumUpdateFnSetHashInvalidFnGetNumaNodeIdFnSkbChangeHeadFnXdpAdjustHeadFnProbeReadStrFnGetSocketCookieFnGetSocketUidFnSetHashFnSetsockoptFnSkbAdjustRoomFnRedirectMapFnSkRedirectMapFnSockMapUpdateFnXdpAdjustMetaFnPerfEventReadValueFnPerfProgReadValueFnGetsockoptFnOverrideReturnFnSockOpsCbFlagsSetFnMsgRedirectMapFnMsgApplyBytesFnMsgCorkBytesFnMsgPullDataFnBindFnXdpAdjustTailFnSkbGetXfrmStateFnGetStackFnSkbLoadBytesRelativeFnFibLookupFnSockHashUpdateFnMsgRedirectHashFnSkRedirectHashFnLwtPushEncapFnLwtSeg6StoreBytesFnLwtSeg6AdjustSrhFnLwtSeg6ActionFnRcRepeatFnRcKeydownFnSkbCgroupIdFnGetCurrentCgroupIdFnGetLocalStorageFnSkSelectReuseportFnSkbAncestorCgroupIdFnSkLookupTcpFnSkLookupUdpFnSkReleaseFnMapPushElemFnMapPopElemFnMapPeekElemFnMsgPushDataFnMsgPopDataFnRcPointerRelFnSpinLockFnSpinUnlockFnSkFullsockFnTcpSockFnSkbEcnSetCeFnGetListenerSockFnSkcLookupTcpFnTcpCheckSyncookieFnSysctlGetNameFnSysctlGetCurrentValueFnSysctlGetNewValueFnSysctlSetNewValueFnStrtolFnStrtoulFnSkStorageGetFnSkStorageDeleteFnSendSignalFnTcpGenSyncookieFnSkbOutputFnProbeReadUserFnProbeReadKernelFnProbeReadUserStrFnProbeReadKernelStrFnTcpSendAckFnSendSignalThreadFnJiffies64FnReadBranchRecordsFnGetNsCurrentPidTgidFnXdpOutputFnGetNetnsCookieFnGetCurrentAncestorCgroupIdFnSkAssignFnKtimeGetBootNsFnSeqPrintfFnSeqWriteFnSkCgroupIdFnSkAncestorCgroupIdFnRingbufOutputFnRingbufReserveFnRingbufSubmitFnRingbufDiscardFnRingbufQueryFnCsumLevelFnSkcToTcp6SockFnSkcToTcpSockFnSkcToTcpTimewaitSockFnSkcToTcpRequestSockFnSkcToUdp6SockFnGetTaskStackFnLoadHdrOptFnStoreHdrOptFnReserveHdrOptFnInodeStorageGetFnInodeStorageDeleteFnDPathFnCopyFromUserFnSnprintfBtfFnSeqPrintfBtfFnSkbCgroupClassidFnRedirectNeighFnPerCpuPtrFnThisCpuPtrFnRedirectPeerFnTaskStorageGetFnTaskStorageDeleteFnGetCurrentTaskBtfFnBprmOptsSetFnKtimeGetCoarseNsFnImaInodeHashFnSockFromFileFnCheckMtuFnForEachMapElemFnSnprintfFnSysBpfFnBtfFindByNameKindFnSysClose"
var _BuiltinFunc_index = [...]uint16{0, 8, 23, 38, 53, 64, 76, 89, 104, 123, 138, 153, 168, 178, 193, 212, 230, 246, 264, 277, 289, 306, 323, 338, 348, 363, 380, 394, 406, 416, 433, 450, 466, 481, 497, 512, 528, 544, 568, 583, 596, 608, 624, 639, 654, 669, 683, 700, 714, 723, 735, 750, 763, 778, 793, 808, 828, 847, 859, 875, 894, 910, 925, 939, 952, 958, 973, 990, 1000, 1022, 1033, 1049, 1066, 1082, 1096, 1115, 1133, 1148, 1158, 1169, 1182, 1202, 1219, 1238, 1259, 1272, 1285, 1296, 1309, 1321, 1334, 1347, 1359, 1373, 1383, 1395, 1407, 1416, 1429, 1446, 1460, 1479, 1494, 1517, 1536, 1555, 1563, 1572, 1586, 1603, 1615, 1632, 1643, 1658, 1675, 1693, 1713, 1725, 1743, 1754, 1773, 1794, 1805, 1821, 1849, 1859, 1875, 1886, 1896, 1908, 1928, 1943, 1959, 1974, 1990, 2004, 2015, 2030, 2044, 2066, 2087, 2102, 2116, 2128, 2141, 2156, 2173, 2193, 2200, 2214, 2227, 2241, 2259, 2274, 2285, 2297, 2311, 2327, 2346, 2365, 2378, 2396, 2410, 2424} var _BuiltinFunc_index = [...]uint16{0, 8, 23, 38, 53, 64, 76, 89, 104, 123, 138, 153, 168, 178, 193, 212, 230, 246, 264, 277, 289, 306, 323, 338, 348, 363, 380, 394, 406, 416, 433, 450, 466, 481, 497, 512, 528, 544, 568, 583, 596, 608, 624, 639, 654, 669, 683, 700, 714, 723, 735, 750, 763, 778, 793, 808, 828, 847, 859, 875, 894, 910, 925, 939, 952, 958, 973, 990, 1000, 1022, 1033, 1049, 1066, 1082, 1096, 1115, 1133, 1148, 1158, 1169, 1182, 1202, 1219, 1238, 1259, 1272, 1285, 1296, 1309, 1321, 1334, 1347, 1359, 1373, 1383, 1395, 1407, 1416, 1429, 1446, 1460, 1479, 1494, 1517, 1536, 1555, 1563, 1572, 1586, 1603, 1615, 1632, 1643, 1658, 1675, 1693, 1713, 1725, 1743, 1754, 1773, 1794, 1805, 1821, 1849, 1859, 1875, 1886, 1896, 1908, 1928, 1943, 1959, 1974, 1990, 2004, 2015, 2030, 2044, 2066, 2087, 2102, 2116, 2128, 2141, 2156, 2173, 2193, 2200, 2214, 2227, 2241, 2259, 2274, 2285, 2297, 2311, 2327, 2346, 2365, 2378, 2396, 2410, 2424, 2434, 2450, 2460, 2468, 2487, 2497}
func (i BuiltinFunc) String() string { func (i BuiltinFunc) String() string {
if i < 0 || i >= BuiltinFunc(len(_BuiltinFunc_index)-1) { if i < 0 || i >= BuiltinFunc(len(_BuiltinFunc_index)-1) {

View File

@ -181,6 +181,11 @@ func (ins *Instruction) IsFunctionCall() bool {
return ins.OpCode.JumpOp() == Call && ins.Src == PseudoCall return ins.OpCode.JumpOp() == Call && ins.Src == PseudoCall
} }
// IsBuiltinCall returns true if the instruction is a built-in call, i.e. BPF helper call.
func (ins *Instruction) IsBuiltinCall() bool {
return ins.OpCode.JumpOp() == Call && ins.Src == R0 && ins.Dst == R0
}
// IsConstantLoad returns true if the instruction loads a constant of the // IsConstantLoad returns true if the instruction loads a constant of the
// given size. // given size.
func (ins *Instruction) IsConstantLoad(size Size) bool { func (ins *Instruction) IsConstantLoad(size Size) bool {

65
vendor/github.com/cilium/ebpf/attachtype_string.go generated vendored Normal file
View File

@ -0,0 +1,65 @@
// Code generated by "stringer -type AttachType -trimprefix Attach"; DO NOT EDIT.
package ebpf
import "strconv"
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[AttachNone-0]
_ = x[AttachCGroupInetIngress-0]
_ = x[AttachCGroupInetEgress-1]
_ = x[AttachCGroupInetSockCreate-2]
_ = x[AttachCGroupSockOps-3]
_ = x[AttachSkSKBStreamParser-4]
_ = x[AttachSkSKBStreamVerdict-5]
_ = x[AttachCGroupDevice-6]
_ = x[AttachSkMsgVerdict-7]
_ = x[AttachCGroupInet4Bind-8]
_ = x[AttachCGroupInet6Bind-9]
_ = x[AttachCGroupInet4Connect-10]
_ = x[AttachCGroupInet6Connect-11]
_ = x[AttachCGroupInet4PostBind-12]
_ = x[AttachCGroupInet6PostBind-13]
_ = x[AttachCGroupUDP4Sendmsg-14]
_ = x[AttachCGroupUDP6Sendmsg-15]
_ = x[AttachLircMode2-16]
_ = x[AttachFlowDissector-17]
_ = x[AttachCGroupSysctl-18]
_ = x[AttachCGroupUDP4Recvmsg-19]
_ = x[AttachCGroupUDP6Recvmsg-20]
_ = x[AttachCGroupGetsockopt-21]
_ = x[AttachCGroupSetsockopt-22]
_ = x[AttachTraceRawTp-23]
_ = x[AttachTraceFEntry-24]
_ = x[AttachTraceFExit-25]
_ = x[AttachModifyReturn-26]
_ = x[AttachLSMMac-27]
_ = x[AttachTraceIter-28]
_ = x[AttachCgroupInet4GetPeername-29]
_ = x[AttachCgroupInet6GetPeername-30]
_ = x[AttachCgroupInet4GetSockname-31]
_ = x[AttachCgroupInet6GetSockname-32]
_ = x[AttachXDPDevMap-33]
_ = x[AttachCgroupInetSockRelease-34]
_ = x[AttachXDPCPUMap-35]
_ = x[AttachSkLookup-36]
_ = x[AttachXDP-37]
_ = x[AttachSkSKBVerdict-38]
_ = x[AttachSkReuseportSelect-39]
_ = x[AttachSkReuseportSelectOrMigrate-40]
_ = x[AttachPerfEvent-41]
}
const _AttachType_name = "NoneCGroupInetEgressCGroupInetSockCreateCGroupSockOpsSkSKBStreamParserSkSKBStreamVerdictCGroupDeviceSkMsgVerdictCGroupInet4BindCGroupInet6BindCGroupInet4ConnectCGroupInet6ConnectCGroupInet4PostBindCGroupInet6PostBindCGroupUDP4SendmsgCGroupUDP6SendmsgLircMode2FlowDissectorCGroupSysctlCGroupUDP4RecvmsgCGroupUDP6RecvmsgCGroupGetsockoptCGroupSetsockoptTraceRawTpTraceFEntryTraceFExitModifyReturnLSMMacTraceIterCgroupInet4GetPeernameCgroupInet6GetPeernameCgroupInet4GetSocknameCgroupInet6GetSocknameXDPDevMapCgroupInetSockReleaseXDPCPUMapSkLookupXDPSkSKBVerdictSkReuseportSelectSkReuseportSelectOrMigratePerfEvent"
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}
func (i AttachType) String() string {
if i >= AttachType(len(_AttachType_index)-1) {
return "AttachType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _AttachType_name[_AttachType_index[i]:_AttachType_index[i+1]]
}

View File

@ -1,6 +1,7 @@
package ebpf package ebpf
import ( import (
"encoding/binary"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -25,6 +26,10 @@ type CollectionOptions struct {
type CollectionSpec struct { type CollectionSpec struct {
Maps map[string]*MapSpec Maps map[string]*MapSpec
Programs map[string]*ProgramSpec Programs map[string]*ProgramSpec
// ByteOrder specifies whether the ELF was compiled for
// big-endian or little-endian architectures.
ByteOrder binary.ByteOrder
} }
// Copy returns a recursive copy of the spec. // Copy returns a recursive copy of the spec.
@ -34,8 +39,9 @@ func (cs *CollectionSpec) Copy() *CollectionSpec {
} }
cpy := CollectionSpec{ cpy := CollectionSpec{
Maps: make(map[string]*MapSpec, len(cs.Maps)), Maps: make(map[string]*MapSpec, len(cs.Maps)),
Programs: make(map[string]*ProgramSpec, len(cs.Programs)), Programs: make(map[string]*ProgramSpec, len(cs.Programs)),
ByteOrder: cs.ByteOrder,
} }
for name, spec := range cs.Maps { for name, spec := range cs.Maps {
@ -123,7 +129,7 @@ func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error
buf := make([]byte, len(value)) buf := make([]byte, len(value))
copy(buf, value) copy(buf, value)
err := patchValue(buf, btf.MapValue(rodata.BTF), consts) err := patchValue(buf, rodata.BTF.Value, consts)
if err != nil { if err != nil {
return err return err
} }
@ -134,15 +140,15 @@ func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error
// Assign the contents of a CollectionSpec to a struct. // Assign the contents of a CollectionSpec to a struct.
// //
// This function is a short-cut to manually checking the presence // This function is a shortcut to manually checking the presence
// of maps and programs in a collection spec. Consider using bpf2go if this // of maps and programs in a CollectionSpec. Consider using bpf2go
// sounds useful. // if this sounds useful.
// //
// The argument to must be a pointer to a struct. A field of the // 'to' must be a pointer to a struct. A field of the
// struct is updated with values from Programs or Maps if it // struct is updated with values from Programs or Maps if it
// has an `ebpf` tag and its type is *ProgramSpec or *MapSpec. // has an `ebpf` tag and its type is *ProgramSpec or *MapSpec.
// The tag gives the name of the program or map as found in // The tag's value specifies the name of the program or map as
// the CollectionSpec. // found in the CollectionSpec.
// //
// struct { // struct {
// Foo *ebpf.ProgramSpec `ebpf:"xdp_foo"` // Foo *ebpf.ProgramSpec `ebpf:"xdp_foo"`
@ -150,42 +156,47 @@ func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error
// Ignored int // Ignored int
// } // }
// //
// Returns an error if any of the fields can't be found, or // Returns an error if any of the eBPF objects can't be found, or
// if the same map or program is assigned multiple times. // if the same MapSpec or ProgramSpec is assigned multiple times.
func (cs *CollectionSpec) Assign(to interface{}) error { func (cs *CollectionSpec) Assign(to interface{}) error {
valueOf := func(typ reflect.Type, name string) (reflect.Value, error) { // Assign() only supports assigning ProgramSpecs and MapSpecs,
// so doesn't load any resources into the kernel.
getValue := func(typ reflect.Type, name string) (interface{}, error) {
switch typ { switch typ {
case reflect.TypeOf((*ProgramSpec)(nil)): case reflect.TypeOf((*ProgramSpec)(nil)):
p := cs.Programs[name] if p := cs.Programs[name]; p != nil {
if p == nil { return p, nil
return reflect.Value{}, fmt.Errorf("missing program %q", name)
} }
return reflect.ValueOf(p), nil return nil, fmt.Errorf("missing program %q", name)
case reflect.TypeOf((*MapSpec)(nil)): case reflect.TypeOf((*MapSpec)(nil)):
m := cs.Maps[name] if m := cs.Maps[name]; m != nil {
if m == nil { return m, nil
return reflect.Value{}, fmt.Errorf("missing map %q", name)
} }
return reflect.ValueOf(m), nil return nil, fmt.Errorf("missing map %q", name)
default: default:
return reflect.Value{}, fmt.Errorf("unsupported type %s", typ) return nil, fmt.Errorf("unsupported type %s", typ)
} }
} }
return assignValues(to, valueOf) return assignValues(to, getValue)
} }
// LoadAndAssign maps and programs into the kernel and assign them to a struct. // LoadAndAssign loads Maps and Programs into the kernel and assigns them
// to a struct.
// //
// This function is a short-cut to manually checking the presence // This function is a shortcut to manually checking the presence
// of maps and programs in a collection spec. Consider using bpf2go if this // of maps and programs in a CollectionSpec. Consider using bpf2go
// sounds useful. // if this sounds useful.
// //
// The argument to must be a pointer to a struct. A field of the // 'to' must be a pointer to a struct. A field of the struct is updated with
// struct is updated with values from Programs or Maps if it // a Program or Map if it has an `ebpf` tag and its type is *Program or *Map.
// has an `ebpf` tag and its type is *Program or *Map. // The tag's value specifies the name of the program or map as found in the
// The tag gives the name of the program or map as found in // CollectionSpec. Before updating the struct, the requested objects and their
// the CollectionSpec. // dependent resources are loaded into the kernel and populated with values if
// specified.
// //
// struct { // struct {
// Foo *ebpf.Program `ebpf:"xdp_foo"` // Foo *ebpf.Program `ebpf:"xdp_foo"`
@ -196,39 +207,53 @@ func (cs *CollectionSpec) Assign(to interface{}) error {
// opts may be nil. // opts may be nil.
// //
// Returns an error if any of the fields can't be found, or // Returns an error if any of the fields can't be found, or
// if the same map or program is assigned multiple times. // if the same Map or Program is assigned multiple times.
func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions) error { func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions) error {
if opts == nil { loader := newCollectionLoader(cs, opts)
opts = &CollectionOptions{} defer loader.cleanup()
}
loadMap, loadProgram, done, cleanup := lazyLoadCollection(cs, opts) // Support assigning Programs and Maps, lazy-loading the required objects.
defer cleanup() assignedMaps := make(map[string]bool)
getValue := func(typ reflect.Type, name string) (interface{}, error) {
valueOf := func(typ reflect.Type, name string) (reflect.Value, error) {
switch typ { switch typ {
case reflect.TypeOf((*Program)(nil)): case reflect.TypeOf((*Program)(nil)):
p, err := loadProgram(name) return loader.loadProgram(name)
if err != nil {
return reflect.Value{}, err
}
return reflect.ValueOf(p), nil
case reflect.TypeOf((*Map)(nil)): case reflect.TypeOf((*Map)(nil)):
m, err := loadMap(name) assignedMaps[name] = true
if err != nil { return loader.loadMap(name)
return reflect.Value{}, err
}
return reflect.ValueOf(m), nil
default: default:
return reflect.Value{}, fmt.Errorf("unsupported type %s", typ) return nil, fmt.Errorf("unsupported type %s", typ)
} }
} }
if err := assignValues(to, valueOf); err != nil { // Load the Maps and Programs requested by the annotated struct.
if err := assignValues(to, getValue); err != nil {
return err return err
} }
done() // Populate the requested maps. Has a chance of lazy-loading other dependent maps.
if err := loader.populateMaps(); err != nil {
return err
}
// Evaluate the loader's objects after all (lazy)loading has taken place.
for n, m := range loader.maps {
switch m.typ {
case ProgramArray:
// Require all lazy-loaded ProgramArrays to be assigned to the given object.
// Without any references, they will be closed on the first GC and all tail
// calls into them will miss.
if !assignedMaps[n] {
return fmt.Errorf("ProgramArray %s must be assigned to prevent missed tail calls", n)
}
}
}
loader.finalize()
return nil return nil
} }
@ -246,24 +271,32 @@ func NewCollection(spec *CollectionSpec) (*Collection, error) {
// NewCollectionWithOptions creates a Collection from a specification. // NewCollectionWithOptions creates a Collection from a specification.
func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (*Collection, error) { func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (*Collection, error) {
loadMap, loadProgram, done, cleanup := lazyLoadCollection(spec, &opts) loader := newCollectionLoader(spec, &opts)
defer cleanup() defer loader.cleanup()
// Create maps first, as their fds need to be linked into programs.
for mapName := range spec.Maps { for mapName := range spec.Maps {
_, err := loadMap(mapName) if _, err := loader.loadMap(mapName); err != nil {
if err != nil {
return nil, err return nil, err
} }
} }
for progName := range spec.Programs { for progName := range spec.Programs {
_, err := loadProgram(progName) if _, err := loader.loadProgram(progName); err != nil {
if err != nil {
return nil, err return nil, err
} }
} }
maps, progs := done() // Maps can contain Program and Map stubs, so populate them after
// all Maps and Programs have been successfully loaded.
if err := loader.populateMaps(); err != nil {
return nil, err
}
maps, progs := loader.maps, loader.programs
loader.finalize()
return &Collection{ return &Collection{
progs, progs,
maps, maps,
@ -314,113 +347,154 @@ func (hc handleCache) close() {
for _, handle := range hc.btfHandles { for _, handle := range hc.btfHandles {
handle.Close() handle.Close()
} }
hc.btfHandles = nil
hc.btfSpecs = nil
} }
func lazyLoadCollection(coll *CollectionSpec, opts *CollectionOptions) ( type collectionLoader struct {
loadMap func(string) (*Map, error), coll *CollectionSpec
loadProgram func(string) (*Program, error), opts *CollectionOptions
done func() (map[string]*Map, map[string]*Program), maps map[string]*Map
cleanup func(), programs map[string]*Program
) { handles *handleCache
var ( }
maps = make(map[string]*Map)
progs = make(map[string]*Program)
handles = newHandleCache()
skipMapsAndProgs = false
)
cleanup = func() { func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) *collectionLoader {
handles.close() if opts == nil {
opts = &CollectionOptions{}
if skipMapsAndProgs {
return
}
for _, m := range maps {
m.Close()
}
for _, p := range progs {
p.Close()
}
} }
done = func() (map[string]*Map, map[string]*Program) { return &collectionLoader{
skipMapsAndProgs = true coll,
return maps, progs opts,
make(map[string]*Map),
make(map[string]*Program),
newHandleCache(),
} }
}
loadMap = func(mapName string) (*Map, error) { // finalize should be called when all the collectionLoader's resources
if m := maps[mapName]; m != nil { // have been successfully loaded into the kernel and populated with values.
return m, nil func (cl *collectionLoader) finalize() {
} cl.maps, cl.programs = nil, nil
}
mapSpec := coll.Maps[mapName] // cleanup cleans up all resources left over in the collectionLoader.
if mapSpec == nil { // Call finalize() when Map and Program creation/population is successful
return nil, fmt.Errorf("missing map %s", mapName) // to prevent them from getting closed.
} func (cl *collectionLoader) cleanup() {
cl.handles.close()
for _, m := range cl.maps {
m.Close()
}
for _, p := range cl.programs {
p.Close()
}
}
m, err := newMapWithOptions(mapSpec, opts.Maps, handles) func (cl *collectionLoader) loadMap(mapName string) (*Map, error) {
if err != nil { if m := cl.maps[mapName]; m != nil {
return nil, fmt.Errorf("map %s: %w", mapName, err)
}
maps[mapName] = m
return m, nil return m, nil
} }
loadProgram = func(progName string) (*Program, error) { mapSpec := cl.coll.Maps[mapName]
if prog := progs[progName]; prog != nil { if mapSpec == nil {
return prog, nil return nil, fmt.Errorf("missing map %s", mapName)
}
m, err := newMapWithOptions(mapSpec, cl.opts.Maps, cl.handles)
if err != nil {
return nil, fmt.Errorf("map %s: %w", mapName, err)
}
cl.maps[mapName] = m
return m, nil
}
func (cl *collectionLoader) loadProgram(progName string) (*Program, error) {
if prog := cl.programs[progName]; prog != nil {
return prog, nil
}
progSpec := cl.coll.Programs[progName]
if progSpec == nil {
return nil, fmt.Errorf("unknown program %s", progName)
}
progSpec = progSpec.Copy()
// Rewrite any reference to a valid map.
for i := range progSpec.Instructions {
ins := &progSpec.Instructions[i]
if !ins.IsLoadFromMap() || ins.Reference == "" {
continue
} }
progSpec := coll.Programs[progName] if uint32(ins.Constant) != math.MaxUint32 {
if progSpec == nil { // Don't overwrite maps already rewritten, users can
return nil, fmt.Errorf("unknown program %s", progName) // rewrite programs in the spec themselves
continue
} }
progSpec = progSpec.Copy() m, err := cl.loadMap(ins.Reference)
// Rewrite any reference to a valid map.
for i := range progSpec.Instructions {
ins := &progSpec.Instructions[i]
if !ins.IsLoadFromMap() || ins.Reference == "" {
continue
}
if uint32(ins.Constant) != math.MaxUint32 {
// Don't overwrite maps already rewritten, users can
// rewrite programs in the spec themselves
continue
}
m, err := loadMap(ins.Reference)
if err != nil {
return nil, fmt.Errorf("program %s: %w", progName, err)
}
fd := m.FD()
if fd < 0 {
return nil, fmt.Errorf("map %s: %w", ins.Reference, internal.ErrClosedFd)
}
if err := ins.RewriteMapPtr(m.FD()); err != nil {
return nil, fmt.Errorf("progam %s: map %s: %w", progName, ins.Reference, err)
}
}
prog, err := newProgramWithOptions(progSpec, opts.Programs, handles)
if err != nil { if err != nil {
return nil, fmt.Errorf("program %s: %w", progName, err) return nil, fmt.Errorf("program %s: %w", progName, err)
} }
progs[progName] = prog fd := m.FD()
return prog, nil if fd < 0 {
return nil, fmt.Errorf("map %s: %w", ins.Reference, internal.ErrClosedFd)
}
if err := ins.RewriteMapPtr(m.FD()); err != nil {
return nil, fmt.Errorf("program %s: map %s: %w", progName, ins.Reference, err)
}
} }
return prog, err := newProgramWithOptions(progSpec, cl.opts.Programs, cl.handles)
if err != nil {
return nil, fmt.Errorf("program %s: %w", progName, err)
}
cl.programs[progName] = prog
return prog, nil
}
func (cl *collectionLoader) populateMaps() error {
for mapName, m := range cl.maps {
mapSpec, ok := cl.coll.Maps[mapName]
if !ok {
return fmt.Errorf("missing map spec %s", mapName)
}
mapSpec = mapSpec.Copy()
// Replace any object stubs with loaded objects.
for i, kv := range mapSpec.Contents {
switch v := kv.Value.(type) {
case programStub:
// loadProgram is idempotent and could return an existing Program.
prog, err := cl.loadProgram(string(v))
if err != nil {
return fmt.Errorf("loading program %s, for map %s: %w", v, mapName, err)
}
mapSpec.Contents[i] = MapKV{kv.Key, prog}
case mapStub:
// loadMap is idempotent and could return an existing Map.
innerMap, err := cl.loadMap(string(v))
if err != nil {
return fmt.Errorf("loading inner map %s, for map %s: %w", v, mapName, err)
}
mapSpec.Contents[i] = MapKV{kv.Key, innerMap}
}
}
// Populate and freeze the map if specified.
if err := m.finalize(mapSpec); err != nil {
return fmt.Errorf("populating map %s: %w", mapName, err)
}
}
return nil
} }
// LoadCollection parses an object file and converts it to a collection. // LoadCollection parses an object file and converts it to a collection.
@ -466,108 +540,81 @@ func (coll *Collection) DetachProgram(name string) *Program {
return p return p
} }
// Assign the contents of a collection to a struct. // structField represents a struct field containing the ebpf struct tag.
// type structField struct {
// Deprecated: use CollectionSpec.Assign instead. It provides the same reflect.StructField
// functionality but creates only the maps and programs requested. value reflect.Value
func (coll *Collection) Assign(to interface{}) error {
assignedMaps := make(map[string]struct{})
assignedPrograms := make(map[string]struct{})
valueOf := func(typ reflect.Type, name string) (reflect.Value, error) {
switch typ {
case reflect.TypeOf((*Program)(nil)):
p := coll.Programs[name]
if p == nil {
return reflect.Value{}, fmt.Errorf("missing program %q", name)
}
assignedPrograms[name] = struct{}{}
return reflect.ValueOf(p), nil
case reflect.TypeOf((*Map)(nil)):
m := coll.Maps[name]
if m == nil {
return reflect.Value{}, fmt.Errorf("missing map %q", name)
}
assignedMaps[name] = struct{}{}
return reflect.ValueOf(m), nil
default:
return reflect.Value{}, fmt.Errorf("unsupported type %s", typ)
}
}
if err := assignValues(to, valueOf); err != nil {
return err
}
for name := range assignedPrograms {
coll.DetachProgram(name)
}
for name := range assignedMaps {
coll.DetachMap(name)
}
return nil
} }
func assignValues(to interface{}, valueOf func(reflect.Type, string) (reflect.Value, error)) error { // ebpfFields extracts field names tagged with 'ebpf' from a struct type.
type structField struct { // Keep track of visited types to avoid infinite recursion.
reflect.StructField func ebpfFields(structVal reflect.Value, visited map[reflect.Type]bool) ([]structField, error) {
value reflect.Value if visited == nil {
visited = make(map[reflect.Type]bool)
} }
var ( structType := structVal.Type()
fields []structField if structType.Kind() != reflect.Struct {
visitedTypes = make(map[reflect.Type]bool) return nil, fmt.Errorf("%s is not a struct", structType)
flattenStruct func(reflect.Value) error }
)
flattenStruct = func(structVal reflect.Value) error { if visited[structType] {
structType := structVal.Type() return nil, fmt.Errorf("recursion on type %s", structType)
if structType.Kind() != reflect.Struct { }
return fmt.Errorf("%s is not a struct", structType)
fields := make([]structField, 0, structType.NumField())
for i := 0; i < structType.NumField(); i++ {
field := structField{structType.Field(i), structVal.Field(i)}
// If the field is tagged, gather it and move on.
name := field.Tag.Get("ebpf")
if name != "" {
fields = append(fields, field)
continue
} }
if visitedTypes[structType] { // If the field does not have an ebpf tag, but is a struct or a pointer
return fmt.Errorf("recursion on type %s", structType) // to a struct, attempt to gather its fields as well.
} var v reflect.Value
switch field.Type.Kind() {
for i := 0; i < structType.NumField(); i++ { case reflect.Ptr:
field := structField{structType.Field(i), structVal.Field(i)} if field.Type.Elem().Kind() != reflect.Struct {
name := field.Tag.Get("ebpf")
if name != "" {
fields = append(fields, field)
continue continue
} }
var err error if field.value.IsNil() {
switch field.Type.Kind() { return nil, fmt.Errorf("nil pointer to %s", structType)
case reflect.Ptr:
if field.Type.Elem().Kind() != reflect.Struct {
continue
}
if field.value.IsNil() {
return fmt.Errorf("nil pointer to %s", structType)
}
err = flattenStruct(field.value.Elem())
case reflect.Struct:
err = flattenStruct(field.value)
default:
continue
} }
if err != nil { // Obtain the destination type of the pointer.
return fmt.Errorf("field %s: %w", field.Name, err) v = field.value.Elem()
}
case reflect.Struct:
// Reference the value's type directly.
v = field.value
default:
continue
} }
return nil inner, err := ebpfFields(v, visited)
if err != nil {
return nil, fmt.Errorf("field %s: %w", field.Name, err)
}
fields = append(fields, inner...)
} }
return fields, nil
}
// assignValues attempts to populate all fields of 'to' tagged with 'ebpf'.
//
// getValue is called for every tagged field of 'to' and must return the value
// to be assigned to the field with the given typ and name.
func assignValues(to interface{},
getValue func(typ reflect.Type, name string) (interface{}, error)) error {
toValue := reflect.ValueOf(to) toValue := reflect.ValueOf(to)
if toValue.Type().Kind() != reflect.Ptr { if toValue.Type().Kind() != reflect.Ptr {
return fmt.Errorf("%T is not a pointer to struct", to) return fmt.Errorf("%T is not a pointer to struct", to)
@ -577,7 +624,8 @@ func assignValues(to interface{}, valueOf func(reflect.Type, string) (reflect.Va
return fmt.Errorf("nil pointer to %T", to) return fmt.Errorf("nil pointer to %T", to)
} }
if err := flattenStruct(toValue.Elem()); err != nil { fields, err := ebpfFields(toValue.Elem(), nil)
if err != nil {
return err return err
} }
@ -587,19 +635,23 @@ func assignValues(to interface{}, valueOf func(reflect.Type, string) (reflect.Va
name string name string
} }
assignedTo := make(map[elem]string) assigned := make(map[elem]string)
for _, field := range fields { for _, field := range fields {
name := field.Tag.Get("ebpf") // Get string value the field is tagged with.
if strings.Contains(name, ",") { tag := field.Tag.Get("ebpf")
if strings.Contains(tag, ",") {
return fmt.Errorf("field %s: ebpf tag contains a comma", field.Name) return fmt.Errorf("field %s: ebpf tag contains a comma", field.Name)
} }
e := elem{field.Type, name} // Check if the eBPF object with the requested
if assignedField := assignedTo[e]; assignedField != "" { // type and tag was already assigned elsewhere.
return fmt.Errorf("field %s: %q was already assigned to %s", field.Name, name, assignedField) e := elem{field.Type, tag}
if af := assigned[e]; af != "" {
return fmt.Errorf("field %s: object %q was already assigned to %s", field.Name, tag, af)
} }
value, err := valueOf(field.Type, name) // Get the eBPF object referred to by the tag.
value, err := getValue(field.Type, tag)
if err != nil { if err != nil {
return fmt.Errorf("field %s: %w", field.Name, err) return fmt.Errorf("field %s: %w", field.Name, err)
} }
@ -607,9 +659,9 @@ func assignValues(to interface{}, valueOf func(reflect.Type, string) (reflect.Va
if !field.value.CanSet() { if !field.value.CanSet() {
return fmt.Errorf("field %s: can't set value", field.Name) return fmt.Errorf("field %s: can't set value", field.Name)
} }
field.value.Set(reflect.ValueOf(value))
field.value.Set(value) assigned[e] = field.Name
assignedTo[e] = field.Name
} }
return nil return nil

View File

@ -19,7 +19,7 @@ import (
) )
// elfCode is a convenience to reduce the amount of arguments that have to // elfCode is a convenience to reduce the amount of arguments that have to
// be passed around explicitly. You should treat it's contents as immutable. // be passed around explicitly. You should treat its contents as immutable.
type elfCode struct { type elfCode struct {
*internal.SafeELFFile *internal.SafeELFFile
sections map[elf.SectionIndex]*elfSection sections map[elf.SectionIndex]*elfSection
@ -188,7 +188,7 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) {
return nil, fmt.Errorf("load programs: %w", err) return nil, fmt.Errorf("load programs: %w", err)
} }
return &CollectionSpec{maps, progs}, nil return &CollectionSpec{maps, progs, ec.ByteOrder}, nil
} }
func loadLicense(sec *elf.Section) (string, error) { func loadLicense(sec *elf.Section) (string, error) {
@ -520,8 +520,12 @@ func (ec *elfCode) loadMaps(maps map[string]*MapSpec) error {
return fmt.Errorf("map %s: missing flags", mapName) return fmt.Errorf("map %s: missing flags", mapName)
} }
if _, err := io.Copy(internal.DiscardZeroes{}, lr); err != nil { extra, err := io.ReadAll(lr)
return fmt.Errorf("map %s: unknown and non-zero fields in definition", mapName) if err != nil {
return fmt.Errorf("map %s: reading map tail: %w", mapName, err)
}
if len(extra) > 0 {
spec.Extra = *bytes.NewReader(extra)
} }
if err := spec.clampPerfEventArraySize(); err != nil { if err := spec.clampPerfEventArraySize(); err != nil {
@ -535,6 +539,9 @@ func (ec *elfCode) loadMaps(maps map[string]*MapSpec) error {
return nil return nil
} }
// loadBTFMaps iterates over all ELF sections marked as BTF map sections
// (like .maps) and parses them into MapSpecs. Dump the .maps section and
// any relocations with `readelf -x .maps -r <elf_file>`.
func (ec *elfCode) loadBTFMaps(maps map[string]*MapSpec) error { func (ec *elfCode) loadBTFMaps(maps map[string]*MapSpec) error {
for _, sec := range ec.sections { for _, sec := range ec.sections {
if sec.kind != btfMapSection { if sec.kind != btfMapSection {
@ -545,33 +552,46 @@ func (ec *elfCode) loadBTFMaps(maps map[string]*MapSpec) error {
return fmt.Errorf("missing BTF") return fmt.Errorf("missing BTF")
} }
_, err := io.Copy(internal.DiscardZeroes{}, bufio.NewReader(sec.Open())) // Each section must appear as a DataSec in the ELF's BTF blob.
if err != nil { var ds *btf.Datasec
return fmt.Errorf("section %v: initializing BTF map definitions: %w", sec.Name, internal.ErrNotSupported)
}
var ds btf.Datasec
if err := ec.btf.FindType(sec.Name, &ds); err != nil { if err := ec.btf.FindType(sec.Name, &ds); err != nil {
return fmt.Errorf("cannot find section '%s' in BTF: %w", sec.Name, err) return fmt.Errorf("cannot find section '%s' in BTF: %w", sec.Name, err)
} }
// Open a Reader to the ELF's raw section bytes so we can assert that all
// of them are zero on a per-map (per-Var) basis. For now, the section's
// sole purpose is to receive relocations, so all must be zero.
rs := sec.Open()
for _, vs := range ds.Vars { for _, vs := range ds.Vars {
// BPF maps are declared as and assigned to global variables,
// so iterate over each Var in the DataSec and validate their types.
v, ok := vs.Type.(*btf.Var) v, ok := vs.Type.(*btf.Var)
if !ok { if !ok {
return fmt.Errorf("section %v: unexpected type %s", sec.Name, vs.Type) return fmt.Errorf("section %v: unexpected type %s", sec.Name, vs.Type)
} }
name := string(v.Name) name := string(v.Name)
// The BTF metadata for each Var contains the full length of the map
// declaration, so read the corresponding amount of bytes from the ELF.
// This way, we can pinpoint which map declaration contains unexpected
// (and therefore unsupported) data.
_, err := io.Copy(internal.DiscardZeroes{}, io.LimitReader(rs, int64(vs.Size)))
if err != nil {
return fmt.Errorf("section %v: map %s: initializing BTF map definitions: %w", sec.Name, name, internal.ErrNotSupported)
}
if maps[name] != nil { if maps[name] != nil {
return fmt.Errorf("section %v: map %s already exists", sec.Name, name) return fmt.Errorf("section %v: map %s already exists", sec.Name, name)
} }
// Each Var representing a BTF map definition contains a Struct.
mapStruct, ok := v.Type.(*btf.Struct) mapStruct, ok := v.Type.(*btf.Struct)
if !ok { if !ok {
return fmt.Errorf("expected struct, got %s", v.Type) return fmt.Errorf("expected struct, got %s", v.Type)
} }
mapSpec, err := mapSpecFromBTF(name, mapStruct, false, ec.btf) mapSpec, err := mapSpecFromBTF(sec, &vs, mapStruct, ec.btf, name, false)
if err != nil { if err != nil {
return fmt.Errorf("map %v: %w", name, err) return fmt.Errorf("map %v: %w", name, err)
} }
@ -582,32 +602,52 @@ func (ec *elfCode) loadBTFMaps(maps map[string]*MapSpec) error {
maps[name] = mapSpec maps[name] = mapSpec
} }
// Drain the ELF section reader to make sure all bytes are accounted for
// with BTF metadata.
i, err := io.Copy(io.Discard, rs)
if err != nil {
return fmt.Errorf("section %v: unexpected error reading remainder of ELF section: %w", sec.Name, err)
}
if i > 0 {
return fmt.Errorf("section %v: %d unexpected remaining bytes in ELF section, invalid BTF?", sec.Name, i)
}
} }
return nil return nil
} }
// A programStub is a placeholder for a Program to be inserted at a certain map key.
// It needs to be resolved into a Program later on in the loader process.
type programStub string
// A mapStub is a placeholder for a Map to be inserted at a certain map key.
// It needs to be resolved into a Map later on in the loader process.
type mapStub string
// mapSpecFromBTF produces a MapSpec based on a btf.Struct def representing // mapSpecFromBTF produces a MapSpec based on a btf.Struct def representing
// a BTF map definition. The name and spec arguments will be copied to the // a BTF map definition. The name and spec arguments will be copied to the
// resulting MapSpec, and inner must be true on any resursive invocations. // resulting MapSpec, and inner must be true on any resursive invocations.
func mapSpecFromBTF(name string, def *btf.Struct, inner bool, spec *btf.Spec) (*MapSpec, error) { func mapSpecFromBTF(es *elfSection, vs *btf.VarSecinfo, def *btf.Struct, spec *btf.Spec, name string, inner bool) (*MapSpec, error) {
var ( var (
key, value btf.Type key, value btf.Type
keySize, valueSize uint32 keySize, valueSize uint32
mapType, flags, maxEntries uint32 mapType MapType
pinType PinType flags, maxEntries uint32
innerMapSpec *MapSpec pinType PinType
err error innerMapSpec *MapSpec
contents []MapKV
err error
) )
for i, member := range def.Members { for i, member := range def.Members {
switch member.Name { switch member.Name {
case "type": case "type":
mapType, err = uintFromBTF(member.Type) mt, err := uintFromBTF(member.Type)
if err != nil { if err != nil {
return nil, fmt.Errorf("can't get type: %w", err) return nil, fmt.Errorf("can't get type: %w", err)
} }
mapType = MapType(mt)
case "map_flags": case "map_flags":
flags, err = uintFromBTF(member.Type) flags, err = uintFromBTF(member.Type)
@ -717,7 +757,7 @@ func mapSpecFromBTF(name string, def *btf.Struct, inner bool, spec *btf.Spec) (*
case *btf.Struct: case *btf.Struct:
// The values member pointing to an array of structs means we're expecting // The values member pointing to an array of structs means we're expecting
// a map-in-map declaration. // a map-in-map declaration.
if MapType(mapType) != ArrayOfMaps && MapType(mapType) != HashOfMaps { if mapType != ArrayOfMaps && mapType != HashOfMaps {
return nil, errors.New("outer map needs to be an array or a hash of maps") return nil, errors.New("outer map needs to be an array or a hash of maps")
} }
if inner { if inner {
@ -731,21 +771,38 @@ func mapSpecFromBTF(name string, def *btf.Struct, inner bool, spec *btf.Spec) (*
// on kernels 5.2 and up) // on kernels 5.2 and up)
// Pass the BTF spec from the parent object, since both parent and // Pass the BTF spec from the parent object, since both parent and
// child must be created from the same BTF blob (on kernels that support BTF). // child must be created from the same BTF blob (on kernels that support BTF).
innerMapSpec, err = mapSpecFromBTF(name+"_inner", t, true, spec) innerMapSpec, err = mapSpecFromBTF(es, vs, t, spec, name+"_inner", true)
if err != nil { if err != nil {
return nil, fmt.Errorf("can't parse BTF map definition of inner map: %w", err) return nil, fmt.Errorf("can't parse BTF map definition of inner map: %w", err)
} }
case *btf.FuncProto:
// The values member contains an array of function pointers, meaning an
// autopopulated PROG_ARRAY.
if mapType != ProgramArray {
return nil, errors.New("map needs to be a program array")
}
default: default:
return nil, fmt.Errorf("unsupported value type %q in 'values' field", t) return nil, fmt.Errorf("unsupported value type %q in 'values' field", t)
} }
contents, err = resolveBTFValuesContents(es, vs, member)
if err != nil {
return nil, fmt.Errorf("resolving values contents: %w", err)
}
default: default:
return nil, fmt.Errorf("unrecognized field %s in BTF map definition", member.Name) return nil, fmt.Errorf("unrecognized field %s in BTF map definition", member.Name)
} }
} }
bm := btf.NewMap(spec, key, value) if key == nil {
key = &btf.Void{}
}
if value == nil {
value = &btf.Void{}
}
return &MapSpec{ return &MapSpec{
Name: SanitizeName(name, -1), Name: SanitizeName(name, -1),
@ -754,9 +811,10 @@ func mapSpecFromBTF(name string, def *btf.Struct, inner bool, spec *btf.Spec) (*
ValueSize: valueSize, ValueSize: valueSize,
MaxEntries: maxEntries, MaxEntries: maxEntries,
Flags: flags, Flags: flags,
BTF: &bm, BTF: &btf.Map{Spec: spec, Key: key, Value: value},
Pinning: pinType, Pinning: pinType,
InnerMap: innerMapSpec, InnerMap: innerMapSpec,
Contents: contents,
}, nil }, nil
} }
@ -793,6 +851,64 @@ func resolveBTFArrayMacro(typ btf.Type) (btf.Type, error) {
return ptr.Target, nil return ptr.Target, nil
} }
// resolveBTFValuesContents resolves relocations into ELF sections belonging
// to btf.VarSecinfo's. This can be used on the 'values' member in BTF map
// definitions to extract static declarations of map contents.
func resolveBTFValuesContents(es *elfSection, vs *btf.VarSecinfo, member btf.Member) ([]MapKV, error) {
// The elements of a .values pointer array are not encoded in BTF.
// Instead, relocations are generated into each array index.
// However, it's possible to leave certain array indices empty, so all
// indices' offsets need to be checked for emitted relocations.
// The offset of the 'values' member within the _struct_ (in bits)
// is the starting point of the array. Convert to bytes. Add VarSecinfo
// offset to get the absolute position in the ELF blob.
start := (member.OffsetBits / 8) + vs.Offset
// 'values' is encoded in BTF as a zero (variable) length struct
// member, and its contents run until the end of the VarSecinfo.
// Add VarSecinfo offset to get the absolute position in the ELF blob.
end := vs.Size + vs.Offset
// The size of an address in this section. This determines the width of
// an index in the array.
align := uint32(es.SectionHeader.Addralign)
// Check if variable-length section is aligned.
if (end-start)%align != 0 {
return nil, errors.New("unaligned static values section")
}
elems := (end - start) / align
if elems == 0 {
return nil, nil
}
contents := make([]MapKV, 0, elems)
// k is the array index, off is its corresponding ELF section offset.
for k, off := uint32(0), start; k < elems; k, off = k+1, off+align {
r, ok := es.relocations[uint64(off)]
if !ok {
continue
}
// Relocation exists for the current offset in the ELF section.
// Emit a value stub based on the type of relocation to be replaced by
// a real fd later in the pipeline before populating the map.
// Map keys are encoded in MapKV entries, so empty array indices are
// skipped here.
switch t := elf.ST_TYPE(r.Info); t {
case elf.STT_FUNC:
contents = append(contents, MapKV{uint32(k), programStub(r.Name)})
case elf.STT_OBJECT:
contents = append(contents, MapKV{uint32(k), mapStub(r.Name)})
default:
return nil, fmt.Errorf("unknown relocation type %v", t)
}
}
return contents, nil
}
func (ec *elfCode) loadDataSections(maps map[string]*MapSpec) error { func (ec *elfCode) loadDataSections(maps map[string]*MapSpec) error {
for _, sec := range ec.sections { for _, sec := range ec.sections {
if sec.kind != dataSection { if sec.kind != dataSection {
@ -809,9 +925,9 @@ func (ec *elfCode) loadDataSections(maps map[string]*MapSpec) error {
return errors.New("data sections require BTF, make sure all consts are marked as static") return errors.New("data sections require BTF, make sure all consts are marked as static")
} }
btfMap, err := ec.btf.Datasec(sec.Name) var datasec *btf.Datasec
if err != nil { if err := ec.btf.FindType(sec.Name, &datasec); err != nil {
return err return fmt.Errorf("data section %s: can't get BTF: %w", sec.Name, err)
} }
data, err := sec.Data() data, err := sec.Data()
@ -830,7 +946,7 @@ func (ec *elfCode) loadDataSections(maps map[string]*MapSpec) error {
ValueSize: uint32(len(data)), ValueSize: uint32(len(data)),
MaxEntries: 1, MaxEntries: 1,
Contents: []MapKV{{uint32(0), data}}, Contents: []MapKV{{uint32(0), data}},
BTF: btfMap, BTF: &btf.Map{Spec: ec.btf, Key: &btf.Void{}, Value: datasec},
} }
switch sec.Name { switch sec.Name {
@ -855,6 +971,8 @@ func getProgType(sectionName string) (ProgramType, AttachType, uint32, string) {
}{ }{
// From https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/lib/bpf/libbpf.c // From https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/lib/bpf/libbpf.c
"socket": {SocketFilter, AttachNone, 0}, "socket": {SocketFilter, AttachNone, 0},
"sk_reuseport/migrate": {SkReuseport, AttachSkReuseportSelectOrMigrate, 0},
"sk_reuseport": {SkReuseport, AttachSkReuseportSelect, 0},
"seccomp": {SocketFilter, AttachNone, 0}, "seccomp": {SocketFilter, AttachNone, 0},
"kprobe/": {Kprobe, AttachNone, 0}, "kprobe/": {Kprobe, AttachNone, 0},
"uprobe/": {Kprobe, AttachNone, 0}, "uprobe/": {Kprobe, AttachNone, 0},
@ -884,6 +1002,7 @@ func getProgType(sectionName string) (ProgramType, AttachType, uint32, string) {
"fmod_ret.s/": {Tracing, AttachModifyReturn, unix.BPF_F_SLEEPABLE}, "fmod_ret.s/": {Tracing, AttachModifyReturn, unix.BPF_F_SLEEPABLE},
"fexit.s/": {Tracing, AttachTraceFExit, unix.BPF_F_SLEEPABLE}, "fexit.s/": {Tracing, AttachTraceFExit, unix.BPF_F_SLEEPABLE},
"sk_lookup/": {SkLookup, AttachSkLookup, 0}, "sk_lookup/": {SkLookup, AttachSkLookup, 0},
"freplace/": {Extension, AttachNone, 0},
"lsm/": {LSM, AttachLSMMac, 0}, "lsm/": {LSM, AttachLSMMac, 0},
"lsm.s/": {LSM, AttachLSMMac, unix.BPF_F_SLEEPABLE}, "lsm.s/": {LSM, AttachLSMMac, unix.BPF_F_SLEEPABLE},
@ -907,6 +1026,11 @@ func getProgType(sectionName string) (ProgramType, AttachType, uint32, string) {
"cgroup/setsockopt": {CGroupSockopt, AttachCGroupSetsockopt, 0}, "cgroup/setsockopt": {CGroupSockopt, AttachCGroupSetsockopt, 0},
"classifier": {SchedCLS, AttachNone, 0}, "classifier": {SchedCLS, AttachNone, 0},
"action": {SchedACT, AttachNone, 0}, "action": {SchedACT, AttachNone, 0},
"cgroup/getsockname4": {CGroupSockAddr, AttachCgroupInet4GetSockname, 0},
"cgroup/getsockname6": {CGroupSockAddr, AttachCgroupInet6GetSockname, 0},
"cgroup/getpeername4": {CGroupSockAddr, AttachCgroupInet4GetPeername, 0},
"cgroup/getpeername6": {CGroupSockAddr, AttachCgroupInet6GetPeername, 0},
} }
for prefix, t := range types { for prefix, t := range types {

View File

@ -1,3 +1,4 @@
//go:build gofuzz
// +build gofuzz // +build gofuzz
// Use with https://github.com/dvyukov/go-fuzz // Use with https://github.com/dvyukov/go-fuzz

View File

@ -1,9 +1,9 @@
module github.com/cilium/ebpf module github.com/cilium/ebpf
go 1.15 go 1.16
require ( require (
github.com/frankban/quicktest v1.11.3 github.com/frankban/quicktest v1.11.3
github.com/google/go-cmp v0.5.4 github.com/google/go-cmp v0.5.4
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34
) )

View File

@ -7,7 +7,7 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk= golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34 h1:GkvMjFtXUmahfDtashnc1mnrCtuBVcwse5QV2lUk/tI=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -12,6 +12,7 @@ import (
"time" "time"
"github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/btf"
) )
// MapInfo describes a map. // MapInfo describes a map.
@ -87,12 +88,16 @@ type ProgramInfo struct {
Tag string Tag string
// Name as supplied by user space at load time. // Name as supplied by user space at load time.
Name string Name string
// BTF for the program.
btf btf.ID
// IDS map ids related to program.
ids []MapID
stats *programStats stats *programStats
} }
func newProgramInfoFromFd(fd *internal.FD) (*ProgramInfo, error) { func newProgramInfoFromFd(fd *internal.FD) (*ProgramInfo, error) {
info, err := bpfGetProgInfoByFD(fd) info, err := bpfGetProgInfoByFD(fd, nil)
if errors.Is(err, syscall.EINVAL) { if errors.Is(err, syscall.EINVAL) {
return newProgramInfoFromProc(fd) return newProgramInfoFromProc(fd)
} }
@ -100,6 +105,15 @@ func newProgramInfoFromFd(fd *internal.FD) (*ProgramInfo, error) {
return nil, err return nil, err
} }
var mapIDs []MapID
if info.nr_map_ids > 0 {
mapIDs = make([]MapID, info.nr_map_ids)
info, err = bpfGetProgInfoByFD(fd, mapIDs)
if err != nil {
return nil, err
}
}
return &ProgramInfo{ return &ProgramInfo{
Type: ProgramType(info.prog_type), Type: ProgramType(info.prog_type),
id: ProgramID(info.id), id: ProgramID(info.id),
@ -107,6 +121,8 @@ func newProgramInfoFromFd(fd *internal.FD) (*ProgramInfo, error) {
Tag: hex.EncodeToString(info.tag[:]), Tag: hex.EncodeToString(info.tag[:]),
// name is available from 4.15. // name is available from 4.15.
Name: internal.CString(info.name[:]), Name: internal.CString(info.name[:]),
btf: btf.ID(info.btf_id),
ids: mapIDs,
stats: &programStats{ stats: &programStats{
runtime: time.Duration(info.run_time_ns), runtime: time.Duration(info.run_time_ns),
runCount: info.run_cnt, runCount: info.run_cnt,
@ -142,6 +158,17 @@ func (pi *ProgramInfo) ID() (ProgramID, bool) {
return pi.id, pi.id > 0 return pi.id, pi.id > 0
} }
// BTFID returns the BTF ID associated with the program.
//
// Available from 5.0.
//
// The bool return value indicates whether this optional field is available and
// populated. (The field may be available but not populated if the kernel
// supports the field but the program was loaded without BTF information.)
func (pi *ProgramInfo) BTFID() (btf.ID, bool) {
return pi.btf, pi.btf > 0
}
// RunCount returns the total number of times the program was called. // RunCount returns the total number of times the program was called.
// //
// Can return 0 if the collection of statistics is not enabled. See EnableStats(). // Can return 0 if the collection of statistics is not enabled. See EnableStats().
@ -164,6 +191,13 @@ func (pi *ProgramInfo) Runtime() (time.Duration, bool) {
return time.Duration(0), false return time.Duration(0), false
} }
// MapIDs returns the maps related to the program.
//
// The bool return value indicates whether this optional field is available.
func (pi *ProgramInfo) MapIDs() ([]MapID, bool) {
return pi.ids, pi.ids != nil
}
func scanFdInfo(fd *internal.FD, fields map[string]interface{}) error { func scanFdInfo(fd *internal.FD, fields map[string]interface{}) error {
raw, err := fd.Value() raw, err := fd.Value()
if err != nil { if err != nil {

6
vendor/github.com/cilium/ebpf/internal/align.go generated vendored Normal file
View File

@ -0,0 +1,6 @@
package internal
// Align returns 'n' updated to 'alignment' boundary.
func Align(n, alignment int) int {
return (int(n) + alignment - 1) / alignment * alignment
}

View File

@ -7,7 +7,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"math" "math"
"os" "os"
"reflect" "reflect"
@ -27,12 +26,15 @@ var (
ErrNoExtendedInfo = errors.New("no extended info") ErrNoExtendedInfo = errors.New("no extended info")
) )
// ID represents the unique ID of a BTF object.
type ID uint32
// Spec represents decoded BTF. // Spec represents decoded BTF.
type Spec struct { type Spec struct {
rawTypes []rawType rawTypes []rawType
strings stringTable strings stringTable
types []Type types []Type
namedTypes map[string][]namedType namedTypes map[string][]NamedType
funcInfos map[string]extInfo funcInfos map[string]extInfo
lineInfos map[string]extInfo lineInfos map[string]extInfo
coreRelos map[string]coreRelos coreRelos map[string]coreRelos
@ -61,15 +63,6 @@ func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) {
} }
defer file.Close() defer file.Close()
btfSection, btfExtSection, sectionSizes, err := findBtfSections(file)
if err != nil {
return nil, err
}
if btfSection == nil {
return nil, fmt.Errorf("btf: %w", ErrNotFound)
}
symbols, err := file.Symbols() symbols, err := file.Symbols()
if err != nil { if err != nil {
return nil, fmt.Errorf("can't read symbols: %v", err) return nil, fmt.Errorf("can't read symbols: %v", err)
@ -87,10 +80,6 @@ func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) {
} }
secName := file.Sections[symbol.Section].Name secName := file.Sections[symbol.Section].Name
if _, ok := sectionSizes[secName]; !ok {
continue
}
if symbol.Value > math.MaxUint32 { if symbol.Value > math.MaxUint32 {
return nil, fmt.Errorf("section %s: symbol %s: size exceeds maximum", secName, symbol.Name) return nil, fmt.Errorf("section %s: symbol %s: size exceeds maximum", secName, symbol.Name)
} }
@ -98,24 +87,10 @@ func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) {
variableOffsets[variable{secName, symbol.Name}] = uint32(symbol.Value) variableOffsets[variable{secName, symbol.Name}] = uint32(symbol.Value)
} }
spec, err := loadNakedSpec(btfSection.Open(), file.ByteOrder, sectionSizes, variableOffsets) return loadSpecFromELF(file, variableOffsets)
if err != nil {
return nil, err
}
if btfExtSection == nil {
return spec, nil
}
spec.funcInfos, spec.lineInfos, spec.coreRelos, err = parseExtInfos(btfExtSection.Open(), file.ByteOrder, spec.strings)
if err != nil {
return nil, fmt.Errorf("can't read ext info: %w", err)
}
return spec, nil
} }
func findBtfSections(file *internal.SafeELFFile) (*elf.Section, *elf.Section, map[string]uint32, error) { func loadSpecFromELF(file *internal.SafeELFFile, variableOffsets map[variable]uint32) (*Spec, error) {
var ( var (
btfSection *elf.Section btfSection *elf.Section
btfExtSection *elf.Section btfExtSection *elf.Section
@ -134,33 +109,45 @@ func findBtfSections(file *internal.SafeELFFile) (*elf.Section, *elf.Section, ma
} }
if sec.Size > math.MaxUint32 { if sec.Size > math.MaxUint32 {
return nil, nil, nil, fmt.Errorf("section %s exceeds maximum size", sec.Name) return nil, fmt.Errorf("section %s exceeds maximum size", sec.Name)
} }
sectionSizes[sec.Name] = uint32(sec.Size) sectionSizes[sec.Name] = uint32(sec.Size)
} }
} }
return btfSection, btfExtSection, sectionSizes, nil
}
func loadSpecFromVmlinux(rd io.ReaderAt) (*Spec, error) { if btfSection == nil {
file, err := internal.NewSafeELFFile(rd) return nil, fmt.Errorf("btf: %w", ErrNotFound)
}
spec, err := loadRawSpec(btfSection.Open(), file.ByteOrder, sectionSizes, variableOffsets)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer file.Close()
btfSection, _, _, err := findBtfSections(file) if btfExtSection == nil {
return spec, nil
}
spec.funcInfos, spec.lineInfos, spec.coreRelos, err = parseExtInfos(btfExtSection.Open(), file.ByteOrder, spec.strings)
if err != nil { if err != nil {
return nil, fmt.Errorf(".BTF ELF section: %s", err) return nil, fmt.Errorf("can't read ext info: %w", err)
} }
if btfSection == nil {
return nil, fmt.Errorf("unable to find .BTF ELF section") return spec, nil
}
return loadNakedSpec(btfSection.Open(), file.ByteOrder, nil, nil)
} }
func loadNakedSpec(btf io.ReadSeeker, bo binary.ByteOrder, sectionSizes map[string]uint32, variableOffsets map[variable]uint32) (*Spec, error) { // LoadRawSpec reads a blob of BTF data that isn't wrapped in an ELF file.
//
// Prefer using LoadSpecFromReader, since this function only supports a subset
// of BTF.
func LoadRawSpec(btf io.Reader, bo binary.ByteOrder) (*Spec, error) {
// This will return an error if we encounter a Datasec, since we can't fix
// it up.
return loadRawSpec(btf, bo, nil, nil)
}
func loadRawSpec(btf io.Reader, bo binary.ByteOrder, sectionSizes map[string]uint32, variableOffsets map[variable]uint32) (*Spec, error) {
rawTypes, rawStrings, err := parseBTF(btf, bo) rawTypes, rawStrings, err := parseBTF(btf, bo)
if err != nil { if err != nil {
return nil, err return nil, err
@ -217,7 +204,7 @@ func loadKernelSpec() (*Spec, error) {
if err == nil { if err == nil {
defer fh.Close() defer fh.Close()
return loadNakedSpec(fh, internal.NativeEndian, nil, nil) return LoadRawSpec(fh, internal.NativeEndian)
} }
// use same list of locations as libbpf // use same list of locations as libbpf
@ -241,14 +228,20 @@ func loadKernelSpec() (*Spec, error) {
} }
defer fh.Close() defer fh.Close()
return loadSpecFromVmlinux(fh) file, err := internal.NewSafeELFFile(fh)
if err != nil {
return nil, err
}
defer file.Close()
return loadSpecFromELF(file, nil)
} }
return nil, fmt.Errorf("no BTF for kernel version %s: %w", release, internal.ErrNotSupported) return nil, fmt.Errorf("no BTF for kernel version %s: %w", release, internal.ErrNotSupported)
} }
func parseBTF(btf io.ReadSeeker, bo binary.ByteOrder) ([]rawType, stringTable, error) { func parseBTF(btf io.Reader, bo binary.ByteOrder) ([]rawType, stringTable, error) {
rawBTF, err := ioutil.ReadAll(btf) rawBTF, err := io.ReadAll(btf)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("can't read BTF: %v", err) return nil, nil, fmt.Errorf("can't read BTF: %v", err)
} }
@ -357,6 +350,30 @@ func fixupDatasec(rawTypes []rawType, rawStrings stringTable, sectionSizes map[s
return nil return nil
} }
// Copy creates a copy of Spec.
func (s *Spec) Copy() *Spec {
types, _ := copyTypes(s.types, nil)
namedTypes := make(map[string][]NamedType)
for _, typ := range types {
if named, ok := typ.(NamedType); ok {
name := essentialName(named.TypeName())
namedTypes[name] = append(namedTypes[name], named)
}
}
// NB: Other parts of spec are not copied since they are immutable.
return &Spec{
s.rawTypes,
s.strings,
types,
namedTypes,
s.funcInfos,
s.lineInfos,
s.coreRelos,
s.byteOrder,
}
}
type marshalOpts struct { type marshalOpts struct {
ByteOrder binary.ByteOrder ByteOrder binary.ByteOrder
StripFuncLinkage bool StripFuncLinkage bool
@ -447,36 +464,37 @@ func (s *Spec) Program(name string, length uint64) (*Program, error) {
return &Program{s, length, funcInfos, lineInfos, relos}, nil return &Program{s, length, funcInfos, lineInfos, relos}, nil
} }
// Datasec returns the BTF required to create maps which represent data sections.
func (s *Spec) Datasec(name string) (*Map, error) {
var datasec Datasec
if err := s.FindType(name, &datasec); err != nil {
return nil, fmt.Errorf("data section %s: can't get BTF: %w", name, err)
}
m := NewMap(s, &Void{}, &datasec)
return &m, nil
}
// FindType searches for a type with a specific name. // FindType searches for a type with a specific name.
// //
// hint determines the type of the returned Type. // Called T a type that satisfies Type, typ must be a non-nil **T.
// On success, the address of the found type will be copied in typ.
// //
// Returns an error wrapping ErrNotFound if no matching // Returns an error wrapping ErrNotFound if no matching
// type exists in spec. // type exists in spec.
func (s *Spec) FindType(name string, typ Type) error { func (s *Spec) FindType(name string, typ interface{}) error {
var ( typValue := reflect.ValueOf(typ)
wanted = reflect.TypeOf(typ) if typValue.Kind() != reflect.Ptr {
candidate Type return fmt.Errorf("%T is not a pointer", typ)
) }
typPtr := typValue.Elem()
if !typPtr.CanSet() {
return fmt.Errorf("%T cannot be set", typ)
}
wanted := typPtr.Type()
if !wanted.AssignableTo(reflect.TypeOf((*Type)(nil)).Elem()) {
return fmt.Errorf("%T does not satisfy Type interface", typ)
}
var candidate Type
for _, typ := range s.namedTypes[essentialName(name)] { for _, typ := range s.namedTypes[essentialName(name)] {
if reflect.TypeOf(typ) != wanted { if reflect.TypeOf(typ) != wanted {
continue continue
} }
// Match against the full name, not just the essential one. // Match against the full name, not just the essential one.
if typ.name() != name { if typ.TypeName() != name {
continue continue
} }
@ -491,15 +509,15 @@ func (s *Spec) FindType(name string, typ Type) error {
return fmt.Errorf("type %s: %w", name, ErrNotFound) return fmt.Errorf("type %s: %w", name, ErrNotFound)
} }
cpy, _ := copyType(candidate, nil) typPtr.Set(reflect.ValueOf(candidate))
value := reflect.Indirect(reflect.ValueOf(cpy))
reflect.Indirect(reflect.ValueOf(typ)).Set(value)
return nil return nil
} }
// Handle is a reference to BTF loaded into the kernel. // Handle is a reference to BTF loaded into the kernel.
type Handle struct { type Handle struct {
fd *internal.FD spec *Spec
fd *internal.FD
} }
// NewHandle loads BTF into the kernel. // NewHandle loads BTF into the kernel.
@ -541,7 +559,32 @@ func NewHandle(spec *Spec) (*Handle, error) {
return nil, internal.ErrorWithLog(err, logBuf, logErr) return nil, internal.ErrorWithLog(err, logBuf, logErr)
} }
return &Handle{fd}, nil return &Handle{spec.Copy(), fd}, nil
}
// NewHandleFromID returns the BTF handle for a given id.
//
// Returns ErrNotExist, if there is no BTF with the given id.
//
// Requires CAP_SYS_ADMIN.
func NewHandleFromID(id ID) (*Handle, error) {
fd, err := internal.BPFObjGetFDByID(internal.BPF_BTF_GET_FD_BY_ID, uint32(id))
if err != nil {
return nil, fmt.Errorf("get BTF by id: %w", err)
}
info, err := newInfoFromFd(fd)
if err != nil {
_ = fd.Close()
return nil, fmt.Errorf("get BTF spec for handle: %w", err)
}
return &Handle{info.BTF, fd}, nil
}
// Spec returns the Spec that defined the BTF loaded into the kernel.
func (h *Handle) Spec() *Spec {
return h.spec
} }
// Close destroys the handle. // Close destroys the handle.
@ -563,43 +606,8 @@ func (h *Handle) FD() int {
// Map is the BTF for a map. // Map is the BTF for a map.
type Map struct { type Map struct {
spec *Spec Spec *Spec
key, value Type Key, Value Type
}
// NewMap returns a new Map containing the given values.
// The key and value arguments are initialized to Void if nil values are given.
func NewMap(spec *Spec, key Type, value Type) Map {
if key == nil {
key = &Void{}
}
if value == nil {
value = &Void{}
}
return Map{
spec: spec,
key: key,
value: value,
}
}
// MapSpec should be a method on Map, but is a free function
// to hide it from users of the ebpf package.
func MapSpec(m *Map) *Spec {
return m.spec
}
// MapKey should be a method on Map, but is a free function
// to hide it from users of the ebpf package.
func MapKey(m *Map) Type {
return m.key
}
// MapValue should be a method on Map, but is a free function
// to hide it from users of the ebpf package.
func MapValue(m *Map) Type {
return m.value
} }
// Program is the BTF information for a stream of instructions. // Program is the BTF information for a stream of instructions.
@ -610,68 +618,59 @@ type Program struct {
coreRelos coreRelos coreRelos coreRelos
} }
// ProgramSpec returns the Spec needed for loading function and line infos into the kernel. // Spec returns the BTF spec of this program.
// func (p *Program) Spec() *Spec {
// This is a free function instead of a method to hide it from users return p.spec
// of package ebpf.
func ProgramSpec(s *Program) *Spec {
return s.spec
} }
// ProgramAppend the information from other to the Program. // Append the information from other to the Program.
// func (p *Program) Append(other *Program) error {
// This is a free function instead of a method to hide it from users if other.spec != p.spec {
// of package ebpf. return fmt.Errorf("can't append program with different BTF specs")
func ProgramAppend(s, other *Program) error { }
funcInfos, err := s.funcInfos.append(other.funcInfos, s.length)
funcInfos, err := p.funcInfos.append(other.funcInfos, p.length)
if err != nil { if err != nil {
return fmt.Errorf("func infos: %w", err) return fmt.Errorf("func infos: %w", err)
} }
lineInfos, err := s.lineInfos.append(other.lineInfos, s.length) lineInfos, err := p.lineInfos.append(other.lineInfos, p.length)
if err != nil { if err != nil {
return fmt.Errorf("line infos: %w", err) return fmt.Errorf("line infos: %w", err)
} }
s.funcInfos = funcInfos p.funcInfos = funcInfos
s.lineInfos = lineInfos p.lineInfos = lineInfos
s.coreRelos = s.coreRelos.append(other.coreRelos, s.length) p.coreRelos = p.coreRelos.append(other.coreRelos, p.length)
s.length += other.length p.length += other.length
return nil return nil
} }
// ProgramFuncInfos returns the binary form of BTF function infos. // FuncInfos returns the binary form of BTF function infos.
// func (p *Program) FuncInfos() (recordSize uint32, bytes []byte, err error) {
// This is a free function instead of a method to hide it from users bytes, err = p.funcInfos.MarshalBinary()
// of package ebpf.
func ProgramFuncInfos(s *Program) (recordSize uint32, bytes []byte, err error) {
bytes, err = s.funcInfos.MarshalBinary()
if err != nil { if err != nil {
return 0, nil, err return 0, nil, fmt.Errorf("func infos: %w", err)
} }
return s.funcInfos.recordSize, bytes, nil return p.funcInfos.recordSize, bytes, nil
} }
// ProgramLineInfos returns the binary form of BTF line infos. // LineInfos returns the binary form of BTF line infos.
// func (p *Program) LineInfos() (recordSize uint32, bytes []byte, err error) {
// This is a free function instead of a method to hide it from users bytes, err = p.lineInfos.MarshalBinary()
// of package ebpf.
func ProgramLineInfos(s *Program) (recordSize uint32, bytes []byte, err error) {
bytes, err = s.lineInfos.MarshalBinary()
if err != nil { if err != nil {
return 0, nil, err return 0, nil, fmt.Errorf("line infos: %w", err)
} }
return s.lineInfos.recordSize, bytes, nil return p.lineInfos.recordSize, bytes, nil
} }
// ProgramFixups returns the changes required to adjust the program to the target. // Fixups returns the changes required to adjust the program to the target.
// //
// This is a free function instead of a method to hide it from users // Passing a nil target will relocate against the running kernel.
// of package ebpf. func (p *Program) Fixups(target *Spec) (COREFixups, error) {
func ProgramFixups(s *Program, target *Spec) (COREFixups, error) { if len(p.coreRelos) == 0 {
if len(s.coreRelos) == 0 {
return nil, nil return nil, nil
} }
@ -683,7 +682,7 @@ func ProgramFixups(s *Program, target *Spec) (COREFixups, error) {
} }
} }
return coreRelocate(s.spec, target, s.coreRelos) return coreRelocate(p.spec, target, p.coreRelos)
} }
type bpfLoadBTFAttr struct { type bpfLoadBTFAttr struct {

View File

@ -31,6 +31,8 @@ const (
// Added ~5.1 // Added ~5.1
kindVar kindVar
kindDatasec kindDatasec
// Added ~5.13
kindFloat
) )
// FuncLinkage describes BTF function linkage metadata. // FuncLinkage describes BTF function linkage metadata.
@ -54,7 +56,7 @@ const (
const ( const (
btfTypeKindShift = 24 btfTypeKindShift = 24
btfTypeKindLen = 4 btfTypeKindLen = 5
btfTypeVlenShift = 0 btfTypeVlenShift = 0
btfTypeVlenMask = 16 btfTypeVlenMask = 16
btfTypeKindFlagShift = 31 btfTypeKindFlagShift = 31
@ -67,8 +69,8 @@ type btfType struct {
/* "info" bits arrangement /* "info" bits arrangement
* bits 0-15: vlen (e.g. # of struct's members), linkage * bits 0-15: vlen (e.g. # of struct's members), linkage
* bits 16-23: unused * bits 16-23: unused
* bits 24-27: kind (e.g. int, ptr, array...etc) * bits 24-28: kind (e.g. int, ptr, array...etc)
* bits 28-30: unused * bits 29-30: unused
* bit 31: kind_flag, currently used by * bit 31: kind_flag, currently used by
* struct, union and fwd * struct, union and fwd
*/ */
@ -117,6 +119,8 @@ func (k btfKind) String() string {
return "Variable" return "Variable"
case kindDatasec: case kindDatasec:
return "Section" return "Section"
case kindFloat:
return "Float"
default: default:
return fmt.Sprintf("Unknown (%d)", k) return fmt.Sprintf("Unknown (%d)", k)
} }
@ -260,6 +264,7 @@ func readTypes(r io.Reader, bo binary.ByteOrder) ([]rawType, error) {
data = new(btfVariable) data = new(btfVariable)
case kindDatasec: case kindDatasec:
data = make([]btfVarSecinfo, header.Vlen()) data = make([]btfVarSecinfo, header.Vlen())
case kindFloat:
default: default:
return nil, fmt.Errorf("type id %v: unknown kind: %v", id, header.Kind()) return nil, fmt.Errorf("type id %v: unknown kind: %v", id, header.Kind())
} }

View File

@ -234,13 +234,13 @@ func coreRelocate(local, target *Spec, relos coreRelos) (COREFixups, error) {
} }
localType := local.types[id] localType := local.types[id]
named, ok := localType.(namedType) named, ok := localType.(NamedType)
if !ok || named.name() == "" { if !ok || named.TypeName() == "" {
return nil, fmt.Errorf("relocate unnamed or anonymous type %s: %w", localType, ErrNotSupported) return nil, fmt.Errorf("relocate unnamed or anonymous type %s: %w", localType, ErrNotSupported)
} }
relos := relosByID[id] relos := relosByID[id]
targets := target.namedTypes[named.essentialName()] targets := target.namedTypes[essentialName(named.TypeName())]
fixups, err := coreCalculateFixups(localType, targets, relos) fixups, err := coreCalculateFixups(localType, targets, relos)
if err != nil { if err != nil {
return nil, fmt.Errorf("relocate %s: %w", localType, err) return nil, fmt.Errorf("relocate %s: %w", localType, err)
@ -262,7 +262,7 @@ var errImpossibleRelocation = errors.New("impossible relocation")
// //
// The best target is determined by scoring: the less poisoning we have to do // The best target is determined by scoring: the less poisoning we have to do
// the better the target is. // the better the target is.
func coreCalculateFixups(local Type, targets []namedType, relos coreRelos) ([]COREFixup, error) { func coreCalculateFixups(local Type, targets []NamedType, relos coreRelos) ([]COREFixup, error) {
localID := local.ID() localID := local.ID()
local, err := copyType(local, skipQualifierAndTypedef) local, err := copyType(local, skipQualifierAndTypedef)
if err != nil { if err != nil {
@ -467,8 +467,8 @@ func parseCoreAccessor(accessor string) (coreAccessor, error) {
return nil, fmt.Errorf("empty accessor") return nil, fmt.Errorf("empty accessor")
} }
var result coreAccessor
parts := strings.Split(accessor, ":") parts := strings.Split(accessor, ":")
result := make(coreAccessor, 0, len(parts))
for _, part := range parts { for _, part := range parts {
// 31 bits to avoid overflowing int on 32 bit platforms. // 31 bits to avoid overflowing int on 32 bit platforms.
index, err := strconv.ParseUint(part, 10, 31) index, err := strconv.ParseUint(part, 10, 31)
@ -564,7 +564,7 @@ func coreFindField(local Type, localAcc coreAccessor, target Type) (_, _ coreFie
// This is an anonymous struct or union, ignore it. // This is an anonymous struct or union, ignore it.
local = localMember.Type local = localMember.Type
localOffset += localMember.Offset localOffset += localMember.OffsetBits
localMaybeFlex = false localMaybeFlex = false
continue continue
} }
@ -585,10 +585,10 @@ func coreFindField(local Type, localAcc coreAccessor, target Type) (_, _ coreFie
local = localMember.Type local = localMember.Type
localMaybeFlex = acc == len(localMembers)-1 localMaybeFlex = acc == len(localMembers)-1
localOffset += localMember.Offset localOffset += localMember.OffsetBits
target = targetMember.Type target = targetMember.Type
targetMaybeFlex = last targetMaybeFlex = last
targetOffset += targetMember.Offset targetOffset += targetMember.OffsetBits
case *Array: case *Array:
// For arrays, acc is the index in the target. // For arrays, acc is the index in the target.
@ -639,7 +639,7 @@ func coreFindField(local Type, localAcc coreAccessor, target Type) (_, _ coreFie
// coreFindMember finds a member in a composite type while handling anonymous // coreFindMember finds a member in a composite type while handling anonymous
// structs and unions. // structs and unions.
func coreFindMember(typ composite, name Name) (Member, bool, error) { func coreFindMember(typ composite, name string) (Member, bool, error) {
if name == "" { if name == "" {
return Member{}, false, errors.New("can't search for anonymous member") return Member{}, false, errors.New("can't search for anonymous member")
} }
@ -670,7 +670,7 @@ func coreFindMember(typ composite, name Name) (Member, bool, error) {
for j, member := range members { for j, member := range members {
if member.Name == name { if member.Name == name {
// NB: This is safe because member is a copy. // NB: This is safe because member is a copy.
member.Offset += target.offset member.OffsetBits += target.offset
return member, j == len(members)-1, nil return member, j == len(members)-1, nil
} }
@ -685,7 +685,7 @@ func coreFindMember(typ composite, name Name) (Member, bool, error) {
return Member{}, false, fmt.Errorf("anonymous non-composite type %T not allowed", member.Type) return Member{}, false, fmt.Errorf("anonymous non-composite type %T not allowed", member.Type)
} }
targets = append(targets, offsetTarget{comp, target.offset + member.Offset}) targets = append(targets, offsetTarget{comp, target.offset + member.OffsetBits})
} }
} }
@ -704,9 +704,9 @@ func coreFindEnumValue(local Type, localAcc coreAccessor, target Type) (localVal
return nil, nil, errImpossibleRelocation return nil, nil, errImpossibleRelocation
} }
localName := localValue.Name.essentialName() localName := essentialName(localValue.Name)
for i, targetValue := range targetEnum.Values { for i, targetValue := range targetEnum.Values {
if targetValue.Name.essentialName() != localName { if essentialName(targetValue.Name) != localName {
continue continue
} }
@ -813,6 +813,7 @@ func coreAreTypesCompatible(localType Type, targetType Type) error {
* least one of enums should be anonymous; * least one of enums should be anonymous;
* - for ENUMs, check sizes, names are ignored; * - for ENUMs, check sizes, names are ignored;
* - for INT, size and signedness are ignored; * - for INT, size and signedness are ignored;
* - any two FLOATs are always compatible;
* - for ARRAY, dimensionality is ignored, element types are checked for * - for ARRAY, dimensionality is ignored, element types are checked for
* compatibility recursively; * compatibility recursively;
* [ NB: coreAreMembersCompatible doesn't recurse, this check is done * [ NB: coreAreMembersCompatible doesn't recurse, this check is done
@ -848,16 +849,16 @@ func coreAreMembersCompatible(localType Type, targetType Type) error {
} }
switch lv := localType.(type) { switch lv := localType.(type) {
case *Array, *Pointer: case *Array, *Pointer, *Float:
return nil return nil
case *Enum: case *Enum:
tv := targetType.(*Enum) tv := targetType.(*Enum)
return doNamesMatch(lv.name(), tv.name()) return doNamesMatch(lv.Name, tv.Name)
case *Fwd: case *Fwd:
tv := targetType.(*Fwd) tv := targetType.(*Fwd)
return doNamesMatch(lv.name(), tv.name()) return doNamesMatch(lv.Name, tv.Name)
case *Int: case *Int:
tv := targetType.(*Int) tv := targetType.(*Int)

View File

@ -7,7 +7,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/asm"
"github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal"
@ -64,7 +63,7 @@ func parseExtInfos(r io.ReadSeeker, bo binary.ByteOrder, strings stringTable) (f
// Of course, the .BTF.ext header has different semantics than the // Of course, the .BTF.ext header has different semantics than the
// .BTF ext header. We need to ignore non-null values. // .BTF ext header. We need to ignore non-null values.
_, err = io.CopyN(ioutil.Discard, r, remainder) _, err = io.CopyN(io.Discard, r, remainder)
if err != nil { if err != nil {
return nil, nil, nil, fmt.Errorf("header padding: %v", err) return nil, nil, nil, fmt.Errorf("header padding: %v", err)
} }
@ -114,11 +113,16 @@ type extInfoRecord struct {
} }
type extInfo struct { type extInfo struct {
byteOrder binary.ByteOrder
recordSize uint32 recordSize uint32
records []extInfoRecord records []extInfoRecord
} }
func (ei extInfo) append(other extInfo, offset uint64) (extInfo, error) { func (ei extInfo) append(other extInfo, offset uint64) (extInfo, error) {
if other.byteOrder != ei.byteOrder {
return extInfo{}, fmt.Errorf("ext_info byte order mismatch, want %v (got %v)", ei.byteOrder, other.byteOrder)
}
if other.recordSize != ei.recordSize { if other.recordSize != ei.recordSize {
return extInfo{}, fmt.Errorf("ext_info record size mismatch, want %d (got %d)", ei.recordSize, other.recordSize) return extInfo{}, fmt.Errorf("ext_info record size mismatch, want %d (got %d)", ei.recordSize, other.recordSize)
} }
@ -131,10 +135,14 @@ func (ei extInfo) append(other extInfo, offset uint64) (extInfo, error) {
Opaque: info.Opaque, Opaque: info.Opaque,
}) })
} }
return extInfo{ei.recordSize, records}, nil return extInfo{ei.byteOrder, ei.recordSize, records}, nil
} }
func (ei extInfo) MarshalBinary() ([]byte, error) { func (ei extInfo) MarshalBinary() ([]byte, error) {
if ei.byteOrder != internal.NativeEndian {
return nil, fmt.Errorf("%s is not the native byte order", ei.byteOrder)
}
if len(ei.records) == 0 { if len(ei.records) == 0 {
return nil, nil return nil, nil
} }
@ -197,6 +205,7 @@ func parseExtInfo(r io.Reader, bo binary.ByteOrder, strings stringTable) (map[st
} }
result[secName] = extInfo{ result[secName] = extInfo{
bo,
recordSize, recordSize,
records, records,
} }

View File

@ -1,3 +1,4 @@
//go:build gofuzz
// +build gofuzz // +build gofuzz
// Use with https://github.com/dvyukov/go-fuzz // Use with https://github.com/dvyukov/go-fuzz

48
vendor/github.com/cilium/ebpf/internal/btf/info.go generated vendored Normal file
View File

@ -0,0 +1,48 @@
package btf
import (
"bytes"
"github.com/cilium/ebpf/internal"
)
// info describes a BTF object.
type info struct {
BTF *Spec
ID ID
// Name is an identifying name for the BTF, currently only used by the
// kernel.
Name string
// KernelBTF is true if the BTf originated with the kernel and not
// userspace.
KernelBTF bool
}
func newInfoFromFd(fd *internal.FD) (*info, error) {
// We invoke the syscall once with a empty BTF and name buffers to get size
// information to allocate buffers. Then we invoke it a second time with
// buffers to receive the data.
bpfInfo, err := bpfGetBTFInfoByFD(fd, nil, nil)
if err != nil {
return nil, err
}
btfBuffer := make([]byte, bpfInfo.btfSize)
nameBuffer := make([]byte, bpfInfo.nameLen)
bpfInfo, err = bpfGetBTFInfoByFD(fd, btfBuffer, nameBuffer)
if err != nil {
return nil, err
}
spec, err := loadRawSpec(bytes.NewReader(btfBuffer), internal.NativeEndian, nil, nil)
if err != nil {
return nil, err
}
return &info{
BTF: spec,
ID: ID(bpfInfo.id),
Name: internal.CString(nameBuffer),
KernelBTF: bpfInfo.kernelBTF != 0,
}, nil
}

View File

@ -5,13 +5,12 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
) )
type stringTable []byte type stringTable []byte
func readStringTable(r io.Reader) (stringTable, error) { func readStringTable(r io.Reader) (stringTable, error) {
contents, err := ioutil.ReadAll(r) contents, err := io.ReadAll(r)
if err != nil { if err != nil {
return nil, fmt.Errorf("can't read string table: %v", err) return nil, fmt.Errorf("can't read string table: %v", err)
} }
@ -53,8 +52,3 @@ func (st stringTable) Lookup(offset uint32) (string, error) {
return string(str[:end]), nil return string(str[:end]), nil
} }
func (st stringTable) LookupName(offset uint32) (Name, error) {
str, err := st.Lookup(offset)
return Name(str), err
}

31
vendor/github.com/cilium/ebpf/internal/btf/syscalls.go generated vendored Normal file
View File

@ -0,0 +1,31 @@
package btf
import (
"fmt"
"unsafe"
"github.com/cilium/ebpf/internal"
)
type bpfBTFInfo struct {
btf internal.Pointer
btfSize uint32
id uint32
name internal.Pointer
nameLen uint32
kernelBTF uint32
}
func bpfGetBTFInfoByFD(fd *internal.FD, btf, name []byte) (*bpfBTFInfo, error) {
info := bpfBTFInfo{
btf: internal.NewSlicePointer(btf),
btfSize: uint32(len(btf)),
name: internal.NewSlicePointer(name),
nameLen: uint32(len(name)),
}
if err := internal.BPFObjGetInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info)); err != nil {
return nil, fmt.Errorf("can't get program info: %w", err)
}
return &info, nil
}

View File

@ -30,27 +30,26 @@ type Type interface {
walk(*typeDeque) walk(*typeDeque)
} }
// namedType is a type with a name. // NamedType is a type with a name.
// type NamedType interface {
// Most named types simply embed Name.
type namedType interface {
Type Type
name() string
essentialName() string // Name of the type, empty for anonymous types.
TypeName() string
} }
// Name identifies a type. var (
// _ NamedType = (*Int)(nil)
// Anonymous types have an empty name. _ NamedType = (*Struct)(nil)
type Name string _ NamedType = (*Union)(nil)
_ NamedType = (*Enum)(nil)
func (n Name) name() string { _ NamedType = (*Fwd)(nil)
return string(n) _ NamedType = (*Func)(nil)
} _ NamedType = (*Typedef)(nil)
_ NamedType = (*Var)(nil)
func (n Name) essentialName() string { _ NamedType = (*Datasec)(nil)
return essentialName(string(n)) _ NamedType = (*Float)(nil)
} )
// Void is the unit type of BTF. // Void is the unit type of BTF.
type Void struct{} type Void struct{}
@ -72,19 +71,17 @@ const (
// Int is an integer of a given length. // Int is an integer of a given length.
type Int struct { type Int struct {
TypeID TypeID
Name Name string
// The size of the integer in bytes. // The size of the integer in bytes.
Size uint32 Size uint32
Encoding IntEncoding Encoding IntEncoding
// Offset is the starting bit offset. Currently always 0. // OffsetBits is the starting bit offset. Currently always 0.
// See https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-int // See https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-int
Offset uint32 OffsetBits uint32
Bits byte Bits byte
} }
var _ namedType = (*Int)(nil)
func (i *Int) String() string { func (i *Int) String() string {
var s strings.Builder var s strings.Builder
@ -110,15 +107,16 @@ func (i *Int) String() string {
return s.String() return s.String()
} }
func (i *Int) size() uint32 { return i.Size } func (i *Int) TypeName() string { return i.Name }
func (i *Int) walk(*typeDeque) {} func (i *Int) size() uint32 { return i.Size }
func (i *Int) walk(*typeDeque) {}
func (i *Int) copy() Type { func (i *Int) copy() Type {
cpy := *i cpy := *i
return &cpy return &cpy
} }
func (i *Int) isBitfield() bool { func (i *Int) isBitfield() bool {
return i.Offset > 0 return i.OffsetBits > 0
} }
// Pointer is a pointer to another type. // Pointer is a pointer to another type.
@ -158,7 +156,7 @@ func (arr *Array) copy() Type {
// Struct is a compound type of consecutive members. // Struct is a compound type of consecutive members.
type Struct struct { type Struct struct {
TypeID TypeID
Name Name string
// The size of the struct including padding, in bytes // The size of the struct including padding, in bytes
Size uint32 Size uint32
Members []Member Members []Member
@ -168,6 +166,8 @@ func (s *Struct) String() string {
return fmt.Sprintf("struct#%d[%q]", s.TypeID, s.Name) return fmt.Sprintf("struct#%d[%q]", s.TypeID, s.Name)
} }
func (s *Struct) TypeName() string { return s.Name }
func (s *Struct) size() uint32 { return s.Size } func (s *Struct) size() uint32 { return s.Size }
func (s *Struct) walk(tdq *typeDeque) { func (s *Struct) walk(tdq *typeDeque) {
@ -189,7 +189,7 @@ func (s *Struct) members() []Member {
// Union is a compound type where members occupy the same memory. // Union is a compound type where members occupy the same memory.
type Union struct { type Union struct {
TypeID TypeID
Name Name string
// The size of the union including padding, in bytes. // The size of the union including padding, in bytes.
Size uint32 Size uint32
Members []Member Members []Member
@ -199,6 +199,8 @@ func (u *Union) String() string {
return fmt.Sprintf("union#%d[%q]", u.TypeID, u.Name) return fmt.Sprintf("union#%d[%q]", u.TypeID, u.Name)
} }
func (u *Union) TypeName() string { return u.Name }
func (u *Union) size() uint32 { return u.Size } func (u *Union) size() uint32 { return u.Size }
func (u *Union) walk(tdq *typeDeque) { func (u *Union) walk(tdq *typeDeque) {
@ -236,17 +238,17 @@ var (
// //
// It is not a valid Type. // It is not a valid Type.
type Member struct { type Member struct {
Name Name string
Type Type Type Type
// Offset is the bit offset of this member // OffsetBits is the bit offset of this member.
Offset uint32 OffsetBits uint32
BitfieldSize uint32 BitfieldSize uint32
} }
// Enum lists possible values. // Enum lists possible values.
type Enum struct { type Enum struct {
TypeID TypeID
Name Name string
Values []EnumValue Values []EnumValue
} }
@ -254,11 +256,13 @@ func (e *Enum) String() string {
return fmt.Sprintf("enum#%d[%q]", e.TypeID, e.Name) return fmt.Sprintf("enum#%d[%q]", e.TypeID, e.Name)
} }
func (e *Enum) TypeName() string { return e.Name }
// EnumValue is part of an Enum // EnumValue is part of an Enum
// //
// Is is not a valid Type // Is is not a valid Type
type EnumValue struct { type EnumValue struct {
Name Name string
Value int32 Value int32
} }
@ -294,7 +298,7 @@ func (fk FwdKind) String() string {
// Fwd is a forward declaration of a Type. // Fwd is a forward declaration of a Type.
type Fwd struct { type Fwd struct {
TypeID TypeID
Name Name string
Kind FwdKind Kind FwdKind
} }
@ -302,6 +306,8 @@ func (f *Fwd) String() string {
return fmt.Sprintf("fwd#%d[%s %q]", f.TypeID, f.Kind, f.Name) return fmt.Sprintf("fwd#%d[%s %q]", f.TypeID, f.Kind, f.Name)
} }
func (f *Fwd) TypeName() string { return f.Name }
func (f *Fwd) walk(*typeDeque) {} func (f *Fwd) walk(*typeDeque) {}
func (f *Fwd) copy() Type { func (f *Fwd) copy() Type {
cpy := *f cpy := *f
@ -311,7 +317,7 @@ func (f *Fwd) copy() Type {
// Typedef is an alias of a Type. // Typedef is an alias of a Type.
type Typedef struct { type Typedef struct {
TypeID TypeID
Name Name string
Type Type Type Type
} }
@ -319,6 +325,8 @@ func (td *Typedef) String() string {
return fmt.Sprintf("typedef#%d[%q #%d]", td.TypeID, td.Name, td.Type.ID()) return fmt.Sprintf("typedef#%d[%q #%d]", td.TypeID, td.Name, td.Type.ID())
} }
func (td *Typedef) TypeName() string { return td.Name }
func (td *Typedef) walk(tdq *typeDeque) { tdq.push(&td.Type) } func (td *Typedef) walk(tdq *typeDeque) { tdq.push(&td.Type) }
func (td *Typedef) copy() Type { func (td *Typedef) copy() Type {
cpy := *td cpy := *td
@ -379,7 +387,7 @@ func (r *Restrict) copy() Type {
// Func is a function definition. // Func is a function definition.
type Func struct { type Func struct {
TypeID TypeID
Name Name string
Type Type Type Type
Linkage FuncLinkage Linkage FuncLinkage
} }
@ -388,6 +396,8 @@ func (f *Func) String() string {
return fmt.Sprintf("func#%d[%s %q proto=#%d]", f.TypeID, f.Linkage, f.Name, f.Type.ID()) return fmt.Sprintf("func#%d[%s %q proto=#%d]", f.TypeID, f.Linkage, f.Name, f.Type.ID())
} }
func (f *Func) TypeName() string { return f.Name }
func (f *Func) walk(tdq *typeDeque) { tdq.push(&f.Type) } func (f *Func) walk(tdq *typeDeque) { tdq.push(&f.Type) }
func (f *Func) copy() Type { func (f *Func) copy() Type {
cpy := *f cpy := *f
@ -426,14 +436,14 @@ func (fp *FuncProto) copy() Type {
} }
type FuncParam struct { type FuncParam struct {
Name Name string
Type Type Type Type
} }
// Var is a global variable. // Var is a global variable.
type Var struct { type Var struct {
TypeID TypeID
Name Name string
Type Type Type Type
Linkage VarLinkage Linkage VarLinkage
} }
@ -442,6 +452,8 @@ func (v *Var) String() string {
return fmt.Sprintf("var#%d[%s %q]", v.TypeID, v.Linkage, v.Name) return fmt.Sprintf("var#%d[%s %q]", v.TypeID, v.Linkage, v.Name)
} }
func (v *Var) TypeName() string { return v.Name }
func (v *Var) walk(tdq *typeDeque) { tdq.push(&v.Type) } func (v *Var) walk(tdq *typeDeque) { tdq.push(&v.Type) }
func (v *Var) copy() Type { func (v *Var) copy() Type {
cpy := *v cpy := *v
@ -451,7 +463,7 @@ func (v *Var) copy() Type {
// Datasec is a global program section containing data. // Datasec is a global program section containing data.
type Datasec struct { type Datasec struct {
TypeID TypeID
Name Name string
Size uint32 Size uint32
Vars []VarSecinfo Vars []VarSecinfo
} }
@ -460,6 +472,8 @@ func (ds *Datasec) String() string {
return fmt.Sprintf("section#%d[%q]", ds.TypeID, ds.Name) return fmt.Sprintf("section#%d[%q]", ds.TypeID, ds.Name)
} }
func (ds *Datasec) TypeName() string { return ds.Name }
func (ds *Datasec) size() uint32 { return ds.Size } func (ds *Datasec) size() uint32 { return ds.Size }
func (ds *Datasec) walk(tdq *typeDeque) { func (ds *Datasec) walk(tdq *typeDeque) {
@ -475,7 +489,7 @@ func (ds *Datasec) copy() Type {
return &cpy return &cpy
} }
// VarSecinfo describes variable in a Datasec // VarSecinfo describes variable in a Datasec.
// //
// It is not a valid Type. // It is not a valid Type.
type VarSecinfo struct { type VarSecinfo struct {
@ -484,6 +498,27 @@ type VarSecinfo struct {
Size uint32 Size uint32
} }
// Float is a float of a given length.
type Float struct {
TypeID
Name string
// The size of the float in bytes.
Size uint32
}
func (f *Float) String() string {
return fmt.Sprintf("float%d#%d[%q]", f.Size*8, f.TypeID, f.Name)
}
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
}
type sizer interface { type sizer interface {
size() uint32 size() uint32
} }
@ -565,14 +600,36 @@ func Sizeof(typ Type) (int, error) {
// //
// Returns any errors from transform verbatim. // Returns any errors from transform verbatim.
func copyType(typ Type, transform func(Type) (Type, error)) (Type, error) { func copyType(typ Type, transform func(Type) (Type, error)) (Type, error) {
var ( copies := make(copier)
copies = make(map[Type]Type) return typ, copies.copy(&typ, transform)
work typeDeque }
)
for t := &typ; t != nil; t = work.pop() { // copy a slice of Types recursively.
//
// Types may form a cycle.
//
// Returns any errors from transform verbatim.
func copyTypes(types []Type, transform func(Type) (Type, error)) ([]Type, error) {
result := make([]Type, len(types))
copy(result, types)
copies := make(copier)
for i := range result {
if err := copies.copy(&result[i], transform); err != nil {
return nil, err
}
}
return result, nil
}
type copier map[Type]Type
func (c copier) copy(typ *Type, transform func(Type) (Type, error)) error {
var work typeDeque
for t := typ; t != nil; t = work.pop() {
// *t is the identity of the type. // *t is the identity of the type.
if cpy := copies[*t]; cpy != nil { if cpy := c[*t]; cpy != nil {
*t = cpy *t = cpy
continue continue
} }
@ -581,21 +638,21 @@ func copyType(typ Type, transform func(Type) (Type, error)) (Type, error) {
if transform != nil { if transform != nil {
tf, err := transform(*t) tf, err := transform(*t)
if err != nil { if err != nil {
return nil, fmt.Errorf("copy %s: %w", typ, err) return fmt.Errorf("copy %s: %w", *t, err)
} }
cpy = tf.copy() cpy = tf.copy()
} else { } else {
cpy = (*t).copy() cpy = (*t).copy()
} }
copies[*t] = cpy c[*t] = cpy
*t = cpy *t = cpy
// Mark any nested types for copying. // Mark any nested types for copying.
cpy.walk(&work) cpy.walk(&work)
} }
return typ, nil return nil
} }
// typeDeque keeps track of pointers to types which still // typeDeque keeps track of pointers to types which still
@ -606,6 +663,10 @@ type typeDeque struct {
mask uint64 mask uint64
} }
func (dq *typeDeque) empty() bool {
return dq.read == dq.write
}
// push adds a type to the stack. // push adds a type to the stack.
func (dq *typeDeque) push(t *Type) { func (dq *typeDeque) push(t *Type) {
if dq.write-dq.read < uint64(len(dq.types)) { if dq.write-dq.read < uint64(len(dq.types)) {
@ -632,7 +693,7 @@ func (dq *typeDeque) push(t *Type) {
// shift returns the first element or null. // shift returns the first element or null.
func (dq *typeDeque) shift() *Type { func (dq *typeDeque) shift() *Type {
if dq.read == dq.write { if dq.empty() {
return nil return nil
} }
@ -645,7 +706,7 @@ func (dq *typeDeque) shift() *Type {
// pop returns the last element or null. // pop returns the last element or null.
func (dq *typeDeque) pop() *Type { func (dq *typeDeque) pop() *Type {
if dq.read == dq.write { if dq.empty() {
return nil return nil
} }
@ -674,7 +735,7 @@ func (dq *typeDeque) all() []*Type {
// Returns a map of named types (so, where NameOff is non-zero) and a slice of types // Returns a map of named types (so, where NameOff is non-zero) and a slice of types
// indexed by TypeID. Since BTF ignores compilation units, multiple types may share // indexed by TypeID. Since BTF ignores compilation units, multiple types may share
// the same name. A Type may form a cyclic graph by pointing at itself. // the same name. A Type may form a cyclic graph by pointing at itself.
func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (types []Type, namedTypes map[string][]namedType, err error) { func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (types []Type, namedTypes map[string][]NamedType, err error) {
type fixupDef struct { type fixupDef struct {
id TypeID id TypeID
expectedKind btfKind expectedKind btfKind
@ -691,17 +752,17 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (types []Type,
// work, since otherwise append might re-allocate members. // work, since otherwise append might re-allocate members.
members := make([]Member, 0, len(raw)) members := make([]Member, 0, len(raw))
for i, btfMember := range raw { for i, btfMember := range raw {
name, err := rawStrings.LookupName(btfMember.NameOff) name, err := rawStrings.Lookup(btfMember.NameOff)
if err != nil { if err != nil {
return nil, fmt.Errorf("can't get name for member %d: %w", i, err) return nil, fmt.Errorf("can't get name for member %d: %w", i, err)
} }
m := Member{ m := Member{
Name: name, Name: name,
Offset: btfMember.Offset, OffsetBits: btfMember.Offset,
} }
if kindFlag { if kindFlag {
m.BitfieldSize = btfMember.Offset >> 24 m.BitfieldSize = btfMember.Offset >> 24
m.Offset &= 0xffffff m.OffsetBits &= 0xffffff
} }
members = append(members, m) members = append(members, m)
} }
@ -713,7 +774,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (types []Type,
types = make([]Type, 0, len(rawTypes)) types = make([]Type, 0, len(rawTypes))
types = append(types, (*Void)(nil)) types = append(types, (*Void)(nil))
namedTypes = make(map[string][]namedType) namedTypes = make(map[string][]NamedType)
for i, raw := range rawTypes { for i, raw := range rawTypes {
var ( var (
@ -723,7 +784,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (types []Type,
typ Type typ Type
) )
name, err := rawStrings.LookupName(raw.NameOff) name, err := rawStrings.Lookup(raw.NameOff)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("get name for type id %d: %w", id, err) return nil, nil, fmt.Errorf("get name for type id %d: %w", id, err)
} }
@ -765,7 +826,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (types []Type,
rawvals := raw.data.([]btfEnum) rawvals := raw.data.([]btfEnum)
vals := make([]EnumValue, 0, len(rawvals)) vals := make([]EnumValue, 0, len(rawvals))
for i, btfVal := range rawvals { for i, btfVal := range rawvals {
name, err := rawStrings.LookupName(btfVal.NameOff) name, err := rawStrings.Lookup(btfVal.NameOff)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("get name for enum value %d: %s", i, err) return nil, nil, fmt.Errorf("get name for enum value %d: %s", i, err)
} }
@ -812,7 +873,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (types []Type,
rawparams := raw.data.([]btfParam) rawparams := raw.data.([]btfParam)
params := make([]FuncParam, 0, len(rawparams)) params := make([]FuncParam, 0, len(rawparams))
for i, param := range rawparams { for i, param := range rawparams {
name, err := rawStrings.LookupName(param.NameOff) name, err := rawStrings.Lookup(param.NameOff)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("get name for func proto parameter %d: %s", i, err) return nil, nil, fmt.Errorf("get name for func proto parameter %d: %s", i, err)
} }
@ -848,14 +909,17 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (types []Type,
} }
typ = &Datasec{id, name, raw.SizeType, vars} typ = &Datasec{id, name, raw.SizeType, vars}
case kindFloat:
typ = &Float{id, name, raw.Size()}
default: default:
return nil, nil, fmt.Errorf("type id %d: unknown kind: %v", id, raw.Kind()) return nil, nil, fmt.Errorf("type id %d: unknown kind: %v", id, raw.Kind())
} }
types = append(types, typ) types = append(types, typ)
if named, ok := typ.(namedType); ok { if named, ok := typ.(NamedType); ok {
if name := essentialName(named.name()); name != "" { if name := essentialName(named.TypeName()); name != "" {
namedTypes[name] = append(namedTypes[name], named) namedTypes[name] = append(namedTypes[name], named)
} }
} }

View File

@ -2,7 +2,7 @@ package internal
import ( import (
"fmt" "fmt"
"io/ioutil" "os"
"strings" "strings"
"sync" "sync"
) )
@ -24,7 +24,7 @@ func PossibleCPUs() (int, error) {
} }
func parseCPUsFromFile(path string) (int, error) { func parseCPUsFromFile(path string) (int, error) {
spec, err := ioutil.ReadFile(path) spec, err := os.ReadFile(path)
if err != nil { if err != nil {
return 0, err return 0, err
} }

View File

@ -1,3 +1,4 @@
//go:build armbe || mips || mips64p32
// +build armbe mips mips64p32 // +build armbe mips mips64p32
package internal package internal

View File

@ -1,3 +1,4 @@
//go:build 386 || amd64p32 || arm || mipsle || mips64p32le
// +build 386 amd64p32 arm mipsle mips64p32le // +build 386 amd64p32 arm mipsle mips64p32le
package internal package internal

View File

@ -1,5 +1,5 @@
// +build !386,!amd64p32,!arm,!mipsle,!mips64p32le //go:build !386 && !amd64p32 && !arm && !mipsle && !mips64p32le && !armbe && !mips && !mips64p32
// +build !armbe,!mips,!mips64p32 // +build !386,!amd64p32,!arm,!mipsle,!mips64p32le,!armbe,!mips,!mips64p32
package internal package internal

View File

@ -1,6 +1,7 @@
package internal package internal
import ( import (
"errors"
"fmt" "fmt"
"path/filepath" "path/filepath"
"runtime" "runtime"
@ -68,6 +69,48 @@ func BPF(cmd BPFCmd, attr unsafe.Pointer, size uintptr) (uintptr, error) {
return r1, err return r1, err
} }
type BPFProgLoadAttr struct {
ProgType uint32
InsCount uint32
Instructions Pointer
License Pointer
LogLevel uint32
LogSize uint32
LogBuf Pointer
KernelVersion uint32 // since 4.1 2541517c32be
ProgFlags uint32 // since 4.11 e07b98d9bffe
ProgName BPFObjName // since 4.15 067cae47771c
ProgIfIndex uint32 // since 4.15 1f6f4cb7ba21
ExpectedAttachType uint32 // since 4.17 5e43f899b03a
ProgBTFFd uint32
FuncInfoRecSize uint32
FuncInfo Pointer
FuncInfoCnt uint32
LineInfoRecSize uint32
LineInfo Pointer
LineInfoCnt uint32
AttachBTFID uint32
AttachProgFd uint32
}
// BPFProgLoad wraps BPF_PROG_LOAD.
func BPFProgLoad(attr *BPFProgLoadAttr) (*FD, error) {
for {
fd, err := BPF(BPF_PROG_LOAD, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
// As of ~4.20 the verifier can be interrupted by a signal,
// and returns EAGAIN in that case.
if errors.Is(err, unix.EAGAIN) {
continue
}
if err != nil {
return nil, err
}
return NewFD(uint32(fd)), nil
}
}
type BPFProgAttachAttr struct { type BPFProgAttachAttr struct {
TargetFd uint32 TargetFd uint32
AttachBpfFd uint32 AttachBpfFd uint32
@ -180,6 +223,22 @@ func BPFObjGetInfoByFD(fd *FD, info unsafe.Pointer, size uintptr) error {
return nil return nil
} }
type bpfGetFDByIDAttr struct {
id uint32
next uint32
}
// BPFObjGetInfoByFD wraps BPF_*_GET_FD_BY_ID.
//
// Available from 4.13.
func BPFObjGetFDByID(cmd BPFCmd, id uint32) (*FD, error) {
attr := bpfGetFDByIDAttr{
id: id,
}
ptr, err := BPF(cmd, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
return NewFD(uint32(ptr)), err
}
// BPFObjName is a null-terminated string made up of // BPFObjName is a null-terminated string made up of
// 'A-Za-z0-9_' characters. // 'A-Za-z0-9_' characters.
type BPFObjName [unix.BPF_OBJ_NAME_LEN]byte type BPFObjName [unix.BPF_OBJ_NAME_LEN]byte

View File

@ -1,3 +1,4 @@
//go:build linux
// +build linux // +build linux
package unix package unix
@ -20,10 +21,11 @@ const (
EPERM = linux.EPERM EPERM = linux.EPERM
ESRCH = linux.ESRCH ESRCH = linux.ESRCH
ENODEV = linux.ENODEV ENODEV = linux.ENODEV
EBADF = linux.EBADF
E2BIG = linux.E2BIG
// ENOTSUPP is not the same as ENOTSUP or EOPNOTSUP // ENOTSUPP is not the same as ENOTSUP or EOPNOTSUP
ENOTSUPP = syscall.Errno(0x20c) ENOTSUPP = syscall.Errno(0x20c)
EBADF = linux.EBADF
BPF_F_NO_PREALLOC = linux.BPF_F_NO_PREALLOC BPF_F_NO_PREALLOC = linux.BPF_F_NO_PREALLOC
BPF_F_NUMA_NODE = linux.BPF_F_NUMA_NODE BPF_F_NUMA_NODE = linux.BPF_F_NUMA_NODE
BPF_F_RDONLY = linux.BPF_F_RDONLY BPF_F_RDONLY = linux.BPF_F_RDONLY
@ -35,6 +37,9 @@ const (
BPF_F_INNER_MAP = linux.BPF_F_INNER_MAP BPF_F_INNER_MAP = linux.BPF_F_INNER_MAP
BPF_OBJ_NAME_LEN = linux.BPF_OBJ_NAME_LEN BPF_OBJ_NAME_LEN = linux.BPF_OBJ_NAME_LEN
BPF_TAG_SIZE = linux.BPF_TAG_SIZE 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 SYS_BPF = linux.SYS_BPF
F_DUPFD_CLOEXEC = linux.F_DUPFD_CLOEXEC F_DUPFD_CLOEXEC = linux.F_DUPFD_CLOEXEC
EPOLL_CTL_ADD = linux.EPOLL_CTL_ADD EPOLL_CTL_ADD = linux.EPOLL_CTL_ADD
@ -69,11 +74,6 @@ type Statfs_t = linux.Statfs_t
// Rlimit is a wrapper // Rlimit is a wrapper
type Rlimit = linux.Rlimit type Rlimit = linux.Rlimit
// Setrlimit is a wrapper
func Setrlimit(resource int, rlim *Rlimit) (err error) {
return linux.Setrlimit(resource, rlim)
}
// Syscall is a wrapper // Syscall is a wrapper
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
return linux.Syscall(trap, a1, a2, a3) return linux.Syscall(trap, a1, a2, a3)
@ -202,3 +202,7 @@ func KernelRelease() (string, error) {
release := string(uname.Release[:end]) release := string(uname.Release[:end])
return release, nil return release, nil
} }
func Prlimit(pid, resource int, new, old *Rlimit) error {
return linux.Prlimit(pid, resource, new, old)
}

View File

@ -1,3 +1,4 @@
//go:build !linux
// +build !linux // +build !linux
package unix package unix
@ -21,6 +22,7 @@ const (
ESRCH = syscall.ESRCH ESRCH = syscall.ESRCH
ENODEV = syscall.ENODEV ENODEV = syscall.ENODEV
EBADF = syscall.Errno(0) EBADF = syscall.Errno(0)
E2BIG = syscall.Errno(0)
// ENOTSUPP is not the same as ENOTSUP or EOPNOTSUP // ENOTSUPP is not the same as ENOTSUP or EOPNOTSUP
ENOTSUPP = syscall.Errno(0x20c) ENOTSUPP = syscall.Errno(0x20c)
@ -35,6 +37,9 @@ const (
BPF_F_INNER_MAP = 0 BPF_F_INNER_MAP = 0
BPF_OBJ_NAME_LEN = 0x10 BPF_OBJ_NAME_LEN = 0x10
BPF_TAG_SIZE = 0x8 BPF_TAG_SIZE = 0x8
BPF_RINGBUF_BUSY_BIT = 0
BPF_RINGBUF_DISCARD_BIT = 0
BPF_RINGBUF_HDR_SZ = 0
SYS_BPF = 321 SYS_BPF = 321
F_DUPFD_CLOEXEC = 0x406 F_DUPFD_CLOEXEC = 0x406
EPOLLIN = 0x1 EPOLLIN = 0x1
@ -86,11 +91,6 @@ type Rlimit struct {
Max uint64 Max uint64
} }
// Setrlimit is a wrapper
func Setrlimit(resource int, rlim *Rlimit) (err error) {
return errNonLinux
}
// Syscall is a wrapper // Syscall is a wrapper
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
return 0, 0, syscall.Errno(1) return 0, 0, syscall.Errno(1)
@ -261,3 +261,7 @@ func Renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags
func KernelRelease() (string, error) { func KernelRelease() (string, error) {
return "", errNonLinux return "", errNonLinux
} }
func Prlimit(pid, resource int, new, old *Rlimit) error {
return errNonLinux
}

View File

@ -2,7 +2,7 @@ package internal
import ( import (
"fmt" "fmt"
"io/ioutil" "os"
"regexp" "regexp"
"sync" "sync"
@ -109,7 +109,7 @@ func detectKernelVersion() (Version, error) {
// Example format: Ubuntu 4.15.0-91.92-generic 4.15.18 // Example format: Ubuntu 4.15.0-91.92-generic 4.15.18
// This method exists in the kernel itself, see d18acd15c // This method exists in the kernel itself, see d18acd15c
// ("perf tools: Fix kernel version error in ubuntu"). // ("perf tools: Fix kernel version error in ubuntu").
if pvs, err := ioutil.ReadFile("/proc/version_signature"); err == nil { if pvs, err := os.ReadFile("/proc/version_signature"); err == nil {
// If /proc/version_signature exists, failing to parse it is an error. // If /proc/version_signature exists, failing to parse it is an error.
// It only exists on Ubuntu, where the real patch level is not obtainable // It only exists on Ubuntu, where the real patch level is not obtainable
// through any other method. // through any other method.

88
vendor/github.com/cilium/ebpf/link/freplace.go generated vendored Normal file
View File

@ -0,0 +1,88 @@
package link
import (
"fmt"
"github.com/cilium/ebpf"
"github.com/cilium/ebpf/internal/btf"
)
type FreplaceLink struct {
RawLink
}
// AttachFreplace attaches the given eBPF program to the function it replaces.
//
// The program and name can either be provided at link time, or can be provided
// at program load time. If they were provided at load time, they should be nil
// and empty respectively here, as they will be ignored by the kernel.
// Examples:
//
// AttachFreplace(dispatcher, "function", replacement)
// AttachFreplace(nil, "", replacement)
func AttachFreplace(targetProg *ebpf.Program, name string, prog *ebpf.Program) (*FreplaceLink, error) {
if (name == "") != (targetProg == nil) {
return nil, fmt.Errorf("must provide both or neither of name and targetProg: %w", errInvalidInput)
}
if prog == nil {
return nil, fmt.Errorf("prog cannot be nil: %w", errInvalidInput)
}
if prog.Type() != ebpf.Extension {
return nil, fmt.Errorf("eBPF program type %s is not an Extension: %w", prog.Type(), errInvalidInput)
}
var (
target int
typeID btf.TypeID
)
if targetProg != nil {
info, err := targetProg.Info()
if err != nil {
return nil, err
}
btfID, ok := info.BTFID()
if !ok {
return nil, fmt.Errorf("could not get BTF ID for program %s: %w", info.Name, errInvalidInput)
}
btfHandle, err := btf.NewHandleFromID(btfID)
if err != nil {
return nil, err
}
defer btfHandle.Close()
var function *btf.Func
if err := btfHandle.Spec().FindType(name, &function); err != nil {
return nil, err
}
target = targetProg.FD()
typeID = function.ID()
}
link, err := AttachRawLink(RawLinkOptions{
Target: target,
Program: prog,
Attach: ebpf.AttachNone,
BTF: typeID,
})
if err != nil {
return nil, err
}
return &FreplaceLink{*link}, nil
}
// Update implements the Link interface.
func (f *FreplaceLink) Update(new *ebpf.Program) error {
return fmt.Errorf("freplace update: %w", ErrNotSupported)
}
// LoadPinnedFreplace loads a pinned iterator from a bpffs.
func LoadPinnedFreplace(fileName string, opts *ebpf.LoadPinOptions) (*FreplaceLink, error) {
link, err := LoadPinnedRawLink(fileName, TracingType, opts)
if err != nil {
return nil, err
}
return &FreplaceLink{*link}, err
}

View File

@ -5,7 +5,6 @@ import (
"crypto/rand" "crypto/rand"
"errors" "errors"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
@ -72,10 +71,11 @@ func (pt probeType) RetprobeBit() (uint64, error) {
// given kernel symbol starts executing. See /proc/kallsyms for available // given kernel symbol starts executing. See /proc/kallsyms for available
// symbols. For example, printk(): // symbols. For example, printk():
// //
// Kprobe("printk", prog) // kp, err := Kprobe("printk", prog)
// //
// The resulting Link must be Closed during program shutdown to avoid leaking // Losing the reference to the resulting Link (kp) will close the Kprobe
// system resources. // and prevent further execution of prog. The Link must be Closed during
// program shutdown to avoid leaking system resources.
func Kprobe(symbol string, prog *ebpf.Program) (Link, error) { func Kprobe(symbol string, prog *ebpf.Program) (Link, error) {
k, err := kprobe(symbol, prog, false) k, err := kprobe(symbol, prog, false)
if err != nil { if err != nil {
@ -95,10 +95,11 @@ func Kprobe(symbol string, prog *ebpf.Program) (Link, error) {
// before the given kernel symbol exits, with the function stack left intact. // before the given kernel symbol exits, with the function stack left intact.
// See /proc/kallsyms for available symbols. For example, printk(): // See /proc/kallsyms for available symbols. For example, printk():
// //
// Kretprobe("printk", prog) // kp, err := Kretprobe("printk", prog)
// //
// The resulting Link must be Closed during program shutdown to avoid leaking // Losing the reference to the resulting Link (kp) will close the Kretprobe
// system resources. // and prevent further execution of prog. The Link must be Closed during
// program shutdown to avoid leaking system resources.
func Kretprobe(symbol string, prog *ebpf.Program) (Link, error) { func Kretprobe(symbol string, prog *ebpf.Program) (Link, error) {
k, err := kprobe(symbol, prog, true) k, err := kprobe(symbol, prog, true)
if err != nil { if err != nil {
@ -157,7 +158,7 @@ func kprobe(symbol string, prog *ebpf.Program, ret bool) (*perfEvent, error) {
// pmuKprobe opens a perf event based on the kprobe PMU. // pmuKprobe opens a perf event based on the kprobe PMU.
// Returns os.ErrNotExist if the given symbol does not exist in the kernel. // Returns os.ErrNotExist if the given symbol does not exist in the kernel.
func pmuKprobe(symbol string, ret bool) (*perfEvent, error) { func pmuKprobe(symbol string, ret bool) (*perfEvent, error) {
return pmuProbe(kprobeType, symbol, "", 0, ret) return pmuProbe(kprobeType, symbol, "", 0, perfAllThreads, ret)
} }
// pmuProbe opens a perf event based on a Performance Monitoring Unit. // pmuProbe opens a perf event based on a Performance Monitoring Unit.
@ -167,7 +168,7 @@ func pmuKprobe(symbol string, ret bool) (*perfEvent, error) {
// 33ea4b24277b "perf/core: Implement the 'perf_uprobe' PMU" // 33ea4b24277b "perf/core: Implement the 'perf_uprobe' PMU"
// //
// Returns ErrNotSupported if the kernel doesn't support perf_[k,u]probe PMU // Returns ErrNotSupported if the kernel doesn't support perf_[k,u]probe PMU
func pmuProbe(typ probeType, symbol, path string, offset uint64, ret bool) (*perfEvent, error) { func pmuProbe(typ probeType, symbol, path string, offset uint64, pid int, ret bool) (*perfEvent, error) {
// Getting the PMU type will fail if the kernel doesn't support // Getting the PMU type will fail if the kernel doesn't support
// the perf_[k,u]probe PMU. // the perf_[k,u]probe PMU.
et, err := getPMUEventType(typ) et, err := getPMUEventType(typ)
@ -191,7 +192,7 @@ func pmuProbe(typ probeType, symbol, path string, offset uint64, ret bool) (*per
switch typ { switch typ {
case kprobeType: case kprobeType:
// Create a pointer to a NUL-terminated string for the kernel. // Create a pointer to a NUL-terminated string for the kernel.
sp, err := unsafeStringPtr(symbol) sp, err = unsafeStringPtr(symbol)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -202,7 +203,7 @@ func pmuProbe(typ probeType, symbol, path string, offset uint64, ret bool) (*per
Config: config, // Retprobe flag Config: config, // Retprobe flag
} }
case uprobeType: case uprobeType:
sp, err := unsafeStringPtr(path) sp, err = unsafeStringPtr(path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -220,7 +221,7 @@ func pmuProbe(typ probeType, symbol, path string, offset uint64, ret bool) (*per
} }
} }
fd, err := unix.PerfEventOpen(&attr, perfAllThreads, 0, -1, unix.PERF_FLAG_FD_CLOEXEC) fd, err := unix.PerfEventOpen(&attr, pid, 0, -1, unix.PERF_FLAG_FD_CLOEXEC)
// Since commit 97c753e62e6c, ENOENT is correctly returned instead of EINVAL // Since commit 97c753e62e6c, ENOENT is correctly returned instead of EINVAL
// when trying to create a kretprobe for a missing symbol. Make sure ENOENT // when trying to create a kretprobe for a missing symbol. Make sure ENOENT
@ -228,6 +229,11 @@ func pmuProbe(typ probeType, symbol, path string, offset uint64, ret bool) (*per
if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL) { if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL) {
return nil, fmt.Errorf("symbol '%s' not found: %w", symbol, os.ErrNotExist) return nil, fmt.Errorf("symbol '%s' not found: %w", symbol, 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", offset, err)
}
if err != nil { if err != nil {
return nil, fmt.Errorf("opening perf event: %w", err) return nil, fmt.Errorf("opening perf event: %w", err)
} }
@ -246,7 +252,7 @@ func pmuProbe(typ probeType, symbol, path string, offset uint64, ret bool) (*per
// tracefsKprobe creates a Kprobe tracefs entry. // tracefsKprobe creates a Kprobe tracefs entry.
func tracefsKprobe(symbol string, ret bool) (*perfEvent, error) { func tracefsKprobe(symbol string, ret bool) (*perfEvent, error) {
return tracefsProbe(kprobeType, symbol, "", 0, ret) return tracefsProbe(kprobeType, symbol, "", 0, perfAllThreads, ret)
} }
// tracefsProbe creates a trace event by writing an entry to <tracefs>/[k,u]probe_events. // tracefsProbe creates a trace event by writing an entry to <tracefs>/[k,u]probe_events.
@ -255,7 +261,7 @@ func tracefsKprobe(symbol string, ret bool) (*perfEvent, error) {
// Path and offset are only set in the case of uprobe(s) and are used to set // Path and offset are only set in the case of uprobe(s) and are used to set
// the executable/library path on the filesystem and the offset where the probe is inserted. // the executable/library path on the filesystem and the offset where the probe is inserted.
// A perf event is then opened on the newly-created trace event and returned to the caller. // A perf event is then opened on the newly-created trace event and returned to the caller.
func tracefsProbe(typ probeType, symbol, path string, offset uint64, ret bool) (*perfEvent, error) { func tracefsProbe(typ probeType, symbol, path string, offset uint64, pid int, ret bool) (*perfEvent, error) {
// Generate a random string for each trace event we attempt to create. // Generate a random string for each trace event we attempt to create.
// This value is used as the 'group' token in tracefs to allow creating // This value is used as the 'group' token in tracefs to allow creating
// multiple kprobe trace events with the same name. // multiple kprobe trace events with the same name.
@ -288,7 +294,7 @@ func tracefsProbe(typ probeType, symbol, path string, offset uint64, ret bool) (
} }
// Kprobes are ephemeral tracepoints and share the same perf event type. // Kprobes are ephemeral tracepoints and share the same perf event type.
fd, err := openTracepointPerfEvent(tid) fd, err := openTracepointPerfEvent(tid, pid)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -413,7 +419,7 @@ func probePrefix(ret bool) string {
func determineRetprobeBit(typ probeType) (uint64, error) { func determineRetprobeBit(typ probeType) (uint64, error) {
p := filepath.Join("/sys/bus/event_source/devices/", typ.String(), "/format/retprobe") p := filepath.Join("/sys/bus/event_source/devices/", typ.String(), "/format/retprobe")
data, err := ioutil.ReadFile(p) data, err := os.ReadFile(p)
if err != nil { if err != nil {
return 0, err return 0, err
} }

View File

@ -6,6 +6,7 @@ import (
"github.com/cilium/ebpf" "github.com/cilium/ebpf"
"github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/btf"
) )
var ErrNotSupported = internal.ErrNotSupported var ErrNotSupported = internal.ErrNotSupported
@ -29,8 +30,8 @@ type Link interface {
// Close frees resources. // Close frees resources.
// //
// The link will be broken unless it has been pinned. A link // The link will be broken unless it has been successfully pinned.
// may continue past the lifetime of the process if Close is // A link may continue past the lifetime of the process if Close is
// not called. // not called.
Close() error Close() error
@ -49,6 +50,8 @@ type RawLinkOptions struct {
Program *ebpf.Program Program *ebpf.Program
// Attach must match the attach type of Program. // Attach must match the attach type of Program.
Attach ebpf.AttachType Attach ebpf.AttachType
// BTF is the BTF of the attachment target.
BTF btf.TypeID
} }
// RawLinkInfo contains metadata on a link. // RawLinkInfo contains metadata on a link.
@ -83,9 +86,10 @@ func AttachRawLink(opts RawLinkOptions) (*RawLink, error) {
} }
attr := bpfLinkCreateAttr{ attr := bpfLinkCreateAttr{
targetFd: uint32(opts.Target), targetFd: uint32(opts.Target),
progFd: uint32(progFd), progFd: uint32(progFd),
attachType: opts.Attach, attachType: opts.Attach,
targetBTFID: uint32(opts.BTF),
} }
fd, err := bpfLinkCreate(&attr) fd, err := bpfLinkCreate(&attr)
if err != nil { if err != nil {

View File

@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"errors" "errors"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
@ -236,7 +235,7 @@ func getPMUEventType(typ probeType) (uint64, error) {
// openTracepointPerfEvent opens a tracepoint-type perf event. System-wide // openTracepointPerfEvent opens a tracepoint-type perf event. System-wide
// [k,u]probes created by writing to <tracefs>/[k,u]probe_events are tracepoints // [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. // behind the scenes, and can be attached to using these perf events.
func openTracepointPerfEvent(tid uint64) (*internal.FD, error) { func openTracepointPerfEvent(tid uint64, pid int) (*internal.FD, error) {
attr := unix.PerfEventAttr{ attr := unix.PerfEventAttr{
Type: unix.PERF_TYPE_TRACEPOINT, Type: unix.PERF_TYPE_TRACEPOINT,
Config: tid, Config: tid,
@ -245,7 +244,7 @@ func openTracepointPerfEvent(tid uint64) (*internal.FD, error) {
Wakeup: 1, Wakeup: 1,
} }
fd, err := unix.PerfEventOpen(&attr, perfAllThreads, 0, -1, unix.PERF_FLAG_FD_CLOEXEC) fd, err := unix.PerfEventOpen(&attr, pid, 0, -1, unix.PERF_FLAG_FD_CLOEXEC)
if err != nil { if err != nil {
return nil, fmt.Errorf("opening tracepoint perf event: %w", err) return nil, fmt.Errorf("opening tracepoint perf event: %w", err)
} }
@ -263,7 +262,7 @@ func uint64FromFile(base string, path ...string) (uint64, error) {
return 0, fmt.Errorf("path '%s' attempts to escape base path '%s': %w", l, base, errInvalidInput) return 0, fmt.Errorf("path '%s' attempts to escape base path '%s': %w", l, base, errInvalidInput)
} }
data, err := ioutil.ReadFile(p) data, err := os.ReadFile(p)
if err != nil { if err != nil {
return 0, fmt.Errorf("reading file %s: %w", p, err) return 0, fmt.Errorf("reading file %s: %w", p, err)
} }

View File

@ -88,10 +88,11 @@ var haveProgAttachReplace = internal.FeatureTest("BPF_PROG_ATTACH atomic replace
}) })
type bpfLinkCreateAttr struct { type bpfLinkCreateAttr struct {
progFd uint32 progFd uint32
targetFd uint32 targetFd uint32
attachType ebpf.AttachType attachType ebpf.AttachType
flags uint32 flags uint32
targetBTFID uint32
} }
func bpfLinkCreate(attr *bpfLinkCreateAttr) (*internal.FD, error) { func bpfLinkCreate(attr *bpfLinkCreateAttr) (*internal.FD, error) {

View File

@ -11,7 +11,11 @@ import (
// tracepoints. The top-level directory is the group, the event's subdirectory // tracepoints. The top-level directory is the group, the event's subdirectory
// is the name. Example: // is the name. Example:
// //
// Tracepoint("syscalls", "sys_enter_fork", prog) // tp, err := Tracepoint("syscalls", "sys_enter_fork", prog)
//
// Losing the reference to the resulting Link (tp) will close the Tracepoint
// and prevent further execution of prog. The Link must be Closed during
// program shutdown to avoid leaking system resources.
// //
// Note that attaching eBPF programs to syscalls (sys_enter_*/sys_exit_*) is // Note that attaching eBPF programs to syscalls (sys_enter_*/sys_exit_*) is
// only possible as of kernel 4.14 (commit cf5f5ce). // only possible as of kernel 4.14 (commit cf5f5ce).
@ -34,7 +38,7 @@ func Tracepoint(group, name string, prog *ebpf.Program) (Link, error) {
return nil, err return nil, err
} }
fd, err := openTracepointPerfEvent(tid) fd, err := openTracepointPerfEvent(tid, perfAllThreads)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -25,14 +25,18 @@ var (
value uint64 value uint64
err error err error
}{} }{}
// ErrNoSymbol indicates that the given symbol was not found
// in the ELF symbols table.
ErrNoSymbol = errors.New("not found")
) )
// Executable defines an executable program on the filesystem. // Executable defines an executable program on the filesystem.
type Executable struct { type Executable struct {
// Path of the executable on the filesystem. // Path of the executable on the filesystem.
path string path string
// Parsed ELF symbols and dynamic symbols. // Parsed ELF symbols and dynamic symbols offsets.
symbols map[string]elf.Symbol offsets map[string]uint64
} }
// UprobeOptions defines additional parameters that will be used // UprobeOptions defines additional parameters that will be used
@ -41,6 +45,9 @@ type UprobeOptions struct {
// Symbol offset. Must be provided in case of external symbols (shared libs). // Symbol offset. Must be provided in case of external symbols (shared libs).
// If set, overrides the offset eventually parsed from the executable. // If set, overrides the offset eventually parsed from the executable.
Offset uint64 Offset uint64
// Only set the uprobe on the given process ID. Useful when tracing
// shared library calls or programs that have many running instances.
PID int
} }
// To open a new Executable, use: // To open a new Executable, use:
@ -64,42 +71,84 @@ func OpenExecutable(path string) (*Executable, error) {
return nil, fmt.Errorf("parse ELF file: %w", err) return nil, fmt.Errorf("parse ELF file: %w", err)
} }
var ex = Executable{ if se.Type != elf.ET_EXEC && se.Type != elf.ET_DYN {
path: path, // ELF is not an executable or a shared object.
symbols: make(map[string]elf.Symbol), return nil, errors.New("the given file is not an executable or a shared object")
}
if err := ex.addSymbols(se.Symbols); err != nil {
return nil, err
} }
if err := ex.addSymbols(se.DynamicSymbols); err != nil { ex := Executable{
path: path,
offsets: make(map[string]uint64),
}
if err := ex.load(se); err != nil {
return nil, err return nil, err
} }
return &ex, nil return &ex, nil
} }
func (ex *Executable) addSymbols(f func() ([]elf.Symbol, error)) error { func (ex *Executable) load(f *internal.SafeELFFile) error {
// elf.Symbols and elf.DynamicSymbols return ErrNoSymbols if the section is not found. syms, err := f.Symbols()
syms, err := f()
if err != nil && !errors.Is(err, elf.ErrNoSymbols) { if err != nil && !errors.Is(err, elf.ErrNoSymbols) {
return err return err
} }
dynsyms, err := f.DynamicSymbols()
if err != nil && !errors.Is(err, elf.ErrNoSymbols) {
return err
}
syms = append(syms, dynsyms...)
for _, s := range syms { for _, s := range syms {
if elf.ST_TYPE(s.Info) != elf.STT_FUNC { if elf.ST_TYPE(s.Info) != elf.STT_FUNC {
// Symbol not associated with a function or other executable code. // Symbol not associated with a function or other executable code.
continue continue
} }
ex.symbols[s.Name] = s
off := s.Value
// Loop over ELF segments.
for _, prog := range f.Progs {
// Skip uninteresting segments.
if prog.Type != elf.PT_LOAD || (prog.Flags&elf.PF_X) == 0 {
continue
}
if prog.Vaddr <= s.Value && s.Value < (prog.Vaddr+prog.Memsz) {
// If the symbol value is contained in the segment, calculate
// the symbol offset.
//
// fn symbol offset = fn symbol VA - .text VA + .text offset
//
// stackoverflow.com/a/40249502
off = s.Value - prog.Vaddr + prog.Off
break
}
}
ex.offsets[s.Name] = off
} }
return nil return nil
} }
func (ex *Executable) symbol(symbol string) (*elf.Symbol, error) { func (ex *Executable) offset(symbol string) (uint64, error) {
if s, ok := ex.symbols[symbol]; ok { if off, ok := ex.offsets[symbol]; ok {
return &s, nil // Symbols with location 0 from section undef are shared library calls and
// are relocated before the binary is executed. Dynamic linking is not
// implemented by the library, so mark this as unsupported for now.
//
// Since only offset values are stored and not elf.Symbol, if the value is 0,
// assume it's an external symbol.
if off == 0 {
return 0, fmt.Errorf("cannot resolve %s library call '%s', "+
"consider providing the offset via options: %w", ex.path, symbol, ErrNotSupported)
}
return off, nil
} }
return nil, fmt.Errorf("symbol %s not found", symbol) return 0, fmt.Errorf("symbol %s: %w", symbol, ErrNoSymbol)
} }
// Uprobe attaches the given eBPF program to a perf event that fires when the // Uprobe attaches the given eBPF program to a perf event that fires when the
@ -112,11 +161,14 @@ func (ex *Executable) symbol(symbol string) (*elf.Symbol, error) {
// When using symbols which belongs to shared libraries, // When using symbols which belongs to shared libraries,
// an offset must be provided via options: // an offset must be provided via options:
// //
// ex.Uprobe("main", prog, &UprobeOptions{Offset: 0x123}) // up, err := ex.Uprobe("main", prog, &UprobeOptions{Offset: 0x123})
// //
// The resulting Link must be Closed during program shutdown to avoid leaking // Losing the reference to the resulting Link (up) will close the Uprobe
// system resources. Functions provided by shared libraries can currently not // and prevent further execution of prog. The Link must be Closed during
// be traced and will result in an ErrNotSupported. // program shutdown to avoid leaking system resources.
//
// Functions provided by shared libraries can currently not be traced and
// will result in an ErrNotSupported.
func (ex *Executable) Uprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions) (Link, error) { func (ex *Executable) Uprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions) (Link, error) {
u, err := ex.uprobe(symbol, prog, opts, false) u, err := ex.uprobe(symbol, prog, opts, false)
if err != nil { if err != nil {
@ -141,11 +193,14 @@ func (ex *Executable) Uprobe(symbol string, prog *ebpf.Program, opts *UprobeOpti
// When using symbols which belongs to shared libraries, // When using symbols which belongs to shared libraries,
// an offset must be provided via options: // an offset must be provided via options:
// //
// ex.Uretprobe("main", prog, &UprobeOptions{Offset: 0x123}) // up, err := ex.Uretprobe("main", prog, &UprobeOptions{Offset: 0x123})
// //
// The resulting Link must be Closed during program shutdown to avoid leaking // Losing the reference to the resulting Link (up) will close the Uprobe
// system resources. Functions provided by shared libraries can currently not // and prevent further execution of prog. The Link must be Closed during
// be traced and will result in an ErrNotSupported. // program shutdown to avoid leaking system resources.
//
// Functions provided by shared libraries can currently not be traced and
// will result in an ErrNotSupported.
func (ex *Executable) Uretprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions) (Link, error) { func (ex *Executable) Uretprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions) (Link, error) {
u, err := ex.uprobe(symbol, prog, opts, true) u, err := ex.uprobe(symbol, prog, opts, true)
if err != nil { if err != nil {
@ -175,24 +230,20 @@ func (ex *Executable) uprobe(symbol string, prog *ebpf.Program, opts *UprobeOpti
if opts != nil && opts.Offset != 0 { if opts != nil && opts.Offset != 0 {
offset = opts.Offset offset = opts.Offset
} else { } else {
sym, err := ex.symbol(symbol) off, err := ex.offset(symbol)
if err != nil { if err != nil {
return nil, fmt.Errorf("symbol '%s' not found: %w", symbol, err) return nil, err
} }
offset = off
}
// Symbols with location 0 from section undef are shared library calls and pid := perfAllThreads
// are relocated before the binary is executed. Dynamic linking is not if opts != nil && opts.PID != 0 {
// implemented by the library, so mark this as unsupported for now. pid = opts.PID
if sym.Section == elf.SHN_UNDEF && sym.Value == 0 {
return nil, fmt.Errorf("cannot resolve %s library call '%s', "+
"consider providing the offset via options: %w", ex.path, symbol, ErrNotSupported)
}
offset = sym.Value
} }
// Use uprobe PMU if the kernel has it available. // Use uprobe PMU if the kernel has it available.
tp, err := pmuUprobe(symbol, ex.path, offset, ret) tp, err := pmuUprobe(symbol, ex.path, offset, pid, ret)
if err == nil { if err == nil {
return tp, nil return tp, nil
} }
@ -201,7 +252,7 @@ func (ex *Executable) uprobe(symbol string, prog *ebpf.Program, opts *UprobeOpti
} }
// Use tracefs if uprobe PMU is missing. // Use tracefs if uprobe PMU is missing.
tp, err = tracefsUprobe(uprobeSanitizedSymbol(symbol), ex.path, offset, ret) tp, err = tracefsUprobe(uprobeSanitizedSymbol(symbol), ex.path, offset, pid, ret)
if err != nil { if err != nil {
return nil, fmt.Errorf("creating trace event '%s:%s' in tracefs: %w", ex.path, symbol, err) return nil, fmt.Errorf("creating trace event '%s:%s' in tracefs: %w", ex.path, symbol, err)
} }
@ -210,13 +261,13 @@ func (ex *Executable) uprobe(symbol string, prog *ebpf.Program, opts *UprobeOpti
} }
// pmuUprobe opens a perf event based on the uprobe PMU. // pmuUprobe opens a perf event based on the uprobe PMU.
func pmuUprobe(symbol, path string, offset uint64, ret bool) (*perfEvent, error) { func pmuUprobe(symbol, path string, offset uint64, pid int, ret bool) (*perfEvent, error) {
return pmuProbe(uprobeType, symbol, path, offset, ret) return pmuProbe(uprobeType, symbol, path, offset, pid, ret)
} }
// tracefsUprobe creates a Uprobe tracefs entry. // tracefsUprobe creates a Uprobe tracefs entry.
func tracefsUprobe(symbol, path string, offset uint64, ret bool) (*perfEvent, error) { func tracefsUprobe(symbol, path string, offset uint64, pid int, ret bool) (*perfEvent, error) {
return tracefsProbe(uprobeType, symbol, path, offset, ret) return tracefsProbe(uprobeType, symbol, path, offset, pid, ret)
} }
// uprobeSanitizedSymbol replaces every invalid characted for the tracefs api with an underscore. // uprobeSanitizedSymbol replaces every invalid characted for the tracefs api with an underscore.

View File

@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/asm"
"github.com/cilium/ebpf/internal/btf"
) )
// link resolves bpf-to-bpf calls. // link resolves bpf-to-bpf calls.
@ -40,7 +39,7 @@ func link(prog *ProgramSpec, libs []*ProgramSpec) error {
pending = append(pending, lib.Instructions) pending = append(pending, lib.Instructions)
if prog.BTF != nil && lib.BTF != nil { if prog.BTF != nil && lib.BTF != nil {
if err := btf.ProgramAppend(prog.BTF, lib.BTF); err != nil { if err := prog.BTF.Append(lib.BTF); err != nil {
return fmt.Errorf("linking BTF of %s: %w", lib.Name, err) return fmt.Errorf("linking BTF of %s: %w", lib.Name, err)
} }
} }
@ -136,5 +135,25 @@ func fixupJumpsAndCalls(insns asm.Instructions) error {
} }
} }
// fixupBPFCalls replaces bpf_probe_read_{kernel,user}[_str] with bpf_probe_read[_str] on older kernels
// https://github.com/libbpf/libbpf/blob/master/src/libbpf.c#L6009
iter = insns.Iterate()
for iter.Next() {
ins := iter.Ins
if !ins.IsBuiltinCall() {
continue
}
switch asm.BuiltinFunc(ins.Constant) {
case asm.FnProbeReadKernel, asm.FnProbeReadUser:
if err := haveProbeReadKernel(); err != nil {
ins.Constant = int64(asm.FnProbeRead)
}
case asm.FnProbeReadKernelStr, asm.FnProbeReadUserStr:
if err := haveProbeReadKernel(); err != nil {
ins.Constant = int64(asm.FnProbeReadStr)
}
}
}
return nil return nil
} }

95
vendor/github.com/cilium/ebpf/map.go generated vendored
View File

@ -1,6 +1,7 @@
package ebpf package ebpf
import ( import (
"bytes"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -65,6 +66,11 @@ type MapSpec struct {
// InnerMap is used as a template for ArrayOfMaps and HashOfMaps // InnerMap is used as a template for ArrayOfMaps and HashOfMaps
InnerMap *MapSpec InnerMap *MapSpec
// Extra trailing bytes found in the ELF map definition when using structs
// larger than libbpf's bpf_map_def. Must be empty before instantiating
// the MapSpec into a Map.
Extra bytes.Reader
// The BTF associated with this map. // The BTF associated with this map.
BTF *btf.Map BTF *btf.Map
} }
@ -82,9 +88,12 @@ func (ms *MapSpec) Copy() *MapSpec {
} }
cpy := *ms cpy := *ms
cpy.Contents = make([]MapKV, len(ms.Contents)) cpy.Contents = make([]MapKV, len(ms.Contents))
copy(cpy.Contents, ms.Contents) copy(cpy.Contents, ms.Contents)
cpy.InnerMap = ms.InnerMap.Copy() cpy.InnerMap = ms.InnerMap.Copy()
return &cpy return &cpy
} }
@ -188,14 +197,24 @@ func NewMap(spec *MapSpec) (*Map, error) {
// //
// The caller is responsible for ensuring the process' rlimit is set // The caller is responsible for ensuring the process' rlimit is set
// sufficiently high for locking memory during map creation. This can be done // sufficiently high for locking memory during map creation. This can be done
// by calling unix.Setrlimit with unix.RLIMIT_MEMLOCK prior to calling NewMapWithOptions. // by calling rlimit.RemoveMemlock() prior to calling NewMapWithOptions.
// //
// May return an error wrapping ErrMapIncompatible. // May return an error wrapping ErrMapIncompatible.
func NewMapWithOptions(spec *MapSpec, opts MapOptions) (*Map, error) { func NewMapWithOptions(spec *MapSpec, opts MapOptions) (*Map, error) {
handles := newHandleCache() handles := newHandleCache()
defer handles.close() defer handles.close()
return newMapWithOptions(spec, opts, handles) m, err := newMapWithOptions(spec, opts, handles)
if err != nil {
return nil, fmt.Errorf("creating map: %w", err)
}
err = m.finalize(spec)
if err != nil {
return nil, fmt.Errorf("populating map: %w", err)
}
return m, nil
} }
func newMapWithOptions(spec *MapSpec, opts MapOptions, handles *handleCache) (_ *Map, err error) { func newMapWithOptions(spec *MapSpec, opts MapOptions, handles *handleCache) (_ *Map, err error) {
@ -207,8 +226,12 @@ func newMapWithOptions(spec *MapSpec, opts MapOptions, handles *handleCache) (_
switch spec.Pinning { switch spec.Pinning {
case PinByName: case PinByName:
if spec.Name == "" || opts.PinPath == "" { if spec.Name == "" {
return nil, fmt.Errorf("pin by name: missing Name or PinPath") return nil, fmt.Errorf("pin by name: missing Name")
}
if opts.PinPath == "" {
return nil, fmt.Errorf("pin by name: missing MapOptions.PinPath")
} }
path := filepath.Join(opts.PinPath, spec.Name) path := filepath.Join(opts.PinPath, spec.Name)
@ -244,16 +267,19 @@ func newMapWithOptions(spec *MapSpec, opts MapOptions, handles *handleCache) (_
return nil, errors.New("inner maps cannot be pinned") return nil, errors.New("inner maps cannot be pinned")
} }
template, err := createMap(spec.InnerMap, nil, opts, handles) template, err := spec.InnerMap.createMap(nil, opts, handles)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("inner map: %w", err)
} }
defer template.Close() defer template.Close()
// Intentionally skip populating and freezing (finalizing)
// the inner map template since it will be removed shortly.
innerFd = template.fd innerFd = template.fd
} }
m, err := createMap(spec, innerFd, opts, handles) m, err := spec.createMap(innerFd, opts, handles)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -269,7 +295,9 @@ func newMapWithOptions(spec *MapSpec, opts MapOptions, handles *handleCache) (_
return m, nil return m, nil
} }
func createMap(spec *MapSpec, inner *internal.FD, opts MapOptions, handles *handleCache) (_ *Map, err error) { // createMap validates the spec's properties and creates the map in the kernel
// using the given opts. It does not populate or freeze the map.
func (spec *MapSpec) createMap(inner *internal.FD, opts MapOptions, handles *handleCache) (_ *Map, err error) {
closeOnError := func(closer io.Closer) { closeOnError := func(closer io.Closer) {
if err != nil { if err != nil {
closer.Close() closer.Close()
@ -278,10 +306,16 @@ func createMap(spec *MapSpec, inner *internal.FD, opts MapOptions, handles *hand
spec = spec.Copy() spec = spec.Copy()
// Kernels 4.13 through 5.4 used a struct bpf_map_def that contained
// additional 'inner_map_idx' and later 'numa_node' fields.
// In order to support loading these definitions, tolerate the presence of
// extra bytes, but require them to be zeroes.
if _, err := io.Copy(internal.DiscardZeroes{}, &spec.Extra); err != nil {
return nil, errors.New("extra contains unhandled non-zero bytes, drain before creating map")
}
switch spec.Type { switch spec.Type {
case ArrayOfMaps: case ArrayOfMaps, HashOfMaps:
fallthrough
case HashOfMaps:
if err := haveNestedMaps(); err != nil { if err := haveNestedMaps(); err != nil {
return nil, err return nil, err
} }
@ -350,7 +384,7 @@ func createMap(spec *MapSpec, inner *internal.FD, opts MapOptions, handles *hand
var btfDisabled bool var btfDisabled bool
if spec.BTF != nil { if spec.BTF != nil {
handle, err := handles.btfHandle(btf.MapSpec(spec.BTF)) handle, err := handles.btfHandle(spec.BTF.Spec)
btfDisabled = errors.Is(err, btf.ErrNotSupported) btfDisabled = errors.Is(err, btf.ErrNotSupported)
if err != nil && !btfDisabled { if err != nil && !btfDisabled {
return nil, fmt.Errorf("load BTF: %w", err) return nil, fmt.Errorf("load BTF: %w", err)
@ -358,15 +392,15 @@ func createMap(spec *MapSpec, inner *internal.FD, opts MapOptions, handles *hand
if handle != nil { if handle != nil {
attr.BTFFd = uint32(handle.FD()) attr.BTFFd = uint32(handle.FD())
attr.BTFKeyTypeID = uint32(btf.MapKey(spec.BTF).ID()) attr.BTFKeyTypeID = uint32(spec.BTF.Key.ID())
attr.BTFValueTypeID = uint32(btf.MapValue(spec.BTF).ID()) attr.BTFValueTypeID = uint32(spec.BTF.Value.ID())
} }
} }
fd, err := internal.BPFMapCreate(&attr) fd, err := internal.BPFMapCreate(&attr)
if err != nil { if err != nil {
if errors.Is(err, unix.EPERM) { if errors.Is(err, unix.EPERM) {
return nil, fmt.Errorf("map create: RLIMIT_MEMLOCK may be too low: %w", err) return nil, fmt.Errorf("map create: %w (MEMLOCK bay be too low, consider rlimit.RemoveMemlock)", err)
} }
if btfDisabled { if btfDisabled {
return nil, fmt.Errorf("map create without BTF: %w", err) return nil, fmt.Errorf("map create without BTF: %w", err)
@ -380,19 +414,11 @@ func createMap(spec *MapSpec, inner *internal.FD, opts MapOptions, handles *hand
return nil, fmt.Errorf("map create: %w", err) return nil, fmt.Errorf("map create: %w", err)
} }
if err := m.populate(spec.Contents); err != nil {
return nil, fmt.Errorf("map create: can't set initial contents: %w", err)
}
if spec.Freeze {
if err := m.Freeze(); err != nil {
return nil, fmt.Errorf("can't freeze map: %w", err)
}
}
return m, nil return m, nil
} }
// newMap allocates and returns a new Map structure.
// Sets the fullValueSize on per-CPU maps.
func newMap(fd *internal.FD, name string, typ MapType, keySize, valueSize, maxEntries, flags uint32) (*Map, error) { func newMap(fd *internal.FD, name string, typ MapType, keySize, valueSize, maxEntries, flags uint32) (*Map, error) {
m := &Map{ m := &Map{
name, name,
@ -415,7 +441,7 @@ func newMap(fd *internal.FD, name string, typ MapType, keySize, valueSize, maxEn
return nil, err return nil, err
} }
m.fullValueSize = align(int(valueSize), 8) * possibleCPUs m.fullValueSize = internal.Align(int(valueSize), 8) * possibleCPUs
return m, nil return m, nil
} }
@ -892,12 +918,21 @@ func (m *Map) Freeze() error {
return nil return nil
} }
func (m *Map) populate(contents []MapKV) error { // finalize populates the Map according to the Contents specified
for _, kv := range contents { // in spec and freezes the Map if requested by spec.
func (m *Map) finalize(spec *MapSpec) error {
for _, kv := range spec.Contents {
if err := m.Put(kv.Key, kv.Value); err != nil { if err := m.Put(kv.Key, kv.Value); err != nil {
return fmt.Errorf("key %v: %w", kv.Key, err) return fmt.Errorf("putting value: key %v: %w", kv.Key, err)
} }
} }
if spec.Freeze {
if err := m.Freeze(); err != nil {
return fmt.Errorf("freezing map: %w", err)
}
}
return nil return nil
} }
@ -1212,7 +1247,7 @@ func MapGetNextID(startID MapID) (MapID, error) {
// //
// Returns ErrNotExist, if there is no eBPF map with the given id. // Returns ErrNotExist, if there is no eBPF map with the given id.
func NewMapFromID(id MapID) (*Map, error) { func NewMapFromID(id MapID) (*Map, error) {
fd, err := bpfObjGetFDByID(internal.BPF_MAP_GET_FD_BY_ID, uint32(id)) fd, err := internal.BPFObjGetFDByID(internal.BPF_MAP_GET_FD_BY_ID, uint32(id))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -8,6 +8,7 @@ import (
"fmt" "fmt"
"reflect" "reflect"
"runtime" "runtime"
"sync"
"unsafe" "unsafe"
"github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal"
@ -39,6 +40,10 @@ func marshalPtr(data interface{}, length int) (internal.Pointer, error) {
// Returns an error if the given value isn't representable in exactly // Returns an error if the given value isn't representable in exactly
// length bytes. // length bytes.
func marshalBytes(data interface{}, length int) (buf []byte, err error) { func marshalBytes(data interface{}, length int) (buf []byte, err error) {
if data == nil {
return nil, errors.New("can't marshal a nil value")
}
switch value := data.(type) { switch value := data.(type) {
case encoding.BinaryMarshaler: case encoding.BinaryMarshaler:
buf, err = value.MarshalBinary() buf, err = value.MarshalBinary()
@ -77,22 +82,30 @@ func makeBuffer(dst interface{}, length int) (internal.Pointer, []byte) {
return internal.NewSlicePointer(buf), buf return internal.NewSlicePointer(buf), buf
} }
var bytesReaderPool = sync.Pool{
New: func() interface{} {
return new(bytes.Reader)
},
}
// unmarshalBytes converts a byte buffer into an arbitrary value. // unmarshalBytes converts a byte buffer into an arbitrary value.
// //
// Prefer using Map.unmarshalKey and Map.unmarshalValue if possible, since // Prefer using Map.unmarshalKey and Map.unmarshalValue if possible, since
// those have special cases that allow more types to be encoded. // those have special cases that allow more types to be encoded.
//
// The common int32 and int64 types are directly handled to avoid
// unnecessary heap allocations as happening in the default case.
func unmarshalBytes(data interface{}, buf []byte) error { func unmarshalBytes(data interface{}, buf []byte) error {
switch value := data.(type) { switch value := data.(type) {
case unsafe.Pointer: case unsafe.Pointer:
// This could be solved in Go 1.17 by unsafe.Slice instead. (https://github.com/golang/go/issues/19367) var dst []byte
// We could opt for removing unsafe.Pointer support in the lib as well. // Use unsafe.Slice when we drop support for pre1.17 (https://github.com/golang/go/issues/19367)
sh := &reflect.SliceHeader{ //nolint:govet // We could opt for removing unsafe.Pointer support in the lib as well
Data: uintptr(value), sh := (*reflect.SliceHeader)(unsafe.Pointer(&dst))
Len: len(buf), sh.Data = uintptr(value)
Cap: len(buf), sh.Len = len(buf)
} sh.Cap = len(buf)
dst := *(*[]byte)(unsafe.Pointer(sh))
copy(dst, buf) copy(dst, buf)
runtime.KeepAlive(value) runtime.KeepAlive(value)
return nil return nil
@ -106,12 +119,38 @@ func unmarshalBytes(data interface{}, buf []byte) error {
case *[]byte: case *[]byte:
*value = buf *value = buf
return nil return nil
case *int32:
if len(buf) < 4 {
return errors.New("int32 requires 4 bytes")
}
*value = int32(internal.NativeEndian.Uint32(buf))
return nil
case *uint32:
if len(buf) < 4 {
return errors.New("uint32 requires 4 bytes")
}
*value = internal.NativeEndian.Uint32(buf)
return nil
case *int64:
if len(buf) < 8 {
return errors.New("int64 requires 8 bytes")
}
*value = int64(internal.NativeEndian.Uint64(buf))
return nil
case *uint64:
if len(buf) < 8 {
return errors.New("uint64 requires 8 bytes")
}
*value = internal.NativeEndian.Uint64(buf)
return nil
case string: case string:
return errors.New("require pointer to string") return errors.New("require pointer to string")
case []byte: case []byte:
return errors.New("require pointer to []byte") return errors.New("require pointer to []byte")
default: default:
rd := bytes.NewReader(buf) rd := bytesReaderPool.Get().(*bytes.Reader)
rd.Reset(buf)
defer bytesReaderPool.Put(rd)
if err := binary.Read(rd, internal.NativeEndian, value); err != nil { if err := binary.Read(rd, internal.NativeEndian, value); err != nil {
return fmt.Errorf("decoding %T: %v", value, err) return fmt.Errorf("decoding %T: %v", value, err)
} }
@ -142,7 +181,7 @@ func marshalPerCPUValue(slice interface{}, elemLength int) (internal.Pointer, er
return internal.Pointer{}, fmt.Errorf("per-CPU value exceeds number of CPUs") return internal.Pointer{}, fmt.Errorf("per-CPU value exceeds number of CPUs")
} }
alignedElemLength := align(elemLength, 8) alignedElemLength := internal.Align(elemLength, 8)
buf := make([]byte, alignedElemLength*possibleCPUs) buf := make([]byte, alignedElemLength*possibleCPUs)
for i := 0; i < sliceLen; i++ { for i := 0; i < sliceLen; i++ {
@ -212,7 +251,3 @@ func unmarshalPerCPUValue(slicePtr interface{}, elemLength int, buf []byte) erro
reflect.ValueOf(slicePtr).Elem().Set(slice) reflect.ValueOf(slicePtr).Elem().Set(slice)
return nil return nil
} }
func align(n, alignment int) int {
return (int(n) + alignment - 1) / alignment * alignment
}

123
vendor/github.com/cilium/ebpf/prog.go generated vendored
View File

@ -57,16 +57,21 @@ type ProgramSpec struct {
// Name is passed to the kernel as a debug aid. Must only contain // Name is passed to the kernel as a debug aid. Must only contain
// alpha numeric and '_' characters. // alpha numeric and '_' characters.
Name string Name string
// Type determines at which hook in the kernel a program will run. // Type determines at which hook in the kernel a program will run.
Type ProgramType Type ProgramType
AttachType AttachType AttachType AttachType
// Name of a kernel data structure to attach to. It's interpretation // Name of a kernel data structure or function to attach to. Its
// depends on Type and AttachType. // interpretation depends on Type and AttachType.
AttachTo string AttachTo string
// The program to attach to. Must be provided manually.
AttachTarget *Program
Instructions asm.Instructions Instructions asm.Instructions
// Flags is passed to the kernel and specifies additional program // Flags is passed to the kernel and specifies additional program
// load attributes. // load attributes.
Flags uint32 Flags uint32
// License of the program. Some helpers are only available if // License of the program. Some helpers are only available if
// the license is deemed compatible with the GPL. // the license is deemed compatible with the GPL.
// //
@ -146,7 +151,7 @@ func NewProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er
func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *handleCache) (*Program, error) { func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *handleCache) (*Program, error) {
if len(spec.Instructions) == 0 { if len(spec.Instructions) == 0 {
return nil, errors.New("Instructions cannot be empty") return nil, errors.New("instructions cannot be empty")
} }
if spec.ByteOrder != nil && spec.ByteOrder != internal.NativeEndian { if spec.ByteOrder != nil && spec.ByteOrder != internal.NativeEndian {
@ -166,16 +171,16 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *hand
kv = v.Kernel() kv = v.Kernel()
} }
attr := &bpfProgLoadAttr{ attr := &internal.BPFProgLoadAttr{
progType: spec.Type, ProgType: uint32(spec.Type),
progFlags: spec.Flags, ProgFlags: spec.Flags,
expectedAttachType: spec.AttachType, ExpectedAttachType: uint32(spec.AttachType),
license: internal.NewStringPointer(spec.License), License: internal.NewStringPointer(spec.License),
kernelVersion: kv, KernelVersion: kv,
} }
if haveObjName() == nil { if haveObjName() == nil {
attr.progName = internal.NewBPFObjName(spec.Name) attr.ProgName = internal.NewBPFObjName(spec.Name)
} }
var err error var err error
@ -190,35 +195,35 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *hand
var btfDisabled bool var btfDisabled bool
var core btf.COREFixups var core btf.COREFixups
if spec.BTF != nil { if spec.BTF != nil {
core, err = btf.ProgramFixups(spec.BTF, targetBTF) core, err = spec.BTF.Fixups(targetBTF)
if err != nil { if err != nil {
return nil, fmt.Errorf("CO-RE relocations: %w", err) return nil, fmt.Errorf("CO-RE relocations: %w", err)
} }
handle, err := handles.btfHandle(btf.ProgramSpec(spec.BTF)) handle, err := handles.btfHandle(spec.BTF.Spec())
btfDisabled = errors.Is(err, btf.ErrNotSupported) btfDisabled = errors.Is(err, btf.ErrNotSupported)
if err != nil && !btfDisabled { if err != nil && !btfDisabled {
return nil, fmt.Errorf("load BTF: %w", err) return nil, fmt.Errorf("load BTF: %w", err)
} }
if handle != nil { if handle != nil {
attr.progBTFFd = uint32(handle.FD()) attr.ProgBTFFd = uint32(handle.FD())
recSize, bytes, err := btf.ProgramLineInfos(spec.BTF) recSize, bytes, err := spec.BTF.LineInfos()
if err != nil { if err != nil {
return nil, fmt.Errorf("get BTF line infos: %w", err) return nil, fmt.Errorf("get BTF line infos: %w", err)
} }
attr.lineInfoRecSize = recSize attr.LineInfoRecSize = recSize
attr.lineInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize)) attr.LineInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize))
attr.lineInfo = internal.NewSlicePointer(bytes) attr.LineInfo = internal.NewSlicePointer(bytes)
recSize, bytes, err = btf.ProgramFuncInfos(spec.BTF) recSize, bytes, err = spec.BTF.FuncInfos()
if err != nil { if err != nil {
return nil, fmt.Errorf("get BTF function infos: %w", err) return nil, fmt.Errorf("get BTF function infos: %w", err)
} }
attr.funcInfoRecSize = recSize attr.FuncInfoRecSize = recSize
attr.funcInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize)) attr.FuncInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize))
attr.funcInfo = internal.NewSlicePointer(bytes) attr.FuncInfo = internal.NewSlicePointer(bytes)
} }
} }
@ -238,16 +243,41 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *hand
} }
bytecode := buf.Bytes() bytecode := buf.Bytes()
attr.instructions = internal.NewSlicePointer(bytecode) attr.Instructions = internal.NewSlicePointer(bytecode)
attr.insCount = uint32(len(bytecode) / asm.InstructionSize) attr.InsCount = uint32(len(bytecode) / asm.InstructionSize)
if spec.AttachTo != "" { if spec.AttachTo != "" {
if spec.AttachTarget != nil {
info, err := spec.AttachTarget.Info()
if err != nil {
return nil, fmt.Errorf("load target BTF: %w", err)
}
btfID, ok := info.BTFID()
if !ok {
return nil, fmt.Errorf("load target BTF: no BTF info available")
}
btfHandle, err := btf.NewHandleFromID(btfID)
if err != nil {
return nil, fmt.Errorf("load target BTF: %w", err)
}
defer btfHandle.Close()
targetBTF = btfHandle.Spec()
if err != nil {
return nil, fmt.Errorf("load target BTF: %w", err)
}
}
target, err := resolveBTFType(targetBTF, spec.AttachTo, spec.Type, spec.AttachType) target, err := resolveBTFType(targetBTF, spec.AttachTo, spec.Type, spec.AttachType)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if target != nil { if target != nil {
attr.attachBTFID = target.ID() attr.AttachBTFID = uint32(target.ID())
}
if spec.AttachTarget != nil {
attr.AttachProgFd = uint32(spec.AttachTarget.FD())
} }
} }
@ -259,12 +289,12 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *hand
var logBuf []byte var logBuf []byte
if opts.LogLevel > 0 { if opts.LogLevel > 0 {
logBuf = make([]byte, logSize) logBuf = make([]byte, logSize)
attr.logLevel = opts.LogLevel attr.LogLevel = opts.LogLevel
attr.logSize = uint32(len(logBuf)) attr.LogSize = uint32(len(logBuf))
attr.logBuf = internal.NewSlicePointer(logBuf) attr.LogBuf = internal.NewSlicePointer(logBuf)
} }
fd, err := bpfProgLoad(attr) fd, err := internal.BPFProgLoad(attr)
if err == nil { if err == nil {
return &Program{internal.CString(logBuf), fd, spec.Name, "", spec.Type}, nil return &Program{internal.CString(logBuf), fd, spec.Name, "", spec.Type}, nil
} }
@ -273,17 +303,20 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *hand
if opts.LogLevel == 0 && opts.LogSize >= 0 { if opts.LogLevel == 0 && opts.LogSize >= 0 {
// Re-run with the verifier enabled to get better error messages. // Re-run with the verifier enabled to get better error messages.
logBuf = make([]byte, logSize) logBuf = make([]byte, logSize)
attr.logLevel = 1 attr.LogLevel = 1
attr.logSize = uint32(len(logBuf)) attr.LogSize = uint32(len(logBuf))
attr.logBuf = internal.NewSlicePointer(logBuf) attr.LogBuf = internal.NewSlicePointer(logBuf)
_, logErr = bpfProgLoad(attr) fd, logErr = internal.BPFProgLoad(attr)
if logErr == nil {
fd.Close()
}
} }
if errors.Is(logErr, unix.EPERM) && logBuf[0] == 0 { if errors.Is(logErr, unix.EPERM) && logBuf[0] == 0 {
// EPERM due to RLIMIT_MEMLOCK happens before the verifier, so we can // EPERM due to RLIMIT_MEMLOCK happens before the verifier, so we can
// check that the log is empty to reduce false positives. // check that the log is empty to reduce false positives.
return nil, fmt.Errorf("load program: RLIMIT_MEMLOCK may be too low: %w", logErr) return nil, fmt.Errorf("load program: %w (MEMLOCK bay be too low, consider rlimit.RemoveMemlock)", logErr)
} }
err = internal.ErrorWithLog(err, logBuf, logErr) err = internal.ErrorWithLog(err, logBuf, logErr)
@ -310,7 +343,7 @@ func NewProgramFromFD(fd int) (*Program, error) {
// //
// Returns ErrNotExist, if there is no eBPF program with the given id. // Returns ErrNotExist, if there is no eBPF program with the given id.
func NewProgramFromID(id ProgramID) (*Program, error) { func NewProgramFromID(id ProgramID) (*Program, error) {
fd, err := bpfObjGetFDByID(internal.BPF_PROG_GET_FD_BY_ID, uint32(id)) fd, err := internal.BPFObjGetFDByID(internal.BPF_PROG_GET_FD_BY_ID, uint32(id))
if err != nil { if err != nil {
return nil, fmt.Errorf("get program by id: %w", err) return nil, fmt.Errorf("get program by id: %w", err)
} }
@ -677,45 +710,44 @@ func ProgramGetNextID(startID ProgramID) (ProgramID, error) {
// //
// Deprecated: use ProgramInfo.ID() instead. // Deprecated: use ProgramInfo.ID() instead.
func (p *Program) ID() (ProgramID, error) { func (p *Program) ID() (ProgramID, error) {
info, err := bpfGetProgInfoByFD(p.fd) info, err := bpfGetProgInfoByFD(p.fd, nil)
if err != nil { if err != nil {
return ProgramID(0), err return ProgramID(0), err
} }
return ProgramID(info.id), nil return ProgramID(info.id), nil
} }
func resolveBTFType(kernel *btf.Spec, name string, progType ProgramType, attachType AttachType) (btf.Type, error) { func resolveBTFType(spec *btf.Spec, name string, progType ProgramType, attachType AttachType) (btf.Type, error) {
type match struct { type match struct {
p ProgramType p ProgramType
a AttachType a AttachType
} }
var target btf.Type
var typeName, featureName string var typeName, featureName string
switch (match{progType, attachType}) { switch (match{progType, attachType}) {
case match{LSM, AttachLSMMac}: case match{LSM, AttachLSMMac}:
target = new(btf.Func)
typeName = "bpf_lsm_" + name typeName = "bpf_lsm_" + name
featureName = name + " LSM hook" featureName = name + " LSM hook"
case match{Tracing, AttachTraceIter}: case match{Tracing, AttachTraceIter}:
target = new(btf.Func)
typeName = "bpf_iter_" + name typeName = "bpf_iter_" + name
featureName = name + " iterator" featureName = name + " iterator"
case match{Extension, AttachNone}:
typeName = name
featureName = fmt.Sprintf("freplace %s", name)
default: default:
return nil, nil return nil, nil
} }
if kernel == nil { if spec == nil {
var err error var err error
kernel, err = btf.LoadKernelSpec() spec, err = btf.LoadKernelSpec()
if err != nil { if err != nil {
return nil, fmt.Errorf("load kernel spec: %w", err) return nil, fmt.Errorf("load kernel spec: %w", err)
} }
} }
err := kernel.FindType(typeName, target) var target *btf.Func
err := spec.FindType(typeName, &target)
if errors.Is(err, btf.ErrNotFound) { if errors.Is(err, btf.ErrNotFound) {
return nil, &internal.UnsupportedFeatureError{ return nil, &internal.UnsupportedFeatureError{
Name: featureName, Name: featureName,
@ -724,5 +756,6 @@ func resolveBTFType(kernel *btf.Spec, name string, progType ProgramType, attachT
if err != nil { if err != nil {
return nil, fmt.Errorf("resolve BTF for %s: %w", featureName, err) return nil, fmt.Errorf("resolve BTF for %s: %w", featureName, err)
} }
return target, nil return target, nil
} }

View File

@ -5,7 +5,7 @@
# Run all tests on a 5.4 kernel # Run all tests on a 5.4 kernel
# $ ./run-tests.sh 5.4 # $ ./run-tests.sh 5.4
# Run a subset of tests: # Run a subset of tests:
# $ ./run-tests.sh 5.4 go test ./link # $ ./run-tests.sh 5.4 ./link
set -euo pipefail set -euo pipefail
@ -48,15 +48,17 @@ if [[ "${1:-}" = "--exec-vm" ]]; then
rm "${output}/fake-stdin" rm "${output}/fake-stdin"
fi fi
$sudo virtme-run --kimg "${input}/bzImage" --memory 768M --pwd \ if ! $sudo virtme-run --kimg "${input}/bzImage" --memory 768M --pwd \
--rwdir="${testdir}=${testdir}" \ --rwdir="${testdir}=${testdir}" \
--rodir=/run/input="${input}" \ --rodir=/run/input="${input}" \
--rwdir=/run/output="${output}" \ --rwdir=/run/output="${output}" \
--script-sh "PATH=\"$PATH\" \"$script\" --exec-test $cmd" \ --script-sh "PATH=\"$PATH\" \"$script\" --exec-test $cmd" \
--qemu-opts -smp 2 # need at least two CPUs for some tests --kopt possible_cpus=2; then # need at least two CPUs for some tests
exit 23
fi
if [[ ! -e "${output}/success" ]]; then if [[ ! -e "${output}/success" ]]; then
exit 1 exit 42
fi fi
$sudo rm -r "$output" $sudo rm -r "$output"
@ -74,7 +76,7 @@ elif [[ "${1:-}" = "--exec-test" ]]; then
dmesg -C dmesg -C
if ! "$@"; then if ! "$@"; then
dmesg dmesg
exit 1 exit 1 # this return code is "swallowed" by qemu
fi fi
touch "/run/output/success" touch "/run/output/success"
exit 0 exit 0
@ -108,7 +110,7 @@ else
echo "No selftests found, disabling" echo "No selftests found, disabling"
fi fi
args=(-v -short -coverpkg=./... -coverprofile=coverage.out -count 1 ./...) args=(-short -coverpkg=./... -coverprofile=coverage.out -count 1 ./...)
if (( $# > 0 )); then if (( $# > 0 )); then
args=("$@") args=("$@")
fi fi

View File

@ -1,13 +1,14 @@
package ebpf package ebpf
import ( import (
"bytes"
"errors" "errors"
"fmt" "fmt"
"os" "os"
"unsafe" "unsafe"
"github.com/cilium/ebpf/asm"
"github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/btf"
"github.com/cilium/ebpf/internal/unix" "github.com/cilium/ebpf/internal/unix"
) )
@ -73,30 +74,6 @@ type bpfMapInfo struct {
btf_value_type_id uint32 btf_value_type_id uint32
} }
type bpfProgLoadAttr struct {
progType ProgramType
insCount uint32
instructions internal.Pointer
license internal.Pointer
logLevel uint32
logSize uint32
logBuf internal.Pointer
kernelVersion uint32 // since 4.1 2541517c32be
progFlags uint32 // since 4.11 e07b98d9bffe
progName internal.BPFObjName // since 4.15 067cae47771c
progIfIndex uint32 // since 4.15 1f6f4cb7ba21
expectedAttachType AttachType // since 4.17 5e43f899b03a
progBTFFd uint32
funcInfoRecSize uint32
funcInfo internal.Pointer
funcInfoCnt uint32
lineInfoRecSize uint32
lineInfo internal.Pointer
lineInfoCnt uint32
attachBTFID btf.TypeID
attachProgFd uint32
}
type bpfProgInfo struct { type bpfProgInfo struct {
prog_type uint32 prog_type uint32
id uint32 id uint32
@ -107,7 +84,7 @@ type bpfProgInfo struct {
xlated_prog_insns internal.Pointer xlated_prog_insns internal.Pointer
load_time uint64 // since 4.15 cb4d2b3f03d8 load_time uint64 // since 4.15 cb4d2b3f03d8
created_by_uid uint32 created_by_uid uint32
nr_map_ids uint32 nr_map_ids uint32 // since 4.15 cb4d2b3f03d8
map_ids internal.Pointer map_ids internal.Pointer
name internal.BPFObjName // since 4.15 067cae47771c name internal.BPFObjName // since 4.15 067cae47771c
ifindex uint32 ifindex uint32
@ -145,11 +122,6 @@ type bpfProgTestRunAttr struct {
duration uint32 duration uint32
} }
type bpfGetFDByIDAttr struct {
id uint32
next uint32
}
type bpfMapFreezeAttr struct { type bpfMapFreezeAttr struct {
mapFd uint32 mapFd uint32
} }
@ -160,23 +132,6 @@ type bpfObjGetNextIDAttr struct {
openFlags uint32 openFlags uint32
} }
func bpfProgLoad(attr *bpfProgLoadAttr) (*internal.FD, error) {
for {
fd, err := internal.BPF(internal.BPF_PROG_LOAD, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
// As of ~4.20 the verifier can be interrupted by a signal,
// and returns EAGAIN in that case.
if errors.Is(err, unix.EAGAIN) {
continue
}
if err != nil {
return nil, err
}
return internal.NewFD(uint32(fd)), nil
}
}
func bpfProgTestRun(attr *bpfProgTestRunAttr) error { func bpfProgTestRun(attr *bpfProgTestRunAttr) error {
_, err := internal.BPF(internal.BPF_PROG_TEST_RUN, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) _, err := internal.BPF(internal.BPF_PROG_TEST_RUN, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
return err return err
@ -372,6 +327,10 @@ func wrapMapError(err error) error {
return internal.SyscallError(ErrNotSupported, unix.ENOTSUPP) return internal.SyscallError(ErrNotSupported, unix.ENOTSUPP)
} }
if errors.Is(err, unix.E2BIG) {
return fmt.Errorf("key too big for map: %w", err)
}
return err return err
} }
@ -388,8 +347,13 @@ func bpfMapFreeze(m *internal.FD) error {
return err return err
} }
func bpfGetProgInfoByFD(fd *internal.FD) (*bpfProgInfo, error) { func bpfGetProgInfoByFD(fd *internal.FD, ids []MapID) (*bpfProgInfo, error) {
var info bpfProgInfo var info bpfProgInfo
if len(ids) > 0 {
info.nr_map_ids = uint32(len(ids))
info.map_ids = internal.NewPointer(unsafe.Pointer(&ids[0]))
}
if err := internal.BPFObjGetInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info)); err != nil { if err := internal.BPFObjGetInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info)); err != nil {
return nil, fmt.Errorf("can't get program info: %w", err) return nil, fmt.Errorf("can't get program info: %w", err)
} }
@ -471,10 +435,30 @@ var haveBatchAPI = internal.FeatureTest("map batch api", "5.6", func() error {
return nil return nil
}) })
func bpfObjGetFDByID(cmd internal.BPFCmd, id uint32) (*internal.FD, error) { var haveProbeReadKernel = internal.FeatureTest("bpf_probe_read_kernel", "5.5", func() error {
attr := bpfGetFDByIDAttr{ insns := asm.Instructions{
id: id, asm.Mov.Reg(asm.R1, asm.R10),
asm.Add.Imm(asm.R1, -8),
asm.Mov.Imm(asm.R2, 8),
asm.Mov.Imm(asm.R3, 0),
asm.FnProbeReadKernel.Call(),
asm.Return(),
} }
ptr, err := internal.BPF(cmd, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) buf := bytes.NewBuffer(make([]byte, 0, len(insns)*asm.InstructionSize))
return internal.NewFD(uint32(ptr)), err if err := insns.Marshal(buf, internal.NativeEndian); err != nil {
} return err
}
bytecode := buf.Bytes()
fd, err := internal.BPFProgLoad(&internal.BPFProgLoadAttr{
ProgType: uint32(Kprobe),
License: internal.NewStringPointer("GPL"),
Instructions: internal.NewSlicePointer(bytecode),
InsCount: uint32(len(bytecode) / asm.InstructionSize),
})
if err != nil {
return internal.ErrNotSupported
}
_ = fd.Close()
return nil
})

View File

@ -4,12 +4,17 @@ import (
"github.com/cilium/ebpf/internal/unix" "github.com/cilium/ebpf/internal/unix"
) )
//go:generate stringer -output types_string.go -type=MapType,ProgramType,AttachType,PinType //go:generate stringer -output types_string.go -type=MapType,ProgramType,PinType
// MapType indicates the type map structure // MapType indicates the type map structure
// that will be initialized in the kernel. // that will be initialized in the kernel.
type MapType uint32 type MapType uint32
// Max returns the latest supported MapType.
func (_ MapType) Max() MapType {
return maxMapType - 1
}
// All the various map types that can be created // All the various map types that can be created
const ( const (
UnspecifiedMap MapType = iota UnspecifiedMap MapType = iota
@ -85,15 +90,28 @@ const (
SkStorage SkStorage
// DevMapHash - Hash-based indexing scheme for references to network devices. // DevMapHash - Hash-based indexing scheme for references to network devices.
DevMapHash DevMapHash
StructOpts // StructOpsMap - This map holds a kernel struct with its function pointer implemented in a BPF
// program.
StructOpsMap
// RingBuf - Similar to PerfEventArray, but shared across all CPUs.
RingBuf RingBuf
// InodeStorage - Specialized local storage map for inodes.
InodeStorage InodeStorage
// TaskStorage - Specialized local storage map for task_struct.
TaskStorage TaskStorage
// maxMapType - Bound enum of MapTypes, has to be last in enum.
maxMapType
) )
// Deprecated: StructOpts was a typo, use StructOpsMap instead.
//
// Declared as a variable to prevent stringer from picking it up
// as an enum value.
var StructOpts MapType = StructOpsMap
// hasPerCPUValue returns true if the Map stores a value per CPU. // hasPerCPUValue returns true if the Map stores a value per CPU.
func (mt MapType) hasPerCPUValue() bool { func (mt MapType) hasPerCPUValue() bool {
return mt == PerCPUHash || mt == PerCPUArray || mt == LRUCPUHash return mt == PerCPUHash || mt == PerCPUArray || mt == LRUCPUHash || mt == PerCPUCGroupStorage
} }
// canStoreMap returns true if the map type accepts a map fd // canStoreMap returns true if the map type accepts a map fd
@ -111,6 +129,11 @@ func (mt MapType) canStoreProgram() bool {
// ProgramType of the eBPF program // ProgramType of the eBPF program
type ProgramType uint32 type ProgramType uint32
// Max return the latest supported ProgramType.
func (_ ProgramType) Max() ProgramType {
return maxProgramType - 1
}
// eBPF program types // eBPF program types
const ( const (
UnspecifiedProgram ProgramType = iota UnspecifiedProgram ProgramType = iota
@ -144,6 +167,7 @@ const (
Extension Extension
LSM LSM
SkLookup SkLookup
maxProgramType
) )
// AttachType of the eBPF program, needed to differentiate allowed context accesses in // AttachType of the eBPF program, needed to differentiate allowed context accesses in
@ -151,6 +175,8 @@ const (
// Will cause invalid argument (EINVAL) at program load time if set incorrectly. // Will cause invalid argument (EINVAL) at program load time if set incorrectly.
type AttachType uint32 type AttachType uint32
//go:generate stringer -type AttachType -trimprefix Attach
// AttachNone is an alias for AttachCGroupInetIngress for readability reasons. // AttachNone is an alias for AttachCGroupInetIngress for readability reasons.
const AttachNone AttachType = 0 const AttachNone AttachType = 0
@ -193,6 +219,10 @@ const (
AttachXDPCPUMap AttachXDPCPUMap
AttachSkLookup AttachSkLookup
AttachXDP AttachXDP
AttachSkSKBVerdict
AttachSkReuseportSelect
AttachSkReuseportSelectOrMigrate
AttachPerfEvent
) )
// AttachFlags of the eBPF program used in BPF_PROG_ATTACH command // AttachFlags of the eBPF program used in BPF_PROG_ATTACH command

View File

@ -1,4 +1,4 @@
// Code generated by "stringer -output types_string.go -type=MapType,ProgramType,AttachType,PinType"; DO NOT EDIT. // Code generated by "stringer -output types_string.go -type=MapType,ProgramType,PinType"; DO NOT EDIT.
package ebpf package ebpf
@ -34,15 +34,16 @@ func _() {
_ = x[Stack-23] _ = x[Stack-23]
_ = x[SkStorage-24] _ = x[SkStorage-24]
_ = x[DevMapHash-25] _ = x[DevMapHash-25]
_ = x[StructOpts-26] _ = x[StructOpsMap-26]
_ = x[RingBuf-27] _ = x[RingBuf-27]
_ = x[InodeStorage-28] _ = x[InodeStorage-28]
_ = x[TaskStorage-29] _ = x[TaskStorage-29]
_ = x[maxMapType-30]
} }
const _MapType_name = "UnspecifiedMapHashArrayProgramArrayPerfEventArrayPerCPUHashPerCPUArrayStackTraceCGroupArrayLRUHashLRUCPUHashLPMTrieArrayOfMapsHashOfMapsDevMapSockMapCPUMapXSKMapSockHashCGroupStorageReusePortSockArrayPerCPUCGroupStorageQueueStackSkStorageDevMapHashStructOptsRingBufInodeStorageTaskStorage" const _MapType_name = "UnspecifiedMapHashArrayProgramArrayPerfEventArrayPerCPUHashPerCPUArrayStackTraceCGroupArrayLRUHashLRUCPUHashLPMTrieArrayOfMapsHashOfMapsDevMapSockMapCPUMapXSKMapSockHashCGroupStorageReusePortSockArrayPerCPUCGroupStorageQueueStackSkStorageDevMapHashStructOpsMapRingBufInodeStorageTaskStoragemaxMapType"
var _MapType_index = [...]uint16{0, 14, 18, 23, 35, 49, 59, 70, 80, 91, 98, 108, 115, 126, 136, 142, 149, 155, 161, 169, 182, 200, 219, 224, 229, 238, 248, 258, 265, 277, 288} var _MapType_index = [...]uint16{0, 14, 18, 23, 35, 49, 59, 70, 80, 91, 98, 108, 115, 126, 136, 142, 149, 155, 161, 169, 182, 200, 219, 224, 229, 238, 248, 260, 267, 279, 290, 300}
func (i MapType) String() string { func (i MapType) String() string {
if i >= MapType(len(_MapType_index)-1) { if i >= MapType(len(_MapType_index)-1) {
@ -85,11 +86,12 @@ func _() {
_ = x[Extension-28] _ = x[Extension-28]
_ = x[LSM-29] _ = x[LSM-29]
_ = x[SkLookup-30] _ = x[SkLookup-30]
_ = x[maxProgramType-31]
} }
const _ProgramType_name = "UnspecifiedProgramSocketFilterKprobeSchedCLSSchedACTTracePointXDPPerfEventCGroupSKBCGroupSockLWTInLWTOutLWTXmitSockOpsSkSKBCGroupDeviceSkMsgRawTracepointCGroupSockAddrLWTSeg6LocalLircMode2SkReuseportFlowDissectorCGroupSysctlRawTracepointWritableCGroupSockoptTracingStructOpsExtensionLSMSkLookup" const _ProgramType_name = "UnspecifiedProgramSocketFilterKprobeSchedCLSSchedACTTracePointXDPPerfEventCGroupSKBCGroupSockLWTInLWTOutLWTXmitSockOpsSkSKBCGroupDeviceSkMsgRawTracepointCGroupSockAddrLWTSeg6LocalLircMode2SkReuseportFlowDissectorCGroupSysctlRawTracepointWritableCGroupSockoptTracingStructOpsExtensionLSMSkLookupmaxProgramType"
var _ProgramType_index = [...]uint16{0, 18, 30, 36, 44, 52, 62, 65, 74, 83, 93, 98, 104, 111, 118, 123, 135, 140, 153, 167, 179, 188, 199, 212, 224, 245, 258, 265, 274, 283, 286, 294} var _ProgramType_index = [...]uint16{0, 18, 30, 36, 44, 52, 62, 65, 74, 83, 93, 98, 104, 111, 118, 123, 135, 140, 153, 167, 179, 188, 199, 212, 224, 245, 258, 265, 274, 283, 286, 294, 308}
func (i ProgramType) String() string { func (i ProgramType) String() string {
if i >= ProgramType(len(_ProgramType_index)-1) { if i >= ProgramType(len(_ProgramType_index)-1) {
@ -97,61 +99,6 @@ func (i ProgramType) String() string {
} }
return _ProgramType_name[_ProgramType_index[i]:_ProgramType_index[i+1]] return _ProgramType_name[_ProgramType_index[i]:_ProgramType_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[AttachNone-0]
_ = x[AttachCGroupInetIngress-0]
_ = x[AttachCGroupInetEgress-1]
_ = x[AttachCGroupInetSockCreate-2]
_ = x[AttachCGroupSockOps-3]
_ = x[AttachSkSKBStreamParser-4]
_ = x[AttachSkSKBStreamVerdict-5]
_ = x[AttachCGroupDevice-6]
_ = x[AttachSkMsgVerdict-7]
_ = x[AttachCGroupInet4Bind-8]
_ = x[AttachCGroupInet6Bind-9]
_ = x[AttachCGroupInet4Connect-10]
_ = x[AttachCGroupInet6Connect-11]
_ = x[AttachCGroupInet4PostBind-12]
_ = x[AttachCGroupInet6PostBind-13]
_ = x[AttachCGroupUDP4Sendmsg-14]
_ = x[AttachCGroupUDP6Sendmsg-15]
_ = x[AttachLircMode2-16]
_ = x[AttachFlowDissector-17]
_ = x[AttachCGroupSysctl-18]
_ = x[AttachCGroupUDP4Recvmsg-19]
_ = x[AttachCGroupUDP6Recvmsg-20]
_ = x[AttachCGroupGetsockopt-21]
_ = x[AttachCGroupSetsockopt-22]
_ = x[AttachTraceRawTp-23]
_ = x[AttachTraceFEntry-24]
_ = x[AttachTraceFExit-25]
_ = x[AttachModifyReturn-26]
_ = x[AttachLSMMac-27]
_ = x[AttachTraceIter-28]
_ = x[AttachCgroupInet4GetPeername-29]
_ = x[AttachCgroupInet6GetPeername-30]
_ = x[AttachCgroupInet4GetSockname-31]
_ = x[AttachCgroupInet6GetSockname-32]
_ = x[AttachXDPDevMap-33]
_ = x[AttachCgroupInetSockRelease-34]
_ = x[AttachXDPCPUMap-35]
_ = x[AttachSkLookup-36]
_ = x[AttachXDP-37]
}
const _AttachType_name = "AttachNoneAttachCGroupInetEgressAttachCGroupInetSockCreateAttachCGroupSockOpsAttachSkSKBStreamParserAttachSkSKBStreamVerdictAttachCGroupDeviceAttachSkMsgVerdictAttachCGroupInet4BindAttachCGroupInet6BindAttachCGroupInet4ConnectAttachCGroupInet6ConnectAttachCGroupInet4PostBindAttachCGroupInet6PostBindAttachCGroupUDP4SendmsgAttachCGroupUDP6SendmsgAttachLircMode2AttachFlowDissectorAttachCGroupSysctlAttachCGroupUDP4RecvmsgAttachCGroupUDP6RecvmsgAttachCGroupGetsockoptAttachCGroupSetsockoptAttachTraceRawTpAttachTraceFEntryAttachTraceFExitAttachModifyReturnAttachLSMMacAttachTraceIterAttachCgroupInet4GetPeernameAttachCgroupInet6GetPeernameAttachCgroupInet4GetSocknameAttachCgroupInet6GetSocknameAttachXDPDevMapAttachCgroupInetSockReleaseAttachXDPCPUMapAttachSkLookupAttachXDP"
var _AttachType_index = [...]uint16{0, 10, 32, 58, 77, 100, 124, 142, 160, 181, 202, 226, 250, 275, 300, 323, 346, 361, 380, 398, 421, 444, 466, 488, 504, 521, 537, 555, 567, 582, 610, 638, 666, 694, 709, 736, 751, 765, 774}
func (i AttachType) String() string {
if i >= AttachType(len(_AttachType_index)-1) {
return "AttachType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _AttachType_name[_AttachType_index[i]:_AttachType_index[i+1]]
}
func _() { func _() {
// An "invalid array index" compiler error signifies that the constant values have changed. // An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again. // Re-run the stringer command to generate them again.

View File

@ -17,10 +17,10 @@
package console package console
import ( import (
"errors"
"fmt" "fmt"
"os" "os"
"github.com/pkg/errors"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
) )
@ -103,7 +103,7 @@ func (m *master) Reset() error {
{m.err, m.errMode}, {m.err, m.errMode},
} { } {
if err := windows.SetConsoleMode(s.fd, s.mode); err != nil { if err := windows.SetConsoleMode(s.fd, s.mode); err != nil {
return errors.Wrap(err, "unable to restore console mode") return fmt.Errorf("unable to restore console mode: %w", err)
} }
} }
@ -114,7 +114,7 @@ func (m *master) Size() (WinSize, error) {
var info windows.ConsoleScreenBufferInfo var info windows.ConsoleScreenBufferInfo
err := windows.GetConsoleScreenBufferInfo(m.out, &info) err := windows.GetConsoleScreenBufferInfo(m.out, &info)
if err != nil { if err != nil {
return WinSize{}, errors.Wrap(err, "unable to get console info") return WinSize{}, fmt.Errorf("unable to get console info: %w", err)
} }
winsize := WinSize{ winsize := WinSize{
@ -139,7 +139,7 @@ func (m *master) DisableEcho() error {
mode |= windows.ENABLE_LINE_INPUT mode |= windows.ENABLE_LINE_INPUT
if err := windows.SetConsoleMode(m.in, mode); err != nil { if err := windows.SetConsoleMode(m.in, mode); err != nil {
return errors.Wrap(err, "unable to set console to disable echo") return fmt.Errorf("unable to set console to disable echo: %w", err)
} }
return nil return nil
@ -192,7 +192,7 @@ func makeInputRaw(fd windows.Handle, mode uint32) error {
} }
if err := windows.SetConsoleMode(fd, mode); err != nil { if err := windows.SetConsoleMode(fd, mode); err != nil {
return errors.Wrap(err, "unable to set console to raw mode") return fmt.Errorf("unable to set console to raw mode: %w", err)
} }
return nil return nil

163
vendor/github.com/containerd/console/console_zos.go generated vendored Normal file
View File

@ -0,0 +1,163 @@
// +build zos
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package console
import (
"fmt"
"os"
"golang.org/x/sys/unix"
)
// NewPty creates a new pty pair
// The master is returned as the first console and a string
// with the path to the pty slave is returned as the second
func NewPty() (Console, string, error) {
var f File
var err error
var slave string
for i := 0;; i++ {
ptyp := fmt.Sprintf("/dev/ptyp%04d", i)
f, err = os.OpenFile(ptyp, os.O_RDWR, 0600)
if err == nil {
slave = fmt.Sprintf("/dev/ttyp%04d", i)
break
}
if os.IsNotExist(err) {
return nil, "", err
}
// else probably Resource Busy
}
m, err := newMaster(f)
if err != nil {
return nil, "", err
}
return m, slave, nil
}
type master struct {
f File
original *unix.Termios
}
func (m *master) Read(b []byte) (int, error) {
return m.f.Read(b)
}
func (m *master) Write(b []byte) (int, error) {
return m.f.Write(b)
}
func (m *master) Close() error {
return m.f.Close()
}
func (m *master) Resize(ws WinSize) error {
return tcswinsz(m.f.Fd(), ws)
}
func (m *master) ResizeFrom(c Console) error {
ws, err := c.Size()
if err != nil {
return err
}
return m.Resize(ws)
}
func (m *master) Reset() error {
if m.original == nil {
return nil
}
return tcset(m.f.Fd(), m.original)
}
func (m *master) getCurrent() (unix.Termios, error) {
var termios unix.Termios
if err := tcget(m.f.Fd(), &termios); err != nil {
return unix.Termios{}, err
}
return termios, nil
}
func (m *master) SetRaw() error {
rawState, err := m.getCurrent()
if err != nil {
return err
}
rawState = cfmakeraw(rawState)
rawState.Oflag = rawState.Oflag | unix.OPOST
return tcset(m.f.Fd(), &rawState)
}
func (m *master) DisableEcho() error {
rawState, err := m.getCurrent()
if err != nil {
return err
}
rawState.Lflag = rawState.Lflag &^ unix.ECHO
return tcset(m.f.Fd(), &rawState)
}
func (m *master) Size() (WinSize, error) {
return tcgwinsz(m.f.Fd())
}
func (m *master) Fd() uintptr {
return m.f.Fd()
}
func (m *master) Name() string {
return m.f.Name()
}
// checkConsole checks if the provided file is a console
func checkConsole(f File) error {
var termios unix.Termios
if tcget(f.Fd(), &termios) != nil {
return ErrNotAConsole
}
return nil
}
func newMaster(f File) (Console, error) {
m := &master{
f: f,
}
t, err := m.getCurrent()
if err != nil {
return nil, err
}
m.original = &t
return m, nil
}
// ClearONLCR sets the necessary tty_ioctl(4)s to ensure that a pty pair
// created by us acts normally. In particular, a not-very-well-known default of
// Linux unix98 ptys is that they have +onlcr by default. While this isn't a
// problem for terminal emulators, because we relay data from the terminal we
// also relay that funky line discipline.
func ClearONLCR(fd uintptr) error {
return setONLCR(fd, false)
}
// SetONLCR sets the necessary tty_ioctl(4)s to ensure that a pty pair
// created by us acts as intended for a terminal emulator.
func SetONLCR(fd uintptr) error {
return setONLCR(fd, true)
}

View File

@ -2,7 +2,4 @@ module github.com/containerd/console
go 1.13 go 1.13
require ( require golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c
github.com/pkg/errors v0.9.1
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c
)

View File

@ -1,4 +1,2 @@
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@ -1,4 +1,4 @@
// +build darwin freebsd linux netbsd openbsd solaris // +build darwin freebsd linux netbsd openbsd solaris zos
/* /*
Copyright The containerd Authors. Copyright The containerd Authors.

26
vendor/github.com/containerd/console/tc_zos.go generated vendored Normal file
View File

@ -0,0 +1,26 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package console
import (
"golang.org/x/sys/unix"
)
const (
cmdTcGet = unix.TCGETS
cmdTcSet = unix.TCSETS
)

View File

@ -4,10 +4,12 @@
language: go language: go
go: go:
- 1.7.x - 1.13.x
- 1.8.x - 1.16.x
- tip - tip
arch:
- AMD64
- ppc64le
os: os:
- linux - linux
- osx - osx

View File

@ -7,6 +7,19 @@ standard library][go#20126]. The purpose of this function is to be a "secure"
alternative to `filepath.Join`, and in particular it provides certain alternative to `filepath.Join`, and in particular it provides certain
guarantees that are not provided by `filepath.Join`. guarantees that are not provided by `filepath.Join`.
> **NOTE**: This code is *only* safe if you are not at risk of other processes
> modifying path components after you've used `SecureJoin`. If it is possible
> for a malicious process to modify path components of the resolved path, then
> you will be vulnerable to some fairly trivial TOCTOU race conditions. [There
> are some Linux kernel patches I'm working on which might allow for a better
> solution.][lwn-obeneath]
>
> In addition, with a slightly modified API it might be possible to use
> `O_PATH` and verify that the opened path is actually the resolved one -- but
> I have not done that yet. I might add it in the future as a helper function
> to help users verify the path (we can't just return `/proc/self/fd/<foo>`
> because that doesn't always work transparently for all users).
This is the function prototype: This is the function prototype:
```go ```go
@ -16,8 +29,8 @@ func SecureJoin(root, unsafePath string) (string, error)
This library **guarantees** the following: This library **guarantees** the following:
* If no error is set, the resulting string **must** be a child path of * If no error is set, the resulting string **must** be a child path of
`SecureJoin` and will not contain any symlink path components (they will all `root` and will not contain any symlink path components (they will all be
be expanded). expanded).
* When expanding symlinks, all symlink path components **must** be resolved * When expanding symlinks, all symlink path components **must** be resolved
relative to the provided root. In particular, this can be considered a relative to the provided root. In particular, this can be considered a
@ -25,7 +38,7 @@ This library **guarantees** the following:
these symlinks will **not** be expanded lexically (`filepath.Clean` is not these symlinks will **not** be expanded lexically (`filepath.Clean` is not
called on the input before processing). called on the input before processing).
* Non-existant path components are unaffected by `SecureJoin` (similar to * Non-existent path components are unaffected by `SecureJoin` (similar to
`filepath.EvalSymlinks`'s semantics). `filepath.EvalSymlinks`'s semantics).
* The returned path will always be `filepath.Clean`ed and thus not contain any * The returned path will always be `filepath.Clean`ed and thus not contain any
@ -57,6 +70,7 @@ func SecureJoin(root, unsafePath string) (string, error) {
} }
``` ```
[lwn-obeneath]: https://lwn.net/Articles/767547/
[go#20126]: https://github.com/golang/go/issues/20126 [go#20126]: https://github.com/golang/go/issues/20126
### License ### ### License ###

View File

@ -1 +1 @@
0.2.2 0.2.3

3
vendor/github.com/cyphar/filepath-securejoin/go.mod generated vendored Normal file
View File

@ -0,0 +1,3 @@
module github.com/cyphar/filepath-securejoin
go 1.13

View File

@ -12,39 +12,20 @@ package securejoin
import ( import (
"bytes" "bytes"
"errors"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"syscall" "syscall"
"github.com/pkg/errors"
) )
// ErrSymlinkLoop is returned by SecureJoinVFS when too many symlinks have been
// evaluated in attempting to securely join the two given paths.
var ErrSymlinkLoop = errors.Wrap(syscall.ELOOP, "secure join")
// IsNotExist tells you if err is an error that implies that either the path // IsNotExist tells you if err is an error that implies that either the path
// accessed does not exist (or path components don't exist). This is // accessed does not exist (or path components don't exist). This is
// effectively a more broad version of os.IsNotExist. // effectively a more broad version of os.IsNotExist.
func IsNotExist(err error) bool { func IsNotExist(err error) bool {
// If it's a bone-fide ENOENT just bail.
if os.IsNotExist(errors.Cause(err)) {
return true
}
// Check that it's not actually an ENOTDIR, which in some cases is a more // Check that it's not actually an ENOTDIR, which in some cases is a more
// convoluted case of ENOENT (usually involving weird paths). // convoluted case of ENOENT (usually involving weird paths).
var errno error return errors.Is(err, os.ErrNotExist) || errors.Is(err, syscall.ENOTDIR) || errors.Is(err, syscall.ENOENT)
switch err := errors.Cause(err).(type) {
case *os.PathError:
errno = err.Err
case *os.LinkError:
errno = err.Err
case *os.SyscallError:
errno = err.Err
}
return errno == syscall.ENOTDIR || errno == syscall.ENOENT
} }
// SecureJoinVFS joins the two given path components (similar to Join) except // SecureJoinVFS joins the two given path components (similar to Join) except
@ -68,7 +49,7 @@ func SecureJoinVFS(root, unsafePath string, vfs VFS) (string, error) {
n := 0 n := 0
for unsafePath != "" { for unsafePath != "" {
if n > 255 { if n > 255 {
return "", ErrSymlinkLoop return "", &os.PathError{Op: "SecureJoin", Path: root + "/" + unsafePath, Err: syscall.ELOOP}
} }
// Next path component, p. // Next path component, p.

View File

@ -1 +0,0 @@
github.com/pkg/errors v0.8.0

View File

@ -56,6 +56,35 @@ func ParseNormalizedNamed(s string) (Named, error) {
return named, nil return named, nil
} }
// ParseDockerRef normalizes the image reference following the docker convention. This is added
// mainly for backward compatibility.
// The reference returned can only be either tagged or digested. For reference contains both tag
// and digest, the function returns digested reference, e.g. docker.io/library/busybox:latest@
// sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa will be returned as
// docker.io/library/busybox@sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa.
func ParseDockerRef(ref string) (Named, error) {
named, err := ParseNormalizedNamed(ref)
if err != nil {
return nil, err
}
if _, ok := named.(NamedTagged); ok {
if canonical, ok := named.(Canonical); ok {
// The reference is both tagged and digested, only
// return digested.
newNamed, err := WithName(canonical.Name())
if err != nil {
return nil, err
}
newCanonical, err := WithDigest(newNamed, canonical.Digest())
if err != nil {
return nil, err
}
return newCanonical, nil
}
}
return TagNameOnly(named), nil
}
// splitDockerDomain splits a repository name to domain and remotename string. // splitDockerDomain splits a repository name to domain and remotename string.
// If no valid domain is found, the default domain is used. Repository name // If no valid domain is found, the default domain is used. Repository name
// needs to be already validated before. // needs to be already validated before.

View File

@ -205,7 +205,7 @@ func Parse(s string) (Reference, error) {
var repo repository var repo repository
nameMatch := anchoredNameRegexp.FindStringSubmatch(matches[1]) nameMatch := anchoredNameRegexp.FindStringSubmatch(matches[1])
if nameMatch != nil && len(nameMatch) == 3 { if len(nameMatch) == 3 {
repo.domain = nameMatch[1] repo.domain = nameMatch[1]
repo.path = nameMatch[2] repo.path = nameMatch[2]
} else { } else {

Some files were not shown because too many files have changed in this diff Show More