[
  {
    "path": ".clang-format",
    "content": "---\nLanguage:        Cpp\nBasedOnStyle:    LLVM\nAlignAfterOpenBracket: DontAlign\nAlignConsecutiveAssignments: true\nAlignEscapedNewlines: DontAlign\n# mkdocs annotations in source code are written as trailing comments\n# and alignment pushes these really far away from the content.\nAlignTrailingComments: false\nAlwaysBreakBeforeMultilineStrings: true\nAlwaysBreakTemplateDeclarations: false\nAllowAllParametersOfDeclarationOnNextLine: false\nAllowShortFunctionsOnASingleLine: false\nBreakBeforeBraces: Attach\nIndentWidth:     4\nKeepEmptyLinesAtTheStartOfBlocks: false\nTabWidth:        4\nUseTab:          ForContinuationAndIndentation\nColumnLimit:     1000\n# Go compiler comments need to stay unindented.\nCommentPragmas: '^go:.*'\n# linux/bpf.h needs to be included before bpf/bpf_helpers.h for types like __u64\n# and sorting makes this impossible.\nSortIncludes: false\n...\n"
  },
  {
    "path": ".gitattributes",
    "content": "# Force line ending normalisation\n*\ttext=auto\n# Show types.go in the PR diff view by default\ninternal/sys/types.go linguist-generated=false\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "content": "name: Bug report\ndescription: Create a report to help us improve\nlabels: [\"bug\"]\nassignees: []\n\nbody:\n  - type: markdown\n    attributes:\n      value: \"Thank you for reporting a bug. Please fill out the fields below.\"\n\n  - type: textarea\n    attributes:\n      label: Describe the bug\n      description: |\n        A clear and concise description of what the bug is.\n        Include what you expected to happen instead.\n    validations:\n        required: true\n\n  - type: textarea\n    attributes:\n      label: How to reproduce\n      description: \"Steps to reproduce the behavior.\"\n    validations:\n        required: true\n\n  - type: input\n    id: version\n    attributes:\n      label: Version information\n      description: The output of `go list -m github.com/cilium/ebpf`.\n      placeholder: github.com/cilium/ebpf vX.Y.Z\n    validations:\n        required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "contact_links:\n  - name: Questions\n    url: https://github.com/cilium/ebpf/discussions/categories/q-a\n    about: Please ask and answer questions here.\n  - name: Slack\n    url: https://cilium.slack.com/messages/ebpf-go\n    about: Join our slack.\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "---\nversion: 2\nupdates:\n  - package-ecosystem: \"pip\"\n    directory: \"/docs\"\n    schedule:\n      interval: \"monthly\"\n    allow:\n      # Only manage direct dependencies in Pipfile, ignore transient\n      # dependencies only appearing in Pipfile.lock.\n      - dependency-name: \"*\"\n        dependency-type: \"direct\"\n    groups:\n      docs:\n        dependency-type: production\n        applies-to: version-updates\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"monthly\"\n"
  },
  {
    "path": ".github/workflows/apidiff.yml",
    "content": "name: apidiff\non:\n  push:\n    branches: [ \"main\" ]\n  pull_request:\n    branches: [ \"main\" ]\n\njobs:\n  go-apidiff:\n    name: go-apidiff\n    runs-on: ubuntu-latest\n    if: github.event_name == 'pull_request'\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n\n      - uses: actions/setup-go@v6\n        with:\n          go-version-file: go.mod\n\n      - name: Run go-apidiff\n        id: apidiff\n        continue-on-error: true\n        uses: joelanford/go-apidiff@main\n\n      - name: Create apidiff.json\n        run: |\n          echo '{\"id\": ${{ github.event.pull_request.number }}, \"semver-type\": \"${{ steps.apidiff.outputs.semver-type }}\"}' > apidiff.json\n\n      - name: Upload apidiff.json\n        uses: actions/upload-artifact@v7\n        with:\n          name: apidiff\n          path: apidiff.json\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: ci\non:\n  push:\n    branches: [ \"main\" ]\n  pull_request:\n    branches: [ \"main\" ]\n\nenv:\n  TMPDIR: /tmp\n  CI_MAX_KERNEL_VERSION: '6.18'\n  CI_MAX_EFW_VERSION: '1.0.0-rc2'\n  CI_MIN_CLANG_VERSION: '13'\n  go_version: '~1.25'\n  prev_go_version: '~1.24'\n  CGO_ENABLED: '0'\n  # Sync with Pipfile and netlify.toml.\n  python_version: '~3.13'\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: ${{ github.event_name == 'pull_request' }}\n\njobs:\n  build-and-lint:\n    name: Build and Lint\n    runs-on: ubuntu-22.04\n    timeout-minutes: 10\n    steps:\n      - uses: actions/checkout@v6\n\n      - name: Set up Go\n        uses: actions/setup-go@v6\n        with:\n          go-version: '${{ env.go_version }}'\n\n      - name: Run staticcheck\n        uses: dominikh/staticcheck-action@v1\n        with:\n          version: \"master\"\n          install-go: false\n\n      - name: Run golangci-lint\n        uses: golangci/golangci-lint-action@v9.2.0\n\n      - name: Generate and format code\n        run: |\n          make clean && make container-all\n          if ! git diff --exit-code; then\n            echo \"found unformatted source files, or generated files are not up to date, run 'make'\" >&2\n            exit 1\n          fi\n\n      - name: Test bpf2go\n        run: |\n          go test -v ./cmd/bpf2go\n\n      - name: Build\n        run: go build -v ./...\n\n  cross-build:\n    name: Cross build\n    runs-on: ubuntu-22.04\n    needs: build-and-lint\n    timeout-minutes: 10\n    steps:\n      - uses: actions/checkout@v6\n\n      - name: Set up Go\n        uses: actions/setup-go@v6\n        with:\n          go-version: '${{ env.go_version }}'\n\n      - name: Cross build darwin\n        env:\n          GOOS: darwin\n        run: |\n          go build -v ./...\n          go test -c -o /dev/null ./... >/dev/null\n\n      - name: Cross build arm32\n        env:\n          GOARCH: arm\n          GOARM: 6\n        run: |\n          go build -v ./...\n          go test -c -o /dev/null ./... >/dev/null\n\n      - name: Cross build wasm\n        env:\n          GOOS: js\n          GOARCH: wasm\n        run: |\n          go build -v ./...\n          go test -c -o /dev/null ./... >/dev/null\n\n  build-docs:\n    name: Build Documentation\n    runs-on: ubuntu-22.04\n    timeout-minutes: 10\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          # The mkdocs git-authors plugin needs access to the full revision\n          # history to correctly generate its statistics.\n          fetch-depth: 0\n\n      - name: Set up Go\n        uses: actions/setup-go@v6\n        with:\n          go-version: '${{ env.go_version }}'\n\n      - name: Set up Python\n        uses: actions/setup-python@v6\n        with:\n          python-version: '${{ env.python_version }}'\n          cache: 'pipenv'\n\n      - name: Install pipenv\n        run: pip3 install pipenv\n\n      - name: Install Dependencies\n        run: pipenv install\n        working-directory: ./docs\n\n      - name: Build Documentation\n        run: make build\n        working-directory: ./docs\n\n  test-on-prev-go:\n    name: Run tests on previous stable Go\n    runs-on: ubuntu-latest\n    needs: build-and-lint\n    timeout-minutes: 15\n    env:\n      CI_KERNEL_SELFTESTS: '/usr/src/linux/tools/testing/selftests/bpf'\n    steps:\n      - uses: actions/checkout@v6\n\n      - name: Set up Go\n        uses: actions/setup-go@v6\n        with:\n          go-version: '${{ env.prev_go_version }}'\n\n      - run: go install lmb.io/vimto@latest\n      - run: go install gotest.tools/gotestsum@v1.12.3\n      - run: sudo apt-get update && sudo apt-get install -y --no-install-recommends qemu-system-x86\n      - run: sudo chmod 0666 /dev/kvm\n\n      - name: Test\n        env:\n          GOTRACEBACK: crash\n          CGO_ENABLED: 1 # CGo is required by `-race`\n        run: |\n          gotestsum --raw-command --ignore-non-json-output-lines --junitfile junit.xml -- vimto -kernel :stable-selftests -- go test -race -timeout 5m -short -count 1 -json ./...\n\n      - name: Benchmark\n        run: vimto -kernel :stable-selftests -- go test -short -run '^$' -bench . -benchtime=1x ./...\n\n      - name: Upload coredumps\n        uses: actions/upload-artifact@v7\n        if: ${{ failure() }}\n        with:\n          name: cores\n          if-no-files-found: ignore\n          path: |\n            **/core-*\n            **/*.test\n\n      - name: Upload Test Results\n        if: always()\n        uses: actions/upload-artifact@v7\n        with:\n          name: Test Results (previous stable Go)\n          path: junit.xml\n\n  test-on-arm64:\n    name: Run tests on arm64\n    runs-on: ubuntu-24.04-arm64\n    needs: build-and-lint\n    timeout-minutes: 15\n    env:\n      EBPF_TEST_IGNORE_VERSION: 'TestKprobeMulti,TestKprobeMultiErrors,TestKprobeMultiCookie,TestKprobeMultiProgramCall,TestHaveBPFLinkKprobeMulti,TestKprobeSession,TestHaveBPFLinkKprobeSession,TestHaveProgramType/LircMode2'\n    steps:\n      - uses: actions/checkout@v6\n\n      - name: Set up Go\n        uses: actions/setup-go@v6\n        with:\n          go-version: '${{ env.go_version }}'\n\n      - run: go install gotest.tools/gotestsum@v1.12.3\n\n      - name: Test\n        # Skip TestGoarches/loong64 because the GH arm64 Go toolchain seems to be weird.\n        # Ubuntu 24.04 crashes when executing TestKfunc.\n        run: gotestsum --ignore-non-json-output-lines --junitfile junit.xml -- -exec 'sudo -E' -short -count 1 -skip '^TestGoarches/loong64$' -skip '^TestKfunc$' -json ./...\n\n      - name: Benchmark\n        run: go test -exec sudo -short -run '^$' -bench . -benchtime=1x ./...\n\n      - name: Upload Test Results\n        if: always()\n        uses: actions/upload-artifact@v7\n        with:\n          name: Test Results (arm64)\n          path: junit.xml\n\n      - name: Show dmesg\n        if: failure()\n        run: |\n          sudo dmesg\n\n  linux-test:\n    name: Run tests (Linux)\n    runs-on: ubuntu-latest\n    needs: build-and-lint\n    timeout-minutes: 15\n    strategy:\n      matrix:\n        tag:\n          - \"mainline\"\n          - \"stable\"\n          - \"6.12\"\n          - \"6.6\"\n          - \"6.1\"\n          - \"5.15\"\n          - \"5.10\"\n          - \"5.4\"\n    steps:\n      - uses: actions/checkout@v6\n\n      - name: Set up Go\n        uses: actions/setup-go@v6\n        with:\n          go-version: '${{ env.go_version }}'\n\n      - run: go install gotest.tools/gotestsum@v1.12.3\n      - run: go install lmb.io/vimto@latest\n      - run: sudo apt-get update && sudo apt-get install -y --no-install-recommends qemu-system-x86\n      - run: sudo chmod 0666 /dev/kvm\n\n      - name: Test\n        run: gotestsum --raw-command --ignore-non-json-output-lines --junitfile junit.xml -- vimto -kernel :${{ matrix.tag }} -- go test -short -count 1 -json ./...\n\n      - name: Upload Test Results\n        if: always()\n        uses: actions/upload-artifact@v7\n        with:\n          name: Test Results (linux ${{ matrix.tag }})\n          path: junit.xml\n\n  windows-test:\n    name: Run tests (Windows)\n    runs-on: windows-2022\n    needs: build-and-lint\n    timeout-minutes: 15\n    strategy:\n      matrix:\n        version:\n          - \"main\"\n          - \"1.0.0-rc1\"\n    env:\n      # Fix slow Go compile and cache restore\n      # See https://github.com/actions/setup-go/pull/515\n      GOCACHE: D:\\gocache\n      GOMODCACHE: D:\\gomodcache\n      # Avoid putting temp on slow C:\n      TEMP: D:\\temp\n      CI_EFW_VERSION: \"0.21.0\"\n\n    steps:\n      - run: mkdir D:\\temp\n        shell: pwsh\n\n      - name: Get eBPF for Windows download URL\n        id: determine-url\n        uses: actions/github-script@v8\n        with:\n          script: |\n            if (\"${{ matrix.version }}\" != \"main\") {\n              // Use version from matrix to fetch from release\n              const version = \"${{ matrix.version }}\";\n              const releaseTag = `Release-v${version}`;\n\n              console.log(`Fetching release: ${releaseTag}`);\n\n              // Get the release by tag\n              const release = await github.rest.repos.getReleaseByTag({\n                owner: 'microsoft',\n                repo: 'ebpf-for-windows',\n                tag: releaseTag\n              });\n\n              if (!release.data) {\n                core.setFailed(`Release ${releaseTag} not found`);\n                return;\n              }\n\n              console.log(`Found release: ${release.data.name}`);\n\n              // Find the Build.Debug.x64.zip asset\n              const assetName = 'Build.Debug.x64.zip';\n              const asset = release.data.assets.find(a => a.name === assetName);\n\n              if (!asset) {\n                console.log('Available assets:', release.data.assets.map(a => a.name));\n                core.setFailed(`${assetName} asset not found in release ${releaseTag}`);\n                return;\n              }\n\n              const download_url = asset.browser_download_url;\n              console.log(`Download URL: ${download_url}`);\n\n              core.setOutput('download_url', download_url);\n              return\n            }\n            \n            // Get the latest successful merge_group run\n            const workflow_runs = await github.rest.actions.listWorkflowRuns({\n              owner: 'microsoft',\n              repo: 'ebpf-for-windows',\n              workflow_id: 'cicd.yml',\n              event: 'schedule',\n              branch: 'main',\n              status: 'completed',\n              per_page: 1\n            });\n\n            if (workflow_runs.data.workflow_runs.length === 0) {\n              core.setFailed('No successful merge_group workflow runs found');\n              return;\n            }\n\n            // Get artifacts from this run\n            const run_id = workflow_runs.data.workflow_runs[0].id;\n            const run_url = workflow_runs.data.workflow_runs[0].html_url;\n            console.log(`Using workflow run: ${run_url}`);\n\n            // Paginate through all artifacts\n            let allArtifacts = [];\n            let page = 1;\n            while (true) {\n              const artifacts = await github.rest.actions.listWorkflowRunArtifacts({\n                owner: 'microsoft',\n                repo: 'ebpf-for-windows',\n                run_id: run_id,\n                per_page: 100,\n                page: page\n              });\n\n              allArtifacts = allArtifacts.concat(artifacts.data.artifacts);\n\n              // If we got fewer than 100, we've reached the last page\n              if (artifacts.data.artifacts.length < 100) break;\n              page++;\n            }\n\n            // Find the specific artifact\n            const artifact = allArtifacts.find(a => a.name === 'Build-x64-Debug');\n\n            if (!artifact) {\n              console.log('Available artifacts:', artifacts.data.artifacts.map(a => a.name));\n              core.setFailed('Build-x64-Debug artifact not found in the workflow run');\n              return;\n            }\n\n            // Get the download URL via redirect\n            const response = await github.rest.actions.downloadArtifact({\n              owner: 'microsoft',\n              repo: 'ebpf-for-windows',\n              artifact_id: artifact.id,\n              archive_format: 'zip',\n              request: {\n                redirect: 'manual'\n              }\n            });\n\n            // Extract the location header which contains the actual download URL\n            const download_url = response.url;\n\n            if (!download_url) {\n              core.setFailed('Failed to get redirect URL from headers');\n              return;\n            }\n\n            core.setOutput('download_url', download_url);\n\n      - name: Download and Install eBPF for Windows\n        shell: pwsh\n        run: |\n          Invoke-WebRequest -Uri \"${{ steps.determine-url.outputs.download_url }}\" -OutFile \"$env:TEMP\\efw.zip\"\n\n          if (\"${{ matrix.version }}\" -eq \"main\") {\n            # Workflow artifact has nested structure: outer zip contains build-Debug.zip\n            Expand-Archive -Path \"$env:TEMP\\efw.zip\" -DestinationPath \"$env:TEMP\"\n            Expand-Archive -Path \"$env:TEMP\\build-Debug.zip\" -DestinationPath \"$env:TEMP\\ebpf\"\n          } else {\n            # Release asset is the final zip, extract directly\n            Expand-Archive -Path \"$env:TEMP\\efw.zip\" -DestinationPath \"$env:TEMP\\ebpf\"\n          }\n\n          $setupScript = Get-ChildItem -Path \"$env:TEMP\\ebpf\" -Filter \"install_ebpf.psm1\" -Recurse | Select-Object -First 1\n          if ($setupScript) {\n            Write-Host \"Found setup script: $($setupScript.FullName)\"\n            $releasePath = \"$env:TEMP\\ebpf\\release\"\n            Rename-Item -Path $setupScript.DirectoryName -NewName $releasePath\n            Write-Host \"Renamed directory to: $releasePath\"\n            Set-Location -Path $releasePath\n            Write-Host \"Changed directory to: $(Get-Location)\"\n            Import-Module .\\\\install_ebpf.psm1 -ArgumentList ($pwd, \"install.log\") -Force\n            Get-PSExec\n            Install-eBPFComponents -KmTracing $false -KmTraceType \"file\" -TestMode \"Normal\"\n          } else {\n            Write-Error \"Setup script not found in the extracted package\"\n            exit 1\n          }\n\n      - name: Add eBPF for Windows to PATH\n        shell: pwsh\n        run: echo \"C:\\Program Files\\ebpf-for-windows\\\" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append\n\n      - uses: actions/checkout@v6\n\n      - name: Set up Go\n        uses: actions/setup-go@v6\n        with:\n          go-version: '${{ env.go_version }}'\n\n      - run: go install gotest.tools/gotestsum@v1.12.3\n\n      - name: Test\n        run: >\n          gotestsum --raw-command --ignore-non-json-output-lines --junitfile junit.xml --\n          go test -short -count 1 -json ./...\n\n      - name: Upload Test Results\n        if: always()\n        uses: actions/upload-artifact@v7\n        with:\n          name: Test Results (windows ${{ matrix.version }})\n          path: junit.xml\n\n  results:\n    name: Results\n    runs-on: ubuntu-latest\n    needs:\n      - build-and-lint\n      - cross-build\n      - build-docs\n      - test-on-prev-go\n      - test-on-arm64\n      - linux-test\n      - windows-test\n    if: always()\n    steps:\n      - name: Check Results\n        run: |\n          if [[ \"${{ contains(needs.*.result, 'failure') }}\" == \"true\" ]]; then\n            echo \"Some checks failed\"\n            exit 1\n          else\n            echo \"All checks passed successfully\"\n          fi\n"
  },
  {
    "path": ".github/workflows/trusted.yml",
    "content": "on:\n  workflow_run:\n    workflows: [\"apidiff\"]\n    types:\n      - completed\n\npermissions:\n  pull-requests: write\n\njobs:\n  tag-breaking-change:\n    name: Tag breaking changes\n    runs-on: ubuntu-latest\n    if: github.event.workflow_run.event == 'pull_request'\n    steps:\n      - name: 'Download artifact'\n        uses: actions/github-script@v8\n        with:\n          script: |\n            var artifacts = await github.rest.actions.listWorkflowRunArtifacts({\n               owner: context.repo.owner,\n               repo: context.repo.repo,\n               run_id: ${{github.event.workflow_run.id }},\n            });\n            var matchArtifact = artifacts.data.artifacts.filter((artifact) => {\n              return artifact.name == \"apidiff\"\n            })[0];\n            var download = await github.rest.actions.downloadArtifact({\n               owner: context.repo.owner,\n               repo: context.repo.repo,\n               artifact_id: matchArtifact.id,\n               archive_format: 'zip',\n            });\n            var fs = require('fs');\n            fs.writeFileSync('${{github.workspace}}/apidiff.zip', Buffer.from(download.data));\n      - run: unzip apidiff.zip\n      - name: 'Add or remove label'\n        uses: actions/github-script@v8\n        with:\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n          script: |\n            var fs = require('fs');\n            var jsonData = JSON.parse(fs.readFileSync('apidiff.json', 'utf8'));\n\n            var issueNumber = jsonData.id;\n            var semverType = jsonData[\"semver-type\"];\n\n            if (semverType === 'major') {\n              // Add 'breaking-change' label\n              await github.rest.issues.addLabels({\n                owner: context.repo.owner,\n                repo: context.repo.repo,\n                issue_number: issueNumber,\n                labels: ['breaking-change']\n              });\n            } else {\n              // Remove 'breaking-change' label if it exists\n              try {\n                await github.rest.issues.removeLabel({\n                  owner: context.repo.owner,\n                  repo: context.repo.repo,\n                  issue_number: issueNumber,\n                  name: 'breaking-change'\n                });\n              } catch (error) {\n                console.log('Label breaking-change not found or already removed');\n              }\n            }"
  },
  {
    "path": ".gitignore",
    "content": "# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\n*.o\n!*_bpf*.o\n\n# Test binary, build with `go test -c`\n*.test\n\n# Output of the go coverage tool, specifically when used with LiteIDE\n*.out\n"
  },
  {
    "path": ".golangci.yaml",
    "content": "version: \"2\"\nlinters:\n  default: none\n  enable:\n    - depguard\n    - govet\n    - ineffassign\n    - misspell\n    - unused\n  settings:\n    depguard:\n      rules:\n        no-x-sys-unix:\n          files:\n            - '!**/internal/unix/*.go'\n            - '!**/examples/**/*.go'\n            - '!**/docs/**/*.go'\n          deny:\n            - pkg: golang.org/x/sys/unix\n              desc: use internal/unix instead\n\nformatters:\n  enable:\n    - gofmt\n    - goimports\n  settings:\n    goimports:\n      local-prefixes:\n        - github.com/cilium/ebpf\n"
  },
  {
    "path": ".vimto.toml",
    "content": "kernel=\"ghcr.io/cilium/ci-kernels:stable\"\nsmp=\"cpus=2\"\nmemory=\"1G\"\nuser=\"root\"\nsetup=[\n  \"mount -t cgroup2 -o nosuid,noexec,nodev cgroup2 /sys/fs/cgroup\",\n  \"/bin/sh -c 'modprobe bpf_testmod || true'\",\n  \"dmesg --clear\",\n]\nteardown=[\n  \"dmesg --read-clear\",\n]\n"
  },
  {
    "path": "CODEOWNERS",
    "content": "* @cilium/ebpf-lib-maintainers\n\n/features/ @rgo3\n/link/ @mmat11\n\n/perf/ @florianl\n/ringbuf/ @florianl\n\n/btf/ @dylandreimerink\n\n/docs/ @ti-mo\n\n# Windows specific code.\n/docs/**/windows*.md @cilium/ebpf-go-windows-reviewers\n/internal/efw @cilium/ebpf-go-windows-reviewers\nwindows/ @cilium/ebpf-go-windows-reviewers # Folders\n*windows*.go @cilium/ebpf-go-windows-reviewers # Go code\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment include:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at nathanjsweet at gmail dot com or i at lmb dot io. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]\n\n[homepage]: http://contributor-covenant.org\n[version]: http://contributor-covenant.org/version/1/4/\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to ebpf-go\n\nWant to contribute to ebpf-go? There are a few things you need to know.\n\nWe wrote a [contribution guide](https://ebpf-go.dev/contributing/) to help you get started.\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2017 Nathan Sweet\nCopyright (c) 2018, 2019 Cloudflare\nCopyright (c) 2019 Authors of Cilium\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "MAINTAINERS.md",
    "content": "# Maintainers\n\nMaintainers can be found in the [Cilium Maintainers file](https://github.com/cilium/community/blob/main/roles/Maintainers.md)\n"
  },
  {
    "path": "Makefile",
    "content": "# The development version of clang is distributed as the 'clang' binary,\n# while stable/released versions have a version number attached.\n# Pin the default clang to a stable version.\nCLANG ?= clang-20\nSTRIP ?= llvm-strip-20\nOBJCOPY ?= llvm-objcopy-20\nCFLAGS := -O2 -g -Wall -Werror -mcpu=v2 $(CFLAGS)\n\nCI_KERNEL_URL ?= https://github.com/cilium/ci-kernels/raw/master/\n\n# Obtain an absolute path to the directory of the Makefile.\n# Assume the Makefile is in the root of the repository.\nREPODIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))\n\n# Prefer podman if installed, otherwise use docker.\n# Note: Setting the var at runtime will always override.\nCONTAINER_ENGINE ?= $(if $(shell command -v podman),podman,docker)\n\n# Configure container runtime arguments based on the container engine.\nCONTAINER_RUN_ARGS := \\\n\t--env MAKEFLAGS \\\n\t--env BPF2GO_CC=\"$(CLANG)\" \\\n\t--env BPF2GO_CFLAGS=\"$(CFLAGS)\" \\\n\t--env HOME=/tmp \\\n\t-v \"${REPODIR}\":/ebpf -w /ebpf \\\n\t-v \"$(shell go env GOCACHE)\":/tmp/.cache/go-build \\\n\t-v \"$(shell go env GOPATH)\":/go \\\n\t-v \"$(shell go env GOMODCACHE)\":/go/pkg/mod\n\nifeq ($(CONTAINER_ENGINE), podman)\nCONTAINER_RUN_ARGS += --log-driver=none --security-opt label=disable\nelse\nCONTAINER_RUN_ARGS += --user \"$(shell stat -c '%u:%g' ${REPODIR})\"\nendif\n\nIMAGE := $(shell cat ${REPODIR}/testdata/docker/IMAGE)\nVERSION := $(shell cat ${REPODIR}/testdata/docker/VERSION)\n\nTARGETS_EL := \\\n\ttestdata/linked1 \\\n\ttestdata/linked2 \\\n\ttestdata/linked\n\nTARGETS := \\\n\ttestdata/loader-clang-14 \\\n\ttestdata/loader-clang-17 \\\n\ttestdata/loader-$(CLANG) \\\n\ttestdata/loader_nobtf \\\n\ttestdata/manyprogs \\\n\ttestdata/btf_map_init \\\n\ttestdata/invalid_map \\\n\ttestdata/raw_tracepoint \\\n\ttestdata/invalid_map_static \\\n\ttestdata/invalid_btf_map_init \\\n\ttestdata/strings \\\n\ttestdata/freplace \\\n\ttestdata/fentry_fexit \\\n\ttestdata/iproute2_map_compat \\\n\ttestdata/map_spin_lock \\\n\ttestdata/subprog_reloc \\\n\ttestdata/fwd_decl \\\n\ttestdata/kconfig \\\n\ttestdata/ksym \\\n\ttestdata/kfunc \\\n\ttestdata/invalid-kfunc \\\n\ttestdata/kfunc-kmod \\\n\ttestdata/constants \\\n\ttestdata/errors \\\n\ttestdata/variables \\\n\ttestdata/arena \\\n\ttestdata/struct_ops \\\n\tbtf/testdata/relocs \\\n\tbtf/testdata/relocs_read \\\n\tbtf/testdata/relocs_read_tgt \\\n\tbtf/testdata/relocs_enum \\\n\tbtf/testdata/tags \\\n\tcmd/bpf2go/testdata/minimal\n\nHEADERS := $(wildcard testdata/*.h)\n\n.PHONY: all clean container-all container-shell generate\n\n.DEFAULT_TARGET = container-all\n\n# Build all ELF binaries using a containerized LLVM toolchain.\ncontainer-all:\n\t+${CONTAINER_ENGINE} run --rm -ti ${CONTAINER_RUN_ARGS} \\\n\t\t\"${IMAGE}:${VERSION}\" \\\n\t\t$(MAKE) all\n\n# (debug) Drop the user into a shell inside the container as root.\n# Set BPF2GO_ envs to make 'make generate' just work.\ncontainer-shell:\n\t${CONTAINER_ENGINE} run --rm -ti ${CONTAINER_RUN_ARGS} \\\n\t\t\"${IMAGE}:${VERSION}\"\n\nclean:\n\tfind \"$(CURDIR)\" -name \"*.elf\" -delete\n\tfind \"$(CURDIR)\" -name \"*.o\" -delete\n\nformat:\n\tfind . -type f -name \"*.c\" | xargs clang-format -i\n\nall: format testdata update-external-deps\n\tln -srf testdata/loader-$(CLANG)-el.elf testdata/loader-el.elf\n\tln -srf testdata/loader-$(CLANG)-eb.elf testdata/loader-eb.elf\n\t$(MAKE) generate\n\ngenerate:\n\tgo generate -run \"stringer\" ./...\n\tgo generate -run \"gentypes\" ./...\n\tgo generate -skip \"(gentypes|stringer)\" ./...\n\ntestdata: $(addsuffix -el.elf,$(TARGETS)) $(addsuffix -eb.elf,$(TARGETS)) $(addsuffix -el.elf,$(TARGETS_EL))\n\ntestdata/loader-%-el.elf: testdata/loader.c $(HEADERS)\n\t$* $(CFLAGS) -target bpfel -c $< -o $@\n\t$(STRIP) -g $@\n\ntestdata/loader-%-eb.elf: testdata/loader.c $(HEADERS)\n\t$* $(CFLAGS) -target bpfeb -c $< -o $@\n\t$(STRIP) -g $@\n\ntestdata/loader_nobtf-el.elf: testdata/loader.c $(HEADERS)\n\t$(CLANG) $(CFLAGS) -g0 -D__NOBTF__ -target bpfel -c $< -o $@\n\ntestdata/loader_nobtf-eb.elf: testdata/loader.c $(HEADERS)\n\t$(CLANG) $(CFLAGS) -g0 -D__NOBTF__ -target bpfeb -c $< -o $@\n\n%-el.elf: %.c $(HEADERS)\n\t$(CLANG) $(CFLAGS) -target bpfel -c $< -o $@\n\t$(STRIP) -g $@\n\n%-eb.elf: %.c $(HEADERS)\n\t$(CLANG) $(CFLAGS) -target bpfeb -c $< -o $@\n\t$(STRIP) -g $@\n\ntestdata/linked-el.elf: testdata/linked1-el.elf testdata/linked2-el.elf\n\tbpftool gen object $@ $^\n\n.PHONY: update-external-deps\nupdate-external-deps:\n\t./scripts/update-kernel-deps.sh\n\t./scripts/update-efw-deps.sh\n"
  },
  {
    "path": "README.md",
    "content": "# eBPF\n\n[![PkgGoDev](https://pkg.go.dev/badge/github.com/cilium/ebpf)](https://pkg.go.dev/github.com/cilium/ebpf)\n\n![HoneyGopher](docs/ebpf/ebpf-go.png)\n\nebpf-go is a pure Go library that provides utilities for loading, compiling, and\ndebugging eBPF programs. It has minimal external dependencies and is intended to\nbe used in long running processes.\n\nSee [ebpf.io](https://ebpf.io) for complementary projects from the wider eBPF\necosystem.\n\n## Getting Started\n\nPlease take a look at our [Getting Started] guide.\n\n[Contributions](https://ebpf-go.dev/contributing) are highly encouraged, as they highlight certain use cases of\neBPF and the library, and help shape the future of the project.\n\n## Getting Help\n\nThe community actively monitors our [GitHub Discussions](https://github.com/cilium/ebpf/discussions) page.\nPlease search for existing threads before starting a new one. Refrain from\nopening issues on the bug tracker if you're just starting out or if you're not\nsure if something is a bug in the library code.\n\nAlternatively, [join](https://ebpf.io/slack) the\n[#ebpf-go](https://cilium.slack.com/messages/ebpf-go) channel on Slack if you\nhave other questions regarding the project. Note that this channel is ephemeral\nand has its history erased past a certain point, which is less helpful for\nothers running into the same problem later.\n\n## Packages\n\nThis library includes the following packages:\n\n* [asm](https://pkg.go.dev/github.com/cilium/ebpf/asm) contains a basic\n  assembler, allowing you to write eBPF assembly instructions directly\n  within your Go code. (You don't need to use this if you prefer to write your eBPF program in C.)\n* [cmd/bpf2go](https://pkg.go.dev/github.com/cilium/ebpf/cmd/bpf2go) allows\n  compiling and embedding eBPF programs written in C within Go code. As well as\n  compiling the C code, it auto-generates Go code for loading and manipulating\n  the eBPF program and map objects.\n* [link](https://pkg.go.dev/github.com/cilium/ebpf/link) allows attaching eBPF\n  to various hooks\n* [perf](https://pkg.go.dev/github.com/cilium/ebpf/perf) allows reading from a\n  `PERF_EVENT_ARRAY`\n* [ringbuf](https://pkg.go.dev/github.com/cilium/ebpf/ringbuf) allows reading from a\n  `BPF_MAP_TYPE_RINGBUF` map\n* [features](https://pkg.go.dev/github.com/cilium/ebpf/features) implements the equivalent\n  of `bpftool feature probe` for discovering BPF-related kernel features using native Go.\n* [rlimit](https://pkg.go.dev/github.com/cilium/ebpf/rlimit) provides a convenient API to lift\n  the `RLIMIT_MEMLOCK` constraint on kernels before 5.11.\n* [btf](https://pkg.go.dev/github.com/cilium/ebpf/btf) allows reading the BPF Type Format.\n* [pin](https://pkg.go.dev/github.com/cilium/ebpf/pin) provides APIs for working with pinned objects on bpffs.\n\n## Requirements\n\n* A version of Go that is [supported by\n  upstream](https://golang.org/doc/devel/release.html#policy)\n* Linux (amd64, arm64): CI is run against kernel.org LTS releases. >= 4.4 should work but EOL'ed\n  versions are not supported.\n* Windows (amd64): CI is run against Windows Server 2022. Only the latest eBPF for Windows\n  release is supported.\n* Other architectures are best effort. 32bit arches are not supported.\n\n## License\n\nMIT\n\n### eBPF Gopher\n\nThe eBPF honeygopher is based on the Go gopher designed by Renee French.\n\n[Getting Started]: https://ebpf-go.dev/guides/getting-started/\n"
  },
  {
    "path": "asm/alu.go",
    "content": "package asm\n\n//go:generate go tool stringer -output alu_string.go -type=Source,Endianness,ALUOp\n\n// Source of ALU / ALU64 / Branch operations\n//\n//\tmsb              lsb\n//\t+------------+-+---+\n//\t|     op     |S|cls|\n//\t+------------+-+---+\ntype Source uint16\n\nconst sourceMask OpCode = 0x0008\n\n// Source bitmask\nconst (\n\t// InvalidSource is returned by getters when invoked\n\t// on non ALU / branch OpCodes.\n\tInvalidSource Source = 0xffff\n\t// ImmSource src is from constant\n\tImmSource Source = 0x0000\n\t// RegSource src is from register\n\tRegSource Source = 0x0008\n)\n\n// The Endianness of a byte swap instruction.\ntype Endianness uint8\n\nconst endianMask = sourceMask\n\n// Endian flags\nconst (\n\tInvalidEndian Endianness = 0xff\n\t// Convert to little endian\n\tLE Endianness = 0x00\n\t// Convert to big endian\n\tBE Endianness = 0x08\n)\n\n// ALUOp are ALU / ALU64 operations\n//\n//\tmsb              lsb\n//\t+-------+----+-+---+\n//\t|  EXT  | OP |s|cls|\n//\t+-------+----+-+---+\ntype ALUOp uint16\n\nconst aluMask OpCode = 0x3ff0\n\nconst (\n\t// InvalidALUOp is returned by getters when invoked\n\t// on non ALU OpCodes\n\tInvalidALUOp ALUOp = 0xffff\n\t// Add - addition\n\tAdd ALUOp = 0x0000\n\t// Sub - subtraction\n\tSub ALUOp = 0x0010\n\t// Mul - multiplication\n\tMul ALUOp = 0x0020\n\t// Div - division\n\tDiv ALUOp = 0x0030\n\t// SDiv - signed division\n\tSDiv ALUOp = Div + 0x0100\n\t// Or - bitwise or\n\tOr ALUOp = 0x0040\n\t// And - bitwise and\n\tAnd ALUOp = 0x0050\n\t// LSh - bitwise shift left\n\tLSh ALUOp = 0x0060\n\t// RSh - bitwise shift right\n\tRSh ALUOp = 0x0070\n\t// Neg - sign/unsign signing bit\n\tNeg ALUOp = 0x0080\n\t// Mod - modulo\n\tMod ALUOp = 0x0090\n\t// SMod - signed modulo\n\tSMod ALUOp = Mod + 0x0100\n\t// Xor - bitwise xor\n\tXor ALUOp = 0x00a0\n\t// Mov - move value from one place to another\n\tMov ALUOp = 0x00b0\n\t// MovSX8 - move lower 8 bits, sign extended upper bits of target\n\tMovSX8 ALUOp = Mov + 0x0100\n\t// MovSX16 - move lower 16 bits, sign extended upper bits of target\n\tMovSX16 ALUOp = Mov + 0x0200\n\t// MovSX32 - move lower 32 bits, sign extended upper bits of target\n\tMovSX32 ALUOp = Mov + 0x0300\n\t// ArSh - arithmetic shift\n\tArSh ALUOp = 0x00c0\n\t// Swap - endian conversions\n\tSwap ALUOp = 0x00d0\n)\n\n// HostTo converts from host to another endianness.\nfunc HostTo(endian Endianness, dst Register, size Size) Instruction {\n\tvar imm int64\n\tswitch size {\n\tcase Half:\n\t\timm = 16\n\tcase Word:\n\t\timm = 32\n\tcase DWord:\n\t\timm = 64\n\tdefault:\n\t\treturn Instruction{OpCode: InvalidOpCode}\n\t}\n\n\treturn Instruction{\n\t\tOpCode:   OpCode(ALUClass).SetALUOp(Swap).SetSource(Source(endian)),\n\t\tDst:      dst,\n\t\tConstant: imm,\n\t}\n}\n\n// BSwap unconditionally reverses the order of bytes in a register.\nfunc BSwap(dst Register, size Size) Instruction {\n\tvar imm int64\n\tswitch size {\n\tcase Half:\n\t\timm = 16\n\tcase Word:\n\t\timm = 32\n\tcase DWord:\n\t\timm = 64\n\tdefault:\n\t\treturn Instruction{OpCode: InvalidOpCode}\n\t}\n\n\treturn Instruction{\n\t\tOpCode:   OpCode(ALU64Class).SetALUOp(Swap),\n\t\tDst:      dst,\n\t\tConstant: imm,\n\t}\n}\n\n// Op returns the OpCode for an ALU operation with a given source.\nfunc (op ALUOp) Op(source Source) OpCode {\n\treturn OpCode(ALU64Class).SetALUOp(op).SetSource(source)\n}\n\n// Reg emits `dst (op) src`.\nfunc (op ALUOp) Reg(dst, src Register) Instruction {\n\treturn Instruction{\n\t\tOpCode: op.Op(RegSource),\n\t\tDst:    dst,\n\t\tSrc:    src,\n\t}\n}\n\n// Imm emits `dst (op) value`.\nfunc (op ALUOp) Imm(dst Register, value int32) Instruction {\n\treturn Instruction{\n\t\tOpCode:   op.Op(ImmSource),\n\t\tDst:      dst,\n\t\tConstant: int64(value),\n\t}\n}\n\n// Op32 returns the OpCode for a 32-bit ALU operation with a given source.\nfunc (op ALUOp) Op32(source Source) OpCode {\n\treturn OpCode(ALUClass).SetALUOp(op).SetSource(source)\n}\n\n// Reg32 emits `dst (op) src`, zeroing the upper 32 bit of dst.\nfunc (op ALUOp) Reg32(dst, src Register) Instruction {\n\treturn Instruction{\n\t\tOpCode: op.Op32(RegSource),\n\t\tDst:    dst,\n\t\tSrc:    src,\n\t}\n}\n\n// Imm32 emits `dst (op) value`, zeroing the upper 32 bit of dst.\nfunc (op ALUOp) Imm32(dst Register, value int32) Instruction {\n\treturn Instruction{\n\t\tOpCode:   op.Op32(ImmSource),\n\t\tDst:      dst,\n\t\tConstant: int64(value),\n\t}\n}\n"
  },
  {
    "path": "asm/alu_string.go",
    "content": "// Code generated by \"stringer -output alu_string.go -type=Source,Endianness,ALUOp\"; DO NOT EDIT.\n\npackage asm\n\nimport \"strconv\"\n\nfunc _() {\n\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n\t// Re-run the stringer command to generate them again.\n\tvar x [1]struct{}\n\t_ = x[InvalidSource-65535]\n\t_ = x[ImmSource-0]\n\t_ = x[RegSource-8]\n}\n\nconst (\n\t_Source_name_0 = \"ImmSource\"\n\t_Source_name_1 = \"RegSource\"\n\t_Source_name_2 = \"InvalidSource\"\n)\n\nfunc (i Source) String() string {\n\tswitch {\n\tcase i == 0:\n\t\treturn _Source_name_0\n\tcase i == 8:\n\t\treturn _Source_name_1\n\tcase i == 65535:\n\t\treturn _Source_name_2\n\tdefault:\n\t\treturn \"Source(\" + strconv.FormatInt(int64(i), 10) + \")\"\n\t}\n}\nfunc _() {\n\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n\t// Re-run the stringer command to generate them again.\n\tvar x [1]struct{}\n\t_ = x[InvalidEndian-255]\n\t_ = x[LE-0]\n\t_ = x[BE-8]\n}\n\nconst (\n\t_Endianness_name_0 = \"LE\"\n\t_Endianness_name_1 = \"BE\"\n\t_Endianness_name_2 = \"InvalidEndian\"\n)\n\nfunc (i Endianness) String() string {\n\tswitch {\n\tcase i == 0:\n\t\treturn _Endianness_name_0\n\tcase i == 8:\n\t\treturn _Endianness_name_1\n\tcase i == 255:\n\t\treturn _Endianness_name_2\n\tdefault:\n\t\treturn \"Endianness(\" + strconv.FormatInt(int64(i), 10) + \")\"\n\t}\n}\nfunc _() {\n\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n\t// Re-run the stringer command to generate them again.\n\tvar x [1]struct{}\n\t_ = x[InvalidALUOp-65535]\n\t_ = x[Add-0]\n\t_ = x[Sub-16]\n\t_ = x[Mul-32]\n\t_ = x[Div-48]\n\t_ = x[SDiv-304]\n\t_ = x[Or-64]\n\t_ = x[And-80]\n\t_ = x[LSh-96]\n\t_ = x[RSh-112]\n\t_ = x[Neg-128]\n\t_ = x[Mod-144]\n\t_ = x[SMod-400]\n\t_ = x[Xor-160]\n\t_ = x[Mov-176]\n\t_ = x[MovSX8-432]\n\t_ = x[MovSX16-688]\n\t_ = x[MovSX32-944]\n\t_ = x[ArSh-192]\n\t_ = x[Swap-208]\n}\n\nconst _ALUOp_name = \"AddSubMulDivOrAndLShRShNegModXorMovArShSwapSDivSModMovSX8MovSX16MovSX32InvalidALUOp\"\n\nvar _ALUOp_map = map[ALUOp]string{\n\t0:     _ALUOp_name[0:3],\n\t16:    _ALUOp_name[3:6],\n\t32:    _ALUOp_name[6:9],\n\t48:    _ALUOp_name[9:12],\n\t64:    _ALUOp_name[12:14],\n\t80:    _ALUOp_name[14:17],\n\t96:    _ALUOp_name[17:20],\n\t112:   _ALUOp_name[20:23],\n\t128:   _ALUOp_name[23:26],\n\t144:   _ALUOp_name[26:29],\n\t160:   _ALUOp_name[29:32],\n\t176:   _ALUOp_name[32:35],\n\t192:   _ALUOp_name[35:39],\n\t208:   _ALUOp_name[39:43],\n\t304:   _ALUOp_name[43:47],\n\t400:   _ALUOp_name[47:51],\n\t432:   _ALUOp_name[51:57],\n\t688:   _ALUOp_name[57:64],\n\t944:   _ALUOp_name[64:71],\n\t65535: _ALUOp_name[71:83],\n}\n\nfunc (i ALUOp) String() string {\n\tif str, ok := _ALUOp_map[i]; ok {\n\t\treturn str\n\t}\n\treturn \"ALUOp(\" + strconv.FormatInt(int64(i), 10) + \")\"\n}\n"
  },
  {
    "path": "asm/doc.go",
    "content": "// Package asm is an assembler for eBPF bytecode.\npackage asm\n"
  },
  {
    "path": "asm/dsl_test.go",
    "content": "package asm\n\nimport (\n\t\"testing\"\n)\n\nfunc TestDSL(t *testing.T) {\n\ttestcases := []struct {\n\t\tname string\n\t\thave Instruction\n\t\twant Instruction\n\t}{\n\t\t{\"Call\", FnMapLookupElem.Call(), Instruction{OpCode: 0x85, Constant: 1}},\n\t\t{\"Exit\", Return(), Instruction{OpCode: 0x95}},\n\t\t{\"LoadAbs\", LoadAbs(2, Byte), Instruction{OpCode: 0x30, Constant: 2}},\n\t\t{\"Store\", StoreMem(RFP, -4, R0, Word), Instruction{\n\t\t\tOpCode: 0x63,\n\t\t\tDst:    RFP,\n\t\t\tSrc:    R0,\n\t\t\tOffset: -4,\n\t\t}},\n\t\t{\"Add.Imm\", Add.Imm(R1, 22), Instruction{OpCode: 0x07, Dst: R1, Constant: 22}},\n\t\t{\"Add.Reg\", Add.Reg(R1, R2), Instruction{OpCode: 0x0f, Dst: R1, Src: R2}},\n\t\t{\"Add.Imm32\", Add.Imm32(R1, 22), Instruction{\n\t\t\tOpCode: 0x04, Dst: R1, Constant: 22,\n\t\t}},\n\t\t{\"JSGT.Imm\", JSGT.Imm(R1, 4, \"foo\"), Instruction{\n\t\t\tOpCode: 0x65, Dst: R1, Constant: 4, Offset: -1,\n\t\t}.WithReference(\"foo\")},\n\t\t{\"JSGT.Imm32\", JSGT.Imm32(R1, -2, \"foo\"), Instruction{\n\t\t\tOpCode: 0x66, Dst: R1, Constant: -2, Offset: -1,\n\t\t}.WithReference(\"foo\")},\n\t\t{\"JSLT.Reg\", JSLT.Reg(R1, R2, \"foo\"), Instruction{\n\t\t\tOpCode: 0xcd, Dst: R1, Src: R2, Offset: -1,\n\t\t}.WithReference(\"foo\")},\n\t\t{\"JSLT.Reg32\", JSLT.Reg32(R1, R3, \"foo\"), Instruction{\n\t\t\tOpCode: 0xce, Dst: R1, Src: R3, Offset: -1,\n\t\t}.WithReference(\"foo\")},\n\t}\n\n\tfor _, tc := range testcases {\n\t\tif !tc.have.equal(tc.want) {\n\t\t\tt.Errorf(\"%s: have %v, want %v\", tc.name, tc.have, tc.want)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "asm/func.go",
    "content": "package asm\n\nimport \"github.com/cilium/ebpf/internal/platform\"\n\n//go:generate go tool stringer -output func_string.go -type=BuiltinFunc\n\n// BuiltinFunc is a built-in eBPF function.\ntype BuiltinFunc uint32\n\n// BuiltinFuncForPlatform returns a platform specific function constant.\n//\n// Use this if the library doesn't provide a constant yet.\nfunc BuiltinFuncForPlatform(plat string, value uint32) (BuiltinFunc, error) {\n\treturn platform.EncodeConstant[BuiltinFunc](plat, value)\n}\n\n// Call emits a function call.\nfunc (fn BuiltinFunc) Call() Instruction {\n\treturn Instruction{\n\t\tOpCode:   OpCode(JumpClass).SetJumpOp(Call),\n\t\tConstant: int64(fn),\n\t}\n}\n"
  },
  {
    "path": "asm/func_lin.go",
    "content": "﻿// Code generated by internal/cmd/genfunctions.awk; DO NOT EDIT.\n\npackage asm\n\n// Code in this file is derived from Linux, available under the GPL-2.0 WITH Linux-syscall-note.\n\nimport \"github.com/cilium/ebpf/internal/platform\"\n\n// Built-in functions (Linux).\nconst (\n\tFnUnspec                     = BuiltinFunc(platform.LinuxTag | 0) //lint:ignore SA4016 consistency\n\tFnMapLookupElem              = BuiltinFunc(platform.LinuxTag | 1)\n\tFnMapUpdateElem              = BuiltinFunc(platform.LinuxTag | 2)\n\tFnMapDeleteElem              = BuiltinFunc(platform.LinuxTag | 3)\n\tFnProbeRead                  = BuiltinFunc(platform.LinuxTag | 4)\n\tFnKtimeGetNs                 = BuiltinFunc(platform.LinuxTag | 5)\n\tFnTracePrintk                = BuiltinFunc(platform.LinuxTag | 6)\n\tFnGetPrandomU32              = BuiltinFunc(platform.LinuxTag | 7)\n\tFnGetSmpProcessorId          = BuiltinFunc(platform.LinuxTag | 8)\n\tFnSkbStoreBytes              = BuiltinFunc(platform.LinuxTag | 9)\n\tFnL3CsumReplace              = BuiltinFunc(platform.LinuxTag | 10)\n\tFnL4CsumReplace              = BuiltinFunc(platform.LinuxTag | 11)\n\tFnTailCall                   = BuiltinFunc(platform.LinuxTag | 12)\n\tFnCloneRedirect              = BuiltinFunc(platform.LinuxTag | 13)\n\tFnGetCurrentPidTgid          = BuiltinFunc(platform.LinuxTag | 14)\n\tFnGetCurrentUidGid           = BuiltinFunc(platform.LinuxTag | 15)\n\tFnGetCurrentComm             = BuiltinFunc(platform.LinuxTag | 16)\n\tFnGetCgroupClassid           = BuiltinFunc(platform.LinuxTag | 17)\n\tFnSkbVlanPush                = BuiltinFunc(platform.LinuxTag | 18)\n\tFnSkbVlanPop                 = BuiltinFunc(platform.LinuxTag | 19)\n\tFnSkbGetTunnelKey            = BuiltinFunc(platform.LinuxTag | 20)\n\tFnSkbSetTunnelKey            = BuiltinFunc(platform.LinuxTag | 21)\n\tFnPerfEventRead              = BuiltinFunc(platform.LinuxTag | 22)\n\tFnRedirect                   = BuiltinFunc(platform.LinuxTag | 23)\n\tFnGetRouteRealm              = BuiltinFunc(platform.LinuxTag | 24)\n\tFnPerfEventOutput            = BuiltinFunc(platform.LinuxTag | 25)\n\tFnSkbLoadBytes               = BuiltinFunc(platform.LinuxTag | 26)\n\tFnGetStackid                 = BuiltinFunc(platform.LinuxTag | 27)\n\tFnCsumDiff                   = BuiltinFunc(platform.LinuxTag | 28)\n\tFnSkbGetTunnelOpt            = BuiltinFunc(platform.LinuxTag | 29)\n\tFnSkbSetTunnelOpt            = BuiltinFunc(platform.LinuxTag | 30)\n\tFnSkbChangeProto             = BuiltinFunc(platform.LinuxTag | 31)\n\tFnSkbChangeType              = BuiltinFunc(platform.LinuxTag | 32)\n\tFnSkbUnderCgroup             = BuiltinFunc(platform.LinuxTag | 33)\n\tFnGetHashRecalc              = BuiltinFunc(platform.LinuxTag | 34)\n\tFnGetCurrentTask             = BuiltinFunc(platform.LinuxTag | 35)\n\tFnProbeWriteUser             = BuiltinFunc(platform.LinuxTag | 36)\n\tFnCurrentTaskUnderCgroup     = BuiltinFunc(platform.LinuxTag | 37)\n\tFnSkbChangeTail              = BuiltinFunc(platform.LinuxTag | 38)\n\tFnSkbPullData                = BuiltinFunc(platform.LinuxTag | 39)\n\tFnCsumUpdate                 = BuiltinFunc(platform.LinuxTag | 40)\n\tFnSetHashInvalid             = BuiltinFunc(platform.LinuxTag | 41)\n\tFnGetNumaNodeId              = BuiltinFunc(platform.LinuxTag | 42)\n\tFnSkbChangeHead              = BuiltinFunc(platform.LinuxTag | 43)\n\tFnXdpAdjustHead              = BuiltinFunc(platform.LinuxTag | 44)\n\tFnProbeReadStr               = BuiltinFunc(platform.LinuxTag | 45)\n\tFnGetSocketCookie            = BuiltinFunc(platform.LinuxTag | 46)\n\tFnGetSocketUid               = BuiltinFunc(platform.LinuxTag | 47)\n\tFnSetHash                    = BuiltinFunc(platform.LinuxTag | 48)\n\tFnSetsockopt                 = BuiltinFunc(platform.LinuxTag | 49)\n\tFnSkbAdjustRoom              = BuiltinFunc(platform.LinuxTag | 50)\n\tFnRedirectMap                = BuiltinFunc(platform.LinuxTag | 51)\n\tFnSkRedirectMap              = BuiltinFunc(platform.LinuxTag | 52)\n\tFnSockMapUpdate              = BuiltinFunc(platform.LinuxTag | 53)\n\tFnXdpAdjustMeta              = BuiltinFunc(platform.LinuxTag | 54)\n\tFnPerfEventReadValue         = BuiltinFunc(platform.LinuxTag | 55)\n\tFnPerfProgReadValue          = BuiltinFunc(platform.LinuxTag | 56)\n\tFnGetsockopt                 = BuiltinFunc(platform.LinuxTag | 57)\n\tFnOverrideReturn             = BuiltinFunc(platform.LinuxTag | 58)\n\tFnSockOpsCbFlagsSet          = BuiltinFunc(platform.LinuxTag | 59)\n\tFnMsgRedirectMap             = BuiltinFunc(platform.LinuxTag | 60)\n\tFnMsgApplyBytes              = BuiltinFunc(platform.LinuxTag | 61)\n\tFnMsgCorkBytes               = BuiltinFunc(platform.LinuxTag | 62)\n\tFnMsgPullData                = BuiltinFunc(platform.LinuxTag | 63)\n\tFnBind                       = BuiltinFunc(platform.LinuxTag | 64)\n\tFnXdpAdjustTail              = BuiltinFunc(platform.LinuxTag | 65)\n\tFnSkbGetXfrmState            = BuiltinFunc(platform.LinuxTag | 66)\n\tFnGetStack                   = BuiltinFunc(platform.LinuxTag | 67)\n\tFnSkbLoadBytesRelative       = BuiltinFunc(platform.LinuxTag | 68)\n\tFnFibLookup                  = BuiltinFunc(platform.LinuxTag | 69)\n\tFnSockHashUpdate             = BuiltinFunc(platform.LinuxTag | 70)\n\tFnMsgRedirectHash            = BuiltinFunc(platform.LinuxTag | 71)\n\tFnSkRedirectHash             = BuiltinFunc(platform.LinuxTag | 72)\n\tFnLwtPushEncap               = BuiltinFunc(platform.LinuxTag | 73)\n\tFnLwtSeg6StoreBytes          = BuiltinFunc(platform.LinuxTag | 74)\n\tFnLwtSeg6AdjustSrh           = BuiltinFunc(platform.LinuxTag | 75)\n\tFnLwtSeg6Action              = BuiltinFunc(platform.LinuxTag | 76)\n\tFnRcRepeat                   = BuiltinFunc(platform.LinuxTag | 77)\n\tFnRcKeydown                  = BuiltinFunc(platform.LinuxTag | 78)\n\tFnSkbCgroupId                = BuiltinFunc(platform.LinuxTag | 79)\n\tFnGetCurrentCgroupId         = BuiltinFunc(platform.LinuxTag | 80)\n\tFnGetLocalStorage            = BuiltinFunc(platform.LinuxTag | 81)\n\tFnSkSelectReuseport          = BuiltinFunc(platform.LinuxTag | 82)\n\tFnSkbAncestorCgroupId        = BuiltinFunc(platform.LinuxTag | 83)\n\tFnSkLookupTcp                = BuiltinFunc(platform.LinuxTag | 84)\n\tFnSkLookupUdp                = BuiltinFunc(platform.LinuxTag | 85)\n\tFnSkRelease                  = BuiltinFunc(platform.LinuxTag | 86)\n\tFnMapPushElem                = BuiltinFunc(platform.LinuxTag | 87)\n\tFnMapPopElem                 = BuiltinFunc(platform.LinuxTag | 88)\n\tFnMapPeekElem                = BuiltinFunc(platform.LinuxTag | 89)\n\tFnMsgPushData                = BuiltinFunc(platform.LinuxTag | 90)\n\tFnMsgPopData                 = BuiltinFunc(platform.LinuxTag | 91)\n\tFnRcPointerRel               = BuiltinFunc(platform.LinuxTag | 92)\n\tFnSpinLock                   = BuiltinFunc(platform.LinuxTag | 93)\n\tFnSpinUnlock                 = BuiltinFunc(platform.LinuxTag | 94)\n\tFnSkFullsock                 = BuiltinFunc(platform.LinuxTag | 95)\n\tFnTcpSock                    = BuiltinFunc(platform.LinuxTag | 96)\n\tFnSkbEcnSetCe                = BuiltinFunc(platform.LinuxTag | 97)\n\tFnGetListenerSock            = BuiltinFunc(platform.LinuxTag | 98)\n\tFnSkcLookupTcp               = BuiltinFunc(platform.LinuxTag | 99)\n\tFnTcpCheckSyncookie          = BuiltinFunc(platform.LinuxTag | 100)\n\tFnSysctlGetName              = BuiltinFunc(platform.LinuxTag | 101)\n\tFnSysctlGetCurrentValue      = BuiltinFunc(platform.LinuxTag | 102)\n\tFnSysctlGetNewValue          = BuiltinFunc(platform.LinuxTag | 103)\n\tFnSysctlSetNewValue          = BuiltinFunc(platform.LinuxTag | 104)\n\tFnStrtol                     = BuiltinFunc(platform.LinuxTag | 105)\n\tFnStrtoul                    = BuiltinFunc(platform.LinuxTag | 106)\n\tFnSkStorageGet               = BuiltinFunc(platform.LinuxTag | 107)\n\tFnSkStorageDelete            = BuiltinFunc(platform.LinuxTag | 108)\n\tFnSendSignal                 = BuiltinFunc(platform.LinuxTag | 109)\n\tFnTcpGenSyncookie            = BuiltinFunc(platform.LinuxTag | 110)\n\tFnSkbOutput                  = BuiltinFunc(platform.LinuxTag | 111)\n\tFnProbeReadUser              = BuiltinFunc(platform.LinuxTag | 112)\n\tFnProbeReadKernel            = BuiltinFunc(platform.LinuxTag | 113)\n\tFnProbeReadUserStr           = BuiltinFunc(platform.LinuxTag | 114)\n\tFnProbeReadKernelStr         = BuiltinFunc(platform.LinuxTag | 115)\n\tFnTcpSendAck                 = BuiltinFunc(platform.LinuxTag | 116)\n\tFnSendSignalThread           = BuiltinFunc(platform.LinuxTag | 117)\n\tFnJiffies64                  = BuiltinFunc(platform.LinuxTag | 118)\n\tFnReadBranchRecords          = BuiltinFunc(platform.LinuxTag | 119)\n\tFnGetNsCurrentPidTgid        = BuiltinFunc(platform.LinuxTag | 120)\n\tFnXdpOutput                  = BuiltinFunc(platform.LinuxTag | 121)\n\tFnGetNetnsCookie             = BuiltinFunc(platform.LinuxTag | 122)\n\tFnGetCurrentAncestorCgroupId = BuiltinFunc(platform.LinuxTag | 123)\n\tFnSkAssign                   = BuiltinFunc(platform.LinuxTag | 124)\n\tFnKtimeGetBootNs             = BuiltinFunc(platform.LinuxTag | 125)\n\tFnSeqPrintf                  = BuiltinFunc(platform.LinuxTag | 126)\n\tFnSeqWrite                   = BuiltinFunc(platform.LinuxTag | 127)\n\tFnSkCgroupId                 = BuiltinFunc(platform.LinuxTag | 128)\n\tFnSkAncestorCgroupId         = BuiltinFunc(platform.LinuxTag | 129)\n\tFnRingbufOutput              = BuiltinFunc(platform.LinuxTag | 130)\n\tFnRingbufReserve             = BuiltinFunc(platform.LinuxTag | 131)\n\tFnRingbufSubmit              = BuiltinFunc(platform.LinuxTag | 132)\n\tFnRingbufDiscard             = BuiltinFunc(platform.LinuxTag | 133)\n\tFnRingbufQuery               = BuiltinFunc(platform.LinuxTag | 134)\n\tFnCsumLevel                  = BuiltinFunc(platform.LinuxTag | 135)\n\tFnSkcToTcp6Sock              = BuiltinFunc(platform.LinuxTag | 136)\n\tFnSkcToTcpSock               = BuiltinFunc(platform.LinuxTag | 137)\n\tFnSkcToTcpTimewaitSock       = BuiltinFunc(platform.LinuxTag | 138)\n\tFnSkcToTcpRequestSock        = BuiltinFunc(platform.LinuxTag | 139)\n\tFnSkcToUdp6Sock              = BuiltinFunc(platform.LinuxTag | 140)\n\tFnGetTaskStack               = BuiltinFunc(platform.LinuxTag | 141)\n\tFnLoadHdrOpt                 = BuiltinFunc(platform.LinuxTag | 142)\n\tFnStoreHdrOpt                = BuiltinFunc(platform.LinuxTag | 143)\n\tFnReserveHdrOpt              = BuiltinFunc(platform.LinuxTag | 144)\n\tFnInodeStorageGet            = BuiltinFunc(platform.LinuxTag | 145)\n\tFnInodeStorageDelete         = BuiltinFunc(platform.LinuxTag | 146)\n\tFnDPath                      = BuiltinFunc(platform.LinuxTag | 147)\n\tFnCopyFromUser               = BuiltinFunc(platform.LinuxTag | 148)\n\tFnSnprintfBtf                = BuiltinFunc(platform.LinuxTag | 149)\n\tFnSeqPrintfBtf               = BuiltinFunc(platform.LinuxTag | 150)\n\tFnSkbCgroupClassid           = BuiltinFunc(platform.LinuxTag | 151)\n\tFnRedirectNeigh              = BuiltinFunc(platform.LinuxTag | 152)\n\tFnPerCpuPtr                  = BuiltinFunc(platform.LinuxTag | 153)\n\tFnThisCpuPtr                 = BuiltinFunc(platform.LinuxTag | 154)\n\tFnRedirectPeer               = BuiltinFunc(platform.LinuxTag | 155)\n\tFnTaskStorageGet             = BuiltinFunc(platform.LinuxTag | 156)\n\tFnTaskStorageDelete          = BuiltinFunc(platform.LinuxTag | 157)\n\tFnGetCurrentTaskBtf          = BuiltinFunc(platform.LinuxTag | 158)\n\tFnBprmOptsSet                = BuiltinFunc(platform.LinuxTag | 159)\n\tFnKtimeGetCoarseNs           = BuiltinFunc(platform.LinuxTag | 160)\n\tFnImaInodeHash               = BuiltinFunc(platform.LinuxTag | 161)\n\tFnSockFromFile               = BuiltinFunc(platform.LinuxTag | 162)\n\tFnCheckMtu                   = BuiltinFunc(platform.LinuxTag | 163)\n\tFnForEachMapElem             = BuiltinFunc(platform.LinuxTag | 164)\n\tFnSnprintf                   = BuiltinFunc(platform.LinuxTag | 165)\n\tFnSysBpf                     = BuiltinFunc(platform.LinuxTag | 166)\n\tFnBtfFindByNameKind          = BuiltinFunc(platform.LinuxTag | 167)\n\tFnSysClose                   = BuiltinFunc(platform.LinuxTag | 168)\n\tFnTimerInit                  = BuiltinFunc(platform.LinuxTag | 169)\n\tFnTimerSetCallback           = BuiltinFunc(platform.LinuxTag | 170)\n\tFnTimerStart                 = BuiltinFunc(platform.LinuxTag | 171)\n\tFnTimerCancel                = BuiltinFunc(platform.LinuxTag | 172)\n\tFnGetFuncIp                  = BuiltinFunc(platform.LinuxTag | 173)\n\tFnGetAttachCookie            = BuiltinFunc(platform.LinuxTag | 174)\n\tFnTaskPtRegs                 = BuiltinFunc(platform.LinuxTag | 175)\n\tFnGetBranchSnapshot          = BuiltinFunc(platform.LinuxTag | 176)\n\tFnTraceVprintk               = BuiltinFunc(platform.LinuxTag | 177)\n\tFnSkcToUnixSock              = BuiltinFunc(platform.LinuxTag | 178)\n\tFnKallsymsLookupName         = BuiltinFunc(platform.LinuxTag | 179)\n\tFnFindVma                    = BuiltinFunc(platform.LinuxTag | 180)\n\tFnLoop                       = BuiltinFunc(platform.LinuxTag | 181)\n\tFnStrncmp                    = BuiltinFunc(platform.LinuxTag | 182)\n\tFnGetFuncArg                 = BuiltinFunc(platform.LinuxTag | 183)\n\tFnGetFuncRet                 = BuiltinFunc(platform.LinuxTag | 184)\n\tFnGetFuncArgCnt              = BuiltinFunc(platform.LinuxTag | 185)\n\tFnGetRetval                  = BuiltinFunc(platform.LinuxTag | 186)\n\tFnSetRetval                  = BuiltinFunc(platform.LinuxTag | 187)\n\tFnXdpGetBuffLen              = BuiltinFunc(platform.LinuxTag | 188)\n\tFnXdpLoadBytes               = BuiltinFunc(platform.LinuxTag | 189)\n\tFnXdpStoreBytes              = BuiltinFunc(platform.LinuxTag | 190)\n\tFnCopyFromUserTask           = BuiltinFunc(platform.LinuxTag | 191)\n\tFnSkbSetTstamp               = BuiltinFunc(platform.LinuxTag | 192)\n\tFnImaFileHash                = BuiltinFunc(platform.LinuxTag | 193)\n\tFnKptrXchg                   = BuiltinFunc(platform.LinuxTag | 194)\n\tFnMapLookupPercpuElem        = BuiltinFunc(platform.LinuxTag | 195)\n\tFnSkcToMptcpSock             = BuiltinFunc(platform.LinuxTag | 196)\n\tFnDynptrFromMem              = BuiltinFunc(platform.LinuxTag | 197)\n\tFnRingbufReserveDynptr       = BuiltinFunc(platform.LinuxTag | 198)\n\tFnRingbufSubmitDynptr        = BuiltinFunc(platform.LinuxTag | 199)\n\tFnRingbufDiscardDynptr       = BuiltinFunc(platform.LinuxTag | 200)\n\tFnDynptrRead                 = BuiltinFunc(platform.LinuxTag | 201)\n\tFnDynptrWrite                = BuiltinFunc(platform.LinuxTag | 202)\n\tFnDynptrData                 = BuiltinFunc(platform.LinuxTag | 203)\n\tFnTcpRawGenSyncookieIpv4     = BuiltinFunc(platform.LinuxTag | 204)\n\tFnTcpRawGenSyncookieIpv6     = BuiltinFunc(platform.LinuxTag | 205)\n\tFnTcpRawCheckSyncookieIpv4   = BuiltinFunc(platform.LinuxTag | 206)\n\tFnTcpRawCheckSyncookieIpv6   = BuiltinFunc(platform.LinuxTag | 207)\n\tFnKtimeGetTaiNs              = BuiltinFunc(platform.LinuxTag | 208)\n\tFnUserRingbufDrain           = BuiltinFunc(platform.LinuxTag | 209)\n\tFnCgrpStorageGet             = BuiltinFunc(platform.LinuxTag | 210)\n\tFnCgrpStorageDelete          = BuiltinFunc(platform.LinuxTag | 211)\n)\n"
  },
  {
    "path": "asm/func_string.go",
    "content": "// Code generated by \"stringer -output func_string.go -type=BuiltinFunc\"; DO NOT EDIT.\n\npackage asm\n\nimport \"strconv\"\n\nfunc _() {\n\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n\t// Re-run the stringer command to generate them again.\n\tvar x [1]struct{}\n\t_ = x[FnUnspec-0]\n\t_ = x[FnMapLookupElem-1]\n\t_ = x[FnMapUpdateElem-2]\n\t_ = x[FnMapDeleteElem-3]\n\t_ = x[FnProbeRead-4]\n\t_ = x[FnKtimeGetNs-5]\n\t_ = x[FnTracePrintk-6]\n\t_ = x[FnGetPrandomU32-7]\n\t_ = x[FnGetSmpProcessorId-8]\n\t_ = x[FnSkbStoreBytes-9]\n\t_ = x[FnL3CsumReplace-10]\n\t_ = x[FnL4CsumReplace-11]\n\t_ = x[FnTailCall-12]\n\t_ = x[FnCloneRedirect-13]\n\t_ = x[FnGetCurrentPidTgid-14]\n\t_ = x[FnGetCurrentUidGid-15]\n\t_ = x[FnGetCurrentComm-16]\n\t_ = x[FnGetCgroupClassid-17]\n\t_ = x[FnSkbVlanPush-18]\n\t_ = x[FnSkbVlanPop-19]\n\t_ = x[FnSkbGetTunnelKey-20]\n\t_ = x[FnSkbSetTunnelKey-21]\n\t_ = x[FnPerfEventRead-22]\n\t_ = x[FnRedirect-23]\n\t_ = x[FnGetRouteRealm-24]\n\t_ = x[FnPerfEventOutput-25]\n\t_ = x[FnSkbLoadBytes-26]\n\t_ = x[FnGetStackid-27]\n\t_ = x[FnCsumDiff-28]\n\t_ = x[FnSkbGetTunnelOpt-29]\n\t_ = x[FnSkbSetTunnelOpt-30]\n\t_ = x[FnSkbChangeProto-31]\n\t_ = x[FnSkbChangeType-32]\n\t_ = x[FnSkbUnderCgroup-33]\n\t_ = x[FnGetHashRecalc-34]\n\t_ = x[FnGetCurrentTask-35]\n\t_ = x[FnProbeWriteUser-36]\n\t_ = x[FnCurrentTaskUnderCgroup-37]\n\t_ = x[FnSkbChangeTail-38]\n\t_ = x[FnSkbPullData-39]\n\t_ = x[FnCsumUpdate-40]\n\t_ = x[FnSetHashInvalid-41]\n\t_ = x[FnGetNumaNodeId-42]\n\t_ = x[FnSkbChangeHead-43]\n\t_ = x[FnXdpAdjustHead-44]\n\t_ = x[FnProbeReadStr-45]\n\t_ = x[FnGetSocketCookie-46]\n\t_ = x[FnGetSocketUid-47]\n\t_ = x[FnSetHash-48]\n\t_ = x[FnSetsockopt-49]\n\t_ = x[FnSkbAdjustRoom-50]\n\t_ = x[FnRedirectMap-51]\n\t_ = x[FnSkRedirectMap-52]\n\t_ = x[FnSockMapUpdate-53]\n\t_ = x[FnXdpAdjustMeta-54]\n\t_ = x[FnPerfEventReadValue-55]\n\t_ = x[FnPerfProgReadValue-56]\n\t_ = x[FnGetsockopt-57]\n\t_ = x[FnOverrideReturn-58]\n\t_ = x[FnSockOpsCbFlagsSet-59]\n\t_ = x[FnMsgRedirectMap-60]\n\t_ = x[FnMsgApplyBytes-61]\n\t_ = x[FnMsgCorkBytes-62]\n\t_ = x[FnMsgPullData-63]\n\t_ = x[FnBind-64]\n\t_ = x[FnXdpAdjustTail-65]\n\t_ = x[FnSkbGetXfrmState-66]\n\t_ = x[FnGetStack-67]\n\t_ = x[FnSkbLoadBytesRelative-68]\n\t_ = x[FnFibLookup-69]\n\t_ = x[FnSockHashUpdate-70]\n\t_ = x[FnMsgRedirectHash-71]\n\t_ = x[FnSkRedirectHash-72]\n\t_ = x[FnLwtPushEncap-73]\n\t_ = x[FnLwtSeg6StoreBytes-74]\n\t_ = x[FnLwtSeg6AdjustSrh-75]\n\t_ = x[FnLwtSeg6Action-76]\n\t_ = x[FnRcRepeat-77]\n\t_ = x[FnRcKeydown-78]\n\t_ = x[FnSkbCgroupId-79]\n\t_ = x[FnGetCurrentCgroupId-80]\n\t_ = x[FnGetLocalStorage-81]\n\t_ = x[FnSkSelectReuseport-82]\n\t_ = x[FnSkbAncestorCgroupId-83]\n\t_ = x[FnSkLookupTcp-84]\n\t_ = x[FnSkLookupUdp-85]\n\t_ = x[FnSkRelease-86]\n\t_ = x[FnMapPushElem-87]\n\t_ = x[FnMapPopElem-88]\n\t_ = x[FnMapPeekElem-89]\n\t_ = x[FnMsgPushData-90]\n\t_ = x[FnMsgPopData-91]\n\t_ = x[FnRcPointerRel-92]\n\t_ = x[FnSpinLock-93]\n\t_ = x[FnSpinUnlock-94]\n\t_ = x[FnSkFullsock-95]\n\t_ = x[FnTcpSock-96]\n\t_ = x[FnSkbEcnSetCe-97]\n\t_ = x[FnGetListenerSock-98]\n\t_ = x[FnSkcLookupTcp-99]\n\t_ = x[FnTcpCheckSyncookie-100]\n\t_ = x[FnSysctlGetName-101]\n\t_ = x[FnSysctlGetCurrentValue-102]\n\t_ = x[FnSysctlGetNewValue-103]\n\t_ = x[FnSysctlSetNewValue-104]\n\t_ = x[FnStrtol-105]\n\t_ = x[FnStrtoul-106]\n\t_ = x[FnSkStorageGet-107]\n\t_ = x[FnSkStorageDelete-108]\n\t_ = x[FnSendSignal-109]\n\t_ = x[FnTcpGenSyncookie-110]\n\t_ = x[FnSkbOutput-111]\n\t_ = x[FnProbeReadUser-112]\n\t_ = x[FnProbeReadKernel-113]\n\t_ = x[FnProbeReadUserStr-114]\n\t_ = x[FnProbeReadKernelStr-115]\n\t_ = x[FnTcpSendAck-116]\n\t_ = x[FnSendSignalThread-117]\n\t_ = x[FnJiffies64-118]\n\t_ = x[FnReadBranchRecords-119]\n\t_ = x[FnGetNsCurrentPidTgid-120]\n\t_ = x[FnXdpOutput-121]\n\t_ = x[FnGetNetnsCookie-122]\n\t_ = x[FnGetCurrentAncestorCgroupId-123]\n\t_ = x[FnSkAssign-124]\n\t_ = x[FnKtimeGetBootNs-125]\n\t_ = x[FnSeqPrintf-126]\n\t_ = x[FnSeqWrite-127]\n\t_ = x[FnSkCgroupId-128]\n\t_ = x[FnSkAncestorCgroupId-129]\n\t_ = x[FnRingbufOutput-130]\n\t_ = x[FnRingbufReserve-131]\n\t_ = x[FnRingbufSubmit-132]\n\t_ = x[FnRingbufDiscard-133]\n\t_ = x[FnRingbufQuery-134]\n\t_ = x[FnCsumLevel-135]\n\t_ = x[FnSkcToTcp6Sock-136]\n\t_ = x[FnSkcToTcpSock-137]\n\t_ = x[FnSkcToTcpTimewaitSock-138]\n\t_ = x[FnSkcToTcpRequestSock-139]\n\t_ = x[FnSkcToUdp6Sock-140]\n\t_ = x[FnGetTaskStack-141]\n\t_ = x[FnLoadHdrOpt-142]\n\t_ = x[FnStoreHdrOpt-143]\n\t_ = x[FnReserveHdrOpt-144]\n\t_ = x[FnInodeStorageGet-145]\n\t_ = x[FnInodeStorageDelete-146]\n\t_ = x[FnDPath-147]\n\t_ = x[FnCopyFromUser-148]\n\t_ = x[FnSnprintfBtf-149]\n\t_ = x[FnSeqPrintfBtf-150]\n\t_ = x[FnSkbCgroupClassid-151]\n\t_ = x[FnRedirectNeigh-152]\n\t_ = x[FnPerCpuPtr-153]\n\t_ = x[FnThisCpuPtr-154]\n\t_ = x[FnRedirectPeer-155]\n\t_ = x[FnTaskStorageGet-156]\n\t_ = x[FnTaskStorageDelete-157]\n\t_ = x[FnGetCurrentTaskBtf-158]\n\t_ = x[FnBprmOptsSet-159]\n\t_ = x[FnKtimeGetCoarseNs-160]\n\t_ = x[FnImaInodeHash-161]\n\t_ = x[FnSockFromFile-162]\n\t_ = x[FnCheckMtu-163]\n\t_ = x[FnForEachMapElem-164]\n\t_ = x[FnSnprintf-165]\n\t_ = x[FnSysBpf-166]\n\t_ = x[FnBtfFindByNameKind-167]\n\t_ = x[FnSysClose-168]\n\t_ = x[FnTimerInit-169]\n\t_ = x[FnTimerSetCallback-170]\n\t_ = x[FnTimerStart-171]\n\t_ = x[FnTimerCancel-172]\n\t_ = x[FnGetFuncIp-173]\n\t_ = x[FnGetAttachCookie-174]\n\t_ = x[FnTaskPtRegs-175]\n\t_ = x[FnGetBranchSnapshot-176]\n\t_ = x[FnTraceVprintk-177]\n\t_ = x[FnSkcToUnixSock-178]\n\t_ = x[FnKallsymsLookupName-179]\n\t_ = x[FnFindVma-180]\n\t_ = x[FnLoop-181]\n\t_ = x[FnStrncmp-182]\n\t_ = x[FnGetFuncArg-183]\n\t_ = x[FnGetFuncRet-184]\n\t_ = x[FnGetFuncArgCnt-185]\n\t_ = x[FnGetRetval-186]\n\t_ = x[FnSetRetval-187]\n\t_ = x[FnXdpGetBuffLen-188]\n\t_ = x[FnXdpLoadBytes-189]\n\t_ = x[FnXdpStoreBytes-190]\n\t_ = x[FnCopyFromUserTask-191]\n\t_ = x[FnSkbSetTstamp-192]\n\t_ = x[FnImaFileHash-193]\n\t_ = x[FnKptrXchg-194]\n\t_ = x[FnMapLookupPercpuElem-195]\n\t_ = x[FnSkcToMptcpSock-196]\n\t_ = x[FnDynptrFromMem-197]\n\t_ = x[FnRingbufReserveDynptr-198]\n\t_ = x[FnRingbufSubmitDynptr-199]\n\t_ = x[FnRingbufDiscardDynptr-200]\n\t_ = x[FnDynptrRead-201]\n\t_ = x[FnDynptrWrite-202]\n\t_ = x[FnDynptrData-203]\n\t_ = x[FnTcpRawGenSyncookieIpv4-204]\n\t_ = x[FnTcpRawGenSyncookieIpv6-205]\n\t_ = x[FnTcpRawCheckSyncookieIpv4-206]\n\t_ = x[FnTcpRawCheckSyncookieIpv6-207]\n\t_ = x[FnKtimeGetTaiNs-208]\n\t_ = x[FnUserRingbufDrain-209]\n\t_ = x[FnCgrpStorageGet-210]\n\t_ = x[FnCgrpStorageDelete-211]\n\t_ = x[WindowsFnMapLookupElem-268435457]\n\t_ = x[WindowsFnMapUpdateElem-268435458]\n\t_ = x[WindowsFnMapDeleteElem-268435459]\n\t_ = x[WindowsFnMapLookupAndDeleteElem-268435460]\n\t_ = x[WindowsFnTailCall-268435461]\n\t_ = x[WindowsFnGetPrandomU32-268435462]\n\t_ = x[WindowsFnKtimeGetBootNs-268435463]\n\t_ = x[WindowsFnGetSmpProcessorId-268435464]\n\t_ = x[WindowsFnKtimeGetNs-268435465]\n\t_ = x[WindowsFnCsumDiff-268435466]\n\t_ = x[WindowsFnRingbufOutput-268435467]\n\t_ = x[WindowsFnTracePrintk2-268435468]\n\t_ = x[WindowsFnTracePrintk3-268435469]\n\t_ = x[WindowsFnTracePrintk4-268435470]\n\t_ = x[WindowsFnTracePrintk5-268435471]\n\t_ = x[WindowsFnMapPushElem-268435472]\n\t_ = x[WindowsFnMapPopElem-268435473]\n\t_ = x[WindowsFnMapPeekElem-268435474]\n\t_ = x[WindowsFnGetCurrentPidTgid-268435475]\n\t_ = x[WindowsFnGetCurrentLogonId-268435476]\n\t_ = x[WindowsFnIsCurrentAdmin-268435477]\n\t_ = x[WindowsFnMemcpyS-268435478]\n\t_ = x[WindowsFnMemcmpS-268435479]\n\t_ = x[WindowsFnMemset-268435480]\n\t_ = x[WindowsFnMemmoveS-268435481]\n\t_ = x[WindowsFnGetSocketCookie-268435482]\n\t_ = x[WindowsFnStrncpyS-268435483]\n\t_ = x[WindowsFnStrncatS-268435484]\n\t_ = x[WindowsFnStrnlenS-268435485]\n\t_ = x[WindowsFnKtimeGetBootMs-268435486]\n\t_ = x[WindowsFnKtimeGetMs-268435487]\n\t_ = x[WindowsFnPerfEventOutput-268435488]\n\t_ = x[WindowsFnGetCurrentProcessStartKey-268435489]\n\t_ = x[WindowsFnGetCurrentThreadCreateTime-268435490]\n}\n\nconst (\n\t_BuiltinFunc_name_0 = \"FnUnspecFnMapLookupElemFnMapUpdateElemFnMapDeleteElemFnProbeReadFnKtimeGetNsFnTracePrintkFnGetPrandomU32FnGetSmpProcessorIdFnSkbStoreBytesFnL3CsumReplaceFnL4CsumReplaceFnTailCallFnCloneRedirectFnGetCurrentPidTgidFnGetCurrentUidGidFnGetCurrentCommFnGetCgroupClassidFnSkbVlanPushFnSkbVlanPopFnSkbGetTunnelKeyFnSkbSetTunnelKeyFnPerfEventReadFnRedirectFnGetRouteRealmFnPerfEventOutputFnSkbLoadBytesFnGetStackidFnCsumDiffFnSkbGetTunnelOptFnSkbSetTunnelOptFnSkbChangeProtoFnSkbChangeTypeFnSkbUnderCgroupFnGetHashRecalcFnGetCurrentTaskFnProbeWriteUserFnCurrentTaskUnderCgroupFnSkbChangeTailFnSkbPullDataFnCsumUpdateFnSetHashInvalidFnGetNumaNodeIdFnSkbChangeHeadFnXdpAdjustHeadFnProbeReadStrFnGetSocketCookieFnGetSocketUidFnSetHashFnSetsockoptFnSkbAdjustRoomFnRedirectMapFnSkRedirectMapFnSockMapUpdateFnXdpAdjustMetaFnPerfEventReadValueFnPerfProgReadValueFnGetsockoptFnOverrideReturnFnSockOpsCbFlagsSetFnMsgRedirectMapFnMsgApplyBytesFnMsgCorkBytesFnMsgPullDataFnBindFnXdpAdjustTailFnSkbGetXfrmStateFnGetStackFnSkbLoadBytesRelativeFnFibLookupFnSockHashUpdateFnMsgRedirectHashFnSkRedirectHashFnLwtPushEncapFnLwtSeg6StoreBytesFnLwtSeg6AdjustSrhFnLwtSeg6ActionFnRcRepeatFnRcKeydownFnSkbCgroupIdFnGetCurrentCgroupIdFnGetLocalStorageFnSkSelectReuseportFnSkbAncestorCgroupIdFnSkLookupTcpFnSkLookupUdpFnSkReleaseFnMapPushElemFnMapPopElemFnMapPeekElemFnMsgPushDataFnMsgPopDataFnRcPointerRelFnSpinLockFnSpinUnlockFnSkFullsockFnTcpSockFnSkbEcnSetCeFnGetListenerSockFnSkcLookupTcpFnTcpCheckSyncookieFnSysctlGetNameFnSysctlGetCurrentValueFnSysctlGetNewValueFnSysctlSetNewValueFnStrtolFnStrtoulFnSkStorageGetFnSkStorageDeleteFnSendSignalFnTcpGenSyncookieFnSkbOutputFnProbeReadUserFnProbeReadKernelFnProbeReadUserStrFnProbeReadKernelStrFnTcpSendAckFnSendSignalThreadFnJiffies64FnReadBranchRecordsFnGetNsCurrentPidTgidFnXdpOutputFnGetNetnsCookieFnGetCurrentAncestorCgroupIdFnSkAssignFnKtimeGetBootNsFnSeqPrintfFnSeqWriteFnSkCgroupIdFnSkAncestorCgroupIdFnRingbufOutputFnRingbufReserveFnRingbufSubmitFnRingbufDiscardFnRingbufQueryFnCsumLevelFnSkcToTcp6SockFnSkcToTcpSockFnSkcToTcpTimewaitSockFnSkcToTcpRequestSockFnSkcToUdp6SockFnGetTaskStackFnLoadHdrOptFnStoreHdrOptFnReserveHdrOptFnInodeStorageGetFnInodeStorageDeleteFnDPathFnCopyFromUserFnSnprintfBtfFnSeqPrintfBtfFnSkbCgroupClassidFnRedirectNeighFnPerCpuPtrFnThisCpuPtrFnRedirectPeerFnTaskStorageGetFnTaskStorageDeleteFnGetCurrentTaskBtfFnBprmOptsSetFnKtimeGetCoarseNsFnImaInodeHashFnSockFromFileFnCheckMtuFnForEachMapElemFnSnprintfFnSysBpfFnBtfFindByNameKindFnSysCloseFnTimerInitFnTimerSetCallbackFnTimerStartFnTimerCancelFnGetFuncIpFnGetAttachCookieFnTaskPtRegsFnGetBranchSnapshotFnTraceVprintkFnSkcToUnixSockFnKallsymsLookupNameFnFindVmaFnLoopFnStrncmpFnGetFuncArgFnGetFuncRetFnGetFuncArgCntFnGetRetvalFnSetRetvalFnXdpGetBuffLenFnXdpLoadBytesFnXdpStoreBytesFnCopyFromUserTaskFnSkbSetTstampFnImaFileHashFnKptrXchgFnMapLookupPercpuElemFnSkcToMptcpSockFnDynptrFromMemFnRingbufReserveDynptrFnRingbufSubmitDynptrFnRingbufDiscardDynptrFnDynptrReadFnDynptrWriteFnDynptrDataFnTcpRawGenSyncookieIpv4FnTcpRawGenSyncookieIpv6FnTcpRawCheckSyncookieIpv4FnTcpRawCheckSyncookieIpv6FnKtimeGetTaiNsFnUserRingbufDrainFnCgrpStorageGetFnCgrpStorageDelete\"\n\t_BuiltinFunc_name_1 = \"WindowsFnMapLookupElemWindowsFnMapUpdateElemWindowsFnMapDeleteElemWindowsFnMapLookupAndDeleteElemWindowsFnTailCallWindowsFnGetPrandomU32WindowsFnKtimeGetBootNsWindowsFnGetSmpProcessorIdWindowsFnKtimeGetNsWindowsFnCsumDiffWindowsFnRingbufOutputWindowsFnTracePrintk2WindowsFnTracePrintk3WindowsFnTracePrintk4WindowsFnTracePrintk5WindowsFnMapPushElemWindowsFnMapPopElemWindowsFnMapPeekElemWindowsFnGetCurrentPidTgidWindowsFnGetCurrentLogonIdWindowsFnIsCurrentAdminWindowsFnMemcpySWindowsFnMemcmpSWindowsFnMemsetWindowsFnMemmoveSWindowsFnGetSocketCookieWindowsFnStrncpySWindowsFnStrncatSWindowsFnStrnlenSWindowsFnKtimeGetBootMsWindowsFnKtimeGetMsWindowsFnPerfEventOutputWindowsFnGetCurrentProcessStartKeyWindowsFnGetCurrentThreadCreateTime\"\n)\n\nvar (\n\t_BuiltinFunc_index_0 = [...]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, 2508, 2526, 2538, 2551, 2562, 2579, 2591, 2610, 2624, 2639, 2659, 2668, 2674, 2683, 2695, 2707, 2722, 2733, 2744, 2759, 2773, 2788, 2806, 2820, 2833, 2843, 2864, 2880, 2895, 2917, 2938, 2960, 2972, 2985, 2997, 3021, 3045, 3071, 3097, 3112, 3130, 3146, 3165}\n\t_BuiltinFunc_index_1 = [...]uint16{0, 22, 44, 66, 97, 114, 136, 159, 185, 204, 221, 243, 264, 285, 306, 327, 347, 366, 386, 412, 438, 461, 477, 493, 508, 525, 549, 566, 583, 600, 623, 642, 666, 700, 735}\n)\n\nfunc (i BuiltinFunc) String() string {\n\tswitch {\n\tcase i <= 211:\n\t\treturn _BuiltinFunc_name_0[_BuiltinFunc_index_0[i]:_BuiltinFunc_index_0[i+1]]\n\tcase 268435457 <= i && i <= 268435490:\n\t\ti -= 268435457\n\t\treturn _BuiltinFunc_name_1[_BuiltinFunc_index_1[i]:_BuiltinFunc_index_1[i+1]]\n\tdefault:\n\t\treturn \"BuiltinFunc(\" + strconv.FormatInt(int64(i), 10) + \")\"\n\t}\n}\n"
  },
  {
    "path": "asm/func_win.go",
    "content": "// Code generated by internal/cmd/genwinfunctions.awk; DO NOT EDIT.\n\npackage asm\n\n// Code in this file is derived from eBPF for Windows, available under the MIT License.\n\nimport \"github.com/cilium/ebpf/internal/platform\"\n\n// Built-in functions (Windows).\nconst (\n\tWindowsFnMapLookupElem              = BuiltinFunc(platform.WindowsTag | 1)\n\tWindowsFnMapUpdateElem              = BuiltinFunc(platform.WindowsTag | 2)\n\tWindowsFnMapDeleteElem              = BuiltinFunc(platform.WindowsTag | 3)\n\tWindowsFnMapLookupAndDeleteElem     = BuiltinFunc(platform.WindowsTag | 4)\n\tWindowsFnTailCall                   = BuiltinFunc(platform.WindowsTag | 5)\n\tWindowsFnGetPrandomU32              = BuiltinFunc(platform.WindowsTag | 6)\n\tWindowsFnKtimeGetBootNs             = BuiltinFunc(platform.WindowsTag | 7)\n\tWindowsFnGetSmpProcessorId          = BuiltinFunc(platform.WindowsTag | 8)\n\tWindowsFnKtimeGetNs                 = BuiltinFunc(platform.WindowsTag | 9)\n\tWindowsFnCsumDiff                   = BuiltinFunc(platform.WindowsTag | 10)\n\tWindowsFnRingbufOutput              = BuiltinFunc(platform.WindowsTag | 11)\n\tWindowsFnTracePrintk2               = BuiltinFunc(platform.WindowsTag | 12)\n\tWindowsFnTracePrintk3               = BuiltinFunc(platform.WindowsTag | 13)\n\tWindowsFnTracePrintk4               = BuiltinFunc(platform.WindowsTag | 14)\n\tWindowsFnTracePrintk5               = BuiltinFunc(platform.WindowsTag | 15)\n\tWindowsFnMapPushElem                = BuiltinFunc(platform.WindowsTag | 16)\n\tWindowsFnMapPopElem                 = BuiltinFunc(platform.WindowsTag | 17)\n\tWindowsFnMapPeekElem                = BuiltinFunc(platform.WindowsTag | 18)\n\tWindowsFnGetCurrentPidTgid          = BuiltinFunc(platform.WindowsTag | 19)\n\tWindowsFnGetCurrentLogonId          = BuiltinFunc(platform.WindowsTag | 20)\n\tWindowsFnIsCurrentAdmin             = BuiltinFunc(platform.WindowsTag | 21)\n\tWindowsFnMemcpyS                    = BuiltinFunc(platform.WindowsTag | 22)\n\tWindowsFnMemcmpS                    = BuiltinFunc(platform.WindowsTag | 23)\n\tWindowsFnMemset                     = BuiltinFunc(platform.WindowsTag | 24)\n\tWindowsFnMemmoveS                   = BuiltinFunc(platform.WindowsTag | 25)\n\tWindowsFnGetSocketCookie            = BuiltinFunc(platform.WindowsTag | 26)\n\tWindowsFnStrncpyS                   = BuiltinFunc(platform.WindowsTag | 27)\n\tWindowsFnStrncatS                   = BuiltinFunc(platform.WindowsTag | 28)\n\tWindowsFnStrnlenS                   = BuiltinFunc(platform.WindowsTag | 29)\n\tWindowsFnKtimeGetBootMs             = BuiltinFunc(platform.WindowsTag | 30)\n\tWindowsFnKtimeGetMs                 = BuiltinFunc(platform.WindowsTag | 31)\n\tWindowsFnPerfEventOutput            = BuiltinFunc(platform.WindowsTag | 32)\n\tWindowsFnGetCurrentProcessStartKey  = BuiltinFunc(platform.WindowsTag | 33)\n\tWindowsFnGetCurrentThreadCreateTime = BuiltinFunc(platform.WindowsTag | 34)\n)\n"
  },
  {
    "path": "asm/instruction.go",
    "content": "package asm\n\nimport (\n\t\"crypto/sha1\"\n\t\"crypto/sha256\"\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\t\"hash\"\n\t\"io\"\n\t\"math\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/platform\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n)\n\n// InstructionSize is the size of a BPF instruction in bytes\nconst InstructionSize = 8\n\n// RawInstructionOffset is an offset in units of raw BPF instructions.\ntype RawInstructionOffset uint64\n\nvar ErrUnreferencedSymbol = errors.New(\"unreferenced symbol\")\nvar ErrUnsatisfiedMapReference = errors.New(\"unsatisfied map reference\")\nvar ErrUnsatisfiedProgramReference = errors.New(\"unsatisfied program reference\")\n\n// Bytes returns the offset of an instruction in bytes.\nfunc (rio RawInstructionOffset) Bytes() uint64 {\n\treturn uint64(rio) * InstructionSize\n}\n\n// Instruction is a single eBPF instruction.\ntype Instruction struct {\n\tOpCode   OpCode\n\tDst      Register\n\tSrc      Register\n\tOffset   int16\n\tConstant int64\n\n\t// Metadata contains optional metadata about this instruction.\n\tMetadata Metadata\n}\n\n// Width returns how many raw BPF instructions the Instruction occupies within\n// an instruction stream. For example, an Instruction encoding a 64-bit value\n// will typically occupy 2 raw instructions, while a 32-bit constant can be\n// encoded in a single raw instruction.\nfunc (ins *Instruction) Width() RawInstructionOffset {\n\treturn RawInstructionOffset(ins.OpCode.rawInstructions())\n}\n\n// Unmarshal decodes a BPF instruction.\nfunc (ins *Instruction) Unmarshal(r io.Reader, bo binary.ByteOrder, platform string) error {\n\tdata := make([]byte, InstructionSize)\n\tif _, err := io.ReadFull(r, data); err != nil {\n\t\treturn err\n\t}\n\n\tins.OpCode = OpCode(data[0])\n\n\tregs := data[1]\n\tswitch bo {\n\tcase binary.LittleEndian:\n\t\tins.Dst, ins.Src = Register(regs&0xF), Register(regs>>4)\n\tcase binary.BigEndian:\n\t\tins.Dst, ins.Src = Register(regs>>4), Register(regs&0xf)\n\t}\n\n\tins.Offset = int16(bo.Uint16(data[2:4]))\n\n\t// Convert to int32 before widening to int64\n\t// to ensure the signed bit is carried over.\n\tins.Constant = int64(int32(bo.Uint32(data[4:8])))\n\n\tif ins.IsBuiltinCall() {\n\t\tif ins.Constant >= 0 {\n\t\t\t// Leave negative constants from the instruction stream\n\t\t\t// unchanged. These are sometimes used as placeholders for later\n\t\t\t// patching.\n\t\t\t// This relies on not having a valid platform tag with a high bit set.\n\t\t\tfn, err := BuiltinFuncForPlatform(platform, uint32(ins.Constant))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tins.Constant = int64(fn)\n\t\t}\n\t} else if ins.OpCode.Class().IsALU() {\n\t\tswitch ins.OpCode.ALUOp() {\n\t\tcase Div:\n\t\t\tif ins.Offset == 1 {\n\t\t\t\tins.OpCode = ins.OpCode.SetALUOp(SDiv)\n\t\t\t\tins.Offset = 0\n\t\t\t}\n\t\tcase Mod:\n\t\t\tif ins.Offset == 1 {\n\t\t\t\tins.OpCode = ins.OpCode.SetALUOp(SMod)\n\t\t\t\tins.Offset = 0\n\t\t\t}\n\t\tcase Mov:\n\t\t\tswitch ins.Offset {\n\t\t\tcase 8:\n\t\t\t\tins.OpCode = ins.OpCode.SetALUOp(MovSX8)\n\t\t\t\tins.Offset = 0\n\t\t\tcase 16:\n\t\t\t\tins.OpCode = ins.OpCode.SetALUOp(MovSX16)\n\t\t\t\tins.Offset = 0\n\t\t\tcase 32:\n\t\t\t\tins.OpCode = ins.OpCode.SetALUOp(MovSX32)\n\t\t\t\tins.Offset = 0\n\t\t\t}\n\t\t}\n\t} else if ins.OpCode.Class() == StXClass &&\n\t\tins.OpCode.Mode() == AtomicMode {\n\t\t// For atomic ops, part of the opcode is stored in the\n\t\t// constant field. Shift over 8 bytes so we can OR with the actual opcode and\n\t\t// apply `atomicMask` to avoid merging unknown bits that may be added in the future.\n\t\tins.OpCode |= (OpCode((ins.Constant << 8)) & atomicMask)\n\t}\n\n\tif !ins.OpCode.IsDWordLoad() {\n\t\treturn nil\n\t}\n\n\t// Pull another instruction from the stream to retrieve the second\n\t// half of the 64-bit immediate value.\n\tif _, err := io.ReadFull(r, data); err != nil {\n\t\t// No Wrap, to avoid io.EOF clash\n\t\treturn errors.New(\"64bit immediate is missing second half\")\n\t}\n\n\t// Require that all fields other than the value are zero.\n\tif bo.Uint32(data[0:4]) != 0 {\n\t\treturn errors.New(\"64bit immediate has non-zero fields\")\n\t}\n\n\tcons1 := uint32(ins.Constant)\n\tcons2 := int32(bo.Uint32(data[4:8]))\n\tins.Constant = int64(cons2)<<32 | int64(cons1)\n\n\treturn nil\n}\n\n// Marshal encodes a BPF instruction.\nfunc (ins Instruction) Marshal(w io.Writer, bo binary.ByteOrder) (uint64, error) {\n\tif ins.OpCode == InvalidOpCode {\n\t\treturn 0, errors.New(\"invalid opcode\")\n\t}\n\n\tisDWordLoad := ins.OpCode.IsDWordLoad()\n\n\tcons := int32(ins.Constant)\n\tif isDWordLoad {\n\t\t// Encode least significant 32bit first for 64bit operations.\n\t\tcons = int32(uint32(ins.Constant))\n\t}\n\n\tregs, err := newBPFRegisters(ins.Dst, ins.Src, bo)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"can't marshal registers: %s\", err)\n\t}\n\n\tif ins.IsBuiltinCall() {\n\t\tfn := BuiltinFunc(ins.Constant)\n\t\tplat, value := platform.DecodeConstant(fn)\n\t\tif plat != platform.Native {\n\t\t\treturn 0, fmt.Errorf(\"function %s (%s): %w\", fn, plat, internal.ErrNotSupportedOnOS)\n\t\t}\n\t\tcons = int32(value)\n\t} else if ins.OpCode.Class().IsALU() {\n\t\tnewOffset := int16(0)\n\t\tswitch ins.OpCode.ALUOp() {\n\t\tcase SDiv:\n\t\t\tins.OpCode = ins.OpCode.SetALUOp(Div)\n\t\t\tnewOffset = 1\n\t\tcase SMod:\n\t\t\tins.OpCode = ins.OpCode.SetALUOp(Mod)\n\t\t\tnewOffset = 1\n\t\tcase MovSX8:\n\t\t\tins.OpCode = ins.OpCode.SetALUOp(Mov)\n\t\t\tnewOffset = 8\n\t\tcase MovSX16:\n\t\t\tins.OpCode = ins.OpCode.SetALUOp(Mov)\n\t\t\tnewOffset = 16\n\t\tcase MovSX32:\n\t\t\tins.OpCode = ins.OpCode.SetALUOp(Mov)\n\t\t\tnewOffset = 32\n\t\t}\n\t\tif newOffset != 0 && ins.Offset != 0 {\n\t\t\treturn 0, fmt.Errorf(\"extended ALU opcodes should have an .Offset of 0: %s\", ins)\n\t\t}\n\t\tins.Offset = newOffset\n\t} else if atomic := ins.OpCode.AtomicOp(); atomic != InvalidAtomic {\n\t\tins.OpCode = ins.OpCode &^ atomicMask\n\t\tins.Constant = int64(atomic >> 8)\n\t}\n\n\top, err := ins.OpCode.bpfOpCode()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tdata := make([]byte, InstructionSize)\n\tdata[0] = op\n\tdata[1] = byte(regs)\n\tbo.PutUint16(data[2:4], uint16(ins.Offset))\n\tbo.PutUint32(data[4:8], uint32(cons))\n\tif _, err := w.Write(data); err != nil {\n\t\treturn 0, err\n\t}\n\n\tif !isDWordLoad {\n\t\treturn InstructionSize, nil\n\t}\n\n\t// The first half of the second part of a double-wide instruction\n\t// must be zero. The second half carries the value.\n\tbo.PutUint32(data[0:4], 0)\n\tbo.PutUint32(data[4:8], uint32(ins.Constant>>32))\n\tif _, err := w.Write(data); err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn 2 * InstructionSize, nil\n}\n\n// AssociateMap associates a Map with this Instruction.\n//\n// Implicitly clears the Instruction's Reference field.\n//\n// Returns an error if the Instruction is not a map load.\nfunc (ins *Instruction) AssociateMap(m FDer) error {\n\tif !ins.IsLoadFromMap() {\n\t\treturn errors.New(\"not a load from a map\")\n\t}\n\n\tins.Metadata.Set(referenceMeta{}, nil)\n\tins.Metadata.Set(mapMeta{}, m)\n\n\treturn nil\n}\n\nfunc (ins *Instruction) encodeMapFD(fd int) {\n\t// Preserve the offset value for direct map loads.\n\toffset := uint64(ins.Constant) & (math.MaxUint32 << 32)\n\trawFd := uint64(uint32(fd))\n\tins.Constant = int64(offset | rawFd)\n}\n\n// mapFd returns the map file descriptor stored in the 32 least significant\n// bits of ins' Constant field.\nfunc (ins *Instruction) mapFd() int {\n\treturn int(int32(ins.Constant))\n}\n\n// RewriteMapOffset changes the offset of a direct load from a map.\n//\n// Returns an error if the instruction is not a direct load.\nfunc (ins *Instruction) RewriteMapOffset(offset uint32) error {\n\tif !ins.OpCode.IsDWordLoad() {\n\t\treturn fmt.Errorf(\"%s is not a 64 bit load\", ins.OpCode)\n\t}\n\n\tif ins.Src != PseudoMapValue {\n\t\treturn errors.New(\"not a direct load from a map\")\n\t}\n\n\tfd := uint64(ins.Constant) & math.MaxUint32\n\tins.Constant = int64(uint64(offset)<<32 | fd)\n\treturn nil\n}\n\nfunc (ins *Instruction) mapOffset() uint32 {\n\treturn uint32(uint64(ins.Constant) >> 32)\n}\n\n// IsLoadFromMap returns true if the instruction loads from a map.\n//\n// This covers both loading the map pointer and direct map value loads.\nfunc (ins *Instruction) IsLoadFromMap() bool {\n\treturn ins.OpCode == LoadImmOp(DWord) && (ins.Src == PseudoMapFD || ins.Src == PseudoMapValue)\n}\n\n// IsFunctionCall returns true if the instruction calls another BPF function.\n//\n// This is not the same thing as a BPF helper call.\nfunc (ins *Instruction) IsFunctionCall() bool {\n\treturn ins.OpCode.JumpOp() == Call && ins.Src == PseudoCall\n}\n\n// IsKfuncCall returns true if the instruction calls a kfunc.\n//\n// This is not the same thing as a BPF helper call.\nfunc (ins *Instruction) IsKfuncCall() bool {\n\treturn ins.OpCode.JumpOp() == Call && ins.Src == PseudoKfuncCall\n}\n\n// IsLoadOfFunctionPointer returns true if the instruction loads a function pointer.\nfunc (ins *Instruction) IsLoadOfFunctionPointer() bool {\n\treturn ins.OpCode.IsDWordLoad() && ins.Src == PseudoFunc\n}\n\n// IsFunctionReference returns true if the instruction references another BPF\n// function, either by invoking a Call jump operation or by loading a function\n// pointer.\nfunc (ins *Instruction) IsFunctionReference() bool {\n\treturn ins.IsFunctionCall() || ins.IsLoadOfFunctionPointer()\n}\n\n// IsBuiltinCall returns true if the instruction is a built-in call, i.e. BPF helper call.\nfunc (ins *Instruction) IsBuiltinCall() bool {\n\treturn ins.OpCode.JumpOp() == Call && ins.Src == R0 && ins.Dst == R0\n}\n\n// IsConstantLoad returns true if the instruction loads a constant of the\n// given size.\nfunc (ins *Instruction) IsConstantLoad(size Size) bool {\n\treturn ins.OpCode == LoadImmOp(size) && ins.Src == R0 && ins.Offset == 0\n}\n\n// Format implements fmt.Formatter.\nfunc (ins Instruction) Format(f fmt.State, c rune) {\n\tif c != 'v' {\n\t\tfmt.Fprintf(f, \"{UNRECOGNIZED: %c}\", c)\n\t\treturn\n\t}\n\n\top := ins.OpCode\n\n\tif op == InvalidOpCode {\n\t\tfmt.Fprint(f, \"INVALID\")\n\t\treturn\n\t}\n\n\t// Omit trailing space for Exit\n\tif op.JumpOp() == Exit {\n\t\tfmt.Fprint(f, op)\n\t\treturn\n\t}\n\n\tif ins.IsLoadFromMap() {\n\t\tfd := ins.mapFd()\n\t\tm := ins.Map()\n\t\tswitch ins.Src {\n\t\tcase PseudoMapFD:\n\t\t\tif m != nil {\n\t\t\t\tfmt.Fprintf(f, \"LoadMapPtr dst: %s map: %s\", ins.Dst, m)\n\t\t\t} else {\n\t\t\t\tfmt.Fprintf(f, \"LoadMapPtr dst: %s fd: %d\", ins.Dst, fd)\n\t\t\t}\n\n\t\tcase PseudoMapValue:\n\t\t\tif m != nil {\n\t\t\t\tfmt.Fprintf(f, \"LoadMapValue dst: %s, map: %s off: %d\", ins.Dst, m, ins.mapOffset())\n\t\t\t} else {\n\t\t\t\tfmt.Fprintf(f, \"LoadMapValue dst: %s, fd: %d off: %d\", ins.Dst, fd, ins.mapOffset())\n\t\t\t}\n\t\t}\n\n\t\tgoto ref\n\t}\n\n\tswitch cls := op.Class(); {\n\tcase cls.isLoadOrStore():\n\t\tfmt.Fprintf(f, \"%v \", op)\n\t\tswitch op.Mode() {\n\t\tcase ImmMode:\n\t\t\tfmt.Fprintf(f, \"dst: %s imm: %d\", ins.Dst, ins.Constant)\n\t\tcase AbsMode:\n\t\t\tfmt.Fprintf(f, \"imm: %d\", ins.Constant)\n\t\tcase IndMode:\n\t\t\tfmt.Fprintf(f, \"dst: %s src: %s imm: %d\", ins.Dst, ins.Src, ins.Constant)\n\t\tcase MemMode, MemSXMode:\n\t\t\tfmt.Fprintf(f, \"dst: %s src: %s off: %d imm: %d\", ins.Dst, ins.Src, ins.Offset, ins.Constant)\n\t\tcase AtomicMode:\n\t\t\tfmt.Fprintf(f, \"dst: %s src: %s off: %d\", ins.Dst, ins.Src, ins.Offset)\n\t\t}\n\n\tcase cls.IsALU():\n\t\tfmt.Fprintf(f, \"%v\", op)\n\t\tif op == Swap.Op(ImmSource) {\n\t\t\tfmt.Fprintf(f, \"%d\", ins.Constant)\n\t\t}\n\n\t\tfmt.Fprintf(f, \" dst: %s \", ins.Dst)\n\t\tswitch {\n\t\tcase op.ALUOp() == Swap:\n\t\t\tbreak\n\t\tcase op.Source() == ImmSource:\n\t\t\tfmt.Fprintf(f, \"imm: %d\", ins.Constant)\n\t\tdefault:\n\t\t\tfmt.Fprintf(f, \"src: %s\", ins.Src)\n\t\t}\n\n\tcase cls.IsJump():\n\t\tfmt.Fprintf(f, \"%v \", op)\n\t\tswitch jop := op.JumpOp(); jop {\n\t\tcase Call:\n\t\t\tswitch ins.Src {\n\t\t\tcase PseudoCall:\n\t\t\t\t// bpf-to-bpf call\n\t\t\t\tfmt.Fprint(f, ins.Constant)\n\t\t\tcase PseudoKfuncCall:\n\t\t\t\t// kfunc call\n\t\t\t\tfmt.Fprintf(f, \"Kfunc(%d)\", ins.Constant)\n\t\t\tdefault:\n\t\t\t\tfmt.Fprint(f, BuiltinFunc(ins.Constant))\n\t\t\t}\n\n\t\tcase Ja:\n\t\t\tif ins.OpCode.Class() == Jump32Class {\n\t\t\t\tfmt.Fprintf(f, \"imm: %d\", ins.Constant)\n\t\t\t} else {\n\t\t\t\tfmt.Fprintf(f, \"off: %d\", ins.Offset)\n\t\t\t}\n\n\t\tdefault:\n\t\t\tfmt.Fprintf(f, \"dst: %s off: %d \", ins.Dst, ins.Offset)\n\t\t\tif op.Source() == ImmSource {\n\t\t\t\tfmt.Fprintf(f, \"imm: %d\", ins.Constant)\n\t\t\t} else {\n\t\t\t\tfmt.Fprintf(f, \"src: %s\", ins.Src)\n\t\t\t}\n\t\t}\n\tdefault:\n\t\tfmt.Fprintf(f, \"%v \", op)\n\t}\n\nref:\n\tif ins.Reference() != \"\" {\n\t\tfmt.Fprintf(f, \" <%s>\", ins.Reference())\n\t}\n}\n\nfunc (ins Instruction) equal(other Instruction) bool {\n\treturn ins.OpCode == other.OpCode &&\n\t\tins.Dst == other.Dst &&\n\t\tins.Src == other.Src &&\n\t\tins.Offset == other.Offset &&\n\t\tins.Constant == other.Constant\n}\n\n// Size returns the amount of bytes ins would occupy in binary form.\nfunc (ins Instruction) Size() uint64 {\n\treturn uint64(InstructionSize * ins.OpCode.rawInstructions())\n}\n\n// WithMetadata sets the given Metadata on the Instruction. e.g. to copy\n// Metadata from another Instruction when replacing it.\nfunc (ins Instruction) WithMetadata(meta Metadata) Instruction {\n\tins.Metadata = meta\n\treturn ins\n}\n\ntype symbolMeta struct{}\n\n// WithSymbol marks the Instruction as a Symbol, which other Instructions\n// can point to using corresponding calls to WithReference.\nfunc (ins Instruction) WithSymbol(name string) Instruction {\n\tins.Metadata.Set(symbolMeta{}, name)\n\treturn ins\n}\n\n// Symbol returns the value ins has been marked with using WithSymbol,\n// otherwise returns an empty string. A symbol is often an Instruction\n// at the start of a function body.\nfunc (ins Instruction) Symbol() string {\n\tsym, _ := ins.Metadata.Get(symbolMeta{}).(string)\n\treturn sym\n}\n\ntype referenceMeta struct{}\n\n// WithReference makes ins reference another Symbol or map by name.\nfunc (ins Instruction) WithReference(ref string) Instruction {\n\tins.Metadata.Set(referenceMeta{}, ref)\n\treturn ins\n}\n\n// Reference returns the Symbol or map name referenced by ins, if any.\nfunc (ins Instruction) Reference() string {\n\tref, _ := ins.Metadata.Get(referenceMeta{}).(string)\n\treturn ref\n}\n\ntype mapMeta struct{}\n\n// Map returns the Map referenced by ins, if any.\n// An Instruction will contain a Map if e.g. it references an existing,\n// pinned map that was opened during ELF loading.\nfunc (ins Instruction) Map() FDer {\n\tfd, _ := ins.Metadata.Get(mapMeta{}).(FDer)\n\treturn fd\n}\n\ntype sourceMeta struct{}\n\n// WithSource adds source information about the Instruction.\nfunc (ins Instruction) WithSource(src fmt.Stringer) Instruction {\n\tins.Metadata.Set(sourceMeta{}, src)\n\treturn ins\n}\n\n// Source returns source information about the Instruction. The field is\n// present when the compiler emits BTF line info about the Instruction and\n// usually contains the line of source code responsible for it.\nfunc (ins Instruction) Source() fmt.Stringer {\n\tstr, _ := ins.Metadata.Get(sourceMeta{}).(fmt.Stringer)\n\treturn str\n}\n\n// A Comment can be passed to Instruction.WithSource to add a comment\n// to an instruction.\ntype Comment string\n\nfunc (s Comment) String() string {\n\treturn string(s)\n}\n\n// FDer represents a resource tied to an underlying file descriptor.\n// Used as a stand-in for e.g. ebpf.Map since that type cannot be\n// imported here and FD() is the only method we rely on.\ntype FDer interface {\n\tFD() int\n}\n\n// Instructions is an eBPF program.\ntype Instructions []Instruction\n\n// AppendInstructions decodes [Instruction] from r and appends them to insns.\nfunc AppendInstructions(insns Instructions, r io.Reader, bo binary.ByteOrder, platform string) (Instructions, error) {\n\tvar offset uint64\n\tfor {\n\t\tvar ins Instruction\n\t\terr := ins.Unmarshal(r, bo, platform)\n\t\tif errors.Is(err, io.EOF) {\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"offset %d: %w\", offset, err)\n\t\t}\n\n\t\tinsns = append(insns, ins)\n\t\toffset += ins.Size()\n\t}\n\n\treturn insns, nil\n}\n\n// Name returns the name of the function insns belongs to, if any.\nfunc (insns Instructions) Name() string {\n\tif len(insns) == 0 {\n\t\treturn \"\"\n\t}\n\treturn insns[0].Symbol()\n}\n\nfunc (insns Instructions) String() string {\n\treturn fmt.Sprint(insns)\n}\n\n// Size returns the amount of bytes insns would occupy in binary form.\nfunc (insns Instructions) Size() uint64 {\n\tvar sum uint64\n\tfor _, ins := range insns {\n\t\tsum += ins.Size()\n\t}\n\treturn sum\n}\n\n// AssociateMap updates all Instructions that Reference the given symbol\n// to point to an existing Map m instead.\n//\n// Returns ErrUnreferencedSymbol error if no references to symbol are found\n// in insns. If symbol is anything else than the symbol name of map (e.g.\n// a bpf2bpf subprogram), an error is returned.\nfunc (insns Instructions) AssociateMap(symbol string, m FDer) error {\n\tif symbol == \"\" {\n\t\treturn errors.New(\"empty symbol\")\n\t}\n\n\tvar found bool\n\tfor i := range insns {\n\t\tins := &insns[i]\n\t\tif ins.Reference() != symbol {\n\t\t\tcontinue\n\t\t}\n\n\t\tif err := ins.AssociateMap(m); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tfound = true\n\t}\n\n\tif !found {\n\t\treturn fmt.Errorf(\"symbol %s: %w\", symbol, ErrUnreferencedSymbol)\n\t}\n\n\treturn nil\n}\n\n// SymbolOffsets returns the set of symbols and their offset in\n// the instructions.\nfunc (insns Instructions) SymbolOffsets() (map[string]int, error) {\n\toffsets := make(map[string]int)\n\n\tfor i, ins := range insns {\n\t\tif ins.Symbol() == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tif _, ok := offsets[ins.Symbol()]; ok {\n\t\t\treturn nil, fmt.Errorf(\"duplicate symbol %s\", ins.Symbol())\n\t\t}\n\n\t\toffsets[ins.Symbol()] = i\n\t}\n\n\treturn offsets, nil\n}\n\n// FunctionReferences returns a set of symbol names these Instructions make\n// bpf-to-bpf calls to.\nfunc (insns Instructions) FunctionReferences() []string {\n\tcalls := make(map[string]struct{})\n\tfor _, ins := range insns {\n\t\tif ins.Constant != -1 {\n\t\t\t// BPF-to-BPF calls have -1 constants.\n\t\t\tcontinue\n\t\t}\n\n\t\tif ins.Reference() == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tif !ins.IsFunctionReference() {\n\t\t\tcontinue\n\t\t}\n\n\t\tcalls[ins.Reference()] = struct{}{}\n\t}\n\n\tresult := make([]string, 0, len(calls))\n\tfor call := range calls {\n\t\tresult = append(result, call)\n\t}\n\n\tsort.Strings(result)\n\treturn result\n}\n\n// ReferenceOffsets returns the set of references and their offset in\n// the instructions.\nfunc (insns Instructions) ReferenceOffsets() map[string][]int {\n\toffsets := make(map[string][]int)\n\n\tfor i, ins := range insns {\n\t\tif ins.Reference() == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\toffsets[ins.Reference()] = append(offsets[ins.Reference()], i)\n\t}\n\n\treturn offsets\n}\n\n// Format implements fmt.Formatter.\n//\n// You can control indentation of symbols by\n// specifying a width. Setting a precision controls the indentation of\n// instructions.\n// The default character is a tab, which can be overridden by specifying\n// the ' ' space flag.\nfunc (insns Instructions) Format(f fmt.State, c rune) {\n\tif c != 's' && c != 'v' {\n\t\tfmt.Fprintf(f, \"{UNKNOWN FORMAT '%c'}\", c)\n\t\treturn\n\t}\n\n\t// Precision is better in this case, because it allows\n\t// specifying 0 padding easily.\n\tpadding, ok := f.Precision()\n\tif !ok {\n\t\tpadding = 1\n\t}\n\n\tindent := strings.Repeat(\"\\t\", padding)\n\tif f.Flag(' ') {\n\t\tindent = strings.Repeat(\" \", padding)\n\t}\n\n\tsymPadding, ok := f.Width()\n\tif !ok {\n\t\tsymPadding = padding - 1\n\t}\n\tif symPadding < 0 {\n\t\tsymPadding = 0\n\t}\n\n\tsymIndent := strings.Repeat(\"\\t\", symPadding)\n\tif f.Flag(' ') {\n\t\tsymIndent = strings.Repeat(\" \", symPadding)\n\t}\n\n\t// Guess how many digits we need at most, by assuming that all instructions\n\t// are double wide.\n\thighestOffset := len(insns) * 2\n\toffsetWidth := int(math.Ceil(math.Log10(float64(highestOffset))))\n\n\titer := insns.Iterate()\n\tfor iter.Next() {\n\t\tif iter.Ins.Symbol() != \"\" {\n\t\t\tfmt.Fprintf(f, \"%s%s:\\n\", symIndent, iter.Ins.Symbol())\n\t\t}\n\t\tif src := iter.Ins.Source(); src != nil {\n\t\t\tline := strings.TrimSpace(src.String())\n\t\t\tif line != \"\" {\n\t\t\t\tfmt.Fprintf(f, \"%s%*s; %s\\n\", indent, offsetWidth, \" \", line)\n\t\t\t}\n\t\t}\n\t\tfmt.Fprintf(f, \"%s%*d: %v\\n\", indent, offsetWidth, iter.Offset, iter.Ins)\n\t}\n}\n\n// Marshal encodes a BPF program into the kernel format.\n//\n// insns may be modified if there are unresolved jumps or bpf2bpf calls.\n//\n// Returns ErrUnsatisfiedProgramReference if there is a Reference Instruction\n// without a matching Symbol Instruction within insns.\nfunc (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error {\n\tif err := insns.encodeFunctionReferences(); err != nil {\n\t\treturn err\n\t}\n\n\tif err := insns.encodeMapPointers(); err != nil {\n\t\treturn err\n\t}\n\n\tfor i, ins := range insns {\n\t\tif _, err := ins.Marshal(w, bo); err != nil {\n\t\t\treturn fmt.Errorf(\"instruction %d: %w\", i, err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// Tag calculates the kernel tag for a series of instructions.\n//\n// It mirrors bpf_prog_calc_tag in the kernel and so can be compared\n// to ProgramInfo.Tag to figure out whether a loaded program matches\n// certain instructions.\n//\n// Deprecated: The value produced by this method no longer matches tags produced\n// by the kernel since Linux 6.18. Use [Instructions.HasTag] instead.\nfunc (insns Instructions) Tag(bo binary.ByteOrder) (string, error) {\n\t// We cannot determine which hashing function to use without probing the kernel.\n\t// So use the legacy SHA-1 implementation and deprecate this method.\n\treturn insns.tagSha1(bo)\n}\n\n// HasTag returns true if the given tag matches the kernel tag of insns.\nfunc (insns Instructions) HasTag(tag string, bo binary.ByteOrder) (bool, error) {\n\tsha256Tag, err := insns.tagSha256(bo)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"hashing sha256: %w\", err)\n\t}\n\tif tag == sha256Tag {\n\t\treturn true, nil\n\t}\n\n\tsha1Tag, err := insns.tagSha1(bo)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"hashing sha1: %w\", err)\n\t}\n\n\treturn tag == sha1Tag, nil\n}\n\n// tagSha1 calculates the kernel tag for a series of instructions.\n//\n// It mirrors bpf_prog_calc_tag in kernels up to v6.18 and can be compared to\n// ProgramInfo.Tag to figure out whether a loaded Program matches insns.\nfunc (insns Instructions) tagSha1(bo binary.ByteOrder) (string, error) {\n\th := sha1.New()\n\tif err := insns.hash(h, bo); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn hex.EncodeToString(h.Sum(nil)[:sys.BPF_TAG_SIZE]), nil\n}\n\n// tagSha256 calculates the kernel tag for a series of instructions.\n//\n// It mirrors bpf_prog_calc_tag in the kernel and can be compared to\n// ProgramInfo.Tag to figure out whether a loaded Program matches insns.\nfunc (insns Instructions) tagSha256(bo binary.ByteOrder) (string, error) {\n\th := sha256.New()\n\tif err := insns.hash(h, bo); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn hex.EncodeToString(h.Sum(nil)[:sys.BPF_TAG_SIZE]), nil\n}\n\n// hash calculates the hash of the instruction stream. Map load instructions\n// are zeroed out, since these contain map file descriptors or pointers to\n// maps, which will be different from load to load and would make the hash\n// non-deterministic.\nfunc (insns Instructions) hash(h hash.Hash, bo binary.ByteOrder) error {\n\tfor i, ins := range insns {\n\t\tif ins.IsLoadFromMap() {\n\t\t\tins.Constant = 0\n\t\t}\n\t\t_, err := ins.Marshal(h, bo)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"instruction %d: %w\", i, err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// encodeFunctionReferences populates the Offset (or Constant, depending on\n// the instruction type) field of instructions with a Reference field to point\n// to the offset of the corresponding instruction with a matching Symbol field.\n//\n// Only Reference Instructions that are either jumps or BPF function references\n// (calls or function pointer loads) are populated.\n//\n// Returns ErrUnsatisfiedProgramReference if there is a Reference Instruction\n// without at least one corresponding Symbol Instruction within insns.\nfunc (insns Instructions) encodeFunctionReferences() error {\n\t// Index the offsets of instructions tagged as a symbol.\n\tsymbolOffsets := make(map[string]RawInstructionOffset)\n\titer := insns.Iterate()\n\tfor iter.Next() {\n\t\tins := iter.Ins\n\n\t\tif ins.Symbol() == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tif _, ok := symbolOffsets[ins.Symbol()]; ok {\n\t\t\treturn fmt.Errorf(\"duplicate symbol %s\", ins.Symbol())\n\t\t}\n\n\t\tsymbolOffsets[ins.Symbol()] = iter.Offset\n\t}\n\n\t// Find all instructions tagged as references to other symbols.\n\t// Depending on the instruction type, populate their constant or offset\n\t// fields to point to the symbol they refer to within the insn stream.\n\titer = insns.Iterate()\n\tfor iter.Next() {\n\t\ti := iter.Index\n\t\toffset := iter.Offset\n\t\tins := iter.Ins\n\n\t\tif ins.Reference() == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tswitch {\n\t\tcase ins.IsFunctionReference() && ins.Constant == -1,\n\t\t\tins.OpCode == Ja.opCode(Jump32Class, ImmSource) && ins.Constant == -1:\n\t\t\tsymOffset, ok := symbolOffsets[ins.Reference()]\n\t\t\tif !ok {\n\t\t\t\treturn fmt.Errorf(\"%s at insn %d: symbol %q: %w\", ins.OpCode, i, ins.Reference(), ErrUnsatisfiedProgramReference)\n\t\t\t}\n\n\t\t\tins.Constant = int64(symOffset - offset - 1)\n\n\t\tcase ins.OpCode.Class().IsJump() && ins.Offset == -1:\n\t\t\tsymOffset, ok := symbolOffsets[ins.Reference()]\n\t\t\tif !ok {\n\t\t\t\treturn fmt.Errorf(\"%s at insn %d: symbol %q: %w\", ins.OpCode, i, ins.Reference(), ErrUnsatisfiedProgramReference)\n\t\t\t}\n\n\t\t\tins.Offset = int16(symOffset - offset - 1)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// encodeMapPointers finds all Map Instructions and encodes their FDs\n// into their Constant fields.\nfunc (insns Instructions) encodeMapPointers() error {\n\titer := insns.Iterate()\n\tfor iter.Next() {\n\t\tins := iter.Ins\n\n\t\tif !ins.IsLoadFromMap() {\n\t\t\tcontinue\n\t\t}\n\n\t\tm := ins.Map()\n\t\tif m == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tfd := m.FD()\n\t\tif fd < 0 {\n\t\t\treturn fmt.Errorf(\"map %s: %w\", m, sys.ErrClosedFd)\n\t\t}\n\n\t\tins.encodeMapFD(m.FD())\n\t}\n\n\treturn nil\n}\n\n// Iterate allows iterating a BPF program while keeping track of\n// various offsets.\n//\n// Modifying the instruction slice will lead to undefined behaviour.\nfunc (insns Instructions) Iterate() *InstructionIterator {\n\treturn &InstructionIterator{insns: insns}\n}\n\n// InstructionIterator iterates over a BPF program.\ntype InstructionIterator struct {\n\tinsns Instructions\n\t// The instruction in question.\n\tIns *Instruction\n\t// The index of the instruction in the original instruction slice.\n\tIndex int\n\t// The offset of the instruction in raw BPF instructions. This accounts\n\t// for double-wide instructions.\n\tOffset RawInstructionOffset\n}\n\n// Next returns true as long as there are any instructions remaining.\nfunc (iter *InstructionIterator) Next() bool {\n\tif len(iter.insns) == 0 {\n\t\treturn false\n\t}\n\n\tif iter.Ins != nil {\n\t\titer.Index++\n\t\titer.Offset += RawInstructionOffset(iter.Ins.OpCode.rawInstructions())\n\t}\n\titer.Ins = &iter.insns[0]\n\titer.insns = iter.insns[1:]\n\treturn true\n}\n\ntype bpfRegisters uint8\n\nfunc newBPFRegisters(dst, src Register, bo binary.ByteOrder) (bpfRegisters, error) {\n\tswitch bo {\n\tcase binary.LittleEndian:\n\t\treturn bpfRegisters((src << 4) | (dst & 0xF)), nil\n\tcase binary.BigEndian:\n\t\treturn bpfRegisters((dst << 4) | (src & 0xF)), nil\n\tdefault:\n\t\treturn 0, fmt.Errorf(\"unrecognized ByteOrder %T\", bo)\n\t}\n}\n"
  },
  {
    "path": "asm/instruction_test.go",
    "content": "package asm\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/internal/platform\"\n)\n\nvar test64bitImmProg = []byte{\n\t// r0 = math.MinInt32 - 1\n\t0x18, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x7f,\n\t0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,\n}\n\nfunc TestRead64bitImmediate(t *testing.T) {\n\tvar ins Instruction\n\terr := ins.Unmarshal(bytes.NewReader(test64bitImmProg), binary.LittleEndian, platform.Linux)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif c := ins.Constant; c != math.MinInt32-1 {\n\t\tt.Errorf(\"Expected immediate to be %v, got %v\", int64(math.MinInt32)-1, c)\n\t}\n}\n\nfunc BenchmarkRead64bitImmediate(b *testing.B) {\n\tr := &bytes.Reader{}\n\tfor b.Loop() {\n\t\tr.Reset(test64bitImmProg)\n\n\t\tvar ins Instruction\n\t\tif err := ins.Unmarshal(r, binary.LittleEndian, platform.Linux); err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc TestWrite64bitImmediate(t *testing.T) {\n\tinsns := Instructions{\n\t\tLoadImm(R0, math.MinInt32-1, DWord),\n\t}\n\n\tvar buf bytes.Buffer\n\tif err := insns.Marshal(&buf, binary.LittleEndian); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif prog := buf.Bytes(); !bytes.Equal(prog, test64bitImmProg) {\n\t\tt.Errorf(\"Marshalled program does not match:\\n%s\", hex.Dump(prog))\n\t}\n}\n\nfunc BenchmarkWrite64BitImmediate(b *testing.B) {\n\tins := LoadImm(R0, math.MinInt32-1, DWord)\n\n\tvar buf bytes.Buffer\n\tfor b.Loop() {\n\t\tbuf.Reset()\n\n\t\tif _, err := ins.Marshal(&buf, binary.LittleEndian); err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc TestAppendInstructions(t *testing.T) {\n\tr := bytes.NewReader(test64bitImmProg)\n\n\tinsns, err := AppendInstructions(nil, r, binary.LittleEndian, platform.Linux)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tif len(insns) != 1 {\n\t\tt.Fatalf(\"Expected one instruction, got %d\", len(insns))\n\t}\n}\n\nfunc TestSignedJump(t *testing.T) {\n\tinsns := Instructions{\n\t\tJSGT.Imm(R0, -1, \"foo\"),\n\t}\n\n\tinsns[0].Offset = 1\n\n\terr := insns.Marshal(io.Discard, binary.LittleEndian)\n\tif err != nil {\n\t\tt.Error(\"Can't marshal signed jump:\", err)\n\t}\n}\n\nfunc TestInstructionLoadMapValue(t *testing.T) {\n\tins := LoadMapValue(R0, 1, 123)\n\tif !ins.IsLoadFromMap() {\n\t\tt.Error(\"isLoadFromMap returns false\")\n\t}\n\tif fd := ins.mapFd(); fd != 1 {\n\t\tt.Error(\"Expected map fd to be 1, got\", fd)\n\t}\n\tif off := ins.mapOffset(); off != 123 {\n\t\tt.Fatal(\"Expected map offset to be 123 after changing the pointer, got\", off)\n\t}\n}\n\nfunc TestInstructionWithMetadata(t *testing.T) {\n\tins := LoadImm(R0, 123, DWord).WithSymbol(\"abc\")\n\tins2 := LoadImm(R0, 567, DWord).WithMetadata(ins.Metadata)\n\n\tif want, got := \"abc\", ins2.Symbol(); want != got {\n\t\tt.Fatalf(\"unexpected Symbol value on ins2: want: %s, got: %s\", want, got)\n\t}\n\n\tif want, got := ins.Metadata, ins2.Metadata; want != got {\n\t\tt.Fatal(\"expected ins and isn2 Metadata to match\")\n\t}\n}\n\nfunc TestReadCallToNegativeOne(t *testing.T) {\n\traw := []byte{\n\t\t0x85, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,\n\t}\n\tvar ins Instruction\n\terr := ins.Unmarshal(bytes.NewReader(raw), binary.LittleEndian, platform.Linux)\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.Equals(ins.Constant, -1))\n}\n\n// You can use format flags to change the way an eBPF\n// program is stringified.\nfunc ExampleInstructions_Format() {\n\n\tinsns := Instructions{\n\t\tFnMapLookupElem.Call().WithSymbol(\"my_func\").WithSource(Comment(\"bpf_map_lookup_elem()\")),\n\t\tLoadImm(R0, 42, DWord).WithSource(Comment(\"abc = 42\")),\n\t\tReturn(),\n\t}\n\n\tfmt.Println(\"Default format:\")\n\tfmt.Printf(\"%v\\n\", insns)\n\n\tfmt.Println(\"Don't indent instructions:\")\n\tfmt.Printf(\"%.0v\\n\", insns)\n\n\tfmt.Println(\"Indent using spaces:\")\n\tfmt.Printf(\"% v\\n\", insns)\n\n\tfmt.Println(\"Control symbol indentation:\")\n\tfmt.Printf(\"%2v\\n\", insns)\n\n\t// Output: Default format:\n\t// my_func:\n\t//\t ; bpf_map_lookup_elem()\n\t// \t0: Call FnMapLookupElem\n\t//\t ; abc = 42\n\t// \t1: LdImmDW dst: r0 imm: 42\n\t// \t3: Exit\n\t//\n\t// Don't indent instructions:\n\t// my_func:\n\t//  ; bpf_map_lookup_elem()\n\t// 0: Call FnMapLookupElem\n\t//  ; abc = 42\n\t// 1: LdImmDW dst: r0 imm: 42\n\t// 3: Exit\n\t//\n\t// Indent using spaces:\n\t// my_func:\n\t//   ; bpf_map_lookup_elem()\n\t//  0: Call FnMapLookupElem\n\t//   ; abc = 42\n\t//  1: LdImmDW dst: r0 imm: 42\n\t//  3: Exit\n\t//\n\t// Control symbol indentation:\n\t// \t\tmy_func:\n\t//\t ; bpf_map_lookup_elem()\n\t// \t0: Call FnMapLookupElem\n\t//\t ; abc = 42\n\t// \t1: LdImmDW dst: r0 imm: 42\n\t// \t3: Exit\n}\n\nfunc TestReadSrcDst(t *testing.T) {\n\ttestSrcDstProg := []byte{\n\t\t// on little-endian: r0 = r1\n\t\t// on big-endian: be: r1 = r0\n\t\t0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t}\n\n\ttestcases := []struct {\n\t\tbo       binary.ByteOrder\n\t\tdst, src Register\n\t}{\n\t\t{binary.BigEndian, R1, R0},\n\t\t{binary.LittleEndian, R0, R1},\n\t}\n\n\tfor _, tc := range testcases {\n\t\tt.Run(tc.bo.String(), func(t *testing.T) {\n\t\t\tvar ins Instruction\n\t\t\terr := ins.Unmarshal(bytes.NewReader(testSrcDstProg), tc.bo, platform.Linux)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tif ins.Dst != tc.dst {\n\t\t\t\tt.Errorf(\"Expected destination to be %v, got %v\", tc.dst, ins.Dst)\n\t\t\t}\n\t\t\tif ins.Src != tc.src {\n\t\t\t\tt.Errorf(\"Expected source to be %v, got %v\", tc.src, ins.Src)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestInstructionIterator(t *testing.T) {\n\tinsns := Instructions{\n\t\tLoadImm(R0, 0, Word),\n\t\tLoadImm(R0, 0, DWord),\n\t\tReturn(),\n\t}\n\toffsets := []RawInstructionOffset{0, 1, 3}\n\n\titer := insns.Iterate()\n\tfor i := 0; i < len(insns); i++ {\n\t\tif !iter.Next() {\n\t\t\tt.Fatalf(\"Expected %dth call to Next to return true\", i)\n\t\t}\n\n\t\tif iter.Ins == nil {\n\t\t\tt.Errorf(\"Expected iter.Ins to be non-nil\")\n\t\t}\n\t\tif iter.Index != i {\n\t\t\tt.Errorf(\"Expected iter.Index to be %d, got %d\", i, iter.Index)\n\t\t}\n\t\tif iter.Offset != offsets[i] {\n\t\t\tt.Errorf(\"Expected iter.Offset to be %d, got %d\", offsets[i], iter.Offset)\n\t\t}\n\t}\n}\n\nfunc TestMetadataCopyOnWrite(t *testing.T) {\n\t// Setting metadata should copy Instruction and modify the metadata pointer\n\t// of the new object without touching the old Instruction.\n\n\t// Reference\n\tins := Ja.Label(\"my_func\")\n\tins2 := ins.WithReference(\"my_func2\")\n\n\tqt.Assert(t, qt.Equals(ins.Reference(), \"my_func\"), qt.Commentf(\"WithReference updated ins\"))\n\tqt.Assert(t, qt.Equals(ins2.Reference(), \"my_func2\"), qt.Commentf(\"WithReference didn't update ins2\"))\n\n\t// Symbol\n\tins = Ja.Label(\"\").WithSymbol(\"my_sym\")\n\tins2 = ins.WithSymbol(\"my_sym2\")\n\n\tqt.Assert(t, qt.Equals(ins.Symbol(), \"my_sym\"), qt.Commentf(\"WithSymbol updated ins\"))\n\tqt.Assert(t, qt.Equals(ins2.Symbol(), \"my_sym2\"), qt.Commentf(\"WithSymbol didn't update ins2\"))\n\n\t// Map\n\tins = LoadMapPtr(R1, 0)\n\tins2 = ins\n\n\ttestMap := testFDer(1)\n\tqt.Assert(t, qt.IsNil(ins2.AssociateMap(testMap)), qt.Commentf(\"failed to associate map with ins2\"))\n\n\tqt.Assert(t, qt.IsNil(ins.Map()), qt.Commentf(\"AssociateMap updated ins\"))\n\tqt.Assert(t, qt.Equals[FDer](ins2.Map(), testMap), qt.Commentf(\"AssociateMap didn't update ins2\"))\n}\n\ntype testFDer int\n\nfunc (t testFDer) FD() int {\n\treturn int(t)\n}\n\nfunc TestAtomics(t *testing.T) {\n\trawInsns := []byte{\n\t\t0xc3, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // lock *(u32 *)(r1 + 0x1) += w2\n\t\t0xc3, 0x21, 0x01, 0x00, 0x50, 0x00, 0x00, 0x00, // lock *(u32 *)(r1 + 0x1) &= w2\n\t\t0xc3, 0x21, 0x01, 0x00, 0xa0, 0x00, 0x00, 0x00, // lock *(u32 *)(r1 + 0x1) ^= w2\n\t\t0xc3, 0x21, 0x01, 0x00, 0x40, 0x00, 0x00, 0x00, // lock *(u32 *)(r1 + 0x1) |= w2\n\n\t\t0xdb, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // lock *(u64 *)(r1 + 0x1) += r2\n\t\t0xdb, 0x21, 0x01, 0x00, 0x50, 0x00, 0x00, 0x00, // lock *(u64 *)(r1 + 0x1) &= r2\n\t\t0xdb, 0x21, 0x01, 0x00, 0xa0, 0x00, 0x00, 0x00, // lock *(u64 *)(r1 + 0x1) ^= r2\n\t\t0xdb, 0x21, 0x01, 0x00, 0x40, 0x00, 0x00, 0x00, // lock *(u64 *)(r1 + 0x1) |= r2\n\n\t\t0xc3, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // w0 = atomic_fetch_add((u32 *)(r1 + 0x0), w0)\n\t\t0xc3, 0x01, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, // w0 = atomic_fetch_and((u32 *)(r1 + 0x0), w0)\n\t\t0xc3, 0x01, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00, // w0 = atomic_fetch_xor((u32 *)(r1 + 0x0), w0)\n\t\t0xc3, 0x01, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, // w0 = atomic_fetch_or((u32 *)(r1 + 0x0), w0)\n\n\t\t0xdb, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // r0 = atomic_fetch_add((u64 *)(r1 + 0x0), r0)\n\t\t0xdb, 0x01, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, // r0 = atomic_fetch_and((u64 *)(r1 + 0x0), r0)\n\t\t0xdb, 0x01, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00, // r0 = atomic_fetch_xor((u64 *)(r1 + 0x0), r0)\n\t\t0xdb, 0x01, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, // r0 = atomic_fetch_or((u64 *)(r1 + 0x0), r0)\n\n\t\t0xc3, 0x01, 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, // w0 = xchg32_32(r1 + 0x0, w0)\n\t\t0xdb, 0x01, 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, // r0 = xchg_64(r1 + 0x0, r0)\n\n\t\t0xc3, 0x11, 0x00, 0x00, 0xf1, 0x00, 0x00, 0x00, // w0 = cmpxchg32_32(r1 + 0x0, w0, w1)\n\t\t0xdb, 0x11, 0x00, 0x00, 0xf1, 0x00, 0x00, 0x00, // r0 = cmpxchg_64(r1 + 0x0, r0, r1)\n\t}\n\n\tinsns, err := AppendInstructions(nil, bytes.NewReader(rawInsns), binary.LittleEndian, platform.Linux)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tlines := []string{\n\t\t\"StXAtomicAddW dst: r1 src: r2 off: 1\",\n\t\t\"StXAtomicAndW dst: r1 src: r2 off: 1\",\n\t\t\"StXAtomicXorW dst: r1 src: r2 off: 1\",\n\t\t\"StXAtomicOrW dst: r1 src: r2 off: 1\",\n\t\t\"StXAtomicAddDW dst: r1 src: r2 off: 1\",\n\t\t\"StXAtomicAndDW dst: r1 src: r2 off: 1\",\n\t\t\"StXAtomicXorDW dst: r1 src: r2 off: 1\",\n\t\t\"StXAtomicOrDW dst: r1 src: r2 off: 1\",\n\t\t\"StXAtomicFetchAddW dst: r1 src: r0 off: 0\",\n\t\t\"StXAtomicFetchAndW dst: r1 src: r0 off: 0\",\n\t\t\"StXAtomicFetchXorW dst: r1 src: r0 off: 0\",\n\t\t\"StXAtomicFetchOrW dst: r1 src: r0 off: 0\",\n\t\t\"StXAtomicFetchAddDW dst: r1 src: r0 off: 0\",\n\t\t\"StXAtomicFetchAndDW dst: r1 src: r0 off: 0\",\n\t\t\"StXAtomicFetchXorDW dst: r1 src: r0 off: 0\",\n\t\t\"StXAtomicFetchOrDW dst: r1 src: r0 off: 0\",\n\t\t\"StXAtomicXchgW dst: r1 src: r0 off: 0\",\n\t\t\"StXAtomicXchgDW dst: r1 src: r0 off: 0\",\n\t\t\"StXAtomicCmpXchgW dst: r1 src: r1 off: 0\",\n\t\t\"StXAtomicCmpXchgDW dst: r1 src: r1 off: 0\",\n\t}\n\n\tfor i, ins := range insns {\n\t\tif want, got := lines[i], fmt.Sprint(ins); want != got {\n\t\t\tt.Errorf(\"Expected %q, got %q\", want, got)\n\t\t}\n\t}\n\n\t// Marshal and unmarshal again to make sure the instructions are\n\t// still valid.\n\tvar buf bytes.Buffer\n\terr = insns.Marshal(&buf, binary.LittleEndian)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif !bytes.Equal(buf.Bytes(), rawInsns) {\n\t\tt.Error(\"Expected instructions to be equal after marshalling\")\n\t}\n}\n\nfunc TestISAv4(t *testing.T) {\n\trawInsns := []byte{\n\t\t0xd7, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // r1 = bswap16 r1\n\t\t0xd7, 0x02, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, // r2 = bswap32 r2\n\t\t0xd7, 0x03, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // r3 = bswap64 r3\n\n\t\t0x91, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // r1 = *(s8 *)(r4 + 0x0)\n\t\t0x89, 0x52, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // r2 = *(s16 *)(r5 + 0x4)\n\t\t0x81, 0x63, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // r3 = *(s32 *)(r6 + 0x8)\n\n\t\t0x91, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // r1 = *(s8 *)(r4 + 0x0)\n\t\t0x89, 0x52, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // r2 = *(s16 *)(r5 + 0x4)\n\n\t\t0xbf, 0x41, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // r1 = (s8)r4\n\t\t0xbf, 0x52, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, // r2 = (s16)r5\n\t\t0xbf, 0x63, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, // r3 = (s32)r6\n\n\t\t0xbc, 0x31, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // w1 = (s8)w3\n\t\t0xbc, 0x42, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, // w2 = (s16)w4\n\n\t\t0x06, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, // gotol +3\n\n\t\t0x3f, 0x31, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // r1 s/= r3\n\t\t0x9f, 0x42, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // r2 s%= r4\n\n\t\t0x3c, 0x31, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // w1 s/= w3\n\t\t0x9c, 0x42, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // w2 s%= w4\n\n\t\t0xd3, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // w0 = load_acquire((u8 *)(r1 + 0x0))\n\t\t0xcb, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // w0 = load_acquire((u16 *)(r1 + 0x0))\n\t\t0xc3, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // w0 = load_acquire((u32 *)(r1 + 0x0))\n\t\t0xdb, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // r0 = load_acquire((u64 *)(r1 + 0x0))\n\n\t\t0xd3, 0x21, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, // store_release((u8 *)(r1 + 0x0), w2)\n\t\t0xcb, 0x21, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, // store_release((u16 *)(r1 + 0x0), w2)\n\t\t0xc3, 0x21, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, // store_release((u32 *)(r1 + 0x0), w2)\n\t\t0xdb, 0x21, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, // store_release((u64 *)(r1 + 0x0), r2)\n\t}\n\n\tinsns, err := AppendInstructions(nil, bytes.NewReader(rawInsns), binary.LittleEndian, platform.Linux)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tlines := []string{\n\t\t\"BSwap16 dst: r1 \",\n\t\t\"BSwap32 dst: r2 \",\n\t\t\"BSwap64 dst: r3 \",\n\t\t\"LdXMemSXB dst: r1 src: r4 off: 0 imm: 0\",\n\t\t\"LdXMemSXH dst: r2 src: r5 off: 4 imm: 0\",\n\t\t\"LdXMemSXW dst: r3 src: r6 off: 8 imm: 0\",\n\t\t\"LdXMemSXB dst: r1 src: r4 off: 0 imm: 0\",\n\t\t\"LdXMemSXH dst: r2 src: r5 off: 4 imm: 0\",\n\t\t\"MovSX8Reg dst: r1 src: r4\",\n\t\t\"MovSX16Reg dst: r2 src: r5\",\n\t\t\"MovSX32Reg dst: r3 src: r6\",\n\t\t\"MovSX8Reg32 dst: r1 src: r3\",\n\t\t\"MovSX16Reg32 dst: r2 src: r4\",\n\t\t\"Ja32 imm: 3\",\n\t\t\"SDivReg dst: r1 src: r3\",\n\t\t\"SModReg dst: r2 src: r4\",\n\t\t\"SDivReg32 dst: r1 src: r3\",\n\t\t\"SModReg32 dst: r2 src: r4\",\n\t\t\"StXAtomicLdAcqB dst: r0 src: r1 off: 0\",\n\t\t\"StXAtomicLdAcqH dst: r0 src: r1 off: 0\",\n\t\t\"StXAtomicLdAcqW dst: r0 src: r1 off: 0\",\n\t\t\"StXAtomicLdAcqDW dst: r0 src: r1 off: 0\",\n\t\t\"StXAtomicStRelB dst: r1 src: r2 off: 0\",\n\t\t\"StXAtomicStRelH dst: r1 src: r2 off: 0\",\n\t\t\"StXAtomicStRelW dst: r1 src: r2 off: 0\",\n\t\t\"StXAtomicStRelDW dst: r1 src: r2 off: 0\",\n\t}\n\n\tfor i, ins := range insns {\n\t\tif want, got := lines[i], fmt.Sprint(ins); want != got {\n\t\t\tt.Errorf(\"Expected %q, got %q\", want, got)\n\t\t}\n\t}\n\n\t// Marshal and unmarshal again to make sure the instructions are\n\t// still valid.\n\tvar buf bytes.Buffer\n\terr = insns.Marshal(&buf, binary.LittleEndian)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif !bytes.Equal(buf.Bytes(), rawInsns) {\n\t\tt.Error(\"Expected instructions to be equal after marshalling\")\n\t}\n}\n\nfunc TestLongJumpPatching(t *testing.T) {\n\tinsns := Instructions{\n\t\tLongJump(\"exit\"),\n\t\tXor.Reg(R0, R0),\n\t\tXor.Reg(R0, R0),\n\t\tXor.Reg(R0, R0),\n\t\tReturn().WithSymbol(\"exit\"),\n\t}\n\n\terr := insns.encodeFunctionReferences()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif insns[0].Constant != 3 {\n\t\tt.Errorf(\"Expected offset to be 3, got %d\", insns[1].Constant)\n\t}\n}\n"
  },
  {
    "path": "asm/jump.go",
    "content": "package asm\n\n//go:generate go tool stringer -output jump_string.go -type=JumpOp\n\n// JumpOp affect control flow.\n//\n//\tmsb      lsb\n//\t+----+-+---+\n//\t|OP  |s|cls|\n//\t+----+-+---+\ntype JumpOp uint8\n\nconst jumpMask OpCode = 0xf0\n\nconst (\n\t// InvalidJumpOp is returned by getters when invoked\n\t// on non branch OpCodes\n\tInvalidJumpOp JumpOp = 0xff\n\t// Ja jumps by offset unconditionally\n\tJa JumpOp = 0x00\n\t// JEq jumps by offset if r == imm\n\tJEq JumpOp = 0x10\n\t// JGT jumps by offset if r > imm\n\tJGT JumpOp = 0x20\n\t// JGE jumps by offset if r >= imm\n\tJGE JumpOp = 0x30\n\t// JSet jumps by offset if r & imm\n\tJSet JumpOp = 0x40\n\t// JNE jumps by offset if r != imm\n\tJNE JumpOp = 0x50\n\t// JSGT jumps by offset if signed r > signed imm\n\tJSGT JumpOp = 0x60\n\t// JSGE jumps by offset if signed r >= signed imm\n\tJSGE JumpOp = 0x70\n\t// Call builtin or user defined function from imm\n\tCall JumpOp = 0x80\n\t// Exit ends execution, with value in r0\n\tExit JumpOp = 0x90\n\t// JLT jumps by offset if r < imm\n\tJLT JumpOp = 0xa0\n\t// JLE jumps by offset if r <= imm\n\tJLE JumpOp = 0xb0\n\t// JSLT jumps by offset if signed r < signed imm\n\tJSLT JumpOp = 0xc0\n\t// JSLE jumps by offset if signed r <= signed imm\n\tJSLE JumpOp = 0xd0\n)\n\n// Return emits an exit instruction.\n//\n// Requires a return value in R0.\nfunc Return() Instruction {\n\treturn Instruction{\n\t\tOpCode: OpCode(JumpClass).SetJumpOp(Exit),\n\t}\n}\n\n// Op returns the OpCode for a given jump source.\nfunc (op JumpOp) Op(source Source) OpCode {\n\treturn OpCode(JumpClass).SetJumpOp(op).SetSource(source)\n}\n\n// Imm compares 64 bit dst to 64 bit value (sign extended), and adjusts PC by offset if the condition is fulfilled.\nfunc (op JumpOp) Imm(dst Register, value int32, label string) Instruction {\n\treturn Instruction{\n\t\tOpCode:   op.opCode(JumpClass, ImmSource),\n\t\tDst:      dst,\n\t\tOffset:   -1,\n\t\tConstant: int64(value),\n\t}.WithReference(label)\n}\n\n// Imm32 compares 32 bit dst to 32 bit value, and adjusts PC by offset if the condition is fulfilled.\n// Requires kernel 5.1.\nfunc (op JumpOp) Imm32(dst Register, value int32, label string) Instruction {\n\treturn Instruction{\n\t\tOpCode:   op.opCode(Jump32Class, ImmSource),\n\t\tDst:      dst,\n\t\tOffset:   -1,\n\t\tConstant: int64(value),\n\t}.WithReference(label)\n}\n\n// Reg compares 64 bit dst to 64 bit src, and adjusts PC by offset if the condition is fulfilled.\nfunc (op JumpOp) Reg(dst, src Register, label string) Instruction {\n\treturn Instruction{\n\t\tOpCode: op.opCode(JumpClass, RegSource),\n\t\tDst:    dst,\n\t\tSrc:    src,\n\t\tOffset: -1,\n\t}.WithReference(label)\n}\n\n// Reg32 compares 32 bit dst to 32 bit src, and adjusts PC by offset if the condition is fulfilled.\n// Requires kernel 5.1.\nfunc (op JumpOp) Reg32(dst, src Register, label string) Instruction {\n\treturn Instruction{\n\t\tOpCode: op.opCode(Jump32Class, RegSource),\n\t\tDst:    dst,\n\t\tSrc:    src,\n\t\tOffset: -1,\n\t}.WithReference(label)\n}\n\nfunc (op JumpOp) opCode(class Class, source Source) OpCode {\n\tif op == Exit || op == Call {\n\t\treturn InvalidOpCode\n\t}\n\n\treturn OpCode(class).SetJumpOp(op).SetSource(source)\n}\n\n// LongJump returns a jump always instruction with a range of [-2^31, 2^31 - 1].\nfunc LongJump(label string) Instruction {\n\treturn Instruction{\n\t\tOpCode:   Ja.opCode(Jump32Class, ImmSource),\n\t\tConstant: -1,\n\t}.WithReference(label)\n}\n\n// Label adjusts PC to the address of the label.\nfunc (op JumpOp) Label(label string) Instruction {\n\tif op == Call {\n\t\treturn Instruction{\n\t\t\tOpCode:   OpCode(JumpClass).SetJumpOp(Call),\n\t\t\tSrc:      PseudoCall,\n\t\t\tConstant: -1,\n\t\t}.WithReference(label)\n\t}\n\n\treturn Instruction{\n\t\tOpCode: OpCode(JumpClass).SetJumpOp(op),\n\t\tOffset: -1,\n\t}.WithReference(label)\n}\n"
  },
  {
    "path": "asm/jump_string.go",
    "content": "// Code generated by \"stringer -output jump_string.go -type=JumpOp\"; DO NOT EDIT.\n\npackage asm\n\nimport \"strconv\"\n\nfunc _() {\n\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n\t// Re-run the stringer command to generate them again.\n\tvar x [1]struct{}\n\t_ = x[InvalidJumpOp-255]\n\t_ = x[Ja-0]\n\t_ = x[JEq-16]\n\t_ = x[JGT-32]\n\t_ = x[JGE-48]\n\t_ = x[JSet-64]\n\t_ = x[JNE-80]\n\t_ = x[JSGT-96]\n\t_ = x[JSGE-112]\n\t_ = x[Call-128]\n\t_ = x[Exit-144]\n\t_ = x[JLT-160]\n\t_ = x[JLE-176]\n\t_ = x[JSLT-192]\n\t_ = x[JSLE-208]\n}\n\nconst _JumpOp_name = \"JaJEqJGTJGEJSetJNEJSGTJSGECallExitJLTJLEJSLTJSLEInvalidJumpOp\"\n\nvar _JumpOp_map = map[JumpOp]string{\n\t0:   _JumpOp_name[0:2],\n\t16:  _JumpOp_name[2:5],\n\t32:  _JumpOp_name[5:8],\n\t48:  _JumpOp_name[8:11],\n\t64:  _JumpOp_name[11:15],\n\t80:  _JumpOp_name[15:18],\n\t96:  _JumpOp_name[18:22],\n\t112: _JumpOp_name[22:26],\n\t128: _JumpOp_name[26:30],\n\t144: _JumpOp_name[30:34],\n\t160: _JumpOp_name[34:37],\n\t176: _JumpOp_name[37:40],\n\t192: _JumpOp_name[40:44],\n\t208: _JumpOp_name[44:48],\n\t255: _JumpOp_name[48:61],\n}\n\nfunc (i JumpOp) String() string {\n\tif str, ok := _JumpOp_map[i]; ok {\n\t\treturn str\n\t}\n\treturn \"JumpOp(\" + strconv.FormatInt(int64(i), 10) + \")\"\n}\n"
  },
  {
    "path": "asm/load_store.go",
    "content": "package asm\n\nimport \"fmt\"\n\n//go:generate go tool stringer -output load_store_string.go -type=Mode,Size\n\n// Mode for load and store operations\n//\n//\tmsb      lsb\n//\t+---+--+---+\n//\t|MDE|sz|cls|\n//\t+---+--+---+\ntype Mode uint8\n\nconst modeMask OpCode = 0xe0\n\nconst (\n\t// InvalidMode is returned by getters when invoked\n\t// on non load / store OpCodes\n\tInvalidMode Mode = 0xff\n\t// ImmMode - immediate value\n\tImmMode Mode = 0x00\n\t// AbsMode - immediate value + offset\n\tAbsMode Mode = 0x20\n\t// IndMode - indirect (imm+src)\n\tIndMode Mode = 0x40\n\t// MemMode - load from memory\n\tMemMode Mode = 0x60\n\t// MemSXMode - load from memory, sign extension\n\tMemSXMode Mode = 0x80\n\t// AtomicMode - add atomically across processors.\n\tAtomicMode Mode = 0xc0\n)\n\nconst atomicMask OpCode = 0x0001_ff00\n\ntype AtomicOp uint32\n\nconst (\n\tInvalidAtomic AtomicOp = 0xffff_ffff\n\n\t// AddAtomic - add src to memory address dst atomically\n\tAddAtomic AtomicOp = AtomicOp(Add) << 8\n\t// FetchAdd - add src to memory address dst atomically, store result in src\n\tFetchAdd AtomicOp = AddAtomic | fetch\n\t// AndAtomic - bitwise AND src with memory address at dst atomically\n\tAndAtomic AtomicOp = AtomicOp(And) << 8\n\t// FetchAnd - bitwise AND src with memory address at dst atomically, store result in src\n\tFetchAnd AtomicOp = AndAtomic | fetch\n\t// OrAtomic - bitwise OR src with memory address at dst atomically\n\tOrAtomic AtomicOp = AtomicOp(Or) << 8\n\t// FetchOr - bitwise OR src with memory address at dst atomically, store result in src\n\tFetchOr AtomicOp = OrAtomic | fetch\n\t// XorAtomic - bitwise XOR src with memory address at dst atomically\n\tXorAtomic AtomicOp = AtomicOp(Xor) << 8\n\t// FetchXor - bitwise XOR src with memory address at dst atomically, store result in src\n\tFetchXor AtomicOp = XorAtomic | fetch\n\n\t// Xchg - atomically exchange the old value with the new value\n\t//\n\t// src gets populated with the old value of *(size *)(dst + offset).\n\tXchg AtomicOp = 0x0000_e000 | fetch\n\t// CmpXchg - atomically compare and exchange the old value with the new value\n\t//\n\t// Compares R0 and *(size *)(dst + offset), writes src to *(size *)(dst + offset) on match.\n\t// R0 gets populated with the old value of *(size *)(dst + offset), even if no exchange occurs.\n\tCmpXchg AtomicOp = 0x0000_f000 | fetch\n\n\t// fetch modifier for copy-modify-write atomics\n\tfetch AtomicOp = 0x0000_0100\n\t// loadAcquire - atomically load with acquire semantics\n\tloadAcquire AtomicOp = 0x0001_0000\n\t// storeRelease - atomically store with release semantics\n\tstoreRelease AtomicOp = 0x0001_1000\n)\n\nfunc (op AtomicOp) String() string {\n\tvar name string\n\tswitch op {\n\tcase AddAtomic, AndAtomic, OrAtomic, XorAtomic:\n\t\tname = ALUOp(op >> 8).String()\n\tcase FetchAdd, FetchAnd, FetchOr, FetchXor:\n\t\tname = \"Fetch\" + ALUOp((op^fetch)>>8).String()\n\tcase Xchg:\n\t\tname = \"Xchg\"\n\tcase CmpXchg:\n\t\tname = \"CmpXchg\"\n\tcase loadAcquire:\n\t\tname = \"LdAcq\"\n\tcase storeRelease:\n\t\tname = \"StRel\"\n\tdefault:\n\t\tname = fmt.Sprintf(\"AtomicOp(%#x)\", uint32(op))\n\t}\n\n\treturn name\n}\n\nfunc (op AtomicOp) OpCode(size Size) OpCode {\n\tswitch op {\n\tcase AddAtomic, AndAtomic, OrAtomic, XorAtomic,\n\t\tFetchAdd, FetchAnd, FetchOr, FetchXor,\n\t\tXchg, CmpXchg:\n\t\tswitch size {\n\t\tcase Byte, Half:\n\t\t\t// 8-bit and 16-bit atomic copy-modify-write atomics are not supported\n\t\t\treturn InvalidOpCode\n\t\t}\n\t}\n\n\treturn OpCode(StXClass).SetMode(AtomicMode).SetSize(size).SetAtomicOp(op)\n}\n\n// Mem emits `*(size *)(dst + offset) (op) src`.\nfunc (op AtomicOp) Mem(dst, src Register, size Size, offset int16) Instruction {\n\treturn Instruction{\n\t\tOpCode: op.OpCode(size),\n\t\tDst:    dst,\n\t\tSrc:    src,\n\t\tOffset: offset,\n\t}\n}\n\n// Emits `lock-acquire dst = *(size *)(src + offset)`.\nfunc LoadAcquire(dst, src Register, size Size, offset int16) Instruction {\n\treturn Instruction{\n\t\tOpCode: loadAcquire.OpCode(size),\n\t\tDst:    dst,\n\t\tSrc:    src,\n\t\tOffset: offset,\n\t}\n}\n\n// Emits `lock-release *(size *)(dst + offset) = src`.\nfunc StoreRelease(dst, src Register, size Size, offset int16) Instruction {\n\treturn Instruction{\n\t\tOpCode: storeRelease.OpCode(size),\n\t\tDst:    dst,\n\t\tSrc:    src,\n\t\tOffset: offset,\n\t}\n}\n\n// Size of load and store operations\n//\n//\tmsb      lsb\n//\t+---+--+---+\n//\t|mde|SZ|cls|\n//\t+---+--+---+\ntype Size uint8\n\nconst sizeMask OpCode = 0x18\n\nconst (\n\t// InvalidSize is returned by getters when invoked\n\t// on non load / store OpCodes\n\tInvalidSize Size = 0xff\n\t// DWord - double word; 64 bits\n\tDWord Size = 0x18\n\t// Word - word; 32 bits\n\tWord Size = 0x00\n\t// Half - half-word; 16 bits\n\tHalf Size = 0x08\n\t// Byte - byte; 8 bits\n\tByte Size = 0x10\n)\n\n// Sizeof returns the size in bytes.\nfunc (s Size) Sizeof() int {\n\tswitch s {\n\tcase DWord:\n\t\treturn 8\n\tcase Word:\n\t\treturn 4\n\tcase Half:\n\t\treturn 2\n\tcase Byte:\n\t\treturn 1\n\tdefault:\n\t\treturn -1\n\t}\n}\n\n// LoadMemOp returns the OpCode to load a value of given size from memory.\nfunc LoadMemOp(size Size) OpCode {\n\treturn OpCode(LdXClass).SetMode(MemMode).SetSize(size)\n}\n\n// LoadMemSXOp returns the OpCode to load a value of given size from memory sign extended.\nfunc LoadMemSXOp(size Size) OpCode {\n\treturn OpCode(LdXClass).SetMode(MemSXMode).SetSize(size)\n}\n\n// LoadMem emits `dst = *(size *)(src + offset)`.\nfunc LoadMem(dst, src Register, offset int16, size Size) Instruction {\n\treturn Instruction{\n\t\tOpCode: LoadMemOp(size),\n\t\tDst:    dst,\n\t\tSrc:    src,\n\t\tOffset: offset,\n\t}\n}\n\n// LoadMemSX emits `dst = *(size *)(src + offset)` but sign extends dst.\nfunc LoadMemSX(dst, src Register, offset int16, size Size) Instruction {\n\tif size == DWord {\n\t\treturn Instruction{OpCode: InvalidOpCode}\n\t}\n\n\treturn Instruction{\n\t\tOpCode: LoadMemSXOp(size),\n\t\tDst:    dst,\n\t\tSrc:    src,\n\t\tOffset: offset,\n\t}\n}\n\n// LoadImmOp returns the OpCode to load an immediate of given size.\n//\n// As of kernel 4.20, only DWord size is accepted.\nfunc LoadImmOp(size Size) OpCode {\n\treturn OpCode(LdClass).SetMode(ImmMode).SetSize(size)\n}\n\n// LoadImm emits `dst = (size)value`.\n//\n// As of kernel 4.20, only DWord size is accepted.\nfunc LoadImm(dst Register, value int64, size Size) Instruction {\n\treturn Instruction{\n\t\tOpCode:   LoadImmOp(size),\n\t\tDst:      dst,\n\t\tConstant: value,\n\t}\n}\n\n// LoadMapPtr stores a pointer to a map in dst.\nfunc LoadMapPtr(dst Register, fd int) Instruction {\n\tif fd < 0 {\n\t\treturn Instruction{OpCode: InvalidOpCode}\n\t}\n\n\treturn Instruction{\n\t\tOpCode:   LoadImmOp(DWord),\n\t\tDst:      dst,\n\t\tSrc:      PseudoMapFD,\n\t\tConstant: int64(uint32(fd)),\n\t}\n}\n\n// LoadMapValue stores a pointer to the value at a certain offset of a map.\nfunc LoadMapValue(dst Register, fd int, offset uint32) Instruction {\n\tif fd < 0 {\n\t\treturn Instruction{OpCode: InvalidOpCode}\n\t}\n\n\tfdAndOffset := (uint64(offset) << 32) | uint64(uint32(fd))\n\treturn Instruction{\n\t\tOpCode:   LoadImmOp(DWord),\n\t\tDst:      dst,\n\t\tSrc:      PseudoMapValue,\n\t\tConstant: int64(fdAndOffset),\n\t}\n}\n\n// LoadIndOp returns the OpCode for loading a value of given size from an sk_buff.\nfunc LoadIndOp(size Size) OpCode {\n\treturn OpCode(LdClass).SetMode(IndMode).SetSize(size)\n}\n\n// LoadInd emits `dst = ntoh(*(size *)(((sk_buff *)R6)->data + src + offset))`.\nfunc LoadInd(dst, src Register, offset int32, size Size) Instruction {\n\treturn Instruction{\n\t\tOpCode:   LoadIndOp(size),\n\t\tDst:      dst,\n\t\tSrc:      src,\n\t\tConstant: int64(offset),\n\t}\n}\n\n// LoadAbsOp returns the OpCode for loading a value of given size from an sk_buff.\nfunc LoadAbsOp(size Size) OpCode {\n\treturn OpCode(LdClass).SetMode(AbsMode).SetSize(size)\n}\n\n// LoadAbs emits `r0 = ntoh(*(size *)(((sk_buff *)R6)->data + offset))`.\nfunc LoadAbs(offset int32, size Size) Instruction {\n\treturn Instruction{\n\t\tOpCode:   LoadAbsOp(size),\n\t\tDst:      R0,\n\t\tConstant: int64(offset),\n\t}\n}\n\n// StoreMemOp returns the OpCode for storing a register of given size in memory.\nfunc StoreMemOp(size Size) OpCode {\n\treturn OpCode(StXClass).SetMode(MemMode).SetSize(size)\n}\n\n// StoreMem emits `*(size *)(dst + offset) = src`\nfunc StoreMem(dst Register, offset int16, src Register, size Size) Instruction {\n\treturn Instruction{\n\t\tOpCode: StoreMemOp(size),\n\t\tDst:    dst,\n\t\tSrc:    src,\n\t\tOffset: offset,\n\t}\n}\n\n// StoreImmOp returns the OpCode for storing an immediate of given size in memory.\nfunc StoreImmOp(size Size) OpCode {\n\treturn OpCode(StClass).SetMode(MemMode).SetSize(size)\n}\n\n// StoreImm emits `*(size *)(dst + offset) = value`.\nfunc StoreImm(dst Register, offset int16, value int64, size Size) Instruction {\n\tif size == DWord {\n\t\treturn Instruction{OpCode: InvalidOpCode}\n\t}\n\n\treturn Instruction{\n\t\tOpCode:   StoreImmOp(size),\n\t\tDst:      dst,\n\t\tOffset:   offset,\n\t\tConstant: value,\n\t}\n}\n\n// StoreXAddOp returns the OpCode to atomically add a register to a value in memory.\nfunc StoreXAddOp(size Size) OpCode {\n\treturn AddAtomic.OpCode(size)\n}\n\n// StoreXAdd atomically adds src to *dst.\nfunc StoreXAdd(dst, src Register, size Size) Instruction {\n\treturn AddAtomic.Mem(dst, src, size, 0)\n}\n"
  },
  {
    "path": "asm/load_store_string.go",
    "content": "// Code generated by \"stringer -output load_store_string.go -type=Mode,Size\"; DO NOT EDIT.\n\npackage asm\n\nimport \"strconv\"\n\nfunc _() {\n\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n\t// Re-run the stringer command to generate them again.\n\tvar x [1]struct{}\n\t_ = x[InvalidMode-255]\n\t_ = x[ImmMode-0]\n\t_ = x[AbsMode-32]\n\t_ = x[IndMode-64]\n\t_ = x[MemMode-96]\n\t_ = x[MemSXMode-128]\n\t_ = x[AtomicMode-192]\n}\n\nconst (\n\t_Mode_name_0 = \"ImmMode\"\n\t_Mode_name_1 = \"AbsMode\"\n\t_Mode_name_2 = \"IndMode\"\n\t_Mode_name_3 = \"MemMode\"\n\t_Mode_name_4 = \"MemSXMode\"\n\t_Mode_name_5 = \"AtomicMode\"\n\t_Mode_name_6 = \"InvalidMode\"\n)\n\nfunc (i Mode) String() string {\n\tswitch {\n\tcase i == 0:\n\t\treturn _Mode_name_0\n\tcase i == 32:\n\t\treturn _Mode_name_1\n\tcase i == 64:\n\t\treturn _Mode_name_2\n\tcase i == 96:\n\t\treturn _Mode_name_3\n\tcase i == 128:\n\t\treturn _Mode_name_4\n\tcase i == 192:\n\t\treturn _Mode_name_5\n\tcase i == 255:\n\t\treturn _Mode_name_6\n\tdefault:\n\t\treturn \"Mode(\" + strconv.FormatInt(int64(i), 10) + \")\"\n\t}\n}\nfunc _() {\n\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n\t// Re-run the stringer command to generate them again.\n\tvar x [1]struct{}\n\t_ = x[InvalidSize-255]\n\t_ = x[DWord-24]\n\t_ = x[Word-0]\n\t_ = x[Half-8]\n\t_ = x[Byte-16]\n}\n\nconst (\n\t_Size_name_0 = \"Word\"\n\t_Size_name_1 = \"Half\"\n\t_Size_name_2 = \"Byte\"\n\t_Size_name_3 = \"DWord\"\n\t_Size_name_4 = \"InvalidSize\"\n)\n\nfunc (i Size) String() string {\n\tswitch {\n\tcase i == 0:\n\t\treturn _Size_name_0\n\tcase i == 8:\n\t\treturn _Size_name_1\n\tcase i == 16:\n\t\treturn _Size_name_2\n\tcase i == 24:\n\t\treturn _Size_name_3\n\tcase i == 255:\n\t\treturn _Size_name_4\n\tdefault:\n\t\treturn \"Size(\" + strconv.FormatInt(int64(i), 10) + \")\"\n\t}\n}\n"
  },
  {
    "path": "asm/metadata.go",
    "content": "package asm\n\n// Metadata contains metadata about an instruction.\ntype Metadata struct {\n\thead *metaElement\n}\n\ntype metaElement struct {\n\tnext       *metaElement\n\tkey, value interface{}\n}\n\n// Find the element containing key.\n//\n// Returns nil if there is no such element.\nfunc (m *Metadata) find(key interface{}) *metaElement {\n\tfor e := m.head; e != nil; e = e.next {\n\t\tif e.key == key {\n\t\t\treturn e\n\t\t}\n\t}\n\treturn nil\n}\n\n// Remove an element from the linked list.\n//\n// Copies as many elements of the list as necessary to remove r, but doesn't\n// perform a full copy.\nfunc (m *Metadata) remove(r *metaElement) {\n\tcurrent := &m.head\n\tfor e := m.head; e != nil; e = e.next {\n\t\tif e == r {\n\t\t\t// We've found the element we want to remove.\n\t\t\t*current = e.next\n\n\t\t\t// No need to copy the tail.\n\t\t\treturn\n\t\t}\n\n\t\t// There is another element in front of the one we want to remove.\n\t\t// We have to copy it to be able to change metaElement.next.\n\t\tcpy := &metaElement{key: e.key, value: e.value}\n\t\t*current = cpy\n\t\tcurrent = &cpy.next\n\t}\n}\n\n// Set a key to a value.\n//\n// If value is nil, the key is removed. Avoids modifying old metadata by\n// copying if necessary.\nfunc (m *Metadata) Set(key, value interface{}) {\n\tif e := m.find(key); e != nil {\n\t\tif e.value == value {\n\t\t\t// Key is present and the value is the same. Nothing to do.\n\t\t\treturn\n\t\t}\n\n\t\t// Key is present with a different value. Create a copy of the list\n\t\t// which doesn't have the element in it.\n\t\tm.remove(e)\n\t}\n\n\t// m.head is now a linked list that doesn't contain key.\n\tif value == nil {\n\t\treturn\n\t}\n\n\tm.head = &metaElement{key: key, value: value, next: m.head}\n}\n\n// Get the value of a key.\n//\n// Returns nil if no value with the given key is present.\nfunc (m *Metadata) Get(key interface{}) interface{} {\n\tif e := m.find(key); e != nil {\n\t\treturn e.value\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "asm/metadata_test.go",
    "content": "package asm\n\nimport (\n\t\"testing\"\n\t\"unsafe\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestMetadata(t *testing.T) {\n\tvar m Metadata\n\n\t// Metadata should be the size of a pointer.\n\tqt.Assert(t, qt.Equals(unsafe.Sizeof(m), unsafe.Sizeof(uintptr(0))))\n\n\t// A lookup in a nil meta should return nil.\n\tqt.Assert(t, qt.IsNil(m.Get(bool(false))))\n\n\t// We can look up anything we inserted.\n\tm.Set(bool(false), int(0))\n\tm.Set(int(1), int(1))\n\tqt.Assert(t, qt.Equals(m.Get(bool(false)), 0))\n\tqt.Assert(t, qt.Equals(m.Get(1), 1))\n\n\t// We have copy on write semantics\n\told := m\n\tm.Set(bool(false), int(1))\n\tqt.Assert(t, qt.Equals(m.Get(bool(false)), 1))\n\tqt.Assert(t, qt.Equals(m.Get(int(1)), 1))\n\tqt.Assert(t, qt.Equals(old.Get(bool(false)), 0))\n\tqt.Assert(t, qt.Equals(old.Get(int(1)), 1))\n\n\t// Newtypes are handled distinctly.\n\ttype b bool\n\tm.Set(b(false), int(42))\n\tqt.Assert(t, qt.Equals(m.Get(bool(false)), 1))\n\tqt.Assert(t, qt.Equals(m.Get(int(1)), 1))\n\tqt.Assert(t, qt.Equals(m.Get(b(false)), 42))\n\n\t// Setting nil removes a key.\n\tm.Set(bool(false), nil)\n\tqt.Assert(t, qt.IsNil(m.Get(bool(false))))\n\tqt.Assert(t, qt.Equals(m.Get(int(1)), 1))\n\tqt.Assert(t, qt.Equals(m.Get(b(false)), 42))\n}\n\nfunc BenchmarkMetadata(b *testing.B) {\n\t// Assume that three bits of metadata on a single instruction is\n\t// our worst case.\n\tconst worstCaseItems = 3\n\n\ttype t struct{}\n\n\tb.Run(\"add first\", func(b *testing.B) {\n\t\tb.ReportAllocs()\n\n\t\tfor b.Loop() {\n\t\t\tvar v Metadata\n\t\t\tv.Set(t{}, 0)\n\t\t}\n\t})\n\n\tb.Run(\"add last\", func(b *testing.B) {\n\t\tvar m Metadata\n\t\tfor i := 0; i < worstCaseItems-1; i++ {\n\t\t\tm.Set(i, i)\n\t\t}\n\n\t\tb.ReportAllocs()\n\n\t\tfor b.Loop() {\n\t\t\tv := m\n\t\t\tv.Set(t{}, 0)\n\t\t}\n\t})\n\n\tb.Run(\"add existing\", func(b *testing.B) {\n\t\tvar m Metadata\n\t\tfor i := 0; i < worstCaseItems-1; i++ {\n\t\t\tm.Set(i, i)\n\t\t}\n\t\tm.Set(t{}, 0)\n\n\t\tb.ReportAllocs()\n\n\t\tfor b.Loop() {\n\t\t\tv := m\n\t\t\tv.Set(t{}, 0)\n\t\t}\n\t})\n\n\tb.Run(\"get miss\", func(b *testing.B) {\n\t\tvar m Metadata\n\t\tfor i := 0; i < worstCaseItems; i++ {\n\t\t\tm.Set(i, i)\n\t\t}\n\n\t\tb.ReportAllocs()\n\n\t\tfor b.Loop() {\n\t\t\tif m.Get(t{}) != nil {\n\t\t\t\tb.Fatal(\"got result from miss\")\n\t\t\t}\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "asm/opcode.go",
    "content": "package asm\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\n//go:generate go tool stringer -output opcode_string.go -type=Class\n\n// Class of operations\n//\n//\tmsb      lsb\n//\t+---+--+---+\n//\t|  ??  |CLS|\n//\t+---+--+---+\ntype Class uint8\n\nconst classMask OpCode = 0x07\n\nconst (\n\t// LdClass loads immediate values into registers.\n\t// Also used for non-standard load operations from cBPF.\n\tLdClass Class = 0x00\n\t// LdXClass loads memory into registers.\n\tLdXClass Class = 0x01\n\t// StClass stores immediate values to memory.\n\tStClass Class = 0x02\n\t// StXClass stores registers to memory.\n\tStXClass Class = 0x03\n\t// ALUClass describes arithmetic operators.\n\tALUClass Class = 0x04\n\t// JumpClass describes jump operators.\n\tJumpClass Class = 0x05\n\t// Jump32Class describes jump operators with 32-bit comparisons.\n\t// Requires kernel 5.1.\n\tJump32Class Class = 0x06\n\t// ALU64Class describes arithmetic operators in 64-bit mode.\n\tALU64Class Class = 0x07\n)\n\n// IsLoad checks if this is either LdClass or LdXClass.\nfunc (cls Class) IsLoad() bool {\n\treturn cls == LdClass || cls == LdXClass\n}\n\n// IsStore checks if this is either StClass or StXClass.\nfunc (cls Class) IsStore() bool {\n\treturn cls == StClass || cls == StXClass\n}\n\nfunc (cls Class) isLoadOrStore() bool {\n\treturn cls.IsLoad() || cls.IsStore()\n}\n\n// IsALU checks if this is either ALUClass or ALU64Class.\nfunc (cls Class) IsALU() bool {\n\treturn cls == ALUClass || cls == ALU64Class\n}\n\n// IsJump checks if this is either JumpClass or Jump32Class.\nfunc (cls Class) IsJump() bool {\n\treturn cls == JumpClass || cls == Jump32Class\n}\n\nfunc (cls Class) isJumpOrALU() bool {\n\treturn cls.IsJump() || cls.IsALU()\n}\n\n// OpCode represents a single operation.\n// It is not a 1:1 mapping to real eBPF opcodes.\n//\n// The encoding varies based on a 3-bit Class:\n//\n//\t7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0\n//\t                          ???                            | CLS\n//\n// For ALUClass and ALUCLass32:\n//\n//\t7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0\n//\t             0                 |           OPC         |S| CLS\n//\n// For LdClass, LdXclass, StClass and StXClass:\n//\n//\t7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0\n//\t                       0                       | MDE |SIZ| CLS\n//\n// For StXClass where MDE == AtomicMode:\n//\n//\t7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0\n//\t              0              |    ATOMIC OP    | MDE |SIZ| CLS\n//\n// For JumpClass, Jump32Class:\n//\n//\t7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0\n//\t                       0                       |  OPC  |S| CLS\ntype OpCode uint32\n\n// InvalidOpCode is returned by setters on OpCode\nconst InvalidOpCode OpCode = 0xffff\n\n// bpfOpCode returns the actual BPF opcode.\nfunc (op OpCode) bpfOpCode() (byte, error) {\n\tconst opCodeMask = 0xff\n\n\tif !valid(op, opCodeMask) {\n\t\treturn 0, fmt.Errorf(\"invalid opcode %x\", op)\n\t}\n\n\treturn byte(op & opCodeMask), nil\n}\n\n// rawInstructions returns the number of BPF instructions required\n// to encode this opcode.\nfunc (op OpCode) rawInstructions() int {\n\tif op.IsDWordLoad() {\n\t\treturn 2\n\t}\n\treturn 1\n}\n\nfunc (op OpCode) IsDWordLoad() bool {\n\treturn op == LoadImmOp(DWord)\n}\n\n// Class returns the class of operation.\nfunc (op OpCode) Class() Class {\n\treturn Class(op & classMask)\n}\n\n// Mode returns the mode for load and store operations.\nfunc (op OpCode) Mode() Mode {\n\tif !op.Class().isLoadOrStore() {\n\t\treturn InvalidMode\n\t}\n\treturn Mode(op & modeMask)\n}\n\n// Size returns the size for load and store operations.\nfunc (op OpCode) Size() Size {\n\tif !op.Class().isLoadOrStore() {\n\t\treturn InvalidSize\n\t}\n\treturn Size(op & sizeMask)\n}\n\n// AtomicOp returns the type of atomic operation.\nfunc (op OpCode) AtomicOp() AtomicOp {\n\tif op.Class() != StXClass || op.Mode() != AtomicMode {\n\t\treturn InvalidAtomic\n\t}\n\treturn AtomicOp(op & atomicMask)\n}\n\n// Source returns the source for branch and ALU operations.\nfunc (op OpCode) Source() Source {\n\tif !op.Class().isJumpOrALU() || op.ALUOp() == Swap {\n\t\treturn InvalidSource\n\t}\n\treturn Source(op & sourceMask)\n}\n\n// ALUOp returns the ALUOp.\nfunc (op OpCode) ALUOp() ALUOp {\n\tif !op.Class().IsALU() {\n\t\treturn InvalidALUOp\n\t}\n\treturn ALUOp(op & aluMask)\n}\n\n// Endianness returns the Endianness for a byte swap instruction.\nfunc (op OpCode) Endianness() Endianness {\n\tif op.ALUOp() != Swap {\n\t\treturn InvalidEndian\n\t}\n\treturn Endianness(op & endianMask)\n}\n\n// JumpOp returns the JumpOp.\n// Returns InvalidJumpOp if it doesn't encode a jump.\nfunc (op OpCode) JumpOp() JumpOp {\n\tif !op.Class().IsJump() {\n\t\treturn InvalidJumpOp\n\t}\n\n\tjumpOp := JumpOp(op & jumpMask)\n\n\t// Some JumpOps are only supported by JumpClass, not Jump32Class.\n\tif op.Class() == Jump32Class && (jumpOp == Exit || jumpOp == Call) {\n\t\treturn InvalidJumpOp\n\t}\n\n\treturn jumpOp\n}\n\n// SetMode sets the mode on load and store operations.\n//\n// Returns InvalidOpCode if op is of the wrong class.\nfunc (op OpCode) SetMode(mode Mode) OpCode {\n\tif !op.Class().isLoadOrStore() || !valid(OpCode(mode), modeMask) {\n\t\treturn InvalidOpCode\n\t}\n\treturn (op & ^modeMask) | OpCode(mode)\n}\n\n// SetSize sets the size on load and store operations.\n//\n// Returns InvalidOpCode if op is of the wrong class.\nfunc (op OpCode) SetSize(size Size) OpCode {\n\tif !op.Class().isLoadOrStore() || !valid(OpCode(size), sizeMask) {\n\t\treturn InvalidOpCode\n\t}\n\treturn (op & ^sizeMask) | OpCode(size)\n}\n\nfunc (op OpCode) SetAtomicOp(atomic AtomicOp) OpCode {\n\tif op.Class() != StXClass || op.Mode() != AtomicMode || !valid(OpCode(atomic), atomicMask) {\n\t\treturn InvalidOpCode\n\t}\n\treturn (op & ^atomicMask) | OpCode(atomic)\n}\n\n// SetSource sets the source on jump and ALU operations.\n//\n// Returns InvalidOpCode if op is of the wrong class.\nfunc (op OpCode) SetSource(source Source) OpCode {\n\tif !op.Class().isJumpOrALU() || !valid(OpCode(source), sourceMask) {\n\t\treturn InvalidOpCode\n\t}\n\treturn (op & ^sourceMask) | OpCode(source)\n}\n\n// SetALUOp sets the ALUOp on ALU operations.\n//\n// Returns InvalidOpCode if op is of the wrong class.\nfunc (op OpCode) SetALUOp(alu ALUOp) OpCode {\n\tif !op.Class().IsALU() || !valid(OpCode(alu), aluMask) {\n\t\treturn InvalidOpCode\n\t}\n\treturn (op & ^aluMask) | OpCode(alu)\n}\n\n// SetJumpOp sets the JumpOp on jump operations.\n//\n// Returns InvalidOpCode if op is of the wrong class.\nfunc (op OpCode) SetJumpOp(jump JumpOp) OpCode {\n\tif !op.Class().IsJump() || !valid(OpCode(jump), jumpMask) {\n\t\treturn InvalidOpCode\n\t}\n\n\tnewOp := (op & ^jumpMask) | OpCode(jump)\n\n\t// Check newOp is legal.\n\tif newOp.JumpOp() == InvalidJumpOp {\n\t\treturn InvalidOpCode\n\t}\n\n\treturn newOp\n}\n\nfunc (op OpCode) String() string {\n\tvar f strings.Builder\n\n\tswitch class := op.Class(); {\n\tcase class.isLoadOrStore():\n\t\tf.WriteString(strings.TrimSuffix(class.String(), \"Class\"))\n\n\t\tmode := op.Mode()\n\t\tf.WriteString(strings.TrimSuffix(mode.String(), \"Mode\"))\n\n\t\tif atomic := op.AtomicOp(); atomic != InvalidAtomic {\n\t\t\tf.WriteString(strings.TrimSuffix(atomic.String(), \"Atomic\"))\n\t\t}\n\n\t\tswitch op.Size() {\n\t\tcase DWord:\n\t\t\tf.WriteString(\"DW\")\n\t\tcase Word:\n\t\t\tf.WriteString(\"W\")\n\t\tcase Half:\n\t\t\tf.WriteString(\"H\")\n\t\tcase Byte:\n\t\t\tf.WriteString(\"B\")\n\t\t}\n\n\tcase class.IsALU():\n\t\tif op.ALUOp() == Swap && op.Class() == ALU64Class {\n\t\t\t// B to make BSwap, uncontitional byte swap\n\t\t\tf.WriteString(\"B\")\n\t\t}\n\n\t\tf.WriteString(op.ALUOp().String())\n\n\t\tif op.ALUOp() == Swap {\n\t\t\tif op.Class() == ALUClass {\n\t\t\t\t// Width for Endian is controlled by Constant\n\t\t\t\tf.WriteString(op.Endianness().String())\n\t\t\t}\n\t\t} else {\n\t\t\tf.WriteString(strings.TrimSuffix(op.Source().String(), \"Source\"))\n\n\t\t\tif class == ALUClass {\n\t\t\t\tf.WriteString(\"32\")\n\t\t\t}\n\t\t}\n\n\tcase class.IsJump():\n\t\tf.WriteString(op.JumpOp().String())\n\n\t\tif class == Jump32Class {\n\t\t\tf.WriteString(\"32\")\n\t\t}\n\n\t\tif jop := op.JumpOp(); jop != Exit && jop != Call && jop != Ja {\n\t\t\tf.WriteString(strings.TrimSuffix(op.Source().String(), \"Source\"))\n\t\t}\n\n\tdefault:\n\t\tfmt.Fprintf(&f, \"OpCode(%#x)\", uint8(op))\n\t}\n\n\treturn f.String()\n}\n\n// valid returns true if all bits in value are covered by mask.\nfunc valid(value, mask OpCode) bool {\n\treturn value & ^mask == 0\n}\n"
  },
  {
    "path": "asm/opcode_string.go",
    "content": "// Code generated by \"stringer -output opcode_string.go -type=Class\"; DO NOT EDIT.\n\npackage asm\n\nimport \"strconv\"\n\nfunc _() {\n\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n\t// Re-run the stringer command to generate them again.\n\tvar x [1]struct{}\n\t_ = x[LdClass-0]\n\t_ = x[LdXClass-1]\n\t_ = x[StClass-2]\n\t_ = x[StXClass-3]\n\t_ = x[ALUClass-4]\n\t_ = x[JumpClass-5]\n\t_ = x[Jump32Class-6]\n\t_ = x[ALU64Class-7]\n}\n\nconst _Class_name = \"LdClassLdXClassStClassStXClassALUClassJumpClassJump32ClassALU64Class\"\n\nvar _Class_index = [...]uint8{0, 7, 15, 22, 30, 38, 47, 58, 68}\n\nfunc (i Class) String() string {\n\tidx := int(i) - 0\n\tif i < 0 || idx >= len(_Class_index)-1 {\n\t\treturn \"Class(\" + strconv.FormatInt(int64(i), 10) + \")\"\n\t}\n\treturn _Class_name[_Class_index[idx]:_Class_index[idx+1]]\n}\n"
  },
  {
    "path": "asm/opcode_test.go",
    "content": "package asm\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestGetSetJumpOp(t *testing.T) {\n\ttest := func(class Class, op JumpOp, valid bool) {\n\t\tt.Run(fmt.Sprintf(\"%s-%s\", class, op), func(t *testing.T) {\n\t\t\topcode := OpCode(class).SetJumpOp(op)\n\n\t\t\tif valid {\n\t\t\t\tqt.Assert(t, qt.Not(qt.Equals(opcode, InvalidOpCode)))\n\t\t\t\tqt.Assert(t, qt.Equals(opcode.JumpOp(), op))\n\t\t\t} else {\n\t\t\t\tqt.Assert(t, qt.Equals(opcode, InvalidOpCode))\n\t\t\t\tqt.Assert(t, qt.Equals(opcode.JumpOp(), InvalidJumpOp))\n\t\t\t}\n\t\t})\n\t}\n\n\t// Exit and call aren't allowed with Jump32\n\ttest(Jump32Class, Exit, false)\n\ttest(Jump32Class, Call, false)\n\n\t// But are with Jump\n\ttest(JumpClass, Exit, true)\n\ttest(JumpClass, Call, true)\n\n\t// All other ops work\n\tfor _, op := range []JumpOp{\n\t\tJa,\n\t\tJEq,\n\t\tJGT,\n\t\tJGE,\n\t\tJSet,\n\t\tJNE,\n\t\tJSGT,\n\t\tJSGE,\n\t\tJLT,\n\t\tJLE,\n\t\tJSLT,\n\t\tJSLE,\n\t} {\n\t\ttest(Jump32Class, op, true)\n\t\ttest(JumpClass, op, true)\n\t}\n}\n"
  },
  {
    "path": "asm/register.go",
    "content": "package asm\n\nimport (\n\t\"fmt\"\n)\n\n// Register is the source or destination of most operations.\ntype Register uint8\n\n// R0 contains return values.\nconst R0 Register = 0\n\n// Registers for function arguments.\nconst (\n\tR1 Register = R0 + 1 + iota\n\tR2\n\tR3\n\tR4\n\tR5\n)\n\n// Callee saved registers preserved by function calls.\nconst (\n\tR6 Register = R5 + 1 + iota\n\tR7\n\tR8\n\tR9\n)\n\n// Read-only frame pointer to access stack.\nconst (\n\tR10 Register = R9 + 1\n\tRFP          = R10\n)\n\n// Pseudo registers used by 64bit loads and jumps\nconst (\n\tPseudoMapFD     = R1 // BPF_PSEUDO_MAP_FD\n\tPseudoMapValue  = R2 // BPF_PSEUDO_MAP_VALUE\n\tPseudoCall      = R1 // BPF_PSEUDO_CALL\n\tPseudoFunc      = R4 // BPF_PSEUDO_FUNC\n\tPseudoKfuncCall = R2 // BPF_PSEUDO_KFUNC_CALL\n)\n\nfunc (r Register) String() string {\n\tv := uint8(r)\n\tif v == 10 {\n\t\treturn \"rfp\"\n\t}\n\treturn fmt.Sprintf(\"r%d\", v)\n}\n"
  },
  {
    "path": "attachtype_string.go",
    "content": "// Code generated by \"stringer -type AttachType -trimprefix Attach\"; DO NOT EDIT.\n\npackage ebpf\n\nimport \"strconv\"\n\nfunc _() {\n\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n\t// Re-run the stringer command to generate them again.\n\tvar x [1]struct{}\n\t_ = x[AttachNone-0]\n\t_ = x[AttachCGroupInetIngress-0]\n\t_ = x[AttachCGroupInetEgress-1]\n\t_ = x[AttachCGroupInetSockCreate-2]\n\t_ = x[AttachCGroupSockOps-3]\n\t_ = x[AttachSkSKBStreamParser-4]\n\t_ = x[AttachSkSKBStreamVerdict-5]\n\t_ = x[AttachCGroupDevice-6]\n\t_ = x[AttachSkMsgVerdict-7]\n\t_ = x[AttachCGroupInet4Bind-8]\n\t_ = x[AttachCGroupInet6Bind-9]\n\t_ = x[AttachCGroupInet4Connect-10]\n\t_ = x[AttachCGroupInet6Connect-11]\n\t_ = x[AttachCGroupInet4PostBind-12]\n\t_ = x[AttachCGroupInet6PostBind-13]\n\t_ = x[AttachCGroupUDP4Sendmsg-14]\n\t_ = x[AttachCGroupUDP6Sendmsg-15]\n\t_ = x[AttachLircMode2-16]\n\t_ = x[AttachFlowDissector-17]\n\t_ = x[AttachCGroupSysctl-18]\n\t_ = x[AttachCGroupUDP4Recvmsg-19]\n\t_ = x[AttachCGroupUDP6Recvmsg-20]\n\t_ = x[AttachCGroupGetsockopt-21]\n\t_ = x[AttachCGroupSetsockopt-22]\n\t_ = x[AttachTraceRawTp-23]\n\t_ = x[AttachTraceFEntry-24]\n\t_ = x[AttachTraceFExit-25]\n\t_ = x[AttachModifyReturn-26]\n\t_ = x[AttachLSMMac-27]\n\t_ = x[AttachTraceIter-28]\n\t_ = x[AttachCgroupInet4GetPeername-29]\n\t_ = x[AttachCgroupInet6GetPeername-30]\n\t_ = x[AttachCgroupInet4GetSockname-31]\n\t_ = x[AttachCgroupInet6GetSockname-32]\n\t_ = x[AttachXDPDevMap-33]\n\t_ = x[AttachCgroupInetSockRelease-34]\n\t_ = x[AttachXDPCPUMap-35]\n\t_ = x[AttachSkLookup-36]\n\t_ = x[AttachXDP-37]\n\t_ = x[AttachSkSKBVerdict-38]\n\t_ = x[AttachSkReuseportSelect-39]\n\t_ = x[AttachSkReuseportSelectOrMigrate-40]\n\t_ = x[AttachPerfEvent-41]\n\t_ = x[AttachTraceKprobeMulti-42]\n\t_ = x[AttachTraceKprobeSession-56]\n\t_ = x[AttachLSMCgroup-43]\n\t_ = x[AttachStructOps-44]\n\t_ = x[AttachNetfilter-45]\n\t_ = x[AttachTCXIngress-46]\n\t_ = x[AttachTCXEgress-47]\n\t_ = x[AttachTraceUprobeMulti-48]\n\t_ = x[AttachCgroupUnixConnect-49]\n\t_ = x[AttachCgroupUnixSendmsg-50]\n\t_ = x[AttachCgroupUnixRecvmsg-51]\n\t_ = x[AttachCgroupUnixGetpeername-52]\n\t_ = x[AttachCgroupUnixGetsockname-53]\n\t_ = x[AttachNetkitPrimary-54]\n\t_ = x[AttachNetkitPeer-55]\n\t_ = x[AttachWindowsXDP-268435457]\n\t_ = x[AttachWindowsBind-268435458]\n\t_ = x[AttachWindowsCGroupInet4Connect-268435459]\n\t_ = x[AttachWindowsCGroupInet6Connect-268435460]\n\t_ = x[AttachWindowsCgroupInet4RecvAccept-268435461]\n\t_ = x[AttachWindowsCgroupInet6RecvAccept-268435462]\n\t_ = x[AttachWindowsCGroupSockOps-268435463]\n\t_ = x[AttachWindowsSample-268435464]\n\t_ = x[AttachWindowsXDPTest-268435465]\n}\n\nconst (\n\t_AttachType_name_0 = \"NoneCGroupInetEgressCGroupInetSockCreateCGroupSockOpsSkSKBStreamParserSkSKBStreamVerdictCGroupDeviceSkMsgVerdictCGroupInet4BindCGroupInet6BindCGroupInet4ConnectCGroupInet6ConnectCGroupInet4PostBindCGroupInet6PostBindCGroupUDP4SendmsgCGroupUDP6SendmsgLircMode2FlowDissectorCGroupSysctlCGroupUDP4RecvmsgCGroupUDP6RecvmsgCGroupGetsockoptCGroupSetsockoptTraceRawTpTraceFEntryTraceFExitModifyReturnLSMMacTraceIterCgroupInet4GetPeernameCgroupInet6GetPeernameCgroupInet4GetSocknameCgroupInet6GetSocknameXDPDevMapCgroupInetSockReleaseXDPCPUMapSkLookupXDPSkSKBVerdictSkReuseportSelectSkReuseportSelectOrMigratePerfEventTraceKprobeMultiLSMCgroupStructOpsNetfilterTCXIngressTCXEgressTraceUprobeMultiCgroupUnixConnectCgroupUnixSendmsgCgroupUnixRecvmsgCgroupUnixGetpeernameCgroupUnixGetsocknameNetkitPrimaryNetkitPeerTraceKprobeSession\"\n\t_AttachType_name_1 = \"WindowsXDPWindowsBindWindowsCGroupInet4ConnectWindowsCGroupInet6ConnectWindowsCgroupInet4RecvAcceptWindowsCgroupInet6RecvAcceptWindowsCGroupSockOpsWindowsSampleWindowsXDPTest\"\n)\n\nvar (\n\t_AttachType_index_0 = [...]uint16{0, 4, 20, 40, 53, 70, 88, 100, 112, 127, 142, 160, 178, 197, 216, 233, 250, 259, 272, 284, 301, 318, 334, 350, 360, 371, 381, 393, 399, 408, 430, 452, 474, 496, 505, 526, 535, 543, 546, 558, 575, 601, 610, 626, 635, 644, 653, 663, 672, 688, 705, 722, 739, 760, 781, 794, 804, 822}\n\t_AttachType_index_1 = [...]uint8{0, 10, 21, 46, 71, 99, 127, 147, 160, 174}\n)\n\nfunc (i AttachType) String() string {\n\tswitch {\n\tcase i <= 56:\n\t\treturn _AttachType_name_0[_AttachType_index_0[i]:_AttachType_index_0[i+1]]\n\tcase 268435457 <= i && i <= 268435465:\n\t\ti -= 268435457\n\t\treturn _AttachType_name_1[_AttachType_index_1[i]:_AttachType_index_1[i+1]]\n\tdefault:\n\t\treturn \"AttachType(\" + strconv.FormatInt(int64(i), 10) + \")\"\n\t}\n}\n"
  },
  {
    "path": "btf/btf.go",
    "content": "package btf\n\nimport (\n\t\"debug/elf\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"iter\"\n\t\"maps\"\n\t\"math\"\n\t\"os\"\n\t\"reflect\"\n\t\"slices\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n)\n\nconst btfMagic = 0xeB9F\n\n// Errors returned by BTF functions.\nvar (\n\tErrNotSupported    = internal.ErrNotSupported\n\tErrNotFound        = errors.New(\"not found\")\n\tErrNoExtendedInfo  = errors.New(\"no extended info\")\n\tErrMultipleMatches = errors.New(\"multiple matching types\")\n)\n\n// ID represents the unique ID of a BTF object.\ntype ID = sys.BTFID\n\ntype elfData struct {\n\tsectionSizes  map[string]uint32\n\tsymbolOffsets map[elfSymbol]uint32\n\tfixups        map[Type]bool\n}\n\ntype elfSymbol struct {\n\tsection string\n\tname    string\n}\n\n// Spec allows querying a set of Types and loading the set into the\n// kernel.\ntype Spec struct {\n\t*decoder\n\n\t// Additional data from ELF, may be nil.\n\telf *elfData\n}\n\n// LoadSpec opens file and calls LoadSpecFromReader on it.\nfunc LoadSpec(file string) (*Spec, error) {\n\tfh, err := os.Open(file)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer fh.Close()\n\n\treturn LoadSpecFromReader(fh)\n}\n\n// LoadSpecFromReader reads from an ELF or a raw BTF blob.\n//\n// Returns ErrNotFound if reading from an ELF which contains no BTF. ExtInfos\n// may be nil.\nfunc LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) {\n\tfile, err := internal.NewSafeELFFile(rd)\n\tif err != nil {\n\t\traw, err := io.ReadAll(io.NewSectionReader(rd, 0, math.MaxInt64))\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"read raw BTF: %w\", err)\n\t\t}\n\n\t\treturn loadRawSpec(raw, nil)\n\t}\n\n\treturn loadSpecFromELF(file)\n}\n\n// LoadSpecAndExtInfosFromReader reads from an ELF.\n//\n// ExtInfos may be nil if the ELF doesn't contain section metadata.\n// Returns ErrNotFound if the ELF contains no BTF.\nfunc LoadSpecAndExtInfosFromReader(rd io.ReaderAt) (*Spec, *ExtInfos, error) {\n\tfile, err := internal.NewSafeELFFile(rd)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tspec, err := loadSpecFromELF(file)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\textInfos, err := loadExtInfosFromELF(file, spec)\n\tif err != nil && !errors.Is(err, ErrNotFound) {\n\t\treturn nil, nil, err\n\t}\n\n\treturn spec, extInfos, nil\n}\n\n// symbolOffsets extracts all symbols offsets from an ELF and indexes them by\n// section and variable name.\n//\n// References to variables in BTF data sections carry unsigned 32-bit offsets.\n// Some ELF symbols (e.g. in vmlinux) may point to virtual memory that is well\n// beyond this range. Since these symbols cannot be described by BTF info,\n// ignore them here.\nfunc symbolOffsets(file *internal.SafeELFFile) (map[elfSymbol]uint32, error) {\n\tsymbols, err := file.Symbols()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't read symbols: %v\", err)\n\t}\n\n\toffsets := make(map[elfSymbol]uint32)\n\tfor _, sym := range symbols {\n\t\tif idx := sym.Section; idx >= elf.SHN_LORESERVE && idx <= elf.SHN_HIRESERVE {\n\t\t\t// Ignore things like SHN_ABS\n\t\t\tcontinue\n\t\t}\n\n\t\tif sym.Value > math.MaxUint32 {\n\t\t\t// VarSecinfo offset is u32, cannot reference symbols in higher regions.\n\t\t\tcontinue\n\t\t}\n\n\t\tif int(sym.Section) >= len(file.Sections) {\n\t\t\treturn nil, fmt.Errorf(\"symbol %s: invalid section %d\", sym.Name, sym.Section)\n\t\t}\n\n\t\tsecName := file.Sections[sym.Section].Name\n\t\toffsets[elfSymbol{secName, sym.Name}] = uint32(sym.Value)\n\t}\n\n\treturn offsets, nil\n}\n\nfunc loadSpecFromELF(file *internal.SafeELFFile) (*Spec, error) {\n\tvar (\n\t\tbtfSection   *elf.Section\n\t\tsectionSizes = make(map[string]uint32)\n\t)\n\n\tfor _, sec := range file.Sections {\n\t\tswitch sec.Name {\n\t\tcase \".BTF\":\n\t\t\tbtfSection = sec\n\t\tdefault:\n\t\t\tif sec.Type != elf.SHT_PROGBITS && sec.Type != elf.SHT_NOBITS {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tif sec.Size > math.MaxUint32 {\n\t\t\t\treturn nil, fmt.Errorf(\"section %s exceeds maximum size\", sec.Name)\n\t\t\t}\n\n\t\t\tsectionSizes[sec.Name] = uint32(sec.Size)\n\t\t}\n\t}\n\n\tif btfSection == nil {\n\t\treturn nil, fmt.Errorf(\"btf: %w\", ErrNotFound)\n\t}\n\n\toffsets, err := symbolOffsets(file)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\trawBTF, err := btfSection.Data()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"reading .BTF section: %w\", err)\n\t}\n\n\tspec, err := loadRawSpec(rawBTF, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif spec.decoder.byteOrder != file.ByteOrder {\n\t\treturn nil, fmt.Errorf(\"BTF byte order %s does not match ELF byte order %s\", spec.decoder.byteOrder, file.ByteOrder)\n\t}\n\n\tspec.elf = &elfData{\n\t\tsectionSizes,\n\t\toffsets,\n\t\tmake(map[Type]bool),\n\t}\n\n\treturn spec, nil\n}\n\nfunc loadRawSpec(btf []byte, base *Spec) (*Spec, error) {\n\tvar (\n\t\tbaseDecoder *decoder\n\t\tbaseStrings *stringTable\n\t\terr         error\n\t)\n\n\tif base != nil {\n\t\tbaseDecoder = base.decoder\n\t\tbaseStrings = base.strings\n\t}\n\n\theader, bo, err := parseBTFHeader(btf)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"parsing .BTF header: %v\", err)\n\t}\n\n\tif header.HdrLen > uint32(len(btf)) {\n\t\treturn nil, fmt.Errorf(\"BTF header length is out of bounds\")\n\t}\n\tbtf = btf[header.HdrLen:]\n\n\tif int(header.StringOff+header.StringLen) > len(btf) {\n\t\treturn nil, fmt.Errorf(\"string table is out of bounds\")\n\t}\n\tstringsSection := btf[header.StringOff : header.StringOff+header.StringLen]\n\n\trawStrings, err := newStringTable(stringsSection, baseStrings)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read string section: %w\", err)\n\t}\n\n\tif int(header.TypeOff+header.TypeLen) > len(btf) {\n\t\treturn nil, fmt.Errorf(\"types section is out of bounds\")\n\t}\n\ttypesSection := btf[header.TypeOff : header.TypeOff+header.TypeLen]\n\n\tdecoder, err := newDecoder(typesSection, bo, rawStrings, baseDecoder)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &Spec{decoder, nil}, nil\n}\n\n// fixupDatasec attempts to patch up missing info in Datasecs and its members by\n// supplementing them with information from the ELF headers and symbol table.\nfunc (elf *elfData) fixupDatasec(typ Type) error {\n\tif elf == nil {\n\t\treturn nil\n\t}\n\n\tif ds, ok := typ.(*Datasec); ok {\n\t\tif elf.fixups[ds] {\n\t\t\treturn nil\n\t\t}\n\t\telf.fixups[ds] = true\n\n\t\tname := ds.Name\n\n\t\t// Some Datasecs are virtual and don't have corresponding ELF sections.\n\t\tswitch name {\n\t\tcase \".ksyms\":\n\t\t\t// .ksyms describes forward declarations of kfunc signatures, as well as\n\t\t\t// references to kernel symbols.\n\t\t\t// Nothing to fix up, all sizes and offsets are 0.\n\t\t\tfor _, vsi := range ds.Vars {\n\t\t\t\tswitch t := vsi.Type.(type) {\n\t\t\t\tcase *Func:\n\t\t\t\t\tcontinue\n\t\t\t\tcase *Var:\n\t\t\t\t\tif _, ok := t.Type.(*Void); !ok {\n\t\t\t\t\t\treturn fmt.Errorf(\"data section %s: expected %s to be *Void, not %T: %w\", name, vsi.Type.TypeName(), vsi.Type, ErrNotSupported)\n\t\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\treturn fmt.Errorf(\"data section %s: expected to be either *btf.Func or *btf.Var, not %T: %w\", name, vsi.Type, ErrNotSupported)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn nil\n\t\tcase \".kconfig\":\n\t\t\t// .kconfig has a size of 0 and has all members' offsets set to 0.\n\t\t\t// Fix up all offsets and set the Datasec's size.\n\t\t\tif err := fixupDatasecLayout(ds); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\t// Fix up extern to global linkage to avoid a BTF verifier error.\n\t\t\tfor _, vsi := range ds.Vars {\n\t\t\t\tvsi.Type.(*Var).Linkage = GlobalVar\n\t\t\t}\n\n\t\t\treturn nil\n\t\t}\n\n\t\tif ds.Size != 0 {\n\t\t\treturn nil\n\t\t}\n\n\t\tds.Size, ok = elf.sectionSizes[name]\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"data section %s: missing size\", name)\n\t\t}\n\n\t\tfor i := range ds.Vars {\n\t\t\tsymName := ds.Vars[i].Type.TypeName()\n\t\t\tds.Vars[i].Offset, ok = elf.symbolOffsets[elfSymbol{name, symName}]\n\t\t\tif !ok {\n\t\t\t\treturn fmt.Errorf(\"data section %s: missing offset for symbol %s\", name, symName)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// fixupDatasecLayout populates ds.Vars[].Offset according to var sizes and\n// alignment. Calculate and set ds.Size.\nfunc fixupDatasecLayout(ds *Datasec) error {\n\tvar off uint32\n\n\tfor i, vsi := range ds.Vars {\n\t\tv, ok := vsi.Type.(*Var)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"member %d: unsupported type %T\", i, vsi.Type)\n\t\t}\n\n\t\tsize, err := Sizeof(v.Type)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"variable %s: getting size: %w\", v.Name, err)\n\t\t}\n\t\talign, err := alignof(v.Type)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"variable %s: getting alignment: %w\", v.Name, err)\n\t\t}\n\n\t\t// Align the current member based on the offset of the end of the previous\n\t\t// member and the alignment of the current member.\n\t\toff = internal.Align(off, uint32(align))\n\n\t\tds.Vars[i].Offset = off\n\n\t\toff += uint32(size)\n\t}\n\n\tds.Size = off\n\n\treturn nil\n}\n\n// Copy a Spec.\n//\n// All contained types are duplicated while preserving any modifications made\n// to them.\nfunc (s *Spec) Copy() *Spec {\n\tif s == nil {\n\t\treturn nil\n\t}\n\n\tcpy := &Spec{\n\t\ts.decoder.Copy(),\n\t\tnil,\n\t}\n\n\tif s.elf != nil {\n\t\tcpy.elf = &elfData{\n\t\t\ts.elf.sectionSizes,\n\t\t\ts.elf.symbolOffsets,\n\t\t\tmaps.Clone(s.elf.fixups),\n\t\t}\n\t}\n\n\treturn cpy\n}\n\n// TypeByID returns the BTF Type with the given type ID.\n//\n// Returns an error wrapping ErrNotFound if a Type with the given ID\n// does not exist in the Spec.\nfunc (s *Spec) TypeByID(id TypeID) (Type, error) {\n\ttyp, err := s.decoder.TypeByID(id)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"inflate type: %w\", err)\n\t}\n\n\tif err := s.elf.fixupDatasec(typ); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn typ, nil\n}\n\n// TypeID returns the ID for a given Type.\n//\n// Returns an error wrapping [ErrNotFound] if the type isn't part of the Spec.\nfunc (s *Spec) TypeID(typ Type) (TypeID, error) {\n\treturn s.decoder.TypeID(typ)\n}\n\n// AnyTypesByName returns a list of BTF Types with the given name.\n//\n// If the BTF blob describes multiple compilation units like vmlinux, multiple\n// Types with the same name and kind can exist, but might not describe the same\n// data structure.\n//\n// Returns an error wrapping ErrNotFound if no matching Type exists in the Spec.\nfunc (s *Spec) AnyTypesByName(name string) ([]Type, error) {\n\ttypes, err := s.TypesByName(newEssentialName(name))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor i := 0; i < len(types); i++ {\n\t\t// Match against the full name, not just the essential one\n\t\t// in case the type being looked up is a struct flavor.\n\t\tif types[i].TypeName() != name {\n\t\t\ttypes = slices.Delete(types, i, i+1)\n\t\t\tcontinue\n\t\t}\n\n\t\tif err := s.elf.fixupDatasec(types[i]); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn types, nil\n}\n\n// AnyTypeByName returns a Type with the given name.\n//\n// Returns an error if multiple types of that name exist.\nfunc (s *Spec) AnyTypeByName(name string) (Type, error) {\n\ttypes, err := s.AnyTypesByName(name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif len(types) > 1 {\n\t\treturn nil, fmt.Errorf(\"found multiple types: %v\", types)\n\t}\n\n\treturn types[0], nil\n}\n\n// TypeByName searches for a Type with a specific name. Since multiple Types\n// with the same name can exist, the parameter typ is taken to narrow down the\n// search in case of a clash.\n//\n// typ must be a non-nil pointer to an implementation of a Type. On success, the\n// address of the found Type will be copied to typ.\n//\n// Returns an error wrapping ErrNotFound if no matching Type exists in the Spec.\n// Returns an error wrapping ErrMultipleTypes if multiple candidates are found.\nfunc (s *Spec) TypeByName(name string, typ interface{}) error {\n\ttypeInterface := reflect.TypeOf((*Type)(nil)).Elem()\n\n\t// typ may be **T or *Type\n\ttypValue := reflect.ValueOf(typ)\n\tif typValue.Kind() != reflect.Ptr {\n\t\treturn fmt.Errorf(\"%T is not a pointer\", typ)\n\t}\n\n\ttypPtr := typValue.Elem()\n\tif !typPtr.CanSet() {\n\t\treturn fmt.Errorf(\"%T cannot be set\", typ)\n\t}\n\n\twanted := typPtr.Type()\n\tif wanted == typeInterface {\n\t\t// This is *Type. Unwrap the value's type.\n\t\twanted = typPtr.Elem().Type()\n\t}\n\n\tif !wanted.AssignableTo(typeInterface) {\n\t\treturn fmt.Errorf(\"%T does not satisfy Type interface\", typ)\n\t}\n\n\ttypes, err := s.AnyTypesByName(name)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar candidate Type\n\tfor _, typ := range types {\n\t\tif reflect.TypeOf(typ) != wanted {\n\t\t\tcontinue\n\t\t}\n\n\t\tif candidate != nil {\n\t\t\treturn fmt.Errorf(\"type %s(%T): %w\", name, typ, ErrMultipleMatches)\n\t\t}\n\n\t\tcandidate = typ\n\t}\n\n\tif candidate == nil {\n\t\treturn fmt.Errorf(\"%s %s: %w\", wanted, name, ErrNotFound)\n\t}\n\n\ttypPtr.Set(reflect.ValueOf(candidate))\n\n\treturn nil\n}\n\n// LoadSplitSpec loads split BTF from the given file.\n//\n// Types from base are used to resolve references in the split BTF.\n// The returned Spec only contains types from the split BTF, not from the base.\nfunc LoadSplitSpec(file string, base *Spec) (*Spec, error) {\n\tfh, err := os.Open(file)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer fh.Close()\n\n\treturn LoadSplitSpecFromReader(fh, base)\n}\n\n// LoadSplitSpecFromReader loads split BTF from a reader.\n//\n// Types from base are used to resolve references in the split BTF.\n// The returned Spec only contains types from the split BTF, not from the base.\nfunc LoadSplitSpecFromReader(r io.ReaderAt, base *Spec) (*Spec, error) {\n\traw, err := io.ReadAll(io.NewSectionReader(r, 0, math.MaxInt64))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read raw BTF: %w\", err)\n\t}\n\n\treturn loadRawSpec(raw, base)\n}\n\n// All iterates over all types.\nfunc (s *Spec) All() iter.Seq2[Type, error] {\n\treturn func(yield func(Type, error) bool) {\n\t\tfor id := s.firstTypeID; ; id++ {\n\t\t\ttyp, err := s.TypeByID(id)\n\t\t\tif errors.Is(err, ErrNotFound) {\n\t\t\t\treturn\n\t\t\t} else if err != nil {\n\t\t\t\tyield(nil, err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Skip declTags, during unmarshaling declTags become `Tags` fields of other types.\n\t\t\t// We keep them in the spec to avoid holes in the ID space, but for the purposes of\n\t\t\t// iteration, they are not useful to the user.\n\t\t\tif _, ok := typ.(*declTag); ok {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif !yield(typ, nil) {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "btf/btf_test.go",
    "content": "package btf\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"runtime\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc vmlinuxSpec(tb testing.TB) *Spec {\n\ttb.Helper()\n\n\t// /sys/kernel/btf was introduced in 341dfcf8d78e (\"btf: expose BTF info\n\t// through sysfs\"), which shipped in Linux 5.4.\n\tif _, err := os.Stat(\"/sys/kernel/btf/vmlinux\"); errors.Is(err, fs.ErrNotExist) {\n\t\ttb.Skip(\"No /sys/kernel/btf/vmlinux\")\n\t}\n\n\tspec, err := LoadKernelSpec()\n\tif err != nil {\n\t\ttb.Fatal(err)\n\t}\n\treturn spec\n}\n\ntype specAndRawBTF struct {\n\traw  []byte\n\tspec *Spec\n}\n\nvar vmlinuxTestdata = sync.OnceValues(func() (specAndRawBTF, error) {\n\tb, err := internal.ReadAllCompressed(\"testdata/vmlinux.btf.gz\")\n\tif err != nil {\n\t\treturn specAndRawBTF{}, err\n\t}\n\n\tspec, err := loadRawSpec(b, nil)\n\tif err != nil {\n\t\treturn specAndRawBTF{}, err\n\t}\n\n\treturn specAndRawBTF{b, spec}, nil\n})\n\nfunc vmlinuxTestdataSpec(tb testing.TB) *Spec {\n\ttb.Helper()\n\n\ttd, err := vmlinuxTestdata()\n\tif err != nil {\n\t\ttb.Fatal(err)\n\t}\n\n\treturn td.spec.Copy()\n}\n\nfunc vmlinuxTestdataBytes(tb testing.TB) []byte {\n\ttb.Helper()\n\n\ttd, err := vmlinuxTestdata()\n\tif err != nil {\n\t\ttb.Fatal(err)\n\t}\n\n\treturn td.raw\n}\n\nfunc parseELFBTF(tb testing.TB, file string) *Spec {\n\ttb.Helper()\n\n\tspec, err := LoadSpec(file)\n\tif err != nil {\n\t\ttb.Fatal(\"Can't load BTF:\", err)\n\t}\n\n\treturn spec\n}\n\nfunc TestAnyTypesByName(t *testing.T) {\n\ttestutils.Files(t, testutils.Glob(t, \"testdata/relocs-*.elf\"), func(t *testing.T, file string) {\n\t\tspec := parseELFBTF(t, file)\n\n\t\ttypes, err := spec.AnyTypesByName(\"ambiguous\")\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tif len(types) != 1 {\n\t\t\tt.Fatalf(\"expected to receive exactly 1 types from querying ambiguous type, got: %v\", types)\n\t\t}\n\n\t\ttypes, err = spec.AnyTypesByName(\"ambiguous___flavour\")\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tif len(types) != 1 {\n\t\t\tt.Fatalf(\"expected to receive exactly 1 type from querying ambiguous flavour, got: %v\", types)\n\t\t}\n\t})\n}\n\nfunc TestTypeByNameAmbiguous(t *testing.T) {\n\ttestutils.Files(t, testutils.Glob(t, \"testdata/relocs-*.elf\"), func(t *testing.T, file string) {\n\t\tspec := parseELFBTF(t, file)\n\n\t\tvar typ *Struct\n\t\tif err := spec.TypeByName(\"ambiguous\", &typ); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tif name := typ.TypeName(); name != \"ambiguous\" {\n\t\t\tt.Fatal(\"expected type name 'ambiguous', got:\", name)\n\t\t}\n\n\t\tif err := spec.TypeByName(\"ambiguous___flavour\", &typ); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tif name := typ.TypeName(); name != \"ambiguous___flavour\" {\n\t\t\tt.Fatal(\"expected type name 'ambiguous___flavour', got:\", name)\n\t\t}\n\t})\n}\n\nfunc TestTypeByName(t *testing.T) {\n\tspec := vmlinuxTestdataSpec(t)\n\n\tfor _, typ := range []interface{}{\n\t\tnil,\n\t\tStruct{},\n\t\t&Struct{},\n\t\t[]Struct{},\n\t\t&[]Struct{},\n\t\tmap[int]Struct{},\n\t\t&map[int]Struct{},\n\t\tint(0),\n\t\tnew(int),\n\t} {\n\t\tt.Run(fmt.Sprintf(\"%T\", typ), func(t *testing.T) {\n\t\t\t// spec.TypeByName MUST fail if typ is a nil btf.Type.\n\t\t\tif err := spec.TypeByName(\"iphdr\", typ); err == nil {\n\t\t\t\tt.Fatalf(\"TypeByName does not fail with type %T\", typ)\n\t\t\t}\n\t\t})\n\t}\n\n\t// spec.TypeByName MUST return the same address for multiple calls with the same type name.\n\tvar iphdr1, iphdr2 *Struct\n\tif err := spec.TypeByName(\"iphdr\", &iphdr1); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif err := spec.TypeByName(\"iphdr\", &iphdr2); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif iphdr1 != iphdr2 {\n\t\tt.Fatal(\"multiple TypeByName calls for `iphdr` name do not return the same addresses\")\n\t}\n\n\t// It's valid to pass a *Type to TypeByName.\n\ttyp := Type(iphdr2)\n\tif err := spec.TypeByName(\"iphdr\", &typ); err != nil {\n\t\tt.Fatal(\"Can't look up using *Type:\", err)\n\t}\n\n\t// Excerpt from linux/ip.h, https://elixir.bootlin.com/linux/latest/A/ident/iphdr\n\t//\n\t// struct iphdr {\n\t// #if defined(__LITTLE_ENDIAN_BITFIELD)\n\t//     __u8 ihl:4, version:4;\n\t// #elif defined (__BIG_ENDIAN_BITFIELD)\n\t//     __u8 version:4, ihl:4;\n\t// #else\n\t//     ...\n\t// }\n\t//\n\t// The BTF we test against is for little endian.\n\tm := iphdr1.Members[1]\n\tif m.Name != \"version\" {\n\t\tt.Fatal(\"Expected version as the second member, got\", m.Name)\n\t}\n\ttd, ok := m.Type.(*Typedef)\n\tif !ok {\n\t\tt.Fatalf(\"version member of iphdr should be a __u8 typedef: actual: %T\", m.Type)\n\t}\n\tu8, ok := td.Type.(*Int)\n\tif !ok {\n\t\tt.Fatalf(\"__u8 typedef should point to an Int type: actual: %T\", td.Type)\n\t}\n\tif m.BitfieldSize != 4 {\n\t\tt.Fatalf(\"incorrect bitfield size: expected: 4 actual: %d\", m.BitfieldSize)\n\t}\n\tif u8.Encoding != 0 {\n\t\tt.Fatalf(\"incorrect encoding of an __u8 int: expected: 0 actual: %x\", u8.Encoding)\n\t}\n\tif m.Offset != 4 {\n\t\tt.Fatalf(\"incorrect bitfield offset: expected: 4 actual: %d\", m.Offset)\n\t}\n}\n\nfunc BenchmarkParseVmlinux(b *testing.B) {\n\tvmlinux := vmlinuxTestdataBytes(b)\n\tb.ReportAllocs()\n\n\tfor b.Loop() {\n\t\tif _, err := loadRawSpec(vmlinux, nil); err != nil {\n\t\t\tb.Fatal(\"Can't load BTF:\", err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkIterateVmlinux(b *testing.B) {\n\tvmlinux := vmlinuxTestdataBytes(b)\n\tb.ReportAllocs()\n\n\tfor b.Loop() {\n\t\tspec, err := loadRawSpec(vmlinux, nil)\n\t\tif err != nil {\n\t\t\tb.Fatal(\"Can't load BTF:\", err)\n\t\t}\n\n\t\tfor range spec.All() {\n\t\t}\n\t}\n}\n\nfunc TestParseCurrentKernelBTF(t *testing.T) {\n\tspec := vmlinuxSpec(t)\n\n\tif len(spec.offsets) == 0 {\n\t\tt.Fatal(\"Empty kernel BTF\")\n\t}\n}\n\nfunc TestFindVMLinux(t *testing.T) {\n\tfile, err := findVMLinux()\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(\"Can't find vmlinux:\", err)\n\t}\n\tdefer file.Close()\n\n\tspec, err := LoadSpecFromReader(file)\n\tif err != nil {\n\t\tt.Fatal(\"Can't load BTF:\", err)\n\t}\n\n\tif len(spec.offsets) == 0 {\n\t\tt.Fatal(\"Empty kernel BTF\")\n\t}\n}\n\nfunc TestLoadSpecFromElf(t *testing.T) {\n\ttestutils.Files(t, testutils.Glob(t, \"../testdata/loader-e*.elf\"), func(t *testing.T, file string) {\n\t\tspec := parseELFBTF(t, file)\n\n\t\tvt, err := spec.TypeByID(0)\n\t\tif err != nil {\n\t\t\tt.Error(\"Can't retrieve void type by ID:\", err)\n\t\t}\n\t\tif _, ok := vt.(*Void); !ok {\n\t\t\tt.Errorf(\"Expected Void for type id 0, but got: %T\", vt)\n\t\t}\n\n\t\tvar bpfMapDef *Struct\n\t\tif err := spec.TypeByName(\"bpf_map_def\", &bpfMapDef); err != nil {\n\t\t\tt.Error(\"Can't find bpf_map_def:\", err)\n\t\t}\n\n\t\tvar tmp *Void\n\t\tif err := spec.TypeByName(\"totally_bogus_type\", &tmp); !errors.Is(err, ErrNotFound) {\n\t\t\tt.Error(\"TypeByName doesn't return ErrNotFound:\", err)\n\t\t}\n\n\t\tvar fn *Func\n\t\tif err := spec.TypeByName(\"global_fn\", &fn); err != nil {\n\t\t\tt.Error(\"Can't find global_fn():\", err)\n\t\t} else {\n\t\t\tif fn.Linkage != GlobalFunc {\n\t\t\t\tt.Error(\"Expected global linkage:\", fn)\n\t\t\t}\n\t\t}\n\n\t\tvar v *Var\n\t\tif err := spec.TypeByName(\"key3\", &v); err != nil {\n\t\t\tt.Error(\"Can't find key3:\", err)\n\t\t} else {\n\t\t\tif v.Linkage != GlobalVar {\n\t\t\t\tt.Error(\"Expected global linkage:\", v)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestVerifierError(t *testing.T) {\n\tb, err := NewBuilder([]Type{&Int{Encoding: 255}}, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\t_, err = NewHandle(b)\n\ttestutils.SkipIfNotSupported(t, err)\n\tvar ve *internal.VerifierError\n\tif !errors.As(err, &ve) {\n\t\tt.Fatalf(\"expected a VerifierError, got: %v\", err)\n\t}\n}\n\nfunc TestSpecCopy(t *testing.T) {\n\tqt.Check(t, qt.IsNil((*Spec)(nil).Copy()))\n\n\tspec := parseELFBTF(t, \"../testdata/loader-el.elf\")\n\tcpy := spec.Copy()\n\n\thave := typesFromSpec(t, spec)\n\tqt.Assert(t, qt.IsTrue(len(have) > 0))\n\n\twant := typesFromSpec(t, cpy)\n\tqt.Assert(t, qt.HasLen(want, len(have)))\n\n\tfor i := range want {\n\t\tif _, ok := have[i].(*Void); ok {\n\t\t\t// Since Void is an empty struct, a Type interface value containing\n\t\t\t// &Void{} stores (*Void, nil). Since interface equality first compares\n\t\t\t// the type and then the concrete value, Void is always equal.\n\t\t\tcontinue\n\t\t}\n\n\t\tif have[i] == want[i] {\n\t\t\tt.Fatalf(\"Type at index %d is not a copy: %T == %T\", i, have[i], want[i])\n\t\t}\n\t}\n}\n\nfunc TestSpecCopyModifications(t *testing.T) {\n\tspec := specFromTypes(t, []Type{&Int{Name: \"a\", Size: 4}})\n\n\ttyp, err := spec.TypeByID(1)\n\tqt.Assert(t, qt.IsNil(err))\n\n\ti := typ.(*Int)\n\ti.Name = \"b\"\n\ti.Size = 2\n\n\tcpy := spec.Copy()\n\ttyp2, err := cpy.TypeByID(1)\n\tqt.Assert(t, qt.IsNil(err))\n\ti2 := typ2.(*Int)\n\n\tqt.Assert(t, qt.Not(qt.Equals(i2, i)), qt.Commentf(\"Types are distinct\"))\n\tqt.Assert(t, qt.DeepEquals(i2, i), qt.Commentf(\"Modifications are preserved\"))\n\n\ti.Name = \"bar\"\n\tqt.Assert(t, qt.Equals(i2.Name, \"b\"))\n}\n\nfunc TestSpecTypeByID(t *testing.T) {\n\tspec := specFromTypes(t, nil)\n\n\t_, err := spec.TypeByID(0)\n\tqt.Assert(t, qt.IsNil(err))\n\n\t_, err = spec.TypeByID(1)\n\tqt.Assert(t, qt.ErrorIs(err, ErrNotFound))\n}\n\nfunc ExampleSpec_TypeByName() {\n\t// Acquire a Spec via one of its constructors.\n\tspec := new(Spec)\n\n\t// Declare a variable of the desired type\n\tvar foo *Struct\n\n\tif err := spec.TypeByName(\"foo\", &foo); err != nil {\n\t\t// There is no struct with name foo, or there\n\t\t// are multiple possibilities.\n\t}\n\n\t// We've found struct foo\n\tfmt.Println(foo.Name)\n}\n\nfunc TestTypesIterator(t *testing.T) {\n\ttypes := []Type{(*Void)(nil), &Int{Size: 4}, &Int{Size: 2}}\n\n\tb, err := NewBuilder(types[1:], nil)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\traw, err := b.Marshal(nil, nil)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tspec, err := LoadSpecFromReader(bytes.NewReader(raw))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tvar have []Type\n\tfor typ, err := range spec.All() {\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\thave = append(have, typ)\n\t}\n\n\tqt.Assert(t, qt.DeepEquals(have, types))\n}\n\nfunc TestLoadSplitSpec(t *testing.T) {\n\tspec, err := LoadSpec(\"testdata/btf_testmod.btf.base\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tsplitSpec, err := LoadSplitSpec(\"testdata/btf_testmod.btf\", spec)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tvar fnType *Func\n\tqt.Assert(t, qt.IsNil(splitSpec.TypeByName(\"bpf_testmod_init\", &fnType)))\n\ttypeID, err := splitSpec.TypeID(fnType)\n\tqt.Assert(t, qt.IsNil(err))\n\n\ttypeByID, err := splitSpec.TypeByID(typeID)\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.Equals(typeByID, Type(fnType)))\n\n\tfnProto := fnType.Type.(*FuncProto)\n\t// 'int' is defined in the base BTF...\n\tintType, err := spec.AnyTypeByName(\"int\")\n\tqt.Assert(t, qt.IsNil(err))\n\n\t// ... but not in the split BTF\n\t_, err = splitSpec.AnyTypeByName(\"int\")\n\tqt.Assert(t, qt.ErrorIs(err, ErrNotFound))\n\n\tqt.Assert(t, qt.Equals(fnProto.Return, intType),\n\t\tqt.Commentf(\"types found in base of split spec should be reused\"))\n\n\tfnProto.Params = []FuncParam{{\"a\", &Pointer{(*Void)(nil)}}}\n\n\t// The behaviour of copying a split spec is quite subtle. When initially\n\t// creating a split spec, types in the split base are shared. This allows\n\t// amortising the cost of decoding vmlinux.\n\t//\n\t// However, we currently define copying a spec to be like forking a process:\n\t// in-memory changes to types are preserved. After the copy finished we have\n\t// two fully independent states.\n\t//\n\t// For split BTF this means that we also need to copy the base and ensure\n\t// that future references to a modified type work correctly.\n\tsplitSpecCopy := splitSpec.Copy()\n\n\tvar fnCopyType *Func\n\tqt.Assert(t, qt.IsNil(splitSpecCopy.TypeByName(\"bpf_testmod_init\", &fnCopyType)))\n\tqt.Assert(t, testutils.IsDeepCopy(fnCopyType, fnType))\n\n\t// Pull out a second type which refers to \"int\" in the base, but which hasn't\n\t// been inflated yet. This forces inflating int from the base.\n\tvar str *Struct\n\tqt.Assert(t, qt.IsNil(splitSpecCopy.TypeByName(\"bpf_testmod_struct_arg_1\", &str)))\n\n\t// Ensure that the int types are indeed the same.\n\tqt.Assert(t, qt.Equals(str.Members[0].Type, fnCopyType.Type.(*FuncProto).Return))\n\n\tcopyTypeID, err := splitSpecCopy.TypeID(fnCopyType)\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.Equals(copyTypeID, typeID), qt.Commentf(\"ID of copied type must match\"))\n}\n\nfunc TestFixupDatasecLayout(t *testing.T) {\n\tds := &Datasec{\n\t\tSize: 0, // Populated by fixup.\n\t\tVars: []VarSecinfo{\n\t\t\t{Type: &Var{Type: &Int{Size: 4}}},\n\t\t\t{Type: &Var{Type: &Int{Size: 1}}},\n\t\t\t{Type: &Var{Type: &Int{Size: 1}}},\n\t\t\t{Type: &Var{Type: &Int{Size: 2}}},\n\t\t\t{Type: &Var{Type: &Int{Size: 16}}},\n\t\t\t{Type: &Var{Type: &Int{Size: 8}}},\n\t\t},\n\t}\n\n\tqt.Assert(t, qt.IsNil(fixupDatasecLayout(ds)))\n\n\tqt.Assert(t, qt.Equals(ds.Size, 40))\n\tqt.Assert(t, qt.Equals(ds.Vars[0].Offset, 0))\n\tqt.Assert(t, qt.Equals(ds.Vars[1].Offset, 4))\n\tqt.Assert(t, qt.Equals(ds.Vars[2].Offset, 5))\n\tqt.Assert(t, qt.Equals(ds.Vars[3].Offset, 6))\n\tqt.Assert(t, qt.Equals(ds.Vars[4].Offset, 16))\n\tqt.Assert(t, qt.Equals(ds.Vars[5].Offset, 32))\n}\n\nfunc TestSpecConcurrentAccess(t *testing.T) {\n\tspec := vmlinuxTestdataSpec(t)\n\n\tmaxprocs := runtime.GOMAXPROCS(0)\n\tif maxprocs < 2 {\n\t\tt.Error(\"GOMAXPROCS is lower than 2:\", maxprocs)\n\t}\n\n\tvar cond atomic.Int64\n\tvar wg sync.WaitGroup\n\tfor i := 0; i < maxprocs; i++ {\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\n\t\t\tn := cond.Add(1)\n\t\t\tfor cond.Load() != int64(maxprocs) {\n\t\t\t\t// Spin to increase the chances of a race.\n\t\t\t}\n\n\t\t\tif n%2 == 0 {\n\t\t\t\t_, _ = spec.AnyTypeByName(\"gov_update_cpu_data\")\n\t\t\t} else {\n\t\t\t\t_ = spec.Copy()\n\t\t\t}\n\t\t}()\n\n\t\t// Try to get the Goroutines scheduled and spinning.\n\t\truntime.Gosched()\n\t}\n\twg.Wait()\n}\n\nfunc TestLoadEmptyRawSpec(t *testing.T) {\n\tbuf, err := binary.Append(nil, binary.LittleEndian, &btfHeader{\n\t\tMagic:     btfMagic,\n\t\tVersion:   1,\n\t\tFlags:     0,\n\t\tHdrLen:    uint32(btfHeaderLen),\n\t\tTypeOff:   0,\n\t\tTypeLen:   0,\n\t\tStringOff: 0,\n\t\tStringLen: 0,\n\t})\n\tqt.Assert(t, qt.IsNil(err))\n\n\t_, err = loadRawSpec(buf, nil)\n\tqt.Assert(t, qt.IsNil(err))\n}\n\nfunc BenchmarkSpecCopy(b *testing.B) {\n\tspec := vmlinuxTestdataSpec(b)\n\n\tfor b.Loop() {\n\t\tspec.Copy()\n\t}\n}\n\nfunc BenchmarkSpecTypeByID(b *testing.B) {\n\tspec := vmlinuxTestdataSpec(b)\n\n\tb.ReportAllocs()\n\tfor b.Loop() {\n\t\t_, err := spec.TypeByID(1)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkInspektorGadget(b *testing.B) {\n\t// This benchmark is the baseline for what Inspektor Gadget loads for a\n\t// common configuration.\n\ttypes := []string{\n\t\t\"pt_regs\",\n\t\t\"file\",\n\t\t\"inode\",\n\t\t\"super_block\",\n\t\t\"socket\",\n\t\t\"syscall_trace_enter\",\n\t\t\"task_struct\",\n\t\t\"nsproxy\",\n\t\t\"mnt_namespace\",\n\t\t// \"fanotify_event\",\n\t\t\"pid\",\n\t\t\"trace_event_raw_sched_process_exec\",\n\t\t\"fs_struct\",\n\t\t\"path\",\n\t\t\"mount\",\n\t\t\"qstr\",\n\t\t\"vfsmount\",\n\t\t\"dentry\",\n\t\t// \"bpf_func_id\",\n\t\t\"mm_struct\",\n\t\t\"syscall_trace_exit\",\n\t\t\"linux_binprm\",\n\t\t\"sock\",\n\t\t\"net\",\n\t\t\"inet_sock\",\n\t}\n\n\tvmlinux, err := internal.ReadAllCompressed(\"testdata/vmlinux.btf.gz\")\n\tqt.Assert(b, qt.IsNil(err))\n\n\tvar rd bytes.Reader\n\n\tfor b.Loop() {\n\t\trd.Reset(vmlinux)\n\t\tspec, err := LoadSpecFromReader(&rd)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\n\t\tvar s *Struct\n\t\tfor _, name := range types {\n\t\t\tif err := spec.TypeByName(name, &s); err != nil {\n\t\t\t\tb.Fatal(name, err)\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "btf/btf_types.go",
    "content": "package btf\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"unsafe\"\n)\n\n//go:generate go tool stringer -linecomment -output=btf_types_string.go -type=FuncLinkage,VarLinkage,btfKind\n\n// btfKind describes a Type.\ntype btfKind uint8\n\n// Equivalents of the BTF_KIND_* constants.\nconst (\n\tkindUnknown  btfKind = iota // Unknown\n\tkindInt                     // Int\n\tkindPointer                 // Pointer\n\tkindArray                   // Array\n\tkindStruct                  // Struct\n\tkindUnion                   // Union\n\tkindEnum                    // Enum\n\tkindForward                 // Forward\n\tkindTypedef                 // Typedef\n\tkindVolatile                // Volatile\n\tkindConst                   // Const\n\tkindRestrict                // Restrict\n\t// Added ~4.20\n\tkindFunc      // Func\n\tkindFuncProto // FuncProto\n\t// Added ~5.1\n\tkindVar     // Var\n\tkindDatasec // Datasec\n\t// Added ~5.13\n\tkindFloat // Float\n\t// Added 5.16\n\tkindDeclTag // DeclTag\n\t// Added 5.17\n\tkindTypeTag // TypeTag\n\t// Added 6.0\n\tkindEnum64 // Enum64\n)\n\n// FuncLinkage describes BTF function linkage metadata.\ntype FuncLinkage int\n\n// Equivalent of enum btf_func_linkage.\nconst (\n\tStaticFunc FuncLinkage = iota // static\n\tGlobalFunc                    // global\n\tExternFunc                    // extern\n)\n\n// VarLinkage describes BTF variable linkage metadata.\ntype VarLinkage int\n\nconst (\n\tStaticVar VarLinkage = iota // static\n\tGlobalVar                   // global\n\tExternVar                   // extern\n)\n\nconst (\n\tbtfTypeKindShift     = 24\n\tbtfTypeKindLen       = 5\n\tbtfTypeVlenShift     = 0\n\tbtfTypeVlenMask      = 16\n\tbtfTypeKindFlagShift = 31\n\tbtfTypeKindFlagMask  = 1\n)\n\nvar btfHeaderLen = binary.Size(&btfHeader{})\n\ntype btfHeader struct {\n\tMagic   uint16\n\tVersion uint8\n\tFlags   uint8\n\tHdrLen  uint32\n\n\tTypeOff   uint32\n\tTypeLen   uint32\n\tStringOff uint32\n\tStringLen uint32\n}\n\n// parseBTFHeader parses the header of the .BTF section.\nfunc parseBTFHeader(buf []byte) (*btfHeader, binary.ByteOrder, error) {\n\tvar header btfHeader\n\tvar bo binary.ByteOrder\n\tfor _, order := range []binary.ByteOrder{binary.LittleEndian, binary.BigEndian} {\n\t\tn, err := binary.Decode(buf, order, &header)\n\t\tif err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"read header: %v\", err)\n\t\t}\n\n\t\tif header.Magic != btfMagic {\n\t\t\tcontinue\n\t\t}\n\n\t\tbuf = buf[n:]\n\t\tbo = order\n\t\tbreak\n\t}\n\n\tif bo == nil {\n\t\treturn nil, nil, fmt.Errorf(\"no valid BTF header\")\n\t}\n\n\tif header.Version != 1 {\n\t\treturn nil, nil, fmt.Errorf(\"unexpected version %v\", header.Version)\n\t}\n\n\tif header.Flags != 0 {\n\t\treturn nil, nil, fmt.Errorf(\"unsupported flags %v\", header.Flags)\n\t}\n\n\tremainder := int64(header.HdrLen) - int64(binary.Size(&header))\n\tif remainder < 0 {\n\t\treturn nil, nil, errors.New(\"header length shorter than btfHeader size\")\n\t}\n\n\tfor _, b := range buf[:remainder] {\n\t\tif b != 0 {\n\t\t\treturn nil, nil, errors.New(\"header contains non-zero trailer\")\n\t\t}\n\t}\n\n\treturn &header, bo, nil\n}\n\n// btfType is equivalent to struct btf_type in Documentation/bpf/btf.rst.\ntype btfType struct {\n\tNameOff uint32\n\t/* \"info\" bits arrangement\n\t * bits  0-15: vlen (e.g. # of struct's members), linkage\n\t * bits 16-23: unused\n\t * bits 24-28: kind (e.g. int, ptr, array...etc)\n\t * bits 29-30: unused\n\t * bit     31: kind_flag, currently used by\n\t *             struct, union and fwd\n\t */\n\tInfo uint32\n\t/* \"size\" is used by INT, ENUM, STRUCT and UNION.\n\t * \"size\" tells the size of the type it is describing.\n\t *\n\t * \"type\" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,\n\t * FUNC and FUNC_PROTO.\n\t * \"type\" is a type_id referring to another type.\n\t */\n\tSizeType uint32\n}\n\nvar btfTypeSize = int(unsafe.Sizeof(btfType{}))\n\nfunc unmarshalBtfType(bt *btfType, b []byte, bo binary.ByteOrder) (int, error) {\n\tif len(b) < btfTypeSize {\n\t\treturn 0, fmt.Errorf(\"not enough bytes to unmarshal btfType\")\n\t}\n\n\tbt.NameOff = bo.Uint32(b[0:])\n\tbt.Info = bo.Uint32(b[4:])\n\tbt.SizeType = bo.Uint32(b[8:])\n\treturn btfTypeSize, nil\n}\n\nfunc mask(len uint32) uint32 {\n\treturn (1 << len) - 1\n}\n\nfunc readBits(value, len, shift uint32) uint32 {\n\treturn (value >> shift) & mask(len)\n}\n\nfunc writeBits(value, len, shift, new uint32) uint32 {\n\tvalue &^= mask(len) << shift\n\tvalue |= (new & mask(len)) << shift\n\treturn value\n}\n\nfunc (bt *btfType) info(len, shift uint32) uint32 {\n\treturn readBits(bt.Info, len, shift)\n}\n\nfunc (bt *btfType) setInfo(value, len, shift uint32) {\n\tbt.Info = writeBits(bt.Info, len, shift, value)\n}\n\nfunc (bt *btfType) Kind() btfKind {\n\treturn btfKind(bt.info(btfTypeKindLen, btfTypeKindShift))\n}\n\nfunc (bt *btfType) SetKind(kind btfKind) {\n\tbt.setInfo(uint32(kind), btfTypeKindLen, btfTypeKindShift)\n}\n\nfunc (bt *btfType) Vlen() int {\n\treturn int(bt.info(btfTypeVlenMask, btfTypeVlenShift))\n}\n\nfunc (bt *btfType) SetVlen(vlen int) {\n\tbt.setInfo(uint32(vlen), btfTypeVlenMask, btfTypeVlenShift)\n}\n\nfunc (bt *btfType) kindFlagBool() bool {\n\treturn bt.info(btfTypeKindFlagMask, btfTypeKindFlagShift) == 1\n}\n\nfunc (bt *btfType) setKindFlagBool(set bool) {\n\tvar value uint32\n\tif set {\n\t\tvalue = 1\n\t}\n\tbt.setInfo(value, btfTypeKindFlagMask, btfTypeKindFlagShift)\n}\n\n// Bitfield returns true if the struct or union contain a bitfield.\nfunc (bt *btfType) Bitfield() bool {\n\treturn bt.kindFlagBool()\n}\n\nfunc (bt *btfType) SetBitfield(isBitfield bool) {\n\tbt.setKindFlagBool(isBitfield)\n}\n\nfunc (bt *btfType) FwdKind() FwdKind {\n\treturn FwdKind(bt.info(btfTypeKindFlagMask, btfTypeKindFlagShift))\n}\n\nfunc (bt *btfType) SetFwdKind(kind FwdKind) {\n\tbt.setInfo(uint32(kind), btfTypeKindFlagMask, btfTypeKindFlagShift)\n}\n\nfunc (bt *btfType) Signed() bool {\n\treturn bt.kindFlagBool()\n}\n\nfunc (bt *btfType) SetSigned(signed bool) {\n\tbt.setKindFlagBool(signed)\n}\n\nfunc (bt *btfType) Linkage() FuncLinkage {\n\treturn FuncLinkage(bt.info(btfTypeVlenMask, btfTypeVlenShift))\n}\n\nfunc (bt *btfType) SetLinkage(linkage FuncLinkage) {\n\tbt.setInfo(uint32(linkage), btfTypeVlenMask, btfTypeVlenShift)\n}\n\nfunc (bt *btfType) Type() TypeID {\n\t// TODO: Panic here if wrong kind?\n\treturn TypeID(bt.SizeType)\n}\n\nfunc (bt *btfType) SetType(id TypeID) {\n\tbt.SizeType = uint32(id)\n}\n\nfunc (bt *btfType) Size() uint32 {\n\t// TODO: Panic here if wrong kind?\n\treturn bt.SizeType\n}\n\nfunc (bt *btfType) SetSize(size uint32) {\n\tbt.SizeType = size\n}\n\nfunc (bt *btfType) Encode(buf []byte, bo binary.ByteOrder) (int, error) {\n\tif len(buf) < btfTypeSize {\n\t\treturn 0, fmt.Errorf(\"not enough bytes to marshal btfType\")\n\t}\n\tbo.PutUint32(buf[0:], bt.NameOff)\n\tbo.PutUint32(buf[4:], bt.Info)\n\tbo.PutUint32(buf[8:], bt.SizeType)\n\treturn btfTypeSize, nil\n}\n\n// DataLen returns the length of additional type specific data in bytes.\nfunc (bt *btfType) DataLen() (int, error) {\n\tswitch bt.Kind() {\n\tcase kindInt:\n\t\treturn int(unsafe.Sizeof(btfInt{})), nil\n\tcase kindPointer:\n\tcase kindArray:\n\t\treturn int(unsafe.Sizeof(btfArray{})), nil\n\tcase kindStruct:\n\t\tfallthrough\n\tcase kindUnion:\n\t\treturn int(unsafe.Sizeof(btfMember{})) * bt.Vlen(), nil\n\tcase kindEnum:\n\t\treturn int(unsafe.Sizeof(btfEnum{})) * bt.Vlen(), nil\n\tcase kindForward:\n\tcase kindTypedef:\n\tcase kindVolatile:\n\tcase kindConst:\n\tcase kindRestrict:\n\tcase kindFunc:\n\tcase kindFuncProto:\n\t\treturn int(unsafe.Sizeof(btfParam{})) * bt.Vlen(), nil\n\tcase kindVar:\n\t\treturn int(unsafe.Sizeof(btfVariable{})), nil\n\tcase kindDatasec:\n\t\treturn int(unsafe.Sizeof(btfVarSecinfo{})) * bt.Vlen(), nil\n\tcase kindFloat:\n\tcase kindDeclTag:\n\t\treturn int(unsafe.Sizeof(btfDeclTag{})), nil\n\tcase kindTypeTag:\n\tcase kindEnum64:\n\t\treturn int(unsafe.Sizeof(btfEnum64{})) * bt.Vlen(), nil\n\tdefault:\n\t\treturn 0, fmt.Errorf(\"unknown kind: %v\", bt.Kind())\n\t}\n\n\treturn 0, nil\n}\n\n// btfInt encodes additional data for integers.\n//\n//\t? ? ? ? e e e e o o o o o o o o ? ? ? ? ? ? ? ? b b b b b b b b\n//\t? = undefined\n//\te = encoding\n//\to = offset (bitfields?)\n//\tb = bits (bitfields)\ntype btfInt struct {\n\tRaw uint32\n}\n\nconst (\n\tbtfIntEncodingLen   = 4\n\tbtfIntEncodingShift = 24\n\tbtfIntOffsetLen     = 8\n\tbtfIntOffsetShift   = 16\n\tbtfIntBitsLen       = 8\n\tbtfIntBitsShift     = 0\n)\n\nvar btfIntLen = int(unsafe.Sizeof(btfInt{}))\n\nfunc unmarshalBtfInt(bi *btfInt, b []byte, bo binary.ByteOrder) (int, error) {\n\tif len(b) < btfIntLen {\n\t\treturn 0, fmt.Errorf(\"not enough bytes to unmarshal btfInt\")\n\t}\n\n\tbi.Raw = bo.Uint32(b[0:])\n\treturn btfIntLen, nil\n}\n\nfunc (bi btfInt) Encoding() IntEncoding {\n\treturn IntEncoding(readBits(bi.Raw, btfIntEncodingLen, btfIntEncodingShift))\n}\n\nfunc (bi *btfInt) SetEncoding(e IntEncoding) {\n\tbi.Raw = writeBits(uint32(bi.Raw), btfIntEncodingLen, btfIntEncodingShift, uint32(e))\n}\n\nfunc (bi btfInt) Offset() Bits {\n\treturn Bits(readBits(bi.Raw, btfIntOffsetLen, btfIntOffsetShift))\n}\n\nfunc (bi *btfInt) SetOffset(offset uint32) {\n\tbi.Raw = writeBits(bi.Raw, btfIntOffsetLen, btfIntOffsetShift, offset)\n}\n\nfunc (bi btfInt) Bits() Bits {\n\treturn Bits(readBits(bi.Raw, btfIntBitsLen, btfIntBitsShift))\n}\n\nfunc (bi *btfInt) SetBits(bits byte) {\n\tbi.Raw = writeBits(bi.Raw, btfIntBitsLen, btfIntBitsShift, uint32(bits))\n}\n\ntype btfArray struct {\n\tType      TypeID\n\tIndexType TypeID\n\tNelems    uint32\n}\n\nvar btfArrayLen = int(unsafe.Sizeof(btfArray{}))\n\nfunc unmarshalBtfArray(ba *btfArray, b []byte, bo binary.ByteOrder) (int, error) {\n\tif len(b) < btfArrayLen {\n\t\treturn 0, fmt.Errorf(\"not enough bytes to unmarshal btfArray\")\n\t}\n\n\tba.Type = TypeID(bo.Uint32(b[0:]))\n\tba.IndexType = TypeID(bo.Uint32(b[4:]))\n\tba.Nelems = bo.Uint32(b[8:])\n\treturn btfArrayLen, nil\n}\n\ntype btfMember struct {\n\tNameOff uint32\n\tType    TypeID\n\tOffset  uint32\n}\n\nvar btfMemberLen = int(unsafe.Sizeof(btfMember{}))\n\nfunc unmarshalBtfMember(bm *btfMember, b []byte, bo binary.ByteOrder) (int, error) {\n\tif btfMemberLen > len(b) {\n\t\treturn 0, fmt.Errorf(\"not enough bytes to unmarshal btfMember\")\n\t}\n\n\tbm.NameOff = bo.Uint32(b[0:])\n\tbm.Type = TypeID(bo.Uint32(b[4:]))\n\tbm.Offset = bo.Uint32(b[8:])\n\treturn btfMemberLen, nil\n}\n\ntype btfVarSecinfo struct {\n\tType   TypeID\n\tOffset uint32\n\tSize   uint32\n}\n\nvar btfVarSecinfoLen = int(unsafe.Sizeof(btfVarSecinfo{}))\n\nfunc unmarshalBtfVarSecInfo(bvsi *btfVarSecinfo, b []byte, bo binary.ByteOrder) (int, error) {\n\tif len(b) < btfVarSecinfoLen {\n\t\treturn 0, fmt.Errorf(\"not enough bytes to unmarshal btfVarSecinfo\")\n\t}\n\n\tbvsi.Type = TypeID(bo.Uint32(b[0:]))\n\tbvsi.Offset = bo.Uint32(b[4:])\n\tbvsi.Size = bo.Uint32(b[8:])\n\treturn btfVarSecinfoLen, nil\n}\n\ntype btfVariable struct {\n\tLinkage uint32\n}\n\nvar btfVariableLen = int(unsafe.Sizeof(btfVariable{}))\n\nfunc unmarshalBtfVariable(bv *btfVariable, b []byte, bo binary.ByteOrder) (int, error) {\n\tif len(b) < btfVariableLen {\n\t\treturn 0, fmt.Errorf(\"not enough bytes to unmarshal btfVariable\")\n\t}\n\n\tbv.Linkage = bo.Uint32(b[0:])\n\treturn btfVariableLen, nil\n}\n\ntype btfEnum struct {\n\tNameOff uint32\n\tVal     uint32\n}\n\nvar btfEnumLen = int(unsafe.Sizeof(btfEnum{}))\n\nfunc unmarshalBtfEnum(be *btfEnum, b []byte, bo binary.ByteOrder) (int, error) {\n\tif btfEnumLen > len(b) {\n\t\treturn 0, fmt.Errorf(\"not enough bytes to unmarshal btfEnum\")\n\t}\n\n\tbe.NameOff = bo.Uint32(b[0:])\n\tbe.Val = bo.Uint32(b[4:])\n\treturn btfEnumLen, nil\n}\n\ntype btfEnum64 struct {\n\tNameOff uint32\n\tValLo32 uint32\n\tValHi32 uint32\n}\n\nvar btfEnum64Len = int(unsafe.Sizeof(btfEnum64{}))\n\nfunc unmarshalBtfEnum64(enum *btfEnum64, b []byte, bo binary.ByteOrder) (int, error) {\n\tif len(b) < btfEnum64Len {\n\t\treturn 0, fmt.Errorf(\"not enough bytes to unmarshal btfEnum64\")\n\t}\n\n\tenum.NameOff = bo.Uint32(b[0:])\n\tenum.ValLo32 = bo.Uint32(b[4:])\n\tenum.ValHi32 = bo.Uint32(b[8:])\n\n\treturn btfEnum64Len, nil\n}\n\ntype btfParam struct {\n\tNameOff uint32\n\tType    TypeID\n}\n\nvar btfParamLen = int(unsafe.Sizeof(btfParam{}))\n\nfunc unmarshalBtfParam(param *btfParam, b []byte, bo binary.ByteOrder) (int, error) {\n\tif len(b) < btfParamLen {\n\t\treturn 0, fmt.Errorf(\"not enough bytes to unmarshal btfParam\")\n\t}\n\n\tparam.NameOff = bo.Uint32(b[0:])\n\tparam.Type = TypeID(bo.Uint32(b[4:]))\n\n\treturn btfParamLen, nil\n}\n\ntype btfDeclTag struct {\n\tComponentIdx uint32\n}\n\nvar btfDeclTagLen = int(unsafe.Sizeof(btfDeclTag{}))\n\nfunc unmarshalBtfDeclTag(bdt *btfDeclTag, b []byte, bo binary.ByteOrder) (int, error) {\n\tif len(b) < btfDeclTagLen {\n\t\treturn 0, fmt.Errorf(\"not enough bytes to unmarshal btfDeclTag\")\n\t}\n\n\tbdt.ComponentIdx = bo.Uint32(b[0:])\n\treturn btfDeclTagLen, nil\n}\n"
  },
  {
    "path": "btf/btf_types_string.go",
    "content": "// Code generated by \"stringer -linecomment -output=btf_types_string.go -type=FuncLinkage,VarLinkage,btfKind\"; DO NOT EDIT.\n\npackage btf\n\nimport \"strconv\"\n\nfunc _() {\n\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n\t// Re-run the stringer command to generate them again.\n\tvar x [1]struct{}\n\t_ = x[StaticFunc-0]\n\t_ = x[GlobalFunc-1]\n\t_ = x[ExternFunc-2]\n}\n\nconst _FuncLinkage_name = \"staticglobalextern\"\n\nvar _FuncLinkage_index = [...]uint8{0, 6, 12, 18}\n\nfunc (i FuncLinkage) String() string {\n\tidx := int(i) - 0\n\tif i < 0 || idx >= len(_FuncLinkage_index)-1 {\n\t\treturn \"FuncLinkage(\" + strconv.FormatInt(int64(i), 10) + \")\"\n\t}\n\treturn _FuncLinkage_name[_FuncLinkage_index[idx]:_FuncLinkage_index[idx+1]]\n}\nfunc _() {\n\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n\t// Re-run the stringer command to generate them again.\n\tvar x [1]struct{}\n\t_ = x[StaticVar-0]\n\t_ = x[GlobalVar-1]\n\t_ = x[ExternVar-2]\n}\n\nconst _VarLinkage_name = \"staticglobalextern\"\n\nvar _VarLinkage_index = [...]uint8{0, 6, 12, 18}\n\nfunc (i VarLinkage) String() string {\n\tidx := int(i) - 0\n\tif i < 0 || idx >= len(_VarLinkage_index)-1 {\n\t\treturn \"VarLinkage(\" + strconv.FormatInt(int64(i), 10) + \")\"\n\t}\n\treturn _VarLinkage_name[_VarLinkage_index[idx]:_VarLinkage_index[idx+1]]\n}\nfunc _() {\n\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n\t// Re-run the stringer command to generate them again.\n\tvar x [1]struct{}\n\t_ = x[kindUnknown-0]\n\t_ = x[kindInt-1]\n\t_ = x[kindPointer-2]\n\t_ = x[kindArray-3]\n\t_ = x[kindStruct-4]\n\t_ = x[kindUnion-5]\n\t_ = x[kindEnum-6]\n\t_ = x[kindForward-7]\n\t_ = x[kindTypedef-8]\n\t_ = x[kindVolatile-9]\n\t_ = x[kindConst-10]\n\t_ = x[kindRestrict-11]\n\t_ = x[kindFunc-12]\n\t_ = x[kindFuncProto-13]\n\t_ = x[kindVar-14]\n\t_ = x[kindDatasec-15]\n\t_ = x[kindFloat-16]\n\t_ = x[kindDeclTag-17]\n\t_ = x[kindTypeTag-18]\n\t_ = x[kindEnum64-19]\n}\n\nconst _btfKind_name = \"UnknownIntPointerArrayStructUnionEnumForwardTypedefVolatileConstRestrictFuncFuncProtoVarDatasecFloatDeclTagTypeTagEnum64\"\n\nvar _btfKind_index = [...]uint8{0, 7, 10, 17, 22, 28, 33, 37, 44, 51, 59, 64, 72, 76, 85, 88, 95, 100, 107, 114, 120}\n\nfunc (i btfKind) String() string {\n\tidx := int(i) - 0\n\tif i < 0 || idx >= len(_btfKind_index)-1 {\n\t\treturn \"btfKind(\" + strconv.FormatInt(int64(i), 10) + \")\"\n\t}\n\treturn _btfKind_name[_btfKind_index[idx]:_btfKind_index[idx+1]]\n}\n"
  },
  {
    "path": "btf/core.go",
    "content": "package btf\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/platform\"\n)\n\n// Code in this file is derived from libbpf, which is available under a BSD\n// 2-Clause license.\n\n// A constant used when CO-RE relocation has to remove instructions.\n//\n// Taken from libbpf.\nconst COREBadRelocationSentinel = 0xbad2310\n\n// COREFixup is the result of computing a CO-RE relocation for a target.\ntype COREFixup struct {\n\tkind   coreKind\n\tlocal  uint64\n\ttarget uint64\n\t// True if there is no valid fixup. The instruction is replaced with an\n\t// invalid dummy.\n\tpoison bool\n\t// True if the validation of the local value should be skipped. Used by\n\t// some kinds of bitfield relocations.\n\tskipLocalValidation bool\n}\n\nfunc (f *COREFixup) equal(other COREFixup) bool {\n\treturn f.local == other.local && f.target == other.target\n}\n\nfunc (f *COREFixup) String() string {\n\tif f.poison {\n\t\treturn fmt.Sprintf(\"%s=poison\", f.kind)\n\t}\n\treturn fmt.Sprintf(\"%s=%d->%d\", f.kind, f.local, f.target)\n}\n\nfunc (f *COREFixup) Apply(ins *asm.Instruction) error {\n\tif !platform.IsLinux {\n\t\treturn fmt.Errorf(\"CO-RE fixup: %w\", internal.ErrNotSupportedOnOS)\n\t}\n\n\tif f.poison {\n\t\t// Relocation is poisoned, replace the instruction with an invalid one.\n\t\tif ins.OpCode.IsDWordLoad() {\n\t\t\t// Replace a dword load with a invalid dword load to preserve instruction size.\n\t\t\t*ins = asm.LoadImm(asm.R10, COREBadRelocationSentinel, asm.DWord)\n\t\t} else {\n\t\t\t// Replace all single size instruction with a invalid call instruction.\n\t\t\t*ins = asm.BuiltinFunc(COREBadRelocationSentinel).Call()\n\t\t}\n\n\t\t// Add context to the kernel verifier output.\n\t\tif source := ins.Source(); source != nil {\n\t\t\t*ins = ins.WithSource(asm.Comment(fmt.Sprintf(\"instruction poisoned by CO-RE: %s\", source)))\n\t\t} else {\n\t\t\t*ins = ins.WithSource(asm.Comment(\"instruction poisoned by CO-RE\"))\n\t\t}\n\n\t\treturn nil\n\t}\n\n\tswitch class := ins.OpCode.Class(); class {\n\tcase asm.LdXClass, asm.StClass, asm.StXClass:\n\t\tif want := int16(f.local); !f.skipLocalValidation && want != ins.Offset {\n\t\t\treturn fmt.Errorf(\"invalid offset %d, expected %d\", ins.Offset, f.local)\n\t\t}\n\n\t\tif f.target > math.MaxInt16 {\n\t\t\treturn fmt.Errorf(\"offset %d exceeds MaxInt16\", f.target)\n\t\t}\n\n\t\tins.Offset = int16(f.target)\n\n\tcase asm.LdClass:\n\t\tif !ins.IsConstantLoad(asm.DWord) {\n\t\t\treturn fmt.Errorf(\"not a dword-sized immediate load\")\n\t\t}\n\n\t\tif want := int64(f.local); !f.skipLocalValidation && want != ins.Constant {\n\t\t\treturn fmt.Errorf(\"invalid immediate %d, expected %d (fixup: %v)\", ins.Constant, want, f)\n\t\t}\n\n\t\tins.Constant = int64(f.target)\n\n\tcase asm.ALUClass:\n\t\tif ins.OpCode.ALUOp() == asm.Swap {\n\t\t\treturn fmt.Errorf(\"relocation against swap\")\n\t\t}\n\n\t\tfallthrough\n\n\tcase asm.ALU64Class:\n\t\tif src := ins.OpCode.Source(); src != asm.ImmSource {\n\t\t\treturn fmt.Errorf(\"invalid source %s\", src)\n\t\t}\n\n\t\tif want := int64(f.local); !f.skipLocalValidation && want != ins.Constant {\n\t\t\treturn fmt.Errorf(\"invalid immediate %d, expected %d (fixup: %v, kind: %v, ins: %v)\", ins.Constant, want, f, f.kind, ins)\n\t\t}\n\n\t\tif f.target > math.MaxInt32 {\n\t\t\treturn fmt.Errorf(\"immediate %d exceeds MaxInt32\", f.target)\n\t\t}\n\n\t\tins.Constant = int64(f.target)\n\n\tdefault:\n\t\treturn fmt.Errorf(\"invalid class %s\", class)\n\t}\n\n\treturn nil\n}\n\nfunc (f COREFixup) isNonExistant() bool {\n\treturn f.kind.checksForExistence() && f.target == 0\n}\n\n// coreKind is the type of CO-RE relocation as specified in BPF source code.\ntype coreKind uint32\n\nconst (\n\treloFieldByteOffset coreKind = iota /* field byte offset */\n\treloFieldByteSize                   /* field size in bytes */\n\treloFieldExists                     /* field existence in target kernel */\n\treloFieldSigned                     /* field signedness (0 - unsigned, 1 - signed) */\n\treloFieldLShiftU64                  /* bitfield-specific left bitshift */\n\treloFieldRShiftU64                  /* bitfield-specific right bitshift */\n\treloTypeIDLocal                     /* type ID in local BPF object */\n\treloTypeIDTarget                    /* type ID in target kernel */\n\treloTypeExists                      /* type existence in target kernel */\n\treloTypeSize                        /* type size in bytes */\n\treloEnumvalExists                   /* enum value existence in target kernel */\n\treloEnumvalValue                    /* enum value integer value */\n\treloTypeMatches                     /* type matches kernel type */\n)\n\nfunc (k coreKind) checksForExistence() bool {\n\treturn k == reloEnumvalExists || k == reloTypeExists || k == reloFieldExists || k == reloTypeMatches\n}\n\nfunc (k coreKind) String() string {\n\tswitch k {\n\tcase reloFieldByteOffset:\n\t\treturn \"byte_off\"\n\tcase reloFieldByteSize:\n\t\treturn \"byte_sz\"\n\tcase reloFieldExists:\n\t\treturn \"field_exists\"\n\tcase reloFieldSigned:\n\t\treturn \"signed\"\n\tcase reloFieldLShiftU64:\n\t\treturn \"lshift_u64\"\n\tcase reloFieldRShiftU64:\n\t\treturn \"rshift_u64\"\n\tcase reloTypeIDLocal:\n\t\treturn \"local_type_id\"\n\tcase reloTypeIDTarget:\n\t\treturn \"target_type_id\"\n\tcase reloTypeExists:\n\t\treturn \"type_exists\"\n\tcase reloTypeSize:\n\t\treturn \"type_size\"\n\tcase reloEnumvalExists:\n\t\treturn \"enumval_exists\"\n\tcase reloEnumvalValue:\n\t\treturn \"enumval_value\"\n\tcase reloTypeMatches:\n\t\treturn \"type_matches\"\n\tdefault:\n\t\treturn fmt.Sprintf(\"unknown (%d)\", k)\n\t}\n}\n\n// CORERelocate calculates changes needed to adjust eBPF instructions for differences\n// in types.\n//\n// targets forms the set of types to relocate against. The first element has to be\n// BTF for vmlinux, the following must be types for kernel modules.\n//\n// resolveLocalTypeID is called for each local type which requires a stable TypeID.\n// Calling the function with the same type multiple times must produce the same\n// result. It is the callers responsibility to ensure that the relocated instructions\n// are loaded with matching BTF.\n//\n// Returns a list of fixups which can be applied to instructions to make them\n// match the target type(s).\n//\n// Fixups are returned in the order of relos, e.g. fixup[i] is the solution\n// for relos[i].\nfunc CORERelocate(relos []*CORERelocation, targets []*Spec, bo binary.ByteOrder, resolveLocalTypeID func(Type) (TypeID, error)) ([]COREFixup, error) {\n\tif len(targets) == 0 {\n\t\t// Explicitly check for nil here since the argument used to be optional.\n\t\treturn nil, fmt.Errorf(\"targets must be provided\")\n\t}\n\n\t// We can't encode type IDs that aren't for vmlinux into instructions at the\n\t// moment.\n\tresolveTargetTypeID := targets[0].TypeID\n\n\tfor _, target := range targets {\n\t\tif bo != target.byteOrder {\n\t\t\treturn nil, fmt.Errorf(\"can't relocate %s against %s\", bo, target.byteOrder)\n\t\t}\n\t}\n\n\ttype reloGroup struct {\n\t\trelos []*CORERelocation\n\t\t// Position of each relocation in relos.\n\t\tindices []int\n\t}\n\n\t// Split relocations into per Type lists.\n\trelosByType := make(map[Type]*reloGroup)\n\tresult := make([]COREFixup, len(relos))\n\tfor i, relo := range relos {\n\t\tif relo.kind == reloTypeIDLocal {\n\t\t\t// Filtering out reloTypeIDLocal here makes our lives a lot easier\n\t\t\t// down the line, since it doesn't have a target at all.\n\t\t\tif len(relo.accessor) > 1 || relo.accessor[0] != 0 {\n\t\t\t\treturn nil, fmt.Errorf(\"%s: unexpected accessor %v\", relo.kind, relo.accessor)\n\t\t\t}\n\n\t\t\tid, err := resolveLocalTypeID(relo.typ)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"%s: get type id: %w\", relo.kind, err)\n\t\t\t}\n\n\t\t\tresult[i] = COREFixup{\n\t\t\t\tkind:   relo.kind,\n\t\t\t\tlocal:  uint64(relo.id),\n\t\t\t\ttarget: uint64(id),\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\tgroup, ok := relosByType[relo.typ]\n\t\tif !ok {\n\t\t\tgroup = &reloGroup{}\n\t\t\trelosByType[relo.typ] = group\n\t\t}\n\t\tgroup.relos = append(group.relos, relo)\n\t\tgroup.indices = append(group.indices, i)\n\t}\n\n\tfor localType, group := range relosByType {\n\t\tlocalTypeName := localType.TypeName()\n\t\tif localTypeName == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"relocate unnamed or anonymous type %s: %w\", localType, ErrNotSupported)\n\t\t}\n\n\t\tessentialName := newEssentialName(localTypeName)\n\n\t\tvar targetTypes []Type\n\t\tfor _, target := range targets {\n\t\t\tnamedTypes, err := target.TypesByName(essentialName)\n\t\t\tif errors.Is(err, ErrNotFound) {\n\t\t\t\tcontinue\n\t\t\t} else if err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\ttargetTypes = append(targetTypes, namedTypes...)\n\t\t}\n\n\t\tfixups, err := coreCalculateFixups(group.relos, targetTypes, bo, resolveTargetTypeID)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"relocate %s: %w\", localType, err)\n\t\t}\n\n\t\tfor j, index := range group.indices {\n\t\t\tresult[index] = fixups[j]\n\t\t}\n\t}\n\n\treturn result, nil\n}\n\nvar errAmbiguousRelocation = errors.New(\"ambiguous relocation\")\nvar errImpossibleRelocation = errors.New(\"impossible relocation\")\nvar errIncompatibleTypes = errors.New(\"incompatible types\")\n\n// coreCalculateFixups finds the target type that best matches all relocations.\n//\n// All relos must target the same type.\n//\n// The best target is determined by scoring: the less poisoning we have to do\n// the better the target is.\nfunc coreCalculateFixups(relos []*CORERelocation, targets []Type, bo binary.ByteOrder, resolveTargetTypeID func(Type) (TypeID, error)) ([]COREFixup, error) {\n\tbestScore := len(relos)\n\tvar bestFixups []COREFixup\n\tfor _, target := range targets {\n\t\tscore := 0 // lower is better\n\t\tfixups := make([]COREFixup, 0, len(relos))\n\t\tfor _, relo := range relos {\n\t\t\tfixup, err := coreCalculateFixup(relo, target, bo, resolveTargetTypeID)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"target %s: %s: %w\", target, relo.kind, err)\n\t\t\t}\n\t\t\tif fixup.poison || fixup.isNonExistant() {\n\t\t\t\tscore++\n\t\t\t}\n\t\t\tfixups = append(fixups, fixup)\n\t\t}\n\n\t\tif score > bestScore {\n\t\t\t// We have a better target already, ignore this one.\n\t\t\tcontinue\n\t\t}\n\n\t\tif score < bestScore {\n\t\t\t// This is the best target yet, use it.\n\t\t\tbestScore = score\n\t\t\tbestFixups = fixups\n\t\t\tcontinue\n\t\t}\n\n\t\t// Some other target has the same score as the current one. Make sure\n\t\t// the fixups agree with each other.\n\t\tfor i, fixup := range bestFixups {\n\t\t\tif !fixup.equal(fixups[i]) {\n\t\t\t\treturn nil, fmt.Errorf(\"%s: multiple types match: %w\", fixup.kind, errAmbiguousRelocation)\n\t\t\t}\n\t\t}\n\t}\n\n\tif bestFixups == nil {\n\t\t// Nothing at all matched, probably because there are no suitable\n\t\t// targets at all.\n\t\t//\n\t\t// Poison everything except checksForExistence.\n\t\tbestFixups = make([]COREFixup, len(relos))\n\t\tfor i, relo := range relos {\n\t\t\tif relo.kind.checksForExistence() {\n\t\t\t\tbestFixups[i] = COREFixup{kind: relo.kind, local: 1, target: 0}\n\t\t\t} else {\n\t\t\t\tbestFixups[i] = COREFixup{kind: relo.kind, poison: true}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn bestFixups, nil\n}\n\nvar errNoSignedness = errors.New(\"no signedness\")\n\n// coreCalculateFixup calculates the fixup given a relocation and a target type.\nfunc coreCalculateFixup(relo *CORERelocation, target Type, bo binary.ByteOrder, resolveTargetTypeID func(Type) (TypeID, error)) (COREFixup, error) {\n\tfixup := func(local, target uint64) (COREFixup, error) {\n\t\treturn COREFixup{kind: relo.kind, local: local, target: target}, nil\n\t}\n\tfixupWithoutValidation := func(local, target uint64) (COREFixup, error) {\n\t\treturn COREFixup{kind: relo.kind, local: local, target: target, skipLocalValidation: true}, nil\n\t}\n\tpoison := func() (COREFixup, error) {\n\t\tif relo.kind.checksForExistence() {\n\t\t\treturn fixup(1, 0)\n\t\t}\n\t\treturn COREFixup{kind: relo.kind, poison: true}, nil\n\t}\n\tzero := COREFixup{}\n\n\tlocal := relo.typ\n\n\tswitch relo.kind {\n\tcase reloTypeMatches:\n\t\tif len(relo.accessor) > 1 || relo.accessor[0] != 0 {\n\t\t\treturn zero, fmt.Errorf(\"unexpected accessor %v\", relo.accessor)\n\t\t}\n\n\t\terr := coreTypesMatch(local, target, nil)\n\t\tif errors.Is(err, errIncompatibleTypes) {\n\t\t\treturn poison()\n\t\t}\n\t\tif err != nil {\n\t\t\treturn zero, err\n\t\t}\n\n\t\treturn fixup(1, 1)\n\n\tcase reloTypeIDTarget, reloTypeSize, reloTypeExists:\n\t\tif len(relo.accessor) > 1 || relo.accessor[0] != 0 {\n\t\t\treturn zero, fmt.Errorf(\"unexpected accessor %v\", relo.accessor)\n\t\t}\n\n\t\terr := CheckTypeCompatibility(local, target)\n\t\tif errors.Is(err, errIncompatibleTypes) {\n\t\t\treturn poison()\n\t\t}\n\t\tif err != nil {\n\t\t\treturn zero, err\n\t\t}\n\n\t\tswitch relo.kind {\n\t\tcase reloTypeExists:\n\t\t\treturn fixup(1, 1)\n\n\t\tcase reloTypeIDTarget:\n\t\t\ttargetID, err := resolveTargetTypeID(target)\n\t\t\tif errors.Is(err, ErrNotFound) {\n\t\t\t\t// Probably a relocation trying to get the ID\n\t\t\t\t// of a type from a kmod.\n\t\t\t\treturn poison()\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\treturn zero, err\n\t\t\t}\n\t\t\treturn fixup(uint64(relo.id), uint64(targetID))\n\n\t\tcase reloTypeSize:\n\t\t\tlocalSize, err := Sizeof(local)\n\t\t\tif err != nil {\n\t\t\t\treturn zero, err\n\t\t\t}\n\n\t\t\ttargetSize, err := Sizeof(target)\n\t\t\tif err != nil {\n\t\t\t\treturn zero, err\n\t\t\t}\n\n\t\t\treturn fixup(uint64(localSize), uint64(targetSize))\n\t\t}\n\n\tcase reloEnumvalValue, reloEnumvalExists:\n\t\tlocalValue, targetValue, err := coreFindEnumValue(local, relo.accessor, target)\n\t\tif errors.Is(err, errImpossibleRelocation) {\n\t\t\treturn poison()\n\t\t}\n\t\tif err != nil {\n\t\t\treturn zero, err\n\t\t}\n\n\t\tswitch relo.kind {\n\t\tcase reloEnumvalExists:\n\t\t\treturn fixup(1, 1)\n\n\t\tcase reloEnumvalValue:\n\t\t\treturn fixup(localValue.Value, targetValue.Value)\n\t\t}\n\n\tcase reloFieldByteOffset, reloFieldByteSize, reloFieldExists, reloFieldLShiftU64, reloFieldRShiftU64, reloFieldSigned:\n\t\tif _, ok := As[*Fwd](target); ok {\n\t\t\t// We can't relocate fields using a forward declaration, so\n\t\t\t// skip it. If a non-forward declaration is present in the BTF\n\t\t\t// we'll find it in one of the other iterations.\n\t\t\treturn poison()\n\t\t}\n\n\t\tlocalField, targetField, err := coreFindField(local, relo.accessor, target)\n\t\tif errors.Is(err, errImpossibleRelocation) {\n\t\t\treturn poison()\n\t\t}\n\t\tif err != nil {\n\t\t\treturn zero, err\n\t\t}\n\n\t\tmaybeSkipValidation := func(f COREFixup, err error) (COREFixup, error) {\n\t\t\tf.skipLocalValidation = localField.bitfieldSize > 0\n\t\t\treturn f, err\n\t\t}\n\n\t\tswitch relo.kind {\n\t\tcase reloFieldExists:\n\t\t\treturn fixup(1, 1)\n\n\t\tcase reloFieldByteOffset:\n\t\t\treturn maybeSkipValidation(fixup(uint64(localField.offset), uint64(targetField.offset)))\n\n\t\tcase reloFieldByteSize:\n\t\t\tlocalSize, err := Sizeof(localField.Type)\n\t\t\tif err != nil {\n\t\t\t\treturn zero, err\n\t\t\t}\n\n\t\t\ttargetSize, err := Sizeof(targetField.Type)\n\t\t\tif err != nil {\n\t\t\t\treturn zero, err\n\t\t\t}\n\t\t\treturn maybeSkipValidation(fixup(uint64(localSize), uint64(targetSize)))\n\n\t\tcase reloFieldLShiftU64:\n\t\t\tvar target uint64\n\t\t\tif bo == binary.LittleEndian {\n\t\t\t\ttargetSize, err := targetField.sizeBits()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn zero, err\n\t\t\t\t}\n\n\t\t\t\ttarget = uint64(64 - targetField.bitfieldOffset - targetSize)\n\t\t\t} else {\n\t\t\t\tloadWidth, err := Sizeof(targetField.Type)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn zero, err\n\t\t\t\t}\n\n\t\t\t\ttarget = uint64(64 - Bits(loadWidth*8) + targetField.bitfieldOffset)\n\t\t\t}\n\t\t\treturn fixupWithoutValidation(0, target)\n\n\t\tcase reloFieldRShiftU64:\n\t\t\ttargetSize, err := targetField.sizeBits()\n\t\t\tif err != nil {\n\t\t\t\treturn zero, err\n\t\t\t}\n\n\t\t\treturn fixupWithoutValidation(0, uint64(64-targetSize))\n\n\t\tcase reloFieldSigned:\n\t\t\tswitch local := UnderlyingType(localField.Type).(type) {\n\t\t\tcase *Enum:\n\t\t\t\ttarget, ok := As[*Enum](targetField.Type)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn zero, fmt.Errorf(\"target isn't *Enum but %T\", targetField.Type)\n\t\t\t\t}\n\n\t\t\t\treturn fixup(boolToUint64(local.Signed), boolToUint64(target.Signed))\n\t\t\tcase *Int:\n\t\t\t\ttarget, ok := As[*Int](targetField.Type)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn zero, fmt.Errorf(\"target isn't *Int but %T\", targetField.Type)\n\t\t\t\t}\n\n\t\t\t\treturn fixup(\n\t\t\t\t\tuint64(local.Encoding&Signed),\n\t\t\t\t\tuint64(target.Encoding&Signed),\n\t\t\t\t)\n\t\t\tdefault:\n\t\t\t\treturn zero, fmt.Errorf(\"type %T: %w\", local, errNoSignedness)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn zero, ErrNotSupported\n}\n\nfunc boolToUint64(val bool) uint64 {\n\tif val {\n\t\treturn 1\n\t}\n\treturn 0\n}\n\n/* coreAccessor contains a path through a struct. It contains at least one index.\n *\n * The interpretation depends on the kind of the relocation. The following is\n * taken from struct bpf_core_relo in libbpf_internal.h:\n *\n * - for field-based relocations, string encodes an accessed field using\n *   a sequence of field and array indices, separated by colon (:). It's\n *   conceptually very close to LLVM's getelementptr ([0]) instruction's\n *   arguments for identifying offset to a field.\n * - for type-based relocations, strings is expected to be just \"0\";\n * - for enum value-based relocations, string contains an index of enum\n *   value within its enum type;\n *\n * Example to provide a better feel.\n *\n *   struct sample {\n *       int a;\n *       struct {\n *           int b[10];\n *       };\n *   };\n *\n *   struct sample s = ...;\n *   int x = &s->a;     // encoded as \"0:0\" (a is field #0)\n *   int y = &s->b[5];  // encoded as \"0:1:0:5\" (anon struct is field #1,\n *                      // b is field #0 inside anon struct, accessing elem #5)\n *   int z = &s[10]->b; // encoded as \"10:1\" (ptr is used as an array)\n */\ntype coreAccessor []int\n\nfunc parseCOREAccessor(accessor string) (coreAccessor, error) {\n\tif accessor == \"\" {\n\t\treturn nil, fmt.Errorf(\"empty accessor\")\n\t}\n\n\tparts := strings.Split(accessor, \":\")\n\tresult := make(coreAccessor, 0, len(parts))\n\tfor _, part := range parts {\n\t\t// 31 bits to avoid overflowing int on 32 bit platforms.\n\t\tindex, err := strconv.ParseUint(part, 10, 31)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"accessor index %q: %s\", part, err)\n\t\t}\n\n\t\tresult = append(result, int(index))\n\t}\n\n\treturn result, nil\n}\n\nfunc (ca coreAccessor) String() string {\n\tstrs := make([]string, 0, len(ca))\n\tfor _, i := range ca {\n\t\tstrs = append(strs, strconv.Itoa(i))\n\t}\n\treturn strings.Join(strs, \":\")\n}\n\nfunc (ca coreAccessor) enumValue(t Type) (*EnumValue, error) {\n\te, ok := As[*Enum](t)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"not an enum: %s\", t)\n\t}\n\n\tif len(ca) > 1 {\n\t\treturn nil, fmt.Errorf(\"invalid accessor %s for enum\", ca)\n\t}\n\n\ti := ca[0]\n\tif i >= len(e.Values) {\n\t\treturn nil, fmt.Errorf(\"invalid index %d for %s\", i, e)\n\t}\n\n\treturn &e.Values[i], nil\n}\n\n// coreField represents the position of a \"child\" of a composite type from the\n// start of that type.\n//\n//\t/- start of composite\n//\t| offset * 8 | bitfieldOffset | bitfieldSize | ... |\n//\t             \\- start of field       end of field -/\ntype coreField struct {\n\tType Type\n\n\t// The position of the field from the start of the composite type in bytes.\n\toffset uint32\n\n\t// The offset of the bitfield in bits from the start of the field.\n\tbitfieldOffset Bits\n\n\t// The size of the bitfield in bits.\n\t//\n\t// Zero if the field is not a bitfield.\n\tbitfieldSize Bits\n}\n\nfunc (cf *coreField) adjustOffsetToNthElement(n int) error {\n\tif n == 0 {\n\t\treturn nil\n\t}\n\n\tsize, err := Sizeof(cf.Type)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tcf.offset += uint32(n) * uint32(size)\n\treturn nil\n}\n\nfunc (cf *coreField) adjustOffsetBits(offset Bits) error {\n\talign, err := alignof(cf.Type)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// We can compute the load offset by:\n\t// 1) converting the bit offset to bytes with a flooring division.\n\t// 2) dividing and multiplying that offset by the alignment, yielding the\n\t//    load size aligned offset.\n\toffsetBytes := uint32(offset/8) / uint32(align) * uint32(align)\n\n\t// The number of bits remaining is the bit offset less the number of bits\n\t// we can \"skip\" with the aligned offset.\n\tcf.bitfieldOffset = offset - Bits(offsetBytes*8)\n\n\t// We know that cf.offset is aligned at to at least align since we get it\n\t// from the compiler via BTF. Adding an aligned offsetBytes preserves the\n\t// alignment.\n\tcf.offset += offsetBytes\n\treturn nil\n}\n\nfunc (cf *coreField) sizeBits() (Bits, error) {\n\tif cf.bitfieldSize > 0 {\n\t\treturn cf.bitfieldSize, nil\n\t}\n\n\t// Someone is trying to access a non-bitfield via a bit shift relocation.\n\t// This happens when a field changes from a bitfield to a regular field\n\t// between kernel versions. Synthesise the size to make the shifts work.\n\tsize, err := Sizeof(cf.Type)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn Bits(size * 8), nil\n}\n\n// coreFindField descends into the local type using the accessor and tries to\n// find an equivalent field in target at each step.\n//\n// Returns the field and the offset of the field from the start of\n// target in bits.\nfunc coreFindField(localT Type, localAcc coreAccessor, targetT Type) (coreField, coreField, error) {\n\tlocal := coreField{Type: localT}\n\ttarget := coreField{Type: targetT}\n\n\tif err := coreAreMembersCompatible(local.Type, target.Type); err != nil {\n\t\treturn coreField{}, coreField{}, fmt.Errorf(\"fields: %w\", err)\n\t}\n\n\t// The first index is used to offset a pointer of the base type like\n\t// when accessing an array.\n\tif err := local.adjustOffsetToNthElement(localAcc[0]); err != nil {\n\t\treturn coreField{}, coreField{}, err\n\t}\n\n\tif err := target.adjustOffsetToNthElement(localAcc[0]); err != nil {\n\t\treturn coreField{}, coreField{}, err\n\t}\n\n\tvar localMaybeFlex, targetMaybeFlex bool\n\tfor i, acc := range localAcc[1:] {\n\t\tswitch localType := UnderlyingType(local.Type).(type) {\n\t\tcase composite:\n\t\t\t// For composite types acc is used to find the field in the local type,\n\t\t\t// and then we try to find a field in target with the same name.\n\t\t\tlocalMembers := localType.members()\n\t\t\tif acc >= len(localMembers) {\n\t\t\t\treturn coreField{}, coreField{}, fmt.Errorf(\"invalid accessor %d for %s\", acc, localType)\n\t\t\t}\n\n\t\t\tlocalMember := localMembers[acc]\n\t\t\tif localMember.Name == \"\" {\n\t\t\t\tlocalMemberType, ok := As[composite](localMember.Type)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn coreField{}, coreField{}, fmt.Errorf(\"unnamed field with type %s: %s\", localMember.Type, ErrNotSupported)\n\t\t\t\t}\n\n\t\t\t\t// This is an anonymous struct or union, ignore it.\n\t\t\t\tlocal = coreField{\n\t\t\t\t\tType:   localMemberType,\n\t\t\t\t\toffset: local.offset + localMember.Offset.Bytes(),\n\t\t\t\t}\n\t\t\t\tlocalMaybeFlex = false\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\ttargetType, ok := As[composite](target.Type)\n\t\t\tif !ok {\n\t\t\t\treturn coreField{}, coreField{}, fmt.Errorf(\"target not composite: %w\", errImpossibleRelocation)\n\t\t\t}\n\n\t\t\ttargetMember, last, err := coreFindMember(targetType, localMember.Name)\n\t\t\tif err != nil {\n\t\t\t\treturn coreField{}, coreField{}, err\n\t\t\t}\n\n\t\t\tlocal = coreField{\n\t\t\t\tType:         localMember.Type,\n\t\t\t\toffset:       local.offset,\n\t\t\t\tbitfieldSize: localMember.BitfieldSize,\n\t\t\t}\n\t\t\tlocalMaybeFlex = acc == len(localMembers)-1\n\n\t\t\ttarget = coreField{\n\t\t\t\tType:         targetMember.Type,\n\t\t\t\toffset:       target.offset,\n\t\t\t\tbitfieldSize: targetMember.BitfieldSize,\n\t\t\t}\n\t\t\ttargetMaybeFlex = last\n\n\t\t\tif local.bitfieldSize == 0 && target.bitfieldSize == 0 {\n\t\t\t\tlocal.offset += localMember.Offset.Bytes()\n\t\t\t\ttarget.offset += targetMember.Offset.Bytes()\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\t// Either of the members is a bitfield. Make sure we're at the\n\t\t\t// end of the accessor.\n\t\t\tif next := i + 1; next < len(localAcc[1:]) {\n\t\t\t\treturn coreField{}, coreField{}, fmt.Errorf(\"can't descend into bitfield\")\n\t\t\t}\n\n\t\t\tif err := local.adjustOffsetBits(localMember.Offset); err != nil {\n\t\t\t\treturn coreField{}, coreField{}, err\n\t\t\t}\n\n\t\t\tif err := target.adjustOffsetBits(targetMember.Offset); err != nil {\n\t\t\t\treturn coreField{}, coreField{}, err\n\t\t\t}\n\n\t\tcase *Array:\n\t\t\t// For arrays, acc is the index in the target.\n\t\t\ttargetType, ok := As[*Array](target.Type)\n\t\t\tif !ok {\n\t\t\t\treturn coreField{}, coreField{}, fmt.Errorf(\"target not array: %w\", errImpossibleRelocation)\n\t\t\t}\n\n\t\t\tif localType.Nelems == 0 && !localMaybeFlex {\n\t\t\t\treturn coreField{}, coreField{}, fmt.Errorf(\"local type has invalid flexible array\")\n\t\t\t}\n\t\t\tif targetType.Nelems == 0 && !targetMaybeFlex {\n\t\t\t\treturn coreField{}, coreField{}, fmt.Errorf(\"target type has invalid flexible array\")\n\t\t\t}\n\n\t\t\tif localType.Nelems > 0 && acc >= int(localType.Nelems) {\n\t\t\t\treturn coreField{}, coreField{}, fmt.Errorf(\"invalid access of %s at index %d\", localType, acc)\n\t\t\t}\n\t\t\tif targetType.Nelems > 0 && acc >= int(targetType.Nelems) {\n\t\t\t\treturn coreField{}, coreField{}, fmt.Errorf(\"out of bounds access of target: %w\", errImpossibleRelocation)\n\t\t\t}\n\n\t\t\tlocal = coreField{\n\t\t\t\tType:   localType.Type,\n\t\t\t\toffset: local.offset,\n\t\t\t}\n\t\t\tlocalMaybeFlex = false\n\n\t\t\tif err := local.adjustOffsetToNthElement(acc); err != nil {\n\t\t\t\treturn coreField{}, coreField{}, err\n\t\t\t}\n\n\t\t\ttarget = coreField{\n\t\t\t\tType:   targetType.Type,\n\t\t\t\toffset: target.offset,\n\t\t\t}\n\t\t\ttargetMaybeFlex = false\n\n\t\t\tif err := target.adjustOffsetToNthElement(acc); err != nil {\n\t\t\t\treturn coreField{}, coreField{}, err\n\t\t\t}\n\n\t\tdefault:\n\t\t\treturn coreField{}, coreField{}, fmt.Errorf(\"relocate field of %T: %w\", localType, ErrNotSupported)\n\t\t}\n\n\t\tif err := coreAreMembersCompatible(local.Type, target.Type); err != nil {\n\t\t\treturn coreField{}, coreField{}, err\n\t\t}\n\t}\n\n\treturn local, target, nil\n}\n\n// coreFindMember finds a member in a composite type while handling anonymous\n// structs and unions.\nfunc coreFindMember(typ composite, name string) (Member, bool, error) {\n\tif name == \"\" {\n\t\treturn Member{}, false, errors.New(\"can't search for anonymous member\")\n\t}\n\n\ttype offsetTarget struct {\n\t\tcomposite\n\t\toffset Bits\n\t}\n\n\ttargets := []offsetTarget{{typ, 0}}\n\tvisited := make(map[composite]bool)\n\n\tfor i := 0; i < len(targets); i++ {\n\t\ttarget := targets[i]\n\n\t\t// Only visit targets once to prevent infinite recursion.\n\t\tif visited[target] {\n\t\t\tcontinue\n\t\t}\n\t\tif len(visited) >= maxResolveDepth {\n\t\t\t// This check is different than libbpf, which restricts the entire\n\t\t\t// path to BPF_CORE_SPEC_MAX_LEN items.\n\t\t\treturn Member{}, false, fmt.Errorf(\"type is nested too deep\")\n\t\t}\n\t\tvisited[target] = true\n\n\t\tmembers := target.members()\n\t\tfor j, member := range members {\n\t\t\tif member.Name == name {\n\t\t\t\t// NB: This is safe because member is a copy.\n\t\t\t\tmember.Offset += target.offset\n\t\t\t\treturn member, j == len(members)-1, nil\n\t\t\t}\n\n\t\t\t// The names don't match, but this member could be an anonymous struct\n\t\t\t// or union.\n\t\t\tif member.Name != \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tcomp, ok := As[composite](member.Type)\n\t\t\tif !ok {\n\t\t\t\treturn Member{}, false, fmt.Errorf(\"anonymous non-composite type %T not allowed\", member.Type)\n\t\t\t}\n\n\t\t\ttargets = append(targets, offsetTarget{comp, target.offset + member.Offset})\n\t\t}\n\t}\n\n\treturn Member{}, false, fmt.Errorf(\"no matching member: %w\", errImpossibleRelocation)\n}\n\n// coreFindEnumValue follows localAcc to find the equivalent enum value in target.\nfunc coreFindEnumValue(local Type, localAcc coreAccessor, target Type) (localValue, targetValue *EnumValue, _ error) {\n\tlocalValue, err := localAcc.enumValue(local)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\ttargetEnum, ok := As[*Enum](target)\n\tif !ok {\n\t\treturn nil, nil, errImpossibleRelocation\n\t}\n\n\tlocalName := newEssentialName(localValue.Name)\n\tfor i, targetValue := range targetEnum.Values {\n\t\tif newEssentialName(targetValue.Name) != localName {\n\t\t\tcontinue\n\t\t}\n\n\t\treturn localValue, &targetEnum.Values[i], nil\n\t}\n\n\treturn nil, nil, errImpossibleRelocation\n}\n\n// CheckTypeCompatibility checks local and target types for Compatibility according to CO-RE rules.\n//\n// Only layout compatibility is checked, ignoring names of the root type.\nfunc CheckTypeCompatibility(localType Type, targetType Type) error {\n\treturn coreAreTypesCompatible(localType, targetType, nil)\n}\n\ntype pair struct {\n\tA, B Type\n}\n\n/* The comment below is from bpf_core_types_are_compat in libbpf.c:\n *\n * Check local and target types for compatibility. This check is used for\n * type-based CO-RE relocations and follow slightly different rules than\n * field-based relocations. This function assumes that root types were already\n * checked for name match. Beyond that initial root-level name check, names\n * are completely ignored. Compatibility rules are as follows:\n *   - any two STRUCTs/UNIONs/FWDs/ENUMs/INTs are considered compatible, but\n *     kind should match for local and target types (i.e., STRUCT is not\n *     compatible with UNION);\n *   - for ENUMs, the size is ignored;\n *   - for INT, size and signedness are ignored;\n *   - for ARRAY, dimensionality is ignored, element types are checked for\n *     compatibility recursively;\n *   - CONST/VOLATILE/RESTRICT modifiers are ignored;\n *   - TYPEDEFs/PTRs are compatible if types they pointing to are compatible;\n *   - FUNC_PROTOs are compatible if they have compatible signature: same\n *     number of input args and compatible return and argument types.\n * These rules are not set in stone and probably will be adjusted as we get\n * more experience with using BPF CO-RE relocations.\n *\n * Returns errIncompatibleTypes if types are not compatible.\n */\nfunc coreAreTypesCompatible(localType Type, targetType Type, visited map[pair]struct{}) error {\n\tlocalType = UnderlyingType(localType)\n\ttargetType = UnderlyingType(targetType)\n\n\tif reflect.TypeOf(localType) != reflect.TypeOf(targetType) {\n\t\treturn fmt.Errorf(\"type mismatch between %v and %v: %w\", localType, targetType, errIncompatibleTypes)\n\t}\n\n\tif _, ok := visited[pair{localType, targetType}]; ok {\n\t\treturn nil\n\t}\n\tif visited == nil {\n\t\tvisited = make(map[pair]struct{})\n\t}\n\tvisited[pair{localType, targetType}] = struct{}{}\n\n\tswitch lv := localType.(type) {\n\tcase *Void, *Struct, *Union, *Enum, *Fwd, *Int:\n\t\treturn nil\n\n\tcase *Pointer:\n\t\ttv := targetType.(*Pointer)\n\t\treturn coreAreTypesCompatible(lv.Target, tv.Target, visited)\n\n\tcase *Array:\n\t\ttv := targetType.(*Array)\n\t\tif err := coreAreTypesCompatible(lv.Index, tv.Index, visited); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn coreAreTypesCompatible(lv.Type, tv.Type, visited)\n\n\tcase *FuncProto:\n\t\ttv := targetType.(*FuncProto)\n\t\tif err := coreAreTypesCompatible(lv.Return, tv.Return, visited); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif len(lv.Params) != len(tv.Params) {\n\t\t\treturn fmt.Errorf(\"function param mismatch: %w\", errIncompatibleTypes)\n\t\t}\n\n\t\tfor i, localParam := range lv.Params {\n\t\t\ttargetParam := tv.Params[i]\n\t\t\tif err := coreAreTypesCompatible(localParam.Type, targetParam.Type, visited); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\treturn nil\n\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported type %T\", localType)\n\t}\n}\n\n/* coreAreMembersCompatible checks two types for field-based relocation compatibility.\n *\n * The comment below is from bpf_core_fields_are_compat in libbpf.c:\n *\n * Check two types for compatibility for the purpose of field access\n * relocation. const/volatile/restrict and typedefs are skipped to ensure we\n * are relocating semantically compatible entities:\n *   - any two STRUCTs/UNIONs are compatible and can be mixed;\n *   - any two FWDs are compatible, if their names match (modulo flavor suffix);\n *   - any two PTRs are always compatible;\n *   - for ENUMs, names should be the same (ignoring flavor suffix) or at\n *     least one of enums should be anonymous;\n *   - for ENUMs, check sizes, names are ignored;\n *   - for INT, size and signedness are ignored;\n *   - any two FLOATs are always compatible;\n *   - for ARRAY, dimensionality is ignored, element types are checked for\n *     compatibility recursively;\n *     [ NB: coreAreMembersCompatible doesn't recurse, this check is done\n *       by coreFindField. ]\n *   - everything else shouldn't be ever a target of relocation.\n * These rules are not set in stone and probably will be adjusted as we get\n * more experience with using BPF CO-RE relocations.\n *\n * Returns errImpossibleRelocation if the members are not compatible.\n */\nfunc coreAreMembersCompatible(localType Type, targetType Type) error {\n\tlocalType = UnderlyingType(localType)\n\ttargetType = UnderlyingType(targetType)\n\n\t_, lok := localType.(composite)\n\t_, tok := targetType.(composite)\n\tif lok && tok {\n\t\treturn nil\n\t}\n\n\tif reflect.TypeOf(localType) != reflect.TypeOf(targetType) {\n\t\treturn fmt.Errorf(\"type mismatch: %w\", errImpossibleRelocation)\n\t}\n\n\tswitch lv := localType.(type) {\n\tcase *Array, *Pointer, *Float, *Int:\n\t\treturn nil\n\n\tcase *Enum:\n\t\ttv := targetType.(*Enum)\n\t\tif !coreEssentialNamesMatch(lv.Name, tv.Name) {\n\t\t\treturn fmt.Errorf(\"names %q and %q don't match: %w\", lv.Name, tv.Name, errImpossibleRelocation)\n\t\t}\n\n\t\treturn nil\n\n\tcase *Fwd:\n\t\ttv := targetType.(*Fwd)\n\t\tif !coreEssentialNamesMatch(lv.Name, tv.Name) {\n\t\t\treturn fmt.Errorf(\"names %q and %q don't match: %w\", lv.Name, tv.Name, errImpossibleRelocation)\n\t\t}\n\n\t\treturn nil\n\n\tdefault:\n\t\treturn fmt.Errorf(\"type %s: %w\", localType, ErrNotSupported)\n\t}\n}\n\n// coreEssentialNamesMatch compares two names while ignoring their flavour suffix.\n//\n// This should only be used on names which are in the global scope, like struct\n// names, typedefs or enum values.\nfunc coreEssentialNamesMatch(a, b string) bool {\n\tif a == \"\" || b == \"\" {\n\t\t// allow anonymous and named type to match\n\t\treturn true\n\t}\n\n\treturn newEssentialName(a) == newEssentialName(b)\n}\n\n/* The comment below is from __bpf_core_types_match in relo_core.c:\n *\n * Check that two types \"match\". This function assumes that root types were\n * already checked for name match.\n *\n * The matching relation is defined as follows:\n * - modifiers and typedefs are stripped (and, hence, effectively ignored)\n * - generally speaking types need to be of same kind (struct vs. struct, union\n *   vs. union, etc.)\n *   - exceptions are struct/union behind a pointer which could also match a\n *     forward declaration of a struct or union, respectively, and enum vs.\n *     enum64 (see below)\n * Then, depending on type:\n * - integers:\n *   - match if size and signedness match\n * - arrays & pointers:\n *   - target types are recursively matched\n * - structs & unions:\n *   - local members need to exist in target with the same name\n *   - for each member we recursively check match unless it is already behind a\n *     pointer, in which case we only check matching names and compatible kind\n * - enums:\n *   - local variants have to have a match in target by symbolic name (but not\n *     numeric value)\n *   - size has to match (but enum may match enum64 and vice versa)\n * - function pointers:\n *   - number and position of arguments in local type has to match target\n *   - for each argument and the return value we recursively check match\n */\nfunc coreTypesMatch(localType Type, targetType Type, visited map[pair]struct{}) error {\n\tlocalType = UnderlyingType(localType)\n\ttargetType = UnderlyingType(targetType)\n\n\tif !coreEssentialNamesMatch(localType.TypeName(), targetType.TypeName()) {\n\t\treturn fmt.Errorf(\"type name %q don't match %q: %w\", localType.TypeName(), targetType.TypeName(), errIncompatibleTypes)\n\t}\n\n\tif reflect.TypeOf(localType) != reflect.TypeOf(targetType) {\n\t\treturn fmt.Errorf(\"type mismatch between %v and %v: %w\", localType, targetType, errIncompatibleTypes)\n\t}\n\n\tif _, ok := visited[pair{localType, targetType}]; ok {\n\t\treturn nil\n\t}\n\tif visited == nil {\n\t\tvisited = make(map[pair]struct{})\n\t}\n\tvisited[pair{localType, targetType}] = struct{}{}\n\n\tswitch lv := (localType).(type) {\n\tcase *Void:\n\n\tcase *Fwd:\n\t\tif targetType.(*Fwd).Kind != lv.Kind {\n\t\t\treturn fmt.Errorf(\"fwd kind mismatch between %v and %v: %w\", localType, targetType, errIncompatibleTypes)\n\t\t}\n\n\tcase *Enum:\n\t\treturn coreEnumsMatch(lv, targetType.(*Enum))\n\n\tcase composite:\n\t\ttv := targetType.(composite)\n\n\t\tif len(lv.members()) > len(tv.members()) {\n\t\t\treturn errIncompatibleTypes\n\t\t}\n\n\t\tlocalMembers := lv.members()\n\t\ttargetMembers := map[string]Member{}\n\t\tfor _, member := range tv.members() {\n\t\t\ttargetMembers[member.Name] = member\n\t\t}\n\n\t\tfor _, localMember := range localMembers {\n\t\t\ttargetMember, found := targetMembers[localMember.Name]\n\t\t\tif !found {\n\t\t\t\treturn fmt.Errorf(\"no field %q in %v: %w\", localMember.Name, targetType, errIncompatibleTypes)\n\t\t\t}\n\n\t\t\terr := coreTypesMatch(localMember.Type, targetMember.Type, visited)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\tcase *Int:\n\t\tif !coreEncodingMatches(lv, targetType.(*Int)) {\n\t\t\treturn fmt.Errorf(\"int mismatch between %v and %v: %w\", localType, targetType, errIncompatibleTypes)\n\t\t}\n\n\tcase *Pointer:\n\t\ttv := targetType.(*Pointer)\n\n\t\t// Allow a pointer to a forward declaration to match a struct\n\t\t// or union.\n\t\tif fwd, ok := As[*Fwd](lv.Target); ok && fwd.matches(tv.Target) {\n\t\t\treturn nil\n\t\t}\n\n\t\tif fwd, ok := As[*Fwd](tv.Target); ok && fwd.matches(lv.Target) {\n\t\t\treturn nil\n\t\t}\n\n\t\treturn coreTypesMatch(lv.Target, tv.Target, visited)\n\n\tcase *Array:\n\t\ttv := targetType.(*Array)\n\n\t\tif lv.Nelems != tv.Nelems {\n\t\t\treturn fmt.Errorf(\"array mismatch between %v and %v: %w\", localType, targetType, errIncompatibleTypes)\n\t\t}\n\n\t\treturn coreTypesMatch(lv.Type, tv.Type, visited)\n\n\tcase *FuncProto:\n\t\ttv := targetType.(*FuncProto)\n\n\t\tif len(lv.Params) != len(tv.Params) {\n\t\t\treturn fmt.Errorf(\"function param mismatch: %w\", errIncompatibleTypes)\n\t\t}\n\n\t\tfor i, lparam := range lv.Params {\n\t\t\tif err := coreTypesMatch(lparam.Type, tv.Params[i].Type, visited); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\treturn coreTypesMatch(lv.Return, tv.Return, visited)\n\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported type %T\", localType)\n\t}\n\n\treturn nil\n}\n\n// coreEncodingMatches returns true if both ints have the same size and signedness.\n// All encodings other than `Signed` are considered unsigned.\nfunc coreEncodingMatches(local, target *Int) bool {\n\treturn local.Size == target.Size && (local.Encoding == Signed) == (target.Encoding == Signed)\n}\n\n// coreEnumsMatch checks two enums match, which is considered to be the case if the following is true:\n// - size has to match (but enum may match enum64 and vice versa)\n// - local variants have to have a match in target by symbolic name (but not numeric value)\nfunc coreEnumsMatch(local *Enum, target *Enum) error {\n\tif local.Size != target.Size {\n\t\treturn fmt.Errorf(\"size mismatch between %v and %v: %w\", local, target, errIncompatibleTypes)\n\t}\n\n\t// If there are more values in the local than the target, there must be at least one value in the local\n\t// that isn't in the target, and therefor the types are incompatible.\n\tif len(local.Values) > len(target.Values) {\n\t\treturn fmt.Errorf(\"local has more values than target: %w\", errIncompatibleTypes)\n\t}\n\nouter:\n\tfor _, lv := range local.Values {\n\t\tfor _, rv := range target.Values {\n\t\t\tif coreEssentialNamesMatch(lv.Name, rv.Name) {\n\t\t\t\tcontinue outer\n\t\t\t}\n\t\t}\n\n\t\treturn fmt.Errorf(\"no match for %v in %v: %w\", lv, target, errIncompatibleTypes)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "btf/core_reloc_test.go",
    "content": "package btf_test\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"os\"\n\t\"slices\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestCORERelocationLoad(t *testing.T) {\n\tfile := testutils.NativeFile(t, \"testdata/relocs-%s.elf\")\n\tfh, err := os.Open(file)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer fh.Close()\n\n\tspec, err := ebpf.LoadCollectionSpecFromReader(fh)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tfor _, progSpec := range spec.Programs {\n\t\tt.Run(progSpec.Name, func(t *testing.T) {\n\t\t\tif _, err := fh.Seek(0, io.SeekStart); err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\n\t\t\tprog, err := ebpf.NewProgramWithOptions(progSpec, ebpf.ProgramOptions{\n\t\t\t\tKernelTypes: spec.Types,\n\t\t\t})\n\t\t\ttestutils.SkipIfNotSupported(t, err)\n\n\t\t\tif strings.HasPrefix(progSpec.Name, \"err_\") {\n\t\t\t\tif err == nil {\n\t\t\t\t\tprog.Close()\n\t\t\t\t\tt.Fatal(\"Expected an error\")\n\t\t\t\t}\n\t\t\t\tt.Log(\"Got expected error:\", err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(\"Load program:\", err)\n\t\t\t}\n\t\t\tdefer prog.Close()\n\n\t\t\tret, _, err := prog.Test(internal.EmptyBPFContext)\n\t\t\ttestutils.SkipIfNotSupported(t, err)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(\"Error when running:\", err)\n\t\t\t}\n\n\t\t\tif ret != 0 {\n\t\t\t\tt.Error(\"Assertion failed on line\", ret)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestCORERelocationRead(t *testing.T) {\n\tfile := testutils.NativeFile(t, \"testdata/relocs_read-%s.elf\")\n\tspec, err := ebpf.LoadCollectionSpec(file)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\ttargetFile := testutils.NativeFile(t, \"testdata/relocs_read_tgt-%s.elf\")\n\ttargetSpec, err := btf.LoadSpec(targetFile)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\ttests := []struct {\n\t\tname string\n\t\topts ebpf.ProgramOptions\n\t}{\n\t\t{\n\t\t\tname: \"KernelTypes\",\n\t\t\topts: ebpf.ProgramOptions{\n\t\t\t\tKernelTypes: targetSpec,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"ExtraRelocationTargets\",\n\t\t\topts: ebpf.ProgramOptions{\n\t\t\t\tExtraRelocationTargets: []*btf.Spec{targetSpec},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, progSpec := range spec.Programs {\n\t\tfor _, test := range tests {\n\t\t\tt.Run(progSpec.Name+\"_\"+test.name, func(t *testing.T) {\n\t\t\t\tprog, err := ebpf.NewProgramWithOptions(progSpec, test.opts)\n\t\t\t\ttestutils.SkipIfNotSupported(t, err)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatal(\"Load program:\", err)\n\t\t\t\t}\n\t\t\t\tdefer prog.Close()\n\n\t\t\t\tret, _, err := prog.Test(internal.EmptyBPFContext)\n\t\t\t\ttestutils.SkipIfNotSupported(t, err)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatal(\"Error when running:\", err)\n\t\t\t\t}\n\n\t\t\t\tif ret != 0 {\n\t\t\t\t\tt.Error(\"Assertion failed on line\", ret)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t}\n}\n\nfunc TestLD64IMMReloc(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.4\", \"vmlinux BTF in sysfs\")\n\n\tfile := testutils.NativeFile(t, \"testdata/relocs_enum-%s.elf\")\n\tfh, err := os.Open(file)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer fh.Close()\n\n\tspec, err := ebpf.LoadCollectionSpecFromReader(fh)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tcoll, err := ebpf.NewCollection(spec)\n\ttestutils.SkipIfNotSupportedOnOS(t, err)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer coll.Close()\n}\n\nfunc TestCOREPoisonLineInfo(t *testing.T) {\n\tspec, err := ebpf.LoadCollectionSpec(testutils.NativeFile(t, \"../testdata/errors-%s.elf\"))\n\tqt.Assert(t, qt.IsNil(err))\n\n\tvar b btf.Builder\n\traw, err := b.Marshal(nil, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\tempty, err := btf.LoadSpecFromReader(bytes.NewReader(raw))\n\tqt.Assert(t, qt.IsNil(err))\n\n\tfor _, test := range []struct {\n\t\tname string\n\t}{\n\t\t{\"poisoned_single\"},\n\t\t{\"poisoned_double\"},\n\t} {\n\t\tprogSpec := spec.Programs[test.name]\n\t\tqt.Assert(t, qt.IsNotNil(progSpec))\n\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tt.Log(progSpec.Instructions)\n\t\t\t_, err := ebpf.NewProgramWithOptions(progSpec, ebpf.ProgramOptions{\n\t\t\t\tKernelTypes: empty,\n\t\t\t})\n\t\t\ttestutils.SkipIfNotSupported(t, err)\n\n\t\t\tvar ve *ebpf.VerifierError\n\t\t\tqt.Assert(t, qt.ErrorAs(err, &ve))\n\t\t\tfound := slices.ContainsFunc(ve.Log, func(line string) bool {\n\t\t\t\treturn strings.HasPrefix(line, \"; instruction poisoned by CO-RE\")\n\t\t\t})\n\t\t\tqt.Assert(t, qt.IsTrue(found))\n\t\t\tt.Logf(\"%-5v\", ve)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "btf/core_test.go",
    "content": "package btf\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"slices\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestCheckTypeCompatibility(t *testing.T) {\n\ttests := []struct {\n\t\ta, b       Type\n\t\tcompatible bool\n\t}{\n\t\t{&Void{}, &Void{}, true},\n\t\t{&Struct{Name: \"a\"}, &Struct{Name: \"b\"}, true},\n\t\t{&Union{Name: \"a\"}, &Union{Name: \"b\"}, true},\n\t\t{&Union{Name: \"a\"}, &Struct{Name: \"b\"}, false},\n\t\t{&Enum{Name: \"a\"}, &Enum{Name: \"b\"}, true},\n\t\t{&Fwd{Name: \"a\"}, &Fwd{Name: \"b\"}, true},\n\t\t{&Int{Name: \"a\", Size: 2}, &Int{Name: \"b\", Size: 4}, true},\n\t\t{&Pointer{Target: &Void{}}, &Pointer{Target: &Void{}}, true},\n\t\t{&Pointer{Target: &Void{}}, &Void{}, false},\n\t\t{&Array{Index: &Void{}, Type: &Void{}}, &Array{Index: &Void{}, Type: &Void{}}, true},\n\t\t{&Array{Index: &Void{}, Type: &Int{}}, &Array{Index: &Void{}, Type: &Void{}}, false},\n\t\t{&FuncProto{Return: &Int{}}, &FuncProto{Return: &Void{}}, false},\n\t\t{\n\t\t\t&FuncProto{Return: &Void{}, Params: []FuncParam{{Name: \"a\", Type: &Void{}}}},\n\t\t\t&FuncProto{Return: &Void{}, Params: []FuncParam{{Name: \"b\", Type: &Void{}}}},\n\t\t\ttrue,\n\t\t},\n\t\t{\n\t\t\t&FuncProto{Return: &Void{}, Params: []FuncParam{{Type: &Void{}}}},\n\t\t\t&FuncProto{Return: &Void{}, Params: []FuncParam{{Type: &Int{}}}},\n\t\t\tfalse,\n\t\t},\n\t\t{\n\t\t\t&FuncProto{Return: &Void{}, Params: []FuncParam{{Type: &Void{}}, {Type: &Void{}}}},\n\t\t\t&FuncProto{Return: &Void{}, Params: []FuncParam{{Type: &Void{}}}},\n\t\t\tfalse,\n\t\t},\n\t\t{&FuncProto{Return: &Typedef{Type: &Int{}}}, &FuncProto{Return: &Int{}}, true},\n\t\t{&FuncProto{Return: &Typedef{Type: &Int{}}}, &FuncProto{Return: &Void{}}, false},\n\t}\n\n\tfor _, test := range tests {\n\t\terr := CheckTypeCompatibility(test.a, test.b)\n\t\tif test.compatible {\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"Expected types to be compatible: %s\\na = %#v\\nb = %#v\", err, test.a, test.b)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t} else {\n\t\t\tif !errors.Is(err, errIncompatibleTypes) {\n\t\t\t\tt.Errorf(\"Expected types to be incompatible: %s\\na = %#v\\nb = %#v\", err, test.a, test.b)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\terr = CheckTypeCompatibility(test.b, test.a)\n\t\tif test.compatible {\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"Expected reversed types to be compatible: %s\\na = %#v\\nb = %#v\", err, test.a, test.b)\n\t\t\t}\n\t\t} else {\n\t\t\tif !errors.Is(err, errIncompatibleTypes) {\n\t\t\t\tt.Errorf(\"Expected reversed types to be incompatible: %s\\na = %#v\\nb = %#v\", err, test.a, test.b)\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, invalid := range []Type{&Var{}, &Datasec{}} {\n\t\terr := CheckTypeCompatibility(invalid, invalid)\n\t\tif errors.Is(err, errIncompatibleTypes) {\n\t\t\tt.Errorf(\"Expected an error for %T, not errIncompatibleTypes\", invalid)\n\t\t} else if err == nil {\n\t\t\tt.Errorf(\"Expected an error for %T\", invalid)\n\t\t}\n\t}\n}\n\nfunc TestCOREAreMembersCompatible(t *testing.T) {\n\ttests := []struct {\n\t\ta, b       Type\n\t\tcompatible bool\n\t}{\n\t\t{&Struct{Name: \"a\"}, &Struct{Name: \"b\"}, true},\n\t\t{&Union{Name: \"a\"}, &Union{Name: \"b\"}, true},\n\t\t{&Union{Name: \"a\"}, &Struct{Name: \"b\"}, true},\n\t\t{&Enum{Name: \"a\"}, &Enum{Name: \"b\"}, false},\n\t\t{&Enum{Name: \"a\"}, &Enum{Name: \"a___foo\"}, true},\n\t\t{&Enum{Name: \"a\"}, &Enum{Name: \"\"}, true},\n\t\t{&Fwd{Name: \"a\"}, &Fwd{Name: \"b\"}, false},\n\t\t{&Fwd{Name: \"a\"}, &Fwd{Name: \"a___foo\"}, true},\n\t\t{&Fwd{Name: \"a\"}, &Fwd{Name: \"\"}, true},\n\t\t{&Int{Name: \"a\", Size: 2}, &Int{Name: \"b\", Size: 4}, true},\n\t\t{&Pointer{Target: &Void{}}, &Pointer{Target: &Void{}}, true},\n\t\t{&Pointer{Target: &Void{}}, &Void{}, false},\n\t\t{&Array{Type: &Int{Size: 1}}, &Array{Type: &Int{Encoding: Signed}}, true},\n\t\t{&Float{Size: 2}, &Float{Size: 4}, true},\n\t}\n\n\tfor _, test := range tests {\n\t\terr := coreAreMembersCompatible(test.a, test.b)\n\t\tif test.compatible {\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"Expected members to be compatible: %s\\na = %#v\\nb = %#v\", err, test.a, test.b)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t} else {\n\t\t\tif !errors.Is(err, errImpossibleRelocation) {\n\t\t\t\tt.Errorf(\"Expected members to be incompatible: %s\\na = %#v\\nb = %#v\", err, test.a, test.b)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\terr = coreAreMembersCompatible(test.b, test.a)\n\t\tif test.compatible {\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"Expected reversed members to be compatible: %s\\na = %#v\\nb = %#v\", err, test.a, test.b)\n\t\t\t}\n\t\t} else {\n\t\t\tif !errors.Is(err, errImpossibleRelocation) {\n\t\t\t\tt.Errorf(\"Expected reversed members to be incompatible: %s\\na = %#v\\nb = %#v\", err, test.a, test.b)\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, invalid := range []Type{&Void{}, &FuncProto{}, &Var{}, &Datasec{}} {\n\t\terr := coreAreMembersCompatible(invalid, invalid)\n\t\tif errors.Is(err, errImpossibleRelocation) {\n\t\t\tt.Errorf(\"Expected an error for %T, not errImpossibleRelocation\", invalid)\n\t\t} else if err == nil {\n\t\t\tt.Errorf(\"Expected an error for %T\", invalid)\n\t\t}\n\t}\n}\n\nfunc TestCOREAccessor(t *testing.T) {\n\tfor _, valid := range []string{\n\t\t\"0\",\n\t\t\"1:0\",\n\t\t\"1:0:3:34:10:1\",\n\t} {\n\t\t_, err := parseCOREAccessor(valid)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Parse %q: %s\", valid, err)\n\t\t}\n\t}\n\n\tfor _, invalid := range []string{\n\t\t\"\",\n\t\t\"-1\",\n\t\t\":\",\n\t\t\"0:\",\n\t\t\":12\",\n\t\t\"4294967296\",\n\t} {\n\t\t_, err := parseCOREAccessor(invalid)\n\t\tif err == nil {\n\t\t\tt.Errorf(\"Accepted invalid accessor %q\", invalid)\n\t\t}\n\t}\n}\n\nfunc TestCOREFindEnumValue(t *testing.T) {\n\ta := &Enum{Values: []EnumValue{{\"foo\", 23}, {\"bar\", 42}}}\n\tb := &Enum{Values: []EnumValue{\n\t\t{\"foo___flavour\", 0},\n\t\t{\"bar\", 123},\n\t\t{\"garbage\", 3},\n\t}}\n\n\tinvalid := []struct {\n\t\tname   string\n\t\tlocal  Type\n\t\ttarget Type\n\t\tacc    coreAccessor\n\t\terr    error\n\t}{\n\t\t{\"o-o-b accessor\", a, b, coreAccessor{len(a.Values)}, nil},\n\t\t{\"long accessor\", a, b, coreAccessor{0, 1}, nil},\n\t\t{\"wrong target\", a, &Void{}, coreAccessor{0, 1}, nil},\n\t\t{\n\t\t\t\"no matching value\",\n\t\t\tb, a,\n\t\t\tcoreAccessor{2},\n\t\t\terrImpossibleRelocation,\n\t\t},\n\t}\n\n\tfor _, test := range invalid {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\t_, _, err := coreFindEnumValue(test.local, test.acc, test.target)\n\t\t\tif test.err != nil && !errors.Is(err, test.err) {\n\t\t\t\tt.Fatalf(\"Expected %s, got %s\", test.err, err)\n\t\t\t}\n\t\t\tif err == nil {\n\t\t\t\tt.Fatal(\"Accepted invalid case\")\n\t\t\t}\n\t\t})\n\t}\n\n\tvalid := []struct {\n\t\tname                    string\n\t\tlocal, target           Type\n\t\tacc                     coreAccessor\n\t\tlocalValue, targetValue uint64\n\t}{\n\t\t{\"a to b\", a, b, coreAccessor{0}, 23, 0},\n\t\t{\"b to a\", b, a, coreAccessor{1}, 123, 42},\n\t}\n\n\tfor _, test := range valid {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tlocal, target, err := coreFindEnumValue(test.local, test.acc, test.target)\n\t\t\tqt.Assert(t, qt.IsNil(err))\n\t\t\tqt.Check(t, qt.Equals(local.Value, test.localValue))\n\t\t\tqt.Check(t, qt.Equals(target.Value, test.targetValue))\n\t\t})\n\t}\n}\n\nfunc TestCOREFindField(t *testing.T) {\n\tptr := &Pointer{}\n\tu16 := &Int{Size: 2}\n\tu32 := &Int{Size: 4}\n\taFields := []Member{\n\t\t{Name: \"foo\", Type: ptr, Offset: 8},\n\t\t{Name: \"bar\", Type: u16, Offset: 16},\n\t\t{Name: \"baz\", Type: u32, Offset: 32, BitfieldSize: 3},\n\t\t{Name: \"quux\", Type: u32, Offset: 35, BitfieldSize: 10},\n\t\t{Name: \"quuz\", Type: u32, Offset: 45, BitfieldSize: 8},\n\t}\n\tbFields := []Member{\n\t\t{Name: \"foo\", Type: ptr, Offset: 16},\n\t\t{Name: \"bar\", Type: u32, Offset: 8},\n\t\t{Name: \"other\", Offset: 4},\n\t\t// baz is separated out from the other bitfields\n\t\t{Name: \"baz\", Type: u32, Offset: 64, BitfieldSize: 3},\n\t\t// quux's type changes u32->u16\n\t\t{Name: \"quux\", Type: u16, Offset: 96, BitfieldSize: 10},\n\t\t// quuz becomes a normal field\n\t\t{Name: \"quuz\", Type: u16, Offset: 112},\n\t}\n\n\taStruct := &Struct{Members: aFields, Size: 48}\n\tbStruct := &Struct{Members: bFields, Size: 80}\n\taArray := &Array{Nelems: 4, Type: u16}\n\tbArray := &Array{Nelems: 3, Type: u32}\n\n\tinvalid := []struct {\n\t\tname          string\n\t\tlocal, target Type\n\t\tacc           coreAccessor\n\t\terr           error\n\t}{\n\t\t{\n\t\t\t\"unsupported type\",\n\t\t\t&Void{}, &Void{},\n\t\t\tcoreAccessor{0, 0},\n\t\t\tErrNotSupported,\n\t\t},\n\t\t{\n\t\t\t\"different types\",\n\t\t\t&Union{}, &Array{Type: u16},\n\t\t\tcoreAccessor{0},\n\t\t\terrImpossibleRelocation,\n\t\t},\n\t\t{\n\t\t\t\"invalid composite accessor\",\n\t\t\taStruct, aStruct,\n\t\t\tcoreAccessor{0, len(aStruct.Members)},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"invalid array accessor\",\n\t\t\taArray, aArray,\n\t\t\tcoreAccessor{0, int(aArray.Nelems)},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"o-o-b array accessor\",\n\t\t\taArray, bArray,\n\t\t\tcoreAccessor{0, int(bArray.Nelems)},\n\t\t\terrImpossibleRelocation,\n\t\t},\n\t\t{\n\t\t\t\"no match\",\n\t\t\tbStruct, aStruct,\n\t\t\tcoreAccessor{0, 2},\n\t\t\terrImpossibleRelocation,\n\t\t},\n\t\t{\n\t\t\t\"incompatible match\",\n\t\t\t&Union{Members: []Member{{Name: \"foo\", Type: &Pointer{}}}},\n\t\t\t&Union{Members: []Member{{Name: \"foo\", Type: &Int{}}}},\n\t\t\tcoreAccessor{0, 0},\n\t\t\terrImpossibleRelocation,\n\t\t},\n\t\t{\n\t\t\t\"unsized type\",\n\t\t\tbStruct, &Func{},\n\t\t\t// non-zero accessor to force calculating the offset.\n\t\t\tcoreAccessor{1},\n\t\t\terrImpossibleRelocation,\n\t\t},\n\t}\n\n\tfor _, test := range invalid {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\t_, _, err := coreFindField(test.local, test.acc, test.target)\n\t\t\tif test.err != nil && !errors.Is(err, test.err) {\n\t\t\t\tt.Fatalf(\"Expected %s, got %s\", test.err, err)\n\t\t\t}\n\t\t\tif err == nil {\n\t\t\t\tt.Fatal(\"Accepted invalid case\")\n\t\t\t}\n\t\t\tt.Log(err)\n\t\t})\n\t}\n\n\tbytes := func(typ Type) uint32 {\n\t\tsz, err := Sizeof(typ)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\treturn uint32(sz)\n\t}\n\n\tanon := func(t Type, offset Bits) []Member {\n\t\treturn []Member{{Type: t, Offset: offset}}\n\t}\n\n\tanonStruct := func(m ...Member) Member {\n\t\treturn Member{Type: &Struct{Members: m}}\n\t}\n\n\tanonUnion := func(m ...Member) Member {\n\t\treturn Member{Type: &Union{Members: m}}\n\t}\n\n\tvalid := []struct {\n\t\tname                    string\n\t\tlocal                   Type\n\t\ttarget                  Type\n\t\tacc                     coreAccessor\n\t\tlocalField, targetField coreField\n\t}{\n\t\t{\n\t\t\t\"array[0]\",\n\t\t\taArray,\n\t\t\tbArray,\n\t\t\tcoreAccessor{0, 0},\n\t\t\tcoreField{u16, 0, 0, 0},\n\t\t\tcoreField{u32, 0, 0, 0},\n\t\t},\n\t\t{\n\t\t\t\"array[1]\",\n\t\t\taArray,\n\t\t\tbArray,\n\t\t\tcoreAccessor{0, 1},\n\t\t\tcoreField{u16, bytes(aArray.Type), 0, 0},\n\t\t\tcoreField{u32, bytes(bArray.Type), 0, 0},\n\t\t},\n\t\t{\n\t\t\t\"array[0] with base offset\",\n\t\t\taArray,\n\t\t\tbArray,\n\t\t\tcoreAccessor{1, 0},\n\t\t\tcoreField{u16, bytes(aArray), 0, 0},\n\t\t\tcoreField{u32, bytes(bArray), 0, 0},\n\t\t},\n\t\t{\n\t\t\t\"array[2] with base offset\",\n\t\t\taArray,\n\t\t\tbArray,\n\t\t\tcoreAccessor{1, 2},\n\t\t\tcoreField{u16, bytes(aArray) + 2*bytes(aArray.Type), 0, 0},\n\t\t\tcoreField{u32, bytes(bArray) + 2*bytes(bArray.Type), 0, 0},\n\t\t},\n\t\t{\n\t\t\t\"flex array\",\n\t\t\t&Struct{Members: []Member{{Name: \"foo\", Type: &Array{Nelems: 0, Type: u16}}}},\n\t\t\t&Struct{Members: []Member{{Name: \"foo\", Type: &Array{Nelems: 0, Type: u32}}}},\n\t\t\tcoreAccessor{0, 0, 9000},\n\t\t\tcoreField{u16, bytes(u16) * 9000, 0, 0},\n\t\t\tcoreField{u32, bytes(u32) * 9000, 0, 0},\n\t\t},\n\t\t{\n\t\t\t\"struct.0\",\n\t\t\taStruct, bStruct,\n\t\t\tcoreAccessor{0, 0},\n\t\t\tcoreField{ptr, 1, 0, 0},\n\t\t\tcoreField{ptr, 2, 0, 0},\n\t\t},\n\t\t{\n\t\t\t\"struct.0 anon\",\n\t\t\taStruct, &Struct{Members: anon(bStruct, 24)},\n\t\t\tcoreAccessor{0, 0},\n\t\t\tcoreField{ptr, 1, 0, 0},\n\t\t\tcoreField{ptr, 3 + 2, 0, 0},\n\t\t},\n\t\t{\n\t\t\t\"struct.0 with base offset\",\n\t\t\taStruct, bStruct,\n\t\t\tcoreAccessor{3, 0},\n\t\t\tcoreField{ptr, 3*bytes(aStruct) + 1, 0, 0},\n\t\t\tcoreField{ptr, 3*bytes(bStruct) + 2, 0, 0},\n\t\t},\n\t\t{\n\t\t\t\"struct.1\",\n\t\t\taStruct, bStruct,\n\t\t\tcoreAccessor{0, 1},\n\t\t\tcoreField{u16, 2, 0, 0},\n\t\t\tcoreField{u32, 1, 0, 0},\n\t\t},\n\t\t{\n\t\t\t\"struct.1 anon\",\n\t\t\taStruct, &Struct{Members: anon(bStruct, 24)},\n\t\t\tcoreAccessor{0, 1},\n\t\t\tcoreField{u16, 2, 0, 0},\n\t\t\tcoreField{u32, 3 + 1, 0, 0},\n\t\t},\n\t\t{\n\t\t\t\"union.1\",\n\t\t\t&Union{Members: aFields, Size: 32},\n\t\t\t&Union{Members: bFields, Size: 32},\n\t\t\tcoreAccessor{0, 1},\n\t\t\tcoreField{u16, 2, 0, 0},\n\t\t\tcoreField{u32, 1, 0, 0},\n\t\t},\n\t\t{\n\t\t\t\"interchangeable composites\",\n\t\t\t&Struct{\n\t\t\t\tMembers: []Member{\n\t\t\t\t\tanonStruct(anonUnion(Member{Name: \"_1\", Type: u16})),\n\t\t\t\t},\n\t\t\t},\n\t\t\t&Struct{\n\t\t\t\tMembers: []Member{\n\t\t\t\t\tanonUnion(anonStruct(Member{Name: \"_1\", Type: u16})),\n\t\t\t\t},\n\t\t\t},\n\t\t\tcoreAccessor{0, 0, 0, 0},\n\t\t\tcoreField{u16, 0, 0, 0},\n\t\t\tcoreField{u16, 0, 0, 0},\n\t\t},\n\t\t{\n\t\t\t\"struct.2 (bitfield baz)\",\n\t\t\taStruct, bStruct,\n\t\t\tcoreAccessor{0, 2},\n\t\t\tcoreField{u32, 4, 0, 3},\n\t\t\tcoreField{u32, 8, 0, 3},\n\t\t},\n\t\t{\n\t\t\t\"struct.3 (bitfield quux)\",\n\t\t\taStruct, bStruct,\n\t\t\tcoreAccessor{0, 3},\n\t\t\tcoreField{u32, 4, 3, 10},\n\t\t\tcoreField{u16, 12, 0, 10},\n\t\t},\n\t\t{\n\t\t\t\"struct.4 (bitfield quuz)\",\n\t\t\taStruct, bStruct,\n\t\t\tcoreAccessor{0, 4},\n\t\t\tcoreField{u32, 4, 13, 8},\n\t\t\tcoreField{u16, 14, 0, 0},\n\t\t},\n\t}\n\n\tallowCoreField := cmp.AllowUnexported(coreField{})\n\n\tcheckCOREField := func(t *testing.T, which string, got, want coreField) {\n\t\tt.Helper()\n\t\tif diff := cmp.Diff(want, got, allowCoreField); diff != \"\" {\n\t\t\tt.Errorf(\"%s mismatch (-want +got):\\n%s\", which, diff)\n\t\t}\n\t}\n\n\tfor _, test := range valid {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tlocalField, targetField, err := coreFindField(test.local, test.acc, test.target)\n\t\t\tqt.Assert(t, qt.IsNil(err))\n\t\t\tcheckCOREField(t, \"local\", localField, test.localField)\n\t\t\tcheckCOREField(t, \"target\", targetField, test.targetField)\n\t\t})\n\t}\n}\n\nfunc TestCOREFindFieldCyclical(t *testing.T) {\n\tmembers := []Member{{Name: \"foo\", Type: &Pointer{}}}\n\n\tcyclicStruct := &Struct{}\n\tcyclicStruct.Members = []Member{{Type: cyclicStruct}}\n\n\tcyclicUnion := &Union{}\n\tcyclicUnion.Members = []Member{{Type: cyclicUnion}}\n\n\tcyclicArray := &Array{Nelems: 1}\n\tcyclicArray.Type = &Pointer{Target: cyclicArray}\n\n\ttests := []struct {\n\t\tname          string\n\t\tlocal, cyclic Type\n\t}{\n\t\t{\"struct\", &Struct{Members: members}, cyclicStruct},\n\t\t{\"union\", &Union{Members: members}, cyclicUnion},\n\t\t{\"array\", &Array{Nelems: 2, Type: &Int{}}, cyclicArray},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\t_, _, err := coreFindField(test.local, coreAccessor{0, 0}, test.cyclic)\n\t\t\tif !errors.Is(err, errImpossibleRelocation) {\n\t\t\t\tt.Fatal(\"Should return errImpossibleRelocation, got\", err)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestCORERelocation(t *testing.T) {\n\ttestutils.Files(t, testutils.Glob(t, \"testdata/*.elf\"), func(t *testing.T, file string) {\n\t\trd, err := os.Open(file)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tdefer rd.Close()\n\n\t\tspec, extInfos, err := LoadSpecAndExtInfosFromReader(rd)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tif extInfos == nil {\n\t\t\tt.Skip(\"No ext_infos\")\n\t\t}\n\n\t\terrs := map[string]error{\n\t\t\t\"err_ambiguous\":         errAmbiguousRelocation,\n\t\t\t\"err_ambiguous_flavour\": errAmbiguousRelocation,\n\t\t}\n\n\t\tfor section := range extInfos.Funcs {\n\t\t\tname := strings.TrimPrefix(section, \"socket/\")\n\t\t\tt.Run(name, func(t *testing.T) {\n\t\t\t\tvar relos []*CORERelocation\n\t\t\t\tfor _, reloInfo := range extInfos.CORERelos[section] {\n\t\t\t\t\trelos = append(relos, reloInfo.Relo)\n\t\t\t\t}\n\n\t\t\t\tfixups, err := CORERelocate(relos, []*Spec{spec}, spec.byteOrder, spec.TypeID)\n\t\t\t\tif want := errs[name]; want != nil {\n\t\t\t\t\tif !errors.Is(err, want) {\n\t\t\t\t\t\tt.Fatal(\"Expected\", want, \"got\", err)\n\t\t\t\t\t}\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatal(\"Can't relocate against itself:\", err)\n\t\t\t\t}\n\n\t\t\t\tfor offset, fixup := range fixups {\n\t\t\t\t\tif want := fixup.local; !fixup.skipLocalValidation && want != fixup.target {\n\t\t\t\t\t\t// Since we're relocating against ourselves both values\n\t\t\t\t\t\t// should match.\n\t\t\t\t\t\tt.Errorf(\"offset %d: local %v doesn't match target %d (kind %s)\", offset, fixup.local, fixup.target, fixup.kind)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestCOREReloFieldSigned(t *testing.T) {\n\tfor _, typ := range []Type{&Int{}, &Enum{}} {\n\t\tt.Run(fmt.Sprintf(\"%T with invalid target\", typ), func(t *testing.T) {\n\t\t\trelo := &CORERelocation{\n\t\t\t\ttyp, coreAccessor{0}, reloFieldSigned, 0,\n\t\t\t}\n\t\t\tfixup, err := coreCalculateFixup(relo, &Void{}, internal.NativeEndian, dummyTypeID)\n\t\t\tqt.Assert(t, qt.IsTrue(fixup.poison))\n\t\t\tqt.Assert(t, qt.IsNil(err))\n\t\t})\n\t}\n\n\tt.Run(\"type without signedness\", func(t *testing.T) {\n\t\trelo := &CORERelocation{\n\t\t\t&Array{}, coreAccessor{0}, reloFieldSigned, 0,\n\t\t}\n\t\t_, err := coreCalculateFixup(relo, &Array{}, internal.NativeEndian, dummyTypeID)\n\t\tqt.Assert(t, qt.ErrorIs(err, errNoSignedness))\n\t})\n}\n\nfunc TestCOREReloFieldShiftU64(t *testing.T) {\n\ttyp := &Struct{\n\t\tMembers: []Member{\n\t\t\t{Name: \"A\", Type: &Fwd{}},\n\t\t},\n\t}\n\n\tfor _, relo := range []*CORERelocation{\n\t\t{typ, coreAccessor{0, 0}, reloFieldRShiftU64, 1},\n\t\t{typ, coreAccessor{0, 0}, reloFieldLShiftU64, 1},\n\t} {\n\t\tt.Run(relo.kind.String(), func(t *testing.T) {\n\t\t\t_, err := coreCalculateFixup(relo, typ, internal.NativeEndian, dummyTypeID)\n\t\t\tqt.Assert(t, qt.ErrorIs(err, errUnsizedType))\n\t\t})\n\t}\n}\n\nfunc TestCORERelosKmodTypeID(t *testing.T) {\n\ta := &Int{Name: \"a\"}\n\tb := &Int{Name: \"b\"}\n\n\trelos := []*CORERelocation{\n\t\t{&Int{}, coreAccessor{0}, reloTypeIDTarget, 0},\n\t}\n\n\ttypeID := func(t Type) (TypeID, error) {\n\t\tif t == a {\n\t\t\treturn 42, nil\n\t\t}\n\t\treturn 0, ErrNotFound\n\t}\n\n\tfixups, err := coreCalculateFixups(\n\t\trelos,\n\t\t[]Type{a, b},\n\t\tinternal.NativeEndian,\n\t\ttypeID,\n\t)\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.IsFalse(fixups[0].poison))\n\tqt.Assert(t, qt.Equals(fixups[0].target, 42))\n\n\tfixups, err = coreCalculateFixups(\n\t\trelos,\n\t\t[]Type{b},\n\t\tinternal.NativeEndian,\n\t\ttypeID,\n\t)\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.IsTrue(fixups[0].poison))\n}\n\nfunc BenchmarkCORESkBuff(b *testing.B) {\n\tspec := vmlinuxTestdataSpec(b)\n\n\tvar skb *Struct\n\terr := spec.TypeByName(\"sk_buff\", &skb)\n\tqt.Assert(b, qt.IsNil(err))\n\n\tskbID, err := spec.TypeID(skb)\n\tqt.Assert(b, qt.IsNil(err))\n\n\tlenIndex := slices.IndexFunc(skb.Members, func(m Member) bool {\n\t\treturn m.Name == \"len\"\n\t})\n\tqt.Assert(b, qt.Not(qt.Equals(lenIndex, -1)))\n\n\tvar pktHashTypes *Enum\n\terr = spec.TypeByName(\"pkt_hash_types\", &pktHashTypes)\n\tqt.Assert(b, qt.IsNil(err))\n\n\tpktHashTypesID, err := spec.TypeID(pktHashTypes)\n\tqt.Assert(b, qt.IsNil(err))\n\n\tfor _, relo := range []*CORERelocation{\n\t\t{skb, coreAccessor{0, lenIndex}, reloFieldByteOffset, skbID},\n\t\t{skb, coreAccessor{0, lenIndex}, reloFieldByteSize, skbID},\n\t\t{skb, coreAccessor{0, lenIndex}, reloFieldExists, skbID},\n\t\t{skb, coreAccessor{0, lenIndex}, reloFieldSigned, skbID},\n\t\t{skb, coreAccessor{0, lenIndex}, reloFieldLShiftU64, skbID},\n\t\t{skb, coreAccessor{0, lenIndex}, reloFieldRShiftU64, skbID},\n\t\t{skb, coreAccessor{0}, reloTypeIDLocal, skbID},\n\t\t{skb, coreAccessor{0}, reloTypeIDTarget, skbID},\n\t\t{skb, coreAccessor{0}, reloTypeExists, skbID},\n\t\t{skb, coreAccessor{0}, reloTypeSize, skbID},\n\t\t{pktHashTypes, coreAccessor{0}, reloEnumvalExists, pktHashTypesID},\n\t\t{pktHashTypes, coreAccessor{0}, reloEnumvalValue, pktHashTypesID},\n\t} {\n\t\tb.Run(relo.kind.String(), func(b *testing.B) {\n\t\t\tb.ReportAllocs()\n\n\t\t\tfor b.Loop() {\n\t\t\t\t_, err = CORERelocate([]*CORERelocation{relo}, []*Spec{spec}, spec.byteOrder, spec.TypeID)\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestCORETypesMatch(t *testing.T) {\n\ttests := []struct {\n\t\ta, b       Type\n\t\tmatch      bool\n\t\treversible bool\n\t}{\n\t\t{&Void{}, &Void{}, true, true},\n\t\t{&Int{Size: 32}, &Int{Size: 32}, true, true},\n\t\t{&Int{Size: 64}, &Int{Size: 32}, false, true},\n\t\t{&Int{Size: 32}, &Int{Size: 32, Encoding: Signed}, false, true},\n\t\t{&Fwd{Name: \"a\"}, &Fwd{Name: \"a\"}, true, true},\n\t\t{&Fwd{Name: \"a\"}, &Fwd{Name: \"b___new\"}, false, true},\n\t\t{&Fwd{Name: \"a\"}, &Fwd{Name: \"a___new\"}, true, true},\n\t\t{&Fwd{Name: \"a\"}, &Struct{Name: \"a___new\"}, false, true},\n\t\t{&Fwd{Name: \"a\"}, &Union{Name: \"a___new\"}, false, true},\n\t\t{&Fwd{Name: \"a\", Kind: FwdStruct}, &Fwd{Name: \"a___new\", Kind: FwdUnion}, false, true},\n\t\t{&Pointer{&Fwd{Name: \"a\", Kind: FwdStruct}}, &Pointer{&Struct{Name: \"a___new\"}}, true, true},\n\t\t{&Pointer{&Fwd{Name: \"a\", Kind: FwdUnion}}, &Pointer{&Union{Name: \"a___new\"}}, true, true},\n\t\t{&Pointer{&Fwd{Name: \"a\", Kind: FwdStruct}}, &Pointer{&Union{Name: \"a___new\"}}, false, true},\n\t\t{&Struct{Name: \"a___new\"}, &Union{Name: \"a___new\"}, false, true},\n\t\t{&Pointer{&Struct{Name: \"a\"}}, &Pointer{&Union{Name: \"a___new\"}}, false, true},\n\t\t{\n\t\t\t&Struct{Name: \"a\", Members: []Member{\n\t\t\t\t{Name: \"foo\", Type: &Int{}},\n\t\t\t}},\n\t\t\t&Struct{Name: \"a___new\", Members: []Member{\n\t\t\t\t{Name: \"foo\", Type: &Int{}},\n\t\t\t}},\n\t\t\ttrue,\n\t\t\ttrue,\n\t\t},\n\t\t{\n\t\t\t&Struct{Name: \"a\", Members: []Member{\n\t\t\t\t{Name: \"foo\", Type: &Int{}},\n\t\t\t}},\n\t\t\t&Struct{Name: \"a___new\", Members: []Member{\n\t\t\t\t{Name: \"foo\", Type: &Int{}},\n\t\t\t\t{Name: \"bar\", Type: &Int{}},\n\t\t\t}},\n\t\t\ttrue,\n\t\t\tfalse,\n\t\t},\n\t\t{\n\t\t\t&Struct{Name: \"a\", Members: []Member{\n\t\t\t\t{Name: \"foo\", Type: &Int{}},\n\t\t\t\t{Name: \"bar\", Type: &Int{}},\n\t\t\t}},\n\t\t\t&Struct{Name: \"a___new\", Members: []Member{\n\t\t\t\t{Name: \"foo\", Type: &Int{}},\n\t\t\t}},\n\t\t\tfalse,\n\t\t\tfalse,\n\t\t},\n\t\t{\n\t\t\t&Struct{Name: \"a\", Members: []Member{\n\t\t\t\t{Name: \"bar\", Type: &Int{}},\n\t\t\t}},\n\t\t\t&Struct{Name: \"a___new\", Members: []Member{\n\t\t\t\t{Name: \"foo\", Type: &Int{}},\n\t\t\t}},\n\t\t\tfalse,\n\t\t\tfalse,\n\t\t},\n\t\t{\n\t\t\t&Struct{Name: \"a\", Members: []Member{\n\t\t\t\t{Name: \"foo\", Type: &Int{Encoding: Signed}},\n\t\t\t}},\n\t\t\t&Struct{Name: \"a___new\", Members: []Member{\n\t\t\t\t{Name: \"foo\", Type: &Int{}},\n\t\t\t}},\n\t\t\tfalse,\n\t\t\tfalse,\n\t\t},\n\t\t{\n\t\t\t&Enum{Name: \"a\", Values: []EnumValue{\n\t\t\t\t{\"foo\", 1},\n\t\t\t}},\n\t\t\t&Enum{Name: \"a___new\", Values: []EnumValue{\n\t\t\t\t{\"foo\", 1},\n\t\t\t}},\n\t\t\ttrue,\n\t\t\ttrue,\n\t\t},\n\t\t{\n\t\t\t&Enum{Name: \"a\", Values: []EnumValue{\n\t\t\t\t{\"foo\", 1},\n\t\t\t}},\n\t\t\t&Enum{Name: \"a___new\", Values: []EnumValue{\n\t\t\t\t{\"foo\", 1},\n\t\t\t\t{\"bar\", 2},\n\t\t\t}},\n\t\t\ttrue,\n\t\t\tfalse,\n\t\t},\n\t\t{\n\t\t\t&Enum{Name: \"a\", Values: []EnumValue{\n\t\t\t\t{\"foo\", 1},\n\t\t\t\t{\"bar\", 2},\n\t\t\t}},\n\t\t\t&Enum{Name: \"a___new\", Values: []EnumValue{\n\t\t\t\t{\"foo\", 1},\n\t\t\t}},\n\t\t\tfalse,\n\t\t\tfalse,\n\t\t},\n\t\t{\n\t\t\t&Enum{Name: \"a\", Values: []EnumValue{\n\t\t\t\t{\"foo\", 1},\n\t\t\t}},\n\t\t\t&Enum{Name: \"a___new\", Values: []EnumValue{\n\t\t\t\t{\"bar\", 1},\n\t\t\t}},\n\t\t\tfalse,\n\t\t\tfalse,\n\t\t},\n\t\t{\n\t\t\t&Enum{Name: \"a\", Values: []EnumValue{\n\t\t\t\t{\"foo\", 1},\n\t\t\t}, Size: 1},\n\t\t\t&Enum{Name: \"a___new\", Values: []EnumValue{\n\t\t\t\t{\"foo\", 1},\n\t\t\t}, Size: 2},\n\t\t\tfalse,\n\t\t\tfalse,\n\t\t},\n\t\t{\n\t\t\t&Array{Type: &Int{}, Nelems: 2},\n\t\t\t&Array{Type: &Int{}, Nelems: 2},\n\t\t\ttrue,\n\t\t\ttrue,\n\t\t},\n\t\t{\n\t\t\t&Array{Type: &Int{}, Nelems: 3},\n\t\t\t&Array{Type: &Int{}, Nelems: 2},\n\t\t\tfalse,\n\t\t\ttrue,\n\t\t},\n\t\t{\n\t\t\t&Array{Type: &Void{}, Nelems: 2},\n\t\t\t&Array{Type: &Int{}, Nelems: 2},\n\t\t\tfalse,\n\t\t\ttrue,\n\t\t},\n\t\t{\n\t\t\t&FuncProto{Return: &Int{}, Params: []FuncParam{\n\t\t\t\t{Name: \"foo\", Type: &Int{}},\n\t\t\t}},\n\t\t\t&FuncProto{Return: &Int{}, Params: []FuncParam{\n\t\t\t\t{Name: \"bar\", Type: &Int{}},\n\t\t\t}},\n\t\t\ttrue,\n\t\t\ttrue,\n\t\t},\n\t\t{\n\t\t\t&FuncProto{Return: &Int{}, Params: []FuncParam{\n\t\t\t\t{Name: \"foo\", Type: &Int{}},\n\t\t\t}},\n\t\t\t&FuncProto{Return: &Int{}, Params: []FuncParam{\n\t\t\t\t{Name: \"bar\", Type: &Int{}},\n\t\t\t\t{Name: \"baz\", Type: &Int{}},\n\t\t\t}},\n\t\t\tfalse,\n\t\t\ttrue,\n\t\t},\n\t\t{\n\t\t\t&FuncProto{Return: &Void{}, Params: []FuncParam{\n\t\t\t\t{Name: \"foo\", Type: &Int{}},\n\t\t\t}},\n\t\t\t&FuncProto{Return: &Int{}, Params: []FuncParam{\n\t\t\t\t{Name: \"bar\", Type: &Int{}},\n\t\t\t}},\n\t\t\tfalse,\n\t\t\ttrue,\n\t\t},\n\t\t{\n\t\t\t&FuncProto{Return: &Void{}, Params: []FuncParam{\n\t\t\t\t{Name: \"bar\", Type: &Int{Encoding: Signed}},\n\t\t\t}},\n\t\t\t&FuncProto{Return: &Int{}, Params: []FuncParam{\n\t\t\t\t{Name: \"bar\", Type: &Int{}},\n\t\t\t}},\n\t\t\tfalse,\n\t\t\ttrue,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\terr := coreTypesMatch(test.a, test.b, nil)\n\t\tif test.match {\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"Expected types to match: %s\\na = %#v\\nb = %#v\", err, test.a, test.b)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t} else {\n\t\t\tif !errors.Is(err, errIncompatibleTypes) {\n\t\t\t\tt.Errorf(\"Expected types to be incompatible: \\na = %#v\\nb = %#v\", test.a, test.b)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\tif test.reversible {\n\t\t\terr = coreTypesMatch(test.b, test.a, nil)\n\t\t\tif test.match {\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Errorf(\"Expected reversed types to match: %s\\na = %#v\\nb = %#v\", err, test.a, test.b)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif !errors.Is(err, errIncompatibleTypes) {\n\t\t\t\t\tt.Errorf(\"Expected reversed types to be incompatible: %s\\na = %#v\\nb = %#v\", err, test.a, test.b)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, invalid := range []Type{&Var{}, &Datasec{}} {\n\t\terr := coreTypesMatch(invalid, invalid, nil)\n\t\tif errors.Is(err, errIncompatibleTypes) {\n\t\t\tt.Errorf(\"Expected an error for %T, not errIncompatibleTypes\", invalid)\n\t\t} else if err == nil {\n\t\t\tt.Errorf(\"Expected an error for %T\", invalid)\n\t\t}\n\t}\n}\n\n// dummyTypeID returns 0, nil for any passed type.\nfunc dummyTypeID(Type) (TypeID, error) {\n\treturn 0, nil\n}\n"
  },
  {
    "path": "btf/dedup.go",
    "content": "package btf\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"hash/maphash\"\n\t\"slices\"\n)\n\n// deduper deduplicates BTF types by finding all types in a Type graph that are\n// Equivalent and replaces them with a single instance.\n//\n// See doc comments in types.go to understand the various ways in which Types\n// can relate to each other and how they are compared for equality. We separate\n// Identity (same memory location), Equivalence (same shape/layout), and\n// Compatibility (CO-RE compatible) to be explicit about intent.\n//\n// This deduper opportunistically uses a combination of Identity and Equivalence\n// to find types that can be deduplicated.\ntype deduper struct {\n\tvisited   map[Type]struct{}\n\thashCache map[hashCacheKey]uint64\n\n\t// Set of types that have been deduplicated.\n\tdone map[Type]Type\n\n\t// Map of hash to types with that hash.\n\thashed  map[uint64][]Type\n\teqCache map[typKey]bool\n\n\tseed maphash.Seed\n}\n\nfunc newDeduper() *deduper {\n\treturn &deduper{\n\t\tmake(map[Type]struct{}),\n\t\tmake(map[hashCacheKey]uint64),\n\t\tmake(map[Type]Type),\n\t\tmake(map[uint64][]Type),\n\t\tmake(map[typKey]bool),\n\t\tmaphash.MakeSeed(),\n\t}\n}\n\nfunc (d *deduper) deduplicate(t Type) (Type, error) {\n\t// If we have already attempted to deduplicate this exact type, return the\n\t// result.\n\tif done, ok := d.done[t]; ok {\n\t\treturn done, nil\n\t}\n\n\t// Visit the subtree, if a type has children, attempt to replace it with a\n\t// deduplicated version of those children.\n\tfor t := range postorder(t, d.visited) {\n\t\tfor c := range children(t) {\n\t\t\tvar err error\n\t\t\t*c, err = d.hashInsert(*c)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\n\t// Finally, deduplicate the root type itself.\n\treturn d.hashInsert(t)\n}\n\n// hashInsert attempts to deduplicate t by hashing it and comparing against\n// other types with the same hash. Returns the Type to be used as the common\n// substitute at this position in the graph.\nfunc (d *deduper) hashInsert(t Type) (Type, error) {\n\t// If we have deduplicated this type before, return the result of that\n\t// deduplication.\n\tif done, ok := d.done[t]; ok {\n\t\treturn done, nil\n\t}\n\n\t// Compute the hash of this type. Types with the same hash are candidates for\n\t// deduplication.\n\thash, err := d.hash(t, -1)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// A hash collision is possible, so we need to compare against all candidates\n\t// with the same hash.\n\tfor _, candidate := range d.hashed[hash] {\n\t\t// Pre-size the visited slice, experimentation on VMLinux shows a capacity\n\t\t// of 16 to give the best performance.\n\t\tconst visitedCapacity = 16\n\t\terr := d.typesEquivalent(candidate, t, make([]Type, 0, visitedCapacity))\n\t\tif errors.Is(err, errNotEquivalent) {\n\t\t\tcontinue\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\t// Found a Type that's both Equivalent and hashes to the same value, choose\n\t\t// it as the deduplicated version.\n\t\td.done[t] = candidate\n\n\t\treturn candidate, nil\n\t}\n\n\td.hashed[hash] = append(d.hashed[hash], t)\n\n\treturn t, nil\n}\n\n// The hash of a Type is the same given its pointer and depth budget.\ntype hashCacheKey struct {\n\tt           Type\n\tdepthBudget int\n}\n\n// hash computes a hash for t. The produced hash is the same for Types which\n// are similar. The hash can collide such that two different Types may produce\n// the same hash, so equivalence must be checked explicitly. It will recurse\n// into children. The initial call should use a depthBudget of -1.\nfunc (d *deduper) hash(t Type, depthBudget int) (uint64, error) {\n\tif depthBudget == 0 {\n\t\treturn 0, nil\n\t}\n\n\th := &maphash.Hash{}\n\th.SetSeed(d.seed)\n\n\tswitch t := t.(type) {\n\tcase *Void:\n\t\tmaphash.WriteComparable(h, kindUnknown)\n\n\tcase *Int:\n\t\tmaphash.WriteComparable(h, kindInt)\n\t\tmaphash.WriteComparable(h, *t)\n\n\tcase *Pointer:\n\t\tmaphash.WriteComparable(h, kindPointer)\n\t\t// If the depth budget is positive, decrement it every time we follow a\n\t\t// pointer.\n\t\tif depthBudget > 0 {\n\t\t\tdepthBudget--\n\t\t}\n\n\t\t// If this is the first time we are following a pointer, set the depth\n\t\t// budget. This limits amount of recursion we do when hashing pointers that\n\t\t// form cycles. This is cheaper than tracking visited types and works\n\t\t// because hash collisions are allowed.\n\t\tif depthBudget < 0 {\n\t\t\tdepthBudget = 1\n\n\t\t\t// Double pointers are common in C. However, with a depth budget of 1, all\n\t\t\t// double pointers would hash the same, causing a performance issue when\n\t\t\t// checking equivalence. So we give double pointers a bit more budget.\n\t\t\tif _, ok := t.Target.(*Pointer); ok {\n\t\t\t\tdepthBudget = 2\n\t\t\t}\n\t\t}\n\t\tsub, err := d.hash(t.Target, depthBudget)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tmaphash.WriteComparable(h, sub)\n\n\tcase *Array:\n\t\tmaphash.WriteComparable(h, kindArray)\n\t\tmaphash.WriteComparable(h, t.Nelems)\n\t\tsub, err := d.hash(t.Index, depthBudget)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tmaphash.WriteComparable(h, sub)\n\t\t_, err = d.hash(t.Type, depthBudget)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tmaphash.WriteComparable(h, sub)\n\n\tcase *Struct, *Union:\n\t\t// Check the cache to avoid recomputing the hash for this type and depth\n\t\t// budget.\n\t\tkey := hashCacheKey{t, depthBudget}\n\t\tif cached, ok := d.hashCache[key]; ok {\n\t\t\treturn cached, nil\n\t\t}\n\n\t\tvar members []Member\n\t\tswitch t := t.(type) {\n\t\tcase *Struct:\n\t\t\tmaphash.WriteComparable(h, kindStruct)\n\t\t\tmaphash.WriteComparable(h, t.Name)\n\t\t\tmaphash.WriteComparable(h, t.Size)\n\t\t\tmembers = t.Members\n\n\t\tcase *Union:\n\t\t\tmaphash.WriteComparable(h, kindUnion)\n\t\t\tmaphash.WriteComparable(h, t.Name)\n\t\t\tmaphash.WriteComparable(h, t.Size)\n\t\t\tmembers = t.Members\n\t\t}\n\n\t\tmaphash.WriteComparable(h, len(members))\n\t\tfor _, m := range members {\n\t\t\tmaphash.WriteComparable(h, m.Name)\n\t\t\tmaphash.WriteComparable(h, m.Offset)\n\t\t\tsub, err := d.hash(m.Type, depthBudget)\n\t\t\tif err != nil {\n\t\t\t\treturn 0, err\n\t\t\t}\n\t\t\tmaphash.WriteComparable(h, sub)\n\t\t}\n\n\t\tsum := h.Sum64()\n\t\td.hashCache[key] = sum\n\t\treturn sum, nil\n\n\tcase *Enum:\n\t\tmaphash.WriteComparable(h, kindEnum)\n\t\tmaphash.WriteComparable(h, t.Name)\n\t\tmaphash.WriteComparable(h, t.Size)\n\t\tmaphash.WriteComparable(h, t.Signed)\n\t\tfor _, v := range t.Values {\n\t\t\tmaphash.WriteComparable(h, v)\n\t\t}\n\n\tcase *Fwd:\n\t\tmaphash.WriteComparable(h, kindForward)\n\t\tmaphash.WriteComparable(h, *t)\n\n\tcase *Typedef:\n\t\tmaphash.WriteComparable(h, kindTypedef)\n\t\tmaphash.WriteComparable(h, t.Name)\n\t\tsub, err := d.hash(t.Type, depthBudget)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tmaphash.WriteComparable(h, sub)\n\n\tcase *Volatile:\n\t\tmaphash.WriteComparable(h, kindVolatile)\n\t\tsub, err := d.hash(t.Type, depthBudget)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tmaphash.WriteComparable(h, sub)\n\n\tcase *Const:\n\t\tmaphash.WriteComparable(h, kindConst)\n\t\tsub, err := d.hash(t.Type, depthBudget)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tmaphash.WriteComparable(h, sub)\n\n\tcase *Restrict:\n\t\tmaphash.WriteComparable(h, kindRestrict)\n\t\tsub, err := d.hash(t.Type, depthBudget)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tmaphash.WriteComparable(h, sub)\n\n\tcase *Func:\n\t\tmaphash.WriteComparable(h, kindFunc)\n\t\tmaphash.WriteComparable(h, t.Name)\n\t\tsub, err := d.hash(t.Type, depthBudget)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tmaphash.WriteComparable(h, sub)\n\n\tcase *FuncProto:\n\t\t// It turns out that pointers to function prototypes are common in C code,\n\t\t// function pointers. Function prototypes frequently have similar patterns\n\t\t// of [ptr, ptr] -> int, or [ptr, ptr, ptr] -> int. Causing frequent hash\n\t\t// collisions, for the default depth budget of 1. So allow one additional\n\t\t// level of pointers when we encounter a function prototype.\n\t\tif depthBudget >= 0 {\n\t\t\tdepthBudget++\n\t\t}\n\n\t\tmaphash.WriteComparable(h, kindFuncProto)\n\t\tfor _, p := range t.Params {\n\t\t\tmaphash.WriteComparable(h, p.Name)\n\t\t\tsub, err := d.hash(p.Type, depthBudget)\n\t\t\tif err != nil {\n\t\t\t\treturn 0, err\n\t\t\t}\n\t\t\tmaphash.WriteComparable(h, sub)\n\t\t}\n\t\tsub, err := d.hash(t.Return, depthBudget)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tmaphash.WriteComparable(h, sub)\n\n\tcase *Var:\n\t\tmaphash.WriteComparable(h, kindVar)\n\t\tmaphash.WriteComparable(h, t.Name)\n\t\tmaphash.WriteComparable(h, t.Linkage)\n\t\tsub, err := d.hash(t.Type, depthBudget)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tmaphash.WriteComparable(h, sub)\n\n\tcase *Datasec:\n\t\tmaphash.WriteComparable(h, kindDatasec)\n\t\tmaphash.WriteComparable(h, t.Name)\n\t\tfor _, v := range t.Vars {\n\t\t\tmaphash.WriteComparable(h, v.Offset)\n\t\t\tmaphash.WriteComparable(h, v.Size)\n\t\t\tsub, err := d.hash(v.Type, depthBudget)\n\t\t\tif err != nil {\n\t\t\t\treturn 0, err\n\t\t\t}\n\t\t\tmaphash.WriteComparable(h, sub)\n\t\t}\n\n\tcase *declTag:\n\t\tmaphash.WriteComparable(h, kindDeclTag)\n\t\tmaphash.WriteComparable(h, t.Value)\n\t\tmaphash.WriteComparable(h, t.Index)\n\t\tsub, err := d.hash(t.Type, depthBudget)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tmaphash.WriteComparable(h, sub)\n\n\tcase *TypeTag:\n\t\tmaphash.WriteComparable(h, kindTypeTag)\n\t\tmaphash.WriteComparable(h, t.Value)\n\t\tsub, err := d.hash(t.Type, depthBudget)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tmaphash.WriteComparable(h, sub)\n\n\tcase *Float:\n\t\tmaphash.WriteComparable(h, kindFloat)\n\t\tmaphash.WriteComparable(h, *t)\n\n\tdefault:\n\t\treturn 0, fmt.Errorf(\"unsupported type for hashing: %T\", t)\n\t}\n\n\treturn h.Sum64(), nil\n}\n\ntype typKey struct {\n\ta Type\n\tb Type\n}\n\nvar errNotEquivalent = errors.New(\"types are not equivalent\")\n\n// typesEquivalent checks if two types are Equivalent.\nfunc (d *deduper) typesEquivalent(ta, tb Type, visited []Type) error {\n\t// Fast path: if Types are Identical, they are also Equivalent.\n\tif ta == tb {\n\t\treturn nil\n\t}\n\n\tswitch a := ta.(type) {\n\tcase *Void:\n\t\tif _, ok := tb.(*Void); ok {\n\t\t\treturn nil\n\t\t}\n\t\treturn errNotEquivalent\n\n\tcase *Int:\n\t\tb, ok := tb.(*Int)\n\t\tif !ok {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\tif a.Name != b.Name || a.Size != b.Size || a.Encoding != b.Encoding {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\treturn nil\n\n\tcase *Enum:\n\t\tb, ok := tb.(*Enum)\n\t\tif !ok {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\tif a.Name != b.Name || len(a.Values) != len(b.Values) {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\tfor i := range a.Values {\n\t\t\tif a.Values[i].Name != b.Values[i].Name || a.Values[i].Value != b.Values[i].Value {\n\t\t\t\treturn errNotEquivalent\n\t\t\t}\n\t\t}\n\t\treturn nil\n\n\tcase *Fwd:\n\t\tb, ok := tb.(*Fwd)\n\t\tif !ok {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\tif a.Name != b.Name || a.Kind != b.Kind {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\treturn nil\n\n\tcase *Float:\n\t\tb, ok := tb.(*Float)\n\t\tif !ok {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\tif a.Name != b.Name || a.Size != b.Size {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\treturn nil\n\n\tcase *Array:\n\t\tb, ok := tb.(*Array)\n\t\tif !ok {\n\t\t\treturn errNotEquivalent\n\t\t}\n\n\t\tif a.Nelems != b.Nelems {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\tif err := d.typesEquivalent(a.Index, b.Index, visited); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := d.typesEquivalent(a.Type, b.Type, visited); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\n\tcase *Pointer:\n\t\tb, ok := tb.(*Pointer)\n\t\tif !ok {\n\t\t\treturn errNotEquivalent\n\t\t}\n\n\t\t// Detect cycles by tracking visited types. Assume types are Equivalent if\n\t\t// we have already visited this type in the current Equivalence check.\n\t\tif slices.Contains(visited, ta) {\n\t\t\treturn nil\n\t\t}\n\t\tvisited = append(visited, ta)\n\n\t\treturn d.typesEquivalent(a.Target, b.Target, visited)\n\n\tcase *Struct, *Union:\n\t\t// Use a cache to avoid recomputation. We only do this for composite types\n\t\t// since they are where types fan out the most. For other types, the\n\t\t// overhead of the lookup and update outweighs performance benefits.\n\t\tcacheKey := typKey{a: ta, b: tb}\n\t\tif equal, ok := d.eqCache[cacheKey]; ok {\n\t\t\tif equal {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn errNotEquivalent\n\t\t}\n\n\t\tcompErr := d.compositeEquivalent(ta, tb, visited)\n\t\td.eqCache[cacheKey] = compErr == nil\n\n\t\treturn compErr\n\n\tcase *Typedef:\n\t\tb, ok := tb.(*Typedef)\n\t\tif !ok {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\tif a.Name != b.Name {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\treturn d.typesEquivalent(a.Type, b.Type, visited)\n\n\tcase *Volatile:\n\t\tb, ok := tb.(*Volatile)\n\t\tif !ok {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\treturn d.typesEquivalent(a.Type, b.Type, visited)\n\n\tcase *Const:\n\t\tb, ok := tb.(*Const)\n\t\tif !ok {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\treturn d.typesEquivalent(a.Type, b.Type, visited)\n\n\tcase *Restrict:\n\t\tb, ok := tb.(*Restrict)\n\t\tif !ok {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\treturn d.typesEquivalent(a.Type, b.Type, visited)\n\n\tcase *Func:\n\t\tb, ok := tb.(*Func)\n\t\tif !ok {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\tif a.Name != b.Name {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\treturn d.typesEquivalent(a.Type, b.Type, visited)\n\n\tcase *FuncProto:\n\t\tb, ok := tb.(*FuncProto)\n\t\tif !ok {\n\t\t\treturn errNotEquivalent\n\t\t}\n\n\t\tif err := d.typesEquivalent(a.Return, b.Return, visited); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif len(a.Params) != len(b.Params) {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\tfor i := range a.Params {\n\t\t\tif a.Params[i].Name != b.Params[i].Name {\n\t\t\t\treturn errNotEquivalent\n\t\t\t}\n\t\t\tif err := d.typesEquivalent(a.Params[i].Type, b.Params[i].Type, visited); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn nil\n\n\tcase *Var:\n\t\tb, ok := tb.(*Var)\n\t\tif !ok {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\tif a.Name != b.Name {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\tif err := d.typesEquivalent(a.Type, b.Type, visited); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif a.Linkage != b.Linkage {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\treturn nil\n\n\tcase *Datasec:\n\t\tb, ok := tb.(*Datasec)\n\t\tif !ok {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\tif a.Name != b.Name || len(a.Vars) != len(b.Vars) {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\tfor i := range a.Vars {\n\t\t\tif a.Vars[i].Offset != b.Vars[i].Offset ||\n\t\t\t\ta.Vars[i].Size != b.Vars[i].Size {\n\t\t\t\treturn errNotEquivalent\n\t\t\t}\n\n\t\t\tif err := d.typesEquivalent(a.Vars[i].Type, b.Vars[i].Type, visited); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn nil\n\n\tcase *declTag:\n\t\tb, ok := tb.(*declTag)\n\t\tif !ok {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\tif a.Value != b.Value || a.Index != b.Index {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\treturn d.typesEquivalent(a.Type, b.Type, visited)\n\n\tcase *TypeTag:\n\t\tb, ok := tb.(*TypeTag)\n\t\tif !ok {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\tif a.Value != b.Value {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\tif err := d.typesEquivalent(a.Type, b.Type, visited); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported type for equivalence: %T\", a)\n\t}\n}\n\n// compositeEquivalent checks if two composite types (Struct or Union) are\n// Equivalent.\nfunc (d *deduper) compositeEquivalent(at, bt Type, visited []Type) error {\n\tvar ma, mb []Member\n\tswitch a := at.(type) {\n\tcase *Struct:\n\t\tb, ok := bt.(*Struct)\n\t\tif !ok {\n\t\t\treturn errNotEquivalent\n\t\t}\n\n\t\tif a.Name != b.Name || a.Size != b.Size || len(a.Members) != len(b.Members) {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\tma = a.Members\n\t\tmb = b.Members\n\n\tcase *Union:\n\t\tb, ok := bt.(*Union)\n\t\tif !ok {\n\t\t\treturn errNotEquivalent\n\t\t}\n\n\t\tif a.Name != b.Name || a.Size != b.Size || len(a.Members) != len(b.Members) {\n\t\t\treturn errNotEquivalent\n\t\t}\n\t\tma = a.Members\n\t\tmb = b.Members\n\t}\n\n\tfor i := range ma {\n\t\tif ma[i].Name != mb[i].Name || ma[i].Offset != mb[i].Offset {\n\t\t\treturn errNotEquivalent\n\t\t}\n\n\t\tif err := d.typesEquivalent(ma[i].Type, mb[i].Type, visited); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "btf/dedup_test.go",
    "content": "package btf\n\nimport (\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc countTypes(typs ...Type) int {\n\ti := 0\n\tvisited := make(map[Type]struct{})\n\tfor _, typ := range typs {\n\t\tfor range postorder(typ, visited) {\n\t\t\ti++\n\t\t}\n\t}\n\treturn i\n}\n\nfunc TestDedupSKBuff(t *testing.T) {\n\tvmlinux := vmlinuxTestdataBytes(t)\n\tspec, err := loadRawSpec(vmlinux, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tvar skBuffOne *Struct\n\tqt.Assert(t, qt.IsNil(spec.TypeByName(\"sk_buff\", &skBuffOne)))\n\n\tskbCount := countTypes(skBuffOne)\n\n\tspec = spec.Copy()\n\tvar skBuffTwo *Struct\n\tqt.Assert(t, qt.IsNil(spec.TypeByName(\"sk_buff\", &skBuffTwo)))\n\n\tdeduper := newDeduper()\n\n\ttypes := []Type{skBuffOne, skBuffTwo}\n\tfor i, typ := range types {\n\t\ttypes[i], err = deduper.deduplicate(typ)\n\t}\n\tqt.Assert(t, qt.IsNil(err))\n\n\tdedupedCount := countTypes(types...)\n\tqt.Assert(t, qt.Equals(skbCount, dedupedCount), qt.Commentf(\"Expected deduplicated sk_buff to have same number of types as original\"))\n}\n\nfunc TestDedupVmlinux(t *testing.T) {\n\tvmlinux := vmlinuxTestdataBytes(t)\n\n\tspec1, err := loadRawSpec(vmlinux, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tspec2 := spec1.Copy()\n\n\trootTypes := func(spec *Spec) []Type {\n\t\trefs := make(map[Type]int)\n\t\tfor t := range spec.All() {\n\t\t\trefs[t] = 0\n\t\t}\n\t\tfor t := range spec.All() {\n\t\t\tfor child := range children(t) {\n\t\t\t\trefs[*child]++\n\t\t\t}\n\t\t}\n\t\ttypes := make([]Type, 0)\n\t\tfor typ := range refs {\n\t\t\tif refs[typ] == 0 {\n\t\t\t\ttypes = append(types, typ)\n\t\t\t}\n\t\t}\n\t\treturn types\n\t}\n\n\tspec1Roots := rootTypes(spec1)\n\tspec1TypeCount := countTypes(spec1Roots...)\n\tspec2Roots := rootTypes(spec2)\n\ttypes := append(spec1Roots, spec2Roots...)\n\n\tdeduper := newDeduper()\n\n\tfor i, typ := range types {\n\t\ttypes[i], err = deduper.deduplicate(typ)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t}\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.Equals(countTypes(types...), spec1TypeCount), qt.Commentf(\"Expected deduplicated vmlinux to have same number of types as original\"))\n}\n\nfunc BenchmarkDeduplicateSKBuff(b *testing.B) {\n\tb.ReportAllocs()\n\n\tvmlinux := vmlinuxTestdataBytes(b)\n\tbase, err := loadRawSpec(vmlinux, nil)\n\tqt.Assert(b, qt.IsNil(err))\n\n\t// Obtain b.N unique copies of sk_buff.\n\ttypes := make([]Type, 0, b.N)\n\tfor range b.N {\n\t\tvar skb *Struct\n\t\tif err := base.Copy().TypeByName(\"sk_buff\", &skb); err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t\ttypes = append(types, skb)\n\t}\n\n\tdedup := newDeduper()\n\n\tb.ResetTimer()\n\n\tfor i := range b.N {\n\t\tif _, err := dedup.deduplicate(types[i]); err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkDeduplicateVMLinux(b *testing.B) {\n\tb.ReportAllocs()\n\n\tvmlinux := vmlinuxTestdataBytes(b)\n\tbase, err := loadRawSpec(vmlinux, nil)\n\tqt.Assert(b, qt.IsNil(err))\n\n\tvar types [][]Type\n\tfor range b.N {\n\t\tvar specTypes []Type\n\t\tfor typ := range base.Copy().All() {\n\t\t\tspecTypes = append(specTypes, typ)\n\t\t}\n\t\ttypes = append(types, specTypes)\n\t}\n\n\tdedup := newDeduper()\n\n\tb.ResetTimer()\n\n\tfor i := range b.N {\n\t\tfor _, typ := range types[i] {\n\t\t\tif _, err := dedup.deduplicate(typ); err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "btf/doc.go",
    "content": "// Package btf handles data encoded according to the BPF Type Format.\n//\n// The canonical documentation lives in the Linux kernel repository and is\n// available at https://www.kernel.org/doc/html/latest/bpf/btf.html\npackage btf\n"
  },
  {
    "path": "btf/ext_info.go",
    "content": "package btf\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"sort\"\n\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/internal\"\n)\n\n// ExtInfos contains raw, per-section extended BTF metadata from the .BTF.ext\n// ELF section.\ntype ExtInfos struct {\n\tFuncs     map[string]FuncOffsets\n\tLines     map[string]LineOffsets\n\tCORERelos map[string]CORERelocationOffsets\n}\n\n// Section returns the FuncOffsets, LineOffsets and CORERelocationOffsets for\n// the given section name. Returns all nils if ExtInfos is nil, or individual\n// nils if there is no metadata of that type for the section.\nfunc (ei *ExtInfos) Section(name string) (FuncOffsets, LineOffsets, CORERelocationOffsets) {\n\tif ei == nil {\n\t\treturn nil, nil, nil\n\t}\n\n\treturn ei.Funcs[name], ei.Lines[name], ei.CORERelos[name]\n}\n\n// loadExtInfosFromELF parses ext infos from the .BTF.ext section in an ELF.\n//\n// Returns an error wrapping ErrNotFound if no ext infos are present.\nfunc loadExtInfosFromELF(file *internal.SafeELFFile, spec *Spec) (*ExtInfos, error) {\n\tsection := file.Section(\".BTF.ext\")\n\tif section == nil {\n\t\treturn nil, fmt.Errorf(\"btf ext infos: %w\", ErrNotFound)\n\t}\n\n\tif section.ReaderAt == nil {\n\t\treturn nil, fmt.Errorf(\"compressed ext_info is not supported\")\n\t}\n\n\treturn loadExtInfos(section.ReaderAt, file.ByteOrder, spec)\n}\n\n// loadExtInfos parses bare ext infos.\nfunc loadExtInfos(r io.ReaderAt, bo binary.ByteOrder, spec *Spec) (*ExtInfos, error) {\n\t// Open unbuffered section reader. binary.Read() calls io.ReadFull on\n\t// the header structs, resulting in one syscall per header.\n\theaderRd := io.NewSectionReader(r, 0, math.MaxInt64)\n\textHeader, err := parseBTFExtHeader(headerRd, bo)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"parsing BTF extension header: %w\", err)\n\t}\n\n\tcoreHeader, err := parseBTFExtCOREHeader(headerRd, bo, extHeader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"parsing BTF CO-RE header: %w\", err)\n\t}\n\n\tbuf := internal.NewBufferedSectionReader(r, extHeader.funcInfoStart(), int64(extHeader.FuncInfoLen))\n\tbtfFuncInfos, err := parseFuncInfos(buf, bo, spec.strings)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"parsing BTF function info: %w\", err)\n\t}\n\n\tfuncInfos := make(map[string]FuncOffsets, len(btfFuncInfos))\n\tfor section, bfis := range btfFuncInfos {\n\t\tfuncInfos[section], err = newFuncOffsets(bfis, spec)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"section %s: func infos: %w\", section, err)\n\t\t}\n\t}\n\n\tbuf = internal.NewBufferedSectionReader(r, extHeader.lineInfoStart(), int64(extHeader.LineInfoLen))\n\tbtfLineInfos, err := parseLineInfos(buf, bo, spec.strings)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"parsing BTF line info: %w\", err)\n\t}\n\n\tlineInfos := make(map[string]LineOffsets, len(btfLineInfos))\n\tfor section, blis := range btfLineInfos {\n\t\tlineInfos[section], err = newLineInfos(blis, spec.strings)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"section %s: line infos: %w\", section, err)\n\t\t}\n\t}\n\n\tif coreHeader == nil || coreHeader.COREReloLen == 0 {\n\t\treturn &ExtInfos{funcInfos, lineInfos, nil}, nil\n\t}\n\n\tvar btfCORERelos map[string][]bpfCORERelo\n\tbuf = internal.NewBufferedSectionReader(r, extHeader.coreReloStart(coreHeader), int64(coreHeader.COREReloLen))\n\tbtfCORERelos, err = parseCORERelos(buf, bo, spec.strings)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"parsing CO-RE relocation info: %w\", err)\n\t}\n\n\tcoreRelos := make(map[string]CORERelocationOffsets, len(btfCORERelos))\n\tfor section, brs := range btfCORERelos {\n\t\tcoreRelos[section], err = newRelocationInfos(brs, spec, spec.strings)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"section %s: CO-RE relocations: %w\", section, err)\n\t\t}\n\t}\n\n\treturn &ExtInfos{funcInfos, lineInfos, coreRelos}, nil\n}\n\n// MarshalExtInfos encodes function and line info embedded in insns into kernel\n// wire format.\n//\n// If an instruction has an [asm.Comment], it will be synthesized into a mostly\n// empty line info.\nfunc MarshalExtInfos(insns asm.Instructions, b *Builder) (funcInfos, lineInfos []byte, _ error) {\n\titer := insns.Iterate()\n\tfor iter.Next() {\n\t\tif iter.Ins.Source() != nil || FuncMetadata(iter.Ins) != nil {\n\t\t\tgoto marshal\n\t\t}\n\t}\n\n\treturn nil, nil, nil\n\nmarshal:\n\tvar fiBuf, liBuf bytes.Buffer\n\tfor {\n\t\tif fn := FuncMetadata(iter.Ins); fn != nil {\n\t\t\tfi := &FuncOffset{\n\t\t\t\tFunc:   fn,\n\t\t\t\tOffset: iter.Offset,\n\t\t\t}\n\t\t\tif err := fi.marshal(&fiBuf, b); err != nil {\n\t\t\t\treturn nil, nil, fmt.Errorf(\"write func info: %w\", err)\n\t\t\t}\n\t\t}\n\n\t\tif source := iter.Ins.Source(); source != nil {\n\t\t\tvar line *Line\n\t\t\tif l, ok := source.(*Line); ok {\n\t\t\t\tline = l\n\t\t\t} else {\n\t\t\t\tline = &Line{\n\t\t\t\t\tline: source.String(),\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tli := &LineOffset{\n\t\t\t\tOffset: iter.Offset,\n\t\t\t\tLine:   line,\n\t\t\t}\n\t\t\tif err := li.marshal(&liBuf, b); err != nil {\n\t\t\t\treturn nil, nil, fmt.Errorf(\"write line info: %w\", err)\n\t\t\t}\n\t\t}\n\n\t\tif !iter.Next() {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn fiBuf.Bytes(), liBuf.Bytes(), nil\n}\n\n// btfExtHeader is found at the start of the .BTF.ext section.\ntype btfExtHeader struct {\n\tMagic   uint16\n\tVersion uint8\n\tFlags   uint8\n\n\t// HdrLen is larger than the size of struct btfExtHeader when it is\n\t// immediately followed by a btfExtCOREHeader.\n\tHdrLen uint32\n\n\tFuncInfoOff uint32\n\tFuncInfoLen uint32\n\tLineInfoOff uint32\n\tLineInfoLen uint32\n}\n\n// parseBTFExtHeader parses the header of the .BTF.ext section.\nfunc parseBTFExtHeader(r io.Reader, bo binary.ByteOrder) (*btfExtHeader, error) {\n\tvar header btfExtHeader\n\tif err := binary.Read(r, bo, &header); err != nil {\n\t\treturn nil, fmt.Errorf(\"can't read header: %v\", err)\n\t}\n\n\tif header.Magic != btfMagic {\n\t\treturn nil, fmt.Errorf(\"incorrect magic value %v\", header.Magic)\n\t}\n\n\tif header.Version != 1 {\n\t\treturn nil, fmt.Errorf(\"unexpected version %v\", header.Version)\n\t}\n\n\tif header.Flags != 0 {\n\t\treturn nil, fmt.Errorf(\"unsupported flags %v\", header.Flags)\n\t}\n\n\tif int64(header.HdrLen) < int64(binary.Size(&header)) {\n\t\treturn nil, fmt.Errorf(\"header length shorter than btfExtHeader size\")\n\t}\n\n\treturn &header, nil\n}\n\n// funcInfoStart returns the offset from the beginning of the .BTF.ext section\n// to the start of its func_info entries.\nfunc (h *btfExtHeader) funcInfoStart() int64 {\n\treturn int64(h.HdrLen + h.FuncInfoOff)\n}\n\n// lineInfoStart returns the offset from the beginning of the .BTF.ext section\n// to the start of its line_info entries.\nfunc (h *btfExtHeader) lineInfoStart() int64 {\n\treturn int64(h.HdrLen + h.LineInfoOff)\n}\n\n// coreReloStart returns the offset from the beginning of the .BTF.ext section\n// to the start of its CO-RE relocation entries.\nfunc (h *btfExtHeader) coreReloStart(ch *btfExtCOREHeader) int64 {\n\treturn int64(h.HdrLen + ch.COREReloOff)\n}\n\n// btfExtCOREHeader is found right after the btfExtHeader when its HdrLen\n// field is larger than its size.\ntype btfExtCOREHeader struct {\n\tCOREReloOff uint32\n\tCOREReloLen uint32\n}\n\n// parseBTFExtCOREHeader parses the tail of the .BTF.ext header. If additional\n// header bytes are present, extHeader.HdrLen will be larger than the struct,\n// indicating the presence of a CO-RE extension header.\nfunc parseBTFExtCOREHeader(r io.Reader, bo binary.ByteOrder, extHeader *btfExtHeader) (*btfExtCOREHeader, error) {\n\textHdrSize := int64(binary.Size(&extHeader))\n\tremainder := int64(extHeader.HdrLen) - extHdrSize\n\n\tif remainder == 0 {\n\t\treturn nil, nil\n\t}\n\n\tvar coreHeader btfExtCOREHeader\n\tif err := binary.Read(r, bo, &coreHeader); err != nil {\n\t\treturn nil, fmt.Errorf(\"can't read header: %v\", err)\n\t}\n\n\treturn &coreHeader, nil\n}\n\ntype btfExtInfoSec struct {\n\tSecNameOff uint32\n\tNumInfo    uint32\n}\n\n// parseExtInfoSec parses a btf_ext_info_sec header within .BTF.ext,\n// appearing within func_info and line_info sub-sections.\n// These headers appear once for each program section in the ELF and are\n// followed by one or more func/line_info records for the section.\nfunc parseExtInfoSec(r io.Reader, bo binary.ByteOrder, strings *stringTable) (string, *btfExtInfoSec, error) {\n\tvar infoHeader btfExtInfoSec\n\tif err := binary.Read(r, bo, &infoHeader); err != nil {\n\t\treturn \"\", nil, fmt.Errorf(\"read ext info header: %w\", err)\n\t}\n\n\tsecName, err := strings.Lookup(infoHeader.SecNameOff)\n\tif err != nil {\n\t\treturn \"\", nil, fmt.Errorf(\"get section name: %w\", err)\n\t}\n\tif secName == \"\" {\n\t\treturn \"\", nil, fmt.Errorf(\"extinfo header refers to empty section name\")\n\t}\n\n\tif infoHeader.NumInfo == 0 {\n\t\treturn \"\", nil, fmt.Errorf(\"section %s has zero records\", secName)\n\t}\n\n\treturn secName, &infoHeader, nil\n}\n\n// parseExtInfoRecordSize parses the uint32 at the beginning of a func_infos\n// or line_infos segment that describes the length of all extInfoRecords in\n// that segment.\nfunc parseExtInfoRecordSize(r io.Reader, bo binary.ByteOrder) (uint32, error) {\n\tconst maxRecordSize = 256\n\n\tvar recordSize uint32\n\tif err := binary.Read(r, bo, &recordSize); err != nil {\n\t\treturn 0, fmt.Errorf(\"can't read record size: %v\", err)\n\t}\n\n\tif recordSize < 4 {\n\t\t// Need at least InsnOff worth of bytes per record.\n\t\treturn 0, errors.New(\"record size too short\")\n\t}\n\tif recordSize > maxRecordSize {\n\t\treturn 0, fmt.Errorf(\"record size %v exceeds %v\", recordSize, maxRecordSize)\n\t}\n\n\treturn recordSize, nil\n}\n\n// FuncOffsets is a slice of FuncOffsets sorted by offset.\ntype FuncOffsets = []FuncOffset\n\n// The size of a FuncInfo in BTF wire format.\nvar FuncInfoSize = uint32(binary.Size(bpfFuncInfo{}))\n\n// FuncOffset represents a [btf.Func] and its raw instruction offset within a\n// BPF program.\ntype FuncOffset struct {\n\tOffset asm.RawInstructionOffset\n\tFunc   *Func\n}\n\ntype bpfFuncInfo struct {\n\t// Instruction offset of the function within an ELF section.\n\tInsnOff uint32\n\tTypeID  TypeID\n}\n\nfunc newFuncOffset(fi bpfFuncInfo, spec *Spec) (*FuncOffset, error) {\n\ttyp, err := spec.TypeByID(fi.TypeID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfn, ok := typ.(*Func)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"type ID %d is a %T, but expected a Func\", fi.TypeID, typ)\n\t}\n\n\t// C doesn't have anonymous functions, but check just in case.\n\tif fn.Name == \"\" {\n\t\treturn nil, fmt.Errorf(\"func with type ID %d doesn't have a name\", fi.TypeID)\n\t}\n\n\treturn &FuncOffset{\n\t\tasm.RawInstructionOffset(fi.InsnOff),\n\t\tfn,\n\t}, nil\n}\n\nfunc newFuncOffsets(bfis []bpfFuncInfo, spec *Spec) (FuncOffsets, error) {\n\tfos := make(FuncOffsets, 0, len(bfis))\n\n\tfor _, bfi := range bfis {\n\t\tfi, err := newFuncOffset(bfi, spec)\n\t\tif err != nil {\n\t\t\treturn FuncOffsets{}, fmt.Errorf(\"offset %d: %w\", bfi.InsnOff, err)\n\t\t}\n\t\tfos = append(fos, *fi)\n\t}\n\tsort.Slice(fos, func(i, j int) bool {\n\t\treturn fos[i].Offset <= fos[j].Offset\n\t})\n\treturn fos, nil\n}\n\n// LoadFuncInfos parses BTF func info from kernel wire format into a\n// [FuncOffsets], a sorted slice of [btf.Func]s of (sub)programs within a BPF\n// program with their corresponding raw instruction offsets.\nfunc LoadFuncInfos(reader io.Reader, bo binary.ByteOrder, recordNum uint32, spec *Spec) (FuncOffsets, error) {\n\tfis, err := parseFuncInfoRecords(\n\t\treader,\n\t\tbo,\n\t\tFuncInfoSize,\n\t\trecordNum,\n\t\tfalse,\n\t)\n\tif err != nil {\n\t\treturn FuncOffsets{}, fmt.Errorf(\"parsing BTF func info: %w\", err)\n\t}\n\n\treturn newFuncOffsets(fis, spec)\n}\n\n// marshal into the BTF wire format.\nfunc (fi *FuncOffset) marshal(w *bytes.Buffer, b *Builder) error {\n\tid, err := b.Add(fi.Func)\n\tif err != nil {\n\t\treturn err\n\t}\n\tbfi := bpfFuncInfo{\n\t\tInsnOff: uint32(fi.Offset),\n\t\tTypeID:  id,\n\t}\n\tbuf := make([]byte, FuncInfoSize)\n\tinternal.NativeEndian.PutUint32(buf, bfi.InsnOff)\n\tinternal.NativeEndian.PutUint32(buf[4:], uint32(bfi.TypeID))\n\t_, err = w.Write(buf)\n\treturn err\n}\n\n// parseFuncInfos parses a func_info sub-section within .BTF.ext ito a map of\n// func infos indexed by section name.\nfunc parseFuncInfos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map[string][]bpfFuncInfo, error) {\n\trecordSize, err := parseExtInfoRecordSize(r, bo)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tresult := make(map[string][]bpfFuncInfo)\n\tfor {\n\t\tsecName, infoHeader, err := parseExtInfoSec(r, bo, strings)\n\t\tif errors.Is(err, io.EOF) {\n\t\t\treturn result, nil\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\trecords, err := parseFuncInfoRecords(r, bo, recordSize, infoHeader.NumInfo, true)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"section %v: %w\", secName, err)\n\t\t}\n\n\t\tresult[secName] = records\n\t}\n}\n\n// parseFuncInfoRecords parses a stream of func_infos into a funcInfos.\n// These records appear after a btf_ext_info_sec header in the func_info\n// sub-section of .BTF.ext.\nfunc parseFuncInfoRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, recordNum uint32, offsetInBytes bool) ([]bpfFuncInfo, error) {\n\tvar out []bpfFuncInfo\n\tvar fi bpfFuncInfo\n\n\tif exp, got := FuncInfoSize, recordSize; exp != got {\n\t\t// BTF blob's record size is longer than we know how to parse.\n\t\treturn nil, fmt.Errorf(\"expected FuncInfo record size %d, but BTF blob contains %d\", exp, got)\n\t}\n\n\tfor i := uint32(0); i < recordNum; i++ {\n\t\tif err := binary.Read(r, bo, &fi); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"can't read function info: %v\", err)\n\t\t}\n\n\t\tif offsetInBytes {\n\t\t\tif fi.InsnOff%asm.InstructionSize != 0 {\n\t\t\t\treturn nil, fmt.Errorf(\"offset %v is not aligned with instruction size\", fi.InsnOff)\n\t\t\t}\n\n\t\t\t// ELF tracks offset in bytes, the kernel expects raw BPF instructions.\n\t\t\t// Convert as early as possible.\n\t\t\tfi.InsnOff /= asm.InstructionSize\n\t\t}\n\n\t\tout = append(out, fi)\n\t}\n\n\treturn out, nil\n}\n\nvar LineInfoSize = uint32(binary.Size(bpfLineInfo{}))\n\n// Line represents the location and contents of a single line of source\n// code a BPF ELF was compiled from.\ntype Line struct {\n\tfileName   string\n\tline       string\n\tlineNumber uint32\n\tlineColumn uint32\n}\n\nfunc (li *Line) FileName() string {\n\treturn li.fileName\n}\n\nfunc (li *Line) Line() string {\n\treturn li.line\n}\n\nfunc (li *Line) LineNumber() uint32 {\n\treturn li.lineNumber\n}\n\nfunc (li *Line) LineColumn() uint32 {\n\treturn li.lineColumn\n}\n\nfunc (li *Line) String() string {\n\treturn li.line\n}\n\n// LineOffsets is a slice of LineOffsets sorted by offset.\ntype LineOffsets = []LineOffset\n\n// LineOffset represents a line info and its raw instruction offset.\ntype LineOffset struct {\n\tOffset asm.RawInstructionOffset\n\tLine   *Line\n}\n\n// Constants for the format of bpfLineInfo.LineCol.\nconst (\n\tbpfLineShift = 10\n\tbpfLineMax   = (1 << (32 - bpfLineShift)) - 1\n\tbpfColumnMax = (1 << bpfLineShift) - 1\n)\n\ntype bpfLineInfo struct {\n\t// Instruction offset of the line within the whole instruction stream, in instructions.\n\tInsnOff     uint32\n\tFileNameOff uint32\n\tLineOff     uint32\n\tLineCol     uint32\n}\n\n// LoadLineInfos parses BTF line info in kernel wire format.\nfunc LoadLineInfos(reader io.Reader, bo binary.ByteOrder, recordNum uint32, spec *Spec) (LineOffsets, error) {\n\tlis, err := parseLineInfoRecords(\n\t\treader,\n\t\tbo,\n\t\tLineInfoSize,\n\t\trecordNum,\n\t\tfalse,\n\t)\n\tif err != nil {\n\t\treturn LineOffsets{}, fmt.Errorf(\"parsing BTF line info: %w\", err)\n\t}\n\n\treturn newLineInfos(lis, spec.strings)\n}\n\nfunc newLineInfo(li bpfLineInfo, strings *stringTable) (LineOffset, error) {\n\tline, err := strings.LookupCached(li.LineOff)\n\tif err != nil {\n\t\treturn LineOffset{}, fmt.Errorf(\"lookup of line: %w\", err)\n\t}\n\n\tfileName, err := strings.LookupCached(li.FileNameOff)\n\tif err != nil {\n\t\treturn LineOffset{}, fmt.Errorf(\"lookup of filename: %w\", err)\n\t}\n\n\tlineNumber := li.LineCol >> bpfLineShift\n\tlineColumn := li.LineCol & bpfColumnMax\n\n\treturn LineOffset{\n\t\tasm.RawInstructionOffset(li.InsnOff),\n\t\t&Line{\n\t\t\tfileName,\n\t\t\tline,\n\t\t\tlineNumber,\n\t\t\tlineColumn,\n\t\t},\n\t}, nil\n}\n\nfunc newLineInfos(blis []bpfLineInfo, strings *stringTable) (LineOffsets, error) {\n\tlis := make([]LineOffset, 0, len(blis))\n\tfor _, bli := range blis {\n\t\tli, err := newLineInfo(bli, strings)\n\t\tif err != nil {\n\t\t\treturn LineOffsets{}, fmt.Errorf(\"offset %d: %w\", bli.InsnOff, err)\n\t\t}\n\t\tlis = append(lis, li)\n\t}\n\tsort.Slice(lis, func(i, j int) bool {\n\t\treturn lis[i].Offset <= lis[j].Offset\n\t})\n\treturn lis, nil\n}\n\n// marshal writes the binary representation of the LineInfo to w.\nfunc (li *LineOffset) marshal(w *bytes.Buffer, b *Builder) error {\n\tline := li.Line\n\tif line.lineNumber > bpfLineMax {\n\t\treturn fmt.Errorf(\"line %d exceeds %d\", line.lineNumber, bpfLineMax)\n\t}\n\n\tif line.lineColumn > bpfColumnMax {\n\t\treturn fmt.Errorf(\"column %d exceeds %d\", line.lineColumn, bpfColumnMax)\n\t}\n\n\tfileNameOff, err := b.addString(line.fileName)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"file name %q: %w\", line.fileName, err)\n\t}\n\n\tlineOff, err := b.addString(line.line)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"line %q: %w\", line.line, err)\n\t}\n\n\tbli := bpfLineInfo{\n\t\tuint32(li.Offset),\n\t\tfileNameOff,\n\t\tlineOff,\n\t\t(line.lineNumber << bpfLineShift) | line.lineColumn,\n\t}\n\n\tbuf := make([]byte, LineInfoSize)\n\tinternal.NativeEndian.PutUint32(buf, bli.InsnOff)\n\tinternal.NativeEndian.PutUint32(buf[4:], bli.FileNameOff)\n\tinternal.NativeEndian.PutUint32(buf[8:], bli.LineOff)\n\tinternal.NativeEndian.PutUint32(buf[12:], bli.LineCol)\n\t_, err = w.Write(buf)\n\treturn err\n}\n\n// parseLineInfos parses a line_info sub-section within .BTF.ext ito a map of\n// line infos indexed by section name.\nfunc parseLineInfos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map[string][]bpfLineInfo, error) {\n\trecordSize, err := parseExtInfoRecordSize(r, bo)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tresult := make(map[string][]bpfLineInfo)\n\tfor {\n\t\tsecName, infoHeader, err := parseExtInfoSec(r, bo, strings)\n\t\tif errors.Is(err, io.EOF) {\n\t\t\treturn result, nil\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\trecords, err := parseLineInfoRecords(r, bo, recordSize, infoHeader.NumInfo, true)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"section %v: %w\", secName, err)\n\t\t}\n\n\t\tresult[secName] = records\n\t}\n}\n\n// parseLineInfoRecords parses a stream of line_infos into a lineInfos.\n// These records appear after a btf_ext_info_sec header in the line_info\n// sub-section of .BTF.ext.\nfunc parseLineInfoRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, recordNum uint32, offsetInBytes bool) ([]bpfLineInfo, error) {\n\tif exp, got := uint32(binary.Size(bpfLineInfo{})), recordSize; exp != got {\n\t\t// BTF blob's record size is longer than we know how to parse.\n\t\treturn nil, fmt.Errorf(\"expected LineInfo record size %d, but BTF blob contains %d\", exp, got)\n\t}\n\n\tout := make([]bpfLineInfo, recordNum)\n\tif err := binary.Read(r, bo, out); err != nil {\n\t\treturn nil, fmt.Errorf(\"can't read line info: %v\", err)\n\t}\n\n\tif offsetInBytes {\n\t\tfor i := range out {\n\t\t\tli := &out[i]\n\t\t\tif li.InsnOff%asm.InstructionSize != 0 {\n\t\t\t\treturn nil, fmt.Errorf(\"offset %v is not aligned with instruction size\", li.InsnOff)\n\t\t\t}\n\n\t\t\t// ELF tracks offset in bytes, the kernel expects raw BPF instructions.\n\t\t\t// Convert as early as possible.\n\t\t\tli.InsnOff /= asm.InstructionSize\n\t\t}\n\t}\n\n\treturn out, nil\n}\n\n// bpfCORERelo matches the kernel's struct bpf_core_relo.\ntype bpfCORERelo struct {\n\tInsnOff      uint32\n\tTypeID       TypeID\n\tAccessStrOff uint32\n\tKind         coreKind\n}\n\ntype CORERelocation struct {\n\t// The local type of the relocation, stripped of typedefs and qualifiers.\n\ttyp      Type\n\taccessor coreAccessor\n\tkind     coreKind\n\t// The ID of the local type in the source BTF.\n\tid TypeID\n}\n\nfunc (cr *CORERelocation) String() string {\n\treturn fmt.Sprintf(\"CORERelocation(%s, %s[%s], local_id=%d)\", cr.kind, cr.typ, cr.accessor, cr.id)\n}\n\ntype coreRelocationMeta struct{}\n\n// CORERelocationMetadata returns the CORERelocation associated with ins.\nfunc CORERelocationMetadata(ins *asm.Instruction) *CORERelocation {\n\trelo, _ := ins.Metadata.Get(coreRelocationMeta{}).(*CORERelocation)\n\treturn relo\n}\n\n// WithCORERelocationMetadata associates a CORERelocation with ins and returns\n// the modified Instruction.\nfunc WithCORERelocationMetadata(ins asm.Instruction, relo *CORERelocation) asm.Instruction {\n\tins.Metadata.Set(coreRelocationMeta{}, relo)\n\treturn ins\n}\n\n// CORERelocationOffsets is a slice of CORERelocationOffsets sorted by offset.\ntype CORERelocationOffsets = []CORERelocationOffset\n\n// CORERelocationOffset represents a CO-RE relocation and an offset at which it\n// should be applied.\ntype CORERelocationOffset struct {\n\tRelo   *CORERelocation\n\tOffset asm.RawInstructionOffset\n}\n\nfunc newRelocationInfo(relo bpfCORERelo, spec *Spec, strings *stringTable) (*CORERelocationOffset, error) {\n\ttyp, err := spec.TypeByID(relo.TypeID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\taccessorStr, err := strings.Lookup(relo.AccessStrOff)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\taccessor, err := parseCOREAccessor(accessorStr)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"accessor %q: %s\", accessorStr, err)\n\t}\n\n\treturn &CORERelocationOffset{\n\t\t&CORERelocation{\n\t\t\ttyp,\n\t\t\taccessor,\n\t\t\trelo.Kind,\n\t\t\trelo.TypeID,\n\t\t},\n\t\tasm.RawInstructionOffset(relo.InsnOff),\n\t}, nil\n}\n\nfunc newRelocationInfos(brs []bpfCORERelo, spec *Spec, strings *stringTable) (CORERelocationOffsets, error) {\n\trs := make(CORERelocationOffsets, 0, len(brs))\n\n\tfor _, br := range brs {\n\t\trelo, err := newRelocationInfo(br, spec, strings)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"offset %d: %w\", br.InsnOff, err)\n\t\t}\n\t\trs = append(rs, *relo)\n\t}\n\n\tsort.Slice(rs, func(i, j int) bool {\n\t\treturn rs[i].Offset < rs[j].Offset\n\t})\n\n\treturn rs, nil\n}\n\nvar extInfoReloSize = binary.Size(bpfCORERelo{})\n\n// parseCORERelos parses a core_relos sub-section within .BTF.ext ito a map of\n// CO-RE relocations indexed by section name.\nfunc parseCORERelos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map[string][]bpfCORERelo, error) {\n\trecordSize, err := parseExtInfoRecordSize(r, bo)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif recordSize != uint32(extInfoReloSize) {\n\t\treturn nil, fmt.Errorf(\"expected record size %d, got %d\", extInfoReloSize, recordSize)\n\t}\n\n\tresult := make(map[string][]bpfCORERelo)\n\tfor {\n\t\tsecName, infoHeader, err := parseExtInfoSec(r, bo, strings)\n\t\tif errors.Is(err, io.EOF) {\n\t\t\treturn result, nil\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\trecords, err := parseCOREReloRecords(r, bo, infoHeader.NumInfo)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"section %v: %w\", secName, err)\n\t\t}\n\n\t\tresult[secName] = records\n\t}\n}\n\n// parseCOREReloRecords parses a stream of CO-RE relocation entries into a\n// coreRelos. These records appear after a btf_ext_info_sec header in the\n// core_relos sub-section of .BTF.ext.\nfunc parseCOREReloRecords(r io.Reader, bo binary.ByteOrder, recordNum uint32) ([]bpfCORERelo, error) {\n\tvar out []bpfCORERelo\n\n\tvar relo bpfCORERelo\n\tfor i := uint32(0); i < recordNum; i++ {\n\t\tif err := binary.Read(r, bo, &relo); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"can't read CO-RE relocation: %v\", err)\n\t\t}\n\n\t\tif relo.InsnOff%asm.InstructionSize != 0 {\n\t\t\treturn nil, fmt.Errorf(\"offset %v is not aligned with instruction size\", relo.InsnOff)\n\t\t}\n\n\t\t// ELF tracks offset in bytes, the kernel expects raw BPF instructions.\n\t\t// Convert as early as possible.\n\t\trelo.InsnOff /= asm.InstructionSize\n\n\t\tout = append(out, relo)\n\t}\n\n\treturn out, nil\n}\n"
  },
  {
    "path": "btf/ext_info_test.go",
    "content": "package btf\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestParseExtInfoBigRecordSize(t *testing.T) {\n\trd := strings.NewReader(\"\\xff\\xff\\xff\\xff\\x00\\x00\\x00\\x000709171295166016\")\n\ttable, err := readStringTable(bytes.NewReader([]byte{0}), nil)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif _, err := parseFuncInfos(rd, internal.NativeEndian, table); err == nil {\n\t\tt.Error(\"Parsing func info with large record size doesn't return an error\")\n\t}\n\n\tif _, err := parseLineInfos(rd, internal.NativeEndian, table); err == nil {\n\t\tt.Error(\"Parsing line info with large record size doesn't return an error\")\n\t}\n}\n\nfunc BenchmarkParseLineInfoRecords(b *testing.B) {\n\tsize := uint32(binary.Size(bpfLineInfo{}))\n\tcount := uint32(4096)\n\tbuf := make([]byte, size*count)\n\n\tb.ReportAllocs()\n\n\tfor b.Loop() {\n\t\tparseLineInfoRecords(bytes.NewReader(buf), internal.NativeEndian, size, count, true)\n\t}\n}\n\nfunc TestParseLineInfoRecordsAllocations(t *testing.T) {\n\tsize := uint32(binary.Size(bpfLineInfo{}))\n\tcount := uint32(4096)\n\tbuf := make([]byte, size*count)\n\n\tallocs := testing.AllocsPerRun(5, func() {\n\t\tparseLineInfoRecords(bytes.NewReader(buf), internal.NativeEndian, size, count, true)\n\t})\n\n\t// 7 is the number of allocations on go 1.22\n\t// what we want to test is that we are not allocating\n\t// once per record\n\tqt.Assert(t, qt.IsTrue(allocs <= 7))\n}\n"
  },
  {
    "path": "btf/feature.go",
    "content": "package btf\n\nimport (\n\t\"errors\"\n\t\"math\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\n// haveBTF attempts to load a BTF blob containing an Int. It should pass on any\n// kernel that supports BPF_BTF_LOAD.\nvar haveBTF = internal.NewFeatureTest(\"BTF\", func() error {\n\t// 0-length anonymous integer\n\terr := probeBTF(&Int{})\n\tif errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM) {\n\t\treturn internal.ErrNotSupported\n\t}\n\treturn err\n}, \"4.18\")\n\n// haveMapBTF attempts to load a minimal BTF blob containing a Var. It is\n// used as a proxy for .bss, .data and .rodata map support, which generally\n// come with a Var and Datasec. These were introduced in Linux 5.2.\nvar haveMapBTF = internal.NewFeatureTest(\"Map BTF (Var/Datasec)\", func() error {\n\tif err := haveBTF(); err != nil {\n\t\treturn err\n\t}\n\n\tv := &Var{\n\t\tName: \"a\",\n\t\tType: &Pointer{(*Void)(nil)},\n\t}\n\n\terr := probeBTF(v)\n\tif errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM) {\n\t\t// Treat both EINVAL and EPERM as not supported: creating the map may still\n\t\t// succeed without Btf* attrs.\n\t\treturn internal.ErrNotSupported\n\t}\n\treturn err\n}, \"5.2\")\n\n// haveProgBTF attempts to load a BTF blob containing a Func and FuncProto. It\n// is used as a proxy for ext_info (func_info) support, which depends on\n// Func(Proto) by definition.\nvar haveProgBTF = internal.NewFeatureTest(\"Program BTF (func/line_info)\", func() error {\n\tif err := haveBTF(); err != nil {\n\t\treturn err\n\t}\n\n\tfn := &Func{\n\t\tName: \"a\",\n\t\tType: &FuncProto{Return: (*Void)(nil)},\n\t}\n\n\terr := probeBTF(fn)\n\tif errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM) {\n\t\treturn internal.ErrNotSupported\n\t}\n\treturn err\n}, \"5.0\")\n\nvar haveFuncLinkage = internal.NewFeatureTest(\"BTF func linkage\", func() error {\n\tif err := haveProgBTF(); err != nil {\n\t\treturn err\n\t}\n\n\tfn := &Func{\n\t\tName:    \"a\",\n\t\tType:    &FuncProto{Return: (*Void)(nil)},\n\t\tLinkage: GlobalFunc,\n\t}\n\n\terr := probeBTF(fn)\n\tif errors.Is(err, unix.EINVAL) {\n\t\treturn internal.ErrNotSupported\n\t}\n\treturn err\n}, \"5.6\")\n\nvar haveDeclTags = internal.NewFeatureTest(\"BTF decl tags\", func() error {\n\tif err := haveBTF(); err != nil {\n\t\treturn err\n\t}\n\n\tt := &Typedef{\n\t\tName: \"a\",\n\t\tType: &Int{},\n\t\tTags: []string{\"a\"},\n\t}\n\n\terr := probeBTF(t)\n\tif errors.Is(err, unix.EINVAL) {\n\t\treturn internal.ErrNotSupported\n\t}\n\treturn err\n}, \"5.16\")\n\nvar haveTypeTags = internal.NewFeatureTest(\"BTF type tags\", func() error {\n\tif err := haveBTF(); err != nil {\n\t\treturn err\n\t}\n\n\tt := &TypeTag{\n\t\tType:  &Int{},\n\t\tValue: \"a\",\n\t}\n\n\terr := probeBTF(t)\n\tif errors.Is(err, unix.EINVAL) {\n\t\treturn internal.ErrNotSupported\n\t}\n\treturn err\n}, \"5.17\")\n\nvar haveEnum64 = internal.NewFeatureTest(\"ENUM64\", func() error {\n\tif err := haveBTF(); err != nil {\n\t\treturn err\n\t}\n\n\tenum := &Enum{\n\t\tSize: 8,\n\t\tValues: []EnumValue{\n\t\t\t{\"TEST\", math.MaxUint32 + 1},\n\t\t},\n\t}\n\n\terr := probeBTF(enum)\n\tif errors.Is(err, unix.EINVAL) {\n\t\treturn internal.ErrNotSupported\n\t}\n\treturn err\n}, \"6.0\")\n\nfunc probeBTF(typ Type) error {\n\tb, err := NewBuilder([]Type{typ}, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tbuf, err := b.Marshal(nil, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfd, err := sys.BtfLoad(&sys.BtfLoadAttr{\n\t\tBtf:     sys.SlicePointer(buf),\n\t\tBtfSize: uint32(len(buf)),\n\t})\n\n\tif err == nil {\n\t\tfd.Close()\n\t}\n\n\treturn err\n}\n"
  },
  {
    "path": "btf/feature_test.go",
    "content": "package btf\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc TestHaveBTF(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, haveBTF)\n}\n\nfunc TestHaveMapBTF(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, haveMapBTF)\n}\n\nfunc TestHaveProgBTF(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, haveProgBTF)\n}\n\nfunc TestHaveFuncLinkage(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, haveFuncLinkage)\n}\n\nfunc TestHaveDeclTags(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, haveDeclTags)\n}\n\nfunc TestHaveTypeTags(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, haveTypeTags)\n}\n\nfunc TestHaveEnum64(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, haveEnum64)\n}\n"
  },
  {
    "path": "btf/format.go",
    "content": "package btf\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n)\n\nvar errNestedTooDeep = errors.New(\"nested too deep\")\n\n// GoFormatter converts a Type to Go syntax.\n//\n// A zero GoFormatter is valid to use.\ntype GoFormatter struct {\n\tw strings.Builder\n\n\t// Types present in this map are referred to using the given name if they\n\t// are encountered when outputting another type.\n\tNames map[Type]string\n\n\t// Identifier is called for each field of struct-like types. By default the\n\t// field name is used as is.\n\tIdentifier func(string) string\n\n\t// EnumIdentifier is called for each element of an enum. By default the\n\t// name of the enum type is concatenated with Identifier(element).\n\tEnumIdentifier func(name, element string) string\n}\n\n// TypeDeclaration generates a Go type declaration for a BTF type.\nfunc (gf *GoFormatter) TypeDeclaration(name string, typ Type) (string, error) {\n\tgf.w.Reset()\n\tif err := gf.writeTypeDecl(name, typ); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn gf.w.String(), nil\n}\n\nfunc (gf *GoFormatter) identifier(s string) string {\n\tif gf.Identifier != nil {\n\t\treturn gf.Identifier(s)\n\t}\n\n\treturn s\n}\n\nfunc (gf *GoFormatter) enumIdentifier(name, element string) string {\n\tif gf.EnumIdentifier != nil {\n\t\treturn gf.EnumIdentifier(name, element)\n\t}\n\n\treturn name + gf.identifier(element)\n}\n\n// writeTypeDecl outputs a declaration of the given type.\n//\n// It encodes https://golang.org/ref/spec#Type_declarations:\n//\n//\ttype foo struct { _ structs.HostLayout; bar uint32; }\n//\ttype bar int32\nfunc (gf *GoFormatter) writeTypeDecl(name string, typ Type) error {\n\tif name == \"\" {\n\t\treturn fmt.Errorf(\"need a name for type %s\", typ)\n\t}\n\n\ttyp = skipQualifiers(typ)\n\tfmt.Fprintf(&gf.w, \"type %s \", name)\n\tif err := gf.writeTypeLit(typ, 0); err != nil {\n\t\treturn err\n\t}\n\n\te, ok := typ.(*Enum)\n\tif !ok || len(e.Values) == 0 {\n\t\treturn nil\n\t}\n\n\tgf.w.WriteString(\"; const ( \")\n\tfor _, ev := range e.Values {\n\t\tid := gf.enumIdentifier(name, ev.Name)\n\t\tvar value any\n\t\tif e.Signed {\n\t\t\tvalue = int64(ev.Value)\n\t\t} else {\n\t\t\tvalue = ev.Value\n\t\t}\n\t\tfmt.Fprintf(&gf.w, \"%s %s = %d; \", id, name, value)\n\t}\n\tgf.w.WriteString(\")\")\n\n\treturn nil\n}\n\n// writeType outputs the name of a named type or a literal describing the type.\n//\n// It encodes https://golang.org/ref/spec#Types.\n//\n//\tfoo                  (if foo is a named type)\n//\tuint32\nfunc (gf *GoFormatter) writeType(typ Type, depth int) error {\n\ttyp = skipQualifiers(typ)\n\n\tname := gf.Names[typ]\n\tif name != \"\" {\n\t\tgf.w.WriteString(name)\n\t\treturn nil\n\t}\n\n\treturn gf.writeTypeLit(typ, depth)\n}\n\n// writeTypeLit outputs a literal describing the type.\n//\n// The function ignores named types.\n//\n// It encodes https://golang.org/ref/spec#TypeLit.\n//\n//\tstruct { _ structs.HostLayout; bar uint32; }\n//\tuint32\nfunc (gf *GoFormatter) writeTypeLit(typ Type, depth int) error {\n\tdepth++\n\tif depth > maxResolveDepth {\n\t\treturn errNestedTooDeep\n\t}\n\n\tvar err error\n\tswitch v := skipQualifiers(typ).(type) {\n\tcase *Int:\n\t\terr = gf.writeIntLit(v)\n\n\tcase *Enum:\n\t\tif !v.Signed {\n\t\t\tgf.w.WriteRune('u')\n\t\t}\n\t\tswitch v.Size {\n\t\tcase 1:\n\t\t\tgf.w.WriteString(\"int8\")\n\t\tcase 2:\n\t\t\tgf.w.WriteString(\"int16\")\n\t\tcase 4:\n\t\t\tgf.w.WriteString(\"int32\")\n\t\tcase 8:\n\t\t\tgf.w.WriteString(\"int64\")\n\t\tdefault:\n\t\t\terr = fmt.Errorf(\"invalid enum size %d\", v.Size)\n\t\t}\n\n\tcase *Typedef:\n\t\terr = gf.writeType(v.Type, depth)\n\n\tcase *Array:\n\t\tfmt.Fprintf(&gf.w, \"[%d]\", v.Nelems)\n\t\terr = gf.writeType(v.Type, depth)\n\n\tcase *Struct:\n\t\terr = gf.writeStructLit(v.Size, v.Members, depth)\n\n\tcase *Union:\n\t\t// Always choose the first member to represent the union in Go.\n\t\terr = gf.writeStructLit(v.Size, v.Members[:1], depth)\n\n\tcase *Datasec:\n\t\terr = gf.writeDatasecLit(v, depth)\n\n\tcase *Var:\n\t\terr = gf.writeTypeLit(v.Type, depth)\n\n\tdefault:\n\t\treturn fmt.Errorf(\"type %T: %w\", v, ErrNotSupported)\n\t}\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"%s: %w\", typ, err)\n\t}\n\n\treturn nil\n}\n\nfunc (gf *GoFormatter) writeIntLit(i *Int) error {\n\tbits := i.Size * 8\n\tswitch i.Encoding {\n\tcase Bool:\n\t\tif i.Size != 1 {\n\t\t\treturn fmt.Errorf(\"bool with size %d\", i.Size)\n\t\t}\n\t\tgf.w.WriteString(\"bool\")\n\tcase Char:\n\t\tif i.Size != 1 {\n\t\t\treturn fmt.Errorf(\"char with size %d\", i.Size)\n\t\t}\n\t\t// BTF doesn't have a way to specify the signedness of a char. Assume\n\t\t// we are dealing with unsigned, since this works nicely with []byte\n\t\t// in Go code.\n\t\tfallthrough\n\tcase Unsigned, Signed:\n\t\tstem := \"uint\"\n\t\tif i.Encoding == Signed {\n\t\t\tstem = \"int\"\n\t\t}\n\t\tif i.Size > 8 {\n\t\t\tfmt.Fprintf(&gf.w, \"[%d]byte /* %s%d */\", i.Size, stem, i.Size*8)\n\t\t} else {\n\t\t\tfmt.Fprintf(&gf.w, \"%s%d\", stem, bits)\n\t\t}\n\tdefault:\n\t\treturn fmt.Errorf(\"can't encode %s\", i.Encoding)\n\t}\n\treturn nil\n}\n\nfunc (gf *GoFormatter) writeStructLit(size uint32, members []Member, depth int) error {\n\tgf.w.WriteString(\"struct { _ structs.HostLayout; \")\n\n\tprevOffset := uint32(0)\n\tskippedBitfield := false\n\tfor i, m := range members {\n\t\tif m.BitfieldSize > 0 {\n\t\t\tskippedBitfield = true\n\t\t\tcontinue\n\t\t}\n\n\t\toffset := m.Offset.Bytes()\n\t\tif n := offset - prevOffset; skippedBitfield && n > 0 {\n\t\t\tfmt.Fprintf(&gf.w, \"_ [%d]byte /* unsupported bitfield */; \", n)\n\t\t} else {\n\t\t\tgf.writePadding(n)\n\t\t}\n\n\t\tfieldSize, err := Sizeof(m.Type)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"field %d: %w\", i, err)\n\t\t}\n\n\t\tprevOffset = offset + uint32(fieldSize)\n\t\tif prevOffset > size {\n\t\t\treturn fmt.Errorf(\"field %d of size %d exceeds type size %d\", i, fieldSize, size)\n\t\t}\n\n\t\tif err := gf.writeStructField(m, depth); err != nil {\n\t\t\treturn fmt.Errorf(\"field %d: %w\", i, err)\n\t\t}\n\t}\n\n\tgf.writePadding(size - prevOffset)\n\tgf.w.WriteString(\"}\")\n\treturn nil\n}\n\nfunc (gf *GoFormatter) writeStructField(m Member, depth int) error {\n\tif m.BitfieldSize > 0 {\n\t\treturn fmt.Errorf(\"bitfields are not supported\")\n\t}\n\tif m.Offset%8 != 0 {\n\t\treturn fmt.Errorf(\"unsupported offset %d\", m.Offset)\n\t}\n\n\tif m.Name == \"\" {\n\t\t// Special case a nested anonymous union like\n\t\t//     struct foo { union { int bar; int baz }; }\n\t\t// by replacing the whole union with its first member.\n\t\tunion, ok := m.Type.(*Union)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"anonymous fields are not supported\")\n\n\t\t}\n\n\t\tif len(union.Members) == 0 {\n\t\t\treturn errors.New(\"empty anonymous union\")\n\t\t}\n\n\t\tdepth++\n\t\tif depth > maxResolveDepth {\n\t\t\treturn errNestedTooDeep\n\t\t}\n\n\t\tm := union.Members[0]\n\t\tsize, err := Sizeof(m.Type)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := gf.writeStructField(m, depth); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tgf.writePadding(union.Size - uint32(size))\n\t\treturn nil\n\n\t}\n\n\tfmt.Fprintf(&gf.w, \"%s \", gf.identifier(m.Name))\n\n\tif err := gf.writeType(m.Type, depth); err != nil {\n\t\treturn err\n\t}\n\n\tgf.w.WriteString(\"; \")\n\treturn nil\n}\n\nfunc (gf *GoFormatter) writeDatasecLit(ds *Datasec, depth int) error {\n\tgf.w.WriteString(\"struct { _ structs.HostLayout; \")\n\n\tprevOffset := uint32(0)\n\tfor i, vsi := range ds.Vars {\n\t\tv, ok := vsi.Type.(*Var)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"can't format %s as part of data section\", vsi.Type)\n\t\t}\n\n\t\tif v.Linkage != GlobalVar {\n\t\t\t// Ignore static, extern, etc. for now.\n\t\t\tcontinue\n\t\t}\n\n\t\tif v.Name == \"\" {\n\t\t\treturn fmt.Errorf(\"variable %d: empty name\", i)\n\t\t}\n\n\t\tgf.writePadding(vsi.Offset - prevOffset)\n\t\tprevOffset = vsi.Offset + vsi.Size\n\n\t\tfmt.Fprintf(&gf.w, \"%s \", gf.identifier(v.Name))\n\n\t\tif err := gf.writeType(v.Type, depth); err != nil {\n\t\t\treturn fmt.Errorf(\"variable %d: %w\", i, err)\n\t\t}\n\n\t\tgf.w.WriteString(\"; \")\n\t}\n\n\tgf.writePadding(ds.Size - prevOffset)\n\tgf.w.WriteString(\"}\")\n\treturn nil\n}\n\nfunc (gf *GoFormatter) writePadding(bytes uint32) {\n\tif bytes > 0 {\n\t\tfmt.Fprintf(&gf.w, \"_ [%d]byte; \", bytes)\n\t}\n}\n\nfunc skipQualifiers(typ Type) Type {\n\tresult := typ\n\tfor depth := 0; depth <= maxResolveDepth; depth++ {\n\t\tswitch v := (result).(type) {\n\t\tcase qualifier:\n\t\t\tresult = v.qualify()\n\t\tdefault:\n\t\t\treturn result\n\t\t}\n\t}\n\treturn &cycle{typ}\n}\n"
  },
  {
    "path": "btf/format_test.go",
    "content": "package btf\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"go/format\"\n\t\"math\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestGoTypeDeclaration(t *testing.T) {\n\ttests := []struct {\n\t\ttyp    Type\n\t\toutput string\n\t}{\n\t\t{&Int{Size: 1}, \"type t uint8\"},\n\t\t{&Int{Size: 1, Encoding: Bool}, \"type t bool\"},\n\t\t{&Int{Size: 1, Encoding: Char}, \"type t uint8\"},\n\t\t{&Int{Size: 2, Encoding: Signed}, \"type t int16\"},\n\t\t{&Int{Size: 4, Encoding: Signed}, \"type t int32\"},\n\t\t{&Int{Size: 8}, \"type t uint64\"},\n\t\t{&Typedef{Name: \"frob\", Type: &Int{Size: 8}}, \"type t uint64\"},\n\t\t{&Int{Size: 16}, \"type t [16]byte /* uint128 */\"},\n\t\t{&Enum{Values: []EnumValue{{\"FOO\", 32}}, Size: 4}, \"type t uint32; const ( tFOO t = 32; )\"},\n\t\t{\n\t\t\t&Enum{\n\t\t\t\tValues: []EnumValue{\n\t\t\t\t\t{\"MINUS_ONE\", math.MaxUint64},\n\t\t\t\t\t{\"MINUS_TWO\", math.MaxUint64 - 1},\n\t\t\t\t},\n\t\t\t\tSize:   1,\n\t\t\t\tSigned: true,\n\t\t\t},\n\t\t\t\"type t int8; const ( tMINUS_ONE t = -1; tMINUS_TWO t = -2; )\",\n\t\t},\n\t\t{\n\t\t\t&Struct{\n\t\t\t\tName: \"enum literals\",\n\t\t\t\tSize: 2,\n\t\t\t\tMembers: []Member{\n\t\t\t\t\t{Name: \"enum\", Type: &Enum{Values: []EnumValue{{\"BAR\", 1}}, Size: 2}, Offset: 0},\n\t\t\t\t},\n\t\t\t},\n\t\t\t\"type t struct { _ structs.HostLayout; enum uint16; }\",\n\t\t},\n\t\t{&Array{Nelems: 2, Type: &Int{Size: 1}}, \"type t [2]uint8\"},\n\t\t{\n\t\t\t&Union{\n\t\t\t\tSize: 8,\n\t\t\t\tMembers: []Member{\n\t\t\t\t\t{Name: \"a\", Type: &Int{Size: 4}},\n\t\t\t\t\t{Name: \"b\", Type: &Int{Size: 8}},\n\t\t\t\t},\n\t\t\t},\n\t\t\t\"type t struct { _ structs.HostLayout; a uint32; _ [4]byte; }\",\n\t\t},\n\t\t{\n\t\t\t&Struct{\n\t\t\t\tName: \"field padding\",\n\t\t\t\tSize: 16,\n\t\t\t\tMembers: []Member{\n\t\t\t\t\t{Name: \"frob\", Type: &Int{Size: 4}, Offset: 0},\n\t\t\t\t\t{Name: \"foo\", Type: &Int{Size: 8}, Offset: 8 * 8},\n\t\t\t\t},\n\t\t\t},\n\t\t\t\"type t struct { _ structs.HostLayout; frob uint32; _ [4]byte; foo uint64; }\",\n\t\t},\n\t\t{\n\t\t\t&Struct{\n\t\t\t\tName: \"end padding\",\n\t\t\t\tSize: 16,\n\t\t\t\tMembers: []Member{\n\t\t\t\t\t{Name: \"foo\", Type: &Int{Size: 8}, Offset: 0},\n\t\t\t\t\t{Name: \"frob\", Type: &Int{Size: 4}, Offset: 8 * 8},\n\t\t\t\t},\n\t\t\t},\n\t\t\t\"type t struct { _ structs.HostLayout; foo uint64; frob uint32; _ [4]byte; }\",\n\t\t},\n\t\t{\n\t\t\t&Struct{\n\t\t\t\tName: \"bitfield\",\n\t\t\t\tSize: 8,\n\t\t\t\tMembers: []Member{\n\t\t\t\t\t{Name: \"foo\", Type: &Int{Size: 4}, Offset: 0, BitfieldSize: 1},\n\t\t\t\t\t{Name: \"frob\", Type: &Int{Size: 4}, Offset: 4 * 8},\n\t\t\t\t},\n\t\t\t},\n\t\t\t\"type t struct { _ structs.HostLayout; _ [4]byte /* unsupported bitfield */; frob uint32; }\",\n\t\t},\n\t\t{\n\t\t\t&Struct{\n\t\t\t\tName: \"nested\",\n\t\t\t\tSize: 8,\n\t\t\t\tMembers: []Member{\n\t\t\t\t\t{\n\t\t\t\t\t\tName: \"foo\",\n\t\t\t\t\t\tType: &Struct{\n\t\t\t\t\t\t\tSize: 4,\n\t\t\t\t\t\t\tMembers: []Member{\n\t\t\t\t\t\t\t\t{Name: \"bar\", Type: &Int{Size: 4}, Offset: 0},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{Name: \"frob\", Type: &Int{Size: 4}, Offset: 4 * 8},\n\t\t\t\t},\n\t\t\t},\n\t\t\t\"type t struct { _ structs.HostLayout; foo struct { _ structs.HostLayout; bar uint32; }; frob uint32; }\",\n\t\t},\n\t\t{\n\t\t\t&Struct{\n\t\t\t\tName: \"nested anon union\",\n\t\t\t\tSize: 8,\n\t\t\t\tMembers: []Member{\n\t\t\t\t\t{\n\t\t\t\t\t\tName: \"\",\n\t\t\t\t\t\tType: &Union{\n\t\t\t\t\t\t\tSize: 4,\n\t\t\t\t\t\t\tMembers: []Member{\n\t\t\t\t\t\t\t\t{Name: \"foo\", Type: &Int{Size: 4}, Offset: 0},\n\t\t\t\t\t\t\t\t{Name: \"bar\", Type: &Int{Size: 4}, Offset: 0},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t\"type t struct { _ structs.HostLayout; foo uint32; _ [4]byte; }\",\n\t\t},\n\t\t{\n\t\t\t&Datasec{\n\t\t\t\tSize: 16,\n\t\t\t\tVars: []VarSecinfo{\n\t\t\t\t\t{&Var{Name: \"s\", Type: &Int{Size: 2}, Linkage: StaticVar}, 0, 2},\n\t\t\t\t\t{&Var{Name: \"g\", Type: &Int{Size: 4}, Linkage: GlobalVar}, 4, 4},\n\t\t\t\t\t{&Var{Name: \"e\", Type: &Int{Size: 8}, Linkage: ExternVar}, 8, 8},\n\t\t\t\t},\n\t\t\t},\n\t\t\t\"type t struct { _ structs.HostLayout; _ [4]byte; g uint32; _ [8]byte; }\",\n\t\t},\n\t\t{&Var{Type: &Int{Size: 4}}, \"type t uint32\"},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(fmt.Sprint(test.typ), func(t *testing.T) {\n\t\t\thave := mustGoTypeDeclaration(t, test.typ, nil, nil)\n\t\t\tif have != test.output {\n\t\t\t\tt.Errorf(\"Unexpected output:\\n\\t-%s\\n\\t+%s\", test.output, have)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGoTypeDeclarationNamed(t *testing.T) {\n\te1 := &Enum{Name: \"e1\", Size: 4}\n\ts1 := &Struct{\n\t\tName: \"s1\",\n\t\tSize: 4,\n\t\tMembers: []Member{\n\t\t\t{Name: \"frob\", Type: e1},\n\t\t},\n\t}\n\ts2 := &Struct{\n\t\tName: \"s2\",\n\t\tSize: 4,\n\t\tMembers: []Member{\n\t\t\t{Name: \"frood\", Type: s1},\n\t\t},\n\t}\n\ttd := &Typedef{Name: \"td\", Type: e1}\n\tarr := &Array{Nelems: 1, Type: td}\n\n\ttests := []struct {\n\t\ttyp    Type\n\t\tnamed  []Type\n\t\toutput string\n\t}{\n\t\t{e1, []Type{e1}, \"type t uint32\"},\n\t\t{s1, []Type{e1, s1}, \"type t struct { _ structs.HostLayout; frob E1; }\"},\n\t\t{s2, []Type{e1}, \"type t struct { _ structs.HostLayout; frood struct { _ structs.HostLayout; frob E1; }; }\"},\n\t\t{s2, []Type{e1, s1}, \"type t struct { _ structs.HostLayout; frood S1; }\"},\n\t\t{td, nil, \"type t uint32\"},\n\t\t{td, []Type{td}, \"type t uint32\"},\n\t\t{arr, []Type{td}, \"type t [1]TD\"},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(fmt.Sprint(test.typ), func(t *testing.T) {\n\t\t\tnames := make(map[Type]string)\n\t\t\tfor _, t := range test.named {\n\t\t\t\tnames[t] = strings.ToUpper(t.TypeName())\n\t\t\t}\n\n\t\t\thave := mustGoTypeDeclaration(t, test.typ, names, nil)\n\t\t\tif have != test.output {\n\t\t\t\tt.Errorf(\"Unexpected output:\\n\\t-%s\\n\\t+%s\", test.output, have)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGoTypeDeclarationQualifiers(t *testing.T) {\n\ti := &Int{Size: 4}\n\twant := mustGoTypeDeclaration(t, i, nil, nil)\n\n\ttests := []struct {\n\t\ttyp Type\n\t}{\n\t\t{&Volatile{Type: i}},\n\t\t{&Const{Type: i}},\n\t\t{&Restrict{Type: i}},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(fmt.Sprint(test.typ), func(t *testing.T) {\n\t\t\thave := mustGoTypeDeclaration(t, test.typ, nil, nil)\n\t\t\tif have != want {\n\t\t\t\tt.Errorf(\"Unexpected output:\\n\\t-%s\\n\\t+%s\", want, have)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGoTypeDeclarationCycle(t *testing.T) {\n\ts := &Struct{Name: \"cycle\"}\n\ts.Members = []Member{{Name: \"f\", Type: s}}\n\n\tvar gf GoFormatter\n\t_, err := gf.TypeDeclaration(\"t\", s)\n\tif !errors.Is(err, errNestedTooDeep) {\n\t\tt.Fatal(\"Expected errNestedTooDeep, got\", err)\n\t}\n}\n\nfunc TestRejectBogusTypes(t *testing.T) {\n\ttests := []struct {\n\t\ttyp Type\n\t}{\n\t\t{&Struct{\n\t\t\tSize: 1,\n\t\t\tMembers: []Member{\n\t\t\t\t{Name: \"foo\", Type: &Int{Size: 2}, Offset: 0},\n\t\t\t},\n\t\t}},\n\t\t{&Int{Size: 2, Encoding: Bool}},\n\t\t{&Int{Size: 1, Encoding: Char | Signed}},\n\t\t{&Int{Size: 2, Encoding: Char}},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(fmt.Sprint(test.typ), func(t *testing.T) {\n\t\t\tvar gf GoFormatter\n\n\t\t\t_, err := gf.TypeDeclaration(\"t\", test.typ)\n\t\t\tif err == nil {\n\t\t\t\tt.Fatal(\"TypeDeclaration does not reject bogus type\")\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc mustGoTypeDeclaration(tb testing.TB, typ Type, names map[Type]string, id func(string) string) string {\n\ttb.Helper()\n\n\tgf := GoFormatter{\n\t\tNames:      names,\n\t\tIdentifier: id,\n\t}\n\n\thave, err := gf.TypeDeclaration(\"t\", typ)\n\tif err != nil {\n\t\ttb.Fatal(err)\n\t}\n\n\t_, err = format.Source([]byte(have))\n\tif err != nil {\n\t\ttb.Fatalf(\"Output can't be formatted: %s\\n%s\", err, have)\n\t}\n\n\treturn have\n}\n"
  },
  {
    "path": "btf/fuzz_test.go",
    "content": "package btf\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"io\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/internal\"\n)\n\nfunc FuzzSpec(f *testing.F) {\n\tf.Add(mustBTFHeader(f))\n\n\tf.Fuzz(func(t *testing.T, data []byte) {\n\t\tif len(data) < binary.Size(btfHeader{}) {\n\t\t\tt.Skip(\"data is too short\")\n\t\t}\n\n\t\tspec, err := loadRawSpec(data, nil)\n\t\tif err != nil {\n\t\t\tif spec != nil {\n\t\t\t\tt.Fatal(\"spec is not nil\")\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\tif spec == nil {\n\t\t\tt.Fatal(\"spec is nil\")\n\t\t}\n\n\t\tfor typ, err := range spec.All() {\n\t\t\tif err == nil {\n\t\t\t\tfmt.Fprintf(io.Discard, \"%+10v\", typ)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc FuzzExtInfo(f *testing.F) {\n\tf.Add(mustBTFHeader(f), []byte(\"\\x00foo\\x00barfoo\\x00\"))\n\n\tf.Fuzz(func(t *testing.T, data, strings []byte) {\n\t\tif len(data) < binary.Size(btfExtHeader{}) {\n\t\t\tt.Skip(\"data is too short\")\n\t\t}\n\n\t\ttable, err := readStringTable(bytes.NewReader(strings), nil)\n\t\tif err != nil {\n\t\t\tt.Skip(\"invalid string table\")\n\t\t}\n\n\t\temptySpec := specFromTypes(t, nil)\n\t\temptySpec.strings = table\n\n\t\tinfo, err := loadExtInfos(bytes.NewReader(data), internal.NativeEndian, emptySpec)\n\t\tif err != nil {\n\t\t\tif info != nil {\n\t\t\t\tt.Fatal(\"info is not nil\")\n\t\t\t}\n\t\t} else if info == nil {\n\t\t\tt.Fatal(\"info is nil\")\n\t\t}\n\t})\n}\n\nfunc mustBTFHeader(f *testing.F) []byte {\n\tbuf, err := binary.Append(nil, internal.NativeEndian, &btfHeader{\n\t\tMagic:   btfMagic,\n\t\tVersion: 1,\n\t\tHdrLen:  uint32(binary.Size(btfHeader{})),\n\t})\n\tqt.Assert(f, qt.IsNil(err))\n\treturn buf\n}\n"
  },
  {
    "path": "btf/handle.go",
    "content": "package btf\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"os\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/platform\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\n// Handle is a reference to BTF loaded into the kernel.\ntype Handle struct {\n\tfd *sys.FD\n\n\t// Size of the raw BTF in bytes.\n\tsize uint32\n\n\tneedsKernelBase bool\n}\n\n// NewHandle loads the contents of a [Builder] into the kernel.\n//\n// Returns an error wrapping ErrNotSupported if the kernel doesn't support BTF.\nfunc NewHandle(b *Builder) (*Handle, error) {\n\tsmall := getByteSlice()\n\tdefer putByteSlice(small)\n\n\tbuf, err := b.Marshal(*small, KernelMarshalOptions())\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"marshal BTF: %w\", err)\n\t}\n\n\treturn NewHandleFromRawBTF(buf)\n}\n\n// NewHandleFromRawBTF loads raw BTF into the kernel.\n//\n// Returns an error wrapping ErrNotSupported if the kernel doesn't support BTF.\nfunc NewHandleFromRawBTF(btf []byte) (*Handle, error) {\n\tconst minLogSize = 64 * 1024\n\n\tif platform.IsWindows {\n\t\treturn nil, fmt.Errorf(\"btf: handle: %w\", internal.ErrNotSupportedOnOS)\n\t}\n\n\tif uint64(len(btf)) > math.MaxUint32 {\n\t\treturn nil, errors.New(\"BTF exceeds the maximum size\")\n\t}\n\n\tattr := &sys.BtfLoadAttr{\n\t\tBtf:     sys.SlicePointer(btf),\n\t\tBtfSize: uint32(len(btf)),\n\t}\n\n\tvar (\n\t\tlogBuf []byte\n\t\terr    error\n\t)\n\tfor {\n\t\tvar fd *sys.FD\n\t\tfd, err = sys.BtfLoad(attr)\n\t\tif err == nil {\n\t\t\treturn &Handle{fd, attr.BtfSize, false}, nil\n\t\t}\n\n\t\tif attr.BtfLogTrueSize != 0 && attr.BtfLogSize >= attr.BtfLogTrueSize {\n\t\t\t// The log buffer already has the correct size.\n\t\t\tbreak\n\t\t}\n\n\t\tif attr.BtfLogSize != 0 && !errors.Is(err, unix.ENOSPC) {\n\t\t\t// Up until at least kernel 6.0, the BTF verifier does not return ENOSPC\n\t\t\t// if there are other verification errors. ENOSPC is only returned when\n\t\t\t// the BTF blob is correct, a log was requested, and the provided buffer\n\t\t\t// is too small. We're therefore not sure whether we got the full\n\t\t\t// log or not.\n\t\t\tbreak\n\t\t}\n\n\t\t// Make an educated guess how large the buffer should be. Start\n\t\t// at a reasonable minimum and then double the size.\n\t\tlogSize := uint32(max(len(logBuf)*2, minLogSize))\n\t\tif int(logSize) < len(logBuf) {\n\t\t\treturn nil, errors.New(\"overflow while probing log buffer size\")\n\t\t}\n\n\t\tif attr.BtfLogTrueSize != 0 {\n\t\t\t// The kernel has given us a hint how large the log buffer has to be.\n\t\t\tlogSize = attr.BtfLogTrueSize\n\t\t}\n\n\t\tlogBuf = make([]byte, logSize)\n\t\tattr.BtfLogSize = logSize\n\t\tattr.BtfLogBuf = sys.SlicePointer(logBuf)\n\t\tattr.BtfLogLevel = 1\n\t}\n\n\tif err := haveBTF(); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn nil, internal.ErrorWithLog(\"load btf\", err, logBuf)\n}\n\n// NewHandleFromID returns the BTF handle for a given id.\n//\n// Prefer calling [ebpf.Program.Handle] or [ebpf.Map.Handle] if possible.\n//\n// Returns ErrNotExist, if there is no BTF with the given id.\n//\n// Requires CAP_SYS_ADMIN.\nfunc NewHandleFromID(id ID) (*Handle, error) {\n\tif platform.IsWindows {\n\t\treturn nil, fmt.Errorf(\"btf: handle: %w\", internal.ErrNotSupportedOnOS)\n\t}\n\n\tfd, err := sys.BtfGetFdById(&sys.BtfGetFdByIdAttr{\n\t\tId: uint32(id),\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"get FD for ID %d: %w\", id, err)\n\t}\n\n\tinfo, err := newHandleInfoFromFD(fd)\n\tif err != nil {\n\t\t_ = fd.Close()\n\t\treturn nil, err\n\t}\n\n\treturn &Handle{fd, info.size, info.IsModule()}, nil\n}\n\n// Spec parses the kernel BTF into Go types.\n//\n// base must contain type information for vmlinux if the handle is for\n// a kernel module. It may be nil otherwise.\nfunc (h *Handle) Spec(base *Spec) (*Spec, error) {\n\tvar btfInfo sys.BtfInfo\n\tbtfBuffer := make([]byte, h.size)\n\tbtfInfo.Btf = sys.SlicePointer(btfBuffer)\n\tbtfInfo.BtfSize = uint32(len(btfBuffer))\n\n\tif err := sys.ObjInfo(h.fd, &btfInfo); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif h.needsKernelBase && base == nil {\n\t\treturn nil, fmt.Errorf(\"missing base types\")\n\t}\n\n\treturn loadRawSpec(btfBuffer, base)\n}\n\n// Close destroys the handle.\n//\n// Subsequent calls to FD will return an invalid value.\nfunc (h *Handle) Close() error {\n\tif h == nil {\n\t\treturn nil\n\t}\n\n\treturn h.fd.Close()\n}\n\n// FD returns the file descriptor for the handle.\nfunc (h *Handle) FD() int {\n\treturn h.fd.Int()\n}\n\n// Info returns metadata about the handle.\nfunc (h *Handle) Info() (*HandleInfo, error) {\n\treturn newHandleInfoFromFD(h.fd)\n}\n\n// HandleInfo describes a Handle.\ntype HandleInfo struct {\n\t// ID of this handle in the kernel. The ID is only valid as long as the\n\t// associated handle is kept alive.\n\tID ID\n\n\t// Name is an identifying name for the BTF, currently only used by the\n\t// kernel.\n\tName string\n\n\t// IsKernel is true if the BTF originated with the kernel and not\n\t// userspace.\n\tIsKernel bool\n\n\t// Size of the raw BTF in bytes.\n\tsize uint32\n}\n\nfunc newHandleInfoFromFD(fd *sys.FD) (*HandleInfo, error) {\n\t// We invoke the syscall once with a empty BTF and name buffers to get size\n\t// information to allocate buffers. Then we invoke it a second time with\n\t// buffers to receive the data.\n\tvar btfInfo sys.BtfInfo\n\tif err := sys.ObjInfo(fd, &btfInfo); err != nil {\n\t\treturn nil, fmt.Errorf(\"get BTF info for fd %s: %w\", fd, err)\n\t}\n\n\tif btfInfo.NameLen > 0 {\n\t\t// NameLen doesn't account for the terminating NUL.\n\t\tbtfInfo.NameLen++\n\t}\n\n\t// Don't pull raw BTF by default, since it may be quite large.\n\tbtfSize := btfInfo.BtfSize\n\tbtfInfo.BtfSize = 0\n\n\tnameBuffer := make([]byte, btfInfo.NameLen)\n\tbtfInfo.Name = sys.SlicePointer(nameBuffer)\n\tbtfInfo.NameLen = uint32(len(nameBuffer))\n\tif err := sys.ObjInfo(fd, &btfInfo); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &HandleInfo{\n\t\tID:       ID(btfInfo.Id),\n\t\tName:     unix.ByteSliceToString(nameBuffer),\n\t\tIsKernel: btfInfo.KernelBtf != 0,\n\t\tsize:     btfSize,\n\t}, nil\n}\n\n// IsVmlinux returns true if the BTF is for the kernel itself.\nfunc (i *HandleInfo) IsVmlinux() bool {\n\treturn i.IsKernel && i.Name == \"vmlinux\"\n}\n\n// IsModule returns true if the BTF is for a kernel module.\nfunc (i *HandleInfo) IsModule() bool {\n\treturn i.IsKernel && i.Name != \"vmlinux\"\n}\n\n// HandleIterator allows enumerating BTF blobs loaded into the kernel.\ntype HandleIterator struct {\n\t// The ID of the current handle. Only valid after a call to Next.\n\tID ID\n\t// The current Handle. Only valid until a call to Next.\n\t// See Take if you want to retain the handle.\n\tHandle *Handle\n\terr    error\n}\n\n// Next retrieves a handle for the next BTF object.\n//\n// Returns true if another BTF object was found. Call [HandleIterator.Err] after\n// the function returns false.\nfunc (it *HandleIterator) Next() bool {\n\tif platform.IsWindows {\n\t\tit.err = fmt.Errorf(\"btf: %w\", internal.ErrNotSupportedOnOS)\n\t\treturn false\n\t}\n\n\tid := it.ID\n\tfor {\n\t\tattr := &sys.BtfGetNextIdAttr{Id: id}\n\t\terr := sys.BtfGetNextId(attr)\n\t\tif errors.Is(err, os.ErrNotExist) {\n\t\t\t// There are no more BTF objects.\n\t\t\tbreak\n\t\t} else if err != nil {\n\t\t\tit.err = fmt.Errorf(\"get next BTF ID: %w\", err)\n\t\t\tbreak\n\t\t}\n\n\t\tid = attr.NextId\n\t\thandle, err := NewHandleFromID(id)\n\t\tif errors.Is(err, os.ErrNotExist) {\n\t\t\t// Try again with the next ID.\n\t\t\tcontinue\n\t\t} else if err != nil {\n\t\t\tit.err = fmt.Errorf(\"retrieve handle for ID %d: %w\", id, err)\n\t\t\tbreak\n\t\t}\n\n\t\tit.Handle.Close()\n\t\tit.ID, it.Handle = id, handle\n\t\treturn true\n\t}\n\n\t// No more handles or we encountered an error.\n\tit.Handle.Close()\n\tit.Handle = nil\n\treturn false\n}\n\n// Take the ownership of the current handle.\n//\n// It's the callers responsibility to close the handle.\nfunc (it *HandleIterator) Take() *Handle {\n\thandle := it.Handle\n\tit.Handle = nil\n\treturn handle\n}\n\n// Err returns an error if iteration failed for some reason.\nfunc (it *HandleIterator) Err() error {\n\treturn it.err\n}\n\n// FindHandle returns the first handle for which predicate returns true.\n//\n// Requires CAP_SYS_ADMIN.\n//\n// Returns an error wrapping ErrNotFound if predicate never returns true or if\n// there is no BTF loaded into the kernel.\nfunc FindHandle(predicate func(info *HandleInfo) bool) (*Handle, error) {\n\tit := new(HandleIterator)\n\tdefer it.Handle.Close()\n\n\tfor it.Next() {\n\t\tinfo, err := it.Handle.Info()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"info for ID %d: %w\", it.ID, err)\n\t\t}\n\n\t\tif predicate(info) {\n\t\t\treturn it.Take(), nil\n\t\t}\n\t}\n\tif err := it.Err(); err != nil {\n\t\treturn nil, fmt.Errorf(\"iterate handles: %w\", err)\n\t}\n\n\treturn nil, fmt.Errorf(\"find handle: %w\", ErrNotFound)\n}\n"
  },
  {
    "path": "btf/handle_test.go",
    "content": "package btf_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc TestHandleIterator(t *testing.T) {\n\t// There is no guarantee that there is a BTF ID allocated, but loading a module\n\t// triggers loading vmlinux.\n\t// See https://github.com/torvalds/linux/commit/5329722057d41aebc31e391907a501feaa42f7d9\n\ttestutils.SkipOnOldKernel(t, \"5.11\", \"vmlinux BTF ID\")\n\n\tit := new(btf.HandleIterator)\n\tdefer it.Handle.Close()\n\n\tif !it.Next() {\n\t\ttestutils.SkipIfNotSupportedOnOS(t, it.Err())\n\t\tt.Fatalf(\"No BTF loaded\")\n\t}\n\tif it.Handle == nil {\n\t\tt.Fatal(\"Next doesn't assign handle\")\n\t}\n\tprev := it.ID\n\tfor it.Next() {\n\t\t// Iterate all loaded BTF.\n\t\tif it.Handle == nil {\n\t\t\tt.Fatal(\"Next doesn't assign handle\")\n\t\t}\n\t\tif it.ID == prev {\n\t\t\tt.Fatal(\"Iterator doesn't advance ID\")\n\t\t}\n\t\tprev = it.ID\n\t}\n\tif err := it.Err(); err != nil {\n\t\tt.Fatal(\"Iteration returned an error:\", err)\n\t}\n\n\tif it.Handle != nil {\n\t\tt.Fatal(\"Next doesn't clean up handle on last iteration\")\n\t}\n\tif prev != it.ID {\n\t\tt.Fatal(\"Next changes ID on last iteration\")\n\t}\n}\n\nfunc TestParseModuleSplitSpec(t *testing.T) {\n\t// See TestNewHandleFromID for reasoning.\n\ttestutils.SkipOnOldKernel(t, \"5.11\", \"vmlinux BTF ID\")\n\n\tmodule, err := btf.FindHandle(func(info *btf.HandleInfo) bool {\n\t\tif info.IsModule() {\n\t\t\tt.Log(\"Using module\", info.Name)\n\t\t\treturn true\n\t\t}\n\t\treturn false\n\t})\n\ttestutils.SkipIfNotSupportedOnOS(t, err)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer module.Close()\n\n\tvmlinux, err := btf.FindHandle(func(info *btf.HandleInfo) bool {\n\t\treturn info.IsVmlinux()\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer vmlinux.Close()\n\n\tbase, err := vmlinux.Spec(nil)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t_, err = module.Spec(base)\n\tif err != nil {\n\t\tt.Fatal(\"Parse module BTF:\", err)\n\t}\n}\n\nfunc ExampleHandleIterator() {\n\tit := new(btf.HandleIterator)\n\tdefer it.Handle.Close()\n\n\tfor it.Next() {\n\t\tinfo, err := it.Handle.Info()\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\tfmt.Printf(\"Found handle with ID %d and name %s\\n\", it.ID, info.Name)\n\t}\n\tif err := it.Err(); err != nil {\n\t\tpanic(err)\n\t}\n}\n"
  },
  {
    "path": "btf/kernel.go",
    "content": "package btf\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"slices\"\n\t\"sort\"\n\t\"sync\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/linux\"\n\t\"github.com/cilium/ebpf/internal/platform\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\n// globalCache amortises decoding BTF across all users of the library.\nvar globalCache = struct {\n\tsync.RWMutex\n\tkernel  *Spec\n\tmodules map[string]*Spec\n}{\n\tmodules: make(map[string]*Spec),\n}\n\n// FlushKernelSpec removes any cached kernel type information.\nfunc FlushKernelSpec() {\n\tglobalCache.Lock()\n\tdefer globalCache.Unlock()\n\n\tglobalCache.kernel = nil\n\tglobalCache.modules = make(map[string]*Spec)\n}\n\n// LoadKernelSpec returns the current kernel's BTF information.\n//\n// Defaults to /sys/kernel/btf/vmlinux and falls back to scanning the file system\n// for vmlinux ELFs. Returns an error wrapping ErrNotSupported if BTF is not enabled.\n//\n// Consider using [Cache] instead.\nfunc LoadKernelSpec() (*Spec, error) {\n\tspec, err := loadCachedKernelSpec()\n\treturn spec.Copy(), err\n}\n\n// load (and cache) the kernel spec.\n//\n// Does not copy Spec.\nfunc loadCachedKernelSpec() (*Spec, error) {\n\tglobalCache.RLock()\n\tspec := globalCache.kernel\n\tglobalCache.RUnlock()\n\n\tif spec != nil {\n\t\treturn spec, nil\n\t}\n\n\tglobalCache.Lock()\n\tdefer globalCache.Unlock()\n\n\t// check again, to prevent race between multiple callers\n\tif globalCache.kernel != nil {\n\t\treturn globalCache.kernel, nil\n\t}\n\n\tspec, err := loadKernelSpec()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tglobalCache.kernel = spec\n\treturn spec, nil\n}\n\n// LoadKernelModuleSpec returns the BTF information for the named kernel module.\n//\n// Using [Cache.Module] is faster when loading BTF for more than one module.\n//\n// Defaults to /sys/kernel/btf/<module>.\n// Returns an error wrapping ErrNotSupported if BTF is not enabled.\n// Returns an error wrapping fs.ErrNotExist if BTF for the specific module doesn't exist.\nfunc LoadKernelModuleSpec(module string) (*Spec, error) {\n\tspec, err := loadCachedKernelModuleSpec(module)\n\treturn spec.Copy(), err\n}\n\n// load (and cache) a module spec.\n//\n// Does not copy Spec.\nfunc loadCachedKernelModuleSpec(module string) (*Spec, error) {\n\tglobalCache.RLock()\n\tspec := globalCache.modules[module]\n\tglobalCache.RUnlock()\n\n\tif spec != nil {\n\t\treturn spec, nil\n\t}\n\n\tbase, err := loadCachedKernelSpec()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// NB: This only allows a single module to be parsed at a time. Not sure\n\t// it makes a difference.\n\tglobalCache.Lock()\n\tdefer globalCache.Unlock()\n\n\t// check again, to prevent race between multiple callers\n\tif spec := globalCache.modules[module]; spec != nil {\n\t\treturn spec, nil\n\t}\n\n\tspec, err = loadKernelModuleSpec(module, base)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tglobalCache.modules[module] = spec\n\treturn spec, nil\n}\n\nfunc loadKernelSpec() (*Spec, error) {\n\tif platform.IsWindows {\n\t\treturn nil, internal.ErrNotSupportedOnOS\n\t}\n\n\tfh, err := os.Open(\"/sys/kernel/btf/vmlinux\")\n\tif err == nil {\n\t\tdefer fh.Close()\n\n\t\tinfo, err := fh.Stat()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"stat vmlinux: %w\", err)\n\t\t}\n\n\t\t// NB: It's not safe to mmap arbitrary files because mmap(2) doesn't\n\t\t// guarantee that changes made after mmap are not visible in the mapping.\n\t\t//\n\t\t// This is not a problem for vmlinux, since it is always a read-only file.\n\t\traw, err := unix.Mmap(int(fh.Fd()), 0, int(info.Size()), unix.PROT_READ, unix.MAP_PRIVATE)\n\t\tif err != nil {\n\t\t\treturn LoadSplitSpecFromReader(fh, nil)\n\t\t}\n\n\t\tspec, err := loadRawSpec(raw, nil)\n\t\tif err != nil {\n\t\t\t_ = unix.Munmap(raw)\n\t\t\treturn nil, fmt.Errorf(\"load vmlinux: %w\", err)\n\t\t}\n\n\t\truntime.AddCleanup(spec.decoder.sharedBuf, func(b []byte) {\n\t\t\t_ = unix.Munmap(b)\n\t\t}, raw)\n\n\t\treturn spec, nil\n\t}\n\n\tfile, err := findVMLinux()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer file.Close()\n\n\tspec, err := LoadSpecFromReader(file)\n\treturn spec, err\n}\n\nfunc loadKernelModuleSpec(module string, base *Spec) (*Spec, error) {\n\tif platform.IsWindows {\n\t\treturn nil, internal.ErrNotSupportedOnOS\n\t}\n\n\tdir, file := filepath.Split(module)\n\tif dir != \"\" || filepath.Ext(file) != \"\" {\n\t\treturn nil, fmt.Errorf(\"invalid module name %q\", module)\n\t}\n\n\tfh, err := os.Open(filepath.Join(\"/sys/kernel/btf\", module))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer fh.Close()\n\n\treturn LoadSplitSpecFromReader(fh, base)\n}\n\n// findVMLinux scans multiple well-known paths for vmlinux kernel images.\nfunc findVMLinux() (*os.File, error) {\n\tif platform.IsWindows {\n\t\treturn nil, fmt.Errorf(\"find vmlinux: %w\", internal.ErrNotSupportedOnOS)\n\t}\n\n\trelease, err := linux.KernelRelease()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// use same list of locations as libbpf\n\t// https://github.com/libbpf/libbpf/blob/9a3a42608dbe3731256a5682a125ac1e23bced8f/src/btf.c#L3114-L3122\n\tlocations := []string{\n\t\t\"/boot/vmlinux-%s\",\n\t\t\"/lib/modules/%s/vmlinux-%[1]s\",\n\t\t\"/lib/modules/%s/build/vmlinux\",\n\t\t\"/usr/lib/modules/%s/kernel/vmlinux\",\n\t\t\"/usr/lib/debug/boot/vmlinux-%s\",\n\t\t\"/usr/lib/debug/boot/vmlinux-%s.debug\",\n\t\t\"/usr/lib/debug/lib/modules/%s/vmlinux\",\n\t}\n\n\tfor _, loc := range locations {\n\t\tfile, err := os.Open(fmt.Sprintf(loc, release))\n\t\tif errors.Is(err, os.ErrNotExist) {\n\t\t\tcontinue\n\t\t}\n\t\treturn file, err\n\t}\n\n\treturn nil, fmt.Errorf(\"no BTF found for kernel version %s: %w\", release, internal.ErrNotSupported)\n}\n\n// Cache allows to amortise the cost of decoding BTF across multiple call-sites.\n//\n// It is not safe for concurrent use.\ntype Cache struct {\n\tkernelTypes   *Spec\n\tmoduleTypes   map[string]*Spec\n\tloadedModules []string\n}\n\n// NewCache creates a new Cache.\n//\n// Opportunistically reuses a global cache if possible.\nfunc NewCache() *Cache {\n\tglobalCache.RLock()\n\tdefer globalCache.RUnlock()\n\n\t// This copy is either a no-op or very cheap, since the spec won't contain\n\t// any inflated types.\n\tkernel := globalCache.kernel.Copy()\n\tif kernel == nil {\n\t\treturn &Cache{}\n\t}\n\n\tmodules := make(map[string]*Spec, len(globalCache.modules))\n\tfor name, spec := range globalCache.modules {\n\t\tdecoder, _ := rebaseDecoder(spec.decoder, kernel.decoder)\n\t\t// NB: Kernel module BTF can't contain ELF fixups because it is always\n\t\t// read from sysfs.\n\t\tmodules[name] = &Spec{decoder: decoder}\n\t}\n\n\tif len(modules) == 0 {\n\t\treturn &Cache{kernel, nil, nil}\n\t}\n\n\treturn &Cache{kernel, modules, nil}\n}\n\n// Kernel is equivalent to [LoadKernelSpec], except that repeated calls do\n// not copy the Spec.\nfunc (c *Cache) Kernel() (*Spec, error) {\n\tif c.kernelTypes != nil {\n\t\treturn c.kernelTypes, nil\n\t}\n\n\tvar err error\n\tc.kernelTypes, err = LoadKernelSpec()\n\treturn c.kernelTypes, err\n}\n\n// Module is equivalent to [LoadKernelModuleSpec], except that repeated calls do\n// not copy the spec.\n//\n// All modules also share the return value of [Kernel] as their base.\nfunc (c *Cache) Module(name string) (*Spec, error) {\n\tif spec := c.moduleTypes[name]; spec != nil {\n\t\treturn spec, nil\n\t}\n\n\tif c.moduleTypes == nil {\n\t\tc.moduleTypes = make(map[string]*Spec)\n\t}\n\n\tbase, err := c.Kernel()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tspec, err := loadCachedKernelModuleSpec(name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Important: base is shared between modules. This allows inflating common\n\t// types only once.\n\tdecoder, err := rebaseDecoder(spec.decoder, base.decoder)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tspec = &Spec{decoder: decoder}\n\tc.moduleTypes[name] = spec\n\treturn spec, err\n}\n\n// Modules returns a sorted list of all loaded modules.\nfunc (c *Cache) Modules() ([]string, error) {\n\tif c.loadedModules != nil {\n\t\treturn c.loadedModules, nil\n\t}\n\n\tbtfDir, err := os.Open(\"/sys/kernel/btf\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer btfDir.Close()\n\n\tentries, err := btfDir.Readdirnames(-1)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tentries = slices.DeleteFunc(entries, func(s string) bool {\n\t\treturn s == \"vmlinux\"\n\t})\n\n\tsort.Strings(entries)\n\tc.loadedModules = entries\n\treturn entries, nil\n}\n"
  },
  {
    "path": "btf/kernel_test.go",
    "content": "package btf\n\nimport (\n\t\"os\"\n\t\"runtime\"\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf/internal/testutils\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestLoadKernelSpec(t *testing.T) {\n\tif _, err := os.Stat(\"/sys/kernel/btf/vmlinux\"); os.IsNotExist(err) {\n\t\tt.Skip(\"/sys/kernel/btf/vmlinux not present\")\n\t}\n\n\tspec, err := LoadKernelSpec()\n\tif err != nil {\n\t\tt.Fatal(\"Can't load kernel spec:\", err)\n\t}\n\n\tif !testutils.IsVersionLessThan(t, \"linux:6.16\") {\n\t\tmaps, err := os.ReadFile(\"/proc/self/maps\")\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.StringContains(string(maps), \" /sys/kernel/btf/vmlinux\\n\"))\n\t}\n\n\t// Prevent finalizer from unmapping vmlinux.\n\truntime.KeepAlive(spec)\n}\n\nfunc TestLoadKernelModuleSpec(t *testing.T) {\n\tif _, err := os.Stat(\"/sys/kernel/btf/bpf_testmod\"); os.IsNotExist(err) {\n\t\tt.Skip(\"/sys/kernel/btf/bpf_testmod not present\")\n\t}\n\n\t_, err := LoadKernelModuleSpec(\"bpf_testmod\")\n\tqt.Assert(t, qt.IsNil(err))\n}\n\nfunc TestCache(t *testing.T) {\n\tFlushKernelSpec()\n\tc := NewCache()\n\n\tqt.Assert(t, qt.IsNil(c.kernelTypes))\n\tqt.Assert(t, qt.HasLen(c.moduleTypes, 0))\n\tqt.Assert(t, qt.IsNil(c.loadedModules))\n\n\t// Test that Kernel() creates only one copy\n\tspec1, err := c.Kernel()\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.IsNotNil(spec1))\n\n\tspec2, err := c.Kernel()\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.IsNotNil(spec2))\n\n\tqt.Assert(t, qt.Equals(spec1, spec2))\n\n\t// Test that Module() creates only one copy\n\tmod1, err := c.Module(\"bpf_testmod\")\n\tif !os.IsNotExist(err) {\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.IsNotNil(mod1))\n\n\t\tmod2, err := c.Module(\"bpf_testmod\")\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.IsNotNil(mod2))\n\n\t\tqt.Assert(t, qt.Equals(mod1, mod2))\n\t}\n\n\t// Pre-populate global cache\n\tvmlinux, err := LoadKernelSpec()\n\tqt.Assert(t, qt.IsNil(err))\n\n\ttestmod, err := LoadKernelModuleSpec(\"bpf_testmod\")\n\tif !os.IsNotExist(err) {\n\t\tqt.Assert(t, qt.IsNil(err))\n\t}\n\n\t// Test that NewCache populates from global cache\n\tc = NewCache()\n\tqt.Assert(t, qt.IsNotNil(c.kernelTypes))\n\tqt.Assert(t, qt.Not(qt.Equals(c.kernelTypes, vmlinux)))\n\tif testmod != nil {\n\t\tqt.Assert(t, qt.IsNotNil(c.moduleTypes[\"bpf_testmod\"]))\n\t\tqt.Assert(t, qt.Not(qt.Equals(c.moduleTypes[\"bpf_testmod\"], testmod)))\n\t}\n\n\t// Test that Modules only reads modules once.\n\t_, err = c.Modules()\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.IsNotNil(c.loadedModules))\n}\n"
  },
  {
    "path": "btf/marshal.go",
    "content": "package btf\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"maps\"\n\t\"math\"\n\t\"slices\"\n\t\"sync\"\n\t\"unsafe\"\n\n\t\"github.com/cilium/ebpf/internal\"\n)\n\ntype MarshalOptions struct {\n\t// Target byte order. Defaults to the system's native endianness.\n\tOrder binary.ByteOrder\n\t// Remove function linkage information for compatibility with <5.6 kernels.\n\tStripFuncLinkage bool\n\t// Replace decl tags with a placeholder for compatibility with <5.16 kernels.\n\tReplaceDeclTags bool\n\t// Replace TypeTags with a placeholder for compatibility with <5.17 kernels.\n\tReplaceTypeTags bool\n\t// Replace Enum64 with a placeholder for compatibility with <6.0 kernels.\n\tReplaceEnum64 bool\n\t// Prevent the \"No type found\" error when loading BTF without any types.\n\tPreventNoTypeFound bool\n}\n\n// KernelMarshalOptions will generate BTF suitable for the current kernel.\nfunc KernelMarshalOptions() *MarshalOptions {\n\treturn &MarshalOptions{\n\t\tOrder:              internal.NativeEndian,\n\t\tStripFuncLinkage:   haveFuncLinkage() != nil,\n\t\tReplaceDeclTags:    haveDeclTags() != nil,\n\t\tReplaceTypeTags:    haveTypeTags() != nil,\n\t\tReplaceEnum64:      haveEnum64() != nil,\n\t\tPreventNoTypeFound: true, // All current kernels require this.\n\t}\n}\n\n// encoder turns Types into raw BTF.\ntype encoder struct {\n\tMarshalOptions\n\n\tpending internal.Deque[Type]\n\tstrings *stringTableBuilder\n\tids     map[Type]TypeID\n\tvisited map[Type]struct{}\n\tlastID  TypeID\n}\n\nvar bufferPool = sync.Pool{\n\tNew: func() any {\n\t\tbuf := make([]byte, btfHeaderLen+128)\n\t\treturn &buf\n\t},\n}\n\nfunc getByteSlice() *[]byte {\n\treturn bufferPool.Get().(*[]byte)\n}\n\nfunc putByteSlice(buf *[]byte) {\n\t*buf = (*buf)[:0]\n\tbufferPool.Put(buf)\n}\n\n// Builder turns Types into raw BTF.\n//\n// The default value may be used and represents an empty BTF blob. Void is\n// added implicitly if necessary.\ntype Builder struct {\n\t// Explicitly added types.\n\ttypes []Type\n\t// IDs for all added types which the user knows about.\n\tstableIDs map[Type]TypeID\n\t// Explicitly added strings.\n\tstrings *stringTableBuilder\n\t// Deduplication data structure.\n\tdeduper *deduper\n}\n\ntype BuilderOptions struct {\n\t// Deduplicate enables type deduplication.\n\tDeduplicate bool\n}\n\n// NewBuilder creates a Builder from a list of types.\n//\n// It is more efficient than calling [Add] individually.\n//\n// Returns an error if adding any of the types fails.\nfunc NewBuilder(types []Type, opts *BuilderOptions) (*Builder, error) {\n\tif opts == nil {\n\t\topts = &BuilderOptions{}\n\t}\n\n\tb := &Builder{\n\t\tmake([]Type, 0, len(types)),\n\t\tmake(map[Type]TypeID, len(types)),\n\t\tnil,\n\t\tnil,\n\t}\n\n\tif opts.Deduplicate {\n\t\tb.deduper = newDeduper()\n\t}\n\n\tfor _, typ := range types {\n\t\t_, err := b.Add(typ)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"add %s: %w\", typ, err)\n\t\t}\n\t}\n\n\treturn b, nil\n}\n\n// Empty returns true if neither types nor strings have been added.\nfunc (b *Builder) Empty() bool {\n\treturn len(b.types) == 0 && (b.strings == nil || b.strings.Length() == 0)\n}\n\n// Add a Type and allocate a stable ID for it.\n//\n// Adding the identical Type multiple times is valid and will return the same ID.\n//\n// See [Type] for details on identity.\nfunc (b *Builder) Add(typ Type) (TypeID, error) {\n\tif _, ok := typ.(*Void); ok {\n\t\t// Equality is weird for void, since it is a zero sized type.\n\t\treturn 0, nil\n\t}\n\n\tif err := internal.IsNil(typ); err != nil {\n\t\treturn 0, fmt.Errorf(\"invalid type: %w\", err)\n\t}\n\n\tif b.stableIDs == nil {\n\t\tb.stableIDs = make(map[Type]TypeID)\n\t}\n\n\tif b.deduper != nil {\n\t\tvar err error\n\t\ttyp, err = b.deduper.deduplicate(typ)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\n\tif ds, ok := typ.(*Datasec); ok {\n\t\tif err := datasecResolveWorkaround(b, ds); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\n\tid, ok := b.stableIDs[typ]\n\tif ok {\n\t\treturn id, nil\n\t}\n\n\tb.types = append(b.types, typ)\n\n\tid = TypeID(len(b.types))\n\tif int(id) != len(b.types) {\n\t\treturn 0, fmt.Errorf(\"no more type IDs\")\n\t}\n\n\tb.stableIDs[typ] = id\n\treturn id, nil\n}\n\n// Spec marshals the Builder's types and returns a new Spec to query them.\n//\n// The resulting Spec does not share any state with the Builder, subsequent\n// additions to the Builder will not affect the Spec.\nfunc (b *Builder) Spec() (*Spec, error) {\n\tbuf, err := b.Marshal(make([]byte, 0), nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn loadRawSpec(buf, nil)\n}\n\n// Marshal encodes all types in the Marshaler into BTF wire format.\n//\n// opts may be nil.\nfunc (b *Builder) Marshal(buf []byte, opts *MarshalOptions) ([]byte, error) {\n\tstb := b.strings\n\tif stb == nil {\n\t\t// Assume that most types are named. This makes encoding large BTF like\n\t\t// vmlinux a lot cheaper.\n\t\tstb = newStringTableBuilder(len(b.types))\n\t} else {\n\t\t// Avoid modifying the Builder's string table.\n\t\tstb = b.strings.Copy()\n\t}\n\n\tif opts == nil {\n\t\topts = &MarshalOptions{Order: internal.NativeEndian}\n\t}\n\n\t// Reserve space for the BTF header.\n\tbuf = slices.Grow(buf, btfHeaderLen)[:btfHeaderLen]\n\n\te := encoder{\n\t\tMarshalOptions: *opts,\n\t\tstrings:        stb,\n\t\tlastID:         TypeID(len(b.types)),\n\t\tvisited:        make(map[Type]struct{}, len(b.types)),\n\t\tids:            maps.Clone(b.stableIDs),\n\t}\n\n\tif e.ids == nil {\n\t\te.ids = make(map[Type]TypeID)\n\t}\n\n\ttypes := b.types\n\tif len(types) == 0 && stb.Length() > 0 && opts.PreventNoTypeFound {\n\t\t// We have strings that need to be written out,\n\t\t// but no types (besides the implicit Void).\n\t\t// Kernels as recent as v6.7 refuse to load such BTF\n\t\t// with a \"No type found\" error in the log.\n\t\t// Fix this by adding a dummy type.\n\t\ttypes = []Type{&Int{Size: 0}}\n\t}\n\n\t// Ensure that types are marshaled in the exact order they were Add()ed.\n\t// Otherwise the ID returned from Add() won't match.\n\te.pending.Grow(len(types))\n\tfor _, typ := range types {\n\t\te.pending.Push(typ)\n\t}\n\n\tbuf, err := e.deflatePending(buf)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlength := len(buf)\n\ttypeLen := uint32(length - btfHeaderLen)\n\n\tstringLen := e.strings.Length()\n\tbuf = e.strings.AppendEncoded(buf)\n\n\t// Fill out the header, and write it out.\n\theader := &btfHeader{\n\t\tMagic:     btfMagic,\n\t\tVersion:   1,\n\t\tFlags:     0,\n\t\tHdrLen:    uint32(btfHeaderLen),\n\t\tTypeOff:   0,\n\t\tTypeLen:   typeLen,\n\t\tStringOff: typeLen,\n\t\tStringLen: uint32(stringLen),\n\t}\n\n\t_, err = binary.Encode(buf[:btfHeaderLen], e.Order, header)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"write header: %v\", err)\n\t}\n\n\treturn buf, nil\n}\n\n// addString adds a string to the resulting BTF.\n//\n// Adding the same string multiple times will return the same result.\n//\n// Returns an identifier into the string table or an error if the string\n// contains invalid characters.\nfunc (b *Builder) addString(str string) (uint32, error) {\n\tif b.strings == nil {\n\t\tb.strings = newStringTableBuilder(0)\n\t}\n\n\treturn b.strings.Add(str)\n}\n\nfunc (e *encoder) allocateIDs(root Type) error {\n\tfor typ := range postorder(root, e.visited) {\n\t\tif _, ok := typ.(*Void); ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tif _, ok := e.ids[typ]; ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tid := e.lastID + 1\n\t\tif id < e.lastID {\n\t\t\treturn errors.New(\"type ID overflow\")\n\t\t}\n\n\t\te.pending.Push(typ)\n\t\te.ids[typ] = id\n\t\te.lastID = id\n\t}\n\n\treturn nil\n}\n\n// id returns the ID for the given type or panics with an error.\nfunc (e *encoder) id(typ Type) TypeID {\n\tif _, ok := typ.(*Void); ok {\n\t\treturn 0\n\t}\n\n\tid, ok := e.ids[typ]\n\tif !ok {\n\t\tpanic(fmt.Errorf(\"no ID for type %v\", typ))\n\t}\n\n\treturn id\n}\n\nfunc (e *encoder) deflatePending(buf []byte) ([]byte, error) {\n\t// Declare root outside of the loop to avoid repeated heap allocations.\n\tvar root Type\n\n\tfor !e.pending.Empty() {\n\t\troot = e.pending.Shift()\n\n\t\t// Allocate IDs for all children of typ, including transitive dependencies.\n\t\terr := e.allocateIDs(root)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tbuf, err = e.deflateType(buf, root)\n\t\tif err != nil {\n\t\t\tid := e.ids[root]\n\t\t\treturn nil, fmt.Errorf(\"deflate %v with ID %d: %w\", root, id, err)\n\t\t}\n\t}\n\n\treturn buf, nil\n}\n\nfunc (e *encoder) deflateType(buf []byte, typ Type) (_ []byte, err error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tvar ok bool\n\t\t\terr, ok = r.(error)\n\t\t\tif !ok {\n\t\t\t\tpanic(r)\n\t\t\t}\n\t\t}\n\t}()\n\n\tvar raw btfType\n\traw.NameOff, err = e.strings.Add(typ.TypeName())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Reserve space for the btfType header.\n\tstart := len(buf)\n\tbuf = append(buf, make([]byte, unsafe.Sizeof(raw))...)\n\n\tswitch v := typ.(type) {\n\tcase *Void:\n\t\treturn nil, errors.New(\"Void is implicit in BTF wire format\")\n\n\tcase *Int:\n\t\tbuf, err = e.deflateInt(buf, &raw, v)\n\n\tcase *Pointer:\n\t\traw.SetKind(kindPointer)\n\t\traw.SetType(e.id(v.Target))\n\n\tcase *Array:\n\t\traw.SetKind(kindArray)\n\t\tbuf, err = binary.Append(buf, e.Order, &btfArray{\n\t\t\te.id(v.Type),\n\t\t\te.id(v.Index),\n\t\t\tv.Nelems,\n\t\t})\n\n\tcase *Struct:\n\t\traw.SetKind(kindStruct)\n\t\traw.SetSize(v.Size)\n\t\tbuf, err = e.deflateMembers(buf, &raw, v.Members)\n\n\tcase *Union:\n\t\tbuf, err = e.deflateUnion(buf, &raw, v)\n\n\tcase *Enum:\n\t\tif v.Size == 8 {\n\t\t\tbuf, err = e.deflateEnum64(buf, &raw, v)\n\t\t} else {\n\t\t\tbuf, err = e.deflateEnum(buf, &raw, v)\n\t\t}\n\n\tcase *Fwd:\n\t\traw.SetKind(kindForward)\n\t\traw.SetFwdKind(v.Kind)\n\n\tcase *Typedef:\n\t\traw.SetKind(kindTypedef)\n\t\traw.SetType(e.id(v.Type))\n\n\tcase *Volatile:\n\t\traw.SetKind(kindVolatile)\n\t\traw.SetType(e.id(v.Type))\n\n\tcase *Const:\n\t\te.deflateConst(&raw, v)\n\n\tcase *Restrict:\n\t\traw.SetKind(kindRestrict)\n\t\traw.SetType(e.id(v.Type))\n\n\tcase *Func:\n\t\traw.SetKind(kindFunc)\n\t\traw.SetType(e.id(v.Type))\n\t\tif !e.StripFuncLinkage {\n\t\t\traw.SetLinkage(v.Linkage)\n\t\t}\n\n\tcase *FuncProto:\n\t\traw.SetKind(kindFuncProto)\n\t\traw.SetType(e.id(v.Return))\n\t\traw.SetVlen(len(v.Params))\n\t\tbuf, err = e.deflateFuncParams(buf, v.Params)\n\n\tcase *Var:\n\t\traw.SetKind(kindVar)\n\t\traw.SetType(e.id(v.Type))\n\t\tbuf, err = binary.Append(buf, e.Order, btfVariable{uint32(v.Linkage)})\n\n\tcase *Datasec:\n\t\traw.SetKind(kindDatasec)\n\t\traw.SetSize(v.Size)\n\t\traw.SetVlen(len(v.Vars))\n\t\tbuf, err = e.deflateVarSecinfos(buf, v.Vars)\n\n\tcase *Float:\n\t\traw.SetKind(kindFloat)\n\t\traw.SetSize(v.Size)\n\n\tcase *declTag:\n\t\tbuf, err = e.deflateDeclTag(buf, &raw, v)\n\n\tcase *TypeTag:\n\t\terr = e.deflateTypeTag(&raw, v)\n\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"don't know how to deflate %T\", v)\n\t}\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\theader := buf[start : start+int(unsafe.Sizeof(raw))]\n\tif _, err = raw.Encode(header, e.Order); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn buf, nil\n}\n\nfunc (e *encoder) deflateInt(buf []byte, raw *btfType, i *Int) ([]byte, error) {\n\traw.SetKind(kindInt)\n\traw.SetSize(i.Size)\n\n\tvar bi btfInt\n\tbi.SetEncoding(i.Encoding)\n\t// We need to set bits in addition to size, since btf_type_int_is_regular\n\t// otherwise flags this as a bitfield.\n\tbi.SetBits(byte(i.Size) * 8)\n\treturn binary.Append(buf, e.Order, bi)\n}\n\nfunc (e *encoder) deflateDeclTag(buf []byte, raw *btfType, tag *declTag) ([]byte, error) {\n\t// Replace a decl tag with an integer for compatibility with <5.16 kernels,\n\t// following libbpf behaviour.\n\tif e.ReplaceDeclTags {\n\t\ttyp := &Int{\"decl_tag_placeholder\", 1, Unsigned}\n\t\tbuf, err := e.deflateInt(buf, raw, typ)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\t// Add the placeholder type name to the string table. The encoder added the\n\t\t// original type name before this call.\n\t\traw.NameOff, err = e.strings.Add(typ.TypeName())\n\t\treturn buf, err\n\t}\n\n\tvar err error\n\traw.SetKind(kindDeclTag)\n\traw.SetType(e.id(tag.Type))\n\traw.NameOff, err = e.strings.Add(tag.Value)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn binary.Append(buf, e.Order, btfDeclTag{uint32(tag.Index)})\n}\n\nfunc (e *encoder) deflateConst(raw *btfType, c *Const) {\n\traw.SetKind(kindConst)\n\traw.SetType(e.id(c.Type))\n}\n\nfunc (e *encoder) deflateTypeTag(raw *btfType, tag *TypeTag) (err error) {\n\t// Replace a type tag with a const qualifier for compatibility with <5.17\n\t// kernels, following libbpf behaviour.\n\tif e.ReplaceTypeTags {\n\t\te.deflateConst(raw, &Const{tag.Type})\n\t\treturn nil\n\t}\n\n\traw.SetKind(kindTypeTag)\n\traw.SetType(e.id(tag.Type))\n\traw.NameOff, err = e.strings.Add(tag.Value)\n\treturn\n}\n\nfunc (e *encoder) deflateUnion(buf []byte, raw *btfType, union *Union) ([]byte, error) {\n\traw.SetKind(kindUnion)\n\traw.SetSize(union.Size)\n\treturn e.deflateMembers(buf, raw, union.Members)\n}\n\nfunc (e *encoder) deflateMembers(buf []byte, header *btfType, members []Member) ([]byte, error) {\n\tvar bm btfMember\n\tisBitfield := false\n\n\tbuf = slices.Grow(buf, len(members)*int(unsafe.Sizeof(bm)))\n\tfor _, member := range members {\n\t\tisBitfield = isBitfield || member.BitfieldSize > 0\n\n\t\toffset := member.Offset\n\t\tif isBitfield {\n\t\t\toffset = member.BitfieldSize<<24 | (member.Offset & 0xffffff)\n\t\t}\n\n\t\tnameOff, err := e.strings.Add(member.Name)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tbm = btfMember{\n\t\t\tnameOff,\n\t\t\te.id(member.Type),\n\t\t\tuint32(offset),\n\t\t}\n\n\t\tbuf, err = binary.Append(buf, e.Order, &bm)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\theader.SetVlen(len(members))\n\theader.SetBitfield(isBitfield)\n\treturn buf, nil\n}\n\nfunc (e *encoder) deflateEnum(buf []byte, raw *btfType, enum *Enum) ([]byte, error) {\n\traw.SetKind(kindEnum)\n\traw.SetSize(enum.Size)\n\traw.SetVlen(len(enum.Values))\n\t// Signedness appeared together with ENUM64 support.\n\traw.SetSigned(enum.Signed && !e.ReplaceEnum64)\n\treturn e.deflateEnumValues(buf, enum)\n}\n\nfunc (e *encoder) deflateEnumValues(buf []byte, enum *Enum) ([]byte, error) {\n\tvar be btfEnum\n\tbuf = slices.Grow(buf, len(enum.Values)*int(unsafe.Sizeof(be)))\n\tfor _, value := range enum.Values {\n\t\tnameOff, err := e.strings.Add(value.Name)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif enum.Signed {\n\t\t\tif signedValue := int64(value.Value); signedValue < math.MinInt32 || signedValue > math.MaxInt32 {\n\t\t\t\treturn nil, fmt.Errorf(\"value %d of enum %q exceeds 32 bits\", signedValue, value.Name)\n\t\t\t}\n\t\t} else {\n\t\t\tif value.Value > math.MaxUint32 {\n\t\t\t\treturn nil, fmt.Errorf(\"value %d of enum %q exceeds 32 bits\", value.Value, value.Name)\n\t\t\t}\n\t\t}\n\n\t\tbe = btfEnum{\n\t\t\tnameOff,\n\t\t\tuint32(value.Value),\n\t\t}\n\n\t\tbuf, err = binary.Append(buf, e.Order, &be)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn buf, nil\n}\n\nfunc (e *encoder) deflateEnum64(buf []byte, raw *btfType, enum *Enum) ([]byte, error) {\n\tif e.ReplaceEnum64 {\n\t\t// Replace the ENUM64 with a union of fields with the correct size.\n\t\t// This matches libbpf behaviour on purpose.\n\t\tplaceholder := &Int{\n\t\t\t\"enum64_placeholder\",\n\t\t\tenum.Size,\n\t\t\tUnsigned,\n\t\t}\n\t\tif enum.Signed {\n\t\t\tplaceholder.Encoding = Signed\n\t\t}\n\t\tif err := e.allocateIDs(placeholder); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"add enum64 placeholder: %w\", err)\n\t\t}\n\n\t\tmembers := make([]Member, 0, len(enum.Values))\n\t\tfor _, v := range enum.Values {\n\t\t\tmembers = append(members, Member{\n\t\t\t\tName: v.Name,\n\t\t\t\tType: placeholder,\n\t\t\t})\n\t\t}\n\n\t\treturn e.deflateUnion(buf, raw, &Union{enum.Name, enum.Size, members, nil})\n\t}\n\n\traw.SetKind(kindEnum64)\n\traw.SetSize(enum.Size)\n\traw.SetVlen(len(enum.Values))\n\traw.SetSigned(enum.Signed)\n\treturn e.deflateEnum64Values(buf, enum.Values)\n}\n\nfunc (e *encoder) deflateEnum64Values(buf []byte, values []EnumValue) ([]byte, error) {\n\tvar be btfEnum64\n\tbuf = slices.Grow(buf, len(values)*int(unsafe.Sizeof(be)))\n\tfor _, value := range values {\n\t\tnameOff, err := e.strings.Add(value.Name)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tbe = btfEnum64{\n\t\t\tnameOff,\n\t\t\tuint32(value.Value),\n\t\t\tuint32(value.Value >> 32),\n\t\t}\n\n\t\tbuf, err = binary.Append(buf, e.Order, &be)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn buf, nil\n}\n\nfunc (e *encoder) deflateFuncParams(buf []byte, params []FuncParam) ([]byte, error) {\n\tvar bp btfParam\n\tbuf = slices.Grow(buf, len(params)*int(unsafe.Sizeof(bp)))\n\tfor _, param := range params {\n\t\tnameOff, err := e.strings.Add(param.Name)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tbp = btfParam{\n\t\t\tnameOff,\n\t\t\te.id(param.Type),\n\t\t}\n\n\t\tbuf, err = binary.Append(buf, e.Order, &bp)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn buf, nil\n}\n\nfunc (e *encoder) deflateVarSecinfos(buf []byte, vars []VarSecinfo) ([]byte, error) {\n\tvar vsi btfVarSecinfo\n\tvar err error\n\tbuf = slices.Grow(buf, len(vars)*int(unsafe.Sizeof(vsi)))\n\tfor _, v := range vars {\n\t\tvsi = btfVarSecinfo{\n\t\t\te.id(v.Type),\n\t\t\tv.Offset,\n\t\t\tv.Size,\n\t\t}\n\n\t\tbuf, err = binary.Append(buf, e.Order, vsi)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn buf, nil\n}\n\n// MarshalMapKV creates a BTF object containing a map key and value.\n//\n// The function is intended for the use of the ebpf package and may be removed\n// at any point in time.\nfunc MarshalMapKV(key, value Type) (_ *Handle, keyID, valueID TypeID, err error) {\n\tvar b Builder\n\n\tif key != nil {\n\t\tkeyID, err = b.Add(key)\n\t\tif err != nil {\n\t\t\treturn nil, 0, 0, fmt.Errorf(\"add key type: %w\", err)\n\t\t}\n\t}\n\n\tif value != nil {\n\t\tvalueID, err = b.Add(value)\n\t\tif err != nil {\n\t\t\treturn nil, 0, 0, fmt.Errorf(\"add value type: %w\", err)\n\t\t}\n\t}\n\n\thandle, err := NewHandle(&b)\n\tif err != nil {\n\t\t// Check for 'full' map BTF support, since kernels between 4.18 and 5.2\n\t\t// already support BTF blobs for maps without Var or Datasec just fine.\n\t\tif err := haveMapBTF(); err != nil {\n\t\t\treturn nil, 0, 0, err\n\t\t}\n\t}\n\treturn handle, keyID, valueID, err\n}\n"
  },
  {
    "path": "btf/marshal_test.go",
    "content": "package btf\n\nimport (\n\t\"math\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\t\"github.com/google/go-cmp/cmp\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc TestBuilderMarshal(t *testing.T) {\n\ttyp := &Int{\n\t\tName:     \"foo\",\n\t\tSize:     2,\n\t\tEncoding: Signed | Char,\n\t}\n\n\twant := []Type{\n\t\t(*Void)(nil),\n\t\ttyp,\n\t\t&Pointer{typ},\n\t\t&Typedef{\"baz\", typ, nil},\n\t}\n\n\tb, err := NewBuilder(want, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tcpy := *b\n\tbuf, err := b.Marshal(nil, &MarshalOptions{Order: internal.NativeEndian})\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.CmpEquals(b, &cpy, cmp.AllowUnexported(*b)), qt.Commentf(\"Marshaling should not change Builder state\"))\n\n\thave, err := loadRawSpec(buf, nil)\n\tqt.Assert(t, qt.IsNil(err), qt.Commentf(\"Couldn't parse BTF\"))\n\tqt.Assert(t, qt.DeepEquals(typesFromSpec(t, have), want))\n}\n\nfunc TestBuilderAdd(t *testing.T) {\n\ti := &Int{\n\t\tName:     \"foo\",\n\t\tSize:     2,\n\t\tEncoding: Signed | Char,\n\t}\n\tpi := &Pointer{i}\n\n\tvar b Builder\n\tid, err := b.Add(pi)\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.Equals(id, TypeID(1)), qt.Commentf(\"First non-void type doesn't get id 1\"))\n\n\tid, err = b.Add(pi)\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.Equals(id, TypeID(1)))\n\n\tid, err = b.Add(i)\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.Equals(id, TypeID(2)), qt.Commentf(\"Second type doesn't get id 2\"))\n\n\tid, err = b.Add(i)\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.Equals(id, TypeID(2)), qt.Commentf(\"Adding a type twice returns different ids\"))\n\n\tid, err = b.Add(&Typedef{\"baz\", i, nil})\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.Equals(id, TypeID(3)))\n\n\t_, err = b.Add(nil)\n\tqt.Assert(t, qt.IsNotNil(err))\n\tt.Log(err)\n\n\t_, err = b.Add((*Int)(nil))\n\tqt.Assert(t, qt.IsNotNil(err))\n\tt.Log(err)\n}\n\nfunc TestBuilderSpec(t *testing.T) {\n\tb, err := NewBuilder([]Type{\n\t\t&Int{Name: \"foo\", Size: 2},\n\t\t&Int{Name: \"foo\", Size: 2},\n\t}, &BuilderOptions{Deduplicate: true})\n\tqt.Assert(t, qt.IsNil(err))\n\n\tspec, err := b.Spec()\n\tqt.Assert(t, qt.IsNil(err))\n\n\t// With deduplication enabled, both ints should be merged into one,\n\t// allowing queries with AnyTypeByName.\n\t_, err = spec.AnyTypeByName(\"foo\")\n\tqt.Assert(t, qt.IsNil(err))\n}\n\nfunc TestRoundtripVMlinux(t *testing.T) {\n\ttypes := typesFromSpec(t, vmlinuxSpec(t))\n\n\t// Randomize the order to force different permutations of walking the type\n\t// graph. Keep Void at index 0.\n\ttestutils.Rand(t).Shuffle(len(types[1:]), func(i, j int) {\n\t\ttypes[i+1], types[j+1] = types[j+1], types[i+1]\n\t})\n\n\tvisited := make(map[Type]struct{})\nlimitTypes:\n\tfor i, typ := range types {\n\t\tfor range postorder(typ, visited) {\n\t\t}\n\t\tif len(visited) >= math.MaxInt16 {\n\t\t\t// IDs exceeding math.MaxUint16 can trigger a bug when loading BTF.\n\t\t\t// This can be removed once the patch lands.\n\t\t\t// See https://lore.kernel.org/bpf/20220909092107.3035-1-oss@lmb.io/\n\t\t\ttypes = types[:i]\n\t\t\tbreak limitTypes\n\t\t}\n\t}\n\n\tb, err := NewBuilder(types, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\tbuf, err := b.Marshal(nil, KernelMarshalOptions())\n\tqt.Assert(t, qt.IsNil(err))\n\n\trebuilt, err := loadRawSpec(buf, nil)\n\tqt.Assert(t, qt.IsNil(err), qt.Commentf(\"round tripping BTF failed\"))\n\n\tif n := len(rebuilt.offsets); n > math.MaxUint16 {\n\t\tt.Logf(\"Rebuilt BTF contains %d types which exceeds uint16, test may fail on older kernels\", n)\n\t}\n\n\th, err := NewHandleFromRawBTF(buf)\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err), qt.Commentf(\"loading rebuilt BTF failed\"))\n\th.Close()\n}\n\nfunc TestMarshalEnum64(t *testing.T) {\n\tenum := &Enum{\n\t\tName:   \"enum64\",\n\t\tSize:   8,\n\t\tSigned: true,\n\t\tValues: []EnumValue{\n\t\t\t{\"A\", 0},\n\t\t\t{\"B\", 1},\n\t\t},\n\t}\n\n\tb, err := NewBuilder([]Type{enum}, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\tbuf, err := b.Marshal(nil, &MarshalOptions{\n\t\tOrder:         internal.NativeEndian,\n\t\tReplaceEnum64: true,\n\t})\n\tqt.Assert(t, qt.IsNil(err))\n\n\tspec, err := loadRawSpec(buf, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tvar have *Union\n\terr = spec.TypeByName(\"enum64\", &have)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tplaceholder := &Int{Name: \"enum64_placeholder\", Size: 8, Encoding: Signed}\n\tqt.Assert(t, qt.DeepEquals(have, &Union{\n\t\tName: \"enum64\",\n\t\tSize: 8,\n\t\tMembers: []Member{\n\t\t\t{Name: \"A\", Type: placeholder},\n\t\t\t{Name: \"B\", Type: placeholder},\n\t\t},\n\t}))\n}\n\nfunc TestMarshalDeclTags(t *testing.T) {\n\ttypes := []Type{\n\t\t// Instead of an adjacent declTag, this will receive a placeholder Int.\n\t\t&Typedef{\n\t\t\tName: \"decl tag typedef\",\n\t\t\tTags: []string{\"decl tag\"},\n\t\t\tType: &Int{Name: \"decl tag target\"},\n\t\t},\n\t}\n\n\tb, err := NewBuilder(types, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\tbuf, err := b.Marshal(nil, &MarshalOptions{\n\t\tOrder:           internal.NativeEndian,\n\t\tReplaceDeclTags: true,\n\t})\n\tqt.Assert(t, qt.IsNil(err))\n\n\tspec, err := loadRawSpec(buf, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tvar td *Typedef\n\tqt.Assert(t, qt.IsNil(spec.TypeByName(\"decl tag typedef\", &td)))\n\tvar ti *Int\n\tqt.Assert(t, qt.IsNil(spec.TypeByName(\"decl_tag_placeholder\", &ti)))\n}\n\nfunc TestMarshalTypeTags(t *testing.T) {\n\ttypes := []Type{\n\t\t// Instead of pointing to a TypeTag, this will point to an intermediary Const.\n\t\t&Typedef{\n\t\t\tName: \"type tag typedef\",\n\t\t\tType: &TypeTag{\n\t\t\t\tValue: \"type tag\",\n\t\t\t\tType: &Pointer{\n\t\t\t\t\tTarget: &Int{Name: \"type tag target\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tb, err := NewBuilder(types, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\tbuf, err := b.Marshal(nil, &MarshalOptions{\n\t\tOrder:           internal.NativeEndian,\n\t\tReplaceTypeTags: true,\n\t})\n\tqt.Assert(t, qt.IsNil(err))\n\n\tspec, err := loadRawSpec(buf, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tvar td *Typedef\n\tqt.Assert(t, qt.IsNil(spec.TypeByName(\"type tag typedef\", &td)))\n\tqt.Assert(t, qt.Satisfies(td.Type, func(typ Type) bool {\n\t\t_, ok := typ.(*Const)\n\t\treturn ok\n\t}))\n}\n\nfunc BenchmarkMarshaler(b *testing.B) {\n\ttypes := typesFromSpec(b, vmlinuxTestdataSpec(b))[:100]\n\n\tb.ReportAllocs()\n\n\tfor b.Loop() {\n\t\tvar b Builder\n\t\tfor _, typ := range types {\n\t\t\t_, _ = b.Add(typ)\n\t\t}\n\t\t_, _ = b.Marshal(nil, nil)\n\t}\n}\n\nfunc BenchmarkBuildVmlinux(b *testing.B) {\n\ttypes := typesFromSpec(b, vmlinuxTestdataSpec(b))\n\n\tb.ReportAllocs()\n\n\tfor b.Loop() {\n\t\tvar b Builder\n\t\tfor _, typ := range types {\n\t\t\t_, _ = b.Add(typ)\n\t\t}\n\t\t_, _ = b.Marshal(nil, nil)\n\t}\n}\n\nfunc marshalNativeEndian(tb testing.TB, types []Type) []byte {\n\ttb.Helper()\n\n\tb, err := NewBuilder(types, nil)\n\tqt.Assert(tb, qt.IsNil(err))\n\tbuf, err := b.Marshal(nil, nil)\n\tqt.Assert(tb, qt.IsNil(err))\n\treturn buf\n}\n\nfunc specFromTypes(tb testing.TB, types []Type) *Spec {\n\ttb.Helper()\n\n\tbtf := marshalNativeEndian(tb, types)\n\tspec, err := loadRawSpec(btf, nil)\n\tqt.Assert(tb, qt.IsNil(err))\n\n\treturn spec\n}\n\nfunc typesFromSpec(tb testing.TB, spec *Spec) []Type {\n\ttb.Helper()\n\n\ttypes := make([]Type, 0, len(spec.offsets))\n\n\tfor typ, err := range spec.All() {\n\t\tqt.Assert(tb, qt.IsNil(err))\n\t\ttypes = append(types, typ)\n\t}\n\n\treturn types\n}\n"
  },
  {
    "path": "btf/strings.go",
    "content": "package btf\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"maps\"\n\t\"strings\"\n\t\"sync\"\n)\n\n// stringTable contains a sequence of null-terminated strings.\n//\n// It is safe for concurrent use.\ntype stringTable struct {\n\tbase  *stringTable\n\tbytes []byte\n\n\tmu    sync.Mutex\n\tcache map[uint32]string\n}\n\n// sizedReader is implemented by bytes.Reader, io.SectionReader, strings.Reader, etc.\ntype sizedReader interface {\n\tio.Reader\n\tSize() int64\n}\n\nfunc readStringTable(r sizedReader, base *stringTable) (*stringTable, error) {\n\tbytes := make([]byte, r.Size())\n\tif _, err := io.ReadFull(r, bytes); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn newStringTable(bytes, base)\n}\n\nfunc newStringTable(bytes []byte, base *stringTable) (*stringTable, error) {\n\t// When parsing split BTF's string table, the first entry offset is derived\n\t// from the last entry offset of the base BTF.\n\tfirstStringOffset := uint32(0)\n\tif base != nil {\n\t\tfirstStringOffset = uint32(len(base.bytes))\n\t}\n\n\tif len(bytes) > 0 {\n\t\tif bytes[len(bytes)-1] != 0 {\n\t\t\treturn nil, errors.New(\"string table isn't null terminated\")\n\t\t}\n\n\t\tif firstStringOffset == 0 && bytes[0] != 0 {\n\t\t\treturn nil, errors.New(\"first item in string table is non-empty\")\n\t\t}\n\t}\n\n\treturn &stringTable{base: base, bytes: bytes}, nil\n}\n\nfunc (st *stringTable) Lookup(offset uint32) (string, error) {\n\t// Fast path: zero offset is the empty string, looked up frequently.\n\tif offset == 0 {\n\t\treturn \"\", nil\n\t}\n\n\tb, err := st.lookupSlow(offset)\n\treturn string(b), err\n}\n\nfunc (st *stringTable) LookupBytes(offset uint32) ([]byte, error) {\n\t// Fast path: zero offset is the empty string, looked up frequently.\n\tif offset == 0 {\n\t\treturn nil, nil\n\t}\n\n\treturn st.lookupSlow(offset)\n}\n\nfunc (st *stringTable) lookupSlow(offset uint32) ([]byte, error) {\n\tif st.base != nil {\n\t\tn := uint32(len(st.base.bytes))\n\t\tif offset < n {\n\t\t\treturn st.base.lookupSlow(offset)\n\t\t}\n\t\toffset -= n\n\t}\n\n\tif offset > uint32(len(st.bytes)) {\n\t\treturn nil, fmt.Errorf(\"offset %d is out of bounds of string table\", offset)\n\t}\n\n\tif offset > 0 && st.bytes[offset-1] != 0 {\n\t\treturn nil, fmt.Errorf(\"offset %d is not the beginning of a string\", offset)\n\t}\n\n\ti := bytes.IndexByte(st.bytes[offset:], 0)\n\treturn st.bytes[offset : offset+uint32(i)], nil\n}\n\n// LookupCache returns the string at the given offset, caching the result\n// for future lookups.\nfunc (cst *stringTable) LookupCached(offset uint32) (string, error) {\n\t// Fast path: zero offset is the empty string, looked up frequently.\n\tif offset == 0 {\n\t\treturn \"\", nil\n\t}\n\n\tcst.mu.Lock()\n\tdefer cst.mu.Unlock()\n\n\tif str, ok := cst.cache[offset]; ok {\n\t\treturn str, nil\n\t}\n\n\tstr, err := cst.Lookup(offset)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tif cst.cache == nil {\n\t\tcst.cache = make(map[uint32]string)\n\t}\n\tcst.cache[offset] = str\n\treturn str, nil\n}\n\n// stringTableBuilder builds BTF string tables.\ntype stringTableBuilder struct {\n\tlength  uint32\n\tstrings map[string]uint32\n}\n\n// newStringTableBuilder creates a builder with the given capacity.\n//\n// capacity may be zero.\nfunc newStringTableBuilder(capacity int) *stringTableBuilder {\n\tvar stb stringTableBuilder\n\n\tif capacity == 0 {\n\t\t// Use the runtime's small default size.\n\t\tstb.strings = make(map[string]uint32)\n\t} else {\n\t\tstb.strings = make(map[string]uint32, capacity)\n\t}\n\n\t// Ensure that the empty string is at index 0.\n\tstb.append(\"\")\n\treturn &stb\n}\n\n// Add a string to the table.\n//\n// Adding the same string multiple times will only store it once.\nfunc (stb *stringTableBuilder) Add(str string) (uint32, error) {\n\tif strings.IndexByte(str, 0) != -1 {\n\t\treturn 0, fmt.Errorf(\"string contains null: %q\", str)\n\t}\n\n\toffset, ok := stb.strings[str]\n\tif ok {\n\t\treturn offset, nil\n\t}\n\n\treturn stb.append(str), nil\n}\n\nfunc (stb *stringTableBuilder) append(str string) uint32 {\n\toffset := stb.length\n\tstb.length += uint32(len(str)) + 1\n\tstb.strings[str] = offset\n\treturn offset\n}\n\n// Lookup finds the offset of a string in the table.\n//\n// Returns an error if str hasn't been added yet.\nfunc (stb *stringTableBuilder) Lookup(str string) (uint32, error) {\n\toffset, ok := stb.strings[str]\n\tif !ok {\n\t\treturn 0, fmt.Errorf(\"string %q is not in table\", str)\n\t}\n\n\treturn offset, nil\n}\n\n// Length returns the length in bytes.\nfunc (stb *stringTableBuilder) Length() int {\n\treturn int(stb.length)\n}\n\n// AppendEncoded appends the string table to the end of the provided buffer.\nfunc (stb *stringTableBuilder) AppendEncoded(buf []byte) []byte {\n\tn := len(buf)\n\tbuf = append(buf, make([]byte, stb.Length())...)\n\tstrings := buf[n:]\n\tfor str, offset := range stb.strings {\n\t\tcopy(strings[offset:], str)\n\t}\n\treturn buf\n}\n\n// Copy the string table builder.\nfunc (stb *stringTableBuilder) Copy() *stringTableBuilder {\n\treturn &stringTableBuilder{\n\t\tstb.length,\n\t\tmaps.Clone(stb.strings),\n\t}\n}\n"
  },
  {
    "path": "btf/strings_test.go",
    "content": "package btf\n\nimport (\n\t\"bytes\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestStringTable(t *testing.T) {\n\tconst in = \"\\x00one\\x00two\\x00\"\n\tconst splitIn = \"three\\x00four\\x00\"\n\n\tst, err := readStringTable(strings.NewReader(in), nil)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// Parse string table of split BTF\n\tsplit, err := readStringTable(strings.NewReader(splitIn), st)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\ttestcases := []struct {\n\t\toffset uint32\n\t\twant   string\n\t}{\n\t\t{0, \"\"},\n\t\t{1, \"one\"},\n\t\t{5, \"two\"},\n\t\t{9, \"three\"},\n\t\t{15, \"four\"},\n\t}\n\n\tfor _, tc := range testcases {\n\t\thave, err := split.Lookup(tc.offset)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Offset %d: %s\", tc.offset, err)\n\t\t\tcontinue\n\t\t}\n\n\t\tif have != tc.want {\n\t\t\tt.Errorf(\"Offset %d: want %s but have %s\", tc.offset, tc.want, have)\n\t\t}\n\t}\n\n\tif _, err := st.Lookup(2); err == nil {\n\t\tt.Error(\"No error when using offset pointing into middle of string\")\n\t}\n\n\t// Make sure we reject bogus tables\n\t_, err = readStringTable(strings.NewReader(\"\\x00one\"), nil)\n\tif err == nil {\n\t\tt.Fatal(\"Accepted non-terminated string\")\n\t}\n\n\t_, err = readStringTable(strings.NewReader(\"one\\x00\"), nil)\n\tif err == nil {\n\t\tt.Fatal(\"Accepted non-empty first item\")\n\t}\n}\n\nfunc TestEmptyStringTable(t *testing.T) {\n\tempty, err := newStringTable(nil, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\tstr, err := empty.Lookup(0)\n\tqt.Assert(t, qt.IsNil(err), qt.Commentf(\"Can't lookup empty string\"))\n\tqt.Assert(t, qt.Equals(str, \"\"), qt.Commentf(\"Empty string lookup returned %q\", str))\n\t_, err = empty.Lookup(1)\n\tqt.Assert(t, qt.IsNotNil(err))\n}\n\nfunc TestStringTableBuilder(t *testing.T) {\n\tstb := newStringTableBuilder(0)\n\n\t_, err := readStringTable(bytes.NewReader(stb.AppendEncoded(nil)), nil)\n\tqt.Assert(t, qt.IsNil(err), qt.Commentf(\"Can't parse string table\"))\n\n\t_, err = stb.Add(\"foo\\x00bar\")\n\tqt.Assert(t, qt.IsNotNil(err))\n\n\tempty, err := stb.Add(\"\")\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.Equals(empty, 0), qt.Commentf(\"The empty string is not at index 0\"))\n\n\tfoo1, _ := stb.Add(\"foo\")\n\tfoo2, _ := stb.Add(\"foo\")\n\tqt.Assert(t, qt.Equals(foo1, foo2), qt.Commentf(\"Adding the same string returns different offsets\"))\n\n\ttable := stb.AppendEncoded(nil)\n\tif n := bytes.Count(table, []byte(\"foo\")); n != 1 {\n\t\tt.Fatalf(\"Marshalled string table contains foo %d times instead of once\", n)\n\t}\n\n\t_, err = readStringTable(bytes.NewReader(table), nil)\n\tqt.Assert(t, qt.IsNil(err), qt.Commentf(\"Can't parse string table\"))\n}\n\nfunc BenchmarkStringTableZeroLookup(b *testing.B) {\n\tstrings := vmlinuxTestdataSpec(b).strings\n\n\tfor b.Loop() {\n\t\ts, err := strings.Lookup(0)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t\tif s != \"\" {\n\t\t\tb.Fatal(\"0 is not the empty string\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "btf/testdata/bpf_core_read.h",
    "content": "/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */\n#ifndef __BPF_CORE_READ_H__\n#define __BPF_CORE_READ_H__\n\n/*\n * enum bpf_field_info_kind is passed as a second argument into\n * __builtin_preserve_field_info() built-in to get a specific aspect of\n * a field, captured as a first argument. __builtin_preserve_field_info(field,\n * info_kind) returns __u32 integer and produces BTF field relocation, which\n * is understood and processed by libbpf during BPF object loading. See\n * selftests/bpf for examples.\n */\nenum bpf_field_info_kind {\n\tBPF_FIELD_BYTE_OFFSET = 0,\t/* field byte offset */\n\tBPF_FIELD_BYTE_SIZE = 1,\n\tBPF_FIELD_EXISTS = 2,\t\t/* field existence in target kernel */\n\tBPF_FIELD_SIGNED = 3,\n\tBPF_FIELD_LSHIFT_U64 = 4,\n\tBPF_FIELD_RSHIFT_U64 = 5,\n};\n\n/* second argument to __builtin_btf_type_id() built-in */\nenum bpf_type_id_kind {\n\tBPF_TYPE_ID_LOCAL = 0,\t\t/* BTF type ID in local program */\n\tBPF_TYPE_ID_TARGET = 1,\t\t/* BTF type ID in target kernel */\n};\n\n/* second argument to __builtin_preserve_type_info() built-in */\nenum bpf_type_info_kind {\n\tBPF_TYPE_EXISTS = 0,\t/* type existence in target kernel */\n\tBPF_TYPE_SIZE = 1,\t\t/* type size in target kernel */\n\tBPF_TYPE_MATCHES = 2, \t/* type match in target kernel */\n};\n\n/* second argument to __builtin_preserve_enum_value() built-in */\nenum bpf_enum_value_kind {\n\tBPF_ENUMVAL_EXISTS = 0,\t\t/* enum value existence in kernel */\n\tBPF_ENUMVAL_VALUE = 1,\t\t/* enum value value relocation */\n};\n\n#define __CORE_RELO(src, field, info)\t\t\t\t\t      \\\n\t__builtin_preserve_field_info((src)->field, BPF_FIELD_##info)\n\n#if __BYTE_ORDER == __LITTLE_ENDIAN\n#define __CORE_BITFIELD_PROBE_READ(dst, src, fld)\t\t\t      \\\n\tbpf_probe_read_kernel(\t\t\t\t\t\t      \\\n\t\t\t(void *)dst,\t\t\t\t      \\\n\t\t\t__CORE_RELO(src, fld, BYTE_SIZE),\t\t      \\\n\t\t\t(const void *)src + __CORE_RELO(src, fld, BYTE_OFFSET))\n#else\n/* semantics of LSHIFT_64 assumes loading values into low-ordered bytes, so\n * for big-endian we need to adjust destination pointer accordingly, based on\n * field byte size\n */\n#define __CORE_BITFIELD_PROBE_READ(dst, src, fld)\t\t\t      \\\n\tbpf_probe_read_kernel(\t\t\t\t\t\t      \\\n\t\t\t(void *)dst + (8 - __CORE_RELO(src, fld, BYTE_SIZE)), \\\n\t\t\t__CORE_RELO(src, fld, BYTE_SIZE),\t\t      \\\n\t\t\t(const void *)src + __CORE_RELO(src, fld, BYTE_OFFSET))\n#endif\n\n/*\n * Extract bitfield, identified by s->field, and return its value as u64.\n * All this is done in relocatable manner, so bitfield changes such as\n * signedness, bit size, offset changes, this will be handled automatically.\n * This version of macro is using bpf_probe_read_kernel() to read underlying\n * integer storage. Macro functions as an expression and its return type is\n * bpf_probe_read_kernel()'s return value: 0, on success, <0 on error.\n */\n#define BPF_CORE_READ_BITFIELD_PROBED(s, field) ({\t\t\t      \\\n\tunsigned long long val = 0;\t\t\t\t\t      \\\n\t\t\t\t\t\t\t\t\t      \\\n\t__CORE_BITFIELD_PROBE_READ(&val, s, field);\t\t\t      \\\n\tval <<= __CORE_RELO(s, field, LSHIFT_U64);\t\t\t      \\\n\tif (__CORE_RELO(s, field, SIGNED))\t\t\t\t      \\\n\t\tval = ((long long)val) >> __CORE_RELO(s, field, RSHIFT_U64);  \\\n\telse\t\t\t\t\t\t\t\t      \\\n\t\tval = val >> __CORE_RELO(s, field, RSHIFT_U64);\t\t      \\\n\tval;\t\t\t\t\t\t\t\t      \\\n})\n\n/*\n * Extract bitfield, identified by s->field, and return its value as u64.\n * This version of macro is using direct memory reads and should be used from\n * BPF program types that support such functionality (e.g., typed raw\n * tracepoints).\n */\n#define BPF_CORE_READ_BITFIELD(s, field) ({\t\t\t\t      \\\n\tconst void *p = (const void *)s + __CORE_RELO(s, field, BYTE_OFFSET); \\\n\tunsigned long long val;\t\t\t\t\t\t      \\\n\t\t\t\t\t\t\t\t\t      \\\n\t/* This is a so-called barrier_var() operation that makes specified   \\\n\t * variable \"a black box\" for optimizing compiler.\t\t      \\\n\t * It forces compiler to perform BYTE_OFFSET relocation on p and use  \\\n\t * its calculated value in the switch below, instead of applying      \\\n\t * the same relocation 4 times for each individual memory load.       \\\n\t */\t\t\t\t\t\t\t\t      \\\n\tasm volatile(\"\" : \"=r\"(p) : \"0\"(p));\t\t\t\t      \\\n\t\t\t\t\t\t\t\t\t      \\\n\tswitch (__CORE_RELO(s, field, BYTE_SIZE)) {\t\t\t      \\\n\tcase 1: val = *(const unsigned char *)p; break;\t\t\t      \\\n\tcase 2: val = *(const unsigned short *)p; break;\t\t      \\\n\tcase 4: val = *(const unsigned int *)p; break;\t\t\t      \\\n\tcase 8: val = *(const unsigned long long *)p; break;\t\t      \\\n\t}\t\t\t\t\t\t\t\t      \\\n\tval <<= __CORE_RELO(s, field, LSHIFT_U64);\t\t\t      \\\n\tif (__CORE_RELO(s, field, SIGNED))\t\t\t\t      \\\n\t\tval = ((long long)val) >> __CORE_RELO(s, field, RSHIFT_U64);  \\\n\telse\t\t\t\t\t\t\t\t      \\\n\t\tval = val >> __CORE_RELO(s, field, RSHIFT_U64);\t\t      \\\n\tval;\t\t\t\t\t\t\t\t      \\\n})\n\n/*\n * Convenience macro to check that field actually exists in target kernel's.\n * Returns:\n *    1, if matching field is present in target kernel;\n *    0, if no matching field found.\n */\n#define bpf_core_field_exists(field)\t\t\t\t\t    \\\n\t__builtin_preserve_field_info(field, BPF_FIELD_EXISTS)\n\n/*\n * Convenience macro to get the byte size of a field. Works for integers,\n * struct/unions, pointers, arrays, and enums.\n */\n#define bpf_core_field_size(field)\t\t\t\t\t    \\\n\t__builtin_preserve_field_info(field, BPF_FIELD_BYTE_SIZE)\n\n/*\n * Convenience macro to get BTF type ID of a specified type, using a local BTF\n * information. Return 32-bit unsigned integer with type ID from program's own\n * BTF. Always succeeds.\n */\n#define bpf_core_type_id_local(type)\t\t\t\t\t    \\\n\t__builtin_btf_type_id(*(typeof(type) *)0, BPF_TYPE_ID_LOCAL)\n\n/*\n * Convenience macro to get BTF type ID of a target kernel's type that matches\n * specified local type.\n * Returns:\n *    - valid 32-bit unsigned type ID in kernel BTF;\n *    - 0, if no matching type was found in a target kernel BTF.\n */\n#define bpf_core_type_id_kernel(type)\t\t\t\t\t    \\\n\t__builtin_btf_type_id(*(typeof(type) *)0, BPF_TYPE_ID_TARGET)\n\n/*\n * Convenience macro to check that provided named type\n * (struct/union/enum/typedef) exists in a target kernel.\n * Returns:\n *    1, if such type is present in target kernel's BTF;\n *    0, if no matching type is found.\n */\n#define bpf_core_type_exists(type)\t\t\t\t\t    \\\n\t__builtin_preserve_type_info(*(typeof(type) *)0, BPF_TYPE_EXISTS)\n\n/*\n * Convenience macro to check that provided named type\n * (struct/union/enum/typedef) \"matches\" that in a target kernel.\n * Returns:\n *    1, if the type matches in the target kernel's BTF;\n *    0, if the type does not match any in the target kernel\n */\n#define bpf_core_type_matches(type)\t\t\t\t\t    \\\n\t__builtin_preserve_type_info(*(typeof(type) *)0, BPF_TYPE_MATCHES)\n\n\n/*\n * Convenience macro to get the byte size of a provided named type\n * (struct/union/enum/typedef) in a target kernel.\n * Returns:\n *    >= 0 size (in bytes), if type is present in target kernel's BTF;\n *    0, if no matching type is found.\n */\n#define bpf_core_type_size(type)\t\t\t\t\t    \\\n\t__builtin_preserve_type_info(*(typeof(type) *)0, BPF_TYPE_SIZE)\n\n/*\n * Convenience macro to check that provided enumerator value is defined in\n * a target kernel.\n * Returns:\n *    1, if specified enum type and its enumerator value are present in target\n *    kernel's BTF;\n *    0, if no matching enum and/or enum value within that enum is found.\n */\n#define bpf_core_enum_value_exists(enum_type, enum_value)\t\t    \\\n\t__builtin_preserve_enum_value(*(typeof(enum_type) *)enum_value, BPF_ENUMVAL_EXISTS)\n\n/*\n * Convenience macro to get the integer value of an enumerator value in\n * a target kernel.\n * Returns:\n *    64-bit value, if specified enum type and its enumerator value are\n *    present in target kernel's BTF;\n *    0, if no matching enum and/or enum value within that enum is found.\n */\n#define bpf_core_enum_value(enum_type, enum_value)\t\t\t    \\\n\t__builtin_preserve_enum_value(*(typeof(enum_type) *)enum_value, BPF_ENUMVAL_VALUE)\n\n/*\n * bpf_core_read() abstracts away bpf_probe_read_kernel() call and captures\n * offset relocation for source address using __builtin_preserve_access_index()\n * built-in, provided by Clang.\n *\n * __builtin_preserve_access_index() takes as an argument an expression of\n * taking an address of a field within struct/union. It makes compiler emit\n * a relocation, which records BTF type ID describing root struct/union and an\n * accessor string which describes exact embedded field that was used to take\n * an address. See detailed description of this relocation format and\n * semantics in comments to struct bpf_field_reloc in libbpf_internal.h.\n *\n * This relocation allows libbpf to adjust BPF instruction to use correct\n * actual field offset, based on target kernel BTF type that matches original\n * (local) BTF, used to record relocation.\n */\n#define bpf_core_read(dst, sz, src)\t\t\t\t\t    \\\n\tbpf_probe_read_kernel(dst, sz, (const void *)__builtin_preserve_access_index(src))\n\n/* NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. */\n#define bpf_core_read_user(dst, sz, src)\t\t\t\t    \\\n\tbpf_probe_read_user(dst, sz, (const void *)__builtin_preserve_access_index(src))\n/*\n * bpf_core_read_str() is a thin wrapper around bpf_probe_read_str()\n * additionally emitting BPF CO-RE field relocation for specified source\n * argument.\n */\n#define bpf_core_read_str(dst, sz, src)\t\t\t\t\t    \\\n\tbpf_probe_read_kernel_str(dst, sz, (const void *)__builtin_preserve_access_index(src))\n\n/* NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. */\n#define bpf_core_read_user_str(dst, sz, src)\t\t\t\t    \\\n\tbpf_probe_read_user_str(dst, sz, (const void *)__builtin_preserve_access_index(src))\n\n#define ___concat(a, b) a ## b\n#define ___apply(fn, n) ___concat(fn, n)\n#define ___nth(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, __11, N, ...) N\n\n/*\n * return number of provided arguments; used for switch-based variadic macro\n * definitions (see ___last, ___arrow, etc below)\n */\n#define ___narg(...) ___nth(_, ##__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)\n/*\n * return 0 if no arguments are passed, N - otherwise; used for\n * recursively-defined macros to specify termination (0) case, and generic\n * (N) case (e.g., ___read_ptrs, ___core_read)\n */\n#define ___empty(...) ___nth(_, ##__VA_ARGS__, N, N, N, N, N, N, N, N, N, N, 0)\n\n#define ___last1(x) x\n#define ___last2(a, x) x\n#define ___last3(a, b, x) x\n#define ___last4(a, b, c, x) x\n#define ___last5(a, b, c, d, x) x\n#define ___last6(a, b, c, d, e, x) x\n#define ___last7(a, b, c, d, e, f, x) x\n#define ___last8(a, b, c, d, e, f, g, x) x\n#define ___last9(a, b, c, d, e, f, g, h, x) x\n#define ___last10(a, b, c, d, e, f, g, h, i, x) x\n#define ___last(...) ___apply(___last, ___narg(__VA_ARGS__))(__VA_ARGS__)\n\n#define ___nolast2(a, _) a\n#define ___nolast3(a, b, _) a, b\n#define ___nolast4(a, b, c, _) a, b, c\n#define ___nolast5(a, b, c, d, _) a, b, c, d\n#define ___nolast6(a, b, c, d, e, _) a, b, c, d, e\n#define ___nolast7(a, b, c, d, e, f, _) a, b, c, d, e, f\n#define ___nolast8(a, b, c, d, e, f, g, _) a, b, c, d, e, f, g\n#define ___nolast9(a, b, c, d, e, f, g, h, _) a, b, c, d, e, f, g, h\n#define ___nolast10(a, b, c, d, e, f, g, h, i, _) a, b, c, d, e, f, g, h, i\n#define ___nolast(...) ___apply(___nolast, ___narg(__VA_ARGS__))(__VA_ARGS__)\n\n#define ___arrow1(a) a\n#define ___arrow2(a, b) a->b\n#define ___arrow3(a, b, c) a->b->c\n#define ___arrow4(a, b, c, d) a->b->c->d\n#define ___arrow5(a, b, c, d, e) a->b->c->d->e\n#define ___arrow6(a, b, c, d, e, f) a->b->c->d->e->f\n#define ___arrow7(a, b, c, d, e, f, g) a->b->c->d->e->f->g\n#define ___arrow8(a, b, c, d, e, f, g, h) a->b->c->d->e->f->g->h\n#define ___arrow9(a, b, c, d, e, f, g, h, i) a->b->c->d->e->f->g->h->i\n#define ___arrow10(a, b, c, d, e, f, g, h, i, j) a->b->c->d->e->f->g->h->i->j\n#define ___arrow(...) ___apply(___arrow, ___narg(__VA_ARGS__))(__VA_ARGS__)\n\n#define ___type(...) typeof(___arrow(__VA_ARGS__))\n\n#define ___read(read_fn, dst, src_type, src, accessor)\t\t\t    \\\n\tread_fn((void *)(dst), sizeof(*(dst)), &((src_type)(src))->accessor)\n\n/* \"recursively\" read a sequence of inner pointers using local __t var */\n#define ___rd_first(fn, src, a) ___read(fn, &__t, ___type(src), src, a);\n#define ___rd_last(fn, ...)\t\t\t\t\t\t    \\\n\t___read(fn, &__t, ___type(___nolast(__VA_ARGS__)), __t, ___last(__VA_ARGS__));\n#define ___rd_p1(fn, ...) const void *__t; ___rd_first(fn, __VA_ARGS__)\n#define ___rd_p2(fn, ...) ___rd_p1(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__)\n#define ___rd_p3(fn, ...) ___rd_p2(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__)\n#define ___rd_p4(fn, ...) ___rd_p3(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__)\n#define ___rd_p5(fn, ...) ___rd_p4(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__)\n#define ___rd_p6(fn, ...) ___rd_p5(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__)\n#define ___rd_p7(fn, ...) ___rd_p6(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__)\n#define ___rd_p8(fn, ...) ___rd_p7(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__)\n#define ___rd_p9(fn, ...) ___rd_p8(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__)\n#define ___read_ptrs(fn, src, ...)\t\t\t\t\t    \\\n\t___apply(___rd_p, ___narg(__VA_ARGS__))(fn, src, __VA_ARGS__)\n\n#define ___core_read0(fn, fn_ptr, dst, src, a)\t\t\t\t    \\\n\t___read(fn, dst, ___type(src), src, a);\n#define ___core_readN(fn, fn_ptr, dst, src, ...)\t\t\t    \\\n\t___read_ptrs(fn_ptr, src, ___nolast(__VA_ARGS__))\t\t    \\\n\t___read(fn, dst, ___type(src, ___nolast(__VA_ARGS__)), __t,\t    \\\n\t\t___last(__VA_ARGS__));\n#define ___core_read(fn, fn_ptr, dst, src, a, ...)\t\t\t    \\\n\t___apply(___core_read, ___empty(__VA_ARGS__))(fn, fn_ptr, dst,\t    \\\n\t\t\t\t\t\t      src, a, ##__VA_ARGS__)\n\n/*\n * BPF_CORE_READ_INTO() is a more performance-conscious variant of\n * BPF_CORE_READ(), in which final field is read into user-provided storage.\n * See BPF_CORE_READ() below for more details on general usage.\n */\n#define BPF_CORE_READ_INTO(dst, src, a, ...) ({\t\t\t\t    \\\n\t___core_read(bpf_core_read, bpf_core_read,\t\t\t    \\\n\t\t     dst, (src), a, ##__VA_ARGS__)\t\t\t    \\\n})\n\n/*\n * Variant of BPF_CORE_READ_INTO() for reading from user-space memory.\n *\n * NOTE: see comments for BPF_CORE_READ_USER() about the proper types use.\n */\n#define BPF_CORE_READ_USER_INTO(dst, src, a, ...) ({\t\t\t    \\\n\t___core_read(bpf_core_read_user, bpf_core_read_user,\t\t    \\\n\t\t     dst, (src), a, ##__VA_ARGS__)\t\t\t    \\\n})\n\n/* Non-CO-RE variant of BPF_CORE_READ_INTO() */\n#define BPF_PROBE_READ_INTO(dst, src, a, ...) ({\t\t\t    \\\n\t___core_read(bpf_probe_read, bpf_probe_read,\t\t\t    \\\n\t\t     dst, (src), a, ##__VA_ARGS__)\t\t\t    \\\n})\n\n/* Non-CO-RE variant of BPF_CORE_READ_USER_INTO().\n *\n * As no CO-RE relocations are emitted, source types can be arbitrary and are\n * not restricted to kernel types only.\n */\n#define BPF_PROBE_READ_USER_INTO(dst, src, a, ...) ({\t\t\t    \\\n\t___core_read(bpf_probe_read_user, bpf_probe_read_user,\t\t    \\\n\t\t     dst, (src), a, ##__VA_ARGS__)\t\t\t    \\\n})\n\n/*\n * BPF_CORE_READ_STR_INTO() does same \"pointer chasing\" as\n * BPF_CORE_READ() for intermediate pointers, but then executes (and returns\n * corresponding error code) bpf_core_read_str() for final string read.\n */\n#define BPF_CORE_READ_STR_INTO(dst, src, a, ...) ({\t\t\t    \\\n\t___core_read(bpf_core_read_str, bpf_core_read,\t\t\t    \\\n\t\t     dst, (src), a, ##__VA_ARGS__)\t\t\t    \\\n})\n\n/*\n * Variant of BPF_CORE_READ_STR_INTO() for reading from user-space memory.\n *\n * NOTE: see comments for BPF_CORE_READ_USER() about the proper types use.\n */\n#define BPF_CORE_READ_USER_STR_INTO(dst, src, a, ...) ({\t\t    \\\n\t___core_read(bpf_core_read_user_str, bpf_core_read_user,\t    \\\n\t\t     dst, (src), a, ##__VA_ARGS__)\t\t\t    \\\n})\n\n/* Non-CO-RE variant of BPF_CORE_READ_STR_INTO() */\n#define BPF_PROBE_READ_STR_INTO(dst, src, a, ...) ({\t\t\t    \\\n\t___core_read(bpf_probe_read_str, bpf_probe_read,\t\t    \\\n\t\t     dst, (src), a, ##__VA_ARGS__)\t\t\t    \\\n})\n\n/*\n * Non-CO-RE variant of BPF_CORE_READ_USER_STR_INTO().\n *\n * As no CO-RE relocations are emitted, source types can be arbitrary and are\n * not restricted to kernel types only.\n */\n#define BPF_PROBE_READ_USER_STR_INTO(dst, src, a, ...) ({\t\t    \\\n\t___core_read(bpf_probe_read_user_str, bpf_probe_read_user,\t    \\\n\t\t     dst, (src), a, ##__VA_ARGS__)\t\t\t    \\\n})\n\n/*\n * BPF_CORE_READ() is used to simplify BPF CO-RE relocatable read, especially\n * when there are few pointer chasing steps.\n * E.g., what in non-BPF world (or in BPF w/ BCC) would be something like:\n *\tint x = s->a.b.c->d.e->f->g;\n * can be succinctly achieved using BPF_CORE_READ as:\n *\tint x = BPF_CORE_READ(s, a.b.c, d.e, f, g);\n *\n * BPF_CORE_READ will decompose above statement into 4 bpf_core_read (BPF\n * CO-RE relocatable bpf_probe_read_kernel() wrapper) calls, logically\n * equivalent to:\n * 1. const void *__t = s->a.b.c;\n * 2. __t = __t->d.e;\n * 3. __t = __t->f;\n * 4. return __t->g;\n *\n * Equivalence is logical, because there is a heavy type casting/preservation\n * involved, as well as all the reads are happening through\n * bpf_probe_read_kernel() calls using __builtin_preserve_access_index() to\n * emit CO-RE relocations.\n *\n * N.B. Only up to 9 \"field accessors\" are supported, which should be more\n * than enough for any practical purpose.\n */\n#define BPF_CORE_READ(src, a, ...) ({\t\t\t\t\t    \\\n\t___type((src), a, ##__VA_ARGS__) __r;\t\t\t\t    \\\n\tBPF_CORE_READ_INTO(&__r, (src), a, ##__VA_ARGS__);\t\t    \\\n\t__r;\t\t\t\t\t\t\t\t    \\\n})\n\n/*\n * Variant of BPF_CORE_READ() for reading from user-space memory.\n *\n * NOTE: all the source types involved are still *kernel types* and need to\n * exist in kernel (or kernel module) BTF, otherwise CO-RE relocation will\n * fail. Custom user types are not relocatable with CO-RE.\n * The typical situation in which BPF_CORE_READ_USER() might be used is to\n * read kernel UAPI types from the user-space memory passed in as a syscall\n * input argument.\n */\n#define BPF_CORE_READ_USER(src, a, ...) ({\t\t\t\t    \\\n\t___type((src), a, ##__VA_ARGS__) __r;\t\t\t\t    \\\n\tBPF_CORE_READ_USER_INTO(&__r, (src), a, ##__VA_ARGS__);\t\t    \\\n\t__r;\t\t\t\t\t\t\t\t    \\\n})\n\n/* Non-CO-RE variant of BPF_CORE_READ() */\n#define BPF_PROBE_READ(src, a, ...) ({\t\t\t\t\t    \\\n\t___type((src), a, ##__VA_ARGS__) __r;\t\t\t\t    \\\n\tBPF_PROBE_READ_INTO(&__r, (src), a, ##__VA_ARGS__);\t\t    \\\n\t__r;\t\t\t\t\t\t\t\t    \\\n})\n\n/*\n * Non-CO-RE variant of BPF_CORE_READ_USER().\n *\n * As no CO-RE relocations are emitted, source types can be arbitrary and are\n * not restricted to kernel types only.\n */\n#define BPF_PROBE_READ_USER(src, a, ...) ({\t\t\t\t    \\\n\t___type((src), a, ##__VA_ARGS__) __r;\t\t\t\t    \\\n\tBPF_PROBE_READ_USER_INTO(&__r, (src), a, ##__VA_ARGS__);\t    \\\n\t__r;\t\t\t\t\t\t\t\t    \\\n})\n\n#endif\n\n"
  },
  {
    "path": "btf/testdata/fuzz/FuzzExtInfo/50a33736610b4a0945179db4c8a88e8247b05fbb25f50ed81e5393baf29bc5bc",
    "content": "go test fuzz v1\n[]byte(\"\\x9f\\xeb\\x01\\x00\\x1c\\x00\\x00\\x00\\x00\\x00\\x00\\x00000\\x10\\x00\\x00\\x00\\x000000\\xf3\\xff\\xff\\xff0\\x00\\x00\\x00\")\n[]byte(\"0\")\n"
  },
  {
    "path": "btf/testdata/fuzz/FuzzExtInfo/72534f53bd90cb52a017013499b11511535c1295bf0e22f856148c02454c323e",
    "content": "go test fuzz v1\n[]byte(\"\\x9f\\xeb\\x01\\x00\\x18\\x00\\x00\\x00\\x00\\x00\\x00\\x000000000000000\\x00\\x00\\x000000\")\n[]byte(\"0\")\n"
  },
  {
    "path": "btf/testdata/fuzz/FuzzExtInfo/a87a26efa64ed50b598ae8e333301d57d5f234527730f042d68ccc736e90c9fa",
    "content": "go test fuzz v1\n[]byte(\"\\x9f\\xeb\\x01\\x00\\x1c\\x00\\x00\\x00\\x00\\x00\\x00\\x000000\\xe8\\xff\\xff\\xff000000000\\x00\\x00\\x00\")\n[]byte(\"0\")\n"
  },
  {
    "path": "btf/testdata/relocs.c",
    "content": "#include \"../../testdata/common.h\"\n#include \"bpf_core_read.h\"\n\nenum e {\n\tZERO = 0,\n\tONE,\n\tTWO,\n};\n\nenum e64 {\n\tLARGE = 0x1ffffffff,\n};\n\ntypedef enum e e_t;\n\nstruct s {\n\tint _1;\n\tchar _2;\n\tunsigned int _3;\n};\n\ntypedef struct s s_t;\n\nunion u {\n\tint *_1;\n\tchar *_2;\n\tunsigned int *_3;\n};\n\ntypedef union u u_t;\n\n#define local_id_not_zero(expr) \\\n\t({ \\\n\t\tif (bpf_core_type_id_local(expr) == 0) { \\\n\t\t\treturn __LINE__; \\\n\t\t} \\\n\t})\n\n#define target_and_local_id_dont_match(expr) \\\n\t({ \\\n\t\tif (bpf_core_type_id_kernel(expr) == bpf_core_type_id_local(expr)) { \\\n\t\t\treturn __LINE__; \\\n\t\t} \\\n\t})\n\n__section(\"socket/type_ids\") int type_ids() {\n\tlocal_id_not_zero(int);\n\tlocal_id_not_zero(struct { int frob; });\n\tlocal_id_not_zero(enum {FRAP});\n\tlocal_id_not_zero(union { char bar; });\n\n\tlocal_id_not_zero(struct s);\n\tlocal_id_not_zero(s_t);\n\tlocal_id_not_zero(const s_t);\n\tlocal_id_not_zero(volatile s_t);\n\tlocal_id_not_zero(enum e);\n\tlocal_id_not_zero(e_t);\n\tlocal_id_not_zero(const e_t);\n\tlocal_id_not_zero(volatile e_t);\n\tlocal_id_not_zero(union u);\n\tlocal_id_not_zero(u_t);\n\tlocal_id_not_zero(const u_t);\n\tlocal_id_not_zero(volatile u_t);\n\n\t// In this context, target is the BTF generated by clang. local is\n\t// generated on the fly by the library. There is a low chance that\n\t// the order on both is the same, so we assert this to make sure that\n\t// CO-RE uses the IDs from the dynamic BTF.\n\t// Qualifiers on types crash clang.\n\ttarget_and_local_id_dont_match(struct s);\n\ttarget_and_local_id_dont_match(s_t);\n\t// target_and_local_id_dont_match(const s_t);\n\t// target_and_local_id_dont_match(volatile s_t);\n\ttarget_and_local_id_dont_match(enum e);\n\ttarget_and_local_id_dont_match(e_t);\n\t// target_and_local_id_dont_match(const e_t);\n\t// target_and_local_id_dont_match(volatile e_t);\n\ttarget_and_local_id_dont_match(union u);\n\ttarget_and_local_id_dont_match(u_t);\n\t// target_and_local_id_dont_match(const u_t);\n\t// target_and_local_id_dont_match(volatile u_t);\n\n\treturn 0;\n}\n\n#define type_exists(expr) \\\n\t({ \\\n\t\tif (!bpf_core_type_exists(expr)) { \\\n\t\t\treturn __LINE__; \\\n\t\t} \\\n\t})\n\n#define type_size_matches(expr) \\\n\t({ \\\n\t\tif (bpf_core_type_size(expr) != sizeof(expr)) { \\\n\t\t\treturn __LINE__; \\\n\t\t} \\\n\t})\n\n#define type_matches(expr) \\\n\t({ \\\n\t\tif (!bpf_core_type_matches(expr)) { \\\n\t\t\treturn __LINE__; \\\n\t\t} \\\n\t})\n\n__section(\"socket/types\") int types() {\n\ttype_exists(struct s);\n\ttype_exists(s_t);\n\ttype_exists(const s_t);\n\ttype_exists(volatile s_t);\n\ttype_exists(enum e);\n\ttype_exists(e_t);\n\ttype_exists(const e_t);\n\ttype_exists(volatile e_t);\n\ttype_exists(union u);\n\ttype_exists(u_t);\n\ttype_exists(const u_t);\n\ttype_exists(volatile u_t);\n\t// TODO: Check non-existence.\n\n\ttype_size_matches(struct s);\n\ttype_size_matches(s_t);\n\ttype_size_matches(const s_t);\n\ttype_size_matches(volatile s_t);\n\ttype_size_matches(enum e);\n\ttype_size_matches(e_t);\n\ttype_size_matches(const e_t);\n\ttype_size_matches(volatile e_t);\n\ttype_size_matches(union u);\n\ttype_size_matches(u_t);\n\ttype_size_matches(const u_t);\n\ttype_size_matches(volatile u_t);\n\n\ttype_matches(struct s);\n\ttype_matches(s_t);\n\ttype_matches(const s_t);\n\ttype_matches(volatile s_t);\n\ttype_matches(enum e);\n\ttype_matches(e_t);\n\ttype_matches(const e_t);\n\ttype_matches(volatile e_t);\n\ttype_matches(union u);\n\ttype_matches(u_t);\n\ttype_matches(const u_t);\n\ttype_matches(volatile u_t);\n\n\treturn 0;\n}\n\n#define enum_value_exists(t, v) \\\n\t({ \\\n\t\tif (!bpf_core_enum_value_exists(t, v)) { \\\n\t\t\treturn __LINE__; \\\n\t\t} \\\n\t})\n\n#define enum_value_matches(t, v) \\\n\t({ \\\n\t\tif (v != bpf_core_enum_value(t, v)) { \\\n\t\t\treturn __LINE__; \\\n\t\t} \\\n\t})\n\n__section(\"socket/enums\") int enums() {\n\tenum_value_exists(enum e, ONE);\n\tenum_value_exists(volatile enum e, ONE);\n\tenum_value_exists(const enum e, ONE);\n\tenum_value_exists(e_t, TWO);\n\tenum_value_exists(enum e64, LARGE);\n\t// TODO: Check non-existence.\n\n\tenum_value_matches(enum e, ZERO);\n\tenum_value_matches(enum e, TWO);\n\tenum_value_matches(e_t, ONE);\n\tenum_value_matches(volatile e_t, ONE);\n\tenum_value_matches(const e_t, ONE);\n\tenum_value_matches(enum e64, LARGE);\n\n\treturn 0;\n}\n\n#define field_exists(f) \\\n\t({ \\\n\t\tif (!bpf_core_field_exists(f)) { \\\n\t\t\treturn __LINE__; \\\n\t\t} \\\n\t})\n\n#define field_size_matches(f) \\\n\t({ \\\n\t\tif (sizeof(f) != bpf_core_field_size(f)) { \\\n\t\t\treturn __LINE__; \\\n\t\t} \\\n\t})\n\n#define field_offset_matches(t, f) \\\n\t({ \\\n\t\tif (__builtin_offsetof(t, f) != __builtin_preserve_field_info(((typeof(t) *)0)->f, BPF_FIELD_BYTE_OFFSET)) { \\\n\t\t\treturn __LINE__; \\\n\t\t} \\\n\t})\n\n#define field_is_signed(f) \\\n\t({ \\\n\t\tif (!__builtin_preserve_field_info(f, BPF_FIELD_SIGNED)) { \\\n\t\t\treturn __LINE__; \\\n\t\t} \\\n\t})\n\n#define field_is_unsigned(f) \\\n\t({ \\\n\t\tif (__builtin_preserve_field_info(f, BPF_FIELD_SIGNED)) { \\\n\t\t\treturn __LINE__; \\\n\t\t} \\\n\t})\n\n__section(\"socket/fields\") int fields() {\n\tfield_exists((struct s){}._1);\n\tfield_exists((s_t){}._2);\n\tfield_exists((union u){}._1);\n\tfield_exists((u_t){}._2);\n\n\tfield_is_signed((struct s){}._1);\n\tfield_is_unsigned((struct s){}._3);\n\t// unions crash clang-14.\n\t// field_is_signed((union u){}._1);\n\t// field_is_unsigned((union u){}._3);\n\n\tfield_size_matches((struct s){}._1);\n\tfield_size_matches((s_t){}._2);\n\tfield_size_matches((union u){}._1);\n\tfield_size_matches((u_t){}._2);\n\n\tfield_offset_matches(struct s, _1);\n\tfield_offset_matches(s_t, _2);\n\tfield_offset_matches(union u, _1);\n\tfield_offset_matches(u_t, _2);\n\n\tstruct t {\n\t\tunion {\n\t\t\ts_t s[10];\n\t\t};\n\t\tstruct {\n\t\t\tunion u u;\n\t\t};\n\t} bar, *barp = &bar;\n\n\tfield_exists(bar.s[2]._1);\n\tfield_exists(bar.s[1]._2);\n\tfield_exists(bar.u._1);\n\tfield_exists(bar.u._2);\n\tfield_exists(barp[1].u._2);\n\n\tfield_is_signed(bar.s[2]._1);\n\tfield_is_unsigned(bar.s[2]._3);\n\t// unions crash clang-14.\n\t// field_is_signed(bar.u._1);\n\t// field_is_signed(bar.u._3);\n\n\tfield_size_matches(bar.s[2]._1);\n\tfield_size_matches(bar.s[1]._2);\n\tfield_size_matches(bar.u._1);\n\tfield_size_matches(bar.u._2);\n\tfield_size_matches(barp[1].u._2);\n\n\tfield_offset_matches(struct t, s[2]._1);\n\tfield_offset_matches(struct t, s[1]._2);\n\tfield_offset_matches(struct t, u._1);\n\tfield_offset_matches(struct t, u._2);\n\n\treturn 0;\n}\n\nstruct ambiguous {\n\tint _1;\n\tchar _2;\n};\n\nstruct ambiguous___flavour {\n\tchar _1;\n\tint _2;\n};\n\n__section(\"socket/err_ambiguous\") int err_ambiguous() {\n\treturn bpf_core_type_id_kernel(struct ambiguous);\n}\n\n__section(\"socket/err_ambiguous_flavour\") int err_ambiguous_flavour() {\n\treturn bpf_core_type_id_kernel(struct ambiguous___flavour);\n}\n"
  },
  {
    "path": "btf/testdata/relocs_enum.c",
    "content": "#include \"bpf_core_read.h\"\n\nenum cgroup_subsys_id {\n\tcpuset_cgrp_id,\n\tcpuset_cgrp_id_lublub,\n\tCGROUP_SUBSYS_COUNT,\n};\n\n#define __section(NAME) __attribute__((section(NAME), used))\n\n__section(\"socket/core_ld64imm\") int core_ld64imm() {\n\tif (bpf_core_enum_value_exists(enum cgroup_subsys_id, cpuset_cgrp_id_lublub)) {\n\t\t__attribute__((unused)) const volatile int val = bpf_core_enum_value(enum cgroup_subsys_id, cpuset_cgrp_id_lublub);\n\t}\n\treturn 0;\n}\n"
  },
  {
    "path": "btf/testdata/relocs_read.c",
    "content": "#include \"../../testdata/common.h\"\n#include \"bpf_core_read.h\"\n\n// Struct with the members declared in the wrong order. Accesses need\n// a successful CO-RE relocation against the type in relocs_read_tgt.c\n// for the test below to pass.\nstruct s {\n\tchar b;\n\tchar a;\n};\n\ntypedef unsigned char u8;\ntypedef unsigned short u16;\ntypedef unsigned int u32;\ntypedef unsigned long u64;\n\n// Struct with bitfields.\nstruct bits {\n\tint x;\n\tu8 a : 4, b : 2;\n\tu16 c : 1;\n\tunsigned int d : 2;\n\tenum { ZERO = 0, ONE = 1 } e : 1;\n\tu64 f : 16, g : 30;\n};\n\nstruct nonexist {\n\tint non_exist;\n};\n\nenum nonexist_enum { NON_EXIST = 1 };\n\n// Perform a read from a subprog to ensure CO-RE relocations\n// occurring there are tracked and executed in the final linked program.\n__attribute__((noinline)) int read_subprog() {\n\tstruct s foo = {\n\t\t.a = 0,\n\t\t.b = 1,\n\t};\n\n\tif (core_access(foo.a) == 0)\n\t\treturn __LINE__;\n\n\tif (core_access(foo.b) == 1)\n\t\treturn __LINE__;\n\n\tstruct bits bar;\n\tchar *p = (char *)&bar;\n\t/* Target:\n\t * [4] STRUCT 'bits' size=8 vlen=7\n\t * 'b' type_id=5 bits_offset=0 bitfield_size=2\n\t * 'a' type_id=5 bits_offset=2 bitfield_size=4\n\t * 'd' type_id=7 bits_offset=6 bitfield_size=2\n\t * 'c' type_id=9 bits_offset=8 bitfield_size=1\n\t * 'e' type_id=11 bits_offset=9 bitfield_size=1\n\t * 'f' type_id=9 bits_offset=16\n\t * 'g' type_id=12 bits_offset=32 bitfield_size=30\n\t */\n\t*p++ = 0xff; // a, b, d\n\t*p++ = 0x00; // c, e\n\t*p++ = 0x56; // f\n\t*p++ = 0x56; // f\n#ifdef __BIG_ENDIAN__\n\t*p++ = 0x55; // g\n\t*p++ = 0x44; // g\n\t*p++ = 0x33; // g\n\t*p++ = 0x22; // g\n#else\n\t*p++ = 0x22; // g\n\t*p++ = 0x33; // g\n\t*p++ = 0x44; // g\n\t*p++ = 0x55; // g\n#endif\n\n\tif (BPF_CORE_READ_BITFIELD(&bar, a) != (1 << 4) - 1)\n\t\treturn __LINE__;\n\n\tif (BPF_CORE_READ_BITFIELD(&bar, b) != (1 << 2) - 1)\n\t\treturn __LINE__;\n\n\tif (BPF_CORE_READ_BITFIELD(&bar, d) != (1 << 2) - 1)\n\t\treturn __LINE__;\n\n\tif (BPF_CORE_READ_BITFIELD(&bar, c) != 0)\n\t\treturn __LINE__;\n\n\tif (BPF_CORE_READ_BITFIELD(&bar, e) != 0)\n\t\treturn __LINE__;\n\n\tif (BPF_CORE_READ_BITFIELD(&bar, f) != 0x5656)\n\t\treturn __LINE__;\n\n\tif (BPF_CORE_READ_BITFIELD(&bar, g) != 0x15443322)\n\t\treturn __LINE__;\n\n\tif (bpf_core_type_exists(struct nonexist) != 0)\n\t\treturn __LINE__;\n\n\tif (bpf_core_field_exists(((struct nonexist *)0)->non_exist) != 0)\n\t\treturn __LINE__;\n\n\tif (bpf_core_enum_value_exists(enum nonexist_enum, NON_EXIST) != 0)\n\t\treturn __LINE__;\n\n\treturn 0;\n}\n\n__section(\"socket\") int reads() {\n\tint ret = read_subprog();\n\tif (ret)\n\t\treturn ret;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "btf/testdata/relocs_read_tgt.c",
    "content": "/*\n\tThis file exists to emit ELFs with specific BTF types to use as target BTF\n\tin tests. It can be made redundant when btf.Spec can be handcrafted and\n\tpassed as a CO-RE target in the future.\n*/\n\nstruct s {\n\tchar a;\n\tchar b;\n};\n\nstruct s *unused_s __attribute__((unused));\n\ntypedef unsigned int my_u32;\ntypedef unsigned char u8;\ntypedef unsigned short u16;\ntypedef unsigned int u32;\ntypedef unsigned long u64;\n\nstruct bits {\n\t/*int x;*/\n\tu8 b : 2, a : 4; /* a was before b */\n\tmy_u32 d : 2; /* was 'unsigned int' */\n\tu16 c : 1; /* was before d */\n\tenum { ZERO = 0, ONE = 1 } e : 1;\n\tu16 f; /* was: u64 f:16 */\n\tu32 g : 30; /* was: u64 g:30 */\n};\n\nstruct bits *unused_bits __attribute__((unused));\n"
  },
  {
    "path": "btf/testdata/tags.c",
    "content": "#include \"../../testdata/common.h\"\n\n#define tagA __attribute__((btf_decl_tag(\"a\")))\n#define tagB __attribute__((btf_decl_tag(\"b\")))\n#define tagC __attribute__((btf_decl_tag(\"c\")))\n#define tagD __attribute__((btf_decl_tag(\"d\")))\n#define tagE __attribute__((btf_decl_tag(\"e\")))\n\nstruct s {\n\tchar tagA foo;\n\tchar tagB bar;\n} tagC;\n\nunion u {\n\tchar tagA foo;\n\tchar tagB bar;\n} tagC;\n\ntypedef tagB char td;\n\nstruct s tagD s1;\nunion u tagE u1;\ntd tagA t1;\n\nint tagA tagB fwdDecl(char tagC x, char tagD y);\n\nint tagE normalDecl1(char tagB x, char tagC y) {\n\treturn fwdDecl(x, y);\n}\n\nint tagE normalDecl2(char tagB x, char tagC y) {\n\treturn fwdDecl(x, y);\n}\n\n__section(\"syscall\") int prog(char *ctx) {\n\treturn normalDecl1(ctx[0], ctx[1]) + normalDecl2(ctx[2], ctx[3]);\n}\n"
  },
  {
    "path": "btf/traversal.go",
    "content": "package btf\n\nimport (\n\t\"fmt\"\n\t\"iter\"\n)\n\n// Functions to traverse a cyclic graph of types. The below was very useful:\n// https://eli.thegreenplace.net/2015/directed-graph-traversal-orderings-and-applications-to-data-flow-analysis/#post-order-and-reverse-post-order\n\n// postorder yields all types reachable from root in post order.\nfunc postorder(root Type, visited map[Type]struct{}) iter.Seq[Type] {\n\treturn func(yield func(Type) bool) {\n\t\tvisitInPostorder(root, visited, yield)\n\t}\n}\n\n// visitInPostorder is a separate function to avoid arguments escaping\n// to the heap. Don't change the setup without re-running the benchmarks.\nfunc visitInPostorder(root Type, visited map[Type]struct{}, yield func(typ Type) bool) bool {\n\tif _, ok := visited[root]; ok {\n\t\treturn true\n\t}\n\tif visited == nil {\n\t\tvisited = make(map[Type]struct{})\n\t}\n\tvisited[root] = struct{}{}\n\n\tfor child := range children(root) {\n\t\tif !visitInPostorder(*child, visited, yield) {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn yield(root)\n}\n\n// children yields all direct descendants of typ.\nfunc children(typ Type) iter.Seq[*Type] {\n\treturn func(yield func(*Type) bool) {\n\t\t// Explicitly type switch on the most common types to allow the inliner to\n\t\t// do its work. This avoids allocating intermediate slices from walk() on\n\t\t// the heap.\n\t\tvar tags []string\n\t\tswitch v := typ.(type) {\n\t\tcase *Void, *Int, *Enum, *Fwd, *Float, *declTag:\n\t\t\t// No children to traverse.\n\t\t\t// declTags is declared as a leaf type since it's parsed into .Tags fields of other types\n\t\t\t// during unmarshaling.\n\t\tcase *Pointer:\n\t\t\tif !yield(&v.Target) {\n\t\t\t\treturn\n\t\t\t}\n\t\tcase *Array:\n\t\t\tif !yield(&v.Index) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif !yield(&v.Type) {\n\t\t\t\treturn\n\t\t\t}\n\t\tcase *Struct:\n\t\t\tfor i := range v.Members {\n\t\t\t\tif !yield(&v.Members[i].Type) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tfor _, t := range v.Members[i].Tags {\n\t\t\t\t\tvar tag Type = &declTag{v, t, i}\n\t\t\t\t\tif !yield(&tag) {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\ttags = v.Tags\n\t\tcase *Union:\n\t\t\tfor i := range v.Members {\n\t\t\t\tif !yield(&v.Members[i].Type) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tfor _, t := range v.Members[i].Tags {\n\t\t\t\t\tvar tag Type = &declTag{v, t, i}\n\t\t\t\t\tif !yield(&tag) {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\ttags = v.Tags\n\t\tcase *Typedef:\n\t\t\tif !yield(&v.Type) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttags = v.Tags\n\t\tcase *Volatile:\n\t\t\tif !yield(&v.Type) {\n\t\t\t\treturn\n\t\t\t}\n\t\tcase *Const:\n\t\t\tif !yield(&v.Type) {\n\t\t\t\treturn\n\t\t\t}\n\t\tcase *Restrict:\n\t\t\tif !yield(&v.Type) {\n\t\t\t\treturn\n\t\t\t}\n\t\tcase *Func:\n\t\t\tif !yield(&v.Type) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif fp, ok := v.Type.(*FuncProto); ok {\n\t\t\t\tfor i := range fp.Params {\n\t\t\t\t\tif len(v.ParamTags) <= i {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tfor _, t := range v.ParamTags[i] {\n\t\t\t\t\t\tvar tag Type = &declTag{v, t, i}\n\t\t\t\t\t\tif !yield(&tag) {\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\ttags = v.Tags\n\t\tcase *FuncProto:\n\t\t\tif !yield(&v.Return) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfor i := range v.Params {\n\t\t\t\tif !yield(&v.Params[i].Type) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\tcase *Var:\n\t\t\tif !yield(&v.Type) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttags = v.Tags\n\t\tcase *Datasec:\n\t\t\tfor i := range v.Vars {\n\t\t\t\tif !yield(&v.Vars[i].Type) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\tcase *TypeTag:\n\t\t\tif !yield(&v.Type) {\n\t\t\t\treturn\n\t\t\t}\n\t\tcase *cycle:\n\t\t\t// cycle has children, but we ignore them deliberately.\n\t\tdefault:\n\t\t\tpanic(fmt.Sprintf(\"don't know how to walk Type %T\", v))\n\t\t}\n\n\t\tfor _, t := range tags {\n\t\t\tvar tag Type = &declTag{typ, t, -1}\n\t\t\tif !yield(&tag) {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "btf/traversal_test.go",
    "content": "package btf\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestPostorderTraversal(t *testing.T) {\n\tptr := newCyclicalType(2).(*Pointer)\n\tcst := ptr.Target.(*Const)\n\tstr := cst.Type.(*Struct)\n\n\tt.Logf(\"%3v\", ptr)\n\tpending := []Type{str, cst, ptr}\n\tfor typ := range postorder(ptr, nil) {\n\t\tqt.Assert(t, qt.Equals(typ, pending[0]))\n\t\tpending = pending[1:]\n\t}\n\tqt.Assert(t, qt.HasLen(pending, 0))\n\n\ti := &Int{Name: \"foo\"}\n\t// i appears twice at the same nesting depth.\n\tarr := &Array{Index: i, Type: i}\n\tseen := make(map[Type]bool)\n\tfor typ := range postorder(arr, nil) {\n\t\tqt.Assert(t, qt.IsFalse(seen[typ]))\n\t\tseen[typ] = true\n\t}\n\tqt.Assert(t, qt.IsTrue(seen[arr]))\n\tqt.Assert(t, qt.IsTrue(seen[i]))\n}\n\nfunc TestPostorderTraversalVmlinux(t *testing.T) {\n\tspec := vmlinuxTestdataSpec(t)\n\n\ttyp, err := spec.AnyTypeByName(\"gov_update_cpu_data\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tfor _, typ := range []Type{typ} {\n\t\tt.Run(fmt.Sprintf(\"%s\", typ), func(t *testing.T) {\n\t\t\tseen := make(map[Type]bool)\n\t\t\tvar last Type\n\t\t\tfor typ := range postorder(typ, nil) {\n\t\t\t\tif seen[typ] {\n\t\t\t\t\tt.Fatalf(\"%s visited twice\", typ)\n\t\t\t\t}\n\t\t\t\tseen[typ] = true\n\t\t\t\tlast = typ\n\t\t\t}\n\t\t\tif last != typ {\n\t\t\t\tt.Fatalf(\"Expected %s got %s as last type\", typ, last)\n\t\t\t}\n\n\t\t\tfor child := range children(typ) {\n\t\t\t\tqt.Check(t, qt.IsTrue(seen[*child]), qt.Commentf(\"missing child %s\", *child))\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestChildren(t *testing.T) {\n\tfor _, test := range []struct {\n\t\ttyp   Type\n\t\tcount int\n\t}{\n\t\t{&Int{}, 0},\n\t\t{&Const{&Int{}}, 1},\n\t\t{&Array{Index: &Int{}, Type: &Int{}}, 2},\n\t} {\n\t\tt.Run(fmt.Sprint(test.typ), func(t *testing.T) {\n\t\t\tvar count int\n\t\t\tallocs := testing.AllocsPerRun(1, func() {\n\t\t\t\tcount = 0\n\t\t\t\tfor range children(test.typ) {\n\t\t\t\t\tcount++\n\t\t\t\t}\n\t\t\t})\n\t\t\tqt.Assert(t, qt.Equals(count, test.count))\n\t\t\tqt.Assert(t, qt.Equals(allocs, 0))\n\t\t})\n\t}\n}\n\nfunc BenchmarkPostorderTraversal(b *testing.B) {\n\tspec := vmlinuxTestdataSpec(b)\n\n\tvar fn *Func\n\terr := spec.TypeByName(\"gov_update_cpu_data\", &fn)\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\tfor _, test := range []struct {\n\t\tname string\n\t\ttyp  Type\n\t}{\n\t\t{\"single type\", &Int{}},\n\t\t{\"cycle(1)\", newCyclicalType(1)},\n\t\t{\"cycle(10)\", newCyclicalType(10)},\n\t\t{\"gov_update_cpu_data\", fn},\n\t} {\n\t\tb.Run(test.name, func(b *testing.B) {\n\t\t\tb.ReportAllocs()\n\t\t\tfor b.Loop() {\n\t\t\t\tfor range postorder(test.typ, nil) {\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "btf/types.go",
    "content": "package btf\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"strings\"\n\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n)\n\n// Mirrors MAX_RESOLVE_DEPTH in libbpf.\n// https://github.com/libbpf/libbpf/blob/e26b84dc330c9644c07428c271ab491b0f01f4e1/src/btf.c#L761\nconst maxResolveDepth = 32\n\n// TypeID identifies a type in a BTF section.\ntype TypeID = sys.TypeID\n\n// Type represents a type described by BTF.\n//\n// A Type has three properties where compared to other Types.\n//\n// Identity: follows the [Go specification], two Types are considered identical\n// if they have the same concrete type and the same dynamic value, aka they point\n// at the same location in memory. This means that the following Types are\n// considered distinct even though they have the same \"shape\".\n//\n//\ta := &Int{Size: 1}\n//\tb := &Int{Size: 1}\n//\ta != b\n//\n// Equivalence: two Types are considered equivalent if they have the same shape\n// and thus are functionally interchangeable, even if they are located at different\n// memory addresses. The above two Int types are equivalent.\n//\n// Compatibility: two Types are considered compatible according to the rules of CO-RE\n// see [coreAreTypesCompatible] for details. This is a non-commutative property,\n// so A may be compatible with B, but B not compatible with A.\n//\n// [Go specification]: https://go.dev/ref/spec#Comparison_operators\ntype Type interface {\n\t// Type can be formatted using the %s and %v verbs. %s outputs only the\n\t// identity of the type, without any detail. %v outputs additional detail.\n\t//\n\t// Use the '+' flag to include the address of the type.\n\t//\n\t// Use the width to specify how many levels of detail to output, for example\n\t// %1v will output detail for the root type and a short description of its\n\t// children. %2v would output details of the root type and its children\n\t// as well as a short description of the grandchildren.\n\tfmt.Formatter\n\n\t// Name of the type, empty for anonymous types and types that cannot\n\t// carry a name, like Void and Pointer.\n\tTypeName() string\n\n\t// Make a copy of the type, without copying Type members.\n\tcopy() Type\n\n\t// New implementations must update children, deduper.typeHash, and typesEquivalent.\n}\n\nvar (\n\t_ Type = (*Int)(nil)\n\t_ Type = (*Struct)(nil)\n\t_ Type = (*Union)(nil)\n\t_ Type = (*Enum)(nil)\n\t_ Type = (*Fwd)(nil)\n\t_ Type = (*Func)(nil)\n\t_ Type = (*Typedef)(nil)\n\t_ Type = (*Var)(nil)\n\t_ Type = (*Datasec)(nil)\n\t_ Type = (*Float)(nil)\n\t_ Type = (*declTag)(nil)\n\t_ Type = (*TypeTag)(nil)\n\t_ Type = (*cycle)(nil)\n)\n\n// Void is the unit type of BTF.\ntype Void struct{}\n\nfunc (v *Void) Format(fs fmt.State, verb rune) { formatType(fs, verb, v) }\nfunc (v *Void) TypeName() string               { return \"\" }\nfunc (v *Void) size() uint32                   { return 0 }\nfunc (v *Void) copy() Type                     { return (*Void)(nil) }\n\ntype IntEncoding byte\n\n// Valid IntEncodings.\n//\n// These may look like they are flags, but they aren't.\nconst (\n\tUnsigned IntEncoding = 0\n\tSigned   IntEncoding = 1\n\tChar     IntEncoding = 2\n\tBool     IntEncoding = 4\n)\n\nfunc (ie IntEncoding) String() string {\n\tswitch ie {\n\tcase Char:\n\t\t// NB: There is no way to determine signedness for char.\n\t\treturn \"char\"\n\tcase Bool:\n\t\treturn \"bool\"\n\tcase Signed:\n\t\treturn \"signed\"\n\tcase Unsigned:\n\t\treturn \"unsigned\"\n\tdefault:\n\t\treturn fmt.Sprintf(\"IntEncoding(%d)\", byte(ie))\n\t}\n}\n\n// Int is an integer of a given length.\n//\n// See https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-int\ntype Int struct {\n\tName string\n\n\t// The size of the integer in bytes.\n\tSize     uint32\n\tEncoding IntEncoding\n}\n\nfunc (i *Int) Format(fs fmt.State, verb rune) {\n\tformatType(fs, verb, i, i.Encoding, \"size=\", i.Size)\n}\n\nfunc (i *Int) TypeName() string { return i.Name }\nfunc (i *Int) size() uint32     { return i.Size }\nfunc (i *Int) copy() Type {\n\tcpy := *i\n\treturn &cpy\n}\n\n// Pointer is a pointer to another type.\ntype Pointer struct {\n\tTarget Type\n}\n\nfunc (p *Pointer) Format(fs fmt.State, verb rune) {\n\tformatType(fs, verb, p, \"target=\", p.Target)\n}\n\nfunc (p *Pointer) TypeName() string { return \"\" }\nfunc (p *Pointer) size() uint32     { return 8 }\nfunc (p *Pointer) copy() Type {\n\tcpy := *p\n\treturn &cpy\n}\n\n// Array is an array with a fixed number of elements.\ntype Array struct {\n\tIndex  Type\n\tType   Type\n\tNelems uint32\n}\n\nfunc (arr *Array) Format(fs fmt.State, verb rune) {\n\tformatType(fs, verb, arr, \"index=\", arr.Index, \"type=\", arr.Type, \"n=\", arr.Nelems)\n}\n\nfunc (arr *Array) TypeName() string { return \"\" }\n\nfunc (arr *Array) copy() Type {\n\tcpy := *arr\n\treturn &cpy\n}\n\n// Struct is a compound type of consecutive members.\ntype Struct struct {\n\tName string\n\t// The size of the struct including padding, in bytes\n\tSize    uint32\n\tMembers []Member\n\tTags    []string\n}\n\n// Format supports the width option to %v to limit or extend the number of\n// struct member names printed (default 5).\n//\n// For example, %1v will print only the first member name followed by '...':\n//\n//\tStruct:\"struct\"[fields=3 fieldNames=[a ...]]\nfunc (s *Struct) Format(fs fmt.State, verb rune) {\n\tmax, _ := fs.Width()\n\tformatType(fs, verb, s, \"fields=\", len(s.Members), \"fieldNames=\", memberNames(s.Members, max))\n}\n\nfunc (s *Struct) TypeName() string { return s.Name }\n\nfunc (s *Struct) size() uint32 { return s.Size }\n\nfunc (s *Struct) copy() Type {\n\tcpy := *s\n\tcpy.Members = copyMembers(s.Members)\n\tcpy.Tags = copyTags(cpy.Tags)\n\treturn &cpy\n}\n\nfunc (s *Struct) members() []Member {\n\treturn s.Members\n}\n\n// Union is a compound type where members occupy the same memory.\ntype Union struct {\n\tName string\n\t// The size of the union including padding, in bytes.\n\tSize    uint32\n\tMembers []Member\n\tTags    []string\n}\n\n// Format supports the width option to %v to limit or extend the number of\n// union member names printed (default 5).\n//\n// For example, %1v will print only the first member name followed by '...':\n//\n//\tUnion:\"union\"[fields=3 fieldNames=[a ...]]\nfunc (u *Union) Format(fs fmt.State, verb rune) {\n\tmax, _ := fs.Width()\n\tformatType(fs, verb, u, \"fields=\", len(u.Members), \"fieldNames=\", memberNames(u.Members, max))\n}\n\nfunc (u *Union) TypeName() string { return u.Name }\n\nfunc (u *Union) size() uint32 { return u.Size }\n\nfunc (u *Union) copy() Type {\n\tcpy := *u\n\tcpy.Members = copyMembers(u.Members)\n\tcpy.Tags = copyTags(cpy.Tags)\n\treturn &cpy\n}\n\nfunc (u *Union) members() []Member {\n\treturn u.Members\n}\n\n// memberNames returns the names of members, or its index in the list of members\n// if the name is empty.\n//\n// Returns up to max entries (default 5), followed by '...'.\nfunc memberNames(members []Member, max int) []string {\n\tif max <= 0 {\n\t\tmax = 5\n\t}\n\tnames := make([]string, min(len(members), max+1))\n\tfor i, m := range members {\n\t\tif i >= max {\n\t\t\tnames[i] = \"...\"\n\t\t\tbreak\n\t\t}\n\t\tif m.Name == \"\" {\n\t\t\tnames[i] = fmt.Sprintf(\"<%d>\", i)\n\t\t\tcontinue\n\t\t}\n\t\tnames[i] = m.Name\n\t}\n\treturn names\n}\n\nfunc copyMembers(orig []Member) []Member {\n\tcpy := make([]Member, len(orig))\n\tcopy(cpy, orig)\n\tfor i, member := range cpy {\n\t\tcpy[i].Tags = copyTags(member.Tags)\n\t}\n\treturn cpy\n}\n\nfunc copyTags(orig []string) []string {\n\tif orig == nil { // preserve nil vs zero-len slice distinction\n\t\treturn nil\n\t}\n\tcpy := make([]string, len(orig))\n\tcopy(cpy, orig)\n\treturn cpy\n}\n\ntype composite interface {\n\tType\n\tmembers() []Member\n}\n\nvar (\n\t_ composite = (*Struct)(nil)\n\t_ composite = (*Union)(nil)\n)\n\n// A value in bits.\ntype Bits uint32\n\n// Bytes converts a bit value into bytes.\nfunc (b Bits) Bytes() uint32 {\n\treturn uint32(b / 8)\n}\n\n// Member is part of a Struct or Union.\n//\n// It is not a valid Type.\ntype Member struct {\n\tName         string\n\tType         Type\n\tOffset       Bits\n\tBitfieldSize Bits\n\tTags         []string\n}\n\n// Enum lists possible values.\ntype Enum struct {\n\tName string\n\t// Size of the enum value in bytes.\n\tSize uint32\n\t// True if the values should be interpreted as signed integers.\n\tSigned bool\n\tValues []EnumValue\n}\n\nfunc (e *Enum) Format(fs fmt.State, verb rune) {\n\tformatType(fs, verb, e, \"size=\", e.Size, \"values=\", len(e.Values))\n}\n\nfunc (e *Enum) TypeName() string { return e.Name }\n\n// EnumValue is part of an Enum\n//\n// Is is not a valid Type\ntype EnumValue struct {\n\tName  string\n\tValue uint64\n}\n\nfunc (e *Enum) size() uint32 { return e.Size }\nfunc (e *Enum) copy() Type {\n\tcpy := *e\n\tcpy.Values = make([]EnumValue, len(e.Values))\n\tcopy(cpy.Values, e.Values)\n\treturn &cpy\n}\n\n// FwdKind is the type of forward declaration.\ntype FwdKind int\n\n// Valid types of forward declaration.\nconst (\n\tFwdStruct FwdKind = iota\n\tFwdUnion\n)\n\nfunc (fk FwdKind) String() string {\n\tswitch fk {\n\tcase FwdStruct:\n\t\treturn \"struct\"\n\tcase FwdUnion:\n\t\treturn \"union\"\n\tdefault:\n\t\treturn fmt.Sprintf(\"%T(%d)\", fk, int(fk))\n\t}\n}\n\n// Fwd is a forward declaration of a Type.\ntype Fwd struct {\n\tName string\n\tKind FwdKind\n}\n\nfunc (f *Fwd) Format(fs fmt.State, verb rune) {\n\tformatType(fs, verb, f, f.Kind)\n}\n\nfunc (f *Fwd) TypeName() string { return f.Name }\n\nfunc (f *Fwd) copy() Type {\n\tcpy := *f\n\treturn &cpy\n}\n\nfunc (f *Fwd) matches(typ Type) bool {\n\tif _, ok := As[*Struct](typ); ok && f.Kind == FwdStruct {\n\t\treturn true\n\t}\n\n\tif _, ok := As[*Union](typ); ok && f.Kind == FwdUnion {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// Typedef is an alias of a Type.\ntype Typedef struct {\n\tName string\n\tType Type\n\tTags []string\n}\n\nfunc (td *Typedef) Format(fs fmt.State, verb rune) {\n\tformatType(fs, verb, td, td.Type)\n}\n\nfunc (td *Typedef) TypeName() string { return td.Name }\n\nfunc (td *Typedef) copy() Type {\n\tcpy := *td\n\tcpy.Tags = copyTags(td.Tags)\n\treturn &cpy\n}\n\n// Volatile is a qualifier.\ntype Volatile struct {\n\tType Type\n}\n\nfunc (v *Volatile) Format(fs fmt.State, verb rune) {\n\tformatType(fs, verb, v, v.Type)\n}\n\nfunc (v *Volatile) TypeName() string { return \"\" }\n\nfunc (v *Volatile) qualify() Type { return v.Type }\nfunc (v *Volatile) copy() Type {\n\tcpy := *v\n\treturn &cpy\n}\n\n// Const is a qualifier.\ntype Const struct {\n\tType Type\n}\n\nfunc (c *Const) Format(fs fmt.State, verb rune) {\n\tformatType(fs, verb, c, c.Type)\n}\n\nfunc (c *Const) TypeName() string { return \"\" }\n\nfunc (c *Const) qualify() Type { return c.Type }\nfunc (c *Const) copy() Type {\n\tcpy := *c\n\treturn &cpy\n}\n\n// Restrict is a qualifier.\ntype Restrict struct {\n\tType Type\n}\n\nfunc (r *Restrict) Format(fs fmt.State, verb rune) {\n\tformatType(fs, verb, r, r.Type)\n}\n\nfunc (r *Restrict) TypeName() string { return \"\" }\n\nfunc (r *Restrict) qualify() Type { return r.Type }\nfunc (r *Restrict) copy() Type {\n\tcpy := *r\n\treturn &cpy\n}\n\n// Func is a function definition.\ntype Func struct {\n\tName    string\n\tType    Type\n\tLinkage FuncLinkage\n\tTags    []string\n\t// ParamTags holds a list of tags for each parameter of the FuncProto to which `Type` points.\n\t// If no tags are present for any param, the outer slice will be nil/len(ParamTags)==0.\n\t// If at least 1 param has a tag, the outer slice will have the same length as the number of params.\n\t// The inner slice contains the tags and may be nil/len(ParamTags[i])==0 if no tags are present for that param.\n\tParamTags [][]string\n}\n\ntype funcInfoMeta struct{}\n\nfunc FuncMetadata(ins *asm.Instruction) *Func {\n\tfn, _ := ins.Metadata.Get(funcInfoMeta{}).(*Func)\n\treturn fn\n}\n\n// WithFuncMetadata adds a btf.Func to the Metadata of asm.Instruction.\nfunc WithFuncMetadata(ins asm.Instruction, fn *Func) asm.Instruction {\n\tins.Metadata.Set(funcInfoMeta{}, fn)\n\treturn ins\n}\n\nfunc (f *Func) Format(fs fmt.State, verb rune) {\n\tformatType(fs, verb, f, f.Linkage, \"proto=\", f.Type)\n}\n\nfunc (f *Func) TypeName() string { return f.Name }\n\nfunc (f *Func) copy() Type {\n\tcpy := *f\n\tcpy.Tags = copyTags(f.Tags)\n\tif f.ParamTags != nil { // preserve nil vs zero-len slice distinction\n\t\tptCopy := make([][]string, len(f.ParamTags))\n\t\tfor i, tags := range f.ParamTags {\n\t\t\tptCopy[i] = copyTags(tags)\n\t\t}\n\t\tcpy.ParamTags = ptCopy\n\t}\n\treturn &cpy\n}\n\n// FuncProto is a function declaration.\ntype FuncProto struct {\n\tReturn Type\n\tParams []FuncParam\n}\n\nfunc (fp *FuncProto) Format(fs fmt.State, verb rune) {\n\tformatType(fs, verb, fp, \"args=\", len(fp.Params), \"return=\", fp.Return)\n}\n\nfunc (fp *FuncProto) TypeName() string { return \"\" }\n\nfunc (fp *FuncProto) copy() Type {\n\tcpy := *fp\n\tcpy.Params = make([]FuncParam, len(fp.Params))\n\tcopy(cpy.Params, fp.Params)\n\treturn &cpy\n}\n\ntype FuncParam struct {\n\tName string\n\tType Type\n}\n\n// Var is a global variable.\ntype Var struct {\n\tName    string\n\tType    Type\n\tLinkage VarLinkage\n\tTags    []string\n}\n\nfunc (v *Var) Format(fs fmt.State, verb rune) {\n\tformatType(fs, verb, v, v.Linkage)\n}\n\nfunc (v *Var) TypeName() string { return v.Name }\n\nfunc (v *Var) copy() Type {\n\tcpy := *v\n\tcpy.Tags = copyTags(v.Tags)\n\treturn &cpy\n}\n\n// Datasec is a global program section containing data.\ntype Datasec struct {\n\tName string\n\tSize uint32\n\tVars []VarSecinfo\n}\n\nfunc (ds *Datasec) Format(fs fmt.State, verb rune) {\n\tformatType(fs, verb, ds)\n}\n\nfunc (ds *Datasec) TypeName() string { return ds.Name }\n\nfunc (ds *Datasec) size() uint32 { return ds.Size }\n\nfunc (ds *Datasec) copy() Type {\n\tcpy := *ds\n\tcpy.Vars = make([]VarSecinfo, len(ds.Vars))\n\tcopy(cpy.Vars, ds.Vars)\n\treturn &cpy\n}\n\n// VarSecinfo describes variable in a Datasec.\n//\n// It is not a valid Type.\ntype VarSecinfo struct {\n\t// Var or Func.\n\tType   Type\n\tOffset uint32\n\tSize   uint32\n}\n\n// Float is a float of a given length.\ntype Float struct {\n\tName string\n\n\t// The size of the float in bytes.\n\tSize uint32\n}\n\nfunc (f *Float) Format(fs fmt.State, verb rune) {\n\tformatType(fs, verb, f, \"size=\", f.Size*8)\n}\n\nfunc (f *Float) TypeName() string { return f.Name }\nfunc (f *Float) size() uint32     { return f.Size }\nfunc (f *Float) copy() Type {\n\tcpy := *f\n\treturn &cpy\n}\n\n// declTag associates metadata with a declaration.\ntype declTag struct {\n\tType  Type\n\tValue string\n\t// The index this tag refers to in the target type. For composite types,\n\t// a value of -1 indicates that the tag refers to the whole type. Otherwise\n\t// it indicates which member or argument the tag applies to.\n\tIndex int\n}\n\nfunc (dt *declTag) Format(fs fmt.State, verb rune) {\n\tformatType(fs, verb, dt, \"type=\", dt.Type, \"value=\", dt.Value, \"index=\", dt.Index)\n}\n\nfunc (dt *declTag) TypeName() string { return \"\" }\nfunc (dt *declTag) copy() Type {\n\tcpy := *dt\n\treturn &cpy\n}\n\n// TypeTag associates metadata with a pointer type. Tag types act as a custom\n// modifier(const, restrict, volatile) for the target type. Unlike declTags,\n// TypeTags are ordered so the order in which they are added matters.\n//\n// One of their uses is to mark pointers as `__kptr` meaning a pointer points\n// to kernel memory. Adding a `__kptr` to pointers in map values allows you\n// to store pointers to kernel memory in maps.\ntype TypeTag struct {\n\tType  Type\n\tValue string\n}\n\nfunc (tt *TypeTag) Format(fs fmt.State, verb rune) {\n\tformatType(fs, verb, tt, \"type=\", tt.Type, \"value=\", tt.Value)\n}\n\nfunc (tt *TypeTag) TypeName() string { return \"\" }\nfunc (tt *TypeTag) qualify() Type    { return tt.Type }\nfunc (tt *TypeTag) copy() Type {\n\tcpy := *tt\n\treturn &cpy\n}\n\n// cycle is a type which had to be elided since it exceeded maxTypeDepth.\ntype cycle struct {\n\troot Type\n}\n\nfunc (c *cycle) ID() TypeID                     { return math.MaxUint32 }\nfunc (c *cycle) Format(fs fmt.State, verb rune) { formatType(fs, verb, c, \"root=\", c.root) }\nfunc (c *cycle) TypeName() string               { return \"\" }\nfunc (c *cycle) copy() Type {\n\tcpy := *c\n\treturn &cpy\n}\n\ntype sizer interface {\n\tsize() uint32\n}\n\nvar (\n\t_ sizer = (*Int)(nil)\n\t_ sizer = (*Pointer)(nil)\n\t_ sizer = (*Struct)(nil)\n\t_ sizer = (*Union)(nil)\n\t_ sizer = (*Enum)(nil)\n\t_ sizer = (*Datasec)(nil)\n)\n\ntype qualifier interface {\n\tqualify() Type\n}\n\nvar (\n\t_ qualifier = (*Const)(nil)\n\t_ qualifier = (*Restrict)(nil)\n\t_ qualifier = (*Volatile)(nil)\n\t_ qualifier = (*TypeTag)(nil)\n)\n\nvar errUnsizedType = errors.New(\"type is unsized\")\n\n// Sizeof returns the size of a type in bytes.\n//\n// Returns an error if the size can't be computed.\nfunc Sizeof(typ Type) (int, error) {\n\tvar (\n\t\tn    = int64(1)\n\t\telem int64\n\t)\n\n\tfor i := 0; i < maxResolveDepth; i++ {\n\t\tswitch v := typ.(type) {\n\t\tcase *Array:\n\t\t\tif n > 0 && int64(v.Nelems) > math.MaxInt64/n {\n\t\t\t\treturn 0, fmt.Errorf(\"type %s: overflow\", typ)\n\t\t\t}\n\n\t\t\t// Arrays may be of zero length, which allows\n\t\t\t// n to be zero as well.\n\t\t\tn *= int64(v.Nelems)\n\t\t\ttyp = v.Type\n\t\t\tcontinue\n\n\t\tcase sizer:\n\t\t\telem = int64(v.size())\n\n\t\tcase *Typedef:\n\t\t\ttyp = v.Type\n\t\t\tcontinue\n\n\t\tcase qualifier:\n\t\t\ttyp = v.qualify()\n\t\t\tcontinue\n\n\t\tdefault:\n\t\t\treturn 0, fmt.Errorf(\"type %T: %w\", typ, errUnsizedType)\n\t\t}\n\n\t\tif n > 0 && elem > math.MaxInt64/n {\n\t\t\treturn 0, fmt.Errorf(\"type %s: overflow\", typ)\n\t\t}\n\n\t\tsize := n * elem\n\t\tif int64(int(size)) != size {\n\t\t\treturn 0, fmt.Errorf(\"type %s: overflow\", typ)\n\t\t}\n\n\t\treturn int(size), nil\n\t}\n\n\treturn 0, fmt.Errorf(\"type %s: exceeded type depth\", typ)\n}\n\n// alignof returns the alignment of a type.\n//\n// Returns an error if the Type can't be aligned, like an integer with an uneven\n// size. Currently only supports the subset of types necessary for bitfield\n// relocations.\nfunc alignof(typ Type) (int, error) {\n\tvar n int\n\n\tswitch t := UnderlyingType(typ).(type) {\n\tcase *Enum:\n\t\tn = int(t.size())\n\tcase *Int:\n\t\tn = int(t.Size)\n\tcase *Array:\n\t\treturn alignof(t.Type)\n\tdefault:\n\t\treturn 0, fmt.Errorf(\"can't calculate alignment of %T\", t)\n\t}\n\n\tif !internal.IsPow(n) {\n\t\treturn 0, fmt.Errorf(\"alignment value %d is not a power of two\", n)\n\t}\n\n\treturn n, nil\n}\n\n// Copy a Type recursively.\n//\n// typ may form a cycle.\nfunc Copy(typ Type) Type {\n\treturn copyType(typ, nil, make(map[Type]Type), nil)\n}\n\nfunc copyType(typ Type, ids map[Type]TypeID, copies map[Type]Type, copiedIDs map[Type]TypeID) Type {\n\tif typ == nil {\n\t\treturn nil\n\t}\n\n\tcpy, ok := copies[typ]\n\tif ok {\n\t\t// This has been copied previously, no need to continue.\n\t\treturn cpy\n\t}\n\n\tcpy = typ.copy()\n\tcopies[typ] = cpy\n\n\tif id, ok := ids[typ]; ok {\n\t\tcopiedIDs[cpy] = id\n\t}\n\n\tfor child := range children(cpy) {\n\t\t*child = copyType(*child, ids, copies, copiedIDs)\n\t}\n\n\treturn cpy\n}\n\ntype typeDeque = internal.Deque[*Type]\n\n// essentialName represents the name of a BTF type stripped of any flavor\n// suffixes after a ___ delimiter.\ntype essentialName string\n\n// newEssentialName returns name without a ___ suffix.\n//\n// CO-RE has the concept of 'struct flavors', which are used to deal with\n// changes in kernel data structures. Anything after three underscores\n// in a type name is ignored for the purpose of finding a candidate type\n// in the kernel's BTF.\nfunc newEssentialName(name string) essentialName {\n\tif name == \"\" {\n\t\treturn \"\"\n\t}\n\tlastIdx := strings.LastIndex(name, \"___\")\n\tif lastIdx > 0 {\n\t\treturn essentialName(name[:lastIdx])\n\t}\n\treturn essentialName(name)\n}\n\n// UnderlyingType skips qualifiers and Typedefs.\nfunc UnderlyingType(typ Type) Type {\n\tresult := typ\n\tfor depth := 0; depth <= maxResolveDepth; depth++ {\n\t\tswitch v := (result).(type) {\n\t\tcase qualifier:\n\t\t\tresult = v.qualify()\n\t\tcase *Typedef:\n\t\t\tresult = v.Type\n\t\tdefault:\n\t\t\treturn result\n\t\t}\n\t}\n\treturn &cycle{typ}\n}\n\n// QualifiedType returns the type with all qualifiers removed.\nfunc QualifiedType(typ Type) Type {\n\tresult := typ\n\tfor depth := 0; depth <= maxResolveDepth; depth++ {\n\t\tswitch v := (result).(type) {\n\t\tcase qualifier:\n\t\t\tresult = v.qualify()\n\t\tdefault:\n\t\t\treturn result\n\t\t}\n\t}\n\treturn &cycle{typ}\n}\n\n// As returns typ if is of type T. Otherwise it peels qualifiers and Typedefs\n// until it finds a T.\n//\n// Returns the zero value and false if there is no T or if the type is nested\n// too deeply.\nfunc As[T Type](typ Type) (T, bool) {\n\t// NB: We can't make this function return (*T) since then\n\t// we can't assert that a type matches an interface which\n\t// embeds Type: as[composite](T).\n\tfor depth := 0; depth <= maxResolveDepth; depth++ {\n\t\tswitch v := (typ).(type) {\n\t\tcase T:\n\t\t\treturn v, true\n\t\tcase qualifier:\n\t\t\ttyp = v.qualify()\n\t\tcase *Typedef:\n\t\t\ttyp = v.Type\n\t\tdefault:\n\t\t\tgoto notFound\n\t\t}\n\t}\nnotFound:\n\tvar zero T\n\treturn zero, false\n}\n\ntype formatState struct {\n\tfmt.State\n\tdepth int\n}\n\n// formattableType is a subset of Type, to ease unit testing of formatType.\ntype formattableType interface {\n\tfmt.Formatter\n\tTypeName() string\n}\n\n// formatType formats a type in a canonical form.\n//\n// Handles cyclical types by only printing cycles up to a certain depth. Elements\n// in extra are separated by spaces unless the preceding element is a string\n// ending in '='.\nfunc formatType(f fmt.State, verb rune, t formattableType, extra ...interface{}) {\n\tif verb != 'v' && verb != 's' {\n\t\tfmt.Fprintf(f, \"{UNRECOGNIZED: %c}\", verb)\n\t\treturn\n\t}\n\n\t_, _ = io.WriteString(f, internal.GoTypeName(t))\n\n\tif name := t.TypeName(); name != \"\" {\n\t\t// Output BTF type name if present.\n\t\tfmt.Fprintf(f, \":%q\", name)\n\t}\n\n\tif f.Flag('+') {\n\t\t// Output address if requested.\n\t\tfmt.Fprintf(f, \":%#p\", t)\n\t}\n\n\tif verb == 's' {\n\t\t// %s omits details.\n\t\treturn\n\t}\n\n\tvar depth int\n\tif ps, ok := f.(*formatState); ok {\n\t\tdepth = ps.depth\n\t\tf = ps.State\n\t}\n\n\tmaxDepth, ok := f.Width()\n\tif !ok {\n\t\tmaxDepth = 0\n\t}\n\n\tif depth > maxDepth {\n\t\t// We've reached the maximum depth. This avoids infinite recursion even\n\t\t// for cyclical types.\n\t\treturn\n\t}\n\n\tif len(extra) == 0 {\n\t\treturn\n\t}\n\n\twantSpace := false\n\t_, _ = io.WriteString(f, \"[\")\n\tfor _, arg := range extra {\n\t\tif wantSpace {\n\t\t\t_, _ = io.WriteString(f, \" \")\n\t\t}\n\n\t\tswitch v := arg.(type) {\n\t\tcase string:\n\t\t\t_, _ = io.WriteString(f, v)\n\t\t\twantSpace = len(v) > 0 && v[len(v)-1] != '='\n\t\t\tcontinue\n\n\t\tcase formattableType:\n\t\t\tv.Format(&formatState{f, depth + 1}, verb)\n\n\t\tdefault:\n\t\t\tfmt.Fprint(f, arg)\n\t\t}\n\n\t\twantSpace = true\n\t}\n\t_, _ = io.WriteString(f, \"]\")\n}\n"
  },
  {
    "path": "btf/types_test.go",
    "content": "package btf\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\t\"github.com/google/go-cmp/cmp\"\n\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc TestSizeof(t *testing.T) {\n\ttestcases := []struct {\n\t\tsize int\n\t\ttyp  Type\n\t}{\n\t\t{0, (*Void)(nil)},\n\t\t{1, &Int{Size: 1}},\n\t\t{8, &Enum{Size: 8}},\n\t\t{0, &Array{Type: &Pointer{Target: (*Void)(nil)}, Nelems: 0}},\n\t\t{12, &Array{Type: &Enum{Size: 4}, Nelems: 3}},\n\t}\n\n\tfor _, tc := range testcases {\n\t\tname := fmt.Sprint(tc.typ)\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\thave, err := Sizeof(tc.typ)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(\"Can't calculate size:\", err)\n\t\t\t}\n\t\t\tif have != tc.size {\n\t\t\t\tt.Errorf(\"Expected size %d, got %d\", tc.size, have)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestCopy(t *testing.T) {\n\ti := &Int{Size: 4}\n\ttags := []string{\"bar:foo\"}\n\n\tgot := Copy(&Struct{\n\t\tMembers: []Member{\n\t\t\t{Name: \"a\", Type: i},\n\t\t\t{Name: \"b\", Type: i},\n\t\t},\n\t})\n\tmembers := got.(*Struct).Members\n\tqt.Check(t, qt.Equals(members[0].Type.(*Int), members[1].Type.(*Int)), qt.Commentf(\"identity should be preserved\"))\n\n\tfor _, test := range []struct {\n\t\tname string\n\t\ttyp  Type\n\t}{\n\t\t{\"nil\", nil},\n\t\t{\"void\", (*Void)(nil)},\n\t\t{\"int\", i},\n\t\t{\"cyclical\", newCyclicalType(2)},\n\t\t{\"struct tags\", &Struct{Tags: tags, Members: []Member{{Tags: tags}}}},\n\t\t{\"union tags\", &Union{Tags: tags, Members: []Member{{Tags: tags}}}},\n\t\t{\"typedef tags\", &Typedef{Type: i, Tags: tags}},\n\t\t{\"var tags\", &Var{Type: i, Tags: tags}},\n\t\t{\"func tags\", &Func{Tags: tags, ParamTags: [][]string{tags}}},\n\t} {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tcpy := Copy(test.typ)\n\t\t\tqt.Assert(t, testutils.IsDeepCopy(cpy, test.typ))\n\t\t})\n\t}\n}\n\nfunc TestAs(t *testing.T) {\n\ti := &Int{}\n\tptr := &Pointer{i}\n\ttd := &Typedef{Type: ptr}\n\tcst := &Const{td}\n\tvol := &Volatile{cst}\n\n\t// It's possible to retrieve qualifiers and Typedefs.\n\thaveVol, ok := As[*Volatile](vol)\n\tqt.Assert(t, qt.IsTrue(ok))\n\tqt.Assert(t, qt.Equals(haveVol, vol))\n\n\thaveTd, ok := As[*Typedef](vol)\n\tqt.Assert(t, qt.IsTrue(ok))\n\tqt.Assert(t, qt.Equals(haveTd, td))\n\n\thaveCst, ok := As[*Const](vol)\n\tqt.Assert(t, qt.IsTrue(ok))\n\tqt.Assert(t, qt.Equals(haveCst, cst))\n\n\t// Make sure we don't skip Pointer.\n\thaveI, ok := As[*Int](vol)\n\tqt.Assert(t, qt.IsFalse(ok))\n\tqt.Assert(t, qt.IsNil(haveI))\n\n\t// Make sure we can always retrieve Pointer.\n\tfor _, typ := range []Type{\n\t\ttd, cst, vol, ptr,\n\t} {\n\t\thave, ok := As[*Pointer](typ)\n\t\tqt.Assert(t, qt.IsTrue(ok))\n\t\tqt.Assert(t, qt.Equals(have, ptr))\n\t}\n}\n\nfunc BenchmarkCopy(b *testing.B) {\n\ttyp := newCyclicalType(10)\n\n\tb.ReportAllocs()\n\n\tfor b.Loop() {\n\t\tCopy(typ)\n\t}\n}\n\n// The following are valid Types.\n//\n// There currently is no better way to document which\n// types implement an interface.\nfunc ExampleType_validTypes() {\n\tvar _ Type = &Void{}\n\tvar _ Type = &Int{}\n\tvar _ Type = &Pointer{}\n\tvar _ Type = &Array{}\n\tvar _ Type = &Struct{}\n\tvar _ Type = &Union{}\n\tvar _ Type = &Enum{}\n\tvar _ Type = &Fwd{}\n\tvar _ Type = &Typedef{}\n\tvar _ Type = &Volatile{}\n\tvar _ Type = &Const{}\n\tvar _ Type = &Restrict{}\n\tvar _ Type = &Func{}\n\tvar _ Type = &FuncProto{}\n\tvar _ Type = &Var{}\n\tvar _ Type = &Datasec{}\n\tvar _ Type = &Float{}\n}\n\nfunc TestType(t *testing.T) {\n\ttypes := []func() Type{\n\t\tfunc() Type { return &Void{} },\n\t\tfunc() Type { return &Int{Size: 2} },\n\t\tfunc() Type { return &Pointer{Target: &Void{}} },\n\t\tfunc() Type { return &Array{Type: &Int{}} },\n\t\tfunc() Type {\n\t\t\treturn &Struct{\n\t\t\t\tMembers: []Member{{Type: &Void{}}},\n\t\t\t}\n\t\t},\n\t\tfunc() Type {\n\t\t\treturn &Union{\n\t\t\t\tMembers: []Member{{Type: &Void{}}},\n\t\t\t}\n\t\t},\n\t\tfunc() Type { return &Enum{} },\n\t\tfunc() Type { return &Fwd{Name: \"thunk\"} },\n\t\tfunc() Type { return &Typedef{Type: &Void{}} },\n\t\tfunc() Type { return &Volatile{Type: &Void{}} },\n\t\tfunc() Type { return &Const{Type: &Void{}} },\n\t\tfunc() Type { return &Restrict{Type: &Void{}} },\n\t\tfunc() Type { return &Func{Name: \"foo\", Type: &Void{}} },\n\t\tfunc() Type {\n\t\t\treturn &FuncProto{\n\t\t\t\tParams: []FuncParam{{Name: \"bar\", Type: &Void{}}},\n\t\t\t\tReturn: &Void{},\n\t\t\t}\n\t\t},\n\t\tfunc() Type { return &Var{Type: &Void{}} },\n\t\tfunc() Type {\n\t\t\treturn &Datasec{\n\t\t\t\tVars: []VarSecinfo{{Type: &Void{}}},\n\t\t\t}\n\t\t},\n\t\tfunc() Type { return &Float{} },\n\t\tfunc() Type { return &TypeTag{Type: &Void{}} },\n\t\tfunc() Type { return &cycle{&Void{}} },\n\t}\n\n\tcompareTypes := cmp.Comparer(func(a, b *Type) bool {\n\t\treturn a == b\n\t})\n\n\tfor _, fn := range types {\n\t\ttyp := fn()\n\t\tt.Run(fmt.Sprintf(\"%T\", typ), func(t *testing.T) {\n\t\t\tt.Logf(\"%v\", typ)\n\n\t\t\tif typ == typ.copy() {\n\t\t\t\tt.Error(\"Copy doesn't copy\")\n\t\t\t}\n\n\t\t\tvar a []*Type\n\t\t\tfor t := range children(typ) {\n\t\t\t\ta = append(a, t)\n\t\t\t}\n\n\t\t\tif _, ok := typ.(*cycle); !ok {\n\t\t\t\tif n := countChildren(t, reflect.TypeOf(typ)); len(a) < n {\n\t\t\t\t\tt.Errorf(\"walkType visited %d children, expected at least %d\", len(a), n)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvar b []*Type\n\t\t\tfor t := range children(typ) {\n\t\t\t\tb = append(b, t)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(a, b, compareTypes); diff != \"\" {\n\t\t\t\tt.Errorf(\"Walk mismatch (-want +got):\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestTagMarshaling(t *testing.T) {\n\tfor _, typ := range []Type{\n\t\t&TypeTag{&Int{}, \"foo\"},\n\t\t&Struct{Members: []Member{\n\t\t\t{Type: &Int{}, Tags: []string{\"bar\"}},\n\t\t}, Tags: []string{\"foo\"}},\n\t\t&Union{Members: []Member{\n\t\t\t{Type: &Int{}, Tags: []string{\"bar\"}},\n\t\t\t{Type: &Int{}, Tags: []string{\"baz\"}},\n\t\t}, Tags: []string{\"foo\"}},\n\t\t&Func{Type: &FuncProto{Return: &Int{}, Params: []FuncParam{\n\t\t\t{Name: \"param1\", Type: &Int{}},\n\t\t}}, Tags: []string{\"foo\"}, ParamTags: [][]string{{\"bar\"}}},\n\t\t&Var{Name: \"var1\", Type: &Int{}, Tags: []string{\"foo\"}},\n\t\t&Typedef{Name: \"baz\", Type: &Int{}, Tags: []string{\"foo\"}},\n\t} {\n\t\tt.Run(fmt.Sprint(typ), func(t *testing.T) {\n\t\t\ts := specFromTypes(t, []Type{typ})\n\n\t\t\thave, err := s.TypeByID(1)\n\t\t\tqt.Assert(t, qt.IsNil(err))\n\n\t\t\tqt.Assert(t, qt.DeepEquals(have, typ))\n\t\t})\n\t}\n}\n\nfunc countChildren(t *testing.T, typ reflect.Type) int {\n\tif typ.Kind() != reflect.Pointer {\n\t\tt.Fatal(\"Expected pointer, got\", typ.Kind())\n\t}\n\n\ttyp = typ.Elem()\n\tif typ.Kind() != reflect.Struct {\n\t\tt.Fatal(\"Expected struct, got\", typ.Kind())\n\t}\n\n\tvar n int\n\tfor i := 0; i < typ.NumField(); i++ {\n\t\tif typ.Field(i).Type == reflect.TypeOf((*Type)(nil)).Elem() {\n\t\t\tn++\n\t\t}\n\t}\n\n\treturn n\n}\n\ntype testFormattableType struct {\n\tname  string\n\textra []interface{}\n}\n\nvar _ formattableType = (*testFormattableType)(nil)\n\nfunc (tft *testFormattableType) TypeName() string { return tft.name }\nfunc (tft *testFormattableType) Format(fs fmt.State, verb rune) {\n\tformatType(fs, verb, tft, tft.extra...)\n}\n\nfunc TestFormatType(t *testing.T) {\n\tt1 := &testFormattableType{\"\", []interface{}{\"extra\"}}\n\tt1Addr := fmt.Sprintf(\"%#p\", t1)\n\tgoType := reflect.TypeOf(t1).Elem().Name()\n\n\tt2 := &testFormattableType{\"foo\", []interface{}{t1}}\n\n\tt3 := &testFormattableType{extra: []interface{}{\"\"}}\n\n\ttests := []struct {\n\t\tt        formattableType\n\t\tfmt      string\n\t\tcontains []string\n\t\tomits    []string\n\t}{\n\t\t// %s doesn't contain address or extra.\n\t\t{t1, \"%s\", []string{goType}, []string{t1Addr, \"extra\"}},\n\t\t// %+s doesn't contain extra.\n\t\t{t1, \"%+s\", []string{goType, t1Addr}, []string{\"extra\"}},\n\t\t// %v does contain extra.\n\t\t{t1, \"%v\", []string{goType, \"extra\"}, []string{t1Addr}},\n\t\t// %+v does contain address.\n\t\t{t1, \"%+v\", []string{goType, \"extra\", t1Addr}, nil},\n\t\t// %v doesn't print nested types' extra.\n\t\t{t2, \"%v\", []string{goType, t2.name}, []string{\"extra\"}},\n\t\t// %1v does print nested types' extra.\n\t\t{t2, \"%1v\", []string{goType, t2.name, \"extra\"}, nil},\n\t\t// empty strings in extra don't emit anything.\n\t\t{t3, \"%v\", []string{\"[]\"}, nil},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.fmt, func(t *testing.T) {\n\t\t\tstr := fmt.Sprintf(test.fmt, test.t)\n\t\t\tt.Log(str)\n\n\t\t\tfor _, want := range test.contains {\n\t\t\t\tqt.Assert(t, qt.StringContains(str, want))\n\t\t\t}\n\n\t\t\tfor _, notWant := range test.omits {\n\t\t\t\tqt.Assert(t, qt.Not(qt.StringContains(str, notWant)))\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestFormatCompoundTypes(t *testing.T) {\n\tu := &Union{\n\t\tName:    \"u\",\n\t\tMembers: []Member{{Name: \"a\"}, {Name: \"b\"}, {Name: \"c\"}, {Name: \"d\"}, {Name: \"e\"}, {Name: \"f\"}},\n\t}\n\tqt.Assert(t, qt.Equals(fmt.Sprintf(\"%v\", u), `Union:\"u\"[fields=6 fieldNames=[a b c d e ...]]`))\n\tqt.Assert(t, qt.Equals(fmt.Sprintf(\"%1v\", u), `Union:\"u\"[fields=6 fieldNames=[a ...]]`))\n\tqt.Assert(t, qt.Equals(fmt.Sprintf(\"%6v\", u), `Union:\"u\"[fields=6 fieldNames=[a b c d e f]]`))\n\n\ts := &Struct{\n\t\tName:    \"s\",\n\t\tMembers: []Member{{Name: \"a\"}, {Name: \"b\"}, {Name: \"c\"}},\n\t}\n\tqt.Assert(t, qt.Equals(fmt.Sprintf(\"%v\", s), `Struct:\"s\"[fields=3 fieldNames=[a b c]]`))\n\tqt.Assert(t, qt.Equals(fmt.Sprintf(\"%1v\", s), `Struct:\"s\"[fields=3 fieldNames=[a ...]]`))\n}\n\nfunc newCyclicalType(n int) Type {\n\tptr := &Pointer{}\n\tprev := Type(ptr)\n\tfor i := 0; i < n; i++ {\n\t\tswitch i % 5 {\n\t\tcase 0:\n\t\t\tprev = &Struct{\n\t\t\t\tMembers: []Member{\n\t\t\t\t\t{Type: prev},\n\t\t\t\t},\n\t\t\t}\n\n\t\tcase 1:\n\t\t\tprev = &Const{Type: prev}\n\t\tcase 2:\n\t\t\tprev = &Volatile{Type: prev}\n\t\tcase 3:\n\t\t\tprev = &Typedef{Type: prev}\n\t\tcase 4:\n\t\t\tprev = &Array{Type: prev, Index: &Int{Size: 1}}\n\t\t}\n\t}\n\tptr.Target = prev\n\treturn ptr\n}\n\nfunc TestUnderlyingType(t *testing.T) {\n\twrappers := []struct {\n\t\tname string\n\t\tfn   func(Type) Type\n\t}{\n\t\t{\"const\", func(t Type) Type { return &Const{Type: t} }},\n\t\t{\"volatile\", func(t Type) Type { return &Volatile{Type: t} }},\n\t\t{\"restrict\", func(t Type) Type { return &Restrict{Type: t} }},\n\t\t{\"typedef\", func(t Type) Type { return &Typedef{Type: t} }},\n\t\t{\"type tag\", func(t Type) Type { return &TypeTag{Type: t} }},\n\t}\n\n\tfor _, test := range wrappers {\n\t\tt.Run(test.name+\" cycle\", func(t *testing.T) {\n\t\t\troot := &Volatile{}\n\t\t\troot.Type = test.fn(root)\n\n\t\t\tgot, ok := UnderlyingType(root).(*cycle)\n\t\t\tqt.Assert(t, qt.IsTrue(ok))\n\t\t\tqt.Assert(t, qt.Equals[Type](got.root, root))\n\t\t})\n\t}\n\n\tfor _, test := range wrappers {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\twant := &Int{}\n\t\t\tgot := UnderlyingType(test.fn(want))\n\t\t\tqt.Assert(t, qt.Equals[Type](got, want))\n\t\t})\n\t}\n}\n\nfunc TestInflateLegacyBitfield(t *testing.T) {\n\tconst offset = 3\n\tconst size = 5\n\n\taddHeaderAndStringTable := func(types ...any) []byte {\n\t\tvar buf []byte\n\t\tvar err error\n\t\tfor _, typ := range types {\n\t\t\tbuf, err = binary.Append(buf, binary.LittleEndian, typ)\n\t\t\tqt.Assert(t, qt.IsNil(err))\n\t\t}\n\n\t\theader, err := binary.Append(nil, binary.LittleEndian, &btfHeader{\n\t\t\tMagic:     btfMagic,\n\t\t\tVersion:   1,\n\t\t\tFlags:     0,\n\t\t\tHdrLen:    uint32(btfHeaderLen),\n\t\t\tTypeOff:   0,\n\t\t\tTypeLen:   uint32(len(buf)),\n\t\t\tStringOff: uint32(len(buf)),\n\t\t\tStringLen: 1,\n\t\t})\n\t\tqt.Assert(t, qt.IsNil(err))\n\n\t\tbuf = append(header, buf...)\n\t\tbuf = append(buf, 0) // string table\n\t\treturn buf\n\t}\n\n\tvar placeholder struct {\n\t\tbtfType\n\t\tbtfInt\n\t}\n\tplaceholder.SetKind(kindInt)\n\tplaceholder.SetSize(4)\n\tplaceholder.SetOffset(offset)\n\tplaceholder.SetBits(size)\n\n\tvar structFirst struct {\n\t\tbtfType\n\t\tMembers [1]btfMember\n\t}\n\tstructFirst.SetKind(kindStruct)\n\tstructFirst.SetVlen(1)\n\tstructFirst.Members = [...]btfMember{{Type: 2}}\n\n\tbefore := addHeaderAndStringTable(&structFirst, &placeholder)\n\n\tstructSecond := structFirst\n\tstructSecond.Members = [...]btfMember{{Type: 1}}\n\n\tafter := addHeaderAndStringTable(&placeholder, &structSecond)\n\n\tfor _, test := range []struct {\n\t\tname string\n\t\tbuf  []byte\n\t}{\n\t\t{\"struct before int\", before},\n\t\t{\"struct after int\", after},\n\t} {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tspec, err := loadRawSpec(test.buf, nil)\n\t\t\tqt.Assert(t, qt.IsNil(err))\n\n\t\t\tfor _, typ := range typesFromSpec(t, spec) {\n\t\t\t\ts, ok := typ.(*Struct)\n\t\t\t\tif !ok {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\ti := s.Members[0]\n\t\t\t\tif i.BitfieldSize != size {\n\t\t\t\t\tt.Errorf(\"Expected bitfield size %d, got %d\", size, i.BitfieldSize)\n\t\t\t\t}\n\n\t\t\t\tif i.Offset != offset {\n\t\t\t\t\tt.Errorf(\"Expected offset %d, got %d\", offset, i.Offset)\n\t\t\t\t}\n\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tt.Fatal(\"No Struct returned from inflateRawTypes\")\n\t\t})\n\t}\n}\n\nfunc TestMemberNames(t *testing.T) {\n\tmembers := []Member{{Name: \"foo\"}, {}, {Name: \"bar\"}}\n\n\tqt.Assert(t, qt.ContentEquals(memberNames(members, 3),\n\t\t[]string{\"foo\", \"<1>\", \"bar\"}))\n\n\tqt.Assert(t, qt.ContentEquals(memberNames(members, 2),\n\t\t[]string{\"foo\", \"<1>\", \"...\"}))\n}\n\nfunc BenchmarkWalk(b *testing.B) {\n\ttypes := []Type{\n\t\t&Void{},\n\t\t&Int{},\n\t\t&Pointer{},\n\t\t&Array{},\n\t\t&Struct{Members: make([]Member, 2)},\n\t\t&Union{Members: make([]Member, 2)},\n\t\t&Enum{},\n\t\t&Fwd{},\n\t\t&Typedef{},\n\t\t&Volatile{},\n\t\t&Const{},\n\t\t&Restrict{},\n\t\t&Func{},\n\t\t&FuncProto{Params: make([]FuncParam, 2)},\n\t\t&Var{},\n\t\t&Datasec{Vars: make([]VarSecinfo, 2)},\n\t}\n\n\tfor _, typ := range types {\n\t\tb.Run(fmt.Sprint(typ), func(b *testing.B) {\n\t\t\tb.ReportAllocs()\n\n\t\t\tfor b.Loop() {\n\t\t\t\tvar dq typeDeque\n\t\t\t\tfor child := range children(typ) {\n\t\t\t\t\tdq.Push(child)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestTagUnmarshaling(t *testing.T) {\n\ttestutils.Files(t, testutils.Glob(t, \"testdata/tags-*.elf\"), func(t *testing.T, file string) {\n\t\tspec, err := LoadSpec(file)\n\t\tqt.Assert(t, qt.IsNil(err))\n\n\t\tvar s *Struct\n\t\terr = spec.TypeByName(\"s\", &s)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.ContentEquals(s.Tags, []string{\"c\"}))\n\t\tqt.Assert(t, qt.ContentEquals(s.Members[0].Tags, []string{\"a\"}))\n\t\tqt.Assert(t, qt.ContentEquals(s.Members[1].Tags, []string{\"b\"}))\n\n\t\tvar u *Union\n\t\terr = spec.TypeByName(\"u\", &u)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.ContentEquals(u.Tags, []string{\"c\"}))\n\t\tqt.Assert(t, qt.ContentEquals(u.Members[0].Tags, []string{\"a\"}))\n\t\tqt.Assert(t, qt.ContentEquals(u.Members[1].Tags, []string{\"b\"}))\n\n\t\tvar td *Typedef\n\t\terr = spec.TypeByName(\"td\", &td)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.ContentEquals(td.Tags, []string{\"b\"}))\n\n\t\tvar s1 *Var\n\t\terr = spec.TypeByName(\"s1\", &s1)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.ContentEquals(s1.Tags, []string{\"d\"}))\n\n\t\tvar s2 *Var\n\t\terr = spec.TypeByName(\"u1\", &s2)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.ContentEquals(s2.Tags, []string{\"e\"}))\n\n\t\tvar t1 *Var\n\t\terr = spec.TypeByName(\"t1\", &t1)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.ContentEquals(t1.Tags, []string{\"a\"}))\n\n\t\tvar extFunc *Func\n\t\terr = spec.TypeByName(\"fwdDecl\", &extFunc)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.ContentEquals(extFunc.Tags, []string{\"a\", \"b\"}))\n\t\tqt.Assert(t, qt.ContentEquals(extFunc.ParamTags, [][]string{{\"c\"}, {\"d\"}}))\n\n\t\tvar normalFunc *Func\n\t\terr = spec.TypeByName(\"normalDecl1\", &normalFunc)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.ContentEquals(normalFunc.Tags, []string{\"e\"}))\n\t\tqt.Assert(t, qt.ContentEquals(normalFunc.ParamTags, [][]string{{\"b\"}, {\"c\"}}))\n\n\t\terr = spec.TypeByName(\"normalDecl2\", &normalFunc)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.ContentEquals(normalFunc.Tags, []string{\"e\"}))\n\t\tqt.Assert(t, qt.ContentEquals(normalFunc.ParamTags, [][]string{{\"b\"}, {\"c\"}}))\n\t})\n}\n\nfunc BenchmarkUnderlyingType(b *testing.B) {\n\tb.Run(\"no unwrapping\", func(b *testing.B) {\n\t\tv := &Int{}\n\t\tb.ReportAllocs()\n\n\t\tfor b.Loop() {\n\t\t\tUnderlyingType(v)\n\t\t}\n\t})\n\n\tb.Run(\"single unwrapping\", func(b *testing.B) {\n\t\tv := &Typedef{Type: &Int{}}\n\t\tb.ReportAllocs()\n\n\t\tfor b.Loop() {\n\t\t\tUnderlyingType(v)\n\t\t}\n\t})\n}\n\n// As can be used to strip qualifiers from a Type.\nfunc ExampleAs() {\n\ta := &Volatile{Type: &Pointer{Target: &Typedef{Name: \"foo\", Type: &Int{Size: 2}}}}\n\tfmt.Println(As[*Pointer](a))\n\t// Output: Pointer[target=Typedef:\"foo\"] true\n}\n"
  },
  {
    "path": "btf/unmarshal.go",
    "content": "package btf\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"hash/maphash\"\n\t\"io\"\n\t\"iter\"\n\t\"maps\"\n\t\"math\"\n\t\"slices\"\n\t\"sync\"\n)\n\n// sharedBuf is a buffer which may be shared between multiple decoders.\n//\n// It must not be modified. Some sharedBuf may be backed by an mmap-ed file, in\n// which case the sharedBuf has a finalizer. sharedBuf must therefore always be\n// passed as a pointer.\ntype sharedBuf struct {\n\traw []byte\n}\n\ntype decoder struct {\n\t// Immutable fields, may be shared.\n\n\tbase      *decoder\n\tbyteOrder binary.ByteOrder\n\t*sharedBuf\n\tstrings *stringTable\n\t// The ID for offsets[0].\n\tfirstTypeID TypeID\n\t// Map from TypeID to offset of the marshaled data in raw. Contains an entry\n\t// for each TypeID, including 0 aka Void. The offset for Void is invalid.\n\toffsets  []int\n\tdeclTags map[TypeID][]TypeID\n\t// An index from essentialName to TypeID.\n\tnamedTypes *fuzzyStringIndex\n\n\t// Protection for mutable fields below.\n\tmu              sync.Mutex\n\ttypes           map[TypeID]Type\n\ttypeIDs         map[Type]TypeID\n\tlegacyBitfields map[TypeID][2]Bits // offset, size\n}\n\nfunc newDecoder(raw []byte, bo binary.ByteOrder, strings *stringTable, base *decoder) (*decoder, error) {\n\tfirstTypeID := TypeID(0)\n\tif base != nil {\n\t\tif base.byteOrder != bo {\n\t\t\treturn nil, fmt.Errorf(\"can't use %v base with %v split BTF\", base.byteOrder, bo)\n\t\t}\n\n\t\tif base.firstTypeID != 0 {\n\t\t\treturn nil, fmt.Errorf(\"can't use split BTF as base\")\n\t\t}\n\n\t\tfirstTypeID = TypeID(len(base.offsets))\n\t}\n\n\tvar header btfType\n\tvar numTypes, numDeclTags, numNamedTypes int\n\n\tfor _, err := range allBtfTypeOffsets(raw, bo, &header) {\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tnumTypes++\n\n\t\tif header.Kind() == kindDeclTag {\n\t\t\tnumDeclTags++\n\t\t}\n\n\t\tif header.NameOff != 0 {\n\t\t\tnumNamedTypes++\n\t\t}\n\t}\n\n\tif firstTypeID == 0 {\n\t\t// Allocate an extra slot for Void so we don't have to deal with\n\t\t// constant off by one issues.\n\t\tnumTypes++\n\t}\n\n\toffsets := make([]int, 0, numTypes)\n\tdeclTags := make(map[TypeID][]TypeID, numDeclTags)\n\tnamedTypes := newFuzzyStringIndex(numNamedTypes)\n\n\tif firstTypeID == 0 {\n\t\t// Add a sentinel for Void.\n\t\toffsets = append(offsets, math.MaxInt)\n\t}\n\n\tid := firstTypeID + TypeID(len(offsets))\n\tfor offset := range allBtfTypeOffsets(raw, bo, &header) {\n\t\tif id < firstTypeID {\n\t\t\treturn nil, fmt.Errorf(\"no more type IDs\")\n\t\t}\n\n\t\toffsets = append(offsets, offset)\n\n\t\tif header.Kind() == kindDeclTag {\n\t\t\tdeclTags[header.Type()] = append(declTags[header.Type()], id)\n\t\t}\n\n\t\t// Build named type index.\n\t\tname, err := strings.LookupBytes(header.NameOff)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"lookup type name for id %v: %w\", id, err)\n\t\t}\n\n\t\tif len(name) > 0 {\n\t\t\tif i := bytes.Index(name, []byte(\"___\")); i != -1 {\n\t\t\t\t// Flavours are rare. It's cheaper to find the first index for some\n\t\t\t\t// reason.\n\t\t\t\ti = bytes.LastIndex(name, []byte(\"___\"))\n\t\t\t\tname = name[:i]\n\t\t\t}\n\n\t\t\tnamedTypes.Add(name, id)\n\t\t}\n\n\t\tid++\n\t}\n\n\tnamedTypes.Build()\n\n\treturn &decoder{\n\t\tbase,\n\t\tbo,\n\t\t&sharedBuf{raw},\n\t\tstrings,\n\t\tfirstTypeID,\n\t\toffsets,\n\t\tdeclTags,\n\t\tnamedTypes,\n\t\tsync.Mutex{},\n\t\tmake(map[TypeID]Type),\n\t\tmake(map[Type]TypeID),\n\t\tmake(map[TypeID][2]Bits),\n\t}, nil\n}\n\nfunc allBtfTypeOffsets(buf []byte, bo binary.ByteOrder, header *btfType) iter.Seq2[int, error] {\n\treturn func(yield func(int, error) bool) {\n\t\tfor offset := 0; offset < len(buf); {\n\t\t\tstart := offset\n\n\t\t\tn, err := unmarshalBtfType(header, buf[offset:], bo)\n\t\t\tif err != nil {\n\t\t\t\tyield(-1, fmt.Errorf(\"unmarshal type header: %w\", err))\n\t\t\t\treturn\n\t\t\t}\n\t\t\toffset += n\n\n\t\t\tn, err = header.DataLen()\n\t\t\tif err != nil {\n\t\t\t\tyield(-1, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\toffset += n\n\n\t\t\tif offset > len(buf) {\n\t\t\t\tyield(-1, fmt.Errorf(\"auxiliary type data: %w\", io.ErrUnexpectedEOF))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif !yield(start, nil) {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc rebaseDecoder(d *decoder, base *decoder) (*decoder, error) {\n\tif d.base == nil {\n\t\treturn nil, fmt.Errorf(\"rebase split spec: not a split spec\")\n\t}\n\n\tif len(d.base.raw) != len(base.raw) || (len(d.base.raw) > 0 && &d.base.raw[0] != &base.raw[0]) {\n\t\treturn nil, fmt.Errorf(\"rebase split spec: raw BTF differs\")\n\t}\n\n\treturn &decoder{\n\t\tbase,\n\t\td.byteOrder,\n\t\td.sharedBuf,\n\t\td.strings,\n\t\td.firstTypeID,\n\t\td.offsets,\n\t\td.declTags,\n\t\td.namedTypes,\n\t\tsync.Mutex{},\n\t\tmake(map[TypeID]Type),\n\t\tmake(map[Type]TypeID),\n\t\tmake(map[TypeID][2]Bits),\n\t}, nil\n}\n\n// Copy performs a deep copy of a decoder and its base.\nfunc (d *decoder) Copy() *decoder {\n\tif d == nil {\n\t\treturn nil\n\t}\n\n\treturn d.copy(nil)\n}\n\nfunc (d *decoder) copy(copiedTypes map[Type]Type) *decoder {\n\tif d == nil {\n\t\treturn nil\n\t}\n\n\td.mu.Lock()\n\tdefer d.mu.Unlock()\n\n\tif copiedTypes == nil {\n\t\tcopiedTypes = make(map[Type]Type, len(d.types))\n\t}\n\n\ttypes := make(map[TypeID]Type, len(d.types))\n\ttypeIDs := make(map[Type]TypeID, len(d.typeIDs))\n\tfor id, typ := range d.types {\n\t\ttypes[id] = copyType(typ, d.typeIDs, copiedTypes, typeIDs)\n\t}\n\n\treturn &decoder{\n\t\td.base.copy(copiedTypes),\n\t\td.byteOrder,\n\t\td.sharedBuf,\n\t\td.strings,\n\t\td.firstTypeID,\n\t\td.offsets,\n\t\td.declTags,\n\t\td.namedTypes,\n\t\tsync.Mutex{},\n\t\ttypes,\n\t\ttypeIDs,\n\t\tmaps.Clone(d.legacyBitfields),\n\t}\n}\n\n// TypeID returns the ID for a Type previously obtained via [TypeByID].\nfunc (d *decoder) TypeID(typ Type) (TypeID, error) {\n\tif _, ok := typ.(*Void); ok {\n\t\t// Equality is weird for void, since it is a zero sized type.\n\t\treturn 0, nil\n\t}\n\n\td.mu.Lock()\n\tdefer d.mu.Unlock()\n\n\tid, ok := d.typeIDs[typ]\n\tif !ok {\n\t\treturn 0, fmt.Errorf(\"no ID for type %s: %w\", typ, ErrNotFound)\n\t}\n\n\treturn id, nil\n}\n\n// TypesByName returns all types which have the given essential name.\n//\n// Returns ErrNotFound if no matching Type exists.\nfunc (d *decoder) TypesByName(name essentialName) ([]Type, error) {\n\tvar types []Type\n\tfor id := range d.namedTypes.Find(string(name)) {\n\t\ttyp, err := d.TypeByID(id)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif newEssentialName(typ.TypeName()) == name {\n\t\t\t// Deal with hash collisions by checking against the name.\n\t\t\ttypes = append(types, typ)\n\t\t}\n\t}\n\n\tif len(types) == 0 {\n\t\t// Return an unwrapped error because this is on the hot path\n\t\t// for CO-RE.\n\t\treturn nil, ErrNotFound\n\t}\n\n\treturn types, nil\n}\n\n// TypeByID decodes a type and any of its descendants.\nfunc (d *decoder) TypeByID(id TypeID) (Type, error) {\n\td.mu.Lock()\n\tdefer d.mu.Unlock()\n\n\treturn d.inflateType(id)\n}\n\nfunc (d *decoder) inflateType(id TypeID) (typ Type, err error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\terr = r.(error)\n\t\t}\n\n\t\t// err is the return value of the enclosing function, even if an explicit\n\t\t// return is used.\n\t\t// See https://go.dev/ref/spec#Defer_statements\n\t\tif err != nil {\n\t\t\t// Remove partially inflated type so that d.types only contains\n\t\t\t// fully inflated ones.\n\t\t\tdelete(d.types, id)\n\t\t} else {\n\t\t\t// Populate reverse index.\n\t\t\td.typeIDs[typ] = id\n\t\t}\n\t}()\n\n\tif id < d.firstTypeID {\n\t\treturn d.base.inflateType(id)\n\t}\n\n\tif id == 0 {\n\t\t// Void is defined to always be type ID 0, and is thus omitted from BTF.\n\t\t// Fast-path because it is looked up frequently.\n\t\treturn (*Void)(nil), nil\n\t}\n\n\tif typ, ok := d.types[id]; ok {\n\t\treturn typ, nil\n\t}\n\n\tfixup := func(id TypeID, typ *Type) {\n\t\tfixup, err := d.inflateType(id)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\t*typ = fixup\n\t}\n\n\tconvertMembers := func(header *btfType, buf []byte) ([]Member, error) {\n\t\tvar bm btfMember\n\t\tmembers := make([]Member, 0, header.Vlen())\n\t\tfor i := range header.Vlen() {\n\t\t\tn, err := unmarshalBtfMember(&bm, buf, d.byteOrder)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"unmarshal member: %w\", err)\n\t\t\t}\n\t\t\tbuf = buf[n:]\n\n\t\t\tname, err := d.strings.Lookup(bm.NameOff)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"can't get name for member %d: %w\", i, err)\n\t\t\t}\n\n\t\t\tmembers = append(members, Member{\n\t\t\t\tName:   name,\n\t\t\t\tOffset: Bits(bm.Offset),\n\t\t\t})\n\n\t\t\tm := &members[i]\n\t\t\tfixup(bm.Type, &m.Type)\n\n\t\t\tif header.Bitfield() {\n\t\t\t\tm.BitfieldSize = Bits(bm.Offset >> 24)\n\t\t\t\tm.Offset &= 0xffffff\n\t\t\t\t// We ignore legacy bitfield definitions if the current composite\n\t\t\t\t// is a new-style bitfield. This is kind of safe since offset and\n\t\t\t\t// size on the type of the member must be zero if kindFlat is set\n\t\t\t\t// according to spec.\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// This may be a legacy bitfield, try to fix it up.\n\t\t\tdata, ok := d.legacyBitfields[bm.Type]\n\t\t\tif ok {\n\t\t\t\t// Bingo!\n\t\t\t\tm.Offset += data[0]\n\t\t\t\tm.BitfieldSize = data[1]\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\treturn members, nil\n\t}\n\n\tidx := int(id - d.firstTypeID)\n\tif idx >= len(d.offsets) {\n\t\treturn nil, fmt.Errorf(\"type id %v: %w\", id, ErrNotFound)\n\t}\n\n\toffset := d.offsets[idx]\n\tif offset >= len(d.raw) {\n\t\treturn nil, fmt.Errorf(\"offset out of bounds\")\n\t}\n\n\tvar (\n\t\theader    btfType\n\t\tbInt      btfInt\n\t\tbArr      btfArray\n\t\tbVariable btfVariable\n\t\tbDeclTag  btfDeclTag\n\t\tpos       = d.raw[offset:]\n\t)\n\n\t{\n\t\tif n, err := unmarshalBtfType(&header, pos, d.byteOrder); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"can't unmarshal type info for id %v: %v\", id, err)\n\t\t} else {\n\t\t\tpos = pos[n:]\n\t\t}\n\n\t\tname, err := d.strings.Lookup(header.NameOff)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"get name for type id %d: %w\", id, err)\n\t\t}\n\n\t\tswitch header.Kind() {\n\t\tcase kindInt:\n\t\t\tsize := header.Size()\n\t\t\tif _, err := unmarshalBtfInt(&bInt, pos, d.byteOrder); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"can't unmarshal btfInt, id: %d: %w\", id, err)\n\t\t\t}\n\t\t\tif bInt.Offset() > 0 || bInt.Bits().Bytes() != size {\n\t\t\t\td.legacyBitfields[id] = [2]Bits{bInt.Offset(), bInt.Bits()}\n\t\t\t}\n\t\t\ttyp = &Int{name, header.Size(), bInt.Encoding()}\n\t\t\td.types[id] = typ\n\n\t\tcase kindPointer:\n\t\t\tptr := &Pointer{nil}\n\t\t\td.types[id] = ptr\n\n\t\t\tfixup(header.Type(), &ptr.Target)\n\t\t\ttyp = ptr\n\n\t\tcase kindArray:\n\t\t\tif _, err := unmarshalBtfArray(&bArr, pos, d.byteOrder); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"can't unmarshal btfArray, id: %d: %w\", id, err)\n\t\t\t}\n\n\t\t\tarr := &Array{nil, nil, bArr.Nelems}\n\t\t\td.types[id] = arr\n\n\t\t\tfixup(bArr.IndexType, &arr.Index)\n\t\t\tfixup(bArr.Type, &arr.Type)\n\t\t\ttyp = arr\n\n\t\tcase kindStruct:\n\t\t\tstr := &Struct{name, header.Size(), nil, nil}\n\t\t\td.types[id] = str\n\t\t\ttyp = str\n\n\t\t\tstr.Members, err = convertMembers(&header, pos)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"struct %s (id %d): %w\", name, id, err)\n\t\t\t}\n\n\t\tcase kindUnion:\n\t\t\tuni := &Union{name, header.Size(), nil, nil}\n\t\t\td.types[id] = uni\n\t\t\ttyp = uni\n\n\t\t\tuni.Members, err = convertMembers(&header, pos)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"union %s (id %d): %w\", name, id, err)\n\t\t\t}\n\n\t\tcase kindEnum:\n\t\t\tenum := &Enum{name, header.Size(), header.Signed(), nil}\n\t\t\td.types[id] = enum\n\t\t\ttyp = enum\n\n\t\t\tvar be btfEnum\n\t\t\tenum.Values = make([]EnumValue, 0, header.Vlen())\n\t\t\tfor i := range header.Vlen() {\n\t\t\t\tn, err := unmarshalBtfEnum(&be, pos, d.byteOrder)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"unmarshal btfEnum %d, id: %d: %w\", i, id, err)\n\t\t\t\t}\n\t\t\t\tpos = pos[n:]\n\n\t\t\t\tname, err := d.strings.Lookup(be.NameOff)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"get name for enum value %d: %s\", i, err)\n\t\t\t\t}\n\n\t\t\t\tvalue := uint64(be.Val)\n\t\t\t\tif enum.Signed {\n\t\t\t\t\t// Sign extend values to 64 bit.\n\t\t\t\t\tvalue = uint64(int32(be.Val))\n\t\t\t\t}\n\t\t\t\tenum.Values = append(enum.Values, EnumValue{name, value})\n\t\t\t}\n\n\t\tcase kindForward:\n\t\t\ttyp = &Fwd{name, header.FwdKind()}\n\t\t\td.types[id] = typ\n\n\t\tcase kindTypedef:\n\t\t\ttypedef := &Typedef{name, nil, nil}\n\t\t\td.types[id] = typedef\n\n\t\t\tfixup(header.Type(), &typedef.Type)\n\t\t\ttyp = typedef\n\n\t\tcase kindVolatile:\n\t\t\tvolatile := &Volatile{nil}\n\t\t\td.types[id] = volatile\n\n\t\t\tfixup(header.Type(), &volatile.Type)\n\t\t\ttyp = volatile\n\n\t\tcase kindConst:\n\t\t\tcnst := &Const{nil}\n\t\t\td.types[id] = cnst\n\n\t\t\tfixup(header.Type(), &cnst.Type)\n\t\t\ttyp = cnst\n\n\t\tcase kindRestrict:\n\t\t\trestrict := &Restrict{nil}\n\t\t\td.types[id] = restrict\n\n\t\t\tfixup(header.Type(), &restrict.Type)\n\t\t\ttyp = restrict\n\n\t\tcase kindFunc:\n\t\t\tfn := &Func{name, nil, header.Linkage(), nil, nil}\n\t\t\td.types[id] = fn\n\n\t\t\tfixup(header.Type(), &fn.Type)\n\t\t\ttyp = fn\n\n\t\tcase kindFuncProto:\n\t\t\tfp := &FuncProto{}\n\t\t\td.types[id] = fp\n\n\t\t\tparams := make([]FuncParam, 0, header.Vlen())\n\t\t\tvar bParam btfParam\n\t\t\tfor i := range header.Vlen() {\n\t\t\t\tn, err := unmarshalBtfParam(&bParam, pos, d.byteOrder)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"can't unmarshal btfParam %d, id: %d: %w\", i, id, err)\n\t\t\t\t}\n\t\t\t\tpos = pos[n:]\n\n\t\t\t\tname, err := d.strings.Lookup(bParam.NameOff)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"get name for func proto parameter %d: %s\", i, err)\n\t\t\t\t}\n\n\t\t\t\tparam := FuncParam{Name: name}\n\t\t\t\tfixup(bParam.Type, &param.Type)\n\t\t\t\tparams = append(params, param)\n\t\t\t}\n\n\t\t\tfixup(header.Type(), &fp.Return)\n\t\t\tfp.Params = params\n\t\t\ttyp = fp\n\n\t\tcase kindVar:\n\t\t\tif _, err := unmarshalBtfVariable(&bVariable, pos, d.byteOrder); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"can't read btfVariable, id: %d: %w\", id, err)\n\t\t\t}\n\n\t\t\tv := &Var{name, nil, VarLinkage(bVariable.Linkage), nil}\n\t\t\td.types[id] = v\n\n\t\t\tfixup(header.Type(), &v.Type)\n\t\t\ttyp = v\n\n\t\tcase kindDatasec:\n\t\t\tds := &Datasec{name, header.Size(), nil}\n\t\t\td.types[id] = ds\n\n\t\t\tvlen := header.Vlen()\n\t\t\tvars := make([]VarSecinfo, 0, vlen)\n\t\t\tvar bSecInfo btfVarSecinfo\n\t\t\tfor i := 0; i < vlen; i++ {\n\t\t\t\tn, err := unmarshalBtfVarSecInfo(&bSecInfo, pos, d.byteOrder)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"can't unmarshal btfVarSecinfo %d, id: %d: %w\", i, id, err)\n\t\t\t\t}\n\t\t\t\tpos = pos[n:]\n\n\t\t\t\tvs := VarSecinfo{\n\t\t\t\t\tOffset: bSecInfo.Offset,\n\t\t\t\t\tSize:   bSecInfo.Size,\n\t\t\t\t}\n\t\t\t\tfixup(bSecInfo.Type, &vs.Type)\n\t\t\t\tvars = append(vars, vs)\n\t\t\t}\n\t\t\tds.Vars = vars\n\t\t\ttyp = ds\n\n\t\tcase kindFloat:\n\t\t\ttyp = &Float{name, header.Size()}\n\t\t\td.types[id] = typ\n\n\t\tcase kindDeclTag:\n\t\t\tif _, err := unmarshalBtfDeclTag(&bDeclTag, pos, d.byteOrder); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"can't read btfDeclTag, id: %d: %w\", id, err)\n\t\t\t}\n\n\t\t\tbtfIndex := bDeclTag.ComponentIdx\n\t\t\tif uint64(btfIndex) > math.MaxInt {\n\t\t\t\treturn nil, fmt.Errorf(\"type id %d: index exceeds int\", id)\n\t\t\t}\n\n\t\t\tdt := &declTag{nil, name, int(int32(btfIndex))}\n\t\t\td.types[id] = dt\n\n\t\t\tfixup(header.Type(), &dt.Type)\n\t\t\ttyp = dt\n\n\t\tcase kindTypeTag:\n\t\t\ttt := &TypeTag{nil, name}\n\t\t\td.types[id] = tt\n\n\t\t\tfixup(header.Type(), &tt.Type)\n\t\t\ttyp = tt\n\n\t\tcase kindEnum64:\n\t\t\tenum := &Enum{name, header.Size(), header.Signed(), nil}\n\t\t\td.types[id] = enum\n\t\t\ttyp = enum\n\n\t\t\tenum.Values = make([]EnumValue, 0, header.Vlen())\n\t\t\tvar bEnum64 btfEnum64\n\t\t\tfor i := range header.Vlen() {\n\t\t\t\tn, err := unmarshalBtfEnum64(&bEnum64, pos, d.byteOrder)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"can't unmarshal btfEnum64 %d, id: %d: %w\", i, id, err)\n\t\t\t\t}\n\t\t\t\tpos = pos[n:]\n\n\t\t\t\tname, err := d.strings.Lookup(bEnum64.NameOff)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"get name for enum64 value %d: %s\", i, err)\n\t\t\t\t}\n\t\t\t\tvalue := (uint64(bEnum64.ValHi32) << 32) | uint64(bEnum64.ValLo32)\n\t\t\t\tenum.Values = append(enum.Values, EnumValue{name, value})\n\t\t\t}\n\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"type id %d: unknown kind: %v\", id, header.Kind())\n\t\t}\n\t}\n\n\tfor _, tagID := range d.declTags[id] {\n\t\tdtType, err := d.inflateType(tagID)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tdt, ok := dtType.(*declTag)\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"type id %v: not a declTag\", tagID)\n\t\t}\n\n\t\tswitch t := typ.(type) {\n\t\tcase *Var:\n\t\t\tif dt.Index != -1 {\n\t\t\t\treturn nil, fmt.Errorf(\"type %s: component idx %d is not -1\", dt, dt.Index)\n\t\t\t}\n\t\t\tt.Tags = append(t.Tags, dt.Value)\n\n\t\tcase *Typedef:\n\t\t\tif dt.Index != -1 {\n\t\t\t\treturn nil, fmt.Errorf(\"type %s: component idx %d is not -1\", dt, dt.Index)\n\t\t\t}\n\t\t\tt.Tags = append(t.Tags, dt.Value)\n\n\t\tcase composite:\n\t\t\tif dt.Index >= 0 {\n\t\t\t\tmembers := t.members()\n\t\t\t\tif dt.Index >= len(members) {\n\t\t\t\t\treturn nil, fmt.Errorf(\"type %s: component idx %d exceeds members of %s\", dt, dt.Index, t)\n\t\t\t\t}\n\n\t\t\t\tmembers[dt.Index].Tags = append(members[dt.Index].Tags, dt.Value)\n\t\t\t} else if dt.Index == -1 {\n\t\t\t\tswitch t2 := t.(type) {\n\t\t\t\tcase *Struct:\n\t\t\t\t\tt2.Tags = append(t2.Tags, dt.Value)\n\t\t\t\tcase *Union:\n\t\t\t\t\tt2.Tags = append(t2.Tags, dt.Value)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn nil, fmt.Errorf(\"type %s: decl tag for type %s has invalid component idx\", dt, t)\n\t\t\t}\n\n\t\tcase *Func:\n\t\t\tfp, ok := t.Type.(*FuncProto)\n\t\t\tif !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"type %s: %s is not a FuncProto\", dt, t.Type)\n\t\t\t}\n\n\t\t\t// Ensure the number of argument tag lists equals the number of arguments\n\t\t\tif len(t.ParamTags) == 0 {\n\t\t\t\tt.ParamTags = make([][]string, len(fp.Params))\n\t\t\t}\n\n\t\t\tif dt.Index >= 0 {\n\t\t\t\tif dt.Index >= len(fp.Params) {\n\t\t\t\t\treturn nil, fmt.Errorf(\"type %s: component idx %d exceeds params of %s\", dt, dt.Index, t)\n\t\t\t\t}\n\n\t\t\t\tt.ParamTags[dt.Index] = append(t.ParamTags[dt.Index], dt.Value)\n\t\t\t} else if dt.Index == -1 {\n\t\t\t\tt.Tags = append(t.Tags, dt.Value)\n\t\t\t} else {\n\t\t\t\treturn nil, fmt.Errorf(\"type %s: decl tag for type %s has invalid component idx\", dt, t)\n\t\t\t}\n\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"type %s: decl tag for type %s is not supported\", dt, t)\n\t\t}\n\t}\n\n\treturn typ, nil\n}\n\n// An index from string to TypeID.\n//\n// Fuzzy because it may return false positive matches.\ntype fuzzyStringIndex struct {\n\tseed    maphash.Seed\n\tentries []fuzzyStringIndexEntry\n}\n\nfunc newFuzzyStringIndex(capacity int) *fuzzyStringIndex {\n\treturn &fuzzyStringIndex{\n\t\tmaphash.MakeSeed(),\n\t\tmake([]fuzzyStringIndexEntry, 0, capacity),\n\t}\n}\n\n// Add a string to the index.\n//\n// Calling the method with identical arguments will create duplicate entries.\nfunc (idx *fuzzyStringIndex) Add(name []byte, id TypeID) {\n\thash := uint32(maphash.Bytes(idx.seed, name))\n\tidx.entries = append(idx.entries, newFuzzyStringIndexEntry(hash, id))\n}\n\n// Build the index.\n//\n// Must be called after [Add] and before [Match].\nfunc (idx *fuzzyStringIndex) Build() {\n\tslices.Sort(idx.entries)\n}\n\n// Find TypeIDs which may match the name.\n//\n// May return false positives, but is guaranteed to not have false negatives.\n//\n// You must call [Build] at least once before calling this method.\nfunc (idx *fuzzyStringIndex) Find(name string) iter.Seq[TypeID] {\n\treturn func(yield func(TypeID) bool) {\n\t\thash := uint32(maphash.String(idx.seed, name))\n\n\t\t// We match only on the first 32 bits here, so ignore found.\n\t\ti, _ := slices.BinarySearch(idx.entries, fuzzyStringIndexEntry(hash)<<32)\n\t\tfor i := i; i < len(idx.entries); i++ {\n\t\t\tif idx.entries[i].hash() != hash {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tif !yield(idx.entries[i].id()) {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Tuple mapping the hash of an essential name to a type.\n//\n// Encoded in an uint64 so that it implements cmp.Ordered.\ntype fuzzyStringIndexEntry uint64\n\nfunc newFuzzyStringIndexEntry(hash uint32, id TypeID) fuzzyStringIndexEntry {\n\treturn fuzzyStringIndexEntry(hash)<<32 | fuzzyStringIndexEntry(id)\n}\n\nfunc (e fuzzyStringIndexEntry) hash() uint32 {\n\treturn uint32(e >> 32)\n}\n\nfunc (e fuzzyStringIndexEntry) id() TypeID {\n\treturn TypeID(e)\n}\n"
  },
  {
    "path": "btf/unmarshal_test.go",
    "content": "package btf\n\nimport (\n\t\"iter\"\n\t\"math\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestFuzzyStringIndex(t *testing.T) {\n\tidx := newFuzzyStringIndex(10)\n\tcount := testing.AllocsPerRun(1, func() {\n\t\tidx.Add([]byte(\"foo\"), 1)\n\t})\n\tqt.Assert(t, qt.Equals(count, 0))\n\n\tidx.entries = idx.entries[:0]\n\tidx.Add([]byte(\"foo\"), 1)\n\tidx.Add([]byte(\"bar\"), 2)\n\tidx.Add([]byte(\"baz\"), 3)\n\tidx.Build()\n\n\tall := func(it iter.Seq[TypeID]) (ids []TypeID) {\n\t\tfor id := range it {\n\t\t\tids = append(ids, id)\n\t\t}\n\t\treturn\n\t}\n\n\tqt.Assert(t, qt.SliceContains(all(idx.Find(\"foo\")), 1))\n\tqt.Assert(t, qt.SliceContains(all(idx.Find(\"bar\")), 2))\n\tqt.Assert(t, qt.SliceContains(all(idx.Find(\"baz\")), 3))\n\n\tqt.Assert(t, qt.IsTrue(newFuzzyStringIndexEntry(0, math.MaxUint32) < newFuzzyStringIndexEntry(1, 0)))\n}\n"
  },
  {
    "path": "btf/workarounds.go",
    "content": "package btf\n\n// datasecResolveWorkaround ensures that certain vars in a Datasec are added\n// to a Spec before the Datasec. This avoids a bug in kernel BTF validation.\n//\n// See https://lore.kernel.org/bpf/20230302123440.1193507-1-lmb@isovalent.com/\nfunc datasecResolveWorkaround(b *Builder, ds *Datasec) error {\n\tfor _, vsi := range ds.Vars {\n\t\tv, ok := vsi.Type.(*Var)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tswitch v.Type.(type) {\n\t\tcase *Typedef, *Volatile, *Const, *Restrict, *TypeTag:\n\t\t\t// NB: We must never call Add on a Datasec, otherwise we risk\n\t\t\t// infinite recursion.\n\t\t\t_, err := b.Add(v.Type)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "btf/workarounds_test.go",
    "content": "package btf\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestDatasecResolveWorkaround(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.2\", \"BTF_KIND_DATASEC\")\n\n\ti := &Int{Size: 1}\n\n\tfor _, typ := range []Type{\n\t\t&Typedef{\"foo\", i, nil},\n\t\t&Volatile{i},\n\t\t&Const{i},\n\t\t&Restrict{i},\n\t\t&TypeTag{i, \"foo\"},\n\t} {\n\t\tt.Run(fmt.Sprint(typ), func(t *testing.T) {\n\t\t\tif _, ok := typ.(*TypeTag); ok {\n\t\t\t\ttestutils.SkipOnOldKernel(t, \"5.17\", \"BTF_KIND_TYPE_TAG\")\n\t\t\t}\n\n\t\t\tds := &Datasec{\n\t\t\t\tName: \"a\",\n\t\t\t\tSize: 2,\n\t\t\t\tVars: []VarSecinfo{\n\t\t\t\t\t{\n\t\t\t\t\t\tSize:   1,\n\t\t\t\t\t\tOffset: 0,\n\t\t\t\t\t\t// struct, union, pointer, array will trigger the bug.\n\t\t\t\t\t\tType: &Var{Name: \"a\", Type: &Pointer{i}},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tSize:   1,\n\t\t\t\t\t\tOffset: 1,\n\t\t\t\t\t\tType: &Var{\n\t\t\t\t\t\t\tName: \"b\",\n\t\t\t\t\t\t\tType: typ,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tb, err := NewBuilder([]Type{ds}, nil)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\n\t\t\th, err := NewHandle(b)\n\t\t\ttestutils.SkipIfNotSupportedOnOS(t, err)\n\t\t\tvar ve *internal.VerifierError\n\t\t\tif errors.As(err, &ve) {\n\t\t\t\tt.Fatalf(\"%+v\\n\", ve)\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\th.Close()\n\t\t})\n\t}\n}\n\nfunc TestEmptyBTFWithStringTableWorkaround(t *testing.T) {\n\tvar b Builder\n\n\t_, err := b.addString(\"foo\")\n\tqt.Assert(t, qt.IsNil(err))\n\n\th, err := NewHandle(&b)\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.IsNil(h.Close()))\n}\n"
  },
  {
    "path": "cmd/bpf2go/README.md",
    "content": "bpf2go\n===\n\n`bpf2go` compiles a C source file into eBPF bytecode and then emits a\nGo file containing the eBPF. The goal is to avoid loading the\neBPF from disk at runtime and to minimise the amount of manual\nwork required to interact with eBPF programs. It takes inspiration\nfrom `bpftool gen skeleton`.\n\nAdd `bpf2go` as a tool dependency in your project's Go module:\n\n    go get -tool github.com/cilium/ebpf/cmd/bpf2go\n\nInvoke the tool using go generate:\n\n    //go:generate go tool bpf2go foo path/to/src.c -- -I/path/to/include\n\nThis will emit `foo_bpfel.go` and `foo_bpfeb.go`, with types using `foo`\nas a stem. The two files contain compiled BPF for little and big\nendian systems, respectively.\n\n## Environment Variables\n\nYou can use environment variables to affect all bpf2go invocations\nacross a project, e.g. to set specific C flags:\n\n    BPF2GO_CFLAGS=\"-O2 -g -Wall -Werror $(CFLAGS)\" go generate ./...\n\nAlternatively, by exporting `$BPF2GO_CFLAGS` from your build system, you can\ncontrol all builds from a single location.\n\nMost bpf2go arguments can be controlled this way. See `bpf2go -h` for an\nup-to-date list.\n\n## Generated types\n\n`bpf2go` generates Go types for all map keys and values by default. You can\ndisable this behaviour using `-no-global-types`. You can add to the set of\ntypes by specifying `-type foo` for each type you'd like to generate.\n\n## Examples\n\nSee [examples/kprobe](../../examples/kprobe/main.go) for a fully worked out example.\n"
  },
  {
    "path": "cmd/bpf2go/doc.go",
    "content": "//go:build !windows\n\n// Program bpf2go embeds eBPF in Go.\n//\n// Please see the README for details how to use it.\npackage main\n"
  },
  {
    "path": "cmd/bpf2go/flags.go",
    "content": "//go:build !windows\n\npackage main\n\nimport (\n\t\"flag\"\n\t\"go/build/constraint\"\n)\n\n// buildTags is a comma-separated list of build tags.\n//\n// This follows the pre-Go 1.17 syntax and is kept for compatibility reasons.\ntype buildTags struct {\n\tExpr constraint.Expr\n}\n\nvar _ flag.Value = (*buildTags)(nil)\n\nfunc (bt *buildTags) String() string {\n\tif bt.Expr == nil {\n\t\treturn \"\"\n\t}\n\n\treturn (bt.Expr).String()\n}\n\nfunc (bt *buildTags) Set(value string) error {\n\tct, err := constraint.Parse(\"// +build \" + value)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tbt.Expr = ct\n\treturn nil\n}\n\nfunc andConstraints(x, y constraint.Expr) constraint.Expr {\n\tif x == nil {\n\t\treturn y\n\t}\n\n\tif y == nil {\n\t\treturn x\n\t}\n\n\treturn &constraint.AndExpr{X: x, Y: y}\n}\n"
  },
  {
    "path": "cmd/bpf2go/gen/compile.go",
    "content": "//go:build !windows\n\npackage gen\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\ntype CompileArgs struct {\n\t// Which compiler to use.\n\tCC string\n\t// Command used to strip DWARF from the ELF.\n\tStrip string\n\t// Flags to pass to the compiler. This may contain positional arguments as well.\n\tFlags []string\n\t// Absolute working directory\n\tWorkdir string\n\t// Absolute input file name\n\tSource string\n\t// Absolute output file name\n\tDest string\n\t// Target to compile for, defaults to compiling generic BPF in host endianness.\n\tTarget           Target\n\tDisableStripping bool\n}\n\nfunc insertDefaultFlags(flags []string) []string {\n\t// Default cflags that can be overridden by the user.\n\toverrideFlags := []string{\n\t\t// Code needs to be optimized, otherwise the verifier will often fail\n\t\t// to understand it.\n\t\t\"-O2\",\n\t\t// Clang defaults to mcpu=probe which checks the kernel that we are\n\t\t// compiling on. This isn't appropriate for ahead of time\n\t\t// compiled code so force the most compatible version.\n\t\t\"-mcpu=v1\",\n\t}\n\n\tinsert := 0\n\n\t// Find the first non-positional argument to support CC commands with\n\t// multiple components. E.g.: BPF2GO_CC=\"ccache clang\" ...\n\tfor ; insert < len(flags); insert++ {\n\t\tif strings.HasPrefix(flags[insert], \"-\") {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tresult := append([]string(nil), flags[:insert]...)\n\tresult = append(result, overrideFlags...)\n\tresult = append(result, flags[insert:]...)\n\n\treturn result\n}\n\n// Compile C to a BPF ELF file.\nfunc Compile(args CompileArgs) error {\n\tcmd := exec.Command(args.CC, insertDefaultFlags(args.Flags)...)\n\tcmd.Stderr = os.Stderr\n\n\tinputDir := filepath.Dir(args.Source)\n\trelInputDir, err := filepath.Rel(args.Workdir, inputDir)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\ttarget := args.Target\n\tif target == (Target{}) {\n\t\ttarget.clang = \"bpf\"\n\t}\n\n\t// C flags that can't be overridden.\n\tif linux := target.linux; linux != \"\" {\n\t\tcmd.Args = append(cmd.Args, \"-D__TARGET_ARCH_\"+linux)\n\t}\n\n\tcmd.Args = append(cmd.Args,\n\t\t\"-Wunused-command-line-argument\",\n\t\t\"-target\", target.clang,\n\t\t\"-c\", args.Source,\n\t\t\"-o\", args.Dest,\n\t\t// Don't include clang version\n\t\t\"-fno-ident\",\n\t\t// Don't output inputDir into debug info\n\t\t\"-fdebug-prefix-map=\"+inputDir+\"=\"+relInputDir,\n\t\t\"-fdebug-compilation-dir\", \".\",\n\t\t// We always want BTF to be generated, so enforce debug symbols\n\t\t\"-g\",\n\t\tfmt.Sprintf(\"-D__BPF_TARGET_MISSING=%q\", \"GCC error \\\"The eBPF is using target specific macros, please provide -target that is not bpf, bpfel or bpfeb\\\"\"),\n\t)\n\tcmd.Dir = args.Workdir\n\n\tif err := cmd.Run(); err != nil {\n\t\treturn err\n\t}\n\n\tif args.DisableStripping {\n\t\treturn nil\n\t}\n\n\tcmd = exec.Command(args.Strip, \"-g\", args.Dest)\n\tcmd.Stderr = os.Stderr\n\tif err := cmd.Run(); err != nil {\n\t\treturn fmt.Errorf(\"strip %s: %w\", args.Dest, err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "cmd/bpf2go/gen/compile_test.go",
    "content": "//go:build !windows\n\npackage gen\n\nimport (\n\t\"bytes\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nconst minimalSocketFilter = `__attribute__((section(\"socket\"), used)) int main() { return 0; }`\n\nfunc TestCompile(t *testing.T) {\n\tif testing.Short() {\n\t\tt.SkipNow()\n\t}\n\n\tdir := t.TempDir()\n\tmustWriteFile(t, dir, \"test.c\", minimalSocketFilter)\n\n\terr := Compile(CompileArgs{\n\t\tCC:               testutils.ClangBin(t),\n\t\tDisableStripping: true,\n\t\tWorkdir:          dir,\n\t\tSource:           filepath.Join(dir, \"test.c\"),\n\t\tDest:             filepath.Join(dir, \"test.o\"),\n\t})\n\tif err != nil {\n\t\tt.Fatal(\"Can't compile:\", err)\n\t}\n\n\tstat, err := os.Stat(filepath.Join(dir, \"test.o\"))\n\tif err != nil {\n\t\tt.Fatal(\"Can't stat output:\", err)\n\t}\n\n\tif stat.Size() == 0 {\n\t\tt.Error(\"Compilation creates an empty file\")\n\t}\n}\n\nfunc TestReproducibleCompile(t *testing.T) {\n\tif testing.Short() {\n\t\tt.SkipNow()\n\t}\n\n\tclangBin := testutils.ClangBin(t)\n\tdir := t.TempDir()\n\tmustWriteFile(t, dir, \"test.c\", minimalSocketFilter)\n\n\terr := Compile(CompileArgs{\n\t\tCC:               clangBin,\n\t\tDisableStripping: true,\n\t\tWorkdir:          dir,\n\t\tSource:           filepath.Join(dir, \"test.c\"),\n\t\tDest:             filepath.Join(dir, \"a.o\"),\n\t})\n\tif err != nil {\n\t\tt.Fatal(\"Can't compile:\", err)\n\t}\n\n\terr = Compile(CompileArgs{\n\t\tCC:               clangBin,\n\t\tDisableStripping: true,\n\t\tWorkdir:          dir,\n\t\tSource:           filepath.Join(dir, \"test.c\"),\n\t\tDest:             filepath.Join(dir, \"b.o\"),\n\t})\n\tif err != nil {\n\t\tt.Fatal(\"Can't compile:\", err)\n\t}\n\n\taBytes, err := os.ReadFile(filepath.Join(dir, \"a.o\"))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tbBytes, err := os.ReadFile(filepath.Join(dir, \"b.o\"))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif !bytes.Equal(aBytes, bBytes) {\n\t\tt.Error(\"Compiling the same file twice doesn't give the same result\")\n\t}\n}\n\nfunc TestTriggerMissingTarget(t *testing.T) {\n\tif testing.Short() {\n\t\tt.SkipNow()\n\t}\n\n\tdir := t.TempDir()\n\tmustWriteFile(t, dir, \"test.c\", `_Pragma(__BPF_TARGET_MISSING);`)\n\n\terr := Compile(CompileArgs{\n\t\tCC:      testutils.ClangBin(t),\n\t\tWorkdir: dir,\n\t\tSource:  filepath.Join(dir, \"test.c\"),\n\t\tDest:    filepath.Join(dir, \"a.o\"),\n\t})\n\n\tif err == nil {\n\t\tt.Fatal(\"No error when compiling __BPF_TARGET_MISSING\")\n\t}\n}\n\nfunc mustWriteFile(tb testing.TB, dir, name, contents string) {\n\ttb.Helper()\n\ttmpFile := filepath.Join(dir, name)\n\tif err := os.WriteFile(tmpFile, []byte(contents), 0660); err != nil {\n\t\ttb.Fatal(err)\n\t}\n}\n"
  },
  {
    "path": "cmd/bpf2go/gen/doc.go",
    "content": "// Package gen contains utilities to generate Go bindings for eBPF ELF files.\npackage gen\n"
  },
  {
    "path": "cmd/bpf2go/gen/output.go",
    "content": "//go:build !windows\n\npackage gen\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"go/build/constraint\"\n\t\"go/token\"\n\t\"io\"\n\t\"sort\"\n\t\"strings\"\n\t\"text/template\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n\n\t\"github.com/cilium/ebpf/btf\"\n\tb2gInt \"github.com/cilium/ebpf/cmd/bpf2go/internal\"\n\t\"github.com/cilium/ebpf/internal\"\n)\n\n//go:embed output.tpl\nvar commonRaw string\n\nvar commonTemplate = template.Must(template.New(\"common\").Parse(commonRaw))\n\ntype templateName string\n\nfunc (n templateName) maybeExport(str string) string {\n\tif token.IsExported(string(n)) {\n\t\treturn toUpperFirst(str)\n\t}\n\n\treturn str\n}\n\nfunc (n templateName) Bytes() string {\n\treturn \"_\" + toUpperFirst(string(n)) + \"Bytes\"\n}\n\nfunc (n templateName) Specs() string {\n\treturn string(n) + \"Specs\"\n}\n\nfunc (n templateName) ProgramSpecs() string {\n\treturn string(n) + \"ProgramSpecs\"\n}\n\nfunc (n templateName) MapSpecs() string {\n\treturn string(n) + \"MapSpecs\"\n}\n\nfunc (n templateName) VariableSpecs() string {\n\treturn string(n) + \"VariableSpecs\"\n}\n\nfunc (n templateName) Load() string {\n\treturn n.maybeExport(\"load\" + toUpperFirst(string(n)))\n}\n\nfunc (n templateName) LoadObjects() string {\n\treturn n.maybeExport(\"load\" + toUpperFirst(string(n)) + \"Objects\")\n}\n\nfunc (n templateName) Objects() string {\n\treturn string(n) + \"Objects\"\n}\n\nfunc (n templateName) Maps() string {\n\treturn string(n) + \"Maps\"\n}\n\nfunc (n templateName) Variables() string {\n\treturn string(n) + \"Variables\"\n}\n\nfunc (n templateName) Programs() string {\n\treturn string(n) + \"Programs\"\n}\n\nfunc (n templateName) CloseHelper() string {\n\treturn \"_\" + toUpperFirst(string(n)) + \"Close\"\n}\n\ntype GenerateArgs struct {\n\t// Package of the resulting file.\n\tPackage string\n\t// The prefix of all names declared at the top-level.\n\tStem string\n\t// Build Constraints included in the resulting file.\n\tConstraints constraint.Expr\n\t// Maps to be emitted.\n\tMaps []string\n\t// Variables to be emitted.\n\tVariables []string\n\t// Programs to be emitted.\n\tPrograms []string\n\t// Types to be emitted.\n\tTypes []btf.Type\n\t// Filename of the object to embed.\n\tObjectFile string\n\t// Output to write template to.\n\tOutput io.Writer\n\t// Function which transforms the input into a valid go identifier. Uses the default behaviour if nil\n\tIdentifier func(string) string\n}\n\n// Generate bindings for a BPF ELF file.\nfunc Generate(args GenerateArgs) error {\n\tif args.Identifier == nil {\n\t\targs.Identifier = internal.Identifier\n\t}\n\tif !token.IsIdentifier(args.Stem) {\n\t\treturn fmt.Errorf(\"%q is not a valid identifier\", args.Stem)\n\t}\n\n\tif strings.ContainsAny(args.ObjectFile, \"\\n\") {\n\t\t// Prevent injecting newlines into the template.\n\t\treturn fmt.Errorf(\"file %q contains an invalid character\", args.ObjectFile)\n\t}\n\n\tfor _, typ := range args.Types {\n\t\tif _, ok := btf.As[*btf.Datasec](typ); ok {\n\t\t\t// Avoid emitting .rodata, .bss, etc. for now. We might want to\n\t\t\t// name these types differently, etc.\n\t\t\treturn fmt.Errorf(\"can't output btf.Datasec: %s\", typ)\n\t\t}\n\t}\n\n\tmaps := make(map[string]string)\n\tfor _, name := range args.Maps {\n\t\tmaps[name] = args.Identifier(name)\n\t}\n\n\tvariables := make(map[string]string)\n\tfor _, name := range args.Variables {\n\t\tvariables[name] = args.Identifier(name)\n\t}\n\n\tprograms := make(map[string]string)\n\tfor _, name := range args.Programs {\n\t\tprograms[name] = args.Identifier(name)\n\t}\n\n\ttypeNames := make(map[btf.Type]string)\n\tfor _, typ := range args.Types {\n\t\t// NB: This also deduplicates types.\n\t\ttypeNames[typ] = args.Stem + args.Identifier(typ.TypeName())\n\t}\n\n\t// Ensure we don't have conflicting names and generate a sorted list of\n\t// named types so that the output is stable.\n\ttypes, err := sortTypes(typeNames)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tgf := &btf.GoFormatter{\n\t\tNames:      typeNames,\n\t\tIdentifier: args.Identifier,\n\t}\n\n\tvar typeDecls []string\n\tneedsStructsPkg := false\n\tfor _, typ := range types {\n\t\tname := typeNames[typ]\n\t\tdecl, err := gf.TypeDeclaration(name, typ)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"generating %s: %w\", name, err)\n\t\t}\n\t\t_, ok := btf.As[*btf.Struct](typ)\n\t\tneedsStructsPkg = needsStructsPkg || ok\n\t\ttypeDecls = append(typeDecls, decl)\n\t}\n\n\tctx := struct {\n\t\tModule           string\n\t\tPackage          string\n\t\tConstraints      constraint.Expr\n\t\tName             templateName\n\t\tMaps             map[string]string\n\t\tVariables        map[string]string\n\t\tPrograms         map[string]string\n\t\tTypeDeclarations []string\n\t\tFile             string\n\t\tNeedsStructsPkg  bool\n\t}{\n\t\tb2gInt.CurrentModule,\n\t\targs.Package,\n\t\targs.Constraints,\n\t\ttemplateName(args.Stem),\n\t\tmaps,\n\t\tvariables,\n\t\tprograms,\n\t\ttypeDecls,\n\t\targs.ObjectFile,\n\t\tneedsStructsPkg,\n\t}\n\n\tvar buf bytes.Buffer\n\tif err := commonTemplate.Execute(&buf, &ctx); err != nil {\n\t\treturn fmt.Errorf(\"can't generate types: %s\", err)\n\t}\n\n\treturn internal.WriteFormatted(buf.Bytes(), args.Output)\n}\n\n// sortTypes returns a list of types sorted by their (generated) Go type name.\n//\n// Duplicate Go type names are rejected.\nfunc sortTypes(typeNames map[btf.Type]string) ([]btf.Type, error) {\n\tvar types []btf.Type\n\tvar names []string\n\tfor typ, name := range typeNames {\n\t\ti := sort.SearchStrings(names, name)\n\t\tif i >= len(names) {\n\t\t\ttypes = append(types, typ)\n\t\t\tnames = append(names, name)\n\t\t\tcontinue\n\t\t}\n\n\t\tif names[i] == name {\n\t\t\treturn nil, fmt.Errorf(\"type name %q is used multiple times\", name)\n\t\t}\n\n\t\ttypes = append(types[:i], append([]btf.Type{typ}, types[i:]...)...)\n\t\tnames = append(names[:i], append([]string{name}, names[i:]...)...)\n\t}\n\n\treturn types, nil\n}\n\nfunc toUpperFirst(str string) string {\n\tfirst, n := utf8.DecodeRuneInString(str)\n\treturn string(unicode.ToUpper(first)) + str[n:]\n}\n"
  },
  {
    "path": "cmd/bpf2go/gen/output.tpl",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n{{ with .Constraints }}//go:build {{ . }}{{ end }}\n\npackage {{ .Package }}\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n{{- if .NeedsStructsPkg }}\n\t\"structs\"\n{{- end }}\n\n\t\"{{ .Module }}\"\n)\n\n{{- if .TypeDeclarations }}\n{{- range $type := .TypeDeclarations }}\n{{ $type }}\n\n{{ end }}\n{{- end }}\n\n// {{ .Name.Load }} returns the embedded CollectionSpec for {{ .Name }}.\nfunc {{ .Name.Load }}() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader({{ .Name.Bytes }})\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load {{ .Name }}: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// {{ .Name.LoadObjects }} loads {{ .Name }} and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*{{ .Name.Objects }}\n//\t*{{ .Name.Programs }}\n//\t*{{ .Name.Maps }}\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc {{ .Name.LoadObjects }}(obj interface{}, opts *ebpf.CollectionOptions) (error) {\n\tspec, err := {{ .Name.Load }}()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// {{ .Name.Specs }} contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype {{ .Name.Specs }} struct {\n\t{{ .Name.ProgramSpecs }}\n\t{{ .Name.MapSpecs }}\n\t{{ .Name.VariableSpecs }}\n}\n\n// {{ .Name.ProgramSpecs }} contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype {{ .Name.ProgramSpecs }} struct {\n{{- range $name, $id := .Programs }}\n\t{{ $id }} *ebpf.ProgramSpec `ebpf:\"{{ $name }}\"`\n{{- end }}\n}\n\n// {{ .Name.MapSpecs }} contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype {{ .Name.MapSpecs }} struct {\n{{- range $name, $id := .Maps }}\n\t{{ $id }} *ebpf.MapSpec `ebpf:\"{{ $name }}\"`\n{{- end }}\n}\n\n// {{ .Name.VariableSpecs }} contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype {{ .Name.VariableSpecs }} struct {\n{{- range $name, $id := .Variables }}\n\t{{ $id }} *ebpf.VariableSpec `ebpf:\"{{ $name }}\"`\n{{- end }}\n}\n\n// {{ .Name.Objects }} contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to {{ .Name.LoadObjects }} or ebpf.CollectionSpec.LoadAndAssign.\ntype {{ .Name.Objects }} struct {\n\t{{ .Name.Programs }}\n\t{{ .Name.Maps }}\n\t{{ .Name.Variables }}\n}\n\nfunc (o *{{ .Name.Objects }}) Close() error {\n\treturn {{ .Name.CloseHelper }}(\n\t\t&o.{{ .Name.Programs }},\n\t\t&o.{{ .Name.Maps }},\n\t)\n}\n\n// {{ .Name.Maps }} contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to {{ .Name.LoadObjects }} or ebpf.CollectionSpec.LoadAndAssign.\ntype {{ .Name.Maps }} struct {\n{{- range $name, $id := .Maps }}\n\t{{ $id }} *ebpf.Map `ebpf:\"{{ $name }}\"`\n{{- end }}\n}\n\nfunc (m *{{ .Name.Maps }}) Close() error {\n\treturn {{ .Name.CloseHelper }}(\n{{- range $id := .Maps }}\n\t\tm.{{ $id }},\n{{- end }}\n\t)\n}\n\n// {{ .Name.Variables }} contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to {{ .Name.LoadObjects }} or ebpf.CollectionSpec.LoadAndAssign.\ntype {{ .Name.Variables }} struct {\n{{- range $name, $id := .Variables }}\n\t{{ $id }} *ebpf.Variable `ebpf:\"{{ $name }}\"`\n{{- end }}\n}\n\n// {{ .Name.Programs }} contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to {{ .Name.LoadObjects }} or ebpf.CollectionSpec.LoadAndAssign.\ntype {{ .Name.Programs }} struct {\n{{- range $name, $id := .Programs }}\n\t{{ $id }} *ebpf.Program `ebpf:\"{{ $name }}\"`\n{{- end }}\n}\n\nfunc (p *{{ .Name.Programs }}) Close() error {\n\treturn {{ .Name.CloseHelper }}(\n{{- range $id := .Programs }}\n\t\tp.{{ $id }},\n{{- end }}\n\t)\n}\n\nfunc {{ .Name.CloseHelper }}(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//go:embed {{ .File }}\nvar {{ .Name.Bytes }} []byte\n"
  },
  {
    "path": "cmd/bpf2go/gen/output_test.go",
    "content": "//go:build !windows\n\npackage gen\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/cmd/bpf2go/internal\"\n)\n\nfunc TestOrderTypes(t *testing.T) {\n\ta := &btf.Int{}\n\tb := &btf.Int{}\n\tc := &btf.Int{}\n\n\tfor _, test := range []struct {\n\t\tname string\n\t\tin   map[btf.Type]string\n\t\tout  []btf.Type\n\t}{\n\t\t{\n\t\t\t\"order\",\n\t\t\tmap[btf.Type]string{\n\t\t\t\ta: \"foo\",\n\t\t\t\tb: \"bar\",\n\t\t\t\tc: \"baz\",\n\t\t\t},\n\t\t\t[]btf.Type{b, c, a},\n\t\t},\n\t} {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tresult, err := sortTypes(test.in)\n\t\t\tqt.Assert(t, qt.IsNil(err))\n\t\t\tqt.Assert(t, qt.Equals(len(result), len(test.out)))\n\t\t\tfor i, o := range test.out {\n\t\t\t\tif result[i] != o {\n\t\t\t\t\tt.Fatalf(\"Index %d: expected %p got %p\", i, o, result[i])\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n\n\tfor _, test := range []struct {\n\t\tname string\n\t\tin   map[btf.Type]string\n\t}{\n\t\t{\n\t\t\t\"duplicate names\",\n\t\t\tmap[btf.Type]string{\n\t\t\t\ta: \"foo\",\n\t\t\t\tb: \"foo\",\n\t\t\t},\n\t\t},\n\t} {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tresult, err := sortTypes(test.in)\n\t\t\tqt.Assert(t, qt.IsNotNil(err))\n\t\t\tqt.Assert(t, qt.IsNil(result))\n\t\t})\n\t}\n}\n\nfunc TestPackageImport(t *testing.T) {\n\tvar buf bytes.Buffer\n\terr := Generate(GenerateArgs{\n\t\tPackage:    \"foo\",\n\t\tStem:       \"bar\",\n\t\tObjectFile: \"frob.o\",\n\t\tOutput:     &buf,\n\t})\n\tqt.Assert(t, qt.IsNil(err))\n\t// NB: It'd be great to test that this is the case for callers outside of\n\t// this module, but that is kind of tricky.\n\tqt.Assert(t, qt.StringContains(buf.String(), fmt.Sprintf(`\"%s\"`, internal.CurrentModule)))\n}\n\nfunc TestCustomIdentifier(t *testing.T) {\n\tvar buf bytes.Buffer\n\targs := GenerateArgs{\n\t\tPackage:    \"foo\",\n\t\tStem:       \"bar\",\n\t\tObjectFile: \"frob.o\",\n\t\tOutput:     &buf,\n\t\tPrograms:   []string{\"do_thing\"},\n\t\tIdentifier: strings.ToUpper,\n\t}\n\terr := Generate(args)\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.StringContains(buf.String(), \"DO_THING\"))\n}\n\nfunc TestObjects(t *testing.T) {\n\tvar buf bytes.Buffer\n\targs := GenerateArgs{\n\t\tPackage:   \"foo\",\n\t\tStem:      \"bar\",\n\t\tMaps:      []string{\"map1\"},\n\t\tVariables: []string{\"var_1\"},\n\t\tPrograms:  []string{\"prog_foo_1\"},\n\t\tOutput:    &buf,\n\t}\n\terr := Generate(args)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tstr := buf.String()\n\n\tqt.Assert(t, qt.StringContains(str, \"Map1 *ebpf.MapSpec `ebpf:\\\"map1\\\"`\"))\n\tqt.Assert(t, qt.StringContains(str, \"Var1 *ebpf.VariableSpec `ebpf:\\\"var_1\\\"`\"))\n\tqt.Assert(t, qt.StringContains(str, \"ProgFoo1 *ebpf.ProgramSpec `ebpf:\\\"prog_foo_1\\\"`\"))\n\n\tqt.Assert(t, qt.StringContains(str, \"Map1 *ebpf.Map `ebpf:\\\"map1\\\"`\"))\n\tqt.Assert(t, qt.StringContains(str, \"Var1 *ebpf.Variable `ebpf:\\\"var_1\\\"`\"))\n\tqt.Assert(t, qt.StringContains(str, \"ProgFoo1 *ebpf.Program `ebpf:\\\"prog_foo_1\\\"`\"))\n}\n\nfunc TestGenerateStructTypes(t *testing.T) {\n\tts := &btf.Struct{\n\t\tName: \"test_struct\",\n\t\tSize: 8,\n\t\tMembers: []btf.Member{\n\t\t\t{\n\t\t\t\tName:   \"field1\",\n\t\t\t\tType:   &btf.Int{Size: 8, Encoding: btf.Unsigned},\n\t\t\t\tOffset: 0,\n\t\t\t},\n\t\t},\n\t}\n\ttd := &btf.Typedef{\n\t\tName: \"test_typedef\",\n\t\tType: ts,\n\t}\n\n\ttests := []struct {\n\t\tname     string\n\t\ttypes    []btf.Type\n\t\texpected string\n\t}{\n\t\t{\n\t\t\tname:     \"simple struct\",\n\t\t\ttypes:    []btf.Type{ts},\n\t\t\texpected: \"type stemTestStruct struct {\\n\\t_      structs.HostLayout\\n\\tField1 uint64\\n}\",\n\t\t},\n\t\t{\n\t\t\tname:     \"typedef struct\",\n\t\t\ttypes:    []btf.Type{td},\n\t\t\texpected: \"type stemTestTypedef struct {\\n\\t_      structs.HostLayout\\n\\tField1 uint64\\n}\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tvar buf bytes.Buffer\n\t\t\terr := Generate(GenerateArgs{\n\t\t\t\tPackage:     \"test\",\n\t\t\t\tStem:        \"stem\",\n\t\t\t\tTypes:       tt.types,\n\t\t\t\tOutput:      &buf,\n\t\t\t\tConstraints: nil,\n\t\t\t})\n\t\t\tqt.Assert(t, qt.IsNil(err))\n\n\t\t\tstr := buf.String()\n\t\t\tqt.Assert(t, qt.StringContains(str, tt.expected))\n\t\t\tqt.Assert(t, qt.StringContains(str, \"\\\"structs\\\"\"))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "cmd/bpf2go/gen/target.go",
    "content": "//go:build !windows\n\npackage gen\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"go/build/constraint\"\n\t\"maps\"\n\t\"runtime\"\n\t\"slices\"\n)\n\nvar ErrInvalidTarget = errors.New(\"unsupported target\")\n\nvar targetsByGoArch = map[GoArch]Target{\n\t\"386\":      {\"bpfel\", \"x86\", \"\"},\n\t\"amd64\":    {\"bpfel\", \"x86\", \"\"},\n\t\"arm\":      {\"bpfel\", \"arm\", \"\"},\n\t\"arm64\":    {\"bpfel\", \"arm64\", \"\"},\n\t\"loong64\":  {\"bpfel\", \"loongarch\", \"\"},\n\t\"mips\":     {\"bpfeb\", \"mips\", \"\"},\n\t\"mipsle\":   {\"bpfel\", \"\", \"\"},\n\t\"mips64\":   {\"bpfeb\", \"\", \"\"},\n\t\"mips64le\": {\"bpfel\", \"\", \"\"},\n\t\"ppc64\":    {\"bpfeb\", \"powerpc\", \"\"},\n\t\"ppc64le\":  {\"bpfel\", \"powerpc\", \"\"},\n\t\"riscv64\":  {\"bpfel\", \"riscv\", \"\"},\n\t\"s390x\":    {\"bpfeb\", \"s390\", \"\"},\n\t\"wasm\":     {\"bpfel\", \"\", \"js\"},\n}\n\ntype Target struct {\n\t// Clang arch string, used to define the clang -target flag, as per\n\t// \"clang -print-targets\".\n\tclang string\n\t// Linux arch string, used to define __TARGET_ARCH_xzy macros used by\n\t// https://github.com/libbpf/libbpf/blob/master/src/bpf_tracing.h\n\tlinux string\n\t// GOOS override for use during tests.\n\tgoos string\n}\n\n// TargetsByGoArch returns all supported targets.\nfunc TargetsByGoArch() map[GoArch]Target {\n\treturn maps.Clone(targetsByGoArch)\n}\n\n// IsGeneric returns true if the target will compile to generic BPF.\nfunc (tgt *Target) IsGeneric() bool {\n\treturn tgt.linux == \"\"\n}\n\n// Suffix returns a a string suitable for appending to a file name to\n// identify the target.\nfunc (tgt *Target) Suffix() string {\n\t// The output filename must not match any of the following patterns:\n\t//\n\t//     *_GOOS\n\t//     *_GOARCH\n\t//     *_GOOS_GOARCH\n\t//\n\t// Otherwise it is interpreted as a build constraint by the Go toolchain.\n\tstem := tgt.clang\n\tif tgt.linux != \"\" {\n\t\tstem = fmt.Sprintf(\"%s_%s\", tgt.linux, tgt.clang)\n\t}\n\treturn stem\n}\n\n// ObsoleteSuffix returns an obsolete suffix for a subset of targets.\n//\n// It's used to work around an old bug and should not be used in new code.\nfunc (tgt *Target) ObsoleteSuffix() string {\n\tif tgt.linux == \"\" {\n\t\treturn \"\"\n\t}\n\n\treturn fmt.Sprintf(\"%s_%s\", tgt.clang, tgt.linux)\n}\n\n// GoArch is a Go arch string.\n//\n// See https://go.dev/doc/install/source#environment for valid GOARCHes when\n// GOOS=linux.\ntype GoArch string\n\ntype GoArches []GoArch\n\n// Constraints is satisfied when GOARCH is any of the arches.\nfunc (arches GoArches) Constraint() constraint.Expr {\n\tvar archConstraint constraint.Expr\n\tfor _, goarch := range arches {\n\t\ttag := &constraint.TagExpr{Tag: string(goarch)}\n\t\tarchConstraint = orConstraints(archConstraint, tag)\n\t}\n\treturn archConstraint\n}\n\n// FindTarget turns a list of identifiers into targets and their respective\n// GoArches.\n//\n// The following are valid identifiers:\n//\n//   - bpf: compile generic BPF for host endianness\n//   - bpfel: compile generic BPF for little endian\n//   - bpfeb: compile generic BPF for big endian\n//   - native: compile BPF for host target\n//   - $GOARCH: compile BPF for $GOARCH target\n//\n// Generic BPF can run on any target goarch with the correct endianness,\n// but doesn't have access to some arch specific tracing functionality.\nfunc FindTarget(id string) (Target, GoArches, error) {\n\tswitch id {\n\tcase \"bpf\", \"bpfel\", \"bpfeb\":\n\t\tvar goarches []GoArch\n\t\tfor arch, archTarget := range targetsByGoArch {\n\t\t\tif archTarget.clang == id {\n\t\t\t\t// Include tags for all goarches that have the same endianness.\n\t\t\t\tgoarches = append(goarches, arch)\n\t\t\t}\n\t\t}\n\t\tslices.Sort(goarches)\n\t\treturn Target{id, \"\", \"\"}, goarches, nil\n\n\tcase \"native\":\n\t\tid = runtime.GOARCH\n\t\tfallthrough\n\n\tdefault:\n\t\tarchTarget, ok := targetsByGoArch[GoArch(id)]\n\t\tif !ok || archTarget.linux == \"\" {\n\t\t\treturn Target{}, nil, fmt.Errorf(\"%q: %w\", id, ErrInvalidTarget)\n\t\t}\n\n\t\tvar goarches []GoArch\n\t\tfor goarch, lt := range targetsByGoArch {\n\t\t\tif lt == archTarget {\n\t\t\t\t// Include tags for all goarches that have the same\n\t\t\t\t// target.\n\t\t\t\tgoarches = append(goarches, goarch)\n\t\t\t}\n\t\t}\n\n\t\tslices.Sort(goarches)\n\t\treturn archTarget, goarches, nil\n\t}\n}\n\nfunc orConstraints(x, y constraint.Expr) constraint.Expr {\n\tif x == nil {\n\t\treturn y\n\t}\n\n\tif y == nil {\n\t\treturn x\n\t}\n\n\treturn &constraint.OrExpr{X: x, Y: y}\n}\n"
  },
  {
    "path": "cmd/bpf2go/gen/target_test.go",
    "content": "//go:build !windows\n\npackage gen\n\nimport (\n\t\"errors\"\n\t\"os/exec\"\n\t\"slices\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestCollectTargets(t *testing.T) {\n\tclangArches := make(map[string][]GoArch)\n\tlinuxArchesLE := make(map[string][]GoArch)\n\tlinuxArchesBE := make(map[string][]GoArch)\n\tfor arch, archTarget := range targetsByGoArch {\n\t\tclangArches[archTarget.clang] = append(clangArches[archTarget.clang], arch)\n\t\tif archTarget.clang == \"bpfel\" {\n\t\t\tlinuxArchesLE[archTarget.linux] = append(linuxArchesLE[archTarget.linux], arch)\n\t\t\tcontinue\n\t\t}\n\t\tlinuxArchesBE[archTarget.linux] = append(linuxArchesBE[archTarget.linux], arch)\n\t}\n\tfor i := range clangArches {\n\t\tslices.Sort(clangArches[i])\n\t}\n\tfor i := range linuxArchesLE {\n\t\tslices.Sort(linuxArchesLE[i])\n\t}\n\tfor i := range linuxArchesBE {\n\t\tslices.Sort(linuxArchesBE[i])\n\t}\n\n\tnativeTarget, nativeArches, err := FindTarget(\"native\")\n\tqt.Assert(t, qt.IsNil(err))\n\n\ttests := []struct {\n\t\tshort  string\n\t\ttarget Target\n\t\tarches GoArches\n\t}{\n\t\t{\n\t\t\t\"bpf\",\n\t\t\tTarget{\"bpf\", \"\", \"\"},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"bpfel\",\n\t\t\tTarget{\"bpfel\", \"\", \"\"},\n\t\t\tclangArches[\"bpfel\"],\n\t\t},\n\t\t{\n\t\t\t\"bpfeb\",\n\t\t\tTarget{\"bpfeb\", \"\", \"\"},\n\t\t\tclangArches[\"bpfeb\"],\n\t\t},\n\t\t{\n\t\t\t\"amd64\",\n\t\t\tTarget{\"bpfel\", \"x86\", \"\"},\n\t\t\tlinuxArchesLE[\"x86\"],\n\t\t},\n\t\t{\n\t\t\t\"386\",\n\t\t\tTarget{\"bpfel\", \"x86\", \"\"},\n\t\t\tlinuxArchesLE[\"x86\"],\n\t\t},\n\t\t{\n\t\t\t\"ppc64\",\n\t\t\tTarget{\"bpfeb\", \"powerpc\", \"\"},\n\t\t\tlinuxArchesBE[\"powerpc\"],\n\t\t},\n\t\t{\n\t\t\t\"native\",\n\t\t\tnativeTarget,\n\t\t\tnativeArches,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.short, func(t *testing.T) {\n\t\t\ttarget, arches, err := FindTarget(test.short)\n\t\t\tqt.Assert(t, qt.IsNil(err))\n\t\t\tqt.Assert(t, qt.Equals(target, test.target))\n\t\t\tqt.Assert(t, qt.DeepEquals(arches, test.arches))\n\t\t})\n\t}\n}\n\nfunc TestCollectTargetsErrors(t *testing.T) {\n\ttests := []struct {\n\t\tname   string\n\t\ttarget string\n\t}{\n\t\t{\"unknown\", \"frood\"},\n\t\t{\"no linux target\", \"mipsle\"},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\t_, _, err := FindTarget(test.target)\n\t\t\tif err == nil {\n\t\t\t\tt.Fatal(\"Function did not return an error\")\n\t\t\t}\n\t\t\tt.Log(\"Error message:\", err)\n\t\t})\n\t}\n}\n\nfunc TestGoarches(t *testing.T) {\n\texe := goBin(t)\n\n\tfor GoArch, tgt := range targetsByGoArch {\n\t\tt.Run(string(GoArch), func(t *testing.T) {\n\t\t\tgoOS := \"linux\"\n\t\t\tif tgt.goos != \"\" {\n\t\t\t\tgoOS = tgt.goos\n\t\t\t}\n\t\t\tgoEnv := exec.Command(exe, \"env\")\n\t\t\tgoEnv.Env = []string{\"GOROOT=/\", \"GOOS=\" + string(goOS), \"GOARCH=\" + string(GoArch)}\n\t\t\toutput, err := goEnv.CombinedOutput()\n\t\t\tqt.Assert(t, qt.IsNil(err), qt.Commentf(\"go output is:\\n%s\", string(output)))\n\t\t})\n\t}\n}\n\nfunc TestClangTargets(t *testing.T) {\n\texe := goBin(t)\n\n\tclangTargets := map[string]struct{}{}\n\tfor _, tgt := range targetsByGoArch {\n\t\tclangTargets[tgt.clang] = struct{}{}\n\t}\n\n\tfor target := range clangTargets {\n\t\tfor _, env := range []string{\"GOOS\", \"GOARCH\"} {\n\t\t\tenv += \"=\" + target\n\t\t\tt.Run(env, func(t *testing.T) {\n\t\t\t\tgoEnv := exec.Command(exe, \"env\")\n\t\t\t\tgoEnv.Env = []string{\"GOROOT=/\", env}\n\t\t\t\toutput, err := goEnv.CombinedOutput()\n\t\t\t\tt.Log(\"go output is:\", string(output))\n\t\t\t\tqt.Assert(t, qt.IsNotNil(err), qt.Commentf(\"No clang target should be a valid build constraint\"))\n\t\t\t})\n\t\t}\n\n\t}\n}\n\nfunc goBin(t *testing.T) string {\n\tt.Helper()\n\n\texe, err := exec.LookPath(\"go\")\n\tif errors.Is(err, exec.ErrNotFound) {\n\t\tt.Skip(\"go binary is not in PATH\")\n\t}\n\tqt.Assert(t, qt.IsNil(err))\n\n\treturn exe\n}\n"
  },
  {
    "path": "cmd/bpf2go/gen/types.go",
    "content": "//go:build !windows\n\npackage gen\n\nimport (\n\t\"cmp\"\n\t\"slices\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/btf\"\n)\n\n// CollectGlobalTypes finds all types which are used in the global scope.\n//\n// This currently includes the types of variables, map keys and values.\nfunc CollectGlobalTypes(spec *ebpf.CollectionSpec) []btf.Type {\n\tvar types []btf.Type\n\n\ttypes = collectMapTypes(types, spec.Maps)\n\ttypes = collectVariableTypes(types, spec.Variables)\n\n\tslices.SortStableFunc(types, func(a, b btf.Type) int {\n\t\treturn cmp.Compare(a.TypeName(), b.TypeName())\n\t})\n\n\treturn types\n}\n\n// collectMapTypes collects all types used by MapSpecs.\nfunc collectMapTypes(types []btf.Type, maps map[string]*ebpf.MapSpec) []btf.Type {\n\tfor _, m := range maps {\n\t\tif m.Key != nil && m.Key.TypeName() != \"\" {\n\t\t\ttypes = addType(types, m.Key)\n\t\t}\n\n\t\tif m.Value != nil && m.Value.TypeName() != \"\" {\n\t\t\ttypes = addType(types, m.Value)\n\t\t}\n\t}\n\n\treturn types\n}\n\n// collectVariableTypes collects all types used by VariableSpecs.\nfunc collectVariableTypes(types []btf.Type, vars map[string]*ebpf.VariableSpec) []btf.Type {\n\tfor _, vs := range vars {\n\t\ttypes = addType(types, vs.Type.Type)\n\t}\n\n\treturn types\n}\n\n// addType adds a type to types if not already present. Types that don't need to\n// be generated are not added to types.\nfunc addType(types []btf.Type, incoming btf.Type) []btf.Type {\n\tincoming = selectType(incoming)\n\tif incoming == nil {\n\t\treturn types\n\t}\n\n\t// Strip only the qualifiers (not typedefs) from the incoming type. Retain\n\t// typedefs since they carry the name of the anonymous type they point to,\n\t// without which we can't generate a named Go type.\n\tincoming = btf.QualifiedType(incoming)\n\tif incoming.TypeName() == \"\" {\n\t\treturn types\n\t}\n\n\texists := func(existing btf.Type) bool {\n\t\treturn existing.TypeName() == incoming.TypeName()\n\t}\n\tif !slices.ContainsFunc(types, exists) {\n\t\ttypes = append(types, incoming)\n\t}\n\treturn types\n}\n\nfunc selectType(t btf.Type) btf.Type {\n\t// Obtain a concrete type with qualifiers and typedefs stripped.\n\tswitch ut := btf.UnderlyingType(t).(type) {\n\tcase *btf.Struct, *btf.Union, *btf.Enum:\n\t\treturn t\n\n\t// Collect the array's element type. Note: qualifiers on array-type variables\n\t// typically appear after the array, e.g. a const volatile int[4] is actually\n\t// an array of const volatile ints.\n\tcase *btf.Array:\n\t\treturn selectType(ut.Type)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "cmd/bpf2go/gen/types_test.go",
    "content": "//go:build !windows\n\npackage gen\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n\n\t\"github.com/go-quicktest/qt\"\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nfunc mustAnyTypeByName(t *testing.T, spec *ebpf.CollectionSpec, name string) btf.Type {\n\tt.Helper()\n\n\ttyp, err := spec.Types.AnyTypeByName(name)\n\tqt.Assert(t, qt.IsNil(err))\n\treturn typ\n}\n\nfunc TestCollectGlobalTypes(t *testing.T) {\n\tspec, err := ebpf.LoadCollectionSpec(testutils.NativeFile(t, \"../testdata/minimal-%s.elf\"))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tbar := mustAnyTypeByName(t, spec, \"bar\")\n\tbarfoo := mustAnyTypeByName(t, spec, \"barfoo\")\n\tbaz := mustAnyTypeByName(t, spec, \"baz\")\n\te := mustAnyTypeByName(t, spec, \"e\")\n\tubar := mustAnyTypeByName(t, spec, \"ubar\")\n\n\tgot := CollectGlobalTypes(spec)\n\tqt.Assert(t, qt.IsNil(err))\n\n\twant := []btf.Type{bar, barfoo, baz, e, ubar}\n\tqt.Assert(t, qt.CmpEquals(got, want, cmp.Comparer(func(a, b btf.Type) bool {\n\t\treturn a.TypeName() == b.TypeName()\n\t})))\n}\n"
  },
  {
    "path": "cmd/bpf2go/internal/module.go",
    "content": "package internal\n\n// We used to have some clever code here which relied on debug.ReadBuildInfo().\n// This is broken due to https://github.com/golang/go/issues/33976, and some build\n// systems like bazel also do not generate the necessary data. Let's keep it\n// simple instead.\n\n// The module containing the code in this repository.\nconst CurrentModule = \"github.com/cilium/ebpf\"\n"
  },
  {
    "path": "cmd/bpf2go/main.go",
    "content": "//go:build !windows\n\npackage main\n\nimport (\n\t\"errors\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"slices\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/cmd/bpf2go/gen\"\n)\n\nconst helpText = `Usage: %[1]s [options] <ident> <source file> [-- <C flags>]\n\nident is used as the stem of all generated Go types and functions, and\nmust be a valid Go identifier.\n\nsource is a single C file that is compiled using the specified compiler\n(usually some version of clang).\n\nYou can pass options to the compiler by appending them after a '--' argument\nor by supplying -cflags. Flags passed as arguments take precedence\nover flags passed via -cflags. Additionally, the program expands quotation\nmarks in -cflags. This means that -cflags 'foo \"bar baz\"' is passed to the\ncompiler as two arguments \"foo\" and \"bar baz\".\n\nThe program expects GOPACKAGE to be set in the environment, and should be invoked\nvia go generate. The generated files are written to the current directory.\n\nSome options take defaults from the environment. Variable name is mentioned\nnext to the respective option.\n\nOptions:\n\n`\n\nfunc run(stdout io.Writer, args []string) (err error) {\n\tb2g, err := newB2G(stdout, args)\n\tswitch {\n\tcase err == nil:\n\t\treturn b2g.convertAll()\n\tcase errors.Is(err, flag.ErrHelp):\n\t\treturn nil\n\tdefault:\n\t\treturn err\n\t}\n}\n\ntype bpf2go struct {\n\tstdout  io.Writer\n\tverbose bool\n\t// Absolute path to a .c file.\n\tsourceFile string\n\t// Absolute path to a directory where .go are written\n\toutputDir string\n\t// Alternative output stem. If empty, identStem is used.\n\toutputStem string\n\t// Suffix in generated file names such as _test.\n\toutputSuffix string\n\t// Valid go package name.\n\tpkg string\n\t// Valid go identifier.\n\tidentStem string\n\t// Targets to build for.\n\ttargetArches map[gen.Target]gen.GoArches\n\t// C compiler.\n\tcc string\n\t// Command used to strip DWARF.\n\tstrip            string\n\tdisableStripping bool\n\t// C flags passed to the compiler.\n\tcFlags          []string\n\tskipGlobalTypes bool\n\t// C types to include in the generated output.\n\tcTypes cTypes\n\t// Build tags to be included in the output.\n\ttags buildTags\n\t// Base directory of the Makefile. Enables outputting make-style dependencies\n\t// in .d files.\n\tmakeBase string\n}\n\nfunc (b2g *bpf2go) Debugln(a ...any) {\n\tif b2g.verbose {\n\t\tfmt.Fprintln(b2g.stdout, a...)\n\t}\n}\n\nfunc newB2G(stdout io.Writer, args []string) (*bpf2go, error) {\n\tb2g := &bpf2go{\n\t\tstdout: stdout,\n\t}\n\n\tfs := flag.NewFlagSet(\"bpf2go\", flag.ContinueOnError)\n\tfs.BoolVar(&b2g.verbose, \"verbose\", getBool(\"V\", false), \"Enable verbose logging ($V)\")\n\tfs.StringVar(&b2g.cc, \"cc\", getEnv(\"BPF2GO_CC\", \"clang\"),\n\t\t\"`binary` used to compile C to BPF ($BPF2GO_CC)\")\n\tfs.StringVar(&b2g.strip, \"strip\", getEnv(\"BPF2GO_STRIP\", \"\"),\n\t\t\"`binary` used to strip DWARF from compiled BPF ($BPF2GO_STRIP)\")\n\tfs.BoolVar(&b2g.disableStripping, \"no-strip\", false, \"disable stripping of DWARF\")\n\tflagCFlags := fs.String(\"cflags\", getEnv(\"BPF2GO_CFLAGS\", \"\"),\n\t\t\"flags passed to the compiler, may contain quoted arguments ($BPF2GO_CFLAGS)\")\n\tfs.Var(&b2g.tags, \"tags\", \"Comma-separated list of Go build tags to include in generated files\")\n\tflagTarget := fs.String(\"target\", \"bpfel,bpfeb\", \"clang target(s) to compile for (comma separated)\")\n\tfs.StringVar(&b2g.makeBase, \"makebase\", getEnv(\"BPF2GO_MAKEBASE\", \"\"),\n\t\t\"write make compatible depinfo files relative to `directory` ($BPF2GO_MAKEBASE)\")\n\tfs.Var(&b2g.cTypes, \"type\", \"`Name` of a type to generate a Go declaration for, may be repeated\")\n\tfs.BoolVar(&b2g.skipGlobalTypes, \"no-global-types\", false, \"Skip generating types for map keys and values, etc.\")\n\tfs.StringVar(&b2g.outputStem, \"output-stem\", \"\", \"alternative stem for names of generated files (defaults to ident)\")\n\toutputSuffix := \"\"\n\tif strings.HasSuffix(getEnv(\"GOFILE\", \"\"), \"_test.go\") {\n\t\toutputSuffix = \"_test\"\n\t}\n\tfs.StringVar(&b2g.outputSuffix, \"output-suffix\", outputSuffix,\n\t\t\"suffix in generated file names such as _test (default based on $GOFILE)\")\n\toutDir := fs.String(\"output-dir\", \"\", \"target directory of generated files (defaults to current directory)\")\n\toutPkg := fs.String(\"go-package\", \"\", \"package for output go file (default as ENV GOPACKAGE)\")\n\n\tfs.SetOutput(b2g.stdout)\n\tfs.Usage = func() {\n\t\tfmt.Fprintf(fs.Output(), helpText, fs.Name())\n\t\tfs.PrintDefaults()\n\t\tfmt.Fprintln(fs.Output())\n\t\tprintTargets(fs.Output())\n\t}\n\tif err := fs.Parse(args); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif *outDir == \"\" {\n\t\tvar err error\n\t\tif *outDir, err = os.Getwd(); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tb2g.outputDir = *outDir\n\n\tif *outPkg == \"\" {\n\t\t*outPkg = os.Getenv(gopackageEnv)\n\t}\n\tb2g.pkg = *outPkg\n\n\tif b2g.pkg == \"\" {\n\t\treturn nil, errors.New(\"missing package, you should either set the go-package flag or the GOPACKAGE env\")\n\t}\n\n\t// Allow CC like \"ccache clang\" to work.\n\tccParts := strings.Fields(b2g.cc)\n\tif len(ccParts) == 0 {\n\t\treturn nil, errors.New(\"no compiler specified\")\n\t}\n\tb2g.cc = ccParts[0]\n\n\targs, cFlags := splitCFlagsFromArgs(fs.Args())\n\n\tif *flagCFlags != \"\" {\n\t\tsplitCFlags, err := splitArguments(*flagCFlags)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\t// Command line arguments take precedence over C flags\n\t\t// from the flag.\n\t\tcFlags = append(splitCFlags, cFlags...)\n\t}\n\n\tfor _, cFlag := range cFlags {\n\t\tif strings.HasPrefix(cFlag, \"-M\") {\n\t\t\treturn nil, fmt.Errorf(\"use -makebase instead of %q\", cFlag)\n\t\t}\n\t}\n\n\tb2g.cFlags = append(ccParts[1:], cFlags[:len(cFlags):len(cFlags)]...)\n\n\tif len(args) < 2 {\n\t\treturn nil, errors.New(\"expected at least two arguments\")\n\t}\n\n\tb2g.identStem = args[0]\n\n\tsourceFile, err := filepath.Abs(args[1])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tb2g.sourceFile = sourceFile\n\n\tif b2g.makeBase != \"\" {\n\t\tb2g.makeBase, err = filepath.Abs(b2g.makeBase)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tif b2g.outputStem != \"\" && strings.ContainsRune(b2g.outputStem, filepath.Separator) {\n\t\treturn nil, fmt.Errorf(\"-output-stem %q must not contain path separation characters\", b2g.outputStem)\n\t}\n\n\tif strings.ContainsRune(b2g.outputSuffix, filepath.Separator) {\n\t\treturn nil, fmt.Errorf(\"-output-suffix %q must not contain path separation characters\", b2g.outputSuffix)\n\t}\n\n\ttargetArches := make(map[gen.Target]gen.GoArches)\n\tfor _, tgt := range strings.Split(*flagTarget, \",\") {\n\t\ttarget, goarches, err := gen.FindTarget(tgt)\n\t\tif err != nil {\n\t\t\tif errors.Is(err, gen.ErrInvalidTarget) {\n\t\t\t\tprintTargets(b2g.stdout)\n\t\t\t\tfmt.Fprintln(b2g.stdout)\n\t\t\t}\n\t\t\treturn nil, err\n\t\t}\n\n\t\ttargetArches[target] = goarches\n\t}\n\n\tif len(targetArches) == 0 {\n\t\treturn nil, fmt.Errorf(\"no targets specified\")\n\t}\n\tb2g.targetArches = targetArches\n\n\t// Try to find a suitable llvm-strip, possibly with a version suffix derived\n\t// from the clang binary.\n\tif b2g.strip == \"\" {\n\t\tb2g.strip = \"llvm-strip\"\n\t\tif after, ok := strings.CutPrefix(b2g.cc, \"clang\"); ok {\n\t\t\tb2g.strip += after\n\t\t}\n\t}\n\n\treturn b2g, nil\n}\n\n// cTypes collects the C type names a user wants to generate Go types for.\n//\n// Names are guaranteed to be unique, and only a subset of names is accepted so\n// that we may extend the flag syntax in the future.\ntype cTypes []string\n\nvar _ flag.Value = (*cTypes)(nil)\n\nfunc (ct *cTypes) String() string {\n\tif ct == nil {\n\t\treturn \"[]\"\n\t}\n\treturn fmt.Sprint(*ct)\n}\n\nconst validCTypeChars = `[a-z0-9_]`\n\nvar reValidCType = regexp.MustCompile(`(?i)^` + validCTypeChars + `+$`)\n\nfunc (ct *cTypes) Set(value string) error {\n\tif !reValidCType.MatchString(value) {\n\t\treturn fmt.Errorf(\"%q contains characters outside of %s\", value, validCTypeChars)\n\t}\n\n\ti := sort.SearchStrings(*ct, value)\n\tif i >= len(*ct) {\n\t\t*ct = append(*ct, value)\n\t\treturn nil\n\t}\n\n\tif (*ct)[i] == value {\n\t\treturn fmt.Errorf(\"duplicate type %q\", value)\n\t}\n\n\t*ct = append((*ct)[:i], append([]string{value}, (*ct)[i:]...)...)\n\treturn nil\n}\n\nfunc getEnv(key, defaultVal string) string {\n\tif val, ok := os.LookupEnv(key); ok {\n\t\treturn val\n\t}\n\treturn defaultVal\n}\n\nfunc getBool(key string, defaultVal bool) bool {\n\tval, ok := os.LookupEnv(key)\n\tif !ok {\n\t\treturn defaultVal\n\t}\n\n\tb, err := strconv.ParseBool(val)\n\tif err != nil {\n\t\treturn defaultVal\n\t}\n\n\treturn b\n}\n\nfunc (b2g *bpf2go) convertAll() (err error) {\n\tif _, err := os.Stat(b2g.sourceFile); os.IsNotExist(err) {\n\t\treturn fmt.Errorf(\"file %s doesn't exist\", b2g.sourceFile)\n\t} else if err != nil {\n\t\treturn err\n\t}\n\n\tif !b2g.disableStripping {\n\t\tb2g.strip, err = exec.LookPath(b2g.strip)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tfor target, arches := range b2g.targetArches {\n\t\tif err := b2g.convert(target, arches); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (b2g *bpf2go) convert(tgt gen.Target, goarches gen.GoArches) (err error) {\n\tremoveOnError := func(f *os.File) {\n\t\tif err != nil {\n\t\t\tos.Remove(f.Name())\n\t\t}\n\t\tf.Close()\n\t}\n\n\toutputStem := b2g.outputStem\n\tif outputStem == \"\" {\n\t\toutputStem = strings.ToLower(b2g.identStem)\n\t}\n\n\tstem := fmt.Sprintf(\"%s_%s%s\", outputStem, tgt.Suffix(), b2g.outputSuffix)\n\n\tabsOutPath, err := filepath.Abs(b2g.outputDir)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tobjFileName := filepath.Join(absOutPath, stem+\".o\")\n\n\tcwd, err := os.Getwd()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tarchConstraint := goarches.Constraint()\n\tconstraints := andConstraints(archConstraint, b2g.tags.Expr)\n\n\tif err := b2g.removeOldOutputFiles(outputStem, tgt); err != nil {\n\t\treturn fmt.Errorf(\"remove obsolete output: %w\", err)\n\t}\n\n\tvar depInput *os.File\n\tcFlags := slices.Clone(b2g.cFlags)\n\tif b2g.makeBase != \"\" {\n\t\tdepInput, err = os.CreateTemp(\"\", \"bpf2go\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer depInput.Close()\n\t\tdefer os.Remove(depInput.Name())\n\n\t\tcFlags = append(cFlags,\n\t\t\t// Output dependency information.\n\t\t\t\"-MD\",\n\t\t\t// Create phony targets so that deleting a dependency doesn't\n\t\t\t// break the build.\n\t\t\t\"-MP\",\n\t\t\t// Write it to temporary file\n\t\t\t\"-MF\"+depInput.Name(),\n\t\t)\n\t}\n\n\terr = gen.Compile(gen.CompileArgs{\n\t\tCC:               b2g.cc,\n\t\tStrip:            b2g.strip,\n\t\tDisableStripping: b2g.disableStripping,\n\t\tFlags:            cFlags,\n\t\tTarget:           tgt,\n\t\tWorkdir:          cwd,\n\t\tSource:           b2g.sourceFile,\n\t\tDest:             objFileName,\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"compile: %w\", err)\n\t}\n\n\tif b2g.disableStripping {\n\t\tb2g.Debugln(\"Compiled object\", \"file\", objFileName)\n\t} else {\n\t\tb2g.Debugln(\"Compiled and stripped object\", \"file\", objFileName)\n\t}\n\n\tspec, err := ebpf.LoadCollectionSpec(objFileName)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"can't load BPF from ELF: %s\", err)\n\t}\n\n\tvar maps []string\n\tfor name := range spec.Maps {\n\t\t// Skip .rodata, .data, .bss, etc. sections\n\t\tif !strings.HasPrefix(name, \".\") {\n\t\t\tmaps = append(maps, name)\n\t\t}\n\t}\n\n\tvar variables []string\n\tfor name := range spec.Variables {\n\t\tvariables = append(variables, name)\n\t}\n\n\tvar programs []string\n\tfor name := range spec.Programs {\n\t\tprograms = append(programs, name)\n\t}\n\n\ttypes, err := collectCTypes(spec.Types, b2g.cTypes)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"collect C types: %w\", err)\n\t}\n\n\tif !b2g.skipGlobalTypes {\n\t\ttypes = append(types, gen.CollectGlobalTypes(spec)...)\n\t}\n\n\t// Write out generated go\n\tgoFileName := filepath.Join(absOutPath, stem+\".go\")\n\tgoFile, err := os.Create(goFileName)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer removeOnError(goFile)\n\n\terr = gen.Generate(gen.GenerateArgs{\n\t\tPackage:     b2g.pkg,\n\t\tStem:        b2g.identStem,\n\t\tConstraints: constraints,\n\t\tMaps:        maps,\n\t\tVariables:   variables,\n\t\tPrograms:    programs,\n\t\tTypes:       types,\n\t\tObjectFile:  filepath.Base(objFileName),\n\t\tOutput:      goFile,\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"can't write %s: %s\", goFileName, err)\n\t}\n\n\tb2g.Debugln(\"Generated bpf2go binding\", \"file\", goFileName)\n\n\tif b2g.makeBase == \"\" {\n\t\treturn\n\t}\n\n\tdeps, err := parseDependencies(cwd, depInput)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"can't read dependency information: %s\", err)\n\t}\n\n\tdepFileName := goFileName + \".d\"\n\tdepOutput, err := os.Create(depFileName)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"write make dependencies: %w\", err)\n\t}\n\tdefer depOutput.Close()\n\n\t// There is always at least a dependency for the main file.\n\tdeps[0].file = goFileName\n\tif err := adjustDependencies(depOutput, b2g.makeBase, deps); err != nil {\n\t\treturn fmt.Errorf(\"can't adjust dependency information: %s\", err)\n\t}\n\n\tb2g.Debugln(\"Wrote dependency\", \"file\", depFileName)\n\n\treturn nil\n}\n\n// removeOldOutputFiles removes output files generated by an old naming scheme.\n//\n// In the old scheme some linux targets were interpreted as build constraints\n// by the go toolchain.\nfunc (b2g *bpf2go) removeOldOutputFiles(outputStem string, tgt gen.Target) error {\n\tsuffix := tgt.ObsoleteSuffix()\n\tif suffix == \"\" {\n\t\treturn nil\n\t}\n\n\tstem := fmt.Sprintf(\"%s_%s\", outputStem, suffix)\n\tfor _, ext := range []string{\".o\", \".go\"} {\n\t\tfilename := filepath.Join(b2g.outputDir, stem+ext)\n\n\t\tif err := os.Remove(filename); errors.Is(err, os.ErrNotExist) {\n\t\t\tcontinue\n\t\t} else if err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tb2g.Debugln(\"Removed obsolete output file\", \"file\", filename)\n\t}\n\n\treturn nil\n}\n\nfunc printTargets(w io.Writer) {\n\tvar arches []string\n\tfor goarch, archTarget := range gen.TargetsByGoArch() {\n\t\tif archTarget.IsGeneric() {\n\t\t\tcontinue\n\t\t}\n\t\tarches = append(arches, string(goarch))\n\t}\n\tsort.Strings(arches)\n\n\tfmt.Fprint(w, \"Supported targets:\\n\")\n\tfmt.Fprint(w, \"\\tbpf\\n\\tbpfel\\n\\tbpfeb\\n\")\n\tfor _, arch := range arches {\n\t\tfmt.Fprintf(w, \"\\t%s\\n\", arch)\n\t}\n}\n\nfunc collectCTypes(types *btf.Spec, names []string) ([]btf.Type, error) {\n\tvar result []btf.Type\n\tfor _, cType := range names {\n\t\ttyp, err := types.AnyTypeByName(cType)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"looking up type %s: %w\", cType, err)\n\t\t}\n\t\tresult = append(result, typ)\n\t}\n\treturn result, nil\n}\n\nconst gopackageEnv = \"GOPACKAGE\"\n\nfunc main() {\n\tif err := run(os.Stdout, os.Args[1:]); err != nil {\n\t\tfmt.Fprintln(os.Stderr, \"Error:\", err)\n\t\tos.Exit(1)\n\t}\n}\n"
  },
  {
    "path": "cmd/bpf2go/main_test.go",
    "content": "//go:build !windows\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/cmd/bpf2go/gen\"\n\t\"github.com/cilium/ebpf/cmd/bpf2go/internal\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nconst minimalSocketFilter = `__attribute__((section(\"socket\"), used)) int main() { return 0; }`\n\nfunc TestRun(t *testing.T) {\n\tclangBin := testutils.ClangBin(t)\n\tdir := t.TempDir()\n\tmustWriteFile(t, dir, \"test.c\", minimalSocketFilter)\n\n\tmodRoot, err := filepath.Abs(\"../..\")\n\tqt.Assert(t, qt.IsNil(err))\n\n\tif _, err := os.Stat(filepath.Join(modRoot, \"go.mod\")); os.IsNotExist(err) {\n\t\tt.Fatal(\"No go.mod file in\", modRoot)\n\t}\n\n\tmodDir := t.TempDir()\n\texecInModule := func(name string, args ...string) {\n\t\tt.Helper()\n\n\t\tcmd := exec.Command(name, args...)\n\t\tcmd.Dir = modDir\n\t\tif out, err := cmd.CombinedOutput(); err != nil {\n\t\t\tif out := string(out); out != \"\" {\n\t\t\t\tt.Log(out)\n\t\t\t}\n\t\t\tt.Fatalf(\"Can't execute %s: %v\", name, args)\n\t\t}\n\t}\n\n\tmodule := internal.CurrentModule\n\n\texecInModule(\"go\", \"mod\", \"init\", \"bpf2go-test\")\n\n\texecInModule(\"go\", \"mod\", \"edit\",\n\t\t// Require the module. The version doesn't matter due to the replace\n\t\t// below.\n\t\tfmt.Sprintf(\"-require=%s@v0.0.0\", module),\n\t\t// Replace the module with the current version.\n\t\tfmt.Sprintf(\"-replace=%s=%s\", module, modRoot),\n\t)\n\n\tgoarches := []string{\n\t\t\"amd64\", // little-endian\n\t\t\"arm64\",\n\t\t\"s390x\", // big-endian\n\t}\n\n\terr = run(io.Discard, []string{\n\t\t\"-go-package\", \"main\",\n\t\t\"-output-dir\", modDir,\n\t\t\"-cc\", clangBin,\n\t\t\"-target\", strings.Join(goarches, \",\"),\n\t\t\"bar\",\n\t\tfilepath.Join(dir, \"test.c\"),\n\t})\n\n\tif err != nil {\n\t\tt.Fatal(\"Can't run:\", err)\n\t}\n\n\tmustWriteFile(t, modDir, \"main.go\",\n\t\t`\npackage main\n\nfunc main() {\n\tvar obj barObjects\n\tprintln(obj.Main)\n}`)\n\n\tfor _, arch := range goarches {\n\t\tt.Run(arch, func(t *testing.T) {\n\t\t\tgoBuild := exec.Command(\"go\", \"build\", \"-mod=mod\", \"-o\", \"/dev/null\")\n\t\t\tgoBuild.Dir = modDir\n\t\t\tgoBuild.Env = append(os.Environ(),\n\t\t\t\t\"GOOS=linux\",\n\t\t\t\t\"GOARCH=\"+arch,\n\t\t\t\t\"GOPROXY=off\",\n\t\t\t\t\"GOSUMDB=off\",\n\t\t\t)\n\t\t\tout, err := goBuild.CombinedOutput()\n\t\t\tif err != nil {\n\t\t\t\tif out := string(out); out != \"\" {\n\t\t\t\t\tt.Log(out)\n\t\t\t\t}\n\t\t\t\tt.Error(\"Can't compile package:\", err)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestHelp(t *testing.T) {\n\tvar stdout bytes.Buffer\n\terr := run(&stdout, []string{\"-help\"})\n\tif err != nil {\n\t\tt.Fatal(\"Can't execute -help\")\n\t}\n\n\tif stdout.Len() == 0 {\n\t\tt.Error(\"-help doesn't write to stdout\")\n\t}\n}\n\nfunc TestErrorMentionsEnvVar(t *testing.T) {\n\terr := run(io.Discard, nil)\n\tqt.Assert(t, qt.StringContains(err.Error(), gopackageEnv), qt.Commentf(\"Error should include name of environment variable\"))\n}\n\nfunc TestDisableStripping(t *testing.T) {\n\tdir := t.TempDir()\n\tmustWriteFile(t, dir, \"test.c\", minimalSocketFilter)\n\n\terr := run(io.Discard, []string{\n\t\t\"-go-package\", \"foo\",\n\t\t\"-output-dir\", dir,\n\t\t\"-cc\", testutils.ClangBin(t),\n\t\t\"-strip\", \"binary-that-certainly-doesnt-exist\",\n\t\t\"-no-strip\",\n\t\t\"bar\",\n\t\tfilepath.Join(dir, \"test.c\"),\n\t})\n\n\tif err != nil {\n\t\tt.Fatal(\"Can't run with stripping disabled:\", err)\n\t}\n}\n\nfunc TestConvertGOARCH(t *testing.T) {\n\ttmp := t.TempDir()\n\tmustWriteFile(t, tmp, \"test.c\",\n\t\t`\n#ifndef __TARGET_ARCH_x86\n#error __TARGET_ARCH_x86 is not defined\n#endif`,\n\t)\n\n\tb2g := bpf2go{\n\t\tpkg:              \"test\",\n\t\tstdout:           io.Discard,\n\t\tidentStem:        \"test\",\n\t\tcc:               testutils.ClangBin(t),\n\t\tdisableStripping: true,\n\t\tsourceFile:       tmp + \"/test.c\",\n\t\toutputDir:        tmp,\n\t}\n\n\tif err := b2g.convert(gen.TargetsByGoArch()[\"amd64\"], nil); err != nil {\n\t\tt.Fatal(\"Can't target GOARCH:\", err)\n\t}\n}\n\nfunc TestCTypes(t *testing.T) {\n\tvar ct cTypes\n\tvalid := []string{\n\t\t\"abcdefghijklmnopqrstuvqxyABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_\",\n\t\t\"y\",\n\t}\n\tfor _, value := range valid {\n\t\tif err := ct.Set(value); err != nil {\n\t\t\tt.Fatalf(\"Set returned an error for %q: %s\", value, err)\n\t\t}\n\t}\n\tqt.Assert(t, qt.ContentEquals(ct, valid))\n\n\tfor _, value := range []string{\n\t\t\"\",\n\t\t\" \",\n\t\t\" frood\",\n\t\t\"foo\\nbar\",\n\t\t\".\",\n\t\t\",\",\n\t\t\"+\",\n\t\t\"-\",\n\t} {\n\t\tct = nil\n\t\tif err := ct.Set(value); err == nil {\n\t\t\tt.Fatalf(\"Set did not return an error for %q\", value)\n\t\t}\n\t}\n\n\tct = nil\n\tqt.Assert(t, qt.IsNil(ct.Set(\"foo\")))\n\tqt.Assert(t, qt.IsNotNil(ct.Set(\"foo\")))\n}\n\nfunc TestParseArgs(t *testing.T) {\n\tconst (\n\t\tpkg       = \"eee\"\n\t\toutputDir = \".\"\n\t\tcsource   = \"testdata/minimal.c\"\n\t\tstem      = \"a\"\n\t)\n\tt.Run(\"makebase\", func(t *testing.T) {\n\t\tt.Setenv(gopackageEnv, pkg)\n\t\tbasePath, _ := filepath.Abs(\"barfoo\")\n\t\targs := []string{\"-makebase\", basePath, stem, csource}\n\t\tb2g, err := newB2G(&bytes.Buffer{}, args)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.Equals(b2g.makeBase, basePath))\n\t})\n\n\tt.Run(\"makebase from env\", func(t *testing.T) {\n\t\tt.Setenv(gopackageEnv, pkg)\n\t\tbasePath, _ := filepath.Abs(\"barfoo\")\n\t\targs := []string{stem, csource}\n\t\tt.Setenv(\"BPF2GO_MAKEBASE\", basePath)\n\t\tb2g, err := newB2G(&bytes.Buffer{}, args)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.Equals(b2g.makeBase, basePath))\n\t})\n\n\tt.Run(\"makebase flag overrides env\", func(t *testing.T) {\n\t\tt.Setenv(gopackageEnv, pkg)\n\t\tbasePathFlag, _ := filepath.Abs(\"barfoo\")\n\t\tbasePathEnv, _ := filepath.Abs(\"foobar\")\n\t\targs := []string{\"-makebase\", basePathFlag, stem, csource}\n\t\tt.Setenv(\"BPF2GO_MAKEBASE\", basePathEnv)\n\t\tb2g, err := newB2G(&bytes.Buffer{}, args)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.Equals(b2g.makeBase, basePathFlag))\n\t})\n\n\tt.Run(\"cc defaults to clang\", func(t *testing.T) {\n\t\tt.Setenv(gopackageEnv, pkg)\n\t\targs := []string{stem, csource}\n\t\tb2g, err := newB2G(&bytes.Buffer{}, args)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.Equals(b2g.cc, \"clang\"))\n\t})\n\n\tt.Run(\"cc\", func(t *testing.T) {\n\t\tt.Setenv(gopackageEnv, pkg)\n\t\targs := []string{\"-cc\", \"barfoo\", stem, csource}\n\t\tb2g, err := newB2G(&bytes.Buffer{}, args)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.Equals(b2g.cc, \"barfoo\"))\n\t})\n\n\tt.Run(\"cc from env\", func(t *testing.T) {\n\t\tt.Setenv(gopackageEnv, pkg)\n\t\targs := []string{stem, csource}\n\t\tt.Setenv(\"BPF2GO_CC\", \"barfoo\")\n\t\tb2g, err := newB2G(&bytes.Buffer{}, args)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.Equals(b2g.cc, \"barfoo\"))\n\t})\n\n\tt.Run(\"cc flag overrides env\", func(t *testing.T) {\n\t\tt.Setenv(gopackageEnv, pkg)\n\t\targs := []string{\"-cc\", \"barfoo\", stem, csource}\n\t\tt.Setenv(\"BPF2GO_CC\", \"foobar\")\n\t\tb2g, err := newB2G(&bytes.Buffer{}, args)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.Equals(b2g.cc, \"barfoo\"))\n\t})\n\n\tt.Run(\"strip defaults to llvm-strip\", func(t *testing.T) {\n\t\tt.Setenv(gopackageEnv, pkg)\n\t\targs := []string{stem, csource}\n\t\tb2g, err := newB2G(&bytes.Buffer{}, args)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.Equals(b2g.strip, \"llvm-strip\"))\n\t})\n\n\tt.Run(\"strip\", func(t *testing.T) {\n\t\tt.Setenv(gopackageEnv, pkg)\n\t\targs := []string{\"-strip\", \"barfoo\", stem, csource}\n\t\tb2g, err := newB2G(&bytes.Buffer{}, args)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.Equals(b2g.strip, \"barfoo\"))\n\t})\n\n\tt.Run(\"strip from env\", func(t *testing.T) {\n\t\tt.Setenv(gopackageEnv, pkg)\n\t\targs := []string{stem, csource}\n\t\tt.Setenv(\"BPF2GO_STRIP\", \"barfoo\")\n\t\tb2g, err := newB2G(&bytes.Buffer{}, args)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.Equals(b2g.strip, \"barfoo\"))\n\t})\n\n\tt.Run(\"strip flag overrides env\", func(t *testing.T) {\n\t\tt.Setenv(gopackageEnv, pkg)\n\t\targs := []string{\"-strip\", \"barfoo\", stem, csource}\n\t\tt.Setenv(\"BPF2GO_STRIP\", \"foobar\")\n\t\tb2g, err := newB2G(&bytes.Buffer{}, args)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.Equals(b2g.strip, \"barfoo\"))\n\t})\n\n\tt.Run(\"no strip defaults to false\", func(t *testing.T) {\n\t\tt.Setenv(gopackageEnv, pkg)\n\t\targs := []string{stem, csource}\n\t\tb2g, err := newB2G(&bytes.Buffer{}, args)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.IsFalse(b2g.disableStripping))\n\t})\n\n\tt.Run(\"no strip\", func(t *testing.T) {\n\t\tt.Setenv(gopackageEnv, pkg)\n\t\targs := []string{\"-no-strip\", stem, csource}\n\t\tb2g, err := newB2G(&bytes.Buffer{}, args)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.IsTrue(b2g.disableStripping))\n\t})\n\n\tt.Run(\"cflags flag\", func(t *testing.T) {\n\t\tt.Setenv(gopackageEnv, pkg)\n\t\targs := []string{\"-cflags\", \"x y z\", stem, csource}\n\t\tb2g, err := newB2G(&bytes.Buffer{}, args)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.DeepEquals(b2g.cFlags, []string{\"x\", \"y\", \"z\"}))\n\t})\n\n\tt.Run(\"cflags multi flag\", func(t *testing.T) {\n\t\tt.Setenv(gopackageEnv, pkg)\n\t\targs := []string{\"-cflags\", \"x y z\", \"-cflags\", \"u v\", stem, csource}\n\t\tb2g, err := newB2G(&bytes.Buffer{}, args)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.DeepEquals(b2g.cFlags, []string{\"u\", \"v\"}))\n\t})\n\n\tt.Run(\"cflags flag and args\", func(t *testing.T) {\n\t\tt.Setenv(gopackageEnv, pkg)\n\t\targs := []string{\"-cflags\", \"x y z\", \"stem\", csource, \"--\", \"u\", \"v\"}\n\t\tb2g, err := newB2G(&bytes.Buffer{}, args)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.DeepEquals(b2g.cFlags, []string{\"x\", \"y\", \"z\", \"u\", \"v\"}))\n\t})\n\n\tt.Run(\"cflags from env\", func(t *testing.T) {\n\t\tt.Setenv(gopackageEnv, pkg)\n\t\targs := []string{stem, csource}\n\t\tt.Setenv(\"BPF2GO_CFLAGS\", \"x y z\")\n\t\tb2g, err := newB2G(&bytes.Buffer{}, args)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.DeepEquals(b2g.cFlags, []string{\"x\", \"y\", \"z\"}))\n\t})\n\n\tt.Run(\"cflags flag overrides env\", func(t *testing.T) {\n\t\tt.Setenv(gopackageEnv, pkg)\n\t\targs := []string{\"-cflags\", \"u v\", stem, csource}\n\t\tt.Setenv(\"BPF2GO_CFLAGS\", \"x y z\")\n\t\tb2g, err := newB2G(&bytes.Buffer{}, args)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.DeepEquals(b2g.cFlags, []string{\"u\", \"v\"}))\n\t})\n\n\tt.Run(\"go package overrides env\", func(t *testing.T) {\n\t\tt.Setenv(gopackageEnv, pkg)\n\t\targs := []string{\"-go-package\", \"aaa\", stem, csource}\n\t\tb2g, err := newB2G(&bytes.Buffer{}, args)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.Equals(b2g.pkg, \"aaa\"))\n\t})\n\n\tt.Run(\"output dir\", func(t *testing.T) {\n\t\tt.Setenv(gopackageEnv, pkg)\n\t\targs := []string{\"-output-dir\", outputDir, stem, csource}\n\t\tb2g, err := newB2G(&bytes.Buffer{}, args)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.Equals(b2g.outputDir, outputDir))\n\t})\n\n\tt.Run(\"output suffix default\", func(t *testing.T) {\n\t\tt.Setenv(gopackageEnv, pkg)\n\t\tb2g, err := newB2G(&bytes.Buffer{}, []string{stem, csource})\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.Equals(b2g.outputSuffix, \"\"))\n\t})\n\n\tt.Run(\"output suffix GOFILE=_test\", func(t *testing.T) {\n\t\tt.Setenv(gopackageEnv, pkg)\n\t\tt.Setenv(\"GOFILE\", \"foo_test.go\")\n\t\tb2g, err := newB2G(&bytes.Buffer{}, []string{stem, csource})\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.Equals(b2g.outputSuffix, \"_test\"))\n\t})\n\n\tt.Run(\"output suffix custom\", func(t *testing.T) {\n\t\tt.Setenv(gopackageEnv, pkg)\n\t\tt.Setenv(\"GOFILE\", \"foo_test.go\")\n\t\targs := []string{\"-output-suffix\", \"_custom\", stem, csource}\n\t\tb2g, err := newB2G(&bytes.Buffer{}, args)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.Equals(b2g.outputSuffix, \"_custom\"))\n\t})\n}\n\nfunc mustWriteFile(tb testing.TB, dir, name, contents string) {\n\ttb.Helper()\n\ttmpFile := filepath.Join(dir, name)\n\tif err := os.WriteFile(tmpFile, []byte(contents), 0660); err != nil {\n\t\ttb.Fatal(err)\n\t}\n}\n"
  },
  {
    "path": "cmd/bpf2go/makedep.go",
    "content": "//go:build !windows\n\npackage main\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\nfunc adjustDependencies(w io.Writer, baseDir string, deps []dependency) error {\n\tfor _, dep := range deps {\n\t\trelativeFile, err := filepath.Rel(baseDir, dep.file)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif len(dep.prerequisites) == 0 {\n\t\t\t_, err := fmt.Fprintf(w, \"%s:\\n\\n\", relativeFile)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\tvar prereqs []string\n\t\tfor _, prereq := range dep.prerequisites {\n\t\t\trelativePrereq, err := filepath.Rel(baseDir, prereq)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tprereqs = append(prereqs, relativePrereq)\n\t\t}\n\n\t\t_, err = fmt.Fprintf(w, \"%s: \\\\\\n %s\\n\\n\", relativeFile, strings.Join(prereqs, \" \\\\\\n \"))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\ntype dependency struct {\n\tfile          string\n\tprerequisites []string\n}\n\nfunc parseDependencies(baseDir string, in io.Reader) ([]dependency, error) {\n\tabs := func(path string) string {\n\t\tif filepath.IsAbs(path) {\n\t\t\treturn path\n\t\t}\n\t\treturn filepath.Join(baseDir, path)\n\t}\n\n\tscanner := bufio.NewScanner(in)\n\tvar line strings.Builder\n\tvar deps []dependency\n\tfor scanner.Scan() {\n\t\tbuf := scanner.Bytes()\n\t\tif line.Len()+len(buf) > 1024*1024 {\n\t\t\treturn nil, errors.New(\"line too long\")\n\t\t}\n\n\t\tif bytes.HasSuffix(buf, []byte{'\\\\'}) {\n\t\t\tline.Write(buf[:len(buf)-1])\n\t\t\tcontinue\n\t\t}\n\n\t\tline.Write(buf)\n\t\tif line.Len() == 0 {\n\t\t\t// Skip empty lines\n\t\t\tcontinue\n\t\t}\n\n\t\tparts := strings.SplitN(line.String(), \":\", 2)\n\t\tif len(parts) < 2 {\n\t\t\treturn nil, fmt.Errorf(\"invalid line without ':'\")\n\t\t}\n\n\t\t// NB: This doesn't handle filenames with spaces in them.\n\t\t// It seems like make doesn't do that either, so oh well.\n\t\tvar prereqs []string\n\t\tfor _, prereq := range strings.Fields(parts[1]) {\n\t\t\tprereqs = append(prereqs, abs(prereq))\n\t\t}\n\n\t\tdeps = append(deps, dependency{\n\t\t\tabs(string(parts[0])),\n\t\t\tprereqs,\n\t\t})\n\t\tline.Reset()\n\t}\n\tif err := scanner.Err(); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// There is always at least a dependency for the main file.\n\tif len(deps) == 0 {\n\t\treturn nil, fmt.Errorf(\"empty dependency file\")\n\t}\n\treturn deps, nil\n}\n"
  },
  {
    "path": "cmd/bpf2go/makedep_test.go",
    "content": "//go:build !windows\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestParseDependencies(t *testing.T) {\n\tconst input = `main.go: /foo/bar baz\n\nfrob: /gobble \\\n gubble\n\nnothing:\n`\n\n\thave, err := parseDependencies(\"/foo\", strings.NewReader(input))\n\tif err != nil {\n\t\tt.Fatal(\"Can't parse dependencies:\", err)\n\t}\n\n\twant := []dependency{\n\t\t{\"/foo/main.go\", []string{\"/foo/bar\", \"/foo/baz\"}},\n\t\t{\"/foo/frob\", []string{\"/gobble\", \"/foo/gubble\"}},\n\t\t{\"/foo/nothing\", nil},\n\t}\n\n\tif !reflect.DeepEqual(have, want) {\n\t\tt.Logf(\"Have: %#v\", have)\n\t\tt.Logf(\"Want: %#v\", want)\n\t\tt.Error(\"Result doesn't match\")\n\t}\n\n\tvar output bytes.Buffer\n\terr = adjustDependencies(&output, \"/foo\", want)\n\tif err != nil {\n\t\tt.Error(\"Can't adjust dependencies\")\n\t}\n\n\tconst wantOutput = `main.go: \\\n bar \\\n baz\n\nfrob: \\\n ../gobble \\\n gubble\n\nnothing:\n\n`\n\n\tif have := output.String(); have != wantOutput {\n\t\tt.Logf(\"Have:\\n%s\", have)\n\t\tt.Logf(\"Want:\\n%s\", wantOutput)\n\t\tt.Error(\"Output doesn't match\")\n\t}\n}\n"
  },
  {
    "path": "cmd/bpf2go/test/api_test.go",
    "content": "//go:build linux\n\npackage test\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n\t\"unsafe\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc TestLoadingSpec(t *testing.T) {\n\tspec, err := loadTest()\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tqt.Assert(t, qt.Not(qt.IsNil(spec)))\n\tqt.Assert(t, qt.Not(qt.IsNil(spec.Programs)))\n\tqt.Assert(t, qt.Not(qt.IsNil(spec.Maps)))\n\tqt.Assert(t, qt.Not(qt.IsNil(spec.Variables)))\n}\n\nfunc TestLoadingObjects(t *testing.T) {\n\tvar objs testObjects\n\terr := loadTestObjects(&objs, nil)\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(\"Can't load objects:\", err)\n\t}\n\tdefer objs.Close()\n\n\tqt.Assert(t, qt.Not(qt.IsNil(objs.Filter)))\n\tqt.Assert(t, qt.Not(qt.IsNil(objs.Map1)))\n\tqt.Assert(t, qt.Not(qt.IsNil(objs.MyConstant)))\n\tqt.Assert(t, qt.Not(qt.IsNil(objs.StructConst)))\n}\n\nfunc TestTypes(t *testing.T) {\n\tif testEHOOPY != 0 {\n\t\tt.Error(\"Expected testEHOOPY to be 0, got\", testEHOOPY)\n\t}\n\tif testEFROOD != 1 {\n\t\tt.Error(\"Expected testEFROOD to be 0, got\", testEFROOD)\n\t}\n\n\te := testE(0)\n\tif size := unsafe.Sizeof(e); size != 4 {\n\t\tt.Error(\"Expected size of exampleE to be 4, got\", size)\n\t}\n\n\tbf := testBarfoo{}\n\tif size := unsafe.Sizeof(bf); size != 16 {\n\t\tt.Error(\"Expected size of exampleE to be 16, got\", size)\n\t}\n\tif reflect.TypeOf(bf.Bar).Kind() != reflect.Int64 {\n\t\tt.Error(\"Expected testBarfoo.Bar to be int64\")\n\t}\n\tif reflect.TypeOf(bf.Baz).Kind() != reflect.Bool {\n\t\tt.Error(\"Expected testBarfoo.Baz to be bool\")\n\t}\n\tif reflect.TypeOf(bf.Boo) != reflect.TypeOf(e) {\n\t\tt.Error(\"Expected testBarfoo.Boo to be exampleE\")\n\t}\n}\n"
  },
  {
    "path": "cmd/bpf2go/test/doc.go",
    "content": "// Package test checks that the code generated by bpf2go conforms to a\n// specific API.\npackage test\n\n//go:generate go tool bpf2go -tags linux test ../testdata/minimal.c\n"
  },
  {
    "path": "cmd/bpf2go/test/test_bpfeb.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (mips || mips64 || ppc64 || s390x) && linux\n\npackage test\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\t\"structs\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\ntype testBar struct {\n\t_ structs.HostLayout\n\tA uint64\n\tB uint32\n\t_ [4]byte\n}\n\ntype testBarfoo struct {\n\t_   structs.HostLayout\n\tBar int64\n\tBaz bool\n\t_   [3]byte\n\tBoo testE\n}\n\ntype testBaz struct {\n\t_ structs.HostLayout\n\tA uint64\n}\n\ntype testE uint32\n\nconst (\n\ttestEHOOPY testE = 0\n\ttestEFROOD testE = 1\n)\n\ntype testUbar struct {\n\t_ structs.HostLayout\n\tA uint32\n\t_ [4]byte\n}\n\n// loadTest returns the embedded CollectionSpec for test.\nfunc loadTest() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_TestBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load test: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadTestObjects loads test and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*testObjects\n//\t*testPrograms\n//\t*testMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadTestObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadTest()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// testSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype testSpecs struct {\n\ttestProgramSpecs\n\ttestMapSpecs\n\ttestVariableSpecs\n}\n\n// testProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype testProgramSpecs struct {\n\tFilter *ebpf.ProgramSpec `ebpf:\"filter\"`\n}\n\n// testMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype testMapSpecs struct {\n\tMap1 *ebpf.MapSpec `ebpf:\"map1\"`\n}\n\n// testVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype testVariableSpecs struct {\n\tAnInt       *ebpf.VariableSpec `ebpf:\"an_int\"`\n\tIntArray    *ebpf.VariableSpec `ebpf:\"int_array\"`\n\tMyConstant  *ebpf.VariableSpec `ebpf:\"my_constant\"`\n\tStructArray *ebpf.VariableSpec `ebpf:\"struct_array\"`\n\tStructConst *ebpf.VariableSpec `ebpf:\"struct_const\"`\n\tStructVar   *ebpf.VariableSpec `ebpf:\"struct_var\"`\n\tUnionVar    *ebpf.VariableSpec `ebpf:\"union_var\"`\n}\n\n// testObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadTestObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype testObjects struct {\n\ttestPrograms\n\ttestMaps\n\ttestVariables\n}\n\nfunc (o *testObjects) Close() error {\n\treturn _TestClose(\n\t\t&o.testPrograms,\n\t\t&o.testMaps,\n\t)\n}\n\n// testMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadTestObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype testMaps struct {\n\tMap1 *ebpf.Map `ebpf:\"map1\"`\n}\n\nfunc (m *testMaps) Close() error {\n\treturn _TestClose(\n\t\tm.Map1,\n\t)\n}\n\n// testVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadTestObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype testVariables struct {\n\tAnInt       *ebpf.Variable `ebpf:\"an_int\"`\n\tIntArray    *ebpf.Variable `ebpf:\"int_array\"`\n\tMyConstant  *ebpf.Variable `ebpf:\"my_constant\"`\n\tStructArray *ebpf.Variable `ebpf:\"struct_array\"`\n\tStructConst *ebpf.Variable `ebpf:\"struct_const\"`\n\tStructVar   *ebpf.Variable `ebpf:\"struct_var\"`\n\tUnionVar    *ebpf.Variable `ebpf:\"union_var\"`\n}\n\n// testPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadTestObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype testPrograms struct {\n\tFilter *ebpf.Program `ebpf:\"filter\"`\n}\n\nfunc (p *testPrograms) Close() error {\n\treturn _TestClose(\n\t\tp.Filter,\n\t)\n}\n\nfunc _TestClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed test_bpfeb.o\nvar _TestBytes []byte\n"
  },
  {
    "path": "cmd/bpf2go/test/test_bpfel.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64 || wasm) && linux\n\npackage test\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\t\"structs\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\ntype testBar struct {\n\t_ structs.HostLayout\n\tA uint64\n\tB uint32\n\t_ [4]byte\n}\n\ntype testBarfoo struct {\n\t_   structs.HostLayout\n\tBar int64\n\tBaz bool\n\t_   [3]byte\n\tBoo testE\n}\n\ntype testBaz struct {\n\t_ structs.HostLayout\n\tA uint64\n}\n\ntype testE uint32\n\nconst (\n\ttestEHOOPY testE = 0\n\ttestEFROOD testE = 1\n)\n\ntype testUbar struct {\n\t_ structs.HostLayout\n\tA uint32\n\t_ [4]byte\n}\n\n// loadTest returns the embedded CollectionSpec for test.\nfunc loadTest() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_TestBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load test: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadTestObjects loads test and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*testObjects\n//\t*testPrograms\n//\t*testMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadTestObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadTest()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// testSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype testSpecs struct {\n\ttestProgramSpecs\n\ttestMapSpecs\n\ttestVariableSpecs\n}\n\n// testProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype testProgramSpecs struct {\n\tFilter *ebpf.ProgramSpec `ebpf:\"filter\"`\n}\n\n// testMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype testMapSpecs struct {\n\tMap1 *ebpf.MapSpec `ebpf:\"map1\"`\n}\n\n// testVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype testVariableSpecs struct {\n\tAnInt       *ebpf.VariableSpec `ebpf:\"an_int\"`\n\tIntArray    *ebpf.VariableSpec `ebpf:\"int_array\"`\n\tMyConstant  *ebpf.VariableSpec `ebpf:\"my_constant\"`\n\tStructArray *ebpf.VariableSpec `ebpf:\"struct_array\"`\n\tStructConst *ebpf.VariableSpec `ebpf:\"struct_const\"`\n\tStructVar   *ebpf.VariableSpec `ebpf:\"struct_var\"`\n\tUnionVar    *ebpf.VariableSpec `ebpf:\"union_var\"`\n}\n\n// testObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadTestObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype testObjects struct {\n\ttestPrograms\n\ttestMaps\n\ttestVariables\n}\n\nfunc (o *testObjects) Close() error {\n\treturn _TestClose(\n\t\t&o.testPrograms,\n\t\t&o.testMaps,\n\t)\n}\n\n// testMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadTestObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype testMaps struct {\n\tMap1 *ebpf.Map `ebpf:\"map1\"`\n}\n\nfunc (m *testMaps) Close() error {\n\treturn _TestClose(\n\t\tm.Map1,\n\t)\n}\n\n// testVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadTestObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype testVariables struct {\n\tAnInt       *ebpf.Variable `ebpf:\"an_int\"`\n\tIntArray    *ebpf.Variable `ebpf:\"int_array\"`\n\tMyConstant  *ebpf.Variable `ebpf:\"my_constant\"`\n\tStructArray *ebpf.Variable `ebpf:\"struct_array\"`\n\tStructConst *ebpf.Variable `ebpf:\"struct_const\"`\n\tStructVar   *ebpf.Variable `ebpf:\"struct_var\"`\n\tUnionVar    *ebpf.Variable `ebpf:\"union_var\"`\n}\n\n// testPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadTestObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype testPrograms struct {\n\tFilter *ebpf.Program `ebpf:\"filter\"`\n}\n\nfunc (p *testPrograms) Close() error {\n\treturn _TestClose(\n\t\tp.Filter,\n\t)\n}\n\nfunc _TestClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed test_bpfel.o\nvar _TestBytes []byte\n"
  },
  {
    "path": "cmd/bpf2go/testdata/minimal.c",
    "content": "#include \"../../../testdata/common.h\"\n\nchar __license[] __section(\"license\") = \"MIT\";\n\nenum e { HOOPY, FROOD };\n\ntypedef long long int longint;\n\ntypedef struct {\n\tlongint bar;\n\t_Bool baz;\n\tenum e boo;\n} barfoo;\n\ntypedef struct {\n\tuint64_t a;\n} baz;\n\nstruct bar {\n\tuint64_t a;\n\tuint32_t b;\n};\n\nunion ubar {\n\tuint32_t a;\n\tuint64_t b;\n};\n\nstruct {\n\t__uint(type, BPF_MAP_TYPE_HASH);\n\t__type(key, enum e);\n\t__type(value, barfoo);\n\t__uint(max_entries, 1);\n} map1 __section(\".maps\");\n\nvolatile const int an_int;\nvolatile const enum e my_constant = FROOD;\nvolatile const int int_array[2];\nvolatile const barfoo struct_const;\nvolatile const baz struct_array[2];\n\nvolatile struct bar struct_var;\nvolatile union ubar union_var;\n\n__section(\"socket\") int filter() {\n\treturn my_constant + struct_const.bar;\n}\n"
  },
  {
    "path": "cmd/bpf2go/tools.go",
    "content": "//go:build !windows\n\npackage main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc splitCFlagsFromArgs(in []string) (args, cflags []string) {\n\tfor i, arg := range in {\n\t\tif arg == \"--\" {\n\t\t\treturn in[:i], in[i+1:]\n\t\t}\n\t}\n\n\treturn in, nil\n}\n\nfunc splitArguments(in string) ([]string, error) {\n\tvar (\n\t\tresult  []string\n\t\tbuilder strings.Builder\n\t\tescaped bool\n\t\tdelim   = ' '\n\t)\n\n\tfor _, r := range strings.TrimSpace(in) {\n\t\tif escaped {\n\t\t\tbuilder.WriteRune(r)\n\t\t\tescaped = false\n\t\t\tcontinue\n\t\t}\n\n\t\tswitch r {\n\t\tcase '\\\\':\n\t\t\tescaped = true\n\n\t\tcase delim:\n\t\t\tcurrent := builder.String()\n\t\t\tbuilder.Reset()\n\n\t\t\tif current != \"\" || delim != ' ' {\n\t\t\t\t// Only append empty words if they are not\n\t\t\t\t// delimited by spaces\n\t\t\t\tresult = append(result, current)\n\t\t\t}\n\t\t\tdelim = ' '\n\n\t\tcase '\"', '\\'', ' ':\n\t\t\tif delim == ' ' {\n\t\t\t\tdelim = r\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tfallthrough\n\n\t\tdefault:\n\t\t\tbuilder.WriteRune(r)\n\t\t}\n\t}\n\n\tif delim != ' ' {\n\t\treturn nil, fmt.Errorf(\"missing `%c`\", delim)\n\t}\n\n\tif escaped {\n\t\treturn nil, errors.New(\"unfinished escape\")\n\t}\n\n\t// Add the last word\n\tif builder.Len() > 0 {\n\t\tresult = append(result, builder.String())\n\t}\n\n\treturn result, nil\n}\n"
  },
  {
    "path": "cmd/bpf2go/tools_test.go",
    "content": "//go:build !windows\n\npackage main\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestSplitArguments(t *testing.T) {\n\ttestcases := []struct {\n\t\tin  string\n\t\tout []string\n\t}{\n\t\t{`foo`, []string{\"foo\"}},\n\t\t{`foo bar`, []string{\"foo\", \"bar\"}},\n\t\t{`foo  bar`, []string{\"foo\", \"bar\"}},\n\t\t{`\\\\`, []string{`\\`}},\n\t\t{`\\\\\\`, nil},\n\t\t{`foo\\ bar`, []string{\"foo bar\"}},\n\t\t{`foo \"\" bar`, []string{\"foo\", \"\", \"bar\"}},\n\t\t{`\"bar baz\"`, []string{\"bar baz\"}},\n\t\t{`'bar baz'`, []string{\"bar baz\"}},\n\t\t{`'bar \" \" baz'`, []string{`bar \" \" baz`}},\n\t\t{`\"bar \\\" baz\"`, []string{`bar \" baz`}},\n\t\t{`\"`, nil},\n\t\t{`'`, nil},\n\t}\n\n\tfor _, testcase := range testcases {\n\t\thave, err := splitArguments(testcase.in)\n\t\tif testcase.out == nil {\n\t\t\tif err == nil {\n\t\t\t\tt.Errorf(\"Test should fail for: %s\", testcase.in)\n\t\t\t}\n\t\t} else if !reflect.DeepEqual(testcase.out, have) {\n\t\t\tt.Logf(\"Have: %q\\n\", have)\n\t\t\tt.Logf(\"Want: %q\\n\", testcase.out)\n\t\t\tt.Errorf(\"Test fails for: %s\", testcase.in)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "collection.go",
    "content": "package ebpf\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"slices\"\n\t\"strings\"\n\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/kallsyms\"\n\t\"github.com/cilium/ebpf/internal/kconfig\"\n\t\"github.com/cilium/ebpf/internal/linux\"\n\t\"github.com/cilium/ebpf/internal/platform\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n)\n\n// CollectionOptions control loading a collection into the kernel.\n//\n// Maps and Programs are passed to NewMapWithOptions and NewProgramsWithOptions.\ntype CollectionOptions struct {\n\tMaps     MapOptions\n\tPrograms ProgramOptions\n\n\t// MapReplacements takes a set of Maps that will be used instead of\n\t// creating new ones when loading the CollectionSpec.\n\t//\n\t// For each given Map, there must be a corresponding MapSpec in\n\t// CollectionSpec.Maps, and its type, key/value size, max entries and flags\n\t// must match the values of the MapSpec.\n\t//\n\t// The given Maps are Clone()d before being used in the Collection, so the\n\t// caller can Close() them freely when they are no longer needed.\n\tMapReplacements map[string]*Map\n}\n\n// CollectionSpec describes a collection.\ntype CollectionSpec struct {\n\tMaps     map[string]*MapSpec\n\tPrograms map[string]*ProgramSpec\n\n\t// Variables refer to global variables declared in the ELF. They can be read\n\t// and modified freely before loading the Collection. Modifying them after\n\t// loading has no effect on a running eBPF program.\n\tVariables map[string]*VariableSpec\n\n\t// Types holds type information about Maps and Programs.\n\t// Modifications to Types are currently undefined behaviour.\n\tTypes *btf.Spec\n\n\t// ByteOrder specifies whether the ELF was compiled for\n\t// big-endian or little-endian architectures.\n\tByteOrder binary.ByteOrder\n}\n\n// Copy returns a recursive copy of the spec.\nfunc (cs *CollectionSpec) Copy() *CollectionSpec {\n\tif cs == nil {\n\t\treturn nil\n\t}\n\n\tcpy := CollectionSpec{\n\t\tMaps:      copyMapOfSpecs(cs.Maps),\n\t\tPrograms:  copyMapOfSpecs(cs.Programs),\n\t\tVariables: make(map[string]*VariableSpec, len(cs.Variables)),\n\t\tByteOrder: cs.ByteOrder,\n\t\tTypes:     cs.Types.Copy(),\n\t}\n\n\tfor name, spec := range cs.Variables {\n\t\tcpy.Variables[name] = spec.Copy()\n\t}\n\tif cs.Variables == nil {\n\t\tcpy.Variables = nil\n\t}\n\n\treturn &cpy\n}\n\nfunc copyMapOfSpecs[T interface{ Copy() T }](m map[string]T) map[string]T {\n\tif m == nil {\n\t\treturn nil\n\t}\n\n\tcpy := make(map[string]T, len(m))\n\tfor k, v := range m {\n\t\tcpy[k] = v.Copy()\n\t}\n\n\treturn cpy\n}\n\n// Assign the contents of a CollectionSpec to a struct.\n//\n// This function is a shortcut to manually checking the presence\n// of maps and programs in a CollectionSpec. Consider using bpf2go\n// if this sounds useful.\n//\n// 'to' must be a pointer to a struct. A field of the\n// struct is updated with values from Programs, Maps or Variables if it\n// has an `ebpf` tag and its type is *ProgramSpec, *MapSpec or *VariableSpec.\n// The tag's value specifies the name of the program or map as\n// found in the CollectionSpec.\n//\n//\tstruct {\n//\t    Foo     *ebpf.ProgramSpec  `ebpf:\"xdp_foo\"`\n//\t    Bar     *ebpf.MapSpec      `ebpf:\"bar_map\"`\n//\t    Var     *ebpf.VariableSpec `ebpf:\"some_var\"`\n//\t    Ignored int\n//\t}\n//\n// Returns an error if any of the eBPF objects can't be found, or\n// if the same Spec is assigned multiple times.\nfunc (cs *CollectionSpec) Assign(to interface{}) error {\n\tgetValue := func(typ reflect.Type, name string) (interface{}, error) {\n\t\tswitch typ {\n\t\tcase reflect.TypeOf((*ProgramSpec)(nil)):\n\t\t\tif p := cs.Programs[name]; p != nil {\n\t\t\t\treturn p, nil\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"missing program %q\", name)\n\n\t\tcase reflect.TypeOf((*MapSpec)(nil)):\n\t\t\tif m := cs.Maps[name]; m != nil {\n\t\t\t\treturn m, nil\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"missing map %q\", name)\n\n\t\tcase reflect.TypeOf((*VariableSpec)(nil)):\n\t\t\tif v := cs.Variables[name]; v != nil {\n\t\t\t\treturn v, nil\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"missing variable %q\", name)\n\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unsupported type %s\", typ)\n\t\t}\n\t}\n\n\treturn assignValues(to, getValue)\n}\n\n// LoadAndAssign loads Maps and Programs into the kernel and assigns them\n// to a struct.\n//\n// Omitting Map/Program.Close() during application shutdown is an error.\n// See the package documentation for details around Map and Program lifecycle.\n//\n// This function is a shortcut to manually checking the presence\n// of maps and programs in a CollectionSpec. Consider using bpf2go\n// if this sounds useful.\n//\n// 'to' must be a pointer to a struct. A field of the struct is updated with\n// a Program or Map if it has an `ebpf` tag and its type is *Program or *Map.\n// The tag's value specifies the name of the program or map as found in the\n// CollectionSpec. Before updating the struct, the requested objects and their\n// dependent resources are loaded into the kernel and populated with values if\n// specified.\n//\n//\tstruct {\n//\t    Foo     *ebpf.Program `ebpf:\"xdp_foo\"`\n//\t    Bar     *ebpf.Map     `ebpf:\"bar_map\"`\n//\t    Ignored int\n//\t}\n//\n// opts may be nil.\n//\n// Returns an error if any of the fields can't be found, or\n// if the same Map or Program is assigned multiple times.\nfunc (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions) error {\n\tloader, err := newCollectionLoader(cs, opts)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer loader.close()\n\n\t// Support assigning Programs and Maps, lazy-loading the required objects.\n\tassignedMaps := make(map[string]bool)\n\tassignedProgs := make(map[string]bool)\n\tassignedVars := make(map[string]bool)\n\n\tgetValue := func(typ reflect.Type, name string) (interface{}, error) {\n\t\tswitch typ {\n\n\t\tcase reflect.TypeOf((*Program)(nil)):\n\t\t\tassignedProgs[name] = true\n\t\t\treturn loader.loadProgram(name)\n\n\t\tcase reflect.TypeOf((*Map)(nil)):\n\t\t\tassignedMaps[name] = true\n\t\t\treturn loader.loadMap(name)\n\n\t\tcase reflect.TypeOf((*Variable)(nil)):\n\t\t\tassignedVars[name] = true\n\t\t\treturn loader.loadVariable(name)\n\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unsupported type %s\", typ)\n\t\t}\n\t}\n\n\t// Load the Maps and Programs requested by the annotated struct.\n\tif err := assignValues(to, getValue); err != nil {\n\t\treturn err\n\t}\n\n\t// Populate the requested maps. Has a chance of lazy-loading other dependent maps.\n\tif err := loader.populateDeferredMaps(); err != nil {\n\t\treturn err\n\t}\n\n\t// Evaluate the loader's objects after all (lazy)loading has taken place.\n\tfor n, m := range loader.maps {\n\t\tif m.typ.canStoreProgram() {\n\t\t\t// Require all lazy-loaded ProgramArrays to be assigned to the given object.\n\t\t\t// The kernel empties a ProgramArray once the last user space reference\n\t\t\t// to it closes, which leads to failed tail calls. Combined with the library\n\t\t\t// closing map fds via GC finalizers this can lead to surprising behaviour.\n\t\t\t// Only allow unassigned ProgramArrays when the library hasn't pre-populated\n\t\t\t// any entries from static value declarations. At this point, we know the map\n\t\t\t// is empty and there's no way for the caller to interact with the map going\n\t\t\t// forward.\n\t\t\tif !assignedMaps[n] && len(cs.Maps[n].Contents) > 0 {\n\t\t\t\treturn fmt.Errorf(\"ProgramArray %s must be assigned to prevent missed tail calls\", n)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Prevent loader.cleanup() from closing assigned Maps and Programs.\n\tfor m := range assignedMaps {\n\t\tdelete(loader.maps, m)\n\t}\n\tfor p := range assignedProgs {\n\t\tdelete(loader.programs, p)\n\t}\n\tfor p := range assignedVars {\n\t\tdelete(loader.vars, p)\n\t}\n\n\treturn nil\n}\n\n// Collection is a collection of live BPF resources present in the kernel.\ntype Collection struct {\n\tPrograms map[string]*Program\n\tMaps     map[string]*Map\n\n\t// Variables contains global variables used by the Collection's program(s). On\n\t// kernels older than 5.5, most interactions with Variables return\n\t// [ErrNotSupported].\n\tVariables map[string]*Variable\n}\n\n// NewCollection creates a Collection from the given spec, creating and\n// loading its declared resources into the kernel.\n//\n// Omitting Collection.Close() during application shutdown is an error.\n// See the package documentation for details around Map and Program lifecycle.\nfunc NewCollection(spec *CollectionSpec) (*Collection, error) {\n\treturn NewCollectionWithOptions(spec, CollectionOptions{})\n}\n\n// NewCollectionWithOptions creates a Collection from the given spec using\n// options, creating and loading its declared resources into the kernel.\n//\n// Omitting Collection.Close() during application shutdown is an error.\n// See the package documentation for details around Map and Program lifecycle.\nfunc NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (*Collection, error) {\n\tloader, err := newCollectionLoader(spec, &opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer loader.close()\n\n\t// Create maps first, as their fds need to be linked into programs.\n\tfor mapName := range spec.Maps {\n\t\tif _, err := loader.loadMap(mapName); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tfor progName, prog := range spec.Programs {\n\t\tif prog.Type == UnspecifiedProgram {\n\t\t\tcontinue\n\t\t}\n\n\t\tif _, err := loader.loadProgram(progName); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tfor varName := range spec.Variables {\n\t\tif _, err := loader.loadVariable(varName); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\t// Maps can contain Program and Map stubs, so populate them after\n\t// all Maps and Programs have been successfully loaded.\n\tif err := loader.populateDeferredMaps(); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Prevent loader.cleanup from closing maps, programs and vars.\n\tmaps, progs, vars := loader.maps, loader.programs, loader.vars\n\tloader.maps, loader.programs, loader.vars = nil, nil, nil\n\n\treturn &Collection{\n\t\tprogs,\n\t\tmaps,\n\t\tvars,\n\t}, nil\n}\n\ntype collectionLoader struct {\n\tcoll     *CollectionSpec\n\topts     *CollectionOptions\n\tmaps     map[string]*Map\n\tprograms map[string]*Program\n\tvars     map[string]*Variable\n\ttypes    *btf.Cache\n}\n\nfunc newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) (*collectionLoader, error) {\n\tif opts == nil {\n\t\topts = &CollectionOptions{}\n\t}\n\n\t// Check for existing MapSpecs in the CollectionSpec for all provided replacement maps.\n\tfor name := range opts.MapReplacements {\n\t\tif _, ok := coll.Maps[name]; !ok {\n\t\t\treturn nil, fmt.Errorf(\"replacement map %s not found in CollectionSpec\", name)\n\t\t}\n\t}\n\n\tif err := populateKallsyms(coll.Programs); err != nil {\n\t\treturn nil, fmt.Errorf(\"populating kallsyms caches: %w\", err)\n\t}\n\n\treturn &collectionLoader{\n\t\tcoll,\n\t\topts,\n\t\tmake(map[string]*Map),\n\t\tmake(map[string]*Program),\n\t\tmake(map[string]*Variable),\n\t\tbtf.NewCache(),\n\t}, nil\n}\n\n// populateKallsyms populates kallsyms caches, making lookups cheaper later on\n// during individual program loading. Since we have less context available\n// at those stages, we batch the lookups here instead to avoid redundant work.\nfunc populateKallsyms(progs map[string]*ProgramSpec) error {\n\t// Look up addresses of all kernel symbols referenced by all programs.\n\taddrs := make(map[string]uint64)\n\tfor _, p := range progs {\n\t\titer := p.Instructions.Iterate()\n\t\tfor iter.Next() {\n\t\t\tins := iter.Ins\n\t\t\tmeta, _ := ins.Metadata.Get(ksymMetaKey{}).(*ksymMeta)\n\t\t\tif meta != nil {\n\t\t\t\taddrs[meta.Name] = 0\n\t\t\t}\n\t\t}\n\t}\n\tif len(addrs) != 0 {\n\t\tif err := kallsyms.AssignAddresses(addrs); err != nil {\n\t\t\treturn fmt.Errorf(\"getting addresses from kallsyms: %w\", err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// close all resources left over in the collectionLoader.\nfunc (cl *collectionLoader) close() {\n\tfor _, m := range cl.maps {\n\t\tm.Close()\n\t}\n\tfor _, p := range cl.programs {\n\t\tp.Close()\n\t}\n}\n\nfunc (cl *collectionLoader) loadMap(mapName string) (*Map, error) {\n\tif m := cl.maps[mapName]; m != nil {\n\t\treturn m, nil\n\t}\n\n\tmapSpec := cl.coll.Maps[mapName]\n\tif mapSpec == nil {\n\t\treturn nil, fmt.Errorf(\"missing map %s\", mapName)\n\t}\n\n\tmapSpec = mapSpec.Copy()\n\n\t// Defer setting the mmapable flag on maps until load time. This avoids the\n\t// MapSpec having different flags on some kernel versions. Also avoid running\n\t// syscalls during ELF loading, so platforms like wasm can also parse an ELF.\n\tif isDataSection(mapSpec.Name) && haveMmapableMaps() == nil {\n\t\tmapSpec.Flags |= sys.BPF_F_MMAPABLE\n\t}\n\n\tif replaceMap, ok := cl.opts.MapReplacements[mapName]; ok {\n\t\t// Check compatibility with the replacement map after setting\n\t\t// feature-dependent map flags.\n\t\tif err := mapSpec.Compatible(replaceMap); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"using replacement map %s: %w\", mapSpec.Name, err)\n\t\t}\n\n\t\t// Clone the map to avoid closing user's map later on.\n\t\tm, err := replaceMap.Clone()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tcl.maps[mapName] = m\n\t\treturn m, nil\n\t}\n\n\tif err := mapSpec.updateDataSection(cl.coll.Variables, mapName); err != nil {\n\t\treturn nil, fmt.Errorf(\"assembling contents of map %s: %w\", mapName, err)\n\t}\n\n\tm, err := newMapWithOptions(mapSpec, cl.opts.Maps, cl.types)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"map %s: %w\", mapName, err)\n\t}\n\n\t// Finalize 'scalar' maps that don't refer to any other eBPF resources\n\t// potentially pending creation. This is needed for frozen maps like .rodata\n\t// that need to be finalized before invoking the verifier.\n\tif !mapSpec.Type.canStoreMapOrProgram() {\n\t\tif err := m.finalize(mapSpec); err != nil {\n\t\t\t_ = m.Close()\n\t\t\treturn nil, fmt.Errorf(\"finalizing map %s: %w\", mapName, err)\n\t\t}\n\t}\n\n\tcl.maps[mapName] = m\n\treturn m, nil\n}\n\nfunc (cl *collectionLoader) loadProgram(progName string) (*Program, error) {\n\tif prog := cl.programs[progName]; prog != nil {\n\t\treturn prog, nil\n\t}\n\n\tprogSpec := cl.coll.Programs[progName]\n\tif progSpec == nil {\n\t\treturn nil, fmt.Errorf(\"unknown program %s\", progName)\n\t}\n\n\t// Bail out early if we know the kernel is going to reject the program.\n\t// This skips loading map dependencies, saving some cleanup work later.\n\tif progSpec.Type == UnspecifiedProgram {\n\t\treturn nil, fmt.Errorf(\"cannot load program %s: program type is unspecified\", progName)\n\t}\n\n\tprogSpec = progSpec.Copy()\n\n\t// Rewrite any reference to a valid map in the program's instructions,\n\t// which includes all of its dependencies.\n\tfor i := range progSpec.Instructions {\n\t\tins := &progSpec.Instructions[i]\n\n\t\tif !ins.IsLoadFromMap() || ins.Reference() == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Don't overwrite map loads containing non-zero map fd's,\n\t\t// they can be manually included by the caller.\n\t\t// Map FDs/IDs are placed in the lower 32 bits of Constant.\n\t\tif int32(ins.Constant) > 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\tm, err := cl.loadMap(ins.Reference())\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"program %s: %w\", progName, err)\n\t\t}\n\n\t\tif err := ins.AssociateMap(m); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"program %s: map %s: %w\", progName, ins.Reference(), err)\n\t\t}\n\t}\n\n\tprog, err := newProgramWithOptions(progSpec, cl.opts.Programs, cl.types)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"program %s: %w\", progName, err)\n\t}\n\n\tcl.programs[progName] = prog\n\n\treturn prog, nil\n}\n\nfunc (cl *collectionLoader) loadVariable(varName string) (*Variable, error) {\n\tif v := cl.vars[varName]; v != nil {\n\t\treturn v, nil\n\t}\n\n\tvarSpec := cl.coll.Variables[varName]\n\tif varSpec == nil {\n\t\treturn nil, fmt.Errorf(\"unknown variable %s\", varName)\n\t}\n\n\tm, err := cl.loadMap(varSpec.SectionName)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"variable %s: %w\", varName, err)\n\t}\n\n\t// If the kernel is too old or the underlying map was created without\n\t// BPF_F_MMAPABLE, [Map.Memory] will return ErrNotSupported. In this case,\n\t// emit a Variable with a nil Memory. This keeps Collection{Spec}.Variables\n\t// consistent across systems with different feature sets without breaking\n\t// LoadAndAssign.\n\tvar mm *Memory\n\tif unsafeMemory {\n\t\tmm, err = m.unsafeMemory()\n\t} else {\n\t\tmm, err = m.Memory()\n\t}\n\tif err != nil && !errors.Is(err, ErrNotSupported) {\n\t\treturn nil, fmt.Errorf(\"variable %s: getting memory for map %s: %w\", varName, varSpec.SectionName, err)\n\t}\n\n\tv, err := newVariable(\n\t\tvarSpec.Name,\n\t\tvarSpec.Offset,\n\t\tvarSpec.Size(),\n\t\tvarSpec.Type,\n\t\tmm,\n\t)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"variable %s: %w\", varName, err)\n\t}\n\n\tcl.vars[varName] = v\n\treturn v, nil\n}\n\n// populateDeferredMaps iterates maps holding programs or other maps and loads\n// any dependencies. Populates all maps in cl and freezes them if specified.\nfunc (cl *collectionLoader) populateDeferredMaps() error {\n\tfor mapName, m := range cl.maps {\n\t\tmapSpec, ok := cl.coll.Maps[mapName]\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"missing map spec %s\", mapName)\n\t\t}\n\n\t\t// Scalar maps without Map or Program references are finalized during\n\t\t// creation. Don't finalize them again.\n\t\tif !mapSpec.Type.canStoreMapOrProgram() {\n\t\t\tcontinue\n\t\t}\n\n\t\tmapSpec = mapSpec.Copy()\n\n\t\t// MapSpecs that refer to inner maps or programs within the same\n\t\t// CollectionSpec do so using strings. These strings are used as the key\n\t\t// to look up the respective object in the Maps or Programs fields.\n\t\t// Resolve those references to actual Map or Program resources that\n\t\t// have been loaded into the kernel.\n\t\tfor i, kv := range mapSpec.Contents {\n\t\t\tobjName, ok := kv.Value.(string)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tswitch t := mapSpec.Type; {\n\t\t\tcase t.canStoreProgram():\n\t\t\t\t// loadProgram is idempotent and could return an existing Program.\n\t\t\t\tprog, err := cl.loadProgram(objName)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"loading program %s, for map %s: %w\", objName, mapName, err)\n\t\t\t\t}\n\t\t\t\tmapSpec.Contents[i] = MapKV{kv.Key, prog}\n\n\t\t\tcase t.canStoreMap():\n\t\t\t\t// loadMap is idempotent and could return an existing Map.\n\t\t\t\tinnerMap, err := cl.loadMap(objName)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"loading inner map %s, for map %s: %w\", objName, mapName, err)\n\t\t\t\t}\n\t\t\t\tmapSpec.Contents[i] = MapKV{kv.Key, innerMap}\n\t\t\t}\n\t\t}\n\n\t\tif mapSpec.Type == StructOpsMap {\n\t\t\t// populate StructOps data into `kernVData`\n\t\t\tif err := cl.populateStructOps(m, mapSpec); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\t// Populate and freeze the map if specified.\n\t\tif err := m.finalize(mapSpec); err != nil {\n\t\t\treturn fmt.Errorf(\"populating map %s: %w\", mapName, err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// populateStructOps translates the user struct bytes into the kernel value struct\n// layout for a struct_ops map and writes the result back to mapSpec.Contents[0].\nfunc (cl *collectionLoader) populateStructOps(m *Map, mapSpec *MapSpec) error {\n\tuserType, ok := btf.As[*btf.Struct](mapSpec.Value)\n\tif !ok {\n\t\treturn fmt.Errorf(\"value should be a *Struct\")\n\t}\n\n\tuserData, err := mapSpec.dataSection()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"getting data section: %w\", err)\n\t}\n\tif len(userData) < int(userType.Size) {\n\t\treturn fmt.Errorf(\"user data too short: have %d, need at least %d\", len(userData), userType.Size)\n\t}\n\n\tvType, _, module, err := structOpsFindTarget(userType, cl.types)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"struct_ops value type %q: %w\", userType.Name, err)\n\t}\n\tdefer module.Close()\n\n\t// Find the inner ops struct embedded in the value struct.\n\tkType, kTypeOff, err := structOpsFindInnerType(vType)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tkernVData := make([]byte, int(vType.Size))\n\tfor _, m := range userType.Members {\n\t\ti := slices.IndexFunc(kType.Members, func(km btf.Member) bool {\n\t\t\treturn km.Name == m.Name\n\t\t})\n\n\t\t// Allow field to not exist in target as long as the source is zero.\n\t\tif i == -1 {\n\t\t\tmSize, err := btf.Sizeof(m.Type)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"sizeof(user.%s): %w\", m.Name, err)\n\t\t\t}\n\t\t\tsrcOff := int(m.Offset.Bytes())\n\t\t\tif srcOff < 0 || srcOff+mSize > len(userData) {\n\t\t\t\treturn fmt.Errorf(\"member %q: userdata is too small\", m.Name)\n\t\t\t}\n\n\t\t\t// let fail if the field in type user type is missing in type kern type\n\t\t\tif !structOpsIsMemZeroed(userData[srcOff : srcOff+mSize]) {\n\t\t\t\treturn fmt.Errorf(\"%s doesn't exist in %s, but it has non-zero value\", m.Name, kType.Name)\n\t\t\t}\n\n\t\t\tcontinue\n\t\t}\n\n\t\tkm := kType.Members[i]\n\n\t\tswitch btf.UnderlyingType(m.Type).(type) {\n\t\tcase *btf.Pointer:\n\t\t\t// If this is a pointer → resolve struct_ops program.\n\t\t\tpsKey := kType.Name + \":\" + m.Name\n\t\t\tfor k, ps := range cl.coll.Programs {\n\t\t\t\tif ps.AttachTo == psKey {\n\t\t\t\t\tp, ok := cl.programs[k]\n\t\t\t\t\tif !ok || p == nil {\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}\n\t\t\t\t\tif err := structOpsPopulateValue(km, kernVData[kTypeOff:], p); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\tdefault:\n\t\t\t// Otherwise → memcpy the field contents.\n\t\t\tif err := structOpsCopyMember(m, km, userData, kernVData[kTypeOff:]); err != nil {\n\t\t\t\treturn fmt.Errorf(\"field %s: %w\", kType.Name, err)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Populate the map explicitly and keep a reference on cl.programs.\n\t// This is necessary because we may inline fds into kernVData which\n\t// may become invalid if the GC frees them.\n\tif err := m.Put(uint32(0), kernVData); err != nil {\n\t\treturn err\n\t}\n\tmapSpec.Contents = nil\n\truntime.KeepAlive(cl.programs)\n\n\treturn nil\n}\n\n// resolveKconfig resolves all variables declared in .kconfig and populates\n// m.Contents. Does nothing if the given m.Contents is non-empty.\nfunc resolveKconfig(m *MapSpec) error {\n\tds, ok := m.Value.(*btf.Datasec)\n\tif !ok {\n\t\treturn errors.New(\"map value is not a Datasec\")\n\t}\n\n\tif platform.IsWindows {\n\t\treturn fmt.Errorf(\".kconfig: %w\", internal.ErrNotSupportedOnOS)\n\t}\n\n\ttype configInfo struct {\n\t\toffset uint32\n\t\tsize   uint32\n\t\ttyp    btf.Type\n\t}\n\n\tconfigs := make(map[string]configInfo)\n\n\tdata := make([]byte, ds.Size)\n\tfor _, vsi := range ds.Vars {\n\t\tv := vsi.Type.(*btf.Var)\n\t\tn := v.TypeName()\n\n\t\tswitch n {\n\t\tcase \"LINUX_KERNEL_VERSION\":\n\t\t\tif integer, ok := v.Type.(*btf.Int); !ok || integer.Size != 4 {\n\t\t\t\treturn fmt.Errorf(\"variable %s must be a 32 bits integer, got %s\", n, v.Type)\n\t\t\t}\n\n\t\t\tkv, err := linux.KernelVersion()\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"getting kernel version: %w\", err)\n\t\t\t}\n\t\t\tinternal.NativeEndian.PutUint32(data[vsi.Offset:], kv.Kernel())\n\n\t\tcase \"LINUX_HAS_SYSCALL_WRAPPER\":\n\t\t\tinteger, ok := v.Type.(*btf.Int)\n\t\t\tif !ok {\n\t\t\t\treturn fmt.Errorf(\"variable %s must be an integer, got %s\", n, v.Type)\n\t\t\t}\n\t\t\tvar value uint64 = 1\n\t\t\tif err := haveSyscallWrapper(); errors.Is(err, ErrNotSupported) {\n\t\t\t\tvalue = 0\n\t\t\t} else if err != nil {\n\t\t\t\treturn fmt.Errorf(\"unable to derive a value for LINUX_HAS_SYSCALL_WRAPPER: %w\", err)\n\t\t\t}\n\n\t\t\tif err := kconfig.PutInteger(data[vsi.Offset:], integer, value); err != nil {\n\t\t\t\treturn fmt.Errorf(\"set LINUX_HAS_SYSCALL_WRAPPER: %w\", err)\n\t\t\t}\n\n\t\tdefault: // Catch CONFIG_*.\n\t\t\tconfigs[n] = configInfo{\n\t\t\t\toffset: vsi.Offset,\n\t\t\t\tsize:   vsi.Size,\n\t\t\t\ttyp:    v.Type,\n\t\t\t}\n\t\t}\n\t}\n\n\t// We only parse kconfig file if a CONFIG_* variable was found.\n\tif len(configs) > 0 {\n\t\tf, err := linux.FindKConfig()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"cannot find a kconfig file: %w\", err)\n\t\t}\n\t\tdefer f.Close()\n\n\t\tfilter := make(map[string]struct{}, len(configs))\n\t\tfor config := range configs {\n\t\t\tfilter[config] = struct{}{}\n\t\t}\n\n\t\tkernelConfig, err := kconfig.Parse(f, filter)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"cannot parse kconfig file: %w\", err)\n\t\t}\n\n\t\tfor n, info := range configs {\n\t\t\tvalue, ok := kernelConfig[n]\n\t\t\tif !ok {\n\t\t\t\treturn fmt.Errorf(\"config option %q does not exist on this kernel\", n)\n\t\t\t}\n\n\t\t\terr := kconfig.PutValue(data[info.offset:info.offset+info.size], info.typ, value)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"problem adding value for %s: %w\", n, err)\n\t\t\t}\n\t\t}\n\t}\n\n\tm.Contents = []MapKV{{uint32(0), data}}\n\n\treturn nil\n}\n\n// LoadCollection reads an object file and creates and loads its declared\n// resources into the kernel.\n//\n// Omitting Collection.Close() during application shutdown is an error.\n// See the package documentation for details around Map and Program lifecycle.\nfunc LoadCollection(file string) (*Collection, error) {\n\tif platform.IsWindows {\n\t\t// This mirrors a check in efW.\n\t\tif ext := filepath.Ext(file); ext == \".sys\" {\n\t\t\treturn loadCollectionFromNativeImage(file)\n\t\t}\n\t}\n\n\tspec, err := LoadCollectionSpec(file)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewCollection(spec)\n}\n\n// Assign the contents of a Collection to a struct.\n//\n// This function bridges functionality between bpf2go generated\n// code and any functionality better implemented in Collection.\n//\n// 'to' must be a pointer to a struct. A field of the\n// struct is updated with values from Programs or Maps if it\n// has an `ebpf` tag and its type is *Program or *Map.\n// The tag's value specifies the name of the program or map as\n// found in the CollectionSpec.\n//\n//\tstruct {\n//\t    Foo     *ebpf.Program `ebpf:\"xdp_foo\"`\n//\t    Bar     *ebpf.Map     `ebpf:\"bar_map\"`\n//\t    Ignored int\n//\t}\n//\n// Returns an error if any of the eBPF objects can't be found, or\n// if the same Map or Program is assigned multiple times.\n//\n// Ownership and Close()ing responsibility is transferred to `to`\n// for any successful assigns. On error `to` is left in an undefined state.\nfunc (coll *Collection) Assign(to interface{}) error {\n\tassignedMaps := make(map[string]bool)\n\tassignedProgs := make(map[string]bool)\n\tassignedVars := make(map[string]bool)\n\n\t// Assign() only transfers already-loaded Maps and Programs. No extra\n\t// loading is done.\n\tgetValue := func(typ reflect.Type, name string) (interface{}, error) {\n\t\tswitch typ {\n\n\t\tcase reflect.TypeOf((*Program)(nil)):\n\t\t\tif p := coll.Programs[name]; p != nil {\n\t\t\t\tassignedProgs[name] = true\n\t\t\t\treturn p, nil\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"missing program %q\", name)\n\n\t\tcase reflect.TypeOf((*Map)(nil)):\n\t\t\tif m := coll.Maps[name]; m != nil {\n\t\t\t\tassignedMaps[name] = true\n\t\t\t\treturn m, nil\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"missing map %q\", name)\n\n\t\tcase reflect.TypeOf((*Variable)(nil)):\n\t\t\tif v := coll.Variables[name]; v != nil {\n\t\t\t\tassignedVars[name] = true\n\t\t\t\treturn v, nil\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"missing variable %q\", name)\n\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unsupported type %s\", typ)\n\t\t}\n\t}\n\n\tif err := assignValues(to, getValue); err != nil {\n\t\treturn err\n\t}\n\n\t// Finalize ownership transfer\n\tfor p := range assignedProgs {\n\t\tdelete(coll.Programs, p)\n\t}\n\tfor m := range assignedMaps {\n\t\tdelete(coll.Maps, m)\n\t}\n\tfor s := range assignedVars {\n\t\tdelete(coll.Variables, s)\n\t}\n\n\treturn nil\n}\n\n// Close frees all maps and programs associated with the collection.\n//\n// The collection mustn't be used afterwards.\nfunc (coll *Collection) Close() {\n\tfor _, prog := range coll.Programs {\n\t\tprog.Close()\n\t}\n\tfor _, m := range coll.Maps {\n\t\tm.Close()\n\t}\n}\n\n// DetachMap removes the named map from the Collection.\n//\n// This means that a later call to Close() will not affect this map.\n//\n// Returns nil if no map of that name exists.\nfunc (coll *Collection) DetachMap(name string) *Map {\n\tm := coll.Maps[name]\n\tdelete(coll.Maps, name)\n\treturn m\n}\n\n// DetachProgram removes the named program from the Collection.\n//\n// This means that a later call to Close() will not affect this program.\n//\n// Returns nil if no program of that name exists.\nfunc (coll *Collection) DetachProgram(name string) *Program {\n\tp := coll.Programs[name]\n\tdelete(coll.Programs, name)\n\treturn p\n}\n\n// structField represents a struct field containing the ebpf struct tag.\ntype structField struct {\n\treflect.StructField\n\tvalue reflect.Value\n}\n\n// ebpfFields extracts field names tagged with 'ebpf' from a struct type.\n// Keep track of visited types to avoid infinite recursion.\nfunc ebpfFields(structVal reflect.Value, visited map[reflect.Type]bool) ([]structField, error) {\n\tif visited == nil {\n\t\tvisited = make(map[reflect.Type]bool)\n\t}\n\n\tstructType := structVal.Type()\n\tif structType.Kind() != reflect.Struct {\n\t\treturn nil, fmt.Errorf(\"%s is not a struct\", structType)\n\t}\n\n\tif visited[structType] {\n\t\treturn nil, fmt.Errorf(\"recursion on type %s\", structType)\n\t}\n\n\tfields := make([]structField, 0, structType.NumField())\n\tfor i := 0; i < structType.NumField(); i++ {\n\t\tfield := structField{structType.Field(i), structVal.Field(i)}\n\n\t\t// If the field is tagged, gather it and move on.\n\t\tname := field.Tag.Get(\"ebpf\")\n\t\tif name != \"\" {\n\t\t\tfields = append(fields, field)\n\t\t\tcontinue\n\t\t}\n\n\t\t// If the field does not have an ebpf tag, but is a struct or a pointer\n\t\t// to a struct, attempt to gather its fields as well.\n\t\tvar v reflect.Value\n\t\tswitch field.Type.Kind() {\n\t\tcase reflect.Ptr:\n\t\t\tif field.Type.Elem().Kind() != reflect.Struct {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif field.value.IsNil() {\n\t\t\t\treturn nil, fmt.Errorf(\"nil pointer to %s\", structType)\n\t\t\t}\n\n\t\t\t// Obtain the destination type of the pointer.\n\t\t\tv = field.value.Elem()\n\n\t\tcase reflect.Struct:\n\t\t\t// Reference the value's type directly.\n\t\t\tv = field.value\n\n\t\tdefault:\n\t\t\tcontinue\n\t\t}\n\n\t\tinner, err := ebpfFields(v, visited)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"field %s: %w\", field.Name, err)\n\t\t}\n\n\t\tfields = append(fields, inner...)\n\t}\n\n\treturn fields, nil\n}\n\n// assignValues attempts to populate all fields of 'to' tagged with 'ebpf'.\n//\n// getValue is called for every tagged field of 'to' and must return the value\n// to be assigned to the field with the given typ and name.\nfunc assignValues(to interface{},\n\tgetValue func(typ reflect.Type, name string) (interface{}, error)) error {\n\n\ttoValue := reflect.ValueOf(to)\n\tif toValue.Type().Kind() != reflect.Ptr {\n\t\treturn fmt.Errorf(\"%T is not a pointer to struct\", to)\n\t}\n\n\tif toValue.IsNil() {\n\t\treturn fmt.Errorf(\"nil pointer to %T\", to)\n\t}\n\n\tfields, err := ebpfFields(toValue.Elem(), nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\ttype elem struct {\n\t\t// Either *Map or *Program\n\t\ttyp  reflect.Type\n\t\tname string\n\t}\n\n\tassigned := make(map[elem]string)\n\tfor _, field := range fields {\n\t\t// Get string value the field is tagged with.\n\t\ttag := field.Tag.Get(\"ebpf\")\n\t\tif strings.Contains(tag, \",\") {\n\t\t\treturn fmt.Errorf(\"field %s: ebpf tag contains a comma\", field.Name)\n\t\t}\n\n\t\t// Check if the eBPF object with the requested\n\t\t// type and tag was already assigned elsewhere.\n\t\te := elem{field.Type, tag}\n\t\tif af := assigned[e]; af != \"\" {\n\t\t\treturn fmt.Errorf(\"field %s: object %q was already assigned to %s\", field.Name, tag, af)\n\t\t}\n\n\t\t// Get the eBPF object referred to by the tag.\n\t\tvalue, err := getValue(field.Type, tag)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"field %s: %w\", field.Name, err)\n\t\t}\n\n\t\tif !field.value.CanSet() {\n\t\t\treturn fmt.Errorf(\"field %s: can't set value\", field.Name)\n\t\t}\n\t\tfield.value.Set(reflect.ValueOf(value))\n\n\t\tassigned[e] = field.Name\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "collection_other.go",
    "content": "//go:build !windows\n\npackage ebpf\n\nimport \"github.com/cilium/ebpf/internal\"\n\nfunc loadCollectionFromNativeImage(_ string) (*Collection, error) {\n\treturn nil, internal.ErrNotSupportedOnOS\n}\n"
  },
  {
    "path": "collection_test.go",
    "content": "package ebpf\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"reflect\"\n\t\"slices\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n\t\"github.com/cilium/ebpf/internal/testutils/testmain\"\n)\n\nfunc TestMain(m *testing.M) {\n\ttestmain.Run(m)\n}\n\nfunc TestCollectionSpecNotModified(t *testing.T) {\n\tspec := &CollectionSpec{\n\t\tMaps: map[string]*MapSpec{\n\t\t\t\"my-map\": {\n\t\t\t\tType:       Array,\n\t\t\t\tKeySize:    4,\n\t\t\t\tValueSize:  4,\n\t\t\t\tMaxEntries: 1,\n\t\t\t},\n\t\t\t\".rodata\": {\n\t\t\t\tType:       Array,\n\t\t\t\tKeySize:    4,\n\t\t\t\tValueSize:  4,\n\t\t\t\tMaxEntries: 1,\n\t\t\t\tFlags:      0, // Loader sets BPF_F_MMAPABLE.\n\t\t\t\tContents:   []MapKV{{uint32(0), uint32(1)}},\n\t\t\t},\n\t\t},\n\t\tPrograms: map[string]*ProgramSpec{\n\t\t\t\"test\": {\n\t\t\t\tType: SocketFilter,\n\t\t\t\tInstructions: asm.Instructions{\n\t\t\t\t\tasm.LoadImm(asm.R1, 0, asm.DWord).WithReference(\".rodata\"),\n\t\t\t\t\tasm.LoadImm(asm.R1, 0, asm.DWord).WithReference(\"my-map\"),\n\t\t\t\t\tasm.LoadImm(asm.R0, 0, asm.DWord),\n\t\t\t\t\tasm.Return(),\n\t\t\t\t},\n\t\t\t\tLicense: \"MIT\",\n\t\t\t},\n\t\t},\n\t}\n\n\torig := spec.Copy()\n\tcoll := mustNewCollection(t, spec, nil)\n\tqt.Assert(t, qt.CmpEquals(orig, spec, csCmpOpts))\n\n\tfor name := range spec.Maps {\n\t\tqt.Assert(t, qt.IsNotNil(coll.Maps[name]))\n\t}\n\n\tfor name := range spec.Programs {\n\t\tqt.Assert(t, qt.IsNotNil(coll.Programs[name]))\n\t}\n}\n\nfunc TestCollectionSpecCopy(t *testing.T) {\n\tms := &MapSpec{\n\t\tType:       Array,\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: 1,\n\t}\n\n\tcs := &CollectionSpec{\n\t\tmap[string]*MapSpec{\"my-map\": ms},\n\t\tmap[string]*ProgramSpec{\n\t\t\t\"test\": {\n\t\t\t\tType: SocketFilter,\n\t\t\t\tInstructions: asm.Instructions{\n\t\t\t\t\tasm.LoadMapPtr(asm.R1, 0),\n\t\t\t\t\tasm.LoadImm(asm.R0, 0, asm.DWord),\n\t\t\t\t\tasm.Return(),\n\t\t\t\t},\n\t\t\t\tLicense: \"MIT\",\n\t\t\t},\n\t\t},\n\t\tmap[string]*VariableSpec{\n\t\t\t\"my-var\": {\n\t\t\t\tName:        \"my-var\",\n\t\t\t\tSectionName: \"my-map\",\n\t\t\t\tOffset:      0,\n\t\t\t},\n\t\t},\n\t\t&btf.Spec{},\n\t\tbinary.LittleEndian,\n\t}\n\n\tqt.Check(t, qt.IsNil((*CollectionSpec)(nil).Copy()))\n\tqt.Assert(t, testutils.IsDeepCopy(cs.Copy(), cs))\n}\n\n// Load key \"0\" from a map called \"test-map\" and return the value.\nvar loadKeyFromMapProgramSpec = &ProgramSpec{\n\tType: SocketFilter,\n\tInstructions: asm.Instructions{\n\t\t// R1 map\n\t\tasm.LoadMapPtr(asm.R1, 0).WithReference(\"test-map\"),\n\t\t// R2 key\n\t\tasm.Mov.Reg(asm.R2, asm.R10),\n\t\tasm.Add.Imm(asm.R2, -4),\n\t\tasm.StoreImm(asm.R2, 0, 0, asm.Word),\n\t\t// Lookup map[0]\n\t\tasm.FnMapLookupElem.Call(),\n\t\tasm.JEq.Imm(asm.R0, 0, \"error\"),\n\t\tasm.LoadMem(asm.R0, asm.R0, 0, asm.Word),\n\t\tasm.Ja.Label(\"ret\"),\n\t\t// Windows doesn't allow directly using R0 result from FnMapLookupElem.\n\t\tasm.Mov.Imm(asm.R0, 0).WithSymbol(\"error\"),\n\t\tasm.Return().WithSymbol(\"ret\"),\n\t},\n}\n\nfunc TestCollectionSpecMapReplacements(t *testing.T) {\n\tcs := &CollectionSpec{\n\t\tMaps: map[string]*MapSpec{\n\t\t\t\"test-map\": {\n\t\t\t\tType:       Array,\n\t\t\t\tKeySize:    4,\n\t\t\t\tValueSize:  4,\n\t\t\t\tMaxEntries: 1,\n\t\t\t},\n\t\t},\n\t\tPrograms: map[string]*ProgramSpec{\n\t\t\t\"test-prog\": loadKeyFromMapProgramSpec.Copy(),\n\t\t},\n\t}\n\n\t// Replace the map with another one\n\tnewMap := mustNewMap(t, cs.Maps[\"test-map\"], nil)\n\n\terr := newMap.Put(uint32(0), uint32(2))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tcoll := mustNewCollection(t, cs, &CollectionOptions{\n\t\tMapReplacements: map[string]*Map{\n\t\t\t\"test-map\": newMap,\n\t\t},\n\t})\n\n\tret := mustRun(t, coll.Programs[\"test-prog\"], nil)\n\n\tif ret != 2 {\n\t\tt.Fatal(\"new / override map not used\")\n\t}\n\n\t// Check that newMap isn't closed when the collection is closed\n\tcoll.Close()\n\terr = newMap.Put(uint32(0), uint32(3))\n\tif err != nil {\n\t\tt.Fatalf(\"failed to update replaced map: %s\", err)\n\t}\n}\n\nfunc TestCollectionSpecMapReplacements_NonExistingMap(t *testing.T) {\n\tcs := &CollectionSpec{\n\t\tMaps: map[string]*MapSpec{\n\t\t\t\"test-map\": {\n\t\t\t\tType:       Array,\n\t\t\t\tKeySize:    4,\n\t\t\t\tValueSize:  4,\n\t\t\t\tMaxEntries: 1,\n\t\t\t},\n\t\t},\n\t}\n\n\t// Override non-existing map\n\tnewMap := mustNewMap(t, cs.Maps[\"test-map\"], nil)\n\n\tcoll, err := newCollection(t, cs, &CollectionOptions{\n\t\tMapReplacements: map[string]*Map{\n\t\t\t\"non-existing-map\": newMap,\n\t\t},\n\t})\n\tif err == nil {\n\t\tcoll.Close()\n\t\tt.Fatal(\"Overriding a non existing map did not fail\")\n\t}\n}\n\nfunc TestCollectionSpecMapReplacements_SpecMismatch(t *testing.T) {\n\tcs := &CollectionSpec{\n\t\tMaps: map[string]*MapSpec{\n\t\t\t\"test-map\": {\n\t\t\t\tType:       Array,\n\t\t\t\tKeySize:    4,\n\t\t\t\tValueSize:  4,\n\t\t\t\tMaxEntries: 1,\n\t\t\t},\n\t\t},\n\t}\n\n\t// Override map with mismatching spec\n\tnewMap := mustNewMap(t, &MapSpec{\n\t\tType:       Array,\n\t\tKeySize:    4,\n\t\tValueSize:  8, // this is different\n\t\tMaxEntries: 1,\n\t}, nil)\n\n\tcoll, err := newCollection(t, cs, &CollectionOptions{\n\t\tMapReplacements: map[string]*Map{\n\t\t\t\"test-map\": newMap,\n\t\t},\n\t})\n\tif err == nil {\n\t\tcoll.Close()\n\t\tt.Fatal(\"Overriding a map with a mismatching spec did not fail\")\n\t}\n\tif !errors.Is(err, ErrMapIncompatible) {\n\t\tt.Fatalf(\"Overriding a map with a mismatching spec failed with the wrong error\")\n\t}\n}\n\nfunc TestMapReplacementsDataSections(t *testing.T) {\n\t// In some circumstances, it can be useful to share data sections between\n\t// Collections, for example to hold a ready/pause flag or some metrics.\n\t// Test read-only maps for good measure.\n\tfile := testutils.NativeFile(t, \"testdata/loader-%s.elf\")\n\tspec, err := LoadCollectionSpec(file)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tvar objs struct {\n\t\tData   *Map `ebpf:\".data\"`\n\t\tROData *Map `ebpf:\".rodata\"`\n\t}\n\n\tmustLoadAndAssign(t, spec, &objs, nil)\n\tdefer objs.Data.Close()\n\tdefer objs.ROData.Close()\n\n\tmustLoadAndAssign(t, spec, &objs, &CollectionOptions{\n\t\tMapReplacements: map[string]*Map{\n\t\t\t\".data\":   objs.Data,\n\t\t\t\".rodata\": objs.ROData,\n\t\t},\n\t})\n\tqt.Assert(t, qt.IsNil(objs.Data.Close()))\n\tqt.Assert(t, qt.IsNil(objs.ROData.Close()))\n}\n\nfunc TestCollectionSpec_LoadAndAssign_LazyLoading(t *testing.T) {\n\tspec := &CollectionSpec{\n\t\tMaps: map[string]*MapSpec{\n\t\t\t\"valid\": {\n\t\t\t\tType:       Array,\n\t\t\t\tKeySize:    4,\n\t\t\t\tValueSize:  4,\n\t\t\t\tMaxEntries: 1,\n\t\t\t},\n\t\t\t\"bogus\": {\n\t\t\t\tType:       Array,\n\t\t\t\tMaxEntries: 0,\n\t\t\t},\n\t\t},\n\t\tPrograms: map[string]*ProgramSpec{\n\t\t\t\"valid\": {\n\t\t\t\tType: SocketFilter,\n\t\t\t\tInstructions: asm.Instructions{\n\t\t\t\t\tasm.LoadImm(asm.R0, 0, asm.DWord),\n\t\t\t\t\tasm.Return(),\n\t\t\t\t},\n\t\t\t\tLicense: \"MIT\",\n\t\t\t},\n\t\t\t\"bogus\": {\n\t\t\t\tType: SocketFilter,\n\t\t\t\tInstructions: asm.Instructions{\n\t\t\t\t\t// Undefined return value is rejected\n\t\t\t\t\tasm.Return(),\n\t\t\t\t},\n\t\t\t\tLicense: \"MIT\",\n\t\t\t},\n\t\t},\n\t}\n\n\tvar objs struct {\n\t\tProg *Program `ebpf:\"valid\"`\n\t\tMap  *Map     `ebpf:\"valid\"`\n\t}\n\n\tmustLoadAndAssign(t, spec, &objs, nil)\n\tdefer objs.Prog.Close()\n\tdefer objs.Map.Close()\n\n\tif objs.Prog == nil {\n\t\tt.Error(\"Program is nil\")\n\t}\n\n\tif objs.Map == nil {\n\t\tt.Error(\"Map is nil\")\n\t}\n}\n\nfunc TestCollectionSpecAssign(t *testing.T) {\n\tvar specs struct {\n\t\tProgram  *ProgramSpec  `ebpf:\"prog1\"`\n\t\tMap      *MapSpec      `ebpf:\"map1\"`\n\t\tVariable *VariableSpec `ebpf:\"var1\"`\n\t}\n\n\tmapSpec := &MapSpec{\n\t\tType:       Array,\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: 1,\n\t}\n\tprogSpec := &ProgramSpec{\n\t\tType: SocketFilter,\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.LoadImm(asm.R0, 0, asm.DWord),\n\t\t\tasm.Return(),\n\t\t},\n\t\tLicense: \"MIT\",\n\t}\n\n\tcs := &CollectionSpec{\n\t\tMaps: map[string]*MapSpec{\n\t\t\t\"map1\": mapSpec,\n\t\t},\n\t\tPrograms: map[string]*ProgramSpec{\n\t\t\t\"prog1\": progSpec,\n\t\t},\n\t\tVariables: map[string]*VariableSpec{\n\t\t\t\"var1\": {},\n\t\t},\n\t}\n\n\tif err := cs.Assign(&specs); err != nil {\n\t\tt.Fatal(\"Can't assign spec:\", err)\n\t}\n\n\tif specs.Program != progSpec {\n\t\tt.Fatalf(\"Expected Program to be %p, got %p\", progSpec, specs.Program)\n\t}\n\n\tif specs.Map != mapSpec {\n\t\tt.Fatalf(\"Expected Map to be %p, got %p\", mapSpec, specs.Map)\n\t}\n\n\tif err := cs.Assign(new(int)); err == nil {\n\t\tt.Fatal(\"Assign allows to besides *struct\")\n\t}\n\n\tif err := cs.Assign(new(struct{ Foo int })); err != nil {\n\t\tt.Fatal(\"Assign doesn't ignore untagged fields\")\n\t}\n\n\tunexported := new(struct {\n\t\tfoo *MapSpec `ebpf:\"map1\"`\n\t})\n\n\tif err := cs.Assign(unexported); err == nil {\n\t\tt.Error(\"Assign should return an error on unexported fields\")\n\t}\n}\n\nfunc TestNewCollectionFdLeak(t *testing.T) {\n\tspec := &CollectionSpec{\n\t\tMaps: map[string]*MapSpec{\n\t\t\t\"map1\": {\n\t\t\t\tType: Array, KeySize: 4, ValueSize: 4, MaxEntries: 1,\n\t\t\t\t// 8 byte value will cause m.finalize to fail.\n\t\t\t\tContents: []MapKV{{uint32(0), uint64(0)}},\n\t\t\t},\n\t\t},\n\t}\n\n\t_, err := newCollection(t, spec, nil)\n\tqt.Assert(t, qt.IsNotNil(err))\n}\n\nfunc TestAssignValues(t *testing.T) {\n\tzero := func(t reflect.Type, name string) (interface{}, error) {\n\t\treturn reflect.Zero(t).Interface(), nil\n\t}\n\n\ttype t1 struct {\n\t\tBar int `ebpf:\"bar\"`\n\t}\n\n\ttype t2 struct {\n\t\tt1\n\t\tFoo int `ebpf:\"foo\"`\n\t}\n\n\ttype t2ptr struct {\n\t\t*t1\n\t\tFoo int `ebpf:\"foo\"`\n\t}\n\n\tinvalid := []struct {\n\t\tname string\n\t\tto   interface{}\n\t}{\n\t\t{\"non-struct\", 1},\n\t\t{\"non-pointer struct\", t1{}},\n\t\t{\"pointer to non-struct\", new(int)},\n\t\t{\"embedded nil pointer\", &t2ptr{}},\n\t\t{\"unexported field\", new(struct {\n\t\t\tfoo int `ebpf:\"foo\"`\n\t\t})},\n\t\t{\"identical tag\", new(struct {\n\t\t\tFoo1 int `ebpf:\"foo\"`\n\t\t\tFoo2 int `ebpf:\"foo\"`\n\t\t})},\n\t}\n\n\tfor _, testcase := range invalid {\n\t\tt.Run(testcase.name, func(t *testing.T) {\n\t\t\tif err := assignValues(testcase.to, zero); err == nil {\n\t\t\t\tt.Fatal(\"assignValues didn't return an error\")\n\t\t\t} else {\n\t\t\t\tt.Log(err)\n\t\t\t}\n\t\t})\n\t}\n\n\tvalid := []struct {\n\t\tname string\n\t\tto   interface{}\n\t}{\n\t\t{\"pointer to struct\", new(t1)},\n\t\t{\"embedded struct\", new(t2)},\n\t\t{\"embedded struct pointer\", &t2ptr{t1: new(t1)}},\n\t\t{\"untagged field\", new(struct{ Foo int })},\n\t}\n\n\tfor _, testcase := range valid {\n\t\tt.Run(testcase.name, func(t *testing.T) {\n\t\t\tif err := assignValues(testcase.to, zero); err != nil {\n\t\t\t\tt.Fatal(\"assignValues returned\", err)\n\t\t\t}\n\t\t})\n\t}\n\n}\n\nfunc TestCollectionAssign(t *testing.T) {\n\tvar objs struct {\n\t\tProgram *Program `ebpf:\"prog1\"`\n\t\tMap     *Map     `ebpf:\"map1\"`\n\t}\n\n\tcs := &CollectionSpec{\n\t\tMaps: map[string]*MapSpec{\n\t\t\t\"map1\": {\n\t\t\t\tType:       Array,\n\t\t\t\tKeySize:    4,\n\t\t\t\tValueSize:  4,\n\t\t\t\tMaxEntries: 1,\n\t\t\t},\n\t\t},\n\t\tPrograms: map[string]*ProgramSpec{\n\t\t\t\"prog1\": {\n\t\t\t\tType: SocketFilter,\n\t\t\t\tInstructions: asm.Instructions{\n\t\t\t\t\tasm.LoadImm(asm.R0, 0, asm.DWord),\n\t\t\t\t\tasm.Return(),\n\t\t\t\t},\n\t\t\t\tLicense: \"MIT\",\n\t\t\t},\n\t\t},\n\t}\n\n\tcoll := mustNewCollection(t, cs, nil)\n\n\tqt.Assert(t, qt.IsNil(coll.Assign(&objs)))\n\tdefer objs.Program.Close()\n\tdefer objs.Map.Close()\n\n\t// Check that objs has received ownership of map and prog\n\tqt.Assert(t, qt.IsTrue(objs.Program.FD() >= 0))\n\tqt.Assert(t, qt.IsTrue(objs.Map.FD() >= 0))\n\n\t// Check that the collection has lost ownership\n\tqt.Assert(t, qt.IsNil(coll.Programs[\"prog1\"]))\n\tqt.Assert(t, qt.IsNil(coll.Maps[\"map1\"]))\n}\n\nfunc TestCollectionAssignFail(t *testing.T) {\n\t// `map2` does not exist\n\tvar objs struct {\n\t\tProgram *Program `ebpf:\"prog1\"`\n\t\tMap     *Map     `ebpf:\"map2\"`\n\t}\n\n\tcs := &CollectionSpec{\n\t\tMaps: map[string]*MapSpec{\n\t\t\t\"map1\": {\n\t\t\t\tType:       Array,\n\t\t\t\tKeySize:    4,\n\t\t\t\tValueSize:  4,\n\t\t\t\tMaxEntries: 1,\n\t\t\t},\n\t\t},\n\t\tPrograms: map[string]*ProgramSpec{\n\t\t\t\"prog1\": {\n\t\t\t\tType: SocketFilter,\n\t\t\t\tInstructions: asm.Instructions{\n\t\t\t\t\tasm.LoadImm(asm.R0, 0, asm.DWord),\n\t\t\t\t\tasm.Return(),\n\t\t\t\t},\n\t\t\t\tLicense: \"MIT\",\n\t\t\t},\n\t\t},\n\t}\n\n\tcoll := mustNewCollection(t, cs, nil)\n\n\tqt.Assert(t, qt.IsNotNil(coll.Assign(&objs)))\n\n\t// Check that the collection has retained ownership\n\tqt.Assert(t, qt.IsNotNil(coll.Programs[\"prog1\"]))\n\tqt.Assert(t, qt.IsNotNil(coll.Maps[\"map1\"]))\n}\n\nfunc TestIncompleteLoadAndAssign(t *testing.T) {\n\tspec := &CollectionSpec{\n\t\tPrograms: map[string]*ProgramSpec{\n\t\t\t\"valid\": {\n\t\t\t\tType: SocketFilter,\n\t\t\t\tInstructions: asm.Instructions{\n\t\t\t\t\tasm.LoadImm(asm.R0, 0, asm.DWord),\n\t\t\t\t\tasm.Return(),\n\t\t\t\t},\n\t\t\t\tLicense: \"MIT\",\n\t\t\t},\n\t\t\t\"invalid\": {\n\t\t\t\tType: SocketFilter,\n\t\t\t\tInstructions: asm.Instructions{\n\t\t\t\t\tasm.Return(),\n\t\t\t\t},\n\t\t\t\tLicense: \"MIT\",\n\t\t\t},\n\t\t},\n\t}\n\n\ts := struct {\n\t\t// Assignment to Valid should execute and succeed.\n\t\tValid *Program `ebpf:\"valid\"`\n\t\t// Assignment to Invalid should fail and cause Valid's fd to be closed.\n\t\tInvalid *Program `ebpf:\"invalid\"`\n\t}{}\n\n\tif err := loadAndAssign(t, spec, &s, nil); err == nil {\n\t\tt.Fatal(\"expected error loading invalid ProgramSpec\")\n\t}\n\n\tif s.Valid == nil {\n\t\tt.Fatal(\"expected valid prog to be non-nil\")\n\t}\n\n\tif fd := s.Valid.FD(); fd != -1 {\n\t\tt.Fatal(\"expected valid prog to have closed fd -1, got:\", fd)\n\t}\n\n\tif s.Invalid != nil {\n\t\tt.Fatal(\"expected invalid prog to be nil due to never being assigned\")\n\t}\n}\n\nfunc BenchmarkNewCollection(b *testing.B) {\n\tfile := testutils.NativeFile(b, \"testdata/loader-%s.elf\")\n\tspec, err := LoadCollectionSpec(file)\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\tfor _, m := range spec.Maps {\n\t\tm.Pinning = PinNone\n\t}\n\n\tspec = fixupCollectionSpec(spec)\n\n\tb.ReportAllocs()\n\n\tfor b.Loop() {\n\t\tcoll, err := NewCollection(spec)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t\tcoll.Close()\n\t}\n}\n\nfunc BenchmarkNewCollectionManyProgs(b *testing.B) {\n\tfile := testutils.NativeFile(b, \"testdata/manyprogs-%s.elf\")\n\tspec, err := LoadCollectionSpec(file)\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\tspec = fixupCollectionSpec(spec)\n\n\tb.ReportAllocs()\n\n\tfor b.Loop() {\n\t\tcoll, err := NewCollection(spec)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t\tcoll.Close()\n\t}\n}\n\nfunc BenchmarkLoadCollectionManyProgs(b *testing.B) {\n\tfile, err := os.Open(testutils.NativeFile(b, \"testdata/manyprogs-%s.elf\"))\n\tqt.Assert(b, qt.IsNil(err))\n\tdefer file.Close()\n\n\tb.ReportAllocs()\n\n\tfor b.Loop() {\n\t\t_, err := file.Seek(0, io.SeekStart)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\n\t\t_, err = LoadCollectionSpecFromReader(file)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc ExampleCollectionSpec_Assign() {\n\tspec := &CollectionSpec{\n\t\tMaps: map[string]*MapSpec{\n\t\t\t\"map1\": {\n\t\t\t\tType:       Array,\n\t\t\t\tKeySize:    4,\n\t\t\t\tValueSize:  4,\n\t\t\t\tMaxEntries: 1,\n\t\t\t},\n\t\t},\n\t\tPrograms: map[string]*ProgramSpec{\n\t\t\t\"prog1\": {\n\t\t\t\tType: SocketFilter,\n\t\t\t\tInstructions: asm.Instructions{\n\t\t\t\t\tasm.LoadImm(asm.R0, 0, asm.DWord),\n\t\t\t\t\tasm.Return(),\n\t\t\t\t},\n\t\t\t\tLicense: \"MIT\",\n\t\t\t},\n\t\t},\n\t}\n\n\ttype maps struct {\n\t\tMap *MapSpec `ebpf:\"map1\"`\n\t}\n\n\tvar specs struct {\n\t\tmaps\n\t\tProgram *ProgramSpec `ebpf:\"prog1\"`\n\t}\n\n\tif err := spec.Assign(&specs); err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(specs.Program.Type)\n\tfmt.Println(specs.Map.Type)\n\n\t// Output: SocketFilter\n\t// Array\n}\n\nfunc ExampleCollectionSpec_LoadAndAssign() {\n\tspec := &CollectionSpec{\n\t\tMaps: map[string]*MapSpec{\n\t\t\t\"map1\": {\n\t\t\t\tType:       Array,\n\t\t\t\tKeySize:    4,\n\t\t\t\tValueSize:  4,\n\t\t\t\tMaxEntries: 1,\n\t\t\t},\n\t\t},\n\t\tPrograms: map[string]*ProgramSpec{\n\t\t\t\"prog1\": {\n\t\t\t\tType: SocketFilter,\n\t\t\t\tInstructions: asm.Instructions{\n\t\t\t\t\tasm.LoadImm(asm.R0, 0, asm.DWord),\n\t\t\t\t\tasm.Return(),\n\t\t\t\t},\n\t\t\t\tLicense: \"MIT\",\n\t\t\t},\n\t\t},\n\t}\n\n\tvar objs struct {\n\t\tProgram *Program `ebpf:\"prog1\"`\n\t\tMap     *Map     `ebpf:\"map1\"`\n\t}\n\n\tif err := spec.LoadAndAssign(&objs, nil); err != nil {\n\t\tpanic(err)\n\t}\n\tdefer objs.Program.Close()\n\tdefer objs.Map.Close()\n}\n\nfunc TestStructOpsMapSpecSimpleLoadAndAssign(t *testing.T) {\n\trequireTestmodOps(t)\n\n\tmakeProg := func(attachTo string) map[string]*ProgramSpec {\n\t\treturn map[string]*ProgramSpec{\n\t\t\t\"test_1\": {\n\t\t\t\tName:     \"test_1\",\n\t\t\t\tType:     StructOps,\n\t\t\t\tAttachTo: attachTo,\n\t\t\t\tLicense:  \"GPL\",\n\t\t\t\tInstructions: asm.Instructions{\n\t\t\t\t\tasm.Mov.Imm(asm.R0, 0),\n\t\t\t\t\tasm.Return(),\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\t}\n\n\tfuncPtr := &btf.Pointer{\n\t\tTarget: &btf.FuncProto{\n\t\t\tReturn: &btf.Int{Name: \"int\", Size: 4, Encoding: btf.Signed},\n\t\t},\n\t}\n\n\ttype testCase struct {\n\t\tname       string\n\t\twithProg   bool\n\t\tattachTo   string\n\t\tvalueType  *btf.Struct\n\t\tvalueBytes []byte\n\t}\n\n\tcases := []testCase{\n\t\t{\n\t\t\tname:     \"ops_with_data\",\n\t\t\twithProg: true,\n\t\t\tattachTo: \"bpf_testmod_ops:test_1\",\n\t\t\tvalueType: &btf.Struct{\n\t\t\t\tName: \"bpf_testmod_ops\",\n\t\t\t\tSize: 16,\n\t\t\t\tMembers: []btf.Member{\n\t\t\t\t\t{\n\t\t\t\t\t\tName:   \"test_1\",\n\t\t\t\t\t\tType:   funcPtr,\n\t\t\t\t\t\tOffset: 0,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tName:   \"data\",\n\t\t\t\t\t\tType:   &btf.Int{Name: \"int\", Size: 4},\n\t\t\t\t\t\tOffset: 64, // bits\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tvalueBytes: []byte{\n\t\t\t\t// test_1 func ptr (8B)\n\t\t\t\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t\t\t\t// data (4B) + padding (4B)\n\t\t\t\t0xde, 0xed, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:     \"ops_only_func\",\n\t\t\twithProg: true,\n\t\t\tattachTo: \"bpf_testmod_ops2:test_1\",\n\t\t\tvalueType: &btf.Struct{\n\t\t\t\tName: \"bpf_testmod_ops2\",\n\t\t\t\tSize: 8,\n\t\t\t\tMembers: []btf.Member{\n\t\t\t\t\t{\n\t\t\t\t\t\tName:   \"test_1\",\n\t\t\t\t\t\tType:   funcPtr,\n\t\t\t\t\t\tOffset: 0,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tvalueBytes: []byte{\n\t\t\t\t// test_1 func ptr (8B)\n\t\t\t\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:     \"ops_empty_value\",\n\t\t\twithProg: false,\n\t\t\tvalueType: &btf.Struct{\n\t\t\t\tName:    \"bpf_testmod_ops2\",\n\t\t\t\tSize:    0,\n\t\t\t\tMembers: []btf.Member{},\n\t\t\t},\n\t\t\tvalueBytes: []byte{},\n\t\t},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(c.name, func(t *testing.T) {\n\t\t\tspec := &CollectionSpec{\n\t\t\t\tPrograms: map[string]*ProgramSpec{},\n\t\t\t\tMaps: map[string]*MapSpec{\n\t\t\t\t\t\"testmod_ops\": {\n\t\t\t\t\t\tName:       \"testmod_ops\",\n\t\t\t\t\t\tType:       StructOpsMap,\n\t\t\t\t\t\tFlags:      sys.BPF_F_LINK,\n\t\t\t\t\t\tKey:        &btf.Int{Size: 4},\n\t\t\t\t\t\tKeySize:    4,\n\t\t\t\t\t\tValue:      c.valueType,\n\t\t\t\t\t\tMaxEntries: 1,\n\t\t\t\t\t\tContents: []MapKV{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tKey:   uint32(0),\n\t\t\t\t\t\t\t\tValue: slices.Clone(c.valueBytes),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\t\t\tif c.withProg {\n\t\t\t\tspec.Programs = makeProg(c.attachTo)\n\t\t\t}\n\n\t\t\tcoll := mustNewCollection(t, spec, nil)\n\n\t\t\tfor name := range spec.Maps {\n\t\t\t\tqt.Assert(t, qt.IsNotNil(coll.Maps[name]))\n\t\t\t}\n\t\t\tfor name := range spec.Programs {\n\t\t\t\tqt.Assert(t, qt.IsNotNil(coll.Programs[name]))\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestLinkedELF(t *testing.T) {\n\tspec, err := LoadCollectionSpec(\"testdata/linked-el.elf\")\n\tqt.Assert(t, qt.IsNil(err))\n\n\t// Require all maps that won during linking to have a MaxEntries of 1.\n\tfor name, m := range spec.Maps {\n\t\tqt.Assert(t, qt.Equals(m.MaxEntries, 1), qt.Commentf(name))\n\t}\n\n\t// Require all programs that won during linking to return 0 when executed.\n\t// Programs that should be overridden during linking should return their line\n\t// numbers.\n\tcoll := mustNewCollection(t, spec, nil)\n\tfor name, prog := range coll.Programs {\n\t\tres := mustRun(t, prog, nil)\n\t\tqt.Assert(t, qt.Equals(res, 0), qt.Commentf(name))\n\t}\n}\n"
  },
  {
    "path": "collection_windows.go",
    "content": "package ebpf\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"unsafe\"\n\n\t\"github.com/cilium/ebpf/internal/efw\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\nfunc loadCollectionFromNativeImage(file string) (_ *Collection, err error) {\n\tmapFds := make([]efw.FD, 32)\n\tprogramFds := make([]efw.FD, 32)\n\tvar maps map[string]*Map\n\tvar programs map[string]*Program\n\n\tdefer func() {\n\t\tif err == nil {\n\t\t\treturn\n\t\t}\n\n\t\tfor _, fd := range append(mapFds, programFds...) {\n\t\t\t// efW never uses fd 0.\n\t\t\tif fd != 0 {\n\t\t\t\t_ = efw.EbpfCloseFd(int(fd))\n\t\t\t}\n\t\t}\n\n\t\tfor _, m := range maps {\n\t\t\t_ = m.Close()\n\t\t}\n\n\t\tfor _, p := range programs {\n\t\t\t_ = p.Close()\n\t\t}\n\t}()\n\n\tnMaps, nPrograms, err := efw.EbpfObjectLoadNativeFds(file, mapFds, programFds)\n\tif errors.Is(err, efw.EBPF_NO_MEMORY) && (nMaps > len(mapFds) || nPrograms > len(programFds)) {\n\t\tmapFds = make([]efw.FD, nMaps)\n\t\tprogramFds = make([]efw.FD, nPrograms)\n\n\t\tnMaps, nPrograms, err = efw.EbpfObjectLoadNativeFds(file, mapFds, programFds)\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tmapFds = mapFds[:nMaps]\n\tprogramFds = programFds[:nPrograms]\n\n\t// The maximum length of a name is only 16 bytes on Linux, longer names\n\t// are truncated. This is not a problem when loading from an ELF, since\n\t// we get the full object name from the symbol table.\n\t// When loading a native image we do not have this luxury. Use an efW native\n\t// API to retrieve up to 64 bytes of the object name.\n\n\tmaps = make(map[string]*Map, len(mapFds))\n\tfor _, raw := range mapFds {\n\t\tfd, err := sys.NewFD(int(raw))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tm, mapErr := newMapFromFD(fd)\n\t\tif mapErr != nil {\n\t\t\t_ = fd.Close()\n\t\t\treturn nil, mapErr\n\t\t}\n\n\t\tvar efwMapInfo efw.BpfMapInfo\n\t\tsize := uint32(unsafe.Sizeof(efwMapInfo))\n\t\t_, err = efw.EbpfObjectGetInfoByFd(m.FD(), unsafe.Pointer(&efwMapInfo), &size)\n\t\tif err != nil {\n\t\t\t_ = m.Close()\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif size >= uint32(unsafe.Offsetof(efwMapInfo.Name)+unsafe.Sizeof(efwMapInfo.Name)) {\n\t\t\tm.name = unix.ByteSliceToString(efwMapInfo.Name[:])\n\t\t}\n\n\t\tif m.name == \"\" {\n\t\t\t_ = m.Close()\n\t\t\treturn nil, fmt.Errorf(\"unnamed map\")\n\t\t}\n\n\t\tif _, ok := maps[m.name]; ok {\n\t\t\treturn nil, fmt.Errorf(\"duplicate map with the same name: %s\", m.name)\n\t\t}\n\n\t\tmaps[m.name] = m\n\t}\n\n\tprograms = make(map[string]*Program, len(programFds))\n\tfor _, raw := range programFds {\n\t\tfd, err := sys.NewFD(int(raw))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tprogram, err := newProgramFromFD(fd)\n\t\tif err != nil {\n\t\t\t_ = fd.Close()\n\t\t\treturn nil, err\n\t\t}\n\n\t\tvar efwProgInfo efw.BpfProgInfo\n\t\tsize := uint32(unsafe.Sizeof(efwProgInfo))\n\t\t_, err = efw.EbpfObjectGetInfoByFd(program.FD(), unsafe.Pointer(&efwProgInfo), &size)\n\t\tif err != nil {\n\t\t\t_ = program.Close()\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif size >= uint32(unsafe.Offsetof(efwProgInfo.Name)+unsafe.Sizeof(efwProgInfo.Name)) {\n\t\t\tprogram.name = unix.ByteSliceToString(efwProgInfo.Name[:])\n\t\t}\n\n\t\tif program.name == \"\" {\n\t\t\t_ = program.Close()\n\t\t\treturn nil, fmt.Errorf(\"unnamed program\")\n\t\t}\n\n\t\tif _, ok := programs[program.name]; ok {\n\t\t\t_ = program.Close()\n\t\t\treturn nil, fmt.Errorf(\"duplicate program with the same name: %s\", program.name)\n\t\t}\n\n\t\tprograms[program.name] = program\n\t}\n\n\treturn &Collection{programs, maps, nil}, nil\n}\n"
  },
  {
    "path": "collection_windows_test.go",
    "content": "package ebpf\n\nimport (\n\t\"path/filepath\"\n\t\"sort\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestLoadNativeImage(t *testing.T) {\n\tfor _, tc := range []struct {\n\t\tfile     string\n\t\tmaps     []string\n\t\tprograms []string\n\t}{\n\t\t{\n\t\t\t\"testdata/windows/cgroup_sock_addr.sys\",\n\t\t\t[]string{\n\t\t\t\t\"egress_connection_policy_map\",\n\t\t\t\t\"ingress_connection_policy_map\",\n\t\t\t\t\"socket_cookie_map\",\n\t\t\t},\n\t\t\t[]string{\n\t\t\t\t\"authorize_connect4\",\n\t\t\t\t\"authorize_connect6\",\n\t\t\t\t\"authorize_recv_accept4\",\n\t\t\t\t\"authorize_recv_accept6\",\n\t\t\t},\n\t\t},\n\t} {\n\t\tt.Run(filepath.Base(tc.file), func(t *testing.T) {\n\t\t\tcoll, err := LoadCollection(tc.file)\n\t\t\tqt.Assert(t, qt.IsNil(err))\n\t\t\tdefer coll.Close()\n\n\t\t\tvar mapNames []string\n\t\t\tfor name, obj := range coll.Maps {\n\t\t\t\tqt.Assert(t, qt.Equals(obj.name, name))\n\t\t\t\tmapNames = append(mapNames, name)\n\t\t\t}\n\t\t\tsort.Strings(mapNames)\n\t\t\tqt.Assert(t, qt.DeepEquals(mapNames, tc.maps))\n\n\t\t\tvar programNames []string\n\t\t\tfor name, obj := range coll.Programs {\n\t\t\t\tqt.Assert(t, qt.Equals(obj.name, name))\n\t\t\t\tprogramNames = append(programNames, name)\n\t\t\t}\n\t\t\tsort.Strings(programNames)\n\t\t\tqt.Assert(t, qt.DeepEquals(programNames, tc.programs))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "cpu.go",
    "content": "package ebpf\n\n// PossibleCPU returns the max number of CPUs a system may possibly have\n// Logical CPU numbers must be of the form 0-n\nfunc PossibleCPU() (int, error) {\n\treturn possibleCPU()\n}\n\n// MustPossibleCPU is a helper that wraps a call to PossibleCPU and panics if\n// the error is non-nil.\nfunc MustPossibleCPU() int {\n\tcpus, err := PossibleCPU()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn cpus\n}\n"
  },
  {
    "path": "cpu_other.go",
    "content": "//go:build !windows\n\npackage ebpf\n\nimport (\n\t\"sync\"\n\n\t\"github.com/cilium/ebpf/internal/linux\"\n)\n\nvar possibleCPU = sync.OnceValues(func() (int, error) {\n\treturn linux.ParseCPUsFromFile(\"/sys/devices/system/cpu/possible\")\n})\n"
  },
  {
    "path": "cpu_test.go",
    "content": "package ebpf\n\nimport (\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestPossibleCPU(t *testing.T) {\n\tnum, err := PossibleCPU()\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.IsTrue(num > 0))\n}\n"
  },
  {
    "path": "cpu_windows.go",
    "content": "package ebpf\n\nimport (\n\t\"sync\"\n\n\t\"golang.org/x/sys/windows\"\n)\n\nvar possibleCPU = sync.OnceValues(func() (int, error) {\n\treturn int(windows.GetMaximumProcessorCount(windows.ALL_PROCESSOR_GROUPS)), nil\n})\n"
  },
  {
    "path": "doc.go",
    "content": "// Package ebpf is a toolkit for working with eBPF programs.\n//\n// eBPF programs are small snippets of code which are executed directly\n// in a VM in the Linux kernel, which makes them very fast and flexible.\n// Many Linux subsystems now accept eBPF programs. This makes it possible\n// to implement highly application specific logic inside the kernel,\n// without having to modify the actual kernel itself.\n//\n// This package is designed for long-running processes which\n// want to use eBPF to implement part of their application logic. It has no\n// run-time dependencies outside of the library and the Linux kernel itself.\n// eBPF code should be compiled ahead of time using clang, and shipped with\n// your application as any other resource.\n//\n// Use the link subpackage to attach a loaded program to a hook in the kernel.\n//\n// Note that losing all references to Map and Program resources will cause\n// their underlying file descriptors to be closed, potentially removing those\n// objects from the kernel. Always retain a reference by e.g. deferring a\n// Close() of a Collection or LoadAndAssign object until application exit.\n//\n// Special care needs to be taken when handling maps of type ProgramArray,\n// as the kernel erases its contents when the last userspace or bpffs\n// reference disappears, regardless of the map being in active use.\npackage ebpf\n"
  },
  {
    "path": "docs/.gitignore",
    "content": "# Python\n__pycache__\n\n# Build output\nbuild/\nsite/\n"
  },
  {
    "path": "docs/Makefile",
    "content": "build: pipenv\n\t@# Run a production build of the documentation. Strict mode makes warnings fatal.\n\tpipenv run mkdocs build --strict\n\n\t@# Build main packages, discarding build output.\n\tgo build -v ./...\n\n\t@# Build _test.go files containing Doc* functions, don't execute tests.\n\tgo test -c -o /dev/null ./... >/dev/null\n\npreview: pipenv\n\tpipenv run mkdocs serve\n\nshell: pipenv\n\t@echo \"pipenv shell\"\n\t@exec pipenv shell\n\npipenv:\nifeq (, $(shell command -v pipenv 2> /dev/null))\n$(error \"pipenv is not installed, exiting..\")\nendif\n\n\t@# Ensure a venv and install dependencies from Pipfile.lock. Buffer stdio\n\t@# and display it on error as pipenv uses stdin and stderr arbitrarily.\n\t@echo \"pipenv sync\"\n\t@out=`pipenv sync 2>&1` || echo \"$${out}\"\n\n.PHONY: pipenv\n"
  },
  {
    "path": "docs/Pipfile",
    "content": "[[source]]\nurl = \"https://pypi.org/simple\"\nverify_ssl = true\nname = \"pypi\"\n\n[packages]\nmkdocs = \"*\"\npymdown-extensions = \"*\"\nmkdocs-material = \"*\"\nmkdocs-macros-plugin = \"*\"\nmkdocs-git-revision-date-localized-plugin = \"*\"\nmkdocs-git-authors-plugin = \"*\"\n\n[dev-packages]\n\n[requires]\n# Whatever Netlify's Ubuntu version uses.\npython_version = \"3.13\"\n"
  },
  {
    "path": "docs/README.md",
    "content": "# epbf-go documentation\n\nThe documentation project uses Pipenv to manage its dependencies, which will\nautomatically create a Python virtualenv when invoked from this subdirectory.\nFollow your distribution's documentation for installing `pipenv`. You may also\nneed `pyenv` to install a different Python version if your distribution doesn't\nprovide the version specified in the `Pipfile`.\n\nHost a live preview of the documentation at http://127.0.0.1:8000:\n\n`make preview`\n\nBuild the documentation, output to the site/ directory. This is a self-contained\nproduction copy that can be uploaded to hosting.\n\n`make build`\n\nTo enter the virtualenv with all the documentation's Python dependencies\ninstalled:\n\n`make shell`\n"
  },
  {
    "path": "docs/ebpf/about.md",
    "content": "The project was initially created in 2017 as\n[`newtools/ebpf`](https://github.com/newtools/ebpf) by a group of passionate\ndevelopers wanting to bring the power eBPF to Go applications. It quickly gained\ntraction within the Go community, especially for projects that couldn't or\nwouldn't build upon the CGo-based BCC bindings at the time (`gobpf`).\n\nSince its inception, {{ proj }} has seen remarkable growth and widespread\nadoption. It has become a fundamental building block for numerous open-source\nprojects. Major industry players and forward-thinking startups have integrated\nthe library into their technology stacks to combine the power and flexibility of\neBPF with the iteration speed, runtime safety and ease of deployment provided by\nthe Go language.\n\n{{ proj }} maintains a strong commitment to collaborating with the upstream\nLinux project, which ensures that it stays aligned with the latest advancements\nin the eBPF ecosystem and remains compatible with the evolving Linux kernel and\nits co-located BPF library, `libbpf`.\n\nThank you for being a part of our :ebee-color: eBPF journey!\n"
  },
  {
    "path": "docs/ebpf/concepts/features.md",
    "content": "# Feature Detection\n\nFeature detection allows applications to check which eBPF-related features are\nsupported by the Linux kernel. This is useful for software that wants to be\ncompatible with multiple kernel versions and lets developers tailor their code\nto use different eBPF features depending on what is supported by the running\nkernel.\n\n## Usage\n\nIn the `features` package, API calls follow a consistent pattern. The returned\nerrors mean the following:\n\n- `nil` means the feature is supported.\n- {{ godoc('ErrNotSupported') }} means the feature is not supported.\n- Any other error suggests inconclusive detection, which could include false\n  negatives.\n\nFor example, here's using {{ godoc('features/HaveProgramType') }}:\n\n{{ go_example('DocDetectXDP', title=\"Detect kernel support for XDP programs\") }}\n\n!!! note \"\"\n    Feature detection results are cached to minimize overhead, except for\n    inconclusive results. Subsequent calls to a conclusive probe will\n    consistently return the same result without rerunning the probe logic.\n\n## Limitations\n\n### {{ godoc ('features/HaveProgramHelper') }}\n\n1. Not all combinations of program types and helpers can be probed. Conclusively\n   probing a BPF helper means successfully loading a generated BPF program.\n   Certain program types like `LSM`, `StructOps` and `Tracing` are difficult to\n   generate on-the-fly, as they depend on other components or symbols being\n   present in the kernel, making the probes fragile. Instead, for these types,\n   we don't rely on successfully loading a program, but we look for specific\n   kernel error responses instead, such as `ENOTSUPP`. This indicates the\n   program type is known, but our generated program  was invalid (which is\n   fine!).\n\n2. This function only confirms the presence of the given BPF helper in the\n   kernel. In cases where helpers themselves gain extra features in subsequent\n   kernel releases, you'll have to write your own feature probe to test the\n   particular combination of helper inputs you're looking for. Feel free to look\n   at the implementation of package `features` for inspiration.\n\n## Compared to `bpftool`\n\nLinux's command-line utility `bpftool` offers the `bpftool feature probe`\nsubcommand for feature detection, inspiring the `features` package in {{ proj }}.\nThat subcommand provides an extensive overview of eBPF-related features,\nissuing thousands of feature probes to identify kernel configuration options,\nand detect map types, program types, and helper functions. {{ proj }} aims to\nprovide an equivalent set of feature probes, implemented in pure Go, to avoid a\n`bpftool` runtime dependency, and to allow users to probe only the exact\nfeatures they need.\n"
  },
  {
    "path": "docs/ebpf/concepts/global-variables.md",
    "content": "{{ linux_version(\"5.2\", \"For all global variable-related BPF operations,\nthe kernel needs to understand the BPF_PSEUDO_MAP_VALUE value in ldimm64\ninstructions. This is needed for direct, lookup-free map access.\" )}}\n\nLike typical C programs, BPF programs allow the use of global variables. These\nvariables can be initialized from the BPF C code itself, or they can be modified\nby the loading user space application before handing it off to the kernel.\n\nThe abstraction {{ proj }} provides to interact with global variables is the\n{{ godoc('VariableSpec') }}, found in the {{ godoc('CollectionSpec.Variables') }}\nfield. This page describes how to declare variables in BPF C and how to interact\nwith them in Go.\n\n## Runtime Constants\n\n{{ linux_version(\"5.2\", \"Read-only maps and the BPF_MAP_FREEZE command are needed\nfor implementing constant variables.\") }}\n\nGlobal runtime constants are typically used for configuration values that\ninfluence the functionality of a BPF program. Think all sorts of network or\nhardware addresses for network filtering, or timeouts for rate limiting. The C\ncompiler will reject any runtime modifications to these variables in the BPF\nprogram, like a typical const.\n\nCrucially, the BPF verifier will also perform dead code analysis if constants\nare used in if statements. If a condition is always true or false, it will\nremove unused code paths from the BPF program, reducing verification time and\nincreasing runtime performance.\n\nThis enables many features like portable kfuncs, allowing C code to refer to\nkfuncs that may not exist in some kernels, as long as those code paths are\nguaranteed not to execute at runtime. Similarly, this can be used to your\nadvantage to disable code paths that are not needed in certain configurations,\nor would result in a verifier error on some kernels or in some contexts.\n\n:ebee-color: Consider the following C BPF program that reads a global constant\nand returns it:\n\n{{ c_example('variables_const', title='BPF C program declaring global constant const_u32') }}\n\n??? warning \"Why is `const_u32` declared `volatile`?\"\n\n    In short: without the `volatile` qualifier, the variable would be optimized\n    away and not appear in the BPF object file, leaving us unable to modify it\n    from our user space application.\n\n    In this program, the compiler (in)correctly deduces two things about `const_u32`:\n    it is never assigned a value, and it doesn't change over the course of the program.\n    Implementation details aside, it will now assume that the return value of\n    `const_example()` is always 0 and omit the variable from the ELF altogether.\n\n    For BPF programs, it's common practice to declare all global variables that\n    need to be accessed from user space as `volatile`, especially non-`const`\n    globals. Doing so ensures the compiler reliably allocates them in a data\n    section in the ELF.\n\n:simple-go: First, let's take a look at a full Go example that will comprise the\nmajority of interactions with constants. In the example below, we'll load a BPF\nobject from disk, pull out a variable, set its value and call the BPF program\nonce with an empty context. Variations on this pattern will follow later.\n\n{{ go_example('DocVariablesSetConst', title='Go program modifying a const, loading and running the BPF program') }}\n\n1. Any values passed into {{ godoc('VariableSpec.Set') }} must marshal to a\n   fixed width. This behaviour is identical to {{ godoc('Map.Put') }} and\n   friends. Using untyped integers is not supported since their size is platform\n   dependent. We recommend the same approach in BPF C to keep data size\n   predictable.\n2. A 15-byte context is the minimum the kernel will accept for dry-running a BPF\n   program. If your BPF program reads from its context, populating this slice is\n   a great way of doing unit testing without setting up a live testing environment.\n\n## Global Variables\n\nNon-const global variables are mutable and can be modified by both the BPF\nprogram and the user space application. They are typically used for keeping\nstate like metrics, counters, rate limiting, etc.\n\nThese variables can also be initialized from user space, much like their `const`\ncounterparts, and can be both read and written to from the BPF program as well\nas the user space application. More on that in a future section.\n\n:ebee-color: The following C BPF program reads a global variable and returns it:\n\n{{ c_example('variables_global', title='BPF C program declaring global variable global_u16') }}\n\n??? warning \"Why is `global_u16` declared `volatile`?\"\n\n    Similar to `volatile const` in a prior example, `volatile` is used here to\n    make compiler output more deterministic. Without it, the compiler may\n    choose to optimize away a variable if it's never assigned to, not knowing\n    its value is actually provided by user space. The `volatile` qualifier\n    doesn't change the variable's semantics.\n\n### Before Loading: Using VariableSpec\n\nFor interacting with global variables before loading the BPF program into the\nkernel, use the methods on its {{ godoc('VariableSpec') }} found in {{\ngodoc('CollectionSpec.Variables') }} or injected using {{ godoc('LoadAndAssign')\n}}. This ensures the variable is populated before the BPF program has a chance\nto execute.\n\n:simple-go: In user space, initialize `global_u16` to 9000:\n\n{{ go_example('DocVariablesSetGlobalU16') }}\n\nDry-running `global_example()` a few times results in the value increasing on\nevery invocation:\n\n{{ go_example('DocVariablesSetGlobalRun') }}\n\nOnce a CollectionSpec has been loaded into the kernel, further modifications\nto a VariableSpec are ineffectual.\n\n### After Loading: Using Variable\n\nAfter loading the BPF program into the kernel, accessing global variables from\nuser space can be done through the {{ godoc('Variable') }} abstraction. These\ncan be injected into an object using {{ godoc('LoadAndAssign') }}, or found in\nthe {{ godoc('Collection.Variables') }} field.\n\n:simple-go: Building on the previous example, read the incremented variable\nusing {{ godoc('Variable.Get') }}:\n\n{{ go_example('DocVariablesGetGlobalU16') }}\n\nModify the Variable at runtime using {{ godoc('Variable.Set') }}.\n\n## Internal/Hidden Global Variables\n\nBy default, all global variables described in an ELF's data sections are exposed\nthrough {{ godoc('CollectionSpec.Variables') }}. However, there may be cases\nwhere you don't want user space to interfere with a variable (either on purpose\nor by accident) and you want to keep the variable internal to the BPF program.\n\n{{ c_example('variables_hidden', title='BPF C program declaring internal global variable internal_var') }}\n\nThe `__hidden` macro is found in Linux' `<bpf/bpf_helpers.h>` as of version 5.13\nand is defined as follows:\n\n```c\n#define __hidden __attribute__((visibility(\"hidden\")))\n```\n\nThis will cause the VariableSpec for `hidden_var` to not be included in\nthe CollectionSpec. \n\n## Static Global Variables\n\nWith the introduction of `bpftool gen object`. BPF received a full-blown static\nlinker, giving the `static` keyword for declaring objects local to a single .c\nfile an actual semantic meaning.\n\n{{ proj }} follows the convention set by libbpf to not expose static variables\nto user space. In our case, this means that static variables are not included in\nthe {{ godoc('CollectionSpec.Variables') }} field or emitted in bpf2go-generated\ncode.\n\nThe ELF loader has no way to differentiate function-scoped local variables (also\nnot exposed) and static variables, since they're both marked with `LOCAL`\nlinkage in the ELF. If you need to expose a variable to user space, drop the\n`static` keyword and declare it in the global scope of your BPF C program.\n"
  },
  {
    "path": "docs/ebpf/concepts/loader.md",
    "content": "# Loading Objects\n\n{{ proj }} ships an eBPF object (ELF) loader that aims to be compatible with the\nupstream libbpf and iproute2 (`tc`/`ip`) projects. An ELF is typically obtained\nby compiling a eBPF C program using the LLVM toolchain (`clang`).\n\nThis page describes the journey from compiled eBPF ELF to resources in the\nkernel. This involves parsing the ELF into intermediate Go (Spec) types that\ncan be modified and copied before loading them into the kernel.\n\n```mermaid\ngraph LR\n    ELF --> ProgramSpec --> Program\n    ELF --> Types\n    ELF --> MapSpec --> Map\n    Map & Program --> Links\n    subgraph Collection\n        Program & Map\n    end\n    subgraph CollectionSpec\n        ProgramSpec & MapSpec & Types\n    end\n```\n\n## {{ godoc('CollectionSpec') }}\n\nA CollectionSpec represents eBPF objects extracted from an ELF, and can be\nobtained by calling {{ godoc('LoadCollectionSpec') }}. In the examples below, we\ndeclare a Map and Program in eBPF C, then load and inspect them using Go. Use\nthe tabs to explore the Go and C counterparts below.\n\n=== \":simple-go: Go\"\n    {{ go_example('DocLoadCollectionSpec', title='Parse ELF and inspect its CollectionSpec') | indent(4) }}\n\n    !!! warning \"\"\n        All of a Spec's attributes can be modified, and those modifications\n        influence the resources created in the kernel. Be aware that doing so\n        may invalidate any assumptions made by the compiler, resulting in maps\n        or programs being rejected by the kernel. Proceed with caution.\n\n=== \":ebee-color: eBPF C\"\n    {{ c_example('DocMyMapProgram', title='Declare a minimal map and a program') | indent(4) }}\n\n    !!! tip \"\"\n        See [Section Naming](section-naming.md) to learn about the use of the\n        `SEC()` macro in the example above.\n\n## {{ godoc('NewCollection') }}\n\nAfter parsing the ELF into a CollectionSpec, it can be loaded into the kernel\nusing {{ godoc('NewCollection') }}, resulting in a {{ godoc('Collection') }}.\n\n{{ go_example('DocNewCollection') }}\n\n!!! note \"\"\n    {{ godoc('Collection.Close') }} closes all Maps and Programs in the\n    Collection. Interacting with any resources after `Close()` will return an\n    error, since their underlying file descriptors will be closed. See [Object\n    Lifecycle](object-lifecycle.md) to gain a better understanding of how {{\n    proj }} manages its resources and for best practices handling Maps and\n    Programs.\n\n## {{ godoc('CollectionSpec.LoadAndAssign', short=True) }}\n\nLoadAndAssign is a convenience API that can be used instead of `NewCollection`.\nIt has two major benefits:\n\n- It automates pulling Maps and Programs out of a Collection. No more `#!go if m\n  := coll.Maps[\"my_map\"]; m == nil { return ... }`.\n- **Selective loading of Maps and Programs!** Only resources of interest and\n  their dependencies are loaded into the kernel. Great for working with large\n  CollectionSpecs that only need to be partially loaded.\n\nFirst, declare a struct that will receive pointers to a Map and a Program after\nloading them into the kernel. Give it a `#!go Close()` method to make cleanup\neasier.\n\n{{ go_example('DocLoadAndAssignObjs', title='Declare a custom struct myObjs') }}\n\n!!! note \"\"\n    Use bpf2go if the preceding code snippet looks\n    tedious. bpf2go can generate this kind of boilerplate code automatically\n    and will make sure it stays in sync with your C code.\n\nNext, instantiate a variable of our newly-declared type and pass its pointer to\n`LoadAndAssign`.\n\n{{ go_example('DocLoadAndAssign', title='Pass a custom struct to LoadAndAssign') }}\n\n!!! warning \"\"\n    If your use case requires dynamically renaming keys in CollectionSpec.Maps,\n    you may need to use NewCollection instead. Map and Program names in struct\n    tags are baked into the Go binary at compile time.\n\n## Type Information (BTF)\n\nIf an eBPF ELF was built with `clang -g`, it will automatically contain BTF type\ninformation. This information can be accessed programmatically through {{\ngodoc('CollectionSpec.Types') }}. Note that this field will be `nil` if the ELF\nwas built without BTF.\n\n{{ go_example('DocBTFTypeByName') }}\n\n!!! note \"\"\n    Many eBPF features rely on ELFs to be built with BTF, and there is\n    little to be gained by opting out of it. `clang -g` also includes DWARF\n    information in the ELF which can be safely removed with `llvm-strip`. eBPF\n    does not rely on DWARF information.\n"
  },
  {
    "path": "docs/ebpf/concepts/object-lifecycle.md",
    "content": "!!! info \"\"\n    This is an advanced topic and does not need to be fully understood in order\n    to get started writing useful tools.\n\n    If you find yourself debugging unexpectedly-detached programs, resource\n    leaks, or you want to gain a deeper understanding of how eBPF objects are\n    managed by {{ proj }}, this page should prove helpful.\n\n## File Descriptors and Go\n\nInteracting with eBPF objects from user space is done using file descriptors.\nCounter-intuitively, 'file' descriptors are used as references to many types of\nkernel resources in modern Linux, not just files. In {{ proj }}, {{ godoc('Map')\n}}, {{ godoc('Program') }} and {{ godoc('link/Link') }} are all modeled around\nthese underlying file descriptors.\n\nGo, being a garbage-collected language, automatically manages the lifecycle of\nGo objects. Keeping in line with the standard library's `os.File` and friends,\neBPF resources in {{ proj }} were designed in a way so their underlying file\ndescriptors are closed when their Go objects are garbage collected. This\ngenerally prevents runaway resource leaks, but is not without its drawbacks.\n\nThis has subtle but important repercussions for BPF, since this means the Go\nruntime will call `Close()` on an object's underlying file descriptor if the\nobject is no longer reachable by the garbage collector. For example, this can\nhappen if an object is created in a function, but is not returned to the caller.\nOne type of map, {{ godoc('ProgramArray') }}, is particularly sensitive to this.\nMore about that in [Program Arrays](#program-arrays).\n\n## Extending Object Lifetime\n\n### Pinning\n\nAside from file descriptors, BPF provides another method of creating references\nto eBPF objects: pinning. This is the concept of associating a file on a virtual\nfile system (the BPF File System, bpffs for short) with a BPF resource like a\nMap, Program or Link. Pins can be organized into arbitrary directory structures,\njust like on any other file system.\n\nWhen the Go process exits, the pin will maintain a reference to the object,\npreventing it from being automatically destroyed. In this scenario, removing the\npin using plain `rm` will remove the last reference, causing the kernel to\ndestroy the object. If you're holding an active object in Go, you can also call\n{{ godoc('Map.Unpin') }}, {{ godoc('Program.Unpin') }} or {{\ngodoc('link/Link.Unpin') }} if the object was previously pinned.\n\n!!! warning\n    Pins do **not** persist through a reboot!\n\nA common use case for pinning is sharing eBPF objects between processes. For\nexample, one could create a Map from Go, pin it, and inspect it using `bpftool\nmap dump pinned /sys/fs/bpf/my_map`.\n\n### Attaching\n\nAttaching a Program to a hook acts as a reference to a Program, since the kernel\nneeds to be able to execute the program's instructions at any point.\n\nFor legacy reasons, some {{ godoc('link/Link') }} types don't support pinning.\nIt is generally safe to assume these links will persist beyond the lifetime of\nthe Go application.\n\n## :warning: Program Arrays\n\nA {{ godoc('ProgramArray') }} is a Map type that holds references to other\nPrograms. This allows programs to 'tail call' into other programs, useful for\nsplitting up long and complex programs.\n\nProgram Arrays have a unique property: they allow cyclic dependencies to be\ncreated between the Program Array and a Program (e.g. allowing programs to call\ninto themselves).To avoid ending up with a set of programs loaded into the\nkernel that cannot be freed, the kernel maintains a hard rule: **Program Arrays\nrequire at least one open file descriptor or bpffs pin**.\n\n!!! warning\n    If all user space/bpffs references are gone, **any tail calls into the array\n    will fail**, but the Map itself will remain loaded as long as there are\n    programs that use it. This property, combined with interactions with Go's\n    garbage collector previously described in [File Descriptors and\n    Go](#file-descriptors-and-go), is a great source of bugs.\n\nA few tips to handle this problem correctly:\n\n- Use {{ godoc('CollectionSpec.LoadAndAssign') }}. It will refuse to load the\n  CollectionSpec if doing so would result in a Program Array without a userspace\n  reference.\n- Pin Program Arrays if execution of your eBPF code needs to continue past the\n  lifetime of your Go application, e.g. for upgrades or short-lived CLI tools.\n- Retain references to the Map at all times in long-running applications. Note\n  that `#!go defer m.Close()` makes Go retain a reference until the end of the\n  current scope.\n"
  },
  {
    "path": "docs/ebpf/concepts/rlimit.md",
    "content": "# Resource Limits\n\nCreating eBPF objects (Maps, Programs, even BTF blobs) requires kernel memory\nallocation. Before kernel version 5.11, the memory available to a process for\ncreating eBPF objects was restricted by its `RLIMIT_MEMLOCK` rlimit value,\nvisible through the `ulimit -l` command.\n\nStarting with [version\n5.11](https://lore.kernel.org/bpf/20201201215900.3569844-1-guro@fb.com), the\nLinux kernel switched from rlimits to memory cgroup (memcg) accounting for\nmanaging memory limits on processes handling eBPF objects in the kernel. eBPF\nobject allocations are tracked alongside regular allocations within the cgroup.\nMemory consumption and limits can be queried and set through cgroupfs, the same\nmechanism used for setting memory limits on containers.\n\n## Purpose of package `rlimit`\n\nOn kernels supporting memcg accounting, there's no need to manage\n`RLIMIT_MEMLOCK` for effectively using eBPF, as eBPF object allocations now\ncount towards the cgroup memory limit instead. However, since many Linux\ndistributions still ship pre-5.11 kernels, it's necessary to conditionally\nmanage rlimit for kernels lacking memcg accounting for eBPF.\n\nTo support writing portable Go tools that work across various kernel versions,\nthe `rlimit` package was introduced. It encapsulates two behaviours:\n\n1. As an **import side effect** of importing the package, it lowers the rlimit\n   of the current process to induce a Map creation failure, then restores the\n   original rlimit.\n2. {{ godoc('rlimit/RemoveMemlock') }} conditionally increases `RLIMIT_MEMLOCK`\n   to infinity based on the probe's result. If the kernel supports memcg\n   accounting, this is a no-op.\n\n## Usage\n\nInclude this in your application:\n\n{{ go_example('DocRlimit', title=\"Remove RLIMIT_MEMLOCK if kernel lacks memcg accounting\") }}\n\n!!! note \"\"\n    You can call `RemoveMemlock()` multiple times if your program has\n    multiple entry points or CLI subcommands. The rlimit operation will only\n    execute once.\n\n## Caveats\n\n### Race Conditions\n\nThe package was carefully designed with Go's runtime initialization semantics in\nmind, meaning only one `init()` will execute at a time across all packages,\nminimizing the risk of racing against other callers to `prlimit(2)` (which\nshould hopefully be rare).\n\nThe rlimit package first gets the process' current `RLIMIT_MEMLOCK` value, drops\nit to 0, attempts to create a BPF map, then finally resets the rlimit to the old\nvalue. It's important to note that this happens **before invoking**\n`RemoveMemlock()` and has two potential side effects:\n\n- On kernels before 5.11, other concurrent BPF object creations may fail due to\n  insufficient memory being available while the rlimit is at 0.\n- Other Go packages interacting with `prlimit(2)` may interfere with this\n  process, leading to a wrong `RLIMIT_MEMLOCK` value being read or restored.\n  Please audit your code and dependencies for potential conflicts.\n\n### Why does my application always create a Map on startup?\n\n!!! note \"\"\n    The `rlimit` package is entirely optional and serves as a convenience\n    feature.\n\nSince the package creates a Map from `init()`, there is currently no way to\nprevent your application from interacting with `bpf(2)`, even if\n`RemoveMemlock()` is never invoked or if none of your application's eBPF\nfeatures remain disabled. We consider this a reasonable trade-off to provide\nmaximum value for the majority of use cases.\n\nIf this is not desirable, you can avoid using package `rlimit` altogether and\nincrease the rlimit through other means like Docker's `--ulimit memlock=-1` flag\nor systemd's `LimitMEMLOCK=infinity` unit limit property.\n\n"
  },
  {
    "path": "docs/ebpf/concepts/section-naming.md",
    "content": "You may have seen the `SEC()` macro used around eBPF C code. This macro sends\na hint to the compiler to place a symbol (a variable or function) in a specific\nsection of the resulting eBPF object binary.\n\nTypically, program binaries for Unix-like systems are divided into so-called\n'sections'. All sections have names, many of which are assigned special meaning.\nFor example, `.text` is where [program\ntext](https://en.wikipedia.org/wiki/Code_segment) (executable instructions) goes\nby default.\n\nLike common application binaries, eBPF also relies heavily on section naming to\ndistinguish various parts of an application. As an example, the section name of\nan individual eBPF program determines its program type, affecting the way the\nprogram is verified by the kernel and defining what the program is allowed to\ndo.\n\n## Executable Linkable Format (ELF)\n\nExecutable Linkable Format (ELF) is the standard application binary format for\nLinux. It is also used as the output format of LLVM's BPF backend. ELF binaries\nare typically [executed directly by the\nkernel](https://lwn.net/Articles/631631/), but for eBPF, a different approach is\nneeded.\n\neBPF programs are not executable in the traditional sense. They depend on a user\nspace component that loads them, manages their resources, and can interact with\ntheir components. This is where projects such as libbpf and {{ proj }} come\nin.\n\nFor compatibility reasons, {{ proj }} follows the section naming conventions\nestablished by libbpf, since we consider upstream decisions to be authoritative\non this subject. There's also little reason to do things differently; section\nnames are essentially considered an API.\n\n??? tip \"How do I explore an ELF's contents?\"\n    You can display an ELF's section table using `readelf -S <binary>`.\n\n    For visualizing a program instructions or the contents of a map's data\n    section, you'll need a tool from the LLVM toolchain: `llvm-objdump`. For\n    example: `llvm-objdump -SD my_ebpf.o -j xdp`. This will limit output to\n    the `xdp` section (see [Program Sections](#program-sections)), display\n    corresponding source code lines if available using `-S`, and display\n    disassembled instructions using `-D`. The same can be done for data sections\n    like `.data` and `.rodata.` (see [Map Sections](#map-sections)).\n\n    Also worth mentioning: display an eBPF object's BTF type information using\n    `bpftool btf dump file my_object.o`.\n\n## Section Prefixes\n\nTo support encoding extra information into section names, a prefix convention\nusing forward slashes `/` is used. For example, a Kprobe-type program meant to\nbe attached to the `slub_flush` kernel symbol would be put into an ELF section\ncalled `kprobe/slub_flush`.\n\n### Miscellaneous Sections\n\n`license`\n\n:   In order to use certain BPF helpers in your program, it must be licensed\n    under a GPL-compatible license. BPF programs licensing follows the same\n    rules as kernel module licensing. This is explained in more detail in the\n    Linux kernel's [BPF licensing\n    documentation](https://docs.kernel.org/bpf/bpf_licensing.html#using-bpf-programs-in-the-linux-kernel).\n    See the\n    [`license_is_gpl_compatible`](https://elixir.bootlin.com/linux/v6.5.4/source/include/linux/license.h)\n    function in the Linux source code or the [Module Licensing\n    table](https://docs.kernel.org/process/license-rules.html#id1).\n\n    This section must only contain the license string of the programs in the\n    ELF. for example: `#!c char __license[] SEC(\"license\") = \"Dual MIT/GPL\";`.\n\n`version`\n\n:   **Deprecated.** Kernels <5.0 require this section to contain a value\n    matching the kernel's `LINUX_VERSION_CODE` for Kprobe-type programs. Always\n    omit this, {{ proj }} will populate this field automatically if needed.\n\n### Map Sections\n\n`.maps`\n\n:   This section is dedicated to BTF-style Map definitions.\n\n`maps`\n\n:   **Deprecated.** This section is expected to only contain fixed-width `struct\n    bpf_map_def` variables. Larger structs like iproute2's `struct bpf_elf_map`\n    can also be used for backwards compatibility. Any extra bytes past the end\n    of the size of a `struct bpf_map_def` are exposed by {{\n    godoc('MapSpec.Extra') }} and must be drained before attempting to create\n    the Map.\n\n#### :material-head-cog: Advanced: Special Map Sections\n\n`.data*`\n\n:   The LLVM BPF backend implements accesses to mutable global variables as\n    direct Array Map accesses. Since a single BPF program can be executed\n    concurrently as a result of the kernel processing packets and other events\n    asynchronously, a data section and the global variables it represents are\n    considered shared memory.\n\n    Variables can be emitted to specific sections, like\n    `#!c SEC(\".data.foo\") my_var = 123;`, as long as they match the `.data*`\n    prefix. This can prove useful for isolating certain variables to well-known\n    sections for Go code generation or custom variable rewriting logic.\n\n    Global, non-hidden variables are emitted to\n    {{ godoc('CollectionSpec.Variables') }}, where they can be modified before\n    loading the CollectionSpec into the kernel. See\n    [Global Variables](../concepts/global-variables.md) for instructions.\n\n`.rodata*`\n\n:   Like `.data*`, but for constants. These become read-only after loading the\n    CollectionSpec into the kernel, and are also exposed through\n    {{ godoc('CollectionSpec.Variables') }}.\n\n`.bss`\n\n:   Section emitted by the compiler when zero-initialized globals are present in\n    the ELF. Is typically zero-length in the ELF, and initialized by {{ proj }}\n    after loading. Also exposed through {{ godoc('CollectionSpec.Variables') }}.\n\n`.rel*`\n\n:   Not exposed by {{ proj }}, only used behind the scenes. Relocation sections\n    contain relocation records against their non-`.rel` prefixed counterparts.\n    This is mainly used for fixing up BPF instructions referring to Maps and\n    global variables.\n\n### Program Sections\n\nNames of Program sections mainly define the program's {{ godoc('ProgramType')\n}}, but also its {{ godoc('AttachType') }} and {{ godoc('AttachFlags') }} are\nautomatically set for convenience based on its section name.\n\nAs described previously, section prefixes containing a forward slash `/` expect\na second component to follow the slash. For example, a program in the\n`kprobe/slub_flush` section will automatically have its {{\ngodoc('ProgramSpec.AttachTo') }} field set to `slub_flush` to facilitate\nattaching the program later on.\n\nAdditionally, the program's original full section name can be found in {{\ngodoc('ProgramSpec.SectionName') }}.\n\n!!! tip \"\"\n    There's also [upstream libbpf\n    documentation](https://docs.kernel.org/bpf/libbpf/program_types.html) for\n    this. Not all of libbpf's program types may be supported by {{ proj }} yet.\n    If a program type you require is missing, please file an issue or send a\n    pull request!\n\n| Section (Prefix)      | {{ godoc('ProgramType') }} | {{ godoc('AttachType') }}        | {{ godoc('AttachFlags') }} |\n|:----------------------|:---------------------------|:---------------------------------|:---------------------------|\n| socket                | SocketFilter               |                                  |                            |\n| sk_reuseport/migrate  | SkReuseport                | AttachSkReuseportSelectOrMigrate |                            |\n| sk_reuseport          | SkReuseport                | AttachSkReuseportSelect          |                            |\n| kprobe/               | Kprobe                     |                                  |                            |\n| uprobe/               | Kprobe                     |                                  |                            |\n| kretprobe/            | Kprobe                     |                                  |                            |\n| uretprobe/            | Kprobe                     |                                  |                            |\n| tc                    | SchedCLS                   |                                  |                            |\n| classifier            | SchedCLS                   |                                  |                            |\n| action                | SchedACT                   |                                  |                            |\n| tracepoint/           | TracePoint                 |                                  |                            |\n| tp/                   | TracePoint                 |                                  |                            |\n| raw_tracepoint/       | RawTracepoint              |                                  |                            |\n| raw_tp/               | RawTracepoint              |                                  |                            |\n| raw_tracepoint.w/     | RawTracepointWritable      |                                  |                            |\n| raw_tp.w/             | RawTracepointWritable      |                                  |                            |\n| tp_btf/               | Tracing                    | AttachTraceRawTp                 |                            |\n| fentry/               | Tracing                    | AttachTraceFEntry                |                            |\n| fmod_ret/             | Tracing                    | AttachModifyReturn               |                            |\n| fexit/                | Tracing                    | AttachTraceFExit                 |                            |\n| fentry.s/             | Tracing                    | AttachTraceFEntry                | BPF_F_SLEEPABLE            |\n| fmod_ret.s/           | Tracing                    | AttachModifyReturn               | BPF_F_SLEEPABLE            |\n| fexit.s/              | Tracing                    | AttachTraceFExit                 | BPF_F_SLEEPABLE            |\n| freplace/             | Extension                  |                                  |                            |\n| lsm/                  | LSM                        | AttachLSMMac                     |                            |\n| lsm.s/                | LSM                        | AttachLSMMac                     | BPF_F_SLEEPABLE            |\n| iter/                 | Tracing                    | AttachTraceIter                  |                            |\n| iter.s/               | Tracing                    | AttachTraceIter                  | BPF_F_SLEEPABLE            |\n| syscall               | Syscall                    |                                  |                            |\n| xdp.frags/devmap      | XDP                        | AttachXDPDevMap                  | BPF_F_XDP_HAS_FRAGS        |\n| xdp/devmap            | XDP                        | AttachXDPDevMap                  |                            |\n| xdp.frags/cpumap      | XDP                        | AttachXDPCPUMap                  | BPF_F_XDP_HAS_FRAGS        |\n| xdp/cpumap            | XDP                        | AttachXDPCPUMap                  |                            |\n| xdp.frags             | XDP                        |                                  | BPF_F_XDP_HAS_FRAGS        |\n| xdp                   | XDP                        |                                  |                            |\n| perf_event            | PerfEvent                  |                                  |                            |\n| lwt_in                | LWTIn                      |                                  |                            |\n| lwt_out               | LWTOut                     |                                  |                            |\n| lwt_xmit              | LWTXmit                    |                                  |                            |\n| lwt_seg6local         | LWTSeg6Local               |                                  |                            |\n| cgroup_skb/ingress    | CGroupSKB                  | AttachCGroupInetIngress          |                            |\n| cgroup_skb/egress     | CGroupSKB                  | AttachCGroupInetEgress           |                            |\n| cgroup/skb            | CGroupSKB                  |                                  |                            |\n| cgroup/sock_create    | CGroupSock                 | AttachCGroupInetSockCreate       |                            |\n| cgroup/sock_release   | CGroupSock                 | AttachCgroupInetSockRelease      |                            |\n| cgroup/sock           | CGroupSock                 | AttachCGroupInetSockCreate       |                            |\n| cgroup/post_bind4     | CGroupSock                 | AttachCGroupInet4PostBind        |                            |\n| cgroup/post_bind6     | CGroupSock                 | AttachCGroupInet6PostBind        |                            |\n| cgroup/dev            | CGroupDevice               | AttachCGroupDevice               |                            |\n| sockops               | SockOps                    | AttachCGroupSockOps              |                            |\n| sk_skb/stream_parser  | SkSKB                      | AttachSkSKBStreamParser          |                            |\n| sk_skb/stream_verdict | SkSKB                      | AttachSkSKBStreamVerdict         |                            |\n| sk_skb                | SkSKB                      |                                  |                            |\n| sk_msg                | SkMsg                      | AttachSkMsgVerdict               |                            |\n| lirc_mode2            | LircMode2                  | AttachLircMode2                  |                            |\n| flow_dissector        | FlowDissector              | AttachFlowDissector              |                            |\n| cgroup/bind4          | CGroupSockAddr             | AttachCGroupInet4Bind            |                            |\n| cgroup/bind6          | CGroupSockAddr             | AttachCGroupInet6Bind            |                            |\n| cgroup/connect4       | CGroupSockAddr             | AttachCGroupInet4Connect         |                            |\n| cgroup/connect6       | CGroupSockAddr             | AttachCGroupInet6Connect         |                            |\n| cgroup/sendmsg4       | CGroupSockAddr             | AttachCGroupUDP4Sendmsg          |                            |\n| cgroup/sendmsg6       | CGroupSockAddr             | AttachCGroupUDP6Sendmsg          |                            |\n| cgroup/recvmsg4       | CGroupSockAddr             | AttachCGroupUDP4Recvmsg          |                            |\n| cgroup/recvmsg6       | CGroupSockAddr             | AttachCGroupUDP6Recvmsg          |                            |\n| cgroup/getpeername4   | CGroupSockAddr             | AttachCgroupInet4GetPeername     |                            |\n| cgroup/getpeername6   | CGroupSockAddr             | AttachCgroupInet6GetPeername     |                            |\n| cgroup/getsockname4   | CGroupSockAddr             | AttachCgroupInet4GetSockname     |                            |\n| cgroup/getsockname6   | CGroupSockAddr             | AttachCgroupInet6GetSockname     |                            |\n| cgroup/sysctl         | CGroupSysctl               | AttachCGroupSysctl               |                            |\n| cgroup/getsockopt     | CGroupSockopt              | AttachCGroupGetsockopt           |                            |\n| cgroup/setsockopt     | CGroupSockopt              | AttachCGroupSetsockopt           |                            |\n| struct_ops+           | StructOps                  |                                  |                            |\n| struct_ops.s+         | StructOps                  |                                  | BPF_F_SLEEPABLE            |\n| sk_lookup/            | SkLookup                   | AttachSkLookup                   |                            |\n| kprobe.multi          | Kprobe                     | AttachTraceKprobeMulti           |                            |\n| kretprobe.multi       | Kprobe                     | AttachTraceKprobeMulti           |                            |\n"
  },
  {
    "path": "docs/ebpf/contributing/architecture.md",
    "content": "Architecture of the library\n===\n\nThe bulk of the functionality of the library split across the `ebpf`, `btf` and\n`link` packages.\nBelow is a diagram how the most important types relate to each other.\nThe graph is in dependecy order, so an arrow from `Links` to `Map` can be read\nas \"Link depends on Map\".\n\n```mermaid\ngraph RL\n    Program --> ProgramSpec --> ELF\n    btf.Spec --> ELF\n    Map --> MapSpec --> ELF\n    Links --> Map & Program\n    ProgramSpec -.-> btf.Spec\n    MapSpec -.-> btf.Spec\n    subgraph Collection\n        Program & Map\n    end\n    subgraph CollectionSpec\n        ProgramSpec & MapSpec & btf.Spec\n    end\n```\n\nELF\n---\n\nBPF is usually produced by using Clang to compile a subset of C. Clang outputs\nan ELF file which contains program byte code (aka BPF), but also metadata for\nmaps used by the program. The metadata follows the conventions set by libbpf\nshipped with the kernel. Certain ELF sections have special meaning\nand contain structures defined by libbpf. Newer versions of clang emit\nadditional metadata in BPF Type Format.\n\nThe library aims to be compatible with libbpf so that moving from a C toolchain\nto a Go one creates little friction. To that end, the ELF reader\nis tested against the Linux selftests and avoids introducing custom behaviour\nif possible.\n\nThe output of the ELF reader is a `CollectionSpec` which encodes\nall of the information contained in the ELF in a form that is easy to work with\nin Go. The returned `CollectionSpec` should be deterministic: reading the same ELF\nfile on different systems must produce the same output.\nAs a corollary, any changes that depend on the runtime environment like the\ncurrent kernel version must happen when creating [Objects](#objects).\n\nSpecifications\n---\n\n`CollectionSpec` is a very simple container for `ProgramSpec`, `MapSpec` and\n`btf.Spec`. Avoid adding functionality to it if possible.\n\n`ProgramSpec` and `MapSpec` are blueprints for in-kernel\nobjects and contain everything necessary to execute the relevant `bpf(2)`\nsyscalls. They refer to `btf.Spec` for type information such as `Map` key and\nvalue types.\n\nThe {{ godoc(\"asm\") }} package provides an assembler that can be used to generate\n`ProgramSpec` on the fly.\n\nObjects\n---\n\n`Program` and `Map` are the result of loading specifications into the kernel.\nFeatures that depend on knowledge of the current system (e.g kernel version)\nare implemented at this point.\n\nSometimes loading a spec will fail because the kernel is too old, or a feature is not\nenabled. There are multiple ways the library deals with that:\n\n* Fallback: older kernels don't allow naming programs and maps. The library\n  automatically detects support for names, and omits them during load if\n  necessary. This works since name is primarily a debug aid.\n\n* Sentinel error: sometimes it's possible to detect that a feature isn't available.\n  In that case the library will return an error wrapping `ErrNotSupported`.\n  This is also useful to skip tests that can't run on the current kernel.\n\nOnce program and map objects are loaded they expose the kernel's low-level API,\ne.g. `NextKey`. Often this API is awkward to use in Go, so there are safer\nwrappers on top of the low-level API, like `MapIterator`. The low-level API is\nuseful when our higher-level API doesn't support a particular use case.\n\nLinks\n---\n\nPrograms can be attached to many different points in the kernel and newer BPF hooks\ntend to use bpf_link to do so. Older hooks unfortunately use a combination of\nsyscalls, netlink messages, etc. Adding support for a new link type should not\npull in large dependencies like netlink, so XDP programs or tracepoints are\nout of scope.\n\nEach bpf_link_type has one corresponding Go type, e.g. `link.tracing` corresponds\nto BPF_LINK_TRACING. In general, these types should be unexported as long as they\ndon't export methods outside of the Link interface. Each Go type may have multiple\nexported constructors. For example `AttachTracing` and `AttachLSM` create a\ntracing link, but are distinct functions since they may require different arguments.\n"
  },
  {
    "path": "docs/ebpf/contributing/index.md",
    "content": "# How to contribute\n\nDevelopment happens on [GitHub](https://github.com/cilium/ebpf) and contributions in\nall forms are welcome. Please take a look at [the architecture](architecture.md) to get\na better understanding of the high-level goals.\n\n## Developer Certificate of Origin\n\nThe Cilium project requires that all contributions to project repositories carry the\n[Developer Certificate of Origin][DCO]. This is as simple as appending a footer\nto your commits:\n\n```\nSigned-off-by: Your Name <name@example.org>\n```\n\nSigning off your contributions this way means that you've read and understood\nthe contents of the DCO.\n\n## Running the tests\n\nMany of the tests require privileges to set resource limits and load eBPF code.\nThe easiest way to obtain these is to run the tests with `sudo`.\n\nRun all tests with the following command:\n\n```shell-session\ngo test -exec sudo ./...\n```\n\nTo test the current package with a different kernel version you can use [vimto].\nOnce you have installed `vimto` and its dependencies you can run all tests on a\ndifferent kernel:\n\n```shell-session\nvimto -- go test ./...\n```\n\nUse one of the [precompiled kernels](https://github.com/cilium/ci-kernels/pkgs/container/ci-kernels/versions) like so:\n\n```shell-session\nvimto -kernel :mainline -- go test ./...\n```\n\n## Regenerating testdata and source code\n\nThe library includes some binary artifacts which are used for tests and some\ngenerated source code. Run `make` in the root of the repository to start\nthis process.\n\n```shell-session\nmake\n```\n\nThis requires Docker, as it relies on a standardized build\nenvironment to keep the build output stable.\nIt is possible to regenerate data using Podman by overriding the `CONTAINER_*`\nvariables:\n\n```shell-session\nmake CONTAINER_ENGINE=podman CONTAINER_RUN_ARGS=\n```\n\n## Project Roles\n\nIf you'd like to contribute to the library more regularly, one of the\n[maintainers][ebpf-lib-maintainers] can add you to the appropriate team or mark\nyou as a code owner. Please create an issue in the repository.\n\n* [ebpf-go-contributors]\n    * Have [\"Triage\"][permissions] role\n    * May be asked to review certain parts of code\n    * May be asked to help with certain issues\n* [ebpf-go-reviewers] and [ebpf-go-windows-reviewers]\n    * Have [\"Write\"][permissions] role\n    * CODEOWNER of a part of the code base\n    * In-depth review of code, escalates to maintainers if necessary\n        * For bugfixes: review within 1-2 days\n        * Otherwise: review within a work week\n        * When lacking time: escalate to maintainers, but don’t ignore\n* [ebpf-lib-maintainers]\n    * Have [\"Admin\"][permissions] role\n    * Manage releases\n    * Triage incoming issues and discussions and pull in CODEOWNERS if needed\n    * Maintain CI & project permissions\n    * Maintain roadmap and encourage contributions towards it\n    * Merge approved PRs\n\n[vimto]: https://github.com/lmb/vimto\n[permissions]: https://docs.github.com/en/organizations/managing-user-access-to-your-organizations-repositories/repository-roles-for-an-organization#permissions-for-each-role\n[ebpf-go-contributors]: https://github.com/cilium/community/blob/main/ladder/teams/ebpf-go-contributors.yaml\n[ebpf-go-reviewers]: https://github.com/cilium/community/blob/main/ladder/teams/ebpf-go-reviewers.yaml\n[ebpf-go-windows-reviewers]: https://github.com/cilium/community/blob/main/ladder/teams/ebpf-go-windows-reviewers.yaml\n[ebpf-lib-maintainers]: https://github.com/cilium/community/blob/main/roles/Maintainers.md#ebpf-lib-maintainers-maintainers-of-ciliumebpf\n[DCO]: https://developercertificate.org/\n"
  },
  {
    "path": "docs/ebpf/contributing/new-example.md",
    "content": "# Adding a new example\n\nThe library includes some examples to make getting started easier.\nThe aim of the examples is to __show how the library works, not how to implement a specific thing in eBPF__.\nThis is because the scope of eBPF is simply too large for us to cover.\n\nPlease consider the following before proposing a new example:\n\n1. What feature __of the library__ does it showcase?\n2. Is there already an existing example for that feature? If yes, could it be extended without making it harder to understand?\n3. How complicated is the eBPF code required to make it work? How could the amount of eBPF be minimised?\n\nPlease contact the maintainers on Slack if you are in doubt about any of\nthese points.\n\n## What makes a good example?\n\n* It should be concise. The less code the better.\n* It should show a single thing. The less configurable the better.\n* It should be well documented. Even a novice user must be able to follow\n  along.\n* It should produce meaningful output or have an easily testable effect.\n* It should have as few requirements on software / hardware as possible.\n"
  },
  {
    "path": "docs/ebpf/contributing/new-feature.md",
    "content": "# Adding a new feature\n\nWe're very much looking for contributions which flesh out the functionality of\nthe library.\n\n1. Have a look at the [architecture](architecture.md) of the library if you\n   haven't already.\n2. [Join](https://ebpf.io/slack) the\n   [#ebpf-go-dev](https://cilium.slack.com/messages/ebpf-go-dev) channel to\n   discuss your requirements and how the feature can be implemented.\n   Alternatively open a new Discussion if you prefer to not use Slack.\n   The most important part is figuring out how much new exported API is necessary.\n   **The less new API is required the easier it will be to land the feature.**\n   Also see [API stability](#api-stability).\n3. (*optional*) Create a draft PR if you want to discuss the implementation or have hit a problem. It's fine if this doesn't compile or contains debug statements.\n4. Create a PR that is ready to merge. This must pass CI and have tests.\n\n## API stability\n\nThere is an emphasis on compatibility even though the library doesn't guarantee\nthe stability of its API at the moment.\n\n1. If possible, avoid breakage by introducing new API and deprecating the old one\n   at the same time. If an API was deprecated in v0.x it can be removed in v0.x+1.\n   This is especially important if there is no straighforward way to convert\n   from the old to the new API.\n2. Breaking API in a way that causes compilation failures is acceptable but must\n   have good reasons.\n3. Changing the semantics of the API without causing compilation failures is\n   heavily discouraged.\n"
  },
  {
    "path": "docs/ebpf/contributing/windows.md",
    "content": "# Working on the Windows port\n\nThe library has basic support for interacting with eBPF for Windows (efW).\nThings are subject to change because eBPF for Windows has not had a stable (signed) release yet.\n\n## Differences between Linux and eBPF for Windows\n\n* eBPF for Windows has three distinct modes of operation: an interpreter, a JIT\n  and a way to compile eBPF to a native Windows driver. The native driver can\n  be signed using the usual mechanisms. It is likely that a stable release of\n  eBPF for Windows will only support native drivers.\n  The library supports both mechanisms, and relies on the JIT for its testsuite.\n  This is because the native Windows driver mechanism still comes with significant\n  downsides.\n* eBPF for Windows has a large user-space component which ebpf-go calls into\n  via dynamic runtime linking. This uses the same infrastructure as CGo but\n  does not require a C toolchain and is therefore trivial to distribute.\n\n## Exported API\n\nThe library only supports a subset of the full API on Windows, because the eBPF for\nWindows runtime doesn't yet or never will support certain features. API which\nare not supported will return `ErrNotSupported`. Some interfaces such as Linux-specific\nlink types are removed outright, but this is kept to a minimum since it is very\ncumbersome for users to deal with API that change based on platform.\n\n## Development setup\n\nThe port is developed using a Windows VM running on a Linux host.\nThere is a [script](https://github.com/cilium/ebpf/tree/main/scripts/windows)\nwhich automates the Windows installation.\nAfter the installation finishes you should be able to SSH to\nthe VM and [follow the instructions to clone and build eBPF for Windows][efw-clone].\n__Execute `Import-VsEnv` (installed by the setup script) to add `msbuild` to PATH.__\n\n```\nPS C:\\Users\\lmbauer> Import-VsEnv\n**********************************************************************\n** Visual Studio 2022 Developer PowerShell v17.10.4\n** Copyright (c) 2022 Microsoft Corporation\n**********************************************************************\nPS C:\\Users\\lmbauer> msbuild\nMSBuild version 17.10.4+10fbfbf2e for .NET Framework\nMSBUILD : error MSB1003: Specify a project or solution file. The current working directory does not contain a project or solution file.\n```\n\n### Compiling the runtime\n\n!!! note \"Pre-built eBPF for Windows binaries\"\n    You may be able to download precompiled binaries from the [efW CI/CD] pipeline.\n    Look for an artifact called \"Build-x64-Debug\", which should contain\n    `setup-ebpf.ps1` mentioned below.\n\nThe upstream instructions currently explain how to compile the full project, which takes quite a long time.\nIt is possible to build only some parts from the command line:\n\n* Installer: `msbuild /m /p:Configuration=Debug /p:Platform=x64 ebpf-for-windows.sln -t:\"installer\\ebpf-for-windows\"`\n* Unit tests: `msbuild /m /p:Configuration=Debug /p:Platform=x64 ebpf-for-windows.sln -t:\"tests\\unit_tests\"`\n* Clean: `msbuild /m /p:Configuration=Debug /p:Platform=x64 ebpf-for-windows.sln -t:\"Clean\"`\n\nAfter compilation of the installer finishes you can install the runtime:\n\n```\n.\\x64\\Debug\\setup-ebpf.ps1\n```\n\n_(You can pass `-Uninstall` to the script to remove a previous installation.)_\n\nYou can now run the Go unit tests of the library:\n\n```\ngo test ./internal/sys\n```\n\n!!! note \"Tests fail with `load ebpfapi.dll: not found`\"\n    This usually means that either the Windows runtime is not installed or that\n    the efW installation folder is not on the PATH yet. The latter tends to\n    happen when executing tests via ssh, since sshd doesn't pick up\n    changes in the environment without restarting.\n    Restart the service by issuing `Restart-Service sshd` from a powershell\n    prompt and then re-establish the ssh session.\n\n### efW extensions\n\nefW separates the runtime from the implementation of the various hooks / program\ntypes. The hooks are shipped as extensions in a separate Windows kernel service.\nInstalling an extension involves two steps:\n\n1. Installing the extension as a Windows kernel service.\n2. Registering the program type(s) in the \"eBPF Store\".\n\nFor [ntosebpfext] the setup process looks as follows, assuming the extension has\nalready been built:\n\n```\nPS C:\\Users\\lorenz\\ntosebpfext> .\\tests\\process_monitor.Tests\\Setup-ProcessMonitorTests.ps1 -ArtifactsRoot .\\x64\\Debug\\\nCreating and starting the ntosebpfext service from C:\\Users\\lorenz\\ntosebpfext\\x64\\Debug\\\\ntosebpfext.sys.\nPS C:\\Users\\lorenz\\ntosebpfext> .\\x64\\Debug\\ntos_ebpf_ext_export_program_info.exe\nExporting program information.\nExporting section information.\n```\n\n## Debugging\n\nDebugging on Windows is a bit painful, since we call from Go into `ebpfapi.dll`\nwhich is implemented in C++. There is currently no debugger which understands\nboth C++ and Go.\n\nThe most fruitful approach is to use [WinDbg].\nIt will catch exceptions in C++ code, give useful backtraces and allows stepping\nthrough source code.\n\nRun the WinDbg GUI as an administrator and then open the executable via `Ctrl-E`.\nAt the prompt you can set a breakpoint on `bpf()`:\n\n```\nbu ebpfapi!bpf\ng\n```\n\nThis will halt execution once the library calls into `bpf()` inside `ebpfapi.dll`.\nUse the [`CDB` commands][cdb-commands] or the GUI to navigate.\n\nIt may be possible to use [CDB] to debug via the command line, but this doesn't\nseem to work via ssh.\n\n### Windows trace log\n\nThe `testmain` package has a small bit of instrumentation which enables tracing\nof the efW subsystem on demand. Simply pass the `-trace-log` flag when running\ntests:\n\n```\nPS C:\\Users\\lorenz\\ebpf> go test -run '^TestMap$' -v -trace-log\n=== RUN   TestMap\n    map_test.go:54: WindowsArray#3\n--- PASS: TestMap (0.02s)\nPASS\n100%  [>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]\n  base       ebpf_api_initiate returned success\n  entry-exit ebpf_map_create\n  entry-exit _create_map\n  entry-exit _ebpf_core_protocol_create_map\n  entry-exit ebpf_core_create_map\n  entry-exit ebpf_map_create\n  base       eBPF object initialized                                 object=0xFFFF8982A875BF30 object_type=       1\n  base       ebpf_map_create returned success\n  entry-exit ebpf_handle_create\n  core       ebpf_handle_create: returning handle                    value=376\n  base       ebpf_handle_create returned success\n  base       ebpf_core_create_map returned success\n...\n```\n\nEnabling the instrumentation can fail if the tests crashed too often. In that\ncase you can manually stop and remove the tracing entries via the GUI:\n`compmgmt.msc` -> \"Performance\" -> \"Data Collector Sets\" -> \"Event Trace Sessions\".\nLook for sessions containing \"ebpf-go\".\nRebooting might also help.\n\n### Interpreting error codes\n\nefW uses several layers of error codes.\n\n* Windows [system error codes] and [RPC errors] are sometimes exposed by\n  exceptions, which appear in the trace log.\n* [`ebpf_result_t`][ebpf_result_t]: wraps Windows errors and\n  is returned from \"native\" efW API.\n* Unix-style errno, as defined by Windows' [`errno.h`][errno.h]:\n  wraps `ebpf_result_t` and is returned from libbpf and `bpf()` API.\n  Unfortunately not all [errno values] line up with Linux.\n  This usually manifests in cryptic `Errno(119)` errors.\n\n[efw-clone]: https://github.com/microsoft/ebpf-for-windows/blob/main/docs/GettingStarted.md#how-to-clone-and-build-the-project-using-visual-studio\n[CDB]: https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/debugging-using-cdb-and-ntsd\n[cdb-commands]: https://learn.microsoft.com/en-us/windows-hardware/drivers/debuggercmds/commands\n[WinDbg]: https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/\n[ebpf_result_t]: https://github.com/microsoft/ebpf-for-windows/blob/main/include/ebpf_result.h\n[system error codes]: https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-\n[RPC errors]: https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--1700-3999-\n[errno.h]: https://learn.microsoft.com/en-us/cpp/c-runtime-library/errno-constants?view=msvc-170\n[errno values]: https://github.com/microsoft/ebpf-for-windows/issues/3729#issuecomment-2289025455\n[ntosebpfext]: https://github.com/microsoft/ntosebpfext\n[access the debug version of the msvc runtime]: https://github.com/microsoft/ebpf-for-windows/issues/3872\n[msvc debug DLLs]: https://github.com/microsoft/ebpf-for-windows/blob/7005b7ff47e7281843d6b414cd69fc5a979507c8/scripts/setup-ebpf.ps1#L17-L27\n[efW CI/CD]: https://github.com/microsoft/ebpf-for-windows/actions/workflows/cicd.yml?query=branch%3Amain+is%3Acompleted\n"
  },
  {
    "path": "docs/ebpf/guides/getting-started.md",
    "content": "# Getting Started with eBPF in Go\n\nIn this guide, we'll walk you through building a new eBPF-powered Go application\nfrom scratch. We'll introduce the toolchain, write a minimal eBPF C example and\ncompile it using bpf2go. Then, we'll put together a Go\napplication that loads the eBPF program into the kernel and periodically\ndisplays its output.\n\nThe application attaches an eBPF program to an XDP hook that counts the number\nof packets received by a physical interface. Filtering and modifying packets is\na major use case for eBPF, so you'll see a lot of its features being geared\ntowards it. However, eBPF's capabilities are ever-growing, and it has been\nadopted for tracing, systems and application observability, security and much\nmore.\n\n## eBPF C program\n\n!!! abstract \"Dependencies\"\n    To follow along with the example, you'll need:\n\n    * Linux kernel version 5.7 or later, for bpf_link support\n    * LLVM 11 or later [^1] (`clang` and `llvm-strip`)\n    * libbpf headers [^2]\n    * Linux kernel headers [^3]\n    * Go compiler version supported by {{ proj }}'s Go module\n\n[^1]:\n    Use `clang --version` to check which version of LLVM you have installed.\n    Refer to your distribution's package index to finding the right packages to\n    install, as this tends to vary wildly across distributions. Some\n    distributions ship `clang` and `llvm-strip` in separate packages.\n\n[^2]:\n    For Debian/Ubuntu, you'll typically need `libbpf-dev`. On Fedora, it's\n    `libbpf-devel`.\n\n[^3]:\n    On AMD64 Debian/Ubuntu, install `linux-headers-amd64`. On Fedora, install\n    `kernel-devel`.\n\n    On Debian, you may also need `ln -sf /usr/include/asm-generic/\n    /usr/include/asm` since the example expects to find `<asm/types.h>`.\n\nLet's begin by writing our eBPF C program, as its structure will be used as the\nbasis for generating Go boilerplate.\n\nClick the :material-plus-circle: annotations in the code snippet for a detailed\nexplanation of the individual components.\n\n{{ c_example('getting_started_counter', title='counter.c') }}\n\n1. When putting C files alongside Go files, they need to be excluded by a Go\n   build tag, otherwise `go build` will complain with `C source files not\n   allowed when not using cgo or SWIG`. The Go toolchain can safely ignore our\n   eBPF C files.\n\n2. Include headers containing the C macros used in the example. Identifiers such\n   as `__u64` and `BPF_MAP_TYPE_ARRAY` are shipped by the Linux kernel, with\n   `__uint`, `__type`, `SEC` and BPF helper definitions being provided by\n   libbpf.\n\n3. Declare a BPF map called `pkt_count`, an Array-type Map holding a single\n   u64 value. See `man bpf` or the online [bpf man\n   pages](https://man7.org/linux/man-pages/man2/bpf.2.html) for an overview of\n   all available map types.<br/><br/>\n   For this example, we went with an array since it's a well-known data\n   structure you're likely familiar with. In BPF, arrays are preallocated and\n   zeroed, making them safe and ready to use without any initialization.\n\n4. The Map definition is placed in the `.maps` ELF section, which is where {{\n   proj }} expects to find it.\n\n5. In BPF, not all programs are equal. Some act on raw packets, some execute\n   within the context of kernel or user space functions, while others expect to\n   be run against an `__sk_buff`. These differences are encoded in the Program\n   Type. libbpf introduced a set of conventions around which ELF sections\n   correspond to which type. In this example, we've chosen `xdp` since we'll\n   attach this program to the XDP hook later.\n\n6. There's only one possible element in `pkt_count` since we've specified a\n   `max_entries` value of 1. We'll always access the 0th element of the array.\n\n7. Here, we're asking the BPF runtime for a pointer to the 0th element of the\n   `pkt_count` Map. <br/><br/>\n   `bpf_map_lookup_elem` is a BPF helper declared in `docs.h`. Helpers are small\n   pieces of logic provided by the kernel that enable a BPF program to interact\n   with its context or other parts of the kernel. Discover all BPF helpers\n    supported by your kernel using `man bpf-helpers` or the online [bpf-helpers\n    man pages](https://man7.org/linux/man-pages/man7/bpf-helpers.7.html).\n\n8. All Map lookups can fail. If there's no element for the requested `key` in\n   the Map, `count` will hold a null pointer. The BPF verifier is very strict\n   about checking access to potential null pointers, so any further access\n   to `count` needs to be gated by a null check.\n\n9. Atomically increase the value pointed to by `count` by 1. It's important to\n   note that on systems with SMP enabled (most systems nowadays), the same BPF\n   program can be executed concurrently.<br/><br/>\n   Even though we're loading only one 'copy' of our Program, accompanied by a\n   single `pkt_count` Map, the kernel may need to process incoming packets on\n   multiple receive queues in parallel, leading to multiple instances of the\n   program being executed, and `pkt_count` effectively becoming a piece of\n   shared memory. Use atomics to avoid dirty reads/writes.\n\n10. XDP allows for dropping packets early, way before it's passed to the\n   kernel's networking stack where routing, firewalling (ip/nftables) and things\n   like TCP and sockets are implemented. We issue the `XDP_PASS` verdict to\n   avoid ever interfering with the kernel's network stack.\n\n11. Since some BPF helpers allow calling kernel code licensed under GPLv2, BPF\n   programs using specific helpers need to declare they're (at least partially)\n   licensed under GPL. Dual-licensing is possible, which we've opted for here\n   with `Dual MIT/GPL`, since {{ proj }} is MIT-licensed.\n\nCreate an empty directory and save this file as `counter.c`. In the next step,\nwe'll set up the necessary bits to compile our eBPF C program using `bpf2go`.\n\n## Compile eBPF C and generate scaffolding using bpf2go\n\nWith the `counter.c` source file in place, create another file called `gen.go`\ncontaining a `//go:generate` statement. This invokes `bpf2go` when running `go\ngenerate` in the project directory.\n\nAside from compiling our eBPF C program, bpf2go will also generate some\nscaffolding code we'll use to load our eBPF program into the kernel and interact\nwith its various components. This greatly reduces the amount of code we need to\nwrite to get up and running.\n\n{{ go_example('getting_started_gen', title='gen.go') }}\n\n!!! tip \"\"\n    Using a dedicated file for your package's `//go:generate` statement(s) is\n    neat for keeping them separated from application logic. At this point in the\n    guide, we don't have a `main.go` file yet. Feel free to include it in\n    existing Go source files if you prefer.\n\nBefore using the Go toolchain, Go wants us to declare a Go module. This command\nshould take care of that:\n\n```{ .shell-session data-copy=\"go mod init ebpf-test && go mod tidy\" }\n% go mod init ebpf-test\ngo: creating new go.mod: module ebpf-test\ngo: to add module requirements and sums:\n    go mod tidy\n% go mod tidy\n```\n\nFirst, add `bpf2go` as a tool dependency to your Go module. This ensures the\nversion of `bpf2go` used by the Go toolchain always matches your version of the\nlibrary.\n\n```{ .shell-session data-copy=\"go get -tool github.com/cilium/ebpf/cmd/bpf2go\" }\n% go get -tool github.com/cilium/ebpf/cmd/bpf2go\n```\n\nNow we're ready to run `go generate`:\n\n```{ .shell-session data-copy=\"go generate\" }\n% go generate\nCompiled /home/timo/getting_started/counter_bpfel.o\nStripped /home/timo/getting_started/counter_bpfel.o\nWrote /home/timo/getting_started/counter_bpfel.go\nCompiled /home/timo/getting_started/counter_bpfeb.o\nStripped /home/timo/getting_started/counter_bpfeb.o\nWrote /home/timo/getting_started/counter_bpfeb.go\n```\n\n`bpf2go` built `counter.c` into `counter_bpf*.o` behind the scenes using\n`clang`. It generated two object files and two corresponding Go source files\nbased on the contents of the object files. Do not remove any of these, we'll\nneed them later.\n\nLet's inspect one of the generated .go files:\n\n{{ go_example('counterPrograms', title='counter_bpfel.go', signature=True) }}\n\nNeat! Looks like bpf2go automatically generated a scaffolding for interacting\nwith our `count_packets` Program from Go. In the next step, we'll explore how to\nload our program into the kernel and put it to work by attaching it to an XDP\nhook!\n\n## The Go application\n\nFinally, with our eBPF C code compiled and Go scaffolding generated, all that's\nleft is writing the Go code responsible for loading and attaching the program to\na hook in the Linux kernel.\n\nClick the :material-plus-circle: annotations in the code snippet for some of the\nmore intricate details. Note that we won't cover anything related to the Go\nstandard library here.\n\n{{ go_example('getting_started_main', title='main.go') }}\n\n1. Linux kernels before 5.11 use RLIMIT_MEMLOCK to control the maximum amount of\n   memory allocated for a process' eBPF resources. By default, it's set to a\n   relatively low value. See [Resource Limits](../concepts/rlimit.md) for a deep\n   dive.\n\n1. `counterObjects` is a struct containing nil pointers to Map and Program\n   objects. A subsequent call to `loadCounterObjects` populates these fields\n   based on the struct tags declared on them. This mechanism saves a lot of\n   repetition that would occur by checking a Collection for Map and Program\n   objects by string.<br/><br/>\n   As an added bonus, `counterObjects` adds type safety by turning these into\n   compile-time lookups. If a Map or Program doesn't appear in the ELF, it won't\n   appear as a struct field and your Go application won't compile, eliminating\n   a whole class of runtime errors.\n\n1. Close all file descriptors held by `objs` right before the Go application\n   terminates. See [Object Lifecycle](../concepts/object-lifecycle.md) for a\n   deep dive.\n\n1. Associate the `count_packets` (stylized in the Go scaffolding as\n   `CountPackets`) eBPF program with `eth0`. This returns a {{\n   godoc('link/Link') }} abstraction.\n\n1. Close the file descriptor of the Program-to-interface association. Note that\n   this will stop the Program from executing on incoming packets if the Link was\n   not {{ godoc('link/Link.Pin') }}ed to the bpf file system.\n\n1. Load a uint64 stored at index 0 from the `pkt_count` Map (stylized in the Go\n   scaffolding as `PktCount`). This corresponds to the logic in `counter.c`.\n\nSave this file as `main.go` in the same directory alongside `counter.c` and\n`gen.go`.\n\n## Building and running the Go application\n\nNow `main.go` is in place, we can finally compile and run our Go application!\n\n```{ .shell-session data-copy=\"go build && sudo ./ebpf-test\" }\n% go build && sudo ./ebpf-test\n2023/09/20 17:18:43 Counting incoming packets on eth0..\n2023/09/20 17:18:47 Received 0 packets\n2023/09/20 17:18:48 Received 4 packets\n2023/09/20 17:18:49 Received 11 packets\n2023/09/20 17:18:50 Received 15 packets\n```\n\nGenerate some traffic on eth0 and you should see the counter increase.\n\n### Iteration Workflow\n\nWhen iterating on the C code, make sure to keep generated files up-to-date.\nWithout re-running bpf2go, the eBPF C won't be recompiled, and any changes made\nto the C program structure won't be reflected in the Go scaffolding.\n\n```{ .shell-session data-copy=\"go generate && go build && sudo ./ebpf-test\" }\n% go generate && go build && sudo ./ebpf-test\n```\n\n## What's Next?\n\nCongratulations, you've just built your (presumably) first eBPF-powered Go app!\nHopefully, this guide piqued your interest and gave you a better sense of what\neBPF can do and how it works.\n\nWith XDP, we've only barely scratched the surface of eBPF's many use cases and\napplications. For more easily-accessible examples, see [the main repository's\nexamples/ folder](https://github.com/cilium/ebpf/tree/main/examples). It\ndemonstrates use cases like tracing user space applications, extracting\ninformation from the kernel, attaching eBPF programs to network sockets and\nmore.\n\nFollow our other guides to continue on your journey of shipping a portable\neBPF-powered application to your users.\n"
  },
  {
    "path": "docs/ebpf/guides/portable-ebpf.md",
    "content": "# Shipping Portable eBPF-powered Applications\n\n!!! incomplete\n    This guide builds on Getting Started.\n    \n    Document what the various ways are for making tools portable across kernel\n    versions and what the various CO-RE techniques are.\n\n!!! tip \"\"\n    We recommend building eBPF C code from within a container with a stable LLVM\n    toolchain, as well as checking all generated `.o` and `.go` files into\n    source control. This buys you fully-reproducible builds, prevents bugs due\n    to team members using different LLVM versions and makes your packages fully\n    independent and `go run`nable. It also prevents PII from leaking into ELFs\n    in the form of absolute paths to `.c` source files in DWARF info.\n\n### Cross-compiling\n\nYou may have noticed bpf2go generating two sets of files:\n\n- `*_bpfel.o` and `*_bpfel.go` for little-endian architectures like amd64,\n  arm64, riscv64 and loong64\n- `*_bpfeb.o` and `*_bpfeb.go` for big-endian architectures like s390(x), mips\n  and sparc\n\nBoth sets of .go files contain a `//go:embed` statement that slurps the contents\nof the respective .o files into a byte slice at compile time. The result is a\nstandalone Go application binary that can be deployed to a target machine\nwithout any of the .o files included. To further reduce runtime dependencies,\nadd `CGO_ENABLED=0` to `go build` and your application won't depend on libc.\n(assuming none of your other dependencies require cgo)\n\nMoreover, because both eBPF objects and Go scaffolding are generated for both\nbig- and little-endian architectures, cross-compiling your Go application is as\nsimple as setting the right `GOARCH` value at compile time.\n\nPulling it all together, for building an eBPF-powered Go application for a\nRaspberry Pi running a 64-bit Linux distribution:\n\n```shell-session\nCGO_ENABLED=0 GOARCH=arm64 go build\n```\n\n### Compile Once - Run Everywhere?\n\nSince we can generate a standalone binary and deploy it to any system, does that\nmean tools built using {{ proj }} will magically work anywhere? Unfortunately,\nno, not really.\n\nThe kernel's internal data structures change as the kernel progresses in\ndevelopment, just like any other software. Differences in compile-time\nconfiguration affect data structures and the presence of certain kernel symbols.\nThis means that, even when using the exact same kernel release, no two Linux\ndistributions will be the same when it comes to data layout.\n\nThis is problematic for authors that want to ship a single binary to their users\nand expect it to work across multiple distributions and kernel versions. In\nresponse to this, the term *Compile Once - Run Everywhere* was coined to\ndescribe the collection of techniques employed to achieve universal\ninteroperability for eBPF. This technique relies on type information encoded in\nBPF Type Format (BTF) to be shipped with the kernel so memory accesses can be\nadjusted right before loading the eBPF program into the kernel.\n\nAlternatively, you may opt for shipping a full LLVM compiler toolchain along\nwith your application and recompiling the eBPF C against Linux kernel headers\npresent on the target machine. This approach is out of scope of the {{ proj }}\ndocumentation.\n"
  },
  {
    "path": "docs/ebpf/guides/windows-support.md",
    "content": "# Windows support\n\nThe library has preliminary support for the [eBPF for Windows] runtime, allowing\nyou to build Go applications for Windows using the same APIs as on Linux.\n\n!!! warning \"Feature parity\"\n    efW doesn't have feature parity with Linux. Many APIs in\n    the library will return `ErrNotSupported` in this case.\n\n!!! warning \"Binary compatibility\"\n    efW is not binary compatible with Linux. It is not possible\n    to compile an eBPF program for Linux and use it on Windows.\n\n## Platform specific constants\n\nefW only provides [source compatibility] with Linux.\nWhile certain Linux map or program types have an equivalent on Windows, they\ndon't always behave the same.\n\nFor this reason, the various type enumerations have completely distinct values\non Windows, for example `WindowsHashMap` is the equivalent of `HashMap`.\nAttempting to create a `HashMap` on Windows will return an error, and vice versa.\n\n## Platform specific ELFs\n\n!!! note \"\"\n    Loading Windows ELFs is not supported yet.\n\nELFs compiled against Linux and Windows headers are not binary compatible.\nAdd the following to ELFs targeting Windows until there is an\n[official way to declare the platform](https://github.com/microsoft/ebpf-for-windows/issues/3956):\n\n```C\nconst bool __ebpf_for_windows_tag __attribute__((section(\".ebpf_for_windows\"))) = true;\n```\n\n## Working with signed programs\n\nThe runtime will most likely require all eBPF programs to be signed by\nMicrosoft. Signing programs relies on packaging eBPF `.c` files as drivers using\nthe [native code pipeline], converting bytecode into a `.sys` file.\n\nThe interface to load such drivers does not allow modifying the bytecode or map\ndefinitions, therefore you can't interact with them via `CollectionSpec`, etc.\nInstead you must load them via `LoadCollection`:\n\n```go\ncoll, err := LoadCollection(\"path\\\\to\\\\driver.sys\")\n```\n\nThe returned Collection contains Maps and Programs which you can interact with\nas usual.\n\n[eBPF for Windows]: https://github.com/microsoft/ebpf-for-windows\n[source compatibility]: https://github.com/microsoft/ebpf-for-windows?tab=readme-ov-file#2-does-this-provide-app-compatibility-with-ebpf-programs-written-for-linux\n[native code pipeline]: https://github.com/microsoft/ebpf-for-windows/blob/main/docs/NativeCodeGeneration.md\n[LoadCollection]: https://pkg.go.dev/github.com/cilium/ebpf#LoadCollection\n"
  },
  {
    "path": "docs/ebpf/index.md",
    "content": "<style>\n  .md-content .md-typeset h1 {\n    display: none;\n  }\n</style>\n\n<p align=\"center\" class=\"tagline\">The eBPF Library for Go</p>\n\n![Honeygopher](ebpf-go.png){ align=right width=\"180\" }\n\n[![PkgGoDev](https://pkg.go.dev/badge/github.com/cilium/ebpf)](https://pkg.go.dev/github.com/cilium/ebpf)\n\n:ebpf-go: {{ proj }} is a Go library for working with :ebee-color: eBPF. It does\nnot depend on C, libbpf, or any other Go libraries other than the standard\nlibrary, making it an excellent choice for writing self-contained, portable\ntools that run on a variety of architectures.\n\nThis documentation aims to provide a central resource for learning how to build\nGo applications that use eBPF.\n\n## Installing\n\nTo add {{ proj }} as a dependency to an existing Go module, run this from within\nthe module's directory:\n\n```\ngo get github.com/cilium/ebpf\n```\n\n## Target Audience\n\nThis documentation assumes familiarity with the basic concepts and terminology\nof eBPF, as well as a basic understanding of the Go toolchain and how to write\nidiomatic Go code.\n\nFor a high-level understanding of what eBPF is and how it works, please see [the\neBPF introduction at :ebee-color: ebpf.io](https://ebpf.io/what-is-ebpf).\n\n## Examples\n\nDiscover [projects using {{ proj }} here](users.md). The repository contains an\n[examples/ directory](https://github.com/cilium/ebpf/tree/main/examples) with\nminimal demo applications that can be tested on any supported Linux machine.\n"
  },
  {
    "path": "docs/ebpf/stylesheets/extra.css",
    "content": "/* Tagline on landing page. */\n.tagline {\n  font-size: 3em;\n  font-weight: 900;\n  letter-spacing: -0.5px;\n  background: linear-gradient(120deg, #4051B5, 35%, #6AD6E4);\n  background-clip: text;\n  -webkit-background-clip: text;\n  -webkit-text-fill-color: transparent;\n}\n\n/* :progress-wrench:\n   Custom 'incomplete' admonition for sections that need work or maintenance.\n   Create blocks using '!!! incomplete'.\n*/\n:root {\n  --md-admonition-icon--incomplete: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M13 2.03v2.02c4.39.54 7.5 4.53 6.96 8.92-.46 3.64-3.32 6.53-6.96 6.96v2c5.5-.55 9.5-5.43 8.95-10.93-.45-4.75-4.22-8.5-8.95-8.97m-2 .03c-1.95.19-3.81.94-5.33 2.2L7.1 5.74c1.12-.9 2.47-1.48 3.9-1.68v-2M4.26 5.67A9.885 9.885 0 0 0 2.05 11h2c.19-1.42.75-2.77 1.64-3.9L4.26 5.67M2.06 13c.2 1.96.97 3.81 2.21 5.33l1.42-1.43A8.002 8.002 0 0 1 4.06 13h-2m5.04 5.37-1.43 1.37A9.994 9.994 0 0 0 11 22v-2a8.002 8.002 0 0 1-3.9-1.63m9.72-3.18-4.11-4.11c.41-1.04.18-2.26-.68-3.11-.9-.91-2.25-1.09-3.34-.59l1.94 1.94-1.35 1.36-1.99-1.95c-.54 1.09-.29 2.44.59 3.35.86.86 2.08 1.08 3.12.68l4.11 4.1c.18.19.45.19.63 0l1.04-1.03c.22-.18.22-.5.04-.64Z\"/></svg>')\n}\n\n.md-typeset .admonition.incomplete,\n.md-typeset details.incomplete {\n  border-color: rgb(255, 204, 77);\n}\n\n.md-typeset .incomplete>.admonition-title,\n.md-typeset .incomplete>summary {\n  background-color: rgba(255, 204, 77, 0.1);\n}\n\n.md-typeset .incomplete>.admonition-title::before,\n.md-typeset .incomplete>summary::before {\n  background-color: rgb(255, 204, 77);\n  -webkit-mask-image: var(--md-admonition-icon--incomplete);\n  mask-image: var(--md-admonition-icon--incomplete);\n}\n\n/* gp and go are the classes used for prompt and output in shell-session code\n   blocks. Prevent these from being highlighted as it hurts UX.\n*/\n.highlight .gp,\n.highlight .go {\n  user-select: none;\n}\n\n.md-typeset {\n  .md-badge {\n    font-size: 0.85em;\n\n    .md-badge__icon {\n      padding: 0.4em;\n      background: var(--md-accent-fg-color--transparent);\n      border-start-start-radius: 0.1em;\n      border-end-start-radius: 0.1em;\n    }\n\n    .md-badge__text {\n      padding: 0.4em 0.8em;\n      border-start-end-radius: 0.1em;\n      border-end-end-radius: 0.1em;\n      box-shadow: 0 0 0 1px inset var(--md-accent-fg-color--transparent);\n    }\n  }\n\n  .md-badge--right {\n    float: right;\n    margin-left: 0.35em;\n  }\n}\n"
  },
  {
    "path": "docs/ebpf/users.md",
    "content": "# Projects built with {{ proj }}\n\nBelow is a non-comprehensive list of open-source software built with {{ proj }},\njust for inspiration or to gain a better understanding of how to tackle certain\nproblems using eBPF.\n\nA list of :fontawesome-brands-golang: {{ proj }} importers can be found on\n[Sourcegraph].\nIf you'd like to include a project on this page, feel free to open a pull request.\n\n[`Cilium`](https://github.com/cilium/cilium)\n\n:   Kubernetes-oriented Container Networking Interface implementation providing\n    network policy and observability.\n\n[`containerd`](https://github.com/containerd/cgroups) & [`runc`](https://github.com/opencontainers/runc)\n\n:   Used by Docker and podman, these use eBPF for implementing device filters\n    in cgroups.\n\n[`coroot`](https://github.com/coroot/coroot)\n\n:   Zero-instrumentation observability featuring root cause analysis and\n    anomaly detection.\n\n[`datadog-agent`](https://github.com/DataDog/datadog-agent)\n\n:   The Datadog agent, the component responsible for collecting system and\n    application metrics and shipping them to the Datadog platform.\n\n[`Delve`](https://github.com/go-delve/delve)\n\n:   A debugger for the Go programming language. Uses eBPF uprobes for tracing\n    user space code execution.\n\n[`gVisor`](https://github.com/google/gvisor)\n\n:   gVisor relies on eBPF for implementing various forms of guest/workload\n    isolation and security.\n\n[`Inspektor Gadget`](https://github.com/inspektor-gadget/inspektor-gadget)\n\n:   A collection of tools to debug and inspect Kubernetes resources and\n    applications. Reimplements many of the BCC tools for easy deployment onto a\n    Kubernetes cluster.\n\n[`Istio`](https://github.com/istio/istio)\n\n:   In Istio’s ambient mode, eBPF is used for redirecting application traffic to\n    the zero-trust tunnel on the node.\n\n[`KubeArmor`](https://github.com/kubearmor/KubeArmor)\n\n:   KubeArmor allows restricting the behaviour of Pods, containers and\n    Kubernetes nodes at the system level.\n\n[`kube-proxy-ng`](https://github.com/kubernetes-sigs/kpng)\n\n:   Emerging eBPF-based `kube-proxy` implementation, developed by the upstream\n    Kubernetes project.\n\n[`OpenShift`](https://github.com/openshift/ingress-node-firewall)\n\n:   OpenShift's ingress node firewall is implemented using eBPF.\n\n[`pwru`](https://github.com/cilium/pwru)\n\n:   Packet, where are you? `tcpdump`, but for tracing a packet's journey through\n    the kernel.\n\n[`Pyroscope`](https://github.com/grafana/pyroscope)\n\n:   From Grafana, open source continuous profiling platform. Flame graphs!\n\n[`Tetragon`](https://github.com/cilium/tetragon)\n\n:   eBPF-based security framework, also providing observability and runtime\n    enforcement.\n\n[`Tubular`](https://github.com/cloudflare/tubular)\n\n:   From Cloudflare, bind a service to any IP or port. See [the announcement\n    blog\n    post](https://blog.cloudflare.com/tubular-fixing-the-socket-api-with-ebpf/)\n    for a deep dive into why it was created and how it works.\n\n[Sourcegraph]: https://sourcegraph.com/search?q=context:global+lang:Go+type:file+github.com/cilium/ebpf+-repo:%5Egithub%5C.com/cilium/ebpf%24+-path:%5Evendor/+select:repo+&patternType=standard&sm=1&groupBy=repo\n"
  },
  {
    "path": "docs/examples/docs.c",
    "content": "//go:build ignore\n\n// DocMyMapProgram {\n#include <linux/bpf.h>\n#include <bpf/bpf_helpers.h>\n\n// Declare a hash map called 'my_map' with a u32 key and a u64 value.\n// The __uint, __type and SEC macros are from libbpf's bpf_helpers.h.\nstruct {\n\t__uint(type, BPF_MAP_TYPE_HASH);\n\t__type(key, __u32);\n\t__type(value, __u64);\n\t__uint(max_entries, 1);\n} my_map SEC(\".maps\");\n\n// Declare a dummy socket program called 'my_prog'.\nSEC(\"socket\") int my_prog() {\n\treturn 0;\n}\n// }\n"
  },
  {
    "path": "docs/examples/docs_test.go",
    "content": "//go:build linux\n\npackage examples\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\nfunc DocLoadCollectionSpec() {\n\t// Parse an ELF into a CollectionSpec.\n\t// bpf_prog.o is the result of compiling BPF C code.\n\tspec, err := ebpf.LoadCollectionSpec(\"bpf_prog.o\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Look up the MapSpec and ProgramSpec in the CollectionSpec.\n\tm := spec.Maps[\"my_map\"]\n\tp := spec.Programs[\"my_prog\"]\n\t// Note: We've omitted nil checks for brevity, take a look at\n\t// LoadAndAssign for an automated way of checking for maps/programs.\n\n\t// Inspect the map and program type.\n\tfmt.Println(m.Type, p.Type)\n\n\t// Print the map's key and value BTF types.\n\tfmt.Println(m.Key, m.Value)\n\n\t// Print the program's instructions in a human-readable form,\n\t// similar to llvm-objdump -S.\n\tfmt.Println(p.Instructions)\n}\n\nfunc DocNewCollection() {\n\tspec, err := ebpf.LoadCollectionSpec(\"bpf_prog.o\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Instantiate a Collection from a CollectionSpec.\n\tcoll, err := ebpf.NewCollection(spec)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\t// Close the Collection before the enclosing function returns.\n\tdefer coll.Close()\n\n\t// Obtain a reference to 'my_map'.\n\tm := coll.Maps[\"my_map\"]\n\n\t// Set map key '1' to value '2'.\n\tif err := m.Put(uint32(1), uint64(2)); err != nil {\n\t\tpanic(err)\n\t}\n}\n\n// DocLoadAndAssignObjs {\ntype myObjs struct {\n\tMyMap  *ebpf.Map     `ebpf:\"my_map\"`\n\tMyProg *ebpf.Program `ebpf:\"my_prog\"`\n}\n\nfunc (objs *myObjs) Close() error {\n\tif err := objs.MyMap.Close(); err != nil {\n\t\treturn err\n\t}\n\tif err := objs.MyProg.Close(); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// }\n\nfunc DocLoadAndAssign() {\n\tspec, err := ebpf.LoadCollectionSpec(\"bpf_prog.o\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Insert only the resources specified in 'obj' into the kernel and assign\n\t// them to their respective fields. If any requested resources are not found\n\t// in the ELF, this will fail. Any errors encountered while loading Maps or\n\t// Programs will also be returned here.\n\tvar objs myObjs\n\tif err := spec.LoadAndAssign(&objs, nil); err != nil {\n\t\tpanic(err)\n\t}\n\tdefer objs.Close()\n\n\t// Interact with MyMap through the custom struct.\n\tif err := objs.MyMap.Put(uint32(1), uint64(2)); err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc DocBTFTypeByName() {\n\tspec, err := ebpf.LoadCollectionSpec(\"bpf_prog.o\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Look up the __64 type declared in linux/bpf.h.\n\tt, err := spec.Types.AnyTypeByName(\"__u64\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Println(t)\n}\n"
  },
  {
    "path": "docs/examples/features_test.go",
    "content": "//go:build linux\n\npackage examples\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/features\"\n)\n\nfunc DocDetectXDP() {\n\terr := features.HaveProgramType(ebpf.XDP)\n\tif errors.Is(err, ebpf.ErrNotSupported) {\n\t\tfmt.Println(\"XDP program type is not supported\")\n\t\treturn\n\t}\n\tif err != nil {\n\t\t// Feature detection was inconclusive.\n\t\t//\n\t\t// Note: always log and investigate these errors! These can be caused\n\t\t// by a lack of permissions, verifier errors, etc. Unless stated\n\t\t// otherwise, probes are expected to be conclusive. Please file\n\t\t// an issue if this is not the case in your environment.\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(\"XDP program type is supported\")\n}\n"
  },
  {
    "path": "docs/examples/getting_started/counter.c",
    "content": "// getting_started_counter {\n// (1)!\n//go:build ignore\n\n#include <linux/bpf.h> // (2)!\n#include <bpf/bpf_helpers.h>\n\nstruct {\n\t__uint(type, BPF_MAP_TYPE_ARRAY); // (3)!\n\t__type(key, __u32);\n\t__type(value, __u64);\n\t__uint(max_entries, 1);\n} pkt_count SEC(\".maps\"); // (4)!\n\n// count_packets atomically increases a packet counter on every invocation.\nSEC(\"xdp\") // (5)!\nint count_packets() {\n\t__u32 key    = 0; // (6)!\n\t__u64 *count = bpf_map_lookup_elem(&pkt_count, &key); // (7)!\n\tif (count) { // (8)!\n\t\t__sync_fetch_and_add(count, 1); // (9)!\n\t}\n\n\treturn XDP_PASS; // (10)!\n}\n\nchar __license[] SEC(\"license\") = \"Dual MIT/GPL\"; // (11)!\n\n// }\n"
  },
  {
    "path": "docs/examples/getting_started/counter_bpfeb.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (mips || mips64 || ppc64 || s390x) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\n// loadCounter returns the embedded CollectionSpec for counter.\nfunc loadCounter() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_CounterBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load counter: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadCounterObjects loads counter and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*counterObjects\n//\t*counterPrograms\n//\t*counterMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadCounterObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadCounter()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// counterSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype counterSpecs struct {\n\tcounterProgramSpecs\n\tcounterMapSpecs\n\tcounterVariableSpecs\n}\n\n// counterProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype counterProgramSpecs struct {\n\tCountPackets *ebpf.ProgramSpec `ebpf:\"count_packets\"`\n}\n\n// counterMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype counterMapSpecs struct {\n\tPktCount *ebpf.MapSpec `ebpf:\"pkt_count\"`\n}\n\n// counterVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype counterVariableSpecs struct {\n}\n\n// counterObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadCounterObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype counterObjects struct {\n\tcounterPrograms\n\tcounterMaps\n\tcounterVariables\n}\n\nfunc (o *counterObjects) Close() error {\n\treturn _CounterClose(\n\t\t&o.counterPrograms,\n\t\t&o.counterMaps,\n\t)\n}\n\n// counterMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadCounterObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype counterMaps struct {\n\tPktCount *ebpf.Map `ebpf:\"pkt_count\"`\n}\n\nfunc (m *counterMaps) Close() error {\n\treturn _CounterClose(\n\t\tm.PktCount,\n\t)\n}\n\n// counterVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadCounterObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype counterVariables struct {\n}\n\n// counterPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadCounterObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype counterPrograms struct {\n\tCountPackets *ebpf.Program `ebpf:\"count_packets\"`\n}\n\nfunc (p *counterPrograms) Close() error {\n\treturn _CounterClose(\n\t\tp.CountPackets,\n\t)\n}\n\nfunc _CounterClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed counter_bpfeb.o\nvar _CounterBytes []byte\n"
  },
  {
    "path": "docs/examples/getting_started/counter_bpfel.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64 || wasm) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\n// loadCounter returns the embedded CollectionSpec for counter.\nfunc loadCounter() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_CounterBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load counter: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadCounterObjects loads counter and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*counterObjects\n//\t*counterPrograms\n//\t*counterMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadCounterObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadCounter()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// counterSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype counterSpecs struct {\n\tcounterProgramSpecs\n\tcounterMapSpecs\n\tcounterVariableSpecs\n}\n\n// counterProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype counterProgramSpecs struct {\n\tCountPackets *ebpf.ProgramSpec `ebpf:\"count_packets\"`\n}\n\n// counterMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype counterMapSpecs struct {\n\tPktCount *ebpf.MapSpec `ebpf:\"pkt_count\"`\n}\n\n// counterVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype counterVariableSpecs struct {\n}\n\n// counterObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadCounterObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype counterObjects struct {\n\tcounterPrograms\n\tcounterMaps\n\tcounterVariables\n}\n\nfunc (o *counterObjects) Close() error {\n\treturn _CounterClose(\n\t\t&o.counterPrograms,\n\t\t&o.counterMaps,\n\t)\n}\n\n// counterMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadCounterObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype counterMaps struct {\n\tPktCount *ebpf.Map `ebpf:\"pkt_count\"`\n}\n\nfunc (m *counterMaps) Close() error {\n\treturn _CounterClose(\n\t\tm.PktCount,\n\t)\n}\n\n// counterVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadCounterObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype counterVariables struct {\n}\n\n// counterPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadCounterObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype counterPrograms struct {\n\tCountPackets *ebpf.Program `ebpf:\"count_packets\"`\n}\n\nfunc (p *counterPrograms) Close() error {\n\treturn _CounterClose(\n\t\tp.CountPackets,\n\t)\n}\n\nfunc _CounterClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed counter_bpfel.o\nvar _CounterBytes []byte\n"
  },
  {
    "path": "docs/examples/getting_started/gen.go",
    "content": "//go:build linux\n\n// getting_started_gen {\npackage main\n\n//go:generate go tool bpf2go -tags linux counter counter.c\n\n// }\n"
  },
  {
    "path": "docs/examples/getting_started/main.go",
    "content": "//go:build linux\n\n// getting_started_main {\npackage main\n\nimport (\n\t\"log\"\n\t\"net\"\n\t\"os\"\n\t\"os/signal\"\n\t\"time\"\n\n\t\"github.com/cilium/ebpf/link\"\n\t\"github.com/cilium/ebpf/rlimit\"\n)\n\nfunc main() {\n\t// Remove resource limits for kernels <5.11.\n\tif err := rlimit.RemoveMemlock(); err != nil { // (1)!\n\t\tlog.Fatal(\"Removing memlock:\", err)\n\t}\n\n\t// Load the compiled eBPF ELF and load it into the kernel.\n\tvar objs counterObjects // (2)!\n\tif err := loadCounterObjects(&objs, nil); err != nil {\n\t\tlog.Fatal(\"Loading eBPF objects:\", err)\n\t}\n\tdefer objs.Close() // (3)!\n\n\tifname := \"eth0\" // Change this to an interface on your machine.\n\tiface, err := net.InterfaceByName(ifname)\n\tif err != nil {\n\t\tlog.Fatalf(\"Getting interface %s: %s\", ifname, err)\n\t}\n\n\t// Attach count_packets to the network interface.\n\tlink, err := link.AttachXDP(link.XDPOptions{ // (4)!\n\t\tProgram:   objs.CountPackets,\n\t\tInterface: iface.Index,\n\t})\n\tif err != nil {\n\t\tlog.Fatal(\"Attaching XDP:\", err)\n\t}\n\tdefer link.Close() // (5)!\n\n\tlog.Printf(\"Counting incoming packets on %s..\", ifname)\n\n\t// Periodically fetch the packet counter from PktCount,\n\t// exit the program when interrupted.\n\ttick := time.Tick(time.Second)\n\tstop := make(chan os.Signal, 5)\n\tsignal.Notify(stop, os.Interrupt)\n\tfor {\n\t\tselect {\n\t\tcase <-tick:\n\t\t\tvar count uint64\n\t\t\terr := objs.PktCount.Lookup(uint32(0), &count) // (6)!\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal(\"Map lookup:\", err)\n\t\t\t}\n\t\t\tlog.Printf(\"Received %d packets\", count)\n\t\tcase <-stop:\n\t\t\tlog.Print(\"Received signal, exiting..\")\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// }\n"
  },
  {
    "path": "docs/examples/rlimit_test.go",
    "content": "//go:build linux\n\npackage examples\n\n// DocRlimit {\nimport \"github.com/cilium/ebpf/rlimit\"\n\nfunc init() {\n\tif err := rlimit.RemoveMemlock(); err != nil {\n\t\tpanic(err)\n\t}\n}\n\n// }\n"
  },
  {
    "path": "docs/examples/variables/gen.go",
    "content": "package main\n\n//go:generate go tool bpf2go variables variables.c\n"
  },
  {
    "path": "docs/examples/variables/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\nfunc main() {\n\tDocVariablesSetConst()\n\tDocVariablesSetGlobal()\n}\n\n// Full example written to be displayed in its entirety, so is commented generously.\nfunc DocVariablesSetConst() {\n\t// Load the object file from disk using a bpf2go-generated scaffolding.\n\tspec, err := loadVariables()\n\tif err != nil {\n\t\tpanicf(\"loading CollectionSpec: %s\", err)\n\t}\n\n\t// Set the 'const_u32' variable to 42 in the CollectionSpec.\n\twant := uint32(42) // (1)!\n\tif err := spec.Variables[\"const_u32\"].Set(want); err != nil {\n\t\tpanicf(\"setting variable: %s\", err)\n\t}\n\n\t// Load the CollectionSpec.\n\t//\n\t// Note: modifying spec.Variables after this point is ineffectual!\n\t// Modifying *Spec resources does not affect loaded/running BPF programs.\n\tvar obj variablesPrograms\n\tif err := spec.LoadAndAssign(&obj, nil); err != nil {\n\t\tpanicf(\"loading BPF program: %s\", err)\n\t}\n\n\tfmt.Println(\"Running program with const_u32 set to\", want)\n\n\t// Dry-run the BPF program with an empty context.\n\tret, _, err := obj.ConstExample.Test(make([]byte, 15)) // (2)!\n\tif err != nil {\n\t\tpanicf(\"running BPF program: %s\", err)\n\t}\n\n\tif ret != want {\n\t\tpanicf(\"unexpected return value %d\", ret)\n\t}\n\n\tfmt.Println(\"BPF program returned\", ret)\n\n\t// Output:\n\t// Running program with const_u32 set to 42\n\t// BPF program returned 42\n}\n\nfunc DocVariablesSetGlobal() {\n\tspec, err := loadVariables()\n\tif err != nil {\n\t\tpanicf(\"loading CollectionSpec: %s\", err)\n\t}\n\n\t// DocVariablesSetGlobalU16 {\n\tset := uint16(9000)\n\tif err := spec.Variables[\"global_u16\"].Set(set); err != nil {\n\t\tpanicf(\"setting variable: %s\", err)\n\t}\n\t// }\n\n\tcoll, err := ebpf.NewCollection(spec)\n\tif err != nil {\n\t\tpanicf(\"loading BPF program: %s\", err)\n\t}\n\n\tfmt.Println(\"Running program with global_u16 set to\", set)\n\n\t// DocVariablesSetGlobalRun {\n\tfor range 3 {\n\t\tret, _, err := coll.Programs[\"global_example\"].Test(make([]byte, 15))\n\t\tif err != nil {\n\t\t\tpanicf(\"running BPF program: %s\", err)\n\t\t}\n\t\tfmt.Println(\"BPF program returned\", ret)\n\t}\n\n\t// Output:\n\t// Running program with global_u16 set to 9000\n\t// BPF program returned 9000\n\t// BPF program returned 9001\n\t// BPF program returned 9002\n\t// }\n\n\t// DocVariablesGetGlobalU16 {\n\tvar global_u16 uint16\n\tif err := coll.Variables[\"global_u16\"].Get(&global_u16); err != nil {\n\t\tpanicf(\"getting variable: %s\", err)\n\t}\n\tfmt.Println(\"Variable global_u16 is now\", global_u16)\n\n\t// Output:\n\t// Variable global_u16 is now 9003\n\t// }\n}\n\nfunc panicf(format string, args ...interface{}) {\n\tpanic(fmt.Sprintf(format, args...))\n}\n"
  },
  {
    "path": "docs/examples/variables/variables.c",
    "content": "//go:build ignore\n\n#include <linux/bpf.h>\n#include <bpf/bpf_helpers.h>\n\n// Remove when toolchain Docker image ships with 5.13+ headers.\n#define __hidden __attribute__((visibility(\"hidden\")))\n\n// variables_const {\nvolatile const __u32 const_u32;\n\nSEC(\"socket\") int const_example() {\n\treturn const_u32;\n}\n// }\n\n// variables_global {\nvolatile __u16 global_u16;\n\nSEC(\"socket\") int global_example() {\n\tglobal_u16++;\n\treturn global_u16;\n}\n// }\n\n// variables_hidden {\n__hidden __u64 hidden_var;\n\nSEC(\"socket\") int hidden_example() {\n\thidden_var++;\n\treturn hidden_var;\n}\n// }\n"
  },
  {
    "path": "docs/examples/variables/variables_bpfeb.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build mips || mips64 || ppc64 || s390x\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\n// loadVariables returns the embedded CollectionSpec for variables.\nfunc loadVariables() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_VariablesBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load variables: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadVariablesObjects loads variables and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*variablesObjects\n//\t*variablesPrograms\n//\t*variablesMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadVariablesObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadVariables()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// variablesSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype variablesSpecs struct {\n\tvariablesProgramSpecs\n\tvariablesMapSpecs\n\tvariablesVariableSpecs\n}\n\n// variablesProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype variablesProgramSpecs struct {\n\tConstExample  *ebpf.ProgramSpec `ebpf:\"const_example\"`\n\tGlobalExample *ebpf.ProgramSpec `ebpf:\"global_example\"`\n\tHiddenExample *ebpf.ProgramSpec `ebpf:\"hidden_example\"`\n}\n\n// variablesMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype variablesMapSpecs struct {\n}\n\n// variablesVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype variablesVariableSpecs struct {\n\tConstU32  *ebpf.VariableSpec `ebpf:\"const_u32\"`\n\tGlobalU16 *ebpf.VariableSpec `ebpf:\"global_u16\"`\n}\n\n// variablesObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadVariablesObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype variablesObjects struct {\n\tvariablesPrograms\n\tvariablesMaps\n\tvariablesVariables\n}\n\nfunc (o *variablesObjects) Close() error {\n\treturn _VariablesClose(\n\t\t&o.variablesPrograms,\n\t\t&o.variablesMaps,\n\t)\n}\n\n// variablesMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadVariablesObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype variablesMaps struct {\n}\n\nfunc (m *variablesMaps) Close() error {\n\treturn _VariablesClose()\n}\n\n// variablesVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadVariablesObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype variablesVariables struct {\n\tConstU32  *ebpf.Variable `ebpf:\"const_u32\"`\n\tGlobalU16 *ebpf.Variable `ebpf:\"global_u16\"`\n}\n\n// variablesPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadVariablesObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype variablesPrograms struct {\n\tConstExample  *ebpf.Program `ebpf:\"const_example\"`\n\tGlobalExample *ebpf.Program `ebpf:\"global_example\"`\n\tHiddenExample *ebpf.Program `ebpf:\"hidden_example\"`\n}\n\nfunc (p *variablesPrograms) Close() error {\n\treturn _VariablesClose(\n\t\tp.ConstExample,\n\t\tp.GlobalExample,\n\t\tp.HiddenExample,\n\t)\n}\n\nfunc _VariablesClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed variables_bpfeb.o\nvar _VariablesBytes []byte\n"
  },
  {
    "path": "docs/examples/variables/variables_bpfel.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build 386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64 || wasm\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\n// loadVariables returns the embedded CollectionSpec for variables.\nfunc loadVariables() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_VariablesBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load variables: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadVariablesObjects loads variables and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*variablesObjects\n//\t*variablesPrograms\n//\t*variablesMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadVariablesObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadVariables()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// variablesSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype variablesSpecs struct {\n\tvariablesProgramSpecs\n\tvariablesMapSpecs\n\tvariablesVariableSpecs\n}\n\n// variablesProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype variablesProgramSpecs struct {\n\tConstExample  *ebpf.ProgramSpec `ebpf:\"const_example\"`\n\tGlobalExample *ebpf.ProgramSpec `ebpf:\"global_example\"`\n\tHiddenExample *ebpf.ProgramSpec `ebpf:\"hidden_example\"`\n}\n\n// variablesMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype variablesMapSpecs struct {\n}\n\n// variablesVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype variablesVariableSpecs struct {\n\tConstU32  *ebpf.VariableSpec `ebpf:\"const_u32\"`\n\tGlobalU16 *ebpf.VariableSpec `ebpf:\"global_u16\"`\n}\n\n// variablesObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadVariablesObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype variablesObjects struct {\n\tvariablesPrograms\n\tvariablesMaps\n\tvariablesVariables\n}\n\nfunc (o *variablesObjects) Close() error {\n\treturn _VariablesClose(\n\t\t&o.variablesPrograms,\n\t\t&o.variablesMaps,\n\t)\n}\n\n// variablesMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadVariablesObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype variablesMaps struct {\n}\n\nfunc (m *variablesMaps) Close() error {\n\treturn _VariablesClose()\n}\n\n// variablesVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadVariablesObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype variablesVariables struct {\n\tConstU32  *ebpf.Variable `ebpf:\"const_u32\"`\n\tGlobalU16 *ebpf.Variable `ebpf:\"global_u16\"`\n}\n\n// variablesPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadVariablesObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype variablesPrograms struct {\n\tConstExample  *ebpf.Program `ebpf:\"const_example\"`\n\tGlobalExample *ebpf.Program `ebpf:\"global_example\"`\n\tHiddenExample *ebpf.Program `ebpf:\"hidden_example\"`\n}\n\nfunc (p *variablesPrograms) Close() error {\n\treturn _VariablesClose(\n\t\tp.ConstExample,\n\t\tp.GlobalExample,\n\t\tp.HiddenExample,\n\t)\n}\n\nfunc _VariablesClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed variables_bpfel.o\nvar _VariablesBytes []byte\n"
  },
  {
    "path": "docs/includes/glossary.md",
    "content": "<!-- This snippet is automatically included on every page and takes care of automatically highlighting terminology. -->\n*[Program]: Instructions that can be loaded and attached to one or more hooks in the Linux kernel.\n*[Map]: Shared piece of memory between userspace and an eBPF program loaded into the kernel.\n*[Link]: Connection between a Program and a hook/event in the kernel.\n*[BTF]: BPF Type Format; a description of all data types present in the Linux kernel an eBPF object.\n*[ELF]: Executable and Linkable Format, a container format used for compiled eBPF programs.\n*[Spec]: Unrealized blueprint of an eBPF resource, e.g. MapSpec, ProgramSpec, btf.Spec.\n*[CollectionSpec]: Bundle of ProgramSpecs, MapSpecs and a btf.Spec. Direct result of loading an eBPF ELF.\n*[VariableSpec]: Accessor for a global variable declared in an eBPF program.\n*[Collection]: Bundle of Maps and Programs that were loaded into the kernel. Direct result of instantiating (loading into the kernel) a CollectionSpec.\n*[Variable]: Accessor for a global variable declared in an eBPF program, used after loading.\n*[bpffs]: Birtual filesystem for 'pinning' references to eBPF resources in an familiar file hierarchy. Usually mounted at /sys/fs/bpf, but many individual instances can be mounted.\n*[helper]: A piece of logic provided by the kernel. Read a map value, redirect a packet, etc.\n*[kfunc]: An extensible evolution of the BPF helper mechanism. Can be dynamically provided by kernel modules. Not specified in UAPI.\n*[XDP]: eXpress Data Path, a high-performance eBPF-powered data path. Only has a receive hook.\n*[bpf2go]: Convenience utility to compile eBPF C using clang and generate a Go skeleton.\n*[libbpf]: A library for writing kernel- and user space BPF programs in C, developed by the upstream Linux project.\n*[qemu]: A popular virtual machine manager.\n*[DCO]: Developer Certificate of Origin.\n*[efW]: eBPF for Windows\n"
  },
  {
    "path": "docs/macros.py",
    "content": "\"\"\"Macro definitions for documentation.\"\"\"\n\n# Use built-in 'list' type when upgrading to Python 3.9.\n\nimport glob\nimport os\nimport re\nimport textwrap\nfrom io import TextIOWrapper\nfrom typing import List\nfrom urllib.parse import ParseResult, urlparse\n\nfrom mkdocs_macros.plugin import MacrosPlugin\n\n\ndef define_env(env: MacrosPlugin):\n    \"\"\"\n    Define the mkdocs-macros-plugin environment.\n\n    This function is called on setup. 'env' can be interacted with\n    for defining variables, macros and filters.\n\n    - variables: the dictionary that contains the environment variables\n    - macro: a decorator function, to declare a macro.\n    - filter: a function with one or more arguments, used to perform a\n    transformation\n    \"\"\"\n    # Values can be overridden in mkdocs.yml:extras.\n    go_examples_path: str = env.variables.get(\n        \"go_examples_path\", \"examples/**/*.go\"\n    )\n    godoc_url: ParseResult = urlparse(\n        env.variables.get(\n            \"godoc_url\", \"https://pkg.go.dev/github.com/cilium/ebpf\"\n        )\n    )\n\n    c_examples_path: str = env.variables.get(\"c_examples_path\", \"examples/**/*.c\")\n\n    @env.macro\n    def godoc(sym: str, short: bool = False):\n        \"\"\"\n        Generate a godoc link based on the configured godoc_url.\n\n        `sym` is the symbol to link to. A dot '.' separator means it's a method\n        on another type. Forward slashes '/' can be used to navigate to symbols\n        in subpackages.\n\n        For example:\n        - CollectionSpec.LoadAndAssign\n        - link/Link\n        - btf/Spec.TypeByID\n\n        `short` renders only the symbol name.\n        \"\"\"\n        if len(godoc_url) == 0:\n            raise ValueError(\"Empty godoc url\")\n\n        # Support referring to symbols in subpackages.\n        subpkg = os.path.dirname(sym)\n        # Symbol name including dots for struct methods. (e.g. Map.Get)\n        name = os.path.basename(sym)\n\n        # Python's urljoin() expects the base path to have a trailing slash for\n        # it to correctly append subdirs. Use urlparse instead, and interact\n        # with the URL's components individually.\n        url = godoc_url._replace(\n            path=os.path.join(godoc_url.path, subpkg),\n            # Anchor token appearing after the # in the URL.\n            fragment=name,\n        ).geturl()\n\n        text = name\n        if short:\n            text = text.split(\".\")[-1]\n\n        return f\"[:fontawesome-brands-golang: `{text}`]({url})\"\n\n    @env.macro\n    def go_example(*args, **kwargs):\n        \"\"\"\n        Include the body of a Go code example.\n\n        See docstring of code_example() for details.\n        \"\"\"\n        return code_example(\n            *args, **kwargs, language=\"go\", path=go_examples_path\n        )\n\n    @env.macro\n    def c_example(*args, **kwargs):\n        \"\"\"\n        Include the body of a C code example.\n\n        See docstring of `code_example` for details.\n        \"\"\"\n        return code_example(\n            *args, **kwargs, language=\"c\", path=c_examples_path\n        )\n\n    @env.macro\n    def linux_version(version: str, why: str = ''):\n        \"\"\"\n        Render a badge with the Linux logo and a version number denoting the\n        minimum kernel version needed to use a feature. Optional string to\n        explain why the feature won't work on older versions.\n        \"\"\"\n        return ('<span class=\"md-badge md-badge--right\">'\n                # TODO: Make the icon link to some docs about handling kernel\n                # versions, once those are written.\n                '<span class=\"md-badge__icon\">:simple-linux:</span>'\n                f'<span class=\"md-badge__text\">[{version}](# \"{why}\")</span>'\n                '</span>')\n\ndef code_example(\n    symbol: str,\n    title: str = None,\n    language: str = \"\",\n    lines: bool = True,\n    signature: bool = False,\n    path: str = \"\",\n) -> str:\n    \"\"\"\n    Include the body of a code example.\n\n    `symbol` takes the name of the function or snippet to include.\n    `title` is rendered as a title at the top of the snippet.\n    `language` is the name of the programming language passed to pygments.\n    `lines` controls rendering line numbers.\n    `signature` controls whether or not the function signature and brackets are\n        included.\n    `path` specifies the include path that may contain globs.\n    \"\"\"\n    opts: List[str] = []\n    if lines:\n        opts.append(\"linenums='1'\")\n    if title:\n        opts.append(f\"title='{title}'\")\n\n    if signature:\n        body = full_body(path, symbol)\n    else:\n        body = inner_body(path, symbol)\n\n    out = f\"``` {language} {' '. join(opts)}\\n{body}```\"\n\n    return out\n\n\ndef inner_body(path: str, sym: str) -> str:\n    \"\"\"\n    Get the inner body of sym, using default delimiters.\n\n    First and last lines (so, function signature and closing bracket) are\n    stripped, the remaining body dedented.\n    \"\"\"\n    out = _search_body(path, sym)\n    if len(out) < 2:\n        raise ValueError(\n            f\"Need at least two lines to get inner body for symbol {sym}\"\n        )\n\n    return textwrap.dedent(\"\".join(out[1:-1]))\n\n\ndef full_body(path: str, sym: str) -> str:\n    \"\"\"Get the full body of sym, using default delimiters, dedented.\"\"\"\n    out = _search_body(path, sym)\n\n    return textwrap.dedent(\"\".join(out))\n\n\ndef _get_body(\n    f: TextIOWrapper, sym: str, start: str = \"{\", end: str = \"}\"\n) -> List[str]:\n    \"\"\"\n    Extract a body of text between sym and start/end delimiters.\n\n    Tailored to finding function bodies of C-family programming languages with\n    curly braces.\n\n    The starting line of the body must contain sym prefixed by a space, with\n    'start' appearing on the same line, for example \" Foo() {\". Further\n    occurrences of \"{\" and its closing counterpart \"}\" are tracked, and the\n    lines between and including the final \"}\" are returned.\n    \"\"\"\n    found = False\n    stack = 0\n    lines = []\n\n    for line in f.readlines():\n        if not found:\n            # Skip current line if we're not in a body and the current line\n            # doesn't contain the given symbol.\n            # \n            # The symbol must be surrounded by non-word characters like spaces\n            # or parentheses. For example, a line \"// DocObjs {\" or \"func\n            # DocLoader() {\" should match.\n            if re.search(rf\"\\W{sym}\\W\", line) is None:\n                continue\n\n            found = True\n\n        # Count the amount of start delimiters.\n        stack += line.count(start)\n\n        if stack == 0:\n            # No opening delimiter found, ignore the line.\n            found = False\n            continue\n\n        lines.append(line)\n\n        # Count the amount of end delimiters and stop if we've escaped the\n        # current scope.\n        stack -= line.count(end)\n        if stack <= 0:\n            break\n\n    # Rewind the file for reuse.\n    f.seek(0)\n\n    if stack > 0:\n        raise LookupError(f\"No end delimiter for {sym}\")\n\n    if len(lines) == 0:\n        raise LookupError(f\"Symbol {sym} not found\")\n\n    return lines\n\n\ndef _search_body(path: str, sym: str) -> List[str]:\n    \"\"\"Find the body of the given symbol in a path glob.\"\"\"\n    files = glob.glob(path, recursive=True)\n    if len(files) == 0:\n        raise LookupError(f\"Path {path} did not match any files\")\n\n    for file in files:\n        with open(file, mode=\"r\") as f:\n            try:\n                return _get_body(f, sym)\n            except LookupError:\n                continue\n\n    raise LookupError(f\"Symbol {sym} not found in any of {files}\")\n"
  },
  {
    "path": "docs/mkdocs.yml",
    "content": "site_name: \"ebpf-go Documentation\"\nsite_description: Pure-Go library to read, modify and load eBPF programs and attach them to various hooks in the Linux kernel.\nsite_author: Cilium Community\n\n# Rendered in header.\nrepo_url: https://github.com/cilium/ebpf\nrepo_name: cilium/ebpf\nedit_uri: edit/main/docs/ebpf/\n\n# Directory to look for Markdown files within docs/.\ndocs_dir: ebpf\n\ntheme:\n  logo: ebpf-go.png\n  favicon: ebpf-go.png\n  name: material\n\n  icon:\n    # GitHub link in the header.\n    repo: fontawesome/brands/github-alt\n    # Edit button at the top of each page.\n    edit: material/pencil-ruler\n\n  features:\n    # Display sections in the navbar.\n    - navigation.sections\n    # Anchor tracking, updates the address bar with the active anchor.\n    - navigation.tracking\n    # Use XHR instead of fully reloading the page when navigating around.\n    - nagivation.instant\n    # Clipboard button in code blocks.\n    - content.code.copy\n    # Enable annotations in code blocks.\n    - content.code.annotate\n    # Button to edit page on GitHub.\n    - content.action.edit\n    # Better (faster) tooltips, replacing the browser's rendering logic.\n    - content.tooltips\n\n  palette:\n    # Palette toggle for light mode\n    - media: \"(prefers-color-scheme: light)\"\n      scheme: default\n      toggle:\n        icon: material/lightbulb-off\n        name: Switch to dark mode\n\n    # Palette toggle for dark mode\n    - media: \"(prefers-color-scheme: dark)\"\n      scheme: slate\n      toggle:\n        icon: material/lightbulb-on\n        name: Switch to light mode\n\n  # Template overrides.\n  custom_dir: overrides\n\nnav:\n  - 'Home': index.md\n  - 'Guides':\n    - 'Getting Started': guides/getting-started.md\n    - 'Portable eBPF': guides/portable-ebpf.md\n    - 'Windows support': guides/windows-support.md\n  - 'Concepts':\n    - 'Loading eBPF Programs': concepts/loader.md\n    - 'Global Variables': concepts/global-variables.md\n    - 'Resource Limits': concepts/rlimit.md\n    - 'Section Naming': concepts/section-naming.md\n    - 'Feature Detection': concepts/features.md\n    - 'Object Lifecycle': concepts/object-lifecycle.md\n  - 'Contributing':\n    - contributing/index.md\n    - contributing/architecture.md\n    - contributing/new-feature.md\n    - contributing/new-example.md\n    - contributing/windows.md\n  - 'Users': users.md\n  - 'Go Reference': https://pkg.go.dev/github.com/cilium/ebpf\n  - 'GitHub':\n    - 'Repository': https://github.com/cilium/ebpf\n    - 'Issue Tracker': https://github.com/cilium/ebpf/issues\n    - 'Discussions': https://github.com/cilium/ebpf/discussions\n  - 'About': about.md\n\nextra:\n  social:\n    - icon: fontawesome/brands/github\n      link: https://github.com/cilium/ebpf\n\nextra_css:\n  - stylesheets/extra.css\n\nwatch:\n- examples/\n- includes/\n- overrides/\n- macros.py\n\nplugins:\n  - search\n  - macros:\n      # This opens macros.py in docs/.\n      module_name: macros\n      # Make the mkdocs build fail if any errors occur.\n      # Otherwise, any errors would be rendered to the build output.\n      on_error_fail: true\n      include_yaml:\n        - vars.yml\n  # Updated/authors displayed in footer.\n  # Layout is customized in overrides/partials/source-file.html.\n  - git-revision-date-localized:\n      type: timeago\n  - git-authors:\n      show_email_address: false\n      authorship_threshold_percent: 10\n      exclude:\n        - index.md\n\n# Enable syntax highlighting in mkdocs-material.\nmarkdown_extensions:\n  # Automatic tooltips for abbreviations/glossary.\n  - abbr\n  # Setting attributes on code fences (e.g. ``` go linenums='1')\n  - attr_list\n  # Special content blocks like '!!! note'\n  - admonition\n  # Definition lists using indented descriptions\n  - def_list\n  - footnotes\n  # Collapsible admonitions\n  - pymdownx.details\n  # Syntax highlighting in code blocks\n  - pymdownx.highlight:\n      anchor_linenums: true\n  - pymdownx.inlinehilite\n  # Glossary\n  - pymdownx.snippets:\n      auto_append:\n        - includes/glossary.md\n  # Superfences enables nested and tabbed code blocks and Mermaid support\n  - pymdownx.superfences:\n      custom_fences:\n        - name: mermaid\n          class: mermaid\n          format: !!python/name:pymdownx.superfences.fence_code_format\n  # Content tabs for code snippets, checklists, etc.\n  - pymdownx.tabbed:\n      alternate_style: true\n  # Emoji and icons like :fontawesome-brands-golang:\n  - pymdownx.emoji:\n      emoji_index: !!python/name:material.extensions.emoji.twemoji\n      emoji_generator: !!python/name:material.extensions.emoji.to_svg\n      options:\n        custom_icons:\n          - overrides/.icons\n  # Table of Contents\n  - toc:\n      permalink: true\n"
  },
  {
    "path": "docs/overrides/partials/source-file.html",
    "content": "<hr>\n<div class=\"md-source-file\">\n  <small>\n    {% if page.meta.git_revision_date_localized %}\n    Last updated {{ page.meta.git_revision_date_localized }}\n    {% endif %}\n    <br />\n    {% if git_page_authors %}\n    Authored by {{ git_page_authors }}\n    {% endif %}\n  </small>\n</div>\n"
  },
  {
    "path": "docs/vars.yml",
    "content": "# Variables accessible in documentation using e.g. '{{ proj }}'.\nextra:\n  proj: \"`ebpf-go`\"\n"
  },
  {
    "path": "elf_reader.go",
    "content": "package ebpf\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"debug/elf\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"iter\"\n\t\"maps\"\n\t\"math\"\n\t\"os\"\n\t\"slices\"\n\t\"strings\"\n\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/platform\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n)\n\ntype kconfigMetaKey struct{}\n\ntype kconfigMeta struct {\n\tMap    *MapSpec\n\tOffset uint32\n}\n\ntype kfuncMetaKey struct{}\n\ntype kfuncMeta struct {\n\tBinding elf.SymBind\n\tFunc    *btf.Func\n}\n\ntype ksymMetaKey struct{}\n\ntype ksymMeta struct {\n\tBinding elf.SymBind\n\tName    string\n}\n\n// elfCode is a convenience to reduce the amount of arguments that have to\n// be passed around explicitly. You should treat its contents as immutable.\ntype elfCode struct {\n\t*internal.SafeELFFile\n\tsections map[elf.SectionIndex]*elfSection\n\tlicense  string\n\tversion  uint32\n\tbtf      *btf.Spec\n\textInfo  *btf.ExtInfos\n\tmaps     map[string]*MapSpec\n\tvars     map[string]*VariableSpec\n\tkfuncs   map[string]*btf.Func\n\tksyms    map[string]struct{}\n\tkconfig  *MapSpec\n}\n\n// LoadCollectionSpec parses an ELF file into a CollectionSpec.\nfunc LoadCollectionSpec(file string) (*CollectionSpec, error) {\n\tf, err := os.Open(file)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer f.Close()\n\n\tspec, err := LoadCollectionSpecFromReader(f)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"file %s: %w\", file, err)\n\t}\n\treturn spec, nil\n}\n\n// LoadCollectionSpecFromReader parses an ELF file into a CollectionSpec.\nfunc LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) {\n\tf, err := internal.NewSafeELFFile(rd)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Checks if the ELF file is for BPF data.\n\t// Old LLVM versions set e_machine to EM_NONE.\n\tif f.Machine != elf.EM_NONE && f.Machine != elf.EM_BPF {\n\t\treturn nil, fmt.Errorf(\"unexpected machine type for BPF ELF: %s\", f.Machine)\n\t}\n\n\tvar (\n\t\tlicenseSection *elf.Section\n\t\tversionSection *elf.Section\n\t\tsections       = make(map[elf.SectionIndex]*elfSection)\n\t\trelSections    = make(map[elf.SectionIndex]*elf.Section)\n\t)\n\n\t// This is the target of relocations generated by inline assembly.\n\tsections[elf.SHN_UNDEF] = newElfSection(new(elf.Section), undefSection)\n\n\t// Collect all the sections we're interested in. This includes relocations\n\t// which we parse later.\n\t//\n\t// Keep the documentation at docs/ebpf/loading/elf-sections.md up-to-date.\n\tfor i, sec := range f.Sections {\n\t\tidx := elf.SectionIndex(i)\n\n\t\tswitch {\n\t\tcase strings.HasPrefix(sec.Name, \"license\"):\n\t\t\tlicenseSection = sec\n\t\tcase strings.HasPrefix(sec.Name, \"version\"):\n\t\t\tversionSection = sec\n\t\tcase strings.HasPrefix(sec.Name, \"maps\"):\n\t\t\tsections[idx] = newElfSection(sec, mapSection)\n\t\tcase sec.Name == \".maps\":\n\t\t\tsections[idx] = newElfSection(sec, btfMapSection)\n\t\tcase isDataSection(sec.Name):\n\t\t\tsections[idx] = newElfSection(sec, dataSection)\n\t\tcase sec.Type == elf.SHT_REL:\n\t\t\t// Store relocations under the section index of the target\n\t\t\trelSections[elf.SectionIndex(sec.Info)] = sec\n\t\tcase sec.Type == elf.SHT_PROGBITS && sec.Size > 0:\n\t\t\tif (sec.Flags&elf.SHF_EXECINSTR) != 0 && sec.Size > 0 {\n\t\t\t\tsections[idx] = newElfSection(sec, programSection)\n\t\t\t} else if sec.Name == structOpsLinkSec {\n\t\t\t\t// classification based on sec names so that struct_ops-specific\n\t\t\t\t// sections (.struct_ops.link) is correctly recognized\n\t\t\t\t// as non-executable PROGBITS, allowing value placement and link metadata to be loaded.\n\t\t\t\tsections[idx] = newElfSection(sec, structOpsSection)\n\t\t\t} else if sec.Name == structOpsSec {\n\t\t\t\treturn nil, fmt.Errorf(\"section %q: got '.struct_ops' section: %w\", sec.Name, ErrNotSupported)\n\t\t\t}\n\t\t}\n\t}\n\n\tlicense, err := loadLicense(licenseSection)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"load license: %w\", err)\n\t}\n\n\tversion, err := loadVersion(versionSection, f.ByteOrder)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"load version: %w\", err)\n\t}\n\n\tbtfSpec, btfExtInfo, err := btf.LoadSpecAndExtInfosFromReader(rd)\n\tif err != nil && !errors.Is(err, btf.ErrNotFound) {\n\t\treturn nil, fmt.Errorf(\"load BTF: %w\", err)\n\t}\n\n\tec := &elfCode{\n\t\tSafeELFFile: f,\n\t\tsections:    sections,\n\t\tlicense:     license,\n\t\tversion:     version,\n\t\tbtf:         btfSpec,\n\t\textInfo:     btfExtInfo,\n\t\tmaps:        make(map[string]*MapSpec),\n\t\tvars:        make(map[string]*VariableSpec),\n\t\tkfuncs:      make(map[string]*btf.Func),\n\t\tksyms:       make(map[string]struct{}),\n\t}\n\n\tsymbols, err := f.Symbols()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"load symbols: %v\", err)\n\t}\n\n\tec.assignSymbols(symbols)\n\n\tif err := ec.loadRelocations(relSections, symbols); err != nil {\n\t\treturn nil, fmt.Errorf(\"load relocations: %w\", err)\n\t}\n\n\tif err := ec.loadMaps(); err != nil {\n\t\treturn nil, fmt.Errorf(\"load maps: %w\", err)\n\t}\n\n\tif err := ec.loadBTFMaps(); err != nil {\n\t\treturn nil, fmt.Errorf(\"load BTF maps: %w\", err)\n\t}\n\n\tif err := ec.loadDataSections(); err != nil {\n\t\treturn nil, fmt.Errorf(\"load data sections: %w\", err)\n\t}\n\n\tif err := ec.loadKconfigSection(); err != nil {\n\t\treturn nil, fmt.Errorf(\"load virtual .kconfig section: %w\", err)\n\t}\n\n\tif err := ec.loadKsymsSection(); err != nil {\n\t\treturn nil, fmt.Errorf(\"load virtual .ksyms section: %w\", err)\n\t}\n\n\t// Finally, collect programs and link them.\n\tprogs, err := ec.loadProgramSections()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"load programs: %w\", err)\n\t}\n\n\t// assiociate members in structs with ProgramSpecs using relo\n\tif err := ec.associateStructOpsRelocs(progs); err != nil {\n\t\treturn nil, fmt.Errorf(\"load struct_ops: %w\", err)\n\t}\n\n\treturn &CollectionSpec{\n\t\tec.maps,\n\t\tprogs,\n\t\tec.vars,\n\t\tbtfSpec,\n\t\tec.ByteOrder,\n\t}, nil\n}\n\nfunc loadLicense(sec *elf.Section) (string, error) {\n\tif sec == nil {\n\t\treturn \"\", nil\n\t}\n\n\tdata, err := sec.Data()\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"section %s: %v\", sec.Name, err)\n\t}\n\treturn string(bytes.TrimRight(data, \"\\000\")), nil\n}\n\nfunc loadVersion(sec *elf.Section, bo binary.ByteOrder) (uint32, error) {\n\tif sec == nil {\n\t\treturn 0, nil\n\t}\n\n\tvar version uint32\n\tif err := binary.Read(sec.Open(), bo, &version); err != nil {\n\t\treturn 0, fmt.Errorf(\"section %s: %v\", sec.Name, err)\n\t}\n\treturn version, nil\n}\n\nfunc isDataSection(name string) bool {\n\treturn name == \".bss\" || strings.HasPrefix(name, \".data\") || strings.HasPrefix(name, \".rodata\")\n}\n\nfunc isConstantDataSection(name string) bool {\n\treturn strings.HasPrefix(name, \".rodata\")\n}\n\nfunc isKconfigSection(name string) bool {\n\treturn name == \".kconfig\"\n}\n\ntype elfSectionKind int\n\nconst (\n\tundefSection elfSectionKind = iota\n\tmapSection\n\tbtfMapSection\n\tprogramSection\n\tdataSection\n\tstructOpsSection\n)\n\ntype elfSection struct {\n\t*elf.Section\n\tkind elfSectionKind\n\t// Offset from the start of the section to a symbol\n\tsymbols map[uint64]elf.Symbol\n\t// Offset from the start of the section to a relocation, which points at\n\t// a symbol in another section.\n\trelocations map[uint64]elf.Symbol\n\t// The number of relocations pointing at this section.\n\treferences int\n}\n\nfunc newElfSection(section *elf.Section, kind elfSectionKind) *elfSection {\n\treturn &elfSection{\n\t\tsection,\n\t\tkind,\n\t\tmake(map[uint64]elf.Symbol),\n\t\tmake(map[uint64]elf.Symbol),\n\t\t0,\n\t}\n}\n\n// symbolsSorted returns the section's symbols sorted by offset.\nfunc (es *elfSection) symbolsSorted() iter.Seq2[uint64, elf.Symbol] {\n\treturn func(yield func(uint64, elf.Symbol) bool) {\n\t\tfor _, off := range slices.Sorted(maps.Keys(es.symbols)) {\n\t\t\tif !yield(off, es.symbols[off]) {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n\n// assignSymbols takes a list of symbols and assigns them to their\n// respective sections, indexed by name.\nfunc (ec *elfCode) assignSymbols(symbols []elf.Symbol) {\n\tfor _, symbol := range symbols {\n\t\tsymType := elf.ST_TYPE(symbol.Info)\n\t\tsymSection := ec.sections[symbol.Section]\n\t\tif symSection == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Anonymous symbols only occur in debug sections which we don't process\n\t\t// relocations for. Anonymous symbols are not referenced from other sections.\n\t\tif symbol.Name == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Older versions of LLVM don't tag symbols correctly, so keep\n\t\t// all NOTYPE ones.\n\t\tswitch symSection.kind {\n\t\tcase mapSection, btfMapSection, dataSection:\n\t\t\tif symType != elf.STT_NOTYPE && symType != elf.STT_OBJECT {\n\t\t\t\tcontinue\n\t\t\t}\n\t\tcase programSection:\n\t\t\tif symType != elf.STT_NOTYPE && symType != elf.STT_FUNC {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Program sections may contain NOTYPE symbols with local scope, these are\n\t\t\t// usually labels for jumps. We do not care for these for the purposes of\n\t\t\t// linking and they may overlap with function symbols.\n\t\t\tif symType == elf.STT_NOTYPE && elf.ST_BIND(symbol.Info) == elf.STB_LOCAL {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t// Only collect symbols that occur in program/maps/data sections.\n\t\tdefault:\n\t\t\tcontinue\n\t\t}\n\n\t\tsymSection.symbols[symbol.Value] = symbol\n\t}\n}\n\n// loadRelocations iterates .rel* sections and extracts relocation entries for\n// sections of interest. Makes sure relocations point at valid sections.\nfunc (ec *elfCode) loadRelocations(relSections map[elf.SectionIndex]*elf.Section, symbols []elf.Symbol) error {\n\tfor idx, relSection := range relSections {\n\t\tsection := ec.sections[idx]\n\t\tif section == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\trels, err := ec.loadSectionRelocations(relSection, symbols)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"relocation for section %q: %w\", section.Name, err)\n\t\t}\n\n\t\tfor _, rel := range rels {\n\t\t\ttarget := ec.sections[rel.Section]\n\t\t\tif target == nil {\n\t\t\t\treturn fmt.Errorf(\"section %q: reference to %q in section %s: %w\", section.Name, rel.Name, rel.Section, ErrNotSupported)\n\t\t\t}\n\n\t\t\ttarget.references++\n\t\t}\n\n\t\tsection.relocations = rels\n\t}\n\n\treturn nil\n}\n\n// loadProgramSections iterates ec's sections and emits a ProgramSpec\n// for each function it finds.\n//\n// The resulting map is indexed by function name.\nfunc (ec *elfCode) loadProgramSections() (map[string]*ProgramSpec, error) {\n\n\tprogs := make(map[string]*ProgramSpec)\n\n\t// Generate a ProgramSpec for each function found in each program section.\n\tvar export []string\n\tfor _, sec := range ec.sections {\n\t\tif sec.kind != programSection {\n\t\t\tcontinue\n\t\t}\n\n\t\tif len(sec.symbols) == 0 {\n\t\t\treturn nil, fmt.Errorf(\"section %v: missing symbols\", sec.Name)\n\t\t}\n\n\t\tfuncs, err := ec.loadFunctions(sec)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"section %v: %w\", sec.Name, err)\n\t\t}\n\n\t\tprogType, attachType, progFlags, attachTo := getProgType(sec.Name)\n\n\t\tfor name, insns := range funcs {\n\t\t\tspec := &ProgramSpec{\n\t\t\t\tName:          name,\n\t\t\t\tType:          progType,\n\t\t\t\tFlags:         progFlags,\n\t\t\t\tAttachType:    attachType,\n\t\t\t\tAttachTo:      attachTo,\n\t\t\t\tSectionName:   sec.Name,\n\t\t\t\tLicense:       ec.license,\n\t\t\t\tKernelVersion: ec.version,\n\t\t\t\tInstructions:  insns,\n\t\t\t\tByteOrder:     ec.ByteOrder,\n\t\t\t}\n\n\t\t\t// Function names must be unique within a single ELF blob.\n\t\t\tif progs[name] != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"duplicate program name %s\", name)\n\t\t\t}\n\t\t\tprogs[name] = spec\n\n\t\t\tif spec.SectionName != \".text\" {\n\t\t\t\texport = append(export, name)\n\t\t\t}\n\t\t}\n\t}\n\n\tflattenPrograms(progs, export)\n\n\t// Hide programs (e.g. library functions) that were not explicitly emitted\n\t// to an ELF section. These could be exposed in a separate CollectionSpec\n\t// field later to allow them to be modified.\n\tfor n, p := range progs {\n\t\tif p.SectionName == \".text\" {\n\t\t\tdelete(progs, n)\n\t\t}\n\t}\n\n\treturn progs, nil\n}\n\n// loadFunctions extracts instruction streams from the given program section\n// starting at each symbol in the section. The section's symbols must already\n// be narrowed down to STT_NOTYPE (emitted by clang <8) or STT_FUNC.\n//\n// The resulting map is indexed by function name.\nfunc (ec *elfCode) loadFunctions(sec *elfSection) (map[string]asm.Instructions, error) {\n\tprogs := make(map[string]asm.Instructions)\n\n\t// Pull out ExtInfos once per section to avoid map lookups on every\n\t// instruction.\n\tfo, lo, ro := ec.extInfo.Section(sec.Name)\n\n\t// Raw instruction count since start of the section. ExtInfos point at raw\n\t// insn offsets and ignore the gaps between symbols in case of linked objects.\n\t// We need to count them, we can't obtain this info by any other means.\n\tvar raw asm.RawInstructionOffset\n\n\t// Sort symbols by offset so we can track instructions by their raw offsets.\n\tfor _, sym := range sec.symbolsSorted() {\n\t\tif progs[sym.Name] != nil {\n\t\t\treturn nil, fmt.Errorf(\"duplicate symbol %s in section %s\", sym.Name, sec.Name)\n\t\t}\n\n\t\t// Decode the symbol's instruction stream, limited to its size.\n\t\tsr := internal.NewBufferedSectionReader(sec, int64(sym.Value), int64(sym.Size))\n\t\tinsns := make(asm.Instructions, 0, sym.Size/asm.InstructionSize)\n\t\tinsns, err := asm.AppendInstructions(insns, sr, ec.ByteOrder, platform.Linux)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"decoding instructions for symbol %s in section %s: %w\", sym.Name, sec.Name, err)\n\t\t}\n\t\tif len(insns) == 0 {\n\t\t\treturn nil, fmt.Errorf(\"no instructions found for symbol %s in section %s\", sym.Name, sec.Name)\n\t\t}\n\n\t\t// Mark the first instruction as the start of a function.\n\t\tinsns[0] = insns[0].WithSymbol(sym.Name)\n\n\t\titer := insns.Iterate()\n\t\tfor iter.Next() {\n\t\t\t// Global byte offset of the instruction within the ELF section.\n\t\t\toffset := sym.Value + iter.Offset.Bytes()\n\n\t\t\t// Apply any relocations for the current instruction. If no relocation is\n\t\t\t// present, resolve any section-relative function calls.\n\t\t\tif rel, ok := sec.relocations[offset]; ok {\n\t\t\t\tif err := ec.relocateInstruction(iter.Ins, rel); err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"offset %d in section %s: relocating instruction: %w\", offset, sec.Name, err)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif err := referenceRelativeJump(iter.Ins, offset, sec.symbols); err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"offset %d in section %s: resolving relative jump: %w\", offset, sec.Name, err)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tassignMetadata(iter.Ins, raw, &fo, &lo, &ro)\n\n\t\t\traw += iter.Ins.Width()\n\t\t}\n\n\t\t// Emit the program's instructions.\n\t\tprogs[sym.Name] = insns\n\t}\n\n\treturn progs, nil\n}\n\n// take pops and returns the first item in q if it matches the given predicate\n// f. Otherwise, it returns nil.\nfunc take[T any](q *[]T, f func(T) bool) *T {\n\tif q == nil || len(*q) == 0 {\n\t\treturn nil\n\t}\n\n\tout := (*q)[0]\n\tif f(out) {\n\t\t*q = (*q)[1:]\n\t\treturn &out\n\t}\n\n\treturn nil\n}\n\n// Tag the instruction with any ExtInfo metadata that's pointing at the given\n// raw instruction.\nfunc assignMetadata(ins *asm.Instruction, raw asm.RawInstructionOffset,\n\tfo *btf.FuncOffsets, lo *btf.LineOffsets, ro *btf.CORERelocationOffsets) {\n\n\tif f := take(fo, func(f btf.FuncOffset) bool { return f.Offset == raw }); f != nil {\n\t\t*ins = btf.WithFuncMetadata(*ins, f.Func)\n\t}\n\n\tif l := take(lo, func(l btf.LineOffset) bool { return l.Offset == raw }); l != nil {\n\t\t*ins = ins.WithSource(l.Line)\n\t}\n\n\tif r := take(ro, func(r btf.CORERelocationOffset) bool { return r.Offset == raw }); r != nil {\n\t\t*ins = btf.WithCORERelocationMetadata(*ins, r.Relo)\n\t}\n}\n\n// referenceRelativeJump turns a relative jump to another bpf subprogram within\n// the same ELF section into a Reference Instruction.\n//\n// Up to LLVM 9, calls to subprograms within the same ELF section are sometimes\n// encoded using relative jumps instead of relocation entries. These jumps go\n// out of bounds of the current program, so their targets must be memoized\n// before the section's instruction stream is split.\n//\n// The relative jump Constant is blinded to -1 and the target Symbol is set as\n// the Instruction's Reference so it can be resolved by the linker.\nfunc referenceRelativeJump(ins *asm.Instruction, offset uint64, symbols map[uint64]elf.Symbol) error {\n\tif !ins.IsFunctionReference() || ins.Constant == -1 {\n\t\treturn nil\n\t}\n\n\ttgt := jumpTarget(offset, *ins)\n\tsym := symbols[tgt].Name\n\tif sym == \"\" {\n\t\treturn fmt.Errorf(\"no jump target found at offset %d\", tgt)\n\t}\n\n\t*ins = ins.WithReference(sym)\n\tins.Constant = -1\n\n\treturn nil\n}\n\n// jumpTarget takes ins' offset within an instruction stream (in bytes)\n// and returns its absolute jump destination (in bytes) within the\n// instruction stream.\nfunc jumpTarget(offset uint64, ins asm.Instruction) uint64 {\n\t// A relative jump instruction describes the amount of raw BPF instructions\n\t// to jump, convert the offset into bytes.\n\tdest := ins.Constant * asm.InstructionSize\n\n\t// The starting point of the jump is the end of the current instruction.\n\tdest += int64(offset + asm.InstructionSize)\n\n\tif dest < 0 {\n\t\treturn 0\n\t}\n\n\treturn uint64(dest)\n}\n\nvar errUnsupportedBinding = errors.New(\"unsupported binding\")\n\nfunc (ec *elfCode) relocateInstruction(ins *asm.Instruction, rel elf.Symbol) error {\n\tvar (\n\t\ttyp  = elf.ST_TYPE(rel.Info)\n\t\tbind = elf.ST_BIND(rel.Info)\n\t\tname = rel.Name\n\t)\n\n\ttarget := ec.sections[rel.Section]\n\n\tswitch target.kind {\n\tcase mapSection, btfMapSection:\n\t\tif bind == elf.STB_LOCAL {\n\t\t\treturn fmt.Errorf(\"possible erroneous static qualifier on map definition: found reference to %q\", name)\n\t\t}\n\n\t\tif bind != elf.STB_GLOBAL {\n\t\t\treturn fmt.Errorf(\"map %q: %w: %s\", name, errUnsupportedBinding, bind)\n\t\t}\n\n\t\tif typ != elf.STT_OBJECT && typ != elf.STT_NOTYPE {\n\t\t\t// STT_NOTYPE is generated on clang < 8 which doesn't tag\n\t\t\t// relocations appropriately.\n\t\t\treturn fmt.Errorf(\"map load: incorrect relocation type %v\", typ)\n\t\t}\n\n\t\tins.Src = asm.PseudoMapFD\n\n\tcase dataSection:\n\t\tvar offset uint32\n\t\tswitch typ {\n\t\tcase elf.STT_SECTION:\n\t\t\tif bind != elf.STB_LOCAL {\n\t\t\t\treturn fmt.Errorf(\"direct load: %s: %w: %s\", name, errUnsupportedBinding, bind)\n\t\t\t}\n\n\t\t\t// This is really a reference to a static symbol, which clang doesn't\n\t\t\t// emit a symbol table entry for. Instead it encodes the offset in\n\t\t\t// the instruction itself.\n\t\t\toffset = uint32(uint64(ins.Constant))\n\n\t\tcase elf.STT_OBJECT:\n\t\t\t// LLVM 9 emits OBJECT-LOCAL symbols for anonymous constants.\n\t\t\tif bind != elf.STB_GLOBAL && bind != elf.STB_LOCAL && bind != elf.STB_WEAK {\n\t\t\t\treturn fmt.Errorf(\"direct load: %s: %w: %s\", name, errUnsupportedBinding, bind)\n\t\t\t}\n\n\t\t\toffset = uint32(rel.Value)\n\n\t\tcase elf.STT_NOTYPE:\n\t\t\t// LLVM 7 emits NOTYPE-LOCAL symbols for anonymous constants.\n\t\t\tif bind != elf.STB_LOCAL {\n\t\t\t\treturn fmt.Errorf(\"direct load: %s: %w: %s\", name, errUnsupportedBinding, bind)\n\t\t\t}\n\n\t\t\toffset = uint32(rel.Value)\n\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"incorrect relocation type %v for direct map load\", typ)\n\t\t}\n\n\t\t// We rely on using the name of the data section as the reference. It\n\t\t// would be nicer to keep the real name in case of an STT_OBJECT, but\n\t\t// it's not clear how to encode that into Instruction.\n\t\tname = target.Name\n\n\t\t// The kernel expects the offset in the second basic BPF instruction.\n\t\tins.Constant = int64(uint64(offset) << 32)\n\t\tins.Src = asm.PseudoMapValue\n\n\tcase programSection:\n\t\tswitch opCode := ins.OpCode; {\n\t\tcase opCode.JumpOp() == asm.Call:\n\t\t\tif ins.Src != asm.PseudoCall {\n\t\t\t\treturn fmt.Errorf(\"call: %s: incorrect source register\", name)\n\t\t\t}\n\n\t\t\tswitch typ {\n\t\t\tcase elf.STT_NOTYPE, elf.STT_FUNC:\n\t\t\t\tif bind != elf.STB_GLOBAL && bind != elf.STB_WEAK {\n\t\t\t\t\treturn fmt.Errorf(\"call: %s: %w: %s\", name, errUnsupportedBinding, bind)\n\t\t\t\t}\n\n\t\t\tcase elf.STT_SECTION:\n\t\t\t\tif bind != elf.STB_LOCAL {\n\t\t\t\t\treturn fmt.Errorf(\"call: %s: %w: %s\", name, errUnsupportedBinding, bind)\n\t\t\t\t}\n\n\t\t\t\t// The function we want to call is in the indicated section,\n\t\t\t\t// at the offset encoded in the instruction itself. Reverse\n\t\t\t\t// the calculation to find the real function we're looking for.\n\t\t\t\t// A value of -1 references the first instruction in the section.\n\t\t\t\toffset := int64(int32(ins.Constant)+1) * asm.InstructionSize\n\t\t\t\tsym, ok := target.symbols[uint64(offset)]\n\t\t\t\tif !ok {\n\t\t\t\t\treturn fmt.Errorf(\"call: no symbol at offset %d\", offset)\n\t\t\t\t}\n\n\t\t\t\tname = sym.Name\n\t\t\t\tins.Constant = -1\n\n\t\t\tdefault:\n\t\t\t\treturn fmt.Errorf(\"call: %s: invalid symbol type %s\", name, typ)\n\t\t\t}\n\t\tcase opCode.IsDWordLoad():\n\t\t\tswitch typ {\n\t\t\tcase elf.STT_FUNC:\n\t\t\t\tif bind != elf.STB_GLOBAL {\n\t\t\t\t\treturn fmt.Errorf(\"load: %s: %w: %s\", name, errUnsupportedBinding, bind)\n\t\t\t\t}\n\n\t\t\tcase elf.STT_SECTION:\n\t\t\t\tif bind != elf.STB_LOCAL {\n\t\t\t\t\treturn fmt.Errorf(\"load: %s: %w: %s\", name, errUnsupportedBinding, bind)\n\t\t\t\t}\n\n\t\t\t\t// ins.Constant already contains the offset in bytes from the\n\t\t\t\t// start of the section. This is different than a call to a\n\t\t\t\t// static function.\n\n\t\t\tdefault:\n\t\t\t\treturn fmt.Errorf(\"load: %s: invalid symbol type %s\", name, typ)\n\t\t\t}\n\n\t\t\tsym, ok := target.symbols[uint64(ins.Constant)]\n\t\t\tif !ok {\n\t\t\t\treturn fmt.Errorf(\"load: no symbol at offset %d\", ins.Constant)\n\t\t\t}\n\n\t\t\tname = sym.Name\n\t\t\tins.Constant = -1\n\t\t\tins.Src = asm.PseudoFunc\n\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"neither a call nor a load instruction: %v\", ins)\n\t\t}\n\n\t// The Undefined section is used for 'virtual' symbols that aren't backed by\n\t// an ELF section. This includes symbol references from inline asm, forward\n\t// function declarations, as well as extern kfunc declarations using __ksym\n\t// and extern kconfig variables declared using __kconfig.\n\tcase undefSection:\n\t\tif bind != elf.STB_GLOBAL && bind != elf.STB_WEAK {\n\t\t\treturn fmt.Errorf(\"asm relocation: %s: %w: %s\", name, errUnsupportedBinding, bind)\n\t\t}\n\n\t\tif typ != elf.STT_NOTYPE {\n\t\t\treturn fmt.Errorf(\"asm relocation: %s: unsupported type %s\", name, typ)\n\t\t}\n\n\t\tkf := ec.kfuncs[name]\n\t\t_, ks := ec.ksyms[name]\n\n\t\tswitch {\n\t\t// If a Call / DWordLoad instruction is found and the datasec has a btf.Func with a Name\n\t\t// that matches the symbol name we mark the instruction as a referencing a kfunc.\n\t\tcase kf != nil && ins.OpCode.JumpOp() == asm.Call:\n\t\t\tins.Metadata.Set(kfuncMetaKey{}, &kfuncMeta{\n\t\t\t\tFunc:    kf,\n\t\t\t\tBinding: bind,\n\t\t\t})\n\n\t\t\tins.Src = asm.PseudoKfuncCall\n\t\t\tins.Constant = -1\n\n\t\tcase kf != nil && ins.OpCode.IsDWordLoad():\n\t\t\tins.Metadata.Set(kfuncMetaKey{}, &kfuncMeta{\n\t\t\t\tFunc:    kf,\n\t\t\t\tBinding: bind,\n\t\t\t})\n\n\t\t\tins.Constant = 0\n\n\t\tcase ks && ins.OpCode.IsDWordLoad():\n\t\t\tif bind != elf.STB_GLOBAL && bind != elf.STB_WEAK {\n\t\t\t\treturn fmt.Errorf(\"asm relocation: %s: %w: %s\", name, errUnsupportedBinding, bind)\n\t\t\t}\n\t\t\tins.Metadata.Set(ksymMetaKey{}, &ksymMeta{\n\t\t\t\tBinding: bind,\n\t\t\t\tName:    name,\n\t\t\t})\n\n\t\t// If no kconfig map is found, this must be a symbol reference from inline\n\t\t// asm (see testdata/loader.c:asm_relocation()) or a call to a forward\n\t\t// function declaration (see testdata/fwd_decl.c). Don't interfere, These\n\t\t// remain standard symbol references.\n\t\t// extern __kconfig reads are represented as dword loads that need to be\n\t\t// rewritten to pseudo map loads from .kconfig. If the map is present,\n\t\t// require it to contain the symbol to disambiguate between inline asm\n\t\t// relos and kconfigs.\n\t\tcase ec.kconfig != nil && ins.OpCode.IsDWordLoad():\n\t\t\tif bind != elf.STB_GLOBAL {\n\t\t\t\treturn fmt.Errorf(\"asm relocation: %s: %w: %s\", name, errUnsupportedBinding, bind)\n\t\t\t}\n\n\t\t\tfor _, vsi := range ec.kconfig.Value.(*btf.Datasec).Vars {\n\t\t\t\tif vsi.Type.(*btf.Var).Name != rel.Name {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tins.Src = asm.PseudoMapValue\n\t\t\t\tins.Metadata.Set(kconfigMetaKey{}, &kconfigMeta{ec.kconfig, vsi.Offset})\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\treturn fmt.Errorf(\"kconfig %s not found in .kconfig\", rel.Name)\n\t\t}\n\n\tdefault:\n\t\treturn fmt.Errorf(\"relocation to %q: %w\", target.Name, ErrNotSupported)\n\t}\n\n\t*ins = ins.WithReference(name)\n\treturn nil\n}\n\n// loadMaps iterates over all ELF sections marked as map sections (like .maps)\n// and parses each symbol into a MapSpec.\nfunc (ec *elfCode) loadMaps() error {\n\tfor _, sec := range ec.sections {\n\t\tif sec.kind != mapSection {\n\t\t\tcontinue\n\t\t}\n\n\t\tif len(sec.symbols) == 0 {\n\t\t\treturn fmt.Errorf(\"section %v: no symbols\", sec.Name)\n\t\t}\n\n\t\tvars, err := ec.sectionVars(ec.btf, sec.Name)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"section %v: loading map variable BTF: %w\", sec.Name, err)\n\t\t}\n\n\t\tfor _, sym := range sec.symbols {\n\t\t\tname := sym.Name\n\t\t\tif ec.maps[name] != nil {\n\t\t\t\treturn fmt.Errorf(\"duplicate symbol %s in section %s\", name, sec.Name)\n\t\t\t}\n\n\t\t\tsr := internal.NewBufferedSectionReader(sec, int64(sym.Value), int64(sym.Size))\n\n\t\t\tspec := MapSpec{\n\t\t\t\tName: sanitizeName(name, -1),\n\t\t\t}\n\t\t\tswitch {\n\t\t\tcase binary.Read(sr, ec.ByteOrder, &spec.Type) != nil:\n\t\t\t\treturn fmt.Errorf(\"map %s: missing type\", name)\n\t\t\tcase binary.Read(sr, ec.ByteOrder, &spec.KeySize) != nil:\n\t\t\t\treturn fmt.Errorf(\"map %s: missing key size\", name)\n\t\t\tcase binary.Read(sr, ec.ByteOrder, &spec.ValueSize) != nil:\n\t\t\t\treturn fmt.Errorf(\"map %s: missing value size\", name)\n\t\t\tcase binary.Read(sr, ec.ByteOrder, &spec.MaxEntries) != nil:\n\t\t\t\treturn fmt.Errorf(\"map %s: missing max entries\", name)\n\t\t\tcase binary.Read(sr, ec.ByteOrder, &spec.Flags) != nil:\n\t\t\t\treturn fmt.Errorf(\"map %s: missing flags\", name)\n\t\t\t}\n\n\t\t\textra, err := io.ReadAll(sr)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"map %s: reading map tail: %w\", name, err)\n\t\t\t}\n\t\t\tif len(extra) > 0 {\n\t\t\t\tspec.Extra = bytes.NewReader(extra)\n\t\t\t}\n\n\t\t\tif v, ok := vars[name]; ok {\n\t\t\t\tspec.Tags = slices.Clone(v.Tags)\n\t\t\t}\n\n\t\t\tec.maps[name] = &spec\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// sectionVars looks up the BTF Datasec for the given section name and returns a\n// map of variable names to their btf.Var definitions.\nfunc (ec *elfCode) sectionVars(spec *btf.Spec, sec string) (map[string]*btf.Var, error) {\n\tvars := make(map[string]*btf.Var)\n\n\tif spec == nil {\n\t\treturn vars, nil\n\t}\n\n\tvar ds *btf.Datasec\n\tif err := ec.btf.TypeByName(sec, &ds); err != nil {\n\t\treturn vars, nil\n\t}\n\n\tfor _, vsi := range ds.Vars {\n\t\tv, ok := btf.As[*btf.Var](vsi.Type)\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"btf.VarSecInfo doesn't point to a *btf.Var: %T\", vsi.Type)\n\t\t}\n\t\tvars[string(v.Name)] = v\n\t}\n\n\treturn vars, nil\n}\n\n// loadBTFMaps iterates over all ELF sections marked as BTF map sections\n// (like .maps) and parses them into MapSpecs. Dump the .maps section and\n// any relocations with `readelf -x .maps -r <elf_file>`.\nfunc (ec *elfCode) loadBTFMaps() error {\n\tfor _, sec := range ec.sections {\n\t\tif sec.kind != btfMapSection {\n\t\t\tcontinue\n\t\t}\n\n\t\tif ec.btf == nil {\n\t\t\treturn fmt.Errorf(\"missing BTF\")\n\t\t}\n\n\t\tvars, err := ec.sectionVars(ec.btf, sec.Name)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"section %v: loading map variable BTF: %w\", sec.Name, err)\n\t\t}\n\n\t\tif len(vars) != len(sec.symbols) {\n\t\t\treturn fmt.Errorf(\"section %v: contains %d symbols but %d btf.Vars\", sec.Name, len(sec.symbols), len(vars))\n\t\t}\n\n\t\tsyms := make(map[string]elf.Symbol)\n\t\tfor _, sym := range sec.symbols {\n\t\t\tsyms[sym.Name] = sym\n\t\t}\n\n\t\tfor _, v := range vars {\n\t\t\tname := v.Name\n\n\t\t\t// Find the ELF symbol corresponding to this Var.\n\t\t\tsym, ok := syms[name]\n\t\t\tif !ok {\n\t\t\t\treturn fmt.Errorf(\"section %v: missing symbol for map %s\", sec.Name, name)\n\t\t\t}\n\n\t\t\tsr := internal.NewBufferedSectionReader(sec, int64(sym.Value), int64(sym.Size))\n\n\t\t\t// The BTF metadata for each Var contains the full length of the map\n\t\t\t// declaration, so read the corresponding amount of bytes from the ELF.\n\t\t\t// This way, we can pinpoint which map declaration contains unexpected\n\t\t\t// (and therefore unsupported) data.\n\t\t\tif _, err = io.Copy(internal.DiscardZeroes{}, sr); err != nil {\n\t\t\t\treturn fmt.Errorf(\"section %v: map %s: initializing BTF map definitions: %w\", sec.Name, name, internal.ErrNotSupported)\n\t\t\t}\n\n\t\t\tif ec.maps[name] != nil {\n\t\t\t\treturn fmt.Errorf(\"section %v: map %s already exists\", sec.Name, name)\n\t\t\t}\n\n\t\t\t// Each Var representing a BTF map definition contains a Struct.\n\t\t\tmapStruct, ok := btf.UnderlyingType(v.Type).(*btf.Struct)\n\t\t\tif !ok {\n\t\t\t\treturn fmt.Errorf(\"expected struct, got %s\", v.Type)\n\t\t\t}\n\n\t\t\tspec, err := mapSpecFromBTF(sec, sym, v, mapStruct, ec.btf, name, false)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"map %v: %w\", name, err)\n\t\t\t}\n\n\t\t\tec.maps[name] = spec\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// mapSpecFromBTF produces a MapSpec based on a btf.Struct def representing\n// a BTF map definition. The name and spec arguments will be copied to the\n// resulting MapSpec, and inner must be true on any recursive invocations.\nfunc mapSpecFromBTF(es *elfSection, sym elf.Symbol, v *btf.Var, def *btf.Struct, spec *btf.Spec, name string, inner bool) (*MapSpec, error) {\n\tvar (\n\t\tkey, value         btf.Type\n\t\tkeySize, valueSize uint64\n\t\tmapType            MapType\n\t\tflags, maxEntries  uint64\n\t\tpinType            PinType\n\t\tmapExtra           uint64\n\t\tinnerMapSpec       *MapSpec\n\t\tcontents           []MapKV\n\t\terr                error\n\t)\n\n\tfor i, member := range def.Members {\n\t\tswitch member.Name {\n\t\tcase \"type\":\n\t\t\tmt, err := uintFromBTF(member.Type)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"can't get type: %w\", err)\n\t\t\t}\n\t\t\tmapType = MapType(mt)\n\n\t\tcase \"map_flags\":\n\t\t\tflags, err = uintFromBTF(member.Type)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"can't get BTF map flags: %w\", err)\n\t\t\t}\n\n\t\tcase \"max_entries\":\n\t\t\tmaxEntries, err = uintFromBTF(member.Type)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"can't get BTF map max entries: %w\", err)\n\t\t\t}\n\n\t\tcase \"key\":\n\t\t\tif keySize != 0 {\n\t\t\t\treturn nil, errors.New(\"both key and key_size given\")\n\t\t\t}\n\n\t\t\tpk, ok := member.Type.(*btf.Pointer)\n\t\t\tif !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"key type is not a pointer: %T\", member.Type)\n\t\t\t}\n\n\t\t\tkey = pk.Target\n\n\t\t\tsize, err := btf.Sizeof(pk.Target)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"can't get size of BTF key: %w\", err)\n\t\t\t}\n\n\t\t\tkeySize = uint64(size)\n\n\t\tcase \"value\":\n\t\t\tif valueSize != 0 {\n\t\t\t\treturn nil, errors.New(\"both value and value_size given\")\n\t\t\t}\n\n\t\t\tvk, ok := member.Type.(*btf.Pointer)\n\t\t\tif !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"value type is not a pointer: %T\", member.Type)\n\t\t\t}\n\n\t\t\tvalue = vk.Target\n\n\t\t\tsize, err := btf.Sizeof(vk.Target)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"can't get size of BTF value: %w\", err)\n\t\t\t}\n\n\t\t\tvalueSize = uint64(size)\n\n\t\tcase \"key_size\":\n\t\t\t// Key needs to be nil and keySize needs to be 0 for key_size to be\n\t\t\t// considered a valid member.\n\t\t\tif key != nil || keySize != 0 {\n\t\t\t\treturn nil, errors.New(\"both key and key_size given\")\n\t\t\t}\n\n\t\t\tkeySize, err = uintFromBTF(member.Type)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"can't get BTF key size: %w\", err)\n\t\t\t}\n\n\t\tcase \"value_size\":\n\t\t\t// Value needs to be nil and valueSize needs to be 0 for value_size to be\n\t\t\t// considered a valid member.\n\t\t\tif value != nil || valueSize != 0 {\n\t\t\t\treturn nil, errors.New(\"both value and value_size given\")\n\t\t\t}\n\n\t\t\tvalueSize, err = uintFromBTF(member.Type)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"can't get BTF value size: %w\", err)\n\t\t\t}\n\n\t\tcase \"pinning\":\n\t\t\tif inner {\n\t\t\t\treturn nil, errors.New(\"inner maps can't be pinned\")\n\t\t\t}\n\n\t\t\tpinning, err := uintFromBTF(member.Type)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"can't get pinning: %w\", err)\n\t\t\t}\n\n\t\t\tpinType = PinType(pinning)\n\n\t\tcase \"values\":\n\t\t\t// The 'values' field in BTF map definitions is used for declaring map\n\t\t\t// value types that are references to other BPF objects, like other maps\n\t\t\t// or programs. It is always expected to be an array of pointers.\n\t\t\tif i != len(def.Members)-1 {\n\t\t\t\treturn nil, errors.New(\"'values' must be the last member in a BTF map definition\")\n\t\t\t}\n\n\t\t\tif valueSize != 0 && valueSize != 4 {\n\t\t\t\treturn nil, errors.New(\"value_size must be 0 or 4\")\n\t\t\t}\n\t\t\tvalueSize = 4\n\n\t\t\tvalueType, err := resolveBTFArrayMacro(member.Type)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"can't resolve type of member 'values': %w\", err)\n\t\t\t}\n\n\t\t\tswitch t := valueType.(type) {\n\t\t\tcase *btf.Struct:\n\t\t\t\t// The values member pointing to an array of structs means we're expecting\n\t\t\t\t// a map-in-map declaration.\n\t\t\t\tif mapType != ArrayOfMaps && mapType != HashOfMaps {\n\t\t\t\t\treturn nil, errors.New(\"outer map needs to be an array or a hash of maps\")\n\t\t\t\t}\n\t\t\t\tif inner {\n\t\t\t\t\treturn nil, fmt.Errorf(\"nested inner maps are not supported\")\n\t\t\t\t}\n\n\t\t\t\t// This inner map spec is used as a map template, but it needs to be\n\t\t\t\t// created as a traditional map before it can be used to do so.\n\t\t\t\t// libbpf names the inner map template '<outer_name>.inner', but we\n\t\t\t\t// opted for _inner to simplify validation logic. (dots only supported\n\t\t\t\t// on kernels 5.2 and up)\n\t\t\t\t// Pass the BTF spec from the parent object, since both parent and\n\t\t\t\t// child must be created from the same BTF blob (on kernels that support BTF).\n\t\t\t\tinnerMapSpec, err = mapSpecFromBTF(es, sym, v, t, spec, name+\"_inner\", true)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"can't parse BTF map definition of inner map: %w\", err)\n\t\t\t\t}\n\n\t\t\tcase *btf.FuncProto:\n\t\t\t\t// The values member contains an array of function pointers, meaning an\n\t\t\t\t// autopopulated PROG_ARRAY.\n\t\t\t\tif mapType != ProgramArray {\n\t\t\t\t\treturn nil, errors.New(\"map needs to be a program array\")\n\t\t\t\t}\n\n\t\t\tdefault:\n\t\t\t\treturn nil, fmt.Errorf(\"unsupported value type %q in 'values' field\", t)\n\t\t\t}\n\n\t\t\tcontents, err = resolveBTFValuesContents(es, sym, member)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"resolving values contents: %w\", err)\n\t\t\t}\n\n\t\tcase \"map_extra\":\n\t\t\tmapExtra, err = uintFromBTF(member.Type)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"resolving map_extra: %w\", err)\n\t\t\t}\n\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized field %s in BTF map definition\", member.Name)\n\t\t}\n\t}\n\n\t// Some maps don't support value sizes, but annotating their map definitions\n\t// with __type macros can still be useful, especially to let bpf2go generate\n\t// type definitions for them.\n\tif value != nil && !mapType.canHaveValueSize() {\n\t\tvalueSize = 0\n\t}\n\n\treturn &MapSpec{\n\t\tName:       sanitizeName(name, -1),\n\t\tType:       MapType(mapType),\n\t\tKeySize:    uint32(keySize),\n\t\tValueSize:  uint32(valueSize),\n\t\tMaxEntries: uint32(maxEntries),\n\t\tFlags:      uint32(flags),\n\t\tKey:        key,\n\t\tValue:      value,\n\t\tPinning:    pinType,\n\t\tInnerMap:   innerMapSpec,\n\t\tContents:   contents,\n\t\tTags:       slices.Clone(v.Tags),\n\t\tMapExtra:   mapExtra,\n\t}, nil\n}\n\n// uintFromBTF resolves the __uint and __ulong macros.\n//\n// __uint emits a pointer to a sized array. For int (*foo)[10], this function\n// will return 10.\n//\n// __ulong emits an enum with a single value that can represent a 64-bit\n// integer. The first (and only) enum value is returned.\nfunc uintFromBTF(typ btf.Type) (uint64, error) {\n\tswitch t := typ.(type) {\n\tcase *btf.Pointer:\n\t\tarr, ok := t.Target.(*btf.Array)\n\t\tif !ok {\n\t\t\treturn 0, fmt.Errorf(\"not a pointer to array: %v\", typ)\n\t\t}\n\t\treturn uint64(arr.Nelems), nil\n\n\tcase *btf.Enum:\n\t\tif len(t.Values) == 0 {\n\t\t\treturn 0, errors.New(\"enum has no values\")\n\t\t}\n\t\treturn t.Values[0].Value, nil\n\n\tdefault:\n\t\treturn 0, fmt.Errorf(\"not a pointer or enum: %v\", typ)\n\t}\n}\n\n// resolveBTFArrayMacro resolves the __array macro, which declares an array\n// of pointers to a given type. This function returns the target Type of\n// the pointers in the array.\nfunc resolveBTFArrayMacro(typ btf.Type) (btf.Type, error) {\n\tarr, ok := typ.(*btf.Array)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"not an array: %v\", typ)\n\t}\n\n\tptr, ok := arr.Type.(*btf.Pointer)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"not an array of pointers: %v\", typ)\n\t}\n\n\treturn ptr.Target, nil\n}\n\n// valuesRelocations returns an iterator over the relocations in the ELF section\n// corresponding to the elements of a .values array in a BTF map definition. Each\n// iteration yields the array index and the symbol referenced by the relocation\n// at that index. Empty indices are skipped.\nfunc valuesRelocations(es *elfSection, sym elf.Symbol, member btf.Member) iter.Seq2[uint32, elf.Symbol] {\n\t// The elements of a .values pointer array are not encoded in BTF itself.\n\t// Instead, each array index receives a relocation pointing at a symbol\n\t// (map/prog) in another section. However, it's possible to leave certain\n\t// array indices empty, so all indices' offsets need to be checked for emitted\n\t// relocations.\n\n\t// Absolute offset of the .values member within the section.\n\tstart := sym.Value + uint64(member.Offset.Bytes())\n\n\t// .values is a variable-length struct member, so its contents run until the\n\t// end of the symbol. The symbol offset + size is the absolute offset of the\n\t// end of the array in the section.\n\tend := sym.Value + sym.Size\n\n\t// The size of an address in this section. This determines the width of an\n\t// index in the array.\n\talign := es.Addralign\n\n\t// Amount of elements in the .values array.\n\telems := (end - start) / align\n\n\treturn func(yield func(uint32, elf.Symbol) bool) {\n\t\tfor i := range uint32(elems) {\n\t\t\t// off increases by align on each iteration, starting at .values.\n\t\t\toff := start + (uint64(i) * align)\n\n\t\t\tr, ok := es.relocations[off]\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif !yield(i, r) {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n}\n\n// resolveBTFValuesContents looks up the symbols referenced by the relocations\n// in a .values array and returns them as MapKV pairs, where the key is the\n// array index and the value is the symbol name. Empty indices are skipped.\nfunc resolveBTFValuesContents(es *elfSection, sym elf.Symbol, member btf.Member) ([]MapKV, error) {\n\tvar contents []MapKV\n\n\tif member.Offset.Bytes() > uint32(sym.Size) {\n\t\treturn nil, fmt.Errorf(\"member offset %d exceeds symbol size %d\", member.Offset.Bytes(), sym.Size)\n\t}\n\n\tfor i, sym := range valuesRelocations(es, sym, member) {\n\t\t// Emit a value stub based on the type of relocation to be replaced by a\n\t\t// real fd later in the pipeline before populating the Map.\n\t\tswitch t := elf.ST_TYPE(sym.Info); t {\n\t\tcase elf.STT_FUNC:\n\t\t\tcontents = append(contents, MapKV{i, sym.Name})\n\t\tcase elf.STT_OBJECT:\n\t\t\tcontents = append(contents, MapKV{i, sym.Name})\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unknown relocation type %v for symbol %s\", t, sym.Name)\n\t\t}\n\t}\n\n\treturn contents, nil\n}\n\nfunc (ec *elfCode) loadDataSections() error {\n\tfor _, sec := range ec.sections {\n\t\tif sec.kind != dataSection {\n\t\t\tcontinue\n\t\t}\n\n\t\t// If a section has no references, it will be freed as soon as the\n\t\t// Collection closes, so creating and populating it is wasteful. If it has\n\t\t// no symbols, it is likely an ephemeral section used during compilation\n\t\t// that wasn't sanitized by the bpf linker. (like .rodata.str1.1)\n\t\t//\n\t\t// No symbols means no VariableSpecs can be generated from it, making it\n\t\t// pointless to emit a data section for.\n\t\tif sec.references == 0 && len(sec.symbols) == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\tif sec.Size > math.MaxUint32 {\n\t\t\treturn fmt.Errorf(\"data section %s: contents exceed maximum size\", sec.Name)\n\t\t}\n\n\t\tmapSpec := &MapSpec{\n\t\t\tName:       sanitizeName(sec.Name, -1),\n\t\t\tType:       Array,\n\t\t\tKeySize:    4,\n\t\t\tValueSize:  uint32(sec.Size),\n\t\t\tMaxEntries: 1,\n\t\t}\n\n\t\tif isConstantDataSection(sec.Name) {\n\t\t\tmapSpec.Flags = sys.BPF_F_RDONLY_PROG\n\t\t}\n\n\t\tvar data []byte\n\t\tswitch sec.Type {\n\t\t// Only open the section if we know there's actual data to be read.\n\t\tcase elf.SHT_PROGBITS:\n\t\t\tvar err error\n\t\t\tdata, err = sec.Data()\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"data section %s: can't get contents: %w\", sec.Name, err)\n\t\t\t}\n\n\t\tcase elf.SHT_NOBITS:\n\t\t\t// NOBITS sections like .bss contain only zeroes and are not allocated in\n\t\t\t// the ELF. Since data sections are Arrays, the kernel can preallocate\n\t\t\t// them. Don't attempt reading zeroes from the ELF, instead allocate the\n\t\t\t// zeroed memory to support getting and setting VariableSpecs for sections\n\t\t\t// like .bss.\n\t\t\tdata = make([]byte, sec.Size)\n\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"data section %s: unknown section type %s\", sec.Name, sec.Type)\n\t\t}\n\n\t\tmapSpec.Contents = []MapKV{{uint32(0), data}}\n\n\t\tfor off, sym := range sec.symbols {\n\t\t\t// Skip symbols marked with the 'hidden' attribute.\n\t\t\tif elf.ST_VISIBILITY(sym.Other) == elf.STV_HIDDEN ||\n\t\t\t\telf.ST_VISIBILITY(sym.Other) == elf.STV_INTERNAL {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Only accept symbols with global or weak bindings. The common\n\t\t\t// alternative is STB_LOCAL, which are either function-scoped or declared\n\t\t\t// 'static'.\n\t\t\tif elf.ST_BIND(sym.Info) != elf.STB_GLOBAL &&\n\t\t\t\telf.ST_BIND(sym.Info) != elf.STB_WEAK {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif ec.vars[sym.Name] != nil {\n\t\t\t\treturn fmt.Errorf(\"data section %s: duplicate variable %s\", sec.Name, sym.Name)\n\t\t\t}\n\n\t\t\t// Skip symbols starting with a dot, they are compiler-internal symbols\n\t\t\t// emitted by clang 11 and earlier and are not cleaned up by the bpf\n\t\t\t// compiler backend (e.g. symbols named .Lconstinit.1 in sections like\n\t\t\t// .rodata.cst32). Variables in C cannot start with a dot, so filter these\n\t\t\t// out.\n\t\t\tif strings.HasPrefix(sym.Name, \".\") {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif off+sym.Size > uint64(len(data)) {\n\t\t\t\treturn fmt.Errorf(\"data section %s: variable %s exceeds section bounds\", sec.Name, sym.Name)\n\t\t\t}\n\n\t\t\tif off > math.MaxUint32 {\n\t\t\t\treturn fmt.Errorf(\"data section %s: variable %s offset %d exceeds maximum\", sec.Name, sym.Name, off)\n\t\t\t}\n\n\t\t\tec.vars[sym.Name] = &VariableSpec{\n\t\t\t\tSectionName: sec.Name,\n\t\t\t\tName:        sym.Name,\n\t\t\t\tOffset:      uint32(off),\n\t\t\t\tValue:       slices.Clone(data[off : off+sym.Size]),\n\t\t\t}\n\t\t}\n\n\t\t// It is possible for a data section to exist without a corresponding BTF Datasec\n\t\t// if it only contains anonymous values like macro-defined arrays.\n\t\tif ec.btf != nil {\n\t\t\tvar ds *btf.Datasec\n\t\t\tif ec.btf.TypeByName(sec.Name, &ds) == nil {\n\t\t\t\t// Assign the spec's key and BTF only if the Datasec lookup was successful.\n\t\t\t\tmapSpec.Key = &btf.Void{}\n\t\t\t\tmapSpec.Value = ds\n\n\t\t\t\t// Populate VariableSpecs with type information, if available.\n\t\t\t\tfor _, v := range ds.Vars {\n\t\t\t\t\tname := v.Type.TypeName()\n\t\t\t\t\tif name == \"\" {\n\t\t\t\t\t\treturn fmt.Errorf(\"data section %s: anonymous variable %v\", sec.Name, v)\n\t\t\t\t\t}\n\n\t\t\t\t\tvt, ok := v.Type.(*btf.Var)\n\t\t\t\t\tif !ok {\n\t\t\t\t\t\treturn fmt.Errorf(\"data section %s: unexpected type %T for variable %s\", sec.Name, v.Type, name)\n\t\t\t\t\t}\n\n\t\t\t\t\tev := ec.vars[name]\n\t\t\t\t\tif ev == nil {\n\t\t\t\t\t\t// Hidden symbols appear in the BTF Datasec but don't receive a VariableSpec.\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\tif v.Offset != ev.Offset {\n\t\t\t\t\t\treturn fmt.Errorf(\"data section %s: variable %s datasec offset (%d) doesn't match ELF symbol offset (%d)\", sec.Name, name, v.Offset, ev.Offset)\n\t\t\t\t\t}\n\n\t\t\t\t\tif v.Size != ev.Size() {\n\t\t\t\t\t\treturn fmt.Errorf(\"data section %s: variable %s size in datasec (%d) doesn't match ELF symbol size (%d)\", sec.Name, name, v.Size, ev.Size())\n\t\t\t\t\t}\n\n\t\t\t\t\t// Decouple the Var in the VariableSpec from the underlying DataSec in\n\t\t\t\t\t// the MapSpec to avoid modifications from affecting map loads later on.\n\t\t\t\t\tev.Type = btf.Copy(vt).(*btf.Var)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tec.maps[sec.Name] = mapSpec\n\t}\n\n\treturn nil\n}\n\n// loadKconfigSection handles the 'virtual' Datasec .kconfig that doesn't\n// have a corresponding ELF section and exist purely in BTF.\nfunc (ec *elfCode) loadKconfigSection() error {\n\tif ec.btf == nil {\n\t\treturn nil\n\t}\n\n\tvar ds *btf.Datasec\n\terr := ec.btf.TypeByName(\".kconfig\", &ds)\n\tif errors.Is(err, btf.ErrNotFound) {\n\t\treturn nil\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif ds.Size == 0 {\n\t\treturn errors.New(\"zero-length .kconfig\")\n\t}\n\n\tec.kconfig = &MapSpec{\n\t\tName:       \".kconfig\",\n\t\tType:       Array,\n\t\tKeySize:    uint32(4),\n\t\tValueSize:  ds.Size,\n\t\tMaxEntries: 1,\n\t\tFlags:      sys.BPF_F_RDONLY_PROG,\n\t\tKey:        &btf.Int{Size: 4},\n\t\tValue:      ds,\n\t}\n\n\treturn nil\n}\n\n// loadKsymsSection handles the 'virtual' Datasec .ksyms that doesn't\n// have a corresponding ELF section and exist purely in BTF.\nfunc (ec *elfCode) loadKsymsSection() error {\n\tif ec.btf == nil {\n\t\treturn nil\n\t}\n\n\tvar ds *btf.Datasec\n\terr := ec.btf.TypeByName(\".ksyms\", &ds)\n\tif errors.Is(err, btf.ErrNotFound) {\n\t\treturn nil\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, v := range ds.Vars {\n\t\tswitch t := v.Type.(type) {\n\t\tcase *btf.Func:\n\t\t\tec.kfuncs[t.TypeName()] = t\n\t\tcase *btf.Var:\n\t\t\tec.ksyms[t.TypeName()] = struct{}{}\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"unexpected variable type in .ksyms: %T\", v)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// associateStructOpsRelocs handles `.struct_ops.link`\n// and associates the target function with the correct struct member in the map.\nfunc (ec *elfCode) associateStructOpsRelocs(progs map[string]*ProgramSpec) error {\n\tfor _, sec := range ec.sections {\n\t\tif sec.kind != structOpsSection {\n\t\t\tcontinue\n\t\t}\n\n\t\tuserData, err := sec.Data()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to read section data: %w\", err)\n\t\t}\n\n\t\t// Resolve the BTF datasec describing variables in this section.\n\t\tvar ds *btf.Datasec\n\t\tif err := ec.btf.TypeByName(sec.Name, &ds); err != nil {\n\t\t\treturn fmt.Errorf(\"datasec %s: %w\", sec.Name, err)\n\t\t}\n\n\t\t// Set flags for .struct_ops.link (BPF_F_LINK).\n\t\tflags := uint32(0)\n\t\tif sec.Name == structOpsLinkSec {\n\t\t\tflags = sys.BPF_F_LINK\n\t\t}\n\n\t\tfor _, vsi := range ds.Vars {\n\t\t\tuserSt, baseOff, err := ec.createStructOpsMap(vsi, userData, flags)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tif err := structOpsSetAttachTo(sec, baseOff, userSt, progs); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// createStructOpsMap() creates and registers a MapSpec for a struct_ops\nfunc (ec *elfCode) createStructOpsMap(vsi btf.VarSecinfo, userData []byte, flags uint32) (*btf.Struct, uint32, error) {\n\tvarType, ok := btf.As[*btf.Var](vsi.Type)\n\tif !ok {\n\t\treturn nil, 0, fmt.Errorf(\"vsi: expect var, got %T\", vsi.Type)\n\t}\n\n\tmapName := varType.Name\n\n\tuserSt, ok := btf.As[*btf.Struct](varType.Type)\n\tif !ok {\n\t\treturn nil, 0, fmt.Errorf(\"var %s: expect struct, got %T\", varType.Name, varType.Type)\n\t}\n\n\tuserSize := userSt.Size\n\tbaseOff := vsi.Offset\n\tif baseOff+userSize > uint32(len(userData)) {\n\t\treturn nil, 0, fmt.Errorf(\"%s exceeds section\", mapName)\n\t}\n\n\t// Register the MapSpec for this struct_ops instance if doesn't exist\n\tif _, exists := ec.maps[mapName]; exists {\n\t\treturn nil, 0, fmt.Errorf(\"struct_ops map %s: already exists\", mapName)\n\t}\n\n\tec.maps[mapName] = &MapSpec{\n\t\tName:       mapName,\n\t\tType:       StructOpsMap,\n\t\tKey:        &btf.Int{Size: 4},\n\t\tKeySize:    structOpsKeySize,\n\t\tValueSize:  userSize, // length of the user-struct type\n\t\tValue:      userSt,\n\t\tFlags:      flags,\n\t\tMaxEntries: 1,\n\t\tContents: []MapKV{\n\t\t\t{\n\t\t\t\tKey:   uint32(0),\n\t\t\t\tValue: append([]byte(nil), userData[baseOff:baseOff+userSize]...),\n\t\t\t},\n\t\t},\n\t}\n\n\treturn userSt, baseOff, nil\n}\n\ntype libbpfElfSectionDef struct {\n\tpattern     string\n\tprogramType sys.ProgType\n\tattachType  sys.AttachType\n\tflags       libbpfElfSectionFlag\n}\n\ntype libbpfElfSectionFlag uint32\n\n// The values correspond to enum sec_def_flags in libbpf.\nconst (\n\t_SEC_NONE libbpfElfSectionFlag = 0\n\n\t_SEC_EXP_ATTACH_OPT libbpfElfSectionFlag = 1 << (iota - 1)\n\t_SEC_ATTACHABLE\n\t_SEC_ATTACH_BTF\n\t_SEC_SLEEPABLE\n\t_SEC_XDP_FRAGS\n\t_SEC_USDT\n\n\t_SEC_ATTACHABLE_OPT = _SEC_ATTACHABLE | _SEC_EXP_ATTACH_OPT\n)\n\nfunc getProgType(sectionName string) (ProgramType, AttachType, uint32, string) {\n\t// Skip optional program marking for now.\n\tsectionName = strings.TrimPrefix(sectionName, \"?\")\n\n\tfor _, t := range elfSectionDefs {\n\t\textra, ok := matchSectionName(sectionName, t.pattern)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tprogramType := ProgramType(t.programType)\n\t\tattachType := AttachType(t.attachType)\n\n\t\tvar flags uint32\n\t\tif t.flags&_SEC_SLEEPABLE > 0 {\n\t\t\tflags |= sys.BPF_F_SLEEPABLE\n\t\t}\n\t\tif t.flags&_SEC_XDP_FRAGS > 0 {\n\t\t\tflags |= sys.BPF_F_XDP_HAS_FRAGS\n\t\t}\n\n\t\t// The libbpf documentation on program types states: 'The struct_ops attach\n\t\t// format supports struct_ops[.s]/<name> convention, but name is ignored and\n\t\t// it is recommended to just use plain SEC(\"struct_ops[.s]\").'\n\t\t//\n\t\t// Ignore any extra for struct_ops to match libbpf behaviour.\n\t\tif programType == StructOps {\n\t\t\textra = \"\"\n\t\t}\n\n\t\treturn programType, attachType, flags, extra\n\t}\n\n\treturn UnspecifiedProgram, AttachNone, 0, \"\"\n}\n\n// matchSectionName checks a section name against a pattern.\n//\n// It's behaviour mirrors that of libbpf's sec_def_matches.\nfunc matchSectionName(sectionName, pattern string) (extra string, found bool) {\n\thave, extra, found := strings.Cut(sectionName, \"/\")\n\twant := strings.TrimRight(pattern, \"+/\")\n\n\tif strings.HasSuffix(pattern, \"/\") {\n\t\t// Section name must have a slash and extra may be empty.\n\t\treturn extra, have == want && found\n\t} else if strings.HasSuffix(pattern, \"+\") {\n\t\t// Section name may have a slash and extra may be empty.\n\t\treturn extra, have == want\n\t}\n\n\t// Section name must have a prefix. extra is ignored.\n\treturn \"\", strings.HasPrefix(sectionName, pattern)\n}\n\nfunc (ec *elfCode) loadSectionRelocations(sec *elf.Section, symbols []elf.Symbol) (map[uint64]elf.Symbol, error) {\n\trels := make(map[uint64]elf.Symbol)\n\n\tif sec.Entsize < 16 {\n\t\treturn nil, fmt.Errorf(\"section %s: relocations are less than 16 bytes\", sec.Name)\n\t}\n\n\tr := bufio.NewReader(sec.Open())\n\tfor off := uint64(0); off < sec.Size; off += sec.Entsize {\n\t\tent := io.LimitReader(r, int64(sec.Entsize))\n\n\t\tvar rel elf.Rel64\n\t\tif binary.Read(ent, ec.ByteOrder, &rel) != nil {\n\t\t\treturn nil, fmt.Errorf(\"can't parse relocation at offset %v\", off)\n\t\t}\n\n\t\tsymNo := int(elf.R_SYM64(rel.Info) - 1)\n\t\tif symNo >= len(symbols) {\n\t\t\treturn nil, fmt.Errorf(\"offset %d: symbol %d doesn't exist\", off, symNo)\n\t\t}\n\n\t\tsymbol := symbols[symNo]\n\t\trels[rel.Off] = symbol\n\t}\n\n\treturn rels, nil\n}\n"
  },
  {
    "path": "elf_reader_test.go",
    "content": "package ebpf\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"flag\"\n\t\"fmt\"\n\t\"maps\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"syscall\"\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/kallsyms\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/google/go-cmp/cmp/cmpopts\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nvar csCmpOpts = cmp.Options{\n\t// Dummy Comparer that works with empty readers to support test cases.\n\tcmp.Comparer(func(a, b bytes.Reader) bool {\n\t\tif a.Len() == 0 && b.Len() == 0 {\n\t\t\treturn true\n\t\t}\n\t\treturn false\n\t}),\n\tcmpopts.IgnoreTypes(btf.Spec{}),\n\tcmpopts.IgnoreFields(CollectionSpec{}, \"ByteOrder\", \"Types\"),\n\tcmpopts.IgnoreFields(ProgramSpec{}, \"Instructions\", \"ByteOrder\"),\n\tcmpopts.IgnoreFields(MapSpec{}, \"Key\", \"Value\", \"Contents\"),\n\tcmpopts.IgnoreFields(VariableSpec{}, \"Type\", \"Value\"),\n\tcmpopts.IgnoreUnexported(ProgramSpec{}),\n}\n\nfunc TestLoadCollectionSpec(t *testing.T) {\n\tcoll := &CollectionSpec{\n\t\tMaps: map[string]*MapSpec{\n\t\t\t\"hash_map\": {\n\t\t\t\tName:       \"hash_map\",\n\t\t\t\tType:       Hash,\n\t\t\t\tKeySize:    4,\n\t\t\t\tValueSize:  8,\n\t\t\t\tMaxEntries: 1,\n\t\t\t\tFlags:      sys.BPF_F_NO_PREALLOC,\n\t\t\t},\n\t\t\t\"hash_map2\": {\n\t\t\t\tName:       \"hash_map2\",\n\t\t\t\tType:       Hash,\n\t\t\t\tKeySize:    4,\n\t\t\t\tValueSize:  8,\n\t\t\t\tMaxEntries: 2,\n\t\t\t},\n\t\t\t\"perf_event_array\": {\n\t\t\t\tName:       \"perf_event_array\",\n\t\t\t\tType:       PerfEventArray,\n\t\t\t\tMaxEntries: 4096,\n\t\t\t},\n\t\t\t\".bss\": {\n\t\t\t\tName:       \".bss\",\n\t\t\t\tType:       Array,\n\t\t\t\tKeySize:    4,\n\t\t\t\tValueSize:  4,\n\t\t\t\tMaxEntries: 1,\n\t\t\t},\n\t\t\t\".data\": {\n\t\t\t\tName:       \".data\",\n\t\t\t\tType:       Array,\n\t\t\t\tKeySize:    4,\n\t\t\t\tValueSize:  4,\n\t\t\t\tMaxEntries: 1,\n\t\t\t},\n\t\t\t\".data.test\": {\n\t\t\t\tName:       \".data.test\",\n\t\t\t\tType:       Array,\n\t\t\t\tKeySize:    4,\n\t\t\t\tValueSize:  4,\n\t\t\t\tMaxEntries: 1,\n\t\t\t},\n\t\t\t\".rodata\": {\n\t\t\t\tName:       \".rodata\",\n\t\t\t\tType:       Array,\n\t\t\t\tKeySize:    4,\n\t\t\t\tValueSize:  24,\n\t\t\t\tMaxEntries: 1,\n\t\t\t\tFlags:      sys.BPF_F_RDONLY_PROG,\n\t\t\t},\n\t\t\t\".rodata.test\": {\n\t\t\t\tName:       \".rodata.test\",\n\t\t\t\tType:       Array,\n\t\t\t\tKeySize:    4,\n\t\t\t\tValueSize:  4,\n\t\t\t\tMaxEntries: 1,\n\t\t\t\tFlags:      sys.BPF_F_RDONLY_PROG,\n\t\t\t},\n\t\t\t\".rodata.cst32\": {\n\t\t\t\tName:       \".rodata.cst32\",\n\t\t\t\tType:       Array,\n\t\t\t\tKeySize:    4,\n\t\t\t\tValueSize:  32,\n\t\t\t\tMaxEntries: 1,\n\t\t\t\tFlags:      sys.BPF_F_RDONLY_PROG,\n\t\t\t},\n\t\t},\n\t\tPrograms: map[string]*ProgramSpec{\n\t\t\t\"xdp_prog\": {\n\t\t\t\tName:        \"xdp_prog\",\n\t\t\t\tType:        XDP,\n\t\t\t\tSectionName: \"xdp\",\n\t\t\t\tAttachType:  AttachXDP,\n\t\t\t\tLicense:     \"MIT\",\n\t\t\t},\n\t\t\t\"no_relocation\": {\n\t\t\t\tName:        \"no_relocation\",\n\t\t\t\tType:        SocketFilter,\n\t\t\t\tSectionName: \"socket\",\n\t\t\t\tLicense:     \"MIT\",\n\t\t\t},\n\t\t\t\"asm_relocation\": {\n\t\t\t\tName:        \"asm_relocation\",\n\t\t\t\tType:        SocketFilter,\n\t\t\t\tSectionName: \"socket/2\",\n\t\t\t\tLicense:     \"MIT\",\n\t\t\t},\n\t\t\t\"data_sections\": {\n\t\t\t\tName:        \"data_sections\",\n\t\t\t\tType:        SocketFilter,\n\t\t\t\tSectionName: \"socket/3\",\n\t\t\t\tLicense:     \"MIT\",\n\t\t\t},\n\t\t\t\"global_fn3\": {\n\t\t\t\tName:        \"global_fn3\",\n\t\t\t\tType:        UnspecifiedProgram,\n\t\t\t\tSectionName: \"other\",\n\t\t\t\tLicense:     \"MIT\",\n\t\t\t},\n\t\t\t\"static_fn\": {\n\t\t\t\tName:        \"static_fn\",\n\t\t\t\tType:        UnspecifiedProgram,\n\t\t\t\tSectionName: \"static\",\n\t\t\t\tLicense:     \"MIT\",\n\t\t\t},\n\t\t\t\"anon_const\": {\n\t\t\t\tName:        \"anon_const\",\n\t\t\t\tType:        SocketFilter,\n\t\t\t\tSectionName: \"socket/4\",\n\t\t\t\tLicense:     \"MIT\",\n\t\t\t},\n\t\t},\n\t\tVariables: map[string]*VariableSpec{\n\t\t\t\"arg\":  {Name: \"arg\", SectionName: \".rodata\", Offset: 4},\n\t\t\t\"arg2\": {Name: \"arg2\", SectionName: \".rodata.test\", Offset: 0},\n\t\t\t\"arg3\": {Name: \"arg3\", SectionName: \".data.test\", Offset: 0},\n\t\t\t\"key1\": {Name: \"key1\", SectionName: \".bss\", Offset: 0},\n\t\t\t\"key2\": {Name: \"key2\", SectionName: \".data\", Offset: 0},\n\t\t\t\"key3\": {Name: \"key3\", SectionName: \".rodata\", Offset: 0},\n\t\t\t\"neg\":  {Name: \"neg\", SectionName: \".rodata\", Offset: 12},\n\t\t\t\"uneg\": {Name: \"uneg\", SectionName: \".rodata\", Offset: 8},\n\t\t},\n\t}\n\n\t// BTF-only maps.\n\tbtfOnly := map[string]*MapSpec{\n\t\t\"btf_pin\": {\n\t\t\tName:       \"btf_pin\",\n\t\t\tType:       Hash,\n\t\t\tKeySize:    4,\n\t\t\tValueSize:  8,\n\t\t\tMaxEntries: 1,\n\t\t\tPinning:    PinByName,\n\t\t},\n\t\t\"bpf_decl_map\": {\n\t\t\tName:       \"bpf_decl_map\",\n\t\t\tType:       Array,\n\t\t\tKeySize:    4,\n\t\t\tValueSize:  8,\n\t\t\tMaxEntries: 1,\n\t\t\tTags:       []string{\"a\", \"b\"},\n\t\t},\n\t\t\"btf_decl_map\": {\n\t\t\tName:       \"btf_decl_map\",\n\t\t\tType:       Array,\n\t\t\tKeySize:    4,\n\t\t\tValueSize:  8,\n\t\t\tMaxEntries: 1,\n\t\t\tTags:       []string{\"a\", \"b\"},\n\t\t},\n\t\t\"btf_outer_map\": {\n\t\t\tName:       \"btf_outer_map\",\n\t\t\tType:       ArrayOfMaps,\n\t\t\tKeySize:    4,\n\t\t\tValueSize:  4,\n\t\t\tMaxEntries: 1,\n\t\t\tInnerMap: &MapSpec{\n\t\t\t\tName:       \"btf_outer_map_inner\",\n\t\t\t\tType:       Hash,\n\t\t\t\tKeySize:    4,\n\t\t\t\tValueSize:  4,\n\t\t\t\tMaxEntries: 1,\n\t\t\t},\n\t\t},\n\t\t\"btf_outer_map_anon\": {\n\t\t\tName:       \"btf_outer_map_anon\",\n\t\t\tType:       ArrayOfMaps,\n\t\t\tKeySize:    4,\n\t\t\tValueSize:  4,\n\t\t\tMaxEntries: 1,\n\t\t\tInnerMap: &MapSpec{\n\t\t\t\tName:       \"btf_outer_map_anon_inner\",\n\t\t\t\tType:       Hash,\n\t\t\t\tKeySize:    4,\n\t\t\t\tValueSize:  4,\n\t\t\t\tMaxEntries: 1,\n\t\t\t},\n\t\t},\n\t\t\"btf_typedef_map\": {\n\t\t\tName:       \"btf_typedef_map\",\n\t\t\tType:       Array,\n\t\t\tKeySize:    4,\n\t\t\tValueSize:  8,\n\t\t\tMaxEntries: 1,\n\t\t},\n\t}\n\n\ttestutils.Files(t, testutils.Glob(t, \"testdata/loader-*.elf\"), func(t *testing.T, file string) {\n\t\tgot, err := LoadCollectionSpec(file)\n\t\tqt.Assert(t, qt.IsNil(err))\n\n\t\t// BTF map definition contains a value type, but the size should remain 0.\n\t\t// The value type needs to be reflected in the MapSpec.\n\t\tqt.Assert(t, qt.Equals(got.Maps[\"perf_event_array\"].ValueSize, 0))\n\t\tqt.Assert(t, qt.IsNotNil(got.Maps[\"perf_event_array\"].Value))\n\n\t\t// Copy and extend the CollectionSpec with BTF-only objects.\n\t\twant := coll.Copy()\n\t\tmaps.Copy(want.Maps, btfOnly)\n\n\t\ttestLoadCollectionSpec(t, got, want)\n\t})\n\n\ttestutils.Files(t, testutils.Glob(t, \"testdata/loader_nobtf-*.elf\"), func(t *testing.T, file string) {\n\t\tgot, err := LoadCollectionSpec(file)\n\t\tqt.Assert(t, qt.IsNil(err))\n\n\t\ttestLoadCollectionSpec(t, got, coll.Copy())\n\t})\n}\n\nfunc testLoadCollectionSpec(t *testing.T, got, want *CollectionSpec) {\n\tt.Helper()\n\n\tqt.Assert(t, qt.CmpEquals(got, want, csCmpOpts))\n\n\tcoll, err := newCollection(t, got, &CollectionOptions{\n\t\tMaps:     MapOptions{PinPath: testutils.TempBPFFS(t)},\n\t\tPrograms: ProgramOptions{LogLevel: LogLevelBranch},\n\t})\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tret := mustRun(t, coll.Programs[\"xdp_prog\"], nil)\n\tqt.Assert(t, qt.Equals(ret, 7))\n}\n\nfunc BenchmarkELFLoader(b *testing.B) {\n\tb.ReportAllocs()\n\n\tfor b.Loop() {\n\t\t_, _ = LoadCollectionSpec(\"testdata/loader-el.elf\")\n\t}\n}\n\nfunc TestDataSections(t *testing.T) {\n\tfile := testutils.NativeFile(t, \"testdata/loader-%s.elf\")\n\tcoll, err := LoadCollectionSpec(file)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tt.Log(coll.Programs[\"data_sections\"].Instructions)\n\n\tvar obj struct {\n\t\tProgram *Program `ebpf:\"data_sections\"`\n\t}\n\n\tmustLoadAndAssign(t, coll, &obj, nil)\n\tdefer obj.Program.Close()\n\n\tret := mustRun(t, obj.Program, nil)\n\tif ret != 0 {\n\t\tt.Error(\"BPF assertion failed on line\", ret)\n\t}\n}\n\nfunc TestInlineASMConstant(t *testing.T) {\n\tfile := testutils.NativeFile(t, \"testdata/loader-%s.elf\")\n\tcoll, err := LoadCollectionSpec(file)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tspec := coll.Programs[\"asm_relocation\"]\n\tif spec.Instructions[0].Reference() != \"MY_CONST\" {\n\t\tt.Fatal(\"First instruction is not a reference to MY_CONST\")\n\t}\n\n\t// -1 is used by the loader to find unrewritten maps.\n\tspec.Instructions[0].Constant = -1\n\n\tt.Log(spec.Instructions)\n\n\tvar obj struct {\n\t\tProgram *Program `ebpf:\"asm_relocation\"`\n\t}\n\n\tmustLoadAndAssign(t, coll, &obj, nil)\n\tobj.Program.Close()\n}\n\nfunc TestFreezeRodata(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.9\", \"sk_lookup program type\")\n\n\tfile := testutils.NativeFile(t, \"testdata/constants-%s.elf\")\n\tspec, err := LoadCollectionSpec(file)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tvar obj struct {\n\t\tProgram *Program `ebpf:\"freeze_rodata\"`\n\t}\n\n\tqt.Assert(t, qt.IsNil(spec.Variables[\"ret\"].Set(uint32(1))))\n\n\tmustLoadAndAssign(t, spec, &obj, nil)\n\tobj.Program.Close()\n}\n\nfunc TestCollectionSpecDetach(t *testing.T) {\n\tcoll := Collection{\n\t\tMaps: map[string]*Map{\n\t\t\t\"foo\": new(Map),\n\t\t},\n\t\tPrograms: map[string]*Program{\n\t\t\t\"bar\": new(Program),\n\t\t},\n\t}\n\n\tfoo := coll.DetachMap(\"foo\")\n\tif foo == nil {\n\t\tt.Error(\"Program not returned from DetachMap\")\n\t}\n\n\tif _, ok := coll.Programs[\"foo\"]; ok {\n\t\tt.Error(\"DetachMap doesn't remove map from Maps\")\n\t}\n\n\tbar := coll.DetachProgram(\"bar\")\n\tif bar == nil {\n\t\tt.Fatal(\"Program not returned from DetachProgram\")\n\t}\n\n\tif _, ok := coll.Programs[\"bar\"]; ok {\n\t\tt.Error(\"DetachProgram doesn't remove program from Programs\")\n\t}\n}\n\nfunc TestLoadInvalidMap(t *testing.T) {\n\tfile := testutils.NativeFile(t, \"testdata/invalid_map-%s.elf\")\n\tcs, err := LoadCollectionSpec(file)\n\tif err != nil {\n\t\tt.Fatal(\"Can't load CollectionSpec\", err)\n\t}\n\n\tms, ok := cs.Maps[\"invalid_map\"]\n\tif !ok {\n\t\tt.Fatal(\"invalid_map not found in CollectionSpec\")\n\t}\n\n\tm, err := NewMap(ms)\n\tt.Log(err)\n\tif err == nil {\n\t\tm.Close()\n\t\tt.Fatal(\"Creating a Map from a MapSpec with non-zero Extra is expected to fail.\")\n\t}\n}\n\nfunc TestLoadInvalidMapMissingSymbol(t *testing.T) {\n\tfile := testutils.NativeFile(t, \"testdata/invalid_map_static-%s.elf\")\n\t_, err := LoadCollectionSpec(file)\n\tt.Log(err)\n\tif err == nil {\n\t\tt.Fatal(\"Loading a map with static qualifier should fail\")\n\t}\n}\n\nfunc TestLoadInitializedBTFMap(t *testing.T) {\n\ttestutils.Files(t, testutils.Glob(t, \"testdata/btf_map_init-*.elf\"), func(t *testing.T, file string) {\n\t\tcoll, err := LoadCollectionSpec(file)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tt.Run(\"NewCollection\", func(t *testing.T) {\n\t\t\t_, err := newCollection(t, coll, nil)\n\t\t\ttestutils.SkipIfNotSupported(t, err)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(\"NewCollection failed:\", err)\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"prog_array\", func(t *testing.T) {\n\t\t\tm, ok := coll.Maps[\"prog_array_init\"]\n\t\t\tif !ok {\n\t\t\t\tt.Fatal(\"map prog_array_init not found in program\")\n\t\t\t}\n\n\t\t\tif len(m.Contents) != 1 {\n\t\t\t\tt.Error(\"expecting exactly 1 item in MapSpec contents\")\n\t\t\t}\n\n\t\t\tp := m.Contents[0]\n\t\t\tif cmp.Equal(p.Key, 1) {\n\t\t\t\tt.Errorf(\"expecting MapSpec entry Key to equal 1, got %v\", p.Key)\n\t\t\t}\n\n\t\t\tif _, ok := p.Value.(string); !ok {\n\t\t\t\tt.Errorf(\"expecting MapSpec entry Value to be a string, got %T\", p.Value)\n\t\t\t}\n\n\t\t\tif p.Value != \"tail_1\" {\n\t\t\t\tt.Errorf(\"expected MapSpec entry Value 'tail_1', got: %s\", p.Value)\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"array_of_maps\", func(t *testing.T) {\n\t\t\tm, ok := coll.Maps[\"outer_map_init\"]\n\t\t\tif !ok {\n\t\t\t\tt.Fatal(\"map outer_map_init not found in program\")\n\t\t\t}\n\n\t\t\tif len(m.Contents) != 1 {\n\t\t\t\tt.Error(\"expecting exactly 1 item in MapSpec contents\")\n\t\t\t}\n\n\t\t\tif m.Key == nil {\n\t\t\t\tt.Error(\"Expected non-nil key\")\n\t\t\t}\n\n\t\t\tif m.Value == nil {\n\t\t\t\tt.Error(\"Expected non-nil value\")\n\t\t\t}\n\n\t\t\tif m.InnerMap.Key == nil {\n\t\t\t\tt.Error(\"Expected non-nil InnerMap key\")\n\t\t\t}\n\n\t\t\tif m.InnerMap.Value == nil {\n\t\t\t\tt.Error(\"Expected non-nil InnerMap value\")\n\t\t\t}\n\n\t\t\tp := m.Contents[0]\n\t\t\tif cmp.Equal(p.Key, 1) {\n\t\t\t\tt.Errorf(\"expecting MapSpec entry Key to equal 1, got %v\", p.Key)\n\t\t\t}\n\n\t\t\tif _, ok := p.Value.(string); !ok {\n\t\t\t\tt.Errorf(\"expecting MapSpec entry Value to be a string, got %T\", p.Value)\n\t\t\t}\n\n\t\t\tif p.Value != \"inner_map\" {\n\t\t\t\tt.Errorf(\"expected MapSpec entry Value 'inner_map', got: %s\", p.Value)\n\t\t\t}\n\t\t})\n\t})\n}\n\nfunc TestLoadInvalidInitializedBTFMap(t *testing.T) {\n\tfile := testutils.NativeFile(t, \"testdata/invalid_btf_map_init-%s.elf\")\n\t_, err := LoadCollectionSpec(file)\n\tt.Log(err)\n\tif !errors.Is(err, internal.ErrNotSupported) {\n\t\tt.Fatal(\"Loading an initialized BTF map should be unsupported\")\n\t}\n}\n\nfunc TestStringSection(t *testing.T) {\n\tfile := testutils.NativeFile(t, \"testdata/strings-%s.elf\")\n\tspec, err := LoadCollectionSpec(file)\n\tif err != nil {\n\t\tt.Fatalf(\"load collection spec: %s\", err)\n\t}\n\n\tfor name := range spec.Maps {\n\t\tt.Log(name)\n\t}\n\n\tstrMap := spec.Maps[\".rodata.str1.1\"]\n\tif strMap == nil {\n\t\tt.Fatal(\"Unable to find map '.rodata.str1.1' in loaded collection\")\n\t}\n\n\tif !strMap.readOnly() {\n\t\tt.Fatal(\"Read only data maps should be frozen\")\n\t}\n\n\tif strMap.Flags != sys.BPF_F_RDONLY_PROG {\n\t\tt.Fatal(\"Read only data maps should have the prog-read-only flag set\")\n\t}\n\n\tcoll, err := newCollection(t, spec, nil)\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatalf(\"new collection: %s\", err)\n\t}\n\n\tprog := coll.Programs[\"filter\"]\n\tif prog == nil {\n\t\tt.Fatal(\"program not found\")\n\t}\n\n\ttestMap := coll.Maps[\"my_map\"]\n\tif testMap == nil {\n\t\tt.Fatal(\"test map not found\")\n\t}\n\n\t_, err = prog.Run(&RunOptions{\n\t\tData: internal.EmptyBPFContext, // Min size for XDP programs\n\t})\n\tif err != nil {\n\t\tt.Fatalf(\"prog run: %s\", err)\n\t}\n\n\tkey := []byte(\"This string is allocated in the string section\\n\\x00\")\n\tvar value uint32\n\tif err = testMap.Lookup(&key, &value); err != nil {\n\t\tt.Fatalf(\"test map lookup: %s\", err)\n\t}\n\n\tif value != 1 {\n\t\tt.Fatal(\"Test map value not 1!\")\n\t}\n}\n\nfunc TestLoadRawTracepoint(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"4.17\", \"BPF_RAW_TRACEPOINT API\")\n\n\tfile := testutils.NativeFile(t, \"testdata/raw_tracepoint-%s.elf\")\n\tspec, err := LoadCollectionSpec(file)\n\tif err != nil {\n\t\tt.Fatal(\"Can't parse ELF:\", err)\n\t}\n\n\tcoll, err := NewCollectionWithOptions(spec, CollectionOptions{\n\t\tPrograms: ProgramOptions{\n\t\t\tLogLevel: LogLevelBranch,\n\t\t},\n\t})\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(\"Can't create collection:\", err)\n\t}\n\n\tcoll.Close()\n}\n\nfunc TestTailCall(t *testing.T) {\n\tfile := testutils.NativeFile(t, \"testdata/btf_map_init-%s.elf\")\n\tspec, err := LoadCollectionSpec(file)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tvar obj struct {\n\t\tTailMain  *Program `ebpf:\"tail_main\"`\n\t\tProgArray *Map     `ebpf:\"prog_array_init\"`\n\t\t// Windows evicts programs from the tail call array when the last\n\t\t// user space reference is closed. This is not the case on Linux.\n\t\tTail *Program `ebpf:\"tail_1\"`\n\t}\n\n\terr = loadAndAssign(t, spec, &obj, nil)\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer obj.TailMain.Close()\n\tdefer obj.Tail.Close()\n\tdefer obj.ProgArray.Close()\n\n\tret := mustRun(t, obj.Tail, nil)\n\n\t// Expect the tail_1 tail call to be taken, returning value 42.\n\tif ret != 42 {\n\t\tt.Fatalf(\"Expected tail call to return value 42, got %d\", ret)\n\t}\n}\n\nfunc TestKconfig(t *testing.T) {\n\tfile := testutils.NativeFile(t, \"testdata/kconfig-%s.elf\")\n\tspec, err := LoadCollectionSpec(file)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tvar obj struct {\n\t\tMain *Program `ebpf:\"kconfig\"`\n\t}\n\n\terr = spec.LoadAndAssign(&obj, nil)\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer obj.Main.Close()\n\n\tret := mustRun(t, obj.Main, nil)\n\tqt.Assert(t, qt.Equals(ret, 0), qt.Commentf(\"Failed assertion at line %d in testdata/kconfig.c\", ret))\n}\n\nfunc TestKsym(t *testing.T) {\n\tfile := testutils.NativeFile(t, \"testdata/ksym-%s.elf\")\n\tspec, err := LoadCollectionSpec(file)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tvar obj struct {\n\t\tMain     *Program `ebpf:\"ksym_test\"`\n\t\tArrayMap *Map     `ebpf:\"array_map\"`\n\t}\n\n\terr = spec.LoadAndAssign(&obj, nil)\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer obj.Main.Close()\n\tdefer obj.ArrayMap.Close()\n\n\tmustRun(t, obj.Main, nil)\n\n\tksyms := map[string]uint64{\n\t\t\"bpf_init\":       0,\n\t\t\"bpf_trace_run1\": 0,\n\t}\n\n\tqt.Assert(t, qt.IsNil(kallsyms.AssignAddresses(ksyms)))\n\tqt.Assert(t, qt.Not(qt.Equals(ksyms[\"bpf_init\"], 0)))\n\tqt.Assert(t, qt.Not(qt.Equals(ksyms[\"bpf_trace_run1\"], 0)))\n\n\tvar value uint64\n\tqt.Assert(t, qt.IsNil(obj.ArrayMap.Lookup(uint32(0), &value)))\n\tqt.Assert(t, qt.Equals(value, ksyms[\"bpf_init\"]))\n\n\tqt.Assert(t, qt.IsNil(obj.ArrayMap.Lookup(uint32(1), &value)))\n\tqt.Assert(t, qt.Equals(value, ksyms[\"bpf_trace_run1\"]))\n}\n\nfunc TestKsymWeakMissing(t *testing.T) {\n\tfile := testutils.NativeFile(t, \"testdata/ksym-%s.elf\")\n\tspec, err := LoadCollectionSpec(file)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tvar obj struct {\n\t\tMain *Program `ebpf:\"ksym_missing_test\"`\n\t}\n\n\terr = spec.LoadAndAssign(&obj, nil)\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer obj.Main.Close()\n\n\tret := mustRun(t, obj.Main, nil)\n\tqt.Assert(t, qt.Equals(ret, 1))\n}\n\nfunc TestKfunc(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.18\", \"kfunc support\")\n\tfile := testutils.NativeFile(t, \"testdata/kfunc-%s.elf\")\n\tspec, err := LoadCollectionSpec(file)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tvar obj struct {\n\t\tMain *Program `ebpf:\"call_kfunc\"`\n\t}\n\n\terr = spec.LoadAndAssign(&obj, nil)\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatalf(\"%+v\", err)\n\t}\n\tdefer obj.Main.Close()\n\n\tret := mustRun(t, obj.Main, nil)\n\n\tif ret != 1 {\n\t\tt.Fatalf(\"Expected kfunc to return value 1, got %d\", ret)\n\t}\n}\n\nfunc TestWeakKfunc(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.18\", \"kfunc support\")\n\n\t// CAP_SYS_ADMIN is required to load kfuncs implemented in kernel modules.\n\t// Assert that when kfuncs are weak, loading still works without the capability.\n\t// CAP_BPF and CAP_PERFMON are still required to load BPF raw tracepoints programs\n\t// such as the one in this test.\n\ttestutils.WithCapabilities(t, []testutils.Capability{testutils.CAP_BPF, testutils.CAP_PERFMON}, func() {\n\t\tfile := testutils.NativeFile(t, \"testdata/kfunc-%s.elf\")\n\t\tspec, err := LoadCollectionSpec(file)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tvar obj struct {\n\t\t\tMissing *Program `ebpf:\"weak_kfunc_missing\"`\n\t\t\tCalling *Program `ebpf:\"call_weak_kfunc\"`\n\t\t}\n\n\t\terr = spec.LoadAndAssign(&obj, nil)\n\t\ttestutils.SkipIfNotSupported(t, err)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"%+v\", err)\n\t\t}\n\t\tdefer obj.Missing.Close()\n\t\tdefer obj.Calling.Close()\n\t})\n}\n\nfunc TestInvalidKfunc(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.18\", \"kfunc support\")\n\trequireTestmod(t)\n\n\tfile := testutils.NativeFile(t, \"testdata/invalid-kfunc-%s.elf\")\n\tcoll, err := LoadCollection(file)\n\tif err == nil {\n\t\tcoll.Close()\n\t\tt.Fatal(\"Expected an error\")\n\t}\n\n\tvar ike *incompatibleKfuncError\n\tif !errors.As(err, &ike) {\n\t\tt.Fatalf(\"Expected an error wrapping incompatibleKfuncError, got %s\", err)\n\t}\n}\n\nfunc TestKfuncKmod(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.18\", \"Kernel module function calls\")\n\trequireTestmod(t)\n\n\tfile := testutils.NativeFile(t, \"testdata/kfunc-kmod-%s.elf\")\n\tspec, err := LoadCollectionSpec(file)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tvar obj struct {\n\t\tMain *Program `ebpf:\"call_kfunc\"`\n\t}\n\n\terr = spec.LoadAndAssign(&obj, nil)\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatalf(\"%v+\", err)\n\t}\n\tdefer obj.Main.Close()\n\n\tret := mustRun(t, obj.Main, nil)\n\n\tif ret != 1 {\n\t\tt.Fatalf(\"Expected kfunc to return value 1, got %d\", ret)\n\t}\n}\n\nfunc TestSubprogRelocation(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.13\", \"bpf_for_each_map_elem\")\n\n\tfile := testutils.NativeFile(t, \"testdata/subprog_reloc-%s.elf\")\n\tspec, err := LoadCollectionSpec(file)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tvar obj struct {\n\t\tMain    *Program `ebpf:\"fp_relocation\"`\n\t\tHashMap *Map     `ebpf:\"hash_map\"`\n\t}\n\n\terr = loadAndAssign(t, spec, &obj, nil)\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer obj.Main.Close()\n\tdefer obj.HashMap.Close()\n\n\tret := mustRun(t, obj.Main, nil)\n\n\tif ret != 42 {\n\t\tt.Fatalf(\"Expected subprog reloc to return value 42, got %d\", ret)\n\t}\n}\n\nfunc TestUnassignedProgArray(t *testing.T) {\n\tfile := testutils.NativeFile(t, \"testdata/btf_map_init-%s.elf\")\n\tspec, err := LoadCollectionSpec(file)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// tail_main references a ProgArray that is not being assigned\n\t// to this struct. Normally, this would clear all its entries\n\t// and make any tail calls into the ProgArray result in a miss.\n\t// The library needs to explicitly refuse such operations.\n\tvar obj struct {\n\t\tTailMain *Program `ebpf:\"tail_main\"`\n\t\t// ProgArray *Map     `ebpf:\"prog_array_init\"`\n\t}\n\n\terr = loadAndAssign(t, spec, &obj, nil)\n\ttestutils.SkipIfNotSupported(t, err)\n\tdefer obj.TailMain.Close()\n\tqt.Assert(t, qt.IsNotNil(err))\n}\n\nfunc TestIPRoute2Compat(t *testing.T) {\n\tfile := testutils.NativeFile(t, \"testdata/iproute2_map_compat-%s.elf\")\n\tspec, err := LoadCollectionSpec(file)\n\tif err != nil {\n\t\tt.Fatal(\"Can't parse ELF:\", err)\n\t}\n\n\tms, ok := spec.Maps[\"hash_map\"]\n\tif !ok {\n\t\tt.Fatal(\"Map hash_map not found\")\n\t}\n\n\tvar id, pinning, innerID, innerIndex uint32\n\n\tif ms.Extra == nil {\n\t\tt.Fatal(\"missing extra bytes\")\n\t}\n\n\tswitch {\n\tcase binary.Read(ms.Extra, spec.ByteOrder, &id) != nil:\n\t\tt.Fatal(\"missing id\")\n\tcase binary.Read(ms.Extra, spec.ByteOrder, &pinning) != nil:\n\t\tt.Fatal(\"missing pinning\")\n\tcase binary.Read(ms.Extra, spec.ByteOrder, &innerID) != nil:\n\t\tt.Fatal(\"missing inner_id\")\n\tcase binary.Read(ms.Extra, spec.ByteOrder, &innerIndex) != nil:\n\t\tt.Fatal(\"missing inner_idx\")\n\t}\n\n\tif id != 0 || innerID != 0 || innerIndex != 0 {\n\t\tt.Fatal(\"expecting id, inner_id and inner_idx to be zero\")\n\t}\n\n\tif pinning != 2 {\n\t\tt.Fatal(\"expecting pinning field to be 2 (PIN_GLOBAL_NS)\")\n\t}\n\n\t// iproute2 (tc) pins maps in /sys/fs/bpf/tc/globals with PIN_GLOBAL_NS,\n\t// which needs to be configured in this library using MapOptions.PinPath.\n\t// For the sake of the test, we use a tempdir on bpffs below.\n\tms.Pinning = PinByName\n\n\tcoll, err := NewCollectionWithOptions(spec, CollectionOptions{\n\t\tMaps: MapOptions{\n\t\t\tPinPath: testutils.TempBPFFS(t),\n\t\t},\n\t})\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(\"Can't create collection:\", err)\n\t}\n\n\tcoll.Close()\n}\n\nfunc TestArena(t *testing.T) {\n\tfile := testutils.NativeFile(t, \"testdata/arena-%s.elf\")\n\tcoll, err := LoadCollectionSpec(file)\n\tqt.Assert(t, qt.IsNil(err))\n\n\twant := &CollectionSpec{\n\t\tMaps: map[string]*MapSpec{\n\t\t\t\"arena\": {\n\t\t\t\tName:       \"arena\",\n\t\t\t\tType:       Arena,\n\t\t\t\tMaxEntries: 100,\n\t\t\t\tFlags:      sys.BPF_F_MMAPABLE,\n\t\t\t\tMapExtra:   1 << 44,\n\t\t\t},\n\t\t},\n\t\tPrograms:  map[string]*ProgramSpec{},\n\t\tVariables: map[string]*VariableSpec{},\n\t}\n\tqt.Assert(t, qt.CmpEquals(coll, want, csCmpOpts))\n\n\ttestutils.SkipOnOldKernel(t, \"6.9\", \"arena maps\")\n\tmustNewCollection(t, coll, nil)\n}\n\nfunc TestStructOps(t *testing.T) {\n\tfile := testutils.NativeFile(t, \"testdata/struct_ops-%s.elf\")\n\tcoll, err := LoadCollectionSpec(file)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tuserData := []byte{\n\t\t// test_1 func ptr (8B)\n\t\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t\t// test_2 func ptr (8B)\n\t\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t\t// data (4B) + padding (4B)\n\t\t0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00,\n\t}\n\n\twant := &CollectionSpec{\n\t\tMaps: map[string]*MapSpec{\n\t\t\t\"testmod_ops\": {\n\t\t\t\tName:       \"testmod_ops\",\n\t\t\t\tType:       StructOpsMap,\n\t\t\t\tMaxEntries: 1,\n\t\t\t\tFlags:      sys.BPF_F_LINK,\n\t\t\t\tKey:        &btf.Int{Size: 4},\n\t\t\t\tKeySize:    4,\n\t\t\t\tValueSize:  24,\n\t\t\t\tValue: &btf.Struct{\n\t\t\t\t\tName: \"bpf_testmod_ops\",\n\t\t\t\t\tSize: 24,\n\t\t\t\t\tMembers: []btf.Member{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tName: \"test_1\",\n\t\t\t\t\t\t\tType: &btf.Pointer{\n\t\t\t\t\t\t\t\tTarget: &btf.FuncProto{\n\t\t\t\t\t\t\t\t\tParams: []btf.FuncParam{},\n\t\t\t\t\t\t\t\t\tReturn: &btf.Int{Name: \"int\", Size: 4, Encoding: btf.Signed}}},\n\t\t\t\t\t\t\tOffset: 0,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tName: \"test_2\",\n\t\t\t\t\t\t\tType: &btf.Pointer{\n\t\t\t\t\t\t\t\tTarget: &btf.FuncProto{\n\t\t\t\t\t\t\t\t\tParams: []btf.FuncParam{\n\t\t\t\t\t\t\t\t\t\t{Type: &btf.Int{Name: \"int\", Size: 4, Encoding: btf.Signed}},\n\t\t\t\t\t\t\t\t\t\t{Type: &btf.Int{Name: \"int\", Size: 4, Encoding: btf.Signed}},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tReturn: (*btf.Void)(nil),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tOffset: 64,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tName:   \"data\",\n\t\t\t\t\t\t\tType:   &btf.Int{Name: \"int\", Size: 4, Encoding: btf.Signed},\n\t\t\t\t\t\t\tOffset: 128, // bits\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tContents: []MapKV{\n\t\t\t\t\t{\n\t\t\t\t\t\tKey:   uint32(0),\n\t\t\t\t\t\tValue: userData,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tPrograms: map[string]*ProgramSpec{\n\t\t\t\"test_1\": {\n\t\t\t\tName:        \"test_1\",\n\t\t\t\tType:        StructOps,\n\t\t\t\tAttachTo:    \"bpf_testmod_ops:test_1\",\n\t\t\t\tLicense:     \"GPL\",\n\t\t\t\tSectionName: \"struct_ops/test_1\",\n\t\t\t\tInstructions: asm.Instructions{\n\t\t\t\t\tasm.Mov.Imm(asm.R0, 0),\n\t\t\t\t\tasm.Return(),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tVariables: map[string]*VariableSpec{},\n\t}\n\n\ttestModOps, ok := coll.Maps[\"testmod_ops\"]\n\tif !ok {\n\t\tt.Fatalf(\"testmod_ops doesn't exist\")\n\t}\n\n\tdata, ok := testModOps.Contents[0].Value.([]byte)\n\tif !ok {\n\t\tt.Fatalf(\"Contents[0].Value should be an array of byte\")\n\t}\n\n\tqt.Assert(t, qt.CmpEquals(coll.Programs, want.Programs, csCmpOpts))\n\tqt.Assert(t, qt.CmpEquals(coll.Maps, want.Maps, csCmpOpts))\n\tqt.Assert(t, qt.CmpEquals(testModOps.Value, want.Maps[\"testmod_ops\"].Value, csCmpOpts))\n\tqt.Assert(t, qt.CmpEquals(data, userData, csCmpOpts))\n}\n\nvar (\n\telfPath    = flag.String(\"elfs\", os.Getenv(\"CI_KERNEL_SELFTESTS\"), \"`Path` containing libbpf-compatible ELFs (defaults to $CI_KERNEL_SELFTESTS)\")\n\telfPattern = flag.String(\"elf-pattern\", \"*.o\", \"Glob `pattern` for object files that should be tested\")\n)\n\nfunc TestLibBPFCompat(t *testing.T) {\n\tif *elfPath == \"\" {\n\t\t// Specify the path to the directory containing the eBPF for\n\t\t// the kernel's selftests if you want to run this test.\n\t\t// As of 5.2 that is tools/testing/selftests/bpf/\n\t\tt.Skip(\"No path specified\")\n\t}\n\n\tload := func(t *testing.T, spec *CollectionSpec, opts CollectionOptions, valid bool) {\n\t\t// Disable retrying a program load with the log enabled, it leads\n\t\t// to OOM kills.\n\t\topts.Programs.LogDisabled = true\n\n\t\tcoll, err := NewCollectionWithOptions(spec, opts)\n\t\ttestutils.SkipIfNotSupported(t, err)\n\t\tvar errno syscall.Errno\n\t\tif errors.As(err, &errno) {\n\t\t\t// This error is most likely from a syscall and caused by us not\n\t\t\t// replicating some fixups done in the selftests or the test\n\t\t\t// intentionally failing. This is expected, so skip the test\n\t\t\t// instead of failing.\n\t\t\tt.Skip(\"Skipping since the kernel rejected the program:\", err)\n\t\t}\n\t\tif err == nil {\n\t\t\tcoll.Close()\n\t\t}\n\t\tif !valid {\n\t\t\tif err == nil {\n\t\t\t\tt.Fatal(\"Expected an error during load\")\n\t\t\t}\n\t\t} else if err != nil {\n\t\t\tt.Fatal(\"Error during loading:\", err)\n\t\t}\n\t}\n\n\tfiles := testutils.Glob(t, filepath.Join(*elfPath, *elfPattern),\n\t\t// These files are only used as a source of btf.\n\t\t\"btf__core_reloc_*\",\n\t)\n\n\ttestutils.Files(t, files, func(t *testing.T, path string) {\n\t\tname := selftestName(path)\n\t\tswitch name {\n\t\tcase \"test_map_in_map\", \"test_select_reuseport_kern\":\n\t\t\tt.Skip(\"Skipping due to missing InnerMap in map definition\")\n\t\tcase \"test_core_autosize\":\n\t\t\tt.Skip(\"Skipping since the test generates dynamic BTF\")\n\t\tcase \"test_static_linked\":\n\t\t\tt.Skip(\"Skipping since .text contains 'subprog' twice\")\n\t\tcase \"netif_receive_skb\",\n\t\t\t\"local_kptr_stash\",\n\t\t\t\"local_kptr_stash_fail\",\n\t\t\t\"type_cast\",\n\t\t\t\"preempted_bpf_ma_op\",\n\t\t\t\"percpu_alloc_fail\":\n\t\t\t// Error message like\n\t\t\t//    fixup for CORERelocation(local_type_id, Struct:\"bin_data\"[0],\n\t\t\t//    local_id=27): invalid immediate 31, expected 27 (fixup: local_type_id=27->1)\n\t\t\t// See https://github.com/cilium/ebpf/issues/739\n\t\t\tt.Skip(\"Skipping due to bug in libbpf type deduplication\")\n\t\tcase \"test_usdt\", \"test_urandom_usdt\", \"test_usdt_multispec\":\n\t\t\tt.Skip(\"Skipping due to missing support for usdt.bpf.h\")\n\t\tcase \"lsm_cgroup\", \"bpf_iter_ipv6_route\", \"test_core_extern\",\n\t\t\t\"profiler1\", \"profiler2\", \"profiler3\":\n\t\t\tt.Skip(\"Skipping due to using weak CONFIG_* variables\")\n\t\tcase \"linked_maps\", \"linked_maps1\", \"linked_maps2\", \"linked_funcs1\", \"linked_funcs2\",\n\t\t\t\"test_subskeleton\", \"test_subskeleton_lib\":\n\t\t\tt.Skip(\"Skipping due to relying on cross ELF linking\")\n\t\tcase \"test_log_fixup\":\n\t\t\tt.Skip(\"Skipping due to intentionally broken CO-RE relocations\")\n\t\t}\n\n\t\tt.Parallel()\n\n\t\tspec, err := LoadCollectionSpec(path)\n\t\ttestutils.SkipIfNotSupported(t, err)\n\t\tif errors.Is(err, errUnsupportedBinding) {\n\t\t\tt.Skip(err)\n\t\t}\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tswitch name {\n\t\tcase \"test_sk_assign\":\n\t\t\t// Test contains a legacy iproute2 bpf_elf_map definition.\n\t\t\tfor _, m := range spec.Maps {\n\t\t\t\tif m.Extra == nil || m.Extra.Len() == 0 {\n\t\t\t\t\tt.Fatalf(\"Expected extra bytes in map %s\", m.Name)\n\t\t\t\t}\n\t\t\t\tm.Extra = nil\n\t\t\t}\n\n\t\tcase \"fexit_bpf2bpf\",\n\t\t\t\"freplace_get_constant\",\n\t\t\t\"freplace_global_func\":\n\t\t\tloadTargetProgram(t, spec, \"test_pkt_access.bpf.o\", \"test_pkt_access\")\n\n\t\tcase \"freplace_cls_redirect\":\n\t\t\tloadTargetProgram(t, spec, \"test_cls_redirect.bpf.o\", \"cls_redirect\")\n\n\t\tcase \"test_trace_ext\":\n\t\t\tloadTargetProgram(t, spec, \"test_pkt_md_access.bpf.o\", \"test_pkt_md_access\")\n\n\t\tcase \"freplace_progmap\":\n\t\t\tloadTargetProgram(t, spec, \"xdp_dummy.bpf.o\", \"xdp_dummy_prog\")\n\n\t\t\tif prog := spec.Programs[\"xdp_cpumap_prog\"]; prog.AttachTo == \"\" {\n\t\t\t\tprog.AttachTo = \"xdp_dummy_prog\"\n\t\t\t}\n\n\t\tcase \"freplace_attach_probe\":\n\t\t\t// Looks like the test should have a target, but 6.6 selftests don't\n\t\t\t// seem to be using it.\n\t\t}\n\n\t\tvar opts CollectionOptions\n\t\tfor _, mapSpec := range spec.Maps {\n\t\t\tif mapSpec.Pinning != PinNone {\n\t\t\t\topts.Maps.PinPath = testutils.TempBPFFS(t)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tcoreFiles := sourceOfBTF(t, path)\n\t\tif len(coreFiles) == 0 {\n\t\t\t// NB: test_core_reloc_kernel.o doesn't have dedicated BTF and\n\t\t\t// therefore goes via this code path.\n\t\t\tload(t, spec, opts, true)\n\t\t\treturn\n\t\t}\n\n\t\tfor _, coreFile := range coreFiles {\n\t\t\tname := selftestName(coreFile)\n\t\t\tt.Run(name, func(t *testing.T) {\n\t\t\t\t// Some files like btf__core_reloc_arrays___err_too_small.o\n\t\t\t\t// trigger an error on purpose. Use the name to infer whether\n\t\t\t\t// the test should succeed.\n\t\t\t\tvar valid bool\n\t\t\t\tswitch name {\n\t\t\t\tcase \"btf__core_reloc_existence___err_wrong_arr_kind\",\n\t\t\t\t\t\"btf__core_reloc_existence___err_wrong_arr_value_type\",\n\t\t\t\t\t\"btf__core_reloc_existence___err_wrong_int_kind\",\n\t\t\t\t\t\"btf__core_reloc_existence___err_wrong_int_sz\",\n\t\t\t\t\t\"btf__core_reloc_existence___err_wrong_int_type\",\n\t\t\t\t\t\"btf__core_reloc_existence___err_wrong_struct_type\":\n\t\t\t\t\t// These tests are buggy upstream, see https://lore.kernel.org/bpf/20210420111639.155580-1-lmb@cloudflare.com/\n\t\t\t\t\tvalid = true\n\t\t\t\tcase \"btf__core_reloc_ints___err_wrong_sz_16\",\n\t\t\t\t\t\"btf__core_reloc_ints___err_wrong_sz_32\",\n\t\t\t\t\t\"btf__core_reloc_ints___err_wrong_sz_64\",\n\t\t\t\t\t\"btf__core_reloc_ints___err_wrong_sz_8\",\n\t\t\t\t\t\"btf__core_reloc_arrays___err_wrong_val_type1\",\n\t\t\t\t\t\"btf__core_reloc_arrays___err_wrong_val_type2\":\n\t\t\t\t\t// These tests are valid according to current libbpf behaviour,\n\t\t\t\t\t// see commit 42765ede5c54ca915de5bfeab83be97207e46f68.\n\t\t\t\t\tvalid = true\n\t\t\t\tcase \"btf__core_reloc_type_id___missing_targets\",\n\t\t\t\t\t\"btf__core_reloc_flavors__err_wrong_name\":\n\t\t\t\t\tvalid = false\n\t\t\t\tcase \"btf__core_reloc_ints___err_bitfield\":\n\t\t\t\t\t// Bitfields are now valid.\n\t\t\t\t\tvalid = true\n\t\t\t\tdefault:\n\t\t\t\t\tvalid = !strings.Contains(name, \"___err_\")\n\t\t\t\t}\n\n\t\t\t\tfh, err := os.Open(coreFile)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatal(err)\n\t\t\t\t}\n\t\t\t\tdefer fh.Close()\n\n\t\t\t\tbtfSpec, err := btf.LoadSpec(coreFile)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatal(err)\n\t\t\t\t}\n\n\t\t\t\topts := opts // copy\n\t\t\t\topts.Programs.KernelTypes = btfSpec\n\t\t\t\tload(t, spec, opts, valid)\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc loadTargetProgram(tb testing.TB, spec *CollectionSpec, file, program string) {\n\ttargetSpec, err := LoadCollectionSpec(filepath.Join(*elfPath, file))\n\tif errors.Is(err, os.ErrNotExist) && strings.HasSuffix(file, \".bpf.o\") {\n\t\t// Prior to v6.1 BPF ELF used a plain .o suffix.\n\t\tfile = strings.TrimSuffix(file, \".bpf.o\") + \".o\"\n\t\ttargetSpec, err = LoadCollectionSpec(filepath.Join(*elfPath, file))\n\t}\n\tif err != nil {\n\t\ttb.Fatalf(\"Can't read %s: %s\", file, err)\n\t}\n\n\tqt.Assert(tb, qt.IsNotNil(targetSpec.Programs[program]))\n\n\tcoll, err := NewCollectionWithOptions(targetSpec, CollectionOptions{\n\t\tPrograms: ProgramOptions{LogDisabled: true},\n\t})\n\tif err != nil {\n\t\ttb.Fatalf(\"Can't load target: %s\", err)\n\t}\n\ttb.Cleanup(func() { coll.Close() })\n\n\ttarget := coll.Programs[program]\n\tfor _, prog := range spec.Programs {\n\t\tif prog.Type == Extension && prog.AttachType == AttachNone {\n\t\t\tprog.AttachTarget = target\n\t\t\tcontinue\n\t\t}\n\n\t\tif prog.Type == Tracing {\n\t\t\tswitch prog.AttachType {\n\t\t\tcase AttachTraceFEntry, AttachTraceFExit, AttachModifyReturn:\n\t\t\t\tprog.AttachTarget = target\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc sourceOfBTF(tb testing.TB, path string) []string {\n\tconst testPrefix = \"test_core_reloc_\"\n\tconst btfPrefix = \"btf__core_reloc_\"\n\n\tdir, base := filepath.Split(path)\n\tif !strings.HasPrefix(base, testPrefix) {\n\t\treturn nil\n\t}\n\n\tbase = strings.TrimSuffix(base[len(testPrefix):], \".o\")\n\tswitch base {\n\tcase \"bitfields_direct\", \"bitfields_probed\":\n\t\tbase = \"bitfields\"\n\t}\n\n\treturn testutils.Glob(tb, filepath.Join(dir, btfPrefix+base+\"*.o\"))\n}\n\nfunc TestELFSectionProgramTypes(t *testing.T) {\n\ttype testcase struct {\n\t\tSection     string\n\t\tProgramType ProgramType\n\t\tAttachType  AttachType\n\t\tFlags       uint32\n\t\tExtra       string\n\t}\n\n\ttestcases := []testcase{\n\t\t{\"socket\", SocketFilter, AttachNone, 0, \"\"},\n\t\t{\"socket/garbage\", SocketFilter, AttachNone, 0, \"\"},\n\t\t{\"sk_reuseport/migrate\", SkReuseport, AttachSkReuseportSelectOrMigrate, 0, \"\"},\n\t\t{\"sk_reuseport\", SkReuseport, AttachSkReuseportSelect, 0, \"\"},\n\t\t{\"kprobe/\", Kprobe, AttachNone, 0, \"\"},\n\t\t{\"kprobe/func\", Kprobe, AttachNone, 0, \"func\"},\n\t\t{\"uprobe/\", Kprobe, AttachNone, 0, \"\"},\n\t\t{\"kretprobe/\", Kprobe, AttachNone, 0, \"\"},\n\t\t{\"uretprobe/\", Kprobe, AttachNone, 0, \"\"},\n\t\t{\"tc\", SchedCLS, AttachNone, 0, \"\"},\n\t\t{\"classifier\", SchedCLS, AttachNone, 0, \"\"},\n\t\t{\"action\", SchedACT, AttachNone, 0, \"\"},\n\t\t{\"tracepoint/\", TracePoint, AttachNone, 0, \"\"},\n\t\t{\"tp/\", TracePoint, AttachNone, 0, \"\"},\n\t\t{\"raw_tracepoint/\", RawTracepoint, AttachNone, 0, \"\"},\n\t\t{\"raw_tp/\", RawTracepoint, AttachNone, 0, \"\"},\n\t\t{\"raw_tracepoint.w/\", RawTracepointWritable, AttachNone, 0, \"\"},\n\t\t{\"raw_tp.w/\", RawTracepointWritable, AttachNone, 0, \"\"},\n\t\t{\"tp_btf/\", Tracing, AttachTraceRawTp, 0, \"\"},\n\t\t{\"fentry/\", Tracing, AttachTraceFEntry, 0, \"\"},\n\t\t{\"fmod_ret/\", Tracing, AttachModifyReturn, 0, \"\"},\n\t\t{\"fexit/\", Tracing, AttachTraceFExit, 0, \"\"},\n\t\t{\"fentry.s/\", Tracing, AttachTraceFEntry, sys.BPF_F_SLEEPABLE, \"\"},\n\t\t{\"fmod_ret.s/\", Tracing, AttachModifyReturn, sys.BPF_F_SLEEPABLE, \"\"},\n\t\t{\"fexit.s/\", Tracing, AttachTraceFExit, sys.BPF_F_SLEEPABLE, \"\"},\n\t\t{\"freplace/\", Extension, AttachNone, 0, \"\"},\n\t\t{\"lsm/foo\", LSM, AttachLSMMac, 0, \"foo\"},\n\t\t{\"lsm.s/foo\", LSM, AttachLSMMac, sys.BPF_F_SLEEPABLE, \"foo\"},\n\t\t{\"iter/bpf_map\", Tracing, AttachTraceIter, 0, \"bpf_map\"},\n\t\t{\"iter.s/\", Tracing, AttachTraceIter, sys.BPF_F_SLEEPABLE, \"\"},\n\t\t{\"syscall\", Syscall, AttachNone, sys.BPF_F_SLEEPABLE, \"\"},\n\t\t{\"xdp.frags/devmap\", XDP, AttachXDPDevMap, sys.BPF_F_XDP_HAS_FRAGS, \"\"},\n\t\t{\"xdp/devmap\", XDP, AttachXDPDevMap, 0, \"\"},\n\t\t{\"xdp.frags/cpumap\", XDP, AttachXDPCPUMap, sys.BPF_F_XDP_HAS_FRAGS, \"\"},\n\t\t{\"xdp/cpumap\", XDP, AttachXDPCPUMap, 0, \"\"},\n\t\t{\"xdp.frags\", XDP, AttachXDP, sys.BPF_F_XDP_HAS_FRAGS, \"\"},\n\t\t{\"xdp\", XDP, AttachXDP, 0, \"\"},\n\t\t{\"perf_event\", PerfEvent, AttachNone, 0, \"\"},\n\t\t{\"lwt_in\", LWTIn, AttachNone, 0, \"\"},\n\t\t{\"lwt_out\", LWTOut, AttachNone, 0, \"\"},\n\t\t{\"lwt_xmit\", LWTXmit, AttachNone, 0, \"\"},\n\t\t{\"lwt_seg6local\", LWTSeg6Local, AttachNone, 0, \"\"},\n\t\t{\"cgroup_skb/ingress\", CGroupSKB, AttachCGroupInetIngress, 0, \"\"},\n\t\t{\"cgroup_skb/egress\", CGroupSKB, AttachCGroupInetEgress, 0, \"\"},\n\t\t{\"cgroup/skb\", CGroupSKB, AttachNone, 0, \"\"},\n\t\t{\"cgroup/sock_create\", CGroupSock, AttachCGroupInetSockCreate, 0, \"\"},\n\t\t{\"cgroup/sock_release\", CGroupSock, AttachCgroupInetSockRelease, 0, \"\"},\n\t\t{\"cgroup/sock\", CGroupSock, AttachCGroupInetSockCreate, 0, \"\"},\n\t\t{\"cgroup/post_bind4\", CGroupSock, AttachCGroupInet4PostBind, 0, \"\"},\n\t\t{\"cgroup/post_bind6\", CGroupSock, AttachCGroupInet6PostBind, 0, \"\"},\n\t\t{\"cgroup/dev\", CGroupDevice, AttachCGroupDevice, 0, \"\"},\n\t\t{\"sockops\", SockOps, AttachCGroupSockOps, 0, \"\"},\n\t\t{\"sk_skb/stream_parser\", SkSKB, AttachSkSKBStreamParser, 0, \"\"},\n\t\t{\"sk_skb/stream_verdict\", SkSKB, AttachSkSKBStreamVerdict, 0, \"\"},\n\t\t{\"sk_skb/stream_verdict/foo\", SkSKB, AttachSkSKBStreamVerdict, 0, \"\"},\n\t\t{\"sk_skb\", SkSKB, AttachNone, 0, \"\"},\n\t\t{\"sk_skb/bar\", SkSKB, AttachNone, 0, \"\"},\n\t\t{\"sk_msg\", SkMsg, AttachSkMsgVerdict, 0, \"\"},\n\t\t{\"lirc_mode2\", LircMode2, AttachLircMode2, 0, \"\"},\n\t\t{\"flow_dissector\", FlowDissector, AttachFlowDissector, 0, \"\"},\n\t\t{\"cgroup/bind4\", CGroupSockAddr, AttachCGroupInet4Bind, 0, \"\"},\n\t\t{\"cgroup/bind6\", CGroupSockAddr, AttachCGroupInet6Bind, 0, \"\"},\n\t\t{\"cgroup/connect4\", CGroupSockAddr, AttachCGroupInet4Connect, 0, \"\"},\n\t\t{\"cgroup/connect6\", CGroupSockAddr, AttachCGroupInet6Connect, 0, \"\"},\n\t\t{\"cgroup/sendmsg4\", CGroupSockAddr, AttachCGroupUDP4Sendmsg, 0, \"\"},\n\t\t{\"cgroup/sendmsg6\", CGroupSockAddr, AttachCGroupUDP6Sendmsg, 0, \"\"},\n\t\t{\"cgroup/recvmsg4\", CGroupSockAddr, AttachCGroupUDP4Recvmsg, 0, \"\"},\n\t\t{\"cgroup/recvmsg6\", CGroupSockAddr, AttachCGroupUDP6Recvmsg, 0, \"\"},\n\t\t{\"cgroup/getpeername4\", CGroupSockAddr, AttachCgroupInet4GetPeername, 0, \"\"},\n\t\t{\"cgroup/getpeername6\", CGroupSockAddr, AttachCgroupInet6GetPeername, 0, \"\"},\n\t\t{\"cgroup/getsockname4\", CGroupSockAddr, AttachCgroupInet4GetSockname, 0, \"\"},\n\t\t{\"cgroup/getsockname6\", CGroupSockAddr, AttachCgroupInet6GetSockname, 0, \"\"},\n\t\t{\"cgroup/sysctl\", CGroupSysctl, AttachCGroupSysctl, 0, \"\"},\n\t\t{\"cgroup/getsockopt\", CGroupSockopt, AttachCGroupGetsockopt, 0, \"\"},\n\t\t{\"cgroup/setsockopt\", CGroupSockopt, AttachCGroupSetsockopt, 0, \"\"},\n\t\t{\"sk_lookup/\", SkLookup, AttachSkLookup, 0, \"\"},\n\t\t{\"kprobe.multi\", Kprobe, AttachTraceKprobeMulti, 0, \"\"},\n\t\t{\"kretprobe.multi\", Kprobe, AttachTraceKprobeMulti, 0, \"\"},\n\t\t{\"struct_ops\", StructOps, AttachNone, 0, \"\"},\n\t\t{\"struct_ops.s\", StructOps, AttachNone, sys.BPF_F_SLEEPABLE, \"\"},\n\t\t{\"struct_ops/foo\", StructOps, AttachNone, 0, \"\"},\n\t}\n\n\tfor _, tc := range testcases {\n\t\tt.Run(tc.Section, func(t *testing.T) {\n\t\t\tpt, at, fl, extra := getProgType(tc.Section)\n\t\t\thave := testcase{tc.Section, pt, at, fl, extra}\n\t\t\tqt.Assert(t, qt.DeepEquals(have, tc))\n\t\t})\n\t}\n}\n\nfunc TestMatchSectionName(t *testing.T) {\n\tfor _, testcase := range []struct {\n\t\tpattern string\n\t\tinput   string\n\t\tmatches bool\n\t\textra   string\n\t}{\n\t\t{\"prefix/\", \"prefix/\", true, \"\"},\n\t\t{\"prefix/\", \"prefix/a\", true, \"a\"},\n\t\t{\"prefix/\", \"prefix/b\", true, \"b\"},\n\t\t{\"prefix/\", \"prefix\", false, \"\"},\n\t\t{\"prefix/\", \"junk\", false, \"\"},\n\n\t\t{\"prefix+\", \"prefix/\", true, \"\"},\n\t\t{\"prefix+\", \"prefix/a\", true, \"a\"},\n\t\t{\"prefix+\", \"prefix/b\", true, \"b\"},\n\t\t{\"prefix+\", \"prefix\", true, \"\"},\n\t\t{\"prefix+\", \"junk\", false, \"\"},\n\n\t\t{\"exact\", \"exact\", true, \"\"},\n\t\t{\"exact\", \"exact/\", true, \"\"},\n\t\t{\"exact\", \"exact/a\", true, \"\"},\n\t\t{\"exact\", \"exactement\", true, \"\"},\n\t\t{\"exact\", \"junk\", false, \"\"},\n\t} {\n\t\tname := fmt.Sprintf(\"%s:%s\", testcase.pattern, testcase.input)\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\textra, matches := matchSectionName(testcase.input, testcase.pattern)\n\t\t\tqt.Assert(t, qt.Equals(matches, testcase.matches))\n\t\t\tif testcase.matches {\n\t\t\t\tqt.Assert(t, qt.Equals(extra, testcase.extra))\n\t\t\t}\n\t\t})\n\t}\n}\n\n// selftestName takes a path to a file and derives a canonical name from it.\n//\n// It strips various suffixes used by the selftest build system.\nfunc selftestName(path string) string {\n\tfile := filepath.Base(path)\n\n\tname := strings.TrimSuffix(file, \".o\")\n\t// Strip various suffixes.\n\t// Various linking suffixes.\n\tname = strings.TrimSuffix(name, \".linked3\")\n\tname = strings.TrimSuffix(name, \".llinked1\")\n\tname = strings.TrimSuffix(name, \".llinked2\")\n\tname = strings.TrimSuffix(name, \".llinked3\")\n\t// v6.1 started adding .bpf to all BPF ELF.\n\tname = strings.TrimSuffix(name, \".bpf\")\n\n\treturn name\n}\n"
  },
  {
    "path": "elf_sections.go",
    "content": "// Code generated by internal/cmd/gensections.awk; DO NOT EDIT.\n\npackage ebpf\n\n// Code in this file is derived from libbpf, available under BSD-2-Clause.\n\nimport \"github.com/cilium/ebpf/internal/sys\"\n\nvar elfSectionDefs = []libbpfElfSectionDef{\n\t{\"socket\", sys.BPF_PROG_TYPE_SOCKET_FILTER, 0, _SEC_NONE},\n\t{\"sk_reuseport/migrate\", sys.BPF_PROG_TYPE_SK_REUSEPORT, sys.BPF_SK_REUSEPORT_SELECT_OR_MIGRATE, _SEC_ATTACHABLE},\n\t{\"sk_reuseport\", sys.BPF_PROG_TYPE_SK_REUSEPORT, sys.BPF_SK_REUSEPORT_SELECT, _SEC_ATTACHABLE},\n\t{\"kprobe+\", sys.BPF_PROG_TYPE_KPROBE, 0, _SEC_NONE},\n\t{\"uprobe+\", sys.BPF_PROG_TYPE_KPROBE, 0, _SEC_NONE},\n\t{\"uprobe.s+\", sys.BPF_PROG_TYPE_KPROBE, 0, _SEC_SLEEPABLE},\n\t{\"kretprobe+\", sys.BPF_PROG_TYPE_KPROBE, 0, _SEC_NONE},\n\t{\"uretprobe+\", sys.BPF_PROG_TYPE_KPROBE, 0, _SEC_NONE},\n\t{\"uretprobe.s+\", sys.BPF_PROG_TYPE_KPROBE, 0, _SEC_SLEEPABLE},\n\t{\"kprobe.multi+\", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_KPROBE_MULTI, _SEC_NONE},\n\t{\"kretprobe.multi+\", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_KPROBE_MULTI, _SEC_NONE},\n\t{\"kprobe.session+\", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_KPROBE_SESSION, _SEC_NONE},\n\t{\"uprobe.multi+\", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_UPROBE_MULTI, _SEC_NONE},\n\t{\"uretprobe.multi+\", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_UPROBE_MULTI, _SEC_NONE},\n\t{\"uprobe.session+\", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_UPROBE_SESSION, _SEC_NONE},\n\t{\"uprobe.multi.s+\", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_UPROBE_MULTI, _SEC_SLEEPABLE},\n\t{\"uretprobe.multi.s+\", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_UPROBE_MULTI, _SEC_SLEEPABLE},\n\t{\"uprobe.session.s+\", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_UPROBE_SESSION, _SEC_SLEEPABLE},\n\t{\"ksyscall+\", sys.BPF_PROG_TYPE_KPROBE, 0, _SEC_NONE},\n\t{\"kretsyscall+\", sys.BPF_PROG_TYPE_KPROBE, 0, _SEC_NONE},\n\t{\"usdt+\", sys.BPF_PROG_TYPE_KPROBE, 0, _SEC_USDT},\n\t{\"usdt.s+\", sys.BPF_PROG_TYPE_KPROBE, 0, _SEC_USDT | _SEC_SLEEPABLE},\n\t{\"tc/ingress\", sys.BPF_PROG_TYPE_SCHED_CLS, sys.BPF_TCX_INGRESS, _SEC_NONE},\n\t{\"tc/egress\", sys.BPF_PROG_TYPE_SCHED_CLS, sys.BPF_TCX_EGRESS, _SEC_NONE},\n\t{\"tcx/ingress\", sys.BPF_PROG_TYPE_SCHED_CLS, sys.BPF_TCX_INGRESS, _SEC_NONE},\n\t{\"tcx/egress\", sys.BPF_PROG_TYPE_SCHED_CLS, sys.BPF_TCX_EGRESS, _SEC_NONE},\n\t{\"tc\", sys.BPF_PROG_TYPE_SCHED_CLS, 0, _SEC_NONE},\n\t{\"classifier\", sys.BPF_PROG_TYPE_SCHED_CLS, 0, _SEC_NONE},\n\t{\"action\", sys.BPF_PROG_TYPE_SCHED_ACT, 0, _SEC_NONE},\n\t{\"netkit/primary\", sys.BPF_PROG_TYPE_SCHED_CLS, sys.BPF_NETKIT_PRIMARY, _SEC_NONE},\n\t{\"netkit/peer\", sys.BPF_PROG_TYPE_SCHED_CLS, sys.BPF_NETKIT_PEER, _SEC_NONE},\n\t{\"tracepoint+\", sys.BPF_PROG_TYPE_TRACEPOINT, 0, _SEC_NONE},\n\t{\"tp+\", sys.BPF_PROG_TYPE_TRACEPOINT, 0, _SEC_NONE},\n\t{\"raw_tracepoint+\", sys.BPF_PROG_TYPE_RAW_TRACEPOINT, 0, _SEC_NONE},\n\t{\"raw_tp+\", sys.BPF_PROG_TYPE_RAW_TRACEPOINT, 0, _SEC_NONE},\n\t{\"raw_tracepoint.w+\", sys.BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, 0, _SEC_NONE},\n\t{\"raw_tp.w+\", sys.BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, 0, _SEC_NONE},\n\t{\"tp_btf+\", sys.BPF_PROG_TYPE_TRACING, sys.BPF_TRACE_RAW_TP, _SEC_ATTACH_BTF},\n\t{\"fentry+\", sys.BPF_PROG_TYPE_TRACING, sys.BPF_TRACE_FENTRY, _SEC_ATTACH_BTF},\n\t{\"fmod_ret+\", sys.BPF_PROG_TYPE_TRACING, sys.BPF_MODIFY_RETURN, _SEC_ATTACH_BTF},\n\t{\"fexit+\", sys.BPF_PROG_TYPE_TRACING, sys.BPF_TRACE_FEXIT, _SEC_ATTACH_BTF},\n\t{\"fentry.s+\", sys.BPF_PROG_TYPE_TRACING, sys.BPF_TRACE_FENTRY, _SEC_ATTACH_BTF | _SEC_SLEEPABLE},\n\t{\"fmod_ret.s+\", sys.BPF_PROG_TYPE_TRACING, sys.BPF_MODIFY_RETURN, _SEC_ATTACH_BTF | _SEC_SLEEPABLE},\n\t{\"fexit.s+\", sys.BPF_PROG_TYPE_TRACING, sys.BPF_TRACE_FEXIT, _SEC_ATTACH_BTF | _SEC_SLEEPABLE},\n\t{\"freplace+\", sys.BPF_PROG_TYPE_EXT, 0, _SEC_ATTACH_BTF},\n\t{\"lsm+\", sys.BPF_PROG_TYPE_LSM, sys.BPF_LSM_MAC, _SEC_ATTACH_BTF},\n\t{\"lsm.s+\", sys.BPF_PROG_TYPE_LSM, sys.BPF_LSM_MAC, _SEC_ATTACH_BTF | _SEC_SLEEPABLE},\n\t{\"lsm_cgroup+\", sys.BPF_PROG_TYPE_LSM, sys.BPF_LSM_CGROUP, _SEC_ATTACH_BTF},\n\t{\"iter+\", sys.BPF_PROG_TYPE_TRACING, sys.BPF_TRACE_ITER, _SEC_ATTACH_BTF},\n\t{\"iter.s+\", sys.BPF_PROG_TYPE_TRACING, sys.BPF_TRACE_ITER, _SEC_ATTACH_BTF | _SEC_SLEEPABLE},\n\t{\"syscall\", sys.BPF_PROG_TYPE_SYSCALL, 0, _SEC_SLEEPABLE},\n\t{\"xdp.frags/devmap\", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP_DEVMAP, _SEC_XDP_FRAGS},\n\t{\"xdp/devmap\", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP_DEVMAP, _SEC_ATTACHABLE},\n\t{\"xdp.frags/cpumap\", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP_CPUMAP, _SEC_XDP_FRAGS},\n\t{\"xdp/cpumap\", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP_CPUMAP, _SEC_ATTACHABLE},\n\t{\"xdp.frags\", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP, _SEC_XDP_FRAGS},\n\t{\"xdp\", sys.BPF_PROG_TYPE_XDP, sys.BPF_XDP, _SEC_ATTACHABLE_OPT},\n\t{\"perf_event\", sys.BPF_PROG_TYPE_PERF_EVENT, 0, _SEC_NONE},\n\t{\"lwt_in\", sys.BPF_PROG_TYPE_LWT_IN, 0, _SEC_NONE},\n\t{\"lwt_out\", sys.BPF_PROG_TYPE_LWT_OUT, 0, _SEC_NONE},\n\t{\"lwt_xmit\", sys.BPF_PROG_TYPE_LWT_XMIT, 0, _SEC_NONE},\n\t{\"lwt_seg6local\", sys.BPF_PROG_TYPE_LWT_SEG6LOCAL, 0, _SEC_NONE},\n\t{\"sockops\", sys.BPF_PROG_TYPE_SOCK_OPS, sys.BPF_CGROUP_SOCK_OPS, _SEC_ATTACHABLE_OPT},\n\t{\"sk_skb/stream_parser\", sys.BPF_PROG_TYPE_SK_SKB, sys.BPF_SK_SKB_STREAM_PARSER, _SEC_ATTACHABLE_OPT},\n\t{\"sk_skb/stream_verdict\", sys.BPF_PROG_TYPE_SK_SKB, sys.BPF_SK_SKB_STREAM_VERDICT, _SEC_ATTACHABLE_OPT},\n\t{\"sk_skb/verdict\", sys.BPF_PROG_TYPE_SK_SKB, sys.BPF_SK_SKB_VERDICT, _SEC_ATTACHABLE_OPT},\n\t{\"sk_skb\", sys.BPF_PROG_TYPE_SK_SKB, 0, _SEC_NONE},\n\t{\"sk_msg\", sys.BPF_PROG_TYPE_SK_MSG, sys.BPF_SK_MSG_VERDICT, _SEC_ATTACHABLE_OPT},\n\t{\"lirc_mode2\", sys.BPF_PROG_TYPE_LIRC_MODE2, sys.BPF_LIRC_MODE2, _SEC_ATTACHABLE_OPT},\n\t{\"flow_dissector\", sys.BPF_PROG_TYPE_FLOW_DISSECTOR, sys.BPF_FLOW_DISSECTOR, _SEC_ATTACHABLE_OPT},\n\t{\"cgroup_skb/ingress\", sys.BPF_PROG_TYPE_CGROUP_SKB, sys.BPF_CGROUP_INET_INGRESS, _SEC_ATTACHABLE_OPT},\n\t{\"cgroup_skb/egress\", sys.BPF_PROG_TYPE_CGROUP_SKB, sys.BPF_CGROUP_INET_EGRESS, _SEC_ATTACHABLE_OPT},\n\t{\"cgroup/skb\", sys.BPF_PROG_TYPE_CGROUP_SKB, 0, _SEC_NONE},\n\t{\"cgroup/sock_create\", sys.BPF_PROG_TYPE_CGROUP_SOCK, sys.BPF_CGROUP_INET_SOCK_CREATE, _SEC_ATTACHABLE},\n\t{\"cgroup/sock_release\", sys.BPF_PROG_TYPE_CGROUP_SOCK, sys.BPF_CGROUP_INET_SOCK_RELEASE, _SEC_ATTACHABLE},\n\t{\"cgroup/sock\", sys.BPF_PROG_TYPE_CGROUP_SOCK, sys.BPF_CGROUP_INET_SOCK_CREATE, _SEC_ATTACHABLE_OPT},\n\t{\"cgroup/post_bind4\", sys.BPF_PROG_TYPE_CGROUP_SOCK, sys.BPF_CGROUP_INET4_POST_BIND, _SEC_ATTACHABLE},\n\t{\"cgroup/post_bind6\", sys.BPF_PROG_TYPE_CGROUP_SOCK, sys.BPF_CGROUP_INET6_POST_BIND, _SEC_ATTACHABLE},\n\t{\"cgroup/bind4\", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_INET4_BIND, _SEC_ATTACHABLE},\n\t{\"cgroup/bind6\", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_INET6_BIND, _SEC_ATTACHABLE},\n\t{\"cgroup/connect4\", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_INET4_CONNECT, _SEC_ATTACHABLE},\n\t{\"cgroup/connect6\", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_INET6_CONNECT, _SEC_ATTACHABLE},\n\t{\"cgroup/connect_unix\", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_UNIX_CONNECT, _SEC_ATTACHABLE},\n\t{\"cgroup/sendmsg4\", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_UDP4_SENDMSG, _SEC_ATTACHABLE},\n\t{\"cgroup/sendmsg6\", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_UDP6_SENDMSG, _SEC_ATTACHABLE},\n\t{\"cgroup/sendmsg_unix\", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_UNIX_SENDMSG, _SEC_ATTACHABLE},\n\t{\"cgroup/recvmsg4\", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_UDP4_RECVMSG, _SEC_ATTACHABLE},\n\t{\"cgroup/recvmsg6\", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_UDP6_RECVMSG, _SEC_ATTACHABLE},\n\t{\"cgroup/recvmsg_unix\", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_UNIX_RECVMSG, _SEC_ATTACHABLE},\n\t{\"cgroup/getpeername4\", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_INET4_GETPEERNAME, _SEC_ATTACHABLE},\n\t{\"cgroup/getpeername6\", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_INET6_GETPEERNAME, _SEC_ATTACHABLE},\n\t{\"cgroup/getpeername_unix\", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_UNIX_GETPEERNAME, _SEC_ATTACHABLE},\n\t{\"cgroup/getsockname4\", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_INET4_GETSOCKNAME, _SEC_ATTACHABLE},\n\t{\"cgroup/getsockname6\", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_INET6_GETSOCKNAME, _SEC_ATTACHABLE},\n\t{\"cgroup/getsockname_unix\", sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR, sys.BPF_CGROUP_UNIX_GETSOCKNAME, _SEC_ATTACHABLE},\n\t{\"cgroup/sysctl\", sys.BPF_PROG_TYPE_CGROUP_SYSCTL, sys.BPF_CGROUP_SYSCTL, _SEC_ATTACHABLE},\n\t{\"cgroup/getsockopt\", sys.BPF_PROG_TYPE_CGROUP_SOCKOPT, sys.BPF_CGROUP_GETSOCKOPT, _SEC_ATTACHABLE},\n\t{\"cgroup/setsockopt\", sys.BPF_PROG_TYPE_CGROUP_SOCKOPT, sys.BPF_CGROUP_SETSOCKOPT, _SEC_ATTACHABLE},\n\t{\"cgroup/dev\", sys.BPF_PROG_TYPE_CGROUP_DEVICE, sys.BPF_CGROUP_DEVICE, _SEC_ATTACHABLE_OPT},\n\t{\"struct_ops+\", sys.BPF_PROG_TYPE_STRUCT_OPS, 0, _SEC_NONE},\n\t{\"struct_ops.s+\", sys.BPF_PROG_TYPE_STRUCT_OPS, 0, _SEC_SLEEPABLE},\n\t{\"sk_lookup\", sys.BPF_PROG_TYPE_SK_LOOKUP, sys.BPF_SK_LOOKUP, _SEC_ATTACHABLE},\n\t{\"netfilter\", sys.BPF_PROG_TYPE_NETFILTER, sys.BPF_NETFILTER, _SEC_NONE},\n}\n"
  },
  {
    "path": "example_sock_elf_test.go",
    "content": "//go:build linux\n\npackage ebpf_test\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"flag\"\n\t\"fmt\"\n\t\"syscall\"\n\t\"time\"\n\t\"unsafe\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\nvar program = [...]byte{\n\t0177, 0105, 0114, 0106, 0002, 0001, 0001, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0001, 0000, 0367, 0000, 0001, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0340, 0001, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0000, 0000, 0000, 0000, 0100, 0000, 0000, 0000, 0000, 0000, 0100, 0000, 0010, 0000, 0001, 0000,\n\t0277, 0026, 0000, 0000, 0000, 0000, 0000, 0000, 0060, 0000, 0000, 0000, 0027, 0000, 0000, 0000,\n\t0143, 0012, 0374, 0377, 0000, 0000, 0000, 0000, 0141, 0141, 0004, 0000, 0000, 0000, 0000, 0000,\n\t0125, 0001, 0010, 0000, 0004, 0000, 0000, 0000, 0277, 0242, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0007, 0002, 0000, 0000, 0374, 0377, 0377, 0377, 0030, 0001, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0205, 0000, 0000, 0000, 0001, 0000, 0000, 0000,\n\t0025, 0000, 0002, 0000, 0000, 0000, 0000, 0000, 0141, 0141, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0333, 0020, 0000, 0000, 0000, 0000, 0000, 0000, 0267, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0225, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0002, 0000, 0000, 0000, 0004, 0000, 0000, 0000,\n\t0010, 0000, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0000, 0000, 0000, 0002, 0000, 0000, 0000,\n\t0004, 0000, 0000, 0000, 0010, 0000, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0107, 0120, 0114, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0065, 0000, 0000, 0000, 0000, 0000, 0003, 0000, 0150, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0034, 0000, 0000, 0000, 0020, 0000, 0006, 0000,\n\t0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0110, 0000, 0000, 0000, 0020, 0000, 0003, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0014, 0000, 0000, 0000, 0020, 0000, 0005, 0000,\n\t0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0023, 0000, 0000, 0000, 0020, 0000, 0005, 0000, 0024, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0070, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0001, 0000, 0000, 0000, 0004, 0000, 0000, 0000, 0000, 0056, 0164, 0145, 0170, 0164, 0000, 0155,\n\t0141, 0160, 0163, 0000, 0155, 0171, 0137, 0155, 0141, 0160, 0000, 0164, 0145, 0163, 0164, 0137,\n\t0155, 0141, 0160, 0000, 0137, 0154, 0151, 0143, 0145, 0156, 0163, 0145, 0000, 0056, 0163, 0164,\n\t0162, 0164, 0141, 0142, 0000, 0056, 0163, 0171, 0155, 0164, 0141, 0142, 0000, 0114, 0102, 0102,\n\t0060, 0137, 0063, 0000, 0056, 0162, 0145, 0154, 0163, 0157, 0143, 0153, 0145, 0164, 0061, 0000,\n\t0142, 0160, 0146, 0137, 0160, 0162, 0157, 0147, 0061, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0045, 0000, 0000, 0000, 0003, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0210, 0001, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0122, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0001, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0001, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0006, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0100, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0004, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0100, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0006, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0100, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0170, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0010, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0074, 0000, 0000, 0000, 0011, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0170, 0001, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0020, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0007, 0000, 0000, 0000, 0003, 0000, 0000, 0000,\n\t0010, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0020, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0007, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0003, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0270, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0050, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0004, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0035, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0003, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0340, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0004, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0001, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0055, 0000, 0000, 0000, 0002, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0350, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n\t0220, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0001, 0000, 0000, 0000, 0002, 0000, 0000, 0000,\n\t0010, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0030, 0000, 0000, 0000, 0000, 0000, 0000, 0000,\n}\n\n// ExampleSocketELF demonstrates how to load an eBPF program from an ELF,\n// and attach it to a raw socket.\nfunc Example_socketELF() {\n\tconst SO_ATTACH_BPF = 50\n\n\tindex := flag.Int(\"index\", 0, \"specify ethernet index\")\n\tflag.Parse()\n\n\tspec, err := ebpf.LoadCollectionSpecFromReader(bytes.NewReader(program[:]))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tvar objs struct {\n\t\tProg  *ebpf.Program `ebpf:\"bpf_prog1\"`\n\t\tStats *ebpf.Map     `ebpf:\"my_map\"`\n\t}\n\n\tif err := spec.LoadAndAssign(&objs, nil); err != nil {\n\t\tpanic(err)\n\t}\n\tdefer objs.Prog.Close()\n\tdefer objs.Stats.Close()\n\n\tsock, err := openRawSock(*index)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer syscall.Close(sock)\n\n\tif err := syscall.SetsockoptInt(sock, syscall.SOL_SOCKET, SO_ATTACH_BPF, objs.Prog.FD()); err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"Filtering on eth index: %d\\n\", *index)\n\tfmt.Println(\"Packet stats:\")\n\n\tfor {\n\t\tconst (\n\t\t\tICMP = 0x01\n\t\t\tTCP  = 0x06\n\t\t\tUDP  = 0x11\n\t\t)\n\n\t\ttime.Sleep(time.Second)\n\t\tvar icmp uint64\n\t\tvar tcp uint64\n\t\tvar udp uint64\n\t\terr := objs.Stats.Lookup(uint32(ICMP), &icmp)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\terr = objs.Stats.Lookup(uint32(TCP), &tcp)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\terr = objs.Stats.Lookup(uint32(UDP), &udp)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tfmt.Printf(\"\\r\\033[m\\tICMP: %d TCP: %d UDP: %d\", icmp, tcp, udp)\n\t}\n}\n\nfunc openRawSock(index int) (int, error) {\n\tsock, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, int(htons(syscall.ETH_P_ALL)))\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tsll := syscall.SockaddrLinklayer{\n\t\tIfindex:  index,\n\t\tProtocol: htons(syscall.ETH_P_ALL),\n\t}\n\tif err := syscall.Bind(sock, &sll); err != nil {\n\t\treturn 0, err\n\t}\n\treturn sock, nil\n}\n\n// htons converts the unsigned short integer hostshort from host byte order to network byte order.\nfunc htons(i uint16) uint16 {\n\tb := make([]byte, 2)\n\tbinary.BigEndian.PutUint16(b, i)\n\treturn *(*uint16)(unsafe.Pointer(&b[0]))\n}\n"
  },
  {
    "path": "example_sock_extract_dist_test.go",
    "content": "//go:build linux\n\npackage ebpf_test\n\n// This code is derived from https://github.com/cloudflare/cloudflare-blog/tree/master/2018-03-ebpf\n//\n// Copyright (c) 2015-2017 Cloudflare, Inc. All rights reserved.\n//\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are\n// met:\n//\n//    * Redistributions of source code must retain the above copyright\n// notice, this list of conditions and the following disclaimer.\n//    * Redistributions in binary form must reproduce the above\n// copyright notice, this list of conditions and the following disclaimer\n// in the documentation and/or other materials provided with the\n// distribution.\n//    * Neither the name of the Cloudflare, Inc. nor the names of its\n// contributors may be used to endorse or promote products derived from\n// this software without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"syscall\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/asm\"\n)\n\n// ExampleExtractDistance shows how to attach an eBPF socket filter to\n// extract the network distance of an IP host.\nfunc Example_extractDistance() {\n\tfilter, TTLs, err := newDistanceFilter()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer filter.Close()\n\tdefer TTLs.Close()\n\n\t// Attach filter before the call to connect()\n\tdialer := net.Dialer{\n\t\tControl: func(network, address string, c syscall.RawConn) (err error) {\n\t\t\tconst SO_ATTACH_BPF = 50\n\n\t\t\terr = c.Control(func(fd uintptr) {\n\t\t\t\terr = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, SO_ATTACH_BPF, filter.FD())\n\t\t\t})\n\t\t\treturn err\n\t\t},\n\t}\n\n\tconn, err := dialer.Dial(\"tcp\", \"1.1.1.1:53\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tconn.Close()\n\n\tminDist, err := minDistance(TTLs)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(\"1.1.1.1:53 is\", minDist, \"hops away\")\n}\n\nfunc newDistanceFilter() (*ebpf.Program, *ebpf.Map, error) {\n\tconst ETH_P_IPV6 uint16 = 0x86DD\n\n\tttls, err := ebpf.NewMap(&ebpf.MapSpec{\n\t\tType:       ebpf.Hash,\n\t\tKeySize:    4,\n\t\tValueSize:  8,\n\t\tMaxEntries: 4,\n\t})\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tinsns := asm.Instructions{\n\t\t// r1 has ctx\n\t\t// r0 = ctx[16] (aka protocol)\n\t\tasm.LoadMem(asm.R0, asm.R1, 16, asm.Word),\n\n\t\t// Perhaps ipv6\n\t\tasm.LoadImm(asm.R2, int64(ETH_P_IPV6), asm.DWord),\n\t\tasm.HostTo(asm.BE, asm.R2, asm.Half),\n\t\tasm.JEq.Reg(asm.R0, asm.R2, \"ipv6\"),\n\n\t\t// otherwise assume ipv4\n\t\t// 8th byte in IPv4 is TTL\n\t\t// LDABS requires ctx in R6\n\t\tasm.Mov.Reg(asm.R6, asm.R1),\n\t\tasm.LoadAbs(-0x100000+8, asm.Byte),\n\t\tasm.Ja.Label(\"store-ttl\"),\n\n\t\t// 7th byte in IPv6 is Hop count\n\t\t// LDABS requires ctx in R6\n\t\tasm.Mov.Reg(asm.R6, asm.R1).WithSymbol(\"ipv6\"),\n\t\tasm.LoadAbs(-0x100000+7, asm.Byte),\n\n\t\t// stash the load result into FP[-4]\n\t\tasm.StoreMem(asm.RFP, -4, asm.R0, asm.Word).WithSymbol(\"store-ttl\"),\n\t\t// stash the &FP[-4] into r2\n\t\tasm.Mov.Reg(asm.R2, asm.RFP),\n\t\tasm.Add.Imm(asm.R2, -4),\n\n\t\t// r1 must point to map\n\t\tasm.LoadMapPtr(asm.R1, ttls.FD()),\n\t\tasm.FnMapLookupElem.Call(),\n\n\t\t// load ok? inc. Otherwise? jmp to mapupdate\n\t\tasm.JEq.Imm(asm.R0, 0, \"update-map\"),\n\t\tasm.Mov.Imm(asm.R1, 1),\n\t\tasm.StoreXAdd(asm.R0, asm.R1, asm.DWord),\n\t\tasm.Ja.Label(\"exit\"),\n\n\t\t// MapUpdate\n\t\t// r1 has map ptr\n\t\tasm.LoadMapPtr(asm.R1, ttls.FD()).WithSymbol(\"update-map\"),\n\t\t// r2 has key -> &FP[-4]\n\t\tasm.Mov.Reg(asm.R2, asm.RFP),\n\t\tasm.Add.Imm(asm.R2, -4),\n\t\t// r3 has value -> &FP[-16] , aka 1\n\t\tasm.StoreImm(asm.RFP, -16, 1, asm.DWord),\n\t\tasm.Mov.Reg(asm.R3, asm.RFP),\n\t\tasm.Add.Imm(asm.R3, -16),\n\t\t// r4 has flags, 0\n\t\tasm.Mov.Imm(asm.R4, 0),\n\t\tasm.FnMapUpdateElem.Call(),\n\n\t\t// set exit code to -1, don't trunc packet\n\t\tasm.Mov.Imm(asm.R0, -1).WithSymbol(\"exit\"),\n\t\tasm.Return(),\n\t}\n\n\tprog, err := ebpf.NewProgram(&ebpf.ProgramSpec{\n\t\tName:         \"distance_filter\",\n\t\tType:         ebpf.SocketFilter,\n\t\tLicense:      \"GPL\",\n\t\tInstructions: insns,\n\t})\n\tif err != nil {\n\t\tttls.Close()\n\t\treturn nil, nil, err\n\t}\n\n\treturn prog, ttls, nil\n}\n\nfunc minDistance(TTLs *ebpf.Map) (int, error) {\n\tvar (\n\t\tentries = TTLs.Iterate()\n\t\tttl     uint32\n\t\tminDist uint32 = 255\n\t\tcount   uint64\n\t)\n\tfor entries.Next(&ttl, &count) {\n\t\tvar dist uint32\n\t\tswitch {\n\t\tcase ttl > 128:\n\t\t\tdist = 255 - ttl\n\t\tcase ttl > 64:\n\t\t\tdist = 128 - ttl\n\t\tcase ttl > 32:\n\t\t\tdist = 64 - ttl\n\t\tdefault:\n\t\t\tdist = 32 - ttl\n\t\t}\n\t\tif minDist > dist {\n\t\t\tminDist = dist\n\t\t}\n\t}\n\treturn int(minDist), entries.Err()\n}\n"
  },
  {
    "path": "examples/README.md",
    "content": "# Examples\n\nA collection of programs showing how to use the library.\nPlease see our [guide on what makes a good example](https://ebpf-go.dev/contributing/new-example/) if you think something is missing.\n\n* Kprobe - Attach a program to the entry or exit of an arbitrary kernel symbol (function).\n  * [kprobe](kprobe/) - Kprobe using bpf2go.\n  * [kprobepin](kprobepin/) - Reuse a pinned map for the kprobe example. It assumes the BPF FS is mounted at `/sys/fs/bpf`.\n  * [kprobe_percpu](kprobe_percpu/) - Use a `BPF_MAP_TYPE_PERCPU_ARRAY` map.\n  * [ringbuffer](ringbuffer/) - Use a `BPF_MAP_TYPE_RINGBUF` map.\n* Uprobe - Attach a program to the entry or exit of an arbitrary userspace binary symbol (function).\n  * [uretprobe](uretprobe/) - Uretprobe using bpf2go.\n* Tracepoint - Attach a program to predetermined kernel tracepoints.\n  * [tracepoint_in_c](tracepoint_in_c/) - Tracepoint using bpf2go.\n  * [tracepoint_in_go](tracepoint_in_go/) - Tracepoint using the `ebpf.NewProgram` API and Go eBPF assembler.\n* Cgroup - Attach a program to control groups (cgroups).\n  * [cgroup_skb](cgroup_skb/) - Count packets egressing the current cgroup.\n* Fentry - Attach a program to the entrypoint of a kernel function.\n  Like kprobes, but with better performance and usability, for kernels 5.5 and later.\n  * [tcp_connect](fentry/) - Trace outgoing IPv4 TCP connections.\n  * [tcp_close](tcprtt/) - Log RTT of IPv4 TCP connections using eBPF CO-RE helpers.\n* TCx - Attach a program to Linux TC (Traffic Control) to process incoming and outgoing packets.\n  * [tcx](./tcx/) - Print packet counts for ingress and egress.\n* XDP - Attach a program to a network interface to process incoming packets.\n  * [xdp](xdp/) - Print packet counts by IPv4 source address.\n  * [xdp_live_frame](xdp_live_frame/) - XDP-based traffic generator that uses live frame mode.\n* sched_ext - Attach a StructOpsMap to register a custom task scheduler. This feature is supported by kernel version starting from version 6.12.\n  * [sched_ext](sched_ext/) - Minimal sched_ext_ops.\n\n## How to run\n\n```bash\ncd ebpf/examples/\ngo run -exec sudo [./kprobe, ./uretprobe, ./ringbuffer, ...]\n```\n\n## How to recompile\n\nThe examples are built via `go generate` invoked by the Makefile in the project root.\n\n```\nmake -C ../\n```\n"
  },
  {
    "path": "examples/cgroup_skb/bpf_bpfeb.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (mips || mips64 || ppc64 || s390x) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\n// loadBpf returns the embedded CollectionSpec for bpf.\nfunc loadBpf() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_BpfBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load bpf: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadBpfObjects loads bpf and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*bpfObjects\n//\t*bpfPrograms\n//\t*bpfMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadBpf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// bpfSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfSpecs struct {\n\tbpfProgramSpecs\n\tbpfMapSpecs\n\tbpfVariableSpecs\n}\n\n// bpfProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfProgramSpecs struct {\n\tCountEgressPackets *ebpf.ProgramSpec `ebpf:\"count_egress_packets\"`\n}\n\n// bpfMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfMapSpecs struct {\n\tPktCount *ebpf.MapSpec `ebpf:\"pkt_count\"`\n}\n\n// bpfVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfVariableSpecs struct {\n}\n\n// bpfObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfObjects struct {\n\tbpfPrograms\n\tbpfMaps\n\tbpfVariables\n}\n\nfunc (o *bpfObjects) Close() error {\n\treturn _BpfClose(\n\t\t&o.bpfPrograms,\n\t\t&o.bpfMaps,\n\t)\n}\n\n// bpfMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfMaps struct {\n\tPktCount *ebpf.Map `ebpf:\"pkt_count\"`\n}\n\nfunc (m *bpfMaps) Close() error {\n\treturn _BpfClose(\n\t\tm.PktCount,\n\t)\n}\n\n// bpfVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfVariables struct {\n}\n\n// bpfPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfPrograms struct {\n\tCountEgressPackets *ebpf.Program `ebpf:\"count_egress_packets\"`\n}\n\nfunc (p *bpfPrograms) Close() error {\n\treturn _BpfClose(\n\t\tp.CountEgressPackets,\n\t)\n}\n\nfunc _BpfClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed bpf_bpfeb.o\nvar _BpfBytes []byte\n"
  },
  {
    "path": "examples/cgroup_skb/bpf_bpfel.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64 || wasm) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\n// loadBpf returns the embedded CollectionSpec for bpf.\nfunc loadBpf() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_BpfBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load bpf: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadBpfObjects loads bpf and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*bpfObjects\n//\t*bpfPrograms\n//\t*bpfMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadBpf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// bpfSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfSpecs struct {\n\tbpfProgramSpecs\n\tbpfMapSpecs\n\tbpfVariableSpecs\n}\n\n// bpfProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfProgramSpecs struct {\n\tCountEgressPackets *ebpf.ProgramSpec `ebpf:\"count_egress_packets\"`\n}\n\n// bpfMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfMapSpecs struct {\n\tPktCount *ebpf.MapSpec `ebpf:\"pkt_count\"`\n}\n\n// bpfVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfVariableSpecs struct {\n}\n\n// bpfObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfObjects struct {\n\tbpfPrograms\n\tbpfMaps\n\tbpfVariables\n}\n\nfunc (o *bpfObjects) Close() error {\n\treturn _BpfClose(\n\t\t&o.bpfPrograms,\n\t\t&o.bpfMaps,\n\t)\n}\n\n// bpfMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfMaps struct {\n\tPktCount *ebpf.Map `ebpf:\"pkt_count\"`\n}\n\nfunc (m *bpfMaps) Close() error {\n\treturn _BpfClose(\n\t\tm.PktCount,\n\t)\n}\n\n// bpfVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfVariables struct {\n}\n\n// bpfPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfPrograms struct {\n\tCountEgressPackets *ebpf.Program `ebpf:\"count_egress_packets\"`\n}\n\nfunc (p *bpfPrograms) Close() error {\n\treturn _BpfClose(\n\t\tp.CountEgressPackets,\n\t)\n}\n\nfunc _BpfClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed bpf_bpfel.o\nvar _BpfBytes []byte\n"
  },
  {
    "path": "examples/cgroup_skb/cgroup_skb.c",
    "content": "//go:build ignore\n\n#include \"common.h\"\n\nchar __license[] SEC(\"license\") = \"Dual MIT/GPL\";\n\nstruct {\n\t__uint(type, BPF_MAP_TYPE_ARRAY);\n\t__type(key, u32);\n\t__type(value, u64);\n\t__uint(max_entries, 1);\n} pkt_count SEC(\".maps\");\n\nSEC(\"cgroup_skb/egress\")\nint count_egress_packets(struct __sk_buff *skb) {\n\tu32 key      = 0;\n\tu64 init_val = 1;\n\n\tu64 *count = bpf_map_lookup_elem(&pkt_count, &key);\n\tif (!count) {\n\t\tbpf_map_update_elem(&pkt_count, &key, &init_val, BPF_ANY);\n\t\treturn 1;\n\t}\n\t__sync_fetch_and_add(count, 1);\n\n\treturn 1;\n}\n"
  },
  {
    "path": "examples/cgroup_skb/main.go",
    "content": "//go:build linux\n\n// This program demonstrates attaching an eBPF program to a control group.\n// The eBPF program will be attached as an egress filter,\n// receiving an `__sk_buff` pointer for each outgoing packet.\n// It prints the count of total packets every second.\npackage main\n\nimport (\n\t\"bufio\"\n\t\"errors\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/link\"\n\t\"github.com/cilium/ebpf/rlimit\"\n)\n\n//go:generate go tool bpf2go -tags linux bpf cgroup_skb.c -- -I../headers\n\nfunc main() {\n\t// Allow the current process to lock memory for eBPF resources.\n\tif err := rlimit.RemoveMemlock(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Load pre-compiled programs and maps into the kernel.\n\tobjs := bpfObjects{}\n\tif err := loadBpfObjects(&objs, nil); err != nil {\n\t\tlog.Fatalf(\"loading objects: %v\", err)\n\t}\n\tdefer objs.Close()\n\n\t// Get the first-mounted cgroupv2 path.\n\tcgroupPath, err := detectCgroupPath()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Link the count_egress_packets program to the cgroup.\n\tl, err := link.AttachCgroup(link.CgroupOptions{\n\t\tPath:    cgroupPath,\n\t\tAttach:  ebpf.AttachCGroupInetEgress,\n\t\tProgram: objs.CountEgressPackets,\n\t})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer l.Close()\n\n\tlog.Println(\"Counting packets...\")\n\n\t// Read loop reporting the total amount of times the kernel\n\t// function was entered, once per second.\n\tticker := time.NewTicker(1 * time.Second)\n\tdefer ticker.Stop()\n\n\tfor range ticker.C {\n\t\tvar value uint64\n\t\tif err := objs.PktCount.Lookup(uint32(0), &value); err != nil {\n\t\t\tlog.Fatalf(\"reading map: %v\", err)\n\t\t}\n\t\tlog.Printf(\"number of packets: %d\\n\", value)\n\t}\n}\n\n// detectCgroupPath returns the first-found mount point of type cgroup2\n// and stores it in the cgroupPath global variable.\nfunc detectCgroupPath() (string, error) {\n\tf, err := os.Open(\"/proc/mounts\")\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer f.Close()\n\n\tscanner := bufio.NewScanner(f)\n\tfor scanner.Scan() {\n\t\t// example fields: cgroup2 /sys/fs/cgroup/unified cgroup2 rw,nosuid,nodev,noexec,relatime 0 0\n\t\tfields := strings.Split(scanner.Text(), \" \")\n\t\tif len(fields) >= 3 && fields[2] == \"cgroup2\" {\n\t\t\treturn fields[1], nil\n\t\t}\n\t}\n\n\treturn \"\", errors.New(\"cgroup2 not mounted\")\n}\n"
  },
  {
    "path": "examples/fentry/bpf_bpfeb.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (mips || mips64 || ppc64 || s390x) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\t\"structs\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\ntype bpfEvent struct {\n\t_     structs.HostLayout\n\tComm  [16]uint8\n\tSport uint16\n\tDport uint16\n\tSaddr uint32\n\tDaddr uint32\n}\n\n// loadBpf returns the embedded CollectionSpec for bpf.\nfunc loadBpf() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_BpfBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load bpf: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadBpfObjects loads bpf and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*bpfObjects\n//\t*bpfPrograms\n//\t*bpfMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadBpf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// bpfSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfSpecs struct {\n\tbpfProgramSpecs\n\tbpfMapSpecs\n\tbpfVariableSpecs\n}\n\n// bpfProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfProgramSpecs struct {\n\tTcpConnect *ebpf.ProgramSpec `ebpf:\"tcp_connect\"`\n}\n\n// bpfMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfMapSpecs struct {\n\tEvents *ebpf.MapSpec `ebpf:\"events\"`\n}\n\n// bpfVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfVariableSpecs struct {\n}\n\n// bpfObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfObjects struct {\n\tbpfPrograms\n\tbpfMaps\n\tbpfVariables\n}\n\nfunc (o *bpfObjects) Close() error {\n\treturn _BpfClose(\n\t\t&o.bpfPrograms,\n\t\t&o.bpfMaps,\n\t)\n}\n\n// bpfMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfMaps struct {\n\tEvents *ebpf.Map `ebpf:\"events\"`\n}\n\nfunc (m *bpfMaps) Close() error {\n\treturn _BpfClose(\n\t\tm.Events,\n\t)\n}\n\n// bpfVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfVariables struct {\n}\n\n// bpfPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfPrograms struct {\n\tTcpConnect *ebpf.Program `ebpf:\"tcp_connect\"`\n}\n\nfunc (p *bpfPrograms) Close() error {\n\treturn _BpfClose(\n\t\tp.TcpConnect,\n\t)\n}\n\nfunc _BpfClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed bpf_bpfeb.o\nvar _BpfBytes []byte\n"
  },
  {
    "path": "examples/fentry/bpf_bpfel.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64 || wasm) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\t\"structs\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\ntype bpfEvent struct {\n\t_     structs.HostLayout\n\tComm  [16]uint8\n\tSport uint16\n\tDport uint16\n\tSaddr uint32\n\tDaddr uint32\n}\n\n// loadBpf returns the embedded CollectionSpec for bpf.\nfunc loadBpf() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_BpfBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load bpf: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadBpfObjects loads bpf and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*bpfObjects\n//\t*bpfPrograms\n//\t*bpfMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadBpf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// bpfSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfSpecs struct {\n\tbpfProgramSpecs\n\tbpfMapSpecs\n\tbpfVariableSpecs\n}\n\n// bpfProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfProgramSpecs struct {\n\tTcpConnect *ebpf.ProgramSpec `ebpf:\"tcp_connect\"`\n}\n\n// bpfMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfMapSpecs struct {\n\tEvents *ebpf.MapSpec `ebpf:\"events\"`\n}\n\n// bpfVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfVariableSpecs struct {\n}\n\n// bpfObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfObjects struct {\n\tbpfPrograms\n\tbpfMaps\n\tbpfVariables\n}\n\nfunc (o *bpfObjects) Close() error {\n\treturn _BpfClose(\n\t\t&o.bpfPrograms,\n\t\t&o.bpfMaps,\n\t)\n}\n\n// bpfMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfMaps struct {\n\tEvents *ebpf.Map `ebpf:\"events\"`\n}\n\nfunc (m *bpfMaps) Close() error {\n\treturn _BpfClose(\n\t\tm.Events,\n\t)\n}\n\n// bpfVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfVariables struct {\n}\n\n// bpfPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfPrograms struct {\n\tTcpConnect *ebpf.Program `ebpf:\"tcp_connect\"`\n}\n\nfunc (p *bpfPrograms) Close() error {\n\treturn _BpfClose(\n\t\tp.TcpConnect,\n\t)\n}\n\nfunc _BpfClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed bpf_bpfel.o\nvar _BpfBytes []byte\n"
  },
  {
    "path": "examples/fentry/fentry.c",
    "content": "//go:build ignore\n\n#include \"common.h\"\n\n#include \"bpf_endian.h\"\n#include \"bpf_tracing.h\"\n\n#define AF_INET 2\n#define TASK_COMM_LEN 16\n\nchar __license[] SEC(\"license\") = \"Dual MIT/GPL\";\n\n/**\n * This example copies parts of struct sock_common and struct sock from\n * the Linux kernel, but doesn't cause any CO-RE information to be emitted\n * into the ELF object. This requires the struct layout (up until the fields\n * that are being accessed) to match the kernel's, and the example will break\n * or misbehave when this is no longer the case.\n *\n * Also note that BTF-enabled programs like fentry, fexit, fmod_ret, tp_btf,\n * lsm, etc. declared using the BPF_PROG macro can read kernel memory without\n * needing to call bpf_probe_read*().\n */\n\n/**\n * struct sock_common reflects the start of the kernel's struct sock_common.\n * It only contains the fields up until skc_family that are accessed in the\n * program, with padding to match the kernel's declaration.\n */\nstruct sock_common {\n\tunion {\n\t\tstruct {\n\t\t\t__be32 skc_daddr;\n\t\t\t__be32 skc_rcv_saddr;\n\t\t};\n\t};\n\tunion {\n\t\t// Padding out union skc_hash.\n\t\t__u32 _;\n\t};\n\tunion {\n\t\tstruct {\n\t\t\t__be16 skc_dport;\n\t\t\t__u16 skc_num;\n\t\t};\n\t};\n\tshort unsigned int skc_family;\n};\n\n/**\n * struct sock reflects the start of the kernel's struct sock.\n */\nstruct sock {\n\tstruct sock_common __sk_common;\n};\n\nstruct {\n\t__uint(type, BPF_MAP_TYPE_RINGBUF);\n\t__uint(max_entries, 1 << 24);\n\t__type(value, struct event);\n} events SEC(\".maps\");\n\n/**\n * The sample submitted to userspace over a ring buffer.\n * Emit struct event's type info into the ELF's BTF so bpf2go\n * can generate a Go type from it.\n */\nstruct event {\n\tu8 comm[16];\n\t__u16 sport;\n\t__be16 dport;\n\t__be32 saddr;\n\t__be32 daddr;\n};\n\nSEC(\"fentry/tcp_connect\")\nint BPF_PROG(tcp_connect, struct sock *sk) {\n\tif (sk->__sk_common.skc_family != AF_INET) {\n\t\treturn 0;\n\t}\n\n\tstruct event *tcp_info;\n\ttcp_info = bpf_ringbuf_reserve(&events, sizeof(struct event), 0);\n\tif (!tcp_info) {\n\t\treturn 0;\n\t}\n\n\ttcp_info->saddr = sk->__sk_common.skc_rcv_saddr;\n\ttcp_info->daddr = sk->__sk_common.skc_daddr;\n\ttcp_info->dport = sk->__sk_common.skc_dport;\n\ttcp_info->sport = bpf_htons(sk->__sk_common.skc_num);\n\n\tbpf_get_current_comm(&tcp_info->comm, TASK_COMM_LEN);\n\n\tbpf_ringbuf_submit(tcp_info, 0);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "examples/fentry/main.go",
    "content": "//go:build linux\n\n// This program demonstrates attaching a fentry eBPF program to\n// tcp_connect. It prints the command/IPs/ports information\n// once the host sent a TCP SYN packet to a destination.\n// It supports IPv4 at this example.\n//\n// Sample output:\n//\n// examples# go run -exec sudo ./fentry\n// 2021/11/06 17:51:15 Comm   Src addr      Port   -> Dest addr        Port\n// 2021/11/06 17:51:25 wget   10.0.2.15     49850  -> 142.250.72.228   443\n// 2021/11/06 17:51:46 ssh    10.0.2.15     58854  -> 10.0.2.1         22\n// 2021/11/06 18:13:15 curl   10.0.2.15     54268  -> 104.21.1.217     80\npackage main\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"log\"\n\t\"net\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n\n\t\"github.com/cilium/ebpf/link\"\n\t\"github.com/cilium/ebpf/ringbuf\"\n\t\"github.com/cilium/ebpf/rlimit\"\n)\n\n//go:generate go tool bpf2go -tags linux bpf fentry.c -- -I../headers\n\nfunc main() {\n\tstopper := make(chan os.Signal, 1)\n\tsignal.Notify(stopper, os.Interrupt, syscall.SIGTERM)\n\n\t// Allow the current process to lock memory for eBPF resources.\n\tif err := rlimit.RemoveMemlock(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Load pre-compiled programs and maps into the kernel.\n\tobjs := bpfObjects{}\n\tif err := loadBpfObjects(&objs, nil); err != nil {\n\t\tlog.Fatalf(\"loading objects: %v\", err)\n\t}\n\tdefer objs.Close()\n\n\tl, err := link.AttachTracing(link.TracingOptions{\n\t\tProgram: objs.TcpConnect,\n\t})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer l.Close()\n\n\trd, err := ringbuf.NewReader(objs.Events)\n\tif err != nil {\n\t\tlog.Fatalf(\"opening ringbuf reader: %s\", err)\n\t}\n\tdefer rd.Close()\n\n\tgo func() {\n\t\t<-stopper\n\n\t\tif err := rd.Close(); err != nil {\n\t\t\tlog.Fatalf(\"closing ringbuf reader: %s\", err)\n\t\t}\n\t}()\n\n\tlog.Printf(\"%-16s %-15s %-6s -> %-15s %-6s\",\n\t\t\"Comm\",\n\t\t\"Src addr\",\n\t\t\"Port\",\n\t\t\"Dest addr\",\n\t\t\"Port\",\n\t)\n\n\t// bpfEvent is generated by bpf2go.\n\tvar event bpfEvent\n\tfor {\n\t\trecord, err := rd.Read()\n\t\tif err != nil {\n\t\t\tif errors.Is(err, ringbuf.ErrClosed) {\n\t\t\t\tlog.Println(\"received signal, exiting..\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlog.Printf(\"reading from reader: %s\", err)\n\t\t\tcontinue\n\t\t}\n\n\t\t// Parse the ringbuf event entry into a bpfEvent structure.\n\t\tif err := binary.Read(bytes.NewBuffer(record.RawSample), binary.BigEndian, &event); err != nil {\n\t\t\tlog.Printf(\"parsing ringbuf event: %s\", err)\n\t\t\tcontinue\n\t\t}\n\n\t\tlog.Printf(\"%-16s %-15s %-6d -> %-15s %-6d\",\n\t\t\tevent.Comm,\n\t\t\tintToIP(event.Saddr),\n\t\t\tevent.Sport,\n\t\t\tintToIP(event.Daddr),\n\t\t\tevent.Dport,\n\t\t)\n\t}\n}\n\n// intToIP converts IPv4 number to net.IP\nfunc intToIP(ipNum uint32) net.IP {\n\tip := make(net.IP, 4)\n\tbinary.BigEndian.PutUint32(ip, ipNum)\n\treturn ip\n}\n"
  },
  {
    "path": "examples/headers/LICENSE.BSD-2-Clause",
    "content": "Valid-License-Identifier: BSD-2-Clause\nSPDX-URL: https://spdx.org/licenses/BSD-2-Clause.html\nUsage-Guide:\n  To use the BSD 2-clause \"Simplified\" License put the following SPDX\n  tag/value pair into a comment according to the placement guidelines in\n  the licensing rules documentation:\n    SPDX-License-Identifier: BSD-2-Clause\nLicense-Text:\n\nCopyright (c) <year> <owner> . All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice,\n   this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright\n   notice, this list of conditions and the following disclaimer in the\n   documentation and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "examples/headers/bpf_endian.h",
    "content": "/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */\n#ifndef __BPF_ENDIAN__\n#define __BPF_ENDIAN__\n\n/*\n * Isolate byte #n and put it into byte #m, for __u##b type.\n * E.g., moving byte #6 (nnnnnnnn) into byte #1 (mmmmmmmm) for __u64:\n * 1) xxxxxxxx nnnnnnnn xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx mmmmmmmm xxxxxxxx\n * 2) nnnnnnnn xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx mmmmmmmm xxxxxxxx 00000000\n * 3) 00000000 00000000 00000000 00000000 00000000 00000000 00000000 nnnnnnnn\n * 4) 00000000 00000000 00000000 00000000 00000000 00000000 nnnnnnnn 00000000\n */\n#define ___bpf_mvb(x, b, n, m) ((__u##b)(x) << (b-(n+1)*8) >> (b-8) << (m*8))\n\n#define ___bpf_swab16(x) ((__u16)(\t\t\t\\\n\t\t\t  ___bpf_mvb(x, 16, 0, 1) |\t\\\n\t\t\t  ___bpf_mvb(x, 16, 1, 0)))\n\n#define ___bpf_swab32(x) ((__u32)(\t\t\t\\\n\t\t\t  ___bpf_mvb(x, 32, 0, 3) |\t\\\n\t\t\t  ___bpf_mvb(x, 32, 1, 2) |\t\\\n\t\t\t  ___bpf_mvb(x, 32, 2, 1) |\t\\\n\t\t\t  ___bpf_mvb(x, 32, 3, 0)))\n\n#define ___bpf_swab64(x) ((__u64)(\t\t\t\\\n\t\t\t  ___bpf_mvb(x, 64, 0, 7) |\t\\\n\t\t\t  ___bpf_mvb(x, 64, 1, 6) |\t\\\n\t\t\t  ___bpf_mvb(x, 64, 2, 5) |\t\\\n\t\t\t  ___bpf_mvb(x, 64, 3, 4) |\t\\\n\t\t\t  ___bpf_mvb(x, 64, 4, 3) |\t\\\n\t\t\t  ___bpf_mvb(x, 64, 5, 2) |\t\\\n\t\t\t  ___bpf_mvb(x, 64, 6, 1) |\t\\\n\t\t\t  ___bpf_mvb(x, 64, 7, 0)))\n\n/* LLVM's BPF target selects the endianness of the CPU\n * it compiles on, or the user specifies (bpfel/bpfeb),\n * respectively. The used __BYTE_ORDER__ is defined by\n * the compiler, we cannot rely on __BYTE_ORDER from\n * libc headers, since it doesn't reflect the actual\n * requested byte order.\n *\n * Note, LLVM's BPF target has different __builtin_bswapX()\n * semantics. It does map to BPF_ALU | BPF_END | BPF_TO_BE\n * in bpfel and bpfeb case, which means below, that we map\n * to cpu_to_be16(). We could use it unconditionally in BPF\n * case, but better not rely on it, so that this header here\n * can be used from application and BPF program side, which\n * use different targets.\n */\n#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__\n# define __bpf_ntohs(x)\t\t\t__builtin_bswap16(x)\n# define __bpf_htons(x)\t\t\t__builtin_bswap16(x)\n# define __bpf_constant_ntohs(x)\t___bpf_swab16(x)\n# define __bpf_constant_htons(x)\t___bpf_swab16(x)\n# define __bpf_ntohl(x)\t\t\t__builtin_bswap32(x)\n# define __bpf_htonl(x)\t\t\t__builtin_bswap32(x)\n# define __bpf_constant_ntohl(x)\t___bpf_swab32(x)\n# define __bpf_constant_htonl(x)\t___bpf_swab32(x)\n# define __bpf_be64_to_cpu(x)\t\t__builtin_bswap64(x)\n# define __bpf_cpu_to_be64(x)\t\t__builtin_bswap64(x)\n# define __bpf_constant_be64_to_cpu(x)\t___bpf_swab64(x)\n# define __bpf_constant_cpu_to_be64(x)\t___bpf_swab64(x)\n#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__\n# define __bpf_ntohs(x)\t\t\t(x)\n# define __bpf_htons(x)\t\t\t(x)\n# define __bpf_constant_ntohs(x)\t(x)\n# define __bpf_constant_htons(x)\t(x)\n# define __bpf_ntohl(x)\t\t\t(x)\n# define __bpf_htonl(x)\t\t\t(x)\n# define __bpf_constant_ntohl(x)\t(x)\n# define __bpf_constant_htonl(x)\t(x)\n# define __bpf_be64_to_cpu(x)\t\t(x)\n# define __bpf_cpu_to_be64(x)\t\t(x)\n# define __bpf_constant_be64_to_cpu(x)  (x)\n# define __bpf_constant_cpu_to_be64(x)  (x)\n#else\n# error \"Fix your compiler's __BYTE_ORDER__?!\"\n#endif\n\n#define bpf_htons(x)\t\t\t\t\\\n\t(__builtin_constant_p(x) ?\t\t\\\n\t __bpf_constant_htons(x) : __bpf_htons(x))\n#define bpf_ntohs(x)\t\t\t\t\\\n\t(__builtin_constant_p(x) ?\t\t\\\n\t __bpf_constant_ntohs(x) : __bpf_ntohs(x))\n#define bpf_htonl(x)\t\t\t\t\\\n\t(__builtin_constant_p(x) ?\t\t\\\n\t __bpf_constant_htonl(x) : __bpf_htonl(x))\n#define bpf_ntohl(x)\t\t\t\t\\\n\t(__builtin_constant_p(x) ?\t\t\\\n\t __bpf_constant_ntohl(x) : __bpf_ntohl(x))\n#define bpf_cpu_to_be64(x)\t\t\t\\\n\t(__builtin_constant_p(x) ?\t\t\\\n\t __bpf_constant_cpu_to_be64(x) : __bpf_cpu_to_be64(x))\n#define bpf_be64_to_cpu(x)\t\t\t\\\n\t(__builtin_constant_p(x) ?\t\t\\\n\t __bpf_constant_be64_to_cpu(x) : __bpf_be64_to_cpu(x))\n\n#endif /* __BPF_ENDIAN__ */\n"
  },
  {
    "path": "examples/headers/bpf_helper_defs.h",
    "content": "/* This is auto-generated file. See bpf_doc.py for details. */\n\n/* Forward declarations of BPF structs */\nstruct bpf_fib_lookup;\nstruct bpf_sk_lookup;\nstruct bpf_perf_event_data;\nstruct bpf_perf_event_value;\nstruct bpf_pidns_info;\nstruct bpf_redir_neigh;\nstruct bpf_sock;\nstruct bpf_sock_addr;\nstruct bpf_sock_ops;\nstruct bpf_sock_tuple;\nstruct bpf_spin_lock;\nstruct bpf_sysctl;\nstruct bpf_tcp_sock;\nstruct bpf_tunnel_key;\nstruct bpf_xfrm_state;\nstruct linux_binprm;\nstruct pt_regs;\nstruct sk_reuseport_md;\nstruct sockaddr;\nstruct tcphdr;\nstruct seq_file;\nstruct tcp6_sock;\nstruct tcp_sock;\nstruct tcp_timewait_sock;\nstruct tcp_request_sock;\nstruct udp6_sock;\nstruct unix_sock;\nstruct task_struct;\nstruct __sk_buff;\nstruct sk_msg_md;\nstruct xdp_md;\nstruct path;\nstruct btf_ptr;\nstruct inode;\nstruct socket;\nstruct file;\nstruct bpf_timer;\n\n/*\n * bpf_map_lookup_elem\n *\n * \tPerform a lookup in *map* for an entry associated to *key*.\n *\n * Returns\n * \tMap value associated to *key*, or **NULL** if no entry was\n * \tfound.\n */\nstatic void *(*bpf_map_lookup_elem)(void *map, const void *key) = (void *) 1;\n\n/*\n * bpf_map_update_elem\n *\n * \tAdd or update the value of the entry associated to *key* in\n * \t*map* with *value*. *flags* is one of:\n *\n * \t**BPF_NOEXIST**\n * \t\tThe entry for *key* must not exist in the map.\n * \t**BPF_EXIST**\n * \t\tThe entry for *key* must already exist in the map.\n * \t**BPF_ANY**\n * \t\tNo condition on the existence of the entry for *key*.\n *\n * \tFlag value **BPF_NOEXIST** cannot be used for maps of types\n * \t**BPF_MAP_TYPE_ARRAY** or **BPF_MAP_TYPE_PERCPU_ARRAY**  (all\n * \telements always exist), the helper would return an error.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_map_update_elem)(void *map, const void *key, const void *value, __u64 flags) = (void *) 2;\n\n/*\n * bpf_map_delete_elem\n *\n * \tDelete entry with *key* from *map*.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_map_delete_elem)(void *map, const void *key) = (void *) 3;\n\n/*\n * bpf_probe_read\n *\n * \tFor tracing programs, safely attempt to read *size* bytes from\n * \tkernel space address *unsafe_ptr* and store the data in *dst*.\n *\n * \tGenerally, use **bpf_probe_read_user**\\ () or\n * \t**bpf_probe_read_kernel**\\ () instead.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_probe_read)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 4;\n\n/*\n * bpf_ktime_get_ns\n *\n * \tReturn the time elapsed since system boot, in nanoseconds.\n * \tDoes not include time the system was suspended.\n * \tSee: **clock_gettime**\\ (**CLOCK_MONOTONIC**)\n *\n * Returns\n * \tCurrent *ktime*.\n */\nstatic __u64 (*bpf_ktime_get_ns)(void) = (void *) 5;\n\n/*\n * bpf_trace_printk\n *\n * \tThis helper is a \"printk()-like\" facility for debugging. It\n * \tprints a message defined by format *fmt* (of size *fmt_size*)\n * \tto file *\\/sys/kernel/debug/tracing/trace* from DebugFS, if\n * \tavailable. It can take up to three additional **u64**\n * \targuments (as an eBPF helpers, the total number of arguments is\n * \tlimited to five).\n *\n * \tEach time the helper is called, it appends a line to the trace.\n * \tLines are discarded while *\\/sys/kernel/debug/tracing/trace* is\n * \topen, use *\\/sys/kernel/debug/tracing/trace_pipe* to avoid this.\n * \tThe format of the trace is customizable, and the exact output\n * \tone will get depends on the options set in\n * \t*\\/sys/kernel/debug/tracing/trace_options* (see also the\n * \t*README* file under the same directory). However, it usually\n * \tdefaults to something like:\n *\n * \t::\n *\n * \t\ttelnet-470   [001] .N.. 419421.045894: 0x00000001: <formatted msg>\n *\n * \tIn the above:\n *\n * \t\t* ``telnet`` is the name of the current task.\n * \t\t* ``470`` is the PID of the current task.\n * \t\t* ``001`` is the CPU number on which the task is\n * \t\t  running.\n * \t\t* In ``.N..``, each character refers to a set of\n * \t\t  options (whether irqs are enabled, scheduling\n * \t\t  options, whether hard/softirqs are running, level of\n * \t\t  preempt_disabled respectively). **N** means that\n * \t\t  **TIF_NEED_RESCHED** and **PREEMPT_NEED_RESCHED**\n * \t\t  are set.\n * \t\t* ``419421.045894`` is a timestamp.\n * \t\t* ``0x00000001`` is a fake value used by BPF for the\n * \t\t  instruction pointer register.\n * \t\t* ``<formatted msg>`` is the message formatted with\n * \t\t  *fmt*.\n *\n * \tThe conversion specifiers supported by *fmt* are similar, but\n * \tmore limited than for printk(). They are **%d**, **%i**,\n * \t**%u**, **%x**, **%ld**, **%li**, **%lu**, **%lx**, **%lld**,\n * \t**%lli**, **%llu**, **%llx**, **%p**, **%s**. No modifier (size\n * \tof field, padding with zeroes, etc.) is available, and the\n * \thelper will return **-EINVAL** (but print nothing) if it\n * \tencounters an unknown specifier.\n *\n * \tAlso, note that **bpf_trace_printk**\\ () is slow, and should\n * \tonly be used for debugging purposes. For this reason, a notice\n * \tblock (spanning several lines) is printed to kernel logs and\n * \tstates that the helper should not be used \"for production use\"\n * \tthe first time this helper is used (or more precisely, when\n * \t**trace_printk**\\ () buffers are allocated). For passing values\n * \tto user space, perf events should be preferred.\n *\n * Returns\n * \tThe number of bytes written to the buffer, or a negative error\n * \tin case of failure.\n */\nstatic long (*bpf_trace_printk)(const char *fmt, __u32 fmt_size, ...) = (void *) 6;\n\n/*\n * bpf_get_prandom_u32\n *\n * \tGet a pseudo-random number.\n *\n * \tFrom a security point of view, this helper uses its own\n * \tpseudo-random internal state, and cannot be used to infer the\n * \tseed of other random functions in the kernel. However, it is\n * \tessential to note that the generator used by the helper is not\n * \tcryptographically secure.\n *\n * Returns\n * \tA random 32-bit unsigned value.\n */\nstatic __u32 (*bpf_get_prandom_u32)(void) = (void *) 7;\n\n/*\n * bpf_get_smp_processor_id\n *\n * \tGet the SMP (symmetric multiprocessing) processor id. Note that\n * \tall programs run with migration disabled, which means that the\n * \tSMP processor id is stable during all the execution of the\n * \tprogram.\n *\n * Returns\n * \tThe SMP id of the processor running the program.\n */\nstatic __u32 (*bpf_get_smp_processor_id)(void) = (void *) 8;\n\n/*\n * bpf_skb_store_bytes\n *\n * \tStore *len* bytes from address *from* into the packet\n * \tassociated to *skb*, at *offset*. *flags* are a combination of\n * \t**BPF_F_RECOMPUTE_CSUM** (automatically recompute the\n * \tchecksum for the packet after storing the bytes) and\n * \t**BPF_F_INVALIDATE_HASH** (set *skb*\\ **->hash**, *skb*\\\n * \t**->swhash** and *skb*\\ **->l4hash** to 0).\n *\n * \tA call to this helper is susceptible to change the underlying\n * \tpacket buffer. Therefore, at load time, all checks on pointers\n * \tpreviously done by the verifier are invalidated and must be\n * \tperformed again, if the helper is used in combination with\n * \tdirect packet access.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_skb_store_bytes)(struct __sk_buff *skb, __u32 offset, const void *from, __u32 len, __u64 flags) = (void *) 9;\n\n/*\n * bpf_l3_csum_replace\n *\n * \tRecompute the layer 3 (e.g. IP) checksum for the packet\n * \tassociated to *skb*. Computation is incremental, so the helper\n * \tmust know the former value of the header field that was\n * \tmodified (*from*), the new value of this field (*to*), and the\n * \tnumber of bytes (2 or 4) for this field, stored in *size*.\n * \tAlternatively, it is possible to store the difference between\n * \tthe previous and the new values of the header field in *to*, by\n * \tsetting *from* and *size* to 0. For both methods, *offset*\n * \tindicates the location of the IP checksum within the packet.\n *\n * \tThis helper works in combination with **bpf_csum_diff**\\ (),\n * \twhich does not update the checksum in-place, but offers more\n * \tflexibility and can handle sizes larger than 2 or 4 for the\n * \tchecksum to update.\n *\n * \tA call to this helper is susceptible to change the underlying\n * \tpacket buffer. Therefore, at load time, all checks on pointers\n * \tpreviously done by the verifier are invalidated and must be\n * \tperformed again, if the helper is used in combination with\n * \tdirect packet access.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_l3_csum_replace)(struct __sk_buff *skb, __u32 offset, __u64 from, __u64 to, __u64 size) = (void *) 10;\n\n/*\n * bpf_l4_csum_replace\n *\n * \tRecompute the layer 4 (e.g. TCP, UDP or ICMP) checksum for the\n * \tpacket associated to *skb*. Computation is incremental, so the\n * \thelper must know the former value of the header field that was\n * \tmodified (*from*), the new value of this field (*to*), and the\n * \tnumber of bytes (2 or 4) for this field, stored on the lowest\n * \tfour bits of *flags*. Alternatively, it is possible to store\n * \tthe difference between the previous and the new values of the\n * \theader field in *to*, by setting *from* and the four lowest\n * \tbits of *flags* to 0. For both methods, *offset* indicates the\n * \tlocation of the IP checksum within the packet. In addition to\n * \tthe size of the field, *flags* can be added (bitwise OR) actual\n * \tflags. With **BPF_F_MARK_MANGLED_0**, a null checksum is left\n * \tuntouched (unless **BPF_F_MARK_ENFORCE** is added as well), and\n * \tfor updates resulting in a null checksum the value is set to\n * \t**CSUM_MANGLED_0** instead. Flag **BPF_F_PSEUDO_HDR** indicates\n * \tthe checksum is to be computed against a pseudo-header.\n *\n * \tThis helper works in combination with **bpf_csum_diff**\\ (),\n * \twhich does not update the checksum in-place, but offers more\n * \tflexibility and can handle sizes larger than 2 or 4 for the\n * \tchecksum to update.\n *\n * \tA call to this helper is susceptible to change the underlying\n * \tpacket buffer. Therefore, at load time, all checks on pointers\n * \tpreviously done by the verifier are invalidated and must be\n * \tperformed again, if the helper is used in combination with\n * \tdirect packet access.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_l4_csum_replace)(struct __sk_buff *skb, __u32 offset, __u64 from, __u64 to, __u64 flags) = (void *) 11;\n\n/*\n * bpf_tail_call\n *\n * \tThis special helper is used to trigger a \"tail call\", or in\n * \tother words, to jump into another eBPF program. The same stack\n * \tframe is used (but values on stack and in registers for the\n * \tcaller are not accessible to the callee). This mechanism allows\n * \tfor program chaining, either for raising the maximum number of\n * \tavailable eBPF instructions, or to execute given programs in\n * \tconditional blocks. For security reasons, there is an upper\n * \tlimit to the number of successive tail calls that can be\n * \tperformed.\n *\n * \tUpon call of this helper, the program attempts to jump into a\n * \tprogram referenced at index *index* in *prog_array_map*, a\n * \tspecial map of type **BPF_MAP_TYPE_PROG_ARRAY**, and passes\n * \t*ctx*, a pointer to the context.\n *\n * \tIf the call succeeds, the kernel immediately runs the first\n * \tinstruction of the new program. This is not a function call,\n * \tand it never returns to the previous program. If the call\n * \tfails, then the helper has no effect, and the caller continues\n * \tto run its subsequent instructions. A call can fail if the\n * \tdestination program for the jump does not exist (i.e. *index*\n * \tis superior to the number of entries in *prog_array_map*), or\n * \tif the maximum number of tail calls has been reached for this\n * \tchain of programs. This limit is defined in the kernel by the\n * \tmacro **MAX_TAIL_CALL_CNT** (not accessible to user space),\n * \twhich is currently set to 33.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_tail_call)(void *ctx, void *prog_array_map, __u32 index) = (void *) 12;\n\n/*\n * bpf_clone_redirect\n *\n * \tClone and redirect the packet associated to *skb* to another\n * \tnet device of index *ifindex*. Both ingress and egress\n * \tinterfaces can be used for redirection. The **BPF_F_INGRESS**\n * \tvalue in *flags* is used to make the distinction (ingress path\n * \tis selected if the flag is present, egress path otherwise).\n * \tThis is the only flag supported for now.\n *\n * \tIn comparison with **bpf_redirect**\\ () helper,\n * \t**bpf_clone_redirect**\\ () has the associated cost of\n * \tduplicating the packet buffer, but this can be executed out of\n * \tthe eBPF program. Conversely, **bpf_redirect**\\ () is more\n * \tefficient, but it is handled through an action code where the\n * \tredirection happens only after the eBPF program has returned.\n *\n * \tA call to this helper is susceptible to change the underlying\n * \tpacket buffer. Therefore, at load time, all checks on pointers\n * \tpreviously done by the verifier are invalidated and must be\n * \tperformed again, if the helper is used in combination with\n * \tdirect packet access.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_clone_redirect)(struct __sk_buff *skb, __u32 ifindex, __u64 flags) = (void *) 13;\n\n/*\n * bpf_get_current_pid_tgid\n *\n *\n * Returns\n * \tA 64-bit integer containing the current tgid and pid, and\n * \tcreated as such:\n * \t*current_task*\\ **->tgid << 32 \\|**\n * \t*current_task*\\ **->pid**.\n */\nstatic __u64 (*bpf_get_current_pid_tgid)(void) = (void *) 14;\n\n/*\n * bpf_get_current_uid_gid\n *\n *\n * Returns\n * \tA 64-bit integer containing the current GID and UID, and\n * \tcreated as such: *current_gid* **<< 32 \\|** *current_uid*.\n */\nstatic __u64 (*bpf_get_current_uid_gid)(void) = (void *) 15;\n\n/*\n * bpf_get_current_comm\n *\n * \tCopy the **comm** attribute of the current task into *buf* of\n * \t*size_of_buf*. The **comm** attribute contains the name of\n * \tthe executable (excluding the path) for the current task. The\n * \t*size_of_buf* must be strictly positive. On success, the\n * \thelper makes sure that the *buf* is NUL-terminated. On failure,\n * \tit is filled with zeroes.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_get_current_comm)(void *buf, __u32 size_of_buf) = (void *) 16;\n\n/*\n * bpf_get_cgroup_classid\n *\n * \tRetrieve the classid for the current task, i.e. for the net_cls\n * \tcgroup to which *skb* belongs.\n *\n * \tThis helper can be used on TC egress path, but not on ingress.\n *\n * \tThe net_cls cgroup provides an interface to tag network packets\n * \tbased on a user-provided identifier for all traffic coming from\n * \tthe tasks belonging to the related cgroup. See also the related\n * \tkernel documentation, available from the Linux sources in file\n * \t*Documentation/admin-guide/cgroup-v1/net_cls.rst*.\n *\n * \tThe Linux kernel has two versions for cgroups: there are\n * \tcgroups v1 and cgroups v2. Both are available to users, who can\n * \tuse a mixture of them, but note that the net_cls cgroup is for\n * \tcgroup v1 only. This makes it incompatible with BPF programs\n * \trun on cgroups, which is a cgroup-v2-only feature (a socket can\n * \tonly hold data for one version of cgroups at a time).\n *\n * \tThis helper is only available is the kernel was compiled with\n * \tthe **CONFIG_CGROUP_NET_CLASSID** configuration option set to\n * \t\"**y**\" or to \"**m**\".\n *\n * Returns\n * \tThe classid, or 0 for the default unconfigured classid.\n */\nstatic __u32 (*bpf_get_cgroup_classid)(struct __sk_buff *skb) = (void *) 17;\n\n/*\n * bpf_skb_vlan_push\n *\n * \tPush a *vlan_tci* (VLAN tag control information) of protocol\n * \t*vlan_proto* to the packet associated to *skb*, then update\n * \tthe checksum. Note that if *vlan_proto* is different from\n * \t**ETH_P_8021Q** and **ETH_P_8021AD**, it is considered to\n * \tbe **ETH_P_8021Q**.\n *\n * \tA call to this helper is susceptible to change the underlying\n * \tpacket buffer. Therefore, at load time, all checks on pointers\n * \tpreviously done by the verifier are invalidated and must be\n * \tperformed again, if the helper is used in combination with\n * \tdirect packet access.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_skb_vlan_push)(struct __sk_buff *skb, __be16 vlan_proto, __u16 vlan_tci) = (void *) 18;\n\n/*\n * bpf_skb_vlan_pop\n *\n * \tPop a VLAN header from the packet associated to *skb*.\n *\n * \tA call to this helper is susceptible to change the underlying\n * \tpacket buffer. Therefore, at load time, all checks on pointers\n * \tpreviously done by the verifier are invalidated and must be\n * \tperformed again, if the helper is used in combination with\n * \tdirect packet access.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_skb_vlan_pop)(struct __sk_buff *skb) = (void *) 19;\n\n/*\n * bpf_skb_get_tunnel_key\n *\n * \tGet tunnel metadata. This helper takes a pointer *key* to an\n * \tempty **struct bpf_tunnel_key** of **size**, that will be\n * \tfilled with tunnel metadata for the packet associated to *skb*.\n * \tThe *flags* can be set to **BPF_F_TUNINFO_IPV6**, which\n * \tindicates that the tunnel is based on IPv6 protocol instead of\n * \tIPv4.\n *\n * \tThe **struct bpf_tunnel_key** is an object that generalizes the\n * \tprincipal parameters used by various tunneling protocols into a\n * \tsingle struct. This way, it can be used to easily make a\n * \tdecision based on the contents of the encapsulation header,\n * \t\"summarized\" in this struct. In particular, it holds the IP\n * \taddress of the remote end (IPv4 or IPv6, depending on the case)\n * \tin *key*\\ **->remote_ipv4** or *key*\\ **->remote_ipv6**. Also,\n * \tthis struct exposes the *key*\\ **->tunnel_id**, which is\n * \tgenerally mapped to a VNI (Virtual Network Identifier), making\n * \tit programmable together with the **bpf_skb_set_tunnel_key**\\\n * \t() helper.\n *\n * \tLet's imagine that the following code is part of a program\n * \tattached to the TC ingress interface, on one end of a GRE\n * \ttunnel, and is supposed to filter out all messages coming from\n * \tremote ends with IPv4 address other than 10.0.0.1:\n *\n * \t::\n *\n * \t\tint ret;\n * \t\tstruct bpf_tunnel_key key = {};\n *\n * \t\tret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 0);\n * \t\tif (ret < 0)\n * \t\t\treturn TC_ACT_SHOT;\t// drop packet\n *\n * \t\tif (key.remote_ipv4 != 0x0a000001)\n * \t\t\treturn TC_ACT_SHOT;\t// drop packet\n *\n * \t\treturn TC_ACT_OK;\t\t// accept packet\n *\n * \tThis interface can also be used with all encapsulation devices\n * \tthat can operate in \"collect metadata\" mode: instead of having\n * \tone network device per specific configuration, the \"collect\n * \tmetadata\" mode only requires a single device where the\n * \tconfiguration can be extracted from this helper.\n *\n * \tThis can be used together with various tunnels such as VXLan,\n * \tGeneve, GRE or IP in IP (IPIP).\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_skb_get_tunnel_key)(struct __sk_buff *skb, struct bpf_tunnel_key *key, __u32 size, __u64 flags) = (void *) 20;\n\n/*\n * bpf_skb_set_tunnel_key\n *\n * \tPopulate tunnel metadata for packet associated to *skb.* The\n * \ttunnel metadata is set to the contents of *key*, of *size*. The\n * \t*flags* can be set to a combination of the following values:\n *\n * \t**BPF_F_TUNINFO_IPV6**\n * \t\tIndicate that the tunnel is based on IPv6 protocol\n * \t\tinstead of IPv4.\n * \t**BPF_F_ZERO_CSUM_TX**\n * \t\tFor IPv4 packets, add a flag to tunnel metadata\n * \t\tindicating that checksum computation should be skipped\n * \t\tand checksum set to zeroes.\n * \t**BPF_F_DONT_FRAGMENT**\n * \t\tAdd a flag to tunnel metadata indicating that the\n * \t\tpacket should not be fragmented.\n * \t**BPF_F_SEQ_NUMBER**\n * \t\tAdd a flag to tunnel metadata indicating that a\n * \t\tsequence number should be added to tunnel header before\n * \t\tsending the packet. This flag was added for GRE\n * \t\tencapsulation, but might be used with other protocols\n * \t\tas well in the future.\n *\n * \tHere is a typical usage on the transmit path:\n *\n * \t::\n *\n * \t\tstruct bpf_tunnel_key key;\n * \t\t     populate key ...\n * \t\tbpf_skb_set_tunnel_key(skb, &key, sizeof(key), 0);\n * \t\tbpf_clone_redirect(skb, vxlan_dev_ifindex, 0);\n *\n * \tSee also the description of the **bpf_skb_get_tunnel_key**\\ ()\n * \thelper for additional information.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_skb_set_tunnel_key)(struct __sk_buff *skb, struct bpf_tunnel_key *key, __u32 size, __u64 flags) = (void *) 21;\n\n/*\n * bpf_perf_event_read\n *\n * \tRead the value of a perf event counter. This helper relies on a\n * \t*map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of\n * \tthe perf event counter is selected when *map* is updated with\n * \tperf event file descriptors. The *map* is an array whose size\n * \tis the number of available CPUs, and each cell contains a value\n * \trelative to one CPU. The value to retrieve is indicated by\n * \t*flags*, that contains the index of the CPU to look up, masked\n * \twith **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to\n * \t**BPF_F_CURRENT_CPU** to indicate that the value for the\n * \tcurrent CPU should be retrieved.\n *\n * \tNote that before Linux 4.13, only hardware perf event can be\n * \tretrieved.\n *\n * \tAlso, be aware that the newer helper\n * \t**bpf_perf_event_read_value**\\ () is recommended over\n * \t**bpf_perf_event_read**\\ () in general. The latter has some ABI\n * \tquirks where error and counter value are used as a return code\n * \t(which is wrong to do since ranges may overlap). This issue is\n * \tfixed with **bpf_perf_event_read_value**\\ (), which at the same\n * \ttime provides more features over the **bpf_perf_event_read**\\\n * \t() interface. Please refer to the description of\n * \t**bpf_perf_event_read_value**\\ () for details.\n *\n * Returns\n * \tThe value of the perf event counter read from the map, or a\n * \tnegative error code in case of failure.\n */\nstatic __u64 (*bpf_perf_event_read)(void *map, __u64 flags) = (void *) 22;\n\n/*\n * bpf_redirect\n *\n * \tRedirect the packet to another net device of index *ifindex*.\n * \tThis helper is somewhat similar to **bpf_clone_redirect**\\\n * \t(), except that the packet is not cloned, which provides\n * \tincreased performance.\n *\n * \tExcept for XDP, both ingress and egress interfaces can be used\n * \tfor redirection. The **BPF_F_INGRESS** value in *flags* is used\n * \tto make the distinction (ingress path is selected if the flag\n * \tis present, egress path otherwise). Currently, XDP only\n * \tsupports redirection to the egress interface, and accepts no\n * \tflag at all.\n *\n * \tThe same effect can also be attained with the more generic\n * \t**bpf_redirect_map**\\ (), which uses a BPF map to store the\n * \tredirect target instead of providing it directly to the helper.\n *\n * Returns\n * \tFor XDP, the helper returns **XDP_REDIRECT** on success or\n * \t**XDP_ABORTED** on error. For other program types, the values\n * \tare **TC_ACT_REDIRECT** on success or **TC_ACT_SHOT** on\n * \terror.\n */\nstatic long (*bpf_redirect)(__u32 ifindex, __u64 flags) = (void *) 23;\n\n/*\n * bpf_get_route_realm\n *\n * \tRetrieve the realm or the route, that is to say the\n * \t**tclassid** field of the destination for the *skb*. The\n * \tidentifier retrieved is a user-provided tag, similar to the\n * \tone used with the net_cls cgroup (see description for\n * \t**bpf_get_cgroup_classid**\\ () helper), but here this tag is\n * \theld by a route (a destination entry), not by a task.\n *\n * \tRetrieving this identifier works with the clsact TC egress hook\n * \t(see also **tc-bpf(8)**), or alternatively on conventional\n * \tclassful egress qdiscs, but not on TC ingress path. In case of\n * \tclsact TC egress hook, this has the advantage that, internally,\n * \tthe destination entry has not been dropped yet in the transmit\n * \tpath. Therefore, the destination entry does not need to be\n * \tartificially held via **netif_keep_dst**\\ () for a classful\n * \tqdisc until the *skb* is freed.\n *\n * \tThis helper is available only if the kernel was compiled with\n * \t**CONFIG_IP_ROUTE_CLASSID** configuration option.\n *\n * Returns\n * \tThe realm of the route for the packet associated to *skb*, or 0\n * \tif none was found.\n */\nstatic __u32 (*bpf_get_route_realm)(struct __sk_buff *skb) = (void *) 24;\n\n/*\n * bpf_perf_event_output\n *\n * \tWrite raw *data* blob into a special BPF perf event held by\n * \t*map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf\n * \tevent must have the following attributes: **PERF_SAMPLE_RAW**\n * \tas **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and\n * \t**PERF_COUNT_SW_BPF_OUTPUT** as **config**.\n *\n * \tThe *flags* are used to indicate the index in *map* for which\n * \tthe value must be put, masked with **BPF_F_INDEX_MASK**.\n * \tAlternatively, *flags* can be set to **BPF_F_CURRENT_CPU**\n * \tto indicate that the index of the current CPU core should be\n * \tused.\n *\n * \tThe value to write, of *size*, is passed through eBPF stack and\n * \tpointed by *data*.\n *\n * \tThe context of the program *ctx* needs also be passed to the\n * \thelper.\n *\n * \tOn user space, a program willing to read the values needs to\n * \tcall **perf_event_open**\\ () on the perf event (either for\n * \tone or for all CPUs) and to store the file descriptor into the\n * \t*map*. This must be done before the eBPF program can send data\n * \tinto it. An example is available in file\n * \t*samples/bpf/trace_output_user.c* in the Linux kernel source\n * \ttree (the eBPF program counterpart is in\n * \t*samples/bpf/trace_output_kern.c*).\n *\n * \t**bpf_perf_event_output**\\ () achieves better performance\n * \tthan **bpf_trace_printk**\\ () for sharing data with user\n * \tspace, and is much better suitable for streaming data from eBPF\n * \tprograms.\n *\n * \tNote that this helper is not restricted to tracing use cases\n * \tand can be used with programs attached to TC or XDP as well,\n * \twhere it allows for passing data to user space listeners. Data\n * \tcan be:\n *\n * \t* Only custom structs,\n * \t* Only the packet payload, or\n * \t* A combination of both.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_perf_event_output)(void *ctx, void *map, __u64 flags, void *data, __u64 size) = (void *) 25;\n\n/*\n * bpf_skb_load_bytes\n *\n * \tThis helper was provided as an easy way to load data from a\n * \tpacket. It can be used to load *len* bytes from *offset* from\n * \tthe packet associated to *skb*, into the buffer pointed by\n * \t*to*.\n *\n * \tSince Linux 4.7, usage of this helper has mostly been replaced\n * \tby \"direct packet access\", enabling packet data to be\n * \tmanipulated with *skb*\\ **->data** and *skb*\\ **->data_end**\n * \tpointing respectively to the first byte of packet data and to\n * \tthe byte after the last byte of packet data. However, it\n * \tremains useful if one wishes to read large quantities of data\n * \tat once from a packet into the eBPF stack.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_skb_load_bytes)(const void *skb, __u32 offset, void *to, __u32 len) = (void *) 26;\n\n/*\n * bpf_get_stackid\n *\n * \tWalk a user or a kernel stack and return its id. To achieve\n * \tthis, the helper needs *ctx*, which is a pointer to the context\n * \ton which the tracing program is executed, and a pointer to a\n * \t*map* of type **BPF_MAP_TYPE_STACK_TRACE**.\n *\n * \tThe last argument, *flags*, holds the number of stack frames to\n * \tskip (from 0 to 255), masked with\n * \t**BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set\n * \ta combination of the following flags:\n *\n * \t**BPF_F_USER_STACK**\n * \t\tCollect a user space stack instead of a kernel stack.\n * \t**BPF_F_FAST_STACK_CMP**\n * \t\tCompare stacks by hash only.\n * \t**BPF_F_REUSE_STACKID**\n * \t\tIf two different stacks hash into the same *stackid*,\n * \t\tdiscard the old one.\n *\n * \tThe stack id retrieved is a 32 bit long integer handle which\n * \tcan be further combined with other data (including other stack\n * \tids) and used as a key into maps. This can be useful for\n * \tgenerating a variety of graphs (such as flame graphs or off-cpu\n * \tgraphs).\n *\n * \tFor walking a stack, this helper is an improvement over\n * \t**bpf_probe_read**\\ (), which can be used with unrolled loops\n * \tbut is not efficient and consumes a lot of eBPF instructions.\n * \tInstead, **bpf_get_stackid**\\ () can collect up to\n * \t**PERF_MAX_STACK_DEPTH** both kernel and user frames. Note that\n * \tthis limit can be controlled with the **sysctl** program, and\n * \tthat it should be manually increased in order to profile long\n * \tuser stacks (such as stacks for Java programs). To do so, use:\n *\n * \t::\n *\n * \t\t# sysctl kernel.perf_event_max_stack=<new value>\n *\n * Returns\n * \tThe positive or null stack id on success, or a negative error\n * \tin case of failure.\n */\nstatic long (*bpf_get_stackid)(void *ctx, void *map, __u64 flags) = (void *) 27;\n\n/*\n * bpf_csum_diff\n *\n * \tCompute a checksum difference, from the raw buffer pointed by\n * \t*from*, of length *from_size* (that must be a multiple of 4),\n * \ttowards the raw buffer pointed by *to*, of size *to_size*\n * \t(same remark). An optional *seed* can be added to the value\n * \t(this can be cascaded, the seed may come from a previous call\n * \tto the helper).\n *\n * \tThis is flexible enough to be used in several ways:\n *\n * \t* With *from_size* == 0, *to_size* > 0 and *seed* set to\n * \t  checksum, it can be used when pushing new data.\n * \t* With *from_size* > 0, *to_size* == 0 and *seed* set to\n * \t  checksum, it can be used when removing data from a packet.\n * \t* With *from_size* > 0, *to_size* > 0 and *seed* set to 0, it\n * \t  can be used to compute a diff. Note that *from_size* and\n * \t  *to_size* do not need to be equal.\n *\n * \tThis helper can be used in combination with\n * \t**bpf_l3_csum_replace**\\ () and **bpf_l4_csum_replace**\\ (), to\n * \twhich one can feed in the difference computed with\n * \t**bpf_csum_diff**\\ ().\n *\n * Returns\n * \tThe checksum result, or a negative error code in case of\n * \tfailure.\n */\nstatic __s64 (*bpf_csum_diff)(__be32 *from, __u32 from_size, __be32 *to, __u32 to_size, __wsum seed) = (void *) 28;\n\n/*\n * bpf_skb_get_tunnel_opt\n *\n * \tRetrieve tunnel options metadata for the packet associated to\n * \t*skb*, and store the raw tunnel option data to the buffer *opt*\n * \tof *size*.\n *\n * \tThis helper can be used with encapsulation devices that can\n * \toperate in \"collect metadata\" mode (please refer to the related\n * \tnote in the description of **bpf_skb_get_tunnel_key**\\ () for\n * \tmore details). A particular example where this can be used is\n * \tin combination with the Geneve encapsulation protocol, where it\n * \tallows for pushing (with **bpf_skb_get_tunnel_opt**\\ () helper)\n * \tand retrieving arbitrary TLVs (Type-Length-Value headers) from\n * \tthe eBPF program. This allows for full customization of these\n * \theaders.\n *\n * Returns\n * \tThe size of the option data retrieved.\n */\nstatic long (*bpf_skb_get_tunnel_opt)(struct __sk_buff *skb, void *opt, __u32 size) = (void *) 29;\n\n/*\n * bpf_skb_set_tunnel_opt\n *\n * \tSet tunnel options metadata for the packet associated to *skb*\n * \tto the option data contained in the raw buffer *opt* of *size*.\n *\n * \tSee also the description of the **bpf_skb_get_tunnel_opt**\\ ()\n * \thelper for additional information.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_skb_set_tunnel_opt)(struct __sk_buff *skb, void *opt, __u32 size) = (void *) 30;\n\n/*\n * bpf_skb_change_proto\n *\n * \tChange the protocol of the *skb* to *proto*. Currently\n * \tsupported are transition from IPv4 to IPv6, and from IPv6 to\n * \tIPv4. The helper takes care of the groundwork for the\n * \ttransition, including resizing the socket buffer. The eBPF\n * \tprogram is expected to fill the new headers, if any, via\n * \t**skb_store_bytes**\\ () and to recompute the checksums with\n * \t**bpf_l3_csum_replace**\\ () and **bpf_l4_csum_replace**\\\n * \t(). The main case for this helper is to perform NAT64\n * \toperations out of an eBPF program.\n *\n * \tInternally, the GSO type is marked as dodgy so that headers are\n * \tchecked and segments are recalculated by the GSO/GRO engine.\n * \tThe size for GSO target is adapted as well.\n *\n * \tAll values for *flags* are reserved for future usage, and must\n * \tbe left at zero.\n *\n * \tA call to this helper is susceptible to change the underlying\n * \tpacket buffer. Therefore, at load time, all checks on pointers\n * \tpreviously done by the verifier are invalidated and must be\n * \tperformed again, if the helper is used in combination with\n * \tdirect packet access.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_skb_change_proto)(struct __sk_buff *skb, __be16 proto, __u64 flags) = (void *) 31;\n\n/*\n * bpf_skb_change_type\n *\n * \tChange the packet type for the packet associated to *skb*. This\n * \tcomes down to setting *skb*\\ **->pkt_type** to *type*, except\n * \tthe eBPF program does not have a write access to *skb*\\\n * \t**->pkt_type** beside this helper. Using a helper here allows\n * \tfor graceful handling of errors.\n *\n * \tThe major use case is to change incoming *skb*s to\n * \t**PACKET_HOST** in a programmatic way instead of having to\n * \trecirculate via **redirect**\\ (..., **BPF_F_INGRESS**), for\n * \texample.\n *\n * \tNote that *type* only allows certain values. At this time, they\n * \tare:\n *\n * \t**PACKET_HOST**\n * \t\tPacket is for us.\n * \t**PACKET_BROADCAST**\n * \t\tSend packet to all.\n * \t**PACKET_MULTICAST**\n * \t\tSend packet to group.\n * \t**PACKET_OTHERHOST**\n * \t\tSend packet to someone else.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_skb_change_type)(struct __sk_buff *skb, __u32 type) = (void *) 32;\n\n/*\n * bpf_skb_under_cgroup\n *\n * \tCheck whether *skb* is a descendant of the cgroup2 held by\n * \t*map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*.\n *\n * Returns\n * \tThe return value depends on the result of the test, and can be:\n *\n * \t* 0, if the *skb* failed the cgroup2 descendant test.\n * \t* 1, if the *skb* succeeded the cgroup2 descendant test.\n * \t* A negative error code, if an error occurred.\n */\nstatic long (*bpf_skb_under_cgroup)(struct __sk_buff *skb, void *map, __u32 index) = (void *) 33;\n\n/*\n * bpf_get_hash_recalc\n *\n * \tRetrieve the hash of the packet, *skb*\\ **->hash**. If it is\n * \tnot set, in particular if the hash was cleared due to mangling,\n * \trecompute this hash. Later accesses to the hash can be done\n * \tdirectly with *skb*\\ **->hash**.\n *\n * \tCalling **bpf_set_hash_invalid**\\ (), changing a packet\n * \tprototype with **bpf_skb_change_proto**\\ (), or calling\n * \t**bpf_skb_store_bytes**\\ () with the\n * \t**BPF_F_INVALIDATE_HASH** are actions susceptible to clear\n * \tthe hash and to trigger a new computation for the next call to\n * \t**bpf_get_hash_recalc**\\ ().\n *\n * Returns\n * \tThe 32-bit hash.\n */\nstatic __u32 (*bpf_get_hash_recalc)(struct __sk_buff *skb) = (void *) 34;\n\n/*\n * bpf_get_current_task\n *\n *\n * Returns\n * \tA pointer to the current task struct.\n */\nstatic __u64 (*bpf_get_current_task)(void) = (void *) 35;\n\n/*\n * bpf_probe_write_user\n *\n * \tAttempt in a safe way to write *len* bytes from the buffer\n * \t*src* to *dst* in memory. It only works for threads that are in\n * \tuser context, and *dst* must be a valid user space address.\n *\n * \tThis helper should not be used to implement any kind of\n * \tsecurity mechanism because of TOC-TOU attacks, but rather to\n * \tdebug, divert, and manipulate execution of semi-cooperative\n * \tprocesses.\n *\n * \tKeep in mind that this feature is meant for experiments, and it\n * \thas a risk of crashing the system and running programs.\n * \tTherefore, when an eBPF program using this helper is attached,\n * \ta warning including PID and process name is printed to kernel\n * \tlogs.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_probe_write_user)(void *dst, const void *src, __u32 len) = (void *) 36;\n\n/*\n * bpf_current_task_under_cgroup\n *\n * \tCheck whether the probe is being run is the context of a given\n * \tsubset of the cgroup2 hierarchy. The cgroup2 to test is held by\n * \t*map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*.\n *\n * Returns\n * \tThe return value depends on the result of the test, and can be:\n *\n * \t* 0, if current task belongs to the cgroup2.\n * \t* 1, if current task does not belong to the cgroup2.\n * \t* A negative error code, if an error occurred.\n */\nstatic long (*bpf_current_task_under_cgroup)(void *map, __u32 index) = (void *) 37;\n\n/*\n * bpf_skb_change_tail\n *\n * \tResize (trim or grow) the packet associated to *skb* to the\n * \tnew *len*. The *flags* are reserved for future usage, and must\n * \tbe left at zero.\n *\n * \tThe basic idea is that the helper performs the needed work to\n * \tchange the size of the packet, then the eBPF program rewrites\n * \tthe rest via helpers like **bpf_skb_store_bytes**\\ (),\n * \t**bpf_l3_csum_replace**\\ (), **bpf_l3_csum_replace**\\ ()\n * \tand others. This helper is a slow path utility intended for\n * \treplies with control messages. And because it is targeted for\n * \tslow path, the helper itself can afford to be slow: it\n * \timplicitly linearizes, unclones and drops offloads from the\n * \t*skb*.\n *\n * \tA call to this helper is susceptible to change the underlying\n * \tpacket buffer. Therefore, at load time, all checks on pointers\n * \tpreviously done by the verifier are invalidated and must be\n * \tperformed again, if the helper is used in combination with\n * \tdirect packet access.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_skb_change_tail)(struct __sk_buff *skb, __u32 len, __u64 flags) = (void *) 38;\n\n/*\n * bpf_skb_pull_data\n *\n * \tPull in non-linear data in case the *skb* is non-linear and not\n * \tall of *len* are part of the linear section. Make *len* bytes\n * \tfrom *skb* readable and writable. If a zero value is passed for\n * \t*len*, then the whole length of the *skb* is pulled.\n *\n * \tThis helper is only needed for reading and writing with direct\n * \tpacket access.\n *\n * \tFor direct packet access, testing that offsets to access\n * \tare within packet boundaries (test on *skb*\\ **->data_end**) is\n * \tsusceptible to fail if offsets are invalid, or if the requested\n * \tdata is in non-linear parts of the *skb*. On failure the\n * \tprogram can just bail out, or in the case of a non-linear\n * \tbuffer, use a helper to make the data available. The\n * \t**bpf_skb_load_bytes**\\ () helper is a first solution to access\n * \tthe data. Another one consists in using **bpf_skb_pull_data**\n * \tto pull in once the non-linear parts, then retesting and\n * \teventually access the data.\n *\n * \tAt the same time, this also makes sure the *skb* is uncloned,\n * \twhich is a necessary condition for direct write. As this needs\n * \tto be an invariant for the write part only, the verifier\n * \tdetects writes and adds a prologue that is calling\n * \t**bpf_skb_pull_data()** to effectively unclone the *skb* from\n * \tthe very beginning in case it is indeed cloned.\n *\n * \tA call to this helper is susceptible to change the underlying\n * \tpacket buffer. Therefore, at load time, all checks on pointers\n * \tpreviously done by the verifier are invalidated and must be\n * \tperformed again, if the helper is used in combination with\n * \tdirect packet access.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_skb_pull_data)(struct __sk_buff *skb, __u32 len) = (void *) 39;\n\n/*\n * bpf_csum_update\n *\n * \tAdd the checksum *csum* into *skb*\\ **->csum** in case the\n * \tdriver has supplied a checksum for the entire packet into that\n * \tfield. Return an error otherwise. This helper is intended to be\n * \tused in combination with **bpf_csum_diff**\\ (), in particular\n * \twhen the checksum needs to be updated after data has been\n * \twritten into the packet through direct packet access.\n *\n * Returns\n * \tThe checksum on success, or a negative error code in case of\n * \tfailure.\n */\nstatic __s64 (*bpf_csum_update)(struct __sk_buff *skb, __wsum csum) = (void *) 40;\n\n/*\n * bpf_set_hash_invalid\n *\n * \tInvalidate the current *skb*\\ **->hash**. It can be used after\n * \tmangling on headers through direct packet access, in order to\n * \tindicate that the hash is outdated and to trigger a\n * \trecalculation the next time the kernel tries to access this\n * \thash or when the **bpf_get_hash_recalc**\\ () helper is called.\n *\n */\nstatic void (*bpf_set_hash_invalid)(struct __sk_buff *skb) = (void *) 41;\n\n/*\n * bpf_get_numa_node_id\n *\n * \tReturn the id of the current NUMA node. The primary use case\n * \tfor this helper is the selection of sockets for the local NUMA\n * \tnode, when the program is attached to sockets using the\n * \t**SO_ATTACH_REUSEPORT_EBPF** option (see also **socket(7)**),\n * \tbut the helper is also available to other eBPF program types,\n * \tsimilarly to **bpf_get_smp_processor_id**\\ ().\n *\n * Returns\n * \tThe id of current NUMA node.\n */\nstatic long (*bpf_get_numa_node_id)(void) = (void *) 42;\n\n/*\n * bpf_skb_change_head\n *\n * \tGrows headroom of packet associated to *skb* and adjusts the\n * \toffset of the MAC header accordingly, adding *len* bytes of\n * \tspace. It automatically extends and reallocates memory as\n * \trequired.\n *\n * \tThis helper can be used on a layer 3 *skb* to push a MAC header\n * \tfor redirection into a layer 2 device.\n *\n * \tAll values for *flags* are reserved for future usage, and must\n * \tbe left at zero.\n *\n * \tA call to this helper is susceptible to change the underlying\n * \tpacket buffer. Therefore, at load time, all checks on pointers\n * \tpreviously done by the verifier are invalidated and must be\n * \tperformed again, if the helper is used in combination with\n * \tdirect packet access.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_skb_change_head)(struct __sk_buff *skb, __u32 len, __u64 flags) = (void *) 43;\n\n/*\n * bpf_xdp_adjust_head\n *\n * \tAdjust (move) *xdp_md*\\ **->data** by *delta* bytes. Note that\n * \tit is possible to use a negative value for *delta*. This helper\n * \tcan be used to prepare the packet for pushing or popping\n * \theaders.\n *\n * \tA call to this helper is susceptible to change the underlying\n * \tpacket buffer. Therefore, at load time, all checks on pointers\n * \tpreviously done by the verifier are invalidated and must be\n * \tperformed again, if the helper is used in combination with\n * \tdirect packet access.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_xdp_adjust_head)(struct xdp_md *xdp_md, int delta) = (void *) 44;\n\n/*\n * bpf_probe_read_str\n *\n * \tCopy a NUL terminated string from an unsafe kernel address\n * \t*unsafe_ptr* to *dst*. See **bpf_probe_read_kernel_str**\\ () for\n * \tmore details.\n *\n * \tGenerally, use **bpf_probe_read_user_str**\\ () or\n * \t**bpf_probe_read_kernel_str**\\ () instead.\n *\n * Returns\n * \tOn success, the strictly positive length of the string,\n * \tincluding the trailing NUL character. On error, a negative\n * \tvalue.\n */\nstatic long (*bpf_probe_read_str)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 45;\n\n/*\n * bpf_get_socket_cookie\n *\n * \tIf the **struct sk_buff** pointed by *skb* has a known socket,\n * \tretrieve the cookie (generated by the kernel) of this socket.\n * \tIf no cookie has been set yet, generate a new cookie. Once\n * \tgenerated, the socket cookie remains stable for the life of the\n * \tsocket. This helper can be useful for monitoring per socket\n * \tnetworking traffic statistics as it provides a global socket\n * \tidentifier that can be assumed unique.\n *\n * Returns\n * \tA 8-byte long unique number on success, or 0 if the socket\n * \tfield is missing inside *skb*.\n */\nstatic __u64 (*bpf_get_socket_cookie)(void *ctx) = (void *) 46;\n\n/*\n * bpf_get_socket_uid\n *\n *\n * Returns\n * \tThe owner UID of the socket associated to *skb*. If the socket\n * \tis **NULL**, or if it is not a full socket (i.e. if it is a\n * \ttime-wait or a request socket instead), **overflowuid** value\n * \tis returned (note that **overflowuid** might also be the actual\n * \tUID value for the socket).\n */\nstatic __u32 (*bpf_get_socket_uid)(struct __sk_buff *skb) = (void *) 47;\n\n/*\n * bpf_set_hash\n *\n * \tSet the full hash for *skb* (set the field *skb*\\ **->hash**)\n * \tto value *hash*.\n *\n * Returns\n * \t0\n */\nstatic long (*bpf_set_hash)(struct __sk_buff *skb, __u32 hash) = (void *) 48;\n\n/*\n * bpf_setsockopt\n *\n * \tEmulate a call to **setsockopt()** on the socket associated to\n * \t*bpf_socket*, which must be a full socket. The *level* at\n * \twhich the option resides and the name *optname* of the option\n * \tmust be specified, see **setsockopt(2)** for more information.\n * \tThe option value of length *optlen* is pointed by *optval*.\n *\n * \t*bpf_socket* should be one of the following:\n *\n * \t* **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**.\n * \t* **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT**\n * \t  and **BPF_CGROUP_INET6_CONNECT**.\n *\n * \tThis helper actually implements a subset of **setsockopt()**.\n * \tIt supports the following *level*\\ s:\n *\n * \t* **SOL_SOCKET**, which supports the following *optname*\\ s:\n * \t  **SO_RCVBUF**, **SO_SNDBUF**, **SO_MAX_PACING_RATE**,\n * \t  **SO_PRIORITY**, **SO_RCVLOWAT**, **SO_MARK**,\n * \t  **SO_BINDTODEVICE**, **SO_KEEPALIVE**.\n * \t* **IPPROTO_TCP**, which supports the following *optname*\\ s:\n * \t  **TCP_CONGESTION**, **TCP_BPF_IW**,\n * \t  **TCP_BPF_SNDCWND_CLAMP**, **TCP_SAVE_SYN**,\n * \t  **TCP_KEEPIDLE**, **TCP_KEEPINTVL**, **TCP_KEEPCNT**,\n * \t  **TCP_SYNCNT**, **TCP_USER_TIMEOUT**, **TCP_NOTSENT_LOWAT**.\n * \t* **IPPROTO_IP**, which supports *optname* **IP_TOS**.\n * \t* **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_setsockopt)(void *bpf_socket, int level, int optname, void *optval, int optlen) = (void *) 49;\n\n/*\n * bpf_skb_adjust_room\n *\n * \tGrow or shrink the room for data in the packet associated to\n * \t*skb* by *len_diff*, and according to the selected *mode*.\n *\n * \tBy default, the helper will reset any offloaded checksum\n * \tindicator of the skb to CHECKSUM_NONE. This can be avoided\n * \tby the following flag:\n *\n * \t* **BPF_F_ADJ_ROOM_NO_CSUM_RESET**: Do not reset offloaded\n * \t  checksum data of the skb to CHECKSUM_NONE.\n *\n * \tThere are two supported modes at this time:\n *\n * \t* **BPF_ADJ_ROOM_MAC**: Adjust room at the mac layer\n * \t  (room space is added or removed below the layer 2 header).\n *\n * \t* **BPF_ADJ_ROOM_NET**: Adjust room at the network layer\n * \t  (room space is added or removed below the layer 3 header).\n *\n * \tThe following flags are supported at this time:\n *\n * \t* **BPF_F_ADJ_ROOM_FIXED_GSO**: Do not adjust gso_size.\n * \t  Adjusting mss in this way is not allowed for datagrams.\n *\n * \t* **BPF_F_ADJ_ROOM_ENCAP_L3_IPV4**,\n * \t  **BPF_F_ADJ_ROOM_ENCAP_L3_IPV6**:\n * \t  Any new space is reserved to hold a tunnel header.\n * \t  Configure skb offsets and other fields accordingly.\n *\n * \t* **BPF_F_ADJ_ROOM_ENCAP_L4_GRE**,\n * \t  **BPF_F_ADJ_ROOM_ENCAP_L4_UDP**:\n * \t  Use with ENCAP_L3 flags to further specify the tunnel type.\n *\n * \t* **BPF_F_ADJ_ROOM_ENCAP_L2**\\ (*len*):\n * \t  Use with ENCAP_L3/L4 flags to further specify the tunnel\n * \t  type; *len* is the length of the inner MAC header.\n *\n * \t* **BPF_F_ADJ_ROOM_ENCAP_L2_ETH**:\n * \t  Use with BPF_F_ADJ_ROOM_ENCAP_L2 flag to further specify the\n * \t  L2 type as Ethernet.\n *\n * \tA call to this helper is susceptible to change the underlying\n * \tpacket buffer. Therefore, at load time, all checks on pointers\n * \tpreviously done by the verifier are invalidated and must be\n * \tperformed again, if the helper is used in combination with\n * \tdirect packet access.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_skb_adjust_room)(struct __sk_buff *skb, __s32 len_diff, __u32 mode, __u64 flags) = (void *) 50;\n\n/*\n * bpf_redirect_map\n *\n * \tRedirect the packet to the endpoint referenced by *map* at\n * \tindex *key*. Depending on its type, this *map* can contain\n * \treferences to net devices (for forwarding packets through other\n * \tports), or to CPUs (for redirecting XDP frames to another CPU;\n * \tbut this is only implemented for native XDP (with driver\n * \tsupport) as of this writing).\n *\n * \tThe lower two bits of *flags* are used as the return code if\n * \tthe map lookup fails. This is so that the return value can be\n * \tone of the XDP program return codes up to **XDP_TX**, as chosen\n * \tby the caller. The higher bits of *flags* can be set to\n * \tBPF_F_BROADCAST or BPF_F_EXCLUDE_INGRESS as defined below.\n *\n * \tWith BPF_F_BROADCAST the packet will be broadcasted to all the\n * \tinterfaces in the map, with BPF_F_EXCLUDE_INGRESS the ingress\n * \tinterface will be excluded when do broadcasting.\n *\n * \tSee also **bpf_redirect**\\ (), which only supports redirecting\n * \tto an ifindex, but doesn't require a map to do so.\n *\n * Returns\n * \t**XDP_REDIRECT** on success, or the value of the two lower bits\n * \tof the *flags* argument on error.\n */\nstatic long (*bpf_redirect_map)(void *map, __u32 key, __u64 flags) = (void *) 51;\n\n/*\n * bpf_sk_redirect_map\n *\n * \tRedirect the packet to the socket referenced by *map* (of type\n * \t**BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and\n * \tegress interfaces can be used for redirection. The\n * \t**BPF_F_INGRESS** value in *flags* is used to make the\n * \tdistinction (ingress path is selected if the flag is present,\n * \tegress path otherwise). This is the only flag supported for now.\n *\n * Returns\n * \t**SK_PASS** on success, or **SK_DROP** on error.\n */\nstatic long (*bpf_sk_redirect_map)(struct __sk_buff *skb, void *map, __u32 key, __u64 flags) = (void *) 52;\n\n/*\n * bpf_sock_map_update\n *\n * \tAdd an entry to, or update a *map* referencing sockets. The\n * \t*skops* is used as a new value for the entry associated to\n * \t*key*. *flags* is one of:\n *\n * \t**BPF_NOEXIST**\n * \t\tThe entry for *key* must not exist in the map.\n * \t**BPF_EXIST**\n * \t\tThe entry for *key* must already exist in the map.\n * \t**BPF_ANY**\n * \t\tNo condition on the existence of the entry for *key*.\n *\n * \tIf the *map* has eBPF programs (parser and verdict), those will\n * \tbe inherited by the socket being added. If the socket is\n * \talready attached to eBPF programs, this results in an error.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_sock_map_update)(struct bpf_sock_ops *skops, void *map, void *key, __u64 flags) = (void *) 53;\n\n/*\n * bpf_xdp_adjust_meta\n *\n * \tAdjust the address pointed by *xdp_md*\\ **->data_meta** by\n * \t*delta* (which can be positive or negative). Note that this\n * \toperation modifies the address stored in *xdp_md*\\ **->data**,\n * \tso the latter must be loaded only after the helper has been\n * \tcalled.\n *\n * \tThe use of *xdp_md*\\ **->data_meta** is optional and programs\n * \tare not required to use it. The rationale is that when the\n * \tpacket is processed with XDP (e.g. as DoS filter), it is\n * \tpossible to push further meta data along with it before passing\n * \tto the stack, and to give the guarantee that an ingress eBPF\n * \tprogram attached as a TC classifier on the same device can pick\n * \tthis up for further post-processing. Since TC works with socket\n * \tbuffers, it remains possible to set from XDP the **mark** or\n * \t**priority** pointers, or other pointers for the socket buffer.\n * \tHaving this scratch space generic and programmable allows for\n * \tmore flexibility as the user is free to store whatever meta\n * \tdata they need.\n *\n * \tA call to this helper is susceptible to change the underlying\n * \tpacket buffer. Therefore, at load time, all checks on pointers\n * \tpreviously done by the verifier are invalidated and must be\n * \tperformed again, if the helper is used in combination with\n * \tdirect packet access.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_xdp_adjust_meta)(struct xdp_md *xdp_md, int delta) = (void *) 54;\n\n/*\n * bpf_perf_event_read_value\n *\n * \tRead the value of a perf event counter, and store it into *buf*\n * \tof size *buf_size*. This helper relies on a *map* of type\n * \t**BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of the perf event\n * \tcounter is selected when *map* is updated with perf event file\n * \tdescriptors. The *map* is an array whose size is the number of\n * \tavailable CPUs, and each cell contains a value relative to one\n * \tCPU. The value to retrieve is indicated by *flags*, that\n * \tcontains the index of the CPU to look up, masked with\n * \t**BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to\n * \t**BPF_F_CURRENT_CPU** to indicate that the value for the\n * \tcurrent CPU should be retrieved.\n *\n * \tThis helper behaves in a way close to\n * \t**bpf_perf_event_read**\\ () helper, save that instead of\n * \tjust returning the value observed, it fills the *buf*\n * \tstructure. This allows for additional data to be retrieved: in\n * \tparticular, the enabled and running times (in *buf*\\\n * \t**->enabled** and *buf*\\ **->running**, respectively) are\n * \tcopied. In general, **bpf_perf_event_read_value**\\ () is\n * \trecommended over **bpf_perf_event_read**\\ (), which has some\n * \tABI issues and provides fewer functionalities.\n *\n * \tThese values are interesting, because hardware PMU (Performance\n * \tMonitoring Unit) counters are limited resources. When there are\n * \tmore PMU based perf events opened than available counters,\n * \tkernel will multiplex these events so each event gets certain\n * \tpercentage (but not all) of the PMU time. In case that\n * \tmultiplexing happens, the number of samples or counter value\n * \twill not reflect the case compared to when no multiplexing\n * \toccurs. This makes comparison between different runs difficult.\n * \tTypically, the counter value should be normalized before\n * \tcomparing to other experiments. The usual normalization is done\n * \tas follows.\n *\n * \t::\n *\n * \t\tnormalized_counter = counter * t_enabled / t_running\n *\n * \tWhere t_enabled is the time enabled for event and t_running is\n * \tthe time running for event since last normalization. The\n * \tenabled and running times are accumulated since the perf event\n * \topen. To achieve scaling factor between two invocations of an\n * \teBPF program, users can use CPU id as the key (which is\n * \ttypical for perf array usage model) to remember the previous\n * \tvalue and do the calculation inside the eBPF program.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_perf_event_read_value)(void *map, __u64 flags, struct bpf_perf_event_value *buf, __u32 buf_size) = (void *) 55;\n\n/*\n * bpf_perf_prog_read_value\n *\n * \tFor en eBPF program attached to a perf event, retrieve the\n * \tvalue of the event counter associated to *ctx* and store it in\n * \tthe structure pointed by *buf* and of size *buf_size*. Enabled\n * \tand running times are also stored in the structure (see\n * \tdescription of helper **bpf_perf_event_read_value**\\ () for\n * \tmore details).\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_perf_prog_read_value)(struct bpf_perf_event_data *ctx, struct bpf_perf_event_value *buf, __u32 buf_size) = (void *) 56;\n\n/*\n * bpf_getsockopt\n *\n * \tEmulate a call to **getsockopt()** on the socket associated to\n * \t*bpf_socket*, which must be a full socket. The *level* at\n * \twhich the option resides and the name *optname* of the option\n * \tmust be specified, see **getsockopt(2)** for more information.\n * \tThe retrieved value is stored in the structure pointed by\n * \t*opval* and of length *optlen*.\n *\n * \t*bpf_socket* should be one of the following:\n *\n * \t* **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**.\n * \t* **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT**\n * \t  and **BPF_CGROUP_INET6_CONNECT**.\n *\n * \tThis helper actually implements a subset of **getsockopt()**.\n * \tIt supports the following *level*\\ s:\n *\n * \t* **IPPROTO_TCP**, which supports *optname*\n * \t  **TCP_CONGESTION**.\n * \t* **IPPROTO_IP**, which supports *optname* **IP_TOS**.\n * \t* **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_getsockopt)(void *bpf_socket, int level, int optname, void *optval, int optlen) = (void *) 57;\n\n/*\n * bpf_override_return\n *\n * \tUsed for error injection, this helper uses kprobes to override\n * \tthe return value of the probed function, and to set it to *rc*.\n * \tThe first argument is the context *regs* on which the kprobe\n * \tworks.\n *\n * \tThis helper works by setting the PC (program counter)\n * \tto an override function which is run in place of the original\n * \tprobed function. This means the probed function is not run at\n * \tall. The replacement function just returns with the required\n * \tvalue.\n *\n * \tThis helper has security implications, and thus is subject to\n * \trestrictions. It is only available if the kernel was compiled\n * \twith the **CONFIG_BPF_KPROBE_OVERRIDE** configuration\n * \toption, and in this case it only works on functions tagged with\n * \t**ALLOW_ERROR_INJECTION** in the kernel code.\n *\n * \tAlso, the helper is only available for the architectures having\n * \tthe CONFIG_FUNCTION_ERROR_INJECTION option. As of this writing,\n * \tx86 architecture is the only one to support this feature.\n *\n * Returns\n * \t0\n */\nstatic long (*bpf_override_return)(struct pt_regs *regs, __u64 rc) = (void *) 58;\n\n/*\n * bpf_sock_ops_cb_flags_set\n *\n * \tAttempt to set the value of the **bpf_sock_ops_cb_flags** field\n * \tfor the full TCP socket associated to *bpf_sock_ops* to\n * \t*argval*.\n *\n * \tThe primary use of this field is to determine if there should\n * \tbe calls to eBPF programs of type\n * \t**BPF_PROG_TYPE_SOCK_OPS** at various points in the TCP\n * \tcode. A program of the same type can change its value, per\n * \tconnection and as necessary, when the connection is\n * \testablished. This field is directly accessible for reading, but\n * \tthis helper must be used for updates in order to return an\n * \terror if an eBPF program tries to set a callback that is not\n * \tsupported in the current kernel.\n *\n * \t*argval* is a flag array which can combine these flags:\n *\n * \t* **BPF_SOCK_OPS_RTO_CB_FLAG** (retransmission time out)\n * \t* **BPF_SOCK_OPS_RETRANS_CB_FLAG** (retransmission)\n * \t* **BPF_SOCK_OPS_STATE_CB_FLAG** (TCP state change)\n * \t* **BPF_SOCK_OPS_RTT_CB_FLAG** (every RTT)\n *\n * \tTherefore, this function can be used to clear a callback flag by\n * \tsetting the appropriate bit to zero. e.g. to disable the RTO\n * \tcallback:\n *\n * \t**bpf_sock_ops_cb_flags_set(bpf_sock,**\n * \t\t**bpf_sock->bpf_sock_ops_cb_flags & ~BPF_SOCK_OPS_RTO_CB_FLAG)**\n *\n * \tHere are some examples of where one could call such eBPF\n * \tprogram:\n *\n * \t* When RTO fires.\n * \t* When a packet is retransmitted.\n * \t* When the connection terminates.\n * \t* When a packet is sent.\n * \t* When a packet is received.\n *\n * Returns\n * \tCode **-EINVAL** if the socket is not a full TCP socket;\n * \totherwise, a positive number containing the bits that could not\n * \tbe set is returned (which comes down to 0 if all bits were set\n * \tas required).\n */\nstatic long (*bpf_sock_ops_cb_flags_set)(struct bpf_sock_ops *bpf_sock, int argval) = (void *) 59;\n\n/*\n * bpf_msg_redirect_map\n *\n * \tThis helper is used in programs implementing policies at the\n * \tsocket level. If the message *msg* is allowed to pass (i.e. if\n * \tthe verdict eBPF program returns **SK_PASS**), redirect it to\n * \tthe socket referenced by *map* (of type\n * \t**BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and\n * \tegress interfaces can be used for redirection. The\n * \t**BPF_F_INGRESS** value in *flags* is used to make the\n * \tdistinction (ingress path is selected if the flag is present,\n * \tegress path otherwise). This is the only flag supported for now.\n *\n * Returns\n * \t**SK_PASS** on success, or **SK_DROP** on error.\n */\nstatic long (*bpf_msg_redirect_map)(struct sk_msg_md *msg, void *map, __u32 key, __u64 flags) = (void *) 60;\n\n/*\n * bpf_msg_apply_bytes\n *\n * \tFor socket policies, apply the verdict of the eBPF program to\n * \tthe next *bytes* (number of bytes) of message *msg*.\n *\n * \tFor example, this helper can be used in the following cases:\n *\n * \t* A single **sendmsg**\\ () or **sendfile**\\ () system call\n * \t  contains multiple logical messages that the eBPF program is\n * \t  supposed to read and for which it should apply a verdict.\n * \t* An eBPF program only cares to read the first *bytes* of a\n * \t  *msg*. If the message has a large payload, then setting up\n * \t  and calling the eBPF program repeatedly for all bytes, even\n * \t  though the verdict is already known, would create unnecessary\n * \t  overhead.\n *\n * \tWhen called from within an eBPF program, the helper sets a\n * \tcounter internal to the BPF infrastructure, that is used to\n * \tapply the last verdict to the next *bytes*. If *bytes* is\n * \tsmaller than the current data being processed from a\n * \t**sendmsg**\\ () or **sendfile**\\ () system call, the first\n * \t*bytes* will be sent and the eBPF program will be re-run with\n * \tthe pointer for start of data pointing to byte number *bytes*\n * \t**+ 1**. If *bytes* is larger than the current data being\n * \tprocessed, then the eBPF verdict will be applied to multiple\n * \t**sendmsg**\\ () or **sendfile**\\ () calls until *bytes* are\n * \tconsumed.\n *\n * \tNote that if a socket closes with the internal counter holding\n * \ta non-zero value, this is not a problem because data is not\n * \tbeing buffered for *bytes* and is sent as it is received.\n *\n * Returns\n * \t0\n */\nstatic long (*bpf_msg_apply_bytes)(struct sk_msg_md *msg, __u32 bytes) = (void *) 61;\n\n/*\n * bpf_msg_cork_bytes\n *\n * \tFor socket policies, prevent the execution of the verdict eBPF\n * \tprogram for message *msg* until *bytes* (byte number) have been\n * \taccumulated.\n *\n * \tThis can be used when one needs a specific number of bytes\n * \tbefore a verdict can be assigned, even if the data spans\n * \tmultiple **sendmsg**\\ () or **sendfile**\\ () calls. The extreme\n * \tcase would be a user calling **sendmsg**\\ () repeatedly with\n * \t1-byte long message segments. Obviously, this is bad for\n * \tperformance, but it is still valid. If the eBPF program needs\n * \t*bytes* bytes to validate a header, this helper can be used to\n * \tprevent the eBPF program to be called again until *bytes* have\n * \tbeen accumulated.\n *\n * Returns\n * \t0\n */\nstatic long (*bpf_msg_cork_bytes)(struct sk_msg_md *msg, __u32 bytes) = (void *) 62;\n\n/*\n * bpf_msg_pull_data\n *\n * \tFor socket policies, pull in non-linear data from user space\n * \tfor *msg* and set pointers *msg*\\ **->data** and *msg*\\\n * \t**->data_end** to *start* and *end* bytes offsets into *msg*,\n * \trespectively.\n *\n * \tIf a program of type **BPF_PROG_TYPE_SK_MSG** is run on a\n * \t*msg* it can only parse data that the (**data**, **data_end**)\n * \tpointers have already consumed. For **sendmsg**\\ () hooks this\n * \tis likely the first scatterlist element. But for calls relying\n * \ton the **sendpage** handler (e.g. **sendfile**\\ ()) this will\n * \tbe the range (**0**, **0**) because the data is shared with\n * \tuser space and by default the objective is to avoid allowing\n * \tuser space to modify data while (or after) eBPF verdict is\n * \tbeing decided. This helper can be used to pull in data and to\n * \tset the start and end pointer to given values. Data will be\n * \tcopied if necessary (i.e. if data was not linear and if start\n * \tand end pointers do not point to the same chunk).\n *\n * \tA call to this helper is susceptible to change the underlying\n * \tpacket buffer. Therefore, at load time, all checks on pointers\n * \tpreviously done by the verifier are invalidated and must be\n * \tperformed again, if the helper is used in combination with\n * \tdirect packet access.\n *\n * \tAll values for *flags* are reserved for future usage, and must\n * \tbe left at zero.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_msg_pull_data)(struct sk_msg_md *msg, __u32 start, __u32 end, __u64 flags) = (void *) 63;\n\n/*\n * bpf_bind\n *\n * \tBind the socket associated to *ctx* to the address pointed by\n * \t*addr*, of length *addr_len*. This allows for making outgoing\n * \tconnection from the desired IP address, which can be useful for\n * \texample when all processes inside a cgroup should use one\n * \tsingle IP address on a host that has multiple IP configured.\n *\n * \tThis helper works for IPv4 and IPv6, TCP and UDP sockets. The\n * \tdomain (*addr*\\ **->sa_family**) must be **AF_INET** (or\n * \t**AF_INET6**). It's advised to pass zero port (**sin_port**\n * \tor **sin6_port**) which triggers IP_BIND_ADDRESS_NO_PORT-like\n * \tbehavior and lets the kernel efficiently pick up an unused\n * \tport as long as 4-tuple is unique. Passing non-zero port might\n * \tlead to degraded performance.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_bind)(struct bpf_sock_addr *ctx, struct sockaddr *addr, int addr_len) = (void *) 64;\n\n/*\n * bpf_xdp_adjust_tail\n *\n * \tAdjust (move) *xdp_md*\\ **->data_end** by *delta* bytes. It is\n * \tpossible to both shrink and grow the packet tail.\n * \tShrink done via *delta* being a negative integer.\n *\n * \tA call to this helper is susceptible to change the underlying\n * \tpacket buffer. Therefore, at load time, all checks on pointers\n * \tpreviously done by the verifier are invalidated and must be\n * \tperformed again, if the helper is used in combination with\n * \tdirect packet access.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_xdp_adjust_tail)(struct xdp_md *xdp_md, int delta) = (void *) 65;\n\n/*\n * bpf_skb_get_xfrm_state\n *\n * \tRetrieve the XFRM state (IP transform framework, see also\n * \t**ip-xfrm(8)**) at *index* in XFRM \"security path\" for *skb*.\n *\n * \tThe retrieved value is stored in the **struct bpf_xfrm_state**\n * \tpointed by *xfrm_state* and of length *size*.\n *\n * \tAll values for *flags* are reserved for future usage, and must\n * \tbe left at zero.\n *\n * \tThis helper is available only if the kernel was compiled with\n * \t**CONFIG_XFRM** configuration option.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_skb_get_xfrm_state)(struct __sk_buff *skb, __u32 index, struct bpf_xfrm_state *xfrm_state, __u32 size, __u64 flags) = (void *) 66;\n\n/*\n * bpf_get_stack\n *\n * \tReturn a user or a kernel stack in bpf program provided buffer.\n * \tTo achieve this, the helper needs *ctx*, which is a pointer\n * \tto the context on which the tracing program is executed.\n * \tTo store the stacktrace, the bpf program provides *buf* with\n * \ta nonnegative *size*.\n *\n * \tThe last argument, *flags*, holds the number of stack frames to\n * \tskip (from 0 to 255), masked with\n * \t**BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set\n * \tthe following flags:\n *\n * \t**BPF_F_USER_STACK**\n * \t\tCollect a user space stack instead of a kernel stack.\n * \t**BPF_F_USER_BUILD_ID**\n * \t\tCollect buildid+offset instead of ips for user stack,\n * \t\tonly valid if **BPF_F_USER_STACK** is also specified.\n *\n * \t**bpf_get_stack**\\ () can collect up to\n * \t**PERF_MAX_STACK_DEPTH** both kernel and user frames, subject\n * \tto sufficient large buffer size. Note that\n * \tthis limit can be controlled with the **sysctl** program, and\n * \tthat it should be manually increased in order to profile long\n * \tuser stacks (such as stacks for Java programs). To do so, use:\n *\n * \t::\n *\n * \t\t# sysctl kernel.perf_event_max_stack=<new value>\n *\n * Returns\n * \tA non-negative value equal to or less than *size* on success,\n * \tor a negative error in case of failure.\n */\nstatic long (*bpf_get_stack)(void *ctx, void *buf, __u32 size, __u64 flags) = (void *) 67;\n\n/*\n * bpf_skb_load_bytes_relative\n *\n * \tThis helper is similar to **bpf_skb_load_bytes**\\ () in that\n * \tit provides an easy way to load *len* bytes from *offset*\n * \tfrom the packet associated to *skb*, into the buffer pointed\n * \tby *to*. The difference to **bpf_skb_load_bytes**\\ () is that\n * \ta fifth argument *start_header* exists in order to select a\n * \tbase offset to start from. *start_header* can be one of:\n *\n * \t**BPF_HDR_START_MAC**\n * \t\tBase offset to load data from is *skb*'s mac header.\n * \t**BPF_HDR_START_NET**\n * \t\tBase offset to load data from is *skb*'s network header.\n *\n * \tIn general, \"direct packet access\" is the preferred method to\n * \taccess packet data, however, this helper is in particular useful\n * \tin socket filters where *skb*\\ **->data** does not always point\n * \tto the start of the mac header and where \"direct packet access\"\n * \tis not available.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_skb_load_bytes_relative)(const void *skb, __u32 offset, void *to, __u32 len, __u32 start_header) = (void *) 68;\n\n/*\n * bpf_fib_lookup\n *\n * \tDo FIB lookup in kernel tables using parameters in *params*.\n * \tIf lookup is successful and result shows packet is to be\n * \tforwarded, the neighbor tables are searched for the nexthop.\n * \tIf successful (ie., FIB lookup shows forwarding and nexthop\n * \tis resolved), the nexthop address is returned in ipv4_dst\n * \tor ipv6_dst based on family, smac is set to mac address of\n * \tegress device, dmac is set to nexthop mac address, rt_metric\n * \tis set to metric from route (IPv4/IPv6 only), and ifindex\n * \tis set to the device index of the nexthop from the FIB lookup.\n *\n * \t*plen* argument is the size of the passed in struct.\n * \t*flags* argument can be a combination of one or more of the\n * \tfollowing values:\n *\n * \t**BPF_FIB_LOOKUP_DIRECT**\n * \t\tDo a direct table lookup vs full lookup using FIB\n * \t\trules.\n * \t**BPF_FIB_LOOKUP_OUTPUT**\n * \t\tPerform lookup from an egress perspective (default is\n * \t\tingress).\n *\n * \t*ctx* is either **struct xdp_md** for XDP programs or\n * \t**struct sk_buff** tc cls_act programs.\n *\n * Returns\n * \t* < 0 if any input argument is invalid\n * \t*   0 on success (packet is forwarded, nexthop neighbor exists)\n * \t* > 0 one of **BPF_FIB_LKUP_RET_** codes explaining why the\n * \t  packet is not forwarded or needs assist from full stack\n *\n * \tIf lookup fails with BPF_FIB_LKUP_RET_FRAG_NEEDED, then the MTU\n * \twas exceeded and output params->mtu_result contains the MTU.\n */\nstatic long (*bpf_fib_lookup)(void *ctx, struct bpf_fib_lookup *params, int plen, __u32 flags) = (void *) 69;\n\n/*\n * bpf_sock_hash_update\n *\n * \tAdd an entry to, or update a sockhash *map* referencing sockets.\n * \tThe *skops* is used as a new value for the entry associated to\n * \t*key*. *flags* is one of:\n *\n * \t**BPF_NOEXIST**\n * \t\tThe entry for *key* must not exist in the map.\n * \t**BPF_EXIST**\n * \t\tThe entry for *key* must already exist in the map.\n * \t**BPF_ANY**\n * \t\tNo condition on the existence of the entry for *key*.\n *\n * \tIf the *map* has eBPF programs (parser and verdict), those will\n * \tbe inherited by the socket being added. If the socket is\n * \talready attached to eBPF programs, this results in an error.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_sock_hash_update)(struct bpf_sock_ops *skops, void *map, void *key, __u64 flags) = (void *) 70;\n\n/*\n * bpf_msg_redirect_hash\n *\n * \tThis helper is used in programs implementing policies at the\n * \tsocket level. If the message *msg* is allowed to pass (i.e. if\n * \tthe verdict eBPF program returns **SK_PASS**), redirect it to\n * \tthe socket referenced by *map* (of type\n * \t**BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and\n * \tegress interfaces can be used for redirection. The\n * \t**BPF_F_INGRESS** value in *flags* is used to make the\n * \tdistinction (ingress path is selected if the flag is present,\n * \tegress path otherwise). This is the only flag supported for now.\n *\n * Returns\n * \t**SK_PASS** on success, or **SK_DROP** on error.\n */\nstatic long (*bpf_msg_redirect_hash)(struct sk_msg_md *msg, void *map, void *key, __u64 flags) = (void *) 71;\n\n/*\n * bpf_sk_redirect_hash\n *\n * \tThis helper is used in programs implementing policies at the\n * \tskb socket level. If the sk_buff *skb* is allowed to pass (i.e.\n * \tif the verdict eBPF program returns **SK_PASS**), redirect it\n * \tto the socket referenced by *map* (of type\n * \t**BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and\n * \tegress interfaces can be used for redirection. The\n * \t**BPF_F_INGRESS** value in *flags* is used to make the\n * \tdistinction (ingress path is selected if the flag is present,\n * \tegress otherwise). This is the only flag supported for now.\n *\n * Returns\n * \t**SK_PASS** on success, or **SK_DROP** on error.\n */\nstatic long (*bpf_sk_redirect_hash)(struct __sk_buff *skb, void *map, void *key, __u64 flags) = (void *) 72;\n\n/*\n * bpf_lwt_push_encap\n *\n * \tEncapsulate the packet associated to *skb* within a Layer 3\n * \tprotocol header. This header is provided in the buffer at\n * \taddress *hdr*, with *len* its size in bytes. *type* indicates\n * \tthe protocol of the header and can be one of:\n *\n * \t**BPF_LWT_ENCAP_SEG6**\n * \t\tIPv6 encapsulation with Segment Routing Header\n * \t\t(**struct ipv6_sr_hdr**). *hdr* only contains the SRH,\n * \t\tthe IPv6 header is computed by the kernel.\n * \t**BPF_LWT_ENCAP_SEG6_INLINE**\n * \t\tOnly works if *skb* contains an IPv6 packet. Insert a\n * \t\tSegment Routing Header (**struct ipv6_sr_hdr**) inside\n * \t\tthe IPv6 header.\n * \t**BPF_LWT_ENCAP_IP**\n * \t\tIP encapsulation (GRE/GUE/IPIP/etc). The outer header\n * \t\tmust be IPv4 or IPv6, followed by zero or more\n * \t\tadditional headers, up to **LWT_BPF_MAX_HEADROOM**\n * \t\ttotal bytes in all prepended headers. Please note that\n * \t\tif **skb_is_gso**\\ (*skb*) is true, no more than two\n * \t\theaders can be prepended, and the inner header, if\n * \t\tpresent, should be either GRE or UDP/GUE.\n *\n * \t**BPF_LWT_ENCAP_SEG6**\\ \\* types can be called by BPF programs\n * \tof type **BPF_PROG_TYPE_LWT_IN**; **BPF_LWT_ENCAP_IP** type can\n * \tbe called by bpf programs of types **BPF_PROG_TYPE_LWT_IN** and\n * \t**BPF_PROG_TYPE_LWT_XMIT**.\n *\n * \tA call to this helper is susceptible to change the underlying\n * \tpacket buffer. Therefore, at load time, all checks on pointers\n * \tpreviously done by the verifier are invalidated and must be\n * \tperformed again, if the helper is used in combination with\n * \tdirect packet access.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_lwt_push_encap)(struct __sk_buff *skb, __u32 type, void *hdr, __u32 len) = (void *) 73;\n\n/*\n * bpf_lwt_seg6_store_bytes\n *\n * \tStore *len* bytes from address *from* into the packet\n * \tassociated to *skb*, at *offset*. Only the flags, tag and TLVs\n * \tinside the outermost IPv6 Segment Routing Header can be\n * \tmodified through this helper.\n *\n * \tA call to this helper is susceptible to change the underlying\n * \tpacket buffer. Therefore, at load time, all checks on pointers\n * \tpreviously done by the verifier are invalidated and must be\n * \tperformed again, if the helper is used in combination with\n * \tdirect packet access.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_lwt_seg6_store_bytes)(struct __sk_buff *skb, __u32 offset, const void *from, __u32 len) = (void *) 74;\n\n/*\n * bpf_lwt_seg6_adjust_srh\n *\n * \tAdjust the size allocated to TLVs in the outermost IPv6\n * \tSegment Routing Header contained in the packet associated to\n * \t*skb*, at position *offset* by *delta* bytes. Only offsets\n * \tafter the segments are accepted. *delta* can be as well\n * \tpositive (growing) as negative (shrinking).\n *\n * \tA call to this helper is susceptible to change the underlying\n * \tpacket buffer. Therefore, at load time, all checks on pointers\n * \tpreviously done by the verifier are invalidated and must be\n * \tperformed again, if the helper is used in combination with\n * \tdirect packet access.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_lwt_seg6_adjust_srh)(struct __sk_buff *skb, __u32 offset, __s32 delta) = (void *) 75;\n\n/*\n * bpf_lwt_seg6_action\n *\n * \tApply an IPv6 Segment Routing action of type *action* to the\n * \tpacket associated to *skb*. Each action takes a parameter\n * \tcontained at address *param*, and of length *param_len* bytes.\n * \t*action* can be one of:\n *\n * \t**SEG6_LOCAL_ACTION_END_X**\n * \t\tEnd.X action: Endpoint with Layer-3 cross-connect.\n * \t\tType of *param*: **struct in6_addr**.\n * \t**SEG6_LOCAL_ACTION_END_T**\n * \t\tEnd.T action: Endpoint with specific IPv6 table lookup.\n * \t\tType of *param*: **int**.\n * \t**SEG6_LOCAL_ACTION_END_B6**\n * \t\tEnd.B6 action: Endpoint bound to an SRv6 policy.\n * \t\tType of *param*: **struct ipv6_sr_hdr**.\n * \t**SEG6_LOCAL_ACTION_END_B6_ENCAP**\n * \t\tEnd.B6.Encap action: Endpoint bound to an SRv6\n * \t\tencapsulation policy.\n * \t\tType of *param*: **struct ipv6_sr_hdr**.\n *\n * \tA call to this helper is susceptible to change the underlying\n * \tpacket buffer. Therefore, at load time, all checks on pointers\n * \tpreviously done by the verifier are invalidated and must be\n * \tperformed again, if the helper is used in combination with\n * \tdirect packet access.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_lwt_seg6_action)(struct __sk_buff *skb, __u32 action, void *param, __u32 param_len) = (void *) 76;\n\n/*\n * bpf_rc_repeat\n *\n * \tThis helper is used in programs implementing IR decoding, to\n * \treport a successfully decoded repeat key message. This delays\n * \tthe generation of a key up event for previously generated\n * \tkey down event.\n *\n * \tSome IR protocols like NEC have a special IR message for\n * \trepeating last button, for when a button is held down.\n *\n * \tThe *ctx* should point to the lirc sample as passed into\n * \tthe program.\n *\n * \tThis helper is only available is the kernel was compiled with\n * \tthe **CONFIG_BPF_LIRC_MODE2** configuration option set to\n * \t\"**y**\".\n *\n * Returns\n * \t0\n */\nstatic long (*bpf_rc_repeat)(void *ctx) = (void *) 77;\n\n/*\n * bpf_rc_keydown\n *\n * \tThis helper is used in programs implementing IR decoding, to\n * \treport a successfully decoded key press with *scancode*,\n * \t*toggle* value in the given *protocol*. The scancode will be\n * \ttranslated to a keycode using the rc keymap, and reported as\n * \tan input key down event. After a period a key up event is\n * \tgenerated. This period can be extended by calling either\n * \t**bpf_rc_keydown**\\ () again with the same values, or calling\n * \t**bpf_rc_repeat**\\ ().\n *\n * \tSome protocols include a toggle bit, in case the button was\n * \treleased and pressed again between consecutive scancodes.\n *\n * \tThe *ctx* should point to the lirc sample as passed into\n * \tthe program.\n *\n * \tThe *protocol* is the decoded protocol number (see\n * \t**enum rc_proto** for some predefined values).\n *\n * \tThis helper is only available is the kernel was compiled with\n * \tthe **CONFIG_BPF_LIRC_MODE2** configuration option set to\n * \t\"**y**\".\n *\n * Returns\n * \t0\n */\nstatic long (*bpf_rc_keydown)(void *ctx, __u32 protocol, __u64 scancode, __u32 toggle) = (void *) 78;\n\n/*\n * bpf_skb_cgroup_id\n *\n * \tReturn the cgroup v2 id of the socket associated with the *skb*.\n * \tThis is roughly similar to the **bpf_get_cgroup_classid**\\ ()\n * \thelper for cgroup v1 by providing a tag resp. identifier that\n * \tcan be matched on or used for map lookups e.g. to implement\n * \tpolicy. The cgroup v2 id of a given path in the hierarchy is\n * \texposed in user space through the f_handle API in order to get\n * \tto the same 64-bit id.\n *\n * \tThis helper can be used on TC egress path, but not on ingress,\n * \tand is available only if the kernel was compiled with the\n * \t**CONFIG_SOCK_CGROUP_DATA** configuration option.\n *\n * Returns\n * \tThe id is returned or 0 in case the id could not be retrieved.\n */\nstatic __u64 (*bpf_skb_cgroup_id)(struct __sk_buff *skb) = (void *) 79;\n\n/*\n * bpf_get_current_cgroup_id\n *\n *\n * Returns\n * \tA 64-bit integer containing the current cgroup id based\n * \ton the cgroup within which the current task is running.\n */\nstatic __u64 (*bpf_get_current_cgroup_id)(void) = (void *) 80;\n\n/*\n * bpf_get_local_storage\n *\n * \tGet the pointer to the local storage area.\n * \tThe type and the size of the local storage is defined\n * \tby the *map* argument.\n * \tThe *flags* meaning is specific for each map type,\n * \tand has to be 0 for cgroup local storage.\n *\n * \tDepending on the BPF program type, a local storage area\n * \tcan be shared between multiple instances of the BPF program,\n * \trunning simultaneously.\n *\n * \tA user should care about the synchronization by himself.\n * \tFor example, by using the **BPF_ATOMIC** instructions to alter\n * \tthe shared data.\n *\n * Returns\n * \tA pointer to the local storage area.\n */\nstatic void *(*bpf_get_local_storage)(void *map, __u64 flags) = (void *) 81;\n\n/*\n * bpf_sk_select_reuseport\n *\n * \tSelect a **SO_REUSEPORT** socket from a\n * \t**BPF_MAP_TYPE_REUSEPORT_SOCKARRAY** *map*.\n * \tIt checks the selected socket is matching the incoming\n * \trequest in the socket buffer.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_sk_select_reuseport)(struct sk_reuseport_md *reuse, void *map, void *key, __u64 flags) = (void *) 82;\n\n/*\n * bpf_skb_ancestor_cgroup_id\n *\n * \tReturn id of cgroup v2 that is ancestor of cgroup associated\n * \twith the *skb* at the *ancestor_level*.  The root cgroup is at\n * \t*ancestor_level* zero and each step down the hierarchy\n * \tincrements the level. If *ancestor_level* == level of cgroup\n * \tassociated with *skb*, then return value will be same as that\n * \tof **bpf_skb_cgroup_id**\\ ().\n *\n * \tThe helper is useful to implement policies based on cgroups\n * \tthat are upper in hierarchy than immediate cgroup associated\n * \twith *skb*.\n *\n * \tThe format of returned id and helper limitations are same as in\n * \t**bpf_skb_cgroup_id**\\ ().\n *\n * Returns\n * \tThe id is returned or 0 in case the id could not be retrieved.\n */\nstatic __u64 (*bpf_skb_ancestor_cgroup_id)(struct __sk_buff *skb, int ancestor_level) = (void *) 83;\n\n/*\n * bpf_sk_lookup_tcp\n *\n * \tLook for TCP socket matching *tuple*, optionally in a child\n * \tnetwork namespace *netns*. The return value must be checked,\n * \tand if non-**NULL**, released via **bpf_sk_release**\\ ().\n *\n * \tThe *ctx* should point to the context of the program, such as\n * \tthe skb or socket (depending on the hook in use). This is used\n * \tto determine the base network namespace for the lookup.\n *\n * \t*tuple_size* must be one of:\n *\n * \t**sizeof**\\ (*tuple*\\ **->ipv4**)\n * \t\tLook for an IPv4 socket.\n * \t**sizeof**\\ (*tuple*\\ **->ipv6**)\n * \t\tLook for an IPv6 socket.\n *\n * \tIf the *netns* is a negative signed 32-bit integer, then the\n * \tsocket lookup table in the netns associated with the *ctx*\n * \twill be used. For the TC hooks, this is the netns of the device\n * \tin the skb. For socket hooks, this is the netns of the socket.\n * \tIf *netns* is any other signed 32-bit value greater than or\n * \tequal to zero then it specifies the ID of the netns relative to\n * \tthe netns associated with the *ctx*. *netns* values beyond the\n * \trange of 32-bit integers are reserved for future use.\n *\n * \tAll values for *flags* are reserved for future usage, and must\n * \tbe left at zero.\n *\n * \tThis helper is available only if the kernel was compiled with\n * \t**CONFIG_NET** configuration option.\n *\n * Returns\n * \tPointer to **struct bpf_sock**, or **NULL** in case of failure.\n * \tFor sockets with reuseport option, the **struct bpf_sock**\n * \tresult is from *reuse*\\ **->socks**\\ [] using the hash of the\n * \ttuple.\n */\nstatic struct bpf_sock *(*bpf_sk_lookup_tcp)(void *ctx, struct bpf_sock_tuple *tuple, __u32 tuple_size, __u64 netns, __u64 flags) = (void *) 84;\n\n/*\n * bpf_sk_lookup_udp\n *\n * \tLook for UDP socket matching *tuple*, optionally in a child\n * \tnetwork namespace *netns*. The return value must be checked,\n * \tand if non-**NULL**, released via **bpf_sk_release**\\ ().\n *\n * \tThe *ctx* should point to the context of the program, such as\n * \tthe skb or socket (depending on the hook in use). This is used\n * \tto determine the base network namespace for the lookup.\n *\n * \t*tuple_size* must be one of:\n *\n * \t**sizeof**\\ (*tuple*\\ **->ipv4**)\n * \t\tLook for an IPv4 socket.\n * \t**sizeof**\\ (*tuple*\\ **->ipv6**)\n * \t\tLook for an IPv6 socket.\n *\n * \tIf the *netns* is a negative signed 32-bit integer, then the\n * \tsocket lookup table in the netns associated with the *ctx*\n * \twill be used. For the TC hooks, this is the netns of the device\n * \tin the skb. For socket hooks, this is the netns of the socket.\n * \tIf *netns* is any other signed 32-bit value greater than or\n * \tequal to zero then it specifies the ID of the netns relative to\n * \tthe netns associated with the *ctx*. *netns* values beyond the\n * \trange of 32-bit integers are reserved for future use.\n *\n * \tAll values for *flags* are reserved for future usage, and must\n * \tbe left at zero.\n *\n * \tThis helper is available only if the kernel was compiled with\n * \t**CONFIG_NET** configuration option.\n *\n * Returns\n * \tPointer to **struct bpf_sock**, or **NULL** in case of failure.\n * \tFor sockets with reuseport option, the **struct bpf_sock**\n * \tresult is from *reuse*\\ **->socks**\\ [] using the hash of the\n * \ttuple.\n */\nstatic struct bpf_sock *(*bpf_sk_lookup_udp)(void *ctx, struct bpf_sock_tuple *tuple, __u32 tuple_size, __u64 netns, __u64 flags) = (void *) 85;\n\n/*\n * bpf_sk_release\n *\n * \tRelease the reference held by *sock*. *sock* must be a\n * \tnon-**NULL** pointer that was returned from\n * \t**bpf_sk_lookup_xxx**\\ ().\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_sk_release)(void *sock) = (void *) 86;\n\n/*\n * bpf_map_push_elem\n *\n * \tPush an element *value* in *map*. *flags* is one of:\n *\n * \t**BPF_EXIST**\n * \t\tIf the queue/stack is full, the oldest element is\n * \t\tremoved to make room for this.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_map_push_elem)(void *map, const void *value, __u64 flags) = (void *) 87;\n\n/*\n * bpf_map_pop_elem\n *\n * \tPop an element from *map*.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_map_pop_elem)(void *map, void *value) = (void *) 88;\n\n/*\n * bpf_map_peek_elem\n *\n * \tGet an element from *map* without removing it.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_map_peek_elem)(void *map, void *value) = (void *) 89;\n\n/*\n * bpf_msg_push_data\n *\n * \tFor socket policies, insert *len* bytes into *msg* at offset\n * \t*start*.\n *\n * \tIf a program of type **BPF_PROG_TYPE_SK_MSG** is run on a\n * \t*msg* it may want to insert metadata or options into the *msg*.\n * \tThis can later be read and used by any of the lower layer BPF\n * \thooks.\n *\n * \tThis helper may fail if under memory pressure (a malloc\n * \tfails) in these cases BPF programs will get an appropriate\n * \terror and BPF programs will need to handle them.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_msg_push_data)(struct sk_msg_md *msg, __u32 start, __u32 len, __u64 flags) = (void *) 90;\n\n/*\n * bpf_msg_pop_data\n *\n * \tWill remove *len* bytes from a *msg* starting at byte *start*.\n * \tThis may result in **ENOMEM** errors under certain situations if\n * \tan allocation and copy are required due to a full ring buffer.\n * \tHowever, the helper will try to avoid doing the allocation\n * \tif possible. Other errors can occur if input parameters are\n * \tinvalid either due to *start* byte not being valid part of *msg*\n * \tpayload and/or *pop* value being to large.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_msg_pop_data)(struct sk_msg_md *msg, __u32 start, __u32 len, __u64 flags) = (void *) 91;\n\n/*\n * bpf_rc_pointer_rel\n *\n * \tThis helper is used in programs implementing IR decoding, to\n * \treport a successfully decoded pointer movement.\n *\n * \tThe *ctx* should point to the lirc sample as passed into\n * \tthe program.\n *\n * \tThis helper is only available is the kernel was compiled with\n * \tthe **CONFIG_BPF_LIRC_MODE2** configuration option set to\n * \t\"**y**\".\n *\n * Returns\n * \t0\n */\nstatic long (*bpf_rc_pointer_rel)(void *ctx, __s32 rel_x, __s32 rel_y) = (void *) 92;\n\n/*\n * bpf_spin_lock\n *\n * \tAcquire a spinlock represented by the pointer *lock*, which is\n * \tstored as part of a value of a map. Taking the lock allows to\n * \tsafely update the rest of the fields in that value. The\n * \tspinlock can (and must) later be released with a call to\n * \t**bpf_spin_unlock**\\ (\\ *lock*\\ ).\n *\n * \tSpinlocks in BPF programs come with a number of restrictions\n * \tand constraints:\n *\n * \t* **bpf_spin_lock** objects are only allowed inside maps of\n * \t  types **BPF_MAP_TYPE_HASH** and **BPF_MAP_TYPE_ARRAY** (this\n * \t  list could be extended in the future).\n * \t* BTF description of the map is mandatory.\n * \t* The BPF program can take ONE lock at a time, since taking two\n * \t  or more could cause dead locks.\n * \t* Only one **struct bpf_spin_lock** is allowed per map element.\n * \t* When the lock is taken, calls (either BPF to BPF or helpers)\n * \t  are not allowed.\n * \t* The **BPF_LD_ABS** and **BPF_LD_IND** instructions are not\n * \t  allowed inside a spinlock-ed region.\n * \t* The BPF program MUST call **bpf_spin_unlock**\\ () to release\n * \t  the lock, on all execution paths, before it returns.\n * \t* The BPF program can access **struct bpf_spin_lock** only via\n * \t  the **bpf_spin_lock**\\ () and **bpf_spin_unlock**\\ ()\n * \t  helpers. Loading or storing data into the **struct\n * \t  bpf_spin_lock** *lock*\\ **;** field of a map is not allowed.\n * \t* To use the **bpf_spin_lock**\\ () helper, the BTF description\n * \t  of the map value must be a struct and have **struct\n * \t  bpf_spin_lock** *anyname*\\ **;** field at the top level.\n * \t  Nested lock inside another struct is not allowed.\n * \t* The **struct bpf_spin_lock** *lock* field in a map value must\n * \t  be aligned on a multiple of 4 bytes in that value.\n * \t* Syscall with command **BPF_MAP_LOOKUP_ELEM** does not copy\n * \t  the **bpf_spin_lock** field to user space.\n * \t* Syscall with command **BPF_MAP_UPDATE_ELEM**, or update from\n * \t  a BPF program, do not update the **bpf_spin_lock** field.\n * \t* **bpf_spin_lock** cannot be on the stack or inside a\n * \t  networking packet (it can only be inside of a map values).\n * \t* **bpf_spin_lock** is available to root only.\n * \t* Tracing programs and socket filter programs cannot use\n * \t  **bpf_spin_lock**\\ () due to insufficient preemption checks\n * \t  (but this may change in the future).\n * \t* **bpf_spin_lock** is not allowed in inner maps of map-in-map.\n *\n * Returns\n * \t0\n */\nstatic long (*bpf_spin_lock)(struct bpf_spin_lock *lock) = (void *) 93;\n\n/*\n * bpf_spin_unlock\n *\n * \tRelease the *lock* previously locked by a call to\n * \t**bpf_spin_lock**\\ (\\ *lock*\\ ).\n *\n * Returns\n * \t0\n */\nstatic long (*bpf_spin_unlock)(struct bpf_spin_lock *lock) = (void *) 94;\n\n/*\n * bpf_sk_fullsock\n *\n * \tThis helper gets a **struct bpf_sock** pointer such\n * \tthat all the fields in this **bpf_sock** can be accessed.\n *\n * Returns\n * \tA **struct bpf_sock** pointer on success, or **NULL** in\n * \tcase of failure.\n */\nstatic struct bpf_sock *(*bpf_sk_fullsock)(struct bpf_sock *sk) = (void *) 95;\n\n/*\n * bpf_tcp_sock\n *\n * \tThis helper gets a **struct bpf_tcp_sock** pointer from a\n * \t**struct bpf_sock** pointer.\n *\n * Returns\n * \tA **struct bpf_tcp_sock** pointer on success, or **NULL** in\n * \tcase of failure.\n */\nstatic struct bpf_tcp_sock *(*bpf_tcp_sock)(struct bpf_sock *sk) = (void *) 96;\n\n/*\n * bpf_skb_ecn_set_ce\n *\n * \tSet ECN (Explicit Congestion Notification) field of IP header\n * \tto **CE** (Congestion Encountered) if current value is **ECT**\n * \t(ECN Capable Transport). Otherwise, do nothing. Works with IPv6\n * \tand IPv4.\n *\n * Returns\n * \t1 if the **CE** flag is set (either by the current helper call\n * \tor because it was already present), 0 if it is not set.\n */\nstatic long (*bpf_skb_ecn_set_ce)(struct __sk_buff *skb) = (void *) 97;\n\n/*\n * bpf_get_listener_sock\n *\n * \tReturn a **struct bpf_sock** pointer in **TCP_LISTEN** state.\n * \t**bpf_sk_release**\\ () is unnecessary and not allowed.\n *\n * Returns\n * \tA **struct bpf_sock** pointer on success, or **NULL** in\n * \tcase of failure.\n */\nstatic struct bpf_sock *(*bpf_get_listener_sock)(struct bpf_sock *sk) = (void *) 98;\n\n/*\n * bpf_skc_lookup_tcp\n *\n * \tLook for TCP socket matching *tuple*, optionally in a child\n * \tnetwork namespace *netns*. The return value must be checked,\n * \tand if non-**NULL**, released via **bpf_sk_release**\\ ().\n *\n * \tThis function is identical to **bpf_sk_lookup_tcp**\\ (), except\n * \tthat it also returns timewait or request sockets. Use\n * \t**bpf_sk_fullsock**\\ () or **bpf_tcp_sock**\\ () to access the\n * \tfull structure.\n *\n * \tThis helper is available only if the kernel was compiled with\n * \t**CONFIG_NET** configuration option.\n *\n * Returns\n * \tPointer to **struct bpf_sock**, or **NULL** in case of failure.\n * \tFor sockets with reuseport option, the **struct bpf_sock**\n * \tresult is from *reuse*\\ **->socks**\\ [] using the hash of the\n * \ttuple.\n */\nstatic struct bpf_sock *(*bpf_skc_lookup_tcp)(void *ctx, struct bpf_sock_tuple *tuple, __u32 tuple_size, __u64 netns, __u64 flags) = (void *) 99;\n\n/*\n * bpf_tcp_check_syncookie\n *\n * \tCheck whether *iph* and *th* contain a valid SYN cookie ACK for\n * \tthe listening socket in *sk*.\n *\n * \t*iph* points to the start of the IPv4 or IPv6 header, while\n * \t*iph_len* contains **sizeof**\\ (**struct iphdr**) or\n * \t**sizeof**\\ (**struct ip6hdr**).\n *\n * \t*th* points to the start of the TCP header, while *th_len*\n * \tcontains **sizeof**\\ (**struct tcphdr**).\n *\n * Returns\n * \t0 if *iph* and *th* are a valid SYN cookie ACK, or a negative\n * \terror otherwise.\n */\nstatic long (*bpf_tcp_check_syncookie)(void *sk, void *iph, __u32 iph_len, struct tcphdr *th, __u32 th_len) = (void *) 100;\n\n/*\n * bpf_sysctl_get_name\n *\n * \tGet name of sysctl in /proc/sys/ and copy it into provided by\n * \tprogram buffer *buf* of size *buf_len*.\n *\n * \tThe buffer is always NUL terminated, unless it's zero-sized.\n *\n * \tIf *flags* is zero, full name (e.g. \"net/ipv4/tcp_mem\") is\n * \tcopied. Use **BPF_F_SYSCTL_BASE_NAME** flag to copy base name\n * \tonly (e.g. \"tcp_mem\").\n *\n * Returns\n * \tNumber of character copied (not including the trailing NUL).\n *\n * \t**-E2BIG** if the buffer wasn't big enough (*buf* will contain\n * \ttruncated name in this case).\n */\nstatic long (*bpf_sysctl_get_name)(struct bpf_sysctl *ctx, char *buf, unsigned long buf_len, __u64 flags) = (void *) 101;\n\n/*\n * bpf_sysctl_get_current_value\n *\n * \tGet current value of sysctl as it is presented in /proc/sys\n * \t(incl. newline, etc), and copy it as a string into provided\n * \tby program buffer *buf* of size *buf_len*.\n *\n * \tThe whole value is copied, no matter what file position user\n * \tspace issued e.g. sys_read at.\n *\n * \tThe buffer is always NUL terminated, unless it's zero-sized.\n *\n * Returns\n * \tNumber of character copied (not including the trailing NUL).\n *\n * \t**-E2BIG** if the buffer wasn't big enough (*buf* will contain\n * \ttruncated name in this case).\n *\n * \t**-EINVAL** if current value was unavailable, e.g. because\n * \tsysctl is uninitialized and read returns -EIO for it.\n */\nstatic long (*bpf_sysctl_get_current_value)(struct bpf_sysctl *ctx, char *buf, unsigned long buf_len) = (void *) 102;\n\n/*\n * bpf_sysctl_get_new_value\n *\n * \tGet new value being written by user space to sysctl (before\n * \tthe actual write happens) and copy it as a string into\n * \tprovided by program buffer *buf* of size *buf_len*.\n *\n * \tUser space may write new value at file position > 0.\n *\n * \tThe buffer is always NUL terminated, unless it's zero-sized.\n *\n * Returns\n * \tNumber of character copied (not including the trailing NUL).\n *\n * \t**-E2BIG** if the buffer wasn't big enough (*buf* will contain\n * \ttruncated name in this case).\n *\n * \t**-EINVAL** if sysctl is being read.\n */\nstatic long (*bpf_sysctl_get_new_value)(struct bpf_sysctl *ctx, char *buf, unsigned long buf_len) = (void *) 103;\n\n/*\n * bpf_sysctl_set_new_value\n *\n * \tOverride new value being written by user space to sysctl with\n * \tvalue provided by program in buffer *buf* of size *buf_len*.\n *\n * \t*buf* should contain a string in same form as provided by user\n * \tspace on sysctl write.\n *\n * \tUser space may write new value at file position > 0. To override\n * \tthe whole sysctl value file position should be set to zero.\n *\n * Returns\n * \t0 on success.\n *\n * \t**-E2BIG** if the *buf_len* is too big.\n *\n * \t**-EINVAL** if sysctl is being read.\n */\nstatic long (*bpf_sysctl_set_new_value)(struct bpf_sysctl *ctx, const char *buf, unsigned long buf_len) = (void *) 104;\n\n/*\n * bpf_strtol\n *\n * \tConvert the initial part of the string from buffer *buf* of\n * \tsize *buf_len* to a long integer according to the given base\n * \tand save the result in *res*.\n *\n * \tThe string may begin with an arbitrary amount of white space\n * \t(as determined by **isspace**\\ (3)) followed by a single\n * \toptional '**-**' sign.\n *\n * \tFive least significant bits of *flags* encode base, other bits\n * \tare currently unused.\n *\n * \tBase must be either 8, 10, 16 or 0 to detect it automatically\n * \tsimilar to user space **strtol**\\ (3).\n *\n * Returns\n * \tNumber of characters consumed on success. Must be positive but\n * \tno more than *buf_len*.\n *\n * \t**-EINVAL** if no valid digits were found or unsupported base\n * \twas provided.\n *\n * \t**-ERANGE** if resulting value was out of range.\n */\nstatic long (*bpf_strtol)(const char *buf, unsigned long buf_len, __u64 flags, long *res) = (void *) 105;\n\n/*\n * bpf_strtoul\n *\n * \tConvert the initial part of the string from buffer *buf* of\n * \tsize *buf_len* to an unsigned long integer according to the\n * \tgiven base and save the result in *res*.\n *\n * \tThe string may begin with an arbitrary amount of white space\n * \t(as determined by **isspace**\\ (3)).\n *\n * \tFive least significant bits of *flags* encode base, other bits\n * \tare currently unused.\n *\n * \tBase must be either 8, 10, 16 or 0 to detect it automatically\n * \tsimilar to user space **strtoul**\\ (3).\n *\n * Returns\n * \tNumber of characters consumed on success. Must be positive but\n * \tno more than *buf_len*.\n *\n * \t**-EINVAL** if no valid digits were found or unsupported base\n * \twas provided.\n *\n * \t**-ERANGE** if resulting value was out of range.\n */\nstatic long (*bpf_strtoul)(const char *buf, unsigned long buf_len, __u64 flags, unsigned long *res) = (void *) 106;\n\n/*\n * bpf_sk_storage_get\n *\n * \tGet a bpf-local-storage from a *sk*.\n *\n * \tLogically, it could be thought of getting the value from\n * \ta *map* with *sk* as the **key**.  From this\n * \tperspective,  the usage is not much different from\n * \t**bpf_map_lookup_elem**\\ (*map*, **&**\\ *sk*) except this\n * \thelper enforces the key must be a full socket and the map must\n * \tbe a **BPF_MAP_TYPE_SK_STORAGE** also.\n *\n * \tUnderneath, the value is stored locally at *sk* instead of\n * \tthe *map*.  The *map* is used as the bpf-local-storage\n * \t\"type\". The bpf-local-storage \"type\" (i.e. the *map*) is\n * \tsearched against all bpf-local-storages residing at *sk*.\n *\n * \t*sk* is a kernel **struct sock** pointer for LSM program.\n * \t*sk* is a **struct bpf_sock** pointer for other program types.\n *\n * \tAn optional *flags* (**BPF_SK_STORAGE_GET_F_CREATE**) can be\n * \tused such that a new bpf-local-storage will be\n * \tcreated if one does not exist.  *value* can be used\n * \ttogether with **BPF_SK_STORAGE_GET_F_CREATE** to specify\n * \tthe initial value of a bpf-local-storage.  If *value* is\n * \t**NULL**, the new bpf-local-storage will be zero initialized.\n *\n * Returns\n * \tA bpf-local-storage pointer is returned on success.\n *\n * \t**NULL** if not found or there was an error in adding\n * \ta new bpf-local-storage.\n */\nstatic void *(*bpf_sk_storage_get)(void *map, void *sk, void *value, __u64 flags) = (void *) 107;\n\n/*\n * bpf_sk_storage_delete\n *\n * \tDelete a bpf-local-storage from a *sk*.\n *\n * Returns\n * \t0 on success.\n *\n * \t**-ENOENT** if the bpf-local-storage cannot be found.\n * \t**-EINVAL** if sk is not a fullsock (e.g. a request_sock).\n */\nstatic long (*bpf_sk_storage_delete)(void *map, void *sk) = (void *) 108;\n\n/*\n * bpf_send_signal\n *\n * \tSend signal *sig* to the process of the current task.\n * \tThe signal may be delivered to any of this process's threads.\n *\n * Returns\n * \t0 on success or successfully queued.\n *\n * \t**-EBUSY** if work queue under nmi is full.\n *\n * \t**-EINVAL** if *sig* is invalid.\n *\n * \t**-EPERM** if no permission to send the *sig*.\n *\n * \t**-EAGAIN** if bpf program can try again.\n */\nstatic long (*bpf_send_signal)(__u32 sig) = (void *) 109;\n\n/*\n * bpf_tcp_gen_syncookie\n *\n * \tTry to issue a SYN cookie for the packet with corresponding\n * \tIP/TCP headers, *iph* and *th*, on the listening socket in *sk*.\n *\n * \t*iph* points to the start of the IPv4 or IPv6 header, while\n * \t*iph_len* contains **sizeof**\\ (**struct iphdr**) or\n * \t**sizeof**\\ (**struct ip6hdr**).\n *\n * \t*th* points to the start of the TCP header, while *th_len*\n * \tcontains the length of the TCP header.\n *\n * Returns\n * \tOn success, lower 32 bits hold the generated SYN cookie in\n * \tfollowed by 16 bits which hold the MSS value for that cookie,\n * \tand the top 16 bits are unused.\n *\n * \tOn failure, the returned value is one of the following:\n *\n * \t**-EINVAL** SYN cookie cannot be issued due to error\n *\n * \t**-ENOENT** SYN cookie should not be issued (no SYN flood)\n *\n * \t**-EOPNOTSUPP** kernel configuration does not enable SYN cookies\n *\n * \t**-EPROTONOSUPPORT** IP packet version is not 4 or 6\n */\nstatic __s64 (*bpf_tcp_gen_syncookie)(void *sk, void *iph, __u32 iph_len, struct tcphdr *th, __u32 th_len) = (void *) 110;\n\n/*\n * bpf_skb_output\n *\n * \tWrite raw *data* blob into a special BPF perf event held by\n * \t*map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf\n * \tevent must have the following attributes: **PERF_SAMPLE_RAW**\n * \tas **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and\n * \t**PERF_COUNT_SW_BPF_OUTPUT** as **config**.\n *\n * \tThe *flags* are used to indicate the index in *map* for which\n * \tthe value must be put, masked with **BPF_F_INDEX_MASK**.\n * \tAlternatively, *flags* can be set to **BPF_F_CURRENT_CPU**\n * \tto indicate that the index of the current CPU core should be\n * \tused.\n *\n * \tThe value to write, of *size*, is passed through eBPF stack and\n * \tpointed by *data*.\n *\n * \t*ctx* is a pointer to in-kernel struct sk_buff.\n *\n * \tThis helper is similar to **bpf_perf_event_output**\\ () but\n * \trestricted to raw_tracepoint bpf programs.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_skb_output)(void *ctx, void *map, __u64 flags, void *data, __u64 size) = (void *) 111;\n\n/*\n * bpf_probe_read_user\n *\n * \tSafely attempt to read *size* bytes from user space address\n * \t*unsafe_ptr* and store the data in *dst*.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_probe_read_user)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 112;\n\n/*\n * bpf_probe_read_kernel\n *\n * \tSafely attempt to read *size* bytes from kernel space address\n * \t*unsafe_ptr* and store the data in *dst*.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_probe_read_kernel)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 113;\n\n/*\n * bpf_probe_read_user_str\n *\n * \tCopy a NUL terminated string from an unsafe user address\n * \t*unsafe_ptr* to *dst*. The *size* should include the\n * \tterminating NUL byte. In case the string length is smaller than\n * \t*size*, the target is not padded with further NUL bytes. If the\n * \tstring length is larger than *size*, just *size*-1 bytes are\n * \tcopied and the last byte is set to NUL.\n *\n * \tOn success, returns the number of bytes that were written,\n * \tincluding the terminal NUL. This makes this helper useful in\n * \ttracing programs for reading strings, and more importantly to\n * \tget its length at runtime. See the following snippet:\n *\n * \t::\n *\n * \t\tSEC(\"kprobe/sys_open\")\n * \t\tvoid bpf_sys_open(struct pt_regs *ctx)\n * \t\t{\n * \t\t        char buf[PATHLEN]; // PATHLEN is defined to 256\n * \t\t        int res = bpf_probe_read_user_str(buf, sizeof(buf),\n * \t\t\t                                  ctx->di);\n *\n * \t\t\t// Consume buf, for example push it to\n * \t\t\t// userspace via bpf_perf_event_output(); we\n * \t\t\t// can use res (the string length) as event\n * \t\t\t// size, after checking its boundaries.\n * \t\t}\n *\n * \tIn comparison, using **bpf_probe_read_user**\\ () helper here\n * \tinstead to read the string would require to estimate the length\n * \tat compile time, and would often result in copying more memory\n * \tthan necessary.\n *\n * \tAnother useful use case is when parsing individual process\n * \targuments or individual environment variables navigating\n * \t*current*\\ **->mm->arg_start** and *current*\\\n * \t**->mm->env_start**: using this helper and the return value,\n * \tone can quickly iterate at the right offset of the memory area.\n *\n * Returns\n * \tOn success, the strictly positive length of the output string,\n * \tincluding the trailing NUL character. On error, a negative\n * \tvalue.\n */\nstatic long (*bpf_probe_read_user_str)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 114;\n\n/*\n * bpf_probe_read_kernel_str\n *\n * \tCopy a NUL terminated string from an unsafe kernel address *unsafe_ptr*\n * \tto *dst*. Same semantics as with **bpf_probe_read_user_str**\\ () apply.\n *\n * Returns\n * \tOn success, the strictly positive length of the string, including\n * \tthe trailing NUL character. On error, a negative value.\n */\nstatic long (*bpf_probe_read_kernel_str)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 115;\n\n/*\n * bpf_tcp_send_ack\n *\n * \tSend out a tcp-ack. *tp* is the in-kernel struct **tcp_sock**.\n * \t*rcv_nxt* is the ack_seq to be sent out.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_tcp_send_ack)(void *tp, __u32 rcv_nxt) = (void *) 116;\n\n/*\n * bpf_send_signal_thread\n *\n * \tSend signal *sig* to the thread corresponding to the current task.\n *\n * Returns\n * \t0 on success or successfully queued.\n *\n * \t**-EBUSY** if work queue under nmi is full.\n *\n * \t**-EINVAL** if *sig* is invalid.\n *\n * \t**-EPERM** if no permission to send the *sig*.\n *\n * \t**-EAGAIN** if bpf program can try again.\n */\nstatic long (*bpf_send_signal_thread)(__u32 sig) = (void *) 117;\n\n/*\n * bpf_jiffies64\n *\n * \tObtain the 64bit jiffies\n *\n * Returns\n * \tThe 64 bit jiffies\n */\nstatic __u64 (*bpf_jiffies64)(void) = (void *) 118;\n\n/*\n * bpf_read_branch_records\n *\n * \tFor an eBPF program attached to a perf event, retrieve the\n * \tbranch records (**struct perf_branch_entry**) associated to *ctx*\n * \tand store it in the buffer pointed by *buf* up to size\n * \t*size* bytes.\n *\n * Returns\n * \tOn success, number of bytes written to *buf*. On error, a\n * \tnegative value.\n *\n * \tThe *flags* can be set to **BPF_F_GET_BRANCH_RECORDS_SIZE** to\n * \tinstead return the number of bytes required to store all the\n * \tbranch entries. If this flag is set, *buf* may be NULL.\n *\n * \t**-EINVAL** if arguments invalid or **size** not a multiple\n * \tof **sizeof**\\ (**struct perf_branch_entry**\\ ).\n *\n * \t**-ENOENT** if architecture does not support branch records.\n */\nstatic long (*bpf_read_branch_records)(struct bpf_perf_event_data *ctx, void *buf, __u32 size, __u64 flags) = (void *) 119;\n\n/*\n * bpf_get_ns_current_pid_tgid\n *\n * \tReturns 0 on success, values for *pid* and *tgid* as seen from the current\n * \t*namespace* will be returned in *nsdata*.\n *\n * Returns\n * \t0 on success, or one of the following in case of failure:\n *\n * \t**-EINVAL** if dev and inum supplied don't match dev_t and inode number\n * \twith nsfs of current task, or if dev conversion to dev_t lost high bits.\n *\n * \t**-ENOENT** if pidns does not exists for the current task.\n */\nstatic long (*bpf_get_ns_current_pid_tgid)(__u64 dev, __u64 ino, struct bpf_pidns_info *nsdata, __u32 size) = (void *) 120;\n\n/*\n * bpf_xdp_output\n *\n * \tWrite raw *data* blob into a special BPF perf event held by\n * \t*map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf\n * \tevent must have the following attributes: **PERF_SAMPLE_RAW**\n * \tas **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and\n * \t**PERF_COUNT_SW_BPF_OUTPUT** as **config**.\n *\n * \tThe *flags* are used to indicate the index in *map* for which\n * \tthe value must be put, masked with **BPF_F_INDEX_MASK**.\n * \tAlternatively, *flags* can be set to **BPF_F_CURRENT_CPU**\n * \tto indicate that the index of the current CPU core should be\n * \tused.\n *\n * \tThe value to write, of *size*, is passed through eBPF stack and\n * \tpointed by *data*.\n *\n * \t*ctx* is a pointer to in-kernel struct xdp_buff.\n *\n * \tThis helper is similar to **bpf_perf_eventoutput**\\ () but\n * \trestricted to raw_tracepoint bpf programs.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_xdp_output)(void *ctx, void *map, __u64 flags, void *data, __u64 size) = (void *) 121;\n\n/*\n * bpf_get_netns_cookie\n *\n * \tRetrieve the cookie (generated by the kernel) of the network\n * \tnamespace the input *ctx* is associated with. The network\n * \tnamespace cookie remains stable for its lifetime and provides\n * \ta global identifier that can be assumed unique. If *ctx* is\n * \tNULL, then the helper returns the cookie for the initial\n * \tnetwork namespace. The cookie itself is very similar to that\n * \tof **bpf_get_socket_cookie**\\ () helper, but for network\n * \tnamespaces instead of sockets.\n *\n * Returns\n * \tA 8-byte long opaque number.\n */\nstatic __u64 (*bpf_get_netns_cookie)(void *ctx) = (void *) 122;\n\n/*\n * bpf_get_current_ancestor_cgroup_id\n *\n * \tReturn id of cgroup v2 that is ancestor of the cgroup associated\n * \twith the current task at the *ancestor_level*. The root cgroup\n * \tis at *ancestor_level* zero and each step down the hierarchy\n * \tincrements the level. If *ancestor_level* == level of cgroup\n * \tassociated with the current task, then return value will be the\n * \tsame as that of **bpf_get_current_cgroup_id**\\ ().\n *\n * \tThe helper is useful to implement policies based on cgroups\n * \tthat are upper in hierarchy than immediate cgroup associated\n * \twith the current task.\n *\n * \tThe format of returned id and helper limitations are same as in\n * \t**bpf_get_current_cgroup_id**\\ ().\n *\n * Returns\n * \tThe id is returned or 0 in case the id could not be retrieved.\n */\nstatic __u64 (*bpf_get_current_ancestor_cgroup_id)(int ancestor_level) = (void *) 123;\n\n/*\n * bpf_sk_assign\n *\n * \tHelper is overloaded depending on BPF program type. This\n * \tdescription applies to **BPF_PROG_TYPE_SCHED_CLS** and\n * \t**BPF_PROG_TYPE_SCHED_ACT** programs.\n *\n * \tAssign the *sk* to the *skb*. When combined with appropriate\n * \trouting configuration to receive the packet towards the socket,\n * \twill cause *skb* to be delivered to the specified socket.\n * \tSubsequent redirection of *skb* via  **bpf_redirect**\\ (),\n * \t**bpf_clone_redirect**\\ () or other methods outside of BPF may\n * \tinterfere with successful delivery to the socket.\n *\n * \tThis operation is only valid from TC ingress path.\n *\n * \tThe *flags* argument must be zero.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure:\n *\n * \t**-EINVAL** if specified *flags* are not supported.\n *\n * \t**-ENOENT** if the socket is unavailable for assignment.\n *\n * \t**-ENETUNREACH** if the socket is unreachable (wrong netns).\n *\n * \t**-EOPNOTSUPP** if the operation is not supported, for example\n * \ta call from outside of TC ingress.\n *\n * \t**-ESOCKTNOSUPPORT** if the socket type is not supported\n * \t(reuseport).\n */\nstatic long (*bpf_sk_assign)(void *ctx, void *sk, __u64 flags) = (void *) 124;\n\n/*\n * bpf_ktime_get_boot_ns\n *\n * \tReturn the time elapsed since system boot, in nanoseconds.\n * \tDoes include the time the system was suspended.\n * \tSee: **clock_gettime**\\ (**CLOCK_BOOTTIME**)\n *\n * Returns\n * \tCurrent *ktime*.\n */\nstatic __u64 (*bpf_ktime_get_boot_ns)(void) = (void *) 125;\n\n/*\n * bpf_seq_printf\n *\n * \t**bpf_seq_printf**\\ () uses seq_file **seq_printf**\\ () to print\n * \tout the format string.\n * \tThe *m* represents the seq_file. The *fmt* and *fmt_size* are for\n * \tthe format string itself. The *data* and *data_len* are format string\n * \targuments. The *data* are a **u64** array and corresponding format string\n * \tvalues are stored in the array. For strings and pointers where pointees\n * \tare accessed, only the pointer values are stored in the *data* array.\n * \tThe *data_len* is the size of *data* in bytes - must be a multiple of 8.\n *\n * \tFormats **%s**, **%p{i,I}{4,6}** requires to read kernel memory.\n * \tReading kernel memory may fail due to either invalid address or\n * \tvalid address but requiring a major memory fault. If reading kernel memory\n * \tfails, the string for **%s** will be an empty string, and the ip\n * \taddress for **%p{i,I}{4,6}** will be 0. Not returning error to\n * \tbpf program is consistent with what **bpf_trace_printk**\\ () does for now.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure:\n *\n * \t**-EBUSY** if per-CPU memory copy buffer is busy, can try again\n * \tby returning 1 from bpf program.\n *\n * \t**-EINVAL** if arguments are invalid, or if *fmt* is invalid/unsupported.\n *\n * \t**-E2BIG** if *fmt* contains too many format specifiers.\n *\n * \t**-EOVERFLOW** if an overflow happened: The same object will be tried again.\n */\nstatic long (*bpf_seq_printf)(struct seq_file *m, const char *fmt, __u32 fmt_size, const void *data, __u32 data_len) = (void *) 126;\n\n/*\n * bpf_seq_write\n *\n * \t**bpf_seq_write**\\ () uses seq_file **seq_write**\\ () to write the data.\n * \tThe *m* represents the seq_file. The *data* and *len* represent the\n * \tdata to write in bytes.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure:\n *\n * \t**-EOVERFLOW** if an overflow happened: The same object will be tried again.\n */\nstatic long (*bpf_seq_write)(struct seq_file *m, const void *data, __u32 len) = (void *) 127;\n\n/*\n * bpf_sk_cgroup_id\n *\n * \tReturn the cgroup v2 id of the socket *sk*.\n *\n * \t*sk* must be a non-**NULL** pointer to a socket, e.g. one\n * \treturned from **bpf_sk_lookup_xxx**\\ (),\n * \t**bpf_sk_fullsock**\\ (), etc. The format of returned id is\n * \tsame as in **bpf_skb_cgroup_id**\\ ().\n *\n * \tThis helper is available only if the kernel was compiled with\n * \tthe **CONFIG_SOCK_CGROUP_DATA** configuration option.\n *\n * Returns\n * \tThe id is returned or 0 in case the id could not be retrieved.\n */\nstatic __u64 (*bpf_sk_cgroup_id)(void *sk) = (void *) 128;\n\n/*\n * bpf_sk_ancestor_cgroup_id\n *\n * \tReturn id of cgroup v2 that is ancestor of cgroup associated\n * \twith the *sk* at the *ancestor_level*.  The root cgroup is at\n * \t*ancestor_level* zero and each step down the hierarchy\n * \tincrements the level. If *ancestor_level* == level of cgroup\n * \tassociated with *sk*, then return value will be same as that\n * \tof **bpf_sk_cgroup_id**\\ ().\n *\n * \tThe helper is useful to implement policies based on cgroups\n * \tthat are upper in hierarchy than immediate cgroup associated\n * \twith *sk*.\n *\n * \tThe format of returned id and helper limitations are same as in\n * \t**bpf_sk_cgroup_id**\\ ().\n *\n * Returns\n * \tThe id is returned or 0 in case the id could not be retrieved.\n */\nstatic __u64 (*bpf_sk_ancestor_cgroup_id)(void *sk, int ancestor_level) = (void *) 129;\n\n/*\n * bpf_ringbuf_output\n *\n * \tCopy *size* bytes from *data* into a ring buffer *ringbuf*.\n * \tIf **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification\n * \tof new data availability is sent.\n * \tIf **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification\n * \tof new data availability is sent unconditionally.\n * \tIf **0** is specified in *flags*, an adaptive notification\n * \tof new data availability is sent.\n *\n * \tAn adaptive notification is a notification sent whenever the user-space\n * \tprocess has caught up and consumed all available payloads. In case the user-space\n * \tprocess is still processing a previous payload, then no notification is needed\n * \tas it will process the newly added payload automatically.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_ringbuf_output)(void *ringbuf, void *data, __u64 size, __u64 flags) = (void *) 130;\n\n/*\n * bpf_ringbuf_reserve\n *\n * \tReserve *size* bytes of payload in a ring buffer *ringbuf*.\n * \t*flags* must be 0.\n *\n * Returns\n * \tValid pointer with *size* bytes of memory available; NULL,\n * \totherwise.\n */\nstatic void *(*bpf_ringbuf_reserve)(void *ringbuf, __u64 size, __u64 flags) = (void *) 131;\n\n/*\n * bpf_ringbuf_submit\n *\n * \tSubmit reserved ring buffer sample, pointed to by *data*.\n * \tIf **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification\n * \tof new data availability is sent.\n * \tIf **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification\n * \tof new data availability is sent unconditionally.\n * \tIf **0** is specified in *flags*, an adaptive notification\n * \tof new data availability is sent.\n *\n * \tSee 'bpf_ringbuf_output()' for the definition of adaptive notification.\n *\n * Returns\n * \tNothing. Always succeeds.\n */\nstatic void (*bpf_ringbuf_submit)(void *data, __u64 flags) = (void *) 132;\n\n/*\n * bpf_ringbuf_discard\n *\n * \tDiscard reserved ring buffer sample, pointed to by *data*.\n * \tIf **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification\n * \tof new data availability is sent.\n * \tIf **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification\n * \tof new data availability is sent unconditionally.\n * \tIf **0** is specified in *flags*, an adaptive notification\n * \tof new data availability is sent.\n *\n * \tSee 'bpf_ringbuf_output()' for the definition of adaptive notification.\n *\n * Returns\n * \tNothing. Always succeeds.\n */\nstatic void (*bpf_ringbuf_discard)(void *data, __u64 flags) = (void *) 133;\n\n/*\n * bpf_ringbuf_query\n *\n * \tQuery various characteristics of provided ring buffer. What\n * \texactly is queries is determined by *flags*:\n *\n * \t* **BPF_RB_AVAIL_DATA**: Amount of data not yet consumed.\n * \t* **BPF_RB_RING_SIZE**: The size of ring buffer.\n * \t* **BPF_RB_CONS_POS**: Consumer position (can wrap around).\n * \t* **BPF_RB_PROD_POS**: Producer(s) position (can wrap around).\n *\n * \tData returned is just a momentary snapshot of actual values\n * \tand could be inaccurate, so this facility should be used to\n * \tpower heuristics and for reporting, not to make 100% correct\n * \tcalculation.\n *\n * Returns\n * \tRequested value, or 0, if *flags* are not recognized.\n */\nstatic __u64 (*bpf_ringbuf_query)(void *ringbuf, __u64 flags) = (void *) 134;\n\n/*\n * bpf_csum_level\n *\n * \tChange the skbs checksum level by one layer up or down, or\n * \treset it entirely to none in order to have the stack perform\n * \tchecksum validation. The level is applicable to the following\n * \tprotocols: TCP, UDP, GRE, SCTP, FCOE. For example, a decap of\n * \t| ETH | IP | UDP | GUE | IP | TCP | into | ETH | IP | TCP |\n * \tthrough **bpf_skb_adjust_room**\\ () helper with passing in\n * \t**BPF_F_ADJ_ROOM_NO_CSUM_RESET** flag would require one\tcall\n * \tto **bpf_csum_level**\\ () with **BPF_CSUM_LEVEL_DEC** since\n * \tthe UDP header is removed. Similarly, an encap of the latter\n * \tinto the former could be accompanied by a helper call to\n * \t**bpf_csum_level**\\ () with **BPF_CSUM_LEVEL_INC** if the\n * \tskb is still intended to be processed in higher layers of the\n * \tstack instead of just egressing at tc.\n *\n * \tThere are three supported level settings at this time:\n *\n * \t* **BPF_CSUM_LEVEL_INC**: Increases skb->csum_level for skbs\n * \t  with CHECKSUM_UNNECESSARY.\n * \t* **BPF_CSUM_LEVEL_DEC**: Decreases skb->csum_level for skbs\n * \t  with CHECKSUM_UNNECESSARY.\n * \t* **BPF_CSUM_LEVEL_RESET**: Resets skb->csum_level to 0 and\n * \t  sets CHECKSUM_NONE to force checksum validation by the stack.\n * \t* **BPF_CSUM_LEVEL_QUERY**: No-op, returns the current\n * \t  skb->csum_level.\n *\n * Returns\n * \t0 on success, or a negative error in case of failure. In the\n * \tcase of **BPF_CSUM_LEVEL_QUERY**, the current skb->csum_level\n * \tis returned or the error code -EACCES in case the skb is not\n * \tsubject to CHECKSUM_UNNECESSARY.\n */\nstatic long (*bpf_csum_level)(struct __sk_buff *skb, __u64 level) = (void *) 135;\n\n/*\n * bpf_skc_to_tcp6_sock\n *\n * \tDynamically cast a *sk* pointer to a *tcp6_sock* pointer.\n *\n * Returns\n * \t*sk* if casting is valid, or **NULL** otherwise.\n */\nstatic struct tcp6_sock *(*bpf_skc_to_tcp6_sock)(void *sk) = (void *) 136;\n\n/*\n * bpf_skc_to_tcp_sock\n *\n * \tDynamically cast a *sk* pointer to a *tcp_sock* pointer.\n *\n * Returns\n * \t*sk* if casting is valid, or **NULL** otherwise.\n */\nstatic struct tcp_sock *(*bpf_skc_to_tcp_sock)(void *sk) = (void *) 137;\n\n/*\n * bpf_skc_to_tcp_timewait_sock\n *\n * \tDynamically cast a *sk* pointer to a *tcp_timewait_sock* pointer.\n *\n * Returns\n * \t*sk* if casting is valid, or **NULL** otherwise.\n */\nstatic struct tcp_timewait_sock *(*bpf_skc_to_tcp_timewait_sock)(void *sk) = (void *) 138;\n\n/*\n * bpf_skc_to_tcp_request_sock\n *\n * \tDynamically cast a *sk* pointer to a *tcp_request_sock* pointer.\n *\n * Returns\n * \t*sk* if casting is valid, or **NULL** otherwise.\n */\nstatic struct tcp_request_sock *(*bpf_skc_to_tcp_request_sock)(void *sk) = (void *) 139;\n\n/*\n * bpf_skc_to_udp6_sock\n *\n * \tDynamically cast a *sk* pointer to a *udp6_sock* pointer.\n *\n * Returns\n * \t*sk* if casting is valid, or **NULL** otherwise.\n */\nstatic struct udp6_sock *(*bpf_skc_to_udp6_sock)(void *sk) = (void *) 140;\n\n/*\n * bpf_get_task_stack\n *\n * \tReturn a user or a kernel stack in bpf program provided buffer.\n * \tTo achieve this, the helper needs *task*, which is a valid\n * \tpointer to **struct task_struct**. To store the stacktrace, the\n * \tbpf program provides *buf* with a nonnegative *size*.\n *\n * \tThe last argument, *flags*, holds the number of stack frames to\n * \tskip (from 0 to 255), masked with\n * \t**BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set\n * \tthe following flags:\n *\n * \t**BPF_F_USER_STACK**\n * \t\tCollect a user space stack instead of a kernel stack.\n * \t**BPF_F_USER_BUILD_ID**\n * \t\tCollect buildid+offset instead of ips for user stack,\n * \t\tonly valid if **BPF_F_USER_STACK** is also specified.\n *\n * \t**bpf_get_task_stack**\\ () can collect up to\n * \t**PERF_MAX_STACK_DEPTH** both kernel and user frames, subject\n * \tto sufficient large buffer size. Note that\n * \tthis limit can be controlled with the **sysctl** program, and\n * \tthat it should be manually increased in order to profile long\n * \tuser stacks (such as stacks for Java programs). To do so, use:\n *\n * \t::\n *\n * \t\t# sysctl kernel.perf_event_max_stack=<new value>\n *\n * Returns\n * \tA non-negative value equal to or less than *size* on success,\n * \tor a negative error in case of failure.\n */\nstatic long (*bpf_get_task_stack)(struct task_struct *task, void *buf, __u32 size, __u64 flags) = (void *) 141;\n\n/*\n * bpf_load_hdr_opt\n *\n * \tLoad header option.  Support reading a particular TCP header\n * \toption for bpf program (**BPF_PROG_TYPE_SOCK_OPS**).\n *\n * \tIf *flags* is 0, it will search the option from the\n * \t*skops*\\ **->skb_data**.  The comment in **struct bpf_sock_ops**\n * \thas details on what skb_data contains under different\n * \t*skops*\\ **->op**.\n *\n * \tThe first byte of the *searchby_res* specifies the\n * \tkind that it wants to search.\n *\n * \tIf the searching kind is an experimental kind\n * \t(i.e. 253 or 254 according to RFC6994).  It also\n * \tneeds to specify the \"magic\" which is either\n * \t2 bytes or 4 bytes.  It then also needs to\n * \tspecify the size of the magic by using\n * \tthe 2nd byte which is \"kind-length\" of a TCP\n * \theader option and the \"kind-length\" also\n * \tincludes the first 2 bytes \"kind\" and \"kind-length\"\n * \titself as a normal TCP header option also does.\n *\n * \tFor example, to search experimental kind 254 with\n * \t2 byte magic 0xeB9F, the searchby_res should be\n * \t[ 254, 4, 0xeB, 0x9F, 0, 0, .... 0 ].\n *\n * \tTo search for the standard window scale option (3),\n * \tthe *searchby_res* should be [ 3, 0, 0, .... 0 ].\n * \tNote, kind-length must be 0 for regular option.\n *\n * \tSearching for No-Op (0) and End-of-Option-List (1) are\n * \tnot supported.\n *\n * \t*len* must be at least 2 bytes which is the minimal size\n * \tof a header option.\n *\n * \tSupported flags:\n *\n * \t* **BPF_LOAD_HDR_OPT_TCP_SYN** to search from the\n * \t  saved_syn packet or the just-received syn packet.\n *\n *\n * Returns\n * \t> 0 when found, the header option is copied to *searchby_res*.\n * \tThe return value is the total length copied. On failure, a\n * \tnegative error code is returned:\n *\n * \t**-EINVAL** if a parameter is invalid.\n *\n * \t**-ENOMSG** if the option is not found.\n *\n * \t**-ENOENT** if no syn packet is available when\n * \t**BPF_LOAD_HDR_OPT_TCP_SYN** is used.\n *\n * \t**-ENOSPC** if there is not enough space.  Only *len* number of\n * \tbytes are copied.\n *\n * \t**-EFAULT** on failure to parse the header options in the\n * \tpacket.\n *\n * \t**-EPERM** if the helper cannot be used under the current\n * \t*skops*\\ **->op**.\n */\nstatic long (*bpf_load_hdr_opt)(struct bpf_sock_ops *skops, void *searchby_res, __u32 len, __u64 flags) = (void *) 142;\n\n/*\n * bpf_store_hdr_opt\n *\n * \tStore header option.  The data will be copied\n * \tfrom buffer *from* with length *len* to the TCP header.\n *\n * \tThe buffer *from* should have the whole option that\n * \tincludes the kind, kind-length, and the actual\n * \toption data.  The *len* must be at least kind-length\n * \tlong.  The kind-length does not have to be 4 byte\n * \taligned.  The kernel will take care of the padding\n * \tand setting the 4 bytes aligned value to th->doff.\n *\n * \tThis helper will check for duplicated option\n * \tby searching the same option in the outgoing skb.\n *\n * \tThis helper can only be called during\n * \t**BPF_SOCK_OPS_WRITE_HDR_OPT_CB**.\n *\n *\n * Returns\n * \t0 on success, or negative error in case of failure:\n *\n * \t**-EINVAL** If param is invalid.\n *\n * \t**-ENOSPC** if there is not enough space in the header.\n * \tNothing has been written\n *\n * \t**-EEXIST** if the option already exists.\n *\n * \t**-EFAULT** on failrue to parse the existing header options.\n *\n * \t**-EPERM** if the helper cannot be used under the current\n * \t*skops*\\ **->op**.\n */\nstatic long (*bpf_store_hdr_opt)(struct bpf_sock_ops *skops, const void *from, __u32 len, __u64 flags) = (void *) 143;\n\n/*\n * bpf_reserve_hdr_opt\n *\n * \tReserve *len* bytes for the bpf header option.  The\n * \tspace will be used by **bpf_store_hdr_opt**\\ () later in\n * \t**BPF_SOCK_OPS_WRITE_HDR_OPT_CB**.\n *\n * \tIf **bpf_reserve_hdr_opt**\\ () is called multiple times,\n * \tthe total number of bytes will be reserved.\n *\n * \tThis helper can only be called during\n * \t**BPF_SOCK_OPS_HDR_OPT_LEN_CB**.\n *\n *\n * Returns\n * \t0 on success, or negative error in case of failure:\n *\n * \t**-EINVAL** if a parameter is invalid.\n *\n * \t**-ENOSPC** if there is not enough space in the header.\n *\n * \t**-EPERM** if the helper cannot be used under the current\n * \t*skops*\\ **->op**.\n */\nstatic long (*bpf_reserve_hdr_opt)(struct bpf_sock_ops *skops, __u32 len, __u64 flags) = (void *) 144;\n\n/*\n * bpf_inode_storage_get\n *\n * \tGet a bpf_local_storage from an *inode*.\n *\n * \tLogically, it could be thought of as getting the value from\n * \ta *map* with *inode* as the **key**.  From this\n * \tperspective,  the usage is not much different from\n * \t**bpf_map_lookup_elem**\\ (*map*, **&**\\ *inode*) except this\n * \thelper enforces the key must be an inode and the map must also\n * \tbe a **BPF_MAP_TYPE_INODE_STORAGE**.\n *\n * \tUnderneath, the value is stored locally at *inode* instead of\n * \tthe *map*.  The *map* is used as the bpf-local-storage\n * \t\"type\". The bpf-local-storage \"type\" (i.e. the *map*) is\n * \tsearched against all bpf_local_storage residing at *inode*.\n *\n * \tAn optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be\n * \tused such that a new bpf_local_storage will be\n * \tcreated if one does not exist.  *value* can be used\n * \ttogether with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify\n * \tthe initial value of a bpf_local_storage.  If *value* is\n * \t**NULL**, the new bpf_local_storage will be zero initialized.\n *\n * Returns\n * \tA bpf_local_storage pointer is returned on success.\n *\n * \t**NULL** if not found or there was an error in adding\n * \ta new bpf_local_storage.\n */\nstatic void *(*bpf_inode_storage_get)(void *map, void *inode, void *value, __u64 flags) = (void *) 145;\n\n/*\n * bpf_inode_storage_delete\n *\n * \tDelete a bpf_local_storage from an *inode*.\n *\n * Returns\n * \t0 on success.\n *\n * \t**-ENOENT** if the bpf_local_storage cannot be found.\n */\nstatic int (*bpf_inode_storage_delete)(void *map, void *inode) = (void *) 146;\n\n/*\n * bpf_d_path\n *\n * \tReturn full path for given **struct path** object, which\n * \tneeds to be the kernel BTF *path* object. The path is\n * \treturned in the provided buffer *buf* of size *sz* and\n * \tis zero terminated.\n *\n *\n * Returns\n * \tOn success, the strictly positive length of the string,\n * \tincluding the trailing NUL character. On error, a negative\n * \tvalue.\n */\nstatic long (*bpf_d_path)(struct path *path, char *buf, __u32 sz) = (void *) 147;\n\n/*\n * bpf_copy_from_user\n *\n * \tRead *size* bytes from user space address *user_ptr* and store\n * \tthe data in *dst*. This is a wrapper of **copy_from_user**\\ ().\n *\n * Returns\n * \t0 on success, or a negative error in case of failure.\n */\nstatic long (*bpf_copy_from_user)(void *dst, __u32 size, const void *user_ptr) = (void *) 148;\n\n/*\n * bpf_snprintf_btf\n *\n * \tUse BTF to store a string representation of *ptr*->ptr in *str*,\n * \tusing *ptr*->type_id.  This value should specify the type\n * \tthat *ptr*->ptr points to. LLVM __builtin_btf_type_id(type, 1)\n * \tcan be used to look up vmlinux BTF type ids. Traversing the\n * \tdata structure using BTF, the type information and values are\n * \tstored in the first *str_size* - 1 bytes of *str*.  Safe copy of\n * \tthe pointer data is carried out to avoid kernel crashes during\n * \toperation.  Smaller types can use string space on the stack;\n * \tlarger programs can use map data to store the string\n * \trepresentation.\n *\n * \tThe string can be subsequently shared with userspace via\n * \tbpf_perf_event_output() or ring buffer interfaces.\n * \tbpf_trace_printk() is to be avoided as it places too small\n * \ta limit on string size to be useful.\n *\n * \t*flags* is a combination of\n *\n * \t**BTF_F_COMPACT**\n * \t\tno formatting around type information\n * \t**BTF_F_NONAME**\n * \t\tno struct/union member names/types\n * \t**BTF_F_PTR_RAW**\n * \t\tshow raw (unobfuscated) pointer values;\n * \t\tequivalent to printk specifier %px.\n * \t**BTF_F_ZERO**\n * \t\tshow zero-valued struct/union members; they\n * \t\tare not displayed by default\n *\n *\n * Returns\n * \tThe number of bytes that were written (or would have been\n * \twritten if output had to be truncated due to string size),\n * \tor a negative error in cases of failure.\n */\nstatic long (*bpf_snprintf_btf)(char *str, __u32 str_size, struct btf_ptr *ptr, __u32 btf_ptr_size, __u64 flags) = (void *) 149;\n\n/*\n * bpf_seq_printf_btf\n *\n * \tUse BTF to write to seq_write a string representation of\n * \t*ptr*->ptr, using *ptr*->type_id as per bpf_snprintf_btf().\n * \t*flags* are identical to those used for bpf_snprintf_btf.\n *\n * Returns\n * \t0 on success or a negative error in case of failure.\n */\nstatic long (*bpf_seq_printf_btf)(struct seq_file *m, struct btf_ptr *ptr, __u32 ptr_size, __u64 flags) = (void *) 150;\n\n/*\n * bpf_skb_cgroup_classid\n *\n * \tSee **bpf_get_cgroup_classid**\\ () for the main description.\n * \tThis helper differs from **bpf_get_cgroup_classid**\\ () in that\n * \tthe cgroup v1 net_cls class is retrieved only from the *skb*'s\n * \tassociated socket instead of the current process.\n *\n * Returns\n * \tThe id is returned or 0 in case the id could not be retrieved.\n */\nstatic __u64 (*bpf_skb_cgroup_classid)(struct __sk_buff *skb) = (void *) 151;\n\n/*\n * bpf_redirect_neigh\n *\n * \tRedirect the packet to another net device of index *ifindex*\n * \tand fill in L2 addresses from neighboring subsystem. This helper\n * \tis somewhat similar to **bpf_redirect**\\ (), except that it\n * \tpopulates L2 addresses as well, meaning, internally, the helper\n * \trelies on the neighbor lookup for the L2 address of the nexthop.\n *\n * \tThe helper will perform a FIB lookup based on the skb's\n * \tnetworking header to get the address of the next hop, unless\n * \tthis is supplied by the caller in the *params* argument. The\n * \t*plen* argument indicates the len of *params* and should be set\n * \tto 0 if *params* is NULL.\n *\n * \tThe *flags* argument is reserved and must be 0. The helper is\n * \tcurrently only supported for tc BPF program types, and enabled\n * \tfor IPv4 and IPv6 protocols.\n *\n * Returns\n * \tThe helper returns **TC_ACT_REDIRECT** on success or\n * \t**TC_ACT_SHOT** on error.\n */\nstatic long (*bpf_redirect_neigh)(__u32 ifindex, struct bpf_redir_neigh *params, int plen, __u64 flags) = (void *) 152;\n\n/*\n * bpf_per_cpu_ptr\n *\n * \tTake a pointer to a percpu ksym, *percpu_ptr*, and return a\n * \tpointer to the percpu kernel variable on *cpu*. A ksym is an\n * \textern variable decorated with '__ksym'. For ksym, there is a\n * \tglobal var (either static or global) defined of the same name\n * \tin the kernel. The ksym is percpu if the global var is percpu.\n * \tThe returned pointer points to the global percpu var on *cpu*.\n *\n * \tbpf_per_cpu_ptr() has the same semantic as per_cpu_ptr() in the\n * \tkernel, except that bpf_per_cpu_ptr() may return NULL. This\n * \thappens if *cpu* is larger than nr_cpu_ids. The caller of\n * \tbpf_per_cpu_ptr() must check the returned value.\n *\n * Returns\n * \tA pointer pointing to the kernel percpu variable on *cpu*, or\n * \tNULL, if *cpu* is invalid.\n */\nstatic void *(*bpf_per_cpu_ptr)(const void *percpu_ptr, __u32 cpu) = (void *) 153;\n\n/*\n * bpf_this_cpu_ptr\n *\n * \tTake a pointer to a percpu ksym, *percpu_ptr*, and return a\n * \tpointer to the percpu kernel variable on this cpu. See the\n * \tdescription of 'ksym' in **bpf_per_cpu_ptr**\\ ().\n *\n * \tbpf_this_cpu_ptr() has the same semantic as this_cpu_ptr() in\n * \tthe kernel. Different from **bpf_per_cpu_ptr**\\ (), it would\n * \tnever return NULL.\n *\n * Returns\n * \tA pointer pointing to the kernel percpu variable on this cpu.\n */\nstatic void *(*bpf_this_cpu_ptr)(const void *percpu_ptr) = (void *) 154;\n\n/*\n * bpf_redirect_peer\n *\n * \tRedirect the packet to another net device of index *ifindex*.\n * \tThis helper is somewhat similar to **bpf_redirect**\\ (), except\n * \tthat the redirection happens to the *ifindex*' peer device and\n * \tthe netns switch takes place from ingress to ingress without\n * \tgoing through the CPU's backlog queue.\n *\n * \tThe *flags* argument is reserved and must be 0. The helper is\n * \tcurrently only supported for tc BPF program types at the ingress\n * \thook and for veth device types. The peer device must reside in a\n * \tdifferent network namespace.\n *\n * Returns\n * \tThe helper returns **TC_ACT_REDIRECT** on success or\n * \t**TC_ACT_SHOT** on error.\n */\nstatic long (*bpf_redirect_peer)(__u32 ifindex, __u64 flags) = (void *) 155;\n\n/*\n * bpf_task_storage_get\n *\n * \tGet a bpf_local_storage from the *task*.\n *\n * \tLogically, it could be thought of as getting the value from\n * \ta *map* with *task* as the **key**.  From this\n * \tperspective,  the usage is not much different from\n * \t**bpf_map_lookup_elem**\\ (*map*, **&**\\ *task*) except this\n * \thelper enforces the key must be an task_struct and the map must also\n * \tbe a **BPF_MAP_TYPE_TASK_STORAGE**.\n *\n * \tUnderneath, the value is stored locally at *task* instead of\n * \tthe *map*.  The *map* is used as the bpf-local-storage\n * \t\"type\". The bpf-local-storage \"type\" (i.e. the *map*) is\n * \tsearched against all bpf_local_storage residing at *task*.\n *\n * \tAn optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be\n * \tused such that a new bpf_local_storage will be\n * \tcreated if one does not exist.  *value* can be used\n * \ttogether with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify\n * \tthe initial value of a bpf_local_storage.  If *value* is\n * \t**NULL**, the new bpf_local_storage will be zero initialized.\n *\n * Returns\n * \tA bpf_local_storage pointer is returned on success.\n *\n * \t**NULL** if not found or there was an error in adding\n * \ta new bpf_local_storage.\n */\nstatic void *(*bpf_task_storage_get)(void *map, struct task_struct *task, void *value, __u64 flags) = (void *) 156;\n\n/*\n * bpf_task_storage_delete\n *\n * \tDelete a bpf_local_storage from a *task*.\n *\n * Returns\n * \t0 on success.\n *\n * \t**-ENOENT** if the bpf_local_storage cannot be found.\n */\nstatic long (*bpf_task_storage_delete)(void *map, struct task_struct *task) = (void *) 157;\n\n/*\n * bpf_get_current_task_btf\n *\n * \tReturn a BTF pointer to the \"current\" task.\n * \tThis pointer can also be used in helpers that accept an\n * \t*ARG_PTR_TO_BTF_ID* of type *task_struct*.\n *\n * Returns\n * \tPointer to the current task.\n */\nstatic struct task_struct *(*bpf_get_current_task_btf)(void) = (void *) 158;\n\n/*\n * bpf_bprm_opts_set\n *\n * \tSet or clear certain options on *bprm*:\n *\n * \t**BPF_F_BPRM_SECUREEXEC** Set the secureexec bit\n * \twhich sets the **AT_SECURE** auxv for glibc. The bit\n * \tis cleared if the flag is not specified.\n *\n * Returns\n * \t**-EINVAL** if invalid *flags* are passed, zero otherwise.\n */\nstatic long (*bpf_bprm_opts_set)(struct linux_binprm *bprm, __u64 flags) = (void *) 159;\n\n/*\n * bpf_ktime_get_coarse_ns\n *\n * \tReturn a coarse-grained version of the time elapsed since\n * \tsystem boot, in nanoseconds. Does not include time the system\n * \twas suspended.\n *\n * \tSee: **clock_gettime**\\ (**CLOCK_MONOTONIC_COARSE**)\n *\n * Returns\n * \tCurrent *ktime*.\n */\nstatic __u64 (*bpf_ktime_get_coarse_ns)(void) = (void *) 160;\n\n/*\n * bpf_ima_inode_hash\n *\n * \tReturns the stored IMA hash of the *inode* (if it's avaialable).\n * \tIf the hash is larger than *size*, then only *size*\n * \tbytes will be copied to *dst*\n *\n * Returns\n * \tThe **hash_algo** is returned on success,\n * \t**-EOPNOTSUP** if IMA is disabled or **-EINVAL** if\n * \tinvalid arguments are passed.\n */\nstatic long (*bpf_ima_inode_hash)(struct inode *inode, void *dst, __u32 size) = (void *) 161;\n\n/*\n * bpf_sock_from_file\n *\n * \tIf the given file represents a socket, returns the associated\n * \tsocket.\n *\n * Returns\n * \tA pointer to a struct socket on success or NULL if the file is\n * \tnot a socket.\n */\nstatic struct socket *(*bpf_sock_from_file)(struct file *file) = (void *) 162;\n\n/*\n * bpf_check_mtu\n *\n * \tCheck packet size against exceeding MTU of net device (based\n * \ton *ifindex*).  This helper will likely be used in combination\n * \twith helpers that adjust/change the packet size.\n *\n * \tThe argument *len_diff* can be used for querying with a planned\n * \tsize change. This allows to check MTU prior to changing packet\n * \tctx. Providing an *len_diff* adjustment that is larger than the\n * \tactual packet size (resulting in negative packet size) will in\n * \tprinciple not exceed the MTU, why it is not considered a\n * \tfailure.  Other BPF-helpers are needed for performing the\n * \tplanned size change, why the responsibility for catch a negative\n * \tpacket size belong in those helpers.\n *\n * \tSpecifying *ifindex* zero means the MTU check is performed\n * \tagainst the current net device.  This is practical if this isn't\n * \tused prior to redirect.\n *\n * \tOn input *mtu_len* must be a valid pointer, else verifier will\n * \treject BPF program.  If the value *mtu_len* is initialized to\n * \tzero then the ctx packet size is use.  When value *mtu_len* is\n * \tprovided as input this specify the L3 length that the MTU check\n * \tis done against. Remember XDP and TC length operate at L2, but\n * \tthis value is L3 as this correlate to MTU and IP-header tot_len\n * \tvalues which are L3 (similar behavior as bpf_fib_lookup).\n *\n * \tThe Linux kernel route table can configure MTUs on a more\n * \tspecific per route level, which is not provided by this helper.\n * \tFor route level MTU checks use the **bpf_fib_lookup**\\ ()\n * \thelper.\n *\n * \t*ctx* is either **struct xdp_md** for XDP programs or\n * \t**struct sk_buff** for tc cls_act programs.\n *\n * \tThe *flags* argument can be a combination of one or more of the\n * \tfollowing values:\n *\n * \t**BPF_MTU_CHK_SEGS**\n * \t\tThis flag will only works for *ctx* **struct sk_buff**.\n * \t\tIf packet context contains extra packet segment buffers\n * \t\t(often knows as GSO skb), then MTU check is harder to\n * \t\tcheck at this point, because in transmit path it is\n * \t\tpossible for the skb packet to get re-segmented\n * \t\t(depending on net device features).  This could still be\n * \t\ta MTU violation, so this flag enables performing MTU\n * \t\tcheck against segments, with a different violation\n * \t\treturn code to tell it apart. Check cannot use len_diff.\n *\n * \tOn return *mtu_len* pointer contains the MTU value of the net\n * \tdevice.  Remember the net device configured MTU is the L3 size,\n * \twhich is returned here and XDP and TC length operate at L2.\n * \tHelper take this into account for you, but remember when using\n * \tMTU value in your BPF-code.\n *\n *\n * Returns\n * \t* 0 on success, and populate MTU value in *mtu_len* pointer.\n *\n * \t* < 0 if any input argument is invalid (*mtu_len* not updated)\n *\n * \tMTU violations return positive values, but also populate MTU\n * \tvalue in *mtu_len* pointer, as this can be needed for\n * \timplementing PMTU handing:\n *\n * \t* **BPF_MTU_CHK_RET_FRAG_NEEDED**\n * \t* **BPF_MTU_CHK_RET_SEGS_TOOBIG**\n */\nstatic long (*bpf_check_mtu)(void *ctx, __u32 ifindex, __u32 *mtu_len, __s32 len_diff, __u64 flags) = (void *) 163;\n\n/*\n * bpf_for_each_map_elem\n *\n * \tFor each element in **map**, call **callback_fn** function with\n * \t**map**, **callback_ctx** and other map-specific parameters.\n * \tThe **callback_fn** should be a static function and\n * \tthe **callback_ctx** should be a pointer to the stack.\n * \tThe **flags** is used to control certain aspects of the helper.\n * \tCurrently, the **flags** must be 0.\n *\n * \tThe following are a list of supported map types and their\n * \trespective expected callback signatures:\n *\n * \tBPF_MAP_TYPE_HASH, BPF_MAP_TYPE_PERCPU_HASH,\n * \tBPF_MAP_TYPE_LRU_HASH, BPF_MAP_TYPE_LRU_PERCPU_HASH,\n * \tBPF_MAP_TYPE_ARRAY, BPF_MAP_TYPE_PERCPU_ARRAY\n *\n * \tlong (\\*callback_fn)(struct bpf_map \\*map, const void \\*key, void \\*value, void \\*ctx);\n *\n * \tFor per_cpu maps, the map_value is the value on the cpu where the\n * \tbpf_prog is running.\n *\n * \tIf **callback_fn** return 0, the helper will continue to the next\n * \telement. If return value is 1, the helper will skip the rest of\n * \telements and return. Other return values are not used now.\n *\n *\n * Returns\n * \tThe number of traversed map elements for success, **-EINVAL** for\n * \tinvalid **flags**.\n */\nstatic long (*bpf_for_each_map_elem)(void *map, void *callback_fn, void *callback_ctx, __u64 flags) = (void *) 164;\n\n/*\n * bpf_snprintf\n *\n * \tOutputs a string into the **str** buffer of size **str_size**\n * \tbased on a format string stored in a read-only map pointed by\n * \t**fmt**.\n *\n * \tEach format specifier in **fmt** corresponds to one u64 element\n * \tin the **data** array. For strings and pointers where pointees\n * \tare accessed, only the pointer values are stored in the *data*\n * \tarray. The *data_len* is the size of *data* in bytes - must be\n * \ta multiple of 8.\n *\n * \tFormats **%s** and **%p{i,I}{4,6}** require to read kernel\n * \tmemory. Reading kernel memory may fail due to either invalid\n * \taddress or valid address but requiring a major memory fault. If\n * \treading kernel memory fails, the string for **%s** will be an\n * \tempty string, and the ip address for **%p{i,I}{4,6}** will be 0.\n * \tNot returning error to bpf program is consistent with what\n * \t**bpf_trace_printk**\\ () does for now.\n *\n *\n * Returns\n * \tThe strictly positive length of the formatted string, including\n * \tthe trailing zero character. If the return value is greater than\n * \t**str_size**, **str** contains a truncated string, guaranteed to\n * \tbe zero-terminated except when **str_size** is 0.\n *\n * \tOr **-EBUSY** if the per-CPU memory copy buffer is busy.\n */\nstatic long (*bpf_snprintf)(char *str, __u32 str_size, const char *fmt, __u64 *data, __u32 data_len) = (void *) 165;\n\n/*\n * bpf_sys_bpf\n *\n * \tExecute bpf syscall with given arguments.\n *\n * Returns\n * \tA syscall result.\n */\nstatic long (*bpf_sys_bpf)(__u32 cmd, void *attr, __u32 attr_size) = (void *) 166;\n\n/*\n * bpf_btf_find_by_name_kind\n *\n * \tFind BTF type with given name and kind in vmlinux BTF or in module's BTFs.\n *\n * Returns\n * \tReturns btf_id and btf_obj_fd in lower and upper 32 bits.\n */\nstatic long (*bpf_btf_find_by_name_kind)(char *name, int name_sz, __u32 kind, int flags) = (void *) 167;\n\n/*\n * bpf_sys_close\n *\n * \tExecute close syscall for given FD.\n *\n * Returns\n * \tA syscall result.\n */\nstatic long (*bpf_sys_close)(__u32 fd) = (void *) 168;\n\n/*\n * bpf_timer_init\n *\n * \tInitialize the timer.\n * \tFirst 4 bits of *flags* specify clockid.\n * \tOnly CLOCK_MONOTONIC, CLOCK_REALTIME, CLOCK_BOOTTIME are allowed.\n * \tAll other bits of *flags* are reserved.\n * \tThe verifier will reject the program if *timer* is not from\n * \tthe same *map*.\n *\n * Returns\n * \t0 on success.\n * \t**-EBUSY** if *timer* is already initialized.\n * \t**-EINVAL** if invalid *flags* are passed.\n * \t**-EPERM** if *timer* is in a map that doesn't have any user references.\n * \tThe user space should either hold a file descriptor to a map with timers\n * \tor pin such map in bpffs. When map is unpinned or file descriptor is\n * \tclosed all timers in the map will be cancelled and freed.\n */\nstatic long (*bpf_timer_init)(struct bpf_timer *timer, void *map, __u64 flags) = (void *) 169;\n\n/*\n * bpf_timer_set_callback\n *\n * \tConfigure the timer to call *callback_fn* static function.\n *\n * Returns\n * \t0 on success.\n * \t**-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier.\n * \t**-EPERM** if *timer* is in a map that doesn't have any user references.\n * \tThe user space should either hold a file descriptor to a map with timers\n * \tor pin such map in bpffs. When map is unpinned or file descriptor is\n * \tclosed all timers in the map will be cancelled and freed.\n */\nstatic long (*bpf_timer_set_callback)(struct bpf_timer *timer, void *callback_fn) = (void *) 170;\n\n/*\n * bpf_timer_start\n *\n * \tSet timer expiration N nanoseconds from the current time. The\n * \tconfigured callback will be invoked in soft irq context on some cpu\n * \tand will not repeat unless another bpf_timer_start() is made.\n * \tIn such case the next invocation can migrate to a different cpu.\n * \tSince struct bpf_timer is a field inside map element the map\n * \towns the timer. The bpf_timer_set_callback() will increment refcnt\n * \tof BPF program to make sure that callback_fn code stays valid.\n * \tWhen user space reference to a map reaches zero all timers\n * \tin a map are cancelled and corresponding program's refcnts are\n * \tdecremented. This is done to make sure that Ctrl-C of a user\n * \tprocess doesn't leave any timers running. If map is pinned in\n * \tbpffs the callback_fn can re-arm itself indefinitely.\n * \tbpf_map_update/delete_elem() helpers and user space sys_bpf commands\n * \tcancel and free the timer in the given map element.\n * \tThe map can contain timers that invoke callback_fn-s from different\n * \tprograms. The same callback_fn can serve different timers from\n * \tdifferent maps if key/value layout matches across maps.\n * \tEvery bpf_timer_set_callback() can have different callback_fn.\n *\n *\n * Returns\n * \t0 on success.\n * \t**-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier\n * \tor invalid *flags* are passed.\n */\nstatic long (*bpf_timer_start)(struct bpf_timer *timer, __u64 nsecs, __u64 flags) = (void *) 171;\n\n/*\n * bpf_timer_cancel\n *\n * \tCancel the timer and wait for callback_fn to finish if it was running.\n *\n * Returns\n * \t0 if the timer was not active.\n * \t1 if the timer was active.\n * \t**-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier.\n * \t**-EDEADLK** if callback_fn tried to call bpf_timer_cancel() on its\n * \town timer which would have led to a deadlock otherwise.\n */\nstatic long (*bpf_timer_cancel)(struct bpf_timer *timer) = (void *) 172;\n\n/*\n * bpf_get_func_ip\n *\n * \tGet address of the traced function (for tracing and kprobe programs).\n *\n * Returns\n * \tAddress of the traced function.\n */\nstatic __u64 (*bpf_get_func_ip)(void *ctx) = (void *) 173;\n\n/*\n * bpf_get_attach_cookie\n *\n * \tGet bpf_cookie value provided (optionally) during the program\n * \tattachment. It might be different for each individual\n * \tattachment, even if BPF program itself is the same.\n * \tExpects BPF program context *ctx* as a first argument.\n *\n * \tSupported for the following program types:\n * \t\t- kprobe/uprobe;\n * \t\t- tracepoint;\n * \t\t- perf_event.\n *\n * Returns\n * \tValue specified by user at BPF link creation/attachment time\n * \tor 0, if it was not specified.\n */\nstatic __u64 (*bpf_get_attach_cookie)(void *ctx) = (void *) 174;\n\n/*\n * bpf_task_pt_regs\n *\n * \tGet the struct pt_regs associated with **task**.\n *\n * Returns\n * \tA pointer to struct pt_regs.\n */\nstatic long (*bpf_task_pt_regs)(struct task_struct *task) = (void *) 175;\n\n/*\n * bpf_get_branch_snapshot\n *\n * \tGet branch trace from hardware engines like Intel LBR. The\n * \thardware engine is stopped shortly after the helper is\n * \tcalled. Therefore, the user need to filter branch entries\n * \tbased on the actual use case. To capture branch trace\n * \tbefore the trigger point of the BPF program, the helper\n * \tshould be called at the beginning of the BPF program.\n *\n * \tThe data is stored as struct perf_branch_entry into output\n * \tbuffer *entries*. *size* is the size of *entries* in bytes.\n * \t*flags* is reserved for now and must be zero.\n *\n *\n * Returns\n * \tOn success, number of bytes written to *buf*. On error, a\n * \tnegative value.\n *\n * \t**-EINVAL** if *flags* is not zero.\n *\n * \t**-ENOENT** if architecture does not support branch records.\n */\nstatic long (*bpf_get_branch_snapshot)(void *entries, __u32 size, __u64 flags) = (void *) 176;\n\n/*\n * bpf_trace_vprintk\n *\n * \tBehaves like **bpf_trace_printk**\\ () helper, but takes an array of u64\n * \tto format and can handle more format args as a result.\n *\n * \tArguments are to be used as in **bpf_seq_printf**\\ () helper.\n *\n * Returns\n * \tThe number of bytes written to the buffer, or a negative error\n * \tin case of failure.\n */\nstatic long (*bpf_trace_vprintk)(const char *fmt, __u32 fmt_size, const void *data, __u32 data_len) = (void *) 177;\n\n/*\n * bpf_skc_to_unix_sock\n *\n * \tDynamically cast a *sk* pointer to a *unix_sock* pointer.\n *\n * Returns\n * \t*sk* if casting is valid, or **NULL** otherwise.\n */\nstatic struct unix_sock *(*bpf_skc_to_unix_sock)(void *sk) = (void *) 178;\n\n/*\n * bpf_kallsyms_lookup_name\n *\n * \tGet the address of a kernel symbol, returned in *res*. *res* is\n * \tset to 0 if the symbol is not found.\n *\n * Returns\n * \tOn success, zero. On error, a negative value.\n *\n * \t**-EINVAL** if *flags* is not zero.\n *\n * \t**-EINVAL** if string *name* is not the same size as *name_sz*.\n *\n * \t**-ENOENT** if symbol is not found.\n *\n * \t**-EPERM** if caller does not have permission to obtain kernel address.\n */\nstatic long (*bpf_kallsyms_lookup_name)(const char *name, int name_sz, int flags, __u64 *res) = (void *) 179;\n\n/*\n * bpf_find_vma\n *\n * \tFind vma of *task* that contains *addr*, call *callback_fn*\n * \tfunction with *task*, *vma*, and *callback_ctx*.\n * \tThe *callback_fn* should be a static function and\n * \tthe *callback_ctx* should be a pointer to the stack.\n * \tThe *flags* is used to control certain aspects of the helper.\n * \tCurrently, the *flags* must be 0.\n *\n * \tThe expected callback signature is\n *\n * \tlong (\\*callback_fn)(struct task_struct \\*task, struct vm_area_struct \\*vma, void \\*callback_ctx);\n *\n *\n * Returns\n * \t0 on success.\n * \t**-ENOENT** if *task->mm* is NULL, or no vma contains *addr*.\n * \t**-EBUSY** if failed to try lock mmap_lock.\n * \t**-EINVAL** for invalid **flags**.\n */\nstatic long (*bpf_find_vma)(struct task_struct *task, __u64 addr, void *callback_fn, void *callback_ctx, __u64 flags) = (void *) 180;\n\n\n"
  },
  {
    "path": "examples/headers/bpf_helpers.h",
    "content": "/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */\n#ifndef __BPF_HELPERS__\n#define __BPF_HELPERS__\n\n/*\n * Note that bpf programs need to include either\n * vmlinux.h (auto-generated from BTF) or linux/types.h\n * in advance since bpf_helper_defs.h uses such types\n * as __u64.\n */\n#include \"bpf_helper_defs.h\"\n\n#define __uint(name, val) int (*name)[val]\n#define __type(name, val) typeof(val) *name\n#define __array(name, val) typeof(val) *name[]\n\n/*\n * Helper macro to place programs, maps, license in\n * different sections in elf_bpf file. Section names\n * are interpreted by libbpf depending on the context (BPF programs, BPF maps,\n * extern variables, etc).\n * To allow use of SEC() with externs (e.g., for extern .maps declarations),\n * make sure __attribute__((unused)) doesn't trigger compilation warning.\n */\n#define SEC(name) \\\n\t_Pragma(\"GCC diagnostic push\")\t\t\t\t\t    \\\n\t_Pragma(\"GCC diagnostic ignored \\\"-Wignored-attributes\\\"\")\t    \\\n\t__attribute__((section(name), used))\t\t\t\t    \\\n\t_Pragma(\"GCC diagnostic pop\")\t\t\t\t\t    \\\n\n/* Avoid 'linux/stddef.h' definition of '__always_inline'. */\n#undef __always_inline\n#define __always_inline inline __attribute__((always_inline))\n\n#ifndef __noinline\n#define __noinline __attribute__((noinline))\n#endif\n#ifndef __weak\n#define __weak __attribute__((weak))\n#endif\n\n/*\n * Use __hidden attribute to mark a non-static BPF subprogram effectively\n * static for BPF verifier's verification algorithm purposes, allowing more\n * extensive and permissive BPF verification process, taking into account\n * subprogram's caller context.\n */\n#define __hidden __attribute__((visibility(\"hidden\")))\n\n/* When utilizing vmlinux.h with BPF CO-RE, user BPF programs can't include\n * any system-level headers (such as stddef.h, linux/version.h, etc), and\n * commonly-used macros like NULL and KERNEL_VERSION aren't available through\n * vmlinux.h. This just adds unnecessary hurdles and forces users to re-define\n * them on their own. So as a convenience, provide such definitions here.\n */\n#ifndef NULL\n#define NULL ((void *)0)\n#endif\n\n#ifndef KERNEL_VERSION\n#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + ((c) > 255 ? 255 : (c)))\n#endif\n\n/*\n * Helper macros to manipulate data structures\n */\n#ifndef offsetof\n#define offsetof(TYPE, MEMBER)\t((unsigned long)&((TYPE *)0)->MEMBER)\n#endif\n#ifndef container_of\n#define container_of(ptr, type, member)\t\t\t\t\\\n\t({\t\t\t\t\t\t\t\\\n\t\tvoid *__mptr = (void *)(ptr);\t\t\t\\\n\t\t((type *)(__mptr - offsetof(type, member)));\t\\\n\t})\n#endif\n\n/*\n * Helper macro to throw a compilation error if __bpf_unreachable() gets\n * built into the resulting code. This works given BPF back end does not\n * implement __builtin_trap(). This is useful to assert that certain paths\n * of the program code are never used and hence eliminated by the compiler.\n *\n * For example, consider a switch statement that covers known cases used by\n * the program. __bpf_unreachable() can then reside in the default case. If\n * the program gets extended such that a case is not covered in the switch\n * statement, then it will throw a build error due to the default case not\n * being compiled out.\n */\n#ifndef __bpf_unreachable\n# define __bpf_unreachable()\t__builtin_trap()\n#endif\n\n/*\n * Helper function to perform a tail call with a constant/immediate map slot.\n */\n#if __clang_major__ >= 8 && defined(__bpf__)\nstatic __always_inline void\nbpf_tail_call_static(void *ctx, const void *map, const __u32 slot)\n{\n\tif (!__builtin_constant_p(slot))\n\t\t__bpf_unreachable();\n\n\t/*\n\t * Provide a hard guarantee that LLVM won't optimize setting r2 (map\n\t * pointer) and r3 (constant map index) from _different paths_ ending\n\t * up at the _same_ call insn as otherwise we won't be able to use the\n\t * jmpq/nopl retpoline-free patching by the x86-64 JIT in the kernel\n\t * given they mismatch. See also d2e4c1e6c294 (\"bpf: Constant map key\n\t * tracking for prog array pokes\") for details on verifier tracking.\n\t *\n\t * Note on clobber list: we need to stay in-line with BPF calling\n\t * convention, so even if we don't end up using r0, r4, r5, we need\n\t * to mark them as clobber so that LLVM doesn't end up using them\n\t * before / after the call.\n\t */\n\tasm volatile(\"r1 = %[ctx]\\n\\t\"\n\t\t     \"r2 = %[map]\\n\\t\"\n\t\t     \"r3 = %[slot]\\n\\t\"\n\t\t     \"call 12\"\n\t\t     :: [ctx]\"r\"(ctx), [map]\"r\"(map), [slot]\"i\"(slot)\n\t\t     : \"r0\", \"r1\", \"r2\", \"r3\", \"r4\", \"r5\");\n}\n#endif\n\n/*\n * Helper structure used by eBPF C program\n * to describe BPF map attributes to libbpf loader\n */\nstruct bpf_map_def {\n\tunsigned int type;\n\tunsigned int key_size;\n\tunsigned int value_size;\n\tunsigned int max_entries;\n\tunsigned int map_flags;\n};\n\nenum libbpf_pin_type {\n\tLIBBPF_PIN_NONE,\n\t/* PIN_BY_NAME: pin maps by name (in /sys/fs/bpf by default) */\n\tLIBBPF_PIN_BY_NAME,\n};\n\nenum libbpf_tristate {\n\tTRI_NO = 0,\n\tTRI_YES = 1,\n\tTRI_MODULE = 2,\n};\n\n#define __kconfig __attribute__((section(\".kconfig\")))\n#define __ksym __attribute__((section(\".ksyms\")))\n\n#ifndef ___bpf_concat\n#define ___bpf_concat(a, b) a ## b\n#endif\n#ifndef ___bpf_apply\n#define ___bpf_apply(fn, n) ___bpf_concat(fn, n)\n#endif\n#ifndef ___bpf_nth\n#define ___bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, N, ...) N\n#endif\n#ifndef ___bpf_narg\n#define ___bpf_narg(...) \\\n\t___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)\n#endif\n\n#define ___bpf_fill0(arr, p, x) do {} while (0)\n#define ___bpf_fill1(arr, p, x) arr[p] = x\n#define ___bpf_fill2(arr, p, x, args...) arr[p] = x; ___bpf_fill1(arr, p + 1, args)\n#define ___bpf_fill3(arr, p, x, args...) arr[p] = x; ___bpf_fill2(arr, p + 1, args)\n#define ___bpf_fill4(arr, p, x, args...) arr[p] = x; ___bpf_fill3(arr, p + 1, args)\n#define ___bpf_fill5(arr, p, x, args...) arr[p] = x; ___bpf_fill4(arr, p + 1, args)\n#define ___bpf_fill6(arr, p, x, args...) arr[p] = x; ___bpf_fill5(arr, p + 1, args)\n#define ___bpf_fill7(arr, p, x, args...) arr[p] = x; ___bpf_fill6(arr, p + 1, args)\n#define ___bpf_fill8(arr, p, x, args...) arr[p] = x; ___bpf_fill7(arr, p + 1, args)\n#define ___bpf_fill9(arr, p, x, args...) arr[p] = x; ___bpf_fill8(arr, p + 1, args)\n#define ___bpf_fill10(arr, p, x, args...) arr[p] = x; ___bpf_fill9(arr, p + 1, args)\n#define ___bpf_fill11(arr, p, x, args...) arr[p] = x; ___bpf_fill10(arr, p + 1, args)\n#define ___bpf_fill12(arr, p, x, args...) arr[p] = x; ___bpf_fill11(arr, p + 1, args)\n#define ___bpf_fill(arr, args...) \\\n\t___bpf_apply(___bpf_fill, ___bpf_narg(args))(arr, 0, args)\n\n/*\n * BPF_SEQ_PRINTF to wrap bpf_seq_printf to-be-printed values\n * in a structure.\n */\n#define BPF_SEQ_PRINTF(seq, fmt, args...)\t\t\t\\\n({\t\t\t\t\t\t\t\t\\\n\tstatic const char ___fmt[] = fmt;\t\t\t\\\n\tunsigned long long ___param[___bpf_narg(args)];\t\t\\\n\t\t\t\t\t\t\t\t\\\n\t_Pragma(\"GCC diagnostic push\")\t\t\t\t\\\n\t_Pragma(\"GCC diagnostic ignored \\\"-Wint-conversion\\\"\")\t\\\n\t___bpf_fill(___param, args);\t\t\t\t\\\n\t_Pragma(\"GCC diagnostic pop\")\t\t\t\t\\\n\t\t\t\t\t\t\t\t\\\n\tbpf_seq_printf(seq, ___fmt, sizeof(___fmt),\t\t\\\n\t\t       ___param, sizeof(___param));\t\t\\\n})\n\n/*\n * BPF_SNPRINTF wraps the bpf_snprintf helper with variadic arguments instead of\n * an array of u64.\n */\n#define BPF_SNPRINTF(out, out_size, fmt, args...)\t\t\\\n({\t\t\t\t\t\t\t\t\\\n\tstatic const char ___fmt[] = fmt;\t\t\t\\\n\tunsigned long long ___param[___bpf_narg(args)];\t\t\\\n\t\t\t\t\t\t\t\t\\\n\t_Pragma(\"GCC diagnostic push\")\t\t\t\t\\\n\t_Pragma(\"GCC diagnostic ignored \\\"-Wint-conversion\\\"\")\t\\\n\t___bpf_fill(___param, args);\t\t\t\t\\\n\t_Pragma(\"GCC diagnostic pop\")\t\t\t\t\\\n\t\t\t\t\t\t\t\t\\\n\tbpf_snprintf(out, out_size, ___fmt,\t\t\t\\\n\t\t     ___param, sizeof(___param));\t\t\\\n})\n\n#ifdef BPF_NO_GLOBAL_DATA\n#define BPF_PRINTK_FMT_MOD\n#else\n#define BPF_PRINTK_FMT_MOD static const\n#endif\n\n#define __bpf_printk(fmt, ...)\t\t\t\t\\\n({\t\t\t\t\t\t\t\\\n\tBPF_PRINTK_FMT_MOD char ____fmt[] = fmt;\t\\\n\tbpf_trace_printk(____fmt, sizeof(____fmt),\t\\\n\t\t\t ##__VA_ARGS__);\t\t\\\n})\n\n/*\n * __bpf_vprintk wraps the bpf_trace_vprintk helper with variadic arguments\n * instead of an array of u64.\n */\n#define __bpf_vprintk(fmt, args...)\t\t\t\t\\\n({\t\t\t\t\t\t\t\t\\\n\tstatic const char ___fmt[] = fmt;\t\t\t\\\n\tunsigned long long ___param[___bpf_narg(args)];\t\t\\\n\t\t\t\t\t\t\t\t\\\n\t_Pragma(\"GCC diagnostic push\")\t\t\t\t\\\n\t_Pragma(\"GCC diagnostic ignored \\\"-Wint-conversion\\\"\")\t\\\n\t___bpf_fill(___param, args);\t\t\t\t\\\n\t_Pragma(\"GCC diagnostic pop\")\t\t\t\t\\\n\t\t\t\t\t\t\t\t\\\n\tbpf_trace_vprintk(___fmt, sizeof(___fmt),\t\t\\\n\t\t\t  ___param, sizeof(___param));\t\t\\\n})\n\n/* Use __bpf_printk when bpf_printk call has 3 or fewer fmt args\n * Otherwise use __bpf_vprintk\n */\n#define ___bpf_pick_printk(...) \\\n\t___bpf_nth(_, ##__VA_ARGS__, __bpf_vprintk, __bpf_vprintk, __bpf_vprintk,\t\\\n\t\t   __bpf_vprintk, __bpf_vprintk, __bpf_vprintk, __bpf_vprintk,\t\t\\\n\t\t   __bpf_vprintk, __bpf_vprintk, __bpf_printk /*3*/, __bpf_printk /*2*/,\\\n\t\t   __bpf_printk /*1*/, __bpf_printk /*0*/)\n\n/* Helper macro to print out debug messages */\n#define bpf_printk(fmt, args...) ___bpf_pick_printk(args)(fmt, ##args)\n\n#endif\n"
  },
  {
    "path": "examples/headers/bpf_tracing.h",
    "content": "/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */\n#ifndef __BPF_TRACING_H__\n#define __BPF_TRACING_H__\n\n/* Scan the ARCH passed in from ARCH env variable (see Makefile) */\n#if defined(__TARGET_ARCH_x86)\n\t#define bpf_target_x86\n\t#define bpf_target_defined\n#elif defined(__TARGET_ARCH_s390)\n\t#define bpf_target_s390\n\t#define bpf_target_defined\n#elif defined(__TARGET_ARCH_arm)\n\t#define bpf_target_arm\n\t#define bpf_target_defined\n#elif defined(__TARGET_ARCH_arm64)\n\t#define bpf_target_arm64\n\t#define bpf_target_defined\n#elif defined(__TARGET_ARCH_mips)\n\t#define bpf_target_mips\n\t#define bpf_target_defined\n#elif defined(__TARGET_ARCH_powerpc)\n\t#define bpf_target_powerpc\n\t#define bpf_target_defined\n#elif defined(__TARGET_ARCH_sparc)\n\t#define bpf_target_sparc\n\t#define bpf_target_defined\n#elif defined(__TARGET_ARCH_riscv)\n\t#define bpf_target_riscv\n\t#define bpf_target_defined\n#else\n\n/* Fall back to what the compiler says */\n#if defined(__x86_64__)\n\t#define bpf_target_x86\n\t#define bpf_target_defined\n#elif defined(__s390__)\n\t#define bpf_target_s390\n\t#define bpf_target_defined\n#elif defined(__arm__)\n\t#define bpf_target_arm\n\t#define bpf_target_defined\n#elif defined(__aarch64__)\n\t#define bpf_target_arm64\n\t#define bpf_target_defined\n#elif defined(__mips__)\n\t#define bpf_target_mips\n\t#define bpf_target_defined\n#elif defined(__powerpc__)\n\t#define bpf_target_powerpc\n\t#define bpf_target_defined\n#elif defined(__sparc__)\n\t#define bpf_target_sparc\n\t#define bpf_target_defined\n#elif defined(__riscv) && __riscv_xlen == 64\n\t#define bpf_target_riscv\n\t#define bpf_target_defined\n#endif /* no compiler target */\n\n#endif\n\n#ifndef __BPF_TARGET_MISSING\n#define __BPF_TARGET_MISSING \"GCC error \\\"Must specify a BPF target arch via __TARGET_ARCH_xxx\\\"\"\n#endif\n\n#if defined(bpf_target_x86)\n\n#if defined(__KERNEL__) || defined(__VMLINUX_H__)\n\n#define PT_REGS_PARM1(x) ((x)->di)\n#define PT_REGS_PARM2(x) ((x)->si)\n#define PT_REGS_PARM3(x) ((x)->dx)\n#define PT_REGS_PARM4(x) ((x)->cx)\n#define PT_REGS_PARM5(x) ((x)->r8)\n#define PT_REGS_RET(x) ((x)->sp)\n#define PT_REGS_FP(x) ((x)->bp)\n#define PT_REGS_RC(x) ((x)->ax)\n#define PT_REGS_SP(x) ((x)->sp)\n#define PT_REGS_IP(x) ((x)->ip)\n\n#define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((x), di)\n#define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((x), si)\n#define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((x), dx)\n#define PT_REGS_PARM4_CORE(x) BPF_CORE_READ((x), cx)\n#define PT_REGS_PARM5_CORE(x) BPF_CORE_READ((x), r8)\n#define PT_REGS_RET_CORE(x) BPF_CORE_READ((x), sp)\n#define PT_REGS_FP_CORE(x) BPF_CORE_READ((x), bp)\n#define PT_REGS_RC_CORE(x) BPF_CORE_READ((x), ax)\n#define PT_REGS_SP_CORE(x) BPF_CORE_READ((x), sp)\n#define PT_REGS_IP_CORE(x) BPF_CORE_READ((x), ip)\n\n#else\n\n#ifdef __i386__\n/* i386 kernel is built with -mregparm=3 */\n#define PT_REGS_PARM1(x) ((x)->eax)\n#define PT_REGS_PARM2(x) ((x)->edx)\n#define PT_REGS_PARM3(x) ((x)->ecx)\n#define PT_REGS_PARM4(x) 0\n#define PT_REGS_PARM5(x) 0\n#define PT_REGS_RET(x) ((x)->esp)\n#define PT_REGS_FP(x) ((x)->ebp)\n#define PT_REGS_RC(x) ((x)->eax)\n#define PT_REGS_SP(x) ((x)->esp)\n#define PT_REGS_IP(x) ((x)->eip)\n\n#define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((x), eax)\n#define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((x), edx)\n#define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((x), ecx)\n#define PT_REGS_PARM4_CORE(x) 0\n#define PT_REGS_PARM5_CORE(x) 0\n#define PT_REGS_RET_CORE(x) BPF_CORE_READ((x), esp)\n#define PT_REGS_FP_CORE(x) BPF_CORE_READ((x), ebp)\n#define PT_REGS_RC_CORE(x) BPF_CORE_READ((x), eax)\n#define PT_REGS_SP_CORE(x) BPF_CORE_READ((x), esp)\n#define PT_REGS_IP_CORE(x) BPF_CORE_READ((x), eip)\n\n#else\n\n#define PT_REGS_PARM1(x) ((x)->rdi)\n#define PT_REGS_PARM2(x) ((x)->rsi)\n#define PT_REGS_PARM3(x) ((x)->rdx)\n#define PT_REGS_PARM4(x) ((x)->rcx)\n#define PT_REGS_PARM5(x) ((x)->r8)\n#define PT_REGS_RET(x) ((x)->rsp)\n#define PT_REGS_FP(x) ((x)->rbp)\n#define PT_REGS_RC(x) ((x)->rax)\n#define PT_REGS_SP(x) ((x)->rsp)\n#define PT_REGS_IP(x) ((x)->rip)\n\n#define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((x), rdi)\n#define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((x), rsi)\n#define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((x), rdx)\n#define PT_REGS_PARM4_CORE(x) BPF_CORE_READ((x), rcx)\n#define PT_REGS_PARM5_CORE(x) BPF_CORE_READ((x), r8)\n#define PT_REGS_RET_CORE(x) BPF_CORE_READ((x), rsp)\n#define PT_REGS_FP_CORE(x) BPF_CORE_READ((x), rbp)\n#define PT_REGS_RC_CORE(x) BPF_CORE_READ((x), rax)\n#define PT_REGS_SP_CORE(x) BPF_CORE_READ((x), rsp)\n#define PT_REGS_IP_CORE(x) BPF_CORE_READ((x), rip)\n\n#endif\n#endif\n\n#elif defined(bpf_target_s390)\n\n/* s390 provides user_pt_regs instead of struct pt_regs to userspace */\nstruct pt_regs;\n#define PT_REGS_S390 const volatile user_pt_regs\n#define PT_REGS_PARM1(x) (((PT_REGS_S390 *)(x))->gprs[2])\n#define PT_REGS_PARM2(x) (((PT_REGS_S390 *)(x))->gprs[3])\n#define PT_REGS_PARM3(x) (((PT_REGS_S390 *)(x))->gprs[4])\n#define PT_REGS_PARM4(x) (((PT_REGS_S390 *)(x))->gprs[5])\n#define PT_REGS_PARM5(x) (((PT_REGS_S390 *)(x))->gprs[6])\n#define PT_REGS_RET(x) (((PT_REGS_S390 *)(x))->gprs[14])\n/* Works only with CONFIG_FRAME_POINTER */\n#define PT_REGS_FP(x) (((PT_REGS_S390 *)(x))->gprs[11])\n#define PT_REGS_RC(x) (((PT_REGS_S390 *)(x))->gprs[2])\n#define PT_REGS_SP(x) (((PT_REGS_S390 *)(x))->gprs[15])\n#define PT_REGS_IP(x) (((PT_REGS_S390 *)(x))->psw.addr)\n\n#define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), gprs[2])\n#define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), gprs[3])\n#define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), gprs[4])\n#define PT_REGS_PARM4_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), gprs[5])\n#define PT_REGS_PARM5_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), gprs[6])\n#define PT_REGS_RET_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), gprs[14])\n#define PT_REGS_FP_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), gprs[11])\n#define PT_REGS_RC_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), gprs[2])\n#define PT_REGS_SP_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), gprs[15])\n#define PT_REGS_IP_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), psw.addr)\n\n#elif defined(bpf_target_arm)\n\n#define PT_REGS_PARM1(x) ((x)->uregs[0])\n#define PT_REGS_PARM2(x) ((x)->uregs[1])\n#define PT_REGS_PARM3(x) ((x)->uregs[2])\n#define PT_REGS_PARM4(x) ((x)->uregs[3])\n#define PT_REGS_PARM5(x) ((x)->uregs[4])\n#define PT_REGS_RET(x) ((x)->uregs[14])\n#define PT_REGS_FP(x) ((x)->uregs[11]) /* Works only with CONFIG_FRAME_POINTER */\n#define PT_REGS_RC(x) ((x)->uregs[0])\n#define PT_REGS_SP(x) ((x)->uregs[13])\n#define PT_REGS_IP(x) ((x)->uregs[12])\n\n#define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((x), uregs[0])\n#define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((x), uregs[1])\n#define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((x), uregs[2])\n#define PT_REGS_PARM4_CORE(x) BPF_CORE_READ((x), uregs[3])\n#define PT_REGS_PARM5_CORE(x) BPF_CORE_READ((x), uregs[4])\n#define PT_REGS_RET_CORE(x) BPF_CORE_READ((x), uregs[14])\n#define PT_REGS_FP_CORE(x) BPF_CORE_READ((x), uregs[11])\n#define PT_REGS_RC_CORE(x) BPF_CORE_READ((x), uregs[0])\n#define PT_REGS_SP_CORE(x) BPF_CORE_READ((x), uregs[13])\n#define PT_REGS_IP_CORE(x) BPF_CORE_READ((x), uregs[12])\n\n#elif defined(bpf_target_arm64)\n\n/* arm64 provides struct user_pt_regs instead of struct pt_regs to userspace */\nstruct pt_regs;\n#define PT_REGS_ARM64 const volatile struct user_pt_regs\n#define PT_REGS_PARM1(x) (((PT_REGS_ARM64 *)(x))->regs[0])\n#define PT_REGS_PARM2(x) (((PT_REGS_ARM64 *)(x))->regs[1])\n#define PT_REGS_PARM3(x) (((PT_REGS_ARM64 *)(x))->regs[2])\n#define PT_REGS_PARM4(x) (((PT_REGS_ARM64 *)(x))->regs[3])\n#define PT_REGS_PARM5(x) (((PT_REGS_ARM64 *)(x))->regs[4])\n#define PT_REGS_RET(x) (((PT_REGS_ARM64 *)(x))->regs[30])\n/* Works only with CONFIG_FRAME_POINTER */\n#define PT_REGS_FP(x) (((PT_REGS_ARM64 *)(x))->regs[29])\n#define PT_REGS_RC(x) (((PT_REGS_ARM64 *)(x))->regs[0])\n#define PT_REGS_SP(x) (((PT_REGS_ARM64 *)(x))->sp)\n#define PT_REGS_IP(x) (((PT_REGS_ARM64 *)(x))->pc)\n\n#define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), regs[0])\n#define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), regs[1])\n#define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), regs[2])\n#define PT_REGS_PARM4_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), regs[3])\n#define PT_REGS_PARM5_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), regs[4])\n#define PT_REGS_RET_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), regs[30])\n#define PT_REGS_FP_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), regs[29])\n#define PT_REGS_RC_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), regs[0])\n#define PT_REGS_SP_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), sp)\n#define PT_REGS_IP_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), pc)\n\n#elif defined(bpf_target_mips)\n\n#define PT_REGS_PARM1(x) ((x)->regs[4])\n#define PT_REGS_PARM2(x) ((x)->regs[5])\n#define PT_REGS_PARM3(x) ((x)->regs[6])\n#define PT_REGS_PARM4(x) ((x)->regs[7])\n#define PT_REGS_PARM5(x) ((x)->regs[8])\n#define PT_REGS_RET(x) ((x)->regs[31])\n#define PT_REGS_FP(x) ((x)->regs[30]) /* Works only with CONFIG_FRAME_POINTER */\n#define PT_REGS_RC(x) ((x)->regs[2])\n#define PT_REGS_SP(x) ((x)->regs[29])\n#define PT_REGS_IP(x) ((x)->cp0_epc)\n\n#define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((x), regs[4])\n#define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((x), regs[5])\n#define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((x), regs[6])\n#define PT_REGS_PARM4_CORE(x) BPF_CORE_READ((x), regs[7])\n#define PT_REGS_PARM5_CORE(x) BPF_CORE_READ((x), regs[8])\n#define PT_REGS_RET_CORE(x) BPF_CORE_READ((x), regs[31])\n#define PT_REGS_FP_CORE(x) BPF_CORE_READ((x), regs[30])\n#define PT_REGS_RC_CORE(x) BPF_CORE_READ((x), regs[2])\n#define PT_REGS_SP_CORE(x) BPF_CORE_READ((x), regs[29])\n#define PT_REGS_IP_CORE(x) BPF_CORE_READ((x), cp0_epc)\n\n#elif defined(bpf_target_powerpc)\n\n#define PT_REGS_PARM1(x) ((x)->gpr[3])\n#define PT_REGS_PARM2(x) ((x)->gpr[4])\n#define PT_REGS_PARM3(x) ((x)->gpr[5])\n#define PT_REGS_PARM4(x) ((x)->gpr[6])\n#define PT_REGS_PARM5(x) ((x)->gpr[7])\n#define PT_REGS_RC(x) ((x)->gpr[3])\n#define PT_REGS_SP(x) ((x)->sp)\n#define PT_REGS_IP(x) ((x)->nip)\n\n#define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((x), gpr[3])\n#define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((x), gpr[4])\n#define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((x), gpr[5])\n#define PT_REGS_PARM4_CORE(x) BPF_CORE_READ((x), gpr[6])\n#define PT_REGS_PARM5_CORE(x) BPF_CORE_READ((x), gpr[7])\n#define PT_REGS_RC_CORE(x) BPF_CORE_READ((x), gpr[3])\n#define PT_REGS_SP_CORE(x) BPF_CORE_READ((x), sp)\n#define PT_REGS_IP_CORE(x) BPF_CORE_READ((x), nip)\n\n#elif defined(bpf_target_sparc)\n\n#define PT_REGS_PARM1(x) ((x)->u_regs[UREG_I0])\n#define PT_REGS_PARM2(x) ((x)->u_regs[UREG_I1])\n#define PT_REGS_PARM3(x) ((x)->u_regs[UREG_I2])\n#define PT_REGS_PARM4(x) ((x)->u_regs[UREG_I3])\n#define PT_REGS_PARM5(x) ((x)->u_regs[UREG_I4])\n#define PT_REGS_RET(x) ((x)->u_regs[UREG_I7])\n#define PT_REGS_RC(x) ((x)->u_regs[UREG_I0])\n#define PT_REGS_SP(x) ((x)->u_regs[UREG_FP])\n\n#define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((x), u_regs[UREG_I0])\n#define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((x), u_regs[UREG_I1])\n#define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((x), u_regs[UREG_I2])\n#define PT_REGS_PARM4_CORE(x) BPF_CORE_READ((x), u_regs[UREG_I3])\n#define PT_REGS_PARM5_CORE(x) BPF_CORE_READ((x), u_regs[UREG_I4])\n#define PT_REGS_RET_CORE(x) BPF_CORE_READ((x), u_regs[UREG_I7])\n#define PT_REGS_RC_CORE(x) BPF_CORE_READ((x), u_regs[UREG_I0])\n#define PT_REGS_SP_CORE(x) BPF_CORE_READ((x), u_regs[UREG_FP])\n\n/* Should this also be a bpf_target check for the sparc case? */\n#if defined(__arch64__)\n#define PT_REGS_IP(x) ((x)->tpc)\n#define PT_REGS_IP_CORE(x) BPF_CORE_READ((x), tpc)\n#else\n#define PT_REGS_IP(x) ((x)->pc)\n#define PT_REGS_IP_CORE(x) BPF_CORE_READ((x), pc)\n#endif\n\n#elif defined(bpf_target_riscv)\n\nstruct pt_regs;\n#define PT_REGS_RV const volatile struct user_regs_struct\n#define PT_REGS_PARM1(x) (((PT_REGS_RV *)(x))->a0)\n#define PT_REGS_PARM2(x) (((PT_REGS_RV *)(x))->a1)\n#define PT_REGS_PARM3(x) (((PT_REGS_RV *)(x))->a2)\n#define PT_REGS_PARM4(x) (((PT_REGS_RV *)(x))->a3)\n#define PT_REGS_PARM5(x) (((PT_REGS_RV *)(x))->a4)\n#define PT_REGS_RET(x) (((PT_REGS_RV *)(x))->ra)\n#define PT_REGS_FP(x) (((PT_REGS_RV *)(x))->s5)\n#define PT_REGS_RC(x) (((PT_REGS_RV *)(x))->a5)\n#define PT_REGS_SP(x) (((PT_REGS_RV *)(x))->sp)\n#define PT_REGS_IP(x) (((PT_REGS_RV *)(x))->epc)\n\n#define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), a0)\n#define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), a1)\n#define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), a2)\n#define PT_REGS_PARM4_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), a3)\n#define PT_REGS_PARM5_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), a4)\n#define PT_REGS_RET_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), ra)\n#define PT_REGS_FP_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), fp)\n#define PT_REGS_RC_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), a5)\n#define PT_REGS_SP_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), sp)\n#define PT_REGS_IP_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), epc)\n\n#endif\n\n#if defined(bpf_target_powerpc)\n#define BPF_KPROBE_READ_RET_IP(ip, ctx)\t\t({ (ip) = (ctx)->link; })\n#define BPF_KRETPROBE_READ_RET_IP\t\tBPF_KPROBE_READ_RET_IP\n#elif defined(bpf_target_sparc)\n#define BPF_KPROBE_READ_RET_IP(ip, ctx)\t\t({ (ip) = PT_REGS_RET(ctx); })\n#define BPF_KRETPROBE_READ_RET_IP\t\tBPF_KPROBE_READ_RET_IP\n#elif defined(bpf_target_defined)\n#define BPF_KPROBE_READ_RET_IP(ip, ctx)\t\t\t\t\t    \\\n\t({ bpf_probe_read_kernel(&(ip), sizeof(ip), (void *)PT_REGS_RET(ctx)); })\n#define BPF_KRETPROBE_READ_RET_IP(ip, ctx)\t\t\t\t    \\\n\t({ bpf_probe_read_kernel(&(ip), sizeof(ip),\t\t\t    \\\n\t\t\t  (void *)(PT_REGS_FP(ctx) + sizeof(ip))); })\n#endif\n\n#if !defined(bpf_target_defined)\n\n#define PT_REGS_PARM1(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })\n#define PT_REGS_PARM2(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })\n#define PT_REGS_PARM3(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })\n#define PT_REGS_PARM4(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })\n#define PT_REGS_PARM5(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })\n#define PT_REGS_RET(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })\n#define PT_REGS_FP(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })\n#define PT_REGS_RC(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })\n#define PT_REGS_SP(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })\n#define PT_REGS_IP(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })\n\n#define PT_REGS_PARM1_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })\n#define PT_REGS_PARM2_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })\n#define PT_REGS_PARM3_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })\n#define PT_REGS_PARM4_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })\n#define PT_REGS_PARM5_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })\n#define PT_REGS_RET_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })\n#define PT_REGS_FP_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })\n#define PT_REGS_RC_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })\n#define PT_REGS_SP_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })\n#define PT_REGS_IP_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })\n\n#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })\n#define BPF_KRETPROBE_READ_RET_IP(ip, ctx) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })\n\n#endif /* !defined(bpf_target_defined) */\n\n#ifndef ___bpf_concat\n#define ___bpf_concat(a, b) a ## b\n#endif\n#ifndef ___bpf_apply\n#define ___bpf_apply(fn, n) ___bpf_concat(fn, n)\n#endif\n#ifndef ___bpf_nth\n#define ___bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, N, ...) N\n#endif\n#ifndef ___bpf_narg\n#define ___bpf_narg(...) \\\n\t___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)\n#endif\n\n#define ___bpf_ctx_cast0() ctx\n#define ___bpf_ctx_cast1(x) ___bpf_ctx_cast0(), (void *)ctx[0]\n#define ___bpf_ctx_cast2(x, args...) ___bpf_ctx_cast1(args), (void *)ctx[1]\n#define ___bpf_ctx_cast3(x, args...) ___bpf_ctx_cast2(args), (void *)ctx[2]\n#define ___bpf_ctx_cast4(x, args...) ___bpf_ctx_cast3(args), (void *)ctx[3]\n#define ___bpf_ctx_cast5(x, args...) ___bpf_ctx_cast4(args), (void *)ctx[4]\n#define ___bpf_ctx_cast6(x, args...) ___bpf_ctx_cast5(args), (void *)ctx[5]\n#define ___bpf_ctx_cast7(x, args...) ___bpf_ctx_cast6(args), (void *)ctx[6]\n#define ___bpf_ctx_cast8(x, args...) ___bpf_ctx_cast7(args), (void *)ctx[7]\n#define ___bpf_ctx_cast9(x, args...) ___bpf_ctx_cast8(args), (void *)ctx[8]\n#define ___bpf_ctx_cast10(x, args...) ___bpf_ctx_cast9(args), (void *)ctx[9]\n#define ___bpf_ctx_cast11(x, args...) ___bpf_ctx_cast10(args), (void *)ctx[10]\n#define ___bpf_ctx_cast12(x, args...) ___bpf_ctx_cast11(args), (void *)ctx[11]\n#define ___bpf_ctx_cast(args...) \\\n\t___bpf_apply(___bpf_ctx_cast, ___bpf_narg(args))(args)\n\n/*\n * BPF_PROG is a convenience wrapper for generic tp_btf/fentry/fexit and\n * similar kinds of BPF programs, that accept input arguments as a single\n * pointer to untyped u64 array, where each u64 can actually be a typed\n * pointer or integer of different size. Instead of requring user to write\n * manual casts and work with array elements by index, BPF_PROG macro\n * allows user to declare a list of named and typed input arguments in the\n * same syntax as for normal C function. All the casting is hidden and\n * performed transparently, while user code can just assume working with\n * function arguments of specified type and name.\n *\n * Original raw context argument is preserved as well as 'ctx' argument.\n * This is useful when using BPF helpers that expect original context\n * as one of the parameters (e.g., for bpf_perf_event_output()).\n */\n#define BPF_PROG(name, args...)\t\t\t\t\t\t    \\\nname(unsigned long long *ctx);\t\t\t\t\t\t    \\\nstatic __attribute__((always_inline)) typeof(name(0))\t\t\t    \\\n____##name(unsigned long long *ctx, ##args);\t\t\t\t    \\\ntypeof(name(0)) name(unsigned long long *ctx)\t\t\t\t    \\\n{\t\t\t\t\t\t\t\t\t    \\\n\t_Pragma(\"GCC diagnostic push\")\t\t\t\t\t    \\\n\t_Pragma(\"GCC diagnostic ignored \\\"-Wint-conversion\\\"\")\t\t    \\\n\treturn ____##name(___bpf_ctx_cast(args));\t\t\t    \\\n\t_Pragma(\"GCC diagnostic pop\")\t\t\t\t\t    \\\n}\t\t\t\t\t\t\t\t\t    \\\nstatic __attribute__((always_inline)) typeof(name(0))\t\t\t    \\\n____##name(unsigned long long *ctx, ##args)\n\nstruct pt_regs;\n\n#define ___bpf_kprobe_args0() ctx\n#define ___bpf_kprobe_args1(x) \\\n\t___bpf_kprobe_args0(), (void *)PT_REGS_PARM1(ctx)\n#define ___bpf_kprobe_args2(x, args...) \\\n\t___bpf_kprobe_args1(args), (void *)PT_REGS_PARM2(ctx)\n#define ___bpf_kprobe_args3(x, args...) \\\n\t___bpf_kprobe_args2(args), (void *)PT_REGS_PARM3(ctx)\n#define ___bpf_kprobe_args4(x, args...) \\\n\t___bpf_kprobe_args3(args), (void *)PT_REGS_PARM4(ctx)\n#define ___bpf_kprobe_args5(x, args...) \\\n\t___bpf_kprobe_args4(args), (void *)PT_REGS_PARM5(ctx)\n#define ___bpf_kprobe_args(args...) \\\n\t___bpf_apply(___bpf_kprobe_args, ___bpf_narg(args))(args)\n\n/*\n * BPF_KPROBE serves the same purpose for kprobes as BPF_PROG for\n * tp_btf/fentry/fexit BPF programs. It hides the underlying platform-specific\n * low-level way of getting kprobe input arguments from struct pt_regs, and\n * provides a familiar typed and named function arguments syntax and\n * semantics of accessing kprobe input paremeters.\n *\n * Original struct pt_regs* context is preserved as 'ctx' argument. This might\n * be necessary when using BPF helpers like bpf_perf_event_output().\n */\n#define BPF_KPROBE(name, args...)\t\t\t\t\t    \\\nname(struct pt_regs *ctx);\t\t\t\t\t\t    \\\nstatic __attribute__((always_inline)) typeof(name(0))\t\t\t    \\\n____##name(struct pt_regs *ctx, ##args);\t\t\t\t    \\\ntypeof(name(0)) name(struct pt_regs *ctx)\t\t\t\t    \\\n{\t\t\t\t\t\t\t\t\t    \\\n\t_Pragma(\"GCC diagnostic push\")\t\t\t\t\t    \\\n\t_Pragma(\"GCC diagnostic ignored \\\"-Wint-conversion\\\"\")\t\t    \\\n\treturn ____##name(___bpf_kprobe_args(args));\t\t\t    \\\n\t_Pragma(\"GCC diagnostic pop\")\t\t\t\t\t    \\\n}\t\t\t\t\t\t\t\t\t    \\\nstatic __attribute__((always_inline)) typeof(name(0))\t\t\t    \\\n____##name(struct pt_regs *ctx, ##args)\n\n#define ___bpf_kretprobe_args0() ctx\n#define ___bpf_kretprobe_args1(x) \\\n\t___bpf_kretprobe_args0(), (void *)PT_REGS_RC(ctx)\n#define ___bpf_kretprobe_args(args...) \\\n\t___bpf_apply(___bpf_kretprobe_args, ___bpf_narg(args))(args)\n\n/*\n * BPF_KRETPROBE is similar to BPF_KPROBE, except, it only provides optional\n * return value (in addition to `struct pt_regs *ctx`), but no input\n * arguments, because they will be clobbered by the time probed function\n * returns.\n */\n#define BPF_KRETPROBE(name, args...)\t\t\t\t\t    \\\nname(struct pt_regs *ctx);\t\t\t\t\t\t    \\\nstatic __attribute__((always_inline)) typeof(name(0))\t\t\t    \\\n____##name(struct pt_regs *ctx, ##args);\t\t\t\t    \\\ntypeof(name(0)) name(struct pt_regs *ctx)\t\t\t\t    \\\n{\t\t\t\t\t\t\t\t\t    \\\n\t_Pragma(\"GCC diagnostic push\")\t\t\t\t\t    \\\n\t_Pragma(\"GCC diagnostic ignored \\\"-Wint-conversion\\\"\")\t\t    \\\n\treturn ____##name(___bpf_kretprobe_args(args));\t\t\t    \\\n\t_Pragma(\"GCC diagnostic pop\")\t\t\t\t\t    \\\n}\t\t\t\t\t\t\t\t\t    \\\nstatic __always_inline typeof(name(0)) ____##name(struct pt_regs *ctx, ##args)\n\n#endif\n"
  },
  {
    "path": "examples/headers/common.h",
    "content": "// This is a compact version of `vmlinux.h` to be used in the examples using C code.\n\n#pragma once\n\ntypedef unsigned char __u8;\ntypedef short int __s16;\ntypedef short unsigned int __u16;\ntypedef int __s32;\ntypedef unsigned int __u32;\ntypedef long long int __s64;\ntypedef long long unsigned int __u64;\ntypedef __u8 u8;\ntypedef __s16 s16;\ntypedef __u16 u16;\ntypedef __s32 s32;\ntypedef __u32 u32;\ntypedef __s64 s64;\ntypedef __u64 u64;\ntypedef __u16 __le16;\ntypedef __u16 __be16;\ntypedef __u32 __be32;\ntypedef __u64 __be64;\ntypedef __u32 __wsum;\n\n#include \"bpf_helpers.h\"\n\nenum bpf_map_type {\n\tBPF_MAP_TYPE_UNSPEC                = 0,\n\tBPF_MAP_TYPE_HASH                  = 1,\n\tBPF_MAP_TYPE_ARRAY                 = 2,\n\tBPF_MAP_TYPE_PROG_ARRAY            = 3,\n\tBPF_MAP_TYPE_PERF_EVENT_ARRAY      = 4,\n\tBPF_MAP_TYPE_PERCPU_HASH           = 5,\n\tBPF_MAP_TYPE_PERCPU_ARRAY          = 6,\n\tBPF_MAP_TYPE_STACK_TRACE           = 7,\n\tBPF_MAP_TYPE_CGROUP_ARRAY          = 8,\n\tBPF_MAP_TYPE_LRU_HASH              = 9,\n\tBPF_MAP_TYPE_LRU_PERCPU_HASH       = 10,\n\tBPF_MAP_TYPE_LPM_TRIE              = 11,\n\tBPF_MAP_TYPE_ARRAY_OF_MAPS         = 12,\n\tBPF_MAP_TYPE_HASH_OF_MAPS          = 13,\n\tBPF_MAP_TYPE_DEVMAP                = 14,\n\tBPF_MAP_TYPE_SOCKMAP               = 15,\n\tBPF_MAP_TYPE_CPUMAP                = 16,\n\tBPF_MAP_TYPE_XSKMAP                = 17,\n\tBPF_MAP_TYPE_SOCKHASH              = 18,\n\tBPF_MAP_TYPE_CGROUP_STORAGE        = 19,\n\tBPF_MAP_TYPE_REUSEPORT_SOCKARRAY   = 20,\n\tBPF_MAP_TYPE_PERCPU_CGROUP_STORAGE = 21,\n\tBPF_MAP_TYPE_QUEUE                 = 22,\n\tBPF_MAP_TYPE_STACK                 = 23,\n\tBPF_MAP_TYPE_SK_STORAGE            = 24,\n\tBPF_MAP_TYPE_DEVMAP_HASH           = 25,\n\tBPF_MAP_TYPE_STRUCT_OPS            = 26,\n\tBPF_MAP_TYPE_RINGBUF               = 27,\n\tBPF_MAP_TYPE_INODE_STORAGE         = 28,\n};\n\nenum xdp_action {\n\tXDP_ABORTED = 0,\n\tXDP_DROP = 1,\n\tXDP_PASS = 2,\n\tXDP_TX = 3,\n\tXDP_REDIRECT = 4,\n};\n\nenum tc_action {\n\tTC_ACT_UNSPEC \t\t= -1,\n\tTC_ACT_OK \t\t\t= 0,\n\tTC_ACT_RECLASSIFY \t= 1,\n\tTC_ACT_SHOT \t\t= 2,\n\tTC_ACT_PIPE \t\t= 3,\n\tTC_ACT_STOLEN \t\t= 4,\n\tTC_ACT_QUEUED \t\t= 5,\n\tTC_ACT_REPEAT \t\t= 6,\n\tTC_ACT_REDIRECT \t= 7,\n\tTC_ACT_JUMP \t\t= 0x10000000\n};\n\nstruct xdp_md {\n\t__u32 data;\n\t__u32 data_end;\n\t__u32 data_meta;\n\t__u32 ingress_ifindex;\n\t__u32 rx_queue_index;\n\t__u32 egress_ifindex;\n};\n\ntypedef __u16 __sum16;\n\n#define ETH_P_IP 0x0800\n\nstruct ethhdr {\n\tunsigned char h_dest[6];\n\tunsigned char h_source[6];\n\t__be16 h_proto;\n};\n\nstruct iphdr {\n\t__u8 ihl: 4;\n\t__u8 version: 4;\n\t__u8 tos;\n\t__be16 tot_len;\n\t__be16 id;\n\t__be16 frag_off;\n\t__u8 ttl;\n\t__u8 protocol;\n\t__sum16 check;\n\t__be32 saddr;\n\t__be32 daddr;\n};\n\nenum {\n\tBPF_ANY     = 0,\n\tBPF_NOEXIST = 1,\n\tBPF_EXIST   = 2,\n\tBPF_F_LOCK  = 4,\n};\n\n/* BPF_FUNC_perf_event_output, BPF_FUNC_perf_event_read and\n * BPF_FUNC_perf_event_read_value flags.\n */\n#define BPF_F_INDEX_MASK 0xffffffffULL\n#define BPF_F_CURRENT_CPU BPF_F_INDEX_MASK\n\n#if defined(__TARGET_ARCH_x86)\nstruct pt_regs {\n\t/*\n\t * C ABI says these regs are callee-preserved. They aren't saved on kernel entry\n\t * unless syscall needs a complete, fully filled \"struct pt_regs\".\n\t */\n\tunsigned long r15;\n\tunsigned long r14;\n\tunsigned long r13;\n\tunsigned long r12;\n\tunsigned long rbp;\n\tunsigned long rbx;\n\t/* These regs are callee-clobbered. Always saved on kernel entry. */\n\tunsigned long r11;\n\tunsigned long r10;\n\tunsigned long r9;\n\tunsigned long r8;\n\tunsigned long rax;\n\tunsigned long rcx;\n\tunsigned long rdx;\n\tunsigned long rsi;\n\tunsigned long rdi;\n\t/*\n\t * On syscall entry, this is syscall#. On CPU exception, this is error code.\n\t * On hw interrupt, it's IRQ number:\n\t */\n\tunsigned long orig_rax;\n\t/* Return frame for iretq */\n\tunsigned long rip;\n\tunsigned long cs;\n\tunsigned long eflags;\n\tunsigned long rsp;\n\tunsigned long ss;\n\t/* top of stack page */\n};\n#endif /* __TARGET_ARCH_x86 */\n"
  },
  {
    "path": "examples/headers/update.sh",
    "content": "#!/usr/bin/env bash\n\n# Version of libbpf to fetch headers from\nLIBBPF_VERSION=0.6.1\n\n# The headers we want\nprefix=libbpf-\"$LIBBPF_VERSION\"\nheaders=(\n    \"$prefix\"/LICENSE.BSD-2-Clause\n    \"$prefix\"/src/bpf_endian.h\n    \"$prefix\"/src/bpf_helper_defs.h\n    \"$prefix\"/src/bpf_helpers.h\n    \"$prefix\"/src/bpf_tracing.h\n)\n\n# Fetch libbpf release and extract the desired headers\ncurl -sL \"https://github.com/libbpf/libbpf/archive/refs/tags/v${LIBBPF_VERSION}.tar.gz\" | \\\n    tar -xz --xform='s#.*/##' \"${headers[@]}\"\n"
  },
  {
    "path": "examples/kprobe/bpf_bpfeb.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (mips || mips64 || ppc64 || s390x) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\n// loadBpf returns the embedded CollectionSpec for bpf.\nfunc loadBpf() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_BpfBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load bpf: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadBpfObjects loads bpf and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*bpfObjects\n//\t*bpfPrograms\n//\t*bpfMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadBpf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// bpfSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfSpecs struct {\n\tbpfProgramSpecs\n\tbpfMapSpecs\n\tbpfVariableSpecs\n}\n\n// bpfProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfProgramSpecs struct {\n\tKprobeExecve *ebpf.ProgramSpec `ebpf:\"kprobe_execve\"`\n}\n\n// bpfMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfMapSpecs struct {\n\tKprobeMap *ebpf.MapSpec `ebpf:\"kprobe_map\"`\n}\n\n// bpfVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfVariableSpecs struct {\n}\n\n// bpfObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfObjects struct {\n\tbpfPrograms\n\tbpfMaps\n\tbpfVariables\n}\n\nfunc (o *bpfObjects) Close() error {\n\treturn _BpfClose(\n\t\t&o.bpfPrograms,\n\t\t&o.bpfMaps,\n\t)\n}\n\n// bpfMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfMaps struct {\n\tKprobeMap *ebpf.Map `ebpf:\"kprobe_map\"`\n}\n\nfunc (m *bpfMaps) Close() error {\n\treturn _BpfClose(\n\t\tm.KprobeMap,\n\t)\n}\n\n// bpfVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfVariables struct {\n}\n\n// bpfPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfPrograms struct {\n\tKprobeExecve *ebpf.Program `ebpf:\"kprobe_execve\"`\n}\n\nfunc (p *bpfPrograms) Close() error {\n\treturn _BpfClose(\n\t\tp.KprobeExecve,\n\t)\n}\n\nfunc _BpfClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed bpf_bpfeb.o\nvar _BpfBytes []byte\n"
  },
  {
    "path": "examples/kprobe/bpf_bpfel.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64 || wasm) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\n// loadBpf returns the embedded CollectionSpec for bpf.\nfunc loadBpf() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_BpfBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load bpf: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadBpfObjects loads bpf and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*bpfObjects\n//\t*bpfPrograms\n//\t*bpfMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadBpf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// bpfSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfSpecs struct {\n\tbpfProgramSpecs\n\tbpfMapSpecs\n\tbpfVariableSpecs\n}\n\n// bpfProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfProgramSpecs struct {\n\tKprobeExecve *ebpf.ProgramSpec `ebpf:\"kprobe_execve\"`\n}\n\n// bpfMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfMapSpecs struct {\n\tKprobeMap *ebpf.MapSpec `ebpf:\"kprobe_map\"`\n}\n\n// bpfVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfVariableSpecs struct {\n}\n\n// bpfObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfObjects struct {\n\tbpfPrograms\n\tbpfMaps\n\tbpfVariables\n}\n\nfunc (o *bpfObjects) Close() error {\n\treturn _BpfClose(\n\t\t&o.bpfPrograms,\n\t\t&o.bpfMaps,\n\t)\n}\n\n// bpfMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfMaps struct {\n\tKprobeMap *ebpf.Map `ebpf:\"kprobe_map\"`\n}\n\nfunc (m *bpfMaps) Close() error {\n\treturn _BpfClose(\n\t\tm.KprobeMap,\n\t)\n}\n\n// bpfVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfVariables struct {\n}\n\n// bpfPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfPrograms struct {\n\tKprobeExecve *ebpf.Program `ebpf:\"kprobe_execve\"`\n}\n\nfunc (p *bpfPrograms) Close() error {\n\treturn _BpfClose(\n\t\tp.KprobeExecve,\n\t)\n}\n\nfunc _BpfClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed bpf_bpfel.o\nvar _BpfBytes []byte\n"
  },
  {
    "path": "examples/kprobe/kprobe.c",
    "content": "//go:build ignore\n\n#include \"common.h\"\n\nchar __license[] SEC(\"license\") = \"Dual MIT/GPL\";\n\nstruct {\n\t__uint(type, BPF_MAP_TYPE_ARRAY);\n\t__type(key, u32);\n\t__type(value, u64);\n\t__uint(max_entries, 1);\n} kprobe_map SEC(\".maps\");\n\nSEC(\"kprobe/sys_execve\")\nint kprobe_execve() {\n\tu32 key     = 0;\n\tu64 initval = 1, *valp;\n\n\tvalp = bpf_map_lookup_elem(&kprobe_map, &key);\n\tif (!valp) {\n\t\tbpf_map_update_elem(&kprobe_map, &key, &initval, BPF_ANY);\n\t\treturn 0;\n\t}\n\t__sync_fetch_and_add(valp, 1);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "examples/kprobe/main.go",
    "content": "//go:build linux\n\n// This program demonstrates attaching an eBPF program to a kernel symbol.\n// The eBPF program will be attached to the start of the sys_execve\n// kernel function and prints out the number of times it has been called\n// every second.\npackage main\n\nimport (\n\t\"log\"\n\t\"time\"\n\n\t\"github.com/cilium/ebpf/link\"\n\t\"github.com/cilium/ebpf/rlimit\"\n)\n\n//go:generate go tool bpf2go -tags linux bpf kprobe.c -- -I../headers\n\nconst mapKey uint32 = 0\n\nfunc main() {\n\n\t// Name of the kernel function to trace.\n\tfn := \"sys_execve\"\n\n\t// Allow the current process to lock memory for eBPF resources.\n\tif err := rlimit.RemoveMemlock(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Load pre-compiled programs and maps into the kernel.\n\tobjs := bpfObjects{}\n\tif err := loadBpfObjects(&objs, nil); err != nil {\n\t\tlog.Fatalf(\"loading objects: %v\", err)\n\t}\n\tdefer objs.Close()\n\n\t// Open a Kprobe at the entry point of the kernel function and attach the\n\t// pre-compiled program. Each time the kernel function enters, the program\n\t// will increment the execution counter by 1. The read loop below polls this\n\t// map value once per second.\n\tkp, err := link.Kprobe(fn, objs.KprobeExecve, nil)\n\tif err != nil {\n\t\tlog.Fatalf(\"opening kprobe: %s\", err)\n\t}\n\tdefer kp.Close()\n\n\t// Read loop reporting the total amount of times the kernel\n\t// function was entered, once per second.\n\tticker := time.NewTicker(1 * time.Second)\n\tdefer ticker.Stop()\n\n\tlog.Println(\"Waiting for events..\")\n\n\tfor range ticker.C {\n\t\tvar value uint64\n\t\tif err := objs.KprobeMap.Lookup(mapKey, &value); err != nil {\n\t\t\tlog.Fatalf(\"reading map: %v\", err)\n\t\t}\n\t\tlog.Printf(\"%s called %d times\\n\", fn, value)\n\t}\n}\n"
  },
  {
    "path": "examples/kprobe_percpu/bpf_bpfeb.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (mips || mips64 || ppc64 || s390x) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\n// loadBpf returns the embedded CollectionSpec for bpf.\nfunc loadBpf() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_BpfBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load bpf: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadBpfObjects loads bpf and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*bpfObjects\n//\t*bpfPrograms\n//\t*bpfMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadBpf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// bpfSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfSpecs struct {\n\tbpfProgramSpecs\n\tbpfMapSpecs\n\tbpfVariableSpecs\n}\n\n// bpfProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfProgramSpecs struct {\n\tKprobeExecve *ebpf.ProgramSpec `ebpf:\"kprobe_execve\"`\n}\n\n// bpfMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfMapSpecs struct {\n\tKprobeMap *ebpf.MapSpec `ebpf:\"kprobe_map\"`\n}\n\n// bpfVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfVariableSpecs struct {\n}\n\n// bpfObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfObjects struct {\n\tbpfPrograms\n\tbpfMaps\n\tbpfVariables\n}\n\nfunc (o *bpfObjects) Close() error {\n\treturn _BpfClose(\n\t\t&o.bpfPrograms,\n\t\t&o.bpfMaps,\n\t)\n}\n\n// bpfMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfMaps struct {\n\tKprobeMap *ebpf.Map `ebpf:\"kprobe_map\"`\n}\n\nfunc (m *bpfMaps) Close() error {\n\treturn _BpfClose(\n\t\tm.KprobeMap,\n\t)\n}\n\n// bpfVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfVariables struct {\n}\n\n// bpfPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfPrograms struct {\n\tKprobeExecve *ebpf.Program `ebpf:\"kprobe_execve\"`\n}\n\nfunc (p *bpfPrograms) Close() error {\n\treturn _BpfClose(\n\t\tp.KprobeExecve,\n\t)\n}\n\nfunc _BpfClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed bpf_bpfeb.o\nvar _BpfBytes []byte\n"
  },
  {
    "path": "examples/kprobe_percpu/bpf_bpfel.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64 || wasm) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\n// loadBpf returns the embedded CollectionSpec for bpf.\nfunc loadBpf() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_BpfBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load bpf: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadBpfObjects loads bpf and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*bpfObjects\n//\t*bpfPrograms\n//\t*bpfMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadBpf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// bpfSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfSpecs struct {\n\tbpfProgramSpecs\n\tbpfMapSpecs\n\tbpfVariableSpecs\n}\n\n// bpfProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfProgramSpecs struct {\n\tKprobeExecve *ebpf.ProgramSpec `ebpf:\"kprobe_execve\"`\n}\n\n// bpfMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfMapSpecs struct {\n\tKprobeMap *ebpf.MapSpec `ebpf:\"kprobe_map\"`\n}\n\n// bpfVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfVariableSpecs struct {\n}\n\n// bpfObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfObjects struct {\n\tbpfPrograms\n\tbpfMaps\n\tbpfVariables\n}\n\nfunc (o *bpfObjects) Close() error {\n\treturn _BpfClose(\n\t\t&o.bpfPrograms,\n\t\t&o.bpfMaps,\n\t)\n}\n\n// bpfMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfMaps struct {\n\tKprobeMap *ebpf.Map `ebpf:\"kprobe_map\"`\n}\n\nfunc (m *bpfMaps) Close() error {\n\treturn _BpfClose(\n\t\tm.KprobeMap,\n\t)\n}\n\n// bpfVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfVariables struct {\n}\n\n// bpfPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfPrograms struct {\n\tKprobeExecve *ebpf.Program `ebpf:\"kprobe_execve\"`\n}\n\nfunc (p *bpfPrograms) Close() error {\n\treturn _BpfClose(\n\t\tp.KprobeExecve,\n\t)\n}\n\nfunc _BpfClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed bpf_bpfel.o\nvar _BpfBytes []byte\n"
  },
  {
    "path": "examples/kprobe_percpu/kprobe_percpu.c",
    "content": "//go:build ignore\n\n#include \"common.h\"\n\nchar __license[] SEC(\"license\") = \"Dual MIT/GPL\";\n\nstruct {\n\t__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);\n\t__type(key, u32);\n\t__type(value, u64);\n\t__uint(max_entries, 1);\n} kprobe_map SEC(\".maps\");\n\nSEC(\"kprobe/sys_execve\")\nint kprobe_execve() {\n\tu32 key     = 0;\n\tu64 initval = 1, *valp;\n\n\tvalp = bpf_map_lookup_elem(&kprobe_map, &key);\n\tif (!valp) {\n\t\tbpf_map_update_elem(&kprobe_map, &key, &initval, BPF_ANY);\n\t\treturn 0;\n\t}\n\t__sync_fetch_and_add(valp, 1);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "examples/kprobe_percpu/main.go",
    "content": "//go:build linux\n\n// This program demonstrates attaching an eBPF program to a kernel symbol and\n// using percpu map to collect data. The eBPF program will be attached to the\n// start of the sys_execve kernel function and prints out the number of called\n// times on each cpu every second.\npackage main\n\nimport (\n\t\"log\"\n\t\"time\"\n\n\t\"github.com/cilium/ebpf/link\"\n\t\"github.com/cilium/ebpf/rlimit\"\n)\n\n//go:generate go tool bpf2go -tags linux bpf kprobe_percpu.c -- -I../headers\n\nconst mapKey uint32 = 0\n\nfunc main() {\n\n\t// Name of the kernel function to trace.\n\tfn := \"sys_execve\"\n\n\t// Allow the current process to lock memory for eBPF resources.\n\tif err := rlimit.RemoveMemlock(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Load pre-compiled programs and maps into the kernel.\n\tobjs := bpfObjects{}\n\tif err := loadBpfObjects(&objs, nil); err != nil {\n\t\tlog.Fatalf(\"loading objects: %v\", err)\n\t}\n\tdefer objs.Close()\n\n\t// Open a Kprobe at the entry point of the kernel function and attach the\n\t// pre-compiled program. Each time the kernel function enters, the program\n\t// will increment the execution counter by 1. The read loop below polls this\n\t// map value once per second.\n\tkp, err := link.Kprobe(fn, objs.KprobeExecve, nil)\n\tif err != nil {\n\t\tlog.Fatalf(\"opening kprobe: %s\", err)\n\t}\n\tdefer kp.Close()\n\n\t// Read loop reporting the total amount of times the kernel\n\t// function was entered, once per second.\n\tticker := time.NewTicker(1 * time.Second)\n\tdefer ticker.Stop()\n\n\tlog.Println(\"Waiting for events..\")\n\n\tfor range ticker.C {\n\t\tvar all_cpu_value []uint64\n\t\tif err := objs.KprobeMap.Lookup(mapKey, &all_cpu_value); err != nil {\n\t\t\tlog.Fatalf(\"reading map: %v\", err)\n\t\t}\n\t\tfor cpuid, cpuvalue := range all_cpu_value {\n\t\t\tlog.Printf(\"%s called %d times on CPU%v\\n\", fn, cpuvalue, cpuid)\n\t\t}\n\t\tlog.Printf(\"\\n\")\n\t}\n}\n"
  },
  {
    "path": "examples/kprobepin/bpf_bpfeb.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (mips || mips64 || ppc64 || s390x) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\n// loadBpf returns the embedded CollectionSpec for bpf.\nfunc loadBpf() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_BpfBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load bpf: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadBpfObjects loads bpf and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*bpfObjects\n//\t*bpfPrograms\n//\t*bpfMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadBpf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// bpfSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfSpecs struct {\n\tbpfProgramSpecs\n\tbpfMapSpecs\n\tbpfVariableSpecs\n}\n\n// bpfProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfProgramSpecs struct {\n\tKprobeExecve *ebpf.ProgramSpec `ebpf:\"kprobe_execve\"`\n}\n\n// bpfMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfMapSpecs struct {\n\tKprobeMap *ebpf.MapSpec `ebpf:\"kprobe_map\"`\n}\n\n// bpfVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfVariableSpecs struct {\n}\n\n// bpfObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfObjects struct {\n\tbpfPrograms\n\tbpfMaps\n\tbpfVariables\n}\n\nfunc (o *bpfObjects) Close() error {\n\treturn _BpfClose(\n\t\t&o.bpfPrograms,\n\t\t&o.bpfMaps,\n\t)\n}\n\n// bpfMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfMaps struct {\n\tKprobeMap *ebpf.Map `ebpf:\"kprobe_map\"`\n}\n\nfunc (m *bpfMaps) Close() error {\n\treturn _BpfClose(\n\t\tm.KprobeMap,\n\t)\n}\n\n// bpfVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfVariables struct {\n}\n\n// bpfPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfPrograms struct {\n\tKprobeExecve *ebpf.Program `ebpf:\"kprobe_execve\"`\n}\n\nfunc (p *bpfPrograms) Close() error {\n\treturn _BpfClose(\n\t\tp.KprobeExecve,\n\t)\n}\n\nfunc _BpfClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed bpf_bpfeb.o\nvar _BpfBytes []byte\n"
  },
  {
    "path": "examples/kprobepin/bpf_bpfel.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64 || wasm) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\n// loadBpf returns the embedded CollectionSpec for bpf.\nfunc loadBpf() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_BpfBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load bpf: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadBpfObjects loads bpf and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*bpfObjects\n//\t*bpfPrograms\n//\t*bpfMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadBpf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// bpfSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfSpecs struct {\n\tbpfProgramSpecs\n\tbpfMapSpecs\n\tbpfVariableSpecs\n}\n\n// bpfProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfProgramSpecs struct {\n\tKprobeExecve *ebpf.ProgramSpec `ebpf:\"kprobe_execve\"`\n}\n\n// bpfMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfMapSpecs struct {\n\tKprobeMap *ebpf.MapSpec `ebpf:\"kprobe_map\"`\n}\n\n// bpfVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfVariableSpecs struct {\n}\n\n// bpfObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfObjects struct {\n\tbpfPrograms\n\tbpfMaps\n\tbpfVariables\n}\n\nfunc (o *bpfObjects) Close() error {\n\treturn _BpfClose(\n\t\t&o.bpfPrograms,\n\t\t&o.bpfMaps,\n\t)\n}\n\n// bpfMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfMaps struct {\n\tKprobeMap *ebpf.Map `ebpf:\"kprobe_map\"`\n}\n\nfunc (m *bpfMaps) Close() error {\n\treturn _BpfClose(\n\t\tm.KprobeMap,\n\t)\n}\n\n// bpfVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfVariables struct {\n}\n\n// bpfPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfPrograms struct {\n\tKprobeExecve *ebpf.Program `ebpf:\"kprobe_execve\"`\n}\n\nfunc (p *bpfPrograms) Close() error {\n\treturn _BpfClose(\n\t\tp.KprobeExecve,\n\t)\n}\n\nfunc _BpfClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed bpf_bpfel.o\nvar _BpfBytes []byte\n"
  },
  {
    "path": "examples/kprobepin/kprobe_pin.c",
    "content": "//go:build ignore\n\n#include \"common.h\"\n\nchar __license[] SEC(\"license\") = \"Dual MIT/GPL\";\n\nstruct {\n\t__uint(type, BPF_MAP_TYPE_ARRAY);\n\t__type(key, u32);\n\t__type(value, u64);\n\t__uint(max_entries, 1);\n\t__uint(pinning, LIBBPF_PIN_BY_NAME);\n} kprobe_map SEC(\".maps\");\n\nSEC(\"kprobe/sys_execve\")\nint kprobe_execve() {\n\tu32 key     = 0;\n\tu64 initval = 1, *valp;\n\n\tvalp = bpf_map_lookup_elem(&kprobe_map, &key);\n\tif (!valp) {\n\t\tbpf_map_update_elem(&kprobe_map, &key, &initval, BPF_ANY);\n\t\treturn 0;\n\t}\n\t__sync_fetch_and_add(valp, 1);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "examples/kprobepin/main.go",
    "content": "//go:build linux\n\n// This program demonstrates attaching an eBPF program to a kernel symbol.\n// The eBPF program will be attached to the start of the sys_execve\n// kernel function and prints out the number of times it has been called\n// every second.\npackage main\n\nimport (\n\t\"log\"\n\t\"os\"\n\t\"path\"\n\t\"time\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/link\"\n\t\"github.com/cilium/ebpf/rlimit\"\n)\n\n//go:generate go tool bpf2go -tags linux bpf kprobe_pin.c -- -I../headers\n\nconst (\n\tmapKey    uint32 = 0\n\tbpfFSPath        = \"/sys/fs/bpf\"\n)\n\nfunc main() {\n\n\t// Name of the kernel function to trace.\n\tfn := \"sys_execve\"\n\n\t// Allow the current process to lock memory for eBPF resources.\n\tif err := rlimit.RemoveMemlock(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tpinPath := path.Join(bpfFSPath, fn)\n\tif err := os.MkdirAll(pinPath, os.ModePerm); err != nil {\n\t\tlog.Fatalf(\"failed to create bpf fs subpath: %+v\", err)\n\t}\n\n\tvar objs bpfObjects\n\tif err := loadBpfObjects(&objs, &ebpf.CollectionOptions{\n\t\tMaps: ebpf.MapOptions{\n\t\t\t// Pin the map to the BPF filesystem and configure the\n\t\t\t// library to automatically re-write it in the BPF\n\t\t\t// program so it can be re-used if it already exists or\n\t\t\t// create it if not\n\t\t\tPinPath: pinPath,\n\t\t},\n\t}); err != nil {\n\t\tlog.Fatalf(\"loading objects: %v\", err)\n\t}\n\tdefer objs.Close()\n\n\t// Open a Kprobe at the entry point of the kernel function and attach the\n\t// pre-compiled program. Each time the kernel function enters, the program\n\t// will increment the execution counter by 1. The read loop below polls this\n\t// map value once per second.\n\tkp, err := link.Kprobe(fn, objs.KprobeExecve, nil)\n\tif err != nil {\n\t\tlog.Fatalf(\"opening kprobe: %s\", err)\n\t}\n\tdefer kp.Close()\n\n\t// Read loop reporting the total amount of times the kernel\n\t// function was entered, once per second.\n\tticker := time.NewTicker(1 * time.Second)\n\tdefer ticker.Stop()\n\n\tlog.Println(\"Waiting for events..\")\n\n\tfor range ticker.C {\n\t\tvar value uint64\n\t\tif err := objs.KprobeMap.Lookup(mapKey, &value); err != nil {\n\t\t\tlog.Fatalf(\"reading map: %v\", err)\n\t\t}\n\t\tlog.Printf(\"%s called %d times\\n\", fn, value)\n\t}\n}\n"
  },
  {
    "path": "examples/map_in_map/main.go",
    "content": "//go:build linux\n\n// An example of using maps within maps. This example demonstrates a few\n// features. Firstly, creating eBPF map specifications in pure Go\n// (typically you'd see them being generated from a loaded ELF).\n// Additionally, creating maps and placing them in other maps (with\n// dynamically sized inner maps).\npackage main\n\nimport (\n\t\"log\"\n\t\"math/rand\"\n\t\"time\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/rlimit\"\n)\n\nconst BPF_F_INNER_MAP = 0x1000\n\nfunc main() {\n\t// Allow the current process to lock memory for eBPF resources.\n\tif err := rlimit.RemoveMemlock(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// We're creating a map spec in pure Go here, but a map spec like\n\t// this can be loaded from an ELF too.\n\touterMapSpec := ebpf.MapSpec{\n\t\tName:       \"outer_map\",\n\t\tType:       ebpf.ArrayOfMaps,\n\t\tKeySize:    4, // 4 bytes for u32\n\t\tValueSize:  4,\n\t\tMaxEntries: 5, // We'll have 5 maps inside this map\n\t\tContents:   make([]ebpf.MapKV, 5),\n\t\tInnerMap: &ebpf.MapSpec{\n\t\t\tName:      \"inner_map\",\n\t\t\tType:      ebpf.Array,\n\t\t\tKeySize:   4, // 4 bytes for u32\n\t\t\tValueSize: 4, // 4 bytes for u32\n\n\t\t\t// This flag is required for dynamically sized inner maps.\n\t\t\t// Added in linux 5.10.\n\t\t\tFlags: BPF_F_INNER_MAP,\n\n\t\t\t// We set this to 1 now, but this inner map spec gets copied\n\t\t\t// and altered later.\n\t\t\tMaxEntries: 1,\n\t\t},\n\t}\n\n\tr := rand.New(rand.NewSource(time.Now().UnixNano()))\n\n\t// For each entry we want to create in the outer map...\n\tfor i := uint32(0); i < outerMapSpec.MaxEntries; i++ {\n\t\t// Copy the inner map spec\n\t\tinnerMapSpec := outerMapSpec.InnerMap.Copy()\n\n\t\t// Randomly generate inner map length\n\t\tinnerMapSpec.MaxEntries = uint32(r.Intn(50) + 1) // Can't be zero.\n\n\t\t// populate the inner map contents\n\t\tinnerMapSpec.Contents = make([]ebpf.MapKV, innerMapSpec.MaxEntries)\n\n\t\tfor j := range innerMapSpec.Contents {\n\t\t\tinnerMapSpec.Contents[uint32(j)] = ebpf.MapKV{Key: uint32(j), Value: uint32(0xCAFE)}\n\t\t}\n\n\t\t// Create the inner map\n\t\tinnerMap, err := ebpf.NewMap(innerMapSpec)\n\t\tif err != nil {\n\t\t\tlog.Fatalf(\"inner_map: %v\", err)\n\t\t}\n\t\t// In this example we close all references to maps before exit.\n\t\t// But typically you may actually want to hold on to the map\n\t\t// reference so that you control the lifecycle of the map. For\n\t\t// the inner (nested) map though, it's safe to close the file\n\t\t// descriptor in userspace once the outer map holds a reference\n\t\t// in the kernel.\n\t\tdefer innerMap.Close()\n\n\t\t// Inner map is created successfully and lives in the kernel,\n\t\t// let's add it to the contents of the outer map spec.\n\t\touterMapSpec.Contents[i] = ebpf.MapKV{Key: i, Value: innerMap}\n\t}\n\n\t// All inner maps are created and inserted into the outer map spec,\n\t// time to create the outer map.\n\touterMap, err := ebpf.NewMap(&outerMapSpec)\n\tif err != nil {\n\t\tlog.Fatalf(\"outer_map: %v\", err)\n\t}\n\tdefer outerMap.Close()\n\n\t// The outer map is created successfully and lives happily in the\n\t// kernel. Let's iterate over the map in the kernel to see what's\n\t// been made.\n\tmapIter := outerMap.Iterate()\n\tvar outerMapKey uint32\n\tvar innerMapID ebpf.MapID\n\tfor mapIter.Next(&outerMapKey, &innerMapID) {\n\t\t// With maps that contain maps, performing a lookup doesn't give\n\t\t// you the map directly, instead it gives you an ID, which you\n\t\t// can then use to get a full map pointer.\n\t\tinnerMap, err := ebpf.NewMapFromID(innerMapID)\n\t\tif err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\n\t\tinnerMapInfo, err := innerMap.Info()\n\t\tif err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\n\t\tlog.Printf(\"outerMapKey %d, innerMap.Info: %+v\", outerMapKey, innerMapInfo)\n\t}\n}\n"
  },
  {
    "path": "examples/ringbuffer/bpf_bpfeb.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (mips || mips64 || ppc64 || s390x) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\t\"structs\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\ntype bpfEvent struct {\n\t_    structs.HostLayout\n\tPid  uint32\n\tComm [16]uint8\n}\n\n// loadBpf returns the embedded CollectionSpec for bpf.\nfunc loadBpf() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_BpfBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load bpf: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadBpfObjects loads bpf and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*bpfObjects\n//\t*bpfPrograms\n//\t*bpfMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadBpf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// bpfSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfSpecs struct {\n\tbpfProgramSpecs\n\tbpfMapSpecs\n\tbpfVariableSpecs\n}\n\n// bpfProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfProgramSpecs struct {\n\tKprobeExecve *ebpf.ProgramSpec `ebpf:\"kprobe_execve\"`\n}\n\n// bpfMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfMapSpecs struct {\n\tEvents *ebpf.MapSpec `ebpf:\"events\"`\n}\n\n// bpfVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfVariableSpecs struct {\n}\n\n// bpfObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfObjects struct {\n\tbpfPrograms\n\tbpfMaps\n\tbpfVariables\n}\n\nfunc (o *bpfObjects) Close() error {\n\treturn _BpfClose(\n\t\t&o.bpfPrograms,\n\t\t&o.bpfMaps,\n\t)\n}\n\n// bpfMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfMaps struct {\n\tEvents *ebpf.Map `ebpf:\"events\"`\n}\n\nfunc (m *bpfMaps) Close() error {\n\treturn _BpfClose(\n\t\tm.Events,\n\t)\n}\n\n// bpfVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfVariables struct {\n}\n\n// bpfPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfPrograms struct {\n\tKprobeExecve *ebpf.Program `ebpf:\"kprobe_execve\"`\n}\n\nfunc (p *bpfPrograms) Close() error {\n\treturn _BpfClose(\n\t\tp.KprobeExecve,\n\t)\n}\n\nfunc _BpfClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed bpf_bpfeb.o\nvar _BpfBytes []byte\n"
  },
  {
    "path": "examples/ringbuffer/bpf_bpfel.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64 || wasm) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\t\"structs\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\ntype bpfEvent struct {\n\t_    structs.HostLayout\n\tPid  uint32\n\tComm [16]uint8\n}\n\n// loadBpf returns the embedded CollectionSpec for bpf.\nfunc loadBpf() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_BpfBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load bpf: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadBpfObjects loads bpf and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*bpfObjects\n//\t*bpfPrograms\n//\t*bpfMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadBpf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// bpfSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfSpecs struct {\n\tbpfProgramSpecs\n\tbpfMapSpecs\n\tbpfVariableSpecs\n}\n\n// bpfProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfProgramSpecs struct {\n\tKprobeExecve *ebpf.ProgramSpec `ebpf:\"kprobe_execve\"`\n}\n\n// bpfMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfMapSpecs struct {\n\tEvents *ebpf.MapSpec `ebpf:\"events\"`\n}\n\n// bpfVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfVariableSpecs struct {\n}\n\n// bpfObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfObjects struct {\n\tbpfPrograms\n\tbpfMaps\n\tbpfVariables\n}\n\nfunc (o *bpfObjects) Close() error {\n\treturn _BpfClose(\n\t\t&o.bpfPrograms,\n\t\t&o.bpfMaps,\n\t)\n}\n\n// bpfMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfMaps struct {\n\tEvents *ebpf.Map `ebpf:\"events\"`\n}\n\nfunc (m *bpfMaps) Close() error {\n\treturn _BpfClose(\n\t\tm.Events,\n\t)\n}\n\n// bpfVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfVariables struct {\n}\n\n// bpfPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfPrograms struct {\n\tKprobeExecve *ebpf.Program `ebpf:\"kprobe_execve\"`\n}\n\nfunc (p *bpfPrograms) Close() error {\n\treturn _BpfClose(\n\t\tp.KprobeExecve,\n\t)\n}\n\nfunc _BpfClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed bpf_bpfel.o\nvar _BpfBytes []byte\n"
  },
  {
    "path": "examples/ringbuffer/main.go",
    "content": "//go:build linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"log\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n\n\t\"golang.org/x/sys/unix\"\n\n\t\"github.com/cilium/ebpf/link\"\n\t\"github.com/cilium/ebpf/ringbuf\"\n\t\"github.com/cilium/ebpf/rlimit\"\n)\n\n//go:generate go tool bpf2go -tags linux bpf ringbuffer.c -- -I../headers\n\nfunc main() {\n\t// Name of the kernel function to trace.\n\tfn := \"sys_execve\"\n\n\t// Subscribe to signals for terminating the program.\n\tstopper := make(chan os.Signal, 1)\n\tsignal.Notify(stopper, os.Interrupt, syscall.SIGTERM)\n\n\t// Allow the current process to lock memory for eBPF resources.\n\tif err := rlimit.RemoveMemlock(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Load pre-compiled programs and maps into the kernel.\n\tobjs := bpfObjects{}\n\tif err := loadBpfObjects(&objs, nil); err != nil {\n\t\tlog.Fatalf(\"loading objects: %v\", err)\n\t}\n\tdefer objs.Close()\n\n\t// Open a Kprobe at the entry point of the kernel function and attach the\n\t// pre-compiled program. Each time the kernel function enters, the program\n\t// will emit an event containing pid and command of the execved task.\n\tkp, err := link.Kprobe(fn, objs.KprobeExecve, nil)\n\tif err != nil {\n\t\tlog.Fatalf(\"opening kprobe: %s\", err)\n\t}\n\tdefer kp.Close()\n\n\t// Open a ringbuf reader from userspace RINGBUF map described in the\n\t// eBPF C program.\n\trd, err := ringbuf.NewReader(objs.Events)\n\tif err != nil {\n\t\tlog.Fatalf(\"opening ringbuf reader: %s\", err)\n\t}\n\tdefer rd.Close()\n\n\t// Close the reader when the process receives a signal, which will exit\n\t// the read loop.\n\tgo func() {\n\t\t<-stopper\n\n\t\tif err := rd.Close(); err != nil {\n\t\t\tlog.Fatalf(\"closing ringbuf reader: %s\", err)\n\t\t}\n\t}()\n\n\tlog.Println(\"Waiting for events..\")\n\n\t// bpfEvent is generated by bpf2go.\n\tvar event bpfEvent\n\tfor {\n\t\trecord, err := rd.Read()\n\t\tif err != nil {\n\t\t\tif errors.Is(err, ringbuf.ErrClosed) {\n\t\t\t\tlog.Println(\"Received signal, exiting..\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlog.Printf(\"reading from reader: %s\", err)\n\t\t\tcontinue\n\t\t}\n\n\t\t// Parse the ringbuf event entry into a bpfEvent structure.\n\t\tif err := binary.Read(bytes.NewBuffer(record.RawSample), binary.LittleEndian, &event); err != nil {\n\t\t\tlog.Printf(\"parsing ringbuf event: %s\", err)\n\t\t\tcontinue\n\t\t}\n\n\t\tlog.Printf(\"pid: %d\\tcomm: %s\\n\", event.Pid, unix.ByteSliceToString(event.Comm[:]))\n\t}\n}\n"
  },
  {
    "path": "examples/ringbuffer/ringbuffer.c",
    "content": "//go:build ignore\n\n#include \"common.h\"\n\n#ifndef TASK_COMM_LEN\n#define TASK_COMM_LEN 16\n#endif\n\nchar __license[] SEC(\"license\") = \"Dual MIT/GPL\";\n\nstruct event {\n\tu32 pid;\n\tu8 comm[TASK_COMM_LEN];\n};\n\nstruct {\n\t__uint(type, BPF_MAP_TYPE_RINGBUF);\n\t__uint(max_entries, 1 << 24);\n\t__type(value, struct event);\n} events SEC(\".maps\");\n\nSEC(\"kprobe/sys_execve\")\nint kprobe_execve(struct pt_regs *ctx) {\n\tu64 id   = bpf_get_current_pid_tgid();\n\tu32 tgid = id >> 32;\n\tstruct event *task_info;\n\n\ttask_info = bpf_ringbuf_reserve(&events, sizeof(struct event), 0);\n\tif (!task_info) {\n\t\treturn 0;\n\t}\n\n\ttask_info->pid = tgid;\n\tbpf_get_current_comm(&task_info->comm, TASK_COMM_LEN);\n\n\tbpf_ringbuf_submit(task_info, 0);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "examples/sched_ext/bpf_bpfeb.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (mips || mips64 || ppc64 || s390x) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\n// loadBpf returns the embedded CollectionSpec for bpf.\nfunc loadBpf() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_BpfBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load bpf: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadBpfObjects loads bpf and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*bpfObjects\n//\t*bpfPrograms\n//\t*bpfMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadBpf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// bpfSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfSpecs struct {\n\tbpfProgramSpecs\n\tbpfMapSpecs\n\tbpfVariableSpecs\n}\n\n// bpfProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfProgramSpecs struct {\n}\n\n// bpfMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfMapSpecs struct {\n\tMinimalSched *ebpf.MapSpec `ebpf:\"minimal_sched\"`\n}\n\n// bpfVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfVariableSpecs struct {\n}\n\n// bpfObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfObjects struct {\n\tbpfPrograms\n\tbpfMaps\n\tbpfVariables\n}\n\nfunc (o *bpfObjects) Close() error {\n\treturn _BpfClose(\n\t\t&o.bpfPrograms,\n\t\t&o.bpfMaps,\n\t)\n}\n\n// bpfMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfMaps struct {\n\tMinimalSched *ebpf.Map `ebpf:\"minimal_sched\"`\n}\n\nfunc (m *bpfMaps) Close() error {\n\treturn _BpfClose(\n\t\tm.MinimalSched,\n\t)\n}\n\n// bpfVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfVariables struct {\n}\n\n// bpfPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfPrograms struct {\n}\n\nfunc (p *bpfPrograms) Close() error {\n\treturn _BpfClose()\n}\n\nfunc _BpfClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed bpf_bpfeb.o\nvar _BpfBytes []byte\n"
  },
  {
    "path": "examples/sched_ext/bpf_bpfel.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64 || wasm) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\n// loadBpf returns the embedded CollectionSpec for bpf.\nfunc loadBpf() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_BpfBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load bpf: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadBpfObjects loads bpf and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*bpfObjects\n//\t*bpfPrograms\n//\t*bpfMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadBpf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// bpfSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfSpecs struct {\n\tbpfProgramSpecs\n\tbpfMapSpecs\n\tbpfVariableSpecs\n}\n\n// bpfProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfProgramSpecs struct {\n}\n\n// bpfMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfMapSpecs struct {\n\tMinimalSched *ebpf.MapSpec `ebpf:\"minimal_sched\"`\n}\n\n// bpfVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfVariableSpecs struct {\n}\n\n// bpfObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfObjects struct {\n\tbpfPrograms\n\tbpfMaps\n\tbpfVariables\n}\n\nfunc (o *bpfObjects) Close() error {\n\treturn _BpfClose(\n\t\t&o.bpfPrograms,\n\t\t&o.bpfMaps,\n\t)\n}\n\n// bpfMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfMaps struct {\n\tMinimalSched *ebpf.Map `ebpf:\"minimal_sched\"`\n}\n\nfunc (m *bpfMaps) Close() error {\n\treturn _BpfClose(\n\t\tm.MinimalSched,\n\t)\n}\n\n// bpfVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfVariables struct {\n}\n\n// bpfPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfPrograms struct {\n}\n\nfunc (p *bpfPrograms) Close() error {\n\treturn _BpfClose()\n}\n\nfunc _BpfClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed bpf_bpfel.o\nvar _BpfBytes []byte\n"
  },
  {
    "path": "examples/sched_ext/main.go",
    "content": "//go:build linux\n\npackage main\n\nimport (\n\t\"log\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n\n\t\"github.com/cilium/ebpf/link\"\n\t\"github.com/cilium/ebpf/rlimit\"\n)\n\n//go:generate go tool bpf2go -no-global-types -tags linux bpf sched_ext.c -- -I../headers/\n\n// Load a minimal defining sched_ext_ops map\n//\n// After run this program, you can find the current status of the BPF scheduler can be determined as follows:\n//\n//\t# cat /sys/kernel/sched_ext/state\n//\tenabled\n//\t# cat /sys/kernel/sched_ext/root/ops\n//\tmiminal\nfunc main() {\n\tif err := rlimit.RemoveMemlock(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tobjs := bpfObjects{}\n\tif err := loadBpfObjects(&objs, nil); err != nil {\n\t\tlog.Fatalf(\"loading objects: %v\", err)\n\t}\n\tdefer objs.Close()\n\n\tm := objs.MinimalSched\n\tl, err := link.AttachStructOps(link.StructOpsOptions{Map: m})\n\tif err != nil {\n\t\tlog.Fatalf(\"failed to attach sched_ext: %s\", err)\n\t}\n\tdefer l.Close()\n\n\tstopper := make(chan os.Signal, 1)\n\tsignal.Notify(stopper, os.Interrupt, syscall.SIGTERM)\n\n\t<-stopper\n\n\tlog.Print(\"quit sched_ext\")\n}\n"
  },
  {
    "path": "examples/sched_ext/sched_ext.c",
    "content": "//go:build ignore\n\n#include \"bpf_endian.h\"\n#include \"common.h\"\n\nchar __license[] SEC(\"license\") = \"Dual MIT/GPL\";\n\nstruct sched_ext_ops {\n\tchar name[128];\n};\n\nSEC(\".struct_ops.link\")\nstruct sched_ext_ops minimal_sched = {\n\t.name = \"minimal\",\n};\n"
  },
  {
    "path": "examples/tcprtt/bpf_bpfeb.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (mips || mips64 || ppc64 || s390x) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\t\"structs\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\ntype bpfEvent struct {\n\t_     structs.HostLayout\n\tSport uint16\n\tDport uint16\n\tSaddr uint32\n\tDaddr uint32\n\tSrtt  uint32\n}\n\n// loadBpf returns the embedded CollectionSpec for bpf.\nfunc loadBpf() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_BpfBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load bpf: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadBpfObjects loads bpf and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*bpfObjects\n//\t*bpfPrograms\n//\t*bpfMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadBpf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// bpfSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfSpecs struct {\n\tbpfProgramSpecs\n\tbpfMapSpecs\n\tbpfVariableSpecs\n}\n\n// bpfProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfProgramSpecs struct {\n\tTcpClose *ebpf.ProgramSpec `ebpf:\"tcp_close\"`\n}\n\n// bpfMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfMapSpecs struct {\n\tEvents *ebpf.MapSpec `ebpf:\"events\"`\n}\n\n// bpfVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfVariableSpecs struct {\n}\n\n// bpfObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfObjects struct {\n\tbpfPrograms\n\tbpfMaps\n\tbpfVariables\n}\n\nfunc (o *bpfObjects) Close() error {\n\treturn _BpfClose(\n\t\t&o.bpfPrograms,\n\t\t&o.bpfMaps,\n\t)\n}\n\n// bpfMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfMaps struct {\n\tEvents *ebpf.Map `ebpf:\"events\"`\n}\n\nfunc (m *bpfMaps) Close() error {\n\treturn _BpfClose(\n\t\tm.Events,\n\t)\n}\n\n// bpfVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfVariables struct {\n}\n\n// bpfPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfPrograms struct {\n\tTcpClose *ebpf.Program `ebpf:\"tcp_close\"`\n}\n\nfunc (p *bpfPrograms) Close() error {\n\treturn _BpfClose(\n\t\tp.TcpClose,\n\t)\n}\n\nfunc _BpfClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed bpf_bpfeb.o\nvar _BpfBytes []byte\n"
  },
  {
    "path": "examples/tcprtt/bpf_bpfel.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64 || wasm) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\t\"structs\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\ntype bpfEvent struct {\n\t_     structs.HostLayout\n\tSport uint16\n\tDport uint16\n\tSaddr uint32\n\tDaddr uint32\n\tSrtt  uint32\n}\n\n// loadBpf returns the embedded CollectionSpec for bpf.\nfunc loadBpf() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_BpfBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load bpf: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadBpfObjects loads bpf and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*bpfObjects\n//\t*bpfPrograms\n//\t*bpfMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadBpf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// bpfSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfSpecs struct {\n\tbpfProgramSpecs\n\tbpfMapSpecs\n\tbpfVariableSpecs\n}\n\n// bpfProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfProgramSpecs struct {\n\tTcpClose *ebpf.ProgramSpec `ebpf:\"tcp_close\"`\n}\n\n// bpfMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfMapSpecs struct {\n\tEvents *ebpf.MapSpec `ebpf:\"events\"`\n}\n\n// bpfVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfVariableSpecs struct {\n}\n\n// bpfObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfObjects struct {\n\tbpfPrograms\n\tbpfMaps\n\tbpfVariables\n}\n\nfunc (o *bpfObjects) Close() error {\n\treturn _BpfClose(\n\t\t&o.bpfPrograms,\n\t\t&o.bpfMaps,\n\t)\n}\n\n// bpfMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfMaps struct {\n\tEvents *ebpf.Map `ebpf:\"events\"`\n}\n\nfunc (m *bpfMaps) Close() error {\n\treturn _BpfClose(\n\t\tm.Events,\n\t)\n}\n\n// bpfVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfVariables struct {\n}\n\n// bpfPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfPrograms struct {\n\tTcpClose *ebpf.Program `ebpf:\"tcp_close\"`\n}\n\nfunc (p *bpfPrograms) Close() error {\n\treturn _BpfClose(\n\t\tp.TcpClose,\n\t)\n}\n\nfunc _BpfClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed bpf_bpfel.o\nvar _BpfBytes []byte\n"
  },
  {
    "path": "examples/tcprtt/main.go",
    "content": "//go:build linux\n\n// This program demonstrates attaching a fentry eBPF program to\n// tcp_close and reading the RTT from the TCP socket using CO-RE helpers.\n// It prints the IPs/ports/RTT information\n// once the host closes a TCP connection.\n// It supports only IPv4 for this example.\n//\n// Sample output:\n//\n// examples# go run -exec sudo ./tcprtt\n// 2022/03/19 22:30:34 Src addr        Port   -> Dest addr       Port   RTT\n// 2022/03/19 22:30:36 10.0.1.205      50578  -> 117.102.109.186 5201   195\n// 2022/03/19 22:30:53 10.0.1.205      0      -> 89.84.1.178     9200   30\n// 2022/03/19 22:30:53 10.0.1.205      36022  -> 89.84.1.178     9200   28\npackage main\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"log\"\n\t\"net\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n\n\t\"github.com/cilium/ebpf/link\"\n\t\"github.com/cilium/ebpf/ringbuf\"\n\t\"github.com/cilium/ebpf/rlimit\"\n)\n\n//go:generate go tool bpf2go -tags linux bpf tcprtt.c -- -I../headers\n\nfunc main() {\n\tstopper := make(chan os.Signal, 1)\n\tsignal.Notify(stopper, os.Interrupt, syscall.SIGTERM)\n\n\t// Allow the current process to lock memory for eBPF resources.\n\tif err := rlimit.RemoveMemlock(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Load pre-compiled programs and maps into the kernel.\n\tobjs := bpfObjects{}\n\tif err := loadBpfObjects(&objs, nil); err != nil {\n\t\tlog.Fatalf(\"loading objects: %v\", err)\n\t}\n\tdefer objs.Close()\n\n\tl, err := link.AttachTracing(link.TracingOptions{\n\t\tProgram: objs.TcpClose,\n\t})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer l.Close()\n\n\trd, err := ringbuf.NewReader(objs.Events)\n\tif err != nil {\n\t\tlog.Fatalf(\"opening ringbuf reader: %s\", err)\n\t}\n\tdefer rd.Close()\n\n\tlog.Printf(\"%-15s %-6s -> %-15s %-6s %-6s\",\n\t\t\"Src addr\",\n\t\t\"Port\",\n\t\t\"Dest addr\",\n\t\t\"Port\",\n\t\t\"RTT\",\n\t)\n\tgo readLoop(rd)\n\n\t// Wait\n\t<-stopper\n}\n\nfunc readLoop(rd *ringbuf.Reader) {\n\t// bpfEvent is generated by bpf2go.\n\tvar event bpfEvent\n\tfor {\n\t\trecord, err := rd.Read()\n\t\tif err != nil {\n\t\t\tif errors.Is(err, ringbuf.ErrClosed) {\n\t\t\t\tlog.Println(\"received signal, exiting..\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlog.Printf(\"reading from reader: %s\", err)\n\t\t\tcontinue\n\t\t}\n\n\t\t// Parse the ringbuf event entry into a bpfEvent structure.\n\t\tif err := binary.Read(bytes.NewBuffer(record.RawSample), binary.NativeEndian, &event); err != nil {\n\t\t\tlog.Printf(\"parsing ringbuf event: %s\", err)\n\t\t\tcontinue\n\t\t}\n\n\t\tlog.Printf(\"%-15s %-6d -> %-15s %-6d %-6d\",\n\t\t\tintToIP(event.Saddr),\n\t\t\tevent.Sport,\n\t\t\tintToIP(event.Daddr),\n\t\t\tevent.Dport,\n\t\t\tevent.Srtt,\n\t\t)\n\t}\n}\n\n// intToIP converts IPv4 number to net.IP\nfunc intToIP(ipNum uint32) net.IP {\n\tip := make(net.IP, 4)\n\tbinary.NativeEndian.PutUint32(ip, ipNum)\n\treturn ip\n}\n"
  },
  {
    "path": "examples/tcprtt/tcprtt.c",
    "content": "//go:build ignore\n\n#include \"common.h\"\n\n#include \"bpf_endian.h\"\n#include \"bpf_tracing.h\"\n\n#define AF_INET 2\n\nchar __license[] SEC(\"license\") = \"Dual MIT/GPL\";\n\n/**\n * For CO-RE relocatable eBPF programs, __attribute__((preserve_access_index))\n * preserves the offset of the specified fields in the original kernel struct.\n * So here we don't need to include \"vmlinux.h\". Instead we only need to define\n * the kernel struct and their fields the eBPF program actually requires.\n *\n * Also note that BTF-enabled programs like fentry, fexit, fmod_ret, tp_btf,\n * lsm, etc. declared using the BPF_PROG macro can read kernel memory without\n * needing to call bpf_probe_read*().\n */\n\n/**\n * struct sock_common is the minimal network layer representation of sockets.\n * This is a simplified copy of the kernel's struct sock_common.\n * This copy contains only the fields needed for this example to\n * fetch the source and destination port numbers and IP addresses.\n */\nstruct sock_common {\n\tunion {\n\t\tstruct {\n\t\t\t// skc_daddr is destination IP address\n\t\t\t__be32 skc_daddr;\n\t\t\t// skc_rcv_saddr is the source IP address\n\t\t\t__be32 skc_rcv_saddr;\n\t\t};\n\t};\n\tunion {\n\t\tstruct {\n\t\t\t// skc_dport is the destination TCP/UDP port\n\t\t\t__be16 skc_dport;\n\t\t\t// skc_num is the source TCP/UDP port\n\t\t\t__u16 skc_num;\n\t\t};\n\t};\n\t// skc_family is the network address family (2 for IPV4)\n\tshort unsigned int skc_family;\n} __attribute__((preserve_access_index));\n\n/**\n * struct sock is the network layer representation of sockets.\n * This is a simplified copy of the kernel's struct sock.\n * This copy is needed only to access struct sock_common.\n */\nstruct sock {\n\tstruct sock_common __sk_common;\n} __attribute__((preserve_access_index));\n\n/**\n * struct tcp_sock is the Linux representation of a TCP socket.\n * This is a simplified copy of the kernel's struct tcp_sock.\n * For this example we only need srtt_us to read the smoothed RTT.\n */\nstruct tcp_sock {\n\tu32 srtt_us;\n} __attribute__((preserve_access_index));\n\nstruct {\n\t__uint(type, BPF_MAP_TYPE_RINGBUF);\n\t__uint(max_entries, 1 << 24);\n\t__type(value, struct event);\n} events SEC(\".maps\");\n\n/**\n * The sample submitted to userspace over a ring buffer.\n * Emit struct event's type info into the ELF's BTF so bpf2go\n * can generate a Go type from it.\n */\nstruct event {\n\tu16 sport;\n\tu16 dport;\n\tu32 saddr;\n\tu32 daddr;\n\tu32 srtt;\n};\n\nSEC(\"fentry/tcp_close\")\nint BPF_PROG(tcp_close, struct sock *sk) {\n\tif (sk->__sk_common.skc_family != AF_INET) {\n\t\treturn 0;\n\t}\n\n\t// The input struct sock is actually a tcp_sock, so we can type-cast\n\tstruct tcp_sock *ts = bpf_skc_to_tcp_sock(sk);\n\tif (!ts) {\n\t\treturn 0;\n\t}\n\n\tstruct event *tcp_info;\n\ttcp_info = bpf_ringbuf_reserve(&events, sizeof(struct event), 0);\n\tif (!tcp_info) {\n\t\treturn 0;\n\t}\n\n\ttcp_info->saddr = sk->__sk_common.skc_rcv_saddr;\n\ttcp_info->daddr = sk->__sk_common.skc_daddr;\n\ttcp_info->dport = bpf_ntohs(sk->__sk_common.skc_dport);\n\ttcp_info->sport = sk->__sk_common.skc_num;\n\n\ttcp_info->srtt = ts->srtt_us >> 3;\n\ttcp_info->srtt /= 1000;\n\n\tbpf_ringbuf_submit(tcp_info, 0);\n\n\treturn 0;\n}\n"
  },
  {
    "path": "examples/tcprtt_sockops/bpf_bpfeb.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (mips || mips64 || ppc64 || s390x) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\t\"structs\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\ntype bpfRttEvent struct {\n\t_     structs.HostLayout\n\tSport uint16\n\tDport uint16\n\tSaddr uint32\n\tDaddr uint32\n\tSrtt  uint32\n}\n\ntype bpfSkInfo struct {\n\t_      structs.HostLayout\n\tSkKey  bpfSkKey\n\tSkType uint8\n\t_      [3]byte\n}\n\ntype bpfSkKey struct {\n\t_          structs.HostLayout\n\tLocalIp4   uint32\n\tRemoteIp4  uint32\n\tLocalPort  uint32\n\tRemotePort uint32\n}\n\n// loadBpf returns the embedded CollectionSpec for bpf.\nfunc loadBpf() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_BpfBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load bpf: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadBpfObjects loads bpf and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*bpfObjects\n//\t*bpfPrograms\n//\t*bpfMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadBpf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// bpfSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfSpecs struct {\n\tbpfProgramSpecs\n\tbpfMapSpecs\n\tbpfVariableSpecs\n}\n\n// bpfProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfProgramSpecs struct {\n\tBpfSockopsCb *ebpf.ProgramSpec `ebpf:\"bpf_sockops_cb\"`\n}\n\n// bpfMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfMapSpecs struct {\n\tMapEstabSk *ebpf.MapSpec `ebpf:\"map_estab_sk\"`\n\tRttEvents  *ebpf.MapSpec `ebpf:\"rtt_events\"`\n}\n\n// bpfVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfVariableSpecs struct {\n}\n\n// bpfObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfObjects struct {\n\tbpfPrograms\n\tbpfMaps\n\tbpfVariables\n}\n\nfunc (o *bpfObjects) Close() error {\n\treturn _BpfClose(\n\t\t&o.bpfPrograms,\n\t\t&o.bpfMaps,\n\t)\n}\n\n// bpfMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfMaps struct {\n\tMapEstabSk *ebpf.Map `ebpf:\"map_estab_sk\"`\n\tRttEvents  *ebpf.Map `ebpf:\"rtt_events\"`\n}\n\nfunc (m *bpfMaps) Close() error {\n\treturn _BpfClose(\n\t\tm.MapEstabSk,\n\t\tm.RttEvents,\n\t)\n}\n\n// bpfVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfVariables struct {\n}\n\n// bpfPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfPrograms struct {\n\tBpfSockopsCb *ebpf.Program `ebpf:\"bpf_sockops_cb\"`\n}\n\nfunc (p *bpfPrograms) Close() error {\n\treturn _BpfClose(\n\t\tp.BpfSockopsCb,\n\t)\n}\n\nfunc _BpfClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed bpf_bpfeb.o\nvar _BpfBytes []byte\n"
  },
  {
    "path": "examples/tcprtt_sockops/bpf_bpfel.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64 || wasm) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\t\"structs\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\ntype bpfRttEvent struct {\n\t_     structs.HostLayout\n\tSport uint16\n\tDport uint16\n\tSaddr uint32\n\tDaddr uint32\n\tSrtt  uint32\n}\n\ntype bpfSkInfo struct {\n\t_      structs.HostLayout\n\tSkKey  bpfSkKey\n\tSkType uint8\n\t_      [3]byte\n}\n\ntype bpfSkKey struct {\n\t_          structs.HostLayout\n\tLocalIp4   uint32\n\tRemoteIp4  uint32\n\tLocalPort  uint32\n\tRemotePort uint32\n}\n\n// loadBpf returns the embedded CollectionSpec for bpf.\nfunc loadBpf() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_BpfBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load bpf: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadBpfObjects loads bpf and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*bpfObjects\n//\t*bpfPrograms\n//\t*bpfMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadBpf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// bpfSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfSpecs struct {\n\tbpfProgramSpecs\n\tbpfMapSpecs\n\tbpfVariableSpecs\n}\n\n// bpfProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfProgramSpecs struct {\n\tBpfSockopsCb *ebpf.ProgramSpec `ebpf:\"bpf_sockops_cb\"`\n}\n\n// bpfMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfMapSpecs struct {\n\tMapEstabSk *ebpf.MapSpec `ebpf:\"map_estab_sk\"`\n\tRttEvents  *ebpf.MapSpec `ebpf:\"rtt_events\"`\n}\n\n// bpfVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfVariableSpecs struct {\n}\n\n// bpfObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfObjects struct {\n\tbpfPrograms\n\tbpfMaps\n\tbpfVariables\n}\n\nfunc (o *bpfObjects) Close() error {\n\treturn _BpfClose(\n\t\t&o.bpfPrograms,\n\t\t&o.bpfMaps,\n\t)\n}\n\n// bpfMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfMaps struct {\n\tMapEstabSk *ebpf.Map `ebpf:\"map_estab_sk\"`\n\tRttEvents  *ebpf.Map `ebpf:\"rtt_events\"`\n}\n\nfunc (m *bpfMaps) Close() error {\n\treturn _BpfClose(\n\t\tm.MapEstabSk,\n\t\tm.RttEvents,\n\t)\n}\n\n// bpfVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfVariables struct {\n}\n\n// bpfPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfPrograms struct {\n\tBpfSockopsCb *ebpf.Program `ebpf:\"bpf_sockops_cb\"`\n}\n\nfunc (p *bpfPrograms) Close() error {\n\treturn _BpfClose(\n\t\tp.BpfSockopsCb,\n\t)\n}\n\nfunc _BpfClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed bpf_bpfel.o\nvar _BpfBytes []byte\n"
  },
  {
    "path": "examples/tcprtt_sockops/bpf_sockops.h",
    "content": "/*\n * Note that this header file contains a subset of kernel \n * definitions needed for the tcprtt_sockops example.\n */\n#ifndef BPF_SOCKOPS_H\n#define BPF_SOCKOPS_H\n\n/*\n * Copy of TCP states.\n * See: https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/bpf.h#L6347.\n */\nenum {\n\tTCP_ESTABLISHED = 1,\n\tTCP_SYN_SENT = 2,\n\tTCP_SYN_RECV = 3,\n\tTCP_FIN_WAIT1 = 4,\n\tTCP_FIN_WAIT2 = 5,\n\tTCP_TIME_WAIT = 6,\n\tTCP_CLOSE = 7,\n\tTCP_CLOSE_WAIT = 8,\n\tTCP_LAST_ACK = 9,\n\tTCP_LISTEN = 10,\n\tTCP_CLOSING = 11,\n\tTCP_NEW_SYN_RECV = 12,\n\tTCP_MAX_STATES = 13,\n};\n\n/*\n * Copy of sock_ops operations.\n * See: https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/bpf.h#L6233.\n */\nenum {\n\tBPF_SOCK_OPS_VOID                   = 0,\n\tBPF_SOCK_OPS_TIMEOUT_INIT           = 1,\n\tBPF_SOCK_OPS_RWND_INIT              = 2,\n\tBPF_SOCK_OPS_TCP_CONNECT_CB         = 3,\n\tBPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB  = 4,\n\tBPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB = 5,\n\tBPF_SOCK_OPS_NEEDS_ECN              = 6,\n\tBPF_SOCK_OPS_BASE_RTT               = 7,\n\tBPF_SOCK_OPS_RTO_CB                 = 8,\n\tBPF_SOCK_OPS_RETRANS_CB             = 9,\n\tBPF_SOCK_OPS_STATE_CB               = 10,\n\tBPF_SOCK_OPS_TCP_LISTEN_CB          = 11,\n\tBPF_SOCK_OPS_RTT_CB                 = 12,\n\tBPF_SOCK_OPS_PARSE_HDR_OPT_CB       = 13,\n\tBPF_SOCK_OPS_HDR_OPT_LEN_CB         = 14,\n\tBPF_SOCK_OPS_WRITE_HDR_OPT_CB       = 15,\n};\n\n/*\n * Copy of definitions for bpf_sock_ops_cb_flags.\n * See: https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/bpf.h#L6178.\n */\nenum {\n\tBPF_SOCK_OPS_RTO_CB_FLAG                   = 1,\n\tBPF_SOCK_OPS_RETRANS_CB_FLAG               = 2,\n\tBPF_SOCK_OPS_STATE_CB_FLAG                 = 4,\n\tBPF_SOCK_OPS_RTT_CB_FLAG                   = 8,\n\tBPF_SOCK_OPS_PARSE_ALL_HDR_OPT_CB_FLAG     = 16,\n\tBPF_SOCK_OPS_PARSE_UNKNOWN_HDR_OPT_CB_FLAG = 32,\n\tBPF_SOCK_OPS_WRITE_HDR_OPT_CB_FLAG         = 64,\n\tBPF_SOCK_OPS_ALL_CB_FLAGS                  = 127,\n};\n\n/*\n * Copy of bpf.h's bpf_sock_ops with minimal subset \n * of fields used by the tcprtt_sockops example.\n * See: https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/bpf.h#L6101.\n */\nstruct bpf_sock_ops {\n\t__u32 op;\n\tunion {\n\t\t__u32 args[4];\n\t\t__u32 reply;\n\t\t__u32 replylong[4];\n\t};\n\t__u32 family;\n\t__u32 remote_ip4;\n\t__u32 local_ip4;\n\t__u32 remote_port;\n\t__u32 local_port;\n\t__u32 srtt_us;\n    __u32 bpf_sock_ops_cb_flags;\n} __attribute__((preserve_access_index));\n\n#endif"
  },
  {
    "path": "examples/tcprtt_sockops/main.go",
    "content": "//go:build linux\n\n// This program demonstrates attaching an eBPF program to\n// a cgroupv2 path and using sockops to process TCP socket events.\n// It prints the IPs/ports/RTT information every time TCP sockets\n// update their internal RTT value.\n// It supports only IPv4 for this example.\n//\n// Sample output:\n//\n// examples# go run -exec sudo ./tcprtt_sockops\n// 2022/08/14 20:58:03 eBPF program loaded and attached on cgroup /sys/fs/cgroup/unified\n// 2022/08/14 20:58:03 Src addr        Port   -> Dest addr       Port   RTT (ms)\n// 2022/08/14 20:58:09 10.0.1.205      54844  -> 20.42.73.25     443    67\n// 2022/08/14 20:58:09 10.0.1.205      54844  -> 20.42.73.25     443    67\n// 2022/08/14 20:58:33 10.0.1.205      38620  -> 140.82.121.4    443    26\n// 2022/08/14 20:58:33 10.0.1.205      38620  -> 140.82.121.4    443    26\n// 2022/08/14 20:58:43 34.67.40.146    45380  -> 10.0.1.205      5201   106\n// 2022/08/14 20:58:43 34.67.40.146    45380  -> 10.0.1.205      5201   106\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"log\"\n\t\"net\"\n\t\"os\"\n\t\"os/signal\"\n\t\"path/filepath\"\n\t\"syscall\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/link\"\n\t\"github.com/cilium/ebpf/ringbuf\"\n\t\"github.com/cilium/ebpf/rlimit\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\n//go:generate go tool bpf2go -tags linux -tags \"linux\" bpf tcprtt_sockops.c -- -I../headers\n\nfunc main() {\n\tstopper := make(chan os.Signal, 1)\n\tsignal.Notify(stopper, os.Interrupt, syscall.SIGTERM)\n\n\t// Allow the current process to lock memory for eBPF resources.\n\tif err := rlimit.RemoveMemlock(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Find the path to a cgroup enabled to version 2\n\tcgroupPath, err := findCgroupPath()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Load pre-compiled programs and maps into the kernel.\n\tobjs := bpfObjects{}\n\tif err := loadBpfObjects(&objs, nil); err != nil {\n\t\tlog.Fatalf(\"loading objects: %v\", err)\n\t}\n\tdefer objs.Close()\n\n\t// Attach ebpf program to a cgroupv2\n\tl, err := link.AttachCgroup(link.CgroupOptions{\n\t\tPath:    cgroupPath,\n\t\tProgram: objs.BpfSockopsCb,\n\t\tAttach:  ebpf.AttachCGroupSockOps,\n\t})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer l.Close()\n\n\tlog.Printf(\"eBPF program loaded and attached on cgroup %s\\n\", cgroupPath)\n\n\trd, err := ringbuf.NewReader(objs.RttEvents)\n\tif err != nil {\n\t\tlog.Fatalf(\"opening ringbuf reader: %s\", err)\n\t}\n\tdefer rd.Close()\n\n\tlog.Printf(\"%-15s %-6s -> %-15s %-6s %-6s\",\n\t\t\"Src addr\",\n\t\t\"Port\",\n\t\t\"Dest addr\",\n\t\t\"Port\",\n\t\t\"RTT (ms)\",\n\t)\n\tgo readLoop(rd)\n\n\t// Wait\n\t<-stopper\n}\n\nfunc findCgroupPath() (string, error) {\n\tcgroupPath := \"/sys/fs/cgroup\"\n\n\tvar st syscall.Statfs_t\n\terr := syscall.Statfs(cgroupPath, &st)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tisCgroupV2Enabled := st.Type == unix.CGROUP2_SUPER_MAGIC\n\tif !isCgroupV2Enabled {\n\t\tcgroupPath = filepath.Join(cgroupPath, \"unified\")\n\t}\n\treturn cgroupPath, nil\n}\n\nfunc readLoop(rd *ringbuf.Reader) {\n\t// bpfRttEvent is generated by bpf2go.\n\tvar event bpfRttEvent\n\tfor {\n\t\trecord, err := rd.Read()\n\t\tif err != nil {\n\t\t\tif errors.Is(err, ringbuf.ErrClosed) {\n\t\t\t\tlog.Println(\"received signal, exiting..\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlog.Printf(\"reading from reader: %s\", err)\n\t\t\tcontinue\n\t\t}\n\n\t\t// Parse the ringbuf event entry into a bpfRttEvent structure.\n\t\tif err := binary.Read(bytes.NewBuffer(record.RawSample), binary.NativeEndian, &event); err != nil {\n\t\t\tlog.Printf(\"parsing ringbuf event: %s\", err)\n\t\t\tcontinue\n\t\t}\n\n\t\tlog.Printf(\"%-15s %-6d -> %-15s %-6d %-6d\",\n\t\t\tintToIP(event.Saddr),\n\t\t\tevent.Sport,\n\t\t\tintToIP(event.Daddr),\n\t\t\tevent.Dport,\n\t\t\tevent.Srtt,\n\t\t)\n\t}\n}\n\n// intToIP converts IPv4 number to net.IP\nfunc intToIP(ipNum uint32) net.IP {\n\tip := make(net.IP, 4)\n\tbinary.BigEndian.PutUint32(ip, ipNum)\n\treturn ip\n}\n"
  },
  {
    "path": "examples/tcprtt_sockops/tcprtt_sockops.c",
    "content": "//go:build ignore\n\n#include \"common.h\"\n\n#include \"bpf_endian.h\"\n#include \"bpf_sockops.h\"\n#include \"bpf_tracing.h\"\n\n#define AF_INET 2\n#define SOCKOPS_MAP_SIZE 65535\n\nchar __license[] SEC(\"license\") = \"Dual MIT/GPL\";\n\nenum {\n\tSOCK_TYPE_ACTIVE  = 0,\n\tSOCK_TYPE_PASSIVE = 1,\n};\n\nstruct {\n\t__uint(type, BPF_MAP_TYPE_HASH);\n\t__uint(max_entries, SOCKOPS_MAP_SIZE);\n\t__type(key, struct sk_key);\n\t__type(value, struct sk_info);\n} map_estab_sk SEC(\".maps\");\n\nstruct sk_key {\n\tu32 local_ip4;\n\tu32 remote_ip4;\n\tu32 local_port;\n\tu32 remote_port;\n};\n\nstruct sk_info {\n\tstruct sk_key sk_key;\n\tu8 sk_type;\n};\n\nstruct {\n\t__uint(type, BPF_MAP_TYPE_RINGBUF);\n\t__uint(max_entries, 1 << 24);\n\t__type(value, struct rtt_event);\n} rtt_events SEC(\".maps\");\n\nstruct rtt_event {\n\tu16 sport;\n\tu16 dport;\n\tu32 saddr;\n\tu32 daddr;\n\tu32 srtt;\n};\n\nstatic inline void init_sk_key(struct bpf_sock_ops *skops, struct sk_key *sk_key) {\n\tsk_key->local_ip4   = bpf_ntohl(skops->local_ip4);\n\tsk_key->remote_ip4  = bpf_ntohl(skops->remote_ip4);\n\tsk_key->local_port  = skops->local_port;\n\tsk_key->remote_port = bpf_ntohl(skops->remote_port);\n}\n\nstatic inline void bpf_sock_ops_establish_cb(struct bpf_sock_ops *skops, u8 sock_type) {\n\tint err;\n\tstruct sk_info sk_info = {};\n\t// Only process IPv4 sockets\n\tif (skops == NULL || skops->family != AF_INET)\n\t\treturn;\n\n\t// Initialize the 4-tuple key\n\tinit_sk_key(skops, &sk_info.sk_key);\n\tsk_info.sk_type = sock_type;\n\n\t// Store the socket info in map using the 4-tuple as key\n\t// We keep track of TCP connections in 'established' state\n\terr = bpf_map_update_elem(&map_estab_sk, &sk_info.sk_key, &sk_info, BPF_NOEXIST);\n\tif (err != 0) {\n\t\t// Storing the 4-tuple in map has failed, return early.\n\t\t// This can happen in case the 4-tuple already exists in the map (i.e. BPF_NOEXIST flag)\n\t\treturn;\n\t}\n\n\t// Enable sockops callbacks for RTT and TCP state change\n\tbpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_RTT_CB_FLAG | BPF_SOCK_OPS_STATE_CB_FLAG);\n}\n\nstatic inline void bpf_sock_ops_rtt_cb(struct bpf_sock_ops *skops) {\n\tstruct sk_key sk_key = {};\n\tstruct sk_info *sk_info;\n\tstruct rtt_event *rtt_event;\n\n\t// Initialize the 4-tuple key\n\tinit_sk_key(skops, &sk_key);\n\n\t// Retrieve the socket info from map of established connections\n\tsk_info = bpf_map_lookup_elem(&map_estab_sk, &sk_key);\n\tif (!sk_info)\n\t\treturn;\n\n\trtt_event = bpf_ringbuf_reserve(&rtt_events, sizeof(struct rtt_event), 0);\n\tif (!rtt_event) {\n\t\treturn;\n\t}\n\n\tswitch (sk_info->sk_type) {\n\tcase SOCK_TYPE_ACTIVE:\n\t\t// If socket is 'active', 'local' means 'source'\n\t\t// and 'remote' means 'destination'\n\t\trtt_event->saddr = sk_info->sk_key.local_ip4;\n\t\trtt_event->daddr = sk_info->sk_key.remote_ip4;\n\t\trtt_event->sport = sk_info->sk_key.local_port;\n\t\trtt_event->dport = sk_info->sk_key.remote_port;\n\t\tbreak;\n\tcase SOCK_TYPE_PASSIVE:\n\t\t// If socket is 'passive', 'local' means 'destination'\n\t\t// and 'remote' means 'source'\n\t\trtt_event->saddr = sk_info->sk_key.remote_ip4;\n\t\trtt_event->daddr = sk_info->sk_key.local_ip4;\n\t\trtt_event->sport = sk_info->sk_key.remote_port;\n\t\trtt_event->dport = sk_info->sk_key.local_port;\n\t\tbreak;\n\t}\n\n\t// Extract smoothed RTT\n\trtt_event->srtt = skops->srtt_us >> 3;\n\trtt_event->srtt /= 1000;\n\n\t// Send RTT event data to userspace app via ring buffer\n\tbpf_ringbuf_submit(rtt_event, 0);\n}\n\nstatic inline void bpf_sock_ops_state_cb(struct bpf_sock_ops *skops) {\n\tstruct sk_key sk_key = {};\n\n\t// Socket changed state. args[0] stores the previous state.\n\t// Perform cleanup of map entry if socket is exiting\n\t// the 'established' state,\n\tif (skops->args[0] == TCP_ESTABLISHED) {\n\t\tinit_sk_key(skops, &sk_key);\n\t\tbpf_map_delete_elem(&map_estab_sk, &sk_key);\n\t}\n}\n\nSEC(\"sockops\")\nint bpf_sockops_cb(struct bpf_sock_ops *skops) {\n\tu32 op;\n\top = skops->op;\n\n\tswitch (op) {\n\tcase BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:\n\t\tbpf_sock_ops_establish_cb(skops, SOCK_TYPE_ACTIVE);\n\t\tbreak;\n\tcase BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:\n\t\tbpf_sock_ops_establish_cb(skops, SOCK_TYPE_PASSIVE);\n\t\tbreak;\n\tcase BPF_SOCK_OPS_RTT_CB:\n\t\tbpf_sock_ops_rtt_cb(skops);\n\t\tbreak;\n\tcase BPF_SOCK_OPS_STATE_CB:\n\t\tbpf_sock_ops_state_cb(skops);\n\t\tbreak;\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "examples/tcx/bpf_bpfeb.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (mips || mips64 || ppc64 || s390x) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\n// loadBpf returns the embedded CollectionSpec for bpf.\nfunc loadBpf() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_BpfBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load bpf: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadBpfObjects loads bpf and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*bpfObjects\n//\t*bpfPrograms\n//\t*bpfMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadBpf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// bpfSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfSpecs struct {\n\tbpfProgramSpecs\n\tbpfMapSpecs\n\tbpfVariableSpecs\n}\n\n// bpfProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfProgramSpecs struct {\n\tEgressProgFunc  *ebpf.ProgramSpec `ebpf:\"egress_prog_func\"`\n\tIngressProgFunc *ebpf.ProgramSpec `ebpf:\"ingress_prog_func\"`\n}\n\n// bpfMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfMapSpecs struct {\n}\n\n// bpfVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfVariableSpecs struct {\n\tEgressPktCount  *ebpf.VariableSpec `ebpf:\"egress_pkt_count\"`\n\tIngressPktCount *ebpf.VariableSpec `ebpf:\"ingress_pkt_count\"`\n}\n\n// bpfObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfObjects struct {\n\tbpfPrograms\n\tbpfMaps\n\tbpfVariables\n}\n\nfunc (o *bpfObjects) Close() error {\n\treturn _BpfClose(\n\t\t&o.bpfPrograms,\n\t\t&o.bpfMaps,\n\t)\n}\n\n// bpfMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfMaps struct {\n}\n\nfunc (m *bpfMaps) Close() error {\n\treturn _BpfClose()\n}\n\n// bpfVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfVariables struct {\n\tEgressPktCount  *ebpf.Variable `ebpf:\"egress_pkt_count\"`\n\tIngressPktCount *ebpf.Variable `ebpf:\"ingress_pkt_count\"`\n}\n\n// bpfPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfPrograms struct {\n\tEgressProgFunc  *ebpf.Program `ebpf:\"egress_prog_func\"`\n\tIngressProgFunc *ebpf.Program `ebpf:\"ingress_prog_func\"`\n}\n\nfunc (p *bpfPrograms) Close() error {\n\treturn _BpfClose(\n\t\tp.EgressProgFunc,\n\t\tp.IngressProgFunc,\n\t)\n}\n\nfunc _BpfClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed bpf_bpfeb.o\nvar _BpfBytes []byte\n"
  },
  {
    "path": "examples/tcx/bpf_bpfel.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64 || wasm) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\n// loadBpf returns the embedded CollectionSpec for bpf.\nfunc loadBpf() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_BpfBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load bpf: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadBpfObjects loads bpf and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*bpfObjects\n//\t*bpfPrograms\n//\t*bpfMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadBpf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// bpfSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfSpecs struct {\n\tbpfProgramSpecs\n\tbpfMapSpecs\n\tbpfVariableSpecs\n}\n\n// bpfProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfProgramSpecs struct {\n\tEgressProgFunc  *ebpf.ProgramSpec `ebpf:\"egress_prog_func\"`\n\tIngressProgFunc *ebpf.ProgramSpec `ebpf:\"ingress_prog_func\"`\n}\n\n// bpfMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfMapSpecs struct {\n}\n\n// bpfVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfVariableSpecs struct {\n\tEgressPktCount  *ebpf.VariableSpec `ebpf:\"egress_pkt_count\"`\n\tIngressPktCount *ebpf.VariableSpec `ebpf:\"ingress_pkt_count\"`\n}\n\n// bpfObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfObjects struct {\n\tbpfPrograms\n\tbpfMaps\n\tbpfVariables\n}\n\nfunc (o *bpfObjects) Close() error {\n\treturn _BpfClose(\n\t\t&o.bpfPrograms,\n\t\t&o.bpfMaps,\n\t)\n}\n\n// bpfMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfMaps struct {\n}\n\nfunc (m *bpfMaps) Close() error {\n\treturn _BpfClose()\n}\n\n// bpfVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfVariables struct {\n\tEgressPktCount  *ebpf.Variable `ebpf:\"egress_pkt_count\"`\n\tIngressPktCount *ebpf.Variable `ebpf:\"ingress_pkt_count\"`\n}\n\n// bpfPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfPrograms struct {\n\tEgressProgFunc  *ebpf.Program `ebpf:\"egress_prog_func\"`\n\tIngressProgFunc *ebpf.Program `ebpf:\"ingress_prog_func\"`\n}\n\nfunc (p *bpfPrograms) Close() error {\n\treturn _BpfClose(\n\t\tp.EgressProgFunc,\n\t\tp.IngressProgFunc,\n\t)\n}\n\nfunc _BpfClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed bpf_bpfel.o\nvar _BpfBytes []byte\n"
  },
  {
    "path": "examples/tcx/main.go",
    "content": "//go:build linux\n\n// This program demonstrates attaching an eBPF program to a network interface\n// with Linux TCX (Traffic Control with eBPF). The program counts ingress and egress\n// packets using two variables. The userspace program (Go code in this file)\n// prints the contents of the two variables to stdout every second.\n// This example depends on tcx bpf_link, available in Linux kernel version 6.6 or newer.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/link\"\n)\n\n//go:generate go tool bpf2go -tags linux bpf tcx.c -- -I../headers\nfunc main() {\n\tif len(os.Args) < 2 {\n\t\tlog.Fatalf(\"Please specify a network interface\")\n\t}\n\n\t// Look up the network interface by name.\n\tifaceName := os.Args[1]\n\tiface, err := net.InterfaceByName(ifaceName)\n\tif err != nil {\n\t\tlog.Fatalf(\"lookup network iface %q: %s\", ifaceName, err)\n\t}\n\n\t// Load pre-compiled programs into the kernel.\n\tobjs := bpfObjects{}\n\tif err := loadBpfObjects(&objs, nil); err != nil {\n\t\tlog.Fatalf(\"loading objects: %s\", err)\n\t}\n\tdefer objs.Close()\n\n\t// Attach the program to Ingress TC.\n\tl, err := link.AttachTCX(link.TCXOptions{\n\t\tInterface: iface.Index,\n\t\tProgram:   objs.IngressProgFunc,\n\t\tAttach:    ebpf.AttachTCXIngress,\n\t})\n\tif err != nil {\n\t\tlog.Fatalf(\"could not attach TCx program: %s\", err)\n\t}\n\tdefer l.Close()\n\n\tlog.Printf(\"Attached TCx program to INGRESS iface %q (index %d)\", iface.Name, iface.Index)\n\n\t// Attach the program to Egress TC.\n\tl2, err := link.AttachTCX(link.TCXOptions{\n\t\tInterface: iface.Index,\n\t\tProgram:   objs.EgressProgFunc,\n\t\tAttach:    ebpf.AttachTCXEgress,\n\t})\n\tif err != nil {\n\t\tlog.Fatalf(\"could not attach TCx program: %s\", err)\n\t}\n\tdefer l2.Close()\n\n\tlog.Printf(\"Attached TCx program to EGRESS iface %q (index %d)\", iface.Name, iface.Index)\n\tlog.Printf(\"Press Ctrl-C to exit and remove the program\")\n\n\t// Print the contents of the counters maps.\n\tticker := time.NewTicker(1 * time.Second)\n\tdefer ticker.Stop()\n\tfor range ticker.C {\n\t\ts, err := formatCounters(objs.IngressPktCount, objs.EgressPktCount)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Error reading map: %s\", err)\n\t\t\tcontinue\n\t\t}\n\n\t\tlog.Printf(\"Packet Count: %s\\n\", s)\n\t}\n}\n\nfunc formatCounters(ingressVar, egressVar *ebpf.Variable) (string, error) {\n\tvar (\n\t\tingressPacketCount uint64\n\t\tegressPacketCount  uint64\n\t)\n\n\t// retrieve value from the ingress map\n\tif err := ingressVar.Get(&ingressPacketCount); err != nil {\n\t\treturn \"\", err\n\t}\n\n\t// retrieve value from the egress map\n\tif err := egressVar.Get(&egressPacketCount); err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn fmt.Sprintf(\"%10v Ingress, %10v Egress\", ingressPacketCount, egressPacketCount), nil\n}\n"
  },
  {
    "path": "examples/tcx/tcx.c",
    "content": "//go:build ignore\n\n#include \"common.h\"\n\nchar __license[] SEC(\"license\") = \"Dual MIT/GPL\";\n\n__u64 ingress_pkt_count = 0;\n__u64 egress_pkt_count  = 0;\n\nSEC(\"tc\")\nint ingress_prog_func(struct __sk_buff *skb) {\n\t__sync_fetch_and_add(&ingress_pkt_count, 1);\n\treturn TC_ACT_OK;\n}\n\nSEC(\"tc\")\nint egress_prog_func(struct __sk_buff *skb) {\n\t__sync_fetch_and_add(&egress_pkt_count, 1);\n\treturn TC_ACT_OK;\n}\n"
  },
  {
    "path": "examples/tracepoint_in_c/bpf_bpfeb.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (mips || mips64 || ppc64 || s390x) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\n// loadBpf returns the embedded CollectionSpec for bpf.\nfunc loadBpf() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_BpfBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load bpf: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadBpfObjects loads bpf and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*bpfObjects\n//\t*bpfPrograms\n//\t*bpfMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadBpf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// bpfSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfSpecs struct {\n\tbpfProgramSpecs\n\tbpfMapSpecs\n\tbpfVariableSpecs\n}\n\n// bpfProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfProgramSpecs struct {\n\tMmPageAlloc *ebpf.ProgramSpec `ebpf:\"mm_page_alloc\"`\n}\n\n// bpfMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfMapSpecs struct {\n\tCountingMap *ebpf.MapSpec `ebpf:\"counting_map\"`\n}\n\n// bpfVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfVariableSpecs struct {\n}\n\n// bpfObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfObjects struct {\n\tbpfPrograms\n\tbpfMaps\n\tbpfVariables\n}\n\nfunc (o *bpfObjects) Close() error {\n\treturn _BpfClose(\n\t\t&o.bpfPrograms,\n\t\t&o.bpfMaps,\n\t)\n}\n\n// bpfMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfMaps struct {\n\tCountingMap *ebpf.Map `ebpf:\"counting_map\"`\n}\n\nfunc (m *bpfMaps) Close() error {\n\treturn _BpfClose(\n\t\tm.CountingMap,\n\t)\n}\n\n// bpfVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfVariables struct {\n}\n\n// bpfPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfPrograms struct {\n\tMmPageAlloc *ebpf.Program `ebpf:\"mm_page_alloc\"`\n}\n\nfunc (p *bpfPrograms) Close() error {\n\treturn _BpfClose(\n\t\tp.MmPageAlloc,\n\t)\n}\n\nfunc _BpfClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed bpf_bpfeb.o\nvar _BpfBytes []byte\n"
  },
  {
    "path": "examples/tracepoint_in_c/bpf_bpfel.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64 || wasm) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\n// loadBpf returns the embedded CollectionSpec for bpf.\nfunc loadBpf() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_BpfBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load bpf: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadBpfObjects loads bpf and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*bpfObjects\n//\t*bpfPrograms\n//\t*bpfMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadBpf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// bpfSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfSpecs struct {\n\tbpfProgramSpecs\n\tbpfMapSpecs\n\tbpfVariableSpecs\n}\n\n// bpfProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfProgramSpecs struct {\n\tMmPageAlloc *ebpf.ProgramSpec `ebpf:\"mm_page_alloc\"`\n}\n\n// bpfMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfMapSpecs struct {\n\tCountingMap *ebpf.MapSpec `ebpf:\"counting_map\"`\n}\n\n// bpfVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfVariableSpecs struct {\n}\n\n// bpfObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfObjects struct {\n\tbpfPrograms\n\tbpfMaps\n\tbpfVariables\n}\n\nfunc (o *bpfObjects) Close() error {\n\treturn _BpfClose(\n\t\t&o.bpfPrograms,\n\t\t&o.bpfMaps,\n\t)\n}\n\n// bpfMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfMaps struct {\n\tCountingMap *ebpf.Map `ebpf:\"counting_map\"`\n}\n\nfunc (m *bpfMaps) Close() error {\n\treturn _BpfClose(\n\t\tm.CountingMap,\n\t)\n}\n\n// bpfVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfVariables struct {\n}\n\n// bpfPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfPrograms struct {\n\tMmPageAlloc *ebpf.Program `ebpf:\"mm_page_alloc\"`\n}\n\nfunc (p *bpfPrograms) Close() error {\n\treturn _BpfClose(\n\t\tp.MmPageAlloc,\n\t)\n}\n\nfunc _BpfClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed bpf_bpfel.o\nvar _BpfBytes []byte\n"
  },
  {
    "path": "examples/tracepoint_in_c/main.go",
    "content": "//go:build linux\n\n// This program demonstrates attaching an eBPF program to a kernel tracepoint.\n// The eBPF program will be attached to the page allocation tracepoint and\n// prints out the number of times it has been reached. The tracepoint fields\n// are printed into /sys/kernel/tracing/trace_pipe.\npackage main\n\nimport (\n\t\"log\"\n\t\"time\"\n\n\t\"github.com/cilium/ebpf/link\"\n\t\"github.com/cilium/ebpf/rlimit\"\n)\n\n//go:generate go tool bpf2go -tags linux bpf tracepoint.c -- -I../headers\n\nconst mapKey uint32 = 0\n\nfunc main() {\n\t// Allow the current process to lock memory for eBPF resources.\n\tif err := rlimit.RemoveMemlock(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Load pre-compiled programs and maps into the kernel.\n\tobjs := bpfObjects{}\n\tif err := loadBpfObjects(&objs, nil); err != nil {\n\t\tlog.Fatalf(\"loading objects: %v\", err)\n\t}\n\tdefer objs.Close()\n\n\t// Open a tracepoint and attach the pre-compiled program. Each time\n\t// the kernel function enters, the program will increment the execution\n\t// counter by 1. The read loop below polls this map value once per\n\t// second.\n\t// The first two arguments are taken from the following pathname:\n\t// /sys/kernel/tracing/events/kmem/mm_page_alloc\n\tkp, err := link.Tracepoint(\"kmem\", \"mm_page_alloc\", objs.MmPageAlloc, nil)\n\tif err != nil {\n\t\tlog.Fatalf(\"opening tracepoint: %s\", err)\n\t}\n\tdefer kp.Close()\n\n\t// Read loop reporting the total amount of times the kernel\n\t// function was entered, once per second.\n\tticker := time.NewTicker(1 * time.Second)\n\tdefer ticker.Stop()\n\n\tlog.Println(\"Waiting for events..\")\n\tfor range ticker.C {\n\t\tvar value uint64\n\t\tif err := objs.CountingMap.Lookup(mapKey, &value); err != nil {\n\t\t\tlog.Fatalf(\"reading map: %v\", err)\n\t\t}\n\t\tlog.Printf(\"%v times\", value)\n\t}\n}\n"
  },
  {
    "path": "examples/tracepoint_in_c/tracepoint.c",
    "content": "//go:build ignore\n\n#include \"common.h\"\n\nchar __license[] SEC(\"license\") = \"Dual MIT/GPL\";\n\nstruct {\n\t__uint(type, BPF_MAP_TYPE_ARRAY);\n\t__type(key, u32);\n\t__type(value, u64);\n\t__uint(max_entries, 1);\n} counting_map SEC(\".maps\");\n\n// This struct is defined according to the following format file:\n// /sys/kernel/tracing/events/kmem/mm_page_alloc/format\nstruct alloc_info {\n\t/* The first 8 bytes is not allowed to read */\n\tunsigned long pad;\n\n\tunsigned long pfn;\n\tunsigned int order;\n\tunsigned int gfp_flags;\n\tint migratetype;\n};\n\n// This tracepoint is defined in mm/page_alloc.c:__alloc_pages_nodemask()\n// Userspace pathname: /sys/kernel/tracing/events/kmem/mm_page_alloc\nSEC(\"tracepoint/kmem/mm_page_alloc\")\nint mm_page_alloc(struct alloc_info *info) {\n\tu32 key     = 0;\n\tu64 initval = 1, *valp;\n\n\tvalp = bpf_map_lookup_elem(&counting_map, &key);\n\tif (!valp) {\n\t\tbpf_map_update_elem(&counting_map, &key, &initval, BPF_ANY);\n\t\treturn 0;\n\t}\n\t__sync_fetch_and_add(valp, 1);\n\treturn 0;\n}\n"
  },
  {
    "path": "examples/tracepoint_in_go/main.go",
    "content": "//go:build linux\n\n// This program demonstrates how to attach an eBPF program to a tracepoint.\n// The program is attached to the syscall/sys_enter_openat tracepoint and\n// prints out the integer 123 every time the syscall is entered.\npackage main\n\nimport (\n\t\"errors\"\n\t\"log\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/link\"\n\t\"github.com/cilium/ebpf/perf\"\n\t\"github.com/cilium/ebpf/rlimit\"\n)\n\n// Metadata for the eBPF program used in this example.\nvar progSpec = &ebpf.ProgramSpec{\n\tName:    \"my_trace_prog\", // non-unique name, will appear in `bpftool prog list` while attached\n\tType:    ebpf.TracePoint, // only TracePoint programs can be attached to trace events created by link.Tracepoint()\n\tLicense: \"GPL\",           // license must be GPL for calling kernel helpers like perf_event_output\n}\n\nfunc main() {\n\n\t// Subscribe to signals for terminating the program.\n\tstopper := make(chan os.Signal, 1)\n\tsignal.Notify(stopper, os.Interrupt, syscall.SIGTERM)\n\n\t// Allow the current process to lock memory for eBPF resources.\n\tif err := rlimit.RemoveMemlock(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Create a perf event array for the kernel to write perf records to.\n\t// These records will be read by userspace below.\n\tevents, err := ebpf.NewMap(&ebpf.MapSpec{\n\t\tType: ebpf.PerfEventArray,\n\t\tName: \"my_perf_array\",\n\t})\n\tif err != nil {\n\t\tlog.Fatalf(\"creating perf event array: %s\", err)\n\t}\n\tdefer events.Close()\n\n\t// Open a perf reader from userspace into the perf event array\n\t// created earlier.\n\trd, err := perf.NewReader(events, os.Getpagesize())\n\tif err != nil {\n\t\tlog.Fatalf(\"creating event reader: %s\", err)\n\t}\n\tdefer rd.Close()\n\n\t// Close the reader when the process receives a signal, which will exit\n\t// the read loop.\n\tgo func() {\n\t\t<-stopper\n\t\trd.Close()\n\t}()\n\n\t// Minimal program that writes the static value '123' to the perf ring on\n\t// each event. Note that this program refers to the file descriptor of\n\t// the perf event array created above, which needs to be created prior to the\n\t// program being verified by and inserted into the kernel.\n\tprogSpec.Instructions = asm.Instructions{\n\t\t// store the integer 123 at FP[-8]\n\t\tasm.Mov.Imm(asm.R2, 123),\n\t\tasm.StoreMem(asm.RFP, -8, asm.R2, asm.Word),\n\n\t\t// load registers with arguments for call of FnPerfEventOutput\n\t\tasm.LoadMapPtr(asm.R2, events.FD()), // file descriptor of the perf event array\n\t\tasm.LoadImm(asm.R3, 0xffffffff, asm.DWord),\n\t\tasm.Mov.Reg(asm.R4, asm.RFP),\n\t\tasm.Add.Imm(asm.R4, -8),\n\t\tasm.Mov.Imm(asm.R5, 4),\n\n\t\t// call FnPerfEventOutput, an eBPF kernel helper\n\t\tasm.FnPerfEventOutput.Call(),\n\n\t\t// set exit code to 0\n\t\tasm.Mov.Imm(asm.R0, 0),\n\t\tasm.Return(),\n\t}\n\n\t// Instantiate and insert the program into the kernel.\n\tprog, err := ebpf.NewProgram(progSpec)\n\tif err != nil {\n\t\tlog.Fatalf(\"creating ebpf program: %s\", err)\n\t}\n\tdefer prog.Close()\n\n\t// Open a trace event based on a pre-existing kernel hook (tracepoint).\n\t// Each time a userspace program uses the 'openat()' syscall, the eBPF\n\t// program specified above will be executed and a '123' value will appear\n\t// in the perf ring.\n\ttp, err := link.Tracepoint(\"syscalls\", \"sys_enter_openat\", prog, nil)\n\tif err != nil {\n\t\tlog.Fatalf(\"opening tracepoint: %s\", err)\n\t}\n\tdefer tp.Close()\n\n\tlog.Println(\"Waiting for events..\")\n\n\tfor {\n\t\trecord, err := rd.Read()\n\t\tif err != nil {\n\t\t\tif errors.Is(err, perf.ErrClosed) {\n\t\t\t\tlog.Println(\"Received signal, exiting..\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlog.Printf(\"reading from reader: %s\", err)\n\t\t\tcontinue\n\t\t}\n\n\t\tlog.Println(\"Record:\", record)\n\t}\n}\n"
  },
  {
    "path": "examples/uretprobe/bpf_x86_bpfel.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (386 || amd64) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\t\"structs\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\ntype bpfEvent struct {\n\t_    structs.HostLayout\n\tPid  uint32\n\tLine [80]uint8\n}\n\n// loadBpf returns the embedded CollectionSpec for bpf.\nfunc loadBpf() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_BpfBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load bpf: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadBpfObjects loads bpf and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*bpfObjects\n//\t*bpfPrograms\n//\t*bpfMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadBpf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// bpfSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfSpecs struct {\n\tbpfProgramSpecs\n\tbpfMapSpecs\n\tbpfVariableSpecs\n}\n\n// bpfProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfProgramSpecs struct {\n\tUretprobeBashReadline *ebpf.ProgramSpec `ebpf:\"uretprobe_bash_readline\"`\n}\n\n// bpfMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfMapSpecs struct {\n\tEvents *ebpf.MapSpec `ebpf:\"events\"`\n}\n\n// bpfVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfVariableSpecs struct {\n}\n\n// bpfObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfObjects struct {\n\tbpfPrograms\n\tbpfMaps\n\tbpfVariables\n}\n\nfunc (o *bpfObjects) Close() error {\n\treturn _BpfClose(\n\t\t&o.bpfPrograms,\n\t\t&o.bpfMaps,\n\t)\n}\n\n// bpfMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfMaps struct {\n\tEvents *ebpf.Map `ebpf:\"events\"`\n}\n\nfunc (m *bpfMaps) Close() error {\n\treturn _BpfClose(\n\t\tm.Events,\n\t)\n}\n\n// bpfVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfVariables struct {\n}\n\n// bpfPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfPrograms struct {\n\tUretprobeBashReadline *ebpf.Program `ebpf:\"uretprobe_bash_readline\"`\n}\n\nfunc (p *bpfPrograms) Close() error {\n\treturn _BpfClose(\n\t\tp.UretprobeBashReadline,\n\t)\n}\n\nfunc _BpfClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed bpf_x86_bpfel.o\nvar _BpfBytes []byte\n"
  },
  {
    "path": "examples/uretprobe/main.go",
    "content": "// This program demonstrates how to attach an eBPF program to a uretprobe.\n// The program will be attached to the 'readline' symbol in the binary '/bin/bash' and print out\n// the line which 'readline' functions returns to the caller.\n\n//go:build amd64 && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"log\"\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n\n\t\"golang.org/x/sys/unix\"\n\n\t\"github.com/cilium/ebpf/link\"\n\t\"github.com/cilium/ebpf/perf\"\n\t\"github.com/cilium/ebpf/rlimit\"\n)\n\n//go:generate go tool bpf2go -tags linux -target amd64 bpf uretprobe.c -- -I../headers\n\nconst (\n\t// The path to the ELF binary containing the function to trace.\n\t// On some distributions, the 'readline' function is provided by a\n\t// dynamically-linked library, so the path of the library will need\n\t// to be specified instead, e.g. /usr/lib/libreadline.so.8.\n\t// Use `ldd /bin/bash` to find these paths.\n\tbinPath = \"/bin/bash\"\n\tsymbol  = \"readline\"\n)\n\nfunc main() {\n\tstopper := make(chan os.Signal, 1)\n\tsignal.Notify(stopper, os.Interrupt, syscall.SIGTERM)\n\n\t// Allow the current process to lock memory for eBPF resources.\n\tif err := rlimit.RemoveMemlock(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Load pre-compiled programs and maps into the kernel.\n\tobjs := bpfObjects{}\n\tif err := loadBpfObjects(&objs, nil); err != nil {\n\t\tlog.Fatalf(\"loading objects: %s\", err)\n\t}\n\tdefer objs.Close()\n\n\t// Open an ELF binary and read its symbols.\n\tex, err := link.OpenExecutable(binPath)\n\tif err != nil {\n\t\tlog.Fatalf(\"opening executable: %s\", err)\n\t}\n\n\t// Open a Uretprobe at the exit point of the symbol and attach\n\t// the pre-compiled eBPF program to it.\n\tup, err := ex.Uretprobe(symbol, objs.UretprobeBashReadline, nil)\n\tif err != nil {\n\t\tlog.Fatalf(\"creating uretprobe: %s\", err)\n\t}\n\tdefer up.Close()\n\n\t// Open a perf event reader from userspace on the PERF_EVENT_ARRAY map\n\t// described in the eBPF C program.\n\trd, err := perf.NewReader(objs.Events, os.Getpagesize())\n\tif err != nil {\n\t\tlog.Fatalf(\"creating perf event reader: %s\", err)\n\t}\n\tdefer rd.Close()\n\n\tgo func() {\n\t\t// Wait for a signal and close the perf reader,\n\t\t// which will interrupt rd.Read() and make the program exit.\n\t\t<-stopper\n\t\tlog.Println(\"Received signal, exiting program..\")\n\n\t\tif err := rd.Close(); err != nil {\n\t\t\tlog.Fatalf(\"closing perf event reader: %s\", err)\n\t\t}\n\t}()\n\n\tlog.Printf(\"Listening for events..\")\n\n\t// bpfEvent is generated by bpf2go.\n\tvar event bpfEvent\n\tfor {\n\t\trecord, err := rd.Read()\n\t\tif err != nil {\n\t\t\tif errors.Is(err, perf.ErrClosed) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlog.Printf(\"reading from perf event reader: %s\", err)\n\t\t\tcontinue\n\t\t}\n\n\t\tif record.LostSamples != 0 {\n\t\t\tlog.Printf(\"perf event ring buffer full, dropped %d samples\", record.LostSamples)\n\t\t\tcontinue\n\t\t}\n\n\t\t// Parse the perf event entry into a bpfEvent structure.\n\t\tif err := binary.Read(bytes.NewBuffer(record.RawSample), binary.LittleEndian, &event); err != nil {\n\t\t\tlog.Printf(\"parsing perf event: %s\", err)\n\t\t\tcontinue\n\t\t}\n\n\t\tlog.Printf(\"%s:%s return value: %s\", binPath, symbol, unix.ByteSliceToString(event.Line[:]))\n\t}\n}\n"
  },
  {
    "path": "examples/uretprobe/uretprobe.c",
    "content": "//go:build ignore\n\n#include \"common.h\"\n\n#include \"bpf_tracing.h\"\n\nchar __license[] SEC(\"license\") = \"Dual MIT/GPL\";\n\nstruct event {\n\tu32 pid;\n\tu8 line[80];\n};\n\nstruct {\n\t__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);\n\t__type(value, struct event);\n} events SEC(\".maps\");\n\nSEC(\"uretprobe/bash_readline\")\nint uretprobe_bash_readline(struct pt_regs *ctx) {\n\tstruct event event;\n\n\tevent.pid = bpf_get_current_pid_tgid();\n\tbpf_probe_read(&event.line, sizeof(event.line), (void *)PT_REGS_RC(ctx));\n\n\tbpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event));\n\n\treturn 0;\n}\n"
  },
  {
    "path": "examples/xdp/bpf_bpfeb.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (mips || mips64 || ppc64 || s390x) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\n// loadBpf returns the embedded CollectionSpec for bpf.\nfunc loadBpf() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_BpfBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load bpf: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadBpfObjects loads bpf and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*bpfObjects\n//\t*bpfPrograms\n//\t*bpfMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadBpf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// bpfSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfSpecs struct {\n\tbpfProgramSpecs\n\tbpfMapSpecs\n\tbpfVariableSpecs\n}\n\n// bpfProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfProgramSpecs struct {\n\tXdpProgFunc *ebpf.ProgramSpec `ebpf:\"xdp_prog_func\"`\n}\n\n// bpfMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfMapSpecs struct {\n\tXdpStatsMap *ebpf.MapSpec `ebpf:\"xdp_stats_map\"`\n}\n\n// bpfVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfVariableSpecs struct {\n}\n\n// bpfObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfObjects struct {\n\tbpfPrograms\n\tbpfMaps\n\tbpfVariables\n}\n\nfunc (o *bpfObjects) Close() error {\n\treturn _BpfClose(\n\t\t&o.bpfPrograms,\n\t\t&o.bpfMaps,\n\t)\n}\n\n// bpfMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfMaps struct {\n\tXdpStatsMap *ebpf.Map `ebpf:\"xdp_stats_map\"`\n}\n\nfunc (m *bpfMaps) Close() error {\n\treturn _BpfClose(\n\t\tm.XdpStatsMap,\n\t)\n}\n\n// bpfVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfVariables struct {\n}\n\n// bpfPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfPrograms struct {\n\tXdpProgFunc *ebpf.Program `ebpf:\"xdp_prog_func\"`\n}\n\nfunc (p *bpfPrograms) Close() error {\n\treturn _BpfClose(\n\t\tp.XdpProgFunc,\n\t)\n}\n\nfunc _BpfClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed bpf_bpfeb.o\nvar _BpfBytes []byte\n"
  },
  {
    "path": "examples/xdp/bpf_bpfel.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64 || wasm) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\n// loadBpf returns the embedded CollectionSpec for bpf.\nfunc loadBpf() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_BpfBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load bpf: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadBpfObjects loads bpf and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*bpfObjects\n//\t*bpfPrograms\n//\t*bpfMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadBpf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// bpfSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfSpecs struct {\n\tbpfProgramSpecs\n\tbpfMapSpecs\n\tbpfVariableSpecs\n}\n\n// bpfProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfProgramSpecs struct {\n\tXdpProgFunc *ebpf.ProgramSpec `ebpf:\"xdp_prog_func\"`\n}\n\n// bpfMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfMapSpecs struct {\n\tXdpStatsMap *ebpf.MapSpec `ebpf:\"xdp_stats_map\"`\n}\n\n// bpfVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfVariableSpecs struct {\n}\n\n// bpfObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfObjects struct {\n\tbpfPrograms\n\tbpfMaps\n\tbpfVariables\n}\n\nfunc (o *bpfObjects) Close() error {\n\treturn _BpfClose(\n\t\t&o.bpfPrograms,\n\t\t&o.bpfMaps,\n\t)\n}\n\n// bpfMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfMaps struct {\n\tXdpStatsMap *ebpf.Map `ebpf:\"xdp_stats_map\"`\n}\n\nfunc (m *bpfMaps) Close() error {\n\treturn _BpfClose(\n\t\tm.XdpStatsMap,\n\t)\n}\n\n// bpfVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfVariables struct {\n}\n\n// bpfPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfPrograms struct {\n\tXdpProgFunc *ebpf.Program `ebpf:\"xdp_prog_func\"`\n}\n\nfunc (p *bpfPrograms) Close() error {\n\treturn _BpfClose(\n\t\tp.XdpProgFunc,\n\t)\n}\n\nfunc _BpfClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed bpf_bpfel.o\nvar _BpfBytes []byte\n"
  },
  {
    "path": "examples/xdp/main.go",
    "content": "//go:build linux\n\n// This program demonstrates attaching an eBPF program to a network interface\n// with XDP (eXpress Data Path). The program parses the IPv4 source address\n// from packets and writes the packet count by IP to an LRU hash map.\n// The userspace program (Go code in this file) prints the contents\n// of the map to stdout every second.\n// It is possible to modify the XDP program to drop or redirect packets\n// as well -- give it a try!\n// This example depends on bpf_link, available in Linux kernel version 5.7 or newer.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net\"\n\t\"net/netip\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/link\"\n)\n\n//go:generate go tool bpf2go -tags linux bpf xdp.c -- -I../headers\n\nfunc main() {\n\tif len(os.Args) < 2 {\n\t\tlog.Fatalf(\"Please specify a network interface\")\n\t}\n\n\t// Look up the network interface by name.\n\tifaceName := os.Args[1]\n\tiface, err := net.InterfaceByName(ifaceName)\n\tif err != nil {\n\t\tlog.Fatalf(\"lookup network iface %q: %s\", ifaceName, err)\n\t}\n\n\t// Load pre-compiled programs into the kernel.\n\tobjs := bpfObjects{}\n\tif err := loadBpfObjects(&objs, nil); err != nil {\n\t\tlog.Fatalf(\"loading objects: %s\", err)\n\t}\n\tdefer objs.Close()\n\n\t// Attach the program.\n\tl, err := link.AttachXDP(link.XDPOptions{\n\t\tProgram:   objs.XdpProgFunc,\n\t\tInterface: iface.Index,\n\t})\n\tif err != nil {\n\t\tlog.Fatalf(\"could not attach XDP program: %s\", err)\n\t}\n\tdefer l.Close()\n\n\tlog.Printf(\"Attached XDP program to iface %q (index %d)\", iface.Name, iface.Index)\n\tlog.Printf(\"Press Ctrl-C to exit and remove the program\")\n\n\t// Print the contents of the BPF hash map (source IP address -> packet count).\n\tticker := time.NewTicker(1 * time.Second)\n\tdefer ticker.Stop()\n\tfor range ticker.C {\n\t\ts, err := formatMapContents(objs.XdpStatsMap)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Error reading map: %s\", err)\n\t\t\tcontinue\n\t\t}\n\t\tlog.Printf(\"Map contents:\\n%s\", s)\n\t}\n}\n\nfunc formatMapContents(m *ebpf.Map) (string, error) {\n\tvar (\n\t\tsb  strings.Builder\n\t\tkey netip.Addr\n\t\tval uint32\n\t)\n\titer := m.Iterate()\n\tfor iter.Next(&key, &val) {\n\t\tsourceIP := key // IPv4 source address in network byte order.\n\t\tpacketCount := val\n\t\tsb.WriteString(fmt.Sprintf(\"\\t%s => %d\\n\", sourceIP, packetCount))\n\t}\n\treturn sb.String(), iter.Err()\n}\n"
  },
  {
    "path": "examples/xdp/xdp.c",
    "content": "//go:build ignore\n\n#include \"bpf_endian.h\"\n#include \"common.h\"\n\nchar __license[] SEC(\"license\") = \"Dual MIT/GPL\";\n\n#define MAX_MAP_ENTRIES 16\n\n/* Define an LRU hash map for storing packet count by source IPv4 address */\nstruct {\n\t__uint(type, BPF_MAP_TYPE_LRU_HASH);\n\t__uint(max_entries, MAX_MAP_ENTRIES);\n\t__type(key, __u32); // source IPv4 address\n\t__type(value, __u32); // packet count\n} xdp_stats_map SEC(\".maps\");\n\n/*\nAttempt to parse the IPv4 source address from the packet.\nReturns 0 if there is no IPv4 header field; otherwise returns non-zero.\n*/\nstatic __always_inline int parse_ip_src_addr(struct xdp_md *ctx, __u32 *ip_src_addr) {\n\tvoid *data_end = (void *)(long)ctx->data_end;\n\tvoid *data     = (void *)(long)ctx->data;\n\n\t// First, parse the ethernet header.\n\tstruct ethhdr *eth = data;\n\tif ((void *)(eth + 1) > data_end) {\n\t\treturn 0;\n\t}\n\n\tif (eth->h_proto != bpf_htons(ETH_P_IP)) {\n\t\t// The protocol is not IPv4, so we can't parse an IPv4 source address.\n\t\treturn 0;\n\t}\n\n\t// Then parse the IP header.\n\tstruct iphdr *ip = (void *)(eth + 1);\n\tif ((void *)(ip + 1) > data_end) {\n\t\treturn 0;\n\t}\n\n\t// Return the source IP address in network byte order.\n\t*ip_src_addr = (__u32)(ip->saddr);\n\treturn 1;\n}\n\nSEC(\"xdp\")\nint xdp_prog_func(struct xdp_md *ctx) {\n\t__u32 ip;\n\tif (!parse_ip_src_addr(ctx, &ip)) {\n\t\t// Not an IPv4 packet, so don't count it.\n\t\tgoto done;\n\t}\n\n\t__u32 *pkt_count = bpf_map_lookup_elem(&xdp_stats_map, &ip);\n\tif (!pkt_count) {\n\t\t// No entry in the map for this IP address yet, so set the initial value to 1.\n\t\t__u32 init_pkt_count = 1;\n\t\tbpf_map_update_elem(&xdp_stats_map, &ip, &init_pkt_count, BPF_ANY);\n\t} else {\n\t\t// Entry already exists for this IP address,\n\t\t// so increment it atomically using an LLVM built-in.\n\t\t__sync_fetch_and_add(pkt_count, 1);\n\t}\n\ndone:\n\t// Try changing this to XDP_DROP and see what happens!\n\treturn XDP_PASS;\n}\n"
  },
  {
    "path": "examples/xdp_live_frame/bpf_bpfeb.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (mips || mips64 || ppc64 || s390x) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\n// loadBpf returns the embedded CollectionSpec for bpf.\nfunc loadBpf() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_BpfBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load bpf: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadBpfObjects loads bpf and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*bpfObjects\n//\t*bpfPrograms\n//\t*bpfMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadBpf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// bpfSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfSpecs struct {\n\tbpfProgramSpecs\n\tbpfMapSpecs\n\tbpfVariableSpecs\n}\n\n// bpfProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfProgramSpecs struct {\n\tXdpProgPass *ebpf.ProgramSpec `ebpf:\"xdp_prog_pass\"`\n\tXdpProgTx   *ebpf.ProgramSpec `ebpf:\"xdp_prog_tx\"`\n}\n\n// bpfMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfMapSpecs struct {\n}\n\n// bpfVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfVariableSpecs struct {\n}\n\n// bpfObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfObjects struct {\n\tbpfPrograms\n\tbpfMaps\n\tbpfVariables\n}\n\nfunc (o *bpfObjects) Close() error {\n\treturn _BpfClose(\n\t\t&o.bpfPrograms,\n\t\t&o.bpfMaps,\n\t)\n}\n\n// bpfMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfMaps struct {\n}\n\nfunc (m *bpfMaps) Close() error {\n\treturn _BpfClose()\n}\n\n// bpfVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfVariables struct {\n}\n\n// bpfPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfPrograms struct {\n\tXdpProgPass *ebpf.Program `ebpf:\"xdp_prog_pass\"`\n\tXdpProgTx   *ebpf.Program `ebpf:\"xdp_prog_tx\"`\n}\n\nfunc (p *bpfPrograms) Close() error {\n\treturn _BpfClose(\n\t\tp.XdpProgPass,\n\t\tp.XdpProgTx,\n\t)\n}\n\nfunc _BpfClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed bpf_bpfeb.o\nvar _BpfBytes []byte\n"
  },
  {
    "path": "examples/xdp_live_frame/bpf_bpfel.go",
    "content": "// Code generated by bpf2go; DO NOT EDIT.\n//go:build (386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64 || wasm) && linux\n\npackage main\n\nimport (\n\t\"bytes\"\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\n// loadBpf returns the embedded CollectionSpec for bpf.\nfunc loadBpf() (*ebpf.CollectionSpec, error) {\n\treader := bytes.NewReader(_BpfBytes)\n\tspec, err := ebpf.LoadCollectionSpecFromReader(reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't load bpf: %w\", err)\n\t}\n\n\treturn spec, err\n}\n\n// loadBpfObjects loads bpf and converts it into a struct.\n//\n// The following types are suitable as obj argument:\n//\n//\t*bpfObjects\n//\t*bpfPrograms\n//\t*bpfMaps\n//\n// See ebpf.CollectionSpec.LoadAndAssign documentation for details.\nfunc loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {\n\tspec, err := loadBpf()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn spec.LoadAndAssign(obj, opts)\n}\n\n// bpfSpecs contains maps and programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfSpecs struct {\n\tbpfProgramSpecs\n\tbpfMapSpecs\n\tbpfVariableSpecs\n}\n\n// bpfProgramSpecs contains programs before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfProgramSpecs struct {\n\tXdpProgPass *ebpf.ProgramSpec `ebpf:\"xdp_prog_pass\"`\n\tXdpProgTx   *ebpf.ProgramSpec `ebpf:\"xdp_prog_tx\"`\n}\n\n// bpfMapSpecs contains maps before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfMapSpecs struct {\n}\n\n// bpfVariableSpecs contains global variables before they are loaded into the kernel.\n//\n// It can be passed ebpf.CollectionSpec.Assign.\ntype bpfVariableSpecs struct {\n}\n\n// bpfObjects contains all objects after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfObjects struct {\n\tbpfPrograms\n\tbpfMaps\n\tbpfVariables\n}\n\nfunc (o *bpfObjects) Close() error {\n\treturn _BpfClose(\n\t\t&o.bpfPrograms,\n\t\t&o.bpfMaps,\n\t)\n}\n\n// bpfMaps contains all maps after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfMaps struct {\n}\n\nfunc (m *bpfMaps) Close() error {\n\treturn _BpfClose()\n}\n\n// bpfVariables contains all global variables after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfVariables struct {\n}\n\n// bpfPrograms contains all programs after they have been loaded into the kernel.\n//\n// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.\ntype bpfPrograms struct {\n\tXdpProgPass *ebpf.Program `ebpf:\"xdp_prog_pass\"`\n\tXdpProgTx   *ebpf.Program `ebpf:\"xdp_prog_tx\"`\n}\n\nfunc (p *bpfPrograms) Close() error {\n\treturn _BpfClose(\n\t\tp.XdpProgPass,\n\t\tp.XdpProgTx,\n\t)\n}\n\nfunc _BpfClose(closers ...io.Closer) error {\n\tfor _, closer := range closers {\n\t\tif err := closer.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// Do not access this directly.\n//\n//go:embed bpf_bpfel.o\nvar _BpfBytes []byte\n"
  },
  {
    "path": "examples/xdp_live_frame/main.go",
    "content": "//go:build linux\n\n// This program demonstrates using BPF_F_TEST_XDP_LIVE_FRAMES to run an XDP\n// program in \"live frame mode\". In this mode, the kernel sends packets directly\n// to the network interface using the XDP program's return value (e.g., XDP_TX).\n// This is useful for high-performance packet generation and testing.\n//\n// Usage: go run . <ifname> <repeat> <batch_size> <src_ip> <dst_ip> <dst_mac>\n//\n// This example requires Linux kernel version 5.18 or newer.\npackage main\n\nimport (\n\t\"encoding/binary\"\n\t\"log\"\n\t\"net\"\n\t\"os\"\n\t\"strconv\"\n\n\t\"golang.org/x/sys/unix\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/link\"\n)\n\n//go:generate go tool bpf2go -tags linux bpf xdp.c -- -I../headers\n\nfunc main() {\n\tif len(os.Args) < 7 {\n\t\tlog.Fatalf(\"Usage: %s <ifname> <repeat> <batch_size> <src_ip> <dst_ip> <dst_mac>\", os.Args[0])\n\t}\n\n\t// Look up the network interface by name.\n\tifaceName := os.Args[1]\n\tiface, err := net.InterfaceByName(ifaceName)\n\tif err != nil {\n\t\tlog.Fatalf(\"lookup network iface %q: %s\", ifaceName, err)\n\t}\n\n\trepeat, err := strconv.ParseUint(os.Args[2], 10, 32)\n\tif err != nil {\n\t\tlog.Fatalf(\"parsing repeat count %q: %s\", os.Args[2], err)\n\t}\n\n\tbatchSize, err := strconv.ParseUint(os.Args[3], 10, 32)\n\tif err != nil {\n\t\tlog.Fatalf(\"parsing batch size %q: %s\", os.Args[3], err)\n\t}\n\n\tsrcIP := net.ParseIP(os.Args[4]).To4()\n\tif srcIP == nil {\n\t\tlog.Fatalf(\"invalid source IP address: %s\", os.Args[4])\n\t}\n\n\tdstIP := net.ParseIP(os.Args[5]).To4()\n\tif dstIP == nil {\n\t\tlog.Fatalf(\"invalid destination IP address: %s\", os.Args[5])\n\t}\n\n\tdstMAC, err := net.ParseMAC(os.Args[6])\n\tif err != nil {\n\t\tlog.Fatalf(\"invalid destination MAC address %q: %s\", os.Args[6], err)\n\t}\n\n\t// Load pre-compiled programs into the kernel.\n\tobjs := bpfObjects{}\n\tif err := loadBpfObjects(&objs, nil); err != nil {\n\t\tlog.Fatalf(\"loading objects: %s\", err)\n\t}\n\tdefer objs.Close()\n\n\t// Attach an XDP program to the interface first.\n\t// This is required for XDP_TX to work in live frame mode.\n\tl, err := link.AttachXDP(link.XDPOptions{\n\t\tProgram:   objs.XdpProgPass,\n\t\tInterface: iface.Index,\n\t})\n\tif err != nil {\n\t\tlog.Fatalf(\"could not attach XDP program: %s\", err)\n\t}\n\tdefer l.Close()\n\n\tlog.Printf(\"Attached XDP program to iface %q (index %d)\", iface.Name, iface.Index)\n\tlog.Printf(\"Running XDP program in live frame mode with Repeat: %d, BatchSize: %d\", repeat, batchSize)\n\tlog.Printf(\"Src MAC: %s, Dst MAC: %s\", iface.HardwareAddr, dstMAC)\n\tlog.Printf(\"Src IP: %s, Dst IP: %s\", srcIP, dstIP)\n\n\t// Build a UDP packet with Ethernet header\n\tpkt := buildUDPPacket(iface.HardwareAddr, dstMAC, srcIP, dstIP, 12345, 9999, []byte(\"Hello, XDP!\"))\n\n\txdpmd := &sys.XdpMd{\n\t\tDataEnd:        uint32(len(pkt)),\n\t\tIngressIfindex: uint32(iface.Index),\n\t}\n\tret, err := objs.XdpProgTx.Run(&ebpf.RunOptions{\n\t\tData:      pkt,\n\t\tRepeat:    uint32(repeat),\n\t\tFlags:     unix.BPF_F_TEST_XDP_LIVE_FRAMES,\n\t\tContext:   xdpmd,\n\t\tBatchSize: uint32(batchSize),\n\t})\n\tif err != nil {\n\t\tlog.Fatalf(\"running XDP program with BPF_F_TEST_XDP_LIVE_FRAMES: %s\", err)\n\t}\n\n\tlog.Printf(\"XDP program completed with return value: %d\", ret)\n}\n\n// buildUDPPacket creates an Ethernet + IPv4 + UDP packet.\nfunc buildUDPPacket(srcMAC, dstMAC net.HardwareAddr, srcIP, dstIP net.IP, srcPort, dstPort uint16, payload []byte) []byte {\n\t// Ethernet header (14 bytes)\n\teth := make([]byte, 14)\n\tcopy(eth[0:6], dstMAC)\n\tcopy(eth[6:12], srcMAC)\n\tbinary.BigEndian.PutUint16(eth[12:14], 0x0800) // IPv4\n\n\t// IPv4 header (20 bytes, no options)\n\tipHeaderLen := 20\n\tudpLen := 8 + len(payload)\n\ttotalLen := ipHeaderLen + udpLen\n\n\tip := make([]byte, ipHeaderLen)\n\tip[0] = 0x45                                          // Version (4) + IHL (5)\n\tip[1] = 0x00                                          // DSCP + ECN\n\tbinary.BigEndian.PutUint16(ip[2:4], uint16(totalLen)) // Total length\n\tbinary.BigEndian.PutUint16(ip[4:6], 0x0000)           // Identification\n\tbinary.BigEndian.PutUint16(ip[6:8], 0x4000)           // Flags (Don't Fragment) + Fragment Offset\n\tip[8] = 64                                            // TTL\n\tip[9] = 17                                            // Protocol (UDP)\n\t// ip[10:12] = checksum (calculated below)\n\tcopy(ip[12:16], srcIP)\n\tcopy(ip[16:20], dstIP)\n\n\t// Calculate IP header checksum\n\tbinary.BigEndian.PutUint16(ip[10:12], ipChecksum(ip))\n\n\t// UDP header (8 bytes)\n\tudp := make([]byte, 8)\n\tbinary.BigEndian.PutUint16(udp[0:2], srcPort)\n\tbinary.BigEndian.PutUint16(udp[2:4], dstPort)\n\tbinary.BigEndian.PutUint16(udp[4:6], uint16(udpLen))\n\t// udp[6:8] = checksum (optional for IPv4, set to 0)\n\n\t// Combine all parts\n\tpkt := make([]byte, 0, 14+totalLen)\n\tpkt = append(pkt, eth...)\n\tpkt = append(pkt, ip...)\n\tpkt = append(pkt, udp...)\n\tpkt = append(pkt, payload...)\n\n\treturn pkt\n}\n\n// ipChecksum calculates the IP header checksum.\nfunc ipChecksum(header []byte) uint16 {\n\tvar sum uint32\n\tfor i := 0; i < len(header); i += 2 {\n\t\tsum += uint32(binary.BigEndian.Uint16(header[i : i+2]))\n\t}\n\tfor sum > 0xffff {\n\t\tsum = (sum & 0xffff) + (sum >> 16)\n\t}\n\treturn ^uint16(sum)\n}\n"
  },
  {
    "path": "examples/xdp_live_frame/xdp.c",
    "content": "//go:build ignore\n\n// XDP program for demonstrating BPF_F_TEST_XDP_LIVE_FRAMES.\n// When run in live frame mode, the provided packet data is sent directly\n// to the network interface based on the XDP program's return value.\n\n#include \"bpf_endian.h\"\n#include \"common.h\"\n\nchar __license[] SEC(\"license\") = \"Dual MIT/GPL\";\n\n// xdp_prog_tx returns XDP_TX to transmit the packet back out the same interface.\n// This is used with BPF_F_TEST_XDP_LIVE_FRAMES for packet generation.\nSEC(\"xdp\")\nint xdp_prog_tx(struct xdp_md *ctx) {\n\treturn XDP_TX;\n}\n\n// xdp_prog_pass is attached to the interface to enable XDP_TX.\n// XDP_TX requires an XDP program to be attached to the target interface.\nSEC(\"xdp\")\nint xdp_prog_pass(struct xdp_md *ctx) {\n\treturn XDP_PASS;\n}\n"
  },
  {
    "path": "features/doc.go",
    "content": "// Package features allows probing for BPF features available to the calling process.\n//\n// In general, the error return values from feature probes in this package\n// all have the following semantics unless otherwise specified:\n//\n//\terr == nil: The feature is available.\n//\terrors.Is(err, ebpf.ErrNotSupported): The feature is not available.\n//\terr != nil: Any errors encountered during probe execution, wrapped.\n//\n// Note that the latter case may include false negatives, and that resource\n// creation may succeed despite an error being returned. For example, some\n// map and program types cannot reliably be probed and will return an\n// inconclusive error.\n//\n// As a rule, only `nil` and `ebpf.ErrNotSupported` are conclusive.\n//\n// Probe results are cached by the library and persist throughout any changes\n// to the process' environment, like capability changes.\npackage features\n"
  },
  {
    "path": "features/link.go",
    "content": "package features\n\nimport (\n\t\"errors\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\n// HaveBPFLinkUprobeMulti probes the running kernel if uprobe_multi link is supported.\n//\n// See the package documentation for the meaning of the error return value.\nfunc HaveBPFLinkUprobeMulti() error {\n\treturn haveBPFLinkUprobeMulti()\n}\n\nvar haveBPFLinkUprobeMulti = internal.NewFeatureTest(\"bpf_link_uprobe_multi\", func() error {\n\tprog, err := ebpf.NewProgram(&ebpf.ProgramSpec{\n\t\tName: \"probe_upm_link\",\n\t\tType: ebpf.Kprobe,\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.Mov.Imm(asm.R0, 0),\n\t\t\tasm.Return(),\n\t\t},\n\t\tAttachType: ebpf.AttachTraceUprobeMulti,\n\t\tLicense:    \"MIT\",\n\t})\n\tif errors.Is(err, unix.E2BIG) {\n\t\t// Kernel doesn't support AttachType field.\n\t\treturn ebpf.ErrNotSupported\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer prog.Close()\n\n\t// We try to create uprobe multi link on '/' path which results in\n\t// error with -EBADF in case uprobe multi link is supported.\n\tfd, err := sys.LinkCreateUprobeMulti(&sys.LinkCreateUprobeMultiAttr{\n\t\tProgFd:     uint32(prog.FD()),\n\t\tAttachType: sys.BPF_TRACE_UPROBE_MULTI,\n\t\tPath:       sys.NewStringPointer(\"/\"),\n\t\tOffsets:    sys.SlicePointer([]uint64{0}),\n\t\tCount:      1,\n\t})\n\tswitch {\n\tcase errors.Is(err, unix.EBADF):\n\t\treturn nil\n\tcase errors.Is(err, unix.EINVAL):\n\t\treturn ebpf.ErrNotSupported\n\tcase err != nil:\n\t\treturn err\n\t}\n\n\t// should not happen\n\tfd.Close()\n\treturn errors.New(\"successfully attached uprobe_multi to /, kernel bug?\")\n}, \"6.6\")\n\n// HaveBPFLinkKprobeMulti probes the running kernel if kprobe_multi link is supported.\n//\n// See the package documentation for the meaning of the error return value.\nfunc HaveBPFLinkKprobeMulti() error {\n\treturn haveBPFLinkKprobeMulti()\n}\n\nvar haveBPFLinkKprobeMulti = internal.NewFeatureTest(\"bpf_link_kprobe_multi\", func() error {\n\tprog, err := ebpf.NewProgram(&ebpf.ProgramSpec{\n\t\tName: \"probe_kpm_link\",\n\t\tType: ebpf.Kprobe,\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.Mov.Imm(asm.R0, 0),\n\t\t\tasm.Return(),\n\t\t},\n\t\tAttachType: ebpf.AttachTraceKprobeMulti,\n\t\tLicense:    \"MIT\",\n\t})\n\tif errors.Is(err, unix.E2BIG) {\n\t\t// Kernel doesn't support AttachType field.\n\t\treturn ebpf.ErrNotSupported\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer prog.Close()\n\n\tfd, err := sys.LinkCreateKprobeMulti(&sys.LinkCreateKprobeMultiAttr{\n\t\tProgFd:     uint32(prog.FD()),\n\t\tAttachType: sys.BPF_TRACE_KPROBE_MULTI,\n\t\tCount:      1,\n\t\tSyms:       sys.NewStringSlicePointer([]string{\"vprintk\"}),\n\t})\n\tswitch {\n\tcase errors.Is(err, unix.EINVAL):\n\t\treturn ebpf.ErrNotSupported\n\t// If CONFIG_FPROBE isn't set.\n\tcase errors.Is(err, unix.EOPNOTSUPP):\n\t\treturn ebpf.ErrNotSupported\n\tcase err != nil:\n\t\treturn err\n\t}\n\n\tfd.Close()\n\n\treturn nil\n}, \"5.18\")\n\n// HaveBPFLinkKprobeSession probes the running kernel if kprobe_session link is supported.\n//\n// See the package documentation for the meaning of the error return value.\nfunc HaveBPFLinkKprobeSession() error {\n\treturn haveBPFLinkKprobeSession()\n}\n\nvar haveBPFLinkKprobeSession = internal.NewFeatureTest(\"bpf_link_kprobe_session\", func() error {\n\tprog, err := ebpf.NewProgram(&ebpf.ProgramSpec{\n\t\tName: \"probe_kps_link\",\n\t\tType: ebpf.Kprobe,\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.Mov.Imm(asm.R0, 0),\n\t\t\tasm.Return(),\n\t\t},\n\t\tAttachType: ebpf.AttachTraceKprobeSession,\n\t\tLicense:    \"MIT\",\n\t})\n\tif errors.Is(err, unix.E2BIG) {\n\t\t// Kernel doesn't support AttachType field.\n\t\treturn ebpf.ErrNotSupported\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer prog.Close()\n\n\tfd, err := sys.LinkCreateKprobeMulti(&sys.LinkCreateKprobeMultiAttr{\n\t\tProgFd:     uint32(prog.FD()),\n\t\tAttachType: sys.BPF_TRACE_KPROBE_SESSION,\n\t\tCount:      1,\n\t\tSyms:       sys.NewStringSlicePointer([]string{\"vprintk\"}),\n\t})\n\tswitch {\n\tcase errors.Is(err, unix.EINVAL):\n\t\treturn ebpf.ErrNotSupported\n\t// If CONFIG_FPROBE isn't set.\n\tcase errors.Is(err, unix.EOPNOTSUPP):\n\t\treturn ebpf.ErrNotSupported\n\tcase err != nil:\n\t\treturn err\n\t}\n\n\tfd.Close()\n\n\treturn nil\n}, \"6.10\")\n"
  },
  {
    "path": "features/link_test.go",
    "content": "package features\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc TestHaveBPFLinkUprobeMulti(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, HaveBPFLinkUprobeMulti)\n}\n\nfunc TestHaveBPFLinkKprobeMulti(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, HaveBPFLinkKprobeMulti)\n}\n\nfunc TestHaveBPFLinkKprobeSession(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, HaveBPFLinkKprobeSession)\n}\n"
  },
  {
    "path": "features/map.go",
    "content": "package features\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"unsafe\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\n// HaveMapType probes the running kernel for the availability of the specified map type.\n//\n// See the package documentation for the meaning of the error return value.\nfunc HaveMapType(mt ebpf.MapType) error {\n\treturn haveMapTypeMatrix.Result(mt)\n}\n\nfunc probeCgroupStorageMap(mt sys.MapType) error {\n\t// keySize needs to be sizeof(struct{u32 + u64}) = 12 (+ padding = 16)\n\t// by using unsafe.Sizeof(int) we are making sure that this works on 32bit and 64bit archs\n\treturn createMap(&sys.MapCreateAttr{\n\t\tMapType:    mt,\n\t\tValueSize:  4,\n\t\tKeySize:    uint32(8 + unsafe.Sizeof(int(0))),\n\t\tMaxEntries: 0,\n\t})\n}\n\nfunc probeStorageMap(mt sys.MapType) error {\n\t// maxEntries needs to be 0\n\t// BPF_F_NO_PREALLOC needs to be set\n\t// btf* fields need to be set\n\t// see alloc_check for local_storage map types\n\terr := createMap(&sys.MapCreateAttr{\n\t\tMapType:        mt,\n\t\tKeySize:        4,\n\t\tValueSize:      4,\n\t\tMaxEntries:     0,\n\t\tMapFlags:       sys.BPF_F_NO_PREALLOC,\n\t\tBtfKeyTypeId:   1,\n\t\tBtfValueTypeId: 1,\n\t\tBtfFd:          ^uint32(0),\n\t})\n\tif errors.Is(err, unix.EBADF) {\n\t\t// Triggered by BtfFd.\n\t\treturn nil\n\t}\n\treturn err\n}\n\nfunc probeNestedMap(mt sys.MapType) error {\n\t// assign invalid innerMapFd to pass validation check\n\t// will return EBADF\n\terr := probeMap(&sys.MapCreateAttr{\n\t\tMapType:    mt,\n\t\tInnerMapFd: ^uint32(0),\n\t})\n\tif errors.Is(err, unix.EBADF) {\n\t\treturn nil\n\t}\n\treturn err\n}\n\nfunc probeMap(attr *sys.MapCreateAttr) error {\n\tif attr.KeySize == 0 {\n\t\tattr.KeySize = 4\n\t}\n\tif attr.ValueSize == 0 {\n\t\tattr.ValueSize = 4\n\t}\n\tattr.MaxEntries = 1\n\treturn createMap(attr)\n}\n\nfunc createMap(attr *sys.MapCreateAttr) error {\n\tfd, err := sys.MapCreate(attr)\n\tif err == nil {\n\t\tfd.Close()\n\t\treturn nil\n\t}\n\n\tswitch {\n\t// EINVAL occurs when attempting to create a map with an unknown type.\n\t// E2BIG occurs when MapCreateAttr contains non-zero bytes past the end\n\t// of the struct known by the running kernel, meaning the kernel is too old\n\t// to support the given map type.\n\tcase errors.Is(err, unix.EINVAL), errors.Is(err, unix.E2BIG):\n\t\treturn ebpf.ErrNotSupported\n\t}\n\n\treturn err\n}\n\nvar haveMapTypeMatrix = internal.FeatureMatrix[ebpf.MapType]{\n\tebpf.Hash:           {Version: \"3.19\"},\n\tebpf.Array:          {Version: \"3.19\"},\n\tebpf.ProgramArray:   {Version: \"4.2\"},\n\tebpf.PerfEventArray: {Version: \"4.3\"},\n\tebpf.PerCPUHash:     {Version: \"4.6\"},\n\tebpf.PerCPUArray:    {Version: \"4.6\"},\n\tebpf.StackTrace: {\n\t\tVersion: \"4.6\",\n\t\tFn: func() error {\n\t\t\treturn probeMap(&sys.MapCreateAttr{\n\t\t\t\tMapType:   sys.BPF_MAP_TYPE_STACK_TRACE,\n\t\t\t\tValueSize: 8, // sizeof(uint64)\n\t\t\t})\n\t\t},\n\t},\n\tebpf.CGroupArray: {Version: \"4.8\"},\n\tebpf.LRUHash:     {Version: \"4.10\"},\n\tebpf.LRUCPUHash:  {Version: \"4.10\"},\n\tebpf.LPMTrie: {\n\t\tVersion: \"4.11\",\n\t\tFn: func() error {\n\t\t\t// keySize and valueSize need to be sizeof(struct{u32 + u8}) + 1 + padding = 8\n\t\t\t// BPF_F_NO_PREALLOC needs to be set\n\t\t\treturn probeMap(&sys.MapCreateAttr{\n\t\t\t\tMapType:   sys.BPF_MAP_TYPE_LPM_TRIE,\n\t\t\t\tKeySize:   8,\n\t\t\t\tValueSize: 8,\n\t\t\t\tMapFlags:  sys.BPF_F_NO_PREALLOC,\n\t\t\t})\n\t\t},\n\t},\n\tebpf.ArrayOfMaps: {\n\t\tVersion: \"4.12\",\n\t\tFn:      func() error { return probeNestedMap(sys.BPF_MAP_TYPE_ARRAY_OF_MAPS) },\n\t},\n\tebpf.HashOfMaps: {\n\t\tVersion: \"4.12\",\n\t\tFn:      func() error { return probeNestedMap(sys.BPF_MAP_TYPE_HASH_OF_MAPS) },\n\t},\n\tebpf.DevMap:   {Version: \"4.14\"},\n\tebpf.SockMap:  {Version: \"4.14\"},\n\tebpf.CPUMap:   {Version: \"4.15\"},\n\tebpf.XSKMap:   {Version: \"4.18\"},\n\tebpf.SockHash: {Version: \"4.18\"},\n\tebpf.CGroupStorage: {\n\t\tVersion: \"4.19\",\n\t\tFn:      func() error { return probeCgroupStorageMap(sys.BPF_MAP_TYPE_CGROUP_STORAGE) },\n\t},\n\tebpf.ReusePortSockArray: {Version: \"4.19\"},\n\tebpf.PerCPUCGroupStorage: {\n\t\tVersion: \"4.20\",\n\t\tFn:      func() error { return probeCgroupStorageMap(sys.BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE) },\n\t},\n\tebpf.Queue: {\n\t\tVersion: \"4.20\",\n\t\tFn: func() error {\n\t\t\treturn createMap(&sys.MapCreateAttr{\n\t\t\t\tMapType:    sys.BPF_MAP_TYPE_QUEUE,\n\t\t\t\tKeySize:    0,\n\t\t\t\tValueSize:  4,\n\t\t\t\tMaxEntries: 1,\n\t\t\t})\n\t\t},\n\t},\n\tebpf.Stack: {\n\t\tVersion: \"4.20\",\n\t\tFn: func() error {\n\t\t\treturn createMap(&sys.MapCreateAttr{\n\t\t\t\tMapType:    sys.BPF_MAP_TYPE_STACK,\n\t\t\t\tKeySize:    0,\n\t\t\t\tValueSize:  4,\n\t\t\t\tMaxEntries: 1,\n\t\t\t})\n\t\t},\n\t},\n\tebpf.SkStorage: {\n\t\tVersion: \"5.2\",\n\t\tFn:      func() error { return probeStorageMap(sys.BPF_MAP_TYPE_SK_STORAGE) },\n\t},\n\tebpf.DevMapHash: {Version: \"5.4\"},\n\tebpf.StructOpsMap: {\n\t\tVersion: \"5.6\",\n\t\tFn: func() error {\n\t\t\t// StructOps requires setting a vmlinux type id, but id 1 will always\n\t\t\t// resolve to some type of integer. This will cause ENOTSUPP.\n\t\t\terr := probeMap(&sys.MapCreateAttr{\n\t\t\t\tMapType:               sys.BPF_MAP_TYPE_STRUCT_OPS,\n\t\t\t\tBtfVmlinuxValueTypeId: 1,\n\t\t\t})\n\t\t\tif errors.Is(err, sys.ENOTSUPP) {\n\t\t\t\t// ENOTSUPP means the map type is at least known to the kernel.\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn err\n\t\t},\n\t},\n\tebpf.RingBuf: {\n\t\tVersion: \"5.8\",\n\t\tFn: func() error {\n\t\t\t// keySize and valueSize need to be 0\n\t\t\t// maxEntries needs to be power of 2 and PAGE_ALIGNED\n\t\t\treturn createMap(&sys.MapCreateAttr{\n\t\t\t\tMapType:    sys.BPF_MAP_TYPE_RINGBUF,\n\t\t\t\tKeySize:    0,\n\t\t\t\tValueSize:  0,\n\t\t\t\tMaxEntries: uint32(os.Getpagesize()),\n\t\t\t})\n\t\t},\n\t},\n\tebpf.InodeStorage: {\n\t\tVersion: \"5.10\",\n\t\tFn:      func() error { return probeStorageMap(sys.BPF_MAP_TYPE_INODE_STORAGE) },\n\t},\n\tebpf.TaskStorage: {\n\t\tVersion: \"5.11\",\n\t\tFn:      func() error { return probeStorageMap(sys.BPF_MAP_TYPE_TASK_STORAGE) },\n\t},\n\tebpf.BloomFilter: {\n\t\tVersion: \"5.16\",\n\t\tFn: func() error {\n\t\t\treturn createMap(&sys.MapCreateAttr{\n\t\t\t\tMapType:    sys.BPF_MAP_TYPE_BLOOM_FILTER,\n\t\t\t\tKeySize:    0,\n\t\t\t\tValueSize:  4,\n\t\t\t\tMaxEntries: 1,\n\t\t\t})\n\t\t},\n\t},\n\tebpf.UserRingbuf: {\n\t\tVersion: \"6.1\",\n\t\tFn: func() error {\n\t\t\t// keySize and valueSize need to be 0\n\t\t\t// maxEntries needs to be power of 2 and PAGE_ALIGNED\n\t\t\treturn createMap(&sys.MapCreateAttr{\n\t\t\t\tMapType:    sys.BPF_MAP_TYPE_USER_RINGBUF,\n\t\t\t\tKeySize:    0,\n\t\t\t\tValueSize:  0,\n\t\t\t\tMaxEntries: uint32(os.Getpagesize()),\n\t\t\t})\n\t\t},\n\t},\n\tebpf.CgroupStorage: {\n\t\tVersion: \"6.2\",\n\t\tFn:      func() error { return probeStorageMap(sys.BPF_MAP_TYPE_CGRP_STORAGE) },\n\t},\n\tebpf.Arena: {\n\t\tVersion: \"6.9\",\n\t\tFn: func() error {\n\t\t\treturn createMap(&sys.MapCreateAttr{\n\t\t\t\tMapType:    sys.BPF_MAP_TYPE_ARENA,\n\t\t\t\tKeySize:    0,\n\t\t\t\tValueSize:  0,\n\t\t\t\tMaxEntries: 1, // one page\n\t\t\t\tMapExtra:   0, // can mmap() at any address\n\t\t\t\tMapFlags:   sys.BPF_F_MMAPABLE,\n\t\t\t})\n\t\t},\n\t},\n}\n\nfunc init() {\n\tfor mt, ft := range haveMapTypeMatrix {\n\t\tft.Name = mt.String()\n\t\tif ft.Fn == nil {\n\t\t\t// Avoid referring to the loop variable in the closure.\n\t\t\tmt := sys.MapType(mt)\n\t\t\tft.Fn = func() error { return probeMap(&sys.MapCreateAttr{MapType: mt}) }\n\t\t}\n\t}\n}\n\n// MapFlags document which flags may be feature probed.\ntype MapFlags uint32\n\n// Flags which may be feature probed.\nconst (\n\tBPF_F_NO_PREALLOC = sys.BPF_F_NO_PREALLOC\n\tBPF_F_RDONLY_PROG = sys.BPF_F_RDONLY_PROG\n\tBPF_F_WRONLY_PROG = sys.BPF_F_WRONLY_PROG\n\tBPF_F_MMAPABLE    = sys.BPF_F_MMAPABLE\n\tBPF_F_INNER_MAP   = sys.BPF_F_INNER_MAP\n)\n\n// HaveMapFlag probes the running kernel for the availability of the specified map flag.\n//\n// Returns an error if flag is not one of the flags declared in this package.\n// See the package documentation for the meaning of the error return value.\nfunc HaveMapFlag(flag MapFlags) (err error) {\n\treturn haveMapFlagsMatrix.Result(flag)\n}\n\nfunc probeMapFlag(attr *sys.MapCreateAttr) error {\n\t// For now, we do not check if the map type is supported because we only support\n\t// probing for flags defined on arrays and hashes that are always supported.\n\t// In the future, if we allow probing on flags defined on newer types, checking for map type\n\t// support will be required.\n\tif attr.MapType == sys.BPF_MAP_TYPE_UNSPEC {\n\t\tattr.MapType = sys.BPF_MAP_TYPE_ARRAY\n\t}\n\n\tattr.KeySize = 4\n\tattr.ValueSize = 4\n\tattr.MaxEntries = 1\n\n\tfd, err := sys.MapCreate(attr)\n\tif err == nil {\n\t\tfd.Close()\n\t} else if errors.Is(err, unix.EINVAL) {\n\t\t// EINVAL occurs when attempting to create a map with an unknown type or an unknown flag.\n\t\terr = ebpf.ErrNotSupported\n\t}\n\n\treturn err\n}\n\nvar haveMapFlagsMatrix = internal.FeatureMatrix[MapFlags]{\n\tBPF_F_NO_PREALLOC: {\n\t\tVersion: \"4.6\",\n\t\tFn: func() error {\n\t\t\treturn probeMapFlag(&sys.MapCreateAttr{\n\t\t\t\tMapType:  sys.BPF_MAP_TYPE_HASH,\n\t\t\t\tMapFlags: BPF_F_NO_PREALLOC,\n\t\t\t})\n\t\t},\n\t},\n\tBPF_F_RDONLY_PROG: {\n\t\tVersion: \"5.2\",\n\t\tFn: func() error {\n\t\t\treturn probeMapFlag(&sys.MapCreateAttr{\n\t\t\t\tMapFlags: BPF_F_RDONLY_PROG,\n\t\t\t})\n\t\t},\n\t},\n\tBPF_F_WRONLY_PROG: {\n\t\tVersion: \"5.2\",\n\t\tFn: func() error {\n\t\t\treturn probeMapFlag(&sys.MapCreateAttr{\n\t\t\t\tMapFlags: BPF_F_WRONLY_PROG,\n\t\t\t})\n\t\t},\n\t},\n\tBPF_F_MMAPABLE: {\n\t\tVersion: \"5.5\",\n\t\tFn: func() error {\n\t\t\treturn probeMapFlag(&sys.MapCreateAttr{\n\t\t\t\tMapFlags: BPF_F_MMAPABLE,\n\t\t\t})\n\t\t},\n\t},\n\tBPF_F_INNER_MAP: {\n\t\tVersion: \"5.10\",\n\t\tFn: func() error {\n\t\t\treturn probeMapFlag(&sys.MapCreateAttr{\n\t\t\t\tMapFlags: BPF_F_INNER_MAP,\n\t\t\t})\n\t\t},\n\t},\n}\n\nfunc init() {\n\tfor mf, ft := range haveMapFlagsMatrix {\n\t\tft.Name = fmt.Sprint(mf)\n\t}\n}\n"
  },
  {
    "path": "features/map_test.go",
    "content": "package features\n\nimport (\n\t\"errors\"\n\t\"math\"\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc TestHaveMapType(t *testing.T) {\n\ttestutils.CheckFeatureMatrix(t, haveMapTypeMatrix)\n}\n\nfunc TestHaveMapFlag(t *testing.T) {\n\ttestutils.CheckFeatureMatrix(t, haveMapFlagsMatrix)\n}\n\nfunc TestHaveMapTypeInvalid(t *testing.T) {\n\tif err := HaveMapType(ebpf.MapType(math.MaxUint32)); err == nil {\n\t\tt.Fatal(\"Expected an error\")\n\t} else if errors.Is(err, internal.ErrNotSupported) {\n\t\tt.Fatal(\"Got ErrNotSupported:\", err)\n\t}\n}\n"
  },
  {
    "path": "features/misc.go",
    "content": "package features\n\nimport (\n\t\"errors\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n)\n\n// HaveLargeInstructions probes the running kernel if more than 4096 instructions\n// per program are supported.\n//\n// Upstream commit c04c0d2b968a (\"bpf: increase complexity limit and maximum program size\").\n//\n// See the package documentation for the meaning of the error return value.\nfunc HaveLargeInstructions() error {\n\treturn haveLargeInstructions()\n}\n\nvar haveLargeInstructions = internal.NewFeatureTest(\">4096 instructions\", func() error {\n\tconst maxInsns = 4096\n\n\tinsns := make(asm.Instructions, maxInsns, maxInsns+1)\n\tfor i := range insns {\n\t\tinsns[i] = asm.Mov.Imm(asm.R0, 1)\n\t}\n\tinsns = append(insns, asm.Return())\n\n\treturn probeProgram(&ebpf.ProgramSpec{\n\t\tType:         ebpf.SocketFilter,\n\t\tInstructions: insns,\n\t})\n}, \"5.2\")\n\n// HaveBoundedLoops probes the running kernel if bounded loops are supported.\n//\n// Upstream commit 2589726d12a1 (\"bpf: introduce bounded loops\").\n//\n// See the package documentation for the meaning of the error return value.\nfunc HaveBoundedLoops() error {\n\treturn haveBoundedLoops()\n}\n\nvar haveBoundedLoops = internal.NewFeatureTest(\"bounded loops\", func() error {\n\treturn probeProgram(&ebpf.ProgramSpec{\n\t\tType: ebpf.SocketFilter,\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.Mov.Imm(asm.R0, 10),\n\t\t\tasm.Sub.Imm(asm.R0, 1).WithSymbol(\"loop\"),\n\t\t\tasm.JNE.Imm(asm.R0, 0, \"loop\"),\n\t\t\tasm.Return(),\n\t\t},\n\t})\n}, \"5.3\")\n\n// HaveV2ISA probes the running kernel if instructions of the v2 ISA are supported.\n//\n// Upstream commit 92b31a9af73b (\"bpf: add BPF_J{LT,LE,SLT,SLE} instructions\").\n//\n// See the package documentation for the meaning of the error return value.\nfunc HaveV2ISA() error {\n\treturn haveV2ISA()\n}\n\nvar haveV2ISA = internal.NewFeatureTest(\"v2 ISA\", func() error {\n\terr := probeProgram(&ebpf.ProgramSpec{\n\t\tType: ebpf.SocketFilter,\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.Mov.Imm(asm.R0, 0),\n\t\t\tasm.JLT.Imm(asm.R0, 0, \"exit\"),\n\t\t\tasm.Mov.Imm(asm.R0, 1),\n\t\t\tasm.Return().WithSymbol(\"exit\"),\n\t\t},\n\t})\n\t// This sometimes bubbles up from the JIT on aarch64.\n\tif errors.Is(err, sys.ENOTSUPP) {\n\t\treturn ebpf.ErrNotSupported\n\t}\n\treturn err\n}, \"4.14\")\n\n// HaveV3ISA probes the running kernel if instructions of the v3 ISA are supported.\n//\n// Upstream commit 092ed0968bb6 (\"bpf: verifier support JMP32\").\n//\n// See the package documentation for the meaning of the error return value.\nfunc HaveV3ISA() error {\n\treturn haveV3ISA()\n}\n\nvar haveV3ISA = internal.NewFeatureTest(\"v3 ISA\", func() error {\n\terr := probeProgram(&ebpf.ProgramSpec{\n\t\tType: ebpf.SocketFilter,\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.Mov.Imm(asm.R0, 0),\n\t\t\tasm.JLT.Imm32(asm.R0, 0, \"exit\"),\n\t\t\tasm.Mov.Imm(asm.R0, 1),\n\t\t\tasm.Return().WithSymbol(\"exit\"),\n\t\t},\n\t})\n\t// This sometimes bubbles up from the JIT on aarch64.\n\tif errors.Is(err, sys.ENOTSUPP) {\n\t\treturn ebpf.ErrNotSupported\n\t}\n\treturn err\n}, \"5.1\")\n\n// HaveV4ISA probes the running kernel if instructions of the v4 ISA are supported.\n//\n// Upstream commit 1f9a1ea821ff (\"bpf: Support new sign-extension load insns\").\n//\n// See the package documentation for the meaning of the error return value.\nfunc HaveV4ISA() error {\n\treturn haveV4ISA()\n}\n\nvar haveV4ISA = internal.NewFeatureTest(\"v4 ISA\", func() error {\n\terr := probeProgram(&ebpf.ProgramSpec{\n\t\tType: ebpf.SocketFilter,\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.Mov.Imm(asm.R0, 0),\n\t\t\tasm.JEq.Imm(asm.R0, 1, \"error\"),\n\t\t\tasm.LongJump(\"exit\"),\n\t\t\tasm.Mov.Imm(asm.R0, 1).WithSymbol(\"error\"),\n\t\t\tasm.Return().WithSymbol(\"exit\"),\n\t\t},\n\t})\n\t// This sometimes bubbles up from the JIT on aarch64.\n\tif errors.Is(err, sys.ENOTSUPP) {\n\t\treturn ebpf.ErrNotSupported\n\t}\n\treturn err\n}, \"6.6\")\n"
  },
  {
    "path": "features/misc_test.go",
    "content": "package features\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc TestHaveLargeInstructions(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, HaveLargeInstructions)\n}\n\nfunc TestHaveBoundedLoops(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, HaveBoundedLoops)\n}\n\nfunc TestHaveV2ISA(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, HaveV2ISA)\n}\n\nfunc TestHaveV3ISA(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, HaveV3ISA)\n}\n\nfunc TestHaveV4ISA(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, HaveV4ISA)\n}\n"
  },
  {
    "path": "features/prog.go",
    "content": "package features\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"slices\"\n\t\"strings\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\n// HaveProgramType probes the running kernel for the availability of the specified program type.\n//\n// See the package documentation for the meaning of the error return value.\nfunc HaveProgramType(pt ebpf.ProgramType) (err error) {\n\treturn haveProgramTypeMatrix.Result(pt)\n}\n\nfunc probeProgram(spec *ebpf.ProgramSpec) error {\n\tif spec.Instructions == nil {\n\t\tspec.Instructions = asm.Instructions{\n\t\t\tasm.LoadImm(asm.R0, 0, asm.DWord),\n\t\t\tasm.Return(),\n\t\t}\n\t}\n\tprog, err := ebpf.NewProgramWithOptions(spec, ebpf.ProgramOptions{\n\t\tLogDisabled: true,\n\t})\n\tif err == nil {\n\t\tprog.Close()\n\t}\n\n\tswitch {\n\t// EINVAL occurs when attempting to create a program with an unknown type.\n\t// E2BIG occurs when ProgLoadAttr contains non-zero bytes past the end\n\t// of the struct known by the running kernel, meaning the kernel is too old\n\t// to support the given prog type.\n\tcase errors.Is(err, unix.EINVAL), errors.Is(err, unix.E2BIG):\n\t\terr = ebpf.ErrNotSupported\n\t}\n\n\treturn err\n}\n\nvar haveProgramTypeMatrix = internal.FeatureMatrix[ebpf.ProgramType]{\n\tebpf.SocketFilter:  {Version: \"3.19\"},\n\tebpf.Kprobe:        {Version: \"4.1\"},\n\tebpf.SchedCLS:      {Version: \"4.1\"},\n\tebpf.SchedACT:      {Version: \"4.1\"},\n\tebpf.TracePoint:    {Version: \"4.7\"},\n\tebpf.XDP:           {Version: \"4.8\"},\n\tebpf.PerfEvent:     {Version: \"4.9\"},\n\tebpf.CGroupSKB:     {Version: \"4.10\"},\n\tebpf.CGroupSock:    {Version: \"4.10\"},\n\tebpf.LWTIn:         {Version: \"4.10\"},\n\tebpf.LWTOut:        {Version: \"4.10\"},\n\tebpf.LWTXmit:       {Version: \"4.10\"},\n\tebpf.SockOps:       {Version: \"4.13\"},\n\tebpf.SkSKB:         {Version: \"4.14\"},\n\tebpf.CGroupDevice:  {Version: \"4.15\"},\n\tebpf.SkMsg:         {Version: \"4.17\"},\n\tebpf.RawTracepoint: {Version: \"4.17\"},\n\tebpf.CGroupSockAddr: {\n\t\tVersion: \"4.17\",\n\t\tFn: func() error {\n\t\t\treturn probeProgram(&ebpf.ProgramSpec{\n\t\t\t\tType:       ebpf.CGroupSockAddr,\n\t\t\t\tAttachType: ebpf.AttachCGroupInet4Connect,\n\t\t\t})\n\t\t},\n\t},\n\tebpf.LWTSeg6Local:          {Version: \"4.18\"},\n\tebpf.LircMode2:             {Version: \"4.18\"},\n\tebpf.SkReuseport:           {Version: \"4.19\"},\n\tebpf.FlowDissector:         {Version: \"4.20\"},\n\tebpf.CGroupSysctl:          {Version: \"5.2\"},\n\tebpf.RawTracepointWritable: {Version: \"5.2\"},\n\tebpf.CGroupSockopt: {\n\t\tVersion: \"5.3\",\n\t\tFn: func() error {\n\t\t\treturn probeProgram(&ebpf.ProgramSpec{\n\t\t\t\tType:       ebpf.CGroupSockopt,\n\t\t\t\tAttachType: ebpf.AttachCGroupGetsockopt,\n\t\t\t})\n\t\t},\n\t},\n\tebpf.Tracing: {\n\t\tVersion: \"5.5\",\n\t\tFn: func() error {\n\t\t\treturn probeProgram(&ebpf.ProgramSpec{\n\t\t\t\tType:       ebpf.Tracing,\n\t\t\t\tAttachType: ebpf.AttachTraceFEntry,\n\t\t\t\tAttachTo:   \"bpf_init\",\n\t\t\t})\n\t\t},\n\t},\n\tebpf.StructOps: {\n\t\tVersion: \"5.6\",\n\t\tFn: func() error {\n\t\t\terr := probeProgram(&ebpf.ProgramSpec{\n\t\t\t\tType:    ebpf.StructOps,\n\t\t\t\tLicense: \"GPL\",\n\t\t\t})\n\t\t\tif errors.Is(err, sys.ENOTSUPP) {\n\t\t\t\t// ENOTSUPP means the program type is at least known to the kernel.\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn err\n\t\t},\n\t},\n\tebpf.Extension: {\n\t\tVersion: \"5.6\",\n\t\tFn: func() error {\n\t\t\t// create btf.Func to add to first ins of target and extension so both progs are btf powered\n\t\t\tbtfFn := btf.Func{\n\t\t\t\tName: \"a\",\n\t\t\t\tType: &btf.FuncProto{\n\t\t\t\t\tReturn: &btf.Int{},\n\t\t\t\t\tParams: []btf.FuncParam{\n\t\t\t\t\t\t{Name: \"ctx\", Type: &btf.Pointer{Target: &btf.Struct{Name: \"xdp_md\"}}},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tLinkage: btf.GlobalFunc,\n\t\t\t}\n\t\t\tinsns := asm.Instructions{\n\t\t\t\tbtf.WithFuncMetadata(asm.Mov.Imm(asm.R0, 0), &btfFn),\n\t\t\t\tasm.Return(),\n\t\t\t}\n\n\t\t\t// create target prog\n\t\t\tprog, err := ebpf.NewProgramWithOptions(\n\t\t\t\t&ebpf.ProgramSpec{\n\t\t\t\t\tType:         ebpf.XDP,\n\t\t\t\t\tInstructions: insns,\n\t\t\t\t},\n\t\t\t\tebpf.ProgramOptions{\n\t\t\t\t\tLogDisabled: true,\n\t\t\t\t},\n\t\t\t)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdefer prog.Close()\n\n\t\t\t// probe for Extension prog with target\n\t\t\treturn probeProgram(&ebpf.ProgramSpec{\n\t\t\t\tType:         ebpf.Extension,\n\t\t\t\tInstructions: insns,\n\t\t\t\tAttachTarget: prog,\n\t\t\t\tAttachTo:     btfFn.Name,\n\t\t\t})\n\t\t},\n\t},\n\tebpf.LSM: {\n\t\tVersion: \"5.7\",\n\t\tFn: func() error {\n\t\t\treturn probeProgram(&ebpf.ProgramSpec{\n\t\t\t\tType:       ebpf.LSM,\n\t\t\t\tAttachType: ebpf.AttachLSMMac,\n\t\t\t\tAttachTo:   \"file_mprotect\",\n\t\t\t\tLicense:    \"GPL\",\n\t\t\t})\n\t\t},\n\t},\n\tebpf.SkLookup: {\n\t\tVersion: \"5.9\",\n\t\tFn: func() error {\n\t\t\treturn probeProgram(&ebpf.ProgramSpec{\n\t\t\t\tType:       ebpf.SkLookup,\n\t\t\t\tAttachType: ebpf.AttachSkLookup,\n\t\t\t})\n\t\t},\n\t},\n\tebpf.Syscall: {\n\t\tVersion: \"5.14\",\n\t\tFn: func() error {\n\t\t\treturn probeProgram(&ebpf.ProgramSpec{\n\t\t\t\tType:  ebpf.Syscall,\n\t\t\t\tFlags: sys.BPF_F_SLEEPABLE,\n\t\t\t})\n\t\t},\n\t},\n\tebpf.Netfilter: {\n\t\tVersion: \"6.4\",\n\t\tFn: func() error {\n\t\t\treturn probeProgram(&ebpf.ProgramSpec{\n\t\t\t\tType:       ebpf.Netfilter,\n\t\t\t\tAttachType: ebpf.AttachNetfilter,\n\t\t\t})\n\t\t},\n\t},\n}\n\nfunc init() {\n\tfor key, ft := range haveProgramTypeMatrix {\n\t\tft.Name = key.String()\n\t\tif ft.Fn == nil {\n\t\t\tkey := key // avoid the dreaded loop variable problem\n\t\t\tft.Fn = func() error { return probeProgram(&ebpf.ProgramSpec{Type: key}) }\n\t\t}\n\t}\n}\n\ntype helperKey struct {\n\ttyp    ebpf.ProgramType\n\thelper asm.BuiltinFunc\n}\n\nvar helperCache = internal.NewFeatureCache(func(key helperKey) *internal.FeatureTest {\n\treturn &internal.FeatureTest{\n\t\tName: fmt.Sprintf(\"%s for program type %s\", key.helper, key.typ),\n\t\tFn: func() error {\n\t\t\treturn haveProgramHelper(key.typ, key.helper)\n\t\t},\n\t}\n})\n\n// HaveProgramHelper probes the running kernel for the availability of the specified helper\n// function to a specified program type.\n// Return values have the following semantics:\n//\n//\terr == nil: The feature is available.\n//\terrors.Is(err, ebpf.ErrNotSupported): The feature is not available.\n//\terr != nil: Any errors encountered during probe execution, wrapped.\n//\n// Note that the latter case may include false negatives, and that program creation may\n// succeed despite an error being returned.\n// Only `nil` and `ebpf.ErrNotSupported` are conclusive.\n//\n// Probe results are cached and persist throughout any process capability changes.\nfunc HaveProgramHelper(pt ebpf.ProgramType, helper asm.BuiltinFunc) error {\n\treturn helperCache.Result(helperKey{pt, helper})\n}\n\nfunc haveProgramHelper(pt ebpf.ProgramType, helper asm.BuiltinFunc) error {\n\tif ok := helperProbeNotImplemented(pt); ok {\n\t\treturn fmt.Errorf(\"no feature probe for %v/%v\", pt, helper)\n\t}\n\n\tif err := HaveProgramType(pt); err != nil {\n\t\treturn err\n\t}\n\n\tspec := &ebpf.ProgramSpec{\n\t\tType: pt,\n\t\tInstructions: asm.Instructions{\n\t\t\thelper.Call(),\n\t\t\tasm.LoadImm(asm.R0, 0, asm.DWord),\n\t\t\tasm.Return(),\n\t\t},\n\t\tLicense: \"GPL\",\n\t}\n\n\tswitch pt {\n\tcase ebpf.CGroupSockAddr:\n\t\tspec.AttachType = ebpf.AttachCGroupInet4Connect\n\tcase ebpf.CGroupSockopt:\n\t\tspec.AttachType = ebpf.AttachCGroupGetsockopt\n\tcase ebpf.SkLookup:\n\t\tspec.AttachType = ebpf.AttachSkLookup\n\tcase ebpf.Syscall:\n\t\tspec.Flags = sys.BPF_F_SLEEPABLE\n\tcase ebpf.Netfilter:\n\t\tspec.AttachType = ebpf.AttachNetfilter\n\t}\n\n\tprog, err := ebpf.NewProgramWithOptions(spec, ebpf.ProgramOptions{\n\t\tLogLevel: 1,\n\t})\n\tif err == nil {\n\t\tprog.Close()\n\t}\n\n\tvar verr *ebpf.VerifierError\n\tif !errors.As(err, &verr) {\n\t\treturn err\n\t}\n\n\thelperTag := fmt.Sprintf(\"#%d\", helper)\n\n\tswitch {\n\t// EACCES occurs when attempting to create a program probe with a helper\n\t// while the register args when calling this helper aren't set up properly.\n\t// We interpret this as the helper being available, because the verifier\n\t// returns EINVAL if the helper is not supported by the running kernel.\n\tcase errors.Is(err, unix.EACCES):\n\t\terr = nil\n\n\t// EINVAL occurs when attempting to create a program with an unknown helper.\n\tcase errors.Is(err, unix.EINVAL):\n\t\t// https://github.com/torvalds/linux/blob/09a0fa92e5b45e99cf435b2fbf5ebcf889cf8780/kernel/bpf/verifier.c#L10663\n\t\tif logContainsAll(verr.Log, \"invalid func\", helperTag) {\n\t\t\treturn ebpf.ErrNotSupported\n\t\t}\n\n\t\t// https://github.com/torvalds/linux/blob/09a0fa92e5b45e99cf435b2fbf5ebcf889cf8780/kernel/bpf/verifier.c#L10668\n\t\twrongProgramType := logContainsAll(verr.Log, \"program of this type cannot use helper\", helperTag)\n\t\t// https://github.com/torvalds/linux/blob/59b418c7063d30e0a3e1f592d47df096db83185c/kernel/bpf/verifier.c#L10204\n\t\t// 4.9 doesn't include # in verifier output.\n\t\twrongProgramType = wrongProgramType || logContainsAll(verr.Log, \"unknown func\")\n\t\tif wrongProgramType {\n\t\t\treturn fmt.Errorf(\"program of this type cannot use helper: %w\", ebpf.ErrNotSupported)\n\t\t}\n\t}\n\n\treturn err\n}\n\nfunc logContainsAll(log []string, needles ...string) bool {\n\tfirst := max(len(log)-5, 0) // Check last 5 lines.\n\treturn slices.ContainsFunc(log[first:], func(line string) bool {\n\t\tfor _, needle := range needles {\n\t\t\tif !strings.Contains(line, needle) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn true\n\t})\n}\n\nfunc helperProbeNotImplemented(pt ebpf.ProgramType) bool {\n\tswitch pt {\n\tcase ebpf.Extension, ebpf.LSM, ebpf.StructOps, ebpf.Tracing:\n\t\treturn true\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "features/prog_test.go",
    "content": "package features\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n\t\"github.com/cilium/ebpf/internal/testutils/testmain\"\n)\n\nfunc TestMain(m *testing.M) {\n\ttestmain.Run(m)\n}\n\nfunc TestHaveProgramType(t *testing.T) {\n\ttestutils.CheckFeatureMatrix(t, haveProgramTypeMatrix)\n}\n\nfunc TestHaveProgramTypeInvalid(t *testing.T) {\n\tif err := HaveProgramType(ebpf.ProgramType(math.MaxUint32)); err == nil {\n\t\tt.Fatal(\"Expected an error\")\n\t} else if errors.Is(err, internal.ErrNotSupported) {\n\t\tt.Fatal(\"Got ErrNotSupported:\", err)\n\t}\n}\n\nfunc TestHaveProgramHelper(t *testing.T) {\n\ttype testCase struct {\n\t\tprog     ebpf.ProgramType\n\t\thelper   asm.BuiltinFunc\n\t\texpected error\n\t\tversion  string\n\t}\n\n\t// Referencing linux kernel commits to track the kernel version required to pass these test cases.\n\t// These cases are derived from libbpf's selftests and helper/prog combinations that are\n\t// probed for in cilium/cilium.\n\ttestCases := []testCase{\n\t\t{ebpf.Kprobe, asm.FnMapLookupElem, nil, \"3.19\"},                     // d0003ec01c66\n\t\t{ebpf.SocketFilter, asm.FnKtimeGetCoarseNs, nil, \"5.11\"},            // d05512618056\n\t\t{ebpf.SchedCLS, asm.FnSkbVlanPush, nil, \"4.3\"},                      // 4e10df9a60d9\n\t\t{ebpf.Kprobe, asm.FnSkbVlanPush, ebpf.ErrNotSupported, \"4.3\"},       // 4e10df9a60d9\n\t\t{ebpf.Kprobe, asm.FnSysBpf, ebpf.ErrNotSupported, \"5.14\"},           // 79a7f8bdb159\n\t\t{ebpf.Syscall, asm.FnSysBpf, nil, \"5.14\"},                           // 79a7f8bdb159\n\t\t{ebpf.XDP, asm.FnJiffies64, nil, \"5.5\"},                             // 5576b991e9c1\n\t\t{ebpf.XDP, asm.FnKtimeGetBootNs, nil, \"5.7\"},                        // 71d19214776e\n\t\t{ebpf.SchedCLS, asm.FnSkbChangeHead, nil, \"5.8\"},                    // 6f3f65d80dac\n\t\t{ebpf.SchedCLS, asm.FnRedirectNeigh, nil, \"5.10\"},                   // b4ab31414970\n\t\t{ebpf.SchedCLS, asm.FnSkbEcnSetCe, nil, \"5.1\"},                      // f7c917ba11a6\n\t\t{ebpf.SchedACT, asm.FnSkAssign, nil, \"5.6\"},                         // cf7fbe660f2d\n\t\t{ebpf.SchedACT, asm.FnFibLookup, nil, \"4.18\"},                       // 87f5fc7e48dd\n\t\t{ebpf.Kprobe, asm.FnFibLookup, ebpf.ErrNotSupported, \"4.18\"},        // 87f5fc7e48dd\n\t\t{ebpf.CGroupSockAddr, asm.FnGetsockopt, nil, \"5.8\"},                 // beecf11bc218\n\t\t{ebpf.CGroupSockAddr, asm.FnSkLookupTcp, nil, \"4.20\"},               // 6acc9b432e67\n\t\t{ebpf.CGroupSockAddr, asm.FnGetNetnsCookie, nil, \"5.7\"},             // f318903c0bf4\n\t\t{ebpf.CGroupSock, asm.FnGetNetnsCookie, nil, \"5.7\"},                 // f318903c0bf4\n\t\t{ebpf.Kprobe, asm.FnKtimeGetCoarseNs, ebpf.ErrNotSupported, \"5.16\"}, // 5e0bc3082e2e\n\t\t{ebpf.CGroupSockAddr, asm.FnGetCgroupClassid, nil, \"5.7\"},           // 5a52ae4e32a6\n\t\t{ebpf.Kprobe, asm.FnGetBranchSnapshot, nil, \"5.16\"},                 // 856c02dbce4f\n\t\t{ebpf.SchedCLS, asm.FnSkbSetTstamp, nil, \"5.18\"},                    // 9bb984f28d5b\n\t\t{ebpf.CGroupSockopt, asm.FnSkStorageDelete, nil, \"5.3\"},             // 6ac99e8f23d4\n\t\t{ebpf.SkLookup, asm.FnSkcToUdp6Sock, nil, \"5.9\"},                    // 0d4fad3e57df\n\t\t{ebpf.Syscall, asm.FnSysClose, nil, \"5.14\"},                         // 3abea089246f\n\t\t{ebpf.Netfilter, asm.FnCgrpStorageDelete, nil, \"6.4\"},               // c4bcfb38a95e\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(fmt.Sprintf(\"%s/%s\", tc.prog.String(), tc.helper.String()), func(t *testing.T) {\n\t\t\tfeature := fmt.Sprintf(\"helper %s for program type %s\", tc.helper.String(), tc.prog.String())\n\n\t\t\ttestutils.SkipOnOldKernel(t, tc.version, feature)\n\n\t\t\terr := HaveProgramHelper(tc.prog, tc.helper)\n\t\t\ttestutils.SkipIfNotSupportedOnOS(t, err)\n\t\t\tif !errors.Is(err, tc.expected) {\n\t\t\t\tt.Fatalf(\"%s/%s: %v\", tc.prog.String(), tc.helper.String(), err)\n\t\t\t}\n\n\t\t})\n\n\t}\n}\n\nfunc TestHelperProbeNotImplemented(t *testing.T) {\n\t// Currently we don't support probing helpers for Tracing, Extension, LSM and StructOps programs.\n\t// For each of those test the availability of the FnMapLookupElem helper and expect it to fail.\n\tfor _, pt := range []ebpf.ProgramType{ebpf.Tracing, ebpf.Extension, ebpf.LSM, ebpf.StructOps} {\n\t\tt.Run(pt.String(), func(t *testing.T) {\n\t\t\tif err := HaveProgramHelper(pt, asm.FnMapLookupElem); err == nil {\n\t\t\t\tt.Fatal(\"Expected an error\")\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "features/version.go",
    "content": "package features\n\nimport \"github.com/cilium/ebpf/internal/linux\"\n\n// LinuxVersionCode returns the version of the currently running kernel\n// as defined in the LINUX_VERSION_CODE compile-time macro. It is represented\n// in the format described by the KERNEL_VERSION macro from linux/version.h.\n//\n// Do not use the version to make assumptions about the presence of certain\n// kernel features, always prefer feature probes in this package. Some\n// distributions backport or disable eBPF features.\nfunc LinuxVersionCode() (uint32, error) {\n\tv, err := linux.KernelVersion()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn v.Kernel(), nil\n}\n"
  },
  {
    "path": "fuzz_test.go",
    "content": "package ebpf\n\nimport (\n\t\"bytes\"\n\t\"debug/elf\"\n\t\"testing\"\n)\n\nfunc FuzzLoadCollectionSpec(f *testing.F) {\n\tf.Add([]byte(elf.ELFMAG))\n\tf.Fuzz(func(t *testing.T, data []byte) {\n\t\tif len(data) < len(elf.ELFMAG) {\n\t\t\tt.Skip(\"input can't be valid ELF\")\n\t\t}\n\n\t\tspec, err := LoadCollectionSpecFromReader(bytes.NewReader(data))\n\t\tif err != nil {\n\t\t\tif spec != nil {\n\t\t\t\tt.Fatal(\"spec is not nil\")\n\t\t\t}\n\t\t} else if spec == nil {\n\t\t\tt.Fatal(\"spec is nil\")\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/cilium/ebpf\n\ngo 1.24.0\n\nrequire (\n\tgithub.com/go-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6\n\tgithub.com/google/go-cmp v0.7.0\n\tgithub.com/jsimonetti/rtnetlink/v2 v2.0.1\n\tgolang.org/x/sync v0.17.0\n\tgolang.org/x/sys v0.37.0\n)\n\nrequire (\n\tgithub.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect\n\tgithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect\n\tgithub.com/docker/cli v29.2.0+incompatible // indirect\n\tgithub.com/docker/distribution v2.8.3+incompatible // indirect\n\tgithub.com/docker/docker-credential-helpers v0.9.3 // indirect\n\tgithub.com/google/go-containerregistry v0.20.6 // indirect\n\tgithub.com/inconshreveable/mousetrap v1.1.0 // indirect\n\tgithub.com/josharian/native v1.1.0 // indirect\n\tgithub.com/klauspost/compress v1.18.0 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/kr/text v0.2.0 // indirect\n\tgithub.com/mdlayher/netlink v1.7.2 // indirect\n\tgithub.com/mdlayher/socket v0.5.1 // indirect\n\tgithub.com/mitchellh/go-homedir v1.1.0 // indirect\n\tgithub.com/opencontainers/go-digest v1.0.0 // indirect\n\tgithub.com/opencontainers/image-spec v1.1.1 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect\n\tgithub.com/rogpeppe/go-internal v1.12.0 // indirect\n\tgithub.com/sirupsen/logrus v1.9.3 // indirect\n\tgithub.com/spf13/cobra v1.9.1 // indirect\n\tgithub.com/spf13/pflag v1.0.7 // indirect\n\tgithub.com/stretchr/testify v1.10.0 // indirect\n\tgithub.com/vbatts/tar-split v0.12.1 // indirect\n\tgolang.org/x/mod v0.29.0 // indirect\n\tgolang.org/x/net v0.46.0 // indirect\n\tgolang.org/x/tools v0.38.0 // indirect\n\tgotest.tools/v3 v3.5.0 // indirect\n)\n\ntool (\n\tgithub.com/cilium/ebpf/cmd/bpf2go\n\tgithub.com/cilium/ebpf/internal/cmd/gentypes\n\tgithub.com/google/go-containerregistry/cmd/crane\n\tgolang.org/x/tools/cmd/stringer\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/containerd/stargz-snapshotter/estargz v0.16.3 h1:7evrXtoh1mSbGj/pfRccTampEyKpjpOnS3CyiV1Ebr8=\ngithub.com/containerd/stargz-snapshotter/estargz v0.16.3/go.mod h1:uyr4BfYfOj3G9WBVE8cOlQmXAbPN9VEQpBBeJIuOipU=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/docker/cli v29.2.0+incompatible h1:9oBd9+YM7rxjZLfyMGxjraKBKE4/nVyvVfN4qNl9XRM=\ngithub.com/docker/cli v29.2.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=\ngithub.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=\ngithub.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=\ngithub.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=\ngithub.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=\ngithub.com/go-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6 h1:teYtXy9B7y5lHTp8V9KPxpYRAVA7dozigQcMiBust1s=\ngithub.com/go-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6/go.mod h1:p4lGIVX+8Wa6ZPNDvqcxq36XpUDLh42FLetFU7odllI=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-containerregistry v0.20.6 h1:cvWX87UxxLgaH76b4hIvya6Dzz9qHB31qAwjAohdSTU=\ngithub.com/google/go-containerregistry v0.20.6/go.mod h1:T0x8MuoAoKX/873bkeSfLD2FAkwCDf9/HZgsFJ02E2Y=\ngithub.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=\ngithub.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=\ngithub.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=\ngithub.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=\ngithub.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM=\ngithub.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE=\ngithub.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=\ngithub.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=\ngithub.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=\ngithub.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=\ngithub.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=\ngithub.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=\ngithub.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=\ngithub.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=\ngithub.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=\ngithub.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=\ngithub.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=\ngithub.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=\ngithub.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=\ngithub.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=\ngithub.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=\ngithub.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=\ngithub.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=\ngithub.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo=\ngithub.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=\ngolang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=\ngolang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=\ngolang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=\ngolang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=\ngolang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=\ngolang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=\ngolang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=\ngolang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY=\ngotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=\n"
  },
  {
    "path": "helpers_test.go",
    "content": "package ebpf\n\nimport (\n\t\"errors\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/platform\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nvar haveTestmod = sync.OnceValues(func() (bool, error) {\n\tif platform.IsWindows {\n\t\treturn false, nil\n\t}\n\n\t// See https://github.com/torvalds/linux/commit/290248a5b7d829871b3ea3c62578613a580a1744\n\ttestmod, err := btf.FindHandle(func(info *btf.HandleInfo) bool {\n\t\treturn info.IsModule() && info.Name == \"bpf_testmod\"\n\t})\n\tif err != nil && !errors.Is(err, btf.ErrNotFound) {\n\t\treturn false, err\n\t}\n\ttestmod.Close()\n\n\treturn testmod != nil, nil\n})\n\nvar haveTestmodOps = sync.OnceValues(func() (bool, error) {\n\thaveTestMod, err := haveTestmod()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tif !haveTestMod {\n\t\treturn false, nil\n\t}\n\n\ttarget := btf.Type((*btf.Struct)(nil))\n\t_, module, err := findTargetInKernel(\"bpf_struct_ops_bpf_testmod_ops\", &target, btf.NewCache())\n\tif err != nil && !errors.Is(err, btf.ErrNotFound) {\n\t\treturn false, err\n\t}\n\tif errors.Is(err, btf.ErrNotFound) {\n\t\treturn false, nil\n\t}\n\tdefer module.Close()\n\n\treturn true, nil\n})\n\nfunc requireTestmod(tb testing.TB) {\n\ttb.Helper()\n\n\ttestutils.SkipOnOldKernel(tb, \"5.11\", \"bpf_testmod\")\n\n\ttestmod, err := haveTestmod()\n\tif err != nil {\n\t\ttb.Fatal(err)\n\t}\n\tif !testmod {\n\t\ttb.Skip(\"bpf_testmod not loaded\")\n\t}\n}\n\nfunc requireTestmodOps(tb testing.TB) {\n\ttb.Helper()\n\n\ttestutils.SkipOnOldKernel(tb, \"5.11\", \"bpf_testmod\")\n\ttestmodOps, err := haveTestmodOps()\n\tif err != nil {\n\t\ttb.Fatal(err)\n\t}\n\tif !testmodOps {\n\t\ttb.Skip(\"bpf_testmod_ops not loaded\")\n\t}\n}\n\nfunc newMap(tb testing.TB, spec *MapSpec, opts *MapOptions) (*Map, error) {\n\ttb.Helper()\n\n\tspec = fixupMapSpec(spec)\n\n\tif opts == nil {\n\t\topts = new(MapOptions)\n\t}\n\n\tm, err := NewMapWithOptions(spec, *opts)\n\ttestutils.SkipIfNotSupportedOnOS(tb, err)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttb.Cleanup(func() { m.Close() })\n\treturn m, nil\n}\n\nfunc mustNewMap(tb testing.TB, spec *MapSpec, opts *MapOptions) *Map {\n\ttb.Helper()\n\n\tm, err := newMap(tb, spec, opts)\n\tqt.Assert(tb, qt.IsNil(err))\n\n\treturn m\n}\n\nfunc createMap(tb testing.TB, typ MapType, maxEntries uint32) *Map {\n\ttb.Helper()\n\n\treturn mustNewMap(tb, &MapSpec{\n\t\tName:       \"test\",\n\t\tType:       typ,\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: maxEntries,\n\t}, nil)\n}\n\nfunc createMapInMap(tb testing.TB, outer, inner MapType) *Map {\n\ttb.Helper()\n\n\treturn mustNewMap(tb, &MapSpec{\n\t\tType:       outer,\n\t\tKeySize:    4,\n\t\tMaxEntries: 2,\n\t\tInnerMap: &MapSpec{\n\t\t\tType:       inner,\n\t\t\tKeySize:    4,\n\t\t\tValueSize:  4,\n\t\t\tMaxEntries: 2,\n\t\t},\n\t}, nil)\n}\n\nfunc newProgram(tb testing.TB, spec *ProgramSpec, opts *ProgramOptions) (*Program, error) {\n\ttb.Helper()\n\n\tif opts == nil {\n\t\topts = new(ProgramOptions)\n\t}\n\n\tspec = fixupProgramSpec(spec)\n\n\tprog, err := NewProgramWithOptions(spec, *opts)\n\ttestutils.SkipIfNotSupportedOnOS(tb, err)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttb.Cleanup(func() { prog.Close() })\n\treturn prog, nil\n}\n\nfunc mustNewProgram(tb testing.TB, spec *ProgramSpec, opts *ProgramOptions) *Program {\n\ttb.Helper()\n\n\tprog, err := newProgram(tb, spec, opts)\n\tqt.Assert(tb, qt.IsNil(err))\n\treturn prog\n}\n\nfunc createProgram(tb testing.TB, typ ProgramType, retval int64) *Program {\n\ttb.Helper()\n\n\treturn mustNewProgram(tb, &ProgramSpec{\n\t\tName: \"test\",\n\t\tType: typ,\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.LoadImm(asm.R0, retval, asm.DWord),\n\t\t\tasm.Return(),\n\t\t},\n\t\tLicense: \"MIT\",\n\t}, nil)\n}\n\nvar basicProgramSpec = &ProgramSpec{\n\tName: \"test\",\n\tType: SocketFilter,\n\tInstructions: asm.Instructions{\n\t\tasm.LoadImm(asm.R0, 2, asm.DWord),\n\t\tasm.Return(),\n\t},\n\tLicense: \"MIT\",\n}\n\n// createBasicProgram returns a program of an unspecified type which returns\n// a non-zero value when executed.\nfunc createBasicProgram(tb testing.TB) *Program {\n\treturn mustNewProgram(tb, basicProgramSpec, nil)\n}\n\nfunc newCollection(tb testing.TB, spec *CollectionSpec, opts *CollectionOptions) (*Collection, error) {\n\ttb.Helper()\n\n\ttestutils.SkipNonNativeEndian(tb, spec.ByteOrder)\n\n\tspec = fixupCollectionSpec(spec)\n\n\tif opts == nil {\n\t\topts = new(CollectionOptions)\n\t}\n\n\tc, err := NewCollectionWithOptions(spec, *opts)\n\ttestutils.SkipIfNotSupportedOnOS(tb, err)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttb.Cleanup(func() { c.Close() })\n\treturn c, nil\n}\n\nfunc mustNewCollection(tb testing.TB, spec *CollectionSpec, opts *CollectionOptions) *Collection {\n\ttb.Helper()\n\tc, err := newCollection(tb, spec, opts)\n\tqt.Assert(tb, qt.IsNil(err))\n\treturn c\n}\n\nfunc loadAndAssign(tb testing.TB, spec *CollectionSpec, to any, opts *CollectionOptions) error {\n\ttb.Helper()\n\tspec = fixupCollectionSpec(spec)\n\terr := spec.LoadAndAssign(to, opts)\n\ttestutils.SkipIfNotSupported(tb, err)\n\treturn err\n}\n\nfunc mustLoadAndAssign(tb testing.TB, spec *CollectionSpec, to any, opts *CollectionOptions) {\n\tqt.Assert(tb, qt.IsNil(loadAndAssign(tb, spec, to, opts)))\n}\n\nfunc mustRun(tb testing.TB, prog *Program, opts *RunOptions) (retval uint32) {\n\ttb.Helper()\n\n\tif opts == nil {\n\t\topts = &RunOptions{}\n\t}\n\tif platform.IsLinux && opts.Data == nil {\n\t\topts.Data = internal.EmptyBPFContext\n\t}\n\tif platform.IsWindows {\n\t\tswitch prog.Type() {\n\t\tcase WindowsSample:\n\t\t\tconst minSampleContextLen = 32\n\n\t\t\tif opts.Context == nil {\n\t\t\t\topts.Context = make([]byte, minSampleContextLen)\n\t\t\t}\n\t\t}\n\t}\n\n\tret, err := prog.Run(opts)\n\ttestutils.SkipIfNotSupported(tb, err)\n\tqt.Assert(tb, qt.IsNil(err))\n\n\treturn ret\n}\n\n// The functions below translate Linux types to their Windows equivalents, if\n// possible. This allows running most tests on Windows without modification.\n\nfunc fixupMapType(typ MapType) MapType {\n\tif !platform.IsWindows {\n\t\treturn typ\n\t}\n\n\tswitch typ {\n\tcase Array:\n\t\treturn WindowsArray\n\tcase Hash:\n\t\treturn WindowsHash\n\tcase ProgramArray:\n\t\treturn WindowsProgramArray\n\tcase PerCPUHash:\n\t\treturn WindowsPerCPUHash\n\tcase PerCPUArray:\n\t\treturn WindowsPerCPUArray\n\tcase LRUHash:\n\t\treturn WindowsLRUHash\n\tcase LRUCPUHash:\n\t\treturn WindowsLRUCPUHash\n\tcase ArrayOfMaps:\n\t\treturn WindowsArrayOfMaps\n\tcase HashOfMaps:\n\t\treturn WindowsHashOfMaps\n\tcase LPMTrie:\n\t\treturn WindowsLPMTrie\n\tcase Queue:\n\t\treturn WindowsQueue\n\tcase Stack:\n\t\treturn WindowsStack\n\tcase RingBuf:\n\t\treturn WindowsRingBuf\n\tdefault:\n\t\treturn typ\n\t}\n}\n\nfunc fixupMapSpec(spec *MapSpec) *MapSpec {\n\tif !platform.IsWindows {\n\t\treturn spec\n\t}\n\n\tspec = spec.Copy()\n\tspec.Type = fixupMapType(spec.Type)\n\tif spec.InnerMap != nil {\n\t\tspec.InnerMap.Type = fixupMapType(spec.InnerMap.Type)\n\t}\n\n\treturn spec\n}\n\nfunc fixupProgramType(typ ProgramType) ProgramType {\n\tif !platform.IsWindows {\n\t\treturn typ\n\t}\n\n\tswitch typ {\n\tcase SocketFilter:\n\t\treturn WindowsSample\n\tcase XDP:\n\t\treturn WindowsSample\n\tdefault:\n\t\treturn typ\n\t}\n}\n\nfunc fixupProgramSpec(spec *ProgramSpec) *ProgramSpec {\n\tif !platform.IsWindows {\n\t\treturn spec\n\t}\n\n\tspec = spec.Copy()\n\tspec.Type = fixupProgramType(spec.Type)\n\n\tfor i, ins := range spec.Instructions {\n\t\tif ins.IsBuiltinCall() {\n\t\t\tswitch asm.BuiltinFunc(ins.Constant) {\n\t\t\tcase asm.FnMapUpdateElem:\n\t\t\t\tspec.Instructions[i].Constant = int64(asm.WindowsFnMapUpdateElem)\n\t\t\tcase asm.FnMapLookupElem:\n\t\t\t\tspec.Instructions[i].Constant = int64(asm.WindowsFnMapLookupElem)\n\t\t\tcase asm.FnTailCall:\n\t\t\t\tspec.Instructions[i].Constant = int64(asm.WindowsFnTailCall)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn spec\n}\n\nfunc fixupCollectionSpec(spec *CollectionSpec) *CollectionSpec {\n\tif !platform.IsWindows {\n\t\treturn spec\n\t}\n\n\tspec = spec.Copy()\n\tfor name := range spec.Maps {\n\t\tspec.Maps[name] = fixupMapSpec(spec.Maps[name])\n\t}\n\n\tfor name := range spec.Programs {\n\t\tspec.Programs[name] = fixupProgramSpec(spec.Programs[name])\n\t}\n\n\treturn spec\n}\n"
  },
  {
    "path": "info.go",
    "content": "package ebpf\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"reflect\"\n\t\"time\"\n\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/platform\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\n// The *Info structs expose metadata about a program or map. Most\n// fields are exposed via a getter:\n//\n//     func (*MapInfo) ID() (MapID, bool)\n//\n// This is because the metadata available changes based on kernel version.\n// The second boolean return value indicates whether a particular field is\n// available on the current kernel.\n//\n// Always add new metadata as such a getter, unless you can somehow get the\n// value of the field on all supported kernels. Also document which version\n// a particular field first appeared in.\n//\n// Some metadata is a buffer which needs additional parsing. In this case,\n// store the undecoded data in the Info struct and provide a getter which\n// decodes it when necessary. See ProgramInfo.Instructions for an example.\n\n// MapInfo describes a map.\ntype MapInfo struct {\n\t// Type of the map.\n\tType MapType\n\t// KeySize is the size of the map key in bytes.\n\tKeySize uint32\n\t// ValueSize is the size of the map value in bytes.\n\tValueSize uint32\n\t// MaxEntries is the maximum number of entries the map can hold. Its meaning\n\t// is map-specific.\n\tMaxEntries uint32\n\t// Flags used during map creation.\n\tFlags uint32\n\t// Name as supplied by user space at load time. Available from 4.15.\n\tName string\n\n\tid       MapID\n\tbtf      btf.ID\n\tmapExtra uint64\n\tmemlock  uint64\n\tfrozen   bool\n}\n\n// minimalMapInfoFromFd queries the minimum information needed to create a Map\n// based on a file descriptor. This requires the map type, key/value sizes,\n// maxentries and flags.\n//\n// Does not fall back to fdinfo since the version gap between fdinfo (4.10) and\n// [sys.ObjInfo] (4.13) is small and both kernels are EOL since at least Nov\n// 2017.\n//\n// Requires at least Linux 4.13.\nfunc minimalMapInfoFromFd(fd *sys.FD) (*MapInfo, error) {\n\tvar info sys.MapInfo\n\tif err := sys.ObjInfo(fd, &info); err != nil {\n\t\treturn nil, fmt.Errorf(\"getting object info: %w\", err)\n\t}\n\n\ttyp, err := MapTypeForPlatform(platform.Native, info.Type)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"map type: %w\", err)\n\t}\n\n\treturn &MapInfo{\n\t\tType:       typ,\n\t\tKeySize:    info.KeySize,\n\t\tValueSize:  info.ValueSize,\n\t\tMaxEntries: info.MaxEntries,\n\t\tFlags:      uint32(info.MapFlags),\n\t\tName:       unix.ByteSliceToString(info.Name[:]),\n\t}, nil\n}\n\n// newMapInfoFromFd queries map information about the given fd. [sys.ObjInfo] is\n// attempted first, supplementing any missing values with information from\n// /proc/self/fdinfo. Ignores EINVAL from ObjInfo as well as ErrNotSupported\n// from reading fdinfo (indicating the file exists, but no fields of interest\n// were found). If both fail, an error is always returned.\nfunc newMapInfoFromFd(fd *sys.FD) (*MapInfo, error) {\n\tvar info sys.MapInfo\n\terr1 := sys.ObjInfo(fd, &info)\n\t// EINVAL means the kernel doesn't support BPF_OBJ_GET_INFO_BY_FD. Continue\n\t// with fdinfo if that's the case.\n\tif err1 != nil && !errors.Is(err1, unix.EINVAL) {\n\t\treturn nil, fmt.Errorf(\"getting object info: %w\", err1)\n\t}\n\n\ttyp, err := MapTypeForPlatform(platform.Native, info.Type)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"map type: %w\", err)\n\t}\n\n\tmi := &MapInfo{\n\t\ttyp,\n\t\tinfo.KeySize,\n\t\tinfo.ValueSize,\n\t\tinfo.MaxEntries,\n\t\tuint32(info.MapFlags),\n\t\tunix.ByteSliceToString(info.Name[:]),\n\t\tMapID(info.Id),\n\t\tbtf.ID(info.BtfId),\n\t\tinfo.MapExtra,\n\t\t0,\n\t\tfalse,\n\t}\n\n\t// Supplement OBJ_INFO with data from /proc/self/fdinfo. It contains fields\n\t// like memlock and frozen that are not present in OBJ_INFO.\n\terr2 := readMapInfoFromProc(fd, mi)\n\tif err2 != nil && !errors.Is(err2, ErrNotSupported) {\n\t\treturn nil, fmt.Errorf(\"getting map info from fdinfo: %w\", err2)\n\t}\n\n\tif err1 != nil && err2 != nil {\n\t\treturn nil, fmt.Errorf(\"ObjInfo and fdinfo both failed: objinfo: %w, fdinfo: %w\", err1, err2)\n\t}\n\n\treturn mi, nil\n}\n\n// readMapInfoFromProc queries map information about the given fd from\n// /proc/self/fdinfo. It only writes data into fields that have a zero value.\nfunc readMapInfoFromProc(fd *sys.FD, mi *MapInfo) error {\n\tvar mapType uint32\n\terr := scanFdInfo(fd, map[string]interface{}{\n\t\t\"map_type\":    &mapType,\n\t\t\"map_id\":      &mi.id,\n\t\t\"key_size\":    &mi.KeySize,\n\t\t\"value_size\":  &mi.ValueSize,\n\t\t\"max_entries\": &mi.MaxEntries,\n\t\t\"map_flags\":   &mi.Flags,\n\t\t\"map_extra\":   &mi.mapExtra,\n\t\t\"memlock\":     &mi.memlock,\n\t\t\"frozen\":      &mi.frozen,\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif mi.Type == 0 {\n\t\tmi.Type, err = MapTypeForPlatform(platform.Linux, mapType)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"map type: %w\", err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// ID returns the map ID.\n//\n// Available from 4.13.\n//\n// The bool return value indicates whether this optional field is available.\nfunc (mi *MapInfo) ID() (MapID, bool) {\n\treturn mi.id, mi.id > 0\n}\n\n// BTFID returns the BTF ID associated with the Map.\n//\n// The ID is only valid as long as the associated Map is kept alive.\n// Available from 4.18.\n//\n// The bool return value indicates whether this optional field is available and\n// populated. (The field may be available but not populated if the kernel\n// supports the field but the Map was loaded without BTF information.)\nfunc (mi *MapInfo) BTFID() (btf.ID, bool) {\n\treturn mi.btf, mi.btf > 0\n}\n\n// MapExtra returns an opaque field whose meaning is map-specific.\n//\n// Available from 5.16.\n//\n// The bool return value indicates whether this optional field is available and\n// populated, if it was specified during Map creation.\nfunc (mi *MapInfo) MapExtra() (uint64, bool) {\n\treturn mi.mapExtra, mi.mapExtra > 0\n}\n\n// Memlock returns an approximate number of bytes allocated to this map.\n//\n// Available from 4.10.\n//\n// The bool return value indicates whether this optional field is available.\nfunc (mi *MapInfo) Memlock() (uint64, bool) {\n\treturn mi.memlock, mi.memlock > 0\n}\n\n// Frozen indicates whether [Map.Freeze] was called on this map. If true,\n// modifications from user space are not allowed.\n//\n// Available from 5.2. Requires access to procfs.\n//\n// If the kernel doesn't support map freezing, this field will always be false.\nfunc (mi *MapInfo) Frozen() bool {\n\treturn mi.frozen\n}\n\n// ProgramStats contains runtime statistics for a single [Program], returned by\n// [Program.Stats].\n//\n// Will contain mostly zero values if the collection of statistics is not\n// enabled, see [EnableStats].\ntype ProgramStats struct {\n\t// Total accumulated runtime of the Program.\n\t//\n\t// Requires at least Linux 5.8.\n\tRuntime time.Duration\n\n\t// Total number of times the Program has executed.\n\t//\n\t// Requires at least Linux 5.8.\n\tRunCount uint64\n\n\t// Total number of times the program was not executed due to recursion. This\n\t// can happen when another bpf program is already running on the cpu, when bpf\n\t// program execution is interrupted, for example.\n\t//\n\t// Requires at least Linux 5.12.\n\tRecursionMisses uint64\n}\n\nfunc newProgramStatsFromFd(fd *sys.FD) (*ProgramStats, error) {\n\tvar info sys.ProgInfo\n\tif err := sys.ObjInfo(fd, &info); err != nil {\n\t\treturn nil, fmt.Errorf(\"getting program info: %w\", err)\n\t}\n\n\treturn &ProgramStats{\n\t\tRuntime:         time.Duration(info.RunTimeNs),\n\t\tRunCount:        info.RunCnt,\n\t\tRecursionMisses: info.RecursionMisses,\n\t}, nil\n}\n\n// programJitedInfo holds information about JITed info of a program.\ntype programJitedInfo struct {\n\t// ksyms holds the ksym addresses of the BPF program, including those of its\n\t// subprograms.\n\t//\n\t// Available from 4.18.\n\tksyms    []uint64\n\tnumKsyms uint32\n\n\t// insns holds the JITed machine native instructions of the program,\n\t// including those of its subprograms.\n\t//\n\t// Available from 4.13.\n\tinsns    []byte\n\tnumInsns uint32\n\n\t// lineInfos holds the JITed line infos, which are kernel addresses.\n\t//\n\t// Available from 5.0.\n\tlineInfos    []uint64\n\tnumLineInfos uint32\n\n\t// lineInfoRecSize is the size of a single line info record.\n\t//\n\t// Available from 5.0.\n\tlineInfoRecSize uint32\n\n\t// funcLens holds the insns length of each function.\n\t//\n\t// Available from 4.18.\n\tfuncLens    []uint32\n\tnumFuncLens uint32\n}\n\n// ProgramInfo describes a Program's immutable metadata. For runtime statistics,\n// see [ProgramStats].\ntype ProgramInfo struct {\n\tType ProgramType\n\tid   ProgramID\n\t// Truncated hash of the BPF bytecode. Available from 4.13.\n\tTag string\n\t// Name as supplied by user space at load time. Available from 4.15.\n\tName string\n\n\tcreatedByUID     uint32\n\thaveCreatedByUID bool\n\tbtf              btf.ID\n\tloadTime         time.Duration\n\n\trestricted bool\n\n\tmaps                 []MapID\n\tinsns                []byte\n\tnumInsns             uint32\n\tjitedSize            uint32\n\tverifiedInstructions uint32\n\n\tjitedInfo programJitedInfo\n\n\tlineInfos    []byte\n\tnumLineInfos uint32\n\tfuncInfos    []byte\n\tnumFuncInfos uint32\n\n\tmemlock uint64\n}\n\n// minimalProgramFromFd queries the minimum information needed to create a\n// Program based on a file descriptor, requiring at least the program type.\n//\n// Does not fall back to fdinfo since the version gap between fdinfo (4.10) and\n// [sys.ObjInfo] (4.13) is small and both kernels are EOL since at least Nov\n// 2017.\n//\n// Requires at least Linux 4.13.\nfunc minimalProgramInfoFromFd(fd *sys.FD) (*ProgramInfo, error) {\n\tvar info sys.ProgInfo\n\tif err := sys.ObjInfo(fd, &info); err != nil {\n\t\treturn nil, fmt.Errorf(\"getting object info: %w\", err)\n\t}\n\n\ttyp, err := ProgramTypeForPlatform(platform.Native, info.Type)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"program type: %w\", err)\n\t}\n\n\treturn &ProgramInfo{\n\t\tType: typ,\n\t\tName: unix.ByteSliceToString(info.Name[:]),\n\t}, nil\n}\n\n// newProgramInfoFromFd queries program information about the given fd.\n//\n// [sys.ObjInfo] is attempted first, supplementing any missing values with\n// information from /proc/self/fdinfo. Ignores EINVAL from ObjInfo as well as\n// ErrNotSupported from reading fdinfo (indicating the file exists, but no\n// fields of interest were found). If both fail, an error is always returned.\nfunc newProgramInfoFromFd(fd *sys.FD) (*ProgramInfo, error) {\n\tvar info sys.ProgInfo\n\terr1 := sys.ObjInfo(fd, &info)\n\t// EINVAL means the kernel doesn't support BPF_OBJ_GET_INFO_BY_FD. Continue\n\t// with fdinfo if that's the case.\n\tif err1 != nil && !errors.Is(err1, unix.EINVAL) {\n\t\treturn nil, fmt.Errorf(\"getting object info: %w\", err1)\n\t}\n\n\ttyp, err := ProgramTypeForPlatform(platform.Native, info.Type)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"program type: %w\", err)\n\t}\n\n\tpi := ProgramInfo{\n\t\tType:                 typ,\n\t\tid:                   ProgramID(info.Id),\n\t\tTag:                  hex.EncodeToString(info.Tag[:]),\n\t\tName:                 unix.ByteSliceToString(info.Name[:]),\n\t\tbtf:                  btf.ID(info.BtfId),\n\t\tjitedSize:            info.JitedProgLen,\n\t\tloadTime:             time.Duration(info.LoadTime),\n\t\tverifiedInstructions: info.VerifiedInsns,\n\t\tnumInsns:             info.XlatedProgLen,\n\t}\n\n\t// Supplement OBJ_INFO with data from /proc/self/fdinfo. It contains fields\n\t// like memlock that is not present in OBJ_INFO.\n\terr2 := readProgramInfoFromProc(fd, &pi)\n\tif err2 != nil && !errors.Is(err2, ErrNotSupported) {\n\t\treturn nil, fmt.Errorf(\"getting map info from fdinfo: %w\", err2)\n\t}\n\n\tif err1 != nil && err2 != nil {\n\t\treturn nil, fmt.Errorf(\"ObjInfo and fdinfo both failed: objinfo: %w, fdinfo: %w\", err1, err2)\n\t}\n\n\tif platform.IsWindows && info.Tag == [8]uint8{} {\n\t\t// Windows doesn't support the tag field, clear it for now.\n\t\tpi.Tag = \"\"\n\t}\n\n\t// Start with a clean struct for the second call, otherwise we may get EFAULT.\n\tvar info2 sys.ProgInfo\n\n\tmakeSecondCall := false\n\n\tif info.NrMapIds > 0 {\n\t\tpi.maps = make([]MapID, info.NrMapIds)\n\t\tinfo2.NrMapIds = info.NrMapIds\n\t\tinfo2.MapIds = sys.SlicePointer(pi.maps)\n\t\tmakeSecondCall = true\n\t} else if haveProgramInfoMapIDs() == nil {\n\t\t// This program really has no associated maps.\n\t\tpi.maps = make([]MapID, 0)\n\t} else {\n\t\t// The kernel doesn't report associated maps.\n\t\tpi.maps = nil\n\t}\n\n\t// createdByUID and NrMapIds were introduced in the same kernel version.\n\tif pi.maps != nil && platform.IsLinux {\n\t\tpi.createdByUID = info.CreatedByUid\n\t\tpi.haveCreatedByUID = true\n\t}\n\n\tif info.XlatedProgLen > 0 {\n\t\tpi.insns = make([]byte, info.XlatedProgLen)\n\t\tvar info3 sys.ProgInfo\n\t\tinfo3.XlatedProgLen = info.XlatedProgLen\n\t\tinfo3.XlatedProgInsns = sys.SlicePointer(pi.insns)\n\n\t\t// When kernel.kptr_restrict and net.core.bpf_jit_harden are both set, it causes the\n\t\t// syscall to abort when trying to readback xlated instructions, skipping other info\n\t\t// as well. So request xlated instructions separately.\n\t\tif err := sys.ObjInfo(fd, &info3); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif info3.XlatedProgInsns.IsNil() {\n\t\t\tpi.restricted = true\n\t\t\tpi.insns = nil\n\t\t}\n\t}\n\n\tif info.NrLineInfo > 0 {\n\t\tpi.lineInfos = make([]byte, btf.LineInfoSize*info.NrLineInfo)\n\t\tinfo2.LineInfo = sys.SlicePointer(pi.lineInfos)\n\t\tinfo2.LineInfoRecSize = btf.LineInfoSize\n\t\tinfo2.NrLineInfo = info.NrLineInfo\n\t\tpi.numLineInfos = info.NrLineInfo\n\t\tmakeSecondCall = true\n\t}\n\n\tif info.NrFuncInfo > 0 {\n\t\tpi.funcInfos = make([]byte, btf.FuncInfoSize*info.NrFuncInfo)\n\t\tinfo2.FuncInfo = sys.SlicePointer(pi.funcInfos)\n\t\tinfo2.FuncInfoRecSize = btf.FuncInfoSize\n\t\tinfo2.NrFuncInfo = info.NrFuncInfo\n\t\tpi.numFuncInfos = info.NrFuncInfo\n\t\tmakeSecondCall = true\n\t}\n\n\tpi.jitedInfo.lineInfoRecSize = info.JitedLineInfoRecSize\n\tif info.JitedProgLen > 0 {\n\t\tpi.jitedInfo.numInsns = info.JitedProgLen\n\t\tpi.jitedInfo.insns = make([]byte, info.JitedProgLen)\n\t\tinfo2.JitedProgLen = info.JitedProgLen\n\t\tinfo2.JitedProgInsns = sys.SlicePointer(pi.jitedInfo.insns)\n\t\tmakeSecondCall = true\n\t}\n\n\tif info.NrJitedFuncLens > 0 {\n\t\tpi.jitedInfo.numFuncLens = info.NrJitedFuncLens\n\t\tpi.jitedInfo.funcLens = make([]uint32, info.NrJitedFuncLens)\n\t\tinfo2.NrJitedFuncLens = info.NrJitedFuncLens\n\t\tinfo2.JitedFuncLens = sys.SlicePointer(pi.jitedInfo.funcLens)\n\t\tmakeSecondCall = true\n\t}\n\n\tif info.NrJitedLineInfo > 0 {\n\t\tpi.jitedInfo.numLineInfos = info.NrJitedLineInfo\n\t\tpi.jitedInfo.lineInfos = make([]uint64, info.NrJitedLineInfo)\n\t\tinfo2.NrJitedLineInfo = info.NrJitedLineInfo\n\t\tinfo2.JitedLineInfo = sys.SlicePointer(pi.jitedInfo.lineInfos)\n\t\tinfo2.JitedLineInfoRecSize = info.JitedLineInfoRecSize\n\t\tmakeSecondCall = true\n\t}\n\n\tif info.NrJitedKsyms > 0 {\n\t\tpi.jitedInfo.numKsyms = info.NrJitedKsyms\n\t\tpi.jitedInfo.ksyms = make([]uint64, info.NrJitedKsyms)\n\t\tinfo2.JitedKsyms = sys.SlicePointer(pi.jitedInfo.ksyms)\n\t\tinfo2.NrJitedKsyms = info.NrJitedKsyms\n\t\tmakeSecondCall = true\n\t}\n\n\tif makeSecondCall {\n\t\tif err := sys.ObjInfo(fd, &info2); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif info.JitedProgLen > 0 && info2.JitedProgInsns.IsNil() {\n\t\t\t// JIT information is not available due to kernel.kptr_restrict\n\t\t\tpi.jitedInfo.lineInfos = nil\n\t\t\tpi.jitedInfo.ksyms = nil\n\t\t\tpi.jitedInfo.insns = nil\n\t\t\tpi.jitedInfo.funcLens = nil\n\t\t}\n\t}\n\n\tif len(pi.Name) == len(info.Name)-1 { // Possibly truncated, check BTF info for full name\n\t\tname, err := readNameFromFunc(&pi)\n\t\tif err == nil {\n\t\t\tpi.Name = name\n\t\t} // If an error occurs, keep the truncated name, which is better than none\n\t}\n\n\treturn &pi, nil\n}\n\nfunc readNameFromFunc(pi *ProgramInfo) (string, error) {\n\tif pi.numFuncInfos == 0 {\n\t\treturn \"\", errors.New(\"no function info\")\n\t}\n\n\tspec, err := pi.btfSpec()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tfuncInfos, err := btf.LoadFuncInfos(\n\t\tbytes.NewReader(pi.funcInfos),\n\t\tinternal.NativeEndian,\n\t\tpi.numFuncInfos,\n\t\tspec,\n\t)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tfor _, funcInfo := range funcInfos {\n\t\tif funcInfo.Offset == 0 { // Information about the whole program\n\t\t\treturn funcInfo.Func.Name, nil\n\t\t}\n\t}\n\treturn \"\", errors.New(\"no function info about program\")\n}\n\nfunc readProgramInfoFromProc(fd *sys.FD, pi *ProgramInfo) error {\n\tvar progType uint32\n\terr := scanFdInfo(fd, map[string]interface{}{\n\t\t\"prog_type\": &progType,\n\t\t\"prog_tag\":  &pi.Tag,\n\t\t\"memlock\":   &pi.memlock,\n\t})\n\tif errors.Is(err, ErrNotSupported) && !errors.Is(err, internal.ErrNotSupportedOnOS) {\n\t\treturn &internal.UnsupportedFeatureError{\n\t\t\tName:           \"reading program info from /proc/self/fdinfo\",\n\t\t\tMinimumVersion: internal.Version{4, 10, 0},\n\t\t}\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tpi.Type, err = ProgramTypeForPlatform(platform.Linux, progType)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"program type: %w\", err)\n\t}\n\n\treturn nil\n}\n\n// ID returns the program ID.\n//\n// Available from 4.13.\n//\n// The bool return value indicates whether this optional field is available.\nfunc (pi *ProgramInfo) ID() (ProgramID, bool) {\n\treturn pi.id, pi.id > 0\n}\n\n// CreatedByUID returns the Uid that created the program.\n//\n// Available from 4.15.\n//\n// The bool return value indicates whether this optional field is available.\nfunc (pi *ProgramInfo) CreatedByUID() (uint32, bool) {\n\treturn pi.createdByUID, pi.haveCreatedByUID\n}\n\n// BTFID returns the BTF ID associated with the program.\n//\n// The ID is only valid as long as the associated program is kept alive.\n// Available from 5.0.\n//\n// The bool return value indicates whether this optional field is available and\n// populated. (The field may be available but not populated if the kernel\n// supports the field but the program was loaded without BTF information.)\nfunc (pi *ProgramInfo) BTFID() (btf.ID, bool) {\n\treturn pi.btf, pi.btf > 0\n}\n\n// btfSpec returns the BTF spec associated with the program.\nfunc (pi *ProgramInfo) btfSpec() (*btf.Spec, error) {\n\tid, ok := pi.BTFID()\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"program created without BTF or unsupported kernel: %w\", ErrNotSupported)\n\t}\n\n\th, err := btf.NewHandleFromID(id)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"get BTF handle: %w\", err)\n\t}\n\tdefer h.Close()\n\n\tspec, err := h.Spec(nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"get BTF spec: %w\", err)\n\t}\n\n\treturn spec, nil\n}\n\n// ErrRestrictedKernel is returned when kernel address information is restricted\n// by kernel.kptr_restrict and/or net.core.bpf_jit_harden sysctls.\nvar ErrRestrictedKernel = internal.ErrRestrictedKernel\n\n// LineInfos returns the BTF line information of the program.\n//\n// Available from 5.0.\n//\n// Returns an error wrapping [ErrRestrictedKernel] if line infos are restricted\n// by sysctls.\n//\n// Requires CAP_SYS_ADMIN or equivalent for reading BTF information. Returns\n// ErrNotSupported if the program was created without BTF or if the kernel\n// doesn't support the field.\nfunc (pi *ProgramInfo) LineInfos() (btf.LineOffsets, error) {\n\tif len(pi.lineInfos) == 0 {\n\t\treturn nil, fmt.Errorf(\"insufficient permissions or unsupported kernel: %w\", ErrNotSupported)\n\t}\n\n\tspec, err := pi.btfSpec()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn btf.LoadLineInfos(\n\t\tbytes.NewReader(pi.lineInfos),\n\t\tinternal.NativeEndian,\n\t\tpi.numLineInfos,\n\t\tspec,\n\t)\n}\n\n// Instructions returns the 'xlated' instruction stream of the program\n// after it has been verified and rewritten by the kernel. These instructions\n// cannot be loaded back into the kernel as-is, this is mainly used for\n// inspecting loaded programs for troubleshooting, dumping, etc.\n//\n// For example, map accesses are made to reference their kernel map IDs,\n// not the FDs they had when the program was inserted. Note that before\n// the introduction of bpf_insn_prepare_dump in kernel 4.16, xlated\n// instructions were not sanitized, making the output even less reusable\n// and less likely to round-trip or evaluate to the same program Tag.\n//\n// The first instruction is marked as a symbol using the Program's name.\n//\n// If available, the instructions will be annotated with metadata from the\n// BTF. This includes line information and function information. Reading\n// this metadata requires CAP_SYS_ADMIN or equivalent. If capability is\n// unavailable, the instructions will be returned without metadata.\n//\n// Returns an error wrapping [ErrRestrictedKernel] if instructions are\n// restricted by sysctls.\n//\n// Available from 4.13. Requires CAP_BPF or equivalent for plain instructions.\n// Requires CAP_SYS_ADMIN for instructions with metadata.\nfunc (pi *ProgramInfo) Instructions() (asm.Instructions, error) {\n\tif platform.IsWindows && len(pi.insns) == 0 {\n\t\treturn nil, fmt.Errorf(\"read instructions: %w\", internal.ErrNotSupportedOnOS)\n\t}\n\n\tif pi.restricted {\n\t\treturn nil, fmt.Errorf(\"instructions: %w\", ErrRestrictedKernel)\n\t}\n\n\t// If the calling process is not BPF-capable or if the kernel doesn't\n\t// support getting xlated instructions, the field will be zero.\n\tif len(pi.insns) == 0 {\n\t\treturn nil, fmt.Errorf(\"insufficient permissions or unsupported kernel: %w\", ErrNotSupported)\n\t}\n\n\tr := bytes.NewReader(pi.insns)\n\tinsns, err := asm.AppendInstructions(nil, r, internal.NativeEndian, platform.Native)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unmarshaling instructions: %w\", err)\n\t}\n\n\tif pi.btf != 0 {\n\t\tbtfh, err := btf.NewHandleFromID(pi.btf)\n\t\tif err != nil {\n\t\t\t// Getting a BTF handle requires CAP_SYS_ADMIN, if not available we get an -EPERM.\n\t\t\t// Ignore it and fall back to instructions without metadata.\n\t\t\tif !errors.Is(err, unix.EPERM) {\n\t\t\t\treturn nil, fmt.Errorf(\"unable to get BTF handle: %w\", err)\n\t\t\t}\n\t\t}\n\n\t\t// If we have a BTF handle, we can use it to assign metadata to the instructions.\n\t\tif btfh != nil {\n\t\t\tdefer btfh.Close()\n\n\t\t\tspec, err := btfh.Spec(nil)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"unable to get BTF spec: %w\", err)\n\t\t\t}\n\n\t\t\tlineInfos, err := btf.LoadLineInfos(bytes.NewReader(pi.lineInfos), internal.NativeEndian, pi.numLineInfos, spec)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"parse line info: %w\", err)\n\t\t\t}\n\n\t\t\tfuncInfos, err := btf.LoadFuncInfos(bytes.NewReader(pi.funcInfos), internal.NativeEndian, pi.numFuncInfos, spec)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"parse func info: %w\", err)\n\t\t\t}\n\n\t\t\titer := insns.Iterate()\n\t\t\tfor iter.Next() {\n\t\t\t\tassignMetadata(iter.Ins, iter.Offset, &funcInfos, &lineInfos, nil)\n\t\t\t}\n\t\t}\n\t}\n\n\tfn := btf.FuncMetadata(&insns[0])\n\tname := pi.Name\n\tif fn != nil {\n\t\tname = fn.Name\n\t}\n\tinsns[0] = insns[0].WithSymbol(name)\n\n\treturn insns, nil\n}\n\n// JitedSize returns the size of the program's JIT-compiled machine code in\n// bytes, which is the actual code executed on the host's CPU. This field\n// requires the BPF JIT compiler to be enabled.\n//\n// Returns an error wrapping [ErrRestrictedKernel] if jited program size is\n// restricted by sysctls.\n//\n// Available from 4.13. Reading this metadata requires CAP_BPF or equivalent.\nfunc (pi *ProgramInfo) JitedSize() (uint32, error) {\n\tif pi.jitedSize == 0 {\n\t\treturn 0, fmt.Errorf(\"insufficient permissions, unsupported kernel, or JIT compiler disabled: %w\", ErrNotSupported)\n\t}\n\treturn pi.jitedSize, nil\n}\n\n// TranslatedSize returns the size of the program's translated instructions in\n// bytes, after it has been verified and rewritten by the kernel.\n//\n// Available from 4.13. Reading this metadata requires CAP_BPF or equivalent.\nfunc (pi *ProgramInfo) TranslatedSize() (int, error) {\n\tif pi.numInsns == 0 {\n\t\treturn 0, fmt.Errorf(\"insufficient permissions or unsupported kernel: %w\", ErrNotSupported)\n\t}\n\treturn int(pi.numInsns), nil\n}\n\n// MapIDs returns the maps related to the program.\n//\n// Available from 4.15.\n//\n// The bool return value indicates whether this optional field is available.\nfunc (pi *ProgramInfo) MapIDs() ([]MapID, bool) {\n\treturn pi.maps, pi.maps != nil\n}\n\n// LoadTime returns when the program was loaded since boot time.\n//\n// Available from 4.15.\n//\n// The bool return value indicates whether this optional field is available.\nfunc (pi *ProgramInfo) LoadTime() (time.Duration, bool) {\n\t// loadTime and NrMapIds were introduced in the same kernel version.\n\treturn pi.loadTime, pi.loadTime > 0\n}\n\n// VerifiedInstructions returns the number verified instructions in the program.\n//\n// Available from 5.16.\n//\n// The bool return value indicates whether this optional field is available.\nfunc (pi *ProgramInfo) VerifiedInstructions() (uint32, bool) {\n\treturn pi.verifiedInstructions, pi.verifiedInstructions > 0\n}\n\n// JitedKsymAddrs returns the ksym addresses of the BPF program, including its\n// subprograms. The addresses correspond to their symbols in /proc/kallsyms.\n//\n// Available from 4.18. Note that before 5.x, this field can be empty for\n// programs without subprograms (bpf2bpf calls).\n//\n// The bool return value indicates whether this optional field is available.\n//\n// When a kernel address can't fit into uintptr (which is usually the case when\n// running 32 bit program on a 64 bit kernel), this returns an empty slice and\n// a false.\nfunc (pi *ProgramInfo) JitedKsymAddrs() ([]uintptr, bool) {\n\tksyms := make([]uintptr, 0, len(pi.jitedInfo.ksyms))\n\tif cap(ksyms) == 0 {\n\t\treturn ksyms, false\n\t}\n\t// Check if a kernel address fits into uintptr (it might not when\n\t// using a 32 bit binary on a 64 bit kernel). This check should work\n\t// with any kernel address, since they have 1s at the highest bits.\n\tif a := pi.jitedInfo.ksyms[0]; uint64(uintptr(a)) != a {\n\t\treturn nil, false\n\t}\n\tfor _, ksym := range pi.jitedInfo.ksyms {\n\t\tksyms = append(ksyms, uintptr(ksym))\n\t}\n\treturn ksyms, true\n}\n\n// JitedInsns returns the JITed machine native instructions of the program.\n//\n// Available from 4.13.\n//\n// The bool return value indicates whether this optional field is available.\nfunc (pi *ProgramInfo) JitedInsns() ([]byte, bool) {\n\treturn pi.jitedInfo.insns, len(pi.jitedInfo.insns) > 0\n}\n\n// JitedLineInfos returns the JITed line infos of the program.\n//\n// Available from 5.0.\n//\n// The bool return value indicates whether this optional field is available.\nfunc (pi *ProgramInfo) JitedLineInfos() ([]uint64, bool) {\n\treturn pi.jitedInfo.lineInfos, len(pi.jitedInfo.lineInfos) > 0\n}\n\n// JitedFuncLens returns the insns length of each function in the JITed program.\n//\n// Available from 4.18.\n//\n// The bool return value indicates whether this optional field is available.\nfunc (pi *ProgramInfo) JitedFuncLens() ([]uint32, bool) {\n\treturn pi.jitedInfo.funcLens, len(pi.jitedInfo.funcLens) > 0\n}\n\n// FuncInfos returns the offset and function information of all (sub)programs in\n// a BPF program.\n//\n// Available from 5.0.\n//\n// Returns an error wrapping [ErrRestrictedKernel] if function information is\n// restricted by sysctls.\n//\n// Requires CAP_SYS_ADMIN or equivalent for reading BTF information. Returns\n// ErrNotSupported if the program was created without BTF or if the kernel\n// doesn't support the field.\nfunc (pi *ProgramInfo) FuncInfos() (btf.FuncOffsets, error) {\n\tif len(pi.funcInfos) == 0 {\n\t\treturn nil, fmt.Errorf(\"insufficient permissions or unsupported kernel: %w\", ErrNotSupported)\n\t}\n\n\tspec, err := pi.btfSpec()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn btf.LoadFuncInfos(\n\t\tbytes.NewReader(pi.funcInfos),\n\t\tinternal.NativeEndian,\n\t\tpi.numFuncInfos,\n\t\tspec,\n\t)\n}\n\n// ProgramInfo returns an approximate number of bytes allocated to this program.\n//\n// Available from 4.10.\n//\n// The bool return value indicates whether this optional field is available.\nfunc (pi *ProgramInfo) Memlock() (uint64, bool) {\n\treturn pi.memlock, pi.memlock > 0\n}\n\nfunc scanFdInfo(fd *sys.FD, fields map[string]interface{}) error {\n\tif platform.IsWindows {\n\t\treturn fmt.Errorf(\"read fdinfo: %w\", internal.ErrNotSupportedOnOS)\n\t}\n\n\tfh, err := os.Open(fmt.Sprintf(\"/proc/self/fdinfo/%d\", fd.Int()))\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer fh.Close()\n\n\tif err := scanFdInfoReader(fh, fields); err != nil {\n\t\treturn fmt.Errorf(\"%s: %w\", fh.Name(), err)\n\t}\n\treturn nil\n}\n\nfunc scanFdInfoReader(r io.Reader, fields map[string]interface{}) error {\n\tvar (\n\t\tscanner = bufio.NewScanner(r)\n\t\tscanned int\n\t\treader  bytes.Reader\n\t)\n\n\tfor scanner.Scan() {\n\t\tkey, rest, found := bytes.Cut(scanner.Bytes(), []byte(\":\"))\n\t\tif !found {\n\t\t\t// Line doesn't contain a colon, skip.\n\t\t\tcontinue\n\t\t}\n\t\tfield, ok := fields[string(key)]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\t// If field already contains a non-zero value, don't overwrite it with fdinfo.\n\t\tif !zero(field) {\n\t\t\tscanned++\n\t\t\tcontinue\n\t\t}\n\n\t\t// Cut the \\t following the : as well as any potential trailing whitespace.\n\t\trest = bytes.TrimSpace(rest)\n\n\t\treader.Reset(rest)\n\t\tif n, err := fmt.Fscan(&reader, field); err != nil || n != 1 {\n\t\t\treturn fmt.Errorf(\"can't parse field %s: %v\", key, err)\n\t\t}\n\n\t\tscanned++\n\t}\n\n\tif err := scanner.Err(); err != nil {\n\t\treturn fmt.Errorf(\"scanning fdinfo: %w\", err)\n\t}\n\n\tif len(fields) > 0 && scanned == 0 {\n\t\treturn ErrNotSupported\n\t}\n\n\treturn nil\n}\n\nfunc zero(arg any) bool {\n\tv := reflect.ValueOf(arg)\n\n\t// Unwrap pointers and interfaces.\n\tfor v.Kind() == reflect.Pointer ||\n\t\tv.Kind() == reflect.Interface {\n\t\tv = v.Elem()\n\t}\n\n\treturn v.IsZero()\n}\n\n// EnableStats starts collecting runtime statistics of eBPF programs, like the\n// amount of program executions and the cumulative runtime.\n//\n// Specify a BPF_STATS_* constant to select which statistics to collect, like\n// [unix.BPF_STATS_RUN_TIME]. Closing the returned [io.Closer] will stop\n// collecting statistics.\n//\n// Collecting statistics may have a performance impact.\n//\n// Requires at least Linux 5.8.\nfunc EnableStats(which uint32) (io.Closer, error) {\n\tfd, err := sys.EnableStats(&sys.EnableStatsAttr{\n\t\tType: which,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn fd, nil\n}\n\nvar haveProgramInfoMapIDs = internal.NewFeatureTest(\"map IDs in program info\", func() error {\n\tif platform.IsWindows {\n\t\t// We only support efW versions which have this feature, no need to probe.\n\t\treturn nil\n\t}\n\n\tprog, err := progLoad(asm.Instructions{\n\t\tasm.LoadImm(asm.R0, 0, asm.DWord),\n\t\tasm.Return(),\n\t}, SocketFilter, \"MIT\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer prog.Close()\n\n\terr = sys.ObjInfo(prog, &sys.ProgInfo{\n\t\t// NB: Don't need to allocate MapIds since the program isn't using\n\t\t// any maps.\n\t\tNrMapIds: 1,\n\t})\n\tif errors.Is(err, unix.EINVAL) {\n\t\t// Most likely the syscall doesn't exist.\n\t\treturn internal.ErrNotSupported\n\t}\n\tif errors.Is(err, unix.E2BIG) {\n\t\t// We've hit check_uarg_tail_zero on older kernels.\n\t\treturn internal.ErrNotSupported\n\t}\n\n\treturn err\n}, \"4.15\", \"windows:0.21.0\")\n"
  },
  {
    "path": "info_test.go",
    "content": "package ebpf\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nvar btfFn = &btf.Func{\n\tName: \"_\",\n\tType: &btf.FuncProto{\n\t\tReturn: &btf.Int{Size: 16},\n\t\tParams: []btf.FuncParam{},\n\t},\n\tLinkage: btf.StaticFunc,\n}\n\nvar hashMapSpec = &MapSpec{\n\tType:       Hash,\n\tKeySize:    4,\n\tValueSize:  5,\n\tMaxEntries: 2,\n\tFlags:      sys.BPF_F_NO_PREALLOC,\n}\n\nvar multiprogSpec = &ProgramSpec{\n\tName: \"test\",\n\tType: SocketFilter,\n\tInstructions: asm.Instructions{\n\t\tbtf.WithFuncMetadata(asm.LoadImm(asm.R0, 0, asm.DWord), btfFn).\n\t\t\tWithSource(asm.Comment(\"line info\")),\n\t\tasm.Call.Label(\"fn\"),\n\t\tasm.Return(),\n\t\tbtf.WithFuncMetadata(asm.LoadImm(asm.R0, 0, asm.DWord), btfFn).\n\t\t\tWithSource(asm.Comment(\"line info\")).WithSymbol(\"fn\"),\n\t\tasm.Return(),\n\t},\n\tLicense: \"MIT\",\n}\n\nfunc validateMapInfo(t *testing.T, info *MapInfo, spec *MapSpec) {\n\tt.Helper()\n\n\tqt.Assert(t, qt.Equals(info.Type, spec.Type))\n\tqt.Assert(t, qt.Equals(info.KeySize, spec.KeySize))\n\tqt.Assert(t, qt.Equals(info.ValueSize, spec.ValueSize))\n\tqt.Assert(t, qt.Equals(info.MaxEntries, spec.MaxEntries))\n\tqt.Assert(t, qt.Equals(info.Flags, spec.Flags))\n\n\tmemlock, _ := info.Memlock()\n\tqt.Assert(t, qt.Not(qt.Equals(memlock, 0)))\n}\n\nfunc TestMapInfo(t *testing.T) {\n\tm := mustNewMap(t, hashMapSpec, nil)\n\n\tinfo, err := m.Info()\n\tqt.Assert(t, qt.IsNil(err))\n\n\tvalidateMapInfo(t, info, hashMapSpec)\n}\n\nfunc TestMapInfoFromProc(t *testing.T) {\n\thash := mustNewMap(t, hashMapSpec, nil)\n\n\tvar info MapInfo\n\terr := readMapInfoFromProc(hash.fd, &info)\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tvalidateMapInfo(t, &info, hashMapSpec)\n}\n\nfunc TestMapInfoFromProcOuterMap(t *testing.T) {\n\touter := &MapSpec{\n\t\tType:       ArrayOfMaps,\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: 2,\n\t\tInnerMap: &MapSpec{\n\t\t\tType:       Array,\n\t\t\tKeySize:    4,\n\t\t\tValueSize:  4,\n\t\t\tMaxEntries: 2,\n\t\t},\n\t}\n\tm := mustNewMap(t, outer, nil)\n\n\tvar info MapInfo\n\terr := readMapInfoFromProc(m.fd, &info)\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tvalidateMapInfo(t, &info, outer)\n}\n\nfunc BenchmarkNewMapFromFD(b *testing.B) {\n\tb.ReportAllocs()\n\n\tm := mustNewMap(b, hashMapSpec, nil)\n\n\tfor b.Loop() {\n\t\tif _, err := newMapFromFD(m.fd); err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkMapInfo(b *testing.B) {\n\tb.ReportAllocs()\n\n\tm := mustNewMap(b, hashMapSpec, nil)\n\n\tfor b.Loop() {\n\t\tif _, err := newMapInfoFromFd(m.fd); err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc validateProgInfo(t *testing.T, spec *ProgramSpec, info *ProgramInfo) {\n\tt.Helper()\n\n\tqt.Assert(t, qt.Equals(info.Type, spec.Type))\n\tif info.Tag != \"\" {\n\t\tqt.Assert(t, qt.SliceAny(\n\t\t\t[]string{\n\t\t\t\t\"d7edec644f05498d\", // SHA1, pre-6.18\n\t\t\t\t\"01e57aadad14352b\", // SHA256\n\t\t\t},\n\t\t\tqt.F2(qt.Equals, info.Tag),\n\t\t))\n\t}\n\tmemlock, ok := info.Memlock()\n\tif ok {\n\t\tqt.Assert(t, qt.Equals(memlock, 4096))\n\t}\n}\n\nfunc TestProgramInfo(t *testing.T) {\n\tspec := fixupProgramSpec(basicProgramSpec)\n\tprog := mustNewProgram(t, spec, nil)\n\n\tinfo, err := newProgramInfoFromFd(prog.fd)\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tvalidateProgInfo(t, spec, info)\n\n\tid, ok := info.ID()\n\tqt.Assert(t, qt.IsTrue(ok))\n\tqt.Assert(t, qt.Not(qt.Equals(id, 0)))\n\n\tif testutils.IsVersionLessThan(t, \"4.15\", \"windows:0.20\") {\n\t\tqt.Assert(t, qt.Equals(info.Name, \"\"))\n\t} else {\n\t\tqt.Assert(t, qt.Equals(info.Name, \"test\"))\n\t}\n\n\tif jitedSize, err := info.JitedSize(); testutils.IsVersionLessThan(t, \"4.13\") {\n\t\tqt.Assert(t, qt.IsNotNil(err))\n\t} else {\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.IsTrue(jitedSize > 0))\n\t}\n\n\tif xlatedSize, err := info.TranslatedSize(); testutils.IsVersionLessThan(t, \"4.13\") {\n\t\tqt.Assert(t, qt.IsNotNil(err))\n\t} else {\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.IsTrue(xlatedSize > 0))\n\t}\n\n\tif uid, ok := info.CreatedByUID(); testutils.IsVersionLessThan(t, \"4.15\") {\n\t\tqt.Assert(t, qt.IsFalse(ok))\n\t} else {\n\t\tqt.Assert(t, qt.IsTrue(ok))\n\t\tqt.Assert(t, qt.Equals(uid, uint32(os.Getuid())))\n\t}\n\n\tif loadTime, ok := info.LoadTime(); testutils.IsVersionLessThan(t, \"4.15\") {\n\t\tqt.Assert(t, qt.IsFalse(ok))\n\t} else {\n\t\tqt.Assert(t, qt.IsTrue(ok))\n\t\tqt.Assert(t, qt.IsTrue(loadTime > 0))\n\t}\n\n\tif verifiedInsns, ok := info.VerifiedInstructions(); testutils.IsVersionLessThan(t, \"5.16\") {\n\t\tqt.Assert(t, qt.IsFalse(ok))\n\t} else {\n\t\tqt.Assert(t, qt.IsTrue(ok))\n\t\tqt.Assert(t, qt.IsTrue(verifiedInsns > 0))\n\t}\n\n\tif insns, ok := info.JitedInsns(); testutils.IsVersionLessThan(t, \"4.13\") {\n\t\tqt.Assert(t, qt.IsFalse(ok))\n\t} else {\n\t\tqt.Assert(t, qt.IsTrue(ok))\n\t\tqt.Assert(t, qt.IsTrue(len(insns) > 0))\n\t}\n}\n\nfunc BenchmarkNewProgramFromFD(b *testing.B) {\n\tb.ReportAllocs()\n\n\tspec := fixupProgramSpec(basicProgramSpec)\n\tprog := mustNewProgram(b, spec, nil)\n\n\tfor b.Loop() {\n\t\tif _, err := newProgramFromFD(prog.fd); err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkProgramInfo(b *testing.B) {\n\tb.ReportAllocs()\n\n\tspec := fixupProgramSpec(basicProgramSpec)\n\tprog := mustNewProgram(b, spec, nil)\n\n\tfor b.Loop() {\n\t\tif _, err := newProgramInfoFromFd(prog.fd); err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc TestProgramInfoProc(t *testing.T) {\n\tspec := fixupProgramSpec(basicProgramSpec)\n\tprog := mustNewProgram(t, spec, nil)\n\n\tvar info ProgramInfo\n\terr := readProgramInfoFromProc(prog.fd, &info)\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tvalidateProgInfo(t, spec, &info)\n}\n\nfunc TestProgramInfoBTF(t *testing.T) {\n\tprog, err := newProgram(t, multiprogSpec, nil)\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tinfo, err := prog.Info()\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\n\t// On kernels before 5.x, nr_jited_ksyms is not set for programs without subprogs.\n\t// It's included here since this test uses a bpf program with subprogs.\n\tif addrs, ok := info.JitedKsymAddrs(); testutils.IsVersionLessThan(t, \"4.18\") {\n\t\tqt.Assert(t, qt.IsFalse(ok))\n\t} else {\n\t\tqt.Assert(t, qt.IsTrue(ok))\n\t\tqt.Assert(t, qt.IsTrue(len(addrs) > 0))\n\t}\n\n\tif lens, ok := info.JitedFuncLens(); testutils.IsVersionLessThan(t, \"4.18\") {\n\t\tqt.Assert(t, qt.IsFalse(ok))\n\t} else {\n\t\tqt.Assert(t, qt.IsTrue(ok))\n\t\tqt.Assert(t, qt.IsTrue(len(lens) > 0))\n\t}\n\n\tif infos, ok := info.JitedLineInfos(); testutils.IsVersionLessThan(t, \"5.0\") {\n\t\tqt.Assert(t, qt.IsFalse(ok))\n\t} else {\n\t\tqt.Assert(t, qt.IsTrue(ok))\n\t\tqt.Assert(t, qt.IsTrue(len(infos) > 0))\n\t}\n\n\tif funcs, err := info.FuncInfos(); testutils.IsVersionLessThan(t, \"5.0\") {\n\t\tqt.Assert(t, qt.IsNotNil(err))\n\t} else {\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.HasLen(funcs, 2))\n\t\tqt.Assert(t, qt.ContentEquals(funcs[0].Func, btfFn))\n\t\tqt.Assert(t, qt.ContentEquals(funcs[1].Func, btfFn))\n\t}\n\n\tif lines, err := info.LineInfos(); testutils.IsVersionLessThan(t, \"5.0\") {\n\t\tqt.Assert(t, qt.IsNotNil(err))\n\t} else {\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.HasLen(lines, 2))\n\t\tqt.Assert(t, qt.Equals(lines[0].Line.Line(), \"line info\"))\n\t\tqt.Assert(t, qt.Equals(lines[1].Line.Line(), \"line info\"))\n\t}\n}\n\nfunc TestProgramInfoMapIDs(t *testing.T) {\n\tarr := createMap(t, Array, 1)\n\n\tprog := mustNewProgram(t, &ProgramSpec{\n\t\tType: SocketFilter,\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.LoadMapPtr(asm.R0, arr.FD()),\n\t\t\tasm.LoadImm(asm.R0, 2, asm.DWord),\n\t\t\tasm.Return(),\n\t\t},\n\t\tLicense: \"MIT\",\n\t}, nil)\n\n\tinfo, err := prog.Info()\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tids, ok := info.MapIDs()\n\tswitch {\n\tcase testutils.IsVersionLessThan(t, \"4.15\", \"windows:0.20\"):\n\t\tqt.Assert(t, qt.IsFalse(ok))\n\t\tqt.Assert(t, qt.HasLen(ids, 0))\n\n\tdefault:\n\t\tqt.Assert(t, qt.IsTrue(ok))\n\n\t\tmapInfo, err := arr.Info()\n\t\tqt.Assert(t, qt.IsNil(err))\n\n\t\tmapID, ok := mapInfo.ID()\n\t\tqt.Assert(t, qt.IsTrue(ok))\n\t\tqt.Assert(t, qt.ContentEquals(ids, []MapID{mapID}))\n\t}\n}\n\nfunc TestProgramInfoMapIDsNoMaps(t *testing.T) {\n\tprog := createBasicProgram(t)\n\n\tinfo, err := prog.Info()\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tids, ok := info.MapIDs()\n\tswitch {\n\tcase testutils.IsVersionLessThan(t, \"4.15\", \"windows:0.20\"):\n\t\tqt.Assert(t, qt.IsFalse(ok))\n\t\tqt.Assert(t, qt.HasLen(ids, 0))\n\n\tdefault:\n\t\tqt.Assert(t, qt.IsTrue(ok))\n\t\tqt.Assert(t, qt.HasLen(ids, 0))\n\t}\n}\n\nfunc TestScanFdInfoReader(t *testing.T) {\n\ttests := []struct {\n\t\tfields map[string]interface{}\n\t\tvalid  bool\n\t}{\n\t\t{nil, true},\n\t\t{map[string]interface{}{\"foo\": new(string)}, true},\n\t\t{map[string]interface{}{\"zap\": new(string)}, false},\n\t\t{map[string]interface{}{\"foo\": new(int)}, false},\n\t}\n\n\tfor _, test := range tests {\n\t\terr := scanFdInfoReader(strings.NewReader(\"foo:\\tbar\\n\"), test.fields)\n\t\tif test.valid {\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"fields %v returns an error: %s\", test.fields, err)\n\t\t\t}\n\t\t} else {\n\t\t\tif err == nil {\n\t\t\t\tt.Errorf(\"fields %v doesn't return an error\", test.fields)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc BenchmarkScanFdInfoReader(b *testing.B) {\n\tb.ReportAllocs()\n\n\t// Pathological case with 9 fields we're not interested in, and one\n\t// field we are, all the way at the very end.\n\tinput := strings.Repeat(\"ignore:\\tthis\\n\", 9)\n\tinput += \"foo:\\tbar\\n\"\n\tr := strings.NewReader(input)\n\n\tvar val string\n\tfields := map[string]any{\"foo\": &val}\n\n\tfor b.Loop() {\n\t\tval = \"\"\n\t\tr.Reset(input)\n\n\t\tif err := scanFdInfoReader(r, fields); err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t\tif val != \"bar\" {\n\t\t\tb.Fatal(\"unexpected value:\", val)\n\t\t}\n\t}\n}\n\n// TestProgramStats loads a BPF program once and executes back-to-back test runs\n// of the program. See testStats for details.\nfunc TestProgramStats(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.8\", \"BPF_ENABLE_STATS\")\n\n\tprog := createBasicProgram(t)\n\n\ts, err := prog.Stats()\n\tqt.Assert(t, qt.IsNil(err))\n\n\tqt.Assert(t, qt.Equals(s.RunCount, 0))\n\tqt.Assert(t, qt.Equals(s.RecursionMisses, 0))\n\n\tif runtime.GOARCH != \"arm64\" {\n\t\t// Runtime is flaky on arm64.\n\t\tqt.Assert(t, qt.Equals(s.Runtime, 0))\n\t}\n\n\tif err := testStats(t, prog); err != nil {\n\t\ttestutils.SkipIfNotSupportedOnOS(t, err)\n\t\tt.Error(err)\n\t}\n}\n\n// BenchmarkStats is a benchmark of TestStats. See testStats for details.\nfunc BenchmarkStats(b *testing.B) {\n\tb.ReportAllocs()\n\n\ttestutils.SkipOnOldKernel(b, \"5.8\", \"BPF_ENABLE_STATS\")\n\n\tprog := createBasicProgram(b)\n\n\tfor b.Loop() {\n\t\tif err := testStats(b, prog); err != nil {\n\t\t\ttestutils.SkipIfNotSupportedOnOS(b, err)\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\n// testStats implements the behaviour under test for TestStats\n// and BenchmarkStats. First, a test run is executed with runtime statistics\n// enabled, followed by another with runtime stats disabled. Counters are only\n// expected to increase on the runs where runtime stats are enabled.\n//\n// Due to runtime behaviour on Go 1.14 and higher, the syscall backing\n// (*Program).Test() could be invoked multiple times for each call to Test(),\n// resulting in RunCount incrementing by more than one. Expecting RunCount to\n// be of a specific value after a call to Test() is therefore not possible.\n// See https://golang.org/doc/go1.14#runtime for more details.\nfunc testStats(tb testing.TB, prog *Program) error {\n\ttb.Helper()\n\n\tin := internal.EmptyBPFContext\n\n\tstats, err := EnableStats(uint32(sys.BPF_STATS_RUN_TIME))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to enable stats: %w\", err)\n\t}\n\tdefer stats.Close()\n\n\t// Program execution with runtime statistics enabled.\n\t// Should increase both runtime and run counter.\n\tmustRun(tb, prog, &RunOptions{Data: in})\n\n\ts1, err := prog.Stats()\n\tqt.Assert(tb, qt.IsNil(err))\n\n\tqt.Assert(tb, qt.Not(qt.Equals(s1.RunCount, 0)), qt.Commentf(\"expected run count to be at least 1 after first invocation\"))\n\tqt.Assert(tb, qt.Not(qt.Equals(s1.Runtime, 0)), qt.Commentf(\"expected runtime to be at least 1ns after first invocation\"))\n\n\tqt.Assert(tb, qt.IsNil(stats.Close()))\n\n\t// Second program execution, with runtime statistics gathering disabled.\n\t// Total runtime and run counters are not expected to increase.\n\tmustRun(tb, prog, &RunOptions{Data: in})\n\n\ts2, err := prog.Stats()\n\tqt.Assert(tb, qt.IsNil(err))\n\n\tqt.Assert(tb, qt.Equals(s2.RunCount, s1.RunCount), qt.Commentf(\"run count (%d) increased after first invocation (%d)\", s2.RunCount, s1.RunCount))\n\tqt.Assert(tb, qt.Equals(s2.Runtime, s1.Runtime), qt.Commentf(\"runtime (%d) increased after first invocation (%d)\", s2.Runtime, s1.Runtime))\n\n\treturn nil\n}\n\nfunc TestHaveProgramInfoMapIDs(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, haveProgramInfoMapIDs)\n}\n\nfunc TestProgInfoExtBTF(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.0\", \"Program BTF (func/line_info)\")\n\n\tspec, err := LoadCollectionSpec(testutils.NativeFile(t, \"testdata/loader-%s.elf\"))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tvar obj struct {\n\t\tMain *Program `ebpf:\"xdp_prog\"`\n\t}\n\n\terr = loadAndAssign(t, spec, &obj, nil)\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer obj.Main.Close()\n\n\tinfo, err := obj.Main.Info()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tinst, err := info.Instructions()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\texpectedLineInfoCount := 28\n\texpectedFuncInfo := map[string]bool{\n\t\t\"xdp_prog\":   false,\n\t\t\"static_fn\":  false,\n\t\t\"global_fn2\": false,\n\t\t\"global_fn3\": false,\n\t}\n\n\tlineInfoCount := 0\n\n\tfor _, ins := range inst {\n\t\tif ins.Source() != nil {\n\t\t\tlineInfoCount++\n\t\t}\n\n\t\tfn := btf.FuncMetadata(&ins)\n\t\tif fn != nil {\n\t\t\texpectedFuncInfo[fn.Name] = true\n\t\t}\n\t}\n\n\tif lineInfoCount != expectedLineInfoCount {\n\t\tt.Errorf(\"expected %d line info entries, got %d\", expectedLineInfoCount, lineInfoCount)\n\t}\n\n\tfor fn, found := range expectedFuncInfo {\n\t\tif !found {\n\t\t\tt.Errorf(\"func %q not found\", fn)\n\t\t}\n\t}\n}\n\nfunc TestInfoExportedFields(t *testing.T) {\n\t// It is highly unlikely that you should be adjusting the asserts below.\n\t// See the comment at the top of info.go for more information.\n\n\tvar names []string\n\tfor _, field := range reflect.VisibleFields(reflect.TypeOf(MapInfo{})) {\n\t\tif field.IsExported() {\n\t\t\tnames = append(names, field.Name)\n\t\t}\n\t}\n\tqt.Assert(t, qt.ContentEquals(names, []string{\n\t\t\"Type\",\n\t\t\"KeySize\",\n\t\t\"ValueSize\",\n\t\t\"MaxEntries\",\n\t\t\"Flags\",\n\t\t\"Name\",\n\t}))\n\n\tnames = nil\n\tfor _, field := range reflect.VisibleFields(reflect.TypeOf(ProgramInfo{})) {\n\t\tif field.IsExported() {\n\t\t\tnames = append(names, field.Name)\n\t\t}\n\t}\n\tqt.Assert(t, qt.ContentEquals(names, []string{\n\t\t\"Type\",\n\t\t\"Tag\",\n\t\t\"Name\",\n\t}))\n}\n\nfunc TestZero(t *testing.T) {\n\tvar (\n\t\tempty        = \"\"\n\t\tnul   uint32 = 0\n\t\tone   uint32 = 1\n\n\t\tiempty any = \"\"\n\t\tinul   any = uint32(0)\n\t\tione   any = uint32(1)\n\t)\n\n\tqt.Assert(t, qt.IsTrue(zero(empty)))\n\tqt.Assert(t, qt.IsTrue(zero(nul)))\n\tqt.Assert(t, qt.IsFalse(zero(one)))\n\n\tqt.Assert(t, qt.IsTrue(zero(&empty)))\n\tqt.Assert(t, qt.IsTrue(zero(&nul)))\n\tqt.Assert(t, qt.IsFalse(zero(&one)))\n\n\tqt.Assert(t, qt.IsTrue(zero(iempty)))\n\tqt.Assert(t, qt.IsTrue(zero(inul)))\n\tqt.Assert(t, qt.IsFalse(zero(ione)))\n\n\tqt.Assert(t, qt.IsTrue(zero(&iempty)))\n\tqt.Assert(t, qt.IsTrue(zero(&inul)))\n\tqt.Assert(t, qt.IsFalse(zero(&ione)))\n}\n"
  },
  {
    "path": "internal/cmd/genfunctions.awk",
    "content": "#!/usr/bin/gawk -f\n# Generate constants from Linux headers.\n#\n# This script expects include/uapi/bpf.h as input.\n\nBEGIN {\n\tprint \"// Code generated by internal/cmd/genfunctions.awk; DO NOT EDIT.\"\n\tprint \"\"\n\tprint \"package asm\"\n\tprint \"\"\n\tprint \"// Code in this file is derived from Linux, available under the GPL-2.0 WITH Linux-syscall-note.\"\n\tprint \"\"\n\tprint \"import \\\"github.com/cilium/ebpf/internal/platform\\\"\"\n\tprint \"\"\n\tprint \"// Built-in functions (Linux).\"\n\tprint \"const (\"\n}\n\n/FN\\([[:alnum:]_]+, [[:digit:]]+,.*\\)/ {\n\tname = gensub(/.*FN\\(([[:alnum:]_]+), [[:digit:]]+,.*\\).*/, \"\\\\1\", 1)\n\tid = gensub(/.*FN\\([[:alnum:]_]+, ([[:digit:]]+),.*\\).*/, \"\\\\1\", 1)\n\n\tsplit(tolower(name), parts, \"_\")\n\tresult = \"Fn\"\n\tfor (i in parts) {\n\t\tpart = parts[i]\n\t\tresult = result substr(toupper(substr(part,1,1)), 1, 1) substr(part, 2)\n\t}\n\n\tprint \"\\t\" result \" = BuiltinFunc(platform.LinuxTag | \" id \")\"\n}\n\nEND {\n\tprint \")\"\n\tprint \"\"\n}\n"
  },
  {
    "path": "internal/cmd/gensections.awk",
    "content": "#!/usr/bin/gawk -f\n# This script expects tools/lib/bpf/libbpf.c as input.\n\nfunction trim(str, left, right) {\n\tstr = gensub(\"^[\\t ]*\" left, \"\", \"g\", str)\n\treturn gensub(right \"$\", \"\", \"g\", str)\n}\n\nBEGIN {\n\tprint \"// Code generated by internal/cmd/gensections.awk; DO NOT EDIT.\"\n\tprint \"\"\n\tprint \"package ebpf\"\n\tprint \"\"\n\tprint \"// Code in this file is derived from libbpf, available under BSD-2-Clause.\"\n\tprint \"\"\n\tprint \"import \\\"github.com/cilium/ebpf/internal/sys\\\"\"\n\tprint \"\"\n\tprint \"var elfSectionDefs = []libbpfElfSectionDef{\"\n\n\tFS=\",\"\n}\n\n/\\tSEC_DEF/ {\n\tpattern = trim(substr($1, 10))\n\tprog_type = \"sys.BPF_PROG_TYPE_\" trim($2)\n\tattach_type = trim($3)\n\tattach_type = attach_type == \"0\" ? \"0\" : \"sys.\" attach_type\n\tflags = trim($4, \"\", \")\")\n\tflags = gensub(\"SEC_\", \"_SEC_\", \"g\", flags)\n\tprintf \"\\t{%s, %s, %s, %s},\\n\", pattern, prog_type, attach_type, flags;\n}\n\nEND {\n\tprint \"}\"\n\tprint \"\"\n}\n"
  },
  {
    "path": "internal/cmd/genwinfunctions.awk",
    "content": "#!/usr/bin/gawk -f\n# Generate constants from eBPF for Windows headers.\n#\n# This script expects include/ebpf_structs.h as input.\n\nBEGIN {\n\tprint \"// Code generated by internal/cmd/genwinfunctions.awk; DO NOT EDIT.\"\n\tprint \"\"\n\tprint \"package asm\"\n\tprint \"\"\n\tprint \"// Code in this file is derived from eBPF for Windows, available under the MIT License.\"\n\tprint \"\"\n\tprint \"import \\\"github.com/cilium/ebpf/internal/platform\\\"\"\n\tprint \"\"\n\tprint \"// Built-in functions (Windows).\"\n\tprint \"const (\"\n}\n\n/BPF_FUNC_[[:alnum:]_]+ *= *[0-9]+,/ {\n\tname = gensub(/.*BPF_FUNC_([[:alnum:]_]+) *=.*/, \"\\\\1\", 1)\n\tid = gensub(/.*BPF_FUNC_[[:alnum:]_]+ *= *([0-9]+),.*/, \"\\\\1\", 1)\n\n\tsplit(tolower(name), parts, \"_\")\n\tresult = \"WindowsFn\"\n\tfor (i in parts) {\n\t\tpart = parts[i]\n\t\tresult = result substr(toupper(substr(part,1,1)), 1, 1) substr(part, 2)\n\t}\n\n\tprint \"\\t\" result \" = BuiltinFunc(platform.WindowsTag | \" id \")\"\n}\n\nEND {\n\tprint \")\"\n\tprint \"\"\n}\n"
  },
  {
    "path": "internal/deque.go",
    "content": "package internal\n\nimport \"math/bits\"\n\n// Deque implements a double ended queue.\ntype Deque[T any] struct {\n\telems       []T\n\tread, write uint64\n\tmask        uint64\n}\n\n// Reset clears the contents of the deque while retaining the backing buffer.\nfunc (dq *Deque[T]) Reset() {\n\tvar zero T\n\n\tfor i := dq.read; i < dq.write; i++ {\n\t\tdq.elems[i&dq.mask] = zero\n\t}\n\n\tdq.read, dq.write = 0, 0\n}\n\nfunc (dq *Deque[T]) Empty() bool {\n\treturn dq.read == dq.write\n}\n\n// Push adds an element to the end.\nfunc (dq *Deque[T]) Push(e T) {\n\tdq.Grow(1)\n\tdq.elems[dq.write&dq.mask] = e\n\tdq.write++\n}\n\n// Shift returns the first element or the zero value.\nfunc (dq *Deque[T]) Shift() T {\n\tvar zero T\n\n\tif dq.Empty() {\n\t\treturn zero\n\t}\n\n\tindex := dq.read & dq.mask\n\tt := dq.elems[index]\n\tdq.elems[index] = zero\n\tdq.read++\n\treturn t\n}\n\n// Pop returns the last element or the zero value.\nfunc (dq *Deque[T]) Pop() T {\n\tvar zero T\n\n\tif dq.Empty() {\n\t\treturn zero\n\t}\n\n\tdq.write--\n\tindex := dq.write & dq.mask\n\tt := dq.elems[index]\n\tdq.elems[index] = zero\n\treturn t\n}\n\n// Grow the deque's capacity, if necessary, to guarantee space for another n\n// elements.\nfunc (dq *Deque[T]) Grow(n int) {\n\thave := dq.write - dq.read\n\tneed := have + uint64(n)\n\tif need < have {\n\t\tpanic(\"overflow\")\n\t}\n\tif uint64(len(dq.elems)) >= need {\n\t\treturn\n\t}\n\n\t// Round up to the new power of two which is at least 8.\n\t// See https://jameshfisher.com/2018/03/30/round-up-power-2/\n\tcapacity := max(1<<(64-bits.LeadingZeros64(need-1)), 8)\n\n\telems := make([]T, have, capacity)\n\tpivot := dq.read & dq.mask\n\tcopied := copy(elems, dq.elems[pivot:])\n\tcopy(elems[copied:], dq.elems[:pivot])\n\n\tdq.elems = elems[:capacity]\n\tdq.mask = uint64(capacity) - 1\n\tdq.read, dq.write = 0, have\n}\n"
  },
  {
    "path": "internal/deque_test.go",
    "content": "package internal\n\nimport \"testing\"\n\nfunc TestDeque(t *testing.T) {\n\tt.Run(\"pop\", func(t *testing.T) {\n\t\tvar dq Deque[int]\n\t\tdq.Push(1)\n\t\tdq.Push(2)\n\n\t\tif dq.Pop() != 2 {\n\t\t\tt.Error(\"Didn't pop 2 first\")\n\t\t}\n\n\t\tif dq.Pop() != 1 {\n\t\t\tt.Error(\"Didn't pop 1 second\")\n\t\t}\n\n\t\tif dq.Pop() != 0 {\n\t\t\tt.Error(\"Didn't pop zero\")\n\t\t}\n\t})\n\n\tt.Run(\"shift\", func(t *testing.T) {\n\t\tvar td Deque[int]\n\t\ttd.Push(1)\n\t\ttd.Push(2)\n\n\t\tif td.Shift() != 1 {\n\t\t\tt.Error(\"Didn't shift 1 first\")\n\t\t}\n\n\t\tif td.Shift() != 2 {\n\t\t\tt.Error(\"Didn't shift b second\")\n\t\t}\n\n\t\tif td.Shift() != 0 {\n\t\t\tt.Error(\"Didn't shift zero\")\n\t\t}\n\t})\n\n\tt.Run(\"push\", func(t *testing.T) {\n\t\tvar td Deque[int]\n\t\ttd.Push(1)\n\t\ttd.Push(2)\n\t\ttd.Shift()\n\n\t\tfor i := 1; i <= 12; i++ {\n\t\t\ttd.Push(i)\n\t\t}\n\n\t\tif td.Shift() != 2 {\n\t\t\tt.Error(\"Didn't shift 2 first\")\n\t\t}\n\t\tfor i := 1; i <= 12; i++ {\n\t\t\tif v := td.Shift(); v != i {\n\t\t\t\tt.Fatalf(\"Shifted %d at pos %d\", v, i)\n\t\t\t}\n\t\t}\n\t})\n\n\tt.Run(\"grow\", func(t *testing.T) {\n\t\tvar td Deque[int]\n\t\ttd.Push(1)\n\t\ttd.Push(2)\n\t\ttd.Push(3)\n\t\ttd.Shift()\n\n\t\ttd.Grow(7)\n\t\tif len(td.elems) < 9 {\n\t\t\tt.Fatal(\"Expected at least 9 elements, got\", len(td.elems))\n\t\t}\n\n\t\tif cap(td.elems)&(cap(td.elems)-1) != 0 {\n\t\t\tt.Fatalf(\"Capacity %d is not a power of two\", cap(td.elems))\n\t\t}\n\n\t\tif td.Shift() != 2 || td.Shift() != 3 {\n\t\t\tt.Fatal(\"Elements don't match after grow\")\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "internal/efw/enums.go",
    "content": "//go:build windows\n\npackage efw\n\nimport (\n\t\"syscall\"\n\t\"unsafe\"\n\n\t\"golang.org/x/sys/windows\"\n)\n\n/*\nConverts an attach type enum into a GUID.\n\n\tebpf_result_t ebpf_get_ebpf_attach_type(\n\t\tbpf_attach_type_t bpf_attach_type,\n\t\t_Out_ ebpf_attach_type_t* ebpf_attach_type_t *ebpf_attach_type)\n*/\nvar ebpfGetEbpfAttachTypeProc = newProc(\"ebpf_get_ebpf_attach_type\")\n\nfunc EbpfGetEbpfAttachType(attachType uint32) (windows.GUID, error) {\n\taddr, err := ebpfGetEbpfAttachTypeProc.Find()\n\tif err != nil {\n\t\treturn windows.GUID{}, err\n\t}\n\n\tvar attachTypeGUID windows.GUID\n\terr = errorResult(syscall.SyscallN(addr,\n\t\tuintptr(attachType),\n\t\tuintptr(unsafe.Pointer(&attachTypeGUID)),\n\t))\n\treturn attachTypeGUID, err\n}\n\n/*\nRetrieve a program type given a GUID.\n\n\tbpf_prog_type_t ebpf_get_bpf_program_type(_In_ const ebpf_program_type_t* program_type)\n*/\nvar ebpfGetBpfProgramTypeProc = newProc(\"ebpf_get_bpf_program_type\")\n\nfunc EbpfGetBpfProgramType(programType windows.GUID) (uint32, error) {\n\taddr, err := ebpfGetBpfProgramTypeProc.Find()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn uint32Result(syscall.SyscallN(addr, uintptr(unsafe.Pointer(&programType)))), nil\n}\n\n/*\nRetrieve an attach type given a GUID.\n\n\tbpf_attach_type_t ebpf_get_bpf_attach_type(_In_ const ebpf_attach_type_t* ebpf_attach_type)\n*/\nvar ebpfGetBpfAttachTypeProc = newProc(\"ebpf_get_bpf_attach_type\")\n\nfunc EbpfGetBpfAttachType(attachType windows.GUID) (uint32, error) {\n\taddr, err := ebpfGetBpfAttachTypeProc.Find()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn uint32Result(syscall.SyscallN(addr, uintptr(unsafe.Pointer(&attachType)))), nil\n}\n"
  },
  {
    "path": "internal/efw/error_reporting.go",
    "content": "//go:build windows\n\npackage efw\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"syscall\"\n\t\"testing\"\n\n\t\"golang.org/x/sys/windows\"\n)\n\nfunc init() {\n\tif !testing.Testing() {\n\t\treturn\n\t}\n\n\tif isDebuggerPresent() {\n\t\treturn\n\t}\n\n\tif err := configureCRTErrorReporting(); err != nil {\n\t\tfmt.Fprintln(os.Stderr, \"WARNING: Could not configure CRT error reporting, tests may hang:\", err)\n\t}\n}\n\nvar errErrorReportingAlreadyConfigured = errors.New(\"error reporting already configured\")\n\n// Configure built-in error reporting of the C runtime library.\n//\n// The C runtime emits assertion failures into a graphical message box by default.\n// This causes a hang in CI environments. This function configures the CRT to\n// log to stderr instead.\nfunc configureCRTErrorReporting() error {\n\tconst ucrtDebug = \"ucrtbased.dll\"\n\n\t// Constants from crtdbg.h\n\t//\n\t// See https://doxygen.reactos.org/da/d40/crt_2crtdbg_8h_source.html\n\tconst (\n\t\t_CRT_ERROR          = 1\n\t\t_CRT_ASSERT         = 2\n\t\t_CRTDBG_MODE_FILE   = 0x1\n\t\t_CRTDBG_MODE_WNDW   = 0x4\n\t\t_CRTDBG_HFILE_ERROR = -2\n\t\t_CRTDBG_FILE_STDERR = -4\n\t)\n\n\t// Load the efW API to trigger loading the CRT. This may fail, in which case\n\t// we can't figure out which CRT is being used.\n\t// In that case we rely on the error bubbling up via some other path.\n\t_ = module.Load()\n\n\tucrtHandle, err := syscall.UTF16PtrFromString(ucrtDebug)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar handle windows.Handle\n\terr = windows.GetModuleHandleEx(0, ucrtHandle, &handle)\n\tif errors.Is(err, windows.ERROR_MOD_NOT_FOUND) {\n\t\t// Loading the ebpf api did not pull in the debug UCRT, so there is\n\t\t// nothing to configure.\n\t\treturn nil\n\t} else if err != nil {\n\t\treturn err\n\t}\n\tdefer windows.FreeLibrary(handle)\n\n\tsetReportModeAddr, err := windows.GetProcAddress(handle, \"_CrtSetReportMode\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tsetReportMode := func(reportType int, reportMode int) (int, error) {\n\t\t// See https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/crtsetreportmode?view=msvc-170\n\t\tr1, _, err := syscall.SyscallN(setReportModeAddr, uintptr(reportType), uintptr(reportMode))\n\t\tif int(r1) == -1 {\n\t\t\treturn 0, fmt.Errorf(\"set report mode for type %d: %w\", reportType, err)\n\t\t}\n\t\treturn int(r1), nil\n\t}\n\n\tsetReportFileAddr, err := windows.GetProcAddress(handle, \"_CrtSetReportFile\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tsetReportFile := func(reportType int, reportFile int) (int, error) {\n\t\t// See https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/crtsetreportfile?view=msvc-170\n\t\tr1, _, err := syscall.SyscallN(setReportFileAddr, uintptr(reportType), uintptr(reportFile))\n\t\tif int(r1) == _CRTDBG_HFILE_ERROR {\n\t\t\treturn 0, fmt.Errorf(\"set report file for type %d: %w\", reportType, err)\n\t\t}\n\t\treturn int(r1), nil\n\t}\n\n\treportToFile := func(reportType, defaultMode int) error {\n\t\toldMode, err := setReportMode(reportType, _CRTDBG_MODE_FILE)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif oldMode != defaultMode {\n\t\t\t// Attempt to restore old mode if it was different from the expected default.\n\t\t\t_, _ = setReportMode(reportType, oldMode)\n\t\t\treturn errErrorReportingAlreadyConfigured\n\t\t}\n\n\t\toldFile, err := setReportFile(reportType, _CRTDBG_FILE_STDERR)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif oldFile != -1 {\n\t\t\t// Attempt to restore old file if it was different from the expected default.\n\t\t\t_, _ = setReportFile(reportType, oldFile)\n\t\t\treturn errErrorReportingAlreadyConfigured\n\t\t}\n\n\t\treturn nil\n\t}\n\n\t// See https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/crtsetreportmode?view=msvc-170#remarks\n\t// for defaults.\n\tif err := reportToFile(_CRT_ASSERT, _CRTDBG_MODE_WNDW); err != nil {\n\t\treturn err\n\t}\n\n\tif err := reportToFile(_CRT_ERROR, _CRTDBG_MODE_WNDW); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// isDebuggerPresent returns true if the current process is being debugged.\n//\n// See https://learn.microsoft.com/en-us/windows/win32/api/debugapi/nf-debugapi-isdebuggerpresent\nfunc isDebuggerPresent() bool {\n\tkernel32Handle, err := windows.LoadLibrary(\"kernel32.dll\")\n\tif err != nil {\n\t\treturn false\n\t}\n\n\tisDebuggerPresentAddr, err := windows.GetProcAddress(kernel32Handle, \"IsDebuggerPresent\")\n\tif err != nil {\n\t\treturn false\n\t}\n\n\tr1, _, _ := syscall.SyscallN(isDebuggerPresentAddr)\n\treturn r1 != 0\n}\n"
  },
  {
    "path": "internal/efw/error_reporting_test.go",
    "content": "//go:build windows\n\npackage efw\n\nimport (\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestConfigureErrorReporting(t *testing.T) {\n\tqt.Assert(t, qt.ErrorIs(configureCRTErrorReporting(), errErrorReportingAlreadyConfigured))\n}\n\nfunc TestIsDebuggerPresent(t *testing.T) {\n\tqt.Assert(t, qt.IsFalse(isDebuggerPresent()))\n}\n"
  },
  {
    "path": "internal/efw/fd.go",
    "content": "//go:build windows\n\npackage efw\n\nimport (\n\t\"syscall\"\n\t\"unsafe\"\n)\n\n// ebpf_result_t ebpf_close_fd(fd_t fd)\nvar ebpfCloseFdProc = newProc(\"ebpf_close_fd\")\n\nfunc EbpfCloseFd(fd int) error {\n\taddr, err := ebpfCloseFdProc.Find()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn errorResult(syscall.SyscallN(addr, uintptr(fd)))\n}\n\n// ebpf_result_t ebpf_duplicate_fd(fd_t fd, _Out_ fd_t* dup)\nvar ebpfDuplicateFdProc = newProc(\"ebpf_duplicate_fd\")\n\nfunc EbpfDuplicateFd(fd int) (int, error) {\n\taddr, err := ebpfDuplicateFdProc.Find()\n\tif err != nil {\n\t\treturn -1, err\n\t}\n\n\tvar dup FD\n\terr = errorResult(syscall.SyscallN(addr, uintptr(fd), uintptr(unsafe.Pointer(&dup))))\n\treturn int(dup), err\n}\n"
  },
  {
    "path": "internal/efw/map.go",
    "content": "//go:build windows\n\npackage efw\n\nimport (\n\t\"runtime\"\n\t\"syscall\"\n\t\"unsafe\"\n\n\t\"golang.org/x/sys/windows\"\n)\n\n/*\nebpf_ring_buffer_map_map_buffer(\n\n\tfd_t map_fd,\n\t_Outptr_result_maybenull_ void** consumer,\n\t_Outptr_result_maybenull_ const void** producer,\n\t_Outptr_result_buffer_maybenull_(*data_size) const uint8_t** data,\n\t_Out_ size_t* data_size) EBPF_NO_EXCEPT;\n*/\nvar ebpfRingBufferMapMapBufferProc = newProc(\"ebpf_ring_buffer_map_map_buffer\")\n\nfunc EbpfRingBufferMapMapBuffer(mapFd int) (consumer, producer, data *uint8, dataLen Size, _ error) {\n\taddr, err := ebpfRingBufferMapMapBufferProc.Find()\n\tif err != nil {\n\t\treturn nil, nil, nil, 0, err\n\t}\n\n\terr = errorResult(syscall.SyscallN(addr,\n\t\tuintptr(mapFd),\n\t\tuintptr(unsafe.Pointer(&consumer)),\n\t\tuintptr(unsafe.Pointer(&producer)),\n\t\tuintptr(unsafe.Pointer(&data)),\n\t\tuintptr(unsafe.Pointer(&dataLen)),\n\t))\n\tif err != nil {\n\t\treturn nil, nil, nil, 0, err\n\t}\n\n\treturn consumer, producer, data, dataLen, nil\n}\n\n/*\nebpf_ring_buffer_map_unmap_buffer(\n\n\tfd_t map_fd, _In_ void* consumer, _In_ const void* producer, _In_ const void* data) EBPF_NO_EXCEPT;\n*/\nvar ebpfRingBufferMapUnmapBufferProc = newProc(\"ebpf_ring_buffer_map_unmap_buffer\")\n\nfunc EbpfRingBufferMapUnmapBuffer(mapFd int, consumer, producer, data *uint8) error {\n\taddr, err := ebpfRingBufferMapUnmapBufferProc.Find()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn errorResult(syscall.SyscallN(addr,\n\t\tuintptr(mapFd),\n\t\tuintptr(unsafe.Pointer(consumer)),\n\t\tuintptr(unsafe.Pointer(producer)),\n\t\tuintptr(unsafe.Pointer(data)),\n\t))\n}\n\n/*\nebpf_result_t ebpf_map_set_wait_handle(\n\n\tfd_t map_fd,\n\tuint64_t index,\n\tebpf_handle_t handle)\n*/\nvar ebpfMapSetWaitHandleProc = newProc(\"ebpf_map_set_wait_handle\")\n\nfunc EbpfMapSetWaitHandle(mapFd int, index uint64, handle windows.Handle) error {\n\taddr, err := ebpfMapSetWaitHandleProc.Find()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn errorResult(syscall.SyscallN(addr,\n\t\tuintptr(mapFd),\n\t\tuintptr(index),\n\t\tuintptr(handle),\n\t))\n}\n\n/*\nebpf_result_t ebpf_ring_buffer_map_write(\n\n\tfd_t ring_buffer_map_fd,\n\tconst void* data,\n\tsize_t data_length)\n*/\nvar ebpfRingBufferMapWriteProc = newProc(\"ebpf_ring_buffer_map_write\")\n\nfunc EbpfRingBufferMapWrite(ringBufferMapFd int, data []byte) error {\n\taddr, err := ebpfRingBufferMapWriteProc.Find()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = errorResult(syscall.SyscallN(addr,\n\t\tuintptr(ringBufferMapFd),\n\t\tuintptr(unsafe.Pointer(&data[0])),\n\t\tuintptr(len(data)),\n\t))\n\truntime.KeepAlive(data)\n\treturn err\n}\n"
  },
  {
    "path": "internal/efw/module.go",
    "content": "//go:build windows\n\n// Package efw contains support code for eBPF for Windows.\npackage efw\n\nimport (\n\t\"golang.org/x/sys/windows\"\n)\n\n// module is the global handle for the eBPF for Windows user-space API.\nvar module = windows.NewLazyDLL(\"ebpfapi.dll\")\n\n// FD is the equivalent of fd_t.\n//\n// See https://github.com/microsoft/ebpf-for-windows/blob/54632eb360c560ebef2f173be1a4a4625d540744/include/ebpf_api.h#L24\ntype FD int32\n\n// Size is the equivalent of size_t.\n//\n// This is correct on amd64 and arm64 according to tests on godbolt.org.\ntype Size uint64\n\n// Int is the equivalent of int on MSVC (am64, arm64) and MinGW (gcc, clang).\ntype Int int32\n\n// ObjectType is the equivalent of ebpf_object_type_t.\n//\n// See https://github.com/microsoft/ebpf-for-windows/blob/44f5de09ec0f3f7ad176c00a290c1cb7106cdd5e/include/ebpf_core_structs.h#L41\ntype ObjectType uint32\n\nconst (\n\tEBPF_OBJECT_UNKNOWN ObjectType = iota\n\tEBPF_OBJECT_MAP\n\tEBPF_OBJECT_LINK\n\tEBPF_OBJECT_PROGRAM\n)\n"
  },
  {
    "path": "internal/efw/native.go",
    "content": "//go:build windows\n\npackage efw\n\nimport (\n\t\"syscall\"\n\t\"unsafe\"\n\n\t\"golang.org/x/sys/windows\"\n)\n\n/*\nebpf_result_t ebpf_object_load_native_by_fds(\n\n\t_In_z_ const char* file_name,\n\t_Inout_ size_t* count_of_maps,\n\t_Out_writes_opt_(count_of_maps) fd_t* map_fds,\n\t_Inout_ size_t* count_of_programs,\n\t_Out_writes_opt_(count_of_programs) fd_t* program_fds)\n*/\nvar ebpfObjectLoadNativeByFdsProc = newProc(\"ebpf_object_load_native_by_fds\")\n\nfunc EbpfObjectLoadNativeFds(fileName string, mapFds []FD, programFds []FD) (int, int, error) {\n\taddr, err := ebpfObjectLoadNativeByFdsProc.Find()\n\tif err != nil {\n\t\treturn 0, 0, err\n\t}\n\n\tfileBytes, err := windows.ByteSliceFromString(fileName)\n\tif err != nil {\n\t\treturn 0, 0, err\n\t}\n\n\tcountOfMaps := Size(len(mapFds))\n\tcountOfPrograms := Size(len(programFds))\n\terr = errorResult(syscall.SyscallN(addr,\n\t\tuintptr(unsafe.Pointer(&fileBytes[0])),\n\t\tuintptr(unsafe.Pointer(&countOfMaps)),\n\t\tuintptr(unsafe.Pointer(&mapFds[0])),\n\t\tuintptr(unsafe.Pointer(&countOfPrograms)),\n\t\tuintptr(unsafe.Pointer(&programFds[0])),\n\t))\n\treturn int(countOfMaps), int(countOfPrograms), err\n}\n"
  },
  {
    "path": "internal/efw/object.go",
    "content": "//go:build windows\n\npackage efw\n\nimport (\n\t\"syscall\"\n\t\"unsafe\"\n\n\t\"golang.org/x/sys/windows\"\n)\n\n// https://github.com/microsoft/ebpf-for-windows/blob/9d9003c39c3fd75be5225ac0fce30077d6bf0604/include/ebpf_core_structs.h#L15\nconst _EBPF_MAX_PIN_PATH_LENGTH = 256\n\n/*\nRetrieve object info and type from a fd.\n\n\tebpf_result_t ebpf_object_get_info_by_fd(\n\t\tfd_t bpf_fd,\n\t\t_Inout_updates_bytes_to_opt_(*info_size, *info_size) void* info,\n\t\t_Inout_opt_ uint32_t* info_size,\n\t\t_Out_opt_ ebpf_object_type_t* type)\n*/\nvar ebpfObjectGetInfoByFdProc = newProc(\"ebpf_object_get_info_by_fd\")\n\nfunc EbpfObjectGetInfoByFd(fd int, info unsafe.Pointer, info_size *uint32) (ObjectType, error) {\n\taddr, err := ebpfObjectGetInfoByFdProc.Find()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tvar objectType ObjectType\n\terr = errorResult(syscall.SyscallN(addr,\n\t\tuintptr(fd),\n\t\tuintptr(info),\n\t\tuintptr(unsafe.Pointer(info_size)),\n\t\tuintptr(unsafe.Pointer(&objectType)),\n\t))\n\treturn objectType, err\n}\n\n// ebpf_result_t ebpf_object_unpin(_In_z_ const char* path)\nvar ebpfObjectUnpinProc = newProc(\"ebpf_object_unpin\")\n\nfunc EbpfObjectUnpin(path string) error {\n\taddr, err := ebpfObjectUnpinProc.Find()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tpathBytes, err := windows.ByteSliceFromString(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn errorResult(syscall.SyscallN(addr, uintptr(unsafe.Pointer(&pathBytes[0]))))\n}\n\n/*\nRetrieve the next pinned object path.\n\n\tebpf_result_t ebpf_get_next_pinned_object_path(\n\t\t_In_opt_z_ const char* start_path,\n\t\t_Out_writes_z_(next_path_len) char* next_path,\n\t\tsize_t next_path_len,\n\t\t_Inout_opt_ ebpf_object_type_t* type)\n*/\nvar ebpfGetNextPinnedObjectPath = newProc(\"ebpf_get_next_pinned_object_path\")\n\nfunc EbpfGetNextPinnedObjectPath(startPath string, objectType ObjectType) (string, ObjectType, error) {\n\taddr, err := ebpfGetNextPinnedObjectPath.Find()\n\tif err != nil {\n\t\treturn \"\", 0, err\n\t}\n\n\tptr, err := windows.BytePtrFromString(startPath)\n\tif err != nil {\n\t\treturn \"\", 0, err\n\t}\n\n\ttmp := make([]byte, _EBPF_MAX_PIN_PATH_LENGTH)\n\terr = errorResult(syscall.SyscallN(addr,\n\t\tuintptr(unsafe.Pointer(ptr)),\n\t\tuintptr(unsafe.Pointer(&tmp[0])),\n\t\tuintptr(len(tmp)),\n\t\tuintptr(unsafe.Pointer(&objectType)),\n\t))\n\treturn windows.ByteSliceToString(tmp), objectType, err\n}\n\n/*\nCanonicalize a path using filesystem canonicalization rules.\n\n\t_Must_inspect_result_ ebpf_result_t\n\t\tebpf_canonicalize_pin_path(_Out_writes_(output_size) char* output, size_t output_size, _In_z_ const char* input)\n*/\nvar ebpfCanonicalizePinPath = newProc(\"ebpf_canonicalize_pin_path\")\n\nfunc EbpfCanonicalizePinPath(input string) (string, error) {\n\taddr, err := ebpfCanonicalizePinPath.Find()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tinputBytes, err := windows.ByteSliceFromString(input)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\toutput := make([]byte, _EBPF_MAX_PIN_PATH_LENGTH)\n\terr = errorResult(syscall.SyscallN(addr,\n\t\tuintptr(unsafe.Pointer(&output[0])),\n\t\tuintptr(len(output)),\n\t\tuintptr(unsafe.Pointer(&inputBytes[0])),\n\t))\n\treturn windows.ByteSliceToString(output), err\n}\n"
  },
  {
    "path": "internal/efw/proc.go",
    "content": "//go:build windows\n\npackage efw\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"syscall\"\n\n\t\"golang.org/x/sys/windows\"\n)\n\n/*\nThe BPF syscall wrapper which is ABI compatible with Linux.\n\n\tint bpf(int cmd, union bpf_attr* attr, unsigned int size)\n*/\nvar BPF = newProc(\"bpf\")\n\ntype proc struct {\n\tproc *windows.LazyProc\n}\n\nfunc newProc(name string) proc {\n\treturn proc{module.NewProc(name)}\n}\n\nfunc (p proc) Find() (uintptr, error) {\n\tif err := p.proc.Find(); err != nil {\n\t\tif errors.Is(err, windows.ERROR_MOD_NOT_FOUND) {\n\t\t\treturn 0, fmt.Errorf(\"load %s: not found\", module.Name)\n\t\t}\n\t\treturn 0, err\n\t}\n\treturn p.proc.Addr(), nil\n}\n\n// uint32Result wraps a function which returns a uint32_t.\nfunc uint32Result(r1, _ uintptr, _ syscall.Errno) uint32 {\n\treturn uint32(r1)\n}\n\n// errorResult wraps a function which returns ebpf_result_t.\nfunc errorResult(r1, _ uintptr, errNo syscall.Errno) error {\n\terr := resultToError(Result(r1))\n\tif err != nil && errNo != 0 {\n\t\treturn fmt.Errorf(\"%w (errno: %v)\", err, errNo)\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "internal/efw/proc_test.go",
    "content": "//go:build windows\n\npackage efw\n\nimport (\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestNewProc(t *testing.T) {\n\t_, err := newProc(\"a_function_which_doesnt_exist\").Find()\n\tqt.Assert(t, qt.ErrorMatches(err, \".* a_function_which_doesnt_exist .*\"))\n}\n\nfunc TestCall(t *testing.T) {\n\tvar err error\n\tallocs := testing.AllocsPerRun(10, func() {\n\t\t_, err = EbpfGetEbpfAttachType(2)\n\t})\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.Equals(allocs, 0))\n}\n"
  },
  {
    "path": "internal/efw/program.go",
    "content": "//go:build windows\n\npackage efw\n\nimport (\n\t\"syscall\"\n\t\"unsafe\"\n\n\t\"golang.org/x/sys/windows\"\n)\n\n/*\nAttach a program.\n\n\tebpf_result_t ebpf_program_attach_by_fds(\n\t\tfd_t program_fd,\n\t\t_In_opt_ const ebpf_attach_type_t* attach_type,\n\t\t_In_reads_bytes_opt_(attach_parameters_size) void* attach_parameters,\n\t\tsize_t attach_parameters_size,\n\t\t_Out_ fd_t* link)\n*/\nvar ebpfProgramAttachByFdsProc = newProc(\"ebpf_program_attach_by_fds\")\n\nfunc EbpfProgramAttachFds(fd int, attachType windows.GUID, params unsafe.Pointer, params_size uintptr) (int, error) {\n\taddr, err := ebpfProgramAttachByFdsProc.Find()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tvar link FD\n\terr = errorResult(syscall.SyscallN(addr,\n\t\tuintptr(fd),\n\t\tuintptr(unsafe.Pointer(&attachType)),\n\t\tuintptr(params),\n\t\tparams_size,\n\t\tuintptr(unsafe.Pointer(&link)),\n\t))\n\treturn int(link), err\n}\n"
  },
  {
    "path": "internal/efw/result.go",
    "content": "//go:build windows\n\npackage efw\n\n// See https://github.com/microsoft/ebpf-for-windows/blob/main/include/ebpf_result.h\ntype Result int32\n\n//go:generate go tool stringer -tags windows -output result_string_windows.go -type=Result\n\nconst (\n\tEBPF_SUCCESS Result = iota\n\tEBPF_VERIFICATION_FAILED\n\tEBPF_JIT_COMPILATION_FAILED\n\tEBPF_PROGRAM_LOAD_FAILED\n\tEBPF_INVALID_FD\n\tEBPF_INVALID_OBJECT\n\tEBPF_INVALID_ARGUMENT\n\tEBPF_OBJECT_NOT_FOUND\n\tEBPF_OBJECT_ALREADY_EXISTS\n\tEBPF_FILE_NOT_FOUND\n\tEBPF_ALREADY_PINNED\n\tEBPF_NOT_PINNED\n\tEBPF_NO_MEMORY\n\tEBPF_PROGRAM_TOO_LARGE\n\tEBPF_RPC_EXCEPTION\n\tEBPF_ALREADY_INITIALIZED\n\tEBPF_ELF_PARSING_FAILED\n\tEBPF_FAILED\n\tEBPF_OPERATION_NOT_SUPPORTED\n\tEBPF_KEY_NOT_FOUND\n\tEBPF_ACCESS_DENIED\n\tEBPF_BLOCKED_BY_POLICY\n\tEBPF_ARITHMETIC_OVERFLOW\n\tEBPF_EXTENSION_FAILED_TO_LOAD\n\tEBPF_INSUFFICIENT_BUFFER\n\tEBPF_NO_MORE_KEYS\n\tEBPF_KEY_ALREADY_EXISTS\n\tEBPF_NO_MORE_TAIL_CALLS\n\tEBPF_PENDING\n\tEBPF_OUT_OF_SPACE\n\tEBPF_CANCELED\n\tEBPF_INVALID_POINTER\n\tEBPF_TIMEOUT\n\tEBPF_STALE_ID\n\tEBPF_INVALID_STATE\n)\n\nfunc (r Result) Error() string {\n\treturn r.String()\n}\n\nfunc resultToError(res Result) error {\n\tif res == EBPF_SUCCESS {\n\t\treturn nil\n\t}\n\treturn res\n}\n"
  },
  {
    "path": "internal/efw/result_string_windows.go",
    "content": "// Code generated by \"stringer -tags windows -output result_string_windows.go -type=Result\"; DO NOT EDIT.\n\npackage efw\n\nimport \"strconv\"\n\nfunc _() {\n\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n\t// Re-run the stringer command to generate them again.\n\tvar x [1]struct{}\n\t_ = x[EBPF_SUCCESS-0]\n\t_ = x[EBPF_VERIFICATION_FAILED-1]\n\t_ = x[EBPF_JIT_COMPILATION_FAILED-2]\n\t_ = x[EBPF_PROGRAM_LOAD_FAILED-3]\n\t_ = x[EBPF_INVALID_FD-4]\n\t_ = x[EBPF_INVALID_OBJECT-5]\n\t_ = x[EBPF_INVALID_ARGUMENT-6]\n\t_ = x[EBPF_OBJECT_NOT_FOUND-7]\n\t_ = x[EBPF_OBJECT_ALREADY_EXISTS-8]\n\t_ = x[EBPF_FILE_NOT_FOUND-9]\n\t_ = x[EBPF_ALREADY_PINNED-10]\n\t_ = x[EBPF_NOT_PINNED-11]\n\t_ = x[EBPF_NO_MEMORY-12]\n\t_ = x[EBPF_PROGRAM_TOO_LARGE-13]\n\t_ = x[EBPF_RPC_EXCEPTION-14]\n\t_ = x[EBPF_ALREADY_INITIALIZED-15]\n\t_ = x[EBPF_ELF_PARSING_FAILED-16]\n\t_ = x[EBPF_FAILED-17]\n\t_ = x[EBPF_OPERATION_NOT_SUPPORTED-18]\n\t_ = x[EBPF_KEY_NOT_FOUND-19]\n\t_ = x[EBPF_ACCESS_DENIED-20]\n\t_ = x[EBPF_BLOCKED_BY_POLICY-21]\n\t_ = x[EBPF_ARITHMETIC_OVERFLOW-22]\n\t_ = x[EBPF_EXTENSION_FAILED_TO_LOAD-23]\n\t_ = x[EBPF_INSUFFICIENT_BUFFER-24]\n\t_ = x[EBPF_NO_MORE_KEYS-25]\n\t_ = x[EBPF_KEY_ALREADY_EXISTS-26]\n\t_ = x[EBPF_NO_MORE_TAIL_CALLS-27]\n\t_ = x[EBPF_PENDING-28]\n\t_ = x[EBPF_OUT_OF_SPACE-29]\n\t_ = x[EBPF_CANCELED-30]\n\t_ = x[EBPF_INVALID_POINTER-31]\n\t_ = x[EBPF_TIMEOUT-32]\n\t_ = x[EBPF_STALE_ID-33]\n\t_ = x[EBPF_INVALID_STATE-34]\n}\n\nconst _Result_name = \"EBPF_SUCCESSEBPF_VERIFICATION_FAILEDEBPF_JIT_COMPILATION_FAILEDEBPF_PROGRAM_LOAD_FAILEDEBPF_INVALID_FDEBPF_INVALID_OBJECTEBPF_INVALID_ARGUMENTEBPF_OBJECT_NOT_FOUNDEBPF_OBJECT_ALREADY_EXISTSEBPF_FILE_NOT_FOUNDEBPF_ALREADY_PINNEDEBPF_NOT_PINNEDEBPF_NO_MEMORYEBPF_PROGRAM_TOO_LARGEEBPF_RPC_EXCEPTIONEBPF_ALREADY_INITIALIZEDEBPF_ELF_PARSING_FAILEDEBPF_FAILEDEBPF_OPERATION_NOT_SUPPORTEDEBPF_KEY_NOT_FOUNDEBPF_ACCESS_DENIEDEBPF_BLOCKED_BY_POLICYEBPF_ARITHMETIC_OVERFLOWEBPF_EXTENSION_FAILED_TO_LOADEBPF_INSUFFICIENT_BUFFEREBPF_NO_MORE_KEYSEBPF_KEY_ALREADY_EXISTSEBPF_NO_MORE_TAIL_CALLSEBPF_PENDINGEBPF_OUT_OF_SPACEEBPF_CANCELEDEBPF_INVALID_POINTEREBPF_TIMEOUTEBPF_STALE_IDEBPF_INVALID_STATE\"\n\nvar _Result_index = [...]uint16{0, 12, 36, 63, 87, 102, 121, 142, 163, 189, 208, 227, 242, 256, 278, 296, 320, 343, 354, 382, 400, 418, 440, 464, 493, 517, 534, 557, 580, 592, 609, 622, 642, 654, 667, 685}\n\nfunc (i Result) String() string {\n\tif i < 0 || i >= Result(len(_Result_index)-1) {\n\t\treturn \"Result(\" + strconv.FormatInt(int64(i), 10) + \")\"\n\t}\n\treturn _Result_name[_Result_index[i]:_Result_index[i+1]]\n}\n"
  },
  {
    "path": "internal/efw/result_test.go",
    "content": "//go:build windows\n\npackage efw\n\nimport (\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestResultToError(t *testing.T) {\n\tqt.Assert(t, qt.IsNil(resultToError(EBPF_SUCCESS)))\n\tqt.Assert(t, qt.IsNotNil(resultToError(EBPF_ACCESS_DENIED)))\n\n\t// Ensure that common results do not allocate.\n\tfor _, result := range []Result{\n\t\tEBPF_SUCCESS,\n\t\tEBPF_NO_MORE_KEYS,\n\t\tEBPF_KEY_NOT_FOUND,\n\t} {\n\t\tt.Run(result.String(), func(t *testing.T) {\n\t\t\tallocs := testing.AllocsPerRun(1, func() {\n\t\t\t\t_ = resultToError(result)\n\t\t\t})\n\t\t\tqt.Assert(t, qt.Equals(allocs, 0.0))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/efw/structs.go",
    "content": "//go:build windows\n\npackage efw\n\nimport \"golang.org/x/sys/windows\"\n\n// https://github.com/microsoft/ebpf-for-windows/blob/95267a53b26c68a94145d1731e2a4c8b546034c3/include/ebpf_structs.h#L366\nconst _BPF_OBJ_NAME_LEN = 64\n\n// See https://github.com/microsoft/ebpf-for-windows/blob/95267a53b26c68a94145d1731e2a4c8b546034c3/include/ebpf_structs.h#L372-L386\ntype BpfMapInfo struct {\n\t_    uint32                  ///< Map ID.\n\t_    uint32                  ///< Type of map.\n\t_    uint32                  ///< Size in bytes of a map key.\n\t_    uint32                  ///< Size in bytes of a map value.\n\t_    uint32                  ///< Maximum number of entries allowed in the map.\n\tName [_BPF_OBJ_NAME_LEN]byte ///< Null-terminated map name.\n\t_    uint32                  ///< Map flags.\n\n\t_ uint32 ///< ID of inner map template.\n\t_ uint32 ///< Number of pinned paths.\n}\n\n// See https://github.com/microsoft/ebpf-for-windows/blob/95267a53b26c68a94145d1731e2a4c8b546034c3/include/ebpf_structs.h#L396-L410\ntype BpfProgInfo struct {\n\t_    uint32                  ///< Program ID.\n\t_    uint32                  ///< Program type, if a cross-platform type.\n\t_    uint32                  ///< Number of maps associated with this program.\n\t_    uintptr                 ///< Pointer to caller-allocated array to fill map IDs into.\n\tName [_BPF_OBJ_NAME_LEN]byte ///< Null-terminated map name.\n\n\t_ windows.GUID ///< Program type UUID.\n\t_ windows.GUID ///< Attach type UUID.\n\t_ uint32       ///< Number of pinned paths.\n\t_ uint32       ///< Number of attached links.\n}\n"
  },
  {
    "path": "internal/elf.go",
    "content": "package internal\n\nimport (\n\t\"debug/elf\"\n\t\"fmt\"\n\t\"io\"\n)\n\ntype SafeELFFile struct {\n\t*elf.File\n}\n\n// NewSafeELFFile reads an ELF safely.\n//\n// Any panic during parsing is turned into an error. This is necessary since\n// there are a bunch of unfixed bugs in debug/elf.\n//\n// https://github.com/golang/go/issues?q=is%3Aissue+is%3Aopen+debug%2Felf+in%3Atitle\nfunc NewSafeELFFile(r io.ReaderAt) (safe *SafeELFFile, err error) {\n\tdefer func() {\n\t\tr := recover()\n\t\tif r == nil {\n\t\t\treturn\n\t\t}\n\n\t\tsafe = nil\n\t\terr = fmt.Errorf(\"reading ELF file panicked: %s\", r)\n\t}()\n\n\tfile, err := elf.NewFile(r)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &SafeELFFile{file}, nil\n}\n\n// OpenSafeELFFile reads an ELF from a file.\n//\n// It works like NewSafeELFFile, with the exception that safe.Close will\n// close the underlying file.\nfunc OpenSafeELFFile(path string) (safe *SafeELFFile, err error) {\n\tdefer func() {\n\t\tr := recover()\n\t\tif r == nil {\n\t\t\treturn\n\t\t}\n\n\t\tsafe = nil\n\t\terr = fmt.Errorf(\"reading ELF file panicked: %s\", r)\n\t}()\n\n\tfile, err := elf.Open(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &SafeELFFile{file}, nil\n}\n\n// Symbols is the safe version of elf.File.Symbols.\nfunc (se *SafeELFFile) Symbols() (syms []elf.Symbol, err error) {\n\tdefer func() {\n\t\tr := recover()\n\t\tif r == nil {\n\t\t\treturn\n\t\t}\n\n\t\tsyms = nil\n\t\terr = fmt.Errorf(\"reading ELF symbols panicked: %s\", r)\n\t}()\n\n\tsyms, err = se.File.Symbols()\n\treturn\n}\n\n// DynamicSymbols is the safe version of elf.File.DynamicSymbols.\nfunc (se *SafeELFFile) DynamicSymbols() (syms []elf.Symbol, err error) {\n\tdefer func() {\n\t\tr := recover()\n\t\tif r == nil {\n\t\t\treturn\n\t\t}\n\n\t\tsyms = nil\n\t\terr = fmt.Errorf(\"reading ELF dynamic symbols panicked: %s\", r)\n\t}()\n\n\tsyms, err = se.File.DynamicSymbols()\n\treturn\n}\n\n// SectionsByType returns all sections in the file with the specified section type.\nfunc (se *SafeELFFile) SectionsByType(typ elf.SectionType) []*elf.Section {\n\tsections := make([]*elf.Section, 0, 1)\n\tfor _, section := range se.Sections {\n\t\tif section.Type == typ {\n\t\t\tsections = append(sections, section)\n\t\t}\n\t}\n\treturn sections\n}\n"
  },
  {
    "path": "internal/endian_be.go",
    "content": "//go:build armbe || arm64be || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64\n\npackage internal\n\nimport \"encoding/binary\"\n\n// NativeEndian is set to either binary.BigEndian or binary.LittleEndian,\n// depending on the host's endianness.\nvar NativeEndian = binary.BigEndian\n"
  },
  {
    "path": "internal/endian_le.go",
    "content": "//go:build 386 || amd64 || amd64p32 || arm || arm64 || loong64 || mipsle || mips64le || mips64p32le || ppc64le || riscv64 || wasm\n\npackage internal\n\nimport \"encoding/binary\"\n\n// NativeEndian is set to either binary.BigEndian or binary.LittleEndian,\n// depending on the host's endianness.\nvar NativeEndian = binary.LittleEndian\n"
  },
  {
    "path": "internal/epoll/poller.go",
    "content": "//go:build !windows\n\npackage epoll\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"os\"\n\t\"runtime\"\n\t\"slices\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\nvar (\n\tErrFlushed                   = errors.New(\"data was flushed\")\n\terrEpollWaitDeadlineExceeded = fmt.Errorf(\"epoll wait: %w\", os.ErrDeadlineExceeded)\n\terrEpollWaitClosed           = fmt.Errorf(\"epoll wait: %w\", os.ErrClosed)\n)\n\n// Poller waits for readiness notifications from multiple file descriptors.\n//\n// The wait can be interrupted by calling Close.\ntype Poller struct {\n\t// mutexes protect the fields declared below them. If you need to\n\t// acquire both at once you must lock epollMu before eventMu.\n\tepollMu sync.Mutex\n\tepollFd int\n\n\teventMu    sync.Mutex\n\tcloseEvent *eventFd\n\tflushEvent *eventFd\n\tcleanup    runtime.Cleanup\n}\n\nfunc New() (_ *Poller, err error) {\n\tcloseFDOnError := func(fd int) {\n\t\tif err != nil {\n\t\t\tunix.Close(fd)\n\t\t}\n\t}\n\tcloseEventFDOnError := func(e *eventFd) {\n\t\tif err != nil {\n\t\t\te.close()\n\t\t}\n\t}\n\n\tepollFd, err := unix.EpollCreate1(unix.EPOLL_CLOEXEC)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"create epoll fd: %w\", err)\n\t}\n\tdefer closeFDOnError(epollFd)\n\n\tp := &Poller{epollFd: epollFd}\n\tp.closeEvent, err = newEventFd()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer closeEventFDOnError(p.closeEvent)\n\n\tp.flushEvent, err = newEventFd()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer closeEventFDOnError(p.flushEvent)\n\n\tif err := p.Add(p.closeEvent.raw, 0); err != nil {\n\t\treturn nil, fmt.Errorf(\"add close eventfd: %w\", err)\n\t}\n\n\tif err := p.Add(p.flushEvent.raw, 0); err != nil {\n\t\treturn nil, fmt.Errorf(\"add flush eventfd: %w\", err)\n\t}\n\n\tp.cleanup = runtime.AddCleanup(p, func(raw int) {\n\t\t_ = unix.Close(raw)\n\t}, p.epollFd)\n\n\treturn p, nil\n}\n\n// Close the poller.\n//\n// Interrupts any calls to Wait. Multiple calls to Close are valid, but subsequent\n// calls will return os.ErrClosed.\nfunc (p *Poller) Close() error {\n\tp.cleanup.Stop()\n\n\t// Interrupt Wait() via the closeEvent fd if it's currently blocked.\n\tif err := p.wakeWaitForClose(); err != nil {\n\t\treturn err\n\t}\n\n\t// Acquire the lock. This ensures that Wait isn't running.\n\tp.epollMu.Lock()\n\tdefer p.epollMu.Unlock()\n\n\t// Prevent other calls to Close().\n\tp.eventMu.Lock()\n\tdefer p.eventMu.Unlock()\n\n\tif p.epollFd != -1 {\n\t\tunix.Close(p.epollFd)\n\t\tp.epollFd = -1\n\t}\n\n\tif p.closeEvent != nil {\n\t\tp.closeEvent.close()\n\t\tp.closeEvent = nil\n\t}\n\n\tif p.flushEvent != nil {\n\t\tp.flushEvent.close()\n\t\tp.flushEvent = nil\n\t}\n\n\treturn nil\n}\n\n// Add an fd to the poller.\n//\n// id is returned by Wait in the unix.EpollEvent.Pad field any may be zero. It\n// must not exceed math.MaxInt32.\n//\n// Add is blocked by Wait.\nfunc (p *Poller) Add(fd int, id int) error {\n\tif int64(id) > math.MaxInt32 {\n\t\treturn fmt.Errorf(\"unsupported id: %d\", id)\n\t}\n\n\tp.epollMu.Lock()\n\tdefer p.epollMu.Unlock()\n\n\tif p.epollFd == -1 {\n\t\treturn fmt.Errorf(\"epoll add: %w\", os.ErrClosed)\n\t}\n\n\t// The representation of EpollEvent isn't entirely accurate.\n\t// Pad is fully usable, not just padding. Hence we stuff the\n\t// id in there, which allows us to identify the event later (e.g.,\n\t// in case of perf events, which CPU sent it).\n\tevent := unix.EpollEvent{\n\t\tEvents: unix.EPOLLIN,\n\t\tFd:     int32(fd),\n\t\tPad:    int32(id),\n\t}\n\n\tif err := unix.EpollCtl(p.epollFd, unix.EPOLL_CTL_ADD, fd, &event); err != nil {\n\t\treturn fmt.Errorf(\"add fd to epoll: %v\", err)\n\t}\n\n\treturn nil\n}\n\n// Wait for events.\n//\n// Returns the number of pending events and any errors.\n//\n//   - [os.ErrClosed] if interrupted by [Close].\n//   - [ErrFlushed] if interrupted by [Flush].\n//   - [os.ErrDeadlineExceeded] if deadline is reached.\nfunc (p *Poller) Wait(events []unix.EpollEvent, deadline time.Time) (int, error) {\n\tp.epollMu.Lock()\n\tdefer p.epollMu.Unlock()\n\n\tif p.epollFd == -1 {\n\t\treturn 0, errEpollWaitClosed\n\t}\n\n\tfor {\n\t\ttimeout := int(-1)\n\t\tif !deadline.IsZero() {\n\t\t\t// Ensure deadline is not in the past and not too far into the future.\n\t\t\ttimeout = int(internal.Between(time.Until(deadline).Milliseconds(), 0, math.MaxInt))\n\t\t}\n\n\t\tn, err := unix.EpollWait(p.epollFd, events, timeout)\n\t\tif temp, ok := err.(temporaryError); ok && temp.Temporary() {\n\t\t\t// Retry the syscall if we were interrupted, see https://github.com/golang/go/issues/20400\n\t\t\tcontinue\n\t\t}\n\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\n\t\tif n == 0 {\n\t\t\treturn 0, errEpollWaitDeadlineExceeded\n\t\t}\n\n\t\tfor i := 0; i < n; {\n\t\t\tevent := events[i]\n\t\t\tif int(event.Fd) == p.closeEvent.raw {\n\t\t\t\t// Since we don't read p.closeEvent the event is never cleared and\n\t\t\t\t// we'll keep getting this wakeup until Close() acquires the\n\t\t\t\t// lock and sets p.epollFd = -1.\n\t\t\t\treturn 0, errEpollWaitClosed\n\t\t\t}\n\t\t\tif int(event.Fd) == p.flushEvent.raw {\n\t\t\t\t// read event to prevent it from continuing to wake\n\t\t\t\tp.flushEvent.read()\n\t\t\t\terr = ErrFlushed\n\t\t\t\tevents = slices.Delete(events, i, i+1)\n\t\t\t\tn -= 1\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ti++\n\t\t}\n\n\t\treturn n, err\n\t}\n}\n\ntype temporaryError interface {\n\tTemporary() bool\n}\n\n// wakeWaitForClose unblocks Wait if it's epoll_wait.\nfunc (p *Poller) wakeWaitForClose() error {\n\tp.eventMu.Lock()\n\tdefer p.eventMu.Unlock()\n\n\tif p.closeEvent == nil {\n\t\treturn fmt.Errorf(\"epoll wake: %w\", os.ErrClosed)\n\t}\n\n\treturn p.closeEvent.add(1)\n}\n\n// Flush unblocks Wait if it's epoll_wait, for purposes of reading pending samples\nfunc (p *Poller) Flush() error {\n\tp.eventMu.Lock()\n\tdefer p.eventMu.Unlock()\n\n\tif p.flushEvent == nil {\n\t\treturn fmt.Errorf(\"epoll wake: %w\", os.ErrClosed)\n\t}\n\n\treturn p.flushEvent.add(1)\n}\n\n// eventFd wraps a Linux eventfd.\n//\n// An eventfd acts like a counter: writes add to the counter, reads retrieve\n// the counter and reset it to zero. Reads also block if the counter is zero.\n//\n// See man 2 eventfd.\ntype eventFd struct {\n\tfile *os.File\n\t// prefer raw over file.Fd(), since the latter puts the file into blocking\n\t// mode.\n\traw int\n}\n\nfunc newEventFd() (*eventFd, error) {\n\tfd, err := unix.Eventfd(0, unix.O_CLOEXEC|unix.O_NONBLOCK)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfile := os.NewFile(uintptr(fd), \"event\")\n\treturn &eventFd{file, fd}, nil\n}\n\nfunc (efd *eventFd) close() error {\n\treturn efd.file.Close()\n}\n\nfunc (efd *eventFd) add(n uint64) error {\n\tvar buf [8]byte\n\tinternal.NativeEndian.PutUint64(buf[:], n)\n\t_, err := efd.file.Write(buf[:])\n\treturn err\n}\n\nfunc (efd *eventFd) read() (uint64, error) {\n\tvar buf [8]byte\n\t_, err := efd.file.Read(buf[:])\n\treturn internal.NativeEndian.Uint64(buf[:]), err\n}\n"
  },
  {
    "path": "internal/epoll/poller_test.go",
    "content": "//go:build !windows\n\npackage epoll\n\nimport (\n\t\"errors\"\n\t\"math\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\nfunc TestPoller(t *testing.T) {\n\tt.Parallel()\n\n\tevent, poller := mustNewPoller(t)\n\n\tdone := make(chan struct{}, 1)\n\tread := func() {\n\t\tdefer func() {\n\t\t\tdone <- struct{}{}\n\t\t}()\n\n\t\tevents := make([]unix.EpollEvent, 1)\n\n\t\tn, err := poller.Wait(events, time.Time{})\n\t\tif errors.Is(err, os.ErrClosed) {\n\t\t\treturn\n\t\t}\n\n\t\tif err != nil {\n\t\t\tt.Error(\"Error from wait:\", err)\n\t\t\treturn\n\t\t}\n\n\t\tif n != 1 {\n\t\t\tt.Errorf(\"Got %d instead of 1 events\", n)\n\t\t}\n\n\t\tif e := events[0]; e.Pad != 42 {\n\t\t\tt.Errorf(\"Incorrect value in EpollEvent.Pad: %d != 42\", e.Pad)\n\t\t}\n\t}\n\n\tif err := event.add(1); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tgo read()\n\tselect {\n\tcase <-done:\n\tcase <-time.After(time.Second):\n\t\tt.Fatal(\"Timed out\")\n\t}\n\n\tif _, err := event.read(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tgo read()\n\tselect {\n\tcase <-done:\n\t\tt.Fatal(\"Wait doesn't block\")\n\tcase <-time.After(time.Second):\n\t}\n\n\tif err := poller.Close(); err != nil {\n\t\tt.Fatal(\"Close returns an error:\", err)\n\t}\n\n\tselect {\n\tcase <-done:\n\tcase <-time.After(time.Second):\n\t\tt.Fatal(\"Close doesn't unblock Wait\")\n\t}\n\n\tif err := poller.Close(); !errors.Is(err, os.ErrClosed) {\n\t\tt.Fatal(\"Closing a second time doesn't return ErrClosed:\", err)\n\t}\n}\n\nfunc TestPollerDeadline(t *testing.T) {\n\tt.Parallel()\n\n\t_, poller := mustNewPoller(t)\n\tevents := make([]unix.EpollEvent, 1)\n\n\t_, err := poller.Wait(events, time.Now().Add(-time.Second))\n\tif !errors.Is(err, os.ErrDeadlineExceeded) {\n\t\tt.Fatal(\"Expected os.ErrDeadlineExceeded on deadline in the past, got\", err)\n\t}\n\n\tdone := make(chan struct{})\n\tgo func() {\n\t\tdefer close(done)\n\n\t\t_, err := poller.Wait(events, time.Now().Add(math.MaxInt64))\n\t\tif !errors.Is(err, os.ErrClosed) {\n\t\t\tt.Error(\"Expected os.ErrClosed when interrupting deadline, got\", err)\n\t\t}\n\t}()\n\n\t// Wait for the goroutine to enter the syscall.\n\ttime.Sleep(500 * time.Microsecond)\n\n\tpoller.Close()\n\t<-done\n}\n\nfunc TestPollerFlush(t *testing.T) {\n\tt.Parallel()\n\n\t_, poller := mustNewPoller(t)\n\tevents := make([]unix.EpollEvent, 1)\n\n\tdone := make(chan struct{})\n\tgo func() {\n\t\tdefer close(done)\n\n\t\t_, err := poller.Wait(events, time.Time{})\n\t\tqt.Check(t, qt.ErrorIs(err, ErrFlushed))\n\t}()\n\n\t// Wait for the goroutine to enter the syscall.\n\ttime.Sleep(500 * time.Microsecond)\n\n\tpoller.Flush()\n\t<-done\n}\n\nfunc mustNewPoller(t *testing.T) (*eventFd, *Poller) {\n\tt.Helper()\n\n\tevent, err := newEventFd()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tt.Cleanup(func() { event.close() })\n\n\tpoller, err := New()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tt.Cleanup(func() { poller.Close() })\n\n\tif err := poller.Add(event.raw, 42); err != nil {\n\t\tt.Fatal(\"Can't add fd:\", err)\n\t}\n\n\treturn event, poller\n}\n"
  },
  {
    "path": "internal/errors.go",
    "content": "package internal\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n)\n\n// ErrorWithLog wraps err in a VerifierError that includes the parsed verifier\n// log buffer.\n//\n// The default error output is a summary of the full log. The latter can be\n// accessed via VerifierError.Log or by formatting the error, see Format.\nfunc ErrorWithLog(source string, err error, log []byte) *VerifierError {\n\tconst whitespace = \"\\t\\r\\v\\n \"\n\n\t// Convert verifier log C string by truncating it on the first 0 byte\n\t// and trimming trailing whitespace before interpreting as a Go string.\n\tif i := bytes.IndexByte(log, 0); i != -1 {\n\t\tlog = log[:i]\n\t}\n\n\tlog = bytes.Trim(log, whitespace)\n\tif len(log) == 0 {\n\t\treturn &VerifierError{source, err, nil}\n\t}\n\n\tlogLines := bytes.Split(log, []byte{'\\n'})\n\tlines := make([]string, 0, len(logLines))\n\tfor _, line := range logLines {\n\t\t// Don't remove leading white space on individual lines. We rely on it\n\t\t// when outputting logs.\n\t\tlines = append(lines, string(bytes.TrimRight(line, whitespace)))\n\t}\n\n\treturn &VerifierError{source, err, lines}\n}\n\n// VerifierError includes information from the eBPF verifier.\n//\n// It summarises the log output, see Format if you want to output the full contents.\ntype VerifierError struct {\n\tsource string\n\t// The error which caused this error.\n\tCause error\n\t// The verifier output split into lines.\n\tLog []string\n}\n\nfunc (le *VerifierError) Unwrap() error {\n\treturn le.Cause\n}\n\nfunc (le *VerifierError) Error() string {\n\tlog := le.Log\n\tif n := len(log); n > 0 && strings.HasPrefix(log[n-1], \"processed \") {\n\t\t// Get rid of \"processed 39 insns (limit 1000000) ...\" from summary.\n\t\tlog = log[:n-1]\n\t}\n\n\tvar b strings.Builder\n\tfmt.Fprintf(&b, \"%s: %s\", le.source, le.Cause.Error())\n\n\tn := len(log)\n\tif n == 0 {\n\t\treturn b.String()\n\t}\n\n\tlines := log[n-1:]\n\tif n >= 2 && includePreviousLine(log[n-1]) {\n\t\t// Add one more line of context if it aids understanding the error.\n\t\tlines = log[n-2:]\n\t}\n\n\tfor _, line := range lines {\n\t\tb.WriteString(\": \")\n\t\tb.WriteString(strings.TrimSpace(line))\n\t}\n\n\tomitted := len(le.Log) - len(lines)\n\tif omitted > 0 {\n\t\tfmt.Fprintf(&b, \" (%d line(s) omitted)\", omitted)\n\t}\n\n\treturn b.String()\n}\n\n// includePreviousLine returns true if the given line likely is better\n// understood with additional context from the preceding line.\nfunc includePreviousLine(line string) bool {\n\t// We need to find a good trade off between understandable error messages\n\t// and too much complexity here. Checking the string prefix is ok, requiring\n\t// regular expressions to do it is probably overkill.\n\n\tif strings.HasPrefix(line, \"\\t\") {\n\t\t// [13] STRUCT drm_rect size=16 vlen=4\n\t\t// \\tx1 type_id=2\n\t\treturn true\n\t}\n\n\tif len(line) >= 2 && line[0] == 'R' && line[1] >= '0' && line[1] <= '9' {\n\t\t// 0: (95) exit\n\t\t// R0 !read_ok\n\t\treturn true\n\t}\n\n\tif strings.HasPrefix(line, \"invalid bpf_context access\") {\n\t\t// 0: (79) r6 = *(u64 *)(r1 +0)\n\t\t// func '__x64_sys_recvfrom' arg0 type FWD is not a struct\n\t\t// invalid bpf_context access off=0 size=8\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// Format the error.\n//\n// Understood verbs are %s and %v, which are equivalent to calling Error(). %v\n// allows outputting additional information using the following flags:\n//\n//\t%+<width>v: Output the first <width> lines, or all lines if no width is given.\n//\t%-<width>v: Output the last <width> lines, or all lines if no width is given.\n//\n// Use width to specify how many lines to output. Use the '-' flag to output\n// lines from the end of the log instead of the beginning.\nfunc (le *VerifierError) Format(f fmt.State, verb rune) {\n\tswitch verb {\n\tcase 's':\n\t\t_, _ = io.WriteString(f, le.Error())\n\n\tcase 'v':\n\t\tn, haveWidth := f.Width()\n\t\tif !haveWidth || n > len(le.Log) {\n\t\t\tn = len(le.Log)\n\t\t}\n\n\t\tif !f.Flag('+') && !f.Flag('-') {\n\t\t\tif haveWidth {\n\t\t\t\t_, _ = io.WriteString(f, \"%!v(BADWIDTH)\")\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t_, _ = io.WriteString(f, le.Error())\n\t\t\treturn\n\t\t}\n\n\t\tif f.Flag('+') && f.Flag('-') {\n\t\t\t_, _ = io.WriteString(f, \"%!v(BADFLAG)\")\n\t\t\treturn\n\t\t}\n\n\t\tfmt.Fprintf(f, \"%s: %s:\", le.source, le.Cause.Error())\n\n\t\tomitted := len(le.Log) - n\n\t\tlines := le.Log[:n]\n\t\tif f.Flag('-') {\n\t\t\t// Print last instead of first lines.\n\t\t\tlines = le.Log[len(le.Log)-n:]\n\t\t\tif omitted > 0 {\n\t\t\t\tfmt.Fprintf(f, \"\\n\\t(%d line(s) omitted)\", omitted)\n\t\t\t}\n\t\t}\n\n\t\tfor _, line := range lines {\n\t\t\tfmt.Fprintf(f, \"\\n\\t%s\", line)\n\t\t}\n\n\t\tif !f.Flag('-') {\n\t\t\tif omitted > 0 {\n\t\t\t\tfmt.Fprintf(f, \"\\n\\t(%d line(s) omitted)\", omitted)\n\t\t\t}\n\t\t}\n\n\tdefault:\n\t\tfmt.Fprintf(f, \"%%!%c(BADVERB)\", verb)\n\t}\n}\n"
  },
  {
    "path": "internal/errors_test.go",
    "content": "package internal\n\nimport (\n\t\"errors\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestVerifierErrorWhitespace(t *testing.T) {\n\tb := []byte(\"unreachable insn 28\")\n\tb = append(b,\n\t\t0xa,  // \\n\n\t\t0xd,  // \\r\n\t\t0x9,  // \\t\n\t\t0x20, // space\n\t\t0, 0, // trailing NUL bytes\n\t)\n\n\terr := ErrorWithLog(\"frob\", errors.New(\"test\"), b)\n\tqt.Assert(t, qt.Equals(err.Error(), \"frob: test: unreachable insn 28\"))\n\n\tfor _, log := range [][]byte{\n\t\tnil,\n\t\t[]byte(\"\\x00\"),\n\t\t[]byte(\" \"),\n\t} {\n\t\terr = ErrorWithLog(\"frob\", errors.New(\"test\"), log)\n\t\tqt.Assert(t, qt.Equals(err.Error(), \"frob: test\"), qt.Commentf(\"empty log %q has incorrect format\", log))\n\t}\n}\n\nfunc TestVerifierErrorWrapping(t *testing.T) {\n\tsentinel := errors.New(\"bad\")\n\n\tve := ErrorWithLog(\"frob\", sentinel, nil)\n\tqt.Assert(t, qt.ErrorIs(ve, sentinel), qt.Commentf(\"should wrap provided error\"))\n\n\tve = ErrorWithLog(\"frob\", sentinel, []byte(\"foo\"))\n\tqt.Assert(t, qt.ErrorIs(ve, sentinel), qt.Commentf(\"should wrap provided error\"))\n\tqt.Assert(t, qt.StringContains(ve.Error(), \"foo\"), qt.Commentf(\"verifier log should appear in error string\"))\n}\n\nfunc TestVerifierErrorSummary(t *testing.T) {\n\t// Suppress the last line containing 'processed ... insns'.\n\terrno524 := readErrorFromFile(t, \"testdata/errno524.log\")\n\tqt.Assert(t, qt.StringContains(errno524.Error(), \"JIT doesn't support bpf-to-bpf calls\"))\n\tqt.Assert(t, qt.Not(qt.StringContains(errno524.Error(), \"processed 39 insns\")))\n\n\t// Include the previous line if the current one starts with a tab.\n\tinvalidMember := readErrorFromFile(t, \"testdata/invalid-member.log\")\n\tqt.Assert(t, qt.StringContains(invalidMember.Error(), \"STRUCT task_struct size=7744 vlen=218: cpus_mask type_id=109 bitfield_size=0 bits_offset=7744 Invalid member\"))\n\n\t// Only include the last line.\n\tissue43 := readErrorFromFile(t, \"testdata/issue-43.log\")\n\tqt.Assert(t, qt.StringContains(issue43.Error(), \"[11] FUNC helper_func2 type_id=10 vlen != 0\"))\n\tqt.Assert(t, qt.Not(qt.StringContains(issue43.Error(), \"[10] FUNC_PROTO (anon) return=3 args=(3 arg)\")))\n\n\t// Include instruction that caused invalid register access.\n\tinvalidR0 := readErrorFromFile(t, \"testdata/invalid-R0.log\")\n\tqt.Assert(t, qt.StringContains(invalidR0.Error(), \"0: (95) exit: R0 !read_ok\"))\n\n\t// Include symbol that doesn't match context type.\n\tinvalidCtx := readErrorFromFile(t, \"testdata/invalid-ctx-access.log\")\n\tqt.Assert(t, qt.StringContains(invalidCtx.Error(), \"func '__x64_sys_recvfrom' arg0 type FWD is not a struct: invalid bpf_context access off=0 size=8\"))\n}\n\nfunc readErrorFromFile(tb testing.TB, file string) *VerifierError {\n\ttb.Helper()\n\n\tcontents, err := os.ReadFile(file)\n\tif err != nil {\n\t\ttb.Fatal(\"Read file:\", err)\n\t}\n\n\treturn ErrorWithLog(\"file\", errors.New(\"error\"), contents)\n}\n"
  },
  {
    "path": "internal/feature.go",
    "content": "package internal\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"runtime\"\n\t\"sync\"\n\n\t\"github.com/cilium/ebpf/internal/platform\"\n)\n\n// ErrNotSupported indicates that a feature is not supported.\nvar ErrNotSupported = errors.New(\"not supported\")\n\n// ErrNotSupportedOnOS indicates that a feature is not supported on the current\n// operating system.\nvar ErrNotSupportedOnOS = fmt.Errorf(\"%w on %s\", ErrNotSupported, runtime.GOOS)\n\n// ErrRestrictedKernel is returned when kernel address information is restricted\n// by kernel.kptr_restrict and/or net.core.bpf_jit_harden sysctls.\nvar ErrRestrictedKernel = errors.New(\"restricted by kernel.kptr_restrict and/or net.core.bpf_jit_harden sysctls\")\n\n// UnsupportedFeatureError is returned by FeatureTest() functions.\ntype UnsupportedFeatureError struct {\n\t// The minimum version required for this feature.\n\t//\n\t// On Linux this refers to the mainline kernel version, on other platforms\n\t// to the version of the runtime.\n\t//\n\t// Used for the error string, and for sanity checking during testing.\n\tMinimumVersion Version\n\n\t// The name of the feature that isn't supported.\n\tName string\n}\n\nfunc (ufe *UnsupportedFeatureError) Error() string {\n\tif ufe.MinimumVersion.Unspecified() {\n\t\treturn fmt.Sprintf(\"%s not supported\", ufe.Name)\n\t}\n\treturn fmt.Sprintf(\"%s not supported (requires >= %s)\", ufe.Name, ufe.MinimumVersion)\n}\n\n// Is indicates that UnsupportedFeatureError is ErrNotSupported.\nfunc (ufe *UnsupportedFeatureError) Is(target error) bool {\n\treturn target == ErrNotSupported\n}\n\n// FeatureTest caches the result of a [FeatureTestFn].\n//\n// Fields should not be modified after creation.\ntype FeatureTest struct {\n\t// The name of the feature being detected.\n\tName string\n\t// Version in the form Major.Minor[.Patch].\n\tVersion string\n\t// The feature test itself.\n\tFn FeatureTestFn\n\n\tmu     sync.RWMutex\n\tdone   bool\n\tresult error\n}\n\n// FeatureTestFn is used to determine whether the kernel supports\n// a certain feature.\n//\n// The return values have the following semantics:\n//\n//\terr == ErrNotSupported: the feature is not available\n//\terr == nil: the feature is available\n//\terr != nil: the test couldn't be executed\ntype FeatureTestFn func() error\n\n// NewFeatureTest is a convenient way to create a single [FeatureTest].\n//\n// versions specifies in which version of a BPF runtime a feature appeared.\n// The format is \"GOOS:Major.Minor[.Patch]\". GOOS may be omitted when targeting\n// Linux. Returns [ErrNotSupportedOnOS] if there is no version specified for the\n// current OS.\nfunc NewFeatureTest(name string, fn FeatureTestFn, versions ...string) func() error {\n\tversion, err := platform.SelectVersion(versions)\n\tif err != nil {\n\t\treturn func() error { return err }\n\t}\n\n\tif version == \"\" {\n\t\treturn func() error {\n\t\t\t// We don't return an UnsupportedFeatureError here, since that will\n\t\t\t// trigger version checks which don't make sense.\n\t\t\treturn fmt.Errorf(\"%s: %w\", name, ErrNotSupportedOnOS)\n\t\t}\n\t}\n\n\tft := &FeatureTest{\n\t\tName:    name,\n\t\tVersion: version,\n\t\tFn:      fn,\n\t}\n\n\treturn ft.execute\n}\n\n// execute the feature test.\n//\n// The result is cached if the test is conclusive.\n//\n// See [FeatureTestFn] for the meaning of the returned error.\nfunc (ft *FeatureTest) execute() error {\n\tft.mu.RLock()\n\tresult, done := ft.result, ft.done\n\tft.mu.RUnlock()\n\n\tif done {\n\t\treturn result\n\t}\n\n\tft.mu.Lock()\n\tdefer ft.mu.Unlock()\n\n\t// The test may have been executed by another caller while we were\n\t// waiting to acquire ft.mu.\n\tif ft.done {\n\t\treturn ft.result\n\t}\n\n\terr := ft.Fn()\n\tif err == nil {\n\t\tft.done = true\n\t\treturn nil\n\t}\n\n\tif errors.Is(err, ErrNotSupported) {\n\t\tvar v Version\n\t\tif ft.Version != \"\" {\n\t\t\tv, err = NewVersion(ft.Version)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"feature %s: %w\", ft.Name, err)\n\t\t\t}\n\t\t}\n\n\t\tft.done = true\n\t\tft.result = &UnsupportedFeatureError{\n\t\t\tMinimumVersion: v,\n\t\t\tName:           ft.Name,\n\t\t}\n\n\t\treturn ft.result\n\t}\n\n\t// We couldn't execute the feature test to a point\n\t// where it could make a determination.\n\t// Don't cache the result, just return it.\n\treturn fmt.Errorf(\"detect support for %s: %w\", ft.Name, err)\n}\n\n// FeatureMatrix groups multiple related feature tests into a map.\n//\n// Useful when there is a small number of discrete features which are known\n// at compile time.\n//\n// It must not be modified concurrently with calling [FeatureMatrix.Result].\ntype FeatureMatrix[K comparable] map[K]*FeatureTest\n\n// Result returns the outcome of the feature test for the given key.\n//\n// It's safe to call this function concurrently.\n//\n// Always returns [ErrNotSupportedOnOS] on Windows.\nfunc (fm FeatureMatrix[K]) Result(key K) error {\n\tft, ok := fm[key]\n\tif !ok {\n\t\treturn fmt.Errorf(\"no feature probe for %v\", key)\n\t}\n\n\tif platform.IsWindows {\n\t\treturn fmt.Errorf(\"%s: %w\", ft.Name, ErrNotSupportedOnOS)\n\t}\n\n\treturn ft.execute()\n}\n\n// FeatureCache caches a potentially unlimited number of feature probes.\n//\n// Useful when there is a high cardinality for a feature test.\ntype FeatureCache[K comparable] struct {\n\tmu       sync.RWMutex\n\tnewTest  func(K) *FeatureTest\n\tfeatures map[K]*FeatureTest\n}\n\nfunc NewFeatureCache[K comparable](newTest func(K) *FeatureTest) *FeatureCache[K] {\n\treturn &FeatureCache[K]{\n\t\tnewTest:  newTest,\n\t\tfeatures: make(map[K]*FeatureTest),\n\t}\n}\n\nfunc (fc *FeatureCache[K]) Result(key K) error {\n\tif platform.IsWindows {\n\t\treturn fmt.Errorf(\"feature probe for %v: %w\", key, ErrNotSupportedOnOS)\n\t}\n\n\t// NB: Executing the feature test happens without fc.mu taken.\n\treturn fc.retrieve(key).execute()\n}\n\nfunc (fc *FeatureCache[K]) retrieve(key K) *FeatureTest {\n\tfc.mu.RLock()\n\tft := fc.features[key]\n\tfc.mu.RUnlock()\n\n\tif ft != nil {\n\t\treturn ft\n\t}\n\n\tfc.mu.Lock()\n\tdefer fc.mu.Unlock()\n\n\tif ft := fc.features[key]; ft != nil {\n\t\treturn ft\n\t}\n\n\tft = fc.newTest(key)\n\tfc.features[key] = ft\n\treturn ft\n}\n"
  },
  {
    "path": "internal/feature_test.go",
    "content": "package internal\n\nimport (\n\t\"errors\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/internal/platform\"\n\t\"github.com/cilium/ebpf/internal/testutils/testmain\"\n)\n\nfunc TestMain(m *testing.M) {\n\ttestmain.Run(m)\n}\n\nfunc TestFeatureTest(t *testing.T) {\n\tvar called bool\n\n\tfn := NewFeatureTest(\"foo\", func() error {\n\t\tcalled = true\n\t\treturn nil\n\t}, \"1.0\")\n\n\tif called {\n\t\tt.Error(\"Function was called too early\")\n\t}\n\n\terr := fn()\n\tif errors.Is(err, ErrNotSupportedOnOS) {\n\t\tqt.Assert(t, qt.IsFalse(called))\n\t\treturn\n\t}\n\n\tqt.Assert(t, qt.IsTrue(called), qt.Commentf(\"function should be invoked\"))\n\n\tif err != nil {\n\t\tt.Error(\"Unexpected negative result:\", err)\n\t}\n\n\tfn = NewFeatureTest(\"bar\", func() error {\n\t\treturn ErrNotSupported\n\t}, \"2.1.1\")\n\n\terr = fn()\n\tif err == nil {\n\t\tt.Fatal(\"Unexpected positive result\")\n\t}\n\n\tfte, ok := err.(*UnsupportedFeatureError)\n\tif !ok {\n\t\tt.Fatal(\"Result is not a *UnsupportedFeatureError\")\n\t}\n\n\tif !strings.Contains(fte.Error(), \"2.1.1\") {\n\t\tt.Error(\"UnsupportedFeatureError.Error doesn't contain version\")\n\t}\n\n\tif !errors.Is(err, ErrNotSupported) {\n\t\tt.Error(\"UnsupportedFeatureError is not ErrNotSupported\")\n\t}\n\n\terr2 := fn()\n\tif err != err2 {\n\t\tt.Error(\"Didn't cache an error wrapping ErrNotSupported\")\n\t}\n\n\tfn = NewFeatureTest(\"bar\", func() error {\n\t\treturn errors.New(\"foo\")\n\t}, \"2.1.1\")\n\n\terr1, err2 := fn(), fn()\n\tif err1 == err2 {\n\t\tt.Error(\"Cached result of unsuccessful execution\")\n\t}\n}\n\nfunc TestFeatureTestNotSupportedOnOS(t *testing.T) {\n\tsentinel := errors.New(\"quux\")\n\tfn := func() error { return sentinel }\n\n\tqt.Assert(t, qt.IsNotNil(NewFeatureTest(\"foo\", fn)()))\n\tqt.Assert(t, qt.ErrorIs(NewFeatureTest(\"foo\", fn, \"froz:1.0.0\")(), ErrNotSupportedOnOS))\n\tqt.Assert(t, qt.ErrorIs(NewFeatureTest(\"foo\", fn, runtime.GOOS+\":1.0\")(), sentinel))\n\tif platform.IsLinux {\n\t\tqt.Assert(t, qt.ErrorIs(NewFeatureTest(\"foo\", fn, \"1.0\")(), sentinel))\n\t}\n}\n"
  },
  {
    "path": "internal/io.go",
    "content": "package internal\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sync\"\n)\n\n// NewBufferedSectionReader wraps an io.ReaderAt in an appropriately-sized\n// buffered reader. It is a convenience function for reading subsections of\n// ELF sections while minimizing the amount of read() syscalls made.\n//\n// Syscall overhead is non-negligible in continuous integration context\n// where ELFs might be accessed over virtual filesystems with poor random\n// access performance. Buffering reads makes sense because (sub)sections\n// end up being read completely anyway.\n//\n// Use instead of the r.Seek() + io.LimitReader() pattern.\nfunc NewBufferedSectionReader(ra io.ReaderAt, off, n int64) *bufio.Reader {\n\t// Clamp the size of the buffer to one page to avoid slurping large parts\n\t// of a file into memory. bufio.NewReader uses a hardcoded default buffer\n\t// of 4096. Allow arches with larger pages to allocate more, but don't\n\t// allocate a fixed 4k buffer if we only need to read a small segment.\n\tbuf := n\n\tif ps := int64(os.Getpagesize()); n > ps {\n\t\tbuf = ps\n\t}\n\n\treturn bufio.NewReaderSize(io.NewSectionReader(ra, off, n), int(buf))\n}\n\n// DiscardZeroes makes sure that all written bytes are zero\n// before discarding them.\ntype DiscardZeroes struct{}\n\nfunc (DiscardZeroes) Write(p []byte) (int, error) {\n\tfor _, b := range p {\n\t\tif b != 0 {\n\t\t\treturn 0, errors.New(\"encountered non-zero byte\")\n\t\t}\n\t}\n\treturn len(p), nil\n}\n\n// ReadAllCompressed decompresses a gzipped file into memory.\nfunc ReadAllCompressed(file string) ([]byte, error) {\n\tfh, err := os.Open(file)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer fh.Close()\n\n\tgz, err := gzip.NewReader(fh)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer gz.Close()\n\n\treturn io.ReadAll(gz)\n}\n\n// ReadUint64FromFile reads a uint64 from a file.\n//\n// format specifies the contents of the file in fmt.Scanf syntax.\nfunc ReadUint64FromFile(format string, path ...string) (uint64, error) {\n\tfilename := filepath.Join(path...)\n\tdata, err := os.ReadFile(filename)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"reading file %q: %w\", filename, err)\n\t}\n\n\tvar value uint64\n\tn, err := fmt.Fscanf(bytes.NewReader(data), format, &value)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"parsing file %q: %w\", filename, err)\n\t}\n\tif n != 1 {\n\t\treturn 0, fmt.Errorf(\"parsing file %q: expected 1 item, got %d\", filename, n)\n\t}\n\n\treturn value, nil\n}\n\ntype uint64FromFileKey struct {\n\tformat, path string\n}\n\nvar uint64FromFileCache = struct {\n\tsync.RWMutex\n\tvalues map[uint64FromFileKey]uint64\n}{\n\tvalues: map[uint64FromFileKey]uint64{},\n}\n\n// ReadUint64FromFileOnce is like readUint64FromFile but memoizes the result.\nfunc ReadUint64FromFileOnce(format string, path ...string) (uint64, error) {\n\tfilename := filepath.Join(path...)\n\tkey := uint64FromFileKey{format, filename}\n\n\tuint64FromFileCache.RLock()\n\tif value, ok := uint64FromFileCache.values[key]; ok {\n\t\tuint64FromFileCache.RUnlock()\n\t\treturn value, nil\n\t}\n\tuint64FromFileCache.RUnlock()\n\n\tvalue, err := ReadUint64FromFile(format, filename)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tuint64FromFileCache.Lock()\n\tdefer uint64FromFileCache.Unlock()\n\n\tif value, ok := uint64FromFileCache.values[key]; ok {\n\t\t// Someone else got here before us, use what is cached.\n\t\treturn value, nil\n\t}\n\n\tuint64FromFileCache.values[key] = value\n\treturn value, nil\n}\n"
  },
  {
    "path": "internal/io_test.go",
    "content": "package internal\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"testing\"\n)\n\nfunc TestDiscardZero(t *testing.T) {\n\t_, err := io.Copy(DiscardZeroes{}, bytes.NewReader([]byte{0, 0, 0}))\n\tif err != nil {\n\t\tt.Error(\"Returned an error even though input was zero:\", err)\n\t}\n\n\t_, err = io.Copy(DiscardZeroes{}, bytes.NewReader([]byte{1}))\n\tif err == nil {\n\t\tt.Error(\"No error even though input is non-zero\")\n\t}\n}\n"
  },
  {
    "path": "internal/kallsyms/cache.go",
    "content": "package kallsyms\n\nimport \"sync\"\n\ntype cache[K, V comparable] struct {\n\tm sync.Map\n}\n\nfunc (c *cache[K, V]) Load(key K) (value V, _ bool) {\n\tv, ok := c.m.Load(key)\n\tif !ok {\n\t\treturn value, false\n\t}\n\tvalue = v.(V)\n\treturn value, true\n}\n\nfunc (c *cache[K, V]) Store(key K, value V) {\n\tc.m.Store(key, value)\n}\n"
  },
  {
    "path": "internal/kallsyms/kallsyms.go",
    "content": "package kallsyms\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"slices\"\n\t\"strconv\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/platform\"\n)\n\nvar errAmbiguousKsym = errors.New(\"multiple kernel symbols with the same name\")\n\nvar symAddrs cache[string, uint64]\n\n// AssignAddresses looks up the addresses of the requested symbols in the kernel\n// and assigns them to their corresponding values in the symbols map. Results\n// of all lookups are cached, successful or otherwise.\n//\n// Any symbols missing in the kernel are ignored. Returns an error if multiple\n// addresses were found for a symbol.\nfunc AssignAddresses(symbols map[string]uint64) error {\n\tif !platform.IsLinux {\n\t\treturn fmt.Errorf(\"read /proc/kallsyms: %w\", internal.ErrNotSupportedOnOS)\n\t}\n\n\tif len(symbols) == 0 {\n\t\treturn nil\n\t}\n\n\t// Attempt to fetch symbols from cache.\n\trequest := make(map[string]uint64)\n\tfor name := range symbols {\n\t\tif addr, ok := symAddrs.Load(name); ok {\n\t\t\tsymbols[name] = addr\n\t\t\tcontinue\n\t\t}\n\n\t\t// Mark the symbol to be read from /proc/kallsyms.\n\t\trequest[name] = 0\n\t}\n\tif len(request) == 0 {\n\t\t// All symbols satisfied from cache.\n\t\treturn nil\n\t}\n\n\tf, err := os.Open(\"/proc/kallsyms\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer f.Close()\n\n\tif err := assignAddresses(f, request); err != nil {\n\t\treturn fmt.Errorf(\"loading symbol addresses: %w\", err)\n\t}\n\n\t// Update the cache with the new symbols. Cache all requested symbols even if\n\t// they weren't found, to avoid repeated lookups.\n\tfor name, addr := range request {\n\t\tsymAddrs.Store(name, addr)\n\t\tsymbols[name] = addr\n\t}\n\n\treturn nil\n}\n\n// assignAddresses assigns kernel symbol addresses read from f to values\n// requested by symbols. Always scans the whole input to make sure the user\n// didn't request an ambiguous symbol.\nfunc assignAddresses(f io.Reader, symbols map[string]uint64) error {\n\tif len(symbols) == 0 {\n\t\treturn nil\n\t}\n\tr := newReader(f)\n\tfor r.Line() {\n\t\ts, err, skip := parseSymbol(r, nil)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"parsing kallsyms line: %w\", err)\n\t\t}\n\t\tif skip {\n\t\t\tcontinue\n\t\t}\n\n\t\texisting, requested := symbols[string(s.name)]\n\t\tif existing != 0 {\n\t\t\t// Multiple addresses for a symbol have been found. Return a friendly\n\t\t\t// error to avoid silently attaching to the wrong symbol. libbpf also\n\t\t\t// rejects referring to ambiguous symbols.\n\t\t\treturn fmt.Errorf(\"symbol %s(0x%x): duplicate found at address 0x%x: %w\", s.name, existing, s.addr, errAmbiguousKsym)\n\t\t}\n\t\tif requested {\n\t\t\t// Reading a symbol with a zero address is a strong indication that\n\t\t\t// kptr_restrict is set and the process doesn't have CAP_SYSLOG, or\n\t\t\t// kptr_restrict is set to 2 (never show addresses).\n\t\t\t//\n\t\t\t// When running the kernel with KASLR disabled (like CI kernels running in\n\t\t\t// microVMs), kallsyms will display many absolute symbols at address 0.\n\t\t\t// This memory is unlikely to contain anything useful, and production\n\t\t\t// machines are unlikely to run without KASLR.\n\t\t\t//\n\t\t\t// Return a helpful error instead of silently returning zero addresses.\n\t\t\tif s.addr == 0 {\n\t\t\t\treturn fmt.Errorf(\"symbol %s: %w\", s.name, internal.ErrRestrictedKernel)\n\t\t\t}\n\t\t\tsymbols[string(s.name)] = s.addr\n\t\t}\n\t}\n\tif err := r.Err(); err != nil {\n\t\treturn fmt.Errorf(\"reading kallsyms: %w\", err)\n\t}\n\n\treturn nil\n}\n\ntype ksym struct {\n\taddr uint64\n\tname []byte\n\tmod  []byte\n}\n\n// parseSymbol parses a line from /proc/kallsyms into an address, type, name and\n// module. Skip will be true if the symbol doesn't match any of the given symbol\n// types. See `man 1 nm` for all available types.\n//\n// Only yields symbols whose type is contained in types. An empty value for types\n// disables this filtering.\n//\n// Example line: `ffffffffc1682010 T nf_nat_init\\t[nf_nat]`\nfunc parseSymbol(r *reader, types []rune) (s ksym, err error, skip bool) {\n\tfor i := 0; r.Word(); i++ {\n\t\tswitch i {\n\t\t// Address of the symbol.\n\t\tcase 0:\n\t\t\ts.addr, err = strconv.ParseUint(r.Text(), 16, 64)\n\t\t\tif err != nil {\n\t\t\t\treturn s, fmt.Errorf(\"parsing address: %w\", err), false\n\t\t\t}\n\t\t// Type of the symbol. Assume the character is ASCII-encoded by converting\n\t\t// it directly to a rune, since it's a fixed field controlled by the kernel.\n\t\tcase 1:\n\t\t\tif len(types) > 0 && !slices.Contains(types, rune(r.Bytes()[0])) {\n\t\t\t\treturn s, nil, true\n\t\t\t}\n\t\t// Name of the symbol.\n\t\tcase 2:\n\t\t\ts.name = r.Bytes()\n\t\t// Kernel module the symbol is provided by.\n\t\tcase 3:\n\t\t\ts.mod = bytes.Trim(r.Bytes(), \"[]\")\n\t\t// Ignore any future fields.\n\t\tdefault:\n\t\t\treturn\n\t\t}\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "internal/kallsyms/kallsyms_test.go",
    "content": "package kallsyms\n\nimport (\n\t\"bytes\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/internal/platform\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nvar syms = []byte(`0000000000000001 t hid_generic_probe\t[hid_generic]\n00000000000000EA t writenote\n00000000000000A0 T tcp_connect\n00000000000000B0 B empty_zero_page\n00000000000000C0 D kimage_vaddr\n00000000000000D0 R __start_pci_fixups_early\n00000000000000E0 V hv_root_partition\n00000000000000F0 W calibrate_delay_is_known\nA0000000000000AA a nft_counter_seq\t[nft_counter]\nA0000000000000BA b bootconfig_found\nA0000000000000CA d __func__.10\nA0000000000000DA r __ksymtab_LZ4_decompress_fast\nA0000000000000EA t writenote\nA0000000000000FA T bench_sym\t[bench_mod]\nA0000000000000FF t __kstrtab_功能\t[mod]`)\n\nfunc TestParseSyms(t *testing.T) {\n\tr := newReader(bytes.NewReader(syms))\n\ti := 0\n\tfor ; r.Line(); i++ {\n\t\ts, err, skip := parseSymbol(r, nil)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.IsFalse(skip))\n\t\tqt.Assert(t, qt.Not(qt.Equals(s.addr, 0)))\n\t\tqt.Assert(t, qt.Not(qt.Equals(s.name, []byte(\"\"))))\n\t}\n\tqt.Assert(t, qt.IsNil(r.Err()))\n\tqt.Assert(t, qt.Equals(i, 15))\n}\n\nfunc TestParseProcKallsyms(t *testing.T) {\n\t// Read up to 50k symbols from kallsyms to avoid a slow test.\n\tr := newReader(mustOpenProcKallsyms(t))\n\tfor i := 0; r.Line() && i < 50_000; i++ {\n\t\ts, err, skip := parseSymbol(r, nil)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.IsFalse(skip))\n\t\tqt.Assert(t, qt.Not(qt.Equals(s.name, []byte(\"\"))))\n\t}\n\tqt.Assert(t, qt.IsNil(r.Err()))\n}\n\nfunc TestAssignAddressesCaching(t *testing.T) {\n\terr := AssignAddresses(\n\t\tmap[string]uint64{\n\t\t\t\"bpf_perf_event_output\": 0,\n\t\t\t\"foo\":                   0,\n\t\t},\n\t)\n\ttestutils.SkipIfNotSupportedOnOS(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tv, ok := symAddrs.Load(\"bpf_perf_event_output\")\n\tqt.Assert(t, qt.IsTrue(ok))\n\tqt.Assert(t, qt.Not(qt.Equals(v, 0)))\n\n\tv, ok = symAddrs.Load(\"foo\")\n\tqt.Assert(t, qt.IsTrue(ok))\n\tqt.Assert(t, qt.Equals(v, 0))\n}\n\nfunc TestAssignAddresses(t *testing.T) {\n\tb := bytes.NewBuffer(syms)\n\tksyms := map[string]uint64{\n\t\t\"hid_generic_probe\": 0,\n\t\t\"tcp_connect\":       0,\n\t\t\"bootconfig_found\":  0,\n\t}\n\tqt.Assert(t, qt.IsNil(assignAddresses(b, ksyms)))\n\n\tqt.Assert(t, qt.Equals(ksyms[\"hid_generic_probe\"], 0x1))\n\tqt.Assert(t, qt.Equals(ksyms[\"tcp_connect\"], 0xA0))\n\tqt.Assert(t, qt.Equals(ksyms[\"bootconfig_found\"], 0xA0000000000000BA))\n\n\tb = bytes.NewBuffer(syms)\n\tksyms = map[string]uint64{\n\t\t\"hid_generic_probe\": 0,\n\t\t\"writenote\":         0,\n\t}\n\tqt.Assert(t, qt.ErrorIs(assignAddresses(b, ksyms), errAmbiguousKsym))\n}\n\nfunc BenchmarkAssignAddresses(b *testing.B) {\n\tb.ReportAllocs()\n\tfor b.Loop() {\n\t\tb.StopTimer()\n\t\tf := bytes.NewBuffer(syms)\n\t\twant := map[string]uint64{\"bench_sym\": 0}\n\t\tb.StartTimer()\n\n\t\tif err := assignAddresses(f, want); err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\n// Benchmark getting 5 kernel symbols from /proc/kallsyms.\nfunc BenchmarkAssignAddressesKallsyms(b *testing.B) {\n\tb.ReportAllocs()\n\tfor b.Loop() {\n\t\tb.StopTimer()\n\t\tf := mustOpenProcKallsyms(b)\n\t\twant := map[string]uint64{\n\t\t\t\"bpf_trace_vprintk\":     0,\n\t\t\t\"bpf_send_signal\":       0,\n\t\t\t\"bpf_event_notify\":      0,\n\t\t\t\"bpf_trace_printk\":      0,\n\t\t\t\"bpf_perf_event_output\": 0,\n\t\t}\n\t\tb.StartTimer()\n\n\t\tif err := assignAddresses(f, want); err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc mustOpenProcKallsyms(tb testing.TB) *os.File {\n\ttb.Helper()\n\n\tif !platform.IsLinux {\n\t\ttb.Skip(\"/proc/kallsyms is a Linux concept\")\n\t}\n\n\tf, err := os.Open(\"/proc/kallsyms\")\n\tqt.Assert(tb, qt.IsNil(err))\n\ttb.Cleanup(func() { f.Close() })\n\treturn f\n}\n"
  },
  {
    "path": "internal/kallsyms/reader.go",
    "content": "package kallsyms\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"io\"\n)\n\n// reader is a line and word-oriented reader built for reading /proc/kallsyms.\n// It takes an io.Reader and iterates its contents line by line, then word by\n// word.\n//\n// It's designed to allow partial reading of lines without paying the cost of\n// allocating objects that will never be accessed, resulting in less work for\n// the garbage collector.\ntype reader struct {\n\ts    *bufio.Scanner\n\tline []byte\n\tword []byte\n\n\terr error\n}\n\nfunc newReader(r io.Reader) *reader {\n\treturn &reader{\n\t\ts: bufio.NewScanner(r),\n\t}\n}\n\n// Bytes returns the current word as a byte slice.\nfunc (r *reader) Bytes() []byte {\n\treturn r.word\n}\n\n// Text returns the output of Bytes as a string.\nfunc (r *reader) Text() string {\n\treturn string(r.Bytes())\n}\n\n// Line advances the reader to the next line in the input. Calling Line resets\n// the current word, making [reader.Bytes] and [reader.Text] return empty\n// values. Follow this up with a call to [reader.Word].\n//\n// Like [bufio.Scanner], [reader.Err] needs to be checked after Line returns\n// false to determine if an error occurred during reading.\n//\n// Returns true if Line can be called again. Returns false if all lines in the\n// input have been read.\nfunc (r *reader) Line() bool {\n\tfor r.s.Scan() {\n\t\tline := r.s.Bytes()\n\t\tif len(line) == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\tr.line = line\n\t\tr.word = nil\n\n\t\treturn true\n\t}\n\tif err := r.s.Err(); err != nil {\n\t\tr.err = err\n\t}\n\n\treturn false\n}\n\n// Word advances the reader to the next word in the current line.\n//\n// Returns true if a word is found and Word should be called again. Returns\n// false when all words on the line have been read.\nfunc (r *reader) Word() bool {\n\tline := bytes.TrimSpace(r.line)\n\n\tif len(line) == 0 {\n\t\treturn false\n\t}\n\n\tvar found bool\n\tr.word, r.line, found = bytes.Cut(line, []byte{' '})\n\tif !found {\n\t\tr.word, r.line, _ = bytes.Cut(line, []byte{'\\t'})\n\t}\n\treturn true\n}\n\nfunc (r *reader) Err() error {\n\treturn r.err\n}\n"
  },
  {
    "path": "internal/kallsyms/reader_test.go",
    "content": "package kallsyms\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestReader(t *testing.T) {\n\tb := []byte(`\none  two \t\tthree\n  four  \n\n  λέξη\t`)\n\n\tr := newReader(bytes.NewReader(b))\n\n\tqt.Assert(t, qt.IsTrue(r.Line()))\n\tqt.Assert(t, qt.IsTrue(r.Word()))\n\tqt.Assert(t, qt.Equals(r.Text(), \"one\"))\n\n\tqt.Assert(t, qt.IsTrue(r.Word()))\n\tqt.Assert(t, qt.Equals(r.Text(), \"two\"))\n\n\tqt.Assert(t, qt.IsTrue(r.Word()))\n\tqt.Assert(t, qt.Equals(r.Text(), \"three\"))\n\tqt.Assert(t, qt.IsFalse(r.Word()))\n\n\tqt.Assert(t, qt.IsTrue(r.Line()))\n\tqt.Assert(t, qt.IsTrue(r.Word()))\n\tqt.Assert(t, qt.Equals(r.Text(), \"four\"))\n\tqt.Assert(t, qt.IsFalse(r.Word()))\n\n\tqt.Assert(t, qt.IsTrue(r.Line()))\n\tqt.Assert(t, qt.IsTrue(r.Word()))\n\tqt.Assert(t, qt.Equals(r.Text(), \"λέξη\"))\n\tqt.Assert(t, qt.IsFalse(r.Word()))\n\n\tqt.Assert(t, qt.IsFalse(r.Line()))\n\tqt.Assert(t, qt.IsNil(r.Err()))\n}\n"
  },
  {
    "path": "internal/kconfig/kconfig.go",
    "content": "// Package kconfig implements a parser for the format of Linux's .config file.\npackage kconfig\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/internal\"\n)\n\n// Parse parses the kconfig file for which a reader is given.\n// All the CONFIG_* which are in filter and which are set set will be\n// put in the returned map as key with their corresponding value as map value.\n// If filter is nil, no filtering will occur.\n// If the kconfig file is not valid, error will be returned.\nfunc Parse(source io.ReaderAt, filter map[string]struct{}) (map[string]string, error) {\n\tvar r io.Reader\n\tzr, err := gzip.NewReader(io.NewSectionReader(source, 0, math.MaxInt64))\n\tif err != nil {\n\t\tr = io.NewSectionReader(source, 0, math.MaxInt64)\n\t} else {\n\t\t// Source is gzip compressed, transparently decompress.\n\t\tr = zr\n\t}\n\n\tret := make(map[string]string, len(filter))\n\n\ts := bufio.NewScanner(r)\n\n\tfor s.Scan() {\n\t\tline := s.Bytes()\n\t\terr = processKconfigLine(line, ret, filter)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"cannot parse line: %w\", err)\n\t\t}\n\n\t\tif filter != nil && len(ret) == len(filter) {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif err := s.Err(); err != nil {\n\t\treturn nil, fmt.Errorf(\"cannot parse: %w\", err)\n\t}\n\n\tif zr != nil {\n\t\treturn ret, zr.Close()\n\t}\n\n\treturn ret, nil\n}\n\n// Golang translation of libbpf bpf_object__process_kconfig_line():\n// https://github.com/libbpf/libbpf/blob/fbd60dbff51c870f5e80a17c4f2fd639eb80af90/src/libbpf.c#L1874\n// It does the same checks but does not put the data inside the BPF map.\nfunc processKconfigLine(line []byte, m map[string]string, filter map[string]struct{}) error {\n\t// Ignore empty lines and \"# CONFIG_* is not set\".\n\tif !bytes.HasPrefix(line, []byte(\"CONFIG_\")) {\n\t\treturn nil\n\t}\n\n\tkey, value, found := bytes.Cut(line, []byte{'='})\n\tif !found {\n\t\treturn fmt.Errorf(\"line %q does not contain separator '='\", line)\n\t}\n\n\tif len(value) == 0 {\n\t\treturn fmt.Errorf(\"line %q has no value\", line)\n\t}\n\n\tif filter != nil {\n\t\t// NB: map[string(key)] gets special optimisation help from the compiler\n\t\t// and doesn't allocate. Don't turn this into a variable.\n\t\t_, ok := filter[string(key)]\n\t\tif !ok {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\t// This can seem odd, but libbpf only sets the value the first time the key is\n\t// met:\n\t// https://github.com/torvalds/linux/blob/0d85b27b0cc6/tools/lib/bpf/libbpf.c#L1906-L1908\n\t_, ok := m[string(key)]\n\tif !ok {\n\t\tm[string(key)] = string(value)\n\t}\n\n\treturn nil\n}\n\n// PutValue translates the value given as parameter depending on the BTF\n// type, the translated value is then written to the byte array.\nfunc PutValue(data []byte, typ btf.Type, value string) error {\n\ttyp = btf.UnderlyingType(typ)\n\n\tswitch value {\n\tcase \"y\", \"n\", \"m\":\n\t\treturn putValueTri(data, typ, value)\n\t}\n\n\tif strings.HasPrefix(value, `\"`) {\n\t\treturn putValueString(data, typ, value)\n\t}\n\n\treturn putValueNumber(data, typ, value)\n}\n\n// Golang translation of libbpf_tristate enum:\n// https://github.com/libbpf/libbpf/blob/fbd60dbff51c870f5e80a17c4f2fd639eb80af90/src/bpf_helpers.h#L169\ntype triState int\n\nconst (\n\tTriNo     triState = 0\n\tTriYes    triState = 1\n\tTriModule triState = 2\n)\n\nfunc putValueTri(data []byte, typ btf.Type, value string) error {\n\tswitch v := typ.(type) {\n\tcase *btf.Int:\n\t\tif v.Encoding != btf.Bool {\n\t\t\treturn fmt.Errorf(\"cannot add tri value, expected btf.Bool, got: %v\", v.Encoding)\n\t\t}\n\n\t\tif v.Size != 1 {\n\t\t\treturn fmt.Errorf(\"cannot add tri value, expected size of 1 byte, got: %d\", v.Size)\n\t\t}\n\n\t\tswitch value {\n\t\tcase \"y\":\n\t\t\tdata[0] = 1\n\t\tcase \"n\":\n\t\t\tdata[0] = 0\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"cannot use %q for btf.Bool\", value)\n\t\t}\n\tcase *btf.Enum:\n\t\tif v.Name != \"libbpf_tristate\" {\n\t\t\treturn fmt.Errorf(\"cannot use enum %q, only libbpf_tristate is supported\", v.Name)\n\t\t}\n\n\t\tif len(data) != 4 {\n\t\t\treturn fmt.Errorf(\"expected enum value to occupy 4 bytes in datasec, got: %d\", len(data))\n\t\t}\n\n\t\tvar tri triState\n\t\tswitch value {\n\t\tcase \"y\":\n\t\t\ttri = TriYes\n\t\tcase \"m\":\n\t\t\ttri = TriModule\n\t\tcase \"n\":\n\t\t\ttri = TriNo\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"value %q is not supported for libbpf_tristate\", value)\n\t\t}\n\n\t\tinternal.NativeEndian.PutUint32(data, uint32(tri))\n\tdefault:\n\t\treturn fmt.Errorf(\"cannot add number value, expected btf.Int or btf.Enum, got: %T\", v)\n\t}\n\n\treturn nil\n}\n\nfunc putValueString(data []byte, typ btf.Type, value string) error {\n\tarray, ok := typ.(*btf.Array)\n\tif !ok {\n\t\treturn fmt.Errorf(\"cannot add string value, expected btf.Array, got %T\", array)\n\t}\n\n\tcontentType, ok := btf.UnderlyingType(array.Type).(*btf.Int)\n\tif !ok {\n\t\treturn fmt.Errorf(\"cannot add string value, expected array of btf.Int, got %T\", contentType)\n\t}\n\n\t// Any Int, which is not bool, of one byte could be used to store char:\n\t// https://github.com/torvalds/linux/blob/1a5304fecee5/tools/lib/bpf/libbpf.c#L3637-L3638\n\tif contentType.Size != 1 && contentType.Encoding != btf.Bool {\n\t\treturn fmt.Errorf(\"cannot add string value, expected array of btf.Int of size 1, got array of btf.Int of size: %v\", contentType.Size)\n\t}\n\n\tif !strings.HasPrefix(value, `\"`) || !strings.HasSuffix(value, `\"`) {\n\t\treturn fmt.Errorf(`value %q must start and finish with '\"'`, value)\n\t}\n\n\tstr := strings.Trim(value, `\"`)\n\n\t// We need to trim string if the bpf array is smaller.\n\tif uint32(len(str)) >= array.Nelems {\n\t\tstr = str[:array.Nelems]\n\t}\n\n\t// Write the string content to .kconfig.\n\tcopy(data, str)\n\n\treturn nil\n}\n\nfunc putValueNumber(data []byte, typ btf.Type, value string) error {\n\tinteger, ok := typ.(*btf.Int)\n\tif !ok {\n\t\treturn fmt.Errorf(\"cannot add number value, expected *btf.Int, got: %T\", integer)\n\t}\n\n\tsize := integer.Size\n\tsizeInBits := size * 8\n\n\tvar n uint64\n\tvar err error\n\tif integer.Encoding == btf.Signed {\n\t\tparsed, e := strconv.ParseInt(value, 0, int(sizeInBits))\n\n\t\tn = uint64(parsed)\n\t\terr = e\n\t} else {\n\t\tparsed, e := strconv.ParseUint(value, 0, int(sizeInBits))\n\n\t\tn = uint64(parsed)\n\t\terr = e\n\t}\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"cannot parse value: %w\", err)\n\t}\n\n\treturn PutInteger(data, integer, n)\n}\n\n// PutInteger writes n into data.\n//\n// integer determines how much is written into data and what the valid values\n// are.\nfunc PutInteger(data []byte, integer *btf.Int, n uint64) error {\n\t// This function should match set_kcfg_value_num in libbpf.\n\tif integer.Encoding == btf.Bool && n > 1 {\n\t\treturn fmt.Errorf(\"invalid boolean value: %d\", n)\n\t}\n\n\tif len(data) < int(integer.Size) {\n\t\treturn fmt.Errorf(\"can't fit an integer of size %d into a byte slice of length %d\", integer.Size, len(data))\n\t}\n\n\tswitch integer.Size {\n\tcase 1:\n\t\tif integer.Encoding == btf.Signed && (int64(n) > math.MaxInt8 || int64(n) < math.MinInt8) {\n\t\t\treturn fmt.Errorf(\"can't represent %d as a signed integer of size %d\", int64(n), integer.Size)\n\t\t}\n\t\tdata[0] = byte(n)\n\tcase 2:\n\t\tif integer.Encoding == btf.Signed && (int64(n) > math.MaxInt16 || int64(n) < math.MinInt16) {\n\t\t\treturn fmt.Errorf(\"can't represent %d as a signed integer of size %d\", int64(n), integer.Size)\n\t\t}\n\t\tinternal.NativeEndian.PutUint16(data, uint16(n))\n\tcase 4:\n\t\tif integer.Encoding == btf.Signed && (int64(n) > math.MaxInt32 || int64(n) < math.MinInt32) {\n\t\t\treturn fmt.Errorf(\"can't represent %d as a signed integer of size %d\", int64(n), integer.Size)\n\t\t}\n\t\tinternal.NativeEndian.PutUint32(data, uint32(n))\n\tcase 8:\n\t\tinternal.NativeEndian.PutUint64(data, uint64(n))\n\tdefault:\n\t\treturn fmt.Errorf(\"size (%d) is not valid, expected: 1, 2, 4 or 8\", integer.Size)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "internal/kconfig/kconfig_test.go",
    "content": "package kconfig\n\nimport (\n\t\"encoding/binary\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/internal\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc BenchmarkParse(b *testing.B) {\n\tf, err := os.Open(\"testdata/config-6.2.15-300.fc38.x86_64.gz\")\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\tdefer f.Close()\n\n\tb.ReportAllocs()\n\n\tfor b.Loop() {\n\t\t_, err := Parse(f, nil)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkParseFiltered(b *testing.B) {\n\tf, err := os.Open(\"testdata/config-6.2.15-300.fc38.x86_64.gz\")\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\tdefer f.Close()\n\n\tb.ReportAllocs()\n\n\t// CONFIG_ARCH_USE_MEMTEST is the last CONFIG_ in the file.\n\t// So, we will easily be able to see how many allocated bytes the filtering\n\t// permits reducing compared to unfiltered benchmark.\n\tfilter := map[string]struct{}{\"CONFIG_ARCH_USE_MEMTEST\": {}}\n\n\tfor b.Loop() {\n\t\t_, err := Parse(f, filter)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc TestParse(t *testing.T) {\n\tt.Parallel()\n\n\tf, err := os.Open(\"testdata/test.kconfig\")\n\tif err != nil {\n\t\tt.Fatal(\"Error reading /testdata/test.kconfig: \", err)\n\t}\n\tdefer f.Close()\n\n\tconfig, err := Parse(f, nil)\n\tif err != nil {\n\t\tt.Fatal(\"Error parsing kconfig: \", err)\n\t}\n\n\texpected := map[string]string{\n\t\t\"CONFIG_TRISTATE\": \"m\",\n\t\t\"CONFIG_BOOL\":     \"y\",\n\t\t\"CONFIG_CHAR\":     \"100\",\n\t\t\"CONFIG_USHORT\":   \"30000\",\n\t\t\"CONFIG_INT\":      \"123456\",\n\t\t\"CONFIG_ULONG\":    \"0xDEADBEEFC0DE\",\n\t\t\"CONFIG_STR\":      `\"abracad\"`,\n\t\t\"CONFIG_FOO\":      `\"foo\"`,\n\t}\n\tqt.Assert(t, qt.DeepEquals(config, expected))\n}\n\nfunc TestParseFiltered(t *testing.T) {\n\tt.Parallel()\n\n\tf, err := os.Open(\"testdata/test.kconfig\")\n\tif err != nil {\n\t\tt.Fatal(\"Error reading /testdata/test.kconfig: \", err)\n\t}\n\tdefer f.Close()\n\n\tfilter := map[string]struct{}{\"CONFIG_FOO\": {}}\n\n\tconfig, err := Parse(f, filter)\n\tif err != nil {\n\t\tt.Fatal(\"Error parsing gzipped kconfig: \", err)\n\t}\n\n\texpected := map[string]string{\"CONFIG_FOO\": `\"foo\"`}\n\tqt.Assert(t, qt.DeepEquals(config, expected))\n}\n\nfunc TestParseGzipped(t *testing.T) {\n\tt.Parallel()\n\n\tf, err := os.Open(\"testdata/config-6.2.15-300.fc38.x86_64.gz\")\n\tif err != nil {\n\t\tt.Fatal(\"Error reading /testdata/config-6.2.15-300.fc38.x86_64.gz: \", err)\n\t}\n\tdefer f.Close()\n\n\t_, err = Parse(f, nil)\n\tif err != nil {\n\t\tt.Fatal(\"Error parsing gzipped kconfig: \", err)\n\t}\n}\n\nfunc TestParseGzippedFiltered(t *testing.T) {\n\tt.Parallel()\n\n\tf, err := os.Open(\"testdata/config-6.2.15-300.fc38.x86_64.gz\")\n\tif err != nil {\n\t\tt.Fatal(\"Error reading /testdata/config-6.2.15-300.fc38.x86_64.gz: \", err)\n\t}\n\tdefer f.Close()\n\n\tfilter := map[string]struct{}{\"CONFIG_HZ\": {}}\n\n\tconfig, err := Parse(f, filter)\n\tif err != nil {\n\t\tt.Fatal(\"Error parsing gzipped kconfig: \", err)\n\t}\n\n\texpected := map[string]string{\"CONFIG_HZ\": \"1000\"}\n\tqt.Assert(t, qt.DeepEquals(config, expected))\n}\n\nfunc TestProcessKconfigBadLine(t *testing.T) {\n\tt.Parallel()\n\n\tm := make(map[string]string)\n\n\terr := processKconfigLine([]byte(\"CONFIG_FOO\"), m, nil)\n\tqt.Assert(t, qt.IsNotNil(err), qt.Commentf(\"line has no '='\"))\n\n\terr = processKconfigLine([]byte(\"CONFIG_FOO=\"), m, nil)\n\tqt.Assert(t, qt.IsNotNil(err), qt.Commentf(\"line has no value\"))\n}\n\nfunc TestPutValue(t *testing.T) {\n\tt.Parallel()\n\n\ttype testCase struct {\n\t\ttyp      btf.Type\n\t\tvalue    string\n\t\texpected any\n\t\tcomment  string\n\t}\n\n\tcases := []testCase{\n\t\t{\n\t\t\ttyp: &btf.Int{\n\t\t\t\tSize:     1,\n\t\t\t\tEncoding: btf.Bool,\n\t\t\t},\n\t\t\tvalue:    \"n\",\n\t\t\texpected: int8(0),\n\t\t},\n\t\t{\n\t\t\ttyp: &btf.Int{\n\t\t\t\tSize:     1,\n\t\t\t\tEncoding: btf.Bool,\n\t\t\t},\n\t\t\tvalue:    \"y\",\n\t\t\texpected: int8(1),\n\t\t},\n\t\t{\n\t\t\ttyp: &btf.Int{\n\t\t\t\tSize:     1,\n\t\t\t\tEncoding: btf.Bool,\n\t\t\t},\n\t\t\tvalue:   \"foo\",\n\t\t\tcomment: \"Bad value\",\n\t\t},\n\t\t{\n\t\t\ttyp:     &btf.Int{},\n\t\t\tcomment: \"Encoding is not Bool\",\n\t\t},\n\t\t{\n\t\t\ttyp: &btf.Int{\n\t\t\t\tEncoding: btf.Bool,\n\t\t\t},\n\t\t\tcomment: \"Size is not 1\",\n\t\t},\n\t\t{\n\t\t\ttyp: &btf.Enum{\n\t\t\t\tName: \"libbpf_tristate\",\n\t\t\t},\n\t\t\tvalue:    \"y\",\n\t\t\texpected: int32(TriYes),\n\t\t},\n\t\t{\n\t\t\ttyp: &btf.Enum{\n\t\t\t\tName: \"libbpf_tristate\",\n\t\t\t},\n\t\t\tvalue:    \"n\",\n\t\t\texpected: int32(TriNo),\n\t\t},\n\t\t{\n\t\t\ttyp: &btf.Enum{\n\t\t\t\tName: \"libbpf_tristate\",\n\t\t\t},\n\t\t\tvalue:    \"m\",\n\t\t\texpected: int32(TriModule),\n\t\t},\n\t\t{\n\t\t\ttyp: &btf.Enum{\n\t\t\t\tName: \"libbpf_tristate\",\n\t\t\t},\n\t\t\tvalue:   \"foo\",\n\t\t\tcomment: \"Bad value\",\n\t\t},\n\t\t{\n\t\t\ttyp: &btf.Enum{\n\t\t\t\tName: \"error\",\n\t\t\t},\n\t\t\tcomment: \"Enum name is wrong\",\n\t\t},\n\t\t{\n\t\t\ttyp:     &btf.Array{},\n\t\t\tvalue:   \"y\",\n\t\t\tcomment: \"Type is not btf.Int\",\n\t\t},\n\t\t{\n\t\t\ttyp: &btf.Int{\n\t\t\t\tSize: 1,\n\t\t\t},\n\t\t\tvalue:    \"255\",\n\t\t\texpected: uint8(255),\n\t\t},\n\t\t{\n\t\t\ttyp: &btf.Int{\n\t\t\t\tSize: 2,\n\t\t\t},\n\t\t\tvalue:    \"0xcafe\",\n\t\t\texpected: uint16(0xcafe),\n\t\t},\n\t\t{\n\t\t\ttyp: &btf.Int{\n\t\t\t\tSize: 2,\n\t\t\t},\n\t\t\tvalue:    \"0755\",\n\t\t\texpected: uint16(0755),\n\t\t},\n\t\t{\n\t\t\ttyp: &btf.Int{\n\t\t\t\tSize:     4,\n\t\t\t\tEncoding: btf.Signed,\n\t\t\t},\n\t\t\tvalue:    \"-2147483648\",\n\t\t\texpected: int32(-2147483648),\n\t\t},\n\t\t{\n\t\t\ttyp: &btf.Int{\n\t\t\t\tSize:     4,\n\t\t\t\tEncoding: btf.Signed,\n\t\t\t},\n\t\t\tvalue:    \"+2147483647\",\n\t\t\texpected: int32(+2147483647),\n\t\t},\n\t\t{\n\t\t\ttyp: &btf.Int{\n\t\t\t\tSize: 4,\n\t\t\t},\n\t\t\tvalue:    \"0xcafec0de\",\n\t\t\texpected: uint32(0xcafec0de),\n\t\t},\n\t\t{\n\t\t\ttyp: &btf.Int{\n\t\t\t\tSize:     8,\n\t\t\t\tEncoding: btf.Signed,\n\t\t\t},\n\t\t\tvalue:    \"+1000000000000\",\n\t\t\texpected: int64(1000000000000),\n\t\t},\n\t\t{\n\t\t\ttyp: &btf.Int{\n\t\t\t\tSize: 8,\n\t\t\t},\n\t\t\tvalue:    \"1000000000000\",\n\t\t\texpected: uint64(1000000000000),\n\t\t},\n\t\t{\n\t\t\ttyp: &btf.Int{\n\t\t\t\tSize: 1,\n\t\t\t},\n\t\t\tvalue:   \"foo\",\n\t\t\tcomment: \"Value is not an int\",\n\t\t},\n\t\t{\n\t\t\ttyp:     &btf.Array{},\n\t\t\tvalue:   \"1\",\n\t\t\tcomment: \"Type is not btf.Int\",\n\t\t},\n\t\t{\n\t\t\ttyp: &btf.Int{\n\t\t\t\tSize: 16,\n\t\t\t},\n\t\t\tvalue:   \"1\",\n\t\t\tcomment: \"Size is wrong\",\n\t\t},\n\t\t{\n\t\t\ttyp: &btf.Typedef{\n\t\t\t\tType: &btf.Int{\n\t\t\t\t\tSize: 1,\n\t\t\t\t},\n\t\t\t},\n\t\t\tvalue:    \"1\",\n\t\t\texpected: uint8(1),\n\t\t},\n\t\t{\n\t\t\ttyp: &btf.Array{\n\t\t\t\tType: &btf.Int{\n\t\t\t\t\tSize:     1,\n\t\t\t\t\tEncoding: btf.Char,\n\t\t\t\t},\n\t\t\t\tNelems: 6,\n\t\t\t},\n\t\t\tvalue:    `\"foobar\"`,\n\t\t\texpected: []byte(\"foobar\"),\n\t\t},\n\t\t{\n\t\t\ttyp: &btf.Array{\n\t\t\t\tType: &btf.Int{\n\t\t\t\t\tSize:     1,\n\t\t\t\t\tEncoding: btf.Unsigned,\n\t\t\t\t},\n\t\t\t\tNelems: 3,\n\t\t\t},\n\t\t\tvalue:    `\"foobar\"`,\n\t\t\texpected: []byte(\"foo\"),\n\t\t},\n\t\t{\n\t\t\ttyp: &btf.Array{\n\t\t\t\tType: &btf.Int{\n\t\t\t\t\tSize:     1,\n\t\t\t\t\tEncoding: btf.Signed,\n\t\t\t\t},\n\t\t\t\tNelems: 2,\n\t\t\t},\n\t\t\tvalue:    `\"42\"`,\n\t\t\texpected: []byte(\"42\"),\n\t\t},\n\t\t{\n\t\t\ttyp:     &btf.Int{},\n\t\t\tvalue:   `\"foo\"`,\n\t\t\tcomment: \"Type is not btf.Array\",\n\t\t},\n\t\t{\n\t\t\ttyp:     &btf.Array{},\n\t\t\tvalue:   `\"foo\"`,\n\t\t\tcomment: \"Type is not btf.Array of btf.Int\",\n\t\t},\n\t\t{\n\t\t\ttyp: &btf.Array{\n\t\t\t\tType: &btf.Int{\n\t\t\t\t\tSize:     1,\n\t\t\t\t\tEncoding: btf.Bool,\n\t\t\t\t},\n\t\t\t},\n\t\t\tcomment: \"Type is not btf.Array of btf.Int of size 1 which is not btf.Bool\",\n\t\t},\n\t\t{\n\t\t\ttyp: &btf.Array{\n\t\t\t\tType: &btf.Int{\n\t\t\t\t\tSize:     4,\n\t\t\t\t\tEncoding: btf.Char,\n\t\t\t\t},\n\t\t\t},\n\t\t\tvalue:   `\"foo\"`,\n\t\t\tcomment: \"Type is not btf.Array of btf.Char of size 1\",\n\t\t},\n\t\t{\n\t\t\ttyp: &btf.Array{\n\t\t\t\tType: &btf.Int{\n\t\t\t\t\tSize:     1,\n\t\t\t\t\tEncoding: btf.Char,\n\t\t\t\t},\n\t\t\t},\n\t\t\tvalue:   `\"foo`,\n\t\t\tcomment: `Value does not start and end with '\"'`,\n\t\t},\n\t}\n\n\tfor _, c := range cases {\n\t\tif len(c.comment) > 0 {\n\t\t\terr := PutValue(make([]byte, 0), c.typ, c.value)\n\n\t\t\tqt.Assert(t, qt.IsNotNil(err), qt.Commentf(c.comment))\n\n\t\t\tcontinue\n\t\t}\n\n\t\texpected, err := binary.Append(nil, internal.NativeEndian, c.expected)\n\t\tqt.Assert(t, qt.IsNil(err))\n\n\t\tdata := make([]byte, len(expected))\n\t\terr = PutValue(data, c.typ, c.value)\n\n\t\tqt.Assert(t, qt.IsNil(err))\n\n\t\tqt.Assert(t, qt.DeepEquals(data, expected))\n\t}\n}\n\nfunc TestPutInteger(t *testing.T) {\n\tt.Parallel()\n\n\ttype testCase struct {\n\t\texpected []byte\n\t\tinteger  *btf.Int\n\t\tn        uint64\n\t\terr      bool\n\t\tcomment  string\n\t}\n\n\tcases := []testCase{\n\t\t{\n\t\t\tinteger:  &btf.Int{Size: 1, Encoding: btf.Unsigned},\n\t\t\tn:        0x01,\n\t\t\texpected: []byte{0x01},\n\t\t},\n\t\t{\n\t\t\tinteger:  &btf.Int{Size: 2, Encoding: btf.Unsigned},\n\t\t\tn:        0x902a,\n\t\t\texpected: []byte{0x2a, 0x90},\n\t\t},\n\t\t{\n\t\t\tinteger:  &btf.Int{Size: 4, Encoding: btf.Unsigned},\n\t\t\tn:        0x01234567,\n\t\t\texpected: []byte{0x67, 0x45, 0x23, 0x01},\n\t\t},\n\t\t{\n\t\t\tinteger: &btf.Int{Size: 1, Encoding: btf.Signed},\n\t\t\tn:       0x80,\n\t\t\terr:     true,\n\t\t\tcomment: \"outside of range int8 -128 ~ 127\",\n\t\t},\n\t\t{\n\t\t\tinteger: &btf.Int{Size: 2, Encoding: btf.Signed},\n\t\t\tn:       0xabcdabcd,\n\t\t\terr:     true,\n\t\t\tcomment: \"outside of range int16 -32768 ~ 32767\",\n\t\t},\n\t\t{\n\t\t\tinteger: &btf.Int{Size: 4, Encoding: btf.Signed},\n\t\t\tn:       0x1234567890,\n\t\t\terr:     true,\n\t\t\tcomment: \"outside of range int32 -2147483648 ~ 2147483647\",\n\t\t},\n\t\t{\n\t\t\tinteger:  &btf.Int{Size: 2, Encoding: btf.Signed},\n\t\t\tn:        0xffffffffffffffff,\n\t\t\texpected: []byte{0xff, 0xff, 0x00, 0x00},\n\t\t\tcomment:  \"n means -1\",\n\t\t},\n\t\t{\n\t\t\tinteger: &btf.Int{Size: 2, Encoding: btf.Signed},\n\t\t\tn:       0xffffffffffffffff - 0x8000,\n\t\t\terr:     true,\n\t\t\tcomment: \"n means -32768(-MinInt16) - 1 in signed value\",\n\t\t},\n\t\t{\n\t\t\tinteger:  &btf.Int{Size: 2, Encoding: btf.Signed},\n\t\t\tn:        0x7fff,\n\t\t\texpected: []byte{0xff, 0x7f},\n\t\t\tcomment:  \"maximum value of int16\",\n\t\t},\n\t\t{\n\t\t\tinteger:  &btf.Int{Size: 2, Encoding: btf.Unsigned},\n\t\t\tn:        0xffff,\n\t\t\texpected: []byte{0xff, 0xff},\n\t\t},\n\t\t{\n\t\t\tinteger:  &btf.Int{Size: 4, Encoding: btf.Unsigned},\n\t\t\tn:        0xffffffff,\n\t\t\texpected: []byte{0xff, 0xff, 0xff, 0xff},\n\t\t},\n\t\t{\n\t\t\tinteger: &btf.Int{Size: 4, Encoding: btf.Signed},\n\t\t\tn:       0x80000000,\n\t\t\terr:     true,\n\t\t\tcomment: \"outside of range int32 ~2147483648 ~ 2147483647\",\n\t\t},\n\t\t{\n\t\t\tinteger: &btf.Int{Size: 4, Encoding: btf.Signed},\n\t\t\tn:       0xffffffffffffffff - 0x80000000,\n\t\t\terr:     true,\n\t\t\tcomment: \"outside of range int32 ~2147483648 ~ 2147483647\",\n\t\t},\n\t\t{\n\t\t\tinteger:  &btf.Int{Size: 8, Encoding: btf.Unsigned},\n\t\t\tn:        0xffffffffffffffff,\n\t\t\texpected: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},\n\t\t},\n\t}\n\n\tfor _, c := range cases {\n\t\tdata := make([]byte, len(c.expected))\n\t\terr := PutInteger(data, c.integer, c.n)\n\n\t\tif c.err {\n\t\t\tqt.Assert(t, qt.IsNotNil(err))\n\t\t\tcontinue\n\t\t}\n\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.DeepEquals(data, c.expected), qt.Commentf(c.comment))\n\t}\n}\n\nfunc TestPutIntegerError(t *testing.T) {\n\tqt.Assert(t, qt.IsNotNil(PutInteger(nil, &btf.Int{Size: 2}, 0)), qt.Commentf(\"slice too small for int\"))\n\tqt.Assert(t, qt.IsNotNil(PutInteger(nil, &btf.Int{Encoding: btf.Bool}, 2)), qt.Commentf(\"n too big for bool\"))\n}\n"
  },
  {
    "path": "internal/kconfig/testdata/test.kconfig",
    "content": "CONFIG_TRISTATE=m\n# CONFIG_IS_NOT_SET is not set\nCONFIG_BOOL=y\nCONFIG_CHAR=100\n\nCONFIG_USHORT=30000\nCONFIG_INT=123456\nCONFIG_ULONG=0xDEADBEEFC0DE\nCONFIG_STR=\"abracad\"\nCONFIG_FOO=\"foo\"\nCONFIG_FOO=\"bar\"\n"
  },
  {
    "path": "internal/linux/auxv.go",
    "content": "package linux\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/platform\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\ntype auxvPairReader interface {\n\tClose() error\n\tReadAuxvPair() (uint64, uint64, error)\n}\n\n// See https://elixir.bootlin.com/linux/v6.5.5/source/include/uapi/linux/auxvec.h\nconst (\n\t_AT_NULL         = 0  // End of vector\n\t_AT_SYSINFO_EHDR = 33 // Offset to vDSO blob in process image\n)\n\ntype auxvRuntimeReader struct {\n\tdata  [][2]uintptr\n\tindex int\n}\n\nfunc (r *auxvRuntimeReader) Close() error {\n\treturn nil\n}\n\nfunc (r *auxvRuntimeReader) ReadAuxvPair() (uint64, uint64, error) {\n\tif r.index >= len(r.data)+2 {\n\t\treturn 0, 0, io.EOF\n\t}\n\n\t// we manually add the (_AT_NULL, _AT_NULL) pair at the end\n\t// that is not provided by the go runtime\n\tvar tag, value uintptr\n\tif r.index < len(r.data) {\n\t\ttag, value = r.data[r.index][0], r.data[r.index][1]\n\t} else {\n\t\ttag, value = _AT_NULL, _AT_NULL\n\t}\n\tr.index += 1\n\treturn uint64(tag), uint64(value), nil\n}\n\nfunc newAuxvRuntimeReader() (auxvPairReader, error) {\n\tif !platform.IsLinux {\n\t\treturn nil, fmt.Errorf(\"read auxv from runtime: %w\", internal.ErrNotSupportedOnOS)\n\t}\n\n\tdata, err := unix.Auxv()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read auxv from runtime: %w\", err)\n\t}\n\n\treturn &auxvRuntimeReader{\n\t\tdata:  data,\n\t\tindex: 0,\n\t}, nil\n}\n"
  },
  {
    "path": "internal/linux/auxv_test.go",
    "content": "package linux\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n\t\"unsafe\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\ntype auxvFileReader struct {\n\tfile            *os.File\n\torder           binary.ByteOrder\n\tuintptrIs32bits bool\n}\n\nfunc (r *auxvFileReader) Close() error {\n\treturn r.file.Close()\n}\n\ntype auxvPair32 struct {\n\tTag, Value uint32\n}\n\ntype auxvPair64 struct {\n\tTag, Value uint64\n}\n\nfunc (r *auxvFileReader) ReadAuxvPair() (tag, value uint64, _ error) {\n\tif r.uintptrIs32bits {\n\t\tvar aux auxvPair32\n\t\tif err := binary.Read(r.file, r.order, &aux); err != nil {\n\t\t\treturn 0, 0, fmt.Errorf(\"reading auxv entry: %w\", err)\n\t\t}\n\t\treturn uint64(aux.Tag), uint64(aux.Value), nil\n\t}\n\n\tvar aux auxvPair64\n\tif err := binary.Read(r.file, r.order, &aux); err != nil {\n\t\treturn 0, 0, fmt.Errorf(\"reading auxv entry: %w\", err)\n\t}\n\treturn aux.Tag, aux.Value, nil\n}\n\nfunc newAuxFileReader(path string, order binary.ByteOrder, uintptrIs32bits bool) (auxvPairReader, error) {\n\t// Read data from the auxiliary vector, which is normally passed directly\n\t// to the process. Go does not expose that data before go 1.21, so we must read it from procfs.\n\t// https://man7.org/linux/man-pages/man3/getauxval.3.html\n\tav, err := os.Open(path)\n\tif errors.Is(err, unix.EACCES) {\n\t\treturn nil, fmt.Errorf(\"opening auxv: %w (process may not be dumpable due to file capabilities)\", err)\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"opening auxv: %w\", err)\n\t}\n\n\treturn &auxvFileReader{\n\t\tfile:            av,\n\t\torder:           order,\n\t\tuintptrIs32bits: uintptrIs32bits,\n\t}, nil\n}\n\nfunc newDefaultAuxvFileReader() (auxvPairReader, error) {\n\tconst uintptrIs32bits = unsafe.Sizeof((uintptr)(0)) == 4\n\treturn newAuxFileReader(\"/proc/self/auxv\", internal.NativeEndian, uintptrIs32bits)\n}\n\nfunc TestAuxvBothSourcesEqual(t *testing.T) {\n\truntimeBased, err := newAuxvRuntimeReader()\n\tskipIfNotSupportedOnOS(t, err)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tfileBased, err := newDefaultAuxvFileReader()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tfor {\n\t\truntimeTag, runtimeValue, err := runtimeBased.ReadAuxvPair()\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tfileTag, fileValue, err := fileBased.ReadAuxvPair()\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tif runtimeTag != fileTag {\n\t\t\tt.Errorf(\"mismatching tags: runtime=%v, file=%v\", runtimeTag, fileTag)\n\t\t}\n\n\t\tif runtimeValue != fileValue {\n\t\t\tt.Errorf(\"mismatching values: runtime=%v, file=%v\", runtimeValue, fileValue)\n\t\t}\n\n\t\tif runtimeTag == _AT_NULL {\n\t\t\tbreak\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "internal/linux/cpu.go",
    "content": "package linux\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n)\n\nfunc ParseCPUsFromFile(path string) (int, error) {\n\tspec, err := os.ReadFile(path)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tn, err := parseCPUs(string(spec))\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"can't parse %s: %v\", path, err)\n\t}\n\n\treturn n, nil\n}\n\n// parseCPUs parses the number of cpus from a string produced\n// by bitmap_list_string() in the Linux kernel.\n// Multiple ranges are rejected, since they can't be unified\n// into a single number.\n// This is the format of /sys/devices/system/cpu/possible, it\n// is not suitable for /sys/devices/system/cpu/online, etc.\nfunc parseCPUs(spec string) (int, error) {\n\tif strings.Trim(spec, \"\\n\") == \"0\" {\n\t\treturn 1, nil\n\t}\n\n\tvar low, high int\n\tn, err := fmt.Sscanf(spec, \"%d-%d\\n\", &low, &high)\n\tif n != 2 || err != nil {\n\t\treturn 0, fmt.Errorf(\"invalid format: %s\", spec)\n\t}\n\tif low != 0 {\n\t\treturn 0, fmt.Errorf(\"CPU spec doesn't start at zero: %s\", spec)\n\t}\n\n\t// cpus is 0 indexed\n\treturn high + 1, nil\n}\n"
  },
  {
    "path": "internal/linux/cpu_test.go",
    "content": "package linux\n\nimport (\n\t\"testing\"\n)\n\nfunc TestParseCPUs(t *testing.T) {\n\tfor str, result := range map[string]int{\n\t\t\"0-1\":   2,\n\t\t\"0-2\\n\": 3,\n\t\t\"0\":     1,\n\t} {\n\t\tn, err := parseCPUs(str)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Can't parse `%s`: %v\", str, err)\n\t\t} else if n != result {\n\t\t\tt.Error(\"Parsing\", str, \"returns\", n, \"instead of\", result)\n\t\t}\n\t}\n\n\tfor _, str := range []string{\n\t\t\"0,3-4\",\n\t\t\"0-\",\n\t\t\"1,\",\n\t\t\"\",\n\t} {\n\t\t_, err := parseCPUs(str)\n\t\tif err == nil {\n\t\t\tt.Error(\"Parsed invalid format:\", str)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "internal/linux/doc.go",
    "content": "// Package linux contains OS specific wrappers around package unix.\npackage linux\n"
  },
  {
    "path": "internal/linux/helper_test.go",
    "content": "package linux\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf/internal\"\n)\n\n// skipIfNotSupportedOnOS is a copy of testutils.SkipIfNotSupported to avoid\n// a circular dependency.\nfunc skipIfNotSupportedOnOS(tb testing.TB, err error) {\n\ttb.Helper()\n\n\tif err == internal.ErrNotSupportedOnOS {\n\t\ttb.Fatal(\"Unwrapped ErrNotSupportedOnOS\")\n\t}\n\n\tif errors.Is(err, internal.ErrNotSupportedOnOS) {\n\t\ttb.Skip(err.Error())\n\t}\n}\n"
  },
  {
    "path": "internal/linux/kconfig.go",
    "content": "package linux\n\nimport (\n\t\"fmt\"\n\t\"os\"\n)\n\n// FindKConfig searches for a kconfig file on the host.\n//\n// It first reads from /boot/config- of the current running kernel and tries\n// /proc/config.gz if nothing was found in /boot.\n// If none of the file provide a kconfig, it returns an error.\nfunc FindKConfig() (*os.File, error) {\n\tkernelRelease, err := KernelRelease()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"cannot get kernel release: %w\", err)\n\t}\n\n\tpath := \"/boot/config-\" + kernelRelease\n\tf, err := os.Open(path)\n\tif err == nil {\n\t\treturn f, nil\n\t}\n\n\tf, err = os.Open(\"/proc/config.gz\")\n\tif err == nil {\n\t\treturn f, nil\n\t}\n\n\treturn nil, fmt.Errorf(\"neither %s nor /proc/config.gz provide a kconfig\", path)\n}\n"
  },
  {
    "path": "internal/linux/platform.go",
    "content": "package linux\n\nimport (\n\t\"runtime\"\n)\n\n// PlatformPrefix returns the platform-dependent syscall wrapper prefix used by\n// the linux kernel.\n//\n// Based on https://github.com/golang/go/blob/master/src/go/build/syslist.go\n// and https://github.com/libbpf/libbpf/blob/master/src/libbpf.c#L10047\nfunc PlatformPrefix() string {\n\tswitch runtime.GOARCH {\n\tcase \"386\":\n\t\treturn \"__ia32_\"\n\tcase \"amd64\", \"amd64p32\":\n\t\treturn \"__x64_\"\n\n\tcase \"arm\", \"armbe\":\n\t\treturn \"__arm_\"\n\tcase \"arm64\", \"arm64be\":\n\t\treturn \"__arm64_\"\n\n\tcase \"mips\", \"mipsle\", \"mips64\", \"mips64le\", \"mips64p32\", \"mips64p32le\":\n\t\treturn \"__mips_\"\n\n\tcase \"s390\":\n\t\treturn \"__s390_\"\n\tcase \"s390x\":\n\t\treturn \"__s390x_\"\n\n\tcase \"riscv\", \"riscv64\":\n\t\treturn \"__riscv_\"\n\n\tcase \"ppc\":\n\t\treturn \"__powerpc_\"\n\tcase \"ppc64\", \"ppc64le\":\n\t\treturn \"__powerpc64_\"\n\n\tdefault:\n\t\treturn \"\"\n\t}\n}\n"
  },
  {
    "path": "internal/linux/statfs.go",
    "content": "package linux\n\nimport (\n\t\"unsafe\"\n\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\nfunc FSType(path string) (int64, error) {\n\tvar statfs unix.Statfs_t\n\tif err := unix.Statfs(path, &statfs); err != nil {\n\t\treturn 0, err\n\t}\n\n\tfsType := int64(statfs.Type)\n\tif unsafe.Sizeof(statfs.Type) == 4 {\n\t\t// We're on a 32 bit arch, where statfs.Type is int32. bpfFSType is a\n\t\t// negative number when interpreted as int32 so we need to cast via\n\t\t// uint32 to avoid sign extension.\n\t\tfsType = int64(uint32(statfs.Type))\n\t}\n\treturn fsType, nil\n}\n"
  },
  {
    "path": "internal/linux/statfs_test.go",
    "content": "package linux\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf/internal/unix\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestFSType(t *testing.T) {\n\tfor _, fs := range []struct {\n\t\tpath  string\n\t\tmagic int64\n\t}{\n\t\t{\"/sys/kernel/tracing\", unix.TRACEFS_MAGIC},\n\t\t{\"/sys/fs/bpf\", unix.BPF_FS_MAGIC},\n\t} {\n\t\tfst, err := FSType(fs.path)\n\t\tskipIfNotSupportedOnOS(t, err)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.Equals(fst, fs.magic))\n\t}\n}\n"
  },
  {
    "path": "internal/linux/vdso.go",
    "content": "package linux\n\nimport (\n\t\"debug/elf\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"os\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\nvar (\n\terrAuxvNoVDSO = errors.New(\"no vdso address found in auxv\")\n)\n\n// vdsoVersion returns the LINUX_VERSION_CODE embedded in the vDSO library\n// linked into the current process image.\nfunc vdsoVersion() (uint32, error) {\n\tav, err := newAuxvRuntimeReader()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tdefer av.Close()\n\n\tvdsoAddr, err := vdsoMemoryAddress(av)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"finding vDSO memory address: %w\", err)\n\t}\n\n\t// Use /proc/self/mem rather than unsafe.Pointer tricks.\n\tmem, err := os.Open(\"/proc/self/mem\")\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"opening mem: %w\", err)\n\t}\n\tdefer mem.Close()\n\n\t// Open ELF at provided memory address, as offset into /proc/self/mem.\n\tc, err := vdsoLinuxVersionCode(io.NewSectionReader(mem, int64(vdsoAddr), math.MaxInt64))\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"reading linux version code: %w\", err)\n\t}\n\n\treturn c, nil\n}\n\n// vdsoMemoryAddress returns the memory address of the vDSO library\n// linked into the current process image. r is an io.Reader into an auxv blob.\nfunc vdsoMemoryAddress(r auxvPairReader) (uintptr, error) {\n\t// Loop through all tag/value pairs in auxv until we find `AT_SYSINFO_EHDR`,\n\t// the address of a page containing the virtual Dynamic Shared Object (vDSO).\n\tfor {\n\t\ttag, value, err := r.ReadAuxvPair()\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\n\t\tswitch tag {\n\t\tcase _AT_SYSINFO_EHDR:\n\t\t\tif value != 0 {\n\t\t\t\treturn uintptr(value), nil\n\t\t\t}\n\t\t\treturn 0, fmt.Errorf(\"invalid vDSO address in auxv\")\n\t\t// _AT_NULL is always the last tag/val pair in the aux vector\n\t\t// and can be treated like EOF.\n\t\tcase _AT_NULL:\n\t\t\treturn 0, errAuxvNoVDSO\n\t\t}\n\t}\n}\n\n// format described at https://www.man7.org/linux/man-pages/man5/elf.5.html in section 'Notes (Nhdr)'\ntype elfNoteHeader struct {\n\tNameSize int32\n\tDescSize int32\n\tType     int32\n}\n\n// vdsoLinuxVersionCode returns the LINUX_VERSION_CODE embedded in\n// the ELF notes section of the binary provided by the reader.\nfunc vdsoLinuxVersionCode(r io.ReaderAt) (uint32, error) {\n\thdr, err := internal.NewSafeELFFile(r)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"reading vDSO ELF: %w\", err)\n\t}\n\n\tsections := hdr.SectionsByType(elf.SHT_NOTE)\n\tif len(sections) == 0 {\n\t\treturn 0, fmt.Errorf(\"no note section found in vDSO ELF\")\n\t}\n\n\tfor _, sec := range sections {\n\t\tsr := sec.Open()\n\t\tvar n elfNoteHeader\n\n\t\t// Read notes until we find one named 'Linux'.\n\t\tfor {\n\t\t\tif err := binary.Read(sr, hdr.ByteOrder, &n); err != nil {\n\t\t\t\tif errors.Is(err, io.EOF) {\n\t\t\t\t\t// We looked at all the notes in this section\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\treturn 0, fmt.Errorf(\"reading note header: %w\", err)\n\t\t\t}\n\n\t\t\t// If a note name is defined, it follows the note header.\n\t\t\tvar name string\n\t\t\tif n.NameSize > 0 {\n\t\t\t\t// Read the note name, aligned to 4 bytes.\n\t\t\t\tbuf := make([]byte, internal.Align(n.NameSize, 4))\n\t\t\t\tif err := binary.Read(sr, hdr.ByteOrder, &buf); err != nil {\n\t\t\t\t\treturn 0, fmt.Errorf(\"reading note name: %w\", err)\n\t\t\t\t}\n\n\t\t\t\t// Read nul-terminated string.\n\t\t\t\tname = unix.ByteSliceToString(buf[:n.NameSize])\n\t\t\t}\n\n\t\t\t// If a note descriptor is defined, it follows the name.\n\t\t\t// It is possible for a note to have a descriptor but not a name.\n\t\t\tif n.DescSize > 0 {\n\t\t\t\t// LINUX_VERSION_CODE is a uint32 value.\n\t\t\t\tif name == \"Linux\" && n.DescSize == 4 && n.Type == 0 {\n\t\t\t\t\tvar version uint32\n\t\t\t\t\tif err := binary.Read(sr, hdr.ByteOrder, &version); err != nil {\n\t\t\t\t\t\treturn 0, fmt.Errorf(\"reading note descriptor: %w\", err)\n\t\t\t\t\t}\n\t\t\t\t\treturn version, nil\n\t\t\t\t}\n\n\t\t\t\t// Discard the note descriptor if it exists but we're not interested in it.\n\t\t\t\tif _, err := io.CopyN(io.Discard, sr, int64(internal.Align(n.DescSize, 4))); err != nil {\n\t\t\t\t\treturn 0, err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn 0, fmt.Errorf(\"no Linux note in ELF\")\n}\n"
  },
  {
    "path": "internal/linux/vdso_test.go",
    "content": "package linux\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestAuxvVDSOMemoryAddress(t *testing.T) {\n\tfor _, testcase := range []struct {\n\t\tsource  string\n\t\tis32bit bool\n\t\taddress uint64\n\t}{\n\t\t{\"auxv64le.bin\", false, 0x7ffd377e5000},\n\t\t{\"auxv32le.bin\", true, 0xb7fc3000},\n\t} {\n\t\tt.Run(testcase.source, func(t *testing.T) {\n\t\t\tav, err := newAuxFileReader(\"testdata/\"+testcase.source, binary.LittleEndian, testcase.is32bit)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tt.Cleanup(func() { av.Close() })\n\n\t\t\taddr, err := vdsoMemoryAddress(av)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\n\t\t\tif uint64(addr) != testcase.address {\n\t\t\t\tt.Errorf(\"Expected vDSO memory address %x, got %x\", testcase.address, addr)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAuxvNoVDSO(t *testing.T) {\n\t// Copy of auxv.bin with the vDSO pointer removed.\n\tav, err := newAuxFileReader(\"testdata/auxv64le_no_vdso.bin\", binary.LittleEndian, false)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tt.Cleanup(func() { av.Close() })\n\n\t_, err = vdsoMemoryAddress(av)\n\tif want, got := errAuxvNoVDSO, err; !errors.Is(got, want) {\n\t\tt.Fatalf(\"expected error '%v', got: %v\", want, got)\n\t}\n}\n\nfunc TestVDSOVersion(t *testing.T) {\n\t_, err := vdsoVersion()\n\tskipIfNotSupportedOnOS(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n}\n\nfunc TestLinuxVersionCodeEmbedded(t *testing.T) {\n\ttests := []struct {\n\t\tfile    string\n\t\tversion uint32\n\t}{\n\t\t{\n\t\t\t\"testdata/vdso.bin\",\n\t\t\tuint32(328828), // 5.4.124\n\t\t},\n\t\t{\n\t\t\t\"testdata/vdso_multiple_notes.bin\",\n\t\t\tuint32(328875), // Container Optimized OS v85 with a 5.4.x kernel\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.file, func(t *testing.T) {\n\t\t\tvdso, err := os.Open(test.file)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tdefer vdso.Close()\n\n\t\t\tvc, err := vdsoLinuxVersionCode(vdso)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\n\t\t\tif vc != test.version {\n\t\t\t\tt.Errorf(\"Expected version code %d, got %d\", test.version, vc)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/linux/version.go",
    "content": "package linux\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\n// KernelVersion returns the version of the currently running kernel.\nvar KernelVersion = sync.OnceValues(detectKernelVersion)\n\n// detectKernelVersion returns the version of the running kernel.\nfunc detectKernelVersion() (internal.Version, error) {\n\tvc, err := vdsoVersion()\n\tif err != nil {\n\t\treturn internal.Version{}, err\n\t}\n\treturn internal.NewVersionFromCode(vc), nil\n}\n\n// KernelRelease returns the release string of the running kernel.\n// Its format depends on the Linux distribution and corresponds to directory\n// names in /lib/modules by convention. Some examples are 5.15.17-1-lts and\n// 4.19.0-16-amd64.\nfunc KernelRelease() (string, error) {\n\tvar uname unix.Utsname\n\tif err := unix.Uname(&uname); err != nil {\n\t\treturn \"\", fmt.Errorf(\"uname failed: %w\", err)\n\t}\n\n\treturn unix.ByteSliceToString(uname.Release[:]), nil\n}\n"
  },
  {
    "path": "internal/linux/version_test.go",
    "content": "package linux\n\nimport (\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestCurrentKernelVersion(t *testing.T) {\n\t_, err := KernelVersion()\n\tskipIfNotSupportedOnOS(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n}\n\nfunc TestKernelRelease(t *testing.T) {\n\tr, err := KernelRelease()\n\tskipIfNotSupportedOnOS(t, err)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif r == \"\" {\n\t\tt.Fatal(\"unexpected empty kernel release\")\n\t}\n}\n"
  },
  {
    "path": "internal/math.go",
    "content": "package internal\n\n// Align returns 'n' updated to 'alignment' boundary.\nfunc Align[I Integer](n, alignment I) I {\n\treturn (n + alignment - 1) / alignment * alignment\n}\n\n// IsPow returns true if n is a power of two.\nfunc IsPow[I Integer](n I) bool {\n\treturn n != 0 && (n&(n-1)) == 0\n}\n\n// Between returns the value clamped between a and b.\nfunc Between[I Integer](val, a, b I) I {\n\tlower, upper := a, b\n\tif lower > upper {\n\t\tupper, lower = a, b\n\t}\n\n\tval = min(val, upper)\n\treturn max(val, lower)\n}\n\n// Integer represents all possible integer types.\n// Remove when x/exp/constraints is moved to the standard library.\ntype Integer interface {\n\t~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr\n}\n\n// List of integer types known by the Go compiler. Used by TestIntegerConstraint\n// to warn if a new integer type is introduced. Remove when x/exp/constraints\n// is moved to the standard library.\nvar integers = []string{\"int\", \"int8\", \"int16\", \"int32\", \"int64\", \"uint\", \"uint8\", \"uint16\", \"uint32\", \"uint64\", \"uintptr\"}\n"
  },
  {
    "path": "internal/math_test.go",
    "content": "package internal\n\nimport (\n\t\"fmt\"\n\t\"go/importer\"\n\t\"regexp\"\n\t\"slices\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestPow(t *testing.T) {\n\ttests := []struct {\n\t\tn int\n\t\tr bool\n\t}{\n\t\t{0, false},\n\t\t{1, true},\n\t\t{2, true},\n\t\t{3, false},\n\t\t{4, true},\n\t\t{5, false},\n\t\t{8, true},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(fmt.Sprintf(\"%d\", tt.n), func(t *testing.T) {\n\t\t\tif want, got := tt.r, IsPow(tt.n); want != got {\n\t\t\t\tt.Errorf(\"unexpected result for n %d; want: %v, got: %v\", tt.n, want, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestIntegerConstraint(t *testing.T) {\n\trgx := regexp.MustCompile(`^(u)?int([0-9]*|ptr)?$`)\n\n\tpkg, err := importer.Default().Import(\"reflect\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tfor _, name := range pkg.Scope().Names() {\n\t\tname = strings.ToLower(name)\n\t\tif !rgx.MatchString(name) {\n\t\t\tcontinue\n\t\t}\n\n\t\tif !slices.Contains(integers, name) {\n\t\t\tt.Errorf(\"Go type %s is not in the list of known integer types\", name)\n\t\t}\n\t}\n}\n\nfunc TestBetween(t *testing.T) {\n\ttests := []struct {\n\t\tval, a, b, r int\n\t}{\n\t\t{0, 1, 2, 1},\n\t\t{1, 1, 2, 1},\n\t\t{2, 1, 2, 2},\n\t\t{3, 1, 2, 2},\n\t\t{4, 10, 5, 5},\n\t\t{11, 10, 5, 10},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(fmt.Sprintf(\"%d between %d and %d\", tt.val, tt.a, tt.b), func(t *testing.T) {\n\t\t\tqt.Assert(t, qt.Equals(Between(tt.val, tt.a, tt.b), tt.r))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/nil.go",
    "content": "package internal\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n)\n\n// IsNil returns an error if i is a nil pointer or a nil interface. Otherwise,\n// it returns nil.\nfunc IsNil(i any) error {\n\tv := reflect.ValueOf(i)\n\tswitch v.Kind() {\n\tcase reflect.Invalid:\n\t\treturn fmt.Errorf(\"nil interface\")\n\tcase reflect.Pointer:\n\t\tif v.IsNil() {\n\t\t\treturn fmt.Errorf(\"nil %T\", i)\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "internal/output.go",
    "content": "package internal\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"go/format\"\n\t\"go/scanner\"\n\t\"io\"\n\t\"reflect\"\n\t\"strings\"\n\t\"unicode\"\n)\n\n// Identifier turns a C style type or field name into an exportable Go equivalent.\nfunc Identifier(str string) string {\n\tprev := rune(-1)\n\treturn strings.Map(func(r rune) rune {\n\t\t// See https://golang.org/ref/spec#Identifiers\n\t\tswitch {\n\t\tcase unicode.IsLetter(r):\n\t\t\tif prev == -1 {\n\t\t\t\tr = unicode.ToUpper(r)\n\t\t\t}\n\n\t\tcase r == '_':\n\t\t\tswitch {\n\t\t\t// The previous rune was deleted, or we are at the\n\t\t\t// beginning of the string.\n\t\t\tcase prev == -1:\n\t\t\t\tfallthrough\n\n\t\t\t// The previous rune is a lower case letter or a digit.\n\t\t\tcase unicode.IsDigit(prev) || (unicode.IsLetter(prev) && unicode.IsLower(prev)):\n\t\t\t\t// delete the current rune, and force the\n\t\t\t\t// next character to be uppercased.\n\t\t\t\tr = -1\n\t\t\t}\n\n\t\tcase unicode.IsDigit(r):\n\n\t\tdefault:\n\t\t\t// Delete the current rune. prev is unchanged.\n\t\t\treturn -1\n\t\t}\n\n\t\tprev = r\n\t\treturn r\n\t}, str)\n}\n\n// WriteFormatted outputs a formatted src into out.\n//\n// If formatting fails it returns an informative error message.\nfunc WriteFormatted(src []byte, out io.Writer) error {\n\tformatted, err := format.Source(src)\n\tif err == nil {\n\t\t_, err = out.Write(formatted)\n\t\treturn err\n\t}\n\n\tvar el scanner.ErrorList\n\tif !errors.As(err, &el) {\n\t\treturn err\n\t}\n\n\tvar nel scanner.ErrorList\n\tfor _, err := range el {\n\t\tif !err.Pos.IsValid() {\n\t\t\tnel = append(nel, err)\n\t\t\tcontinue\n\t\t}\n\n\t\tbuf := src[err.Pos.Offset:]\n\t\tnl := bytes.IndexRune(buf, '\\n')\n\t\tif nl == -1 {\n\t\t\tnel = append(nel, err)\n\t\t\tcontinue\n\t\t}\n\n\t\terr.Msg += \": \" + string(buf[:nl])\n\t\tnel = append(nel, err)\n\t}\n\n\treturn nel\n}\n\n// GoTypeName is like %T, but elides the package name.\n//\n// Pointers to a type are peeled off.\nfunc GoTypeName(t any) string {\n\trT := reflect.TypeOf(t)\n\tfor rT.Kind() == reflect.Pointer {\n\t\trT = rT.Elem()\n\t}\n\n\tname := rT.Name()\n\tif pkgPath := rT.PkgPath(); pkgPath != \"\" {\n\t\tname = strings.ReplaceAll(name, pkgPath+\".\", \"\")\n\t}\n\n\treturn name\n}\n"
  },
  {
    "path": "internal/output_test.go",
    "content": "package internal\n\nimport (\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestIdentifier(t *testing.T) {\n\ttestcases := []struct {\n\t\tin, out string\n\t}{\n\t\t{\".rodata\", \"Rodata\"},\n\t\t{\"_foo_bar_\", \"FooBar\"},\n\t\t{\"ipv6_test\", \"Ipv6Test\"},\n\t\t{\"FOO_BAR\", \"FOO_BAR\"},\n\t\t{\"FOO_\", \"FOO_\"},\n\t\t{\"FOO__BAR\", \"FOO__BAR\"},\n\t\t{\"FOO___BAR\", \"FOO___BAR\"},\n\t\t{\"_FOO__BAR\", \"FOO__BAR\"},\n\t\t{\"__FOO__BAR\", \"FOO__BAR\"},\n\t}\n\n\tfor _, tc := range testcases {\n\t\thave := Identifier(tc.in)\n\t\tif have != tc.out {\n\t\t\tt.Errorf(\"Expected %q as output of %q, got %q\", tc.out, tc.in, have)\n\t\t}\n\t}\n}\n\ntype foo struct{}\n\nfunc TestGoTypeName(t *testing.T) {\n\ttype bar[T any] struct{}\n\tqt.Assert(t, qt.Equals(GoTypeName(foo{}), \"foo\"))\n\tqt.Assert(t, qt.Equals(GoTypeName(new(foo)), \"foo\"))\n\tqt.Assert(t, qt.Equals(GoTypeName(new(*foo)), \"foo\"))\n\tqt.Assert(t, qt.Equals(GoTypeName(bar[int]{}), \"bar[int]\"))\n\tqt.Assert(t, qt.Equals(GoTypeName(bar[foo]{}), \"bar[foo]\"))\n\tqt.Assert(t, qt.Equals(GoTypeName(bar[testing.T]{}), \"bar[testing.T]\"))\n}\n"
  },
  {
    "path": "internal/platform/constants.go",
    "content": "package platform\n\nimport \"fmt\"\n\n// Values used to tag platform specific constants.\n//\n// The value for Linux is zero so that existing constants do not change.\nconst (\n\tLinuxTag = uint32(iota) << platformShift\n\tWindowsTag\n)\n\nconst (\n\tplatformMax   = 1<<3 - 1 // most not exceed 3 bits to avoid setting the high bit\n\tplatformShift = 28\n\tplatformMask  = platformMax << platformShift\n)\n\nfunc tagForPlatform(platform string) (uint32, error) {\n\tswitch platform {\n\tcase Linux:\n\t\treturn LinuxTag, nil\n\tcase Windows:\n\t\treturn WindowsTag, nil\n\tdefault:\n\t\treturn 0, fmt.Errorf(\"unrecognized platform: %s\", platform)\n\t}\n}\n\nfunc platformForConstant(c uint32) string {\n\ttag := uint32(c & platformMask)\n\tswitch tag {\n\tcase LinuxTag:\n\t\treturn Linux\n\tcase WindowsTag:\n\t\treturn Windows\n\tdefault:\n\t\treturn \"\"\n\t}\n}\n\n// Encode a platform and a value into a tagged constant.\n//\n// Returns an error if platform is unknown or c is out of bounds.\nfunc EncodeConstant[T ~uint32](platform string, c uint32) (T, error) {\n\tif c>>platformShift > 0 {\n\t\treturn 0, fmt.Errorf(\"invalid constant 0x%x\", c)\n\t}\n\n\ttag, err := tagForPlatform(platform)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn T(tag | c), nil\n}\n\n// Decode a platform and a value from a tagged constant.\nfunc DecodeConstant[T ~uint32](c T) (string, uint32) {\n\tv := uint32(c) & ^uint32(platformMask)\n\treturn platformForConstant(uint32(c)), v\n}\n"
  },
  {
    "path": "internal/platform/constants_test.go",
    "content": "package platform\n\nimport (\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestConstant(t *testing.T) {\n\tconst maxConstant = uint32(1<<platformShift - 1)\n\tfor _, plat := range []string{\n\t\tLinux,\n\t\tWindows,\n\t} {\n\t\tt.Run(plat, func(t *testing.T) {\n\t\t\tc, err := EncodeConstant[uint32](plat, maxConstant)\n\t\t\tqt.Assert(t, qt.IsNil(err))\n\t\t\tgotPlat, gotValue := DecodeConstant(c)\n\t\t\tqt.Assert(t, qt.Equals(gotPlat, plat))\n\t\t\tqt.Assert(t, qt.Equals(gotValue, maxConstant))\n\n\t\t\t_, err = EncodeConstant[uint32](plat, maxConstant+1)\n\t\t\tqt.Assert(t, qt.IsNotNil(err))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/platform/platform.go",
    "content": "package platform\n\nimport (\n\t\"errors\"\n\t\"runtime\"\n\t\"strings\"\n)\n\nconst (\n\tLinux   = \"linux\"\n\tWindows = \"windows\"\n)\n\nconst (\n\tIsLinux   = runtime.GOOS == \"linux\"\n\tIsWindows = runtime.GOOS == \"windows\"\n)\n\n// SelectVersion extracts the platform-appropriate version from a list of strings like\n// `linux:6.1` or `windows:0.20.0`.\n//\n// Returns an empty string and nil if no version matched or an error if no strings were passed.\nfunc SelectVersion(versions []string) (string, error) {\n\tconst prefix = runtime.GOOS + \":\"\n\n\tif len(versions) == 0 {\n\t\treturn \"\", errors.New(\"no versions specified\")\n\t}\n\n\tfor _, version := range versions {\n\t\tif after, ok := strings.CutPrefix(version, prefix); ok {\n\t\t\treturn after, nil\n\t\t}\n\n\t\tif IsLinux && !strings.ContainsRune(version, ':') {\n\t\t\t// Allow version numbers without a GOOS prefix on Linux.\n\t\t\treturn version, nil\n\t\t}\n\t}\n\n\treturn \"\", nil\n}\n"
  },
  {
    "path": "internal/platform/platform_linux.go",
    "content": "package platform\n\nconst Native = Linux\n"
  },
  {
    "path": "internal/platform/platform_other.go",
    "content": "//go:build !linux && !windows\n\npackage platform\n\nconst Native = \"\"\n"
  },
  {
    "path": "internal/platform/platform_windows.go",
    "content": "package platform\n\nconst Native = Windows\n"
  },
  {
    "path": "internal/prog.go",
    "content": "package internal\n\n// EmptyBPFContext is the smallest-possible BPF input context to be used for\n// invoking `Program.{Run,Benchmark,Test}`.\n//\n// Programs require a context input buffer of at least 15 bytes. Looking in\n// net/bpf/test_run.c, bpf_test_init() requires that the input is at least\n// ETH_HLEN (14) bytes. As of Linux commit fd18942 (\"bpf: Don't redirect packets\n// with invalid pkt_len\"), it also requires the skb to be non-empty after\n// removing the Layer 2 header.\nvar EmptyBPFContext = make([]byte, 15)\n"
  },
  {
    "path": "internal/sys/doc.go",
    "content": "// Package sys contains bindings for the BPF syscall.\npackage sys\n\n// Regenerate types.go by invoking go generate in the current directory.\n\n//go:generate go tool gentypes ../../btf/testdata/vmlinux.btf.gz\n"
  },
  {
    "path": "internal/sys/fd.go",
    "content": "package sys\n\nimport (\n\t\"math\"\n\t\"runtime\"\n\t\"strconv\"\n\n\t\"github.com/cilium/ebpf/internal/testutils/testmain\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\nvar ErrClosedFd = unix.EBADF\n\n// A value for an invalid fd.\n//\n// Luckily this is consistent across Linux and Windows.\n//\n// See https://github.com/microsoft/ebpf-for-windows/blob/54632eb360c560ebef2f173be1a4a4625d540744/include/ebpf_api.h#L25\nconst invalidFd = -1\n\nfunc newFD(value int) *FD {\n\ttestmain.TraceFD(value, 1)\n\n\tfd := &FD{raw: value}\n\tfd.cleanup = runtime.AddCleanup(fd, func(raw int) {\n\t\ttestmain.LeakFD(raw)\n\t\t_ = unix.Close(raw)\n\t}, fd.raw)\n\treturn fd\n}\n\nfunc (fd *FD) String() string {\n\treturn strconv.FormatInt(int64(fd.raw), 10)\n}\n\nfunc (fd *FD) Int() int {\n\treturn int(fd.raw)\n}\n\nfunc (fd *FD) Uint() uint32 {\n\tif fd.raw == invalidFd {\n\t\t// Best effort: this is the number most likely to be an invalid file\n\t\t// descriptor. It is equal to -1 (on two's complement arches).\n\t\treturn math.MaxUint32\n\t}\n\treturn uint32(fd.raw)\n}\n\n// Disown destroys the FD and returns its raw file descriptor without closing\n// it. After this call, the underlying fd is no longer tied to the FD's\n// lifecycle.\nfunc (fd *FD) Disown() int {\n\tvalue := fd.raw\n\ttestmain.ForgetFD(value)\n\tfd.raw = invalidFd\n\n\tfd.cleanup.Stop()\n\treturn value\n}\n"
  },
  {
    "path": "internal/sys/fd_linux_test.go",
    "content": "package sys\n\nimport (\n\t\"errors\"\n\t\"os\"\n\t\"syscall\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\nfunc init() {\n\t// Free up fd 0 for TestFD.\n\tstdin, err := unix.FcntlInt(os.Stdin.Fd(), unix.F_DUPFD_CLOEXEC, 1)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\told := os.Stdin\n\tos.Stdin = os.NewFile(uintptr(stdin), \"stdin\")\n\told.Close()\n\n\treserveFdZero()\n}\n\nfunc reserveFdZero() {\n\tfd, err := unix.Open(os.DevNull, syscall.O_RDONLY, 0)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tif fd != 0 {\n\t\tpanic(errors.New(\"zero fd already taken\"))\n\t}\n}\n\nfunc TestFD(t *testing.T) {\n\t_, err := NewFD(-1)\n\tqt.Assert(t, qt.IsNotNil(err), qt.Commentf(\"invalid fd should be rejected\"))\n\n\tfd, err := NewFD(0)\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.Not(qt.Equals(fd.Int(), 0)), qt.Commentf(\"fd value should not be zero\"))\n\tqt.Assert(t, qt.IsNil(fd.Close()))\n\n\tvar stat unix.Stat_t\n\terr = unix.Fstat(0, &stat)\n\tqt.Assert(t, qt.ErrorIs(err, unix.EBADF), qt.Commentf(\"zero fd should be closed\"))\n\n\treserveFdZero()\n}\n\nfunc TestFDFile(t *testing.T) {\n\tfd := newFD(openFd(t))\n\tfile, err := fd.File(\"test\")\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.IsNotNil(file))\n\n\tqt.Assert(t, qt.IsNil(file.Close()))\n\n\t_, err = fd.File(\"closed\")\n\tqt.Assert(t, qt.ErrorIs(err, ErrClosedFd))\n\n\t_, err = fd.Dup()\n\tqt.Assert(t, qt.ErrorIs(err, ErrClosedFd))\n}\n\nfunc openFd(tb testing.TB) int {\n\tfd, err := unix.Open(os.DevNull, syscall.O_RDONLY, 0)\n\tqt.Assert(tb, qt.IsNil(err))\n\treturn fd\n}\n"
  },
  {
    "path": "internal/sys/fd_other.go",
    "content": "//go:build !windows\n\npackage sys\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"runtime\"\n\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\ntype FD struct {\n\traw     int\n\tcleanup runtime.Cleanup\n}\n\n// NewFD wraps a raw fd with a finalizer.\n//\n// You must not use the raw fd after calling this function, since the underlying\n// file descriptor number may change. This is because the BPF UAPI assumes that\n// zero is not a valid fd value.\nfunc NewFD(value int) (*FD, error) {\n\tif value < 0 {\n\t\treturn nil, fmt.Errorf(\"invalid fd %d\", value)\n\t}\n\n\tfd := newFD(value)\n\tif value != 0 {\n\t\treturn fd, nil\n\t}\n\n\tdup, err := fd.Dup()\n\t_ = fd.Close()\n\treturn dup, err\n}\n\nfunc (fd *FD) Close() error {\n\tif fd.raw < 0 {\n\t\treturn nil\n\t}\n\n\treturn unix.Close(fd.Disown())\n}\n\nfunc (fd *FD) Dup() (*FD, error) {\n\tif fd.raw < 0 {\n\t\treturn nil, ErrClosedFd\n\t}\n\n\t// Always require the fd to be larger than zero: the BPF API treats the value\n\t// as \"no argument provided\".\n\tdup, err := unix.FcntlInt(uintptr(fd.raw), unix.F_DUPFD_CLOEXEC, 1)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't dup fd: %v\", err)\n\t}\n\n\treturn newFD(dup), nil\n}\n\n// File takes ownership of FD and turns it into an [*os.File].\n//\n// You must not use the FD after the call returns.\n//\n// Returns [ErrClosedFd] if the fd is not valid.\nfunc (fd *FD) File(name string) (*os.File, error) {\n\tif fd.raw == invalidFd {\n\t\treturn nil, ErrClosedFd\n\t}\n\n\treturn os.NewFile(uintptr(fd.Disown()), name), nil\n}\n"
  },
  {
    "path": "internal/sys/fd_windows.go",
    "content": "package sys\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"runtime\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/efw\"\n)\n\n// FD wraps a handle which is managed by the eBPF for Windows runtime.\n//\n// It is not equivalent to a real file descriptor or handle.\ntype FD struct {\n\traw     int\n\tcleanup runtime.Cleanup\n}\n\n// NewFD wraps a raw fd with a finalizer.\n//\n// You must not use the raw fd after calling this function.\nfunc NewFD(value int) (*FD, error) {\n\tif value == invalidFd {\n\t\treturn nil, fmt.Errorf(\"invalid fd %d\", value)\n\t}\n\n\tif value == 0 {\n\t\t// The efW runtime never uses zero fd it seems. No need to dup it.\n\t\treturn nil, fmt.Errorf(\"invalid zero fd\")\n\t}\n\n\treturn newFD(value), nil\n}\n\nfunc (fd *FD) Close() error {\n\tif fd.raw == invalidFd {\n\t\treturn nil\n\t}\n\n\treturn efw.EbpfCloseFd(fd.Disown())\n}\n\nfunc (fd *FD) Dup() (*FD, error) {\n\tif fd.raw == invalidFd {\n\t\treturn nil, ErrClosedFd\n\t}\n\n\tdup, err := efw.EbpfDuplicateFd(fd.raw)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn NewFD(int(dup))\n}\n\n// File is not implemented.\nfunc (fd *FD) File(name string) (*os.File, error) {\n\treturn nil, fmt.Errorf(\"file from fd: %w\", internal.ErrNotSupportedOnOS)\n}\n"
  },
  {
    "path": "internal/sys/pinning_other.go",
    "content": "//go:build !windows\n\npackage sys\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\n\t\"github.com/cilium/ebpf/internal/linux\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\nfunc Pin(currentPath, newPath string, fd *FD) error {\n\tif newPath == \"\" {\n\t\treturn errors.New(\"given pinning path cannot be empty\")\n\t}\n\tif currentPath == newPath {\n\t\treturn nil\n\t}\n\n\tfsType, err := linux.FSType(filepath.Dir(newPath))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif fsType != unix.BPF_FS_MAGIC {\n\t\treturn fmt.Errorf(\"%s is not on a bpf filesystem\", newPath)\n\t}\n\n\tdefer runtime.KeepAlive(fd)\n\n\tif currentPath == \"\" {\n\t\treturn ObjPin(&ObjPinAttr{\n\t\t\tPathname: NewStringPointer(newPath),\n\t\t\tBpfFd:    fd.Uint(),\n\t\t})\n\t}\n\n\t// Renameat2 is used instead of os.Rename to disallow the new path replacing\n\t// an existing path.\n\terr = unix.Renameat2(unix.AT_FDCWD, currentPath, unix.AT_FDCWD, newPath, unix.RENAME_NOREPLACE)\n\tif err == nil {\n\t\t// Object is now moved to the new pinning path.\n\t\treturn nil\n\t}\n\tif !os.IsNotExist(err) {\n\t\treturn fmt.Errorf(\"unable to move pinned object to new path %v: %w\", newPath, err)\n\t}\n\t// Internal state not in sync with the file system so let's fix it.\n\treturn ObjPin(&ObjPinAttr{\n\t\tPathname: NewStringPointer(newPath),\n\t\tBpfFd:    fd.Uint(),\n\t})\n}\n\nfunc Unpin(pinnedPath string) error {\n\tif pinnedPath == \"\" {\n\t\treturn nil\n\t}\n\terr := os.Remove(pinnedPath)\n\tif err == nil || os.IsNotExist(err) {\n\t\treturn nil\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "internal/sys/pinning_windows.go",
    "content": "package sys\n\nimport (\n\t\"errors\"\n\t\"runtime\"\n\n\t\"github.com/cilium/ebpf/internal/efw\"\n)\n\nfunc Pin(currentPath, newPath string, fd *FD) error {\n\tdefer runtime.KeepAlive(fd)\n\n\tif newPath == \"\" {\n\t\treturn errors.New(\"given pinning path cannot be empty\")\n\t}\n\tif currentPath == newPath {\n\t\treturn nil\n\t}\n\n\tif currentPath == \"\" {\n\t\treturn ObjPin(&ObjPinAttr{\n\t\t\tPathname: NewStringPointer(newPath),\n\t\t\tBpfFd:    fd.Uint(),\n\t\t})\n\t}\n\n\treturn ObjPin(&ObjPinAttr{\n\t\tPathname: NewStringPointer(newPath),\n\t\tBpfFd:    fd.Uint(),\n\t})\n}\n\nfunc Unpin(pinnedPath string) error {\n\tif pinnedPath == \"\" {\n\t\treturn nil\n\t}\n\n\terr := efw.EbpfObjectUnpin(pinnedPath)\n\tif err != nil && !errors.Is(err, efw.EBPF_KEY_NOT_FOUND) {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "internal/sys/ptr.go",
    "content": "package sys\n\nimport (\n\t\"unsafe\"\n\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\n// UnsafePointer creates a 64-bit pointer from an unsafe Pointer.\nfunc UnsafePointer(ptr unsafe.Pointer) Pointer {\n\treturn Pointer{ptr: ptr}\n}\n\n// UnsafeSlicePointer creates an untyped [Pointer] from a slice.\nfunc UnsafeSlicePointer[T comparable](buf []T) Pointer {\n\tif len(buf) == 0 {\n\t\treturn Pointer{}\n\t}\n\n\treturn Pointer{ptr: unsafe.Pointer(unsafe.SliceData(buf))}\n}\n\n// TypedPointer points to typed memory.\n//\n// It is like a *T except that it accounts for the BPF syscall interface.\ntype TypedPointer[T any] struct {\n\t_   [0]*T // prevent TypedPointer[a] to be convertible to TypedPointer[b]\n\tptr Pointer\n}\n\nfunc (p TypedPointer[T]) IsNil() bool {\n\treturn p.ptr.ptr == nil\n}\n\n// SlicePointer creates a [TypedPointer] from a slice.\nfunc SlicePointer[T comparable](s []T) TypedPointer[T] {\n\treturn TypedPointer[T]{ptr: UnsafeSlicePointer(s)}\n}\n\n// StringPointer points to a null-terminated string.\ntype StringPointer struct {\n\t_   [0]string\n\tptr Pointer\n}\n\n// NewStringPointer creates a [StringPointer] from a string.\nfunc NewStringPointer(str string) StringPointer {\n\tslice, err := unix.ByteSliceFromString(str)\n\tif err != nil {\n\t\treturn StringPointer{}\n\t}\n\n\treturn StringPointer{ptr: Pointer{ptr: unsafe.Pointer(&slice[0])}}\n}\n\n// StringSlicePointer points to a slice of [StringPointer].\ntype StringSlicePointer struct {\n\t_   [0][]string\n\tptr Pointer\n}\n\n// NewStringSlicePointer allocates an array of Pointers to each string in the\n// given slice of strings and returns a 64-bit pointer to the start of the\n// resulting array.\n//\n// Use this function to pass arrays of strings as syscall arguments.\nfunc NewStringSlicePointer(strings []string) StringSlicePointer {\n\tsp := make([]StringPointer, 0, len(strings))\n\tfor _, s := range strings {\n\t\tsp = append(sp, NewStringPointer(s))\n\t}\n\n\treturn StringSlicePointer{ptr: Pointer{ptr: unsafe.Pointer(&sp[0])}}\n}\n"
  },
  {
    "path": "internal/sys/ptr_32_be.go",
    "content": "//go:build armbe || mips || mips64p32\n\npackage sys\n\nimport (\n\t\"structs\"\n\t\"unsafe\"\n)\n\n// Pointer wraps an unsafe.Pointer to be 64bit to\n// conform to the syscall specification.\ntype Pointer struct {\n\tstructs.HostLayout\n\tpad uint32\n\tptr unsafe.Pointer\n}\n"
  },
  {
    "path": "internal/sys/ptr_32_le.go",
    "content": "//go:build 386 || amd64p32 || arm || mipsle || mips64p32le\n\npackage sys\n\nimport (\n\t\"structs\"\n\t\"unsafe\"\n)\n\n// Pointer wraps an unsafe.Pointer to be 64bit to\n// conform to the syscall specification.\ntype Pointer struct {\n\tstructs.HostLayout\n\tptr unsafe.Pointer\n\tpad uint32\n}\n"
  },
  {
    "path": "internal/sys/ptr_64.go",
    "content": "//go:build !386 && !amd64p32 && !arm && !mipsle && !mips64p32le && !armbe && !mips && !mips64p32\n\npackage sys\n\nimport (\n\t\"structs\"\n\t\"unsafe\"\n)\n\n// Pointer wraps an unsafe.Pointer to be 64bit to\n// conform to the syscall specification.\ntype Pointer struct {\n\tstructs.HostLayout\n\tptr unsafe.Pointer\n}\n"
  },
  {
    "path": "internal/sys/ptr_test.go",
    "content": "package sys\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"testing\"\n\t\"unsafe\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestTypedPointer(t *testing.T) {\n\tptrs := []any{\n\t\tTypedPointer[uint32]{},\n\t\tTypedPointer[complex128]{},\n\t\tStringPointer{},\n\t\tStringSlicePointer{},\n\t}\n\n\tfor i, a := range ptrs {\n\t\tqt.Assert(t, qt.Equals(unsafe.Alignof(a), unsafe.Alignof(unsafe.Pointer(nil))))\n\n\t\tfor _, b := range ptrs[i+1:] {\n\t\t\tt.Run(fmt.Sprintf(\"%T %T\", a, b), func(t *testing.T) {\n\t\t\t\ttypeOfA := reflect.TypeOf(a)\n\t\t\t\ttypeOfB := reflect.TypeOf(b)\n\t\t\t\tqt.Assert(t, qt.IsFalse(typeOfA.ConvertibleTo(typeOfB)))\n\t\t\t})\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "internal/sys/signals.go",
    "content": "//go:build !windows\n\npackage sys\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\t\"unsafe\"\n\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\n// A sigset containing only SIGPROF.\nvar profSet unix.Sigset_t\n\nfunc init() {\n\t// See sigsetAdd for details on the implementation. Open coded here so\n\t// that the compiler will check the constant calculations for us.\n\tprofSet.Val[sigprofBit/wordBits] |= 1 << (sigprofBit % wordBits)\n}\n\n// maskProfilerSignal locks the calling goroutine to its underlying OS thread\n// and adds SIGPROF to the thread's signal mask. This prevents pprof from\n// interrupting expensive syscalls like e.g. BPF_PROG_LOAD.\n//\n// The caller must defer unmaskProfilerSignal() to reverse the operation.\nfunc maskProfilerSignal() {\n\truntime.LockOSThread()\n\n\tif err := unix.PthreadSigmask(unix.SIG_BLOCK, &profSet, nil); err != nil {\n\t\truntime.UnlockOSThread()\n\t\tpanic(fmt.Errorf(\"masking profiler signal: %w\", err))\n\t}\n}\n\n// unmaskProfilerSignal removes SIGPROF from the underlying thread's signal\n// mask, allowing it to be interrupted for profiling once again.\n//\n// It also unlocks the current goroutine from its underlying OS thread.\nfunc unmaskProfilerSignal() {\n\tdefer runtime.UnlockOSThread()\n\n\tif err := unix.PthreadSigmask(unix.SIG_UNBLOCK, &profSet, nil); err != nil {\n\t\tpanic(fmt.Errorf(\"unmasking profiler signal: %w\", err))\n\t}\n}\n\nconst (\n\t// Signal is the nth bit in the bitfield.\n\tsigprofBit = int(unix.SIGPROF - 1)\n\t// The number of bits in one Sigset_t word.\n\twordBits = int(unsafe.Sizeof(unix.Sigset_t{}.Val[0])) * 8\n)\n\n// sigsetAdd adds signal to set.\n//\n// Note: Sigset_t.Val's value type is uint32 or uint64 depending on the arch.\n// This function must be able to deal with both and so must avoid any direct\n// references to u32 or u64 types.\nfunc sigsetAdd(set *unix.Sigset_t, signal unix.Signal) error {\n\tif signal < 1 {\n\t\treturn fmt.Errorf(\"signal %d must be larger than 0\", signal)\n\t}\n\n\t// For amd64, runtime.sigaddset() performs the following operation:\n\t// set[(signal-1)/32] |= 1 << ((uint32(signal) - 1) & 31)\n\t//\n\t// This trick depends on sigset being two u32's, causing a signal in the\n\t// bottom 31 bits to be written to the low word if bit 32 is low, or the high\n\t// word if bit 32 is high.\n\n\t// Signal is the nth bit in the bitfield.\n\tbit := int(signal - 1)\n\t// Word within the sigset the bit needs to be written to.\n\tword := bit / wordBits\n\n\tif word >= len(set.Val) {\n\t\treturn fmt.Errorf(\"signal %d does not fit within unix.Sigset_t\", signal)\n\t}\n\n\t// Write the signal bit into its corresponding word at the corrected offset.\n\tset.Val[word] |= 1 << (bit % wordBits)\n\n\treturn nil\n}\n"
  },
  {
    "path": "internal/sys/signals_test.go",
    "content": "//go:build linux\n\npackage sys\n\nimport (\n\t\"runtime\"\n\t\"testing\"\n\t\"unsafe\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\nfunc TestSigset(t *testing.T) {\n\tconst maxSignal = unix.Signal(unsafe.Sizeof(unix.Sigset_t{}) * 8)\n\n\t// Type-infer a sigset word. This is a typed uint of 32 or 64 bits depending\n\t// on the target architecture, so we can't use an untyped uint.\n\tzero := unix.Sigset_t{}.Val[0]\n\twords := len(unix.Sigset_t{}.Val)\n\n\tvar want, got unix.Sigset_t\n\t// Flip the first bit of the first word.\n\tif err := sigsetAdd(&got, 1); err != nil {\n\t\tt.Fatal(err)\n\t}\n\twant.Val[0] = 1\n\tif want != got {\n\t\tt.Fatalf(\"expected first word to be 0x%x, got: 0x%x\", want, got)\n\t}\n\n\t// And the last bit of the last word.\n\tif err := sigsetAdd(&got, maxSignal); err != nil {\n\t\tt.Fatal(err)\n\t}\n\twant.Val[words-1] = ^(^zero >> 1)\n\tif want != got {\n\t\tt.Fatalf(\"expected last word to be 0x%x, got: 0x%x\", want, got)\n\t}\n\n\tif err := sigsetAdd(&got, maxSignal+1); err == nil {\n\t\tt.Fatal(\"expected out-of-bounds add to be rejected\")\n\t}\n\tif err := sigsetAdd(&got, -1); err == nil {\n\t\tt.Fatal(\"expected negative signal to be rejected\")\n\t}\n}\n\nfunc TestProfilerSignal(t *testing.T) {\n\t// Additional goroutine lock to make the PthreadSigmask below execute on the\n\t// same OS thread as the functions under test. UnlockOSThread needs to be\n\t// called as many times as LockOSThread to unlock the goroutine.\n\truntime.LockOSThread()\n\tdefer runtime.UnlockOSThread()\n\n\tvar old unix.Sigset_t\n\tif err := unix.PthreadSigmask(0, nil, &old); err != nil {\n\t\tt.Fatal(\"get sigmask:\", err)\n\t}\n\n\tmaskProfilerSignal()\n\n\tvar have unix.Sigset_t\n\tif err := unix.PthreadSigmask(0, nil, &have); err != nil {\n\t\tt.Fatal(\"get sigmask:\", err)\n\t}\n\n\twant := have\n\tqt.Assert(t, qt.IsNil(sigsetAdd(&want, unix.SIGPROF)))\n\tqt.Assert(t, qt.Equals(have, want))\n\n\tunmaskProfilerSignal()\n\n\tif err := unix.PthreadSigmask(0, nil, &have); err != nil {\n\t\tt.Fatal(\"get sigmask:\", err)\n\t}\n\n\tqt.Assert(t, qt.Equals(have, old))\n}\n"
  },
  {
    "path": "internal/sys/syscall.go",
    "content": "package sys\n\nimport (\n\t\"runtime\"\n\t\"unsafe\"\n\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\n// ENOTSUPP is a Linux internal error code that has leaked into UAPI.\n//\n// It is not the same as ENOTSUP or EOPNOTSUPP.\nconst ENOTSUPP = unix.Errno(524)\n\n// Info is implemented by all structs that can be passed to the ObjInfo syscall.\n//\n//\tMapInfo\n//\tProgInfo\n//\tLinkInfo\n//\tBtfInfo\ntype Info interface {\n\tinfo() (unsafe.Pointer, uint32)\n}\n\nvar _ Info = (*MapInfo)(nil)\n\nfunc (i *MapInfo) info() (unsafe.Pointer, uint32) {\n\treturn unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))\n}\n\nvar _ Info = (*ProgInfo)(nil)\n\nfunc (i *ProgInfo) info() (unsafe.Pointer, uint32) {\n\treturn unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))\n}\n\nvar _ Info = (*LinkInfo)(nil)\n\nfunc (i *LinkInfo) info() (unsafe.Pointer, uint32) {\n\treturn unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))\n}\n\nfunc (i *TracingLinkInfo) info() (unsafe.Pointer, uint32) {\n\treturn unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))\n}\n\nfunc (i *CgroupLinkInfo) info() (unsafe.Pointer, uint32) {\n\treturn unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))\n}\n\nfunc (i *NetNsLinkInfo) info() (unsafe.Pointer, uint32) {\n\treturn unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))\n}\n\nfunc (i *XDPLinkInfo) info() (unsafe.Pointer, uint32) {\n\treturn unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))\n}\n\nfunc (i *TcxLinkInfo) info() (unsafe.Pointer, uint32) {\n\treturn unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))\n}\n\nfunc (i *NetfilterLinkInfo) info() (unsafe.Pointer, uint32) {\n\treturn unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))\n}\n\nfunc (i *NetkitLinkInfo) info() (unsafe.Pointer, uint32) {\n\treturn unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))\n}\n\nfunc (i *KprobeMultiLinkInfo) info() (unsafe.Pointer, uint32) {\n\treturn unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))\n}\n\nfunc (i *UprobeMultiLinkInfo) info() (unsafe.Pointer, uint32) {\n\treturn unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))\n}\n\nfunc (i *RawTracepointLinkInfo) info() (unsafe.Pointer, uint32) {\n\treturn unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))\n}\n\nfunc (i *KprobeLinkInfo) info() (unsafe.Pointer, uint32) {\n\treturn unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))\n}\n\nfunc (i *UprobeLinkInfo) info() (unsafe.Pointer, uint32) {\n\treturn unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))\n}\n\nfunc (i *TracepointLinkInfo) info() (unsafe.Pointer, uint32) {\n\treturn unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))\n}\n\nfunc (i *EventLinkInfo) info() (unsafe.Pointer, uint32) {\n\treturn unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))\n}\n\nvar _ Info = (*BtfInfo)(nil)\n\nfunc (i *BtfInfo) info() (unsafe.Pointer, uint32) {\n\treturn unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))\n}\n\nfunc (i *PerfEventLinkInfo) info() (unsafe.Pointer, uint32) {\n\treturn unsafe.Pointer(i), uint32(unsafe.Sizeof(*i))\n}\n\n// ObjInfo retrieves information about a BPF Fd.\n//\n// info may be one of MapInfo, ProgInfo, LinkInfo and BtfInfo.\nfunc ObjInfo(fd *FD, info Info) error {\n\tptr, len := info.info()\n\terr := ObjGetInfoByFd(&ObjGetInfoByFdAttr{\n\t\tBpfFd:   fd.Uint(),\n\t\tInfoLen: len,\n\t\tInfo:    UnsafePointer(ptr),\n\t})\n\truntime.KeepAlive(fd)\n\treturn err\n}\n\n// BPFObjName is a null-terminated string made up of\n// 'A-Za-z0-9_' characters.\ntype ObjName [BPF_OBJ_NAME_LEN]byte\n\n// NewObjName truncates the result if it is too long.\nfunc NewObjName(name string) ObjName {\n\tvar result ObjName\n\tcopy(result[:BPF_OBJ_NAME_LEN-1], name)\n\treturn result\n}\n\n// LogLevel controls the verbosity of the kernel's eBPF program verifier.\ntype LogLevel uint32\n\nconst (\n\tBPF_LOG_LEVEL1 LogLevel = 1 << iota\n\tBPF_LOG_LEVEL2\n\tBPF_LOG_STATS\n)\n\n// MapID uniquely identifies a bpf_map.\ntype MapID uint32\n\n// ProgramID uniquely identifies a bpf_map.\ntype ProgramID uint32\n\n// LinkID uniquely identifies a bpf_link.\ntype LinkID uint32\n\n// BTFID uniquely identifies a BTF blob loaded into the kernel.\ntype BTFID uint32\n\n// TypeID identifies a type in a BTF blob.\ntype TypeID uint32\n\n// Flags used by bpf_mprog.\nconst (\n\tBPF_F_REPLACE = 1 << (iota + 2)\n\tBPF_F_BEFORE\n\tBPF_F_AFTER\n\tBPF_F_ID\n\tBPF_F_LINK_MPROG = 1 << 13 // aka BPF_F_LINK\n)\n\n// Flags used by BPF_PROG_LOAD.\nconst (\n\tBPF_F_SLEEPABLE          = 1 << 4\n\tBPF_F_XDP_HAS_FRAGS      = 1 << 5\n\tBPF_F_XDP_DEV_BOUND_ONLY = 1 << 6\n)\n\nconst BPF_TAG_SIZE = 8\nconst BPF_OBJ_NAME_LEN = 16\n\n// wrappedErrno wraps [unix.Errno] to prevent direct comparisons with\n// syscall.E* or unix.E* constants.\n//\n// You should never export an error of this type.\ntype wrappedErrno struct {\n\tunix.Errno\n}\n\nfunc (we wrappedErrno) Unwrap() error {\n\treturn we.Errno\n}\n\nfunc (we wrappedErrno) Error() string {\n\tif we.Errno == ENOTSUPP {\n\t\treturn \"operation not supported\"\n\t}\n\treturn we.Errno.Error()\n}\n\ntype syscallError struct {\n\terror\n\terrno unix.Errno\n}\n\nfunc Error(err error, errno unix.Errno) error {\n\treturn &syscallError{err, errno}\n}\n\nfunc (se *syscallError) Is(target error) bool {\n\treturn target == se.error\n}\n\nfunc (se *syscallError) Unwrap() error {\n\treturn se.errno\n}\n"
  },
  {
    "path": "internal/sys/syscall_other.go",
    "content": "//go:build !windows\n\npackage sys\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"unsafe\"\n\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\n// BPF wraps SYS_BPF.\n//\n// Any pointers contained in attr must use the Pointer type from this package.\nfunc BPF(cmd Cmd, attr unsafe.Pointer, size uintptr) (uintptr, error) {\n\t// Prevent the Go profiler from repeatedly interrupting the verifier,\n\t// which could otherwise lead to a livelock due to receiving EAGAIN.\n\tif cmd == BPF_PROG_LOAD || cmd == BPF_PROG_RUN {\n\t\tmaskProfilerSignal()\n\t\tdefer unmaskProfilerSignal()\n\t}\n\n\tfor {\n\t\tr1, _, errNo := unix.Syscall(unix.SYS_BPF, uintptr(cmd), uintptr(attr), size)\n\t\truntime.KeepAlive(attr)\n\n\t\t// As of ~4.20 the verifier can be interrupted by a signal,\n\t\t// and returns EAGAIN in that case.\n\t\tif errNo == unix.EAGAIN && cmd == BPF_PROG_LOAD {\n\t\t\tcontinue\n\t\t}\n\n\t\tvar err error\n\t\tif errNo != 0 {\n\t\t\terr = wrappedErrno{errNo}\n\t\t}\n\n\t\treturn r1, err\n\t}\n}\n\n// ObjGetTyped wraps [ObjGet] with a readlink call to extract the type of the\n// underlying bpf object.\nfunc ObjGetTyped(attr *ObjGetAttr) (*FD, ObjType, error) {\n\tfd, err := ObjGet(attr)\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\ttyp, err := readType(fd)\n\tif err != nil {\n\t\t_ = fd.Close()\n\t\treturn nil, 0, fmt.Errorf(\"reading fd type: %w\", err)\n\t}\n\n\treturn fd, typ, nil\n}\n\n// readType returns the bpf object type of the file descriptor by calling\n// readlink(3). Returns an error if the file descriptor does not represent a bpf\n// object.\nfunc readType(fd *FD) (ObjType, error) {\n\ts, err := os.Readlink(filepath.Join(\"/proc/self/fd/\", fd.String()))\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"readlink fd %d: %w\", fd.Int(), err)\n\t}\n\n\ts = strings.TrimPrefix(s, \"anon_inode:\")\n\n\tswitch s {\n\tcase \"bpf-map\":\n\t\treturn BPF_TYPE_MAP, nil\n\tcase \"bpf-prog\":\n\t\treturn BPF_TYPE_PROG, nil\n\tcase \"bpf-link\":\n\t\treturn BPF_TYPE_LINK, nil\n\t}\n\n\treturn 0, fmt.Errorf(\"unknown type %s of fd %d\", s, fd.Int())\n}\n"
  },
  {
    "path": "internal/sys/syscall_test.go",
    "content": "package sys\n\nimport (\n\t\"errors\"\n\t\"math\"\n\t\"testing\"\n\t\"unsafe\"\n\n\t\"github.com/cilium/ebpf/internal/testutils/testmain\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestBPF(t *testing.T) {\n\tfd, err := MapCreate(&MapCreateAttr{\n\t\tMapType:    BPF_MAP_TYPE_HASH,\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: 1,\n\t})\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.IsNil(fd.Close()))\n}\n\nfunc TestBPFAllocations(t *testing.T) {\n\tn := testing.AllocsPerRun(10, func() {\n\t\tvar attr struct {\n\t\t\tFoo uint64\n\t\t}\n\n\t\tBPF(math.MaxUint32, unsafe.Pointer(&attr), 0)\n\t})\n\tqt.Assert(t, qt.Equals(n, 0))\n}\n\nfunc TestObjName(t *testing.T) {\n\tname := NewObjName(\"more_than_16_characters_long\")\n\tif name[len(name)-1] != 0 {\n\t\tt.Error(\"NewBPFObjName doesn't null terminate\")\n\t}\n\tif len(name) != BPF_OBJ_NAME_LEN {\n\t\tt.Errorf(\"Name is %d instead of %d bytes long\", len(name), BPF_OBJ_NAME_LEN)\n\t}\n}\n\nfunc TestWrappedErrno(t *testing.T) {\n\ta := error(wrappedErrno{unix.EINVAL})\n\tb := error(unix.EINVAL)\n\n\tif a == b {\n\t\tt.Error(\"wrappedErrno is comparable to plain errno\")\n\t}\n\n\tif !errors.Is(a, b) {\n\t\tt.Error(\"errors.Is(wrappedErrno, errno) returns false\")\n\t}\n\n\tif errors.Is(a, unix.EAGAIN) {\n\t\tt.Error(\"errors.Is(wrappedErrno, EAGAIN) returns true\")\n\t}\n\n\tnotsupp := wrappedErrno{ENOTSUPP}\n\tqt.Assert(t, qt.StringContains(notsupp.Error(), \"operation not supported\"))\n}\n\nfunc TestSyscallError(t *testing.T) {\n\terr := errors.New(\"foo\")\n\tfoo := Error(err, unix.EINVAL)\n\n\tif !errors.Is(foo, unix.EINVAL) {\n\t\tt.Error(\"SyscallError is not the wrapped errno\")\n\t}\n\n\tif !errors.Is(foo, err) {\n\t\tt.Error(\"SyscallError is not the wrapped error\")\n\t}\n\n\tif errors.Is(unix.EINVAL, foo) {\n\t\tt.Error(\"Errno is the SyscallError\")\n\t}\n\n\tif errors.Is(err, foo) {\n\t\tt.Error(\"Error is the SyscallError\")\n\t}\n}\n\nfunc TestMain(m *testing.M) {\n\ttestmain.Run(m)\n}\n"
  },
  {
    "path": "internal/sys/syscall_windows.go",
    "content": "package sys\n\nimport (\n\t\"fmt\"\n\t\"syscall\"\n\t\"unsafe\"\n\n\t\"golang.org/x/sys/windows\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/efw\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\n// BPF calls the BPF syscall wrapper in ebpfapi.dll.\n//\n// Any pointers contained in attr must use the Pointer type from this package.\n//\n// The implementation lives in https://github.com/microsoft/ebpf-for-windows/blob/main/libs/api/bpf_syscall.cpp\nfunc BPF(cmd Cmd, attr unsafe.Pointer, size uintptr) (uintptr, error) {\n\t// On Linux we need to guard against preemption by the profiler here. On\n\t// Windows it seems like a cgocall may not be preempted:\n\t// https://github.com/golang/go/blob/8b51146c698bcfcc2c2b73fa9390db5230f2ce0a/src/runtime/os_windows.go#L1240-L1246\n\n\taddr, err := efw.BPF.Find()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\t// Using [LazyProc.Call] forces attr to escape, which isn't the case when using syscall.Syscall directly.\n\tr1, _, lastError := syscall.SyscallN(addr, uintptr(cmd), uintptr(attr), size)\n\n\tif ret := int(efw.Int(r1)); ret < 0 {\n\t\terrNo := unix.Errno(-ret)\n\t\tif errNo == unix.EINVAL && lastError == windows.ERROR_CALL_NOT_IMPLEMENTED {\n\t\t\treturn 0, internal.ErrNotSupportedOnOS\n\t\t}\n\t\treturn 0, wrappedErrno{errNo}\n\t}\n\n\treturn r1, nil\n}\n\n// ObjGetTyped retrieves an pinned object and its type.\nfunc ObjGetTyped(attr *ObjGetAttr) (*FD, ObjType, error) {\n\tfd, err := ObjGet(attr)\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\tefwType, err := efw.EbpfObjectGetInfoByFd(fd.Int(), nil, nil)\n\tif err != nil {\n\t\t_ = fd.Close()\n\t\treturn nil, 0, err\n\t}\n\n\tswitch efwType {\n\tcase efw.EBPF_OBJECT_UNKNOWN:\n\t\treturn fd, BPF_TYPE_UNSPEC, nil\n\tcase efw.EBPF_OBJECT_MAP:\n\t\treturn fd, BPF_TYPE_MAP, nil\n\tcase efw.EBPF_OBJECT_LINK:\n\t\treturn fd, BPF_TYPE_LINK, nil\n\tcase efw.EBPF_OBJECT_PROGRAM:\n\t\treturn fd, BPF_TYPE_PROG, nil\n\tdefault:\n\t\treturn nil, 0, fmt.Errorf(\"unrecognized object type %v\", efwType)\n\t}\n}\n"
  },
  {
    "path": "internal/sys/types.go",
    "content": "// Code generated by internal/cmd/gentypes; DO NOT EDIT.\n\npackage sys\n\nimport (\n\t\"structs\"\n\t\"unsafe\"\n)\n\nconst (\n\tBPF_ADJ_ROOM_ENCAP_L2_MASK                 = 255\n\tBPF_ADJ_ROOM_ENCAP_L2_SHIFT                = 56\n\tBPF_ANY                                    = 0\n\tBPF_CSUM_LEVEL_DEC                         = 2\n\tBPF_CSUM_LEVEL_INC                         = 1\n\tBPF_CSUM_LEVEL_QUERY                       = 0\n\tBPF_CSUM_LEVEL_RESET                       = 3\n\tBPF_EXIST                                  = 2\n\tBPF_FIB_LKUP_RET_BLACKHOLE                 = 1\n\tBPF_FIB_LKUP_RET_FRAG_NEEDED               = 8\n\tBPF_FIB_LKUP_RET_FWD_DISABLED              = 5\n\tBPF_FIB_LKUP_RET_NOT_FWDED                 = 4\n\tBPF_FIB_LKUP_RET_NO_NEIGH                  = 7\n\tBPF_FIB_LKUP_RET_NO_SRC_ADDR               = 9\n\tBPF_FIB_LKUP_RET_PROHIBIT                  = 3\n\tBPF_FIB_LKUP_RET_SUCCESS                   = 0\n\tBPF_FIB_LKUP_RET_UNREACHABLE               = 2\n\tBPF_FIB_LKUP_RET_UNSUPP_LWT                = 6\n\tBPF_FIB_LOOKUP_DIRECT                      = 1\n\tBPF_FIB_LOOKUP_MARK                        = 32\n\tBPF_FIB_LOOKUP_OUTPUT                      = 2\n\tBPF_FIB_LOOKUP_SKIP_NEIGH                  = 4\n\tBPF_FIB_LOOKUP_SRC                         = 16\n\tBPF_FIB_LOOKUP_TBID                        = 8\n\tBPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG        = 1\n\tBPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP         = 4\n\tBPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL    = 2\n\tBPF_F_ADJ_ROOM_DECAP_L3_IPV4               = 128\n\tBPF_F_ADJ_ROOM_DECAP_L3_IPV6               = 256\n\tBPF_F_ADJ_ROOM_ENCAP_L2_ETH                = 64\n\tBPF_F_ADJ_ROOM_ENCAP_L3_IPV4               = 2\n\tBPF_F_ADJ_ROOM_ENCAP_L3_IPV6               = 4\n\tBPF_F_ADJ_ROOM_ENCAP_L4_GRE                = 8\n\tBPF_F_ADJ_ROOM_ENCAP_L4_UDP                = 16\n\tBPF_F_ADJ_ROOM_FIXED_GSO                   = 1\n\tBPF_F_ADJ_ROOM_NO_CSUM_RESET               = 32\n\tBPF_F_BPRM_SECUREEXEC                      = 1\n\tBPF_F_BROADCAST                            = 8\n\tBPF_F_CLONE                                = 512\n\tBPF_F_CTXLEN_MASK                          = 4503595332403200\n\tBPF_F_CURRENT_CPU                          = 4294967295\n\tBPF_F_CURRENT_NETNS                        = 18446744073709551615\n\tBPF_F_DONT_FRAGMENT                        = 4\n\tBPF_F_EXCLUDE_INGRESS                      = 16\n\tBPF_F_FAST_STACK_CMP                       = 512\n\tBPF_F_GET_BRANCH_RECORDS_SIZE              = 1\n\tBPF_F_HDR_FIELD_MASK                       = 15\n\tBPF_F_INDEX_MASK                           = 4294967295\n\tBPF_F_INGRESS                              = 1\n\tBPF_F_INNER_MAP                            = 4096\n\tBPF_F_INVALIDATE_HASH                      = 2\n\tBPF_F_IPV6                                 = 128\n\tBPF_F_KPROBE_MULTI_RETURN                  = 1\n\tBPF_F_LINK                                 = 8192\n\tBPF_F_LOCK                                 = 4\n\tBPF_F_MARK_ENFORCE                         = 64\n\tBPF_F_MARK_MANGLED_0                       = 32\n\tBPF_F_MMAPABLE                             = 1024\n\tBPF_F_NEIGH                                = 65536\n\tBPF_F_NEXTHOP                              = 262144\n\tBPF_F_NO_COMMON_LRU                        = 2\n\tBPF_F_NO_PREALLOC                          = 1\n\tBPF_F_NO_TUNNEL_KEY                        = 16\n\tBPF_F_NO_USER_CONV                         = 262144\n\tBPF_F_NUMA_NODE                            = 4\n\tBPF_F_PATH_FD                              = 16384\n\tBPF_F_PEER                                 = 131072\n\tBPF_F_PRESERVE_ELEMS                       = 2048\n\tBPF_F_PSEUDO_HDR                           = 16\n\tBPF_F_RDONLY                               = 8\n\tBPF_F_RDONLY_PROG                          = 128\n\tBPF_F_RECOMPUTE_CSUM                       = 1\n\tBPF_F_REUSE_STACKID                        = 1024\n\tBPF_F_SEGV_ON_FAULT                        = 131072\n\tBPF_F_SEQ_NUMBER                           = 8\n\tBPF_F_SKIP_FIELD_MASK                      = 255\n\tBPF_F_STACK_BUILD_ID                       = 32\n\tBPF_F_SYSCTL_BASE_NAME                     = 1\n\tBPF_F_TIMER_ABS                            = 1\n\tBPF_F_TIMER_CPU_PIN                        = 2\n\tBPF_F_TOKEN_FD                             = 65536\n\tBPF_F_TUNINFO_FLAGS                        = 16\n\tBPF_F_TUNINFO_IPV6                         = 1\n\tBPF_F_UPROBE_MULTI_RETURN                  = 1\n\tBPF_F_USER_BUILD_ID                        = 2048\n\tBPF_F_USER_STACK                           = 256\n\tBPF_F_VTYPE_BTF_OBJ_FD                     = 32768\n\tBPF_F_WRONLY                               = 16\n\tBPF_F_WRONLY_PROG                          = 256\n\tBPF_F_ZERO_CSUM_TX                         = 2\n\tBPF_F_ZERO_SEED                            = 64\n\tBPF_LOAD_HDR_OPT_TCP_SYN                   = 1\n\tBPF_LOCAL_STORAGE_GET_F_CREATE             = 1\n\tBPF_MAX_LOOPS                              = 8388608\n\tBPF_MAX_TIMED_LOOPS                        = 65535\n\tBPF_MAX_TRAMP_LINKS                        = 38\n\tBPF_NOEXIST                                = 1\n\tBPF_RB_AVAIL_DATA                          = 0\n\tBPF_RB_CONS_POS                            = 2\n\tBPF_RB_FORCE_WAKEUP                        = 2\n\tBPF_RB_NO_WAKEUP                           = 1\n\tBPF_RB_PROD_POS                            = 3\n\tBPF_RB_RING_SIZE                           = 1\n\tBPF_REG_0                                  = 0\n\tBPF_REG_1                                  = 1\n\tBPF_REG_10                                 = 10\n\tBPF_REG_2                                  = 2\n\tBPF_REG_3                                  = 3\n\tBPF_REG_4                                  = 4\n\tBPF_REG_5                                  = 5\n\tBPF_REG_6                                  = 6\n\tBPF_REG_7                                  = 7\n\tBPF_REG_8                                  = 8\n\tBPF_REG_9                                  = 9\n\tBPF_RINGBUF_BUSY_BIT                       = 2147483648\n\tBPF_RINGBUF_DISCARD_BIT                    = 1073741824\n\tBPF_RINGBUF_HDR_SZ                         = 8\n\tBPF_SKB_CLOCK_MONOTONIC                    = 1\n\tBPF_SKB_CLOCK_REALTIME                     = 0\n\tBPF_SKB_CLOCK_TAI                          = 2\n\tBPF_SKB_TSTAMP_DELIVERY_MONO               = 1\n\tBPF_SKB_TSTAMP_UNSPEC                      = 0\n\tBPF_SK_LOOKUP_F_NO_REUSEPORT               = 2\n\tBPF_SK_LOOKUP_F_REPLACE                    = 1\n\tBPF_SK_STORAGE_GET_F_CREATE                = 1\n\tBPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB         = 4\n\tBPF_SOCK_OPS_ALL_CB_FLAGS                  = 127\n\tBPF_SOCK_OPS_BASE_RTT                      = 7\n\tBPF_SOCK_OPS_HDR_OPT_LEN_CB                = 14\n\tBPF_SOCK_OPS_NEEDS_ECN                     = 6\n\tBPF_SOCK_OPS_PARSE_ALL_HDR_OPT_CB_FLAG     = 16\n\tBPF_SOCK_OPS_PARSE_HDR_OPT_CB              = 13\n\tBPF_SOCK_OPS_PARSE_UNKNOWN_HDR_OPT_CB_FLAG = 32\n\tBPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB        = 5\n\tBPF_SOCK_OPS_RETRANS_CB                    = 9\n\tBPF_SOCK_OPS_RETRANS_CB_FLAG               = 2\n\tBPF_SOCK_OPS_RTO_CB                        = 8\n\tBPF_SOCK_OPS_RTO_CB_FLAG                   = 1\n\tBPF_SOCK_OPS_RTT_CB                        = 12\n\tBPF_SOCK_OPS_RTT_CB_FLAG                   = 8\n\tBPF_SOCK_OPS_RWND_INIT                     = 2\n\tBPF_SOCK_OPS_STATE_CB                      = 10\n\tBPF_SOCK_OPS_STATE_CB_FLAG                 = 4\n\tBPF_SOCK_OPS_TCP_CONNECT_CB                = 3\n\tBPF_SOCK_OPS_TCP_LISTEN_CB                 = 11\n\tBPF_SOCK_OPS_TIMEOUT_INIT                  = 1\n\tBPF_SOCK_OPS_TSTAMP_ACK_CB                 = 19\n\tBPF_SOCK_OPS_TSTAMP_SCHED_CB               = 16\n\tBPF_SOCK_OPS_TSTAMP_SENDMSG_CB             = 20\n\tBPF_SOCK_OPS_TSTAMP_SND_HW_CB              = 18\n\tBPF_SOCK_OPS_TSTAMP_SND_SW_CB              = 17\n\tBPF_SOCK_OPS_VOID                          = 0\n\tBPF_SOCK_OPS_WRITE_HDR_OPT_CB              = 15\n\tBPF_SOCK_OPS_WRITE_HDR_OPT_CB_FLAG         = 64\n\tBPF_STREAM_MAX_CAPACITY                    = 100000\n\tBPF_TASK_ITER_ALL_PROCS                    = 0\n\tBPF_TASK_ITER_ALL_THREADS                  = 1\n\tBPF_TASK_ITER_PROC_THREADS                 = 2\n\tBPF_TCP_BOUND_INACTIVE                     = 13\n\tBPF_TCP_CLOSE                              = 7\n\tBPF_TCP_CLOSE_WAIT                         = 8\n\tBPF_TCP_CLOSING                            = 11\n\tBPF_TCP_ESTABLISHED                        = 1\n\tBPF_TCP_FIN_WAIT1                          = 4\n\tBPF_TCP_FIN_WAIT2                          = 5\n\tBPF_TCP_LAST_ACK                           = 9\n\tBPF_TCP_LISTEN                             = 10\n\tBPF_TCP_MAX_STATES                         = 14\n\tBPF_TCP_NEW_SYN_RECV                       = 12\n\tBPF_TCP_SYN_RECV                           = 3\n\tBPF_TCP_SYN_SENT                           = 2\n\tBPF_TCP_TIME_WAIT                          = 6\n\tBPF_WRITE_HDR_TCP_CURRENT_MSS              = 1\n\tBPF_WRITE_HDR_TCP_SYNACK_COOKIE            = 2\n\tBPF_XFRM_STATE_OPTS_SZ                     = 36\n)\n\ntype AdjRoomMode uint32\n\nconst (\n\tBPF_ADJ_ROOM_NET AdjRoomMode = 0\n\tBPF_ADJ_ROOM_MAC AdjRoomMode = 1\n)\n\ntype AttachType uint32\n\nconst (\n\tBPF_CGROUP_INET_INGRESS            AttachType = 0\n\tBPF_CGROUP_INET_EGRESS             AttachType = 1\n\tBPF_CGROUP_INET_SOCK_CREATE        AttachType = 2\n\tBPF_CGROUP_SOCK_OPS                AttachType = 3\n\tBPF_SK_SKB_STREAM_PARSER           AttachType = 4\n\tBPF_SK_SKB_STREAM_VERDICT          AttachType = 5\n\tBPF_CGROUP_DEVICE                  AttachType = 6\n\tBPF_SK_MSG_VERDICT                 AttachType = 7\n\tBPF_CGROUP_INET4_BIND              AttachType = 8\n\tBPF_CGROUP_INET6_BIND              AttachType = 9\n\tBPF_CGROUP_INET4_CONNECT           AttachType = 10\n\tBPF_CGROUP_INET6_CONNECT           AttachType = 11\n\tBPF_CGROUP_INET4_POST_BIND         AttachType = 12\n\tBPF_CGROUP_INET6_POST_BIND         AttachType = 13\n\tBPF_CGROUP_UDP4_SENDMSG            AttachType = 14\n\tBPF_CGROUP_UDP6_SENDMSG            AttachType = 15\n\tBPF_LIRC_MODE2                     AttachType = 16\n\tBPF_FLOW_DISSECTOR                 AttachType = 17\n\tBPF_CGROUP_SYSCTL                  AttachType = 18\n\tBPF_CGROUP_UDP4_RECVMSG            AttachType = 19\n\tBPF_CGROUP_UDP6_RECVMSG            AttachType = 20\n\tBPF_CGROUP_GETSOCKOPT              AttachType = 21\n\tBPF_CGROUP_SETSOCKOPT              AttachType = 22\n\tBPF_TRACE_RAW_TP                   AttachType = 23\n\tBPF_TRACE_FENTRY                   AttachType = 24\n\tBPF_TRACE_FEXIT                    AttachType = 25\n\tBPF_MODIFY_RETURN                  AttachType = 26\n\tBPF_LSM_MAC                        AttachType = 27\n\tBPF_TRACE_ITER                     AttachType = 28\n\tBPF_CGROUP_INET4_GETPEERNAME       AttachType = 29\n\tBPF_CGROUP_INET6_GETPEERNAME       AttachType = 30\n\tBPF_CGROUP_INET4_GETSOCKNAME       AttachType = 31\n\tBPF_CGROUP_INET6_GETSOCKNAME       AttachType = 32\n\tBPF_XDP_DEVMAP                     AttachType = 33\n\tBPF_CGROUP_INET_SOCK_RELEASE       AttachType = 34\n\tBPF_XDP_CPUMAP                     AttachType = 35\n\tBPF_SK_LOOKUP                      AttachType = 36\n\tBPF_XDP                            AttachType = 37\n\tBPF_SK_SKB_VERDICT                 AttachType = 38\n\tBPF_SK_REUSEPORT_SELECT            AttachType = 39\n\tBPF_SK_REUSEPORT_SELECT_OR_MIGRATE AttachType = 40\n\tBPF_PERF_EVENT                     AttachType = 41\n\tBPF_TRACE_KPROBE_MULTI             AttachType = 42\n\tBPF_LSM_CGROUP                     AttachType = 43\n\tBPF_STRUCT_OPS                     AttachType = 44\n\tBPF_NETFILTER                      AttachType = 45\n\tBPF_TCX_INGRESS                    AttachType = 46\n\tBPF_TCX_EGRESS                     AttachType = 47\n\tBPF_TRACE_UPROBE_MULTI             AttachType = 48\n\tBPF_CGROUP_UNIX_CONNECT            AttachType = 49\n\tBPF_CGROUP_UNIX_SENDMSG            AttachType = 50\n\tBPF_CGROUP_UNIX_RECVMSG            AttachType = 51\n\tBPF_CGROUP_UNIX_GETPEERNAME        AttachType = 52\n\tBPF_CGROUP_UNIX_GETSOCKNAME        AttachType = 53\n\tBPF_NETKIT_PRIMARY                 AttachType = 54\n\tBPF_NETKIT_PEER                    AttachType = 55\n\tBPF_TRACE_KPROBE_SESSION           AttachType = 56\n\tBPF_TRACE_UPROBE_SESSION           AttachType = 57\n\t__MAX_BPF_ATTACH_TYPE              AttachType = 58\n)\n\ntype Cmd uint32\n\nconst (\n\tBPF_MAP_CREATE                  Cmd = 0\n\tBPF_MAP_LOOKUP_ELEM             Cmd = 1\n\tBPF_MAP_UPDATE_ELEM             Cmd = 2\n\tBPF_MAP_DELETE_ELEM             Cmd = 3\n\tBPF_MAP_GET_NEXT_KEY            Cmd = 4\n\tBPF_PROG_LOAD                   Cmd = 5\n\tBPF_OBJ_PIN                     Cmd = 6\n\tBPF_OBJ_GET                     Cmd = 7\n\tBPF_PROG_ATTACH                 Cmd = 8\n\tBPF_PROG_DETACH                 Cmd = 9\n\tBPF_PROG_TEST_RUN               Cmd = 10\n\tBPF_PROG_RUN                    Cmd = 10\n\tBPF_PROG_GET_NEXT_ID            Cmd = 11\n\tBPF_MAP_GET_NEXT_ID             Cmd = 12\n\tBPF_PROG_GET_FD_BY_ID           Cmd = 13\n\tBPF_MAP_GET_FD_BY_ID            Cmd = 14\n\tBPF_OBJ_GET_INFO_BY_FD          Cmd = 15\n\tBPF_PROG_QUERY                  Cmd = 16\n\tBPF_RAW_TRACEPOINT_OPEN         Cmd = 17\n\tBPF_BTF_LOAD                    Cmd = 18\n\tBPF_BTF_GET_FD_BY_ID            Cmd = 19\n\tBPF_TASK_FD_QUERY               Cmd = 20\n\tBPF_MAP_LOOKUP_AND_DELETE_ELEM  Cmd = 21\n\tBPF_MAP_FREEZE                  Cmd = 22\n\tBPF_BTF_GET_NEXT_ID             Cmd = 23\n\tBPF_MAP_LOOKUP_BATCH            Cmd = 24\n\tBPF_MAP_LOOKUP_AND_DELETE_BATCH Cmd = 25\n\tBPF_MAP_UPDATE_BATCH            Cmd = 26\n\tBPF_MAP_DELETE_BATCH            Cmd = 27\n\tBPF_LINK_CREATE                 Cmd = 28\n\tBPF_LINK_UPDATE                 Cmd = 29\n\tBPF_LINK_GET_FD_BY_ID           Cmd = 30\n\tBPF_LINK_GET_NEXT_ID            Cmd = 31\n\tBPF_ENABLE_STATS                Cmd = 32\n\tBPF_ITER_CREATE                 Cmd = 33\n\tBPF_LINK_DETACH                 Cmd = 34\n\tBPF_PROG_BIND_MAP               Cmd = 35\n\tBPF_TOKEN_CREATE                Cmd = 36\n\tBPF_PROG_STREAM_READ_BY_FD      Cmd = 37\n\t__MAX_BPF_CMD                   Cmd = 38\n)\n\ntype FunctionId uint32\n\nconst (\n\tBPF_FUNC_unspec                         FunctionId = 0\n\tBPF_FUNC_map_lookup_elem                FunctionId = 1\n\tBPF_FUNC_map_update_elem                FunctionId = 2\n\tBPF_FUNC_map_delete_elem                FunctionId = 3\n\tBPF_FUNC_probe_read                     FunctionId = 4\n\tBPF_FUNC_ktime_get_ns                   FunctionId = 5\n\tBPF_FUNC_trace_printk                   FunctionId = 6\n\tBPF_FUNC_get_prandom_u32                FunctionId = 7\n\tBPF_FUNC_get_smp_processor_id           FunctionId = 8\n\tBPF_FUNC_skb_store_bytes                FunctionId = 9\n\tBPF_FUNC_l3_csum_replace                FunctionId = 10\n\tBPF_FUNC_l4_csum_replace                FunctionId = 11\n\tBPF_FUNC_tail_call                      FunctionId = 12\n\tBPF_FUNC_clone_redirect                 FunctionId = 13\n\tBPF_FUNC_get_current_pid_tgid           FunctionId = 14\n\tBPF_FUNC_get_current_uid_gid            FunctionId = 15\n\tBPF_FUNC_get_current_comm               FunctionId = 16\n\tBPF_FUNC_get_cgroup_classid             FunctionId = 17\n\tBPF_FUNC_skb_vlan_push                  FunctionId = 18\n\tBPF_FUNC_skb_vlan_pop                   FunctionId = 19\n\tBPF_FUNC_skb_get_tunnel_key             FunctionId = 20\n\tBPF_FUNC_skb_set_tunnel_key             FunctionId = 21\n\tBPF_FUNC_perf_event_read                FunctionId = 22\n\tBPF_FUNC_redirect                       FunctionId = 23\n\tBPF_FUNC_get_route_realm                FunctionId = 24\n\tBPF_FUNC_perf_event_output              FunctionId = 25\n\tBPF_FUNC_skb_load_bytes                 FunctionId = 26\n\tBPF_FUNC_get_stackid                    FunctionId = 27\n\tBPF_FUNC_csum_diff                      FunctionId = 28\n\tBPF_FUNC_skb_get_tunnel_opt             FunctionId = 29\n\tBPF_FUNC_skb_set_tunnel_opt             FunctionId = 30\n\tBPF_FUNC_skb_change_proto               FunctionId = 31\n\tBPF_FUNC_skb_change_type                FunctionId = 32\n\tBPF_FUNC_skb_under_cgroup               FunctionId = 33\n\tBPF_FUNC_get_hash_recalc                FunctionId = 34\n\tBPF_FUNC_get_current_task               FunctionId = 35\n\tBPF_FUNC_probe_write_user               FunctionId = 36\n\tBPF_FUNC_current_task_under_cgroup      FunctionId = 37\n\tBPF_FUNC_skb_change_tail                FunctionId = 38\n\tBPF_FUNC_skb_pull_data                  FunctionId = 39\n\tBPF_FUNC_csum_update                    FunctionId = 40\n\tBPF_FUNC_set_hash_invalid               FunctionId = 41\n\tBPF_FUNC_get_numa_node_id               FunctionId = 42\n\tBPF_FUNC_skb_change_head                FunctionId = 43\n\tBPF_FUNC_xdp_adjust_head                FunctionId = 44\n\tBPF_FUNC_probe_read_str                 FunctionId = 45\n\tBPF_FUNC_get_socket_cookie              FunctionId = 46\n\tBPF_FUNC_get_socket_uid                 FunctionId = 47\n\tBPF_FUNC_set_hash                       FunctionId = 48\n\tBPF_FUNC_setsockopt                     FunctionId = 49\n\tBPF_FUNC_skb_adjust_room                FunctionId = 50\n\tBPF_FUNC_redirect_map                   FunctionId = 51\n\tBPF_FUNC_sk_redirect_map                FunctionId = 52\n\tBPF_FUNC_sock_map_update                FunctionId = 53\n\tBPF_FUNC_xdp_adjust_meta                FunctionId = 54\n\tBPF_FUNC_perf_event_read_value          FunctionId = 55\n\tBPF_FUNC_perf_prog_read_value           FunctionId = 56\n\tBPF_FUNC_getsockopt                     FunctionId = 57\n\tBPF_FUNC_override_return                FunctionId = 58\n\tBPF_FUNC_sock_ops_cb_flags_set          FunctionId = 59\n\tBPF_FUNC_msg_redirect_map               FunctionId = 60\n\tBPF_FUNC_msg_apply_bytes                FunctionId = 61\n\tBPF_FUNC_msg_cork_bytes                 FunctionId = 62\n\tBPF_FUNC_msg_pull_data                  FunctionId = 63\n\tBPF_FUNC_bind                           FunctionId = 64\n\tBPF_FUNC_xdp_adjust_tail                FunctionId = 65\n\tBPF_FUNC_skb_get_xfrm_state             FunctionId = 66\n\tBPF_FUNC_get_stack                      FunctionId = 67\n\tBPF_FUNC_skb_load_bytes_relative        FunctionId = 68\n\tBPF_FUNC_fib_lookup                     FunctionId = 69\n\tBPF_FUNC_sock_hash_update               FunctionId = 70\n\tBPF_FUNC_msg_redirect_hash              FunctionId = 71\n\tBPF_FUNC_sk_redirect_hash               FunctionId = 72\n\tBPF_FUNC_lwt_push_encap                 FunctionId = 73\n\tBPF_FUNC_lwt_seg6_store_bytes           FunctionId = 74\n\tBPF_FUNC_lwt_seg6_adjust_srh            FunctionId = 75\n\tBPF_FUNC_lwt_seg6_action                FunctionId = 76\n\tBPF_FUNC_rc_repeat                      FunctionId = 77\n\tBPF_FUNC_rc_keydown                     FunctionId = 78\n\tBPF_FUNC_skb_cgroup_id                  FunctionId = 79\n\tBPF_FUNC_get_current_cgroup_id          FunctionId = 80\n\tBPF_FUNC_get_local_storage              FunctionId = 81\n\tBPF_FUNC_sk_select_reuseport            FunctionId = 82\n\tBPF_FUNC_skb_ancestor_cgroup_id         FunctionId = 83\n\tBPF_FUNC_sk_lookup_tcp                  FunctionId = 84\n\tBPF_FUNC_sk_lookup_udp                  FunctionId = 85\n\tBPF_FUNC_sk_release                     FunctionId = 86\n\tBPF_FUNC_map_push_elem                  FunctionId = 87\n\tBPF_FUNC_map_pop_elem                   FunctionId = 88\n\tBPF_FUNC_map_peek_elem                  FunctionId = 89\n\tBPF_FUNC_msg_push_data                  FunctionId = 90\n\tBPF_FUNC_msg_pop_data                   FunctionId = 91\n\tBPF_FUNC_rc_pointer_rel                 FunctionId = 92\n\tBPF_FUNC_spin_lock                      FunctionId = 93\n\tBPF_FUNC_spin_unlock                    FunctionId = 94\n\tBPF_FUNC_sk_fullsock                    FunctionId = 95\n\tBPF_FUNC_tcp_sock                       FunctionId = 96\n\tBPF_FUNC_skb_ecn_set_ce                 FunctionId = 97\n\tBPF_FUNC_get_listener_sock              FunctionId = 98\n\tBPF_FUNC_skc_lookup_tcp                 FunctionId = 99\n\tBPF_FUNC_tcp_check_syncookie            FunctionId = 100\n\tBPF_FUNC_sysctl_get_name                FunctionId = 101\n\tBPF_FUNC_sysctl_get_current_value       FunctionId = 102\n\tBPF_FUNC_sysctl_get_new_value           FunctionId = 103\n\tBPF_FUNC_sysctl_set_new_value           FunctionId = 104\n\tBPF_FUNC_strtol                         FunctionId = 105\n\tBPF_FUNC_strtoul                        FunctionId = 106\n\tBPF_FUNC_sk_storage_get                 FunctionId = 107\n\tBPF_FUNC_sk_storage_delete              FunctionId = 108\n\tBPF_FUNC_send_signal                    FunctionId = 109\n\tBPF_FUNC_tcp_gen_syncookie              FunctionId = 110\n\tBPF_FUNC_skb_output                     FunctionId = 111\n\tBPF_FUNC_probe_read_user                FunctionId = 112\n\tBPF_FUNC_probe_read_kernel              FunctionId = 113\n\tBPF_FUNC_probe_read_user_str            FunctionId = 114\n\tBPF_FUNC_probe_read_kernel_str          FunctionId = 115\n\tBPF_FUNC_tcp_send_ack                   FunctionId = 116\n\tBPF_FUNC_send_signal_thread             FunctionId = 117\n\tBPF_FUNC_jiffies64                      FunctionId = 118\n\tBPF_FUNC_read_branch_records            FunctionId = 119\n\tBPF_FUNC_get_ns_current_pid_tgid        FunctionId = 120\n\tBPF_FUNC_xdp_output                     FunctionId = 121\n\tBPF_FUNC_get_netns_cookie               FunctionId = 122\n\tBPF_FUNC_get_current_ancestor_cgroup_id FunctionId = 123\n\tBPF_FUNC_sk_assign                      FunctionId = 124\n\tBPF_FUNC_ktime_get_boot_ns              FunctionId = 125\n\tBPF_FUNC_seq_printf                     FunctionId = 126\n\tBPF_FUNC_seq_write                      FunctionId = 127\n\tBPF_FUNC_sk_cgroup_id                   FunctionId = 128\n\tBPF_FUNC_sk_ancestor_cgroup_id          FunctionId = 129\n\tBPF_FUNC_ringbuf_output                 FunctionId = 130\n\tBPF_FUNC_ringbuf_reserve                FunctionId = 131\n\tBPF_FUNC_ringbuf_submit                 FunctionId = 132\n\tBPF_FUNC_ringbuf_discard                FunctionId = 133\n\tBPF_FUNC_ringbuf_query                  FunctionId = 134\n\tBPF_FUNC_csum_level                     FunctionId = 135\n\tBPF_FUNC_skc_to_tcp6_sock               FunctionId = 136\n\tBPF_FUNC_skc_to_tcp_sock                FunctionId = 137\n\tBPF_FUNC_skc_to_tcp_timewait_sock       FunctionId = 138\n\tBPF_FUNC_skc_to_tcp_request_sock        FunctionId = 139\n\tBPF_FUNC_skc_to_udp6_sock               FunctionId = 140\n\tBPF_FUNC_get_task_stack                 FunctionId = 141\n\tBPF_FUNC_load_hdr_opt                   FunctionId = 142\n\tBPF_FUNC_store_hdr_opt                  FunctionId = 143\n\tBPF_FUNC_reserve_hdr_opt                FunctionId = 144\n\tBPF_FUNC_inode_storage_get              FunctionId = 145\n\tBPF_FUNC_inode_storage_delete           FunctionId = 146\n\tBPF_FUNC_d_path                         FunctionId = 147\n\tBPF_FUNC_copy_from_user                 FunctionId = 148\n\tBPF_FUNC_snprintf_btf                   FunctionId = 149\n\tBPF_FUNC_seq_printf_btf                 FunctionId = 150\n\tBPF_FUNC_skb_cgroup_classid             FunctionId = 151\n\tBPF_FUNC_redirect_neigh                 FunctionId = 152\n\tBPF_FUNC_per_cpu_ptr                    FunctionId = 153\n\tBPF_FUNC_this_cpu_ptr                   FunctionId = 154\n\tBPF_FUNC_redirect_peer                  FunctionId = 155\n\tBPF_FUNC_task_storage_get               FunctionId = 156\n\tBPF_FUNC_task_storage_delete            FunctionId = 157\n\tBPF_FUNC_get_current_task_btf           FunctionId = 158\n\tBPF_FUNC_bprm_opts_set                  FunctionId = 159\n\tBPF_FUNC_ktime_get_coarse_ns            FunctionId = 160\n\tBPF_FUNC_ima_inode_hash                 FunctionId = 161\n\tBPF_FUNC_sock_from_file                 FunctionId = 162\n\tBPF_FUNC_check_mtu                      FunctionId = 163\n\tBPF_FUNC_for_each_map_elem              FunctionId = 164\n\tBPF_FUNC_snprintf                       FunctionId = 165\n\tBPF_FUNC_sys_bpf                        FunctionId = 166\n\tBPF_FUNC_btf_find_by_name_kind          FunctionId = 167\n\tBPF_FUNC_sys_close                      FunctionId = 168\n\tBPF_FUNC_timer_init                     FunctionId = 169\n\tBPF_FUNC_timer_set_callback             FunctionId = 170\n\tBPF_FUNC_timer_start                    FunctionId = 171\n\tBPF_FUNC_timer_cancel                   FunctionId = 172\n\tBPF_FUNC_get_func_ip                    FunctionId = 173\n\tBPF_FUNC_get_attach_cookie              FunctionId = 174\n\tBPF_FUNC_task_pt_regs                   FunctionId = 175\n\tBPF_FUNC_get_branch_snapshot            FunctionId = 176\n\tBPF_FUNC_trace_vprintk                  FunctionId = 177\n\tBPF_FUNC_skc_to_unix_sock               FunctionId = 178\n\tBPF_FUNC_kallsyms_lookup_name           FunctionId = 179\n\tBPF_FUNC_find_vma                       FunctionId = 180\n\tBPF_FUNC_loop                           FunctionId = 181\n\tBPF_FUNC_strncmp                        FunctionId = 182\n\tBPF_FUNC_get_func_arg                   FunctionId = 183\n\tBPF_FUNC_get_func_ret                   FunctionId = 184\n\tBPF_FUNC_get_func_arg_cnt               FunctionId = 185\n\tBPF_FUNC_get_retval                     FunctionId = 186\n\tBPF_FUNC_set_retval                     FunctionId = 187\n\tBPF_FUNC_xdp_get_buff_len               FunctionId = 188\n\tBPF_FUNC_xdp_load_bytes                 FunctionId = 189\n\tBPF_FUNC_xdp_store_bytes                FunctionId = 190\n\tBPF_FUNC_copy_from_user_task            FunctionId = 191\n\tBPF_FUNC_skb_set_tstamp                 FunctionId = 192\n\tBPF_FUNC_ima_file_hash                  FunctionId = 193\n\tBPF_FUNC_kptr_xchg                      FunctionId = 194\n\tBPF_FUNC_map_lookup_percpu_elem         FunctionId = 195\n\tBPF_FUNC_skc_to_mptcp_sock              FunctionId = 196\n\tBPF_FUNC_dynptr_from_mem                FunctionId = 197\n\tBPF_FUNC_ringbuf_reserve_dynptr         FunctionId = 198\n\tBPF_FUNC_ringbuf_submit_dynptr          FunctionId = 199\n\tBPF_FUNC_ringbuf_discard_dynptr         FunctionId = 200\n\tBPF_FUNC_dynptr_read                    FunctionId = 201\n\tBPF_FUNC_dynptr_write                   FunctionId = 202\n\tBPF_FUNC_dynptr_data                    FunctionId = 203\n\tBPF_FUNC_tcp_raw_gen_syncookie_ipv4     FunctionId = 204\n\tBPF_FUNC_tcp_raw_gen_syncookie_ipv6     FunctionId = 205\n\tBPF_FUNC_tcp_raw_check_syncookie_ipv4   FunctionId = 206\n\tBPF_FUNC_tcp_raw_check_syncookie_ipv6   FunctionId = 207\n\tBPF_FUNC_ktime_get_tai_ns               FunctionId = 208\n\tBPF_FUNC_user_ringbuf_drain             FunctionId = 209\n\tBPF_FUNC_cgrp_storage_get               FunctionId = 210\n\tBPF_FUNC_cgrp_storage_delete            FunctionId = 211\n\t__BPF_FUNC_MAX_ID                       FunctionId = 212\n)\n\ntype HdrStartOff uint32\n\nconst (\n\tBPF_HDR_START_MAC HdrStartOff = 0\n\tBPF_HDR_START_NET HdrStartOff = 1\n)\n\ntype LinkType uint32\n\nconst (\n\tBPF_LINK_TYPE_UNSPEC         LinkType = 0\n\tBPF_LINK_TYPE_RAW_TRACEPOINT LinkType = 1\n\tBPF_LINK_TYPE_TRACING        LinkType = 2\n\tBPF_LINK_TYPE_CGROUP         LinkType = 3\n\tBPF_LINK_TYPE_ITER           LinkType = 4\n\tBPF_LINK_TYPE_NETNS          LinkType = 5\n\tBPF_LINK_TYPE_XDP            LinkType = 6\n\tBPF_LINK_TYPE_PERF_EVENT     LinkType = 7\n\tBPF_LINK_TYPE_KPROBE_MULTI   LinkType = 8\n\tBPF_LINK_TYPE_STRUCT_OPS     LinkType = 9\n\tBPF_LINK_TYPE_NETFILTER      LinkType = 10\n\tBPF_LINK_TYPE_TCX            LinkType = 11\n\tBPF_LINK_TYPE_UPROBE_MULTI   LinkType = 12\n\tBPF_LINK_TYPE_NETKIT         LinkType = 13\n\tBPF_LINK_TYPE_SOCKMAP        LinkType = 14\n\t__MAX_BPF_LINK_TYPE          LinkType = 15\n)\n\ntype MapType uint32\n\nconst (\n\tBPF_MAP_TYPE_UNSPEC                           MapType = 0\n\tBPF_MAP_TYPE_HASH                             MapType = 1\n\tBPF_MAP_TYPE_ARRAY                            MapType = 2\n\tBPF_MAP_TYPE_PROG_ARRAY                       MapType = 3\n\tBPF_MAP_TYPE_PERF_EVENT_ARRAY                 MapType = 4\n\tBPF_MAP_TYPE_PERCPU_HASH                      MapType = 5\n\tBPF_MAP_TYPE_PERCPU_ARRAY                     MapType = 6\n\tBPF_MAP_TYPE_STACK_TRACE                      MapType = 7\n\tBPF_MAP_TYPE_CGROUP_ARRAY                     MapType = 8\n\tBPF_MAP_TYPE_LRU_HASH                         MapType = 9\n\tBPF_MAP_TYPE_LRU_PERCPU_HASH                  MapType = 10\n\tBPF_MAP_TYPE_LPM_TRIE                         MapType = 11\n\tBPF_MAP_TYPE_ARRAY_OF_MAPS                    MapType = 12\n\tBPF_MAP_TYPE_HASH_OF_MAPS                     MapType = 13\n\tBPF_MAP_TYPE_DEVMAP                           MapType = 14\n\tBPF_MAP_TYPE_SOCKMAP                          MapType = 15\n\tBPF_MAP_TYPE_CPUMAP                           MapType = 16\n\tBPF_MAP_TYPE_XSKMAP                           MapType = 17\n\tBPF_MAP_TYPE_SOCKHASH                         MapType = 18\n\tBPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED        MapType = 19\n\tBPF_MAP_TYPE_CGROUP_STORAGE                   MapType = 19\n\tBPF_MAP_TYPE_REUSEPORT_SOCKARRAY              MapType = 20\n\tBPF_MAP_TYPE_PERCPU_CGROUP_STORAGE_DEPRECATED MapType = 21\n\tBPF_MAP_TYPE_PERCPU_CGROUP_STORAGE            MapType = 21\n\tBPF_MAP_TYPE_QUEUE                            MapType = 22\n\tBPF_MAP_TYPE_STACK                            MapType = 23\n\tBPF_MAP_TYPE_SK_STORAGE                       MapType = 24\n\tBPF_MAP_TYPE_DEVMAP_HASH                      MapType = 25\n\tBPF_MAP_TYPE_STRUCT_OPS                       MapType = 26\n\tBPF_MAP_TYPE_RINGBUF                          MapType = 27\n\tBPF_MAP_TYPE_INODE_STORAGE                    MapType = 28\n\tBPF_MAP_TYPE_TASK_STORAGE                     MapType = 29\n\tBPF_MAP_TYPE_BLOOM_FILTER                     MapType = 30\n\tBPF_MAP_TYPE_USER_RINGBUF                     MapType = 31\n\tBPF_MAP_TYPE_CGRP_STORAGE                     MapType = 32\n\tBPF_MAP_TYPE_ARENA                            MapType = 33\n\t__MAX_BPF_MAP_TYPE                            MapType = 34\n)\n\ntype NetfilterInetHook uint32\n\nconst (\n\tNF_INET_PRE_ROUTING  NetfilterInetHook = 0\n\tNF_INET_LOCAL_IN     NetfilterInetHook = 1\n\tNF_INET_FORWARD      NetfilterInetHook = 2\n\tNF_INET_LOCAL_OUT    NetfilterInetHook = 3\n\tNF_INET_POST_ROUTING NetfilterInetHook = 4\n\tNF_INET_NUMHOOKS     NetfilterInetHook = 5\n\tNF_INET_INGRESS      NetfilterInetHook = 5\n)\n\ntype ObjType uint32\n\nconst (\n\tBPF_TYPE_UNSPEC ObjType = 0\n\tBPF_TYPE_PROG   ObjType = 1\n\tBPF_TYPE_MAP    ObjType = 2\n\tBPF_TYPE_LINK   ObjType = 3\n)\n\ntype PerfEventType uint32\n\nconst (\n\tBPF_PERF_EVENT_UNSPEC     PerfEventType = 0\n\tBPF_PERF_EVENT_UPROBE     PerfEventType = 1\n\tBPF_PERF_EVENT_URETPROBE  PerfEventType = 2\n\tBPF_PERF_EVENT_KPROBE     PerfEventType = 3\n\tBPF_PERF_EVENT_KRETPROBE  PerfEventType = 4\n\tBPF_PERF_EVENT_TRACEPOINT PerfEventType = 5\n\tBPF_PERF_EVENT_EVENT      PerfEventType = 6\n)\n\ntype ProgType uint32\n\nconst (\n\tBPF_PROG_TYPE_UNSPEC                  ProgType = 0\n\tBPF_PROG_TYPE_SOCKET_FILTER           ProgType = 1\n\tBPF_PROG_TYPE_KPROBE                  ProgType = 2\n\tBPF_PROG_TYPE_SCHED_CLS               ProgType = 3\n\tBPF_PROG_TYPE_SCHED_ACT               ProgType = 4\n\tBPF_PROG_TYPE_TRACEPOINT              ProgType = 5\n\tBPF_PROG_TYPE_XDP                     ProgType = 6\n\tBPF_PROG_TYPE_PERF_EVENT              ProgType = 7\n\tBPF_PROG_TYPE_CGROUP_SKB              ProgType = 8\n\tBPF_PROG_TYPE_CGROUP_SOCK             ProgType = 9\n\tBPF_PROG_TYPE_LWT_IN                  ProgType = 10\n\tBPF_PROG_TYPE_LWT_OUT                 ProgType = 11\n\tBPF_PROG_TYPE_LWT_XMIT                ProgType = 12\n\tBPF_PROG_TYPE_SOCK_OPS                ProgType = 13\n\tBPF_PROG_TYPE_SK_SKB                  ProgType = 14\n\tBPF_PROG_TYPE_CGROUP_DEVICE           ProgType = 15\n\tBPF_PROG_TYPE_SK_MSG                  ProgType = 16\n\tBPF_PROG_TYPE_RAW_TRACEPOINT          ProgType = 17\n\tBPF_PROG_TYPE_CGROUP_SOCK_ADDR        ProgType = 18\n\tBPF_PROG_TYPE_LWT_SEG6LOCAL           ProgType = 19\n\tBPF_PROG_TYPE_LIRC_MODE2              ProgType = 20\n\tBPF_PROG_TYPE_SK_REUSEPORT            ProgType = 21\n\tBPF_PROG_TYPE_FLOW_DISSECTOR          ProgType = 22\n\tBPF_PROG_TYPE_CGROUP_SYSCTL           ProgType = 23\n\tBPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE ProgType = 24\n\tBPF_PROG_TYPE_CGROUP_SOCKOPT          ProgType = 25\n\tBPF_PROG_TYPE_TRACING                 ProgType = 26\n\tBPF_PROG_TYPE_STRUCT_OPS              ProgType = 27\n\tBPF_PROG_TYPE_EXT                     ProgType = 28\n\tBPF_PROG_TYPE_LSM                     ProgType = 29\n\tBPF_PROG_TYPE_SK_LOOKUP               ProgType = 30\n\tBPF_PROG_TYPE_SYSCALL                 ProgType = 31\n\tBPF_PROG_TYPE_NETFILTER               ProgType = 32\n\t__MAX_BPF_PROG_TYPE                   ProgType = 33\n)\n\ntype RetCode uint32\n\nconst (\n\tBPF_OK                      RetCode = 0\n\tBPF_DROP                    RetCode = 2\n\tBPF_REDIRECT                RetCode = 7\n\tBPF_LWT_REROUTE             RetCode = 128\n\tBPF_FLOW_DISSECTOR_CONTINUE RetCode = 129\n)\n\ntype SkAction uint32\n\nconst (\n\tSK_DROP SkAction = 0\n\tSK_PASS SkAction = 1\n)\n\ntype StackBuildIdStatus uint32\n\nconst (\n\tBPF_STACK_BUILD_ID_EMPTY StackBuildIdStatus = 0\n\tBPF_STACK_BUILD_ID_VALID StackBuildIdStatus = 1\n\tBPF_STACK_BUILD_ID_IP    StackBuildIdStatus = 2\n)\n\ntype StatsType uint32\n\nconst (\n\tBPF_STATS_RUN_TIME StatsType = 0\n)\n\ntype TcxActionBase int32\n\nconst (\n\tTCX_NEXT     TcxActionBase = -1\n\tTCX_PASS     TcxActionBase = 0\n\tTCX_DROP     TcxActionBase = 2\n\tTCX_REDIRECT TcxActionBase = 7\n)\n\ntype XdpAction uint32\n\nconst (\n\tXDP_ABORTED  XdpAction = 0\n\tXDP_DROP     XdpAction = 1\n\tXDP_PASS     XdpAction = 2\n\tXDP_TX       XdpAction = 3\n\tXDP_REDIRECT XdpAction = 4\n)\n\ntype NetfilterProtocolFamily uint32\n\nconst (\n\tNFPROTO_UNSPEC   NetfilterProtocolFamily = 0\n\tNFPROTO_INET     NetfilterProtocolFamily = 1\n\tNFPROTO_IPV4     NetfilterProtocolFamily = 2\n\tNFPROTO_ARP      NetfilterProtocolFamily = 3\n\tNFPROTO_NETDEV   NetfilterProtocolFamily = 5\n\tNFPROTO_BRIDGE   NetfilterProtocolFamily = 7\n\tNFPROTO_IPV6     NetfilterProtocolFamily = 10\n\tNFPROTO_NUMPROTO NetfilterProtocolFamily = 11\n)\n\ntype BtfInfo struct {\n\t_         structs.HostLayout\n\tBtf       TypedPointer[uint8]\n\tBtfSize   uint32\n\tId        BTFID\n\tName      TypedPointer[uint8]\n\tNameLen   uint32\n\tKernelBtf uint32\n}\n\ntype FuncInfo struct {\n\t_       structs.HostLayout\n\tInsnOff uint32\n\tTypeId  uint32\n}\n\ntype LineInfo struct {\n\t_           structs.HostLayout\n\tInsnOff     uint32\n\tFileNameOff uint32\n\tLineOff     uint32\n\tLineCol     uint32\n}\n\ntype LinkInfo struct {\n\t_      structs.HostLayout\n\tType   LinkType\n\tId     LinkID\n\tProgId uint32\n\t_      [4]byte\n\tExtra  [48]uint8\n}\n\ntype MapInfo struct {\n\t_                     structs.HostLayout\n\tType                  uint32\n\tId                    MapID\n\tKeySize               uint32\n\tValueSize             uint32\n\tMaxEntries            uint32\n\tMapFlags              uint32\n\tName                  ObjName\n\tIfindex               uint32\n\tBtfVmlinuxValueTypeId TypeID\n\tNetnsDev              uint64\n\tNetnsIno              uint64\n\tBtfId                 uint32\n\tBtfKeyTypeId          TypeID\n\tBtfValueTypeId        TypeID\n\tBtfVmlinuxId          uint32\n\tMapExtra              uint64\n\tHash                  uint64\n\tHashSize              uint32\n\t_                     [4]byte\n}\n\ntype ProgInfo struct {\n\t_                    structs.HostLayout\n\tType                 uint32\n\tId                   uint32\n\tTag                  [8]uint8\n\tJitedProgLen         uint32\n\tXlatedProgLen        uint32\n\tJitedProgInsns       TypedPointer[uint8]\n\tXlatedProgInsns      TypedPointer[uint8]\n\tLoadTime             uint64\n\tCreatedByUid         uint32\n\tNrMapIds             uint32\n\tMapIds               TypedPointer[MapID]\n\tName                 ObjName\n\tIfindex              uint32\n\t_                    [4]byte /* unsupported bitfield */\n\tNetnsDev             uint64\n\tNetnsIno             uint64\n\tNrJitedKsyms         uint32\n\tNrJitedFuncLens      uint32\n\tJitedKsyms           TypedPointer[uint64]\n\tJitedFuncLens        TypedPointer[uint32]\n\tBtfId                BTFID\n\tFuncInfoRecSize      uint32\n\tFuncInfo             TypedPointer[uint8]\n\tNrFuncInfo           uint32\n\tNrLineInfo           uint32\n\tLineInfo             TypedPointer[uint8]\n\tJitedLineInfo        TypedPointer[uint64]\n\tNrJitedLineInfo      uint32\n\tLineInfoRecSize      uint32\n\tJitedLineInfoRecSize uint32\n\tNrProgTags           uint32\n\tProgTags             uint64\n\tRunTimeNs            uint64\n\tRunCnt               uint64\n\tRecursionMisses      uint64\n\tVerifiedInsns        uint32\n\tAttachBtfObjId       BTFID\n\tAttachBtfId          TypeID\n\t_                    [4]byte\n}\n\ntype SkLookup struct {\n\t_              structs.HostLayout\n\tCookie         uint64\n\tFamily         uint32\n\tProtocol       uint32\n\tRemoteIp4      [4]uint8\n\tRemoteIp6      [16]uint8\n\tRemotePort     uint16\n\t_              [2]byte\n\tLocalIp4       [4]uint8\n\tLocalIp6       [16]uint8\n\tLocalPort      uint32\n\tIngressIfindex uint32\n\t_              [4]byte\n}\n\ntype XdpMd struct {\n\t_              structs.HostLayout\n\tData           uint32\n\tDataEnd        uint32\n\tDataMeta       uint32\n\tIngressIfindex uint32\n\tRxQueueIndex   uint32\n\tEgressIfindex  uint32\n}\n\ntype BtfGetFdByIdAttr struct {\n\t_  structs.HostLayout\n\tId uint32\n}\n\nfunc BtfGetFdById(attr *BtfGetFdByIdAttr) (*FD, error) {\n\tfd, err := BPF(BPF_BTF_GET_FD_BY_ID, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewFD(int(fd))\n}\n\ntype BtfGetNextIdAttr struct {\n\t_      structs.HostLayout\n\tId     BTFID\n\tNextId BTFID\n}\n\nfunc BtfGetNextId(attr *BtfGetNextIdAttr) error {\n\t_, err := BPF(BPF_BTF_GET_NEXT_ID, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\treturn err\n}\n\ntype BtfLoadAttr struct {\n\t_              structs.HostLayout\n\tBtf            TypedPointer[uint8]\n\tBtfLogBuf      TypedPointer[uint8]\n\tBtfSize        uint32\n\tBtfLogSize     uint32\n\tBtfLogLevel    uint32\n\tBtfLogTrueSize uint32\n\tBtfFlags       uint32\n\tBtfTokenFd     int32\n}\n\nfunc BtfLoad(attr *BtfLoadAttr) (*FD, error) {\n\tfd, err := BPF(BPF_BTF_LOAD, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewFD(int(fd))\n}\n\ntype EnableStatsAttr struct {\n\t_    structs.HostLayout\n\tType uint32\n}\n\nfunc EnableStats(attr *EnableStatsAttr) (*FD, error) {\n\tfd, err := BPF(BPF_ENABLE_STATS, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewFD(int(fd))\n}\n\ntype IterCreateAttr struct {\n\t_      structs.HostLayout\n\tLinkFd uint32\n\tFlags  uint32\n}\n\nfunc IterCreate(attr *IterCreateAttr) (*FD, error) {\n\tfd, err := BPF(BPF_ITER_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewFD(int(fd))\n}\n\ntype LinkCreateAttr struct {\n\t_           structs.HostLayout\n\tProgFd      uint32\n\tTargetFd    uint32\n\tAttachType  AttachType\n\tFlags       uint32\n\tTargetBtfId TypeID\n\t_           [44]byte\n}\n\nfunc LinkCreate(attr *LinkCreateAttr) (*FD, error) {\n\tfd, err := BPF(BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewFD(int(fd))\n}\n\ntype LinkCreateIterAttr struct {\n\t_           structs.HostLayout\n\tProgFd      uint32\n\tTargetFd    uint32\n\tAttachType  AttachType\n\tFlags       uint32\n\tIterInfo    Pointer\n\tIterInfoLen uint32\n\t_           [36]byte\n}\n\nfunc LinkCreateIter(attr *LinkCreateIterAttr) (*FD, error) {\n\tfd, err := BPF(BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewFD(int(fd))\n}\n\ntype LinkCreateKprobeMultiAttr struct {\n\t_                structs.HostLayout\n\tProgFd           uint32\n\tTargetFd         uint32\n\tAttachType       AttachType\n\tFlags            uint32\n\tKprobeMultiFlags uint32\n\tCount            uint32\n\tSyms             StringSlicePointer\n\tAddrs            TypedPointer[uintptr]\n\tCookies          TypedPointer[uint64]\n\t_                [16]byte\n}\n\nfunc LinkCreateKprobeMulti(attr *LinkCreateKprobeMultiAttr) (*FD, error) {\n\tfd, err := BPF(BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewFD(int(fd))\n}\n\ntype LinkCreateNetfilterAttr struct {\n\t_              structs.HostLayout\n\tProgFd         uint32\n\tTargetFd       uint32\n\tAttachType     AttachType\n\tFlags          uint32\n\tPf             NetfilterProtocolFamily\n\tHooknum        NetfilterInetHook\n\tPriority       int32\n\tNetfilterFlags uint32\n\t_              [32]byte\n}\n\nfunc LinkCreateNetfilter(attr *LinkCreateNetfilterAttr) (*FD, error) {\n\tfd, err := BPF(BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewFD(int(fd))\n}\n\ntype LinkCreateNetkitAttr struct {\n\t_                structs.HostLayout\n\tProgFd           uint32\n\tTargetIfindex    uint32\n\tAttachType       AttachType\n\tFlags            uint32\n\tRelativeFdOrId   uint32\n\t_                [4]byte\n\tExpectedRevision uint64\n\t_                [32]byte\n}\n\nfunc LinkCreateNetkit(attr *LinkCreateNetkitAttr) (*FD, error) {\n\tfd, err := BPF(BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewFD(int(fd))\n}\n\ntype LinkCreatePerfEventAttr struct {\n\t_          structs.HostLayout\n\tProgFd     uint32\n\tTargetFd   uint32\n\tAttachType AttachType\n\tFlags      uint32\n\tBpfCookie  uint64\n\t_          [40]byte\n}\n\nfunc LinkCreatePerfEvent(attr *LinkCreatePerfEventAttr) (*FD, error) {\n\tfd, err := BPF(BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewFD(int(fd))\n}\n\ntype LinkCreateTcxAttr struct {\n\t_                structs.HostLayout\n\tProgFd           uint32\n\tTargetIfindex    uint32\n\tAttachType       AttachType\n\tFlags            uint32\n\tRelativeFdOrId   uint32\n\t_                [4]byte\n\tExpectedRevision uint64\n\t_                [32]byte\n}\n\nfunc LinkCreateTcx(attr *LinkCreateTcxAttr) (*FD, error) {\n\tfd, err := BPF(BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewFD(int(fd))\n}\n\ntype LinkCreateTracingAttr struct {\n\t_           structs.HostLayout\n\tProgFd      uint32\n\tTargetFd    uint32\n\tAttachType  AttachType\n\tFlags       uint32\n\tTargetBtfId BTFID\n\t_           [4]byte\n\tCookie      uint64\n\t_           [32]byte\n}\n\nfunc LinkCreateTracing(attr *LinkCreateTracingAttr) (*FD, error) {\n\tfd, err := BPF(BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewFD(int(fd))\n}\n\ntype LinkCreateUprobeMultiAttr struct {\n\t_                structs.HostLayout\n\tProgFd           uint32\n\tTargetFd         uint32\n\tAttachType       AttachType\n\tFlags            uint32\n\tPath             StringPointer\n\tOffsets          TypedPointer[uint64]\n\tRefCtrOffsets    TypedPointer[uint64]\n\tCookies          TypedPointer[uint64]\n\tCount            uint32\n\tUprobeMultiFlags uint32\n\tPid              uint32\n\t_                [4]byte\n}\n\nfunc LinkCreateUprobeMulti(attr *LinkCreateUprobeMultiAttr) (*FD, error) {\n\tfd, err := BPF(BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewFD(int(fd))\n}\n\ntype LinkDetachAttr struct {\n\t_      structs.HostLayout\n\tLinkFd uint32\n}\n\nfunc LinkDetach(attr *LinkDetachAttr) error {\n\t_, err := BPF(BPF_LINK_DETACH, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\treturn err\n}\n\ntype LinkGetFdByIdAttr struct {\n\t_  structs.HostLayout\n\tId LinkID\n}\n\nfunc LinkGetFdById(attr *LinkGetFdByIdAttr) (*FD, error) {\n\tfd, err := BPF(BPF_LINK_GET_FD_BY_ID, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewFD(int(fd))\n}\n\ntype LinkGetNextIdAttr struct {\n\t_      structs.HostLayout\n\tId     LinkID\n\tNextId LinkID\n}\n\nfunc LinkGetNextId(attr *LinkGetNextIdAttr) error {\n\t_, err := BPF(BPF_LINK_GET_NEXT_ID, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\treturn err\n}\n\ntype LinkUpdateAttr struct {\n\t_         structs.HostLayout\n\tLinkFd    uint32\n\tNewProgFd uint32\n\tFlags     uint32\n\tOldProgFd uint32\n}\n\nfunc LinkUpdate(attr *LinkUpdateAttr) error {\n\t_, err := BPF(BPF_LINK_UPDATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\treturn err\n}\n\ntype MapCreateAttr struct {\n\t_                     structs.HostLayout\n\tMapType               MapType\n\tKeySize               uint32\n\tValueSize             uint32\n\tMaxEntries            uint32\n\tMapFlags              uint32\n\tInnerMapFd            uint32\n\tNumaNode              uint32\n\tMapName               ObjName\n\tMapIfindex            uint32\n\tBtfFd                 uint32\n\tBtfKeyTypeId          TypeID\n\tBtfValueTypeId        TypeID\n\tBtfVmlinuxValueTypeId TypeID\n\tMapExtra              uint64\n\tValueTypeBtfObjFd     int32\n\tMapTokenFd            int32\n\tExclProgHash          uint64\n\tExclProgHashSize      uint32\n\t_                     [4]byte\n}\n\nfunc MapCreate(attr *MapCreateAttr) (*FD, error) {\n\tfd, err := BPF(BPF_MAP_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewFD(int(fd))\n}\n\ntype MapDeleteBatchAttr struct {\n\t_         structs.HostLayout\n\tInBatch   Pointer\n\tOutBatch  Pointer\n\tKeys      Pointer\n\tValues    Pointer\n\tCount     uint32\n\tMapFd     uint32\n\tElemFlags uint64\n\tFlags     uint64\n}\n\nfunc MapDeleteBatch(attr *MapDeleteBatchAttr) error {\n\t_, err := BPF(BPF_MAP_DELETE_BATCH, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\treturn err\n}\n\ntype MapDeleteElemAttr struct {\n\t_     structs.HostLayout\n\tMapFd uint32\n\t_     [4]byte\n\tKey   Pointer\n\tValue Pointer\n\tFlags uint64\n}\n\nfunc MapDeleteElem(attr *MapDeleteElemAttr) error {\n\t_, err := BPF(BPF_MAP_DELETE_ELEM, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\treturn err\n}\n\ntype MapFreezeAttr struct {\n\t_     structs.HostLayout\n\tMapFd uint32\n}\n\nfunc MapFreeze(attr *MapFreezeAttr) error {\n\t_, err := BPF(BPF_MAP_FREEZE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\treturn err\n}\n\ntype MapGetFdByIdAttr struct {\n\t_  structs.HostLayout\n\tId uint32\n}\n\nfunc MapGetFdById(attr *MapGetFdByIdAttr) (*FD, error) {\n\tfd, err := BPF(BPF_MAP_GET_FD_BY_ID, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewFD(int(fd))\n}\n\ntype MapGetNextIdAttr struct {\n\t_      structs.HostLayout\n\tId     uint32\n\tNextId uint32\n}\n\nfunc MapGetNextId(attr *MapGetNextIdAttr) error {\n\t_, err := BPF(BPF_MAP_GET_NEXT_ID, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\treturn err\n}\n\ntype MapGetNextKeyAttr struct {\n\t_       structs.HostLayout\n\tMapFd   uint32\n\t_       [4]byte\n\tKey     Pointer\n\tNextKey Pointer\n}\n\nfunc MapGetNextKey(attr *MapGetNextKeyAttr) error {\n\t_, err := BPF(BPF_MAP_GET_NEXT_KEY, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\treturn err\n}\n\ntype MapLookupAndDeleteBatchAttr struct {\n\t_         structs.HostLayout\n\tInBatch   Pointer\n\tOutBatch  Pointer\n\tKeys      Pointer\n\tValues    Pointer\n\tCount     uint32\n\tMapFd     uint32\n\tElemFlags uint64\n\tFlags     uint64\n}\n\nfunc MapLookupAndDeleteBatch(attr *MapLookupAndDeleteBatchAttr) error {\n\t_, err := BPF(BPF_MAP_LOOKUP_AND_DELETE_BATCH, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\treturn err\n}\n\ntype MapLookupAndDeleteElemAttr struct {\n\t_     structs.HostLayout\n\tMapFd uint32\n\t_     [4]byte\n\tKey   Pointer\n\tValue Pointer\n\tFlags uint64\n}\n\nfunc MapLookupAndDeleteElem(attr *MapLookupAndDeleteElemAttr) error {\n\t_, err := BPF(BPF_MAP_LOOKUP_AND_DELETE_ELEM, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\treturn err\n}\n\ntype MapLookupBatchAttr struct {\n\t_         structs.HostLayout\n\tInBatch   Pointer\n\tOutBatch  Pointer\n\tKeys      Pointer\n\tValues    Pointer\n\tCount     uint32\n\tMapFd     uint32\n\tElemFlags uint64\n\tFlags     uint64\n}\n\nfunc MapLookupBatch(attr *MapLookupBatchAttr) error {\n\t_, err := BPF(BPF_MAP_LOOKUP_BATCH, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\treturn err\n}\n\ntype MapLookupElemAttr struct {\n\t_     structs.HostLayout\n\tMapFd uint32\n\t_     [4]byte\n\tKey   Pointer\n\tValue Pointer\n\tFlags uint64\n}\n\nfunc MapLookupElem(attr *MapLookupElemAttr) error {\n\t_, err := BPF(BPF_MAP_LOOKUP_ELEM, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\treturn err\n}\n\ntype MapUpdateBatchAttr struct {\n\t_         structs.HostLayout\n\tInBatch   Pointer\n\tOutBatch  Pointer\n\tKeys      Pointer\n\tValues    Pointer\n\tCount     uint32\n\tMapFd     uint32\n\tElemFlags uint64\n\tFlags     uint64\n}\n\nfunc MapUpdateBatch(attr *MapUpdateBatchAttr) error {\n\t_, err := BPF(BPF_MAP_UPDATE_BATCH, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\treturn err\n}\n\ntype MapUpdateElemAttr struct {\n\t_     structs.HostLayout\n\tMapFd uint32\n\t_     [4]byte\n\tKey   Pointer\n\tValue Pointer\n\tFlags uint64\n}\n\nfunc MapUpdateElem(attr *MapUpdateElemAttr) error {\n\t_, err := BPF(BPF_MAP_UPDATE_ELEM, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\treturn err\n}\n\ntype ObjGetAttr struct {\n\t_         structs.HostLayout\n\tPathname  StringPointer\n\tBpfFd     uint32\n\tFileFlags uint32\n\tPathFd    int32\n\t_         [4]byte\n}\n\nfunc ObjGet(attr *ObjGetAttr) (*FD, error) {\n\tfd, err := BPF(BPF_OBJ_GET, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewFD(int(fd))\n}\n\ntype ObjGetInfoByFdAttr struct {\n\t_       structs.HostLayout\n\tBpfFd   uint32\n\tInfoLen uint32\n\tInfo    Pointer\n}\n\nfunc ObjGetInfoByFd(attr *ObjGetInfoByFdAttr) error {\n\t_, err := BPF(BPF_OBJ_GET_INFO_BY_FD, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\treturn err\n}\n\ntype ObjPinAttr struct {\n\t_         structs.HostLayout\n\tPathname  StringPointer\n\tBpfFd     uint32\n\tFileFlags uint32\n\tPathFd    int32\n\t_         [4]byte\n}\n\nfunc ObjPin(attr *ObjPinAttr) error {\n\t_, err := BPF(BPF_OBJ_PIN, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\treturn err\n}\n\ntype ProgAttachAttr struct {\n\t_                 structs.HostLayout\n\tTargetFdOrIfindex uint32\n\tAttachBpfFd       uint32\n\tAttachType        uint32\n\tAttachFlags       uint32\n\tReplaceBpfFd      uint32\n\tRelativeFdOrId    uint32\n\tExpectedRevision  uint64\n}\n\nfunc ProgAttach(attr *ProgAttachAttr) error {\n\t_, err := BPF(BPF_PROG_ATTACH, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\treturn err\n}\n\ntype ProgBindMapAttr struct {\n\t_      structs.HostLayout\n\tProgFd uint32\n\tMapFd  uint32\n\tFlags  uint32\n}\n\nfunc ProgBindMap(attr *ProgBindMapAttr) error {\n\t_, err := BPF(BPF_PROG_BIND_MAP, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\treturn err\n}\n\ntype ProgDetachAttr struct {\n\t_                 structs.HostLayout\n\tTargetFdOrIfindex uint32\n\tAttachBpfFd       uint32\n\tAttachType        uint32\n\tAttachFlags       uint32\n\t_                 [4]byte\n\tRelativeFdOrId    uint32\n\tExpectedRevision  uint64\n}\n\nfunc ProgDetach(attr *ProgDetachAttr) error {\n\t_, err := BPF(BPF_PROG_DETACH, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\treturn err\n}\n\ntype ProgGetFdByIdAttr struct {\n\t_  structs.HostLayout\n\tId uint32\n}\n\nfunc ProgGetFdById(attr *ProgGetFdByIdAttr) (*FD, error) {\n\tfd, err := BPF(BPF_PROG_GET_FD_BY_ID, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewFD(int(fd))\n}\n\ntype ProgGetNextIdAttr struct {\n\t_      structs.HostLayout\n\tId     uint32\n\tNextId uint32\n}\n\nfunc ProgGetNextId(attr *ProgGetNextIdAttr) error {\n\t_, err := BPF(BPF_PROG_GET_NEXT_ID, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\treturn err\n}\n\ntype ProgLoadAttr struct {\n\t_                  structs.HostLayout\n\tProgType           ProgType\n\tInsnCnt            uint32\n\tInsns              TypedPointer[uint8]\n\tLicense            StringPointer\n\tLogLevel           LogLevel\n\tLogSize            uint32\n\tLogBuf             TypedPointer[uint8]\n\tKernVersion        uint32\n\tProgFlags          uint32\n\tProgName           ObjName\n\tProgIfindex        uint32\n\tExpectedAttachType AttachType\n\tProgBtfFd          uint32\n\tFuncInfoRecSize    uint32\n\tFuncInfo           TypedPointer[uint8]\n\tFuncInfoCnt        uint32\n\tLineInfoRecSize    uint32\n\tLineInfo           TypedPointer[uint8]\n\tLineInfoCnt        uint32\n\tAttachBtfId        TypeID\n\tAttachBtfObjFd     uint32\n\tCoreReloCnt        uint32\n\tFdArray            TypedPointer[int32]\n\tCoreRelos          TypedPointer[uint8]\n\tCoreReloRecSize    uint32\n\tLogTrueSize        uint32\n\tProgTokenFd        int32\n\tFdArrayCnt         uint32\n\tSignature          uint64\n\tSignatureSize      uint32\n\tKeyringId          int32\n}\n\nfunc ProgLoad(attr *ProgLoadAttr) (*FD, error) {\n\tfd, err := BPF(BPF_PROG_LOAD, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewFD(int(fd))\n}\n\ntype ProgQueryAttr struct {\n\t_                 structs.HostLayout\n\tTargetFdOrIfindex uint32\n\tAttachType        AttachType\n\tQueryFlags        uint32\n\tAttachFlags       uint32\n\tProgIds           TypedPointer[ProgramID]\n\tCount             uint32\n\t_                 [4]byte\n\tProgAttachFlags   TypedPointer[ProgramID]\n\tLinkIds           TypedPointer[LinkID]\n\tLinkAttachFlags   TypedPointer[LinkID]\n\tRevision          uint64\n}\n\nfunc ProgQuery(attr *ProgQueryAttr) error {\n\t_, err := BPF(BPF_PROG_QUERY, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\treturn err\n}\n\ntype ProgRunAttr struct {\n\t_           structs.HostLayout\n\tProgFd      uint32\n\tRetval      uint32\n\tDataSizeIn  uint32\n\tDataSizeOut uint32\n\tDataIn      TypedPointer[uint8]\n\tDataOut     TypedPointer[uint8]\n\tRepeat      uint32\n\tDuration    uint32\n\tCtxSizeIn   uint32\n\tCtxSizeOut  uint32\n\tCtxIn       TypedPointer[uint8]\n\tCtxOut      TypedPointer[uint8]\n\tFlags       uint32\n\tCpu         uint32\n\tBatchSize   uint32\n\t_           [4]byte\n}\n\nfunc ProgRun(attr *ProgRunAttr) error {\n\t_, err := BPF(BPF_PROG_TEST_RUN, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\treturn err\n}\n\ntype RawTracepointOpenAttr struct {\n\t_      structs.HostLayout\n\tName   StringPointer\n\tProgFd uint32\n\t_      [4]byte\n\tCookie uint64\n}\n\nfunc RawTracepointOpen(attr *RawTracepointOpenAttr) (*FD, error) {\n\tfd, err := BPF(BPF_RAW_TRACEPOINT_OPEN, unsafe.Pointer(attr), unsafe.Sizeof(*attr))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewFD(int(fd))\n}\n\ntype CgroupLinkInfo struct {\n\t_          structs.HostLayout\n\tType       LinkType\n\tId         LinkID\n\tProgId     uint32\n\t_          [4]byte\n\tCgroupId   uint64\n\tAttachType AttachType\n\t_          [36]byte\n}\n\ntype EventLinkInfo struct {\n\t_             structs.HostLayout\n\tType          LinkType\n\tId            LinkID\n\tProgId        uint32\n\t_             [4]byte\n\tPerfEventType PerfEventType\n\t_             [4]byte\n\tConfig        uint64\n\tEventType     uint32\n\t_             [4]byte\n\tCookie        uint64\n\t_             [16]byte\n}\n\ntype IterLinkInfo struct {\n\t_             structs.HostLayout\n\tType          LinkType\n\tId            LinkID\n\tProgId        uint32\n\t_             [4]byte\n\tTargetName    TypedPointer[uint8]\n\tTargetNameLen uint32\n}\n\ntype KprobeLinkInfo struct {\n\t_             structs.HostLayout\n\tType          LinkType\n\tId            LinkID\n\tProgId        uint32\n\t_             [4]byte\n\tPerfEventType PerfEventType\n\t_             [4]byte\n\tFuncName      TypedPointer[uint8]\n\tNameLen       uint32\n\tOffset        uint32\n\tAddr          uint64\n\tMissed        uint64\n\tCookie        uint64\n}\n\ntype KprobeMultiLinkInfo struct {\n\t_       structs.HostLayout\n\tType    LinkType\n\tId      LinkID\n\tProgId  uint32\n\t_       [4]byte\n\tAddrs   TypedPointer[uint64]\n\tCount   uint32\n\tFlags   uint32\n\tMissed  uint64\n\tCookies TypedPointer[uint64]\n\t_       [16]byte\n}\n\ntype NetNsLinkInfo struct {\n\t_          structs.HostLayout\n\tType       LinkType\n\tId         LinkID\n\tProgId     uint32\n\t_          [4]byte\n\tNetnsIno   uint32\n\tAttachType AttachType\n\t_          [40]byte\n}\n\ntype NetfilterLinkInfo struct {\n\t_        structs.HostLayout\n\tType     LinkType\n\tId       LinkID\n\tProgId   uint32\n\t_        [4]byte\n\tPf       NetfilterProtocolFamily\n\tHooknum  NetfilterInetHook\n\tPriority int32\n\tFlags    uint32\n\t_        [32]byte\n}\n\ntype NetkitLinkInfo struct {\n\t_          structs.HostLayout\n\tType       LinkType\n\tId         LinkID\n\tProgId     uint32\n\t_          [4]byte\n\tIfindex    uint32\n\tAttachType AttachType\n\t_          [40]byte\n}\n\ntype PerfEventLinkInfo struct {\n\t_             structs.HostLayout\n\tType          LinkType\n\tId            LinkID\n\tProgId        uint32\n\t_             [4]byte\n\tPerfEventType PerfEventType\n}\n\ntype RawTracepointLinkInfo struct {\n\t_         structs.HostLayout\n\tType      LinkType\n\tId        LinkID\n\tProgId    uint32\n\t_         [4]byte\n\tTpName    TypedPointer[uint8]\n\tTpNameLen uint32\n\t_         [4]byte\n\tCookie    uint64\n\t_         [24]byte\n}\n\ntype TcxLinkInfo struct {\n\t_          structs.HostLayout\n\tType       LinkType\n\tId         LinkID\n\tProgId     uint32\n\t_          [4]byte\n\tIfindex    uint32\n\tAttachType AttachType\n\t_          [40]byte\n}\n\ntype TracepointLinkInfo struct {\n\t_             structs.HostLayout\n\tType          LinkType\n\tId            LinkID\n\tProgId        uint32\n\t_             [4]byte\n\tPerfEventType PerfEventType\n\t_             [4]byte\n\tTpName        TypedPointer[uint8]\n\tNameLen       uint32\n\t_             [4]byte\n\tCookie        uint64\n\t_             [16]byte\n}\n\ntype TracingLinkInfo struct {\n\t_           structs.HostLayout\n\tType        LinkType\n\tId          LinkID\n\tProgId      uint32\n\t_           [4]byte\n\tAttachType  AttachType\n\tTargetObjId uint32\n\tTargetBtfId TypeID\n\t_           [4]byte\n\tCookie      uint64\n\t_           [24]byte\n}\n\ntype UprobeLinkInfo struct {\n\t_             structs.HostLayout\n\tType          LinkType\n\tId            LinkID\n\tProgId        uint32\n\t_             [4]byte\n\tPerfEventType PerfEventType\n\t_             [4]byte\n\tFileName      TypedPointer[uint8]\n\tNameLen       uint32\n\tOffset        uint32\n\tCookie        uint64\n\tRefCtrOffset  uint64\n\t_             [8]byte\n}\n\ntype UprobeMultiLinkInfo struct {\n\t_             structs.HostLayout\n\tType          LinkType\n\tId            LinkID\n\tProgId        uint32\n\t_             [4]byte\n\tPath          TypedPointer[uint8]\n\tOffsets       TypedPointer[uint64]\n\tRefCtrOffsets TypedPointer[uint64]\n\tCookies       TypedPointer[uint64]\n\tPathSize      uint32\n\tCount         uint32\n\tFlags         uint32\n\tPid           uint32\n}\n\ntype XDPLinkInfo struct {\n\t_       structs.HostLayout\n\tType    LinkType\n\tId      LinkID\n\tProgId  uint32\n\t_       [4]byte\n\tIfindex uint32\n\t_       [44]byte\n}\n"
  },
  {
    "path": "internal/sysenc/buffer.go",
    "content": "package sysenc\n\nimport (\n\t\"unsafe\"\n\n\t\"github.com/cilium/ebpf/internal/sys\"\n)\n\ntype Buffer struct {\n\tptr unsafe.Pointer\n\t// Size of the buffer. syscallPointerOnly if created from UnsafeBuffer or when using\n\t// zero-copy unmarshaling.\n\tsize int\n}\n\nconst syscallPointerOnly = -1\n\nfunc newBuffer(buf []byte) Buffer {\n\tif len(buf) == 0 {\n\t\treturn Buffer{}\n\t}\n\treturn Buffer{unsafe.Pointer(&buf[0]), len(buf)}\n}\n\n// UnsafeBuffer constructs a Buffer for zero-copy unmarshaling.\n//\n// [Pointer] is the only valid method to call on such a Buffer.\n// Use [SyscallBuffer] instead if possible.\nfunc UnsafeBuffer(ptr unsafe.Pointer) Buffer {\n\treturn Buffer{ptr, syscallPointerOnly}\n}\n\n// SyscallOutput prepares a Buffer for a syscall to write into.\n//\n// size is the length of the desired buffer in bytes.\n// The buffer may point at the underlying memory of dst, in which case [Unmarshal]\n// becomes a no-op.\n//\n// The contents of the buffer are undefined and may be non-zero.\nfunc SyscallOutput(dst any, size int) Buffer {\n\tif dstBuf := unsafeBackingMemory(dst); len(dstBuf) == size {\n\t\tbuf := newBuffer(dstBuf)\n\t\tbuf.size = syscallPointerOnly\n\t\treturn buf\n\t}\n\n\treturn newBuffer(make([]byte, size))\n}\n\n// CopyTo copies the buffer into dst.\n//\n// Returns the number of copied bytes.\nfunc (b Buffer) CopyTo(dst []byte) int {\n\treturn copy(dst, b.Bytes())\n}\n\n// AppendTo appends the buffer onto dst.\nfunc (b Buffer) AppendTo(dst []byte) []byte {\n\treturn append(dst, b.Bytes()...)\n}\n\n// Pointer returns the location where a syscall should write.\nfunc (b Buffer) Pointer() sys.Pointer {\n\t// NB: This deliberately ignores b.length to support zero-copy\n\t// marshaling / unmarshaling using unsafe.Pointer.\n\treturn sys.UnsafePointer(b.ptr)\n}\n\n// Unmarshal the buffer into the provided value.\nfunc (b Buffer) Unmarshal(data any) error {\n\tif b.size == syscallPointerOnly {\n\t\treturn nil\n\t}\n\n\treturn Unmarshal(data, b.Bytes())\n}\n\n// Bytes returns the buffer as a byte slice. Returns nil if the Buffer was\n// created using UnsafeBuffer or by zero-copy unmarshaling.\nfunc (b Buffer) Bytes() []byte {\n\tif b.size == syscallPointerOnly {\n\t\treturn nil\n\t}\n\treturn unsafe.Slice((*byte)(b.ptr), b.size)\n}\n"
  },
  {
    "path": "internal/sysenc/buffer_test.go",
    "content": "package sysenc_test\n\nimport (\n\t\"testing\"\n\t\"unsafe\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/sysenc\"\n)\n\nfunc TestZeroBuffer(t *testing.T) {\n\tvar zero sysenc.Buffer\n\n\tqt.Assert(t, qt.Equals(zero.CopyTo(make([]byte, 1)), 0))\n\tqt.Assert(t, qt.Equals(zero.Pointer(), sys.Pointer{}))\n\tqt.Assert(t, qt.IsNotNil(zero.Unmarshal(new(uint16))))\n}\n\nfunc TestUnsafeBuffer(t *testing.T) {\n\tptr := unsafe.Pointer(new(uint16))\n\tbuf := sysenc.UnsafeBuffer(ptr)\n\n\tqt.Assert(t, qt.Equals(buf.CopyTo(make([]byte, 1)), 0))\n\tqt.Assert(t, qt.Equals(buf.Pointer(), sys.UnsafePointer(ptr)))\n\tqt.Assert(t, qt.IsNil(buf.Unmarshal(new(uint16))))\n}\n"
  },
  {
    "path": "internal/sysenc/doc.go",
    "content": "// Package sysenc provides efficient conversion of Go values to system\n// call interfaces.\npackage sysenc\n"
  },
  {
    "path": "internal/sysenc/layout.go",
    "content": "// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found at https://go.dev/LICENSE.\n\npackage sysenc\n\nimport (\n\t\"reflect\"\n\t\"sync\"\n)\n\nvar hasUnexportedFieldsCache sync.Map // map[reflect.Type]bool\n\nfunc hasUnexportedFields(typ reflect.Type) bool {\n\tswitch typ.Kind() {\n\tcase reflect.Slice, reflect.Array, reflect.Pointer:\n\t\treturn hasUnexportedFields(typ.Elem())\n\n\tcase reflect.Struct:\n\t\tif unexported, ok := hasUnexportedFieldsCache.Load(typ); ok {\n\t\t\treturn unexported.(bool)\n\t\t}\n\n\t\tunexported := false\n\t\tfor i, n := 0, typ.NumField(); i < n; i++ {\n\t\t\tfield := typ.Field(i)\n\t\t\t// Package binary allows _ fields but always writes zeroes into them.\n\t\t\tif (!field.IsExported() && field.Name != \"_\") || hasUnexportedFields(field.Type) {\n\t\t\t\tunexported = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\thasUnexportedFieldsCache.Store(typ, unexported)\n\t\treturn unexported\n\n\tdefault:\n\t\t// NB: It's not clear what this means for Chan and so on.\n\t\treturn false\n\t}\n}\n"
  },
  {
    "path": "internal/sysenc/layout_test.go",
    "content": "package sysenc\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestHasUnexportedFields(t *testing.T) {\n\tfor _, test := range []struct {\n\t\tvalue  any\n\t\tresult bool\n\t}{\n\t\t{struct{ A any }{}, false},\n\t\t{(*struct{ A any })(nil), false},\n\t\t{([]struct{ A any })(nil), false},\n\t\t{[1]struct{ A any }{}, false},\n\t\t{struct{ _ any }{}, false},\n\t\t{struct{ _ struct{ a any } }{}, true},\n\t\t{(*struct{ _ any })(nil), false},\n\t\t{([]struct{ _ any })(nil), false},\n\t\t{[1]struct{ _ any }{}, false},\n\t\t{struct{ a any }{}, true},\n\t\t{(*struct{ a any })(nil), true},\n\t\t{([]struct{ a any })(nil), true},\n\t\t{[1]struct{ a any }{}, true},\n\t\t{(*struct{ A []struct{ a any } })(nil), true},\n\t\t{(*struct{ A [1]struct{ a any } })(nil), true},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"%T\", test.value), func(t *testing.T) {\n\t\t\thave := hasUnexportedFields(reflect.TypeOf(test.value))\n\t\t\tqt.Assert(t, qt.Equals(have, test.result))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/sysenc/marshal.go",
    "content": "package sysenc\n\nimport (\n\t\"encoding\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"slices\"\n\t\"unsafe\"\n\n\t\"github.com/cilium/ebpf/internal\"\n)\n\n// Marshal turns data into a byte slice using the system's native endianness.\n//\n// If possible, avoids allocations by directly using the backing memory\n// of data. This means that the variable must not be modified for the lifetime\n// of the returned [Buffer].\n//\n// Returns an error if the data can't be turned into a byte slice according to\n// the behaviour of [binary.Write].\nfunc Marshal(data any, size int) (Buffer, error) {\n\tif data == nil {\n\t\treturn Buffer{}, errors.New(\"can't marshal a nil value\")\n\t}\n\n\tvar buf []byte\n\tvar err error\n\tswitch value := data.(type) {\n\tcase encoding.BinaryMarshaler:\n\t\tbuf, err = value.MarshalBinary()\n\tcase string:\n\t\tbuf = unsafe.Slice(unsafe.StringData(value), len(value))\n\tcase []byte:\n\t\tbuf = value\n\tcase int16:\n\t\tbuf = internal.NativeEndian.AppendUint16(make([]byte, 0, 2), uint16(value))\n\tcase uint16:\n\t\tbuf = internal.NativeEndian.AppendUint16(make([]byte, 0, 2), value)\n\tcase int32:\n\t\tbuf = internal.NativeEndian.AppendUint32(make([]byte, 0, 4), uint32(value))\n\tcase uint32:\n\t\tbuf = internal.NativeEndian.AppendUint32(make([]byte, 0, 4), value)\n\tcase int64:\n\t\tbuf = internal.NativeEndian.AppendUint64(make([]byte, 0, 8), uint64(value))\n\tcase uint64:\n\t\tbuf = internal.NativeEndian.AppendUint64(make([]byte, 0, 8), value)\n\tdefault:\n\t\tif buf := unsafeBackingMemory(data); len(buf) == size {\n\t\t\treturn newBuffer(buf), nil\n\t\t}\n\n\t\tbuf, err = binary.Append(nil, internal.NativeEndian, value)\n\t}\n\tif err != nil {\n\t\treturn Buffer{}, err\n\t}\n\n\tif len(buf) != size {\n\t\treturn Buffer{}, fmt.Errorf(\"%T doesn't marshal to %d bytes\", data, size)\n\t}\n\n\treturn newBuffer(buf), nil\n}\n\n// Unmarshal a byte slice in the system's native endianness into data.\n//\n// Returns an error if buf can't be unmarshalled according to the behaviour\n// of [binary.Decode].\nfunc Unmarshal(data interface{}, buf []byte) error {\n\tswitch value := data.(type) {\n\tcase encoding.BinaryUnmarshaler:\n\t\treturn value.UnmarshalBinary(buf)\n\n\tcase *string:\n\t\t*value = string(buf)\n\t\treturn nil\n\n\tcase *[]byte:\n\t\t// Backwards compat: unmarshaling into a slice replaces the whole slice.\n\t\t*value = slices.Clone(buf)\n\t\treturn nil\n\n\tdefault:\n\t\tif dataBuf := unsafeBackingMemory(data); len(dataBuf) == len(buf) {\n\t\t\tcopy(dataBuf, buf)\n\t\t\treturn nil\n\t\t}\n\n\t\tn, err := binary.Decode(buf, internal.NativeEndian, value)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif n != len(buf) {\n\t\t\treturn fmt.Errorf(\"unmarshaling %T doesn't consume all data\", data)\n\t\t}\n\n\t\treturn nil\n\t}\n}\n\n// unsafeBackingMemory returns the backing memory of data if it can be used\n// instead of calling into package binary.\n//\n// Returns nil if the value is not a pointer or a slice, or if it contains\n// padding or unexported fields.\nfunc unsafeBackingMemory(data any) []byte {\n\tif data == nil {\n\t\treturn nil\n\t}\n\n\tvalue := reflect.ValueOf(data)\n\tvar valueSize int\n\tswitch value.Kind() {\n\tcase reflect.Pointer:\n\t\tif value.IsNil() {\n\t\t\treturn nil\n\t\t}\n\n\t\tif elemType := value.Type().Elem(); elemType.Kind() != reflect.Slice {\n\t\t\tvalueSize = int(elemType.Size())\n\t\t\tbreak\n\t\t}\n\n\t\t// We're dealing with a pointer to a slice. Dereference and\n\t\t// handle it like a regular slice.\n\t\tvalue = value.Elem()\n\t\tfallthrough\n\n\tcase reflect.Slice:\n\t\tvalueSize = int(value.Type().Elem().Size()) * value.Len()\n\n\tdefault:\n\t\t// Prevent Value.UnsafePointer from panicking.\n\t\treturn nil\n\t}\n\n\t// Some nil pointer types currently crash binary.Size. Call it after our own\n\t// code so that the panic isn't reachable.\n\t// See https://github.com/golang/go/issues/60892\n\tif size := binary.Size(data); size == -1 || size != valueSize {\n\t\t// The type contains padding or unsupported types.\n\t\treturn nil\n\t}\n\n\tif hasUnexportedFields(reflect.TypeOf(data)) {\n\t\treturn nil\n\t}\n\n\t// Reinterpret the pointer as a byte slice. This violates the unsafe.Pointer\n\t// rules because it's very unlikely that the source data has \"an equivalent\n\t// memory layout\". However, we can make it safe-ish because of the\n\t// following reasons:\n\t//  - There is no alignment mismatch since we cast to a type with an\n\t//    alignment of 1.\n\t//  - There are no pointers in the source type so we don't upset the GC.\n\t//  - The length is verified at runtime.\n\treturn unsafe.Slice((*byte)(value.UnsafePointer()), valueSize)\n}\n"
  },
  {
    "path": "internal/sysenc/marshal_test.go",
    "content": "package sysenc\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"math\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\t\"github.com/google/go-cmp/cmp/cmpopts\"\n\n\t\"github.com/cilium/ebpf/internal\"\n)\n\ntype testcase struct {\n\tnew        func() any\n\tzeroAllocs bool // marshaling does not allocate\n}\n\ntype struc struct {\n\tA uint64\n\tB uint32\n}\n\ntype explicitPad struct {\n\t_ uint32\n}\n\nfunc testcases() []testcase {\n\treturn []testcase{\n\t\t{func() any { return new([1]uint64) }, true},\n\t\t{func() any { return new(int16) }, true},\n\t\t{func() any { return new(uint16) }, true},\n\t\t{func() any { return new(int32) }, true},\n\t\t{func() any { return new(uint32) }, true},\n\t\t{func() any { return new(int64) }, true},\n\t\t{func() any { return new(uint64) }, true},\n\t\t{func() any { return make([]byte, 9) }, true},\n\t\t{func() any { return new(explicitPad) }, true},\n\t\t{func() any { return make([]explicitPad, 0) }, false},\n\t\t{func() any { return make([]explicitPad, 1) }, false},\n\t\t{func() any { return make([]explicitPad, 2) }, false},\n\t\t{func() any { return new(struc) }, false},\n\t\t{func() any { return make([]struc, 0) }, false},\n\t\t{func() any { return make([]struc, 1) }, false},\n\t\t{func() any { return make([]struc, 2) }, false},\n\t\t{func() any { return int16(math.MaxInt16) }, false},\n\t\t{func() any { return uint16(math.MaxUint16) }, false},\n\t\t{func() any { return int32(math.MaxInt32) }, false},\n\t\t{func() any { return uint32(math.MaxUint32) }, false},\n\t\t{func() any { return int64(math.MaxInt64) }, false},\n\t\t{func() any { return uint64(math.MaxUint64) }, false},\n\t\t{func() any { return struc{math.MaxUint64, math.MaxUint32} }, false},\n\t}\n}\n\nfunc TestMarshal(t *testing.T) {\n\tfor _, test := range testcases() {\n\t\tvalue := test.new()\n\t\tt.Run(fmt.Sprintf(\"%T\", value), func(t *testing.T) {\n\t\t\twant, err := binary.Append(nil, internal.NativeEndian, value)\n\t\t\tqt.Assert(t, qt.IsNil(err))\n\n\t\t\thave := make([]byte, len(want))\n\t\t\tbuf, err := Marshal(value, binary.Size(value))\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tqt.Assert(t, qt.Equals(buf.CopyTo(have), len(want)))\n\t\t\tqt.Assert(t, qt.CmpEquals(have, want, cmpopts.EquateEmpty()))\n\t\t})\n\t}\n}\n\nfunc TestMarshalAllocations(t *testing.T) {\n\tallocationsPerMarshal := func(t *testing.T, data any) float64 {\n\t\tsize := binary.Size(data)\n\t\treturn testing.AllocsPerRun(5, func() {\n\t\t\t_, err := Marshal(data, size)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t})\n\t}\n\n\tfor _, test := range testcases() {\n\t\tif !test.zeroAllocs {\n\t\t\tcontinue\n\t\t}\n\n\t\tvalue := test.new()\n\t\tt.Run(fmt.Sprintf(\"%T\", value), func(t *testing.T) {\n\t\t\tqt.Assert(t, qt.Equals(allocationsPerMarshal(t, value), 0))\n\t\t})\n\t}\n}\n\nfunc TestUnmarshal(t *testing.T) {\n\tfor _, test := range testcases() {\n\t\tvalue := test.new()\n\t\tif !canUnmarshalInto(value) {\n\t\t\tcontinue\n\t\t}\n\n\t\tt.Run(fmt.Sprintf(\"%T\", value), func(t *testing.T) {\n\t\t\twant := test.new()\n\t\t\tbuf := randomiseValue(t, want)\n\n\t\t\tqt.Assert(t, qt.IsNil(Unmarshal(value, buf)))\n\t\t\tqt.Assert(t, qt.DeepEquals(value, want))\n\t\t})\n\t}\n}\n\nfunc TestUnmarshalAllocations(t *testing.T) {\n\tallocationsPerUnmarshal := func(t *testing.T, data any, buf []byte) float64 {\n\t\treturn testing.AllocsPerRun(5, func() {\n\t\t\terr := Unmarshal(data, buf)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t})\n\t}\n\n\tfor _, test := range testcases() {\n\t\tvalue := test.new()\n\t\tif !canUnmarshalInto(value) {\n\t\t\tcontinue\n\t\t}\n\n\t\tt.Run(fmt.Sprintf(\"%T\", value), func(t *testing.T) {\n\t\t\tbuf := make([]byte, binary.Size(value))\n\t\t\tqt.Assert(t, qt.Equals(allocationsPerUnmarshal(t, value, buf), 0))\n\t\t})\n\t}\n}\n\nfunc TestUnsafeBackingMemory(t *testing.T) {\n\tmarshalNative := func(t *testing.T, data any) []byte {\n\t\tt.Helper()\n\n\t\tbuf, err := binary.Append(nil, internal.NativeEndian, data)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\treturn buf\n\t}\n\n\tfor _, test := range []struct {\n\t\tname  string\n\t\tvalue any\n\t}{\n\t\t{\n\t\t\t\"slice\",\n\t\t\t[]uint32{1, 2},\n\t\t},\n\t\t{\n\t\t\t\"pointer to slice\",\n\t\t\t&[]uint32{2},\n\t\t},\n\t\t{\n\t\t\t\"pointer to array\",\n\t\t\t&[2]uint64{},\n\t\t},\n\t\t{\n\t\t\t\"pointer to int64\",\n\t\t\tnew(int64),\n\t\t},\n\t\t{\n\t\t\t\"pointer to struct\",\n\t\t\t&struct {\n\t\t\t\tA, B uint16\n\t\t\t\tC    uint32\n\t\t\t}{},\n\t\t},\n\t\t{\n\t\t\t\"struct with explicit padding\",\n\t\t\t&struct{ _ uint64 }{},\n\t\t},\n\t} {\n\t\tt.Run(\"valid: \"+test.name, func(t *testing.T) {\n\t\t\twant := marshalNative(t, test.value)\n\t\t\thave := unsafeBackingMemory(test.value)\n\t\t\tqt.Assert(t, qt.DeepEquals(have, want))\n\t\t})\n\t}\n\n\tfor _, test := range []struct {\n\t\tname  string\n\t\tvalue any\n\t}{\n\t\t{\n\t\t\t\"nil\",\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"nil slice\",\n\t\t\t([]byte)(nil),\n\t\t},\n\t\t{\n\t\t\t\"nil pointer\",\n\t\t\t(*uint64)(nil),\n\t\t},\n\t\t{\n\t\t\t\"nil pointer to slice\",\n\t\t\t(*[]uint32)(nil),\n\t\t},\n\t\t{\n\t\t\t\"nil pointer to array\",\n\t\t\t(*[2]uint64)(nil),\n\t\t},\n\t\t{\n\t\t\t\"unexported field\",\n\t\t\t&struct{ a uint64 }{},\n\t\t},\n\t\t{\n\t\t\t\"struct containing pointer\",\n\t\t\t&struct{ A *uint64 }{},\n\t\t},\n\t\t{\n\t\t\t\"struct with trailing padding\",\n\t\t\t&struc{},\n\t\t},\n\t\t{\n\t\t\t\"struct with interspersed padding\",\n\t\t\t&struct {\n\t\t\t\tB uint32\n\t\t\t\tA uint64\n\t\t\t}{},\n\t\t},\n\t\t{\n\t\t\t\"padding between slice entries\",\n\t\t\t&[]struc{{}},\n\t\t},\n\t\t{\n\t\t\t\"padding between array entries\",\n\t\t\t&[2]struc{},\n\t\t},\n\t} {\n\t\tt.Run(\"invalid: \"+test.name, func(t *testing.T) {\n\t\t\tqt.Assert(t, qt.IsNil(unsafeBackingMemory(test.value)))\n\t\t})\n\t}\n}\n\nfunc BenchmarkMarshal(b *testing.B) {\n\tfor _, test := range testcases() {\n\t\tvalue := test.new()\n\t\tb.Run(fmt.Sprintf(\"%T\", value), func(b *testing.B) {\n\t\t\tsize := binary.Size(value)\n\t\t\tb.ReportAllocs()\n\t\t\tfor b.Loop() {\n\t\t\t\t_, _ = Marshal(value, size)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkUnmarshal(b *testing.B) {\n\tfor _, test := range testcases() {\n\t\tvalue := test.new()\n\t\tif !canUnmarshalInto(value) {\n\t\t\tcontinue\n\t\t}\n\n\t\tb.Run(fmt.Sprintf(\"%T\", value), func(b *testing.B) {\n\t\t\tsize := binary.Size(value)\n\t\t\tbuf := make([]byte, size)\n\t\t\tb.ReportAllocs()\n\t\t\tfor b.Loop() {\n\t\t\t\t_ = Unmarshal(value, buf)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc randomiseValue(tb testing.TB, value any) []byte {\n\ttb.Helper()\n\n\tsize := binary.Size(value)\n\tif size == -1 {\n\t\ttb.Fatalf(\"Can't unmarshal into %T\", value)\n\t}\n\n\tbuf := make([]byte, size)\n\tfor i := range buf {\n\t\tbuf[i] = byte(i)\n\t}\n\n\terr := binary.Read(bytes.NewReader(buf), internal.NativeEndian, value)\n\tqt.Assert(tb, qt.IsNil(err))\n\n\treturn buf\n}\n\nfunc canUnmarshalInto(data any) bool {\n\tkind := reflect.TypeOf(data).Kind()\n\treturn kind == reflect.Slice || kind == reflect.Pointer\n}\n"
  },
  {
    "path": "internal/testutils/bpffs_other.go",
    "content": "//go:build !windows\n\npackage testutils\n\nimport (\n\t\"os\"\n\t\"testing\"\n)\n\n// TempBPFFS creates a temporary directory on a BPF FS.\n//\n// The directory is automatically cleaned up at the end of the test run.\nfunc TempBPFFS(tb testing.TB) string {\n\ttb.Helper()\n\n\ttmp, err := os.MkdirTemp(\"/sys/fs/bpf\", \"ebpf-test\")\n\tif err != nil {\n\t\ttb.Fatal(\"Create temporary directory on BPFFS:\", err)\n\t}\n\ttb.Cleanup(func() { os.RemoveAll(tmp) })\n\n\treturn tmp\n}\n"
  },
  {
    "path": "internal/testutils/bpffs_windows.go",
    "content": "package testutils\n\nimport (\n\t\"errors\"\n\t\"math/rand\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/internal/efw\"\n)\n\n// TempBPFFS creates a random prefix to use when pinning on Windows.\nfunc TempBPFFS(tb testing.TB) string {\n\ttb.Helper()\n\n\tpath := filepath.Join(\"ebpf-go-test\", strconv.Itoa(rand.Int()))\n\tpath, err := efw.EbpfCanonicalizePinPath(path)\n\tqt.Assert(tb, qt.IsNil(err))\n\n\ttb.Cleanup(func() {\n\t\ttb.Helper()\n\n\t\tcursor := path\n\t\tfor {\n\t\t\tnext, _, err := efw.EbpfGetNextPinnedObjectPath(cursor, efw.EBPF_OBJECT_UNKNOWN)\n\t\t\tif errors.Is(err, efw.EBPF_NO_MORE_KEYS) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tqt.Assert(tb, qt.IsNil(err))\n\n\t\t\tif !strings.HasPrefix(next, path) {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tif err := efw.EbpfObjectUnpin(next); err != nil {\n\t\t\t\ttb.Errorf(\"Failed to unpin %s: %s\", next, err)\n\t\t\t}\n\n\t\t\tcursor = next\n\t\t}\n\t})\n\n\treturn path\n}\n"
  },
  {
    "path": "internal/testutils/bpffs_windows_test.go",
    "content": "package testutils_test\n\nimport (\n\t\"bytes\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc TestTempBPFFS(t *testing.T) {\n\tvar progPath, mapPath string\n\tt.Run(\"pin\", func(t *testing.T) {\n\t\ttmp := testutils.TempBPFFS(t)\n\t\tprogPath = filepath.Join(tmp, \"prog\")\n\t\tmapPath = filepath.Join(tmp, \"map\")\n\n\t\tvar buffer bytes.Buffer\n\t\tinsns := asm.Instructions{\n\t\t\tasm.LoadImm(asm.R0, 0, asm.DWord),\n\t\t\tasm.Return(),\n\t\t}\n\t\terr := insns.Marshal(&buffer, internal.NativeEndian)\n\t\tqt.Assert(t, qt.IsNil(err))\n\n\t\tprogFd, err := sys.ProgLoad(&sys.ProgLoadAttr{\n\t\t\tProgType: 999, // SAMPLE\n\t\t\tLicense:  sys.NewStringPointer(\"\"),\n\t\t\tInsnCnt:  uint32(buffer.Len() / asm.InstructionSize),\n\t\t\tInsns:    sys.SlicePointer(buffer.Bytes()),\n\t\t})\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tdefer progFd.Close()\n\n\t\terr = sys.ObjPin(&sys.ObjPinAttr{\n\t\t\tBpfFd:    progFd.Uint(),\n\t\t\tPathname: sys.NewStringPointer(progPath),\n\t\t})\n\t\tqt.Assert(t, qt.IsNil(err))\n\n\t\tmapFd, err := sys.MapCreate(&sys.MapCreateAttr{\n\t\t\tMapType:    2, // ARRAY\n\t\t\tKeySize:    4,\n\t\t\tValueSize:  4,\n\t\t\tMaxEntries: 1,\n\t\t})\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tdefer mapFd.Close()\n\n\t\terr = sys.ObjPin(&sys.ObjPinAttr{\n\t\t\tBpfFd:    progFd.Uint(),\n\t\t\tPathname: sys.NewStringPointer(mapPath),\n\t\t})\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.IsNil(mapFd.Close()))\n\t})\n\n\t_, err := sys.ObjGet(&sys.ObjGetAttr{\n\t\tPathname: sys.NewStringPointer(progPath),\n\t})\n\tqt.Assert(t, qt.ErrorIs(err, os.ErrNotExist))\n\n\t_, err = sys.ObjGet(&sys.ObjGetAttr{\n\t\tPathname: sys.NewStringPointer(mapPath),\n\t})\n\tqt.Assert(t, qt.ErrorIs(err, os.ErrNotExist))\n}\n"
  },
  {
    "path": "internal/testutils/cap.go",
    "content": "package testutils\n\nimport (\n\t\"runtime\"\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf/internal/platform\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\ntype Capability int\n\n// Mirrors of constants from x/sys/unix\nconst (\n\tCAP_NET_ADMIN    Capability = 12\n\tCAP_SYS_ADMIN    Capability = 21\n\tCAP_SYS_RESOURCE Capability = 24\n\tCAP_PERFMON      Capability = 38\n\tCAP_BPF          Capability = 39\n)\n\n// WithCapabilities runs `f` with only the given capabilities\n// in the effective set. This allows us to assert that certain operations\n// only require specific capabilities.\n//\n// The code in `f` and any code called by `f` must NOT call [runtime.LockOSThread],\n// as this could leave the current goroutine permanently pinned to an OS thread.\n// It must also not create any goroutines of its own, as that will result in a new\n// OS thread being created that may or may not inherit the new capabilities of its\n// parent, and will later be released into the schedulable pool of threads available\n// for goroutine scheduling.\n//\n// Warning: on non-linux platforms, this function calls through to `f` without\n// side effects.\nfunc WithCapabilities(tb testing.TB, caps []Capability, f func()) {\n\ttb.Helper()\n\n\tif !platform.IsLinux {\n\t\tf()\n\t\treturn\n\t}\n\n\truntime.LockOSThread()\n\tdefer runtime.UnlockOSThread()\n\n\torig, err := capget()\n\tif err != nil {\n\t\ttb.Fatal(\"Can't get capabilities:\", err)\n\t}\n\n\tvar set capUserData\n\tfor _, cap := range caps {\n\t\tset.Effective |= 1 << uint(cap)\n\t}\n\tset.Permitted = orig.Permitted\n\n\tif err := capset(set); err != nil {\n\t\ttb.Fatal(\"Can't set capabilities:\", err)\n\t}\n\n\tf()\n\n\tif err := capset(orig); err != nil {\n\t\ttb.Fatal(\"Can't restore capabilities:\", err)\n\t}\n}\n\ntype capUserData struct {\n\tEffective   uint64\n\tPermitted   uint64\n\tInheritable uint64\n}\n\nfunc capget() (capUserData, error) {\n\tvar hdr = &unix.CapUserHeader{\n\t\tVersion: unix.LINUX_CAPABILITY_VERSION_3,\n\t}\n\n\tvar data [2]unix.CapUserData\n\terr := unix.Capget(hdr, &data[0])\n\tif err != nil {\n\t\treturn capUserData{}, err\n\t}\n\n\treturn capUserData{\n\t\tEffective:   uint64(data[0].Effective) | uint64(data[1].Effective)<<32,\n\t\tPermitted:   uint64(data[0].Permitted) | uint64(data[1].Permitted)<<32,\n\t\tInheritable: uint64(data[0].Inheritable) | uint64(data[1].Inheritable)<<32,\n\t}, err\n}\n\nfunc capset(data capUserData) error {\n\tvar hdr = &unix.CapUserHeader{\n\t\tVersion: unix.LINUX_CAPABILITY_VERSION_3,\n\t}\n\n\tvar linuxData [2]unix.CapUserData\n\tlinuxData[0].Effective = uint32(data.Effective & 0xFFFFFFFF)\n\tlinuxData[0].Permitted = uint32(data.Permitted & 0xFFFFFFFF)\n\tlinuxData[0].Inheritable = uint32(data.Inheritable & 0xFFFFFFFF)\n\tlinuxData[1].Effective = uint32(data.Effective >> 32)\n\tlinuxData[1].Permitted = uint32(data.Permitted >> 32)\n\tlinuxData[1].Inheritable = uint32(data.Inheritable >> 32)\n\n\treturn unix.Capset(hdr, &linuxData[0])\n}\n"
  },
  {
    "path": "internal/testutils/cgroup.go",
    "content": "package testutils\n\nimport (\n\t\"errors\"\n\t\"os\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\nvar cgroup2Path = sync.OnceValues(func() (string, error) {\n\tmounts, err := os.ReadFile(\"/proc/mounts\")\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tfor _, line := range strings.Split(string(mounts), \"\\n\") {\n\t\tmount := strings.SplitN(line, \" \", 3)\n\t\tif mount[0] == \"cgroup2\" {\n\t\t\treturn mount[1], nil\n\t\t}\n\n\t\tcontinue\n\t}\n\n\treturn \"\", errors.New(\"cgroup2 not mounted\")\n})\n\nfunc CreateCgroup(tb testing.TB) *os.File {\n\ttb.Helper()\n\n\tcg2, err := cgroup2Path()\n\tif err != nil {\n\t\ttb.Fatal(\"Can't locate cgroup2 mount:\", err)\n\t}\n\n\tcgdir, err := os.MkdirTemp(cg2, \"ebpf-link\")\n\tif err != nil {\n\t\ttb.Fatal(\"Can't create cgroupv2:\", err)\n\t}\n\n\tcgroup, err := os.Open(cgdir)\n\tif err != nil {\n\t\tos.Remove(cgdir)\n\t\ttb.Fatal(err)\n\t}\n\ttb.Cleanup(func() {\n\t\tcgroup.Close()\n\t\tos.Remove(cgdir)\n\t})\n\n\treturn cgroup\n}\n\nfunc GetCgroupIno(t *testing.T, cgroup *os.File) uint64 {\n\tcgroupStat := unix.Stat_t{}\n\terr := unix.Fstat(int(cgroup.Fd()), &cgroupStat)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\treturn cgroupStat.Ino\n}\n"
  },
  {
    "path": "internal/testutils/chan.go",
    "content": "package testutils\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\n// WaitChan waits for a value to be sent on a channel, or for a timeout to\n// occur. If the timeout is reached, the test will fail.\nfunc WaitChan[T any](tb testing.TB, ch <-chan T, timeout time.Duration) {\n\ttb.Helper()\n\n\tselect {\n\tcase <-ch:\n\t\treturn\n\tcase <-time.After(timeout):\n\t\ttb.Fatalf(\"timeout waiting for channel\")\n\t}\n}\n"
  },
  {
    "path": "internal/testutils/checkers.go",
    "content": "package testutils\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"reflect\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\n// Contains checks if interface value I is of type T. Use with qt.Satisfies:\n//\n//\tqt.Assert(t, qt.Satisfies(p, testutils.Contains[*ebpf.Program]))\nfunc Contains[T, I any](i I) bool {\n\t_, ok := any(i).(T)\n\treturn ok\n}\n\n// IsDeepCopy checks that got is a deep copy of want.\n//\n// All primitive values must be equal, but pointers must be distinct.\n// This is different from [reflect.DeepEqual] which will accept equal pointer values.\n// That is, reflect.DeepEqual(a, a) is true, while IsDeepCopy(a, a) is false.\nfunc IsDeepCopy[T any](got, want T) qt.Checker {\n\treturn &deepCopyChecker[T]{got, want, make(map[pair]struct{})}\n}\n\ntype pair struct {\n\tgot, want reflect.Value\n}\n\ntype deepCopyChecker[T any] struct {\n\tgot, want T\n\tvisited   map[pair]struct{}\n}\n\nfunc (dcc *deepCopyChecker[T]) Check(_ func(key string, value any)) error {\n\treturn dcc.check(reflect.ValueOf(dcc.got), reflect.ValueOf(dcc.want))\n}\n\nfunc (dcc *deepCopyChecker[T]) check(got, want reflect.Value) error {\n\tswitch want.Kind() {\n\tcase reflect.Interface:\n\t\treturn dcc.check(got.Elem(), want.Elem())\n\n\tcase reflect.Pointer:\n\t\tif got.IsNil() && want.IsNil() {\n\t\t\treturn nil\n\t\t}\n\n\t\tif got.IsNil() {\n\t\t\treturn fmt.Errorf(\"expected non-nil pointer\")\n\t\t}\n\n\t\tif want.IsNil() {\n\t\t\treturn fmt.Errorf(\"expected nil pointer\")\n\t\t}\n\n\t\tif got.UnsafePointer() == want.UnsafePointer() {\n\t\t\treturn fmt.Errorf(\"equal pointer values\")\n\t\t}\n\n\t\tswitch want.Type() {\n\t\tcase reflect.TypeOf((*bytes.Reader)(nil)):\n\t\t\t// bytes.Reader doesn't allow modifying it's contents, so we\n\t\t\t// allow a shallow copy.\n\t\t\treturn nil\n\t\t}\n\n\t\tif _, ok := dcc.visited[pair{got, want}]; ok {\n\t\t\t// Deal with recursive types.\n\t\t\treturn nil\n\t\t}\n\n\t\tdcc.visited[pair{got, want}] = struct{}{}\n\t\treturn dcc.check(got.Elem(), want.Elem())\n\n\tcase reflect.Slice:\n\t\tif got.IsNil() && want.IsNil() {\n\t\t\treturn nil\n\t\t}\n\n\t\tif got.IsNil() {\n\t\t\treturn fmt.Errorf(\"expected non-nil slice\")\n\t\t}\n\n\t\tif want.IsNil() {\n\t\t\treturn fmt.Errorf(\"expected nil slice\")\n\t\t}\n\n\t\tif got.Len() != want.Len() {\n\t\t\treturn fmt.Errorf(\"expected %d elements, got %d\", want.Len(), got.Len())\n\t\t}\n\n\t\tif want.Len() == 0 {\n\t\t\treturn nil\n\t\t}\n\n\t\tif got.UnsafePointer() == want.UnsafePointer() {\n\t\t\treturn fmt.Errorf(\"equal backing memory\")\n\t\t}\n\n\t\tfallthrough\n\n\tcase reflect.Array:\n\t\tfor i := 0; i < want.Len(); i++ {\n\t\t\tif err := dcc.check(got.Index(i), want.Index(i)); err != nil {\n\t\t\t\treturn fmt.Errorf(\"index %d: %w\", i, err)\n\t\t\t}\n\t\t}\n\n\t\treturn nil\n\n\tcase reflect.Struct:\n\t\tfor i := 0; i < want.NumField(); i++ {\n\t\t\tif err := dcc.check(got.Field(i), want.Field(i)); err != nil {\n\t\t\t\treturn fmt.Errorf(\"%q: %w\", want.Type().Field(i).Name, err)\n\t\t\t}\n\t\t}\n\n\t\treturn nil\n\n\tcase reflect.Map:\n\t\tif got.Len() != want.Len() {\n\t\t\treturn fmt.Errorf(\"expected %d items, got %d\", want.Len(), got.Len())\n\t\t}\n\n\t\tif got.UnsafePointer() == want.UnsafePointer() {\n\t\t\treturn fmt.Errorf(\"maps are equal\")\n\t\t}\n\n\t\titer := want.MapRange()\n\t\tfor iter.Next() {\n\t\t\tkey := iter.Key()\n\t\t\tgot := got.MapIndex(iter.Key())\n\t\t\tif !got.IsValid() {\n\t\t\t\treturn fmt.Errorf(\"key %v is missing\", key)\n\t\t\t}\n\n\t\t\twant := iter.Value()\n\t\t\tif err := dcc.check(got, want); err != nil {\n\t\t\t\treturn fmt.Errorf(\"key %v: %w\", key, err)\n\t\t\t}\n\t\t}\n\n\t\treturn nil\n\n\tcase reflect.Chan, reflect.UnsafePointer:\n\t\treturn fmt.Errorf(\"%s is not supported\", want.Type())\n\n\tdefault:\n\t\t// Compare by value as usual.\n\t\tif !got.Equal(want) {\n\t\t\treturn fmt.Errorf(\"%#v is not equal to %#v\", got, want)\n\t\t}\n\n\t\treturn nil\n\t}\n}\n\nfunc (dcc *deepCopyChecker[T]) Args() []qt.Arg {\n\treturn []qt.Arg{\n\t\t{Name: \"got\", Value: dcc.got},\n\t\t{Name: \"want\", Value: dcc.want},\n\t}\n}\n"
  },
  {
    "path": "internal/testutils/checkers_test.go",
    "content": "package testutils\n\nimport (\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestIsDeepCopy(t *testing.T) {\n\ttype s struct {\n\t\tbasic  int\n\t\tarray  [1]*int\n\t\tarray0 [0]int\n\t\tptr    *int\n\t\tslice  []*int\n\t\tifc    any\n\t\tm      map[*int]*int\n\t\trec    *s\n\t}\n\n\tkey := 1\n\tcopy := func() *s {\n\t\tv := &s{\n\t\t\t0,\n\t\t\t[...]*int{new(int)},\n\t\t\t[...]int{},\n\t\t\tnew(int),\n\t\t\t[]*int{new(int)},\n\t\t\tnew(int),\n\t\t\tmap[*int]*int{&key: new(int)},\n\t\t\tnil,\n\t\t}\n\t\tv.rec = v\n\t\treturn v\n\t}\n\n\ta, b := copy(), copy()\n\tqt.Check(t, qt.IsNil(IsDeepCopy(a, b).Check(nil)))\n\n\ta.basic++\n\tqt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `\"basic\": .*`))\n\n\ta = copy()\n\t(*a.array[0])++\n\tqt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `\"array\": index 0: .*`))\n\n\ta = copy()\n\ta.array[0] = nil\n\tqt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `\"array\": index 0: .*`))\n\n\ta = copy()\n\ta.array = b.array\n\tqt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `\"array\": index 0: .*`))\n\n\ta = copy()\n\t(*a.ptr)++\n\tqt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `\"ptr\": .*`))\n\n\ta = copy()\n\ta.ptr = b.ptr\n\tqt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `\"ptr\": .*`))\n\n\ta = copy()\n\t(*a.slice[0])++\n\tqt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `\"slice\": .*`))\n\n\ta = copy()\n\ta.slice[0] = nil\n\tqt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `\"slice\": .*`))\n\n\ta = copy()\n\ta.slice = nil\n\tqt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `\"slice\": .*`))\n\n\ta = copy()\n\ta.slice = b.slice\n\tqt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `\"slice\": .*`))\n\n\ta = copy()\n\t*(a.ifc.(*int))++\n\tqt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `\"ifc\": .*`))\n\n\ta = copy()\n\ta.ifc = b.ifc\n\tqt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `\"ifc\": .*`))\n\n\ta = copy()\n\ta.rec = b.rec\n\tqt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `\"rec\": .*`))\n\n\ta = copy()\n\ta.m = b.m\n\tqt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `\"m\": .*`))\n\n\ta = copy()\n\t(*a.m[&key])++\n\tqt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `\"m\": .*`))\n\n\ta = copy()\n\ta.m[new(int)] = new(int)\n\tqt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `\"m\": .*`))\n\n\ta = copy()\n\tdelete(a.m, &key)\n\tqt.Check(t, qt.ErrorMatches(IsDeepCopy(a, b).Check(nil), `\"m\": .*`))\n}\n"
  },
  {
    "path": "internal/testutils/cpu.go",
    "content": "package testutils\n\nimport (\n\t\"runtime\"\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf/internal/unix\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\n// LockOSThreadToSingleCPU force the current goroutine to run on a single CPU.\nfunc LockOSThreadToSingleCPU(tb testing.TB) {\n\ttb.Helper()\n\n\truntime.LockOSThread()\n\ttb.Cleanup(runtime.UnlockOSThread)\n\n\tvar old unix.CPUSet\n\terr := unix.SchedGetaffinity(0, &old)\n\tqt.Assert(tb, qt.IsNil(err))\n\n\t// Schedule test to run on only CPU 0\n\tvar first unix.CPUSet\n\tfirst.Set(0)\n\terr = unix.SchedSetaffinity(0, &first)\n\tqt.Assert(tb, qt.IsNil(err))\n\n\ttb.Cleanup(func() {\n\t\t_ = unix.SchedSetaffinity(0, &old)\n\t})\n}\n"
  },
  {
    "path": "internal/testutils/fd_other.go",
    "content": "//go:build !windows\n\npackage testutils\n\nimport (\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\nfunc DupFD(tb testing.TB, fd int) int {\n\ttb.Helper()\n\n\tdup, err := unix.FcntlInt(uintptr(fd), unix.F_DUPFD_CLOEXEC, 1)\n\tqt.Assert(tb, qt.IsNil(err))\n\n\treturn dup\n}\n"
  },
  {
    "path": "internal/testutils/fd_windows.go",
    "content": "package testutils\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf/internal/efw\"\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc DupFD(tb testing.TB, fd int) int {\n\ttb.Helper()\n\n\tdup, err := efw.EbpfDuplicateFd(fd)\n\tqt.Assert(tb, qt.IsNil(err))\n\n\treturn dup\n}\n"
  },
  {
    "path": "internal/testutils/feature.go",
    "content": "package testutils\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"os\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/platform\"\n)\n\nconst (\n\tignoreVersionEnvVar = \"EBPF_TEST_IGNORE_VERSION\"\n)\n\nfunc CheckFeatureTest(t *testing.T, fn func() error) {\n\tt.Helper()\n\n\tcheckFeatureTestError(t, fn())\n}\n\nfunc checkFeatureTestError(t *testing.T, err error) {\n\tt.Helper()\n\n\tif err == nil {\n\t\treturn\n\t}\n\n\tif errors.Is(err, internal.ErrNotSupportedOnOS) {\n\t\tt.Skip(err)\n\t}\n\n\tvar ufe *internal.UnsupportedFeatureError\n\tif errors.As(err, &ufe) {\n\t\tcheckVersion(t, ufe)\n\t} else {\n\t\tt.Error(\"Feature test failed:\", err)\n\t}\n}\n\nfunc CheckFeatureMatrix[K comparable](t *testing.T, fm internal.FeatureMatrix[K]) {\n\tt.Helper()\n\n\tfor key, ft := range fm {\n\t\tt.Run(ft.Name, func(t *testing.T) {\n\t\t\tcheckFeatureTestError(t, fm.Result(key))\n\t\t})\n\t}\n}\n\nfunc SkipIfNotSupported(tb testing.TB, err error) {\n\ttb.Helper()\n\n\tif err == internal.ErrNotSupported {\n\t\ttb.Fatal(\"Unwrapped ErrNotSupported\")\n\t}\n\n\tvar ufe *internal.UnsupportedFeatureError\n\tif errors.As(err, &ufe) {\n\t\tcheckVersion(tb, ufe)\n\t\ttb.Skip(ufe.Error())\n\t}\n\tif errors.Is(err, internal.ErrNotSupported) {\n\t\ttb.Skip(err.Error())\n\t}\n}\n\nfunc SkipIfNotSupportedOnOS(tb testing.TB, err error) {\n\ttb.Helper()\n\n\tif err == internal.ErrNotSupportedOnOS {\n\t\ttb.Fatal(\"Unwrapped ErrNotSupportedOnOS\")\n\t}\n\n\tif errors.Is(err, internal.ErrNotSupportedOnOS) {\n\t\ttb.Skip(err.Error())\n\t}\n}\n\nfunc checkVersion(tb testing.TB, ufe *internal.UnsupportedFeatureError) {\n\tif ufe.MinimumVersion.Unspecified() {\n\t\treturn\n\t}\n\n\ttb.Helper()\n\n\tif ignoreVersionCheck(tb.Name()) {\n\t\ttb.Logf(\"Ignoring error due to %s: %s\", ignoreVersionEnvVar, ufe.Error())\n\t\treturn\n\t}\n\n\tif !isPlatformVersionLessThan(tb, ufe.MinimumVersion, platformVersion(tb)) {\n\t\ttb.Fatalf(\"Feature '%s' isn't supported even though kernel is newer than %s\",\n\t\t\tufe.Name, ufe.MinimumVersion)\n\t}\n}\n\n// Skip a test based on the Linux version we are running on.\n//\n// Warning: this function does not have an effect on platforms other than Linux.\nfunc SkipOnOldKernel(tb testing.TB, minVersion, feature string) {\n\ttb.Helper()\n\n\tif !platform.IsLinux {\n\t\ttb.Logf(\"Ignoring version constraint %s for %s on %s\", minVersion, feature, runtime.GOOS)\n\t\treturn\n\t}\n\n\tif IsVersionLessThan(tb, minVersion) {\n\t\ttb.Skipf(\"Test requires at least kernel %s (due to missing %s)\", minVersion, feature)\n\t}\n}\n\n// Check whether the current runtime version is less than some minimum.\nfunc IsVersionLessThan(tb testing.TB, minVersions ...string) bool {\n\ttb.Helper()\n\n\tversion, err := platform.SelectVersion(minVersions)\n\tqt.Assert(tb, qt.IsNil(err))\n\n\tif version == \"\" {\n\t\t// No matching version means that the platform\n\t\t// doesn't support whatever feature.\n\t\treturn true\n\t}\n\n\tminv, err := internal.NewVersion(version)\n\tif err != nil {\n\t\ttb.Fatalf(\"Invalid version %s: %s\", version, err)\n\t}\n\n\treturn isPlatformVersionLessThan(tb, minv, platformVersion(tb))\n}\n\nfunc isPlatformVersionLessThan(tb testing.TB, minv, runv internal.Version) bool {\n\ttb.Helper()\n\n\tkey := \"CI_MAX_KERNEL_VERSION\"\n\tif platform.IsWindows {\n\t\tkey = \"CI_MAX_EFW_VERSION\"\n\t}\n\n\tif max := os.Getenv(key); max != \"\" {\n\t\tmaxv, err := internal.NewVersion(max)\n\t\tif err != nil {\n\t\t\ttb.Fatalf(\"Invalid version %q in %s: %s\", max, key, err)\n\t\t}\n\n\t\tif maxv.Less(minv) {\n\t\t\ttb.Fatalf(\"Test for %s will never execute on CI since %s is the most recent runtime\", minv, maxv)\n\t\t}\n\t}\n\n\treturn runv.Less(minv)\n}\n\n// ignoreVersionCheck checks whether to omit the version check for a test.\n//\n// It reads a comma separated list of test names from an environment variable.\n//\n// For example:\n//\n//\tEBPF_TEST_IGNORE_VERSION=TestABC,TestXYZ go test ...\nfunc ignoreVersionCheck(tName string) bool {\n\ttNames := os.Getenv(ignoreVersionEnvVar)\n\tif tNames == \"\" {\n\t\treturn false\n\t}\n\n\tignored := strings.Split(tNames, \",\")\n\tfor _, n := range ignored {\n\t\tif strings.TrimSpace(n) == tName {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// SkipNonNativeEndian skips the test or benchmark if bo doesn't match the\n// host's native endianness.\nfunc SkipNonNativeEndian(tb testing.TB, bo binary.ByteOrder) {\n\ttb.Helper()\n\n\tif bo != internal.NativeEndian {\n\t\ttb.Skip(\"Skipping due to non-native endianness\")\n\t}\n}\n"
  },
  {
    "path": "internal/testutils/feature_other.go",
    "content": "//go:build !windows\n\npackage testutils\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/linux\"\n)\n\nfunc platformVersion(tb testing.TB) internal.Version {\n\ttb.Helper()\n\n\tv, err := linux.KernelVersion()\n\tif err != nil {\n\t\ttb.Fatal(err)\n\t}\n\treturn v\n}\n"
  },
  {
    "path": "internal/testutils/feature_test.go",
    "content": "package testutils\n\nimport (\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestIgnoreKernelVersionCheckWhenEnvVarIsSet(t *testing.T) {\n\ttests := []struct {\n\t\tname                     string\n\t\ttoIgnoreNamesEnvValue    string\n\t\ttestName                 string\n\t\tignoreKernelVersionCheck bool\n\t}{\n\t\t{\n\t\t\tname:                     \"should NOT ignore kernel version check if environment var set to empty string\",\n\t\t\ttoIgnoreNamesEnvValue:    \"\",\n\t\t\ttestName:                 \"TestABC\",\n\t\t\tignoreKernelVersionCheck: false,\n\t\t},\n\t\t{\n\t\t\tname:                     \"should ignore kernel version check if environment var set to skip test name with single value\",\n\t\t\ttoIgnoreNamesEnvValue:    \"TestABC\",\n\t\t\ttestName:                 \"TestABC\",\n\t\t\tignoreKernelVersionCheck: true,\n\t\t},\n\t\t{\n\t\t\tname:                     \"should match test name when multiple comma separated names list is provided\",\n\t\t\ttoIgnoreNamesEnvValue:    \"TestABC,TestXYZ\",\n\t\t\ttestName:                 \"TestXYZ\",\n\t\t\tignoreKernelVersionCheck: true,\n\t\t},\n\t\t{\n\t\t\tname:                     \"should NOT match test name when multiple comma separated names list is provided but name is not present in list\",\n\t\t\ttoIgnoreNamesEnvValue:    \"TestABC,TestXYZ\",\n\t\t\ttestName:                 \"TestPQR\",\n\t\t\tignoreKernelVersionCheck: false,\n\t\t},\n\t\t{\n\t\t\tname:                     \"should match test name if names list has leading/trailing spaces\",\n\t\t\ttoIgnoreNamesEnvValue:    \"TestABC, TestXYZ , TestPQR\",\n\t\t\ttestName:                 \"TestXYZ\",\n\t\t\tignoreKernelVersionCheck: true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tt.Setenv(ignoreVersionEnvVar, tt.toIgnoreNamesEnvValue)\n\n\t\t\tif got := ignoreVersionCheck(tt.testName); got != tt.ignoreKernelVersionCheck {\n\t\t\t\tt.Errorf(\"ignoreKernelVersionCheck() = %v, want %v\", got, tt.ignoreKernelVersionCheck)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestPlatformVersion(t *testing.T) {\n\tqt.Assert(t, qt.IsFalse(platformVersion(t).Unspecified()))\n}\n"
  },
  {
    "path": "internal/testutils/feature_windows.go",
    "content": "package testutils\n\nimport (\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc platformVersion(tb testing.TB) internal.Version {\n\ttb.Helper()\n\tversionStr, ok := os.LookupEnv(\"CI_EFW_VERSION\")\n\tqt.Assert(tb, qt.IsTrue(ok), qt.Commentf(\"Missing CI_EFW_VERSION environment variable\"))\n\tversion, err := internal.NewVersion(versionStr)\n\tqt.Assert(tb, qt.IsNil(err), qt.Commentf(\"Parse eBPF for Windows version\"))\n\treturn version\n}\n"
  },
  {
    "path": "internal/testutils/glob.go",
    "content": "package testutils\n\nimport (\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"golang.org/x/sys/cpu\"\n)\n\n// Files calls fn for each given file.\n//\n// The function errors out if the pattern matches no files.\nfunc Files(t *testing.T, files []string, fn func(*testing.T, string)) {\n\tt.Helper()\n\n\tif len(files) == 0 {\n\t\tt.Fatalf(\"No files given\")\n\t}\n\n\tfor _, f := range files {\n\t\tfile := f // force copy\n\t\tname := filepath.Base(file)\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tfn(t, file)\n\t\t})\n\t}\n}\n\n// Glob finds files matching a pattern.\n//\n// The pattern should may include full path. Excludes use the same syntax as\n// pattern, but are only applied to the basename instead of the full path.\nfunc Glob(tb testing.TB, pattern string, excludes ...string) []string {\n\ttb.Helper()\n\n\tfiles, err := filepath.Glob(pattern)\n\tif err != nil {\n\t\ttb.Fatal(\"Can't glob files:\", err)\n\t}\n\n\tif len(excludes) == 0 {\n\t\treturn files\n\t}\n\n\tvar filtered []string\nnextFile:\n\tfor _, file := range files {\n\t\tbase := filepath.Base(file)\n\t\tfor _, exclude := range excludes {\n\t\t\tif matched, err := filepath.Match(exclude, base); err != nil {\n\t\t\t\ttb.Fatal(err)\n\t\t\t} else if matched {\n\t\t\t\tcontinue nextFile\n\t\t\t}\n\t\t}\n\t\tfiltered = append(filtered, file)\n\t}\n\n\treturn filtered\n}\n\n// NativeFile substitutes %s with an abbreviation of the host endianness.\nfunc NativeFile(tb testing.TB, path string) string {\n\ttb.Helper()\n\n\tif !strings.Contains(path, \"%s\") {\n\t\ttb.Fatalf(\"File %q doesn't contain %%s\", path)\n\t}\n\n\tif cpu.IsBigEndian {\n\t\treturn fmt.Sprintf(path, \"eb\")\n\t}\n\n\treturn fmt.Sprintf(path, \"el\")\n}\n"
  },
  {
    "path": "internal/testutils/netns_linux.go",
    "content": "//go:build linux\n\n// The netns implementation in this file was taken from cilium/cilium.\npackage testutils\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"runtime\"\n\t\"testing\"\n\n\t\"golang.org/x/sync/errgroup\"\n\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\ntype NetNS struct {\n\tf *os.File\n}\n\n// NewNetNS returns a new network namespace.\nfunc NewNetNS(tb testing.TB) *NetNS {\n\ttb.Helper()\n\n\tns, err := newNetNS()\n\tif err != nil {\n\t\ttb.Fatal(err)\n\t}\n\n\ttb.Cleanup(func() {\n\t\tns.close()\n\t})\n\n\treturn ns\n}\n\n// Do runs the provided func in the netns without changing the calling thread's\n// netns.\n//\n// The code in f and any code called by f must NOT call [runtime.LockOSThread],\n// as this could leave the goroutine created by Do permanently pinned to an OS\n// thread.\nfunc (h *NetNS) Do(f func() error) error {\n\n\t// Start the func in a new goroutine and lock it to an exclusive thread. This\n\t// ensures that if execution of the goroutine fails unexpectedly before we\n\t// call UnlockOSThread, the go runtime will ensure the underlying OS thread is\n\t// disposed of, rather than reused in a potentially undefined state.\n\t//\n\t// See also: https://pkg.go.dev/runtime#UnlockOSThread\n\tvar g errgroup.Group\n\tg.Go(func() error {\n\t\t// Lock the newly-created goroutine to the OS thread it's running on so we\n\t\t// can safely move it into another network namespace. (per-thread state)\n\t\trestoreUnlock, err := lockOSThread()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif err := set(h.f); err != nil {\n\t\t\treturn fmt.Errorf(\"set netns: %w (terminating OS thread)\", err)\n\t\t}\n\n\t\tferr := f()\n\n\t\t// Attempt to restore the underlying OS thread to its original network\n\t\t// namespace and unlock the running goroutine from its OS thread. Any\n\t\t// failures during this process will leave the goroutine locked, making the\n\t\t// underlying OS thread terminate when this function returns.\n\t\tif err := restoreUnlock(); err != nil {\n\t\t\treturn fmt.Errorf(\"restore original netns: %w (terminating OS thread)\", err)\n\t\t}\n\t\treturn ferr\n\t})\n\n\treturn g.Wait()\n}\n\nfunc newNetNS() (*NetNS, error) {\n\tvar f *os.File\n\n\t// Perform network namespace creation in a new goroutine to give us the\n\t// possibility of terminating the underlying OS thread (by terminating the\n\t// goroutine) if something goes wrong.\n\tvar g errgroup.Group\n\tg.Go(func() error {\n\t\trestoreUnlock, err := lockOSThread()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"lock OS thread: %w\", err)\n\t\t}\n\n\t\t// Move the underlying OS thread to a new network namespace. This can be\n\t\t// undone by calling restoreUnlock().\n\t\tif err := unshare(); err != nil {\n\t\t\treturn fmt.Errorf(\"create new netns: %w\", err)\n\t\t}\n\n\t\t// Take out a reference to the new netns.\n\t\tf, err = getCurrent()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"get current netns: %w (terminating OS thread)\", err)\n\t\t}\n\n\t\t// Restore the OS thread to its original network namespace or implicitly\n\t\t// terminate it if something went wrong.\n\t\tif err := restoreUnlock(); err != nil {\n\t\t\treturn fmt.Errorf(\"restore current netns: %w (terminating OS thread)\", err)\n\t\t}\n\n\t\treturn nil\n\t})\n\n\tif err := g.Wait(); err != nil {\n\t\treturn nil, err\n\t}\n\n\tns := &NetNS{f: f}\n\n\t// Prevent resource leaks by eventually closing the underlying file descriptor\n\t// after ns is garbage collected.\n\truntime.SetFinalizer(ns, (*NetNS).close)\n\n\treturn ns, nil\n}\n\nfunc (h *NetNS) close() error {\n\tif h.f == nil {\n\t\treturn nil\n\t}\n\n\t// Close closes the handle to the network namespace. This does not necessarily\n\t// mean destroying the network namespace itself, which only happens when all\n\t// references to it are gone and all of its processes have been terminated.\n\tif err := h.f.Close(); err != nil {\n\t\treturn err\n\t}\n\th.f = nil\n\n\treturn nil\n}\n\nfunc lockOSThread() (func() error, error) {\n\truntime.LockOSThread()\n\n\torig, err := getCurrent()\n\tif err != nil {\n\t\truntime.UnlockOSThread()\n\t\treturn nil, fmt.Errorf(\"get current namespace: %w\", err)\n\t}\n\n\treturn func() error {\n\t\tdefer orig.Close()\n\n\t\tif err := set(orig); err != nil {\n\t\t\t// We didn't manage to restore the OS thread to its original namespace.\n\t\t\t// Don't unlock the current goroutine from its thread, so the thread will\n\t\t\t// terminate when the current goroutine does.\n\t\t\treturn err\n\t\t}\n\n\t\t// Original netns was restored, release the OS thread back into the\n\t\t// schedulable pool.\n\t\truntime.UnlockOSThread()\n\n\t\treturn nil\n\t}, nil\n}\n\n// unshare moves the calling OS thread of the calling goroutine to a new network\n// namespace. Must only be called after a prior call to lockOSThread().\nfunc unshare() error {\n\tif err := unix.Unshare(unix.CLONE_NEWNET); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// set sets the underlying OS thread of the calling goroutine to the netns\n// pointed at by f.\nfunc set(f *os.File) error {\n\treturn unix.Setns(int(f.Fd()), unix.CLONE_NEWNET)\n}\n\n// getCurrent gets a file descriptor to the current thread network namespace.\nfunc getCurrent() (*os.File, error) {\n\treturn getFromThread(os.Getpid(), unix.Gettid())\n}\n\n// getFromPath gets a file descriptor to the network namespace pinned at path.\nfunc getFromPath(path string) (*os.File, error) {\n\treturn os.OpenFile(path, unix.O_RDONLY|unix.O_CLOEXEC, 0)\n}\n\n// getFromThread gets a file descriptor to the network namespace of a given pid\n// and tid.\nfunc getFromThread(pid, tid int) (*os.File, error) {\n\treturn getFromPath(fmt.Sprintf(\"/proc/%d/task/%d/ns/net\", pid, tid))\n}\n"
  },
  {
    "path": "internal/testutils/netns_other.go",
    "content": "//go:build !linux\n\n// This file is a stub to allow netns to be compiled on non-Linux platforms.\npackage testutils\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf/internal\"\n)\n\ntype NetNS struct {\n}\n\nfunc NewNetNS(tb testing.TB) *NetNS {\n\treturn nil\n}\n\nfunc (h *NetNS) Do(f func() error) error {\n\treturn internal.ErrNotSupportedOnOS\n}\n"
  },
  {
    "path": "internal/testutils/programs.go",
    "content": "package testutils\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n)\n\nfunc ClangBin(tb testing.TB) string {\n\ttb.Helper()\n\n\tif testing.Short() {\n\t\ttb.Skip(\"Not compiling with -short\")\n\t}\n\n\t// Use a floating clang version for local development, but allow CI to run\n\t// against oldest supported clang.\n\tclang := \"clang\"\n\tif minVersion := os.Getenv(\"CI_MIN_CLANG_VERSION\"); minVersion != \"\" {\n\t\tclang = fmt.Sprintf(\"clang-%s\", minVersion)\n\t}\n\n\ttb.Log(\"Testing against\", clang)\n\treturn clang\n}\n"
  },
  {
    "path": "internal/testutils/rlimit.go",
    "content": "package testutils\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/cilium/ebpf/rlimit\"\n)\n\nfunc init() {\n\t// Increase the memlock for all tests unconditionally. It's a great source of\n\t// weird bugs, since different distros have different default limits.\n\tif err := rlimit.RemoveMemlock(); err != nil {\n\t\tfmt.Fprintln(os.Stderr, \"WARNING: Failed to adjust rlimit, tests may fail\")\n\t}\n}\n"
  },
  {
    "path": "internal/testutils/seed.go",
    "content": "package testutils\n\nimport (\n\t\"math/rand\"\n\t\"os\"\n\t\"strconv\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n)\n\nvar randSeed struct {\n\tvalue int64\n\tonce  sync.Once\n}\n\nfunc Rand(tb testing.TB) *rand.Rand {\n\trandSeed.once.Do(func() {\n\t\trandSeed.value = time.Now().UnixMicro()\n\t})\n\n\tseed := randSeed.value\n\tif seedStr, ok := os.LookupEnv(\"TEST_SEED\"); ok {\n\t\tvar err error\n\t\tseed, err = strconv.ParseInt(seedStr, 0, 64)\n\t\tif err != nil {\n\t\t\ttb.Fatal(\"Parse TEST_SEED environment variable:\", err)\n\t\t}\n\t}\n\n\ttb.Logf(\"TEST_SEED=%d\\n\", seed)\n\treturn rand.New(rand.NewSource(seed))\n}\n"
  },
  {
    "path": "internal/testutils/testmain/fd_trace.go",
    "content": "package testmain\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"os\"\n\t\"runtime\"\n\t\"sync\"\n\t\"sync/atomic\"\n)\n\n// foundLeak is atomic since the GC may collect objects in parallel.\nvar foundLeak atomic.Bool\n\nfunc onLeakFD(fs *runtime.Frames) {\n\tfoundLeak.Store(true)\n\tfmt.Fprintln(os.Stderr, \"leaked fd created at:\")\n\tfmt.Fprintln(os.Stderr, formatFrames(fs))\n}\n\n// fds is a registry of all file descriptors wrapped into sys.fds that were\n// created while an fd tracer was active.\nvar fds *sync.Map // map[int]*runtime.Frames\n\n// TraceFD associates raw with the current execution stack.\n//\n// skip controls how many entries of the stack the function should skip.\nfunc TraceFD(raw int, skip int) {\n\tif fds == nil {\n\t\treturn\n\t}\n\n\t// Attempt to store the caller's stack for the given fd value.\n\t// Panic if fds contains an existing stack for the fd.\n\told, exist := fds.LoadOrStore(raw, callersFrames(skip))\n\tif exist {\n\t\tf := old.(*runtime.Frames)\n\t\tpanic(fmt.Sprintf(\"found existing stack for fd %d:\\n%s\", raw, formatFrames(f)))\n\t}\n}\n\n// ForgetFD removes any existing association for raw.\nfunc ForgetFD(raw int) {\n\tif fds != nil {\n\t\tfds.Delete(raw)\n\t}\n}\n\n// LeakFD indicates that raw was leaked.\n//\n// Calling the function with a value that was not passed to [TraceFD] before\n// is undefined.\nfunc LeakFD(raw int) {\n\tif fds == nil {\n\t\treturn\n\t}\n\n\t// Invoke the fd leak callback. Calls LoadAndDelete to guarantee the callback\n\t// is invoked at most once for one sys.FD allocation, runtime.Frames can only\n\t// be unwound once.\n\tf, ok := fds.LoadAndDelete(raw)\n\tif ok {\n\t\tonLeakFD(f.(*runtime.Frames))\n\t}\n}\n\n// flushFrames removes all elements from fds and returns them as a slice. This\n// deals with the fact that a runtime.Frames can only be unwound once using\n// Next().\nfunc flushFrames() []*runtime.Frames {\n\tvar frames []*runtime.Frames\n\tfds.Range(func(key, value any) bool {\n\t\tframes = append(frames, value.(*runtime.Frames))\n\t\tfds.Delete(key)\n\t\treturn true\n\t})\n\treturn frames\n}\n\nfunc callersFrames(skip int) *runtime.Frames {\n\tc := make([]uintptr, 32)\n\n\t// Skip runtime.Callers and this function.\n\ti := runtime.Callers(skip+2, c)\n\tif i == 0 {\n\t\treturn nil\n\t}\n\n\treturn runtime.CallersFrames(c)\n}\n\n// formatFrames formats a runtime.Frames as a human-readable string.\nfunc formatFrames(fs *runtime.Frames) string {\n\tvar b bytes.Buffer\n\tfor {\n\t\tf, more := fs.Next()\n\t\tb.WriteString(fmt.Sprintf(\"\\t%s+%#x\\n\\t\\t%s:%d\\n\", f.Function, f.PC-f.Entry, f.File, f.Line))\n\t\tif !more {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn b.String()\n}\n"
  },
  {
    "path": "internal/testutils/testmain/main.go",
    "content": "package testmain\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n\t\"sync\"\n\n\t\"github.com/cilium/ebpf/internal/platform\"\n)\n\ntype testingM interface {\n\tRun() int\n}\n\n// Run m with various debug aids enabled.\n//\n// The function calls [os.Exit] and does not return.\nfunc Run(m testingM) {\n\tconst traceLogFlag = \"trace-log\"\n\n\tvar ts *traceSession\n\tif platform.IsWindows {\n\t\ttraceLog := flag.Bool(traceLogFlag, false, \"Output a trace of eBPF runtime activity\")\n\t\tflag.Parse()\n\n\t\tif *traceLog {\n\t\t\tvar err error\n\t\t\tts, err = newTraceSession()\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintln(os.Stderr, \"Disabling trace logging:\", err)\n\t\t\t}\n\t\t}\n\t}\n\tdefer ts.Close()\n\n\tfds = new(sync.Map)\n\tret := m.Run()\n\n\tfor _, f := range flushFrames() {\n\t\tonLeakFD(f)\n\t}\n\n\tif foundLeak.Load() {\n\t\tret = 99\n\t}\n\n\tif err := ts.Dump(os.Stderr); err != nil {\n\t\tfmt.Fprintln(os.Stderr, \"Error while dumping trace log:\", err)\n\t\tret = 99\n\t}\n\n\tif platform.IsWindows && ret != 0 && ts == nil {\n\t\tfmt.Fprintf(os.Stderr, \"Consider enabling trace logging with -%s\\n\", traceLogFlag)\n\t}\n\n\tos.Exit(ret)\n}\n"
  },
  {
    "path": "internal/testutils/testmain/windows.go",
    "content": "package testmain\n\nimport (\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"text/tabwriter\"\n)\n\ntype tracelogKeywords uint64\n\n// Know tracelog keywords.\n//\n// See https://github.com/microsoft/ebpf-for-windows/blob/main/libs/shared/ebpf_tracelog.h\nvar allKeywords = []string{\n\t\"entry-exit\",\n\t\"base\",\n\t\"error\",\n\t\"epoch\",\n\t\"core\",\n\t\"link\",\n\t\"map\",\n\t\"program\",\n\t\"api\",\n\t\"printk\",\n\t\"native\",\n}\n\nfunc (kw *tracelogKeywords) UnmarshalText(text []byte) error {\n\tdecoded, err := strconv.ParseUint(string(text), 0, 64)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"foo: %w\", err)\n\t}\n\t*kw = tracelogKeywords(decoded)\n\treturn nil\n}\n\nfunc (kw tracelogKeywords) decode() []string {\n\tvar keywords []string\n\tfor _, keyword := range allKeywords {\n\t\tif kw&1 > 0 {\n\t\t\tkeywords = append(keywords, keyword)\n\t\t}\n\t\tkw >>= 1\n\t}\n\tif kw > 0 {\n\t\tkeywords = append(keywords, fmt.Sprintf(\"0x%x\", kw))\n\t}\n\treturn keywords\n}\n\ntype traceSession struct {\n\tsession string\n}\n\n// newTraceSession starts a trace log for eBPF for Windows related events.\n//\n// * https://github.com/microsoft/ebpf-for-windows/blob/main/docs/GettingStarted.md#using-tracing\n// * https://devblogs.microsoft.com/performance-diagnostics/controlling-the-event-session-name-with-the-instance-name/ and\nfunc newTraceSession() (*traceSession, error) {\n\tdef := filepath.Join(os.Getenv(\"ProgramFiles\"), \"ebpf-for-windows\\\\ebpfforwindows.wprp\")\n\tif _, err := os.Stat(def); err != nil {\n\t\treturn nil, err\n\t}\n\n\tsession := fmt.Sprintf(\"epbf-go-%d\", os.Getpid())\n\twpr := exec.Command(\"wpr.exe\", \"-start\", def, \"-filemode\", \"-instancename\", session)\n\twpr.Stderr = os.Stderr\n\tif err := wpr.Run(); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &traceSession{session}, nil\n}\n\nfunc (ts *traceSession) Close() error {\n\tif ts == nil {\n\t\treturn nil\n\t}\n\n\treturn ts.stop(os.DevNull)\n}\n\nfunc (ts *traceSession) stop(file string) error {\n\tif ts.session == \"\" {\n\t\treturn nil\n\t}\n\n\twpr := exec.Command(\"wpr.exe\", \"-stop\", file, \"-instancename\", ts.session)\n\tif err := wpr.Run(); err != nil {\n\t\treturn err\n\t}\n\n\tts.session = \"\"\n\treturn nil\n}\n\nfunc (ts *traceSession) Dump(w io.Writer) error {\n\tif ts == nil {\n\t\treturn nil\n\t}\n\n\tpath, err := os.MkdirTemp(\"\", \"ebpf-go-trace\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer os.RemoveAll(path)\n\n\ttrace := filepath.Join(path, \"trace.etl\")\n\tif err := ts.stop(trace); err != nil {\n\t\treturn fmt.Errorf(\"write trace: %w\", err)\n\t}\n\n\tnetsh := exec.Command(\"netsh.exe\", \"trace\", \"convert\", trace, \"dump=XML\")\n\tif err := netsh.Run(); err != nil {\n\t\treturn err\n\t}\n\n\tf, err := os.Open(filepath.Join(path, \"trace.xml\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer f.Close()\n\n\treturn summariseWPRTrace(f, w)\n}\n\nfunc summariseWPRTrace(r io.Reader, w io.Writer) error {\n\ttype nameValue struct {\n\t\tName  string `xml:\"Name,attr\"`\n\t\tValue string `xml:\",chardata\"`\n\t}\n\n\ttype event struct {\n\t\tXMLName xml.Name `xml:\"Event\"`\n\t\tSystem  struct {\n\t\t\tProvider struct {\n\t\t\t\tName string `xml:\"Name,attr\"`\n\t\t\t} `xml:\"Provider\"`\n\t\t\tTimeCreated struct {\n\t\t\t\tSystemTime string `xml:\"SystemTime,attr\"`\n\t\t\t} `xml:\"TimeCreated\"`\n\t\t\tKeywords tracelogKeywords `xml:\"Keywords\"`\n\t\t\tLevel    uint64           `xml:\"Level\"`\n\t\t} `xml:\"System\"`\n\t\tEventData struct {\n\t\t\tData []nameValue `xml:\"Data\"`\n\t\t} `xml:\"EventData\"`\n\t\tRenderingInfo struct {\n\t\t\tTask string `xml:\"Task\"`\n\t\t} `xml:\"RenderingInfo\"`\n\t}\n\n\tvar events struct {\n\t\tEvents []event `xml:\"Event\"`\n\t}\n\n\terr := xml.NewDecoder(r).Decode(&events)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unmarshal trace XML: %w\", err)\n\t}\n\n\ttw := tabwriter.NewWriter(w, 0, 0, 1, ' ', 0)\n\tfor _, event := range events.Events {\n\t\tif !strings.Contains(event.System.Provider.Name, \"Ebpf\") {\n\t\t\tcontinue\n\t\t}\n\n\t\tflag := \" \"\n\t\t// See https://learn.microsoft.com/en-us/windows/win32/api/traceloggingprovider/nf-traceloggingprovider-tracelogginglevel#remarks\n\t\tif event.System.Level > 0 && event.System.Level <= 3 {\n\t\t\tflag = \"!\"\n\t\t}\n\n\t\tkw := event.System.Keywords.decode()\n\t\tfmt.Fprintf(tw, \"%s\\t%s\\t\", flag, strings.Join(kw, \",\"))\n\n\t\tdata := event.EventData.Data\n\t\tslices.SortFunc(data, func(a, b nameValue) int {\n\t\t\treturn strings.Compare(a.Name, b.Name)\n\t\t})\n\n\t\tvar first string\n\t\tfor _, name := range []string{\n\t\t\t\"Entry\",\n\t\t\t\"Message\",\n\t\t\t\"ErrorMessage\",\n\t\t} {\n\t\t\ti := slices.IndexFunc(data, func(kv nameValue) bool {\n\t\t\t\treturn kv.Name == name\n\t\t\t})\n\n\t\t\tif i == -1 {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tfirst = data[i].Value\n\t\t\tdata = slices.Delete(data, i, i+1)\n\t\t\tbreak\n\t\t}\n\n\t\t// NB: This may be empty.\n\t\tfmt.Fprintf(tw, \"%s\\t\", first)\n\n\t\tfor _, data := range data {\n\t\t\tfmt.Fprintf(tw, \"%s=%s\\t\", data.Name, data.Value)\n\t\t}\n\n\t\tfmt.Fprintln(tw)\n\t}\n\n\treturn tw.Flush()\n}\n"
  },
  {
    "path": "internal/testutils/testmain/windows_test.go",
    "content": "package testmain\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestSummariseWPRTrace(t *testing.T) {\n\tf, err := os.Open(\"testdata/trace.xml.gz\")\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer f.Close()\n\n\ttrace, err := gzip.NewReader(f)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tvar buf bytes.Buffer\n\tqt.Assert(t, qt.IsNil(summariseWPRTrace(trace, &buf)))\n\tt.Log(\"\\n\", buf.String())\n}\n"
  },
  {
    "path": "internal/tracefs/kprobe.go",
    "content": "package tracefs\n\nimport (\n\t\"crypto/rand\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"sync\"\n\t\"syscall\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/linux\"\n\t\"github.com/cilium/ebpf/internal/platform\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\nvar (\n\tErrInvalidInput = errors.New(\"invalid input\")\n\n\tErrInvalidMaxActive = errors.New(\"can only set maxactive on kretprobes\")\n)\n\n//go:generate go tool stringer -type=ProbeType -linecomment\n\ntype ProbeType uint8\n\nconst (\n\tKprobe ProbeType = iota // kprobe\n\tUprobe                  // uprobe\n)\n\nfunc (pt ProbeType) eventsFile() (*os.File, error) {\n\tpath, err := sanitizeTracefsPath(fmt.Sprintf(\"%s_events\", pt.String()))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0666)\n}\n\ntype ProbeArgs struct {\n\tType                         ProbeType\n\tSymbol, Group, Path          string\n\tOffset, RefCtrOffset, Cookie uint64\n\tPid, RetprobeMaxActive       int\n\tRet                          bool\n}\n\n// RandomGroup generates a pseudorandom string for use as a tracefs group name.\n// Returns an error when the output string would exceed 63 characters (kernel\n// limitation), when rand.Read() fails or when prefix contains characters not\n// allowed by IsValidTraceID.\nfunc RandomGroup(prefix string) (string, error) {\n\tif !validIdentifier(prefix) {\n\t\treturn \"\", fmt.Errorf(\"prefix '%s' must be alphanumeric or underscore: %w\", prefix, ErrInvalidInput)\n\t}\n\n\tb := make([]byte, 8)\n\tif _, err := rand.Read(b); err != nil {\n\t\treturn \"\", fmt.Errorf(\"reading random bytes: %w\", err)\n\t}\n\n\tgroup := fmt.Sprintf(\"%s_%x\", prefix, b)\n\tif len(group) > 63 {\n\t\treturn \"\", fmt.Errorf(\"group name '%s' cannot be longer than 63 characters: %w\", group, ErrInvalidInput)\n\t}\n\n\treturn group, nil\n}\n\n// validIdentifier implements the equivalent of a regex match\n// against \"^[a-zA-Z_][0-9a-zA-Z_-]*$\".\n//\n// Trace event groups, names and kernel symbols must adhere to this set of\n// characters. Non-empty, first character must not be a number or hyphen, all\n// characters must be alphanumeric, underscore or hyphen.\nfunc validIdentifier(s string) bool {\n\tif len(s) < 1 {\n\t\treturn false\n\t}\n\tfor i, c := range []byte(s) {\n\t\tswitch {\n\t\tcase c >= 'a' && c <= 'z':\n\t\tcase c >= 'A' && c <= 'Z':\n\t\tcase c == '_':\n\t\tcase i > 0 && (c == '-' || c >= '0' && c <= '9'):\n\n\t\tdefault:\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n\nfunc sanitizeTracefsPath(path ...string) (string, error) {\n\tbase, err := getTracefsPath()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tl := filepath.Join(path...)\n\tp := filepath.Join(base, l)\n\tif !strings.HasPrefix(p, base) {\n\t\treturn \"\", fmt.Errorf(\"path '%s' attempts to escape base path '%s': %w\", l, base, ErrInvalidInput)\n\t}\n\treturn p, nil\n}\n\n// getTracefsPath will return a correct path to the tracefs mount point.\n// Since kernel 4.1 tracefs should be mounted by default at /sys/kernel/tracing,\n// but may be also be available at /sys/kernel/debug/tracing if debugfs is mounted.\n// The available tracefs paths will depends on distribution choices.\nvar getTracefsPath = sync.OnceValues(func() (string, error) {\n\tif !platform.IsLinux {\n\t\treturn \"\", fmt.Errorf(\"tracefs: %w\", internal.ErrNotSupportedOnOS)\n\t}\n\n\tfor _, p := range []struct {\n\t\tpath   string\n\t\tfsType int64\n\t}{\n\t\t{\"/sys/kernel/tracing\", unix.TRACEFS_MAGIC},\n\t\t{\"/sys/kernel/debug/tracing\", unix.TRACEFS_MAGIC},\n\t\t// RHEL/CentOS\n\t\t{\"/sys/kernel/debug/tracing\", unix.DEBUGFS_MAGIC},\n\t} {\n\t\tif fsType, err := linux.FSType(p.path); err == nil && fsType == p.fsType {\n\t\t\treturn p.path, nil\n\t\t}\n\t}\n\n\treturn \"\", errors.New(\"neither debugfs nor tracefs are mounted\")\n})\n\n// sanitizeIdentifier replaces every invalid character for the tracefs api with an underscore.\n//\n// It is equivalent to calling regexp.MustCompile(\"[^a-zA-Z0-9]+\").ReplaceAllString(\"_\").\nfunc sanitizeIdentifier(s string) string {\n\tvar skip bool\n\treturn strings.Map(func(c rune) rune {\n\t\tswitch {\n\t\tcase c >= 'a' && c <= 'z',\n\t\t\tc >= 'A' && c <= 'Z',\n\t\t\tc >= '0' && c <= '9':\n\t\t\tskip = false\n\t\t\treturn c\n\n\t\tcase skip:\n\t\t\treturn -1\n\n\t\tdefault:\n\t\t\tskip = true\n\t\t\treturn '_'\n\t\t}\n\t}, s)\n}\n\n// EventID reads a trace event's ID from tracefs given its group and name.\n// The kernel requires group and name to be alphanumeric or underscore.\nfunc EventID(group, name string) (uint64, error) {\n\tif !validIdentifier(group) {\n\t\treturn 0, fmt.Errorf(\"invalid tracefs group: %q\", group)\n\t}\n\n\tif !validIdentifier(name) {\n\t\treturn 0, fmt.Errorf(\"invalid tracefs name: %q\", name)\n\t}\n\n\tpath, err := sanitizeTracefsPath(\"events\", group, name, \"id\")\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\ttid, err := internal.ReadUint64FromFile(\"%d\\n\", path)\n\tif errors.Is(err, os.ErrNotExist) {\n\t\treturn 0, err\n\t}\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"reading trace event ID of %s/%s: %w\", group, name, err)\n\t}\n\n\treturn tid, nil\n}\n\nfunc probePrefix(ret bool, maxActive int) string {\n\tif ret {\n\t\tif maxActive > 0 {\n\t\t\treturn fmt.Sprintf(\"r%d\", maxActive)\n\t\t}\n\t\treturn \"r\"\n\t}\n\treturn \"p\"\n}\n\n// Event represents an entry in a tracefs probe events file.\ntype Event struct {\n\ttyp         ProbeType\n\tgroup, name string\n\t// event id allocated by the kernel. 0 if the event has already been removed.\n\tid uint64\n\n\tcleanup runtime.Cleanup\n}\n\n// NewEvent creates a new ephemeral trace event.\n//\n// Returns os.ErrNotExist if symbol is not a valid\n// kernel symbol, or if it is not traceable with kprobes. Returns os.ErrExist\n// if a probe with the same group and symbol already exists. Returns an error if\n// args.RetprobeMaxActive is used on non kprobe types. Returns ErrNotSupported if\n// the kernel is too old to support kretprobe maxactive.\nfunc NewEvent(args ProbeArgs) (*Event, error) {\n\t// Before attempting to create a trace event through tracefs,\n\t// check if an event with the same group and name already exists.\n\t// Kernels 4.x and earlier don't return os.ErrExist on writing a duplicate\n\t// entry, so we need to rely on reads for detecting uniqueness.\n\teventName := sanitizeIdentifier(args.Symbol)\n\t_, err := EventID(args.Group, eventName)\n\tif err == nil {\n\t\treturn nil, fmt.Errorf(\"trace event %s/%s: %w\", args.Group, eventName, os.ErrExist)\n\t}\n\tif errors.Is(err, unix.EINVAL) {\n\t\treturn nil, fmt.Errorf(\"trace event %s/%s: %w (unknown symbol?)\", args.Group, eventName, err)\n\t}\n\tif !errors.Is(err, os.ErrNotExist) {\n\t\treturn nil, fmt.Errorf(\"checking trace event %s/%s: %w\", args.Group, eventName, err)\n\t}\n\n\t// Open the kprobe_events file in tracefs.\n\tf, err := args.Type.eventsFile()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer f.Close()\n\n\tvar pe, token string\n\tswitch args.Type {\n\tcase Kprobe:\n\t\t// The kprobe_events syntax is as follows (see Documentation/trace/kprobetrace.txt):\n\t\t// p[:[GRP/]EVENT] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS] : Set a probe\n\t\t// r[MAXACTIVE][:[GRP/]EVENT] [MOD:]SYM[+0] [FETCHARGS] : Set a return probe\n\t\t// -:[GRP/]EVENT                                        : Clear a probe\n\t\t//\n\t\t// Some examples:\n\t\t// r:ebpf_1234/r_my_kretprobe nf_conntrack_destroy\n\t\t// p:ebpf_5678/p_my_kprobe __x64_sys_execve\n\t\t//\n\t\t// Leaving the kretprobe's MAXACTIVE set to 0 (or absent) will make the\n\t\t// kernel default to NR_CPUS. This is desired in most eBPF cases since\n\t\t// subsampling or rate limiting logic can be more accurately implemented in\n\t\t// the eBPF program itself.\n\t\t// See Documentation/kprobes.txt for more details.\n\t\tif args.RetprobeMaxActive != 0 && !args.Ret {\n\t\t\treturn nil, ErrInvalidMaxActive\n\t\t}\n\t\ttoken = KprobeToken(args)\n\t\tpe = fmt.Sprintf(\"%s:%s/%s %s\", probePrefix(args.Ret, args.RetprobeMaxActive), args.Group, eventName, token)\n\tcase Uprobe:\n\t\t// The uprobe_events syntax is as follows:\n\t\t// p[:[GRP/]EVENT] PATH:OFFSET [FETCHARGS] : Set a probe\n\t\t// r[:[GRP/]EVENT] PATH:OFFSET [FETCHARGS] : Set a return probe\n\t\t// -:[GRP/]EVENT                           : Clear a probe\n\t\t//\n\t\t// Some examples:\n\t\t// r:ebpf_1234/readline /bin/bash:0x12345\n\t\t// p:ebpf_5678/main_mySymbol /bin/mybin:0x12345(0x123)\n\t\t//\n\t\t// See Documentation/trace/uprobetracer.txt for more details.\n\t\tif args.RetprobeMaxActive != 0 {\n\t\t\treturn nil, ErrInvalidMaxActive\n\t\t}\n\t\ttoken = UprobeToken(args)\n\t\tpe = fmt.Sprintf(\"%s:%s/%s %s\", probePrefix(args.Ret, 0), args.Group, eventName, token)\n\t}\n\t_, err = f.WriteString(pe)\n\n\t// Since commit 97c753e62e6c, ENOENT is correctly returned instead of EINVAL\n\t// when trying to create a retprobe for a missing symbol.\n\tif errors.Is(err, os.ErrNotExist) {\n\t\treturn nil, fmt.Errorf(\"token %s: not found: %w\", token, err)\n\t}\n\t// Since commit ab105a4fb894, EILSEQ is returned when a kprobe sym+offset is resolved\n\t// to an invalid insn boundary. The exact conditions that trigger this error are\n\t// arch specific however.\n\tif errors.Is(err, syscall.EILSEQ) {\n\t\treturn nil, fmt.Errorf(\"token %s: bad insn boundary: %w\", token, os.ErrNotExist)\n\t}\n\t// ERANGE is returned when the `SYM[+offs]` token is too big and cannot\n\t// be resolved.\n\tif errors.Is(err, syscall.ERANGE) {\n\t\treturn nil, fmt.Errorf(\"token %s: offset too big: %w\", token, os.ErrNotExist)\n\t}\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"token %s: writing '%s': %w\", token, pe, err)\n\t}\n\n\t// Get the newly-created trace event's id.\n\ttid, err := EventID(args.Group, eventName)\n\tif args.RetprobeMaxActive != 0 && errors.Is(err, os.ErrNotExist) {\n\t\t// Kernels < 4.12 don't support maxactive and therefore auto generate\n\t\t// group and event names from the symbol and offset. The symbol is used\n\t\t// without any sanitization.\n\t\t// See https://elixir.bootlin.com/linux/v4.10/source/kernel/trace/trace_kprobe.c#L712\n\t\tevent := fmt.Sprintf(\"kprobes/r_%s_%d\", args.Symbol, args.Offset)\n\t\tif err := removeEvent(args.Type, event); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to remove spurious maxactive event: %s\", err)\n\t\t}\n\n\t\treturn nil, &internal.UnsupportedFeatureError{\n\t\t\tMinimumVersion: internal.Version{4, 12},\n\t\t\tName:           \"trace event with non-default maxactive\",\n\t\t}\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"get trace event id: %w\", err)\n\t}\n\n\tevt := &Event{typ: args.Type, group: args.Group, name: eventName, id: tid}\n\tevt.cleanup = runtime.AddCleanup(evt, func(*byte) {\n\t\t_ = removeEvent(args.Type, fmt.Sprintf(\"%s/%s\", args.Group, eventName))\n\t}, nil)\n\n\treturn evt, nil\n}\n\n// Close removes the event from tracefs.\n//\n// Returns os.ErrClosed if the event has already been closed before.\nfunc (evt *Event) Close() error {\n\tif evt.id == 0 {\n\t\treturn os.ErrClosed\n\t}\n\n\tevt.id = 0\n\tevt.cleanup.Stop()\n\tpe := fmt.Sprintf(\"%s/%s\", evt.group, evt.name)\n\treturn removeEvent(evt.typ, pe)\n}\n\nfunc removeEvent(typ ProbeType, pe string) error {\n\tf, err := typ.eventsFile()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer f.Close()\n\n\t// See [k,u]probe_events syntax above. The probe type does not need to be specified\n\t// for removals.\n\tif _, err = f.WriteString(\"-:\" + pe); err != nil {\n\t\treturn fmt.Errorf(\"remove event %q from %s: %w\", pe, f.Name(), err)\n\t}\n\n\treturn nil\n}\n\n// ID returns the tracefs ID associated with the event.\nfunc (evt *Event) ID() uint64 {\n\treturn evt.id\n}\n\n// Group returns the tracefs group used by the event.\nfunc (evt *Event) Group() string {\n\treturn evt.group\n}\n\n// KprobeToken creates the SYM[+offs] token for the tracefs api.\nfunc KprobeToken(args ProbeArgs) string {\n\tpo := args.Symbol\n\n\tif args.Offset != 0 {\n\t\tpo += fmt.Sprintf(\"+%#x\", args.Offset)\n\t}\n\n\treturn po\n}\n"
  },
  {
    "path": "internal/tracefs/kprobe_test.go",
    "content": "package tracefs\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\n// Global symbol, present on all tested kernels.\nconst ksym = \"vprintk\"\n\nfunc TestKprobeTraceFSGroup(t *testing.T) {\n\t// Expect <prefix>_<16 random hex chars>.\n\tg, err := RandomGroup(\"ebpftest\")\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.Matches(g, `ebpftest_[a-f0-9]{16}`))\n\n\t// Expect error when the generator's output exceeds 63 characters.\n\tp := make([]byte, 47) // 63 - 17 (length of the random suffix and underscore) + 1\n\tfor i := range p {\n\t\tp[i] = byte('a')\n\t}\n\t_, err = RandomGroup(string(p))\n\tqt.Assert(t, qt.Not(qt.IsNil(err)))\n\n\t// Reject non-alphanumeric characters.\n\t_, err = RandomGroup(\"/\")\n\tqt.Assert(t, qt.Not(qt.IsNil(err)))\n}\n\nfunc TestKprobeToken(t *testing.T) {\n\ttests := []struct {\n\t\targs     ProbeArgs\n\t\texpected string\n\t}{\n\t\t{ProbeArgs{Symbol: \"symbol\"}, \"symbol\"},\n\t\t{ProbeArgs{Symbol: \"symbol\", Offset: 1}, \"symbol+0x1\"},\n\t\t{ProbeArgs{Symbol: \"symbol\", Offset: 65535}, \"symbol+0xffff\"},\n\t\t{ProbeArgs{Symbol: \"symbol\", Offset: 65536}, \"symbol+0x10000\"},\n\t}\n\n\tfor i, tt := range tests {\n\t\tt.Run(fmt.Sprint(i), func(t *testing.T) {\n\t\t\tpo := KprobeToken(tt.args)\n\t\t\tif tt.expected != po {\n\t\t\t\tt.Errorf(\"Expected symbol+offset to be '%s', got '%s'\", tt.expected, po)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNewEvent(t *testing.T) {\n\tfor _, args := range []ProbeArgs{\n\t\t{Type: Kprobe, Symbol: ksym},\n\t\t{Type: Kprobe, Symbol: ksym, Ret: true},\n\t\t{Type: Uprobe, Path: \"/bin/bash\", Symbol: \"main\"},\n\t\t{Type: Uprobe, Path: \"/bin/bash\", Symbol: \"main\", Ret: true},\n\t} {\n\t\tname := fmt.Sprintf(\"%s ret=%v\", args.Type, args.Ret)\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\targs.Group, _ = RandomGroup(\"ebpftest\")\n\n\t\t\tevt, err := NewEvent(args)\n\t\t\ttestutils.SkipIfNotSupportedOnOS(t, err)\n\t\t\tqt.Assert(t, qt.IsNil(err))\n\t\t\tdefer evt.Close()\n\n\t\t\t_, err = NewEvent(args)\n\t\t\tqt.Assert(t, qt.ErrorIs(err, os.ErrExist),\n\t\t\t\tqt.Commentf(\"expected consecutive event creation to contain os.ErrExist\"))\n\n\t\t\tqt.Assert(t, qt.IsNil(evt.Close()))\n\t\t\tqt.Assert(t, qt.ErrorIs(evt.Close(), os.ErrClosed))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/tracefs/perf_event_test.go",
    "content": "package tracefs\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc TestEventID(t *testing.T) {\n\teid, err := EventID(\"syscalls\", \"sys_enter_mmap\")\n\ttestutils.SkipIfNotSupportedOnOS(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.Not(qt.Equals(eid, 0)))\n}\n\nfunc TestSanitizePath(t *testing.T) {\n\t_, err := sanitizeTracefsPath(\"../escaped\")\n\ttestutils.SkipIfNotSupportedOnOS(t, err)\n\tif !errors.Is(err, ErrInvalidInput) {\n\t\tt.Errorf(\"expected error %s, got: %s\", ErrInvalidInput, err)\n\t}\n\n\t_, err = sanitizeTracefsPath(\"./not/escaped\")\n\tif err != nil {\n\t\tt.Errorf(\"expected no error, got: %s\", err)\n\t}\n}\n\nfunc TestValidIdentifier(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tin   string\n\t\tfail bool\n\t}{\n\t\t{\"empty string\", \"\", true},\n\t\t{\"leading number\", \"1test\", true},\n\t\t{\"underscore first\", \"__x64_syscall\", false},\n\t\t{\"contains number\", \"bpf_trace_run1\", false},\n\t\t{\"underscore\", \"_\", false},\n\t\t{\"leading dash\", \"-EINVAL\", true},\n\t\t{\"contains number\", \"all0wed\", false},\n\t\t{\"contains dash\", \"trace-group\", false},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\texp := \"pass\"\n\t\t\tif tt.fail {\n\t\t\t\texp = \"fail\"\n\t\t\t}\n\n\t\t\tif validIdentifier(tt.in) == tt.fail {\n\t\t\t\tt.Errorf(\"expected string '%s' to %s valid ID check\", tt.in, exp)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSanitizeIdentifier(t *testing.T) {\n\ttests := []struct {\n\t\tsymbol   string\n\t\texpected string\n\t}{\n\t\t{\"readline\", \"readline\"},\n\t\t{\"main.Func123\", \"main_Func123\"},\n\t\t{\"a.....a\", \"a_a\"},\n\t\t{\"./;'{}[]a\", \"_a\"},\n\t\t{\"***xx**xx###\", \"_xx_xx_\"},\n\t\t{`@P#r$i%v^3*+t)i&k++--`, \"_P_r_i_v_3_t_i_k_\"},\n\t}\n\n\tfor i, tt := range tests {\n\t\tt.Run(fmt.Sprint(i), func(t *testing.T) {\n\t\t\tsanitized := sanitizeIdentifier(tt.symbol)\n\t\t\tif tt.expected != sanitized {\n\t\t\t\tt.Errorf(\"Expected sanitized symbol to be '%s', got '%s'\", tt.expected, sanitized)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGetTracefsPath(t *testing.T) {\n\tpath, err := getTracefsPath()\n\ttestutils.SkipIfNotSupportedOnOS(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\t_, err = os.Stat(path)\n\tqt.Assert(t, qt.IsNil(err))\n}\n"
  },
  {
    "path": "internal/tracefs/probetype_string.go",
    "content": "// Code generated by \"stringer -type=ProbeType -linecomment\"; DO NOT EDIT.\n\npackage tracefs\n\nimport \"strconv\"\n\nfunc _() {\n\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n\t// Re-run the stringer command to generate them again.\n\tvar x [1]struct{}\n\t_ = x[Kprobe-0]\n\t_ = x[Uprobe-1]\n}\n\nconst _ProbeType_name = \"kprobeuprobe\"\n\nvar _ProbeType_index = [...]uint8{0, 6, 12}\n\nfunc (i ProbeType) String() string {\n\tidx := int(i) - 0\n\tif i < 0 || idx >= len(_ProbeType_index)-1 {\n\t\treturn \"ProbeType(\" + strconv.FormatInt(int64(i), 10) + \")\"\n\t}\n\treturn _ProbeType_name[_ProbeType_index[idx]:_ProbeType_index[idx+1]]\n}\n"
  },
  {
    "path": "internal/tracefs/uprobe.go",
    "content": "package tracefs\n\nimport \"fmt\"\n\n// UprobeToken creates the PATH:OFFSET(REF_CTR_OFFSET) token for the tracefs api.\nfunc UprobeToken(args ProbeArgs) string {\n\tpo := fmt.Sprintf(\"%s:%#x\", args.Path, args.Offset)\n\n\tif args.RefCtrOffset != 0 {\n\t\t// This is not documented in Documentation/trace/uprobetracer.txt.\n\t\t// elixir.bootlin.com/linux/v5.15-rc7/source/kernel/trace/trace.c#L5564\n\t\tpo += fmt.Sprintf(\"(%#x)\", args.RefCtrOffset)\n\t}\n\n\treturn po\n}\n"
  },
  {
    "path": "internal/tracefs/uprobe_test.go",
    "content": "package tracefs\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\nfunc TestUprobeToken(t *testing.T) {\n\ttests := []struct {\n\t\targs     ProbeArgs\n\t\texpected string\n\t}{\n\t\t{ProbeArgs{Path: \"/bin/bash\"}, \"/bin/bash:0x0\"},\n\t\t{ProbeArgs{Path: \"/bin/bash\", Offset: 1}, \"/bin/bash:0x1\"},\n\t\t{ProbeArgs{Path: \"/bin/bash\", Offset: 65535}, \"/bin/bash:0xffff\"},\n\t\t{ProbeArgs{Path: \"/bin/bash\", Offset: 65536}, \"/bin/bash:0x10000\"},\n\t\t{ProbeArgs{Path: \"/bin/bash\", Offset: 1, RefCtrOffset: 1}, \"/bin/bash:0x1(0x1)\"},\n\t\t{ProbeArgs{Path: \"/bin/bash\", Offset: 1, RefCtrOffset: 65535}, \"/bin/bash:0x1(0xffff)\"},\n\t}\n\n\tfor i, tt := range tests {\n\t\tt.Run(fmt.Sprint(i), func(t *testing.T) {\n\t\t\tpo := UprobeToken(tt.args)\n\t\t\tif tt.expected != po {\n\t\t\t\tt.Errorf(\"Expected path:offset to be '%s', got '%s'\", tt.expected, po)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/unix/doc.go",
    "content": "// Package unix re-exports Linux specific parts of golang.org/x/sys/unix.\n//\n// It avoids breaking compilation on other OS by providing stubs as follows:\n//   - Invoking a function always returns an error.\n//   - Errnos have distinct, non-zero values.\n//   - Constants have distinct but meaningless values.\n//   - Types use the same names for members, but may or may not follow the\n//     Linux layout.\npackage unix\n\n// Note: please don't add any custom API to this package. Use internal/sys instead.\n"
  },
  {
    "path": "internal/unix/errno_linux.go",
    "content": "package unix\n\nimport (\n\t\"syscall\"\n\n\tlinux \"golang.org/x/sys/unix\"\n)\n\ntype Errno = syscall.Errno\n\nconst (\n\tE2BIG      = linux.E2BIG\n\tEACCES     = linux.EACCES\n\tEAGAIN     = linux.EAGAIN\n\tEBADF      = linux.EBADF\n\tEEXIST     = linux.EEXIST\n\tEFAULT     = linux.EFAULT\n\tEILSEQ     = linux.EILSEQ\n\tEINTR      = linux.EINTR\n\tEINVAL     = linux.EINVAL\n\tENODEV     = linux.ENODEV\n\tENOENT     = linux.ENOENT\n\tENOSPC     = linux.ENOSPC\n\tEOPNOTSUPP = linux.EOPNOTSUPP\n\tEPERM      = linux.EPERM\n\tEPOLLIN    = linux.EPOLLIN\n\tESRCH      = linux.ESRCH\n\tESTALE     = linux.ESTALE\n)\n"
  },
  {
    "path": "internal/unix/errno_linux_test.go",
    "content": "package unix\n\nimport (\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\t\"golang.org/x/sys/unix\"\n)\n\nfunc TestErrnoIsUnix(t *testing.T) {\n\tqt.Assert(t, qt.ErrorIs(EPERM, unix.EPERM))\n\tqt.Assert(t, qt.ErrorIs(ENOENT, unix.ENOENT))\n}\n"
  },
  {
    "path": "internal/unix/errno_other.go",
    "content": "//go:build !linux && !windows\n\npackage unix\n\nimport \"syscall\"\n\ntype Errno = syscall.Errno\n\n// Errnos are distinct and non-zero.\nconst (\n\tE2BIG Errno = iota + 1\n\tEACCES\n\tEAGAIN\n\tEBADF\n\tEEXIST\n\tEFAULT\n\tEILSEQ\n\tEINTR\n\tEINVAL\n\tENODEV\n\tENOENT\n\tENOSPC\n\tENOTSUP\n\tENOTSUPP\n\tEOPNOTSUPP\n\tEPERM\n\tESRCH\n\tESTALE\n)\n"
  },
  {
    "path": "internal/unix/errno_string_windows.go",
    "content": "// Code generated by \"stringer -type=Errno -tags=windows -output=errno_string_windows.go\"; DO NOT EDIT.\n\npackage unix\n\nimport \"strconv\"\n\nfunc _() {\n\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n\t// Re-run the stringer command to generate them again.\n\tvar x [1]struct{}\n\t_ = x[EPERM-1]\n\t_ = x[ENOENT-2]\n\t_ = x[ESRCH-3]\n\t_ = x[EINTR-4]\n\t_ = x[E2BIG-7]\n\t_ = x[EBADF-9]\n\t_ = x[EAGAIN-11]\n\t_ = x[EACCES-13]\n\t_ = x[EFAULT-14]\n\t_ = x[EEXIST-17]\n\t_ = x[ENODEV-19]\n\t_ = x[EINVAL-22]\n\t_ = x[ENOSPC-28]\n\t_ = x[EILSEQ-42]\n\t_ = x[ENOTSUP-129]\n\t_ = x[EOPNOTSUPP-130]\n\t_ = x[ENOTSUPP-536870912]\n\t_ = x[ESTALE-536870913]\n}\n\nconst _Errno_name = \"EPERMENOENTESRCHEINTRE2BIGEBADFEAGAINEACCESEFAULTEEXISTENODEVEINVALENOSPCEILSEQENOTSUPEOPNOTSUPPENOTSUPPESTALE\"\n\nvar _Errno_map = map[Errno]string{\n\t1:         _Errno_name[0:5],\n\t2:         _Errno_name[5:11],\n\t3:         _Errno_name[11:16],\n\t4:         _Errno_name[16:21],\n\t7:         _Errno_name[21:26],\n\t9:         _Errno_name[26:31],\n\t11:        _Errno_name[31:37],\n\t13:        _Errno_name[37:43],\n\t14:        _Errno_name[43:49],\n\t17:        _Errno_name[49:55],\n\t19:        _Errno_name[55:61],\n\t22:        _Errno_name[61:67],\n\t28:        _Errno_name[67:73],\n\t42:        _Errno_name[73:79],\n\t129:       _Errno_name[79:86],\n\t130:       _Errno_name[86:96],\n\t536870912: _Errno_name[96:104],\n\t536870913: _Errno_name[104:110],\n}\n\nfunc (i Errno) String() string {\n\tif str, ok := _Errno_map[i]; ok {\n\t\treturn str\n\t}\n\treturn \"Errno(\" + strconv.FormatInt(int64(i), 10) + \")\"\n}\n"
  },
  {
    "path": "internal/unix/errno_test.go",
    "content": "package unix\n\nimport (\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestErrno(t *testing.T) {\n\tqt.Assert(t, qt.ErrorIs(ENOENT, os.ErrNotExist))\n}\n"
  },
  {
    "path": "internal/unix/errno_windows.go",
    "content": "package unix\n\n// The code in this file is derived from syscall_unix.go in the Go source code,\n// licensed under the MIT license.\n\nimport (\n\t\"errors\"\n\t\"os\"\n\t\"syscall\"\n)\n\n//go:generate go tool stringer -type=Errno -tags=windows -output=errno_string_windows.go\n\n// Windows specific constants for Unix errnos.\n//\n// The values do not always match Linux, for example EILSEQ and EOPNOTSUPP.\n//\n// See https://learn.microsoft.com/en-us/cpp/c-runtime-library/errno-constants?view=msvc-170\nconst (\n\tEPERM       Errno = 1\n\tENOENT      Errno = 2\n\tESRCH       Errno = 3\n\tEINTR       Errno = 4\n\tE2BIG       Errno = 7\n\tEBADF       Errno = 9\n\tEAGAIN      Errno = 11\n\tEACCES      Errno = 13\n\tEFAULT      Errno = 14\n\tEEXIST      Errno = 17\n\tENODEV      Errno = 19\n\tEINVAL      Errno = 22\n\tENFILE      Errno = 23\n\tEMFILE      Errno = 24\n\tENOSPC      Errno = 28\n\tENOSYS      Errno = 40\n\tENOTEMPTY   Errno = 41\n\tEILSEQ      Errno = 42\n\tENOTSUP     Errno = 129\n\tEOPNOTSUPP  Errno = 130\n\tEOTHER      Errno = 131\n\tETIMEDOUT   Errno = 138\n\tEWOULDBLOCK Errno = 140\n)\n\n// These constants do not exist on Windows and therefore have a non-zero\n// dummy value.\nconst (\n\tENOTSUPP Errno = Errno(syscall.APPLICATION_ERROR) + iota\n\tESTALE\n)\n\n// Errno is a Windows compatibility shim for Unix errnos.\ntype Errno uintptr\n\nfunc (e Errno) Error() string {\n\treturn e.String()\n}\n\nfunc (e Errno) Is(target error) bool {\n\tswitch target {\n\tcase os.ErrPermission:\n\t\treturn e == EACCES || e == EPERM\n\tcase os.ErrExist:\n\t\treturn e == EEXIST || e == ENOTEMPTY\n\tcase os.ErrNotExist:\n\t\treturn e == ENOENT\n\tcase errors.ErrUnsupported:\n\t\treturn e == ENOSYS || e == ENOTSUP || e == EOPNOTSUPP\n\t}\n\treturn false\n}\n\nfunc (e Errno) Temporary() bool {\n\treturn e == EINTR || e == EMFILE || e == ENFILE || e.Timeout()\n}\n\nfunc (e Errno) Timeout() bool {\n\treturn e == EAGAIN || e == EWOULDBLOCK || e == ETIMEDOUT\n}\n"
  },
  {
    "path": "internal/unix/error.go",
    "content": "package unix\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\t\"strings\"\n\n\t\"github.com/cilium/ebpf/internal\"\n)\n\n// errNonLinux returns an error which wraps [internal.ErrNotSupportedOnOS] and\n// includes the name of the calling function.\nfunc errNonLinux() error {\n\tname := \"unknown\"\n\tpc, _, _, ok := runtime.Caller(1)\n\tif ok {\n\t\tname = runtime.FuncForPC(pc).Name()\n\t\tif pos := strings.LastIndexByte(name, '.'); pos != -1 {\n\t\t\tname = name[pos+1:]\n\t\t}\n\t}\n\treturn fmt.Errorf(\"unix: %s: %w\", name, internal.ErrNotSupportedOnOS)\n}\n"
  },
  {
    "path": "internal/unix/error_test.go",
    "content": "package unix\n\nimport (\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/internal\"\n)\n\nfunc TestErrNonLinux(t *testing.T) {\n\terr := errNonLinux()\n\tqt.Assert(t, qt.StringContains(err.Error(), t.Name()))\n\tqt.Assert(t, qt.ErrorIs(err, internal.ErrNotSupportedOnOS))\n}\n"
  },
  {
    "path": "internal/unix/strings_other.go",
    "content": "//go:build !linux && !windows\n\npackage unix\n\nfunc BytePtrFromString(s string) (*byte, error) {\n\treturn nil, errNonLinux()\n}\n\nfunc ByteSliceToString(s []byte) string {\n\treturn \"\"\n}\n\nfunc ByteSliceFromString(s string) ([]byte, error) {\n\treturn nil, errNonLinux()\n}\n"
  },
  {
    "path": "internal/unix/strings_windows.go",
    "content": "package unix\n\nimport (\n\t\"syscall\"\n\n\t\"golang.org/x/sys/windows\"\n)\n\nfunc BytePtrFromString(s string) (*byte, error) {\n\tp, err := windows.BytePtrFromString(s)\n\tif err == syscall.EINVAL {\n\t\terr = EINVAL\n\t}\n\treturn p, err\n}\n\nfunc ByteSliceToString(s []byte) string {\n\treturn windows.ByteSliceToString(s)\n}\n\nfunc ByteSliceFromString(s string) ([]byte, error) {\n\treturn windows.ByteSliceFromString(s)\n}\n"
  },
  {
    "path": "internal/unix/types_linux.go",
    "content": "//go:build linux\n\npackage unix\n\nimport (\n\t\"syscall\"\n\t\"unsafe\"\n\n\tlinux \"golang.org/x/sys/unix\"\n)\n\nconst (\n\tBPF_F_NO_PREALLOC          = linux.BPF_F_NO_PREALLOC\n\tBPF_F_NUMA_NODE            = linux.BPF_F_NUMA_NODE\n\tBPF_F_RDONLY               = linux.BPF_F_RDONLY\n\tBPF_F_WRONLY               = linux.BPF_F_WRONLY\n\tBPF_F_RDONLY_PROG          = linux.BPF_F_RDONLY_PROG\n\tBPF_F_WRONLY_PROG          = linux.BPF_F_WRONLY_PROG\n\tBPF_F_SLEEPABLE            = linux.BPF_F_SLEEPABLE\n\tBPF_F_XDP_HAS_FRAGS        = linux.BPF_F_XDP_HAS_FRAGS\n\tBPF_F_MMAPABLE             = linux.BPF_F_MMAPABLE\n\tBPF_F_INNER_MAP            = linux.BPF_F_INNER_MAP\n\tBPF_F_KPROBE_MULTI_RETURN  = linux.BPF_F_KPROBE_MULTI_RETURN\n\tBPF_F_UPROBE_MULTI_RETURN  = linux.BPF_F_UPROBE_MULTI_RETURN\n\tBPF_F_LOCK                 = linux.BPF_F_LOCK\n\tBPF_OBJ_NAME_LEN           = linux.BPF_OBJ_NAME_LEN\n\tBPF_TAG_SIZE               = linux.BPF_TAG_SIZE\n\tBPF_RINGBUF_BUSY_BIT       = linux.BPF_RINGBUF_BUSY_BIT\n\tBPF_RINGBUF_DISCARD_BIT    = linux.BPF_RINGBUF_DISCARD_BIT\n\tBPF_RINGBUF_HDR_SZ         = linux.BPF_RINGBUF_HDR_SZ\n\tSYS_BPF                    = linux.SYS_BPF\n\tF_DUPFD_CLOEXEC            = linux.F_DUPFD_CLOEXEC\n\tEPOLL_CTL_ADD              = linux.EPOLL_CTL_ADD\n\tEPOLL_CLOEXEC              = linux.EPOLL_CLOEXEC\n\tO_RDONLY                   = linux.O_RDONLY\n\tO_CLOEXEC                  = linux.O_CLOEXEC\n\tO_NONBLOCK                 = linux.O_NONBLOCK\n\tPROT_NONE                  = linux.PROT_NONE\n\tPROT_READ                  = linux.PROT_READ\n\tPROT_WRITE                 = linux.PROT_WRITE\n\tMAP_ANON                   = linux.MAP_ANON\n\tMAP_SHARED                 = linux.MAP_SHARED\n\tMAP_FIXED                  = linux.MAP_FIXED\n\tMAP_PRIVATE                = linux.MAP_PRIVATE\n\tPERF_ATTR_SIZE_VER1        = linux.PERF_ATTR_SIZE_VER1\n\tPERF_TYPE_SOFTWARE         = linux.PERF_TYPE_SOFTWARE\n\tPERF_TYPE_TRACEPOINT       = linux.PERF_TYPE_TRACEPOINT\n\tPERF_COUNT_SW_BPF_OUTPUT   = linux.PERF_COUNT_SW_BPF_OUTPUT\n\tPERF_EVENT_IOC_DISABLE     = linux.PERF_EVENT_IOC_DISABLE\n\tPERF_EVENT_IOC_ENABLE      = linux.PERF_EVENT_IOC_ENABLE\n\tPERF_EVENT_IOC_SET_BPF     = linux.PERF_EVENT_IOC_SET_BPF\n\tPerfBitWatermark           = linux.PerfBitWatermark\n\tPerfBitWriteBackward       = linux.PerfBitWriteBackward\n\tPERF_SAMPLE_RAW            = linux.PERF_SAMPLE_RAW\n\tPERF_FLAG_FD_CLOEXEC       = linux.PERF_FLAG_FD_CLOEXEC\n\tRLIM_INFINITY              = linux.RLIM_INFINITY\n\tRLIMIT_MEMLOCK             = linux.RLIMIT_MEMLOCK\n\tBPF_STATS_RUN_TIME         = linux.BPF_STATS_RUN_TIME\n\tPERF_RECORD_LOST           = linux.PERF_RECORD_LOST\n\tPERF_RECORD_SAMPLE         = linux.PERF_RECORD_SAMPLE\n\tAT_FDCWD                   = linux.AT_FDCWD\n\tRENAME_NOREPLACE           = linux.RENAME_NOREPLACE\n\tSO_ATTACH_BPF              = linux.SO_ATTACH_BPF\n\tSO_DETACH_BPF              = linux.SO_DETACH_BPF\n\tSOL_SOCKET                 = linux.SOL_SOCKET\n\tSIGPROF                    = linux.SIGPROF\n\tSIGUSR1                    = linux.SIGUSR1\n\tSIG_BLOCK                  = linux.SIG_BLOCK\n\tSIG_UNBLOCK                = linux.SIG_UNBLOCK\n\tBPF_FS_MAGIC               = linux.BPF_FS_MAGIC\n\tTRACEFS_MAGIC              = linux.TRACEFS_MAGIC\n\tDEBUGFS_MAGIC              = linux.DEBUGFS_MAGIC\n\tBPF_RB_NO_WAKEUP           = linux.BPF_RB_NO_WAKEUP\n\tBPF_RB_FORCE_WAKEUP        = linux.BPF_RB_FORCE_WAKEUP\n\tAF_UNSPEC                  = linux.AF_UNSPEC\n\tIFF_UP                     = linux.IFF_UP\n\tCLONE_NEWNET               = linux.CLONE_NEWNET\n\tLINUX_CAPABILITY_VERSION_3 = linux.LINUX_CAPABILITY_VERSION_3\n)\n\ntype Statfs_t = linux.Statfs_t\ntype Stat_t = linux.Stat_t\ntype Rlimit = linux.Rlimit\ntype Signal = linux.Signal\ntype Sigset_t = linux.Sigset_t\ntype PerfEventMmapPage = linux.PerfEventMmapPage\ntype EpollEvent = linux.EpollEvent\ntype PerfEventAttr = linux.PerfEventAttr\ntype Utsname = linux.Utsname\ntype CPUSet = linux.CPUSet\ntype CapUserData = linux.CapUserData\ntype CapUserHeader = linux.CapUserHeader\n\nfunc Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {\n\treturn linux.Syscall(trap, a1, a2, a3)\n}\n\nfunc PthreadSigmask(how int, set, oldset *Sigset_t) error {\n\treturn linux.PthreadSigmask(how, set, oldset)\n}\n\nfunc FcntlInt(fd uintptr, cmd, arg int) (int, error) {\n\treturn linux.FcntlInt(fd, cmd, arg)\n}\n\nfunc IoctlSetInt(fd int, req uint, value int) error {\n\treturn linux.IoctlSetInt(fd, req, value)\n}\n\nfunc Statfs(path string, buf *Statfs_t) (err error) {\n\treturn linux.Statfs(path, buf)\n}\n\nfunc Close(fd int) (err error) {\n\treturn linux.Close(fd)\n}\n\nfunc EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {\n\treturn linux.EpollWait(epfd, events, msec)\n}\n\nfunc EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) {\n\treturn linux.EpollCtl(epfd, op, fd, event)\n}\n\nfunc Eventfd(initval uint, flags int) (fd int, err error) {\n\treturn linux.Eventfd(initval, flags)\n}\n\nfunc Write(fd int, p []byte) (n int, err error) {\n\treturn linux.Write(fd, p)\n}\n\nfunc EpollCreate1(flag int) (fd int, err error) {\n\treturn linux.EpollCreate1(flag)\n}\n\nfunc SetNonblock(fd int, nonblocking bool) (err error) {\n\treturn linux.SetNonblock(fd, nonblocking)\n}\n\nfunc Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) {\n\treturn linux.Mmap(fd, offset, length, prot, flags)\n}\n\n//go:nocheckptr\nfunc MmapPtr(fd int, offset int64, addr unsafe.Pointer, length uintptr, prot int, flags int) (ret unsafe.Pointer, err error) {\n\treturn linux.MmapPtr(fd, offset, addr, length, prot, flags)\n}\n\nfunc Munmap(b []byte) (err error) {\n\treturn linux.Munmap(b)\n}\n\nfunc PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) {\n\treturn linux.PerfEventOpen(attr, pid, cpu, groupFd, flags)\n}\n\nfunc Uname(buf *Utsname) (err error) {\n\treturn linux.Uname(buf)\n}\n\nfunc Getpid() int {\n\treturn linux.Getpid()\n}\n\nfunc Gettid() int {\n\treturn linux.Gettid()\n}\n\nfunc Tgkill(tgid int, tid int, sig syscall.Signal) (err error) {\n\treturn linux.Tgkill(tgid, tid, sig)\n}\n\nfunc BytePtrFromString(s string) (*byte, error) {\n\treturn linux.BytePtrFromString(s)\n}\n\nfunc ByteSliceToString(s []byte) string {\n\treturn linux.ByteSliceToString(s)\n}\n\nfunc ByteSliceFromString(s string) ([]byte, error) {\n\treturn linux.ByteSliceFromString(s)\n}\n\nfunc Renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags uint) error {\n\treturn linux.Renameat2(olddirfd, oldpath, newdirfd, newpath, flags)\n}\n\nfunc Prlimit(pid, resource int, new, old *Rlimit) error {\n\treturn linux.Prlimit(pid, resource, new, old)\n}\n\nfunc Open(path string, mode int, perm uint32) (int, error) {\n\treturn linux.Open(path, mode, perm)\n}\n\nfunc Fstat(fd int, stat *Stat_t) error {\n\treturn linux.Fstat(fd, stat)\n}\n\nfunc SetsockoptInt(fd, level, opt, value int) error {\n\treturn linux.SetsockoptInt(fd, level, opt, value)\n}\n\nfunc SchedSetaffinity(pid int, set *CPUSet) error {\n\treturn linux.SchedSetaffinity(pid, set)\n}\n\nfunc SchedGetaffinity(pid int, set *CPUSet) error {\n\treturn linux.SchedGetaffinity(pid, set)\n}\n\nfunc Auxv() ([][2]uintptr, error) {\n\treturn linux.Auxv()\n}\n\nfunc Unshare(flag int) error {\n\treturn linux.Unshare(flag)\n}\n\nfunc Setns(fd int, nstype int) error {\n\treturn linux.Setns(fd, nstype)\n}\n\nfunc Capget(hdr *CapUserHeader, data *CapUserData) (err error) {\n\treturn linux.Capget(hdr, data)\n}\n\nfunc Capset(hdr *CapUserHeader, data *CapUserData) (err error) {\n\treturn linux.Capset(hdr, data)\n}\n"
  },
  {
    "path": "internal/unix/types_other.go",
    "content": "//go:build !linux\n\npackage unix\n\nimport (\n\t\"syscall\"\n\t\"unsafe\"\n)\n\n// Constants are distinct to avoid breaking switch statements.\nconst (\n\tBPF_F_NO_PREALLOC = iota\n\tBPF_F_NUMA_NODE\n\tBPF_F_RDONLY\n\tBPF_F_WRONLY\n\tBPF_F_RDONLY_PROG\n\tBPF_F_WRONLY_PROG\n\tBPF_F_SLEEPABLE\n\tBPF_F_MMAPABLE\n\tBPF_F_INNER_MAP\n\tBPF_F_KPROBE_MULTI_RETURN\n\tBPF_F_UPROBE_MULTI_RETURN\n\tBPF_F_XDP_HAS_FRAGS\n\tBPF_OBJ_NAME_LEN\n\tBPF_TAG_SIZE\n\tBPF_RINGBUF_BUSY_BIT\n\tBPF_RINGBUF_DISCARD_BIT\n\tBPF_RINGBUF_HDR_SZ\n\tSYS_BPF\n\tF_DUPFD_CLOEXEC\n\tEPOLLIN\n\tEPOLL_CTL_ADD\n\tEPOLL_CLOEXEC\n\tO_CLOEXEC\n\tO_NONBLOCK\n\tPROT_NONE\n\tPROT_READ\n\tPROT_WRITE\n\tMAP_ANON\n\tMAP_SHARED\n\tMAP_FIXED\n\tMAP_PRIVATE\n\tPERF_ATTR_SIZE_VER1\n\tPERF_TYPE_SOFTWARE\n\tPERF_TYPE_TRACEPOINT\n\tPERF_COUNT_SW_BPF_OUTPUT\n\tPERF_EVENT_IOC_DISABLE\n\tPERF_EVENT_IOC_ENABLE\n\tPERF_EVENT_IOC_SET_BPF\n\tPerfBitWatermark\n\tPerfBitWriteBackward\n\tPERF_SAMPLE_RAW\n\tPERF_FLAG_FD_CLOEXEC\n\tRLIM_INFINITY\n\tRLIMIT_MEMLOCK\n\tBPF_STATS_RUN_TIME\n\tPERF_RECORD_LOST\n\tPERF_RECORD_SAMPLE\n\tAT_FDCWD\n\tRENAME_NOREPLACE\n\tSO_ATTACH_BPF\n\tSO_DETACH_BPF\n\tSOL_SOCKET\n\tSIGPROF\n\tSIGUSR1\n\tSIG_BLOCK\n\tSIG_UNBLOCK\n\tBPF_FS_MAGIC\n\tTRACEFS_MAGIC\n\tDEBUGFS_MAGIC\n\tBPF_RB_NO_WAKEUP\n\tBPF_RB_FORCE_WAKEUP\n\tBPF_F_LOCK\n\tAF_UNSPEC\n\tIFF_UP\n\tLINUX_CAPABILITY_VERSION_3\n)\n\ntype Statfs_t struct {\n\tType    int64\n\tBsize   int64\n\tBlocks  uint64\n\tBfree   uint64\n\tBavail  uint64\n\tFiles   uint64\n\tFfree   uint64\n\tFsid    [2]int32\n\tNamelen int64\n\tFrsize  int64\n\tFlags   int64\n\tSpare   [4]int64\n}\n\ntype Stat_t struct {\n\tDev     uint64\n\tIno     uint64\n\tNlink   uint64\n\tMode    uint32\n\tUid     uint32\n\tGid     uint32\n\t_       int32\n\tRdev    uint64\n\tSize    int64\n\tBlksize int64\n\tBlocks  int64\n}\n\ntype Rlimit struct {\n\tCur uint64\n\tMax uint64\n}\n\ntype Signal int\n\ntype Sigset_t struct {\n\tVal [4]uint64\n}\n\ntype CapUserHeader struct {\n\tVersion uint32\n\tPid     int32\n}\n\ntype CapUserData struct {\n\tEffective   uint32\n\tPermitted   uint32\n\tInheritable uint32\n}\n\nfunc Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) {\n\treturn 0, 0, ENOTSUP\n}\n\nfunc PthreadSigmask(how int, set, oldset *Sigset_t) error {\n\treturn errNonLinux()\n}\n\nfunc FcntlInt(fd uintptr, cmd, arg int) (int, error) {\n\treturn -1, errNonLinux()\n}\n\nfunc IoctlSetInt(fd int, req uint, value int) error {\n\treturn errNonLinux()\n}\n\nfunc Statfs(path string, buf *Statfs_t) error {\n\treturn errNonLinux()\n}\n\nfunc Close(fd int) (err error) {\n\treturn errNonLinux()\n}\n\ntype EpollEvent struct {\n\tEvents uint32\n\tFd     int32\n\tPad    int32\n}\n\nfunc EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {\n\treturn 0, errNonLinux()\n}\n\nfunc EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) {\n\treturn errNonLinux()\n}\n\nfunc Eventfd(initval uint, flags int) (fd int, err error) {\n\treturn 0, errNonLinux()\n}\n\nfunc Write(fd int, p []byte) (n int, err error) {\n\treturn 0, errNonLinux()\n}\n\nfunc EpollCreate1(flag int) (fd int, err error) {\n\treturn 0, errNonLinux()\n}\n\ntype PerfEventMmapPage struct {\n\tVersion        uint32\n\tCompat_version uint32\n\tLock           uint32\n\tIndex          uint32\n\tOffset         int64\n\tTime_enabled   uint64\n\tTime_running   uint64\n\tCapabilities   uint64\n\tPmc_width      uint16\n\tTime_shift     uint16\n\tTime_mult      uint32\n\tTime_offset    uint64\n\tTime_zero      uint64\n\tSize           uint32\n\n\tData_head   uint64\n\tData_tail   uint64\n\tData_offset uint64\n\tData_size   uint64\n\tAux_head    uint64\n\tAux_tail    uint64\n\tAux_offset  uint64\n\tAux_size    uint64\n}\n\nfunc SetNonblock(fd int, nonblocking bool) (err error) {\n\treturn errNonLinux()\n}\n\nfunc Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) {\n\treturn []byte{}, errNonLinux()\n}\n\nfunc MmapPtr(fd int, offset int64, addr unsafe.Pointer, length uintptr, prot int, flags int) (ret unsafe.Pointer, err error) {\n\treturn nil, errNonLinux()\n}\n\nfunc Munmap(b []byte) (err error) {\n\treturn errNonLinux()\n}\n\ntype PerfEventAttr struct {\n\tType               uint32\n\tSize               uint32\n\tConfig             uint64\n\tSample             uint64\n\tSample_type        uint64\n\tRead_format        uint64\n\tBits               uint64\n\tWakeup             uint32\n\tBp_type            uint32\n\tExt1               uint64\n\tExt2               uint64\n\tBranch_sample_type uint64\n\tSample_regs_user   uint64\n\tSample_stack_user  uint32\n\tClockid            int32\n\tSample_regs_intr   uint64\n\tAux_watermark      uint32\n\tSample_max_stack   uint16\n}\n\nfunc PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) {\n\treturn 0, errNonLinux()\n}\n\ntype Utsname struct {\n\tRelease [65]byte\n\tVersion [65]byte\n}\n\nfunc Uname(buf *Utsname) (err error) {\n\treturn errNonLinux()\n}\n\nfunc Getpid() int {\n\treturn -1\n}\n\nfunc Gettid() int {\n\treturn -1\n}\n\nfunc Tgkill(tgid int, tid int, sig syscall.Signal) (err error) {\n\treturn errNonLinux()\n}\n\nfunc Renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags uint) error {\n\treturn errNonLinux()\n}\n\nfunc Prlimit(pid, resource int, new, old *Rlimit) error {\n\treturn errNonLinux()\n}\n\nfunc Open(path string, mode int, perm uint32) (int, error) {\n\treturn -1, errNonLinux()\n}\n\nfunc Fstat(fd int, stat *Stat_t) error {\n\treturn errNonLinux()\n}\n\nfunc SetsockoptInt(fd, level, opt, value int) error {\n\treturn errNonLinux()\n}\n\ntype CPUSet struct{}\n\nfunc (*CPUSet) Set(int) {}\n\nfunc SchedSetaffinity(pid int, set *CPUSet) error {\n\treturn errNonLinux()\n}\n\nfunc SchedGetaffinity(pid int, set *CPUSet) error {\n\treturn errNonLinux()\n}\n\nfunc Auxv() ([][2]uintptr, error) {\n\treturn nil, errNonLinux()\n}\n\nfunc Capget(hdr *CapUserHeader, data *CapUserData) (err error) {\n\treturn errNonLinux()\n}\n\nfunc Capset(hdr *CapUserHeader, data *CapUserData) (err error) {\n\treturn errNonLinux()\n}\n"
  },
  {
    "path": "internal/version.go",
    "content": "package internal\n\nimport (\n\t\"fmt\"\n)\n\nconst (\n\t// Version constant used in ELF binaries indicating that the loader needs to\n\t// substitute the eBPF program's version with the value of the kernel's\n\t// KERNEL_VERSION compile-time macro. Used for compatibility with BCC, gobpf\n\t// and RedSift.\n\tMagicKernelVersion = 0xFFFFFFFE\n)\n\n// A Version in the form Major.Minor.Patch.\ntype Version [3]uint16\n\n// NewVersion creates a version from a string like \"Major.Minor.Patch\".\n//\n// Patch is optional.\nfunc NewVersion(ver string) (Version, error) {\n\tvar major, minor, patch uint16\n\tn, _ := fmt.Sscanf(ver, \"%d.%d.%d\", &major, &minor, &patch)\n\tif n < 2 {\n\t\treturn Version{}, fmt.Errorf(\"invalid version: %s\", ver)\n\t}\n\treturn Version{major, minor, patch}, nil\n}\n\n// NewVersionFromCode creates a version from a LINUX_VERSION_CODE.\nfunc NewVersionFromCode(code uint32) Version {\n\treturn Version{\n\t\tuint16(uint8(code >> 16)),\n\t\tuint16(uint8(code >> 8)),\n\t\tuint16(uint8(code)),\n\t}\n}\n\nfunc (v Version) String() string {\n\tif v[2] == 0 {\n\t\treturn fmt.Sprintf(\"v%d.%d\", v[0], v[1])\n\t}\n\treturn fmt.Sprintf(\"v%d.%d.%d\", v[0], v[1], v[2])\n}\n\n// Less returns true if the version is less than another version.\nfunc (v Version) Less(other Version) bool {\n\tfor i, a := range v {\n\t\tif a == other[i] {\n\t\t\tcontinue\n\t\t}\n\t\treturn a < other[i]\n\t}\n\treturn false\n}\n\n// Unspecified returns true if the version is all zero.\nfunc (v Version) Unspecified() bool {\n\treturn v[0] == 0 && v[1] == 0 && v[2] == 0\n}\n\n// Kernel implements the kernel's KERNEL_VERSION macro from linux/version.h.\n// It represents the kernel version and patch level as a single value.\nfunc (v Version) Kernel() uint32 {\n\n\t// Kernels 4.4 and 4.9 have their SUBLEVEL clamped to 255 to avoid\n\t// overflowing into PATCHLEVEL.\n\t// See kernel commit 9b82f13e7ef3 (\"kbuild: clamp SUBLEVEL to 255\").\n\ts := min(v[2], 255)\n\n\t// Truncate members to uint8 to prevent them from spilling over into\n\t// each other when overflowing 8 bits.\n\treturn uint32(uint8(v[0]))<<16 | uint32(uint8(v[1]))<<8 | uint32(uint8(s))\n}\n"
  },
  {
    "path": "internal/version_test.go",
    "content": "package internal\n\nimport (\n\t\"testing\"\n)\n\nfunc TestVersion(t *testing.T) {\n\ta, err := NewVersion(\"1.2\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tb, err := NewVersion(\"2.2.1\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif !a.Less(b) {\n\t\tt.Error(\"A should be less than B\")\n\t}\n\n\tif b.Less(a) {\n\t\tt.Error(\"B shouldn't be less than A\")\n\t}\n\n\tv200 := Version{2, 0, 0}\n\tif !a.Less(v200) {\n\t\tt.Error(\"1.2.1 should not be less than 2.0.0\")\n\t}\n\n\tif v200.Less(a) {\n\t\tt.Error(\"2.0.0 should not be less than 1.2.1\")\n\t}\n}\n\nfunc TestKernelVersion(t *testing.T) {\n\t// Kernels 4.4 and 4.9 have a SUBLEVEL of over 255 and clamp it to 255.\n\t// In our implementation, the other version segments are truncated.\n\tif v, want := (Version{256, 256, 256}), uint32(255); v.Kernel() != want {\n\t\tt.Errorf(\"256.256.256 should result in a kernel version of %d, got: %d\", want, v.Kernel())\n\t}\n\n\t// Known good version.\n\tif v, want := (Version{4, 9, 128}), uint32(264576); v.Kernel() != want {\n\t\tt.Errorf(\"4.9.1 should result in a kernel version of %d, got: %d\", want, v.Kernel())\n\t}\n}\n\nfunc TestVersionFromCode(t *testing.T) {\n\tvar tests = []struct {\n\t\tname string\n\t\tcode uint32\n\t\tv    Version\n\t}{\n\t\t{\"0.0.0\", 0, Version{0, 0, 0}},\n\t\t{\"1.0.0\", 0x10000, Version{1, 0, 0}},\n\t\t{\"4.4.255\", 0x404ff, Version{4, 4, 255}},\n\t\t{\"255.255.255\", 0xffffff, Version{255, 255, 255}},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tv := NewVersionFromCode(tt.code)\n\t\t\tif v != tt.v {\n\t\t\t\tt.Errorf(\"unexpected version for code '%d'. got: %v, want: %v\", tt.code, v, tt.v)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "link/anchor.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n)\n\nconst anchorFlags = sys.BPF_F_REPLACE |\n\tsys.BPF_F_BEFORE |\n\tsys.BPF_F_AFTER |\n\tsys.BPF_F_ID |\n\tsys.BPF_F_LINK_MPROG\n\n// Anchor is a reference to a link or program.\n//\n// It is used to describe where an attachment or detachment should take place\n// for link types which support multiple attachment.\ntype Anchor interface {\n\t// anchor returns an fd or ID and a set of flags.\n\t//\n\t// By default fdOrID is taken to reference a program, but BPF_F_LINK_MPROG\n\t// changes this to refer to a link instead.\n\t//\n\t// BPF_F_BEFORE, BPF_F_AFTER, BPF_F_REPLACE modify where a link or program\n\t// is attached. The default behaviour if none of these flags is specified\n\t// matches BPF_F_AFTER.\n\tanchor() (fdOrID, flags uint32, _ error)\n}\n\ntype firstAnchor struct{}\n\nfunc (firstAnchor) anchor() (fdOrID, flags uint32, _ error) {\n\treturn 0, sys.BPF_F_BEFORE, nil\n}\n\n// Head is the position before all other programs or links.\nfunc Head() Anchor {\n\treturn firstAnchor{}\n}\n\ntype lastAnchor struct{}\n\nfunc (lastAnchor) anchor() (fdOrID, flags uint32, _ error) {\n\treturn 0, sys.BPF_F_AFTER, nil\n}\n\n// Tail is the position after all other programs or links.\nfunc Tail() Anchor {\n\treturn lastAnchor{}\n}\n\n// Before is the position just in front of target.\nfunc BeforeLink(target Link) Anchor {\n\treturn anchor{target, sys.BPF_F_BEFORE}\n}\n\n// After is the position just after target.\nfunc AfterLink(target Link) Anchor {\n\treturn anchor{target, sys.BPF_F_AFTER}\n}\n\n// Before is the position just in front of target.\nfunc BeforeLinkByID(target ID) Anchor {\n\treturn anchor{target, sys.BPF_F_BEFORE}\n}\n\n// After is the position just after target.\nfunc AfterLinkByID(target ID) Anchor {\n\treturn anchor{target, sys.BPF_F_AFTER}\n}\n\n// Before is the position just in front of target.\nfunc BeforeProgram(target *ebpf.Program) Anchor {\n\treturn anchor{target, sys.BPF_F_BEFORE}\n}\n\n// After is the position just after target.\nfunc AfterProgram(target *ebpf.Program) Anchor {\n\treturn anchor{target, sys.BPF_F_AFTER}\n}\n\n// Replace the target itself.\nfunc ReplaceProgram(target *ebpf.Program) Anchor {\n\treturn anchor{target, sys.BPF_F_REPLACE}\n}\n\n// Before is the position just in front of target.\nfunc BeforeProgramByID(target ebpf.ProgramID) Anchor {\n\treturn anchor{target, sys.BPF_F_BEFORE}\n}\n\n// After is the position just after target.\nfunc AfterProgramByID(target ebpf.ProgramID) Anchor {\n\treturn anchor{target, sys.BPF_F_AFTER}\n}\n\n// Replace the target itself.\nfunc ReplaceProgramByID(target ebpf.ProgramID) Anchor {\n\treturn anchor{target, sys.BPF_F_REPLACE}\n}\n\ntype anchor struct {\n\ttarget   any\n\tposition uint32\n}\n\nfunc (ap anchor) anchor() (fdOrID, flags uint32, _ error) {\n\tvar typeFlag uint32\n\tswitch target := ap.target.(type) {\n\tcase *ebpf.Program:\n\t\tfd := target.FD()\n\t\tif fd < 0 {\n\t\t\treturn 0, 0, sys.ErrClosedFd\n\t\t}\n\t\tfdOrID = uint32(fd)\n\t\ttypeFlag = 0\n\tcase ebpf.ProgramID:\n\t\tfdOrID = uint32(target)\n\t\ttypeFlag = sys.BPF_F_ID\n\tcase interface{ FD() int }:\n\t\tfd := target.FD()\n\t\tif fd < 0 {\n\t\t\treturn 0, 0, sys.ErrClosedFd\n\t\t}\n\t\tfdOrID = uint32(fd)\n\t\ttypeFlag = sys.BPF_F_LINK_MPROG\n\tcase ID:\n\t\tfdOrID = uint32(target)\n\t\ttypeFlag = sys.BPF_F_LINK_MPROG | sys.BPF_F_ID\n\tdefault:\n\t\treturn 0, 0, fmt.Errorf(\"invalid target %T\", ap.target)\n\t}\n\n\treturn fdOrID, ap.position | typeFlag, nil\n}\n"
  },
  {
    "path": "link/cgroup.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n)\n\ntype cgroupAttachFlags uint32\n\nconst (\n\t// Allow programs attached to sub-cgroups to override the verdict of this\n\t// program.\n\tflagAllowOverride cgroupAttachFlags = 1 << iota\n\t// Allow attaching multiple programs to the cgroup. Only works if the cgroup\n\t// has zero or more programs attached using the Multi flag. Implies override.\n\tflagAllowMulti\n\t// Set automatically by progAttachCgroup.Update(). Used for updating a\n\t// specific given program attached in multi-mode.\n\tflagReplace\n)\n\ntype CgroupOptions struct {\n\t// Path to a cgroupv2 folder.\n\tPath string\n\t// One of the AttachCgroup* constants\n\tAttach ebpf.AttachType\n\t// Program must be of type CGroup*, and the attach type must match Attach.\n\tProgram *ebpf.Program\n}\n\n// AttachCgroup links a BPF program to a cgroup.\n//\n// If the running kernel doesn't support bpf_link, attempts to emulate its\n// semantics using the legacy PROG_ATTACH mechanism. If bpf_link is not\n// available, the returned [Link] will not support pinning to bpffs.\n//\n// If you need more control over attachment flags or the attachment mechanism\n// used, look at [RawAttachProgram] and [AttachRawLink] instead.\nfunc AttachCgroup(opts CgroupOptions) (cg Link, err error) {\n\tcgroup, err := os.Open(opts.Path)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't open cgroup: %s\", err)\n\t}\n\tdefer func() {\n\t\tif _, ok := cg.(*progAttachCgroup); ok {\n\t\t\t// Skip closing the cgroup handle if we return a valid progAttachCgroup,\n\t\t\t// where the handle is retained to implement Update().\n\t\t\treturn\n\t\t}\n\t\tcgroup.Close()\n\t}()\n\n\tcg, err = newLinkCgroup(cgroup, opts.Attach, opts.Program)\n\tif err == nil {\n\t\treturn cg, nil\n\t}\n\n\tif errors.Is(err, ErrNotSupported) {\n\t\tcg, err = newProgAttachCgroup(cgroup, opts.Attach, opts.Program, flagAllowMulti)\n\t}\n\tif errors.Is(err, ErrNotSupported) {\n\t\tcg, err = newProgAttachCgroup(cgroup, opts.Attach, opts.Program, flagAllowOverride)\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn cg, nil\n}\n\ntype progAttachCgroup struct {\n\tcgroup     *os.File\n\tcurrent    *ebpf.Program\n\tattachType ebpf.AttachType\n\tflags      cgroupAttachFlags\n}\n\nvar _ Link = (*progAttachCgroup)(nil)\n\nfunc (cg *progAttachCgroup) isLink() {}\n\n// newProgAttachCgroup attaches prog to cgroup using BPF_PROG_ATTACH.\n// cgroup and prog are retained by [progAttachCgroup].\nfunc newProgAttachCgroup(cgroup *os.File, attach ebpf.AttachType, prog *ebpf.Program, flags cgroupAttachFlags) (*progAttachCgroup, error) {\n\tif flags&flagAllowMulti > 0 {\n\t\tif err := haveProgAttachReplace(); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"can't support multiple programs: %w\", err)\n\t\t}\n\t}\n\n\t// Use a program handle that cannot be closed by the caller.\n\tclone, err := prog.Clone()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\terr = RawAttachProgram(RawAttachProgramOptions{\n\t\tTarget:  int(cgroup.Fd()),\n\t\tProgram: clone,\n\t\tFlags:   uint32(flags),\n\t\tAttach:  attach,\n\t})\n\tif err != nil {\n\t\tclone.Close()\n\t\treturn nil, fmt.Errorf(\"cgroup: %w\", err)\n\t}\n\n\treturn &progAttachCgroup{cgroup, clone, attach, flags}, nil\n}\n\nfunc (cg *progAttachCgroup) Close() error {\n\tdefer cg.cgroup.Close()\n\tdefer cg.current.Close()\n\n\terr := RawDetachProgram(RawDetachProgramOptions{\n\t\tTarget:  int(cg.cgroup.Fd()),\n\t\tProgram: cg.current,\n\t\tAttach:  cg.attachType,\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"close cgroup: %s\", err)\n\t}\n\treturn nil\n}\n\nfunc (cg *progAttachCgroup) Update(prog *ebpf.Program) error {\n\tnew, err := prog.Clone()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\targs := RawAttachProgramOptions{\n\t\tTarget:  int(cg.cgroup.Fd()),\n\t\tProgram: prog,\n\t\tAttach:  cg.attachType,\n\t\tFlags:   uint32(cg.flags),\n\t}\n\n\tif cg.flags&flagAllowMulti > 0 {\n\t\t// Atomically replacing multiple programs requires at least\n\t\t// 5.5 (commit 7dd68b3279f17921 \"bpf: Support replacing cgroup-bpf\n\t\t// program in MULTI mode\")\n\t\targs.Anchor = ReplaceProgram(cg.current)\n\t}\n\n\tif err := RawAttachProgram(args); err != nil {\n\t\tnew.Close()\n\t\treturn fmt.Errorf(\"can't update cgroup: %s\", err)\n\t}\n\n\tcg.current.Close()\n\tcg.current = new\n\treturn nil\n}\n\nfunc (cg *progAttachCgroup) Pin(string) error {\n\treturn fmt.Errorf(\"can't pin cgroup: %w\", ErrNotSupported)\n}\n\nfunc (cg *progAttachCgroup) Unpin() error {\n\treturn fmt.Errorf(\"can't unpin cgroup: %w\", ErrNotSupported)\n}\n\nfunc (cg *progAttachCgroup) Detach() error {\n\treturn fmt.Errorf(\"can't detach cgroup: %w\", ErrNotSupported)\n}\n\nfunc (cg *progAttachCgroup) Info() (*Info, error) {\n\treturn nil, fmt.Errorf(\"can't get cgroup info: %w\", ErrNotSupported)\n}\n\ntype linkCgroup struct {\n\tRawLink\n}\n\nvar _ Link = (*linkCgroup)(nil)\n\n// newLinkCgroup attaches prog to cgroup using BPF_LINK_CREATE.\nfunc newLinkCgroup(cgroup *os.File, attach ebpf.AttachType, prog *ebpf.Program) (*linkCgroup, error) {\n\tlink, err := AttachRawLink(RawLinkOptions{\n\t\tTarget:  int(cgroup.Fd()),\n\t\tProgram: prog,\n\t\tAttach:  attach,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &linkCgroup{*link}, err\n}\n\nfunc (cg *linkCgroup) Info() (*Info, error) {\n\tvar info sys.CgroupLinkInfo\n\tif err := sys.ObjInfo(cg.fd, &info); err != nil {\n\t\treturn nil, fmt.Errorf(\"cgroup link info: %s\", err)\n\t}\n\textra := &CgroupInfo{\n\t\tCgroupId:   info.CgroupId,\n\t\tAttachType: info.AttachType,\n\t}\n\n\treturn &Info{\n\t\tinfo.Type,\n\t\tinfo.Id,\n\t\tebpf.ProgramID(info.ProgId),\n\t\textra,\n\t}, nil\n}\n"
  },
  {
    "path": "link/cgroup_test.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc TestAttachCgroup(t *testing.T) {\n\tcgroup, prog := mustCgroupFixtures(t)\n\n\tlink, err := AttachCgroup(CgroupOptions{\n\t\tPath:    cgroup.Name(),\n\t\tAttach:  ebpf.AttachCGroupInetEgress,\n\t\tProgram: prog,\n\t})\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer link.Close()\n\n\tif haveBPFLink() == nil {\n\t\tif _, ok := link.(*linkCgroup); !ok {\n\t\t\tt.Fatalf(\"Have support for bpf_link, but got %T instead of linkCgroup\", link)\n\t\t}\n\t} else {\n\t\tif _, ok := link.(*progAttachCgroup); !ok {\n\t\t\tt.Fatalf(\"Expected progAttachCgroup, got %T instead\", link)\n\t\t}\n\t}\n}\n\nfunc TestProgAttachCgroup(t *testing.T) {\n\tcgroup, prog := mustCgroupFixtures(t)\n\n\tlink, err := newProgAttachCgroup(cgroup, ebpf.AttachCGroupInetEgress, prog, 0)\n\tif err != nil {\n\t\tt.Fatal(\"Can't create link:\", err)\n\t}\n\n\ttestLink(t, link, prog)\n}\n\nfunc TestProgAttachCgroupAllowMulti(t *testing.T) {\n\tcgroup, prog := mustCgroupFixtures(t)\n\n\tlink, err := newProgAttachCgroup(cgroup, ebpf.AttachCGroupInetEgress, prog, flagAllowMulti)\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(\"Can't create link:\", err)\n\t}\n\n\t// It's currently not possible for a program to replace\n\t// itself.\n\tprog2 := mustLoadProgram(t, ebpf.CGroupSKB, ebpf.AttachCGroupInetEgress, \"\")\n\ttestLink(t, link, prog2)\n}\n\nfunc TestLinkCgroup(t *testing.T) {\n\tcgroup, prog := mustCgroupFixtures(t)\n\n\tlink, err := newLinkCgroup(cgroup, ebpf.AttachCGroupInetEgress, prog)\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(\"Can't create link:\", err)\n\t}\n\n\ttestLink(t, link, prog)\n}\n"
  },
  {
    "path": "link/doc.go",
    "content": "// Package link allows attaching eBPF programs to various kernel hooks.\npackage link\n"
  },
  {
    "path": "link/helpers_windows_test.go",
    "content": "package link\n\nimport (\n\t\"errors\"\n\t\"os\"\n\t\"testing\"\n\n\t\"golang.org/x/sys/windows\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\n// windowsProgramTypeForGUID resolves a GUID to a ProgramType.\nfunc windowsProgramTypeForGUID(tb testing.TB, guid windows.GUID) ebpf.ProgramType {\n\tprogramType, err := ebpf.WindowsProgramTypeForGUID(guid.String())\n\tif errors.Is(err, os.ErrNotExist) {\n\t\ttb.Skipf(\"Attach type not found for GUID %v\", guid)\n\t}\n\tqt.Assert(tb, qt.IsNil(err))\n\treturn programType\n}\n\n// windowsAttachTypeForGUID resolves a GUID to an AttachType.\nfunc windowsAttachTypeForGUID(tb testing.TB, guid windows.GUID) ebpf.AttachType {\n\tattachType, err := ebpf.WindowsAttachTypeForGUID(guid.String())\n\tif errors.Is(err, os.ErrNotExist) {\n\t\ttb.Skipf(\"Attach type not found for GUID %v\", guid)\n\t}\n\tqt.Assert(tb, qt.IsNil(err))\n\treturn attachType\n}\n"
  },
  {
    "path": "link/iter.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"unsafe\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n)\n\ntype IterOptions struct {\n\t// Program must be of type Tracing with attach type\n\t// AttachTraceIter. The kind of iterator to attach to is\n\t// determined at load time via the AttachTo field.\n\t//\n\t// AttachTo requires the kernel to include BTF of itself,\n\t// and it to be compiled with a recent pahole (>= 1.16).\n\tProgram *ebpf.Program\n\n\t// Map specifies the target map for bpf_map_elem and sockmap iterators.\n\t// It may be nil.\n\tMap *ebpf.Map\n}\n\n// AttachIter attaches a BPF seq_file iterator.\nfunc AttachIter(opts IterOptions) (*Iter, error) {\n\tprogFd := opts.Program.FD()\n\tif progFd < 0 {\n\t\treturn nil, fmt.Errorf(\"invalid program: %s\", sys.ErrClosedFd)\n\t}\n\n\tvar info bpfIterLinkInfoMap\n\tif opts.Map != nil {\n\t\tmapFd := opts.Map.FD()\n\t\tif mapFd < 0 {\n\t\t\treturn nil, fmt.Errorf(\"invalid map: %w\", sys.ErrClosedFd)\n\t\t}\n\t\tinfo.map_fd = uint32(mapFd)\n\t}\n\n\tattr := sys.LinkCreateIterAttr{\n\t\tProgFd:      uint32(progFd),\n\t\tAttachType:  sys.AttachType(ebpf.AttachTraceIter),\n\t\tIterInfo:    sys.UnsafePointer(unsafe.Pointer(&info)),\n\t\tIterInfoLen: uint32(unsafe.Sizeof(info)),\n\t}\n\n\tfd, err := sys.LinkCreateIter(&attr)\n\tif err != nil {\n\t\tif haveFeatErr := haveBPFLink(); haveFeatErr != nil {\n\t\t\treturn nil, haveFeatErr\n\t\t}\n\t\treturn nil, fmt.Errorf(\"can't link iterator: %w\", err)\n\t}\n\n\treturn &Iter{RawLink{fd, \"\"}}, err\n}\n\n// Iter represents an attached bpf_iter.\ntype Iter struct {\n\tRawLink\n}\n\n// Open creates a new instance of the iterator.\n//\n// Reading from the returned reader triggers the BPF program.\nfunc (it *Iter) Open() (io.ReadCloser, error) {\n\tattr := &sys.IterCreateAttr{\n\t\tLinkFd: it.fd.Uint(),\n\t}\n\n\tfd, err := sys.IterCreate(attr)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't create iterator: %w\", err)\n\t}\n\n\treturn fd.File(\"bpf_iter\")\n}\n\n// union bpf_iter_link_info.map\ntype bpfIterLinkInfoMap struct {\n\tmap_fd uint32\n}\n"
  },
  {
    "path": "link/iter_test.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"io\"\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc TestIter(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.9\", \"bpf_map iter\")\n\n\tprog := mustLoadProgram(t, ebpf.Tracing, ebpf.AttachTraceIter, \"bpf_map\")\n\n\tit, err := AttachIter(IterOptions{\n\t\tProgram: prog,\n\t})\n\tif err != nil {\n\t\tt.Fatal(\"Can't create iter:\", err)\n\t}\n\n\tfile, err := it.Open()\n\tif err != nil {\n\t\tt.Fatal(\"Can't open iter instance:\", err)\n\t}\n\tdefer file.Close()\n\n\tcontents, err := io.ReadAll(file)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif len(contents) != 0 {\n\t\tt.Error(\"Non-empty output from no-op iterator:\", string(contents))\n\t}\n\n\ttestLink(t, it, prog)\n}\n\nfunc TestIterMapElements(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.9\", \"bpf_map_elem iter\")\n\n\tprog := mustLoadProgram(t, ebpf.Tracing, ebpf.AttachTraceIter, \"bpf_map_elem\")\n\n\tarr, err := ebpf.NewMap(&ebpf.MapSpec{\n\t\tType:       ebpf.Array,\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: 3,\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer arr.Close()\n\n\tit, err := AttachIter(IterOptions{\n\t\tProgram: prog,\n\t\tMap:     arr,\n\t})\n\tif err != nil {\n\t\tt.Fatal(\"Can't create iter:\", err)\n\t}\n\tdefer it.Close()\n\n\tfile, err := it.Open()\n\tif err != nil {\n\t\tt.Fatal(\"Can't open iter instance:\", err)\n\t}\n\tdefer file.Close()\n\n\tcontents, err := io.ReadAll(file)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif len(contents) != 0 {\n\t\tt.Error(\"Non-empty output from no-op iterator:\", string(contents))\n\t}\n}\n\nfunc TestUDPIter(t *testing.T) {\n\t// Introduced by 5788b3a07fc5 (\"net: bpf: Implement bpf iterator for udp\")\n\ttestutils.SkipOnOldKernel(t, \"5.9\", \"udp iter\")\n\n\tprog := mustLoadProgram(t, ebpf.Tracing, ebpf.AttachTraceIter, \"udp\")\n\n\tit, err := AttachIter(IterOptions{\n\t\tProgram: prog,\n\t})\n\tif err != nil {\n\t\tt.Fatal(\"Can't create iter:\", err)\n\t}\n\n\tfile, err := it.Open()\n\tif err != nil {\n\t\tt.Fatal(\"Can't open iter instance:\", err)\n\t}\n\tdefer file.Close()\n\n\tcontents, err := io.ReadAll(file)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif len(contents) != 0 {\n\t\tt.Error(\"Non-empty output from no-op iterator:\", string(contents))\n\t}\n\n\ttestLink(t, it, prog)\n}\n"
  },
  {
    "path": "link/kprobe.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"runtime\"\n\t\"strings\"\n\t\"unsafe\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/linux\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/tracefs\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\n// KprobeOptions defines additional parameters that will be used\n// when loading Kprobes.\ntype KprobeOptions struct {\n\t// Arbitrary value that can be fetched from an eBPF program\n\t// via `bpf_get_attach_cookie()`.\n\t//\n\t// Needs kernel 5.15+.\n\tCookie uint64\n\t// Offset of the kprobe relative to the traced symbol.\n\t// Can be used to insert kprobes at arbitrary offsets in kernel functions,\n\t// e.g. in places where functions have been inlined.\n\tOffset uint64\n\t// Increase the maximum number of concurrent invocations of a kretprobe.\n\t// Required when tracing some long running functions in the kernel.\n\t//\n\t// Warning: this setting forces the use of an outdated kernel API and is\n\t// not portable across kernel versions. On supported kernels, consider using\n\t// fexit programs instead, as they don't have this MaxActive limitation.\n\tRetprobeMaxActive int\n\t// Prefix used for the event name if the kprobe must be attached using tracefs.\n\t// The group name will be formatted as `<prefix>_<randomstr>`.\n\t// The default empty string is equivalent to \"ebpf\" as the prefix.\n\tTraceFSPrefix string\n}\n\nfunc (ko *KprobeOptions) cookie() uint64 {\n\tif ko == nil {\n\t\treturn 0\n\t}\n\treturn ko.Cookie\n}\n\n// Kprobe attaches the given eBPF program to a perf event that fires when the\n// given kernel symbol starts executing. See /proc/kallsyms for available\n// symbols. For example, printk():\n//\n//\tkp, err := Kprobe(\"printk\", prog, nil)\n//\n// Losing the reference to the resulting Link (kp) will close the Kprobe\n// and prevent further execution of prog. The Link must be Closed during\n// program shutdown to avoid leaking system resources.\n//\n// If attaching to symbol fails, automatically retries with the running\n// platform's syscall prefix (e.g. __x64_) to support attaching to syscalls\n// in a portable fashion.\n//\n// On kernels 6.11 and later, setting a kprobe on a nonexistent symbol using\n// tracefs incorrectly returns [unix.EINVAL] instead of [os.ErrNotExist].\n//\n// The returned Link may implement [PerfEvent].\nfunc Kprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions) (Link, error) {\n\tk, err := kprobe(symbol, prog, opts, false)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlnk, err := attachPerfEvent(k, prog, opts.cookie())\n\tif err != nil {\n\t\tk.Close()\n\t\treturn nil, err\n\t}\n\n\treturn lnk, nil\n}\n\n// Kretprobe attaches the given eBPF program to a perf event that fires right\n// before the given kernel symbol exits, with the function stack left intact.\n// See /proc/kallsyms for available symbols. For example, printk():\n//\n//\tkp, err := Kretprobe(\"printk\", prog, nil)\n//\n// Losing the reference to the resulting Link (kp) will close the Kretprobe\n// and prevent further execution of prog. The Link must be Closed during\n// program shutdown to avoid leaking system resources.\n//\n// If attaching to symbol fails, automatically retries with the running\n// platform's syscall prefix (e.g. __x64_) to support attaching to syscalls\n// in a portable fashion.\n//\n// On kernels 5.10 and earlier, setting a kretprobe on a nonexistent symbol\n// incorrectly returns [unix.EINVAL] instead of [os.ErrNotExist].\n//\n// The returned Link may implement [PerfEvent].\nfunc Kretprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions) (Link, error) {\n\tk, err := kprobe(symbol, prog, opts, true)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlnk, err := attachPerfEvent(k, prog, opts.cookie())\n\tif err != nil {\n\t\tk.Close()\n\t\treturn nil, err\n\t}\n\n\treturn lnk, nil\n}\n\n// isValidKprobeSymbol implements the equivalent of a regex match\n// against \"^[a-zA-Z_][0-9a-zA-Z_.]*$\".\nfunc isValidKprobeSymbol(s string) bool {\n\tif len(s) < 1 {\n\t\treturn false\n\t}\n\n\tfor i, c := range []byte(s) {\n\t\tswitch {\n\t\tcase c >= 'a' && c <= 'z':\n\t\tcase c >= 'A' && c <= 'Z':\n\t\tcase c == '_':\n\t\tcase i > 0 && c >= '0' && c <= '9':\n\n\t\t// Allow `.` in symbol name. GCC-compiled kernel may change symbol name\n\t\t// to have a `.isra.$n` suffix, like `udp_send_skb.isra.52`.\n\t\t// See: https://gcc.gnu.org/gcc-10/changes.html\n\t\tcase i > 0 && c == '.':\n\n\t\tdefault:\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n\n// kprobe opens a perf event on the given symbol and attaches prog to it.\n// If ret is true, create a kretprobe.\nfunc kprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions, ret bool) (*perfEvent, error) {\n\tif symbol == \"\" {\n\t\treturn nil, fmt.Errorf(\"symbol name cannot be empty: %w\", errInvalidInput)\n\t}\n\tif prog == nil {\n\t\treturn nil, fmt.Errorf(\"prog cannot be nil: %w\", errInvalidInput)\n\t}\n\tif !isValidKprobeSymbol(symbol) {\n\t\treturn nil, fmt.Errorf(\"symbol '%s' must be a valid symbol in /proc/kallsyms: %w\", symbol, errInvalidInput)\n\t}\n\tif prog.Type() != ebpf.Kprobe {\n\t\treturn nil, fmt.Errorf(\"eBPF program type %s is not a Kprobe: %w\", prog.Type(), errInvalidInput)\n\t}\n\n\targs := tracefs.ProbeArgs{\n\t\tType:   tracefs.Kprobe,\n\t\tPid:    perfAllThreads,\n\t\tSymbol: symbol,\n\t\tRet:    ret,\n\t}\n\n\tif opts != nil {\n\t\targs.RetprobeMaxActive = opts.RetprobeMaxActive\n\t\targs.Cookie = opts.Cookie\n\t\targs.Offset = opts.Offset\n\t\targs.Group = opts.TraceFSPrefix\n\t}\n\n\t// Use kprobe PMU if the kernel has it available.\n\ttp, err := pmuProbe(args)\n\tif errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL) {\n\t\tif prefix := linux.PlatformPrefix(); prefix != \"\" {\n\t\t\targs.Symbol = prefix + symbol\n\t\t\ttp, err = pmuProbe(args)\n\t\t}\n\t}\n\tif err == nil {\n\t\treturn tp, nil\n\t}\n\tif !errors.Is(err, ErrNotSupported) {\n\t\treturn nil, fmt.Errorf(\"creating perf_kprobe PMU (arch-specific fallback for %q): %w\", symbol, err)\n\t}\n\n\t// Use tracefs if kprobe PMU is missing.\n\targs.Symbol = symbol\n\ttp, err = tracefsProbe(args)\n\tif errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL) {\n\t\tif prefix := linux.PlatformPrefix(); prefix != \"\" {\n\t\t\targs.Symbol = prefix + symbol\n\t\t\ttp, err = tracefsProbe(args)\n\t\t}\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"creating tracefs event (arch-specific fallback for %q): %w\", symbol, err)\n\t}\n\n\treturn tp, nil\n}\n\n// pmuProbe opens a perf event based on a Performance Monitoring Unit.\n//\n// Requires at least a 4.17 kernel.\n// e12f03d7031a \"perf/core: Implement the 'perf_kprobe' PMU\"\n// 33ea4b24277b \"perf/core: Implement the 'perf_uprobe' PMU\"\n//\n// Returns ErrNotSupported if the kernel doesn't support perf_[k,u]probe PMU\nfunc pmuProbe(args tracefs.ProbeArgs) (*perfEvent, error) {\n\t// Getting the PMU type will fail if the kernel doesn't support\n\t// the perf_[k,u]probe PMU.\n\teventType, err := internal.ReadUint64FromFileOnce(\"%d\\n\", \"/sys/bus/event_source/devices\", args.Type.String(), \"type\")\n\tif errors.Is(err, os.ErrNotExist) {\n\t\treturn nil, fmt.Errorf(\"%s: %w\", args.Type, ErrNotSupported)\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Use tracefs if we want to set kretprobe's retprobeMaxActive.\n\tif args.RetprobeMaxActive != 0 {\n\t\treturn nil, fmt.Errorf(\"pmu probe: non-zero retprobeMaxActive: %w\", ErrNotSupported)\n\t}\n\n\tvar config uint64\n\tif args.Ret {\n\t\tbit, err := internal.ReadUint64FromFileOnce(\"config:%d\\n\", \"/sys/bus/event_source/devices\", args.Type.String(), \"/format/retprobe\")\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tconfig |= 1 << bit\n\t}\n\n\tvar (\n\t\tattr  unix.PerfEventAttr\n\t\tsp    unsafe.Pointer\n\t\ttoken string\n\t)\n\tswitch args.Type {\n\tcase tracefs.Kprobe:\n\t\t// Create a pointer to a NUL-terminated string for the kernel.\n\t\tsp, err = unsafeStringPtr(args.Symbol)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\ttoken = tracefs.KprobeToken(args)\n\n\t\tattr = unix.PerfEventAttr{\n\t\t\t// The minimum size required for PMU kprobes is PERF_ATTR_SIZE_VER1,\n\t\t\t// since it added the config2 (Ext2) field. Use Ext2 as probe_offset.\n\t\t\tSize:   unix.PERF_ATTR_SIZE_VER1,\n\t\t\tType:   uint32(eventType),   // PMU event type read from sysfs\n\t\t\tExt1:   uint64(uintptr(sp)), // Kernel symbol to trace\n\t\t\tExt2:   args.Offset,         // Kernel symbol offset\n\t\t\tConfig: config,              // Retprobe flag\n\t\t}\n\tcase tracefs.Uprobe:\n\t\tsp, err = unsafeStringPtr(args.Path)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif args.RefCtrOffset != 0 {\n\t\t\tconfig |= args.RefCtrOffset << uprobeRefCtrOffsetShift\n\t\t}\n\n\t\ttoken = tracefs.UprobeToken(args)\n\n\t\tattr = unix.PerfEventAttr{\n\t\t\t// The minimum size required for PMU uprobes is PERF_ATTR_SIZE_VER1,\n\t\t\t// since it added the config2 (Ext2) field. The Size field controls the\n\t\t\t// size of the internal buffer the kernel allocates for reading the\n\t\t\t// perf_event_attr argument from userspace.\n\t\t\tSize:   unix.PERF_ATTR_SIZE_VER1,\n\t\t\tType:   uint32(eventType),   // PMU event type read from sysfs\n\t\t\tExt1:   uint64(uintptr(sp)), // Uprobe path\n\t\t\tExt2:   args.Offset,         // Uprobe offset\n\t\t\tConfig: config,              // RefCtrOffset, Retprobe flag\n\t\t}\n\t}\n\n\tcpu := 0\n\tif args.Pid != perfAllThreads {\n\t\tcpu = -1\n\t}\n\trawFd, err := unix.PerfEventOpen(&attr, args.Pid, cpu, -1, unix.PERF_FLAG_FD_CLOEXEC)\n\n\t// On some old kernels, kprobe PMU doesn't allow `.` in symbol names and\n\t// return -EINVAL. Return ErrNotSupported to allow falling back to tracefs.\n\t// https://github.com/torvalds/linux/blob/94710cac0ef4/kernel/trace/trace_kprobe.c#L340-L343\n\tif errors.Is(err, unix.EINVAL) && strings.Contains(args.Symbol, \".\") {\n\t\treturn nil, fmt.Errorf(\"token %s: older kernels don't accept dots: %w\", token, ErrNotSupported)\n\t}\n\t// Since commit 97c753e62e6c, ENOENT is correctly returned instead of EINVAL\n\t// when trying to create a retprobe for a missing symbol.\n\tif errors.Is(err, os.ErrNotExist) {\n\t\treturn nil, fmt.Errorf(\"token %s: not found: %w\", token, err)\n\t}\n\t// Since commit ab105a4fb894, EILSEQ is returned when a kprobe sym+offset is resolved\n\t// to an invalid insn boundary. The exact conditions that trigger this error are\n\t// arch specific however.\n\tif errors.Is(err, unix.EILSEQ) {\n\t\treturn nil, fmt.Errorf(\"token %s: bad insn boundary: %w\", token, os.ErrNotExist)\n\t}\n\t// Since at least commit cb9a19fe4aa51, ENOTSUPP is returned\n\t// when attempting to set a uprobe on a trap instruction.\n\tif errors.Is(err, sys.ENOTSUPP) {\n\t\treturn nil, fmt.Errorf(\"token %s: failed setting uprobe on offset %#x (possible trap insn): %w\", token, args.Offset, err)\n\t}\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"token %s: opening perf event: %w\", token, err)\n\t}\n\n\t// Ensure the string pointer is not collected before PerfEventOpen returns.\n\truntime.KeepAlive(sp)\n\n\tfd, err := sys.NewFD(rawFd)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Kernel has perf_[k,u]probe PMU available, initialize perf event.\n\treturn newPerfEvent(fd, nil), nil\n}\n\n// tracefsProbe creates a trace event by writing an entry to <tracefs>/[k,u]probe_events.\n// A new trace event group name is generated on every call to support creating\n// multiple trace events for the same kernel or userspace symbol.\n// Path and offset are only set in the case of uprobe(s) and are used to set\n// the executable/library path on the filesystem and the offset where the probe is inserted.\n// A perf event is then opened on the newly-created trace event and returned to the caller.\nfunc tracefsProbe(args tracefs.ProbeArgs) (*perfEvent, error) {\n\tgroupPrefix := \"ebpf\"\n\tif args.Group != \"\" {\n\t\tgroupPrefix = args.Group\n\t}\n\n\t// Generate a random string for each trace event we attempt to create.\n\t// This value is used as the 'group' token in tracefs to allow creating\n\t// multiple kprobe trace events with the same name.\n\tgroup, err := tracefs.RandomGroup(groupPrefix)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"randomizing group name: %w\", err)\n\t}\n\targs.Group = group\n\n\t// Create the [k,u]probe trace event using tracefs.\n\tevt, err := tracefs.NewEvent(args)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"creating probe entry on tracefs: %w\", err)\n\t}\n\n\t// Kprobes are ephemeral tracepoints and share the same perf event type.\n\tfd, err := openTracepointPerfEvent(evt.ID(), args.Pid)\n\tif err != nil {\n\t\t// Make sure we clean up the created tracefs event when we return error.\n\t\t// If a livepatch handler is already active on the symbol, the write to\n\t\t// tracefs will succeed, a trace event will show up, but creating the\n\t\t// perf event will fail with EBUSY.\n\t\t_ = evt.Close()\n\t\treturn nil, err\n\t}\n\n\treturn newPerfEvent(fd, evt), nil\n}\n"
  },
  {
    "path": "link/kprobe_multi.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/features\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\n// KprobeMultiOptions defines additional parameters that will be used\n// when opening a KprobeMulti Link.\ntype KprobeMultiOptions struct {\n\t// Symbols takes a list of kernel symbol names to attach an ebpf program to.\n\t//\n\t// Mutually exclusive with Addresses.\n\tSymbols []string\n\n\t// Addresses takes a list of kernel symbol addresses in case they can not\n\t// be referred to by name.\n\t//\n\t// Note that only start addresses can be specified, since the fprobe API\n\t// limits the attach point to the function entry or return.\n\t//\n\t// Mutually exclusive with Symbols.\n\tAddresses []uintptr\n\n\t// Cookies specifies arbitrary values that can be fetched from an eBPF\n\t// program via `bpf_get_attach_cookie()`.\n\t//\n\t// If set, its length should be equal to the length of Symbols or Addresses.\n\t// Each Cookie is assigned to the Symbol or Address specified at the\n\t// corresponding slice index.\n\tCookies []uint64\n\n\t// Session must be true when attaching Programs with the\n\t// [ebpf.AttachTraceKprobeSession] attach type.\n\t//\n\t// This makes a Kprobe execute on both function entry and return. The entry\n\t// program can share a cookie value with the return program and can decide\n\t// whether the return program gets executed.\n\tSession bool\n}\n\n// KprobeMulti attaches the given eBPF program to the entry point of a given set\n// of kernel symbols.\n//\n// The difference with Kprobe() is that multi-kprobe accomplishes this in a\n// single system call, making it significantly faster than attaching many\n// probes one at a time.\n//\n// Requires at least Linux 5.18.\nfunc KprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions) (Link, error) {\n\treturn kprobeMulti(prog, opts, 0)\n}\n\n// KretprobeMulti attaches the given eBPF program to the return point of a given\n// set of kernel symbols.\n//\n// The difference with Kretprobe() is that multi-kprobe accomplishes this in a\n// single system call, making it significantly faster than attaching many\n// probes one at a time.\n//\n// Requires at least Linux 5.18.\nfunc KretprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions) (Link, error) {\n\treturn kprobeMulti(prog, opts, sys.BPF_F_KPROBE_MULTI_RETURN)\n}\n\nfunc kprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions, flags uint32) (Link, error) {\n\tif prog == nil {\n\t\treturn nil, errors.New(\"cannot attach a nil program\")\n\t}\n\n\tsyms := uint32(len(opts.Symbols))\n\taddrs := uint32(len(opts.Addresses))\n\tcookies := uint32(len(opts.Cookies))\n\n\tif syms == 0 && addrs == 0 {\n\t\treturn nil, fmt.Errorf(\"one of Symbols or Addresses is required: %w\", errInvalidInput)\n\t}\n\tif syms != 0 && addrs != 0 {\n\t\treturn nil, fmt.Errorf(\"fields Symbols and Addresses are mutually exclusive: %w\", errInvalidInput)\n\t}\n\tif cookies > 0 && cookies != syms && cookies != addrs {\n\t\treturn nil, fmt.Errorf(\"field Cookies must be exactly Symbols or Addresses in length: %w\", errInvalidInput)\n\t}\n\n\tattachType := sys.BPF_TRACE_KPROBE_MULTI\n\tif opts.Session {\n\t\tattachType = sys.BPF_TRACE_KPROBE_SESSION\n\t}\n\n\tattr := &sys.LinkCreateKprobeMultiAttr{\n\t\tProgFd:           uint32(prog.FD()),\n\t\tAttachType:       attachType,\n\t\tKprobeMultiFlags: flags,\n\t}\n\n\tswitch {\n\tcase syms != 0:\n\t\tattr.Count = syms\n\t\tattr.Syms = sys.NewStringSlicePointer(opts.Symbols)\n\n\tcase addrs != 0:\n\t\tattr.Count = addrs\n\t\tattr.Addrs = sys.SlicePointer(opts.Addresses)\n\t}\n\n\tif cookies != 0 {\n\t\tattr.Cookies = sys.SlicePointer(opts.Cookies)\n\t}\n\n\tfd, err := sys.LinkCreateKprobeMulti(attr)\n\tif err == nil {\n\t\treturn &kprobeMultiLink{RawLink{fd, \"\"}}, nil\n\t}\n\n\tif errors.Is(err, unix.ESRCH) {\n\t\treturn nil, fmt.Errorf(\"couldn't find one or more symbols: %w\", os.ErrNotExist)\n\t}\n\n\tif opts.Session {\n\t\tif haveFeatErr := features.HaveBPFLinkKprobeSession(); haveFeatErr != nil {\n\t\t\treturn nil, haveFeatErr\n\t\t}\n\t} else {\n\t\tif haveFeatErr := features.HaveBPFLinkKprobeMulti(); haveFeatErr != nil {\n\t\t\treturn nil, haveFeatErr\n\t\t}\n\t}\n\n\t// Check EINVAL after running feature probes, since it's also returned when\n\t// the kernel doesn't support the multi/session attach types.\n\tif errors.Is(err, unix.EINVAL) {\n\t\treturn nil, fmt.Errorf(\"%w (missing kernel symbol or prog's AttachType not %s?)\", err, ebpf.AttachType(attachType))\n\t}\n\n\treturn nil, err\n}\n\ntype kprobeMultiLink struct {\n\tRawLink\n}\n\nvar _ Link = (*kprobeMultiLink)(nil)\n\nfunc (kml *kprobeMultiLink) Update(_ *ebpf.Program) error {\n\treturn fmt.Errorf(\"update kprobe_multi: %w\", ErrNotSupported)\n}\n\nfunc (kml *kprobeMultiLink) Info() (*Info, error) {\n\tvar info sys.KprobeMultiLinkInfo\n\tif err := sys.ObjInfo(kml.fd, &info); err != nil {\n\t\treturn nil, fmt.Errorf(\"kprobe multi link info: %w\", err)\n\t}\n\tvar addrs = make([]uint64, info.Count)\n\tvar cookies = make([]uint64, info.Count)\n\tinfo = sys.KprobeMultiLinkInfo{\n\t\tAddrs:   sys.SlicePointer(addrs),\n\t\tCookies: sys.SlicePointer(cookies),\n\t\tCount:   uint32(len(addrs)),\n\t}\n\tif err := sys.ObjInfo(kml.fd, &info); err != nil {\n\t\treturn nil, fmt.Errorf(\"kprobe multi link info: %w\", err)\n\t}\n\tif info.Addrs.IsNil() {\n\t\taddrs = nil\n\t}\n\tif info.Cookies.IsNil() {\n\t\tcookies = nil\n\t}\n\textra := &KprobeMultiInfo{\n\t\tCount:   info.Count,\n\t\tFlags:   info.Flags,\n\t\tMissed:  info.Missed,\n\t\taddrs:   addrs,\n\t\tcookies: cookies,\n\t}\n\n\treturn &Info{\n\t\tinfo.Type,\n\t\tinfo.Id,\n\t\tebpf.ProgramID(info.ProgId),\n\t\textra,\n\t}, nil\n}\n"
  },
  {
    "path": "link/kprobe_multi_test.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"errors\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/features\"\n\t\"github.com/cilium/ebpf/internal/linux\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\nvar kprobeMultiSyms = []string{\"vprintk\", \"inet6_release\"}\n\nfunc TestKprobeMulti(t *testing.T) {\n\ttestutils.SkipIfNotSupported(t, features.HaveBPFLinkKprobeMulti())\n\n\tprog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceKprobeMulti, \"\")\n\n\tkm, err := KprobeMulti(prog, KprobeMultiOptions{Symbols: kprobeMultiSyms})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer km.Close()\n\n\ttestLink(t, km, prog)\n}\n\nfunc TestKprobeMultiInfo(t *testing.T) {\n\ttestutils.SkipIfNotSupported(t, features.HaveBPFLinkKprobeMulti())\n\ttestutils.SkipOnOldKernel(t, \"6.6\", \"bpf_link_info_kprobe_multi\")\n\n\tprog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceKprobeMulti, \"\")\n\n\tkm, err := KprobeMulti(prog, KprobeMultiOptions{Symbols: kprobeMultiSyms})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer km.Close()\n\n\tinfo, err := km.Info()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tkmInfo := info.KprobeMulti()\n\taddresses, ok := kmInfo.Addresses()\n\tqt.Assert(t, qt.IsTrue(ok))\n\t// kprobe_multi only returns addresses, no symbols, so we can't verify that the addresses are correct\n\tqt.Assert(t, qt.HasLen(addresses, len(kprobeMultiSyms)))\n}\n\nfunc TestKprobeMultiInput(t *testing.T) {\n\t// Program type that loads on all kernels. Not expected to link successfully.\n\tprog := mustLoadProgram(t, ebpf.SocketFilter, 0, \"\")\n\n\t// One of Symbols or Addresses must be given.\n\t_, err := KprobeMulti(prog, KprobeMultiOptions{})\n\tif !errors.Is(err, errInvalidInput) {\n\t\tt.Fatalf(\"expected errInvalidInput, got: %v\", err)\n\t}\n\n\t// Symbols and Addresses are mutually exclusive.\n\t_, err = KprobeMulti(prog, KprobeMultiOptions{\n\t\tSymbols:   []string{\"foo\"},\n\t\tAddresses: []uintptr{1},\n\t})\n\tif !errors.Is(err, errInvalidInput) {\n\t\tt.Fatalf(\"expected errInvalidInput, got: %v\", err)\n\t}\n\n\t// One Symbol, two cookies..\n\t_, err = KprobeMulti(prog, KprobeMultiOptions{\n\t\tSymbols: []string{\"one\"},\n\t\tCookies: []uint64{2, 3},\n\t})\n\tif !errors.Is(err, errInvalidInput) {\n\t\tt.Fatalf(\"expected errInvalidInput, got: %v\", err)\n\t}\n}\n\nfunc TestKprobeMultiErrors(t *testing.T) {\n\ttestutils.SkipIfNotSupported(t, features.HaveBPFLinkKprobeMulti())\n\n\tprog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceKprobeMulti, \"\")\n\n\t// Nonexistent kernel symbol.\n\t_, err := KprobeMulti(prog, KprobeMultiOptions{Symbols: []string{\"bogus\"}})\n\tif !errors.Is(err, os.ErrNotExist) && !errors.Is(err, unix.EINVAL) {\n\t\tt.Fatalf(\"expected ErrNotExist or EINVAL, got: %s\", err)\n\t}\n\n\t// Only have a negative test for addresses as it would be hard to maintain a\n\t// proper one.\n\t_, err = KprobeMulti(prog, KprobeMultiOptions{\n\t\tAddresses: []uintptr{^uintptr(0)},\n\t})\n\tif !errors.Is(err, os.ErrNotExist) && !errors.Is(err, unix.EINVAL) {\n\t\tt.Fatalf(\"expected ErrNotExist or EINVAL, got: %s\", err)\n\t}\n}\n\nfunc TestKprobeMultiCookie(t *testing.T) {\n\ttestutils.SkipIfNotSupported(t, features.HaveBPFLinkKprobeMulti())\n\n\tprog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceKprobeMulti, \"\")\n\n\tkm, err := KprobeMulti(prog, KprobeMultiOptions{\n\t\tSymbols: kprobeMultiSyms,\n\t\tCookies: []uint64{0, 1},\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\t_ = km.Close()\n}\n\nfunc TestKprobeMultiProgramCall(t *testing.T) {\n\ttestutils.SkipIfNotSupported(t, features.HaveBPFLinkKprobeMulti())\n\n\tm, p := newUpdaterMapProg(t, ebpf.Kprobe, ebpf.AttachTraceKprobeMulti)\n\n\t// Use actual syscall names with platform prefix.\n\t// For simplicity, just assert the increment happens with any symbol in the array.\n\tprefix := linux.PlatformPrefix()\n\topts := KprobeMultiOptions{\n\t\tSymbols: []string{prefix + \"sys_getpid\", prefix + \"sys_gettid\"},\n\t}\n\tkm, err := KprobeMulti(p, opts)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// Trigger ebpf program call.\n\tunix.Getpid()\n\tunix.Gettid()\n\n\t// Assert that the value got incremented to at least 2, while allowing\n\t// for bigger values, because we could race with other getpid/gettid\n\t// callers.\n\tassertMapValueGE(t, m, 0, 2)\n\n\t// Close the link.\n\tif err := km.Close(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// Reset map value to 0 at index 0.\n\tif err := m.Update(uint32(0), uint32(0), ebpf.UpdateExist); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// Retrigger the ebpf program call.\n\tunix.Getpid()\n\tunix.Gettid()\n\n\t// Assert that this time the value has not been updated.\n\tassertMapValue(t, m, 0, 0)\n}\n\nfunc TestKprobeSession(t *testing.T) {\n\ttestutils.SkipIfNotSupported(t, features.HaveBPFLinkKprobeSession())\n\n\tprog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceKprobeSession, \"\")\n\n\tkm, err := KprobeMulti(prog, KprobeMultiOptions{Symbols: kprobeMultiSyms, Session: true})\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer km.Close()\n\n\ttestLink(t, km, prog)\n}\n"
  },
  {
    "path": "link/kprobe_test.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"errors\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n\t\"github.com/cilium/ebpf/internal/tracefs\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\n// Global symbol, present on all tested kernels.\nvar ksym = \"vprintk\"\n\n// Collection of various symbols present in all tested kernels.\n// Compiler optimizations result in different names for these symbols.\nvar symTests = []string{\n\t\"echo_char.isra.0\",          // function optimized by -fipa-sra\n\t\"proc_get_long.constprop.0\", // optimized function with constant operands\n\t\"unregister_kprobes.part.0\", // function body that was split and partially inlined\n}\n\nfunc TestKprobe(t *testing.T) {\n\tprog := mustLoadProgram(t, ebpf.Kprobe, 0, \"\")\n\n\tfor _, tt := range symTests {\n\t\tt.Run(tt, func(t *testing.T) {\n\t\t\tk, err := Kprobe(tt, prog, nil)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tdefer k.Close()\n\t\t})\n\t}\n\n\tk, err := Kprobe(\"bogus\", prog, nil)\n\tqt.Assert(t, qt.ErrorIs(err, os.ErrNotExist), qt.Commentf(\"got error: %s\", err))\n\tif k != nil {\n\t\tk.Close()\n\t}\n\n\tk, err = Kprobe(ksym, prog, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer k.Close()\n\n\ttestLink(t, k, prog)\n}\n\nfunc TestKprobeInfo(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"6.6\", \"bpf_link_info_perf_event\")\n\n\tprog := mustLoadProgram(t, ebpf.Kprobe, 0, \"\")\n\n\tk, err := Kprobe(ksym, prog, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer k.Close()\n\n\tinfo, err := k.Info()\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.Equals(info.PerfEvent().Kprobe().Function, ksym))\n\tqt.Assert(t, qt.Equals(info.PerfEvent().Kprobe().Offset, 0))\n}\n\nfunc TestKprobeOffset(t *testing.T) {\n\tprog := mustLoadProgram(t, ebpf.Kprobe, 0, \"\")\n\n\t// The layout of a function is compiler and arch dependent, so we try to\n\t// find a valid attach target in the first few bytes of the function.\n\tfor i := uint64(1); i < 16; i++ {\n\t\tk, err := Kprobe(\"inet6_release\", prog, &KprobeOptions{Offset: i})\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tk.Close()\n\t\treturn\n\t}\n\n\tt.Fatal(\"Can't attach with non-zero offset\")\n}\n\nfunc TestKretprobeMaxActive(t *testing.T) {\n\tprog := mustLoadProgram(t, ebpf.Kprobe, 0, \"\")\n\tdefer prog.Close()\n\n\t_, err := Kprobe(\"do_sys_open\", prog, &KprobeOptions{RetprobeMaxActive: 4096})\n\tif !errors.Is(err, tracefs.ErrInvalidMaxActive) {\n\t\tt.Fatal(\"Expected ErrInvalidMaxActive, got\", err)\n\t}\n\n\tk, err := Kretprobe(\"__put_task_struct\", prog, &KprobeOptions{RetprobeMaxActive: 4096})\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(\"Kretprobe with maxactive returned an error:\", err)\n\t}\n\tif err := k.Close(); err != nil {\n\t\tt.Fatal(\"Closing kretprobe:\", err)\n\t}\n}\n\nfunc TestKretprobe(t *testing.T) {\n\tprog := mustLoadProgram(t, ebpf.Kprobe, 0, \"\")\n\n\tfor _, tt := range symTests {\n\t\tt.Run(tt, func(t *testing.T) {\n\t\t\tk, err := Kretprobe(tt, prog, nil)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tdefer k.Close()\n\t\t})\n\t}\n\n\tk, err := Kretprobe(\"bogus\", prog, nil)\n\tif !errors.Is(err, os.ErrNotExist) && !errors.Is(err, unix.EINVAL) {\n\t\tt.Fatal(err)\n\t}\n\tif k != nil {\n\t\tk.Close()\n\t}\n\n\tk, err = Kretprobe(ksym, prog, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer k.Close()\n\n\ttestLink(t, k, prog)\n}\n\nfunc TestKprobeErrors(t *testing.T) {\n\t// Invalid Kprobe incantations. Kretprobe uses the same code paths\n\t// with a different ret flag.\n\t_, err := Kprobe(\"\", nil, nil) // empty symbol\n\tqt.Assert(t, qt.ErrorIs(err, errInvalidInput))\n\n\t_, err = Kprobe(\"_\", nil, nil) // empty prog\n\tqt.Assert(t, qt.ErrorIs(err, errInvalidInput))\n\n\t_, err = Kprobe(\".\", &ebpf.Program{}, nil) // illegal chars in symbol\n\tqt.Assert(t, qt.ErrorIs(err, errInvalidInput))\n\n\t_, err = Kprobe(\"foo\", &ebpf.Program{}, nil) // wrong prog type\n\tqt.Assert(t, qt.ErrorIs(err, errInvalidInput))\n}\n\n// Test k(ret)probe creation using perf_kprobe PMU.\nfunc TestKprobeCreatePMU(t *testing.T) {\n\t// Requires at least 4.17 (e12f03d7031a \"perf/core: Implement the 'perf_kprobe' PMU\")\n\ttestutils.SkipOnOldKernel(t, \"4.17\", \"perf_kprobe PMU\")\n\n\t// kprobe happy path. printk is always present.\n\tpk, err := pmuProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym})\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer pk.Close()\n\n\t// kretprobe happy path.\n\tpr, err := pmuProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym, Ret: true})\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer pr.Close()\n\n\t// Expect os.ErrNotExist when specifying a non-existent kernel symbol\n\t// on kernels 4.17 and up.\n\t_, err = pmuProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: \"bogus\"})\n\tqt.Assert(t, qt.ErrorIs(err, os.ErrNotExist), qt.Commentf(\"got error: %s\", err))\n\n\t// A kernel bug was fixed in 97c753e62e6c where EINVAL was returned instead\n\t// of ENOENT, but only for kretprobes.\n\t_, err = pmuProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: \"bogus\", Ret: true})\n\tqt.Assert(t, qt.ErrorIs(err, os.ErrNotExist), qt.Commentf(\"got error: %s\", err))\n}\n\n// Test fallback behaviour on kernels without perf_kprobe PMU available.\nfunc TestKprobePMUUnavailable(t *testing.T) {\n\tpk, err := pmuProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym})\n\tif err == nil {\n\t\tpk.Close()\n\t\tt.Skipf(\"Kernel supports perf_kprobe PMU, not asserting error.\")\n\t}\n\n\t// Only allow a PMU creation with a valid kernel symbol to fail with ErrNotSupported.\n\tqt.Assert(t, qt.ErrorIs(err, ErrNotSupported), qt.Commentf(\"got error: %s\", err))\n}\n\nfunc BenchmarkKprobeCreatePMU(b *testing.B) {\n\tfor b.Loop() {\n\t\tpr, err := pmuProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym})\n\t\tif err != nil {\n\t\t\tb.Error(\"error creating perf_kprobe PMU:\", err)\n\t\t}\n\n\t\tif err := pr.Close(); err != nil {\n\t\t\tb.Error(\"error closing perf_kprobe PMU:\", err)\n\t\t}\n\t}\n}\n\n// Test tracefs k(ret)probe creation on all kernel versions.\nfunc TestKprobeTraceFS(t *testing.T) {\n\t// Open and close tracefs k(ret)probes, checking all errors.\n\tkp, err := tracefsProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym})\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.IsNil(kp.Close()))\n\n\tkp, err = tracefsProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym, Ret: true})\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.IsNil(kp.Close()))\n\n\t// Create two identical trace events, ensure their IDs differ.\n\tk1, err := tracefsProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym})\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer k1.Close()\n\tqt.Assert(t, qt.IsNotNil(k1.tracefsEvent))\n\n\tk2, err := tracefsProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym})\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer k2.Close()\n\tqt.Assert(t, qt.IsNotNil(k2.tracefsEvent))\n\n\t// Compare the kprobes' tracefs IDs.\n\tqt.Assert(t, qt.Not(qt.Equals(k1.tracefsEvent.ID(), k2.tracefsEvent.ID())))\n\n\t// Expect an error when supplying an invalid custom group name\n\t_, err = tracefsProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym, Group: \"/\"})\n\tqt.Assert(t, qt.Not(qt.IsNil(err)))\n\n\tcg := \"customgroup\"\n\tk3, err := tracefsProbe(tracefs.ProbeArgs{Type: tracefs.Kprobe, Symbol: ksym, Group: cg})\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer k3.Close()\n\tqt.Assert(t, qt.Matches(k3.tracefsEvent.Group(), `customgroup_[a-f0-9]{16}`))\n\n\t// Prepare probe args.\n\targs := tracefs.ProbeArgs{Type: tracefs.Kprobe, Group: \"testgroup\", Symbol: \"symbol\"}\n\n\t// Write a k(ret)probe event for a non-existing symbol.\n\t_, err = tracefs.NewEvent(args)\n\t// A kernel bug was introduced in 9d8616034f16 that causes EINVAL to be returned\n\t// instead of ENOENT when trying to attach kprobes to non-existing symbols.\n\tqt.Assert(t, qt.IsTrue(errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL)), qt.Commentf(\"got error: %s\", err))\n\n\t// A kernel bug was fixed in 97c753e62e6c where EINVAL was returned instead\n\t// of ENOENT, but only for kretprobes.\n\targs.Ret = true\n\t_, err = tracefs.NewEvent(args)\n\tqt.Assert(t, qt.IsTrue(errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL)), qt.Commentf(\"got error: %s\", err))\n}\n\nfunc BenchmarkKprobeCreateTraceFS(b *testing.B) {\n\tfor b.Loop() {\n\t\t// Include <tracefs>/kprobe_events operations in the benchmark loop\n\t\t// because we create one per perf event.\n\t\tpr, err := tracefsProbe(tracefs.ProbeArgs{Symbol: ksym})\n\t\tif err != nil {\n\t\t\tb.Error(\"error creating tracefs perf event:\", err)\n\t\t}\n\n\t\tif err := pr.Close(); err != nil {\n\t\t\tb.Error(\"error closing tracefs perf event:\", err)\n\t\t}\n\t}\n}\n\nfunc TestKprobeProgramCall(t *testing.T) {\n\tm, p := newUpdaterMapProg(t, ebpf.Kprobe, 0)\n\n\t// Open Kprobe on `sys_getpid` and attach it\n\t// to the ebpf program created above.\n\tk, err := Kprobe(\"sys_getpid\", p, nil)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// Trigger ebpf program call.\n\tunix.Getpid()\n\n\t// Assert that the value got incremented to at least 1, while allowing\n\t// for bigger values, because we could race with other getpid callers.\n\tassertMapValueGE(t, m, 0, 1)\n\n\t// Detach the Kprobe.\n\tif err := k.Close(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// Reset map value to 0 at index 0.\n\tif err := m.Update(uint32(0), uint32(0), ebpf.UpdateExist); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// Retrigger the ebpf program call.\n\tunix.Getpid()\n\n\t// Assert that this time the value has not been updated.\n\tassertMapValue(t, m, 0, 0)\n}\n\nfunc newUpdaterMapProg(t *testing.T, typ ebpf.ProgramType, attach ebpf.AttachType) (*ebpf.Map, *ebpf.Program) {\n\t// Create ebpf map. Will contain only one key with initial value 0.\n\tm, err := ebpf.NewMap(&ebpf.MapSpec{\n\t\tType:       ebpf.Array,\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: 1,\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// Create ebpf program. When called, will increase the value of key 0 by 1\n\t// in the map created above.\n\tp, err := ebpf.NewProgram(&ebpf.ProgramSpec{\n\t\tType: typ,\n\t\tInstructions: asm.Instructions{\n\t\t\t// R1 map\n\t\t\tasm.LoadMapPtr(asm.R1, m.FD()),\n\n\t\t\t// R2 key\n\t\t\tasm.Mov.Reg(asm.R2, asm.R10),\n\t\t\tasm.Add.Imm(asm.R2, -4),\n\t\t\tasm.StoreImm(asm.R2, 0, 0, asm.Word),\n\n\t\t\t// Lookup map[0]\n\t\t\tasm.FnMapLookupElem.Call(),\n\t\t\tasm.JEq.Imm(asm.R0, 0, \"ret\"),\n\n\t\t\t// u32 val = R0++\n\t\t\tasm.LoadMem(asm.R1, asm.R0, 0, asm.Word),\n\t\t\tasm.Add.Imm(asm.R1, 1),\n\t\t\tasm.StoreMem(asm.RFP, -8, asm.R1, asm.Word),\n\n\t\t\t// u32 key = 0\n\t\t\tasm.Mov.Imm(asm.R1, 0),\n\t\t\tasm.StoreMem(asm.RFP, -4, asm.R1, asm.Word),\n\n\t\t\t// bpf_map_update_elem(...)\n\t\t\tasm.Mov.Reg(asm.R2, asm.RFP),\n\t\t\tasm.Add.Imm(asm.R2, -4),\n\t\t\tasm.Mov.Reg(asm.R3, asm.RFP),\n\t\t\tasm.Add.Imm(asm.R3, -8),\n\t\t\tasm.LoadMapPtr(asm.R1, m.FD()),\n\t\t\tasm.Mov.Imm(asm.R4, 0),\n\t\t\tasm.FnMapUpdateElem.Call(),\n\n\t\t\t// exit 0\n\t\t\tasm.Mov.Imm(asm.R0, 0),\n\t\t\tasm.Return().WithSymbol(\"ret\"),\n\t\t},\n\t\tAttachType: attach,\n\t\tLicense:    \"Dual MIT/GPL\",\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// Close the program and map on test teardown.\n\tt.Cleanup(func() {\n\t\tm.Close()\n\t\tp.Close()\n\t})\n\n\treturn m, p\n}\n\nfunc assertMapValue(t *testing.T, m *ebpf.Map, k, v uint32) {\n\tvar val uint32\n\tif err := m.Lookup(k, &val); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif val != v {\n\t\tt.Fatalf(\"unexpected value: want '%d', got '%d'\", v, val)\n\t}\n}\n\nfunc assertMapValueGE(t *testing.T, m *ebpf.Map, k, v uint32) {\n\tvar val uint32\n\tif err := m.Lookup(k, &val); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif val < v {\n\t\tt.Fatalf(\"unexpected value: want >= '%d', got '%d'\", v, val)\n\t}\n}\n\nfunc TestKprobeCookie(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.15\", \"bpf_perf_link\")\n\n\tprog := mustLoadProgram(t, ebpf.Kprobe, 0, \"\")\n\tk, err := Kprobe(ksym, prog, &KprobeOptions{Cookie: 1000})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tk.Close()\n}\n"
  },
  {
    "path": "link/link.go",
    "content": "package link\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\n// Type is the kind of link.\ntype Type = sys.LinkType\n\nvar ErrNotSupported = internal.ErrNotSupported\n\n// Link represents a Program attached to a BPF hook.\ntype Link interface {\n\t// Replace the current program with a new program.\n\t//\n\t// Passing a nil program is an error. May return an error wrapping ErrNotSupported.\n\tUpdate(*ebpf.Program) error\n\n\t// Persist a link by pinning it into a bpffs.\n\t//\n\t// May return an error wrapping ErrNotSupported.\n\tPin(string) error\n\n\t// Undo a previous call to Pin.\n\t//\n\t// May return an error wrapping ErrNotSupported.\n\tUnpin() error\n\n\t// Close frees resources.\n\t//\n\t// The link will be broken unless it has been successfully pinned.\n\t// A link may continue past the lifetime of the process if Close is\n\t// not called.\n\tClose() error\n\n\t// Detach the link from its corresponding attachment point.\n\t//\n\t// May return an error wrapping ErrNotSupported.\n\tDetach() error\n\n\t// Info returns metadata on a link.\n\t//\n\t// May return an error wrapping ErrNotSupported.\n\tInfo() (*Info, error)\n\n\t// Prevent external users from implementing this interface.\n\tisLink()\n}\n\n// NewFromFD creates a link from a raw fd.\n//\n// You should not use fd after calling this function.\nfunc NewFromFD(fd int) (Link, error) {\n\tsysFD, err := sys.NewFD(fd)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn wrapRawLink(&RawLink{fd: sysFD})\n}\n\n// NewFromID returns the link associated with the given id.\n//\n// Returns ErrNotExist if there is no link with the given id.\nfunc NewFromID(id ID) (Link, error) {\n\tgetFdAttr := &sys.LinkGetFdByIdAttr{Id: id}\n\tfd, err := sys.LinkGetFdById(getFdAttr)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"get link fd from ID %d: %w\", id, err)\n\t}\n\n\treturn wrapRawLink(&RawLink{fd, \"\"})\n}\n\n// LoadPinnedLink loads a Link from a pin (file) on the BPF virtual filesystem.\n//\n// Requires at least Linux 5.7.\nfunc LoadPinnedLink(fileName string, opts *ebpf.LoadPinOptions) (Link, error) {\n\traw, err := loadPinnedRawLink(fileName, opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn wrapRawLink(raw)\n}\n\n// ID uniquely identifies a BPF link.\ntype ID = sys.LinkID\n\n// RawLinkOptions control the creation of a raw link.\ntype RawLinkOptions struct {\n\t// File descriptor to attach to. This differs for each attach type.\n\tTarget int\n\t// Program to attach.\n\tProgram *ebpf.Program\n\t// Attach must match the attach type of Program.\n\tAttach ebpf.AttachType\n\t// BTF is the BTF of the attachment target.\n\tBTF btf.TypeID\n\t// Flags control the attach behaviour.\n\tFlags uint32\n}\n\n// Info contains metadata on a link.\ntype Info struct {\n\tType    Type\n\tID      ID\n\tProgram ebpf.ProgramID\n\textra   interface{}\n}\n\n// RawLink is the low-level API to bpf_link.\n//\n// You should consider using the higher level interfaces in this\n// package instead.\ntype RawLink struct {\n\tfd         *sys.FD\n\tpinnedPath string\n}\n\nfunc loadPinnedRawLink(fileName string, opts *ebpf.LoadPinOptions) (*RawLink, error) {\n\tfd, typ, err := sys.ObjGetTyped(&sys.ObjGetAttr{\n\t\tPathname:  sys.NewStringPointer(fileName),\n\t\tFileFlags: opts.Marshal(),\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"load pinned link: %w\", err)\n\t}\n\n\tif typ != sys.BPF_TYPE_LINK {\n\t\t_ = fd.Close()\n\t\treturn nil, fmt.Errorf(\"%s is not a Link\", fileName)\n\t}\n\n\treturn &RawLink{fd, fileName}, nil\n}\n\nfunc (l *RawLink) isLink() {}\n\n// FD returns the raw file descriptor.\nfunc (l *RawLink) FD() int {\n\treturn l.fd.Int()\n}\n\n// Close breaks the link.\n//\n// Use Pin if you want to make the link persistent.\nfunc (l *RawLink) Close() error {\n\treturn l.fd.Close()\n}\n\n// Pin persists a link past the lifetime of the process.\n//\n// Calling Close on a pinned Link will not break the link\n// until the pin is removed.\nfunc (l *RawLink) Pin(fileName string) error {\n\tif err := sys.Pin(l.pinnedPath, fileName, l.fd); err != nil {\n\t\treturn err\n\t}\n\tl.pinnedPath = fileName\n\treturn nil\n}\n\n// Unpin implements the Link interface.\nfunc (l *RawLink) Unpin() error {\n\tif err := sys.Unpin(l.pinnedPath); err != nil {\n\t\treturn err\n\t}\n\tl.pinnedPath = \"\"\n\treturn nil\n}\n\n// IsPinned returns true if the Link has a non-empty pinned path.\nfunc (l *RawLink) IsPinned() bool {\n\treturn l.pinnedPath != \"\"\n}\n\n// Update implements the Link interface.\nfunc (l *RawLink) Update(new *ebpf.Program) error {\n\treturn l.UpdateArgs(RawLinkUpdateOptions{\n\t\tNew: new,\n\t})\n}\n\n// RawLinkUpdateOptions control the behaviour of RawLink.UpdateArgs.\ntype RawLinkUpdateOptions struct {\n\tNew   *ebpf.Program\n\tOld   *ebpf.Program\n\tFlags uint32\n}\n\n// UpdateArgs updates a link based on args.\nfunc (l *RawLink) UpdateArgs(opts RawLinkUpdateOptions) error {\n\tnewFd := opts.New.FD()\n\tif newFd < 0 {\n\t\treturn fmt.Errorf(\"invalid program: %s\", sys.ErrClosedFd)\n\t}\n\n\tvar oldFd int\n\tif opts.Old != nil {\n\t\toldFd = opts.Old.FD()\n\t\tif oldFd < 0 {\n\t\t\treturn fmt.Errorf(\"invalid replacement program: %s\", sys.ErrClosedFd)\n\t\t}\n\t}\n\n\tattr := sys.LinkUpdateAttr{\n\t\tLinkFd:    l.fd.Uint(),\n\t\tNewProgFd: uint32(newFd),\n\t\tOldProgFd: uint32(oldFd),\n\t\tFlags:     opts.Flags,\n\t}\n\tif err := sys.LinkUpdate(&attr); err != nil {\n\t\treturn fmt.Errorf(\"update link: %w\", err)\n\t}\n\treturn nil\n}\n\n// Detach the link from its corresponding attachment point.\nfunc (l *RawLink) Detach() error {\n\tattr := sys.LinkDetachAttr{\n\t\tLinkFd: l.fd.Uint(),\n\t}\n\n\terr := sys.LinkDetach(&attr)\n\n\tswitch {\n\tcase errors.Is(err, unix.EOPNOTSUPP):\n\t\treturn internal.ErrNotSupported\n\tcase err != nil:\n\t\treturn fmt.Errorf(\"detach link: %w\", err)\n\tdefault:\n\t\treturn nil\n\t}\n}\n\n// Info returns metadata about the link.\n//\n// Linktype specific metadata is not included and can be retrieved\n// via the linktype specific Info() method.\nfunc (l *RawLink) Info() (*Info, error) {\n\tvar info sys.LinkInfo\n\n\tif err := sys.ObjInfo(l.fd, &info); err != nil {\n\t\treturn nil, fmt.Errorf(\"link info: %s\", err)\n\t}\n\n\treturn &Info{\n\t\tinfo.Type,\n\t\tinfo.Id,\n\t\tebpf.ProgramID(info.ProgId),\n\t\tnil,\n\t}, nil\n}\n\n// Iterator allows iterating over links attached into the kernel.\ntype Iterator struct {\n\t// The ID of the current link. Only valid after a call to Next\n\tID ID\n\t// The current link. Only valid until a call to Next.\n\t// See Take if you want to retain the link.\n\tLink Link\n\terr  error\n}\n\n// Next retrieves the next link.\n//\n// Returns true if another link was found. Call [Iterator.Err] after the function returns false.\nfunc (it *Iterator) Next() bool {\n\tid := it.ID\n\tfor {\n\t\tgetIdAttr := &sys.LinkGetNextIdAttr{Id: id}\n\t\terr := sys.LinkGetNextId(getIdAttr)\n\t\tif errors.Is(err, os.ErrNotExist) {\n\t\t\t// There are no more links.\n\t\t\tbreak\n\t\t} else if err != nil {\n\t\t\tit.err = fmt.Errorf(\"get next link ID: %w\", err)\n\t\t\tbreak\n\t\t}\n\n\t\tid = getIdAttr.NextId\n\t\tl, err := NewFromID(id)\n\t\tif errors.Is(err, os.ErrNotExist) {\n\t\t\t// Couldn't load the link fast enough. Try next ID.\n\t\t\tcontinue\n\t\t} else if err != nil {\n\t\t\tit.err = fmt.Errorf(\"get link for ID %d: %w\", id, err)\n\t\t\tbreak\n\t\t}\n\n\t\tif it.Link != nil {\n\t\t\tit.Link.Close()\n\t\t}\n\t\tit.ID, it.Link = id, l\n\t\treturn true\n\t}\n\n\t// No more links or we encountered an error.\n\tif it.Link != nil {\n\t\tit.Link.Close()\n\t}\n\tit.Link = nil\n\treturn false\n}\n\n// Take the ownership of the current link.\n//\n// It's the callers responsibility to close the link.\nfunc (it *Iterator) Take() Link {\n\tl := it.Link\n\tit.Link = nil\n\treturn l\n}\n\n// Err returns an error if iteration failed for some reason.\nfunc (it *Iterator) Err() error {\n\treturn it.err\n}\n\nfunc (it *Iterator) Close() {\n\tif it.Link != nil {\n\t\tit.Link.Close()\n\t}\n}\n"
  },
  {
    "path": "link/link_other.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/platform\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n)\n\n// Valid link types.\nconst (\n\tUnspecifiedType   = sys.BPF_LINK_TYPE_UNSPEC\n\tRawTracepointType = sys.BPF_LINK_TYPE_RAW_TRACEPOINT\n\tTracingType       = sys.BPF_LINK_TYPE_TRACING\n\tCgroupType        = sys.BPF_LINK_TYPE_CGROUP\n\tIterType          = sys.BPF_LINK_TYPE_ITER\n\tNetNsType         = sys.BPF_LINK_TYPE_NETNS\n\tXDPType           = sys.BPF_LINK_TYPE_XDP\n\tPerfEventType     = sys.BPF_LINK_TYPE_PERF_EVENT\n\tKprobeMultiType   = sys.BPF_LINK_TYPE_KPROBE_MULTI\n\tTCXType           = sys.BPF_LINK_TYPE_TCX\n\tUprobeMultiType   = sys.BPF_LINK_TYPE_UPROBE_MULTI\n\tNetfilterType     = sys.BPF_LINK_TYPE_NETFILTER\n\tNetkitType        = sys.BPF_LINK_TYPE_NETKIT\n\tStructOpsType     = sys.BPF_LINK_TYPE_STRUCT_OPS\n)\n\n// AttachRawLink creates a raw link.\nfunc AttachRawLink(opts RawLinkOptions) (*RawLink, error) {\n\tif err := haveBPFLink(); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif opts.Target < 0 {\n\t\treturn nil, fmt.Errorf(\"invalid target: %s\", sys.ErrClosedFd)\n\t}\n\n\tprogFd := opts.Program.FD()\n\tif progFd < 0 {\n\t\treturn nil, fmt.Errorf(\"invalid program: %s\", sys.ErrClosedFd)\n\t}\n\n\tp, attachType := platform.DecodeConstant(opts.Attach)\n\tif p != platform.Linux {\n\t\treturn nil, fmt.Errorf(\"attach type %s: %w\", opts.Attach, internal.ErrNotSupportedOnOS)\n\t}\n\n\tattr := sys.LinkCreateAttr{\n\t\tTargetFd:    uint32(opts.Target),\n\t\tProgFd:      uint32(progFd),\n\t\tAttachType:  sys.AttachType(attachType),\n\t\tTargetBtfId: opts.BTF,\n\t\tFlags:       opts.Flags,\n\t}\n\tfd, err := sys.LinkCreate(&attr)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"create link: %w\", err)\n\t}\n\n\treturn &RawLink{fd, \"\"}, nil\n}\n\n// wrap a RawLink in a more specific type if possible.\n//\n// The function takes ownership of raw and closes it on error.\nfunc wrapRawLink(raw *RawLink) (_ Link, err error) {\n\tdefer func() {\n\t\tif err != nil {\n\t\t\traw.Close()\n\t\t}\n\t}()\n\n\tinfo, err := raw.Info()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tswitch info.Type {\n\tcase RawTracepointType:\n\t\treturn &rawTracepoint{*raw}, nil\n\tcase TracingType:\n\t\treturn &tracing{*raw}, nil\n\tcase CgroupType:\n\t\treturn &linkCgroup{*raw}, nil\n\tcase IterType:\n\t\treturn &Iter{*raw}, nil\n\tcase NetNsType:\n\t\treturn &NetNsLink{*raw}, nil\n\tcase KprobeMultiType:\n\t\treturn &kprobeMultiLink{*raw}, nil\n\tcase UprobeMultiType:\n\t\treturn &uprobeMultiLink{*raw}, nil\n\tcase PerfEventType:\n\t\treturn &perfEventLink{*raw, nil}, nil\n\tcase TCXType:\n\t\treturn &tcxLink{*raw}, nil\n\tcase NetfilterType:\n\t\treturn &netfilterLink{*raw}, nil\n\tcase NetkitType:\n\t\treturn &netkitLink{*raw}, nil\n\tcase XDPType:\n\t\treturn &xdpLink{*raw}, nil\n\tcase StructOpsType:\n\t\treturn &structOpsLink{*raw}, nil\n\tdefault:\n\t\treturn raw, nil\n\t}\n}\n\ntype TracingInfo struct {\n\tAttachType     sys.AttachType\n\tTargetObjectId uint32\n\tTargetBtfId    sys.TypeID\n}\n\ntype CgroupInfo struct {\n\tCgroupId   uint64\n\tAttachType sys.AttachType\n\t_          [4]byte\n}\n\ntype NetNsInfo struct {\n\tNetnsInode uint32\n\tAttachType sys.AttachType\n}\n\ntype TCXInfo struct {\n\tIfindex    uint32\n\tAttachType sys.AttachType\n}\n\ntype XDPInfo struct {\n\tIfindex uint32\n}\n\ntype NetfilterInfo struct {\n\tProtocolFamily NetfilterProtocolFamily\n\tHook           NetfilterInetHook\n\tPriority       int32\n\tFlags          uint32\n}\n\ntype NetkitInfo struct {\n\tIfindex    uint32\n\tAttachType sys.AttachType\n}\n\ntype RawTracepointInfo struct {\n\tName string\n}\n\ntype KprobeMultiInfo struct {\n\t// Count is the number of addresses hooked by the kprobe.\n\tCount   uint32\n\tFlags   uint32\n\tMissed  uint64\n\taddrs   []uint64\n\tcookies []uint64\n}\n\ntype KprobeMultiAddress struct {\n\tAddress uint64\n\tCookie  uint64\n}\n\n// Addresses are the addresses hooked by the kprobe.\nfunc (kpm *KprobeMultiInfo) Addresses() ([]KprobeMultiAddress, bool) {\n\tif kpm.addrs == nil || len(kpm.addrs) != len(kpm.cookies) {\n\t\treturn nil, false\n\t}\n\taddrs := make([]KprobeMultiAddress, len(kpm.addrs))\n\tfor i := range kpm.addrs {\n\t\taddrs[i] = KprobeMultiAddress{\n\t\t\tAddress: kpm.addrs[i],\n\t\t\tCookie:  kpm.cookies[i],\n\t\t}\n\t}\n\treturn addrs, true\n}\n\ntype UprobeMultiInfo struct {\n\tCount         uint32\n\tFlags         uint32\n\tMissed        uint64\n\toffsets       []uint64\n\tcookies       []uint64\n\trefCtrOffsets []uint64\n\t// File is the path that the file the uprobe was attached to\n\t// had at creation time.\n\t//\n\t// However, due to various circumstances (differing mount namespaces,\n\t// file replacement, ...), this path may not point to the same binary\n\t// the uprobe was originally attached to.\n\tFile string\n\tpid  uint32\n}\n\ntype UprobeMultiOffset struct {\n\tOffset         uint64\n\tCookie         uint64\n\tReferenceCount uint64\n}\n\n// Offsets returns the offsets that the uprobe was attached to along with the related cookies and ref counters.\nfunc (umi *UprobeMultiInfo) Offsets() ([]UprobeMultiOffset, bool) {\n\tif umi.offsets == nil || len(umi.cookies) != len(umi.offsets) || len(umi.refCtrOffsets) != len(umi.offsets) {\n\t\treturn nil, false\n\t}\n\tvar adresses = make([]UprobeMultiOffset, len(umi.offsets))\n\tfor i := range umi.offsets {\n\t\tadresses[i] = UprobeMultiOffset{\n\t\t\tOffset:         umi.offsets[i],\n\t\t\tCookie:         umi.cookies[i],\n\t\t\tReferenceCount: umi.refCtrOffsets[i],\n\t\t}\n\t}\n\treturn adresses, true\n}\n\n// Pid returns the process ID that this uprobe is attached to.\n//\n// If it does not exist, the uprobe will trigger for all processes.\nfunc (umi *UprobeMultiInfo) Pid() (uint32, bool) {\n\treturn umi.pid, umi.pid > 0\n}\n\nconst (\n\tPerfEventUnspecified = sys.BPF_PERF_EVENT_UNSPEC\n\tPerfEventUprobe      = sys.BPF_PERF_EVENT_UPROBE\n\tPerfEventUretprobe   = sys.BPF_PERF_EVENT_URETPROBE\n\tPerfEventKprobe      = sys.BPF_PERF_EVENT_KPROBE\n\tPerfEventKretprobe   = sys.BPF_PERF_EVENT_KRETPROBE\n\tPerfEventTracepoint  = sys.BPF_PERF_EVENT_TRACEPOINT\n\tPerfEventEvent       = sys.BPF_PERF_EVENT_EVENT\n)\n\ntype PerfEventInfo struct {\n\tType  sys.PerfEventType\n\textra interface{}\n}\n\nfunc (r *PerfEventInfo) Kprobe() *KprobeInfo {\n\te, _ := r.extra.(*KprobeInfo)\n\treturn e\n}\n\nfunc (r *PerfEventInfo) Uprobe() *UprobeInfo {\n\te, _ := r.extra.(*UprobeInfo)\n\treturn e\n}\n\nfunc (r *PerfEventInfo) Tracepoint() *TracepointInfo {\n\te, _ := r.extra.(*TracepointInfo)\n\treturn e\n}\n\nfunc (r *PerfEventInfo) Event() *EventInfo {\n\te, _ := r.extra.(*EventInfo)\n\treturn e\n}\n\ntype KprobeInfo struct {\n\tAddress  uint64\n\tMissed   uint64\n\tFunction string\n\tOffset   uint32\n}\n\ntype UprobeInfo struct {\n\t// File is the path that the file the uprobe was attached to\n\t// had at creation time.\n\t//\n\t// However, due to various circumstances (differing mount namespaces,\n\t// file replacement, ...), this path may not point to the same binary\n\t// the uprobe was originally attached to.\n\tFile                 string\n\tOffset               uint32\n\tCookie               uint64\n\tOffsetReferenceCount uint64\n}\n\ntype TracepointInfo struct {\n\tTracepoint string\n\tCookie     uint64\n}\n\ntype EventInfo struct {\n\tConfig uint64\n\tType   uint32\n\tCookie uint64\n}\n\n// Tracing returns tracing type-specific link info.\n//\n// Returns nil if the type-specific link info isn't available.\nfunc (r Info) Tracing() *TracingInfo {\n\te, _ := r.extra.(*TracingInfo)\n\treturn e\n}\n\n// Cgroup returns cgroup type-specific link info.\n//\n// Returns nil if the type-specific link info isn't available.\nfunc (r Info) Cgroup() *CgroupInfo {\n\te, _ := r.extra.(*CgroupInfo)\n\treturn e\n}\n\n// NetNs returns netns type-specific link info.\n//\n// Returns nil if the type-specific link info isn't available.\nfunc (r Info) NetNs() *NetNsInfo {\n\te, _ := r.extra.(*NetNsInfo)\n\treturn e\n}\n\n// XDP returns XDP type-specific link info.\n//\n// Returns nil if the type-specific link info isn't available.\nfunc (r Info) XDP() *XDPInfo {\n\te, _ := r.extra.(*XDPInfo)\n\treturn e\n}\n\n// TCX returns TCX type-specific link info.\n//\n// Returns nil if the type-specific link info isn't available.\nfunc (r Info) TCX() *TCXInfo {\n\te, _ := r.extra.(*TCXInfo)\n\treturn e\n}\n\n// Netfilter returns netfilter type-specific link info.\n//\n// Returns nil if the type-specific link info isn't available.\nfunc (r Info) Netfilter() *NetfilterInfo {\n\te, _ := r.extra.(*NetfilterInfo)\n\treturn e\n}\n\n// Netkit returns netkit type-specific link info.\n//\n// Returns nil if the type-specific link info isn't available.\nfunc (r Info) Netkit() *NetkitInfo {\n\te, _ := r.extra.(*NetkitInfo)\n\treturn e\n}\n\n// KprobeMulti returns kprobe-multi type-specific link info.\n//\n// Returns nil if the type-specific link info isn't available.\nfunc (r Info) KprobeMulti() *KprobeMultiInfo {\n\te, _ := r.extra.(*KprobeMultiInfo)\n\treturn e\n}\n\n// UprobeMulti returns uprobe-multi type-specific link info.\n//\n// Returns nil if the type-specific link info isn't available.\nfunc (r Info) UprobeMulti() *UprobeMultiInfo {\n\te, _ := r.extra.(*UprobeMultiInfo)\n\treturn e\n}\n\n// PerfEvent returns perf-event type-specific link info.\n//\n// Returns nil if the type-specific link info isn't available.\nfunc (r Info) PerfEvent() *PerfEventInfo {\n\te, _ := r.extra.(*PerfEventInfo)\n\treturn e\n}\n\n// RawTracepoint returns raw-tracepoint type-specific link info.\n//\n// Returns nil if the type-specific link info isn't available.\nfunc (r Info) RawTracepoint() *RawTracepointInfo {\n\te, _ := r.extra.(*RawTracepointInfo)\n\treturn e\n}\n"
  },
  {
    "path": "link/link_other_test.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc testLinkArch(t *testing.T, link Link) {\n\tt.Run(\"link/info\", func(t *testing.T) {\n\t\tinfo, err := link.Info()\n\t\ttestutils.SkipIfNotSupported(t, err)\n\t\tif err != nil {\n\t\t\tt.Fatal(\"Link info returns an error:\", err)\n\t\t}\n\n\t\tif info.Type == 0 {\n\t\t\tt.Fatal(\"Failed to get link info type\")\n\t\t}\n\n\t\tswitch link.(type) {\n\t\tcase *tracing:\n\t\t\tif info.Tracing() == nil {\n\t\t\t\tt.Fatalf(\"Failed to get link tracing extra info\")\n\t\t\t}\n\t\tcase *linkCgroup:\n\t\t\tcg := info.Cgroup()\n\t\t\tif cg.CgroupId == 0 {\n\t\t\t\tt.Fatalf(\"Failed to get link Cgroup extra info\")\n\t\t\t}\n\t\tcase *NetNsLink:\n\t\t\tnetns := info.NetNs()\n\t\t\tif netns.AttachType == 0 {\n\t\t\t\tt.Fatalf(\"Failed to get link NetNs extra info\")\n\t\t\t}\n\t\tcase *xdpLink:\n\t\t\txdp := info.XDP()\n\t\t\tif xdp.Ifindex == 0 {\n\t\t\t\tt.Fatalf(\"Failed to get link XDP extra info\")\n\t\t\t}\n\t\tcase *tcxLink:\n\t\t\ttcx := info.TCX()\n\t\t\tif tcx.Ifindex == 0 {\n\t\t\t\tt.Fatalf(\"Failed to get link TCX extra info\")\n\t\t\t}\n\t\tcase *netfilterLink:\n\t\t\tnf := info.Netfilter()\n\t\t\tif nf.Priority == 0 {\n\t\t\t\tt.Fatalf(\"Failed to get link Netfilter extra info\")\n\t\t\t}\n\t\tcase *kprobeMultiLink:\n\t\t\t// test default Info data\n\t\t\tkmulti := info.KprobeMulti()\n\t\t\t// kprobe multi link info is supported since kernel 6.6\n\t\t\ttestutils.SkipOnOldKernel(t, \"6.6\", \"bpf_kprobe_multi_link_fill_link_info\")\n\t\t\tqt.Assert(t, qt.Not(qt.Equals(kmulti.Count, 0)))\n\t\t\t// NB: We don't check that missed is actually correct\n\t\t\t// since it's not easy to trigger from tests.\n\t\tcase *perfEventLink:\n\t\t\t// test default Info data\n\t\t\tpevent := info.PerfEvent()\n\t\t\tswitch pevent.Type {\n\t\t\tcase sys.BPF_PERF_EVENT_KPROBE, sys.BPF_PERF_EVENT_KRETPROBE:\n\t\t\t\t_ = pevent.Kprobe()\n\t\t\t\t// NB: We don't check that missed is actually correct\n\t\t\t\t// since it's not easy to trigger from tests.\n\t\t\t\t// Nor do we check the address (since we don't know it here).\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc newRawLink(t *testing.T) (*RawLink, *ebpf.Program) {\n\tt.Helper()\n\n\tcgroup, prog := mustCgroupFixtures(t)\n\tlink, err := AttachRawLink(RawLinkOptions{\n\t\tTarget:  int(cgroup.Fd()),\n\t\tProgram: prog,\n\t\tAttach:  ebpf.AttachCGroupInetEgress,\n\t})\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(\"Can't create raw link:\", err)\n\t}\n\tt.Cleanup(func() { link.Close() })\n\n\treturn link, prog\n}\n\nfunc mustCgroupFixtures(t *testing.T) (*os.File, *ebpf.Program) {\n\tt.Helper()\n\n\ttestutils.SkipIfNotSupported(t, haveProgAttach())\n\n\treturn testutils.CreateCgroup(t), mustLoadProgram(t, ebpf.CGroupSKB, 0, \"\")\n}\n\nfunc mustLoadProgram(tb testing.TB, typ ebpf.ProgramType, attachType ebpf.AttachType, attachTo string) *ebpf.Program {\n\ttb.Helper()\n\n\tlicense := \"MIT\"\n\tswitch typ {\n\tcase ebpf.RawTracepoint, ebpf.LSM:\n\t\tlicense = \"GPL\"\n\t}\n\n\tprog, err := ebpf.NewProgram(&ebpf.ProgramSpec{\n\t\tType:       typ,\n\t\tAttachType: attachType,\n\t\tAttachTo:   attachTo,\n\t\tLicense:    license,\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.Mov.Imm(asm.R0, 0),\n\t\t\tasm.Return(),\n\t\t},\n\t})\n\tif err != nil {\n\t\ttb.Fatal(err)\n\t}\n\n\ttb.Cleanup(func() {\n\t\tprog.Close()\n\t})\n\n\treturn prog\n}\n\nfunc TestDetachLinkFail(t *testing.T) {\n\tprog := mustLoadProgram(t, ebpf.Kprobe, 0, \"\")\n\tdefer prog.Close()\n\n\tuprobeLink, err := bashEx.Uprobe(bashSym, prog, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer uprobeLink.Close()\n\n\terr = uprobeLink.Detach()\n\tqt.Assert(t, qt.ErrorIs(err, ErrNotSupported), qt.Commentf(\"got error: %s\", err))\n}\n"
  },
  {
    "path": "link/link_test.go",
    "content": "package link\n\nimport (\n\t\"errors\"\n\t\"math\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n\t\"github.com/cilium/ebpf/internal/testutils/testmain\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\nfunc TestMain(m *testing.M) {\n\ttestmain.Run(m)\n}\n\nfunc TestRawLink(t *testing.T) {\n\tlink, prog := newRawLink(t)\n\n\tinfo, err := link.Info()\n\tif err != nil {\n\t\tt.Fatal(\"Can't get link info:\", err)\n\t}\n\n\tpi, err := prog.Info()\n\tif err != nil {\n\t\tt.Fatal(\"Can't get program info:\", err)\n\t}\n\n\tprogID, ok := pi.ID()\n\tif !ok {\n\t\tt.Fatal(\"Program ID not available in program info\")\n\t}\n\n\tif info.Program != progID {\n\t\tt.Error(\"Link program ID doesn't match program ID\")\n\t}\n\n\ttestLink(t, link, prog)\n}\n\nfunc TestUnpinRawLink(t *testing.T) {\n\tlink, _ := newPinnedRawLink(t)\n\n\tqt.Assert(t, qt.IsTrue(link.IsPinned()))\n\n\tif err := link.Unpin(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tqt.Assert(t, qt.IsFalse(link.IsPinned()))\n}\n\nfunc TestDetachRawLink(t *testing.T) {\n\tlink, _ := newRawLink(t)\n\n\tif err := link.Detach(); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestRawLinkLoadPinnedWithOptions(t *testing.T) {\n\tlink, path := newPinnedRawLink(t)\n\tdefer link.Close()\n\n\tqt.Assert(t, qt.IsTrue(link.IsPinned()))\n\n\t// It seems like the kernel ignores BPF_F_RDONLY when updating a link,\n\t// so we can't test this.\n\t_, err := loadPinnedRawLink(path, &ebpf.LoadPinOptions{\n\t\tFlags: math.MaxUint32,\n\t})\n\tif !errors.Is(err, unix.EINVAL) {\n\t\tt.Fatal(\"Invalid flags don't trigger an error:\", err)\n\t}\n}\n\nfunc TestIterator(t *testing.T) {\n\ttLink, _ := newPinnedRawLink(t)\n\ttLinkInfo, err := tLink.Info()\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(\"Can't get original link info:\", err)\n\t}\n\n\tit := new(Iterator)\n\tdefer it.Close()\n\n\tprev := it.ID\n\tvar foundLink Link\n\tfor it.Next() {\n\t\t// Iterate all loaded links.\n\t\tif it.Link == nil {\n\t\t\tt.Fatal(\"Next doesn't assign link\")\n\t\t}\n\t\tif it.ID == prev {\n\t\t\tt.Fatal(\"Iterator doesn't advance ID\")\n\t\t}\n\t\tprev = it.ID\n\t\tif it.ID == tLinkInfo.ID {\n\t\t\tfoundLink = it.Take()\n\t\t}\n\t}\n\tif err := it.Err(); err != nil {\n\t\tt.Fatal(\"Iteration returned an error:\", err)\n\t}\n\tif it.Link != nil {\n\t\tt.Fatal(\"Next doesn't clean up link on last iteration\")\n\t}\n\tif prev != it.ID {\n\t\tt.Fatal(\"Next changes ID on last iteration\")\n\t}\n\tif foundLink == nil {\n\t\tt.Fatal(\"Original link not found\")\n\t}\n\tdefer foundLink.Close()\n\t// Confirm that we found the original link.\n\tinfo, err := foundLink.Info()\n\tif err != nil {\n\t\tt.Fatal(\"Can't get link info:\", err)\n\t}\n\tif info.ID != tLinkInfo.ID {\n\t\tt.Fatal(\"Found link has wrong ID\")\n\t}\n\n}\n\nfunc newPinnedRawLink(t *testing.T) (*RawLink, string) {\n\tt.Helper()\n\n\tlink, _ := newRawLink(t)\n\n\tpath := filepath.Join(testutils.TempBPFFS(t), \"link\")\n\terr := link.Pin(path)\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\n\treturn link, path\n}\n\nfunc testLink(t *testing.T, link Link, prog *ebpf.Program) {\n\tt.Helper()\n\n\ttmp := testutils.TempBPFFS(t)\n\n\t_, isRawLink := link.(*RawLink)\n\n\tt.Run(\"link/pinning\", func(t *testing.T) {\n\t\tpath := filepath.Join(tmp, \"link\")\n\t\terr := link.Pin(path)\n\t\ttestutils.SkipIfNotSupported(t, err)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Can't pin %T: %s\", link, err)\n\t\t}\n\n\t\tlink2, err := LoadPinnedLink(path, nil)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Can't load pinned %T: %s\", link, err)\n\t\t}\n\t\tlink2.Close()\n\n\t\tif !isRawLink && reflect.TypeOf(link) != reflect.TypeOf(link2) {\n\t\t\tt.Errorf(\"Loading a pinned %T returns a %T\", link, link2)\n\t\t}\n\n\t\t_, err = LoadPinnedLink(path, &ebpf.LoadPinOptions{\n\t\t\tFlags: math.MaxUint32,\n\t\t})\n\t\tif !errors.Is(err, unix.EINVAL) {\n\t\t\tt.Errorf(\"Loading a pinned %T doesn't respect flags\", link)\n\t\t}\n\t})\n\n\tt.Run(\"link/update\", func(t *testing.T) {\n\t\terr := link.Update(prog)\n\t\ttestutils.SkipIfNotSupported(t, err)\n\t\tif err != nil {\n\t\t\tt.Fatal(\"Update returns an error:\", err)\n\t\t}\n\n\t\tfunc() {\n\t\t\t// Panicking is OK\n\t\t\tdefer func() {\n\t\t\t\t_ = recover()\n\t\t\t}()\n\n\t\t\tif err := link.Update(nil); err == nil {\n\t\t\t\tt.Fatalf(\"%T.Update accepts nil program\", link)\n\t\t\t}\n\t\t}()\n\t})\n\n\ttestLinkArch(t, link)\n\n\ttype FDer interface {\n\t\tFD() int\n\t}\n\n\tt.Run(\"from fd\", func(t *testing.T) {\n\t\tfder, ok := link.(FDer)\n\t\tif !ok {\n\t\t\tt.Skip(\"Link doesn't allow retrieving FD\")\n\t\t}\n\n\t\t// We need to dup the FD since NewLinkFromFD takes\n\t\t// ownership.\n\t\tdupFD := testutils.DupFD(t, fder.FD())\n\n\t\tnewLink, err := NewFromFD(dupFD)\n\t\ttestutils.SkipIfNotSupported(t, err)\n\t\tif err != nil {\n\t\t\tt.Fatal(\"Can't create new link from dup link FD:\", err)\n\t\t}\n\t\tdefer newLink.Close()\n\n\t\tif !isRawLink && reflect.TypeOf(newLink) != reflect.TypeOf(link) {\n\t\t\tt.Fatalf(\"Expected type %T, got %T\", link, newLink)\n\t\t}\n\t})\n\n\tif err := link.Close(); err != nil {\n\t\tt.Fatalf(\"%T.Close returns an error: %s\", link, err)\n\t}\n}\n\nfunc TestLoadWrongPin(t *testing.T) {\n\tl, p := newRawLink(t)\n\n\ttmp := testutils.TempBPFFS(t)\n\tppath := filepath.Join(tmp, \"prog\")\n\tlpath := filepath.Join(tmp, \"link\")\n\n\tqt.Assert(t, qt.IsNil(p.Pin(ppath)))\n\tqt.Assert(t, qt.IsNil(l.Pin(lpath)))\n\n\t_, err := LoadPinnedLink(ppath, nil)\n\tqt.Assert(t, qt.IsNotNil(err))\n\n\tll, err := LoadPinnedLink(lpath, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.IsNil(ll.Close()))\n}\n"
  },
  {
    "path": "link/link_windows.go",
    "content": "package link\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/efw\"\n\t\"github.com/cilium/ebpf/internal/platform\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n)\n\n// AttachRawLink creates a raw link.\nfunc AttachRawLink(opts RawLinkOptions) (*RawLink, error) {\n\tif opts.Target != 0 || opts.BTF != 0 || opts.Flags != 0 {\n\t\treturn nil, fmt.Errorf(\"specified option(s) %w\", internal.ErrNotSupportedOnOS)\n\t}\n\n\tplat, attachType := platform.DecodeConstant(opts.Attach)\n\tif plat != platform.Windows {\n\t\treturn nil, fmt.Errorf(\"attach type %s: %w\", opts.Attach, internal.ErrNotSupportedOnOS)\n\t}\n\n\tattachTypeGUID, err := efw.EbpfGetEbpfAttachType(attachType)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"get attach type: %w\", err)\n\t}\n\n\tprogFd := opts.Program.FD()\n\tif progFd < 0 {\n\t\treturn nil, fmt.Errorf(\"invalid program: %s\", sys.ErrClosedFd)\n\t}\n\n\traw, err := efw.EbpfProgramAttachFds(progFd, attachTypeGUID, nil, 0)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"attach link: %w\", err)\n\t}\n\n\tfd, err := sys.NewFD(int(raw))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &RawLink{fd: fd}, nil\n}\n\nfunc wrapRawLink(raw *RawLink) (Link, error) {\n\treturn raw, nil\n}\n"
  },
  {
    "path": "link/link_windows_test.go",
    "content": "package link\n\nimport (\n\t\"os/exec\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\t\"golang.org/x/sys/windows\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/asm\"\n)\n\n// ntosebpfext has not yet assigned a stable enum value so we can't refer to\n// it via that (https://github.com/microsoft/ntosebpfext/issues/152).\n//\n// See https://github.com/microsoft/ntosebpfext/blob/75ceaac38a0254e44f3219852d79a336d10ad9f3/include/ebpf_ntos_program_attach_type_guids.h\nvar (\n\tprogramTypeProcessGUID = makeGUID(0x22ea7b37, 0x1043, 0x4d0d, [8]byte{0xb6, 0x0d, 0xca, 0xfa, 0x1c, 0x7b, 0x63, 0x8e})\n\tattachTypeProcessGUID  = makeGUID(0x66e20687, 0x9805, 0x4458, [8]byte{0xa0, 0xdb, 0x38, 0xe2, 0x20, 0xd3, 0x16, 0x85})\n)\n\nfunc testLinkArch(t *testing.T, link Link) {}\n\nfunc newRawLink(t *testing.T) (*RawLink, *ebpf.Program) {\n\tprog, err := ebpf.NewProgram(&ebpf.ProgramSpec{\n\t\tType: ebpf.WindowsBind,\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.Mov.Imm(asm.R0, 0),\n\t\t\tasm.Return(),\n\t\t},\n\t\tLicense: \"MIT\",\n\t})\n\tqt.Assert(t, qt.IsNil(err))\n\tt.Cleanup(func() { prog.Close() })\n\n\tlink, err := AttachRawLink(RawLinkOptions{\n\t\tProgram: prog,\n\t\tAttach:  ebpf.AttachWindowsBind,\n\t})\n\tqt.Assert(t, qt.IsNil(err))\n\tt.Cleanup(func() { link.Close() })\n\n\treturn link, prog\n}\n\nfunc TestProcessLink(t *testing.T) {\n\tarray, err := ebpf.NewMap(&ebpf.MapSpec{\n\t\tType:       ebpf.WindowsArray,\n\t\tName:       \"process_state\",\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: 1,\n\t})\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer array.Close()\n\n\tprog, err := ebpf.NewProgram(&ebpf.ProgramSpec{\n\t\tType: windowsProgramTypeForGUID(t, programTypeProcessGUID),\n\t\tName: \"process_test\",\n\t\tInstructions: asm.Instructions{\n\t\t\t// R1 = map\n\t\t\tasm.LoadMapPtr(asm.R1, array.FD()),\n\t\t\t// R2 = key\n\t\t\tasm.Mov.Reg(asm.R2, asm.R10),\n\t\t\tasm.Add.Imm(asm.R2, -4),\n\t\t\tasm.StoreImm(asm.R2, 0, 0, asm.Word),\n\t\t\t// R3 = value\n\t\t\tasm.Mov.Reg(asm.R3, asm.R2),\n\t\t\tasm.Add.Imm(asm.R3, -4),\n\t\t\tasm.StoreImm(asm.R3, 0, 1, asm.Word),\n\t\t\t// R4 = flags\n\t\t\tasm.Mov.Imm(asm.R4, 0),\n\t\t\t// bpf_map_update_elem(map, key, value, flags)\n\t\t\tasm.WindowsFnMapUpdateElem.Call(),\n\t\t\tasm.Mov.Imm(asm.R0, 0),\n\t\t\tasm.Return(),\n\t\t},\n\t\tLicense: \"MIT\",\n\t})\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer prog.Close()\n\n\tlink, err := AttachRawLink(RawLinkOptions{\n\t\tProgram: prog,\n\t\tAttach:  windowsAttachTypeForGUID(t, attachTypeProcessGUID),\n\t})\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer link.Close()\n\n\tqt.Assert(t, qt.IsNil(exec.Command(\"cmd.exe\", \"/c\", \"exit 0\").Run()))\n\n\tvar value uint32\n\tqt.Assert(t, qt.IsNil(array.Lookup(uint32(0), &value)))\n\tqt.Assert(t, qt.Equals(value, 1), qt.Commentf(\"Executing a binary should trigger the program\"))\n\n\tqt.Assert(t, qt.IsNil(link.Close()))\n}\n\nfunc makeGUID(data1 uint32, data2 uint16, data3 uint16, data4 [8]byte) windows.GUID {\n\treturn windows.GUID{Data1: data1, Data2: data2, Data3: data3, Data4: data4}\n}\n"
  },
  {
    "path": "link/netfilter.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n)\n\nconst NetfilterIPDefrag NetfilterAttachFlags = 0 // Enable IP packet defragmentation\n\ntype NetfilterAttachFlags uint32\n\ntype NetfilterInetHook = sys.NetfilterInetHook\n\nconst (\n\tNetfilterInetPreRouting  = sys.NF_INET_PRE_ROUTING\n\tNetfilterInetLocalIn     = sys.NF_INET_LOCAL_IN\n\tNetfilterInetForward     = sys.NF_INET_FORWARD\n\tNetfilterInetLocalOut    = sys.NF_INET_LOCAL_OUT\n\tNetfilterInetPostRouting = sys.NF_INET_POST_ROUTING\n)\n\ntype NetfilterProtocolFamily = sys.NetfilterProtocolFamily\n\nconst (\n\tNetfilterProtoUnspec = sys.NFPROTO_UNSPEC\n\tNetfilterProtoInet   = sys.NFPROTO_INET // Inet applies to both IPv4 and IPv6\n\tNetfilterProtoIPv4   = sys.NFPROTO_IPV4\n\tNetfilterProtoARP    = sys.NFPROTO_ARP\n\tNetfilterProtoNetdev = sys.NFPROTO_NETDEV\n\tNetfilterProtoBridge = sys.NFPROTO_BRIDGE\n\tNetfilterProtoIPv6   = sys.NFPROTO_IPV6\n)\n\ntype NetfilterOptions struct {\n\t// Program must be a netfilter BPF program.\n\tProgram *ebpf.Program\n\t// The protocol family.\n\tProtocolFamily NetfilterProtocolFamily\n\t// The netfilter hook to attach to.\n\tHook NetfilterInetHook\n\t// Priority within hook\n\tPriority int32\n\t// Extra link flags\n\tFlags uint32\n\t// Netfilter flags\n\tNetfilterFlags NetfilterAttachFlags\n}\n\ntype netfilterLink struct {\n\tRawLink\n}\n\n// AttachNetfilter links a netfilter BPF program to a netfilter hook.\nfunc AttachNetfilter(opts NetfilterOptions) (Link, error) {\n\tif opts.Program == nil {\n\t\treturn nil, fmt.Errorf(\"netfilter program is nil\")\n\t}\n\n\tif t := opts.Program.Type(); t != ebpf.Netfilter {\n\t\treturn nil, fmt.Errorf(\"invalid program type %s, expected netfilter\", t)\n\t}\n\n\tprogFd := opts.Program.FD()\n\tif progFd < 0 {\n\t\treturn nil, fmt.Errorf(\"invalid program: %s\", sys.ErrClosedFd)\n\t}\n\n\tattr := sys.LinkCreateNetfilterAttr{\n\t\tProgFd:         uint32(opts.Program.FD()),\n\t\tAttachType:     sys.BPF_NETFILTER,\n\t\tFlags:          opts.Flags,\n\t\tPf:             opts.ProtocolFamily,\n\t\tHooknum:        opts.Hook,\n\t\tPriority:       opts.Priority,\n\t\tNetfilterFlags: uint32(opts.NetfilterFlags),\n\t}\n\n\tfd, err := sys.LinkCreateNetfilter(&attr)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"attach netfilter link: %w\", err)\n\t}\n\n\treturn &netfilterLink{RawLink{fd, \"\"}}, nil\n}\n\nfunc (*netfilterLink) Update(_ *ebpf.Program) error {\n\treturn fmt.Errorf(\"netfilter update: %w\", ErrNotSupported)\n}\n\nfunc (nf *netfilterLink) Info() (*Info, error) {\n\tvar info sys.NetfilterLinkInfo\n\tif err := sys.ObjInfo(nf.fd, &info); err != nil {\n\t\treturn nil, fmt.Errorf(\"netfilter link info: %s\", err)\n\t}\n\textra := &NetfilterInfo{\n\t\tProtocolFamily: info.Pf,\n\t\tHook:           info.Hooknum,\n\t\tPriority:       info.Priority,\n\t\tFlags:          info.Flags,\n\t}\n\n\treturn &Info{\n\t\tinfo.Type,\n\t\tinfo.Id,\n\t\tebpf.ProgramID(info.ProgId),\n\t\textra,\n\t}, nil\n}\n\nvar _ Link = (*netfilterLink)(nil)\n"
  },
  {
    "path": "link/netfilter_test.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc TestAttachNetfilter(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"6.4\", \"BPF_LINK_TYPE_NETFILTER\")\n\n\tprog := mustLoadProgram(t, ebpf.Netfilter, ebpf.AttachNetfilter, \"\")\n\n\tl, err := AttachNetfilter(NetfilterOptions{\n\t\tProgram:        prog,\n\t\tProtocolFamily: NetfilterProtoIPv4,\n\t\tHook:           NetfilterInetLocalOut,\n\t\tPriority:       -128,\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tinfo, err := l.Info()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tnfInfo := info.Netfilter()\n\tqt.Assert(t, qt.Equals(nfInfo.ProtocolFamily, NetfilterProtoIPv4))\n\tqt.Assert(t, qt.Equals(nfInfo.Hook, NetfilterInetLocalOut))\n\tqt.Assert(t, qt.Equals(nfInfo.Priority, -128))\n\n\ttestLink(t, l, prog)\n}\n"
  },
  {
    "path": "link/netkit.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n)\n\ntype NetkitOptions struct {\n\t// Index of the interface to attach to.\n\tInterface int\n\t// Program to attach.\n\tProgram *ebpf.Program\n\t// One of the AttachNetkit* constants.\n\tAttach ebpf.AttachType\n\t// Attach relative to an anchor. Optional.\n\tAnchor Anchor\n\t// Only attach if the expected revision matches.\n\tExpectedRevision uint64\n\t// Flags control the attach behaviour. Specify an Anchor instead of\n\t// F_LINK, F_ID, F_BEFORE, F_AFTER and R_REPLACE. Optional.\n\tFlags uint32\n}\n\nfunc AttachNetkit(opts NetkitOptions) (Link, error) {\n\tif opts.Interface < 0 {\n\t\treturn nil, fmt.Errorf(\"interface %d is out of bounds\", opts.Interface)\n\t}\n\n\tif opts.Flags&anchorFlags != 0 {\n\t\treturn nil, fmt.Errorf(\"disallowed flags: use Anchor to specify attach target\")\n\t}\n\n\tattr := sys.LinkCreateNetkitAttr{\n\t\tProgFd:           uint32(opts.Program.FD()),\n\t\tAttachType:       sys.AttachType(opts.Attach),\n\t\tTargetIfindex:    uint32(opts.Interface),\n\t\tExpectedRevision: opts.ExpectedRevision,\n\t\tFlags:            opts.Flags,\n\t}\n\n\tif opts.Anchor != nil {\n\t\tfdOrID, flags, err := opts.Anchor.anchor()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"attach netkit link: %w\", err)\n\t\t}\n\n\t\tattr.RelativeFdOrId = fdOrID\n\t\tattr.Flags |= flags\n\t}\n\n\tfd, err := sys.LinkCreateNetkit(&attr)\n\truntime.KeepAlive(opts.Program)\n\truntime.KeepAlive(opts.Anchor)\n\tif err != nil {\n\t\tif haveFeatErr := haveNetkit(); haveFeatErr != nil {\n\t\t\treturn nil, haveFeatErr\n\t\t}\n\t\treturn nil, fmt.Errorf(\"attach netkit link: %w\", err)\n\t}\n\n\treturn &netkitLink{RawLink{fd, \"\"}}, nil\n}\n\ntype netkitLink struct {\n\tRawLink\n}\n\nvar _ Link = (*netkitLink)(nil)\n\nfunc (netkit *netkitLink) Info() (*Info, error) {\n\tvar info sys.NetkitLinkInfo\n\tif err := sys.ObjInfo(netkit.fd, &info); err != nil {\n\t\treturn nil, fmt.Errorf(\"netkit link info: %s\", err)\n\t}\n\textra := &NetkitInfo{\n\t\tIfindex:    info.Ifindex,\n\t\tAttachType: info.AttachType,\n\t}\n\n\treturn &Info{\n\t\tinfo.Type,\n\t\tinfo.Id,\n\t\tebpf.ProgramID(info.ProgId),\n\t\textra,\n\t}, nil\n}\n"
  },
  {
    "path": "link/netkit_test.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"fmt\"\n\t\"sync/atomic\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\t\"github.com/jsimonetti/rtnetlink/v2\"\n\t\"github.com/jsimonetti/rtnetlink/v2/driver\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\nfunc TestAttachNetkit(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"6.7\", \"Netkit Device\")\n\n\tns := testutils.NewNetNS(t)\n\n\tprog := mustLoadProgram(t, ebpf.SchedCLS, ebpf.AttachNetkitPrimary, \"\")\n\tlink, _ := mustAttachNetkit(t, prog, ebpf.AttachNetkitPrimary, ns)\n\n\ttestLink(t, link, prog)\n}\n\nfunc TestNetkitAnchor(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"6.7\", \"Netkit Device\")\n\n\ta := mustLoadProgram(t, ebpf.SchedCLS, ebpf.AttachNetkitPrimary, \"\")\n\tb := mustLoadProgram(t, ebpf.SchedCLS, ebpf.AttachNetkitPrimary, \"\")\n\n\tns := testutils.NewNetNS(t)\n\n\tlinkA, ifIndex := mustAttachNetkit(t, a, ebpf.AttachNetkitPrimary, ns)\n\n\tprogramInfo, err := a.Info()\n\tqt.Assert(t, qt.IsNil(err))\n\tprogramID, _ := programInfo.ID()\n\n\tlinkInfo, err := linkA.Info()\n\tqt.Assert(t, qt.IsNil(err))\n\tlinkID := linkInfo.ID\n\n\tfor _, anchor := range []Anchor{\n\t\tHead(),\n\t\tTail(),\n\t\tBeforeProgram(a),\n\t\tBeforeProgramByID(programID),\n\t\tAfterLink(linkA),\n\t\tAfterLinkByID(linkID),\n\t} {\n\t\tt.Run(fmt.Sprintf(\"%T\", anchor), func(t *testing.T) {\n\t\t\tvar linkB Link\n\t\t\tqt.Assert(t, qt.IsNil(ns.Do(func() (err error) {\n\t\t\t\tlinkB, err = AttachNetkit(NetkitOptions{\n\t\t\t\t\tProgram:   b,\n\t\t\t\t\tAttach:    ebpf.AttachNetkitPrimary,\n\t\t\t\t\tInterface: ifIndex,\n\t\t\t\t\tAnchor:    anchor,\n\t\t\t\t})\n\t\t\t\treturn err\n\t\t\t})))\n\t\t\tqt.Assert(t, qt.IsNil(linkB.Close()))\n\t\t})\n\t}\n}\n\n// The last ifindex we created.\nvar prevIfindex atomic.Uint32\n\nfunc init() { prevIfindex.Store(1000 - 1) }\n\nfunc mustAttachNetkit(tb testing.TB, prog *ebpf.Program, attachType ebpf.AttachType, ns *testutils.NetNS) (Link, int) {\n\tvar conn *rtnetlink.Conn\n\tqt.Assert(tb, qt.IsNil(ns.Do(func() (err error) {\n\t\tconn, err = rtnetlink.Dial(nil)\n\t\treturn err\n\t})))\n\ttb.Cleanup(func() {\n\t\tqt.Assert(tb, qt.IsNil(conn.Close()))\n\t})\n\n\tifIndex := prevIfindex.Add(1)\n\n\tlayer2 := driver.NetkitModeL2\n\tblackhole := driver.NetkitPolicyDrop\n\tqt.Assert(tb, qt.IsNil(conn.Link.New(&rtnetlink.LinkMessage{\n\t\tFamily: unix.AF_UNSPEC,\n\t\tIndex:  ifIndex,\n\t\tFlags:  unix.IFF_UP,\n\t\tChange: unix.IFF_UP,\n\t\tAttributes: &rtnetlink.LinkAttributes{\n\t\t\tInfo: &rtnetlink.LinkInfo{\n\t\t\t\tKind: \"netkit\",\n\t\t\t\tData: &driver.Netkit{\n\t\t\t\t\tMode:       &layer2,\n\t\t\t\t\tPeerPolicy: &blackhole,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t})))\n\ttb.Cleanup(func() {\n\t\tqt.Assert(tb, qt.IsNil(conn.Link.Delete(uint32(ifIndex))))\n\t})\n\n\tvar link Link\n\tqt.Assert(tb, qt.IsNil(ns.Do(func() (err error) {\n\t\tlink, err = AttachNetkit(NetkitOptions{\n\t\t\tProgram:   prog,\n\t\t\tAttach:    attachType,\n\t\t\tInterface: int(ifIndex),\n\t\t})\n\t\treturn err\n\t})))\n\ttb.Cleanup(func() {\n\t\tqt.Assert(tb, qt.IsNil(link.Close()))\n\t})\n\n\treturn link, int(ifIndex)\n}\n"
  },
  {
    "path": "link/netns.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n)\n\n// NetNsLink is a program attached to a network namespace.\ntype NetNsLink struct {\n\tRawLink\n}\n\n// AttachNetNs attaches a program to a network namespace.\nfunc AttachNetNs(ns int, prog *ebpf.Program) (*NetNsLink, error) {\n\tvar attach ebpf.AttachType\n\tswitch t := prog.Type(); t {\n\tcase ebpf.FlowDissector:\n\t\tattach = ebpf.AttachFlowDissector\n\tcase ebpf.SkLookup:\n\t\tattach = ebpf.AttachSkLookup\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"can't attach %v to network namespace\", t)\n\t}\n\n\tlink, err := AttachRawLink(RawLinkOptions{\n\t\tTarget:  ns,\n\t\tProgram: prog,\n\t\tAttach:  attach,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &NetNsLink{*link}, nil\n}\n\nfunc (ns *NetNsLink) Info() (*Info, error) {\n\tvar info sys.NetNsLinkInfo\n\tif err := sys.ObjInfo(ns.fd, &info); err != nil {\n\t\treturn nil, fmt.Errorf(\"netns link info: %s\", err)\n\t}\n\textra := &NetNsInfo{\n\t\tNetnsInode: info.NetnsIno,\n\t\tAttachType: info.AttachType,\n\t}\n\n\treturn &Info{\n\t\tinfo.Type,\n\t\tinfo.Id,\n\t\tebpf.ProgramID(info.ProgId),\n\t\textra,\n\t}, nil\n}\n"
  },
  {
    "path": "link/netns_test.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc TestSkLookup(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.8\", \"sk_lookup program\")\n\n\tprog := mustLoadProgram(t, ebpf.SkLookup, ebpf.AttachSkLookup, \"\")\n\n\tnetns, err := os.Open(\"/proc/self/ns/net\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer netns.Close()\n\n\tlink, err := AttachNetNs(int(netns.Fd()), prog)\n\tif err != nil {\n\t\tt.Fatal(\"Can't attach link:\", err)\n\t}\n\n\ttestLink(t, link, prog)\n}\n\nfunc createSkLookupProgram() (*ebpf.Program, error) {\n\tprog, err := ebpf.NewProgram(&ebpf.ProgramSpec{\n\t\tType:       ebpf.SkLookup,\n\t\tAttachType: ebpf.AttachSkLookup,\n\t\tLicense:    \"MIT\",\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.Mov.Imm(asm.R0, 0),\n\t\t\tasm.Return(),\n\t\t},\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn prog, nil\n}\n\nfunc ExampleAttachNetNs() {\n\tprog, err := createSkLookupProgram()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer prog.Close()\n\n\t// This can be a path to another netns as well.\n\tnetns, err := os.Open(\"/proc/self/ns/net\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer netns.Close()\n\n\tlink, err := AttachNetNs(int(netns.Fd()), prog)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// The socket lookup program is now active until Close().\n\tlink.Close()\n}\n"
  },
  {
    "path": "link/perf_event.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"unsafe\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/tracefs\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\n// Getting the terminology right is usually the hardest part. For posterity and\n// for staying sane during implementation:\n//\n// - trace event: Representation of a kernel runtime hook. Filesystem entries\n//   under <tracefs>/events. Can be tracepoints (static), kprobes or uprobes.\n//   Can be instantiated into perf events (see below).\n// - tracepoint: A predetermined hook point in the kernel. Exposed as trace\n//   events in (sub)directories under <tracefs>/events. Cannot be closed or\n//   removed, they are static.\n// - k(ret)probe: Ephemeral trace events based on entry or exit points of\n//   exported kernel symbols. kprobe-based (tracefs) trace events can be\n//   created system-wide by writing to the <tracefs>/kprobe_events file, or\n//   they can be scoped to the current process by creating PMU perf events.\n// - u(ret)probe: Ephemeral trace events based on user provides ELF binaries\n//   and offsets. uprobe-based (tracefs) trace events can be\n//   created system-wide by writing to the <tracefs>/uprobe_events file, or\n//   they can be scoped to the current process by creating PMU perf events.\n// - perf event: An object instantiated based on an existing trace event or\n//   kernel symbol. Referred to by fd in userspace.\n//   Exactly one eBPF program can be attached to a perf event. Multiple perf\n//   events can be created from a single trace event. Closing a perf event\n//   stops any further invocations of the attached eBPF program.\n\nvar (\n\terrInvalidInput = tracefs.ErrInvalidInput\n)\n\nconst (\n\tperfAllThreads = -1\n)\n\n// A perfEvent represents a perf event kernel object. Exactly one eBPF program\n// can be attached to it. It is created based on a tracefs trace event or a\n// Performance Monitoring Unit (PMU).\ntype perfEvent struct {\n\t// Trace event backing this perfEvent. May be nil.\n\ttracefsEvent *tracefs.Event\n\n\t// This is the perf event FD.\n\tfd *sys.FD\n}\n\nfunc newPerfEvent(fd *sys.FD, event *tracefs.Event) *perfEvent {\n\tpe := &perfEvent{event, fd}\n\treturn pe\n}\n\nfunc (pe *perfEvent) Close() error {\n\t// We close the perf event before attempting to remove the tracefs event.\n\tif err := pe.fd.Close(); err != nil {\n\t\treturn fmt.Errorf(\"closing perf event fd: %w\", err)\n\t}\n\n\tif pe.tracefsEvent != nil {\n\t\treturn pe.tracefsEvent.Close()\n\t}\n\n\treturn nil\n}\n\n// PerfEvent is implemented by some Link types which use a perf event under\n// the hood.\ntype PerfEvent interface {\n\t// PerfEvent returns a file for the underlying perf event.\n\t//\n\t// It is the callers responsibility to close the returned file.\n\t//\n\t// Making changes to the associated perf event lead to\n\t// undefined behaviour.\n\tPerfEvent() (*os.File, error)\n}\n\n// perfEventLink represents a bpf perf link.\ntype perfEventLink struct {\n\tRawLink\n\tpe *perfEvent\n}\n\nfunc (pl *perfEventLink) isLink() {}\n\nfunc (pl *perfEventLink) Close() error {\n\tif err := pl.fd.Close(); err != nil {\n\t\treturn fmt.Errorf(\"perf link close: %w\", err)\n\t}\n\n\t// when created from pinned link\n\tif pl.pe == nil {\n\t\treturn nil\n\t}\n\n\tif err := pl.pe.Close(); err != nil {\n\t\treturn fmt.Errorf(\"perf event close: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc (pl *perfEventLink) Update(_ *ebpf.Program) error {\n\treturn fmt.Errorf(\"perf event link update: %w\", ErrNotSupported)\n}\n\nvar _ PerfEvent = (*perfEventLink)(nil)\n\nfunc (pl *perfEventLink) PerfEvent() (*os.File, error) {\n\t// when created from pinned link\n\tif pl.pe == nil {\n\t\treturn nil, ErrNotSupported\n\t}\n\n\tfd, err := pl.pe.fd.Dup()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn fd.File(\"perf-event\")\n}\n\n// queryInfoWithString queries object info that contains a string field.\n//\n// The passed stringField and stringLengthField must point to the string field\n// and its length field inside the info struct respectively.\n//\n// It returns the queried string and fills in the passed info struct.\nfunc queryInfoWithString(fd *sys.FD, info sys.Info, stringField *sys.TypedPointer[byte], stringLengthField *uint32) (string, error) {\n\t// Query info to get the length\n\tif err := sys.ObjInfo(fd, info); err != nil {\n\t\treturn \"\", err\n\t}\n\n\t// The stringLengthField pointer points to a field inside info, so it is now populated.\n\tvar stringData = make([]byte, *stringLengthField)\n\t*stringField = sys.SlicePointer(stringData)\n\n\t// Query info again to fill in the string.\n\t// Since the stringField pointer points to a field inside info,\n\t// the info now contains the pointer to our allocated stringData.\n\tif err := sys.ObjInfo(fd, info); err != nil {\n\t\treturn \"\", fmt.Errorf(\"object info with string: %s\", err)\n\t}\n\n\treturn unix.ByteSliceToString(stringData), nil\n}\n\nfunc (pl *perfEventLink) Info() (*Info, error) {\n\tvar info sys.PerfEventLinkInfo\n\tif err := sys.ObjInfo(pl.fd, &info); err != nil {\n\t\treturn nil, fmt.Errorf(\"perf event link info: %s\", err)\n\t}\n\n\tvar extra2 interface{}\n\tswitch info.PerfEventType {\n\tcase PerfEventKprobe, PerfEventKretprobe:\n\t\tvar kprobeInfo sys.KprobeLinkInfo\n\t\tfuncName, err := queryInfoWithString(pl.fd, &kprobeInfo, &kprobeInfo.FuncName, &kprobeInfo.NameLen)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"kprobe link info: %s\", err)\n\t\t}\n\t\textra2 = &KprobeInfo{\n\t\t\tAddress:  kprobeInfo.Addr,\n\t\t\tMissed:   kprobeInfo.Missed,\n\t\t\tFunction: funcName,\n\t\t\tOffset:   kprobeInfo.Offset,\n\t\t}\n\tcase PerfEventUprobe, PerfEventUretprobe:\n\t\tvar uprobeInfo sys.UprobeLinkInfo\n\t\tfileName, err := queryInfoWithString(pl.fd, &uprobeInfo, &uprobeInfo.FileName, &uprobeInfo.NameLen)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"uprobe link info: %s\", err)\n\t\t}\n\t\textra2 = &UprobeInfo{\n\t\t\tOffset:               uprobeInfo.Offset,\n\t\t\tCookie:               uprobeInfo.Cookie,\n\t\t\tOffsetReferenceCount: uprobeInfo.RefCtrOffset,\n\t\t\tFile:                 fileName,\n\t\t}\n\tcase PerfEventTracepoint:\n\t\tvar tracepointInfo sys.TracepointLinkInfo\n\t\ttpName, err := queryInfoWithString(pl.fd, &tracepointInfo, &tracepointInfo.TpName, &tracepointInfo.NameLen)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"perf event link info: %w\", err)\n\t\t}\n\t\textra2 = &TracepointInfo{\n\t\t\tTracepoint: tpName,\n\t\t\tCookie:     tracepointInfo.Cookie,\n\t\t}\n\tcase PerfEventEvent:\n\t\tvar eventInfo sys.EventLinkInfo\n\t\terr := sys.ObjInfo(pl.fd, &eventInfo)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"trace point link info: %s\", err)\n\t\t}\n\t\textra2 = &EventInfo{\n\t\t\tConfig: eventInfo.Config,\n\t\t\tType:   eventInfo.EventType,\n\t\t\tCookie: eventInfo.Cookie,\n\t\t}\n\t}\n\n\textra := &PerfEventInfo{\n\t\tType:  info.PerfEventType,\n\t\textra: extra2,\n\t}\n\n\treturn &Info{\n\t\tinfo.Type,\n\t\tinfo.Id,\n\t\tebpf.ProgramID(info.ProgId),\n\t\textra,\n\t}, nil\n}\n\n// perfEventIoctl implements Link and handles the perf event lifecycle\n// via ioctl().\ntype perfEventIoctl struct {\n\t*perfEvent\n}\n\nfunc (pi *perfEventIoctl) isLink() {}\n\n// Since 4.15 (e87c6bc3852b \"bpf: permit multiple bpf attachments for a single perf event\"),\n// calling PERF_EVENT_IOC_SET_BPF appends the given program to a prog_array\n// owned by the perf event, which means multiple programs can be attached\n// simultaneously.\n//\n// Before 4.15, calling PERF_EVENT_IOC_SET_BPF more than once on a perf event\n// returns EEXIST.\n//\n// Detaching a program from a perf event is currently not possible, so a\n// program replacement mechanism cannot be implemented for perf events.\nfunc (pi *perfEventIoctl) Update(_ *ebpf.Program) error {\n\treturn fmt.Errorf(\"perf event ioctl update: %w\", ErrNotSupported)\n}\n\nfunc (pi *perfEventIoctl) Pin(string) error {\n\treturn fmt.Errorf(\"perf event ioctl pin: %w\", ErrNotSupported)\n}\n\nfunc (pi *perfEventIoctl) Unpin() error {\n\treturn fmt.Errorf(\"perf event ioctl unpin: %w\", ErrNotSupported)\n}\n\nfunc (pi *perfEventIoctl) Detach() error {\n\treturn fmt.Errorf(\"perf event ioctl detach: %w\", ErrNotSupported)\n}\n\nfunc (pi *perfEventIoctl) Info() (*Info, error) {\n\treturn nil, fmt.Errorf(\"perf event ioctl info: %w\", ErrNotSupported)\n}\n\nvar _ PerfEvent = (*perfEventIoctl)(nil)\n\nfunc (pi *perfEventIoctl) PerfEvent() (*os.File, error) {\n\tfd, err := pi.fd.Dup()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn fd.File(\"perf-event\")\n}\n\n// attach the given eBPF prog to the perf event stored in pe.\n// pe must contain a valid perf event fd.\n// prog's type must match the program type stored in pe.\nfunc attachPerfEvent(pe *perfEvent, prog *ebpf.Program, cookie uint64) (Link, error) {\n\tif prog == nil {\n\t\treturn nil, errors.New(\"cannot attach a nil program\")\n\t}\n\tif prog.FD() < 0 {\n\t\treturn nil, fmt.Errorf(\"invalid program: %w\", sys.ErrClosedFd)\n\t}\n\n\tif err := haveBPFLinkPerfEvent(); err == nil {\n\t\treturn attachPerfEventLink(pe, prog, cookie)\n\t}\n\n\tif cookie != 0 {\n\t\treturn nil, fmt.Errorf(\"cookies are not supported: %w\", ErrNotSupported)\n\t}\n\n\treturn attachPerfEventIoctl(pe, prog)\n}\n\nfunc attachPerfEventIoctl(pe *perfEvent, prog *ebpf.Program) (*perfEventIoctl, error) {\n\t// Assign the eBPF program to the perf event.\n\terr := unix.IoctlSetInt(pe.fd.Int(), unix.PERF_EVENT_IOC_SET_BPF, prog.FD())\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"setting perf event bpf program: %w\", err)\n\t}\n\n\t// PERF_EVENT_IOC_ENABLE and _DISABLE ignore their given values.\n\tif err := unix.IoctlSetInt(pe.fd.Int(), unix.PERF_EVENT_IOC_ENABLE, 0); err != nil {\n\t\treturn nil, fmt.Errorf(\"enable perf event: %s\", err)\n\t}\n\n\treturn &perfEventIoctl{pe}, nil\n}\n\n// Use the bpf api to attach the perf event (BPF_LINK_TYPE_PERF_EVENT, 5.15+).\n//\n// https://github.com/torvalds/linux/commit/b89fbfbb854c9afc3047e8273cc3a694650b802e\nfunc attachPerfEventLink(pe *perfEvent, prog *ebpf.Program, cookie uint64) (*perfEventLink, error) {\n\tfd, err := sys.LinkCreatePerfEvent(&sys.LinkCreatePerfEventAttr{\n\t\tProgFd:     uint32(prog.FD()),\n\t\tTargetFd:   pe.fd.Uint(),\n\t\tAttachType: sys.BPF_PERF_EVENT,\n\t\tBpfCookie:  cookie,\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"cannot create bpf perf link: %v\", err)\n\t}\n\n\treturn &perfEventLink{RawLink{fd: fd}, pe}, nil\n}\n\n// unsafeStringPtr returns an unsafe.Pointer to a NUL-terminated copy of str.\nfunc unsafeStringPtr(str string) (unsafe.Pointer, error) {\n\tp, err := unix.BytePtrFromString(str)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn unsafe.Pointer(p), nil\n}\n\n// openTracepointPerfEvent opens a tracepoint-type perf event. System-wide\n// [k,u]probes created by writing to <tracefs>/[k,u]probe_events are tracepoints\n// behind the scenes, and can be attached to using these perf events.\nfunc openTracepointPerfEvent(tid uint64, pid int) (*sys.FD, error) {\n\tattr := unix.PerfEventAttr{\n\t\tType:        unix.PERF_TYPE_TRACEPOINT,\n\t\tConfig:      tid,\n\t\tSample_type: unix.PERF_SAMPLE_RAW,\n\t\tSample:      1,\n\t\tWakeup:      1,\n\t}\n\n\tcpu := 0\n\tif pid != perfAllThreads {\n\t\tcpu = -1\n\t}\n\tfd, err := unix.PerfEventOpen(&attr, pid, cpu, -1, unix.PERF_FLAG_FD_CLOEXEC)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"opening tracepoint perf event: %w\", err)\n\t}\n\n\treturn sys.NewFD(fd)\n}\n\n// Probe BPF perf link.\n//\n// https://elixir.bootlin.com/linux/v5.16.8/source/kernel/bpf/syscall.c#L4307\n// https://github.com/torvalds/linux/commit/b89fbfbb854c9afc3047e8273cc3a694650b802e\nvar haveBPFLinkPerfEvent = internal.NewFeatureTest(\"bpf_link_perf_event\", func() error {\n\tprog, err := ebpf.NewProgram(&ebpf.ProgramSpec{\n\t\tName: \"probe_bpf_perf_link\",\n\t\tType: ebpf.Kprobe,\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.Mov.Imm(asm.R0, 0),\n\t\t\tasm.Return(),\n\t\t},\n\t\tLicense: \"MIT\",\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer prog.Close()\n\n\t_, err = sys.LinkCreatePerfEvent(&sys.LinkCreatePerfEventAttr{\n\t\tProgFd:     uint32(prog.FD()),\n\t\tAttachType: sys.BPF_PERF_EVENT,\n\t})\n\tif errors.Is(err, unix.EINVAL) {\n\t\treturn internal.ErrNotSupported\n\t}\n\tif errors.Is(err, unix.EBADF) {\n\t\treturn nil\n\t}\n\treturn err\n}, \"5.15\")\n"
  },
  {
    "path": "link/perf_event_test.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc TestHaveBPFLinkPerfEvent(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, haveBPFLinkPerfEvent)\n}\n"
  },
  {
    "path": "link/program.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n)\n\ntype RawAttachProgramOptions struct {\n\t// Target to query. This is usually a file descriptor but may refer to\n\t// something else based on the attach type.\n\tTarget int\n\t// Program to attach.\n\tProgram *ebpf.Program\n\t// Attach must match the attach type of Program.\n\tAttach ebpf.AttachType\n\t// Attach relative to an anchor. Optional.\n\tAnchor Anchor\n\t// Flags control the attach behaviour. Specify an Anchor instead of\n\t// F_LINK, F_ID, F_BEFORE, F_AFTER and F_REPLACE. Optional.\n\tFlags uint32\n\t// Only attach if the internal revision matches the given value.\n\tExpectedRevision uint64\n}\n\n// RawAttachProgram is a low level wrapper around BPF_PROG_ATTACH.\n//\n// You should use one of the higher level abstractions available in this\n// package if possible.\nfunc RawAttachProgram(opts RawAttachProgramOptions) error {\n\tif opts.Flags&anchorFlags != 0 {\n\t\treturn fmt.Errorf(\"disallowed flags: use Anchor to specify attach target\")\n\t}\n\n\tattr := sys.ProgAttachAttr{\n\t\tTargetFdOrIfindex: uint32(opts.Target),\n\t\tAttachBpfFd:       uint32(opts.Program.FD()),\n\t\tAttachType:        uint32(opts.Attach),\n\t\tAttachFlags:       uint32(opts.Flags),\n\t\tExpectedRevision:  opts.ExpectedRevision,\n\t}\n\n\tif opts.Anchor != nil {\n\t\tfdOrID, flags, err := opts.Anchor.anchor()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"attach program: %w\", err)\n\t\t}\n\n\t\tif flags == sys.BPF_F_REPLACE {\n\t\t\t// Ensure that replacing a program works on old kernels.\n\t\t\tattr.ReplaceBpfFd = fdOrID\n\t\t} else {\n\t\t\tattr.RelativeFdOrId = fdOrID\n\t\t\tattr.AttachFlags |= flags\n\t\t}\n\t}\n\n\tif err := sys.ProgAttach(&attr); err != nil {\n\t\tif haveFeatErr := haveProgAttach(); haveFeatErr != nil {\n\t\t\treturn haveFeatErr\n\t\t}\n\t\treturn fmt.Errorf(\"attach program: %w\", err)\n\t}\n\truntime.KeepAlive(opts.Program)\n\n\treturn nil\n}\n\ntype RawDetachProgramOptions RawAttachProgramOptions\n\n// RawDetachProgram is a low level wrapper around BPF_PROG_DETACH.\n//\n// You should use one of the higher level abstractions available in this\n// package if possible.\nfunc RawDetachProgram(opts RawDetachProgramOptions) error {\n\tif opts.Flags&anchorFlags != 0 {\n\t\treturn fmt.Errorf(\"disallowed flags: use Anchor to specify attach target\")\n\t}\n\n\tattr := sys.ProgDetachAttr{\n\t\tTargetFdOrIfindex: uint32(opts.Target),\n\t\tAttachBpfFd:       uint32(opts.Program.FD()),\n\t\tAttachType:        uint32(opts.Attach),\n\t\tExpectedRevision:  opts.ExpectedRevision,\n\t}\n\n\tif opts.Anchor != nil {\n\t\tfdOrID, flags, err := opts.Anchor.anchor()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"detach program: %w\", err)\n\t\t}\n\n\t\tattr.RelativeFdOrId = fdOrID\n\t\tattr.AttachFlags |= flags\n\t}\n\n\tif err := sys.ProgDetach(&attr); err != nil {\n\t\tif haveFeatErr := haveProgAttach(); haveFeatErr != nil {\n\t\t\treturn haveFeatErr\n\t\t}\n\t\treturn fmt.Errorf(\"can't detach program: %w\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "link/program_test.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestProgramAlter(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"4.13\", \"SkSKB type\")\n\n\tprog := mustLoadProgram(t, ebpf.SkSKB, 0, \"\")\n\n\tvar sockMap *ebpf.Map\n\tsockMap, err := ebpf.NewMap(&ebpf.MapSpec{\n\t\tType:       ebpf.MapType(15), // BPF_MAP_TYPE_SOCKMAP\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: 2,\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer sockMap.Close()\n\n\terr = RawAttachProgram(RawAttachProgramOptions{\n\t\tTarget:  sockMap.FD(),\n\t\tProgram: prog,\n\t\tAttach:  ebpf.AttachSkSKBStreamParser,\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\terr = RawDetachProgram(RawDetachProgramOptions{\n\t\tTarget:  sockMap.FD(),\n\t\tProgram: prog,\n\t\tAttach:  ebpf.AttachSkSKBStreamParser,\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestRawAttachProgramAnchor(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"6.6\", \"attach anchor\")\n\n\tiface, err := net.InterfaceByName(\"lo\")\n\tqt.Assert(t, qt.IsNil(err))\n\n\ta := mustLoadProgram(t, ebpf.SchedCLS, 0, \"\")\n\tinfo, err := a.Info()\n\tqt.Assert(t, qt.IsNil(err))\n\taID, _ := info.ID()\n\n\terr = RawAttachProgram(RawAttachProgramOptions{\n\t\tTarget:  iface.Index,\n\t\tProgram: a,\n\t\tAttach:  ebpf.AttachTCXIngress,\n\t})\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer RawDetachProgram(RawDetachProgramOptions{\n\t\tTarget:  iface.Index,\n\t\tProgram: a,\n\t\tAttach:  ebpf.AttachTCXIngress,\n\t})\n\n\tlink, err := AttachTCX(TCXOptions{\n\t\tInterface: iface.Index,\n\t\tProgram:   mustLoadProgram(t, ebpf.SchedCLS, 0, \"\"),\n\t\tAttach:    ebpf.AttachTCXIngress,\n\t})\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer link.Close()\n\n\tlinkInfo, err := link.Info()\n\tqt.Assert(t, qt.IsNil(err))\n\n\tb := mustLoadProgram(t, ebpf.SchedCLS, 0, \"\")\n\n\tfor _, anchor := range []Anchor{\n\t\tHead(),\n\t\tTail(),\n\t\tAfterProgram(a),\n\t\tAfterProgramByID(aID),\n\t\tAfterLink(link),\n\t\tAfterLinkByID(linkInfo.ID),\n\t} {\n\t\tt.Run(fmt.Sprintf(\"%T\", anchor), func(t *testing.T) {\n\t\t\terr := RawAttachProgram(RawAttachProgramOptions{\n\t\t\t\tTarget:  iface.Index,\n\t\t\t\tProgram: b,\n\t\t\t\tAttach:  ebpf.AttachTCXIngress,\n\t\t\t\tAnchor:  anchor,\n\t\t\t})\n\t\t\tqt.Assert(t, qt.IsNil(err))\n\n\t\t\t// Detach doesn't allow first or last anchor.\n\t\t\tif _, ok := anchor.(firstAnchor); ok {\n\t\t\t\tanchor = nil\n\t\t\t} else if _, ok := anchor.(lastAnchor); ok {\n\t\t\t\tanchor = nil\n\t\t\t}\n\n\t\t\terr = RawDetachProgram(RawDetachProgramOptions{\n\t\t\t\tTarget:  iface.Index,\n\t\t\t\tProgram: b,\n\t\t\t\tAttach:  ebpf.AttachTCXIngress,\n\t\t\t\tAnchor:  anchor,\n\t\t\t})\n\t\t\tqt.Assert(t, qt.IsNil(err))\n\t\t})\n\t}\n\n\t// Check that legacy replacement with a program works.\n\terr = RawAttachProgram(RawAttachProgramOptions{\n\t\tTarget:  iface.Index,\n\t\tProgram: b,\n\t\tAttach:  ebpf.AttachTCXIngress,\n\t\tAnchor:  ReplaceProgram(a),\n\t})\n\tqt.Assert(t, qt.IsNil(err))\n\n\terr = RawDetachProgram(RawDetachProgramOptions{\n\t\tTarget:  iface.Index,\n\t\tProgram: b,\n\t\tAttach:  ebpf.AttachTCXIngress,\n\t})\n\tqt.Assert(t, qt.IsNil(err))\n}\n"
  },
  {
    "path": "link/query.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"fmt\"\n\t\"slices\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n)\n\n// QueryOptions defines additional parameters when querying for programs.\ntype QueryOptions struct {\n\t// Target to query. This is usually a file descriptor but may refer to\n\t// something else based on the attach type.\n\tTarget int\n\t// Attach specifies the AttachType of the programs queried for\n\tAttach ebpf.AttachType\n\t// QueryFlags are flags for BPF_PROG_QUERY, e.g. BPF_F_QUERY_EFFECTIVE\n\tQueryFlags uint32\n}\n\n// QueryResult describes which programs and links are active.\ntype QueryResult struct {\n\t// List of attached programs.\n\tPrograms []AttachedProgram\n\n\t// Incremented by one every time the set of attached programs changes.\n\t// May be zero if not supported by the [ebpf.AttachType].\n\tRevision uint64\n}\n\n// HaveLinkInfo returns true if the kernel supports querying link information\n// for a particular [ebpf.AttachType].\nfunc (qr *QueryResult) HaveLinkInfo() bool {\n\treturn slices.ContainsFunc(qr.Programs,\n\t\tfunc(ap AttachedProgram) bool {\n\t\t\t_, ok := ap.LinkID()\n\t\t\treturn ok\n\t\t},\n\t)\n}\n\ntype AttachedProgram struct {\n\tID     ebpf.ProgramID\n\tlinkID ID\n}\n\n// LinkID returns the ID associated with the program.\n//\n// Returns 0, false if the kernel doesn't support retrieving the ID or if the\n// program wasn't attached via a link.\nfunc (ap *AttachedProgram) LinkID() (ID, bool) {\n\treturn ap.linkID, ap.linkID != 0\n}\n\n// QueryPrograms retrieves a list of programs for the given AttachType.\n//\n// Returns a slice of attached programs, which may be empty.\n// revision counts how many times the set of attached programs has changed and\n// may be zero if not supported by the [ebpf.AttachType].\n// Returns ErrNotSupportd on a kernel without BPF_PROG_QUERY\nfunc QueryPrograms(opts QueryOptions) (*QueryResult, error) {\n\t// query the number of programs to allocate correct slice size\n\tattr := sys.ProgQueryAttr{\n\t\tTargetFdOrIfindex: uint32(opts.Target),\n\t\tAttachType:        sys.AttachType(opts.Attach),\n\t\tQueryFlags:        opts.QueryFlags,\n\t}\n\terr := sys.ProgQuery(&attr)\n\tif err != nil {\n\t\tif haveFeatErr := haveProgQuery(); haveFeatErr != nil {\n\t\t\treturn nil, fmt.Errorf(\"query programs: %w\", haveFeatErr)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"query programs: %w\", err)\n\t}\n\tif attr.Count == 0 {\n\t\treturn &QueryResult{Revision: attr.Revision}, nil\n\t}\n\n\t// The minimum bpf_mprog revision is 1, so we can use the field to detect\n\t// whether the attach type supports link ids.\n\thaveLinkIDs := attr.Revision != 0\n\n\tcount := attr.Count\n\tprogIds := make([]ebpf.ProgramID, count)\n\tattr = sys.ProgQueryAttr{\n\t\tTargetFdOrIfindex: uint32(opts.Target),\n\t\tAttachType:        sys.AttachType(opts.Attach),\n\t\tQueryFlags:        opts.QueryFlags,\n\t\tCount:             count,\n\t\tProgIds:           sys.SlicePointer(progIds),\n\t}\n\n\tvar linkIds []ID\n\tif haveLinkIDs {\n\t\tlinkIds = make([]ID, count)\n\t\tattr.LinkIds = sys.SlicePointer(linkIds)\n\t}\n\n\tif err := sys.ProgQuery(&attr); err != nil {\n\t\treturn nil, fmt.Errorf(\"query programs: %w\", err)\n\t}\n\n\t// NB: attr.Count might have changed between the two syscalls.\n\tvar programs []AttachedProgram\n\tfor i, id := range progIds[:attr.Count] {\n\t\tap := AttachedProgram{ID: id}\n\t\tif haveLinkIDs {\n\t\t\tap.linkID = linkIds[i]\n\t\t}\n\t\tprograms = append(programs, ap)\n\t}\n\n\treturn &QueryResult{programs, attr.Revision}, nil\n}\n"
  },
  {
    "path": "link/query_test.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"os\"\n\t\"slices\"\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestQueryPrograms(t *testing.T) {\n\tfor name, fn := range map[string]func(*testing.T) (*ebpf.Program, Link, QueryOptions){\n\t\t\"cgroup\":      queryCgroupProgAttachFixtures,\n\t\t\"cgroup link\": queryCgroupLinkFixtures,\n\t\t\"netns\":       queryNetNSFixtures,\n\t\t\"tcx\":         queryTCXFixtures,\n\t} {\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tprog, link, opts := fn(t)\n\t\t\tresult, err := QueryPrograms(opts)\n\t\t\ttestutils.SkipIfNotSupported(t, err)\n\t\t\tqt.Assert(t, qt.IsNil(err))\n\n\t\t\tprogInfo, err := prog.Info()\n\t\t\tqt.Assert(t, qt.IsNil(err))\n\t\t\tprogID, _ := progInfo.ID()\n\n\t\t\ti := slices.IndexFunc(result.Programs, func(ap AttachedProgram) bool {\n\t\t\t\treturn ap.ID == progID\n\t\t\t})\n\t\t\tqt.Assert(t, qt.Not(qt.Equals(i, -1)))\n\n\t\t\tif name == \"tcx\" {\n\t\t\t\tqt.Assert(t, qt.Not(qt.Equals(result.Revision, 0)))\n\t\t\t}\n\n\t\t\tif result.HaveLinkInfo() {\n\t\t\t\tap := result.Programs[i]\n\t\t\t\tlinkInfo, err := link.Info()\n\t\t\t\tqt.Assert(t, qt.IsNil(err))\n\n\t\t\t\tlinkID, ok := ap.LinkID()\n\t\t\t\tqt.Assert(t, qt.IsTrue(ok))\n\t\t\t\tqt.Assert(t, qt.Equals(linkID, linkInfo.ID))\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc queryCgroupProgAttachFixtures(t *testing.T) (*ebpf.Program, Link, QueryOptions) {\n\tcgroup, prog := mustCgroupFixtures(t)\n\n\tlink, err := newProgAttachCgroup(cgroup, ebpf.AttachCGroupInetEgress, prog, flagAllowOverride)\n\tif err != nil {\n\t\tt.Fatal(\"Can't create link:\", err)\n\t}\n\tt.Cleanup(func() {\n\t\tqt.Assert(t, qt.IsNil(link.Close()))\n\t})\n\n\treturn prog, nil, QueryOptions{\n\t\tTarget: int(cgroup.Fd()),\n\t\tAttach: ebpf.AttachCGroupInetEgress,\n\t}\n}\n\nfunc queryCgroupLinkFixtures(t *testing.T) (*ebpf.Program, Link, QueryOptions) {\n\tcgroup, prog := mustCgroupFixtures(t)\n\n\tlink, err := newLinkCgroup(cgroup, ebpf.AttachCGroupInetEgress, prog)\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(\"Can't create link:\", err)\n\t}\n\tt.Cleanup(func() {\n\t\tqt.Assert(t, qt.IsNil(link.Close()))\n\t})\n\n\treturn prog, nil, QueryOptions{\n\t\tTarget: int(cgroup.Fd()),\n\t\tAttach: ebpf.AttachCGroupInetEgress,\n\t}\n}\n\nfunc queryNetNSFixtures(t *testing.T) (*ebpf.Program, Link, QueryOptions) {\n\ttestutils.SkipOnOldKernel(t, \"4.20\", \"flow_dissector program\")\n\n\tprog := mustLoadProgram(t, ebpf.FlowDissector, ebpf.AttachFlowDissector, \"\")\n\n\t// RawAttachProgramOptions.Target needs to be 0, as PROG_ATTACH with namespaces\n\t// only works with the threads current netns. Any other fd will be rejected.\n\tif err := RawAttachProgram(RawAttachProgramOptions{\n\t\tTarget:  0,\n\t\tProgram: prog,\n\t\tAttach:  ebpf.AttachFlowDissector,\n\t}); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tt.Cleanup(func() {\n\t\terr := RawDetachProgram(RawDetachProgramOptions{\n\t\t\tTarget:  0,\n\t\t\tProgram: prog,\n\t\t\tAttach:  ebpf.AttachFlowDissector,\n\t\t})\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t})\n\n\tnetns, err := os.Open(\"/proc/self/ns/net\")\n\tqt.Assert(t, qt.IsNil(err))\n\tt.Cleanup(func() { netns.Close() })\n\n\treturn prog, nil, QueryOptions{\n\t\tTarget: int(netns.Fd()),\n\t\tAttach: ebpf.AttachFlowDissector,\n\t}\n}\n\nfunc queryTCXFixtures(t *testing.T) (*ebpf.Program, Link, QueryOptions) {\n\ttestutils.SkipOnOldKernel(t, \"6.6\", \"TCX link\")\n\n\tprog := mustLoadProgram(t, ebpf.SchedCLS, ebpf.AttachTCXIngress, \"\")\n\n\tlink, iface := mustAttachTCX(t, prog, ebpf.AttachTCXIngress)\n\n\treturn prog, link, QueryOptions{\n\t\tTarget: iface,\n\t\tAttach: ebpf.AttachTCXIngress,\n\t}\n}\n"
  },
  {
    "path": "link/raw_tracepoint.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n)\n\ntype RawTracepointOptions struct {\n\t// Tracepoint name.\n\tName string\n\t// Program must be of type RawTracepoint*\n\tProgram *ebpf.Program\n}\n\n// AttachRawTracepoint links a BPF program to a raw_tracepoint.\n//\n// Requires at least Linux 4.17.\nfunc AttachRawTracepoint(opts RawTracepointOptions) (Link, error) {\n\tif t := opts.Program.Type(); t != ebpf.RawTracepoint && t != ebpf.RawTracepointWritable {\n\t\treturn nil, fmt.Errorf(\"invalid program type %s, expected RawTracepoint(Writable)\", t)\n\t}\n\tif opts.Program.FD() < 0 {\n\t\treturn nil, fmt.Errorf(\"invalid program: %w\", sys.ErrClosedFd)\n\t}\n\n\tfd, err := sys.RawTracepointOpen(&sys.RawTracepointOpenAttr{\n\t\tName:   sys.NewStringPointer(opts.Name),\n\t\tProgFd: uint32(opts.Program.FD()),\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\terr = haveBPFLink()\n\tif errors.Is(err, ErrNotSupported) {\n\t\t// Prior to commit 70ed506c3bbc (\"bpf: Introduce pinnable bpf_link abstraction\")\n\t\t// raw_tracepoints are just a plain fd.\n\t\treturn &simpleRawTracepoint{fd}, nil\n\t}\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &rawTracepoint{RawLink{fd: fd}}, nil\n}\n\ntype simpleRawTracepoint struct {\n\tfd *sys.FD\n}\n\nvar _ Link = (*simpleRawTracepoint)(nil)\n\nfunc (frt *simpleRawTracepoint) isLink() {}\n\nfunc (frt *simpleRawTracepoint) Close() error {\n\treturn frt.fd.Close()\n}\n\nfunc (frt *simpleRawTracepoint) Update(_ *ebpf.Program) error {\n\treturn fmt.Errorf(\"update raw_tracepoint: %w\", ErrNotSupported)\n}\n\nfunc (frt *simpleRawTracepoint) Pin(string) error {\n\treturn fmt.Errorf(\"pin raw_tracepoint: %w\", ErrNotSupported)\n}\n\nfunc (frt *simpleRawTracepoint) Unpin() error {\n\treturn fmt.Errorf(\"unpin raw_tracepoint: %w\", ErrNotSupported)\n}\n\nfunc (frt *simpleRawTracepoint) Detach() error {\n\treturn fmt.Errorf(\"detach raw_tracepoint: %w\", ErrNotSupported)\n}\n\nfunc (frt *simpleRawTracepoint) Info() (*Info, error) {\n\treturn nil, fmt.Errorf(\"can't get raw_tracepoint info: %w\", ErrNotSupported)\n}\n\ntype rawTracepoint struct {\n\tRawLink\n}\n\nvar _ Link = (*rawTracepoint)(nil)\n\nfunc (rt *rawTracepoint) Update(_ *ebpf.Program) error {\n\treturn fmt.Errorf(\"update raw_tracepoint: %w\", ErrNotSupported)\n}\n\nfunc (rt *rawTracepoint) Info() (*Info, error) {\n\tvar info sys.RawTracepointLinkInfo\n\tname, err := queryInfoWithString(rt.fd, &info, &info.TpName, &info.TpNameLen)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &Info{\n\t\tinfo.Type,\n\t\tinfo.Id,\n\t\tebpf.ProgramID(info.ProgId),\n\t\t&RawTracepointInfo{\n\t\t\tName: name,\n\t\t},\n\t}, nil\n}\n"
  },
  {
    "path": "link/raw_tracepoint_test.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc TestRawTracepoint(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"4.17\", \"BPF_RAW_TRACEPOINT API\")\n\n\tprog := mustLoadProgram(t, ebpf.RawTracepoint, 0, \"\")\n\n\tlink, err := AttachRawTracepoint(RawTracepointOptions{\n\t\tName:    \"cgroup_mkdir\",\n\t\tProgram: prog,\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\ttestLink(t, link, prog)\n}\n\nfunc TestRawTracepointInfo(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.8\", \"bpf_link_info_raw_tracepoint\")\n\n\tprog := mustLoadProgram(t, ebpf.RawTracepoint, 0, \"\")\n\n\tlink, err := AttachRawTracepoint(RawTracepointOptions{\n\t\tName:    \"cgroup_mkdir\",\n\t\tProgram: prog,\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer link.Close()\n\n\tinfo, err := link.Info()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tqt.Assert(t, qt.Equals(RawTracepointType, info.Type))\n\ttpInfo := info.RawTracepoint()\n\tqt.Assert(t, qt.Equals(tpInfo.Name, \"cgroup_mkdir\"))\n}\n\nfunc TestRawTracepoint_writable(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.2\", \"BPF_RAW_TRACEPOINT_WRITABLE API\")\n\n\tprog := mustLoadProgram(t, ebpf.RawTracepoint, 0, \"\")\n\n\tdefer prog.Close()\n\n\tlink, err := AttachRawTracepoint(RawTracepointOptions{\n\t\tName:    \"cgroup_rmdir\",\n\t\tProgram: prog,\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\ttestLink(t, link, prog)\n}\n"
  },
  {
    "path": "link/socket_filter.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"syscall\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\n// AttachSocketFilter attaches a SocketFilter BPF program to a socket.\nfunc AttachSocketFilter(conn syscall.Conn, program *ebpf.Program) error {\n\trawConn, err := conn.SyscallConn()\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar ssoErr error\n\terr = rawConn.Control(func(fd uintptr) {\n\t\tssoErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_ATTACH_BPF, program.FD())\n\t})\n\tif ssoErr != nil {\n\t\treturn ssoErr\n\t}\n\treturn err\n}\n\n// DetachSocketFilter detaches a SocketFilter BPF program from a socket.\nfunc DetachSocketFilter(conn syscall.Conn) error {\n\trawConn, err := conn.SyscallConn()\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar ssoErr error\n\terr = rawConn.Control(func(fd uintptr) {\n\t\tssoErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_DETACH_BPF, 0)\n\t})\n\tif ssoErr != nil {\n\t\treturn ssoErr\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "link/socket_filter_test.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"net\"\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\nfunc TestSocketFilterAttach(t *testing.T) {\n\tprog := mustLoadProgram(t, ebpf.SocketFilter, 0, \"\")\n\n\tdefer prog.Close()\n\n\tconn, err := net.ListenUDP(\"udp4\", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer conn.Close()\n\n\tif err := AttachSocketFilter(conn, prog); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif err := DetachSocketFilter(conn); err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n"
  },
  {
    "path": "link/struct_ops.go",
    "content": "package link\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n)\n\ntype structOpsLink struct {\n\tRawLink\n}\n\nfunc (*structOpsLink) Update(*ebpf.Program) error {\n\treturn fmt.Errorf(\"update struct_ops link: %w\", ErrNotSupported)\n}\n\ntype StructOpsOptions struct {\n\tMap *ebpf.Map\n}\n\n// AttachStructOps attaches a struct_ops map (created from a \".struct_ops.link\"\n// section) to its kernel subsystem via a BPF link.\nfunc AttachStructOps(opts StructOpsOptions) (Link, error) {\n\tm := opts.Map\n\n\tif m == nil {\n\t\treturn nil, fmt.Errorf(\"map cannot be nil\")\n\t}\n\n\tif t := m.Type(); t != ebpf.StructOpsMap {\n\t\treturn nil, fmt.Errorf(\"can't attach non-struct_ops map\")\n\t}\n\n\tmapFD := m.FD()\n\tif mapFD <= 0 {\n\t\treturn nil, fmt.Errorf(\"invalid map: %s\", sys.ErrClosedFd)\n\t}\n\n\tfd, err := sys.LinkCreate(&sys.LinkCreateAttr{\n\t\t// For struct_ops links, the mapFD must be passed as ProgFd.\n\t\tProgFd:     uint32(mapFD),\n\t\tAttachType: sys.AttachType(ebpf.AttachStructOps),\n\t\tTargetFd:   0,\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"attach StructOps: create link: %w\", err)\n\t}\n\n\treturn &structOpsLink{RawLink{fd: fd}}, nil\n}\n"
  },
  {
    "path": "link/struct_ops_test.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc TestStructOps(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"6.12\", \"bpf_testmod_ops\")\n\n\tm := mustStructOpsFixtures(t)\n\tl, err := AttachStructOps(StructOpsOptions{Map: m})\n\tqt.Assert(t, qt.IsNil(err))\n\n\ttestLink(t, l, nil)\n}\n\nfunc mustStructOpsFixtures(tb testing.TB) *ebpf.Map {\n\ttb.Helper()\n\n\ttestutils.SkipIfNotSupported(tb, haveBPFLink())\n\n\tuserData := []byte{\n\t\t// test_1 func ptr (8B)\n\t\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t\t// test_2 func ptr (8B)\n\t\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t\t// data (4B) + padding (4B)\n\t\t0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00,\n\t}\n\n\tspec := &ebpf.CollectionSpec{\n\t\tMaps: map[string]*ebpf.MapSpec{\n\t\t\t\"testmod_ops\": {\n\t\t\t\tName:       \"testmod_ops\",\n\t\t\t\tType:       ebpf.StructOpsMap,\n\t\t\t\tMaxEntries: 1,\n\t\t\t\tFlags:      sys.BPF_F_LINK,\n\t\t\t\tKey:        &btf.Int{Size: 4},\n\t\t\t\tKeySize:    4,\n\t\t\t\tValueSize:  24,\n\t\t\t\tValue: &btf.Struct{\n\t\t\t\t\tName: \"bpf_testmod_ops\",\n\t\t\t\t\tSize: 24,\n\t\t\t\t\tMembers: []btf.Member{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tName: \"test_1\",\n\t\t\t\t\t\t\tType: &btf.Pointer{\n\t\t\t\t\t\t\t\tTarget: &btf.FuncProto{\n\t\t\t\t\t\t\t\t\tParams: []btf.FuncParam{},\n\t\t\t\t\t\t\t\t\tReturn: &btf.Int{Name: \"int\", Size: 4, Encoding: btf.Signed}}},\n\t\t\t\t\t\t\tOffset: 0,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tName: \"test_2\",\n\t\t\t\t\t\t\tType: &btf.Pointer{\n\t\t\t\t\t\t\t\tTarget: &btf.FuncProto{\n\t\t\t\t\t\t\t\t\tParams: []btf.FuncParam{\n\t\t\t\t\t\t\t\t\t\t{Type: &btf.Int{Name: \"int\", Size: 4, Encoding: btf.Signed}},\n\t\t\t\t\t\t\t\t\t\t{Type: &btf.Int{Name: \"int\", Size: 4, Encoding: btf.Signed}},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tReturn: (*btf.Void)(nil),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tOffset: 64,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tName:   \"data\",\n\t\t\t\t\t\t\tType:   &btf.Int{Name: \"int\", Size: 4, Encoding: btf.Signed},\n\t\t\t\t\t\t\tOffset: 128, // bits\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tContents: []ebpf.MapKV{\n\t\t\t\t\t{\n\t\t\t\t\t\tKey:   uint32(0),\n\t\t\t\t\t\tValue: userData,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tPrograms: map[string]*ebpf.ProgramSpec{\n\t\t\t\"test_1\": {\n\t\t\t\tName:        \"test_1\",\n\t\t\t\tType:        ebpf.StructOps,\n\t\t\t\tAttachTo:    \"bpf_testmod_ops:test_1\",\n\t\t\t\tLicense:     \"GPL\",\n\t\t\t\tSectionName: \"struct_ops/test_1\",\n\t\t\t\tInstructions: asm.Instructions{\n\t\t\t\t\tasm.Mov.Imm(asm.R0, 0),\n\t\t\t\t\tasm.Return(),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tVariables: map[string]*ebpf.VariableSpec{},\n\t}\n\n\tcoll, err := ebpf.NewCollection(spec)\n\ttestutils.SkipIfNotSupported(tb, err)\n\tqt.Assert(tb, qt.IsNil(err))\n\ttb.Cleanup(func() {\n\t\tcoll.Close()\n\t})\n\n\tm := coll.Maps[\"testmod_ops\"]\n\tqt.Assert(tb, qt.IsNotNil(m))\n\n\treturn m\n}\n"
  },
  {
    "path": "link/syscalls.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"errors\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\nvar haveProgAttach = internal.NewFeatureTest(\"BPF_PROG_ATTACH\", func() error {\n\tprog, err := ebpf.NewProgram(&ebpf.ProgramSpec{\n\t\tType:    ebpf.CGroupSKB,\n\t\tLicense: \"MIT\",\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.Mov.Imm(asm.R0, 0),\n\t\t\tasm.Return(),\n\t\t},\n\t})\n\tif err != nil {\n\t\treturn internal.ErrNotSupported\n\t}\n\n\t// BPF_PROG_ATTACH was introduced at the same time as CGgroupSKB,\n\t// so being able to load the program is enough to infer that we\n\t// have the syscall.\n\tprog.Close()\n\treturn nil\n}, \"4.10\")\n\nvar haveProgAttachReplace = internal.NewFeatureTest(\"BPF_PROG_ATTACH atomic replacement of MULTI progs\", func() error {\n\tif err := haveProgAttach(); err != nil {\n\t\treturn err\n\t}\n\n\tprog, err := ebpf.NewProgram(&ebpf.ProgramSpec{\n\t\tType:       ebpf.CGroupSKB,\n\t\tAttachType: ebpf.AttachCGroupInetIngress,\n\t\tLicense:    \"MIT\",\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.Mov.Imm(asm.R0, 0),\n\t\t\tasm.Return(),\n\t\t},\n\t})\n\n\tif err != nil {\n\t\treturn internal.ErrNotSupported\n\t}\n\n\tdefer prog.Close()\n\n\t// We know that we have BPF_PROG_ATTACH since we can load CGroupSKB programs.\n\t// If passing BPF_F_REPLACE gives us EINVAL we know that the feature isn't\n\t// present.\n\tattr := sys.ProgAttachAttr{\n\t\t// We rely on this being checked after attachFlags.\n\t\tTargetFdOrIfindex: ^uint32(0),\n\t\tAttachBpfFd:       uint32(prog.FD()),\n\t\tAttachType:        uint32(ebpf.AttachCGroupInetIngress),\n\t\tAttachFlags:       uint32(flagReplace),\n\t}\n\n\terr = sys.ProgAttach(&attr)\n\tif errors.Is(err, unix.EINVAL) {\n\t\treturn internal.ErrNotSupported\n\t}\n\tif errors.Is(err, unix.EBADF) {\n\t\treturn nil\n\t}\n\treturn err\n}, \"5.5\")\n\nvar haveBPFLink = internal.NewFeatureTest(\"bpf_link\", func() error {\n\tattr := sys.LinkCreateAttr{\n\t\t// This is a hopefully invalid file descriptor, which triggers EBADF.\n\t\tTargetFd:   ^uint32(0),\n\t\tProgFd:     ^uint32(0),\n\t\tAttachType: sys.AttachType(ebpf.AttachCGroupInetIngress),\n\t}\n\t_, err := sys.LinkCreate(&attr)\n\tif errors.Is(err, unix.EINVAL) {\n\t\treturn internal.ErrNotSupported\n\t}\n\tif errors.Is(err, unix.EBADF) {\n\t\treturn nil\n\t}\n\treturn err\n}, \"5.7\")\n\nvar haveProgQuery = internal.NewFeatureTest(\"BPF_PROG_QUERY\", func() error {\n\tattr := sys.ProgQueryAttr{\n\t\t// We rely on this being checked during the syscall.\n\t\t// With an otherwise correct payload we expect EBADF here\n\t\t// as an indication that the feature is present.\n\t\tTargetFdOrIfindex: ^uint32(0),\n\t\tAttachType:        sys.AttachType(ebpf.AttachCGroupInetIngress),\n\t}\n\n\terr := sys.ProgQuery(&attr)\n\n\tif errors.Is(err, unix.EBADF) {\n\t\treturn nil\n\t}\n\tif err != nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn errors.New(\"syscall succeeded unexpectedly\")\n}, \"4.15\")\n\nvar haveTCX = internal.NewFeatureTest(\"tcx\", func() error {\n\tprog, err := ebpf.NewProgram(&ebpf.ProgramSpec{\n\t\tType:    ebpf.SchedCLS,\n\t\tLicense: \"MIT\",\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.Mov.Imm(asm.R0, 0),\n\t\t\tasm.Return(),\n\t\t},\n\t})\n\n\tif err != nil {\n\t\treturn internal.ErrNotSupported\n\t}\n\n\tdefer prog.Close()\n\tattr := sys.LinkCreateTcxAttr{\n\t\t// We rely on this being checked during the syscall.\n\t\t// With an otherwise correct payload we expect ENODEV here\n\t\t// as an indication that the feature is present.\n\t\tTargetIfindex: ^uint32(0),\n\t\tProgFd:        uint32(prog.FD()),\n\t\tAttachType:    sys.AttachType(ebpf.AttachTCXIngress),\n\t}\n\n\t_, err = sys.LinkCreateTcx(&attr)\n\n\tif errors.Is(err, unix.ENODEV) {\n\t\treturn nil\n\t}\n\tif err != nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn errors.New(\"syscall succeeded unexpectedly\")\n}, \"6.6\")\n\nvar haveNetkit = internal.NewFeatureTest(\"netkit\", func() error {\n\tprog, err := ebpf.NewProgram(&ebpf.ProgramSpec{\n\t\tType:    ebpf.SchedCLS,\n\t\tLicense: \"MIT\",\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.Mov.Imm(asm.R0, 0),\n\t\t\tasm.Return(),\n\t\t},\n\t})\n\n\tif err != nil {\n\t\treturn internal.ErrNotSupported\n\t}\n\n\tdefer prog.Close()\n\tattr := sys.LinkCreateNetkitAttr{\n\t\t// We rely on this being checked during the syscall.\n\t\t// With an otherwise correct payload we expect ENODEV here\n\t\t// as an indication that the feature is present.\n\t\tTargetIfindex: ^uint32(0),\n\t\tProgFd:        uint32(prog.FD()),\n\t\tAttachType:    sys.AttachType(ebpf.AttachNetkitPrimary),\n\t}\n\n\t_, err = sys.LinkCreateNetkit(&attr)\n\n\tif errors.Is(err, unix.ENODEV) {\n\t\treturn nil\n\t}\n\tif err != nil {\n\t\treturn ErrNotSupported\n\t}\n\treturn errors.New(\"syscall succeeded unexpectedly\")\n}, \"6.7\")\n"
  },
  {
    "path": "link/syscalls_test.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc TestHaveProgAttach(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, haveProgAttach)\n}\n\nfunc TestHaveProgAttachReplace(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, haveProgAttachReplace)\n}\n\nfunc TestHaveBPFLink(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, haveBPFLink)\n}\n\nfunc TestHaveProgQuery(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, haveProgQuery)\n}\n\nfunc TestHaveTCX(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, haveTCX)\n}\n\nfunc TestHaveNetkit(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, haveNetkit)\n}\n"
  },
  {
    "path": "link/tcx.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n)\n\ntype TCXOptions struct {\n\t// Index of the interface to attach to.\n\tInterface int\n\t// Program to attach.\n\tProgram *ebpf.Program\n\t// One of the AttachTCX* constants.\n\tAttach ebpf.AttachType\n\t// Attach relative to an anchor. Optional.\n\tAnchor Anchor\n\t// Only attach if the expected revision matches.\n\tExpectedRevision uint64\n\t// Flags control the attach behaviour. Specify an Anchor instead of\n\t// F_LINK, F_ID, F_BEFORE, F_AFTER and R_REPLACE. Optional.\n\tFlags uint32\n}\n\nfunc AttachTCX(opts TCXOptions) (Link, error) {\n\tif opts.Interface < 0 {\n\t\treturn nil, fmt.Errorf(\"interface %d is out of bounds\", opts.Interface)\n\t}\n\n\tif opts.Flags&anchorFlags != 0 {\n\t\treturn nil, fmt.Errorf(\"disallowed flags: use Anchor to specify attach target\")\n\t}\n\n\tattr := sys.LinkCreateTcxAttr{\n\t\tProgFd:           uint32(opts.Program.FD()),\n\t\tAttachType:       sys.AttachType(opts.Attach),\n\t\tTargetIfindex:    uint32(opts.Interface),\n\t\tExpectedRevision: opts.ExpectedRevision,\n\t\tFlags:            opts.Flags,\n\t}\n\n\tif opts.Anchor != nil {\n\t\tfdOrID, flags, err := opts.Anchor.anchor()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"attach tcx link: %w\", err)\n\t\t}\n\n\t\tattr.RelativeFdOrId = fdOrID\n\t\tattr.Flags |= flags\n\t}\n\n\tfd, err := sys.LinkCreateTcx(&attr)\n\truntime.KeepAlive(opts.Program)\n\truntime.KeepAlive(opts.Anchor)\n\tif err != nil {\n\t\tif haveFeatErr := haveTCX(); haveFeatErr != nil {\n\t\t\treturn nil, haveFeatErr\n\t\t}\n\t\treturn nil, fmt.Errorf(\"attach tcx link: %w\", err)\n\t}\n\n\treturn &tcxLink{RawLink{fd, \"\"}}, nil\n}\n\ntype tcxLink struct {\n\tRawLink\n}\n\nvar _ Link = (*tcxLink)(nil)\n\nfunc (tcx *tcxLink) Info() (*Info, error) {\n\tvar info sys.TcxLinkInfo\n\tif err := sys.ObjInfo(tcx.fd, &info); err != nil {\n\t\treturn nil, fmt.Errorf(\"tcx link info: %s\", err)\n\t}\n\textra := &TCXInfo{\n\t\tIfindex:    info.Ifindex,\n\t\tAttachType: info.AttachType,\n\t}\n\n\treturn &Info{\n\t\tinfo.Type,\n\t\tinfo.Id,\n\t\tebpf.ProgramID(info.ProgId),\n\t\textra,\n\t}, nil\n}\n"
  },
  {
    "path": "link/tcx_test.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"net\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\nfunc TestAttachTCX(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"6.6\", \"TCX link\")\n\n\tprog := mustLoadProgram(t, ebpf.SchedCLS, ebpf.AttachNone, \"\")\n\tlink, _ := mustAttachTCX(t, prog, ebpf.AttachTCXIngress)\n\n\ttestLink(t, link, prog)\n}\n\nfunc TestTCXAnchor(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"6.6\", \"TCX link\")\n\n\ta := mustLoadProgram(t, ebpf.SchedCLS, ebpf.AttachNone, \"\")\n\tb := mustLoadProgram(t, ebpf.SchedCLS, ebpf.AttachNone, \"\")\n\n\tlinkA, iface := mustAttachTCX(t, a, ebpf.AttachTCXEgress)\n\n\tprogramInfo, err := a.Info()\n\tqt.Assert(t, qt.IsNil(err))\n\tprogramID, _ := programInfo.ID()\n\n\tlinkInfo, err := linkA.Info()\n\tqt.Assert(t, qt.IsNil(err))\n\tlinkID := linkInfo.ID\n\n\tfor _, anchor := range []Anchor{\n\t\tHead(),\n\t\tTail(),\n\t\tBeforeProgram(a),\n\t\tBeforeProgramByID(programID),\n\t\tAfterLink(linkA),\n\t\tAfterLinkByID(linkID),\n\t} {\n\t\tt.Run(fmt.Sprintf(\"%T\", anchor), func(t *testing.T) {\n\t\t\tlinkB, err := AttachTCX(TCXOptions{\n\t\t\t\tProgram:   b,\n\t\t\t\tAttach:    ebpf.AttachTCXEgress,\n\t\t\t\tInterface: iface,\n\t\t\t\tAnchor:    anchor,\n\t\t\t})\n\t\t\tqt.Assert(t, qt.IsNil(err))\n\t\t\tqt.Assert(t, qt.IsNil(linkB.Close()))\n\t\t})\n\t}\n}\n\nfunc TestTCXExpectedRevision(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"6.6\", \"TCX link\")\n\n\tiface, err := net.InterfaceByName(\"lo\")\n\tqt.Assert(t, qt.IsNil(err))\n\n\t_, err = AttachTCX(TCXOptions{\n\t\tProgram:          mustLoadProgram(t, ebpf.SchedCLS, ebpf.AttachNone, \"\"),\n\t\tAttach:           ebpf.AttachTCXEgress,\n\t\tInterface:        iface.Index,\n\t\tExpectedRevision: math.MaxUint64,\n\t})\n\tqt.Assert(t, qt.ErrorIs(err, unix.ESTALE))\n}\n\nfunc mustAttachTCX(tb testing.TB, prog *ebpf.Program, attachType ebpf.AttachType) (Link, int) {\n\tiface, err := net.InterfaceByName(\"lo\")\n\tqt.Assert(tb, qt.IsNil(err))\n\n\tlink, err := AttachTCX(TCXOptions{\n\t\tProgram:   prog,\n\t\tAttach:    attachType,\n\t\tInterface: iface.Index,\n\t})\n\tqt.Assert(tb, qt.IsNil(err))\n\ttb.Cleanup(func() { qt.Assert(tb, qt.IsNil(link.Close())) })\n\n\treturn link, iface.Index\n}\n"
  },
  {
    "path": "link/tracepoint.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/tracefs\"\n)\n\n// TracepointOptions defines additional parameters that will be used\n// when loading Tracepoints.\ntype TracepointOptions struct {\n\t// Arbitrary value that can be fetched from an eBPF program\n\t// via `bpf_get_attach_cookie()`.\n\t//\n\t// Needs kernel 5.15+.\n\tCookie uint64\n}\n\n// Tracepoint attaches the given eBPF program to the tracepoint with the given\n// group and name. See /sys/kernel/tracing/events to find available\n// tracepoints. The top-level directory is the group, the event's subdirectory\n// is the name. Example:\n//\n//\ttp, err := Tracepoint(\"syscalls\", \"sys_enter_fork\", prog, nil)\n//\n// Losing the reference to the resulting Link (tp) will close the Tracepoint\n// and prevent further execution of prog. The Link must be Closed during\n// program shutdown to avoid leaking system resources.\n//\n// Note that attaching eBPF programs to syscalls (sys_enter_*/sys_exit_*) is\n// only possible as of kernel 4.14 (commit cf5f5ce).\n//\n// The returned Link may implement [PerfEvent].\nfunc Tracepoint(group, name string, prog *ebpf.Program, opts *TracepointOptions) (Link, error) {\n\tif group == \"\" || name == \"\" {\n\t\treturn nil, fmt.Errorf(\"group and name cannot be empty: %w\", errInvalidInput)\n\t}\n\tif prog == nil {\n\t\treturn nil, fmt.Errorf(\"prog cannot be nil: %w\", errInvalidInput)\n\t}\n\tif prog.Type() != ebpf.TracePoint {\n\t\treturn nil, fmt.Errorf(\"eBPF program type %s is not a Tracepoint: %w\", prog.Type(), errInvalidInput)\n\t}\n\n\ttid, err := tracefs.EventID(group, name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfd, err := openTracepointPerfEvent(tid, perfAllThreads)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar cookie uint64\n\tif opts != nil {\n\t\tcookie = opts.Cookie\n\t}\n\n\tpe := newPerfEvent(fd, nil)\n\n\tlnk, err := attachPerfEvent(pe, prog, cookie)\n\tif err != nil {\n\t\tpe.Close()\n\t\treturn nil, err\n\t}\n\n\treturn lnk, nil\n}\n"
  },
  {
    "path": "link/tracepoint_test.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"errors\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestTracepoint(t *testing.T) {\n\t// Requires at least 4.7 (98b5c2c65c29 \"perf, bpf: allow bpf programs attach to tracepoints\")\n\ttestutils.SkipOnOldKernel(t, \"4.7\", \"tracepoint support\")\n\n\tprog := mustLoadProgram(t, ebpf.TracePoint, 0, \"\")\n\n\t// printk is guaranteed to be present.\n\t// Kernels before 4.14 don't support attaching to syscall tracepoints.\n\ttp, err := Tracepoint(\"printk\", \"console\", prog, nil)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif err := tp.Close(); err != nil {\n\t\tt.Error(\"closing tracepoint:\", err)\n\t}\n}\n\nfunc TestTracepointMissing(t *testing.T) {\n\t// Requires at least 4.7 (98b5c2c65c29 \"perf, bpf: allow bpf programs attach to tracepoints\")\n\ttestutils.SkipOnOldKernel(t, \"4.7\", \"tracepoint support\")\n\n\tprog := mustLoadProgram(t, ebpf.TracePoint, 0, \"\")\n\n\t_, err := Tracepoint(\"missing\", \"foobazbar\", prog, nil)\n\tif !errors.Is(err, os.ErrNotExist) {\n\t\tt.Error(\"Expected os.ErrNotExist, got\", err)\n\t}\n}\n\nfunc TestTracepointErrors(t *testing.T) {\n\t// Invalid Tracepoint incantations.\n\t_, err := Tracepoint(\"\", \"\", nil, nil) // empty names\n\tqt.Assert(t, qt.ErrorIs(err, errInvalidInput))\n\n\t_, err = Tracepoint(\"_\", \"_\", nil, nil) // empty prog\n\tqt.Assert(t, qt.ErrorIs(err, errInvalidInput))\n\n\t_, err = Tracepoint(\".\", \"+\", &ebpf.Program{}, nil) // illegal chars in group/name\n\tqt.Assert(t, qt.ErrorIs(err, errInvalidInput))\n\n\t_, err = Tracepoint(\"foo\", \"bar\", &ebpf.Program{}, nil) // wrong prog type\n\tqt.Assert(t, qt.ErrorIs(err, errInvalidInput))\n}\n\nfunc TestTracepointProgramCall(t *testing.T) {\n\t// Kernels before 4.14 don't support attaching to syscall tracepoints.\n\ttestutils.SkipOnOldKernel(t, \"4.14\", \"syscalls tracepoint support\")\n\n\tm, p := newUpdaterMapProg(t, ebpf.TracePoint, 0)\n\n\t// Open Tracepoint at /sys/kernel/tracing/events/syscalls/sys_enter_getpid\n\t// and attach it to the ebpf program created above.\n\ttp, err := Tracepoint(\"syscalls\", \"sys_enter_getpid\", p, nil)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// Trigger ebpf program call.\n\tunix.Getpid()\n\n\t// Assert that the value got incremented to at least 1, while allowing\n\t// for bigger values, because we could race with other getpid callers.\n\tassertMapValueGE(t, m, 0, 1)\n\n\t// Detach the Tracepoint.\n\tif err := tp.Close(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// Reset map value to 0 at index 0.\n\tif err := m.Update(uint32(0), uint32(0), ebpf.UpdateExist); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// Retrigger the ebpf program call.\n\tunix.Getpid()\n\n\t// Assert that this time the value has not been updated.\n\tassertMapValue(t, m, 0, 0)\n}\n\nfunc TestTracepointInfo(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"6.6\", \"bpf_link_info_perf_event\")\n\n\tprog := mustLoadProgram(t, ebpf.TracePoint, 0, \"\")\n\n\t// printk is guaranteed to be present.\n\t// Kernels before 4.14 don't support attaching to syscall tracepoints.\n\ttp, err := Tracepoint(\"printk\", \"console\", prog, nil)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer tp.Close()\n\n\tinfo, err := tp.Info()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\ttpInfo := info.PerfEvent().Tracepoint()\n\tqt.Assert(t, qt.Equals(tpInfo.Tracepoint, \"console\"))\n}\n"
  },
  {
    "path": "link/tracing.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\ntype tracing struct {\n\tRawLink\n}\n\nfunc (f *tracing) Update(_ *ebpf.Program) error {\n\treturn fmt.Errorf(\"tracing update: %w\", ErrNotSupported)\n}\n\nfunc (f *tracing) Info() (*Info, error) {\n\tvar info sys.TracingLinkInfo\n\tif err := sys.ObjInfo(f.fd, &info); err != nil {\n\t\treturn nil, fmt.Errorf(\"tracing link info: %s\", err)\n\t}\n\textra := &TracingInfo{\n\t\tTargetObjectId: info.TargetObjId,\n\t\tTargetBtfId:    info.TargetBtfId,\n\t\tAttachType:     info.AttachType,\n\t}\n\n\treturn &Info{\n\t\tinfo.Type,\n\t\tinfo.Id,\n\t\tebpf.ProgramID(info.ProgId),\n\t\textra,\n\t}, nil\n}\n\n// AttachFreplace attaches the given eBPF program to the function it replaces.\n//\n// The program and name can either be provided at link time, or can be provided\n// at program load time. If they were provided at load time, they should be nil\n// and empty respectively here, as they will be ignored by the kernel.\n// Examples:\n//\n//\tAttachFreplace(dispatcher, \"function\", replacement)\n//\tAttachFreplace(nil, \"\", replacement)\nfunc AttachFreplace(targetProg *ebpf.Program, name string, prog *ebpf.Program) (Link, error) {\n\tif (name == \"\") != (targetProg == nil) {\n\t\treturn nil, fmt.Errorf(\"must provide both or neither of name and targetProg: %w\", errInvalidInput)\n\t}\n\tif prog == nil {\n\t\treturn nil, fmt.Errorf(\"prog cannot be nil: %w\", errInvalidInput)\n\t}\n\tif prog.Type() != ebpf.Extension {\n\t\treturn nil, fmt.Errorf(\"eBPF program type %s is not an Extension: %w\", prog.Type(), errInvalidInput)\n\t}\n\n\tvar (\n\t\ttarget int\n\t\ttypeID btf.TypeID\n\t)\n\tif targetProg != nil {\n\t\tbtfHandle, err := targetProg.Handle()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdefer btfHandle.Close()\n\n\t\tspec, err := btfHandle.Spec(nil)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tvar function *btf.Func\n\t\tif err := spec.TypeByName(name, &function); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\ttarget = targetProg.FD()\n\t\ttypeID, err = spec.TypeID(function)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tlink, err := AttachRawLink(RawLinkOptions{\n\t\tTarget:  target,\n\t\tProgram: prog,\n\t\tAttach:  ebpf.AttachNone,\n\t\tBTF:     typeID,\n\t})\n\tif errors.Is(err, sys.ENOTSUPP) {\n\t\t// This may be returned by bpf_tracing_prog_attach via bpf_arch_text_poke.\n\t\treturn nil, fmt.Errorf(\"create raw tracepoint: %w\", ErrNotSupported)\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &tracing{*link}, nil\n}\n\ntype TracingOptions struct {\n\t// Program must be of type Tracing with attach type\n\t// AttachTraceFEntry/AttachTraceFExit/AttachModifyReturn or\n\t// AttachTraceRawTp.\n\tProgram *ebpf.Program\n\t// Program attach type. Can be one of:\n\t// \t- AttachTraceFEntry\n\t// \t- AttachTraceFExit\n\t// \t- AttachModifyReturn\n\t// \t- AttachTraceRawTp\n\t// This field is optional.\n\tAttachType ebpf.AttachType\n\t// Arbitrary value that can be fetched from an eBPF program\n\t// via `bpf_get_attach_cookie()`.\n\tCookie uint64\n}\n\ntype LSMOptions struct {\n\t// Program must be of type LSM with attach type\n\t// AttachLSMMac.\n\tProgram *ebpf.Program\n\t// Arbitrary value that can be fetched from an eBPF program\n\t// via `bpf_get_attach_cookie()`.\n\tCookie uint64\n}\n\n// attachBTFID links all BPF program types (Tracing/LSM) that they attach to a btf_id.\nfunc attachBTFID(program *ebpf.Program, at ebpf.AttachType, cookie uint64) (Link, error) {\n\tif program.FD() < 0 {\n\t\treturn nil, fmt.Errorf(\"invalid program %w\", sys.ErrClosedFd)\n\t}\n\n\tvar (\n\t\tfd  *sys.FD\n\t\terr error\n\t)\n\tswitch at {\n\tcase ebpf.AttachTraceFEntry, ebpf.AttachTraceFExit, ebpf.AttachTraceRawTp,\n\t\tebpf.AttachModifyReturn, ebpf.AttachLSMMac:\n\t\t// Attach via BPF link\n\t\tfd, err = sys.LinkCreateTracing(&sys.LinkCreateTracingAttr{\n\t\t\tProgFd:     uint32(program.FD()),\n\t\t\tAttachType: sys.AttachType(at),\n\t\t\tCookie:     cookie,\n\t\t})\n\t\tif err == nil {\n\t\t\tbreak\n\t\t}\n\t\tif !errors.Is(err, unix.EINVAL) && !errors.Is(err, sys.ENOTSUPP) {\n\t\t\treturn nil, fmt.Errorf(\"create tracing link: %w\", err)\n\t\t}\n\t\tfallthrough\n\tcase ebpf.AttachNone:\n\t\t// Attach via RawTracepointOpen\n\t\tif cookie > 0 {\n\t\t\treturn nil, fmt.Errorf(\"create raw tracepoint with cookie: %w\", ErrNotSupported)\n\t\t}\n\n\t\tfd, err = sys.RawTracepointOpen(&sys.RawTracepointOpenAttr{\n\t\t\tProgFd: uint32(program.FD()),\n\t\t})\n\t\tif errors.Is(err, sys.ENOTSUPP) {\n\t\t\t// This may be returned by bpf_tracing_prog_attach via bpf_arch_text_poke.\n\t\t\treturn nil, fmt.Errorf(\"create raw tracepoint: %w\", ErrNotSupported)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"create raw tracepoint: %w\", err)\n\t\t}\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"invalid attach type: %s\", at.String())\n\t}\n\n\traw := RawLink{fd: fd}\n\tinfo, err := raw.Info()\n\tif err != nil {\n\t\traw.Close()\n\t\treturn nil, err\n\t}\n\n\tif info.Type == RawTracepointType {\n\t\t// Sadness upon sadness: a Tracing program with AttachRawTp returns\n\t\t// a raw_tracepoint link. Other types return a tracing link.\n\t\treturn &rawTracepoint{raw}, nil\n\t}\n\treturn &tracing{raw}, nil\n}\n\n// AttachTracing links a tracing (fentry/fexit/fmod_ret) BPF program or\n// a BTF-powered raw tracepoint (tp_btf) BPF Program to a BPF hook defined\n// in kernel modules.\nfunc AttachTracing(opts TracingOptions) (Link, error) {\n\tif t := opts.Program.Type(); t != ebpf.Tracing {\n\t\treturn nil, fmt.Errorf(\"invalid program type %s, expected Tracing\", t)\n\t}\n\n\tswitch opts.AttachType {\n\tcase ebpf.AttachTraceFEntry, ebpf.AttachTraceFExit, ebpf.AttachModifyReturn,\n\t\tebpf.AttachTraceRawTp, ebpf.AttachNone:\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"invalid attach type: %s\", opts.AttachType.String())\n\t}\n\n\treturn attachBTFID(opts.Program, opts.AttachType, opts.Cookie)\n}\n\n// AttachLSM links a Linux security module (LSM) BPF Program to a BPF\n// hook defined in kernel modules.\nfunc AttachLSM(opts LSMOptions) (Link, error) {\n\tif t := opts.Program.Type(); t != ebpf.LSM {\n\t\treturn nil, fmt.Errorf(\"invalid program type %s, expected LSM\", t)\n\t}\n\n\treturn attachBTFID(opts.Program, ebpf.AttachLSMMac, opts.Cookie)\n}\n"
  },
  {
    "path": "link/tracing_test.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc TestFreplace(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.10\", \"freplace\")\n\n\tfile := testutils.NativeFile(t, \"../testdata/freplace-%s.elf\")\n\tspec, err := ebpf.LoadCollectionSpec(file)\n\tif err != nil {\n\t\tt.Fatal(\"Can't parse ELF:\", err)\n\t}\n\n\ttarget, err := ebpf.NewProgram(spec.Programs[\"sched_process_exec\"])\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(\"Can't create target program:\", err)\n\t}\n\tdefer target.Close()\n\n\t// Test attachment specified at load time\n\tspec.Programs[\"replacement\"].AttachTarget = target\n\treplacement, err := ebpf.NewProgram(spec.Programs[\"replacement\"])\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(\"Can't create replacement program:\", err)\n\t}\n\tdefer replacement.Close()\n\n\tfreplace, err := AttachFreplace(nil, \"\", replacement)\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(\"Can't create freplace:\", err)\n\t}\n\n\ttestLink(t, freplace, replacement)\n}\n\nfunc TestFentryFexit(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.5\", \"fentry\")\n\n\tspec, err := ebpf.LoadCollectionSpec(testutils.NativeFile(t, \"../testdata/fentry_fexit-%s.elf\"))\n\tif err != nil {\n\t\tt.Fatal(\"Can't parse ELF:\", err)\n\t}\n\n\ttarget, err := ebpf.NewProgram(spec.Programs[\"target\"])\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(\"Can't create target program:\", err)\n\t}\n\tdefer target.Close()\n\n\tfor _, name := range []string{\"trace_on_entry\", \"trace_on_exit\"} {\n\t\tprogSpec := spec.Programs[name]\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tprogSpec.AttachTarget = target\n\n\t\t\tprog, err := ebpf.NewProgram(progSpec)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tdefer prog.Close()\n\n\t\t\tt.Run(\"link\", func(t *testing.T) {\n\t\t\t\ttestutils.SkipOnOldKernel(t, \"5.11\", \"BPF_LINK_TYPE_TRACING\")\n\n\t\t\t\ttracingLink, err := AttachTracing(TracingOptions{\n\t\t\t\t\tProgram: prog,\n\t\t\t\t})\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatal(\"Can't attach tracing:\", err)\n\t\t\t\t}\n\t\t\t\tdefer tracingLink.Close()\n\n\t\t\t\ttestLink(t, tracingLink, prog)\n\t\t\t})\n\n\t\t})\n\t}\n}\n\nfunc TestTracing(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.11\", \"BPF_LINK_TYPE_TRACING\")\n\n\ttests := []struct {\n\t\tname                             string\n\t\tattachTo                         string\n\t\tprogramType                      ebpf.ProgramType\n\t\tprogramAttachType, attachTypeOpt ebpf.AttachType\n\t\tcookie                           uint64\n\t}{\n\t\t{\n\t\t\tname:              \"AttachTraceFEntry\",\n\t\t\tattachTo:          \"inet_dgram_connect\",\n\t\t\tprogramType:       ebpf.Tracing,\n\t\t\tprogramAttachType: ebpf.AttachTraceFEntry,\n\t\t},\n\t\t{\n\t\t\tname:              \"AttachTraceFEntry\",\n\t\t\tattachTo:          \"inet_dgram_connect\",\n\t\t\tprogramType:       ebpf.Tracing,\n\t\t\tprogramAttachType: ebpf.AttachTraceFEntry,\n\t\t\tattachTypeOpt:     ebpf.AttachTraceFEntry,\n\t\t\tcookie:            1,\n\t\t},\n\t\t{\n\t\t\tname:              \"AttachTraceFEntry\",\n\t\t\tattachTo:          \"inet_dgram_connect\",\n\t\t\tprogramType:       ebpf.Tracing,\n\t\t\tprogramAttachType: ebpf.AttachTraceFEntry,\n\t\t},\n\t\t{\n\t\t\tname:              \"AttachTraceFExit\",\n\t\t\tattachTo:          \"inet_dgram_connect\",\n\t\t\tprogramType:       ebpf.Tracing,\n\t\t\tprogramAttachType: ebpf.AttachTraceFExit,\n\t\t},\n\t\t{\n\t\t\tname:              \"AttachModifyReturn\",\n\t\t\tattachTo:          \"bpf_modify_return_test\",\n\t\t\tprogramType:       ebpf.Tracing,\n\t\t\tprogramAttachType: ebpf.AttachModifyReturn,\n\t\t},\n\t\t{\n\t\t\tname:              \"AttachTraceRawTp\",\n\t\t\tattachTo:          \"kfree_skb\",\n\t\t\tprogramType:       ebpf.Tracing,\n\t\t\tprogramAttachType: ebpf.AttachTraceRawTp,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tprog := mustLoadProgram(t, tt.programType, tt.programAttachType, tt.attachTo)\n\n\t\t\topts := TracingOptions{Program: prog, AttachType: tt.attachTypeOpt, Cookie: tt.cookie}\n\t\t\tlink, err := AttachTracing(opts)\n\t\t\ttestutils.SkipIfNotSupported(t, err)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\ttestLink(t, link, prog)\n\t\t\tif err = link.Close(); err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestLSM(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.11\", \"BPF_LINK_TYPE_TRACING\")\n\n\tprog := mustLoadProgram(t, ebpf.LSM, ebpf.AttachLSMMac, \"file_mprotect\")\n\n\tlink, err := AttachLSM(LSMOptions{Program: prog})\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\ttestLink(t, link, prog)\n}\n"
  },
  {
    "path": "link/uprobe.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"debug/elf\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"sync\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/tracefs\"\n)\n\nvar (\n\tuprobeRefCtrOffsetPMUPath = \"/sys/bus/event_source/devices/uprobe/format/ref_ctr_offset\"\n\t// elixir.bootlin.com/linux/v5.15-rc7/source/kernel/events/core.c#L9799\n\tuprobeRefCtrOffsetShift = 32\n\thaveRefCtrOffsetPMU     = internal.NewFeatureTest(\"RefCtrOffsetPMU\", func() error {\n\t\t_, err := os.Stat(uprobeRefCtrOffsetPMUPath)\n\t\tif errors.Is(err, os.ErrNotExist) {\n\t\t\treturn internal.ErrNotSupported\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}, \"4.20\")\n\n\t// ErrNoSymbol indicates that the given symbol was not found\n\t// in the ELF symbols table.\n\tErrNoSymbol = errors.New(\"not found\")\n)\n\nconst permExec fs.FileMode = 0111\n\n// Executable defines an executable program on the filesystem.\ntype Executable struct {\n\t// Path of the executable on the filesystem.\n\tpath string\n\t// Cached symbol values for all ELF and dynamic symbols.\n\t// Before using this, lazyLoadSymbols() must be called.\n\tcachedSymbols map[string]symbol\n\t// cachedSymbolsOnce tracks the lazy load of cachedSymbols.\n\tcachedSymbolsOnce sync.Once\n}\n\ntype symbol struct {\n\taddr uint64\n\tsize uint64\n}\n\n// contains returns true if the given address falls within the range\n// covered by the symbol.\nfunc (s symbol) contains(address uint64) bool {\n\treturn s.addr <= address && address < s.addr+s.size\n}\n\n// UprobeOptions defines additional parameters that will be used\n// when loading Uprobes.\ntype UprobeOptions struct {\n\t// Symbol address. Must be provided in case of external symbols (shared libs).\n\t// If set, overrides the address eventually parsed from the executable.\n\tAddress uint64\n\t// The offset relative to given symbol. Useful when tracing an arbitrary point\n\t// inside the frame of given symbol.\n\t//\n\t// Note: this field changed from being an absolute offset to being relative\n\t// to Address.\n\tOffset uint64\n\t// Only set the uprobe on the given process ID. Useful when tracing\n\t// shared library calls or programs that have many running instances.\n\tPID int\n\t// Automatically manage SDT reference counts (semaphores).\n\t//\n\t// If this field is set, the Kernel will increment/decrement the\n\t// semaphore located in the process memory at the provided address on\n\t// probe attach/detach.\n\t//\n\t// See also:\n\t// sourceware.org/systemtap/wiki/UserSpaceProbeImplementation (Semaphore Handling)\n\t// github.com/torvalds/linux/commit/1cc33161a83d\n\t// github.com/torvalds/linux/commit/a6ca88b241d5\n\tRefCtrOffset uint64\n\t// Arbitrary value that can be fetched from an eBPF program\n\t// via `bpf_get_attach_cookie()`.\n\t//\n\t// Needs kernel 5.15+.\n\tCookie uint64\n\t// Prefix used for the event name if the uprobe must be attached using tracefs.\n\t// The group name will be formatted as `<prefix>_<randomstr>`.\n\t// The default empty string is equivalent to \"ebpf\" as the prefix.\n\tTraceFSPrefix string\n}\n\nfunc (uo *UprobeOptions) cookie() uint64 {\n\tif uo == nil {\n\t\treturn 0\n\t}\n\treturn uo.Cookie\n}\n\n// To open a new Executable, use:\n//\n//\tOpenExecutable(\"/bin/bash\")\n//\n// The returned value can then be used to open Uprobe(s).\nfunc OpenExecutable(path string) (*Executable, error) {\n\tif path == \"\" {\n\t\treturn nil, fmt.Errorf(\"path cannot be empty\")\n\t}\n\n\tinfo, err := os.Stat(path)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"stat executable: %w\", err)\n\t}\n\n\tif info.Mode()&permExec == 0 {\n\t\treturn nil, fmt.Errorf(\"file %s is not executable\", path)\n\t}\n\n\treturn &Executable{\n\t\tpath:          path,\n\t\tcachedSymbols: make(map[string]symbol),\n\t}, nil\n}\n\nfunc (ex *Executable) load(f *internal.SafeELFFile) error {\n\tsyms, err := f.Symbols()\n\tif err != nil && !errors.Is(err, elf.ErrNoSymbols) {\n\t\treturn err\n\t}\n\n\tdynsyms, err := f.DynamicSymbols()\n\tif err != nil && !errors.Is(err, elf.ErrNoSymbols) {\n\t\treturn err\n\t}\n\n\tsyms = append(syms, dynsyms...)\n\n\tfor _, s := range syms {\n\t\tif elf.ST_TYPE(s.Info) != elf.STT_FUNC {\n\t\t\t// Symbol not associated with a function or other executable code.\n\t\t\tcontinue\n\t\t}\n\n\t\taddress := s.Value\n\n\t\t// Loop over ELF segments.\n\t\tfor _, prog := range f.Progs {\n\t\t\t// Skip uninteresting segments.\n\t\t\tif prog.Type != elf.PT_LOAD || (prog.Flags&elf.PF_X) == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif prog.Vaddr <= s.Value && s.Value < (prog.Vaddr+prog.Memsz) {\n\t\t\t\t// If the symbol value is contained in the segment, calculate\n\t\t\t\t// the symbol offset.\n\t\t\t\t//\n\t\t\t\t// fn symbol offset = fn symbol VA - .text VA + .text offset\n\t\t\t\t//\n\t\t\t\t// stackoverflow.com/a/40249502\n\t\t\t\taddress = s.Value - prog.Vaddr + prog.Off\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tex.cachedSymbols[s.Name] = symbol{\n\t\t\taddr: address,\n\t\t\tsize: s.Size,\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (ex *Executable) lazyLoadSymbols() error {\n\tvar err error\n\tex.cachedSymbolsOnce.Do(func() {\n\t\tvar f *internal.SafeELFFile\n\t\tf, err = internal.OpenSafeELFFile(ex.path)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"parse ELF file: %w\", err)\n\t\t\treturn\n\t\t}\n\t\tdefer f.Close()\n\n\t\tif f.Type != elf.ET_EXEC && f.Type != elf.ET_DYN {\n\t\t\t// ELF is not an executable or a shared object.\n\t\t\terr = errors.New(\"the given file is not an executable or a shared object\")\n\t\t\treturn\n\t\t}\n\t\terr = ex.load(f)\n\t})\n\treturn err\n}\n\n// address calculates the address of a symbol in the executable.\n//\n// opts must not be nil.\nfunc (ex *Executable) address(symbol string, address, offset uint64) (uint64, error) {\n\tif address > 0 {\n\t\treturn address + offset, nil\n\t}\n\n\terr := ex.lazyLoadSymbols()\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"lazy load symbols: %w\", err)\n\t}\n\n\tsym, ok := ex.cachedSymbols[symbol]\n\tif !ok {\n\t\treturn 0, fmt.Errorf(\"symbol %s: %w\", symbol, ErrNoSymbol)\n\t}\n\n\t// Symbols with location 0 from section undef are shared library calls and\n\t// are relocated before the binary is executed. Dynamic linking is not\n\t// implemented by the library, so mark this as unsupported for now.\n\t//\n\t// Since only offset values are stored and not elf.Symbol, if the value is 0,\n\t// assume it's an external symbol.\n\tif sym.addr == 0 {\n\t\treturn 0, fmt.Errorf(\"cannot resolve %s library call '%s': %w \"+\n\t\t\t\"(consider providing UprobeOptions.Address)\", ex.path, symbol, ErrNotSupported)\n\t}\n\n\tif offset >= sym.size {\n\t\treturn 0, fmt.Errorf(\"offset %d is out of range of symbol %s\", offset, symbol)\n\t}\n\n\treturn sym.addr + offset, nil\n}\n\n// SymbolOffset represents an offset within a symbol within a binary.\ntype SymbolOffset struct {\n\tSymbol string\n\tOffset uint64\n}\n\n// Symbol returns the SymbolOffset that the given address points to.\n// This includes the symbol name and the offset within that symbol.\n//\n// If no symbol is found for the given address, ErrNoSymbol is returned.\nfunc (ex *Executable) Symbol(address uint64) (SymbolOffset, error) {\n\tif err := ex.lazyLoadSymbols(); err != nil {\n\t\treturn SymbolOffset{}, fmt.Errorf(\"lazy load symbols: %w\", err)\n\t}\n\n\tfor name, symbol := range ex.cachedSymbols {\n\t\tif symbol.contains(address) {\n\t\t\treturn SymbolOffset{name, address - symbol.addr}, nil\n\t\t}\n\t}\n\treturn SymbolOffset{}, ErrNoSymbol\n}\n\n// Uprobe attaches the given eBPF program to a perf event that fires when the\n// given symbol starts executing in the given Executable.\n// For example, /bin/bash::main():\n//\n//\tex, _ = OpenExecutable(\"/bin/bash\")\n//\tex.Uprobe(\"main\", prog, nil)\n//\n// When using symbols which belongs to shared libraries,\n// an offset must be provided via options:\n//\n//\tup, err := ex.Uprobe(\"main\", prog, &UprobeOptions{Offset: 0x123})\n//\n// Note: Setting the Offset field in the options supersedes the symbol's offset.\n//\n// Losing the reference to the resulting Link (up) will close the Uprobe\n// and prevent further execution of prog. The Link must be Closed during\n// program shutdown to avoid leaking system resources.\n//\n// Functions provided by shared libraries can currently not be traced and\n// will result in an ErrNotSupported.\n//\n// The returned Link may implement [PerfEvent].\nfunc (ex *Executable) Uprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions) (Link, error) {\n\tu, err := ex.uprobe(symbol, prog, opts, false)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlnk, err := attachPerfEvent(u, prog, opts.cookie())\n\tif err != nil {\n\t\tu.Close()\n\t\treturn nil, err\n\t}\n\n\treturn lnk, nil\n}\n\n// Uretprobe attaches the given eBPF program to a perf event that fires right\n// before the given symbol exits. For example, /bin/bash::main():\n//\n//\tex, _ = OpenExecutable(\"/bin/bash\")\n//\tex.Uretprobe(\"main\", prog, nil)\n//\n// When using symbols which belongs to shared libraries,\n// an offset must be provided via options:\n//\n//\tup, err := ex.Uretprobe(\"main\", prog, &UprobeOptions{Offset: 0x123})\n//\n// Note: Setting the Offset field in the options supersedes the symbol's offset.\n//\n// Losing the reference to the resulting Link (up) will close the Uprobe\n// and prevent further execution of prog. The Link must be Closed during\n// program shutdown to avoid leaking system resources.\n//\n// Functions provided by shared libraries can currently not be traced and\n// will result in an ErrNotSupported.\n//\n// The returned Link may implement [PerfEvent].\nfunc (ex *Executable) Uretprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions) (Link, error) {\n\tu, err := ex.uprobe(symbol, prog, opts, true)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlnk, err := attachPerfEvent(u, prog, opts.cookie())\n\tif err != nil {\n\t\tu.Close()\n\t\treturn nil, err\n\t}\n\n\treturn lnk, nil\n}\n\n// uprobe opens a perf event for the given binary/symbol and attaches prog to it.\n// If ret is true, create a uretprobe.\nfunc (ex *Executable) uprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions, ret bool) (*perfEvent, error) {\n\tif prog == nil {\n\t\treturn nil, fmt.Errorf(\"prog cannot be nil: %w\", errInvalidInput)\n\t}\n\tif prog.Type() != ebpf.Kprobe {\n\t\treturn nil, fmt.Errorf(\"eBPF program type %s is not Kprobe: %w\", prog.Type(), errInvalidInput)\n\t}\n\tif opts == nil {\n\t\topts = &UprobeOptions{}\n\t}\n\n\toffset, err := ex.address(symbol, opts.Address, opts.Offset)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tpid := opts.PID\n\tif pid == 0 {\n\t\tpid = perfAllThreads\n\t}\n\n\tif opts.RefCtrOffset != 0 {\n\t\tif err := haveRefCtrOffsetPMU(); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"uprobe ref_ctr_offset: %w\", err)\n\t\t}\n\t}\n\n\targs := tracefs.ProbeArgs{\n\t\tType:         tracefs.Uprobe,\n\t\tSymbol:       symbol,\n\t\tPath:         ex.path,\n\t\tOffset:       offset,\n\t\tPid:          pid,\n\t\tRefCtrOffset: opts.RefCtrOffset,\n\t\tRet:          ret,\n\t\tCookie:       opts.Cookie,\n\t\tGroup:        opts.TraceFSPrefix,\n\t}\n\n\t// Use uprobe PMU if the kernel has it available.\n\ttp, err := pmuProbe(args)\n\tif err == nil {\n\t\treturn tp, nil\n\t}\n\tif !errors.Is(err, ErrNotSupported) {\n\t\treturn nil, fmt.Errorf(\"creating perf_uprobe PMU: %w\", err)\n\t}\n\n\t// Use tracefs if uprobe PMU is missing.\n\ttp, err = tracefsProbe(args)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"creating trace event '%s:%s' in tracefs: %w\", ex.path, symbol, err)\n\t}\n\n\treturn tp, nil\n}\n"
  },
  {
    "path": "link/uprobe_multi.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/features\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\n// UprobeMultiOptions defines additional parameters that will be used\n// when opening a UprobeMulti Link.\ntype UprobeMultiOptions struct {\n\t// Symbol addresses. If set, overrides the addresses eventually parsed from\n\t// the executable. Mutually exclusive with UprobeMulti's symbols argument.\n\tAddresses []uint64\n\n\t// Offsets into functions provided by UprobeMulti's symbols argument.\n\t// For example: to set uprobes to main+5 and _start+10, call UprobeMulti\n\t// with:\n\t//     symbols: \"main\", \"_start\"\n\t//     opt.Offsets: 5, 10\n\tOffsets []uint64\n\n\t// Optional list of associated ref counter offsets.\n\tRefCtrOffsets []uint64\n\n\t// Optional list of associated BPF cookies.\n\tCookies []uint64\n\n\t// Only set the uprobe_multi link on the given process ID, zero PID means\n\t// system-wide.\n\tPID uint32\n}\n\nfunc (ex *Executable) UprobeMulti(symbols []string, prog *ebpf.Program, opts *UprobeMultiOptions) (Link, error) {\n\treturn ex.uprobeMulti(symbols, prog, opts, 0)\n}\n\nfunc (ex *Executable) UretprobeMulti(symbols []string, prog *ebpf.Program, opts *UprobeMultiOptions) (Link, error) {\n\n\t// The return probe is not limited for symbols entry, so there's no special\n\t// setup for return uprobes (other than the extra flag). The symbols, opts.Offsets\n\t// and opts.Addresses arrays follow the same logic as for entry uprobes.\n\treturn ex.uprobeMulti(symbols, prog, opts, sys.BPF_F_UPROBE_MULTI_RETURN)\n}\n\nfunc (ex *Executable) uprobeMulti(symbols []string, prog *ebpf.Program, opts *UprobeMultiOptions, flags uint32) (Link, error) {\n\tif prog == nil {\n\t\treturn nil, errors.New(\"cannot attach a nil program\")\n\t}\n\n\tif opts == nil {\n\t\topts = &UprobeMultiOptions{}\n\t}\n\n\taddresses, err := ex.addresses(symbols, opts.Addresses, opts.Offsets)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\taddrs := len(addresses)\n\tcookies := len(opts.Cookies)\n\trefCtrOffsets := len(opts.RefCtrOffsets)\n\n\tif addrs == 0 {\n\t\treturn nil, fmt.Errorf(\"field Addresses is required: %w\", errInvalidInput)\n\t}\n\tif refCtrOffsets > 0 && refCtrOffsets != addrs {\n\t\treturn nil, fmt.Errorf(\"field RefCtrOffsets must be exactly Addresses in length: %w\", errInvalidInput)\n\t}\n\tif cookies > 0 && cookies != addrs {\n\t\treturn nil, fmt.Errorf(\"field Cookies must be exactly Addresses in length: %w\", errInvalidInput)\n\t}\n\n\tattr := &sys.LinkCreateUprobeMultiAttr{\n\t\tPath:             sys.NewStringPointer(ex.path),\n\t\tProgFd:           uint32(prog.FD()),\n\t\tAttachType:       sys.BPF_TRACE_UPROBE_MULTI,\n\t\tUprobeMultiFlags: flags,\n\t\tCount:            uint32(addrs),\n\t\tOffsets:          sys.SlicePointer(addresses),\n\t\tPid:              opts.PID,\n\t}\n\n\tif refCtrOffsets != 0 {\n\t\tattr.RefCtrOffsets = sys.SlicePointer(opts.RefCtrOffsets)\n\t}\n\tif cookies != 0 {\n\t\tattr.Cookies = sys.SlicePointer(opts.Cookies)\n\t}\n\n\tfd, err := sys.LinkCreateUprobeMulti(attr)\n\tif errors.Is(err, unix.ESRCH) {\n\t\treturn nil, fmt.Errorf(\"%w (specified pid not found?)\", os.ErrNotExist)\n\t}\n\t// Since Linux commit 46ba0e49b642 (\"bpf: fix multi-uprobe PID filtering\n\t// logic\"), if the provided pid overflows MaxInt32 (turning it negative), the\n\t// kernel will return EINVAL instead of ESRCH.\n\tif errors.Is(err, unix.EINVAL) {\n\t\treturn nil, fmt.Errorf(\"%w (invalid pid, missing symbol or prog's AttachType not AttachTraceUprobeMulti?)\", err)\n\t}\n\n\tif err != nil {\n\t\tif haveFeatErr := features.HaveBPFLinkUprobeMulti(); haveFeatErr != nil {\n\t\t\treturn nil, haveFeatErr\n\t\t}\n\t\treturn nil, err\n\t}\n\n\treturn &uprobeMultiLink{RawLink{fd, \"\"}}, nil\n}\n\nfunc (ex *Executable) addresses(symbols []string, addresses, offsets []uint64) ([]uint64, error) {\n\tn := len(symbols)\n\tif n == 0 {\n\t\tn = len(addresses)\n\t}\n\n\tif n == 0 {\n\t\treturn nil, fmt.Errorf(\"%w: neither symbols nor addresses given\", errInvalidInput)\n\t}\n\n\tif symbols != nil && len(symbols) != n {\n\t\treturn nil, fmt.Errorf(\"%w: have %d symbols but want %d\", errInvalidInput, len(symbols), n)\n\t}\n\n\tif addresses != nil && len(addresses) != n {\n\t\treturn nil, fmt.Errorf(\"%w: have %d addresses but want %d\", errInvalidInput, len(addresses), n)\n\t}\n\n\tif offsets != nil && len(offsets) != n {\n\t\treturn nil, fmt.Errorf(\"%w: have %d offsets but want %d\", errInvalidInput, len(offsets), n)\n\t}\n\n\tresults := make([]uint64, 0, n)\n\tfor i := 0; i < n; i++ {\n\t\tvar sym string\n\t\tif symbols != nil {\n\t\t\tsym = symbols[i]\n\t\t}\n\n\t\tvar addr, off uint64\n\t\tif addresses != nil {\n\t\t\taddr = addresses[i]\n\t\t}\n\n\t\tif offsets != nil {\n\t\t\toff = offsets[i]\n\t\t}\n\n\t\tresult, err := ex.address(sym, addr, off)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tresults = append(results, result)\n\t}\n\n\treturn results, nil\n}\n\ntype uprobeMultiLink struct {\n\tRawLink\n}\n\nvar _ Link = (*uprobeMultiLink)(nil)\n\nfunc (kml *uprobeMultiLink) Update(_ *ebpf.Program) error {\n\treturn fmt.Errorf(\"update uprobe_multi: %w\", ErrNotSupported)\n}\n\nfunc (kml *uprobeMultiLink) Info() (*Info, error) {\n\tvar info sys.UprobeMultiLinkInfo\n\tif err := sys.ObjInfo(kml.fd, &info); err != nil {\n\t\treturn nil, fmt.Errorf(\"uprobe multi link info: %s\", err)\n\t}\n\tvar (\n\t\tpath          = make([]byte, info.PathSize)\n\t\trefCtrOffsets = make([]uint64, info.Count)\n\t\taddrs         = make([]uint64, info.Count)\n\t\tcookies       = make([]uint64, info.Count)\n\t)\n\tinfo = sys.UprobeMultiLinkInfo{\n\t\tPath:          sys.SlicePointer(path),\n\t\tPathSize:      uint32(len(path)),\n\t\tOffsets:       sys.SlicePointer(addrs),\n\t\tRefCtrOffsets: sys.SlicePointer(refCtrOffsets),\n\t\tCookies:       sys.SlicePointer(cookies),\n\t\tCount:         uint32(len(addrs)),\n\t}\n\tif err := sys.ObjInfo(kml.fd, &info); err != nil {\n\t\treturn nil, fmt.Errorf(\"uprobe multi link info: %s\", err)\n\t}\n\tif info.Path.IsNil() {\n\t\tpath = nil\n\t}\n\tif info.Cookies.IsNil() {\n\t\tcookies = nil\n\t}\n\tif info.Offsets.IsNil() {\n\t\taddrs = nil\n\t}\n\tif info.RefCtrOffsets.IsNil() {\n\t\trefCtrOffsets = nil\n\t}\n\textra := &UprobeMultiInfo{\n\t\tCount:         info.Count,\n\t\tFlags:         info.Flags,\n\t\tpid:           info.Pid,\n\t\toffsets:       addrs,\n\t\tcookies:       cookies,\n\t\trefCtrOffsets: refCtrOffsets,\n\t\tFile:          unix.ByteSliceToString(path),\n\t}\n\n\treturn &Info{\n\t\tinfo.Type,\n\t\tinfo.Id,\n\t\tebpf.ProgramID(info.ProgId),\n\t\textra,\n\t}, nil\n}\n"
  },
  {
    "path": "link/uprobe_multi_test.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"errors\"\n\t\"math\"\n\t\"os\"\n\t\"os/exec\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/features\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc TestUprobeMulti(t *testing.T) {\n\ttestutils.SkipIfNotSupported(t, features.HaveBPFLinkUprobeMulti())\n\n\tprog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceUprobeMulti, \"\")\n\n\t// uprobe\n\tum, err := bashEx.UprobeMulti(bashSyms, prog, nil)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\ttestLink(t, um, prog)\n\t_ = um.Close()\n\n\t// uretprobe\n\tum, err = bashEx.UretprobeMulti(bashSyms, prog, nil)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\ttestLink(t, um, prog)\n\t_ = um.Close()\n}\n\nfunc TestUprobeMultiInfo(t *testing.T) {\n\ttestutils.SkipIfNotSupported(t, features.HaveBPFLinkKprobeMulti())\n\ttestutils.SkipOnOldKernel(t, \"6.8\", \"bpf_link_info_uprobe_multi\")\n\n\tprog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceUprobeMulti, \"\")\n\n\t// uprobe\n\tum, err := bashEx.UprobeMulti(bashSyms, prog, nil)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer um.Close()\n\n\tlinkInfo, err := um.Info()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tqt.Assert(t, qt.Equals(linkInfo.Type, UprobeMultiType))\n\tuprobeDetails := linkInfo.UprobeMulti()\n\t// On some platforms, /bin/bash may point to /usr/bin/bash, thus only a contains and no equals check\n\tqt.Assert(t, qt.StringContains(uprobeDetails.File, bashEx.path))\n\tuprobeOffsets, ok := uprobeDetails.Offsets()\n\tqt.Assert(t, qt.IsTrue(ok))\n\tqt.Assert(t, qt.HasLen(uprobeOffsets, len(bashSyms)))\n\tbashElf, err := OpenExecutable(uprobeDetails.File)\n\tqt.Assert(t, qt.IsNil(err))\n\tvar symnames = make([]string, len(uprobeOffsets))\n\tfor i, offset := range uprobeOffsets {\n\t\tsymOffset, err := bashElf.Symbol(offset.Offset)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.Equals(symOffset.Offset, 0))\n\t\tsymnames[i] = symOffset.Symbol\n\t}\n\tqt.Assert(t, qt.ContentEquals(symnames, bashSyms))\n}\n\nfunc TestUprobeMultiInput(t *testing.T) {\n\ttestutils.SkipIfNotSupported(t, features.HaveBPFLinkUprobeMulti())\n\n\tprog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceUprobeMulti, \"\")\n\n\t// Always doing same test for both uprobe and uretprobe\n\n\t// One of symbols or offsets must be given.\n\t_, err := bashEx.UprobeMulti([]string{}, prog, nil)\n\tqt.Assert(t, qt.ErrorIs(err, errInvalidInput))\n\n\t_, err = bashEx.UretprobeMulti([]string{}, prog, nil)\n\tqt.Assert(t, qt.ErrorIs(err, errInvalidInput))\n\n\t// One address, two cookies.\n\t_, err = bashEx.UprobeMulti([]string{}, prog, &UprobeMultiOptions{\n\t\tAddresses: []uint64{1},\n\t\tCookies:   []uint64{2, 3},\n\t})\n\tqt.Assert(t, qt.ErrorIs(err, errInvalidInput))\n\n\t_, err = bashEx.UretprobeMulti([]string{}, prog, &UprobeMultiOptions{\n\t\tAddresses: []uint64{1},\n\t\tCookies:   []uint64{2, 3},\n\t})\n\tqt.Assert(t, qt.ErrorIs(err, errInvalidInput))\n\n\t// Two addresses, one refctr offset.\n\t_, err = bashEx.UprobeMulti([]string{}, prog, &UprobeMultiOptions{\n\t\tAddresses:     []uint64{1, 2},\n\t\tRefCtrOffsets: []uint64{4},\n\t})\n\tqt.Assert(t, qt.ErrorIs(err, errInvalidInput))\n\n\t_, err = bashEx.UretprobeMulti([]string{}, prog, &UprobeMultiOptions{\n\t\tAddresses:     []uint64{1, 2},\n\t\tRefCtrOffsets: []uint64{4},\n\t})\n\tqt.Assert(t, qt.ErrorIs(err, errInvalidInput))\n\n\t// It's either symbols or addresses.\n\t_, err = bashEx.UprobeMulti(bashSyms, prog, &UprobeMultiOptions{\n\t\tAddresses: []uint64{1},\n\t})\n\tqt.Assert(t, qt.ErrorIs(err, errInvalidInput))\n\n\t_, err = bashEx.UretprobeMulti(bashSyms, prog, &UprobeMultiOptions{\n\t\tAddresses: []uint64{1},\n\t})\n\tqt.Assert(t, qt.ErrorIs(err, errInvalidInput))\n\n\t// No addresses and no symbols\n\t_, err = bashEx.UprobeMulti([]string{}, prog, nil)\n\tqt.Assert(t, qt.ErrorIs(err, errInvalidInput))\n\n\t_, err = bashEx.UretprobeMulti([]string{}, prog, nil)\n\tqt.Assert(t, qt.ErrorIs(err, errInvalidInput))\n\n\t// PID not found\n\t_, err = bashEx.UprobeMulti(bashSyms, prog, &UprobeMultiOptions{\n\t\t// pid_t is int32, overflowing it will return EINVAL.\n\t\tPID: math.MaxInt32,\n\t})\n\tqt.Assert(t, qt.ErrorIs(err, os.ErrNotExist))\n\n\t_, err = bashEx.UretprobeMulti(bashSyms, prog, &UprobeMultiOptions{\n\t\tPID: math.MaxInt32,\n\t})\n\tqt.Assert(t, qt.ErrorIs(err, os.ErrNotExist))\n}\n\nfunc TestUprobeMultiResolveOk(t *testing.T) {\n\taddrSym1, err := bashEx.address(bashSyms[0], 0, 0)\n\tqt.Assert(t, qt.IsNil(err))\n\taddrSym2, err := bashEx.address(bashSyms[1], 0, 0)\n\tqt.Assert(t, qt.IsNil(err))\n\taddrSym3, err := bashEx.address(bashSyms[2], 0, 0)\n\tqt.Assert(t, qt.IsNil(err))\n\n\taddrs, err := bashEx.addresses(bashSyms, nil, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.DeepEquals(addrs, []uint64{addrSym1, addrSym2, addrSym3}))\n\n\taddrs, err = bashEx.addresses(bashSyms, nil, []uint64{5, 10, 11})\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.DeepEquals(addrs, []uint64{addrSym1 + 5, addrSym2 + 10, addrSym3 + 11}))\n\n\taddrs, err = bashEx.addresses(bashSyms, []uint64{1, 2, 3}, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tqt.Assert(t, qt.DeepEquals(addrs, []uint64{1, 2, 3}))\n}\n\nfunc TestUprobeMultiResolveFail(t *testing.T) {\n\t// No input\n\t_, err := bashEx.addresses(nil, nil, nil)\n\tqt.Assert(t, qt.ErrorIs(err, errInvalidInput))\n\n\t// Different dimensions for Addresses and Offsets\n\t_, err = bashEx.addresses(nil, []uint64{100, 200}, []uint64{5, 10, 11})\n\tqt.Assert(t, qt.ErrorIs(err, errInvalidInput))\n\n\t// Different dimensions for symbols and Offsets\n\t_, err = bashEx.addresses(bashSyms, nil, []uint64{5, 10})\n\tqt.Assert(t, qt.ErrorIs(err, errInvalidInput))\n}\n\nfunc TestUprobeMultiCookie(t *testing.T) {\n\ttestutils.SkipIfNotSupported(t, features.HaveBPFLinkUprobeMulti())\n\n\tprog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceUprobeMulti, \"\")\n\n\t// uprobe\n\tum, err := bashEx.UprobeMulti(bashSyms, prog,\n\t\t&UprobeMultiOptions{\n\t\t\tCookies: []uint64{1, 2, 3},\n\t\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\t_ = um.Close()\n\n\t// uretprobe\n\tum, err = bashEx.UretprobeMulti(bashSyms, prog,\n\t\t&UprobeMultiOptions{\n\t\t\tCookies: []uint64{3, 2, 1},\n\t\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\t_ = um.Close()\n}\n\nfunc TestUprobeMultiProgramCall(t *testing.T) {\n\ttestutils.SkipIfNotSupported(t, features.HaveBPFLinkUprobeMulti())\n\n\t// We execute 'bash --help'\n\targs := []string{\"--help\"}\n\telf := \"/bin/bash\"\n\n\ttest := func(retprobe bool, expected uint32) {\n\t\tm, p := newUpdaterMapProg(t, ebpf.Kprobe, ebpf.AttachTraceUprobeMulti)\n\n\t\tvar err error\n\n\t\t// Load the executable.\n\t\tex, err := OpenExecutable(elf)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tvar um Link\n\n\t\t// Open UprobeMulti on the executable for the given symbol\n\t\t// and attach it to the ebpf program created above.\n\t\tif retprobe {\n\t\t\tum, err = ex.UretprobeMulti(bashSyms, p, nil)\n\t\t} else {\n\t\t\tum, err = ex.UprobeMulti(bashSyms, p, nil)\n\t\t}\n\t\tif errors.Is(err, ErrNoSymbol) {\n\t\t\t// Assume bash_Syms symbols always exist and skip the test\n\t\t\t// if the symbol can't be found as certain OS (eg. Debian)\n\t\t\t// strip binaries.\n\t\t\tt.Skipf(\"executable %s appear to be stripped, skipping\", elf)\n\t\t}\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\t// Trigger ebpf program call.\n\t\ttrigger := func(t *testing.T) {\n\t\t\tif err := exec.Command(elf, args...).Run(); err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t}\n\t\ttrigger(t)\n\n\t\t// Detach link.\n\t\tif err := um.Close(); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tassertMapValueGE(t, m, 0, expected)\n\n\t\t// Reset map value to 0 at index 0.\n\t\tif err := m.Update(uint32(0), uint32(0), ebpf.UpdateExist); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\t// Retrigger the ebpf program call.\n\t\ttrigger(t)\n\n\t\t// Assert that this time the value has not been updated.\n\t\tassertMapValue(t, m, 0, 0)\n\t}\n\n\t// all 3 uprobes should trigger for entry uprobes\n\ttest(false, 3)\n\n\t// We have return uprobe installed on main, _start and check_dev_tty\n\t// functions, but only check_dev_tty is triggered, because 'bash --help'\n\t// calls exit(0).\n\ttest(true, 1)\n}\n"
  },
  {
    "path": "link/uprobe_test.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"errors\"\n\t\"go/build\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n\t\"github.com/cilium/ebpf/internal/tracefs\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\nvar (\n\tbashEx, _ = OpenExecutable(\"/bin/bash\")\n\tbashSyms  = []string{\"main\", \"_start\", \"check_dev_tty\"}\n\tbashSym   = bashSyms[0]\n)\n\nfunc TestExecutable(t *testing.T) {\n\t_, err := OpenExecutable(\"\")\n\tif err == nil {\n\t\tt.Fatal(\"create executable: expected error on empty path\")\n\t}\n\n\t_, err = OpenExecutable(\"/non/existent/path\")\n\tif err == nil {\n\t\tt.Fatal(\"create executable: expected error on non-existent path\")\n\t}\n\tvar pe *os.PathError\n\tqt.Assert(t, qt.ErrorAs(err, &pe))\n\n\t// create temp non-executable file\n\tdir := t.TempDir()\n\tpath := filepath.Join(dir, \"file.txt\")\n\terr = os.WriteFile(path, []byte(\"hello\"), 0600)\n\tif err != nil {\n\t\tt.Fatalf(\"write file: %v\", err)\n\t}\n\n\t_, err = OpenExecutable(path)\n\tif err == nil {\n\t\tt.Fatal(\"create executable: expected error on non-executable file\")\n\t}\n\n\t// make it executable\n\terr = os.Chmod(path, 0700)\n\tif err != nil {\n\t\tt.Fatalf(\"chmod file: %v\", err)\n\t}\n\n\t_, err = OpenExecutable(path)\n\tif err != nil {\n\t\tt.Fatalf(\"create executable: %v\", err)\n\t}\n\n\tif bashEx.path != \"/bin/bash\" {\n\t\tt.Fatalf(\"create executable: unexpected path '%s'\", bashEx.path)\n\t}\n\n\t_, err = bashEx.address(bashSym, 0, 0)\n\tif err != nil {\n\t\tt.Fatalf(\"find offset: %v\", err)\n\t}\n\n\t_, err = bashEx.address(\"bogus\", 0, 0)\n\tif err == nil {\n\t\tt.Fatal(\"find symbol: expected error\")\n\t}\n}\n\nfunc TestExecutableOffset(t *testing.T) {\n\tsymbolOffset, err := bashEx.address(bashSym, 0, 0)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\toffset, err := bashEx.address(bashSym, 0x1, 0)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tqt.Assert(t, qt.Equals(offset, 0x1))\n\n\toffset, err = bashEx.address(bashSym, 0, 0x2)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tqt.Assert(t, qt.Equals(offset, symbolOffset+0x2))\n\n\toffset, err = bashEx.address(bashSym, 0x1, 0x2)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tqt.Assert(t, qt.Equals(offset, 0x1+0x2))\n}\n\nfunc TestExecutableLazyLoadSymbols(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"4.14\", \"uprobe on v4.9 returns EIO on vimto\")\n\n\tex, err := OpenExecutable(\"/bin/bash\")\n\tqt.Assert(t, qt.IsNil(err))\n\t// Addresses must be empty, will be lazy loaded.\n\tqt.Assert(t, qt.HasLen(ex.cachedSymbols, 0))\n\n\tprog := mustLoadProgram(t, ebpf.Kprobe, 0, \"\")\n\t// Address must be a multiple of 4 on arm64, see\n\t// https://elixir.bootlin.com/linux/v6.6.4/source/arch/arm64/kernel/probes/uprobes.c#L42\n\tup, err := ex.Uprobe(bashSym, prog, &UprobeOptions{Address: 124})\n\tqt.Assert(t, qt.IsNil(err))\n\tup.Close()\n\n\t// Addresses must still be empty as Address has been provided via options.\n\tqt.Assert(t, qt.HasLen(ex.cachedSymbols, 0))\n\n\tup, err = ex.Uprobe(bashSym, prog, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\tup.Close()\n\n\t// Symbol table should be loaded.\n\tqt.Assert(t, qt.Not(qt.HasLen(ex.cachedSymbols, 0)))\n}\n\nfunc TestUprobe(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"4.14\", \"uprobe on v4.9 returns EIO on vimto\")\n\n\tprog := mustLoadProgram(t, ebpf.Kprobe, 0, \"\")\n\n\tup, err := bashEx.Uprobe(bashSym, prog, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer up.Close()\n\n\ttestLink(t, up, prog)\n}\n\nfunc TestUprobeInfo(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"6.6\", \"bpf_link_info_perf_event\")\n\n\tprog := mustLoadProgram(t, ebpf.Kprobe, 0, \"\")\n\n\tup, err := bashEx.Uprobe(bashSym, prog, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer up.Close()\n\n\tinfo, err := up.Info()\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.Equals(info.Type, PerfEventType))\n\teventInfo := info.PerfEvent()\n\tqt.Assert(t, qt.Equals(eventInfo.Type, PerfEventUprobe))\n\tuprobeInfo := eventInfo.Uprobe()\n\tqt.Assert(t, qt.StringContains(uprobeInfo.File, bashEx.path))\n\texecutable, err := OpenExecutable(uprobeInfo.File)\n\tqt.Assert(t, qt.IsNil(err))\n\tsym, err := executable.Symbol(uint64(uprobeInfo.Offset))\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.Equals(sym.Symbol, bashSym))\n\tqt.Assert(t, qt.Equals(sym.Offset, 0))\n}\n\nfunc TestUprobeExtNotFound(t *testing.T) {\n\tprog := mustLoadProgram(t, ebpf.Kprobe, 0, \"\")\n\n\t// This symbol will not be present in Executable (elf.SHN_UNDEF).\n\t_, err := bashEx.Uprobe(\"open\", prog, nil)\n\tif err == nil {\n\t\tt.Fatal(\"expected error\")\n\t}\n}\n\nfunc TestUprobeExtWithOpts(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"4.14\", \"uprobe on v4.9 returns EIO on vimto\")\n\n\tprog := mustLoadProgram(t, ebpf.Kprobe, 0, \"\")\n\n\t// NB: It's not possible to invoke the uprobe since we use an arbitrary\n\t// address.\n\tup, err := bashEx.Uprobe(\"open\", prog, &UprobeOptions{\n\t\t// arm64 requires the addresses to be aligned (a multiple of 4)\n\t\tAddress: 0x4,\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer up.Close()\n}\n\nfunc TestUprobeWithPID(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"4.14\", \"uprobe on v4.9 returns EIO on vimto\")\n\n\tprog := mustLoadProgram(t, ebpf.Kprobe, 0, \"\")\n\n\tup, err := bashEx.Uprobe(bashSym, prog, &UprobeOptions{PID: os.Getpid()})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer up.Close()\n}\n\nfunc TestUprobeWithNonExistentPID(t *testing.T) {\n\tprog := mustLoadProgram(t, ebpf.Kprobe, 0, \"\")\n\n\t// trying to open a perf event on a non-existent PID will return ESRCH.\n\t_, err := bashEx.Uprobe(bashSym, prog, &UprobeOptions{PID: -2})\n\tif !errors.Is(err, unix.ESRCH) {\n\t\tt.Fatalf(\"expected ESRCH, got %v\", err)\n\t}\n}\n\nfunc TestUretprobe(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"4.14\", \"uprobe on v4.9 returns EIO on vimto\")\n\n\tprog := mustLoadProgram(t, ebpf.Kprobe, 0, \"\")\n\n\tup, err := bashEx.Uretprobe(bashSym, prog, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer up.Close()\n\n\ttestLink(t, up, prog)\n}\n\n// Test u(ret)probe creation using perf_uprobe PMU.\nfunc TestUprobeCreatePMU(t *testing.T) {\n\t// Requires at least 4.17 (e12f03d7031a \"perf/core: Implement the 'perf_kprobe' PMU\")\n\ttestutils.SkipOnOldKernel(t, \"4.17\", \"perf_kprobe PMU\")\n\n\t// Fetch the offset from the /bin/bash Executable already defined.\n\toff, err := bashEx.address(bashSym, 0, 0)\n\tqt.Assert(t, qt.IsNil(err))\n\n\t// Prepare probe args.\n\targs := tracefs.ProbeArgs{\n\t\tType:   tracefs.Uprobe,\n\t\tSymbol: bashSym,\n\t\tPath:   bashEx.path,\n\t\tOffset: off,\n\t\tPid:    perfAllThreads,\n\t}\n\n\t// uprobe PMU\n\tpu, err := pmuProbe(args)\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer pu.Close()\n\n\t// uretprobe PMU\n\targs.Ret = true\n\tpr, err := pmuProbe(args)\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer pr.Close()\n}\n\n// Test fallback behaviour on kernels without perf_uprobe PMU available.\nfunc TestUprobePMUUnavailable(t *testing.T) {\n\t// Fetch the offset from the /bin/bash Executable already defined.\n\toff, err := bashEx.address(bashSym, 0, 0)\n\tqt.Assert(t, qt.IsNil(err))\n\n\t// Prepare probe args.\n\targs := tracefs.ProbeArgs{\n\t\tType:   tracefs.Uprobe,\n\t\tSymbol: bashSym,\n\t\tPath:   bashEx.path,\n\t\tOffset: off,\n\t\tPid:    perfAllThreads,\n\t}\n\n\tpk, err := pmuProbe(args)\n\tif err == nil {\n\t\tpk.Close()\n\t\tt.Skipf(\"Kernel supports perf_uprobe PMU, not asserting error.\")\n\t}\n\n\t// Expect ErrNotSupported.\n\tqt.Assert(t, qt.ErrorIs(err, ErrNotSupported), qt.Commentf(\"got error: %s\", err))\n}\n\n// Test tracefs u(ret)probe creation on all kernel versions.\nfunc TestUprobeTraceFS(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"4.14\", \"uprobe on v4.9 returns EIO on vimto\")\n\n\t// Fetch the offset from the /bin/bash Executable already defined.\n\toff, err := bashEx.address(bashSym, 0, 0)\n\tqt.Assert(t, qt.IsNil(err))\n\n\t// Prepare probe args.\n\targs := tracefs.ProbeArgs{\n\t\tType:   tracefs.Uprobe,\n\t\tSymbol: bashSym,\n\t\tPath:   bashEx.path,\n\t\tOffset: off,\n\t\tPid:    perfAllThreads,\n\t}\n\n\t// Open and close tracefs u(ret)probes, checking all errors.\n\tup, err := tracefsProbe(args)\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.IsNil(up.Close()))\n\n\targs.Ret = true\n\tup, err = tracefsProbe(args)\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.IsNil(up.Close()))\n\n\t// Create two identical trace events, ensure their IDs differ.\n\targs.Ret = false\n\tu1, err := tracefsProbe(args)\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer u1.Close()\n\tqt.Assert(t, qt.IsNotNil(u1.tracefsEvent))\n\n\tu2, err := tracefsProbe(args)\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer u2.Close()\n\tqt.Assert(t, qt.IsNotNil(u2.tracefsEvent))\n\n\t// Compare the uprobes' tracefs IDs.\n\tqt.Assert(t, qt.Not(qt.Equals(u1.tracefsEvent.ID(), u2.tracefsEvent.ID())))\n\n\t// Expect an error when supplying an invalid custom group name\n\targs.Group = \"/\"\n\t_, err = tracefsProbe(args)\n\tqt.Assert(t, qt.Not(qt.IsNil(err)))\n\n\targs.Group = \"customgroup\"\n\tu3, err := tracefsProbe(args)\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer u3.Close()\n\tqt.Assert(t, qt.Matches(u3.tracefsEvent.Group(), `customgroup_[a-f0-9]{16}`))\n}\n\nfunc TestUprobeProgramCall(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"4.14\", \"uprobe on v4.9 returns EIO on vimto\")\n\n\ttests := []struct {\n\t\tname string\n\t\telf  string\n\t\targs []string\n\t\tsym  string\n\t}{\n\t\t{\n\t\t\t\"bash\",\n\t\t\t\"/bin/bash\",\n\t\t\t[]string{\"--help\"},\n\t\t\t\"main\",\n\t\t},\n\t\t{\n\t\t\t\"go-binary\",\n\t\t\tpath.Join(build.Default.GOROOT, \"bin/go\"),\n\t\t\t[]string{\"version\"},\n\t\t\t\"main.main\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif tt.name == \"go-binary\" {\n\t\t\t\t// https://github.com/cilium/ebpf/issues/406\n\t\t\t\ttestutils.SkipOnOldKernel(t, \"4.14\", \"uprobes on Go binaries silently fail on kernel < 4.14\")\n\t\t\t}\n\n\t\t\tm, p := newUpdaterMapProg(t, ebpf.Kprobe, 0)\n\n\t\t\t// Load the executable.\n\t\t\tex, err := OpenExecutable(tt.elf)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\n\t\t\t// Open Uprobe on the executable for the given symbol\n\t\t\t// and attach it to the ebpf program created above.\n\t\t\tu, err := ex.Uprobe(tt.sym, p, nil)\n\t\t\tif errors.Is(err, ErrNoSymbol) {\n\t\t\t\t// Assume bash::main and go::main.main always exists\n\t\t\t\t// and skip the test if the symbol can't be found as\n\t\t\t\t// certain OS (eg. Debian) strip binaries.\n\t\t\t\tt.Skipf(\"executable %s appear to be stripped, skipping\", tt.elf)\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\n\t\t\t// Trigger ebpf program call.\n\t\t\ttrigger := func(t *testing.T) {\n\t\t\t\tif err := exec.Command(tt.elf, tt.args...).Run(); err != nil {\n\t\t\t\t\tt.Fatal(err)\n\t\t\t\t}\n\t\t\t}\n\t\t\ttrigger(t)\n\n\t\t\t// Assert that the value got incremented to at least 1, while allowing\n\t\t\t// for bigger values, because we could race with other bash execution.\n\t\t\tassertMapValueGE(t, m, 0, 1)\n\n\t\t\t// Detach the Uprobe.\n\t\t\tif err := u.Close(); err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\n\t\t\t// Reset map value to 0 at index 0.\n\t\t\tif err := m.Update(uint32(0), uint32(0), ebpf.UpdateExist); err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\n\t\t\t// Retrigger the ebpf program call.\n\t\t\ttrigger(t)\n\n\t\t\t// Assert that this time the value has not been updated.\n\t\t\tassertMapValue(t, m, 0, 0)\n\t\t})\n\t}\n}\n\nfunc TestUprobeProgramWrongPID(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"4.14\", \"uprobe on v4.9 returns EIO on vimto\")\n\n\tm, p := newUpdaterMapProg(t, ebpf.Kprobe, 0)\n\n\t// Load the '/bin/bash' executable.\n\tex, err := OpenExecutable(\"/bin/bash\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// Open Uprobe on '/bin/bash' for the symbol 'main'\n\t// and attach it to the ebpf program created above.\n\t// Create the perf-event with the current process' PID\n\t// to make sure the event is not fired when we will try\n\t// to trigger the program execution via exec.\n\tu, err := ex.Uprobe(\"main\", p, &UprobeOptions{PID: os.Getpid()})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer u.Close()\n\n\t// Trigger ebpf program call.\n\tif err := exec.Command(\"/bin/bash\", \"--help\").Run(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// Assert that the value at index 0 is still 0.\n\tassertMapValue(t, m, 0, 0)\n}\n\nfunc TestHaveRefCtrOffsetPMU(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, haveRefCtrOffsetPMU)\n}\n"
  },
  {
    "path": "link/xdp.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n)\n\n// XDPAttachFlags represents how XDP program will be attached to interface.\ntype XDPAttachFlags uint32\n\nconst (\n\t// XDPGenericMode (SKB) links XDP BPF program for drivers which do\n\t// not yet support native XDP.\n\tXDPGenericMode XDPAttachFlags = 1 << (iota + 1)\n\t// XDPDriverMode links XDP BPF program into the driver’s receive path.\n\tXDPDriverMode\n\t// XDPOffloadMode offloads the entire XDP BPF program into hardware.\n\tXDPOffloadMode\n)\n\ntype XDPOptions struct {\n\t// Program must be an XDP BPF program.\n\tProgram *ebpf.Program\n\n\t// Interface is the interface index to attach program to.\n\tInterface int\n\n\t// Flags is one of XDPAttachFlags (optional).\n\t//\n\t// Only one XDP mode should be set, without flag defaults\n\t// to driver/generic mode (best effort).\n\tFlags XDPAttachFlags\n}\n\n// AttachXDP links an XDP BPF program to an XDP hook.\nfunc AttachXDP(opts XDPOptions) (Link, error) {\n\tif t := opts.Program.Type(); t != ebpf.XDP {\n\t\treturn nil, fmt.Errorf(\"invalid program type %s, expected XDP\", t)\n\t}\n\n\tif opts.Interface < 1 {\n\t\treturn nil, fmt.Errorf(\"invalid interface index: %d\", opts.Interface)\n\t}\n\n\trawLink, err := AttachRawLink(RawLinkOptions{\n\t\tProgram: opts.Program,\n\t\tAttach:  ebpf.AttachXDP,\n\t\tTarget:  opts.Interface,\n\t\tFlags:   uint32(opts.Flags),\n\t})\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to attach link: %w\", err)\n\t}\n\n\treturn &xdpLink{*rawLink}, nil\n}\n\ntype xdpLink struct {\n\tRawLink\n}\n\nfunc (xdp *xdpLink) Info() (*Info, error) {\n\tvar info sys.XDPLinkInfo\n\tif err := sys.ObjInfo(xdp.fd, &info); err != nil {\n\t\treturn nil, fmt.Errorf(\"xdp link info: %s\", err)\n\t}\n\textra := &XDPInfo{\n\t\tIfindex: info.Ifindex,\n\t}\n\n\treturn &Info{\n\t\tinfo.Type,\n\t\tinfo.Id,\n\t\tebpf.ProgramID(info.ProgId),\n\t\textra,\n\t}, nil\n}\n"
  },
  {
    "path": "link/xdp_test.go",
    "content": "//go:build !windows\n\npackage link\n\nimport (\n\t\"math\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nconst IfIndexLO = 1\n\nfunc TestAttachXDP(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.9\", \"BPF_LINK_TYPE_XDP\")\n\n\tprog := mustLoadProgram(t, ebpf.XDP, 0, \"\")\n\n\t_, err := AttachXDP(XDPOptions{\n\t\tProgram:   prog,\n\t\tInterface: math.MaxInt,\n\t})\n\tqt.Assert(t, qt.IsNotNil(err))\n\n\tl, err := AttachXDP(XDPOptions{\n\t\tProgram:   prog,\n\t\tInterface: IfIndexLO,\n\t})\n\tqt.Assert(t, qt.IsNil(err))\n\n\tinfo, err := l.Info()\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.Equals(info.XDP().Ifindex, IfIndexLO))\n\n\ttestLink(t, l, prog)\n}\n"
  },
  {
    "path": "linker.go",
    "content": "package ebpf\n\nimport (\n\t\"debug/elf\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"math\"\n\t\"slices\"\n\t\"strings\"\n\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/kallsyms\"\n\t\"github.com/cilium/ebpf/internal/platform\"\n)\n\n// handles stores handle objects to avoid gc cleanup\ntype handles []*btf.Handle\n\nfunc (hs *handles) add(h *btf.Handle) (int, error) {\n\tif h == nil {\n\t\treturn 0, nil\n\t}\n\n\tif len(*hs) == math.MaxInt16 {\n\t\treturn 0, fmt.Errorf(\"can't add more than %d module FDs to fdArray\", math.MaxInt16)\n\t}\n\n\t*hs = append(*hs, h)\n\n\t// return length of slice so that indexes start at 1\n\treturn len(*hs), nil\n}\n\nfunc (hs handles) fdArray() []int32 {\n\t// first element of fda is reserved as no module can be indexed with 0\n\tfda := []int32{0}\n\tfor _, h := range hs {\n\t\tfda = append(fda, int32(h.FD()))\n\t}\n\n\treturn fda\n}\n\nfunc (hs *handles) Close() error {\n\tvar errs []error\n\tfor _, h := range *hs {\n\t\terrs = append(errs, h.Close())\n\t}\n\treturn errors.Join(errs...)\n}\n\n// The linker is responsible for resolving bpf-to-bpf calls between programs\n// within an ELF. Each BPF program must be a self-contained binary blob,\n// so when an instruction in one ELF program section wants to jump to\n// a function in another, the linker needs to pull in the bytecode\n// (and BTF info) of the target function and concatenate the instruction\n// streams.\n//\n// Later on in the pipeline, all call sites are fixed up with relative jumps\n// within this newly-created instruction stream to then finally hand off to\n// the kernel with BPF_PROG_LOAD.\n//\n// Each function is denoted by an ELF symbol and the compiler takes care of\n// register setup before each jump instruction.\n\n// hasFunctionReferences returns true if insns contains one or more bpf2bpf\n// function references.\nfunc hasFunctionReferences(insns asm.Instructions) bool {\n\tfor _, i := range insns {\n\t\tif i.IsFunctionReference() {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// applyRelocations collects and applies any CO-RE relocations in insns.\n//\n// insns are modified in place.\nfunc applyRelocations(insns asm.Instructions, bo binary.ByteOrder, b *btf.Builder, c *btf.Cache, kernelOverride *btf.Spec, extraTargets []*btf.Spec) error {\n\tvar relos []*btf.CORERelocation\n\tvar reloInsns []*asm.Instruction\n\titer := insns.Iterate()\n\tfor iter.Next() {\n\t\tif relo := btf.CORERelocationMetadata(iter.Ins); relo != nil {\n\t\t\trelos = append(relos, relo)\n\t\t\treloInsns = append(reloInsns, iter.Ins)\n\t\t}\n\t}\n\n\tif len(relos) == 0 {\n\t\treturn nil\n\t}\n\n\tif bo == nil {\n\t\tbo = internal.NativeEndian\n\t}\n\n\tvar targets []*btf.Spec\n\tif kernelOverride == nil {\n\t\tkernel, err := c.Kernel()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"load kernel spec: %w\", err)\n\t\t}\n\n\t\tmodules, err := c.Modules()\n\t\t// Ignore ErrNotExists to cater to kernels which have CONFIG_DEBUG_INFO_BTF_MODULES\n\t\t// or CONFIG_DEBUG_INFO_BTF disabled.\n\t\tif err != nil && !errors.Is(err, fs.ErrNotExist) {\n\t\t\treturn err\n\t\t}\n\n\t\ttargets = make([]*btf.Spec, 0, 1+len(modules)+len(extraTargets))\n\t\ttargets = append(targets, kernel)\n\n\t\tfor _, kmod := range modules {\n\t\t\tspec, err := c.Module(kmod)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"load BTF for kmod %s: %w\", kmod, err)\n\t\t\t}\n\n\t\t\ttargets = append(targets, spec)\n\t\t}\n\t} else {\n\t\t// We expect kernelOverride to contain the merged types\n\t\t// of vmlinux and kernel modules, as distributed by btfhub.\n\t\ttargets = []*btf.Spec{kernelOverride}\n\t}\n\n\ttargets = append(targets, extraTargets...)\n\n\tfixups, err := btf.CORERelocate(relos, targets, bo, b.Add)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor i, fixup := range fixups {\n\t\tif err := fixup.Apply(reloInsns[i]); err != nil {\n\t\t\treturn fmt.Errorf(\"fixup for %s: %w\", relos[i], err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// flattenPrograms resolves bpf-to-bpf calls for a set of programs.\n//\n// Links all programs in names by modifying their ProgramSpec in progs.\nfunc flattenPrograms(progs map[string]*ProgramSpec, names []string) {\n\t// Pre-calculate all function references.\n\trefs := make(map[*ProgramSpec][]string)\n\tfor _, prog := range progs {\n\t\trefs[prog] = prog.Instructions.FunctionReferences()\n\t}\n\n\t// Create a flattened instruction stream, but don't modify progs yet to\n\t// avoid linking multiple times.\n\tflattened := make([]asm.Instructions, 0, len(names))\n\tfor _, name := range names {\n\t\tflattened = append(flattened, flattenInstructions(name, progs, refs))\n\t}\n\n\t// Finally, assign the flattened instructions.\n\tfor i, name := range names {\n\t\tprogs[name].Instructions = flattened[i]\n\t}\n}\n\n// flattenInstructions resolves bpf-to-bpf calls for a single program.\n//\n// Flattens the instructions of prog by concatenating the instructions of all\n// direct and indirect dependencies.\n//\n// progs contains all referenceable programs, while refs contain the direct\n// dependencies of each program.\nfunc flattenInstructions(name string, progs map[string]*ProgramSpec, refs map[*ProgramSpec][]string) asm.Instructions {\n\tprog := progs[name]\n\tprogRefs := refs[prog]\n\n\tif len(progRefs) == 0 {\n\t\t// No references, nothing to do.\n\t\treturn prog.Instructions\n\t}\n\n\tinsns := make(asm.Instructions, len(prog.Instructions))\n\tcopy(insns, prog.Instructions)\n\n\t// Add all direct references of prog to the list of to be linked programs.\n\tpending := make([]string, len(progRefs))\n\tcopy(pending, progRefs)\n\n\t// All references for which we've appended instructions.\n\tlinked := make(map[string]bool)\n\n\t// Iterate all pending references. We can't use a range since pending is\n\t// modified in the body below.\n\tfor len(pending) > 0 {\n\t\tvar ref string\n\t\tref, pending = pending[0], pending[1:]\n\n\t\tif linked[ref] {\n\t\t\t// We've already linked this ref, don't append instructions again.\n\t\t\tcontinue\n\t\t}\n\n\t\tprogRef := progs[ref]\n\t\tif progRef == nil {\n\t\t\t// We don't have instructions that go with this reference. This\n\t\t\t// happens when calling extern functions.\n\t\t\tcontinue\n\t\t}\n\n\t\tinsns = append(insns, progRef.Instructions...)\n\t\tlinked[ref] = true\n\n\t\t// Make sure we link indirect references.\n\t\tpending = append(pending, refs[progRef]...)\n\t}\n\n\treturn insns\n}\n\n// fixupAndValidate is called by the ELF reader right before marshaling the\n// instruction stream. It performs last-minute adjustments to the program and\n// runs some sanity checks before sending it off to the kernel.\nfunc fixupAndValidate(insns asm.Instructions) error {\n\titer := insns.Iterate()\n\tfor iter.Next() {\n\t\tins := iter.Ins\n\n\t\t// Map load was tagged with a Reference, but does not contain a Map pointer.\n\t\tneedsMap := ins.Reference() != \"\" || ins.Metadata.Get(kconfigMetaKey{}) != nil\n\t\tif ins.IsLoadFromMap() && needsMap && ins.Map() == nil {\n\t\t\treturn fmt.Errorf(\"instruction %d: %w\", iter.Index, asm.ErrUnsatisfiedMapReference)\n\t\t}\n\n\t\tfixupProbeReadKernel(ins)\n\t}\n\n\treturn nil\n}\n\n// A constant used to poison calls to non-existent kfuncs.\n//\n// Similar POISON_CALL_KFUNC_BASE in libbpf, except that we use a value lower\n// than 2^28 to fit into a tagged constant.\nconst kfuncCallPoisonBase = 0xdedc0de\n\n// fixupKfuncs loops over all instructions in search for kfunc calls.\n// If at least one is found, the current kernels BTF and module BTFis are searched to set Instruction.Constant\n// and Instruction.Offset to the correct values.\nfunc fixupKfuncs(insns asm.Instructions, cache *btf.Cache) (_ handles, err error) {\n\tcloseOnError := func(c io.Closer) {\n\t\tif err != nil {\n\t\t\tc.Close()\n\t\t}\n\t}\n\n\titer := insns.Iterate()\n\tfor iter.Next() {\n\t\tins := iter.Ins\n\t\tif metadata := ins.Metadata.Get(kfuncMetaKey{}); metadata != nil {\n\t\t\tgoto fixups\n\t\t}\n\t}\n\n\treturn nil, nil\n\nfixups:\n\t// Only load kernel BTF if we found at least one kfunc call. kernelSpec can be\n\t// nil if the kernel does not have BTF, in which case we poison all kfunc\n\t// calls.\n\t_, err = cache.Kernel()\n\t// ErrNotSupportedOnOS wraps ErrNotSupported, check for it first.\n\tif errors.Is(err, internal.ErrNotSupportedOnOS) {\n\t\treturn nil, fmt.Errorf(\"kfuncs are not supported on this platform: %w\", err)\n\t}\n\tif err != nil && !errors.Is(err, ErrNotSupported) {\n\t\treturn nil, err\n\t}\n\n\tfdArray := make(handles, 0)\n\tdefer closeOnError(&fdArray)\n\n\tfor {\n\t\tins := iter.Ins\n\n\t\tmetadata := ins.Metadata.Get(kfuncMetaKey{})\n\t\tif metadata == nil {\n\t\t\tif !iter.Next() {\n\t\t\t\t// break loop if this was the last instruction in the stream.\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\t// check meta, if no meta return err\n\t\tkfm, _ := metadata.(*kfuncMeta)\n\t\tif kfm == nil {\n\t\t\treturn nil, fmt.Errorf(\"kfuncMetaKey doesn't contain kfuncMeta\")\n\t\t}\n\n\t\t// findTargetInKernel returns btf.ErrNotFound if the input btf.Spec is nil.\n\t\ttarget := btf.Type((*btf.Func)(nil))\n\t\tspec, module, err := findTargetInKernel(kfm.Func.Name, &target, cache)\n\t\tif errors.Is(err, btf.ErrNotFound) {\n\t\t\tif kfm.Binding == elf.STB_WEAK {\n\t\t\t\tif ins.IsKfuncCall() {\n\t\t\t\t\t// If the kfunc call is weak and not found, poison the call. Use a\n\t\t\t\t\t// recognizable constant to make it easier to debug.\n\t\t\t\t\tfn, err := asm.BuiltinFuncForPlatform(platform.Native, kfuncCallPoisonBase)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\t\t\t\t\t*ins = fn.Call()\n\t\t\t\t} else if ins.OpCode.IsDWordLoad() {\n\t\t\t\t\t// If the kfunc DWordLoad is weak and not found, set its address to 0.\n\t\t\t\t\tins.Constant = 0\n\t\t\t\t\tins.Src = 0\n\t\t\t\t} else {\n\t\t\t\t\treturn nil, fmt.Errorf(\"only kfunc calls and dword loads may have kfunc metadata\")\n\t\t\t\t}\n\n\t\t\t\titer.Next()\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Error on non-weak kfunc not found.\n\t\t\treturn nil, fmt.Errorf(\"kfunc %q: %w\", kfm.Func.Name, ErrNotSupported)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"finding kfunc in kernel: %w\", err)\n\t\t}\n\n\t\tidx, err := fdArray.add(module)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif err := btf.CheckTypeCompatibility(kfm.Func.Type, target.(*btf.Func).Type); err != nil {\n\t\t\treturn nil, &incompatibleKfuncError{kfm.Func.Name, err}\n\t\t}\n\n\t\tid, err := spec.TypeID(target)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tins.Constant = int64(id)\n\t\tins.Offset = int16(idx)\n\n\t\tif !iter.Next() {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn fdArray, nil\n}\n\ntype incompatibleKfuncError struct {\n\tname string\n\terr  error\n}\n\nfunc (ike *incompatibleKfuncError) Error() string {\n\treturn fmt.Sprintf(\"kfunc %q: %s\", ike.name, ike.err)\n}\n\n// fixupProbeReadKernel replaces calls to bpf_probe_read_{kernel,user}(_str)\n// with bpf_probe_read(_str) on kernels that don't support it yet.\nfunc fixupProbeReadKernel(ins *asm.Instruction) {\n\tif !ins.IsBuiltinCall() {\n\t\treturn\n\t}\n\n\t// Kernel supports bpf_probe_read_kernel, nothing to do.\n\tif haveProbeReadKernel() == nil {\n\t\treturn\n\t}\n\n\tswitch asm.BuiltinFunc(ins.Constant) {\n\tcase asm.FnProbeReadKernel, asm.FnProbeReadUser:\n\t\tins.Constant = int64(asm.FnProbeRead)\n\tcase asm.FnProbeReadKernelStr, asm.FnProbeReadUserStr:\n\t\tins.Constant = int64(asm.FnProbeReadStr)\n\t}\n}\n\n// resolveKconfigReferences creates and populates a .kconfig map if necessary.\n//\n// Returns a nil Map and no error if no references exist.\nfunc resolveKconfigReferences(insns asm.Instructions) (_ *Map, err error) {\n\tcloseOnError := func(c io.Closer) {\n\t\tif err != nil {\n\t\t\tc.Close()\n\t\t}\n\t}\n\n\tvar spec *MapSpec\n\titer := insns.Iterate()\n\tfor iter.Next() {\n\t\tmeta, _ := iter.Ins.Metadata.Get(kconfigMetaKey{}).(*kconfigMeta)\n\t\tif meta != nil {\n\t\t\tspec = meta.Map\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif spec == nil {\n\t\treturn nil, nil\n\t}\n\n\tcpy := spec.Copy()\n\tif err := resolveKconfig(cpy); err != nil {\n\t\treturn nil, err\n\t}\n\n\tkconfig, err := NewMap(cpy)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer closeOnError(kconfig)\n\n\t// Resolve all instructions which load from .kconfig map with actual map\n\t// and offset inside it.\n\titer = insns.Iterate()\n\tfor iter.Next() {\n\t\tmeta, _ := iter.Ins.Metadata.Get(kconfigMetaKey{}).(*kconfigMeta)\n\t\tif meta == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif meta.Map != spec {\n\t\t\treturn nil, fmt.Errorf(\"instruction %d: reference to multiple .kconfig maps is not allowed\", iter.Index)\n\t\t}\n\n\t\tif err := iter.Ins.AssociateMap(kconfig); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"instruction %d: %w\", iter.Index, err)\n\t\t}\n\n\t\t// Encode a map read at the offset of the var in the datasec.\n\t\titer.Ins.Constant = int64(uint64(meta.Offset) << 32)\n\t\titer.Ins.Metadata.Set(kconfigMetaKey{}, nil)\n\t}\n\n\treturn kconfig, nil\n}\n\nfunc resolveKsymReferences(insns asm.Instructions) error {\n\ttype fixup struct {\n\t\t*asm.Instruction\n\t\t*ksymMeta\n\t}\n\n\tvar symbols map[string]uint64\n\tvar fixups []fixup\n\n\titer := insns.Iterate()\n\tfor iter.Next() {\n\t\tins := iter.Ins\n\t\tmeta, _ := ins.Metadata.Get(ksymMetaKey{}).(*ksymMeta)\n\t\tif meta == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif symbols == nil {\n\t\t\tsymbols = make(map[string]uint64)\n\t\t}\n\n\t\tsymbols[meta.Name] = 0\n\t\tfixups = append(fixups, fixup{\n\t\t\titer.Ins, meta,\n\t\t})\n\t}\n\n\tif len(symbols) == 0 {\n\t\treturn nil\n\t}\n\n\terr := kallsyms.AssignAddresses(symbols)\n\t// Tolerate ErrRestrictedKernel during initial lookup, user may have all weak\n\t// ksyms and a fallback path.\n\tif err != nil && !errors.Is(err, ErrRestrictedKernel) {\n\t\treturn fmt.Errorf(\"resolve ksyms: %w\", err)\n\t}\n\n\tvar missing []string\n\tfor _, fixup := range fixups {\n\t\taddr := symbols[fixup.Name]\n\t\t// A weak ksym variable in eBPF C means its resolution is optional.\n\t\tif addr == 0 && fixup.Binding != elf.STB_WEAK {\n\t\t\tif !slices.Contains(missing, fixup.Name) {\n\t\t\t\tmissing = append(missing, fixup.Name)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\tfixup.Constant = int64(addr)\n\t}\n\n\tif len(missing) > 0 {\n\t\tif err != nil {\n\t\t\t// Program contains required ksyms, return the error from above.\n\t\t\treturn fmt.Errorf(\"resolve required ksyms: %s: %w\", strings.Join(missing, \",\"), err)\n\t\t}\n\n\t\treturn fmt.Errorf(\"kernel is missing symbol: %s\", strings.Join(missing, \",\"))\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "linker_test.go",
    "content": "package ebpf\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestFindReferences(t *testing.T) {\n\tprogs := map[string]*ProgramSpec{\n\t\t\"entrypoint\": {\n\t\t\tType: SocketFilter,\n\t\t\tInstructions: asm.Instructions{\n\t\t\t\t// Make sure the call doesn't happen at instruction 0\n\t\t\t\t// to exercise the relative offset calculation.\n\t\t\t\tasm.Mov.Reg(asm.R0, asm.R1),\n\t\t\t\tasm.Call.Label(\"my_func\"),\n\t\t\t\tasm.Return(),\n\t\t\t},\n\t\t\tLicense: \"MIT\",\n\t\t},\n\t\t\"my_other_func\": {\n\t\t\tInstructions: asm.Instructions{\n\t\t\t\tasm.LoadImm(asm.R0, 1337, asm.DWord).WithSymbol(\"my_other_func\"),\n\t\t\t\tasm.Return(),\n\t\t\t},\n\t\t},\n\t\t\"my_func\": {\n\t\t\tInstructions: asm.Instructions{\n\t\t\t\tasm.Call.Label(\"my_other_func\").WithSymbol(\"my_func\"),\n\t\t\t\tasm.Return(),\n\t\t\t},\n\t\t},\n\t}\n\n\tflattenPrograms(progs, []string{\"entrypoint\"})\n\n\tprog, err := newProgram(t, progs[\"entrypoint\"], nil)\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tret := mustRun(t, prog, nil)\n\n\tif ret != 1337 {\n\t\tt.Errorf(\"Expected return code 1337, got %d\", ret)\n\t}\n}\n\nfunc TestForwardFunctionDeclaration(t *testing.T) {\n\tfile := testutils.NativeFile(t, \"testdata/fwd_decl-%s.elf\")\n\tcoll, err := LoadCollectionSpec(file)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tspec := coll.Programs[\"call_fwd\"]\n\n\t// This program calls an unimplemented forward function declaration.\n\t_, err = newProgram(t, spec, nil)\n\tif !errors.Is(err, asm.ErrUnsatisfiedProgramReference) {\n\t\tt.Fatal(\"Expected an error wrapping ErrUnsatisfiedProgramReference, got:\", err)\n\t}\n\n\t// Append the implementation of fwd().\n\tspec.Instructions = append(spec.Instructions,\n\t\tasm.Mov.Imm32(asm.R0, 23).WithSymbol(\"fwd\"),\n\t\tasm.Return(),\n\t)\n\n\t// The body of the subprog we appended does not come with BTF func_infos,\n\t// so the verifier will reject it. Load without BTF.\n\tfor i, ins := range spec.Instructions {\n\t\tif btf.FuncMetadata(&ins) != nil || ins.Source() != nil {\n\t\t\tsym := ins.Symbol()\n\t\t\tref := ins.Reference()\n\t\t\tins.Metadata = asm.Metadata{}\n\t\t\tspec.Instructions[i] = ins.WithSymbol(sym).WithReference(ref)\n\t\t}\n\t}\n\n\tprog, err := newProgram(t, spec, nil)\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tret := mustRun(t, prog, nil)\n\n\tif ret != 23 {\n\t\tt.Fatalf(\"Expected 23, got %d\", ret)\n\t}\n}\n\nfunc TestFlattenInstructionsAllocations(t *testing.T) {\n\tname := \"entrypoint\"\n\tinstructions := asm.Instructions{\n\t\tasm.LoadImm(asm.R0, 0, asm.DWord),\n\t\tasm.Return(),\n\t}\n\tprog := &ProgramSpec{\n\t\tName:         name,\n\t\tInstructions: instructions,\n\t}\n\tprogs := map[string]*ProgramSpec{name: prog}\n\trefs := make(map[*ProgramSpec][]string)\n\n\t// ensure that flattenInstructions does not allocate memory\n\t// if there is no reference for the given program.\n\tallocs := testing.AllocsPerRun(5, func() {\n\t\t_ = flattenInstructions(name, progs, refs)\n\t})\n\tqt.Assert(t, qt.Equals(allocs, float64(0)))\n}\n"
  },
  {
    "path": "map.go",
    "content": "package ebpf\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math/rand\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"slices\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\t\"unsafe\"\n\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/platform\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/sysenc\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\n// Errors returned by Map and MapIterator methods.\nvar (\n\tErrKeyNotExist      = errors.New(\"key does not exist\")\n\tErrKeyExist         = errors.New(\"key already exists\")\n\tErrIterationAborted = errors.New(\"iteration aborted\")\n\tErrMapIncompatible  = errors.New(\"map spec is incompatible with existing map\")\n\n\t// pre-allocating these errors here since they may get called in hot code paths\n\t// and cause unnecessary memory allocations\n\terrMapLookupKeyNotExist = fmt.Errorf(\"lookup: %w\", sysErrKeyNotExist)\n)\n\n// MapOptions control loading a map into the kernel.\ntype MapOptions struct {\n\t// The base path to pin maps in if requested via PinByName.\n\t// Existing maps will be re-used if they are compatible, otherwise an\n\t// error is returned.\n\tPinPath        string\n\tLoadPinOptions LoadPinOptions\n}\n\n// MapID represents the unique ID of an eBPF map\ntype MapID = sys.MapID\n\n// MapSpec defines a Map.\ntype MapSpec struct {\n\t// Name is passed to the kernel as a debug aid.\n\t//\n\t// Unsupported characters will be stripped.\n\tName       string\n\tType       MapType\n\tKeySize    uint32\n\tValueSize  uint32\n\tMaxEntries uint32\n\n\t// Flags is passed to the kernel and specifies additional map\n\t// creation attributes.\n\tFlags uint32\n\n\t// Automatically pin and load a map from MapOptions.PinPath.\n\t// Generates an error if an existing pinned map is incompatible with the MapSpec.\n\tPinning PinType\n\n\t// Specify numa node during map creation\n\t// (effective only if sys.BPF_F_NUMA_NODE flag is set,\n\t// which can be imported from golang.org/x/sys/unix)\n\tNumaNode uint32\n\n\t// The initial contents of the map. May be nil.\n\tContents []MapKV\n\n\t// InnerMap is used as a template for ArrayOfMaps and HashOfMaps\n\tInnerMap *MapSpec\n\n\t// MapExtra is an opaque field whose meaning is map-specific.\n\t//\n\t// Available from 5.16.\n\tMapExtra uint64\n\n\t// Extra trailing bytes found in the ELF map definition when using structs\n\t// larger than libbpf's bpf_map_def. nil if no trailing bytes were present.\n\t// Must be nil or empty before instantiating the MapSpec into a Map.\n\tExtra *bytes.Reader\n\n\t// The key and value type of this map. May be nil.\n\tKey, Value btf.Type\n\n\t// Tags is a list of btf_decl_tag attributes set on the map definition.\n\t//\n\t// Decorate a map definition with `__attribute__((btf_decl_tag(\"foo\")))`.\n\tTags []string\n}\n\nfunc (ms *MapSpec) String() string {\n\treturn fmt.Sprintf(\"%s(keySize=%d, valueSize=%d, maxEntries=%d, flags=%d)\", ms.Type, ms.KeySize, ms.ValueSize, ms.MaxEntries, ms.Flags)\n}\n\n// Copy returns a copy of the spec.\n//\n// MapSpec.Contents is a shallow copy.\nfunc (ms *MapSpec) Copy() *MapSpec {\n\tif ms == nil {\n\t\treturn nil\n\t}\n\n\tcpy := *ms\n\tcpy.Contents = slices.Clone(cpy.Contents)\n\tcpy.Key = btf.Copy(cpy.Key)\n\tcpy.Value = btf.Copy(cpy.Value)\n\tcpy.Tags = slices.Clone(cpy.Tags)\n\n\tif cpy.InnerMap == ms {\n\t\tcpy.InnerMap = &cpy\n\t} else {\n\t\tcpy.InnerMap = ms.InnerMap.Copy()\n\t}\n\n\tif cpy.Extra != nil {\n\t\textra := *cpy.Extra\n\t\tcpy.Extra = &extra\n\t}\n\n\treturn &cpy\n}\n\n// fixupMagicFields fills fields of MapSpec which are usually\n// left empty in ELF or which depend on runtime information.\n//\n// The method doesn't modify Spec, instead returning a copy.\n// The copy is only performed if fixups are necessary, so callers mustn't mutate\n// the returned spec.\nfunc (spec *MapSpec) fixupMagicFields() (*MapSpec, error) {\n\tswitch {\n\tcase spec.Type.canStoreMap():\n\t\tif spec.ValueSize != 0 && spec.ValueSize != 4 {\n\t\t\treturn nil, errors.New(\"ValueSize must be zero or four for map of map\")\n\t\t}\n\n\t\tspec = spec.Copy()\n\t\tspec.ValueSize = 4\n\n\tcase spec.Type == PerfEventArray:\n\t\tif spec.KeySize != 0 && spec.KeySize != 4 {\n\t\t\treturn nil, errors.New(\"KeySize must be zero or four for perf event array\")\n\t\t}\n\n\t\tif spec.ValueSize != 0 && spec.ValueSize != 4 {\n\t\t\treturn nil, errors.New(\"ValueSize must be zero or four for perf event array\")\n\t\t}\n\n\t\tspec = spec.Copy()\n\t\tspec.KeySize = 4\n\t\tspec.ValueSize = 4\n\n\t\tn, err := PossibleCPU()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"fixup perf event array: %w\", err)\n\t\t}\n\n\t\tif n := uint32(n); spec.MaxEntries == 0 || spec.MaxEntries > n {\n\t\t\t// MaxEntries should be zero most of the time, but there is code\n\t\t\t// out there which hardcodes large constants. Clamp the number\n\t\t\t// of entries to the number of CPUs at most. Allow creating maps with\n\t\t\t// less than n items since some kernel selftests relied on this\n\t\t\t// behaviour in the past.\n\t\t\tspec.MaxEntries = n\n\t\t}\n\n\tcase spec.Type == CPUMap:\n\t\tn, err := PossibleCPU()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"fixup cpu map: %w\", err)\n\t\t}\n\n\t\tif n := uint32(n); spec.MaxEntries == 0 || spec.MaxEntries > n {\n\t\t\t// Perform clamping similar to PerfEventArray.\n\t\t\tspec.MaxEntries = n\n\t\t}\n\t}\n\n\treturn spec, nil\n}\n\n// dataSection returns the contents of a datasec if the MapSpec represents one.\nfunc (ms *MapSpec) dataSection() ([]byte, error) {\n\tif n := len(ms.Contents); n != 1 {\n\t\treturn nil, fmt.Errorf(\"expected one key, found %d\", n)\n\t}\n\n\tkv := ms.Contents[0]\n\tif key, ok := ms.Contents[0].Key.(uint32); !ok || key != 0 {\n\t\treturn nil, fmt.Errorf(\"expected contents to have key 0\")\n\t}\n\n\tvalue, ok := kv.Value.([]byte)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"value at first map key is %T, not []byte\", kv.Value)\n\t}\n\n\treturn value, nil\n}\n\n// updateDataSection copies the values of variables into MapSpec.Contents[0].Value.\n//\n// Only variables declared in sectionName will be updated.\nfunc (ms *MapSpec) updateDataSection(vars map[string]*VariableSpec, sectionName string) error {\n\tvar specs []*VariableSpec\n\tfor _, vs := range vars {\n\t\tif vs.SectionName != sectionName {\n\t\t\tcontinue\n\t\t}\n\n\t\tspecs = append(specs, vs)\n\t}\n\n\tif len(specs) == 0 {\n\t\treturn nil\n\t}\n\n\tdata, err := ms.dataSection()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Do not modify the original data slice, ms.Contents is a shallow copy.\n\tdata = slices.Clone(data)\n\n\tslices.SortFunc(specs, func(a, b *VariableSpec) int {\n\t\treturn int(int64(a.Offset) - int64(b.Offset))\n\t})\n\n\toffset := uint32(0)\n\tfor _, v := range specs {\n\t\tif v.Offset < offset {\n\t\t\treturn fmt.Errorf(\"variable %s (offset %d) overlaps with previous variable (offset %d)\", v.Name, v.Offset, offset)\n\t\t}\n\n\t\tend := v.Offset + v.Size()\n\t\tif int(end) > len(data) {\n\t\t\treturn fmt.Errorf(\"variable %s exceeds map size\", v.Name)\n\t\t}\n\n\t\tcopy(data[v.Offset:end], v.Value)\n\t\toffset = end\n\t}\n\n\tms.Contents = []MapKV{{Key: uint32(0), Value: data}}\n\treturn nil\n}\n\nfunc (ms *MapSpec) readOnly() bool {\n\treturn (ms.Flags & sys.BPF_F_RDONLY_PROG) > 0\n}\n\nfunc (ms *MapSpec) writeOnly() bool {\n\treturn (ms.Flags & sys.BPF_F_WRONLY_PROG) > 0\n}\n\n// MapKV is used to initialize the contents of a Map.\ntype MapKV struct {\n\tKey   interface{}\n\tValue interface{}\n}\n\n// Compatible returns nil if an existing map may be used instead of creating\n// one from the spec.\n//\n// Returns an error wrapping [ErrMapIncompatible] otherwise.\nfunc (ms *MapSpec) Compatible(m *Map) error {\n\tms, err := ms.fixupMagicFields()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdiffs := []string{}\n\tif m.typ != ms.Type {\n\t\tdiffs = append(diffs, fmt.Sprintf(\"Type: %s changed to %s\", m.typ, ms.Type))\n\t}\n\tif m.keySize != ms.KeySize {\n\t\tdiffs = append(diffs, fmt.Sprintf(\"KeySize: %d changed to %d\", m.keySize, ms.KeySize))\n\t}\n\tif m.valueSize != ms.ValueSize {\n\t\tdiffs = append(diffs, fmt.Sprintf(\"ValueSize: %d changed to %d\", m.valueSize, ms.ValueSize))\n\t}\n\tif m.maxEntries != ms.MaxEntries {\n\t\tdiffs = append(diffs, fmt.Sprintf(\"MaxEntries: %d changed to %d\", m.maxEntries, ms.MaxEntries))\n\t}\n\n\tflags := ms.Flags\n\tif ms.Type == DevMap || ms.Type == DevMapHash {\n\t\t// As of 0cdbb4b09a06 (\"devmap: Allow map lookups from eBPF\")\n\t\t// BPF_F_RDONLY_PROG is set unconditionally for devmaps. Explicitly\n\t\t// allow this mismatch.\n\t\tflags |= (m.flags & sys.BPF_F_RDONLY_PROG)\n\t}\n\n\tif m.flags != flags {\n\t\tdiffs = append(diffs, fmt.Sprintf(\"Flags: %d changed to %d\", m.flags, flags))\n\t}\n\n\tif len(diffs) == 0 {\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"%s: %w\", strings.Join(diffs, \", \"), ErrMapIncompatible)\n}\n\n// Map represents a Map file descriptor.\n//\n// It is not safe to close a map which is used by other goroutines.\n//\n// Methods which take interface{} arguments by default encode\n// them using binary.Read/Write in the machine's native endianness.\n//\n// Implement encoding.BinaryMarshaler or encoding.BinaryUnmarshaler\n// if you require custom encoding.\ntype Map struct {\n\tname       string\n\tfd         *sys.FD\n\ttyp        MapType\n\tkeySize    uint32\n\tvalueSize  uint32\n\tmaxEntries uint32\n\tflags      uint32\n\tpinnedPath string\n\t// Per CPU maps return values larger than the size in the spec\n\tfullValueSize int\n\n\tmemory *Memory\n}\n\n// NewMapFromFD creates a [Map] around a raw fd.\n//\n// You should not use fd after calling this function.\n//\n// Requires at least Linux 4.13.\nfunc NewMapFromFD(fd int) (*Map, error) {\n\tf, err := sys.NewFD(fd)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn newMapFromFD(f)\n}\n\nfunc newMapFromFD(fd *sys.FD) (*Map, error) {\n\tinfo, err := minimalMapInfoFromFd(fd)\n\tif err != nil {\n\t\tfd.Close()\n\t\treturn nil, fmt.Errorf(\"get map info: %w\", err)\n\t}\n\n\treturn newMapFromParts(fd, info.Name, info.Type, info.KeySize, info.ValueSize, info.MaxEntries, info.Flags)\n}\n\n// NewMap creates a new Map.\n//\n// It's equivalent to calling NewMapWithOptions with default options.\nfunc NewMap(spec *MapSpec) (*Map, error) {\n\treturn NewMapWithOptions(spec, MapOptions{})\n}\n\n// NewMapWithOptions creates a new Map.\n//\n// Creating a map for the first time will perform feature detection\n// by creating small, temporary maps.\n//\n// The caller is responsible for ensuring the process' rlimit is set\n// sufficiently high for locking memory during map creation. This can be done\n// by calling rlimit.RemoveMemlock() prior to calling NewMapWithOptions.\n//\n// May return an error wrapping ErrMapIncompatible.\nfunc NewMapWithOptions(spec *MapSpec, opts MapOptions) (*Map, error) {\n\tm, err := newMapWithOptions(spec, opts, btf.NewCache())\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"creating map: %w\", err)\n\t}\n\n\tif err := m.finalize(spec); err != nil {\n\t\tm.Close()\n\t\treturn nil, fmt.Errorf(\"populating map: %w\", err)\n\t}\n\n\treturn m, nil\n}\n\nfunc newMapWithOptions(spec *MapSpec, opts MapOptions, c *btf.Cache) (_ *Map, err error) {\n\tcloseOnError := func(c io.Closer) {\n\t\tif err != nil {\n\t\t\tc.Close()\n\t\t}\n\t}\n\n\tswitch spec.Pinning {\n\tcase PinByName:\n\t\tif spec.Name == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"pin by name: missing Name\")\n\t\t}\n\n\t\tif opts.PinPath == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"pin by name: missing MapOptions.PinPath\")\n\t\t}\n\n\t\tpath := filepath.Join(opts.PinPath, spec.Name)\n\t\tm, err := LoadPinnedMap(path, &opts.LoadPinOptions)\n\t\tif errors.Is(err, unix.ENOENT) {\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"load pinned map: %w\", err)\n\t\t}\n\t\tdefer closeOnError(m)\n\n\t\tif err := spec.Compatible(m); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"use pinned map %s: %w\", spec.Name, err)\n\t\t}\n\n\t\treturn m, nil\n\n\tcase PinNone:\n\t\t// Nothing to do here\n\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"pin type %d: %w\", int(spec.Pinning), ErrNotSupported)\n\t}\n\n\tvar innerFd *sys.FD\n\tif spec.Type.canStoreMap() {\n\t\tif spec.InnerMap == nil {\n\t\t\treturn nil, fmt.Errorf(\"%s requires InnerMap\", spec.Type)\n\t\t}\n\n\t\tif spec.InnerMap.Pinning != PinNone {\n\t\t\treturn nil, errors.New(\"inner maps cannot be pinned\")\n\t\t}\n\n\t\ttemplate, err := spec.InnerMap.createMap(nil, c)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"inner map: %w\", err)\n\t\t}\n\t\tdefer template.Close()\n\n\t\t// Intentionally skip populating and freezing (finalizing)\n\t\t// the inner map template since it will be removed shortly.\n\n\t\tinnerFd = template.fd\n\t}\n\n\tm, err := spec.createMap(innerFd, c)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer closeOnError(m)\n\n\tif spec.Pinning == PinByName {\n\t\tpath := filepath.Join(opts.PinPath, spec.Name)\n\t\tif err := m.Pin(path); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"pin map to %s: %w\", path, err)\n\t\t}\n\t}\n\n\treturn m, nil\n}\n\n// Memory returns a memory-mapped region for the Map. The Map must have been\n// created with the BPF_F_MMAPABLE flag. Repeated calls to Memory return the\n// same mapping. Callers are responsible for coordinating access to Memory.\nfunc (m *Map) Memory() (*Memory, error) {\n\tif m.memory != nil {\n\t\treturn m.memory, nil\n\t}\n\n\tif m.flags&sys.BPF_F_MMAPABLE == 0 {\n\t\treturn nil, fmt.Errorf(\"Map was not created with the BPF_F_MMAPABLE flag: %w\", ErrNotSupported)\n\t}\n\n\tsize, err := m.memorySize()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tmm, err := newMemory(m.FD(), size)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"creating new Memory: %w\", err)\n\t}\n\n\tm.memory = mm\n\n\treturn mm, nil\n}\n\n// unsafeMemory returns a heap-mapped memory region for the Map. The Map must\n// have been created with the BPF_F_MMAPABLE flag. Repeated calls to Memory\n// return the same mapping. Callers are responsible for coordinating access to\n// Memory.\nfunc (m *Map) unsafeMemory() (*Memory, error) {\n\tif m.memory != nil {\n\t\tif !m.memory.heap {\n\t\t\treturn nil, errors.New(\"unsafeMemory would return existing non-heap memory\")\n\t\t}\n\n\t\treturn m.memory, nil\n\t}\n\n\tif m.flags&sys.BPF_F_MMAPABLE == 0 {\n\t\treturn nil, fmt.Errorf(\"Map was not created with the BPF_F_MMAPABLE flag: %w\", ErrNotSupported)\n\t}\n\n\tsize, err := m.memorySize()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tmm, err := newUnsafeMemory(m.FD(), size)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"creating new Memory: %w\", err)\n\t}\n\n\tm.memory = mm\n\n\treturn mm, nil\n}\n\nfunc (m *Map) memorySize() (int, error) {\n\tswitch m.Type() {\n\tcase Array:\n\t\t// In Arrays, values are always laid out on 8-byte boundaries regardless of\n\t\t// architecture. Multiply by MaxEntries and align the result to the host's\n\t\t// page size.\n\t\tsize := int(internal.Align(m.ValueSize(), 8) * m.MaxEntries())\n\t\tsize = internal.Align(size, os.Getpagesize())\n\t\treturn size, nil\n\tcase Arena:\n\t\t// For Arenas, MaxEntries denotes the maximum number of pages available to\n\t\t// the arena.\n\t\treturn int(m.MaxEntries()) * os.Getpagesize(), nil\n\t}\n\n\treturn 0, fmt.Errorf(\"determine memory size of map type %s: %w\", m.Type(), ErrNotSupported)\n}\n\n// createMap validates the spec's properties and creates the map in the kernel\n// using the given opts. It does not populate or freeze the map.\nfunc (spec *MapSpec) createMap(inner *sys.FD, c *btf.Cache) (_ *Map, err error) {\n\tcloseOnError := func(closer io.Closer) {\n\t\tif err != nil {\n\t\t\tcloser.Close()\n\t\t}\n\t}\n\n\t// Kernels 4.13 through 5.4 used a struct bpf_map_def that contained\n\t// additional 'inner_map_idx' and later 'numa_node' fields.\n\t// In order to support loading these definitions, tolerate the presence of\n\t// extra bytes, but require them to be zeroes.\n\tif spec.Extra != nil {\n\t\tif _, err := io.Copy(internal.DiscardZeroes{}, spec.Extra); err != nil {\n\t\t\treturn nil, errors.New(\"extra contains unhandled non-zero bytes, drain before creating map\")\n\t\t}\n\t}\n\n\tspec, err = spec.fixupMagicFields()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tp, sysMapType := platform.DecodeConstant(spec.Type)\n\tif p != platform.Native {\n\t\treturn nil, fmt.Errorf(\"map type %s (%s): %w\", spec.Type, p, internal.ErrNotSupportedOnOS)\n\t}\n\n\tattr := sys.MapCreateAttr{\n\t\tMapName:    maybeFillObjName(spec.Name),\n\t\tMapType:    sys.MapType(sysMapType),\n\t\tKeySize:    spec.KeySize,\n\t\tValueSize:  spec.ValueSize,\n\t\tMaxEntries: spec.MaxEntries,\n\t\tMapFlags:   spec.Flags,\n\t\tNumaNode:   spec.NumaNode,\n\t\tMapExtra:   spec.MapExtra,\n\t}\n\n\tif inner != nil {\n\t\tattr.InnerMapFd = inner.Uint()\n\t}\n\n\tif spec.Key != nil || spec.Value != nil {\n\t\thandle, keyTypeID, valueTypeID, err := btf.MarshalMapKV(spec.Key, spec.Value)\n\t\tif err != nil && !errors.Is(err, btf.ErrNotSupported) {\n\t\t\treturn nil, fmt.Errorf(\"load BTF: %w\", err)\n\t\t}\n\n\t\tif handle != nil {\n\t\t\tdefer handle.Close()\n\n\t\t\t// Use BTF k/v during map creation.\n\t\t\tattr.BtfFd = uint32(handle.FD())\n\t\t\tattr.BtfKeyTypeId = keyTypeID\n\t\t\tattr.BtfValueTypeId = valueTypeID\n\t\t}\n\n\t\tif spec.Type == StructOpsMap {\n\t\t\tif handle == nil {\n\t\t\t\treturn nil, fmt.Errorf(\"struct_ops requires BTF\")\n\t\t\t}\n\n\t\t\tlocalValue, ok := btf.As[*btf.Struct](spec.Value)\n\t\t\tif !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"struct_ops: value must be struct\")\n\t\t\t}\n\n\t\t\ttargetValue, targetID, module, err := structOpsFindTarget(localValue, c)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"struct_ops: %w\", err)\n\t\t\t}\n\t\t\tdefer module.Close()\n\n\t\t\tspec = spec.Copy()\n\t\t\tspec.ValueSize = targetValue.Size\n\n\t\t\tattr.ValueSize = targetValue.Size\n\t\t\tattr.BtfVmlinuxValueTypeId = targetID\n\n\t\t\tif module != nil {\n\t\t\t\t// BPF_F_VTYPE_BTF_OBJ_FD is required if the type comes from a module\n\t\t\t\tattr.MapFlags |= sys.BPF_F_VTYPE_BTF_OBJ_FD\n\t\t\t\t// set FD for the kernel module\n\t\t\t\tattr.ValueTypeBtfObjFd = int32(module.FD())\n\t\t\t}\n\n\t\t\t// StructOpsMap forbids passing BtfKeyTypeId or BtfValueTypeId, but\n\t\t\t// requires BtfFd. Do the simple thing and just zero out the fields.\n\t\t\t// See https://github.com/torvalds/linux/blob/9b332cece987ee1790b2ed4c989e28162fa47860/kernel/bpf/syscall.c#L1382-L1384\n\t\t\tattr.BtfKeyTypeId = 0\n\t\t\tattr.BtfValueTypeId = 0\n\t\t}\n\t}\n\n\tfd, err := sys.MapCreate(&attr)\n\n\t// Some map types don't support BTF k/v in earlier kernel versions.\n\t// Remove BTF metadata and retry map creation.\n\tif (errors.Is(err, sys.ENOTSUPP) || errors.Is(err, unix.EINVAL)) && attr.BtfFd != 0 {\n\t\tattr.BtfFd, attr.BtfKeyTypeId, attr.BtfValueTypeId = 0, 0, 0\n\t\tfd, err = sys.MapCreate(&attr)\n\t}\n\tif err != nil {\n\t\treturn nil, handleMapCreateError(attr, spec, err)\n\t}\n\n\tdefer closeOnError(fd)\n\tm, err := newMapFromParts(fd, spec.Name, spec.Type, spec.KeySize, spec.ValueSize, spec.MaxEntries, spec.Flags)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"map create: %w\", err)\n\t}\n\treturn m, nil\n}\n\nfunc handleMapCreateError(attr sys.MapCreateAttr, spec *MapSpec, err error) error {\n\tif platform.IsWindows {\n\t\tif errors.Is(err, unix.EINVAL) && attr.MapFlags != 0 {\n\t\t\treturn fmt.Errorf(\"map create: flags: %w\", internal.ErrNotSupportedOnOS)\n\t\t}\n\n\t\treturn err\n\t}\n\n\tif errors.Is(err, unix.EPERM) {\n\t\treturn fmt.Errorf(\"map create: %w (MEMLOCK may be too low, consider rlimit.RemoveMemlock)\", err)\n\t}\n\tif errors.Is(err, unix.EINVAL) {\n\t\tif spec.MaxEntries == 0 {\n\t\t\treturn fmt.Errorf(\"map create: %w (MaxEntries may be incorrectly set to zero)\", err)\n\t\t}\n\t\tif spec.Type == UnspecifiedMap {\n\t\t\treturn fmt.Errorf(\"map create: cannot use type %s\", UnspecifiedMap)\n\t\t}\n\t\tif spec.Flags&sys.BPF_F_NO_PREALLOC != 0 && !spec.Type.mustHaveNoPrealloc() {\n\t\t\treturn fmt.Errorf(\"map create: %w (BPF_F_NO_PREALLOC flag may be incompatible with map type %s)\", err, spec.Type)\n\t\t}\n\t\tif spec.Flags&sys.BPF_F_NO_PREALLOC == 0 && spec.Type.mustHaveNoPrealloc() {\n\t\t\treturn fmt.Errorf(\"map create: %w (BPF_F_NO_PREALLOC flag may need to be set for map type %s)\", err, spec.Type)\n\t\t}\n\t}\n\n\tif spec.Type.canStoreMap() {\n\t\tif haveFeatErr := haveNestedMaps(); haveFeatErr != nil {\n\t\t\treturn fmt.Errorf(\"map create: %w\", haveFeatErr)\n\t\t}\n\t}\n\n\tif spec.readOnly() || spec.writeOnly() {\n\t\tif haveFeatErr := haveMapMutabilityModifiers(); haveFeatErr != nil {\n\t\t\treturn fmt.Errorf(\"map create: %w\", haveFeatErr)\n\t\t}\n\t}\n\tif spec.Flags&sys.BPF_F_MMAPABLE > 0 {\n\t\tif haveFeatErr := haveMmapableMaps(); haveFeatErr != nil {\n\t\t\treturn fmt.Errorf(\"map create: %w\", haveFeatErr)\n\t\t}\n\t}\n\tif spec.Flags&sys.BPF_F_INNER_MAP > 0 {\n\t\tif haveFeatErr := haveInnerMaps(); haveFeatErr != nil {\n\t\t\treturn fmt.Errorf(\"map create: %w\", haveFeatErr)\n\t\t}\n\t}\n\tif spec.Flags&sys.BPF_F_NO_PREALLOC > 0 {\n\t\tif haveFeatErr := haveNoPreallocMaps(); haveFeatErr != nil {\n\t\t\treturn fmt.Errorf(\"map create: %w\", haveFeatErr)\n\t\t}\n\t}\n\t// BPF_MAP_TYPE_RINGBUF's max_entries must be a power-of-2 multiple of kernel's page size.\n\tif errors.Is(err, unix.EINVAL) &&\n\t\t(attr.MapType == sys.BPF_MAP_TYPE_RINGBUF || attr.MapType == sys.BPF_MAP_TYPE_USER_RINGBUF) {\n\t\tpageSize := uint32(os.Getpagesize())\n\t\tmaxEntries := attr.MaxEntries\n\t\tif maxEntries%pageSize != 0 || !internal.IsPow(maxEntries) {\n\t\t\treturn fmt.Errorf(\"map create: %w (ring map size %d not a multiple of page size %d)\", err, maxEntries, pageSize)\n\t\t}\n\t}\n\n\treturn fmt.Errorf(\"map create: %w\", err)\n}\n\n// newMapFromParts allocates and returns a new Map structure.\n// Sets the fullValueSize on per-CPU maps.\nfunc newMapFromParts(fd *sys.FD, name string, typ MapType, keySize, valueSize, maxEntries, flags uint32) (*Map, error) {\n\tm := &Map{\n\t\tname,\n\t\tfd,\n\t\ttyp,\n\t\tkeySize,\n\t\tvalueSize,\n\t\tmaxEntries,\n\t\tflags,\n\t\t\"\",\n\t\tint(valueSize),\n\t\tnil,\n\t}\n\n\tif !typ.hasPerCPUValue() {\n\t\treturn m, nil\n\t}\n\n\tpossibleCPUs, err := PossibleCPU()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tm.fullValueSize = int(internal.Align(valueSize, 8)) * possibleCPUs\n\treturn m, nil\n}\n\nfunc (m *Map) String() string {\n\tif m.name != \"\" {\n\t\treturn fmt.Sprintf(\"%s(%s)#%v\", m.typ, m.name, m.fd)\n\t}\n\treturn fmt.Sprintf(\"%s#%v\", m.typ, m.fd)\n}\n\n// Type returns the underlying type of the map.\nfunc (m *Map) Type() MapType {\n\treturn m.typ\n}\n\n// KeySize returns the size of the map key in bytes.\nfunc (m *Map) KeySize() uint32 {\n\treturn m.keySize\n}\n\n// ValueSize returns the size of the map value in bytes.\nfunc (m *Map) ValueSize() uint32 {\n\treturn m.valueSize\n}\n\n// MaxEntries returns the maximum number of elements the map can hold.\nfunc (m *Map) MaxEntries() uint32 {\n\treturn m.maxEntries\n}\n\n// Flags returns the flags of the map.\nfunc (m *Map) Flags() uint32 {\n\treturn m.flags\n}\n\n// Info returns metadata about the map. This was first introduced in Linux 4.5,\n// but newer kernels support more MapInfo fields with the introduction of more\n// features. See [MapInfo] and its methods for more details.\n//\n// Returns an error wrapping [ErrNotSupported] if the kernel supports neither\n// BPF_OBJ_GET_INFO_BY_FD nor reading map information from /proc/self/fdinfo.\nfunc (m *Map) Info() (*MapInfo, error) {\n\treturn newMapInfoFromFd(m.fd)\n}\n\n// Handle returns a reference to the Map's type information in the kernel.\n//\n// Returns [ErrNotSupported] if the kernel has no BTF support, or if there is no\n// BTF associated with the Map.\nfunc (m *Map) Handle() (*btf.Handle, error) {\n\tinfo, err := m.Info()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tid, ok := info.BTFID()\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"map %s: retrieve BTF ID: %w\", m, ErrNotSupported)\n\t}\n\n\treturn btf.NewHandleFromID(id)\n}\n\n// MapLookupFlags controls the behaviour of the map lookup calls.\ntype MapLookupFlags uint64\n\n// LookupLock look up the value of a spin-locked map.\nconst LookupLock MapLookupFlags = sys.BPF_F_LOCK\n\n// Lookup retrieves a value from a Map.\n//\n// Calls Close() on valueOut if it is of type **Map or **Program,\n// and *valueOut is not nil.\n//\n// Returns an error if the key doesn't exist, see ErrKeyNotExist.\nfunc (m *Map) Lookup(key, valueOut interface{}) error {\n\treturn m.LookupWithFlags(key, valueOut, 0)\n}\n\n// LookupWithFlags retrieves a value from a Map with flags.\n//\n// Passing LookupLock flag will look up the value of a spin-locked\n// map without returning the lock. This must be specified if the\n// elements contain a spinlock.\n//\n// Calls Close() on valueOut if it is of type **Map or **Program,\n// and *valueOut is not nil.\n//\n// Returns an error if the key doesn't exist, see ErrKeyNotExist.\nfunc (m *Map) LookupWithFlags(key, valueOut interface{}, flags MapLookupFlags) error {\n\tif m.typ.hasPerCPUValue() {\n\t\treturn m.lookupPerCPU(key, valueOut, flags)\n\t}\n\n\tvalueBytes := makeMapSyscallOutput(valueOut, m.fullValueSize)\n\tif err := m.lookup(key, valueBytes.Pointer(), flags); err != nil {\n\t\treturn err\n\t}\n\n\treturn m.unmarshalValue(valueOut, valueBytes)\n}\n\n// LookupAndDelete retrieves and deletes a value from a Map.\n//\n// Returns ErrKeyNotExist if the key doesn't exist.\nfunc (m *Map) LookupAndDelete(key, valueOut interface{}) error {\n\treturn m.LookupAndDeleteWithFlags(key, valueOut, 0)\n}\n\n// LookupAndDeleteWithFlags retrieves and deletes a value from a Map.\n//\n// Passing LookupLock flag will look up and delete the value of a spin-locked\n// map without returning the lock. This must be specified if the elements\n// contain a spinlock.\n//\n// Returns ErrKeyNotExist if the key doesn't exist.\nfunc (m *Map) LookupAndDeleteWithFlags(key, valueOut interface{}, flags MapLookupFlags) error {\n\tif m.typ.hasPerCPUValue() {\n\t\treturn m.lookupAndDeletePerCPU(key, valueOut, flags)\n\t}\n\n\tvalueBytes := makeMapSyscallOutput(valueOut, m.fullValueSize)\n\tif err := m.lookupAndDelete(key, valueBytes.Pointer(), flags); err != nil {\n\t\treturn err\n\t}\n\treturn m.unmarshalValue(valueOut, valueBytes)\n}\n\n// LookupBytes gets a value from Map.\n//\n// Returns a nil value if a key doesn't exist.\nfunc (m *Map) LookupBytes(key interface{}) ([]byte, error) {\n\tvalueBytes := make([]byte, m.fullValueSize)\n\tvaluePtr := sys.UnsafeSlicePointer(valueBytes)\n\n\terr := m.lookup(key, valuePtr, 0)\n\tif errors.Is(err, ErrKeyNotExist) {\n\t\treturn nil, nil\n\t}\n\n\treturn valueBytes, err\n}\n\nfunc (m *Map) lookupPerCPU(key, valueOut any, flags MapLookupFlags) error {\n\tslice, err := ensurePerCPUSlice(valueOut)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvalueBytes := make([]byte, m.fullValueSize)\n\tif err := m.lookup(key, sys.UnsafeSlicePointer(valueBytes), flags); err != nil {\n\t\treturn err\n\t}\n\treturn unmarshalPerCPUValue(slice, int(m.valueSize), valueBytes)\n}\n\nfunc (m *Map) lookup(key interface{}, valueOut sys.Pointer, flags MapLookupFlags) error {\n\tkeyPtr, err := m.marshalKey(key)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"can't marshal key: %w\", err)\n\t}\n\n\tattr := sys.MapLookupElemAttr{\n\t\tMapFd: m.fd.Uint(),\n\t\tKey:   keyPtr,\n\t\tValue: valueOut,\n\t\tFlags: uint64(flags),\n\t}\n\n\tif err = sys.MapLookupElem(&attr); err != nil {\n\t\tif errors.Is(err, unix.ENOENT) {\n\t\t\treturn errMapLookupKeyNotExist\n\t\t}\n\t\treturn fmt.Errorf(\"lookup: %w\", wrapMapError(err))\n\t}\n\treturn nil\n}\n\nfunc (m *Map) lookupAndDeletePerCPU(key, valueOut any, flags MapLookupFlags) error {\n\tslice, err := ensurePerCPUSlice(valueOut)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvalueBytes := make([]byte, m.fullValueSize)\n\tif err := m.lookupAndDelete(key, sys.UnsafeSlicePointer(valueBytes), flags); err != nil {\n\t\treturn err\n\t}\n\treturn unmarshalPerCPUValue(slice, int(m.valueSize), valueBytes)\n}\n\n// ensurePerCPUSlice allocates a slice for a per-CPU value if necessary.\nfunc ensurePerCPUSlice(sliceOrPtr any) (any, error) {\n\tsliceOrPtrType := reflect.TypeOf(sliceOrPtr)\n\tif sliceOrPtrType.Kind() == reflect.Slice {\n\t\t// The target is a slice, the caller is responsible for ensuring that\n\t\t// size is correct.\n\t\treturn sliceOrPtr, nil\n\t}\n\n\tslicePtrType := sliceOrPtrType\n\tif slicePtrType.Kind() != reflect.Ptr || slicePtrType.Elem().Kind() != reflect.Slice {\n\t\treturn nil, fmt.Errorf(\"per-cpu value requires a slice or a pointer to slice\")\n\t}\n\n\tpossibleCPUs, err := PossibleCPU()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsliceType := slicePtrType.Elem()\n\tslice := reflect.MakeSlice(sliceType, possibleCPUs, possibleCPUs)\n\n\tsliceElemType := sliceType.Elem()\n\tsliceElemIsPointer := sliceElemType.Kind() == reflect.Ptr\n\treflect.ValueOf(sliceOrPtr).Elem().Set(slice)\n\tif !sliceElemIsPointer {\n\t\treturn slice.Interface(), nil\n\t}\n\tsliceElemType = sliceElemType.Elem()\n\n\tfor i := 0; i < possibleCPUs; i++ {\n\t\tnewElem := reflect.New(sliceElemType)\n\t\tslice.Index(i).Set(newElem)\n\t}\n\n\treturn slice.Interface(), nil\n}\n\nfunc (m *Map) lookupAndDelete(key any, valuePtr sys.Pointer, flags MapLookupFlags) error {\n\tkeyPtr, err := m.marshalKey(key)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"can't marshal key: %w\", err)\n\t}\n\n\tattr := sys.MapLookupAndDeleteElemAttr{\n\t\tMapFd: m.fd.Uint(),\n\t\tKey:   keyPtr,\n\t\tValue: valuePtr,\n\t\tFlags: uint64(flags),\n\t}\n\n\tif err := sys.MapLookupAndDeleteElem(&attr); err != nil {\n\t\treturn fmt.Errorf(\"lookup and delete: %w\", wrapMapError(err))\n\t}\n\n\treturn nil\n}\n\n// MapUpdateFlags controls the behaviour of the Map.Update call.\n//\n// The exact semantics depend on the specific MapType.\ntype MapUpdateFlags uint64\n\nconst (\n\t// UpdateAny creates a new element or update an existing one.\n\tUpdateAny MapUpdateFlags = iota\n\t// UpdateNoExist creates a new element.\n\tUpdateNoExist MapUpdateFlags = 1 << (iota - 1)\n\t// UpdateExist updates an existing element.\n\tUpdateExist\n\t// UpdateLock updates elements under bpf_spin_lock.\n\tUpdateLock\n)\n\n// Put replaces or creates a value in map.\n//\n// It is equivalent to calling Update with UpdateAny.\nfunc (m *Map) Put(key, value interface{}) error {\n\treturn m.Update(key, value, UpdateAny)\n}\n\n// Update changes the value of a key.\nfunc (m *Map) Update(key, value any, flags MapUpdateFlags) error {\n\tif m.typ.hasPerCPUValue() {\n\t\treturn m.updatePerCPU(key, value, flags)\n\t}\n\n\tvaluePtr, err := m.marshalValue(value)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"marshal value: %w\", err)\n\t}\n\n\treturn m.update(key, valuePtr, flags)\n}\n\nfunc (m *Map) updatePerCPU(key, value any, flags MapUpdateFlags) error {\n\tvaluePtr, err := marshalPerCPUValue(value, int(m.valueSize))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"marshal value: %w\", err)\n\t}\n\n\treturn m.update(key, valuePtr, flags)\n}\n\nfunc (m *Map) update(key any, valuePtr sys.Pointer, flags MapUpdateFlags) error {\n\tkeyPtr, err := m.marshalKey(key)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"marshal key: %w\", err)\n\t}\n\n\tattr := sys.MapUpdateElemAttr{\n\t\tMapFd: m.fd.Uint(),\n\t\tKey:   keyPtr,\n\t\tValue: valuePtr,\n\t\tFlags: uint64(flags),\n\t}\n\n\tif err = sys.MapUpdateElem(&attr); err != nil {\n\t\treturn fmt.Errorf(\"update: %w\", wrapMapError(err))\n\t}\n\n\treturn nil\n}\n\n// Delete removes a value.\n//\n// Returns ErrKeyNotExist if the key does not exist.\nfunc (m *Map) Delete(key interface{}) error {\n\tkeyPtr, err := m.marshalKey(key)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"can't marshal key: %w\", err)\n\t}\n\n\tattr := sys.MapDeleteElemAttr{\n\t\tMapFd: m.fd.Uint(),\n\t\tKey:   keyPtr,\n\t}\n\n\tif err = sys.MapDeleteElem(&attr); err != nil {\n\t\treturn fmt.Errorf(\"delete: %w\", wrapMapError(err))\n\t}\n\treturn nil\n}\n\n// NextKey finds the key following an initial key.\n//\n// See NextKeyBytes for details.\n//\n// Returns ErrKeyNotExist if there is no next key.\nfunc (m *Map) NextKey(key, nextKeyOut interface{}) error {\n\tnextKeyBytes := makeMapSyscallOutput(nextKeyOut, int(m.keySize))\n\n\tif err := m.nextKey(key, nextKeyBytes.Pointer()); err != nil {\n\t\treturn err\n\t}\n\n\tif err := nextKeyBytes.Unmarshal(nextKeyOut); err != nil {\n\t\treturn fmt.Errorf(\"can't unmarshal next key: %w\", err)\n\t}\n\treturn nil\n}\n\n// NextKeyBytes returns the key following an initial key as a byte slice.\n//\n// Passing nil will return the first key.\n//\n// Use Iterate if you want to traverse all entries in the map.\n//\n// Returns nil if there are no more keys.\nfunc (m *Map) NextKeyBytes(key interface{}) ([]byte, error) {\n\tnextKey := make([]byte, m.keySize)\n\tnextKeyPtr := sys.UnsafeSlicePointer(nextKey)\n\n\terr := m.nextKey(key, nextKeyPtr)\n\tif errors.Is(err, ErrKeyNotExist) {\n\t\treturn nil, nil\n\t}\n\n\treturn nextKey, err\n}\n\nfunc (m *Map) nextKey(key interface{}, nextKeyOut sys.Pointer) error {\n\tvar (\n\t\tkeyPtr sys.Pointer\n\t\terr    error\n\t)\n\n\tif key != nil {\n\t\tkeyPtr, err = m.marshalKey(key)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"can't marshal key: %w\", err)\n\t\t}\n\t}\n\n\tattr := sys.MapGetNextKeyAttr{\n\t\tMapFd:   m.fd.Uint(),\n\t\tKey:     keyPtr,\n\t\tNextKey: nextKeyOut,\n\t}\n\n\tif err = sys.MapGetNextKey(&attr); err != nil {\n\t\t// Kernels 4.4.131 and earlier return EFAULT instead of a pointer to the\n\t\t// first map element when a nil key pointer is specified.\n\t\tif platform.IsLinux && key == nil && errors.Is(err, unix.EFAULT) {\n\t\t\tvar guessKey []byte\n\t\t\tguessKey, err = m.guessNonExistentKey()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\t// Retry the syscall with a valid non-existing key.\n\t\t\tattr.Key = sys.UnsafeSlicePointer(guessKey)\n\t\t\tif err = sys.MapGetNextKey(&attr); err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\n\t\treturn fmt.Errorf(\"next key: %w\", wrapMapError(err))\n\t}\n\n\treturn nil\n}\n\nvar mmapProtectedPage = sync.OnceValues(func() ([]byte, error) {\n\treturn unix.Mmap(-1, 0, os.Getpagesize(), unix.PROT_NONE, unix.MAP_ANON|unix.MAP_SHARED)\n})\n\n// guessNonExistentKey attempts to perform a map lookup that returns ENOENT.\n// This is necessary on kernels before 4.4.132, since those don't support\n// iterating maps from the start by providing an invalid key pointer.\nfunc (m *Map) guessNonExistentKey() ([]byte, error) {\n\t// Map a protected page and use that as the value pointer. This saves some\n\t// work copying out the value, which we're not interested in.\n\tpage, err := mmapProtectedPage()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvaluePtr := sys.UnsafeSlicePointer(page)\n\n\trandKey := make([]byte, int(m.keySize))\n\n\tfor i := 0; i < 4; i++ {\n\t\tswitch i {\n\t\t// For hash maps, the 0 key is less likely to be occupied. They're often\n\t\t// used for storing data related to pointers, and their access pattern is\n\t\t// generally scattered across the keyspace.\n\t\tcase 0:\n\t\t// An all-0xff key is guaranteed to be out of bounds of any array, since\n\t\t// those have a fixed key size of 4 bytes. The only corner case being\n\t\t// arrays with 2^32 max entries, but those are prohibitively expensive\n\t\t// in many environments.\n\t\tcase 1:\n\t\t\tfor r := range randKey {\n\t\t\t\trandKey[r] = 0xff\n\t\t\t}\n\t\t// Inspired by BCC, 0x55 is an alternating binary pattern (0101), so\n\t\t// is unlikely to be taken.\n\t\tcase 2:\n\t\t\tfor r := range randKey {\n\t\t\t\trandKey[r] = 0x55\n\t\t\t}\n\t\t// Last ditch effort, generate a random key.\n\t\tcase 3:\n\t\t\trand.New(rand.NewSource(time.Now().UnixNano())).Read(randKey)\n\t\t}\n\n\t\terr := m.lookup(randKey, valuePtr, 0)\n\t\tif errors.Is(err, ErrKeyNotExist) {\n\t\t\treturn randKey, nil\n\t\t}\n\t}\n\n\treturn nil, errors.New(\"couldn't find non-existing key\")\n}\n\n// BatchLookup looks up many elements in a map at once.\n//\n// \"keysOut\" and \"valuesOut\" must be of type slice, a pointer\n// to a slice or buffer will not work.\n// \"cursor\" is an pointer to an opaque handle. It must be non-nil. Pass\n// \"cursor\" to subsequent calls of this function to continue the batching\n// operation in the case of chunking.\n//\n// Warning: This API is not very safe to use as the kernel implementation for\n// batching relies on the user to be aware of subtle details with regarding to\n// different map type implementations.\n//\n// ErrKeyNotExist is returned when the batch lookup has reached\n// the end of all possible results, even when partial results\n// are returned. It should be used to evaluate when lookup is \"done\".\nfunc (m *Map) BatchLookup(cursor *MapBatchCursor, keysOut, valuesOut interface{}, opts *BatchOptions) (int, error) {\n\tn, err := m.batchLookup(sys.BPF_MAP_LOOKUP_BATCH, cursor, keysOut, valuesOut, opts)\n\tif err != nil {\n\t\treturn n, fmt.Errorf(\"map batch lookup: %w\", err)\n\t}\n\treturn n, nil\n}\n\n// BatchLookupAndDelete looks up many elements in a map at once,\n//\n// It then deletes all those elements.\n// \"keysOut\" and \"valuesOut\" must be of type slice, a pointer\n// to a slice or buffer will not work.\n// \"cursor\" is an pointer to an opaque handle. It must be non-nil. Pass\n// \"cursor\" to subsequent calls of this function to continue the batching\n// operation in the case of chunking.\n//\n// Warning: This API is not very safe to use as the kernel implementation for\n// batching relies on the user to be aware of subtle details with regarding to\n// different map type implementations.\n//\n// ErrKeyNotExist is returned when the batch lookup has reached\n// the end of all possible results, even when partial results\n// are returned. It should be used to evaluate when lookup is \"done\".\nfunc (m *Map) BatchLookupAndDelete(cursor *MapBatchCursor, keysOut, valuesOut interface{}, opts *BatchOptions) (int, error) {\n\tn, err := m.batchLookup(sys.BPF_MAP_LOOKUP_AND_DELETE_BATCH, cursor, keysOut, valuesOut, opts)\n\tif err != nil {\n\t\treturn n, fmt.Errorf(\"map batch lookup and delete: %w\", err)\n\t}\n\treturn n, nil\n}\n\n// MapBatchCursor represents a starting point for a batch operation.\ntype MapBatchCursor struct {\n\tm      *Map\n\topaque []byte\n}\n\nfunc (m *Map) batchLookup(cmd sys.Cmd, cursor *MapBatchCursor, keysOut, valuesOut interface{}, opts *BatchOptions) (int, error) {\n\tif m.typ.hasPerCPUValue() {\n\t\treturn m.batchLookupPerCPU(cmd, cursor, keysOut, valuesOut, opts)\n\t}\n\n\tcount, err := batchCount(keysOut, valuesOut)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tvalueBuf := sysenc.SyscallOutput(valuesOut, count*int(m.fullValueSize))\n\n\tn, sysErr := m.batchLookupCmd(cmd, cursor, count, keysOut, valueBuf.Pointer(), opts)\n\tif errors.Is(sysErr, unix.ENOSPC) {\n\t\t// Hash tables return ENOSPC when the size of the batch is smaller than\n\t\t// any bucket.\n\t\treturn n, fmt.Errorf(\"%w (batch size too small?)\", sysErr)\n\t} else if sysErr != nil && !errors.Is(sysErr, unix.ENOENT) {\n\t\treturn 0, sysErr\n\t}\n\n\terr = valueBuf.Unmarshal(valuesOut)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn n, sysErr\n}\n\nfunc (m *Map) batchLookupPerCPU(cmd sys.Cmd, cursor *MapBatchCursor, keysOut, valuesOut interface{}, opts *BatchOptions) (int, error) {\n\tcount, err := sliceLen(keysOut)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"keys: %w\", err)\n\t}\n\n\tvalueBuf := sysenc.SyscallOutput(valuesOut, count*int(m.fullValueSize))\n\n\tn, sysErr := m.batchLookupCmd(cmd, cursor, count, keysOut, valueBuf.Pointer(), opts)\n\tif sysErr != nil && !errors.Is(sysErr, unix.ENOENT) {\n\t\treturn 0, sysErr\n\t}\n\n\tif bytesBuf := valueBuf.Bytes(); bytesBuf != nil {\n\t\terr = unmarshalBatchPerCPUValue(valuesOut, count, int(m.valueSize), bytesBuf)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\n\treturn n, sysErr\n}\n\nfunc (m *Map) batchLookupCmd(cmd sys.Cmd, cursor *MapBatchCursor, count int, keysOut any, valuePtr sys.Pointer, opts *BatchOptions) (int, error) {\n\t// * generic_map_lookup_batch requires that batch_out is key_size bytes.\n\t//   This is used by array and LPM maps.\n\t//\n\t// * __htab_map_lookup_and_delete_batch requires u32. This is used by the\n\t//   various hash maps.\n\t//\n\t// Use a minimum of 4 bytes to avoid having to distinguish between the two.\n\tcursorLen := max(int(m.keySize), 4)\n\n\tinBatch := cursor.opaque\n\tif inBatch == nil {\n\t\t// This is the first lookup, allocate a buffer to hold the cursor.\n\t\tcursor.opaque = make([]byte, cursorLen)\n\t\tcursor.m = m\n\t} else if cursor.m != m {\n\t\t// Prevent reuse of a cursor across maps. First, it's unlikely to work.\n\t\t// Second, the maps may require different cursorLen and cursor.opaque\n\t\t// may therefore be too short. This could lead to the kernel clobbering\n\t\t// user space memory.\n\t\treturn 0, errors.New(\"a cursor may not be reused across maps\")\n\t}\n\n\tif err := haveBatchAPI(); err != nil {\n\t\treturn 0, err\n\t}\n\n\tkeyBuf := sysenc.SyscallOutput(keysOut, count*int(m.keySize))\n\n\tattr := sys.MapLookupBatchAttr{\n\t\tMapFd:    m.fd.Uint(),\n\t\tKeys:     keyBuf.Pointer(),\n\t\tValues:   valuePtr,\n\t\tCount:    uint32(count),\n\t\tInBatch:  sys.UnsafeSlicePointer(inBatch),\n\t\tOutBatch: sys.UnsafeSlicePointer(cursor.opaque),\n\t}\n\n\tif opts != nil {\n\t\tattr.ElemFlags = opts.ElemFlags\n\t\tattr.Flags = opts.Flags\n\t}\n\n\t_, sysErr := sys.BPF(cmd, unsafe.Pointer(&attr), unsafe.Sizeof(attr))\n\tsysErr = wrapMapError(sysErr)\n\tif sysErr != nil && !errors.Is(sysErr, unix.ENOENT) {\n\t\treturn 0, sysErr\n\t}\n\n\tif err := keyBuf.Unmarshal(keysOut); err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn int(attr.Count), sysErr\n}\n\n// BatchUpdate updates the map with multiple keys and values\n// simultaneously.\n// \"keys\" and \"values\" must be of type slice, a pointer\n// to a slice or buffer will not work.\nfunc (m *Map) BatchUpdate(keys, values interface{}, opts *BatchOptions) (int, error) {\n\tif m.typ.hasPerCPUValue() {\n\t\treturn m.batchUpdatePerCPU(keys, values, opts)\n\t}\n\n\tcount, err := batchCount(keys, values)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tvaluePtr, err := marshalMapSyscallInput(values, count*int(m.valueSize))\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn m.batchUpdate(count, keys, valuePtr, opts)\n}\n\nfunc (m *Map) batchUpdate(count int, keys any, valuePtr sys.Pointer, opts *BatchOptions) (int, error) {\n\tkeyPtr, err := marshalMapSyscallInput(keys, count*int(m.keySize))\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tattr := sys.MapUpdateBatchAttr{\n\t\tMapFd:  m.fd.Uint(),\n\t\tKeys:   keyPtr,\n\t\tValues: valuePtr,\n\t\tCount:  uint32(count),\n\t}\n\tif opts != nil {\n\t\tattr.ElemFlags = opts.ElemFlags\n\t\tattr.Flags = opts.Flags\n\t}\n\n\terr = sys.MapUpdateBatch(&attr)\n\tif err != nil {\n\t\tif haveFeatErr := haveBatchAPI(); haveFeatErr != nil {\n\t\t\treturn 0, haveFeatErr\n\t\t}\n\t\treturn int(attr.Count), fmt.Errorf(\"batch update: %w\", wrapMapError(err))\n\t}\n\n\treturn int(attr.Count), nil\n}\n\nfunc (m *Map) batchUpdatePerCPU(keys, values any, opts *BatchOptions) (int, error) {\n\tcount, err := sliceLen(keys)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"keys: %w\", err)\n\t}\n\n\tvalueBuf, err := marshalBatchPerCPUValue(values, count, int(m.valueSize))\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn m.batchUpdate(count, keys, sys.UnsafeSlicePointer(valueBuf), opts)\n}\n\n// BatchDelete batch deletes entries in the map by keys.\n// \"keys\" must be of type slice, a pointer to a slice or buffer will not work.\nfunc (m *Map) BatchDelete(keys interface{}, opts *BatchOptions) (int, error) {\n\tcount, err := sliceLen(keys)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"keys: %w\", err)\n\t}\n\n\tkeyPtr, err := marshalMapSyscallInput(keys, count*int(m.keySize))\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"cannot marshal keys: %v\", err)\n\t}\n\n\tattr := sys.MapDeleteBatchAttr{\n\t\tMapFd: m.fd.Uint(),\n\t\tKeys:  keyPtr,\n\t\tCount: uint32(count),\n\t}\n\n\tif opts != nil {\n\t\tattr.ElemFlags = opts.ElemFlags\n\t\tattr.Flags = opts.Flags\n\t}\n\n\tif err = sys.MapDeleteBatch(&attr); err != nil {\n\t\tif haveFeatErr := haveBatchAPI(); haveFeatErr != nil {\n\t\t\treturn 0, haveFeatErr\n\t\t}\n\t\treturn int(attr.Count), fmt.Errorf(\"batch delete: %w\", wrapMapError(err))\n\t}\n\n\treturn int(attr.Count), nil\n}\n\nfunc batchCount(keys, values any) (int, error) {\n\tkeysLen, err := sliceLen(keys)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"keys: %w\", err)\n\t}\n\n\tvaluesLen, err := sliceLen(values)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"values: %w\", err)\n\t}\n\n\tif keysLen != valuesLen {\n\t\treturn 0, fmt.Errorf(\"keys and values must have the same length\")\n\t}\n\n\treturn keysLen, nil\n}\n\n// Iterate traverses a map.\n//\n// It's safe to create multiple iterators at the same time.\n//\n// It's not possible to guarantee that all keys in a map will be\n// returned if there are concurrent modifications to the map.\nfunc (m *Map) Iterate() *MapIterator {\n\treturn newMapIterator(m)\n}\n\n// Close the Map's underlying file descriptor, which could unload the\n// Map from the kernel if it is not pinned or in use by a loaded Program.\nfunc (m *Map) Close() error {\n\tif m == nil {\n\t\t// This makes it easier to clean up when iterating maps\n\t\t// of maps / programs.\n\t\treturn nil\n\t}\n\n\treturn m.fd.Close()\n}\n\n// FD gets the file descriptor of the Map.\n//\n// Calling this function is invalid after Close has been called.\nfunc (m *Map) FD() int {\n\treturn m.fd.Int()\n}\n\n// Clone creates a duplicate of the Map.\n//\n// Closing the duplicate does not affect the original, and vice versa.\n// Changes made to the map are reflected by both instances however.\n// If the original map was pinned, the cloned map will not be pinned by default.\n//\n// Cloning a nil Map returns nil.\nfunc (m *Map) Clone() (*Map, error) {\n\tif m == nil {\n\t\treturn nil, nil\n\t}\n\n\tdup, err := m.fd.Dup()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't clone map: %w\", err)\n\t}\n\n\treturn &Map{\n\t\tm.name,\n\t\tdup,\n\t\tm.typ,\n\t\tm.keySize,\n\t\tm.valueSize,\n\t\tm.maxEntries,\n\t\tm.flags,\n\t\t\"\",\n\t\tm.fullValueSize,\n\t\tnil,\n\t}, nil\n}\n\n// Pin persists the map on the BPF virtual file system past the lifetime of\n// the process that created it .\n//\n// Calling Pin on a previously pinned map will overwrite the path, except when\n// the new path already exists. Re-pinning across filesystems is not supported.\n// You can Clone a map to pin it to a different path.\n//\n// This requires bpffs to be mounted above fileName.\n// See https://docs.cilium.io/en/stable/network/kubernetes/configuration/#mounting-bpffs-with-systemd\nfunc (m *Map) Pin(fileName string) error {\n\tif err := sys.Pin(m.pinnedPath, fileName, m.fd); err != nil {\n\t\treturn err\n\t}\n\tm.pinnedPath = fileName\n\treturn nil\n}\n\n// Unpin removes the persisted state for the map from the BPF virtual filesystem.\n//\n// Failed calls to Unpin will not alter the state returned by IsPinned.\n//\n// Unpinning an unpinned Map returns nil.\nfunc (m *Map) Unpin() error {\n\tif err := sys.Unpin(m.pinnedPath); err != nil {\n\t\treturn err\n\t}\n\tm.pinnedPath = \"\"\n\treturn nil\n}\n\n// IsPinned returns true if the map has a non-empty pinned path.\nfunc (m *Map) IsPinned() bool {\n\treturn m.pinnedPath != \"\"\n}\n\n// Freeze prevents a map to be modified from user space.\n//\n// It makes no changes to kernel-side restrictions.\nfunc (m *Map) Freeze() error {\n\tattr := sys.MapFreezeAttr{\n\t\tMapFd: m.fd.Uint(),\n\t}\n\n\tif err := sys.MapFreeze(&attr); err != nil {\n\t\tif haveFeatErr := haveMapMutabilityModifiers(); haveFeatErr != nil {\n\t\t\treturn fmt.Errorf(\"can't freeze map: %w\", haveFeatErr)\n\t\t}\n\t\treturn fmt.Errorf(\"can't freeze map: %w\", err)\n\t}\n\treturn nil\n}\n\n// finalize populates the Map according to the Contents specified\n// in spec and freezes the Map if requested by spec.\nfunc (m *Map) finalize(spec *MapSpec) error {\n\tfor _, kv := range spec.Contents {\n\t\tif err := m.Put(kv.Key, kv.Value); err != nil {\n\t\t\treturn fmt.Errorf(\"putting value: key %v: %w\", kv.Key, err)\n\t\t}\n\t}\n\n\tif isConstantDataSection(spec.Name) || isKconfigSection(spec.Name) {\n\t\tif err := m.Freeze(); err != nil {\n\t\t\treturn fmt.Errorf(\"freezing map: %w\", err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (m *Map) marshalKey(data interface{}) (sys.Pointer, error) {\n\tif data == nil {\n\t\tif m.keySize == 0 {\n\t\t\t// Queues have a key length of zero, so passing nil here is valid.\n\t\t\treturn sys.UnsafePointer(nil), nil\n\t\t}\n\t\treturn sys.Pointer{}, errors.New(\"can't use nil as key of map\")\n\t}\n\n\treturn marshalMapSyscallInput(data, int(m.keySize))\n}\n\nfunc (m *Map) marshalValue(data interface{}) (sys.Pointer, error) {\n\tvar (\n\t\tbuf []byte\n\t\terr error\n\t)\n\n\tswitch value := data.(type) {\n\tcase *Map:\n\t\tif !m.typ.canStoreMap() {\n\t\t\treturn sys.Pointer{}, fmt.Errorf(\"can't store map in %s\", m.typ)\n\t\t}\n\t\tbuf, err = marshalMap(value, int(m.valueSize))\n\n\tcase *Program:\n\t\tif !m.typ.canStoreProgram() {\n\t\t\treturn sys.Pointer{}, fmt.Errorf(\"can't store program in %s\", m.typ)\n\t\t}\n\t\tbuf, err = marshalProgram(value, int(m.valueSize))\n\n\tdefault:\n\t\treturn marshalMapSyscallInput(data, int(m.valueSize))\n\t}\n\n\tif err != nil {\n\t\treturn sys.Pointer{}, err\n\t}\n\n\treturn sys.UnsafeSlicePointer(buf), nil\n}\n\nfunc (m *Map) unmarshalValue(value any, buf sysenc.Buffer) error {\n\tswitch value := value.(type) {\n\tcase **Map:\n\t\tif !m.typ.canStoreMap() {\n\t\t\treturn fmt.Errorf(\"can't read a map from %s\", m.typ)\n\t\t}\n\n\t\tother, err := unmarshalMap(buf)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// The caller might close the map externally, so ignore errors.\n\t\t_ = (*value).Close()\n\n\t\t*value = other\n\t\treturn nil\n\n\tcase *Map:\n\t\tif !m.typ.canStoreMap() {\n\t\t\treturn fmt.Errorf(\"can't read a map from %s\", m.typ)\n\t\t}\n\t\treturn errors.New(\"require pointer to *Map\")\n\n\tcase **Program:\n\t\tif !m.typ.canStoreProgram() {\n\t\t\treturn fmt.Errorf(\"can't read a program from %s\", m.typ)\n\t\t}\n\n\t\tother, err := unmarshalProgram(buf)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// The caller might close the program externally, so ignore errors.\n\t\t_ = (*value).Close()\n\n\t\t*value = other\n\t\treturn nil\n\n\tcase *Program:\n\t\tif !m.typ.canStoreProgram() {\n\t\t\treturn fmt.Errorf(\"can't read a program from %s\", m.typ)\n\t\t}\n\t\treturn errors.New(\"require pointer to *Program\")\n\t}\n\n\treturn buf.Unmarshal(value)\n}\n\n// LoadPinnedMap opens a Map from a pin (file) on the BPF virtual filesystem.\n//\n// Requires at least Linux 4.5.\nfunc LoadPinnedMap(fileName string, opts *LoadPinOptions) (*Map, error) {\n\tfd, typ, err := sys.ObjGetTyped(&sys.ObjGetAttr{\n\t\tPathname:  sys.NewStringPointer(fileName),\n\t\tFileFlags: opts.Marshal(),\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif typ != sys.BPF_TYPE_MAP {\n\t\t_ = fd.Close()\n\t\treturn nil, fmt.Errorf(\"%s is not a Map\", fileName)\n\t}\n\n\tm, err := newMapFromFD(fd)\n\tif err == nil {\n\t\tm.pinnedPath = fileName\n\t}\n\n\treturn m, err\n}\n\n// unmarshalMap creates a map from a map ID encoded in host endianness.\nfunc unmarshalMap(buf sysenc.Buffer) (*Map, error) {\n\tvar id uint32\n\tif err := buf.Unmarshal(&id); err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewMapFromID(MapID(id))\n}\n\n// marshalMap marshals the fd of a map into a buffer in host endianness.\nfunc marshalMap(m *Map, length int) ([]byte, error) {\n\tif m == nil {\n\t\treturn nil, errors.New(\"can't marshal a nil Map\")\n\t}\n\n\tif length != 4 {\n\t\treturn nil, fmt.Errorf(\"can't marshal map to %d bytes\", length)\n\t}\n\n\tbuf := make([]byte, 4)\n\tinternal.NativeEndian.PutUint32(buf, m.fd.Uint())\n\treturn buf, nil\n}\n\n// MapIterator iterates a Map.\n//\n// See Map.Iterate.\ntype MapIterator struct {\n\ttarget *Map\n\t// Temporary storage to avoid allocations in Next(). This is any instead\n\t// of []byte to avoid allocations.\n\tcursor            any\n\tcount, maxEntries uint32\n\tdone              bool\n\terr               error\n}\n\nfunc newMapIterator(target *Map) *MapIterator {\n\treturn &MapIterator{\n\t\ttarget:     target,\n\t\tmaxEntries: target.maxEntries,\n\t}\n}\n\n// Next decodes the next key and value.\n//\n// Iterating a hash map from which keys are being deleted is not\n// safe. You may see the same key multiple times. Iteration may\n// also abort with an error, see IsIterationAborted.\n//\n// Returns false if there are no more entries. You must check\n// the result of Err afterwards.\n//\n// See Map.Get for further caveats around valueOut.\nfunc (mi *MapIterator) Next(keyOut, valueOut interface{}) bool {\n\tif mi.err != nil || mi.done {\n\t\treturn false\n\t}\n\n\t// For array-like maps NextKey returns nil only after maxEntries\n\t// iterations.\n\tfor mi.count <= mi.maxEntries {\n\t\tif mi.cursor == nil {\n\t\t\t// Pass nil interface to NextKey to make sure the Map's first key\n\t\t\t// is returned. If we pass an uninitialized []byte instead, it'll see a\n\t\t\t// non-nil interface and try to marshal it.\n\t\t\tmi.cursor = make([]byte, mi.target.keySize)\n\t\t\tmi.err = mi.target.NextKey(nil, mi.cursor)\n\t\t} else {\n\t\t\tmi.err = mi.target.NextKey(mi.cursor, mi.cursor)\n\t\t}\n\n\t\tif errors.Is(mi.err, ErrKeyNotExist) {\n\t\t\tmi.done = true\n\t\t\tmi.err = nil\n\t\t\treturn false\n\t\t} else if mi.err != nil {\n\t\t\tmi.err = fmt.Errorf(\"get next key: %w\", mi.err)\n\t\t\treturn false\n\t\t}\n\n\t\tmi.count++\n\t\tmi.err = mi.target.Lookup(mi.cursor, valueOut)\n\t\tif errors.Is(mi.err, ErrKeyNotExist) {\n\t\t\t// Even though the key should be valid, we couldn't look up\n\t\t\t// its value. If we're iterating a hash map this is probably\n\t\t\t// because a concurrent delete removed the value before we\n\t\t\t// could get it. This means that the next call to NextKeyBytes\n\t\t\t// is very likely to restart iteration.\n\t\t\t// If we're iterating one of the fd maps like\n\t\t\t// ProgramArray it means that a given slot doesn't have\n\t\t\t// a valid fd associated. It's OK to continue to the next slot.\n\t\t\tcontinue\n\t\t}\n\t\tif mi.err != nil {\n\t\t\tmi.err = fmt.Errorf(\"look up next key: %w\", mi.err)\n\t\t\treturn false\n\t\t}\n\n\t\tbuf := mi.cursor.([]byte)\n\t\tif ptr, ok := keyOut.(unsafe.Pointer); ok {\n\t\t\tcopy(unsafe.Slice((*byte)(ptr), len(buf)), buf)\n\t\t} else {\n\t\t\tmi.err = sysenc.Unmarshal(keyOut, buf)\n\t\t}\n\n\t\treturn mi.err == nil\n\t}\n\n\tmi.err = fmt.Errorf(\"%w\", ErrIterationAborted)\n\treturn false\n}\n\n// Err returns any encountered error.\n//\n// The method must be called after Next returns nil.\n//\n// Returns ErrIterationAborted if it wasn't possible to do a full iteration.\nfunc (mi *MapIterator) Err() error {\n\treturn mi.err\n}\n\n// MapGetNextID returns the ID of the next eBPF map.\n//\n// Returns ErrNotExist, if there is no next eBPF map.\nfunc MapGetNextID(startID MapID) (MapID, error) {\n\tattr := &sys.MapGetNextIdAttr{Id: uint32(startID)}\n\treturn MapID(attr.NextId), sys.MapGetNextId(attr)\n}\n\n// NewMapFromID returns the [Map] for a given map id. Returns [ErrNotExist] if\n// there is no eBPF map with the given id.\n//\n// Requires at least Linux 4.13.\nfunc NewMapFromID(id MapID) (*Map, error) {\n\tfd, err := sys.MapGetFdById(&sys.MapGetFdByIdAttr{\n\t\tId: uint32(id),\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn newMapFromFD(fd)\n}\n\n// sliceLen returns the length if the value is a slice or an error otherwise.\nfunc sliceLen(slice any) (int, error) {\n\tsliceValue := reflect.ValueOf(slice)\n\tif sliceValue.Kind() != reflect.Slice {\n\t\treturn 0, fmt.Errorf(\"%T is not a slice\", slice)\n\t}\n\treturn sliceValue.Len(), nil\n}\n"
  },
  {
    "path": "map_test.go",
    "content": "package ebpf\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"slices\"\n\t\"sort\"\n\t\"testing\"\n\t\"unsafe\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/platform\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\nvar (\n\tspec1 = &MapSpec{\n\t\tName:       \"foo\",\n\t\tType:       Hash,\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: 1,\n\t\tPinning:    PinByName,\n\t}\n)\n\nfunc TestMap(t *testing.T) {\n\tm := createMap(t, Array, 2)\n\n\tt.Log(m)\n\n\tif err := m.Put(uint32(0), uint32(42)); err != nil {\n\t\tt.Fatal(\"Can't put:\", err)\n\t}\n\tif err := m.Put(uint32(1), uint32(4242)); err != nil {\n\t\tt.Fatal(\"Can't put:\", err)\n\t}\n\n\tm2, err := m.Clone()\n\tif err != nil {\n\t\tt.Fatal(\"Can't clone map:\", err)\n\t}\n\tdefer m2.Close()\n\n\tm.Close()\n\tm = m2\n\n\tvar v uint32\n\tif err := m.Lookup(uint32(0), &v); err != nil {\n\t\tt.Fatal(\"Can't lookup 0:\", err)\n\t}\n\tif v != 42 {\n\t\tt.Error(\"Want value 42, got\", v)\n\t}\n\n\tsliceVal := make([]uint32, 1)\n\tqt.Assert(t, qt.IsNil(m.Lookup(uint32(0), sliceVal)))\n\tqt.Assert(t, qt.DeepEquals(sliceVal, []uint32{42}))\n\n\tvar slice []byte\n\tqt.Assert(t, qt.IsNil(m.Lookup(uint32(0), &slice)))\n\tqt.Assert(t, qt.DeepEquals(slice, internal.NativeEndian.AppendUint32(nil, 42)))\n\n\tvar k uint32\n\tif err := m.NextKey(uint32(0), &k); err != nil {\n\t\tt.Fatal(\"Can't get:\", err)\n\t}\n\tif k != 1 {\n\t\tt.Error(\"Want key 1, got\", k)\n\t}\n}\n\nfunc TestMapSpecCopy(t *testing.T) {\n\ta := &MapSpec{\n\t\t\"foo\",\n\t\tHash,\n\t\t4,\n\t\t4,\n\t\t1,\n\t\t1,\n\t\tPinByName,\n\t\t1,\n\t\t[]MapKV{{1, 2}}, // Can't copy Contents, use value types\n\t\tnil,             // InnerMap\n\t\t0,               // MapExtra\n\t\tbytes.NewReader(nil),\n\t\t&btf.Int{},\n\t\t&btf.Int{},\n\t\tnil,\n\t}\n\ta.InnerMap = a\n\n\tqt.Check(t, qt.IsNil((*MapSpec)(nil).Copy()))\n\tqt.Assert(t, testutils.IsDeepCopy(a.Copy(), a))\n}\n\nfunc TestMapBatch(t *testing.T) {\n\tcontents := []uint32{\n\t\t42, 4242, 23, 2323,\n\t}\n\n\tkeysAndValuesForMap := func(m *Map, contents []uint32) (keys, values []uint32, stride int) {\n\t\tpossibleCPU := 1\n\t\tif m.Type().hasPerCPUValue() {\n\t\t\tpossibleCPU = MustPossibleCPU()\n\t\t}\n\n\t\tkeys = make([]uint32, 0, len(contents))\n\t\tvalues = make([]uint32, 0, len(contents)*possibleCPU)\n\t\tfor key, value := range contents {\n\t\t\tkeys = append(keys, uint32(key))\n\t\t\tfor i := 0; i < possibleCPU; i++ {\n\t\t\t\tvalues = append(values, value*uint32((i+1)))\n\t\t\t}\n\t\t}\n\n\t\treturn keys, values, possibleCPU\n\t}\n\n\tfor _, typ := range []MapType{Array, PerCPUArray} {\n\t\tt.Run(typ.String(), func(t *testing.T) {\n\t\t\tif typ == PerCPUArray {\n\t\t\t\t// https://lore.kernel.org/bpf/20210424214510.806627-2-pctammela@mojatatu.com/\n\t\t\t\ttestutils.SkipOnOldKernel(t, \"5.13\", \"batched ops support for percpu array\")\n\t\t\t}\n\n\t\t\tm := createMap(t, typ, uint32(len(contents)))\n\t\t\tkeys, values, _ := keysAndValuesForMap(m, contents)\n\t\t\tcount, err := m.BatchUpdate(keys, values, nil)\n\t\t\ttestutils.SkipIfNotSupported(t, err)\n\t\t\tqt.Assert(t, qt.IsNil(err))\n\t\t\tqt.Assert(t, qt.Equals(count, len(contents)))\n\n\t\t\tlookupKeys := make([]uint32, len(keys))\n\t\t\tlookupValues := make([]uint32, len(values))\n\n\t\t\tvar cursor MapBatchCursor\n\t\t\tcount, err = m.BatchLookup(&cursor, lookupKeys, lookupValues, nil)\n\t\t\tqt.Assert(t, qt.IsNil(err))\n\t\t\tqt.Assert(t, qt.Equals(count, len(contents)))\n\t\t\tqt.Assert(t, qt.ContentEquals(lookupKeys, keys))\n\t\t\tqt.Assert(t, qt.ContentEquals(lookupValues, values))\n\n\t\t\tcount, err = m.BatchLookup(&cursor, lookupKeys, lookupValues, nil)\n\t\t\tqt.Assert(t, qt.ErrorIs(err, ErrKeyNotExist))\n\t\t\tqt.Assert(t, qt.Equals(count, 0))\n\t\t})\n\t}\n\n\tfor _, typ := range []MapType{Hash, PerCPUHash} {\n\t\tt.Run(typ.String(), func(t *testing.T) {\n\t\t\tm := createMap(t, typ, uint32(len(contents)))\n\t\t\tkeys, values, stride := keysAndValuesForMap(m, contents)\n\t\t\tcount, err := m.BatchUpdate(keys, values, nil)\n\t\t\ttestutils.SkipIfNotSupported(t, err)\n\t\t\tqt.Assert(t, qt.IsNil(err))\n\t\t\tqt.Assert(t, qt.Equals(count, len(contents)))\n\n\t\t\t// BPF hash tables seem to have lots of collisions when keys\n\t\t\t// are following a sequence.\n\t\t\t// This causes ENOSPC since a single large bucket may be larger\n\t\t\t// than the batch size. We work around this by making the batch size\n\t\t\t// equal to the map size.\n\t\t\tlookupKeys := make([]uint32, len(keys))\n\t\t\tlookupValues := make([]uint32, len(values))\n\n\t\t\tvar cursor MapBatchCursor\n\t\t\tcount, err = m.BatchLookup(&cursor, lookupKeys, lookupValues, nil)\n\t\t\tqt.Assert(t, qt.ErrorIs(err, ErrKeyNotExist))\n\t\t\tqt.Assert(t, qt.Equals(count, len(contents)))\n\n\t\t\tqt.Assert(t, qt.ContentEquals(lookupKeys, keys))\n\t\t\tqt.Assert(t, qt.ContentEquals(lookupValues, values))\n\n\t\t\tcursor = MapBatchCursor{}\n\t\t\tcount, err = m.BatchLookupAndDelete(&cursor, lookupKeys, lookupValues, nil)\n\t\t\tqt.Assert(t, qt.ErrorIs(err, ErrKeyNotExist))\n\t\t\tqt.Assert(t, qt.Equals(count, len(contents)))\n\n\t\t\tqt.Assert(t, qt.ContentEquals(lookupKeys, keys))\n\t\t\tqt.Assert(t, qt.ContentEquals(lookupValues, values))\n\n\t\t\tif stride > 1 {\n\t\t\t\tvalues := make([]uint32, stride)\n\t\t\t\tqt.Assert(t, qt.ErrorIs(m.Lookup(uint32(0), values), ErrKeyNotExist))\n\t\t\t} else {\n\t\t\t\tvar v uint32\n\t\t\t\tqt.Assert(t, qt.ErrorIs(m.Lookup(uint32(0), &v), ErrKeyNotExist))\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMapBatchCursorReuse(t *testing.T) {\n\tarr1 := createMap(t, Array, 4)\n\tarr2 := createMap(t, Array, 4)\n\n\ttmp := make([]uint32, 2)\n\n\tvar cursor MapBatchCursor\n\t_, err := arr1.BatchLookup(&cursor, tmp, tmp, nil)\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\n\t_, err = arr2.BatchLookup(&cursor, tmp, tmp, nil)\n\tqt.Assert(t, qt.IsNotNil(err))\n}\n\nfunc TestMapLookupKeyTooSmall(t *testing.T) {\n\tm := createMap(t, Array, 2)\n\tdefer m.Close()\n\n\tvar small uint16\n\tqt.Assert(t, qt.IsNil(m.Put(uint32(0), uint32(1234))))\n\tqt.Assert(t, qt.IsNotNil(m.Lookup(uint32(0), &small)))\n}\n\nfunc TestMapLookupKeyNotFoundAllocations(t *testing.T) {\n\tm := createMap(t, Array, 2)\n\tdefer m.Close()\n\tvar key, out uint32 = 3, 0\n\tvar err error\n\n\tallocs := testing.AllocsPerRun(5, func() {\n\t\terr = m.Lookup(&key, &out)\n\t})\n\tqt.Assert(t, qt.ErrorIs(err, ErrKeyNotExist))\n\tqt.Assert(t, qt.Equals(allocs, float64(0)))\n}\n\nfunc TestBatchAPIMapDelete(t *testing.T) {\n\tif err := haveBatchAPI(); err != nil {\n\t\tt.Skipf(\"batch api not available: %v\", err)\n\t}\n\n\tm := createMap(t, Hash, 10)\n\n\tvar (\n\t\tkeys   = []uint32{0, 1}\n\t\tvalues = []uint32{42, 4242}\n\t)\n\n\tcount, err := m.BatchUpdate(keys, values, nil)\n\tif err != nil {\n\t\tt.Fatalf(\"BatchUpdate: %v\", err)\n\t}\n\tif count != len(keys) {\n\t\tt.Fatalf(\"BatchUpdate: expected count, %d, to be %d\", count, len(keys))\n\t}\n\n\tvar v uint32\n\tif err := m.Lookup(uint32(0), &v); err != nil {\n\t\tt.Fatal(\"Can't lookup 0:\", err)\n\t}\n\tif v != 42 {\n\t\tt.Error(\"Want value 42, got\", v)\n\t}\n\n\tcount, err = m.BatchDelete(keys, nil)\n\tif err != nil {\n\t\tt.Fatalf(\"BatchDelete: %v\", err)\n\t}\n\tif count != len(keys) {\n\t\tt.Fatalf(\"BatchDelete: expected %d deletions got %d\", len(keys), count)\n\t}\n\n\tif err := m.Lookup(uint32(0), &v); !errors.Is(err, ErrKeyNotExist) {\n\t\tt.Fatalf(\"Lookup should have failed with error, %v, instead error is %v\", ErrKeyNotExist, err)\n\t}\n}\n\nfunc TestMapClose(t *testing.T) {\n\tm := createMap(t, Array, 2)\n\n\tif err := m.Close(); err != nil {\n\t\tt.Fatal(\"Can't close map:\", err)\n\t}\n\n\tif err := m.Put(uint32(0), uint32(42)); !errors.Is(err, sys.ErrClosedFd) {\n\t\tt.Fatal(\"Put doesn't check for closed fd\", err)\n\t}\n\n\tif _, err := m.LookupBytes(uint32(0)); !errors.Is(err, sys.ErrClosedFd) {\n\t\tt.Fatal(\"Get doesn't check for closed fd\", err)\n\t}\n}\n\nfunc TestBatchMapWithLock(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.13\", \"MAP BATCH BPF_F_LOCK\")\n\n\tspec, err := LoadCollectionSpec(testutils.NativeFile(t, \"testdata/map_spin_lock-%s.elf\"))\n\tqt.Assert(t, qt.IsNil(err))\n\n\tcoll := mustNewCollection(t, spec, nil)\n\n\ttype spinLockValue struct {\n\t\tCnt     uint32\n\t\tPadding uint32\n\t}\n\n\tm, ok := coll.Maps[\"spin_lock_map\"]\n\tif !ok {\n\t\tt.Fatal(err)\n\t}\n\n\tkeys := []uint32{0, 1}\n\tvalues := []spinLockValue{{Cnt: 42}, {Cnt: 4242}}\n\tcount, err := m.BatchUpdate(keys, values, &BatchOptions{ElemFlags: uint64(UpdateLock)})\n\ttestutils.SkipIfNotSupportedOnOS(t, err)\n\tif err != nil {\n\t\tt.Fatalf(\"BatchUpdate: %v\", err)\n\t}\n\tif count != len(keys) {\n\t\tt.Fatalf(\"BatchUpdate: expected count, %d, to be %d\", count, len(keys))\n\t}\n\n\tvar cursor MapBatchCursor\n\tlookupKeys := make([]uint32, 2)\n\tlookupValues := make([]spinLockValue, 2)\n\tcount, err = m.BatchLookup(&cursor, lookupKeys, lookupValues, &BatchOptions{ElemFlags: uint64(LookupLock)})\n\tif !errors.Is(err, ErrKeyNotExist) {\n\t\tt.Fatalf(\"BatchLookup: %v\", err)\n\t}\n\tif count != 2 {\n\t\tt.Fatalf(\"BatchLookup: expected two keys, got %d\", count)\n\t}\n\n\tcursor = MapBatchCursor{}\n\tdeleteKeys := []uint32{0, 1}\n\tdeleteValues := make([]spinLockValue, 2)\n\tcount, err = m.BatchLookupAndDelete(&cursor, deleteKeys, deleteValues, nil)\n\tif !errors.Is(err, ErrKeyNotExist) {\n\t\tt.Fatalf(\"BatchLookupAndDelete: %v\", err)\n\t}\n\tif count != 2 {\n\t\tt.Fatalf(\"BatchLookupAndDelete: expected two keys, got %d\", count)\n\t}\n}\n\nfunc TestMapWithLock(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.13\", \"MAP BPF_F_LOCK\")\n\n\tspec, err := LoadCollectionSpec(testutils.NativeFile(t, \"testdata/map_spin_lock-%s.elf\"))\n\tqt.Assert(t, qt.IsNil(err))\n\n\tcoll := mustNewCollection(t, spec, nil)\n\n\ttype spinLockValue struct {\n\t\tCnt     uint32\n\t\tPadding uint32\n\t}\n\n\tm, ok := coll.Maps[\"spin_lock_map\"]\n\tif !ok {\n\t\tt.Fatal(err)\n\t}\n\n\tkey := uint32(1)\n\tvalue := spinLockValue{Cnt: 5}\n\terr = m.Update(key, value, UpdateLock)\n\tif platform.IsWindows && errors.Is(err, unix.EINVAL) {\n\t\tt.Skip(\"Windows doesn't support UpdateLock\")\n\t}\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tvalue.Cnt = 0\n\terr = m.LookupWithFlags(&key, &value, LookupLock)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif value.Cnt != 5 {\n\t\tt.Fatalf(\"Want value 5, got %d\", value.Cnt)\n\t}\n\n\tt.Run(\"LookupAndDelete\", func(t *testing.T) {\n\t\ttestutils.SkipOnOldKernel(t, \"5.14\", \"LOOKUP_AND_DELETE flags\")\n\n\t\tvalue.Cnt = 0\n\t\terr = m.LookupAndDeleteWithFlags(&key, &value, LookupLock)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tif value.Cnt != 5 {\n\t\t\tt.Fatalf(\"Want value 5, got %d\", value.Cnt)\n\t\t}\n\n\t\terr = m.LookupWithFlags(&key, &value, LookupLock)\n\t\tif err != nil && !errors.Is(err, ErrKeyNotExist) {\n\t\t\tt.Fatal(err)\n\t\t}\n\t})\n}\n\nfunc TestMapCloneNil(t *testing.T) {\n\tm, err := (*Map)(nil).Clone()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif m != nil {\n\t\tt.Fatal(\"Cloning a nil map doesn't return nil\")\n\t}\n}\n\nfunc TestMapPin(t *testing.T) {\n\tm := createMap(t, Array, 2)\n\n\tif err := m.Put(uint32(0), uint32(42)); err != nil {\n\t\tt.Fatal(\"Can't put:\", err)\n\t}\n\n\ttmp := testutils.TempBPFFS(t)\n\tpath := filepath.Join(tmp, \"map\")\n\n\tif err := m.Pin(path); err != nil {\n\t\ttestutils.SkipIfNotSupported(t, err)\n\t\tt.Fatal(err)\n\t}\n\n\tpinned := m.IsPinned()\n\tqt.Assert(t, qt.IsTrue(pinned))\n\n\tm.Close()\n\n\tm, err := LoadPinnedMap(path, nil)\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer m.Close()\n\n\tvar v uint32\n\tif err := m.Lookup(uint32(0), &v); err != nil {\n\t\tt.Fatal(\"Can't lookup 0:\", err)\n\t}\n\tif v != 42 {\n\t\tt.Error(\"Want value 42, got\", v)\n\t}\n}\n\nfunc TestNestedMapPin(t *testing.T) {\n\tm := createMapInMap(t, ArrayOfMaps, Array)\n\n\ttmp := testutils.TempBPFFS(t)\n\n\tpath := filepath.Join(tmp, \"nested\")\n\tif err := m.Pin(path); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tm.Close()\n\n\tm, err := LoadPinnedMap(path, nil)\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer m.Close()\n}\n\nfunc TestNestedMapPinNested(t *testing.T) {\n\tif _, err := newMap(t, &MapSpec{\n\t\tType:       ArrayOfMaps,\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: 2,\n\t\tInnerMap: &MapSpec{\n\t\t\tName:       \"inner\",\n\t\t\tType:       Array,\n\t\t\tKeySize:    4,\n\t\t\tValueSize:  4,\n\t\t\tMaxEntries: 1,\n\t\t\tPinning:    PinByName,\n\t\t},\n\t}, nil); err == nil {\n\t\tt.Error(\"Inner maps should not be pinnable\")\n\t}\n}\n\nfunc TestMapPinMultiple(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"4.9\", \"atomic re-pinning was introduced in 4.9 series\")\n\n\ttmp := testutils.TempBPFFS(t)\n\n\tspec := spec1.Copy()\n\n\tm1 := mustNewMap(t, spec, &MapOptions{PinPath: tmp})\n\tpinned := m1.IsPinned()\n\tqt.Assert(t, qt.IsTrue(pinned))\n\n\tnewPath := filepath.Join(tmp, \"bar\")\n\terr := m1.Pin(newPath)\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\toldPath := filepath.Join(tmp, spec.Name)\n\tif _, err := os.Stat(oldPath); err == nil {\n\t\tt.Fatal(\"Previous pinned map path still exists:\", err)\n\t}\n\tm2, err := LoadPinnedMap(newPath, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\tpinned = m2.IsPinned()\n\tqt.Assert(t, qt.IsTrue(pinned))\n\tdefer m2.Close()\n}\n\nfunc TestMapPinWithEmptyPath(t *testing.T) {\n\tm := createMap(t, Array, 2)\n\n\terr := m.Pin(\"\")\n\n\tqt.Assert(t, qt.Not(qt.IsNil(err)))\n}\n\nfunc TestMapPinFailReplace(t *testing.T) {\n\ttmp := testutils.TempBPFFS(t)\n\tspec := spec1.Copy()\n\tspec2 := spec1.Copy()\n\tspec2.Name = spec1.Name + \"bar\"\n\n\tm := mustNewMap(t, spec, &MapOptions{PinPath: tmp})\n\t_ = mustNewMap(t, spec2, &MapOptions{PinPath: tmp})\n\n\tqt.Assert(t, qt.IsTrue(m.IsPinned()))\n\tnewPath := filepath.Join(tmp, spec2.Name)\n\n\tqt.Assert(t, qt.Not(qt.IsNil(m.Pin(newPath))), qt.Commentf(\"Pin didn't\"+\n\t\t\" fail new path from replacing an existing path\"))\n}\n\nfunc TestMapUnpin(t *testing.T) {\n\ttmp := testutils.TempBPFFS(t)\n\tspec := spec1.Copy()\n\n\tm := mustNewMap(t, spec, &MapOptions{PinPath: tmp})\n\n\tpinned := m.IsPinned()\n\tqt.Assert(t, qt.IsTrue(pinned))\n\tpath := filepath.Join(tmp, spec.Name)\n\tm2, err := LoadPinnedMap(path, nil)\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer m2.Close()\n\n\tif err = m.Unpin(); err != nil {\n\t\tt.Fatal(\"Failed to unpin map:\", err)\n\t}\n\tif _, err := os.Stat(path); err == nil {\n\t\tt.Fatal(\"Pinned map path still exists after unpinning:\", err)\n\t}\n}\n\nfunc TestMapLoadPinned(t *testing.T) {\n\ttmp := testutils.TempBPFFS(t)\n\n\tspec := spec1.Copy()\n\n\tm1 := mustNewMap(t, spec, &MapOptions{PinPath: tmp})\n\tpinned := m1.IsPinned()\n\tqt.Assert(t, qt.IsTrue(pinned))\n\n\tpath := filepath.Join(tmp, spec.Name)\n\tm2, err := LoadPinnedMap(path, nil)\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer m2.Close()\n\tpinned = m2.IsPinned()\n\tqt.Assert(t, qt.IsTrue(pinned))\n}\n\nfunc TestMapLoadReusePinned(t *testing.T) {\n\tfor _, typ := range []MapType{Array, Hash, DevMap, DevMapHash} {\n\t\tt.Run(typ.String(), func(t *testing.T) {\n\t\t\tif typ == DevMap {\n\t\t\t\ttestutils.SkipOnOldKernel(t, \"4.14\", \"devmap\")\n\t\t\t}\n\t\t\tif typ == DevMapHash {\n\t\t\t\ttestutils.SkipOnOldKernel(t, \"5.4\", \"devmap_hash\")\n\t\t\t}\n\t\t\ttmp := testutils.TempBPFFS(t)\n\t\t\tspec := &MapSpec{\n\t\t\t\tName:       \"pinmap\",\n\t\t\t\tType:       typ,\n\t\t\t\tKeySize:    4,\n\t\t\t\tValueSize:  4,\n\t\t\t\tMaxEntries: 1,\n\t\t\t\tPinning:    PinByName,\n\t\t\t}\n\n\t\t\t_ = mustNewMap(t, spec, &MapOptions{PinPath: tmp})\n\t\t\t_ = mustNewMap(t, spec, &MapOptions{PinPath: tmp})\n\t\t})\n\t}\n}\n\nfunc TestMapLoadPinnedUnpin(t *testing.T) {\n\ttmp := testutils.TempBPFFS(t)\n\n\tspec := spec1.Copy()\n\n\tm1 := mustNewMap(t, spec, &MapOptions{PinPath: tmp})\n\tpinned := m1.IsPinned()\n\tqt.Assert(t, qt.IsTrue(pinned))\n\n\tpath := filepath.Join(tmp, spec.Name)\n\tm2, err := LoadPinnedMap(path, nil)\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer m2.Close()\n\terr = m1.Unpin()\n\tqt.Assert(t, qt.IsNil(err))\n\terr = m2.Unpin()\n\tqt.Assert(t, qt.IsNil(err))\n}\n\nfunc TestMapLoadPinnedWithOptions(t *testing.T) {\n\t// Introduced in commit 6e71b04a8224.\n\ttestutils.SkipOnOldKernel(t, \"4.15\", \"file_flags in BPF_OBJ_GET\")\n\n\tarray := createMap(t, Array, 2)\n\n\ttmp := testutils.TempBPFFS(t)\n\n\tpath := filepath.Join(tmp, \"map\")\n\tif err := array.Pin(path); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tif err := array.Put(uint32(0), uint32(123)); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tarray.Close()\n\n\tt.Run(\"read-only\", func(t *testing.T) {\n\t\tarray, err := LoadPinnedMap(path, &LoadPinOptions{\n\t\t\tReadOnly: true,\n\t\t})\n\t\tif platform.IsWindows && errors.Is(err, unix.EINVAL) {\n\t\t\tt.Skip(\"Windows doesn't support file_flags in OBJ_GET\")\n\t\t}\n\t\ttestutils.SkipIfNotSupported(t, err)\n\t\tif err != nil {\n\t\t\tt.Fatal(\"Can't load map:\", err)\n\t\t}\n\t\tdefer array.Close()\n\n\t\tif err := array.Put(uint32(0), uint32(1)); !errors.Is(err, unix.EPERM) {\n\t\t\tt.Fatal(\"Expected EPERM from Put, got\", err)\n\t\t}\n\t})\n\n\tt.Run(\"write-only\", func(t *testing.T) {\n\t\tarray, err := LoadPinnedMap(path, &LoadPinOptions{\n\t\t\tWriteOnly: true,\n\t\t})\n\t\tif platform.IsWindows && errors.Is(err, unix.EINVAL) {\n\t\t\tt.Skip(\"Windows doesn't support file_flags in OBJ_GET\")\n\t\t}\n\t\ttestutils.SkipIfNotSupported(t, err)\n\t\tif err != nil {\n\t\t\tt.Fatal(\"Can't load map:\", err)\n\t\t}\n\t\tdefer array.Close()\n\n\t\tvar value uint32\n\t\tif err := array.Lookup(uint32(0), &value); !errors.Is(err, unix.EPERM) {\n\t\t\tt.Fatal(\"Expected EPERM from Lookup, got\", err)\n\t\t}\n\t})\n}\n\nfunc TestMapPinFlags(t *testing.T) {\n\ttmp := testutils.TempBPFFS(t)\n\n\tspec := &MapSpec{\n\t\tName:       \"map\",\n\t\tType:       Array,\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: 1,\n\t\tPinning:    PinByName,\n\t}\n\n\t_ = mustNewMap(t, spec, &MapOptions{PinPath: tmp})\n\n\t_, err := newMap(t, spec, &MapOptions{\n\t\tPinPath: tmp,\n\t\tLoadPinOptions: LoadPinOptions{\n\t\t\tFlags: math.MaxUint32,\n\t\t},\n\t})\n\tif !errors.Is(err, unix.EINVAL) {\n\t\tt.Fatal(\"Invalid flags should trigger EINVAL:\", err)\n\t}\n}\n\nfunc TestMapQueue(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"4.20\", \"map type queue\")\n\n\tm := mustNewMap(t, &MapSpec{\n\t\tType:       Queue,\n\t\tValueSize:  4,\n\t\tMaxEntries: 2,\n\t}, nil)\n\n\tfor _, v := range []uint32{42, 4242} {\n\t\tif err := m.Put(nil, v); err != nil {\n\t\t\tt.Fatalf(\"Can't put %d: %s\", v, err)\n\t\t}\n\t}\n\n\tvar v uint32\n\tif err := m.Lookup(nil, &v); err != nil {\n\t\tt.Fatal(\"Lookup (Peek) on Queue:\", err)\n\t}\n\tif v != 42 {\n\t\tt.Error(\"Want value 42, got\", v)\n\t}\n\tv = 0\n\n\tif err := m.LookupAndDelete(nil, &v); err != nil {\n\t\tt.Fatal(\"Can't lookup and delete element:\", err)\n\t}\n\tif v != 42 {\n\t\tt.Error(\"Want value 42, got\", v)\n\t}\n\n\tv = 0\n\tif err := m.LookupAndDelete(nil, unsafe.Pointer(&v)); err != nil {\n\t\tt.Fatal(\"Can't lookup and delete element using unsafe.Pointer:\", err)\n\t}\n\tif v != 4242 {\n\t\tt.Error(\"Want value 4242, got\", v)\n\t}\n\n\tif err := m.LookupAndDelete(nil, &v); !errors.Is(err, ErrKeyNotExist) {\n\t\tt.Fatal(\"Lookup and delete on empty Queue:\", err)\n\t}\n\n\tif err := m.Lookup(nil, &v); !errors.Is(err, ErrKeyNotExist) {\n\t\tt.Fatal(\"Lookup (Peek) on empty Queue:\", err)\n\t}\n}\n\nfunc TestMapInMap(t *testing.T) {\n\tfor _, typ := range []MapType{ArrayOfMaps, HashOfMaps} {\n\t\tt.Run(typ.String(), func(t *testing.T) {\n\t\t\tinner := createMap(t, Array, 2)\n\t\t\tif err := inner.Put(uint32(1), uint32(4242)); err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\n\t\t\touter := createMapInMap(t, typ, Array)\n\t\t\tif err := outer.Put(uint32(0), inner); err != nil {\n\t\t\t\tt.Fatal(\"Can't put inner map:\", err)\n\t\t\t}\n\n\t\t\tif err := outer.Put(uint32(0), (*Map)(nil)); err == nil {\n\t\t\t\tt.Fatal(\"Put accepted a nil Map\")\n\t\t\t}\n\n\t\t\tvar inner2 *Map\n\t\t\tif err := outer.Lookup(uint32(0), &inner2); err != nil {\n\t\t\t\tt.Fatal(\"Can't lookup 0:\", err)\n\t\t\t}\n\t\t\tdefer inner2.Close()\n\n\t\t\tvar v uint32\n\t\t\tif err := inner2.Lookup(uint32(1), &v); err != nil {\n\t\t\t\tt.Fatal(\"Can't lookup 1 in inner2:\", err)\n\t\t\t}\n\n\t\t\tif v != 4242 {\n\t\t\t\tt.Error(\"Expected value 4242, got\", v)\n\t\t\t}\n\n\t\t\tinner2.Close()\n\n\t\t\t// Make sure we can still access the original map\n\t\t\tif err := inner.Lookup(uint32(1), &v); err != nil {\n\t\t\t\tt.Fatal(\"Can't lookup 1 in inner:\", err)\n\t\t\t}\n\n\t\t\tif v != 4242 {\n\t\t\t\tt.Error(\"Expected value 4242, got\", v)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNewMapInMapFromFD(t *testing.T) {\n\tnested := createMapInMap(t, ArrayOfMaps, Array)\n\n\t// Do not copy this, use Clone instead.\n\tanother, err := NewMapFromFD(testutils.DupFD(t, nested.FD()))\n\ttestutils.SkipIfNotSupportedOnOS(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\tanother.Close()\n}\n\nfunc TestPerfEventArray(t *testing.T) {\n\tspecs := []*MapSpec{\n\t\t{Type: PerfEventArray},\n\t\t{Type: PerfEventArray, KeySize: 4},\n\t\t{Type: PerfEventArray, ValueSize: 4},\n\t}\n\n\tfor _, spec := range specs {\n\t\t_ = mustNewMap(t, spec, nil)\n\t}\n}\n\nfunc TestCPUMap(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"4.15\", \"cpu map\")\n\n\tm := mustNewMap(t, &MapSpec{Type: CPUMap, KeySize: 4, ValueSize: 4}, nil)\n\tqt.Assert(t, qt.Equals(m.MaxEntries(), uint32(MustPossibleCPU())))\n}\n\nfunc TestMapInMapValueSize(t *testing.T) {\n\tspec := &MapSpec{\n\t\tType:       ArrayOfMaps,\n\t\tKeySize:    4,\n\t\tValueSize:  0,\n\t\tMaxEntries: 2,\n\t\tInnerMap: &MapSpec{\n\t\t\tType:       Array,\n\t\t\tKeySize:    4,\n\t\t\tValueSize:  4,\n\t\t\tMaxEntries: 2,\n\t\t},\n\t}\n\n\t_ = mustNewMap(t, spec, nil)\n\n\tspec.ValueSize = 4\n\t_ = mustNewMap(t, spec, nil)\n\n\tspec.ValueSize = 1\n\t_, err := newMap(t, spec, nil)\n\tqt.Assert(t, qt.IsNotNil(err))\n}\n\nfunc TestIterateEmptyMap(t *testing.T) {\n\tmakeMap := func(t *testing.T, mapType MapType) *Map {\n\t\tm, err := newMap(t, &MapSpec{\n\t\t\tType:       mapType,\n\t\t\tKeySize:    4,\n\t\t\tValueSize:  8,\n\t\t\tMaxEntries: 2,\n\t\t}, nil)\n\t\tif errors.Is(err, unix.EINVAL) {\n\t\t\tt.Skip(mapType, \"is not supported\")\n\t\t}\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\treturn m\n\t}\n\n\tfor _, mapType := range []MapType{\n\t\tHash,\n\t\tSockHash,\n\t} {\n\t\tt.Run(mapType.String(), func(t *testing.T) {\n\t\t\tm := makeMap(t, mapType)\n\t\t\tentries := m.Iterate()\n\n\t\t\tvar key string\n\t\t\tvar value uint64\n\t\t\tif entries.Next(&key, &value) {\n\t\t\t\tt.Error(\"Empty hash should not be iterable\")\n\t\t\t}\n\t\t\tif err := entries.Err(); err != nil {\n\t\t\t\tt.Error(\"Empty hash shouldn't return an error:\", err)\n\t\t\t}\n\t\t})\n\t}\n\n\tfor _, mapType := range []MapType{\n\t\tArray,\n\t\tSockMap,\n\t} {\n\t\tt.Run(mapType.String(), func(t *testing.T) {\n\t\t\tm := makeMap(t, mapType)\n\t\t\tentries := m.Iterate()\n\t\t\tvar key string\n\t\t\tvar value uint64\n\t\t\tfor entries.Next(&key, &value) {\n\t\t\t\t// Some empty arrays like sockmap don't return any keys.\n\t\t\t}\n\t\t\tif err := entries.Err(); err != nil {\n\t\t\t\tt.Error(\"Empty array shouldn't return an error:\", err)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMapIterate(t *testing.T) {\n\thash := createMap(t, Hash, 2)\n\n\tdata := []string{\"test\", \"more\"}\n\tslices.Sort(data)\n\tfor i, k := range data {\n\t\tif err := hash.Put(k, uint32(i)); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t}\n\n\tvar key string\n\tvar value uint32\n\tvar keys []string\n\n\tentries := hash.Iterate()\n\tfor entries.Next(&key, &value) {\n\t\tkeys = append(keys, key)\n\t}\n\tqt.Assert(t, qt.IsNil(entries.Err()))\n\n\tsort.Strings(keys)\n\tqt.Assert(t, qt.DeepEquals(keys, data))\n}\n\nfunc TestIterateWrongMap(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"4.20\", \"map type queue\")\n\n\tm := mustNewMap(t, &MapSpec{\n\t\tType:       Queue,\n\t\tValueSize:  4,\n\t\tMaxEntries: 2,\n\t\tContents: []MapKV{\n\t\t\t{nil, uint32(0)},\n\t\t\t{nil, uint32(1)},\n\t\t},\n\t}, nil)\n\n\tvar value uint32\n\tentries := m.Iterate()\n\n\tqt.Assert(t, qt.IsFalse(entries.Next(nil, &value)))\n\tqt.Assert(t, qt.IsNotNil(entries.Err()))\n}\n\nfunc TestMapIteratorAllocations(t *testing.T) {\n\tarr := createMap(t, Array, 10)\n\n\tvar k, v uint32\n\titer := arr.Iterate()\n\n\t// AllocsPerRun warms up the function for us.\n\tallocs := testing.AllocsPerRun(int(arr.MaxEntries()-1), func() {\n\t\tif !iter.Next(&k, &v) {\n\t\t\tt.Fatal(\"Next failed while iterating: %w\", iter.Err())\n\t\t}\n\t})\n\n\tqt.Assert(t, qt.Equals(allocs, float64(0)))\n}\n\nfunc TestMapBatchLookupAllocations(t *testing.T) {\n\ttestutils.SkipIfNotSupported(t, haveBatchAPI())\n\n\tfor _, typ := range []MapType{Array, PerCPUArray} {\n\t\tif typ == PerCPUArray {\n\t\t\t// https://lore.kernel.org/bpf/20210424214510.806627-2-pctammela@mojatatu.com/\n\t\t\ttestutils.SkipOnOldKernel(t, \"5.13\", \"batched ops support for percpu array\")\n\t\t}\n\n\t\tt.Run(typ.String(), func(t *testing.T) {\n\t\t\tm := mustNewMap(t, &MapSpec{\n\t\t\t\tName:       \"test\",\n\t\t\t\tType:       typ,\n\t\t\t\tKeySize:    4,\n\t\t\t\tValueSize:  8, // PerCPU values must be 8 byte aligned.\n\t\t\t\tMaxEntries: 10,\n\t\t\t}, nil)\n\n\t\t\tpossibleCPU := 1\n\t\t\tif m.Type().hasPerCPUValue() {\n\t\t\t\tpossibleCPU = MustPossibleCPU()\n\t\t\t}\n\n\t\t\tvar cursor MapBatchCursor\n\t\t\tkeys := any(make([]uint32, 2))\n\t\t\tvalues := any(make([]uint64, 2*possibleCPU))\n\n\t\t\t// AllocsPerRun warms up the function for us.\n\t\t\tallocs := testing.AllocsPerRun(1, func() {\n\t\t\t\t_, err := m.BatchLookup(&cursor, keys, values, nil)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatal(err)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tqt.Assert(t, qt.Equals(allocs, 0))\n\t\t})\n\t}\n}\n\ntype customTestUnmarshaler []uint8\n\nfunc (c customTestUnmarshaler) UnmarshalBinary(data []byte) error {\n\tchunkSize := len(data) / len(c)\n\n\tfor i := range len(data) / chunkSize {\n\t\tc[i] = data[i*chunkSize]\n\t}\n\n\treturn nil\n}\n\nfunc TestMapBatchLookupCustomUnmarshaler(t *testing.T) {\n\ttestutils.SkipIfNotSupported(t, haveBatchAPI())\n\n\tm := mustNewMap(t, &MapSpec{\n\t\tType:       Array,\n\t\tMaxEntries: 3,\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tContents: []MapKV{\n\t\t\t{uint32(0), uint32(3)},\n\t\t\t{uint32(1), uint32(4)},\n\t\t\t{uint32(2), uint32(5)},\n\t\t},\n\t}, nil)\n\n\tvar (\n\t\tcursor MapBatchCursor\n\t\t// Use data structures that result in different memory size than the\n\t\t// map keys and values. Otherwise their memory is used as backing\n\t\t// memory for the syscall directly and Unmarshal is a no-op.\n\t\t// Use batch size that results in partial second lookup.\n\t\tbatchKeys   = make(customTestUnmarshaler, 2)\n\t\tbatchValues = make(customTestUnmarshaler, 2)\n\t\tkeys        []uint8\n\t\tvalues      []uint8\n\t)\n\n\t_, err := m.BatchLookup(&cursor, batchKeys, batchValues, nil)\n\tif err != nil {\n\t\tt.Fatal(\"Full batch lookup failed:\", err)\n\t}\n\n\tkeys = append(keys, batchKeys...)\n\tvalues = append(values, batchValues...)\n\n\t_, err = m.BatchLookup(&cursor, batchKeys, batchValues, nil)\n\tif !errors.Is(err, ErrKeyNotExist) {\n\t\tt.Fatal(\"Partial batch lookup doesn't return ErrKeyNotExist:\", err)\n\t}\n\n\tkeys = append(keys, batchKeys[0])\n\tvalues = append(values, batchValues[0])\n\n\tqt.Assert(t, qt.DeepEquals(keys, []uint8{0, 1, 2}))\n\tqt.Assert(t, qt.DeepEquals(values, []uint8{3, 4, 5}))\n}\n\nfunc TestMapIterateHashKeyOneByteFull(t *testing.T) {\n\thash := mustNewMap(t, &MapSpec{\n\t\tType:       Hash,\n\t\tKeySize:    1,\n\t\tValueSize:  1,\n\t\tMaxEntries: 256,\n\t}, nil)\n\n\tfor i := 0; i < int(hash.MaxEntries()); i++ {\n\t\tif err := hash.Put(uint8(i), uint8(i)); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t}\n\tvar key uint8\n\tvar value uint8\n\tvar keys int\n\n\tentries := hash.Iterate()\n\tfor entries.Next(&key, &value) {\n\t\tif key != value {\n\t\t\tt.Fatalf(\"Expected key == value, got key %v value %v\", key, value)\n\t\t}\n\t\tkeys++\n\t}\n\n\tif err := entries.Err(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif keys != int(hash.MaxEntries()) {\n\t\tt.Fatalf(\"Expected to get %d keys, have %d\", hash.MaxEntries(), keys)\n\t}\n}\n\nfunc TestMapGuessNonExistentKey(t *testing.T) {\n\tif !platform.IsLinux {\n\t\tt.Skip(\"No need to test linux quirk on\", runtime.GOOS)\n\t}\n\n\ttests := []struct {\n\t\tname    string\n\t\tmapType MapType\n\t\tkeys    []uint32\n\t}{\n\t\t{\n\t\t\t\"empty\", Hash, []uint32{},\n\t\t},\n\t\t{\n\t\t\t\"all zero key\", Hash, []uint32{0},\n\t\t},\n\t\t{\n\t\t\t\"all ones key\", Hash, []uint32{math.MaxUint32},\n\t\t},\n\t\t{\n\t\t\t\"alternating bits key\", Hash, []uint32{0x5555_5555},\n\t\t},\n\t\t{\n\t\t\t\"all special patterns\", Hash, []uint32{0, math.MaxUint32, 0x5555_5555},\n\t\t},\n\t\t{\n\t\t\t\"empty\", Array, []uint32{},\n\t\t},\n\t\t{\n\t\t\t\"all zero key\", Array, []uint32{0},\n\t\t},\n\t\t{\n\t\t\t\"full\", Array, []uint32{0, 1},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(fmt.Sprintf(\"%s: %s\", tt.mapType, tt.name), func(t *testing.T) {\n\t\t\tmaxEntries := uint32(len(tt.keys))\n\t\t\tif maxEntries == 0 {\n\t\t\t\tmaxEntries = 1\n\t\t\t}\n\n\t\t\tm := mustNewMap(t, &MapSpec{\n\t\t\t\tType:       tt.mapType,\n\t\t\t\tKeySize:    4,\n\t\t\t\tValueSize:  4,\n\t\t\t\tMaxEntries: maxEntries,\n\t\t\t}, nil)\n\n\t\t\tfor _, key := range tt.keys {\n\t\t\t\tif err := m.Put(key, key); err != nil {\n\t\t\t\t\tt.Fatal(err)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tguess, err := m.guessNonExistentKey()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\n\t\t\tif len(guess) != int(m.keySize) {\n\t\t\t\tt.Fatal(\"Guessed key has wrong size\")\n\t\t\t}\n\n\t\t\tvar value uint32\n\t\t\tif err := m.Lookup(guess, &value); !errors.Is(err, unix.ENOENT) {\n\t\t\t\tt.Fatal(\"Doesn't return ENOENT:\", err)\n\t\t\t}\n\t\t})\n\t}\n\n\tt.Run(\"Hash: full\", func(t *testing.T) {\n\t\tconst n = math.MaxUint8 + 1\n\n\t\thash := mustNewMap(t, &MapSpec{\n\t\t\tType:       Hash,\n\t\t\tKeySize:    1,\n\t\t\tValueSize:  1,\n\t\t\tMaxEntries: n,\n\t\t}, nil)\n\n\t\tfor i := 0; i < n; i++ {\n\t\t\tif err := hash.Put(uint8(i), uint8(i)); err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t}\n\n\t\t_, err := hash.guessNonExistentKey()\n\t\tif err == nil {\n\t\t\tt.Fatal(\"guessNonExistentKey doesn't return error on full hash table\")\n\t\t}\n\t})\n}\n\nfunc TestNotExist(t *testing.T) {\n\thash := createMap(t, Hash, 10)\n\n\tvar tmp uint32\n\terr := hash.Lookup(\"test\", &tmp)\n\tif !errors.Is(err, ErrKeyNotExist) {\n\t\tt.Error(\"Lookup doesn't return ErrKeyNotExist\")\n\t}\n\n\tbuf, err := hash.LookupBytes(\"test\")\n\tif err != nil {\n\t\tt.Error(\"Looking up non-existent key return an error:\", err)\n\t}\n\tif buf != nil {\n\t\tt.Error(\"LookupBytes returns non-nil buffer for non-existent key\")\n\t}\n\n\tif err := hash.Delete(\"test\"); !errors.Is(err, ErrKeyNotExist) {\n\t\tt.Error(\"Deleting unknown key doesn't return ErrKeyNotExist\", err)\n\t}\n\n\tvar k = []byte{1, 2, 3, 4}\n\tif err := hash.NextKey(&k, &tmp); !errors.Is(err, ErrKeyNotExist) {\n\t\tt.Error(\"Looking up next key in empty map doesn't return a non-existing error\", err)\n\t}\n\n\tif err := hash.NextKey(nil, &tmp); !errors.Is(err, ErrKeyNotExist) {\n\t\tt.Error(\"Looking up next key in empty map doesn't return a non-existing error\", err)\n\t}\n}\n\nfunc TestExist(t *testing.T) {\n\thash := createMap(t, Hash, 10)\n\n\tif err := hash.Put(\"test\", uint32(21)); err != nil {\n\t\tt.Errorf(\"Failed to put key/value pair into hash: %v\", err)\n\t}\n\n\tif err := hash.Update(\"test\", uint32(42), UpdateNoExist); !errors.Is(err, ErrKeyExist) {\n\t\tt.Error(\"Updating existing key doesn't return ErrKeyExist\")\n\t}\n}\n\nfunc TestIterateMapInMap(t *testing.T) {\n\tconst idx = uint32(1)\n\n\tparent := createMapInMap(t, ArrayOfMaps, Array)\n\tdefer parent.Close()\n\n\ta := createMap(t, Array, 2)\n\n\tif err := parent.Put(idx, a); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tvar (\n\t\tkey     uint32\n\t\tm       *Map\n\t\tentries = parent.Iterate()\n\t)\n\n\tif !entries.Next(&key, &m) {\n\t\tt.Fatal(\"Iterator encountered error:\", entries.Err())\n\t}\n\tm.Close()\n\n\tif key != 1 {\n\t\tt.Error(\"Iterator didn't skip first entry\")\n\t}\n\n\tif m == nil {\n\t\tt.Fatal(\"Map is nil\")\n\t}\n}\n\nfunc TestPerCPUMarshaling(t *testing.T) {\n\tfor _, typ := range []MapType{PerCPUHash, PerCPUArray, LRUCPUHash} {\n\t\tt.Run(typ.String(), func(t *testing.T) {\n\t\t\tnumCPU := MustPossibleCPU()\n\t\t\tif numCPU < 2 {\n\t\t\t\tt.Skip(\"Test requires at least two CPUs\")\n\t\t\t}\n\t\t\tif typ == PerCPUHash || typ == PerCPUArray {\n\t\t\t\ttestutils.SkipOnOldKernel(t, \"4.6\", \"per-CPU hash and array\")\n\t\t\t}\n\t\t\tif typ == LRUCPUHash {\n\t\t\t\ttestutils.SkipOnOldKernel(t, \"4.10\", \"LRU per-CPU hash\")\n\t\t\t}\n\n\t\t\tarr := createMap(t, typ, 1)\n\n\t\t\tvalues := []*customEncoding{\n\t\t\t\t{\"test\"},\n\t\t\t\t{\"more\"},\n\t\t\t}\n\t\t\tif err := arr.Put(uint32(0), values); err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\n\t\t\t// Make sure unmarshaling works on slices containing pointers\n\t\t\tretrievedVal := make([]*customEncoding, numCPU)\n\t\t\tif err := arr.Lookup(uint32(0), retrievedVal); err == nil {\n\t\t\t\tt.Fatal(\"Slices with nil values should generate error\")\n\t\t\t}\n\t\t\tfor i := range retrievedVal {\n\t\t\t\tretrievedVal[i] = &customEncoding{}\n\t\t\t}\n\t\t\tif err := arr.Lookup(uint32(0), retrievedVal); err != nil {\n\t\t\t\tt.Fatal(\"Can't retrieve key 0:\", err)\n\t\t\t}\n\t\t\tvar retrieved []*customEncoding\n\t\t\tif err := arr.Lookup(uint32(0), &retrieved); err != nil {\n\t\t\t\tt.Fatal(\"Can't retrieve key 0:\", err)\n\t\t\t}\n\n\t\t\tfor i, want := range []string{\"TEST\", \"MORE\"} {\n\t\t\t\tif retrieved[i] == nil {\n\t\t\t\t\tt.Error(\"First item is nil\")\n\t\t\t\t} else if have := retrieved[i].data; have != want {\n\t\t\t\t\tt.Errorf(\"Put doesn't use BinaryMarshaler, expected %s but got %s\", want, have)\n\t\t\t\t}\n\t\t\t}\n\n\t\t})\n\t}\n}\n\ntype bpfCgroupStorageKey struct {\n\tCgroupInodeId uint64\n\tAttachType    AttachType\n\t_             [4]byte // Padding\n}\n\nfunc TestCgroupPerCPUStorageMarshaling(t *testing.T) {\n\tnumCPU := MustPossibleCPU()\n\tif numCPU < 2 {\n\t\tt.Skip(\"Test requires at least two CPUs\")\n\t}\n\ttestutils.SkipOnOldKernel(t, \"5.9\", \"per-CPU CGoup storage with write from user space support\")\n\n\tarr := mustNewMap(t, &MapSpec{\n\t\tType:      PerCPUCGroupStorage,\n\t\tKeySize:   uint32(unsafe.Sizeof(bpfCgroupStorageKey{})),\n\t\tValueSize: uint32(unsafe.Sizeof(uint64(0))),\n\t}, nil)\n\n\tprog := mustNewProgram(t, &ProgramSpec{\n\t\tType:       CGroupSKB,\n\t\tAttachType: AttachCGroupInetEgress,\n\t\tLicense:    \"MIT\",\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.LoadMapPtr(asm.R1, arr.FD()),\n\t\t\tasm.Mov.Imm(asm.R2, 0),\n\t\t\tasm.FnGetLocalStorage.Call(),\n\t\t\tasm.Mov.Imm(asm.R0, 0),\n\t\t\tasm.Return(),\n\t\t},\n\t}, nil)\n\n\tcgroup := testutils.CreateCgroup(t)\n\n\tprogAttachAttrs := sys.ProgAttachAttr{\n\t\tTargetFdOrIfindex: uint32(cgroup.Fd()),\n\t\tAttachBpfFd:       uint32(prog.FD()),\n\t\tAttachType:        uint32(AttachCGroupInetEgress),\n\t\tAttachFlags:       0,\n\t\tReplaceBpfFd:      0,\n\t}\n\terr := sys.ProgAttach(&progAttachAttrs)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer func() {\n\t\tattr := sys.ProgDetachAttr{\n\t\t\tTargetFdOrIfindex: uint32(cgroup.Fd()),\n\t\t\tAttachBpfFd:       uint32(prog.FD()),\n\t\t\tAttachType:        uint32(AttachCGroupInetEgress),\n\t\t}\n\t\tif err := sys.ProgDetach(&attr); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t}()\n\n\tvar mapKey = &bpfCgroupStorageKey{\n\t\tCgroupInodeId: testutils.GetCgroupIno(t, cgroup),\n\t\tAttachType:    AttachCGroupInetEgress,\n\t}\n\n\tvalues := []uint64{1, 2}\n\tif err := arr.Put(mapKey, values); err != nil {\n\t\tt.Fatalf(\"Can't set cgroup %s storage: %s\", cgroup.Name(), err)\n\t}\n\n\tvar retrieved []uint64\n\tif err := arr.Lookup(mapKey, &retrieved); err != nil {\n\t\tt.Fatalf(\"Can't retrieve cgroup %s storage: %s\", cgroup.Name(), err)\n\t}\n\n\tfor i, want := range []uint64{1, 2} {\n\t\tif retrieved[i] == 0 {\n\t\t\tt.Errorf(\"Item %d is 0\", i)\n\t\t} else if have := retrieved[i]; have != want {\n\t\t\tt.Errorf(\"PerCPUCGroupStorage map is not correctly unmarshaled, expected %d but got %d\", want, have)\n\t\t}\n\t}\n}\n\nfunc TestMapMarshalUnsafe(t *testing.T) {\n\tm := createMap(t, Hash, 1)\n\n\tkey := uint32(1)\n\tvalue := uint32(42)\n\n\tif err := m.Put(unsafe.Pointer(&key), unsafe.Pointer(&value)); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tvar res uint32\n\tif err := m.Lookup(unsafe.Pointer(&key), unsafe.Pointer(&res)); err != nil {\n\t\tt.Fatal(\"Can't get item:\", err)\n\t}\n\n\tvar sum uint32\n\titer := m.Iterate()\n\tfor iter.Next(&key, unsafe.Pointer(&res)) {\n\t\tsum += res\n\t}\n\tif err := iter.Err(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif res != 42 {\n\t\tt.Fatalf(\"Expected 42, got %d\", res)\n\t}\n\n\titer = m.Iterate()\n\titer.Next(unsafe.Pointer(&key), &res)\n\tif err := iter.Err(); err != nil {\n\t\tt.Error(err)\n\t}\n\tif key != 1 {\n\t\tt.Errorf(\"Expected key 1, got %d\", key)\n\t}\n\n\tif err := m.Delete(unsafe.Pointer(&key)); err != nil {\n\t\tt.Fatal(\"Can't delete:\", err)\n\t}\n}\n\nfunc TestMapName(t *testing.T) {\n\ttestutils.SkipIfNotSupported(t, haveObjName())\n\n\tm := mustNewMap(t, &MapSpec{\n\t\tName:       \"test!123\",\n\t\tType:       Array,\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: 1,\n\t}, nil)\n\n\tvar info sys.MapInfo\n\tif err := sys.ObjInfo(m.fd, &info); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tname := unix.ByteSliceToString(info.Name[:])\n\tqt.Assert(t, qt.Equals(name, \"test123\"))\n}\n\nfunc TestMapFromFD(t *testing.T) {\n\tm := createMap(t, Array, 2)\n\n\tif err := m.Put(uint32(0), uint32(123)); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// If you're thinking about copying this, don't. Use\n\t// Clone() instead.\n\tm2, err := NewMapFromFD(testutils.DupFD(t, m.FD()))\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer m2.Close()\n\n\tvar val uint32\n\tif err := m2.Lookup(uint32(0), &val); err != nil {\n\t\tt.Fatal(\"Can't look up key:\", err)\n\t}\n\n\tif val != 123 {\n\t\tt.Error(\"Wrong value\")\n\t}\n}\n\nfunc TestMapContents(t *testing.T) {\n\tspec := &MapSpec{\n\t\tType:       Array,\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: 2,\n\t\tContents: []MapKV{\n\t\t\t{uint32(0), uint32(23)},\n\t\t\t{uint32(1), uint32(42)},\n\t\t},\n\t}\n\n\tm := mustNewMap(t, spec, nil)\n\n\tvar value uint32\n\tif err := m.Lookup(uint32(0), &value); err != nil {\n\t\tt.Error(\"Can't look up key 0:\", err)\n\t} else if value != 23 {\n\t\tt.Errorf(\"Incorrect value for key 0, expected 23, have %d\", value)\n\t}\n\n\tif err := m.Lookup(uint32(1), &value); err != nil {\n\t\tt.Error(\"Can't look up key 1:\", err)\n\t} else if value != 42 {\n\t\tt.Errorf(\"Incorrect value for key 0, expected 23, have %d\", value)\n\t}\n\n\tspec.Contents = []MapKV{\n\t\t// Key is larger than MaxEntries\n\t\t{uint32(14), uint32(0)},\n\t}\n\n\t// Invalid contents should be rejected\n\t_, err := newMap(t, spec, nil)\n\tqt.Assert(t, qt.IsNotNil(err))\n}\n\nfunc TestMapFreeze(t *testing.T) {\n\tarr := createMap(t, Array, 2)\n\n\terr := arr.Freeze()\n\ttestutils.SkipIfNotSupported(t, err)\n\n\tif err != nil {\n\t\tt.Fatal(\"Can't freeze map:\", err)\n\t}\n\n\tif err := arr.Put(uint32(0), uint32(1)); err == nil {\n\t\tt.Error(\"Freeze doesn't prevent modification from user space\")\n\t}\n\n\tinfo, err := arr.Info()\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.IsTrue(info.Frozen()))\n}\n\nfunc TestMapGetNextID(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"4.13\", \"bpf_map_get_next_id\")\n\tvar next MapID\n\tvar err error\n\n\t// Ensure there is at least one map on the system.\n\t_ = createMap(t, Hash, 10)\n\n\tif next, err = MapGetNextID(MapID(0)); err != nil {\n\t\tt.Fatal(\"Can't get next ID:\", err)\n\t}\n\tif next == MapID(0) {\n\t\tt.Fatal(\"Expected next ID other than 0\")\n\t}\n\n\t// As there can be multiple eBPF maps, we loop over all of them and\n\t// make sure, the IDs increase and the last call will return ErrNotExist\n\tfor {\n\t\tlast := next\n\t\tif next, err = MapGetNextID(last); err != nil {\n\t\t\tif !errors.Is(err, os.ErrNotExist) {\n\t\t\t\tt.Fatal(\"Expected ErrNotExist, got:\", err)\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\tif next <= last {\n\t\t\tt.Fatalf(\"Expected next ID (%d) to be higher than the last ID (%d)\", next, last)\n\t\t}\n\t}\n}\n\nfunc TestNewMapFromID(t *testing.T) {\n\thash := createMap(t, Hash, 10)\n\n\tinfo, err := hash.Info()\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(\"Couldn't get map info:\", err)\n\t}\n\n\tid, ok := info.ID()\n\tif !ok {\n\t\tt.Skip(\"Map ID not supported\")\n\t}\n\n\thash2, err := NewMapFromID(id)\n\tif err != nil {\n\t\tt.Fatalf(\"Can't get map for ID %d: %v\", id, err)\n\t}\n\thash2.Close()\n\n\t// As there can be multiple maps, we use max(uint32) as MapID to trigger an expected error.\n\t_, err = NewMapFromID(MapID(math.MaxUint32))\n\tif !errors.Is(err, os.ErrNotExist) {\n\t\tt.Fatal(\"Expected ErrNotExist, got:\", err)\n\t}\n}\n\nfunc TestMapPinning(t *testing.T) {\n\ttmp := testutils.TempBPFFS(t)\n\n\tspec := &MapSpec{\n\t\tName:       \"test\",\n\t\tType:       Hash,\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: 1,\n\t\tPinning:    PinByName,\n\t}\n\n\tm1 := mustNewMap(t, spec, &MapOptions{PinPath: tmp})\n\tpinned := m1.IsPinned()\n\tqt.Assert(t, qt.IsTrue(pinned))\n\n\tm1Info, err := m1.Info()\n\tqt.Assert(t, qt.IsNil(err))\n\n\tif err := m1.Put(uint32(0), uint32(42)); err != nil {\n\t\tt.Fatal(\"Can't write value:\", err)\n\t}\n\n\tm2 := mustNewMap(t, spec, &MapOptions{PinPath: tmp})\n\n\tm2Info, err := m2.Info()\n\tqt.Assert(t, qt.IsNil(err))\n\n\tif m1ID, ok := m1Info.ID(); ok {\n\t\tm2ID, _ := m2Info.ID()\n\t\tqt.Assert(t, qt.Equals(m2ID, m1ID))\n\t}\n\n\tvar value uint32\n\tif err := m2.Lookup(uint32(0), &value); err != nil {\n\t\tt.Fatal(\"Can't read from map:\", err)\n\t}\n\n\tif value != 42 {\n\t\tt.Fatal(\"Pinning doesn't use pinned maps\")\n\t}\n\n\tspec.KeySize = 8\n\tspec.ValueSize = 8\n\t_, err = newMap(t, spec, &MapOptions{PinPath: tmp})\n\tif err == nil {\n\t\tt.Fatalf(\"Opening a pinned map with a mismatching spec did not fail\")\n\t}\n\tif !errors.Is(err, ErrMapIncompatible) {\n\t\tt.Fatalf(\"Opening a pinned map with a mismatching spec failed with the wrong error\")\n\t}\n\n\t// Check if error string mentions both KeySize and ValueSize.\n\tqt.Assert(t, qt.StringContains(err.Error(), \"KeySize\"))\n\tqt.Assert(t, qt.StringContains(err.Error(), \"ValueSize\"))\n}\n\nfunc TestMapHandle(t *testing.T) {\n\tkv := &btf.Int{Size: 4}\n\tm := mustNewMap(t, &MapSpec{\n\t\tType:       Hash,\n\t\tKeySize:    kv.Size,\n\t\tValueSize:  kv.Size,\n\t\tKey:        kv,\n\t\tValue:      kv,\n\t\tMaxEntries: 1,\n\t}, nil)\n\n\th, err := m.Handle()\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.IsNotNil(h))\n\tdefer h.Close()\n\n\tspec, err := h.Spec(nil)\n\tqt.Assert(t, qt.IsNil(err))\n\n\ttyp, err := spec.TypeByID(1)\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.ContentEquals(typ, btf.Type(kv)))\n}\n\nfunc TestPerfEventArrayCompatible(t *testing.T) {\n\tms := &MapSpec{\n\t\tType: PerfEventArray,\n\t}\n\n\tm := mustNewMap(t, ms, nil)\n\tqt.Assert(t, qt.IsNil(ms.Compatible(m)))\n\n\tms.MaxEntries = m.MaxEntries() - 1\n\tqt.Assert(t, qt.IsNotNil(ms.Compatible(m)))\n}\n\nfunc TestLoadWrongPin(t *testing.T) {\n\tp := createBasicProgram(t)\n\tm := createMap(t, Hash, 10)\n\ttmp := testutils.TempBPFFS(t)\n\n\tppath := filepath.Join(tmp, \"prog\")\n\tmpath := filepath.Join(tmp, \"map\")\n\n\tqt.Assert(t, qt.IsNil(m.Pin(mpath)))\n\tqt.Assert(t, qt.IsNil(p.Pin(ppath)))\n\n\tt.Run(\"Program\", func(t *testing.T) {\n\t\tlp, err := LoadPinnedProgram(ppath, nil)\n\t\ttestutils.SkipIfNotSupported(t, err)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.IsNil(lp.Close()))\n\n\t\t_, err = LoadPinnedProgram(mpath, nil)\n\t\tqt.Assert(t, qt.IsNotNil(err))\n\t})\n\n\tt.Run(\"Map\", func(t *testing.T) {\n\t\tlm, err := LoadPinnedMap(mpath, nil)\n\t\ttestutils.SkipIfNotSupported(t, err)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\tqt.Assert(t, qt.IsNil(lm.Close()))\n\n\t\t_, err = LoadPinnedMap(ppath, nil)\n\t\tqt.Assert(t, qt.IsNotNil(err))\n\t})\n}\n\ntype benchValue struct {\n\tID      uint32\n\tVal16   uint16\n\tVal16_2 uint16\n\tName    [8]byte\n\tLID     uint64\n}\n\ntype customBenchValue benchValue\n\nfunc (cbv *customBenchValue) UnmarshalBinary(buf []byte) error {\n\tcbv.ID = internal.NativeEndian.Uint32(buf)\n\tcbv.Val16 = internal.NativeEndian.Uint16(buf[4:])\n\tcbv.Val16_2 = internal.NativeEndian.Uint16(buf[6:])\n\tcopy(cbv.Name[:], buf[8:])\n\tcbv.LID = internal.NativeEndian.Uint64(buf[16:])\n\treturn nil\n}\n\nfunc (cbv *customBenchValue) MarshalBinary() ([]byte, error) {\n\tbuf := make([]byte, 24)\n\tinternal.NativeEndian.PutUint32(buf, cbv.ID)\n\tinternal.NativeEndian.PutUint16(buf[4:], cbv.Val16)\n\tinternal.NativeEndian.PutUint16(buf[6:], cbv.Val16_2)\n\tcopy(buf[8:], cbv.Name[:])\n\tinternal.NativeEndian.PutUint64(buf[16:], cbv.LID)\n\treturn buf, nil\n}\n\ntype benchKey struct {\n\tid uint64\n}\n\nfunc (bk *benchKey) MarshalBinary() ([]byte, error) {\n\tbuf := make([]byte, 8)\n\tinternal.NativeEndian.PutUint64(buf, bk.id)\n\treturn buf, nil\n}\n\nfunc BenchmarkMarshaling(b *testing.B) {\n\tnewMap := func(valueSize uint32) *Map {\n\t\treturn mustNewMap(b, &MapSpec{\n\t\t\tType:       Hash,\n\t\t\tKeySize:    8,\n\t\t\tValueSize:  valueSize,\n\t\t\tMaxEntries: 1,\n\t\t}, nil)\n\t}\n\n\tkey := uint64(0)\n\n\tm := newMap(24)\n\tif err := m.Put(key, benchValue{}); err != nil {\n\t\tb.Fatal(err)\n\t}\n\tb.Cleanup(func() { m.Close() })\n\n\tb.Run(\"ValueUnmarshalReflect\", func(b *testing.B) {\n\t\tb.ReportAllocs()\n\n\t\tvar value benchValue\n\n\t\tfor b.Loop() {\n\t\t\terr := m.Lookup(unsafe.Pointer(&key), &value)\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(\"Can't get key:\", err)\n\t\t\t}\n\t\t}\n\t})\n\n\tb.Run(\"KeyMarshalReflect\", func(b *testing.B) {\n\t\tb.ReportAllocs()\n\n\t\tvar value benchValue\n\n\t\tfor b.Loop() {\n\t\t\terr := m.Lookup(&key, unsafe.Pointer(&value))\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(\"Can't get key:\", err)\n\t\t\t}\n\t\t}\n\t})\n\n\tb.Run(\"ValueBinaryUnmarshaler\", func(b *testing.B) {\n\t\tb.ReportAllocs()\n\n\t\tvar value customBenchValue\n\n\t\tfor b.Loop() {\n\t\t\terr := m.Lookup(unsafe.Pointer(&key), &value)\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(\"Can't get key:\", err)\n\t\t\t}\n\t\t}\n\t})\n\n\tb.Run(\"KeyBinaryMarshaler\", func(b *testing.B) {\n\t\tb.ReportAllocs()\n\n\t\tvar key benchKey\n\t\tvar value customBenchValue\n\n\t\tfor b.Loop() {\n\t\t\terr := m.Lookup(&key, unsafe.Pointer(&value))\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(\"Can't get key:\", err)\n\t\t\t}\n\t\t}\n\t})\n\n\tb.Run(\"KeyValueUnsafe\", func(b *testing.B) {\n\t\tb.ReportAllocs()\n\n\t\tvar value benchValue\n\n\t\tfor b.Loop() {\n\t\t\terr := m.Lookup(unsafe.Pointer(&key), unsafe.Pointer(&value))\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(\"Can't get key:\", err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkPerCPUMarshalling(b *testing.B) {\n\tkey := uint64(1)\n\tval := make([]uint64, MustPossibleCPU())\n\tfor i := range val {\n\t\tval[i] = uint64(i)\n\t}\n\n\tm := mustNewMap(b, &MapSpec{\n\t\tType:       PerCPUHash,\n\t\tKeySize:    8,\n\t\tValueSize:  8,\n\t\tMaxEntries: 1,\n\t}, nil)\n\n\tif err := m.Put(key, val[0:]); err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\tb.Run(\"reflection\", func(b *testing.B) {\n\t\tb.ReportAllocs()\n\n\t\tvar value []uint64\n\n\t\tfor b.Loop() {\n\t\t\terr := m.Lookup(unsafe.Pointer(&key), &value)\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(\"Can't get key:\", err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkMap(b *testing.B) {\n\tm := createMap(b, Hash, 1)\n\n\tif err := m.Put(uint32(0), uint32(42)); err != nil {\n\t\tb.Fatal(err)\n\t}\n\n\tb.Run(\"Lookup\", func(b *testing.B) {\n\t\tvar key, value uint32\n\n\t\tb.ReportAllocs()\n\n\t\tfor b.Loop() {\n\t\t\terr := m.Lookup(unsafe.Pointer(&key), unsafe.Pointer(&value))\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n\n\tb.Run(\"Update\", func(b *testing.B) {\n\t\tvar key, value uint32\n\n\t\tb.ReportAllocs()\n\n\t\tfor b.Loop() {\n\t\t\terr := m.Update(unsafe.Pointer(&key), unsafe.Pointer(&value), UpdateAny)\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n\n\tb.Run(\"NextKey\", func(b *testing.B) {\n\t\tvar key uint32\n\n\t\tb.ReportAllocs()\n\n\t\tfor b.Loop() {\n\t\t\terr := m.NextKey(nil, unsafe.Pointer(&key))\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n\n\tb.Run(\"Delete\", func(b *testing.B) {\n\t\tvar key uint32\n\n\t\tb.ReportAllocs()\n\n\t\tfor b.Loop() {\n\t\t\terr := m.Delete(unsafe.Pointer(&key))\n\t\t\tif err != nil && !errors.Is(err, ErrKeyNotExist) {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkIterate(b *testing.B) {\n\tfor _, mt := range []MapType{Hash, PerCPUHash} {\n\t\tm := mustNewMap(b, &MapSpec{\n\t\t\tType:       mt,\n\t\t\tKeySize:    8,\n\t\t\tValueSize:  8,\n\t\t\tMaxEntries: 1000,\n\t\t}, nil)\n\n\t\tpossibleCPU := 1\n\t\tif m.Type().hasPerCPUValue() {\n\t\t\tpossibleCPU = MustPossibleCPU()\n\t\t}\n\t\tvar (\n\t\t\tn      = m.MaxEntries()\n\t\t\tkeys   = make([]uint64, n)\n\t\t\tvalues = make([]uint64, n*uint32(possibleCPU))\n\t\t)\n\n\t\tfor i := 0; uint32(i) < n; i++ {\n\t\t\tkeys[i] = uint64(i)\n\t\t\tfor j := 0; j < possibleCPU; j++ {\n\t\t\t\tvalues[i] = uint64((i * possibleCPU) + j)\n\t\t\t}\n\t\t}\n\n\t\t_, err := m.BatchUpdate(keys, values, nil)\n\t\ttestutils.SkipIfNotSupported(b, err)\n\t\tqt.Assert(b, qt.IsNil(err))\n\n\t\tb.Run(m.Type().String(), func(b *testing.B) {\n\t\t\tb.Run(\"MapIterator\", func(b *testing.B) {\n\t\t\t\tvar k uint64\n\t\t\t\tv := make([]uint64, possibleCPU)\n\n\t\t\t\tb.ReportAllocs()\n\n\t\t\t\tfor b.Loop() {\n\t\t\t\t\titer := m.Iterate()\n\t\t\t\t\tfor iter.Next(&k, v) {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif err := iter.Err(); err != nil {\n\t\t\t\t\t\tb.Fatal(err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tb.Run(\"MapIteratorDelete\", func(b *testing.B) {\n\t\t\t\tvar k uint64\n\t\t\t\tv := make([]uint64, possibleCPU)\n\n\t\t\t\tb.ReportAllocs()\n\n\t\t\t\tfor b.Loop() {\n\t\t\t\t\tb.StopTimer()\n\t\t\t\t\tif _, err := m.BatchUpdate(keys, values, nil); err != nil {\n\t\t\t\t\t\tb.Fatal(err)\n\t\t\t\t\t}\n\t\t\t\t\tb.StartTimer()\n\n\t\t\t\t\titer := m.Iterate()\n\t\t\t\t\tfor iter.Next(&k, &v) {\n\t\t\t\t\t\tif err := m.Delete(&k); err != nil {\n\t\t\t\t\t\t\tb.Fatal(err)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif err := iter.Err(); err != nil {\n\t\t\t\t\t\tb.Fatal(err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tb.Run(\"BatchLookup\", func(b *testing.B) {\n\t\t\t\tk := make([]uint64, m.MaxEntries())\n\t\t\t\tv := make([]uint64, m.MaxEntries()*uint32(possibleCPU))\n\n\t\t\t\tb.ReportAllocs()\n\n\t\t\t\tfor b.Loop() {\n\t\t\t\t\tvar cursor MapBatchCursor\n\t\t\t\t\tfor {\n\t\t\t\t\t\t_, err := m.BatchLookup(&cursor, k, v, nil)\n\t\t\t\t\t\tif errors.Is(err, ErrKeyNotExist) {\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tb.Fatal(err)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tb.Run(\"BatchLookupAndDelete\", func(b *testing.B) {\n\t\t\t\tk := make([]uint64, m.MaxEntries())\n\t\t\t\tv := make([]uint64, m.MaxEntries()*uint32(possibleCPU))\n\n\t\t\t\tb.ReportAllocs()\n\n\t\t\t\tfor b.Loop() {\n\t\t\t\t\tb.StopTimer()\n\t\t\t\t\tif _, err := m.BatchUpdate(keys, values, nil); err != nil {\n\t\t\t\t\t\tb.Fatal(err)\n\t\t\t\t\t}\n\t\t\t\t\tb.StartTimer()\n\n\t\t\t\t\tvar cursor MapBatchCursor\n\t\t\t\t\tfor {\n\t\t\t\t\t\t_, err := m.BatchLookupAndDelete(&cursor, k, v, nil)\n\t\t\t\t\t\tif errors.Is(err, ErrKeyNotExist) {\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tb.Fatal(err)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tb.Run(\"BatchDelete\", func(b *testing.B) {\n\t\t\t\tb.ReportAllocs()\n\n\t\t\t\tfor b.Loop() {\n\t\t\t\t\tb.StopTimer()\n\t\t\t\t\tif _, err := m.BatchUpdate(keys, values, nil); err != nil {\n\t\t\t\t\t\tb.Fatal(err)\n\t\t\t\t\t}\n\t\t\t\t\tb.StartTimer()\n\n\t\t\t\t\tif _, err := m.BatchDelete(keys, nil); err != nil {\n\t\t\t\t\t\tb.Fatal(err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\n// Per CPU maps store a distinct value for each CPU. They are useful\n// to collect metrics.\nfunc ExampleMap_perCPU() {\n\tarr, err := NewMap(&MapSpec{\n\t\tType:       PerCPUArray,\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: 2,\n\t})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer arr.Close()\n\n\tpossibleCPUs := MustPossibleCPU()\n\tperCPUValues := map[uint32]uint32{\n\t\t0: 4,\n\t\t1: 5,\n\t}\n\n\tfor k, v := range perCPUValues {\n\t\t// We set each perCPU slots to the same value.\n\t\tvalues := make([]uint32, possibleCPUs)\n\t\tfor i := range values {\n\t\t\tvalues[i] = v\n\t\t}\n\t\tif err := arr.Put(k, values); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n\n\tfor k := 0; k < 2; k++ {\n\t\tvar values []uint32\n\t\tif err := arr.Lookup(uint32(k), &values); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\t// Note we will print an unexpected message if this is not true.\n\t\tfmt.Printf(\"Value of key %v on all CPUs: %v\\n\", k, values[0])\n\t}\n\tvar (\n\t\tkey     uint32\n\t\tentries = arr.Iterate()\n\t)\n\n\tvar values []uint32\n\tfor entries.Next(&key, &values) {\n\t\texpected, ok := perCPUValues[key]\n\t\tif !ok {\n\t\t\tfmt.Printf(\"Unexpected key %v\\n\", key)\n\t\t\tcontinue\n\t\t}\n\n\t\tfor i, n := range values {\n\t\t\tif n != expected {\n\t\t\t\tfmt.Printf(\"Key %v, Value for cpu %v is %v not %v\\n\",\n\t\t\t\t\tkey, i, n, expected)\n\t\t\t}\n\t\t}\n\t}\n\n\tif err := entries.Err(); err != nil {\n\t\tpanic(err)\n\t}\n}\n\n// It is possible to use unsafe.Pointer to avoid marshalling\n// and copy overhead. It is the responsibility of the caller to ensure\n// the correct size of unsafe.Pointers.\n//\n// Note that using unsafe.Pointer is only marginally faster than\n// implementing Marshaler on the type.\nfunc ExampleMap_zeroCopy() {\n\thash, err := NewMap(&MapSpec{\n\t\tType:       Hash,\n\t\tKeySize:    5,\n\t\tValueSize:  4,\n\t\tMaxEntries: 10,\n\t})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer hash.Close()\n\n\tkey := [5]byte{'h', 'e', 'l', 'l', 'o'}\n\tvalue := uint32(23)\n\n\tif err := hash.Put(unsafe.Pointer(&key), unsafe.Pointer(&value)); err != nil {\n\t\tpanic(err)\n\t}\n\n\tvalue = 0\n\tif err := hash.Lookup(unsafe.Pointer(&key), unsafe.Pointer(&value)); err != nil {\n\t\tpanic(\"can't get value:\" + err.Error())\n\t}\n\n\tfmt.Printf(\"The value is: %d\\n\", value)\n}\n\nfunc ExampleMap_NextKey() {\n\thash, err := NewMap(&MapSpec{\n\t\tType:       Hash,\n\t\tKeySize:    5,\n\t\tValueSize:  4,\n\t\tMaxEntries: 10,\n\t\tContents: []MapKV{\n\t\t\t{\"hello\", uint32(21)},\n\t\t\t{\"world\", uint32(42)},\n\t\t},\n\t})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer hash.Close()\n\n\tvar cur, next string\n\tvar keys []string\n\n\tfor err = hash.NextKey(nil, &next); ; err = hash.NextKey(cur, &next) {\n\t\tif errors.Is(err, ErrKeyNotExist) {\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tkeys = append(keys, next)\n\t\tcur = next\n\t}\n\n\t// Order of keys is non-deterministic due to randomized map seed\n\tsort.Strings(keys)\n\tfmt.Printf(\"Keys are %v\\n\", keys)\n}\n\n// ExampleMap_Iterate demonstrates how to iterate over all entries\n// in a map.\nfunc ExampleMap_Iterate() {\n\thash, err := NewMap(&MapSpec{\n\t\tType:       Hash,\n\t\tKeySize:    5,\n\t\tValueSize:  4,\n\t\tMaxEntries: 10,\n\t\tContents: []MapKV{\n\t\t\t{\"hello\", uint32(21)},\n\t\t\t{\"world\", uint32(42)},\n\t\t},\n\t})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer hash.Close()\n\n\tvar (\n\t\tkey     string\n\t\tvalue   uint32\n\t\tentries = hash.Iterate()\n\t)\n\n\tvalues := make(map[string]uint32)\n\tfor entries.Next(&key, &value) {\n\t\t// Order of keys is non-deterministic due to randomized map seed\n\t\tvalues[key] = value\n\t}\n\n\tif err := entries.Err(); err != nil {\n\t\tpanic(fmt.Sprint(\"Iterator encountered an error:\", err))\n\t}\n\n\tfor k, v := range values {\n\t\tfmt.Printf(\"key: %s, value: %d\\n\", k, v)\n\t}\n}\n\n// It is possible to iterate nested maps and program arrays by\n// unmarshaling into a *Map or *Program.\nfunc ExampleMap_Iterate_nestedMapsAndProgramArrays() {\n\tinner := &MapSpec{\n\t\tType:       Array,\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: 2,\n\t\tContents: []MapKV{\n\t\t\t{uint32(0), uint32(1)},\n\t\t\t{uint32(1), uint32(2)},\n\t\t},\n\t}\n\tim, err := NewMap(inner)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer im.Close()\n\n\touter := &MapSpec{\n\t\tType:       ArrayOfMaps,\n\t\tInnerMap:   inner,\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: 10,\n\t\tContents: []MapKV{\n\t\t\t{uint32(0), im},\n\t\t},\n\t}\n\tarrayOfMaps, err := NewMap(outer)\n\tif errors.Is(err, internal.ErrNotSupported) {\n\t\t// Fake the output if on very old kernel.\n\t\tfmt.Println(\"outerKey: 0\")\n\t\tfmt.Println(\"\\tinnerKey 0 innerValue 1\")\n\t\tfmt.Println(\"\\tinnerKey 1 innerValue 2\")\n\t\treturn\n\t}\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer arrayOfMaps.Close()\n\n\tvar (\n\t\tkey     uint32\n\t\tm       *Map\n\t\tentries = arrayOfMaps.Iterate()\n\t)\n\tfor entries.Next(&key, &m) {\n\t\t// Make sure that the iterated map is closed after\n\t\t// we are done.\n\t\tdefer m.Close()\n\n\t\t// Order of keys is non-deterministic due to randomized map seed\n\t\tfmt.Printf(\"outerKey: %v\\n\", key)\n\n\t\tvar innerKey, innerValue uint32\n\t\titems := m.Iterate()\n\t\tfor items.Next(&innerKey, &innerValue) {\n\t\t\tfmt.Printf(\"\\tinnerKey %v innerValue %v\\n\", innerKey, innerValue)\n\t\t}\n\t\tif err := items.Err(); err != nil {\n\t\t\tpanic(fmt.Sprint(\"Inner Iterator encountered an error:\", err))\n\t\t}\n\t}\n\n\tif err := entries.Err(); err != nil {\n\t\tpanic(fmt.Sprint(\"Iterator encountered an error:\", err))\n\t}\n}\n"
  },
  {
    "path": "marshaler_example_test.go",
    "content": "package ebpf\n\nimport (\n\t\"encoding\"\n\t\"fmt\"\n\t\"strings\"\n)\n\n// Assert that customEncoding implements the correct interfaces.\nvar (\n\t_ encoding.BinaryMarshaler   = (*customEncoding)(nil)\n\t_ encoding.BinaryUnmarshaler = (*customEncoding)(nil)\n)\n\ntype customEncoding struct {\n\tdata string\n}\n\nfunc (ce *customEncoding) MarshalBinary() ([]byte, error) {\n\treturn []byte(strings.ToUpper(ce.data)), nil\n}\n\nfunc (ce *customEncoding) UnmarshalBinary(buf []byte) error {\n\tce.data = string(buf)\n\treturn nil\n}\n\n// ExampleMarshaler shows how to use custom encoding with map methods.\nfunc Example_customMarshaler() {\n\thash, err := NewMap(&MapSpec{\n\t\tType:       Hash,\n\t\tKeySize:    5,\n\t\tValueSize:  4,\n\t\tMaxEntries: 10,\n\t})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer hash.Close()\n\n\tif err := hash.Put(&customEncoding{\"hello\"}, uint32(111)); err != nil {\n\t\tpanic(err)\n\t}\n\n\tvar (\n\t\tkey     customEncoding\n\t\tvalue   uint32\n\t\tentries = hash.Iterate()\n\t)\n\n\tfor entries.Next(&key, &value) {\n\t\tfmt.Printf(\"key: %s, value: %d\\n\", key.data, value)\n\t}\n\n\tif err := entries.Err(); err != nil {\n\t\tpanic(err)\n\t}\n}\n"
  },
  {
    "path": "marshalers.go",
    "content": "package ebpf\n\nimport (\n\t\"encoding\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"slices\"\n\t\"unsafe\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/sysenc\"\n)\n\n// marshalMapSyscallInput converts an arbitrary value into a pointer suitable\n// to be passed to the kernel.\n//\n// As an optimization, it returns the original value if it is an\n// unsafe.Pointer.\nfunc marshalMapSyscallInput(data any, length int) (sys.Pointer, error) {\n\tif ptr, ok := data.(unsafe.Pointer); ok {\n\t\treturn sys.UnsafePointer(ptr), nil\n\t}\n\n\tbuf, err := sysenc.Marshal(data, length)\n\tif err != nil {\n\t\treturn sys.Pointer{}, err\n\t}\n\n\treturn buf.Pointer(), nil\n}\n\nfunc makeMapSyscallOutput(dst any, length int) sysenc.Buffer {\n\tif ptr, ok := dst.(unsafe.Pointer); ok {\n\t\treturn sysenc.UnsafeBuffer(ptr)\n\t}\n\n\t_, ok := dst.(encoding.BinaryUnmarshaler)\n\tif ok {\n\t\treturn sysenc.SyscallOutput(nil, length)\n\t}\n\n\treturn sysenc.SyscallOutput(dst, length)\n}\n\n// appendPerCPUSlice encodes a slice containing one value per\n// possible CPU into a buffer of bytes.\n//\n// Values are initialized to zero if the slice has less elements than CPUs.\nfunc appendPerCPUSlice(buf []byte, slice any, possibleCPUs, elemLength, alignedElemLength int) ([]byte, error) {\n\tsliceType := reflect.TypeOf(slice)\n\tif sliceType.Kind() != reflect.Slice {\n\t\treturn nil, errors.New(\"per-CPU value requires slice\")\n\t}\n\n\tsliceValue := reflect.ValueOf(slice)\n\tsliceLen := sliceValue.Len()\n\tif sliceLen > possibleCPUs {\n\t\treturn nil, fmt.Errorf(\"per-CPU value greater than number of CPUs\")\n\t}\n\n\t// Grow increases the slice's capacity, _if_necessary_\n\tbuf = slices.Grow(buf, alignedElemLength*possibleCPUs)\n\tfor i := 0; i < sliceLen; i++ {\n\t\telem := sliceValue.Index(i).Interface()\n\t\telemBytes, err := sysenc.Marshal(elem, elemLength)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tbuf = elemBytes.AppendTo(buf)\n\t\tbuf = append(buf, make([]byte, alignedElemLength-elemLength)...)\n\t}\n\n\t// Ensure buf is zero-padded full size.\n\tbuf = append(buf, make([]byte, (possibleCPUs-sliceLen)*alignedElemLength)...)\n\n\treturn buf, nil\n}\n\n// marshalPerCPUValue encodes a slice containing one value per\n// possible CPU into a buffer of bytes.\n//\n// Values are initialized to zero if the slice has less elements than CPUs.\nfunc marshalPerCPUValue(slice any, elemLength int) (sys.Pointer, error) {\n\tpossibleCPUs, err := PossibleCPU()\n\tif err != nil {\n\t\treturn sys.Pointer{}, err\n\t}\n\n\talignedElemLength := internal.Align(elemLength, 8)\n\tbuf := make([]byte, 0, alignedElemLength*possibleCPUs)\n\tbuf, err = appendPerCPUSlice(buf, slice, possibleCPUs, elemLength, alignedElemLength)\n\tif err != nil {\n\t\treturn sys.Pointer{}, err\n\t}\n\n\treturn sys.UnsafeSlicePointer(buf), nil\n}\n\n// marshalBatchPerCPUValue encodes a batch-sized slice of slices containing\n// one value per possible CPU into a buffer of bytes.\nfunc marshalBatchPerCPUValue(slice any, batchLen, elemLength int) ([]byte, error) {\n\tsliceType := reflect.TypeOf(slice)\n\tif sliceType.Kind() != reflect.Slice {\n\t\treturn nil, fmt.Errorf(\"batch value requires a slice\")\n\t}\n\tsliceValue := reflect.ValueOf(slice)\n\n\tpossibleCPUs, err := PossibleCPU()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif sliceValue.Len() != batchLen*possibleCPUs {\n\t\treturn nil, fmt.Errorf(\"per-CPU slice has incorrect length, expected %d, got %d\",\n\t\t\tbatchLen*possibleCPUs, sliceValue.Len())\n\t}\n\talignedElemLength := internal.Align(elemLength, 8)\n\tbuf := make([]byte, 0, batchLen*alignedElemLength*possibleCPUs)\n\tfor i := 0; i < batchLen; i++ {\n\t\tbatch := sliceValue.Slice(i*possibleCPUs, (i+1)*possibleCPUs).Interface()\n\t\tbuf, err = appendPerCPUSlice(buf, batch, possibleCPUs, elemLength, alignedElemLength)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"batch %d: %w\", i, err)\n\t\t}\n\t}\n\treturn buf, nil\n}\n\n// unmarshalPerCPUValue decodes a buffer into a slice containing one value per\n// possible CPU.\n//\n// slice must be a literal slice and not a pointer.\nfunc unmarshalPerCPUValue(slice any, elemLength int, buf []byte) error {\n\tsliceType := reflect.TypeOf(slice)\n\tif sliceType.Kind() != reflect.Slice {\n\t\treturn fmt.Errorf(\"per-CPU value requires a slice\")\n\t}\n\n\tpossibleCPUs, err := PossibleCPU()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tsliceValue := reflect.ValueOf(slice)\n\tif sliceValue.Len() != possibleCPUs {\n\t\treturn fmt.Errorf(\"per-CPU slice has incorrect length, expected %d, got %d\",\n\t\t\tpossibleCPUs, sliceValue.Len())\n\t}\n\n\tsliceElemType := sliceType.Elem()\n\tsliceElemIsPointer := sliceElemType.Kind() == reflect.Ptr\n\tstride := internal.Align(elemLength, 8)\n\tfor i := 0; i < possibleCPUs; i++ {\n\t\tvar elem any\n\t\tv := sliceValue.Index(i)\n\t\tif sliceElemIsPointer {\n\t\t\tif !v.Elem().CanAddr() {\n\t\t\t\treturn fmt.Errorf(\"per-CPU slice elements cannot be nil\")\n\t\t\t}\n\t\t\telem = v.Elem().Addr().Interface()\n\t\t} else {\n\t\t\telem = v.Addr().Interface()\n\t\t}\n\t\terr := sysenc.Unmarshal(elem, buf[:elemLength])\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"cpu %d: %w\", i, err)\n\t\t}\n\n\t\tbuf = buf[stride:]\n\t}\n\treturn nil\n}\n\n// unmarshalBatchPerCPUValue decodes a buffer into a batch-sized slice\n// containing one value per possible CPU.\n//\n// slice must have length batchLen * PossibleCPUs().\nfunc unmarshalBatchPerCPUValue(slice any, batchLen, elemLength int, buf []byte) error {\n\tsliceType := reflect.TypeOf(slice)\n\tif sliceType.Kind() != reflect.Slice {\n\t\treturn fmt.Errorf(\"batch requires a slice\")\n\t}\n\n\tsliceValue := reflect.ValueOf(slice)\n\tpossibleCPUs, err := PossibleCPU()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif sliceValue.Len() != batchLen*possibleCPUs {\n\t\treturn fmt.Errorf(\"per-CPU slice has incorrect length, expected %d, got %d\",\n\t\t\tsliceValue.Len(), batchLen*possibleCPUs)\n\t}\n\n\tfullValueSize := possibleCPUs * internal.Align(elemLength, 8)\n\tif len(buf) != batchLen*fullValueSize {\n\t\treturn fmt.Errorf(\"input buffer has incorrect length, expected %d, got %d\",\n\t\t\tlen(buf), batchLen*fullValueSize)\n\t}\n\n\tfor i := 0; i < batchLen; i++ {\n\t\telem := sliceValue.Slice(i*possibleCPUs, (i+1)*possibleCPUs).Interface()\n\t\tif err := unmarshalPerCPUValue(elem, elemLength, buf[:fullValueSize]); err != nil {\n\t\t\treturn fmt.Errorf(\"batch %d: %w\", i, err)\n\t\t}\n\t\tbuf = buf[fullValueSize:]\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "marshalers_test.go",
    "content": "package ebpf\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestMarshalUnmarshalBatchPerCPUValue(t *testing.T) {\n\tconst (\n\t\tbatchLen   = 3\n\t\telemLength = 4\n\t)\n\tpossibleCPU := MustPossibleCPU()\n\tsliceLen := batchLen * possibleCPU\n\tslice := makeFilledSlice(sliceLen)\n\tbuf, err := marshalBatchPerCPUValue(slice, batchLen, elemLength)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\toutput := make([]uint32, sliceLen)\n\terr = unmarshalBatchPerCPUValue(output, batchLen, elemLength, buf)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tqt.Assert(t, qt.DeepEquals(output, slice))\n}\n\nfunc TestMarshalBatchPerCPUValue(t *testing.T) {\n\tconst (\n\t\tbatchLen   = 3\n\t\telemLength = 4\n\t)\n\tpossibleCPU := MustPossibleCPU()\n\tsliceLen := batchLen * possibleCPU\n\tslice := makeFilledSlice(sliceLen)\n\texpected := make([]byte, sliceLen*internal.Align(elemLength, 8))\n\tb := expected\n\tfor _, elem := range slice {\n\t\tinternal.NativeEndian.PutUint32(b, elem)\n\t\tb = b[8:]\n\t}\n\tbuf, err := marshalBatchPerCPUValue(slice, batchLen, elemLength)\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.DeepEquals(buf, expected))\n\n\ttooSmall := slice[:len(slice)-1]\n\tbuf, err = marshalBatchPerCPUValue(tooSmall, batchLen, elemLength)\n\tqt.Assert(t, qt.IsNotNil(err))\n\tqt.Assert(t, qt.HasLen(buf, 0))\n\n\ttooBig := append(slice, 0)\n\tbuf, err = marshalBatchPerCPUValue(tooBig, batchLen, elemLength)\n\tqt.Assert(t, qt.IsNotNil(err))\n\tqt.Assert(t, qt.HasLen(buf, 0))\n}\n\nfunc TestUnmarshalBatchPerCPUValue(t *testing.T) {\n\tconst (\n\t\tbatchLen   = 3\n\t\telemLength = 4\n\t)\n\tpossibleCPU := MustPossibleCPU()\n\toutputLen := batchLen * possibleCPU\n\toutput := make([]uint32, outputLen)\n\texpected := makeFilledSlice(batchLen * possibleCPU)\n\n\tbuf := make([]byte, batchLen*possibleCPU*internal.Align(elemLength, 8))\n\tb := buf\n\tfor _, elem := range expected {\n\t\tinternal.NativeEndian.PutUint32(b, elem)\n\t\tb = b[8:]\n\t}\n\terr := unmarshalBatchPerCPUValue(output, batchLen, elemLength, buf)\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.DeepEquals(output, expected))\n\n\ttooSmall := make([]uint32, outputLen-1)\n\terr = unmarshalBatchPerCPUValue(tooSmall, batchLen, elemLength, buf)\n\tqt.Assert(t, qt.IsNotNil(err))\n\n\ttooBig := make([]uint32, outputLen+1)\n\terr = unmarshalBatchPerCPUValue(tooBig, batchLen, elemLength, buf)\n\tqt.Assert(t, qt.IsNotNil(err))\n\n\tempty := make([]uint32, outputLen)\n\ttooSmallBuf := buf[:len(buf)-1]\n\terr = unmarshalBatchPerCPUValue(empty, batchLen, elemLength, tooSmallBuf)\n\tqt.Assert(t, qt.IsNotNil(err))\n\n\ttooBigBuf := append(buf, 0)\n\terr = unmarshalBatchPerCPUValue(empty, batchLen, elemLength, tooBigBuf)\n\tqt.Assert(t, qt.IsNotNil(err))\n}\n\nfunc TestUnmarshalPerCPUValue(t *testing.T) {\n\tpossibleCPUs := MustPossibleCPU()\n\texpected := make([]uint32, possibleCPUs)\n\tfor i := 0; i < possibleCPUs; i++ {\n\t\texpected[i] = uint32(1021 * (i + 1))\n\t}\n\telemLength := 4\n\n\tbuf := make([]byte, possibleCPUs*internal.Align(elemLength, 8))\n\tb := buf\n\tfor _, elem := range expected {\n\t\tinternal.NativeEndian.PutUint32(b, elem)\n\t\tb = b[8:]\n\t}\n\tslice := make([]uint32, possibleCPUs)\n\terr := unmarshalPerCPUValue(slice, elemLength, buf)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tqt.Assert(t, qt.DeepEquals(slice, expected))\n\n\tsmallSlice := make([]uint32, possibleCPUs-1)\n\tqt.Assert(t, qt.IsNotNil(unmarshalPerCPUValue(smallSlice, elemLength, buf)))\n\n\tnilElemSlice := make([]*uint32, possibleCPUs)\n\tqt.Assert(t, qt.IsNotNil(unmarshalPerCPUValue(nilElemSlice, elemLength, buf)))\n}\n\nfunc makeFilledSlice(len int) []uint32 {\n\tslice := make([]uint32, len)\n\tfor i := range slice {\n\t\tslice[i] = uint32(1021 * (i + 1))\n\t}\n\treturn slice\n}\n"
  },
  {
    "path": "memory.go",
    "content": "package ebpf\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"runtime\"\n\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\n// Memory is the building block for accessing the memory of specific bpf map\n// types (Array and Arena at the time of writing) without going through the bpf\n// syscall interface.\n//\n// Given the fd of a bpf map created with the BPF_F_MMAPABLE flag, a shared\n// 'file'-based memory-mapped region can be allocated in the process' address\n// space, exposing the bpf map's memory by simply accessing a memory location.\n\nvar ErrReadOnly = errors.New(\"resource is read-only\")\n\n// Memory implements accessing a Map's memory without making any syscalls.\n// Pay attention to the difference between Go and C struct alignment rules. Use\n// [structs.HostLayout] on supported Go versions to help with alignment.\n//\n// Note on memory coherence: avoid using packed structs in memory shared between\n// user space and eBPF C programs. This drops a struct's memory alignment to 1,\n// forcing the compiler to use single-byte loads and stores for field accesses.\n// This may lead to partially-written data to be observed from user space.\n//\n// On most architectures, the memmove implementation used by Go's copy() will\n// access data in word-sized chunks. If paired with a matching access pattern on\n// the eBPF C side (and if using default memory alignment), accessing shared\n// memory without atomics or other synchronization primitives should be sound\n// for individual values. For accesses beyond a single value, the usual\n// concurrent programming rules apply.\ntype Memory struct {\n\tb    []byte\n\tro   bool\n\theap bool\n\n\tcleanup runtime.Cleanup\n}\n\nfunc newMemory(fd, size int) (*Memory, error) {\n\t// Typically, maps created with BPF_F_RDONLY_PROG remain writable from user\n\t// space until frozen. As a security precaution, the kernel doesn't allow\n\t// mapping bpf map memory as read-write into user space if the bpf map was\n\t// frozen, or if it was created using the RDONLY_PROG flag.\n\t//\n\t// The user would be able to write to the map after freezing (since the kernel\n\t// can't change the protection mode of an already-mapped page), while the\n\t// verifier assumes the contents to be immutable.\n\tb, err := unix.Mmap(fd, 0, size, unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)\n\n\t// If the map is frozen when an rw mapping is requested, expect EPERM. If the\n\t// map was created with BPF_F_RDONLY_PROG, expect EACCES.\n\tvar ro bool\n\tif errors.Is(err, unix.EPERM) || errors.Is(err, unix.EACCES) {\n\t\tro = true\n\t\tb, err = unix.Mmap(fd, 0, size, unix.PROT_READ, unix.MAP_SHARED)\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"setting up memory-mapped region: %w\", err)\n\t}\n\n\tmm := &Memory{b: b, ro: ro, heap: false}\n\tmm.cleanup = runtime.AddCleanup(mm, memoryCleanupFunc(), b)\n\n\treturn mm, nil\n}\n\nfunc memoryCleanupFunc() func([]byte) {\n\treturn func(b []byte) {\n\t\tif err := unix.Munmap(b); err != nil {\n\t\t\tpanic(fmt.Errorf(\"unmapping memory: %w\", err))\n\t\t}\n\t}\n}\n\nfunc (mm *Memory) close() {\n\tmm.cleanup.Stop()\n\tmemoryCleanupFunc()(mm.b)\n\tmm.b = nil\n}\n\n// Size returns the size of the memory-mapped region in bytes.\nfunc (mm *Memory) Size() uint32 {\n\treturn uint32(len(mm.b))\n}\n\n// ReadOnly returns true if the memory-mapped region is read-only.\nfunc (mm *Memory) ReadOnly() bool {\n\treturn mm.ro\n}\n\n// bounds returns true if an access at off of the given size is within bounds.\nfunc (mm *Memory) bounds(off, size uint32) bool {\n\tif off+size < off {\n\t\treturn false\n\t}\n\treturn off+size <= uint32(len(mm.b))\n}\n\n// ReadAt implements [io.ReaderAt]. Useful for creating a new [io.OffsetWriter].\n//\n// See [Memory] for details around memory coherence.\nfunc (mm *Memory) ReadAt(p []byte, off int64) (int, error) {\n\tif mm.b == nil {\n\t\treturn 0, fmt.Errorf(\"memory-mapped region closed\")\n\t}\n\n\tif p == nil {\n\t\treturn 0, fmt.Errorf(\"input buffer p is nil\")\n\t}\n\n\tif off < 0 || off >= int64(len(mm.b)) {\n\t\treturn 0, fmt.Errorf(\"read offset out of range\")\n\t}\n\n\tn := copy(p, mm.b[off:])\n\tif n < len(p) {\n\t\treturn n, io.EOF\n\t}\n\n\treturn n, nil\n}\n\n// WriteAt implements [io.WriterAt]. Useful for creating a new\n// [io.SectionReader].\n//\n// See [Memory] for details around memory coherence.\nfunc (mm *Memory) WriteAt(p []byte, off int64) (int, error) {\n\tif mm.b == nil {\n\t\treturn 0, fmt.Errorf(\"memory-mapped region closed\")\n\t}\n\tif mm.ro {\n\t\treturn 0, fmt.Errorf(\"memory-mapped region not writable: %w\", ErrReadOnly)\n\t}\n\n\tif p == nil {\n\t\treturn 0, fmt.Errorf(\"output buffer p is nil\")\n\t}\n\n\tif off < 0 || off >= int64(len(mm.b)) {\n\t\treturn 0, fmt.Errorf(\"write offset out of range\")\n\t}\n\n\tn := copy(mm.b[off:], p)\n\tif n < len(p) {\n\t\treturn n, io.EOF\n\t}\n\n\treturn n, nil\n}\n"
  },
  {
    "path": "memory_test.go",
    "content": "package ebpf\n\nimport (\n\t\"io\"\n\t\"math\"\n\t\"os\"\n\t\"runtime\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc mustMmapableArray(tb testing.TB, extraFlags uint32) *Map {\n\ttb.Helper()\n\n\tm, err := newMap(tb, &MapSpec{\n\t\tName:       \"ebpf_mmap\",\n\t\tType:       Array,\n\t\tKeySize:    4,\n\t\tValueSize:  8,\n\t\tMaxEntries: 8,\n\t\tFlags:      sys.BPF_F_MMAPABLE | extraFlags,\n\t}, nil)\n\ttestutils.SkipIfNotSupported(tb, err)\n\tqt.Assert(tb, qt.IsNil(err))\n\treturn m\n}\n\nfunc TestMemory(t *testing.T) {\n\tmm, err := mustMmapableArray(t, 0).Memory()\n\tqt.Assert(t, qt.IsNil(err))\n\n\t// Ensure the cleanup is set correctly and doesn't unmap the region while\n\t// we're using it.\n\truntime.GC()\n\n\t// The mapping is always at least one page long, and the Map created here fits\n\t// in a single page.\n\tqt.Assert(t, qt.Equals(mm.Size(), uint32(os.Getpagesize())))\n\n\t// No BPF_F_RDONLY_PROG flag, so the Memory should be read-write.\n\tqt.Assert(t, qt.IsFalse(mm.ReadOnly()))\n\n\twant := []byte{1, 2, 3, 4, 4, 3, 2, 1}\n\tw := io.NewOffsetWriter(mm, 16)\n\tn, err := w.Write(want)\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.Equals(n, 8))\n\n\tr := io.NewSectionReader(mm, 16, int64(len(want)))\n\tgot := make([]byte, len(want))\n\tn, err = r.Read(got)\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.Equals(n, len(want)))\n}\n\nfunc TestMemoryBounds(t *testing.T) {\n\tmm, err := mustMmapableArray(t, 0).Memory()\n\tqt.Assert(t, qt.IsNil(err))\n\n\tend := mm.Size()\n\n\tqt.Assert(t, qt.IsTrue(mm.bounds(0, 0)))\n\tqt.Assert(t, qt.IsTrue(mm.bounds(end, 0)))\n\tqt.Assert(t, qt.IsTrue(mm.bounds(end-8, 8)))\n\tqt.Assert(t, qt.IsTrue(mm.bounds(0, end)))\n\n\tqt.Assert(t, qt.IsFalse(mm.bounds(end-8, 9)))\n\tqt.Assert(t, qt.IsFalse(mm.bounds(end, 1)))\n\tqt.Assert(t, qt.IsFalse(mm.bounds(math.MaxUint32, 1)))\n}\n\nfunc TestMemoryReadOnly(t *testing.T) {\n\trd, err := mustMmapableArray(t, sys.BPF_F_RDONLY_PROG).Memory()\n\tqt.Assert(t, qt.IsNil(err))\n\n\t// BPF_F_RDONLY_PROG flag, so the Memory should be read-only.\n\tqt.Assert(t, qt.IsTrue(rd.ReadOnly()))\n\n\t// Frozen maps can't be mapped rw either.\n\tfrozen := mustMmapableArray(t, 0)\n\tqt.Assert(t, qt.IsNil(frozen.Freeze()))\n\tfz, err := frozen.Memory()\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.IsTrue(fz.ReadOnly()))\n}\n\nfunc TestMemoryClose(t *testing.T) {\n\tmm, err := mustMmapableArray(t, 0).Memory()\n\tqt.Assert(t, qt.IsNil(err))\n\n\t// unmap panics if the operation fails.\n\tmm.close()\n}\n"
  },
  {
    "path": "memory_unsafe.go",
    "content": "package ebpf\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"unsafe\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\n// This file contains an experimental, unsafe implementation of Memory that\n// allows taking a Go pointer to a memory-mapped region. This currently does not\n// have first-class support from the Go runtime, so it may break in future Go\n// versions. The Go proposal for the runtime to track off-heap pointers is here:\n// https://github.com/golang/go/issues/70224.\n//\n// In Go, the programmer should not have to worry about freeing memory. Since\n// this API synthesizes Go variables around global variables declared in a BPF\n// C program, we want to lean on the runtime for making sure accessing them is\n// safe at all times. Unfortunately, Go (as of 1.24) does not have the ability\n// of automatically managing memory that was not allocated by the runtime.\n//\n// This led to a solution that requests regular Go heap memory by allocating a\n// slice (making the runtime track pointers into the slice's backing array) and\n// memory-mapping the bpf map's memory over it. Then, before returning the\n// Memory to the caller, a finalizer is set on the backing array, making sure\n// the bpf map's memory is unmapped from the heap before releasing the backing\n// array to the runtime for reallocation.\n//\n// This obviates the need to maintain a reference to the *Memory at all times,\n// which is difficult for the caller to achieve if the variable access is done\n// through another object (like a sync.Atomic) that can potentially be passed\n// around the Go application. Accidentally losing the reference to the *Memory\n// would result in hard-to-debug segfaults, which are always unexpected in Go.\n\n//go:linkname heapObjectsCanMove runtime.heapObjectsCanMove\nfunc heapObjectsCanMove() bool\n\n// Set from a file behind the ebpf_unsafe_memory_experiment build tag to enable\n// features that require mapping bpf map memory over the Go heap.\nvar unsafeMemory = false\n\n// ErrInvalidType is returned when the given type cannot be used as a Memory or\n// Variable pointer.\nvar ErrInvalidType = errors.New(\"invalid type\")\n\nfunc newUnsafeMemory(fd, size int) (*Memory, error) {\n\t// Some architectures need the size to be page-aligned to work with MAP_FIXED.\n\tif size%os.Getpagesize() != 0 {\n\t\treturn nil, fmt.Errorf(\"memory: must be a multiple of page size (requested %d bytes)\", size)\n\t}\n\n\t// Allocate a page-aligned span of memory on the Go heap.\n\talloc, err := allocate(size)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"allocating memory: %w\", err)\n\t}\n\n\t// Typically, maps created with BPF_F_RDONLY_PROG remain writable from user\n\t// space until frozen. As a security precaution, the kernel doesn't allow\n\t// mapping bpf map memory as read-write into user space if the bpf map was\n\t// frozen, or if it was created using the RDONLY_PROG flag.\n\t//\n\t// The user would be able to write to the map after freezing (since the kernel\n\t// can't change the protection mode of an already-mapped page), while the\n\t// verifier assumes the contents to be immutable.\n\t//\n\t// Map the bpf map memory over a page-aligned allocation on the Go heap.\n\terr = mapmap(fd, alloc, size, unix.PROT_READ|unix.PROT_WRITE)\n\n\t// If the map is frozen when an rw mapping is requested, expect EPERM. If the\n\t// map was created with BPF_F_RDONLY_PROG, expect EACCES.\n\tvar ro bool\n\tif errors.Is(err, unix.EPERM) || errors.Is(err, unix.EACCES) {\n\t\tro = true\n\t\terr = mapmap(fd, alloc, size, unix.PROT_READ)\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"setting up memory-mapped region: %w\", err)\n\t}\n\n\tmm := &Memory{\n\t\tunsafe.Slice((*byte)(alloc), size),\n\t\tro,\n\t\ttrue,\n\t\truntime.Cleanup{},\n\t}\n\n\treturn mm, nil\n}\n\n// allocate returns a pointer to a page-aligned section of memory on the Go\n// heap, managed by the runtime.\n//\n//go:nocheckptr\nfunc allocate(size int) (unsafe.Pointer, error) {\n\t// Memory-mapping over a piece of the Go heap is unsafe when the GC can\n\t// randomly decide to move objects around, in which case the mapped region\n\t// will not move along with it.\n\tif heapObjectsCanMove() {\n\t\treturn nil, errors.New(\"this Go runtime has a moving garbage collector\")\n\t}\n\n\tif size == 0 {\n\t\treturn nil, errors.New(\"size must be greater than 0\")\n\t}\n\n\t// Request at least two pages of memory from the runtime to ensure we can\n\t// align the requested allocation to a page boundary. This is needed for\n\t// MAP_FIXED and makes sure we don't mmap over some other allocation on the Go\n\t// heap.\n\tsize = internal.Align(size+os.Getpagesize(), os.Getpagesize())\n\n\t// Allocate a new slice and store a pointer to its backing array.\n\talloc := unsafe.Pointer(unsafe.SliceData(make([]byte, size)))\n\n\t// nolint:govet\n\t//\n\t// Align the pointer to a page boundary within the allocation. This may alias\n\t// the initial pointer if it was already page-aligned. Ignore govet warnings\n\t// since we're calling [runtime.KeepAlive] on the original Go memory.\n\taligned := unsafe.Pointer(internal.Align(uintptr(alloc), uintptr(os.Getpagesize())))\n\truntime.KeepAlive(alloc)\n\n\t// Return an aligned pointer into the backing array, losing the original\n\t// reference. The runtime.SetFinalizer docs specify that its argument 'must be\n\t// a pointer to an object, complit or local var', but this is still somewhat\n\t// vague and not enforced by the current implementation.\n\t//\n\t// Currently, finalizers can be set and triggered from any address within a\n\t// heap allocation, even individual struct fields or arbitrary offsets within\n\t// a slice. In this case, finalizers set on struct fields or slice offsets\n\t// will only run when the whole struct or backing array are collected. The\n\t// accepted runtime.AddCleanup proposal makes this behaviour more explicit and\n\t// is set to deprecate runtime.SetFinalizer.\n\t//\n\t// Alternatively, we'd have to track the original allocation and the aligned\n\t// pointer separately, which severely complicates finalizer setup and makes it\n\t// prone to human error. For now, just bump the pointer and treat it as the\n\t// new and only reference to the backing array.\n\treturn aligned, nil\n}\n\n// mapmap memory-maps the given file descriptor at the given address and sets a\n// finalizer on addr to unmap it when it's no longer reachable.\nfunc mapmap(fd int, addr unsafe.Pointer, size, flags int) error {\n\t// Map the bpf map memory over the Go heap. This will result in the following\n\t// mmap layout in the process' address space (0xc000000000 is a span of Go\n\t// heap), visualized using pmap:\n\t//\n\t// Address           Kbytes     RSS   Dirty Mode  Mapping\n\t// 000000c000000000    1824     864     864 rw--- [ anon ]\n\t// 000000c0001c8000       4       4       4 rw-s- [ anon ]\n\t// 000000c0001c9000    2268      16      16 rw--- [ anon ]\n\t//\n\t// This will break up the Go heap, but as long as the runtime doesn't try to\n\t// move our allocation around, this is safe for as long as we hold a reference\n\t// to our allocated object.\n\t//\n\t// Use MAP_SHARED to make sure the kernel sees any writes we do, and MAP_FIXED\n\t// to ensure the mapping starts exactly at the address we requested. If alloc\n\t// isn't page-aligned, the mapping operation will fail.\n\tif _, err := unix.MmapPtr(fd, 0, addr, uintptr(size),\n\t\tflags, unix.MAP_SHARED|unix.MAP_FIXED); err != nil {\n\t\treturn fmt.Errorf(\"setting up memory-mapped region: %w\", err)\n\t}\n\n\t// Set a finalizer on the heap allocation to undo the mapping before the span\n\t// is collected and reused by the runtime. This has a few reasons:\n\t//\n\t//  - Avoid leaking memory/mappings.\n\t//  - Future writes to this memory should never clobber a bpf map's contents.\n\t//  - Some bpf maps are mapped read-only, causing a segfault if the runtime\n\t//    reallocates and zeroes the span later.\n\truntime.SetFinalizer((*byte)(addr), unmap(size))\n\n\treturn nil\n}\n\n// unmap returns a function that takes a pointer to a memory-mapped region on\n// the Go heap. The function undoes any mappings and discards the span's\n// contents.\n//\n// Used as a finalizer in [newMemory], split off into a separate function for\n// testing and to avoid accidentally closing over the unsafe.Pointer to the\n// memory region, which would cause a cyclical reference.\n//\n// The resulting function panics if the mmap operation returns an error, since\n// it would mean the integrity of the Go heap is compromised.\nfunc unmap(size int) func(*byte) {\n\treturn func(a *byte) {\n\t\t// Create another mapping at the same address to undo the original mapping.\n\t\t// This will cause the kernel to repair the slab since we're using the same\n\t\t// protection mode and flags as the original mapping for the Go heap.\n\t\t//\n\t\t// Address           Kbytes     RSS   Dirty Mode  Mapping\n\t\t// 000000c000000000    4096     884     884 rw--- [ anon ]\n\t\t//\n\t\t// Using munmap here would leave an unmapped hole in the heap, compromising\n\t\t// its integrity.\n\t\t//\n\t\t// MmapPtr allocates another unsafe.Pointer at the same address. Even though\n\t\t// we discard it here, it may temporarily resurrect the backing array and\n\t\t// delay its collection to the next GC cycle.\n\t\t_, err := unix.MmapPtr(-1, 0, unsafe.Pointer(a), uintptr(size),\n\t\t\tunix.PROT_READ|unix.PROT_WRITE,\n\t\t\tunix.MAP_PRIVATE|unix.MAP_FIXED|unix.MAP_ANON)\n\t\tif err != nil {\n\t\t\tpanic(fmt.Errorf(\"undoing bpf map memory mapping: %w\", err))\n\t\t}\n\t}\n}\n\n// checkUnsafeMemory ensures value T can be accessed in mm at offset off.\n//\n// The comparable constraint narrows down the set of eligible types to exclude\n// slices, maps and functions. These complex types cannot be mapped to memory\n// directly.\nfunc checkUnsafeMemory[T comparable](mm *Memory, off uint32) error {\n\tif mm.b == nil {\n\t\treturn fmt.Errorf(\"memory-mapped region is nil\")\n\t}\n\tif mm.ro {\n\t\treturn ErrReadOnly\n\t}\n\tif !mm.heap {\n\t\treturn fmt.Errorf(\"memory region is not heap-mapped, build with '-tags ebpf_unsafe_memory_experiment' to enable: %w\", ErrNotSupported)\n\t}\n\n\tt := reflect.TypeFor[T]()\n\tif err := checkType(t.String(), t); err != nil {\n\t\treturn err\n\t}\n\n\tsize := t.Size()\n\tif size == 0 {\n\t\treturn fmt.Errorf(\"zero-sized type %s: %w\", t, ErrInvalidType)\n\t}\n\n\tif off%uint32(t.Align()) != 0 {\n\t\treturn fmt.Errorf(\"unaligned access of memory-mapped region: %d-byte aligned read at offset %d\", t.Align(), off)\n\t}\n\n\tvs, bs := uint32(size), uint32(len(mm.b))\n\tif off+vs > bs {\n\t\treturn fmt.Errorf(\"%d-byte value at offset %d exceeds mmap size of %d bytes\", vs, off, bs)\n\t}\n\n\treturn nil\n}\n\n// checkType recursively checks if the given type is supported for memory\n// mapping. Only fixed-size, non-Go-pointer types are supported: bools, floats,\n// (u)int[8-64], arrays, and structs containing them. As an exception, uintptr\n// is allowed since the backing memory is expected to contain 32-bit pointers on\n// 32-bit systems despite BPF always allocating 64 bits for pointers in a data\n// section.\n//\n// Doesn't check for loops since it rejects pointers. Should that ever change, a\n// visited set would be needed.\nfunc checkType(name string, t reflect.Type) error {\n\t// Special-case atomic types to allow them to be used as root types as well as\n\t// struct fields. Notably, omit atomic.Value and atomic.Pointer since those\n\t// are pointer types. Also, atomic.Value embeds an interface value, which\n\t// doesn't make sense to share with C land.\n\tif t.PkgPath() == \"sync/atomic\" {\n\t\tswitch t.Name() {\n\t\tcase \"Bool\", \"Int32\", \"Int64\", \"Uint32\", \"Uint64\", \"Uintptr\":\n\t\t\treturn nil\n\t\t}\n\t}\n\n\tswitch t.Kind() {\n\tcase reflect.Uintptr, reflect.Bool, reflect.Float32, reflect.Float64,\n\t\treflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,\n\t\treflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:\n\t\treturn nil\n\n\tcase reflect.Array:\n\t\tat := t.Elem()\n\t\tif err := checkType(fmt.Sprintf(\"%s.%s\", name, at.String()), at); err != nil {\n\t\t\treturn err\n\t\t}\n\n\tcase reflect.Struct:\n\t\tvar hasHostLayout bool\n\t\tfor i := range t.NumField() {\n\t\t\tat := t.Field(i).Type\n\n\t\t\t// Require [structs.HostLayout] to be embedded in all structs. Check the\n\t\t\t// full package path to reject a user-defined HostLayout type.\n\t\t\tif at.PkgPath() == \"structs\" && at.Name() == \"HostLayout\" {\n\t\t\t\thasHostLayout = true\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif err := checkType(fmt.Sprintf(\"%s.%s\", name, at.String()), at); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tif !hasHostLayout {\n\t\t\treturn fmt.Errorf(\"struct %s must embed structs.HostLayout: %w\", name, ErrInvalidType)\n\t\t}\n\n\tdefault:\n\t\t// For basic types like int and bool, the kind name is the same as the type\n\t\t// name, so the fallthrough case would print 'int type int not supported'.\n\t\t// Omit the kind name if it matches the type name.\n\t\tif t.String() == t.Kind().String() {\n\t\t\t// Output: type int not supported\n\t\t\treturn fmt.Errorf(\"type %s not supported: %w\", name, ErrInvalidType)\n\t\t}\n\n\t\t// Output: interface value io.Reader not supported\n\t\treturn fmt.Errorf(\"%s type %s not supported: %w\", t.Kind(), name, ErrInvalidType)\n\t}\n\n\treturn nil\n}\n\n// memoryPointer returns a pointer to a value of type T at offset off in mm.\n// Taking a pointer to a read-only Memory or to a Memory that is not heap-mapped\n// is not supported.\n//\n// T must contain only fixed-size, non-Go-pointer types: bools, floats,\n// (u)int[8-64], arrays, and structs containing them. Structs must embed\n// [structs.HostLayout]. [ErrInvalidType] is returned if T is not a valid type.\n//\n// Memory must be writable, off must be aligned to the size of T, and the value\n// must be within bounds of the Memory.\n//\n// To access read-only memory, use [Memory.ReadAt].\nfunc memoryPointer[T comparable](mm *Memory, off uint32) (*T, error) {\n\tif err := checkUnsafeMemory[T](mm, off); err != nil {\n\t\treturn nil, fmt.Errorf(\"memory pointer: %w\", err)\n\t}\n\treturn (*T)(unsafe.Pointer(&mm.b[off])), nil\n}\n"
  },
  {
    "path": "memory_unsafe_tag.go",
    "content": "//go:build ebpf_unsafe_memory_experiment\n\npackage ebpf\n\nfunc init() {\n\tunsafeMemory = true\n}\n"
  },
  {
    "path": "memory_unsafe_test.go",
    "content": "package ebpf\n\nimport (\n\t\"runtime\"\n\t\"structs\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"unsafe\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/internal/sys\"\n)\n\nfunc TestUnsafeMemoryDisabled(t *testing.T) {\n\tmm, err := mustMmapableArray(t, 0).Memory()\n\tqt.Assert(t, qt.IsNil(err))\n\n\t_, err = memoryPointer[uint32](mm, 0)\n\tqt.Assert(t, qt.ErrorIs(err, ErrNotSupported))\n}\n\nfunc TestUnsafeMemoryUnmap(t *testing.T) {\n\tmm, err := mustMmapableArray(t, 0).unsafeMemory()\n\tqt.Assert(t, qt.IsNil(err))\n\n\t// Avoid unmap running twice.\n\truntime.SetFinalizer(unsafe.SliceData(mm.b), nil)\n\n\t// unmap panics if the operation fails.\n\tunmap(int(mm.Size()))(unsafe.SliceData(mm.b))\n}\n\nfunc TestUnsafeMemoryPointer(t *testing.T) {\n\tm := mustMmapableArray(t, 0)\n\tmm, err := m.unsafeMemory()\n\tqt.Assert(t, qt.IsNil(err))\n\n\t// Requesting an unaligned value should fail.\n\t_, err = memoryPointer[uint32](mm, 7)\n\tqt.Assert(t, qt.IsNotNil(err))\n\n\tu64, err := memoryPointer[uint64](mm, 8)\n\tqt.Assert(t, qt.IsNil(err))\n\n\twant := uint64(0xf00d)\n\t*u64 = want\n\tqt.Assert(t, qt.Equals(*u64, want))\n\n\t// Read back the value using the bpf syscall interface.\n\tvar got uint64\n\tqt.Assert(t, qt.IsNil(m.Lookup(uint32(1), &got)))\n\tqt.Assert(t, qt.Equals(got, want))\n\n\t_, err = memoryPointer[*uint32](mm, 0)\n\tqt.Assert(t, qt.ErrorIs(err, ErrInvalidType))\n}\n\nfunc TestUnsafeMemoryReadOnly(t *testing.T) {\n\trd, err := mustMmapableArray(t, sys.BPF_F_RDONLY_PROG).unsafeMemory()\n\tqt.Assert(t, qt.IsNil(err))\n\n\t// BPF_F_RDONLY_PROG flag, so the Memory should be read-only.\n\tqt.Assert(t, qt.IsTrue(rd.ReadOnly()))\n\n\t// Frozen maps can't be mapped rw either.\n\tfrozen := mustMmapableArray(t, 0)\n\tqt.Assert(t, qt.IsNil(frozen.Freeze()))\n\tfz, err := frozen.Memory()\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.IsTrue(fz.ReadOnly()))\n\n\t_, err = fz.WriteAt([]byte{1}, 0)\n\tqt.Assert(t, qt.ErrorIs(err, ErrReadOnly))\n\n\t_, err = memoryPointer[uint32](fz, 0)\n\tqt.Assert(t, qt.ErrorIs(err, ErrReadOnly))\n}\n\nfunc TestCheckUnsafeMemory(t *testing.T) {\n\tmm, err := mustMmapableArray(t, 0).unsafeMemory()\n\tqt.Assert(t, qt.IsNil(err))\n\n\t// Primitive types\n\tqt.Assert(t, qt.IsNil(checkUnsafeMemory[bool](mm, 0)))\n\tqt.Assert(t, qt.IsNil(checkUnsafeMemory[uint32](mm, 0)))\n\n\t// Arrays\n\tqt.Assert(t, qt.IsNil(checkUnsafeMemory[[4]byte](mm, 0)))\n\tqt.Assert(t, qt.IsNil(checkUnsafeMemory[[2]struct {\n\t\t_ structs.HostLayout\n\t\tA uint32\n\t\tB uint64\n\t}](mm, 0)))\n\n\t// Structs\n\tqt.Assert(t, qt.IsNil(checkUnsafeMemory[struct {\n\t\t_ structs.HostLayout\n\t\t_ uint32\n\t}](mm, 0)))\n\tqt.Assert(t, qt.IsNil(checkUnsafeMemory[struct {\n\t\t_ structs.HostLayout\n\t\t_ [4]byte\n\t}](mm, 0)))\n\n\t// Atomics\n\tqt.Assert(t, qt.IsNil(checkUnsafeMemory[atomic.Uint32](mm, 0)))\n\tqt.Assert(t, qt.IsNil(checkUnsafeMemory[struct {\n\t\t_ structs.HostLayout\n\t\t_ atomic.Uint32\n\t}](mm, 0)))\n\n\t// Special cases\n\tqt.Assert(t, qt.IsNil(checkUnsafeMemory[uintptr](mm, 0)))\n\tqt.Assert(t, qt.IsNil(checkUnsafeMemory[atomic.Uintptr](mm, 0)))\n\tqt.Assert(t, qt.IsNil(checkUnsafeMemory[struct {\n\t\t_ structs.HostLayout\n\t\t_ uintptr\n\t}](mm, 0)))\n\n\t// No pointers\n\tqt.Assert(t, qt.ErrorIs(checkUnsafeMemory[*uint32](mm, 0), ErrInvalidType))\n\tqt.Assert(t, qt.ErrorIs(checkUnsafeMemory[**uint32](mm, 0), ErrInvalidType))\n\tqt.Assert(t, qt.ErrorIs(checkUnsafeMemory[[1]*uint8](mm, 0), ErrInvalidType))\n\tqt.Assert(t, qt.ErrorIs(checkUnsafeMemory[struct {\n\t\t_ structs.HostLayout\n\t\t_ *uint8\n\t}](mm, 0), ErrInvalidType))\n\tqt.Assert(t, qt.ErrorIs(checkUnsafeMemory[atomic.Pointer[uint64]](mm, 0), ErrInvalidType))\n\tqt.Assert(t, qt.ErrorIs(checkUnsafeMemory[atomic.Value](mm, 0), ErrInvalidType))\n\n\t// No variable-sized types\n\tqt.Assert(t, qt.ErrorIs(checkUnsafeMemory[int](mm, 0), ErrInvalidType))\n\tqt.Assert(t, qt.ErrorIs(checkUnsafeMemory[uint](mm, 0), ErrInvalidType))\n\n\t// No interface types\n\tqt.Assert(t, qt.ErrorIs(checkUnsafeMemory[any](mm, 0), ErrInvalidType))\n\n\t// No zero-sized types\n\tqt.Assert(t, qt.ErrorIs(checkUnsafeMemory[struct{ _ structs.HostLayout }](mm, 0), ErrInvalidType))\n\n\t// No structs without HostLayout\n\tqt.Assert(t, qt.ErrorIs(checkUnsafeMemory[struct{ _ uint32 }](mm, 0), ErrInvalidType))\n}\n"
  },
  {
    "path": "netlify.toml",
    "content": "[build]\n  base = \"docs/\"\n  publish = \"site/\"\n  command = \"mkdocs build\"\n  environment = { PYTHON_VERSION = \"3.13\" }\n"
  },
  {
    "path": "perf/doc.go",
    "content": "// Package perf allows reading from BPF perf event arrays.\n//\n// A perf event array contains multiple perf event ringbuffers which can be used\n// to exchange sample like data with user space.\npackage perf\n"
  },
  {
    "path": "perf/reader.go",
    "content": "//go:build !windows\n\npackage perf\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/epoll\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\nvar (\n\tErrClosed  = os.ErrClosed\n\tErrFlushed = epoll.ErrFlushed\n\terrEOR     = errors.New(\"end of ring\")\n)\n\nvar perfEventHeaderSize = binary.Size(perfEventHeader{})\n\n// perfEventHeader must match 'struct perf_event_header` in <linux/perf_event.h>.\ntype perfEventHeader struct {\n\tType uint32\n\tMisc uint16\n\tSize uint16\n}\n\n// Record contains either a sample or a counter of the\n// number of lost samples.\ntype Record struct {\n\t// The CPU this record was generated on.\n\tCPU int\n\n\t// The data submitted via bpf_perf_event_output.\n\t// Due to a kernel bug, this can contain between 0 and 7 bytes of trailing\n\t// garbage from the ring depending on the input sample's length.\n\tRawSample []byte\n\n\t// The number of samples which could not be output, since\n\t// the ring buffer was full.\n\tLostSamples uint64\n\n\t// The minimum number of bytes remaining in the per-CPU buffer after this Record has been read.\n\t// Negative for overwritable buffers.\n\tRemaining int\n}\n\n// Read a record from a reader and tag it as being from the given CPU.\n//\n// buf must be at least perfEventHeaderSize bytes long.\nfunc readRecord(rd io.Reader, rec *Record, buf []byte, overwritable bool) error {\n\t// Assert that the buffer is large enough.\n\tbuf = buf[:perfEventHeaderSize]\n\t_, err := io.ReadFull(rd, buf)\n\tif errors.Is(err, io.EOF) {\n\t\treturn errEOR\n\t} else if err != nil {\n\t\treturn fmt.Errorf(\"read perf event header: %v\", err)\n\t}\n\n\theader := perfEventHeader{\n\t\tinternal.NativeEndian.Uint32(buf[0:4]),\n\t\tinternal.NativeEndian.Uint16(buf[4:6]),\n\t\tinternal.NativeEndian.Uint16(buf[6:8]),\n\t}\n\n\tswitch header.Type {\n\tcase unix.PERF_RECORD_LOST:\n\t\trec.RawSample = rec.RawSample[:0]\n\t\trec.LostSamples, err = readLostRecords(rd)\n\t\treturn err\n\n\tcase unix.PERF_RECORD_SAMPLE:\n\t\trec.LostSamples = 0\n\t\t// We can reuse buf here because perfEventHeaderSize > perfEventSampleSize.\n\t\trec.RawSample, err = readRawSample(rd, buf, rec.RawSample)\n\t\treturn err\n\n\tdefault:\n\t\treturn &unknownEventError{header.Type}\n\t}\n}\n\nfunc readLostRecords(rd io.Reader) (uint64, error) {\n\t// lostHeader must match 'struct perf_event_lost in kernel sources.\n\tvar lostHeader struct {\n\t\tID   uint64\n\t\tLost uint64\n\t}\n\n\terr := binary.Read(rd, internal.NativeEndian, &lostHeader)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"can't read lost records header: %v\", err)\n\t}\n\n\treturn lostHeader.Lost, nil\n}\n\nvar perfEventSampleSize = binary.Size(uint32(0))\n\n// This must match 'struct perf_event_sample in kernel sources.\ntype perfEventSample struct {\n\tSize uint32\n}\n\nfunc readRawSample(rd io.Reader, buf, sampleBuf []byte) ([]byte, error) {\n\tbuf = buf[:perfEventSampleSize]\n\tif _, err := io.ReadFull(rd, buf); err != nil {\n\t\treturn nil, fmt.Errorf(\"read sample size: %w\", err)\n\t}\n\n\tsample := perfEventSample{\n\t\tinternal.NativeEndian.Uint32(buf),\n\t}\n\n\tvar data []byte\n\tif size := int(sample.Size); cap(sampleBuf) < size {\n\t\tdata = make([]byte, size)\n\t} else {\n\t\tdata = sampleBuf[:size]\n\t}\n\n\tif _, err := io.ReadFull(rd, data); err != nil {\n\t\treturn nil, fmt.Errorf(\"read sample: %w\", err)\n\t}\n\treturn data, nil\n}\n\n// Reader allows reading bpf_perf_event_output\n// from user space.\ntype Reader struct {\n\tpoller *epoll.Poller\n\n\t// mu protects read/write access to the Reader structure with the\n\t// exception fields protected by 'pauseMu'.\n\t// If locking both 'mu' and 'pauseMu', 'mu' must be locked first.\n\tmu           sync.Mutex\n\tarray        *ebpf.Map\n\trings        []*perfEventRing\n\tepollEvents  []unix.EpollEvent\n\tepollRings   []*perfEventRing\n\teventHeader  []byte\n\tdeadline     time.Time\n\toverwritable bool\n\tbufferSize   int\n\tpendingErr   error\n\n\t// pauseMu protects eventFds so that Pause / Resume can be invoked while\n\t// Read is blocked.\n\tpauseMu  sync.Mutex\n\teventFds []*sys.FD\n\tpaused   bool\n}\n\n// ReaderOptions control the behaviour of the user\n// space reader.\ntype ReaderOptions struct {\n\t// The number of events required in any per CPU buffer before\n\t// Read will process data. This is mutually exclusive with Watermark.\n\t// The default is zero, which means Watermark will take precedence.\n\tWakeupEvents int\n\t// The number of written bytes required in any per CPU buffer before\n\t// Read will process data. Must be smaller than PerCPUBuffer.\n\t// The default is to start processing as soon as data is available.\n\tWatermark int\n\t// This perf ring buffer is overwritable, once full the oldest event will be\n\t// overwritten by newest.\n\tOverwritable bool\n}\n\n// NewReader creates a new reader with default options.\n//\n// array must be a PerfEventArray. perCPUBuffer gives the size of the\n// per CPU buffer in bytes. It is rounded up to the nearest multiple\n// of the current page size.\nfunc NewReader(array *ebpf.Map, perCPUBuffer int) (*Reader, error) {\n\treturn NewReaderWithOptions(array, perCPUBuffer, ReaderOptions{})\n}\n\n// NewReaderWithOptions creates a new reader with the given options.\nfunc NewReaderWithOptions(array *ebpf.Map, perCPUBuffer int, opts ReaderOptions) (pr *Reader, err error) {\n\tcloseOnError := func(c io.Closer) {\n\t\tif err != nil {\n\t\t\tc.Close()\n\t\t}\n\t}\n\n\tif perCPUBuffer < 1 {\n\t\treturn nil, errors.New(\"perCPUBuffer must be larger than 0\")\n\t}\n\tif opts.WakeupEvents > 0 && opts.Watermark > 0 {\n\t\treturn nil, errors.New(\"WakeupEvents and Watermark cannot both be non-zero\")\n\t}\n\n\tvar (\n\t\tnCPU     = int(array.MaxEntries())\n\t\trings    = make([]*perfEventRing, 0, nCPU)\n\t\teventFds = make([]*sys.FD, 0, nCPU)\n\t)\n\n\tpoller, err := epoll.New()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer closeOnError(poller)\n\n\t// bpf_perf_event_output checks which CPU an event is enabled on,\n\t// but doesn't allow using a wildcard like -1 to specify \"all CPUs\".\n\t// Hence we have to create a ring for each CPU.\n\tbufferSize := 0\n\tfor i := 0; i < nCPU; i++ {\n\t\tevent, ring, err := newPerfEventRing(i, perCPUBuffer, opts)\n\t\tif errors.Is(err, unix.ENODEV) {\n\t\t\t// The requested CPU is currently offline, skip it.\n\t\t\tcontinue\n\t\t}\n\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to create perf ring for CPU %d: %v\", i, err)\n\t\t}\n\t\tdefer closeOnError(event)\n\t\tdefer closeOnError(ring)\n\n\t\tbufferSize = ring.size()\n\t\trings = append(rings, ring)\n\t\teventFds = append(eventFds, event)\n\n\t\tif err := poller.Add(event.Int(), 0); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\t// Closing a PERF_EVENT_ARRAY removes all event fds\n\t// stored in it, so we keep a reference alive.\n\tarray, err = array.Clone()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tpr = &Reader{\n\t\tarray:        array,\n\t\trings:        rings,\n\t\tpoller:       poller,\n\t\tdeadline:     time.Time{},\n\t\tepollEvents:  make([]unix.EpollEvent, len(rings)),\n\t\tepollRings:   make([]*perfEventRing, 0, len(rings)),\n\t\teventHeader:  make([]byte, perfEventHeaderSize),\n\t\teventFds:     eventFds,\n\t\toverwritable: opts.Overwritable,\n\t\tbufferSize:   bufferSize,\n\t}\n\tif err = pr.Resume(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn pr, nil\n}\n\n// Close frees resources used by the reader.\n//\n// It interrupts calls to Read.\n//\n// Calls to perf_event_output from eBPF programs will return\n// ENOENT after calling this method.\nfunc (pr *Reader) Close() error {\n\tif err := pr.poller.Close(); err != nil {\n\t\tif errors.Is(err, os.ErrClosed) {\n\t\t\treturn nil\n\t\t}\n\t\treturn fmt.Errorf(\"close poller: %w\", err)\n\t}\n\n\t// Trying to poll will now fail, so Read() can't block anymore. Acquire the\n\t// locks so that we can clean up.\n\tpr.mu.Lock()\n\tdefer pr.mu.Unlock()\n\n\tpr.pauseMu.Lock()\n\tdefer pr.pauseMu.Unlock()\n\n\tfor _, ring := range pr.rings {\n\t\tring.Close()\n\t}\n\tfor _, event := range pr.eventFds {\n\t\tevent.Close()\n\t}\n\tpr.rings = nil\n\tpr.eventFds = nil\n\tpr.array.Close()\n\n\treturn nil\n}\n\n// SetDeadline controls how long Read and ReadInto will block waiting for samples.\n//\n// Passing a zero time.Time will remove the deadline. Passing a deadline in the\n// past will prevent the reader from blocking if there are no records to be read.\nfunc (pr *Reader) SetDeadline(t time.Time) {\n\tpr.mu.Lock()\n\tdefer pr.mu.Unlock()\n\n\tpr.deadline = t\n}\n\n// Read the next record from the perf ring buffer.\n//\n// The method blocks until there are at least Watermark bytes in one\n// of the per CPU buffers. Records from buffers below the Watermark\n// are not returned.\n//\n// Records can contain between 0 and 7 bytes of trailing garbage from the ring\n// depending on the input sample's length.\n//\n// Calling [Close] interrupts the method with [os.ErrClosed]. Calling [Flush]\n// makes it return all records currently in the ring buffer, followed by [ErrFlushed].\n//\n// Returns [os.ErrDeadlineExceeded] if a deadline was set and after all records\n// have been read from the ring.\n//\n// See [Reader.ReadInto] for a more efficient version of this method.\nfunc (pr *Reader) Read() (Record, error) {\n\tvar r Record\n\n\treturn r, pr.ReadInto(&r)\n}\n\nvar errMustBePaused = fmt.Errorf(\"perf ringbuffer: must have been paused before reading overwritable buffer\")\n\n// ReadInto is like [Reader.Read] except that it allows reusing Record and associated buffers.\nfunc (pr *Reader) ReadInto(rec *Record) error {\n\tpr.mu.Lock()\n\tdefer pr.mu.Unlock()\n\n\tpr.pauseMu.Lock()\n\tdefer pr.pauseMu.Unlock()\n\n\tif pr.overwritable && !pr.paused {\n\t\treturn errMustBePaused\n\t}\n\n\tif pr.rings == nil {\n\t\treturn fmt.Errorf(\"perf ringbuffer: %w\", ErrClosed)\n\t}\n\n\tfor {\n\t\tif len(pr.epollRings) == 0 {\n\t\t\tif pe := pr.pendingErr; pe != nil {\n\t\t\t\t// All rings have been emptied since the error occurred, return\n\t\t\t\t// appropriate error.\n\t\t\t\tpr.pendingErr = nil\n\t\t\t\treturn pe\n\t\t\t}\n\n\t\t\t// NB: The deferred pauseMu.Unlock will panic if Wait panics, which\n\t\t\t// might obscure the original panic.\n\t\t\tpr.pauseMu.Unlock()\n\t\t\t_, err := pr.poller.Wait(pr.epollEvents, pr.deadline)\n\t\t\tpr.pauseMu.Lock()\n\n\t\t\tif errors.Is(err, os.ErrDeadlineExceeded) || errors.Is(err, ErrFlushed) {\n\t\t\t\t// We've hit the deadline, check whether there is any data in\n\t\t\t\t// the rings that we've not been woken up for.\n\t\t\t\tpr.pendingErr = err\n\t\t\t} else if err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\t// Re-validate pr.paused since we dropped pauseMu.\n\t\t\tif pr.overwritable && !pr.paused {\n\t\t\t\treturn errMustBePaused\n\t\t\t}\n\n\t\t\t// Waking up userspace is expensive, make the most of it by checking\n\t\t\t// all rings.\n\t\t\tfor _, ring := range pr.rings {\n\t\t\t\tring.loadHead()\n\t\t\t\tpr.epollRings = append(pr.epollRings, ring)\n\t\t\t}\n\t\t}\n\n\t\t// Start at the last available event. The order in which we\n\t\t// process them doesn't matter, and starting at the back allows\n\t\t// resizing epollRings to keep track of processed rings.\n\t\terr := pr.readRecordFromRing(rec, pr.epollRings[len(pr.epollRings)-1])\n\t\tif err == errEOR {\n\t\t\t// We've emptied the current ring buffer, process\n\t\t\t// the next one.\n\t\t\tpr.epollRings = pr.epollRings[:len(pr.epollRings)-1]\n\t\t\tcontinue\n\t\t}\n\n\t\treturn err\n\t}\n}\n\n// Pause stops all notifications from this Reader.\n//\n// While the Reader is paused, any attempts to write to the event buffer from\n// BPF programs will return -ENOENT.\n//\n// Subsequent calls to Read will block until a call to Resume.\nfunc (pr *Reader) Pause() error {\n\tpr.pauseMu.Lock()\n\tdefer pr.pauseMu.Unlock()\n\n\tif pr.eventFds == nil {\n\t\treturn fmt.Errorf(\"%w\", ErrClosed)\n\t}\n\n\tfor i := range pr.eventFds {\n\t\tif err := pr.array.Delete(uint32(i)); err != nil && !errors.Is(err, ebpf.ErrKeyNotExist) {\n\t\t\treturn fmt.Errorf(\"could't delete event fd for CPU %d: %w\", i, err)\n\t\t}\n\t}\n\n\tpr.paused = true\n\n\treturn nil\n}\n\n// Resume allows this perf reader to emit notifications.\n//\n// Subsequent calls to Read will block until the next event notification.\nfunc (pr *Reader) Resume() error {\n\tpr.pauseMu.Lock()\n\tdefer pr.pauseMu.Unlock()\n\n\tif pr.eventFds == nil {\n\t\treturn fmt.Errorf(\"%w\", ErrClosed)\n\t}\n\n\tfor i, fd := range pr.eventFds {\n\t\tif fd == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif err := pr.array.Put(uint32(i), fd.Uint()); err != nil {\n\t\t\treturn fmt.Errorf(\"couldn't put event fd %d for CPU %d: %w\", fd, i, err)\n\t\t}\n\t}\n\n\tpr.paused = false\n\n\treturn nil\n}\n\n// BufferSize is the size in bytes of each per-CPU buffer\nfunc (pr *Reader) BufferSize() int {\n\treturn pr.bufferSize\n}\n\n// Flush unblocks Read/ReadInto and successive Read/ReadInto calls will return pending samples at this point,\n// until you receive a [ErrFlushed] error.\nfunc (pr *Reader) Flush() error {\n\treturn pr.poller.Flush()\n}\n\n// NB: Has to be preceded by a call to ring.loadHead.\nfunc (pr *Reader) readRecordFromRing(rec *Record, ring *perfEventRing) error {\n\tdefer ring.writeTail()\n\n\trec.CPU = ring.cpu\n\terr := readRecord(ring, rec, pr.eventHeader, pr.overwritable)\n\tif pr.overwritable && (errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF)) {\n\t\treturn errEOR\n\t}\n\trec.Remaining = ring.remaining()\n\treturn err\n}\n\ntype unknownEventError struct {\n\teventType uint32\n}\n\nfunc (uev *unknownEventError) Error() string {\n\treturn fmt.Sprintf(\"unknown event type: %d\", uev.eventType)\n}\n\n// IsUnknownEvent returns true if the error occurred\n// because an unknown event was submitted to the perf event ring.\nfunc IsUnknownEvent(err error) bool {\n\tvar uee *unknownEventError\n\treturn errors.As(err, &uee)\n}\n"
  },
  {
    "path": "perf/reader_test.go",
    "content": "//go:build !windows\n\npackage perf\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"os\"\n\t\"syscall\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n\t\"github.com/cilium/ebpf/internal/testutils/testmain\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nvar (\n\treadTimeout = 250 * time.Millisecond\n)\n\nfunc TestMain(m *testing.M) {\n\ttestmain.Run(m)\n}\n\nfunc TestPerfReader(t *testing.T) {\n\tevents := perfEventArray(t)\n\n\trd, err := NewReader(events, 4096)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer rd.Close()\n\n\tqt.Assert(t, qt.Equals(rd.BufferSize(), 4096))\n\n\toutputSamples(t, events, 5, 5)\n\n\t_, rem := checkRecord(t, rd)\n\tqt.Assert(t, qt.IsTrue(rem >= 5), qt.Commentf(\"expected at least 5 Remaining\"))\n\n\t_, rem = checkRecord(t, rd)\n\tqt.Assert(t, qt.Equals(rem, 0), qt.Commentf(\"expected zero Remaining\"))\n\n\trd.SetDeadline(time.Now().Add(4 * time.Millisecond))\n\t_, err = rd.Read()\n\tqt.Assert(t, qt.ErrorIs(err, os.ErrDeadlineExceeded), qt.Commentf(\"expected os.ErrDeadlineExceeded\"))\n}\n\nfunc TestReaderSetDeadline(t *testing.T) {\n\tevents := perfEventArray(t)\n\n\trd, err := NewReader(events, 4096)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer rd.Close()\n\n\trd.SetDeadline(time.Now().Add(-time.Second))\n\tif _, err := rd.Read(); !errors.Is(err, os.ErrDeadlineExceeded) {\n\t\tt.Error(\"Expected os.ErrDeadlineExceeded from first Read, got:\", err)\n\t}\n\tif _, err := rd.Read(); !errors.Is(err, os.ErrDeadlineExceeded) {\n\t\tt.Error(\"Expected os.ErrDeadlineExceeded from second Read, got:\", err)\n\t}\n\n\trd.SetDeadline(time.Now().Add(10 * time.Millisecond))\n\tif _, err := rd.Read(); !errors.Is(err, os.ErrDeadlineExceeded) {\n\t\tt.Error(\"Expected os.ErrDeadlineExceeded from third Read, got:\", err)\n\t}\n}\n\nfunc TestReaderSetDeadlinePendingEvents(t *testing.T) {\n\tevents := perfEventArray(t)\n\n\trd, err := NewReaderWithOptions(events, 4096, ReaderOptions{WakeupEvents: 2})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer rd.Close()\n\n\toutputSamples(t, events, 5)\n\n\trd.SetDeadline(time.Now().Add(-time.Second))\n\t_, rem := checkRecord(t, rd)\n\tqt.Assert(t, qt.Equals(rem, 0), qt.Commentf(\"expected zero Remaining\"))\n\n\toutputSamples(t, events, 5)\n\n\t// another sample should not be returned before we get ErrFlushed to indicate initial set of samples read\n\t_, err = rd.Read()\n\tif !errors.Is(err, os.ErrDeadlineExceeded) {\n\t\tt.Error(\"Expected os.ErrDeadlineExceeded from second Read, got:\", err)\n\t}\n\n\t// the second sample should now be read\n\t_, _ = checkRecord(t, rd)\n}\n\nfunc TestReaderFlushPendingEvents(t *testing.T) {\n\ttestutils.LockOSThreadToSingleCPU(t)\n\tevents := perfEventArray(t)\n\n\trd, err := NewReaderWithOptions(events, 4096, ReaderOptions{WakeupEvents: 2})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer rd.Close()\n\n\toutputSamples(t, events, 5)\n\n\twait := make(chan int)\n\tgo func() {\n\t\twait <- 0\n\t\t_, rem := checkRecord(t, rd)\n\t\twait <- rem\n\t}()\n\n\t<-wait\n\ttime.Sleep(10 * time.Millisecond)\n\terr = rd.Flush()\n\tqt.Assert(t, qt.IsNil(err))\n\n\trem := <-wait\n\tqt.Assert(t, qt.Equals(rem, 0), qt.Commentf(\"expected zero Remaining\"))\n\n\toutputSamples(t, events, 5)\n\n\t// another sample should not be returned before we get ErrFlushed to indicate initial set of samples read\n\t_, err = rd.Read()\n\tif !errors.Is(err, ErrFlushed) {\n\t\tt.Error(\"Expected ErrFlushed from second Read, got:\", err)\n\t}\n\n\t// the second sample should now be read\n\t_, _ = checkRecord(t, rd)\n}\n\nfunc outputSamples(tb testing.TB, events *ebpf.Map, sampleSizes ...byte) {\n\tprog := outputSamplesProg(tb, events, sampleSizes...)\n\n\tret, _, err := prog.Test(internal.EmptyBPFContext)\n\ttestutils.SkipIfNotSupported(tb, err)\n\tif err != nil {\n\t\ttb.Fatal(err)\n\t}\n\n\tif errno := syscall.Errno(-int32(ret)); errno != 0 {\n\t\ttb.Fatal(\"Expected 0 as return value, got\", errno)\n\t}\n}\n\n// outputSamplesProg creates a program which submits a series of samples to a PerfEventArray.\n//\n// The format of each sample is:\n//\n//\tindex:   0    1    2    3    ... size - 1\n//\tcontent: size id   0xff 0xff ... 0xff     [padding]\n//\n// padding is an implementation detail of the perf buffer and 1-7 bytes long. The\n// contents are undefined.\nfunc outputSamplesProg(tb testing.TB, events *ebpf.Map, sampleSizes ...byte) *ebpf.Program {\n\ttb.Helper()\n\n\t// Requires at least 4.9 (0515e5999a46 \"bpf: introduce BPF_PROG_TYPE_PERF_EVENT program type\")\n\ttestutils.SkipOnOldKernel(tb, \"4.9\", \"perf events support\")\n\n\tconst bpfFCurrentCPU = 0xffffffff\n\n\tvar maxSampleSize byte\n\tfor _, sampleSize := range sampleSizes {\n\t\tif sampleSize < 2 {\n\t\t\ttb.Fatalf(\"Sample size %d is too small to contain size and counter\", sampleSize)\n\t\t}\n\t\tif sampleSize > maxSampleSize {\n\t\t\tmaxSampleSize = sampleSize\n\t\t}\n\t}\n\n\t// Fill a buffer on the stack, and stash context somewhere\n\tinsns := asm.Instructions{\n\t\tasm.LoadImm(asm.R0, ^int64(0), asm.DWord),\n\t\tasm.Mov.Reg(asm.R9, asm.R1),\n\t}\n\n\tbufDwords := int(maxSampleSize/8) + 1\n\tfor i := 0; i < bufDwords; i++ {\n\t\tinsns = append(insns,\n\t\t\tasm.StoreMem(asm.RFP, int16(i+1)*-8, asm.R0, asm.DWord),\n\t\t)\n\t}\n\n\tfor i, sampleSize := range sampleSizes {\n\t\tinsns = append(insns,\n\t\t\t// Restore stashed context.\n\t\t\tasm.Mov.Reg(asm.R1, asm.R9),\n\t\t\t// map\n\t\t\tasm.LoadMapPtr(asm.R2, events.FD()),\n\t\t\t// flags\n\t\t\tasm.LoadImm(asm.R3, bpfFCurrentCPU, asm.DWord),\n\t\t\t// buffer\n\t\t\tasm.Mov.Reg(asm.R4, asm.RFP),\n\t\t\tasm.Add.Imm(asm.R4, int32(bufDwords*-8)),\n\t\t\t// buffer[0] = size\n\t\t\tasm.StoreImm(asm.R4, 0, int64(sampleSize), asm.Byte),\n\t\t\t// buffer[1] = i\n\t\t\tasm.StoreImm(asm.R4, 1, int64(i&math.MaxUint8), asm.Byte),\n\t\t\t// size\n\t\t\tasm.Mov.Imm(asm.R5, int32(sampleSize)),\n\t\t\tasm.FnPerfEventOutput.Call(),\n\t\t)\n\t}\n\n\tinsns = append(insns, asm.Return())\n\n\tprog, err := ebpf.NewProgram(&ebpf.ProgramSpec{\n\t\tLicense:      \"GPL\",\n\t\tType:         ebpf.XDP,\n\t\tInstructions: insns,\n\t})\n\tif err != nil {\n\t\ttb.Fatal(err)\n\t}\n\ttb.Cleanup(func() { prog.Close() })\n\n\treturn prog\n}\n\nfunc checkRecord(tb testing.TB, rd *Reader) (id int, remaining int) {\n\ttb.Helper()\n\n\trec, err := rd.Read()\n\tqt.Assert(tb, qt.IsNil(err))\n\n\tqt.Assert(tb, qt.IsTrue(rec.CPU >= 0), qt.Commentf(\"Record has invalid CPU number\"))\n\n\tsize := int(rec.RawSample[0])\n\tqt.Assert(tb, qt.IsTrue(len(rec.RawSample) >= size), qt.Commentf(\"RawSample is at least size bytes\"))\n\n\tfor i, v := range rec.RawSample[2:size] {\n\t\tqt.Assert(tb, qt.Equals(v, 0xff), qt.Commentf(\"filler at position %d should match\", i+2))\n\t}\n\n\t// padding is ignored since it's value is undefined.\n\n\treturn int(rec.RawSample[1]), rec.Remaining\n}\n\nfunc TestPerfReaderLostSample(t *testing.T) {\n\t// To generate a lost sample perf record:\n\t//\n\t// 1. Fill the perf ring buffer almost completely, with the output_large program.\n\t//    The buffer is sized in number of pages, which are architecture dependant.\n\t//\n\t// 2. Write an extra event that doesn't fit in the space remaining.\n\t//\n\t// 3. Write a smaller event that does fit, with output_single program.\n\t//    Lost sample records are generated opportunistically, when the kernel\n\t//    is writing an event and realizes that there were events lost previously.\n\t//\n\t// The event size is hardcoded in the test BPF programs, there's no way\n\t// to parametrize it without rebuilding the programs.\n\t//\n\t// The event size needs to be selected so that, for any page size, there are at least\n\t// 48 bytes left in the perf ring page after filling it with a whole number of events:\n\t//\n\t//  - PERF_RECORD_LOST: 8 (perf_event_header) + 16 (PERF_RECORD_LOST)\n\t//\n\t//  - output_single: 8 (perf_event_header) + 4 (size) + 5 (payload) + 7 (padding to 64bits)\n\t//\n\t// By selecting an event size of the form 2^n + 2^(n+1), for any page size 2^(n+m), m >= 0,\n\t// the number of bytes left, x, after filling a page with a whole number of events is:\n\t//\n\t//                     2^(n+m)                            2^n * 2^m\n\t//  x = 2^n * frac(---------------) <=> x = 2^n * frac(---------------)\n\t//                  2^n + 2^(n+1)                       2^n + 2^n * 2\n\t//\n\t//                                                        2^n * 2^m\n\t//                                  <=> x = 2^n * frac(---------------)\n\t//                                                      2^n * (1 + 2)\n\t//\n\t//                                                      2^m\n\t//                                  <=> x = 2^n * frac(-----)\n\t//                                                       3\n\t//\n\t//                                                1                2\n\t//                                  <=> x = 2^n * -  or  x = 2^n * -\n\t//                                                3                3\n\t//\n\t// Selecting n = 6, we have:\n\t//\n\t//  x = 64  or  x = 128, no matter the page size 2^(6+m)\n\t//\n\t//  event size = 2^6 + 2^7 = 192\n\t//\n\t// Accounting for perf headers, output_large uses a 180 byte payload:\n\t//\n\t//  8 (perf_event_header) + 4 (size) + 180 (payload)\n\tconst (\n\t\teventSize = 192\n\t)\n\n\tvar (\n\t\tpageSize  = os.Getpagesize()\n\t\tmaxEvents = (pageSize / eventSize)\n\t)\n\tif remainder := pageSize % eventSize; remainder != 64 && remainder != 128 {\n\t\t// Page size isn't 2^(6+m), m >= 0\n\t\tt.Fatal(\"unsupported page size:\", pageSize)\n\t}\n\n\tvar sampleSizes []byte\n\t// Fill the ring with the maximum number of output_large events that will fit,\n\t// and generate a lost event by writing an additional event.\n\tfor i := 0; i < maxEvents+1; i++ {\n\t\tsampleSizes = append(sampleSizes, 180)\n\t}\n\n\t// Generate a small event to trigger the lost record\n\tsampleSizes = append(sampleSizes, 5)\n\n\tevents := perfEventArray(t)\n\n\trd, err := NewReader(events, pageSize)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer rd.Close()\n\n\toutputSamples(t, events, sampleSizes...)\n\n\tfor range sampleSizes {\n\t\trecord, err := rd.Read()\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tif record.RawSample == nil && record.LostSamples != 1 {\n\t\t\tt.Fatal(\"Expected a record with LostSamples 1, got\", record.LostSamples)\n\t\t}\n\t}\n}\n\nfunc TestPerfReaderOverwritable(t *testing.T) {\n\t// Smallest buffer size.\n\tpageSize := os.Getpagesize()\n\n\tconst sampleSize = math.MaxUint8\n\n\t// Account for perf header (8) and size (4), align to 8 bytes as perf does.\n\trealSampleSize := internal.Align(sampleSize+8+4, 8)\n\tmaxEvents := pageSize / realSampleSize\n\n\tvar sampleSizes []byte\n\tfor i := 0; i < maxEvents; i++ {\n\t\tsampleSizes = append(sampleSizes, sampleSize)\n\t}\n\t// Append an extra sample that will overwrite the first sample.\n\tsampleSizes = append(sampleSizes, sampleSize)\n\n\tevents := perfEventArray(t)\n\n\trd, err := NewReaderWithOptions(events, pageSize, ReaderOptions{Overwritable: true})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer rd.Close()\n\n\t_, err = rd.Read()\n\tqt.Assert(t, qt.ErrorIs(err, errMustBePaused))\n\n\toutputSamples(t, events, sampleSizes...)\n\n\tqt.Assert(t, qt.IsNil(rd.Pause()))\n\trd.SetDeadline(time.Now())\n\n\tnextID := maxEvents\n\tfor i := 0; i < maxEvents; i++ {\n\t\tid, rem := checkRecord(t, rd)\n\t\tqt.Assert(t, qt.Equals(id, nextID))\n\t\tqt.Assert(t, qt.Equals(rem, -1))\n\t\tnextID--\n\t}\n}\n\nfunc TestPerfReaderOverwritableEmpty(t *testing.T) {\n\tevents := perfEventArray(t)\n\trd, err := NewReaderWithOptions(events, os.Getpagesize(), ReaderOptions{Overwritable: true})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer rd.Close()\n\n\terr = rd.Pause()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\trd.SetDeadline(time.Now().Add(4 * time.Millisecond))\n\t_, err = rd.Read()\n\tqt.Assert(t, qt.ErrorIs(err, os.ErrDeadlineExceeded), qt.Commentf(\"expected os.ErrDeadlineExceeded\"))\n\n\terr = rd.Resume()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n}\n\nfunc TestPerfReaderClose(t *testing.T) {\n\tevents := perfEventArray(t)\n\n\trd, err := NewReader(events, 4096)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer rd.Close()\n\n\terrs := make(chan error, 1)\n\twaiting := make(chan struct{})\n\tgo func() {\n\t\tclose(waiting)\n\t\t_, err := rd.Read()\n\t\terrs <- err\n\t}()\n\n\t<-waiting\n\n\t// Close should interrupt Read\n\tif err := rd.Close(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tselect {\n\tcase <-errs:\n\tcase <-time.After(time.Second):\n\t\tt.Fatal(\"Close doesn't interrupt Read\")\n\t}\n\n\t// And we should be able to call it multiple times\n\tif err := rd.Close(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif _, err := rd.Read(); err == nil {\n\t\tt.Fatal(\"Read on a closed PerfReader doesn't return an error\")\n\t}\n}\n\nfunc TestCreatePerfEvent(t *testing.T) {\n\tfd, err := createPerfEvent(0, ReaderOptions{Watermark: 1, Overwritable: false})\n\tif err != nil {\n\t\tt.Fatal(\"Can't create perf event:\", err)\n\t}\n\tfd.Close()\n}\n\nfunc TestReadRecord(t *testing.T) {\n\tvar buf bytes.Buffer\n\n\terr := binary.Write(&buf, internal.NativeEndian, &perfEventHeader{})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tvar rec Record\n\terr = readRecord(&buf, &rec, make([]byte, perfEventHeaderSize), false)\n\tif !IsUnknownEvent(err) {\n\t\tt.Error(\"readRecord should return unknown event error, got\", err)\n\t}\n}\n\nfunc TestPause(t *testing.T) {\n\tt.Parallel()\n\n\tevents := perfEventArray(t)\n\n\trd, err := NewReader(events, 4096)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer rd.Close()\n\n\t// Reader is already unpaused by default. It should be idempotent.\n\tif err = rd.Resume(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// Write a sample. The reader should read it.\n\tprog := outputSamplesProg(t, events, 5)\n\tret, _, err := prog.Test(internal.EmptyBPFContext)\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil || ret != 0 {\n\t\tt.Fatal(\"Can't write sample\")\n\t}\n\tif _, err := rd.Read(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// Pause. No notification should trigger.\n\tif err = rd.Pause(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\terrChan := make(chan error, 1)\n\tgo func() {\n\t\t// Read one notification then send any errors and exit.\n\t\t_, err := rd.Read()\n\t\terrChan <- err\n\t}()\n\tret, _, err = prog.Test(internal.EmptyBPFContext)\n\tif err == nil && ret == 0 {\n\t\tt.Fatal(\"Unexpectedly wrote sample while paused\")\n\t} // else Success\n\tselect {\n\tcase err := <-errChan:\n\t\t// Failure: Pause was unsuccessful.\n\t\tt.Fatalf(\"received notification on paused reader: %s\", err)\n\tcase <-time.After(readTimeout):\n\t\t// Success\n\t}\n\n\t// Pause should be idempotent.\n\tif err = rd.Pause(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// Resume. Now notifications should continue.\n\tif err = rd.Resume(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tret, _, err = prog.Test(internal.EmptyBPFContext)\n\tif err != nil || ret != 0 {\n\t\tt.Fatal(\"Can't write sample\")\n\t}\n\tselect {\n\tcase err := <-errChan:\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t} // else Success\n\tcase <-time.After(readTimeout):\n\t\tt.Fatal(\"timed out waiting for notification after resume\")\n\t}\n\n\tif err = rd.Close(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// Pause/Resume after close should be no-op.\n\terr = rd.Pause()\n\tqt.Assert(t, qt.Not(qt.Equals(err, ErrClosed)), qt.Commentf(\"returns unwrapped ErrClosed\"))\n\tqt.Assert(t, qt.ErrorIs(err, ErrClosed), qt.Commentf(\"doesn't wrap ErrClosed\"))\n\n\terr = rd.Resume()\n\tqt.Assert(t, qt.Not(qt.Equals(err, ErrClosed)), qt.Commentf(\"returns unwrapped ErrClosed\"))\n\tqt.Assert(t, qt.ErrorIs(err, ErrClosed), qt.Commentf(\"doesn't wrap ErrClosed\"))\n}\n\nfunc TestPerfReaderWakeupEvents(t *testing.T) {\n\ttestutils.LockOSThreadToSingleCPU(t)\n\n\tevents := perfEventArray(t)\n\n\tnumEvents := 2\n\trd, err := NewReaderWithOptions(events, 4096, ReaderOptions{WakeupEvents: numEvents})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer rd.Close()\n\n\tprog := outputSamplesProg(t, events, 5)\n\n\t// Send enough events to trigger WakeupEvents.\n\tfor i := 0; i < numEvents; i++ {\n\t\t_, _, err = prog.Test(internal.EmptyBPFContext)\n\t\ttestutils.SkipIfNotSupported(t, err)\n\t\tqt.Assert(t, qt.IsNil(err))\n\t}\n\n\ttime.AfterFunc(5*time.Second, func() {\n\t\t// Interrupt Read() in case the implementation is buggy.\n\t\trd.Close()\n\t})\n\n\tfor i := 0; i < numEvents; i++ {\n\t\tcheckRecord(t, rd)\n\t}\n}\n\nfunc TestReadWithoutWakeup(t *testing.T) {\n\tt.Parallel()\n\n\tevents := perfEventArray(t)\n\n\trd, err := NewReaderWithOptions(events, 1, ReaderOptions{WakeupEvents: 2})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer rd.Close()\n\n\tprog := outputSamplesProg(t, events, 5)\n\tret, _, err := prog.Test(internal.EmptyBPFContext)\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.Equals(ret, 0))\n\n\trd.SetDeadline(time.Now())\n\tcheckRecord(t, rd)\n}\n\nfunc BenchmarkReader(b *testing.B) {\n\tevents := perfEventArray(b)\n\tprog := outputSamplesProg(b, events, 80)\n\n\trd, err := NewReader(events, 4096)\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\tdefer rd.Close()\n\n\tbuf := internal.EmptyBPFContext\n\n\tb.ReportAllocs()\n\tfor b.Loop() {\n\t\tret, _, err := prog.Test(buf)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t} else if errno := syscall.Errno(-int32(ret)); errno != 0 {\n\t\t\tb.Fatal(\"Expected 0 as return value, got\", errno)\n\t\t}\n\n\t\tif _, err = rd.Read(); err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkReadInto(b *testing.B) {\n\tevents := perfEventArray(b)\n\tprog := outputSamplesProg(b, events, 80)\n\n\trd, err := NewReader(events, 4096)\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\tdefer rd.Close()\n\n\tbuf := internal.EmptyBPFContext\n\n\tb.ReportAllocs()\n\n\tvar rec Record\n\tfor b.Loop() {\n\t\t// NB: Submitting samples into the perf event ring dominates\n\t\t// the benchmark time unfortunately.\n\t\tret, _, err := prog.Test(buf)\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t} else if errno := syscall.Errno(-int32(ret)); errno != 0 {\n\t\t\tb.Fatal(\"Expected 0 as return value, got\", errno)\n\t\t}\n\n\t\tif err := rd.ReadInto(&rec); err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\n// This exists just to make the example below nicer.\nfunc bpfPerfEventOutputProgram() (*ebpf.Program, *ebpf.Map) {\n\treturn nil, nil\n}\n\n// ExampleReader submits a perf event using BPF,\n// and then reads it in user space.\n//\n// The BPF will look something like this:\n//\n//\tstruct map events __section(\"maps\") = {\n//\t  .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,\n//\t};\n//\n//\t__section(\"xdp\") int output_single(void *ctx) {\n//\t  unsigned char buf[] = {\n//\t    1, 2, 3, 4, 5\n//\t  };\n//\n//\t   return perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &buf[0], 5);\n//\t }\n//\n// Also see BPF_F_CTXLEN_MASK if you want to sample packet data\n// from SKB or XDP programs.\nfunc ExampleReader() {\n\tprog, events := bpfPerfEventOutputProgram()\n\tdefer prog.Close()\n\tdefer events.Close()\n\n\trd, err := NewReader(events, 4096)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer rd.Close()\n\n\t// Writes out a sample with content 1,2,3,4,4\n\tret, _, err := prog.Test(internal.EmptyBPFContext)\n\tif err != nil || ret != 0 {\n\t\tpanic(\"Can't write sample\")\n\t}\n\n\trecord, err := rd.Read()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Data is padded with 0 for alignment\n\tfmt.Println(\"Sample:\", record.RawSample)\n}\n\n// ReadRecord allows reducing memory allocations.\nfunc ExampleReader_ReadInto() {\n\tprog, events := bpfPerfEventOutputProgram()\n\tdefer prog.Close()\n\tdefer events.Close()\n\n\trd, err := NewReader(events, 4096)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer rd.Close()\n\n\tfor i := 0; i < 2; i++ {\n\t\t// Write out two samples\n\t\tret, _, err := prog.Test(internal.EmptyBPFContext)\n\t\tif err != nil || ret != 0 {\n\t\t\tpanic(\"Can't write sample\")\n\t\t}\n\t}\n\n\tvar rec Record\n\tfor i := 0; i < 2; i++ {\n\t\tif err := rd.ReadInto(&rec); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\tfmt.Println(\"Sample:\", rec.RawSample[:5])\n\t}\n}\n\nfunc perfEventArray(tb testing.TB) *ebpf.Map {\n\tevents, err := ebpf.NewMap(&ebpf.MapSpec{\n\t\tType: ebpf.PerfEventArray,\n\t})\n\tif err != nil {\n\t\ttb.Fatal(err)\n\t}\n\ttb.Cleanup(func() { events.Close() })\n\treturn events\n}\n"
  },
  {
    "path": "perf/ring.go",
    "content": "//go:build !windows\n\npackage perf\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"os\"\n\t\"runtime\"\n\t\"sync/atomic\"\n\t\"unsafe\"\n\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\n// perfEventRing is a page of metadata followed by\n// a variable number of pages which form a ring buffer.\ntype perfEventRing struct {\n\tcpu  int\n\tmmap []byte\n\tringReader\n\tcleanup runtime.Cleanup\n}\n\nfunc newPerfEventRing(cpu, perCPUBuffer int, opts ReaderOptions) (_ *sys.FD, _ *perfEventRing, err error) {\n\tcloseOnError := func(c io.Closer) {\n\t\tif err != nil {\n\t\t\tc.Close()\n\t\t}\n\t}\n\n\tif opts.Watermark >= perCPUBuffer {\n\t\treturn nil, nil, errors.New(\"watermark must be smaller than perCPUBuffer\")\n\t}\n\n\tfd, err := createPerfEvent(cpu, opts)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tdefer closeOnError(fd)\n\n\tif err := unix.SetNonblock(fd.Int(), true); err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tprotections := unix.PROT_READ\n\tif !opts.Overwritable {\n\t\tprotections |= unix.PROT_WRITE\n\t}\n\n\tmmap, err := unix.Mmap(fd.Int(), 0, perfBufferSize(perCPUBuffer), protections, unix.MAP_SHARED)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"can't mmap: %v\", err)\n\t}\n\n\t// This relies on the fact that we allocate an extra metadata page,\n\t// and that the struct is smaller than an OS page.\n\t// This use of unsafe.Pointer isn't explicitly sanctioned by the\n\t// documentation, since a byte is smaller than sampledPerfEvent.\n\tmeta := (*unix.PerfEventMmapPage)(unsafe.Pointer(&mmap[0]))\n\n\tvar reader ringReader\n\tif opts.Overwritable {\n\t\treader = newReverseReader(meta, mmap[meta.Data_offset:meta.Data_offset+meta.Data_size])\n\t} else {\n\t\treader = newForwardReader(meta, mmap[meta.Data_offset:meta.Data_offset+meta.Data_size])\n\t}\n\n\tring := &perfEventRing{\n\t\tcpu:        cpu,\n\t\tmmap:       mmap,\n\t\tringReader: reader,\n\t}\n\tring.cleanup = runtime.AddCleanup(ring, func(mmap []byte) {\n\t\t_ = unix.Munmap(mmap)\n\t}, ring.mmap)\n\n\treturn fd, ring, nil\n}\n\n// perfBufferSize returns a valid mmap buffer size for use with perf_event_open (1+2^n pages)\nfunc perfBufferSize(perCPUBuffer int) int {\n\tpageSize := os.Getpagesize()\n\n\t// Smallest whole number of pages\n\tnPages := (perCPUBuffer + pageSize - 1) / pageSize\n\n\t// Round up to nearest power of two number of pages\n\tnPages = int(math.Pow(2, math.Ceil(math.Log2(float64(nPages)))))\n\n\t// Add one for metadata\n\tnPages += 1\n\n\treturn nPages * pageSize\n}\n\nfunc (ring *perfEventRing) Close() error {\n\tring.cleanup.Stop()\n\tmmap := ring.mmap\n\tring.mmap = nil\n\treturn unix.Munmap(mmap)\n}\n\nfunc createPerfEvent(cpu int, opts ReaderOptions) (*sys.FD, error) {\n\twakeup := 0\n\tbits := 0\n\tif opts.WakeupEvents > 0 {\n\t\twakeup = opts.WakeupEvents\n\t} else {\n\t\twakeup = opts.Watermark\n\t\tif wakeup == 0 {\n\t\t\twakeup = 1\n\t\t}\n\t\tbits |= unix.PerfBitWatermark\n\t}\n\n\tif opts.Overwritable {\n\t\tbits |= unix.PerfBitWriteBackward\n\t}\n\n\tattr := unix.PerfEventAttr{\n\t\tType:        unix.PERF_TYPE_SOFTWARE,\n\t\tConfig:      unix.PERF_COUNT_SW_BPF_OUTPUT,\n\t\tBits:        uint64(bits),\n\t\tSample_type: unix.PERF_SAMPLE_RAW,\n\t\tWakeup:      uint32(wakeup),\n\t}\n\n\tattr.Size = uint32(unsafe.Sizeof(attr))\n\tfd, err := unix.PerfEventOpen(&attr, -1, cpu, -1, unix.PERF_FLAG_FD_CLOEXEC)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't create perf event: %w\", err)\n\t}\n\treturn sys.NewFD(fd)\n}\n\ntype ringReader interface {\n\tloadHead()\n\tsize() int\n\tremaining() int\n\twriteTail()\n\tRead(p []byte) (int, error)\n}\n\ntype forwardReader struct {\n\tmeta       *unix.PerfEventMmapPage\n\thead, tail uint64\n\tmask       uint64\n\tring       []byte\n}\n\nfunc newForwardReader(meta *unix.PerfEventMmapPage, ring []byte) *forwardReader {\n\treturn &forwardReader{\n\t\tmeta: meta,\n\t\thead: atomic.LoadUint64(&meta.Data_head),\n\t\ttail: atomic.LoadUint64(&meta.Data_tail),\n\t\t// cap is always a power of two\n\t\tmask: uint64(cap(ring) - 1),\n\t\tring: ring,\n\t}\n}\n\nfunc (rr *forwardReader) loadHead() {\n\trr.head = atomic.LoadUint64(&rr.meta.Data_head)\n}\n\nfunc (rr *forwardReader) size() int {\n\treturn len(rr.ring)\n}\n\nfunc (rr *forwardReader) remaining() int {\n\treturn int((rr.head - rr.tail) & rr.mask)\n}\n\nfunc (rr *forwardReader) writeTail() {\n\t// Commit the new tail. This lets the kernel know that\n\t// the ring buffer has been consumed.\n\tatomic.StoreUint64(&rr.meta.Data_tail, rr.tail)\n}\n\nfunc (rr *forwardReader) Read(p []byte) (int, error) {\n\tstart := int(rr.tail & rr.mask)\n\n\tn := len(p)\n\t// Truncate if the read wraps in the ring buffer\n\tif remainder := cap(rr.ring) - start; n > remainder {\n\t\tn = remainder\n\t}\n\n\t// Truncate if there isn't enough data\n\tif remainder := int(rr.head - rr.tail); n > remainder {\n\t\tn = remainder\n\t}\n\n\tcopy(p, rr.ring[start:start+n])\n\trr.tail += uint64(n)\n\n\tif rr.tail == rr.head {\n\t\treturn n, io.EOF\n\t}\n\n\treturn n, nil\n}\n\ntype reverseReader struct {\n\tmeta *unix.PerfEventMmapPage\n\t// head is the position where the kernel last wrote data.\n\thead uint64\n\t// read is the position we read the next data from. Updated as reads are made.\n\tread uint64\n\t// tail is the end of the ring buffer. No reads must be made past it.\n\ttail uint64\n\tmask uint64\n\tring []byte\n}\n\nfunc newReverseReader(meta *unix.PerfEventMmapPage, ring []byte) *reverseReader {\n\trr := &reverseReader{\n\t\tmeta: meta,\n\t\tmask: uint64(cap(ring) - 1),\n\t\tring: ring,\n\t}\n\trr.loadHead()\n\treturn rr\n}\n\nfunc (rr *reverseReader) loadHead() {\n\t// The diagram below represents an overwritable perf ring buffer:\n\t//\n\t//    head     read                            tail\n\t//     |        |                               |\n\t//     V        V                               V\n\t// +---+--------+------------+---------+--------+\n\t// |   |H-D....D|H-C........C|H-B.....B|H-A....A|\n\t// +---+--------+------------+---------+--------+\n\t// <--Write from right to left\n\t//                     Read from left to right-->\n\t// (H means header)\n\t//\n\t// The buffer is read left to right beginning from head to tail.\n\t// [head, read) is the read portion of the buffer, [read, tail) the unread one.\n\t// read is adjusted as we progress through the buffer.\n\n\t// Avoid reading sample D multiple times by discarding unread samples C, B, A.\n\trr.tail = rr.head\n\n\t// Get the new head and starting reading from it.\n\trr.head = atomic.LoadUint64(&rr.meta.Data_head)\n\trr.read = rr.head\n\n\tif rr.tail-rr.head > uint64(cap(rr.ring)) {\n\t\t// ring has been fully written, only permit at most cap(rr.ring)\n\t\t// bytes to be read.\n\t\trr.tail = rr.head + uint64(cap(rr.ring))\n\t}\n}\n\nfunc (rr *reverseReader) size() int {\n\treturn len(rr.ring)\n}\n\nfunc (rr *reverseReader) remaining() int {\n\t// remaining data is inaccurate for overwritable buffers\n\t// once an overwrite happens, so return -1 here.\n\treturn -1\n}\n\nfunc (rr *reverseReader) writeTail() {\n\t// We do not care about tail for over writable perf buffer.\n\t// So, this function is noop.\n}\n\nfunc (rr *reverseReader) Read(p []byte) (int, error) {\n\tstart := int(rr.read & rr.mask)\n\n\tn := len(p)\n\t// Truncate if the read wraps in the ring buffer\n\tif remainder := cap(rr.ring) - start; n > remainder {\n\t\tn = remainder\n\t}\n\n\t// Truncate if there isn't enough data\n\tif remainder := int(rr.tail - rr.read); n > remainder {\n\t\tn = remainder\n\t}\n\n\tcopy(p, rr.ring[start:start+n])\n\trr.read += uint64(n)\n\n\tif rr.read == rr.tail {\n\t\treturn n, io.EOF\n\t}\n\n\treturn n, nil\n}\n"
  },
  {
    "path": "perf/ring_test.go",
    "content": "//go:build !windows\n\npackage perf\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\nfunc TestRingBufferReader(t *testing.T) {\n\tring := makeForwardRing(2, 0)\n\tcheckRead(t, ring, []byte{0, 1}, io.EOF)\n\tcheckRead(t, ring, []byte{}, io.EOF)\n\n\t// Wrapping read\n\tring = makeForwardRing(2, 1)\n\tcheckRead(t, ring, []byte{1}, nil)\n\tcheckRead(t, ring, []byte{0}, io.EOF)\n\tcheckRead(t, ring, []byte{}, io.EOF)\n}\n\nfunc TestRingBufferReverseReader(t *testing.T) {\n\t// First case: read 4, starting from offset 2.\n\t// The buffer should contain the following:\n\t//\n\t// [0 1 2 3]\n\t//      ^\n\t//      |\n\t//     head\n\t//\n\t// As we read from position 2, we should get [2, 3].\n\t// Then, when we read it for the second time, we should get [0, 1] as we would\n\t// have looped around the buffer.\n\tring := makeReverseRing(4, 2)\n\tcheckRead(t, ring, []byte{2, 3}, nil)\n\tcheckRead(t, ring, []byte{0, 1}, io.EOF)\n\tcheckRead(t, ring, []byte{}, io.EOF)\n\n\t// Complicated case: read bytes until previous_head.\n\t//\n\t// [0 1 2 3]\n\t//  ^   ^\n\t//  |   |\n\t//  |   +---previous_head\n\t// head\n\tring = makeReverseRing(4, 2)\n\tcheckReadBuffer(t, ring, []byte{2}, nil, make([]byte, 1))\n\t// Next read would be {3}, but we don't consume it.\n\n\t// Pretend the kernel wrote another 2 bytes.\n\tring.meta.Data_head -= 2\n\tring.loadHead()\n\n\t// {3} is discarded.\n\tcheckRead(t, ring, []byte{0, 1}, io.EOF)\n\n\t// Complicated case: read the whole buffer because it was \"overwritten\".\n\t//\n\t// [0 1 2 3]\n\t//      ^\n\t//      |\n\t//      +---previous_head\n\t//      |\n\t//     head\n\t//\n\t// So, we should first read [2, 3] then [0, 1].\n\tring = makeReverseRing(4, 2)\n\tring.meta.Data_head -= ring.meta.Data_size\n\tring.loadHead()\n\n\tcheckRead(t, ring, []byte{2, 3}, nil)\n\tcheckRead(t, ring, []byte{0, 1}, io.EOF)\n}\n\n// ensure that the next call to Read() yields the correct result.\n//\n// Read is called with a buffer that is larger than want so\n// that corner cases around wrapping can be checked. Use\n// checkReadBuffer if that is not desired.\nfunc checkRead(t *testing.T, r io.Reader, want []byte, wantErr error) {\n\tcheckReadBuffer(t, r, want, wantErr, make([]byte, len(want)+1))\n}\n\nfunc checkReadBuffer(t *testing.T, r io.Reader, want []byte, wantErr error, buf []byte) {\n\tt.Helper()\n\n\tn, err := r.Read(buf)\n\tbuf = buf[:n]\n\tqt.Assert(t, qt.Equals(err, wantErr))\n\tqt.Assert(t, qt.DeepEquals(buf, want))\n}\n\nfunc makeBuffer(size int) []byte {\n\tbuf := make([]byte, size)\n\tfor i := range buf {\n\t\tbuf[i] = byte(i)\n\t}\n\treturn buf\n}\n\nfunc makeReverseRing(size, offset int) *reverseReader {\n\tif size != 0 && (size&(size-1)) != 0 {\n\t\tpanic(\"size must be power of two\")\n\t}\n\n\tmeta := unix.PerfEventMmapPage{\n\t\tData_head: 0 - uint64(size) - uint64(offset),\n\t\tData_tail: 0, // never written by the kernel\n\t\tData_size: uint64(size),\n\t}\n\n\treturn newReverseReader(&meta, makeBuffer(size))\n}\n\nfunc makeForwardRing(size, offset int) *forwardReader {\n\tif size != 0 && (size&(size-1)) != 0 {\n\t\tpanic(\"size must be power of two\")\n\t}\n\n\tmeta := unix.PerfEventMmapPage{\n\t\tData_head: uint64(size + offset),\n\t\tData_tail: uint64(offset),\n\t\tData_size: uint64(size),\n\t}\n\n\treturn newForwardReader(&meta, makeBuffer(size))\n}\n\nfunc TestPerfEventRing(t *testing.T) {\n\tcheck := func(buffer, watermark int, overwritable bool) {\n\t\tevent, ring, err := newPerfEventRing(0, buffer, ReaderOptions{Watermark: watermark, Overwritable: overwritable})\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tdefer event.Close()\n\t\tdefer ring.Close()\n\n\t\tsize := ring.size()\n\n\t\t// Ring size should be at least as big as buffer\n\t\tif size < buffer {\n\t\t\tt.Fatalf(\"ring size %d smaller than buffer %d\", size, buffer)\n\t\t}\n\n\t\t// Ring size should be of the form 2^n pages (meta page has already been removed)\n\t\tif size%os.Getpagesize() != 0 {\n\t\t\tt.Fatalf(\"ring size %d not whole number of pages (pageSize %d)\", size, os.Getpagesize())\n\t\t}\n\t\tnPages := size / os.Getpagesize()\n\t\tif nPages&(nPages-1) != 0 {\n\t\t\tt.Fatalf(\"ring size %d (%d pages) not a power of two pages (pageSize %d)\", size, nPages, os.Getpagesize())\n\t\t}\n\t}\n\n\t// watermark > buffer\n\t_, _, err := newPerfEventRing(0, 8192, ReaderOptions{Watermark: 8193, Overwritable: false})\n\tif err == nil {\n\t\tt.Fatal(\"watermark > buffer allowed\")\n\t}\n\t_, _, err = newPerfEventRing(0, 8192, ReaderOptions{Watermark: 8193, Overwritable: true})\n\tif err == nil {\n\t\tt.Fatal(\"watermark > buffer allowed\")\n\t}\n\n\t// watermark == buffer\n\t_, _, err = newPerfEventRing(0, 8192, ReaderOptions{Watermark: 8192, Overwritable: false})\n\tif err == nil {\n\t\tt.Fatal(\"watermark == buffer allowed\")\n\t}\n\t_, _, err = newPerfEventRing(0, 8192, ReaderOptions{Watermark: 8193, Overwritable: true})\n\tif err == nil {\n\t\tt.Fatal(\"watermark == buffer allowed\")\n\t}\n\n\t// buffer not a power of two, watermark < buffer\n\tcheck(8193, 8192, false)\n\tcheck(8193, 8192, true)\n\n\t// large buffer not a multiple of page size at all (prime)\n\tcheck(65537, 8192, false)\n\tcheck(65537, 8192, true)\n}\n"
  },
  {
    "path": "pin/doc.go",
    "content": "// Package pin provides utility functions for working with pinned objects on bpffs.\n\npackage pin\n"
  },
  {
    "path": "pin/load.go",
    "content": "package pin\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/link\"\n)\n\n// Pinner is an interface implemented by all eBPF objects that support pinning\n// to a bpf virtual filesystem.\ntype Pinner interface {\n\tio.Closer\n\tPin(string) error\n}\n\n// Load retrieves a pinned object from a bpf virtual filesystem. It returns one\n// of [ebpf.Map], [ebpf.Program], or [link.Link].\n//\n// Trying to open anything other than a bpf object is an error.\nfunc Load(path string, opts *ebpf.LoadPinOptions) (Pinner, error) {\n\tfd, typ, err := sys.ObjGetTyped(&sys.ObjGetAttr{\n\t\tPathname:  sys.NewStringPointer(path),\n\t\tFileFlags: opts.Marshal(),\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"opening pin %s: %w\", path, err)\n\t}\n\n\tswitch typ {\n\tcase sys.BPF_TYPE_MAP:\n\t\treturn ebpf.NewMapFromFD(fd.Disown())\n\tcase sys.BPF_TYPE_PROG:\n\t\treturn ebpf.NewProgramFromFD(fd.Disown())\n\tcase sys.BPF_TYPE_LINK:\n\t\treturn link.NewFromFD(fd.Disown())\n\t}\n\n\treturn nil, fmt.Errorf(\"unknown object type %d\", typ)\n}\n"
  },
  {
    "path": "pin/load_test.go",
    "content": "package pin\n\nimport (\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/internal/platform\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n\t\"github.com/cilium/ebpf/internal/testutils/testmain\"\n)\n\nfunc mustPinnedProgram(t *testing.T, path string) *ebpf.Program {\n\tt.Helper()\n\n\ttyp := ebpf.SocketFilter\n\tif platform.IsWindows {\n\t\ttyp = ebpf.WindowsSample\n\t}\n\n\tspec := &ebpf.ProgramSpec{\n\t\tName: \"test\",\n\t\tType: typ,\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.LoadImm(asm.R0, 2, asm.DWord),\n\t\t\tasm.Return(),\n\t\t},\n\t\tLicense: \"MIT\",\n\t}\n\n\tp, err := ebpf.NewProgram(spec)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tt.Cleanup(func() { p.Close() })\n\n\tif err := p.Pin(path); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\treturn p\n}\n\nfunc mustPinnedMap(t *testing.T, path string) *ebpf.Map {\n\tt.Helper()\n\n\ttyp := ebpf.Array\n\tif platform.IsWindows {\n\t\ttyp = ebpf.WindowsArray\n\t}\n\n\tspec := &ebpf.MapSpec{\n\t\tName:       \"test\",\n\t\tType:       typ,\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: 1,\n\t}\n\n\tm, err := ebpf.NewMap(spec)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tt.Cleanup(func() { m.Close() })\n\n\tif err := m.Pin(path); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\treturn m\n}\n\nfunc TestLoad(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"4.10\", \"reading program fdinfo\")\n\n\ttmp := testutils.TempBPFFS(t)\n\n\tmpath := filepath.Join(tmp, \"map\")\n\tppath := filepath.Join(tmp, \"prog\")\n\n\tmustPinnedMap(t, mpath)\n\tmustPinnedProgram(t, ppath)\n\n\t_, err := Load(tmp, nil)\n\tqt.Assert(t, qt.IsNotNil(err))\n\n\tm, err := Load(mpath, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer m.Close()\n\tqt.Assert(t, qt.Satisfies(m, testutils.Contains[*ebpf.Map]))\n\n\tp, err := Load(ppath, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\tdefer p.Close()\n\tqt.Assert(t, qt.Satisfies(p, testutils.Contains[*ebpf.Program]))\n}\n\nfunc TestMain(m *testing.M) {\n\ttestmain.Run(m)\n}\n"
  },
  {
    "path": "pin/pin.go",
    "content": "package pin\n\nimport \"io\"\n\n// Pin represents an object and its pinned path.\ntype Pin struct {\n\tPath   string\n\tObject io.Closer\n}\n\nfunc (p *Pin) close() {\n\tif p.Object != nil {\n\t\tp.Object.Close()\n\t}\n}\n\n// Take ownership of Pin.Object.\n//\n// The caller is responsible for calling close on [Pin.Object].\nfunc (p *Pin) Take() {\n\tp.Object = nil\n}\n"
  },
  {
    "path": "pin/walk_other.go",
    "content": "//go:build !windows\n\npackage pin\n\nimport (\n\t\"fmt\"\n\t\"io/fs\"\n\t\"iter\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/linux\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\n// WalkDir walks the file tree rooted at path and yields a [Pin] for each\n// BPF object below the path.\n//\n// Callers must invoke [Pin.Take] if they wish to hold on to the object.\nfunc WalkDir(root string, opts *ebpf.LoadPinOptions) iter.Seq2[*Pin, error] {\n\treturn func(yield func(*Pin, error) bool) {\n\t\tfsType, err := linux.FSType(root)\n\t\tif err != nil {\n\t\t\tyield(nil, err)\n\t\t\treturn\n\t\t}\n\t\tif fsType != unix.BPF_FS_MAGIC {\n\t\t\tyield(nil, fmt.Errorf(\"%s is not on a bpf filesystem\", root))\n\t\t\treturn\n\t\t}\n\n\t\tfn := func(path string, d fs.DirEntry, err error) error {\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tif d.IsDir() {\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tpath = filepath.Join(root, path)\n\t\t\tobj, err := Load(path, opts)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tpin := &Pin{Path: path, Object: obj}\n\t\t\tdefer pin.close()\n\n\t\t\tif !yield(pin, nil) {\n\t\t\t\treturn fs.SkipAll\n\t\t\t}\n\n\t\t\treturn nil\n\t\t}\n\n\t\terr = fs.WalkDir(os.DirFS(root), \".\", fn)\n\t\tif err != nil {\n\t\t\tyield(nil, fmt.Errorf(\"walk: %w\", err))\n\t\t\treturn\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pin/walk_test.go",
    "content": "package pin\n\nimport (\n\t\"iter\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/platform\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc TestWalkDir(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"4.13\", \"reading program objinfo\")\n\n\ttmp := testutils.TempBPFFS(t)\n\n\tdir := filepath.Join(tmp, \"dir\")\n\tif !platform.IsWindows {\n\t\t// Windows doesn't have a BPF file system, so mkdir below fails.\n\t\tqt.Assert(t, qt.IsNil(os.Mkdir(dir, 0755)))\n\t}\n\n\tprogPath := filepath.Join(tmp, \"pinned_prog\")\n\tmustPinnedProgram(t, progPath)\n\tmapPath := filepath.Join(dir, \"pinned_map\")\n\tmustPinnedMap(t, mapPath)\n\n\tnext, stop := iter.Pull2(WalkDir(tmp, nil))\n\tdefer stop()\n\n\tpin, err, ok := next()\n\tqt.Assert(t, qt.IsTrue(ok))\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.Equals(reflect.TypeOf(pin.Object), reflect.TypeFor[*ebpf.Map]()))\n\tqt.Assert(t, qt.Equals(pin.Path, mapPath))\n\n\tpin, err, ok = next()\n\tqt.Assert(t, qt.IsTrue(ok))\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.Equals(reflect.TypeOf(pin.Object), reflect.TypeFor[*ebpf.Program]()))\n\tqt.Assert(t, qt.Equals(pin.Path, progPath))\n\n\t_, _, ok = next()\n\tqt.Assert(t, qt.IsFalse(ok))\n\n\tt.Run(\"Not BPFFS\", func(t *testing.T) {\n\t\tif platform.IsWindows {\n\t\t\tt.Skip(\"Windows does not have BPFFS\")\n\t\t}\n\n\t\tnext, stop := iter.Pull2(WalkDir(\"/\", nil))\n\t\tdefer stop()\n\n\t\t_, err, ok = next()\n\t\tqt.Assert(t, qt.IsTrue(ok))\n\t\tqt.Assert(t, qt.IsNotNil(err))\n\n\t\t_, _, ok = next()\n\t\tqt.Assert(t, qt.IsFalse(ok))\n\t})\n\n}\n"
  },
  {
    "path": "pin/walk_windows.go",
    "content": "package pin\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"iter\"\n\t\"strings\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/efw\"\n)\n\n// WalkDir walks the file tree rooted at path and yields a [Pin] for each\n// BPF object below the path.\n//\n// Callers must invoke [Pin.Take] if they wish to hold on to the object.\nfunc WalkDir(root string, opts *ebpf.LoadPinOptions) iter.Seq2[*Pin, error] {\n\treturn func(yield func(*Pin, error) bool) {\n\t\troot, err := efw.EbpfCanonicalizePinPath(root)\n\t\tif err != nil {\n\t\t\tyield(nil, fmt.Errorf(\"failed to canonicalize pin path %q: %w\", root, err))\n\t\t\treturn\n\t\t}\n\n\t\tcursor := root\n\t\tfor {\n\t\t\tnext, _, err := efw.EbpfGetNextPinnedObjectPath(cursor, efw.EBPF_OBJECT_UNKNOWN)\n\t\t\tif errors.Is(err, efw.EBPF_NO_MORE_KEYS) {\n\t\t\t\tbreak\n\t\t\t} else if err != nil {\n\t\t\t\tyield(nil, err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif !strings.HasPrefix(next, root) {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tobj, err := Load(next, opts)\n\t\t\tif err != nil {\n\t\t\t\tyield(nil, err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tpin := &Pin{next, obj}\n\t\t\tok := yield(pin, nil)\n\t\t\tpin.close()\n\t\t\tif !ok {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tcursor = next\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "prog.go",
    "content": "package ebpf\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"slices\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/linux\"\n\t\"github.com/cilium/ebpf/internal/platform\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/sysenc\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\n// ErrNotSupported is returned whenever the kernel doesn't support a feature.\nvar ErrNotSupported = internal.ErrNotSupported\n\n// ErrProgIncompatible is returned when a loaded Program is incompatible with a\n// given spec.\nvar ErrProgIncompatible = errors.New(\"program is incompatible\")\n\n// errBadRelocation is returned when the verifier rejects a program due to a\n// bad CO-RE relocation.\n//\n// This error is detected based on heuristics and therefore may not be reliable.\nvar errBadRelocation = errors.New(\"bad CO-RE relocation\")\n\n// errUnknownKfunc is returned when the verifier rejects a program due to an\n// unknown kfunc.\n//\n// This error is detected based on heuristics and therefore may not be reliable.\nvar errUnknownKfunc = errors.New(\"unknown kfunc\")\n\n// ProgramID represents the unique ID of an eBPF program.\ntype ProgramID = sys.ProgramID\n\nconst (\n\t// Number of bytes to pad the output buffer for BPF_PROG_TEST_RUN.\n\t// This is currently the maximum of spare space allocated for SKB\n\t// and XDP programs, and equal to XDP_PACKET_HEADROOM + NET_IP_ALIGN.\n\toutputPad = 256 + 2\n)\n\n// minVerifierLogSize is the default number of bytes allocated for the\n// verifier log.\nconst minVerifierLogSize = 64 * 1024\n\n// maxVerifierLogSize is the maximum size of verifier log buffer the kernel\n// will accept before returning EINVAL. May be increased to MaxUint32 in the\n// future, but avoid the unnecessary EINVAL for now.\nconst maxVerifierLogSize = math.MaxUint32 >> 2\n\n// maxVerifierAttempts is the maximum number of times the verifier will retry\n// loading a program with a growing log buffer before giving up. Since we double\n// the log size on every attempt, this is the absolute maximum number of\n// attempts before the buffer reaches [maxVerifierLogSize].\nconst maxVerifierAttempts = 30\n\n// ProgramOptions control loading a program into the kernel.\ntype ProgramOptions struct {\n\t// Bitmap controlling the detail emitted by the kernel's eBPF verifier log.\n\t// LogLevel-type values can be ORed together to request specific kinds of\n\t// verifier output. See the documentation on [ebpf.LogLevel] for details.\n\t//\n\t//  opts.LogLevel = (ebpf.LogLevelBranch | ebpf.LogLevelStats)\n\t//\n\t// If left to its default value, the program will first be loaded without\n\t// verifier output enabled. Upon error, the program load will be repeated\n\t// with LogLevelBranch and the given (or default) LogSize value.\n\t//\n\t// Unless LogDisabled is set, setting this to a non-zero value will enable the verifier\n\t// log, populating the [ebpf.Program.VerifierLog] field on successful loads\n\t// and including detailed verifier errors if the program is rejected. This\n\t// will always allocate an output buffer, but will result in only a single\n\t// attempt at loading the program.\n\tLogLevel LogLevel\n\n\t// Starting size of the verifier log buffer. If the verifier log is larger\n\t// than this size, the buffer will be grown to fit the entire log. Leave at\n\t// its default value unless troubleshooting.\n\tLogSizeStart uint32\n\n\t// Disables the verifier log completely, regardless of other options.\n\tLogDisabled bool\n\n\t// Type information used for CO-RE relocations.\n\t//\n\t// This is useful in environments where the kernel BTF is not available\n\t// (containers) or where it is in a non-standard location. Defaults to\n\t// use the kernel BTF from a well-known location if nil.\n\tKernelTypes *btf.Spec\n\n\t// Additional targets to consider for CO-RE relocations. This can be used to\n\t// pass BTF information for kernel modules when it's not present on\n\t// KernelTypes.\n\tExtraRelocationTargets []*btf.Spec\n}\n\n// ProgramSpec defines a Program.\ntype ProgramSpec struct {\n\t// Name is passed to the kernel as a debug aid.\n\t//\n\t// Unsupported characters will be stripped.\n\tName string\n\n\t// Type determines at which hook in the kernel a program will run.\n\tType ProgramType\n\n\t// Network interface index the user intends to attach this program to after\n\t// loading. Only valid for some program types.\n\t//\n\t// Provides driver-specific context about the target interface to the\n\t// verifier, required when using certain BPF helpers.\n\tIfindex uint32\n\n\t// AttachType of the program, needed to differentiate allowed context\n\t// accesses in some newer program types like CGroupSockAddr.\n\t//\n\t// Available on kernels 4.17 and later.\n\tAttachType AttachType\n\n\t// Name of a kernel data structure or function to attach to. Its\n\t// interpretation depends on Type and AttachType.\n\tAttachTo string\n\n\t// The program to attach to. Must be provided manually.\n\tAttachTarget *Program\n\n\t// The name of the ELF section this program originated from.\n\tSectionName string\n\n\tInstructions asm.Instructions\n\n\t// Flags is passed to the kernel and specifies additional program\n\t// load attributes.\n\tFlags uint32\n\n\t// License of the program. Some helpers are only available if\n\t// the license is deemed compatible with the GPL.\n\t//\n\t// See https://www.kernel.org/doc/html/latest/process/license-rules.html#id1\n\tLicense string\n\n\t// Version used by Kprobe programs.\n\t//\n\t// Deprecated on kernels 5.0 and later. Leave empty to let the library\n\t// detect this value automatically.\n\tKernelVersion uint32\n\n\t// The byte order this program was compiled for, may be nil.\n\tByteOrder binary.ByteOrder\n}\n\n// Copy returns a copy of the spec.\nfunc (ps *ProgramSpec) Copy() *ProgramSpec {\n\tif ps == nil {\n\t\treturn nil\n\t}\n\n\tcpy := *ps\n\tcpy.Instructions = make(asm.Instructions, len(ps.Instructions))\n\tcopy(cpy.Instructions, ps.Instructions)\n\treturn &cpy\n}\n\n// Tag calculates the kernel tag for a series of instructions.\n//\n// Use asm.Instructions.Tag if you need to calculate for non-native endianness.\n//\n// Deprecated: The value produced by this method no longer matches tags produced\n// by the kernel since Linux 6.18. Use [ProgramSpec.Compatible] instead.\nfunc (ps *ProgramSpec) Tag() (string, error) {\n\treturn ps.Instructions.Tag(internal.NativeEndian)\n}\n\n// Compatible returns nil if a loaded Program's kernel tag matches the one of\n// the ProgramSpec.\n//\n// Returns [ErrProgIncompatible] if the tags do not match.\nfunc (ps *ProgramSpec) Compatible(info *ProgramInfo) error {\n\tif platform.IsWindows {\n\t\treturn fmt.Errorf(\"%w: Windows does not support tag readback from kernel\", internal.ErrNotSupportedOnOS)\n\t}\n\n\tok, err := ps.Instructions.HasTag(info.Tag, internal.NativeEndian)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif !ok {\n\t\treturn fmt.Errorf(\"%w: ProgramSpec and Program tags do not match\", ErrProgIncompatible)\n\t}\n\n\treturn nil\n}\n\n// targetsKernelModule returns true if the program supports being attached to a\n// symbol provided by a kernel module.\nfunc (ps *ProgramSpec) targetsKernelModule() bool {\n\tif ps.AttachTo == \"\" {\n\t\treturn false\n\t}\n\n\tswitch ps.Type {\n\tcase Tracing:\n\t\tswitch ps.AttachType {\n\t\tcase AttachTraceFEntry, AttachTraceFExit:\n\t\t\treturn true\n\t\t}\n\tcase Kprobe:\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// VerifierError is returned by [NewProgram] and [NewProgramWithOptions] if a\n// program is rejected by the verifier.\n//\n// Use [errors.As] to access the error.\ntype VerifierError = internal.VerifierError\n\n// Program represents BPF program loaded into the kernel.\n//\n// It is not safe to close a Program which is used by other goroutines.\ntype Program struct {\n\t// Contains the output of the kernel verifier if enabled,\n\t// otherwise it is empty.\n\tVerifierLog string\n\n\tfd         *sys.FD\n\tname       string\n\tpinnedPath string\n\ttyp        ProgramType\n}\n\n// NewProgram creates a new Program.\n//\n// See [NewProgramWithOptions] for details.\n//\n// Returns a [VerifierError] containing the full verifier log if the program is\n// rejected by the kernel.\nfunc NewProgram(spec *ProgramSpec) (*Program, error) {\n\treturn NewProgramWithOptions(spec, ProgramOptions{})\n}\n\n// NewProgramWithOptions creates a new Program.\n//\n// Loading a program for the first time will perform\n// feature detection by loading small, temporary programs.\n//\n// Returns a [VerifierError] containing the full verifier log if the program is\n// rejected by the kernel.\nfunc NewProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, error) {\n\tif spec == nil {\n\t\treturn nil, errors.New(\"can't load a program from a nil spec\")\n\t}\n\n\tprog, err := newProgramWithOptions(spec, opts, btf.NewCache())\n\tif errors.Is(err, asm.ErrUnsatisfiedMapReference) {\n\t\treturn nil, fmt.Errorf(\"cannot load program without loading its whole collection: %w\", err)\n\t}\n\treturn prog, err\n}\n\nvar (\n\tcoreBadLoad = []byte(fmt.Sprintf(\"(18) r10 = 0x%x\\n\", btf.COREBadRelocationSentinel))\n\t// This log message was introduced by ebb676daa1a3 (\"bpf: Print function name in\n\t// addition to function id\") which first appeared in v4.10 and has remained\n\t// unchanged since.\n\tcoreBadCall  = []byte(fmt.Sprintf(\"invalid func unknown#%d\\n\", btf.COREBadRelocationSentinel))\n\tkfuncBadCall = []byte(fmt.Sprintf(\"invalid func unknown#%d\\n\", kfuncCallPoisonBase))\n)\n\nfunc newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, c *btf.Cache) (*Program, error) {\n\tif len(spec.Instructions) == 0 {\n\t\treturn nil, errors.New(\"instructions cannot be empty\")\n\t}\n\n\tif spec.Type == UnspecifiedProgram {\n\t\treturn nil, errors.New(\"can't load program of unspecified type\")\n\t}\n\n\tif spec.ByteOrder != nil && spec.ByteOrder != internal.NativeEndian {\n\t\treturn nil, fmt.Errorf(\"can't load %s program on %s\", spec.ByteOrder, internal.NativeEndian)\n\t}\n\n\t// Kernels before 5.0 (6c4fc209fcf9 \"bpf: remove useless version check for prog load\")\n\t// require the version field to be set to the value of the KERNEL_VERSION\n\t// macro for kprobe-type programs.\n\t// Overwrite Kprobe program version if set to zero or the magic version constant.\n\tkv := spec.KernelVersion\n\tif spec.Type == Kprobe && (kv == 0 || kv == internal.MagicKernelVersion) {\n\t\tv, err := linux.KernelVersion()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"detecting kernel version: %w\", err)\n\t\t}\n\t\tkv = v.Kernel()\n\t}\n\n\tp, progType := platform.DecodeConstant(spec.Type)\n\tif p != platform.Native {\n\t\treturn nil, fmt.Errorf(\"program type %s (%s): %w\", spec.Type, p, internal.ErrNotSupportedOnOS)\n\t}\n\n\tattr := &sys.ProgLoadAttr{\n\t\tProgName:           maybeFillObjName(spec.Name),\n\t\tProgType:           sys.ProgType(progType),\n\t\tProgFlags:          spec.Flags,\n\t\tProgIfindex:        spec.Ifindex,\n\t\tExpectedAttachType: sys.AttachType(spec.AttachType),\n\t\tLicense:            sys.NewStringPointer(spec.License),\n\t\tKernVersion:        kv,\n\t}\n\n\tinsns := make(asm.Instructions, len(spec.Instructions))\n\tcopy(insns, spec.Instructions)\n\n\tvar b btf.Builder\n\tif err := applyRelocations(insns, spec.ByteOrder, &b, c, opts.KernelTypes, opts.ExtraRelocationTargets); err != nil {\n\t\treturn nil, fmt.Errorf(\"apply CO-RE relocations: %w\", err)\n\t}\n\n\terrExtInfos := haveProgramExtInfos()\n\tif !b.Empty() && errors.Is(errExtInfos, ErrNotSupported) {\n\t\t// There is at least one CO-RE relocation which relies on a stable local\n\t\t// type ID.\n\t\t// Return ErrNotSupported instead of E2BIG if there is no BTF support.\n\t\treturn nil, errExtInfos\n\t}\n\n\tif errExtInfos == nil {\n\t\t// Only add func and line info if the kernel supports it. This allows\n\t\t// BPF compiled with modern toolchains to work on old kernels.\n\t\tfib, lib, err := btf.MarshalExtInfos(insns, &b)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"marshal ext_infos: %w\", err)\n\t\t}\n\n\t\tattr.FuncInfoRecSize = btf.FuncInfoSize\n\t\tattr.FuncInfoCnt = uint32(len(fib)) / btf.FuncInfoSize\n\t\tattr.FuncInfo = sys.SlicePointer(fib)\n\n\t\tattr.LineInfoRecSize = btf.LineInfoSize\n\t\tattr.LineInfoCnt = uint32(len(lib)) / btf.LineInfoSize\n\t\tattr.LineInfo = sys.SlicePointer(lib)\n\t}\n\n\tif !b.Empty() {\n\t\thandle, err := btf.NewHandle(&b)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"load BTF: %w\", err)\n\t\t}\n\t\tdefer handle.Close()\n\n\t\tattr.ProgBtfFd = uint32(handle.FD())\n\t}\n\n\tkconfig, err := resolveKconfigReferences(insns)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"resolve .kconfig: %w\", err)\n\t}\n\tdefer kconfig.Close()\n\n\tif err := resolveKsymReferences(insns); err != nil {\n\t\treturn nil, fmt.Errorf(\"resolve .ksyms: %w\", err)\n\t}\n\n\tif err := fixupAndValidate(insns); err != nil {\n\t\treturn nil, err\n\t}\n\n\thandles, err := fixupKfuncs(insns, c)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"fixing up kfuncs: %w\", err)\n\t}\n\tdefer handles.Close()\n\n\tif len(handles) > 0 {\n\t\tfdArray := handles.fdArray()\n\t\tattr.FdArray = sys.SlicePointer(fdArray)\n\t}\n\n\tbuf := bytes.NewBuffer(make([]byte, 0, insns.Size()))\n\terr = insns.Marshal(buf, internal.NativeEndian)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tbytecode := buf.Bytes()\n\tattr.Insns = sys.SlicePointer(bytecode)\n\tattr.InsnCnt = uint32(len(bytecode) / asm.InstructionSize)\n\n\tif spec.AttachTarget != nil {\n\t\ttargetID, err := findTargetInProgram(spec.AttachTarget, spec.AttachTo, spec.Type, spec.AttachType)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"attach %s/%s: %w\", spec.Type, spec.AttachType, err)\n\t\t}\n\n\t\tattr.AttachBtfId = targetID\n\t\tattr.AttachBtfObjFd = uint32(spec.AttachTarget.FD())\n\t\tdefer runtime.KeepAlive(spec.AttachTarget)\n\t} else if spec.AttachTo != \"\" {\n\t\tvar targetMember string\n\t\tattachTo := spec.AttachTo\n\n\t\tif spec.Type == StructOps {\n\t\t\tattachTo, targetMember, _ = strings.Cut(attachTo, \":\")\n\t\t\tif targetMember == \"\" {\n\t\t\t\treturn nil, fmt.Errorf(\"struct_ops: AttachTo must be '<ops>:<member>' (got %s)\", spec.AttachTo)\n\t\t\t}\n\t\t}\n\n\t\tmodule, targetID, err := findProgramTargetInKernel(attachTo, spec.Type, spec.AttachType, c)\n\t\tif err != nil && !errors.Is(err, errUnrecognizedAttachType) {\n\t\t\t// We ignore errUnrecognizedAttachType since AttachTo may be non-empty\n\t\t\t// for programs that don't attach anywhere.\n\t\t\treturn nil, fmt.Errorf(\"attach %s/%s: %w\", spec.Type, spec.AttachType, err)\n\t\t}\n\n\t\tif spec.Type == StructOps {\n\t\t\tvar s *btf.Spec\n\n\t\t\ttarget := btf.Type((*btf.Struct)(nil))\n\t\t\ts, module, err = findTargetInKernel(attachTo, &target, c)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"lookup struct_ops kern type %q: %w\", attachTo, err)\n\t\t\t}\n\t\t\tkType := target.(*btf.Struct)\n\n\t\t\ttargetID, err = s.TypeID(kType)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"type id for %s: %w\", kType.TypeName(), err)\n\t\t\t}\n\n\t\t\tidx := slices.IndexFunc(kType.Members, func(m btf.Member) bool {\n\t\t\t\treturn m.Name == targetMember\n\t\t\t})\n\t\t\tif idx < 0 {\n\t\t\t\treturn nil, fmt.Errorf(\"member %q not found in %s\", targetMember, kType.Name)\n\t\t\t}\n\n\t\t\t// ExpectedAttachType: index of the target member in the struct\n\t\t\tattr.ExpectedAttachType = sys.AttachType(idx)\n\t\t}\n\n\t\tattr.AttachBtfId = targetID\n\t\tif module != nil && attr.AttachBtfObjFd == 0 {\n\t\t\tattr.AttachBtfObjFd = uint32(module.FD())\n\t\t\tdefer module.Close()\n\t\t}\n\t}\n\n\tif platform.IsWindows && opts.LogLevel != 0 {\n\t\treturn nil, fmt.Errorf(\"log level: %w\", internal.ErrNotSupportedOnOS)\n\t}\n\n\tvar logBuf []byte\n\tvar fd *sys.FD\n\tif opts.LogDisabled {\n\t\t// Loading with logging disabled should never retry.\n\t\tfd, err = sys.ProgLoad(attr)\n\t\tif err == nil {\n\t\t\treturn &Program{\"\", fd, spec.Name, \"\", spec.Type}, nil\n\t\t}\n\t} else {\n\t\t// Only specify log size if log level is also specified. Setting size\n\t\t// without level results in EINVAL. Level will be bumped to LogLevelBranch\n\t\t// if the first load fails.\n\t\tif opts.LogLevel != 0 {\n\t\t\tattr.LogLevel = opts.LogLevel\n\t\t\tattr.LogSize = internal.Between(opts.LogSizeStart, minVerifierLogSize, maxVerifierLogSize)\n\t\t}\n\n\t\tattempts := 1\n\t\tfor {\n\t\t\tif attr.LogLevel != 0 {\n\t\t\t\tlogBuf = make([]byte, attr.LogSize)\n\t\t\t\tattr.LogBuf = sys.SlicePointer(logBuf)\n\t\t\t}\n\n\t\t\tfd, err = sys.ProgLoad(attr)\n\t\t\tif err == nil {\n\t\t\t\treturn &Program{unix.ByteSliceToString(logBuf), fd, spec.Name, \"\", spec.Type}, nil\n\t\t\t}\n\n\t\t\tif !retryLogAttrs(attr, opts.LogSizeStart, err) {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tif attempts >= maxVerifierAttempts {\n\t\t\t\treturn nil, fmt.Errorf(\"load program: %w (bug: hit %d verifier attempts)\", err, maxVerifierAttempts)\n\t\t\t}\n\t\t\tattempts++\n\t\t}\n\t}\n\n\tend := bytes.IndexByte(logBuf, 0)\n\tif end < 0 {\n\t\tend = len(logBuf)\n\t}\n\n\ttail := logBuf[max(end-256, 0):end]\n\tswitch {\n\tcase errors.Is(err, unix.EPERM):\n\t\tif len(logBuf) > 0 && logBuf[0] == 0 {\n\t\t\t// EPERM due to RLIMIT_MEMLOCK happens before the verifier, so we can\n\t\t\t// check that the log is empty to reduce false positives.\n\t\t\treturn nil, fmt.Errorf(\"load program: %w (MEMLOCK may be too low, consider rlimit.RemoveMemlock)\", err)\n\t\t}\n\n\tcase errors.Is(err, unix.EFAULT):\n\t\t// EFAULT is returned when the kernel hits a verifier bug, and always\n\t\t// overrides ENOSPC, defeating the buffer growth strategy. Warn the user\n\t\t// that they may need to increase the buffer size manually.\n\t\treturn nil, fmt.Errorf(\"load program: %w (hit verifier bug, increase LogSizeStart to fit the log and check dmesg)\", err)\n\n\tcase errors.Is(err, unix.EINVAL):\n\t\tif bytes.Contains(tail, coreBadCall) {\n\t\t\terr = errBadRelocation\n\t\t\tbreak\n\t\t} else if bytes.Contains(tail, kfuncBadCall) {\n\t\t\terr = errUnknownKfunc\n\t\t\tbreak\n\t\t}\n\n\tcase errors.Is(err, unix.EACCES):\n\t\tif bytes.Contains(tail, coreBadLoad) {\n\t\t\terr = errBadRelocation\n\t\t\tbreak\n\t\t}\n\t}\n\n\t// hasFunctionReferences may be expensive, so check it last.\n\tif (errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM)) &&\n\t\thasFunctionReferences(spec.Instructions) {\n\t\tif err := haveBPFToBPFCalls(); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"load program: %w\", err)\n\t\t}\n\t}\n\n\treturn nil, internal.ErrorWithLog(\"load program\", err, logBuf)\n}\n\nfunc retryLogAttrs(attr *sys.ProgLoadAttr, startSize uint32, err error) bool {\n\tif attr.LogSize == maxVerifierLogSize {\n\t\t// Maximum buffer size reached, don't grow or retry.\n\t\treturn false\n\t}\n\n\t// ENOSPC means the log was enabled on the previous iteration, so we only\n\t// need to grow the buffer.\n\tif errors.Is(err, unix.ENOSPC) {\n\t\tif attr.LogTrueSize != 0 {\n\t\t\t// Kernel supports LogTrueSize and previous iteration undershot the buffer\n\t\t\t// size. Try again with the given true size.\n\t\t\tattr.LogSize = attr.LogTrueSize\n\t\t\treturn true\n\t\t}\n\n\t\t// Ensure the size doesn't overflow.\n\t\tconst factor = 2\n\t\tif attr.LogSize >= maxVerifierLogSize/factor {\n\t\t\tattr.LogSize = maxVerifierLogSize\n\t\t\treturn true\n\t\t}\n\n\t\t// Make an educated guess how large the buffer should be by multiplying. Due\n\t\t// to int division, this rounds down odd sizes.\n\t\tattr.LogSize = internal.Between(attr.LogSize, minVerifierLogSize, maxVerifierLogSize/factor)\n\t\tattr.LogSize *= factor\n\n\t\treturn true\n\t}\n\n\tif attr.LogLevel == 0 {\n\t\t// Loading the program failed, it wasn't a buffer-related error, and the log\n\t\t// was disabled the previous iteration. Enable basic logging and retry.\n\t\tattr.LogLevel = LogLevelBranch\n\t\tattr.LogSize = internal.Between(startSize, minVerifierLogSize, maxVerifierLogSize)\n\t\treturn true\n\t}\n\n\t// Loading the program failed for a reason other than buffer size and the log\n\t// was already enabled the previous iteration. Don't retry.\n\treturn false\n}\n\n// NewProgramFromFD creates a [Program] around a raw fd.\n//\n// You should not use fd after calling this function.\n//\n// Requires at least Linux 4.13. Returns an error on Windows.\nfunc NewProgramFromFD(fd int) (*Program, error) {\n\tf, err := sys.NewFD(fd)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn newProgramFromFD(f)\n}\n\n// NewProgramFromID returns the [Program] for a given program id. Returns\n// [ErrNotExist] if there is no eBPF program with the given id.\n//\n// Requires at least Linux 4.13.\nfunc NewProgramFromID(id ProgramID) (*Program, error) {\n\tfd, err := sys.ProgGetFdById(&sys.ProgGetFdByIdAttr{\n\t\tId: uint32(id),\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"get program by id: %w\", err)\n\t}\n\n\treturn newProgramFromFD(fd)\n}\n\nfunc newProgramFromFD(fd *sys.FD) (*Program, error) {\n\tinfo, err := minimalProgramInfoFromFd(fd)\n\tif err != nil {\n\t\tfd.Close()\n\t\treturn nil, fmt.Errorf(\"discover program type: %w\", err)\n\t}\n\n\treturn &Program{\"\", fd, info.Name, \"\", info.Type}, nil\n}\n\nfunc (p *Program) String() string {\n\tif p.name != \"\" {\n\t\treturn fmt.Sprintf(\"%s(%s)#%v\", p.typ, p.name, p.fd)\n\t}\n\treturn fmt.Sprintf(\"%s(%v)\", p.typ, p.fd)\n}\n\n// Type returns the underlying type of the program.\nfunc (p *Program) Type() ProgramType {\n\treturn p.typ\n}\n\n// Info returns metadata about the program.\n//\n// Requires at least 4.10.\nfunc (p *Program) Info() (*ProgramInfo, error) {\n\treturn newProgramInfoFromFd(p.fd)\n}\n\n// Stats returns runtime statistics about the Program. Requires BPF statistics\n// collection to be enabled, see [EnableStats].\n//\n// Requires at least Linux 5.8.\nfunc (p *Program) Stats() (*ProgramStats, error) {\n\treturn newProgramStatsFromFd(p.fd)\n}\n\n// Handle returns a reference to the program's type information in the kernel.\n//\n// Returns ErrNotSupported if the kernel has no BTF support, or if there is no\n// BTF associated with the program.\nfunc (p *Program) Handle() (*btf.Handle, error) {\n\tinfo, err := p.Info()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tid, ok := info.BTFID()\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"program %s: retrieve BTF ID: %w\", p, ErrNotSupported)\n\t}\n\n\treturn btf.NewHandleFromID(id)\n}\n\n// FD gets the file descriptor of the Program.\n//\n// It is invalid to call this function after Close has been called.\nfunc (p *Program) FD() int {\n\treturn p.fd.Int()\n}\n\n// Clone creates a duplicate of the Program.\n//\n// Closing the duplicate does not affect the original, and vice versa.\n//\n// Cloning a nil Program returns nil.\nfunc (p *Program) Clone() (*Program, error) {\n\tif p == nil {\n\t\treturn nil, nil\n\t}\n\n\tdup, err := p.fd.Dup()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't clone program: %w\", err)\n\t}\n\n\treturn &Program{p.VerifierLog, dup, p.name, \"\", p.typ}, nil\n}\n\n// Pin persists the Program on the BPF virtual file system past the lifetime of\n// the process that created it\n//\n// Calling Pin on a previously pinned program will overwrite the path, except when\n// the new path already exists. Re-pinning across filesystems is not supported.\n//\n// This requires bpffs to be mounted above fileName.\n// See https://docs.cilium.io/en/stable/network/kubernetes/configuration/#mounting-bpffs-with-systemd\nfunc (p *Program) Pin(fileName string) error {\n\tif err := sys.Pin(p.pinnedPath, fileName, p.fd); err != nil {\n\t\treturn err\n\t}\n\tp.pinnedPath = fileName\n\treturn nil\n}\n\n// Unpin removes the persisted state for the Program from the BPF virtual filesystem.\n//\n// Failed calls to Unpin will not alter the state returned by IsPinned.\n//\n// Unpinning an unpinned Program returns nil.\nfunc (p *Program) Unpin() error {\n\tif err := sys.Unpin(p.pinnedPath); err != nil {\n\t\treturn err\n\t}\n\tp.pinnedPath = \"\"\n\treturn nil\n}\n\n// IsPinned returns true if the Program has a non-empty pinned path.\nfunc (p *Program) IsPinned() bool {\n\treturn p.pinnedPath != \"\"\n}\n\n// Close the Program's underlying file descriptor, which could unload\n// the program from the kernel if it is not pinned or attached to a\n// kernel hook.\nfunc (p *Program) Close() error {\n\tif p == nil {\n\t\treturn nil\n\t}\n\n\treturn p.fd.Close()\n}\n\n// Various options for Run'ing a Program\ntype RunOptions struct {\n\t// Program's data input. Required field.\n\t//\n\t// The kernel expects at least 14 bytes input for an ethernet header for\n\t// XDP and SKB programs.\n\tData []byte\n\t// Program's data after Program has run. Caller must allocate. Optional field.\n\tDataOut []byte\n\t// Program's context input. Optional field.\n\tContext interface{}\n\t// Program's context after Program has run. Must be a pointer or slice. Optional field.\n\tContextOut interface{}\n\t// Minimum number of times to run Program. Optional field. Defaults to 1.\n\t//\n\t// The program may be executed more often than this due to interruptions, e.g.\n\t// when runtime.AllThreadsSyscall is invoked.\n\tRepeat uint32\n\t// Optional flags.\n\tFlags uint32\n\t// CPU to run Program on. Optional field.\n\t// Note not all program types support this field.\n\tCPU uint32\n\t// BatchSize (default 64) affects the kernel's packet buffer allocation behaviour when running\n\t// programs with BPF_F_TEST_XDP_LIVE_FRAMES and a non-zero [RunOptions.Repeat] value.\n\t// For more details, see the kernel documentation on BPF_PROG_RUN:\n\t// https://docs.kernel.org/bpf/bpf_prog_run.html#running-xdp-programs-in-live-frame-mode\n\tBatchSize uint32\n\t// Called whenever the syscall is interrupted, and should be set to testing.B.ResetTimer\n\t// or similar. Typically used during benchmarking. Optional field.\n\t//\n\t// Deprecated: use [testing.B.ReportMetric] with unit \"ns/op\" instead.\n\tReset func()\n}\n\n// Test runs the Program in the kernel with the given input and returns the\n// value returned by the eBPF program.\n//\n// Note: the kernel expects at least 14 bytes input for an ethernet header for\n// XDP and SKB programs.\n//\n// This function requires at least Linux 4.12.\nfunc (p *Program) Test(in []byte) (uint32, []byte, error) {\n\t// Older kernels ignore the dataSizeOut argument when copying to user space.\n\t// Combined with things like bpf_xdp_adjust_head() we don't really know what the final\n\t// size will be. Hence we allocate an output buffer which we hope will always be large\n\t// enough, and panic if the kernel wrote past the end of the allocation.\n\t// See https://patchwork.ozlabs.org/cover/1006822/\n\tvar out []byte\n\tif len(in) > 0 {\n\t\tout = make([]byte, len(in)+outputPad)\n\t}\n\n\topts := RunOptions{\n\t\tData:    in,\n\t\tDataOut: out,\n\t\tRepeat:  1,\n\t}\n\n\tret, _, err := p.run(&opts)\n\tif err != nil {\n\t\treturn ret, nil, fmt.Errorf(\"test program: %w\", err)\n\t}\n\treturn ret, opts.DataOut, nil\n}\n\n// Run runs the Program in kernel with given RunOptions.\n//\n// Note: the same restrictions from Test apply.\nfunc (p *Program) Run(opts *RunOptions) (uint32, error) {\n\tif opts == nil {\n\t\topts = &RunOptions{}\n\t}\n\n\tret, _, err := p.run(opts)\n\tif err != nil {\n\t\treturn ret, fmt.Errorf(\"run program: %w\", err)\n\t}\n\treturn ret, nil\n}\n\n// Benchmark runs the Program with the given input for a number of times\n// and returns the time taken per iteration.\n//\n// Returns the result of the last execution of the program and the time per\n// run or an error. reset is called whenever the benchmark syscall is\n// interrupted, and should be set to testing.B.ResetTimer or similar.\n//\n// This function requires at least Linux 4.12.\nfunc (p *Program) Benchmark(in []byte, repeat int, reset func()) (uint32, time.Duration, error) {\n\tif uint(repeat) > math.MaxUint32 {\n\t\treturn 0, 0, fmt.Errorf(\"repeat is too high\")\n\t}\n\n\topts := RunOptions{\n\t\tData:   in,\n\t\tRepeat: uint32(repeat),\n\t\tReset:  reset,\n\t}\n\n\tret, total, err := p.run(&opts)\n\tif err != nil {\n\t\treturn ret, total, fmt.Errorf(\"benchmark program: %w\", err)\n\t}\n\treturn ret, total, nil\n}\n\nvar haveProgRun = internal.NewFeatureTest(\"BPF_PROG_RUN\", func() error {\n\tif platform.IsWindows {\n\t\treturn nil\n\t}\n\n\tprog, err := NewProgram(&ProgramSpec{\n\t\t// SocketFilter does not require privileges on newer kernels.\n\t\tType: SocketFilter,\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.LoadImm(asm.R0, 0, asm.DWord),\n\t\t\tasm.Return(),\n\t\t},\n\t\tLicense: \"MIT\",\n\t})\n\tif err != nil {\n\t\t// This may be because we lack sufficient permissions, etc.\n\t\treturn err\n\t}\n\tdefer prog.Close()\n\n\tin := internal.EmptyBPFContext\n\tattr := sys.ProgRunAttr{\n\t\tProgFd:     uint32(prog.FD()),\n\t\tDataSizeIn: uint32(len(in)),\n\t\tDataIn:     sys.SlicePointer(in),\n\t}\n\n\terr = sys.ProgRun(&attr)\n\tswitch {\n\tcase errors.Is(err, unix.EINVAL):\n\t\t// Check for EINVAL specifically, rather than err != nil since we\n\t\t// otherwise misdetect due to insufficient permissions.\n\t\treturn internal.ErrNotSupported\n\n\tcase errors.Is(err, unix.EINTR):\n\t\t// We know that PROG_TEST_RUN is supported if we get EINTR.\n\t\treturn nil\n\n\tcase errors.Is(err, sys.ENOTSUPP):\n\t\t// The first PROG_TEST_RUN patches shipped in 4.12 didn't include\n\t\t// a test runner for SocketFilter. ENOTSUPP means PROG_TEST_RUN is\n\t\t// supported, but not for the program type used in the probe.\n\t\treturn nil\n\t}\n\n\treturn err\n}, \"4.12\", \"windows:0.20\")\n\nfunc (p *Program) run(opts *RunOptions) (uint32, time.Duration, error) {\n\tif uint(len(opts.Data)) > math.MaxUint32 {\n\t\treturn 0, 0, fmt.Errorf(\"input is too long\")\n\t}\n\n\tif err := haveProgRun(); err != nil {\n\t\treturn 0, 0, err\n\t}\n\n\tvar ctxIn []byte\n\tif opts.Context != nil {\n\t\tvar err error\n\t\tctxIn, err = binary.Append(nil, internal.NativeEndian, opts.Context)\n\t\tif err != nil {\n\t\t\treturn 0, 0, fmt.Errorf(\"cannot serialize context: %v\", err)\n\t\t}\n\t}\n\n\tvar ctxOut []byte\n\tif opts.ContextOut != nil {\n\t\tctxOut = make([]byte, binary.Size(opts.ContextOut))\n\t} else if platform.IsWindows && len(ctxIn) > 0 {\n\t\t// Windows rejects a non-zero ctxIn with a nil ctxOut.\n\t\tctxOut = make([]byte, len(ctxIn))\n\t}\n\n\tattr := sys.ProgRunAttr{\n\t\tProgFd:      p.fd.Uint(),\n\t\tDataSizeIn:  uint32(len(opts.Data)),\n\t\tDataSizeOut: uint32(len(opts.DataOut)),\n\t\tDataIn:      sys.SlicePointer(opts.Data),\n\t\tDataOut:     sys.SlicePointer(opts.DataOut),\n\t\tRepeat:      uint32(opts.Repeat),\n\t\tCtxSizeIn:   uint32(len(ctxIn)),\n\t\tCtxSizeOut:  uint32(len(ctxOut)),\n\t\tCtxIn:       sys.SlicePointer(ctxIn),\n\t\tCtxOut:      sys.SlicePointer(ctxOut),\n\t\tFlags:       opts.Flags,\n\t\tCpu:         opts.CPU,\n\t\tBatchSize:   opts.BatchSize,\n\t}\n\n\tif p.Type() == Syscall && ctxIn != nil && ctxOut != nil {\n\t\t// Linux syscall program errors on non-nil ctxOut, uses ctxIn\n\t\t// for both input and output. Shield the user from this wart.\n\t\tif len(ctxIn) != len(ctxOut) {\n\t\t\treturn 0, 0, errors.New(\"length mismatch: Context and ContextOut\")\n\t\t}\n\t\tattr.CtxOut, attr.CtxSizeOut = sys.TypedPointer[uint8]{}, 0\n\t\tctxOut = ctxIn\n\t}\n\nretry:\n\tfor {\n\t\terr := sys.ProgRun(&attr)\n\t\tif err == nil {\n\t\t\tbreak retry\n\t\t}\n\n\t\tif errors.Is(err, unix.EINTR) {\n\t\t\tif attr.Repeat <= 1 {\n\t\t\t\t// Older kernels check whether enough repetitions have been\n\t\t\t\t// executed only after checking for pending signals.\n\t\t\t\t//\n\t\t\t\t//     run signal? done? run ...\n\t\t\t\t//\n\t\t\t\t// As a result we can get EINTR for repeat==1 even though\n\t\t\t\t// the program was run exactly once. Treat this as a\n\t\t\t\t// successful run instead.\n\t\t\t\t//\n\t\t\t\t// Since commit 607b9cc92bd7 (\"bpf: Consolidate shared test timing code\")\n\t\t\t\t// the conditions are reversed:\n\t\t\t\t//     run done? signal? ...\n\t\t\t\tbreak retry\n\t\t\t}\n\n\t\t\tif opts.Reset != nil {\n\t\t\t\topts.Reset()\n\t\t\t}\n\t\t\tcontinue retry\n\t\t}\n\n\t\tif errors.Is(err, sys.ENOTSUPP) {\n\t\t\treturn 0, 0, fmt.Errorf(\"kernel doesn't support running %s: %w\", p.Type(), ErrNotSupported)\n\t\t}\n\n\t\treturn 0, 0, err\n\t}\n\n\tif opts.DataOut != nil {\n\t\tif int(attr.DataSizeOut) > cap(opts.DataOut) {\n\t\t\t// Houston, we have a problem. The program created more data than we allocated,\n\t\t\t// and the kernel wrote past the end of our buffer.\n\t\t\tpanic(\"kernel wrote past end of output buffer\")\n\t\t}\n\t\topts.DataOut = opts.DataOut[:int(attr.DataSizeOut)]\n\t}\n\n\tif opts.ContextOut != nil {\n\t\tb := bytes.NewReader(ctxOut)\n\t\tif err := binary.Read(b, internal.NativeEndian, opts.ContextOut); err != nil {\n\t\t\treturn 0, 0, fmt.Errorf(\"failed to decode ContextOut: %v\", err)\n\t\t}\n\t}\n\n\ttotal := time.Duration(attr.Duration) * time.Nanosecond\n\treturn attr.Retval, total, nil\n}\n\nfunc unmarshalProgram(buf sysenc.Buffer) (*Program, error) {\n\tvar id uint32\n\tif err := buf.Unmarshal(&id); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Looking up an entry in a nested map or prog array returns an id,\n\t// not an fd.\n\treturn NewProgramFromID(ProgramID(id))\n}\n\nfunc marshalProgram(p *Program, length int) ([]byte, error) {\n\tif p == nil {\n\t\treturn nil, errors.New(\"can't marshal a nil Program\")\n\t}\n\n\tif length != 4 {\n\t\treturn nil, fmt.Errorf(\"can't marshal program to %d bytes\", length)\n\t}\n\n\tbuf := make([]byte, 4)\n\tinternal.NativeEndian.PutUint32(buf, p.fd.Uint())\n\treturn buf, nil\n}\n\n// LoadPinnedProgram loads a Program from a pin (file) on the BPF virtual\n// filesystem.\n//\n// Requires at least Linux 4.11.\nfunc LoadPinnedProgram(fileName string, opts *LoadPinOptions) (*Program, error) {\n\tfd, typ, err := sys.ObjGetTyped(&sys.ObjGetAttr{\n\t\tPathname:  sys.NewStringPointer(fileName),\n\t\tFileFlags: opts.Marshal(),\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif typ != sys.BPF_TYPE_PROG {\n\t\t_ = fd.Close()\n\t\treturn nil, fmt.Errorf(\"%s is not a Program\", fileName)\n\t}\n\n\tp, err := newProgramFromFD(fd)\n\tif err == nil {\n\t\tp.pinnedPath = fileName\n\n\t\tif haveObjName() != nil {\n\t\t\tp.name = filepath.Base(fileName)\n\t\t}\n\t}\n\n\treturn p, err\n}\n\n// ProgramGetNextID returns the ID of the next eBPF program.\n//\n// Returns ErrNotExist, if there is no next eBPF program.\nfunc ProgramGetNextID(startID ProgramID) (ProgramID, error) {\n\tattr := &sys.ProgGetNextIdAttr{Id: uint32(startID)}\n\treturn ProgramID(attr.NextId), sys.ProgGetNextId(attr)\n}\n\n// BindMap binds map to the program and is only released once program is released.\n//\n// This may be used in cases where metadata should be associated with the program\n// which otherwise does not contain any references to the map.\nfunc (p *Program) BindMap(m *Map) error {\n\tattr := &sys.ProgBindMapAttr{\n\t\tProgFd: uint32(p.FD()),\n\t\tMapFd:  uint32(m.FD()),\n\t}\n\n\treturn sys.ProgBindMap(attr)\n}\n\nvar errUnrecognizedAttachType = errors.New(\"unrecognized attach type\")\n\n// find an attach target type in the kernel.\n//\n// name, progType and attachType determine which type we need to attach to.\n//\n// The attach target may be in a loaded kernel module.\n// In that case the returned handle will be non-nil.\n// The caller is responsible for closing the handle.\n//\n// Returns errUnrecognizedAttachType if the combination of progType and attachType\n// is not recognised.\nfunc findProgramTargetInKernel(name string, progType ProgramType, attachType AttachType, cache *btf.Cache) (*btf.Handle, btf.TypeID, error) {\n\ttype match struct {\n\t\tp ProgramType\n\t\ta AttachType\n\t}\n\n\tvar (\n\t\ttypeName, featureName string\n\t\ttarget                btf.Type\n\t)\n\n\tswitch (match{progType, attachType}) {\n\tcase match{StructOps, AttachStructOps}:\n\t\ttypeName = name\n\t\tfeatureName = \"struct_ops \" + name\n\t\ttarget = (*btf.Struct)(nil)\n\tcase match{LSM, AttachLSMMac}:\n\t\ttypeName = \"bpf_lsm_\" + name\n\t\tfeatureName = name + \" LSM hook\"\n\t\ttarget = (*btf.Func)(nil)\n\tcase match{Tracing, AttachTraceIter}:\n\t\ttypeName = \"bpf_iter_\" + name\n\t\tfeatureName = name + \" iterator\"\n\t\ttarget = (*btf.Func)(nil)\n\tcase match{Tracing, AttachTraceFEntry}:\n\t\ttypeName = name\n\t\tfeatureName = fmt.Sprintf(\"fentry %s\", name)\n\t\ttarget = (*btf.Func)(nil)\n\tcase match{Tracing, AttachTraceFExit}:\n\t\ttypeName = name\n\t\tfeatureName = fmt.Sprintf(\"fexit %s\", name)\n\t\ttarget = (*btf.Func)(nil)\n\tcase match{Tracing, AttachModifyReturn}:\n\t\ttypeName = name\n\t\tfeatureName = fmt.Sprintf(\"fmod_ret %s\", name)\n\t\ttarget = (*btf.Func)(nil)\n\tcase match{Tracing, AttachTraceRawTp}:\n\t\ttypeName = fmt.Sprintf(\"btf_trace_%s\", name)\n\t\tfeatureName = fmt.Sprintf(\"raw_tp %s\", name)\n\t\ttarget = (*btf.Typedef)(nil)\n\tdefault:\n\t\treturn nil, 0, errUnrecognizedAttachType\n\t}\n\n\tspec, module, err := findTargetInKernel(typeName, &target, cache)\n\tif errors.Is(err, btf.ErrNotFound) {\n\t\treturn nil, 0, &internal.UnsupportedFeatureError{Name: featureName}\n\t}\n\t// See cilium/ebpf#894. Until we can disambiguate between equally-named kernel\n\t// symbols, we should explicitly refuse program loads. They will not reliably\n\t// do what the caller intended.\n\tif errors.Is(err, btf.ErrMultipleMatches) {\n\t\treturn nil, 0, fmt.Errorf(\"attaching to ambiguous kernel symbol is not supported: %w\", err)\n\t}\n\tif err != nil {\n\t\treturn nil, 0, fmt.Errorf(\"find target for %s: %w\", featureName, err)\n\t}\n\n\tid, err := spec.TypeID(target)\n\tif err != nil {\n\t\tmodule.Close()\n\t\treturn nil, 0, err\n\t}\n\n\treturn module, id, nil\n}\n\n// findTargetInKernel attempts to find a named type in the current kernel.\n//\n// target will point at the found type after a successful call. Searches both\n// vmlinux and any loaded modules.\n//\n// Returns a non-nil handle if the type was found in a module, or btf.ErrNotFound\n// if the type wasn't found at all.\nfunc findTargetInKernel(typeName string, target *btf.Type, cache *btf.Cache) (*btf.Spec, *btf.Handle, error) {\n\tkernelSpec, err := cache.Kernel()\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\terr = kernelSpec.TypeByName(typeName, target)\n\tif errors.Is(err, btf.ErrNotFound) {\n\t\tspec, module, err := findTargetInModule(typeName, target, cache)\n\t\tif err != nil {\n\t\t\t// EPERM may be returned when we do not have CAP_SYS_ADMIN.\n\t\t\t// Wrap error with btf.ErrNotFound so callers can handle it accordingly.\n\t\t\tif errors.Is(err, unix.EPERM) {\n\t\t\t\treturn spec, nil, fmt.Errorf(\"find target in modules: %w (%w)\", btf.ErrNotFound, err)\n\t\t\t}\n\n\t\t\treturn nil, nil, fmt.Errorf(\"find target in modules: %w\", err)\n\t\t}\n\t\treturn spec, module, nil\n\t}\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"find target in vmlinux: %w\", err)\n\t}\n\treturn kernelSpec, nil, err\n}\n\n// findTargetInModule attempts to find a named type in any loaded module.\n//\n// base must contain the kernel's types and is used to parse kmod BTF. Modules\n// are searched in the order they were loaded.\n//\n// Returns btf.ErrNotFound if the target can't be found in any module.\nfunc findTargetInModule(typeName string, target *btf.Type, cache *btf.Cache) (*btf.Spec, *btf.Handle, error) {\n\tit := new(btf.HandleIterator)\n\tdefer it.Handle.Close()\n\n\tfor it.Next() {\n\t\tinfo, err := it.Handle.Info()\n\t\tif err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"get info for BTF ID %d: %w\", it.ID, err)\n\t\t}\n\n\t\tif !info.IsModule() {\n\t\t\tcontinue\n\t\t}\n\n\t\tspec, err := cache.Module(info.Name)\n\t\tif err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"parse types for module %s: %w\", info.Name, err)\n\t\t}\n\n\t\terr = spec.TypeByName(typeName, target)\n\t\tif errors.Is(err, btf.ErrNotFound) {\n\t\t\tcontinue\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"lookup type in module %s: %w\", info.Name, err)\n\t\t}\n\n\t\treturn spec, it.Take(), nil\n\t}\n\tif err := it.Err(); err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"iterate modules: %w\", err)\n\t}\n\n\treturn nil, nil, btf.ErrNotFound\n}\n\n// find an attach target type in a program.\n//\n// Returns errUnrecognizedAttachType.\nfunc findTargetInProgram(prog *Program, name string, progType ProgramType, attachType AttachType) (btf.TypeID, error) {\n\ttype match struct {\n\t\tp ProgramType\n\t\ta AttachType\n\t}\n\n\tvar typeName string\n\tswitch (match{progType, attachType}) {\n\tcase match{Extension, AttachNone},\n\t\tmatch{Tracing, AttachTraceFEntry},\n\t\tmatch{Tracing, AttachTraceFExit}:\n\t\ttypeName = name\n\tdefault:\n\t\treturn 0, errUnrecognizedAttachType\n\t}\n\n\tbtfHandle, err := prog.Handle()\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"load target BTF: %w\", err)\n\t}\n\tdefer btfHandle.Close()\n\n\tspec, err := btfHandle.Spec(nil)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tvar targetFunc *btf.Func\n\terr = spec.TypeByName(typeName, &targetFunc)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"find target %s: %w\", typeName, err)\n\t}\n\n\treturn spec.TypeID(targetFunc)\n}\n"
  },
  {
    "path": "prog_linux_test.go",
    "content": "package ebpf\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"runtime\"\n\t\"slices\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\nfunc TestProgramTestRunInterrupt(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.0\", \"EINTR from BPF_PROG_TEST_RUN\")\n\n\tprog := createBasicProgram(t)\n\n\tvar (\n\t\ttgid    = unix.Getpid()\n\t\ttidChan = make(chan int, 1)\n\t\texit    = make(chan struct{})\n\t\terrs    = make(chan error, 1)\n\t\ttimeout = time.After(5 * time.Second)\n\t)\n\n\tdefer close(exit)\n\n\tgo func() {\n\t\truntime.LockOSThread()\n\t\tdefer func() {\n\t\t\t// Wait for the test to allow us to unlock the OS thread, to\n\t\t\t// ensure that we don't send SIGUSR1 to the wrong thread.\n\t\t\t<-exit\n\t\t\truntime.UnlockOSThread()\n\t\t}()\n\n\t\ttidChan <- unix.Gettid()\n\n\t\t// Block this thread in the BPF syscall, so that we can\n\t\t// trigger EINTR by sending a signal.\n\t\topts := RunOptions{\n\t\t\tData:   internal.EmptyBPFContext,\n\t\t\tRepeat: math.MaxInt32,\n\t\t\tReset: func() {\n\t\t\t\t// We don't know how long finishing the\n\t\t\t\t// test run would take, so flag that we've seen\n\t\t\t\t// an interruption and abort the goroutine.\n\t\t\t\tclose(errs)\n\t\t\t\truntime.Goexit()\n\t\t\t},\n\t\t}\n\t\t_, _, err := prog.run(&opts)\n\n\t\terrs <- err\n\t}()\n\n\ttid := <-tidChan\n\tfor {\n\t\terr := unix.Tgkill(tgid, tid, unix.SIGUSR1)\n\t\tif err != nil {\n\t\t\tt.Fatal(\"Can't send signal to goroutine thread:\", err)\n\t\t}\n\n\t\tselect {\n\t\tcase err, ok := <-errs:\n\t\t\tif !ok {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\ttestutils.SkipIfNotSupported(t, err)\n\t\t\tif err == nil {\n\t\t\t\tt.Fatal(\"testRun wasn't interrupted\")\n\t\t\t}\n\n\t\t\tt.Fatal(\"testRun returned an error:\", err)\n\n\t\tcase <-timeout:\n\t\t\tt.Fatal(\"Timed out trying to interrupt the goroutine\")\n\n\t\tdefault:\n\t\t}\n\t}\n}\n\nfunc TestProgramVerifierLogLinux(t *testing.T) {\n\tcheck := func(t *testing.T, err error) {\n\t\tt.Helper()\n\n\t\tvar ve *internal.VerifierError\n\t\tqt.Assert(t, qt.ErrorAs(err, &ve))\n\n\t\tloglen := len(fmt.Sprintf(\"%+v\", ve))\n\t\tqt.Assert(t, qt.IsTrue(loglen > minVerifierLogSize),\n\t\t\tqt.Commentf(\"Log buffer didn't grow past minimum, got %d bytes\", loglen))\n\t}\n\n\t// Generate a base program of sufficient size whose verifier log does not fit\n\t// in the minimum buffer size. Stay under 4096 insn limit of older kernels.\n\tvar base asm.Instructions\n\tfor i := 0; i < 4093; i++ {\n\t\tbase = append(base, asm.Mov.Reg(asm.R0, asm.R1))\n\t}\n\n\t// Touch R10 (read-only frame pointer) to reliably force a verifier error.\n\tinvalid := slices.Clone(base)\n\tinvalid = append(invalid, asm.Mov.Reg(asm.R10, asm.R0))\n\tinvalid = append(invalid, asm.Return())\n\n\tvalid := slices.Clone(base)\n\tvalid = append(valid, asm.Return())\n\n\t// Start out with testing against the invalid program.\n\tspec := &ProgramSpec{\n\t\tType:         SocketFilter,\n\t\tLicense:      \"MIT\",\n\t\tInstructions: invalid,\n\t}\n\n\t_, err := newProgram(t, spec, nil)\n\tcheck(t, err)\n\n\t// Run tests against a valid program from here on out.\n\tspec.Instructions = valid\n\n\t// Explicitly request verifier log for a valid program and a start size.\n\tprog := mustNewProgram(t, spec, &ProgramOptions{\n\t\tLogLevel:     LogLevelInstruction,\n\t\tLogSizeStart: minVerifierLogSize * 2,\n\t})\n\tqt.Assert(t, qt.IsTrue(len(prog.VerifierLog) > minVerifierLogSize))\n}\n\nfunc TestProgramTestRunSyscall(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.14\", \"BPF_PROG_TYPE_SYSCALL\")\n\n\tprog := mustNewProgram(t, &ProgramSpec{\n\t\tType:    Syscall,\n\t\tFlags:   sys.BPF_F_SLEEPABLE,\n\t\tLicense: \"MIT\",\n\t\tInstructions: []asm.Instruction{\n\t\t\t// fn (ctx *u64) { *ctx++; return *ctx }\n\t\t\tasm.LoadMem(asm.R0, asm.R1, 0, asm.DWord),\n\t\t\tasm.Add.Imm(asm.R0, 1),\n\t\t\tasm.StoreMem(asm.R1, 0, asm.R0, asm.DWord),\n\t\t\tasm.Return(),\n\t\t},\n\t}, nil)\n\n\t// only Context\n\trc, err := prog.Run(&RunOptions{Context: uint64(42)})\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tqt.Assert(t, qt.Equals(rc, 43))\n\n\t// Context and ContextOut\n\tout := uint64(0)\n\trc, err = prog.Run(&RunOptions{Context: uint64(99), ContextOut: &out})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tqt.Assert(t, qt.Equals(rc, 100))\n\tqt.Assert(t, qt.Equals(out, 100))\n}\n"
  },
  {
    "path": "prog_test.go",
    "content": "package ebpf\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"syscall\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/platform\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\nfunc TestProgramRun(t *testing.T) {\n\tpat := []byte{0xDE, 0xAD, 0xBE, 0xEF}\n\tbuf := internal.EmptyBPFContext\n\n\t// r1  : ctx_start\n\t// r1+4: ctx_end\n\tins := asm.Instructions{\n\t\t// r2 = *(r1+4)\n\t\tasm.LoadMem(asm.R2, asm.R1, 4, asm.Word),\n\t\t// r1 = *(r1+0)\n\t\tasm.LoadMem(asm.R1, asm.R1, 0, asm.Word),\n\t\t// r3 = r1\n\t\tasm.Mov.Reg(asm.R3, asm.R1),\n\t\t// r3 += len(buf)\n\t\tasm.Add.Imm(asm.R3, int32(len(buf))),\n\t\t// if r3 > r2 goto +len(pat)\n\t\tasm.JGT.Reg(asm.R3, asm.R2, \"out\"),\n\t}\n\tfor i, b := range pat {\n\t\tins = append(ins, asm.StoreImm(asm.R1, int16(i), int64(b), asm.Byte))\n\t}\n\tins = append(ins,\n\t\t// return 42\n\t\tasm.LoadImm(asm.R0, 42, asm.DWord).WithSymbol(\"out\"),\n\t\tasm.Return(),\n\t)\n\n\tif platform.IsWindows {\n\t\t// Windows uses an incompatible context for XDP. Pointers are\n\t\t// 64 bit.\n\t\t// See https://github.com/microsoft/ebpf-for-windows/issues/3873\n\t\t// r2 = *(r1+8)\n\t\tins[0] = asm.LoadMem(asm.R2, asm.R1, 8, asm.DWord)\n\t\t// r1 = *(r1+0)\n\t\tins[1] = asm.LoadMem(asm.R1, asm.R1, 0, asm.DWord)\n\t}\n\n\tt.Log(ins)\n\n\tprog := mustNewProgram(t, &ProgramSpec{\n\t\tName:         \"test\",\n\t\tType:         XDP,\n\t\tInstructions: ins,\n\t\tLicense:      \"MIT\",\n\t}, nil)\n\n\tp2, err := prog.Clone()\n\tif err != nil {\n\t\tt.Fatal(\"Can't clone program\")\n\t}\n\tdefer p2.Close()\n\n\tprog.Close()\n\tprog = p2\n\n\tout := make([]byte, len(buf))\n\tret := mustRun(t, prog, &RunOptions{Data: buf, DataOut: out})\n\tqt.Assert(t, qt.Equals(ret, 42))\n\tqt.Assert(t, qt.DeepEquals(out[:len(pat)], pat))\n}\n\nfunc TestProgramRunWithOptions(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.15\", \"XDP ctx_in/ctx_out\")\n\n\tbuf := internal.EmptyBPFContext\n\tvar prog *Program\n\tvar in, out any\n\tif platform.IsWindows {\n\t\ttype winSampleProgramContext struct {\n\t\t\t_           uint64 // data_start (currently leaks kernel pointer)\n\t\t\t_           uint64 // data_end (currently leaks kernel pointer)\n\t\t\tUint32Data  uint32\n\t\t\tUint16Data  uint16\n\t\t\t_           uint16\n\t\t\tHelperData1 uint32\n\t\t\tHelperData2 uint32\n\t\t}\n\t\tprog = createProgram(t, WindowsSample, 0)\n\t\tin = &winSampleProgramContext{Uint32Data: 23, HelperData2: 42}\n\t\tout = &winSampleProgramContext{Uint32Data: 23, HelperData2: 42}\n\t} else {\n\t\tprog = createProgram(t, XDP, int64(sys.XDP_ABORTED))\n\t\tin = &sys.XdpMd{Data: 0, DataEnd: uint32(len(buf))}\n\t\tout = &sys.XdpMd{}\n\t}\n\n\topts := RunOptions{\n\t\tData:       buf,\n\t\tContext:    in,\n\t\tContextOut: out,\n\t}\n\tret, err := prog.Run(&opts)\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif ret != 0 {\n\t\tt.Error(\"Expected return value to be 0, got\", ret)\n\t}\n\n\tqt.Assert(t, qt.DeepEquals(out, in))\n}\n\nfunc TestProgramRunRawTracepoint(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.10\", \"RawTracepoint test run\")\n\n\tprog := createProgram(t, RawTracepoint, 0)\n\n\tret, err := prog.Run(&RunOptions{})\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif ret != 0 {\n\t\tt.Error(\"Expected return value to be 0, got\", ret)\n\t}\n}\n\nfunc TestProgramRunEmptyData(t *testing.T) {\n\tprog := createProgram(t, SocketFilter, 0)\n\t_, err := prog.Run(nil)\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.ErrorIs(err, unix.EINVAL))\n}\n\nfunc TestProgramBenchmark(t *testing.T) {\n\tif platform.IsWindows {\n\t\tt.Skip(\"BPF_PROG_TEST_RUN requires providing context on Windows\")\n\t}\n\n\tprog := createBasicProgram(t)\n\n\tret, duration, err := prog.Benchmark(internal.EmptyBPFContext, 1, nil)\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(\"Error from Benchmark:\", err)\n\t}\n\n\tif ret != 2 {\n\t\tt.Error(\"Expected return value 2, got\", ret)\n\t}\n\n\tif duration == 0 {\n\t\tt.Error(\"Expected non-zero duration\")\n\t}\n}\n\nfunc TestProgramClose(t *testing.T) {\n\tprog := createBasicProgram(t)\n\n\tif err := prog.Close(); err != nil {\n\t\tt.Fatal(\"Can't close program:\", err)\n\t}\n}\n\nfunc TestProgramPin(t *testing.T) {\n\tspec := fixupProgramSpec(basicProgramSpec)\n\tprog := mustNewProgram(t, spec, nil)\n\n\ttmp := testutils.TempBPFFS(t)\n\n\tpath := filepath.Join(tmp, \"program\")\n\tif err := prog.Pin(path); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tpinned := prog.IsPinned()\n\tqt.Assert(t, qt.IsTrue(pinned))\n\n\tprog.Close()\n\n\tprog, err := LoadPinnedProgram(path, nil)\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer prog.Close()\n\n\tqt.Assert(t, qt.Equals(prog.Type(), spec.Type))\n\n\tif haveObjName() == nil {\n\t\tqt.Assert(t, qt.Equals(prog.name, \"test\"))\n\t} else {\n\t\tqt.Assert(t, qt.Equals(prog.name, \"program\"))\n\t}\n\n\tif !prog.IsPinned() {\n\t\tt.Error(\"Expected IsPinned to be true\")\n\t}\n}\n\nfunc TestProgramUnpin(t *testing.T) {\n\tprog := createBasicProgram(t)\n\n\ttmp := testutils.TempBPFFS(t)\n\n\tpath := filepath.Join(tmp, \"program\")\n\tif err := prog.Pin(path); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tpinned := prog.IsPinned()\n\tqt.Assert(t, qt.IsTrue(pinned))\n\n\tif err := prog.Unpin(); err != nil {\n\t\tt.Fatal(\"Failed to unpin program:\", err)\n\t}\n\tif _, err := os.Stat(path); err == nil {\n\t\tt.Fatal(\"Pinned program path still exists after unpinning:\", err)\n\t}\n}\n\nfunc TestProgramLoadPinnedWithFlags(t *testing.T) {\n\t// Introduced in commit 6e71b04a8224.\n\ttestutils.SkipOnOldKernel(t, \"4.14\", \"file_flags in BPF_OBJ_GET\")\n\n\tprog := createBasicProgram(t)\n\n\ttmp := testutils.TempBPFFS(t)\n\n\tpath := filepath.Join(tmp, \"program\")\n\tif err := prog.Pin(path); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tprog.Close()\n\n\t_, err := LoadPinnedProgram(path, &LoadPinOptions{\n\t\tFlags: math.MaxUint32,\n\t})\n\ttestutils.SkipIfNotSupported(t, err)\n\tif !errors.Is(err, unix.EINVAL) {\n\t\tt.Fatal(\"Invalid flags don't trigger an error:\", err)\n\t}\n}\n\nfunc TestProgramVerifierOutputOnError(t *testing.T) {\n\t_, err := newProgram(t, &ProgramSpec{\n\t\tType: SocketFilter,\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.Return(),\n\t\t},\n\t\tLicense: \"MIT\",\n\t}, nil)\n\tif err == nil {\n\t\tt.Fatal(\"Expected program to be invalid\")\n\t}\n\n\tve, ok := err.(*VerifierError)\n\tif !ok {\n\t\tt.Fatal(\"NewProgram does return an unwrapped VerifierError\")\n\t}\n\n\tswitch {\n\tcase platform.IsLinux:\n\t\tif !strings.Contains(ve.Error(), \"R0 !read_ok\") {\n\t\t\tt.Logf(\"%+v\", ve)\n\t\t\tt.Error(\"Missing verifier log in error summary\")\n\t\t}\n\tcase platform.IsWindows:\n\t\tif !strings.Contains(ve.Error(), \"r0.type == number\") {\n\t\t\tt.Logf(\"%+v\", ve)\n\t\t\tt.Error(\"Missing verifier log in error summary\")\n\t\t}\n\tdefault:\n\t\tt.Error(\"Unsupported platform\", runtime.GOOS)\n\t}\n}\n\nfunc TestProgramKernelVersion(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"4.20\", \"KernelVersion\")\n\n\t_ = mustNewProgram(t, &ProgramSpec{\n\t\tType: Kprobe,\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.LoadImm(asm.R0, 0, asm.DWord),\n\t\t\tasm.Return(),\n\t\t},\n\t\tKernelVersion: 42,\n\t\tLicense:       \"MIT\",\n\t}, nil)\n}\n\nfunc TestProgramVerifierLog(t *testing.T) {\n\tcheck := func(t *testing.T, err error) {\n\t\tt.Helper()\n\n\t\tvar ve *internal.VerifierError\n\t\tqt.Assert(t, qt.ErrorAs(err, &ve))\n\t\tloglen := 0\n\t\tfor _, line := range ve.Log {\n\t\t\tloglen += len(line)\n\t\t}\n\t\tqt.Assert(t, qt.IsTrue(loglen > 0))\n\t}\n\n\t// Touch R10 (read-only frame pointer) to reliably force a verifier error.\n\tinvalid := asm.Instructions{\n\t\tasm.Mov.Reg(asm.R10, asm.R0),\n\t\tasm.Return(),\n\t}\n\n\tvalid := asm.Instructions{\n\t\tasm.Mov.Imm(asm.R0, 0),\n\t\tasm.Return(),\n\t}\n\n\t// Start out with testing against the invalid program.\n\tspec := &ProgramSpec{\n\t\tType:         SocketFilter,\n\t\tLicense:      \"MIT\",\n\t\tInstructions: invalid,\n\t}\n\n\t// Don't explicitly request a verifier log for an invalid program.\n\t_, err := newProgram(t, spec, nil)\n\tcheck(t, err)\n\n\t// Disabling the verifier log should result in a VerifierError without a log.\n\t_, err = newProgram(t, spec, &ProgramOptions{\n\t\tLogDisabled: true,\n\t})\n\tvar ve *internal.VerifierError\n\tqt.Assert(t, qt.ErrorAs(err, &ve))\n\tqt.Assert(t, qt.HasLen(ve.Log, 0))\n\n\t// Explicitly request a verifier log for an invalid program.\n\t_, err = newProgram(t, spec, &ProgramOptions{\n\t\tLogLevel: LogLevelInstruction,\n\t})\n\tcheck(t, err)\n\n\t// Run tests against a valid program from here on out.\n\tspec.Instructions = valid\n\n\t// Don't request a verifier log, expect the valid program to be created\n\t// without errors.\n\tprog := mustNewProgram(t, spec, nil)\n\tqt.Assert(t, qt.HasLen(prog.VerifierLog, 0))\n\n\t// Explicitly request verifier log for a valid program.\n\tprog = mustNewProgram(t, spec, &ProgramOptions{\n\t\tLogLevel: LogLevelInstruction,\n\t})\n\tqt.Assert(t, qt.Not(qt.HasLen(prog.VerifierLog, 0)))\n}\n\nfunc TestProgramVerifierLogRetry(t *testing.T) {\n\tsomeError := errors.New(\"not a buffer error\")\n\n\tt.Run(\"retry with oversized buffer, no log_true_size\", func(t *testing.T) {\n\t\t// First load failure, without logging enabled. Retry with logging enabled.\n\t\tattr := &sys.ProgLoadAttr{LogLevel: 0, LogSize: 0}\n\t\tqt.Assert(t, qt.IsTrue(retryLogAttrs(attr, 0, someError)))\n\t\tqt.Assert(t, qt.Equals(attr.LogLevel, LogLevelBranch))\n\t\tqt.Assert(t, qt.Equals(attr.LogSize, minVerifierLogSize))\n\n\t\t// Second failure with logging enabled. No buffer error, don't retry.\n\t\tqt.Assert(t, qt.IsFalse(retryLogAttrs(attr, 0, someError)))\n\t\tqt.Assert(t, qt.Equals(attr.LogLevel, LogLevelBranch))\n\t\tqt.Assert(t, qt.Equals(attr.LogSize, minVerifierLogSize))\n\t})\n\n\tt.Run(\"retry with oversized buffer, with log_true_size\", func(t *testing.T) {\n\t\t// First load failure, without logging enabled. Retry with larger buffer.\n\t\tattr := &sys.ProgLoadAttr{LogLevel: 0, LogSize: 0}\n\t\tqt.Assert(t, qt.IsTrue(retryLogAttrs(attr, 0, someError)))\n\n\t\t// Buffer was sufficiently large and log_true_size was set. Don't retry and\n\t\t// don't modify LogSize to LogTrueSize.\n\t\tattr.LogTrueSize = 123\n\t\tqt.Assert(t, qt.IsFalse(retryLogAttrs(attr, 0, someError)))\n\t\tqt.Assert(t, qt.Equals(attr.LogSize, minVerifierLogSize))\n\t})\n\n\tt.Run(\"retry with undersized buffer, no log_true_size\", func(t *testing.T) {\n\t\t// First load failure, without logging enabled. Retry with larger buffer.\n\t\tattr := &sys.ProgLoadAttr{LogLevel: 0, LogSize: 0}\n\t\tqt.Assert(t, qt.IsTrue(retryLogAttrs(attr, 0, someError)))\n\n\t\t// Second failure, this time the kernel signals an undersized buffer. Retry\n\t\t// with double the size.\n\t\tqt.Assert(t, qt.IsTrue(retryLogAttrs(attr, 0, unix.ENOSPC)))\n\t\tqt.Assert(t, qt.Equals(attr.LogSize, minVerifierLogSize*2))\n\t})\n\n\tt.Run(\"retry with undersized buffer, with log_true_size\", func(t *testing.T) {\n\t\t// First load failure, without logging enabled. Retry with larger buffer.\n\t\tattr := &sys.ProgLoadAttr{LogLevel: 0, LogSize: 0}\n\t\tqt.Assert(t, qt.IsTrue(retryLogAttrs(attr, 0, someError)))\n\n\t\t// Second failure, the kernel signals undersized buffer and also sets\n\t\t// log_true_size. Retry with the exact size required.\n\t\tattr.LogTrueSize = 123\n\t\tqt.Assert(t, qt.IsTrue(retryLogAttrs(attr, 0, unix.ENOSPC)))\n\t\tqt.Assert(t, qt.Equals(attr.LogSize, 123))\n\t})\n\n\tt.Run(\"grow to maximum buffer size\", func(t *testing.T) {\n\t\t// Previous loads pushed the log size to (or above) half of the maximum,\n\t\t// which would make it overflow on the next retry. Make sure the log size\n\t\t// actually hits the maximum so we can bail out.\n\t\tattr := &sys.ProgLoadAttr{LogLevel: LogLevelBranch, LogSize: maxVerifierLogSize / 2}\n\t\tqt.Assert(t, qt.IsTrue(retryLogAttrs(attr, 0, unix.ENOSPC)))\n\t\tqt.Assert(t, qt.Equals(attr.LogSize, maxVerifierLogSize))\n\n\t\t// Don't retry if the buffer is already at the maximum size, no matter\n\t\t// the return code.\n\t\tqt.Assert(t, qt.IsFalse(retryLogAttrs(attr, 0, someError)))\n\t\tqt.Assert(t, qt.IsFalse(retryLogAttrs(attr, 0, unix.ENOSPC)))\n\t})\n\n\tt.Run(\"start at maximum buffer size\", func(t *testing.T) {\n\t\t// The user requested a log buffer exceeding the maximum size, but no log\n\t\t// level. Retry with the maximum size and default log level.\n\t\tattr := &sys.ProgLoadAttr{LogLevel: 0, LogSize: 0}\n\t\tqt.Assert(t, qt.IsTrue(retryLogAttrs(attr, math.MaxUint32, unix.EINVAL)))\n\t\tqt.Assert(t, qt.Equals(attr.LogLevel, LogLevelBranch))\n\t\tqt.Assert(t, qt.Equals(attr.LogSize, maxVerifierLogSize))\n\n\t\t// Log still doesn't fit maximum-size buffer. Don't retry.\n\t\tqt.Assert(t, qt.IsFalse(retryLogAttrs(attr, 0, unix.ENOSPC)))\n\t})\n\n\tt.Run(\"ensure growth terminates within max attempts\", func(t *testing.T) {\n\t\tattr := &sys.ProgLoadAttr{LogLevel: 0, LogSize: 0}\n\t\tvar terminated bool\n\t\tfor i := 1; i <= maxVerifierAttempts; i++ {\n\t\t\tif !retryLogAttrs(attr, 0, syscall.ENOSPC) {\n\t\t\t\tterminated = true\n\t\t\t}\n\t\t}\n\t\tqt.Assert(t, qt.IsTrue(terminated))\n\t})\n}\n\nfunc TestProgramWithUnsatisfiedMap(t *testing.T) {\n\tcoll, err := LoadCollectionSpec(\"testdata/loader-el.elf\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// The program will have at least one map reference.\n\tprogSpec := coll.Programs[\"xdp_prog\"]\n\tprogSpec.ByteOrder = nil\n\n\t_, err = newProgram(t, progSpec, nil)\n\tif !errors.Is(err, asm.ErrUnsatisfiedMapReference) {\n\t\tt.Fatal(\"Expected an error wrapping asm.ErrUnsatisfiedMapReference, got\", err)\n\t}\n\tt.Log(err)\n}\n\nfunc TestProgramName(t *testing.T) {\n\ttestutils.SkipIfNotSupported(t, haveObjName())\n\n\tprog := mustNewProgram(t, &ProgramSpec{\n\t\tName: \"test*123\",\n\t\tType: SocketFilter,\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.LoadImm(asm.R0, 1, asm.DWord),\n\t\t\tasm.Return(),\n\t\t},\n\t\tLicense: \"MIT\",\n\t}, nil)\n\n\tvar info sys.ProgInfo\n\tif err := sys.ObjInfo(prog.fd, &info); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tname := unix.ByteSliceToString(info.Name[:])\n\tqt.Assert(t, qt.Equals(name, \"test123\"))\n}\n\nfunc TestProgramCloneNil(t *testing.T) {\n\tp, err := (*Program)(nil).Clone()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif p != nil {\n\t\tt.Fatal(\"Cloning a nil Program doesn't return nil\")\n\t}\n}\n\nfunc TestProgramMarshaling(t *testing.T) {\n\tconst idx = uint32(0)\n\n\tarr := createMap(t, ProgramArray, 1)\n\tdefer arr.Close()\n\n\tif err := arr.Put(idx, (*Program)(nil)); err == nil {\n\t\tt.Fatal(\"Put accepted a nil Program\")\n\t}\n\n\tprog := createBasicProgram(t)\n\n\tif err := arr.Put(idx, prog); err != nil {\n\t\tt.Fatal(\"Can't put program:\", err)\n\t}\n\n\tif err := arr.Lookup(idx, Program{}); err == nil {\n\t\tt.Fatal(\"Lookup accepts non-pointer Program\")\n\t}\n\n\tvar prog2 *Program\n\tdefer prog2.Close()\n\n\tif err := arr.Lookup(idx, prog2); err == nil {\n\t\tt.Fatal(\"Get accepts *Program\")\n\t}\n\n\ttestutils.SkipOnOldKernel(t, \"4.12\", \"lookup for ProgramArray\")\n\n\tif err := arr.Lookup(idx, &prog2); err != nil {\n\t\tt.Fatal(\"Can't unmarshal program:\", err)\n\t}\n\tdefer prog2.Close()\n\n\tif prog2 == nil {\n\t\tt.Fatal(\"Unmarshalling set program to nil\")\n\t}\n}\n\nfunc TestProgramFromFD(t *testing.T) {\n\tspec := fixupProgramSpec(basicProgramSpec)\n\tprog := mustNewProgram(t, spec, nil)\n\n\t// If you're thinking about copying this, don't. Use\n\t// Clone() instead.\n\tprog2, err := NewProgramFromFD(testutils.DupFD(t, prog.FD()))\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer prog2.Close()\n\n\t// Name and type are supposed to be copied from program info.\n\tif haveObjName() == nil && prog2.name != \"test\" {\n\t\tt.Errorf(\"Expected program to have name test, got '%s'\", prog2.name)\n\t}\n\n\tqt.Assert(t, qt.Equals(prog2.Type(), spec.Type))\n}\n\nfunc TestHaveProgTestRun(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, haveProgRun)\n}\n\nfunc TestProgramGetNextID(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"4.13\", \"bpf_prog_get_next_id\")\n\n\t// Ensure there is at least one program loaded\n\t_ = createBasicProgram(t)\n\n\t// As there can be multiple eBPF programs, we loop over all of them and\n\t// make sure, the IDs increase and the last call will return ErrNotExist\n\tlast := ProgramID(0)\n\tfor {\n\t\tnext, err := ProgramGetNextID(last)\n\t\tif errors.Is(err, os.ErrNotExist) {\n\t\t\tif last == 0 {\n\t\t\t\tt.Fatal(\"Got ErrNotExist on the first iteration\")\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\tt.Fatal(\"Unexpected error:\", err)\n\t\t}\n\t\tif next <= last {\n\t\t\tt.Fatalf(\"Expected next ID (%d) to be higher than the last ID (%d)\", next, last)\n\t\t}\n\t\tlast = next\n\t}\n}\n\nfunc TestNewProgramFromID(t *testing.T) {\n\tprog := createBasicProgram(t)\n\n\tinfo, err := prog.Info()\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(\"Could not get program info:\", err)\n\t}\n\n\tid, ok := info.ID()\n\tif !ok {\n\t\tt.Skip(\"Program ID not supported\")\n\t}\n\n\tprog2, err := NewProgramFromID(id)\n\tif err != nil {\n\t\tt.Fatalf(\"Can't get FD for program ID %d: %v\", id, err)\n\t}\n\tprog2.Close()\n\n\t// As there can be multiple programs, we use max(uint32) as ProgramID to trigger an expected error.\n\t_, err = NewProgramFromID(ProgramID(math.MaxUint32))\n\tif !errors.Is(err, os.ErrNotExist) {\n\t\tt.Fatal(\"Expected ErrNotExist, got:\", err)\n\t}\n}\n\nfunc TestProgramRejectIncorrectByteOrder(t *testing.T) {\n\tspec := basicProgramSpec.Copy()\n\n\tspec.ByteOrder = binary.BigEndian\n\tif spec.ByteOrder == internal.NativeEndian {\n\t\tspec.ByteOrder = binary.LittleEndian\n\t}\n\n\t_, err := newProgram(t, spec, nil)\n\tif err == nil {\n\t\tt.Error(\"Incorrect ByteOrder should be rejected at load time\")\n\t}\n}\n\n// This uses unkeyed fields on purpose to force setting a non-zero value when\n// a new field is added.\nfunc TestProgramSpecCopy(t *testing.T) {\n\ta := &ProgramSpec{\n\t\t\"test\",\n\t\t1,\n\t\t1,\n\t\t1,\n\t\t\"attach\",\n\t\tnil, // Can't copy Program\n\t\t\"section\",\n\t\tasm.Instructions{\n\t\t\tasm.Return(),\n\t\t},\n\t\t1,\n\t\t\"license\",\n\t\t1,\n\t\tbinary.LittleEndian,\n\t}\n\n\tqt.Check(t, qt.IsNil((*ProgramSpec)(nil).Copy()))\n\tqt.Assert(t, testutils.IsDeepCopy(a.Copy(), a))\n}\n\nfunc TestProgramSpecCompatible(t *testing.T) {\n\tarr := createMap(t, Array, 2)\n\n\tspec := &ProgramSpec{\n\t\tType: SocketFilter,\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.LoadImm(asm.R0, -1, asm.DWord),\n\t\t\tasm.LoadMapPtr(asm.R1, arr.FD()),\n\t\t\tasm.Mov.Imm32(asm.R0, 0),\n\t\t\tasm.Return(),\n\t\t},\n\t\tLicense: \"MIT\",\n\t}\n\n\tprog := mustNewProgram(t, spec, nil)\n\n\tinfo, err := prog.Info()\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n\n\terr = spec.Compatible(info)\n\ttestutils.SkipIfNotSupportedOnOS(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n}\n\nfunc TestProgramAttachToKernel(t *testing.T) {\n\t// See https://github.com/torvalds/linux/commit/290248a5b7d829871b3ea3c62578613a580a1744\n\ttestutils.SkipOnOldKernel(t, \"5.5\", \"attach_btf_id\")\n\n\ttests := []struct {\n\t\tattachTo    string\n\t\tprogramType ProgramType\n\t\tattachType  AttachType\n\t\tflags       uint32\n\t}{\n\t\t{\n\t\t\tattachTo:    \"task_getpgid\",\n\t\t\tprogramType: LSM,\n\t\t\tattachType:  AttachLSMMac,\n\t\t},\n\t\t{\n\t\t\tattachTo:    \"inet_dgram_connect\",\n\t\t\tprogramType: Tracing,\n\t\t\tattachType:  AttachTraceFEntry,\n\t\t},\n\t\t{\n\t\t\tattachTo:    \"inet_dgram_connect\",\n\t\t\tprogramType: Tracing,\n\t\t\tattachType:  AttachTraceFExit,\n\t\t},\n\t\t{\n\t\t\tattachTo:    \"bpf_modify_return_test\",\n\t\t\tprogramType: Tracing,\n\t\t\tattachType:  AttachModifyReturn,\n\t\t},\n\t\t{\n\t\t\tattachTo:    \"kfree_skb\",\n\t\t\tprogramType: Tracing,\n\t\t\tattachType:  AttachTraceRawTp,\n\t\t},\n\t\t{\n\t\t\tattachTo:    \"bpf_testmod_test_read\",\n\t\t\tprogramType: Tracing,\n\t\t\tattachType:  AttachTraceFEntry,\n\t\t},\n\t\t{\n\t\t\tattachTo:    \"bpf_testmod_test_read\",\n\t\t\tprogramType: Tracing,\n\t\t\tattachType:  AttachTraceFExit,\n\t\t},\n\t\t{\n\t\t\tattachTo:    \"bpf_testmod_test_read\",\n\t\t\tprogramType: Tracing,\n\t\t\tattachType:  AttachModifyReturn,\n\t\t},\n\t\t{\n\t\t\tattachTo:    \"bpf_testmod_test_read\",\n\t\t\tprogramType: Tracing,\n\t\t\tattachType:  AttachTraceRawTp,\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tname := fmt.Sprintf(\"%s:%s\", test.attachType, test.attachTo)\n\t\tt.Run(name, func(t *testing.T) {\n\t\t\tif strings.HasPrefix(test.attachTo, \"bpf_testmod_\") {\n\t\t\t\trequireTestmod(t)\n\t\t\t}\n\n\t\t\t_ = mustNewProgram(t, &ProgramSpec{\n\t\t\t\tAttachTo:   test.attachTo,\n\t\t\t\tAttachType: test.attachType,\n\t\t\t\tInstructions: asm.Instructions{\n\t\t\t\t\tasm.LoadImm(asm.R0, 0, asm.DWord),\n\t\t\t\t\tasm.Return(),\n\t\t\t\t},\n\t\t\t\tLicense: \"GPL\",\n\t\t\t\tType:    test.programType,\n\t\t\t\tFlags:   test.flags,\n\t\t\t}, nil)\n\t\t})\n\t}\n}\n\nfunc TestProgramKernelTypes(t *testing.T) {\n\tif _, err := os.Stat(\"/sys/kernel/btf/vmlinux\"); os.IsNotExist(err) {\n\t\tt.Skip(\"/sys/kernel/btf/vmlinux not present\")\n\t}\n\n\tbtfSpec, err := btf.LoadSpec(\"/sys/kernel/btf/vmlinux\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t_, err = newProgram(t, &ProgramSpec{\n\t\tType:       Tracing,\n\t\tAttachType: AttachTraceIter,\n\t\tAttachTo:   \"bpf_map\",\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.Mov.Imm(asm.R0, 0),\n\t\t\tasm.Return(),\n\t\t},\n\t\tLicense: \"MIT\",\n\t}, &ProgramOptions{\n\t\tKernelTypes: btfSpec,\n\t})\n\ttestutils.SkipIfNotSupported(t, err)\n\tqt.Assert(t, qt.IsNil(err))\n}\n\nfunc TestProgramBindMap(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.10\", \"BPF_PROG_BIND_MAP\")\n\n\tarr := createMap(t, Array, 2)\n\tprog := createBasicProgram(t)\n\n\t// The attached map does not contain BTF information. So\n\t// the metadata part of the program will be empty. This\n\t// test just makes sure that we can bind a map to a program.\n\tif err := prog.BindMap(arr); err != nil {\n\t\tt.Errorf(\"Failed to bind map to program: %v\", err)\n\t}\n}\n\nfunc TestProgramInstructions(t *testing.T) {\n\tname := \"test_prog\"\n\tspec := &ProgramSpec{\n\t\tType: SocketFilter,\n\t\tName: name,\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.LoadImm(asm.R0, -1, asm.DWord).WithSymbol(name),\n\t\t\tasm.Return(),\n\t\t},\n\t\tLicense: \"MIT\",\n\t}\n\n\tprog := mustNewProgram(t, spec, nil)\n\n\tpi, err := prog.Info()\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif platform.IsWindows {\n\t\tt.Skip(\"prog.Info() does not return a valid Tag on Windows\")\n\t}\n\n\tok, err := spec.Instructions.HasTag(pi.Tag, internal.NativeEndian)\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.IsTrue(ok), qt.Commentf(\"ProgramSpec tag differs from xlated instructions\"))\n}\n\nfunc TestProgramLoadErrors(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"4.10\", \"stable verifier log output\")\n\n\tspec, err := LoadCollectionSpec(testutils.NativeFile(t, \"testdata/errors-%s.elf\"))\n\tqt.Assert(t, qt.IsNil(err))\n\n\tvar b btf.Builder\n\traw, err := b.Marshal(nil, nil)\n\tqt.Assert(t, qt.IsNil(err))\n\tempty, err := btf.LoadSpecFromReader(bytes.NewReader(raw))\n\tqt.Assert(t, qt.IsNil(err))\n\n\tfor _, test := range []struct {\n\t\tname string\n\t\twant error\n\t}{\n\t\t{\"poisoned_single\", errBadRelocation},\n\t\t{\"poisoned_double\", errBadRelocation},\n\t\t{\"poisoned_kfunc\", errUnknownKfunc},\n\t} {\n\t\tprogSpec := spec.Programs[test.name]\n\t\tqt.Assert(t, qt.IsNotNil(progSpec))\n\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tt.Log(progSpec.Instructions)\n\t\t\t_, err := newProgram(t, progSpec, &ProgramOptions{\n\t\t\t\tKernelTypes: empty,\n\t\t\t})\n\t\t\ttestutils.SkipIfNotSupported(t, err)\n\n\t\t\tvar ve *VerifierError\n\t\t\tqt.Assert(t, qt.ErrorAs(err, &ve))\n\t\t\tt.Logf(\"%-5v\", ve)\n\n\t\t\tqt.Assert(t, qt.ErrorIs(err, test.want))\n\t\t})\n\t}\n}\n\nfunc TestProgramTargetsKernelModule(t *testing.T) {\n\tps := ProgramSpec{Type: Kprobe}\n\tqt.Assert(t, qt.IsFalse(ps.targetsKernelModule()))\n\n\tps.AttachTo = \"bpf_testmod_test_read\"\n\tqt.Assert(t, qt.IsTrue(ps.targetsKernelModule()))\n}\n\nfunc TestProgramLoadBoundToDevice(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"6.3\", \"device-bound XDP programs\")\n\n\tins := asm.Instructions{\n\t\tasm.LoadImm(asm.R0, 2, asm.DWord).WithSymbol(\"out\"),\n\t\tasm.Return(),\n\t}\n\n\t_, err := NewProgram(&ProgramSpec{\n\t\tType:         XDP,\n\t\tIfindex:      math.MaxUint32,\n\t\tAttachType:   AttachXDP,\n\t\tInstructions: ins,\n\t\tFlags:        sys.BPF_F_XDP_DEV_BOUND_ONLY,\n\t\tLicense:      \"MIT\",\n\t})\n\ttestutils.SkipIfNotSupportedOnOS(t, err)\n\n\t// Binding to loopback leads to crashes, yet is only explicitly disallowed\n\t// since 3595599fa836 (\"net: xdp: Disallow attaching device-bound programs in\n\t// generic mode\"). This only landed in 6.14 and returns EOPNOTSUPP.\n\t//\n\t// However, since attaching to loopback quietly succeeds on older kernels, use\n\t// a non-existent ifindex to trigger EINVAL on all kernels. Without specifying\n\t// ifindex, loading the program succeeds if the kernel knows the\n\t// DEV_BOUND_ONLY flag.\n\tqt.Assert(t, qt.ErrorIs(err, unix.EINVAL))\n}\n\nfunc BenchmarkNewProgram(b *testing.B) {\n\ttestutils.SkipOnOldKernel(b, \"5.18\", \"kfunc support\")\n\tspec, err := LoadCollectionSpec(testutils.NativeFile(b, \"testdata/kfunc-%s.elf\"))\n\tqt.Assert(b, qt.IsNil(err))\n\n\tb.ReportAllocs()\n\n\tfor b.Loop() {\n\t\t_, err := NewProgram(spec.Programs[\"benchmark\"])\n\t\tif !errors.Is(err, unix.EACCES) {\n\t\t\tb.Fatal(\"Unexpected error:\", err)\n\t\t}\n\t}\n}\n\n// Print the full verifier log when loading a program fails.\nfunc ExampleVerifierError_retrieveFullLog() {\n\t_, err := NewProgram(&ProgramSpec{\n\t\tType: SocketFilter,\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.LoadImm(asm.R0, 0, asm.DWord),\n\t\t\t// Missing Return\n\t\t},\n\t\tLicense: \"MIT\",\n\t})\n\n\tvar ve *VerifierError\n\tif errors.As(err, &ve) {\n\t\t// Using %+v will print the whole verifier error, not just the last\n\t\t// few lines.\n\t\tfmt.Printf(\"Verifier error: %+v\\n\", ve)\n\t}\n}\n\n// VerifierLog understands a variety of formatting flags.\nfunc ExampleVerifierError() {\n\terr := internal.ErrorWithLog(\n\t\t\"catastrophe\",\n\t\tsyscall.ENOSPC,\n\t\t[]byte(\"first\\nsecond\\nthird\"),\n\t)\n\n\tfmt.Printf(\"With %%s: %s\\n\", err)\n\tfmt.Printf(\"All log lines: %+v\\n\", err)\n\tfmt.Printf(\"First line: %+1v\\n\", err)\n\tfmt.Printf(\"Last two lines: %-2v\\n\", err)\n\n\t// Output: With %s: catastrophe: no space left on device: third (2 line(s) omitted)\n\t// All log lines: catastrophe: no space left on device:\n\t// \tfirst\n\t// \tsecond\n\t// \tthird\n\t// First line: catastrophe: no space left on device:\n\t// \tfirst\n\t// \t(2 line(s) omitted)\n\t// Last two lines: catastrophe: no space left on device:\n\t// \t(1 line(s) omitted)\n\t// \tsecond\n\t// \tthird\n}\n\n// Use NewProgramWithOptions if you'd like to get the verifier output\n// for a program, or if you want to change the buffer size used when\n// generating error messages.\nfunc ExampleProgram_retrieveVerifierLog() {\n\tspec := &ProgramSpec{\n\t\tType: SocketFilter,\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.LoadImm(asm.R0, 0, asm.DWord),\n\t\t\tasm.Return(),\n\t\t},\n\t\tLicense: \"MIT\",\n\t}\n\n\tprog, err := NewProgramWithOptions(spec, ProgramOptions{\n\t\tLogLevel: LogLevelInstruction,\n\t})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer prog.Close()\n\n\tfmt.Println(\"The verifier output is:\")\n\tfmt.Println(prog.VerifierLog)\n}\n\n// It's possible to read a program directly from a ProgramArray.\nfunc ExampleProgram_unmarshalFromMap() {\n\tprogArray, err := LoadPinnedMap(\"/path/to/map\", nil)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer progArray.Close()\n\n\t// Load a single program\n\tvar prog *Program\n\tif err := progArray.Lookup(uint32(0), &prog); err != nil {\n\t\tpanic(err)\n\t}\n\tdefer prog.Close()\n\n\tfmt.Println(\"first prog:\", prog)\n\n\t// Iterate all programs\n\tvar (\n\t\tkey     uint32\n\t\tentries = progArray.Iterate()\n\t)\n\n\tfor entries.Next(&key, &prog) {\n\t\tfmt.Println(key, \"is\", prog)\n\t}\n\n\tif err := entries.Err(); err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc ExampleProgramSpec_Compatible() {\n\tspec := &ProgramSpec{\n\t\tType: SocketFilter,\n\t\tInstructions: asm.Instructions{\n\t\t\tasm.LoadImm(asm.R0, 0, asm.DWord),\n\t\t\tasm.Return(),\n\t\t},\n\t\tLicense: \"MIT\",\n\t}\n\n\tprog, _ := NewProgram(spec)\n\tinfo, _ := prog.Info()\n\n\tif err := spec.Compatible(info); err != nil {\n\t\tfmt.Printf(\"The programs are incompatible: %s\\n\", err)\n\t} else {\n\t\tfmt.Println(\"The programs are compatible\")\n\t}\n}\n"
  },
  {
    "path": "ringbuf/doc.go",
    "content": "// Package ringbuf allows interacting with the BPF ring buffer.\n//\n// BPF allows submitting custom events to a BPF ring buffer map set up\n// by userspace. This is very useful to push things like packet samples\n// from BPF to a daemon running in user space.\npackage ringbuf\n"
  },
  {
    "path": "ringbuf/helper_other_test.go",
    "content": "//go:build !windows\n\npackage ringbuf\n\nimport (\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/asm\"\n)\n\nfunc mustOutputSamplesProg(tb testing.TB, sampleMessages ...sampleMessage) (*ebpf.Program, *ebpf.Map) {\n\ttb.Helper()\n\n\tevents, err := ebpf.NewMap(&ebpf.MapSpec{\n\t\tType:       ebpf.RingBuf,\n\t\tMaxEntries: 4096,\n\t})\n\tqt.Assert(tb, qt.IsNil(err))\n\ttb.Cleanup(func() {\n\t\tevents.Close()\n\t})\n\n\tvar maxSampleSize int\n\tfor _, sampleMessage := range sampleMessages {\n\t\tif sampleMessage.size > maxSampleSize {\n\t\t\tmaxSampleSize = sampleMessage.size\n\t\t}\n\t}\n\n\tinsns := asm.Instructions{\n\t\tasm.LoadImm(asm.R0, 0x0102030404030201, asm.DWord),\n\t\tasm.Mov.Reg(asm.R9, asm.R1),\n\t}\n\n\tbufDwords := (maxSampleSize / 8) + 1\n\tfor i := range bufDwords {\n\t\tinsns = append(insns,\n\t\t\tasm.StoreMem(asm.RFP, int16(i+1)*-8, asm.R0, asm.DWord),\n\t\t)\n\t}\n\n\tfor _, sampleMessage := range sampleMessages {\n\t\tinsns = append(insns,\n\t\t\tasm.LoadMapPtr(asm.R1, events.FD()),\n\t\t\tasm.Mov.Imm(asm.R2, int32(sampleMessage.size)),\n\t\t\tasm.Mov.Imm(asm.R3, int32(0)),\n\t\t\tasm.FnRingbufReserve.Call(),\n\t\t\tasm.JEq.Imm(asm.R0, 0, \"exit\"),\n\t\t\tasm.Mov.Reg(asm.R5, asm.R0),\n\t\t)\n\t\tfor i := range sampleMessage.size {\n\t\t\tinsns = append(insns,\n\t\t\t\tasm.LoadMem(asm.R4, asm.RFP, int16(i+1)*-1, asm.Byte),\n\t\t\t\tasm.StoreMem(asm.R5, int16(i), asm.R4, asm.Byte),\n\t\t\t)\n\t\t}\n\n\t\tif sampleMessage.discard {\n\t\t\tinsns = append(insns,\n\t\t\t\tasm.Mov.Reg(asm.R1, asm.R5),\n\t\t\t\tasm.Mov.Imm(asm.R2, sampleMessage.flags),\n\t\t\t\tasm.FnRingbufDiscard.Call(),\n\t\t\t)\n\t\t} else {\n\t\t\tinsns = append(insns,\n\t\t\t\tasm.Mov.Reg(asm.R1, asm.R5),\n\t\t\t\tasm.Mov.Imm(asm.R2, sampleMessage.flags),\n\t\t\t\tasm.FnRingbufSubmit.Call(),\n\t\t\t)\n\t\t}\n\t}\n\n\tinsns = append(insns,\n\t\tasm.Mov.Imm(asm.R0, int32(0)).WithSymbol(\"exit\"),\n\t\tasm.Return(),\n\t)\n\n\tprog, err := ebpf.NewProgram(&ebpf.ProgramSpec{\n\t\tLicense:      \"MIT\",\n\t\tType:         ebpf.XDP,\n\t\tInstructions: insns,\n\t})\n\tqt.Assert(tb, qt.IsNil(err))\n\ttb.Cleanup(func() {\n\t\tprog.Close()\n\t})\n\n\treturn prog, events\n}\n"
  },
  {
    "path": "ringbuf/helper_test.go",
    "content": "package ringbuf\n\nimport (\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/platform\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc mustRun(tb testing.TB, prog *ebpf.Program) {\n\ttb.Helper()\n\n\topts := &ebpf.RunOptions{\n\t\tData: internal.EmptyBPFContext,\n\t}\n\tif platform.IsWindows {\n\t\topts.Context = make([]byte, 32)\n\t}\n\n\tret, err := prog.Run(opts)\n\ttestutils.SkipIfNotSupported(tb, err)\n\tqt.Assert(tb, qt.IsNil(err))\n\n\tqt.Assert(tb, qt.Equals(ret, uint32(0)))\n}\n"
  },
  {
    "path": "ringbuf/helper_windows_test.go",
    "content": "package ringbuf\n\nimport (\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/asm\"\n)\n\nfunc mustOutputSamplesProg(tb testing.TB, sampleMessages ...sampleMessage) (*ebpf.Program, *ebpf.Map) {\n\ttb.Helper()\n\n\tevents, err := ebpf.NewMap(&ebpf.MapSpec{\n\t\tType:       ebpf.WindowsRingBuf,\n\t\tMaxEntries: 4096,\n\t})\n\tqt.Assert(tb, qt.IsNil(err))\n\ttb.Cleanup(func() {\n\t\tevents.Close()\n\t})\n\n\tvar maxSampleSize int\n\tfor _, sampleMessage := range sampleMessages {\n\t\tif sampleMessage.size > maxSampleSize {\n\t\t\tmaxSampleSize = sampleMessage.size\n\t\t}\n\t}\n\n\tinsns := asm.Instructions{\n\t\tasm.LoadImm(asm.R0, 0x0102030404030201, asm.DWord),\n\t\tasm.Mov.Reg(asm.R9, asm.R1),\n\t}\n\n\tbufDwords := (maxSampleSize / 8) + 1\n\tfor i := range bufDwords {\n\t\tinsns = append(insns,\n\t\t\tasm.StoreMem(asm.RFP, int16(i+1)*-8, asm.R0, asm.DWord),\n\t\t)\n\t}\n\n\tfor _, sampleMessage := range sampleMessages {\n\t\tif sampleMessage.discard {\n\t\t\ttb.Skip(\"discard is not supported on Windows\")\n\t\t}\n\n\t\tinsns = append(insns,\n\t\t\tasm.LoadMapPtr(asm.R1, events.FD()),\n\t\t\tasm.Mov.Reg(asm.R2, asm.RFP),\n\t\t\tasm.Add.Imm(asm.R2, -int32(8*bufDwords)),\n\t\t\tasm.Mov.Imm(asm.R3, int32(sampleMessage.size)),\n\t\t\tasm.Mov.Imm(asm.R4, sampleMessage.flags),\n\t\t\tasm.WindowsFnRingbufOutput.Call(),\n\t\t\tasm.JNE.Imm(asm.R0, 0, \"exit\"),\n\t\t)\n\t}\n\n\tinsns = append(insns,\n\t\tasm.Mov.Imm(asm.R0, int32(0)),\n\t\tasm.Return().WithSymbol(\"exit\"),\n\t)\n\n\tprog, err := ebpf.NewProgram(&ebpf.ProgramSpec{\n\t\tLicense:      \"MIT\",\n\t\tType:         ebpf.WindowsSample,\n\t\tInstructions: insns,\n\t})\n\tqt.Assert(tb, qt.IsNil(err))\n\ttb.Cleanup(func() {\n\t\tprog.Close()\n\t})\n\n\treturn prog, events\n}\n"
  },
  {
    "path": "ringbuf/reader.go",
    "content": "package ringbuf\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"sync\"\n\t\"time\"\n\t\"unsafe\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal/platform\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n)\n\nvar (\n\tErrClosed = os.ErrClosed\n\terrEOR    = errors.New(\"end of ring\")\n\terrBusy   = errors.New(\"sample not committed yet\")\n)\n\n// poller abstracts platform-specific event notification.\ntype poller interface {\n\tWait(deadline time.Time) error\n\tFlush() error\n\tClose() error\n}\n\n// eventRing abstracts platform-specific ring buffer memory access.\ntype eventRing interface {\n\tsize() int\n\tAvailableBytes() uint64\n\treadRecord(rec *Record) error\n\tClose() error\n}\n\n// ringbufHeader from 'struct bpf_ringbuf_hdr' in kernel/bpf/ringbuf.c\ntype ringbufHeader struct {\n\tLen uint32\n\t_   uint32 // pg_off, only used by kernel internals\n}\n\nconst ringbufHeaderSize = int(unsafe.Sizeof(ringbufHeader{}))\n\nfunc (rh *ringbufHeader) isBusy() bool {\n\treturn rh.Len&sys.BPF_RINGBUF_BUSY_BIT != 0\n}\n\nfunc (rh *ringbufHeader) isDiscard() bool {\n\treturn rh.Len&sys.BPF_RINGBUF_DISCARD_BIT != 0\n}\n\nfunc (rh *ringbufHeader) dataLen() int {\n\treturn int(rh.Len & ^uint32(sys.BPF_RINGBUF_BUSY_BIT|sys.BPF_RINGBUF_DISCARD_BIT))\n}\n\ntype Record struct {\n\tRawSample []byte\n\n\t// The minimum number of bytes remaining in the ring buffer after this Record has been read.\n\tRemaining int\n}\n\n// Reader allows reading bpf_ringbuf_output\n// from user space.\ntype Reader struct {\n\tpoller poller\n\n\t// mu protects read/write access to the Reader structure\n\tmu         sync.Mutex\n\tring       eventRing\n\thaveData   bool\n\tdeadline   time.Time\n\tbufferSize int\n\tpendingErr error\n}\n\n// NewReader creates a new BPF ringbuf reader.\nfunc NewReader(ringbufMap *ebpf.Map) (*Reader, error) {\n\tif ringbufMap.Type() != ebpf.RingBuf && ringbufMap.Type() != ebpf.WindowsRingBuf {\n\t\treturn nil, fmt.Errorf(\"invalid Map type: %s\", ringbufMap.Type())\n\t}\n\n\tmaxEntries := int(ringbufMap.MaxEntries())\n\tif maxEntries == 0 || (maxEntries&(maxEntries-1)) != 0 {\n\t\treturn nil, fmt.Errorf(\"ringbuffer map size %d is zero or not a power of two\", maxEntries)\n\t}\n\n\tpoller, err := newPoller(ringbufMap.FD())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tring, err := newRingBufEventRing(ringbufMap.FD(), maxEntries)\n\tif err != nil {\n\t\tpoller.Close()\n\t\treturn nil, fmt.Errorf(\"failed to create ringbuf ring: %w\", err)\n\t}\n\n\treturn &Reader{\n\t\tpoller:     poller,\n\t\tring:       ring,\n\t\tbufferSize: ring.size(),\n\t\t// On Windows, the wait handle is only set when the reader is created,\n\t\t// so we miss any wakeups that happened before.\n\t\t// Do an opportunistic read to get any pending samples.\n\t\thaveData: platform.IsWindows,\n\t}, nil\n}\n\n// Close frees resources used by the reader.\n//\n// It interrupts calls to Read.\nfunc (r *Reader) Close() error {\n\tif err := r.poller.Close(); err != nil {\n\t\tif errors.Is(err, os.ErrClosed) {\n\t\t\treturn nil\n\t\t}\n\t\treturn err\n\t}\n\n\t// Acquire the lock. This ensures that Read isn't running.\n\tr.mu.Lock()\n\tdefer r.mu.Unlock()\n\n\tvar err error\n\tif r.ring != nil {\n\t\terr = r.ring.Close()\n\t\tr.ring = nil\n\t}\n\n\treturn err\n}\n\n// SetDeadline controls how long Read and ReadInto will block waiting for samples.\n//\n// Passing a zero time.Time will remove the deadline.\nfunc (r *Reader) SetDeadline(t time.Time) {\n\tr.mu.Lock()\n\tdefer r.mu.Unlock()\n\n\tr.deadline = t\n}\n\n// Read the next record from the BPF ringbuf.\n//\n// Calling [Close] interrupts the method with [os.ErrClosed]. Calling [Flush]\n// makes it return all records currently in the ring buffer, followed by [ErrFlushed].\n//\n// Returns [os.ErrDeadlineExceeded] if a deadline was set and after all records\n// have been read from the ring.\n//\n// See [ReadInto] for a more efficient version of this method.\nfunc (r *Reader) Read() (Record, error) {\n\tvar rec Record\n\terr := r.ReadInto(&rec)\n\treturn rec, err\n}\n\n// ReadInto is like Read except that it allows reusing Record and associated buffers.\nfunc (r *Reader) ReadInto(rec *Record) error {\n\tr.mu.Lock()\n\tdefer r.mu.Unlock()\n\n\tif r.ring == nil {\n\t\treturn fmt.Errorf(\"ringbuffer: %w\", ErrClosed)\n\t}\n\n\tfor {\n\t\tif !r.haveData {\n\t\t\tif pe := r.pendingErr; pe != nil {\n\t\t\t\tr.pendingErr = nil\n\t\t\t\treturn pe\n\t\t\t}\n\n\t\t\terr := r.poller.Wait(r.deadline)\n\t\t\tif errors.Is(err, os.ErrDeadlineExceeded) || errors.Is(err, ErrFlushed) {\n\t\t\t\t// Ignoring this for reading a valid entry after timeout or flush.\n\t\t\t\t// This can occur if the producer submitted to the ring buffer\n\t\t\t\t// with BPF_RB_NO_WAKEUP.\n\t\t\t\tr.pendingErr = err\n\t\t\t} else if err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tr.haveData = true\n\t\t}\n\n\t\tfor {\n\t\t\terr := r.ring.readRecord(rec)\n\t\t\t// Not using errors.Is which is quite a bit slower\n\t\t\t// For a tight loop it might make a difference\n\t\t\tif err == errBusy {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif err == errEOR {\n\t\t\t\tr.haveData = false\n\t\t\t\tbreak\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t}\n}\n\n// BufferSize returns the size in bytes of the ring buffer\nfunc (r *Reader) BufferSize() int {\n\treturn r.bufferSize\n}\n\n// Flush unblocks Read/ReadInto and successive Read/ReadInto calls will return pending samples at this point,\n// until you receive a ErrFlushed error.\nfunc (r *Reader) Flush() error {\n\treturn r.poller.Flush()\n}\n\n// AvailableBytes returns the amount of data available to read in the ring buffer in bytes.\nfunc (r *Reader) AvailableBytes() int {\n\t// Don't need to acquire the lock here since the implementation of AvailableBytes\n\t// performs atomic loads on the producer and consumer positions.\n\treturn int(r.ring.AvailableBytes())\n}\n"
  },
  {
    "path": "ringbuf/reader_other.go",
    "content": "//go:build !windows\n\npackage ringbuf\n\nimport (\n\t\"time\"\n\n\t\"github.com/cilium/ebpf/internal/epoll\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\nvar ErrFlushed = epoll.ErrFlushed\n\nvar _ poller = (*epollPoller)(nil)\n\ntype epollPoller struct {\n\t*epoll.Poller\n\tevents []unix.EpollEvent\n}\n\nfunc newPoller(fd int) (*epollPoller, error) {\n\tep, err := epoll.New()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := ep.Add(fd, 0); err != nil {\n\t\tep.Close()\n\t\treturn nil, err\n\t}\n\n\treturn &epollPoller{\n\t\tPoller: ep,\n\t\tevents: make([]unix.EpollEvent, 1),\n\t}, nil\n}\n\n// Wait blocks until data is available or the deadline is reached.\n// Returns [os.ErrDeadlineExceeded] if a deadline was set and no wakeup was received.\n// Returns [ErrFlushed] if the ring buffer was flushed manually.\nfunc (p *epollPoller) Wait(deadline time.Time) error {\n\t_, err := p.Poller.Wait(p.events, deadline)\n\treturn err\n}\n"
  },
  {
    "path": "ringbuf/reader_test.go",
    "content": "package ringbuf\n\nimport (\n\t\"errors\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/go-quicktest/qt\"\n\t\"github.com/google/go-cmp/cmp\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/platform\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n\t\"github.com/cilium/ebpf/internal/testutils/testmain\"\n)\n\ntype sampleMessage struct {\n\tsize    int\n\tflags   int32\n\tdiscard bool\n}\n\nfunc TestMain(m *testing.M) {\n\ttestmain.Run(m)\n}\n\nfunc TestRingbufReader(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.8\", \"BPF ring buffer\")\n\n\treaderTests := []struct {\n\t\tname     string\n\t\tmessages []sampleMessage\n\t\twant     map[int][]byte\n\t}{\n\t\t{\n\t\t\tname:     \"send one short sample\",\n\t\t\tmessages: []sampleMessage{{size: 5}},\n\t\t\twant: map[int][]byte{\n\t\t\t\t5: {1, 2, 3, 4, 4},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:     \"send three short samples, the second is discarded\",\n\t\t\tmessages: []sampleMessage{{size: 5}, {size: 10, discard: true}, {size: 15}},\n\t\t\twant: map[int][]byte{\n\t\t\t\t5:  {1, 2, 3, 4, 4},\n\t\t\t\t15: {1, 2, 3, 4, 4, 3, 2, 1, 1, 2, 3, 4, 4, 3, 2},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:     \"send five samples, every even is discarded\",\n\t\t\tmessages: []sampleMessage{{size: 5}, {size: 10, discard: true}, {size: 15}, {size: 20, discard: true}, {size: 25}},\n\t\t\twant: map[int][]byte{\n\t\t\t\t5:  {1, 2, 3, 4, 4},\n\t\t\t\t15: {1, 2, 3, 4, 4, 3, 2, 1, 1, 2, 3, 4, 4, 3, 2},\n\t\t\t\t25: {1, 2, 3, 4, 4, 3, 2, 1, 1, 2, 3, 4, 4, 3, 2, 1, 1, 2, 3, 4, 4, 3, 2, 1, 1},\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range readerTests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tprog, events := mustOutputSamplesProg(t, tt.messages...)\n\n\t\t\trd, err := NewReader(events)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tdefer rd.Close()\n\n\t\t\tqt.Assert(t, qt.Equals(rd.AvailableBytes(), 0))\n\n\t\t\tif uint32(rd.BufferSize()) != events.MaxEntries() {\n\t\t\t\tt.Errorf(\"expected %d BufferSize, got %d\", events.MaxEntries(), rd.BufferSize())\n\t\t\t}\n\n\t\t\topts := &ebpf.RunOptions{\n\t\t\t\tData: internal.EmptyBPFContext,\n\t\t\t}\n\n\t\t\tif platform.IsWindows {\n\t\t\t\topts.Context = make([]byte, 32)\n\t\t\t}\n\n\t\t\tmustRun(t, prog)\n\n\t\t\tvar avail int\n\t\t\tfor _, m := range tt.messages {\n\t\t\t\tavail += ringbufHeaderSize + internal.Align(m.size, 8)\n\t\t\t}\n\t\t\tqt.Assert(t, qt.Equals(rd.AvailableBytes(), avail))\n\n\t\t\traw := make(map[int][]byte)\n\n\t\t\tfor len(raw) < len(tt.want) {\n\t\t\t\trecord, err := rd.Read()\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatal(\"Can't read samples:\", err)\n\t\t\t\t}\n\t\t\t\traw[len(record.RawSample)] = record.RawSample\n\t\t\t\tif len(raw) == len(tt.want) {\n\t\t\t\t\tif record.Remaining != 0 {\n\t\t\t\t\t\tt.Errorf(\"expected 0 Remaining, got %d\", record.Remaining)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif record.Remaining == 0 {\n\t\t\t\t\t\tt.Error(\"expected non-zero Remaining, got 0\")\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tt.want, raw); diff != \"\" {\n\t\t\t\tt.Errorf(\"Read samples mismatch (-want +got):\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestReaderBlocking(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.8\", \"BPF ring buffer\")\n\n\tprog, events := mustOutputSamplesProg(t, sampleMessage{size: 5, flags: 0})\n\n\tmustRun(t, prog)\n\n\trd, err := NewReader(events)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer rd.Close()\n\n\tif _, err := rd.Read(); err != nil {\n\t\tt.Fatal(\"Can't read first sample:\", err)\n\t}\n\n\terrs := make(chan error, 1)\n\tgo func() {\n\t\t_, err := rd.Read()\n\t\terrs <- err\n\t}()\n\n\tselect {\n\tcase err := <-errs:\n\t\tt.Fatal(\"Read returns error instead of blocking:\", err)\n\tcase <-time.After(100 * time.Millisecond):\n\t}\n\n\t// Close should interrupt blocking Read\n\tif err := rd.Close(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tselect {\n\tcase err := <-errs:\n\t\tif !errors.Is(err, ErrClosed) {\n\t\t\tt.Fatal(\"Expected os.ErrClosed from interrupted Read, got:\", err)\n\t\t}\n\tcase <-time.After(time.Second):\n\t\tt.Fatal(\"Close doesn't interrupt Read\")\n\t}\n\n\t// And we should be able to call it multiple times\n\tif err := rd.Close(); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tif _, err := rd.Read(); !errors.Is(err, ErrClosed) {\n\t\tt.Fatal(\"Second Read on a closed RingbufReader doesn't return ErrClosed\")\n\t}\n}\n\nfunc TestReaderNoWakeup(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.8\", \"BPF ring buffer\")\n\n\tprog, events := mustOutputSamplesProg(t,\n\t\tsampleMessage{size: 5, flags: sys.BPF_RB_NO_WAKEUP}, // Read after timeout\n\t\tsampleMessage{size: 7, flags: sys.BPF_RB_NO_WAKEUP}, // Read won't block\n\t)\n\n\trd, err := NewReader(events)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer rd.Close()\n\n\tqt.Assert(t, qt.Equals(rd.AvailableBytes(), 0))\n\n\tmustRun(t, prog)\n\n\tqt.Assert(t, qt.Equals(rd.AvailableBytes(), 2*16))\n\n\trd.SetDeadline(time.Now())\n\trecord, err := rd.Read()\n\n\tif err != nil {\n\t\tt.Error(\"Expected no error from first Read, got:\", err)\n\t}\n\tif len(record.RawSample) != 5 {\n\t\tt.Errorf(\"Expected to read 5 bytes but got %d\", len(record.RawSample))\n\t}\n\n\tqt.Assert(t, qt.Equals(rd.AvailableBytes(), 1*16))\n\n\trecord, err = rd.Read()\n\n\tif err != nil {\n\t\tt.Error(\"Expected no error from second Read, got:\", err)\n\t}\n\tif len(record.RawSample) != 7 {\n\t\tt.Errorf(\"Expected to read 7 bytes but got %d\", len(record.RawSample))\n\t}\n\n\tqt.Assert(t, qt.Equals(rd.AvailableBytes(), 0))\n\n\t_, err = rd.Read()\n\tif !errors.Is(err, os.ErrDeadlineExceeded) {\n\t\tt.Errorf(\"Expected os.ErrDeadlineExceeded from third Read but got %v\", err)\n\t}\n}\n\nfunc TestReaderFlushPendingEvents(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.8\", \"BPF ring buffer\")\n\n\tprog, events := mustOutputSamplesProg(t,\n\t\tsampleMessage{size: 5, flags: sys.BPF_RB_NO_WAKEUP}, // Read after Flush\n\t\tsampleMessage{size: 7, flags: sys.BPF_RB_NO_WAKEUP}, // Read won't block\n\t)\n\n\trd, err := NewReader(events)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer rd.Close()\n\n\tmustRun(t, prog)\n\n\twait := make(chan *Record)\n\tgo func() {\n\t\twait <- nil\n\t\trecord, err := rd.Read()\n\t\tqt.Assert(t, qt.IsNil(err))\n\t\twait <- &record\n\t}()\n\n\t<-wait\n\ttime.Sleep(10 * time.Millisecond)\n\terr = rd.Flush()\n\tqt.Assert(t, qt.IsNil(err))\n\n\twaitRec := <-wait\n\tif waitRec == nil {\n\t\tt.Error(\"Expected to read record but got nil\")\n\t}\n\tif waitRec != nil && len(waitRec.RawSample) != 5 {\n\t\tt.Errorf(\"Expected to read 5 bytes but got %d\", len(waitRec.RawSample))\n\t}\n\n\trecord, err := rd.Read()\n\n\tif err != nil {\n\t\tt.Error(\"Expected no error from second Read, got:\", err)\n\t}\n\tif len(record.RawSample) != 7 {\n\t\tt.Errorf(\"Expected to read 7 bytes but got %d\", len(record.RawSample))\n\t}\n\n\t_, err = rd.Read()\n\tif !errors.Is(err, ErrFlushed) {\n\t\tt.Errorf(\"Expected ErrFlushed from third Read but got %v\", err)\n\t}\n}\n\nfunc TestReaderSetDeadline(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.8\", \"BPF ring buffer\")\n\n\t_, events := mustOutputSamplesProg(t, sampleMessage{size: 5, flags: 0})\n\trd, err := NewReader(events)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer rd.Close()\n\n\trd.SetDeadline(time.Now().Add(-time.Second))\n\tif _, err := rd.Read(); !errors.Is(err, os.ErrDeadlineExceeded) {\n\t\tt.Error(\"Expected os.ErrDeadlineExceeded from first Read, got:\", err)\n\t}\n\tif _, err := rd.Read(); !errors.Is(err, os.ErrDeadlineExceeded) {\n\t\tt.Error(\"Expected os.ErrDeadlineExceeded from second Read, got:\", err)\n\t}\n}\n\nfunc TestReadAfterClose(t *testing.T) {\n\ttestutils.SkipOnOldKernel(t, \"5.8\", \"BPF ring buffer\")\n\n\tprog, events := mustOutputSamplesProg(t,\n\t\tsampleMessage{size: 5, flags: 0},\n\t\tsampleMessage{size: 5, flags: 0},\n\t)\n\n\tmustRun(t, prog)\n\n\trd, err := NewReader(events)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t_, err = rd.Read()\n\tif err != nil {\n\t\tt.Error(\"Expected no error after first Read, got:\", err)\n\t}\n\n\terr = rd.Close()\n\tif err != nil {\n\t\tt.Error(\"Expected no error from Close, got: \", err)\n\t}\n\n\t_, err = rd.Read()\n\tif err == nil || !errors.Is(err, ErrClosed) {\n\t\tt.Error(\"Expected ErrClosed but got: \", err)\n\t}\n}\n\nfunc BenchmarkReader(b *testing.B) {\n\ttestutils.SkipOnOldKernel(b, \"5.8\", \"BPF ring buffer\")\n\n\treaderBenchmarks := []struct {\n\t\tname  string\n\t\tflags int32\n\t}{\n\t\t{\n\t\t\tname: \"normal epoll with timeout -1\",\n\t\t},\n\t}\n\n\tfor _, bm := range readerBenchmarks {\n\t\tb.Run(bm.name, func(b *testing.B) {\n\t\t\tprog, events := mustOutputSamplesProg(b, sampleMessage{size: 80, flags: bm.flags})\n\n\t\t\trd, err := NewReader(events)\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t\tdefer rd.Close()\n\n\t\t\tb.ReportAllocs()\n\n\t\t\tfor b.Loop() {\n\t\t\t\tb.StopTimer()\n\t\t\t\tmustRun(b, prog)\n\t\t\t\tb.StartTimer()\n\n\t\t\t\t_, err = rd.Read()\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatal(\"Can't read samples:\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkReadInto(b *testing.B) {\n\ttestutils.SkipOnOldKernel(b, \"5.8\", \"BPF ring buffer\")\n\n\tprog, events := mustOutputSamplesProg(b, sampleMessage{size: 80, flags: 0})\n\n\trd, err := NewReader(events)\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\tdefer rd.Close()\n\n\tb.ReportAllocs()\n\n\tvar rec Record\n\tfor b.Loop() {\n\t\tb.StopTimer()\n\t\tmustRun(b, prog)\n\t\tb.StartTimer()\n\n\t\tif err := rd.ReadInto(&rec); err != nil {\n\t\t\tb.Fatal(\"Can't read samples:\", err)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "ringbuf/reader_windows.go",
    "content": "package ringbuf\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"golang.org/x/sys/windows\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/efw\"\n)\n\nvar ErrFlushed = errors.New(\"ring buffer flushed\")\n\nvar _ poller = (*windowsPoller)(nil)\n\ntype windowsPoller struct {\n\tclosed      atomic.Bool\n\thandle      windows.Handle\n\tflushHandle windows.Handle\n\thandles     []windows.Handle\n}\n\nfunc newPoller(fd int) (*windowsPoller, error) {\n\thandle, err := windows.CreateEvent(nil, 0, 0, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tflushHandle, err := windows.CreateEvent(nil, 0, 0, nil)\n\tif err != nil {\n\t\twindows.CloseHandle(handle)\n\t\treturn nil, err\n\t}\n\n\tif err := efw.EbpfMapSetWaitHandle(fd, 0, handle); err != nil {\n\t\twindows.CloseHandle(handle)\n\t\twindows.CloseHandle(flushHandle)\n\t\treturn nil, err\n\t}\n\n\treturn &windowsPoller{\n\t\thandle:      handle,\n\t\tflushHandle: flushHandle,\n\t\thandles:     []windows.Handle{handle, flushHandle},\n\t}, nil\n}\n\n// Wait blocks until data is available or the deadline is reached.\n// Returns [os.ErrDeadlineExceeded] if a deadline was set and no wakeup was received.\n// Returns [ErrFlushed] if the ring buffer was flushed manually.\n// Returns [os.ErrClosed] if the poller was closed.\nfunc (p *windowsPoller) Wait(deadline time.Time) error {\n\tif p.closed.Load() {\n\t\treturn os.ErrClosed\n\t}\n\n\ttimeout := uint32(windows.INFINITE)\n\tif !deadline.IsZero() {\n\t\ttimeout = uint32(internal.Between(time.Until(deadline).Milliseconds(), 0, windows.INFINITE-1))\n\t}\n\n\t// Wait for either the ring buffer handle or the flush handle to be signaled\n\tresult, err := windows.WaitForMultipleObjects(p.handles, false, timeout)\n\tswitch result {\n\tcase windows.WAIT_OBJECT_0:\n\t\t// Ring buffer event\n\t\treturn nil\n\tcase windows.WAIT_OBJECT_0 + 1:\n\t\tif p.closed.Load() {\n\t\t\treturn os.ErrClosed\n\t\t}\n\t\t// Flush event\n\t\treturn ErrFlushed\n\tcase uint32(windows.WAIT_TIMEOUT):\n\t\treturn os.ErrDeadlineExceeded\n\tcase windows.WAIT_FAILED:\n\t\treturn err\n\tdefault:\n\t\treturn fmt.Errorf(\"unexpected wait result 0x%x: %w\", result, err)\n\t}\n}\n\n// Flush interrupts [Wait] with [ErrFlushed].\nfunc (p *windowsPoller) Flush() error {\n\t// Signal the handle to wake up any waiting threads\n\tif err := windows.SetEvent(p.flushHandle); err != nil {\n\t\tif errors.Is(err, windows.ERROR_INVALID_HANDLE) {\n\t\t\treturn os.ErrClosed\n\t\t}\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (p *windowsPoller) Close() error {\n\tp.closed.Store(true)\n\n\tif err := p.Flush(); err != nil {\n\t\treturn err\n\t}\n\n\treturn errors.Join(windows.CloseHandle(p.handle), windows.CloseHandle(p.flushHandle))\n}\n"
  },
  {
    "path": "ringbuf/ring.go",
    "content": "package ringbuf\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"sync/atomic\"\n\t\"unsafe\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n)\n\ntype ringReader struct {\n\t// These point into mmap'ed memory and must be accessed atomically.\n\tprod_pos, cons_pos *uintptr\n\tmask               uintptr\n\tring               []byte\n}\n\nfunc newRingReader(cons_ptr, prod_ptr *uintptr, ring []byte) *ringReader {\n\treturn &ringReader{\n\t\tprod_pos: prod_ptr,\n\t\tcons_pos: cons_ptr,\n\t\t// cap is always a power of two\n\t\tmask: uintptr(cap(ring)/2 - 1),\n\t\tring: ring,\n\t}\n}\n\n// To be able to wrap around data, data pages in ring buffers are mapped twice in\n// a single contiguous virtual region.\n// Therefore the returned usable size is half the size of the mmaped region.\nfunc (rr *ringReader) size() int {\n\treturn cap(rr.ring) / 2\n}\n\n// The amount of data available to read in the ring buffer.\nfunc (rr *ringReader) AvailableBytes() uint64 {\n\tprod := atomic.LoadUintptr(rr.prod_pos)\n\tcons := atomic.LoadUintptr(rr.cons_pos)\n\treturn uint64(prod - cons)\n}\n\n// Read a record from an event ring.\nfunc (rr *ringReader) readRecord(rec *Record) error {\n\tprod := atomic.LoadUintptr(rr.prod_pos)\n\tcons := atomic.LoadUintptr(rr.cons_pos)\n\n\tfor {\n\t\tif remaining := prod - cons; remaining == 0 {\n\t\t\treturn errEOR\n\t\t} else if remaining < sys.BPF_RINGBUF_HDR_SZ {\n\t\t\treturn fmt.Errorf(\"read record header: %w\", io.ErrUnexpectedEOF)\n\t\t}\n\n\t\t// read the len field of the header atomically to ensure a happens before\n\t\t// relationship with the xchg in the kernel. Without this we may see len\n\t\t// without BPF_RINGBUF_BUSY_BIT before the written data is visible.\n\t\t// See https://github.com/torvalds/linux/blob/v6.8/kernel/bpf/ringbuf.c#L484\n\t\tstart := cons & rr.mask\n\t\tlen := atomic.LoadUint32((*uint32)((unsafe.Pointer)(&rr.ring[start])))\n\t\theader := ringbufHeader{Len: len}\n\n\t\tif header.isBusy() {\n\t\t\t// the next sample in the ring is not committed yet so we\n\t\t\t// exit without storing the reader/consumer position\n\t\t\t// and start again from the same position.\n\t\t\treturn errBusy\n\t\t}\n\n\t\tcons += sys.BPF_RINGBUF_HDR_SZ\n\n\t\t// Data is always padded to 8 byte alignment.\n\t\tdataLenAligned := uintptr(internal.Align(header.dataLen(), 8))\n\t\tif remaining := prod - cons; remaining < dataLenAligned {\n\t\t\treturn fmt.Errorf(\"read sample data: %w\", io.ErrUnexpectedEOF)\n\t\t}\n\n\t\tstart = cons & rr.mask\n\t\tcons += dataLenAligned\n\n\t\tif header.isDiscard() {\n\t\t\t// when the record header indicates that the data should be\n\t\t\t// discarded, we skip it by just updating the consumer position\n\t\t\t// to the next record.\n\t\t\tatomic.StoreUintptr(rr.cons_pos, cons)\n\t\t\tcontinue\n\t\t}\n\n\t\tif n := header.dataLen(); cap(rec.RawSample) < n {\n\t\t\trec.RawSample = make([]byte, n)\n\t\t} else {\n\t\t\trec.RawSample = rec.RawSample[:n]\n\t\t}\n\n\t\tcopy(rec.RawSample, rr.ring[start:])\n\t\trec.Remaining = int(prod - cons)\n\t\tatomic.StoreUintptr(rr.cons_pos, cons)\n\t\treturn nil\n\t}\n}\n"
  },
  {
    "path": "ringbuf/ring_other.go",
    "content": "//go:build !windows\n\npackage ringbuf\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"runtime\"\n\t\"unsafe\"\n\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\nvar _ eventRing = (*mmapEventRing)(nil)\n\ntype mmapEventRing struct {\n\tprod []byte\n\tcons []byte\n\t*ringReader\n\tcleanup runtime.Cleanup\n}\n\nfunc newRingBufEventRing(mapFD, size int) (*mmapEventRing, error) {\n\tcons, err := unix.Mmap(mapFD, 0, os.Getpagesize(), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"mmap consumer page: %w\", err)\n\t}\n\n\tprod, err := unix.Mmap(mapFD, (int64)(os.Getpagesize()), os.Getpagesize()+2*size, unix.PROT_READ, unix.MAP_SHARED)\n\tif err != nil {\n\t\t_ = unix.Munmap(cons)\n\t\treturn nil, fmt.Errorf(\"mmap data pages: %w\", err)\n\t}\n\n\tcons_pos := (*uintptr)(unsafe.Pointer(&cons[0]))\n\tprod_pos := (*uintptr)(unsafe.Pointer(&prod[0]))\n\n\tring := &mmapEventRing{\n\t\tprod:       prod,\n\t\tcons:       cons,\n\t\tringReader: newRingReader(cons_pos, prod_pos, prod[os.Getpagesize():]),\n\t}\n\tring.cleanup = runtime.AddCleanup(ring, func(*byte) {\n\t\t_ = unix.Munmap(prod)\n\t\t_ = unix.Munmap(cons)\n\t}, nil)\n\n\treturn ring, nil\n}\n\nfunc (ring *mmapEventRing) Close() error {\n\tring.cleanup.Stop()\n\n\tprod, cons := ring.prod, ring.cons\n\tring.prod, ring.cons = nil, nil\n\n\treturn errors.Join(\n\t\tunix.Munmap(prod),\n\t\tunix.Munmap(cons),\n\t)\n}\n"
  },
  {
    "path": "ringbuf/ring_windows.go",
    "content": "package ringbuf\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"runtime\"\n\t\"unsafe\"\n\n\t\"github.com/cilium/ebpf/internal/efw\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n)\n\nvar _ eventRing = (*windowsEventRing)(nil)\n\ntype windowsEventRing struct {\n\tmapFd            *sys.FD\n\tcons, prod, data *uint8\n\t*ringReader\n\n\tcleanup runtime.Cleanup\n}\n\nfunc newRingBufEventRing(mapFD, size int) (*windowsEventRing, error) {\n\tdupFd, err := efw.EbpfDuplicateFd(mapFD)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"duplicate map fd: %w\", err)\n\t}\n\n\tfd, err := sys.NewFD(dupFd)\n\tif err != nil {\n\t\t_ = efw.EbpfCloseFd(dupFd)\n\t\treturn nil, err\n\t}\n\n\tconsPtr, prodPtr, dataPtr, dataLen, err := efw.EbpfRingBufferMapMapBuffer(dupFd)\n\tif err != nil {\n\t\t_ = fd.Close()\n\t\treturn nil, fmt.Errorf(\"map consumer page: %w\", err)\n\t}\n\n\tif dataLen != efw.Size(size) {\n\t\t_ = fd.Close()\n\t\treturn nil, fmt.Errorf(\"map data length mismatch: %d != %d\", dataLen, size)\n\t}\n\n\t// consPtr and prodPtr are guaranteed to be page size aligned.\n\tconsPos := (*uintptr)(unsafe.Pointer(consPtr))\n\tprodPos := (*uintptr)(unsafe.Pointer(prodPtr))\n\tdata := unsafe.Slice(dataPtr, dataLen*2)\n\n\tring := &windowsEventRing{\n\t\tmapFd:      fd,\n\t\tcons:       consPtr,\n\t\tprod:       prodPtr,\n\t\tdata:       dataPtr,\n\t\tringReader: newRingReader(consPos, prodPos, data),\n\t}\n\tring.cleanup = runtime.AddCleanup(ring, func(*byte) {\n\t\tefw.EbpfRingBufferMapUnmapBuffer(fd.Int(), consPtr, prodPtr, dataPtr)\n\t}, nil)\n\n\treturn ring, nil\n}\n\nfunc (ring *windowsEventRing) Close() error {\n\tring.cleanup.Stop()\n\n\treturn errors.Join(\n\t\tefw.EbpfRingBufferMapUnmapBuffer(ring.mapFd.Int(), ring.cons, ring.prod, ring.data),\n\t\tring.mapFd.Close(),\n\t)\n}\n"
  },
  {
    "path": "rlimit/doc.go",
    "content": "// Package rlimit allows raising RLIMIT_MEMLOCK if necessary for the use of BPF.\npackage rlimit\n"
  },
  {
    "path": "rlimit/rlimit_linux.go",
    "content": "package rlimit\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"sync\"\n\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\nvar (\n\tunsupportedMemcgAccounting = &internal.UnsupportedFeatureError{\n\t\tMinimumVersion: internal.Version{5, 11, 0},\n\t\tName:           \"memcg-based accounting for BPF memory\",\n\t}\n\thaveMemcgAccounting error\n\n\trlimitMu sync.Mutex\n)\n\nfunc init() {\n\t// We have to run this feature test at init, since it relies on changing\n\t// RLIMIT_MEMLOCK. Doing so is not safe in a concurrent program. Instead,\n\t// we rely on the initialization order guaranteed by the Go runtime to\n\t// execute the test in a safe environment:\n\t//\n\t//    the invocation of init functions happens in a single goroutine,\n\t//    sequentially, one package at a time.\n\t//\n\t// This is also the reason why RemoveMemlock is in its own package:\n\t// we only want to run the initializer if RemoveMemlock is called\n\t// from somewhere.\n\thaveMemcgAccounting = detectMemcgAccounting()\n}\n\nfunc detectMemcgAccounting() error {\n\t// Retrieve the original limit to prevent lowering Max, since\n\t// doing so is a permanent operation when running unprivileged.\n\tvar oldLimit unix.Rlimit\n\tif err := unix.Prlimit(0, unix.RLIMIT_MEMLOCK, nil, &oldLimit); err != nil {\n\t\treturn fmt.Errorf(\"getting original memlock rlimit: %s\", err)\n\t}\n\n\t// Drop the current limit to zero, maintaining the old Max value.\n\t// This is always permitted by the kernel for unprivileged users.\n\t// Retrieve a new copy of the old limit tuple to minimize the chances\n\t// of failing the restore operation below.\n\tzeroLimit := unix.Rlimit{Cur: 0, Max: oldLimit.Max}\n\tif err := unix.Prlimit(0, unix.RLIMIT_MEMLOCK, &zeroLimit, &oldLimit); err != nil {\n\t\treturn fmt.Errorf(\"lowering memlock rlimit: %s\", err)\n\t}\n\n\tattr := sys.MapCreateAttr{\n\t\tMapType:    2, /* Array */\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: 1,\n\t}\n\n\t// Creating a map allocates shared (and locked) memory that counts against\n\t// the rlimit on pre-5.11 kernels, but against the memory cgroup budget on\n\t// kernels 5.11 and over. If this call succeeds with the process' memlock\n\t// rlimit set to 0, we can reasonably assume memcg accounting is supported.\n\tfd, mapErr := sys.MapCreate(&attr)\n\n\t// Restore old limits regardless of what happened.\n\tif err := unix.Prlimit(0, unix.RLIMIT_MEMLOCK, &oldLimit, nil); err != nil {\n\t\treturn fmt.Errorf(\"restoring old memlock rlimit: %s\", err)\n\t}\n\n\t// Map creation successful, memcg accounting supported.\n\tif mapErr == nil {\n\t\tfd.Close()\n\t\treturn nil\n\t}\n\n\t// EPERM shows up when map creation would exceed the memory budget.\n\tif errors.Is(mapErr, unix.EPERM) {\n\t\treturn unsupportedMemcgAccounting\n\t}\n\n\t// This shouldn't happen really.\n\treturn fmt.Errorf(\"unexpected error detecting memory cgroup accounting: %s\", mapErr)\n}\n\n// RemoveMemlock removes the limit on the amount of memory the current\n// process can lock into RAM, if necessary.\n//\n// This is not required to load eBPF resources on kernel versions 5.11+\n// due to the introduction of cgroup-based memory accounting. On such kernels\n// the function is a no-op.\n//\n// Since the function may change global per-process limits it should be invoked\n// at program start up, in main() or init().\n//\n// This function exists as a convenience and should only be used when\n// permanently raising RLIMIT_MEMLOCK to infinite is appropriate. Consider\n// invoking prlimit(2) directly with a more reasonable limit if desired.\n//\n// Requires CAP_SYS_RESOURCE on kernels < 5.11.\nfunc RemoveMemlock() error {\n\tif haveMemcgAccounting == nil {\n\t\treturn nil\n\t}\n\n\tif !errors.Is(haveMemcgAccounting, unsupportedMemcgAccounting) {\n\t\treturn haveMemcgAccounting\n\t}\n\n\trlimitMu.Lock()\n\tdefer rlimitMu.Unlock()\n\n\t// pid 0 affects the current process. Requires CAP_SYS_RESOURCE.\n\tnewLimit := unix.Rlimit{Cur: unix.RLIM_INFINITY, Max: unix.RLIM_INFINITY}\n\tif err := unix.Prlimit(0, unix.RLIMIT_MEMLOCK, &newLimit, nil); err != nil {\n\t\treturn fmt.Errorf(\"failed to set memlock rlimit: %w\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "rlimit/rlimit_linux_test.go",
    "content": "package rlimit\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf/internal/linux\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n\n\t\"github.com/go-quicktest/qt\"\n)\n\nfunc TestRemoveMemlock(t *testing.T) {\n\tvar before unix.Rlimit\n\tqt.Assert(t, qt.IsNil(unix.Prlimit(0, unix.RLIMIT_MEMLOCK, nil, &before)))\n\n\terr := RemoveMemlock()\n\tqt.Assert(t, qt.IsNil(err))\n\n\tvar after unix.Rlimit\n\tqt.Assert(t, qt.IsNil(unix.Prlimit(0, unix.RLIMIT_MEMLOCK, nil, &after)))\n\n\t// We can't use testutils here due to an import cycle.\n\tversion, err := linux.KernelVersion()\n\tqt.Assert(t, qt.IsNil(err))\n\n\tif version.Less(unsupportedMemcgAccounting.MinimumVersion) {\n\t\tqt.Assert(t, qt.Equals(after.Cur, unix.RLIM_INFINITY), qt.Commentf(\"cur should be INFINITY\"))\n\t\tqt.Assert(t, qt.Equals(after.Max, unix.RLIM_INFINITY), qt.Commentf(\"max should be INFINITY\"))\n\t} else {\n\t\tqt.Assert(t, qt.Equals(after.Cur, before.Cur), qt.Commentf(\"cur should be unchanged\"))\n\t\tqt.Assert(t, qt.Equals(after.Max, before.Max), qt.Commentf(\"max should be unchanged\"))\n\t}\n}\n"
  },
  {
    "path": "rlimit/rlimit_other.go",
    "content": "//go:build !linux\n\npackage rlimit\n\n// RemoveMemlock is a no-op on platforms other than Linux.\nfunc RemoveMemlock() error { return nil }\n"
  },
  {
    "path": "scripts/update-efw-deps.sh",
    "content": "#!/bin/bash\n\nset -euo pipefail\n\n# Extract EFW version from CI workflow file\nefw_version=$(awk -F': ' '/CI_MAX_EFW_VERSION:/ {gsub(/['\\''\"]/, \"\", $2); print $2}' .github/workflows/ci.yml)\n\nif [ -z \"$efw_version\" ]; then\n\techo \"Error: Could not extract CI_MAX_EFW_VERSION from .github/workflows/ci.yml\" >&2\n\texit 1\nfi\n\necho \"Using EFW version: $efw_version\"\n\ntmp=$(mktemp -d)\n\ncleanup() {\n\trm -r \"$tmp\"\n}\n\ntrap cleanup EXIT\n\n# Download and process ebpf_structs.h\ncurl -fL \"https://github.com/microsoft/ebpf-for-windows/raw/refs/tags/Release-v${efw_version}/include/ebpf_structs.h\" -o \"$tmp/ebpf_structs.h\"\n\"./internal/cmd/genwinfunctions.awk\" \"$tmp/ebpf_structs.h\" | gofmt > \"./asm/func_win.go\"\n"
  },
  {
    "path": "scripts/update-kernel-deps.sh",
    "content": "#!/bin/bash\n\nset -euo pipefail\n\n# Extract kernel version from CI workflow file\nkernel_version=$(awk -F': ' '/CI_MAX_KERNEL_VERSION:/ {gsub(/['\\''\"]/, \"\", $2); print $2}' .github/workflows/ci.yml)\n\nif [ -z \"$kernel_version\" ]; then\n\techo \"Error: Could not extract CI_MAX_KERNEL_VERSION from .github/workflows/ci.yml\" >&2\n\texit 1\nfi\n\necho \"Using kernel version: $kernel_version\"\n\ntmp=$(mktemp -d)\n\ncleanup() {\n\trm -r \"$tmp\"\n}\n\ntrap cleanup EXIT\n\n# Download and process libbpf.c\n# Truncate .0 patch versions (e.g., 6.16.0 -> 6.16, but leave 7.0 as 7.0)\nkernel_version_for_url=\"$kernel_version\"\nif [[ $kernel_version =~ ^([0-9]+\\.[0-9]+)\\.0$ ]]; then\n\tkernel_version_for_url=\"${BASH_REMATCH[1]}\"\nfi\ncurl -fL \"https://raw.githubusercontent.com/gregkh/linux/refs/tags/v$kernel_version_for_url/tools/lib/bpf/libbpf.c\" -o \"$tmp/libbpf.c\"\n\"./internal/cmd/gensections.awk\" \"$tmp/libbpf.c\" | gofmt > \"./elf_sections.go\"\n\n# Download and process vmlinux and btf_testmod\ngo tool crane export \"ghcr.io/cilium/ci-kernels:$kernel_version\" | tar -x -C \"$tmp\"\n\nextract-vmlinux \"$tmp/boot/vmlinuz\" > \"$tmp/vmlinux\"\n\nobjcopy --dump-section .BTF=/dev/stdout \"$tmp/vmlinux\" /dev/null | gzip > \"btf/testdata/vmlinux.btf.gz\"\nfind \"$tmp/lib/modules\" -type f -name bpf_testmod.ko -exec objcopy --dump-section .BTF=\"btf/testdata/btf_testmod.btf\" {} /dev/null \\;\nfind \"$tmp/lib/modules\" -type f -name bpf_testmod.ko -exec objcopy --dump-section .BTF.base=\"btf/testdata/btf_testmod.btf.base\" {} /dev/null \\;\n"
  },
  {
    "path": "scripts/windows/README.md",
    "content": "# Windows Development Setup\n\nYou will need access to a Windows environment to work on ebpf-go for Windows.\nThis repository contains a script which (mostly) automatically installs a Windows VM.\nIt then proceeds to install dependencies necessary to compile and install eBPF for Windows.\n\n```shell\n./setup.sh path-to-windows.iso\n```\n\nObtain the ISO by choosing \"Download Windows 11 Disk Image (ISO)\" on the\n[download page](https://www.microsoft.com/en-gb/software-download/windows11/)\nand then following the instructions.\n__Choose \"English (United States)\" as product language for a fully automated installation.__\n\n## SSH\n\nThe setup script adds a public key from `~/.ssh`,\nyou should be able to simply ssh into the VM by executing `ssh $IP`.\n\n## Requirements\n\n* Only tested with Windows 11\n* `libvirt` using qemu backend\n* `genisoimage`\n* `curl`\n* `envsubst`\n* `fzf` (optional, to select an ssh key)\n"
  },
  {
    "path": "scripts/windows/Setup.ps1",
    "content": "# Configure a fresh installation of Windows via SSH.\n\nparam (\n  [switch] $RunOnce = $false\n)\n\nif ($RunOnce) {\n  # Visual Studio really doesn't seem to like being installed via SSH, so\n  # we invoke from a RunOnce script.\n  Invoke-WebRequest 'https://raw.githubusercontent.com/microsoft/ebpf-for-windows/main/scripts/Setup-DevEnv.ps1' -OutFile $env:TEMP\\Setup-DevEnv.ps1\n  &\"$env:TEMP\\Setup-DevEnv.ps1\"\n\n  # sshd needs to be restarted to pick up new environment variables.\n  Restart-Service sshd\n\n  return\n}\n\n# Enable Developer Mode (so that symlinks work)\n# See https://stackoverflow.com/a/40033638\n$RegistryKeyPath = \"HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock\"\nNew-Item -Path $RegistryKeyPath -ItemType Directory -Force\nNew-ItemProperty -Path $RegistryKeyPath -Name AllowDevelopmentWithoutDevLicense -PropertyType DWORD -Value 1 -Force\n\n# Ensure we have a PROFILE.\n#\n# This also allows chocolatey to add its hooks.\nif (!(Test-Path -Path $PROFILE)) {\n  New-Item -ItemType File -Path $PROFILE -Force\n}\n\n# Add VS tools to PATH\n$addVsToolsToPath = @'\n# Add VS to PATH\nfunction Import-VsEnv {\n  $vswherePath = \"${env:ProgramFiles(x86)}\\Microsoft Visual Studio\\Installer\\vswhere.exe\"\n  $vspath = & $vswherePath -property installationPath\n  $vsDevShell = \"${vspath}\\Common7\\Tools\\Launch-VsDevShell.ps1\"\n  & $vsDevShell -SkipAutomaticLocation\n}\n'@\n\nif (-not (Get-Content $PROFILE | Select-String \"Add VS to PATH\")) {\n    $addVsToolsToPath | Add-Content -Path $PROFILE\n}\n\n# Enable git symlink support globally.\n$gitConfig = \"${HOME}/.gitconfig\"\nif (!(Test-Path -Path $gitConfig)) {\n  New-Item -ItemType File -Path $gitConfig -Force\n}\n\nif (-not (Get-Content $gitConfig | Select-String \"symlinks = true\")) {\n  @'\n[core]\nsymlinks = true\n'@ | Add-Content -Path $gitConfig\n}\n\n# Install winget version which supports configure -f\n# if ([version]($(winget --version).substring(1)) -lt [version]'1.6.0') {\n#   echo \"Updating winget\"\n#   # From https://andrewstaylor.com/2023/11/28/winget-powershell-module/\n#   Get-PackageProvider NuGet -ForceBootstrap | Out-Null\n#   install-module microsoft.winget.client -Force -AllowClobber\n#   import-module microsoft.winget.client\n#   repair-wingetpackagemanager -Force -Latest -AllUsers\n# }\n\necho \"Scheduling installation of eBPF for Windows dependencies for next reboot.\"\nSet-ItemProperty \"HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce\" -Name 'InstallEFWDependencies' -Value \"powershell.exe -command `\"start -verb runas powershell.exe -argumentlist \\`\"-file $PSCommandPath -RunOnce\\`\"\"\n\necho \"Rebooting.\"\nRestart-Computer -Force\n"
  },
  {
    "path": "scripts/windows/autounattend.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<unattend xmlns=\"urn:schemas-microsoft-com:unattend\" xmlns:wcm=\"http://schemas.microsoft.com/WMIConfig/2002/State\">\n\t<!--https://schneegans.de/windows/unattend-generator/?LanguageMode=Unattended&UILanguage=en-US&Locale=en-US&Keyboard=00000409&GeoLocation=244&ProcessorArchitecture=amd64&ComputerNameMode=Random&CompactOsMode=Default&TimeZoneMode=Implicit&PartitionMode=Unattended&PartitionLayout=GPT&EspSize=300&RecoveryMode=Partition&RecoverySize=1000&DiskAssertionMode=Skip&WindowsEditionMode=Generic&WindowsEdition=pro_n&UserAccountMode=Unattended&AccountName0=%24USER&AccountDisplayName0=&AccountPassword0=&AccountGroup0=Administrators&AccountName1=&AccountName2=&AccountName3=&AutoLogonMode=Own&PasswordExpirationMode=Unlimited&LockoutMode=Disabled&HideFiles=Hidden&TaskbarSearch=Box&TaskbarIconsMode=Default&StartTilesMode=Default&StartPinsMode=Default&DisableDefender=true&DisableSystemRestore=true&AllowPowerShellScripts=true&DisableAppSuggestions=true&PreventDeviceEncryption=true&HideEdgeFre=true&EffectsMode=Default&DesktopIconsMode=Default&VirtIoGuestTools=true&WifiMode=Skip&ExpressSettings=DisableAll&KeysMode=Skip&ColorMode=Default&WallpaperMode=Default&SystemScript0=bcdedit.exe+-set+TESTSIGNING+ON%3B%0D%0A&SystemScriptType0=Ps1&FirstLogonScript0=%23+https%3A%2F%2Flearn.microsoft.com%2Fen-us%2Fwindows-server%2Fadministration%2Fopenssh%2Fopenssh_server_configuration%0D%0AAdd-WindowsCapability+-Online+-Name+OpenSSH.Server%7E%7E%7E%7E0.0.1.0%3B%0D%0AStart-Service+sshd%3B%0D%0ASet-Service+-Name+sshd+-StartupType+%27Automatic%27%3B%0D%0ANew-ItemProperty+-Path+%22HKLM%3A%5CSOFTWARE%5COpenSSH%22+-Name+DefaultShell+-Value+%22C%3A%5CWindows%5CSystem32%5CWindowsPowerShell%5Cv1.0%5Cpowershell.exe%22+-PropertyType+String+-Force%3B%0D%0Aicacls.exe+%22C%3A%5CProgramData%5Cssh%5Cadministrators_authorized_keys%22+%2Finheritance%3Ar+%2Fgrant+%22Administrators%3AF%22+%2Fgrant+%22SYSTEM%3AF%22%3B%0D%0A%0D%0A%23+Allow+inbound+connections%0D%0ASet-NetFirewallProfile+-Profile+Domain%2CPublic%2CPrivate+-DefaultInboundAction+Allow%3B%0D%0A%0D%0A%23+Set+up+authorized+keys%0D%0Aecho+%22%24AUTHORIZED_KEYS%22+%7C+Out-File+-Encoding+utf8+-FilePath+%22C%3A%5CProgramData%5Cssh%5Cadministrators_authorized_keys%22%0D%0A&FirstLogonScriptType0=Ps1&WdacMode=Skip-->\n\t<settings pass=\"offlineServicing\"/>\n\t<settings pass=\"windowsPE\">\n\t\t<component name=\"Microsoft-Windows-International-Core-WinPE\" processorArchitecture=\"amd64\" publicKeyToken=\"31bf3856ad364e35\" language=\"neutral\" versionScope=\"nonSxS\">\n\t\t\t<SetupUILanguage>\n\t\t\t\t<UILanguage>en-US</UILanguage>\n\t\t\t</SetupUILanguage>\n\t\t\t<InputLocale>0409:00000409</InputLocale>\n\t\t\t<SystemLocale>en-US</SystemLocale>\n\t\t\t<UILanguage>en-US</UILanguage>\n\t\t\t<UserLocale>en-US</UserLocale>\n\t\t</component>\n\t\t<component name=\"Microsoft-Windows-Setup\" processorArchitecture=\"amd64\" publicKeyToken=\"31bf3856ad364e35\" language=\"neutral\" versionScope=\"nonSxS\">\n\t\t\t<ImageInstall>\n\t\t\t\t<OSImage>\n\t\t\t\t\t<InstallTo>\n\t\t\t\t\t\t<DiskID>0</DiskID>\n\t\t\t\t\t\t<PartitionID>3</PartitionID>\n\t\t\t\t\t</InstallTo>\n\t\t\t\t</OSImage>\n\t\t\t</ImageInstall>\n\t\t\t<UserData>\n\t\t\t\t<ProductKey>\n\t\t\t\t\t<Key>2B87N-8KFHP-DKV6R-Y2C8J-PKCKT</Key>\n\t\t\t\t\t<WillShowUI>OnError</WillShowUI>\n\t\t\t\t</ProductKey>\n\t\t\t\t<AcceptEula>true</AcceptEula>\n\t\t\t</UserData>\n\t\t\t<UseConfigurationSet>false</UseConfigurationSet>\n\t\t\t<RunSynchronous>\n\t\t\t\t<RunSynchronousCommand wcm:action=\"add\">\n\t\t\t\t\t<Order>1</Order>\n\t\t\t\t\t<Path>cmd.exe /c \"&gt;&gt;\"X:\\diskpart.txt\" (echo SELECT DISK=0&amp;echo CLEAN&amp;echo CONVERT GPT&amp;echo CREATE PARTITION EFI SIZE=300&amp;echo FORMAT QUICK FS=FAT32 LABEL=\"System\"&amp;echo CREATE PARTITION MSR SIZE=16)\"</Path>\n\t\t\t\t</RunSynchronousCommand>\n\t\t\t\t<RunSynchronousCommand wcm:action=\"add\">\n\t\t\t\t\t<Order>2</Order>\n\t\t\t\t\t<Path>cmd.exe /c \"&gt;&gt;\"X:\\diskpart.txt\" (echo CREATE PARTITION PRIMARY&amp;echo SHRINK MINIMUM=1000&amp;echo FORMAT QUICK FS=NTFS LABEL=\"Windows\"&amp;echo CREATE PARTITION PRIMARY&amp;echo FORMAT QUICK FS=NTFS LABEL=\"Recovery\")\"</Path>\n\t\t\t\t</RunSynchronousCommand>\n\t\t\t\t<RunSynchronousCommand wcm:action=\"add\">\n\t\t\t\t\t<Order>3</Order>\n\t\t\t\t\t<Path>cmd.exe /c \"&gt;&gt;\"X:\\diskpart.txt\" (echo SET ID=\"de94bba4-06d1-4d40-a16a-bfd50179d6ac\"&amp;echo GPT ATTRIBUTES=0x8000000000000001)\"</Path>\n\t\t\t\t</RunSynchronousCommand>\n\t\t\t\t<RunSynchronousCommand wcm:action=\"add\">\n\t\t\t\t\t<Order>4</Order>\n\t\t\t\t\t<Path>cmd.exe /c \"diskpart.exe /s \"X:\\diskpart.txt\" &gt;&gt;\"X:\\diskpart.log\" || ( type \"X:\\diskpart.log\" &amp; echo diskpart encountered an error. &amp; pause &amp; exit /b 1 )\"</Path>\n\t\t\t\t</RunSynchronousCommand>\n\t\t\t\t<RunSynchronousCommand wcm:action=\"add\">\n\t\t\t\t\t<Order>5</Order>\n\t\t\t\t\t<Path>cmd.exe /c \"&gt;&gt;\"X:\\defender.vbs\" (echo WScript.Echo \"Scanning for newly created SYSTEM registry hive file to disable Windows Defender services...\"&amp;echo Set fso = CreateObject(\"Scripting.FileSystemObject\"^))\"</Path>\n\t\t\t\t</RunSynchronousCommand>\n\t\t\t\t<RunSynchronousCommand wcm:action=\"add\">\n\t\t\t\t\t<Order>6</Order>\n\t\t\t\t\t<Path>cmd.exe /c \"&gt;&gt;\"X:\\defender.vbs\" (echo Set existing = CreateObject(\"Scripting.Dictionary\"^)&amp;echo Function Execute(command^)&amp;echo WScript.Echo \"Running command '\" + command + \"'\"&amp;echo Set shell = CreateObject(\"WScript.Shell\"^))\"</Path>\n\t\t\t\t</RunSynchronousCommand>\n\t\t\t\t<RunSynchronousCommand wcm:action=\"add\">\n\t\t\t\t\t<Order>7</Order>\n\t\t\t\t\t<Path>cmd.exe /c \"&gt;&gt;\"X:\\defender.vbs\" (echo Set exec = shell.Exec(command^)&amp;echo Do While exec.Status = 0&amp;echo WScript.Sleep 100&amp;echo Loop&amp;echo WScript.Echo exec.StdOut.ReadAll&amp;echo WScript.Echo exec.StdErr.ReadAll)\"</Path>\n\t\t\t\t</RunSynchronousCommand>\n\t\t\t\t<RunSynchronousCommand wcm:action=\"add\">\n\t\t\t\t\t<Order>8</Order>\n\t\t\t\t\t<Path>cmd.exe /c \"&gt;&gt;\"X:\\defender.vbs\" (echo Execute = exec.ExitCode&amp;echo End Function&amp;echo Function FindHiveFiles&amp;echo Set FindHiveFiles = CreateObject(\"Scripting.Dictionary\"^)&amp;echo For Each drive In fso.Drives)\"</Path>\n\t\t\t\t</RunSynchronousCommand>\n\t\t\t\t<RunSynchronousCommand wcm:action=\"add\">\n\t\t\t\t\t<Order>9</Order>\n\t\t\t\t\t<Path>cmd.exe /c \"&gt;&gt;\"X:\\defender.vbs\" (echo If drive.IsReady And drive.DriveLetter ^&lt;^&gt; \"X\" Then&amp;echo For Each folder In Array(\"$Windows.~BT\\NewOS\\Windows\", \"Windows\"^))\"</Path>\n\t\t\t\t</RunSynchronousCommand>\n\t\t\t\t<RunSynchronousCommand wcm:action=\"add\">\n\t\t\t\t\t<Order>10</Order>\n\t\t\t\t\t<Path>cmd.exe /c \"&gt;&gt;\"X:\\defender.vbs\" (echo file = fso.BuildPath(fso.BuildPath(drive.RootFolder, folder^), \"System32\\config\\SYSTEM\"^)&amp;echo If fso.FileExists(file^) And fso.FileExists(file + \".LOG1\"^) And fso.FileExists(file + \".LOG2\"^) Then)\"</Path>\n\t\t\t\t</RunSynchronousCommand>\n\t\t\t\t<RunSynchronousCommand wcm:action=\"add\">\n\t\t\t\t\t<Order>11</Order>\n\t\t\t\t\t<Path>cmd.exe /c \"&gt;&gt;\"X:\\defender.vbs\" (echo FindHiveFiles.Add file, Nothing&amp;echo End If&amp;echo Next&amp;echo End If&amp;echo Next&amp;echo End Function&amp;echo For Each file In FindHiveFiles)\"</Path>\n\t\t\t\t</RunSynchronousCommand>\n\t\t\t\t<RunSynchronousCommand wcm:action=\"add\">\n\t\t\t\t\t<Order>12</Order>\n\t\t\t\t\t<Path>cmd.exe /c \"&gt;&gt;\"X:\\defender.vbs\" (echo WScript.Echo \"Will ignore file at '\" + file + \"' because it was already present when Windows Setup started.\"&amp;echo existing.Add file, Nothing&amp;echo Next&amp;echo Do)\"</Path>\n\t\t\t\t</RunSynchronousCommand>\n\t\t\t\t<RunSynchronousCommand wcm:action=\"add\">\n\t\t\t\t\t<Order>13</Order>\n\t\t\t\t\t<Path>cmd.exe /c \"&gt;&gt;\"X:\\defender.vbs\" (echo For Each file In FindHiveFiles&amp;echo If Not existing.Exists(file^) Then&amp;echo ret = 1&amp;echo While ret ^&gt; 0&amp;echo WScript.Sleep 500&amp;echo ret = Execute(\"reg.exe LOAD HKLM\\mount \" + file^))\"</Path>\n\t\t\t\t</RunSynchronousCommand>\n\t\t\t\t<RunSynchronousCommand wcm:action=\"add\">\n\t\t\t\t\t<Order>14</Order>\n\t\t\t\t\t<Path>cmd.exe /c \"&gt;&gt;\"X:\\defender.vbs\" (echo Wend&amp;echo For Each service In Array(\"Sense\", \"WdBoot\", \"WdFilter\", \"WdNisDrv\", \"WdNisSvc\", \"WinDefend\"^))\"</Path>\n\t\t\t\t</RunSynchronousCommand>\n\t\t\t\t<RunSynchronousCommand wcm:action=\"add\">\n\t\t\t\t\t<Order>15</Order>\n\t\t\t\t\t<Path>cmd.exe /c \"&gt;&gt;\"X:\\defender.vbs\" (echo ret = Execute(\"reg.exe ADD HKLM\\mount\\ControlSet001\\Services\\\" + service + \" /v Start /t REG_DWORD /d 4 /f\"^)&amp;echo Next&amp;echo ret = Execute(\"reg.exe UNLOAD HKLM\\mount\"^))\"</Path>\n\t\t\t\t</RunSynchronousCommand>\n\t\t\t\t<RunSynchronousCommand wcm:action=\"add\">\n\t\t\t\t\t<Order>16</Order>\n\t\t\t\t\t<Path>cmd.exe /c \"&gt;&gt;\"X:\\defender.vbs\" (echo WScript.Echo \"Found and successfully modified SYSTEM registry hive file at '\" + file + \"'. This window will now close.\"&amp;echo WScript.Sleep 5000&amp;echo Exit Do&amp;echo End If)\"</Path>\n\t\t\t\t</RunSynchronousCommand>\n\t\t\t\t<RunSynchronousCommand wcm:action=\"add\">\n\t\t\t\t\t<Order>17</Order>\n\t\t\t\t\t<Path>cmd.exe /c \"&gt;&gt;\"X:\\defender.vbs\" (echo WScript.Sleep 1000&amp;echo Next&amp;echo Loop)\"</Path>\n\t\t\t\t</RunSynchronousCommand>\n\t\t\t\t<RunSynchronousCommand wcm:action=\"add\">\n\t\t\t\t\t<Order>18</Order>\n\t\t\t\t\t<Path>cmd.exe /c \"start /MIN cscript.exe //E:vbscript X:\\defender.vbs\"</Path>\n\t\t\t\t</RunSynchronousCommand>\n\t\t\t</RunSynchronous>\n\t\t</component>\n\t</settings>\n\t<settings pass=\"generalize\"/>\n\t<settings pass=\"specialize\">\n\t\t<component name=\"Microsoft-Windows-Deployment\" processorArchitecture=\"amd64\" publicKeyToken=\"31bf3856ad364e35\" language=\"neutral\" versionScope=\"nonSxS\">\n\t\t\t<RunSynchronous>\n\t\t\t\t<RunSynchronousCommand wcm:action=\"add\">\n\t\t\t\t\t<Order>1</Order>\n\t\t\t\t\t<Path>powershell.exe -WindowStyle Normal -NoProfile -Command \"$xml = [xml]::new(); $xml.Load('C:\\Windows\\Panther\\unattend.xml'); $sb = [scriptblock]::Create( $xml.unattend.Extensions.ExtractScript ); Invoke-Command -ScriptBlock $sb -ArgumentList $xml;\"</Path>\n\t\t\t\t</RunSynchronousCommand>\n\t\t\t\t<RunSynchronousCommand wcm:action=\"add\">\n\t\t\t\t\t<Order>2</Order>\n\t\t\t\t\t<Path>powershell.exe -WindowStyle Normal -NoProfile -Command \"Get-Content -LiteralPath 'C:\\Windows\\Setup\\Scripts\\Specialize.ps1' -Raw | Invoke-Expression;\"</Path>\n\t\t\t\t</RunSynchronousCommand>\n\t\t\t\t<RunSynchronousCommand wcm:action=\"add\">\n\t\t\t\t\t<Order>3</Order>\n\t\t\t\t\t<Path>reg.exe load \"HKU\\DefaultUser\" \"C:\\Users\\Default\\NTUSER.DAT\"</Path>\n\t\t\t\t</RunSynchronousCommand>\n\t\t\t\t<RunSynchronousCommand wcm:action=\"add\">\n\t\t\t\t\t<Order>4</Order>\n\t\t\t\t\t<Path>powershell.exe -WindowStyle Normal -NoProfile -Command \"Get-Content -LiteralPath 'C:\\Windows\\Setup\\Scripts\\DefaultUser.ps1' -Raw | Invoke-Expression;\"</Path>\n\t\t\t\t</RunSynchronousCommand>\n\t\t\t\t<RunSynchronousCommand wcm:action=\"add\">\n\t\t\t\t\t<Order>5</Order>\n\t\t\t\t\t<Path>reg.exe unload \"HKU\\DefaultUser\"</Path>\n\t\t\t\t</RunSynchronousCommand>\n\t\t\t</RunSynchronous>\n\t\t</component>\n\t</settings>\n\t<settings pass=\"auditSystem\"/>\n\t<settings pass=\"auditUser\"/>\n\t<settings pass=\"oobeSystem\">\n\t\t<component name=\"Microsoft-Windows-International-Core\" processorArchitecture=\"amd64\" publicKeyToken=\"31bf3856ad364e35\" language=\"neutral\" versionScope=\"nonSxS\">\n\t\t\t<InputLocale>0409:00000409</InputLocale>\n\t\t\t<SystemLocale>en-US</SystemLocale>\n\t\t\t<UILanguage>en-US</UILanguage>\n\t\t\t<UserLocale>en-US</UserLocale>\n\t\t</component>\n\t\t<component name=\"Microsoft-Windows-Shell-Setup\" processorArchitecture=\"amd64\" publicKeyToken=\"31bf3856ad364e35\" language=\"neutral\" versionScope=\"nonSxS\">\n\t\t\t<UserAccounts>\n\t\t\t\t<LocalAccounts>\n\t\t\t\t\t<LocalAccount wcm:action=\"add\">\n\t\t\t\t\t\t<Name>$USER</Name>\n\t\t\t\t\t\t<DisplayName/>\n\t\t\t\t\t\t<Group>Administrators</Group>\n\t\t\t\t\t\t<Password>\n\t\t\t\t\t\t\t<Value/>\n\t\t\t\t\t\t\t<PlainText>true</PlainText>\n\t\t\t\t\t\t</Password>\n\t\t\t\t\t</LocalAccount>\n\t\t\t\t</LocalAccounts>\n\t\t\t</UserAccounts>\n\t\t\t<AutoLogon>\n\t\t\t\t<Username>$USER</Username>\n\t\t\t\t<Enabled>true</Enabled>\n\t\t\t\t<LogonCount>1</LogonCount>\n\t\t\t\t<Password>\n\t\t\t\t\t<Value/>\n\t\t\t\t\t<PlainText>true</PlainText>\n\t\t\t\t</Password>\n\t\t\t</AutoLogon>\n\t\t\t<OOBE>\n\t\t\t\t<ProtectYourPC>3</ProtectYourPC>\n\t\t\t\t<HideEULAPage>true</HideEULAPage>\n\t\t\t\t<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>\n\t\t\t\t<HideOnlineAccountScreens>false</HideOnlineAccountScreens>\n\t\t\t</OOBE>\n\t\t\t<FirstLogonCommands>\n\t\t\t\t<SynchronousCommand wcm:action=\"add\">\n\t\t\t\t\t<Order>1</Order>\n\t\t\t\t\t<CommandLine>powershell.exe -WindowStyle Normal -NoProfile -Command \"Get-Content -LiteralPath 'C:\\Windows\\Setup\\Scripts\\FirstLogon.ps1' -Raw | Invoke-Expression;\"</CommandLine>\n\t\t\t\t</SynchronousCommand>\n\t\t\t</FirstLogonCommands>\n\t\t</component>\n\t</settings>\n\t<Extensions xmlns=\"https://schneegans.de/windows/unattend-generator/\">\n\t\t<ExtractScript>\nparam(\n    [xml] $Document\n);\n\nforeach( $file in $Document.unattend.Extensions.File ) {\n    $path = [System.Environment]::ExpandEnvironmentVariables( $file.GetAttribute( 'path' ) );\n    mkdir -Path( $path | Split-Path -Parent ) -ErrorAction 'SilentlyContinue';\n    $encoding = switch( [System.IO.Path]::GetExtension( $path ) ) {\n        { $_ -in '.ps1', '.xml' } { [System.Text.Encoding]::UTF8; }\n        { $_ -in '.reg', '.vbs', '.js' } { [System.Text.UnicodeEncoding]::new( $false, $true ); }\n        default { [System.Text.Encoding]::Default; }\n    };\n    $bytes = $encoding.GetPreamble() + $encoding.GetBytes( $file.InnerText.Trim() );\n    [System.IO.File]::WriteAllBytes( $path, $bytes );\n}\n\t\t</ExtractScript>\n\t\t<File path=\"C:\\Windows\\Setup\\Scripts\\VirtIoGuestTools.ps1\">\n&amp; {\n\tforeach( $letter in 'DEFGHIJKLMNOPQRSTUVWXYZ'.ToCharArray() ) {\n\t\t$exe = \"${letter}:\\virtio-win-guest-tools.exe\";\n\t\tif( Test-Path -LiteralPath $exe ) {\n\t\t\tStart-Process -FilePath $exe -ArgumentList '/passive', '/norestart' -Wait;\n\t\t\treturn;\n\t\t}\n\t}\n\t'VirtIO Guest Tools image (virtio-win-*.iso) is not attached to this VM.';\n} *&gt;&amp;1 &gt;&gt; 'C:\\Windows\\Setup\\Scripts\\VirtIoGuestTools.log';\n\t\t</File>\n\t\t<File path=\"C:\\Windows\\Setup\\Scripts\\unattend-01.ps1\">\nbcdedit.exe -set TESTSIGNING ON;\n\t\t</File>\n\t\t<File path=\"C:\\Windows\\Setup\\Scripts\\unattend-02.ps1\">\n# https://learn.microsoft.com/en-us/windows-server/administration/openssh/openssh_server_configuration\nAdd-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0;\nStart-Service sshd;\nSet-Service -Name sshd -StartupType 'Automatic';\nNew-ItemProperty -Path \"HKLM:\\SOFTWARE\\OpenSSH\" -Name DefaultShell -Value \"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -PropertyType String -Force;\nicacls.exe \"C:\\ProgramData\\ssh\\administrators_authorized_keys\" /inheritance:r /grant \"Administrators:F\" /grant \"SYSTEM:F\";\n\n# Allow inbound connections\nSet-NetFirewallProfile -Profile Domain,Public,Private -DefaultInboundAction Allow;\n\n# Set up authorized keys\necho \"$AUTHORIZED_KEYS\" | Out-File -Encoding utf8 -FilePath \"C:\\ProgramData\\ssh\\administrators_authorized_keys\"\n\t\t</File>\n\t\t<File path=\"C:\\Windows\\Setup\\Scripts\\Specialize.ps1\">\n$scripts = @(\n\t{\n\t\tnet.exe accounts /lockoutthreshold:0;\n\t};\n\t{\n\t\tnet.exe accounts /maxpwage:UNLIMITED;\n\t};\n\t{\n\t\treg.exe add \"HKLM\\SOFTWARE\\Policies\\Microsoft\\Windows Defender Security Center\\Notifications\" /v DisableNotifications /t REG_DWORD /d 1 /f;\n\t};\n\t{\n\t\tSet-ExecutionPolicy -Scope 'LocalMachine' -ExecutionPolicy 'RemoteSigned' -Force;\n\t};\n\t{\n\t\treg.exe add \"HKLM\\Software\\Policies\\Microsoft\\Windows\\CloudContent\" /v \"DisableWindowsConsumerFeatures\" /t REG_DWORD /d 1 /f;\n\t};\n\t{\n\t\tGet-Content -LiteralPath 'C:\\Windows\\Setup\\Scripts\\VirtIoGuestTools.ps1' -Raw | Invoke-Expression;\n\t};\n\t{\n\t\treg.exe add \"HKLM\\SYSTEM\\CurrentControlSet\\Control\\BitLocker\" /v \"PreventDeviceEncryption\" /t REG_DWORD /d 1 /f;\n\t};\n\t{\n\t\treg.exe add \"HKLM\\SOFTWARE\\Policies\\Microsoft\\Edge\" /v HideFirstRunExperience /t REG_DWORD /d 1 /f;\n\t};\n\t{\n\t\tGet-Content -LiteralPath 'C:\\Windows\\Setup\\Scripts\\unattend-01.ps1' -Raw | Invoke-Expression;\n\t};\n);\n\n&amp; {\n  [float] $complete = 0;\n  [float] $increment = 100 / $scripts.Count;\n  foreach( $script in $scripts ) {\n    Write-Progress -Activity 'Running scripts to customize your Windows installation. Do not close this window.' -PercentComplete $complete;\n    '*** Will now execute command «{0}».' -f $(\n      $str = $script.ToString().Trim() -replace '\\s+', ' ';\n      $max = 100;\n      if( $str.Length -le $max ) {\n        $str;\n      } else {\n        $str.Substring( 0, $max - 1 ) + '…';\n      }\n    );\n    $start = [datetime]::Now;\n    &amp; $script;\n    '*** Finished executing command after {0:0} ms.' -f [datetime]::Now.Subtract( $start ).TotalMilliseconds;\n    \"`r`n\" * 3;\n    $complete += $increment;\n  }\n} *&gt;&amp;1 &gt;&gt; \"C:\\Windows\\Setup\\Scripts\\Specialize.log\";\n\t\t</File>\n\t\t<File path=\"C:\\Windows\\Setup\\Scripts\\DefaultUser.ps1\">\n$scripts = @(\n\t{\n\t\t$names = @(\n\t\t  'ContentDeliveryAllowed';\n\t\t  'FeatureManagementEnabled';\n\t\t  'OEMPreInstalledAppsEnabled';\n\t\t  'PreInstalledAppsEnabled';\n\t\t  'PreInstalledAppsEverEnabled';\n\t\t  'SilentInstalledAppsEnabled';\n\t\t  'SoftLandingEnabled';\n\t\t  'SubscribedContentEnabled';\n\t\t  'SubscribedContent-310093Enabled';\n\t\t  'SubscribedContent-338387Enabled';\n\t\t  'SubscribedContent-338388Enabled';\n\t\t  'SubscribedContent-338389Enabled';\n\t\t  'SubscribedContent-338393Enabled';\n\t\t  'SubscribedContent-353698Enabled';\n\t\t  'SystemPaneSuggestionsEnabled';\n\t\t);\n\t\t\n\t\tforeach( $name in $names ) {\n\t\t  reg.exe add \"HKU\\DefaultUser\\Software\\Microsoft\\Windows\\CurrentVersion\\ContentDeliveryManager\" /v $name /t REG_DWORD /d 0 /f;\n\t\t}\n\t};\n);\n\n&amp; {\n  [float] $complete = 0;\n  [float] $increment = 100 / $scripts.Count;\n  foreach( $script in $scripts ) {\n    Write-Progress -Activity 'Running scripts to modify the default user’’s registry hive. Do not close this window.' -PercentComplete $complete;\n    '*** Will now execute command «{0}».' -f $(\n      $str = $script.ToString().Trim() -replace '\\s+', ' ';\n      $max = 100;\n      if( $str.Length -le $max ) {\n        $str;\n      } else {\n        $str.Substring( 0, $max - 1 ) + '…';\n      }\n    );\n    $start = [datetime]::Now;\n    &amp; $script;\n    '*** Finished executing command after {0:0} ms.' -f [datetime]::Now.Subtract( $start ).TotalMilliseconds;\n    \"`r`n\" * 3;\n    $complete += $increment;\n  }\n} *&gt;&amp;1 &gt;&gt; \"C:\\Windows\\Setup\\Scripts\\DefaultUser.log\";\n\t\t</File>\n\t\t<File path=\"C:\\Windows\\Setup\\Scripts\\FirstLogon.ps1\">\n$scripts = @(\n\t{\n\t\tSet-ItemProperty -LiteralPath 'Registry::HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon' -Name 'AutoLogonCount' -Type 'DWord' -Force -Value 0;\n\t};\n\t{\n\t\tDisable-ComputerRestore -Drive 'C:\\';\n\t};\n\t{\n\t\tGet-Content -LiteralPath 'C:\\Windows\\Setup\\Scripts\\unattend-02.ps1' -Raw | Invoke-Expression;\n\t};\n);\n\n&amp; {\n  [float] $complete = 0;\n  [float] $increment = 100 / $scripts.Count;\n  foreach( $script in $scripts ) {\n    Write-Progress -Activity 'Running scripts to finalize your Windows installation. Do not close this window.' -PercentComplete $complete;\n    '*** Will now execute command «{0}».' -f $(\n      $str = $script.ToString().Trim() -replace '\\s+', ' ';\n      $max = 100;\n      if( $str.Length -le $max ) {\n        $str;\n      } else {\n        $str.Substring( 0, $max - 1 ) + '…';\n      }\n    );\n    $start = [datetime]::Now;\n    &amp; $script;\n    '*** Finished executing command after {0:0} ms.' -f [datetime]::Now.Subtract( $start ).TotalMilliseconds;\n    \"`r`n\" * 3;\n    $complete += $increment;\n  }\n} *&gt;&amp;1 &gt;&gt; \"C:\\Windows\\Setup\\Scripts\\FirstLogon.log\";\n\t\t</File>\n\t</Extensions>\n</unattend>"
  },
  {
    "path": "scripts/windows/setup-efw.sh",
    "content": "#!/usr/bin/env bash\n# Install dependencies required by eBPF for Windows.\n\nset -euo pipefail\n\nVM_NAME=\"$1\"\n\nip=$(virsh --connect qemu:///system domifaddr \"$VM_NAME\" | gawk 'match($0, /([[:digit:]\\.]+)\\//, a) { print a[1] }')\n\nif [ -z \"$ip\" ]; then\n  echo \"Can't figure out IP address of VM, giving up\"\n  exit 1\nfi\n\necho \"VM IP is $ip\"\n\necho Installing eBPF for Windows dependencies\nscp ./*.ps1 \"$ip\":\nssh -t \"$ip\" \".\\\\Setup.ps1\"\n"
  },
  {
    "path": "scripts/windows/setup.sh",
    "content": "#!/usr/bin/env bash\n\nset -euo pipefail\n\n# Variables\nVIRTIO_ISO_URL=\"https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/archive-virtio/virtio-win-0.1.266-1/virtio-win-0.1.266.iso\"\nVIRTIO_ISO=\"/tmp/virtio-win.iso\"\n\n# Check if ISO path is provided\nif [ -z \"$1\" ]; then\n    echo \"Usage: $0 <path_to_windows_iso>\"\n    exit 1\nelse\n    ISO_PATH=$1\nfi\n\nif [ \"$(whoami)\" = \"root\" ]; then\n  echo \"Do not run this script as root: it prevents detecting the correct user name\"\n  exit 1\nfi\n\n# Prompt settings\nread -p \"Enter the name of the VM (default is vm): \" VM_NAME\nVM_NAME=${VM_NAME:-vm}\nread -p \"Enter the amount of RAM in MB (default is 8192): \" RAM_MB\nRAM_MB=${RAM_MB:-8192}\nread -p \"Enter the disk size in GB (default is 100): \" DISK_SIZE\nDISK_SIZE=${DISK_SIZE:-100}\n\nSSH_PUBKEYS=(\"$HOME/.ssh\"/*.pub)\nif [ ${#SSH_PUBKEYS[@]} -eq 0 ]; then\n  echo \"No .pub files found in ~/.ssh directory.\"\n  exit 1\nelif [ ${#SSH_PUBKEYS[@]} -eq 1 ]; then\n  SSH_PUBKEY=${SSH_PUBKEYS[0]}\nelse\n  SSH_PUBKEY=$(printf \"%s\\n\" \"${SSH_PUBKEYS[@]}\" | fzf --prompt=\"Select a .pub file: \")\nfi\n\nif [ -z \"$SSH_PUBKEY\" ]; then\n  echo \"No SSH pubkey selected.\"\n  exit 1\nfi\n\n# Check disk before starting download\nVM_DISK=\"/var/lib/libvirt/images/${VM_NAME}.qcow2\"\n\nif [ -f \"$VM_DISK\" ]; then\n  echo \"Error: $VM_DISK already exists\"\n  exit 1\nfi\n\n# Download Virtio Drivers ISO\necho \"Downloading Virtio drivers ISO...\"\ncurl -L -o \"$VIRTIO_ISO\" --etag-save \"$VIRTIO_ISO.tmp\" --etag-compare \"$VIRTIO_ISO.etag\" \"$VIRTIO_ISO_URL\"\nmv \"$VIRTIO_ISO.tmp\" \"$VIRTIO_ISO.etag\"\n\n# Create autounattend\ntemp=\"$(mktemp -d)\"\n\ncleanup() {\n  sudo umount \"$temp/mount\" 2> /dev/null\n  rm -rf \"$temp\"\n}\ntrap cleanup EXIT\n\nchmod 0755 \"$temp\"\nmkdir -p \"$temp/mount\" \"$temp/modifications\"\n\n\n# Prepare an installation file automatically installs Windows.\n#\n# Allows ssh authentication with all public keys found in ~/.ssh.\nAUTHORIZED_KEYS=\"$(cat \"$SSH_PUBKEY\")\" envsubst '$USER $AUTHORIZED_KEYS' < autounattend.xml > \"$temp/modifications/autounattend.xml\"\n\n# Generate bootable ISO.\n#\n# This ISO contains the autounattend.xml and doesn't require pressing a button\n# to start the installation. See:\n#  * https://palant.info/2023/02/13/automating-windows-installation-in-a-vm/\nsudo mount -o loop \"$ISO_PATH\" \"$temp/mount\"\n\ngenisoimage \\\n  -iso-level 4 -rock -udf \\\n  -disable-deep-relocation \\\n  -untranslated-filenames \\\n  -allow-limited-size \\\n  -no-emul-boot \\\n  -boot-load-size 8 \\\n  -eltorito-boot boot/etfsboot.com \\\n  -eltorito-alt-boot \\\n  -eltorito-boot efi/microsoft/boot/efisys_noprompt.bin \\\n  -o \"$temp/win.iso\" \\\n  \"$temp/mount\" \"$temp/modifications\"\n\n# Create VM Disk\nsudo qemu-img create -f qcow2 \"$VM_DISK\" \"${DISK_SIZE}G\"\n\n# Define and create the VM using virt-install.\n# This will start the VM.\nsudo virt-install \\\n  --connect qemu:///system \\\n  --name \"$VM_NAME\" \\\n  --ram \"$RAM_MB\" \\\n  --vcpus \"$(nproc),cores=$(nproc)\" \\\n  --cpu \"host-passthrough,check=none,migratable=off,feature.vmx=require,feature.hle=disable,feature.rtm=disable\" \\\n  --os-variant win11 \\\n  --network network=default,model=e1000 \\\n  --channel type=unix,source.mode=bind,target.type=virtio,target.name=org.qemu.guest_agent.0 \\\n  --graphics spice \\\n  --disk path=\"$VM_DISK\",format=qcow2,bus=sata,size=\"$DISK_SIZE\",boot.order=1 \\\n  --disk path=\"$temp/win.iso\",device=cdrom,bus=sata,boot.order=2 \\\n  --disk path=\"$VIRTIO_ISO\",device=cdrom,bus=sata \\\n  --install bootdev=cdrom \\\n  --boot uefi,firmware.feature0.name=enrolled-keys,firmware.feature0.enabled=no  \\\n  --noautoconsole\n\necho \"Windows VM setup initiated, click through the installer.\"\necho \"You may have to manually start the VM a couple of times.\"\n\n# Show the graphical output so that the user can follow along.\nvirt-manager --connect qemu:///system --show-domain-console \"$VM_NAME\"\n\necho \"Waiting for VM to receive an IP.\"\nip=\"\"\nwhile [ -z \"$ip\" ]; do\n  sleep 10\n  ip=\"$(virsh --connect qemu:///system domifaddr \"$VM_NAME\" | gawk 'match($0, /([[:digit:]\\.]+)\\//, a) { print a[1] }')\"\n  echo -n .\ndone\necho\n\necho \"Waiting for SSH to become available to continue installation.\"\nwhile ! ssh -o ConnectTimeout=10 -T \"$ip\" '$true' &> /dev/null; do\n  echo -n .\ndone\necho\n\n./setup-efw.sh \"$VM_NAME\"\n"
  },
  {
    "path": "struct_ops.go",
    "content": "package ebpf\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/internal\"\n)\n\nconst structOpsValuePrefix = \"bpf_struct_ops_\"\nconst structOpsLinkSec = \".struct_ops.link\"\nconst structOpsSec = \".struct_ops\"\nconst structOpsKeySize = 4\n\n// structOpsFindInnerType returns the \"inner\" struct inside a value struct_ops type.\n//\n// Given a value like:\n//\n//\tstruct bpf_struct_ops_bpf_testmod_ops {\n//\t    struct bpf_struct_ops_common common;\n//\t    struct bpf_testmod_ops data;\n//\t};\n//\n// this function returns the *btf.Struct for \"bpf_testmod_ops\" along with the\n// byte offset of the \"data\" member inside the value type.\n//\n// The inner struct name is derived by trimming the \"bpf_struct_ops_\" prefix\n// from the value's name.\nfunc structOpsFindInnerType(vType *btf.Struct) (*btf.Struct, uint32, error) {\n\tinnerName := strings.TrimPrefix(vType.Name, structOpsValuePrefix)\n\n\tfor _, m := range vType.Members {\n\t\tif st, ok := btf.As[*btf.Struct](m.Type); ok && st.Name == innerName {\n\t\t\treturn st, m.Offset.Bytes(), nil\n\t\t}\n\t}\n\n\treturn nil, 0, fmt.Errorf(\"inner struct %q not found in %s\", innerName, vType.Name)\n}\n\n// structOpsFindTarget resolves the kernel-side \"value struct\" for a struct_ops map.\nfunc structOpsFindTarget(userType *btf.Struct, cache *btf.Cache) (vType *btf.Struct, id btf.TypeID, module *btf.Handle, err error) {\n\t// the kernel value type name, e.g. \"bpf_struct_ops_<name>\"\n\tvTypeName := structOpsValuePrefix + userType.Name\n\n\ttarget := btf.Type((*btf.Struct)(nil))\n\tspec, module, err := findTargetInKernel(vTypeName, &target, cache)\n\tif errors.Is(err, btf.ErrNotFound) {\n\t\treturn nil, 0, nil, fmt.Errorf(\"%q doesn't exist in kernel: %w\", vTypeName, ErrNotSupported)\n\t}\n\tif err != nil {\n\t\treturn nil, 0, nil, fmt.Errorf(\"lookup value type %q: %w\", vTypeName, err)\n\t}\n\n\tid, err = spec.TypeID(target)\n\tif err != nil {\n\t\treturn nil, 0, nil, err\n\t}\n\n\treturn target.(*btf.Struct), id, module, nil\n}\n\n// structOpsPopulateValue writes a `prog FD` which references to `p` into the\n// struct_ops value buffer `kernVData` at byte offset `dstOff` corresponding to\n// the member `km`.\nfunc structOpsPopulateValue(km btf.Member, kernVData []byte, p *Program) error {\n\tkmPtr, ok := btf.As[*btf.Pointer](km.Type)\n\tif !ok {\n\t\treturn fmt.Errorf(\"member %s is not a func pointer\", km.Name)\n\t}\n\n\tif _, isFuncProto := btf.As[*btf.FuncProto](kmPtr.Target); !isFuncProto {\n\t\treturn fmt.Errorf(\"member %s is not a func pointer\", km.Name)\n\t}\n\n\tdstOff := int(km.Offset.Bytes())\n\tif dstOff < 0 || dstOff+8 > len(kernVData) {\n\t\treturn fmt.Errorf(\"member %q: value buffer too small for func ptr\", km.Name)\n\t}\n\n\tinternal.NativeEndian.PutUint64(kernVData[dstOff:dstOff+8], uint64(p.FD()))\n\treturn nil\n}\n\n// structOpsCopyMember copies a single member from the user struct (m)\n// into the kernel value struct (km) for struct_ops.\nfunc structOpsCopyMember(m, km btf.Member, data []byte, kernVData []byte) error {\n\tmSize, err := btf.Sizeof(m.Type)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"sizeof(user.%s): %w\", m.Name, err)\n\t}\n\tkSize, err := btf.Sizeof(km.Type)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"sizeof(kernel.%s): %w\", km.Name, err)\n\t}\n\tif mSize != kSize {\n\t\treturn fmt.Errorf(\"size mismatch for %s: user=%d kernel=%d\", m.Name, mSize, kSize)\n\t}\n\tif km.BitfieldSize > 0 || m.BitfieldSize > 0 {\n\t\treturn fmt.Errorf(\"bitfield %s not supported\", m.Name)\n\t}\n\n\tsrcOff := int(m.Offset.Bytes())\n\tdstOff := int(km.Offset.Bytes())\n\n\tif srcOff < 0 || srcOff+mSize > len(data) {\n\t\treturn fmt.Errorf(\"member %q: userdata is too small\", m.Name)\n\t}\n\n\tif dstOff < 0 || dstOff+mSize > len(kernVData) {\n\t\treturn fmt.Errorf(\"member %q: value type is too small\", m.Name)\n\t}\n\n\t// skip mods(const, restrict, volatile and typetag)\n\t// and typedef to check type compatibility\n\tmType := btf.UnderlyingType(m.Type)\n\tkernMType := btf.UnderlyingType(km.Type)\n\tif reflect.TypeOf(mType) != reflect.TypeOf(kernMType) {\n\t\treturn fmt.Errorf(\"unmatched member type %s != %s (kernel)\", m.Name, km.Name)\n\t}\n\n\tswitch mType.(type) {\n\tcase *btf.Struct, *btf.Union:\n\t\tif !structOpsIsMemZeroed(data[srcOff : srcOff+mSize]) {\n\t\t\treturn fmt.Errorf(\"non-zero nested struct %s: %w\", m.Name, ErrNotSupported)\n\t\t}\n\t\t// the bytes has zeroed value, we simply skip the copy.\n\t\treturn nil\n\t}\n\n\tcopy(kernVData[dstOff:dstOff+mSize], data[srcOff:srcOff+mSize])\n\treturn nil\n}\n\n// structOpsIsMemZeroed() checks whether all bytes in data are zero.\nfunc structOpsIsMemZeroed(data []byte) bool {\n\tfor _, b := range data {\n\t\tif b != 0 {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// structOpsSetAttachTo sets p.AttachTo in the expected \"struct_name:memberName\" format\n// based on the struct definition.\n//\n// this relies on the assumption that each member in the\n// `.struct_ops` section has a relocation at its starting byte offset.\nfunc structOpsSetAttachTo(\n\tsec *elfSection,\n\tbaseOff uint32,\n\tuserSt *btf.Struct,\n\tprogs map[string]*ProgramSpec) error {\n\tfor _, m := range userSt.Members {\n\t\tmemberOff := m.Offset\n\t\tsym, ok := sec.relocations[uint64(baseOff+memberOff.Bytes())]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tp, ok := progs[sym.Name]\n\t\tif !ok || p == nil {\n\t\t\treturn fmt.Errorf(\"program %s not found\", sym.Name)\n\t\t}\n\n\t\tif p.Type != StructOps {\n\t\t\treturn fmt.Errorf(\"program %s is not StructOps\", sym.Name)\n\t\t}\n\t\tp.AttachTo = userSt.Name + \":\" + m.Name\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "struct_ops_test.go",
    "content": "package ebpf\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc TestCreateStructOpsMapSpecSimple(t *testing.T) {\n\trequireTestmodOps(t)\n\n\tms := &MapSpec{\n\t\tName:       \"testmod_ops\",\n\t\tType:       StructOpsMap,\n\t\tFlags:      sys.BPF_F_LINK,\n\t\tKey:        &btf.Int{Size: 4},\n\t\tKeySize:    4,\n\t\tValue:      &btf.Struct{Name: \"bpf_testmod_ops\"},\n\t\tMaxEntries: 1,\n\t\tContents: []MapKV{\n\t\t\t{\n\t\t\t\tKey:   uint32(0),\n\t\t\t\tValue: make([]byte, 448),\n\t\t\t},\n\t\t},\n\t}\n\n\tm, err := NewMap(ms)\n\ttestutils.SkipIfNotSupported(t, err)\n\tif err != nil {\n\t\tt.Fatalf(\"creating struct_ops map failed: %v\", err)\n\t}\n\tt.Cleanup(func() { _ = m.Close() })\n}\n"
  },
  {
    "path": "syscalls.go",
    "content": "package ebpf\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"os\"\n\t\"runtime\"\n\t\"strings\"\n\n\t\"github.com/cilium/ebpf/asm\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/linux\"\n\t\"github.com/cilium/ebpf/internal/platform\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n\t\"github.com/cilium/ebpf/internal/tracefs\"\n\t\"github.com/cilium/ebpf/internal/unix\"\n)\n\nvar (\n\t// pre-allocating these here since they may\n\t// get called in hot code paths and cause\n\t// unnecessary memory allocations\n\tsysErrKeyNotExist  = sys.Error(ErrKeyNotExist, unix.ENOENT)\n\tsysErrKeyExist     = sys.Error(ErrKeyExist, unix.EEXIST)\n\tsysErrNotSupported = sys.Error(ErrNotSupported, sys.ENOTSUPP)\n)\n\n// sanitizeName replaces all invalid characters in name with replacement.\n// Passing a negative value for replacement will delete characters instead\n// of replacing them.\n//\n// The set of allowed characters may change over time.\nfunc sanitizeName(name string, replacement rune) string {\n\treturn strings.Map(func(char rune) rune {\n\t\tswitch {\n\t\tcase char >= 'A' && char <= 'Z':\n\t\t\treturn char\n\t\tcase char >= 'a' && char <= 'z':\n\t\t\treturn char\n\t\tcase char >= '0' && char <= '9':\n\t\t\treturn char\n\t\tcase char == '.':\n\t\t\treturn char\n\t\tcase char == '_':\n\t\t\treturn char\n\t\tdefault:\n\t\t\treturn replacement\n\t\t}\n\t}, name)\n}\n\nfunc maybeFillObjName(name string) sys.ObjName {\n\tif errors.Is(haveObjName(), ErrNotSupported) {\n\t\treturn sys.ObjName{}\n\t}\n\n\tname = sanitizeName(name, -1)\n\tif errors.Is(objNameAllowsDot(), ErrNotSupported) {\n\t\tname = strings.ReplaceAll(name, \".\", \"\")\n\t}\n\n\treturn sys.NewObjName(name)\n}\n\nfunc progLoad(insns asm.Instructions, typ ProgramType, license string) (*sys.FD, error) {\n\tbuf := bytes.NewBuffer(make([]byte, 0, insns.Size()))\n\tif err := insns.Marshal(buf, internal.NativeEndian); err != nil {\n\t\treturn nil, err\n\t}\n\tbytecode := buf.Bytes()\n\n\treturn sys.ProgLoad(&sys.ProgLoadAttr{\n\t\tProgType: sys.ProgType(typ),\n\t\tLicense:  sys.NewStringPointer(license),\n\t\tInsns:    sys.SlicePointer(bytecode),\n\t\tInsnCnt:  uint32(len(bytecode) / asm.InstructionSize),\n\t})\n}\n\nvar haveNestedMaps = internal.NewFeatureTest(\"nested maps\", func() error {\n\tif platform.IsWindows {\n\t\t// We only support efW versions which have this feature, no need to probe.\n\t\treturn nil\n\t}\n\n\t_, err := sys.MapCreate(&sys.MapCreateAttr{\n\t\tMapType:    sys.MapType(ArrayOfMaps),\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: 1,\n\t\t// Invalid file descriptor.\n\t\tInnerMapFd: ^uint32(0),\n\t})\n\tif errors.Is(err, unix.EINVAL) {\n\t\treturn internal.ErrNotSupported\n\t}\n\tif errors.Is(err, unix.EBADF) {\n\t\treturn nil\n\t}\n\treturn err\n}, \"4.12\", \"windows:0.21.0\")\n\nvar haveMapMutabilityModifiers = internal.NewFeatureTest(\"read- and write-only maps\", func() error {\n\t// This checks BPF_F_RDONLY_PROG and BPF_F_WRONLY_PROG. Since\n\t// BPF_MAP_FREEZE appeared in 5.2 as well we don't do a separate check.\n\tm, err := sys.MapCreate(&sys.MapCreateAttr{\n\t\tMapType:    sys.MapType(Array),\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: 1,\n\t\tMapFlags:   sys.BPF_F_RDONLY_PROG,\n\t})\n\tif err != nil {\n\t\treturn internal.ErrNotSupported\n\t}\n\t_ = m.Close()\n\treturn nil\n}, \"5.2\")\n\nvar haveMmapableMaps = internal.NewFeatureTest(\"mmapable maps\", func() error {\n\t// This checks BPF_F_MMAPABLE, which appeared in 5.5 for array maps.\n\tm, err := sys.MapCreate(&sys.MapCreateAttr{\n\t\tMapType:    sys.MapType(Array),\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: 1,\n\t\tMapFlags:   sys.BPF_F_MMAPABLE,\n\t})\n\tif err != nil {\n\t\treturn internal.ErrNotSupported\n\t}\n\t_ = m.Close()\n\treturn nil\n}, \"5.5\")\n\nvar haveInnerMaps = internal.NewFeatureTest(\"inner maps\", func() error {\n\t// This checks BPF_F_INNER_MAP, which appeared in 5.10.\n\tm, err := sys.MapCreate(&sys.MapCreateAttr{\n\t\tMapType:    sys.MapType(Array),\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: 1,\n\t\tMapFlags:   sys.BPF_F_INNER_MAP,\n\t})\n\n\tif err != nil {\n\t\treturn internal.ErrNotSupported\n\t}\n\t_ = m.Close()\n\treturn nil\n}, \"5.10\")\n\nvar haveNoPreallocMaps = internal.NewFeatureTest(\"prealloc maps\", func() error {\n\t// This checks BPF_F_NO_PREALLOC, which appeared in 4.6.\n\tm, err := sys.MapCreate(&sys.MapCreateAttr{\n\t\tMapType:    sys.MapType(Hash),\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: 1,\n\t\tMapFlags:   sys.BPF_F_NO_PREALLOC,\n\t})\n\n\tif err != nil {\n\t\treturn internal.ErrNotSupported\n\t}\n\t_ = m.Close()\n\treturn nil\n}, \"4.6\")\n\nfunc wrapMapError(err error) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\tif errors.Is(err, unix.ENOENT) {\n\t\treturn sysErrKeyNotExist\n\t}\n\n\tif errors.Is(err, unix.EEXIST) {\n\t\treturn sysErrKeyExist\n\t}\n\n\tif errors.Is(err, sys.ENOTSUPP) {\n\t\treturn sysErrNotSupported\n\t}\n\n\tif errors.Is(err, unix.E2BIG) {\n\t\treturn fmt.Errorf(\"key too big for map: %w\", err)\n\t}\n\n\treturn err\n}\n\nvar haveObjName = internal.NewFeatureTest(\"object names\", func() error {\n\tif platform.IsWindows {\n\t\t// We only support efW versions which have this feature, no need to probe.\n\t\treturn nil\n\t}\n\n\tattr := sys.MapCreateAttr{\n\t\tMapType:    sys.MapType(Array),\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: 1,\n\t\tMapName:    sys.NewObjName(\"feature_test\"),\n\t}\n\n\tfd, err := sys.MapCreate(&attr)\n\tif err != nil {\n\t\treturn internal.ErrNotSupported\n\t}\n\n\t_ = fd.Close()\n\treturn nil\n}, \"4.15\", \"windows:0.21.0\")\n\nvar objNameAllowsDot = internal.NewFeatureTest(\"dot in object names\", func() error {\n\tif platform.IsWindows {\n\t\t// We only support efW versions which have this feature, no need to probe.\n\t\treturn nil\n\t}\n\n\tif err := haveObjName(); err != nil {\n\t\treturn err\n\t}\n\n\tattr := sys.MapCreateAttr{\n\t\tMapType:    sys.MapType(Array),\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: 1,\n\t\tMapName:    sys.NewObjName(\".test\"),\n\t}\n\n\tfd, err := sys.MapCreate(&attr)\n\tif err != nil {\n\t\treturn internal.ErrNotSupported\n\t}\n\n\t_ = fd.Close()\n\treturn nil\n}, \"5.2\", \"windows:0.21.0\")\n\nvar haveBatchAPI = internal.NewFeatureTest(\"map batch api\", func() error {\n\tvar maxEntries uint32 = 2\n\tattr := sys.MapCreateAttr{\n\t\tMapType:    sys.MapType(Hash),\n\t\tKeySize:    4,\n\t\tValueSize:  4,\n\t\tMaxEntries: maxEntries,\n\t}\n\n\tfd, err := sys.MapCreate(&attr)\n\tif err != nil {\n\t\treturn internal.ErrNotSupported\n\t}\n\tdefer fd.Close()\n\n\tkeys := []uint32{1, 2}\n\tvalues := []uint32{3, 4}\n\tkp, _ := marshalMapSyscallInput(keys, 8)\n\tvp, _ := marshalMapSyscallInput(values, 8)\n\n\terr = sys.MapUpdateBatch(&sys.MapUpdateBatchAttr{\n\t\tMapFd:  fd.Uint(),\n\t\tKeys:   kp,\n\t\tValues: vp,\n\t\tCount:  maxEntries,\n\t})\n\tif err != nil {\n\t\treturn internal.ErrNotSupported\n\t}\n\treturn nil\n}, \"5.6\")\n\nvar haveProbeReadKernel = internal.NewFeatureTest(\"bpf_probe_read_kernel\", func() error {\n\tinsns := asm.Instructions{\n\t\tasm.Mov.Reg(asm.R1, asm.R10),\n\t\tasm.Add.Imm(asm.R1, -8),\n\t\tasm.Mov.Imm(asm.R2, 8),\n\t\tasm.Mov.Imm(asm.R3, 0),\n\t\tasm.FnProbeReadKernel.Call(),\n\t\tasm.Return(),\n\t}\n\n\tfd, err := progLoad(insns, Kprobe, \"GPL\")\n\tif err != nil {\n\t\treturn internal.ErrNotSupported\n\t}\n\t_ = fd.Close()\n\treturn nil\n}, \"5.5\")\n\nvar haveBPFToBPFCalls = internal.NewFeatureTest(\"bpf2bpf calls\", func() error {\n\tinsns := asm.Instructions{\n\t\tasm.Call.Label(\"prog2\").WithSymbol(\"prog1\"),\n\t\tasm.Return(),\n\t\tasm.Mov.Imm(asm.R0, 0).WithSymbol(\"prog2\"),\n\t\tasm.Return(),\n\t}\n\n\tfd, err := progLoad(insns, SocketFilter, \"MIT\")\n\tif err != nil {\n\t\treturn internal.ErrNotSupported\n\t}\n\t_ = fd.Close()\n\treturn nil\n}, \"4.16\")\n\nvar haveSyscallWrapper = internal.NewFeatureTest(\"syscall wrapper\", func() error {\n\tprefix := linux.PlatformPrefix()\n\tif prefix == \"\" {\n\t\treturn fmt.Errorf(\"unable to find the platform prefix for (%s)\", runtime.GOARCH)\n\t}\n\n\targs := tracefs.ProbeArgs{\n\t\tType:   tracefs.Kprobe,\n\t\tSymbol: prefix + \"sys_bpf\",\n\t\tPid:    -1,\n\t}\n\n\tvar err error\n\targs.Group, err = tracefs.RandomGroup(\"ebpf_probe\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tevt, err := tracefs.NewEvent(args)\n\tif errors.Is(err, os.ErrNotExist) {\n\t\treturn internal.ErrNotSupported\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn evt.Close()\n}, \"4.17\")\n\nvar haveProgramExtInfos = internal.NewFeatureTest(\"program ext_infos\", func() error {\n\tinsns := asm.Instructions{\n\t\tasm.Mov.Imm(asm.R0, 0),\n\t\tasm.Return(),\n\t}\n\n\tbuf := bytes.NewBuffer(make([]byte, 0, insns.Size()))\n\tif err := insns.Marshal(buf, internal.NativeEndian); err != nil {\n\t\treturn err\n\t}\n\tbytecode := buf.Bytes()\n\n\t_, err := sys.ProgLoad(&sys.ProgLoadAttr{\n\t\tProgType:    sys.ProgType(SocketFilter),\n\t\tLicense:     sys.NewStringPointer(\"MIT\"),\n\t\tInsns:       sys.SlicePointer(bytecode),\n\t\tInsnCnt:     uint32(len(bytecode) / asm.InstructionSize),\n\t\tFuncInfoCnt: 1,\n\t\tProgBtfFd:   math.MaxUint32,\n\t})\n\n\tif errors.Is(err, unix.EBADF) {\n\t\treturn nil\n\t}\n\n\tif errors.Is(err, unix.E2BIG) {\n\t\treturn ErrNotSupported\n\t}\n\n\treturn err\n}, \"5.0\")\n"
  },
  {
    "path": "syscalls_test.go",
    "content": "package ebpf\n\nimport (\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc TestSanitizeName(t *testing.T) {\n\tfor input, want := range map[string]string{\n\t\t\"test\":     \"test\",\n\t\t\"\":         \"\",\n\t\t\"a-b\":      \"ab\",\n\t\t\"yeah so\":  \"yeahso\",\n\t\t\"dot.\":     \"dot.\",\n\t\t\"Capital\":  \"Capital\",\n\t\t\"t_est\":    \"t_est\",\n\t\t\"hörnchen\": \"hrnchen\",\n\t} {\n\t\tqt.Assert(t, qt.Equals(sanitizeName(input, -1), want), qt.Commentf(\"input: %s\", input))\n\t}\n}\n\nfunc TestHaveBatchAPI(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, haveBatchAPI)\n}\n\nfunc TestHaveObjName(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, haveObjName)\n}\n\nfunc TestObjNameAllowsDot(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, objNameAllowsDot)\n}\n\nfunc TestHaveNestedMaps(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, haveNestedMaps)\n}\n\nfunc TestHaveMapMutabilityModifiers(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, haveMapMutabilityModifiers)\n}\n\nfunc TestHaveMmapableMaps(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, haveMmapableMaps)\n}\n\nfunc TestHaveInnerMaps(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, haveInnerMaps)\n}\n\nfunc TestHaveProbeReadKernel(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, haveProbeReadKernel)\n}\n\nfunc TestHaveBPFToBPFCalls(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, haveBPFToBPFCalls)\n}\n\nfunc TestHaveSyscallWrapper(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, haveSyscallWrapper)\n}\n\nfunc TestHaveProgramExtInfos(t *testing.T) {\n\ttestutils.CheckFeatureTest(t, haveProgramExtInfos)\n}\n"
  },
  {
    "path": "testdata/arena.c",
    "content": "/* This file excercises the ELF loader. It is not a valid BPF program. */\n\n#include \"common.h\"\n\nstruct {\n\t__uint(type, BPF_MAP_TYPE_ARENA);\n\t__uint(map_flags, BPF_F_MMAPABLE);\n\t__uint(max_entries, 100); /* number of pages */\n\t__ulong(map_extra, 0x1ull << 44); /* start of mmap region */\n} arena __section(\".maps\");\n"
  },
  {
    "path": "testdata/btf_map_init.c",
    "content": "/* This file excercises the ELF loader. It is not a valid BPF program. */\n\n#include \"common.h\"\n\nint __section(\"socket/tail\") tail_1() {\n\treturn 42;\n}\n\n// Tail call map (program array) initialized with program pointers.\nstruct {\n\t__uint(type, BPF_MAP_TYPE_PROG_ARRAY);\n\t__type(key, uint32_t);\n\t__type(value, uint32_t);\n\t__uint(max_entries, 2);\n\t__array(values, int());\n} prog_array_init __section(\".maps\") = {\n\t.values =\n\t\t{\n\t\t\t// Skip index 0 to exercise empty array slots.\n\t\t\t[1] = &tail_1,\n\t\t},\n};\n\nint __section(\"socket/main\") tail_main(void *ctx) {\n\t// If prog_array_init is correctly populated, the tail call\n\t// will succeed and the program will continue in tail_1 and\n\t// not return here.\n\tbpf_tail_call(ctx, &prog_array_init, 1);\n\n\treturn 0;\n}\n\n// Inner map with a single possible entry.\nstruct {\n\t__uint(type, BPF_MAP_TYPE_ARRAY);\n\t__uint(max_entries, 1);\n\t__type(key, uint32_t);\n\t__type(value, uint32_t);\n} inner_map __section(\".maps\");\n\n// Outer map carrying a reference to the inner map.\nstruct {\n\t__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);\n\t__uint(max_entries, 2);\n\t__type(key, uint32_t);\n\t__type(value, uint32_t);\n\t__array(values, typeof(inner_map));\n} outer_map_init __section(\".maps\") = {\n\t.values =\n\t\t{\n\t\t\t// Skip index 0 to exercise empty array slots.\n\t\t\t[1] = &inner_map,\n\t\t},\n};\n"
  },
  {
    "path": "testdata/common.h",
    "content": "#pragma once\n\ntypedef _Bool bool;\ntypedef unsigned char uint8_t;\ntypedef unsigned short uint16_t;\ntypedef unsigned int uint32_t;\ntypedef signed int int32_t;\ntypedef unsigned long uint64_t;\n\nenum libbpf_tristate {\n\tTRI_NO     = 0,\n\tTRI_YES    = 1,\n\tTRI_MODULE = 2,\n};\n\n#define ___bpf_concat(a, b) ____bpf_concat(a, b)\n#define ____bpf_concat(a, b) a ## b\n\n#define __section(NAME) __attribute__((section(NAME), used))\n#define __uint(name, val) int(*name)[val]\n#define __type(name, val) typeof(val) *name\n#define __array(name, val) typeof(val) *name[]\n#define __ulong(name, val) enum { ___bpf_concat(__unique_value, __COUNTER__) = val } name\n\n#define __kconfig __attribute__((section(\".kconfig\")))\n#define __ksym __attribute__((section(\".ksyms\")))\n#define __noinline __attribute__((noinline))\n#define __weak __attribute__((weak))\n\n#define __hidden __attribute__((visibility(\"hidden\")))\n\n#define bpf_ksym_exists(sym) \\\n\t({ \\\n\t\t_Static_assert(!__builtin_constant_p(!!sym), #sym \" should be marked as __weak\"); \\\n\t\t!!sym; \\\n\t})\n\n#define core_access __builtin_preserve_access_index\n\n#define BPF_MAP_TYPE_HASH (1)\n#define BPF_MAP_TYPE_ARRAY (2)\n#define BPF_MAP_TYPE_PROG_ARRAY (3)\n#define BPF_MAP_TYPE_PERF_EVENT_ARRAY (4)\n#define BPF_MAP_TYPE_ARRAY_OF_MAPS (12)\n#define BPF_MAP_TYPE_HASH_OF_MAPS (13)\n#define BPF_MAP_TYPE_ARENA (33)\n\n#define BPF_F_NO_PREALLOC (1U << 0)\n#define BPF_F_MMAPABLE (1U << 10)\n#define BPF_F_CURRENT_CPU (0xffffffffULL)\n\n/* From tools/lib/bpf/libbpf.h */\nstruct bpf_map_def {\n\tunsigned int type;\n\tunsigned int key_size;\n\tunsigned int value_size;\n\tunsigned int max_entries;\n\tunsigned int map_flags;\n};\n\nstatic void *(*bpf_map_lookup_elem)(const void *map, const void *key) = (void *)1;\n\nstatic long (*bpf_map_update_elem)(const void *map, const void *key, const void *value, uint64_t flags) = (void *)2;\n\nstatic long (*bpf_trace_printk)(const char *fmt, uint32_t fmt_size, ...) = (void *)6;\n\nstatic uint32_t (*bpf_get_smp_processor_id)(void) = (void *)8;\n\nstatic long (*bpf_tail_call)(void *ctx, void *prog_array_map, uint32_t index) = (void *)12;\n\nstatic int (*bpf_perf_event_output)(const void *ctx, const void *map, uint64_t index, const void *data, uint64_t size) = (void *)25;\n\nstatic void *(*bpf_get_current_task)() = (void *)35;\n\nstatic long (*bpf_probe_read_kernel)(void *dst, uint32_t size, const void *unsafe_ptr) = (void *)113;\n\nstatic long (*bpf_for_each_map_elem)(const void *map, void *callback_fn, void *callback_ctx, uint64_t flags) = (void *)164;\n"
  },
  {
    "path": "testdata/constants.c",
    "content": "/* This file exercises the ELF loader. It is not a valid BPF program. */\n\n#include \"common.h\"\n\nchar __license[] __section(\"license\") = \"MIT\";\n\n/*\n * Maps with the Freeze flag set (like .rodata) must be frozen before sending\n * programs to the verifier so constants can be used during verification. If\n * done incorrectly, the following sk_lookup program will fail to verify since\n * the only valid return code is 1. See bpf/verifier.c:check_return_code().\n */\nvolatile const uint32_t ret = -1;\n__section(\"sk_lookup/\") int freeze_rodata() {\n\treturn ret;\n}\n"
  },
  {
    "path": "testdata/docker/Dockerfile",
    "content": "# This Dockerfile generates a build environment for generating ELFs\n# of testdata programs. Run `make build` in this directory to build it.\nFROM golang:1.25-bookworm\n\nCOPY llvm-snapshot.gpg.key .\n\nRUN apt-get update && \\\n    apt-get -y --no-install-recommends install ca-certificates gnupg && \\\n    apt-key add llvm-snapshot.gpg.key && \\\n    rm llvm-snapshot.gpg.key && \\\n    apt-get remove -y gnupg && \\\n    apt-get autoremove -y && \\\n    rm -rf /var/lib/apt/lists/*\n\nCOPY llvm.list /etc/apt/sources.list.d\n\nRUN apt-get update && \\\n    apt-get -y --no-install-recommends install \\\n    make git gawk \\\n    libbpf-dev \\\n    bpftool \\\n    clang-format \\\n    clang-14 llvm-14 \\\n    clang-17 llvm-17 \\\n    clang-20 llvm-20 && \\\n    rm -rf /var/lib/apt/lists/*\n\n# Examples use `#include <asm/types.h>` which Debian carries in asm-generic/ instead.\nRUN ln -s /usr/include/asm-generic /usr/include/asm\n\nRUN curl -fL \"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/plain/scripts/extract-vmlinux\" -o \"/usr/local/bin/extract-vmlinux\" && \\\n    chmod +x /usr/local/bin/extract-vmlinux\n"
  },
  {
    "path": "testdata/docker/IMAGE",
    "content": "ghcr.io/cilium/ebpf-builder\n"
  },
  {
    "path": "testdata/docker/Makefile",
    "content": "# Makefile to build and push the `cilium/ebpf` llvm builder Docker image.\nCONTAINER_ENGINE ?= docker\n\nIMAGE := $(shell cat IMAGE)\nEPOCH := $(shell date +'%s')\n\nifndef IMAGE\n$(error IMAGE file not present in Makefile directory)\nendif\n\n.PHONY: build push\n\nbuild:\n\t${CONTAINER_ENGINE} build --no-cache . -t \"$(IMAGE):$(EPOCH)\"\n\techo $(EPOCH) > VERSION\n\npush:\n\t${CONTAINER_ENGINE} push \"$(IMAGE):$(shell cat VERSION)\"\n"
  },
  {
    "path": "testdata/docker/README.md",
    "content": "# `cilium/ebpf` LLVM Builder Image\n\nThis is a simple Docker image to provide reproducible eBPF ELF builds across\ncontributors' workstations. This standardizes on a single environment used to\nregenerate e.g. testdata ELFs and does not depend on the toolchain installed\non the host machine.\n\nAdditionally, it reduces drift in the bytecode committed to the repository over\ntime as the same exact clang + llc version is used throughout the development\nlifecycle. Only when upgrading or rebuilding the Docker image would changes in\n.elf files be expected (assuming the .c files are untouched).\n\n## Building\n\nBuilding the image requires Docker. Run the build with:\n\n`make build`\n\nThis updates the `VERSION` file. Commit it and submit a PR upstream.\n\n### Regeneration Testdata on non-x86 platforms\n\nBefore running `make`, ensure [Docker buildx](https://docs.docker.com/buildx/working-with-buildx/)\nis enabled. Additionally `QEMU user` and `binfmt` should be installed. On a Debian based distribution\nthe command to add them is `apt install qemu-user-static binfmt-support`.\n\n\n## Pushing\n\nAfter building, push the image to the Docker registry specified in `IMAGE` with:\n\n`make push`\n"
  },
  {
    "path": "testdata/docker/VERSION",
    "content": "1768997810\n"
  },
  {
    "path": "testdata/docker/llvm-snapshot.gpg.key",
    "content": "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: GnuPG v1.4.12 (GNU/Linux)\n\nmQINBFE9lCwBEADi0WUAApM/mgHJRU8lVkkw0CHsZNpqaQDNaHefD6Rw3S4LxNmM\nEZaOTkhP200XZM8lVdbfUW9xSjA3oPldc1HG26NjbqqCmWpdo2fb+r7VmU2dq3NM\nR18ZlKixiLDE6OUfaXWKamZsXb6ITTYmgTO6orQWYrnW6ckYHSeaAkW0wkDAryl2\nB5v8aoFnQ1rFiVEMo4NGzw4UX+MelF7rxaaregmKVTPiqCOSPJ1McC1dHFN533FY\nWh/RVLKWo6npu+owtwYFQW+zyQhKzSIMvNujFRzhIxzxR9Gn87MoLAyfgKEzrbbT\nDhqqNXTxS4UMUKCQaO93TzetX/EBrRpJj+vP640yio80h4Dr5pAd7+LnKwgpTDk1\nG88bBXJAcPZnTSKu9I2c6KY4iRNbvRz4i+ZdwwZtdW4nSdl2792L7Sl7Nc44uLL/\nZqkKDXEBF6lsX5XpABwyK89S/SbHOytXv9o4puv+65Ac5/UShspQTMSKGZgvDauU\ncs8kE1U9dPOqVNCYq9Nfwinkf6RxV1k1+gwtclxQuY7UpKXP0hNAXjAiA5KS5Crq\n7aaJg9q2F4bub0mNU6n7UI6vXguF2n4SEtzPRk6RP+4TiT3bZUsmr+1ktogyOJCc\nHa8G5VdL+NBIYQthOcieYCBnTeIH7D3Sp6FYQTYtVbKFzmMK+36ERreL/wARAQAB\ntD1TeWx2ZXN0cmUgTGVkcnUgLSBEZWJpYW4gTExWTSBwYWNrYWdlcyA8c3lsdmVz\ndHJlQGRlYmlhbi5vcmc+iQI4BBMBAgAiBQJRPZQsAhsDBgsJCAcDAgYVCAIJCgsE\nFgIDAQIeAQIXgAAKCRAVz00Yr090Ibx+EADArS/hvkDF8juWMXxh17CgR0WZlHCC\n9CTBWkg5a0bNN/3bb97cPQt/vIKWjQtkQpav6/5JTVCSx2riL4FHYhH0iuo4iAPR\nudC7Cvg8g7bSPrKO6tenQZNvQm+tUmBHgFiMBJi92AjZ/Qn1Shg7p9ITivFxpLyX\nwpmnF1OKyI2Kof2rm4BFwfSWuf8Fvh7kDMRLHv+MlnK/7j/BNpKdozXxLcwoFBmn\nl0WjpAH3OFF7Pvm1LJdf1DjWKH0Dc3sc6zxtmBR/KHHg6kK4BGQNnFKujcP7TVdv\ngMYv84kun14pnwjZcqOtN3UJtcx22880DOQzinoMs3Q4w4o05oIF+sSgHViFpc3W\nR0v+RllnH05vKZo+LDzc83DQVrdwliV12eHxrMQ8UYg88zCbF/cHHnlzZWAJgftg\nhB08v1BKPgYRUzwJ6VdVqXYcZWEaUJmQAPuAALyZESw94hSo28FAn0/gzEc5uOYx\nK+xG/lFwgAGYNb3uGM5m0P6LVTfdg6vDwwOeTNIExVk3KVFXeSQef2ZMkhwA7wya\nKJptkb62wBHFE+o9TUdtMCY6qONxMMdwioRE5BYNwAsS1PnRD2+jtlI0DzvKHt7B\nMWd8hnoUKhMeZ9TNmo+8CpsAtXZcBho0zPGz/R8NlJhAWpdAZ1CmcPo83EW86Yq7\nBxQUKnNHcwj2ebkCDQRRPZQsARAA4jxYmbTHwmMjqSizlMJYNuGOpIidEdx9zQ5g\nzOr431/VfWq4S+VhMDhs15j9lyml0y4ok215VRFwrAREDg6UPMr7ajLmBQGau0Fc\nbvZJ90l4NjXp5p0NEE/qOb9UEHT7EGkEhaZ1ekkWFTWCgsy7rRXfZLxB6sk7pzLC\nDshyW3zjIakWAnpQ5j5obiDy708pReAuGB94NSyb1HoW/xGsGgvvCw4r0w3xPStw\nF1PhmScE6NTBIfLliea3pl8vhKPlCh54Hk7I8QGjo1ETlRP4Qll1ZxHJ8u25f/ta\nRES2Aw8Hi7j0EVcZ6MT9JWTI83yUcnUlZPZS2HyeWcUj+8nUC8W4N8An+aNps9l/\n21inIl2TbGo3Yn1JQLnA1YCoGwC34g8QZTJhElEQBN0X29ayWW6OdFx8MDvllbBV\nymmKq2lK1U55mQTfDli7S3vfGz9Gp/oQwZ8bQpOeUkc5hbZszYwP4RX+68xDPfn+\nM9udl+qW9wu+LyePbW6HX90LmkhNkkY2ZzUPRPDHZANU5btaPXc2H7edX4y4maQa\nxenqD0lGh9LGz/mps4HEZtCI5CY8o0uCMF3lT0XfXhuLksr7Pxv57yue8LLTItOJ\nd9Hmzp9G97SRYYeqU+8lyNXtU2PdrLLq7QHkzrsloG78lCpQcalHGACJzrlUWVP/\nfN3Ht3kAEQEAAYkCHwQYAQIACQUCUT2ULAIbDAAKCRAVz00Yr090IbhWEADbr50X\nOEXMIMGRLe+YMjeMX9NG4jxs0jZaWHc/WrGR+CCSUb9r6aPXeLo+45949uEfdSsB\npbaEdNWxF5Vr1CSjuO5siIlgDjmT655voXo67xVpEN4HhMrxugDJfCa6z97P0+ML\nPdDxim57uNqkam9XIq9hKQaurxMAECDPmlEXI4QT3eu5qw5/knMzDMZj4Vi6hovL\nwvvAeLHO/jsyfIdNmhBGU2RWCEZ9uo/MeerPHtRPfg74g+9PPfP6nyHD2Wes6yGd\noVQwtPNAQD6Cj7EaA2xdZYLJ7/jW6yiPu98FFWP74FN2dlyEA2uVziLsfBrgpS4l\ntVOlrO2YzkkqUGrybzbLpj6eeHx+Cd7wcjI8CalsqtL6cG8cUEjtWQUHyTbQWAgG\n5VPEgIAVhJ6RTZ26i/G+4J8neKyRs4vz+57UGwY6zI4AB1ZcWGEE3Bf+CDEDgmnP\nLSwbnHefK9IljT9XU98PelSryUO/5UPw7leE0akXKB4DtekToO226px1VnGp3Bov\n1GBGvpHvL2WizEwdk+nfk8LtrLzej+9FtIcq3uIrYnsac47Pf7p0otcFeTJTjSq3\nkrCaoG4Hx0zGQG2ZFpHrSrZTVy6lxvIdfi0beMgY6h78p6M9eYZHQHc02DjFkQXN\nbXb5c6gCHESH5PXwPU4jQEE7Ib9J6sbk7ZT2Mw==\n=j+4q\n-----END PGP PUBLIC KEY BLOCK-----\n"
  },
  {
    "path": "testdata/docker/llvm.list",
    "content": "# Taken from https://apt.llvm.org.\n\ndeb http://apt.llvm.org/bookworm/ llvm-toolchain-bookworm main\ndeb-src http://apt.llvm.org/bookworm/ llvm-toolchain-bookworm main\n\ndeb http://apt.llvm.org/bookworm/ llvm-toolchain-bookworm-17 main\ndeb-src http://apt.llvm.org/bookworm/ llvm-toolchain-bookworm-17 main\n\ndeb http://apt.llvm.org/bookworm/ llvm-toolchain-bookworm-20 main\ndeb-src http://apt.llvm.org/bookworm/ llvm-toolchain-bookworm-20 main\n"
  },
  {
    "path": "testdata/errors.c",
    "content": "#include \"common.h\"\n#include \"../btf/testdata/bpf_core_read.h\"\n\nstruct nonexist {\n\tint non_exist;\n};\n\nenum nonexist_enum { NON_EXIST = 1 };\n\n// Force loading program with BTF by including a relocation for a local type.\n#define FORCE_BTF \\\n\tdo { \\\n\t\tif (bpf_core_type_id_local(int) == 0) \\\n\t\t\treturn __LINE__; \\\n\t} while (0)\n\n__section(\"socket\") int poisoned_single() {\n\tFORCE_BTF;\n\tstruct nonexist ne;\n\treturn core_access(ne.non_exist);\n}\n\n__section(\"socket\") int poisoned_double() {\n\tFORCE_BTF;\n\treturn bpf_core_enum_value(enum nonexist_enum, NON_EXIST);\n}\n\nextern int invalid_kfunc(void) __ksym __weak;\n\n__section(\"socket\") int poisoned_kfunc() {\n\t// NB: This doesn't go via CO-RE but uses a similar mechanism to generate\n\t// an invalid instruction. We test it here for convenience.\n\treturn invalid_kfunc();\n}\n"
  },
  {
    "path": "testdata/fentry_fexit.c",
    "content": "#include \"common.h\"\n\nchar __license[] __section(\"license\") = \"Dual MIT/GPL\";\n\n__section(\"fentry/target\") int trace_on_entry() {\n\treturn 0;\n}\n\n__section(\"fexit/target\") int trace_on_exit() {\n\treturn 0;\n}\n\n__section(\"tc\") int target() {\n\treturn 0;\n}\n"
  },
  {
    "path": "testdata/freplace.c",
    "content": "// /* This file excercises freplace. */\n\n#include \"common.h\"\n\nchar __license[] __section(\"license\") = \"MIT\";\n\nstruct bpf_args {\n\tuint64_t args[0];\n};\n\n__attribute__((noinline)) int subprog() {\n\tvolatile int ret = 0;\n\treturn ret;\n}\n\n__section(\"raw_tracepoint/sched_process_exec\") int sched_process_exec(struct bpf_args *ctx) {\n\treturn subprog();\n}\n\n__section(\"freplace/subprog\") int replacement() {\n\treturn 0;\n}\n"
  },
  {
    "path": "testdata/fwd_decl.c",
    "content": "/* This file excercises the ELF loader. It is not a valid BPF program. */\n\n#include \"common.h\"\n\n// Forward function declaration, never implemented.\nint fwd();\n\n__section(\"socket\") int call_fwd() {\n\treturn fwd();\n}\n"
  },
  {
    "path": "testdata/invalid-kfunc.c",
    "content": "#include \"common.h\"\n\nchar __license[] __section(\"license\") = \"Dual MIT/GPL\";\n\n// This function declaration is incorrect on purpose.\nextern void bpf_kfunc_call_test_mem_len_pass1(void) __ksym;\n\n__section(\"tc\") int call_kfunc() {\n\tbpf_kfunc_call_test_mem_len_pass1();\n\treturn 1;\n}\n"
  },
  {
    "path": "testdata/invalid_btf_map_init.c",
    "content": "/* This file excercises the ELF loader. It is not a valid BPF program. */\n\n#include \"common.h\"\n\nstruct {\n\t__uint(type, BPF_MAP_TYPE_HASH);\n\t__type(key, uint32_t);\n\t__type(value, uint64_t);\n\t__uint(max_entries, 1);\n} hash_map __section(\".maps\") = {\n\t/* This forces a non-zero byte into the .maps section. */\n\t.key = (void *)1,\n};\n"
  },
  {
    "path": "testdata/invalid_map.c",
    "content": "/* This file excercises the ELF loader. It is not a valid BPF program.\n */\n\n#include \"common.h\"\n\nchar __license[] __section(\"license\") = \"MIT\";\n\nstruct {\n\tstruct bpf_map_def def;\n\tuint32_t dummy;\n} invalid_map __section(\"maps\") = {\n\t.def =\n\t\t{\n\t\t\t.type        = BPF_MAP_TYPE_HASH,\n\t\t\t.key_size    = 4,\n\t\t\t.value_size  = 2,\n\t\t\t.max_entries = 1,\n\t\t},\n\t.dummy = 1,\n};\n"
  },
  {
    "path": "testdata/invalid_map_static.c",
    "content": "/* This file excercises the ELF loader. It is not a valid BPF program. */\n\n#include \"common.h\"\n\nstruct bpf_map_def dummy __section(\"maps\") = {\n\t.type        = BPF_MAP_TYPE_HASH,\n\t.key_size    = sizeof(uint32_t),\n\t.value_size  = sizeof(uint64_t),\n\t.max_entries = 1,\n\t.map_flags   = 0,\n};\n\n/* The static qualifier leads to clang not emitting a symbol. */\nstatic struct bpf_map_def hash_map __section(\"maps\") = {\n\t.type        = BPF_MAP_TYPE_HASH,\n\t.key_size    = sizeof(uint32_t),\n\t.value_size  = sizeof(uint64_t),\n\t.max_entries = 1,\n\t.map_flags   = 0,\n};\n\n__section(\"xdp\") int xdp_prog() {\n\tuint32_t key = 0;\n\tvoid *p      = bpf_map_lookup_elem(&hash_map, &key);\n\treturn !!p;\n}\n"
  },
  {
    "path": "testdata/iproute2_map_compat.c",
    "content": "/* This file excercises the ELF loader. It is not a valid BPF program. */\n\n#include \"common.h\"\n\n#define PIN_GLOBAL_NS 2\n\n// bpf_elf_map is a custom BPF map definition used by iproute2.\n// It contains the id, pinning, inner_id and inner_idx fields\n// in addition to the ones in struct bpf_map_def which is commonly\n// used in the kernel and libbpf.\nstruct bpf_elf_map {\n\tunsigned int type;\n\tunsigned int size_key;\n\tunsigned int size_value;\n\tunsigned int max_elem;\n\tunsigned int flags;\n\tunsigned int id;\n\tunsigned int pinning;\n\tunsigned int inner_id;\n\tunsigned int inner_idx;\n};\n\nstruct bpf_elf_map hash_map __section(\"maps\") = {\n\t.type       = BPF_MAP_TYPE_HASH,\n\t.size_key   = sizeof(uint32_t),\n\t.size_value = sizeof(uint64_t),\n\t.max_elem   = 2,\n\t.pinning    = PIN_GLOBAL_NS,\n};\n"
  },
  {
    "path": "testdata/kconfig.c",
    "content": "#include \"common.h\"\n\nchar __license[] __section(\"license\") = \"GPL-2.0\";\n\n/* Special cases requiring feature testing or vDSO magic. */\nextern int LINUX_KERNEL_VERSION __kconfig;\nextern _Bool LINUX_HAS_SYSCALL_WRAPPER __kconfig;\n\n/* Values pulled from /proc/kconfig. */\nextern int CONFIG_HZ __kconfig;\nextern enum libbpf_tristate CONFIG_BPF_SYSCALL __kconfig;\nextern char CONFIG_DEFAULT_HOSTNAME[1] __kconfig;\n\n__section(\"socket\") int kconfig() {\n\tif (LINUX_KERNEL_VERSION == 0)\n\t\treturn __LINE__;\n\n\tif (LINUX_HAS_SYSCALL_WRAPPER == 0)\n\t\treturn __LINE__;\n\n\tif (CONFIG_HZ == 0)\n\t\treturn __LINE__;\n\n\tif (CONFIG_BPF_SYSCALL == TRI_NO)\n\t\treturn __LINE__;\n\n\tif (CONFIG_DEFAULT_HOSTNAME[0] == 0)\n\t\treturn __LINE__;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "testdata/kfunc-kmod.c",
    "content": "#include \"common.h\"\n\nchar __license[] __section(\"license\") = \"Dual MIT/GPL\";\n\nextern void bpf_testmod_test_mod_kfunc(int) __ksym;\n\n__section(\"tc\") int call_kfunc() {\n\tbpf_testmod_test_mod_kfunc(0);\n\treturn 1;\n}\n"
  },
  {
    "path": "testdata/kfunc.c",
    "content": "#include \"common.h\"\n\nchar __license[] __section(\"license\") = \"Dual MIT/GPL\";\n\n// CO-RE type compat checking doesn't allow matches between forward declarations\n// and structs so we can't use forward declarations. Empty structs work just fine.\nstruct __sk_buff {};\nstruct nf_conn {};\nstruct bpf_sock_tuple {};\nstruct bpf_ct_opts {};\nstruct bpf_cpumask {};\n\nextern struct nf_conn *bpf_skb_ct_lookup(struct __sk_buff *, struct bpf_sock_tuple *, uint32_t, struct bpf_ct_opts *, uint32_t) __ksym;\nextern void bpf_ct_release(struct nf_conn *) __ksym;\n\n__section(\"tc\") int call_kfunc(void *ctx) {\n\tchar buf[1];\n\tstruct nf_conn *conn = bpf_skb_ct_lookup(ctx, (void *)buf, 0, (void *)buf, 0);\n\tif (conn) {\n\t\tbpf_ct_release(conn);\n\t}\n\treturn 1;\n}\n\nextern int bpf_fentry_test1(int) __ksym;\n\n__section(\"fentry/bpf_fentry_test2\") int benchmark() {\n\t// bpf_fentry_test1 is a valid kfunc but not allowed to be called from\n\t// TC context. We use this to avoid loading a gajillion programs into\n\t// the kernel when benchmarking the loader.\n\treturn bpf_fentry_test1(0);\n}\n\nextern void invalid_kfunc(void) __ksym __weak;\n\nextern struct bpf_cpumask *bpf_cpumask_create(void) __ksym __weak;\nextern void bpf_cpumask_release(struct bpf_cpumask *cpumask) __ksym __weak;\n\n__section(\"tp_btf/task_newtask\") int weak_kfunc_missing(void *ctx) {\n\tif (bpf_ksym_exists(invalid_kfunc)) {\n\t\tinvalid_kfunc();\n\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n\n__section(\"tp_btf/task_newtask\") int call_weak_kfunc(void *ctx) {\n\tif (bpf_ksym_exists(bpf_cpumask_create)) {\n\t\tstruct bpf_cpumask *mask = bpf_cpumask_create();\n\t\tif (mask)\n\t\t\tbpf_cpumask_release(mask);\n\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "testdata/ksym.c",
    "content": "#include \"common.h\"\n\nchar __license[] __section(\"license\") = \"Dual MIT/GPL\";\n\nstruct {\n\t__uint(type, BPF_MAP_TYPE_ARRAY);\n\t__uint(max_entries, 2);\n\t__type(key, uint32_t);\n\t__type(value, uint64_t);\n} array_map __section(\".maps\");\n\n// Non-weak ksyms must be present in the kernel.\nextern void bpf_init __ksym;\n// Weak ksyms are potentially zero at runtime.\nextern void bpf_trace_run1 __ksym __weak;\n\n__section(\"socket\") int ksym_test() {\n\tuint32_t i;\n\tuint64_t val;\n\n\ti   = 0;\n\tval = (uint64_t)&bpf_init;\n\tbpf_map_update_elem(&array_map, &i, &val, 0);\n\n\ti   = 1;\n\tval = (uint64_t)&bpf_trace_run1;\n\tbpf_map_update_elem(&array_map, &i, &val, 0);\n\n\treturn 0;\n}\n\nextern void non_existing_symbol __ksym __weak;\n\n__section(\"socket\") int ksym_missing_test() {\n\tif (&non_existing_symbol == 0) {\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n"
  },
  {
    "path": "testdata/linked.h",
    "content": "#pragma once\n\n#include \"common.h\"\n\n/* When linking BTF map definitions, all maps must be compatible with each\n * other, otherwise bpftool throws an error. */\nstruct h32_btf {\n\t__uint(type, BPF_MAP_TYPE_HASH);\n\t__type(key, uint32_t);\n\t__type(value, uint32_t);\n\t__uint(max_entries, 1);\n};\n\n/* Legacy map definitions are appended like programs sections are, and can\n * win/lose based on linking order, even if they're completely different maps.\n * Test whether the expected candidate wins by configuring different maxentries.\n */\n#define h32_legacy(MAX_ENTRIES) \\\n{ \\\n        .type        = BPF_MAP_TYPE_HASH, \\\n        .key_size    = sizeof(int), \\\n        .value_size  = sizeof(int), \\\n        .max_entries = MAX_ENTRIES, \\\n        .map_flags   = BPF_F_NO_PREALLOC, \\\n}\n"
  },
  {
    "path": "testdata/linked1.c",
    "content": "#include \"common.h\"\n#include \"linked.h\"\n\n// Weak in L1, strong in L2.\n__weak __section(\".maps\") struct h32_btf map_l1_w;\n\n// Strong in L1, weak in L2.\n__section(\".maps\") struct h32_btf map_l1_s;\n\n// Weak in both L1 and L2.\n__weak __section(\".maps\") struct h32_btf map_ww;\n\n// Strong in L1, only defined here.\n__section(\".maps\") struct h32_btf map_l1;\n\n// Strong in L1, weak in L2.\n__section(\"maps\") struct bpf_map_def map_legacy_l1_s = h32_legacy(1);\n\n// Weak in L1, strong in L2.\n__weak __section(\"maps\") struct bpf_map_def map_legacy_l2_s = h32_legacy(__LINE__);\n\n// Call external symbol only defined in L2.\nextern int l2(void);\n__section(\"socket\") int entry_l2() {\n\treturn l2();\n}\n\n// Weak and only defined in L1, called extern in L2.\n__weak __noinline int l1() {\n\treturn 0;\n}\n\n// Weak in L1, strong in L2.\n__weak __noinline int l1_w() {\n\treturn __LINE__;\n}\n__weak __section(\"socket\") int entry_l1_w() {\n\treturn l1_w();\n}\n\n// Strong in L1, weak in L2.\n__noinline int l1_s() {\n\treturn 0;\n}\n__section(\"socket\") int entry_l1_s() {\n\treturn l1_s();\n}\n\n// Weak in both L1 and L2.\n__weak __noinline int ww() {\n\treturn 0;\n}\n__weak __section(\"socket\") int entry_ww() {\n\treturn ww();\n}\n"
  },
  {
    "path": "testdata/linked2.c",
    "content": "#include \"common.h\"\n#include \"linked.h\"\n\n// Weak in L1, strong in L2.\n__section(\".maps\") struct h32_btf map_l1_w;\n\n// Strong in L1, weak in L2.\n__weak __section(\".maps\") struct h32_btf map_l1_s;\n\n// Weak in both L1 and L2.\n__weak __section(\".maps\") struct h32_btf map_ww;\n\n// Strong in L2, only defined here.\n__section(\".maps\") struct h32_btf map_l2;\n\n// Strong in L1, weak in L2.\n__weak __section(\"maps\") struct bpf_map_def map_legacy_l1_s = h32_legacy(__LINE__);\n\n// Weak in L1, strong in L2.\n__section(\"maps\") struct bpf_map_def map_legacy_l2_s = h32_legacy(1);\n\n// Call external symbol only defined in L1.\nextern int l1(void);\n__section(\"socket\") int entry_l1() {\n\treturn l1();\n}\n\n// Weak and only defined in L2, called extern in L1.\n__weak __noinline int l2() {\n\treturn 0;\n}\n\n// Weak in L1, strong in L2.\n__noinline int l1_w() {\n\treturn 0;\n}\n__section(\"socket\") int entry_l1_w() {\n\treturn l1_w();\n}\n\n// Strong in L1, weak in L2.\n__weak __noinline int l1_s() {\n\treturn __LINE__;\n}\n__weak __section(\"socket\") int entry_l1_s() {\n\treturn l1_s();\n}\n\n// Weak in both L1 and L2.\n__weak __noinline int ww() {\n\treturn __LINE__;\n}\n__weak __section(\"socket\") int entry_ww() {\n\treturn ww();\n}\n"
  },
  {
    "path": "testdata/loader.c",
    "content": "/* This file excercises the ELF loader.\n */\n\n#include \"common.h\"\n\nchar __license[] __section(\"license\") = \"MIT\";\n\n#ifdef __NOBTF__\n#include \"loader_nobtf.h\"\n#else\n#include \"loader.h\"\n#endif\n\nstatic int __attribute__((noinline)) __section(\"static\") static_fn(uint32_t arg) {\n\treturn arg - 1;\n}\n\nint __attribute__((noinline)) global_fn2(uint32_t arg) {\n\treturn arg + 2;\n}\n\nint __attribute__((noinline)) __section(\"other\") global_fn3(uint32_t arg) {\n\treturn arg + 1;\n}\n\nint __attribute__((noinline)) global_fn(uint32_t arg) {\n\treturn static_fn(arg) + global_fn2(arg) + global_fn3(arg);\n}\n\nvolatile unsigned int key1       = 0; // .bss\nvolatile unsigned int key2       = 1; // .data\nvolatile const unsigned int key3 = 2; // .rodata\n\n// .rodata\nvolatile const uint32_t arg = 1;\n// custom .rodata section\nvolatile const uint32_t arg2 __section(\".rodata.test\") = 2;\n// custom .data section\nvolatile uint32_t arg3 __section(\".data.test\");\n\n__section(\"xdp\") int xdp_prog() {\n\tbpf_map_lookup_elem(&hash_map, (void *)&key1);\n\tbpf_map_lookup_elem(&hash_map2, (void *)&key2);\n\tbpf_map_lookup_elem(&hash_map2, (void *)&key3);\n\treturn static_fn(arg) + global_fn(arg) + arg2 + arg3;\n}\n\n// This function has no relocations, and is thus parsed differently.\n__section(\"socket\") int no_relocation() {\n\treturn 0;\n}\n\n// Make sure we allow relocations generated by inline assembly.\n__section(\"socket/2\") int asm_relocation() {\n\tint my_const;\n\tasm(\"%0 = MY_CONST ll\" : \"=r\"(my_const));\n\treturn my_const;\n}\n\nvolatile const unsigned int uneg               = -1;\nvolatile const int neg                         = -2;\nstatic volatile const unsigned int static_uneg = -3;\nstatic volatile const int static_neg           = -4;\n\n__section(\"socket/3\") int data_sections() {\n\tif (uneg != (unsigned int)-1)\n\t\treturn __LINE__;\n\n\tif (neg != -2)\n\t\treturn __LINE__;\n\n\tif (static_uneg != (unsigned int)-3)\n\t\treturn __LINE__;\n\n\tif (static_neg != -4)\n\t\treturn __LINE__;\n\n\treturn 0;\n}\n\n/*\n * Up until LLVM 14, this program results in an .rodata.cst32 section\n * that is accessed by 'return values[i]'. For this section, no BTF is\n * emitted. 'values' cannot be rewritten, since there is no BTF info\n * describing the data section.\n */\n__section(\"socket/4\") int anon_const() {\n\tvolatile int ctx = 0;\n\n// 32 bytes wide results in a .rodata.cst32 section.\n#define values (uint64_t[]){0x0, 0x1, 0x2, 0x3}\n\n\tint i;\n\tfor (i = 0; i < 3; i++) {\n\t\tif (ctx == values[i]) {\n\t\t\treturn values[i];\n\t\t}\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "testdata/loader.h",
    "content": "/* BTF-style map definitions for loader.c */\n\n#pragma once\n\n#include \"common.h\"\n\nstruct {\n\t__uint(type, BPF_MAP_TYPE_HASH);\n\t__type(key, uint32_t);\n\t__type(value, uint64_t);\n\t__uint(max_entries, 1);\n\t__uint(map_flags, BPF_F_NO_PREALLOC);\n} hash_map __section(\".maps\");\n\nstruct {\n\t__uint(type, BPF_MAP_TYPE_HASH);\n\t__uint(key_size, sizeof(uint32_t));\n\t__uint(value_size, sizeof(uint64_t));\n\t__uint(max_entries, 2);\n} hash_map2 __section(\".maps\");\n\nstruct {\n\t__uint(type, BPF_MAP_TYPE_HASH);\n\t__type(key, uint32_t);\n\t__type(value, uint64_t);\n\t__uint(max_entries, 1);\n\t__uint(pinning, 1 /* LIBBPF_PIN_BY_NAME */);\n} btf_pin __section(\".maps\");\n\n// Named map type definition, without structure variable declaration.\nstruct inner_map_t {\n\t__uint(type, BPF_MAP_TYPE_HASH);\n\t__type(key, uint32_t);\n\t__type(value, int);\n\t__uint(max_entries, 1);\n};\n\n// Anonymous map type definition with structure variable declaration.\nstruct {\n\t__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);\n\t__uint(key_size, sizeof(uint32_t));\n\t__uint(max_entries, 1);\n\t__array(values, struct inner_map_t);\n} btf_outer_map __section(\".maps\");\n\n// Array of maps with anonymous inner struct.\nstruct {\n\t__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);\n\t__uint(key_size, sizeof(uint32_t));\n\t__uint(max_entries, 1);\n\t__array(\n\t\tvalues, struct {\n\t\t\t__uint(type, BPF_MAP_TYPE_HASH);\n\t\t\t__uint(max_entries, 1);\n\t\t\t__type(key, uint32_t);\n\t\t\t__type(value, uint32_t);\n\t\t});\n} btf_outer_map_anon __section(\".maps\");\n\nstruct perf_event {\n\tuint64_t foo;\n\tuint64_t bar;\n};\n\nstruct {\n\t__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);\n\t__uint(max_entries, 4096);\n\t__type(value, struct perf_event);\n} perf_event_array __section(\".maps\");\n\ntypedef struct {\n\t__uint(type, BPF_MAP_TYPE_ARRAY);\n\t__uint(key_size, sizeof(uint32_t));\n\t__uint(value_size, sizeof(uint64_t));\n\t__uint(max_entries, 1);\n} array_map_t;\n\n// Map definition behind a typedef.\narray_map_t btf_typedef_map __section(\".maps\");\n\n#define __decl_tags __attribute__((btf_decl_tag(\"a\"), btf_decl_tag(\"b\")))\n\n// Legacy map definition decorated with decl tags.\nstruct bpf_map_def bpf_decl_map __decl_tags __section(\"maps\") = {\n\t.type        = BPF_MAP_TYPE_ARRAY,\n\t.key_size    = sizeof(uint32_t),\n\t.value_size  = sizeof(uint64_t),\n\t.max_entries = 1,\n};\n\n// BTF map definition decorated with decl tags.\nstruct {\n\t__uint(type, BPF_MAP_TYPE_ARRAY);\n\t__uint(key_size, sizeof(uint32_t));\n\t__uint(value_size, sizeof(uint64_t));\n\t__uint(max_entries, 1);\n} btf_decl_map __decl_tags __section(\".maps\");\n"
  },
  {
    "path": "testdata/loader_nobtf.h",
    "content": "/* Legacy map definitions for loader.c (no BTF) */\n\n#pragma once\n\n#include \"common.h\"\n\nstruct bpf_map_def hash_map __section(\"maps\") = {\n\t.type        = BPF_MAP_TYPE_HASH,\n\t.key_size    = sizeof(uint32_t),\n\t.value_size  = sizeof(uint64_t),\n\t.max_entries = 1,\n\t.map_flags   = BPF_F_NO_PREALLOC,\n};\n\nstruct bpf_map_def hash_map2 __section(\"maps\") = {\n\t.type        = BPF_MAP_TYPE_HASH,\n\t.key_size    = sizeof(uint32_t),\n\t.value_size  = sizeof(uint64_t),\n\t.max_entries = 2,\n};\n\n// key_size and value_size always need to be 4 bytes and are automatically set\n// when the map is created if left at 0 in the ELF. Leave them at 0 for\n// consistency with the BTF map definitions, which specify key and value types,\n// causing sizes to be 0 in the MapSpec. This avoids special casing in tests.\nstruct bpf_map_def perf_event_array __section(\"maps\") = {\n\t.type        = BPF_MAP_TYPE_PERF_EVENT_ARRAY,\n\t.max_entries = 4096,\n};\n"
  },
  {
    "path": "testdata/manyprogs.c",
    "content": "/* This file is used for benchmarking NewCollection().\n */\n\n#include \"../btf/testdata/bpf_core_read.h\"\n#include \"common.h\"\n\nchar __license[] __section(\"license\") = \"Dual MIT/GPL\";\n\nstruct bpf_map_def __section(\"maps\") kprobe_map = {\n\t.type        = BPF_MAP_TYPE_HASH,\n\t.key_size    = sizeof(uint32_t),\n\t.value_size  = sizeof(uint64_t),\n\t.max_entries = 128,\n};\n\n#pragma clang attribute push(__attribute__((preserve_access_index)), apply_to = record)\nstruct ns_common {\n\tunsigned int inum;\n};\nstruct mnt_namespace {\n\tstruct ns_common ns;\n};\nstruct nsproxy {\n\tstruct mnt_namespace *mnt_ns;\n};\nstruct task_struct {\n\tstruct nsproxy *nsproxy;\n};\n#pragma clang attribute pop\n\nstatic inline int impl() {\n\tuint64_t initval = 1, *valp;\n\n\tstruct task_struct *task = (struct task_struct *)bpf_get_current_task();\n\tuint32_t mntns           = BPF_CORE_READ(task, nsproxy, mnt_ns, ns.inum);\n\n\tvalp = bpf_map_lookup_elem(&kprobe_map, &mntns);\n\tif (!valp) {\n\t\tbpf_map_update_elem(&kprobe_map, &mntns, &initval, 0);\n\t\treturn 0;\n\t}\n\t__sync_fetch_and_add(valp, 1);\n\n\treturn 0;\n}\n\n#define DEFINE_PROBE(i) \\\n\t__section(\"kprobe/sys_execvea\" #i) int kprobe_execve##i() { \\\n\t\treturn impl(); \\\n\t}\n\nDEFINE_PROBE(0);\nDEFINE_PROBE(1);\nDEFINE_PROBE(2);\nDEFINE_PROBE(3);\nDEFINE_PROBE(4);\nDEFINE_PROBE(5);\nDEFINE_PROBE(6);\nDEFINE_PROBE(7);\nDEFINE_PROBE(8);\nDEFINE_PROBE(9);\n\nDEFINE_PROBE(10);\nDEFINE_PROBE(11);\nDEFINE_PROBE(12);\nDEFINE_PROBE(13);\nDEFINE_PROBE(14);\nDEFINE_PROBE(15);\nDEFINE_PROBE(16);\nDEFINE_PROBE(17);\nDEFINE_PROBE(18);\nDEFINE_PROBE(19);\n\nDEFINE_PROBE(20);\nDEFINE_PROBE(21);\nDEFINE_PROBE(22);\nDEFINE_PROBE(23);\nDEFINE_PROBE(24);\nDEFINE_PROBE(25);\nDEFINE_PROBE(26);\nDEFINE_PROBE(27);\nDEFINE_PROBE(28);\nDEFINE_PROBE(29);\n"
  },
  {
    "path": "testdata/map_spin_lock.c",
    "content": "/* This file excercises bpf_spin_lock. */\n\n#include \"common.h\"\n\nstruct bpf_spin_lock {\n\tuint32_t val;\n};\n\nstruct hash_elem {\n\tint cnt;\n\tstruct bpf_spin_lock lock;\n};\n\nstruct {\n\t__uint(type, BPF_MAP_TYPE_HASH);\n\t__type(key, uint32_t);\n\t__type(value, struct hash_elem);\n\t__uint(max_entries, 2);\n} spin_lock_map __section(\".maps\");\n"
  },
  {
    "path": "testdata/raw_tracepoint.c",
    "content": "/* This file excercises the ELF loader. */\n\n#include \"common.h\"\n\nchar __license[] __section(\"license\") = \"MIT\";\n\nstruct bpf_args {\n\tuint64_t args[0];\n};\n\n__section(\"raw_tracepoint/sched_process_exec\") int sched_process_exec(struct bpf_args *ctx) {\n\treturn 0;\n}\n"
  },
  {
    "path": "testdata/strings.c",
    "content": "#include \"common.h\"\n\nchar __license[] __section(\"license\") = \"MIT\";\n\ntypedef char custkey[48];\n\nstruct {\n\t__uint(type, BPF_MAP_TYPE_HASH);\n\t__uint(max_entries, 2);\n\t__type(key, custkey);\n\t__type(value, uint32_t);\n} my_map __section(\".maps\");\n\n#define KEY \"This string is allocated in the string section\\n\"\n\n__section(\"xdp\") int filter() {\n\tuint32_t *value = bpf_map_lookup_elem(&my_map, KEY);\n\tif (value)\n\t\t(*value)++;\n\telse {\n\t\tuint32_t newValue = 1;\n\t\tbpf_map_update_elem(&my_map, KEY, &newValue, 0);\n\t}\n\n\treturn 2;\n}\n"
  },
  {
    "path": "testdata/struct_ops.c",
    "content": "#include \"common.h\"\n\nchar _license[] __section(\"license\") = \"GPL\";\n\nstruct bpf_testmod_ops {\n\tint (*test_1)(void);\n\tvoid (*test_2)(int, int);\n\tint data;\n};\n\n__section(\"struct_ops/test_1\") int test_1(void) {\n\treturn 0;\n}\n\n__section(\".struct_ops.link\") struct bpf_testmod_ops testmod_ops = {\n\t.test_1 = (void *)test_1,\n\t.data   = 0xdeadbeef,\n};\n"
  },
  {
    "path": "testdata/subprog_reloc.c",
    "content": "/* This file excercises the ELF loader.\n */\n\n#include \"common.h\"\n\nchar __license[] __section(\"license\") = \"MIT\";\n\nstruct bpf_map_def hash_map __section(\"maps\") = {\n\t.type        = BPF_MAP_TYPE_HASH,\n\t.key_size    = sizeof(uint32_t),\n\t.value_size  = sizeof(uint64_t),\n\t.max_entries = 1,\n};\n\nstatic int sub_prog() {\n\tuint32_t key = 0;\n\tuint64_t val = 42;\n\n\tbpf_map_update_elem(&hash_map, &key, &val, /* BPF_ANY */ 0);\n\n\treturn 0;\n}\n\n__section(\"xdp\") int fp_relocation() {\n\tuint32_t key = 0;\n\tuint64_t val = 1;\n\n\tbpf_map_update_elem(&hash_map, &key, &val, /* BPF_ANY */ 0);\n\n\tbpf_for_each_map_elem(&hash_map, sub_prog, (void *)0, 0);\n\n\tuint64_t *new_val = bpf_map_lookup_elem(&hash_map, &key);\n\tif (!new_val) {\n\t\treturn -1;\n\t}\n\n\treturn *new_val;\n}\n"
  },
  {
    "path": "testdata/variables.c",
    "content": "#include \"common.h\"\n\n// Should not appear in CollectionSpec.Variables.\n__hidden volatile uint32_t hidden;\n\n// Weak variables can be overridden by non-weak symbols when linking BPF\n// programs using bpftool. Make sure they appear in CollectionSpec.Variables.\n__weak volatile uint32_t weak __section(\".data.weak\");\n\n// Ensure vars are referenced so they are not culled by the loader.\n__section(\"socket\") int set_vars() {\n\thidden = 0xbeef1;\n\tweak   = 0xbeef2;\n\treturn 0;\n}\n\nvolatile uint32_t var_bss __section(\".bss\");\n__section(\"socket\") int get_bss() {\n\treturn var_bss;\n}\nvolatile uint32_t var_data __section(\".data\");\n__section(\"socket\") int get_data() {\n\treturn var_data;\n}\nvolatile const uint32_t var_rodata __section(\".rodata\");\n__section(\"socket\") int get_rodata() {\n\treturn var_rodata;\n}\n\nstruct var_struct_t {\n\tuint64_t a;\n\tuint64_t b;\n};\nvolatile struct var_struct_t var_struct __section(\".data.struct\");\n__section(\"socket\") int check_struct() {\n\treturn var_struct.a == 0xa && var_struct.b == 0xb;\n}\n\n/* Padding before b and after 1-byte-aligned d. */\nstruct var_struct_pad_t {\n\tuint32_t a;\n\tuint64_t b;\n\tuint16_t c;\n\tuint8_t d[5];\n\tuint64_t e;\n};\nvolatile struct var_struct_pad_t var_struct_pad __section(\".data.struct\");\n__section(\"socket\") int check_struct_pad() {\n\treturn var_struct_pad.a == 0xa && var_struct_pad.b == 0xb && var_struct_pad.c == 0xc && var_struct_pad.d[0] == 0xd && var_struct_pad.e == 0xe;\n}\n\n// Variable aligned on page boundary to ensure all bytes in the mapping can be\n// accessed through the Variable API.\nvolatile uint8_t var_array[8192] __section(\".data.array\");\n__section(\"socket\") int check_array() {\n\treturn var_array[sizeof(var_array) - 1] == 0xff;\n}\n\nvolatile uint32_t var_atomic __section(\".data.atomic\");\n__section(\"socket\") int add_atomic() {\n\t__sync_fetch_and_add(&var_atomic, 1);\n\treturn 0;\n}\n"
  },
  {
    "path": "testdata/windows/LICENSE",
    "content": "MIT License\n\nCopyright (c) eBPF for Windows contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "types.go",
    "content": "package ebpf\n\nimport (\n\t\"github.com/cilium/ebpf/internal/platform\"\n\t\"github.com/cilium/ebpf/internal/sys\"\n)\n\n//go:generate go tool stringer -output types_string.go -type=MapType,ProgramType,PinType\n\n// MapType indicates the type map structure\n// that will be initialized in the kernel.\ntype MapType uint32\n\n// All the various map types that can be created\nconst (\n\tUnspecifiedMap MapType = MapType(platform.LinuxTag | iota)\n\t// Hash is a hash map\n\tHash\n\t// Array is an array map\n\tArray\n\t// ProgramArray - A program array map is a special kind of array map whose map\n\t// values contain only file descriptors referring to other eBPF\n\t// programs.  Thus, both the key_size and value_size must be\n\t// exactly four bytes.  This map is used in conjunction with the\n\t// TailCall helper.\n\tProgramArray\n\t// PerfEventArray - A perf event array is used in conjunction with PerfEventRead\n\t// and PerfEventOutput calls, to read the raw bpf_perf_data from the registers.\n\tPerfEventArray\n\t// PerCPUHash - This data structure is useful for people who have high performance\n\t// network needs and can reconcile adds at the end of some cycle, so that\n\t// hashes can be lock free without the use of XAdd, which can be costly.\n\tPerCPUHash\n\t// PerCPUArray - This data structure is useful for people who have high performance\n\t// network needs and can reconcile adds at the end of some cycle, so that\n\t// hashes can be lock free without the use of XAdd, which can be costly.\n\t// Each CPU gets a copy of this hash, the contents of all of which can be reconciled\n\t// later.\n\tPerCPUArray\n\t// StackTrace - This holds whole user and kernel stack traces, it can be retrieved with\n\t// GetStackID\n\tStackTrace\n\t// CGroupArray - This is a very niche structure used to help SKBInCGroup determine\n\t// if an skb is from a socket belonging to a specific cgroup\n\tCGroupArray\n\t// LRUHash - This allows you to create a small hash structure that will purge the\n\t// least recently used items rather than throw an error when you run out of memory\n\tLRUHash\n\t// LRUCPUHash - This is NOT like PerCPUHash, this structure is shared among the CPUs,\n\t// it has more to do with including the CPU id with the LRU calculation so that if a\n\t// particular CPU is using a value over-and-over again, then it will be saved, but if\n\t// a value is being retrieved a lot but sparsely across CPUs it is not as important, basically\n\t// giving weight to CPU locality over overall usage.\n\tLRUCPUHash\n\t// LPMTrie - This is an implementation of Longest-Prefix-Match Trie structure. It is useful,\n\t// for storing things like IP addresses which can be bit masked allowing for keys of differing\n\t// values to refer to the same reference based on their masks. See wikipedia for more details.\n\tLPMTrie\n\t// ArrayOfMaps - Each item in the array is another map. The inner map mustn't be a map of maps\n\t// itself.\n\tArrayOfMaps\n\t// HashOfMaps - Each item in the hash map is another map. The inner map mustn't be a map of maps\n\t// itself.\n\tHashOfMaps\n\t// DevMap - Specialized map to store references to network devices.\n\tDevMap\n\t// SockMap - Specialized map to store references to sockets.\n\tSockMap\n\t// CPUMap - Specialized map to store references to CPUs.\n\tCPUMap\n\t// XSKMap - Specialized map for XDP programs to store references to open sockets.\n\tXSKMap\n\t// SockHash - Specialized hash to store references to sockets.\n\tSockHash\n\t// CGroupStorage - Special map for CGroups.\n\tCGroupStorage\n\t// ReusePortSockArray - Specialized map to store references to sockets that can be reused.\n\tReusePortSockArray\n\t// PerCPUCGroupStorage - Special per CPU map for CGroups.\n\tPerCPUCGroupStorage\n\t// Queue - FIFO storage for BPF programs.\n\tQueue\n\t// Stack - LIFO storage for BPF programs.\n\tStack\n\t// SkStorage - Specialized map for local storage at SK for BPF programs.\n\tSkStorage\n\t// DevMapHash - Hash-based indexing scheme for references to network devices.\n\tDevMapHash\n\t// StructOpsMap - This map holds a kernel struct with its function pointer implemented in a BPF\n\t// program.\n\tStructOpsMap\n\t// RingBuf - Similar to PerfEventArray, but shared across all CPUs.\n\tRingBuf\n\t// InodeStorage - Specialized local storage map for inodes.\n\tInodeStorage\n\t// TaskStorage - Specialized local storage map for task_struct.\n\tTaskStorage\n\t// BloomFilter - Space-efficient data structure to quickly test whether an element exists in a set.\n\tBloomFilter\n\t// UserRingbuf - The reverse of RingBuf, used to send messages from user space to BPF programs.\n\tUserRingbuf\n\t// CgroupStorage - Store data keyed on a cgroup. If the cgroup disappears, the key is automatically removed.\n\tCgroupStorage\n\t// Arena - Sparse shared memory region between a BPF program and user space.\n\tArena\n)\n\n// Map types (Windows).\nconst (\n\tWindowsHash MapType = MapType(platform.WindowsTag | iota + 1)\n\tWindowsArray\n\tWindowsProgramArray\n\tWindowsPerCPUHash\n\tWindowsPerCPUArray\n\tWindowsHashOfMaps\n\tWindowsArrayOfMaps\n\tWindowsLRUHash\n\tWindowsLPMTrie\n\tWindowsQueue\n\tWindowsLRUCPUHash\n\tWindowsStack\n\tWindowsRingBuf\n)\n\n// MapTypeForPlatform returns a platform specific map type.\n//\n// Use this if the library doesn't provide a constant yet.\nfunc MapTypeForPlatform(plat string, typ uint32) (MapType, error) {\n\treturn platform.EncodeConstant[MapType](plat, typ)\n}\n\n// hasPerCPUValue returns true if the Map stores a value per CPU.\nfunc (mt MapType) hasPerCPUValue() bool {\n\tswitch mt {\n\tcase PerCPUHash, PerCPUArray, LRUCPUHash, PerCPUCGroupStorage:\n\t\treturn true\n\tcase WindowsPerCPUHash, WindowsPerCPUArray, WindowsLRUCPUHash:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\n// canStoreMapOrProgram returns true if the Map stores references to another Map\n// or Program.\nfunc (mt MapType) canStoreMapOrProgram() bool {\n\treturn mt.canStoreMap() || mt.canStoreProgram() || mt == StructOpsMap\n}\n\n// canStoreMap returns true if the map type accepts a map fd\n// for update and returns a map id for lookup.\nfunc (mt MapType) canStoreMap() bool {\n\treturn mt == ArrayOfMaps || mt == HashOfMaps || mt == WindowsArrayOfMaps || mt == WindowsHashOfMaps\n}\n\n// canStoreProgram returns true if the map type accepts a program fd\n// for update and returns a program id for lookup.\nfunc (mt MapType) canStoreProgram() bool {\n\treturn mt == ProgramArray || mt == WindowsProgramArray\n}\n\n// canHaveValueSize returns true if the map type supports setting a value size.\nfunc (mt MapType) canHaveValueSize() bool {\n\tswitch mt {\n\tcase RingBuf, Arena:\n\t\treturn false\n\n\t// Special-case perf events since they require a value size of either 0 or 4\n\t// for historical reasons. Let the library fix this up later.\n\tcase PerfEventArray:\n\t\treturn false\n\t}\n\n\treturn true\n}\n\n// mustHaveNoPrealloc returns true if the map type does not support\n// preallocation and needs the BPF_F_NO_PREALLOC flag set to be created\n// successfully.\nfunc (mt MapType) mustHaveNoPrealloc() bool {\n\tswitch mt {\n\tcase CgroupStorage, InodeStorage, TaskStorage, SkStorage:\n\t\treturn true\n\tcase LPMTrie:\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// ProgramType of the eBPF program\ntype ProgramType uint32\n\n// eBPF program types (Linux).\nconst (\n\tUnspecifiedProgram    = ProgramType(sys.BPF_PROG_TYPE_UNSPEC)\n\tSocketFilter          = ProgramType(sys.BPF_PROG_TYPE_SOCKET_FILTER)\n\tKprobe                = ProgramType(sys.BPF_PROG_TYPE_KPROBE)\n\tSchedCLS              = ProgramType(sys.BPF_PROG_TYPE_SCHED_CLS)\n\tSchedACT              = ProgramType(sys.BPF_PROG_TYPE_SCHED_ACT)\n\tTracePoint            = ProgramType(sys.BPF_PROG_TYPE_TRACEPOINT)\n\tXDP                   = ProgramType(sys.BPF_PROG_TYPE_XDP)\n\tPerfEvent             = ProgramType(sys.BPF_PROG_TYPE_PERF_EVENT)\n\tCGroupSKB             = ProgramType(sys.BPF_PROG_TYPE_CGROUP_SKB)\n\tCGroupSock            = ProgramType(sys.BPF_PROG_TYPE_CGROUP_SOCK)\n\tLWTIn                 = ProgramType(sys.BPF_PROG_TYPE_LWT_IN)\n\tLWTOut                = ProgramType(sys.BPF_PROG_TYPE_LWT_OUT)\n\tLWTXmit               = ProgramType(sys.BPF_PROG_TYPE_LWT_XMIT)\n\tSockOps               = ProgramType(sys.BPF_PROG_TYPE_SOCK_OPS)\n\tSkSKB                 = ProgramType(sys.BPF_PROG_TYPE_SK_SKB)\n\tCGroupDevice          = ProgramType(sys.BPF_PROG_TYPE_CGROUP_DEVICE)\n\tSkMsg                 = ProgramType(sys.BPF_PROG_TYPE_SK_MSG)\n\tRawTracepoint         = ProgramType(sys.BPF_PROG_TYPE_RAW_TRACEPOINT)\n\tCGroupSockAddr        = ProgramType(sys.BPF_PROG_TYPE_CGROUP_SOCK_ADDR)\n\tLWTSeg6Local          = ProgramType(sys.BPF_PROG_TYPE_LWT_SEG6LOCAL)\n\tLircMode2             = ProgramType(sys.BPF_PROG_TYPE_LIRC_MODE2)\n\tSkReuseport           = ProgramType(sys.BPF_PROG_TYPE_SK_REUSEPORT)\n\tFlowDissector         = ProgramType(sys.BPF_PROG_TYPE_FLOW_DISSECTOR)\n\tCGroupSysctl          = ProgramType(sys.BPF_PROG_TYPE_CGROUP_SYSCTL)\n\tRawTracepointWritable = ProgramType(sys.BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE)\n\tCGroupSockopt         = ProgramType(sys.BPF_PROG_TYPE_CGROUP_SOCKOPT)\n\tTracing               = ProgramType(sys.BPF_PROG_TYPE_TRACING)\n\tStructOps             = ProgramType(sys.BPF_PROG_TYPE_STRUCT_OPS)\n\tExtension             = ProgramType(sys.BPF_PROG_TYPE_EXT)\n\tLSM                   = ProgramType(sys.BPF_PROG_TYPE_LSM)\n\tSkLookup              = ProgramType(sys.BPF_PROG_TYPE_SK_LOOKUP)\n\tSyscall               = ProgramType(sys.BPF_PROG_TYPE_SYSCALL)\n\tNetfilter             = ProgramType(sys.BPF_PROG_TYPE_NETFILTER)\n)\n\n// eBPF program types (Windows).\n//\n// See https://github.com/microsoft/ebpf-for-windows/blob/main/include/ebpf_structs.h#L170\nconst (\n\tWindowsXDP ProgramType = ProgramType(platform.WindowsTag) | (iota + 1)\n\tWindowsBind\n\tWindowsCGroupSockAddr\n\tWindowsSockOps\n\tWindowsXDPTest ProgramType = ProgramType(platform.WindowsTag) | 998\n\tWindowsSample  ProgramType = ProgramType(platform.WindowsTag) | 999\n)\n\n// ProgramTypeForPlatform returns a platform specific program type.\n//\n// Use this if the library doesn't provide a constant yet.\nfunc ProgramTypeForPlatform(plat string, value uint32) (ProgramType, error) {\n\treturn platform.EncodeConstant[ProgramType](plat, value)\n}\n\n// AttachType of the eBPF program, needed to differentiate allowed context accesses in\n// some newer program types like CGroupSockAddr. Should be set to AttachNone if not required.\n// Will cause invalid argument (EINVAL) at program load time if set incorrectly.\ntype AttachType uint32\n\n//go:generate go tool stringer -type AttachType -trimprefix Attach\n\n// AttachNone is an alias for AttachCGroupInetIngress for readability reasons.\nconst AttachNone AttachType = 0\n\n// Attach types (Linux).\nconst (\n\tAttachCGroupInetIngress          = AttachType(sys.BPF_CGROUP_INET_INGRESS)\n\tAttachCGroupInetEgress           = AttachType(sys.BPF_CGROUP_INET_EGRESS)\n\tAttachCGroupInetSockCreate       = AttachType(sys.BPF_CGROUP_INET_SOCK_CREATE)\n\tAttachCGroupSockOps              = AttachType(sys.BPF_CGROUP_SOCK_OPS)\n\tAttachSkSKBStreamParser          = AttachType(sys.BPF_SK_SKB_STREAM_PARSER)\n\tAttachSkSKBStreamVerdict         = AttachType(sys.BPF_SK_SKB_STREAM_VERDICT)\n\tAttachCGroupDevice               = AttachType(sys.BPF_CGROUP_DEVICE)\n\tAttachSkMsgVerdict               = AttachType(sys.BPF_SK_MSG_VERDICT)\n\tAttachCGroupInet4Bind            = AttachType(sys.BPF_CGROUP_INET4_BIND)\n\tAttachCGroupInet6Bind            = AttachType(sys.BPF_CGROUP_INET6_BIND)\n\tAttachCGroupInet4Connect         = AttachType(sys.BPF_CGROUP_INET4_CONNECT)\n\tAttachCGroupInet6Connect         = AttachType(sys.BPF_CGROUP_INET6_CONNECT)\n\tAttachCGroupInet4PostBind        = AttachType(sys.BPF_CGROUP_INET4_POST_BIND)\n\tAttachCGroupInet6PostBind        = AttachType(sys.BPF_CGROUP_INET6_POST_BIND)\n\tAttachCGroupUDP4Sendmsg          = AttachType(sys.BPF_CGROUP_UDP4_SENDMSG)\n\tAttachCGroupUDP6Sendmsg          = AttachType(sys.BPF_CGROUP_UDP6_SENDMSG)\n\tAttachLircMode2                  = AttachType(sys.BPF_LIRC_MODE2)\n\tAttachFlowDissector              = AttachType(sys.BPF_FLOW_DISSECTOR)\n\tAttachCGroupSysctl               = AttachType(sys.BPF_CGROUP_SYSCTL)\n\tAttachCGroupUDP4Recvmsg          = AttachType(sys.BPF_CGROUP_UDP4_RECVMSG)\n\tAttachCGroupUDP6Recvmsg          = AttachType(sys.BPF_CGROUP_UDP6_RECVMSG)\n\tAttachCGroupGetsockopt           = AttachType(sys.BPF_CGROUP_GETSOCKOPT)\n\tAttachCGroupSetsockopt           = AttachType(sys.BPF_CGROUP_SETSOCKOPT)\n\tAttachTraceRawTp                 = AttachType(sys.BPF_TRACE_RAW_TP)\n\tAttachTraceFEntry                = AttachType(sys.BPF_TRACE_FENTRY)\n\tAttachTraceFExit                 = AttachType(sys.BPF_TRACE_FEXIT)\n\tAttachModifyReturn               = AttachType(sys.BPF_MODIFY_RETURN)\n\tAttachLSMMac                     = AttachType(sys.BPF_LSM_MAC)\n\tAttachTraceIter                  = AttachType(sys.BPF_TRACE_ITER)\n\tAttachCgroupInet4GetPeername     = AttachType(sys.BPF_CGROUP_INET4_GETPEERNAME)\n\tAttachCgroupInet6GetPeername     = AttachType(sys.BPF_CGROUP_INET6_GETPEERNAME)\n\tAttachCgroupInet4GetSockname     = AttachType(sys.BPF_CGROUP_INET4_GETSOCKNAME)\n\tAttachCgroupInet6GetSockname     = AttachType(sys.BPF_CGROUP_INET6_GETSOCKNAME)\n\tAttachXDPDevMap                  = AttachType(sys.BPF_XDP_DEVMAP)\n\tAttachCgroupInetSockRelease      = AttachType(sys.BPF_CGROUP_INET_SOCK_RELEASE)\n\tAttachXDPCPUMap                  = AttachType(sys.BPF_XDP_CPUMAP)\n\tAttachSkLookup                   = AttachType(sys.BPF_SK_LOOKUP)\n\tAttachXDP                        = AttachType(sys.BPF_XDP)\n\tAttachSkSKBVerdict               = AttachType(sys.BPF_SK_SKB_VERDICT)\n\tAttachSkReuseportSelect          = AttachType(sys.BPF_SK_REUSEPORT_SELECT)\n\tAttachSkReuseportSelectOrMigrate = AttachType(sys.BPF_SK_REUSEPORT_SELECT_OR_MIGRATE)\n\tAttachPerfEvent                  = AttachType(sys.BPF_PERF_EVENT)\n\tAttachTraceKprobeMulti           = AttachType(sys.BPF_TRACE_KPROBE_MULTI)\n\tAttachTraceKprobeSession         = AttachType(sys.BPF_TRACE_KPROBE_SESSION)\n\tAttachLSMCgroup                  = AttachType(sys.BPF_LSM_CGROUP)\n\tAttachStructOps                  = AttachType(sys.BPF_STRUCT_OPS)\n\tAttachNetfilter                  = AttachType(sys.BPF_NETFILTER)\n\tAttachTCXIngress                 = AttachType(sys.BPF_TCX_INGRESS)\n\tAttachTCXEgress                  = AttachType(sys.BPF_TCX_EGRESS)\n\tAttachTraceUprobeMulti           = AttachType(sys.BPF_TRACE_UPROBE_MULTI)\n\tAttachCgroupUnixConnect          = AttachType(sys.BPF_CGROUP_UNIX_CONNECT)\n\tAttachCgroupUnixSendmsg          = AttachType(sys.BPF_CGROUP_UNIX_SENDMSG)\n\tAttachCgroupUnixRecvmsg          = AttachType(sys.BPF_CGROUP_UNIX_RECVMSG)\n\tAttachCgroupUnixGetpeername      = AttachType(sys.BPF_CGROUP_UNIX_GETPEERNAME)\n\tAttachCgroupUnixGetsockname      = AttachType(sys.BPF_CGROUP_UNIX_GETSOCKNAME)\n\tAttachNetkitPrimary              = AttachType(sys.BPF_NETKIT_PRIMARY)\n\tAttachNetkitPeer                 = AttachType(sys.BPF_NETKIT_PEER)\n)\n\n// Attach types (Windows).\n//\n// See https://github.com/microsoft/ebpf-for-windows/blob/main/include/ebpf_structs.h#L260\nconst (\n\tAttachWindowsXDP = AttachType(platform.WindowsTag | iota + 1)\n\tAttachWindowsBind\n\tAttachWindowsCGroupInet4Connect\n\tAttachWindowsCGroupInet6Connect\n\tAttachWindowsCgroupInet4RecvAccept\n\tAttachWindowsCgroupInet6RecvAccept\n\tAttachWindowsCGroupSockOps\n\tAttachWindowsSample\n\tAttachWindowsXDPTest\n)\n\n// AttachTypeForPlatform returns a platform specific attach type.\n//\n// Use this if the library doesn't provide a constant yet.\nfunc AttachTypeForPlatform(plat string, value uint32) (AttachType, error) {\n\treturn platform.EncodeConstant[AttachType](plat, value)\n}\n\n// AttachFlags of the eBPF program used in BPF_PROG_ATTACH command\ntype AttachFlags uint32\n\n// PinType determines whether a map is pinned into a BPFFS.\ntype PinType uint32\n\n// Valid pin types.\n//\n// Mirrors enum libbpf_pin_type.\nconst (\n\tPinNone PinType = iota\n\t// Pin an object by using its name as the filename.\n\tPinByName\n)\n\n// LoadPinOptions control how a pinned object is loaded.\ntype LoadPinOptions struct {\n\t// Request a read-only or write-only object. The default is a read-write\n\t// object. Only one of the flags may be set.\n\tReadOnly  bool\n\tWriteOnly bool\n\n\t// Raw flags for the syscall. Other fields of this struct take precedence.\n\tFlags uint32\n}\n\n// Marshal returns a value suitable for BPF_OBJ_GET syscall file_flags parameter.\nfunc (lpo *LoadPinOptions) Marshal() uint32 {\n\tif lpo == nil {\n\t\treturn 0\n\t}\n\n\tflags := lpo.Flags\n\tif lpo.ReadOnly {\n\t\tflags |= sys.BPF_F_RDONLY\n\t}\n\tif lpo.WriteOnly {\n\t\tflags |= sys.BPF_F_WRONLY\n\t}\n\treturn flags\n}\n\n// BatchOptions batch map operations options\n//\n// Mirrors libbpf struct bpf_map_batch_opts\n// Currently BPF_F_FLAG is the only supported\n// flag (for ElemFlags).\ntype BatchOptions struct {\n\tElemFlags uint64\n\tFlags     uint64\n}\n\n// LogLevel controls the verbosity of the kernel's eBPF program verifier.\n// These constants can be used for the ProgramOptions.LogLevel field.\ntype LogLevel = sys.LogLevel\n\nconst (\n\t// Print verifier state at branch points.\n\tLogLevelBranch = sys.BPF_LOG_LEVEL1\n\n\t// Print verifier state for every instruction.\n\t// Available since Linux v5.2.\n\tLogLevelInstruction = sys.BPF_LOG_LEVEL2\n\n\t// Print verifier errors and stats at the end of the verification process.\n\t// Available since Linux v5.2.\n\tLogLevelStats = sys.BPF_LOG_STATS\n)\n"
  },
  {
    "path": "types_string.go",
    "content": "// Code generated by \"stringer -output types_string.go -type=MapType,ProgramType,PinType\"; DO NOT EDIT.\n\npackage ebpf\n\nimport \"strconv\"\n\nfunc _() {\n\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n\t// Re-run the stringer command to generate them again.\n\tvar x [1]struct{}\n\t_ = x[UnspecifiedMap-0]\n\t_ = x[Hash-1]\n\t_ = x[Array-2]\n\t_ = x[ProgramArray-3]\n\t_ = x[PerfEventArray-4]\n\t_ = x[PerCPUHash-5]\n\t_ = x[PerCPUArray-6]\n\t_ = x[StackTrace-7]\n\t_ = x[CGroupArray-8]\n\t_ = x[LRUHash-9]\n\t_ = x[LRUCPUHash-10]\n\t_ = x[LPMTrie-11]\n\t_ = x[ArrayOfMaps-12]\n\t_ = x[HashOfMaps-13]\n\t_ = x[DevMap-14]\n\t_ = x[SockMap-15]\n\t_ = x[CPUMap-16]\n\t_ = x[XSKMap-17]\n\t_ = x[SockHash-18]\n\t_ = x[CGroupStorage-19]\n\t_ = x[ReusePortSockArray-20]\n\t_ = x[PerCPUCGroupStorage-21]\n\t_ = x[Queue-22]\n\t_ = x[Stack-23]\n\t_ = x[SkStorage-24]\n\t_ = x[DevMapHash-25]\n\t_ = x[StructOpsMap-26]\n\t_ = x[RingBuf-27]\n\t_ = x[InodeStorage-28]\n\t_ = x[TaskStorage-29]\n\t_ = x[BloomFilter-30]\n\t_ = x[UserRingbuf-31]\n\t_ = x[CgroupStorage-32]\n\t_ = x[Arena-33]\n\t_ = x[WindowsHash-268435457]\n\t_ = x[WindowsArray-268435458]\n\t_ = x[WindowsProgramArray-268435459]\n\t_ = x[WindowsPerCPUHash-268435460]\n\t_ = x[WindowsPerCPUArray-268435461]\n\t_ = x[WindowsHashOfMaps-268435462]\n\t_ = x[WindowsArrayOfMaps-268435463]\n\t_ = x[WindowsLRUHash-268435464]\n\t_ = x[WindowsLPMTrie-268435465]\n\t_ = x[WindowsQueue-268435466]\n\t_ = x[WindowsLRUCPUHash-268435467]\n\t_ = x[WindowsStack-268435468]\n\t_ = x[WindowsRingBuf-268435469]\n}\n\nconst (\n\t_MapType_name_0 = \"UnspecifiedMapHashArrayProgramArrayPerfEventArrayPerCPUHashPerCPUArrayStackTraceCGroupArrayLRUHashLRUCPUHashLPMTrieArrayOfMapsHashOfMapsDevMapSockMapCPUMapXSKMapSockHashCGroupStorageReusePortSockArrayPerCPUCGroupStorageQueueStackSkStorageDevMapHashStructOpsMapRingBufInodeStorageTaskStorageBloomFilterUserRingbufCgroupStorageArena\"\n\t_MapType_name_1 = \"WindowsHashWindowsArrayWindowsProgramArrayWindowsPerCPUHashWindowsPerCPUArrayWindowsHashOfMapsWindowsArrayOfMapsWindowsLRUHashWindowsLPMTrieWindowsQueueWindowsLRUCPUHashWindowsStackWindowsRingBuf\"\n)\n\nvar (\n\t_MapType_index_0 = [...]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, 301, 312, 325, 330}\n\t_MapType_index_1 = [...]uint8{0, 11, 23, 42, 59, 77, 94, 112, 126, 140, 152, 169, 181, 195}\n)\n\nfunc (i MapType) String() string {\n\tswitch {\n\tcase i <= 33:\n\t\treturn _MapType_name_0[_MapType_index_0[i]:_MapType_index_0[i+1]]\n\tcase 268435457 <= i && i <= 268435469:\n\t\ti -= 268435457\n\t\treturn _MapType_name_1[_MapType_index_1[i]:_MapType_index_1[i+1]]\n\tdefault:\n\t\treturn \"MapType(\" + strconv.FormatInt(int64(i), 10) + \")\"\n\t}\n}\nfunc _() {\n\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n\t// Re-run the stringer command to generate them again.\n\tvar x [1]struct{}\n\t_ = x[UnspecifiedProgram-0]\n\t_ = x[SocketFilter-1]\n\t_ = x[Kprobe-2]\n\t_ = x[SchedCLS-3]\n\t_ = x[SchedACT-4]\n\t_ = x[TracePoint-5]\n\t_ = x[XDP-6]\n\t_ = x[PerfEvent-7]\n\t_ = x[CGroupSKB-8]\n\t_ = x[CGroupSock-9]\n\t_ = x[LWTIn-10]\n\t_ = x[LWTOut-11]\n\t_ = x[LWTXmit-12]\n\t_ = x[SockOps-13]\n\t_ = x[SkSKB-14]\n\t_ = x[CGroupDevice-15]\n\t_ = x[SkMsg-16]\n\t_ = x[RawTracepoint-17]\n\t_ = x[CGroupSockAddr-18]\n\t_ = x[LWTSeg6Local-19]\n\t_ = x[LircMode2-20]\n\t_ = x[SkReuseport-21]\n\t_ = x[FlowDissector-22]\n\t_ = x[CGroupSysctl-23]\n\t_ = x[RawTracepointWritable-24]\n\t_ = x[CGroupSockopt-25]\n\t_ = x[Tracing-26]\n\t_ = x[StructOps-27]\n\t_ = x[Extension-28]\n\t_ = x[LSM-29]\n\t_ = x[SkLookup-30]\n\t_ = x[Syscall-31]\n\t_ = x[Netfilter-32]\n\t_ = x[WindowsXDP-268435457]\n\t_ = x[WindowsBind-268435458]\n\t_ = x[WindowsCGroupSockAddr-268435459]\n\t_ = x[WindowsSockOps-268435460]\n\t_ = x[WindowsXDPTest-268436454]\n\t_ = x[WindowsSample-268436455]\n}\n\nconst (\n\t_ProgramType_name_0 = \"UnspecifiedProgramSocketFilterKprobeSchedCLSSchedACTTracePointXDPPerfEventCGroupSKBCGroupSockLWTInLWTOutLWTXmitSockOpsSkSKBCGroupDeviceSkMsgRawTracepointCGroupSockAddrLWTSeg6LocalLircMode2SkReuseportFlowDissectorCGroupSysctlRawTracepointWritableCGroupSockoptTracingStructOpsExtensionLSMSkLookupSyscallNetfilter\"\n\t_ProgramType_name_1 = \"WindowsXDPWindowsBindWindowsCGroupSockAddrWindowsSockOps\"\n\t_ProgramType_name_2 = \"WindowsXDPTestWindowsSample\"\n)\n\nvar (\n\t_ProgramType_index_0 = [...]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, 301, 310}\n\t_ProgramType_index_1 = [...]uint8{0, 10, 21, 42, 56}\n\t_ProgramType_index_2 = [...]uint8{0, 14, 27}\n)\n\nfunc (i ProgramType) String() string {\n\tswitch {\n\tcase i <= 32:\n\t\treturn _ProgramType_name_0[_ProgramType_index_0[i]:_ProgramType_index_0[i+1]]\n\tcase 268435457 <= i && i <= 268435460:\n\t\ti -= 268435457\n\t\treturn _ProgramType_name_1[_ProgramType_index_1[i]:_ProgramType_index_1[i+1]]\n\tcase 268436454 <= i && i <= 268436455:\n\t\ti -= 268436454\n\t\treturn _ProgramType_name_2[_ProgramType_index_2[i]:_ProgramType_index_2[i+1]]\n\tdefault:\n\t\treturn \"ProgramType(\" + strconv.FormatInt(int64(i), 10) + \")\"\n\t}\n}\nfunc _() {\n\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n\t// Re-run the stringer command to generate them again.\n\tvar x [1]struct{}\n\t_ = x[PinNone-0]\n\t_ = x[PinByName-1]\n}\n\nconst _PinType_name = \"PinNonePinByName\"\n\nvar _PinType_index = [...]uint8{0, 7, 16}\n\nfunc (i PinType) String() string {\n\tidx := int(i) - 0\n\tif i < 0 || idx >= len(_PinType_index)-1 {\n\t\treturn \"PinType(\" + strconv.FormatInt(int64(i), 10) + \")\"\n\t}\n\treturn _PinType_name[_PinType_index[idx]:_PinType_index[idx+1]]\n}\n"
  },
  {
    "path": "types_windows.go",
    "content": "package ebpf\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"golang.org/x/sys/windows\"\n\n\t\"github.com/cilium/ebpf/internal/efw\"\n\t\"github.com/cilium/ebpf/internal/platform\"\n)\n\n// WindowsProgramTypeForGUID resolves a GUID to a ProgramType.\n//\n// The GUID must be in the form of \"{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}\".\n//\n// Returns an error wrapping [os.ErrNotExist] if the GUID is not recignized.\nfunc WindowsProgramTypeForGUID(guid string) (ProgramType, error) {\n\tprogTypeGUID, err := windows.GUIDFromString(guid)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"parse GUID: %w\", err)\n\t}\n\n\trawProgramType, err := efw.EbpfGetBpfProgramType(progTypeGUID)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"get program type: %w\", err)\n\t}\n\n\tif rawProgramType == 0 {\n\t\treturn 0, fmt.Errorf(\"program type not found for GUID %v: %w\", guid, os.ErrNotExist)\n\t}\n\n\treturn ProgramTypeForPlatform(platform.Windows, rawProgramType)\n}\n\n// WindowsAttachTypeForGUID resolves a GUID to an AttachType.\n//\n// The GUID must be in the form of \"{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}\".\n//\n// Returns an error wrapping [os.ErrNotExist] if the GUID is not recignized.\nfunc WindowsAttachTypeForGUID(guid string) (AttachType, error) {\n\tattachTypeGUID, err := windows.GUIDFromString(guid)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"parse GUID: %w\", err)\n\t}\n\n\trawAttachType, err := efw.EbpfGetBpfAttachType(attachTypeGUID)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"get attach type: %w\", err)\n\t}\n\n\tif rawAttachType == 0 {\n\t\treturn 0, fmt.Errorf(\"attach type not found for GUID %v: %w\", attachTypeGUID, os.ErrNotExist)\n\t}\n\n\treturn AttachTypeForPlatform(platform.Windows, rawAttachType)\n}\n"
  },
  {
    "path": "types_windows_test.go",
    "content": "package ebpf\n\nimport (\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/go-quicktest/qt\"\n\t\"golang.org/x/sys/windows\"\n)\n\nfunc TestWindowsProgramTypeForGUID(t *testing.T) {\n\tsampleGUID := windows.GUID{\n\t\tData1: 0xf788ef4a, Data2: 0x207d, Data3: 0x4dc3,\n\t\tData4: [...]byte{0x85, 0xcf, 0x0f, 0x2e, 0xa1, 0x07, 0x21, 0x3c},\n\t}\n\n\t_, err := WindowsProgramTypeForGUID(\"{00000000-0000-0000-0000-000000000001}\")\n\tqt.Assert(t, qt.ErrorIs(err, os.ErrNotExist))\n\n\tprogramType, err := WindowsProgramTypeForGUID(sampleGUID.String())\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.Equals(WindowsSample, programType))\n}\n\nfunc TestWindowsAttachTypeForGUID(t *testing.T) {\n\tsampleGUID := windows.GUID{\n\t\tData1: 0xf788ef4b, Data2: 0x207d, Data3: 0x4dc3,\n\t\tData4: [...]byte{0x85, 0xcf, 0x0f, 0x2e, 0xa1, 0x07, 0x21, 0x3c},\n\t}\n\n\t_, err := WindowsAttachTypeForGUID(\"{00000000-0000-0000-0000-000000000001}\")\n\tqt.Assert(t, qt.ErrorIs(err, os.ErrNotExist))\n\n\tattachType, err := WindowsAttachTypeForGUID(sampleGUID.String())\n\tqt.Assert(t, qt.IsNil(err))\n\tqt.Assert(t, qt.Equals(AttachWindowsSample, attachType))\n}\n"
  },
  {
    "path": "variable.go",
    "content": "package ebpf\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"io\"\n\t\"reflect\"\n\t\"slices\"\n\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/internal/sysenc\"\n)\n\n// VariableSpec is a convenience wrapper for modifying global variables of a\n// CollectionSpec before loading it into the kernel.\n//\n// All operations on a VariableSpec's underlying MapSpec are performed in the\n// host's native endianness.\ntype VariableSpec struct {\n\tName string\n\t// Name of the section this variable was allocated in.\n\tSectionName string\n\t// Offset of the variable within the datasec.\n\tOffset uint32\n\t// Byte representation of the variable's value.\n\tValue []byte\n\t// Type information of the variable. Optional.\n\tType *btf.Var\n}\n\n// Set sets the value of the VariableSpec to the provided input using the host's\n// native endianness.\nfunc (s *VariableSpec) Set(in any) error {\n\tsize := int(s.Size())\n\tif size == 0 {\n\t\tbs := binary.Size(in)\n\t\tif bs < 0 {\n\t\t\treturn fmt.Errorf(\"cannot determine binary size of value %v\", in)\n\t\t}\n\t\tsize = bs\n\t}\n\n\tif s.Value == nil {\n\t\ts.Value = make([]byte, size)\n\t}\n\n\tbuf, err := sysenc.Marshal(in, size)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"marshaling value %s: %w\", s.Name, err)\n\t}\n\n\tbuf.CopyTo(s.Value)\n\treturn nil\n}\n\n// Get writes the value of the VariableSpec to the provided output using the\n// host's native endianness.\n//\n// Returns an error if the variable is not initialized or if the unmarshaling fails.\nfunc (s *VariableSpec) Get(out any) error {\n\tif s.Value == nil {\n\t\treturn fmt.Errorf(\"variable is not initialized\")\n\t}\n\n\tif err := sysenc.Unmarshal(out, s.Value); err != nil {\n\t\treturn fmt.Errorf(\"unmarshaling value: %w\", err)\n\t}\n\n\treturn nil\n}\n\n// Size returns the size of the variable in bytes.\nfunc (s *VariableSpec) Size() uint32 {\n\tif s.Value != nil {\n\t\treturn uint32(len(s.Value))\n\t}\n\n\tif s.Type != nil {\n\t\tsize, err := btf.Sizeof(s.Type.Type)\n\t\tif err != nil {\n\t\t\treturn 0\n\t\t}\n\t\treturn uint32(size)\n\t}\n\n\treturn 0\n}\n\n// Constant returns true if the variable is located in a data section intended\n// for constant values.\nfunc (s *VariableSpec) Constant() bool {\n\treturn isConstantDataSection(s.SectionName)\n}\n\nfunc (s *VariableSpec) String() string {\n\treturn fmt.Sprintf(\"%s (type=%v, section=%s, offset=%d, size=%d)\", s.Name, s.Type, s.SectionName, s.Offset, s.Size())\n}\n\n// Copy the VariableSpec.\nfunc (s *VariableSpec) Copy() *VariableSpec {\n\tcpy := *s\n\tcpy.Value = slices.Clone(s.Value)\n\n\tif s.Type != nil {\n\t\tcpy.Type = btf.Copy(s.Type).(*btf.Var)\n\t}\n\n\treturn &cpy\n}\n\n// Variable is a convenience wrapper for modifying global variables of a\n// Collection after loading it into the kernel. Operations on a Variable are\n// performed using direct memory access, bypassing the BPF map syscall API.\n//\n// On kernels older than 5.5, most interactions with Variable return\n// [ErrNotSupported].\ntype Variable struct {\n\tname   string\n\toffset uint32\n\tsize   uint32\n\tt      *btf.Var\n\n\tmm *Memory\n}\n\nfunc newVariable(name string, offset, size uint32, t *btf.Var, mm *Memory) (*Variable, error) {\n\tif mm != nil {\n\t\tif offset+size > mm.Size() {\n\t\t\treturn nil, fmt.Errorf(\"offset %d(+%d) is out of bounds\", offset, size)\n\t\t}\n\t}\n\n\treturn &Variable{\n\t\tname:   name,\n\t\toffset: offset,\n\t\tsize:   size,\n\t\tt:      t,\n\t\tmm:     mm,\n\t}, nil\n}\n\n// Size returns the size of the variable.\nfunc (v *Variable) Size() uint32 {\n\treturn v.size\n}\n\n// ReadOnly returns true if the Variable represents a variable that is read-only\n// after loading the Collection into the kernel.\n//\n// On systems without BPF_F_MMAPABLE support, ReadOnly always returns true.\nfunc (v *Variable) ReadOnly() bool {\n\tif v.mm == nil {\n\t\treturn true\n\t}\n\treturn v.mm.ReadOnly()\n}\n\n// Type returns the [btf.Var] representing the variable in its data section.\n// This is useful for inspecting the variable's decl tags and the type\n// information of the inner type.\n//\n// Returns nil if the original ELF object did not contain BTF information.\nfunc (v *Variable) Type() *btf.Var {\n\treturn v.t\n}\n\nfunc (v *Variable) String() string {\n\treturn fmt.Sprintf(\"%s (type=%v)\", v.name, v.t)\n}\n\n// Set the value of the Variable to the provided input. The input must marshal\n// to the same length as the size of the Variable.\nfunc (v *Variable) Set(in any) error {\n\tif v.mm == nil {\n\t\treturn fmt.Errorf(\"variable %s: direct access requires Linux 5.5 or later: %w\", v.name, ErrNotSupported)\n\t}\n\n\tif v.ReadOnly() {\n\t\treturn fmt.Errorf(\"variable %s: %w\", v.name, ErrReadOnly)\n\t}\n\n\tif !v.mm.bounds(v.offset, v.size) {\n\t\treturn fmt.Errorf(\"variable %s: access out of bounds: %w\", v.name, io.EOF)\n\t}\n\n\tbuf, err := sysenc.Marshal(in, int(v.size))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"marshaling value %s: %w\", v.name, err)\n\t}\n\n\tif _, err := v.mm.WriteAt(buf.Bytes(), int64(v.offset)); err != nil {\n\t\treturn fmt.Errorf(\"writing value to %s: %w\", v.name, err)\n\t}\n\n\treturn nil\n}\n\n// Get writes the value of the Variable to the provided output. The output must\n// be a pointer to a value whose size matches the Variable.\nfunc (v *Variable) Get(out any) error {\n\tif v.mm == nil {\n\t\treturn fmt.Errorf(\"variable %s: direct access requires Linux 5.5 or later: %w\", v.name, ErrNotSupported)\n\t}\n\n\tif !v.mm.bounds(v.offset, v.size) {\n\t\treturn fmt.Errorf(\"variable %s: access out of bounds: %w\", v.name, io.EOF)\n\t}\n\n\tif err := sysenc.Unmarshal(out, v.mm.b[v.offset:v.offset+v.size]); err != nil {\n\t\treturn fmt.Errorf(\"unmarshaling value %s: %w\", v.name, err)\n\t}\n\n\treturn nil\n}\n\nfunc checkVariable[T any](v *Variable) error {\n\tif v.ReadOnly() {\n\t\treturn ErrReadOnly\n\t}\n\n\tt := reflect.TypeFor[T]()\n\tif t.Kind() == reflect.Uintptr && v.size == 8 {\n\t\t// uintptr is 8 bytes on 64-bit and 4 on 32-bit. In BPF/BTF, pointers are\n\t\t// always 8 bytes. For the sake of portability, allow accessing 8-byte BPF\n\t\t// variables as uintptr on 32-bit systems, since the upper 32 bits of the\n\t\t// pointer should be zero anyway.\n\t\treturn nil\n\t}\n\tif uintptr(v.size) != t.Size() {\n\t\treturn fmt.Errorf(\"can't create %d-byte accessor to %d-byte variable: %w\", t.Size(), v.size, ErrInvalidType)\n\t}\n\n\treturn nil\n}\n\n// VariablePointer returns a pointer to a variable of type T backed by memory\n// shared with the BPF program. Requires building the Go application with -tags\n// ebpf_unsafe_memory_experiment.\n//\n// T must contain only fixed-size, non-Go-pointer types: bools, floats,\n// (u)int[8-64], arrays, and structs containing them. Structs must embed\n// [structs.HostLayout]. [ErrInvalidType] is returned if T is not a valid type.\nfunc VariablePointer[T comparable](v *Variable) (*T, error) {\n\tif err := checkVariable[T](v); err != nil {\n\t\treturn nil, fmt.Errorf(\"variable pointer %s: %w\", v.name, err)\n\t}\n\treturn memoryPointer[T](v.mm, v.offset)\n}\n"
  },
  {
    "path": "variable_test.go",
    "content": "package ebpf\n\nimport (\n\t\"encoding/binary\"\n\t\"runtime\"\n\t\"structs\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\t\"unsafe\"\n\n\t\"github.com/go-quicktest/qt\"\n\n\t\"github.com/cilium/ebpf/btf\"\n\t\"github.com/cilium/ebpf/internal\"\n\t\"github.com/cilium/ebpf/internal/testutils\"\n)\n\nfunc TestVariableSpec(t *testing.T) {\n\tfile := testutils.NativeFile(t, \"testdata/variables-%s.elf\")\n\tspec, err := LoadCollectionSpec(file)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tqt.Assert(t, qt.IsNil(spec.Variables[\"hidden\"]))\n\tqt.Assert(t, qt.IsNotNil(spec.Variables[\"weak\"]))\n\n\tconst want uint32 = 12345\n\n\t// Update a variable in each type of data section (.bss,.data,.rodata)\n\tqt.Assert(t, qt.IsNil(spec.Variables[\"var_bss\"].Set(want)))\n\tqt.Assert(t, qt.IsNil(spec.Variables[\"var_data\"].Set(want)))\n\tqt.Assert(t, qt.IsNil(spec.Variables[\"var_rodata\"].Set(want)))\n\n\tvar v uint32\n\tqt.Assert(t, qt.IsNil(spec.Variables[\"var_bss\"].Get(&v)))\n\tqt.Assert(t, qt.Equals(v, want))\n\tqt.Assert(t, qt.IsNil(spec.Variables[\"var_data\"].Get(&v)))\n\tqt.Assert(t, qt.Equals(v, want))\n\tqt.Assert(t, qt.IsNil(spec.Variables[\"var_rodata\"].Get(&v)))\n\tqt.Assert(t, qt.Equals(v, want))\n\n\t// Composite values.\n\ttype structT struct {\n\t\tA, B uint64\n\t}\n\tqt.Assert(t, qt.IsNil(spec.Variables[\"var_struct\"].Set(&structT{1, 2})))\n\n\tvar s structT\n\tqt.Assert(t, qt.IsNil(spec.Variables[\"var_struct\"].Get(&s)))\n\tqt.Assert(t, qt.Equals(s, structT{1, 2}))\n}\n\nfunc TestVariableSpecCopy(t *testing.T) {\n\tfile := testutils.NativeFile(t, \"testdata/variables-%s.elf\")\n\tspec, err := LoadCollectionSpec(file)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tcpy := spec.Copy()\n\n\t// Update a variable in a section with only a single variable (.rodata).\n\tconst want uint32 = 0xfefefefe\n\twantb := []byte{0xfe, 0xfe, 0xfe, 0xfe} // Same byte sequence regardless of endianness\n\tqt.Assert(t, qt.IsNil(cpy.Variables[\"var_rodata\"].Set(want)))\n\tqt.Assert(t, qt.DeepEquals(cpy.Variables[\"var_rodata\"].Value, wantb))\n\n\t// Verify that the original underlying MapSpec was not modified.\n\tzero := make([]byte, 4)\n\tqt.Assert(t, qt.DeepEquals(spec.Maps[\".rodata\"].Contents[0].Value.([]byte), zero))\n\n\t// Check that modifications to the VariableSpec's Type don't affect the\n\t// underlying MapSpec's type information on either the original or the copy.\n\tcpy.Variables[\"var_rodata\"].Type.Name = \"modified\"\n\tspec.Variables[\"var_rodata\"].Type.Name = \"modified\"\n\n\tqt.Assert(t, qt.Equals(cpy.Maps[\".rodata\"].Value.(*btf.Datasec).Vars[0].Type.(*btf.Var).Name, \"var_rodata\"))\n\tqt.Assert(t, qt.Equals(spec.Maps[\".rodata\"].Value.(*btf.Datasec).Vars[0].Type.(*btf.Var).Name, \"var_rodata\"))\n}\n\nfunc TestVariableSpecEmptyValue(t *testing.T) {\n\tspec := &VariableSpec{\n\t\tType: &btf.Var{\n\t\t\tType: &btf.Int{\n\t\t\t\tSize: 4,\n\t\t\t},\n\t\t},\n\t}\n\n\tvalue := uint32(0x12345678)\n\traw, err := binary.Append(nil, internal.NativeEndian, value)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tqt.Assert(t, qt.IsNotNil(spec.Get(new(uint32))))\n\n\tqt.Assert(t, qt.IsNotNil(spec.Set(uint64(0))), qt.Commentf(\"Setting a value of incorrect size should fail\"))\n\n\tqt.Assert(t, qt.IsNil(spec.Set(value)))\n\tqt.Assert(t, qt.DeepEquals(spec.Value, raw))\n\n\tspec.Value = nil\n\tspec.Type = nil\n\tqt.Assert(t, qt.IsNil(spec.Set(uint64(0))), qt.Commentf(\"Setting an empty value without a type should accept any type\"))\n\tqt.Assert(t, qt.HasLen(spec.Value, 8))\n}\n\nfunc mustReturn(tb testing.TB, prog *Program, value uint32) {\n\ttb.Helper()\n\n\tret, _, err := prog.Test(internal.EmptyBPFContext)\n\tqt.Assert(tb, qt.IsNil(err))\n\tqt.Assert(tb, qt.Equals(ret, value))\n}\n\nfunc TestVariable(t *testing.T) {\n\ttestutils.SkipIfNotSupported(t, haveMmapableMaps())\n\n\tfile := testutils.NativeFile(t, \"testdata/variables-%s.elf\")\n\tspec, err := LoadCollectionSpec(file)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tobj := struct {\n\t\tGetBSS      *Program `ebpf:\"get_bss\"`\n\t\tGetData     *Program `ebpf:\"get_data\"`\n\t\tCheckStruct *Program `ebpf:\"check_struct\"`\n\n\t\tBSS    *Variable `ebpf:\"var_bss\"`\n\t\tData   *Variable `ebpf:\"var_data\"`\n\t\tStruct *Variable `ebpf:\"var_struct\"`\n\t\tArray  *Variable `ebpf:\"var_array\"`\n\t}{}\n\n\tqt.Assert(t, qt.IsNil(loadAndAssign(t, spec, &obj, nil)))\n\tt.Cleanup(func() {\n\t\tobj.GetBSS.Close()\n\t\tobj.GetData.Close()\n\t\tobj.CheckStruct.Close()\n\t})\n\n\tmustReturn(t, obj.GetBSS, 0)\n\tmustReturn(t, obj.GetData, 0)\n\tmustReturn(t, obj.CheckStruct, 0)\n\n\twant := uint32(4242424242)\n\tqt.Assert(t, qt.IsNil(obj.BSS.Set(want)))\n\tmustReturn(t, obj.GetBSS, want)\n\tqt.Assert(t, qt.IsNil(obj.Data.Set(want)))\n\tmustReturn(t, obj.GetData, want)\n\tqt.Assert(t, qt.IsNil(obj.Struct.Set(&struct{ A, B uint64 }{0xa, 0xb})))\n\tmustReturn(t, obj.CheckStruct, 1)\n\n\t// Ensure page-aligned array variable can be accessed in its entirety.\n\tarr := make([]byte, obj.Array.Size())\n\tqt.Assert(t, qt.IsNil(obj.Array.Get(arr)))\n\tqt.Assert(t, qt.IsNil(obj.Array.Set(arr)))\n\n\ttyp := obj.BSS.Type()\n\tqt.Assert(t, qt.IsNotNil(typ))\n\ti, ok := btf.As[*btf.Int](typ.Type)\n\tqt.Assert(t, qt.IsTrue(ok))\n\tqt.Assert(t, qt.Equals(i.Size, 4))\n\n\tqt.Assert(t, qt.IsNotNil(obj.Data.Type()))\n\tqt.Assert(t, qt.IsNotNil(obj.Struct.Type()))\n}\n\nfunc TestVariableConst(t *testing.T) {\n\ttestutils.SkipIfNotSupported(t, haveMmapableMaps())\n\n\tfile := testutils.NativeFile(t, \"testdata/variables-%s.elf\")\n\tspec, err := LoadCollectionSpec(file)\n\tqt.Assert(t, qt.IsNil(err))\n\n\twant := uint32(12345)\n\tqt.Assert(t, qt.IsNil(spec.Variables[\"var_rodata\"].Set(want)))\n\n\tobj := struct {\n\t\tGetRodata *Program  `ebpf:\"get_rodata\"`\n\t\tRodata    *Variable `ebpf:\"var_rodata\"`\n\t}{}\n\n\tqt.Assert(t, qt.IsNil(loadAndAssign(t, spec, &obj, nil)))\n\tt.Cleanup(func() {\n\t\tobj.GetRodata.Close()\n\t})\n\n\tvar got uint32\n\tqt.Assert(t, qt.IsNil(obj.Rodata.Get(&got)))\n\tqt.Assert(t, qt.Equals(got, want))\n\tmustReturn(t, obj.GetRodata, want)\n\n\tqt.Assert(t, qt.IsTrue(obj.Rodata.ReadOnly()))\n\tqt.Assert(t, qt.ErrorIs(obj.Rodata.Set(want), ErrReadOnly))\n}\n\nfunc TestVariableFallback(t *testing.T) {\n\t// LoadAndAssign should work on Variable regardless of BPF_F_MMAPABLE support.\n\tfile := testutils.NativeFile(t, \"testdata/variables-%s.elf\")\n\tspec, err := LoadCollectionSpec(file)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tobj := struct {\n\t\tData *Variable `ebpf:\"var_data\"`\n\t}{}\n\n\tmustLoadAndAssign(t, spec, &obj, nil)\n\n\t// Expect either success or ErrNotSupported on all systems.\n\tu32 := uint32(0)\n\tif err := obj.Data.Get(&u32); err != nil {\n\t\tqt.Assert(t, qt.ErrorIs(err, ErrNotSupported))\n\t}\n\n\tif err := obj.Data.Set(&u32); err != nil {\n\t\tqt.Assert(t, qt.ErrorIs(err, ErrNotSupported))\n\t}\n}\n\nfunc TestVariablePointer(t *testing.T) {\n\ttestutils.SkipIfNotSupported(t, haveMmapableMaps())\n\n\tfile := testutils.NativeFile(t, \"testdata/variables-%s.elf\")\n\tspec, err := LoadCollectionSpec(file)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tobj := struct {\n\t\tAddAtomic      *Program `ebpf:\"add_atomic\"`\n\t\tCheckStructPad *Program `ebpf:\"check_struct_pad\"`\n\t\tCheckArray     *Program `ebpf:\"check_array\"`\n\n\t\tAtomic    *Variable `ebpf:\"var_atomic\"`\n\t\tStructPad *Variable `ebpf:\"var_struct_pad\"`\n\t\tArray     *Variable `ebpf:\"var_array\"`\n\t}{}\n\n\tunsafeMemory = true\n\tt.Cleanup(func() {\n\t\tunsafeMemory = false\n\t})\n\n\tqt.Assert(t, qt.IsNil(loadAndAssign(t, spec, &obj, nil)))\n\tt.Cleanup(func() {\n\t\tobj.AddAtomic.Close()\n\t\tobj.CheckStructPad.Close()\n\t\tobj.CheckArray.Close()\n\t})\n\n\t// Bump the value by 1 using a bpf program.\n\twant := uint32(1338)\n\ta32, err := VariablePointer[atomic.Uint32](obj.Atomic)\n\tqt.Assert(t, qt.IsNil(err))\n\ta32.Store(want - 1)\n\n\tmustReturn(t, obj.AddAtomic, 0)\n\tqt.Assert(t, qt.Equals(a32.Load(), want))\n\n\t_, err = VariablePointer[*uint32](obj.Atomic)\n\tqt.Assert(t, qt.ErrorIs(err, ErrInvalidType))\n\n\t_, err = VariablePointer[struct{ _ *uint64 }](obj.StructPad)\n\tqt.Assert(t, qt.ErrorIs(err, ErrInvalidType))\n\n\ttype S struct {\n\t\t_ structs.HostLayout\n\t\tA uint32\n\t\tB uint64\n\t\tC uint16\n\t\tD [5]byte\n\t\tE uint64\n\t}\n\n\ts, err := VariablePointer[S](obj.StructPad)\n\tqt.Assert(t, qt.IsNil(err))\n\t*s = S{A: 0xa, B: 0xb, C: 0xc, D: [5]byte{0xd, 0, 0, 0, 0}, E: 0xe}\n\tmustReturn(t, obj.CheckStructPad, 1)\n\n\ta, err := VariablePointer[[8192]byte](obj.Array)\n\tqt.Assert(t, qt.IsNil(err))\n\ta[len(a)-1] = 0xff\n\tmustReturn(t, obj.CheckArray, 1)\n}\n\nfunc TestVariablePointerError(t *testing.T) {\n\ttestutils.SkipIfNotSupported(t, haveMmapableMaps())\n\n\tfile := testutils.NativeFile(t, \"testdata/variables-%s.elf\")\n\tspec, err := LoadCollectionSpec(file)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tobj := struct {\n\t\tAtomic *Variable `ebpf:\"var_atomic\"`\n\t}{}\n\n\tqt.Assert(t, qt.IsNil(loadAndAssign(t, spec, &obj, nil)))\n\n\t_, err = VariablePointer[atomic.Uint32](obj.Atomic)\n\tqt.Assert(t, qt.ErrorIs(err, ErrNotSupported))\n}\n\nfunc TestVariablePointerGC(t *testing.T) {\n\ttestutils.SkipIfNotSupported(t, haveMmapableMaps())\n\n\tfile := testutils.NativeFile(t, \"testdata/variables-%s.elf\")\n\tspec, err := LoadCollectionSpec(file)\n\tqt.Assert(t, qt.IsNil(err))\n\n\tcancel := make(chan struct{})\n\n\ttype obj_s struct {\n\t\tAddAtomic *Program  `ebpf:\"add_atomic\"`\n\t\tAtomic    *Variable `ebpf:\"var_atomic\"`\n\t\tAtomicMap *Map      `ebpf:\".data.atomic\"`\n\t}\n\n\tunsafeMemory = true\n\tt.Cleanup(func() {\n\t\tunsafeMemory = false\n\t})\n\tvar obj obj_s\n\tqt.Assert(t, qt.IsNil(loadAndAssign(t, spec, &obj, nil)))\n\n\t// Set cleanup on obj to get notified when it is collected.\n\togc := make(chan struct{})\n\truntime.AddCleanup(&obj, func(*byte) {\n\t\tclose(ogc)\n\t}, nil)\n\tmem, err := obj.AtomicMap.unsafeMemory()\n\tqt.Assert(t, qt.IsNil(err))\n\tobj.AtomicMap.Close()\n\n\t// Start a goroutine that panics if the finalizer runs before we expect it to.\n\tmgc := make(chan struct{})\n\tgo func() {\n\t\tselect {\n\t\tcase <-mgc:\n\t\t\tpanic(\"memory cleanup ran unexpectedly\")\n\t\tcase <-cancel:\n\t\t\treturn\n\t\t}\n\t}()\n\n\t// Set cleanup on the Memory's backing array to get notified when it is\n\t// collected.\n\truntime.AddCleanup(unsafe.SliceData(mem.b), func(*byte) {\n\t\tclose(mgc)\n\t}, nil)\n\n\t// Pull out Program handle and Variable pointer so reference to obj is\n\t// dropped.\n\tprog := obj.AddAtomic\n\tt.Cleanup(func() {\n\t\tprog.Close()\n\t})\n\n\ta32, err := VariablePointer[atomic.Uint32](obj.Atomic)\n\tqt.Assert(t, qt.IsNil(err))\n\n\t// No references to obj past this point. Trigger GC and wait for the obj\n\t// finalizer to complete.\n\truntime.GC()\n\ttestutils.WaitChan(t, ogc, time.Second)\n\n\t// Trigger prog and read memory to ensure variable reference is still valid.\n\tmustReturn(t, prog, 0)\n\tqt.Assert(t, qt.Equals(a32.Load(), 1))\n\n\t// Close the cancel channel while holding a backing array reference to avoid\n\t// false-positive panics in case we get a GC cycle before the manual call to\n\t// runtime.GC below.\n\tclose(cancel)\n\truntime.KeepAlive(a32)\n\n\t// More GC cycles to collect the backing array. As long as the unsafe memory\n\t// implementation is still on SetFinalizer, this needs multiple cycles to\n\t// work, since finalizers can resurrect objects. 3 GCs seems to work reliably.\n\truntime.GC()\n\truntime.GC()\n\truntime.GC()\n\n\t// Wait for backing array to be finalized.\n\ttestutils.WaitChan(t, mgc, time.Second*5)\n}\n"
  }
]